gfx/layers/d3d11/CompositorD3D11.cpp
author Phil Ringnalda <philringnalda@gmail.com>
Wed, 29 Jun 2016 19:35:24 -0700
changeset 303220 48bd14a01b55464215c0bfe973f5657a7b5b7f9c
parent 303196 99d1da1293b7af8a5a42612a103d81b3b7edfc22
child 303383 0cbb330c02c75657c3f4f9265d705217bb005c5b
permissions -rw-r--r--
Back out 7 changesets (bug 1281998) for Windows Marionette crashes Backed out changeset d806fac2c856 (bug 1281998) Backed out changeset b8d4fedfd7eb (bug 1281998) Backed out changeset a72929c0c3ec (bug 1281998) Backed out changeset 74198f88fa37 (bug 1281998) Backed out changeset 54a0e73f6906 (bug 1281998) Backed out changeset 99d1da1293b7 (bug 1281998) Backed out changeset a5a9585754b1 (bug 1281998)

/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
 * 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/. */

#include "CompositorD3D11.h"

#include "TextureD3D11.h"
#include "CompositorD3D11Shaders.h"
#include "CompositorD3D11ShadersVR.h"

#include "gfxWindowsPlatform.h"
#include "nsIWidget.h"
#include "nsIGfxInfo.h"
#include "mozilla/layers/ImageHost.h"
#include "mozilla/layers/ContentHost.h"
#include "mozilla/layers/Effects.h"
#include "nsWindowsHelpers.h"
#include "gfxPrefs.h"
#include "gfxConfig.h"
#include "gfxCrashReporterUtils.h"
#include "gfxVR.h"
#include "mozilla/gfx/StackArray.h"
#include "mozilla/Services.h"
#include "mozilla/widget/WinCompositorWidgetProxy.h"

#include "mozilla/EnumeratedArray.h"
#include "mozilla/Telemetry.h"
#include "BlendShaderConstants.h"

#include <dxgi1_2.h>

namespace mozilla {

using namespace gfx;

namespace layers {

struct Vertex
{
    float position[2];
};

// {1E4D7BEB-D8EC-4A0B-BF0A-63E6DE129425}
static const GUID sDeviceAttachmentsD3D11 =
{ 0x1e4d7beb, 0xd8ec, 0x4a0b, { 0xbf, 0xa, 0x63, 0xe6, 0xde, 0x12, 0x94, 0x25 } };
// {88041664-C835-4AA8-ACB8-7EC832357ED8}
static const GUID sLayerManagerCount =
{ 0x88041664, 0xc835, 0x4aa8, { 0xac, 0xb8, 0x7e, 0xc8, 0x32, 0x35, 0x7e, 0xd8 } };

const FLOAT sBlendFactor[] = { 0, 0, 0, 0 };

namespace TexSlot {
  static const int RGB = 0;
  static const int Y = 1;
  static const int Cb = 2;
  static const int Cr = 3;
  static const int RGBWhite = 4;
  static const int Mask = 5;
  static const int Backdrop = 6;
}

struct DeviceAttachmentsD3D11
{
  DeviceAttachmentsD3D11(ID3D11Device* device)
   : mSyncHandle(0),
     mDevice(device),
     mInitOkay(true)
  {}

  bool CreateShaders();
  bool InitBlendShaders();
  bool InitSyncObject();

  typedef EnumeratedArray<MaskType, MaskType::NumMaskTypes, RefPtr<ID3D11VertexShader>>
          VertexShaderArray;
  typedef EnumeratedArray<MaskType, MaskType::NumMaskTypes, RefPtr<ID3D11PixelShader>>
          PixelShaderArray;

  RefPtr<ID3D11InputLayout> mInputLayout;
  RefPtr<ID3D11Buffer> mVertexBuffer;

  VertexShaderArray mVSQuadShader;
  VertexShaderArray mVSQuadBlendShader;
  PixelShaderArray mSolidColorShader;
  PixelShaderArray mRGBAShader;
  PixelShaderArray mRGBShader;
  PixelShaderArray mYCbCrShader;
  PixelShaderArray mComponentAlphaShader;
  PixelShaderArray mBlendShader;
  RefPtr<ID3D11Buffer> mPSConstantBuffer;
  RefPtr<ID3D11Buffer> mVSConstantBuffer;
  RefPtr<ID3D11RasterizerState> mRasterizerState;
  RefPtr<ID3D11SamplerState> mLinearSamplerState;
  RefPtr<ID3D11SamplerState> mPointSamplerState;
  RefPtr<ID3D11BlendState> mPremulBlendState;
  RefPtr<ID3D11BlendState> mNonPremulBlendState;
  RefPtr<ID3D11BlendState> mComponentBlendState;
  RefPtr<ID3D11BlendState> mDisabledBlendState;
  RefPtr<IDXGIResource> mSyncTexture;
  HANDLE mSyncHandle;

  //
  // VR pieces
  //
  typedef EnumeratedArray<VRHMDType, VRHMDType::NumHMDTypes, RefPtr<ID3D11InputLayout>>
          VRDistortionInputLayoutArray;
  typedef EnumeratedArray<VRHMDType, VRHMDType::NumHMDTypes, RefPtr<ID3D11VertexShader>>
          VRVertexShaderArray;
  typedef EnumeratedArray<VRHMDType, VRHMDType::NumHMDTypes, RefPtr<ID3D11PixelShader>>
          VRPixelShaderArray;

  VRDistortionInputLayoutArray mVRDistortionInputLayout;
  VRVertexShaderArray mVRDistortionVS;
  VRPixelShaderArray mVRDistortionPS;

  RefPtr<ID3D11Buffer> mVRDistortionConstants;

  // These will be created/filled in as needed during rendering whenever the configuration
  // changes.
  VRHMDConfiguration mVRConfiguration;
  RefPtr<ID3D11Buffer> mVRDistortionVertices[2]; // one for each eye
  RefPtr<ID3D11Buffer> mVRDistortionIndices[2];
  uint32_t mVRDistortionIndexCount[2];

private:
  void InitVertexShader(const ShaderBytes& aShader, VertexShaderArray& aArray, MaskType aMaskType) {
    InitVertexShader(aShader, getter_AddRefs(aArray[aMaskType]));
  }
  void InitPixelShader(const ShaderBytes& aShader, PixelShaderArray& aArray, MaskType aMaskType) {
    InitPixelShader(aShader, getter_AddRefs(aArray[aMaskType]));
  }
  void InitVertexShader(const ShaderBytes& aShader, ID3D11VertexShader** aOut) {
    if (!mInitOkay) {
      return;
    }
    if (Failed(mDevice->CreateVertexShader(aShader.mData, aShader.mLength, nullptr, aOut), "create vs")) {
      mInitOkay = false;
    }
  }
  void InitPixelShader(const ShaderBytes& aShader, ID3D11PixelShader** aOut) {
    if (!mInitOkay) {
      return;
    }
    if (Failed(mDevice->CreatePixelShader(aShader.mData, aShader.mLength, nullptr, aOut), "create ps")) {
      mInitOkay = false;
    }
  }

  bool Failed(HRESULT hr, const char* aContext) {
    if (SUCCEEDED(hr))
      return false;

    gfxCriticalNote << "[D3D11] " << aContext << " failed: " << hexa(hr);
    return true;
  }

