diff options
Diffstat (limited to '')
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, ©Box); + } + } + + 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(¶ms); + 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, + ¶ms); + + 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(¶ms); + params.DirtyRectsCount = mBackBufferInvalid.GetNumRects(); + params.pDirtyRects = rects.data(); + hr = mSwapChain1->Present1(0, 0, ¶ms); + } 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(¤tDesc); + + // 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 |