gfx/layers/d3d11/TextureD3D11.h
author Jean-Yves Avenard <jyavenard@mozilla.com>
Thu, 11 Apr 2019 12:38:49 +0000
changeset 469139 bfef6e9e74d53eaa3ef44f27498f31c2b5aee34f
parent 469134 884f5dc55873c9345ee945cd6d5427e8ab398071
child 469142 574e1da58e2e3d2a4a876028940b60cedb0232f6
permissions -rw-r--r--
Bug 1540581 - P11. Handle colorspace correctly with D3D11 images. r=mattwoodrow,nical Differential Revision: https://phabricator.services.mozilla.com/D26466

/* -*- 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_TEXTURED3D11_H
#define MOZILLA_GFX_TEXTURED3D11_H

#include <d3d11.h>
#include <vector>
#include "d3d9.h"
#include "gfxWindowsPlatform.h"
#include "mozilla/GfxMessageUtils.h"
#include "mozilla/Maybe.h"
#include "mozilla/gfx/2D.h"
#include "mozilla/layers/Compositor.h"
#include "mozilla/layers/SyncObject.h"
#include "mozilla/layers/TextureClient.h"
#include "mozilla/layers/TextureHost.h"

namespace mozilla {
namespace gl {
class GLBlitHelper;
}

namespace layers {

already_AddRefed<TextureHost> CreateTextureHostD3D11(
    const SurfaceDescriptor& aDesc, ISurfaceAllocator* aDeallocator,
    LayersBackend aBackend, TextureFlags aFlags);

class MOZ_RAII AutoTextureLock final {
 public:
  AutoTextureLock(IDXGIKeyedMutex* aMutex, HRESULT& aResult,
                  uint32_t aTimeout = 0);
  ~AutoTextureLock();

 private:
  RefPtr<IDXGIKeyedMutex> mMutex;
  HRESULT mResult;
};

class CompositorD3D11;

class DXGITextureData : public TextureData {
 public:
  void FillInfo(TextureData::Info& aInfo) const override;

  bool SerializeSpecific(SurfaceDescriptorD3D10* aOutDesc);
  bool Serialize(SurfaceDescriptor& aOutDescrptor) override;
  void GetSubDescriptor(GPUVideoSubDescriptor* aOutDesc) override;

  static DXGITextureData* Create(gfx::IntSize aSize, gfx::SurfaceFormat aFormat,
                                 TextureAllocationFlags aFlags);

  gfx::YUVColorSpace GetYUVColorSpace() const { return mYUVColorSpace; }
  void SetYUVColorSpace(gfx::YUVColorSpace aColorSpace) {
    mYUVColorSpace = aColorSpace;
  }

  gfx::IntSize GetSize() const { return mSize; }
  gfx::SurfaceFormat GetSurfaceFormat() const { return mFormat; }

 protected:
  bool PrepareDrawTargetInLock(OpenMode aMode);

  DXGITextureData(gfx::IntSize aSize, gfx::SurfaceFormat aFormat,
                  bool aNeedsClear, bool aNeedsClearWhite,
                  bool aIsForOutOfBandContent);

  virtual void GetDXGIResource(IDXGIResource** aOutResource) = 0;

  // Hold on to the DrawTarget because it is expensive to create one each
  // ::Lock.
  RefPtr<gfx::DrawTarget> mDrawTarget;
  gfx::IntSize mSize;
  gfx::SurfaceFormat mFormat;
  gfx::YUVColorSpace mYUVColorSpace = gfx::YUVColorSpace::UNKNOWN;
  bool mNeedsClear;
  bool mNeedsClearWhite;
  bool mHasSynchronization;
  bool mIsForOutOfBandContent;
};

class D3D11TextureData : public DXGITextureData {
 public:
  // If aDevice is null, use one provided by gfxWindowsPlatform.
  static DXGITextureData* Create(gfx::IntSize aSize, gfx::SurfaceFormat aFormat,
                                 TextureAllocationFlags aAllocFlags,
                                 ID3D11Device* aDevice = nullptr);
  static DXGITextureData* Create(gfx::SourceSurface* aSurface,
                                 TextureAllocationFlags aAllocFlags,
                                 ID3D11Device* aDevice = nullptr);

  bool UpdateFromSurface(gfx::SourceSurface* aSurface) override;

  bool Lock(OpenMode aMode) override;

  void Unlock() override;

  already_AddRefed<gfx::DrawTarget> BorrowDrawTarget() override;

  TextureData* CreateSimilar(LayersIPCChannel* aAllocator,
                             LayersBackend aLayersBackend, TextureFlags aFlags,
                             TextureAllocationFlags aAllocFlags) const override;

  void SyncWithObject(SyncObjectClient* aSyncObject) override;

  ID3D11Texture2D* GetD3D11Texture() { return mTexture; }

  void Deallocate(LayersIPCChannel* aAllocator) override;

  D3D11TextureData* AsD3D11TextureData() override { return this; }

  TextureAllocationFlags GetTextureAllocationFlags() const {
    return mAllocationFlags;
  }

  virtual ~D3D11TextureData();

 protected:
  D3D11TextureData(ID3D11Texture2D* aTexture, gfx::IntSize aSize,
                   gfx::SurfaceFormat aFormat, TextureAllocationFlags aFlags);

  void GetDXGIResource(IDXGIResource** aOutResource) override;

  static DXGITextureData* Create(gfx::IntSize aSize, gfx::SurfaceFormat aFormat,
                                 gfx::SourceSurface* aSurface,
                                 TextureAllocationFlags aAllocFlags,
                                 ID3D11Device* aDevice = nullptr);

  RefPtr<ID3D11Texture2D> mTexture;
  const TextureAllocationFlags mAllocationFlags;
};

class DXGIYCbCrTextureData : public TextureData {
  friend class gl::GLBlitHelper;

 public:
  static DXGIYCbCrTextureData* Create(
      IDirect3DTexture9* aTextureY, IDirect3DTexture9* aTextureCb,
      IDirect3DTexture9* aTextureCr, HANDLE aHandleY, HANDLE aHandleCb,
      HANDLE aHandleCr, const gfx::IntSize& aSize, const gfx::IntSize& aSizeY,
      const gfx::IntSize& aSizeCbCr, gfx::ColorDepth aColorDepth,
      gfx::YUVColorSpace aYUVColorSpace);

  static DXGIYCbCrTextureData* Create(
      ID3D11Texture2D* aTextureCb, ID3D11Texture2D* aTextureY,
      ID3D11Texture2D* aTextureCr, const gfx::IntSize& aSize,
      const gfx::IntSize& aSizeY, const gfx::IntSize& aSizeCbCr,
      gfx::ColorDepth aColorDepth, gfx::YUVColorSpace aYUVColorSpace);

  bool Lock(OpenMode) override { return true; }

  void Unlock() override {}

  void FillInfo(TextureData::Info& aInfo) const override;

  void SerializeSpecific(SurfaceDescriptorDXGIYCbCr* aOutDesc);
  bool Serialize(SurfaceDescriptor& aOutDescriptor) override;
  void GetSubDescriptor(GPUVideoSubDescriptor* aOutDesc) override;

  already_AddRefed<gfx::DrawTarget> BorrowDrawTarget() override {
    return nullptr;
  }

  void Deallocate(LayersIPCChannel* aAllocator) override;

  bool UpdateFromSurface(gfx::SourceSurface*) override { return false; }

  TextureFlags GetTextureFlags() const override {
    return TextureFlags::DEALLOCATE_MAIN_THREAD;
  }

  DXGIYCbCrTextureData* AsDXGIYCbCrTextureData() override { return this; }

  gfx::IntSize GetYSize() const { return mSizeY; }

  gfx::IntSize GetCbCrSize() const { return mSizeCbCr; }

  gfx::ColorDepth GetColorDepth() const { return mColorDepth; }

  gfx::YUVColorSpace GetYUVColorSpace() const { return mYUVColorSpace; }

  ID3D11Texture2D* GetD3D11Texture(size_t index) {
    return mD3D11Textures[index];
  }

 protected:
  RefPtr<ID3D11Texture2D> mD3D11Textures[3];
  RefPtr<IDirect3DTexture9> mD3D9Textures[3];
  HANDLE mHandles[3];
  gfx::IntSize mSize;
  gfx::IntSize mSizeY;
  gfx::IntSize mSizeCbCr;
  gfx::ColorDepth mColorDepth;
  gfx::YUVColorSpace mYUVColorSpace;
};

/**
 * TextureSource that provides with the necessary APIs to be composited by a
 * CompositorD3D11.
 */