  // Only used during initialization.
  RefPtr<ID3D11Device> mDevice;
  bool mInitOkay;
};

CompositorD3D11::CompositorD3D11(CompositorBridgeParent* aParent, widget::CompositorWidgetProxy* aWidget)
  : Compositor(aWidget, aParent)
  , mAttachments(nullptr)
  , mHwnd(nullptr)
  , mDisableSequenceForNextFrame(false)
  , mVerifyBuffersFailed(false)
{
}

CompositorD3D11::~CompositorD3D11()
{
  if (mDevice) {
    int referenceCount = 0;
    UINT size = sizeof(referenceCount);
    HRESULT hr = mDevice->GetPrivateData(sLayerManagerCount, &size, &referenceCount);
    NS_ASSERTION(SUCCEEDED(hr), "Reference count not found on device.");
    referenceCount--;
    mDevice->SetPrivateData(sLayerManagerCount,
                            sizeof(referenceCount),
                            &referenceCount);

    if (!referenceCount) {
      DeviceAttachmentsD3D11 *attachments;
      size = sizeof(attachments);
      mDevice->GetPrivateData(sDeviceAttachmentsD3D11, &size, &attachments);
      // No LayerManagers left for this device. Clear out interfaces stored
      // which hold a reference to the device.
      mDevice->SetPrivateData(sDeviceAttachmentsD3D11, 0, nullptr);

      delete attachments;
    }
  }
}

bool
CompositorD3D11::Initialize()
{
  ScopedGfxFeatureReporter reporter("D3D11 Layers");

  MOZ_ASSERT(gfxConfig::IsEnabled(Feature::D3D11_COMPOSITING));

  HRESULT hr;

  if (!gfxWindowsPlatform::GetPlatform()->GetD3D11Device(&mDevice)) {
    return false;
  }

  mDevice->GetImmediateContext(getter_AddRefs(mContext));

  if (!mContext) {
    gfxCriticalNote << "[D3D11] failed to get immediate context";
    return false;
  }

  mFeatureLevel = mDevice->GetFeatureLevel();

  mHwnd = mWidget->AsWindowsProxy()->GetHwnd();

  memset(&mVSConstants, 0, sizeof(VertexShaderConstants));

  int referenceCount = 0;
  UINT size = sizeof(referenceCount);
  // If this isn't there yet it'll fail, count will remain 0, which is correct.
  mDevice->GetPrivateData(sLayerManagerCount, &size, &referenceCount);
  referenceCount++;
  mDevice->SetPrivateData(sLayerManagerCount,
                          sizeof(referenceCount),
                          &referenceCount);

  size = sizeof(DeviceAttachmentsD3D11*);
  if (FAILED(mDevice->GetPrivateData(sDeviceAttachmentsD3D11,
                                     &size,
                                     &mAttachments))) {
    mAttachments = new DeviceAttachmentsD3D11(mDevice);
    mDevice->SetPrivateData(sDeviceAttachmentsD3D11,
                            sizeof(mAttachments),
                            &mAttachments);

    D3D11_INPUT_ELEMENT_DESC layout[] =
    {
      { "POSITION", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 },
    };

    hr = mDevice->CreateInputLayout(layout,
                                    sizeof(layout) / sizeof(D3D11_INPUT_ELEMENT_DESC),
                                    LayerQuadVS,
                                    sizeof(LayerQuadVS),
                                    getter_AddRefs(mAttachments->mInputLayout));

    if (Failed(hr, "CreateInputLayout")) {
      return false;
    }

    Vertex vertices[] = { {{0.0, 0.0}}, {{1.0, 0.0}}, {{0.0, 1.0}}, {{1.0, 1.0}} };
    CD3D11_BUFFER_DESC bufferDesc(sizeof(vertices), D3D11_BIND_VERTEX_BUFFER);
    D3D11_SUBRESOURCE_DATA data;
    data.pSysMem = (void*)vertices;

    hr = mDevice->CreateBuffer(&bufferDesc, &data, getter_AddRefs(mAttachments->mVertexBuffer));

    if (Failed(hr, "create vertex buffer")) {
      return false;
    }

    if (!mAttachments->CreateShaders()) {
      return false;
    }

    CD3D11_BUFFER_DESC cBufferDesc(sizeof(VertexShaderConstants),
                                   D3D11_BIND_CONSTANT_BUFFER,
                                   D3D11_USAGE_DYNAMIC,
                                   D3D11_CPU_ACCESS_WRITE);

    hr = mDevice->CreateBuffer(&cBufferDesc, nullptr, getter_AddRefs(mAttachments->mVSConstantBuffer));
    if (Failed(hr, "create vs buffer")) {
      return false;
    }

    cBufferDesc.ByteWidth = sizeof(PixelShaderConstants);
    hr = mDevice->CreateBuffer(&cBufferDesc, nullptr, getter_AddRefs(mAttachments->mPSConstantBuffer));
    if (Failed(hr, "create ps buffer")) {
      return false;
    }

    CD3D11_RASTERIZER_DESC rastDesc(D3D11_DEFAULT);
    rastDesc.CullMode = D3D11_CULL_NONE;
    rastDesc.ScissorEnable = TRUE;

    hr = mDevice->CreateRasterizerState(&rastDesc, getter_AddRefs(mAttachments->mRasterizerState));
    if (Failed(hr, "create rasterizer")) {
      return false;
    }

    CD3D11_SAMPLER_DESC samplerDesc(D3D11_DEFAULT);
    hr = mDevice->CreateSamplerState(&samplerDesc, getter_AddRefs(mAttachments->mLinearSamplerState));
    if (Failed(hr, "create linear sampler")) {
      return false;
    }

    samplerDesc.Filter = D3D11_FILTER_MIN_MAG_MIP_POINT;
    hr = mDevice->CreateSamplerState(&samplerDesc, getter_AddRefs(mAttachments->mPointSamplerState));
    if (Failed(hr, "create point sampler")) {
      return false;
    }

    CD3D11_BLEND_DESC blendDesc(D3D11_DEFAULT);
    D3D11_RENDER_TARGET_BLEND_DESC rtBlendPremul = {
      TRUE,
      D3D11_BLEND_ONE, D3D11_BLEND_INV_SRC_ALPHA, D3D11_BLEND_OP_ADD,
      D3D11_BLEND_ONE, D3D11_BLEND_INV_SRC_ALPHA, D3D11_BLEND_OP_ADD,
      D3D11_COLOR_WRITE_ENABLE_ALL
    };
    blendDesc.RenderTarget[0] = rtBlendPremul;
    hr = mDevice->CreateBlendState(&blendDesc, getter_AddRefs(mAttachments->mPremulBlendState));
    if (Failed(hr, "create pm blender")) {
      return false;
    }

    D3D11_RENDER_TARGET_BLEND_DESC rtBlendNonPremul = {
      TRUE,
      D3D11_BLEND_SRC_ALPHA, D3D11_BLEND_INV_SRC_ALPHA, D3D11_BLEND_OP_ADD,
      D3D11_BLEND_ONE, D3D11_BLEND_INV_SRC_ALPHA, D3D11_BLEND_OP_ADD,
      D3D11_COLOR_WRITE_ENABLE_ALL
    };
    blendDesc.RenderTarget[0] = rtBlendNonPremul;
    hr = mDevice->CreateBlendState(&blendDesc, getter_AddRefs(mAttachments->mNonPremulBlendState));
    if (Failed(hr, "create npm blender")) {
      return false;
    }

    if (gfxPrefs::ComponentAlphaEnabled()) {
      D3D11_RENDER_TARGET_BLEND_DESC rtBlendComponent = {
        TRUE,
        D3D11_BLEND_ONE,
        D3D11_BLEND_INV_SRC1_COLOR,
        D3D11_BLEND_OP_ADD,
        D3D11_BLEND_ONE,
        D3D11_BLEND_INV_SRC_ALPHA,
        D3D11_BLEND_OP_ADD,
        D3D11_COLOR_WRITE_ENABLE_ALL
      };
      blendDesc.RenderTarget[0] = rtBlendComponent;
      hr = mDevice->CreateBlendState(&blendDesc, getter_AddRefs(mAttachments->mComponentBlendState));
      if (Failed(hr, "create component blender")) {
        return false;
      }
    }

    D3D11_RENDER_TARGET_BLEND_DESC rtBlendDisabled = {
      FALSE,
      D3D11_BLEND_SRC_ALPHA, D3D11_BLEND_INV_SRC_ALPHA, D3D11_BLEND_OP_ADD,
      D3D11_BLEND_ONE, D3D11_BLEND_INV_SRC_ALPHA, D3D11_BLEND_OP_ADD,
      D3D11_COLOR_WRITE_ENABLE_ALL
    };
    blendDesc.RenderTarget[0] = rtBlendDisabled;
    hr = mDevice->CreateBlendState(&blendDesc, getter_AddRefs(mAttachments->mDisabledBlendState));
    if (Failed(hr, "create null blender")) {
      return false;
    }

    if (!mAttachments->InitSyncObject()) {
      return false;
    }
    
    //
    // VR additions
    //
    D3D11_INPUT_ELEMENT_DESC vrlayout[] =
    {
      { "POSITION", 0, DXGI_FORMAT_R32G32_FLOAT,       0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 },
      { "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT,       0, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA, 0 },
      { "TEXCOORD", 1, DXGI_FORMAT_R32G32_FLOAT,       0, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA, 0 },
      { "TEXCOORD", 2, DXGI_FORMAT_R32G32_FLOAT,       0, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA, 0 },
      { "COLOR",    0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA, 0 },
    };

    hr = mDevice->CreateInputLayout(vrlayout,
                                    sizeof(vrlayout) / sizeof(D3D11_INPUT_ELEMENT_DESC),
                                    Oculus050VRDistortionVS,
                                    sizeof(Oculus050VRDistortionVS),
                                    getter_AddRefs(mAttachments->mVRDistortionInputLayout[VRHMDType::Oculus050]));

    // XXX shared for now, rename
    mAttachments->mVRDistortionInputLayout[VRHMDType::Cardboard] =
      mAttachments->mVRDistortionInputLayout[VRHMDType::Oculus050];

    cBufferDesc.ByteWidth = sizeof(gfx::VRDistortionConstants);
    hr = mDevice->CreateBuffer(&cBufferDesc, nullptr, getter_AddRefs(mAttachments->mVRDistortionConstants));
    if (Failed(hr, "create vr buffer ")) {
      return false;
    }
  }

  RefPtr<IDXGIDevice> dxgiDevice;
  RefPtr<IDXGIAdapter> dxgiAdapter;

  mDevice->QueryInterface(dxgiDevice.StartAssignment());
  dxgiDevice->GetAdapter(getter_AddRefs(dxgiAdapter));

  {
    RefPtr<IDXGIFactory> dxgiFactory;
    dxgiAdapter->GetParent(IID_PPV_ARGS(dxgiFactory.StartAssignment()));

    DXGI_SWAP_CHAIN_DESC swapDesc;
    ::ZeroMemory(&swapDesc, sizeof(swapDesc));
    swapDesc.BufferDesc.Width = 0;
    swapDesc.BufferDesc.Height = 0;
    swapDesc.BufferDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
    swapDesc.BufferDesc.RefreshRate.Numerator = 60;
    swapDesc.BufferDesc.RefreshRate.Denominator = 1;
    swapDesc.SampleDesc.Count = 1;
    swapDesc.SampleDesc.Quality = 0;
    swapDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
    swapDesc.BufferCount = 1;
    swapDesc.OutputWindow = mHwnd;
    swapDesc.Windowed = TRUE;
    swapDesc.Flags = 0;
    swapDesc.SwapEffect = DXGI_SWAP_EFFECT_SEQUENTIAL;


    /**
     * Create a swap chain, this swap chain will contain the backbuffer for
     * the window we draw to. The front buffer is the full screen front
     * buffer.
     */
    hr = dxgiFactory->CreateSwapChain(dxgiDevice, &swapDesc, getter_AddRefs(mSwapChain));
    if (Failed(hr, "create swap chain")) {
     return false;
    }

    // We need this because we don't want DXGI to respond to Alt+Enter.
    dxgiFactory->MakeWindowAssociation(swapDesc.OutputWindow,
                                       DXGI_MWA_NO_WINDOW_CHANGES);
  }

  if (!mWidget->InitCompositor(this)) {
    return false;
  }

  reporter.SetSuccessful();
  return true;
}

already_AddRefed<DataTextureSource>
CompositorD3D11::CreateDataTextureSource(TextureFlags aFlags)
{
  RefPtr<DataTextureSource> result = new DataTextureSourceD3D11(gfx::SurfaceFormat::UNKNOWN,
                                                                this, aFlags);
  return result.forget();
}

TextureFactoryIdentifier
CompositorD3D11::GetTextureFactoryIdentifier()
{
  TextureFactoryIdentifier ident;
  ident.mMaxTextureSize = GetMaxTextureSize();
  ident.mParentProcessId = XRE_GetProcessType();
  ident.mParentBackend = LayersBackend::LAYERS_D3D11;
  ident.mSyncHandle = mAttachments->mSyncHandle;
  return ident;
}

bool
CompositorD3D11::CanUseCanvasLayerForSize(const gfx::IntSize& aSize)
{
  int32_t maxTextureSize = GetMaxTextureSize();

  if (aSize.width > maxTextureSize || aSize.height > maxTextureSize) {
    return false;
  }

  return true;
}

int32_t
CompositorD3D11::GetMaxTextureSize() const
{
  return GetMaxTextureSizeForFeatureLevel(mFeatureLevel);
}

already_AddRefed<CompositingRenderTarget>
CompositorD3D11::CreateRenderTarget(const gfx::IntRect& aRect,
                                    SurfaceInitMode aInit)
{
  MOZ_ASSERT(aRect.width != 0 && aRect.height != 0);

  if (aRect.width * aRect.height == 0) {
    return nullptr;
  }

  CD3D11_TEXTURE2D_DESC desc(DXGI_FORMAT_B8G8R8A8_UNORM, aRect.width, aRect.height, 1, 1,
                             D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_RENDER_TARGET);

  RefPtr<ID3D11Texture2D> texture;
  HRESULT hr = mDevice->CreateTexture2D(&desc, nullptr, getter_AddRefs(texture));
  if (FAILED(hr) || !texture) {
    gfxCriticalNote << "Failed in CreateRenderTarget " << hexa(hr);
    return nullptr;
  }

  RefPtr<CompositingRenderTargetD3D11> rt = new CompositingRenderTargetD3D11(texture, aRect.TopLeft());
  rt->SetSize(IntSize(aRect.width, aRect.height));

  if (aInit == INIT_MODE_CLEAR) {
    FLOAT clear[] = { 0, 0, 0, 0 };
    mContext->ClearRenderTargetView(rt->mRTView, clear);
  }

  return rt.forget();
}

RefPtr<ID3D11Texture2D>
CompositorD3D11::CreateTexture(const gfx::IntRect& aRect,
                               const CompositingRenderTarget* aSource,
                               const gfx::IntPoint& aSourcePoint)
{
  MOZ_ASSERT(aRect.width != 0 && aRect.height != 0);

  if (aRect.width * aRect.height == 0) {
    return nullptr;
  }

  CD3D11_TEXTURE2D_DESC desc(DXGI_FORMAT_B8G8R8A8_UNORM,
                             aRect.width, aRect.height, 1, 1,
                             D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_RENDER_TARGET);

  RefPtr<ID3D11Texture2D> texture;
  HRESULT hr = mDevice->CreateTexture2D(&desc, nullptr, getter_AddRefs(texture));

  if (FAILED(hr) || !texture) {
    gfxCriticalNote << "Failed in CreateRenderTargetFromSource " << hexa(hr);
    HandleError(hr);
    return nullptr;
  }

  if (aSource) {
    const CompositingRenderTargetD3D11* sourceD3D11 =
      static_cast<const CompositingRenderTargetD3D11*>(aSource);

    const IntSize& srcSize = sourceD3D11->GetSize();
    MOZ_ASSERT(srcSize.width >= 0 && srcSize.height >= 0,
               "render targets should have nonnegative sizes");

    IntRect srcRect(IntPoint(), srcSize);
    IntRect copyRect(aSourcePoint, aRect.Size());
    if (!srcRect.Contains(copyRect)) {
      NS_WARNING("Could not copy the whole copy rect from the render target");
    }

    copyRect = copyRect.Intersect(srcRect);

    if (!copyRect.IsEmpty()) {
      D3D11_BOX copyBox;
      copyBox.front = 0;
      copyBox.back = 1;
      copyBox.left = copyRect.x;
      copyBox.top = copyRect.y;
      copyBox.right = copyRect.XMost();
      copyBox.bottom = copyRect.YMost();

      mContext->CopySubresourceRegion(texture, 0,
                                      0, 0, 0,
                                      sourceD3D11->GetD3D11Texture(), 0,
                                      &copyBox);
    }
  }

  return texture;
}

already_AddRefed<CompositingRenderTarget>
CompositorD3D11::CreateRenderTargetFromSource(const gfx::IntRect &aRect,
                                              const CompositingRenderTarget* aSource,
                                              const gfx::IntPoint &aSourcePoint)
{
  RefPtr<ID3D11Texture2D> texture = CreateTexture(aRect, aSource, aSourcePoint);
  if (!texture) {
    return nullptr;
  }

  RefPtr<CompositingRenderTargetD3D11> rt =
    new CompositingRenderTargetD3D11(texture, aRect.TopLeft());
  rt->SetSize(aRect.Size());

  return rt.forget();
}

bool
CompositorD3D11::CopyBackdrop(const gfx::IntRect& aRect,
                              RefPtr<ID3D11Texture2D>* aOutTexture,
                              RefPtr<ID3D11ShaderResourceView>* aOutView)
{
  RefPtr<ID3D11Texture2D> texture = CreateTexture(aRect, mCurrentRT, aRect.TopLeft());
  if (!texture) {
    return false;
  }

  CD3D11_SHADER_RESOURCE_VIEW_DESC desc(D3D11_SRV_DIMENSION_TEXTURE2D, DXGI_FORMAT_B8G8R8A8_UNORM);

  RefPtr<ID3D11ShaderResourceView> srv;
  HRESULT hr = mDevice->CreateShaderResourceView(texture, &desc, getter_AddRefs(srv));
  if (FAILED(hr) || !srv) {
    return false;
  }

  *aOutTexture = texture.forget();
  *aOutView = srv.forget();
  return true;
}

void
CompositorD3D11::SetRenderTarget(CompositingRenderTarget* aRenderTarget)
{
  MOZ_ASSERT(aRenderTarget);
  CompositingRenderTargetD3D11* newRT =
    static_cast<CompositingRenderTargetD3D11*>(aRenderTarget);
  if (mCurrentRT != newRT) {
    mCurrentRT = newRT;
    mCurrentRT->BindRenderTarget(mContext);
  }

  if (newRT->HasComplexProjection()) {
    gfx::Matrix4x4 projection;
    bool depthEnable;
    float zNear, zFar;
    newRT->GetProjection(projection, depthEnable, zNear, zFar);
    PrepareViewport(newRT->GetSize(), projection, zNear, zFar);
  } else {
    PrepareViewport(newRT->GetSize());
  }
}

ID3D11PixelShader*
CompositorD3D11::GetPSForEffect(Effect* aEffect, MaskType aMaskType)
{
  switch (aEffect->mType) {
  case EffectTypes::SOLID_COLOR:
    return mAttachments->mSolidColorShader[aMaskType];
  case EffectTypes::RENDER_TARGET:
    return mAttachments->mRGBAShader[aMaskType];
  case EffectTypes::RGB: {
    SurfaceFormat format = static_cast<TexturedEffect*>(aEffect)->mTexture->GetFormat();
    return (format == SurfaceFormat::B8G8R8A8 || format == SurfaceFormat::R8G8B8A8)
           ? mAttachments->mRGBAShader[aMaskType]
           : mAttachments->mRGBShader[aMaskType];
  }
  case EffectTypes::YCBCR:
    return mAttachments->mYCbCrShader[aMaskType];
  case EffectTypes::COMPONENT_ALPHA:
    return mAttachments->mComponentAlphaShader[aMaskType];
  default:
    NS_WARNING("No shader to load");
    return nullptr;
  }
}

void
CompositorD3D11::ClearRect(const gfx::Rect& aRect)
{
  mContext->OMSetBlendState(mAttachments->mDisabledBlendState, sBlendFactor, 0xFFFFFFFF);

  Matrix4x4 identity;
  memcpy(&mVSConstants.layerTransform, &identity._11, 64);

  mVSConstants.layerQuad = aRect;
  mVSConstants.renderTargetOffset[0] = 0;
  mVSConstants.renderTargetOffset[1] = 0;
  mPSConstants.layerOpacity[0] = 1.0f;

  D3D11_RECT scissor;
  scissor.left = aRect.x;
  scissor.right = aRect.XMost();
  scissor.top = aRect.y;
  scissor.bottom = aRect.YMost();
  mContext->RSSetScissorRects(1, &scissor);
  mContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP);
  mContext->VSSetShader(mAttachments->mVSQuadShader[MaskType::MaskNone], nullptr, 0);

  mContext->PSSetShader(mAttachments->mSolidColorShader[MaskType::MaskNone], nullptr, 0);
  mPSConstants.layerColor[0] = 0;
  mPSConstants.layerColor[1] = 0;
  mPSConstants.layerColor[2] = 0;
  mPSConstants.layerColor[3] = 0;

  if (!UpdateConstantBuffers()) {
    NS_WARNING("Failed to update shader constant buffers");
    return;
  }

  mContext->Draw(4, 0);

  mContext->OMSetBlendState(mAttachments->mPremulBlendState, sBlendFactor, 0xFFFFFFFF);
}

void
CompositorD3D11::DrawVRDistortion(const gfx::Rect& aRect,
                                  const gfx::IntRect& aClipRect,
                                  const EffectChain& aEffectChain,
                                  gfx::Float aOpacity,
                                  const gfx::Matrix4x4& aTransform)
{
  MOZ_ASSERT(aEffectChain.mPrimaryEffect->mType == EffectTypes::VR_DISTORTION);

  if (aEffectChain.mSecondaryEffects[EffectTypes::MASK] ||
      aEffectChain.mSecondaryEffects[EffectTypes::BLEND_MODE])
  {
    NS_WARNING("DrawVRDistortion: ignoring secondary effect!");
  }

  HRESULT hr;

  EffectVRDistortion* vrEffect =
    static_cast<EffectVRDistortion*>(aEffectChain.mPrimaryEffect.get());

  TextureSourceD3D11* source = vrEffect->mTexture->AsSourceD3D11();

  VRHMDInfo* hmdInfo = vrEffect->mHMD;
  VRHMDType hmdType = hmdInfo->GetDeviceInfo().GetType();

  if (!mAttachments->mVRDistortionVS[hmdType] ||
      !mAttachments->mVRDistortionPS[hmdType])
  {
    NS_WARNING("No VS/PS for hmd type for VR distortion!");
    return;
  }

  VRDistortionConstants shaderConstants;

  // do we need to recreate the VR buffers, since the config has changed?
  if (hmdInfo->GetConfiguration() != mAttachments->mVRConfiguration) {
    D3D11_SUBRESOURCE_DATA sdata = { 0 };
    CD3D11_BUFFER_DESC desc(0, D3D11_BIND_VERTEX_BUFFER, D3D11_USAGE_IMMUTABLE);

    // XXX as an optimization, we should really pack the indices and vertices for both eyes
    // into one buffer instead of needing one eye each.  Then we can just bind them once.
    for (uint32_t eye = 0; eye < 2; eye++) {
      const gfx::VRDistortionMesh& mesh = hmdInfo->GetDistortionMesh(eye);

      desc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
      desc.ByteWidth = mesh.mVertices.Length() * sizeof(gfx::VRDistortionVertex);
      sdata.pSysMem = mesh.mVertices.Elements();
      
      hr = mDevice->CreateBuffer(&desc, &sdata, getter_AddRefs(mAttachments->mVRDistortionVertices[eye]));
      if (FAILED(hr)) {
        NS_WARNING("CreateBuffer failed");
        return;
      }

      desc.BindFlags = D3D11_BIND_INDEX_BUFFER;
      desc.ByteWidth = mesh.mIndices.Length() * sizeof(uint16_t);
      sdata.pSysMem = mesh.mIndices.Elements();

      hr = mDevice->CreateBuffer(&desc, &sdata, getter_AddRefs(mAttachments->mVRDistortionIndices[eye]));
      if (FAILED(hr)) {
        NS_WARNING("CreateBuffer failed");
        return;
      }

      mAttachments->mVRDistortionIndexCount[eye] = mesh.mIndices.Length();
    }

    mAttachments->mVRConfiguration = hmdInfo->GetConfiguration();
  }

  // XXX do I need to set a scissor rect? Is this the right scissor rect?
  D3D11_RECT scissor;
  scissor.left = aClipRect.x;
  scissor.right = aClipRect.XMost();
  scissor.top = aClipRect.y;
  scissor.bottom = aClipRect.YMost();
  mContext->RSSetScissorRects(1, &scissor);

  // Triangle lists and same layout for both eyes
  mContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
  mContext->IASetInputLayout(mAttachments->mVRDistortionInputLayout[hmdType]);
  mContext->VSSetShader(mAttachments->mVRDistortionVS[hmdType], nullptr, 0);
  mContext->PSSetShader(mAttachments->mVRDistortionPS[hmdType], nullptr, 0);

  // This is the source texture SRV for the pixel shader
  ID3D11ShaderResourceView* srView = source->GetShaderResourceView();
  mContext->PSSetShaderResources(0, 1, &srView);

  Rect destRect = aTransform.TransformBounds(aRect);
  gfx::IntSize preDistortionSize = vrEffect->mRenderTarget->GetSize(); // XXX source->GetSize()
  gfx::Size vpSize = destRect.Size();

  ID3D11Buffer* vbuffer;
  UINT vsize, voffset;

  for (uint32_t eye = 0; eye < 2; eye++) {
    gfx::IntRect eyeViewport;
    eyeViewport.x = eye * preDistortionSize.width / 2;
    eyeViewport.y = 0;
    eyeViewport.width = preDistortionSize.width / 2;
    eyeViewport.height = preDistortionSize.height;

    hmdInfo->FillDistortionConstants(eye,
                                     preDistortionSize, eyeViewport,
                                     vpSize, destRect,
                                     shaderConstants);

    // D3D has clip space top-left as -1,1 so we need to flip the Y coordinate offset here
    shaderConstants.destinationScaleAndOffset[1] = - shaderConstants.destinationScaleAndOffset[1];

    // XXX I really want to write a templated helper for these next 4 lines
    D3D11_MAPPED_SUBRESOURCE resource;
    hr = mContext->Map(mAttachments->mVRDistortionConstants, 0, D3D11_MAP_WRITE_DISCARD, 0, &resource);
    if (FAILED(hr) || !resource.pData) {
      gfxCriticalError() << "Failed to map VRDistortionConstants. Result: " << hr;
      HandleError(hr);
      return;
    }
    *(gfx::VRDistortionConstants*)resource.pData = shaderConstants;
    mContext->Unmap(mAttachments->mVRDistortionConstants, 0);
    resource.pData = nullptr;

    // XXX is there a better way to change a bunch of these things from what they were set to
    // in BeginFrame/etc?
    vbuffer = mAttachments->mVRDistortionVertices[eye];
    vsize = sizeof(gfx::VRDistortionVertex);
    voffset = 0;
    mContext->IASetVertexBuffers(0, 1, &vbuffer, &vsize, &voffset);
    mContext->IASetIndexBuffer(mAttachments->mVRDistortionIndices[eye], DXGI_FORMAT_R16_UINT, 0);

    ID3D11Buffer* constBuf = mAttachments->mVRDistortionConstants;
    mContext->VSSetConstantBuffers(0, 1, &constBuf);

    mContext->DrawIndexed(mAttachments->mVRDistortionIndexCount[eye], 0, 0);
  }

  // restore previous configurations
  vbuffer = mAttachments->mVertexBuffer;
  vsize = sizeof(Vertex);
  voffset = 0;
  mContext->IASetVertexBuffers(0, 1, &vbuffer, &vsize, &voffset);
  mContext->IASetIndexBuffer(nullptr, DXGI_FORMAT_R16_UINT, 0);
  mContext->IASetInputLayout(mAttachments->mInputLayout);
}

static inline bool
EffectHasPremultipliedAlpha(Effect* aEffect)
{
  if (aEffect->mType == EffectTypes::RGB) {
    return static_cast<TexturedEffect*>(aEffect)->mPremultiplied;
  }
  return true;
}

static inline int
EffectToBlendLayerType(Effect* aEffect)
{
  switch (aEffect->mType) {
  case EffectTypes::SOLID_COLOR:
    return PS_LAYER_COLOR;
  case EffectTypes::RGB: {
    gfx::SurfaceFormat format = static_cast<TexturedEffect*>(aEffect)->mTexture->GetFormat();
    return (format == gfx::SurfaceFormat::B8G8R8A8 || format == gfx::SurfaceFormat::R8G8B8A8)
           ? PS_LAYER_RGBA
           : PS_LAYER_RGB;
  }
  case EffectTypes::RENDER_TARGET:
    return PS_LAYER_RGBA;
  case EffectTypes::YCBCR:
    return PS_LAYER_YCBCR;
  default:
    MOZ_ASSERT_UNREACHABLE("blending not supported for this layer type");
    return 0;
  }
}

void
CompositorD3D11::DrawQuad(const gfx::Rect& aRect,
                          const gfx::IntRect& aClipRect,
                          const EffectChain& aEffectChain,
                          gfx::Float aOpacity,
                          const gfx::Matrix4x4& aTransform,
                          const gfx::Rect& aVisibleRect)
{
  if (mCurrentClip.IsEmpty()) {
    return;
  }

  MOZ_ASSERT(mCurrentRT, "No render target");

  if (aEffectChain.mPrimaryEffect->mType == EffectTypes::VR_DISTORTION) {
    DrawVRDistortion(aRect, aClipRect, aEffectChain, aOpacity, aTransform);
    return;
  }

  memcpy(&mVSConstants.layerTransform, &aTransform._11, 64);
  IntPoint origin = mCurrentRT->GetOrigin();
  mVSConstants.renderTargetOffset[0] = origin.x;
  mVSConstants.renderTargetOffset[1] = origin.y;

  mPSConstants.layerOpacity[0] = aOpacity;

  bool restoreBlendMode = false;

  MaskType maskType = MaskType::MaskNone;

  if (aEffectChain.mSecondaryEffects[EffectTypes::MASK]) {
    maskType = MaskType::Mask;

    EffectMask* maskEffect =
      static_cast<EffectMask*>(aEffectChain.mSecondaryEffects[EffectTypes::MASK].get());
    TextureSourceD3D11* source = maskEffect->mMaskTexture->AsSourceD3D11();

    if (!source) {
      NS_WARNING("Missing texture source!");
      return;
    }

    ID3D11ShaderResourceView* srView = source->GetShaderResourceView();
    mContext->PSSetShaderResources(TexSlot::Mask, 1, &srView);

    const gfx::Matrix4x4& maskTransform = maskEffect->mMaskTransform;
    NS_ASSERTION(maskTransform.Is2D(), "How did we end up with a 3D transform here?!");
    Rect bounds = Rect(Point(), Size(maskEffect->mSize));

    mVSConstants.maskQuad = maskTransform.As2D().TransformBounds(bounds);
  }

  D3D11_RECT scissor;

  IntRect clipRect(aClipRect.x, aClipRect.y, aClipRect.width, aClipRect.height);
  if (mCurrentRT == mDefaultRT) {
    clipRect = clipRect.Intersect(mCurrentClip);
  }

  if (clipRect.IsEmpty()) {
    return;
  }

  scissor.left = clipRect.x;
  scissor.right = clipRect.XMost();
  scissor.top = clipRect.y;
  scissor.bottom = clipRect.YMost();

  RefPtr<ID3D11VertexShader> vertexShader = mAttachments->mVSQuadShader[maskType];
  RefPtr<ID3D11PixelShader> pixelShader = GetPSForEffect(aEffectChain.mPrimaryEffect, maskType);

  RefPtr<ID3D11Texture2D> mixBlendBackdrop;
  gfx::CompositionOp blendMode = gfx::CompositionOp::OP_OVER;
  if (aEffectChain.mSecondaryEffects[EffectTypes::BLEND_MODE]) {
    EffectBlendMode *blendEffect =
      static_cast<EffectBlendMode*>(aEffectChain.mSecondaryEffects[EffectTypes::BLEND_MODE].get());
    blendMode = blendEffect->mBlendMode;

    // If the blend operation needs to read from the backdrop, copy the
    // current render target into a new texture and bind it now.
    if (BlendOpIsMixBlendMode(blendMode)) {
      gfx::Matrix4x4 backdropTransform;
      gfx::IntRect rect = ComputeBackdropCopyRect(aRect, aClipRect, aTransform, &backdropTransform);

      RefPtr<ID3D11ShaderResourceView> srv;
      if (CopyBackdrop(rect, &mixBlendBackdrop, &srv) &&
          mAttachments->InitBlendShaders())
      {
        vertexShader = mAttachments->mVSQuadBlendShader[maskType];
        pixelShader = mAttachments->mBlendShader[MaskType::MaskNone];

        ID3D11ShaderResourceView* srView = srv.get();
        mContext->PSSetShaderResources(TexSlot::Backdrop, 1, &srView);

        memcpy(&mVSConstants.backdropTransform, &backdropTransform._11, 64);

        mPSConstants.blendConfig[0] = EffectToBlendLayerType(aEffectChain.mPrimaryEffect);
        mPSConstants.blendConfig[1] = int(maskType);
        mPSConstants.blendConfig[2] = BlendOpToShaderConstant(blendMode);
        mPSConstants.blendConfig[3] = EffectHasPremultipliedAlpha(aEffectChain.mPrimaryEffect);
      }
    }
  }

  mContext->RSSetScissorRects(1, &scissor);
  mContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP);
  mContext->VSSetShader(vertexShader, nullptr, 0);
  mContext->PSSetShader(pixelShader, nullptr, 0);

