Skip to content

Commit

Permalink
Merge pull request xbmc#23606 from CrystalP/fix-nvidia-deinterlacing
Browse files Browse the repository at this point in the history
[Windows][dxva] Don't send repeated frames through the processor / Fix nVidia deinterlacing / VSR with deinterlacing
  • Loading branch information
CrystalP authored Aug 12, 2023
2 parents 1561351 + 3e9b539 commit 43bf471
Show file tree
Hide file tree
Showing 2 changed files with 201 additions and 79 deletions.
254 changes: 175 additions & 79 deletions xbmc/cores/VideoPlayer/VideoRenderers/HwDecRender/DXVAHD.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ void CProcessorHD::Close()
m_pVideoContext = nullptr;
m_pVideoDevice = nullptr;
m_superResolutionEnabled = false;
m_configured = false;
}

bool CProcessorHD::InitProcessor()
Expand Down Expand Up @@ -177,6 +178,21 @@ bool CProcessorHD::OpenProcessor()
return false;
}

m_pVideoContext->VideoProcessorSetStreamAutoProcessingMode(m_pVideoProcessor.Get(), 0, FALSE);
m_pVideoContext->VideoProcessorSetStreamOutputRate(
m_pVideoProcessor.Get(), 0, D3D11_VIDEO_PROCESSOR_OUTPUT_RATE_NORMAL, FALSE, 0);

ComPtr<ID3D11VideoContext1> videoCtx1;
if (SUCCEEDED(m_pVideoContext.As(&videoCtx1)))
{
videoCtx1->VideoProcessorSetOutputShaderUsage(m_pVideoProcessor.Get(), 1);
}
else
{
CLog::LogF(LOGWARNING, "unable to retrieve ID3D11VideoContext1 to allow usage of shaders on "
"video processor output surfaces.");
}

// Output background color (black)
D3D11_VIDEO_COLOR color;
color.YCbCr = { 0.0625f, 0.5f, 0.5f, 1.0f }; // black color
Expand Down Expand Up @@ -255,6 +271,119 @@ ComPtr<ID3D11VideoProcessorInputView> CProcessorHD::GetInputView(CRenderBuffer*
return {};
}

