/* 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 "VideoProcessorD3D11.h" #include #include #include "mozilla/gfx/Logging.h" #include "mozilla/gfx/Types.h" #include "mozilla/layers/TextureD3D11.h" #include "mozilla/Maybe.h" namespace mozilla { namespace layers { // TODO: De-duplicate this, it also exists in DCLayerTree.cpp. static Maybe GetSourceDXGIColorSpace( const gfx::YUVColorSpace aYUVColorSpace, const gfx::ColorRange aColorRange, const gfx::TransferFunction aTransferFunction) { switch (aYUVColorSpace) { case gfx::YUVColorSpace::BT601: // https://en.wikipedia.org/wiki/Rec._601 - this is the NTSC and SECAM/PAL // color spaces if (aTransferFunction != gfx::TransferFunction::BT709) { gfxCriticalNoteOnce << "GetSourceDXGIColorSpace: Unhandled transfer function " << static_cast(aTransferFunction) << " for BT601, treating as BT709 transfer function"; } switch (aColorRange) { case gfx::ColorRange::FULL: return Some(DXGI_COLOR_SPACE_YCBCR_FULL_G22_LEFT_P601); case gfx::ColorRange::LIMITED: return Some(DXGI_COLOR_SPACE_YCBCR_STUDIO_G22_LEFT_P601); } gfxCriticalNoteOnce << "GetSourceDXGIColorSpace: Unhandled color range " << static_cast(aColorRange) << " for BT601"; return Nothing(); case gfx::YUVColorSpace::Identity: gfxCriticalNoteOnce << "GetSourceDXGIColorSpace: Unhandled YUV color space " << static_cast(aYUVColorSpace) << ", treating as BT709 color space"; FMT_FALLTHROUGH; case gfx::YUVColorSpace::BT709: // https://en.wikipedia.org/wiki/Rec._709 - this is the HDTV color space if (aTransferFunction != gfx::TransferFunction::BT709) { gfxCriticalNoteOnce << "GetSourceDXGIColorSpace: Unhandled transfer function " << static_cast(aTransferFunction) << " for BT709, treating as BT709 transfer function"; } switch (aColorRange) { case gfx::ColorRange::FULL: return Some(DXGI_COLOR_SPACE_YCBCR_FULL_G22_LEFT_P709); case gfx::ColorRange::LIMITED: return Some(DXGI_COLOR_SPACE_YCBCR_STUDIO_G22_LEFT_P709); } gfxCriticalNoteOnce << "GetSourceDXGIColorSpace: Unhandled color range " << static_cast(aColorRange) << " for BT709"; return Nothing(); case gfx::YUVColorSpace::BT2020: // https://en.wikipedia.org/wiki/Rec._2020 - this is the UHDTV color space if (!StaticPrefs::gfx_color_management_hdr_video()) { // This pref being off mimics legacy behavior, it's wrong but it's // precisely what we did before, looks washed out if it's PQ. switch (aColorRange) { case gfx::ColorRange::FULL: return Some(DXGI_COLOR_SPACE_YCBCR_FULL_G22_LEFT_P2020); case gfx::ColorRange::LIMITED: return Some(DXGI_COLOR_SPACE_YCBCR_STUDIO_G22_LEFT_P2020); } gfxCriticalNoteOnce << "GetSourceDXGIColorSpace: Unhandled color range " << static_cast(aColorRange) << " for BT2020"; return Nothing(); } switch (aTransferFunction) { case gfx::TransferFunction::SRGB: case gfx::TransferFunction::LINEAR: // Almost certainly never used, but cover all switch cases to support // the compiler warning if any are added later. gfxCriticalNoteOnce << "GetSourceDXGIColorSpace: DXGI has no support for " << static_cast(aTransferFunction) << " transfer function for YCBCR content, treating as BT2020 " "transfer function"; FMT_FALLTHROUGH; case gfx::TransferFunction::BT709: // BT2020 defines a transfer function that is almost identical to // BT709 + BT1886, so this refers to BT2020 transfer function. // https://en.wikipedia.org/wiki/Rec._2020 switch (aColorRange) { case gfx::ColorRange::FULL: return Some(DXGI_COLOR_SPACE_YCBCR_FULL_G22_LEFT_P2020); case gfx::ColorRange::LIMITED: return Some(DXGI_COLOR_SPACE_YCBCR_STUDIO_G22_LEFT_P2020); } gfxCriticalNoteOnce << "GetSourceDXGIColorSpace: Unhandled color range " << static_cast(aColorRange) << " for BT2020"; return Nothing(); case gfx::TransferFunction::PQ: // This is an HDR video transfer function, needs 10bit (HDR10) to // avoid being lower quality than BT709 over the SDR range. // https://en.wikipedia.org/wiki/Perceptual_quantizer switch (aColorRange) { case gfx::ColorRange::FULL: gfxCriticalNoteOnce << "GetSourceDXGIColorSpace: DXGI has no support for PQ " "transfer function with full color range for BT2020 " "content, treating as studio range"; return Some(DXGI_COLOR_SPACE_YCBCR_STUDIO_G2084_LEFT_P2020); case gfx::ColorRange::LIMITED: return Some(DXGI_COLOR_SPACE_YCBCR_STUDIO_G2084_LEFT_P2020); } gfxCriticalNoteOnce << "GetSourceDXGIColorSpace: Unhandled color range " << static_cast(aColorRange) << " for BT2020"; return Nothing(); case gfx::TransferFunction::HLG: // This is an HDR video transfer function, does not strictly require // 10bit but certainly benefits from it. // https://en.wikipedia.org/wiki/Hybrid_log%E2%80%93gamma switch (aColorRange) { case gfx::ColorRange::FULL: return Some(DXGI_COLOR_SPACE_YCBCR_FULL_GHLG_TOPLEFT_P2020); case gfx::ColorRange::LIMITED: return Some(DXGI_COLOR_SPACE_YCBCR_STUDIO_GHLG_TOPLEFT_P2020); } gfxCriticalNoteOnce << "GetSourceDXGIColorSpace: Unhandled color range " << static_cast(aColorRange) << " for BT2020"; return Nothing(); } gfxCriticalNoteOnce << "GetSourceDXGIColorSpace: Unhandled transfer function " << static_cast(aTransferFunction) << " for BT2020"; return Nothing(); } return Nothing(); } static Maybe GetSourceDXGIColorSpace( const gfx::YUVRangedColorSpace aYUVColorSpace) { const auto info = FromYUVRangedColorSpace(aYUVColorSpace); return GetSourceDXGIColorSpace(info.space, info.range, info.transferFunction); } /* static */ RefPtr VideoProcessorD3D11::Create(ID3D11Device* aDevice) { MOZ_ASSERT(aDevice); if (!aDevice) { return nullptr; } RefPtr context; aDevice->GetImmediateContext(getter_AddRefs(context)); if (!context) { return nullptr; } HRESULT hr; RefPtr videoDevice; hr = aDevice->QueryInterface((ID3D11VideoDevice**)getter_AddRefs(videoDevice)); if (FAILED(hr)) { gfxCriticalNoteOnce << "Failed to get D3D11VideoDevice: " << gfx::hexa(hr); return nullptr; } RefPtr videoContext; hr = context->QueryInterface( (ID3D11VideoContext**)getter_AddRefs(videoContext)); if (FAILED(hr)) { gfxCriticalNoteOnce << "Failed to get D3D11VideoContext: " << gfx::hexa(hr); return nullptr; } RefPtr videoContext1; hr = videoContext->QueryInterface( (ID3D11VideoContext1**)getter_AddRefs(videoContext1)); if (FAILED(hr)) { gfxCriticalNoteOnce << "Failed to get D3D11VideoContext1: " << gfx::hexa(hr); return nullptr; } RefPtr videoProcessor = new VideoProcessorD3D11( aDevice, context, videoDevice, videoContext, videoContext1); return videoProcessor; } VideoProcessorD3D11::VideoProcessorD3D11(ID3D11Device* aDevice, ID3D11DeviceContext* aDeviceContext, ID3D11VideoDevice* aVideoDevice, ID3D11VideoContext* aVideoContext, ID3D11VideoContext1* aVideoContext1) : mDevice(aDevice), mDeviceContext(aDeviceContext), mVideoDevice(aVideoDevice), mVideoContext(aVideoContext), mVideoContext1(aVideoContext1) {} VideoProcessorD3D11::~VideoProcessorD3D11() {} HRESULT VideoProcessorD3D11::Init(const gfx::IntSize& aSize) { if (mSize == aSize) { return S_OK; } mVideoProcessorEnumerator = nullptr; mVideoProcessor = nullptr; mSize = gfx::IntSize(); D3D11_VIDEO_PROCESSOR_CONTENT_DESC desc; desc.InputFrameFormat = D3D11_VIDEO_FRAME_FORMAT_PROGRESSIVE; desc.InputFrameRate.Numerator = 60; desc.InputFrameRate.Denominator = 1; desc.InputWidth = aSize.width; desc.InputHeight = aSize.height; desc.OutputFrameRate.Numerator = 60; desc.OutputFrameRate.Denominator = 1; desc.OutputWidth = aSize.width; desc.OutputHeight = aSize.height; desc.Usage = D3D11_VIDEO_USAGE_PLAYBACK_NORMAL; HRESULT hr = mVideoDevice->CreateVideoProcessorEnumerator( &desc, getter_AddRefs(mVideoProcessorEnumerator)); if (FAILED(hr)) { gfxCriticalNoteOnce << "Failed to create VideoProcessorEnumerator: " << gfx::hexa(hr); return hr; } hr = mVideoDevice->CreateVideoProcessor(mVideoProcessorEnumerator, 0, getter_AddRefs(mVideoProcessor)); if (FAILED(hr)) { gfxCriticalNoteOnce << "Failed to create VideoProcessor: " << gfx::hexa(hr); return hr; } // Turn off auto stream processing (the default) that will hurt power // consumption. mVideoContext->VideoProcessorSetStreamAutoProcessingMode(mVideoProcessor, 0, FALSE); mSize = aSize; return S_OK; } bool VideoProcessorD3D11::CallVideoProcessorBlt( InputTextureInfo& aTextureInfo, ID3D11Texture2D* aOutputTexture) { MOZ_ASSERT(mVideoProcessorEnumerator); MOZ_ASSERT(mVideoProcessor); MOZ_ASSERT(aTextureInfo.mTexture); MOZ_ASSERT(aOutputTexture); HRESULT hr; auto yuvRangedColorSpace = gfx::ToYUVRangedColorSpace( gfx::ToYUVColorSpace(aTextureInfo.mColorSpace), aTextureInfo.mColorRange, aTextureInfo.mTransferFunction); auto sourceColorSpace = GetSourceDXGIColorSpace(yuvRangedColorSpace); if (sourceColorSpace.isNothing()) { gfxCriticalNoteOnce << "Unsupported color space"; return false; } DXGI_COLOR_SPACE_TYPE inputColorSpace = sourceColorSpace.ref(); mVideoContext1->VideoProcessorSetStreamColorSpace1(mVideoProcessor, 0, inputColorSpace); const DXGI_COLOR_SPACE_TYPE outputColorSpace = DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709; mVideoContext1->VideoProcessorSetOutputColorSpace1(mVideoProcessor, outputColorSpace); D3D11_VIDEO_PROCESSOR_INPUT_VIEW_DESC inputDesc = {}; inputDesc.ViewDimension = D3D11_VPIV_DIMENSION_TEXTURE2D; inputDesc.Texture2D.ArraySlice = aTextureInfo.mIndex; RefPtr inputView; hr = mVideoDevice->CreateVideoProcessorInputView( aTextureInfo.mTexture, mVideoProcessorEnumerator, &inputDesc, getter_AddRefs(inputView)); if (FAILED(hr)) { gfxCriticalNoteOnce << "ID3D11VideoProcessorInputView creation failed: " << gfx::hexa(hr); return false; } D3D11_VIDEO_PROCESSOR_OUTPUT_VIEW_DESC outputDesc = {}; outputDesc.ViewDimension = D3D11_VPOV_DIMENSION_TEXTURE2D; outputDesc.Texture2D.MipSlice = 0; RefPtr outputView; hr = mVideoDevice->CreateVideoProcessorOutputView( aOutputTexture, mVideoProcessorEnumerator, &outputDesc, getter_AddRefs(outputView)); if (FAILED(hr)) { gfxCriticalNoteOnce << "ID3D11VideoProcessorOutputView creation failed: " << gfx::hexa(hr); return false; } D3D11_VIDEO_PROCESSOR_STREAM stream = {}; stream.Enable = true; stream.pInputSurface = inputView.get(); hr = mVideoContext->VideoProcessorBlt(mVideoProcessor, outputView, 0, 1, &stream); if (FAILED(hr)) { gfxCriticalNoteOnce << "VideoProcessorBlt failed: " << gfx::hexa(hr); return false; } return true; } } // namespace layers } // namespace mozilla