  const Rect* pTexCoordRect = nullptr;

  switch (aEffectChain.mPrimaryEffect->mType) {
  case EffectTypes::SOLID_COLOR: {
      Color color =
        static_cast<EffectSolidColor*>(aEffectChain.mPrimaryEffect.get())->mColor;
      mPSConstants.layerColor[0] = color.r * color.a * aOpacity;
      mPSConstants.layerColor[1] = color.g * color.a * aOpacity;
      mPSConstants.layerColor[2] = color.b * color.a * aOpacity;
      mPSConstants.layerColor[3] = color.a * aOpacity;
    }
    break;
  case EffectTypes::RGB:
  case EffectTypes::RENDER_TARGET:
    {
      TexturedEffect* texturedEffect =
        static_cast<TexturedEffect*>(aEffectChain.mPrimaryEffect.get());

      pTexCoordRect = &texturedEffect->mTextureCoords;

      TextureSourceD3D11* source = texturedEffect->mTexture->AsSourceD3D11();

      if (!source) {
        NS_WARNING("Missing texture source!");
        return;
      }

      ID3D11ShaderResourceView* srView = source->GetShaderResourceView();
      mContext->PSSetShaderResources(TexSlot::RGB, 1, &srView);

      if (!texturedEffect->mPremultiplied) {
        mContext->OMSetBlendState(mAttachments->mNonPremulBlendState, sBlendFactor, 0xFFFFFFFF);
        restoreBlendMode = true;
      }

      SetSamplerForSamplingFilter(texturedEffect->mSamplingFilter);
    }
    break;
  case EffectTypes::YCBCR: {
      EffectYCbCr* ycbcrEffect =
        static_cast<EffectYCbCr*>(aEffectChain.mPrimaryEffect.get());

      SetSamplerForSamplingFilter(SamplingFilter::LINEAR);

      pTexCoordRect = &ycbcrEffect->mTextureCoords;

      const int Y = 0, Cb = 1, Cr = 2;
      TextureSource* source = ycbcrEffect->mTexture;

      if (!source) {
        NS_WARNING("No texture to composite");
        return;
      }

      if (!source->GetSubSource(Y) || !source->GetSubSource(Cb) || !source->GetSubSource(Cr)) {
        // This can happen if we failed to upload the textures, most likely
        // because of unsupported dimensions (we don't tile YCbCr textures).
        return;
      }

      TextureSourceD3D11* sourceY  = source->GetSubSource(Y)->AsSourceD3D11();
      TextureSourceD3D11* sourceCb = source->GetSubSource(Cb)->AsSourceD3D11();
      TextureSourceD3D11* sourceCr = source->GetSubSource(Cr)->AsSourceD3D11();

      ID3D11ShaderResourceView* srViews[3] = { sourceY->GetShaderResourceView(),
                                               sourceCb->GetShaderResourceView(),
                                               sourceCr->GetShaderResourceView() };
      mContext->PSSetShaderResources(TexSlot::Y, 3, srViews);
    }
    break;
  case EffectTypes::COMPONENT_ALPHA:
    {
      MOZ_ASSERT(gfxPrefs::ComponentAlphaEnabled());
      MOZ_ASSERT(mAttachments->mComponentBlendState);
      EffectComponentAlpha* effectComponentAlpha =
        static_cast<EffectComponentAlpha*>(aEffectChain.mPrimaryEffect.get());

      TextureSourceD3D11* sourceOnWhite = effectComponentAlpha->mOnWhite->AsSourceD3D11();
      TextureSourceD3D11* sourceOnBlack = effectComponentAlpha->mOnBlack->AsSourceD3D11();

      if (!sourceOnWhite || !sourceOnBlack) {
        NS_WARNING("Missing texture source(s)!");
        return;
      }

      SetSamplerForSamplingFilter(effectComponentAlpha->mSamplingFilter);

      pTexCoordRect = &effectComponentAlpha->mTextureCoords;

      ID3D11ShaderResourceView* srViews[2] = { sourceOnBlack->GetShaderResourceView(),
                                               sourceOnWhite->GetShaderResourceView() };
      mContext->PSSetShaderResources(TexSlot::RGB, 1, &srViews[0]);
      mContext->PSSetShaderResources(TexSlot::RGBWhite, 1, &srViews[1]);

      mContext->OMSetBlendState(mAttachments->mComponentBlendState, sBlendFactor, 0xFFFFFFFF);
      restoreBlendMode = true;
    }
    break;
  default:
    NS_WARNING("Unknown shader type");
    return;
  }

