From 54ede5160d4872917fa2e056787f525ff9884b56 Mon Sep 17 00:00:00 2001 From: sotaro Date: Wed, 9 Sep 2020 01:04:53 +0000 Subject: [PATCH] Bug 1460499 - Use DirectComposition for hardware decoded video on Windows r=nical Use ID3D11VideoProcessor for video frame rendering. WebRenderError::VIDEO_OVERLAY does not cause disabling WebRender. It just change gfxVars::UseWebRenderDCompVideoOverlayWin() to false. Differential Revision: https://phabricator.services.mozilla.com/D88763 --- gfx/config/gfxVars.h | 1 + gfx/ipc/GPUProcessManager.cpp | 9 + gfx/layers/d3d11/TextureD3D11.cpp | 13 +- gfx/thebes/DeviceManagerDx.cpp | 28 ++ gfx/thebes/DeviceManagerDx.h | 2 + gfx/thebes/gfxPlatform.cpp | 7 + gfx/webrender_bindings/DCLayerTree.cpp | 388 +++++++++++++++++- gfx/webrender_bindings/DCLayerTree.h | 60 ++- .../RenderCompositorANGLE.cpp | 12 +- .../RenderCompositorANGLE.h | 3 + .../RenderD3D11TextureHostOGL.cpp | 9 +- .../RenderD3D11TextureHostOGL.h | 14 +- .../RenderTextureHostWrapper.cpp | 9 + .../RenderTextureHostWrapper.h | 2 + gfx/webrender_bindings/RenderThread.cpp | 11 +- gfx/webrender_bindings/RenderThread.h | 3 + gfx/webrender_bindings/WebRenderTypes.h | 1 + 17 files changed, 555 insertions(+), 17 deletions(-) diff --git a/gfx/config/gfxVars.h b/gfx/config/gfxVars.h index abd7082e7766..526aabe765ac 100644 --- a/gfx/config/gfxVars.h +++ b/gfx/config/gfxVars.h @@ -42,6 +42,7 @@ class gfxVarReceiver; _(UseWebRenderANGLE, bool, false) \ _(UseWebRenderFlipSequentialWin, bool, false) \ _(UseWebRenderDCompWin, bool, false) \ + _(UseWebRenderDCompVideoOverlayWin, bool, false) \ _(UseWebRenderTripleBufferingWin, bool, false) \ _(UseWebRenderCompositor, bool, false) \ _(UseWebRenderProgramBinaryDisk, bool, false) \ diff --git a/gfx/ipc/GPUProcessManager.cpp b/gfx/ipc/GPUProcessManager.cpp index 75ae993fc881..28cdd193bd5d 100644 --- a/gfx/ipc/GPUProcessManager.cpp +++ b/gfx/ipc/GPUProcessManager.cpp @@ -475,6 +475,7 @@ void GPUProcessManager::DisableWebRender(wr::WebRenderError aError, MOZ_ASSERT_UNREACHABLE("Invalid value"); } gfx::gfxVars::SetUseWebRender(false); + gfx::gfxVars::SetUseWebRenderDCompVideoOverlayWin(false); #if defined(MOZ_WIDGET_ANDROID) // If aError is not wr::WebRenderError::INITIALIZE, nsWindow does not @@ -493,6 +494,14 @@ void GPUProcessManager::DisableWebRender(wr::WebRenderError aError, } void GPUProcessManager::NotifyWebRenderError(wr::WebRenderError aError) { + if (aError == wr::WebRenderError::VIDEO_OVERLAY) { +#ifdef XP_WIN + gfxVars::SetUseWebRenderDCompVideoOverlayWin(false); +#else + MOZ_ASSERT_UNREACHABLE("unexpected to be called"); +#endif + return; + } DisableWebRender(aError, nsCString()); } diff --git a/gfx/layers/d3d11/TextureD3D11.cpp b/gfx/layers/d3d11/TextureD3D11.cpp index 7145ed1747cf..2184043de476 100644 --- a/gfx/layers/d3d11/TextureD3D11.cpp +++ b/gfx/layers/d3d11/TextureD3D11.cpp @@ -981,8 +981,8 @@ bool DXGITextureHostD3D11::AcquireTextureSource( void DXGITextureHostD3D11::CreateRenderTexture( const wr::ExternalImageId& aExternalImageId) { - RefPtr texture = - new wr::RenderDXGITextureHostOGL(mHandle, mFormat, mSize); + RefPtr texture = new wr::RenderDXGITextureHostOGL( + mHandle, mFormat, mYUVColorSpace, mColorRange, mSize); wr::RenderThread::Get()->RegisterExternalImage(wr::AsUint64(aExternalImageId), texture.forget()); @@ -1097,12 +1097,19 @@ void DXGITextureHostD3D11::PushDisplayItems( case gfx::SurfaceFormat::P016: case gfx::SurfaceFormat::NV12: { MOZ_ASSERT(aImageKeys.length() == 2); + bool supportsExternalCompositing = false; + // XXX Add P010 and P016 support. + if (GetFormat() == gfx::SurfaceFormat::NV12 && + gfx::gfxVars::UseWebRenderDCompVideoOverlayWin()) { + supportsExternalCompositing = true; + } aBuilder.PushNV12Image( aBounds, aClip, true, aImageKeys[0], aImageKeys[1], GetFormat() == gfx::SurfaceFormat::NV12 ? wr::ColorDepth::Color8 : wr::ColorDepth::Color16, wr::ToWrYuvColorSpace(mYUVColorSpace), - wr::ToWrColorRange(mColorRange), aFilter, aPreferCompositorSurface); + wr::ToWrColorRange(mColorRange), aFilter, aPreferCompositorSurface, + supportsExternalCompositing); break; } default: { diff --git a/gfx/thebes/DeviceManagerDx.cpp b/gfx/thebes/DeviceManagerDx.cpp index b1b4b1be272d..9621974f9f9f 100644 --- a/gfx/thebes/DeviceManagerDx.cpp +++ b/gfx/thebes/DeviceManagerDx.cpp @@ -51,6 +51,10 @@ decltype(D3D11CreateDevice)* sD3D11CreateDeviceFn = nullptr; // It should only be used within CreateDirectCompositionDevice. decltype(DCompositionCreateDevice2)* sDcompCreateDevice2Fn = nullptr; +// It should only be used within CreateDCompSurfaceHandle +decltype(DCompositionCreateSurfaceHandle)* sDcompCreateSurfaceHandleFn = + nullptr; + // We don't have access to the DirectDrawCreateEx type in gfxWindowsPlatform.h, // since it doesn't include ddraw.h, so we use a static here. It should only // be used within InitializeDirectDrawConfig. @@ -125,6 +129,14 @@ bool DeviceManagerDx::LoadDcomp() { return false; } + // Load optional API for external compositing + sDcompCreateSurfaceHandleFn = + (decltype(DCompositionCreateSurfaceHandle)*)::GetProcAddress( + module, "DCompositionCreateSurfaceHandle"); + if (!sDcompCreateDevice2Fn) { + return false; + } + mDcompModule.steal(module); return true; } @@ -413,6 +425,22 @@ void DeviceManagerDx::CreateDirectCompositionDevice() { mDirectCompositionDevice = compositionDevice; } +/* static */ +HANDLE DeviceManagerDx::CreateDCompSurfaceHandle() { + if (!sDcompCreateSurfaceHandleFn) { + return 0; + } + + HANDLE handle = 0; + HRESULT hr = sDcompCreateSurfaceHandleFn(COMPOSITIONOBJECT_ALL_ACCESS, + nullptr, &handle); + if (FAILED(hr)) { + return 0; + } + + return handle; +} + void DeviceManagerDx::ImportDeviceInfo(const D3D11DeviceStatus& aDeviceStatus) { MOZ_ASSERT(!ProcessOwnsCompositor()); diff --git a/gfx/thebes/DeviceManagerDx.h b/gfx/thebes/DeviceManagerDx.h index 23267bdb074c..44cb13cc860f 100644 --- a/gfx/thebes/DeviceManagerDx.h +++ b/gfx/thebes/DeviceManagerDx.h @@ -99,6 +99,8 @@ class DeviceManagerDx final { void CreateDirectCompositionDevice(); bool CreateCanvasDevice(); + static HANDLE CreateDCompSurfaceHandle(); + void GetCompositorDevices( RefPtr* aOutDevice, RefPtr* aOutAttachments); diff --git a/gfx/thebes/gfxPlatform.cpp b/gfx/thebes/gfxPlatform.cpp index 32bb26aab3f1..c4a2ec896fe6 100644 --- a/gfx/thebes/gfxPlatform.cpp +++ b/gfx/thebes/gfxPlatform.cpp @@ -2767,6 +2767,13 @@ void gfxPlatform::InitWebRenderConfig() { if (gfxConfig::IsEnabled(Feature::WEBRENDER_DCOMP_PRESENT)) { gfxVars::SetUseWebRenderDCompWin(true); } + if (Preferences::GetBool("gfx.webrender.dcomp-video-overlay-win", false)) { + if (IsWin10AnniversaryUpdateOrLater() && + gfxConfig::IsEnabled(Feature::WEBRENDER_COMPOSITOR)) { + MOZ_ASSERT(gfxConfig::IsEnabled(Feature::WEBRENDER_DCOMP_PRESENT)); + gfxVars::SetUseWebRenderDCompVideoOverlayWin(true); + } + } if (Preferences::GetBool("gfx.webrender.flip-sequential", false)) { // XXX relax win version to windows 8. if (IsWin10OrLater() && UseWebRender() && gfxVars::UseWebRenderANGLE()) { diff --git a/gfx/webrender_bindings/DCLayerTree.cpp b/gfx/webrender_bindings/DCLayerTree.cpp index b7645a53a68f..673e7be40784 100644 --- a/gfx/webrender_bindings/DCLayerTree.cpp +++ b/gfx/webrender_bindings/DCLayerTree.cpp @@ -9,7 +9,10 @@ #include "GLContext.h" #include "GLContextEGL.h" #include "mozilla/gfx/DeviceManagerDx.h" +#include "mozilla/gfx/gfxVars.h" #include "mozilla/StaticPrefs_gfx.h" +#include "mozilla/webrender/RenderD3D11TextureHostOGL.h" +#include "mozilla/webrender/RenderTextureHost.h" #include "mozilla/webrender/RenderThread.h" #include "mozilla/Telemetry.h" @@ -19,6 +22,7 @@ #define NTDDI_VERSION NTDDI_WINBLUE #include +#include #include #include @@ -28,7 +32,9 @@ namespace wr { /* static */ UniquePtr DCLayerTree::Create(gl::GLContext* aGL, EGLConfig aEGLConfig, - ID3D11Device* aDevice, HWND aHwnd) { + ID3D11Device* aDevice, + ID3D11DeviceContext* aCtx, + HWND aHwnd) { RefPtr dCompDevice = gfx::DeviceManagerDx::Get()->GetDirectCompositionDevice(); if (!dCompDevice) { @@ -36,7 +42,7 @@ UniquePtr DCLayerTree::Create(gl::GLContext* aGL, } auto layerTree = - MakeUnique(aGL, aEGLConfig, aDevice, dCompDevice); + MakeUnique(aGL, aEGLConfig, aDevice, aCtx, dCompDevice); if (!layerTree->Initialize(aHwnd)) { return nullptr; } @@ -45,12 +51,14 @@ UniquePtr DCLayerTree::Create(gl::GLContext* aGL, } DCLayerTree::DCLayerTree(gl::GLContext* aGL, EGLConfig aEGLConfig, - ID3D11Device* aDevice, + ID3D11Device* aDevice, ID3D11DeviceContext* aCtx, IDCompositionDevice2* aCompositionDevice) : mGL(aGL), mEGLConfig(aEGLConfig), mDevice(aDevice), + mCtx(aCtx), mCompositionDevice(aCompositionDevice), + mVideoOverlaySupported(false), mDebugCounter(false), mDebugVisualRedrawRegions(false), mEGLImage(EGL_NO_IMAGE), @@ -103,6 +111,12 @@ bool DCLayerTree::Initialize(HWND aHwnd) { return false; } + if (gfx::gfxVars::UseWebRenderDCompVideoOverlayWin()) { + if (!InitializeVideoOverlaySupport()) { + RenderThread::Get()->HandleWebRenderError(WebRenderError::VIDEO_OVERLAY); + } + } + mCompositionTarget->SetRoot(mRootVisual); // Set interporation mode to nearest, to ensure 1:1 sampling. // By default, a visual inherits the interpolation mode of the parent visual. @@ -113,6 +127,32 @@ bool DCLayerTree::Initialize(HWND aHwnd) { return true; } +bool DCLayerTree::InitializeVideoOverlaySupport() { + HRESULT hr; + + hr = mDevice->QueryInterface( + (ID3D11VideoDevice**)getter_AddRefs(mVideoDevice)); + if (FAILED(hr)) { + gfxCriticalNote << "Failed to get D3D11VideoDevice: " << gfx::hexa(hr); + return false; + } + + hr = + mCtx->QueryInterface((ID3D11VideoContext**)getter_AddRefs(mVideoContext)); + if (FAILED(hr)) { + gfxCriticalNote << "Failed to get D3D11VideoContext: " << gfx::hexa(hr); + return false; + } + + // XXX When video is rendered to DXGI_FORMAT_B8G8R8A8_UNORM SwapChain with + // VideoProcessor, it seems that we do not need to check + // IDXGIOutput3::CheckOverlaySupport(). + // If we want to yuv at DecodeSwapChain, its support seems necessary. + + mVideoOverlaySupported = true; + return true; +} + DCSurface* DCLayerTree::GetSurface(wr::NativeSurfaceId aId) const { auto surface_it = mDCSurfaces.find(aId); MOZ_RELEASE_ASSERT(surface_it != mDCSurfaces.end()); @@ -316,6 +356,21 @@ void DCLayerTree::CreateSurface(wr::NativeSurfaceId aId, mDCSurfaces[aId] = std::move(surface); } +void DCLayerTree::CreateExternalSurface(wr::NativeSurfaceId aId, + bool aIsOpaque) { + auto it = mDCSurfaces.find(aId); + MOZ_RELEASE_ASSERT(it == mDCSurfaces.end()); + + auto surface = MakeUnique(aIsOpaque, this); + if (!surface->Initialize()) { + gfxCriticalNote << "Failed to initialize DCSurfaceVideo: " + << wr::AsUint64(aId); + return; + } + + mDCSurfaces[aId] = std::move(surface); +} + void DCLayerTree::DestroySurface(NativeSurfaceId aId) { auto surface_it = mDCSurfaces.find(aId); MOZ_RELEASE_ASSERT(surface_it != mDCSurfaces.end()); @@ -335,6 +390,16 @@ void DCLayerTree::DestroyTile(wr::NativeSurfaceId aId, int aX, int aY) { surface->DestroyTile(aX, aY); } +void DCLayerTree::AttachExternalImage(wr::NativeSurfaceId aId, + wr::ExternalImageId aExternalImage) { + auto surface_it = mDCSurfaces.find(aId); + MOZ_RELEASE_ASSERT(surface_it != mDCSurfaces.end()); + auto* surfaceVideo = surface_it->second->AsDCSurfaceVideo(); + MOZ_RELEASE_ASSERT(surfaceVideo); + + surfaceVideo->AttachExternalImage(aExternalImage); +} + template static inline D2D1_RECT_F D2DRect(const T& aRect) { return D2D1::RectF(aRect.X(), aRect.Y(), aRect.XMost(), aRect.YMost()); @@ -438,6 +503,58 @@ GLuint DCLayerTree::GetOrCreateFbo(int aWidth, int aHeight) { return fboId; } +bool DCLayerTree::EnsureVideoProcessor(const gfx::IntSize& aVideoSize) { + HRESULT hr; + + if (!mVideoDevice || !mVideoContext) { + return false; + } + + if (mVideoProcessor && aVideoSize == mVideoSize) { + return true; + } + + mVideoProcessor = nullptr; + mVideoProcessorEnumerator = nullptr; + + D3D11_VIDEO_PROCESSOR_CONTENT_DESC desc = {}; + desc.InputFrameFormat = D3D11_VIDEO_FRAME_FORMAT_PROGRESSIVE; + desc.InputFrameRate.Numerator = 60; + desc.InputFrameRate.Denominator = 1; + desc.InputWidth = aVideoSize.width; + desc.InputHeight = aVideoSize.height; + desc.OutputFrameRate.Numerator = 60; + desc.OutputFrameRate.Denominator = 1; + desc.OutputWidth = aVideoSize.width; + desc.OutputHeight = aVideoSize.height; + desc.Usage = D3D11_VIDEO_USAGE_PLAYBACK_NORMAL; + + hr = mVideoDevice->CreateVideoProcessorEnumerator( + &desc, getter_AddRefs(mVideoProcessorEnumerator)); + if (FAILED(hr)) { + gfxCriticalNote << "Failed to create VideoProcessorEnumerator: " + << gfx::hexa(hr); + return false; + } + + hr = mVideoDevice->CreateVideoProcessor(mVideoProcessorEnumerator, 0, + getter_AddRefs(mVideoProcessor)); + if (FAILED(hr)) { + mVideoProcessor = nullptr; + mVideoProcessorEnumerator = nullptr; + gfxCriticalNote << "Failed to create VideoProcessor: " << gfx::hexa(hr); + return false; + } + + // Reduce power cosumption + // By default, the driver might perform certain processing tasks automatically + mVideoContext->VideoProcessorSetStreamAutoProcessingMode(mVideoProcessor, 0, + FALSE); + + mVideoSize = aVideoSize; + return true; +} + DCSurface::DCSurface(wr::DeviceIntSize aTileSize, wr::DeviceIntPoint aVirtualOffset, bool aIsOpaque, DCLayerTree* aDCLayerTree) @@ -529,6 +646,271 @@ DCTile* DCSurface::GetTile(int aX, int aY) const { return tile_it->second.get(); } +DCSurfaceVideo::DCSurfaceVideo(bool aIsOpaque, DCLayerTree* aDCLayerTree) + : DCSurface(wr::DeviceIntSize{}, wr::DeviceIntPoint{}, aIsOpaque, + aDCLayerTree) {} + +void DCSurfaceVideo::AttachExternalImage(wr::ExternalImageId aExternalImage) { + RenderTextureHost* texture = + RenderThread::Get()->GetRenderTexture(aExternalImage); + MOZ_RELEASE_ASSERT(texture); + + // XXX if software decoded video frame format is nv12, it could be used as + // video overlay. + if (!texture || !texture->AsRenderDXGITextureHostOGL() || + texture->AsRenderDXGITextureHostOGL()->GetFormat() != + gfx::SurfaceFormat::NV12) { + gfxCriticalNote << "Unsupported RenderTexture for overlay: " + << gfx::hexa(texture); + return; + } + + gfx::IntSize size = texture->AsRenderDXGITextureHostOGL()->GetSize(0); + if (!mVideoSwapChain || mSwapChainSize != size) { + ReleaseDecodeSwapChainResources(); + CreateVideoSwapChain(texture); + } + + if (!mVideoSwapChain) { + gfxCriticalNote << "Failed to create VideoSwapChain"; + RenderThread::Get()->NotifyWebRenderError( + wr::WebRenderError::VIDEO_OVERLAY); + return; + } + + mVisual->SetContent(mVideoSwapChain); + + if (!CallVideoProcessorBlt(texture)) { + RenderThread::Get()->NotifyWebRenderError( + wr::WebRenderError::VIDEO_OVERLAY); + return; + } + + mVideoSwapChain->Present(0, 0); +} + +bool DCSurfaceVideo::CreateVideoSwapChain(RenderTextureHost* aTexture) { + const auto device = mDCLayerTree->GetDevice(); + + RefPtr dxgiDevice; + device->QueryInterface((IDXGIDevice**)getter_AddRefs(dxgiDevice)); + + RefPtr dxgiFactoryMedia; + { + RefPtr adapter; + dxgiDevice->GetAdapter(getter_AddRefs(adapter)); + adapter->GetParent( + IID_PPV_ARGS((IDXGIFactoryMedia**)getter_AddRefs(dxgiFactoryMedia))); + } + + mSwapChainSurfaceHandle = gfx::DeviceManagerDx::CreateDCompSurfaceHandle(); + if (!mSwapChainSurfaceHandle) { + gfxCriticalNote << "Failed to create DCompSurfaceHandle"; + return false; + } + + gfx::IntSize size = aTexture->AsRenderDXGITextureHostOGL()->GetSize(0); + DXGI_ALPHA_MODE alpha_mode = + mIsOpaque ? DXGI_ALPHA_MODE_IGNORE : DXGI_ALPHA_MODE_PREMULTIPLIED; + + DXGI_SWAP_CHAIN_DESC1 desc = {}; + desc.Width = size.width; + desc.Height = size.height; + desc.Format = DXGI_FORMAT_B8G8R8A8_UNORM; + desc.Stereo = FALSE; + desc.SampleDesc.Count = 1; + desc.BufferCount = 2; + desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; + desc.Scaling = DXGI_SCALING_STRETCH; + desc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL; + desc.Flags = 0; + desc.AlphaMode = alpha_mode; + + HRESULT hr; + hr = dxgiFactoryMedia->CreateSwapChainForCompositionSurfaceHandle( + device, mSwapChainSurfaceHandle, &desc, nullptr, + getter_AddRefs(mVideoSwapChain)); + + if (FAILED(hr)) { + gfxCriticalNote << "Failed to create video SwapChain: " << gfx::hexa(hr); + return false; + } + + mSwapChainSize = size; + return true; +} + +static Maybe GetSourceDXGIColorSpace( + const gfx::YUVColorSpace aYUVColorSpace, + const gfx::ColorRange aColorRange) { + if (aYUVColorSpace == gfx::YUVColorSpace::BT601) { + if (aColorRange == gfx::ColorRange::FULL) { + return Some(DXGI_COLOR_SPACE_YCBCR_FULL_G22_LEFT_P601); + } else { + return Some(DXGI_COLOR_SPACE_YCBCR_STUDIO_G22_LEFT_P601); + } + } else if (aYUVColorSpace == gfx::YUVColorSpace::BT709) { + if (aColorRange == gfx::ColorRange::FULL) { + return Some(DXGI_COLOR_SPACE_YCBCR_FULL_G22_LEFT_P709); + } else { + return Some(DXGI_COLOR_SPACE_YCBCR_STUDIO_G22_LEFT_P709); + } + } else if (aYUVColorSpace == gfx::YUVColorSpace::BT2020) { + if (aColorRange == gfx::ColorRange::FULL) { + // XXX Add SMPTEST2084 handling. HDR content is not handled yet by + // video overlay. + return Some(DXGI_COLOR_SPACE_YCBCR_FULL_G22_LEFT_P2020); + } else { + return Some(DXGI_COLOR_SPACE_YCBCR_STUDIO_G22_LEFT_P2020); + } + } + + return Nothing(); +} + +bool DCSurfaceVideo::CallVideoProcessorBlt(RenderTextureHost* aTexture) { + HRESULT hr; + const auto videoDevice = mDCLayerTree->GetVideoDevice(); + const auto videoContext = mDCLayerTree->GetVideoContext(); + const auto texture = aTexture->AsRenderDXGITextureHostOGL(); + + Maybe sourceColorSpace = GetSourceDXGIColorSpace( + texture->GetYUVColorSpace(), texture->GetColorRange()); + if (sourceColorSpace.isNothing()) { + gfxCriticalNote << "Unsupported color space"; + return false; + } + + RefPtr texture2D = texture->GetD3D11Texture2D(); + if (!texture2D) { + gfxCriticalNote << "Failed to get D3D11Texture2D"; + return false; + } + + if (!mVideoSwapChain) { + return false; + } + + if (!mDCLayerTree->EnsureVideoProcessor(mSwapChainSize)) { + gfxCriticalNote << "EnsureVideoProcessor Failed"; + return false; + } + + RefPtr swapChain3; + mVideoSwapChain->QueryInterface( + (IDXGISwapChain3**)getter_AddRefs(swapChain3)); + if (!swapChain3) { + gfxCriticalNote << "Failed to get IDXGISwapChain3"; + return false; + } + + RefPtr videoContext1; + videoContext->QueryInterface( + (ID3D11VideoContext1**)getter_AddRefs(videoContext1)); + if (!videoContext1) { + gfxCriticalNote << "Failed to get ID3D11VideoContext1"; + return false; + } + + const auto videoProcessor = mDCLayerTree->GetVideoProcessor(); + const auto videoProcessorEnumerator = + mDCLayerTree->GetVideoProcessorEnumerator(); + + DXGI_COLOR_SPACE_TYPE inputColorSpace = sourceColorSpace.ref(); + videoContext1->VideoProcessorSetStreamColorSpace1(videoProcessor, 0, + inputColorSpace); + // XXX when content is hdr or yuv swapchain, it need to use other color space. + DXGI_COLOR_SPACE_TYPE outputColorSpace = + DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709; + hr = swapChain3->SetColorSpace1(outputColorSpace); + if (FAILED(hr)) { + gfxCriticalNote << "SetColorSpace1 failed: " << gfx::hexa(hr); + return false; + } + videoContext1->VideoProcessorSetOutputColorSpace1(videoProcessor, + outputColorSpace); + + D3D11_VIDEO_PROCESSOR_INPUT_VIEW_DESC inputDesc = {}; + inputDesc.ViewDimension = D3D11_VPIV_DIMENSION_TEXTURE2D; + inputDesc.Texture2D.ArraySlice = 0; + + RefPtr inputView; + hr = videoDevice->CreateVideoProcessorInputView( + texture2D, videoProcessorEnumerator, &inputDesc, + getter_AddRefs(inputView)); + if (FAILED(hr)) { + gfxCriticalNote << "ID3D11VideoProcessorInputView creation failed: " + << gfx::hexa(hr); + return false; + } + + D3D11_VIDEO_PROCESSOR_STREAM stream = {}; + stream.Enable = true; + stream.OutputIndex = 0; + stream.InputFrameOrField = 0; + stream.PastFrames = 0; + stream.FutureFrames = 0; + stream.pInputSurface = inputView.get(); + + RECT destRect; + destRect.left = 0; + destRect.top = 0; + destRect.right = mSwapChainSize.width; + destRect.bottom = mSwapChainSize.height; + + videoContext->VideoProcessorSetOutputTargetRect(videoProcessor, TRUE, + &destRect); + videoContext->VideoProcessorSetStreamDestRect(videoProcessor, 0, TRUE, + &destRect); + RECT sourceRect; + sourceRect.left = 0; + sourceRect.top = 0; + sourceRect.right = mSwapChainSize.width; + sourceRect.bottom = mSwapChainSize.height; + videoContext->VideoProcessorSetStreamSourceRect(videoProcessor, 0, TRUE, + &sourceRect); + + if (!mOutputView) { + RefPtr backBuf; + mVideoSwapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), + (void**)getter_AddRefs(backBuf)); + + D3D11_VIDEO_PROCESSOR_OUTPUT_VIEW_DESC outputDesc = {}; + outputDesc.ViewDimension = D3D11_VPOV_DIMENSION_TEXTURE2D; + outputDesc.Texture2D.MipSlice = 0; + + hr = videoDevice->CreateVideoProcessorOutputView( + backBuf, videoProcessorEnumerator, &outputDesc, + getter_AddRefs(mOutputView)); + if (FAILED(hr)) { + gfxCriticalNote << "ID3D11VideoProcessorOutputView creation failed: " + << gfx::hexa(hr); + return false; + } + } + + hr = videoContext->VideoProcessorBlt(videoProcessor, mOutputView, 0, 1, + &stream); + if (FAILED(hr)) { + gfxCriticalNote << "VideoProcessorBlt failed: " << gfx::hexa(hr); + return false; + } + + return true; +} + +void DCSurfaceVideo::ReleaseDecodeSwapChainResources() { + mOutputView = nullptr; + mVideoSwapChain = nullptr; + mDecodeSwapChain = nullptr; + mDecodeResource = nullptr; + if (mSwapChainSurfaceHandle) { + ::CloseHandle(mSwapChainSurfaceHandle); + mSwapChainSurfaceHandle = 0; + } + mSwapChainSize = gfx::IntSize(); +} + DCTile::DCTile(DCLayerTree* aDCLayerTree) : mDCLayerTree(aDCLayerTree) {} DCTile::~DCTile() {} diff --git a/gfx/webrender_bindings/DCLayerTree.h b/gfx/webrender_bindings/DCLayerTree.h index fdf3f889eca6..b5437e863243 100644 --- a/gfx/webrender_bindings/DCLayerTree.h +++ b/gfx/webrender_bindings/DCLayerTree.h @@ -18,10 +18,17 @@ struct ID3D11Device; struct ID3D11DeviceContext; +struct ID3D11VideoDevice; +struct ID3D11VideoContext; +struct ID3D11VideoProcessor; +struct ID3D11VideoProcessorEnumerator; +struct ID3D11VideoProcessorOutputView; struct IDCompositionDevice2; struct IDCompositionSurface; struct IDCompositionTarget; struct IDCompositionVisual2; +struct IDXGIDecodeSwapChain; +struct IDXGIResource; struct IDXGISwapChain1; struct IDCompositionVirtualSurface; @@ -39,6 +46,8 @@ namespace wr { class DCTile; class DCSurface; +class DCSurfaceVideo; +class RenderTextureHost; /** * DCLayerTree manages direct composition layers. @@ -47,9 +56,10 @@ class DCSurface; class DCLayerTree { public: static UniquePtr Create(gl::GLContext* aGL, EGLConfig aEGLConfig, - ID3D11Device* aDevice, HWND aHwnd); + ID3D11Device* aDevice, + ID3D11DeviceContext* aCtx, HWND aHwnd); explicit DCLayerTree(gl::GLContext* aGL, EGLConfig aEGLConfig, - ID3D11Device* aDevice, + ID3D11Device* aDevice, ID3D11DeviceContext* aCtx, IDCompositionDevice2* aCompositionDevice); ~DCLayerTree(); @@ -67,9 +77,12 @@ class DCLayerTree { void Unbind(); void CreateSurface(wr::NativeSurfaceId aId, wr::DeviceIntPoint aVirtualOffset, wr::DeviceIntSize aTileSize, bool aIsOpaque); + void CreateExternalSurface(wr::NativeSurfaceId aId, bool aIsOpaque); void DestroySurface(NativeSurfaceId aId); void CreateTile(wr::NativeSurfaceId aId, int32_t aX, int32_t aY); void DestroyTile(wr::NativeSurfaceId aId, int32_t aX, int32_t aY); + void AttachExternalImage(wr::NativeSurfaceId aId, + wr::ExternalImageId aExternalImage); void AddSurface(wr::NativeSurfaceId aId, const wr::CompositorSurfaceTransform& aTransform, wr::DeviceIntRect aClipRect, @@ -81,6 +94,14 @@ class DCLayerTree { IDCompositionDevice2* GetCompositionDevice() const { return mCompositionDevice; } + ID3D11VideoDevice* GetVideoDevice() const { return mVideoDevice; } + ID3D11VideoContext* GetVideoContext() const { return mVideoContext; } + ID3D11VideoProcessor* GetVideoProcessor() const { return mVideoProcessor; } + ID3D11VideoProcessorEnumerator* GetVideoProcessorEnumerator() const { + return mVideoProcessorEnumerator; + } + bool EnsureVideoProcessor(const gfx::IntSize& aVideoSize); + DCSurface* GetSurface(wr::NativeSurfaceId aId) const; // Get or create an FBO with depth buffer suitable for specified dimensions @@ -88,6 +109,7 @@ class DCLayerTree { protected: bool Initialize(HWND aHwnd); + bool InitializeVideoOverlaySupport(); bool MaybeUpdateDebugCounter(); bool MaybeUpdateDebugVisualRedrawRegions(); void DestroyEGLSurface(); @@ -101,12 +123,21 @@ class DCLayerTree { EGLConfig mEGLConfig; RefPtr mDevice; + RefPtr mCtx; RefPtr mCompositionDevice; RefPtr mCompositionTarget; RefPtr mRootVisual; RefPtr mDefaultSwapChainVisual; + RefPtr mVideoDevice; + RefPtr mVideoContext; + RefPtr mVideoProcessor; + RefPtr mVideoProcessorEnumerator; + gfx::IntSize mVideoSize; + + bool mVideoOverlaySupported; + bool mDebugCounter; bool mDebugVisualRedrawRegions; @@ -165,7 +196,7 @@ class DCSurface { explicit DCSurface(wr::DeviceIntSize aTileSize, wr::DeviceIntPoint aVirtualOffset, bool aIsOpaque, DCLayerTree* aDCLayerTree); - ~DCSurface(); + virtual ~DCSurface(); bool Initialize(); void CreateTile(int32_t aX, int32_t aY); @@ -191,6 +222,8 @@ class DCSurface { void UpdateAllocatedRect(); void DirtyAllocatedRect(); + virtual DCSurfaceVideo* AsDCSurfaceVideo() { return nullptr; } + protected: DCLayerTree* mDCLayerTree; @@ -214,6 +247,27 @@ class DCSurface { RefPtr mVirtualSurface; }; +class DCSurfaceVideo : public DCSurface { + public: + DCSurfaceVideo(bool aIsOpaque, DCLayerTree* aDCLayerTree); + + void AttachExternalImage(wr::ExternalImageId aExternalImage); + + DCSurfaceVideo* AsDCSurfaceVideo() override { return this; } + + protected: + bool CreateVideoSwapChain(RenderTextureHost* aTexture); + bool CallVideoProcessorBlt(RenderTextureHost* aTexture); + void ReleaseDecodeSwapChainResources(); + + RefPtr mOutputView; + RefPtr mDecodeResource; + RefPtr mVideoSwapChain; + RefPtr mDecodeSwapChain; + HANDLE mSwapChainSurfaceHandle; + gfx::IntSize mSwapChainSize; +}; + class DCTile { public: explicit DCTile(DCLayerTree* aDCLayerTree); diff --git a/gfx/webrender_bindings/RenderCompositorANGLE.cpp b/gfx/webrender_bindings/RenderCompositorANGLE.cpp index fd7ae8a1a051..83d5d1077ce4 100644 --- a/gfx/webrender_bindings/RenderCompositorANGLE.cpp +++ b/gfx/webrender_bindings/RenderCompositorANGLE.cpp @@ -167,7 +167,7 @@ bool RenderCompositorANGLE::Initialize() { HWND compositorHwnd = GetCompositorHwnd(); if (compositorHwnd) { mDCLayerTree = - DCLayerTree::Create(gl, mEGLConfig, mDevice, compositorHwnd); + DCLayerTree::Create(gl, mEGLConfig, mDevice, mCtx, compositorHwnd); if (!mDCLayerTree) { gfxCriticalNote << "Failed to create DCLayerTree"; return false; @@ -851,6 +851,11 @@ void RenderCompositorANGLE::CreateSurface(wr::NativeSurfaceId aId, mDCLayerTree->CreateSurface(aId, aVirtualOffset, aTileSize, aIsOpaque); } +void RenderCompositorANGLE::CreateExternalSurface(wr::NativeSurfaceId aId, + bool aIsOpaque) { + mDCLayerTree->CreateExternalSurface(aId, aIsOpaque); +} + void RenderCompositorANGLE::DestroySurface(NativeSurfaceId aId) { mDCLayerTree->DestroySurface(aId); } @@ -865,6 +870,11 @@ void RenderCompositorANGLE::DestroyTile(wr::NativeSurfaceId aId, int aX, mDCLayerTree->DestroyTile(aId, aX, aY); } +void RenderCompositorANGLE::AttachExternalImage( + wr::NativeSurfaceId aId, wr::ExternalImageId aExternalImage) { + mDCLayerTree->AttachExternalImage(aId, aExternalImage); +} + void RenderCompositorANGLE::AddSurface( wr::NativeSurfaceId aId, const wr::CompositorSurfaceTransform& aTransform, wr::DeviceIntRect aClipRect, wr::ImageRendering aImageRendering) { diff --git a/gfx/webrender_bindings/RenderCompositorANGLE.h b/gfx/webrender_bindings/RenderCompositorANGLE.h index 7b2ff3ba5398..461ae574443c 100644 --- a/gfx/webrender_bindings/RenderCompositorANGLE.h +++ b/gfx/webrender_bindings/RenderCompositorANGLE.h @@ -78,9 +78,12 @@ class RenderCompositorANGLE : public RenderCompositor { void Unbind() override; void CreateSurface(wr::NativeSurfaceId aId, wr::DeviceIntPoint aVirtualOffset, wr::DeviceIntSize aTileSize, bool aIsOpaque) override; + void CreateExternalSurface(wr::NativeSurfaceId aId, bool aIsOpaque) override; void DestroySurface(NativeSurfaceId aId) override; void CreateTile(wr::NativeSurfaceId aId, int32_t aX, int32_t aY) override; void DestroyTile(wr::NativeSurfaceId aId, int32_t aX, int32_t aY) override; + void AttachExternalImage(wr::NativeSurfaceId aId, + wr::ExternalImageId aExternalImage) override; void AddSurface(wr::NativeSurfaceId aId, const wr::CompositorSurfaceTransform& aTransform, wr::DeviceIntRect aClipRect, diff --git a/gfx/webrender_bindings/RenderD3D11TextureHostOGL.cpp b/gfx/webrender_bindings/RenderD3D11TextureHostOGL.cpp index 2ed16107d0db..05f4281cf96b 100644 --- a/gfx/webrender_bindings/RenderD3D11TextureHostOGL.cpp +++ b/gfx/webrender_bindings/RenderD3D11TextureHostOGL.cpp @@ -17,14 +17,17 @@ namespace mozilla { namespace wr { -RenderDXGITextureHostOGL::RenderDXGITextureHostOGL(WindowsHandle aHandle, - gfx::SurfaceFormat aFormat, - gfx::IntSize aSize) +RenderDXGITextureHostOGL::RenderDXGITextureHostOGL( + WindowsHandle aHandle, gfx::SurfaceFormat aFormat, + gfx::YUVColorSpace aYUVColorSpace, gfx::ColorRange aColorRange, + gfx::IntSize aSize) : mHandle(aHandle), mSurface(0), mStream(0), mTextureHandle{0}, mFormat(aFormat), + mYUVColorSpace(aYUVColorSpace), + mColorRange(aColorRange), mSize(aSize), mLocked(false) { MOZ_COUNT_CTOR_INHERITED(RenderDXGITextureHostOGL, RenderTextureHostOGL); diff --git a/gfx/webrender_bindings/RenderD3D11TextureHostOGL.h b/gfx/webrender_bindings/RenderD3D11TextureHostOGL.h index f2d2ee2fea8c..2812fcca985c 100644 --- a/gfx/webrender_bindings/RenderD3D11TextureHostOGL.h +++ b/gfx/webrender_bindings/RenderD3D11TextureHostOGL.h @@ -21,6 +21,8 @@ class RenderDXGITextureHostOGL final : public RenderTextureHostOGL { public: explicit RenderDXGITextureHostOGL(WindowsHandle aHandle, gfx::SurfaceFormat aFormat, + gfx::YUVColorSpace aYUVColorSpace, + gfx::ColorRange aColorRange, gfx::IntSize aSize); wr::WrExternalImage Lock(uint8_t aChannelIndex, gl::GLContext* aGL, @@ -37,6 +39,12 @@ class RenderDXGITextureHostOGL final : public RenderTextureHostOGL { return this; } + gfx::SurfaceFormat GetFormat() const { return mFormat; } + + gfx::YUVColorSpace GetYUVColorSpace() const { return mYUVColorSpace; } + + gfx::ColorRange GetColorRange() const { return mColorRange; } + ID3D11Texture2D* GetD3D11Texture2D(); private: @@ -60,8 +68,10 @@ class RenderDXGITextureHostOGL final : public RenderTextureHostOGL { // handles for Y and CbCr data. GLuint mTextureHandle[2]; - gfx::SurfaceFormat mFormat; - gfx::IntSize mSize; + const gfx::SurfaceFormat mFormat; + const gfx::YUVColorSpace mYUVColorSpace; + const gfx::ColorRange mColorRange; + const gfx::IntSize mSize; bool mLocked; }; diff --git a/gfx/webrender_bindings/RenderTextureHostWrapper.cpp b/gfx/webrender_bindings/RenderTextureHostWrapper.cpp index a23bad6f930a..206324f040e2 100644 --- a/gfx/webrender_bindings/RenderTextureHostWrapper.cpp +++ b/gfx/webrender_bindings/RenderTextureHostWrapper.cpp @@ -64,5 +64,14 @@ RenderTextureHostWrapper::AsRenderMacIOSurfaceTextureHostOGL() { return mTextureHost->AsRenderMacIOSurfaceTextureHostOGL(); } +RenderDXGITextureHostOGL* +RenderTextureHostWrapper::AsRenderDXGITextureHostOGL() { + EnsureTextureHost(); + if (!mTextureHost) { + return nullptr; + } + return mTextureHost->AsRenderDXGITextureHostOGL(); +} + } // namespace wr } // namespace mozilla diff --git a/gfx/webrender_bindings/RenderTextureHostWrapper.h b/gfx/webrender_bindings/RenderTextureHostWrapper.h index c69574ddbc49..cf990767451a 100644 --- a/gfx/webrender_bindings/RenderTextureHostWrapper.h +++ b/gfx/webrender_bindings/RenderTextureHostWrapper.h @@ -36,6 +36,8 @@ class RenderTextureHostWrapper final : public RenderTextureHost { RenderMacIOSurfaceTextureHostOGL* AsRenderMacIOSurfaceTextureHostOGL() override; + virtual RenderDXGITextureHostOGL* AsRenderDXGITextureHostOGL() override; + private: ~RenderTextureHostWrapper() override; diff --git a/gfx/webrender_bindings/RenderThread.cpp b/gfx/webrender_bindings/RenderThread.cpp index d8d873a885b7..714902a61b04 100644 --- a/gfx/webrender_bindings/RenderThread.cpp +++ b/gfx/webrender_bindings/RenderThread.cpp @@ -835,13 +835,20 @@ static void DoNotifyWebRenderError(WebRenderError aError) { layers::CompositorManagerParent::NotifyWebRenderError(aError); } +void RenderThread::NotifyWebRenderError(WebRenderError aError) { + MOZ_ASSERT(IsInRenderThread()); + + layers::CompositorThread()->Dispatch(NewRunnableFunction( + "DoNotifyWebRenderErrorRunnable", &DoNotifyWebRenderError, aError)); +} + void RenderThread::HandleWebRenderError(WebRenderError aError) { if (mHandlingWebRenderError) { return; } - layers::CompositorThread()->Dispatch(NewRunnableFunction( - "DoNotifyWebRenderErrorRunnable", &DoNotifyWebRenderError, aError)); + NotifyWebRenderError(aError); + { MutexAutoLock lock(mRenderTextureMapLock); mRenderTexturesDeferred.clear(); diff --git a/gfx/webrender_bindings/RenderThread.h b/gfx/webrender_bindings/RenderThread.h index 0f8aae2c58b3..de6c0bf7a62d 100644 --- a/gfx/webrender_bindings/RenderThread.h +++ b/gfx/webrender_bindings/RenderThread.h @@ -264,6 +264,9 @@ class RenderThread final { /// Can be called from any thread. void SimulateDeviceReset(); + /// Can only be called from the render thread. + void NotifyWebRenderError(WebRenderError aError); + /// Can only be called from the render thread. void HandleWebRenderError(WebRenderError aError); /// Can only be called from the render thread. diff --git a/gfx/webrender_bindings/WebRenderTypes.h b/gfx/webrender_bindings/WebRenderTypes.h index 82e1c1aaf031..da4ff626b796 100644 --- a/gfx/webrender_bindings/WebRenderTypes.h +++ b/gfx/webrender_bindings/WebRenderTypes.h @@ -767,6 +767,7 @@ enum class WebRenderError : int8_t { MAKE_CURRENT, RENDER, NEW_SURFACE, + VIDEO_OVERLAY, Sentinel /* this must be last for serialization purposes. */ };