Source code

Revision control

Copy as Markdown

Other Tools

/* 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 <d3d11.h>
#include <d3d11_1.h>
#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<DXGI_COLOR_SPACE_TYPE> 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<int>(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<int>(aColorRange) << " for BT601";
return Nothing();
case gfx::YUVColorSpace::Identity:
gfxCriticalNoteOnce
<< "GetSourceDXGIColorSpace: Unhandled YUV color space "
<< static_cast<int>(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<int>(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<int>(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<int>(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<int>(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.
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<int>(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.
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<int>(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.
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<int>(aColorRange) << " for BT2020";
return Nothing();
}
gfxCriticalNoteOnce
<< "GetSourceDXGIColorSpace: Unhandled transfer function "
<< static_cast<int>(aTransferFunction) << " for BT2020";
return Nothing();
}
return Nothing();
}
static Maybe<DXGI_COLOR_SPACE_TYPE> GetSourceDXGIColorSpace(
const gfx::YUVRangedColorSpace aYUVColorSpace) {
const auto info = FromYUVRangedColorSpace(aYUVColorSpace);
return GetSourceDXGIColorSpace(info.space, info.range, info.transferFunction);
}
/* static */
RefPtr<VideoProcessorD3D11> VideoProcessorD3D11::Create(ID3D11Device* aDevice) {
MOZ_ASSERT(aDevice);
if (!aDevice) {
return nullptr;
}
RefPtr<ID3D11DeviceContext> context;
aDevice->GetImmediateContext(getter_AddRefs(context));
if (!context) {
return nullptr;
}
HRESULT hr;
RefPtr<ID3D11VideoDevice> videoDevice;
hr =
aDevice->QueryInterface((ID3D11VideoDevice**)getter_AddRefs(videoDevice));
if (FAILED(hr)) {
gfxCriticalNoteOnce << "Failed to get D3D11VideoDevice: " << gfx::hexa(hr);
return nullptr;
}
RefPtr<ID3D11VideoContext> videoContext;
hr = context->QueryInterface(
(ID3D11VideoContext**)getter_AddRefs(videoContext));
if (FAILED(hr)) {
gfxCriticalNoteOnce << "Failed to get D3D11VideoContext: " << gfx::hexa(hr);
return nullptr;
}
RefPtr<ID3D11VideoContext1> videoContext1;
hr = videoContext->QueryInterface(
(ID3D11VideoContext1**)getter_AddRefs(videoContext1));
if (FAILED(hr)) {
gfxCriticalNoteOnce << "Failed to get D3D11VideoContext1: "
<< gfx::hexa(hr);
return nullptr;
}
RefPtr<VideoProcessorD3D11> 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<ID3D11VideoProcessorInputView> 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<ID3D11VideoProcessorOutputView> 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