  if (pTexCoordRect) {
    Rect layerRects[4];
    Rect textureRects[4];
    size_t rects = DecomposeIntoNoRepeatRects(aRect,
                                              *pTexCoordRect,
                                              &layerRects,
                                              &textureRects);
    for (size_t i = 0; i < rects; i++) {
      mVSConstants.layerQuad = layerRects[i];
      mVSConstants.textureCoords = textureRects[i];

      if (!UpdateConstantBuffers()) {
        NS_WARNING("Failed to update shader constant buffers");
        break;
      }
      mContext->Draw(4, 0);
    }
  } else {
    mVSConstants.layerQuad = aRect;

    if (!UpdateConstantBuffers()) {
      NS_WARNING("Failed to update shader constant buffers");
    } else {
      mContext->Draw(4, 0);
    }
  }

  if (restoreBlendMode) {
    mContext->OMSetBlendState(mAttachments->mPremulBlendState, sBlendFactor, 0xFFFFFFFF);
  }
}

void
CompositorD3D11::BeginFrame(const nsIntRegion& aInvalidRegion,
                            const IntRect* aClipRectIn,
                            const IntRect& aRenderBounds,
                            const nsIntRegion& aOpaqueRegion,
                            IntRect* aClipRectOut,
                            IntRect* aRenderBoundsOut)
{
  // Don't composite if we are minimised. Other than for the sake of efficency,
  // this is important because resizing our buffers when mimised will fail and
  // cause a crash when we're restored.
  NS_ASSERTION(mHwnd, "Couldn't find an HWND when initialising?");
  if (::IsIconic(mHwnd) || mDevice->GetDeviceRemovedReason() != S_OK) {
    *aRenderBoundsOut = IntRect();
    return;
  }

  LayoutDeviceIntSize oldSize = mSize;

  // Failed to create a render target or the view.
  if (!UpdateRenderTarget() || !mDefaultRT || !mDefaultRT->mRTView ||
      mSize.width <= 0 || mSize.height <= 0) {
    *aRenderBoundsOut = IntRect();
    return;
  }

  IntRect intRect = IntRect(IntPoint(0, 0), mSize.ToUnknownSize());
  // Sometimes the invalid region is larger than we want to draw.
  nsIntRegion invalidRegionSafe;

  if (mSize != oldSize) {
    invalidRegionSafe = intRect;
  } else {
    invalidRegionSafe.And(aInvalidRegion, intRect);
  }

  IntRect invalidRect = invalidRegionSafe.GetBounds();

  IntRect clipRect = invalidRect;
  if (aClipRectIn) {
    clipRect.IntersectRect(clipRect, IntRect(aClipRectIn->x, aClipRectIn->y, aClipRectIn->width, aClipRectIn->height));
  }

  if (clipRect.IsEmpty()) {
    *aRenderBoundsOut = IntRect();
    return;
  }

  mContext->IASetInputLayout(mAttachments->mInputLayout);

  ID3D11Buffer* buffer = mAttachments->mVertexBuffer;
  UINT size = sizeof(Vertex);
  UINT offset = 0;
  mContext->IASetVertexBuffers(0, 1, &buffer, &size, &offset);

  mInvalidRect = IntRect(invalidRect.x, invalidRect.y, invalidRect.width, invalidRect.height);
  mInvalidRegion = invalidRegionSafe;

  if (aClipRectOut) {
    *aClipRectOut = IntRect(0, 0, mSize.width, mSize.height);
  }
  if (aRenderBoundsOut) {
    *aRenderBoundsOut = IntRect(0, 0, mSize.width, mSize.height);
  }

  mCurrentClip = IntRect(clipRect.x, clipRect.y, clipRect.width, clipRect.height);

  mContext->RSSetState(mAttachments->mRasterizerState);

  SetRenderTarget(mDefaultRT);

  // ClearRect will set the correct blend state for us.
  ClearRect(Rect(clipRect.x, clipRect.y, clipRect.width, clipRect.height));

  if (mAttachments->mSyncTexture) {
    RefPtr<IDXGIKeyedMutex> mutex;
    mAttachments->mSyncTexture->QueryInterface((IDXGIKeyedMutex**)getter_AddRefs(mutex));

    MOZ_ASSERT(mutex);
    HRESULT hr = mutex->AcquireSync(0, 10000);
    if (hr == WAIT_TIMEOUT) {
      hr = mDevice->GetDeviceRemovedReason();
      if (hr == S_OK) {
        // There is no driver-removed event. Crash with this timeout.
        MOZ_CRASH("GFX: D3D11 normal status timeout");
      }

      // Since the timeout is related to the driver-removed, clear the
      // render-bounding size to skip this frame.
      gfxCriticalNote << "GFX: D3D11 timeout with device-removed:" << gfx::hexa(hr);
      *aRenderBoundsOut = IntRect();
      return;
    } else if (hr == WAIT_ABANDONED) {
      gfxCriticalNote << "GFX: D3D11 abandoned sync";
    }

    mutex->ReleaseSync(0);
  }
}

void
CompositorD3D11::EndFrame()
{
  Compositor::EndFrame();

  if (!mDefaultRT) {
    return;
  }

  LayoutDeviceIntSize oldSize = mSize;
  EnsureSize();
  if (mSize.width <= 0 || mSize.height <= 0) {
    return;
  }

  RefPtr<ID3D11Query> query;
  CD3D11_QUERY_DESC  desc(D3D11_QUERY_EVENT);
  mDevice->CreateQuery(&desc, getter_AddRefs(query));
  if (query) {
    mContext->End(query);
  }

  UINT presentInterval = 0;

  if (gfxWindowsPlatform::GetPlatform()->IsWARP()) {
    // When we're using WARP we cannot present immediately as it causes us
    // to tear when rendering. When not using WARP it appears the DWM takes
    // care of tearing for us.
    presentInterval = 1;
  }

  if (oldSize == mSize) {
    RefPtr<IDXGISwapChain1> chain;
    HRESULT hr = mSwapChain->QueryInterface((IDXGISwapChain1**)getter_AddRefs(chain));
    // We can force partial present or block partial present, based on the value of
    // this preference; the default is to disable it on Nvidia (bug 1189940)
    bool allowPartialPresent = false;

    int32_t partialPresentPref = gfxPrefs::PartialPresent();
    if (partialPresentPref > 0) {
      allowPartialPresent = true;
    } else if (partialPresentPref < 0) {
      allowPartialPresent = false;
    } else if (partialPresentPref == 0) {
      nsString vendorID;
      nsCOMPtr<nsIGfxInfo> gfxInfo = services::GetGfxInfo();
      gfxInfo->GetAdapterVendorID(vendorID);
      allowPartialPresent = !vendorID.EqualsLiteral("0x10de") ||
                            gfxWindowsPlatform::GetPlatform()->IsWARP();
    }

    if (SUCCEEDED(hr) && chain && allowPartialPresent) {
      DXGI_PRESENT_PARAMETERS params;
      PodZero(&params);
      params.DirtyRectsCount = mInvalidRegion.GetNumRects();
      StackArray<RECT, 4> rects(params.DirtyRectsCount);

      uint32_t i = 0;
      for (auto iter = mInvalidRegion.RectIter(); !iter.Done(); iter.Next()) {
        const IntRect& r = iter.Get();
        rects[i].left = r.x;
        rects[i].top = r.y;
        rects[i].bottom = r.YMost();
        rects[i].right = r.XMost();
        i++;
      }

      params.pDirtyRects = params.DirtyRectsCount ? rects.data() : nullptr;
      chain->Present1(presentInterval, mDisableSequenceForNextFrame ? DXGI_PRESENT_DO_NOT_SEQUENCE : 0, &params);
    } else {
      hr = mSwapChain->Present(presentInterval, mDisableSequenceForNextFrame ? DXGI_PRESENT_DO_NOT_SEQUENCE : 0);
      if (FAILED(hr)) {
        gfxCriticalNote << "D3D11 swap chain preset failed " << hexa(hr);
        HandleError(hr);
      }
    }
    mDisableSequenceForNextFrame = false;
    if (mTarget) {
      PaintToTarget();
    }
  }

  // Block until the previous frame's work has been completed.
  if (mQuery) {
    TimeStamp start = TimeStamp::Now();
    BOOL result;
    while (mContext->GetData(mQuery, &result, sizeof(BOOL), 0) != S_OK) {
      if (mDevice->GetDeviceRemovedReason() != S_OK) {
        break;
      }
      if ((TimeStamp::Now() - start) > TimeDuration::FromSeconds(2)) {
        break;
      }
      Sleep(0);
    }
  }
  // Store the query for this frame so we can flush it next time.
  mQuery = query;

  mCurrentRT = nullptr;
}

void
CompositorD3D11::PrepareViewport(const gfx::IntSize& aSize)
{
  // This view matrix translates coordinates from 0..width and 0..height to
  // -1..1 on the X axis, and -1..1 on the Y axis (flips the Y coordinate)
  Matrix viewMatrix = Matrix::Translation(-1.0, 1.0);
  viewMatrix.PreScale(2.0f / float(aSize.width), 2.0f / float(aSize.height));
  viewMatrix.PreScale(1.0f, -1.0f);

  Matrix4x4 projection = Matrix4x4::From2D(viewMatrix);
  projection._33 = 0.0f;

  PrepareViewport(aSize, projection, 0.0f, 1.0f);
}

void
CompositorD3D11::ForcePresent()
{
  LayoutDeviceIntSize size = mWidget->GetClientSize();

  DXGI_SWAP_CHAIN_DESC desc;
  mSwapChain->GetDesc(&desc);

  if (desc.BufferDesc.Width == size.width && desc.BufferDesc.Height == size.height) {
    mSwapChain->Present(0, 0);
  }
}

void
CompositorD3D11::PrepareViewport(const gfx::IntSize& aSize,
                                 const gfx::Matrix4x4& aProjection,
                                 float aZNear, float aZFar)
{
  D3D11_VIEWPORT viewport;
  viewport.MaxDepth = aZFar;
  viewport.MinDepth = aZNear;
  viewport.Width = aSize.width;
  viewport.Height = aSize.height;
  viewport.TopLeftX = 0;
  viewport.TopLeftY = 0;

  mContext->RSSetViewports(1, &viewport);

  memcpy(&mVSConstants.projection, &aProjection._11, sizeof(mVSConstants.projection));
}

void
CompositorD3D11::EnsureSize()
{
  mSize = mWidget->GetClientSize();
}

bool
CompositorD3D11::VerifyBufferSize()
{
  DXGI_SWAP_CHAIN_DESC swapDesc;
  HRESULT hr;

  hr = mSwapChain->GetDesc(&swapDesc);
  if (FAILED(hr)) {
    gfxCriticalError() << "Failed to get the description " << hexa(hr) << ", " << mSize << ", " << (int)mVerifyBuffersFailed;
    HandleError(hr);
    return false;
  }

  if (((swapDesc.BufferDesc.Width == mSize.width &&
       swapDesc.BufferDesc.Height == mSize.height) ||
       mSize.width <= 0 || mSize.height <= 0) &&
      !mVerifyBuffersFailed) {
    return true;
  }

  ID3D11RenderTargetView* view = nullptr;
  mContext->OMSetRenderTargets(1, &view, nullptr);

  if (mDefaultRT) {
    RefPtr<ID3D11RenderTargetView> rtView = mDefaultRT->mRTView;
    RefPtr<ID3D11ShaderResourceView> srView = mDefaultRT->mSRV;

    // Make sure the texture, which belongs to the swapchain, is destroyed
    // before resizing the swapchain.
    if (mCurrentRT == mDefaultRT) {
      mCurrentRT = nullptr;
    }
    MOZ_ASSERT(mDefaultRT->hasOneRef());
    mDefaultRT = nullptr;

    RefPtr<ID3D11Resource> resource;
    rtView->GetResource(getter_AddRefs(resource));

    ULONG newRefCnt = rtView.forget().take()->Release();

    if (newRefCnt > 0) {
      gfxCriticalError() << "mRTView not destroyed on final release! RefCnt: " << newRefCnt;
    }

    if (srView) {
      newRefCnt = srView.forget().take()->Release();

      if (newRefCnt > 0) {
        gfxCriticalError() << "mSRV not destroyed on final release! RefCnt: " << newRefCnt;
      }
    }

    newRefCnt = resource.forget().take()->Release();

    if (newRefCnt > 0) {
      gfxCriticalError() << "Unexpecting lingering references to backbuffer! RefCnt: " << newRefCnt;
    }
  }

  hr = mSwapChain->ResizeBuffers(1, mSize.width, mSize.height,
                                 DXGI_FORMAT_B8G8R8A8_UNORM,
                                 0);

  mVerifyBuffersFailed = FAILED(hr);
  if (mVerifyBuffersFailed) {
    gfxCriticalNote << "D3D11 swap resize buffers failed " << hexa(hr) << " on " << mSize;
    HandleError(hr);
  }

  return !mVerifyBuffersFailed;
}

bool
CompositorD3D11::UpdateRenderTarget()
{
  EnsureSize();
  if (!VerifyBufferSize()) {
    gfxCriticalNote << "Failed VerifyBufferSize in UpdateRenderTarget " << mSize;
    return false;
  }

  if (mDefaultRT) {
    return true;
  }

  if (mSize.width <= 0 || mSize.height <= 0) {
    gfxCriticalNote << "Invalid size in UpdateRenderTarget " << mSize << ", " << (int)mVerifyBuffersFailed;
    return false;
  }

  HRESULT hr;

  RefPtr<ID3D11Texture2D> backBuf;

  hr = mSwapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), (void**)backBuf.StartAssignment());
  if (hr == DXGI_ERROR_INVALID_CALL) {
    // This happens on some GPUs/drivers when there's a TDR.
    if (mDevice->GetDeviceRemovedReason() != S_OK) {
      gfxCriticalError() << "GetBuffer returned invalid call! " << mSize << ", " << (int)mVerifyBuffersFailed;
      return false;
    }
  }
  if (FAILED(hr)) {
    gfxCriticalNote << "Failed in UpdateRenderTarget " << hexa(hr) << ", " << mSize << ", " << (int)mVerifyBuffersFailed;
    HandleError(hr);
    return false;
  }

  mDefaultRT = new CompositingRenderTargetD3D11(backBuf, IntPoint(0, 0));
  mDefaultRT->SetSize(mSize.ToUnknownSize());

  return true;
}

