/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* 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_GFX_DCLAYER_TREE_H
#define MOZILLA_GFX_DCLAYER_TREE_H

#include <deque>
#include <dxgiformat.h>
#include <unordered_map>
#include <vector>
#include <windows.h>

#include "Colorspaces.h"
#include "GLTypes.h"
#include "mozilla/HashFunctions.h"
#include "mozilla/layers/OverlayInfo.h"
#include "mozilla/Maybe.h"
#include "mozilla/RefPtr.h"
#include "mozilla/StaticPtr.h"
#include "mozilla/UniquePtr.h"
#include "mozilla/webrender/WebRenderTypes.h"

struct ID3D11Device;
struct ID3D11DeviceContext;
struct ID3D11Texture2D;
struct ID3D11VideoDevice;
struct ID3D11VideoContext;
struct ID3D11VideoProcessor;
struct ID3D11VideoProcessorEnumerator;
struct ID3D11VideoProcessorOutputView;
struct IDCompositionColorMatrixEffect;
struct IDCompositionFilterEffect;
struct IDCompositionTableTransferEffect;
struct IDCompositionTexture;
struct IDCompositionDevice2;
struct IDCompositionDevice3;
struct IDCompositionSurface;
struct IDCompositionTarget;
struct IDCompositionVisual2;
struct IDXGIDecodeSwapChain;
struct IDXGIResource;
struct IDXGISwapChain1;
struct IDCompositionVirtualSurface;
struct IDCompositionRectangleClip;

namespace mozilla {

namespace gfx {
color::ColorProfileDesc QueryOutputColorProfile();
}

namespace gl {
class GLContext;
}

namespace wr {

// The size of the virtual surface. This is large enough such that we
// will never render a surface larger than this.
#define VIRTUAL_SURFACE_SIZE (1024 * 1024)

class DCLayerSurface;
class DCTile;
class DCLayerDCompositionTexture;
class DCSurface;
class DCSwapChain;
class DCSurfaceVideo;
class DCSurfaceHandle;
class RenderTextureHost;
class RenderTextureHostUsageInfo;
class RenderDcompSurfaceTextureHost;

struct GpuOverlayInfo {
  bool mSupportsOverlays = false;
  bool mSupportsHardwareOverlays = false;
  DXGI_FORMAT mOverlayFormatUsed = DXGI_FORMAT_B8G8R8A8_UNORM;
  DXGI_FORMAT mOverlayFormatUsedHdr = DXGI_FORMAT_R10G10B10A2_UNORM;
  UINT mNv12OverlaySupportFlags = 0;
  UINT mYuy2OverlaySupportFlags = 0;
  UINT mBgra8OverlaySupportFlags = 0;
  UINT mRgb10a2OverlaySupportFlags = 0;

  bool mSupportsVpSuperResolution = false;
  bool mSupportsVpAutoHDR = false;
};

// -

struct ColorManagementChain {
  RefPtr<IDCompositionColorMatrixEffect> srcRgbFromSrcYuv;
  RefPtr<IDCompositionTableTransferEffect> srcLinearFromSrcTf;
  RefPtr<IDCompositionColorMatrixEffect> dstLinearFromSrcLinear;
  RefPtr<IDCompositionTableTransferEffect> dstTfFromDstLinear;
  RefPtr<IDCompositionFilterEffect> last;

  static ColorManagementChain From(IDCompositionDevice3& dcomp,
                                   const color::ColorProfileConversionDesc&);

  ~ColorManagementChain();
};

// -

enum class DCompOverlayTypes : uint8_t {
  NO_OVERLAY = 0,
  HARDWARE_DECODED_VIDEO = 1 << 0,
  SOFTWARE_DECODED_VIDEO = 1 << 1,
};
MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(DCompOverlayTypes)

// -

/**
 * DCLayerTree manages direct composition layers.
 * It does not manage gecko's layers::Layer.
 */
class DCLayerTree {
 public:
  static UniquePtr<DCLayerTree> Create(gl::GLContext* aGL, EGLConfig aEGLConfig,
                                       ID3D11Device* aDevice,
                                       ID3D11DeviceContext* aCtx, HWND aHwnd,
                                       nsACString& aError);

