没想到又要再搞这个话题(叹气)。
在上一篇(PotPlayer无MadVR设置方案),因为我当时使用一台没有独立显卡的电脑,完全跑不动 MadVR,所以我研究了下 MadVR 之外的渲染器的方案,很欣喜地发现 D3D11 的效果其实很不错,故用之。
最近,添了一张 nV 4系的显卡,理论上很简单,只要再回到上上文用 MadVR 的方案不就行了呗?呃,其实用习惯了D3D11,发现MadVR有些小问题还能挺烦的:
- 估计是因为MadVR“太重”的缘故,在用它的时候,拖拽视频进度条有时候还是不够流畅。尤其是反向拖动的时候。实话说并不是很严重,一般来说你拖动前几次都非常流畅,但是如果你狂拖进度条几十次,前后来回(一般是找某个场景的时候),就会出现卡顿现象。
- 在开启反交错(双倍帧率)的时候,无论是LAV中开启还是Pot开启还是MadVR开启,在暂停的时候都会跳帧(画面往回跳一帧)。这个真的是个非常非常小的问题,但是强迫症很难忍受。
另外,每次放个高清视频动辄50%的GPU占用率感觉还是挺没必要的,不够环保。
那有人会问了,好吧,那你直接换回你上文说的D3D11渲染器的方案不就行了?没错,我就是这么干的。结果一干就出事了:nVidia GPU下的的D3D11,和iGPU的行为完全不一样,甚至有很多BUG!下面详述。
在开始之前,我先提一下另外一个渲染器,MPC Video Renderer(下称MPC-VR)。这个渲染器根据我的理解并不是MPC的一部分,但是是针对给MPC-BE使用为第一目标而开发的(反正他们的开发者都是那一堆人就是啦)。我第一次知道这个东西是之前搜索如何在视频播放器中调用nV的超分辨率功能时看到的。但是除了这个功能(和本文无关,后面附录谈一下)之外,这也是一个蛮优秀的渲染器,安装之后PotPlayer里也能调用(但是有坑,后谈)。
这个渲染器其实是一个很轻量的渲染器,其主要目的是方便你调用显卡自带的渲染器,再加上了一些很少的HDR相关和部分放缩算法调整,总体上而言和Pot用EVR-CP时,自带的一些设置选项差不多:
但是对于本文的目的就非常合适:非常便于我们测试各种DXVA2/D3D11相关的参数。
另外我们可以同时在MPC-BE和Pot中使用,这样可以控制变量,防止被一些PotPlayer自身引入的、而不是nVidia驱动的BUG干扰。而且其有极其详尽的OSD信息。
nVidia的D3D11视频处理器对于P010格式的BUG
要触发BUG,首先视频格式是 P010(一个10-bit的YUV格式,几乎所有的10-bit和HEVC和H.264解码后都是这个格式)。根据MPC-VR判断,这个错误其实发生在“D3D11 video processor”这一层。使用上图所示的默认设置就可以触发。如果用PotPlayer,最简单使用D3D11硬解+D3D11渲染器即可触发(不过还有一些其它条件)。
BUG1:整体颜色发绿
图片经过对比度、亮度加强。可以看到,右侧的使用MPC-VR明显有绿色的色调。
如果在MPC-VR的设置中进行以下任意修改:
- 去掉 Use Direct3D 11,也就是用 DXVA2(D3D9);
- 或在 DXVA2 and D3D11 video processor 中去掉P010/P016
这个BUG立刻就不见了。所以我的推测,这个BUG出现在 P010 转换到 R10G10B10A2_UNORM 这个YUV转RGB的过程中。因为如果用 D3D9,是转换成 A2R10G10B10 这个格式:
再放一张PotPlayer的绿图。作为对比的左边是使用 Intel iGPU 同样设置的结果。可以看到,颜色没有任何问题(但是有很严重的banding,这个下面单独说)。
其实用Pot的时候(MPC-BE+MPC-VR不会),还有其他一种情况也会触发发绿BUG,就是使用D3D9解码+D3D9渲染器。但是使用D3D9解码+D3D11就不会:
虽然不是很确定,但通过观察 Renderer OSD里列出来的格式,我们可以揣测一下发生了什么。可以看到左边是从P010(被Pot自己?)先转成了RGB10A2才进的 Video Processor,所以规避了BUG;而右边则是很奇妙地直接P010一把梭转成了XRGB(一种4:4:4的RGB格式),可能这个过程中也触发了类似原理的变绿的BUG。
OK,所以为什么会这样?其实我很多年前就发现 nVidia 和视频相关的技术栈会有发绿的问题,当时是发现用 ShadowPlay 录制视频,有时候会发绿。虽然没有任何证据,但是我强烈怀疑这个bug和我之前在ffmpeg静态图转视频这篇blog里提过的、ffmpeg的swscale组件的一个有十几年的bgr->yuv颜色和rgb->yuv颜色不一致的BUG有关(虽然我们这里的 bug 是反向转换)。
更两人恼火的是我不知道应该去哪里汇报这个BUG。这种应该是得自己写一个不牵扯到视频渲染等一系列客户端软件layer、而是直接调用 Windows的 D3D11 API 的最小实现,再去nVidia Developers 相关论坛或者 bug tracker 汇报才会比较被重视,但是我实在没这个水平,更完全不懂C++,不知道从何下手。而且我想各种第三方渲染器的开发者应该早就知晓此问题了,但是也没有搜到比较任何相关的 documentation。比如我在MPC-VR的issues中发现了至少三个相关的问题:
https://github.com/Aleksoid1978/VideoRenderer/issues/48
这个完完全全就是这个BUG!开发者提到:
To convert from NV12 and P010 to RGB you use D3D11 VP. This conversion is controlled by the driver manufacturer and MicroSoft.
v0lt
But you can disable NV12 and P010 options and convert with our shader.
因此,提问者在关闭了 D3D11 的 Video Processor 选项(上述)之后就规避掉了bug,TA也就没有再深究。
https://github.com/Aleksoid1978/VideoRenderer/issues/98
这个虽然是HDR视频,但是我怀疑也是同样的问题,因为也是P010格式的。
https://github.com/Aleksoid1978/VideoRenderer/issues/22
这个其实是最有趣的:提报者(后来才知道,原来是某群群友)使用的其实并不是原生的 D3D11 VP,而是 MPC-VR 自带的 Shader 来做 Video process,但是居然出现了同样的bug!MPC-VR后面修复了这个BUG,理论上可以根据改动一窥端倪;但是我完全搞不懂是怎么修复的:因为并没有链接到对应的commit,根据评论中提供的2个分别反馈为bad和good的测试版本的commit hash,应该是这个diff——但是感觉完全没有相关的啊?!线索断了。
BUG2:Downscale时整个画面边糊出现重影
如果说上面那个BUG可能不是太明显,有的人估计看不出来的话,那么下面这个就非常离谱了。当使用 nVidia 的 D3D11 Video Processor(VP)进行 resize 时,如果是缩小,则整个画面会变得非常模糊且出现重影,这点在比较锐利的线条尤其是硬字幕的时候非常明显:
我相信不瞎的应该都能看出来吧!要规避这个BUG,除了上面的说的完全禁用D3D11 VP的办法之外,也可以在MPC-VR中单独关闭“Use for reszing“,或者在Pot里把 resizer 改成其他任意的。再次强调,这个BUG依然是只有P010格式的视频会触发。
实话说,很难相信这么明显的bug会一直没人修复!有点怀疑是不是最近的驱动才引入这个bug的。我又去MPC-VR的repo瞅了下,确实有两个汇报(第一个,第二个),都是比较近期的。
再测 nVidia 语境下的 D3D11 渲染器的质量
OK,BUG说完,让我们来回到这一切折腾的起因——渲染器的质量。先回顾一下:
- MadVR质量最好/最可控,但是太重,在Pot调用时连带还有一些UX上的小毛病;
- 之前惊喜发现 D3D11 video renderer (Intel) 的效果其实非常好,尤其是缩放锐度和 halo 都很不赖,故用之。不过,10bit视频是直接截取,所以有非常明显的 banding 的问题,所以我们采用一些措施来尽量软解,靠前面的解码器部分来给我们dither到8bit再渲染(如果硬解会 P010 直通到 renderer)。主要的难点是Pot很弱智的内置解码器HEVC强制硬解,需要调用LAV来克服。
所以我们现在需要重新调研一下 nVidia 的D3D11(以及D3D9)在缩放和10 to 8这两个重点上的表现,毕竟已经知道了和 Intel 完全不同。当然还有 HDR tone-mapping 的问题。
不过我们先试试MPC-VR这款不错的渲染器在Pot上表现如何。如果不错,我们完全可以改用这个。很可惜,虽然没有上面提到的MadVR那些UX上的小bug,但是有个更离谱的:在调整播放窗口大小(包括切到全屏)时,会非常的卡,外加闪烁。同样,这只是个一个非常小的问题,但是我表示不能接受,故放弃。
缩放
OK,那么我们就来比较最常见的4:2:0 8-bit的1080p视频缩放到1440p的表现吧:
呃,这一比就比较尴尬了——nVidia的渲染效果比Intel差了不是一点半点。锐度不如而且 ringing 更大。
D3D9(DXVA2)和基于D3D9的EVR则更差,整体又多糊了一档;而且还有很奇怪的图像整体向左偏移约1像素的问题(假设MadVR为 ground truth),不过这个倒是不影响观看。加强对比度后对比:
当然,我们可以在 Pot 选用其他的基于 shader 的 resizer,但是那些效果也都挺差的,毕竟都是一些比较基础的算法。
10 to 8
上文提过, Intel 的渲染器上文有提到过完全没有 dithering,所以如果在那里进行10转8,会出现非常严重的 banding。这点 nVidia 这边终于有改善了!如果用 D3D11 renderer,会有 ordered dithering,而且还是渲染像素级的(非原始分辨率级),效果很好:
但是!别忘了我们上面提到的发绿的BUG!(其实这图里都能看出来绿了。)所以说,这个我们其实还是享受不到,还是得老老实实把 P010的解码设置为软解,让解码器输出NV12(也就是已经降过位深),然后再给 D3D11 VP/VR。
那么在nV下,其他几款渲染器效果如何呢?直接上结论,结合上面的BUG一起说:
- D3D9解码+D3D9渲染:发绿,downscale重影,banding
- D3D11解码+D3D11渲染:发绿,downscale重影
- D3D9解码+EVR-VP渲染:完全OK!
怪了,说好的 EVR-CP 是基于 D3D9 的呢,怎么现在居然有 dithering 了?虽然不是工作在渲染分辨率(而是视频分辨率),但是效果完全OK啊!
从 OSD 可以看到其格式流程是 P010 先到 A2RGB10 进 Mixer,然后在 RGB 空间 dither 到 XRGB 进行后面的步骤。
也就是说,如果不是缩放质量太差,我们甚至可以换回 EVR-VP 了。
另外还有一点,上文提到修改内置解码器设置中,把 HEVC 的解码器改成 ffmpeg.dll 来强制软解,很可惜现在已经没有这个选项了!所以说现在没有任何办法可以关闭 Pot 对 HEVC 的硬解(也就是会导致10-bit输出 P010 导致 BUG),所以 HEVC 必须得调用外部的 LAV。
所以,最后下来,我的配置其实和之前差不多:
Pot 内置解码器设置中不开启硬解(但是先勾选一下然后勾选下面的D3D11后再取消以防万一:
对于HEVC,手动设置LAV为编码器,来规避P010直通D3D11 renderer导致的BUG:
使用内置解码器的时候(所有非HEVC的格式),其实都等价于会自动使用转换滤镜。对于调用LAV的情况(HEVC),开不开出来的结果对于D3D11渲染器其实都没差,我就开了。
不过要注意,如果 LAV 勾选了 P010、且不开启转换滤镜的时候,LAV播放10-bit 会出P010给D3D11,导致触发 BUG。而且不管你在 Pot 的 Colorspaces 那里怎么设置都不行。如果真的不想开转换滤镜,那一定要进 LAV 把 P010 输出取消勾选。这点我们下面详细总结里会再赘述一遍。
BT.2020/HDR
设置完后,我们再去验证一下几个非标情况的播放结果。首先是 HDR——其实这个东西我也不是很熟,纯粹靠自己瞎看了。说错了请指教。
因为我的屏幕是SDR,所以需要tone mapping到 SDR。如果用MPC-BE先做个简单测试,会发现播放的时候其实MPC-VR端收到的 Transfer Function 变成了BT.709了(但是 Matrix 和 Primaries 还是 BT.2020):
EVP-CP的话则是显示了两个BT.709,搞不清楚哪个是哪个。
这个转换是完全不可控的,似乎也没有任何选项,无论硬解软解都会转换。当然我也不是想折腾,毕竟颜色是对的就行了。
而如果你用 MadVR,则是显示了两个BT.2020:
而且颜色是错的,你需要单独设置MadVR显示器校准那边才能把颜色给整对。而且MadVR那边的设置非常迷惑,我之前提过一次。
(题外话,在MPC-VR的repo里有搜到有人说 VR 的 OSD 显示的 Primaries 和 Matrix 是和 MediaInfo 正好反了但是开发者不置可否,介于这三个东西经常有各种奇怪的别名,我不好说也不深究了。)
同理,用 Pot 调用各种渲染器也有这个类似的操作,但是有的时候没有 MPC-BE 那么灵光。比如,如果用LAV 输出 NV12(输出 P010 同理)然后喂给 MPC-VR:
可以看到和 MPC-BE 不同,这里 MPC-VR 是收到了 BT.2020 transfer function。然后出来的颜色也不对。虽然 MPC-VR 里有转换 SDR 的选项:
但是我折腾了一万年也没整明白怎么能触发他(触发了的话 OSD 中应该会有一行后处理:转换到 SDR 云云)。不过,如果我把 HEVC 的解码器改回内置(并被 Pot 强制硬解),则一切就又正常了,和 MPC-BE一致:
搞不懂啊搞不懂。还好,用 D3D11 renderer 的话没这毛病,无论什么解码器,都可以正常转 SDR。
另外有个细节,如果 resizer 选的是 auto,这里这个测试用的 4K BT.2020 视频无论解码器用的啥(Pot 自带硬解,LAV 各种格式输出,etc.),resizer都会变成 Texture Bilinear 而不是一般常见的 D3D11 Video Processor,也就是类似 EVR-CP 的画质。当然你可以手动选回去(我有想是不是因为视频是4K的缘故,但是另外找了一个 4K BT.709 的视频,没触发)。
Full range, 4:2:2, 4:4:4 等各种情况总结
还是直接先上表吧!
这次和上次的比稍微复杂了点,多加了几列,有两列还调换了下顺序,如果要对比请注意。标注思路还是一样:绿色 good,黄色可忍,红色不能忍。EVR Vista 啥的那个就不测试了,反正没人会用到。
我上次的表格没有区分是否勾选Pot的视频“转换滤镜”,后来发现这个对于LAV的还是有些区别的,故加上。
第四列那个再罗嗦一次,就是这个选项,名字太长了:
中文是叫:
下称“直接转换输出色彩空间”。其大概意思就是,如果条件允许绕过Pot自带的转换滤镜,直接从解码器通到渲染器。因此,3、4列也不是所有组合都会有区别,简单来说:
- 使用内置解码器时,相当于始终启用了“转换滤镜”。实际上是否能跳过,取决于“直接转换输出色彩空间”是否启用。
- 使用 LAV 等外部解码器时,只有开启转换滤镜时,开启“直接转换输出色彩空间”才有意义。因为如果不开启转换滤镜,其实就相当于可以直接转换输出色彩空间。唯一的区别就是,如果使用开启转换滤镜+直接转换输出色彩空间的组合时,Pot 不会接受LAV的P010输出为输入(所以 LAV 会变成NV12 喂 Pot),但是不开启转换滤镜则可以。
那么让我们仔细端详下这个表。
可以看到,D3D9对于 full range 的视频束手无策,总会clip,我们先一票否决。
使用内置解码器时:
对于422和444只能软解。如果不开启直接转换输出色彩空间,因为转换滤镜的缘故会用NV12,所以自然损失了不少 Chroma 空间的质量;即使开启,renderer最大也只吃422,所以444还是会损失。
对于视频本来就是8 bit YUV的,无论软硬解都OK,反正都是NV12。对于10-bit则比较tricky。如果是软解(H264的情况),内置的 ffmpeg 解码器会帮你 dithering 后 NV12 输出,除非开启了直接转换输出色彩空间,则会直通 P010。而硬解怎么搞都是 P010 ——别忘了,如果是 HEVC,Pot 的内置解码器是强制硬解的。
如上文所述,我们的渲染器碰到 P010,EVR-CP 是很OK的,D3D9、D3D11 都有严重的问题。所以如果和我一样要用 D3D11,就得不开启直接转换输出色彩空间来保证软解时输出正确。至于硬解的情况?我们直接选择 HEVC 的解码器为 LAV 来解决。
再来看看LAV:
基本来说,使用“禁用转换滤镜”和”启用转换滤镜+同时开启直接转换输出色彩空间”这两个组合都可以完美处理所有格式:对于 10-bit,如果是 D3D11 渲染器的情况,要手动取消掉 LAV 里的 P010 输出,在 LAV 中直接 dither 到 NV12。
如果使用转换滤镜但是却不开启直接转换输出色彩空间,因为Pot的转换滤镜只能工作在 NV12,这两种色彩空间,所以即使是10-bit 视频,也是会直接找 LAV 要 NV12,即使你 LAV 没有取消 P010 输出。所以很OK。但是副作用是,无法正确处理442和444的视频,这个和上面原因类似,就不赘述了。
介于我们想用 D3D11 渲染器,然后还要应对 P010 的BUG,我们其实有以下几种选择:
- 全局使用 LAV + 禁用转换滤镜 + LAV 中禁用 P010:所有格式都OK。缺点是如果其他情况调用LAV时无法用 P010。
- 全局使用 LAV + 转化滤镜 + 直接转换输出色彩空间:所有格式都OK。
- AVC使用内置(软解),HEVC 使用 LAV,同时禁用“直接转换输出色彩空间”防止内置软解直通 P010:422、444效果差。
理论上自然应该用2,但是因为某些很纠结的原因(主要是用内置滤镜比较流畅),我实际上是用的3……呃。我就是有毛病我承认。
再看看默认的 EVR-CP:
其中真·默认设置的是倒数第二行。可以看到,效果还是挺不错的(和我改了半天的结果基本一样)。
也就是说,如果不在意缩放画质(这个其实也可以修改算法来稍微改善),Pot的默认设置现在已经完全可以用了,因为实际上他现在已经解决了 banding (or lack of dithering) 这一大痛点。我突然感觉到一阵空虚:我干嘛要折腾这个!
附录:nVidia RTX Video Super Resolution
顺便提一嘴这个(下称VSR)。毕竟我最开始折腾有这个的因素。目前为止支持这个的播放器还不是很多,Chrome 是原生支持的,什么都不需要设置。VLC有个专门的魔改版,但是这个版本有个非常恶性的bug,播放一切非16:9等非标准宽屏分辨率的视频时会显示比例错误。
说个题外话:这个BUG已经提出了9个月了,但是根本没人修复。对于这种大型知名开源项目,经常看到这种核心功能还算维护的OK、但是稍微非核心功能的、即使是严重bug也没人修复的窘状。但是与之相反,有些明明理论上规模相当的项目,比如MPV,就有非常充裕的核心开发者和路人在进行快速迭代。搞不懂这是什么原因,是项目太老不够吸引新人?还是PR审核太慢,久而久之就没有人愿意参与?(嘛,想起 ffmpeg 现在还得在 mailing list patch……)哦,对于 VLC 这个特例,丫的 repo 和报错网站(https://code.videolan.org/)居然需要审批才能注册账号,这能好吗?
对于 MPC-VR,已经很早就加入了对VSR的支持,勾选一个选项即可(必须得用D3D11,所以连带着上面各种BUG)。另外还有个fork据说是加强了对HDR的支持,不过我没试过。
至于Pot这边,虽然已经加上了选项:
(要启用D3D11渲染器才能选)但是我这边实际上并不好使,无法调用到 VSR。更离谱的是,如果我改用MPC-VR 渲染器,然后再里面勾选,在 Pot 里还是无法触发 VSR。所以我放弃了。(我有发邮件问作者,但是他说他没问题。)
效果方面,我只测试了开到4(最大):denoise 的强度还是蛮高,对于那种 compression artifact极多的超低分辨率视频效果还行:
但是看 720P 或者 1080P 之类的本身就不是很模糊的视频就油画感很强了,感觉没有必要,不如传统缩放算法。
哦对了,这个 VSR 对于小于 360P(以及大于 1080P?)的视频是无效的,这是 nV 那边写死的,不是播放器/渲染器的问题。