diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 00:47:55 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 00:47:55 +0000 |
commit | 26a029d407be480d791972afb5975cf62c9360a6 (patch) | |
tree | f435a8308119effd964b339f76abb83a57c29483 /gfx/layers/d3d11 | |
parent | Initial commit. (diff) | |
download | firefox-26a029d407be480d791972afb5975cf62c9360a6.tar.xz firefox-26a029d407be480d791972afb5975cf62c9360a6.zip |
Adding upstream version 124.0.1.upstream/124.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'gfx/layers/d3d11')
20 files changed, 6341 insertions, 0 deletions
diff --git a/gfx/layers/d3d11/BlendShaderConstants.h b/gfx/layers/d3d11/BlendShaderConstants.h new file mode 100644 index 0000000000..4c7493b6db --- /dev/null +++ b/gfx/layers/d3d11/BlendShaderConstants.h @@ -0,0 +1,18 @@ +/* -*- 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 + +#endif // MOZILLA_GFX_LAYERS_D3D11_BLENDSHADERCONSTANTS_H_ diff --git a/gfx/layers/d3d11/CompositorD3D11.cpp b/gfx/layers/d3d11/CompositorD3D11.cpp new file mode 100644 index 0000000000..a63e4218ea --- /dev/null +++ b/gfx/layers/d3d11/CompositorD3D11.cpp @@ -0,0 +1,1390 @@ +/* -*- 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 "mozilla/gfx/D3D11Checks.h" +#include "mozilla/gfx/DeviceManagerDx.h" +#include "mozilla/gfx/GPUParent.h" +#include "mozilla/gfx/Swizzle.h" +#include "mozilla/layers/Diagnostics.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/ProfilerState.h" +#include "mozilla/StaticPrefs_gfx.h" +#include "mozilla/StaticPrefs_layers.h" +#include "mozilla/Telemetry.h" + +#include "D3D11ShareHandleImage.h" +#include "DeviceAttachmentsD3D11.h" +#include "BlendShaderConstants.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(widget::CompositorWidget* aWidget) + : Compositor(aWidget), + mWindowRTCopy(nullptr), + mAttachments(nullptr), + mHwnd(nullptr), + mDisableSequenceForNextFrame(false), + mAllowPartialPresents(false), + mIsDoubleBuffered(false), + mVerifyBuffersFailed(false), + mUseMutexOnPresent(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::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; + } + + 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 (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. + 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) { + 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(); +} + +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; +} + +bool CompositorD3D11::ShouldAllowFrameRecording() const { + return mAllowFrameRecording || + profiler_feature_active(ProfilerFeature::Screenshots); +} + +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; +} + +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) { + switch (aEffect->mType) { + case EffectTypes::RGB: { + SurfaceFormat format = + static_cast<TexturedEffect*>(aEffect)->mTexture->GetFormat(); + return (format == SurfaceFormat::B8G8R8A8 || + format == SurfaceFormat::R8G8B8A8) + ? mAttachments->mRGBAShader + : mAttachments->mRGBShader; + } + case EffectTypes::NV12: + return mAttachments->mNV12Shader; + case EffectTypes::YCBCR: + return mAttachments->mYCbCrShader; + 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, nullptr, 0); + + mContext->PSSetShader(mAttachments->mSolidColorShader, 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); +} + +void CompositorD3D11::PrepareStaticVertexBuffer() { + mContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP); + mContext->IASetInputLayout(mAttachments->mInputLayout); + SetVertexBuffer<Vertex>(mAttachments->mVertexBuffer); +} + +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); + } +} + +void CompositorD3D11::DrawQuad(const gfx::Rect& aRect, + const gfx::IntRect& aClipRect, + const EffectChain& aEffectChain, + gfx::Float aOpacity, + const gfx::Matrix4x4& aTransform, + const gfx::Rect& aVisibleRect) { + if (mCurrentClip.IsEmpty()) { + return; + } + + MOZ_ASSERT(mCurrentRT, "No render target"); + + memcpy(&mVSConstants.layerTransform, &aTransform._11, 64); + IntPoint origin = mCurrentRT->GetOrigin(); + mVSConstants.renderTargetOffset[0] = origin.x; + mVSConstants.renderTargetOffset[1] = origin.y; + + mPSConstants.layerOpacity[0] = aOpacity; + + 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 restoreBlendMode = false; + + mContext->RSSetScissorRects(1, &scissor); + + RefPtr<ID3D11VertexShader> vertexShader = mAttachments->mVSQuadShader; + + RefPtr<ID3D11PixelShader> pixelShader = + GetPSForEffect(aEffectChain.mPrimaryEffect); + + mContext->VSSetShader(vertexShader, nullptr, 0); + mContext->PSSetShader(pixelShader, nullptr, 0); + + const Rect* pTexCoordRect = nullptr; + + switch (aEffectChain.mPrimaryEffect->mType) { + case EffectTypes::RGB: { + 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->mPremultipliedCopy) { + MOZ_RELEASE_ASSERT(texturedEffect->mPremultiplied); + mContext->OMSetBlendState(mAttachments->mPremulCopyState, sBlendFactor, + 0xFFFFFFFF); + restoreBlendMode = true; + } else 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; + default: + NS_WARNING("Unknown shader type"); + return; + } + + Draw(aRect, 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::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. + return Nothing(); + } + + if (mDevice->GetDeviceRemovedReason() != S_OK) { + if (!mAttachments->IsDeviceReset()) { + gfxCriticalNote << "GFX: D3D11 skip BeginFrame with device-removed."; + 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) { + 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(); + } + } + + return Some(rect); +} + +void CompositorD3D11::EndFrame() { + if (!profiler_feature_active(ProfilerFeature::Screenshots) && mWindowRTCopy) { + mWindowRTCopy = nullptr; + } + + 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(); + } + + // 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::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) { + // 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::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 (((static_cast<int32_t>(swapDesc.BufferDesc.Width) == mSize.width && + static_cast<int32_t>(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..d8cdd2d2f8 --- /dev/null +++ b/gfx/layers/d3d11/CompositorD3D11.h @@ -0,0 +1,227 @@ +/* -*- 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 CompositorD3D11 : public Compositor { + public: + explicit CompositorD3D11(widget::CompositorWidget* aWidget); + virtual ~CompositorD3D11(); + + CompositorD3D11* AsCompositorD3D11() override { return this; } + + bool Initialize(nsCString* const out_failureReason) override; + + already_AddRefed<DataTextureSource> CreateDataTextureSource( + TextureFlags aFlags = TextureFlags::NO_FLAGS) override; + + int32_t GetMaxTextureSize() const final; + + already_AddRefed<CompositingRenderTarget> CreateRenderTarget( + const gfx::IntRect& aRect, SurfaceInitMode aInit) 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); + + 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; + + /** + * 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); + +#ifdef MOZ_DUMP_PAINTING + const char* Name() const override { return "Direct3D 11"; } +#endif + + // 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(); + + 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); + 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); + + void PrepareStaticVertexBuffer(); + + void Draw(const gfx::Rect& aGeometry, const gfx::Rect* aTexCoords); + + void Present(); + + 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; + + 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; +}; + +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..4e13011782 --- /dev/null +++ b/gfx/layers/d3d11/CompositorD3D11.hlsl @@ -0,0 +1,186 @@ +/* -*- 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 "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); + +float4 fLayerColor : register(ps, c0); +float fLayerOpacity : register(ps, c1); + +// x = layer type +// y = mask type +// z = blend op +// w = is premultiplied + +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); + +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 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; +} + +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; +} + +/* 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 SolidColorShader(const VS_OUTPUT aVertex) : SV_Target +{ + return fLayerColor; +} + +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; +} diff --git a/gfx/layers/d3d11/DeviceAttachmentsD3D11.cpp b/gfx/layers/d3d11/DeviceAttachmentsD3D11.cpp new file mode 100644 index 0000000000..3a80e30cc4 --- /dev/null +++ b/gfx/layers/d3d11/DeviceAttachmentsD3D11.cpp @@ -0,0 +1,249 @@ +/* -*- 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/gfx/Logging.h" +#include "mozilla/layers/Compositor.h" +#include "CompositorD3D11Shaders.h" +#include "ShaderDefinitionsD3D11.h" + +namespace mozilla { +namespace layers { + +using namespace gfx; + +DeviceAttachmentsD3D11::DeviceAttachmentsD3D11(ID3D11Device* device) + : 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; + } + 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 rtCopyPremul = {TRUE, + D3D11_BLEND_ONE, + D3D11_BLEND_ZERO, + D3D11_BLEND_OP_ADD, + D3D11_BLEND_ONE, + D3D11_BLEND_ZERO, + D3D11_BLEND_OP_ADD, + D3D11_COLOR_WRITE_ENABLE_ALL}; + blendDesc.RenderTarget[0] = rtCopyPremul; + hr = mDevice->CreateBlendState(&blendDesc, getter_AddRefs(mPremulCopyState)); + if (Failed(hr, "create pm copy blender")) { + mInitFailureId = "FEATURE_FAILURE_D3D11_PM_COPY_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; + } + + 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::CreateShaders() { + InitVertexShader(sLayerQuadVS, mVSQuadShader); + + InitPixelShader(sSolidColorShader, mSolidColorShader); + InitPixelShader(sRGBShader, mRGBShader); + InitPixelShader(sRGBAShader, mRGBAShader); + InitPixelShader(sYCbCrShader, mYCbCrShader); + InitPixelShader(sNV12Shader, mNV12Shader); + 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; +} + +} // namespace layers +} // namespace mozilla diff --git a/gfx/layers/d3d11/DeviceAttachmentsD3D11.h b/gfx/layers/d3d11/DeviceAttachmentsD3D11.h new file mode 100644 index 0000000000..4837dc76f1 --- /dev/null +++ b/gfx/layers/d3d11/DeviceAttachmentsD3D11.h @@ -0,0 +1,96 @@ +/* -*- 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 IsValid() const { return mInitialized; } + const nsCString& GetFailureId() const { + MOZ_ASSERT(!IsValid()); + return mInitFailureId; + } + + RefPtr<ID3D11InputLayout> mInputLayout; + + RefPtr<ID3D11Buffer> mVertexBuffer; + + RefPtr<ID3D11VertexShader> mVSQuadShader; + + RefPtr<ID3D11PixelShader> mSolidColorShader; + RefPtr<ID3D11PixelShader> mRGBAShader; + RefPtr<ID3D11PixelShader> mRGBShader; + RefPtr<ID3D11PixelShader> mYCbCrShader; + RefPtr<ID3D11PixelShader> mNV12Shader; + RefPtr<ID3D11Buffer> mPSConstantBuffer; + RefPtr<ID3D11Buffer> mVSConstantBuffer; + RefPtr<ID3D11RasterizerState> mRasterizerState; + RefPtr<ID3D11SamplerState> mLinearSamplerState; + RefPtr<ID3D11SamplerState> mPointSamplerState; + + RefPtr<ID3D11BlendState> mPremulBlendState; + RefPtr<ID3D11BlendState> mPremulCopyState; + RefPtr<ID3D11BlendState> mNonPremulBlendState; + 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, + RefPtr<ID3D11VertexShader>& aDest) { + InitVertexShader(aShader, getter_AddRefs(aDest)); + } + void InitPixelShader(const ShaderBytes& aShader, + RefPtr<ID3D11PixelShader>& aDest) { + InitPixelShader(aShader, getter_AddRefs(aDest)); + } + + void InitVertexShader(const ShaderBytes& aShader, ID3D11VertexShader** aOut); + void InitPixelShader(const ShaderBytes& aShader, ID3D11PixelShader** aOut); + + bool Failed(HRESULT hr, const char* aContext); + + private: + // 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/FenceD3D11.cpp b/gfx/layers/d3d11/FenceD3D11.cpp new file mode 100644 index 0000000000..d300eab965 --- /dev/null +++ b/gfx/layers/d3d11/FenceD3D11.cpp @@ -0,0 +1,190 @@ +/* -*- 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 "FenceD3D11.h" + +#include <d3d11.h> +#include <d3d11_3.h> +#include <d3d11_4.h> + +#include "mozilla/gfx/Logging.h" + +namespace mozilla { +namespace layers { + +RefPtr<ID3D11Device> mDevice; + +/* static */ +RefPtr<FenceD3D11> FenceD3D11::Create(ID3D11Device* aDevice) { + MOZ_ASSERT(aDevice); + + if (!aDevice) { + return nullptr; + } + + RefPtr<ID3D11Device5> d3d11_5; + auto hr = + aDevice->QueryInterface(__uuidof(ID3D11Device5), getter_AddRefs(d3d11_5)); + if (FAILED(hr)) { + gfxCriticalNoteOnce << "Failed to get ID3D11Device5: " << gfx::hexa(hr); + return nullptr; + } + + RefPtr<ID3D11Fence> fenceD3D11; + d3d11_5->CreateFence(0, D3D11_FENCE_FLAG_SHARED, + IID_PPV_ARGS((ID3D11Fence**)getter_AddRefs(fenceD3D11))); + if (FAILED(hr)) { + gfxCriticalNoteOnce << "Fence creation failed: " << gfx::hexa(hr); + return nullptr; + } + + HANDLE sharedHandle = nullptr; + hr = fenceD3D11->CreateSharedHandle(nullptr, GENERIC_ALL, nullptr, + &sharedHandle); + if (FAILED(hr)) { + gfxCriticalNoteOnce << "Fence shared handle creation failed " + << gfx::hexa(hr); + return nullptr; + } + + RefPtr<gfx::FileHandleWrapper> handle = + new gfx::FileHandleWrapper(UniqueFileHandle(sharedHandle)); + RefPtr<FenceD3D11> fence = new FenceD3D11(handle); + fence->mDevice = aDevice; + fence->mSignalFence = fenceD3D11; + + return fence; +} + +/* static */ +RefPtr<FenceD3D11> FenceD3D11::CreateFromHandle( + RefPtr<gfx::FileHandleWrapper> aHandle) { + // Opening shared handle is deferred. + return new FenceD3D11(aHandle); +} + +/* static */ +bool FenceD3D11::IsSupported(ID3D11Device* aDevice) { + RefPtr<ID3D11Device5> d3d11_5; + auto hr = + aDevice->QueryInterface(__uuidof(ID3D11Device5), getter_AddRefs(d3d11_5)); + if (FAILED(hr)) { + return false; + } + return true; +} + +FenceD3D11::FenceD3D11(RefPtr<gfx::FileHandleWrapper>& aHandle) + : mHandle(aHandle) { + MOZ_ASSERT(mHandle); +} + +FenceD3D11::~FenceD3D11() {} + +gfx::FenceInfo FenceD3D11::GetFenceInfo() const { + return gfx::FenceInfo(mHandle, mFenceValue); +} + +bool FenceD3D11::IncrementAndSignal() { + MOZ_ASSERT(mDevice); + MOZ_ASSERT(mSignalFence); + + if (!mDevice || !mSignalFence) { + return false; + } + + RefPtr<ID3D11DeviceContext> context; + mDevice->GetImmediateContext(getter_AddRefs(context)); + RefPtr<ID3D11DeviceContext4> context4; + auto hr = context->QueryInterface(__uuidof(ID3D11DeviceContext4), + getter_AddRefs(context4)); + if (FAILED(hr)) { + gfxCriticalNoteOnce << "Failed to get D3D11DeviceContext4: " + << gfx::hexa(hr); + return false; + } + + hr = context4->Signal(mSignalFence, mFenceValue + 1); + if (FAILED(hr)) { + gfxCriticalNoteOnce << "Signal fence failed: " << gfx::hexa(hr); + return false; + } + + mFenceValue++; + return true; +} + +void FenceD3D11::Update(uint64_t aFenceValue) { + MOZ_ASSERT(!mDevice); + MOZ_ASSERT(!mSignalFence); + + if (mFenceValue > aFenceValue) { + MOZ_ASSERT_UNREACHABLE("unexpected to be called"); + return; + } + mFenceValue = aFenceValue; +} + +bool FenceD3D11::Wait(ID3D11Device* aDevice) { + MOZ_ASSERT(aDevice); + + if (!aDevice) { + return false; + } + + // Skip wait if passed device is the same as signaling device. + if (mDevice == aDevice) { + return true; + } + + RefPtr<ID3D11Fence> fence; + auto it = mWaitFenceMap.find(aDevice); + if (it == mWaitFenceMap.end()) { + RefPtr<ID3D11Device5> d3d11_5; + auto hr = aDevice->QueryInterface(__uuidof(ID3D11Device5), + getter_AddRefs(d3d11_5)); + if (FAILED(hr)) { + gfxCriticalNoteOnce << "Failed to get ID3D11Device5: " << gfx::hexa(hr); + return false; + } + hr = d3d11_5->OpenSharedFence(mHandle->GetHandle(), __uuidof(ID3D11Fence), + (void**)(ID3D11Fence**)getter_AddRefs(fence)); + if (FAILED(hr)) { + gfxCriticalNoteOnce << "Opening fence shared handle failed " + << gfx::hexa(hr); + return false; + } + mWaitFenceMap.emplace(aDevice, fence); + } else { + fence = it->second; + } + + if (!fence) { + MOZ_ASSERT_UNREACHABLE("unexpected to be called"); + return false; + } + + RefPtr<ID3D11DeviceContext> context; + aDevice->GetImmediateContext(getter_AddRefs(context)); + RefPtr<ID3D11DeviceContext4> context4; + auto hr = context->QueryInterface(__uuidof(ID3D11DeviceContext4), + getter_AddRefs(context4)); + if (FAILED(hr)) { + gfxCriticalNoteOnce << "Failed to get D3D11DeviceContext4: " + << gfx::hexa(hr); + return false; + } + hr = context4->Wait(fence, mFenceValue); + if (FAILED(hr)) { + gfxCriticalNoteOnce << "Failed to wait fence: " << gfx::hexa(hr); + return false; + } + + return true; +} + +} // namespace layers +} // namespace mozilla diff --git a/gfx/layers/d3d11/FenceD3D11.h b/gfx/layers/d3d11/FenceD3D11.h new file mode 100644 index 0000000000..19037bc0ce --- /dev/null +++ b/gfx/layers/d3d11/FenceD3D11.h @@ -0,0 +1,86 @@ +/* -*- 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_FenceD3D11_H +#define MOZILLA_GFX_FenceD3D11_H + +#include <unordered_map> + +#include "mozilla/gfx/FileHandleWrapper.h" +#include "nsISupportsImpl.h" + +struct ID3D11Device; +struct ID3D11Fence; + +namespace mozilla { + +namespace layers { + +// +// A class for wrapping ID3D11Fence. +// +// The class can be used for singaling fence and waiting fence. When the class +// is created by Create(), it can be used for singaling fence and waiting +// fence. When the class is created by CreateFromHandle() it can be used only +// for waiting fence. +// +// There is a case that ID3D12Fence is used for fence signaling. In this case, +// the class can be used for waitng fence by using CreateFromHandle(). +// +// IncrementAndSignal() is used for signaling fence. The fence will be updated +// after all previous work has completed. +// +// For waiting fence, Update() is used to update the target value of the +// waiting. Wait() is then used to wait for the fence. +// +class FenceD3D11 final { + public: + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(FenceD3D11); + + static RefPtr<FenceD3D11> Create(ID3D11Device* aDevice); + static RefPtr<FenceD3D11> CreateFromHandle( + RefPtr<gfx::FileHandleWrapper> aHandle); + + // Check if ID3D11Device suppors ID3D11Fence creation. + static bool IsSupported(ID3D11Device* aDevice); + + // Updates mSignalFence to incremented value after all previous work has + // completed. Used only when FenceD3D11 is created by FenceD3D11::Create(). + bool IncrementAndSignal(); + + // Update FenceValue to the specified value. + // Used only when FenceD3D11 is created by FenceD3D11::CreateFromHandle(). + void Update(uint64_t aFenceValue); + + // Wait for fence until it reaches or exceeds mFenceValue. + bool Wait(ID3D11Device* aDevice); + + uint64_t GetFenceValue() const { return mFenceValue; } + + gfx::FenceInfo GetFenceInfo() const; + + const RefPtr<gfx::FileHandleWrapper> mHandle; + + protected: + explicit FenceD3D11(RefPtr<gfx::FileHandleWrapper>& aHandle); + ~FenceD3D11(); + + // Device that is used for creating mSignalFence. + RefPtr<ID3D11Device> mDevice; + // Fence that is used for updating fence value. + // Valid only when created with FenceD3D11::Create(). + RefPtr<ID3D11Fence> mSignalFence; + uint64_t mFenceValue = 0; + // Fences that are used for waiting. + // They are opened for each D3D11 device that the fence is waited on. + // XXX change to LRU cache + std::unordered_map<const ID3D11Device*, RefPtr<ID3D11Fence>> mWaitFenceMap; +}; + +} // namespace layers +} // namespace mozilla + +#endif // MOZILLA_GFX_FenceD3D11_H diff --git a/gfx/layers/d3d11/GpuProcessD3D11QueryMap.cpp b/gfx/layers/d3d11/GpuProcessD3D11QueryMap.cpp new file mode 100644 index 0000000000..858e39d9d5 --- /dev/null +++ b/gfx/layers/d3d11/GpuProcessD3D11QueryMap.cpp @@ -0,0 +1,67 @@ +/* -*- 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 "GpuProcessD3D11QueryMap.h" + +#include "mozilla/webrender/RenderThread.h" + +namespace mozilla { + +namespace layers { + +StaticAutoPtr<GpuProcessD3D11QueryMap> GpuProcessD3D11QueryMap::sInstance; + +/* static */ +void GpuProcessD3D11QueryMap::Init() { + MOZ_ASSERT(XRE_IsGPUProcess()); + sInstance = new GpuProcessD3D11QueryMap(); +} + +/* static */ +void GpuProcessD3D11QueryMap::Shutdown() { + MOZ_ASSERT(XRE_IsGPUProcess()); + sInstance = nullptr; +} + +GpuProcessD3D11QueryMap::GpuProcessD3D11QueryMap() + : mMonitor("GpuProcessD3D11QueryMap::mMonitor") {} + +GpuProcessD3D11QueryMap::~GpuProcessD3D11QueryMap() {} + +void GpuProcessD3D11QueryMap::Register(GpuProcessQueryId aQueryId, + ID3D11Query* aQuery) { + MOZ_RELEASE_ASSERT(aQuery); + + MonitorAutoLock lock(mMonitor); + mD3D11QueriesById[aQueryId] = aQuery; +} + +void GpuProcessD3D11QueryMap::Unregister(GpuProcessQueryId aQueryId) { + MonitorAutoLock lock(mMonitor); + + auto it = mD3D11QueriesById.find(aQueryId); + if (it == mD3D11QueriesById.end()) { + return; + } + mD3D11QueriesById.erase(it); +} + +RefPtr<ID3D11Query> GpuProcessD3D11QueryMap::GetQuery( + GpuProcessQueryId aQueryId) { + MOZ_ASSERT(wr::RenderThread::IsInRenderThread()); + + MonitorAutoLock lock(mMonitor); + + auto it = mD3D11QueriesById.find(aQueryId); + if (it == mD3D11QueriesById.end()) { + return nullptr; + } + + return it->second; +} + +} // namespace layers +} // namespace mozilla diff --git a/gfx/layers/d3d11/GpuProcessD3D11QueryMap.h b/gfx/layers/d3d11/GpuProcessD3D11QueryMap.h new file mode 100644 index 0000000000..85c46a12cb --- /dev/null +++ b/gfx/layers/d3d11/GpuProcessD3D11QueryMap.h @@ -0,0 +1,52 @@ +/* -*- 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_GpuProcessD3D11QueryMap_H +#define MOZILLA_GFX_GpuProcessD3D11QueryMap_H + +#include <d3d11.h> +#include <unordered_map> + +#include "mozilla/gfx/2D.h" +#include "mozilla/layers/LayersTypes.h" +#include "mozilla/Maybe.h" +#include "mozilla/Monitor.h" +#include "mozilla/StaticPtr.h" + +namespace mozilla { +namespace layers { + +/** + * A class to manage ID3D11Queries that is shared in GPU process. + */ +class GpuProcessD3D11QueryMap { + public: + static void Init(); + static void Shutdown(); + static GpuProcessD3D11QueryMap* Get() { return sInstance; } + + GpuProcessD3D11QueryMap(); + ~GpuProcessD3D11QueryMap(); + + void Register(GpuProcessQueryId aQueryId, ID3D11Query* aQuery); + void Unregister(GpuProcessQueryId aQueryId); + + RefPtr<ID3D11Query> GetQuery(GpuProcessQueryId aQueryId); + + private: + mutable Monitor mMonitor MOZ_UNANNOTATED; + + std::unordered_map<GpuProcessQueryId, RefPtr<ID3D11Query>, + GpuProcessQueryId::HashFn> + mD3D11QueriesById; + + static StaticAutoPtr<GpuProcessD3D11QueryMap> sInstance; +}; + +} // namespace layers +} // namespace mozilla + +#endif /* MOZILLA_GFX_GpuProcessD3D11QueryMap_H */ diff --git a/gfx/layers/d3d11/GpuProcessD3D11TextureMap.cpp b/gfx/layers/d3d11/GpuProcessD3D11TextureMap.cpp new file mode 100644 index 0000000000..36651b3279 --- /dev/null +++ b/gfx/layers/d3d11/GpuProcessD3D11TextureMap.cpp @@ -0,0 +1,409 @@ +/* -*- 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 "GpuProcessD3D11TextureMap.h" + +#include "libyuv.h" +#include "mozilla/layers/CompositorThread.h" +#include "mozilla/layers/D3D11TextureIMFSampleImage.h" +#include "mozilla/layers/HelpersD3D11.h" +#include "mozilla/layers/TextureHostWrapperD3D11.h" +#include "mozilla/ProfilerMarkers.h" +#include "mozilla/SharedThreadPool.h" +#include "mozilla/webrender/RenderThread.h" + +namespace mozilla { + +namespace layers { + +StaticAutoPtr<GpuProcessD3D11TextureMap> GpuProcessD3D11TextureMap::sInstance; + +/* static */ +void GpuProcessD3D11TextureMap::Init() { + MOZ_ASSERT(XRE_IsGPUProcess()); + sInstance = new GpuProcessD3D11TextureMap(); +} + +/* static */ +void GpuProcessD3D11TextureMap::Shutdown() { + MOZ_ASSERT(XRE_IsGPUProcess()); + sInstance = nullptr; +} + +/* static */ +GpuProcessTextureId GpuProcessD3D11TextureMap::GetNextTextureId() { + MOZ_ASSERT(XRE_IsGPUProcess()); + return GpuProcessTextureId::GetNext(); +} + +GpuProcessD3D11TextureMap::GpuProcessD3D11TextureMap() + : mMonitor("GpuProcessD3D11TextureMap::mMonitor") {} + +GpuProcessD3D11TextureMap::~GpuProcessD3D11TextureMap() {} + +void GpuProcessD3D11TextureMap::Register( + GpuProcessTextureId aTextureId, ID3D11Texture2D* aTexture, + uint32_t aArrayIndex, const gfx::IntSize& aSize, + RefPtr<IMFSampleUsageInfo> aUsageInfo) { + MonitorAutoLock lock(mMonitor); + Register(lock, aTextureId, aTexture, aArrayIndex, aSize, aUsageInfo); +} +void GpuProcessD3D11TextureMap::Register( + const MonitorAutoLock& aProofOfLock, GpuProcessTextureId aTextureId, + ID3D11Texture2D* aTexture, uint32_t aArrayIndex, const gfx::IntSize& aSize, + RefPtr<IMFSampleUsageInfo> aUsageInfo) { + MOZ_RELEASE_ASSERT(aTexture); + + auto it = mD3D11TexturesById.find(aTextureId); + if (it != mD3D11TexturesById.end()) { + MOZ_ASSERT_UNREACHABLE("unexpected to be called"); + return; + } + mD3D11TexturesById.emplace( + aTextureId, TextureHolder(aTexture, aArrayIndex, aSize, aUsageInfo)); +} + +void GpuProcessD3D11TextureMap::Unregister(GpuProcessTextureId aTextureId) { + MonitorAutoLock lock(mMonitor); + + auto it = mD3D11TexturesById.find(aTextureId); + if (it == mD3D11TexturesById.end()) { + return; + } + mD3D11TexturesById.erase(it); +} + +RefPtr<ID3D11Texture2D> GpuProcessD3D11TextureMap::GetTexture( + GpuProcessTextureId aTextureId) { + MonitorAutoLock lock(mMonitor); + + auto it = mD3D11TexturesById.find(aTextureId); + if (it == mD3D11TexturesById.end()) { + return nullptr; + } + + return it->second.mTexture; +} + +Maybe<HANDLE> GpuProcessD3D11TextureMap::GetSharedHandleOfCopiedTexture( + GpuProcessTextureId aTextureId) { + TextureHolder holder; + { + MonitorAutoLock lock(mMonitor); + + auto it = mD3D11TexturesById.find(aTextureId); + if (it == mD3D11TexturesById.end()) { + return Nothing(); + } + + if (it->second.mCopiedTextureSharedHandle) { + return Some(it->second.mCopiedTextureSharedHandle->GetHandle()); + } + + holder = it->second; + } + + RefPtr<ID3D11Device> device; + holder.mTexture->GetDevice(getter_AddRefs(device)); + if (!device) { + return Nothing(); + } + + RefPtr<ID3D11DeviceContext> context; + device->GetImmediateContext(getter_AddRefs(context)); + if (!context) { + return Nothing(); + } + + CD3D11_TEXTURE2D_DESC newDesc( + DXGI_FORMAT_NV12, holder.mSize.width, holder.mSize.height, 1, 1, + D3D11_BIND_RENDER_TARGET | D3D11_BIND_SHADER_RESOURCE); + newDesc.MiscFlags = + D3D11_RESOURCE_MISC_SHARED_NTHANDLE | D3D11_RESOURCE_MISC_SHARED; + + RefPtr<ID3D11Texture2D> copiedTexture; + HRESULT hr = + device->CreateTexture2D(&newDesc, nullptr, getter_AddRefs(copiedTexture)); + if (FAILED(hr)) { + return Nothing(); + } + + D3D11_TEXTURE2D_DESC inDesc; + holder.mTexture->GetDesc(&inDesc); + + D3D11_TEXTURE2D_DESC outDesc; + copiedTexture->GetDesc(&outDesc); + + UINT height = std::min(inDesc.Height, outDesc.Height); + UINT width = std::min(inDesc.Width, outDesc.Width); + D3D11_BOX srcBox = {0, 0, 0, width, height, 1}; + + context->CopySubresourceRegion(copiedTexture, 0, 0, 0, 0, holder.mTexture, + holder.mArrayIndex, &srcBox); + + RefPtr<IDXGIResource1> resource; + copiedTexture->QueryInterface((IDXGIResource1**)getter_AddRefs(resource)); + if (!resource) { + return Nothing(); + } + + HANDLE sharedHandle; + hr = resource->CreateSharedHandle( + nullptr, DXGI_SHARED_RESOURCE_READ | DXGI_SHARED_RESOURCE_WRITE, nullptr, + &sharedHandle); + if (FAILED(hr)) { + return Nothing(); + } + + RefPtr<gfx::FileHandleWrapper> handle = + new gfx::FileHandleWrapper(UniqueFileHandle(sharedHandle)); + + RefPtr<ID3D11Query> query; + CD3D11_QUERY_DESC desc(D3D11_QUERY_EVENT); + hr = device->CreateQuery(&desc, getter_AddRefs(query)); + if (FAILED(hr) || !query) { + gfxWarning() << "Could not create D3D11_QUERY_EVENT: " << gfx::hexa(hr); + return Nothing(); + } + + context->End(query); + + BOOL result; + bool ret = WaitForFrameGPUQuery(device, context, query, &result); + if (!ret) { + gfxCriticalNoteOnce << "WaitForFrameGPUQuery() failed"; + } + + { + MonitorAutoLock lock(mMonitor); + + auto it = mD3D11TexturesById.find(aTextureId); + if (it == mD3D11TexturesById.end()) { + MOZ_ASSERT_UNREACHABLE("unexpected to be called"); + return Nothing(); + } + + // Disable no video copy for future decoded video frames. Since + // GetSharedHandleOfCopiedTexture() is slow. + if (it->second.mIMFSampleUsageInfo) { + it->second.mIMFSampleUsageInfo->DisableZeroCopyNV12Texture(); + } + + it->second.mCopiedTexture = copiedTexture; + it->second.mCopiedTextureSharedHandle = handle; + } + + return Some(handle->GetHandle()); +} + +size_t GpuProcessD3D11TextureMap::GetWaitingTextureCount() const { + MonitorAutoLock lock(mMonitor); + return mWaitingTextures.size(); +} + +bool GpuProcessD3D11TextureMap::WaitTextureReady( + const GpuProcessTextureId aTextureId) { + MOZ_ASSERT(wr::RenderThread::IsInRenderThread()); + + MonitorAutoLock lock(mMonitor); + auto it = mWaitingTextures.find(aTextureId); + if (it == mWaitingTextures.end()) { + return true; + } + + auto start = TimeStamp::Now(); + const TimeDuration timeout = TimeDuration::FromMilliseconds(1000); + while (1) { + CVStatus status = mMonitor.Wait(timeout); + if (status == CVStatus::Timeout) { + MOZ_ASSERT_UNREACHABLE("unexpected to be called"); + gfxCriticalNoteOnce << "GpuProcessTextureI wait time out id:" + << uint64_t(aTextureId); + return false; + } + + auto it = mWaitingTextures.find(aTextureId); + if (it == mWaitingTextures.end()) { + break; + } + } + + auto end = TimeStamp::Now(); + const auto durationMs = static_cast<uint32_t>((end - start).ToMilliseconds()); + nsPrintfCString marker("TextureReadyWait %ums ", durationMs); + PROFILER_MARKER_TEXT("PresentWait", GRAPHICS, {}, marker); + + return true; +} + +void GpuProcessD3D11TextureMap::PostUpdateTextureDataTask( + const GpuProcessTextureId aTextureId, TextureHost* aTextureHost, + TextureHost* aWrappedTextureHost, + TextureWrapperD3D11Allocator* aAllocator) { + { + MonitorAutoLock lock(mMonitor); + + auto it = mWaitingTextures.find(aTextureId); + if (it != mWaitingTextures.end()) { + MOZ_ASSERT_UNREACHABLE("unexpected to be called"); + return; + } + + auto updatingTexture = MakeUnique<UpdatingTextureHolder>( + aTextureId, aTextureHost, aWrappedTextureHost, aAllocator); + + mWaitingTextures.emplace(aTextureId); + mWaitingTextureQueue.push_back(std::move(updatingTexture)); + } + + RefPtr<Runnable> runnable = NS_NewRunnableFunction( + "GpuProcessD3D11TextureMap::PostUpdateTextureDataTaskRunnable", []() { + GpuProcessD3D11TextureMap::Get()->HandleInTextureUpdateThread(); + }); + nsCOMPtr<nsIEventTarget> thread = aAllocator->mThread; + thread->Dispatch(runnable.forget()); +} + +void GpuProcessD3D11TextureMap::HandleInTextureUpdateThread() { + UniquePtr<UpdatingTextureHolder> textureHolder; + { + MonitorAutoLock lock(mMonitor); + + if (mWaitingTextureQueue.empty()) { + return; + } + textureHolder = std::move(mWaitingTextureQueue.front()); + mWaitingTextureQueue.pop_front(); + } + + RefPtr<ID3D11Texture2D> texture = UpdateTextureData(textureHolder.get()); + + { + MonitorAutoLock lock(mMonitor); + if (texture) { + auto size = textureHolder->mWrappedTextureHost->GetSize(); + Register(lock, textureHolder->mTextureId, texture, /* aArrayIndex */ 0, + size, /* aUsageInfo */ nullptr); + } + mWaitingTextures.erase(textureHolder->mTextureId); + MOZ_ASSERT(mWaitingTextures.size() == mWaitingTextureQueue.size()); + mMonitor.Notify(); + } + + // Release UpdatingTextureHolder in CompositorThread + RefPtr<Runnable> runnable = NS_NewRunnableFunction( + "GpuProcessD3D11TextureMap::HandleInTextureUpdateThread::Runnable", + [textureHolder = std::move(textureHolder)]() mutable { + textureHolder = nullptr; + }); + CompositorThread()->Dispatch(runnable.forget()); +} + +RefPtr<ID3D11Texture2D> GpuProcessD3D11TextureMap::UpdateTextureData( + UpdatingTextureHolder* aHolder) { + MOZ_ASSERT(aHolder); + MOZ_ASSERT(aHolder->mAllocator->mThread->IsOnCurrentThread()); + + auto* bufferTexture = aHolder->mWrappedTextureHost->AsBufferTextureHost(); + MOZ_ASSERT(bufferTexture); + if (!bufferTexture) { + MOZ_ASSERT_UNREACHABLE("unexpected to be called"); + return nullptr; + } + + auto size = bufferTexture->GetSize(); + + RefPtr<ID3D11Texture2D> texture2D = + aHolder->mAllocator->CreateOrRecycle(gfx::SurfaceFormat::NV12, size); + if (!texture2D) { + return nullptr; + } + + RefPtr<ID3D11Texture2D> stagingTexture = + aHolder->mAllocator->GetStagingTextureNV12(); + if (!stagingTexture) { + return nullptr; + } + + RefPtr<ID3D11DeviceContext> context; + RefPtr<ID3D11Device> device = aHolder->mAllocator->GetDevice(); + device->GetImmediateContext(getter_AddRefs(context)); + if (!context) { + return nullptr; + } + + RefPtr<ID3D10Multithread> mt; + HRESULT hr = device->QueryInterface((ID3D10Multithread**)getter_AddRefs(mt)); + if (FAILED(hr) || !mt) { + gfxCriticalError() << "Multithread safety interface not supported. " << hr; + return nullptr; + } + + if (!mt->GetMultithreadProtected()) { + gfxCriticalError() << "Device used not marked as multithread-safe."; + return nullptr; + } + + D3D11MTAutoEnter mtAutoEnter(mt.forget()); + + AutoLockD3D11Texture lockSt(stagingTexture); + + D3D11_MAP mapType = D3D11_MAP_WRITE; + D3D11_MAPPED_SUBRESOURCE mappedResource; + + hr = context->Map(stagingTexture, 0, mapType, 0, &mappedResource); + if (FAILED(hr)) { + gfxCriticalNoteOnce << "Mapping D3D11 staging texture failed: " + << gfx::hexa(hr); + return nullptr; + } + + const size_t destStride = mappedResource.RowPitch; + uint8_t* yDestPlaneStart = reinterpret_cast<uint8_t*>(mappedResource.pData); + uint8_t* uvDestPlaneStart = reinterpret_cast<uint8_t*>(mappedResource.pData) + + destStride * size.height; + // Convert I420 to NV12, + const uint8_t* yChannel = bufferTexture->GetYChannel(); + const uint8_t* cbChannel = bufferTexture->GetCbChannel(); + const uint8_t* crChannel = bufferTexture->GetCrChannel(); + int32_t yStride = bufferTexture->GetYStride(); + int32_t cbCrStride = bufferTexture->GetCbCrStride(); + + libyuv::I420ToNV12(yChannel, yStride, cbChannel, cbCrStride, crChannel, + cbCrStride, yDestPlaneStart, destStride, uvDestPlaneStart, + destStride, size.width, size.height); + + context->Unmap(stagingTexture, 0); + + context->CopyResource(texture2D, stagingTexture); + + return texture2D; +} + +GpuProcessD3D11TextureMap::TextureHolder::TextureHolder( + ID3D11Texture2D* aTexture, uint32_t aArrayIndex, const gfx::IntSize& aSize, + RefPtr<IMFSampleUsageInfo> aUsageInfo) + : mTexture(aTexture), + mArrayIndex(aArrayIndex), + mSize(aSize), + mIMFSampleUsageInfo(aUsageInfo) {} + +GpuProcessD3D11TextureMap::UpdatingTextureHolder::UpdatingTextureHolder( + const GpuProcessTextureId aTextureId, TextureHost* aTextureHost, + TextureHost* aWrappedTextureHost, TextureWrapperD3D11Allocator* aAllocator) + : mTextureId(aTextureId), + mTextureHost(aTextureHost), + mWrappedTextureHost(aWrappedTextureHost), + mAllocator(aAllocator) { + MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread()); +} + +GpuProcessD3D11TextureMap::UpdatingTextureHolder::~UpdatingTextureHolder() { + MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread()); +} + +} // namespace layers +} // namespace mozilla diff --git a/gfx/layers/d3d11/GpuProcessD3D11TextureMap.h b/gfx/layers/d3d11/GpuProcessD3D11TextureMap.h new file mode 100644 index 0000000000..6c348aa4d2 --- /dev/null +++ b/gfx/layers/d3d11/GpuProcessD3D11TextureMap.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_GpuProcessD3D11TextureMap_H +#define MOZILLA_GFX_GpuProcessD3D11TextureMap_H + +#include <d3d11.h> +#include <unordered_map> +#include <unordered_set> + +#include "mozilla/gfx/2D.h" +#include "mozilla/layers/LayersTypes.h" +#include "mozilla/layers/TextureHost.h" +#include "mozilla/Maybe.h" +#include "mozilla/StaticPtr.h" + +namespace mozilla { +namespace layers { + +class IMFSampleUsageInfo; +class TextureWrapperD3D11Allocator; + +/** + * A class to manage ID3D11Texture2Ds that is shared without using shared handle + * in GPU process. On some GPUs, ID3D11Texture2Ds of hardware decoded video + * frames with zero video frame copy could not use shared handle. + */ +class GpuProcessD3D11TextureMap { + struct UpdatingTextureHolder; + + public: + static void Init(); + static void Shutdown(); + static GpuProcessD3D11TextureMap* Get() { return sInstance; } + static GpuProcessTextureId GetNextTextureId(); + + GpuProcessD3D11TextureMap(); + ~GpuProcessD3D11TextureMap(); + + void Register(GpuProcessTextureId aTextureId, ID3D11Texture2D* aTexture, + uint32_t aArrayIndex, const gfx::IntSize& aSize, + RefPtr<IMFSampleUsageInfo> aUsageInfo); + void Register(const MonitorAutoLock& aProofOfLock, + GpuProcessTextureId aTextureId, ID3D11Texture2D* aTexture, + uint32_t aArrayIndex, const gfx::IntSize& aSize, + RefPtr<IMFSampleUsageInfo> aUsageInfo); + void Unregister(GpuProcessTextureId aTextureId); + + RefPtr<ID3D11Texture2D> GetTexture(GpuProcessTextureId aTextureId); + Maybe<HANDLE> GetSharedHandleOfCopiedTexture(GpuProcessTextureId aTextureId); + + size_t GetWaitingTextureCount() const; + + bool WaitTextureReady(const GpuProcessTextureId aTextureId); + + void PostUpdateTextureDataTask(const GpuProcessTextureId aTextureId, + TextureHost* aTextureHost, + TextureHost* aWrappedTextureHost, + TextureWrapperD3D11Allocator* aAllocator); + + void HandleInTextureUpdateThread(); + + private: + struct TextureHolder { + TextureHolder(ID3D11Texture2D* aTexture, uint32_t aArrayIndex, + const gfx::IntSize& aSize, + RefPtr<IMFSampleUsageInfo> aUsageInfo); + TextureHolder() = default; + + RefPtr<ID3D11Texture2D> mTexture; + uint32_t mArrayIndex = 0; + gfx::IntSize mSize; + RefPtr<IMFSampleUsageInfo> mIMFSampleUsageInfo; + RefPtr<ID3D11Texture2D> mCopiedTexture; + RefPtr<gfx::FileHandleWrapper> mCopiedTextureSharedHandle; + }; + + struct UpdatingTextureHolder { + UpdatingTextureHolder(const GpuProcessTextureId aTextureId, + TextureHost* aTextureHost, + TextureHost* aWrappedTextureHost, + TextureWrapperD3D11Allocator* aAllocator); + + ~UpdatingTextureHolder(); + + const GpuProcessTextureId mTextureId; + RefPtr<TextureHost> mTextureHost; + CompositableTextureHostRef mWrappedTextureHost; + RefPtr<TextureWrapperD3D11Allocator> mAllocator; + }; + + enum class UpdatingStatus { Waiting, Updating, Error }; + + RefPtr<ID3D11Texture2D> UpdateTextureData(UpdatingTextureHolder* aHolder); + + mutable Monitor mMonitor MOZ_UNANNOTATED; + + std::unordered_map<GpuProcessTextureId, TextureHolder, + GpuProcessTextureId::HashFn> + mD3D11TexturesById; + + std::deque<UniquePtr<UpdatingTextureHolder>> mWaitingTextureQueue; + + std::unordered_set<GpuProcessTextureId, GpuProcessTextureId::HashFn> + mWaitingTextures; + + static StaticAutoPtr<GpuProcessD3D11TextureMap> sInstance; +}; + +} // namespace layers +} // namespace mozilla + +#endif /* MOZILLA_GFX_GpuProcessD3D11TextureMap_H */ diff --git a/gfx/layers/d3d11/HelpersD3D11.h b/gfx/layers/d3d11/HelpersD3D11.h new file mode 100644 index 0000000000..ef40acd0a0 --- /dev/null +++ b/gfx/layers/d3d11/HelpersD3D11.h @@ -0,0 +1,67 @@ +/* -*- 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 <array> +#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; +} + +inline void ClearResource(ID3D11Device* const device, ID3D11Resource* const res, + const std::array<float, 4>& vals) { + RefPtr<ID3D11RenderTargetView> rtv; + (void)device->CreateRenderTargetView(res, nullptr, getter_AddRefs(rtv)); + + RefPtr<ID3D11DeviceContext> context; + device->GetImmediateContext(getter_AddRefs(context)); + context->ClearRenderTargetView(rtv, vals.data()); +} + +} // namespace layers +} // namespace mozilla + +#endif // mozilla_gfx_layers_d3d11_HelpersD3D11_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..3293ffe47e --- /dev/null +++ b/gfx/layers/d3d11/TextureD3D11.cpp @@ -0,0 +1,1786 @@ +/* -*- 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 "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/FileHandleWrapper.h" +#include "mozilla/gfx/Logging.h" +#include "mozilla/gfx/gfxVars.h" +#include "mozilla/ipc/FileDescriptor.h" +#include "mozilla/layers/CompositorBridgeChild.h" +#include "mozilla/layers/D3D11TextureIMFSampleImage.h" +#include "mozilla/layers/GpuProcessD3D11QueryMap.h" +#include "mozilla/layers/GpuProcessD3D11TextureMap.h" +#include "mozilla/layers/HelpersD3D11.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() {} + +enum class SerializeWithMoz2D : bool { No, Yes }; + +template <typename T> // ID3D10Texture2D or ID3D11Texture2D +static bool LockD3DTexture( + T* aTexture, SerializeWithMoz2D aSerialize = SerializeWithMoz2D::No) { + 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; + if (aSerialize == SerializeWithMoz2D::Yes) { + AutoSerializeWithMoz2D serializeWithMoz2D(BackendType::DIRECT2D1_1); + hr = mutex->AcquireSync(0, 10000); + } else { + hr = mutex->AcquireSync(0, 10000); + } + if (hr == WAIT_TIMEOUT) { + RefPtr<ID3D11Device> device; + aTexture->GetDevice(getter_AddRefs(device)); + if (!device) { + gfxCriticalNote << "GFX: D3D11 lock mutex timeout - no device returned"; + } else if (device->GetDeviceRemovedReason() != S_OK) { + gfxCriticalNote << "GFX: D3D11 lock mutex timeout - device removed"; + } else { + gfxDevCrash(LogReason::D3DLockTimeout) + << "D3D lock mutex timeout - device not removed"; + } + } 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) { + MOZ_ASSERT(aTexture); + RefPtr<IDXGIKeyedMutex> mutex; + aTexture->QueryInterface((IDXGIKeyedMutex**)getter_AddRefs(mutex)); + return !!mutex; +} + +template <typename T> // ID3D10Texture2D or ID3D11Texture2D +static void UnlockD3DTexture( + T* aTexture, SerializeWithMoz2D aSerialize = SerializeWithMoz2D::No) { + MOZ_ASSERT(aTexture); + RefPtr<IDXGIKeyedMutex> mutex; + aTexture->QueryInterface((IDXGIKeyedMutex**)getter_AddRefs(mutex)); + if (mutex) { + HRESULT hr; + if (aSerialize == SerializeWithMoz2D::Yes) { + AutoSerializeWithMoz2D serializeWithMoz2D(BackendType::DIRECT2D1_1); + hr = mutex->ReleaseSync(0); + } else { + hr = mutex->ReleaseSync(0); + } + if (FAILED(hr)) { + NS_WARNING("Failed to unlock the texture"); + } + } +} + +D3D11TextureData::D3D11TextureData(ID3D11Texture2D* aTexture, + uint32_t aArrayIndex, + RefPtr<gfx::FileHandleWrapper> aSharedHandle, + gfx::IntSize aSize, + gfx::SurfaceFormat aFormat, + TextureAllocationFlags aFlags) + : mSize(aSize), + mFormat(aFormat), + mNeedsClear(aFlags & ALLOC_CLEAR_BUFFER), + mHasKeyedMutex(HasKeyedMutex(aTexture)), + mTexture(aTexture), + mSharedHandle(std::move(aSharedHandle)), + mArrayIndex(aArrayIndex), + mAllocationFlags(aFlags) { + MOZ_ASSERT(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(), SerializeWithMoz2D::Yes); + aDT = nullptr; + + // Do the serialization here, so we can hold it while destroying the texture. + AutoSerializeWithMoz2D serializeWithMoz2D(BackendType::DIRECT2D1_1); + UnlockD3DTexture(aTexture.get(), SerializeWithMoz2D::No); + aTexture = nullptr; +} + +D3D11TextureData::~D3D11TextureData() { + if (mDrawTarget) { + DestroyDrawTarget(mDrawTarget, mTexture); + } + + if (mGpuProcessTextureId.isSome()) { + auto* textureMap = GpuProcessD3D11TextureMap::Get(); + if (textureMap) { + textureMap->Unregister(mGpuProcessTextureId.ref()); + } else { + gfxCriticalNoteOnce << "GpuProcessD3D11TextureMap does not exist"; + } + } + + if (mGpuProcessQueryId.isSome()) { + auto* queryMap = GpuProcessD3D11QueryMap::Get(); + if (queryMap) { + queryMap->Unregister(mGpuProcessQueryId.ref()); + } else { + gfxCriticalNoteOnce << "GpuProcessD3D11QueryMap does not exist"; + } + } +} + +bool D3D11TextureData::Lock(OpenMode aMode) { + if (mHasKeyedMutex && + !LockD3DTexture(mTexture.get(), SerializeWithMoz2D::Yes)) { + return false; + } + + if (NS_IsMainThread()) { + 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)) { + mDrawTarget = BorrowDrawTarget(); + if (!mDrawTarget) { + return false; + } + } + + // Reset transform + mDrawTarget->SetTransform(Matrix()); + + if (mNeedsClear) { + mDrawTarget->ClearRect(Rect(0, 0, mSize.width, mSize.height)); + mNeedsClear = false; + } + + return true; +} + +void D3D11TextureData::Unlock() { + if (mHasKeyedMutex) { + UnlockD3DTexture(mTexture.get(), SerializeWithMoz2D::Yes); + } +} + +void D3D11TextureData::FillInfo(TextureData::Info& aInfo) const { + aInfo.size = mSize; + aInfo.format = mFormat; + aInfo.supportsMoz2D = true; + aInfo.hasSynchronization = mHasKeyedMutex; +} + +void D3D11TextureData::SyncWithObject(RefPtr<SyncObjectClient> aSyncObject) { + if (!aSyncObject || mHasKeyedMutex) { + // 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) { + if (mGpuProcessTextureId.isNothing()) { + } + *aOutDesc = SurfaceDescriptorD3D10( + mSharedHandle, mGpuProcessTextureId, mArrayIndex, mFormat, mSize, + mColorSpace, mColorRange, /* hasKeyedMutex */ mHasKeyedMutex, + /* fenceInfo */ Nothing(), mGpuProcessQueryId); + 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); +} + +/* static */ +already_AddRefed<TextureClient> D3D11TextureData::CreateTextureClient( + ID3D11Texture2D* aTexture, uint32_t aIndex, gfx::IntSize aSize, + gfx::SurfaceFormat aFormat, gfx::ColorSpace2 aColorSpace, + gfx::ColorRange aColorRange, KnowsCompositor* aKnowsCompositor, + RefPtr<IMFSampleUsageInfo> aUsageInfo) { + D3D11TextureData* data = new D3D11TextureData( + aTexture, aIndex, nullptr, aSize, aFormat, + TextureAllocationFlags::ALLOC_MANUAL_SYNCHRONIZATION); + data->mColorSpace = aColorSpace; + data->SetColorRange(aColorRange); + + RefPtr<TextureClient> textureClient = MakeAndAddRef<TextureClient>( + data, TextureFlags::NO_FLAGS, aKnowsCompositor->GetTextureForwarder()); + const auto textureId = GpuProcessD3D11TextureMap::GetNextTextureId(); + data->SetGpuProcessTextureId(textureId); + + // Register ID3D11Texture2D to GpuProcessD3D11TextureMap + auto* textureMap = GpuProcessD3D11TextureMap::Get(); + if (textureMap) { + textureMap->Register(textureId, aTexture, aIndex, aSize, aUsageInfo); + } else { + gfxCriticalNoteOnce << "GpuProcessD3D11TextureMap does not exist"; + } + + return textureClient.forget(); +} + +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_NTHANDLE | D3D11_RESOURCE_MISC_SHARED; + bool useKeyedMutex = false; + if (!NS_IsMainThread()) { + // On the main thread we use the syncobject to handle synchronization. + if (!(aFlags & ALLOC_MANUAL_SYNCHRONIZATION)) { + newDesc.MiscFlags = D3D11_RESOURCE_MISC_SHARED_NTHANDLE | + D3D11_RESOURCE_MISC_SHARED_KEYEDMUTEX; + useKeyedMutex = true; + } + } + + if (aSurface && useKeyedMutex && + !DeviceManagerDx::Get()->CanInitializeKeyedMutexTextures()) { + return nullptr; + } + + D3D11_SUBRESOURCE_DATA uploadData; + D3D11_SUBRESOURCE_DATA* uploadDataPtr = nullptr; + RefPtr<DataSourceSurface> srcSurf; + + if (aSurface) { + srcSurf = aSurface->GetDataSurface(); + + if (!srcSurf) { + gfxCriticalError() + << "Failed to GetDataSurface in D3D11TextureData::Create"; + return nullptr; + } + + DataSourceSurface::MappedSurface sourceMap; + if (!srcSurf->Map(DataSourceSurface::READ, &sourceMap)) { + gfxCriticalError() + << "Failed to map source surface for D3D11TextureData::Create"; + return nullptr; + } + + 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; + + { + AutoSerializeWithMoz2D serializeWithMoz2D(BackendType::DIRECT2D1_1); + 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) { + 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 && useKeyedMutex) { + if (!LockD3DTexture(texture11.get(), SerializeWithMoz2D::Yes)) { + return nullptr; + } + UnlockD3DTexture(texture11.get(), SerializeWithMoz2D::Yes); + } + + RefPtr<IDXGIResource1> resource; + texture11->QueryInterface((IDXGIResource1**)getter_AddRefs(resource)); + if (!resource) { + gfxCriticalNoteOnce << "Failed to get IDXGIResource"; + return nullptr; + } + + HANDLE sharedHandle; + HRESULT hr = resource->GetSharedHandle(&sharedHandle); + hr = resource->CreateSharedHandle( + nullptr, DXGI_SHARED_RESOURCE_READ | DXGI_SHARED_RESOURCE_WRITE, nullptr, + &sharedHandle); + if (FAILED(hr)) { + gfxCriticalNoteOnce << "GetSharedHandle failed: " << gfx::hexa(hr); + return nullptr; + } + + texture11->SetPrivateDataInterface( + sD3D11TextureUsage, + new TextureMemoryMeasurer(newDesc.Width * newDesc.Height * 4)); + + RefPtr<gfx::FileHandleWrapper> handle = + new gfx::FileHandleWrapper(UniqueFileHandle(sharedHandle)); + + return new D3D11TextureData(texture11, 0, std::move(handle), 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); +} + +TextureFlags D3D11TextureData::GetTextureFlags() const { + // 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. + return TextureFlags::WAIT_HOST_USAGE_END; +} + +void D3D11TextureData::RegisterQuery(RefPtr<ID3D11Query> aQuery) { + MOZ_ASSERT(XRE_IsGPUProcess()); + MOZ_ASSERT(GpuProcessD3D11QueryMap::Get()); + + if (!GpuProcessD3D11QueryMap::Get()) { + return; + } + + if (mGpuProcessQueryId.isNothing()) { + mGpuProcessQueryId = Some(GpuProcessQueryId::GetNext()); + } + GpuProcessD3D11QueryMap::Get()->Register(mGpuProcessQueryId.ref(), aQuery); +} + +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<IDXGIResource1> resource; + + aTextureY->QueryInterface((IDXGIResource1**)getter_AddRefs(resource)); + + HANDLE handleY; + HRESULT hr = resource->CreateSharedHandle( + nullptr, DXGI_SHARED_RESOURCE_READ | DXGI_SHARED_RESOURCE_WRITE, nullptr, + &handleY); + if (FAILED(hr)) { + return nullptr; + } + const RefPtr<gfx::FileHandleWrapper> sharedHandleY = + new gfx::FileHandleWrapper(UniqueFileHandle(handleY)); + + aTextureCb->QueryInterface((IDXGIResource1**)getter_AddRefs(resource)); + + HANDLE handleCb; + hr = resource->CreateSharedHandle( + nullptr, DXGI_SHARED_RESOURCE_READ | DXGI_SHARED_RESOURCE_WRITE, nullptr, + &handleCb); + if (FAILED(hr)) { + return nullptr; + } + const RefPtr<gfx::FileHandleWrapper> sharedHandleCb = + new gfx::FileHandleWrapper(UniqueFileHandle(handleCb)); + + aTextureCr->QueryInterface((IDXGIResource1**)getter_AddRefs(resource)); + HANDLE handleCr; + hr = resource->CreateSharedHandle( + nullptr, DXGI_SHARED_RESOURCE_READ | DXGI_SHARED_RESOURCE_WRITE, nullptr, + &handleCr); + if (FAILED(hr)) { + return nullptr; + } + const RefPtr<gfx::FileHandleWrapper> sharedHandleCr = + new gfx::FileHandleWrapper(UniqueFileHandle(handleCr)); + + DXGIYCbCrTextureData* texture = new DXGIYCbCrTextureData(); + texture->mHandles[0] = sharedHandleY; + texture->mHandles[1] = sharedHandleCb; + texture->mHandles[2] = sharedHandleCr; + 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.hasSynchronization = false; +} + +void DXGIYCbCrTextureData::SerializeSpecific( + SurfaceDescriptorDXGIYCbCr* const aOutDesc) { + *aOutDesc = SurfaceDescriptorDXGIYCbCr(mHandles[0], mHandles[1], 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*) { + mD3D11Textures[0] = nullptr; + mD3D11Textures[1] = nullptr; + mD3D11Textures[2] = nullptr; +} + +TextureFlags DXGIYCbCrTextureData::GetTextureFlags() const { + // 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. + return TextureFlags::WAIT_HOST_USAGE_END; +} + +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() || 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(TextureHostType::DXGI, aFlags), + mGpuProcessTextureId(aDescriptor.gpuProcessTextureId()), + mGpuProcessQueryId(aDescriptor.gpuProcessQueryId()), + mArrayIndex(aDescriptor.arrayIndex()), + mSize(aDescriptor.size()), + mHandle(aDescriptor.handle()), + mFormat(aDescriptor.format()), + mHasKeyedMutex(aDescriptor.hasKeyedMutex()), + mAcquireFenceInfo(aDescriptor.fenceInfo().isSome() + ? aDescriptor.fenceInfo().ref() + : gfx::FenceInfo()), + mColorSpace(aDescriptor.colorSpace()), + mColorRange(aDescriptor.colorRange()), + mIsLocked(false) {} + +bool DXGITextureHostD3D11::EnsureTexture() { + if (mGpuProcessTextureId.isSome()) { + return false; + } + + 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; + } + + RefPtr<ID3D11Device1> device1; + device->QueryInterface((ID3D11Device1**)getter_AddRefs(device1)); + if (!device1) { + gfxCriticalNoteOnce << "Failed to get ID3D11Device1"; + return false; + } + + HRESULT hr = device1->OpenSharedResource1( + (HANDLE)mHandle.get(), __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; + } + + return mDevice; +} + +bool DXGITextureHostD3D11::LockWithoutCompositor() { + if (!mDevice) { + mDevice = DeviceManagerDx::Get()->GetCompositorDevice(); + } + return LockInternal(); +} + +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() { + 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; + } + + mTextureSource = new DataTextureSourceD3D11(mDevice, mFormat, mTexture); + return true; +} + +void DXGITextureHostD3D11::UnlockInternal() { + UnlockD3DTexture(mTextureSource->GetD3D11Texture()); +} + +void DXGITextureHostD3D11::CreateRenderTexture( + const wr::ExternalImageId& aExternalImageId) { + MOZ_ASSERT(mExternalImageId.isSome()); + + RefPtr<wr::RenderDXGITextureHost> texture = new wr::RenderDXGITextureHost( + mHandle, mGpuProcessTextureId, mArrayIndex, mFormat, mColorSpace, + mColorRange, mSize, mHasKeyedMutex, mAcquireFenceInfo, + mGpuProcessQueryId); + if (mFlags & TextureFlags::SOFTWARE_DECODED_VIDEO) { + texture->SetIsSoftwareDecodedVideo(); + } + if (mFlags & TextureFlags::DRM_SOURCE) { + texture->SetIsFromDRMSource(/* aIsFromDRMSource */ true); + } + wr::RenderThread::Get()->RegisterExternalImage(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 || mGpuProcessTextureId.isSome()); + 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()); + // Prefer TextureExternal unless the backend requires TextureRect. + TextureHost::NativeTexturePolicy policy = + TextureHost::BackendNativeTexturePolicy(aResources.GetBackendType(), + mSize); + auto imageType = policy == TextureHost::NativeTexturePolicy::REQUIRE + ? 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); + // Prefer TextureExternal unless the backend requires TextureRect. + TextureHost::NativeTexturePolicy policy = + TextureHost::BackendNativeTexturePolicy(aResources.GetBackendType(), + mSize); + auto imageType = policy == TextureHost::NativeTexturePolicy::REQUIRE + ? 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, false, aFilter, aImageKeys[0], + !(mFlags & TextureFlags::NON_PREMULTIPLIED), + wr::ColorF{1.0f, 1.0f, 1.0f, 1.0f}, preferCompositorSurface, + SupportsExternalCompositing(aBuilder.GetBackendType())); + break; + } + case gfx::SurfaceFormat::P010: + case gfx::SurfaceFormat::P016: + case gfx::SurfaceFormat::NV12: { + // DXGI_FORMAT_P010 stores its 10 bit value in the most significant bits + // of each 16 bit word with the unused lower bits cleared to zero so that + // it may be handled as if it was DXGI_FORMAT_P016. This is approximately + // perceptually correct. However, due to rounding error, the precise + // quantized value after sampling may be off by 1. + 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(ToYUVColorSpace(mColorSpace)), + wr::ToWrColorRange(mColorRange), aFilter, preferCompositorSurface, + SupportsExternalCompositing(aBuilder.GetBackendType())); + break; + } + default: { + MOZ_ASSERT_UNREACHABLE("unexpected to be called"); + } + } +} + +bool DXGITextureHostD3D11::SupportsExternalCompositing( + WebRenderBackend aBackend) { + if (aBackend == WebRenderBackend::SOFTWARE) { + return true; + } + // XXX Add P010 and P016 support. + if (GetFormat() == gfx::SurfaceFormat::NV12) { + if ((mFlags & TextureFlags::SOFTWARE_DECODED_VIDEO) && + (gfx::gfxVars::UseWebRenderDCompVideoSwOverlayWin())) { + return true; + } + if (!(mFlags & TextureFlags::SOFTWARE_DECODED_VIDEO) && + (gfx::gfxVars::UseWebRenderDCompVideoHwOverlayWin())) { + return true; + } + } + return false; +} + +DXGIYCbCrTextureHostD3D11::DXGIYCbCrTextureHostD3D11( + TextureFlags aFlags, const SurfaceDescriptorDXGIYCbCr& aDescriptor) + : TextureHost(TextureHostType::DXGIYCbCr, 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(); +} + +void DXGIYCbCrTextureHostD3D11::CreateRenderTexture( + const wr::ExternalImageId& aExternalImageId) { + MOZ_ASSERT(mExternalImageId.isSome()); + + RefPtr<wr::RenderTextureHost> texture = new wr::RenderDXGIYCbCrTextureHost( + mHandles, mYUVColorSpace, mColorDepth, mColorRange, mSizeY, mSizeCbCr); + + wr::RenderThread::Get()->RegisterExternalImage(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; + + // Prefer TextureExternal unless the backend requires TextureRect. + // Use a size that is the maximum of the Y and CbCr sizes. + IntSize textureSize = std::max(mSizeY, mSizeCbCr); + TextureHost::NativeTexturePolicy policy = + TextureHost::BackendNativeTexturePolicy(aResources.GetBackendType(), + textureSize); + auto imageType = policy == TextureHost::NativeTexturePolicy::REQUIRE + ? 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(aBuilder.GetBackendType())); +} + +bool DXGIYCbCrTextureHostD3D11::SupportsExternalCompositing( + WebRenderBackend aBackend) { + return aBackend == WebRenderBackend::SOFTWARE; +} + +bool DataTextureSourceD3D11::Update(DataSourceSurface* aSurface, + nsIntRegion* aDestRegion, + IntPoint* aSrcOffset, + IntPoint* aDstOffset) { + // 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_RELEASE_ASSERT(!aDstOffset); + 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 (static_cast<int32_t>(currentDesc.Width) != mSize.width || + static_cast<int32_t>(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(nullptr), 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_NTHANDLE | + 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((IDXGIResource1**)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; + } + + HANDLE sharedHandle; + hr = mSyncTexture->CreateSharedHandle( + nullptr, DXGI_SHARED_RESOURCE_READ | DXGI_SHARED_RESOURCE_WRITE, nullptr, + &sharedHandle); + if (FAILED(hr)) { + 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; + } + mSyncHandle = new gfx::FileHandleWrapper(UniqueFileHandle(sharedHandle)); + + 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; + } + + if (!mSyncHandle) { + return false; + } + + RefPtr<ID3D11Device1> device1; + aDevice->QueryInterface((ID3D11Device1**)getter_AddRefs(device1)); + if (!device1) { + gfxCriticalNoteOnce << "Failed to get ID3D11Device1"; + return 0; + } + + HRESULT hr = device1->OpenSharedResource1( + mSyncHandle->GetHandle(), __uuidof(ID3D11Texture2D), + (void**)(ID3D11Texture2D**)getter_AddRefs(mSyncTexture)); + if (FAILED(hr) || !mSyncTexture) { + gfxCriticalNote << "Failed to OpenSharedResource1 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()) { + 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..95368e6157 --- /dev/null +++ b/gfx/layers/d3d11/TextureD3D11.h @@ -0,0 +1,604 @@ +/* -*- 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 <d3d11_1.h> +#include <vector> + +#include "d3d9.h" +#include "gfxWindowsPlatform.h" +#include "mozilla/DataMutex.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 gfx { +class FileHandleWrapper; +struct FenceInfo; +} // namespace gfx + +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 IMFSampleUsageInfo; + +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); + + static already_AddRefed<TextureClient> CreateTextureClient( + ID3D11Texture2D* aTexture, uint32_t aIndex, gfx::IntSize aSize, + gfx::SurfaceFormat aFormat, gfx::ColorSpace2 aColorSpace, + gfx::ColorRange aColorRange, KnowsCompositor* aKnowsCompositor, + RefPtr<IMFSampleUsageInfo> aUsageInfo); + + 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; + } + + TextureType GetTextureType() const override { return TextureType::D3D11; } + + void FillInfo(TextureData::Info& aInfo) const override; + + bool Serialize(SurfaceDescriptor& aOutDescrptor) override; + void GetSubDescriptor(RemoteDecoderVideoSubDescriptor* aOutDesc) override; + + 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; + + void SetGpuProcessTextureId(GpuProcessTextureId aTextureId) { + mGpuProcessTextureId = Some(aTextureId); + } + + Maybe<GpuProcessTextureId> GetGpuProcessTextureId() { + return mGpuProcessTextureId; + } + + void RegisterQuery(RefPtr<ID3D11Query> aQuery); + + private: + D3D11TextureData(ID3D11Texture2D* aTexture, uint32_t aArrayIndex, + RefPtr<gfx::FileHandleWrapper> aSharedHandle, + gfx::IntSize aSize, gfx::SurfaceFormat aFormat, + TextureAllocationFlags aFlags); + + 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; + const gfx::IntSize mSize; + const gfx::SurfaceFormat mFormat; + + public: + gfx::ColorSpace2 mColorSpace = gfx::ColorSpace2::SRGB; + + private: + gfx::ColorRange mColorRange = gfx::ColorRange::LIMITED; + bool mNeedsClear = false; + const bool mHasKeyedMutex; + + RefPtr<ID3D11Texture2D> mTexture; + const RefPtr<gfx::FileHandleWrapper> mSharedHandle; + Maybe<GpuProcessTextureId> mGpuProcessTextureId; + uint32_t mArrayIndex = 0; + const TextureAllocationFlags mAllocationFlags; + Maybe<GpuProcessQueryId> mGpuProcessQueryId; +}; + +class DXGIYCbCrTextureData : public TextureData { + friend class gl::GLBlitHelper; + + public: + 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<gfx::FileHandleWrapper> 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, + gfx::IntPoint* aDstOffset = 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); + + void DeallocateDeviceData() override {} + + gfx::SurfaceFormat GetFormat() const override { return mFormat; } + + bool LockWithoutCompositor() override; + void UnlockWithoutCompositor() override; + + gfx::IntSize GetSize() const override { return mSize; } + 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(WebRenderBackend aBackend) override; + + protected: + bool LockInternal(); + void UnlockInternal(); + + bool EnsureTextureSource(); + + RefPtr<ID3D11Device> GetDevice(); + + bool EnsureTexture(); + + RefPtr<ID3D11Device> mDevice; + RefPtr<ID3D11Texture2D> mTexture; + Maybe<GpuProcessTextureId> mGpuProcessTextureId; + Maybe<GpuProcessQueryId> mGpuProcessQueryId; + uint32_t mArrayIndex = 0; + RefPtr<DataTextureSourceD3D11> mTextureSource; + gfx::IntSize mSize; + const RefPtr<gfx::FileHandleWrapper> mHandle; + gfx::SurfaceFormat mFormat; + bool mHasKeyedMutex; + gfx::FenceInfo mAcquireFenceInfo; + + public: + const gfx::ColorSpace2 mColorSpace; + + protected: + const gfx::ColorRange mColorRange; + bool mIsLocked; +}; + +class DXGIYCbCrTextureHostD3D11 : public TextureHost { + public: + DXGIYCbCrTextureHostD3D11(TextureFlags aFlags, + const SurfaceDescriptorDXGIYCbCr& aDescriptor); + + void DeallocateDeviceData() 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; } + + 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(WebRenderBackend aBackend) override; + + protected: + RefPtr<ID3D11Texture2D> mTextures[3]; + + gfx::IntSize mSize; + gfx::IntSize mSizeY; + gfx::IntSize mSizeCbCr; + // Handles will be closed automatically when `UniqueFileHandle` gets + // destroyed. + RefPtr<gfx::FileHandleWrapper> 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<IDXGIResource1> 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 MOZ_UNANNOTATED; + RefPtr<ID3D11Texture2D> mSyncTexture; + std::vector<ID3D11Texture2D*> mSyncedTextures; + + private: + 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/TextureHostWrapperD3D11.cpp b/gfx/layers/d3d11/TextureHostWrapperD3D11.cpp new file mode 100644 index 0000000000..380307fcea --- /dev/null +++ b/gfx/layers/d3d11/TextureHostWrapperD3D11.cpp @@ -0,0 +1,396 @@ +/* -*- 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 "TextureHostWrapperD3D11.h" + +#include <d3d11.h> + +#include "mozilla/gfx/DeviceManagerDx.h" +#include "mozilla/layers/AsyncImagePipelineManager.h" +#include "mozilla/layers/CompositorThread.h" +#include "mozilla/layers/GpuProcessD3D11TextureMap.h" +#include "mozilla/layers/TextureD3D11.h" +#include "mozilla/layers/WebRenderTextureHost.h" +#include "mozilla/SharedThreadPool.h" + +namespace mozilla { +namespace layers { + +TextureWrapperD3D11Allocator::TextureWrapperD3D11Allocator() + : mThread(SharedThreadPool::Get("TextureUpdate"_ns, 1)), + mMutex("TextureWrapperD3D11Allocator::mMutex") {} +TextureWrapperD3D11Allocator::~TextureWrapperD3D11Allocator() = default; + +RefPtr<ID3D11Texture2D> TextureWrapperD3D11Allocator::CreateOrRecycle( + gfx::SurfaceFormat aSurfaceFormat, gfx::IntSize aSize) { + MOZ_ASSERT(mThread->IsOnCurrentThread()); + + RefPtr<ID3D11Device> device = gfx::DeviceManagerDx::Get()->GetImageDevice(); + { + MutexAutoLock lock(mMutex); + if (!!mDevice && mDevice != device) { + // Device reset might happen + ClearAllTextures(lock); + mDevice = nullptr; + } + + if (!mDevice) { + mDevice = device; + MOZ_ASSERT(mDevice == gfx::DeviceManagerDx::Get()->GetCompositorDevice()); + } + + if (!mDevice) { + MOZ_ASSERT_UNREACHABLE("unexpected to be called"); + return nullptr; + } + + if (aSurfaceFormat != gfx::SurfaceFormat::NV12) { + MOZ_ASSERT_UNREACHABLE("unexpected to be called"); + return nullptr; + } + + if (mSize != aSize) { + ClearAllTextures(lock); + mSize = aSize; + } + + if (!mRecycledTextures.empty()) { + RefPtr<ID3D11Texture2D> texture2D = mRecycledTextures.front(); + mRecycledTextures.pop_front(); + return texture2D; + } + } + + CD3D11_TEXTURE2D_DESC desc( + DXGI_FORMAT_NV12, mSize.width, mSize.height, 1, 1, + D3D11_BIND_RENDER_TARGET | D3D11_BIND_SHADER_RESOURCE); + + RefPtr<ID3D11Texture2D> texture2D; + HRESULT hr = + device->CreateTexture2D(&desc, nullptr, getter_AddRefs(texture2D)); + if (FAILED(hr) || !texture2D) { + return nullptr; + } + + EnsureStagingTextureNV12(device); + if (!mStagingTexture) { + return nullptr; + } + + return texture2D; +} + +void TextureWrapperD3D11Allocator::EnsureStagingTextureNV12( + RefPtr<ID3D11Device> aDevice) { + MOZ_ASSERT(mThread->IsOnCurrentThread()); + MOZ_ASSERT(aDevice); + + if (mStagingTexture) { + return; + } + + D3D11_TEXTURE2D_DESC desc = {}; + desc.Width = mSize.width; + desc.Height = mSize.height; + desc.Format = DXGI_FORMAT_NV12; + desc.MipLevels = 1; + desc.ArraySize = 1; + desc.Usage = D3D11_USAGE_STAGING; + desc.BindFlags = 0; + desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE; + desc.MiscFlags = 0; + desc.SampleDesc.Count = 1; + + RefPtr<ID3D11Texture2D> stagingTexture; + HRESULT hr = + aDevice->CreateTexture2D(&desc, nullptr, getter_AddRefs(stagingTexture)); + if (FAILED(hr) || !stagingTexture) { + return; + } + mStagingTexture = stagingTexture; +} + +RefPtr<ID3D11Texture2D> TextureWrapperD3D11Allocator::GetStagingTextureNV12() { + MOZ_ASSERT(mThread->IsOnCurrentThread()); + + return mStagingTexture; +} + +RefPtr<ID3D11Device> TextureWrapperD3D11Allocator::GetDevice() { + MOZ_ASSERT(mThread->IsOnCurrentThread()); + + MutexAutoLock lock(mMutex); + return mDevice; +}; + +void TextureWrapperD3D11Allocator::ClearAllTextures( + const MutexAutoLock& aProofOfLock) { + MOZ_ASSERT(mThread->IsOnCurrentThread()); + + mStagingTexture = nullptr; + mRecycledTextures.clear(); +} + +void TextureWrapperD3D11Allocator::RecycleTexture( + RefPtr<ID3D11Texture2D>& aTexture) { + MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread()); + MOZ_ASSERT(aTexture); + + RefPtr<ID3D11Device> device; + aTexture->GetDevice(getter_AddRefs(device)); + + D3D11_TEXTURE2D_DESC desc; + aTexture->GetDesc(&desc); + + { + MutexAutoLock lock(mMutex); + if (device != mDevice || desc.Format != DXGI_FORMAT_NV12 || + desc.Width != static_cast<UINT>(mSize.width) || + desc.Height != static_cast<UINT>(mSize.height)) { + return; + } + + const auto kMaxPooledSized = 5; + if (mRecycledTextures.size() > kMaxPooledSized) { + return; + } + mRecycledTextures.emplace_back(aTexture); + } +} + +void TextureWrapperD3D11Allocator::RegisterTextureHostWrapper( + const wr::ExternalImageId& aExternalImageId, + RefPtr<TextureHost> aTextureHost) { + MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread()); + + auto it = mTextureHostWrappers.find(wr::AsUint64(aExternalImageId)); + if (it != mTextureHostWrappers.end()) { + MOZ_ASSERT_UNREACHABLE("unexpected to be called"); + return; + } + mTextureHostWrappers.emplace(wr::AsUint64(aExternalImageId), aTextureHost); +} + +void TextureWrapperD3D11Allocator::UnregisterTextureHostWrapper( + const wr::ExternalImageId& aExternalImageId) { + MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread()); + + auto it = mTextureHostWrappers.find(wr::AsUint64(aExternalImageId)); + if (it == mTextureHostWrappers.end()) { + MOZ_ASSERT_UNREACHABLE("unexpected to be called"); + return; + } + mTextureHostWrappers.erase(it); +} + +RefPtr<TextureHost> TextureWrapperD3D11Allocator::GetTextureHostWrapper( + const wr::ExternalImageId& aExternalImageId) { + MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread()); + + auto it = mTextureHostWrappers.find(wr::AsUint64(aExternalImageId)); + if (it == mTextureHostWrappers.end()) { + return nullptr; + } + return it->second; +} + +// static +RefPtr<TextureHost> TextureHostWrapperD3D11::CreateFromBufferTexture( + const RefPtr<TextureWrapperD3D11Allocator>& aAllocator, + TextureHost* aTextureHost) { + MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread()); + MOZ_ASSERT(aAllocator); + MOZ_ASSERT(aTextureHost); + + if (!XRE_IsGPUProcess()) { + return nullptr; + } + + auto* bufferTexture = aTextureHost->AsBufferTextureHost(); + if (!bufferTexture || bufferTexture->GetFormat() != gfx::SurfaceFormat::YUV) { + MOZ_ASSERT_UNREACHABLE("unexpected to be called"); + return nullptr; + } + + auto extId = aTextureHost->GetMaybeExternalImageId(); + MOZ_RELEASE_ASSERT(extId.isSome()); + + // Reuse TextureHostWrapperD3D11 if it is still valid. + RefPtr<TextureHost> textureHost = + aAllocator->GetTextureHostWrapper(extId.ref()); + if (textureHost) { + return textureHost; + } + + auto size = bufferTexture->GetSize(); + auto colorDepth = bufferTexture->GetColorDepth(); + auto colorRange = bufferTexture->GetColorRange(); + auto chromaSubsampling = bufferTexture->GetChromaSubsampling(); + + // Check if data could be used with NV12 + // XXX support gfx::ColorRange::FULL + if (size.width % 2 != 0 || size.height % 2 != 0 || + colorDepth != gfx::ColorDepth::COLOR_8 || + colorRange != gfx::ColorRange::LIMITED || + chromaSubsampling != gfx::ChromaSubsampling::HALF_WIDTH_AND_HEIGHT) { + return nullptr; + } + + auto id = GpuProcessD3D11TextureMap::GetNextTextureId(); + auto flags = aTextureHost->GetFlags() | TextureFlags::SOFTWARE_DECODED_VIDEO; + + auto colorSpace = ToColorSpace2(bufferTexture->GetYUVColorSpace()); + + auto descD3D10 = SurfaceDescriptorD3D10( + nullptr, Some(id), + /* arrayIndex */ 0, gfx::SurfaceFormat::NV12, size, colorSpace, + colorRange, /* hasKeyedMutex */ false, /* fenceInfo */ Nothing(), + /* gpuProcessQueryId */ Nothing()); + + RefPtr<DXGITextureHostD3D11> textureHostD3D11 = + new DXGITextureHostD3D11(flags, descD3D10); + + RefPtr<TextureHostWrapperD3D11> textureHostWrapper = + new TextureHostWrapperD3D11(flags, aAllocator, id, textureHostD3D11, + aTextureHost, extId.ref()); + textureHostWrapper->PostTask(); + + auto externalImageId = AsyncImagePipelineManager::GetNextExternalImageId(); + + textureHost = + new WebRenderTextureHost(flags, textureHostWrapper, externalImageId); + aAllocator->RegisterTextureHostWrapper(extId.ref(), textureHost); + + return textureHost; +} + +TextureHostWrapperD3D11::TextureHostWrapperD3D11( + TextureFlags aFlags, const RefPtr<TextureWrapperD3D11Allocator>& aAllocator, + const GpuProcessTextureId aTextureId, + DXGITextureHostD3D11* aTextureHostD3D11, TextureHost* aWrappedTextureHost, + const wr::ExternalImageId aWrappedExternalImageId) + : TextureHost(TextureHostType::DXGI, aFlags), + mAllocator(aAllocator), + mTextureId(aTextureId), + mTextureHostD3D11(aTextureHostD3D11), + mWrappedTextureHost(aWrappedTextureHost), + mWrappedExternalImageId(aWrappedExternalImageId) { + MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread()); + MOZ_ASSERT(mAllocator); + MOZ_ASSERT(mTextureHostD3D11); + MOZ_ASSERT(mWrappedTextureHost); + + MOZ_COUNT_CTOR(TextureHostWrapperD3D11); +} + +TextureHostWrapperD3D11::~TextureHostWrapperD3D11() { + MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread()); + MOZ_COUNT_DTOR(TextureHostWrapperD3D11); + + auto* textureMap = GpuProcessD3D11TextureMap::Get(); + if (textureMap) { + RefPtr<ID3D11Texture2D> texture = textureMap->GetTexture(mTextureId); + if (texture) { + mAllocator->RecycleTexture(texture); + textureMap->Unregister(mTextureId); + } + } else { + gfxCriticalNoteOnce << "GpuProcessD3D11TextureMap does not exist"; + } +} + +void TextureHostWrapperD3D11::PostTask() { + GpuProcessD3D11TextureMap::Get()->PostUpdateTextureDataTask( + mTextureId, this, mWrappedTextureHost, mAllocator); +} + +bool TextureHostWrapperD3D11::IsValid() { return true; } + +gfx::ColorRange TextureHostWrapperD3D11::GetColorRange() const { + return mTextureHostD3D11->GetColorRange(); +} + +gfx::IntSize TextureHostWrapperD3D11::GetSize() const { + return mTextureHostD3D11->GetSize(); +} + +gfx::SurfaceFormat TextureHostWrapperD3D11::GetFormat() const { + return mTextureHostD3D11->GetFormat(); +} + +void TextureHostWrapperD3D11::CreateRenderTexture( + const wr::ExternalImageId& aExternalImageId) { + MOZ_ASSERT(mExternalImageId.isSome()); + + mTextureHostD3D11->EnsureRenderTexture(mExternalImageId); +} + +void TextureHostWrapperD3D11::MaybeDestroyRenderTexture() { + // TextureHostWrapperD3D11 does not create RenderTexture, then + // TextureHostWrapperD3D11 does not need to destroy RenderTexture. + mExternalImageId = Nothing(); +} + +uint32_t TextureHostWrapperD3D11::NumSubTextures() { + return mTextureHostD3D11->NumSubTextures(); +} + +void TextureHostWrapperD3D11::PushResourceUpdates( + wr::TransactionBuilder& aResources, ResourceUpdateOp aOp, + const Range<wr::ImageKey>& aImageKeys, const wr::ExternalImageId& aExtID) { + mTextureHostD3D11->PushResourceUpdates(aResources, aOp, aImageKeys, aExtID); +} + +void TextureHostWrapperD3D11::PushDisplayItems( + wr::DisplayListBuilder& aBuilder, const wr::LayoutRect& aBounds, + const wr::LayoutRect& aClip, wr::ImageRendering aFilter, + const Range<wr::ImageKey>& aImageKeys, PushDisplayItemFlagSet aFlags) { + MOZ_ASSERT(aImageKeys.length() > 0); + + mTextureHostD3D11->PushDisplayItems(aBuilder, aBounds, aClip, aFilter, + aImageKeys, aFlags); +} + +bool TextureHostWrapperD3D11::SupportsExternalCompositing( + WebRenderBackend aBackend) { + return mTextureHostD3D11->SupportsExternalCompositing(aBackend); +} + +void TextureHostWrapperD3D11::UnbindTextureSource() { + mTextureHostD3D11->UnbindTextureSource(); + // Handle read unlock + TextureHost::UnbindTextureSource(); +} + +void TextureHostWrapperD3D11::NotifyNotUsed() { + MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread()); + + mAllocator->UnregisterTextureHostWrapper(mWrappedExternalImageId); + + MOZ_ASSERT(mWrappedTextureHost); + if (mWrappedTextureHost) { + mWrappedTextureHost = nullptr; + } + mTextureHostD3D11->NotifyNotUsed(); + TextureHost::NotifyNotUsed(); +} + +BufferTextureHost* TextureHostWrapperD3D11::AsBufferTextureHost() { + return nullptr; +} + +bool TextureHostWrapperD3D11::IsWrappingSurfaceTextureHost() { return false; } + +TextureHostType TextureHostWrapperD3D11::GetTextureHostType() { + return mTextureHostD3D11->GetTextureHostType(); +} + +bool TextureHostWrapperD3D11::NeedsDeferredDeletion() const { + return mTextureHostD3D11->NeedsDeferredDeletion(); +} + +} // namespace layers +} // namespace mozilla diff --git a/gfx/layers/d3d11/TextureHostWrapperD3D11.h b/gfx/layers/d3d11/TextureHostWrapperD3D11.h new file mode 100644 index 0000000000..dd0afc8be7 --- /dev/null +++ b/gfx/layers/d3d11/TextureHostWrapperD3D11.h @@ -0,0 +1,173 @@ +/* -*- 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_TextureHostWrapperD3D11_H +#define MOZILLA_GFX_TextureHostWrapperD3D11_H + +#include <deque> +#include <unordered_map> + +#include "mozilla/layers/LayersTypes.h" +#include "mozilla/layers/TextureHost.h" +#include "mozilla/UniquePtr.h" + +class nsIThreadPool; +struct ID3D11Texture2D; + +namespace mozilla { +namespace layers { + +class DXGITextureHostD3D11; + +/** + * A Class that allocates and recycles ID3D11Texture2D in TextureUpdate thread. + * And manages in use TextureHostWrapperD3D11s in compositor thread. + */ +class TextureWrapperD3D11Allocator { + public: + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(TextureWrapperD3D11Allocator) + + TextureWrapperD3D11Allocator(); + + RefPtr<ID3D11Texture2D> CreateOrRecycle(gfx::SurfaceFormat aSurfaceFormat, + gfx::IntSize aSize); + + void EnsureStagingTextureNV12(RefPtr<ID3D11Device> aDevice); + + RefPtr<ID3D11Texture2D> GetStagingTextureNV12(); + + RefPtr<ID3D11Device> GetDevice(); + + void RecycleTexture(RefPtr<ID3D11Texture2D>& aTexture); + + void RegisterTextureHostWrapper(const wr::ExternalImageId& aExternalImageId, + RefPtr<TextureHost> aTextureHost); + void UnregisterTextureHostWrapper( + const wr::ExternalImageId& aExternalImageId); + + RefPtr<TextureHost> GetTextureHostWrapper( + const wr::ExternalImageId& aExternalImageId); + + // Holds TextureUpdate thread. + // Use the SharedThreadPool to create an nsIThreadPool with a maximum of one + // thread, which is equivalent to an nsIThread for our purposes. + const nsCOMPtr<nsIThreadPool> mThread; + + protected: + ~TextureWrapperD3D11Allocator(); + + void ClearAllTextures(const MutexAutoLock& aProofOfLock); + + // Holds TextureHostWrapperD3D11s that are in use. TextureHostWrapperD3D11 is + // wrapped by WebRenderTextureHost. Accessed only from compositor thread + std::unordered_map<uint64_t, RefPtr<TextureHost>> mTextureHostWrappers; + + // Accessed only from TextureUpdate thread + RefPtr<ID3D11Texture2D> mStagingTexture; + + Mutex mMutex; + + // Protected by mMutex + + RefPtr<ID3D11Device> mDevice; + gfx::IntSize mSize; + std::deque<RefPtr<ID3D11Texture2D>> mRecycledTextures; +}; + +/** + * A TextureHost that exposes DXGITextureHostD3D11 as wrapping YUV + * BufferTextureHost. The DXGITextureHostD3D11 holds GpuProcessTextureId of + * ID3D11Texture2D. The ID3D11Texture2D is allocated and data updated in + * TextureUpdate thread. + */ +class TextureHostWrapperD3D11 : public TextureHost { + public: + static RefPtr<TextureHost> CreateFromBufferTexture( + const RefPtr<TextureWrapperD3D11Allocator>& aAllocator, + TextureHost* aTextureHost); + + void DeallocateDeviceData() override {} + + gfx::SurfaceFormat GetFormat() const override; + + already_AddRefed<gfx::DataSourceSurface> GetAsSurface() override { + return nullptr; // XXX - implement this (for MOZ_DUMP_PAINTING) + } + + gfx::ColorRange GetColorRange() const override; + + gfx::IntSize GetSize() const override; + + bool IsValid() override; + +#ifdef MOZ_LAYERS_HAVE_LOG + const char* Name() override { return "TextureHostWrapperD3D11"; } +#endif + + void CreateRenderTexture( + const wr::ExternalImageId& aExternalImageId) override; + + void MaybeDestroyRenderTexture() 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(WebRenderBackend aBackend) override; + + void UnbindTextureSource() override; + + void NotifyNotUsed() override; + + BufferTextureHost* AsBufferTextureHost() override; + + bool IsWrappingSurfaceTextureHost() override; + + TextureHostType GetTextureHostType() override; + + bool NeedsDeferredDeletion() const override; + + TextureHostWrapperD3D11* AsTextureHostWrapperD3D11() override { return this; } + + void PostTask(); + + bool UpdateTextureData(); + + protected: + TextureHostWrapperD3D11( + TextureFlags aFlags, + const RefPtr<TextureWrapperD3D11Allocator>& aAllocator, + const GpuProcessTextureId aTextureId, + DXGITextureHostD3D11* aTextureHostD3D11, TextureHost* aWrappedTextureHost, + const wr::ExternalImageId aWrappedExternalImageId); + + virtual ~TextureHostWrapperD3D11(); + + TextureHost* EnsureWrappedTextureHost(); + + const RefPtr<TextureWrapperD3D11Allocator> mAllocator; + const GpuProcessTextureId mTextureId; + // DXGITextureHostD3D11 that replaces the wrapping TextureHost. + const RefPtr<DXGITextureHostD3D11> mTextureHostD3D11; + // WebRenderTextureHost that wraps BufferTextureHost + // BufferTextureHost might be further wrapped by GPUVideoTextureHost. + CompositableTextureHostRef mWrappedTextureHost; + const wr::ExternalImageId mWrappedExternalImageId; +}; + +} // namespace layers +} // namespace mozilla + +#endif // MOZILLA_GFX_TextureHostWrapperD3D11_H diff --git a/gfx/layers/d3d11/genshaders.py b/gfx/layers/d3d11/genshaders.py new file mode 100644 index 0000000000..b9701978d5 --- /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/. +import argparse +import codecs +import locale +import os +import re +import subprocess +import sys +import tempfile + +import buildconfig +import yaml + + +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/shaders.manifest b/gfx/layers/d3d11/shaders.manifest new file mode 100644 index 0000000000..d8836d2740 --- /dev/null +++ b/gfx/layers/d3d11/shaders.manifest @@ -0,0 +1,13 @@ +- type: vs_4_0_level_9_3 + file: CompositorD3D11.hlsl + shaders: + - LayerQuadVS + +- type: ps_4_0_level_9_3 + file: CompositorD3D11.hlsl + shaders: + - SolidColorShader + - RGBShader + - RGBAShader + - YCbCrShader + - NV12Shader |