  static void Shutdown();

  explicit DCLayerTree(gl::GLContext* aGL, EGLConfig aEGLConfig,
                       ID3D11Device* aDevice, ID3D11DeviceContext* aCtx,
                       HWND aHwnd, IDCompositionDevice2* aCompositionDevice);
  ~DCLayerTree();

  void SetDefaultSwapChain(IDXGISwapChain1* aSwapChain);
  void MaybeUpdateDebug();
  void MaybeCommit();
  void WaitForCommitCompletion();

  bool UseNativeCompositor() const;
  bool UseLayerCompositor() const;
  void DisableNativeCompositor();
  bool EnableAsyncScreenshot();
  bool GetAsyncScreenshotEnabled() const { return mEnableAsyncScreenshot; }

  // Interface for wr::Compositor
  void CompositorBeginFrame();
  void CompositorEndFrame();
  void Bind(wr::NativeTileId aId, wr::DeviceIntPoint* aOffset, uint32_t* aFboId,
            wr::DeviceIntRect aDirtyRect, wr::DeviceIntRect aValidRect);
  void Unbind();
  void CreateSurface(wr::NativeSurfaceId aId, wr::DeviceIntPoint aVirtualOffset,
                     wr::DeviceIntSize aTileSize, bool aIsOpaque);
  void CreateSwapChainSurface(wr::NativeSurfaceId aId, wr::DeviceIntSize aSize,
                              bool aIsOpaque, bool aNeedsSyncDcompCommit);
  void ResizeSwapChainSurface(wr::NativeSurfaceId aId, wr::DeviceIntSize aSize);
  void CreateExternalSurface(wr::NativeSurfaceId aId, bool aIsOpaque);
  void DestroySurface(NativeSurfaceId aId);
  void CreateTile(wr::NativeSurfaceId aId, int32_t aX, int32_t aY);
  void DestroyTile(wr::NativeSurfaceId aId, int32_t aX, int32_t aY);
  void AttachExternalImage(wr::NativeSurfaceId aId,
                           wr::ExternalImageId aExternalImage);
  void AddSurface(wr::NativeSurfaceId aId,
                  const wr::CompositorSurfaceTransform& aTransform,
                  wr::DeviceIntRect aClipRect,
                  wr::ImageRendering aImageRendering,
                  wr::DeviceIntRect aRoundedClipRect,
                  wr::ClipRadius aClipRadius);
  void BindSwapChain(wr::NativeSurfaceId aId,
                     const wr::DeviceIntRect* aDirtyRects,
                     size_t aNumDirtyRects);
  void PresentSwapChain(wr::NativeSurfaceId aId,
                        const wr::DeviceIntRect* aDirtyRects,
                        size_t aNumDirtyRects);

  gl::GLContext* GetGLContext() const { return mGL; }
  EGLConfig GetEGLConfig() const { return mEGLConfig; }
  ID3D11Device* GetDevice() const { return mDevice; }
  ID3D11DeviceContext* GetDeviceContext() const { return mCtx; }
  IDCompositionDevice2* GetCompositionDevice() const {
    return mCompositionDevice;
  }
  ID3D11VideoDevice* GetVideoDevice() const { return mVideoDevice; }
  ID3D11VideoContext* GetVideoContext() const { return mVideoContext; }
  ID3D11VideoProcessor* GetVideoProcessor() const { return mVideoProcessor; }
  ID3D11VideoProcessorEnumerator* GetVideoProcessorEnumerator() const {
    return mVideoProcessorEnumerator;
  }
  bool EnsureVideoProcessor(const gfx::IntSize& aInputSize,
                            const gfx::IntSize& aOutputSize);