bool
DeviceAttachmentsD3D11::InitSyncObject()
{
  // Sync object is not supported on WARP.
  if (gfxWindowsPlatform::GetPlatform()->IsWARP()) {
    return true;
  }

  // It's okay to do this on Windows 8. But for now we'll just bail
  // whenever we're using WARP.
  CD3D11_TEXTURE2D_DESC desc(DXGI_FORMAT_B8G8R8A8_UNORM, 1, 1, 1, 1,
                             D3D11_BIND_SHADER_RESOURCE |
                             D3D11_BIND_RENDER_TARGET);
  desc.MiscFlags = D3D11_RESOURCE_MISC_SHARED_KEYEDMUTEX;

  RefPtr<ID3D11Texture2D> texture;
  HRESULT hr = mDevice->CreateTexture2D(&desc, nullptr, getter_AddRefs(texture));
  if (Failed(hr, "create sync texture")) {
    return false;
  }

  hr = texture->QueryInterface((IDXGIResource**)getter_AddRefs(mSyncTexture));
  if (Failed(hr, "QI sync texture")) {
    return false;
  }

  hr = mSyncTexture->GetSharedHandle(&mSyncHandle);
  if (FAILED(hr) || !mSyncHandle) {
    gfxCriticalError() << "Failed to get SharedHandle for sync texture. Result: "
                       << hexa(hr);
    NS_DispatchToMainThread(NS_NewRunnableFunction([] () -> void {
      Accumulate(Telemetry::D3D11_SYNC_HANDLE_FAILURE, 1);
    }));
    return false;
  }

  return true;
}