class TextureSourceD3D11 {
 public:
  TextureSourceD3D11() : mFormatOverride(DXGI_FORMAT_UNKNOWN) {}
  virtual ~TextureSourceD3D11() = default;

  virtual ID3D11Texture2D* GetD3D11Texture() const { return mTexture; }
  virtual ID3D11ShaderResourceView* GetShaderResourceView();

 protected:
  virtual gfx::IntSize GetSize() const { return mSize; }

  gfx::IntSize mSize;
  RefPtr<ID3D11Texture2D> mTexture;
  RefPtr<ID3D11ShaderResourceView> mSRV;
  DXGI_FORMAT mFormatOverride;
};

/**
 * A TextureSource that implements the DataTextureSource interface.
 * it can be used without a TextureHost and is able to upload texture data
 * from a gfx::DataSourceSurface.
 */
class DataTextureSourceD3D11 : public DataTextureSource,
                               public TextureSourceD3D11,
                               public BigImageIterator {
 public:
  /// Constructor allowing the texture to perform texture uploads.
  ///
  /// The texture can be used as an actual DataTextureSource.
  DataTextureSourceD3D11(ID3D11Device* aDevice, gfx::SurfaceFormat aFormat,
                         TextureFlags aFlags);

  /// Constructor for textures created around DXGI shared handles, disallowing
  /// texture uploads.
  ///
  /// The texture CANNOT be used as a DataTextureSource.
  DataTextureSourceD3D11(ID3D11Device* aDevice, gfx::SurfaceFormat aFormat,
                         ID3D11Texture2D* aTexture);

  DataTextureSourceD3D11(gfx::SurfaceFormat aFormat,
                         TextureSourceProvider* aProvider,
                         ID3D11Texture2D* aTexture);
  DataTextureSourceD3D11(gfx::SurfaceFormat aFormat,
                         TextureSourceProvider* aProvider, TextureFlags aFlags);

  virtual ~DataTextureSourceD3D11();

  const char* Name() const override { return "DataTextureSourceD3D11"; }

  // DataTextureSource

  bool Update(gfx::DataSourceSurface* aSurface,
              nsIntRegion* aDestRegion = nullptr,
              gfx::IntPoint* aSrcOffset = nullptr) override;

  // TextureSource

  TextureSourceD3D11* AsSourceD3D11() override { return this; }

  ID3D11Texture2D* GetD3D11Texture() const override;

  ID3D11ShaderResourceView* GetShaderResourceView() override;

  // Returns nullptr if this texture was created by a DXGI TextureHost.
  DataTextureSource* AsDataTextureSource() override {
    return mAllowTextureUploads ? this : nullptr;
  }

  void DeallocateDeviceData() override { mTexture = nullptr; }

  gfx::IntSize GetSize() const override { return mSize; }

  gfx::SurfaceFormat GetFormat() const override { return mFormat; }

  // BigImageIterator

  BigImageIterator* AsBigImageIterator() override {
    return mIsTiled ? this : nullptr;
  }

  size_t GetTileCount() override { return mTileTextures.size(); }

  bool NextTile() override { return (++mCurrentTile < mTileTextures.size()); }

  gfx::IntRect GetTileRect() override;

  void EndBigImageIteration() override { mIterating = false; }

  void BeginBigImageIteration() override {
    mIterating = true;
    mCurrentTile = 0;
  }

  RefPtr<TextureSource> ExtractCurrentTile() override;

  void Reset();

 protected:
  gfx::IntRect GetTileRect(uint32_t aIndex) const;

  std::vector<RefPtr<ID3D11Texture2D> > mTileTextures;
  std::vector<RefPtr<ID3D11ShaderResourceView> > mTileSRVs;
  RefPtr<ID3D11Device> mDevice;
  gfx::SurfaceFormat mFormat;
  TextureFlags mFlags;
  uint32_t mCurrentTile;
  bool mIsTiled;
  bool mIterating;
  // Sadly, the code was originally organized so that this class is used both in
  // the cases where we want to perform texture uploads through the
  // DataTextureSource interface, and the cases where we wrap the texture around
  // an existing DXGI handle in which case we should not use it as a
  // DataTextureSource. This member differentiates the two scenarios. When it is
  // false the texture "pretends" to not be a DataTextureSource.
  bool mAllowTextureUploads;
};

