Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Windows] Refactor DXVA renderer to enumerate and use the supported conversions #23513

Merged
merged 9 commits into from Jul 18, 2023

Conversation

CrystalP
Copy link
Contributor

@CrystalP CrystalP commented Jul 14, 2023

Description

This is the refactor that the previous ones enabled and mostly finishes a transition to have the dxva render method rely on the Windows 10 ID3D11VideoProcessorEnumerator1 interface (with provisions to keep Windows 8 working)

There are barely any functionality changes, with very minor improvements.
One change by commit, clearly labelled (I hope!). A few of the commits were kept separate for review and will be combined for the merge.

Reorganized the DXVA renderer so that it contains the rules to decide how to handle the rendering. The processor is now "dumb", the enumerator still contains some rules to pre-select which conversions make sense for a source/output combination.
The process goes like this:

GetWeight:

  • Create enumerator
  • List supported conversions for the given source and destination
    • the list has separate entries for the different output formats 8 bits, 10 bits
    • special case for Windows 8: return all SDR conversions as supported, in the absence of the Enumerator1 interface
  • At least one result => mark dxva as a candidate render method

Configure:

  • Create enumerator
  • List supported conversions (same function used by GetWeight)
  • Chose best candidate based on source, destination, desired quality, VSR use, ...
  • Create processor
  • Configure processor for the chosen conversion
  • Turn on VSR when requested

CheckVideoParameters (detect changes during playback):

  • compare render buffer attributes that impact conversion choice with values in previous frame
  • in case of change, list supported conversions and choose new best match
    • no support available: keep using the same conversion. This avoids a black screen. A better way (fallback to Pixel Shaders) requires changes to WinRenderer to switch render methods mid-stream.
  • if the new best conversion is different from current conversion, reconfigure the renderer and processor

notes:

  • HLG to SDR conversion is supported by AMD processor and used to be activated, but gave strange results. With this PR it will not be used at all, with better visual results. HLG could be revisited in future PR.
  • the previous code did not react to changes in VSR setting, high quality setting or source size / pixel format and that was not added here in order to limit the scope. Could be implemented in future PR.
  • an enumerator still gets created twice in succession for GetWeight and then Configure, due to the way WinRenderer probes the render methods. Could be improved in future PR.

Motivation and context

Reduces logic duplication in the code, enable reacting to changes during playback in a saner way by centralizing the rules in one place.

How has this been tested?

Windows 10, AMD, Intel gen4 and gen13
no behaviour changes for combinations of SDR/HDR/HLG playback to limited/full/SDR/HDR output.

No nVidia hardware at hand to test impact on VSR. It seems to activate when needed on Intel gen13 but not much visual effect.

What is the effect on users?

none, will make further improvements possible