bool
DeviceAttachmentsD3D11::InitBlendShaders()
{
  if (!mVSQuadBlendShader[MaskType::MaskNone]) {
    InitVertexShader(sLayerQuadBlendVS, mVSQuadBlendShader, MaskType::MaskNone);
    InitVertexShader(sLayerQuadBlendMaskVS, mVSQuadBlendShader, MaskType::Mask);
  }
  if (!mBlendShader[MaskType::MaskNone]) {
    InitPixelShader(sBlendShader, mBlendShader, MaskType::MaskNone);
  }
  return mInitOkay;
}

bool
DeviceAttachmentsD3D11::CreateShaders()
{
  InitVertexShader(sLayerQuadVS, mVSQuadShader, MaskType::MaskNone);
  InitVertexShader(sLayerQuadMaskVS, mVSQuadShader, MaskType::Mask);

  InitPixelShader(sSolidColorShader, mSolidColorShader, MaskType::MaskNone);
  InitPixelShader(sSolidColorShaderMask, mSolidColorShader, MaskType::Mask);
  InitPixelShader(sRGBShader, mRGBShader, MaskType::MaskNone);
  InitPixelShader(sRGBShaderMask, mRGBShader, MaskType::Mask);
  InitPixelShader(sRGBAShader, mRGBAShader, MaskType::MaskNone);
  InitPixelShader(sRGBAShaderMask, mRGBAShader, MaskType::Mask);
  InitPixelShader(sYCbCrShader, mYCbCrShader, MaskType::MaskNone);
  InitPixelShader(sYCbCrShaderMask, mYCbCrShader, MaskType::Mask);
  if (gfxPrefs::ComponentAlphaEnabled()) {
    InitPixelShader(sComponentAlphaShader, mComponentAlphaShader, MaskType::MaskNone);
    InitPixelShader(sComponentAlphaShaderMask, mComponentAlphaShader, MaskType::Mask);
  }

  InitVertexShader(sOculus050VRDistortionVS, getter_AddRefs(mVRDistortionVS[VRHMDType::Oculus050]));
  InitPixelShader(sOculus050VRDistortionPS, getter_AddRefs(mVRDistortionPS[VRHMDType::Oculus050]));

  // These are shared
  // XXX rename Oculus050 shaders to something more generic
  mVRDistortionVS[VRHMDType::Cardboard] = mVRDistortionVS[VRHMDType::Oculus050];
  mVRDistortionPS[VRHMDType::Cardboard] = mVRDistortionPS[VRHMDType::Oculus050];
  return mInitOkay;
}