/**
 * A TextureHost for shared D3D11 textures.
 */
class DXGITextureHostD3D11 : public TextureHost {
 public:
  DXGITextureHostD3D11(TextureFlags aFlags,
                       const SurfaceDescriptorD3D10& aDescriptor);

  bool BindTextureSource(CompositableTextureSourceRef& aTexture) override;
  bool AcquireTextureSource(CompositableTextureSourceRef& aTexture) override;

  void DeallocateDeviceData() override {}

  void SetTextureSourceProvider(TextureSourceProvider* aProvider) override;

  gfx::SurfaceFormat GetFormat() const override { return mFormat; }

  bool Lock() override;
  void Unlock() override;

  bool LockWithoutCompositor() override;
  void UnlockWithoutCompositor() override;

  gfx::IntSize GetSize() const override { return mSize; }
  gfx::YUVColorSpace GetYUVColorSpace() const override {
    return mYUVColorSpace;
  }

  already_AddRefed<gfx::DataSourceSurface> GetAsSurface() override;

  void CreateRenderTexture(
      const wr::ExternalImageId& aExternalImageId) override;

  uint32_t NumSubTextures() const override;

  void PushResourceUpdates(wr::TransactionBuilder& aResources,
                           ResourceUpdateOp aOp,
                           const Range<wr::ImageKey>& aImageKeys,
                           const wr::ExternalImageId& aExtID) override;