bool CProcessorHD::CheckVideoParameters(const CRect& src,
const CRect& dst,
const UINT& rotation,
const float& contrast,
const float& brightness,
const CRenderBuffer& rb)
{
bool updatedParameter{false};

if (!m_configured || m_lastSrc != src)
{
const RECT sourceRECT = {static_cast<LONG>(src.x1), static_cast<LONG>(src.y1),
static_cast<LONG>(src.x2), static_cast<LONG>(src.y2)};

m_pVideoContext->VideoProcessorSetStreamSourceRect(m_pVideoProcessor.Get(),
DEFAULT_STREAM_INDEX, TRUE, &sourceRECT);
m_lastSrc = src;
updatedParameter = true;
}

if (!m_configured || m_lastDst != dst)
{
const RECT dstRECT = {static_cast<LONG>(dst.x1), static_cast<LONG>(dst.y1),
static_cast<LONG>(dst.x2), static_cast<LONG>(dst.y2)};

m_pVideoContext->VideoProcessorSetStreamDestRect(m_pVideoProcessor.Get(), DEFAULT_STREAM_INDEX,
TRUE, &dstRECT);
m_pVideoContext->VideoProcessorSetOutputTargetRect(m_pVideoProcessor.Get(), TRUE, &dstRECT);

m_lastDst = dst;
updatedParameter = true;
}

if (!m_configured || m_lastBrightness != brightness)
{
ApplyFilter(D3D11_VIDEO_PROCESSOR_FILTER_BRIGHTNESS, static_cast<int>(brightness), 0, 100, 50);

m_lastBrightness = brightness;
updatedParameter = true;
}

if (!m_configured || m_lastContrast != contrast)
{
ApplyFilter(D3D11_VIDEO_PROCESSOR_FILTER_CONTRAST, static_cast<int>(contrast), 0, 100, 50);

m_lastContrast = contrast;
updatedParameter = true;
}

// unused filters - set once and forget
if (!m_configured)
{
ApplyFilter(D3D11_VIDEO_PROCESSOR_FILTER_HUE, 50, 0, 100, 50);
ApplyFilter(D3D11_VIDEO_PROCESSOR_FILTER_SATURATION, 50, 0, 100, 50);
}

if (!m_configured || m_lastRotation != rotation)
{
m_pVideoContext->VideoProcessorSetStreamRotation(
m_pVideoProcessor.Get(), DEFAULT_STREAM_INDEX, rotation != 0,
static_cast<D3D11_VIDEO_PROCESSOR_ROTATION>(rotation / 90));

m_lastRotation = rotation;
updatedParameter = true;
}

ComPtr<ID3D11VideoContext1> videoCtx1;
if (SUCCEEDED(m_pVideoContext.As(&videoCtx1)))
{
if (!m_configured || m_lastConversion != m_conversion)
{
videoCtx1->VideoProcessorSetStreamColorSpace1(m_pVideoProcessor.Get(), DEFAULT_STREAM_INDEX,
m_conversion.m_inputCS);
videoCtx1->VideoProcessorSetOutputColorSpace1(m_pVideoProcessor.Get(),
m_conversion.m_outputCS);

m_lastConversion = m_conversion;
updatedParameter = true;
}
}
else if (!m_configured || m_lastColorSpace != rb.color_space || m_lastFullRange != rb.full_range)
{
// input colorspace
bool isBT601 = rb.color_space == AVCOL_SPC_BT470BG || rb.color_space == AVCOL_SPC_SMPTE170M;
// clang-format off
D3D11_VIDEO_PROCESSOR_COLOR_SPACE colorSpace
{
0u, // 0 - Playback, 1 - Processing
rb.full_range ? 0u : 1u, // 0 - Full (0-255), 1 - Limited (16-235) (RGB)
isBT601 ? 1u : 0u, // 0 - BT.601, 1 - BT.709
0u, // 0 - Conventional YCbCr, 1 - xvYCC
rb.full_range ? 2u : 1u // 0 - driver defaults, 2 - Full range [0-255], 1 - Studio range [16-235] (YUV)
};
// clang-format on
m_pVideoContext->VideoProcessorSetStreamColorSpace(m_pVideoProcessor.Get(),
DEFAULT_STREAM_INDEX, &colorSpace);
// Output color space
// don't apply any color range conversion, this will be fixed at later stage.
colorSpace.Usage = 0; // 0 - playback, 1 - video processing
colorSpace.RGB_Range = DX::Windowing()->UseLimitedColor() ? 1 : 0; // 0 - 0-255, 1 - 16-235
colorSpace.YCbCr_Matrix = 1; // 0 - BT.601, 1 = BT.709
colorSpace.YCbCr_xvYCC = 1; // 0 - Conventional YCbCr, 1 - xvYCC
colorSpace.Nominal_Range = 0; // 2 - 0-255, 1 = 16-235, 0 - undefined
m_pVideoContext->VideoProcessorSetOutputColorSpace(m_pVideoProcessor.Get(), &colorSpace);

m_lastColorSpace = rb.color_space;
m_lastFullRange = rb.full_range;
updatedParameter = true;
}

return updatedParameter;
}