  DCSurface* GetSurface(wr::NativeSurfaceId aId) const;

  HWND GetHwnd() const { return mHwnd; }

  // Get or create an FBO with depth buffer suitable for specified dimensions
  GLuint GetOrCreateFbo(int aWidth, int aHeight);

  bool SupportsHardwareOverlays();
  DXGI_FORMAT GetOverlayFormatForSDR();

  bool SupportsSwapChainTearing();
  bool SupportsDCompositionTexture();

  void SetUsedOverlayTypeInFrame(DCompOverlayTypes aTypes);

  int GetFrameId() { return mCurrentFrame; }

 protected:
  bool Initialize(HWND aHwnd, nsACString& aError);
  bool InitializeVideoOverlaySupport();
  bool MaybeUpdateDebugCounter();
  bool MaybeUpdateDebugVisualRedrawRegions();
  void DestroyEGLSurface();
  GLuint CreateEGLSurfaceForCompositionSurface(
      wr::DeviceIntRect aDirtyRect, wr::DeviceIntPoint* aOffset,
      RefPtr<IDCompositionSurface> aCompositionSurface,
      wr::DeviceIntPoint aSurfaceOffset);
  void ReleaseNativeCompositorResources();
  layers::OverlayInfo GetOverlayInfo();

  bool mUseNativeCompositor = true;
  bool mEnableAsyncScreenshot = false;
  bool mEnableAsyncScreenshotInNextFrame = false;
  int mAsyncScreenshotLastFrameUsed = 0;

  RefPtr<gl::GLContext> mGL;
  EGLConfig mEGLConfig;

  RefPtr<ID3D11Device> mDevice;
  RefPtr<ID3D11DeviceContext> mCtx;
  HWND mHwnd;

  RefPtr<IDCompositionDevice2> mCompositionDevice;
  RefPtr<IDCompositionTarget> mCompositionTarget;
  RefPtr<IDCompositionVisual2> mRootVisual;
  RefPtr<IDCompositionVisual2> mDefaultSwapChainVisual;

  RefPtr<ID3D11VideoDevice> mVideoDevice;
  RefPtr<ID3D11VideoContext> mVideoContext;
  RefPtr<ID3D11VideoProcessor> mVideoProcessor;
  RefPtr<ID3D11VideoProcessorEnumerator> mVideoProcessorEnumerator;
  gfx::IntSize mVideoInputSize;
  gfx::IntSize mVideoOutputSize;

  bool mDebugCounter;
  bool mDebugVisualRedrawRegions;

  Maybe<RefPtr<IDCompositionSurface>> mCurrentSurface;

  // The EGL image that is bound to the D3D texture provided by
  // DirectComposition.
  EGLImage mEGLImage;

  // The GL render buffer ID that maps the EGLImage to an RBO for attaching to
  // an FBO.
  GLuint mColorRBO;

  struct SurfaceIdHashFn {
    std::size_t operator()(const wr::NativeSurfaceId& aId) const {
      return HashGeneric(wr::AsUint64(aId));
    }
  };

  std::unordered_map<wr::NativeSurfaceId, UniquePtr<DCSurface>, SurfaceIdHashFn>
      mDCSurfaces;

  // A list of layer IDs as they are added to the visual tree this frame.
  std::vector<wr::NativeSurfaceId> mCurrentLayers;

  // The previous frame's list of layer IDs in visual order.
  std::vector<wr::NativeSurfaceId> mPrevLayers;

  // Information about a cached FBO that is retained between frames.
  struct CachedFrameBuffer {
    int width;
    int height;
    GLuint fboId;
    GLuint depthRboId;
    int lastFrameUsed;
  };

  // A cache of FBOs, containing a depth buffer allocated to a specific size.
  // TODO(gw): Might be faster as a hashmap? The length is typically much less
  // than 10.
  nsTArray<CachedFrameBuffer> mFrameBuffers;
  int mCurrentFrame = 0;

  bool mPendingCommit;

  mutable Maybe<color::ColorProfileDesc> mOutputColorProfile;

  DCompOverlayTypes mUsedOverlayTypesInFrame = DCompOverlayTypes::NO_OVERLAY;

 public:
  const color::ColorProfileDesc& OutputColorProfile() const {
    if (!mOutputColorProfile) {
      mOutputColorProfile = Some(gfx::QueryOutputColorProfile());
    }
    return *mOutputColorProfile;
  }

 protected:
  static StaticAutoPtr<GpuOverlayInfo> sGpuOverlayInfo;
};

/**
 Represents a single picture cache slice. Each surface contains some
 number of tiles. An implementation may choose to allocate individual
 tiles to render in to (as the current impl does), or allocate a large
 single virtual surface to draw into (e.g. the DirectComposition virtual
 surface API in future).
 */
class DCSurface {
 public:
  const bool mIsVirtualSurface;

  explicit DCSurface(wr::DeviceIntSize aTileSize,
                     wr::DeviceIntPoint aVirtualOffset, bool aIsVirtualSurface,
                     bool aIsOpaque, DCLayerTree* aDCLayerTree);
  virtual ~DCSurface();

  virtual bool Initialize();
  void CreateTile(int32_t aX, int32_t aY);
  void DestroyTile(int32_t aX, int32_t aY);
  void SetClip(wr::DeviceIntRect aClipRect, wr::ClipRadius aClipRadius);

  IDCompositionVisual2* GetContentVisual() const { return mContentVisual; }
  IDCompositionVisual2* GetRootVisual() const { return mRootVisual; }
  DCTile* GetTile(int32_t aX, int32_t aY) const;

  struct TileKey {
    TileKey(int32_t aX, int32_t aY) : mX(aX), mY(aY) {}

    int32_t mX;
    int32_t mY;
  };

  wr::DeviceIntSize GetTileSize() const { return mTileSize; }
  wr::DeviceIntPoint GetVirtualOffset() const { return mVirtualOffset; }

  IDCompositionVirtualSurface* GetCompositionSurface() const {
    return mVirtualSurface;
  }

  void UpdateAllocatedRect();
  void DirtyAllocatedRect();

  // Implement these if the inherited surface supports attaching external image.
  virtual void AttachExternalImage(wr::ExternalImageId aExternalImage) {
    MOZ_RELEASE_ASSERT(true, "Not support attaching external image");
  }
  virtual void PresentExternalSurface(gfx::Matrix& aTransform) {
    MOZ_RELEASE_ASSERT(true, "Not support presenting external surface");
  }

  virtual DCSurfaceVideo* AsDCSurfaceVideo() { return nullptr; }
  virtual DCSurfaceHandle* AsDCSurfaceHandle() { return nullptr; }
  virtual DCLayerSurface* AsDCLayerSurface() { return nullptr; }
  virtual DCSwapChain* AsDCSwapChain() { return nullptr; }
  virtual DCLayerDCompositionTexture* AsDCLayerDCompositionTexture() {
    return nullptr;
  }

  bool IsUpdated(const wr::CompositorSurfaceTransform& aTransform,
                 const wr::DeviceIntRect& aClipRect,
                 const wr::ImageRendering aImageRendering,
                 const wr::DeviceIntRect& aRoundedClipRect,
                 const wr::ClipRadius& aClipRadius);

 protected:
  DCLayerTree* mDCLayerTree;

  struct TileKeyHashFn {
    std::size_t operator()(const TileKey& aId) const {
      return HashGeneric(aId.mX, aId.mY);
    }
  };

  struct DCSurfaceData {
    DCSurfaceData(const wr::CompositorSurfaceTransform& aTransform,
                  const wr::DeviceIntRect& aClipRect,
                  const wr::ImageRendering aImageRendering,
                  const wr::DeviceIntRect& aRoundedClipRect,
                  const wr::ClipRadius& aClipRadius)
        : mTransform(aTransform),
          mClipRect(aClipRect),
          mImageRendering(aImageRendering),
          mRoundedClipRect(aRoundedClipRect),
          mClipRadius(aClipRadius) {}