Types of change

  • Bug fix (non-breaking change which fixes an issue)
  • Clean up (non-breaking change which removes non-working, unmaintained functionality)
  • Improvement (non-breaking change which improves existing functionality)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that will cause existing functionality to change)
  • Cosmetic change (non-breaking change that doesn't touch code)
  • None of the above (please explain below)

Checklist:

  • My code follows the Code Guidelines of this project
  • My change requires a change to the documentation, either Doxygen or wiki
  • I have updated the documentation accordingly
  • I have read the Contributing document
  • I have added tests to cover my change
  • All new and existing tests passed

Copy link
Member

@thexai thexai left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree with all the changes and I think it's a great improvement not only because of the re-usability of the code, but also by using the same logic for configure and rendering. This keeps things simple and prevents weird behavior / black screens. It will also make it easier to maintain or extend in the future.

HLG thing: I agree to remove this particular case. Very few systems support it and also (although the code comment seems to indicate otherwise) I currently think that this color space can only be used for HLG to HLG (not HLG to SDR). So it can never be used on Windows (desktop) because no video driver supports HLG output.

HLG is backwards compatible with SDR but then the BT.2020 SDR legacy color space should be used.

Runtime tested and no issues found. All seems work as before. No performance regressions found playing 4K HDR 59.94 fps

Only minor code cosmetic things:

@CrystalP
Copy link
Contributor Author

HLG thing: I agree to remove this particular case. Very few systems support it and also (although the code comment seems to indicate otherwise) I currently think that this color space can only be used for HLG to HLG (not HLG to SDR). So it can never be used on Windows (desktop) because no video driver supports HLG output.

Yes Windows doesn't support HLG output.

However AMD claims support and does the HLG to HDR conversion properly (same results as Kodi's own method) so I plan to add the support later.

AMD also claims support for HLG to SDR but by default it looks weird. Adjusting brightness and contrast can make it look good. I suppose it's the same type of issue noticed with tonemapping HDR to SDR in general, so I would still keep that one out. At least for v21.

About HLG back compatibility with SDR: it's compatible yes so converting just the primaries works, but I don't think it's the optimal result. It should benefit from tonemapping the picture from PQ to SDR and I plan to play a little with that.

Thanks for the comments, I'll take care of them.

@CrystalP
Copy link
Contributor Author

Comments integrated, except for the const & return, maybe someone will weight in

{
m_enumerator->LogSupportedConversions(
dxgi_format, CProcessorHD::AvToDxgiColorSpace(DXVA::DXGIColorSpaceArgs(picture)),
m_intermediateTargetFormat);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When code reach this point m_intermediateTargetFormat is not yet initialized, then in LogSupportedConversions the list of conversions not indicates which is picked

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah yes, I took a note to remove that parameter, which doesn't make sense anymore, and forgot to apply it. It may come back in another refactor.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done, removed.


const DXGI_COLOR_SPACE_TYPE inputCS = isSourceFullRange
? DXGI_COLOR_SPACE_YCBCR_FULL_G22_LEFT_P709
: DXGI_COLOR_SPACE_YCBCR_STUDIO_G22_LEFT_P709;
Copy link
Member

@thexai thexai Jul 17, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There at least one use case with old SD videos (DVD) not handled properly: these MPEG2 videos should use primaries P601 and color space DXGI_COLOR_SPACE_YCBCR_STUDIO_G22_LEFT_P601. As this is working properly in current master could be considered a regression, then please add here the code to handle these cases:

Mainly use colors space:

DXGI_COLOR_SPACE_YCBCR_STUDIO_G22_LEFT_P601
DXGI_COLOR_SPACE_YCBCR_FULL_G22_LEFT_P601

when primaries reported by FFmpeg are AVCOL_PRI_BT470BG or AVCOL_PRI_SMPTE170M (see old code)

This is a sample video:
tsbuffer.zip

Note that some times these old videos not has this information's properly signaled (AVCOL_PRI_UNSPECIFIED ) then can be used video resolution to infer that is SD video. As this is not implemented in current code is not a blocking thing and can be implemented later. But RenderShaders has it implemented:

AVColorPrimaries CRendererShaders::GetSrcPrimaries(AVColorPrimaries srcPrimaries, unsigned width, unsigned height)
{
AVColorPrimaries ret = srcPrimaries;
if (ret == AVCOL_PRI_UNSPECIFIED)
{
if (width > 1024 || height >= 600)
ret = AVCOL_PRI_BT709;
else
ret = AVCOL_PRI_BT470BG;
}
return ret;
}

There also some special case with DXGI_COLOR_SPACE_YCBCR_FULL_G22_NONE_P709_X601 (primaries BT.709 and tranfer BT.601) and RGB color spaces but again this can be done on a follow-up PR.

As a requirement I think it is enough to support regular SD videos here.

General info about this:
https://guide.encode.moe/archived-websites/bt601-vs-bt709.html

Copy link
Contributor Author

@CrystalP CrystalP Jul 17, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed. I never saw any video reporting proper flags and it slipped my mind, so I'll integrate the flag-based detection.
I'll try playing a raw DVD in case ffmpeg reports flagging for those.

Something similar to PS to guess BT601 is OK, I was thinking of another PR but can do a separate commit here.
If the information is available, maybe extend with conditions on codec or container type, like mpeg-2, xvid, avi... all that predate HD basically)

edit: and might as well do it at RendererBase level so that all render methods benefit.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the information is available, maybe extend with conditions on codec or container type, like mpeg-2, xvid, avi... all that

I vote for keep it simple same as is in PS for now. Codecs and containers not helps much... i.e. MPEG-2 is in both DVD and Blu-Ray and guess only when primaries are AVCOL_PRI_UNSPECIFIED

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure.

For P601/P709_X601, rather than duplicate the logic, I'm going to change the Query*Conversions to use AvToDxgiColorSpace(). I kept it around for that purpose for a future refactor, might as well do it now. It only needs minor changes. Will be a separate commit.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Refactor of Query*Conversions to use AvToDxgiColorSpace() will come in another PR for clarity. Added the few missing lines to QuerySDRConversions() for now. Tested with your sample, with DVD (flags properly reported) > OK.

MS indicates that DXGI_COLOR_SPACE_YCBCR_FULL_G22_NONE_P709_X601 is for jpg, which Kodi won't decode with dxva, so the code will likely never be used.

Improvement for AVCOL_PRI_UNSPECIFIED requires touching other render methods to share the logic and code, so beyond the scope of this PR.

…ed GetWeight

Refactor of the existing logic to present GetWeight with a list of the supported conversions,
using the ID3D11VideoProcessorEnumerator1 interface of Windows 10 and above.
Support for SDR is assumed in the absence of the D3D11 interface (Windows 8)

At least one conversion: the dxva processor supports the rendering.

Keep the previous API around for now as it is used by the Configure() path.
Removed in later commits, including the temporary adaptor functions.
…ist of supported conversions

Configure retrieves the same list of conversions as GetWeight, applies some rules to pick the best
(refactor of CalcIntermediateTargetFormat) and tells CProcessorHD to use it.
No need to check the input format in Configure() anymore, it is checked by extracting the
supported conversion, and when setting the conversion in the processor.

The Configure and GetWeight paths now execute the same logic to gather information, even though
it's still executed twice.
…e it with the processor

It will make it easier to get the renderer react to video attributes changes during playback.
Removed all methods that CProcessorHD was simply forwarding to CEnumeratorHD.
Removed PreInit method of CProcessorHD since the enumerator is created externally and has
already been created successfully by the time PreInit was called.
First do everything enumerator related, then processor related.
to be merged with previous commit once reviewed
…g playback.

Compare attributes of new frame with old frame. Differences could require a different conversion, re-enumerate conversions and choose the best one again.

Similar to the replaced code, does not react to:
- VSR setting change
- High precision processing setting change
- Source dimensions or input dxgi format changes - in practice doesn't seem to have an effect on D1D11 enumerator interfaces behavior , but enumerator should be reopened.
To be added in future changes.
@CrystalP CrystalP merged commit 60ae8f5 into xbmc:master Jul 18, 2023
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants