diff options
Diffstat (limited to '')
-rw-r--r-- | gfx/layers/d3d11/CompositorD3D11.cpp | 1794 |
1 files changed, 1794 insertions, 0 deletions
diff --git a/gfx/layers/d3d11/CompositorD3D11.cpp b/gfx/layers/d3d11/CompositorD3D11.cpp new file mode 100644 index 0000000000..69b7617112 --- /dev/null +++ b/gfx/layers/d3d11/CompositorD3D11.cpp @@ -0,0 +1,1794 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "CompositorD3D11.h" + +#include "TextureD3D11.h" + +#include "gfxWindowsPlatform.h" +#include "nsIWidget.h" +#include "Layers.h" +#include "mozilla/gfx/D3D11Checks.h" +#include "mozilla/gfx/DeviceManagerDx.h" +#include "mozilla/gfx/GPUParent.h" +#include "mozilla/gfx/Swizzle.h" +#include "mozilla/layers/ImageHost.h" +#include "mozilla/layers/ContentHost.h" +#include "mozilla/layers/Diagnostics.h" +#include "mozilla/layers/DiagnosticsD3D11.h" +#include "mozilla/layers/Effects.h" +#include "mozilla/layers/HelpersD3D11.h" +#include "nsWindowsHelpers.h" +#include "gfxConfig.h" +#include "gfxCrashReporterUtils.h" +#include "gfxUtils.h" +#include "mozilla/gfx/StackArray.h" +#include "mozilla/widget/WinCompositorWidget.h" + +#include "mozilla/EnumeratedArray.h" +#include "mozilla/StaticPrefs_gfx.h" +#include "mozilla/StaticPrefs_layers.h" +#include "mozilla/Telemetry.h" +#include "BlendShaderConstants.h" + +#include "D3D11ShareHandleImage.h" +#include "DeviceAttachmentsD3D11.h" + +#include <versionhelpers.h> // For IsWindows8OrGreater +#include <winsdkver.h> + +namespace mozilla { + +using namespace gfx; + +namespace layers { + +bool CanUsePartialPresents(ID3D11Device* aDevice); + +const FLOAT sBlendFactor[] = {0, 0, 0, 0}; + +class AsyncReadbackBufferD3D11 final : public AsyncReadbackBuffer { + public: + AsyncReadbackBufferD3D11(ID3D11DeviceContext* aContext, + ID3D11Texture2D* aTexture, const IntSize& aSize); + + bool MapAndCopyInto(DataSourceSurface* aSurface, + const IntSize& aReadSize) const override; + + ID3D11Texture2D* GetTexture() { return mTexture; } + + private: + RefPtr<ID3D11DeviceContext> mContext; + RefPtr<ID3D11Texture2D> mTexture; +}; + +AsyncReadbackBufferD3D11::AsyncReadbackBufferD3D11( + ID3D11DeviceContext* aContext, ID3D11Texture2D* aTexture, + const IntSize& aSize) + : AsyncReadbackBuffer(aSize), mContext(aContext), mTexture(aTexture) {} + +bool AsyncReadbackBufferD3D11::MapAndCopyInto(DataSourceSurface* aSurface, + const IntSize& aReadSize) const { + D3D11_MAPPED_SUBRESOURCE map; + HRESULT hr = mContext->Map(mTexture, 0, D3D11_MAP_READ, 0, &map); + + if (FAILED(hr)) { + return false; + } + + RefPtr<DataSourceSurface> sourceSurface = + Factory::CreateWrappingDataSourceSurface(static_cast<uint8_t*>(map.pData), + map.RowPitch, mSize, + SurfaceFormat::B8G8R8A8); + + bool result; + { + DataSourceSurface::ScopedMap sourceMap(sourceSurface, + DataSourceSurface::READ); + DataSourceSurface::ScopedMap destMap(aSurface, DataSourceSurface::WRITE); + + result = SwizzleData(sourceMap.GetData(), sourceMap.GetStride(), + SurfaceFormat::B8G8R8A8, destMap.GetData(), + destMap.GetStride(), aSurface->GetFormat(), aReadSize); + } + + mContext->Unmap(mTexture, 0); + + return result; +} + +CompositorD3D11::CompositorD3D11(CompositorBridgeParent* aParent, + widget::CompositorWidget* aWidget) + : Compositor(aWidget, aParent), + mWindowRTCopy(nullptr), + mAttachments(nullptr), + mHwnd(nullptr), + mDisableSequenceForNextFrame(false), + mAllowPartialPresents(false), + mIsDoubleBuffered(false), + mVerifyBuffersFailed(false), + mUseMutexOnPresent(false), + mUseForSoftwareWebRender(false) { + mUseMutexOnPresent = StaticPrefs::gfx_use_mutex_on_present_AtStartup(); +} + +CompositorD3D11::~CompositorD3D11() {} + +template <typename VertexType> +void CompositorD3D11::SetVertexBuffer(ID3D11Buffer* aBuffer) { + UINT size = sizeof(VertexType); + UINT offset = 0; + mContext->IASetVertexBuffers(0, 1, &aBuffer, &size, &offset); +} + +bool CompositorD3D11::SupportsLayerGeometry() const { + return StaticPrefs::layers_geometry_d3d11_enabled(); +} + +bool CompositorD3D11::UpdateDynamicVertexBuffer( + const nsTArray<gfx::TexturedTriangle>& aTriangles) { + HRESULT hr; + + // Resize the dynamic vertex buffer if needed. + if (!mAttachments->EnsureTriangleBuffer(aTriangles.Length())) { + return false; + } + + D3D11_MAPPED_SUBRESOURCE resource{}; + hr = mContext->Map(mAttachments->mDynamicVertexBuffer, 0, + D3D11_MAP_WRITE_DISCARD, 0, &resource); + + if (Failed(hr, "map dynamic vertex buffer")) { + return false; + } + + const nsTArray<TexturedVertex> vertices = + TexturedTrianglesToVertexArray(aTriangles); + + memcpy(resource.pData, vertices.Elements(), + vertices.Length() * sizeof(TexturedVertex)); + + mContext->Unmap(mAttachments->mDynamicVertexBuffer, 0); + + return true; +} + +bool CompositorD3D11::Initialize(nsCString* const out_failureReason) { + ScopedGfxFeatureReporter reporter("D3D11 Layers"); + + HRESULT hr; + + DeviceManagerDx::Get()->GetCompositorDevices(&mDevice, &mAttachments); + if (!mDevice) { + gfxCriticalNote << "[D3D11] failed to get compositor device."; + *out_failureReason = "FEATURE_FAILURE_D3D11_NO_DEVICE"; + return false; + } + if (!mAttachments || !mAttachments->IsValid()) { + gfxCriticalNote << "[D3D11] failed to get compositor device attachments"; + *out_failureReason = mAttachments ? mAttachments->GetFailureId() + : "FEATURE_FAILURE_NO_ATTACHMENTS"_ns; + return false; + } + + mDevice->GetImmediateContext(getter_AddRefs(mContext)); + if (!mContext) { + gfxCriticalNote << "[D3D11] failed to get immediate context"; + *out_failureReason = "FEATURE_FAILURE_D3D11_CONTEXT"; + return false; + } + + mDiagnostics = MakeUnique<DiagnosticsD3D11>(mDevice, mContext); + mFeatureLevel = mDevice->GetFeatureLevel(); + + mHwnd = mWidget->AsWindows()->GetHwnd(); + + memset(&mVSConstants, 0, sizeof(VertexShaderConstants)); + + RefPtr<IDXGIDevice> dxgiDevice; + RefPtr<IDXGIAdapter> dxgiAdapter; + + mDevice->QueryInterface(dxgiDevice.StartAssignment()); + dxgiDevice->GetAdapter(getter_AddRefs(dxgiAdapter)); + + { + RefPtr<IDXGIFactory> dxgiFactory; + dxgiAdapter->GetParent(IID_PPV_ARGS(dxgiFactory.StartAssignment())); + + RefPtr<IDXGIFactory2> dxgiFactory2; + hr = dxgiFactory->QueryInterface( + (IDXGIFactory2**)getter_AddRefs(dxgiFactory2)); + +#if (_WIN32_WINDOWS_MAXVER >= 0x0A00) + if (gfxVars::UseDoubleBufferingWithCompositor() && SUCCEEDED(hr) && + dxgiFactory2) { + // DXGI_SCALING_NONE is not available on Windows 7 with Platform Update. + // This looks awful for things like the awesome bar and browser window + // resizing so we don't use a flip buffer chain here. When using + // EFFECT_SEQUENTIAL it looks like windows doesn't stretch the surface + // when resizing. We chose not to run this before Windows 10 because it + // appears sometimes this breaks our ability to test ASAP compositing. + RefPtr<IDXGISwapChain1> swapChain; + + DXGI_SWAP_CHAIN_DESC1 swapDesc; + ::ZeroMemory(&swapDesc, sizeof(swapDesc)); + swapDesc.Width = 0; + swapDesc.Height = 0; + swapDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM; + swapDesc.SampleDesc.Count = 1; + swapDesc.SampleDesc.Quality = 0; + swapDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; + swapDesc.BufferCount = 2; + swapDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL; + swapDesc.Scaling = DXGI_SCALING_NONE; + mIsDoubleBuffered = true; + swapDesc.Flags = 0; + + /** + * Create a swap chain, this swap chain will contain the backbuffer for + * the window we draw to. The front buffer is the full screen front + * buffer. + */ + hr = dxgiFactory2->CreateSwapChainForHwnd(mDevice, mHwnd, &swapDesc, + nullptr, nullptr, + getter_AddRefs(swapChain)); + if (SUCCEEDED(hr)) { + DXGI_RGBA color = {1.0f, 1.0f, 1.0f, 1.0f}; + swapChain->SetBackgroundColor(&color); + + mSwapChain = swapChain; + } else if (mWidget->AsWindows()->GetCompositorHwnd()) { + // Destroy compositor window. + mWidget->AsWindows()->DestroyCompositorWindow(); + mHwnd = mWidget->AsWindows()->GetHwnd(); + } + } + + // In some configurations double buffering may have failed with an + // ACCESS_DENIED error. + if (!mSwapChain) +#endif + { + if (mWidget->AsWindows()->GetCompositorHwnd()) { + // Destroy compositor window. + mWidget->AsWindows()->DestroyCompositorWindow(); + mHwnd = mWidget->AsWindows()->GetHwnd(); + } + + DXGI_SWAP_CHAIN_DESC swapDesc; + ::ZeroMemory(&swapDesc, sizeof(swapDesc)); + swapDesc.BufferDesc.Width = 0; + swapDesc.BufferDesc.Height = 0; + swapDesc.BufferDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM; + swapDesc.BufferDesc.RefreshRate.Numerator = 60; + swapDesc.BufferDesc.RefreshRate.Denominator = 1; + swapDesc.SampleDesc.Count = 1; + swapDesc.SampleDesc.Quality = 0; + swapDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; + swapDesc.BufferCount = 1; + swapDesc.OutputWindow = mHwnd; + swapDesc.Windowed = TRUE; + swapDesc.Flags = 0; + swapDesc.SwapEffect = DXGI_SWAP_EFFECT_SEQUENTIAL; + + /** + * Create a swap chain, this swap chain will contain the backbuffer for + * the window we draw to. The front buffer is the full screen front + * buffer. + */ + hr = dxgiFactory->CreateSwapChain(dxgiDevice, &swapDesc, + getter_AddRefs(mSwapChain)); + if (Failed(hr, "create swap chain")) { + *out_failureReason = "FEATURE_FAILURE_D3D11_SWAP_CHAIN"; + return false; + } + } + + // We need this because we don't want DXGI to respond to Alt+Enter. + dxgiFactory->MakeWindowAssociation(mHwnd, DXGI_MWA_NO_WINDOW_CHANGES); + } + + if (!mWidget->InitCompositor(this)) { + *out_failureReason = "FEATURE_FAILURE_D3D11_INIT_COMPOSITOR"; + return false; + } + + mAllowPartialPresents = CanUsePartialPresents(mDevice); + + reporter.SetSuccessful(); + return true; +} + +bool CanUsePartialPresents(ID3D11Device* aDevice) { + if (StaticPrefs::gfx_partialpresent_force() > 0) { + return true; + } + if (StaticPrefs::gfx_partialpresent_force() < 0) { + return false; + } + if (DeviceManagerDx::Get()->IsWARP()) { + return true; + } + + DXGI_ADAPTER_DESC desc; + if (!D3D11Checks::GetDxgiDesc(aDevice, &desc)) { + return false; + } + + // We have to disable partial presents on NVIDIA (bug 1189940). + if (desc.VendorId == 0x10de) { + return false; + } + + return true; +} + +already_AddRefed<DataTextureSource> CompositorD3D11::CreateDataTextureSource( + TextureFlags aFlags) { + RefPtr<DataTextureSource> result = + new DataTextureSourceD3D11(gfx::SurfaceFormat::UNKNOWN, this, aFlags); + return result.forget(); +} + +TextureFactoryIdentifier CompositorD3D11::GetTextureFactoryIdentifier() { + TextureFactoryIdentifier ident; + ident.mMaxTextureSize = GetMaxTextureSize(); + ident.mParentProcessType = XRE_GetProcessType(); + ident.mParentBackend = LayersBackend::LAYERS_D3D11; + if (mWidget) { + ident.mUseCompositorWnd = !!mWidget->AsWindows()->GetCompositorHwnd(); + } + if (mAttachments->mSyncObject) { + ident.mSyncHandle = mAttachments->mSyncObject->GetSyncHandle(); + } + return ident; +} + +bool CompositorD3D11::CanUseCanvasLayerForSize(const gfx::IntSize& aSize) { + int32_t maxTextureSize = GetMaxTextureSize(); + + if (aSize.width > maxTextureSize || aSize.height > maxTextureSize) { + return false; + } + + return true; +} + +int32_t CompositorD3D11::GetMaxTextureSize() const { + return GetMaxTextureSizeForFeatureLevel(mFeatureLevel); +} + +already_AddRefed<CompositingRenderTarget> CompositorD3D11::CreateRenderTarget( + const gfx::IntRect& aRect, SurfaceInitMode aInit) { + MOZ_ASSERT(!aRect.IsZeroArea()); + + if (aRect.IsZeroArea()) { + return nullptr; + } + + CD3D11_TEXTURE2D_DESC desc( + DXGI_FORMAT_B8G8R8A8_UNORM, aRect.width, aRect.height, 1, 1, + D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_RENDER_TARGET); + + RefPtr<ID3D11Texture2D> texture; + HRESULT hr = + mDevice->CreateTexture2D(&desc, nullptr, getter_AddRefs(texture)); + if (FAILED(hr) || !texture) { + gfxCriticalNote << "Failed in CreateRenderTarget " << hexa(hr); + return nullptr; + } + + RefPtr<CompositingRenderTargetD3D11> rt = + new CompositingRenderTargetD3D11(texture, aRect.TopLeft()); + rt->SetSize(IntSize(aRect.Width(), aRect.Height())); + + if (aInit == INIT_MODE_CLEAR) { + FLOAT clear[] = {0, 0, 0, 0}; + mContext->ClearRenderTargetView(rt->mRTView, clear); + } + + return rt.forget(); +} + +RefPtr<ID3D11Texture2D> CompositorD3D11::CreateTexture( + const gfx::IntRect& aRect, const CompositingRenderTarget* aSource, + const gfx::IntPoint& aSourcePoint) { + MOZ_ASSERT(!aRect.IsZeroArea()); + + if (aRect.IsZeroArea()) { + return nullptr; + } + + CD3D11_TEXTURE2D_DESC desc( + DXGI_FORMAT_B8G8R8A8_UNORM, aRect.Width(), aRect.Height(), 1, 1, + D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_RENDER_TARGET); + + RefPtr<ID3D11Texture2D> texture; + HRESULT hr = + mDevice->CreateTexture2D(&desc, nullptr, getter_AddRefs(texture)); + + if (FAILED(hr) || !texture) { + gfxCriticalNote << "Failed in CreateRenderTargetFromSource " << hexa(hr); + HandleError(hr); + return nullptr; + } + + if (aSource) { + const CompositingRenderTargetD3D11* sourceD3D11 = + static_cast<const CompositingRenderTargetD3D11*>(aSource); + + const IntSize& srcSize = sourceD3D11->GetSize(); + MOZ_ASSERT(srcSize.width >= 0 && srcSize.height >= 0, + "render targets should have nonnegative sizes"); + + IntRect srcRect(IntPoint(), srcSize); + IntRect copyRect(aSourcePoint, aRect.Size()); + if (!srcRect.Contains(copyRect)) { + NS_WARNING("Could not copy the whole copy rect from the render target"); + } + + copyRect = copyRect.Intersect(srcRect); + + if (!copyRect.IsEmpty()) { + D3D11_BOX copyBox; + copyBox.front = 0; + copyBox.back = 1; + copyBox.left = copyRect.X(); + copyBox.top = copyRect.Y(); + copyBox.right = copyRect.XMost(); + copyBox.bottom = copyRect.YMost(); + + mContext->CopySubresourceRegion( + texture, 0, 0, 0, 0, sourceD3D11->GetD3D11Texture(), 0, ©Box); + } + } + + return texture; +} + +already_AddRefed<CompositingRenderTarget> +CompositorD3D11::CreateRenderTargetFromSource( + const gfx::IntRect& aRect, const CompositingRenderTarget* aSource, + const gfx::IntPoint& aSourcePoint) { + RefPtr<ID3D11Texture2D> texture = CreateTexture(aRect, aSource, aSourcePoint); + if (!texture) { + return nullptr; + } + + RefPtr<CompositingRenderTargetD3D11> rt = + new CompositingRenderTargetD3D11(texture, aRect.TopLeft()); + rt->SetSize(aRect.Size()); + + return rt.forget(); +} + +bool CompositorD3D11::ShouldAllowFrameRecording() const { +#ifdef MOZ_GECKO_PROFILER + return mAllowFrameRecording || + profiler_feature_active(ProfilerFeature::Screenshots); +#else + return mAllowFrameRecording; +#endif +} + +already_AddRefed<CompositingRenderTarget> +CompositorD3D11::GetWindowRenderTarget() const { + if (!ShouldAllowFrameRecording()) { + return nullptr; + } + + if (!mDefaultRT) { + return nullptr; + } + + const IntSize size = mDefaultRT->GetSize(); + + RefPtr<ID3D11Texture2D> rtTexture; + + if (!mWindowRTCopy || mWindowRTCopy->GetSize() != size) { + /* + * The compositor screenshots infrastructure is going to scale down the + * render target returned by this method. However, mDefaultRT does not + * contain a texture created wth the D3D11_BIND_SHADER_RESOURCE flag, so if + * we were to simply return mDefaultRT then scaling would fail. + */ + CD3D11_TEXTURE2D_DESC desc(DXGI_FORMAT_B8G8R8A8_UNORM, size.width, + size.height, 1, 1, D3D11_BIND_SHADER_RESOURCE); + + HRESULT hr = + mDevice->CreateTexture2D(&desc, nullptr, getter_AddRefs(rtTexture)); + if (FAILED(hr)) { + return nullptr; + } + + mWindowRTCopy = MakeRefPtr<CompositingRenderTargetD3D11>( + rtTexture, IntPoint(0, 0), DXGI_FORMAT_B8G8R8A8_UNORM); + mWindowRTCopy->SetSize(size); + } else { + rtTexture = mWindowRTCopy->GetD3D11Texture(); + } + + const RefPtr<ID3D11Texture2D> sourceTexture = mDefaultRT->GetD3D11Texture(); + mContext->CopyResource(rtTexture, sourceTexture); + + return RefPtr<CompositingRenderTarget>( + static_cast<CompositingRenderTarget*>(mWindowRTCopy)) + .forget(); +} + +bool CompositorD3D11::ReadbackRenderTarget(CompositingRenderTarget* aSource, + AsyncReadbackBuffer* aDest) { + RefPtr<CompositingRenderTargetD3D11> srcTexture = + static_cast<CompositingRenderTargetD3D11*>(aSource); + RefPtr<AsyncReadbackBufferD3D11> destBuffer = + static_cast<AsyncReadbackBufferD3D11*>(aDest); + + mContext->CopyResource(destBuffer->GetTexture(), + srcTexture->GetD3D11Texture()); + + return true; +} + +already_AddRefed<AsyncReadbackBuffer> +CompositorD3D11::CreateAsyncReadbackBuffer(const gfx::IntSize& aSize) { + RefPtr<ID3D11Texture2D> texture; + + CD3D11_TEXTURE2D_DESC desc(DXGI_FORMAT_B8G8R8A8_UNORM, aSize.width, + aSize.height, 1, 1, 0, D3D11_USAGE_STAGING, + D3D11_CPU_ACCESS_READ); + + HRESULT hr = + mDevice->CreateTexture2D(&desc, nullptr, getter_AddRefs(texture)); + + if (FAILED(hr)) { + HandleError(hr); + return nullptr; + } + + return MakeAndAddRef<AsyncReadbackBufferD3D11>(mContext, texture, aSize); +} + +bool CompositorD3D11::BlitRenderTarget(CompositingRenderTarget* aSource, + const gfx::IntSize& aSourceSize, + const gfx::IntSize& aDestSize) { + RefPtr<CompositingRenderTargetD3D11> source = + static_cast<CompositingRenderTargetD3D11*>(aSource); + + RefPtr<TexturedEffect> texturedEffect = CreateTexturedEffect( + SurfaceFormat::B8G8R8A8, source, SamplingFilter::LINEAR, true); + texturedEffect->mTextureCoords = + Rect(0, 0, Float(aSourceSize.width) / Float(source->GetSize().width), + Float(aSourceSize.height) / Float(source->GetSize().height)); + + EffectChain effect; + effect.mPrimaryEffect = texturedEffect; + + const Float scaleX = Float(aDestSize.width) / Float(aSourceSize.width); + const Float scaleY = Float(aDestSize.height) / (aSourceSize.height); + const Matrix4x4 transform = Matrix4x4::Scaling(scaleX, scaleY, 1.0f); + + const Rect sourceRect(0, 0, aSourceSize.width, aSourceSize.height); + + DrawQuad(sourceRect, IntRect(0, 0, aDestSize.width, aDestSize.height), effect, + 1.0f, transform, sourceRect); + + return true; +} + +bool CompositorD3D11::CopyBackdrop(const gfx::IntRect& aRect, + RefPtr<ID3D11Texture2D>* aOutTexture, + RefPtr<ID3D11ShaderResourceView>* aOutView) { + RefPtr<ID3D11Texture2D> texture = + CreateTexture(aRect, mCurrentRT, aRect.TopLeft()); + if (!texture) { + return false; + } + + CD3D11_SHADER_RESOURCE_VIEW_DESC desc(D3D11_SRV_DIMENSION_TEXTURE2D, + DXGI_FORMAT_B8G8R8A8_UNORM); + + RefPtr<ID3D11ShaderResourceView> srv; + HRESULT hr = + mDevice->CreateShaderResourceView(texture, &desc, getter_AddRefs(srv)); + if (FAILED(hr) || !srv) { + return false; + } + + *aOutTexture = texture.forget(); + *aOutView = srv.forget(); + return true; +} + +void CompositorD3D11::SetRenderTarget(CompositingRenderTarget* aRenderTarget) { + MOZ_ASSERT(aRenderTarget); + CompositingRenderTargetD3D11* newRT = + static_cast<CompositingRenderTargetD3D11*>(aRenderTarget); + if (mCurrentRT != newRT) { + mCurrentRT = newRT; + mCurrentRT->BindRenderTarget(mContext); + } + + if (newRT->HasComplexProjection()) { + gfx::Matrix4x4 projection; + bool depthEnable; + float zNear, zFar; + newRT->GetProjection(projection, depthEnable, zNear, zFar); + PrepareViewport(newRT->GetSize(), projection, zNear, zFar); + } else { + PrepareViewport(newRT->GetSize()); + } +} + +ID3D11PixelShader* CompositorD3D11::GetPSForEffect(Effect* aEffect, + const bool aUseBlendShader, + const MaskType aMaskType) { + if (aUseBlendShader) { + return mAttachments->mBlendShader[MaskType::MaskNone]; + } + + switch (aEffect->mType) { + case EffectTypes::SOLID_COLOR: + return mAttachments->mSolidColorShader[aMaskType]; + case EffectTypes::RENDER_TARGET: + return mAttachments->mRGBAShader[aMaskType]; + case EffectTypes::RGB: { + SurfaceFormat format = + static_cast<TexturedEffect*>(aEffect)->mTexture->GetFormat(); + return (format == SurfaceFormat::B8G8R8A8 || + format == SurfaceFormat::R8G8B8A8) + ? mAttachments->mRGBAShader[aMaskType] + : mAttachments->mRGBShader[aMaskType]; + } + case EffectTypes::NV12: + return mAttachments->mNV12Shader[aMaskType]; + case EffectTypes::YCBCR: + return mAttachments->mYCbCrShader[aMaskType]; + case EffectTypes::COMPONENT_ALPHA: + return mAttachments->mComponentAlphaShader[aMaskType]; + default: + NS_WARNING("No shader to load"); + return nullptr; + } +} + +void CompositorD3D11::ClearRect(const gfx::Rect& aRect) { + if (aRect.IsEmpty()) { + return; + } + + mContext->OMSetBlendState(mAttachments->mDisabledBlendState, sBlendFactor, + 0xFFFFFFFF); + + Matrix4x4 identity; + memcpy(&mVSConstants.layerTransform, &identity._11, 64); + + mVSConstants.layerQuad = aRect; + mVSConstants.renderTargetOffset[0] = 0; + mVSConstants.renderTargetOffset[1] = 0; + mPSConstants.layerOpacity[0] = 1.0f; + + D3D11_RECT scissor; + scissor.left = aRect.X(); + scissor.right = aRect.XMost(); + scissor.top = aRect.Y(); + scissor.bottom = aRect.YMost(); + mContext->RSSetScissorRects(1, &scissor); + mContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP); + mContext->VSSetShader(mAttachments->mVSQuadShader[MaskType::MaskNone], + nullptr, 0); + + mContext->PSSetShader(mAttachments->mSolidColorShader[MaskType::MaskNone], + nullptr, 0); + mPSConstants.layerColor[0] = 0; + mPSConstants.layerColor[1] = 0; + mPSConstants.layerColor[2] = 0; + mPSConstants.layerColor[3] = 0; + + if (!UpdateConstantBuffers()) { + NS_WARNING("Failed to update shader constant buffers"); + return; + } + + mContext->Draw(4, 0); + + // Restore the default blend state. + mContext->OMSetBlendState(mAttachments->mPremulBlendState, sBlendFactor, + 0xFFFFFFFF); +} + +static inline bool EffectHasPremultipliedAlpha(Effect* aEffect) { + if (aEffect->mType == EffectTypes::RGB) { + return static_cast<TexturedEffect*>(aEffect)->mPremultiplied; + } + return true; +} + +static inline int EffectToBlendLayerType(Effect* aEffect) { + switch (aEffect->mType) { + case EffectTypes::SOLID_COLOR: + return PS_LAYER_COLOR; + case EffectTypes::RGB: { + gfx::SurfaceFormat format = + static_cast<TexturedEffect*>(aEffect)->mTexture->GetFormat(); + return (format == gfx::SurfaceFormat::B8G8R8A8 || + format == gfx::SurfaceFormat::R8G8B8A8) + ? PS_LAYER_RGBA + : PS_LAYER_RGB; + } + case EffectTypes::RENDER_TARGET: + return PS_LAYER_RGBA; + case EffectTypes::YCBCR: + return PS_LAYER_YCBCR; + case EffectTypes::NV12: + return PS_LAYER_NV12; + default: + MOZ_ASSERT_UNREACHABLE("blending not supported for this layer type"); + return 0; + } +} + +void CompositorD3D11::DrawQuad(const gfx::Rect& aRect, + const gfx::IntRect& aClipRect, + const EffectChain& aEffectChain, + gfx::Float aOpacity, + const gfx::Matrix4x4& aTransform, + const gfx::Rect& aVisibleRect) { + DrawGeometry(aRect, aRect, aClipRect, aEffectChain, aOpacity, aTransform, + aVisibleRect); +} + +void CompositorD3D11::DrawTriangles( + const nsTArray<gfx::TexturedTriangle>& aTriangles, const gfx::Rect& aRect, + const gfx::IntRect& aClipRect, const EffectChain& aEffectChain, + gfx::Float aOpacity, const gfx::Matrix4x4& aTransform, + const gfx::Rect& aVisibleRect) { + DrawGeometry(aTriangles, aRect, aClipRect, aEffectChain, aOpacity, aTransform, + aVisibleRect); +} + +void CompositorD3D11::PrepareDynamicVertexBuffer() { + mContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST); + mContext->IASetInputLayout(mAttachments->mDynamicInputLayout); + SetVertexBuffer<TexturedVertex>(mAttachments->mDynamicVertexBuffer); +} + +void CompositorD3D11::PrepareStaticVertexBuffer() { + mContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP); + mContext->IASetInputLayout(mAttachments->mInputLayout); + SetVertexBuffer<Vertex>(mAttachments->mVertexBuffer); +} + +void CompositorD3D11::Draw(const nsTArray<gfx::TexturedTriangle>& aTriangles, + const gfx::Rect*) { + if (!UpdateConstantBuffers()) { + NS_WARNING("Failed to update shader constant buffers"); + return; + } + + PrepareDynamicVertexBuffer(); + + if (!UpdateDynamicVertexBuffer(aTriangles)) { + NS_WARNING("Failed to update shader dynamic buffers"); + return; + } + + mContext->Draw(3 * aTriangles.Length(), 0); + + PrepareStaticVertexBuffer(); +} + +void CompositorD3D11::Draw(const gfx::Rect& aRect, + const gfx::Rect* aTexCoords) { + Rect layerRects[4] = {aRect}; + Rect textureRects[4] = {}; + size_t rects = 1; + + if (aTexCoords) { + rects = DecomposeIntoNoRepeatRects(aRect, *aTexCoords, &layerRects, + &textureRects); + } + + for (size_t i = 0; i < rects; i++) { + mVSConstants.layerQuad = layerRects[i]; + mVSConstants.textureCoords = textureRects[i]; + + if (!UpdateConstantBuffers()) { + NS_WARNING("Failed to update shader constant buffers"); + break; + } + + mContext->Draw(4, 0); + } +} + +ID3D11VertexShader* CompositorD3D11::GetVSForGeometry( + const nsTArray<gfx::TexturedTriangle>& aTriangles, + const bool aUseBlendShaders, const MaskType aMaskType) { + return aUseBlendShaders ? mAttachments->mVSDynamicBlendShader[aMaskType] + : mAttachments->mVSDynamicShader[aMaskType]; +} + +ID3D11VertexShader* CompositorD3D11::GetVSForGeometry( + const gfx::Rect& aRect, const bool aUseBlendShaders, + const MaskType aMaskType) { + return aUseBlendShaders ? mAttachments->mVSQuadBlendShader[aMaskType] + : mAttachments->mVSQuadShader[aMaskType]; +} + +template <typename Geometry> +void CompositorD3D11::DrawGeometry(const Geometry& aGeometry, + const gfx::Rect& aRect, + const gfx::IntRect& aClipRect, + const EffectChain& aEffectChain, + gfx::Float aOpacity, + const gfx::Matrix4x4& aTransform, + const gfx::Rect& aVisibleRect) { + if (mCurrentClip.IsEmpty()) { + return; + } + + MOZ_ASSERT(mCurrentRT, "No render target"); + + memcpy(&mVSConstants.layerTransform, &aTransform._11, 64); + IntPoint origin = mCurrentRT->GetOrigin(); + mVSConstants.renderTargetOffset[0] = origin.x; + mVSConstants.renderTargetOffset[1] = origin.y; + + mPSConstants.layerOpacity[0] = aOpacity; + + bool restoreBlendMode = false; + + MaskType maskType = MaskType::MaskNone; + + if (aEffectChain.mSecondaryEffects[EffectTypes::MASK]) { + maskType = MaskType::Mask; + + EffectMask* maskEffect = static_cast<EffectMask*>( + aEffectChain.mSecondaryEffects[EffectTypes::MASK].get()); + TextureSourceD3D11* source = maskEffect->mMaskTexture->AsSourceD3D11(); + + if (!source) { + NS_WARNING("Missing texture source!"); + return; + } + + ID3D11ShaderResourceView* srView = source->GetShaderResourceView(); + mContext->PSSetShaderResources(TexSlot::Mask, 1, &srView); + + const gfx::Matrix4x4& maskTransform = maskEffect->mMaskTransform; + NS_ASSERTION(maskTransform.Is2D(), + "How did we end up with a 3D transform here?!"); + Rect bounds = Rect(Point(), Size(maskEffect->mSize)); + bounds = maskTransform.As2D().TransformBounds(bounds); + + Matrix4x4 transform; + transform._11 = 1.0f / bounds.Width(); + transform._22 = 1.0f / bounds.Height(); + transform._41 = float(-bounds.X()) / bounds.Width(); + transform._42 = float(-bounds.Y()) / bounds.Height(); + memcpy(mVSConstants.maskTransform, &transform._11, 64); + } + + D3D11_RECT scissor; + + IntRect clipRect(aClipRect.X(), aClipRect.Y(), aClipRect.Width(), + aClipRect.Height()); + if (mCurrentRT == mDefaultRT) { + clipRect = clipRect.Intersect(mCurrentClip); + } + + if (clipRect.IsEmpty()) { + return; + } + + scissor.left = clipRect.X(); + scissor.right = clipRect.XMost(); + scissor.top = clipRect.Y(); + scissor.bottom = clipRect.YMost(); + + bool useBlendShaders = false; + RefPtr<ID3D11Texture2D> mixBlendBackdrop; + gfx::CompositionOp blendMode = gfx::CompositionOp::OP_OVER; + if (aEffectChain.mSecondaryEffects[EffectTypes::BLEND_MODE]) { + EffectBlendMode* blendEffect = static_cast<EffectBlendMode*>( + aEffectChain.mSecondaryEffects[EffectTypes::BLEND_MODE].get()); + blendMode = blendEffect->mBlendMode; + + // If the blend operation needs to read from the backdrop, copy the + // current render target into a new texture and bind it now. + if (BlendOpIsMixBlendMode(blendMode)) { + gfx::Matrix4x4 backdropTransform; + gfx::IntRect rect = ComputeBackdropCopyRect(aRect, aClipRect, aTransform, + &backdropTransform); + + RefPtr<ID3D11ShaderResourceView> srv; + if (CopyBackdrop(rect, &mixBlendBackdrop, &srv) && + mAttachments->InitBlendShaders()) { + useBlendShaders = true; + + ID3D11ShaderResourceView* srView = srv.get(); + mContext->PSSetShaderResources(TexSlot::Backdrop, 1, &srView); + + memcpy(&mVSConstants.backdropTransform, &backdropTransform._11, 64); + + mPSConstants.blendConfig[0] = + EffectToBlendLayerType(aEffectChain.mPrimaryEffect); + mPSConstants.blendConfig[1] = int(maskType); + mPSConstants.blendConfig[2] = BlendOpToShaderConstant(blendMode); + mPSConstants.blendConfig[3] = + EffectHasPremultipliedAlpha(aEffectChain.mPrimaryEffect); + } + } + } + + mContext->RSSetScissorRects(1, &scissor); + + RefPtr<ID3D11VertexShader> vertexShader = + GetVSForGeometry(aGeometry, useBlendShaders, maskType); + + RefPtr<ID3D11PixelShader> pixelShader = + GetPSForEffect(aEffectChain.mPrimaryEffect, useBlendShaders, maskType); + + mContext->VSSetShader(vertexShader, nullptr, 0); + mContext->PSSetShader(pixelShader, nullptr, 0); + + const Rect* pTexCoordRect = nullptr; + + switch (aEffectChain.mPrimaryEffect->mType) { + case EffectTypes::SOLID_COLOR: { + DeviceColor color = + static_cast<EffectSolidColor*>(aEffectChain.mPrimaryEffect.get()) + ->mColor; + mPSConstants.layerColor[0] = color.r * color.a * aOpacity; + mPSConstants.layerColor[1] = color.g * color.a * aOpacity; + mPSConstants.layerColor[2] = color.b * color.a * aOpacity; + mPSConstants.layerColor[3] = color.a * aOpacity; + } break; + case EffectTypes::RGB: + case EffectTypes::RENDER_TARGET: { + TexturedEffect* texturedEffect = + static_cast<TexturedEffect*>(aEffectChain.mPrimaryEffect.get()); + + pTexCoordRect = &texturedEffect->mTextureCoords; + + TextureSourceD3D11* source = texturedEffect->mTexture->AsSourceD3D11(); + + if (!source) { + NS_WARNING("Missing texture source!"); + return; + } + + ID3D11ShaderResourceView* srView = source->GetShaderResourceView(); + mContext->PSSetShaderResources(TexSlot::RGB, 1, &srView); + + if (!texturedEffect->mPremultiplied) { + mContext->OMSetBlendState(mAttachments->mNonPremulBlendState, + sBlendFactor, 0xFFFFFFFF); + restoreBlendMode = true; + } + + SetSamplerForSamplingFilter(texturedEffect->mSamplingFilter); + } break; + case EffectTypes::NV12: { + EffectNV12* effectNV12 = + static_cast<EffectNV12*>(aEffectChain.mPrimaryEffect.get()); + + pTexCoordRect = &effectNV12->mTextureCoords; + + TextureSourceD3D11* source = effectNV12->mTexture->AsSourceD3D11(); + if (!source) { + NS_WARNING("Missing texture source!"); + return; + } + + RefPtr<ID3D11Texture2D> texture = source->GetD3D11Texture(); + if (!texture) { + NS_WARNING("No texture found in texture source!"); + } + + D3D11_TEXTURE2D_DESC sourceDesc; + texture->GetDesc(&sourceDesc); + MOZ_DIAGNOSTIC_ASSERT(sourceDesc.Format == DXGI_FORMAT_NV12 || + sourceDesc.Format == DXGI_FORMAT_P010 || + sourceDesc.Format == DXGI_FORMAT_P016); + + // Might want to cache these for efficiency. + RefPtr<ID3D11ShaderResourceView> srViewY; + RefPtr<ID3D11ShaderResourceView> srViewCbCr; + D3D11_SHADER_RESOURCE_VIEW_DESC srvDesc = + CD3D11_SHADER_RESOURCE_VIEW_DESC(D3D11_SRV_DIMENSION_TEXTURE2D, + sourceDesc.Format == DXGI_FORMAT_NV12 + ? DXGI_FORMAT_R8_UNORM + : DXGI_FORMAT_R16_UNORM); + mDevice->CreateShaderResourceView(texture, &srvDesc, + getter_AddRefs(srViewY)); + srvDesc.Format = sourceDesc.Format == DXGI_FORMAT_NV12 + ? DXGI_FORMAT_R8G8_UNORM + : DXGI_FORMAT_R16G16_UNORM; + mDevice->CreateShaderResourceView(texture, &srvDesc, + getter_AddRefs(srViewCbCr)); + + ID3D11ShaderResourceView* views[] = {srViewY, srViewCbCr}; + mContext->PSSetShaderResources(TexSlot::Y, 2, views); + + const float* yuvToRgb = + gfxUtils::YuvToRgbMatrix4x3RowMajor(effectNV12->mYUVColorSpace); + memcpy(&mPSConstants.yuvColorMatrix, yuvToRgb, + sizeof(mPSConstants.yuvColorMatrix)); + mPSConstants.vCoefficient[0] = + RescalingFactorForColorDepth(effectNV12->mColorDepth); + + SetSamplerForSamplingFilter(effectNV12->mSamplingFilter); + } break; + case EffectTypes::YCBCR: { + EffectYCbCr* ycbcrEffect = + static_cast<EffectYCbCr*>(aEffectChain.mPrimaryEffect.get()); + + SetSamplerForSamplingFilter(SamplingFilter::LINEAR); + + pTexCoordRect = &ycbcrEffect->mTextureCoords; + + const int Y = 0, Cb = 1, Cr = 2; + TextureSource* source = ycbcrEffect->mTexture; + + if (!source) { + NS_WARNING("No texture to composite"); + return; + } + + if (!source->GetSubSource(Y) || !source->GetSubSource(Cb) || + !source->GetSubSource(Cr)) { + // This can happen if we failed to upload the textures, most likely + // because of unsupported dimensions (we don't tile YCbCr textures). + return; + } + + const float* yuvToRgb = + gfxUtils::YuvToRgbMatrix4x3RowMajor(ycbcrEffect->mYUVColorSpace); + memcpy(&mPSConstants.yuvColorMatrix, yuvToRgb, + sizeof(mPSConstants.yuvColorMatrix)); + + // Adjust range according to the bit depth. + mPSConstants.vCoefficient[0] = + RescalingFactorForColorDepth(ycbcrEffect->mColorDepth); + + TextureSourceD3D11* sourceY = source->GetSubSource(Y)->AsSourceD3D11(); + TextureSourceD3D11* sourceCb = source->GetSubSource(Cb)->AsSourceD3D11(); + TextureSourceD3D11* sourceCr = source->GetSubSource(Cr)->AsSourceD3D11(); + + ID3D11ShaderResourceView* srViews[3] = { + sourceY->GetShaderResourceView(), sourceCb->GetShaderResourceView(), + sourceCr->GetShaderResourceView()}; + mContext->PSSetShaderResources(TexSlot::Y, 3, srViews); + } break; + case EffectTypes::COMPONENT_ALPHA: { + MOZ_ASSERT(LayerManager::LayersComponentAlphaEnabled()); + MOZ_ASSERT(mAttachments->mComponentBlendState); + EffectComponentAlpha* effectComponentAlpha = + static_cast<EffectComponentAlpha*>(aEffectChain.mPrimaryEffect.get()); + + TextureSourceD3D11* sourceOnWhite = + effectComponentAlpha->mOnWhite->AsSourceD3D11(); + TextureSourceD3D11* sourceOnBlack = + effectComponentAlpha->mOnBlack->AsSourceD3D11(); + + if (!sourceOnWhite || !sourceOnBlack) { + NS_WARNING("Missing texture source(s)!"); + return; + } + + SetSamplerForSamplingFilter(effectComponentAlpha->mSamplingFilter); + + pTexCoordRect = &effectComponentAlpha->mTextureCoords; + + ID3D11ShaderResourceView* srViews[2] = { + sourceOnBlack->GetShaderResourceView(), + sourceOnWhite->GetShaderResourceView()}; + mContext->PSSetShaderResources(TexSlot::RGB, 1, &srViews[0]); + mContext->PSSetShaderResources(TexSlot::RGBWhite, 1, &srViews[1]); + + mContext->OMSetBlendState(mAttachments->mComponentBlendState, + sBlendFactor, 0xFFFFFFFF); + restoreBlendMode = true; + } break; + default: + NS_WARNING("Unknown shader type"); + return; + } + + Draw(aGeometry, pTexCoordRect); + + if (restoreBlendMode) { + mContext->OMSetBlendState(mAttachments->mPremulBlendState, sBlendFactor, + 0xFFFFFFFF); + } +} + +Maybe<IntRect> CompositorD3D11::BeginFrameForWindow( + const nsIntRegion& aInvalidRegion, const Maybe<IntRect>& aClipRect, + const IntRect& aRenderBounds, const nsIntRegion& aOpaqueRegion) { + MOZ_RELEASE_ASSERT(!mTarget, "mTarget not cleared properly"); + return BeginFrame(aInvalidRegion, aClipRect, aRenderBounds, aOpaqueRegion); +} + +Maybe<IntRect> CompositorD3D11::BeginFrameForTarget( + const nsIntRegion& aInvalidRegion, const Maybe<IntRect>& aClipRect, + const IntRect& aRenderBounds, const nsIntRegion& aOpaqueRegion, + DrawTarget* aTarget, const IntRect& aTargetBounds) { + MOZ_RELEASE_ASSERT(!mTarget, "mTarget not cleared properly"); + mTarget = aTarget; // Will be cleared in EndFrame(). + mTargetBounds = aTargetBounds; + Maybe<IntRect> result = + BeginFrame(aInvalidRegion, aClipRect, aRenderBounds, aOpaqueRegion); + if (!result) { + // Composition has been aborted. Reset mTarget. + mTarget = nullptr; + } + return result; +} + +void CompositorD3D11::BeginFrameForNativeLayers() { + MOZ_CRASH("Native layers are not implemented on Windows."); +} + +Maybe<gfx::IntRect> CompositorD3D11::BeginRenderingToNativeLayer( + const nsIntRegion& aInvalidRegion, const Maybe<gfx::IntRect>& aClipRect, + const nsIntRegion& aOpaqueRegion, NativeLayer* aNativeLayer) { + MOZ_CRASH("Native layers are not implemented on Windows."); +} + +void CompositorD3D11::EndRenderingToNativeLayer() { + MOZ_CRASH("Native layers are not implemented on Windows."); +} + +Maybe<IntRect> CompositorD3D11::BeginFrame(const nsIntRegion& aInvalidRegion, + const Maybe<IntRect>& aClipRect, + const IntRect& aRenderBounds, + const nsIntRegion& aOpaqueRegion) { + // Don't composite if we are minimised. Other than for the sake of efficency, + // this is important because resizing our buffers when mimised will fail and + // cause a crash when we're restored. + NS_ASSERTION(mHwnd, "Couldn't find an HWND when initialising?"); + if (mWidget->IsHidden()) { + // We are not going to render, and not going to call EndFrame so we have to + // read-unlock our textures to prevent them from accumulating. + ReadUnlockTextures(); + return Nothing(); + } + + if (mDevice->GetDeviceRemovedReason() != S_OK) { + ReadUnlockTextures(); + + if (!mAttachments->IsDeviceReset()) { + gfxCriticalNote << "GFX: D3D11 skip BeginFrame with device-removed."; + + // If we are in the GPU process then the main process doesn't + // know that a device reset has happened and needs to be informed. + // + // When CompositorD3D11 is used for Software WebRender, it does not need + // to notify device reset. The device reset is notified by WebRender. + if (XRE_IsGPUProcess() && !mUseForSoftwareWebRender) { + GPUParent::GetSingleton()->NotifyDeviceReset(); + } + mAttachments->SetDeviceReset(); + } + return Nothing(); + } + + LayoutDeviceIntSize oldSize = mSize; + + EnsureSize(); + + IntRect rect = IntRect(IntPoint(0, 0), mSize.ToUnknownSize()); + // Sometimes the invalid region is larger than we want to draw. + nsIntRegion invalidRegionSafe; + + if (mSize != oldSize) { + invalidRegionSafe = rect; + } else { + invalidRegionSafe.And(aInvalidRegion, rect); + } + + IntRect invalidRect = invalidRegionSafe.GetBounds(); + + IntRect clipRect = invalidRect; + if (aClipRect) { + clipRect.IntersectRect(clipRect, *aClipRect); + } + + if (clipRect.IsEmpty()) { + CancelFrame(); + return Nothing(); + } + + PrepareStaticVertexBuffer(); + + mBackBufferInvalid.Or(mBackBufferInvalid, invalidRegionSafe); + if (mIsDoubleBuffered) { + mFrontBufferInvalid.Or(mFrontBufferInvalid, invalidRegionSafe); + } + + // We have to call UpdateRenderTarget after we've determined the invalid regi + // Failed to create a render target or the view. + if (!UpdateRenderTarget() || !mDefaultRT || !mDefaultRT->mRTView || + mSize.width <= 0 || mSize.height <= 0) { + ReadUnlockTextures(); + return Nothing(); + } + + mCurrentClip = mBackBufferInvalid.GetBounds(); + + mContext->RSSetState(mAttachments->mRasterizerState); + + SetRenderTarget(mDefaultRT); + + IntRegion regionToClear(mCurrentClip); + regionToClear.Sub(regionToClear, aOpaqueRegion); + + ClearRect(Rect(regionToClear.GetBounds())); + + mContext->OMSetBlendState(mAttachments->mPremulBlendState, sBlendFactor, + 0xFFFFFFFF); + + if (mAttachments->mSyncObject) { + if (!mAttachments->mSyncObject->Synchronize()) { + // It's timeout here. Since the timeout is related to the driver-removed, + // skip this frame. + return Nothing(); + } + } + + if (StaticPrefs::layers_acceleration_draw_fps()) { + uint32_t pixelsPerFrame = 0; + for (auto iter = mBackBufferInvalid.RectIter(); !iter.Done(); iter.Next()) { + pixelsPerFrame += iter.Get().Width() * iter.Get().Height(); + } + + mDiagnostics->Start(pixelsPerFrame); + } + + return Some(rect); +} + +void CompositorD3D11::NormalDrawingDone() { mDiagnostics->End(); } + +void CompositorD3D11::EndFrame() { +#ifdef MOZ_GECKO_PROFILER + if (!profiler_feature_active(ProfilerFeature::Screenshots) && mWindowRTCopy) { + mWindowRTCopy = nullptr; + } +#endif // MOZ_GECKO_PROFILER + + if (!mDefaultRT) { + Compositor::EndFrame(); + mTarget = nullptr; + return; + } + + if (XRE_IsParentProcess() && mDevice->GetDeviceRemovedReason() != S_OK) { + gfxCriticalNote << "GFX: D3D11 skip EndFrame with device-removed."; + Compositor::EndFrame(); + mTarget = nullptr; + mCurrentRT = nullptr; + return; + } + + LayoutDeviceIntSize oldSize = mSize; + EnsureSize(); + if (mSize.width <= 0 || mSize.height <= 0) { + Compositor::EndFrame(); + mTarget = nullptr; + return; + } + + RefPtr<ID3D11Query> query; + if (mRecycledQuery) { + query = mRecycledQuery.forget(); + } else { + CD3D11_QUERY_DESC desc(D3D11_QUERY_EVENT); + mDevice->CreateQuery(&desc, getter_AddRefs(query)); + } + if (query) { + mContext->End(query); + } + + if (oldSize == mSize) { + Present(); + if (StaticPrefs::gfx_compositor_clearstate()) { + mContext->ClearState(); + } + } else { + mDiagnostics->Cancel(); + } + + // Block until the previous frame's work has been completed. + if (mQuery) { + BOOL result; + WaitForFrameGPUQuery(mDevice, mContext, mQuery, &result); + // Store the query for recycling + mRecycledQuery = mQuery; + } + // Store the query for this frame so we can flush it next time. + mQuery = query; + + Compositor::EndFrame(); + mTarget = nullptr; + mCurrentRT = nullptr; +} + +void CompositorD3D11::GetFrameStats(GPUStats* aStats) { + mDiagnostics->Query(aStats); +} + +void CompositorD3D11::Present() { + UINT presentInterval = 0; + + bool isWARP = DeviceManagerDx::Get()->IsWARP(); + if (isWARP) { + // When we're using WARP we cannot present immediately as it causes us + // to tear when rendering. When not using WARP it appears the DWM takes + // care of tearing for us. + presentInterval = 1; + } + + // This must be called before present so our back buffer has the validated + // window content. + if (mTarget) { + PaintToTarget(); + } + + RefPtr<IDXGISwapChain1> chain; + HRESULT hr = + mSwapChain->QueryInterface((IDXGISwapChain1**)getter_AddRefs(chain)); + + RefPtr<IDXGIKeyedMutex> mutex; + if (mUseMutexOnPresent && mAttachments->mSyncObject) { + SyncObjectD3D11Host* d3dSyncObj = + (SyncObjectD3D11Host*)mAttachments->mSyncObject.get(); + mutex = d3dSyncObj->GetKeyedMutex(); + MOZ_ASSERT(mutex); + } + + if (SUCCEEDED(hr) && mAllowPartialPresents) { + DXGI_PRESENT_PARAMETERS params; + PodZero(¶ms); + params.DirtyRectsCount = mBackBufferInvalid.GetNumRects(); + StackArray<RECT, 4> rects(params.DirtyRectsCount); + + uint32_t i = 0; + for (auto iter = mBackBufferInvalid.RectIter(); !iter.Done(); iter.Next()) { + const IntRect& r = iter.Get(); + rects[i].left = r.X(); + rects[i].top = r.Y(); + rects[i].bottom = r.YMost(); + rects[i].right = r.XMost(); + i++; + } + + params.pDirtyRects = params.DirtyRectsCount ? rects.data() : nullptr; + + if (mutex) { + hr = mutex->AcquireSync(0, 2000); + NS_ENSURE_TRUE_VOID(SUCCEEDED(hr)); + } + + chain->Present1( + presentInterval, + mDisableSequenceForNextFrame ? DXGI_PRESENT_DO_NOT_SEQUENCE : 0, + ¶ms); + + if (mutex) { + mutex->ReleaseSync(0); + } + } else { + if (mutex) { + hr = mutex->AcquireSync(0, 2000); + NS_ENSURE_TRUE_VOID(SUCCEEDED(hr)); + } + + hr = mSwapChain->Present( + 0, mDisableSequenceForNextFrame ? DXGI_PRESENT_DO_NOT_SEQUENCE : 0); + + if (mutex) { + mutex->ReleaseSync(0); + } + + if (FAILED(hr)) { + gfxCriticalNote << "D3D11 swap chain preset failed " << hexa(hr); + HandleError(hr); + } + } + + if (mIsDoubleBuffered) { + mBackBufferInvalid = mFrontBufferInvalid; + mFrontBufferInvalid.SetEmpty(); + } else { + mBackBufferInvalid.SetEmpty(); + } + + mDisableSequenceForNextFrame = false; +} + +void CompositorD3D11::CancelFrame(bool aNeedFlush) { + ReadUnlockTextures(); + // Flush the context, otherwise the driver might hold some resources alive + // until the next flush or present. + if (aNeedFlush) { + mContext->Flush(); + } +} + +void CompositorD3D11::PrepareViewport(const gfx::IntSize& aSize) { + // This view matrix translates coordinates from 0..width and 0..height to + // -1..1 on the X axis, and -1..1 on the Y axis (flips the Y coordinate) + Matrix viewMatrix = Matrix::Translation(-1.0, 1.0); + viewMatrix.PreScale(2.0f / float(aSize.width), 2.0f / float(aSize.height)); + viewMatrix.PreScale(1.0f, -1.0f); + + Matrix4x4 projection = Matrix4x4::From2D(viewMatrix); + projection._33 = 0.0f; + + PrepareViewport(aSize, projection, 0.0f, 1.0f); +} + +void CompositorD3D11::ForcePresent() { + LayoutDeviceIntSize size = mWidget->GetClientSize(); + + DXGI_SWAP_CHAIN_DESC desc; + mSwapChain->GetDesc(&desc); + + if (desc.BufferDesc.Width == size.width && + desc.BufferDesc.Height == size.height && size == mBufferSize) { + mSwapChain->Present(0, 0); + if (mIsDoubleBuffered) { + // Make sure we present what was the front buffer before that we know is + // completely valid. This non v-synced present should be pretty much + // 'free' for a flip chain. + mSwapChain->Present(0, 0); + } + } +} + +void CompositorD3D11::PrepareViewport(const gfx::IntSize& aSize, + const gfx::Matrix4x4& aProjection, + float aZNear, float aZFar) { + D3D11_VIEWPORT viewport; + viewport.MaxDepth = aZFar; + viewport.MinDepth = aZNear; + viewport.Width = aSize.width; + viewport.Height = aSize.height; + viewport.TopLeftX = 0; + viewport.TopLeftY = 0; + + mContext->RSSetViewports(1, &viewport); + + memcpy(&mVSConstants.projection, &aProjection._11, + sizeof(mVSConstants.projection)); +} + +void CompositorD3D11::EnsureSize() { mSize = mWidget->GetClientSize(); } + +bool CompositorD3D11::VerifyBufferSize() { + mWidget->AsWindows()->UpdateCompositorWndSizeIfNecessary(); + + DXGI_SWAP_CHAIN_DESC swapDesc; + HRESULT hr; + + hr = mSwapChain->GetDesc(&swapDesc); + if (FAILED(hr)) { + gfxCriticalError() << "Failed to get the description " << hexa(hr) << ", " + << mSize << ", " << (int)mVerifyBuffersFailed; + HandleError(hr); + return false; + } + + if (((swapDesc.BufferDesc.Width == mSize.width && + swapDesc.BufferDesc.Height == mSize.height) || + mSize.width <= 0 || mSize.height <= 0) && + !mVerifyBuffersFailed) { + return true; + } + + ID3D11RenderTargetView* view = nullptr; + mContext->OMSetRenderTargets(1, &view, nullptr); + + if (mDefaultRT) { + RefPtr<ID3D11RenderTargetView> rtView = mDefaultRT->mRTView; + RefPtr<ID3D11ShaderResourceView> srView = mDefaultRT->mSRV; + + // Make sure the texture, which belongs to the swapchain, is destroyed + // before resizing the swapchain. + if (mCurrentRT == mDefaultRT) { + mCurrentRT = nullptr; + } + + MOZ_ASSERT(mDefaultRT->hasOneRef()); + mDefaultRT = nullptr; + + RefPtr<ID3D11Resource> resource; + rtView->GetResource(getter_AddRefs(resource)); + + ULONG newRefCnt = rtView.forget().take()->Release(); + + if (newRefCnt > 0) { + gfxCriticalError() << "mRTView not destroyed on final release! RefCnt: " + << newRefCnt; + } + + if (srView) { + newRefCnt = srView.forget().take()->Release(); + + if (newRefCnt > 0) { + gfxCriticalError() << "mSRV not destroyed on final release! RefCnt: " + << newRefCnt; + } + } + + newRefCnt = resource.forget().take()->Release(); + + if (newRefCnt > 0) { + gfxCriticalError() + << "Unexpecting lingering references to backbuffer! RefCnt: " + << newRefCnt; + } + } + + hr = mSwapChain->ResizeBuffers(0, mSize.width, mSize.height, + DXGI_FORMAT_B8G8R8A8_UNORM, 0); + + mVerifyBuffersFailed = FAILED(hr); + if (mVerifyBuffersFailed) { + gfxCriticalNote << "D3D11 swap resize buffers failed " << hexa(hr) << " on " + << mSize; + HandleError(hr); + mBufferSize = LayoutDeviceIntSize(); + } else { + mBufferSize = mSize; + } + + mBackBufferInvalid = mFrontBufferInvalid = + IntRect(0, 0, mSize.width, mSize.height); + + return !mVerifyBuffersFailed; +} + +bool CompositorD3D11::UpdateRenderTarget() { + HRESULT hr; + + RefPtr<ID3D11Texture2D> backBuf; + + if (!VerifyBufferSize()) { + gfxCriticalNote << "Failed VerifyBufferSize in UpdateRenderTarget " + << mSize; + return false; + } + + if (mSize.width <= 0 || mSize.height <= 0) { + gfxCriticalNote << "Invalid size in UpdateRenderTarget " << mSize << ", " + << (int)mVerifyBuffersFailed; + return false; + } + + hr = mSwapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), + (void**)backBuf.StartAssignment()); + if (hr == DXGI_ERROR_INVALID_CALL) { + // This happens on some GPUs/drivers when there's a TDR. + if (mDevice->GetDeviceRemovedReason() != S_OK) { + gfxCriticalError() << "GetBuffer returned invalid call! " << mSize << ", " + << (int)mVerifyBuffersFailed; + return false; + } + } + + if (FAILED(hr)) { + gfxCriticalNote << "Failed in UpdateRenderTarget " << hexa(hr) << ", " + << mSize << ", " << (int)mVerifyBuffersFailed; + HandleError(hr); + return false; + } + + IntRegion validFront; + validFront.Sub(mBackBufferInvalid, mFrontBufferInvalid); + + if (mIsDoubleBuffered && !validFront.IsEmpty()) { + RefPtr<ID3D11Texture2D> frontBuf; + hr = mSwapChain->GetBuffer(1, __uuidof(ID3D11Texture2D), + (void**)frontBuf.StartAssignment()); + + if (SUCCEEDED(hr)) { + for (auto iter = validFront.RectIter(); !iter.Done(); iter.Next()) { + const IntRect& rect = iter.Get(); + + D3D11_BOX box; + box.back = 1; + box.front = 0; + box.left = rect.X(); + box.right = rect.XMost(); + box.top = rect.Y(); + box.bottom = rect.YMost(); + mContext->CopySubresourceRegion(backBuf, 0, rect.X(), rect.Y(), 0, + frontBuf, 0, &box); + } + mBackBufferInvalid = mFrontBufferInvalid; + } + } + + mDefaultRT = new CompositingRenderTargetD3D11(backBuf, IntPoint(0, 0)); + mDefaultRT->SetSize(mSize.ToUnknownSize()); + + return true; +} + +bool CompositorD3D11::UpdateConstantBuffers() { + HRESULT hr; + D3D11_MAPPED_SUBRESOURCE resource; + resource.pData = nullptr; + + hr = mContext->Map(mAttachments->mVSConstantBuffer, 0, + D3D11_MAP_WRITE_DISCARD, 0, &resource); + if (FAILED(hr) || !resource.pData) { + gfxCriticalError() << "Failed to map VSConstantBuffer. Result: " << hexa(hr) + << ", " << (int)mVerifyBuffersFailed; + HandleError(hr); + return false; + } + *(VertexShaderConstants*)resource.pData = mVSConstants; + mContext->Unmap(mAttachments->mVSConstantBuffer, 0); + resource.pData = nullptr; + + hr = mContext->Map(mAttachments->mPSConstantBuffer, 0, + D3D11_MAP_WRITE_DISCARD, 0, &resource); + if (FAILED(hr) || !resource.pData) { + gfxCriticalError() << "Failed to map PSConstantBuffer. Result: " << hexa(hr) + << ", " << (int)mVerifyBuffersFailed; + HandleError(hr); + return false; + } + *(PixelShaderConstants*)resource.pData = mPSConstants; + mContext->Unmap(mAttachments->mPSConstantBuffer, 0); + + ID3D11Buffer* buffer = mAttachments->mVSConstantBuffer; + + mContext->VSSetConstantBuffers(0, 1, &buffer); + + buffer = mAttachments->mPSConstantBuffer; + mContext->PSSetConstantBuffers(0, 1, &buffer); + return true; +} + +void CompositorD3D11::SetSamplerForSamplingFilter( + SamplingFilter aSamplingFilter) { + ID3D11SamplerState* sampler; + switch (aSamplingFilter) { + case SamplingFilter::POINT: + sampler = mAttachments->mPointSamplerState; + break; + case SamplingFilter::LINEAR: + default: + sampler = mAttachments->mLinearSamplerState; + break; + } + + mContext->PSSetSamplers(0, 1, &sampler); +} + +void CompositorD3D11::PaintToTarget() { + RefPtr<ID3D11Texture2D> backBuf; + HRESULT hr; + + hr = mSwapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), + (void**)backBuf.StartAssignment()); + if (FAILED(hr)) { + gfxCriticalErrorOnce(gfxCriticalError::DefaultOptions(false)) + << "Failed in PaintToTarget 1"; + HandleError(hr); + return; + } + + D3D11_TEXTURE2D_DESC bbDesc; + backBuf->GetDesc(&bbDesc); + + CD3D11_TEXTURE2D_DESC softDesc(bbDesc.Format, bbDesc.Width, bbDesc.Height); + softDesc.MipLevels = 1; + softDesc.CPUAccessFlags = D3D11_CPU_ACCESS_READ; + softDesc.Usage = D3D11_USAGE_STAGING; + softDesc.BindFlags = 0; + + RefPtr<ID3D11Texture2D> readTexture; + + hr = + mDevice->CreateTexture2D(&softDesc, nullptr, getter_AddRefs(readTexture)); + if (FAILED(hr)) { + gfxCriticalErrorOnce(gfxCriticalError::DefaultOptions(false)) + << "Failed in PaintToTarget 2"; + HandleError(hr); + return; + } + mContext->CopyResource(readTexture, backBuf); + + D3D11_MAPPED_SUBRESOURCE map; + hr = mContext->Map(readTexture, 0, D3D11_MAP_READ, 0, &map); + if (FAILED(hr)) { + gfxCriticalErrorOnce(gfxCriticalError::DefaultOptions(false)) + << "Failed in PaintToTarget 3"; + HandleError(hr); + return; + } + RefPtr<DataSourceSurface> sourceSurface = + Factory::CreateWrappingDataSourceSurface( + (uint8_t*)map.pData, map.RowPitch, + IntSize(bbDesc.Width, bbDesc.Height), SurfaceFormat::B8G8R8A8); + mTarget->CopySurface(sourceSurface, + IntRect(0, 0, bbDesc.Width, bbDesc.Height), + IntPoint(-mTargetBounds.X(), -mTargetBounds.Y())); + + mTarget->Flush(); + mContext->Unmap(readTexture, 0); +} + +bool CompositorD3D11::Failed(HRESULT hr, const char* aContext) { + if (SUCCEEDED(hr)) return false; + + gfxCriticalNote << "[D3D11] " << aContext << " failed: " << hexa(hr) << ", " + << (int)mVerifyBuffersFailed; + return true; +} + +SyncObjectHost* CompositorD3D11::GetSyncObject() { + if (mAttachments) { + return mAttachments->mSyncObject; + } + return nullptr; +} + +void CompositorD3D11::HandleError(HRESULT hr, Severity aSeverity) { + if (SUCCEEDED(hr)) { + return; + } + + if (aSeverity == Critical) { + MOZ_CRASH("GFX: Unrecoverable D3D11 error"); + } + + if (mDevice && DeviceManagerDx::Get()->GetCompositorDevice() != mDevice) { + gfxCriticalNote << "Out of sync D3D11 devices in HandleError, " + << (int)mVerifyBuffersFailed; + } + + HRESULT hrOnReset = S_OK; + bool deviceRemoved = hr == DXGI_ERROR_DEVICE_REMOVED; + + if (deviceRemoved && mDevice) { + hrOnReset = mDevice->GetDeviceRemovedReason(); + } else if (hr == DXGI_ERROR_INVALID_CALL && mDevice) { + hrOnReset = mDevice->GetDeviceRemovedReason(); + if (hrOnReset != S_OK) { + deviceRemoved = true; + } + } + + // Device reset may not be an error on our side, but can mess things up so + // it's useful to see it in the reports. + gfxCriticalError(CriticalLog::DefaultOptions(!deviceRemoved)) + << (deviceRemoved ? "[CompositorD3D11] device removed with error code: " + : "[CompositorD3D11] error code: ") + << hexa(hr) << ", " << hexa(hrOnReset) << ", " + << (int)mVerifyBuffersFailed; + + // Crash if we are making invalid calls outside of device removal + if (hr == DXGI_ERROR_INVALID_CALL) { + gfxDevCrash(deviceRemoved ? LogReason::D3D11InvalidCallDeviceRemoved + : LogReason::D3D11InvalidCall) + << "Invalid D3D11 api call"; + } + + if (aSeverity == Recoverable) { + NS_WARNING("Encountered a recoverable D3D11 error"); + } +} + +} // namespace layers +} // namespace mozilla |