    wr::CompositorSurfaceTransform mTransform;
    wr::DeviceIntRect mClipRect;
    wr::ImageRendering mImageRendering;
    wr::DeviceIntRect mRoundedClipRect;
    wr::ClipRadius mClipRadius;
  };

  // Each surface creates two visuals. The root is where it gets attached
  // to parent visuals, the content is where surface (or child visuals)
  // get attached. Most of the time, the root visual does nothing, but
  // in the case of a complex clip, we attach the clip here. This allows
  // us to implement the simple rectangle clip on the content, and apply
  // the complex clip, if present, in a way that it's not affected by
  // the transform of the content visual.
  //
  // When using a virtual surface, it is directly attached to this
  // child visual and the tiles do not own visuals.
  //
  // Whether mIsVirtualSurface is enabled is decided at DCSurface creation
  // time based on the pref gfx.webrender.dcomp-use-virtual-surfaces
  RefPtr<IDCompositionVisual2> mRootVisual;
  RefPtr<IDCompositionVisual2> mContentVisual;
  RefPtr<IDCompositionRectangleClip> mClip;

  wr::DeviceIntSize mTileSize;
  bool mIsOpaque;
  bool mAllocatedRectDirty;
  std::unordered_map<TileKey, UniquePtr<DCTile>, TileKeyHashFn> mDCTiles;
  wr::DeviceIntPoint mVirtualOffset;
  RefPtr<IDCompositionVirtualSurface> mVirtualSurface;
  Maybe<DCSurfaceData> mDCSurfaceData;
};

class DCLayerSurface : public DCSurface {
 public:
  DCLayerSurface(bool aIsOpaque, DCLayerTree* aDCLayerTree)
      : DCSurface(wr::DeviceIntSize{}, wr::DeviceIntPoint{}, false, aIsOpaque,
                  aDCLayerTree) {}
  virtual ~DCLayerSurface() = default;

  virtual void Bind(const wr::DeviceIntRect* aDirtyRects,
                    size_t aNumDirtyRects) = 0;
  virtual bool Resize(wr::DeviceIntSize aSize) = 0;
  virtual void Present(const wr::DeviceIntRect* aDirtyRects,
                       size_t aNumDirtyRects) = 0;

  DCLayerSurface* AsDCLayerSurface() override { return this; }
};

class DCLayerDCompositionTexture : public DCLayerSurface {
 public:
  DCLayerDCompositionTexture(wr::DeviceIntSize aSize, bool aIsOpaque,
                             DCLayerTree* aDCLayerTree);
  virtual ~DCLayerDCompositionTexture();

  bool Initialize() override;

  void Bind(const wr::DeviceIntRect* aDirtyRects,
            size_t aNumDirtyRects) override;
  bool Resize(wr::DeviceIntSize aSize) override;
  void Present(const wr::DeviceIntRect* aDirtyRects,
               size_t aNumDirtyRects) override;

  DCLayerDCompositionTexture* AsDCLayerDCompositionTexture() override {
    return this;
  }

  const size_t mSwapChainBufferCount;

 private:
  struct TextureHolder {
    TextureHolder(ID3D11Texture2D* aTexture,
                  IDCompositionTexture* aDCompositionTexture,
                  EGLSurface aEGLSurface);
    TextureHolder() = default;

    RefPtr<ID3D11Texture2D> mTexture;
    RefPtr<IDCompositionTexture> mDCompositionTexture;
    EGLSurface mEGLSurface;
  };

  bool AllocateTextures();
  void DestroyTextures();
  UniquePtr<TextureHolder> GetNextTexture();
  void UpdateCurrentTexture();