bool
CompositorD3D11::UpdateConstantBuffers()
{
  HRESULT hr;
  D3D11_MAPPED_SUBRESOURCE resource;
  resource.pData = nullptr;

  hr = mContext->Map(mAttachments->mVSConstantBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &resource);
  if (FAILED(hr) || !resource.pData) {
    gfxCriticalError() << "Failed to map VSConstantBuffer. Result: " << hexa(hr) << ", " << (int)mVerifyBuffersFailed;
    HandleError(hr);
    return false;
  }
  *(VertexShaderConstants*)resource.pData = mVSConstants;
  mContext->Unmap(mAttachments->mVSConstantBuffer, 0);
  resource.pData = nullptr;

  hr = mContext->Map(mAttachments->mPSConstantBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &resource);
  if (FAILED(hr) || !resource.pData) {
    gfxCriticalError() << "Failed to map PSConstantBuffer. Result: " << hexa(hr) << ", " << (int)mVerifyBuffersFailed;
    HandleError(hr);
    return false;
  }
  *(PixelShaderConstants*)resource.pData = mPSConstants;
  mContext->Unmap(mAttachments->mPSConstantBuffer, 0);

  ID3D11Buffer *buffer = mAttachments->mVSConstantBuffer;

  mContext->VSSetConstantBuffers(0, 1, &buffer);

  buffer = mAttachments->mPSConstantBuffer;
  mContext->PSSetConstantBuffers(0, 1, &buffer);
  return true;
}