  void PushDisplayItems(wr::DisplayListBuilder& aBuilder,
                        const wr::LayoutRect& aBounds,
                        const wr::LayoutRect& aClip, wr::ImageRendering aFilter,
                        const Range<wr::ImageKey>& aImageKeys) override;

  bool SupportsWrNativeTexture() override { return true; }

 protected:
  bool LockInternal();
  void UnlockInternal();

  bool EnsureTextureSource();

  RefPtr<ID3D11Device> GetDevice();

  bool EnsureTexture();

  RefPtr<ID3D11Device> mDevice;
  RefPtr<ID3D11Texture2D> mTexture;
  RefPtr<DataTextureSourceD3D11> mTextureSource;
  gfx::IntSize mSize;
  WindowsHandle mHandle;
  gfx::SurfaceFormat mFormat;
  const gfx::YUVColorSpace mYUVColorSpace;
  bool mIsLocked;
};

class DXGIYCbCrTextureHostD3D11 : public TextureHost {
 public:
  DXGIYCbCrTextureHostD3D11(TextureFlags aFlags,
                            const SurfaceDescriptorDXGIYCbCr& aDescriptor);

  bool BindTextureSource(CompositableTextureSourceRef& aTexture) override;
  bool AcquireTextureSource(CompositableTextureSourceRef& aTexture) override;

  void DeallocateDeviceData() override {}

  void SetTextureSourceProvider(TextureSourceProvider* aProvider) override;

  gfx::SurfaceFormat GetFormat() const override {
    return gfx::SurfaceFormat::YUV;
  }

  gfx::ColorDepth GetColorDepth() const override { return mColorDepth; }

  gfx::YUVColorSpace GetYUVColorSpace() const override {
    return mYUVColorSpace;
  }

  bool Lock() override;

  void Unlock() override;

  gfx::IntSize GetSize() const override { return mSize; }

  already_AddRefed<gfx::DataSourceSurface> GetAsSurface() override {
    return nullptr;
  }

  void CreateRenderTexture(
      const wr::ExternalImageId& aExternalImageId) override;

  uint32_t NumSubTextures() const override;

  void PushResourceUpdates(wr::TransactionBuilder& aResources,
                           ResourceUpdateOp aOp,
                           const Range<wr::ImageKey>& aImageKeys,
                           const wr::ExternalImageId& aExtID) override;

  void PushDisplayItems(wr::DisplayListBuilder& aBuilder,
                        const wr::LayoutRect& aBounds,
                        const wr::LayoutRect& aClip, wr::ImageRendering aFilter,
                        const Range<wr::ImageKey>& aImageKeys) override;

  bool SupportsWrNativeTexture() override { return true; }

 private:
  bool EnsureTextureSource();

 protected:
  RefPtr<ID3D11Device> GetDevice();

  bool EnsureTexture();

  RefPtr<ID3D11Texture2D> mTextures[3];
  RefPtr<DataTextureSourceD3D11> mTextureSources[3];

  gfx::IntSize mSize;
  gfx::IntSize mSizeCbCr;
  WindowsHandle mHandles[3];
  bool mIsLocked;
  gfx::ColorDepth mColorDepth;
  gfx::YUVColorSpace mYUVColorSpace;
};