  wr::DeviceIntSize mSize;
  std::deque<UniquePtr<TextureHolder>> mAvailableTextureHolders;

  UniquePtr<TextureHolder> mCurrentTextureHolder;
  UniquePtr<TextureHolder> mPresentingTextureHolder;
};

class DCSwapChain : public DCLayerSurface {
 public:
  DCSwapChain(wr::DeviceIntSize aSize, bool aIsOpaque,
              DCLayerTree* aDCLayerTree);
  virtual ~DCSwapChain();

  bool Initialize() override;

  void Bind(const wr::DeviceIntRect* aDirtyRects,
            size_t aNumDirtyRects) override;
  bool Resize(wr::DeviceIntSize aSize) override;
  void Present(const wr::DeviceIntRect* aDirtyRects,
               size_t aNumDirtyRects) override;

  DCSwapChain* AsDCSwapChain() override { return this; }

  const int mSwapChainBufferCount;

 private:
  wr::DeviceIntSize mSize;
  RefPtr<IDXGISwapChain1> mSwapChain;
  EGLSurface mEGLSurface;
  bool mFirstPresent = true;
};

class DCLayerCompositionSurface : public DCLayerSurface {
 public:
  DCLayerCompositionSurface(wr::DeviceIntSize aSize, bool aIsOpaque,
                            DCLayerTree* aDCLayerTree);
  virtual ~DCLayerCompositionSurface();

  bool Initialize() override;

  void Bind(const wr::DeviceIntRect* aDirtyRects,
            size_t aNumDirtyRects) override;
  bool Resize(wr::DeviceIntSize aSize) override;
  void Present(const wr::DeviceIntRect* aDirtyRects,
               size_t aNumDirtyRects) override;

 private:
  wr::DeviceIntSize mSize;
  EGLSurface mEGLSurface = EGL_NO_SURFACE;
  RefPtr<IDCompositionSurface> mCompositionSurface;
  bool mFirstDraw = true;
};

/**
 * A wrapper surface which can contain either a DCVideo or a DCSurfaceHandle.
 */
class DCExternalSurfaceWrapper : public DCSurface {
 public:
  DCExternalSurfaceWrapper(bool aIsOpaque, DCLayerTree* aDCLayerTree)
      : DCSurface(wr::DeviceIntSize{}, wr::DeviceIntPoint{},
                  false /* virtual surface */, false /* opaque */,
                  aDCLayerTree),
        mIsOpaque(aIsOpaque) {}
  virtual ~DCExternalSurfaceWrapper() = default;

  void AttachExternalImage(wr::ExternalImageId aExternalImage) override;

  void PresentExternalSurface(gfx::Matrix& aTransform) override;

  DCSurfaceVideo* AsDCSurfaceVideo() override {
    return mSurface ? mSurface->AsDCSurfaceVideo() : nullptr;
  }

  DCSurfaceHandle* AsDCSurfaceHandle() override {
    return mSurface ? mSurface->AsDCSurfaceHandle() : nullptr;
  }

 private:
  DCSurface* EnsureSurfaceForExternalImage(wr::ExternalImageId aExternalImage);

  UniquePtr<DCSurface> mSurface;
  const bool mIsOpaque;
  Maybe<ColorManagementChain> mCManageChain;
};

class DCSurfaceVideo : public DCSurface {
 public:
  DCSurfaceVideo(bool aIsOpaque, DCLayerTree* aDCLayerTree);

  void AttachExternalImage(wr::ExternalImageId aExternalImage) override;
  bool CalculateSwapChainSize(gfx::Matrix& aTransform);
  void PresentVideo();
  void OnCompositorEndFrame(int aFrameId, uint32_t aDurationMs);

  DCSurfaceVideo* AsDCSurfaceVideo() override { return this; }

 protected:
  virtual ~DCSurfaceVideo();