void
CompositorD3D11::SetSamplerForSamplingFilter(SamplingFilter aSamplingFilter)
{
  ID3D11SamplerState *sampler;
  switch (aSamplingFilter) {
    case SamplingFilter::POINT:
    sampler = mAttachments->mPointSamplerState;
    break;
  case SamplingFilter::LINEAR:
  default:
    sampler = mAttachments->mLinearSamplerState;
    break;
  }

  mContext->PSSetSamplers(0, 1, &sampler);
}

void
CompositorD3D11::PaintToTarget()
{
  RefPtr<ID3D11Texture2D> backBuf;
  HRESULT hr;

  hr = mSwapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), (void**)backBuf.StartAssignment());
  if (FAILED(hr)) {
    gfxCriticalErrorOnce(gfxCriticalError::DefaultOptions(false)) << "Failed in PaintToTarget 1";
    HandleError(hr);
    return;
  }

  D3D11_TEXTURE2D_DESC bbDesc;
  backBuf->GetDesc(&bbDesc);

  CD3D11_TEXTURE2D_DESC softDesc(bbDesc.Format, bbDesc.Width, bbDesc.Height);
  softDesc.MipLevels = 1;
  softDesc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
  softDesc.Usage = D3D11_USAGE_STAGING;
  softDesc.BindFlags = 0;

  RefPtr<ID3D11Texture2D> readTexture;

  hr = mDevice->CreateTexture2D(&softDesc, nullptr, getter_AddRefs(readTexture));
  if (FAILED(hr)) {
    gfxCriticalErrorOnce(gfxCriticalError::DefaultOptions(false)) << "Failed in PaintToTarget 2";
    HandleError(hr);
    return;
  }
  mContext->CopyResource(readTexture, backBuf);

  D3D11_MAPPED_SUBRESOURCE map;
  hr = mContext->Map(readTexture, 0, D3D11_MAP_READ, 0, &map);
  if (FAILED(hr)) {
    gfxCriticalErrorOnce(gfxCriticalError::DefaultOptions(false)) << "Failed in PaintToTarget 3";
    HandleError(hr);
    return;
  }
  RefPtr<DataSourceSurface> sourceSurface =
    Factory::CreateWrappingDataSourceSurface((uint8_t*)map.pData,
                                             map.RowPitch,
                                             IntSize(bbDesc.Width, bbDesc.Height),
                                             SurfaceFormat::B8G8R8A8);
  mTarget->CopySurface(sourceSurface,
                       IntRect(0, 0, bbDesc.Width, bbDesc.Height),
                       IntPoint(-mTargetBounds.x, -mTargetBounds.y));

  mTarget->Flush();
  mContext->Unmap(readTexture, 0);
}

bool
CompositorD3D11::Failed(HRESULT hr, const char* aContext)
{
  if (SUCCEEDED(hr))
    return false;

  gfxCriticalNote << "[D3D11] " << aContext << " failed: " << hexa(hr) << ", " << (int)mVerifyBuffersFailed;
  return true;
}

void
CompositorD3D11::HandleError(HRESULT hr, Severity aSeverity)
{
  if (SUCCEEDED(hr)) {
    return;
  }

  if (aSeverity == Critical) {
    MOZ_CRASH("GFX: Unrecoverable D3D11 error");
  }

  RefPtr<ID3D11Device> device;
  if (!gfxWindowsPlatform::GetPlatform()->GetD3D11Device(&device) || device != mDevice) {
    gfxCriticalError() << "Out of sync D3D11 devices in HandleError, " << (int)mVerifyBuffersFailed;
  }

  HRESULT hrOnReset = S_OK;
  bool deviceRemoved = hr == DXGI_ERROR_DEVICE_REMOVED;

  if (deviceRemoved && mDevice) {
    hrOnReset = mDevice->GetDeviceRemovedReason();
  } else if (hr == DXGI_ERROR_INVALID_CALL && mDevice) {
    hrOnReset = mDevice->GetDeviceRemovedReason();
    if (hrOnReset != S_OK) {
      deviceRemoved = true;
    }
  }

  // Device reset may not be an error on our side, but can mess things up so
  // it's useful to see it in the reports.
  gfxCriticalError(CriticalLog::DefaultOptions(!deviceRemoved))
    << (deviceRemoved ? "[CompositorD3D11] device removed with error code: "
                      : "[CompositorD3D11] error code: ")
    << hexa(hr) << ", " << hexa(hrOnReset) << ", " << (int)mVerifyBuffersFailed;

  // Crash if we are making invalid calls outside of device removal
  if (hr == DXGI_ERROR_INVALID_CALL) {
    gfxDevCrash(deviceRemoved ? LogReason::D3D11InvalidCallDeviceRemoved : LogReason::D3D11InvalidCall) << "Invalid D3D11 api call";
  }

  if (aSeverity == Recoverable) {
    NS_WARNING("Encountered a recoverable D3D11 error");
  }
}

}
}