class CompositingRenderTargetD3D11 : public CompositingRenderTarget,
                                     public TextureSourceD3D11 {
 public:
  CompositingRenderTargetD3D11(
      ID3D11Texture2D* aTexture, const gfx::IntPoint& aOrigin,
      DXGI_FORMAT aFormatOverride = DXGI_FORMAT_UNKNOWN);

  const char* Name() const override { return "CompositingRenderTargetD3D11"; }

  TextureSourceD3D11* AsSourceD3D11() override { return this; }

  void BindRenderTarget(ID3D11DeviceContext* aContext);

  gfx::IntSize GetSize() const override;

  void SetSize(const gfx::IntSize& aSize) { mSize = aSize; }

 private:
  friend class CompositorD3D11;
  RefPtr<ID3D11RenderTargetView> mRTView;
};

class SyncObjectD3D11Host : public SyncObjectHost {
 public:
  explicit SyncObjectD3D11Host(ID3D11Device* aDevice);

  bool Init() override;

  SyncHandle GetSyncHandle() override;

  bool Synchronize(bool aFallible) override;

  IDXGIKeyedMutex* GetKeyedMutex() { return mKeyedMutex.get(); };

 private:
  virtual ~SyncObjectD3D11Host() = default;

  SyncHandle mSyncHandle;
  RefPtr<ID3D11Device> mDevice;
  RefPtr<IDXGIResource> mSyncTexture;
  RefPtr<IDXGIKeyedMutex> mKeyedMutex;
};

class SyncObjectD3D11Client : public SyncObjectClient {
 public:
  SyncObjectD3D11Client(SyncHandle aSyncHandle, ID3D11Device* aDevice);

  bool Synchronize(bool aFallible) override;

  bool IsSyncObjectValid() override;

  SyncType GetSyncType() override { return SyncType::D3D11; }

  void RegisterTexture(ID3D11Texture2D* aTexture);

 private:
  bool Init(bool aFallible);

  SyncHandle mSyncHandle;
  RefPtr<ID3D11Device> mDevice;
  RefPtr<ID3D11Texture2D> mSyncTexture;
  RefPtr<IDXGIKeyedMutex> mKeyedMutex;
  std::vector<ID3D11Texture2D*> mSyncedTextures;
  Mutex mSyncLock;
};

inline uint32_t GetMaxTextureSizeForFeatureLevel(
    D3D_FEATURE_LEVEL aFeatureLevel) {
  int32_t maxTextureSize;
  switch (aFeatureLevel) {
    case D3D_FEATURE_LEVEL_11_1:
    case D3D_FEATURE_LEVEL_11_0:
      maxTextureSize = D3D11_REQ_TEXTURE2D_U_OR_V_DIMENSION;
      break;
    case D3D_FEATURE_LEVEL_10_1:
    case D3D_FEATURE_LEVEL_10_0:
      maxTextureSize = D3D10_REQ_TEXTURE2D_U_OR_V_DIMENSION;
      break;
    case D3D_FEATURE_LEVEL_9_3:
      maxTextureSize = D3D_FL9_3_REQ_TEXTURE2D_U_OR_V_DIMENSION;
      break;
    default:
      maxTextureSize = D3D_FL9_1_REQ_TEXTURE2D_U_OR_V_DIMENSION;
  }
  return maxTextureSize;
}

uint32_t GetMaxTextureSizeFromDevice(ID3D11Device* aDevice);
void ReportTextureMemoryUsage(ID3D11Texture2D* aTexture, size_t aBytes);

class AutoLockD3D11Texture {
 public:
  explicit AutoLockD3D11Texture(ID3D11Texture2D* aTexture);
  ~AutoLockD3D11Texture();

 private:
  RefPtr<IDXGIKeyedMutex> mMutex;
};

class D3D11MTAutoEnter {
 public:
  explicit D3D11MTAutoEnter(already_AddRefed<ID3D10Multithread> aMT)
      : mMT(aMT) {
    if (mMT) {
      mMT->Enter();
    }
  }
  ~D3D11MTAutoEnter() {
    if (mMT) {
      mMT->Leave();
    }
  }

 private:
  RefPtr<ID3D10Multithread> mMT;
};

}  // namespace layers
}  // namespace mozilla

#endif /* MOZILLA_GFX_TEXTURED3D11_H */