  DXGI_FORMAT GetSwapChainFormat(bool aUseVpAutoHDR);
  bool CreateVideoSwapChain(DXGI_FORMAT aFormat);
  bool CallVideoProcessorBlt();
  void ReleaseDecodeSwapChainResources();

  RefPtr<ID3D11VideoProcessorOutputView> mOutputView;
  RefPtr<IDXGIResource> mDecodeResource;
  RefPtr<IDXGISwapChain1> mVideoSwapChain;
  RefPtr<IDXGIDecodeSwapChain> mDecodeSwapChain;
  HANDLE mSwapChainSurfaceHandle = 0;
  gfx::IntSize mVideoSize;
  gfx::IntSize mSwapChainSize;
  DXGI_FORMAT mSwapChainFormat = DXGI_FORMAT_B8G8R8A8_UNORM;
  bool mIsDRM = false;
  bool mFailedYuvSwapChain = false;
  RefPtr<RenderTextureHost> mRenderTextureHost;
  RefPtr<RenderTextureHost> mPrevTexture;
  RefPtr<RenderTextureHostUsageInfo> mRenderTextureHostUsageInfo;
  bool mFirstPresent = true;
  const UINT mSwapChainBufferCount;
  bool mUseVpAutoHDR = false;
  bool mVpAutoHDRFailed = false;
  bool mVpSuperResolutionFailed = false;
};

/**
 * A DC surface contains a IDCompositionSurface that is directly constructed by
 * a handle. This is used by the Media Foundataion media engine, which would
 * store the decoded video content in the surface.
 */
class DCSurfaceHandle : public DCSurface {
 public:
  DCSurfaceHandle(bool aIsOpaque, DCLayerTree* aDCLayerTree);
  virtual ~DCSurfaceHandle() = default;

  void AttachExternalImage(wr::ExternalImageId aExternalImage) override;
  void PresentSurfaceHandle();

  DCSurfaceHandle* AsDCSurfaceHandle() override { return this; }

 protected:
  HANDLE GetSurfaceHandle() const;
  IDCompositionSurface* EnsureSurface();

  RefPtr<RenderDcompSurfaceTextureHost> mDcompTextureHost;
};

class DCTile {
 public:
  gfx::IntRect mValidRect;

  DCLayerTree* mDCLayerTree;
  // Indicates that when the first BeginDraw occurs on the surface it must be
  // full size - required by dcomp on non-virtual surfaces.
  bool mNeedsFullDraw;

  explicit DCTile(DCLayerTree* aDCLayerTree);
  ~DCTile();
  bool Initialize(int aX, int aY, wr::DeviceIntSize aSize,
                  bool aIsVirtualSurface, bool aIsOpaque,
                  RefPtr<IDCompositionVisual2> mSurfaceVisual);
  RefPtr<IDCompositionSurface> Bind(wr::DeviceIntRect aValidRect);
  IDCompositionVisual2* GetVisual() { return mVisual; }

 protected:
  // Size in pixels of this tile, some may be unused.  Set by Initialize.
  wr::DeviceIntSize mSize;
  // Whether the tile is composited as opaque (ignores alpha) or transparent.
  // Set by Initialize.
  bool mIsOpaque;
  // Some code paths differ based on whether parent surface is virtual.
  bool mIsVirtualSurface;
  // Visual that displays the composition surface, or NULL if the tile belongs
  // to a virtual surface.
  RefPtr<IDCompositionVisual2> mVisual;
  // Surface for the visual, or NULL if the tile has not had its first Bind or
  // belongs to a virtual surface.
  RefPtr<IDCompositionSurface> mCompositionSurface;

  RefPtr<IDCompositionSurface> CreateCompositionSurface(wr::DeviceIntSize aSize,
                                                        bool aIsOpaque);
};

static inline bool operator==(const DCSurface::TileKey& a0,
                              const DCSurface::TileKey& a1) {
  return a0.mX == a1.mX && a0.mY == a1.mY;
}

}  // namespace wr
}  // namespace mozilla

#endif