bool CProcessorHD::Render(CRect src, CRect dst, ID3D11Resource* target, CRenderBuffer** views, DWORD flags, UINT frameIdx, UINT rotation, float contrast, float brightness)
{
std::unique_lock<CCriticalSection> lock(m_section);
Expand All @@ -266,13 +395,50 @@ bool CProcessorHD::Render(CRect src, CRect dst, ID3D11Resource* target, CRenderB
if (!views[2])
return false;

RECT sourceRECT = {static_cast<LONG>(src.x1), static_cast<LONG>(src.y1),
static_cast<LONG>(src.x2), static_cast<LONG>(src.y2)};
RECT dstRECT = {static_cast<LONG>(dst.x1), static_cast<LONG>(dst.y1), static_cast<LONG>(dst.x2),
static_cast<LONG>(dst.y2)};
const bool updatedParam =
CheckVideoParameters(src, dst, rotation, contrast, brightness, *views[2]);

D3D11_VIDEO_FRAME_FORMAT dxvaFrameFormat = D3D11_VIDEO_FRAME_FORMAT_PROGRESSIVE;

if ((flags & RENDER_FLAG_FIELD0) && (flags & RENDER_FLAG_TOP))
dxvaFrameFormat = D3D11_VIDEO_FRAME_FORMAT_INTERLACED_TOP_FIELD_FIRST;
else if ((flags & RENDER_FLAG_FIELD1) && (flags & RENDER_FLAG_BOT))
dxvaFrameFormat = D3D11_VIDEO_FRAME_FORMAT_INTERLACED_TOP_FIELD_FIRST;
else if ((flags & RENDER_FLAG_FIELD0) && (flags & RENDER_FLAG_BOT))
dxvaFrameFormat = D3D11_VIDEO_FRAME_FORMAT_INTERLACED_BOTTOM_FIELD_FIRST;
else if ((flags & RENDER_FLAG_FIELD1) && (flags & RENDER_FLAG_TOP))
dxvaFrameFormat = D3D11_VIDEO_FRAME_FORMAT_INTERLACED_BOTTOM_FIELD_FIRST;

m_pVideoContext->VideoProcessorSetStreamFrameFormat(m_pVideoProcessor.Get(), DEFAULT_STREAM_INDEX,
dxvaFrameFormat);

D3D11_VIDEO_PROCESSOR_STREAM stream_data = {};
stream_data.Enable = TRUE;

const bool frameProgressive = dxvaFrameFormat == D3D11_VIDEO_FRAME_FORMAT_PROGRESSIVE;

// Progressive or Interlaced video at normal rate.
const bool secondField = ((flags & RENDER_FLAG_FIELD1) && !frameProgressive) ? 1 : 0;
stream_data.InputFrameOrField = frameIdx + (secondField ? 1 : 0);
stream_data.OutputIndex = secondField;

// Render() gets called once for each displayed frame, following the pattern necessary to adapt
// the source fps to the display fps, with repetitions as needed (ex. 3:2 for 23.98fps at 59Hz)
// However there is no need to render the same frame more than once, the intermediate target is
// not cleared and the output of the previous processing is still there.
// For nVidia deinterlacing it's more than an optimization. The processor must see each field
// only once or it won't switch from bob to a more advanced algorithm.
// for ex. when playing 25i at 60fps, decoded frames A B => output A0 A1 B0 B1 B1
// B1 field is repeated and the second B1 must be skipped.
// Exception: always process when a parameter changes to provide immediate feedback to the user

if (m_configured && m_lastInputFrameOrField == stream_data.InputFrameOrField &&
m_lastOutputIndex == stream_data.OutputIndex && !updatedParam)
return true;

m_lastInputFrameOrField = stream_data.InputFrameOrField;
m_lastOutputIndex = stream_data.OutputIndex;

unsigned int providedPast = 0;
for (int i = 3; i < 8; i++)
{
Expand All @@ -290,8 +456,6 @@ bool CProcessorHD::Render(CRect src, CRect dst, ID3D11Resource* target, CRenderB
std::vector<ID3D11VideoProcessorInputView*> pastViews(pastFrames, nullptr);
std::vector<ID3D11VideoProcessorInputView*> futureViews(futureFrames, nullptr);

D3D11_VIDEO_PROCESSOR_STREAM stream_data = {};
stream_data.Enable = TRUE;
stream_data.PastFrames = pastFrames;
stream_data.FutureFrames = futureFrames;
stream_data.ppPastSurfaces = pastViews.data();
Expand Down Expand Up @@ -336,75 +500,6 @@ bool CProcessorHD::Render(CRect src, CRect dst, ID3D11Resource* target, CRenderB
return false;
}

if (flags & RENDER_FLAG_FIELD0 && flags & RENDER_FLAG_TOP)
dxvaFrameFormat = D3D11_VIDEO_FRAME_FORMAT_INTERLACED_TOP_FIELD_FIRST;
else if (flags & RENDER_FLAG_FIELD1 && flags & RENDER_FLAG_BOT)
dxvaFrameFormat = D3D11_VIDEO_FRAME_FORMAT_INTERLACED_TOP_FIELD_FIRST;
if (flags & RENDER_FLAG_FIELD0 && flags & RENDER_FLAG_BOT)
dxvaFrameFormat = D3D11_VIDEO_FRAME_FORMAT_INTERLACED_BOTTOM_FIELD_FIRST;
if (flags & RENDER_FLAG_FIELD1 && flags & RENDER_FLAG_TOP)
dxvaFrameFormat = D3D11_VIDEO_FRAME_FORMAT_INTERLACED_BOTTOM_FIELD_FIRST;

bool frameProgressive = dxvaFrameFormat == D3D11_VIDEO_FRAME_FORMAT_PROGRESSIVE;

// Progressive or Interlaced video at normal rate.
stream_data.InputFrameOrField = frameIdx;
stream_data.OutputIndex = flags & RENDER_FLAG_FIELD1 && !frameProgressive ? 1 : 0;

// input format
m_pVideoContext->VideoProcessorSetStreamFrameFormat(m_pVideoProcessor.Get(), DEFAULT_STREAM_INDEX, dxvaFrameFormat);
// Source rect
m_pVideoContext->VideoProcessorSetStreamSourceRect(m_pVideoProcessor.Get(), DEFAULT_STREAM_INDEX, TRUE, &sourceRECT);
// Stream dest rect
m_pVideoContext->VideoProcessorSetStreamDestRect(m_pVideoProcessor.Get(), DEFAULT_STREAM_INDEX, TRUE, &dstRECT);
// Output rect
m_pVideoContext->VideoProcessorSetOutputTargetRect(m_pVideoProcessor.Get(), TRUE, &dstRECT);

ComPtr<ID3D11VideoContext1> videoCtx1;
if (SUCCEEDED(m_pVideoContext.As(&videoCtx1)))
{
videoCtx1->VideoProcessorSetStreamColorSpace1(m_pVideoProcessor.Get(), DEFAULT_STREAM_INDEX,
m_conversion.m_inputCS);
videoCtx1->VideoProcessorSetOutputColorSpace1(m_pVideoProcessor.Get(), m_conversion.m_outputCS);
// makes target available for processing in shaders
videoCtx1->VideoProcessorSetOutputShaderUsage(m_pVideoProcessor.Get(), 1);
}
else
{
// input colorspace
bool isBT601 = views[2]->color_space == AVCOL_SPC_BT470BG || views[2]->color_space == AVCOL_SPC_SMPTE170M;
// clang-format off
D3D11_VIDEO_PROCESSOR_COLOR_SPACE colorSpace
{
0u, // 0 - Playback, 1 - Processing
views[2]->full_range ? 0u : 1u, // 0 - Full (0-255), 1 - Limited (16-235) (RGB)
isBT601 ? 1u : 0u, // 0 - BT.601, 1 - BT.709
0u, // 0 - Conventional YCbCr, 1 - xvYCC
views[2]->full_range ? 2u : 1u // 0 - driver defaults, 2 - Full range [0-255], 1 - Studio range [16-235] (YUV)
};
// clang-format on
m_pVideoContext->VideoProcessorSetStreamColorSpace(m_pVideoProcessor.Get(), DEFAULT_STREAM_INDEX, &colorSpace);
// Output color space
// don't apply any color range conversion, this will be fixed at later stage.
colorSpace.Usage = 0; // 0 - playback, 1 - video processing
colorSpace.RGB_Range = DX::Windowing()->UseLimitedColor() ? 1 : 0; // 0 - 0-255, 1 - 16-235
colorSpace.YCbCr_Matrix = 1; // 0 - BT.601, 1 = BT.709
colorSpace.YCbCr_xvYCC = 1; // 0 - Conventional YCbCr, 1 - xvYCC
colorSpace.Nominal_Range = 0; // 2 - 0-255, 1 = 16-235, 0 - undefined
m_pVideoContext->VideoProcessorSetOutputColorSpace(m_pVideoProcessor.Get(), &colorSpace);
}

// brightness
ApplyFilter(D3D11_VIDEO_PROCESSOR_FILTER_BRIGHTNESS, static_cast<int>(brightness), 0, 100, 50);
// contrast
ApplyFilter(D3D11_VIDEO_PROCESSOR_FILTER_CONTRAST, static_cast<int>(contrast), 0, 100, 50);
// unused filters
ApplyFilter(D3D11_VIDEO_PROCESSOR_FILTER_HUE, 50, 0, 100, 50);
ApplyFilter(D3D11_VIDEO_PROCESSOR_FILTER_SATURATION, 50, 0, 100, 50);
// Rotation
m_pVideoContext->VideoProcessorSetStreamRotation(m_pVideoProcessor.Get(), DEFAULT_STREAM_INDEX, rotation != 0,
static_cast<D3D11_VIDEO_PROCESSOR_ROTATION>(rotation / 90));

// create output view for surface.
const D3D11_VIDEO_PROCESSOR_OUTPUT_VIEW_DESC outputViewDesc = {D3D11_VPOV_DIMENSION_TEXTURE2D,
{0}};
Expand All @@ -414,7 +509,8 @@ bool CProcessorHD::Render(CRect src, CRect dst, ID3D11Resource* target, CRenderB
HRESULT hr{};
if (pOutputView)
{
hr = m_pVideoContext->VideoProcessorBlt(m_pVideoProcessor.Get(), pOutputView.Get(), frameIdx, 1, &stream_data);
hr = m_pVideoContext->VideoProcessorBlt(m_pVideoProcessor.Get(), pOutputView.Get(), 0, 1,
&stream_data);
if (S_OK != hr)
{
CLog::LogF(FAILED(hr) ? LOGERROR : LOGWARNING,
Expand All @@ -423,6 +519,9 @@ bool CProcessorHD::Render(CRect src, CRect dst, ID3D11Resource* target, CRenderB
}
}

if (!m_configured)
m_configured = true;

return pOutputView && !FAILED(hr);
}

Expand All @@ -436,9 +535,6 @@ bool CProcessorHD::IsSuperResolutionSuitable(const VideoPicture& picture)
if (outputWidth <= picture.iWidth)
return false;

if (picture.iFlags & DVP_FLAG_INTERLACED)
return false;

if (picture.color_primaries == AVCOL_PRI_BT2020 ||
picture.color_transfer == AVCOL_TRC_SMPTE2084 ||
picture.color_transfer == AVCOL_TRC_ARIB_STD_B67)
Expand Down
26 changes: 26 additions & 0 deletions xbmc/cores/VideoPlayer/VideoRenderers/HwDecRender/DXVAHD.h
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,15 @@ class CProcessorHD : public ID3DResource
bool OpenProcessor();
void ApplyFilter(D3D11_VIDEO_PROCESSOR_FILTER filter, int value, int min, int max, int def) const;
ComPtr<ID3D11VideoProcessorInputView> GetInputView(CRenderBuffer* view) const;
/*!
* \brief Apply new video settings if there was a change. Returns true if a parameter changed, false otherwise.
*/
bool CheckVideoParameters(const CRect& src,
const CRect& dst,
const UINT& rotation,
const float& contrast,
const float& brightness,
const CRenderBuffer& rb);

void EnableIntelVideoSuperResolution();
void EnableNvidiaRTXVideoSuperResolution();
Expand All @@ -83,5 +92,22 @@ class CProcessorHD : public ID3DResource
bool m_superResolutionEnabled{false};
ProcessorConversion m_conversion;
bool m_isValidConversion{false};

/*!
* \brief true when at least one frame has been processed successfully since init
*/
bool m_configured{false};

// Members to compare the current frame with the previous frame
UINT m_lastInputFrameOrField{0};
UINT m_lastOutputIndex{0};
CRect m_lastSrc{};
CRect m_lastDst{};
UINT m_lastRotation{0};
float m_lastContrast{.0f};
float m_lastBrightness{.0f};
ProcessorConversion m_lastConversion{};
AVColorSpace m_lastColorSpace{AVCOL_SPC_UNSPECIFIED};
bool m_lastFullRange{false};
};
};

0 comments on commit 43bf471

Please sign in to comment.