summaryrefslogtreecommitdiffstats
path: root/gfx/layers/d3d11
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--gfx/layers/d3d11/BlendShaderConstants.h65
-rw-r--r--gfx/layers/d3d11/BlendingHelpers.hlslh184
-rw-r--r--gfx/layers/d3d11/CompositorD3D11.cpp1794
-rw-r--r--gfx/layers/d3d11/CompositorD3D11.h303
-rw-r--r--gfx/layers/d3d11/CompositorD3D11.hlsl503
-rw-r--r--gfx/layers/d3d11/DeviceAttachmentsD3D11.cpp342
-rw-r--r--gfx/layers/d3d11/DeviceAttachmentsD3D11.h116
-rw-r--r--gfx/layers/d3d11/DiagnosticsD3D11.cpp83
-rw-r--r--gfx/layers/d3d11/DiagnosticsD3D11.h51
-rw-r--r--gfx/layers/d3d11/HelpersD3D11.h56
-rw-r--r--gfx/layers/d3d11/MLGDeviceD3D11.cpp2034
-rw-r--r--gfx/layers/d3d11/MLGDeviceD3D11.h326
-rw-r--r--gfx/layers/d3d11/ReadbackManagerD3D11.cpp153
-rw-r--r--gfx/layers/d3d11/ReadbackManagerD3D11.h65
-rw-r--r--gfx/layers/d3d11/ShaderDefinitionsD3D11.h40
-rw-r--r--gfx/layers/d3d11/TextureD3D11.cpp1867
-rw-r--r--gfx/layers/d3d11/TextureD3D11.h600
-rw-r--r--gfx/layers/d3d11/genshaders.py176
-rw-r--r--gfx/layers/d3d11/mlgshaders/blend-common.hlsl15
-rw-r--r--gfx/layers/d3d11/mlgshaders/blend-ps-generated.hlslh540
-rw-r--r--gfx/layers/d3d11/mlgshaders/blend-ps-generated.hlslh.tpl36
-rw-r--r--gfx/layers/d3d11/mlgshaders/blend-ps.hlsl13
-rw-r--r--gfx/layers/d3d11/mlgshaders/blend-vs.hlsl52
-rw-r--r--gfx/layers/d3d11/mlgshaders/clear-common.hlsl9
-rw-r--r--gfx/layers/d3d11/mlgshaders/clear-ps.hlsl12
-rw-r--r--gfx/layers/d3d11/mlgshaders/clear-vs.hlsl30
-rw-r--r--gfx/layers/d3d11/mlgshaders/color-common.hlsl19
-rw-r--r--gfx/layers/d3d11/mlgshaders/color-ps.hlsl20
-rw-r--r--gfx/layers/d3d11/mlgshaders/color-vs.hlsl58
-rw-r--r--gfx/layers/d3d11/mlgshaders/common-ps.hlsl37
-rw-r--r--gfx/layers/d3d11/mlgshaders/common-vs.hlsl167
-rw-r--r--gfx/layers/d3d11/mlgshaders/common.hlsl17
-rw-r--r--gfx/layers/d3d11/mlgshaders/component-alpha-ps.hlsl45
-rw-r--r--gfx/layers/d3d11/mlgshaders/diagnostics-common.hlsl10
-rw-r--r--gfx/layers/d3d11/mlgshaders/diagnostics-ps.hlsl13
-rw-r--r--gfx/layers/d3d11/mlgshaders/diagnostics-vs.hlsl25
-rw-r--r--gfx/layers/d3d11/mlgshaders/mask-combiner-common.hlsl10
-rw-r--r--gfx/layers/d3d11/mlgshaders/mask-combiner-ps.hlsl16
-rw-r--r--gfx/layers/d3d11/mlgshaders/mask-combiner-vs.hlsl26
-rw-r--r--gfx/layers/d3d11/mlgshaders/shaders.manifest100
-rw-r--r--gfx/layers/d3d11/mlgshaders/test-features-vs.hlsl32
-rw-r--r--gfx/layers/d3d11/mlgshaders/textured-common.hlsl43
-rw-r--r--gfx/layers/d3d11/mlgshaders/textured-ps.hlsl45
-rw-r--r--gfx/layers/d3d11/mlgshaders/textured-vs.hlsl49
-rw-r--r--gfx/layers/d3d11/mlgshaders/ycbcr-ps.hlsl118
-rw-r--r--gfx/layers/d3d11/shaders.manifest28
46 files changed, 10343 insertions, 0 deletions
diff --git a/gfx/layers/d3d11/BlendShaderConstants.h b/gfx/layers/d3d11/BlendShaderConstants.h
new file mode 100644
index 0000000000..84b2c68a4a
--- /dev/null
+++ b/gfx/layers/d3d11/BlendShaderConstants.h
@@ -0,0 +1,65 @@
+/* -*- 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_LAYERS_D3D11_BLENDSHADERCONSTANTS_H_
+#define MOZILLA_GFX_LAYERS_D3D11_BLENDSHADERCONSTANTS_H_
+
+// These constants are shared between CompositorD3D11 and the blend pixel
+// shader.
+#define PS_LAYER_RGB 0
+#define PS_LAYER_RGBA 1
+#define PS_LAYER_YCBCR 2
+#define PS_LAYER_COLOR 3
+#define PS_LAYER_NV12 4
+
+// These must be in the same order as the Mask enum.
+#define PS_MASK_NONE 0
+#define PS_MASK 1
+
+// These must be in the same order as CompositionOp.
+#define PS_BLEND_MULTIPLY 0
+#define PS_BLEND_SCREEN 1
+#define PS_BLEND_OVERLAY 2
+#define PS_BLEND_DARKEN 3
+#define PS_BLEND_LIGHTEN 4
+#define PS_BLEND_COLOR_DODGE 5
+#define PS_BLEND_COLOR_BURN 6
+#define PS_BLEND_HARD_LIGHT 7
+#define PS_BLEND_SOFT_LIGHT 8
+#define PS_BLEND_DIFFERENCE 9
+#define PS_BLEND_EXCLUSION 10
+#define PS_BLEND_HUE 11
+#define PS_BLEND_SATURATION 12
+#define PS_BLEND_COLOR 13
+#define PS_BLEND_LUMINOSITY 14
+
+#if defined(__cplusplus)
+namespace mozilla {
+namespace layers {
+
+static inline int BlendOpToShaderConstant(gfx::CompositionOp aOp) {
+ return int(aOp) - int(gfx::CompositionOp::OP_MULTIPLY);
+}
+
+} // namespace layers
+} // namespace mozilla
+
+// Sanity checks.
+namespace {
+static inline void BlendShaderConstantAsserts() {
+ static_assert(PS_MASK_NONE == int(mozilla::layers::MaskType::MaskNone),
+ "shader constant is out of sync");
+ static_assert(PS_MASK == int(mozilla::layers::MaskType::Mask),
+ "shader constant is out of sync");
+ static_assert(int(mozilla::gfx::CompositionOp::OP_LUMINOSITY) -
+ int(mozilla::gfx::CompositionOp::OP_MULTIPLY) ==
+ 14,
+ "shader constants are out of sync");
+}
+} // anonymous namespace
+#endif
+
+#endif // MOZILLA_GFX_LAYERS_D3D11_BLENDSHADERCONSTANTS_H_
diff --git a/gfx/layers/d3d11/BlendingHelpers.hlslh b/gfx/layers/d3d11/BlendingHelpers.hlslh
new file mode 100644
index 0000000000..57d27b23b2
--- /dev/null
+++ b/gfx/layers/d3d11/BlendingHelpers.hlslh
@@ -0,0 +1,184 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * 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/. */
+
+// Helper functions.
+float hardlight(float dest, float src) {
+ if (src <= 0.5) {
+ return dest * (2.0 * src);
+ } else {
+ // Note: we substitute (2*src-1) into the screen formula below.
+ return 2.0 * dest + 2.0 * src - 1.0 - 2.0 * dest * src;
+ }
+}
+
+float dodge(float dest, float src) {
+ if (dest == 0.0) {
+ return 0.0;
+ } else if (src == 1.0) {
+ return 1.0;
+ } else {
+ return min(1.0, dest / (1.0 - src));
+ }
+}
+
+float burn(float dest, float src) {
+ if (dest == 1.0) {
+ return 1.0;
+ } else if (src == 0.0) {
+ return 0.0;
+ } else {
+ return 1.0 - min(1.0, (1.0 - dest) / src);
+ }
+}
+
+float darken(float dest) {
+ if (dest <= 0.25) {
+ return ((16.0 * dest - 12.0) * dest + 4.0) * dest;
+ } else {
+ return sqrt(dest);
+ }
+}
+
+float softlight(float dest, float src) {
+ if (src <= 0.5) {
+ return dest - (1.0 - 2.0 * src) * dest * (1.0 - dest);
+ } else {
+ return dest + (2.0 * src - 1.0) * (darken(dest) - dest);
+ }
+}
+
+float Lum(float3 c) {
+ return dot(float3(0.3, 0.59, 0.11), c);
+}
+
+float3 ClipColor(float3 c) {
+ float L = Lum(c);
+ float n = min(min(c.r, c.g), c.b);
+ float x = max(max(c.r, c.g), c.b);
+ if (n < 0.0) {
+ c = L + (((c - L) * L) / (L - n));
+ }
+ if (x > 1.0) {
+ c = L + (((c - L) * (1.0 - L)) / (x - L));
+ }
+ return c;
+}
+
+float3 SetLum(float3 c, float L) {
+ float d = L - Lum(c);
+ return ClipColor(float3(
+ c.r + d,
+ c.g + d,
+ c.b + d));
+}
+
+float Sat(float3 c) {
+ return max(max(c.r, c.g), c.b) - min(min(c.r, c.g), c.b);
+}
+
+// To use this helper, re-arrange rgb such that r=min, g=mid, and b=max.
+float3 SetSatInner(float3 c, float s) {
+ if (c.b > c.r) {
+ c.g = (((c.g - c.r) * s) / (c.b - c.r));
+ c.b = s;
+ } else {
+ c.gb = float2(0.0, 0.0);
+ }
+ return float3(0.0, c.g, c.b);
+}
+
+float3 SetSat(float3 c, float s) {
+ if (c.r <= c.g) {
+ if (c.g <= c.b) {
+ c.rgb = SetSatInner(c.rgb, s);
+ } else if (c.r <= c.b) {
+ c.rbg = SetSatInner(c.rbg, s);
+ } else {
+ c.brg = SetSatInner(c.brg, s);
+ }
+ } else if (c.r <= c.b) {
+ c.grb = SetSatInner(c.grb, s);
+ } else if (c.g <= c.b) {
+ c.gbr = SetSatInner(c.gbr, s);
+ } else {
+ c.bgr = SetSatInner(c.bgr, s);
+ }
+ return c;
+}
+
+float3 BlendMultiply(float3 dest, float3 src) {
+ return dest * src;
+}
+
+float3 BlendScreen(float3 dest, float3 src) {
+ return dest + src - (dest * src);
+}
+
+float3 BlendOverlay(float3 dest, float3 src) {
+ return float3(
+ hardlight(src.r, dest.r),
+ hardlight(src.g, dest.g),
+ hardlight(src.b, dest.b));
+}
+
+float3 BlendDarken(float3 dest, float3 src) {
+ return min(dest, src);
+}
+
+float3 BlendLighten(float3 dest, float3 src) {
+ return max(dest, src);
+}
+
+float3 BlendColorDodge(float3 dest, float3 src) {
+ return float3(
+ dodge(dest.r, src.r),
+ dodge(dest.g, src.g),
+ dodge(dest.b, src.b));
+}
+
+float3 BlendColorBurn(float3 dest, float3 src) {
+ return float3(
+ burn(dest.r, src.r),
+ burn(dest.g, src.g),
+ burn(dest.b, src.b));
+}
+
+float3 BlendHardLight(float3 dest, float3 src) {
+ return float3(
+ hardlight(dest.r, src.r),
+ hardlight(dest.g, src.g),
+ hardlight(dest.b, src.b));
+}
+
+float3 BlendSoftLight(float3 dest, float3 src) {
+ return float3(
+ softlight(dest.r, src.r),
+ softlight(dest.g, src.g),
+ softlight(dest.b, src.b));
+}
+
+float3 BlendDifference(float3 dest, float3 src) {
+ return abs(dest - src);
+}
+
+float3 BlendExclusion(float3 dest, float3 src) {
+ return dest + src - 2.0 * dest * src;
+}
+
+float3 BlendHue(float3 dest, float3 src) {
+ return SetLum(SetSat(src, Sat(dest)), Lum(dest));
+}
+
+float3 BlendSaturation(float3 dest, float3 src) {
+ return SetLum(SetSat(dest, Sat(src)), Lum(dest));
+}
+
+float3 BlendColor(float3 dest, float3 src) {
+ return SetLum(src, Lum(dest));
+}
+
+float3 BlendLuminosity(float3 dest, float3 src) {
+ return SetLum(dest, Lum(src));
+}
diff --git a/gfx/layers/d3d11/CompositorD3D11.cpp b/gfx/layers/d3d11/CompositorD3D11.cpp
new file mode 100644
index 0000000000..69b7617112
--- /dev/null
+++ b/gfx/layers/d3d11/CompositorD3D11.cpp
@@ -0,0 +1,1794 @@
+/* -*- 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/. */
+
+#include "CompositorD3D11.h"
+
+#include "TextureD3D11.h"
+
+#include "gfxWindowsPlatform.h"
+#include "nsIWidget.h"
+#include "Layers.h"
+#include "mozilla/gfx/D3D11Checks.h"
+#include "mozilla/gfx/DeviceManagerDx.h"
+#include "mozilla/gfx/GPUParent.h"
+#include "mozilla/gfx/Swizzle.h"
+#include "mozilla/layers/ImageHost.h"
+#include "mozilla/layers/ContentHost.h"
+#include "mozilla/layers/Diagnostics.h"
+#include "mozilla/layers/DiagnosticsD3D11.h"
+#include "mozilla/layers/Effects.h"
+#include "mozilla/layers/HelpersD3D11.h"
+#include "nsWindowsHelpers.h"
+#include "gfxConfig.h"
+#include "gfxCrashReporterUtils.h"
+#include "gfxUtils.h"
+#include "mozilla/gfx/StackArray.h"
+#include "mozilla/widget/WinCompositorWidget.h"
+
+#include "mozilla/EnumeratedArray.h"
+#include "mozilla/StaticPrefs_gfx.h"
+#include "mozilla/StaticPrefs_layers.h"
+#include "mozilla/Telemetry.h"
+#include "BlendShaderConstants.h"
+
+#include "D3D11ShareHandleImage.h"
+#include "DeviceAttachmentsD3D11.h"
+
+#include <versionhelpers.h> // For IsWindows8OrGreater
+#include <winsdkver.h>
+
+namespace mozilla {
+
+using namespace gfx;
+
+namespace layers {
+
+bool CanUsePartialPresents(ID3D11Device* aDevice);
+
+const FLOAT sBlendFactor[] = {0, 0, 0, 0};
+
+class AsyncReadbackBufferD3D11 final : public AsyncReadbackBuffer {
+ public:
+ AsyncReadbackBufferD3D11(ID3D11DeviceContext* aContext,
+ ID3D11Texture2D* aTexture, const IntSize& aSize);
+
+ bool MapAndCopyInto(DataSourceSurface* aSurface,
+ const IntSize& aReadSize) const override;
+
+ ID3D11Texture2D* GetTexture() { return mTexture; }
+
+ private:
+ RefPtr<ID3D11DeviceContext> mContext;
+ RefPtr<ID3D11Texture2D> mTexture;
+};
+
+AsyncReadbackBufferD3D11::AsyncReadbackBufferD3D11(
+ ID3D11DeviceContext* aContext, ID3D11Texture2D* aTexture,
+ const IntSize& aSize)
+ : AsyncReadbackBuffer(aSize), mContext(aContext), mTexture(aTexture) {}
+
+bool AsyncReadbackBufferD3D11::MapAndCopyInto(DataSourceSurface* aSurface,
+ const IntSize& aReadSize) const {
+ D3D11_MAPPED_SUBRESOURCE map;
+ HRESULT hr = mContext->Map(mTexture, 0, D3D11_MAP_READ, 0, &map);
+
+ if (FAILED(hr)) {
+ return false;
+ }
+
+ RefPtr<DataSourceSurface> sourceSurface =
+ Factory::CreateWrappingDataSourceSurface(static_cast<uint8_t*>(map.pData),
+ map.RowPitch, mSize,
+ SurfaceFormat::B8G8R8A8);
+
+ bool result;
+ {
+ DataSourceSurface::ScopedMap sourceMap(sourceSurface,
+ DataSourceSurface::READ);
+ DataSourceSurface::ScopedMap destMap(aSurface, DataSourceSurface::WRITE);
+
+ result = SwizzleData(sourceMap.GetData(), sourceMap.GetStride(),
+ SurfaceFormat::B8G8R8A8, destMap.GetData(),
+ destMap.GetStride(), aSurface->GetFormat(), aReadSize);
+ }
+
+ mContext->Unmap(mTexture, 0);
+
+ return result;
+}
+
+CompositorD3D11::CompositorD3D11(CompositorBridgeParent* aParent,
+ widget::CompositorWidget* aWidget)
+ : Compositor(aWidget, aParent),
+ mWindowRTCopy(nullptr),
+ mAttachments(nullptr),
+ mHwnd(nullptr),
+ mDisableSequenceForNextFrame(false),
+ mAllowPartialPresents(false),
+ mIsDoubleBuffered(false),
+ mVerifyBuffersFailed(false),
+ mUseMutexOnPresent(false),
+ mUseForSoftwareWebRender(false) {
+ mUseMutexOnPresent = StaticPrefs::gfx_use_mutex_on_present_AtStartup();
+}
+
+CompositorD3D11::~CompositorD3D11() {}
+
+template <typename VertexType>
+void CompositorD3D11::SetVertexBuffer(ID3D11Buffer* aBuffer) {
+ UINT size = sizeof(VertexType);
+ UINT offset = 0;
+ mContext->IASetVertexBuffers(0, 1, &aBuffer, &size, &offset);
+}
+
+bool CompositorD3D11::SupportsLayerGeometry() const {
+ return StaticPrefs::layers_geometry_d3d11_enabled();
+}
+
+bool CompositorD3D11::UpdateDynamicVertexBuffer(
+ const nsTArray<gfx::TexturedTriangle>& aTriangles) {
+ HRESULT hr;
+
+ // Resize the dynamic vertex buffer if needed.
+ if (!mAttachments->EnsureTriangleBuffer(aTriangles.Length())) {
+ return false;
+ }
+
+ D3D11_MAPPED_SUBRESOURCE resource{};
+ hr = mContext->Map(mAttachments->mDynamicVertexBuffer, 0,
+ D3D11_MAP_WRITE_DISCARD, 0, &resource);
+
+ if (Failed(hr, "map dynamic vertex buffer")) {
+ return false;
+ }
+
+ const nsTArray<TexturedVertex> vertices =
+ TexturedTrianglesToVertexArray(aTriangles);
+
+ memcpy(resource.pData, vertices.Elements(),
+ vertices.Length() * sizeof(TexturedVertex));
+
+ mContext->Unmap(mAttachments->mDynamicVertexBuffer, 0);
+
+ return true;
+}
+
+bool CompositorD3D11::Initialize(nsCString* const out_failureReason) {
+ ScopedGfxFeatureReporter reporter("D3D11 Layers");
+
+ HRESULT hr;
+
+ DeviceManagerDx::Get()->GetCompositorDevices(&mDevice, &mAttachments);
+ if (!mDevice) {
+ gfxCriticalNote << "[D3D11] failed to get compositor device.";
+ *out_failureReason = "FEATURE_FAILURE_D3D11_NO_DEVICE";
+ return false;
+ }
+ if (!mAttachments || !mAttachments->IsValid()) {
+ gfxCriticalNote << "[D3D11] failed to get compositor device attachments";
+ *out_failureReason = mAttachments ? mAttachments->GetFailureId()
+ : "FEATURE_FAILURE_NO_ATTACHMENTS"_ns;
+ return false;
+ }
+
+ mDevice->GetImmediateContext(getter_AddRefs(mContext));
+ if (!mContext) {
+ gfxCriticalNote << "[D3D11] failed to get immediate context";
+ *out_failureReason = "FEATURE_FAILURE_D3D11_CONTEXT";
+ return false;
+ }
+
+ mDiagnostics = MakeUnique<DiagnosticsD3D11>(mDevice, mContext);
+ mFeatureLevel = mDevice->GetFeatureLevel();
+
+ mHwnd = mWidget->AsWindows()->GetHwnd();
+
+ memset(&mVSConstants, 0, sizeof(VertexShaderConstants));
+
+ 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()));
+
+ RefPtr<IDXGIFactory2> dxgiFactory2;
+ hr = dxgiFactory->QueryInterface(
+ (IDXGIFactory2**)getter_AddRefs(dxgiFactory2));
+
+#if (_WIN32_WINDOWS_MAXVER >= 0x0A00)
+ if (gfxVars::UseDoubleBufferingWithCompositor() && SUCCEEDED(hr) &&
+ dxgiFactory2) {
+ // DXGI_SCALING_NONE is not available on Windows 7 with Platform Update.
+ // This looks awful for things like the awesome bar and browser window
+ // resizing so we don't use a flip buffer chain here. When using
+ // EFFECT_SEQUENTIAL it looks like windows doesn't stretch the surface
+ // when resizing. We chose not to run this before Windows 10 because it
+ // appears sometimes this breaks our ability to test ASAP compositing.
+ RefPtr<IDXGISwapChain1> swapChain;
+
+ DXGI_SWAP_CHAIN_DESC1 swapDesc;
+ ::ZeroMemory(&swapDesc, sizeof(swapDesc));
+ swapDesc.Width = 0;
+ swapDesc.Height = 0;
+ swapDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
+ swapDesc.SampleDesc.Count = 1;
+ swapDesc.SampleDesc.Quality = 0;
+ swapDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
+ swapDesc.BufferCount = 2;
+ swapDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL;
+ swapDesc.Scaling = DXGI_SCALING_NONE;
+ mIsDoubleBuffered = true;
+ swapDesc.Flags = 0;
+
+ /**
+ * 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 = dxgiFactory2->CreateSwapChainForHwnd(mDevice, mHwnd, &swapDesc,
+ nullptr, nullptr,
+ getter_AddRefs(swapChain));
+ if (SUCCEEDED(hr)) {
+ DXGI_RGBA color = {1.0f, 1.0f, 1.0f, 1.0f};
+ swapChain->SetBackgroundColor(&color);
+
+ mSwapChain = swapChain;
+ } else if (mWidget->AsWindows()->GetCompositorHwnd()) {
+ // Destroy compositor window.
+ mWidget->AsWindows()->DestroyCompositorWindow();
+ mHwnd = mWidget->AsWindows()->GetHwnd();
+ }
+ }
+
+ // In some configurations double buffering may have failed with an
+ // ACCESS_DENIED error.
+ if (!mSwapChain)
+#endif
+ {
+ if (mWidget->AsWindows()->GetCompositorHwnd()) {
+ // Destroy compositor window.
+ mWidget->AsWindows()->DestroyCompositorWindow();
+ mHwnd = mWidget->AsWindows()->GetHwnd();
+ }
+
+ 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")) {
+ *out_failureReason = "FEATURE_FAILURE_D3D11_SWAP_CHAIN";
+ return false;
+ }
+ }
+
+ // We need this because we don't want DXGI to respond to Alt+Enter.
+ dxgiFactory->MakeWindowAssociation(mHwnd, DXGI_MWA_NO_WINDOW_CHANGES);
+ }
+
+ if (!mWidget->InitCompositor(this)) {
+ *out_failureReason = "FEATURE_FAILURE_D3D11_INIT_COMPOSITOR";
+ return false;
+ }
+
+ mAllowPartialPresents = CanUsePartialPresents(mDevice);
+
+ reporter.SetSuccessful();
+ return true;
+}
+
+bool CanUsePartialPresents(ID3D11Device* aDevice) {
+ if (StaticPrefs::gfx_partialpresent_force() > 0) {
+ return true;
+ }
+ if (StaticPrefs::gfx_partialpresent_force() < 0) {
+ return false;
+ }
+ if (DeviceManagerDx::Get()->IsWARP()) {
+ return true;
+ }
+
+ DXGI_ADAPTER_DESC desc;
+ if (!D3D11Checks::GetDxgiDesc(aDevice, &desc)) {
+ return false;
+ }
+
+ // We have to disable partial presents on NVIDIA (bug 1189940).
+ if (desc.VendorId == 0x10de) {
+ return false;
+ }
+
+ 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.mParentProcessType = XRE_GetProcessType();
+ ident.mParentBackend = LayersBackend::LAYERS_D3D11;
+ if (mWidget) {
+ ident.mUseCompositorWnd = !!mWidget->AsWindows()->GetCompositorHwnd();
+ }
+ if (mAttachments->mSyncObject) {
+ ident.mSyncHandle = mAttachments->mSyncObject->GetSyncHandle();
+ }
+ 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.IsZeroArea());
+
+ if (aRect.IsZeroArea()) {
+ 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.IsZeroArea());
+
+ if (aRect.IsZeroArea()) {
+ 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::ShouldAllowFrameRecording() const {
+#ifdef MOZ_GECKO_PROFILER
+ return mAllowFrameRecording ||
+ profiler_feature_active(ProfilerFeature::Screenshots);
+#else
+ return mAllowFrameRecording;
+#endif
+}
+
+already_AddRefed<CompositingRenderTarget>
+CompositorD3D11::GetWindowRenderTarget() const {
+ if (!ShouldAllowFrameRecording()) {
+ return nullptr;
+ }
+
+ if (!mDefaultRT) {
+ return nullptr;
+ }
+
+ const IntSize size = mDefaultRT->GetSize();
+
+ RefPtr<ID3D11Texture2D> rtTexture;
+
+ if (!mWindowRTCopy || mWindowRTCopy->GetSize() != size) {
+ /*
+ * The compositor screenshots infrastructure is going to scale down the
+ * render target returned by this method. However, mDefaultRT does not
+ * contain a texture created wth the D3D11_BIND_SHADER_RESOURCE flag, so if
+ * we were to simply return mDefaultRT then scaling would fail.
+ */
+ CD3D11_TEXTURE2D_DESC desc(DXGI_FORMAT_B8G8R8A8_UNORM, size.width,
+ size.height, 1, 1, D3D11_BIND_SHADER_RESOURCE);
+
+ HRESULT hr =
+ mDevice->CreateTexture2D(&desc, nullptr, getter_AddRefs(rtTexture));
+ if (FAILED(hr)) {
+ return nullptr;
+ }
+
+ mWindowRTCopy = MakeRefPtr<CompositingRenderTargetD3D11>(
+ rtTexture, IntPoint(0, 0), DXGI_FORMAT_B8G8R8A8_UNORM);
+ mWindowRTCopy->SetSize(size);
+ } else {
+ rtTexture = mWindowRTCopy->GetD3D11Texture();
+ }
+
+ const RefPtr<ID3D11Texture2D> sourceTexture = mDefaultRT->GetD3D11Texture();
+ mContext->CopyResource(rtTexture, sourceTexture);
+
+ return RefPtr<CompositingRenderTarget>(
+ static_cast<CompositingRenderTarget*>(mWindowRTCopy))
+ .forget();
+}
+
+bool CompositorD3D11::ReadbackRenderTarget(CompositingRenderTarget* aSource,
+ AsyncReadbackBuffer* aDest) {
+ RefPtr<CompositingRenderTargetD3D11> srcTexture =
+ static_cast<CompositingRenderTargetD3D11*>(aSource);
+ RefPtr<AsyncReadbackBufferD3D11> destBuffer =
+ static_cast<AsyncReadbackBufferD3D11*>(aDest);
+
+ mContext->CopyResource(destBuffer->GetTexture(),
+ srcTexture->GetD3D11Texture());
+
+ return true;
+}
+
+already_AddRefed<AsyncReadbackBuffer>
+CompositorD3D11::CreateAsyncReadbackBuffer(const gfx::IntSize& aSize) {
+ RefPtr<ID3D11Texture2D> texture;
+
+ CD3D11_TEXTURE2D_DESC desc(DXGI_FORMAT_B8G8R8A8_UNORM, aSize.width,
+ aSize.height, 1, 1, 0, D3D11_USAGE_STAGING,
+ D3D11_CPU_ACCESS_READ);
+
+ HRESULT hr =
+ mDevice->CreateTexture2D(&desc, nullptr, getter_AddRefs(texture));
+
+ if (FAILED(hr)) {
+ HandleError(hr);
+ return nullptr;
+ }
+
+ return MakeAndAddRef<AsyncReadbackBufferD3D11>(mContext, texture, aSize);
+}
+
+bool CompositorD3D11::BlitRenderTarget(CompositingRenderTarget* aSource,
+ const gfx::IntSize& aSourceSize,
+ const gfx::IntSize& aDestSize) {
+ RefPtr<CompositingRenderTargetD3D11> source =
+ static_cast<CompositingRenderTargetD3D11*>(aSource);
+
+ RefPtr<TexturedEffect> texturedEffect = CreateTexturedEffect(
+ SurfaceFormat::B8G8R8A8, source, SamplingFilter::LINEAR, true);
+ texturedEffect->mTextureCoords =
+ Rect(0, 0, Float(aSourceSize.width) / Float(source->GetSize().width),
+ Float(aSourceSize.height) / Float(source->GetSize().height));
+
+ EffectChain effect;
+ effect.mPrimaryEffect = texturedEffect;
+
+ const Float scaleX = Float(aDestSize.width) / Float(aSourceSize.width);
+ const Float scaleY = Float(aDestSize.height) / (aSourceSize.height);
+ const Matrix4x4 transform = Matrix4x4::Scaling(scaleX, scaleY, 1.0f);
+
+ const Rect sourceRect(0, 0, aSourceSize.width, aSourceSize.height);
+
+ DrawQuad(sourceRect, IntRect(0, 0, aDestSize.width, aDestSize.height), effect,
+ 1.0f, transform, sourceRect);
+
+ return true;
+}
+
+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,
+ const bool aUseBlendShader,
+ const MaskType aMaskType) {
+ if (aUseBlendShader) {
+ return mAttachments->mBlendShader[MaskType::MaskNone];
+ }
+
+ 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::NV12:
+ return mAttachments->mNV12Shader[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) {
+ if (aRect.IsEmpty()) {
+ return;
+ }
+
+ 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);
+
+ // Restore the default blend state.
+ mContext->OMSetBlendState(mAttachments->mPremulBlendState, sBlendFactor,
+ 0xFFFFFFFF);
+}
+
+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;
+ case EffectTypes::NV12:
+ return PS_LAYER_NV12;
+ 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) {
+ DrawGeometry(aRect, aRect, aClipRect, aEffectChain, aOpacity, aTransform,
+ aVisibleRect);
+}
+
+void CompositorD3D11::DrawTriangles(
+ const nsTArray<gfx::TexturedTriangle>& aTriangles, const gfx::Rect& aRect,
+ const gfx::IntRect& aClipRect, const EffectChain& aEffectChain,
+ gfx::Float aOpacity, const gfx::Matrix4x4& aTransform,
+ const gfx::Rect& aVisibleRect) {
+ DrawGeometry(aTriangles, aRect, aClipRect, aEffectChain, aOpacity, aTransform,
+ aVisibleRect);
+}
+
+void CompositorD3D11::PrepareDynamicVertexBuffer() {
+ mContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
+ mContext->IASetInputLayout(mAttachments->mDynamicInputLayout);
+ SetVertexBuffer<TexturedVertex>(mAttachments->mDynamicVertexBuffer);
+}
+
+void CompositorD3D11::PrepareStaticVertexBuffer() {
+ mContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP);
+ mContext->IASetInputLayout(mAttachments->mInputLayout);
+ SetVertexBuffer<Vertex>(mAttachments->mVertexBuffer);
+}
+
+void CompositorD3D11::Draw(const nsTArray<gfx::TexturedTriangle>& aTriangles,
+ const gfx::Rect*) {
+ if (!UpdateConstantBuffers()) {
+ NS_WARNING("Failed to update shader constant buffers");
+ return;
+ }
+
+ PrepareDynamicVertexBuffer();
+
+ if (!UpdateDynamicVertexBuffer(aTriangles)) {
+ NS_WARNING("Failed to update shader dynamic buffers");
+ return;
+ }
+
+ mContext->Draw(3 * aTriangles.Length(), 0);
+
+ PrepareStaticVertexBuffer();
+}
+
+void CompositorD3D11::Draw(const gfx::Rect& aRect,
+ const gfx::Rect* aTexCoords) {
+ Rect layerRects[4] = {aRect};
+ Rect textureRects[4] = {};
+ size_t rects = 1;
+
+ if (aTexCoords) {
+ rects = DecomposeIntoNoRepeatRects(aRect, *aTexCoords, &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);
+ }
+}
+
+ID3D11VertexShader* CompositorD3D11::GetVSForGeometry(
+ const nsTArray<gfx::TexturedTriangle>& aTriangles,
+ const bool aUseBlendShaders, const MaskType aMaskType) {
+ return aUseBlendShaders ? mAttachments->mVSDynamicBlendShader[aMaskType]
+ : mAttachments->mVSDynamicShader[aMaskType];
+}
+
+ID3D11VertexShader* CompositorD3D11::GetVSForGeometry(
+ const gfx::Rect& aRect, const bool aUseBlendShaders,
+ const MaskType aMaskType) {
+ return aUseBlendShaders ? mAttachments->mVSQuadBlendShader[aMaskType]
+ : mAttachments->mVSQuadShader[aMaskType];
+}
+
+template <typename Geometry>
+void CompositorD3D11::DrawGeometry(const Geometry& aGeometry,
+ 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");
+
+ 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));
+ bounds = maskTransform.As2D().TransformBounds(bounds);
+
+ Matrix4x4 transform;
+ transform._11 = 1.0f / bounds.Width();
+ transform._22 = 1.0f / bounds.Height();
+ transform._41 = float(-bounds.X()) / bounds.Width();
+ transform._42 = float(-bounds.Y()) / bounds.Height();
+ memcpy(mVSConstants.maskTransform, &transform._11, 64);
+ }
+
+ 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();
+
+ bool useBlendShaders = false;
+ 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()) {
+ useBlendShaders = true;
+
+ 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);
+
+ RefPtr<ID3D11VertexShader> vertexShader =
+ GetVSForGeometry(aGeometry, useBlendShaders, maskType);
+
+ RefPtr<ID3D11PixelShader> pixelShader =
+ GetPSForEffect(aEffectChain.mPrimaryEffect, useBlendShaders, maskType);
+
+ mContext->VSSetShader(vertexShader, nullptr, 0);
+ mContext->PSSetShader(pixelShader, nullptr, 0);
+
+ const Rect* pTexCoordRect = nullptr;
+
+ switch (aEffectChain.mPrimaryEffect->mType) {
+ case EffectTypes::SOLID_COLOR: {
+ DeviceColor 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::NV12: {
+ EffectNV12* effectNV12 =
+ static_cast<EffectNV12*>(aEffectChain.mPrimaryEffect.get());
+
+ pTexCoordRect = &effectNV12->mTextureCoords;
+
+ TextureSourceD3D11* source = effectNV12->mTexture->AsSourceD3D11();
+ if (!source) {
+ NS_WARNING("Missing texture source!");
+ return;
+ }
+
+ RefPtr<ID3D11Texture2D> texture = source->GetD3D11Texture();
+ if (!texture) {
+ NS_WARNING("No texture found in texture source!");
+ }
+
+ D3D11_TEXTURE2D_DESC sourceDesc;
+ texture->GetDesc(&sourceDesc);
+ MOZ_DIAGNOSTIC_ASSERT(sourceDesc.Format == DXGI_FORMAT_NV12 ||
+ sourceDesc.Format == DXGI_FORMAT_P010 ||
+ sourceDesc.Format == DXGI_FORMAT_P016);
+
+ // Might want to cache these for efficiency.
+ RefPtr<ID3D11ShaderResourceView> srViewY;
+ RefPtr<ID3D11ShaderResourceView> srViewCbCr;
+ D3D11_SHADER_RESOURCE_VIEW_DESC srvDesc =
+ CD3D11_SHADER_RESOURCE_VIEW_DESC(D3D11_SRV_DIMENSION_TEXTURE2D,
+ sourceDesc.Format == DXGI_FORMAT_NV12
+ ? DXGI_FORMAT_R8_UNORM
+ : DXGI_FORMAT_R16_UNORM);
+ mDevice->CreateShaderResourceView(texture, &srvDesc,
+ getter_AddRefs(srViewY));
+ srvDesc.Format = sourceDesc.Format == DXGI_FORMAT_NV12
+ ? DXGI_FORMAT_R8G8_UNORM
+ : DXGI_FORMAT_R16G16_UNORM;
+ mDevice->CreateShaderResourceView(texture, &srvDesc,
+ getter_AddRefs(srViewCbCr));
+
+ ID3D11ShaderResourceView* views[] = {srViewY, srViewCbCr};
+ mContext->PSSetShaderResources(TexSlot::Y, 2, views);
+
+ const float* yuvToRgb =
+ gfxUtils::YuvToRgbMatrix4x3RowMajor(effectNV12->mYUVColorSpace);
+ memcpy(&mPSConstants.yuvColorMatrix, yuvToRgb,
+ sizeof(mPSConstants.yuvColorMatrix));
+ mPSConstants.vCoefficient[0] =
+ RescalingFactorForColorDepth(effectNV12->mColorDepth);
+
+ SetSamplerForSamplingFilter(effectNV12->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;
+ }
+
+ const float* yuvToRgb =
+ gfxUtils::YuvToRgbMatrix4x3RowMajor(ycbcrEffect->mYUVColorSpace);
+ memcpy(&mPSConstants.yuvColorMatrix, yuvToRgb,
+ sizeof(mPSConstants.yuvColorMatrix));
+
+ // Adjust range according to the bit depth.
+ mPSConstants.vCoefficient[0] =
+ RescalingFactorForColorDepth(ycbcrEffect->mColorDepth);
+
+ 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(LayerManager::LayersComponentAlphaEnabled());
+ 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;
+ }
+
+ Draw(aGeometry, pTexCoordRect);
+
+ if (restoreBlendMode) {
+ mContext->OMSetBlendState(mAttachments->mPremulBlendState, sBlendFactor,
+ 0xFFFFFFFF);
+ }
+}
+
+Maybe<IntRect> CompositorD3D11::BeginFrameForWindow(
+ const nsIntRegion& aInvalidRegion, const Maybe<IntRect>& aClipRect,
+ const IntRect& aRenderBounds, const nsIntRegion& aOpaqueRegion) {
+ MOZ_RELEASE_ASSERT(!mTarget, "mTarget not cleared properly");
+ return BeginFrame(aInvalidRegion, aClipRect, aRenderBounds, aOpaqueRegion);
+}
+
+Maybe<IntRect> CompositorD3D11::BeginFrameForTarget(
+ const nsIntRegion& aInvalidRegion, const Maybe<IntRect>& aClipRect,
+ const IntRect& aRenderBounds, const nsIntRegion& aOpaqueRegion,
+ DrawTarget* aTarget, const IntRect& aTargetBounds) {
+ MOZ_RELEASE_ASSERT(!mTarget, "mTarget not cleared properly");
+ mTarget = aTarget; // Will be cleared in EndFrame().
+ mTargetBounds = aTargetBounds;
+ Maybe<IntRect> result =
+ BeginFrame(aInvalidRegion, aClipRect, aRenderBounds, aOpaqueRegion);
+ if (!result) {
+ // Composition has been aborted. Reset mTarget.
+ mTarget = nullptr;
+ }
+ return result;
+}
+
+void CompositorD3D11::BeginFrameForNativeLayers() {
+ MOZ_CRASH("Native layers are not implemented on Windows.");
+}
+
+Maybe<gfx::IntRect> CompositorD3D11::BeginRenderingToNativeLayer(
+ const nsIntRegion& aInvalidRegion, const Maybe<gfx::IntRect>& aClipRect,
+ const nsIntRegion& aOpaqueRegion, NativeLayer* aNativeLayer) {
+ MOZ_CRASH("Native layers are not implemented on Windows.");
+}
+
+void CompositorD3D11::EndRenderingToNativeLayer() {
+ MOZ_CRASH("Native layers are not implemented on Windows.");
+}
+
+Maybe<IntRect> CompositorD3D11::BeginFrame(const nsIntRegion& aInvalidRegion,
+ const Maybe<IntRect>& aClipRect,
+ const IntRect& aRenderBounds,
+ const nsIntRegion& aOpaqueRegion) {
+ // 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 (mWidget->IsHidden()) {
+ // We are not going to render, and not going to call EndFrame so we have to
+ // read-unlock our textures to prevent them from accumulating.
+ ReadUnlockTextures();
+ return Nothing();
+ }
+
+ if (mDevice->GetDeviceRemovedReason() != S_OK) {
+ ReadUnlockTextures();
+
+ if (!mAttachments->IsDeviceReset()) {
+ gfxCriticalNote << "GFX: D3D11 skip BeginFrame with device-removed.";
+
+ // If we are in the GPU process then the main process doesn't
+ // know that a device reset has happened and needs to be informed.
+ //
+ // When CompositorD3D11 is used for Software WebRender, it does not need
+ // to notify device reset. The device reset is notified by WebRender.
+ if (XRE_IsGPUProcess() && !mUseForSoftwareWebRender) {
+ GPUParent::GetSingleton()->NotifyDeviceReset();
+ }
+ mAttachments->SetDeviceReset();
+ }
+ return Nothing();
+ }
+
+ LayoutDeviceIntSize oldSize = mSize;
+
+ EnsureSize();
+
+ IntRect rect = IntRect(IntPoint(0, 0), mSize.ToUnknownSize());
+ // Sometimes the invalid region is larger than we want to draw.
+ nsIntRegion invalidRegionSafe;
+
+ if (mSize != oldSize) {
+ invalidRegionSafe = rect;
+ } else {
+ invalidRegionSafe.And(aInvalidRegion, rect);
+ }
+
+ IntRect invalidRect = invalidRegionSafe.GetBounds();
+
+ IntRect clipRect = invalidRect;
+ if (aClipRect) {
+ clipRect.IntersectRect(clipRect, *aClipRect);
+ }
+
+ if (clipRect.IsEmpty()) {
+ CancelFrame();
+ return Nothing();
+ }
+
+ PrepareStaticVertexBuffer();
+
+ mBackBufferInvalid.Or(mBackBufferInvalid, invalidRegionSafe);
+ if (mIsDoubleBuffered) {
+ mFrontBufferInvalid.Or(mFrontBufferInvalid, invalidRegionSafe);
+ }
+
+ // We have to call UpdateRenderTarget after we've determined the invalid regi
+ // Failed to create a render target or the view.
+ if (!UpdateRenderTarget() || !mDefaultRT || !mDefaultRT->mRTView ||
+ mSize.width <= 0 || mSize.height <= 0) {
+ ReadUnlockTextures();
+ return Nothing();
+ }
+
+ mCurrentClip = mBackBufferInvalid.GetBounds();
+
+ mContext->RSSetState(mAttachments->mRasterizerState);
+
+ SetRenderTarget(mDefaultRT);
+
+ IntRegion regionToClear(mCurrentClip);
+ regionToClear.Sub(regionToClear, aOpaqueRegion);
+
+ ClearRect(Rect(regionToClear.GetBounds()));
+
+ mContext->OMSetBlendState(mAttachments->mPremulBlendState, sBlendFactor,
+ 0xFFFFFFFF);
+
+ if (mAttachments->mSyncObject) {
+ if (!mAttachments->mSyncObject->Synchronize()) {
+ // It's timeout here. Since the timeout is related to the driver-removed,
+ // skip this frame.
+ return Nothing();
+ }
+ }
+
+ if (StaticPrefs::layers_acceleration_draw_fps()) {
+ uint32_t pixelsPerFrame = 0;
+ for (auto iter = mBackBufferInvalid.RectIter(); !iter.Done(); iter.Next()) {
+ pixelsPerFrame += iter.Get().Width() * iter.Get().Height();
+ }
+
+ mDiagnostics->Start(pixelsPerFrame);
+ }
+
+ return Some(rect);
+}
+
+void CompositorD3D11::NormalDrawingDone() { mDiagnostics->End(); }
+
+void CompositorD3D11::EndFrame() {
+#ifdef MOZ_GECKO_PROFILER
+ if (!profiler_feature_active(ProfilerFeature::Screenshots) && mWindowRTCopy) {
+ mWindowRTCopy = nullptr;
+ }
+#endif // MOZ_GECKO_PROFILER
+
+ if (!mDefaultRT) {
+ Compositor::EndFrame();
+ mTarget = nullptr;
+ return;
+ }
+
+ if (XRE_IsParentProcess() && mDevice->GetDeviceRemovedReason() != S_OK) {
+ gfxCriticalNote << "GFX: D3D11 skip EndFrame with device-removed.";
+ Compositor::EndFrame();
+ mTarget = nullptr;
+ mCurrentRT = nullptr;
+ return;
+ }
+
+ LayoutDeviceIntSize oldSize = mSize;
+ EnsureSize();
+ if (mSize.width <= 0 || mSize.height <= 0) {
+ Compositor::EndFrame();
+ mTarget = nullptr;
+ return;
+ }
+
+ RefPtr<ID3D11Query> query;
+ if (mRecycledQuery) {
+ query = mRecycledQuery.forget();
+ } else {
+ CD3D11_QUERY_DESC desc(D3D11_QUERY_EVENT);
+ mDevice->CreateQuery(&desc, getter_AddRefs(query));
+ }
+ if (query) {
+ mContext->End(query);
+ }
+
+ if (oldSize == mSize) {
+ Present();
+ if (StaticPrefs::gfx_compositor_clearstate()) {
+ mContext->ClearState();
+ }
+ } else {
+ mDiagnostics->Cancel();
+ }
+
+ // Block until the previous frame's work has been completed.
+ if (mQuery) {
+ BOOL result;
+ WaitForFrameGPUQuery(mDevice, mContext, mQuery, &result);
+ // Store the query for recycling
+ mRecycledQuery = mQuery;
+ }
+ // Store the query for this frame so we can flush it next time.
+ mQuery = query;
+
+ Compositor::EndFrame();
+ mTarget = nullptr;
+ mCurrentRT = nullptr;
+}
+
+void CompositorD3D11::GetFrameStats(GPUStats* aStats) {
+ mDiagnostics->Query(aStats);
+}
+
+void CompositorD3D11::Present() {
+ UINT presentInterval = 0;
+
+ bool isWARP = DeviceManagerDx::Get()->IsWARP();
+ if (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;
+ }
+
+ // This must be called before present so our back buffer has the validated
+ // window content.
+ if (mTarget) {
+ PaintToTarget();
+ }
+
+ RefPtr<IDXGISwapChain1> chain;
+ HRESULT hr =
+ mSwapChain->QueryInterface((IDXGISwapChain1**)getter_AddRefs(chain));
+
+ RefPtr<IDXGIKeyedMutex> mutex;
+ if (mUseMutexOnPresent && mAttachments->mSyncObject) {
+ SyncObjectD3D11Host* d3dSyncObj =
+ (SyncObjectD3D11Host*)mAttachments->mSyncObject.get();
+ mutex = d3dSyncObj->GetKeyedMutex();
+ MOZ_ASSERT(mutex);
+ }
+
+ if (SUCCEEDED(hr) && mAllowPartialPresents) {
+ DXGI_PRESENT_PARAMETERS params;
+ PodZero(&params);
+ params.DirtyRectsCount = mBackBufferInvalid.GetNumRects();
+ StackArray<RECT, 4> rects(params.DirtyRectsCount);
+
+ uint32_t i = 0;
+ for (auto iter = mBackBufferInvalid.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;
+
+ if (mutex) {
+ hr = mutex->AcquireSync(0, 2000);
+ NS_ENSURE_TRUE_VOID(SUCCEEDED(hr));
+ }
+
+ chain->Present1(
+ presentInterval,
+ mDisableSequenceForNextFrame ? DXGI_PRESENT_DO_NOT_SEQUENCE : 0,
+ &params);
+
+ if (mutex) {
+ mutex->ReleaseSync(0);
+ }
+ } else {
+ if (mutex) {
+ hr = mutex->AcquireSync(0, 2000);
+ NS_ENSURE_TRUE_VOID(SUCCEEDED(hr));
+ }
+
+ hr = mSwapChain->Present(
+ 0, mDisableSequenceForNextFrame ? DXGI_PRESENT_DO_NOT_SEQUENCE : 0);
+
+ if (mutex) {
+ mutex->ReleaseSync(0);
+ }
+
+ if (FAILED(hr)) {
+ gfxCriticalNote << "D3D11 swap chain preset failed " << hexa(hr);
+ HandleError(hr);
+ }
+ }
+
+ if (mIsDoubleBuffered) {
+ mBackBufferInvalid = mFrontBufferInvalid;
+ mFrontBufferInvalid.SetEmpty();
+ } else {
+ mBackBufferInvalid.SetEmpty();
+ }
+
+ mDisableSequenceForNextFrame = false;
+}
+
+void CompositorD3D11::CancelFrame(bool aNeedFlush) {
+ ReadUnlockTextures();
+ // Flush the context, otherwise the driver might hold some resources alive
+ // until the next flush or present.
+ if (aNeedFlush) {
+ mContext->Flush();
+ }
+}
+
+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 && size == mBufferSize) {
+ mSwapChain->Present(0, 0);
+ if (mIsDoubleBuffered) {
+ // Make sure we present what was the front buffer before that we know is
+ // completely valid. This non v-synced present should be pretty much
+ // 'free' for a flip chain.
+ 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() {
+ mWidget->AsWindows()->UpdateCompositorWndSizeIfNecessary();
+
+ 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(0, 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);
+ mBufferSize = LayoutDeviceIntSize();
+ } else {
+ mBufferSize = mSize;
+ }
+
+ mBackBufferInvalid = mFrontBufferInvalid =
+ IntRect(0, 0, mSize.width, mSize.height);
+
+ return !mVerifyBuffersFailed;
+}
+
+bool CompositorD3D11::UpdateRenderTarget() {
+ HRESULT hr;
+
+ RefPtr<ID3D11Texture2D> backBuf;
+
+ if (!VerifyBufferSize()) {
+ gfxCriticalNote << "Failed VerifyBufferSize in UpdateRenderTarget "
+ << mSize;
+ return false;
+ }
+
+ if (mSize.width <= 0 || mSize.height <= 0) {
+ gfxCriticalNote << "Invalid size in UpdateRenderTarget " << mSize << ", "
+ << (int)mVerifyBuffersFailed;
+ return false;
+ }
+
+ 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;
+ }
+
+ IntRegion validFront;
+ validFront.Sub(mBackBufferInvalid, mFrontBufferInvalid);
+
+ if (mIsDoubleBuffered && !validFront.IsEmpty()) {
+ RefPtr<ID3D11Texture2D> frontBuf;
+ hr = mSwapChain->GetBuffer(1, __uuidof(ID3D11Texture2D),
+ (void**)frontBuf.StartAssignment());
+
+ if (SUCCEEDED(hr)) {
+ for (auto iter = validFront.RectIter(); !iter.Done(); iter.Next()) {
+ const IntRect& rect = iter.Get();
+
+ D3D11_BOX box;
+ box.back = 1;
+ box.front = 0;
+ box.left = rect.X();
+ box.right = rect.XMost();
+ box.top = rect.Y();
+ box.bottom = rect.YMost();
+ mContext->CopySubresourceRegion(backBuf, 0, rect.X(), rect.Y(), 0,
+ frontBuf, 0, &box);
+ }
+ mBackBufferInvalid = mFrontBufferInvalid;
+ }
+ }
+
+ mDefaultRT = new CompositingRenderTargetD3D11(backBuf, IntPoint(0, 0));
+ mDefaultRT->SetSize(mSize.ToUnknownSize());
+
+ return true;
+}
+
+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;
+}
+
+SyncObjectHost* CompositorD3D11::GetSyncObject() {
+ if (mAttachments) {
+ return mAttachments->mSyncObject;
+ }
+ return nullptr;
+}
+
+void CompositorD3D11::HandleError(HRESULT hr, Severity aSeverity) {
+ if (SUCCEEDED(hr)) {
+ return;
+ }
+
+ if (aSeverity == Critical) {
+ MOZ_CRASH("GFX: Unrecoverable D3D11 error");
+ }
+
+ if (mDevice && DeviceManagerDx::Get()->GetCompositorDevice() != mDevice) {
+ gfxCriticalNote << "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");
+ }
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/d3d11/CompositorD3D11.h b/gfx/layers/d3d11/CompositorD3D11.h
new file mode 100644
index 0000000000..c1a0c81c23
--- /dev/null
+++ b/gfx/layers/d3d11/CompositorD3D11.h
@@ -0,0 +1,303 @@
+/* -*- 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_COMPOSITORD3D11_H
+#define MOZILLA_GFX_COMPOSITORD3D11_H
+
+#include "mozilla/gfx/2D.h"
+#include "gfx2DGlue.h"
+#include "mozilla/layers/Compositor.h"
+#include "TextureD3D11.h"
+#include <d3d11.h>
+#include <dxgi1_2.h>
+#include "ShaderDefinitionsD3D11.h"
+
+class nsWidget;
+
+namespace mozilla {
+namespace layers {
+
+#define LOGD3D11(param)
+
+class DeviceAttachmentsD3D11;
+class DiagnosticsD3D11;
+
+class CompositorD3D11 : public Compositor {
+ public:
+ CompositorD3D11(CompositorBridgeParent* aParent,
+ widget::CompositorWidget* aWidget);
+ virtual ~CompositorD3D11();
+
+ CompositorD3D11* AsCompositorD3D11() override { return this; }
+
+ bool Initialize(nsCString* const out_failureReason) override;
+
+ TextureFactoryIdentifier GetTextureFactoryIdentifier() override;
+
+ already_AddRefed<DataTextureSource> CreateDataTextureSource(
+ TextureFlags aFlags = TextureFlags::NO_FLAGS) override;
+
+ bool CanUseCanvasLayerForSize(const gfx::IntSize& aSize) override;
+ int32_t GetMaxTextureSize() const final;
+
+ void MakeCurrent(MakeCurrentFlags aFlags = 0) override {}
+
+ already_AddRefed<CompositingRenderTarget> CreateRenderTarget(
+ const gfx::IntRect& aRect, SurfaceInitMode aInit) override;
+
+ already_AddRefed<CompositingRenderTarget> CreateRenderTargetFromSource(
+ const gfx::IntRect& aRect, const CompositingRenderTarget* aSource,
+ const gfx::IntPoint& aSourcePoint) override;
+
+ void SetRenderTarget(CompositingRenderTarget* aSurface) override;
+ already_AddRefed<CompositingRenderTarget> GetCurrentRenderTarget()
+ const override {
+ return do_AddRef(mCurrentRT);
+ }
+ already_AddRefed<CompositingRenderTarget> GetWindowRenderTarget()
+ const override;
+
+ bool ReadbackRenderTarget(CompositingRenderTarget* aSource,
+ AsyncReadbackBuffer* aDest) override;
+ already_AddRefed<AsyncReadbackBuffer> CreateAsyncReadbackBuffer(
+ const gfx::IntSize& aSize) override;
+
+ bool BlitRenderTarget(CompositingRenderTarget* aSource,
+ const gfx::IntSize& aSourceSize,
+ const gfx::IntSize& aDestSize) override;
+
+ void SetDestinationSurfaceSize(const gfx::IntSize& aSize) override {}
+
+ void ClearRect(const gfx::Rect& aRect) override;
+
+ void DrawQuad(const gfx::Rect& aRect, const gfx::IntRect& aClipRect,
+ const EffectChain& aEffectChain, gfx::Float aOpacity,
+ const gfx::Matrix4x4& aTransform,
+ const gfx::Rect& aVisibleRect) override;
+
+ /**
+ * Start a new frame.
+ */
+ Maybe<gfx::IntRect> BeginFrameForWindow(
+ const nsIntRegion& aInvalidRegion, const Maybe<gfx::IntRect>& aClipRect,
+ const gfx::IntRect& aRenderBounds,
+ const nsIntRegion& aOpaqueRegion) override;
+
+ Maybe<gfx::IntRect> BeginFrameForTarget(
+ const nsIntRegion& aInvalidRegion, const Maybe<gfx::IntRect>& aClipRect,
+ const gfx::IntRect& aRenderBounds, const nsIntRegion& aOpaqueRegion,
+ gfx::DrawTarget* aTarget, const gfx::IntRect& aTargetBounds) override;
+
+ void BeginFrameForNativeLayers() override;
+
+ Maybe<gfx::IntRect> BeginRenderingToNativeLayer(
+ const nsIntRegion& aInvalidRegion, const Maybe<gfx::IntRect>& aClipRect,
+ const nsIntRegion& aOpaqueRegion, NativeLayer* aNativeLayer) override;
+
+ void EndRenderingToNativeLayer() override;
+
+ void NormalDrawingDone() override;
+
+ /**
+ * Flush the current frame to the screen.
+ */
+ void EndFrame() override;
+
+ void CancelFrame(bool aNeedFlush = true) override;
+
+ /**
+ * Setup the viewport and projection matrix for rendering
+ * to a window of the given dimensions.
+ */
+ virtual void PrepareViewport(const gfx::IntSize& aSize);
+ virtual void PrepareViewport(const gfx::IntSize& aSize,
+ const gfx::Matrix4x4& aProjection, float aZNear,
+ float aZFar);
+
+ bool SupportsPartialTextureUpdate() override { return true; }
+
+ bool SupportsLayerGeometry() const override;
+
+#ifdef MOZ_DUMP_PAINTING
+ const char* Name() const override { return "Direct3D 11"; }
+#endif
+
+ LayersBackend GetBackendType() const override {
+ return LayersBackend::LAYERS_D3D11;
+ }
+
+ virtual void ForcePresent();
+
+ // For TextureSourceProvider.
+ ID3D11Device* GetD3D11Device() const override { return mDevice; }
+
+ ID3D11Device* GetDevice() { return mDevice; }
+
+ ID3D11DeviceContext* GetDC() { return mContext; }
+
+ virtual void RequestAllowFrameRecording(bool aWillRecord) override {
+ mAllowFrameRecording = aWillRecord;
+ }
+
+ void Readback(gfx::DrawTarget* aDrawTarget) {
+ mTarget = aDrawTarget;
+ mTargetBounds = gfx::IntRect();
+ PaintToTarget();
+ mTarget = nullptr;
+ }
+
+ SyncObjectHost* GetSyncObject();
+
+ void UseForSoftwareWebRender() { mUseForSoftwareWebRender = true; }
+
+ private:
+ enum Severity {
+ Recoverable,
+ DebugAssert,
+ Critical,
+ };
+
+ void HandleError(HRESULT hr, Severity aSeverity = DebugAssert);
+
+ // Same as Failed(), except the severity is critical (with no abort) and
+ // a string prefix must be provided.
+ bool Failed(HRESULT hr, const char* aContext);
+
+ // ensure mSize is up to date with respect to mWidget
+ void EnsureSize();
+ bool VerifyBufferSize();
+ bool UpdateRenderTarget();
+ bool UpdateConstantBuffers();
+ void SetSamplerForSamplingFilter(gfx::SamplingFilter aSamplingFilter);
+
+ ID3D11PixelShader* GetPSForEffect(Effect* aEffect, const bool aUseBlendShader,
+ const MaskType aMaskType);
+ Maybe<gfx::IntRect> BeginFrame(const nsIntRegion& aInvalidRegion,
+ const Maybe<gfx::IntRect>& aClipRect,
+ const gfx::IntRect& aRenderBounds,
+ const nsIntRegion& aOpaqueRegion);
+ void PaintToTarget();
+ RefPtr<ID3D11Texture2D> CreateTexture(const gfx::IntRect& aRect,
+ const CompositingRenderTarget* aSource,
+ const gfx::IntPoint& aSourcePoint);
+ bool CopyBackdrop(const gfx::IntRect& aRect,
+ RefPtr<ID3D11Texture2D>* aOutTexture,
+ RefPtr<ID3D11ShaderResourceView>* aOutView);
+
+ void DrawTriangles(const nsTArray<gfx::TexturedTriangle>& aTriangles,
+ const gfx::Rect& aRect, const gfx::IntRect& aClipRect,
+ const EffectChain& aEffectChain, gfx::Float aOpacity,
+ const gfx::Matrix4x4& aTransform,
+ const gfx::Rect& aVisibleRect) override;
+
+ template <typename Geometry>
+ void DrawGeometry(const Geometry& aGeometry, const gfx::Rect& aRect,
+ const gfx::IntRect& aClipRect,
+ const EffectChain& aEffectChain, gfx::Float aOpacity,
+ const gfx::Matrix4x4& aTransform,
+ const gfx::Rect& aVisibleRect);
+
+ bool UpdateDynamicVertexBuffer(
+ const nsTArray<gfx::TexturedTriangle>& aTriangles);
+
+ void PrepareDynamicVertexBuffer();
+ void PrepareStaticVertexBuffer();
+
+ // Overloads for rendering both rects and triangles with same rendering path
+ void Draw(const nsTArray<gfx::TexturedTriangle>& aGeometry,
+ const gfx::Rect* aTexCoords);
+
+ void Draw(const gfx::Rect& aGeometry, const gfx::Rect* aTexCoords);
+
+ void GetFrameStats(GPUStats* aStats) override;
+
+ void Present();
+
+ ID3D11VertexShader* GetVSForGeometry(
+ const nsTArray<gfx::TexturedTriangle>& aTriangles,
+ const bool aUseBlendShader, const MaskType aMaskType);
+
+ ID3D11VertexShader* GetVSForGeometry(const gfx::Rect& aRect,
+ const bool aUseBlendShader,
+ const MaskType aMaskType);
+
+ template <typename VertexType>
+ void SetVertexBuffer(ID3D11Buffer* aBuffer);
+
+ /**
+ * Whether or not the recorder should be recording frames.
+ *
+ * When this returns true, the CompositorD3D11 will allocate and return window
+ * render targets from |GetWindowRenderTarget|, which otherwise returns
+ * nullptr.
+ *
+ * This will be true when either we are recording a profile with screenshots
+ * enabled or the |LayerManagerComposite| has requested us to record frames
+ * for the |CompositionRecorder|.
+ */
+ bool ShouldAllowFrameRecording() const;
+
+ // The DrawTarget from BeginFrameForTarget, which EndFrame needs to copy the
+ // window contents into.
+ // Only non-null between BeginFrameForTarget and EndFrame.
+ RefPtr<gfx::DrawTarget> mTarget;
+ gfx::IntRect mTargetBounds;
+
+ RefPtr<ID3D11DeviceContext> mContext;
+ RefPtr<ID3D11Device> mDevice;
+ RefPtr<IDXGISwapChain> mSwapChain;
+ RefPtr<CompositingRenderTargetD3D11> mDefaultRT;
+ RefPtr<CompositingRenderTargetD3D11> mCurrentRT;
+ mutable RefPtr<CompositingRenderTargetD3D11> mWindowRTCopy;
+
+ RefPtr<ID3D11Query> mQuery;
+ RefPtr<ID3D11Query> mRecycledQuery;
+
+ RefPtr<DeviceAttachmentsD3D11> mAttachments;
+ UniquePtr<DiagnosticsD3D11> mDiagnostics;
+
+ LayoutDeviceIntSize mSize;
+
+ // The size that we passed to ResizeBuffers to set
+ // the swapchain buffer size.
+ LayoutDeviceIntSize mBufferSize;
+
+ HWND mHwnd;
+
+ D3D_FEATURE_LEVEL mFeatureLevel;
+
+ VertexShaderConstants mVSConstants;
+ PixelShaderConstants mPSConstants;
+ bool mDisableSequenceForNextFrame;
+ bool mAllowPartialPresents;
+ bool mIsDoubleBuffered;
+
+ gfx::IntRegion mFrontBufferInvalid;
+ gfx::IntRegion mBackBufferInvalid;
+ // This is the clip rect applied to the default DrawTarget (i.e. the window)
+ gfx::IntRect mCurrentClip;
+
+ bool mVerifyBuffersFailed;
+ bool mUseMutexOnPresent;
+ bool mAllowFrameRecording;
+
+ bool mUseForSoftwareWebRender;
+};
+
+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;
+} // namespace TexSlot
+
+} // namespace layers
+} // namespace mozilla
+
+#endif
diff --git a/gfx/layers/d3d11/CompositorD3D11.hlsl b/gfx/layers/d3d11/CompositorD3D11.hlsl
new file mode 100644
index 0000000000..c740662548
--- /dev/null
+++ b/gfx/layers/d3d11/CompositorD3D11.hlsl
@@ -0,0 +1,503 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * 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 "BlendingHelpers.hlslh"
+#include "BlendShaderConstants.h"
+
+typedef float4 rect;
+
+float4x4 mLayerTransform : register(vs, c0);
+float4x4 mProjection : register(vs, c4);
+float4 vRenderTargetOffset : register(vs, c8);
+rect vTextureCoords : register(vs, c9);
+rect vLayerQuad : register(vs, c10);
+float4x4 mMaskTransform : register(vs, c11);
+float4x4 mBackdropTransform : register(vs, c15);
+
+float4 fLayerColor : register(ps, c0);
+float fLayerOpacity : register(ps, c1);
+
+// x = layer type
+// y = mask type
+// z = blend op
+// w = is premultiplied
+uint4 iBlendConfig : register(ps, c2);
+
+float fCoefficient : register(ps, c3);
+
+row_major float3x3 mYuvColorMatrix : register(ps, c4);
+
+sampler sSampler : register(ps, s0);
+
+// The mix-blend mega shader uses all variables, so we have to make sure they
+// are assigned fixed slots.
+Texture2D tRGB : register(ps, t0);
+Texture2D tY : register(ps, t1);
+Texture2D tCb : register(ps, t2);
+Texture2D tCr : register(ps, t3);
+Texture2D tRGBWhite : register(ps, t4);
+Texture2D tMask : register(ps, t5);
+Texture2D tBackdrop : register(ps, t6);
+
+struct VS_INPUT {
+ float2 vPosition : POSITION;
+};
+
+struct VS_TEX_INPUT {
+ float2 vPosition : POSITION;
+ float2 vTexCoords : TEXCOORD0;
+};
+
+struct VS_OUTPUT {
+ float4 vPosition : SV_Position;
+ float2 vTexCoords : TEXCOORD0;
+};
+
+struct VS_MASK_OUTPUT {
+ float4 vPosition : SV_Position;
+ float2 vTexCoords : TEXCOORD0;
+ float3 vMaskCoords : TEXCOORD1;
+};
+
+// Combined struct for the mix-blend compatible vertex shaders.
+struct VS_BLEND_OUTPUT {
+ float4 vPosition : SV_Position;
+ float2 vTexCoords : TEXCOORD0;
+ float3 vMaskCoords : TEXCOORD1;
+ float2 vBackdropCoords : TEXCOORD2;
+};
+
+struct PS_OUTPUT {
+ float4 vSrc;
+ float4 vAlpha;
+};
+
+float2 TexCoords(const float2 aPosition)
+{
+ float2 result;
+ const float2 size = vTextureCoords.zw;
+ result.x = vTextureCoords.x + aPosition.x * size.x;
+ result.y = vTextureCoords.y + aPosition.y * size.y;
+
+ return result;
+}
+
+SamplerState LayerTextureSamplerLinear
+{
+ Filter = MIN_MAG_MIP_LINEAR;
+ AddressU = Clamp;
+ AddressV = Clamp;
+};
+
+float4 TransformedPosition(float2 aInPosition)
+{
+ // the current vertex's position on the quad
+ // [x,y,0,1] is mandated by the CSS Transforms spec as the point value to transform
+ float4 position = float4(0, 0, 0, 1);
+
+ // We use 4 component floats to uniquely describe a rectangle, by the structure
+ // of x, y, width, height. This allows us to easily generate the 4 corners
+ // of any rectangle from the 4 corners of the 0,0-1,1 quad that we use as the
+ // stream source for our LayerQuad vertex shader. We do this by doing:
+ // Xout = x + Xin * width
+ // Yout = y + Yin * height
+ float2 size = vLayerQuad.zw;
+ position.x = vLayerQuad.x + aInPosition.x * size.x;
+ position.y = vLayerQuad.y + aInPosition.y * size.y;
+
+ position = mul(mLayerTransform, position);
+
+ return position;
+}
+
+float4 VertexPosition(float4 aTransformedPosition)
+{
+ float4 result;
+ result.w = aTransformedPosition.w;
+ result.xyz = aTransformedPosition.xyz / aTransformedPosition.w;
+ result -= vRenderTargetOffset;
+ result.xyz *= result.w;
+
+ result = mul(mProjection, result);
+
+ return result;
+}
+
+float2 BackdropPosition(float4 aPosition)
+{
+ // Move the position from clip space (-1,1) into 0..1 space.
+ float2 pos;
+ pos.x = (aPosition.x + 1.0) / 2.0;
+ pos.y = 1.0 - (aPosition.y + 1.0) / 2.0;
+
+ return mul(mBackdropTransform, float4(pos.xy, 0, 1.0)).xy;
+}
+
+VS_OUTPUT LayerQuadVS(const VS_INPUT aVertex)
+{
+ VS_OUTPUT outp;
+ float4 position = TransformedPosition(aVertex.vPosition);
+
+ outp.vPosition = VertexPosition(position);
+ outp.vTexCoords = TexCoords(aVertex.vPosition.xy);
+
+ return outp;
+}
+
+float3 MaskCoords(float4 aPosition)
+{
+ // We use the w coord to do non-perspective correct interpolation:
+ // the quad might be transformed in 3D, in which case it will have some
+ // perspective. The graphics card will do perspective-correct interpolation
+ // of the texture, but our mask is already transformed and so we require
+ // linear interpolation. Therefore, we must correct the interpolation
+ // ourselves, we do this by multiplying all coords by w here, and dividing by
+ // w in the pixel shader (post-interpolation), we pass w in outp.vMaskCoords.z.
+ // See http://en.wikipedia.org/wiki/Texture_mapping#Perspective_correctness
+ return float3(mul(mMaskTransform, (aPosition / aPosition.w)).xy, 1.0) * aPosition.w;
+}
+
+VS_MASK_OUTPUT LayerQuadMaskVS(const VS_INPUT aVertex)
+{
+ float4 position = TransformedPosition(aVertex.vPosition);
+
+ VS_MASK_OUTPUT outp;
+ outp.vPosition = VertexPosition(position);
+ outp.vMaskCoords = MaskCoords(position);
+ outp.vTexCoords = TexCoords(aVertex.vPosition.xy);
+ return outp;
+}
+
+VS_OUTPUT LayerDynamicVS(const VS_TEX_INPUT aVertex)
+{
+ VS_OUTPUT outp;
+
+ float4 position = float4(aVertex.vPosition, 0, 1);
+ position = mul(mLayerTransform, position);
+ outp.vPosition = VertexPosition(position);
+
+ outp.vTexCoords = aVertex.vTexCoords;
+
+ return outp;
+}
+
+VS_MASK_OUTPUT LayerDynamicMaskVS(const VS_TEX_INPUT aVertex)
+{
+ VS_MASK_OUTPUT outp;
+
+ float4 position = float4(aVertex.vPosition, 0, 1);
+ position = mul(mLayerTransform, position);
+ outp.vPosition = VertexPosition(position);
+
+ // calculate the position on the mask texture
+ outp.vMaskCoords = MaskCoords(position);
+ outp.vTexCoords = aVertex.vTexCoords;
+ return outp;
+}
+
+float4 RGBAShaderMask(const VS_MASK_OUTPUT aVertex) : SV_Target
+{
+ float2 maskCoords = aVertex.vMaskCoords.xy / aVertex.vMaskCoords.z;
+ float mask = tMask.Sample(sSampler, maskCoords).r;
+ return tRGB.Sample(sSampler, aVertex.vTexCoords) * fLayerOpacity * mask;
+}
+
+float4 RGBShaderMask(const VS_MASK_OUTPUT aVertex) : SV_Target
+{
+ float4 result;
+ result = tRGB.Sample(sSampler, aVertex.vTexCoords) * fLayerOpacity;
+ result.a = fLayerOpacity;
+
+ float2 maskCoords = aVertex.vMaskCoords.xy / aVertex.vMaskCoords.z;
+ float mask = tMask.Sample(sSampler, maskCoords).r;
+ return result * mask;
+}
+
+/* From Rec601:
+[R] [1.1643835616438356, 0.0, 1.5960267857142858] [ Y - 16]
+[G] = [1.1643835616438358, -0.3917622900949137, -0.8129676472377708] x [Cb - 128]
+[B] [1.1643835616438356, 2.017232142857143, 8.862867620416422e-17] [Cr - 128]
+
+For [0,1] instead of [0,255], and to 5 places:
+[R] [1.16438, 0.00000, 1.59603] [ Y - 0.06275]
+[G] = [1.16438, -0.39176, -0.81297] x [Cb - 0.50196]
+[B] [1.16438, 2.01723, 0.00000] [Cr - 0.50196]
+
+From Rec709:
+[R] [1.1643835616438356, 4.2781193979771426e-17, 1.7927410714285714] [ Y - 16]
+[G] = [1.1643835616438358, -0.21324861427372963, -0.532909328559444] x [Cb - 128]
+[B] [1.1643835616438356, 2.1124017857142854, 0.0] [Cr - 128]
+
+For [0,1] instead of [0,255], and to 5 places:
+[R] [1.16438, 0.00000, 1.79274] [ Y - 0.06275]
+[G] = [1.16438, -0.21325, -0.53291] x [Cb - 0.50196]
+[B] [1.16438, 2.11240, 0.00000] [Cr - 0.50196]
+*/
+float4 CalculateYCbCrColor(const float2 aTexCoords)
+{
+ float3 yuv = float3(
+ tY.Sample(sSampler, aTexCoords).r,
+ tCb.Sample(sSampler, aTexCoords).r,
+ tCr.Sample(sSampler, aTexCoords).r);
+ yuv = yuv * fCoefficient - float3(0.06275, 0.50196, 0.50196);
+
+ return float4(mul(mYuvColorMatrix, yuv), 1.0);
+}
+
+float4 CalculateNV12Color(const float2 aTexCoords)
+{
+ float3 yuv = float3(
+ tY.Sample(sSampler, aTexCoords).r,
+ tCb.Sample(sSampler, aTexCoords).r,
+ tCb.Sample(sSampler, aTexCoords).g);
+ yuv = yuv * fCoefficient - float3(0.06275, 0.50196, 0.50196);
+
+ return float4(mul(mYuvColorMatrix, yuv), 1.0);
+}
+
+float4 YCbCrShaderMask(const VS_MASK_OUTPUT aVertex) : SV_Target
+{
+ float2 maskCoords = aVertex.vMaskCoords.xy / aVertex.vMaskCoords.z;
+ float mask = tMask.Sample(sSampler, maskCoords).r;
+
+ return CalculateYCbCrColor(aVertex.vTexCoords) * fLayerOpacity * mask;
+}
+
+float4 NV12ShaderMask(const VS_MASK_OUTPUT aVertex) : SV_Target
+{
+ float2 maskCoords = aVertex.vMaskCoords.xy / aVertex.vMaskCoords.z;
+ float mask = tMask.Sample(sSampler, maskCoords).r;
+
+ return CalculateNV12Color(aVertex.vTexCoords) * fLayerOpacity * mask;
+}
+
+PS_OUTPUT ComponentAlphaShaderMask(const VS_MASK_OUTPUT aVertex) : SV_Target
+{
+ PS_OUTPUT result;
+
+ result.vSrc = tRGB.Sample(sSampler, aVertex.vTexCoords);
+ result.vAlpha = 1.0 - tRGBWhite.Sample(sSampler, aVertex.vTexCoords) + result.vSrc;
+ result.vSrc.a = result.vAlpha.g;
+
+ float2 maskCoords = aVertex.vMaskCoords.xy / aVertex.vMaskCoords.z;
+ float mask = tMask.Sample(sSampler, maskCoords).r;
+ result.vSrc *= fLayerOpacity * mask;
+ result.vAlpha *= fLayerOpacity * mask;
+
+ return result;
+}
+
+float4 SolidColorShaderMask(const VS_MASK_OUTPUT aVertex) : SV_Target
+{
+ float2 maskCoords = aVertex.vMaskCoords.xy / aVertex.vMaskCoords.z;
+ float mask = tMask.Sample(sSampler, maskCoords).r;
+ return fLayerColor * mask;
+}
+
+/*
+ * Un-masked versions
+ *************************************************************
+ */
+float4 RGBAShader(const VS_OUTPUT aVertex) : SV_Target
+{
+ return tRGB.Sample(sSampler, aVertex.vTexCoords) * fLayerOpacity;
+}
+
+float4 RGBShader(const VS_OUTPUT aVertex) : SV_Target
+{
+ float4 result;
+ result = tRGB.Sample(sSampler, aVertex.vTexCoords) * fLayerOpacity;
+ result.a = fLayerOpacity;
+ return result;
+}
+
+float4 YCbCrShader(const VS_OUTPUT aVertex) : SV_Target
+{
+ return CalculateYCbCrColor(aVertex.vTexCoords) * fLayerOpacity;
+}
+
+float4 NV12Shader(const VS_OUTPUT aVertex) : SV_Target
+{
+ return CalculateNV12Color(aVertex.vTexCoords) * fLayerOpacity;
+}
+
+PS_OUTPUT ComponentAlphaShader(const VS_OUTPUT aVertex) : SV_Target
+{
+ PS_OUTPUT result;
+
+ result.vSrc = tRGB.Sample(sSampler, aVertex.vTexCoords);
+ result.vAlpha = 1.0 - tRGBWhite.Sample(sSampler, aVertex.vTexCoords) + result.vSrc;
+ result.vSrc.a = result.vAlpha.g;
+ result.vSrc *= fLayerOpacity;
+ result.vAlpha *= fLayerOpacity;
+ return result;
+}
+
+float4 SolidColorShader(const VS_OUTPUT aVertex) : SV_Target
+{
+ return fLayerColor;
+}
+
+// Mix-blend compatible vertex shaders.
+VS_BLEND_OUTPUT LayerQuadBlendVS(const VS_INPUT aVertex)
+{
+ VS_OUTPUT v = LayerQuadVS(aVertex);
+
+ VS_BLEND_OUTPUT o;
+ o.vPosition = v.vPosition;
+ o.vTexCoords = v.vTexCoords;
+ o.vMaskCoords = float3(0, 0, 0);
+ o.vBackdropCoords = BackdropPosition(v.vPosition);
+ return o;
+}
+
+VS_BLEND_OUTPUT LayerQuadBlendMaskVS(const VS_INPUT aVertex)
+{
+ VS_MASK_OUTPUT v = LayerQuadMaskVS(aVertex);
+
+ VS_BLEND_OUTPUT o;
+ o.vPosition = v.vPosition;
+ o.vTexCoords = v.vTexCoords;
+ o.vMaskCoords = v.vMaskCoords;
+ o.vBackdropCoords = BackdropPosition(v.vPosition);
+ return o;
+}
+
+VS_BLEND_OUTPUT LayerDynamicBlendVS(const VS_TEX_INPUT aVertex)
+{
+ VS_OUTPUT v = LayerDynamicVS(aVertex);
+
+ VS_BLEND_OUTPUT o;
+ o.vPosition = v.vPosition;
+ o.vTexCoords = v.vTexCoords;
+ o.vMaskCoords = float3(0, 0, 0);
+ o.vBackdropCoords = BackdropPosition(v.vPosition);
+ return o;
+}
+
+VS_BLEND_OUTPUT LayerDynamicBlendMaskVS(const VS_TEX_INPUT aVertex)
+{
+ VS_MASK_OUTPUT v = LayerDynamicMaskVS(aVertex);
+
+ VS_BLEND_OUTPUT o;
+ o.vPosition = v.vPosition;
+ o.vTexCoords = v.vTexCoords;
+ o.vMaskCoords = v.vMaskCoords;
+ o.vBackdropCoords = BackdropPosition(v.vPosition);
+ return o;
+}
+
+// The layer type and mask type are specified as constants. We use these to
+// call the correct pixel shader to determine the source color for blending.
+// Unfortunately this also requires some boilerplate to convert VS_BLEND_OUTPUT
+// to a compatible pixel shader input.
+float4 ComputeBlendSourceColor(const VS_BLEND_OUTPUT aVertex)
+{
+ if (iBlendConfig.y == PS_MASK_NONE) {
+ VS_OUTPUT tmp;
+ tmp.vPosition = aVertex.vPosition;
+ tmp.vTexCoords = aVertex.vTexCoords;
+ if (iBlendConfig.x == PS_LAYER_RGB) {
+ return RGBShader(tmp);
+ } else if (iBlendConfig.x == PS_LAYER_RGBA) {
+ return RGBAShader(tmp);
+ } else if (iBlendConfig.x == PS_LAYER_YCBCR) {
+ return YCbCrShader(tmp);
+ } else if (iBlendConfig.x == PS_LAYER_NV12) {
+ return NV12Shader(tmp);
+ } else {
+ return SolidColorShader(tmp);
+ }
+ } else if (iBlendConfig.y == PS_MASK) {
+ VS_MASK_OUTPUT tmp;
+ tmp.vPosition = aVertex.vPosition;
+ tmp.vTexCoords = aVertex.vTexCoords;
+ tmp.vMaskCoords = aVertex.vMaskCoords;
+
+ if (iBlendConfig.x == PS_LAYER_RGB) {
+ return RGBShaderMask(tmp);
+ } else if (iBlendConfig.x == PS_LAYER_RGBA) {
+ return RGBAShaderMask(tmp);
+ } else if (iBlendConfig.x == PS_LAYER_YCBCR) {
+ return YCbCrShaderMask(tmp);
+ } else if (iBlendConfig.x == PS_LAYER_NV12) {
+ return NV12ShaderMask(tmp);
+ } else {
+ return SolidColorShaderMask(tmp);
+ }
+ } else {
+ return float4(0.0, 0.0, 0.0, 1.0);
+ }
+}
+
+float3 ChooseBlendFunc(float3 dest, float3 src)
+{
+ [flatten] switch (iBlendConfig.z) {
+ case PS_BLEND_MULTIPLY:
+ return BlendMultiply(dest, src);
+ case PS_BLEND_SCREEN:
+ return BlendScreen(dest, src);
+ case PS_BLEND_OVERLAY:
+ return BlendOverlay(dest, src);
+ case PS_BLEND_DARKEN:
+ return BlendDarken(dest, src);
+ case PS_BLEND_LIGHTEN:
+ return BlendLighten(dest, src);
+ case PS_BLEND_COLOR_DODGE:
+ return BlendColorDodge(dest, src);
+ case PS_BLEND_COLOR_BURN:
+ return BlendColorBurn(dest, src);
+ case PS_BLEND_HARD_LIGHT:
+ return BlendHardLight(dest, src);
+ case PS_BLEND_SOFT_LIGHT:
+ return BlendSoftLight(dest, src);
+ case PS_BLEND_DIFFERENCE:
+ return BlendDifference(dest, src);
+ case PS_BLEND_EXCLUSION:
+ return BlendExclusion(dest, src);
+ case PS_BLEND_HUE:
+ return BlendHue(dest, src);
+ case PS_BLEND_SATURATION:
+ return BlendSaturation(dest, src);
+ case PS_BLEND_COLOR:
+ return BlendColor(dest, src);
+ case PS_BLEND_LUMINOSITY:
+ return BlendLuminosity(dest, src);
+ default:
+ return float3(0, 0, 0);
+ }
+}
+
+float4 BlendShader(const VS_BLEND_OUTPUT aVertex) : SV_Target
+{
+ float4 backdrop = tBackdrop.Sample(sSampler, aVertex.vBackdropCoords.xy);
+ float4 source = ComputeBlendSourceColor(aVertex);
+
+ // Shortcut when the backdrop or source alpha is 0, otherwise we may leak
+ // infinity into the blend function and return incorrect results.
+ if (backdrop.a == 0.0) {
+ return source;
+ }
+ if (source.a == 0.0) {
+ return float4(0, 0, 0, 0);
+ }
+
+ // The spec assumes there is no premultiplied alpha. The backdrop is always
+ // premultiplied, so undo the premultiply. If the source is premultiplied we
+ // must fix that as well.
+ backdrop.rgb /= backdrop.a;
+ if (iBlendConfig.w) {
+ source.rgb /= source.a;
+ }
+
+ float4 result;
+ result.rgb = ChooseBlendFunc(backdrop.rgb, source.rgb);
+ result.a = source.a;
+
+ // Factor backdrop alpha, then premultiply for the final OP_OVER.
+ result.rgb = (1.0 - backdrop.a) * source.rgb + backdrop.a * result.rgb;
+ result.rgb *= result.a;
+ return result;
+}
diff --git a/gfx/layers/d3d11/DeviceAttachmentsD3D11.cpp b/gfx/layers/d3d11/DeviceAttachmentsD3D11.cpp
new file mode 100644
index 0000000000..5cca7b111f
--- /dev/null
+++ b/gfx/layers/d3d11/DeviceAttachmentsD3D11.cpp
@@ -0,0 +1,342 @@
+/* -*- 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/. */
+
+#include "DeviceAttachmentsD3D11.h"
+#include "mozilla/Telemetry.h"
+#include "mozilla/layers/Compositor.h"
+#include "CompositorD3D11Shaders.h"
+#include "Layers.h"
+#include "ShaderDefinitionsD3D11.h"
+
+namespace mozilla {
+namespace layers {
+
+using namespace gfx;
+
+static const size_t kInitialMaximumTriangles = 64;
+
+DeviceAttachmentsD3D11::DeviceAttachmentsD3D11(ID3D11Device* device)
+ : mMaximumTriangles(kInitialMaximumTriangles),
+ mDevice(device),
+ mContinueInit(true),
+ mInitialized(false),
+ mDeviceReset(false) {}
+
+DeviceAttachmentsD3D11::~DeviceAttachmentsD3D11() {}
+
+/* static */
+RefPtr<DeviceAttachmentsD3D11> DeviceAttachmentsD3D11::Create(
+ ID3D11Device* aDevice) {
+ // We don't return null even if the attachments object even if it fails to
+ // initialize, so the compositor can grab the failure ID.
+ RefPtr<DeviceAttachmentsD3D11> attachments =
+ new DeviceAttachmentsD3D11(aDevice);
+ attachments->Initialize();
+ return attachments.forget();
+}
+
+bool DeviceAttachmentsD3D11::Initialize() {
+ D3D11_INPUT_ELEMENT_DESC layout[] = {
+ {"POSITION", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 0,
+ D3D11_INPUT_PER_VERTEX_DATA, 0},
+ };
+
+ HRESULT hr;
+ hr = mDevice->CreateInputLayout(
+ layout, sizeof(layout) / sizeof(D3D11_INPUT_ELEMENT_DESC), LayerQuadVS,
+ sizeof(LayerQuadVS), getter_AddRefs(mInputLayout));
+
+ if (Failed(hr, "CreateInputLayout")) {
+ mInitFailureId = "FEATURE_FAILURE_D3D11_INPUT_LAYOUT";
+ 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(mVertexBuffer));
+ if (Failed(hr, "create vertex buffer")) {
+ mInitFailureId = "FEATURE_FAILURE_D3D11_VERTEX_BUFFER";
+ return false;
+ }
+
+ // Create a second input layout for layers with dynamic geometry.
+ D3D11_INPUT_ELEMENT_DESC dynamicLayout[] = {
+ {"POSITION", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 0,
+ D3D11_INPUT_PER_VERTEX_DATA, 0},
+ {"TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 8,
+ D3D11_INPUT_PER_VERTEX_DATA, 0},
+ };
+
+ hr = mDevice->CreateInputLayout(
+ dynamicLayout, sizeof(dynamicLayout) / sizeof(D3D11_INPUT_ELEMENT_DESC),
+ LayerDynamicVS, sizeof(LayerDynamicVS),
+ getter_AddRefs(mDynamicInputLayout));
+
+ if (Failed(hr, "CreateInputLayout")) {
+ mInitFailureId = "FEATURE_FAILURE_D3D11_INPUT_LAYOUT";
+ return false;
+ }
+
+ // Allocate memory for the dynamic vertex buffer.
+ bufferDesc = CD3D11_BUFFER_DESC(
+ sizeof(TexturedVertex) * mMaximumTriangles * 3, D3D11_BIND_VERTEX_BUFFER,
+ D3D11_USAGE_DYNAMIC, D3D11_CPU_ACCESS_WRITE);
+
+ hr = mDevice->CreateBuffer(&bufferDesc, nullptr,
+ getter_AddRefs(mDynamicVertexBuffer));
+ if (Failed(hr, "create dynamic vertex buffer")) {
+ mInitFailureId = "FEATURE_FAILURE_D3D11_VERTEX_BUFFER";
+ return false;
+ }
+
+ if (!CreateShaders()) {
+ mInitFailureId = "FEATURE_FAILURE_D3D11_CREATE_SHADERS";
+ 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(mVSConstantBuffer));
+ if (Failed(hr, "create vs buffer")) {
+ mInitFailureId = "FEATURE_FAILURE_D3D11_VS_BUFFER";
+ return false;
+ }
+
+ cBufferDesc.ByteWidth = sizeof(PixelShaderConstants);
+ hr = mDevice->CreateBuffer(&cBufferDesc, nullptr,
+ getter_AddRefs(mPSConstantBuffer));
+ if (Failed(hr, "create ps buffer")) {
+ mInitFailureId = "FEATURE_FAILURE_D3D11_PS_BUFFER";
+ return false;
+ }
+
+ CD3D11_RASTERIZER_DESC rastDesc(D3D11_DEFAULT);
+ rastDesc.CullMode = D3D11_CULL_NONE;
+ rastDesc.ScissorEnable = TRUE;
+
+ hr = mDevice->CreateRasterizerState(&rastDesc,
+ getter_AddRefs(mRasterizerState));
+ if (Failed(hr, "create rasterizer")) {
+ mInitFailureId = "FEATURE_FAILURE_D3D11_RASTERIZER";
+ return false;
+ }
+
+ CD3D11_SAMPLER_DESC samplerDesc(D3D11_DEFAULT);
+ hr = mDevice->CreateSamplerState(&samplerDesc,
+ getter_AddRefs(mLinearSamplerState));
+ if (Failed(hr, "create linear sampler")) {
+ mInitFailureId = "FEATURE_FAILURE_D3D11_LINEAR_SAMPLER";
+ return false;
+ }
+
+ samplerDesc.Filter = D3D11_FILTER_MIN_MAG_MIP_POINT;
+ hr = mDevice->CreateSamplerState(&samplerDesc,
+ getter_AddRefs(mPointSamplerState));
+ if (Failed(hr, "create point sampler")) {
+ mInitFailureId = "FEATURE_FAILURE_D3D11_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(mPremulBlendState));
+ if (Failed(hr, "create pm blender")) {
+ mInitFailureId = "FEATURE_FAILURE_D3D11_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(mNonPremulBlendState));
+ if (Failed(hr, "create npm blender")) {
+ mInitFailureId = "FEATURE_FAILURE_D3D11_NPM_BLENDER";
+ return false;
+ }
+
+ if (LayerManager::LayersComponentAlphaEnabled()) {
+ 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(mComponentBlendState));
+ if (Failed(hr, "create component blender")) {
+ mInitFailureId = "FEATURE_FAILURE_D3D11_COMP_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(mDisabledBlendState));
+ if (Failed(hr, "create null blender")) {
+ mInitFailureId = "FEATURE_FAILURE_D3D11_NULL_BLENDER";
+ return false;
+ }
+
+ if (!InitSyncObject()) {
+ mInitFailureId = "FEATURE_FAILURE_D3D11_OBJ_SYNC";
+ return false;
+ }
+
+ mInitialized = true;
+ return true;
+}
+
+bool DeviceAttachmentsD3D11::InitSyncObject() {
+ // Sync object is not supported on WARP.
+ if (DeviceManagerDx::Get()->IsWARP()) {
+ return true;
+ }
+
+ MOZ_ASSERT(!mSyncObject);
+ MOZ_ASSERT(mDevice);
+
+ mSyncObject = SyncObjectHost::CreateSyncObjectHost(mDevice);
+ MOZ_ASSERT(mSyncObject);
+
+ return mSyncObject->Init();
+}
+
+bool DeviceAttachmentsD3D11::InitBlendShaders() {
+ if (!mVSQuadBlendShader[MaskType::MaskNone]) {
+ InitVertexShader(sLayerQuadBlendVS, mVSQuadBlendShader, MaskType::MaskNone);
+ InitVertexShader(sLayerQuadBlendMaskVS, mVSQuadBlendShader, MaskType::Mask);
+ }
+
+ if (!mVSDynamicBlendShader[MaskType::MaskNone]) {
+ InitVertexShader(sLayerDynamicBlendVS, mVSDynamicBlendShader,
+ MaskType::MaskNone);
+ InitVertexShader(sLayerDynamicBlendMaskVS, mVSDynamicBlendShader,
+ MaskType::Mask);
+ }
+
+ if (!mBlendShader[MaskType::MaskNone]) {
+ InitPixelShader(sBlendShader, mBlendShader, MaskType::MaskNone);
+ }
+ return mContinueInit;
+}
+
+bool DeviceAttachmentsD3D11::CreateShaders() {
+ InitVertexShader(sLayerQuadVS, mVSQuadShader, MaskType::MaskNone);
+ InitVertexShader(sLayerQuadMaskVS, mVSQuadShader, MaskType::Mask);
+
+ InitVertexShader(sLayerDynamicVS, mVSDynamicShader, MaskType::MaskNone);
+ InitVertexShader(sLayerDynamicMaskVS, mVSDynamicShader, 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);
+ InitPixelShader(sNV12Shader, mNV12Shader, MaskType::MaskNone);
+ InitPixelShader(sNV12ShaderMask, mNV12Shader, MaskType::Mask);
+ if (LayerManager::LayersComponentAlphaEnabled()) {
+ InitPixelShader(sComponentAlphaShader, mComponentAlphaShader,
+ MaskType::MaskNone);
+ InitPixelShader(sComponentAlphaShaderMask, mComponentAlphaShader,
+ MaskType::Mask);
+ }
+
+ return mContinueInit;
+}
+
+void DeviceAttachmentsD3D11::InitVertexShader(const ShaderBytes& aShader,
+ ID3D11VertexShader** aOut) {
+ if (!mContinueInit) {
+ return;
+ }
+ if (Failed(mDevice->CreateVertexShader(aShader.mData, aShader.mLength,
+ nullptr, aOut),
+ "create vs")) {
+ mContinueInit = false;
+ }
+}
+
+void DeviceAttachmentsD3D11::InitPixelShader(const ShaderBytes& aShader,
+ ID3D11PixelShader** aOut) {
+ if (!mContinueInit) {
+ return;
+ }
+ if (Failed(mDevice->CreatePixelShader(aShader.mData, aShader.mLength, nullptr,
+ aOut),
+ "create ps")) {
+ mContinueInit = false;
+ }
+}
+
+bool DeviceAttachmentsD3D11::Failed(HRESULT hr, const char* aContext) {
+ if (SUCCEEDED(hr)) {
+ return false;
+ }
+
+ gfxCriticalNote << "[D3D11] " << aContext << " failed: " << hexa(hr);
+ return true;
+}
+
+bool DeviceAttachmentsD3D11::EnsureTriangleBuffer(size_t aNumTriangles) {
+ if (aNumTriangles > mMaximumTriangles) {
+ CD3D11_BUFFER_DESC bufferDesc(sizeof(TexturedVertex) * aNumTriangles * 3,
+ D3D11_BIND_VERTEX_BUFFER, D3D11_USAGE_DYNAMIC,
+ D3D11_CPU_ACCESS_WRITE);
+
+ HRESULT hr = mDevice->CreateBuffer(&bufferDesc, nullptr,
+ getter_AddRefs(mDynamicVertexBuffer));
+
+ if (Failed(hr, "resize dynamic vertex buffer")) {
+ return false;
+ }
+
+ mMaximumTriangles = aNumTriangles;
+ }
+
+ MOZ_ASSERT(mMaximumTriangles >= aNumTriangles);
+ return true;
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/d3d11/DeviceAttachmentsD3D11.h b/gfx/layers/d3d11/DeviceAttachmentsD3D11.h
new file mode 100644
index 0000000000..2d655ceb2f
--- /dev/null
+++ b/gfx/layers/d3d11/DeviceAttachmentsD3D11.h
@@ -0,0 +1,116 @@
+/* -*- 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_layers_d3d11_DeviceAttachmentsD3D11_h
+#define mozilla_gfx_layers_d3d11_DeviceAttachmentsD3D11_h
+
+#include "mozilla/EnumeratedArray.h"
+#include "mozilla/RefPtr.h"
+#include "mozilla/gfx/DeviceManagerDx.h"
+#include "mozilla/layers/CompositorTypes.h"
+#include "mozilla/layers/SyncObject.h"
+#include <d3d11.h>
+#include <dxgi1_2.h>
+
+namespace mozilla {
+namespace layers {
+
+struct ShaderBytes;
+
+class DeviceAttachmentsD3D11 final {
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(DeviceAttachmentsD3D11);
+
+ public:
+ static RefPtr<DeviceAttachmentsD3D11> Create(ID3D11Device* aDevice);
+
+ bool InitBlendShaders();
+ bool EnsureTriangleBuffer(size_t aNumTriangles);
+
+ bool IsValid() const { return mInitialized; }
+ const nsCString& GetFailureId() const {
+ MOZ_ASSERT(!IsValid());
+ return mInitFailureId;
+ }
+
+ typedef EnumeratedArray<MaskType, MaskType::NumMaskTypes,
+ RefPtr<ID3D11VertexShader>>
+ VertexShaderArray;
+ typedef EnumeratedArray<MaskType, MaskType::NumMaskTypes,
+ RefPtr<ID3D11PixelShader>>
+ PixelShaderArray;
+
+ RefPtr<ID3D11InputLayout> mInputLayout;
+ RefPtr<ID3D11InputLayout> mDynamicInputLayout;
+
+ RefPtr<ID3D11Buffer> mVertexBuffer;
+ RefPtr<ID3D11Buffer> mDynamicVertexBuffer;
+
+ VertexShaderArray mVSQuadShader;
+ VertexShaderArray mVSQuadBlendShader;
+
+ VertexShaderArray mVSDynamicShader;
+ VertexShaderArray mVSDynamicBlendShader;
+
+ PixelShaderArray mSolidColorShader;
+ PixelShaderArray mRGBAShader;
+ PixelShaderArray mRGBShader;
+ PixelShaderArray mYCbCrShader;
+ PixelShaderArray mNV12Shader;
+ 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<SyncObjectHost> mSyncObject;
+
+ void SetDeviceReset() { mDeviceReset = true; }
+ bool IsDeviceReset() const { return mDeviceReset; }
+
+ private:
+ explicit DeviceAttachmentsD3D11(ID3D11Device* device);
+ ~DeviceAttachmentsD3D11();
+
+ bool Initialize();
+ bool CreateShaders();
+ bool InitSyncObject();
+
+ 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);
+ void InitPixelShader(const ShaderBytes& aShader, ID3D11PixelShader** aOut);
+
+ bool Failed(HRESULT hr, const char* aContext);
+
+ private:
+ size_t mMaximumTriangles;
+
+ // Only used during initialization.
+ RefPtr<ID3D11Device> mDevice;
+ bool mContinueInit;
+ bool mInitialized;
+ bool mDeviceReset;
+ nsCString mInitFailureId;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // mozilla_gfx_layers_d3d11_DeviceAttachmentsD3D11_h
diff --git a/gfx/layers/d3d11/DiagnosticsD3D11.cpp b/gfx/layers/d3d11/DiagnosticsD3D11.cpp
new file mode 100644
index 0000000000..6d37280036
--- /dev/null
+++ b/gfx/layers/d3d11/DiagnosticsD3D11.cpp
@@ -0,0 +1,83 @@
+/* -*- 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/. */
+
+#include "DiagnosticsD3D11.h"
+#include "mozilla/layers/Diagnostics.h"
+#include "mozilla/layers/HelpersD3D11.h"
+
+namespace mozilla {
+namespace layers {
+
+DiagnosticsD3D11::DiagnosticsD3D11(ID3D11Device* aDevice,
+ ID3D11DeviceContext* aContext)
+ : mDevice(aDevice), mContext(aContext) {}
+
+void DiagnosticsD3D11::Start(uint32_t aPixelsPerFrame) {
+ mPrevFrame = mCurrentFrame;
+ mCurrentFrame = FrameQueries();
+
+ CD3D11_QUERY_DESC desc(D3D11_QUERY_PIPELINE_STATISTICS);
+ mDevice->CreateQuery(&desc, getter_AddRefs(mCurrentFrame.stats));
+ if (mCurrentFrame.stats) {
+ mContext->Begin(mCurrentFrame.stats);
+ }
+ mCurrentFrame.pixelsPerFrame = aPixelsPerFrame;
+
+ desc = CD3D11_QUERY_DESC(D3D11_QUERY_TIMESTAMP_DISJOINT);
+ mDevice->CreateQuery(&desc, getter_AddRefs(mCurrentFrame.timing));
+ if (mCurrentFrame.timing) {
+ mContext->Begin(mCurrentFrame.timing);
+ }
+
+ desc = CD3D11_QUERY_DESC(D3D11_QUERY_TIMESTAMP);
+ mDevice->CreateQuery(&desc, getter_AddRefs(mCurrentFrame.frameBegin));
+ if (mCurrentFrame.frameBegin) {
+ mContext->End(mCurrentFrame.frameBegin);
+ }
+}
+
+void DiagnosticsD3D11::End() {
+ if (mCurrentFrame.stats) {
+ mContext->End(mCurrentFrame.stats);
+ }
+ if (mCurrentFrame.frameBegin) {
+ CD3D11_QUERY_DESC desc(D3D11_QUERY_TIMESTAMP);
+ mDevice->CreateQuery(&desc, getter_AddRefs(mCurrentFrame.frameEnd));
+ if (mCurrentFrame.frameEnd) {
+ mContext->End(mCurrentFrame.frameEnd);
+ }
+ }
+ if (mCurrentFrame.timing) {
+ mContext->End(mCurrentFrame.timing);
+ }
+}
+
+void DiagnosticsD3D11::Cancel() { mCurrentFrame = FrameQueries(); }
+
+void DiagnosticsD3D11::Query(GPUStats* aStats) {
+ // Collect pixel shader stats.
+ if (mPrevFrame.stats) {
+ D3D11_QUERY_DATA_PIPELINE_STATISTICS stats;
+ if (WaitForGPUQuery(mDevice, mContext, mPrevFrame.stats, &stats)) {
+ aStats->mInvalidPixels = mPrevFrame.pixelsPerFrame;
+ aStats->mPixelsFilled = uint32_t(stats.PSInvocations);
+ }
+ }
+ if (mPrevFrame.timing) {
+ UINT64 begin, end;
+ D3D11_QUERY_DATA_TIMESTAMP_DISJOINT timing;
+ if (WaitForGPUQuery(mDevice, mContext, mPrevFrame.timing, &timing) &&
+ !timing.Disjoint &&
+ WaitForGPUQuery(mDevice, mContext, mPrevFrame.frameBegin, &begin) &&
+ WaitForGPUQuery(mDevice, mContext, mPrevFrame.frameEnd, &end)) {
+ float timeMs = float(end - begin) / float(timing.Frequency) * 1000.0f;
+ aStats->mDrawTime = Some(timeMs);
+ }
+ }
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/d3d11/DiagnosticsD3D11.h b/gfx/layers/d3d11/DiagnosticsD3D11.h
new file mode 100644
index 0000000000..c4e2226a62
--- /dev/null
+++ b/gfx/layers/d3d11/DiagnosticsD3D11.h
@@ -0,0 +1,51 @@
+/* -*- 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_layers_d3d11_DiagnosticsD3D11_h
+#define mozilla_gfx_layers_d3d11_DiagnosticsD3D11_h
+
+#include <stdint.h>
+#include "mozilla/RefPtr.h"
+#include <d3d11.h>
+
+namespace mozilla {
+namespace layers {
+
+struct GPUStats;
+
+class DiagnosticsD3D11 {
+ public:
+ DiagnosticsD3D11(ID3D11Device* aDevice, ID3D11DeviceContext* aContext);
+
+ void Start(uint32_t aPixelsPerFrame);
+ void End();
+ void Cancel();
+
+ void Query(GPUStats* aStats);
+
+ private:
+ RefPtr<ID3D11Device> mDevice;
+ RefPtr<ID3D11DeviceContext> mContext;
+
+ // When using the diagnostic overlay, we double-buffer some queries for
+ // frame statistics.
+ struct FrameQueries {
+ FrameQueries() : pixelsPerFrame(0) {}
+
+ RefPtr<ID3D11Query> stats;
+ RefPtr<ID3D11Query> timing;
+ RefPtr<ID3D11Query> frameBegin;
+ RefPtr<ID3D11Query> frameEnd;
+ uint32_t pixelsPerFrame;
+ };
+ FrameQueries mPrevFrame;
+ FrameQueries mCurrentFrame;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // mozilla_gfx_layers_d3d11_DiagnosticsD3D11_h
diff --git a/gfx/layers/d3d11/HelpersD3D11.h b/gfx/layers/d3d11/HelpersD3D11.h
new file mode 100644
index 0000000000..337212aadc
--- /dev/null
+++ b/gfx/layers/d3d11/HelpersD3D11.h
@@ -0,0 +1,56 @@
+/* -*- 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_layers_d3d11_HelpersD3D11_h
+#define mozilla_gfx_layers_d3d11_HelpersD3D11_h
+
+#include <d3d11.h>
+#include "mozilla/Telemetry.h"
+#include "mozilla/TimeStamp.h"
+
+namespace mozilla {
+namespace layers {
+
+template <typename T>
+static inline bool WaitForGPUQuery(ID3D11Device* aDevice,
+ ID3D11DeviceContext* aContext,
+ ID3D11Query* aQuery, T* aOut) {
+ TimeStamp start = TimeStamp::Now();
+ while (aContext->GetData(aQuery, aOut, sizeof(*aOut), 0) != S_OK) {
+ if (aDevice->GetDeviceRemovedReason() != S_OK) {
+ return false;
+ }
+ if (TimeStamp::Now() - start > TimeDuration::FromSeconds(2)) {
+ return false;
+ }
+ Sleep(0);
+ }
+ return true;
+}
+
+static inline bool WaitForFrameGPUQuery(ID3D11Device* aDevice,
+ ID3D11DeviceContext* aContext,
+ ID3D11Query* aQuery, BOOL* aOut) {
+ TimeStamp start = TimeStamp::Now();
+ bool success = true;
+ while (aContext->GetData(aQuery, aOut, sizeof(*aOut), 0) != S_OK) {
+ if (aDevice->GetDeviceRemovedReason() != S_OK) {
+ return false;
+ }
+ if (TimeStamp::Now() - start > TimeDuration::FromSeconds(2)) {
+ success = false;
+ break;
+ }
+ Sleep(0);
+ }
+ Telemetry::AccumulateTimeDelta(Telemetry::GPU_WAIT_TIME_MS, start);
+ return success;
+}
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // mozilla_gfx_layers_d3d11_HelpersD3D11_h
diff --git a/gfx/layers/d3d11/MLGDeviceD3D11.cpp b/gfx/layers/d3d11/MLGDeviceD3D11.cpp
new file mode 100644
index 0000000000..219662ead1
--- /dev/null
+++ b/gfx/layers/d3d11/MLGDeviceD3D11.cpp
@@ -0,0 +1,2034 @@
+/* -*- 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/. */
+
+#include "MLGDeviceD3D11.h"
+#include "mozilla/ArrayUtils.h"
+#include "mozilla/Telemetry.h"
+#include "mozilla/WindowsVersion.h"
+#include "mozilla/gfx/GPUParent.h"
+#include "mozilla/gfx/StackArray.h"
+#include "mozilla/layers/DiagnosticsD3D11.h"
+#include "mozilla/layers/HelpersD3D11.h"
+#include "mozilla/layers/LayerMLGPU.h"
+#include "mozilla/layers/MemoryReportingMLGPU.h"
+#include "mozilla/layers/ShaderDefinitionsMLGPU.h"
+#include "mozilla/layers/UtilityMLGPU.h"
+#include "mozilla/widget/CompositorWidget.h"
+#include "mozilla/widget/WinCompositorWidget.h"
+#include "MLGShaders.h"
+#include "TextureD3D11.h"
+#include "gfxConfig.h"
+#include "mozilla/StaticPrefs_layers.h"
+#include "FxROutputHandler.h"
+
+namespace mozilla {
+namespace layers {
+
+using namespace mozilla::gfx;
+using namespace mozilla::widget;
+using namespace mozilla::layers::mlg;
+
+// Defined in CompositorD3D11.cpp.
+bool CanUsePartialPresents(ID3D11Device* aDevice);
+
+static D3D11_BOX RectToBox(const gfx::IntRect& aRect);
+
+MLGRenderTargetD3D11::MLGRenderTargetD3D11(const gfx::IntSize& aSize,
+ MLGRenderTargetFlags aFlags)
+ : MLGRenderTarget(aFlags), mSize(aSize) {}
+
+MLGRenderTargetD3D11::~MLGRenderTargetD3D11() {
+ if (mDepthBuffer) {
+ sRenderTargetUsage -= mSize.width * mSize.height * 1;
+ }
+ ForgetTexture();
+}
+
+bool MLGRenderTargetD3D11::Initialize(ID3D11Device* aDevice) {
+ D3D11_TEXTURE2D_DESC desc;
+ ::ZeroMemory(&desc, sizeof(desc));
+ desc.Width = mSize.width;
+ desc.Height = mSize.height;
+ desc.MipLevels = 1;
+ desc.ArraySize = 1;
+ desc.SampleDesc.Count = 1;
+ desc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
+ desc.Usage = D3D11_USAGE_DEFAULT;
+ desc.BindFlags = D3D11_BIND_RENDER_TARGET | D3D11_BIND_SHADER_RESOURCE;
+
+ RefPtr<ID3D11Texture2D> texture;
+ HRESULT hr =
+ aDevice->CreateTexture2D(&desc, nullptr, getter_AddRefs(texture));
+ if (FAILED(hr) || !texture) {
+ gfxCriticalNote << "Failed to create render target texture: " << hexa(hr);
+ return false;
+ }
+
+ return Initialize(aDevice, texture);
+}
+
+bool MLGRenderTargetD3D11::Initialize(ID3D11Device* aDevice,
+ ID3D11Texture2D* aTexture) {
+ if (!UpdateTexture(aTexture)) {
+ return false;
+ }
+ if ((mFlags & MLGRenderTargetFlags::ZBuffer) && !CreateDepthBuffer(aDevice)) {
+ return false;
+ }
+ return true;
+}
+
+bool MLGRenderTargetD3D11::UpdateTexture(ID3D11Texture2D* aTexture) {
+ // Save the view first, in case we can re-use it.
+ RefPtr<ID3D11RenderTargetView> view = mRTView.forget();
+
+ ForgetTexture();
+
+ if (!aTexture) {
+ return true;
+ }
+
+#ifdef DEBUG
+ D3D11_TEXTURE2D_DESC desc;
+ aTexture->GetDesc(&desc);
+ MOZ_ASSERT(desc.Width == mSize.width && desc.Height == mSize.height);
+#endif
+
+ RefPtr<ID3D11Device> device;
+ aTexture->GetDevice(getter_AddRefs(device));
+
+ if (view) {
+ // Check that the view matches the backing texture.
+ RefPtr<ID3D11Resource> resource;
+ view->GetResource(getter_AddRefs(resource));
+ if (resource != aTexture) {
+ view = nullptr;
+ }
+ }
+
+ // If we couldn't re-use a view from before, make one now.
+ if (!view) {
+ HRESULT hr =
+ device->CreateRenderTargetView(aTexture, nullptr, getter_AddRefs(view));
+ if (FAILED(hr) || !view) {
+ gfxCriticalNote << "Failed to create render target view: " << hexa(hr);
+ return false;
+ }
+ }
+
+ mTexture = aTexture;
+ mRTView = view.forget();
+ sRenderTargetUsage += mSize.width * mSize.height * 4;
+ return true;
+}
+
+void MLGRenderTargetD3D11::ForgetTexture() {
+ if (mTexture) {
+ sRenderTargetUsage -= mSize.width * mSize.height * 4;
+ mTexture = nullptr;
+ }
+ mRTView = nullptr;
+ mTextureSource = nullptr;
+}
+
+bool MLGRenderTargetD3D11::CreateDepthBuffer(ID3D11Device* aDevice) {
+ D3D11_TEXTURE2D_DESC desc;
+ ::ZeroMemory(&desc, sizeof(desc));
+ desc.Width = mSize.width;
+ desc.Height = mSize.height;
+ desc.MipLevels = 1;
+ desc.ArraySize = 1;
+ desc.Format = DXGI_FORMAT_D32_FLOAT;
+ desc.SampleDesc.Count = 1;
+ desc.SampleDesc.Quality = 0;
+ desc.Usage = D3D11_USAGE_DEFAULT;
+ desc.BindFlags = D3D11_BIND_DEPTH_STENCIL;
+
+ RefPtr<ID3D11Texture2D> buffer;
+ HRESULT hr = aDevice->CreateTexture2D(&desc, nullptr, getter_AddRefs(buffer));
+ if (FAILED(hr) || !buffer) {
+ gfxCriticalNote << "Could not create depth-stencil buffer: " << hexa(hr);
+ return false;
+ }
+
+ D3D11_DEPTH_STENCIL_VIEW_DESC viewDesc;
+ ::ZeroMemory(&viewDesc, sizeof(viewDesc));
+ viewDesc.Format = DXGI_FORMAT_D32_FLOAT;
+ viewDesc.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2D;
+
+ RefPtr<ID3D11DepthStencilView> dsv;
+ hr = aDevice->CreateDepthStencilView(buffer, &viewDesc, getter_AddRefs(dsv));
+ if (FAILED(hr) || !dsv) {
+ gfxCriticalNote << "Could not create depth-stencil view: " << hexa(hr);
+ return false;
+ }
+
+ mDepthBuffer = buffer;
+ mDepthStencilView = dsv;
+ sRenderTargetUsage += mSize.width * mSize.height * 1;
+ return true;
+}
+
+ID3D11DepthStencilView* MLGRenderTargetD3D11::GetDSV() {
+ return mDepthStencilView;
+}
+
+ID3D11RenderTargetView* MLGRenderTargetD3D11::GetRenderTargetView() {
+ return mRTView;
+}
+
+IntSize MLGRenderTargetD3D11::GetSize() const { return mSize; }
+
+MLGTexture* MLGRenderTargetD3D11::GetTexture() {
+ if (!mTextureSource) {
+ mTextureSource = new MLGTextureD3D11(mTexture);
+ }
+ return mTextureSource;
+}
+
+MLGSwapChainD3D11::MLGSwapChainD3D11(MLGDeviceD3D11* aParent,
+ ID3D11Device* aDevice)
+ : mParent(aParent),
+ mDevice(aDevice),
+ mWidget(nullptr),
+ mCanUsePartialPresents(CanUsePartialPresents(aDevice)) {}
+
+MLGSwapChainD3D11::~MLGSwapChainD3D11() {}
+
+void MLGSwapChainD3D11::Destroy() {
+ if (mRT == mParent->GetRenderTarget()) {
+ mParent->SetRenderTarget(nullptr);
+ }
+ mWidget = nullptr;
+ mRT = nullptr;
+ mSwapChain = nullptr;
+ mSwapChain1 = nullptr;
+}
+
+RefPtr<MLGSwapChainD3D11> MLGSwapChainD3D11::Create(MLGDeviceD3D11* aParent,
+ ID3D11Device* aDevice,
+ CompositorWidget* aWidget) {
+ RefPtr<MLGSwapChainD3D11> swapChain = new MLGSwapChainD3D11(aParent, aDevice);
+ if (!swapChain->Initialize(aWidget)) {
+ return nullptr;
+ }
+ return swapChain.forget();
+}
+
+bool MLGSwapChainD3D11::Initialize(CompositorWidget* aWidget) {
+ HWND hwnd = aWidget->AsWindows()->GetHwnd();
+
+ RefPtr<IDXGIDevice> dxgiDevice;
+ mDevice->QueryInterface(dxgiDevice.StartAssignment());
+
+ RefPtr<IDXGIFactory> dxgiFactory;
+ {
+ RefPtr<IDXGIAdapter> adapter;
+ dxgiDevice->GetAdapter(getter_AddRefs(adapter));
+
+ adapter->GetParent(IID_PPV_ARGS(dxgiFactory.StartAssignment()));
+ }
+
+ RefPtr<IDXGIFactory2> dxgiFactory2;
+ if (gfxVars::UseDoubleBufferingWithCompositor() &&
+ SUCCEEDED(dxgiFactory->QueryInterface(dxgiFactory2.StartAssignment())) &&
+ dxgiFactory2 && XRE_IsGPUProcess()) {
+ // DXGI_SCALING_NONE is not available on Windows 7 with the Platform Update:
+ // This looks awful for things like the awesome bar and browser window
+ // resizing, so we don't use a flip buffer chain here. (Note when using
+ // EFFECT_SEQUENTIAL Windows doesn't stretch the surface when resizing).
+ //
+ // We choose not to run this on platforms earlier than Windows 10 because
+ // it appears sometimes this breaks our ability to test ASAP compositing,
+ // which breaks Talos.
+ //
+ // When the GPU process is disabled we don't have a compositor window which
+ // can lead to issues with Window re-use so we don't use this.
+ DXGI_SWAP_CHAIN_DESC1 desc;
+ ::ZeroMemory(&desc, sizeof(desc));
+ desc.Width = 0;
+ desc.Height = 0;
+ desc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
+ desc.SampleDesc.Count = 1;
+ desc.SampleDesc.Quality = 0;
+ desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
+ desc.BufferCount = 2;
+ desc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL;
+ desc.Scaling = DXGI_SCALING_NONE;
+ desc.Flags = 0;
+
+ HRESULT hr = dxgiFactory2->CreateSwapChainForHwnd(
+ mDevice, hwnd, &desc, nullptr, nullptr, getter_AddRefs(mSwapChain1));
+ if (SUCCEEDED(hr) && mSwapChain1) {
+ DXGI_RGBA color = {1.0f, 1.0f, 1.0f, 1.0f};
+ mSwapChain1->SetBackgroundColor(&color);
+ mSwapChain = mSwapChain1;
+ mIsDoubleBuffered = true;
+ } else if (aWidget->AsWindows()->GetCompositorHwnd()) {
+ // Destroy compositor window.
+ aWidget->AsWindows()->DestroyCompositorWindow();
+ hwnd = aWidget->AsWindows()->GetHwnd();
+ }
+ }
+
+ if (!mSwapChain) {
+ 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 = hwnd;
+ swapDesc.Windowed = TRUE;
+ swapDesc.Flags = 0;
+ swapDesc.SwapEffect = DXGI_SWAP_EFFECT_SEQUENTIAL;
+
+ HRESULT hr = dxgiFactory->CreateSwapChain(dxgiDevice, &swapDesc,
+ getter_AddRefs(mSwapChain));
+ if (FAILED(hr)) {
+ gfxCriticalNote << "Could not create swap chain: " << hexa(hr);
+ return false;
+ }
+
+ // Try to get an IDXGISwapChain1 if we can, for partial presents.
+ mSwapChain->QueryInterface(mSwapChain1.StartAssignment());
+ }
+
+ // We need this because we don't want DXGI to respond to Alt+Enter.
+ dxgiFactory->MakeWindowAssociation(hwnd, DXGI_MWA_NO_WINDOW_CHANGES);
+ mWidget = aWidget;
+ return true;
+}
+
+RefPtr<MLGRenderTarget> MLGSwapChainD3D11::AcquireBackBuffer() {
+ RefPtr<ID3D11Texture2D> texture;
+ HRESULT hr = mSwapChain->GetBuffer(0, __uuidof(ID3D11Texture2D),
+ getter_AddRefs(texture));
+ if (hr == DXGI_ERROR_INVALID_CALL &&
+ mDevice->GetDeviceRemovedReason() != S_OK) {
+ // This can happen on some drivers when there's a TDR.
+ mParent->HandleDeviceReset("SwapChain::GetBuffer");
+ return nullptr;
+ }
+ if (FAILED(hr)) {
+ gfxCriticalNote << "Failed to acquire swap chain's backbuffer: "
+ << hexa(hr);
+ return nullptr;
+ }
+
+ if (!mRT) {
+ MLGRenderTargetFlags flags = MLGRenderTargetFlags::Default;
+ if (StaticPrefs::layers_mlgpu_enable_depth_buffer_AtStartup()) {
+ flags |= MLGRenderTargetFlags::ZBuffer;
+ }
+
+ mRT = new MLGRenderTargetD3D11(mSize, flags);
+ if (!mRT->Initialize(mDevice, nullptr)) {
+ return nullptr;
+ }
+ }
+
+ if (!mRT->UpdateTexture(texture)) {
+ return nullptr;
+ }
+
+ if (mIsDoubleBuffered) {
+ UpdateBackBufferContents(texture);
+ }
+ return mRT;
+}
+
+void MLGSwapChainD3D11::UpdateBackBufferContents(ID3D11Texture2D* aBack) {
+ MOZ_ASSERT(mIsDoubleBuffered);
+
+ // The front region contains the newly invalid region for this frame. The
+ // back region contains that, plus the region that was only drawn into the
+ // back buffer on the previous frame. Thus by subtracting the two, we can
+ // find the region that needs to be copied from the front buffer to the
+ // back. We do this so we don't have to re-render those pixels.
+ nsIntRegion frontValid;
+ frontValid.Sub(mBackBufferInvalid, mFrontBufferInvalid);
+ if (frontValid.IsEmpty()) {
+ return;
+ }
+
+ RefPtr<ID3D11Texture2D> front;
+ HRESULT hr = mSwapChain->GetBuffer(1, __uuidof(ID3D11Texture2D),
+ getter_AddRefs(front));
+ if (FAILED(hr) || !front) {
+ return;
+ }
+
+ RefPtr<ID3D11DeviceContext> context;
+ mDevice->GetImmediateContext(getter_AddRefs(context));
+
+ for (auto iter = frontValid.RectIter(); !iter.Done(); iter.Next()) {
+ const IntRect& rect = iter.Get();
+ D3D11_BOX box = RectToBox(rect);
+ context->CopySubresourceRegion(aBack, 0, rect.X(), rect.Y(), 0, front, 0,
+ &box);
+ }
+
+ // The back and front buffers are now in sync.
+ mBackBufferInvalid = mFrontBufferInvalid;
+ MOZ_ASSERT(!mBackBufferInvalid.IsEmpty());
+}
+
+bool MLGSwapChainD3D11::ResizeBuffers(const IntSize& aSize) {
+ // We have to clear all references to the old backbuffer before resizing.
+ mRT = nullptr;
+
+ // Clear the size before re-allocating. If allocation fails we want to try
+ // again, because we had to sacrifice our original backbuffer to try
+ // resizing.
+ mSize = IntSize(0, 0);
+
+ HRESULT hr = mSwapChain->ResizeBuffers(0, aSize.width, aSize.height,
+ DXGI_FORMAT_B8G8R8A8_UNORM, 0);
+ if (hr == DXGI_ERROR_DEVICE_REMOVED) {
+ mParent->HandleDeviceReset("ResizeBuffers");
+ return false;
+ }
+ if (FAILED(hr)) {
+ gfxCriticalNote << "Failed to resize swap chain buffers: " << hexa(hr);
+ return false;
+ }
+
+ mSize = aSize;
+ mBackBufferInvalid = IntRect(IntPoint(0, 0), mSize);
+ mFrontBufferInvalid = IntRect(IntPoint(0, 0), mSize);
+ return true;
+}
+
+IntSize MLGSwapChainD3D11::GetSize() const { return mSize; }
+
+void MLGSwapChainD3D11::Present() {
+ MOZ_ASSERT(!mBackBufferInvalid.IsEmpty());
+ MOZ_ASSERT(mBackBufferInvalid.GetNumRects() > 0);
+
+ // See bug 1260611 comment #28 for why we do this.
+ mParent->InsertPresentWaitQuery();
+
+ if (mWidget->AsWindows()->HasFxrOutputHandler()) {
+ // There is a Firefox Reality handler for this swapchain. Update this
+ // window's contents to the VR window.
+ FxROutputHandler* fxrHandler = mWidget->AsWindows()->GetFxrOutputHandler();
+ if (fxrHandler->TryInitialize(mSwapChain, mDevice)) {
+ RefPtr<ID3D11DeviceContext> context;
+ mDevice->GetImmediateContext(getter_AddRefs(context));
+ fxrHandler->UpdateOutput(context);
+ }
+ }
+
+ HRESULT hr;
+ if (mCanUsePartialPresents && mSwapChain1) {
+ StackArray<RECT, 4> rects(mBackBufferInvalid.GetNumRects());
+ size_t i = 0;
+ for (auto iter = mBackBufferInvalid.RectIter(); !iter.Done(); iter.Next()) {
+ const IntRect& rect = iter.Get();
+ rects[i].left = rect.X();
+ rects[i].top = rect.Y();
+ rects[i].bottom = rect.YMost();
+ rects[i].right = rect.XMost();
+ i++;
+ }
+
+ DXGI_PRESENT_PARAMETERS params;
+ PodZero(&params);
+ params.DirtyRectsCount = mBackBufferInvalid.GetNumRects();
+ params.pDirtyRects = rects.data();
+ hr = mSwapChain1->Present1(0, 0, &params);
+ } else {
+ hr = mSwapChain->Present(0, 0);
+ }
+
+ if (hr == DXGI_ERROR_DEVICE_REMOVED) {
+ mParent->HandleDeviceReset("Present");
+ }
+
+ if (FAILED(hr)) {
+ gfxCriticalNote << "D3D11 swap chain failed to present: " << hexa(hr);
+ }
+
+ if (mIsDoubleBuffered) {
+ // Both the front and back buffer invalid regions are in sync, but now the
+ // presented buffer (the front buffer) is clean, so we clear its invalid
+ // region. The back buffer that will be used next frame however is now
+ // dirty.
+ MOZ_ASSERT(mFrontBufferInvalid.GetBounds() ==
+ mBackBufferInvalid.GetBounds());
+ mFrontBufferInvalid.SetEmpty();
+ } else {
+ mBackBufferInvalid.SetEmpty();
+ }
+ mLastPresentSize = mSize;
+
+ // Note: this waits on the query we inserted in the previous frame,
+ // not the one we just inserted now. Example:
+ // Insert query #1
+ // Present #1
+ // (first frame, no wait)
+ // Insert query #2
+ // Present #2
+ // Wait for query #1.
+ // Insert query #3
+ // Present #3
+ // Wait for query #2.
+ //
+ // This ensures we're done reading textures before swapping buffers.
+ mParent->WaitForPreviousPresentQuery();
+}
+
+void MLGSwapChainD3D11::ForcePresent() {
+ DXGI_SWAP_CHAIN_DESC desc;
+ mSwapChain->GetDesc(&desc);
+
+ LayoutDeviceIntSize size = mWidget->GetClientSize();
+
+ if (desc.BufferDesc.Width != size.width ||
+ desc.BufferDesc.Height != size.height) {
+ return;
+ }
+
+ mSwapChain->Present(0, 0);
+ if (mIsDoubleBuffered) {
+ // Make sure we present the old front buffer since we know it is completely
+ // valid. This non-vsynced present should be pretty much 'free' for a flip
+ // chain.
+ mSwapChain->Present(0, 0);
+ }
+
+ mLastPresentSize = mSize;
+}
+
+void MLGSwapChainD3D11::CopyBackbuffer(gfx::DrawTarget* aTarget,
+ const gfx::IntRect& aBounds) {
+ RefPtr<ID3D11DeviceContext> context;
+ mDevice->GetImmediateContext(getter_AddRefs(context));
+
+ RefPtr<ID3D11Texture2D> backbuffer;
+ HRESULT hr = mSwapChain->GetBuffer(0, __uuidof(ID3D11Texture2D),
+ (void**)backbuffer.StartAssignment());
+ if (FAILED(hr)) {
+ gfxWarning() << "Failed to acquire swapchain backbuffer: " << hexa(hr);
+ return;
+ }
+
+ D3D11_TEXTURE2D_DESC bbDesc;
+ backbuffer->GetDesc(&bbDesc);
+
+ CD3D11_TEXTURE2D_DESC tempDesc(bbDesc.Format, bbDesc.Width, bbDesc.Height);
+ tempDesc.MipLevels = 1;
+ tempDesc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
+ tempDesc.Usage = D3D11_USAGE_STAGING;
+ tempDesc.BindFlags = 0;
+
+ RefPtr<ID3D11Texture2D> temp;
+ hr = mDevice->CreateTexture2D(&tempDesc, nullptr, getter_AddRefs(temp));
+ if (FAILED(hr)) {
+ gfxWarning() << "Failed to create a temporary texture for PresentAndCopy: "
+ << hexa(hr);
+ return;
+ }
+
+ context->CopyResource(temp, backbuffer);
+
+ D3D11_MAPPED_SUBRESOURCE map;
+ hr = context->Map(temp, 0, D3D11_MAP_READ, 0, &map);
+ if (FAILED(hr)) {
+ gfxWarning() << "Failed to map temporary texture for PresentAndCopy: "
+ << hexa(hr);
+ return;
+ }
+
+ RefPtr<DataSourceSurface> source = Factory::CreateWrappingDataSourceSurface(
+ (uint8_t*)map.pData, map.RowPitch, IntSize(bbDesc.Width, bbDesc.Height),
+ SurfaceFormat::B8G8R8A8);
+
+ aTarget->CopySurface(source, IntRect(0, 0, bbDesc.Width, bbDesc.Height),
+ IntPoint(-aBounds.X(), -aBounds.Y()));
+ aTarget->Flush();
+
+ context->Unmap(temp, 0);
+}
+
+RefPtr<MLGBufferD3D11> MLGBufferD3D11::Create(ID3D11Device* aDevice,
+ MLGBufferType aType,
+ uint32_t aSize, MLGUsage aUsage,
+ const void* aInitialData) {
+ D3D11_BUFFER_DESC desc;
+ desc.ByteWidth = aSize;
+ desc.MiscFlags = 0;
+ desc.StructureByteStride = 0;
+
+ switch (aUsage) {
+ case MLGUsage::Dynamic:
+ desc.Usage = D3D11_USAGE_DYNAMIC;
+ desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
+ break;
+ case MLGUsage::Immutable:
+ desc.Usage = D3D11_USAGE_IMMUTABLE;
+ desc.CPUAccessFlags = 0;
+ break;
+ default:
+ MOZ_ASSERT_UNREACHABLE("Unknown buffer usage type");
+ return nullptr;
+ }
+
+ switch (aType) {
+ case MLGBufferType::Vertex:
+ desc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
+ break;
+ case MLGBufferType::Constant:
+ desc.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
+ break;
+ default:
+ MOZ_ASSERT_UNREACHABLE("Unknown buffer type");
+ return nullptr;
+ }
+
+ D3D11_SUBRESOURCE_DATA data;
+ data.pSysMem = aInitialData;
+ data.SysMemPitch = aSize;
+ data.SysMemSlicePitch = 0;
+
+ RefPtr<ID3D11Buffer> buffer;
+ HRESULT hr = aDevice->CreateBuffer(&desc, aInitialData ? &data : nullptr,
+ getter_AddRefs(buffer));
+ if (FAILED(hr) || !buffer) {
+ gfxCriticalError() << "Failed to create ID3D11Buffer.";
+ return nullptr;
+ }
+
+ return new MLGBufferD3D11(buffer, aType, aSize);
+}
+
+MLGBufferD3D11::MLGBufferD3D11(ID3D11Buffer* aBuffer, MLGBufferType aType,
+ size_t aSize)
+ : mBuffer(aBuffer), mType(aType), mSize(aSize) {
+ switch (mType) {
+ case MLGBufferType::Vertex:
+ mlg::sVertexBufferUsage += mSize;
+ break;
+ case MLGBufferType::Constant:
+ mlg::sConstantBufferUsage += mSize;
+ break;
+ }
+}
+
+MLGBufferD3D11::~MLGBufferD3D11() {
+ switch (mType) {
+ case MLGBufferType::Vertex:
+ mlg::sVertexBufferUsage -= mSize;
+ break;
+ case MLGBufferType::Constant:
+ mlg::sConstantBufferUsage -= mSize;
+ break;
+ }
+}
+
+MLGTextureD3D11::MLGTextureD3D11(ID3D11Texture2D* aTexture)
+ : mTexture(aTexture) {
+ D3D11_TEXTURE2D_DESC desc;
+ aTexture->GetDesc(&desc);
+
+ mSize.width = desc.Width;
+ mSize.height = desc.Height;
+}
+
+/* static */
+RefPtr<MLGTextureD3D11> MLGTextureD3D11::Create(ID3D11Device* aDevice,
+ const gfx::IntSize& aSize,
+ gfx::SurfaceFormat aFormat,
+ MLGUsage aUsage,
+ MLGTextureFlags aFlags) {
+ D3D11_TEXTURE2D_DESC desc;
+ ::ZeroMemory(&desc, sizeof(desc));
+ desc.Width = aSize.width;
+ desc.Height = aSize.height;
+ desc.MipLevels = 1;
+ desc.ArraySize = 1;
+ desc.SampleDesc.Count = 1;
+
+ switch (aFormat) {
+ case SurfaceFormat::B8G8R8A8:
+ desc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
+ break;
+ default:
+ MOZ_ASSERT_UNREACHABLE("Unsupported surface format");
+ return nullptr;
+ }
+
+ switch (aUsage) {
+ case MLGUsage::Immutable:
+ desc.Usage = D3D11_USAGE_IMMUTABLE;
+ break;
+ case MLGUsage::Default:
+ desc.Usage = D3D11_USAGE_DEFAULT;
+ break;
+ case MLGUsage::Dynamic:
+ desc.Usage = D3D11_USAGE_DYNAMIC;
+ desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
+ break;
+ case MLGUsage::Staging:
+ desc.Usage = D3D11_USAGE_STAGING;
+ desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
+ break;
+ default:
+ MOZ_ASSERT_UNREACHABLE("Unsupported usage type");
+ break;
+ }
+
+ if (aFlags & MLGTextureFlags::ShaderResource) {
+ desc.BindFlags |= D3D11_BIND_SHADER_RESOURCE;
+ }
+ if (aFlags & MLGTextureFlags::RenderTarget) {
+ desc.BindFlags |= D3D11_BIND_RENDER_TARGET;
+ }
+
+ RefPtr<ID3D11Texture2D> texture;
+ HRESULT hr =
+ aDevice->CreateTexture2D(&desc, nullptr, getter_AddRefs(texture));
+ if (FAILED(hr) || !texture) {
+ gfxCriticalNote << "Failed to create 2D texture: " << hexa(hr);
+ return nullptr;
+ }
+
+ ReportTextureMemoryUsage(texture, aSize.width * aSize.height * 4);
+
+ return new MLGTextureD3D11(texture);
+}
+
+ID3D11ShaderResourceView* MLGTextureD3D11::GetShaderResourceView() {
+ if (!mView) {
+ RefPtr<ID3D11Device> device;
+ mTexture->GetDevice(getter_AddRefs(device));
+
+ HRESULT hr = device->CreateShaderResourceView(mTexture, nullptr,
+ getter_AddRefs(mView));
+ if (FAILED(hr) || !mView) {
+ gfxWarning() << "Could not create shader resource view: " << hexa(hr);
+ return nullptr;
+ }
+ }
+ return mView;
+}
+
+MLGDeviceD3D11::MLGDeviceD3D11(ID3D11Device* aDevice)
+ : mDevice(aDevice), mScissored(false) {}
+
+MLGDeviceD3D11::~MLGDeviceD3D11() {
+ // Caller should have unlocked all textures after presenting.
+ MOZ_ASSERT(mLockedTextures.IsEmpty());
+ MOZ_ASSERT(mLockAttemptedTextures.IsEmpty());
+}
+
+bool MLGDeviceD3D11::Initialize() {
+ if (!mDevice) {
+ return Fail("FEATURE_FAILURE_NO_DEVICE");
+ }
+
+ if (mDevice->GetFeatureLevel() < D3D_FEATURE_LEVEL_10_0) {
+ return Fail("FEATURE_FAILURE_NEED_LEVEL_10_0");
+ }
+
+ mDevice->GetImmediateContext(getter_AddRefs(mCtx));
+ if (!mCtx) {
+ return Fail("FEATURE_FAILURE_NO_CONTEXT");
+ }
+
+ mCtx->QueryInterface((ID3D11DeviceContext1**)getter_AddRefs(mCtx1));
+
+ if (mCtx1) {
+ // Windows 7 can have Direct3D 11.1 if the platform update is installed,
+ // but according to some NVIDIA presentations it is known to be buggy.
+ // It's not clear whether that only refers to command list emulation,
+ // or whether it just has performance penalties. To be safe we only use
+ // it on Windows 8 or higher.
+ //
+ // https://docs.microsoft.com/en-us/windows-hardware/drivers/display/directx-feature-improvements-in-windows-8#buffers
+ D3D11_FEATURE_DATA_D3D11_OPTIONS options;
+ HRESULT hr = mDevice->CheckFeatureSupport(D3D11_FEATURE_D3D11_OPTIONS,
+ &options, sizeof(options));
+ if (SUCCEEDED(hr)) {
+ if (IsWin8OrLater()) {
+ mCanUseConstantBufferOffsetBinding =
+ (options.ConstantBufferOffsetting != FALSE);
+ } else {
+ gfxConfig::EnableFallback(Fallback::NO_CONSTANT_BUFFER_OFFSETTING,
+ "Unsupported by driver");
+ }
+ mCanUseClearView = (options.ClearView != FALSE);
+ } else {
+ gfxCriticalNote << "Failed to query D3D11.1 feature support: "
+ << hexa(hr);
+ }
+ }
+
+ // Get capabilities.
+ switch (mDevice->GetFeatureLevel()) {
+ case D3D_FEATURE_LEVEL_11_1:
+ case D3D_FEATURE_LEVEL_11_0:
+ mMaxConstantBufferBindSize = D3D11_REQ_CONSTANT_BUFFER_ELEMENT_COUNT * 16;
+ break;
+ case D3D_FEATURE_LEVEL_10_1:
+ case D3D_FEATURE_LEVEL_10_0:
+ mMaxConstantBufferBindSize = D3D10_REQ_CONSTANT_BUFFER_ELEMENT_COUNT * 16;
+ break;
+ default:
+ MOZ_ASSERT_UNREACHABLE("Unknown feature level");
+ }
+
+ mDiagnostics = MakeUnique<DiagnosticsD3D11>(mDevice, mCtx);
+
+ {
+ struct Vertex2D {
+ float x;
+ float y;
+ };
+ Vertex2D vertices[] = {{0, 0}, {1.0f, 0}, {0, 1.0f}, {1.0f, 1.0f}};
+ mUnitQuadVB = CreateBuffer(MLGBufferType::Vertex, sizeof(Vertex2D) * 4,
+ MLGUsage::Immutable, &vertices);
+ if (!mUnitQuadVB) {
+ return Fail("FEATURE_FAILURE_UNIT_QUAD_BUFFER");
+ }
+ }
+
+ {
+ struct Vertex3D {
+ float x;
+ float y;
+ float z;
+ };
+ Vertex3D vertices[3] = {
+ {1.0f, 0.0f, 0.0f}, {0.0f, 1.0f, 0.0f}, {0.0f, 0.0f, 1.0f}};
+ mUnitTriangleVB = CreateBuffer(MLGBufferType::Vertex, sizeof(Vertex3D) * 3,
+ MLGUsage::Immutable, &vertices);
+ if (!mUnitTriangleVB) {
+ return Fail("FEATURE_FAILURE_UNIT_TRIANGLE_BUFFER");
+ }
+ }
+
+ // Define pixel shaders.
+#define LAZY_PS(cxxName, enumName) \
+ mLazyPixelShaders[PixelShaderID::enumName] = &s##cxxName;
+ LAZY_PS(TexturedVertexRGB, TexturedVertexRGB);
+ LAZY_PS(TexturedVertexRGBA, TexturedVertexRGBA);
+ LAZY_PS(TexturedQuadRGB, TexturedQuadRGB);
+ LAZY_PS(TexturedQuadRGBA, TexturedQuadRGBA);
+ LAZY_PS(ColoredQuadPS, ColoredQuad);
+ LAZY_PS(ColoredVertexPS, ColoredVertex);
+ LAZY_PS(ComponentAlphaQuadPS, ComponentAlphaQuad);
+ LAZY_PS(ComponentAlphaVertexPS, ComponentAlphaVertex);
+ LAZY_PS(TexturedVertexIMC4, TexturedVertexIMC4);
+ LAZY_PS(TexturedVertexIdentityIMC4, TexturedVertexIdentityIMC4);
+ LAZY_PS(TexturedVertexNV12, TexturedVertexNV12);
+ LAZY_PS(TexturedQuadIMC4, TexturedQuadIMC4);
+ LAZY_PS(TexturedQuadIdentityIMC4, TexturedQuadIdentityIMC4);
+ LAZY_PS(TexturedQuadNV12, TexturedQuadNV12);
+ LAZY_PS(BlendMultiplyPS, BlendMultiply);
+ LAZY_PS(BlendScreenPS, BlendScreen);
+ LAZY_PS(BlendOverlayPS, BlendOverlay);
+ LAZY_PS(BlendDarkenPS, BlendDarken);
+ LAZY_PS(BlendLightenPS, BlendLighten);
+ LAZY_PS(BlendColorDodgePS, BlendColorDodge);
+ LAZY_PS(BlendColorBurnPS, BlendColorBurn);
+ LAZY_PS(BlendHardLightPS, BlendHardLight);
+ LAZY_PS(BlendSoftLightPS, BlendSoftLight);
+ LAZY_PS(BlendDifferencePS, BlendDifference);
+ LAZY_PS(BlendExclusionPS, BlendExclusion);
+ LAZY_PS(BlendHuePS, BlendHue);
+ LAZY_PS(BlendSaturationPS, BlendSaturation);
+ LAZY_PS(BlendColorPS, BlendColor);
+ LAZY_PS(BlendLuminosityPS, BlendLuminosity);
+ LAZY_PS(ClearPS, Clear);
+ LAZY_PS(MaskCombinerPS, MaskCombiner);
+ LAZY_PS(DiagnosticTextPS, DiagnosticText);
+#undef LAZY_PS
+
+ // Define vertex shaders.
+#define LAZY_VS(cxxName, enumName) \
+ mLazyVertexShaders[VertexShaderID::enumName] = &s##cxxName;
+ LAZY_VS(TexturedQuadVS, TexturedQuad);
+ LAZY_VS(TexturedVertexVS, TexturedVertex);
+ LAZY_VS(BlendVertexVS, BlendVertex);
+ LAZY_VS(ColoredQuadVS, ColoredQuad);
+ LAZY_VS(ColoredVertexVS, ColoredVertex);
+ LAZY_VS(ClearVS, Clear);
+ LAZY_VS(MaskCombinerVS, MaskCombiner);
+ LAZY_VS(DiagnosticTextVS, DiagnosticText);
+#undef LAZY_VS
+
+ // Force critical shaders to initialize early.
+ if (!InitPixelShader(PixelShaderID::TexturedQuadRGB) ||
+ !InitPixelShader(PixelShaderID::TexturedQuadRGBA) ||
+ !InitPixelShader(PixelShaderID::ColoredQuad) ||
+ !InitPixelShader(PixelShaderID::ComponentAlphaQuad) ||
+ !InitPixelShader(PixelShaderID::Clear) ||
+ !InitVertexShader(VertexShaderID::TexturedQuad) ||
+ !InitVertexShader(VertexShaderID::ColoredQuad) ||
+ !InitVertexShader(VertexShaderID::Clear)) {
+ return Fail("FEATURE_FAILURE_CRITICAL_SHADER_FAILURE");
+ }
+
+ // Common unit quad layout: vPos, vRect, vLayerIndex, vDepth
+#define BASE_UNIT_QUAD_LAYOUT \
+ {"POSITION", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, \
+ 0}, \
+ {"TEXCOORD", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, \
+ 1, 0, D3D11_INPUT_PER_INSTANCE_DATA, \
+ 1}, \
+ {"TEXCOORD", \
+ 1, \
+ DXGI_FORMAT_R32_UINT, \
+ 1, \
+ D3D11_APPEND_ALIGNED_ELEMENT, \
+ D3D11_INPUT_PER_INSTANCE_DATA, \
+ 1}, \
+ { \
+ "TEXCOORD", 2, DXGI_FORMAT_R32_SINT, 1, D3D11_APPEND_ALIGNED_ELEMENT, \
+ D3D11_INPUT_PER_INSTANCE_DATA, 1 \
+ }
+
+ // Common unit triangle layout: vUnitPos, vPos1-3, vLayerIndex, vDepth
+#define BASE_UNIT_TRIANGLE_LAYOUT \
+ {"POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, \
+ 0, 0, D3D11_INPUT_PER_VERTEX_DATA, \
+ 0}, \
+ {"POSITION", 1, DXGI_FORMAT_R32G32_FLOAT, \
+ 1, 0, D3D11_INPUT_PER_INSTANCE_DATA, \
+ 1}, \
+ {"POSITION", \
+ 2, \
+ DXGI_FORMAT_R32G32_FLOAT, \
+ 1, \
+ D3D11_APPEND_ALIGNED_ELEMENT, \
+ D3D11_INPUT_PER_INSTANCE_DATA, \
+ 1}, \
+ {"POSITION", \
+ 3, \
+ DXGI_FORMAT_R32G32_FLOAT, \
+ 1, \
+ D3D11_APPEND_ALIGNED_ELEMENT, \
+ D3D11_INPUT_PER_INSTANCE_DATA, \
+ 1}, \
+ {"TEXCOORD", \
+ 0, \
+ DXGI_FORMAT_R32_UINT, \
+ 1, \
+ D3D11_APPEND_ALIGNED_ELEMENT, \
+ D3D11_INPUT_PER_INSTANCE_DATA, \
+ 1}, \
+ {"TEXCOORD", \
+ 1, \
+ DXGI_FORMAT_R32_SINT, \
+ 1, \
+ D3D11_APPEND_ALIGNED_ELEMENT, \
+ D3D11_INPUT_PER_INSTANCE_DATA, \
+ 1}
+
+ // Initialize input layouts.
+ {
+ D3D11_INPUT_ELEMENT_DESC inputDesc[] = {
+ BASE_UNIT_QUAD_LAYOUT,
+ // vTexRect
+ {"TEXCOORD", 3, DXGI_FORMAT_R32G32B32A32_FLOAT, 1,
+ D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_INSTANCE_DATA, 1}};
+ if (!InitInputLayout(inputDesc, MOZ_ARRAY_LENGTH(inputDesc),
+ sTexturedQuadVS, VertexShaderID::TexturedQuad)) {
+ return Fail("FEATURE_FAILURE_UNIT_QUAD_TEXTURED_LAYOUT");
+ }
+ }
+ {
+ D3D11_INPUT_ELEMENT_DESC inputDesc[] = {
+ BASE_UNIT_QUAD_LAYOUT,
+ // vColor
+ {"TEXCOORD", 3, DXGI_FORMAT_R32G32B32A32_FLOAT, 1,
+ D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_INSTANCE_DATA, 1}};
+ if (!InitInputLayout(inputDesc, MOZ_ARRAY_LENGTH(inputDesc), sColoredQuadVS,
+ VertexShaderID::ColoredQuad)) {
+ return Fail("FEATURE_FAILURE_UNIT_QUAD_COLORED_LAYOUT");
+ }
+ }
+ {
+ D3D11_INPUT_ELEMENT_DESC inputDesc[] = {
+ BASE_UNIT_TRIANGLE_LAYOUT,
+ // vTexCoord1, vTexCoord2, vTexCoord3
+ {"TEXCOORD", 2, DXGI_FORMAT_R32G32_FLOAT, 1,
+ D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_INSTANCE_DATA, 1},
+ {"TEXCOORD", 3, DXGI_FORMAT_R32G32_FLOAT, 1,
+ D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_INSTANCE_DATA, 1},
+ {"TEXCOORD", 4, DXGI_FORMAT_R32G32_FLOAT, 1,
+ D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_INSTANCE_DATA, 1}};
+ if (!InitInputLayout(inputDesc, MOZ_ARRAY_LENGTH(inputDesc),
+ sTexturedVertexVS, VertexShaderID::TexturedVertex)) {
+ return Fail("FEATURE_FAILURE_TEXTURED_INPUT_LAYOUT");
+ }
+ // Propagate the input layout to other vertex shaders that use the same.
+ mInputLayouts[VertexShaderID::BlendVertex] =
+ mInputLayouts[VertexShaderID::TexturedVertex];
+ }
+ {
+ D3D11_INPUT_ELEMENT_DESC inputDesc[] = {
+ BASE_UNIT_TRIANGLE_LAYOUT,
+ {"TEXCOORD", 2, DXGI_FORMAT_R32G32B32A32_FLOAT, 1,
+ D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_INSTANCE_DATA, 1}};
+ if (!InitInputLayout(inputDesc, MOZ_ARRAY_LENGTH(inputDesc),
+ sColoredVertexVS, VertexShaderID::ColoredVertex)) {
+ return Fail("FEATURE_FAILURE_COLORED_INPUT_LAYOUT");
+ }
+ }
+
+#undef BASE_UNIT_QUAD_LAYOUT
+#undef BASE_UNIT_TRIANGLE_LAYOUT
+
+ // Ancillary shaders that are not used for batching.
+ {
+ D3D11_INPUT_ELEMENT_DESC inputDesc[] = {
+ // vPos
+ {"POSITION", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 0,
+ D3D11_INPUT_PER_VERTEX_DATA, 0},
+ // vRect
+ {"TEXCOORD", 0, DXGI_FORMAT_R32G32B32A32_SINT, 1, 0,
+ D3D11_INPUT_PER_INSTANCE_DATA, 1},
+ // vDepth
+ {"TEXCOORD", 1, DXGI_FORMAT_R32_SINT, 1, D3D11_APPEND_ALIGNED_ELEMENT,
+ D3D11_INPUT_PER_INSTANCE_DATA, 1},
+ };
+ if (!InitInputLayout(inputDesc, MOZ_ARRAY_LENGTH(inputDesc), sClearVS,
+ VertexShaderID::Clear)) {
+ return Fail("FEATURE_FAILURE_CLEAR_INPUT_LAYOUT");
+ }
+ }
+ {
+ D3D11_INPUT_ELEMENT_DESC inputDesc[] = {
+ // vPos
+ {"POSITION", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 0,
+ D3D11_INPUT_PER_VERTEX_DATA, 0},
+ // vTexCoords
+ {"POSITION", 1, DXGI_FORMAT_R32G32B32A32_FLOAT, 1,
+ D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_INSTANCE_DATA, 1}};
+ if (!InitInputLayout(inputDesc, MOZ_ARRAY_LENGTH(inputDesc),
+ sMaskCombinerVS, VertexShaderID::MaskCombiner)) {
+ return Fail("FEATURE_FAILURE_MASK_COMBINER_INPUT_LAYOUT");
+ }
+ }
+ {
+ D3D11_INPUT_ELEMENT_DESC inputDesc[] = {
+ // vPos
+ {"POSITION", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 0,
+ D3D11_INPUT_PER_VERTEX_DATA, 0},
+ // vRect
+ {"TEXCOORD", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 1, 0,
+ D3D11_INPUT_PER_INSTANCE_DATA, 1},
+ // vTexCoords
+ {"TEXCOORD", 1, DXGI_FORMAT_R32G32B32A32_FLOAT, 1,
+ D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_INSTANCE_DATA, 1},
+ };
+ if (!InitInputLayout(inputDesc, MOZ_ARRAY_LENGTH(inputDesc),
+ sDiagnosticTextVS, VertexShaderID::DiagnosticText)) {
+ return Fail("FEATURE_FAILURE_DIAGNOSTIC_INPUT_LAYOUT");
+ }
+ }
+
+ if (!InitRasterizerStates() || !InitDepthStencilState() ||
+ !InitBlendStates() || !InitSamplerStates() || !InitSyncObject()) {
+ return false;
+ }
+
+ mCtx->RSSetState(mRasterizerStateNoScissor);
+
+ return MLGDevice::Initialize();
+}
+
+bool MLGDeviceD3D11::InitPixelShader(PixelShaderID aShaderID) {
+ const ShaderBytes* code = mLazyPixelShaders[aShaderID];
+ HRESULT hr =
+ mDevice->CreatePixelShader(code->mData, code->mLength, nullptr,
+ getter_AddRefs(mPixelShaders[aShaderID]));
+ if (FAILED(hr)) {
+ gfxCriticalNote << "Could not create pixel shader "
+ << hexa(unsigned(aShaderID)) << ": " << hexa(hr);
+ return false;
+ }
+ return true;
+}
+
+bool MLGDeviceD3D11::InitRasterizerStates() {
+ {
+ CD3D11_RASTERIZER_DESC desc = CD3D11_RASTERIZER_DESC(CD3D11_DEFAULT());
+ desc.CullMode = D3D11_CULL_NONE;
+ desc.FillMode = D3D11_FILL_SOLID;
+ desc.ScissorEnable = TRUE;
+ HRESULT hr = mDevice->CreateRasterizerState(
+ &desc, getter_AddRefs(mRasterizerStateScissor));
+ if (FAILED(hr)) {
+ return Fail("FEATURE_FAILURE_SCISSOR_RASTERIZER",
+ "Could not create scissor rasterizer (%x)", hr);
+ }
+ }
+ {
+ CD3D11_RASTERIZER_DESC desc = CD3D11_RASTERIZER_DESC(CD3D11_DEFAULT());
+ desc.CullMode = D3D11_CULL_NONE;
+ desc.FillMode = D3D11_FILL_SOLID;
+ HRESULT hr = mDevice->CreateRasterizerState(
+ &desc, getter_AddRefs(mRasterizerStateNoScissor));
+ if (FAILED(hr)) {
+ return Fail("FEATURE_FAILURE_DEFAULT_RASTERIZER",
+ "Could not create default rasterizer (%x)", hr);
+ }
+ }
+ return true;
+}
+
+bool MLGDeviceD3D11::InitSamplerStates() {
+ {
+ CD3D11_SAMPLER_DESC desc = CD3D11_SAMPLER_DESC(CD3D11_DEFAULT());
+ HRESULT hr = mDevice->CreateSamplerState(
+ &desc, getter_AddRefs(mSamplerStates[SamplerMode::LinearClamp]));
+ if (FAILED(hr)) {
+ return Fail("FEATURE_FAILURE_LINEAR_CLAMP_SAMPLER",
+ "Could not create linear clamp sampler (%x)", hr);
+ }
+ }
+ {
+ CD3D11_SAMPLER_DESC desc = CD3D11_SAMPLER_DESC(CD3D11_DEFAULT());
+ desc.AddressU = D3D11_TEXTURE_ADDRESS_BORDER;
+ desc.AddressV = D3D11_TEXTURE_ADDRESS_BORDER;
+ desc.AddressW = D3D11_TEXTURE_ADDRESS_BORDER;
+ memset(desc.BorderColor, 0, sizeof(desc.BorderColor));
+ HRESULT hr = mDevice->CreateSamplerState(
+ &desc, getter_AddRefs(mSamplerStates[SamplerMode::LinearClampToZero]));
+ if (FAILED(hr)) {
+ return Fail("FEATURE_FAILURE_LINEAR_CLAMP_ZERO_SAMPLER",
+ "Could not create linear clamp to zero sampler (%x)", hr);
+ }
+ }
+ {
+ CD3D11_SAMPLER_DESC desc = CD3D11_SAMPLER_DESC(CD3D11_DEFAULT());
+ desc.AddressU = D3D11_TEXTURE_ADDRESS_WRAP;
+ desc.AddressV = D3D11_TEXTURE_ADDRESS_WRAP;
+ desc.AddressW = D3D11_TEXTURE_ADDRESS_WRAP;
+ HRESULT hr = mDevice->CreateSamplerState(
+ &desc, getter_AddRefs(mSamplerStates[SamplerMode::LinearRepeat]));
+ if (FAILED(hr)) {
+ return Fail("FEATURE_FAILURE_LINEAR_CLAMP_ZERO_SAMPLER",
+ "Could not create linear clamp to zero sampler (%x)", hr);
+ }
+ }
+
+ {
+ CD3D11_SAMPLER_DESC desc = CD3D11_SAMPLER_DESC(CD3D11_DEFAULT());
+ desc.Filter = D3D11_FILTER_MIN_MAG_MIP_POINT;
+ HRESULT hr = mDevice->CreateSamplerState(
+ &desc, getter_AddRefs(mSamplerStates[SamplerMode::Point]));
+ if (FAILED(hr)) {
+ return Fail("FEATURE_FAILURE_POINT_SAMPLER",
+ "Could not create point sampler (%x)", hr);
+ }
+ }
+ return true;
+}
+
+bool MLGDeviceD3D11::InitBlendStates() {
+ {
+ CD3D11_BLEND_DESC desc = CD3D11_BLEND_DESC(CD3D11_DEFAULT());
+ HRESULT hr = mDevice->CreateBlendState(
+ &desc, getter_AddRefs(mBlendStates[MLGBlendState::Copy]));
+ if (FAILED(hr)) {
+ return Fail("FEATURE_FAILURE_COPY_BLEND_STATE",
+ "Could not create copy blend state (%x)", hr);
+ }
+ }
+
+ {
+ CD3D11_BLEND_DESC desc = CD3D11_BLEND_DESC(CD3D11_DEFAULT());
+ desc.RenderTarget[0].BlendEnable = TRUE;
+ desc.RenderTarget[0].SrcBlend = D3D11_BLEND_ONE;
+ desc.RenderTarget[0].DestBlend = D3D11_BLEND_INV_SRC_ALPHA;
+ desc.RenderTarget[0].BlendOp = D3D11_BLEND_OP_ADD;
+ desc.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_ONE;
+ desc.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_INV_SRC_ALPHA;
+ desc.RenderTarget[0].BlendOpAlpha = D3D11_BLEND_OP_ADD;
+ HRESULT hr = mDevice->CreateBlendState(
+ &desc, getter_AddRefs(mBlendStates[MLGBlendState::Over]));
+ if (FAILED(hr)) {
+ return Fail("FEATURE_FAILURE_OVER_BLEND_STATE",
+ "Could not create over blend state (%x)", hr);
+ }
+ }
+
+ {
+ CD3D11_BLEND_DESC desc = CD3D11_BLEND_DESC(CD3D11_DEFAULT());
+ desc.RenderTarget[0].BlendEnable = TRUE;
+ desc.RenderTarget[0].SrcBlend = D3D11_BLEND_SRC_ALPHA;
+ desc.RenderTarget[0].DestBlend = D3D11_BLEND_INV_SRC_ALPHA;
+ desc.RenderTarget[0].BlendOp = D3D11_BLEND_OP_ADD;
+ desc.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_ONE;
+ desc.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_INV_SRC_ALPHA;
+ desc.RenderTarget[0].BlendOpAlpha = D3D11_BLEND_OP_ADD;
+ HRESULT hr = mDevice->CreateBlendState(
+ &desc, getter_AddRefs(mBlendStates[MLGBlendState::OverAndPremultiply]));
+ if (FAILED(hr)) {
+ return Fail("FEATURE_FAILURE_OVER_BLEND_STATE",
+ "Could not create over blend state (%x)", hr);
+ }
+ }
+
+ {
+ CD3D11_BLEND_DESC desc = CD3D11_BLEND_DESC(CD3D11_DEFAULT());
+ desc.RenderTarget[0].BlendEnable = TRUE;
+ desc.RenderTarget[0].SrcBlend = D3D11_BLEND_ONE;
+ desc.RenderTarget[0].DestBlend = D3D11_BLEND_ONE;
+ desc.RenderTarget[0].BlendOp = D3D11_BLEND_OP_MIN;
+ desc.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_ONE;
+ desc.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_ONE;
+ desc.RenderTarget[0].BlendOpAlpha = D3D11_BLEND_OP_MIN;
+ HRESULT hr = mDevice->CreateBlendState(
+ &desc, getter_AddRefs(mBlendStates[MLGBlendState::Min]));
+ if (FAILED(hr)) {
+ return Fail("FEATURE_FAILURE_MIN_BLEND_STATE",
+ "Could not create min blend state (%x)", hr);
+ }
+ }
+
+ {
+ CD3D11_BLEND_DESC desc = CD3D11_BLEND_DESC(CD3D11_DEFAULT());
+ desc.RenderTarget[0].BlendEnable = TRUE;
+ desc.RenderTarget[0].SrcBlend = D3D11_BLEND_ONE;
+ desc.RenderTarget[0].DestBlend = D3D11_BLEND_INV_SRC1_COLOR;
+ desc.RenderTarget[0].BlendOp = D3D11_BLEND_OP_ADD;
+ desc.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_ONE;
+ desc.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_INV_SRC_ALPHA;
+ desc.RenderTarget[0].BlendOpAlpha = D3D11_BLEND_OP_ADD;
+ HRESULT hr = mDevice->CreateBlendState(
+ &desc, getter_AddRefs(mBlendStates[MLGBlendState::ComponentAlpha]));
+ if (FAILED(hr)) {
+ return Fail("FEATURE_FAILURE_COMPONENT_ALPHA_BLEND_STATE",
+ "Could not create component alpha blend state (%x)", hr);
+ }
+ }
+ return true;
+}
+
+bool MLGDeviceD3D11::InitDepthStencilState() {
+ D3D11_DEPTH_STENCIL_DESC desc = CD3D11_DEPTH_STENCIL_DESC(D3D11_DEFAULT);
+
+ HRESULT hr = mDevice->CreateDepthStencilState(
+ &desc, getter_AddRefs(mDepthStencilStates[MLGDepthTestMode::Write]));
+ if (FAILED(hr)) {
+ return Fail("FEATURE_FAILURE_WRITE_DEPTH_STATE",
+ "Could not create write depth stencil state (%x)", hr);
+ }
+
+ desc.DepthWriteMask = D3D11_DEPTH_WRITE_MASK_ZERO;
+ hr = mDevice->CreateDepthStencilState(
+ &desc, getter_AddRefs(mDepthStencilStates[MLGDepthTestMode::ReadOnly]));
+ if (FAILED(hr)) {
+ return Fail("FEATURE_FAILURE_READ_DEPTH_STATE",
+ "Could not create read depth stencil state (%x)", hr);
+ }
+
+ desc.DepthEnable = FALSE;
+ hr = mDevice->CreateDepthStencilState(
+ &desc, getter_AddRefs(mDepthStencilStates[MLGDepthTestMode::Disabled]));
+ if (FAILED(hr)) {
+ return Fail("FEATURE_FAILURE_DISABLED_DEPTH_STATE",
+ "Could not create disabled depth stencil state (%x)", hr);
+ }
+
+ desc = CD3D11_DEPTH_STENCIL_DESC(D3D11_DEFAULT);
+ desc.DepthFunc = D3D11_COMPARISON_ALWAYS;
+ hr = mDevice->CreateDepthStencilState(
+ &desc,
+ getter_AddRefs(mDepthStencilStates[MLGDepthTestMode::AlwaysWrite]));
+ if (FAILED(hr)) {
+ return Fail("FEATURE_FAILURE_WRITE_DEPTH_STATE",
+ "Could not create always-write depth stencil state (%x)", hr);
+ }
+
+ return true;
+}
+
+bool MLGDeviceD3D11::InitVertexShader(VertexShaderID aShaderID) {
+ const ShaderBytes* code = mLazyVertexShaders[aShaderID];
+ HRESULT hr =
+ mDevice->CreateVertexShader(code->mData, code->mLength, nullptr,
+ getter_AddRefs(mVertexShaders[aShaderID]));
+ if (FAILED(hr)) {
+ gfxCriticalNote << "Could not create vertex shader "
+ << hexa(unsigned(aShaderID)) << ": " << hexa(hr);
+ return false;
+ }
+ return true;
+}
+
+bool MLGDeviceD3D11::InitInputLayout(D3D11_INPUT_ELEMENT_DESC* aDesc,
+ size_t aNumElements,
+ const ShaderBytes& aCode,
+ VertexShaderID aShaderID) {
+ HRESULT hr = mDevice->CreateInputLayout(
+ aDesc, aNumElements, aCode.mData, aCode.mLength,
+ getter_AddRefs(mInputLayouts[aShaderID]));
+ if (FAILED(hr)) {
+ gfxCriticalNote << "Could not create input layout for shader "
+ << hexa(unsigned(aShaderID)) << ": " << hexa(hr);
+ return false;
+ }
+ return true;
+}
+
+TextureFactoryIdentifier MLGDeviceD3D11::GetTextureFactoryIdentifier(
+ widget::CompositorWidget* aWidget) const {
+ TextureFactoryIdentifier ident(GetLayersBackend(), XRE_GetProcessType(),
+ GetMaxTextureSize());
+ if (aWidget) {
+ ident.mUseCompositorWnd = !!aWidget->AsWindows()->GetCompositorHwnd();
+ }
+ if (mSyncObject) {
+ ident.mSyncHandle = mSyncObject->GetSyncHandle();
+ }
+
+ return ident;
+}
+
+inline uint32_t GetMaxTextureSizeForFeatureLevel1(
+ 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;
+}
+
+LayersBackend MLGDeviceD3D11::GetLayersBackend() const {
+ return LayersBackend::LAYERS_D3D11;
+}
+
+int32_t MLGDeviceD3D11::GetMaxTextureSize() const {
+ return GetMaxTextureSizeForFeatureLevel1(mDevice->GetFeatureLevel());
+}
+
+RefPtr<MLGSwapChain> MLGDeviceD3D11::CreateSwapChainForWidget(
+ widget::CompositorWidget* aWidget) {
+ return MLGSwapChainD3D11::Create(this, mDevice, aWidget);
+}
+
+RefPtr<DataTextureSource> MLGDeviceD3D11::CreateDataTextureSource(
+ TextureFlags aFlags) {
+ return new DataTextureSourceD3D11(mDevice, gfx::SurfaceFormat::UNKNOWN,
+ aFlags);
+}
+
+static inline D3D11_MAP ToD3D11Map(MLGMapType aType) {
+ switch (aType) {
+ case MLGMapType::READ:
+ return D3D11_MAP_READ;
+ case MLGMapType::READ_WRITE:
+ return D3D11_MAP_READ_WRITE;
+ case MLGMapType::WRITE:
+ return D3D11_MAP_WRITE;
+ case MLGMapType::WRITE_DISCARD:
+ return D3D11_MAP_WRITE_DISCARD;
+ }
+ return D3D11_MAP_WRITE;
+}
+
+bool MLGDeviceD3D11::Map(MLGResource* aResource, MLGMapType aType,
+ MLGMappedResource* aMap) {
+ ID3D11Resource* resource = aResource->AsResourceD3D11()->GetResource();
+ MOZ_ASSERT(resource);
+
+ D3D11_MAPPED_SUBRESOURCE map;
+ HRESULT hr = mCtx->Map(resource, 0, ToD3D11Map(aType), 0, &map);
+
+ if (FAILED(hr)) {
+ gfxWarning() << "Could not map MLG resource: " << hexa(hr);
+ return false;
+ }
+
+ aMap->mData = reinterpret_cast<uint8_t*>(map.pData);
+ aMap->mStride = map.RowPitch;
+ return true;
+}
+
+void MLGDeviceD3D11::Unmap(MLGResource* aResource) {
+ ID3D11Resource* resource = aResource->AsResourceD3D11()->GetResource();
+ mCtx->Unmap(resource, 0);
+}
+
+void MLGDeviceD3D11::UpdatePartialResource(MLGResource* aResource,
+ const gfx::IntRect* aRect,
+ void* aData, uint32_t aStride) {
+ D3D11_BOX box;
+ if (aRect) {
+ box = RectToBox(*aRect);
+ }
+
+ ID3D11Resource* resource = aResource->AsResourceD3D11()->GetResource();
+ mCtx->UpdateSubresource(resource, 0, aRect ? &box : nullptr, aData, aStride,
+ 0);
+}
+
+void MLGDeviceD3D11::SetRenderTarget(MLGRenderTarget* aRT) {
+ ID3D11RenderTargetView* rtv = nullptr;
+ ID3D11DepthStencilView* dsv = nullptr;
+
+ if (aRT) {
+ MLGRenderTargetD3D11* rt = aRT->AsD3D11();
+ rtv = rt->GetRenderTargetView();
+ dsv = rt->GetDSV();
+ }
+
+ mCtx->OMSetRenderTargets(1, &rtv, dsv);
+ mCurrentRT = aRT;
+}
+
+MLGRenderTarget* MLGDeviceD3D11::GetRenderTarget() { return mCurrentRT; }
+
+void MLGDeviceD3D11::SetViewport(const gfx::IntRect& aViewport) {
+ D3D11_VIEWPORT vp;
+ vp.MaxDepth = 1.0f;
+ vp.MinDepth = 0.0f;
+ vp.TopLeftX = aViewport.X();
+ vp.TopLeftY = aViewport.Y();
+ vp.Width = aViewport.Width();
+ vp.Height = aViewport.Height();
+ mCtx->RSSetViewports(1, &vp);
+}
+
+static inline D3D11_RECT ToD3D11Rect(const gfx::IntRect& aRect) {
+ D3D11_RECT rect;
+ rect.left = aRect.X();
+ rect.top = aRect.Y();
+ rect.right = aRect.XMost();
+ rect.bottom = aRect.YMost();
+ return rect;
+}
+
+void MLGDeviceD3D11::SetScissorRect(const Maybe<gfx::IntRect>& aScissorRect) {
+ if (!aScissorRect) {
+ if (mScissored) {
+ mCtx->RSSetState(mRasterizerStateNoScissor);
+ mScissored = false;
+ }
+ return;
+ }
+ D3D11_RECT rect = ToD3D11Rect(aScissorRect.value());
+ mCtx->RSSetScissorRects(1, &rect);
+ if (!mScissored) {
+ mScissored = true;
+ mCtx->RSSetState(mRasterizerStateScissor);
+ }
+}
+
+void MLGDeviceD3D11::SetVertexShader(VertexShaderID aShader) {
+ if (!mVertexShaders[aShader]) {
+ InitVertexShader(aShader);
+ MOZ_ASSERT(mInputLayouts[aShader]);
+ }
+ SetVertexShader(mVertexShaders[aShader]);
+ SetInputLayout(mInputLayouts[aShader]);
+}
+
+void MLGDeviceD3D11::SetInputLayout(ID3D11InputLayout* aLayout) {
+ if (mCurrentInputLayout == aLayout) {
+ return;
+ }
+ mCtx->IASetInputLayout(aLayout);
+ mCurrentInputLayout = aLayout;
+}
+
+void MLGDeviceD3D11::SetVertexShader(ID3D11VertexShader* aShader) {
+ if (mCurrentVertexShader == aShader) {
+ return;
+ }
+ mCtx->VSSetShader(aShader, nullptr, 0);
+ mCurrentVertexShader = aShader;
+}
+
+void MLGDeviceD3D11::SetPixelShader(PixelShaderID aShader) {
+ if (!mPixelShaders[aShader]) {
+ InitPixelShader(aShader);
+ }
+ if (mCurrentPixelShader != mPixelShaders[aShader]) {
+ mCtx->PSSetShader(mPixelShaders[aShader], nullptr, 0);
+ mCurrentPixelShader = mPixelShaders[aShader];
+ }
+}
+
+void MLGDeviceD3D11::SetSamplerMode(uint32_t aIndex, SamplerMode aMode) {
+ ID3D11SamplerState* sampler = mSamplerStates[aMode];
+ mCtx->PSSetSamplers(aIndex, 1, &sampler);
+}
+
+void MLGDeviceD3D11::SetBlendState(MLGBlendState aState) {
+ if (mCurrentBlendState != mBlendStates[aState]) {
+ FLOAT blendFactor[4] = {0, 0, 0, 0};
+ mCtx->OMSetBlendState(mBlendStates[aState], blendFactor, 0xFFFFFFFF);
+ mCurrentBlendState = mBlendStates[aState];
+ }
+}
+
+void MLGDeviceD3D11::SetVertexBuffer(uint32_t aSlot, MLGBuffer* aBuffer,
+ uint32_t aStride, uint32_t aOffset) {
+ ID3D11Buffer* buffer = aBuffer ? aBuffer->AsD3D11()->GetBuffer() : nullptr;
+ mCtx->IASetVertexBuffers(aSlot, 1, &buffer, &aStride, &aOffset);
+}
+
+void MLGDeviceD3D11::SetVSConstantBuffer(uint32_t aSlot, MLGBuffer* aBuffer) {
+ MOZ_ASSERT(aSlot < kMaxVertexShaderConstantBuffers);
+
+ ID3D11Buffer* buffer = aBuffer ? aBuffer->AsD3D11()->GetBuffer() : nullptr;
+ mCtx->VSSetConstantBuffers(aSlot, 1, &buffer);
+}
+
+void MLGDeviceD3D11::SetPSConstantBuffer(uint32_t aSlot, MLGBuffer* aBuffer) {
+ MOZ_ASSERT(aSlot < kMaxPixelShaderConstantBuffers);
+
+ ID3D11Buffer* buffer = aBuffer ? aBuffer->AsD3D11()->GetBuffer() : nullptr;
+ mCtx->PSSetConstantBuffers(aSlot, 1, &buffer);
+}
+
+void MLGDeviceD3D11::SetVSConstantBuffer(uint32_t aSlot, MLGBuffer* aBuffer,
+ uint32_t aFirstConstant,
+ uint32_t aNumConstants) {
+ MOZ_ASSERT(aSlot < kMaxVertexShaderConstantBuffers);
+ MOZ_ASSERT(mCanUseConstantBufferOffsetBinding);
+ MOZ_ASSERT(mCtx1);
+ MOZ_ASSERT(aFirstConstant % 16 == 0);
+
+ ID3D11Buffer* buffer = aBuffer ? aBuffer->AsD3D11()->GetBuffer() : nullptr;
+ mCtx1->VSSetConstantBuffers1(aSlot, 1, &buffer, &aFirstConstant,
+ &aNumConstants);
+}
+
+void MLGDeviceD3D11::SetPSConstantBuffer(uint32_t aSlot, MLGBuffer* aBuffer,
+ uint32_t aFirstConstant,
+ uint32_t aNumConstants) {
+ MOZ_ASSERT(aSlot < kMaxPixelShaderConstantBuffers);
+ MOZ_ASSERT(mCanUseConstantBufferOffsetBinding);
+ MOZ_ASSERT(mCtx1);
+ MOZ_ASSERT(aFirstConstant % 16 == 0);
+
+ ID3D11Buffer* buffer = aBuffer ? aBuffer->AsD3D11()->GetBuffer() : nullptr;
+ mCtx1->PSSetConstantBuffers1(aSlot, 1, &buffer, &aFirstConstant,
+ &aNumConstants);
+}
+
+void MLGDeviceD3D11::SetPrimitiveTopology(MLGPrimitiveTopology aTopology) {
+ D3D11_PRIMITIVE_TOPOLOGY topology;
+ switch (aTopology) {
+ case MLGPrimitiveTopology::TriangleStrip:
+ topology = D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP;
+ break;
+ case MLGPrimitiveTopology::TriangleList:
+ topology = D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST;
+ break;
+ case MLGPrimitiveTopology::UnitQuad:
+ SetVertexBuffer(0, mUnitQuadVB, sizeof(float) * 2, 0);
+ topology = D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP;
+ break;
+ case MLGPrimitiveTopology::UnitTriangle:
+ SetVertexBuffer(0, mUnitTriangleVB, sizeof(float) * 3, 0);
+ topology = D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST;
+ break;
+ default:
+ MOZ_ASSERT_UNREACHABLE("Unknown topology");
+ topology = D3D11_PRIMITIVE_TOPOLOGY_UNDEFINED;
+ break;
+ }
+
+ mCtx->IASetPrimitiveTopology(topology);
+}
+
+RefPtr<MLGBuffer> MLGDeviceD3D11::CreateBuffer(MLGBufferType aType,
+ uint32_t aSize, MLGUsage aUsage,
+ const void* aInitialData) {
+ return MLGBufferD3D11::Create(mDevice, aType, aSize, aUsage, aInitialData);
+}
+
+RefPtr<MLGRenderTarget> MLGDeviceD3D11::CreateRenderTarget(
+ const gfx::IntSize& aSize, MLGRenderTargetFlags aFlags) {
+ RefPtr<MLGRenderTargetD3D11> rt = new MLGRenderTargetD3D11(aSize, aFlags);
+ if (!rt->Initialize(mDevice)) {
+ return nullptr;
+ }
+ return rt;
+}
+
+void MLGDeviceD3D11::Clear(MLGRenderTarget* aRT,
+ const gfx::DeviceColor& aColor) {
+ MLGRenderTargetD3D11* rt = aRT->AsD3D11();
+ FLOAT rgba[4] = {aColor.r, aColor.g, aColor.b, aColor.a};
+ mCtx->ClearRenderTargetView(rt->GetRenderTargetView(), rgba);
+ if (ID3D11DepthStencilView* dsv = rt->GetDSV()) {
+ mCtx->ClearDepthStencilView(dsv, D3D11_CLEAR_DEPTH, 1.0, 0);
+ }
+}
+
+void MLGDeviceD3D11::ClearDepthBuffer(MLGRenderTarget* aRT) {
+ MLGRenderTargetD3D11* rt = aRT->AsD3D11();
+ if (ID3D11DepthStencilView* dsv = rt->GetDSV()) {
+ mCtx->ClearDepthStencilView(dsv, D3D11_CLEAR_DEPTH, 1.0, 0);
+ }
+}
+
+void MLGDeviceD3D11::ClearView(MLGRenderTarget* aRT, const DeviceColor& aColor,
+ const IntRect* aRects, size_t aNumRects) {
+ MOZ_ASSERT(mCanUseClearView);
+ MOZ_ASSERT(mCtx1);
+
+ MLGRenderTargetD3D11* rt = aRT->AsD3D11();
+ FLOAT rgba[4] = {aColor.r, aColor.g, aColor.b, aColor.a};
+
+ StackArray<D3D11_RECT, 8> rects(aNumRects);
+ for (size_t i = 0; i < aNumRects; i++) {
+ rects[i] = ToD3D11Rect(aRects[i]);
+ }
+
+ // Batch ClearView calls since too many will crash NVIDIA drivers.
+ size_t remaining = aNumRects;
+ size_t cursor = 0;
+ while (remaining > 0) {
+ size_t amount = std::min(remaining, kMaxClearViewRects);
+ mCtx1->ClearView(rt->GetRenderTargetView(), rgba, rects.data() + cursor,
+ amount);
+
+ remaining -= amount;
+ cursor += amount;
+ }
+}
+
+void MLGDeviceD3D11::Draw(uint32_t aVertexCount, uint32_t aOffset) {
+ mCtx->Draw(aVertexCount, aOffset);
+}
+
+void MLGDeviceD3D11::DrawInstanced(uint32_t aVertexCountPerInstance,
+ uint32_t aInstanceCount,
+ uint32_t aVertexOffset,
+ uint32_t aInstanceOffset) {
+ mCtx->DrawInstanced(aVertexCountPerInstance, aInstanceCount, aVertexOffset,
+ aInstanceOffset);
+}
+
+void MLGDeviceD3D11::SetPSTextures(uint32_t aSlot, uint32_t aNumTextures,
+ TextureSource* const* aTextures) {
+ // TextureSource guarantees that the ID3D11ShaderResourceView will be cached,
+ // so we don't hold a RefPtr here.
+ StackArray<ID3D11ShaderResourceView*, 3> views(aNumTextures);
+
+ for (size_t i = 0; i < aNumTextures; i++) {
+ views[i] = ResolveTextureSourceForShader(aTextures[i]);
+ }
+
+ mCtx->PSSetShaderResources(aSlot, aNumTextures, views.data());
+}
+
+ID3D11ShaderResourceView* MLGDeviceD3D11::ResolveTextureSourceForShader(
+ TextureSource* aTexture) {
+ if (!aTexture) {
+ return nullptr;
+ }
+
+ if (TextureSourceD3D11* source = aTexture->AsSourceD3D11()) {
+ ID3D11Texture2D* texture = source->GetD3D11Texture();
+ if (!texture) {
+ gfxWarning() << "No D3D11 texture present in SetPSTextures";
+ return nullptr;
+ }
+
+ MaybeLockTexture(texture);
+ return source->GetShaderResourceView();
+ }
+
+ gfxWarning() << "Unknown texture type in SetPSTextures";
+ return nullptr;
+}
+
+void MLGDeviceD3D11::SetPSTexture(uint32_t aSlot, MLGTexture* aTexture) {
+ RefPtr<ID3D11ShaderResourceView> view;
+ if (aTexture) {
+ MLGTextureD3D11* texture = aTexture->AsD3D11();
+ view = texture->GetShaderResourceView();
+ }
+
+ ID3D11ShaderResourceView* viewPtr = view.get();
+ mCtx->PSSetShaderResources(aSlot, 1, &viewPtr);
+}
+
+void MLGDeviceD3D11::MaybeLockTexture(ID3D11Texture2D* aTexture) {
+ RefPtr<IDXGIKeyedMutex> mutex;
+ HRESULT hr = aTexture->QueryInterface(__uuidof(IDXGIKeyedMutex),
+ (void**)getter_AddRefs(mutex));
+ if (FAILED(hr) || !mutex) {
+ return;
+ }
+
+ hr = mutex->AcquireSync(0, 10000);
+
+ if (hr == WAIT_TIMEOUT) {
+ gfxDevCrash(LogReason::D3DLockTimeout) << "D3D lock mutex timeout";
+ mLockAttemptedTextures.PutEntry(mutex);
+ } else if (hr == WAIT_ABANDONED) {
+ gfxCriticalNote << "GFX: D3D11 lock mutex abandoned";
+ mLockAttemptedTextures.PutEntry(mutex);
+ } else if (FAILED(hr)) {
+ gfxCriticalNote << "D3D11 lock mutex failed: " << hexa(hr);
+ mLockAttemptedTextures.PutEntry(mutex);
+ } else {
+ mLockedTextures.PutEntry(mutex);
+ }
+}
+
+void MLGDeviceD3D11::SetPSTexturesNV12(uint32_t aSlot,
+ TextureSource* aTexture) {
+ MOZ_ASSERT(aTexture->GetFormat() == SurfaceFormat::NV12 ||
+ aTexture->GetFormat() == SurfaceFormat::P010 ||
+ aTexture->GetFormat() == SurfaceFormat::P016);
+
+ TextureSourceD3D11* source = aTexture->AsSourceD3D11();
+ if (!source) {
+ gfxWarning() << "Unknown texture type in SetPSCompoundTexture";
+ return;
+ }
+
+ ID3D11Texture2D* texture = source->GetD3D11Texture();
+ if (!texture) {
+ gfxWarning() << "TextureSourceD3D11 does not have an ID3D11Texture";
+ return;
+ }
+
+ MaybeLockTexture(texture);
+
+ const bool isNV12 = aTexture->GetFormat() == SurfaceFormat::NV12;
+
+ RefPtr<ID3D11ShaderResourceView> views[2];
+ D3D11_SHADER_RESOURCE_VIEW_DESC desc = CD3D11_SHADER_RESOURCE_VIEW_DESC(
+ D3D11_SRV_DIMENSION_TEXTURE2D,
+ isNV12 ? DXGI_FORMAT_R8_UNORM : DXGI_FORMAT_R16_UNORM);
+
+ HRESULT hr = mDevice->CreateShaderResourceView(texture, &desc,
+ getter_AddRefs(views[0]));
+ if (FAILED(hr) || !views[0]) {
+ gfxWarning() << "Could not bind an SRV for Y plane of NV12 texture: "
+ << hexa(hr);
+ return;
+ }
+
+ desc.Format = isNV12 ? DXGI_FORMAT_R8G8_UNORM : DXGI_FORMAT_R16G16_UNORM;
+ hr = mDevice->CreateShaderResourceView(texture, &desc,
+ getter_AddRefs(views[1]));
+ if (FAILED(hr) || !views[1]) {
+ gfxWarning() << "Could not bind an SRV for CbCr plane of NV12 texture: "
+ << hexa(hr);
+ return;
+ }
+
+ ID3D11ShaderResourceView* bind[2] = {views[0], views[1]};
+ mCtx->PSSetShaderResources(aSlot, 2, bind);
+}
+
+bool MLGDeviceD3D11::InitSyncObject() {
+ MOZ_ASSERT(!mSyncObject);
+ MOZ_ASSERT(mDevice);
+
+ mSyncObject = SyncObjectHost::CreateSyncObjectHost(mDevice);
+ MOZ_ASSERT(mSyncObject);
+
+ return mSyncObject->Init();
+}
+
+void MLGDeviceD3D11::StartDiagnostics(uint32_t aInvalidPixels) {
+ mDiagnostics->Start(aInvalidPixels);
+}
+
+void MLGDeviceD3D11::EndDiagnostics() { mDiagnostics->End(); }
+
+void MLGDeviceD3D11::GetDiagnostics(GPUStats* aStats) {
+ mDiagnostics->Query(aStats);
+}
+
+bool MLGDeviceD3D11::Synchronize() {
+ MOZ_ASSERT(mSyncObject);
+
+ if (mSyncObject) {
+ if (!mSyncObject->Synchronize()) {
+ // It's timeout or other error. Handle the device-reset here.
+ HandleDeviceReset("SyncObject");
+ return false;
+ }
+ }
+
+ return true;
+}
+
+void MLGDeviceD3D11::UnlockAllTextures() {
+ for (auto iter = mLockedTextures.Iter(); !iter.Done(); iter.Next()) {
+ RefPtr<IDXGIKeyedMutex> mutex = iter.Get()->GetKey();
+ mutex->ReleaseSync(0);
+ }
+ mLockedTextures.Clear();
+ mLockAttemptedTextures.Clear();
+}
+
+void MLGDeviceD3D11::SetDepthTestMode(MLGDepthTestMode aMode) {
+ mCtx->OMSetDepthStencilState(mDepthStencilStates[aMode], 0xffffffff);
+}
+
+void MLGDeviceD3D11::InsertPresentWaitQuery() {
+ CD3D11_QUERY_DESC desc(D3D11_QUERY_EVENT);
+ HRESULT hr =
+ mDevice->CreateQuery(&desc, getter_AddRefs(mNextWaitForPresentQuery));
+ if (FAILED(hr) || !mNextWaitForPresentQuery) {
+ gfxWarning() << "Could not create D3D11_QUERY_EVENT: " << hexa(hr);
+ return;
+ }
+
+ mCtx->End(mNextWaitForPresentQuery);
+}
+
+void MLGDeviceD3D11::WaitForPreviousPresentQuery() {
+ if (mWaitForPresentQuery) {
+ BOOL result;
+ WaitForFrameGPUQuery(mDevice, mCtx, mWaitForPresentQuery, &result);
+ }
+ mWaitForPresentQuery = mNextWaitForPresentQuery.forget();
+}
+
+void MLGDeviceD3D11::Flush() { mCtx->Flush(); }
+
+void MLGDeviceD3D11::EndFrame() {
+ // On our Windows 8 x64 machines, we have observed a driver bug related to
+ // XXSetConstantBuffers1. It appears binding the same buffer to multiple
+ // slots, and potentially leaving slots bound for many frames (as can
+ // happen if we bind a high slot, like for blending), can consistently
+ // cause shaders to read wrong values much later. It is possible there is
+ // a driver bug related to aliasing and partial binding.
+ //
+ // Configuration: GeForce GT 610 (0x104a), Driver 9.18.13.3523, 3-4-2014,
+ // on Windows 8 x64.
+ //
+ // To alleviate this we unbind all buffers at the end of the frame.
+ static ID3D11Buffer* nullBuffers[6] = {
+ nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
+ };
+ MOZ_ASSERT(MOZ_ARRAY_LENGTH(nullBuffers) >= kMaxVertexShaderConstantBuffers);
+ MOZ_ASSERT(MOZ_ARRAY_LENGTH(nullBuffers) >= kMaxPixelShaderConstantBuffers);
+
+ mCtx->VSSetConstantBuffers(0, kMaxVertexShaderConstantBuffers, nullBuffers);
+ mCtx->VSSetConstantBuffers(0, kMaxPixelShaderConstantBuffers, nullBuffers);
+
+ MLGDevice::EndFrame();
+}
+
+void MLGDeviceD3D11::HandleDeviceReset(const char* aWhere) {
+ if (!IsValid()) {
+ return;
+ }
+
+ Fail("FEATURE_FAILURE_DEVICE_RESET"_ns, nullptr);
+
+ gfxCriticalNote << "GFX: D3D11 detected a device reset in " << aWhere;
+ if (XRE_IsGPUProcess()) {
+ GPUParent::GetSingleton()->NotifyDeviceReset();
+ }
+
+ UnmapSharedBuffers();
+ mIsValid = false;
+}
+
+RefPtr<MLGTexture> MLGDeviceD3D11::CreateTexture(const gfx::IntSize& aSize,
+ gfx::SurfaceFormat aFormat,
+ MLGUsage aUsage,
+ MLGTextureFlags aFlags) {
+ return MLGTextureD3D11::Create(mDevice, aSize, aFormat, aUsage, aFlags);
+}
+
+RefPtr<MLGTexture> MLGDeviceD3D11::CreateTexture(TextureSource* aSource) {
+ TextureSourceD3D11* source = aSource->AsSourceD3D11();
+ if (!source) {
+ gfxWarning() << "Attempted to wrap a non-D3D11 texture";
+ return nullptr;
+ }
+ if (!source->GetD3D11Texture()) {
+ return nullptr;
+ }
+ return new MLGTextureD3D11(source->GetD3D11Texture());
+}
+
+void MLGDeviceD3D11::CopyTexture(MLGTexture* aDest,
+ const gfx::IntPoint& aTarget,
+ MLGTexture* aSource,
+ const gfx::IntRect& aRect) {
+ MLGTextureD3D11* dest = aDest->AsD3D11();
+ MLGTextureD3D11* source = aSource->AsD3D11();
+
+ // We check both the source and destination copy regions, because
+ // CopySubresourceRegion is documented as causing a device reset if
+ // the operation is out-of-bounds. And it's not lying.
+ IntRect sourceBounds(IntPoint(0, 0), aSource->GetSize());
+ if (!sourceBounds.Contains(aRect)) {
+ gfxWarning() << "Attempt to read out-of-bounds in CopySubresourceRegion: "
+ << sourceBounds << ", " << aRect;
+ return;
+ }
+
+ IntRect destBounds(IntPoint(0, 0), aDest->GetSize());
+ if (!destBounds.Contains(IntRect(aTarget, aRect.Size()))) {
+ gfxWarning() << "Attempt to write out-of-bounds in CopySubresourceRegion: "
+ << destBounds << ", " << aTarget << ", " << aRect.Size();
+ return;
+ }
+
+ D3D11_BOX box = RectToBox(aRect);
+ mCtx->CopySubresourceRegion(dest->GetTexture(), 0, aTarget.x, aTarget.y, 0,
+ source->GetTexture(), 0, &box);
+}
+
+bool MLGDeviceD3D11::VerifyConstantBufferOffsetting() {
+ RefPtr<ID3D11VertexShader> vs;
+ HRESULT hr = mDevice->CreateVertexShader(sTestConstantBuffersVS.mData,
+ sTestConstantBuffersVS.mLength,
+ nullptr, getter_AddRefs(vs));
+ if (FAILED(hr)) {
+ gfxCriticalNote << "Failed creating vertex shader for buffer test: "
+ << hexa(hr);
+ return false;
+ }
+
+ D3D11_INPUT_ELEMENT_DESC inputDesc[] = {{"POSITION", 0,
+ DXGI_FORMAT_R32G32_FLOAT, 0, 0,
+ D3D11_INPUT_PER_VERTEX_DATA, 0}};
+
+ RefPtr<ID3D11InputLayout> layout;
+ hr = mDevice->CreateInputLayout(
+ inputDesc, sizeof(inputDesc) / sizeof(inputDesc[0]),
+ sTestConstantBuffersVS.mData, sTestConstantBuffersVS.mLength,
+ getter_AddRefs(layout));
+ if (FAILED(hr)) {
+ gfxCriticalNote << "Failed creating input layout for buffer test: "
+ << hexa(hr);
+ return false;
+ }
+
+ RefPtr<MLGRenderTarget> rt =
+ CreateRenderTarget(IntSize(2, 2), MLGRenderTargetFlags::Default);
+ if (!rt) {
+ return false;
+ }
+
+ static const size_t kConstantSize = 4 * sizeof(float);
+ static const size_t kMinConstants = 16;
+ static const size_t kNumBindings = 3;
+
+ RefPtr<MLGBuffer> buffer = CreateBuffer(
+ MLGBufferType::Constant, kConstantSize * kMinConstants * kNumBindings,
+ MLGUsage::Dynamic, nullptr);
+ if (!buffer) {
+ return false;
+ }
+
+ // Populate the buffer. The shader will pick R from buffer 1, G from buffer
+ // 2, and B from buffer 3.
+ {
+ MLGMappedResource map;
+ if (!Map(buffer, MLGMapType::WRITE_DISCARD, &map)) {
+ return false;
+ }
+
+ *reinterpret_cast<DeviceColor*>(map.mData) =
+ DeviceColor(1.0f, 0.2f, 0.3f, 1.0f);
+ *reinterpret_cast<DeviceColor*>(map.mData + kConstantSize * kMinConstants) =
+ DeviceColor(0.4f, 0.0f, 0.5f, 1.0f);
+ *reinterpret_cast<DeviceColor*>(map.mData +
+ (kConstantSize * kMinConstants) * 2) =
+ DeviceColor(0.6f, 0.7f, 1.0f, 1.0f);
+
+ Unmap(buffer);
+ }
+
+ Clear(rt, DeviceColor(0.0f, 0.0f, 0.0f, 1.0f));
+ SetRenderTarget(rt);
+ SetViewport(IntRect(0, 0, 2, 2));
+ SetScissorRect(Nothing());
+ SetBlendState(MLGBlendState::Over);
+
+ SetTopology(MLGPrimitiveTopology::UnitQuad);
+ SetInputLayout(layout);
+ SetVertexShader(vs);
+ SetPixelShader(PixelShaderID::ColoredQuad);
+
+ ID3D11Buffer* buffers[3] = {buffer->AsD3D11()->GetBuffer(),
+ buffer->AsD3D11()->GetBuffer(),
+ buffer->AsD3D11()->GetBuffer()};
+ UINT offsets[3] = {0 * kMinConstants, 1 * kMinConstants, 2 * kMinConstants};
+ UINT counts[3] = {kMinConstants, kMinConstants, kMinConstants};
+
+ mCtx1->VSSetConstantBuffers1(0, 3, buffers, offsets, counts);
+ mCtx->Draw(4, 0);
+
+ // Kill bindings to resources.
+ SetRenderTarget(nullptr);
+
+ ID3D11Buffer* nulls[3] = {nullptr, nullptr, nullptr};
+ mCtx->VSSetConstantBuffers(0, 3, nulls);
+
+ RefPtr<MLGTexture> copy =
+ CreateTexture(IntSize(2, 2), SurfaceFormat::B8G8R8A8, MLGUsage::Staging,
+ MLGTextureFlags::None);
+ if (!copy) {
+ return false;
+ }
+
+ CopyTexture(copy, IntPoint(0, 0), rt->GetTexture(), IntRect(0, 0, 2, 2));
+
+ uint8_t r, g, b, a;
+ {
+ MLGMappedResource map;
+ if (!Map(copy, MLGMapType::READ, &map)) {
+ return false;
+ }
+ r = map.mData[0];
+ g = map.mData[1];
+ b = map.mData[2];
+ a = map.mData[3];
+ Unmap(copy);
+ }
+
+ return r == 255 && g == 0 && b == 255 && a == 255;
+}
+
+static D3D11_BOX RectToBox(const gfx::IntRect& aRect) {
+ D3D11_BOX box;
+ box.front = 0;
+ box.back = 1;
+ box.left = aRect.X();
+ box.top = aRect.Y();
+ box.right = aRect.XMost();
+ box.bottom = aRect.YMost();
+ return box;
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/d3d11/MLGDeviceD3D11.h b/gfx/layers/d3d11/MLGDeviceD3D11.h
new file mode 100644
index 0000000000..2a6aa8ffc7
--- /dev/null
+++ b/gfx/layers/d3d11/MLGDeviceD3D11.h
@@ -0,0 +1,326 @@
+/* -*- 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_layers_d3d11_MLGDeviceD3D11_h
+#define mozilla_gfx_layers_d3d11_MLGDeviceD3D11_h
+
+#include <d3d11_1.h>
+
+#include "mozilla/layers/MLGDevice.h"
+#include "mozilla/layers/SyncObject.h"
+#include "mozilla/EnumeratedArray.h"
+#include "nsTHashtable.h"
+#include "nsPrintfCString.h"
+
+namespace mozilla {
+namespace layers {
+
+struct GPUStats;
+struct ShaderBytes;
+class DiagnosticsD3D11;
+
+class MLGRenderTargetD3D11 final : public MLGRenderTarget {
+ public:
+ MLGRenderTargetD3D11(const gfx::IntSize& aSize, MLGRenderTargetFlags aFlags);
+
+ // Create with a new texture.
+ bool Initialize(ID3D11Device* aDevice);
+
+ // Do not create a texture - use the given one provided, which may be null.
+ // The depth buffer is still initialized.
+ bool Initialize(ID3D11Device* aDevice, ID3D11Texture2D* aTexture);
+
+ gfx::IntSize GetSize() const override;
+ MLGRenderTargetD3D11* AsD3D11() override { return this; }
+ MLGTexture* GetTexture() override;
+
+ // This is exposed only for MLGSwapChainD3D11.
+ bool UpdateTexture(ID3D11Texture2D* aTexture);
+
+ ID3D11DepthStencilView* GetDSV();
+ ID3D11RenderTargetView* GetRenderTargetView();
+
+ private:
+ bool CreateDepthBuffer(ID3D11Device* aDevice);
+ void ForgetTexture();
+
+ private:
+ virtual ~MLGRenderTargetD3D11();
+
+ private:
+ RefPtr<ID3D11Texture2D> mTexture;
+ RefPtr<ID3D11RenderTargetView> mRTView;
+ RefPtr<ID3D11Texture2D> mDepthBuffer;
+ RefPtr<ID3D11DepthStencilView> mDepthStencilView;
+ RefPtr<MLGTexture> mTextureSource;
+ gfx::IntSize mSize;
+};
+
+class MLGSwapChainD3D11 final : public MLGSwapChain {
+ public:
+ static RefPtr<MLGSwapChainD3D11> Create(MLGDeviceD3D11* aParent,
+ ID3D11Device* aDevice,
+ widget::CompositorWidget* aWidget);
+
+ RefPtr<MLGRenderTarget> AcquireBackBuffer() override;
+ gfx::IntSize GetSize() const override;
+ bool ResizeBuffers(const gfx::IntSize& aSize) override;
+ void CopyBackbuffer(gfx::DrawTarget* aTarget,
+ const gfx::IntRect& aBounds) override;
+ void Present() override;
+ void ForcePresent() override;
+ void Destroy() override;
+
+ private:
+ MLGSwapChainD3D11(MLGDeviceD3D11* aParent, ID3D11Device* aDevice);
+ virtual ~MLGSwapChainD3D11();
+
+ bool Initialize(widget::CompositorWidget* aWidget);
+ void UpdateBackBufferContents(ID3D11Texture2D* aBack);
+
+ private:
+ RefPtr<MLGDeviceD3D11> mParent;
+ RefPtr<ID3D11Device> mDevice;
+ RefPtr<IDXGISwapChain> mSwapChain;
+ RefPtr<IDXGISwapChain1> mSwapChain1;
+ RefPtr<MLGRenderTargetD3D11> mRT;
+ widget::CompositorWidget* mWidget;
+ gfx::IntSize mSize;
+ bool mCanUsePartialPresents;
+};
+
+class MLGResourceD3D11 {
+ public:
+ virtual ID3D11Resource* GetResource() const = 0;
+};
+
+class MLGBufferD3D11 final : public MLGBuffer, public MLGResourceD3D11 {
+ public:
+ static RefPtr<MLGBufferD3D11> Create(ID3D11Device* aDevice,
+ MLGBufferType aType, uint32_t aSize,
+ MLGUsage aUsage,
+ const void* aInitialData);
+
+ MLGBufferD3D11* AsD3D11() override { return this; }
+ ID3D11Resource* GetResource() const override { return mBuffer; }
+ ID3D11Buffer* GetBuffer() const { return mBuffer; }
+ MLGResourceD3D11* AsResourceD3D11() override { return this; }
+ size_t GetSize() const override { return mSize; }
+
+ protected:
+ MLGBufferD3D11(ID3D11Buffer* aBuffer, MLGBufferType aType, size_t aSize);
+ virtual ~MLGBufferD3D11();
+
+ private:
+ RefPtr<ID3D11Buffer> mBuffer;
+ MLGBufferType mType;
+ size_t mSize;
+};
+
+class MLGTextureD3D11 final : public MLGTexture, public MLGResourceD3D11 {
+ public:
+ explicit MLGTextureD3D11(ID3D11Texture2D* aTexture);
+
+ static RefPtr<MLGTextureD3D11> Create(ID3D11Device* aDevice,
+ const gfx::IntSize& aSize,
+ gfx::SurfaceFormat aFormat,
+ MLGUsage aUsage,
+ MLGTextureFlags aFlags);
+
+ MLGTextureD3D11* AsD3D11() override { return this; }
+ MLGResourceD3D11* AsResourceD3D11() override { return this; }
+ ID3D11Texture2D* GetTexture() const { return mTexture; }
+ ID3D11Resource* GetResource() const override { return mTexture; }
+ ID3D11ShaderResourceView* GetShaderResourceView();
+
+ private:
+ RefPtr<ID3D11Texture2D> mTexture;
+ RefPtr<ID3D11ShaderResourceView> mView;
+};
+
+class MLGDeviceD3D11 final : public MLGDevice {
+ public:
+ explicit MLGDeviceD3D11(ID3D11Device* aDevice);
+ virtual ~MLGDeviceD3D11();
+
+ bool Initialize() override;
+
+ void StartDiagnostics(uint32_t aInvalidPixels) override;
+ void EndDiagnostics() override;
+ void GetDiagnostics(GPUStats* aStats) override;
+
+ MLGDeviceD3D11* AsD3D11() override { return this; }
+ TextureFactoryIdentifier GetTextureFactoryIdentifier(
+ widget::CompositorWidget* aWidget) const override;
+
+ RefPtr<MLGSwapChain> CreateSwapChainForWidget(
+ widget::CompositorWidget* aWidget) override;
+
+ int32_t GetMaxTextureSize() const override;
+ LayersBackend GetLayersBackend() const override;
+
+ void EndFrame() override;
+
+ bool Map(MLGResource* aResource, MLGMapType aType,
+ MLGMappedResource* aMap) override;
+ void Unmap(MLGResource* aResource) override;
+ void UpdatePartialResource(MLGResource* aResource, const gfx::IntRect* aRect,
+ void* aData, uint32_t aStride) override;
+ void CopyTexture(MLGTexture* aDest, const gfx::IntPoint& aTarget,
+ MLGTexture* aSource, const gfx::IntRect& aRect) override;
+
+ RefPtr<DataTextureSource> CreateDataTextureSource(
+ TextureFlags aFlags) override;
+
+ void SetRenderTarget(MLGRenderTarget* aRT) override;
+ MLGRenderTarget* GetRenderTarget() override;
+ void SetViewport(const gfx::IntRect& aViewport) override;
+ void SetScissorRect(const Maybe<gfx::IntRect>& aScissorRect) override;
+ void SetVertexShader(VertexShaderID aVertexShader) override;
+ void SetPixelShader(PixelShaderID aPixelShader) override;
+ void SetSamplerMode(uint32_t aIndex, SamplerMode aSamplerMode) override;
+ void SetBlendState(MLGBlendState aBlendState) override;
+ void SetVertexBuffer(uint32_t aSlot, MLGBuffer* aBuffer, uint32_t aStride,
+ uint32_t aOffset) override;
+ void SetPSTextures(uint32_t aSlot, uint32_t aNumTextures,
+ TextureSource* const* aTextures) override;
+ void SetPSTexture(uint32_t aSlot, MLGTexture* aTexture) override;
+ void SetPSTexturesNV12(uint32_t aSlot, TextureSource* aTexture) override;
+ void SetPrimitiveTopology(MLGPrimitiveTopology aTopology) override;
+ void SetDepthTestMode(MLGDepthTestMode aMode) override;
+
+ void SetVSConstantBuffer(uint32_t aSlot, MLGBuffer* aBuffer) override;
+ void SetPSConstantBuffer(uint32_t aSlot, MLGBuffer* aBuffer) override;
+ void SetVSConstantBuffer(uint32_t aSlot, MLGBuffer* aBuffer,
+ uint32_t aFirstConstant,
+ uint32_t aNumConstants) override;
+ void SetPSConstantBuffer(uint32_t aSlot, MLGBuffer* aBuffer,
+ uint32_t aFirstConstant,
+ uint32_t aNumConstants) override;
+
+ RefPtr<MLGBuffer> CreateBuffer(MLGBufferType aType, uint32_t aSize,
+ MLGUsage aUsage,
+ const void* aInitialData) override;
+
+ RefPtr<MLGRenderTarget> CreateRenderTarget(
+ const gfx::IntSize& aSize, MLGRenderTargetFlags aFlags) override;
+
+ RefPtr<MLGTexture> CreateTexture(const gfx::IntSize& aSize,
+ gfx::SurfaceFormat aFormat, MLGUsage aUsage,
+ MLGTextureFlags aFlags) override;
+
+ RefPtr<MLGTexture> CreateTexture(TextureSource* aSource) override;
+
+ void Clear(MLGRenderTarget* aRT, const gfx::DeviceColor& aColor) override;
+ void ClearDepthBuffer(MLGRenderTarget* aRT) override;
+ void ClearView(MLGRenderTarget* aRT, const gfx::DeviceColor& aColor,
+ const gfx::IntRect* aRects, size_t aNumRects) override;
+ void Draw(uint32_t aVertexCount, uint32_t aOffset) override;
+ void DrawInstanced(uint32_t aVertexCountPerInstance, uint32_t aInstanceCount,
+ uint32_t aVertexOffset, uint32_t aInstanceOffset) override;
+ void Flush() override;
+
+ // This is exposed for TextureSourceProvider.
+ ID3D11Device* GetD3D11Device() const { return mDevice; }
+
+ bool Synchronize() override;
+ void UnlockAllTextures() override;
+
+ void InsertPresentWaitQuery();
+ void WaitForPreviousPresentQuery();
+ void HandleDeviceReset(const char* aWhere);
+
+ private:
+ bool InitSyncObject();
+
+ void MaybeLockTexture(ID3D11Texture2D* aTexture);
+
+ bool InitPixelShader(PixelShaderID aShaderID);
+ bool InitVertexShader(VertexShaderID aShaderID);
+ bool InitInputLayout(D3D11_INPUT_ELEMENT_DESC* aDesc, size_t aNumElements,
+ const ShaderBytes& aCode, VertexShaderID aShaderID);
+ bool InitRasterizerStates();
+ bool InitSamplerStates();
+ bool InitBlendStates();
+ bool InitDepthStencilState();
+ bool VerifyConstantBufferOffsetting() override;
+
+ void SetInputLayout(ID3D11InputLayout* aLayout);
+ void SetVertexShader(ID3D11VertexShader* aShader);
+
+ // Resolve a TextureSource to an ID3D11ShaderResourceView, locking the
+ // texture if needed. The lock is released at the end of the frame.
+ ID3D11ShaderResourceView* ResolveTextureSourceForShader(
+ TextureSource* aSource);
+
+ private:
+ RefPtr<ID3D11Device> mDevice;
+ RefPtr<ID3D11DeviceContext> mCtx;
+ RefPtr<ID3D11DeviceContext1> mCtx1;
+ UniquePtr<DiagnosticsD3D11> mDiagnostics;
+
+ typedef EnumeratedArray<PixelShaderID, PixelShaderID::MaxShaders,
+ RefPtr<ID3D11PixelShader>>
+ PixelShaderArray;
+ typedef EnumeratedArray<VertexShaderID, VertexShaderID::MaxShaders,
+ RefPtr<ID3D11VertexShader>>
+ VertexShaderArray;
+ typedef EnumeratedArray<VertexShaderID, VertexShaderID::MaxShaders,
+ RefPtr<ID3D11InputLayout>>
+ InputLayoutArray;
+ typedef EnumeratedArray<SamplerMode, SamplerMode::MaxModes,
+ RefPtr<ID3D11SamplerState>>
+ SamplerStateArray;
+ typedef EnumeratedArray<MLGBlendState, MLGBlendState::MaxStates,
+ RefPtr<ID3D11BlendState>>
+ BlendStateArray;
+ typedef EnumeratedArray<MLGDepthTestMode, MLGDepthTestMode::MaxModes,
+ RefPtr<ID3D11DepthStencilState>>
+ DepthStencilStateArray;
+
+ PixelShaderArray mPixelShaders;
+ VertexShaderArray mVertexShaders;
+ InputLayoutArray mInputLayouts;
+ SamplerStateArray mSamplerStates;
+ BlendStateArray mBlendStates;
+ DepthStencilStateArray mDepthStencilStates;
+ RefPtr<ID3D11RasterizerState> mRasterizerStateNoScissor;
+ RefPtr<ID3D11RasterizerState> mRasterizerStateScissor;
+
+ RefPtr<SyncObjectHost> mSyncObject;
+
+ RefPtr<MLGBuffer> mUnitQuadVB;
+ RefPtr<MLGBuffer> mUnitTriangleVB;
+ RefPtr<ID3D11VertexShader> mCurrentVertexShader;
+ RefPtr<ID3D11InputLayout> mCurrentInputLayout;
+ RefPtr<ID3D11PixelShader> mCurrentPixelShader;
+ RefPtr<ID3D11BlendState> mCurrentBlendState;
+
+ RefPtr<ID3D11Query> mWaitForPresentQuery;
+ RefPtr<ID3D11Query> mNextWaitForPresentQuery;
+
+ nsTHashtable<nsRefPtrHashKey<IDXGIKeyedMutex>> mLockedTextures;
+ nsTHashtable<nsRefPtrHashKey<IDXGIKeyedMutex>> mLockAttemptedTextures;
+
+ typedef EnumeratedArray<PixelShaderID, PixelShaderID::MaxShaders,
+ const ShaderBytes*>
+ LazyPixelShaderArray;
+ LazyPixelShaderArray mLazyPixelShaders;
+
+ typedef EnumeratedArray<VertexShaderID, VertexShaderID::MaxShaders,
+ const ShaderBytes*>
+ LazyVertexShaderArray;
+ LazyVertexShaderArray mLazyVertexShaders;
+
+ bool mScissored;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+struct ShaderBytes;
+
+#endif // mozilla_gfx_layers_d3d11_MLGDeviceD3D11_h
diff --git a/gfx/layers/d3d11/ReadbackManagerD3D11.cpp b/gfx/layers/d3d11/ReadbackManagerD3D11.cpp
new file mode 100644
index 0000000000..bfe7c12af6
--- /dev/null
+++ b/gfx/layers/d3d11/ReadbackManagerD3D11.cpp
@@ -0,0 +1,153 @@
+/* -*- 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/. */
+
+#include "ReadbackManagerD3D11.h"
+#include "ReadbackProcessor.h"
+#include "ReadbackLayer.h"
+#include "mozilla/layers/TextureClient.h"
+#include "mozilla/gfx/2D.h"
+
+#include "nsIThread.h"
+#include "nsThreadUtils.h"
+
+namespace mozilla {
+
+using namespace gfx;
+
+namespace layers {
+
+// Structure that contains the information required to execute a readback task,
+// the only member accessed off the main thread here is mReadbackTexture. Since
+// mSink may be released only on the main thread this object should always be
+// destroyed on the main thread!
+struct ReadbackTask {
+ // The texture that we copied the contents of the paintedlayer to.
+ RefPtr<ID3D10Texture2D> mReadbackTexture;
+ // The sink that we're trying to read back to.
+ RefPtr<TextureReadbackSink> mSink;
+};
+
+// This class is created and dispatched from the Readback thread but it must be
+// destroyed by the main thread.
+class ReadbackResultWriterD3D11 final : public nsIRunnable {
+ ~ReadbackResultWriterD3D11() {}
+ NS_DECL_THREADSAFE_ISUPPORTS
+ public:
+ explicit ReadbackResultWriterD3D11(UniquePtr<ReadbackTask>&& aTask)
+ : mTask(std::move(aTask)) {}
+
+ NS_IMETHOD Run() override {
+ D3D10_TEXTURE2D_DESC desc;
+ mTask->mReadbackTexture->GetDesc(&desc);
+
+ D3D10_MAPPED_TEXTURE2D mappedTex;
+ // Unless there is an error this map should succeed immediately, as we've
+ // recently mapped (and unmapped) this copied data on our task thread.
+ HRESULT hr = mTask->mReadbackTexture->Map(0, D3D10_MAP_READ, 0, &mappedTex);
+
+ if (FAILED(hr)) {
+ mTask->mSink->ProcessReadback(nullptr);
+ return NS_OK;
+ }
+
+ {
+ RefPtr<DataSourceSurface> surf = Factory::CreateWrappingDataSourceSurface(
+ (uint8_t*)mappedTex.pData, mappedTex.RowPitch,
+ IntSize(desc.Width, desc.Height), SurfaceFormat::B8G8R8A8);
+
+ mTask->mSink->ProcessReadback(surf);
+
+ MOZ_ASSERT(surf->hasOneRef());
+ }
+
+ mTask->mReadbackTexture->Unmap(0);
+
+ return NS_OK;
+ }
+
+ private:
+ UniquePtr<ReadbackTask> mTask;
+};
+
+NS_IMPL_ISUPPORTS(ReadbackResultWriterD3D11, nsIRunnable)
+
+DWORD WINAPI ReadbackManagerD3D11::StartTaskThread(void* aManager) {
+ static_cast<ReadbackManagerD3D11*>(aManager)->ProcessTasks();
+
+ return 0;
+}
+
+ReadbackManagerD3D11::ReadbackManagerD3D11() : mRefCnt(0) {
+ ::InitializeCriticalSection(&mTaskMutex);
+ mShutdownEvent = ::CreateEventA(nullptr, FALSE, FALSE, nullptr);
+ mTaskSemaphore = ::CreateSemaphoreA(nullptr, 0, 1000000, nullptr);
+ mTaskThread = ::CreateThread(nullptr, 0, StartTaskThread, this, 0, 0);
+}
+
+ReadbackManagerD3D11::~ReadbackManagerD3D11() {
+ ::SetEvent(mShutdownEvent);
+
+ // This shouldn't take longer than 5 seconds, if it does we're going to choose
+ // to leak the thread and its synchronisation in favor of crashing or freezing
+ DWORD result = ::WaitForSingleObject(mTaskThread, 5000);
+ if (result != WAIT_TIMEOUT) {
+ ::DeleteCriticalSection(&mTaskMutex);
+ ::CloseHandle(mShutdownEvent);
+ ::CloseHandle(mTaskSemaphore);
+ ::CloseHandle(mTaskThread);
+ } else {
+ MOZ_CRASH("ReadbackManager: Task thread did not shutdown in 5 seconds.");
+ }
+}
+
+void ReadbackManagerD3D11::PostTask(ID3D10Texture2D* aTexture,
+ TextureReadbackSink* aSink) {
+ auto task = MakeUnique<ReadbackTask>();
+ task->mReadbackTexture = aTexture;
+ task->mSink = aSink;
+
+ ::EnterCriticalSection(&mTaskMutex);
+ mPendingReadbackTasks.AppendElement(std::move(task));
+ ::LeaveCriticalSection(&mTaskMutex);
+
+ ::ReleaseSemaphore(mTaskSemaphore, 1, nullptr);
+}
+
+void ReadbackManagerD3D11::ProcessTasks() {
+ HANDLE handles[] = {mTaskSemaphore, mShutdownEvent};
+
+ while (true) {
+ DWORD result = ::WaitForMultipleObjects(2, handles, FALSE, INFINITE);
+ if (result != WAIT_OBJECT_0) {
+ return;
+ }
+
+ ::EnterCriticalSection(&mTaskMutex);
+ if (mPendingReadbackTasks.Length() == 0) {
+ MOZ_CRASH("Trying to read from an empty array, bad bad bad");
+ }
+ UniquePtr<ReadbackTask> nextReadbackTask =
+ std::move(mPendingReadbackTasks[0]);
+ mPendingReadbackTasks.RemoveElementAt(0);
+ ::LeaveCriticalSection(&mTaskMutex);
+
+ // We want to block here until the texture contents are available, the
+ // easiest thing is to simply map and unmap.
+ D3D10_MAPPED_TEXTURE2D mappedTex;
+ nextReadbackTask->mReadbackTexture->Map(0, D3D10_MAP_READ, 0, &mappedTex);
+ nextReadbackTask->mReadbackTexture->Unmap(0);
+
+ // We can only send the update to the sink on the main thread, so post an
+ // event there to do so. Ownership of the task is passed from
+ // mPendingReadbackTasks to ReadbackResultWriter here.
+ nsCOMPtr<nsIThread> thread = do_GetMainThread();
+ thread->Dispatch(new ReadbackResultWriterD3D11(std::move(nextReadbackTask)),
+ nsIEventTarget::DISPATCH_NORMAL);
+ }
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/d3d11/ReadbackManagerD3D11.h b/gfx/layers/d3d11/ReadbackManagerD3D11.h
new file mode 100644
index 0000000000..00f5dfd467
--- /dev/null
+++ b/gfx/layers/d3d11/ReadbackManagerD3D11.h
@@ -0,0 +1,65 @@
+/* -*- 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 GFX_READBACKMANAGERD3D11_H
+#define GFX_READBACKMANAGERD3D11_H
+
+#include <windows.h>
+#include <d3d10_1.h>
+
+#include "mozilla/UniquePtr.h"
+#include "nsTArray.h"
+
+namespace mozilla {
+namespace layers {
+
+class TextureReadbackSink;
+struct ReadbackTask;
+
+class ReadbackManagerD3D11 final {
+ NS_INLINE_DECL_REFCOUNTING(ReadbackManagerD3D11)
+ public:
+ ReadbackManagerD3D11();
+
+ /**
+ * Tell the readback manager to post a readback task.
+ *
+ * @param aTexture D3D10_USAGE_STAGING texture that will contain the data that
+ * was readback.
+ * @param aSink TextureReadbackSink that the resulting DataSourceSurface
+ * should be dispatched to.
+ */
+ void PostTask(ID3D10Texture2D* aTexture, TextureReadbackSink* aSink);
+
+ private:
+ ~ReadbackManagerD3D11();
+
+ static DWORD WINAPI StartTaskThread(void* aManager);
+
+ void ProcessTasks();
+
+ // The invariant maintained by |mTaskSemaphore| is that the readback thread
+ // will awaken from WaitForMultipleObjects() at least once per readback
+ // task enqueued by the main thread. Since the readback thread processes
+ // exactly one task per wakeup (with one exception), no tasks are lost. The
+ // exception is when the readback thread is shut down, which orphans the
+ // remaining tasks, on purpose.
+ HANDLE mTaskSemaphore;
+ // Event signaled when the task thread should shutdown
+ HANDLE mShutdownEvent;
+ // Handle to the task thread
+ HANDLE mTaskThread;
+
+ // FiFo list of readback tasks that are to be executed. Access is synchronized
+ // by mTaskMutex.
+ CRITICAL_SECTION mTaskMutex;
+ nsTArray<UniquePtr<ReadbackTask>> mPendingReadbackTasks;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif /* GFX_READBACKMANAGERD3D11_H */
diff --git a/gfx/layers/d3d11/ShaderDefinitionsD3D11.h b/gfx/layers/d3d11/ShaderDefinitionsD3D11.h
new file mode 100644
index 0000000000..1e15cf7987
--- /dev/null
+++ b/gfx/layers/d3d11/ShaderDefinitionsD3D11.h
@@ -0,0 +1,40 @@
+/* -*- 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_layers_d3d11_ShaderDefinitionsD3D11_h
+#define mozilla_gfx_layers_d3d11_ShaderDefinitionsD3D11_h
+
+#include "mozilla/gfx/Rect.h"
+
+namespace mozilla {
+namespace layers {
+
+struct VertexShaderConstants {
+ float layerTransform[4][4];
+ float projection[4][4];
+ float renderTargetOffset[4];
+ gfx::Rect textureCoords;
+ gfx::Rect layerQuad;
+ float maskTransform[4][4];
+ float backdropTransform[4][4];
+};
+
+struct PixelShaderConstants {
+ float layerColor[4];
+ float layerOpacity[4];
+ int blendConfig[4];
+ float vCoefficient[4];
+ float yuvColorMatrix[3][4];
+};
+
+struct Vertex {
+ float position[2];
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // mozilla_gfx_layers_d3d11_ShaderDefinitionsD3D11_h
diff --git a/gfx/layers/d3d11/TextureD3D11.cpp b/gfx/layers/d3d11/TextureD3D11.cpp
new file mode 100644
index 0000000000..7acfd61544
--- /dev/null
+++ b/gfx/layers/d3d11/TextureD3D11.cpp
@@ -0,0 +1,1867 @@
+/* -*- 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/. */
+
+#include "TextureD3D11.h"
+
+#include "CompositorD3D11.h"
+#include "Effects.h"
+#include "MainThreadUtils.h"
+#include "PaintThread.h"
+#include "ReadbackManagerD3D11.h"
+#include "gfx2DGlue.h"
+#include "gfxContext.h"
+#include "gfxWindowsPlatform.h"
+#include "mozilla/StaticPrefs_gfx.h"
+#include "mozilla/Telemetry.h"
+#include "mozilla/gfx/DataSurfaceHelpers.h"
+#include "mozilla/gfx/DeviceManagerDx.h"
+#include "mozilla/gfx/Logging.h"
+#include "mozilla/gfx/gfxVars.h"
+#include "mozilla/layers/CompositorBridgeChild.h"
+#include "mozilla/webrender/RenderD3D11TextureHost.h"
+#include "mozilla/webrender/RenderThread.h"
+#include "mozilla/webrender/WebRenderAPI.h"
+
+namespace mozilla {
+
+using namespace gfx;
+
+namespace layers {
+
+static const GUID sD3D11TextureUsage = {
+ 0xd89275b0,
+ 0x6c7d,
+ 0x4038,
+ {0xb5, 0xfa, 0x4d, 0x87, 0x16, 0xd5, 0xcc, 0x4e}};
+
+/* This class gets its lifetime tied to a D3D texture
+ * and increments memory usage on construction and decrements
+ * on destruction */
+class TextureMemoryMeasurer final : public IUnknown {
+ public:
+ explicit TextureMemoryMeasurer(size_t aMemoryUsed) {
+ mMemoryUsed = aMemoryUsed;
+ gfxWindowsPlatform::sD3D11SharedTextures += mMemoryUsed;
+ mRefCnt = 0;
+ }
+ STDMETHODIMP_(ULONG) AddRef() {
+ mRefCnt++;
+ return mRefCnt;
+ }
+ STDMETHODIMP QueryInterface(REFIID riid, void** ppvObject) {
+ IUnknown* punk = nullptr;
+ if (riid == IID_IUnknown) {
+ punk = this;
+ }
+ *ppvObject = punk;
+ if (punk) {
+ punk->AddRef();
+ return S_OK;
+ } else {
+ return E_NOINTERFACE;
+ }
+ }
+
+ STDMETHODIMP_(ULONG) Release() {
+ int refCnt = --mRefCnt;
+ if (refCnt == 0) {
+ gfxWindowsPlatform::sD3D11SharedTextures -= mMemoryUsed;
+ delete this;
+ }
+ return refCnt;
+ }
+
+ private:
+ int mRefCnt;
+ int mMemoryUsed;
+
+ ~TextureMemoryMeasurer() = default;
+};
+
+static DXGI_FORMAT SurfaceFormatToDXGIFormat(gfx::SurfaceFormat aFormat) {
+ switch (aFormat) {
+ case SurfaceFormat::B8G8R8A8:
+ return DXGI_FORMAT_B8G8R8A8_UNORM;
+ case SurfaceFormat::B8G8R8X8:
+ return DXGI_FORMAT_B8G8R8A8_UNORM;
+ case SurfaceFormat::R8G8B8A8:
+ return DXGI_FORMAT_R8G8B8A8_UNORM;
+ case SurfaceFormat::R8G8B8X8:
+ return DXGI_FORMAT_R8G8B8A8_UNORM;
+ case SurfaceFormat::A8:
+ return DXGI_FORMAT_R8_UNORM;
+ case SurfaceFormat::A16:
+ return DXGI_FORMAT_R16_UNORM;
+ default:
+ MOZ_ASSERT(false, "unsupported format");
+ return DXGI_FORMAT_UNKNOWN;
+ }
+}
+
+void ReportTextureMemoryUsage(ID3D11Texture2D* aTexture, size_t aBytes) {
+ aTexture->SetPrivateDataInterface(sD3D11TextureUsage,
+ new TextureMemoryMeasurer(aBytes));
+}
+
+static uint32_t GetRequiredTilesD3D11(uint32_t aSize, uint32_t aMaxSize) {
+ uint32_t requiredTiles = aSize / aMaxSize;
+ if (aSize % aMaxSize) {
+ requiredTiles++;
+ }
+ return requiredTiles;
+}
+
+static IntRect GetTileRectD3D11(uint32_t aID, IntSize aSize,
+ uint32_t aMaxSize) {
+ uint32_t horizontalTiles = GetRequiredTilesD3D11(aSize.width, aMaxSize);
+ uint32_t verticalTiles = GetRequiredTilesD3D11(aSize.height, aMaxSize);
+
+ uint32_t verticalTile = aID / horizontalTiles;
+ uint32_t horizontalTile = aID % horizontalTiles;
+
+ return IntRect(
+ horizontalTile * aMaxSize, verticalTile * aMaxSize,
+ horizontalTile < (horizontalTiles - 1) ? aMaxSize
+ : aSize.width % aMaxSize,
+ verticalTile < (verticalTiles - 1) ? aMaxSize : aSize.height % aMaxSize);
+}
+
+AutoTextureLock::AutoTextureLock(IDXGIKeyedMutex* aMutex, HRESULT& aResult,
+ uint32_t aTimeout) {
+ mMutex = aMutex;
+ if (mMutex) {
+ mResult = mMutex->AcquireSync(0, aTimeout);
+ aResult = mResult;
+ } else {
+ aResult = E_INVALIDARG;
+ }
+}
+
+AutoTextureLock::~AutoTextureLock() {
+ if (mMutex && !FAILED(mResult) && mResult != WAIT_TIMEOUT &&
+ mResult != WAIT_ABANDONED) {
+ mMutex->ReleaseSync(0);
+ }
+}
+
+ID3D11ShaderResourceView* TextureSourceD3D11::GetShaderResourceView() {
+ MOZ_ASSERT(mTexture == GetD3D11Texture(),
+ "You need to override GetShaderResourceView if you're overriding "
+ "GetD3D11Texture!");
+
+ if (!mSRV && mTexture) {
+ RefPtr<ID3D11Device> device;
+ mTexture->GetDevice(getter_AddRefs(device));
+
+ // see comment in CompositingRenderTargetD3D11 constructor
+ CD3D11_SHADER_RESOURCE_VIEW_DESC srvDesc(D3D11_SRV_DIMENSION_TEXTURE2D,
+ mFormatOverride);
+ D3D11_SHADER_RESOURCE_VIEW_DESC* desc =
+ mFormatOverride == DXGI_FORMAT_UNKNOWN ? nullptr : &srvDesc;
+
+ HRESULT hr =
+ device->CreateShaderResourceView(mTexture, desc, getter_AddRefs(mSRV));
+ if (FAILED(hr)) {
+ gfxCriticalNote << "[D3D11] TextureSourceD3D11:GetShaderResourceView "
+ "CreateSRV failure "
+ << gfx::hexa(hr);
+ return nullptr;
+ }
+ }
+ return mSRV;
+}
+
+DataTextureSourceD3D11::DataTextureSourceD3D11(ID3D11Device* aDevice,
+ SurfaceFormat aFormat,
+ TextureFlags aFlags)
+ : mDevice(aDevice),
+ mFormat(aFormat),
+ mFlags(aFlags),
+ mCurrentTile(0),
+ mIsTiled(false),
+ mIterating(false),
+ mAllowTextureUploads(true) {}
+
+DataTextureSourceD3D11::DataTextureSourceD3D11(ID3D11Device* aDevice,
+ SurfaceFormat aFormat,
+ ID3D11Texture2D* aTexture)
+ : mDevice(aDevice),
+ mFormat(aFormat),
+ mFlags(TextureFlags::NO_FLAGS),
+ mCurrentTile(0),
+ mIsTiled(false),
+ mIterating(false),
+ mAllowTextureUploads(false) {
+ mTexture = aTexture;
+ D3D11_TEXTURE2D_DESC desc;
+ aTexture->GetDesc(&desc);
+
+ mSize = IntSize(desc.Width, desc.Height);
+}
+
+DataTextureSourceD3D11::DataTextureSourceD3D11(gfx::SurfaceFormat aFormat,
+ TextureSourceProvider* aProvider,
+ ID3D11Texture2D* aTexture)
+ : DataTextureSourceD3D11(aProvider->GetD3D11Device(), aFormat, aTexture) {}
+
+DataTextureSourceD3D11::DataTextureSourceD3D11(gfx::SurfaceFormat aFormat,
+ TextureSourceProvider* aProvider,
+ TextureFlags aFlags)
+ : DataTextureSourceD3D11(aProvider->GetD3D11Device(), aFormat, aFlags) {}
+
+DataTextureSourceD3D11::~DataTextureSourceD3D11() {}
+
+template <typename T> // ID3D10Texture2D or ID3D11Texture2D
+static bool LockD3DTexture(T* aTexture) {
+ MOZ_ASSERT(aTexture);
+ RefPtr<IDXGIKeyedMutex> mutex;
+ aTexture->QueryInterface((IDXGIKeyedMutex**)getter_AddRefs(mutex));
+ // Textures created by the DXVA decoders don't have a mutex for
+ // synchronization
+ if (mutex) {
+ HRESULT hr = mutex->AcquireSync(0, 10000);
+ if (hr == WAIT_TIMEOUT) {
+ gfxDevCrash(LogReason::D3DLockTimeout) << "D3D lock mutex timeout";
+ } else if (hr == WAIT_ABANDONED) {
+ gfxCriticalNote << "GFX: D3D11 lock mutex abandoned";
+ }
+
+ if (FAILED(hr)) {
+ NS_WARNING("Failed to lock the texture");
+ return false;
+ }
+ }
+ return true;
+}
+
+template <typename T>
+static bool HasKeyedMutex(T* aTexture) {
+ RefPtr<IDXGIKeyedMutex> mutex;
+ aTexture->QueryInterface((IDXGIKeyedMutex**)getter_AddRefs(mutex));
+ return !!mutex;
+}
+
+template <typename T> // ID3D10Texture2D or ID3D11Texture2D
+static void UnlockD3DTexture(T* aTexture) {
+ MOZ_ASSERT(aTexture);
+ RefPtr<IDXGIKeyedMutex> mutex;
+ aTexture->QueryInterface((IDXGIKeyedMutex**)getter_AddRefs(mutex));
+ if (mutex) {
+ HRESULT hr = mutex->ReleaseSync(0);
+ if (FAILED(hr)) {
+ NS_WARNING("Failed to unlock the texture");
+ }
+ }
+}
+
+D3D11TextureData::D3D11TextureData(ID3D11Texture2D* aTexture,
+ gfx::IntSize aSize,
+ gfx::SurfaceFormat aFormat,
+ TextureAllocationFlags aFlags)
+ : mSize(aSize),
+ mFormat(aFormat),
+ mNeedsClear(aFlags & ALLOC_CLEAR_BUFFER),
+ mNeedsClearWhite(aFlags & ALLOC_CLEAR_BUFFER_WHITE),
+ mIsForOutOfBandContent(aFlags & ALLOC_FOR_OUT_OF_BAND_CONTENT),
+ mTexture(aTexture),
+ mAllocationFlags(aFlags) {
+ MOZ_ASSERT(aTexture);
+ mHasSynchronization = HasKeyedMutex(aTexture);
+}
+
+static void DestroyDrawTarget(RefPtr<DrawTarget>& aDT,
+ RefPtr<ID3D11Texture2D>& aTexture) {
+ // An Azure DrawTarget needs to be locked when it gets nullptr'ed as this is
+ // when it calls EndDraw. This EndDraw should not execute anything so it
+ // shouldn't -really- need the lock but the debug layer chokes on this.
+ LockD3DTexture(aTexture.get());
+ aDT = nullptr;
+ UnlockD3DTexture(aTexture.get());
+ aTexture = nullptr;
+}
+
+D3D11TextureData::~D3D11TextureData() {
+ if (mDrawTarget) {
+ DestroyDrawTarget(mDrawTarget, mTexture);
+ }
+}
+
+bool D3D11TextureData::Lock(OpenMode aMode) {
+ if (!LockD3DTexture(mTexture.get())) {
+ return false;
+ }
+
+ if (NS_IsMainThread() && !mIsForOutOfBandContent) {
+ if (!PrepareDrawTargetInLock(aMode)) {
+ Unlock();
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool D3D11TextureData::PrepareDrawTargetInLock(OpenMode aMode) {
+ // Make sure that successful write-lock means we will have a DrawTarget to
+ // write into.
+ if (!mDrawTarget &&
+ (aMode & OpenMode::OPEN_WRITE || mNeedsClear || mNeedsClearWhite)) {
+ mDrawTarget = BorrowDrawTarget();
+ if (!mDrawTarget) {
+ return false;
+ }
+ }
+
+ // Reset transform
+ mDrawTarget->SetTransform(Matrix());
+
+ if (mNeedsClear) {
+ mDrawTarget->ClearRect(Rect(0, 0, mSize.width, mSize.height));
+ mNeedsClear = false;
+ }
+ if (mNeedsClearWhite) {
+ mDrawTarget->FillRect(Rect(0, 0, mSize.width, mSize.height),
+ ColorPattern(DeviceColor(1.0, 1.0, 1.0, 1.0)));
+ mNeedsClearWhite = false;
+ }
+
+ return true;
+}
+
+void D3D11TextureData::Unlock() { UnlockD3DTexture(mTexture.get()); }
+
+void D3D11TextureData::FillInfo(TextureData::Info& aInfo) const {
+ aInfo.size = mSize;
+ aInfo.format = mFormat;
+ aInfo.supportsMoz2D = true;
+ aInfo.hasIntermediateBuffer = false;
+ aInfo.hasSynchronization = mHasSynchronization;
+}
+
+void D3D11TextureData::SyncWithObject(RefPtr<SyncObjectClient> aSyncObject) {
+ if (!aSyncObject || mHasSynchronization) {
+ // When we have per texture synchronization we sync using the keyed mutex.
+ return;
+ }
+
+ MOZ_ASSERT(aSyncObject->GetSyncType() == SyncObjectClient::SyncType::D3D11);
+ SyncObjectD3D11Client* sync =
+ static_cast<SyncObjectD3D11Client*>(aSyncObject.get());
+ sync->RegisterTexture(mTexture);
+}
+
+bool D3D11TextureData::SerializeSpecific(
+ SurfaceDescriptorD3D10* const aOutDesc) {
+ RefPtr<IDXGIResource> resource;
+ GetDXGIResource((IDXGIResource**)getter_AddRefs(resource));
+ if (!resource) {
+ return false;
+ }
+ HANDLE sharedHandle;
+ HRESULT hr = resource->GetSharedHandle(&sharedHandle);
+ if (FAILED(hr)) {
+ LOGD3D11("Error getting shared handle for texture.");
+ return false;
+ }
+
+ *aOutDesc = SurfaceDescriptorD3D10((WindowsHandle)sharedHandle, mFormat,
+ mSize, mYUVColorSpace, mColorRange);
+ return true;
+}
+
+bool D3D11TextureData::Serialize(SurfaceDescriptor& aOutDescriptor) {
+ SurfaceDescriptorD3D10 desc;
+ if (!SerializeSpecific(&desc)) return false;
+
+ aOutDescriptor = std::move(desc);
+ return true;
+}
+
+void D3D11TextureData::GetSubDescriptor(
+ RemoteDecoderVideoSubDescriptor* const aOutDesc) {
+ SurfaceDescriptorD3D10 ret;
+ if (!SerializeSpecific(&ret)) return;
+
+ *aOutDesc = std::move(ret);
+}
+
+D3D11TextureData* D3D11TextureData::Create(IntSize aSize, SurfaceFormat aFormat,
+ TextureAllocationFlags aFlags,
+ ID3D11Device* aDevice) {
+ return Create(aSize, aFormat, nullptr, aFlags, aDevice);
+}
+
+D3D11TextureData* D3D11TextureData::Create(SourceSurface* aSurface,
+ TextureAllocationFlags aFlags,
+ ID3D11Device* aDevice) {
+ return Create(aSurface->GetSize(), aSurface->GetFormat(), aSurface, aFlags,
+ aDevice);
+}
+
+D3D11TextureData* D3D11TextureData::Create(IntSize aSize, SurfaceFormat aFormat,
+ SourceSurface* aSurface,
+ TextureAllocationFlags aFlags,
+ ID3D11Device* aDevice) {
+ if (aFormat == SurfaceFormat::A8) {
+ // Currently we don't support A8 surfaces. Fallback.
+ return nullptr;
+ }
+
+ // Just grab any device. We never use the immediate context, so the devices
+ // are fine to use from any thread.
+ RefPtr<ID3D11Device> device = aDevice;
+ if (!device) {
+ device = DeviceManagerDx::Get()->GetContentDevice();
+ if (!device) {
+ return nullptr;
+ }
+ }
+
+ CD3D11_TEXTURE2D_DESC newDesc(
+ DXGI_FORMAT_B8G8R8A8_UNORM, aSize.width, aSize.height, 1, 1,
+ D3D11_BIND_RENDER_TARGET | D3D11_BIND_SHADER_RESOURCE);
+
+ if (aFormat == SurfaceFormat::NV12) {
+ newDesc.Format = DXGI_FORMAT_NV12;
+ } else if (aFormat == SurfaceFormat::P010) {
+ newDesc.Format = DXGI_FORMAT_P010;
+ } else if (aFormat == SurfaceFormat::P016) {
+ newDesc.Format = DXGI_FORMAT_P016;
+ }
+
+ newDesc.MiscFlags = D3D11_RESOURCE_MISC_SHARED;
+ if (!NS_IsMainThread() || !!(aFlags & ALLOC_FOR_OUT_OF_BAND_CONTENT)) {
+ // On the main thread we use the syncobject to handle synchronization.
+ if (!(aFlags & ALLOC_MANUAL_SYNCHRONIZATION)) {
+ newDesc.MiscFlags = D3D11_RESOURCE_MISC_SHARED_KEYEDMUTEX;
+ }
+ }
+
+ if (aSurface && newDesc.MiscFlags == D3D11_RESOURCE_MISC_SHARED_KEYEDMUTEX &&
+ !DeviceManagerDx::Get()->CanInitializeKeyedMutexTextures()) {
+ return nullptr;
+ }
+
+ D3D11_SUBRESOURCE_DATA uploadData;
+ D3D11_SUBRESOURCE_DATA* uploadDataPtr = nullptr;
+ RefPtr<DataSourceSurface> srcSurf;
+ DataSourceSurface::MappedSurface sourceMap;
+
+ if (aSurface) {
+ srcSurf = aSurface->GetDataSurface();
+
+ if (!srcSurf) {
+ gfxCriticalError()
+ << "Failed to GetDataSurface in D3D11TextureData::Create";
+ return nullptr;
+ }
+
+ if (!srcSurf->Map(DataSourceSurface::READ, &sourceMap)) {
+ gfxCriticalError()
+ << "Failed to map source surface for D3D11TextureData::Create";
+ return nullptr;
+ }
+ }
+
+ if (srcSurf && !DeviceManagerDx::Get()->HasCrashyInitData()) {
+ uploadData.pSysMem = sourceMap.mData;
+ uploadData.SysMemPitch = sourceMap.mStride;
+ uploadData.SysMemSlicePitch = 0; // unused
+
+ uploadDataPtr = &uploadData;
+ }
+
+ // See bug 1397040
+ RefPtr<ID3D10Multithread> mt;
+ device->QueryInterface((ID3D10Multithread**)getter_AddRefs(mt));
+
+ RefPtr<ID3D11Texture2D> texture11;
+
+ {
+ D3D11MTAutoEnter lock(mt.forget());
+
+ HRESULT hr = device->CreateTexture2D(&newDesc, uploadDataPtr,
+ getter_AddRefs(texture11));
+
+ if (FAILED(hr) || !texture11) {
+ gfxCriticalNote << "[D3D11] 2 CreateTexture2D failure Size: " << aSize
+ << "texture11: " << texture11
+ << " Code: " << gfx::hexa(hr);
+ return nullptr;
+ }
+
+ if (srcSurf && DeviceManagerDx::Get()->HasCrashyInitData()) {
+ D3D11_BOX box;
+ box.front = box.top = box.left = 0;
+ box.back = 1;
+ box.right = aSize.width;
+ box.bottom = aSize.height;
+ RefPtr<ID3D11DeviceContext> ctx;
+ device->GetImmediateContext(getter_AddRefs(ctx));
+ ctx->UpdateSubresource(texture11, 0, &box, sourceMap.mData,
+ sourceMap.mStride, 0);
+ }
+ }
+
+ if (srcSurf) {
+ srcSurf->Unmap();
+ }
+
+ // If we created the texture with a keyed mutex, then we expect all operations
+ // on it to be synchronized using it. If we did an initial upload using
+ // aSurface then bizarely this isn't covered, so we insert a manual
+ // lock/unlock pair to force this.
+ if (aSurface && newDesc.MiscFlags == D3D11_RESOURCE_MISC_SHARED_KEYEDMUTEX) {
+ if (!LockD3DTexture(texture11.get())) {
+ return nullptr;
+ }
+ UnlockD3DTexture(texture11.get());
+ }
+ texture11->SetPrivateDataInterface(
+ sD3D11TextureUsage,
+ new TextureMemoryMeasurer(newDesc.Width * newDesc.Height * 4));
+ return new D3D11TextureData(texture11, aSize, aFormat, aFlags);
+}
+
+void D3D11TextureData::Deallocate(LayersIPCChannel* aAllocator) {
+ mDrawTarget = nullptr;
+ mTexture = nullptr;
+}
+
+TextureData* D3D11TextureData::CreateSimilar(
+ LayersIPCChannel* aAllocator, LayersBackend aLayersBackend,
+ TextureFlags aFlags, TextureAllocationFlags aAllocFlags) const {
+ return D3D11TextureData::Create(mSize, mFormat, aAllocFlags);
+}
+
+void D3D11TextureData::GetDXGIResource(IDXGIResource** aOutResource) {
+ mTexture->QueryInterface(aOutResource);
+}
+
+TextureFlags D3D11TextureData::GetTextureFlags() const {
+ TextureFlags flags = TextureFlags::NO_FLAGS;
+ // With WebRender, resource open happens asynchronously on RenderThread.
+ // During opening the resource on host side, TextureClient needs to be alive.
+ // With WAIT_HOST_USAGE_END, keep TextureClient alive during host side usage.
+ if (gfx::gfxVars::UseWebRender()) {
+ flags |= TextureFlags::WAIT_HOST_USAGE_END;
+ }
+ return flags;
+}
+
+DXGIYCbCrTextureData* 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,
+ YUVColorSpace aYUVColorSpace, gfx::ColorRange aColorRange) {
+ if (!aHandleY || !aHandleCb || !aHandleCr || !aTextureY || !aTextureCb ||
+ !aTextureCr) {
+ return nullptr;
+ }
+
+ DXGIYCbCrTextureData* texture = new DXGIYCbCrTextureData();
+ texture->mHandles[0] = aHandleY;
+ texture->mHandles[1] = aHandleCb;
+ texture->mHandles[2] = aHandleCr;
+ texture->mD3D9Textures[0] = aTextureY;
+ texture->mD3D9Textures[1] = aTextureCb;
+ texture->mD3D9Textures[2] = aTextureCr;
+ texture->mSize = aSize;
+ texture->mSizeY = aSizeY;
+ texture->mSizeCbCr = aSizeCbCr;
+ texture->mColorDepth = aColorDepth;
+ texture->mYUVColorSpace = aYUVColorSpace;
+ texture->mColorRange = aColorRange;
+
+ return texture;
+}
+
+DXGIYCbCrTextureData* DXGIYCbCrTextureData::Create(
+ ID3D11Texture2D* aTextureY, ID3D11Texture2D* aTextureCb,
+ ID3D11Texture2D* aTextureCr, const gfx::IntSize& aSize,
+ const gfx::IntSize& aSizeY, const gfx::IntSize& aSizeCbCr,
+ gfx::ColorDepth aColorDepth, YUVColorSpace aYUVColorSpace,
+ gfx::ColorRange aColorRange) {
+ if (!aTextureY || !aTextureCb || !aTextureCr) {
+ return nullptr;
+ }
+
+ aTextureY->SetPrivateDataInterface(
+ sD3D11TextureUsage,
+ new TextureMemoryMeasurer(aSizeY.width * aSizeY.height));
+ aTextureCb->SetPrivateDataInterface(
+ sD3D11TextureUsage,
+ new TextureMemoryMeasurer(aSizeCbCr.width * aSizeCbCr.height));
+ aTextureCr->SetPrivateDataInterface(
+ sD3D11TextureUsage,
+ new TextureMemoryMeasurer(aSizeCbCr.width * aSizeCbCr.height));
+
+ RefPtr<IDXGIResource> resource;
+
+ aTextureY->QueryInterface((IDXGIResource**)getter_AddRefs(resource));
+
+ HANDLE handleY;
+ HRESULT hr = resource->GetSharedHandle(&handleY);
+ if (FAILED(hr)) {
+ return nullptr;
+ }
+
+ aTextureCb->QueryInterface((IDXGIResource**)getter_AddRefs(resource));
+
+ HANDLE handleCb;
+ hr = resource->GetSharedHandle(&handleCb);
+ if (FAILED(hr)) {
+ return nullptr;
+ }
+
+ aTextureCr->QueryInterface((IDXGIResource**)getter_AddRefs(resource));
+ HANDLE handleCr;
+ hr = resource->GetSharedHandle(&handleCr);
+ if (FAILED(hr)) {
+ return nullptr;
+ }
+
+ DXGIYCbCrTextureData* texture = new DXGIYCbCrTextureData();
+ texture->mHandles[0] = handleY;
+ texture->mHandles[1] = handleCb;
+ texture->mHandles[2] = handleCr;
+ texture->mD3D11Textures[0] = aTextureY;
+ texture->mD3D11Textures[1] = aTextureCb;
+ texture->mD3D11Textures[2] = aTextureCr;
+ texture->mSize = aSize;
+ texture->mSizeY = aSizeY;
+ texture->mSizeCbCr = aSizeCbCr;
+ texture->mColorDepth = aColorDepth;
+ texture->mYUVColorSpace = aYUVColorSpace;
+ texture->mColorRange = aColorRange;
+
+ return texture;
+}
+
+void DXGIYCbCrTextureData::FillInfo(TextureData::Info& aInfo) const {
+ aInfo.size = mSize;
+ aInfo.format = gfx::SurfaceFormat::YUV;
+ aInfo.supportsMoz2D = false;
+ aInfo.hasIntermediateBuffer = false;
+ aInfo.hasSynchronization = false;
+}
+
+void DXGIYCbCrTextureData::SerializeSpecific(
+ SurfaceDescriptorDXGIYCbCr* const aOutDesc) {
+ *aOutDesc = SurfaceDescriptorDXGIYCbCr(
+ (WindowsHandle)mHandles[0], (WindowsHandle)mHandles[1],
+ (WindowsHandle)mHandles[2], mSize, mSizeY, mSizeCbCr, mColorDepth,
+ mYUVColorSpace, mColorRange);
+}
+
+bool DXGIYCbCrTextureData::Serialize(SurfaceDescriptor& aOutDescriptor) {
+ SurfaceDescriptorDXGIYCbCr desc;
+ SerializeSpecific(&desc);
+
+ aOutDescriptor = std::move(desc);
+ return true;
+}
+
+void DXGIYCbCrTextureData::GetSubDescriptor(
+ RemoteDecoderVideoSubDescriptor* const aOutDesc) {
+ SurfaceDescriptorDXGIYCbCr desc;
+ SerializeSpecific(&desc);
+
+ *aOutDesc = std::move(desc);
+}
+
+void DXGIYCbCrTextureData::Deallocate(LayersIPCChannel*) {
+ mD3D9Textures[0] = nullptr;
+ mD3D9Textures[1] = nullptr;
+ mD3D9Textures[2] = nullptr;
+ mD3D11Textures[0] = nullptr;
+ mD3D11Textures[1] = nullptr;
+ mD3D11Textures[2] = nullptr;
+}
+
+TextureFlags DXGIYCbCrTextureData::GetTextureFlags() const {
+ TextureFlags flags = TextureFlags::DEALLOCATE_MAIN_THREAD;
+ // With WebRender, resource open happens asynchronously on RenderThread.
+ // During opening the resource on host side, TextureClient needs to be alive.
+ // With WAIT_HOST_USAGE_END, keep TextureClient alive during host side usage.
+ if (gfx::gfxVars::UseWebRender()) {
+ flags |= TextureFlags::WAIT_HOST_USAGE_END;
+ }
+ return flags;
+}
+
+already_AddRefed<TextureHost> CreateTextureHostD3D11(
+ const SurfaceDescriptor& aDesc, ISurfaceAllocator* aDeallocator,
+ LayersBackend aBackend, TextureFlags aFlags) {
+ RefPtr<TextureHost> result;
+ switch (aDesc.type()) {
+ case SurfaceDescriptor::TSurfaceDescriptorD3D10: {
+ result =
+ new DXGITextureHostD3D11(aFlags, aDesc.get_SurfaceDescriptorD3D10());
+ break;
+ }
+ case SurfaceDescriptor::TSurfaceDescriptorDXGIYCbCr: {
+ result = new DXGIYCbCrTextureHostD3D11(
+ aFlags, aDesc.get_SurfaceDescriptorDXGIYCbCr());
+ break;
+ }
+ default: {
+ MOZ_ASSERT_UNREACHABLE("Unsupported SurfaceDescriptor type");
+ }
+ }
+ return result.forget();
+}
+
+already_AddRefed<DrawTarget> D3D11TextureData::BorrowDrawTarget() {
+ MOZ_ASSERT(NS_IsMainThread() || PaintThread::IsOnPaintThread() ||
+ NS_IsInCanvasThreadOrWorker());
+
+ if (!mDrawTarget && mTexture) {
+ // This may return a null DrawTarget
+ mDrawTarget = Factory::CreateDrawTargetForD3D11Texture(mTexture, mFormat);
+ if (!mDrawTarget) {
+ gfxCriticalNote << "Could not borrow DrawTarget (D3D11) " << (int)mFormat;
+ }
+ }
+
+ RefPtr<DrawTarget> result = mDrawTarget;
+ return result.forget();
+}
+
+bool D3D11TextureData::UpdateFromSurface(gfx::SourceSurface* aSurface) {
+ // Supporting texture updates after creation requires an ID3D11DeviceContext
+ // and those aren't threadsafe. We'd need to either lock, or have a device for
+ // whatever thread this runs on and we're trying to avoid extra devices (bug
+ // 1284672).
+ MOZ_ASSERT(false,
+ "UpdateFromSurface not supported for D3D11! Use CreateFromSurface "
+ "instead");
+ return false;
+}
+
+DXGITextureHostD3D11::DXGITextureHostD3D11(
+ TextureFlags aFlags, const SurfaceDescriptorD3D10& aDescriptor)
+ : TextureHost(aFlags),
+ mSize(aDescriptor.size()),
+ mHandle(aDescriptor.handle()),
+ mFormat(aDescriptor.format()),
+ mYUVColorSpace(aDescriptor.yUVColorSpace()),
+ mColorRange(aDescriptor.colorRange()),
+ mIsLocked(false) {}
+
+bool DXGITextureHostD3D11::EnsureTexture() {
+ RefPtr<ID3D11Device> device;
+ if (mTexture) {
+ mTexture->GetDevice(getter_AddRefs(device));
+ if (device == DeviceManagerDx::Get()->GetCompositorDevice()) {
+ NS_WARNING("Incompatible texture.");
+ return true;
+ }
+ mTexture = nullptr;
+ }
+
+ device = GetDevice();
+ if (!device || device != DeviceManagerDx::Get()->GetCompositorDevice()) {
+ NS_WARNING("No device or incompatible device.");
+ return false;
+ }
+
+ HRESULT hr = device->OpenSharedResource(
+ (HANDLE)mHandle, __uuidof(ID3D11Texture2D),
+ (void**)(ID3D11Texture2D**)getter_AddRefs(mTexture));
+ if (FAILED(hr)) {
+ MOZ_ASSERT(false, "Failed to open shared texture");
+ return false;
+ }
+
+ D3D11_TEXTURE2D_DESC desc;
+ mTexture->GetDesc(&desc);
+ mSize = IntSize(desc.Width, desc.Height);
+ return true;
+}
+
+RefPtr<ID3D11Device> DXGITextureHostD3D11::GetDevice() {
+ if (mFlags & TextureFlags::INVALID_COMPOSITOR) {
+ return nullptr;
+ }
+
+ if (mProvider) {
+ return mProvider->GetD3D11Device();
+ } else {
+ return mDevice;
+ }
+}
+
+void DXGITextureHostD3D11::SetTextureSourceProvider(
+ TextureSourceProvider* aProvider) {
+ if (!aProvider || !aProvider->GetD3D11Device()) {
+ mDevice = nullptr;
+ mProvider = nullptr;
+ mTextureSource = nullptr;
+ return;
+ }
+
+ if (mDevice && (aProvider->GetD3D11Device() != mDevice)) {
+ if (mTextureSource) {
+ mTextureSource->Reset();
+ }
+ mTextureSource = nullptr;
+ return;
+ }
+
+ mProvider = aProvider;
+ mDevice = aProvider->GetD3D11Device();
+
+ if (mTextureSource) {
+ mTextureSource->SetTextureSourceProvider(aProvider);
+ }
+}
+
+bool DXGITextureHostD3D11::Lock() {
+ if (!mProvider) {
+ // Make an early return here if we call SetTextureSourceProvider() with an
+ // incompatible compositor. This check tries to prevent the problem where we
+ // use that incompatible compositor to compose this texture.
+ return false;
+ }
+
+ return LockInternal();
+}
+
+bool DXGITextureHostD3D11::LockWithoutCompositor() {
+ // Unlike the normal Lock() function, this function may be called when
+ // mProvider is nullptr such as during WebVR frame submission. So, there is
+ // no 'mProvider' checking here.
+ if (!mDevice) {
+ mDevice = DeviceManagerDx::Get()->GetCompositorDevice();
+ }
+ return LockInternal();
+}
+
+void DXGITextureHostD3D11::Unlock() { UnlockInternal(); }
+
+void DXGITextureHostD3D11::UnlockWithoutCompositor() { UnlockInternal(); }
+
+bool DXGITextureHostD3D11::LockInternal() {
+ if (!GetDevice()) {
+ NS_WARNING("trying to lock a TextureHost without a D3D device");
+ return false;
+ }
+
+ if (!EnsureTextureSource()) {
+ return false;
+ }
+
+ mIsLocked = LockD3DTexture(mTextureSource->GetD3D11Texture());
+
+ return mIsLocked;
+}
+
+already_AddRefed<gfx::DataSourceSurface> DXGITextureHostD3D11::GetAsSurface() {
+ if (!gfxVars::UseWebRender()) {
+ return nullptr;
+ }
+
+ switch (GetFormat()) {
+ case gfx::SurfaceFormat::R8G8B8X8:
+ case gfx::SurfaceFormat::R8G8B8A8:
+ case gfx::SurfaceFormat::B8G8R8A8:
+ case gfx::SurfaceFormat::B8G8R8X8:
+ break;
+ default: {
+ MOZ_ASSERT_UNREACHABLE("DXGITextureHostD3D11: unsupported format!");
+ return nullptr;
+ }
+ }
+
+ AutoLockTextureHostWithoutCompositor autoLock(this);
+ if (autoLock.Failed()) {
+ NS_WARNING("Failed to lock the D3DTexture");
+ return nullptr;
+ }
+
+ RefPtr<ID3D11Device> device;
+ mTexture->GetDevice(getter_AddRefs(device));
+
+ D3D11_TEXTURE2D_DESC textureDesc = {0};
+ mTexture->GetDesc(&textureDesc);
+
+ RefPtr<ID3D11DeviceContext> context;
+ device->GetImmediateContext(getter_AddRefs(context));
+
+ textureDesc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
+ textureDesc.Usage = D3D11_USAGE_STAGING;
+ textureDesc.BindFlags = 0;
+ textureDesc.MiscFlags = 0;
+ textureDesc.MipLevels = 1;
+ RefPtr<ID3D11Texture2D> cpuTexture;
+ HRESULT hr = device->CreateTexture2D(&textureDesc, nullptr,
+ getter_AddRefs(cpuTexture));
+ if (FAILED(hr)) {
+ return nullptr;
+ }
+
+ context->CopyResource(cpuTexture, mTexture);
+
+ D3D11_MAPPED_SUBRESOURCE mappedSubresource;
+ hr = context->Map(cpuTexture, 0, D3D11_MAP_READ, 0, &mappedSubresource);
+ if (FAILED(hr)) {
+ return nullptr;
+ }
+
+ RefPtr<DataSourceSurface> surf = gfx::CreateDataSourceSurfaceFromData(
+ IntSize(textureDesc.Width, textureDesc.Height), GetFormat(),
+ (uint8_t*)mappedSubresource.pData, mappedSubresource.RowPitch);
+ context->Unmap(cpuTexture, 0);
+ return surf.forget();
+}
+
+bool DXGITextureHostD3D11::EnsureTextureSource() {
+ if (mTextureSource) {
+ return true;
+ }
+
+ if (!EnsureTexture()) {
+ DeviceManagerDx::Get()->ForceDeviceReset(
+ ForcedDeviceResetReason::OPENSHAREDHANDLE);
+ return false;
+ }
+
+ if (mProvider) {
+ if (!mProvider->IsValid()) {
+ return false;
+ }
+ mTextureSource = new DataTextureSourceD3D11(mFormat, mProvider, mTexture);
+ } else {
+ mTextureSource = new DataTextureSourceD3D11(mDevice, mFormat, mTexture);
+ }
+ return true;
+}
+
+void DXGITextureHostD3D11::UnlockInternal() {
+ UnlockD3DTexture(mTextureSource->GetD3D11Texture());
+}
+
+bool DXGITextureHostD3D11::BindTextureSource(
+ CompositableTextureSourceRef& aTexture) {
+ MOZ_ASSERT(mIsLocked);
+ // If Lock was successful we must have a valid TextureSource.
+ MOZ_ASSERT(mTextureSource);
+ return AcquireTextureSource(aTexture);
+}
+
+bool DXGITextureHostD3D11::AcquireTextureSource(
+ CompositableTextureSourceRef& aTexture) {
+ if (!EnsureTextureSource()) {
+ return false;
+ }
+ aTexture = mTextureSource;
+ return true;
+}
+
+void DXGITextureHostD3D11::CreateRenderTexture(
+ const wr::ExternalImageId& aExternalImageId) {
+ RefPtr<wr::RenderTextureHost> texture = new wr::RenderDXGITextureHost(
+ mHandle, mFormat, mYUVColorSpace, mColorRange, mSize);
+
+ wr::RenderThread::Get()->RegisterExternalImage(wr::AsUint64(aExternalImageId),
+ texture.forget());
+}
+
+uint32_t DXGITextureHostD3D11::NumSubTextures() {
+ switch (GetFormat()) {
+ case gfx::SurfaceFormat::R8G8B8X8:
+ case gfx::SurfaceFormat::R8G8B8A8:
+ case gfx::SurfaceFormat::B8G8R8A8:
+ case gfx::SurfaceFormat::B8G8R8X8: {
+ return 1;
+ }
+ case gfx::SurfaceFormat::NV12:
+ case gfx::SurfaceFormat::P010:
+ case gfx::SurfaceFormat::P016: {
+ return 2;
+ }
+ default: {
+ MOZ_ASSERT_UNREACHABLE("unexpected format");
+ return 1;
+ }
+ }
+}
+
+void DXGITextureHostD3D11::PushResourceUpdates(
+ wr::TransactionBuilder& aResources, ResourceUpdateOp aOp,
+ const Range<wr::ImageKey>& aImageKeys, const wr::ExternalImageId& aExtID) {
+ if (!gfx::gfxVars::UseWebRenderANGLE()) {
+ MOZ_ASSERT_UNREACHABLE("unexpected to be called without ANGLE");
+ return;
+ }
+
+ MOZ_ASSERT(mHandle);
+ auto method = aOp == TextureHost::ADD_IMAGE
+ ? &wr::TransactionBuilder::AddExternalImage
+ : &wr::TransactionBuilder::UpdateExternalImage;
+ switch (mFormat) {
+ case gfx::SurfaceFormat::R8G8B8X8:
+ case gfx::SurfaceFormat::R8G8B8A8:
+ case gfx::SurfaceFormat::B8G8R8A8:
+ case gfx::SurfaceFormat::B8G8R8X8: {
+ MOZ_ASSERT(aImageKeys.length() == 1);
+
+ wr::ImageDescriptor descriptor(mSize, GetFormat());
+ auto imageType = gfx::gfxVars::UseSoftwareWebRender()
+ ? wr::ExternalImageType::TextureHandle(
+ wr::ImageBufferKind::TextureRect)
+ : wr::ExternalImageType::TextureHandle(
+ wr::ImageBufferKind::TextureExternal);
+ (aResources.*method)(aImageKeys[0], descriptor, aExtID, imageType, 0);
+ break;
+ }
+ case gfx::SurfaceFormat::P010:
+ case gfx::SurfaceFormat::P016:
+ case gfx::SurfaceFormat::NV12: {
+ MOZ_ASSERT(aImageKeys.length() == 2);
+ MOZ_ASSERT(mSize.width % 2 == 0);
+ MOZ_ASSERT(mSize.height % 2 == 0);
+
+ wr::ImageDescriptor descriptor0(mSize, mFormat == gfx::SurfaceFormat::NV12
+ ? gfx::SurfaceFormat::A8
+ : gfx::SurfaceFormat::A16);
+ wr::ImageDescriptor descriptor1(mSize / 2,
+ mFormat == gfx::SurfaceFormat::NV12
+ ? gfx::SurfaceFormat::R8G8
+ : gfx::SurfaceFormat::R16G16);
+ auto imageType = gfx::gfxVars::UseSoftwareWebRender()
+ ? wr::ExternalImageType::TextureHandle(
+ wr::ImageBufferKind::TextureRect)
+ : wr::ExternalImageType::TextureHandle(
+ wr::ImageBufferKind::TextureExternal);
+ (aResources.*method)(aImageKeys[0], descriptor0, aExtID, imageType, 0);
+ (aResources.*method)(aImageKeys[1], descriptor1, aExtID, imageType, 1);
+ break;
+ }
+ default: {
+ MOZ_ASSERT_UNREACHABLE("unexpected to be called");
+ }
+ }
+}
+
+void DXGITextureHostD3D11::PushDisplayItems(
+ wr::DisplayListBuilder& aBuilder, const wr::LayoutRect& aBounds,
+ const wr::LayoutRect& aClip, wr::ImageRendering aFilter,
+ const Range<wr::ImageKey>& aImageKeys, PushDisplayItemFlagSet aFlags) {
+ bool preferCompositorSurface =
+ aFlags.contains(PushDisplayItemFlag::PREFER_COMPOSITOR_SURFACE);
+ if (!gfx::gfxVars::UseWebRenderANGLE()) {
+ MOZ_ASSERT_UNREACHABLE("unexpected to be called without ANGLE");
+ return;
+ }
+
+ switch (GetFormat()) {
+ case gfx::SurfaceFormat::R8G8B8X8:
+ case gfx::SurfaceFormat::R8G8B8A8:
+ case gfx::SurfaceFormat::B8G8R8A8:
+ case gfx::SurfaceFormat::B8G8R8X8: {
+ MOZ_ASSERT(aImageKeys.length() == 1);
+ aBuilder.PushImage(aBounds, aClip, true, aFilter, aImageKeys[0],
+ !(mFlags & TextureFlags::NON_PREMULTIPLIED),
+ wr::ColorF{1.0f, 1.0f, 1.0f, 1.0f},
+ preferCompositorSurface,
+ SupportsExternalCompositing());
+ break;
+ }
+ case gfx::SurfaceFormat::P010:
+ case gfx::SurfaceFormat::P016:
+ case gfx::SurfaceFormat::NV12: {
+ MOZ_ASSERT(aImageKeys.length() == 2);
+ aBuilder.PushNV12Image(
+ aBounds, aClip, true, aImageKeys[0], aImageKeys[1],
+ GetFormat() == gfx::SurfaceFormat::NV12 ? wr::ColorDepth::Color8
+ : wr::ColorDepth::Color16,
+ wr::ToWrYuvColorSpace(mYUVColorSpace),
+ wr::ToWrColorRange(mColorRange), aFilter, preferCompositorSurface,
+ SupportsExternalCompositing());
+ break;
+ }
+ default: {
+ MOZ_ASSERT_UNREACHABLE("unexpected to be called");
+ }
+ }
+}
+
+bool DXGITextureHostD3D11::SupportsExternalCompositing() {
+ if (gfx::gfxVars::UseSoftwareWebRender()) {
+ return true;
+ }
+ // XXX Add P010 and P016 support.
+ if (GetFormat() == gfx::SurfaceFormat::NV12 &&
+ gfx::gfxVars::UseWebRenderDCompVideoOverlayWin()) {
+ return true;
+ }
+ return false;
+}
+
+DXGIYCbCrTextureHostD3D11::DXGIYCbCrTextureHostD3D11(
+ TextureFlags aFlags, const SurfaceDescriptorDXGIYCbCr& aDescriptor)
+ : TextureHost(aFlags),
+ mSize(aDescriptor.size()),
+ mSizeY(aDescriptor.sizeY()),
+ mSizeCbCr(aDescriptor.sizeCbCr()),
+ mIsLocked(false),
+ mColorDepth(aDescriptor.colorDepth()),
+ mYUVColorSpace(aDescriptor.yUVColorSpace()),
+ mColorRange(aDescriptor.colorRange()) {
+ mHandles[0] = aDescriptor.handleY();
+ mHandles[1] = aDescriptor.handleCb();
+ mHandles[2] = aDescriptor.handleCr();
+}
+
+bool DXGIYCbCrTextureHostD3D11::EnsureTexture() {
+ RefPtr<ID3D11Device> device;
+ if (mTextures[0]) {
+ mTextures[0]->GetDevice(getter_AddRefs(device));
+ if (device == DeviceManagerDx::Get()->GetCompositorDevice()) {
+ NS_WARNING("Incompatible texture.");
+ return true;
+ }
+ mTextures[0] = nullptr;
+ mTextures[1] = nullptr;
+ mTextures[2] = nullptr;
+ }
+
+ if (!GetDevice() ||
+ GetDevice() != DeviceManagerDx::Get()->GetCompositorDevice()) {
+ NS_WARNING("No device or incompatible device.");
+ return false;
+ }
+
+ device = GetDevice();
+ RefPtr<ID3D11Texture2D> textures[3];
+
+ HRESULT hr = device->OpenSharedResource(
+ (HANDLE)mHandles[0], __uuidof(ID3D11Texture2D),
+ (void**)(ID3D11Texture2D**)getter_AddRefs(textures[0]));
+ if (FAILED(hr)) {
+ NS_WARNING("Failed to open shared texture for Y Plane");
+ return false;
+ }
+
+ hr = device->OpenSharedResource(
+ (HANDLE)mHandles[1], __uuidof(ID3D11Texture2D),
+ (void**)(ID3D11Texture2D**)getter_AddRefs(textures[1]));
+ if (FAILED(hr)) {
+ NS_WARNING("Failed to open shared texture for Cb Plane");
+ return false;
+ }
+
+ hr = device->OpenSharedResource(
+ (HANDLE)mHandles[2], __uuidof(ID3D11Texture2D),
+ (void**)(ID3D11Texture2D**)getter_AddRefs(textures[2]));
+ if (FAILED(hr)) {
+ NS_WARNING("Failed to open shared texture for Cr Plane");
+ return false;
+ }
+
+ mTextures[0] = textures[0].forget();
+ mTextures[1] = textures[1].forget();
+ mTextures[2] = textures[2].forget();
+
+ return true;
+}
+
+RefPtr<ID3D11Device> DXGIYCbCrTextureHostD3D11::GetDevice() {
+ if (mFlags & TextureFlags::INVALID_COMPOSITOR) {
+ return nullptr;
+ }
+
+ return mProvider->GetD3D11Device();
+}
+
+void DXGIYCbCrTextureHostD3D11::SetTextureSourceProvider(
+ TextureSourceProvider* aProvider) {
+ if (!aProvider || !aProvider->GetD3D11Device()) {
+ mProvider = nullptr;
+ mTextureSources[0] = nullptr;
+ mTextureSources[1] = nullptr;
+ mTextureSources[2] = nullptr;
+ return;
+ }
+
+ mProvider = aProvider;
+
+ if (mTextureSources[0]) {
+ mTextureSources[0]->SetTextureSourceProvider(aProvider);
+ }
+}
+
+bool DXGIYCbCrTextureHostD3D11::Lock() {
+ if (!EnsureTextureSource()) {
+ return false;
+ }
+
+ mIsLocked = LockD3DTexture(mTextureSources[0]->GetD3D11Texture()) &&
+ LockD3DTexture(mTextureSources[1]->GetD3D11Texture()) &&
+ LockD3DTexture(mTextureSources[2]->GetD3D11Texture());
+
+ return mIsLocked;
+}
+
+bool DXGIYCbCrTextureHostD3D11::EnsureTextureSource() {
+ if (!mProvider) {
+ NS_WARNING("no suitable compositor");
+ return false;
+ }
+
+ if (!GetDevice()) {
+ NS_WARNING("trying to lock a TextureHost without a D3D device");
+ return false;
+ }
+ if (!mTextureSources[0]) {
+ if (!EnsureTexture()) {
+ return false;
+ }
+
+ MOZ_ASSERT(mTextures[1] && mTextures[2]);
+
+ mTextureSources[0] =
+ new DataTextureSourceD3D11(SurfaceFormat::A8, mProvider, mTextures[0]);
+ mTextureSources[1] =
+ new DataTextureSourceD3D11(SurfaceFormat::A8, mProvider, mTextures[1]);
+ mTextureSources[2] =
+ new DataTextureSourceD3D11(SurfaceFormat::A8, mProvider, mTextures[2]);
+ mTextureSources[0]->SetNextSibling(mTextureSources[1]);
+ mTextureSources[1]->SetNextSibling(mTextureSources[2]);
+ }
+ return true;
+}
+
+void DXGIYCbCrTextureHostD3D11::Unlock() {
+ MOZ_ASSERT(mIsLocked);
+ UnlockD3DTexture(mTextureSources[0]->GetD3D11Texture());
+ UnlockD3DTexture(mTextureSources[1]->GetD3D11Texture());
+ UnlockD3DTexture(mTextureSources[2]->GetD3D11Texture());
+ mIsLocked = false;
+}
+
+bool DXGIYCbCrTextureHostD3D11::BindTextureSource(
+ CompositableTextureSourceRef& aTexture) {
+ MOZ_ASSERT(mIsLocked);
+ // If Lock was successful we must have a valid TextureSource.
+ MOZ_ASSERT(mTextureSources[0] && mTextureSources[1] && mTextureSources[2]);
+ aTexture = mTextureSources[0].get();
+ return !!aTexture;
+}
+
+void DXGIYCbCrTextureHostD3D11::CreateRenderTexture(
+ const wr::ExternalImageId& aExternalImageId) {
+ RefPtr<wr::RenderTextureHost> texture = new wr::RenderDXGIYCbCrTextureHost(
+ mHandles, mYUVColorSpace, mColorDepth, mColorRange, mSizeY, mSizeCbCr);
+
+ wr::RenderThread::Get()->RegisterExternalImage(wr::AsUint64(aExternalImageId),
+ texture.forget());
+}
+
+uint32_t DXGIYCbCrTextureHostD3D11::NumSubTextures() {
+ // ycbcr use 3 sub textures.
+ return 3;
+}
+
+void DXGIYCbCrTextureHostD3D11::PushResourceUpdates(
+ wr::TransactionBuilder& aResources, ResourceUpdateOp aOp,
+ const Range<wr::ImageKey>& aImageKeys, const wr::ExternalImageId& aExtID) {
+ if (!gfx::gfxVars::UseWebRenderANGLE()) {
+ MOZ_ASSERT_UNREACHABLE("unexpected to be called without ANGLE");
+ return;
+ }
+
+ MOZ_ASSERT(mHandles[0] && mHandles[1] && mHandles[2]);
+ MOZ_ASSERT(aImageKeys.length() == 3);
+ // Assume the chroma planes are rounded up if the luma plane is odd sized.
+ MOZ_ASSERT((mSizeCbCr.width == mSizeY.width ||
+ mSizeCbCr.width == (mSizeY.width + 1) >> 1) &&
+ (mSizeCbCr.height == mSizeY.height ||
+ mSizeCbCr.height == (mSizeY.height + 1) >> 1));
+
+ auto method = aOp == TextureHost::ADD_IMAGE
+ ? &wr::TransactionBuilder::AddExternalImage
+ : &wr::TransactionBuilder::UpdateExternalImage;
+ auto imageType = gfx::gfxVars::UseSoftwareWebRender()
+ ? wr::ExternalImageType::TextureHandle(
+ wr::ImageBufferKind::TextureRect)
+ : wr::ExternalImageType::TextureHandle(
+ wr::ImageBufferKind::TextureExternal);
+
+ // y
+ wr::ImageDescriptor descriptor0(mSizeY, gfx::SurfaceFormat::A8);
+ // cb and cr
+ wr::ImageDescriptor descriptor1(mSizeCbCr, gfx::SurfaceFormat::A8);
+ (aResources.*method)(aImageKeys[0], descriptor0, aExtID, imageType, 0);
+ (aResources.*method)(aImageKeys[1], descriptor1, aExtID, imageType, 1);
+ (aResources.*method)(aImageKeys[2], descriptor1, aExtID, imageType, 2);
+}
+
+void DXGIYCbCrTextureHostD3D11::PushDisplayItems(
+ wr::DisplayListBuilder& aBuilder, const wr::LayoutRect& aBounds,
+ const wr::LayoutRect& aClip, wr::ImageRendering aFilter,
+ const Range<wr::ImageKey>& aImageKeys, PushDisplayItemFlagSet aFlags) {
+ if (!gfx::gfxVars::UseWebRenderANGLE()) {
+ MOZ_ASSERT_UNREACHABLE("unexpected to be called without ANGLE");
+ return;
+ }
+
+ MOZ_ASSERT(aImageKeys.length() == 3);
+
+ aBuilder.PushYCbCrPlanarImage(
+ aBounds, aClip, true, aImageKeys[0], aImageKeys[1], aImageKeys[2],
+ wr::ToWrColorDepth(mColorDepth), wr::ToWrYuvColorSpace(mYUVColorSpace),
+ wr::ToWrColorRange(mColorRange), aFilter,
+ aFlags.contains(PushDisplayItemFlag::PREFER_COMPOSITOR_SURFACE),
+ SupportsExternalCompositing());
+}
+
+bool DXGIYCbCrTextureHostD3D11::SupportsExternalCompositing() {
+ if (gfx::gfxVars::UseSoftwareWebRender()) {
+ return true;
+ }
+ return false;
+}
+
+bool DXGIYCbCrTextureHostD3D11::AcquireTextureSource(
+ CompositableTextureSourceRef& aTexture) {
+ if (!EnsureTextureSource()) {
+ return false;
+ }
+ aTexture = mTextureSources[0].get();
+ return !!aTexture;
+}
+
+bool DataTextureSourceD3D11::Update(DataSourceSurface* aSurface,
+ nsIntRegion* aDestRegion,
+ IntPoint* aSrcOffset) {
+ // Incremental update with a source offset is only used on Mac so it is not
+ // clear that we ever will need to support it for D3D.
+ MOZ_ASSERT(!aSrcOffset);
+ MOZ_ASSERT(aSurface);
+
+ MOZ_ASSERT(mAllowTextureUploads);
+ if (!mAllowTextureUploads) {
+ return false;
+ }
+
+ HRESULT hr;
+
+ if (!mDevice) {
+ return false;
+ }
+
+ uint32_t bpp = BytesPerPixel(aSurface->GetFormat());
+ DXGI_FORMAT dxgiFormat = SurfaceFormatToDXGIFormat(aSurface->GetFormat());
+
+ mSize = aSurface->GetSize();
+ mFormat = aSurface->GetFormat();
+
+ CD3D11_TEXTURE2D_DESC desc(dxgiFormat, mSize.width, mSize.height, 1, 1);
+
+ int32_t maxSize = GetMaxTextureSizeFromDevice(mDevice);
+ if ((mSize.width <= maxSize && mSize.height <= maxSize) ||
+ (mFlags & TextureFlags::DISALLOW_BIGIMAGE)) {
+ if (mTexture) {
+ D3D11_TEXTURE2D_DESC currentDesc;
+ mTexture->GetDesc(&currentDesc);
+
+ // Make sure there's no size mismatch, if there is, recreate.
+ if (currentDesc.Width != mSize.width ||
+ currentDesc.Height != mSize.height ||
+ currentDesc.Format != dxgiFormat) {
+ mTexture = nullptr;
+ // Make sure we upload the whole surface.
+ aDestRegion = nullptr;
+ }
+ }
+
+ nsIntRegion* regionToUpdate = aDestRegion;
+ if (!mTexture) {
+ hr = mDevice->CreateTexture2D(&desc, nullptr, getter_AddRefs(mTexture));
+ mIsTiled = false;
+ if (FAILED(hr) || !mTexture) {
+ Reset();
+ return false;
+ }
+
+ if (mFlags & TextureFlags::COMPONENT_ALPHA) {
+ regionToUpdate = nullptr;
+ }
+ }
+
+ DataSourceSurface::MappedSurface map;
+ if (!aSurface->Map(DataSourceSurface::MapType::READ, &map)) {
+ gfxCriticalError() << "Failed to map surface.";
+ Reset();
+ return false;
+ }
+
+ RefPtr<ID3D11DeviceContext> context;
+ mDevice->GetImmediateContext(getter_AddRefs(context));
+
+ if (regionToUpdate) {
+ for (auto iter = regionToUpdate->RectIter(); !iter.Done(); iter.Next()) {
+ const IntRect& rect = iter.Get();
+ D3D11_BOX box;
+ box.front = 0;
+ box.back = 1;
+ box.left = rect.X();
+ box.top = rect.Y();
+ box.right = rect.XMost();
+ box.bottom = rect.YMost();
+
+ void* data = map.mData + map.mStride * rect.Y() +
+ BytesPerPixel(aSurface->GetFormat()) * rect.X();
+
+ context->UpdateSubresource(mTexture, 0, &box, data, map.mStride,
+ map.mStride * rect.Height());
+ }
+ } else {
+ context->UpdateSubresource(mTexture, 0, nullptr, map.mData, map.mStride,
+ map.mStride * mSize.height);
+ }
+
+ aSurface->Unmap();
+ } else {
+ mIsTiled = true;
+ uint32_t tileCount = GetRequiredTilesD3D11(mSize.width, maxSize) *
+ GetRequiredTilesD3D11(mSize.height, maxSize);
+
+ mTileTextures.resize(tileCount);
+ mTileSRVs.resize(tileCount);
+ mTexture = nullptr;
+
+ DataSourceSurface::ScopedMap map(aSurface, DataSourceSurface::READ);
+ if (!map.IsMapped()) {
+ gfxCriticalError() << "Failed to map surface.";
+ Reset();
+ return false;
+ }
+
+ for (uint32_t i = 0; i < tileCount; i++) {
+ IntRect tileRect = GetTileRect(i);
+
+ desc.Width = tileRect.Width();
+ desc.Height = tileRect.Height();
+ desc.Usage = D3D11_USAGE_IMMUTABLE;
+
+ D3D11_SUBRESOURCE_DATA initData;
+ initData.pSysMem =
+ map.GetData() + tileRect.Y() * map.GetStride() + tileRect.X() * bpp;
+ initData.SysMemPitch = map.GetStride();
+
+ hr = mDevice->CreateTexture2D(&desc, &initData,
+ getter_AddRefs(mTileTextures[i]));
+ if (FAILED(hr) || !mTileTextures[i]) {
+ Reset();
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
+ID3D11Texture2D* DataTextureSourceD3D11::GetD3D11Texture() const {
+ return mIterating ? mTileTextures[mCurrentTile] : mTexture;
+}
+
+RefPtr<TextureSource> DataTextureSourceD3D11::ExtractCurrentTile() {
+ MOZ_ASSERT(mIterating);
+ return new DataTextureSourceD3D11(mDevice, mFormat,
+ mTileTextures[mCurrentTile]);
+}
+
+ID3D11ShaderResourceView* DataTextureSourceD3D11::GetShaderResourceView() {
+ if (mIterating) {
+ if (!mTileSRVs[mCurrentTile]) {
+ if (!mTileTextures[mCurrentTile]) {
+ return nullptr;
+ }
+
+ RefPtr<ID3D11Device> device;
+ mTileTextures[mCurrentTile]->GetDevice(getter_AddRefs(device));
+ HRESULT hr = device->CreateShaderResourceView(
+ mTileTextures[mCurrentTile], nullptr,
+ getter_AddRefs(mTileSRVs[mCurrentTile]));
+ if (FAILED(hr)) {
+ gfxCriticalNote
+ << "[D3D11] DataTextureSourceD3D11:GetShaderResourceView CreateSRV "
+ "failure "
+ << gfx::hexa(hr);
+ return nullptr;
+ }
+ }
+ return mTileSRVs[mCurrentTile];
+ }
+
+ return TextureSourceD3D11::GetShaderResourceView();
+}
+
+void DataTextureSourceD3D11::Reset() {
+ mTexture = nullptr;
+ mTileSRVs.resize(0);
+ mTileTextures.resize(0);
+ mIsTiled = false;
+ mSize.width = 0;
+ mSize.height = 0;
+}
+
+IntRect DataTextureSourceD3D11::GetTileRect(uint32_t aIndex) const {
+ return GetTileRectD3D11(aIndex, mSize, GetMaxTextureSizeFromDevice(mDevice));
+}
+
+IntRect DataTextureSourceD3D11::GetTileRect() {
+ IntRect rect = GetTileRect(mCurrentTile);
+ return IntRect(rect.X(), rect.Y(), rect.Width(), rect.Height());
+}
+
+CompositingRenderTargetD3D11::CompositingRenderTargetD3D11(
+ ID3D11Texture2D* aTexture, const gfx::IntPoint& aOrigin,
+ DXGI_FORMAT aFormatOverride)
+ : CompositingRenderTarget(aOrigin) {
+ MOZ_ASSERT(aTexture);
+
+ mTexture = aTexture;
+
+ RefPtr<ID3D11Device> device;
+ mTexture->GetDevice(getter_AddRefs(device));
+
+ mFormatOverride = aFormatOverride;
+
+ // If we happen to have a typeless underlying DXGI surface, we need to be
+ // explicit about the format here. (Such a surface could come from an external
+ // source, such as the Oculus compositor)
+ CD3D11_RENDER_TARGET_VIEW_DESC rtvDesc(D3D11_RTV_DIMENSION_TEXTURE2D,
+ mFormatOverride);
+ D3D11_RENDER_TARGET_VIEW_DESC* desc =
+ aFormatOverride == DXGI_FORMAT_UNKNOWN ? nullptr : &rtvDesc;
+
+ HRESULT hr =
+ device->CreateRenderTargetView(mTexture, desc, getter_AddRefs(mRTView));
+
+ if (FAILED(hr)) {
+ LOGD3D11("Failed to create RenderTargetView.");
+ }
+}
+
+void CompositingRenderTargetD3D11::BindRenderTarget(
+ ID3D11DeviceContext* aContext) {
+ if (mClearOnBind) {
+ FLOAT clear[] = {0, 0, 0, 0};
+ aContext->ClearRenderTargetView(mRTView, clear);
+ mClearOnBind = false;
+ }
+ ID3D11RenderTargetView* view = mRTView;
+ aContext->OMSetRenderTargets(1, &view, nullptr);
+}
+
+IntSize CompositingRenderTargetD3D11::GetSize() const {
+ return TextureSourceD3D11::GetSize();
+}
+
+static inline bool ShouldDevCrashOnSyncInitFailure() {
+ // Compositor shutdown does not wait for video decoding to finish, so it is
+ // possible for the compositor to destroy the SyncObject before video has a
+ // chance to initialize it.
+ if (!NS_IsMainThread()) {
+ return false;
+ }
+
+ // Note: CompositorIsInGPUProcess is a main-thread-only function.
+ return !CompositorBridgeChild::CompositorIsInGPUProcess() &&
+ !DeviceManagerDx::Get()->HasDeviceReset();
+}
+
+SyncObjectD3D11Host::SyncObjectD3D11Host(ID3D11Device* aDevice)
+ : mSyncHandle(0), mDevice(aDevice) {
+ MOZ_ASSERT(aDevice);
+}
+
+bool SyncObjectD3D11Host::Init() {
+ 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) || !texture) {
+ gfxWarning() << "Could not create a sync texture: " << gfx::hexa(hr);
+ return false;
+ }
+
+ hr = texture->QueryInterface((IDXGIResource**)getter_AddRefs(mSyncTexture));
+ if (FAILED(hr) || !mSyncTexture) {
+ gfxWarning() << "Could not QI sync texture: " << gfx::hexa(hr);
+ return false;
+ }
+
+ hr = mSyncTexture->QueryInterface(
+ (IDXGIKeyedMutex**)getter_AddRefs(mKeyedMutex));
+ if (FAILED(hr) || !mKeyedMutex) {
+ gfxWarning() << "Could not QI keyed-mutex: " << gfx::hexa(hr);
+ return false;
+ }
+
+ hr = mSyncTexture->GetSharedHandle(&mSyncHandle);
+ if (FAILED(hr) || !mSyncHandle) {
+ NS_DispatchToMainThread(NS_NewRunnableFunction(
+ "layers::SyncObjectD3D11Renderer::Init",
+ []() -> void { Accumulate(Telemetry::D3D11_SYNC_HANDLE_FAILURE, 1); }));
+ gfxWarning() << "Could not get sync texture shared handle: "
+ << gfx::hexa(hr);
+ return false;
+ }
+
+ return true;
+}
+
+SyncHandle SyncObjectD3D11Host::GetSyncHandle() { return mSyncHandle; }
+
+bool SyncObjectD3D11Host::Synchronize(bool aFallible) {
+ HRESULT hr;
+ AutoTextureLock lock(mKeyedMutex, hr, 10000);
+
+ if (hr == WAIT_TIMEOUT) {
+ hr = mDevice->GetDeviceRemovedReason();
+ if (hr != S_OK) {
+ // Since the timeout is related to the driver-removed. Return false for
+ // error handling.
+ gfxCriticalNote << "GFX: D3D11 timeout with device-removed:"
+ << gfx::hexa(hr);
+ } else if (aFallible) {
+ gfxCriticalNote << "GFX: D3D11 timeout on the D3D11 sync lock.";
+ } else {
+ // There is no driver-removed event. Crash with this timeout.
+ MOZ_CRASH("GFX: D3D11 normal status timeout");
+ }
+
+ return false;
+ }
+ if (hr == WAIT_ABANDONED) {
+ gfxCriticalNote << "GFX: AL_D3D11 abandoned sync";
+ }
+
+ return true;
+}
+
+SyncObjectD3D11Client::SyncObjectD3D11Client(SyncHandle aSyncHandle,
+ ID3D11Device* aDevice)
+ : mSyncLock("SyncObjectD3D11"), mSyncHandle(aSyncHandle), mDevice(aDevice) {
+ MOZ_ASSERT(aDevice);
+}
+
+SyncObjectD3D11Client::SyncObjectD3D11Client(SyncHandle aSyncHandle)
+ : mSyncLock("SyncObjectD3D11"), mSyncHandle(aSyncHandle) {}
+
+bool SyncObjectD3D11Client::Init(ID3D11Device* aDevice, bool aFallible) {
+ if (mKeyedMutex) {
+ return true;
+ }
+
+ HRESULT hr = aDevice->OpenSharedResource(
+ mSyncHandle, __uuidof(ID3D11Texture2D),
+ (void**)(ID3D11Texture2D**)getter_AddRefs(mSyncTexture));
+ if (FAILED(hr) || !mSyncTexture) {
+ gfxCriticalNote << "Failed to OpenSharedResource for SyncObjectD3D11: "
+ << hexa(hr);
+ if (!aFallible && ShouldDevCrashOnSyncInitFailure()) {
+ gfxDevCrash(LogReason::D3D11FinalizeFrame)
+ << "Without device reset: " << hexa(hr);
+ }
+ return false;
+ }
+
+ hr = mSyncTexture->QueryInterface(__uuidof(IDXGIKeyedMutex),
+ getter_AddRefs(mKeyedMutex));
+ if (FAILED(hr) || !mKeyedMutex) {
+ // Leave both the critical error and MOZ_CRASH for now; the critical error
+ // lets us "save" the hr value. We will probably eventually replace this
+ // with gfxDevCrash.
+ if (!aFallible) {
+ gfxCriticalError() << "Failed to get KeyedMutex (2): " << hexa(hr);
+ MOZ_CRASH("GFX: Cannot get D3D11 KeyedMutex");
+ } else {
+ gfxCriticalNote << "Failed to get KeyedMutex (3): " << hexa(hr);
+ }
+ return false;
+ }
+
+ return true;
+}
+
+void SyncObjectD3D11Client::RegisterTexture(ID3D11Texture2D* aTexture) {
+ mSyncedTextures.push_back(aTexture);
+}
+
+bool SyncObjectD3D11Client::IsSyncObjectValid() {
+ MOZ_ASSERT(mDevice);
+ return true;
+}
+
+// We have only 1 sync object. As a thing that somehow works,
+// we copy each of the textures that need to be synced with the compositor
+// into our sync object and only use a lock for this sync object.
+// This way, we don't have to sync every texture we send to the compositor.
+// We only have to do this once per transaction.
+bool SyncObjectD3D11Client::Synchronize(bool aFallible) {
+ MOZ_ASSERT(mDevice);
+ // Since this can be called from either the Paint or Main thread.
+ // We don't want this to race since we initialize the sync texture here
+ // too.
+ MutexAutoLock syncLock(mSyncLock);
+
+ if (!mSyncedTextures.size()) {
+ return true;
+ }
+ if (!Init(mDevice, aFallible)) {
+ return false;
+ }
+
+ return SynchronizeInternal(mDevice, aFallible);
+}
+
+bool SyncObjectD3D11Client::SynchronizeInternal(ID3D11Device* aDevice,
+ bool aFallible) {
+ mSyncLock.AssertCurrentThreadOwns();
+
+ HRESULT hr;
+ AutoTextureLock lock(mKeyedMutex, hr, 20000);
+
+ if (hr == WAIT_TIMEOUT) {
+ if (DeviceManagerDx::Get()->HasDeviceReset()) {
+ gfxWarning() << "AcquireSync timed out because of device reset.";
+ return false;
+ }
+ if (aFallible) {
+ gfxWarning() << "Timeout on the D3D11 sync lock.";
+ } else {
+ gfxDevCrash(LogReason::D3D11SyncLock)
+ << "Timeout on the D3D11 sync lock.";
+ }
+ return false;
+ }
+
+ D3D11_BOX box;
+ box.front = box.top = box.left = 0;
+ box.back = box.bottom = box.right = 1;
+
+ RefPtr<ID3D11DeviceContext> ctx;
+ aDevice->GetImmediateContext(getter_AddRefs(ctx));
+
+ for (auto iter = mSyncedTextures.begin(); iter != mSyncedTextures.end();
+ iter++) {
+ ctx->CopySubresourceRegion(mSyncTexture, 0, 0, 0, 0, *iter, 0, &box);
+ }
+
+ mSyncedTextures.clear();
+
+ return true;
+}
+
+uint32_t GetMaxTextureSizeFromDevice(ID3D11Device* aDevice) {
+ return GetMaxTextureSizeForFeatureLevel(aDevice->GetFeatureLevel());
+}
+
+AutoLockD3D11Texture::AutoLockD3D11Texture(ID3D11Texture2D* aTexture) {
+ aTexture->QueryInterface((IDXGIKeyedMutex**)getter_AddRefs(mMutex));
+ if (!mMutex) {
+ return;
+ }
+ HRESULT hr = mMutex->AcquireSync(0, 10000);
+ if (hr == WAIT_TIMEOUT) {
+ MOZ_CRASH("GFX: IMFYCbCrImage timeout");
+ }
+
+ if (FAILED(hr)) {
+ NS_WARNING("Failed to lock the texture");
+ }
+}
+
+AutoLockD3D11Texture::~AutoLockD3D11Texture() {
+ if (!mMutex) {
+ return;
+ }
+ HRESULT hr = mMutex->ReleaseSync(0);
+ if (FAILED(hr)) {
+ NS_WARNING("Failed to unlock the texture");
+ }
+}
+
+SyncObjectD3D11ClientContentDevice::SyncObjectD3D11ClientContentDevice(
+ SyncHandle aSyncHandle)
+ : SyncObjectD3D11Client(aSyncHandle) {}
+
+bool SyncObjectD3D11ClientContentDevice::Synchronize(bool aFallible) {
+ // Since this can be called from either the Paint or Main thread.
+ // We don't want this to race since we initialize the sync texture here
+ // too.
+ MutexAutoLock syncLock(mSyncLock);
+
+ MOZ_ASSERT(mContentDevice);
+
+ if (!mSyncedTextures.size()) {
+ return true;
+ }
+
+ if (!Init(mContentDevice, aFallible)) {
+ return false;
+ }
+
+ RefPtr<ID3D11Device> dev;
+ mSyncTexture->GetDevice(getter_AddRefs(dev));
+
+ if (dev == DeviceManagerDx::Get()->GetContentDevice()) {
+ if (DeviceManagerDx::Get()->HasDeviceReset()) {
+ return false;
+ }
+ }
+
+ if (dev != mContentDevice) {
+ gfxWarning() << "Attempt to sync texture from invalid device.";
+ return false;
+ }
+
+ return SyncObjectD3D11Client::SynchronizeInternal(dev, aFallible);
+}
+
+bool SyncObjectD3D11ClientContentDevice::IsSyncObjectValid() {
+ RefPtr<ID3D11Device> dev;
+ // There is a case that devices are not initialized yet with WebRender.
+ if (gfxPlatform::GetPlatform()->DevicesInitialized()) {
+ dev = DeviceManagerDx::Get()->GetContentDevice();
+ }
+
+ // Update mDevice if the ContentDevice initialization is detected.
+ if (!mContentDevice && dev && NS_IsMainThread() && gfxVars::UseWebRender()) {
+ mContentDevice = dev;
+ }
+
+ if (!dev || (NS_IsMainThread() && dev != mContentDevice)) {
+ return false;
+ }
+ return true;
+}
+
+void SyncObjectD3D11ClientContentDevice::EnsureInitialized() {
+ if (mContentDevice) {
+ return;
+ }
+
+ if (XRE_IsGPUProcess() || !gfxPlatform::GetPlatform()->DevicesInitialized()) {
+ return;
+ }
+
+ mContentDevice = DeviceManagerDx::Get()->GetContentDevice();
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/d3d11/TextureD3D11.h b/gfx/layers/d3d11/TextureD3D11.h
new file mode 100644
index 0000000000..2a69015719
--- /dev/null
+++ b/gfx/layers/d3d11/TextureD3D11.h
@@ -0,0 +1,600 @@
+/* -*- 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 D3D11TextureData final : public TextureData {
+ public:
+ // If aDevice is null, use one provided by gfxWindowsPlatform.
+ static D3D11TextureData* Create(gfx::IntSize aSize,
+ gfx::SurfaceFormat aFormat,
+ TextureAllocationFlags aAllocFlags,
+ ID3D11Device* aDevice = nullptr);
+ static D3D11TextureData* Create(gfx::SourceSurface* aSurface,
+ TextureAllocationFlags aAllocFlags,
+ ID3D11Device* aDevice = nullptr);
+
+ virtual ~D3D11TextureData();
+
+ 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(RefPtr<SyncObjectClient> aSyncObject) override;
+
+ ID3D11Texture2D* GetD3D11Texture() const { return mTexture; }
+
+ void Deallocate(LayersIPCChannel* aAllocator) override;
+
+ D3D11TextureData* AsD3D11TextureData() override { return this; }
+
+ TextureAllocationFlags GetTextureAllocationFlags() const {
+ return mAllocationFlags;
+ }
+
+ void FillInfo(TextureData::Info& aInfo) const override;
+
+ bool Serialize(SurfaceDescriptor& aOutDescrptor) override;
+ void GetSubDescriptor(RemoteDecoderVideoSubDescriptor* aOutDesc) override;
+
+ gfx::YUVColorSpace GetYUVColorSpace() const { return mYUVColorSpace; }
+ void SetYUVColorSpace(gfx::YUVColorSpace aColorSpace) {
+ mYUVColorSpace = aColorSpace;
+ }
+ gfx::ColorRange GetColorRange() const { return mColorRange; }
+ void SetColorRange(gfx::ColorRange aColorRange) { mColorRange = aColorRange; }
+
+ gfx::IntSize GetSize() const { return mSize; }
+ gfx::SurfaceFormat GetSurfaceFormat() const { return mFormat; }
+
+ TextureFlags GetTextureFlags() const override;
+
+ private:
+ D3D11TextureData(ID3D11Texture2D* aTexture, gfx::IntSize aSize,
+ gfx::SurfaceFormat aFormat, TextureAllocationFlags aFlags);
+
+ void GetDXGIResource(IDXGIResource** aOutResource);
+
+ bool PrepareDrawTargetInLock(OpenMode aMode);
+
+ friend class gl::GLBlitHelper;
+ bool SerializeSpecific(SurfaceDescriptorD3D10* aOutDesc);
+
+ static D3D11TextureData* Create(gfx::IntSize aSize,
+ gfx::SurfaceFormat aFormat,
+ gfx::SourceSurface* aSurface,
+ TextureAllocationFlags aAllocFlags,
+ ID3D11Device* aDevice = nullptr);
+
+ // 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;
+ gfx::ColorRange mColorRange = gfx::ColorRange::LIMITED;
+ bool mNeedsClear;
+ bool mNeedsClearWhite;
+ bool mHasSynchronization;
+ bool mIsForOutOfBandContent;
+
+ 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, gfx::ColorRange aColorRange);
+
+ 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,
+ gfx::ColorRange aColorRange);
+
+ 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(RemoteDecoderVideoSubDescriptor* 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;
+
+ 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; }
+ gfx::ColorRange GetColorRange() const { return mColorRange; }
+
+ 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;
+ gfx::ColorRange mColorRange;
+};
+
+/**
+ * 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;
+ }
+ gfx::ColorRange GetColorRange() const override { return mColorRange; }
+
+ already_AddRefed<gfx::DataSourceSurface> GetAsSurface() override;
+
+ void CreateRenderTexture(
+ const wr::ExternalImageId& aExternalImageId) override;
+
+ uint32_t NumSubTextures() 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,
+ PushDisplayItemFlagSet aFlags) override;
+
+ bool SupportsExternalCompositing() override;
+
+ 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;
+ const gfx::ColorRange mColorRange;
+ 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;
+ }
+ gfx::ColorRange GetColorRange() const override { return mColorRange; }
+
+ 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() 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,
+ PushDisplayItemFlagSet aFlags) override;
+
+ bool SupportsExternalCompositing() override;
+
+ private:
+ bool EnsureTextureSource();
+
+ protected:
+ RefPtr<ID3D11Device> GetDevice();
+
+ bool EnsureTexture();
+
+ RefPtr<ID3D11Texture2D> mTextures[3];
+ RefPtr<DataTextureSourceD3D11> mTextureSources[3];
+
+ gfx::IntSize mSize;
+ gfx::IntSize mSizeY;
+ gfx::IntSize mSizeCbCr;
+ WindowsHandle mHandles[3];
+ bool mIsLocked;
+ gfx::ColorDepth mColorDepth;
+ gfx::YUVColorSpace mYUVColorSpace;
+ gfx::ColorRange mColorRange;
+};
+
+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;
+
+ void EnsureInitialized() override {}
+
+ SyncType GetSyncType() override { return SyncType::D3D11; }
+
+ void RegisterTexture(ID3D11Texture2D* aTexture);
+
+ protected:
+ explicit SyncObjectD3D11Client(SyncHandle aSyncHandle);
+ bool Init(ID3D11Device* aDevice, bool aFallible);
+ bool SynchronizeInternal(ID3D11Device* aDevice, bool aFallible);
+ Mutex mSyncLock;
+ RefPtr<ID3D11Texture2D> mSyncTexture;
+ std::vector<ID3D11Texture2D*> mSyncedTextures;
+
+ private:
+ const SyncHandle mSyncHandle;
+ RefPtr<IDXGIKeyedMutex> mKeyedMutex;
+ const RefPtr<ID3D11Device> mDevice;
+};
+
+class SyncObjectD3D11ClientContentDevice : public SyncObjectD3D11Client {
+ public:
+ explicit SyncObjectD3D11ClientContentDevice(SyncHandle aSyncHandle);
+
+ bool Synchronize(bool aFallible) override;
+
+ bool IsSyncObjectValid() override;
+
+ void EnsureInitialized() override;
+
+ private:
+ RefPtr<ID3D11Device> mContentDevice;
+};
+
+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 */
diff --git a/gfx/layers/d3d11/genshaders.py b/gfx/layers/d3d11/genshaders.py
new file mode 100644
index 0000000000..2108bfe693
--- /dev/null
+++ b/gfx/layers/d3d11/genshaders.py
@@ -0,0 +1,176 @@
+# 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/.
+from __future__ import absolute_import
+import argparse
+import codecs
+import locale
+import os
+import re
+import subprocess
+import sys
+import tempfile
+import yaml
+import buildconfig
+
+
+def shell_main():
+ parser = argparse.ArgumentParser()
+ parser.add_argument("-o", "--output", type=str, required=True, help="Output file")
+ parser.add_argument("manifest", type=str, help="Manifest source file")
+ args = parser.parse_args()
+
+ with open(args.output, "w") as out_file:
+ process_manifest(out_file, args.manifest)
+
+
+def main(output_fp, input_filename):
+ return process_manifest(output_fp, input_filename)
+
+
+HEADER = """// AUTOGENERATED - DO NOT EDIT
+namespace mozilla {
+namespace layers {
+
+struct ShaderBytes { const void* mData; size_t mLength; };
+"""
+FOOTER = """
+} // namespace layers
+} // namespace mozilla"""
+
+
+def process_manifest(output_fp, manifest_filename):
+ with codecs.open(manifest_filename, "r", "UTF-8") as in_fp:
+ manifest = yaml.safe_load(in_fp)
+ shader_folder, _ = os.path.split(manifest_filename)
+
+ output_fp.write(HEADER)
+
+ deps = set()
+ for block in manifest:
+ if "type" not in block:
+ raise Exception("Expected 'type' key with shader mode")
+ if "file" not in block:
+ raise Exception("Expected 'file' key with shader file")
+ if "shaders" not in block:
+ raise Exception("Expected 'shaders' key with shader name list")
+
+ shader_file = os.path.join(shader_folder, block["file"])
+ deps.add(shader_file)
+
+ shader_model = block["type"]
+ for shader_name in block["shaders"]:
+ new_deps = run_fxc(
+ shader_model=shader_model,
+ shader_file=shader_file,
+ shader_name=shader_name,
+ output_fp=output_fp,
+ )
+ deps |= new_deps
+
+ output_fp.write(FOOTER)
+ return deps
+
+
+def run_fxc(shader_model, shader_file, shader_name, output_fp):
+ fxc_location = buildconfig.substs["FXC"]
+
+ argv = [
+ fxc_location,
+ "-nologo",
+ "-T{0}".format(shader_model),
+ os.path.relpath(shader_file),
+ "-E{0}".format(shader_name),
+ "-Vn{0}".format(shader_name),
+ "-Vi",
+ ]
+ if "WINNT" not in buildconfig.substs["HOST_OS_ARCH"]:
+ argv.insert(0, buildconfig.substs["WINE"])
+ if shader_model.startswith("vs_"):
+ argv += ["-DVERTEX_SHADER"]
+ elif shader_model.startswith("ps_"):
+ argv += ["-DPIXEL_SHADER"]
+
+ deps = None
+ with ScopedTempFilename() as temp_filename:
+ argv += ["-Fh{0}".format(os.path.relpath(temp_filename))]
+
+ sys.stdout.write("{0}\n".format(" ".join(argv)))
+ sys.stdout.flush()
+ proc_stdout = subprocess.check_output(argv)
+ proc_stdout = decode_console_text(sys.stdout, proc_stdout)
+ deps = find_dependencies(proc_stdout)
+ assert "fxc2" in fxc_location or len(deps) > 0
+
+ with open(temp_filename, "r") as temp_fp:
+ output_fp.write(temp_fp.read())
+
+ output_fp.write("ShaderBytes s{0} = {{ {0}, sizeof({0}) }};\n".format(shader_name))
+ return deps
+
+
+def find_dependencies(fxc_output):
+ # Dependencies look like this:
+ # Resolved to [<path>]
+ #
+ # Microsoft likes to change output strings based on the user's language, so
+ # instead of pattern matching on that string, we take everything in between
+ # brackets. We filter out potentially bogus strings later.
+ deps = set()
+ for line in fxc_output.split("\n"):
+ m = re.search(r"\[([^\]]+)\]", line)
+ if m is None:
+ continue
+ dep_path = m.group(1)
+ dep_path = os.path.normpath(dep_path)
+ # When run via Wine, FXC's output contains Windows paths on the Z drive.
+ # We want to normalize them back to unix paths for the build system.
+ if "WINNT" not in buildconfig.substs[
+ "HOST_OS_ARCH"
+ ] and dep_path.lower().startswith("z:"):
+ dep_path = dep_path[2:].replace("\\", "/")
+ if os.path.isfile(dep_path):
+ deps.add(dep_path)
+ return deps
+
+
+# Python reads the raw bytes from stdout, so we need to try our best to
+# capture that as a valid Python string.
+
+
+def decode_console_text(pipe, text):
+ try:
+ if pipe.encoding:
+ return text.decode(pipe.encoding, "replace")
+ except Exception:
+ pass
+ try:
+ return text.decode(locale.getpreferredencoding(), "replace")
+ except Exception:
+ return text.decode("utf8", "replace")
+
+
+# Allocate a temporary file name and delete it when done. We need an extra
+# wrapper for this since TemporaryNamedFile holds the file open.
+
+
+class ScopedTempFilename(object):
+ def __init__(self):
+ self.name = None
+
+ def __enter__(self):
+ with tempfile.NamedTemporaryFile(dir=os.getcwd(), delete=False) as tmp:
+ self.name = tmp.name
+ return self.name
+
+ def __exit__(self, type, value, traceback):
+ if not self.name:
+ return
+ try:
+ os.unlink(self.name)
+ except Exception:
+ pass
+
+
+if __name__ == "__main__":
+ shell_main()
diff --git a/gfx/layers/d3d11/mlgshaders/blend-common.hlsl b/gfx/layers/d3d11/mlgshaders/blend-common.hlsl
new file mode 100644
index 0000000000..1194fc0801
--- /dev/null
+++ b/gfx/layers/d3d11/mlgshaders/blend-common.hlsl
@@ -0,0 +1,15 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * 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 "common-vs.hlsl"
+
+struct VS_BLEND_OUTPUT
+{
+ float4 vPosition : SV_Position;
+ float2 vTexCoords : TEXCOORD0;
+ float2 vBackdropCoords : TEXCOORD1;
+ float2 vLocalPos : TEXCOORD2;
+ float3 vMaskCoords : TEXCOORD3;
+ nointerpolation float4 vClipRect : TEXCOORD4;
+};
diff --git a/gfx/layers/d3d11/mlgshaders/blend-ps-generated.hlslh b/gfx/layers/d3d11/mlgshaders/blend-ps-generated.hlslh
new file mode 100644
index 0000000000..655f3e0cb4
--- /dev/null
+++ b/gfx/layers/d3d11/mlgshaders/blend-ps-generated.hlslh
@@ -0,0 +1,540 @@
+float4 BlendMultiplyPS(const VS_BLEND_OUTPUT aInput) : SV_Target
+{
+ if (!RectContainsPoint(aInput.vClipRect, aInput.vPosition.xy)) {
+ discard;
+ }
+
+ float4 backdrop = tBackdrop.Sample(sSampler, aInput.vBackdropCoords);
+ float4 source = simpleTex.Sample(sSampler, aInput.vTexCoords);
+
+ source *= ReadMask(aInput.vMaskCoords);
+
+ // Shortcut when the backdrop or source alpha is 0, otherwise we may leak
+ // infinity into the blend function and return incorrect results.
+ if (backdrop.a == 0.0) {
+ return source;
+ }
+ if (source.a == 0.0) {
+ return float4(0, 0, 0, 0);
+ }
+
+ // The spec assumes there is no premultiplied alpha. The backdrop and
+ // source are both render targets and always premultiplied, so we undo
+ // that here.
+ backdrop.rgb /= backdrop.a;
+ source.rgb /= source.a;
+
+ float4 result;
+ result.rgb = BlendMultiply(backdrop.rgb, source.rgb);
+ result.a = source.a;
+
+ // Factor backdrop alpha, then premultiply for the final OP_OVER.
+ result.rgb = (1.0 - backdrop.a) * source.rgb + backdrop.a * result.rgb;
+ result.rgb *= result.a;
+ return result;
+}
+
+float4 BlendScreenPS(const VS_BLEND_OUTPUT aInput) : SV_Target
+{
+ if (!RectContainsPoint(aInput.vClipRect, aInput.vPosition.xy)) {
+ discard;
+ }
+
+ float4 backdrop = tBackdrop.Sample(sSampler, aInput.vBackdropCoords);
+ float4 source = simpleTex.Sample(sSampler, aInput.vTexCoords);
+
+ source *= ReadMask(aInput.vMaskCoords);
+
+ // Shortcut when the backdrop or source alpha is 0, otherwise we may leak
+ // infinity into the blend function and return incorrect results.
+ if (backdrop.a == 0.0) {
+ return source;
+ }
+ if (source.a == 0.0) {
+ return float4(0, 0, 0, 0);
+ }
+
+ // The spec assumes there is no premultiplied alpha. The backdrop and
+ // source are both render targets and always premultiplied, so we undo
+ // that here.
+ backdrop.rgb /= backdrop.a;
+ source.rgb /= source.a;
+
+ float4 result;
+ result.rgb = BlendScreen(backdrop.rgb, source.rgb);
+ result.a = source.a;
+
+ // Factor backdrop alpha, then premultiply for the final OP_OVER.
+ result.rgb = (1.0 - backdrop.a) * source.rgb + backdrop.a * result.rgb;
+ result.rgb *= result.a;
+ return result;
+}
+
+float4 BlendOverlayPS(const VS_BLEND_OUTPUT aInput) : SV_Target
+{
+ if (!RectContainsPoint(aInput.vClipRect, aInput.vPosition.xy)) {
+ discard;
+ }
+
+ float4 backdrop = tBackdrop.Sample(sSampler, aInput.vBackdropCoords);
+ float4 source = simpleTex.Sample(sSampler, aInput.vTexCoords);
+
+ source *= ReadMask(aInput.vMaskCoords);
+
+ // Shortcut when the backdrop or source alpha is 0, otherwise we may leak
+ // infinity into the blend function and return incorrect results.
+ if (backdrop.a == 0.0) {
+ return source;
+ }
+ if (source.a == 0.0) {
+ return float4(0, 0, 0, 0);
+ }
+
+ // The spec assumes there is no premultiplied alpha. The backdrop and
+ // source are both render targets and always premultiplied, so we undo
+ // that here.
+ backdrop.rgb /= backdrop.a;
+ source.rgb /= source.a;
+
+ float4 result;
+ result.rgb = BlendOverlay(backdrop.rgb, source.rgb);
+ result.a = source.a;
+
+ // Factor backdrop alpha, then premultiply for the final OP_OVER.
+ result.rgb = (1.0 - backdrop.a) * source.rgb + backdrop.a * result.rgb;
+ result.rgb *= result.a;
+ return result;
+}
+
+float4 BlendDarkenPS(const VS_BLEND_OUTPUT aInput) : SV_Target
+{
+ if (!RectContainsPoint(aInput.vClipRect, aInput.vPosition.xy)) {
+ discard;
+ }
+
+ float4 backdrop = tBackdrop.Sample(sSampler, aInput.vBackdropCoords);
+ float4 source = simpleTex.Sample(sSampler, aInput.vTexCoords);
+
+ source *= ReadMask(aInput.vMaskCoords);
+
+ // Shortcut when the backdrop or source alpha is 0, otherwise we may leak
+ // infinity into the blend function and return incorrect results.
+ if (backdrop.a == 0.0) {
+ return source;
+ }
+ if (source.a == 0.0) {
+ return float4(0, 0, 0, 0);
+ }
+
+ // The spec assumes there is no premultiplied alpha. The backdrop and
+ // source are both render targets and always premultiplied, so we undo
+ // that here.
+ backdrop.rgb /= backdrop.a;
+ source.rgb /= source.a;
+
+ float4 result;
+ result.rgb = BlendDarken(backdrop.rgb, source.rgb);
+ result.a = source.a;
+
+ // Factor backdrop alpha, then premultiply for the final OP_OVER.
+ result.rgb = (1.0 - backdrop.a) * source.rgb + backdrop.a * result.rgb;
+ result.rgb *= result.a;
+ return result;
+}
+
+float4 BlendLightenPS(const VS_BLEND_OUTPUT aInput) : SV_Target
+{
+ if (!RectContainsPoint(aInput.vClipRect, aInput.vPosition.xy)) {
+ discard;
+ }
+
+ float4 backdrop = tBackdrop.Sample(sSampler, aInput.vBackdropCoords);
+ float4 source = simpleTex.Sample(sSampler, aInput.vTexCoords);
+
+ source *= ReadMask(aInput.vMaskCoords);
+
+ // Shortcut when the backdrop or source alpha is 0, otherwise we may leak
+ // infinity into the blend function and return incorrect results.
+ if (backdrop.a == 0.0) {
+ return source;
+ }
+ if (source.a == 0.0) {
+ return float4(0, 0, 0, 0);
+ }
+
+ // The spec assumes there is no premultiplied alpha. The backdrop and
+ // source are both render targets and always premultiplied, so we undo
+ // that here.
+ backdrop.rgb /= backdrop.a;
+ source.rgb /= source.a;
+
+ float4 result;
+ result.rgb = BlendLighten(backdrop.rgb, source.rgb);
+ result.a = source.a;
+
+ // Factor backdrop alpha, then premultiply for the final OP_OVER.
+ result.rgb = (1.0 - backdrop.a) * source.rgb + backdrop.a * result.rgb;
+ result.rgb *= result.a;
+ return result;
+}
+
+float4 BlendColorDodgePS(const VS_BLEND_OUTPUT aInput) : SV_Target
+{
+ if (!RectContainsPoint(aInput.vClipRect, aInput.vPosition.xy)) {
+ discard;
+ }
+
+ float4 backdrop = tBackdrop.Sample(sSampler, aInput.vBackdropCoords);
+ float4 source = simpleTex.Sample(sSampler, aInput.vTexCoords);
+
+ source *= ReadMask(aInput.vMaskCoords);
+
+ // Shortcut when the backdrop or source alpha is 0, otherwise we may leak
+ // infinity into the blend function and return incorrect results.
+ if (backdrop.a == 0.0) {
+ return source;
+ }
+ if (source.a == 0.0) {
+ return float4(0, 0, 0, 0);
+ }
+
+ // The spec assumes there is no premultiplied alpha. The backdrop and
+ // source are both render targets and always premultiplied, so we undo
+ // that here.
+ backdrop.rgb /= backdrop.a;
+ source.rgb /= source.a;
+
+ float4 result;
+ result.rgb = BlendColorDodge(backdrop.rgb, source.rgb);
+ result.a = source.a;
+
+ // Factor backdrop alpha, then premultiply for the final OP_OVER.
+ result.rgb = (1.0 - backdrop.a) * source.rgb + backdrop.a * result.rgb;
+ result.rgb *= result.a;
+ return result;
+}
+
+float4 BlendColorBurnPS(const VS_BLEND_OUTPUT aInput) : SV_Target
+{
+ if (!RectContainsPoint(aInput.vClipRect, aInput.vPosition.xy)) {
+ discard;
+ }
+
+ float4 backdrop = tBackdrop.Sample(sSampler, aInput.vBackdropCoords);
+ float4 source = simpleTex.Sample(sSampler, aInput.vTexCoords);
+
+ source *= ReadMask(aInput.vMaskCoords);
+
+ // Shortcut when the backdrop or source alpha is 0, otherwise we may leak
+ // infinity into the blend function and return incorrect results.
+ if (backdrop.a == 0.0) {
+ return source;
+ }
+ if (source.a == 0.0) {
+ return float4(0, 0, 0, 0);
+ }
+
+ // The spec assumes there is no premultiplied alpha. The backdrop and
+ // source are both render targets and always premultiplied, so we undo
+ // that here.
+ backdrop.rgb /= backdrop.a;
+ source.rgb /= source.a;
+
+ float4 result;
+ result.rgb = BlendColorBurn(backdrop.rgb, source.rgb);
+ result.a = source.a;
+
+ // Factor backdrop alpha, then premultiply for the final OP_OVER.
+ result.rgb = (1.0 - backdrop.a) * source.rgb + backdrop.a * result.rgb;
+ result.rgb *= result.a;
+ return result;
+}
+
+float4 BlendHardLightPS(const VS_BLEND_OUTPUT aInput) : SV_Target
+{
+ if (!RectContainsPoint(aInput.vClipRect, aInput.vPosition.xy)) {
+ discard;
+ }
+
+ float4 backdrop = tBackdrop.Sample(sSampler, aInput.vBackdropCoords);
+ float4 source = simpleTex.Sample(sSampler, aInput.vTexCoords);
+
+ source *= sOpacity;
+
+ // Shortcut when the backdrop or source alpha is 0, otherwise we may leak
+ // infinity into the blend function and return incorrect results.
+ if (backdrop.a == 0.0) {
+ return source;
+ }
+ if (source.a == 0.0) {
+ return float4(0, 0, 0, 0);
+ }
+
+ // The spec assumes there is no premultiplied alpha. The backdrop and
+ // source are both render targets and always premultiplied, so we undo
+ // that here.
+ backdrop.rgb /= backdrop.a;
+ source.rgb /= source.a;
+
+ float4 result;
+ result.rgb = BlendHardLight(backdrop.rgb, source.rgb);
+ result.a = source.a;
+
+ // Factor backdrop alpha, then premultiply for the final OP_OVER.
+ result.rgb = (1.0 - backdrop.a) * source.rgb + backdrop.a * result.rgb;
+ result.rgb *= result.a;
+ return result;
+}
+
+float4 BlendSoftLightPS(const VS_BLEND_OUTPUT aInput) : SV_Target
+{
+ if (!RectContainsPoint(aInput.vClipRect, aInput.vPosition.xy)) {
+ discard;
+ }
+
+ float4 backdrop = tBackdrop.Sample(sSampler, aInput.vBackdropCoords);
+ float4 source = simpleTex.Sample(sSampler, aInput.vTexCoords);
+
+ source *= ReadMask(aInput.vMaskCoords);
+
+ // Shortcut when the backdrop or source alpha is 0, otherwise we may leak
+ // infinity into the blend function and return incorrect results.
+ if (backdrop.a == 0.0) {
+ return source;
+ }
+ if (source.a == 0.0) {
+ return float4(0, 0, 0, 0);
+ }
+
+ // The spec assumes there is no premultiplied alpha. The backdrop and
+ // source are both render targets and always premultiplied, so we undo
+ // that here.
+ backdrop.rgb /= backdrop.a;
+ source.rgb /= source.a;
+
+ float4 result;
+ result.rgb = BlendSoftLight(backdrop.rgb, source.rgb);
+ result.a = source.a;
+
+ // Factor backdrop alpha, then premultiply for the final OP_OVER.
+ result.rgb = (1.0 - backdrop.a) * source.rgb + backdrop.a * result.rgb;
+ result.rgb *= result.a;
+ return result;
+}
+
+float4 BlendDifferencePS(const VS_BLEND_OUTPUT aInput) : SV_Target
+{
+ if (!RectContainsPoint(aInput.vClipRect, aInput.vPosition.xy)) {
+ discard;
+ }
+
+ float4 backdrop = tBackdrop.Sample(sSampler, aInput.vBackdropCoords);
+ float4 source = simpleTex.Sample(sSampler, aInput.vTexCoords);
+
+ source *= ReadMask(aInput.vMaskCoords);
+
+ // Shortcut when the backdrop or source alpha is 0, otherwise we may leak
+ // infinity into the blend function and return incorrect results.
+ if (backdrop.a == 0.0) {
+ return source;
+ }
+ if (source.a == 0.0) {
+ return float4(0, 0, 0, 0);
+ }
+
+ // The spec assumes there is no premultiplied alpha. The backdrop and
+ // source are both render targets and always premultiplied, so we undo
+ // that here.
+ backdrop.rgb /= backdrop.a;
+ source.rgb /= source.a;
+
+ float4 result;
+ result.rgb = BlendDifference(backdrop.rgb, source.rgb);
+ result.a = source.a;
+
+ // Factor backdrop alpha, then premultiply for the final OP_OVER.
+ result.rgb = (1.0 - backdrop.a) * source.rgb + backdrop.a * result.rgb;
+ result.rgb *= result.a;
+ return result;
+}
+
+float4 BlendExclusionPS(const VS_BLEND_OUTPUT aInput) : SV_Target
+{
+ if (!RectContainsPoint(aInput.vClipRect, aInput.vPosition.xy)) {
+ discard;
+ }
+
+ float4 backdrop = tBackdrop.Sample(sSampler, aInput.vBackdropCoords);
+ float4 source = simpleTex.Sample(sSampler, aInput.vTexCoords);
+
+ source *= ReadMask(aInput.vMaskCoords);
+
+ // Shortcut when the backdrop or source alpha is 0, otherwise we may leak
+ // infinity into the blend function and return incorrect results.
+ if (backdrop.a == 0.0) {
+ return source;
+ }
+ if (source.a == 0.0) {
+ return float4(0, 0, 0, 0);
+ }
+
+ // The spec assumes there is no premultiplied alpha. The backdrop and
+ // source are both render targets and always premultiplied, so we undo
+ // that here.
+ backdrop.rgb /= backdrop.a;
+ source.rgb /= source.a;
+
+ float4 result;
+ result.rgb = BlendExclusion(backdrop.rgb, source.rgb);
+ result.a = source.a;
+
+ // Factor backdrop alpha, then premultiply for the final OP_OVER.
+ result.rgb = (1.0 - backdrop.a) * source.rgb + backdrop.a * result.rgb;
+ result.rgb *= result.a;
+ return result;
+}
+
+float4 BlendHuePS(const VS_BLEND_OUTPUT aInput) : SV_Target
+{
+ if (!RectContainsPoint(aInput.vClipRect, aInput.vPosition.xy)) {
+ discard;
+ }
+
+ float4 backdrop = tBackdrop.Sample(sSampler, aInput.vBackdropCoords);
+ float4 source = simpleTex.Sample(sSampler, aInput.vTexCoords);
+
+ source *= ReadMask(aInput.vMaskCoords);
+
+ // Shortcut when the backdrop or source alpha is 0, otherwise we may leak
+ // infinity into the blend function and return incorrect results.
+ if (backdrop.a == 0.0) {
+ return source;
+ }
+ if (source.a == 0.0) {
+ return float4(0, 0, 0, 0);
+ }
+
+ // The spec assumes there is no premultiplied alpha. The backdrop and
+ // source are both render targets and always premultiplied, so we undo
+ // that here.
+ backdrop.rgb /= backdrop.a;
+ source.rgb /= source.a;
+
+ float4 result;
+ result.rgb = BlendHue(backdrop.rgb, source.rgb);
+ result.a = source.a;
+
+ // Factor backdrop alpha, then premultiply for the final OP_OVER.
+ result.rgb = (1.0 - backdrop.a) * source.rgb + backdrop.a * result.rgb;
+ result.rgb *= result.a;
+ return result;
+}
+
+float4 BlendSaturationPS(const VS_BLEND_OUTPUT aInput) : SV_Target
+{
+ if (!RectContainsPoint(aInput.vClipRect, aInput.vPosition.xy)) {
+ discard;
+ }
+
+ float4 backdrop = tBackdrop.Sample(sSampler, aInput.vBackdropCoords);
+ float4 source = simpleTex.Sample(sSampler, aInput.vTexCoords);
+
+ source *= ReadMask(aInput.vMaskCoords);
+
+ // Shortcut when the backdrop or source alpha is 0, otherwise we may leak
+ // infinity into the blend function and return incorrect results.
+ if (backdrop.a == 0.0) {
+ return source;
+ }
+ if (source.a == 0.0) {
+ return float4(0, 0, 0, 0);
+ }
+
+ // The spec assumes there is no premultiplied alpha. The backdrop and
+ // source are both render targets and always premultiplied, so we undo
+ // that here.
+ backdrop.rgb /= backdrop.a;
+ source.rgb /= source.a;
+
+ float4 result;
+ result.rgb = BlendSaturation(backdrop.rgb, source.rgb);
+ result.a = source.a;
+
+ // Factor backdrop alpha, then premultiply for the final OP_OVER.
+ result.rgb = (1.0 - backdrop.a) * source.rgb + backdrop.a * result.rgb;
+ result.rgb *= result.a;
+ return result;
+}
+
+float4 BlendColorPS(const VS_BLEND_OUTPUT aInput) : SV_Target
+{
+ if (!RectContainsPoint(aInput.vClipRect, aInput.vPosition.xy)) {
+ discard;
+ }
+
+ float4 backdrop = tBackdrop.Sample(sSampler, aInput.vBackdropCoords);
+ float4 source = simpleTex.Sample(sSampler, aInput.vTexCoords);
+
+ source *= ReadMask(aInput.vMaskCoords);
+
+ // Shortcut when the backdrop or source alpha is 0, otherwise we may leak
+ // infinity into the blend function and return incorrect results.
+ if (backdrop.a == 0.0) {
+ return source;
+ }
+ if (source.a == 0.0) {
+ return float4(0, 0, 0, 0);
+ }
+
+ // The spec assumes there is no premultiplied alpha. The backdrop and
+ // source are both render targets and always premultiplied, so we undo
+ // that here.
+ backdrop.rgb /= backdrop.a;
+ source.rgb /= source.a;
+
+ float4 result;
+ result.rgb = BlendColor(backdrop.rgb, source.rgb);
+ result.a = source.a;
+
+ // Factor backdrop alpha, then premultiply for the final OP_OVER.
+ result.rgb = (1.0 - backdrop.a) * source.rgb + backdrop.a * result.rgb;
+ result.rgb *= result.a;
+ return result;
+}
+
+float4 BlendLuminosityPS(const VS_BLEND_OUTPUT aInput) : SV_Target
+{
+ if (!RectContainsPoint(aInput.vClipRect, aInput.vPosition.xy)) {
+ discard;
+ }
+
+ float4 backdrop = tBackdrop.Sample(sSampler, aInput.vBackdropCoords);
+ float4 source = simpleTex.Sample(sSampler, aInput.vTexCoords);
+
+ source *= ReadMask(aInput.vMaskCoords);
+
+ // Shortcut when the backdrop or source alpha is 0, otherwise we may leak
+ // infinity into the blend function and return incorrect results.
+ if (backdrop.a == 0.0) {
+ return source;
+ }
+ if (source.a == 0.0) {
+ return float4(0, 0, 0, 0);
+ }
+
+ // The spec assumes there is no premultiplied alpha. The backdrop and
+ // source are both render targets and always premultiplied, so we undo
+ // that here.
+ backdrop.rgb /= backdrop.a;
+ source.rgb /= source.a;
+
+ float4 result;
+ result.rgb = BlendLuminosity(backdrop.rgb, source.rgb);
+ result.a = source.a;
+
+ // Factor backdrop alpha, then premultiply for the final OP_OVER.
+ result.rgb = (1.0 - backdrop.a) * source.rgb + backdrop.a * result.rgb;
+ result.rgb *= result.a;
+ return result;
+}
+
diff --git a/gfx/layers/d3d11/mlgshaders/blend-ps-generated.hlslh.tpl b/gfx/layers/d3d11/mlgshaders/blend-ps-generated.hlslh.tpl
new file mode 100644
index 0000000000..a24577a09a
--- /dev/null
+++ b/gfx/layers/d3d11/mlgshaders/blend-ps-generated.hlslh.tpl
@@ -0,0 +1,36 @@
+float4 Blend{BLEND_FUNC}PS(const VS_BLEND_OUTPUT aInput) : SV_Target
+{
+ if (!RectContainsPoint(aInput.vClipRect, aInput.vPosition.xy)) {
+ discard;
+ }
+
+ float4 backdrop = tBackdrop.Sample(sSampler, aInput.vBackdropCoords);
+ float4 source = simpleTex.Sample(sSampler, aInput.vTexCoords);
+
+ // Apply masks to the source texture, not the result.
+ source *= ReadMask(aInput.vMaskCoords);
+
+ // Shortcut when the backdrop or source alpha is 0, otherwise we may leak
+ // infinity into the blend function and return incorrect results.
+ if (backdrop.a == 0.0) {
+ return source;
+ }
+ if (source.a == 0.0) {
+ return float4(0, 0, 0, 0);
+ }
+
+ // The spec assumes there is no premultiplied alpha. The backdrop and
+ // source are both render targets and always premultiplied, so we undo
+ // that here.
+ backdrop.rgb /= backdrop.a;
+ source.rgb /= source.a;
+
+ float4 result;
+ result.rgb = Blend{BLEND_FUNC}(backdrop.rgb, source.rgb);
+ result.a = source.a;
+
+ // Factor backdrop alpha, then premultiply for the final OP_OVER.
+ result.rgb = (1.0 - backdrop.a) * source.rgb + backdrop.a * result.rgb;
+ result.rgb *= result.a;
+ return result;
+}
diff --git a/gfx/layers/d3d11/mlgshaders/blend-ps.hlsl b/gfx/layers/d3d11/mlgshaders/blend-ps.hlsl
new file mode 100644
index 0000000000..094bfdfdb1
--- /dev/null
+++ b/gfx/layers/d3d11/mlgshaders/blend-ps.hlsl
@@ -0,0 +1,13 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * 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 "common.hlsl"
+#include "common-ps.hlsl"
+#include "blend-common.hlsl"
+#include "../BlendingHelpers.hlslh"
+
+Texture2D simpleTex : register(ps, t0);
+Texture2D tBackdrop : register(ps, t1);
+
+#include "blend-ps-generated.hlslh"
diff --git a/gfx/layers/d3d11/mlgshaders/blend-vs.hlsl b/gfx/layers/d3d11/mlgshaders/blend-vs.hlsl
new file mode 100644
index 0000000000..db873e34a2
--- /dev/null
+++ b/gfx/layers/d3d11/mlgshaders/blend-vs.hlsl
@@ -0,0 +1,52 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * 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 "common-vs.hlsl"
+#include "blend-common.hlsl"
+#include "textured-common.hlsl"
+
+cbuffer BlendConstants : register(b4)
+{
+ float4x4 mBackdropTransform;
+}
+
+float2 BackdropPosition(float4 aPosition)
+{
+ // Move the position from clip space (-1,1) into 0..1 space.
+ float2 pos;
+ pos.x = (aPosition.x + 1.0) / 2.0;
+ pos.y = 1.0 - (aPosition.y + 1.0) / 2.0;
+
+ return mul(mBackdropTransform, float4(pos.xy, 0, 1.0)).xy;
+}
+
+VS_BLEND_OUTPUT BlendImpl(const VertexInfo aInfo, float2 aTexCoord)
+{
+ VS_BLEND_OUTPUT output;
+ output.vPosition = aInfo.worldPos;
+ output.vTexCoords = aTexCoord;
+ output.vBackdropCoords = BackdropPosition(output.vPosition);
+ output.vLocalPos = aInfo.screenPos;
+ output.vClipRect = aInfo.clipRect;
+ output.vMaskCoords = aInfo.maskCoords;
+ return output;
+}
+
+VS_BLEND_OUTPUT BlendVertexVS(const VS_TEXTUREDVERTEX aVertex)
+{
+ float2 layerPos = UnitTriangleToPos(
+ aVertex.vUnitPos,
+ aVertex.vPos1,
+ aVertex.vPos2,
+ aVertex.vPos3);
+
+ float2 texCoord = UnitTriangleToPos(
+ aVertex.vUnitPos,
+ aVertex.vTexCoord1,
+ aVertex.vTexCoord2,
+ aVertex.vTexCoord3);
+
+ VertexInfo info = ComputePosition(layerPos, aVertex.vLayerId, aVertex.vDepth);
+ return BlendImpl(info, texCoord);
+}
diff --git a/gfx/layers/d3d11/mlgshaders/clear-common.hlsl b/gfx/layers/d3d11/mlgshaders/clear-common.hlsl
new file mode 100644
index 0000000000..da69301d34
--- /dev/null
+++ b/gfx/layers/d3d11/mlgshaders/clear-common.hlsl
@@ -0,0 +1,9 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * 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/. */
+
+struct VS_CLEAR_OUT
+{
+ float4 vPosition : SV_Position;
+};
diff --git a/gfx/layers/d3d11/mlgshaders/clear-ps.hlsl b/gfx/layers/d3d11/mlgshaders/clear-ps.hlsl
new file mode 100644
index 0000000000..7313585eed
--- /dev/null
+++ b/gfx/layers/d3d11/mlgshaders/clear-ps.hlsl
@@ -0,0 +1,12 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * 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 "common-ps.hlsl"
+#include "clear-common.hlsl"
+
+float4 ClearPS(const VS_CLEAR_OUT aVS) : SV_Target
+{
+ return float4(0.0, 0.0, 0.0, 0.0);
+}
+
diff --git a/gfx/layers/d3d11/mlgshaders/clear-vs.hlsl b/gfx/layers/d3d11/mlgshaders/clear-vs.hlsl
new file mode 100644
index 0000000000..5d95fcf611
--- /dev/null
+++ b/gfx/layers/d3d11/mlgshaders/clear-vs.hlsl
@@ -0,0 +1,30 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * 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 "common-vs.hlsl"
+#include "clear-common.hlsl"
+
+struct VS_CLEAR_IN
+{
+ float2 vPos : POSITION;
+ int4 vRect : TEXCOORD0;
+};
+
+// Note: we use slot 2 so we don't have to rebind the layer slot (1) to run
+// this shader.
+cbuffer ClearConstants : register(b2) {
+ int sDepth : packoffset(c0.x);
+};
+
+VS_CLEAR_OUT ClearVS(const VS_CLEAR_IN aInput)
+{
+ float4 rect = float4(aInput.vRect.x, aInput.vRect.y, aInput.vRect.z, aInput.vRect.w);
+ float4 screenPos = float4(UnitQuadToRect(aInput.vPos, rect), 0, 1);
+ float4 worldPos = mul(WorldTransform, screenPos);
+ worldPos.z = ComputeDepth(worldPos, sDepth);
+
+ VS_CLEAR_OUT output;
+ output.vPosition = worldPos;
+ return output;
+}
diff --git a/gfx/layers/d3d11/mlgshaders/color-common.hlsl b/gfx/layers/d3d11/mlgshaders/color-common.hlsl
new file mode 100644
index 0000000000..1769cbed80
--- /dev/null
+++ b/gfx/layers/d3d11/mlgshaders/color-common.hlsl
@@ -0,0 +1,19 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * 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/. */
+
+struct VS_COLOROUTPUT
+{
+ nointerpolation float4 vColor : COLOR0;
+ nointerpolation float4 vClipRect : TEXCOORD0;
+ float4 vPosition : SV_Position;
+ float2 vLocalPos : TEXCOORD1;
+ float3 vMaskCoords : TEXCOORD2;
+};
+
+struct VS_COLOROUTPUT_CLIPPED
+{
+ float4 vPosition : SV_Position;
+ nointerpolation float4 vColor : COLOR0;
+};
diff --git a/gfx/layers/d3d11/mlgshaders/color-ps.hlsl b/gfx/layers/d3d11/mlgshaders/color-ps.hlsl
new file mode 100644
index 0000000000..4bbd545594
--- /dev/null
+++ b/gfx/layers/d3d11/mlgshaders/color-ps.hlsl
@@ -0,0 +1,20 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * 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 "common-ps.hlsl"
+#include "color-common.hlsl"
+
+float4 ColoredQuadPS(const VS_COLOROUTPUT_CLIPPED aVS) : SV_Target
+{
+ // Opacity is always 1.0, we premultiply it on the CPU.
+ return aVS.vColor;
+}
+
+float4 ColoredVertexPS(const VS_COLOROUTPUT aVS) : SV_Target
+{
+ if (!RectContainsPoint(aVS.vClipRect, aVS.vPosition.xy)) {
+ return float4(0, 0, 0, 0);
+ }
+ return aVS.vColor * ReadMaskWithOpacity(aVS.vMaskCoords, 1.0);
+}
diff --git a/gfx/layers/d3d11/mlgshaders/color-vs.hlsl b/gfx/layers/d3d11/mlgshaders/color-vs.hlsl
new file mode 100644
index 0000000000..31f5037daf
--- /dev/null
+++ b/gfx/layers/d3d11/mlgshaders/color-vs.hlsl
@@ -0,0 +1,58 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * 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 "common-vs.hlsl"
+#include "color-common.hlsl"
+
+struct VS_COLORQUAD
+{
+ float2 vPos : POSITION;
+ float4 vRect : TEXCOORD0;
+ uint vLayerId : TEXCOORD1;
+ int vDepth : TEXCOORD2;
+ float4 vColor : TEXCOORD3;
+};
+
+struct VS_COLORVERTEX
+{
+ float3 vUnitPos : POSITION0;
+ float2 vPos1 : POSITION1;
+ float2 vPos2 : POSITION2;
+ float2 vPos3 : POSITION3;
+ uint vLayerId : TEXCOORD0;
+ int vDepth : TEXCOORD1;
+ float4 vColor : TEXCOORD2;
+};
+
+VS_COLOROUTPUT ColorImpl(float4 aColor, const VertexInfo aInfo)
+{
+ VS_COLOROUTPUT output;
+ output.vPosition = aInfo.worldPos;
+ output.vLocalPos = aInfo.screenPos;
+ output.vColor = aColor;
+ output.vClipRect = aInfo.clipRect;
+ output.vMaskCoords = aInfo.maskCoords;
+ return output;
+}
+
+VS_COLOROUTPUT_CLIPPED ColoredQuadVS(const VS_COLORQUAD aInput)
+{
+ float4 worldPos = ComputeClippedPosition(
+ aInput.vPos,
+ aInput.vRect,
+ aInput.vLayerId,
+ aInput.vDepth);
+
+ VS_COLOROUTPUT_CLIPPED output;
+ output.vPosition = worldPos;
+ output.vColor = aInput.vColor;
+ return output;
+}
+
+VS_COLOROUTPUT ColoredVertexVS(const VS_COLORVERTEX aInput)
+{
+ float2 layerPos = UnitTriangleToPos(aInput.vUnitPos, aInput.vPos1, aInput.vPos2, aInput.vPos3);
+ VertexInfo info = ComputePosition(layerPos, aInput.vLayerId, aInput.vDepth);
+ return ColorImpl(aInput.vColor, info);
+}
diff --git a/gfx/layers/d3d11/mlgshaders/common-ps.hlsl b/gfx/layers/d3d11/mlgshaders/common-ps.hlsl
new file mode 100644
index 0000000000..624f87e2ce
--- /dev/null
+++ b/gfx/layers/d3d11/mlgshaders/common-ps.hlsl
@@ -0,0 +1,37 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * 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 "common.hlsl"
+
+sampler sSampler : register(ps, s0);
+sampler sMaskSampler : register(ps, s1);
+
+Texture2D tMaskTexture : register(ps, t4);
+
+cbuffer MaskInformation : register(b0)
+{
+ float sOpacity : packoffset(c0.x);
+ uint sHasMask : packoffset(c0.y);
+};
+
+float2 MaskCoordsToUV(float3 aMaskCoords)
+{
+ return aMaskCoords.xy / aMaskCoords.z;
+}
+
+float ReadMaskWithOpacity(float3 aMaskCoords, float aOpacity)
+{
+ if (!sHasMask) {
+ return aOpacity;
+ }
+
+ float2 uv = MaskCoordsToUV(aMaskCoords);
+ float r = tMaskTexture.Sample(sMaskSampler, uv).r;
+ return min(aOpacity, r);
+}
+
+float ReadMask(float3 aMaskCoords)
+{
+ return ReadMaskWithOpacity(aMaskCoords, sOpacity);
+}
diff --git a/gfx/layers/d3d11/mlgshaders/common-vs.hlsl b/gfx/layers/d3d11/mlgshaders/common-vs.hlsl
new file mode 100644
index 0000000000..6231b525a9
--- /dev/null
+++ b/gfx/layers/d3d11/mlgshaders/common-vs.hlsl
@@ -0,0 +1,167 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * 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_layers_d3d11_mlgshaders_common_vs_hlsl
+#define mozilla_gfx_layers_d3d11_mlgshaders_common_vs_hlsl
+
+#include "common.hlsl"
+
+cbuffer VSBufSimple : register(b0)
+{
+ float4x4 WorldTransform;
+ float2 RenderTargetOffset;
+ int SortIndexOffset;
+ uint DebugFrameNumber;
+};
+
+struct Layer {
+ float4x4 transform;
+ float4 clipRect;
+ uint4 info;
+};
+
+cbuffer Layers : register(b1)
+{
+ Layer sLayers[682];
+};
+
+cbuffer MaskRects : register(b3)
+{
+ float4 sMaskRects[4096];
+};
+
+struct VertexInfo {
+ float4 worldPos;
+ float2 screenPos;
+ float3 maskCoords;
+ float4 clipRect;
+};
+
+float3 ComputeMaskCoords(float4 aPosition, Layer aLayer)
+{
+ if (aLayer.info.x == 0) {
+ return float3(0.0, 0.0, 0.0);
+ }
+
+ float4 maskRect = sMaskRects[aLayer.info.x - 1];
+
+ // See the perspective comment in CompositorD3D11.hlsl.
+ float4x4 transform = float4x4(
+ 1.0/maskRect.z, 0.0, 0.0, -maskRect.x/maskRect.z,
+ 0.0, 1.0/maskRect.w, 0.0, -maskRect.y/maskRect.w,
+ 0.0, 0.0, 1.0, 0.0,
+ 0.0, 0.0, 0.0, 1.0);
+
+ return float3(mul(transform, aPosition / aPosition.w).xy, 1.0) * aPosition.w;
+}
+
+float2 UnitTriangleToPos(const float3 aVertex,
+ const float2 aPos1,
+ const float2 aPos2,
+ const float2 aPos3)
+{
+ return aVertex.x * aPos1 +
+ aVertex.y * aPos2 +
+ aVertex.z * aPos3;
+}
+
+float2 UnitQuadToRect(const float2 aVertex, const float4 aRect)
+{
+ return float2(aRect.x + aVertex.x * aRect.z, aRect.y + aVertex.y * aRect.w);
+}
+
+float ComputeDepth(float4 aPosition, float aSortIndex)
+{
+ // Note: this value should match ShaderDefinitionsMLGPU.h.
+ return ((aSortIndex + SortIndexOffset) / 1000000.0f) * aPosition.w;
+}
+
+// Compute the world-space, screen-space, layer-space clip, and mask
+// uv-coordinates given a layer-space vertex, id, and z-index.
+VertexInfo ComputePosition(float2 aVertex, uint aLayerId, float aSortIndex)
+{
+ Layer layer = sLayers[aLayerId];
+
+ // Translate from unit vertex to layer quad vertex.
+ float4 clipRect = layer.clipRect;
+
+ // Transform to screen coordinates.
+ float4x4 transform = layer.transform;
+ float4 layerPos = mul(transform, float4(aVertex, 0, 1));
+ float4 position = layerPos;
+ position.xyz /= position.w;
+ position.xy -= RenderTargetOffset.xy;
+ position.xyz *= position.w;
+
+ float4 worldPos = mul(WorldTransform, position);
+
+ // Depth must be computed after the world transform, since we don't want
+ // 3d transforms clobbering the z-value. We assume a viewport culling
+ // everything outside of [0, 1). Note that when depth-testing, we do not
+ // use sorting indices < 1.
+ //
+ // Note that we have to normalize this value to w=1, since the GPU will
+ // divide all values by w internally.
+ worldPos.z = ComputeDepth(worldPos, aSortIndex);
+
+ VertexInfo info;
+ info.screenPos = position.xy;
+ info.worldPos = worldPos;
+ info.maskCoords = ComputeMaskCoords(layerPos, layer);
+ info.clipRect = clipRect;
+ return info;
+}
+
+// This function takes a unit quad position and a layer rectangle, and computes
+// a clipped draw rect. It is only valid to use this function for layers with
+// rectilinear transforms that do not have masks.
+float4 ComputeClippedPosition(const float2 aVertex,
+ const float4 aRect,
+ uint aLayerId,
+ float aDepth)
+{
+ Layer layer = sLayers[aLayerId];
+
+ float4 position = float4(UnitQuadToRect(aVertex, aRect), 0, 1);
+
+ float4x4 transform = layer.transform;
+ float4 clipRect = layer.clipRect;
+
+ // Transform to screen coordinates.
+ //
+ // We clamp the draw rect to the clip. This lets us use faster shaders.
+ // For opaque shapes, it is necessary to do this anyway since we might
+ // otherwrite write transparent pixels in the pixel which would also be
+ // written to the depth buffer. We cannot use discard in the pixel shader
+ // as this would break early-z tests.
+ //
+ // Note that for some shaders, like textured shaders, it is not valid to
+ // change the draw rect like this without also clamping the texture
+ // coordinates. We take care to adjust for this in our batching code.
+ //
+ // We do not need to do this for 3D transforms since we always treat those
+ // as transparent (they are not written to the depth buffer). 3D items
+ // will always use the full clip+masking shader.
+ position = mul(transform, position);
+ position.xyz /= position.w;
+ position.xy -= RenderTargetOffset.xy;
+ position.xy = clamp(position.xy, clipRect.xy, clipRect.xy + clipRect.zw);
+ position.xyz *= position.w;
+
+ float4 worldPos = mul(WorldTransform, position);
+
+ // Depth must be computed after the world transform, since we don't want
+ // 3d transforms clobbering the z-value. We assume a viewport culling
+ // everything outside of [0, 1). Note that when depth-testing, we do not
+ // use sorting indices < 1.
+ //
+ // Note that we have to normalize this value to w=1, since the GPU will
+ // divide all values by w internally.
+ worldPos.z = ComputeDepth(worldPos, aDepth);
+
+ return worldPos;
+}
+
+#endif // mozilla_gfx_layers_d3d11_mlgshaders_common_vs_hlsl
diff --git a/gfx/layers/d3d11/mlgshaders/common.hlsl b/gfx/layers/d3d11/mlgshaders/common.hlsl
new file mode 100644
index 0000000000..8c51370d08
--- /dev/null
+++ b/gfx/layers/d3d11/mlgshaders/common.hlsl
@@ -0,0 +1,17 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * 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_layers_d3d11_mlgshaders_common_hlsl
+#define mozilla_gfx_layers_d3d11_mlgshaders_common_hlsl
+
+bool RectContainsPoint(float4 aRect, float2 aPoint)
+{
+ return aPoint.x >= aRect.x &&
+ aPoint.y >= aRect.y &&
+ aPoint.x <= (aRect.x + aRect.z) &&
+ aPoint.y <= (aRect.y + aRect.w);
+}
+
+#endif // mozilla_gfx_layers_d3d11_mlgshaders_common_hlsl
diff --git a/gfx/layers/d3d11/mlgshaders/component-alpha-ps.hlsl b/gfx/layers/d3d11/mlgshaders/component-alpha-ps.hlsl
new file mode 100644
index 0000000000..d1705dd1b9
--- /dev/null
+++ b/gfx/layers/d3d11/mlgshaders/component-alpha-ps.hlsl
@@ -0,0 +1,45 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * 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 "common.hlsl"
+#include "common-ps.hlsl"
+#include "textured-common.hlsl"
+
+Texture2D texOnBlack : register(ps, t0);
+Texture2D texOnWhite : register(ps, t1);
+
+struct PS_OUTPUT {
+ float4 vSrc;
+ float4 vAlpha;
+};
+
+PS_OUTPUT ComponentAlphaQuadPS(const VS_SAMPLEOUTPUT_CLIPPED aInput) : SV_Target
+{
+ PS_OUTPUT result;
+ result.vSrc = texOnBlack.Sample(sSampler, aInput.vTexCoords);
+ result.vAlpha = 1.0 - texOnWhite.Sample(sSampler, aInput.vTexCoords) + result.vSrc;
+ result.vSrc.a = result.vAlpha.g;
+ result.vSrc *= sOpacity;
+ result.vAlpha *= sOpacity;
+ return result;
+}
+
+PS_OUTPUT ComponentAlphaVertexPS(const VS_SAMPLEOUTPUT aInput) : SV_Target
+{
+ PS_OUTPUT result;
+ if (!RectContainsPoint(aInput.vClipRect, aInput.vPosition.xy)) {
+ result.vSrc = float4(0, 0, 0, 0);
+ result.vAlpha = float4(0, 0, 0, 0);
+ return result;
+ }
+
+ float alpha = ReadMask(aInput.vMaskCoords);
+
+ result.vSrc = texOnBlack.Sample(sSampler, aInput.vTexCoords);
+ result.vAlpha = 1.0 - texOnWhite.Sample(sSampler, aInput.vTexCoords) + result.vSrc;
+ result.vSrc.a = result.vAlpha.g;
+ result.vSrc *= alpha;
+ result.vAlpha *= alpha;
+ return result;
+}
diff --git a/gfx/layers/d3d11/mlgshaders/diagnostics-common.hlsl b/gfx/layers/d3d11/mlgshaders/diagnostics-common.hlsl
new file mode 100644
index 0000000000..49ddcb3d99
--- /dev/null
+++ b/gfx/layers/d3d11/mlgshaders/diagnostics-common.hlsl
@@ -0,0 +1,10 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * 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/. */
+
+struct VS_DIAGOUTPUT
+{
+ float4 vPosition : SV_Position;
+ float2 vTexCoord : TEXCOORD0;
+};
diff --git a/gfx/layers/d3d11/mlgshaders/diagnostics-ps.hlsl b/gfx/layers/d3d11/mlgshaders/diagnostics-ps.hlsl
new file mode 100644
index 0000000000..893e5e19b4
--- /dev/null
+++ b/gfx/layers/d3d11/mlgshaders/diagnostics-ps.hlsl
@@ -0,0 +1,13 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * 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 "common-ps.hlsl"
+#include "diagnostics-common.hlsl"
+
+Texture2D sTexture: register(ps, t0);
+
+float4 DiagnosticTextPS(const VS_DIAGOUTPUT aInput) : SV_Target
+{
+ return sTexture.Sample(sSampler, aInput.vTexCoord);
+}
diff --git a/gfx/layers/d3d11/mlgshaders/diagnostics-vs.hlsl b/gfx/layers/d3d11/mlgshaders/diagnostics-vs.hlsl
new file mode 100644
index 0000000000..e240b3c374
--- /dev/null
+++ b/gfx/layers/d3d11/mlgshaders/diagnostics-vs.hlsl
@@ -0,0 +1,25 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * 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 "common-vs.hlsl"
+#include "textured-common.hlsl"
+#include "diagnostics-common.hlsl"
+
+struct VS_DIAGINPUT
+{
+ float2 vPos : POSITION;
+ float4 vRect : TEXCOORD0;
+ float4 vTexCoords : TEXCOORD1;
+};
+
+VS_DIAGOUTPUT DiagnosticTextVS(const VS_DIAGINPUT aInput)
+{
+ float2 pos = UnitQuadToRect(aInput.vPos, aInput.vRect);
+ float2 texCoord = UnitQuadToRect(aInput.vPos, aInput.vTexCoords);
+
+ VS_DIAGOUTPUT output;
+ output.vPosition = mul(WorldTransform, float4(pos, 0, 1));
+ output.vTexCoord = texCoord;
+ return output;
+}
diff --git a/gfx/layers/d3d11/mlgshaders/mask-combiner-common.hlsl b/gfx/layers/d3d11/mlgshaders/mask-combiner-common.hlsl
new file mode 100644
index 0000000000..a1201f7833
--- /dev/null
+++ b/gfx/layers/d3d11/mlgshaders/mask-combiner-common.hlsl
@@ -0,0 +1,10 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * 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/. */
+
+struct VS_MASKOUTPUT
+{
+ float4 vPosition : SV_Position;
+ float2 vTexCoords : TEXCOORD0;
+};
diff --git a/gfx/layers/d3d11/mlgshaders/mask-combiner-ps.hlsl b/gfx/layers/d3d11/mlgshaders/mask-combiner-ps.hlsl
new file mode 100644
index 0000000000..35010aed6f
--- /dev/null
+++ b/gfx/layers/d3d11/mlgshaders/mask-combiner-ps.hlsl
@@ -0,0 +1,16 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * 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 "common.hlsl"
+#include "mask-combiner-common.hlsl"
+
+sampler sSampler;
+
+Texture2D tMaskTexture : register(ps, t0);
+
+float4 MaskCombinerPS(VS_MASKOUTPUT aInput) : SV_Target
+{
+ float4 value = tMaskTexture.Sample(sSampler, aInput.vTexCoords);
+ return float4(value.r, 0, 0, value.r);
+}
diff --git a/gfx/layers/d3d11/mlgshaders/mask-combiner-vs.hlsl b/gfx/layers/d3d11/mlgshaders/mask-combiner-vs.hlsl
new file mode 100644
index 0000000000..7db1d5edca
--- /dev/null
+++ b/gfx/layers/d3d11/mlgshaders/mask-combiner-vs.hlsl
@@ -0,0 +1,26 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * 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 "common-vs.hlsl"
+#include "mask-combiner-common.hlsl"
+
+struct VS_MASKINPUT
+{
+ // Note, the input is
+ float2 vPos : POSITION;
+ float4 vTexCoords : POSITION1;
+};
+
+VS_MASKOUTPUT MaskCombinerVS(VS_MASKINPUT aInput)
+{
+ float4 position = float4(
+ aInput.vPos.x * 2.0f - 1.0f,
+ 1.0f - (aInput.vPos.y * 2.0f),
+ 0, 1);
+
+ VS_MASKOUTPUT output;
+ output.vPosition = position;
+ output.vTexCoords = UnitQuadToRect(aInput.vPos, aInput.vTexCoords);
+ return output;
+}
diff --git a/gfx/layers/d3d11/mlgshaders/shaders.manifest b/gfx/layers/d3d11/mlgshaders/shaders.manifest
new file mode 100644
index 0000000000..3c6b41a2c9
--- /dev/null
+++ b/gfx/layers/d3d11/mlgshaders/shaders.manifest
@@ -0,0 +1,100 @@
+- type: vs_4_0
+ file: textured-vs.hlsl
+ shaders:
+ - TexturedQuadVS
+ - TexturedVertexVS
+
+- type: ps_4_0
+ file: textured-ps.hlsl
+ shaders:
+ - TexturedVertexRGB
+ - TexturedVertexRGBA
+ - TexturedQuadRGB
+ - TexturedQuadRGBA
+
+- type: ps_4_0
+ file: ycbcr-ps.hlsl
+ shaders:
+ - TexturedVertexIMC4
+ - TexturedVertexNV12
+ - TexturedQuadIMC4
+ - TexturedQuadNV12
+ - TexturedVertexIdentityIMC4
+ - TexturedQuadIdentityIMC4
+
+- type: vs_4_0
+ file: color-vs.hlsl
+ shaders:
+ - ColoredQuadVS
+ - ColoredVertexVS
+
+- type: ps_4_0
+ file: color-ps.hlsl
+ shaders:
+ - ColoredQuadPS
+ - ColoredVertexPS
+
+- type: ps_4_0
+ file: component-alpha-ps.hlsl
+ shaders:
+ - ComponentAlphaQuadPS
+ - ComponentAlphaVertexPS
+
+- type: vs_4_0
+ file: blend-vs.hlsl
+ shaders:
+ - BlendVertexVS
+
+- type: ps_4_0
+ file: blend-ps.hlsl
+ shaders:
+ - BlendMultiplyPS
+ - BlendScreenPS
+ - BlendOverlayPS
+ - BlendDarkenPS
+ - BlendLightenPS
+ - BlendColorDodgePS
+ - BlendColorBurnPS
+ - BlendHardLightPS
+ - BlendSoftLightPS
+ - BlendDifferencePS
+ - BlendExclusionPS
+ - BlendHuePS
+ - BlendSaturationPS
+ - BlendColorPS
+ - BlendLuminosityPS
+
+- type: vs_4_0
+ file: clear-vs.hlsl
+ shaders:
+ - ClearVS
+
+- type: ps_4_0
+ file: clear-ps.hlsl
+ shaders:
+ - ClearPS
+
+- type: vs_4_0
+ file: mask-combiner-vs.hlsl
+ shaders:
+ - MaskCombinerVS
+
+- type: ps_4_0
+ file: mask-combiner-ps.hlsl
+ shaders:
+ - MaskCombinerPS
+
+- type: vs_4_0
+ file: diagnostics-vs.hlsl
+ shaders:
+ - DiagnosticTextVS
+
+- type: ps_4_0
+ file: diagnostics-ps.hlsl
+ shaders:
+ - DiagnosticTextPS
+
+- type: vs_4_0
+ file: test-features-vs.hlsl
+ shaders:
+ - TestConstantBuffersVS
diff --git a/gfx/layers/d3d11/mlgshaders/test-features-vs.hlsl b/gfx/layers/d3d11/mlgshaders/test-features-vs.hlsl
new file mode 100644
index 0000000000..d22de5e3e4
--- /dev/null
+++ b/gfx/layers/d3d11/mlgshaders/test-features-vs.hlsl
@@ -0,0 +1,32 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * 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 "common-vs.hlsl"
+#include "color-common.hlsl"
+
+struct VS_INPUT {
+ float2 vPos : POSITION;
+};
+
+cbuffer Buffer0 : register(b0) {
+ float4 aValue0;
+};
+cbuffer Buffer1 : register(b1) {
+ float4 aValue1;
+};
+cbuffer Buffer2 : register(b2) {
+ float4 aValue2;
+};
+
+VS_COLOROUTPUT_CLIPPED TestConstantBuffersVS(VS_INPUT aInput)
+{
+ // Draw to the entire viewport.
+ float2 pos = UnitQuadToRect(aInput.vPos, float4(-1, -1, 2, 2));
+
+ VS_COLOROUTPUT_CLIPPED output;
+ output.vPosition = float4(pos, 0, 1);
+ output.vColor = float4(aValue0.r, aValue1.g, aValue2.b, 1.0);
+ return output;
+}
diff --git a/gfx/layers/d3d11/mlgshaders/textured-common.hlsl b/gfx/layers/d3d11/mlgshaders/textured-common.hlsl
new file mode 100644
index 0000000000..27c2b32198
--- /dev/null
+++ b/gfx/layers/d3d11/mlgshaders/textured-common.hlsl
@@ -0,0 +1,43 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * 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/. */
+
+// Instanced version.
+struct VS_TEXTUREDINPUT
+{
+ float2 vPos : POSITION;
+ float4 vRect : TEXCOORD0;
+ uint vLayerId : TEXCOORD1;
+ int vDepth : TEXCOORD2;
+ float4 vTexRect : TEXCOORD3;
+};
+
+// Non-instanced version.
+struct VS_TEXTUREDVERTEX
+{
+ float3 vUnitPos : POSITION0;
+ float2 vPos1: POSITION1;
+ float2 vPos2: POSITION2;
+ float2 vPos3: POSITION3;
+ uint vLayerId : TEXCOORD0;
+ int vDepth : TEXCOORD1;
+ float2 vTexCoord1 : TEXCOORD2;
+ float2 vTexCoord2 : TEXCOORD3;
+ float2 vTexCoord3 : TEXCOORD4;
+};
+
+struct VS_SAMPLEOUTPUT
+{
+ float4 vPosition : SV_Position;
+ float2 vTexCoords : TEXCOORD0;
+ float2 vLocalPos : TEXCOORD1;
+ float3 vMaskCoords : TEXCOORD2;
+ nointerpolation float4 vClipRect : TEXCOORD3;
+};
+
+struct VS_SAMPLEOUTPUT_CLIPPED
+{
+ float4 vPosition : SV_Position;
+ float2 vTexCoords : TEXCOORD0;
+};
diff --git a/gfx/layers/d3d11/mlgshaders/textured-ps.hlsl b/gfx/layers/d3d11/mlgshaders/textured-ps.hlsl
new file mode 100644
index 0000000000..e00f6cbc44
--- /dev/null
+++ b/gfx/layers/d3d11/mlgshaders/textured-ps.hlsl
@@ -0,0 +1,45 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * 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 "common.hlsl"
+#include "common-ps.hlsl"
+#include "textured-common.hlsl"
+
+Texture2D simpleTex : register(ps, t0);
+
+float4 FixRGBOpacity(float4 color, float alpha) {
+ return float4(color.rgb * alpha, alpha);
+}
+
+// Fast cases that don't require complex clipping.
+float4 TexturedQuadRGBA(const VS_SAMPLEOUTPUT_CLIPPED aInput) : SV_Target
+{
+ return simpleTex.Sample(sSampler, aInput.vTexCoords) * sOpacity;
+}
+float4 TexturedQuadRGB(const VS_SAMPLEOUTPUT_CLIPPED aInput) : SV_Target
+{
+ return FixRGBOpacity(simpleTex.Sample(sSampler, aInput.vTexCoords), sOpacity);
+}
+
+// PaintedLayer common case.
+float4 TexturedVertexRGBA(const VS_SAMPLEOUTPUT aInput) : SV_Target
+{
+ if (!RectContainsPoint(aInput.vClipRect, aInput.vPosition.xy)) {
+ return float4(0, 0, 0, 0);
+ }
+
+ float alpha = ReadMask(aInput.vMaskCoords);
+ return simpleTex.Sample(sSampler, aInput.vTexCoords) * alpha;
+}
+
+// ImageLayers.
+float4 TexturedVertexRGB(const VS_SAMPLEOUTPUT aInput) : SV_Target
+{
+ if (!RectContainsPoint(aInput.vClipRect, aInput.vPosition.xy)) {
+ return float4(0, 0, 0, 0);
+ }
+
+ float alpha = ReadMask(aInput.vMaskCoords);
+ return FixRGBOpacity(simpleTex.Sample(sSampler, aInput.vTexCoords), alpha);
+}
diff --git a/gfx/layers/d3d11/mlgshaders/textured-vs.hlsl b/gfx/layers/d3d11/mlgshaders/textured-vs.hlsl
new file mode 100644
index 0000000000..9475ec048d
--- /dev/null
+++ b/gfx/layers/d3d11/mlgshaders/textured-vs.hlsl
@@ -0,0 +1,49 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * 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 "common-vs.hlsl"
+#include "textured-common.hlsl"
+
+VS_SAMPLEOUTPUT TexturedQuadImpl(const VertexInfo aInfo, const float2 aTexCoord)
+{
+ VS_SAMPLEOUTPUT output;
+ output.vPosition = aInfo.worldPos;
+ output.vTexCoords = aTexCoord;
+ output.vLocalPos = aInfo.screenPos;
+ output.vClipRect = aInfo.clipRect;
+ output.vMaskCoords = aInfo.maskCoords;
+ return output;
+}
+
+VS_SAMPLEOUTPUT_CLIPPED TexturedQuadVS(const VS_TEXTUREDINPUT aVertex)
+{
+ float4 worldPos = ComputeClippedPosition(
+ aVertex.vPos,
+ aVertex.vRect,
+ aVertex.vLayerId,
+ aVertex.vDepth);
+
+ VS_SAMPLEOUTPUT_CLIPPED output;
+ output.vPosition = worldPos;
+ output.vTexCoords = UnitQuadToRect(aVertex.vPos, aVertex.vTexRect);
+ return output;
+}
+
+VS_SAMPLEOUTPUT TexturedVertexVS(const VS_TEXTUREDVERTEX aVertex)
+{
+ float2 layerPos = UnitTriangleToPos(
+ aVertex.vUnitPos,
+ aVertex.vPos1,
+ aVertex.vPos2,
+ aVertex.vPos3);
+
+ float2 texCoord = UnitTriangleToPos(
+ aVertex.vUnitPos,
+ aVertex.vTexCoord1,
+ aVertex.vTexCoord2,
+ aVertex.vTexCoord3);
+
+ VertexInfo info = ComputePosition(layerPos, aVertex.vLayerId, aVertex.vDepth);
+ return TexturedQuadImpl(info, texCoord);
+}
diff --git a/gfx/layers/d3d11/mlgshaders/ycbcr-ps.hlsl b/gfx/layers/d3d11/mlgshaders/ycbcr-ps.hlsl
new file mode 100644
index 0000000000..1a30e6cb5a
--- /dev/null
+++ b/gfx/layers/d3d11/mlgshaders/ycbcr-ps.hlsl
@@ -0,0 +1,118 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * 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 "common-ps.hlsl"
+#include "textured-common.hlsl"
+
+Texture2D tY : register(ps, t0);
+Texture2D tCb : register(ps, t1);
+Texture2D tCr : register(ps, t2);
+
+cbuffer YCbCrBuffer : register(b1) {
+ row_major float3x3 YuvColorMatrix;
+};
+
+cbuffer vCoefficientBuffer : register(b2) {
+ float vCoefficient;
+}
+
+/* From Rec601:
+[R] [1.1643835616438356, 0.0, 1.5960267857142858] [ Y - 16]
+[G] = [1.1643835616438358, -0.3917622900949137, -0.8129676472377708] x [Cb - 128]
+[B] [1.1643835616438356, 2.017232142857143, 8.862867620416422e-17] [Cr - 128]
+
+For [0,1] instead of [0,255], and to 5 places:
+[R] [1.16438, 0.00000, 1.59603] [ Y - 0.06275]
+[G] = [1.16438, -0.39176, -0.81297] x [Cb - 0.50196]
+[B] [1.16438, 2.01723, 0.00000] [Cr - 0.50196]
+
+From Rec709:
+[R] [1.1643835616438356, 4.2781193979771426e-17, 1.7927410714285714] [ Y - 16]
+[G] = [1.1643835616438358, -0.21324861427372963, -0.532909328559444] x [Cb - 128]
+[B] [1.1643835616438356, 2.1124017857142854, 0.0] [Cr - 128]
+
+For [0,1] instead of [0,255], and to 5 places:
+[R] [1.16438, 0.00000, 1.79274] [ Y - 0.06275]
+[G] = [1.16438, -0.21325, -0.53291] x [Cb - 0.50196]
+[B] [1.16438, 2.11240, 0.00000] [Cr - 0.50196]
+*/
+float4 CalculateYCbCrColor(float3 rgb)
+{
+ return float4(
+ mul(YuvColorMatrix,
+ float3(
+ rgb.r - 0.06275,
+ rgb.g - 0.50196,
+ rgb.b - 0.50196)),
+ 1.0);
+}
+
+float4 CalculateIMC4Color(const float2 aTexCoords)
+{
+ float3 yuv = float3(
+ tY.Sample(sSampler, aTexCoords).r,
+ tCb.Sample(sSampler, aTexCoords).r,
+ tCr.Sample(sSampler, aTexCoords).r);
+ return CalculateYCbCrColor(yuv * vCoefficient);
+}
+
+float4 CalculateNV12Color(const float2 aTexCoords)
+{
+ float y = tY.Sample(sSampler, aTexCoords).r;
+ float2 cbcr = tCb.Sample(sSampler, aTexCoords).rg;
+ return CalculateYCbCrColor(float3(y, cbcr) * vCoefficient);
+}
+
+float4 TexturedQuadIMC4(const VS_SAMPLEOUTPUT_CLIPPED aInput) : SV_Target
+{
+ return CalculateIMC4Color(aInput.vTexCoords) * sOpacity;
+}
+
+float4 TexturedQuadNV12(const VS_SAMPLEOUTPUT_CLIPPED aInput) : SV_Target
+{
+ return CalculateNV12Color(aInput.vTexCoords) * sOpacity;
+}
+
+float4 TexturedVertexIMC4(const VS_SAMPLEOUTPUT aInput) : SV_Target
+{
+ if (!RectContainsPoint(aInput.vClipRect, aInput.vPosition.xy)) {
+ return float4(0, 0, 0, 0);
+ }
+
+ float alpha = ReadMask(aInput.vMaskCoords);
+ return CalculateIMC4Color(aInput.vTexCoords) * alpha;
+}
+
+float4 TexturedVertexNV12(const VS_SAMPLEOUTPUT aInput) : SV_Target
+{
+ if (!RectContainsPoint(aInput.vClipRect, aInput.vPosition.xy)) {
+ return float4(0, 0, 0, 0);
+ }
+
+ float alpha = ReadMask(aInput.vMaskCoords);
+ return CalculateNV12Color(aInput.vTexCoords) * alpha;
+}
+
+float4 TexturedQuadIdentityIMC4(const VS_SAMPLEOUTPUT_CLIPPED aInput) : SV_Target
+{
+ float3 rgb = float3(
+ tCr.Sample(sSampler, aInput.vTexCoords).r,
+ tY.Sample(sSampler, aInput.vTexCoords).r,
+ tCb.Sample(sSampler, aInput.vTexCoords).r);
+ return float4(rgb * vCoefficient, 1.0) * sOpacity;
+}
+
+float4 TexturedVertexIdentityIMC4(const VS_SAMPLEOUTPUT aInput) : SV_Target
+{
+ if (!RectContainsPoint(aInput.vClipRect, aInput.vPosition.xy)) {
+ return float4(0, 0, 0, 0);
+ }
+
+ float alpha = ReadMask(aInput.vMaskCoords);
+ float3 rgb = float3(
+ tCr.Sample(sSampler, aInput.vTexCoords).r,
+ tY.Sample(sSampler, aInput.vTexCoords).r,
+ tCb.Sample(sSampler, aInput.vTexCoords).r);
+ return float4(rgb * vCoefficient, 1.0) * alpha;
+}
diff --git a/gfx/layers/d3d11/shaders.manifest b/gfx/layers/d3d11/shaders.manifest
new file mode 100644
index 0000000000..f1f43dd23a
--- /dev/null
+++ b/gfx/layers/d3d11/shaders.manifest
@@ -0,0 +1,28 @@
+- type: vs_4_0_level_9_3
+ file: CompositorD3D11.hlsl
+ shaders:
+ - LayerQuadVS
+ - LayerDynamicVS
+ - LayerQuadMaskVS
+ - LayerDynamicMaskVS
+ - LayerQuadBlendVS
+ - LayerQuadBlendMaskVS
+ - LayerDynamicBlendVS
+ - LayerDynamicBlendMaskVS
+
+- type: ps_4_0_level_9_3
+ file: CompositorD3D11.hlsl
+ shaders:
+ - SolidColorShader
+ - RGBShader
+ - RGBAShader
+ - ComponentAlphaShader
+ - YCbCrShader
+ - NV12Shader
+ - SolidColorShaderMask
+ - RGBShaderMask
+ - RGBAShaderMask
+ - YCbCrShaderMask
+ - NV12ShaderMask
+ - ComponentAlphaShaderMask
+ - BlendShader