From f67272b15312fb30ae4d5f5dfb71843ee5610518 Mon Sep 17 00:00:00 2001 From: Cameron Gutman Date: Sat, 1 Nov 2025 22:42:51 -0500 Subject: [PATCH] Handle chroma co-siting in the D3D11 shaders --- app/resources.qrc | 4 +- app/shaders/build_hlsl.bat | 4 +- app/shaders/d3d11_ayuv_pixel.fxc | Bin 1452 -> 1508 bytes app/shaders/d3d11_bt2020lim_pixel.fxc | Bin 1548 -> 0 bytes app/shaders/d3d11_bt2020lim_pixel.hlsl | 15 -- app/shaders/d3d11_bt601lim_pixel.fxc | Bin 1548 -> 0 bytes app/shaders/d3d11_bt601lim_pixel.hlsl | 15 -- app/shaders/d3d11_genyuv_pixel.fxc | Bin 1632 -> 0 bytes app/shaders/d3d11_video_pixel_end.hlsli | 2 +- app/shaders/d3d11_y410_pixel.fxc | Bin 1452 -> 1508 bytes app/shaders/d3d11_yuv420_pixel.fxc | Bin 0 -> 1740 bytes ...yuv_pixel.hlsl => d3d11_yuv420_pixel.hlsl} | 1 + app/shaders/d3d11_yuv444_pixel_start.hlsli | 1 + .../video/ffmpeg-renderers/d3d11va.cpp | 215 ++++++++++-------- .../video/ffmpeg-renderers/d3d11va.h | 4 +- 15 files changed, 129 insertions(+), 132 deletions(-) delete mode 100644 app/shaders/d3d11_bt2020lim_pixel.fxc delete mode 100644 app/shaders/d3d11_bt2020lim_pixel.hlsl delete mode 100644 app/shaders/d3d11_bt601lim_pixel.fxc delete mode 100644 app/shaders/d3d11_bt601lim_pixel.hlsl delete mode 100644 app/shaders/d3d11_genyuv_pixel.fxc create mode 100644 app/shaders/d3d11_yuv420_pixel.fxc rename app/shaders/{d3d11_genyuv_pixel.hlsl => d3d11_yuv420_pixel.hlsl} (85%) diff --git a/app/resources.qrc b/app/resources.qrc index 9e520bf8..7d8cc9dc 100644 --- a/app/resources.qrc +++ b/app/resources.qrc @@ -91,9 +91,7 @@ shaders/egl_overlay.vert shaders/d3d11_vertex.fxc shaders/d3d11_overlay_pixel.fxc - shaders/d3d11_genyuv_pixel.fxc - shaders/d3d11_bt601lim_pixel.fxc - shaders/d3d11_bt2020lim_pixel.fxc + shaders/d3d11_yuv420_pixel.fxc shaders/d3d11_ayuv_pixel.fxc shaders/d3d11_y410_pixel.fxc shaders/vt_renderer.metal diff --git a/app/shaders/build_hlsl.bat b/app/shaders/build_hlsl.bat index 8ad73264..b2971d57 100644 --- a/app/shaders/build_hlsl.bat +++ b/app/shaders/build_hlsl.bat @@ -1,8 +1,6 @@ fxc /T vs_4_0_level_9_3 /Fo d3d11_vertex.fxc d3d11_vertex.hlsl fxc /T ps_4_0_level_9_3 /Fo d3d11_overlay_pixel.fxc d3d11_overlay_pixel.hlsl -fxc /T ps_4_0_level_9_3 /Fo d3d11_genyuv_pixel.fxc d3d11_genyuv_pixel.hlsl -fxc /T ps_4_0_level_9_3 /Fo d3d11_bt601lim_pixel.fxc d3d11_bt601lim_pixel.hlsl -fxc /T ps_4_0_level_9_3 /Fo d3d11_bt2020lim_pixel.fxc d3d11_bt2020lim_pixel.hlsl +fxc /T ps_4_0_level_9_3 /Fo d3d11_yuv420_pixel.fxc d3d11_yuv420_pixel.hlsl fxc /T ps_4_0_level_9_3 /Fo d3d11_ayuv_pixel.fxc d3d11_ayuv_pixel.hlsl fxc /T ps_4_0_level_9_3 /Fo d3d11_y410_pixel.fxc d3d11_y410_pixel.hlsl \ No newline at end of file diff --git a/app/shaders/d3d11_ayuv_pixel.fxc b/app/shaders/d3d11_ayuv_pixel.fxc index e820f23ff368f7c33645e49cd96f2b0b61e47d92..5fdf17a919221a28301d537618b346e85146a45e 100644 GIT binary patch delta 175 zcmZ3({e)Z6CBn&BgEK0ssQ&3hmz6)~%?tSu&d9*P@MNN-G^+zE1H+1qmg3BeZzcya zOEVssoXdQflNl&&0aOz(*^|ZDu>qtS2y}p$35a{3{5e3{0LbToifcf{mjG!8pcn@b qgTR)_TUoT_lQW9)a})j3(uz|{7*?-d&B$QJzz8-EtZ4Fmma_l}%p>Cf delta 119 zcmaFDy@p%TCBn)1!?t#D9h;5`CR>Y3HIrSYF)}bPteGe&&C0>bz)-N!QkqM{W<+FD8lvD>6c6KGA%rV{mFW1Esllaw@-P*4^T zk0OXS4><@zd&x;p>d|Wtf<1W5!JE>XAjEI>Cs{+mA+v90-Z$UOdpnzjTr~D!_1?~@ z4ed|)-2U&0S3h$KfY2Z?#PvG$Ed$R2G=e~cS{wilsq@v!wKeXELhW?Mxz5s5aG6iB zW})J|YfD^!vO1zL=N2%aAl%e+>!YUQGhofhuEbTw8+)(krmb&w%!>!y`1hjmsTH`$ z90wnC6&KVB{FR+=Pq2@Yvz@`tjqWhc4d} zhu_1^aO6gPd?T(cIh0V%=O@%&UDW`*`Eh0l2opc5_b|_Wxq|PXD|Qk(>%)8eQrpA( z=S}#de*6ji{yF5?2A}%y&V5^RH~9L@?0mM)I-Elefg`f=6YGk52VLK9csc)z`S(5B zOr$0_pYEFjG7Is<3X4g-BPu_4oTehkNn}vI@i%nk@1S2KNAw-l>tFIa6fX9j?7ye> z?|Zp&<}cKf#KY8!1v6jX-YV3vwOcM#@|E?%%2vKozaWS zZsGYwb*IlRrAd$yqDpULu5UBuvxUZTzQObZ1vAX=oqoBrUaRg@H|zR%=8~RVGM99- znBOSW^jNjLU1E>=IjBB+4;<3Jj>+2E_m07 diff --git a/app/shaders/d3d11_bt2020lim_pixel.hlsl b/app/shaders/d3d11_bt2020lim_pixel.hlsl deleted file mode 100644 index bb9f3100..00000000 --- a/app/shaders/d3d11_bt2020lim_pixel.hlsl +++ /dev/null @@ -1,15 +0,0 @@ -#include "d3d11_video_pixel_start.hlsli" - -static const min16float3x3 cscMatrix = -{ - 1.1678, 1.1678, 1.1678, - 0.0, -0.1879, 2.1481, - 1.6836, -0.6524, 0.0, -}; - -static const min16float3 offsets = -{ - 64.0 / 1023.0, 512.0 / 1023.0, 512.0 / 1023.0 -}; - -#include "d3d11_video_pixel_end.hlsli" \ No newline at end of file diff --git a/app/shaders/d3d11_bt601lim_pixel.fxc b/app/shaders/d3d11_bt601lim_pixel.fxc deleted file mode 100644 index 09f256535f7fcf4193a4581b56d692df9a3848a5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1548 zcmZux&1(~J5PiEz+gJk$Jyj80L}~?*rb?|KcAGS50;!4FRH7FfYf2(bQerAaP_`mH ziFgq_c*sEz+Dk8b5Y(d{M0yY@cn#=9kp2Vl&3+_n?a;}5{NB8o{q1aL3enh)-|68O zJNGx@UTN1;L zO@%S%R~5aV_#Knv#15E>-ll$Q-H=<-75cwSm3%5TTLG(HwJ<*BY>eNE{fzy@2q*La zbB>>6e{yy@fRT(Bb_vh{+@szJD9UyANj%jU+4o6K9~Qz^To~pIE4DvB54bTYAn~m* zeB}h2~+Fh1RVzk_t2|NIQWqVHn%&E7bi1x z`5xN*E^dM=H{{_PbbP6y1RI_>q4xT!I^gb)y+c5V_#v%_+3?f~e*a!^lHdss-sP9x zF5bIu!XNeG58(IiA)l@Pkq2+zw>fp0ug}O&=bP-qJ>(MDBDT-*#W;81$@E;erkCmQ zcykB&Mj|!F{dC?Okei7o(Q>q|{-GOwQue{_nv$1oj0#dl@iIPiT&3f z^V3(>-|QdWc>DcOA)>VrqEXm8>?5EBiT1F+vJDeWAs=rwZ%-n}n*xKu0ZfWrx+c98 z{3+=ZDfr5jhJkQ9drRB!Wc2UFpA~NVMnq3j8r7@#~P*?-BKP z699@9MNNN~BK;m?64+6IF+)JnM!^5(KpfmrN74FDNbA3kxLn1>i1h>xxHvGxAr}XM zS&OuY6V^!h8T5tLU_fMK(xn3sG$MM2{Uo9&4~RngOBG0*frmk(5cWgvzZT;(l`bH( zA^eqoE$>#bWL$+9*F#W0;F8iK>_j??yL?&drg~&?Q%Q)FF?#X`-u z;PSglO_zJ=3|!{3ZsH7FK0mz6$hMCw&evJEi`Ir2H@o2A{^1gc#TI`e!{yC44EP+c z#upmu_+zK}hMt|l-IQO7sF+IYztD^|L$ExLeKFUS3UGWnF{aen_N$DD4}Hmt@*-ELETvr%hW&9c2*x0*JUtL@fbfg}g4 zT&+>-CN?XSG?J^y!jfUGCRTKJ4%VB;L*7icGxz8Bt0S(t5%I_|8S6YY=O0PzcC}1< zD{pPl&d!d4+o1SP@SWuGe>|@HAn_;I8Q_2PI_138ZP!>V2(6$`t5vCV>~4qhwQ{@F zX;r%Fbn&X1$r(A-s9KNhwwi1;HflH(-%LOBAW(!MXrc22}p_t-#E}+l6lpxu CJ{e2^ diff --git a/app/shaders/d3d11_yuv420_pixel.fxc b/app/shaders/d3d11_yuv420_pixel.fxc new file mode 100644 index 0000000000000000000000000000000000000000..4285c1a9ee3614235b59934539a9dbbf11ad9153 GIT binary patch literal 1740 zcma)6OKTHR6h4zl(%1?KUAYh(M5IEIwiZDYJIN$z0%=n-Q;833$C#K%UL+}WqaFGK zq&v59-AxxRUAS}OqAT$S1T47GE~J7GzjNnKI)*NK%9(q&Ks8&lDp15+<6!dJo!$qo)%6gi?@b2C+S8yw$ujix?{fhQlK)Nj7O#T1nW~ zq)nvYkH!7EYsm8<{{c}bOwm4(vm}V}bA?O7Nu(Y~dygW~zGo}2LBMn>ID>gFa&gSV2qy#!Lay!;YdKI$SHDPjIdi)Ccb)B3E$&zzXWQI3O^5 z5(foVf!$gWC#;ieXV4e9!y%EGNfrNspcBy!^i@Pr9^q8eZ#5wIOgs!4g`khQ|7MKC zRJ?>6XuJ0M`YC2E@Hj6Bz2)b4 zT*=`AEGeSNV>+#$mb~k)-TvOpeje8!^?c*b;PGzb$xZv}O-h?{AX6P5P6vE_cQq%j zyJx3@h^kZ5nlCQ7qBppF96h<@j8qI)2|-wwcb(;r+^+A}S(Q!Iy5(NiJpnr}(CI8(#g&y>snhDVDm`tca6`-F%$#Oc z?HA>aX0#gZ8qQRkpSw9vS#x=Q6Dgd@bFgIGMclKuyQuW>V)<;2^Vq5+nh!VD@@Ce` m=2yu|6^(qpkmULD=(8xfUZ0t k_VideoShaderNames = { - "d3d11_genyuv_pixel.fxc", - "d3d11_bt601lim_pixel.fxc", - "d3d11_bt2020lim_pixel.fxc", + "d3d11_yuv420_pixel.fxc", "d3d11_ayuv_pixel.fxc", "d3d11_y410_pixel.fxc", }; @@ -541,6 +545,8 @@ bool D3D11VARenderer::initialize(PDECODER_PARAMETERS params) D3D11_TEXTURE2D_DESC textureDesc; d3d11vaFramesContext->texture_infos->texture->GetDesc(&textureDesc); m_TextureFormat = textureDesc.Format; + m_TextureWidth = textureDesc.Width; + m_TextureHeight = textureDesc.Height; if (m_BindDecoderOutputTextures) { // Create SRVs for all textures in the decoder pool @@ -705,107 +711,130 @@ void D3D11VARenderer::bindColorConversion(AVFrame* frame) bool yuv444 = (m_DecoderParams.videoFormat & VIDEO_FORMAT_MASK_YUV444); int bits = (colorspace == COLORSPACE_REC_2020) ? 10 : 8; - // We have purpose-built shaders for the common Rec 601 (SDR) and Rec 2020 (HDR) YUV 4:2:0 cases - if (!yuv444 && !fullRange && colorspace == COLORSPACE_REC_601) { - m_DeviceContext->PSSetShader(m_VideoPixelShaders[PixelShaders::BT_601_LIMITED_YUV_420].Get(), nullptr, 0); - } - else if (!yuv444 && !fullRange && colorspace == COLORSPACE_REC_2020) { - m_DeviceContext->PSSetShader(m_VideoPixelShaders[PixelShaders::BT_2020_LIMITED_YUV_420].Get(), nullptr, 0); - } - else { - if (yuv444) { - // We'll need to use one of the 4:4:4 shaders for this pixel format - switch (m_TextureFormat) - { - case DXGI_FORMAT_AYUV: - m_DeviceContext->PSSetShader(m_VideoPixelShaders[PixelShaders::GENERIC_AYUV].Get(), nullptr, 0); - break; - case DXGI_FORMAT_Y410: - m_DeviceContext->PSSetShader(m_VideoPixelShaders[PixelShaders::GENERIC_Y410].Get(), nullptr, 0); - break; - default: - SDL_assert(false); - } - } - else { - // We'll need to use the generic 4:2:0 shader for this colorspace and color range combo - m_DeviceContext->PSSetShader(m_VideoPixelShaders[PixelShaders::GENERIC_YUV_420].Get(), nullptr, 0); - } - - // If nothing has changed since last frame, we're done - if (colorspace == m_LastColorSpace && fullRange == m_LastFullRange) { - return; - } - - if (!yuv444) { - SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, - "Falling back to generic video pixel shader for %d (%s range)", - colorspace, - fullRange ? "full" : "limited"); - } - - D3D11_BUFFER_DESC constDesc = {}; - constDesc.ByteWidth = sizeof(CSC_CONST_BUF); - constDesc.Usage = D3D11_USAGE_IMMUTABLE; - constDesc.BindFlags = D3D11_BIND_CONSTANT_BUFFER; - constDesc.CPUAccessFlags = 0; - constDesc.MiscFlags = 0; - - CSC_CONST_BUF constBuf = {}; - const float* rawCscMatrix; - switch (colorspace) { - case COLORSPACE_REC_601: - rawCscMatrix = k_CscMatrix_Bt601; + if (yuv444) { + // We'll need to use one of the 4:4:4 shaders for this pixel format + switch (m_TextureFormat) + { + case DXGI_FORMAT_AYUV: + m_DeviceContext->PSSetShader(m_VideoPixelShaders[PixelShaders::GENERIC_AYUV].Get(), nullptr, 0); break; - case COLORSPACE_REC_709: - rawCscMatrix = k_CscMatrix_Bt709; - break; - case COLORSPACE_REC_2020: - rawCscMatrix = k_CscMatrix_Bt2020; + case DXGI_FORMAT_Y410: + m_DeviceContext->PSSetShader(m_VideoPixelShaders[PixelShaders::GENERIC_Y410].Get(), nullptr, 0); break; default: SDL_assert(false); - return; } + } + else { + // We'll need to use the generic 4:2:0 shader for this colorspace and color range combo + m_DeviceContext->PSSetShader(m_VideoPixelShaders[PixelShaders::GENERIC_YUV_420].Get(), nullptr, 0); + } - int range = (1 << bits); - double yMin = (fullRange ? 0 : (16 << (bits - 8))); - double yMax = (fullRange ? (range - 1) : (235 << (bits - 8))); - double yScale = (range - 1) / (yMax - yMin); - double uvMin = (fullRange ? 0 : (16 << (bits - 8))); - double uvMax = (fullRange ? (range - 1) : (240 << (bits - 8))); - double uvScale = (range - 1) / (uvMax - uvMin); + // If nothing has changed since last frame, we're done + if (colorspace == m_LastColorSpace && fullRange == m_LastFullRange) { + return; + } - // Calculate YUV offsets - constBuf.offsets[0] = yMin / (double)(range - 1); - constBuf.offsets[1] = (range / 2) / (double)(range - 1); - constBuf.offsets[2] = (range / 2) / (double)(range - 1); + D3D11_BUFFER_DESC constDesc = {}; + constDesc.ByteWidth = sizeof(CSC_CONST_BUF); + constDesc.Usage = D3D11_USAGE_IMMUTABLE; + constDesc.BindFlags = D3D11_BIND_CONSTANT_BUFFER; + constDesc.CPUAccessFlags = 0; + constDesc.MiscFlags = 0; - // We need to adjust our raw CSC matrix to be column-major and with float3 vectors - // padded with a float in between each of them to adhere to HLSL requirements. - for (int i = 0; i < 3; i++) { - for (int j = 0; j < 3; j++) { - constBuf.cscMatrix[i * 4 + j] = rawCscMatrix[j * 3 + i]; + CSC_CONST_BUF constBuf = {}; + const float* rawCscMatrix; + switch (colorspace) { + case COLORSPACE_REC_601: + rawCscMatrix = k_CscMatrix_Bt601; + break; + case COLORSPACE_REC_709: + rawCscMatrix = k_CscMatrix_Bt709; + break; + case COLORSPACE_REC_2020: + rawCscMatrix = k_CscMatrix_Bt2020; + break; + default: + SDL_assert(false); + return; + } - // Scale the color matrix according to the color range - constBuf.cscMatrix[i * 4 + j] *= (j == 0) ? yScale : uvScale; - } + int range = (1 << bits); + double yMin = (fullRange ? 0 : (16 << (bits - 8))); + double yMax = (fullRange ? (range - 1) : (235 << (bits - 8))); + double yScale = (range - 1) / (yMax - yMin); + double uvMin = (fullRange ? 0 : (16 << (bits - 8))); + double uvMax = (fullRange ? (range - 1) : (240 << (bits - 8))); + double uvScale = (range - 1) / (uvMax - uvMin); + + // Calculate YUV offsets + constBuf.offsets[0] = yMin / (double)(range - 1); + constBuf.offsets[1] = (range / 2) / (double)(range - 1); + constBuf.offsets[2] = (range / 2) / (double)(range - 1); + + // We need to adjust our raw CSC matrix to be column-major and with float3 vectors + // padded with a float in between each of them to adhere to HLSL requirements. + for (int i = 0; i < 3; i++) { + for (int j = 0; j < 3; j++) { + constBuf.cscMatrix[i * 4 + j] = rawCscMatrix[j * 3 + i]; + + // Scale the color matrix according to the color range + constBuf.cscMatrix[i * 4 + j] *= (j == 0) ? yScale : uvScale; } + } - D3D11_SUBRESOURCE_DATA constData = {}; - constData.pSysMem = &constBuf; + switch (frame->chroma_location) { + default: + case AVCHROMA_LOC_LEFT: + constBuf.chromaOffset[0] = 0; + constBuf.chromaOffset[1] = 0.5; + break; + case AVCHROMA_LOC_CENTER: + constBuf.chromaOffset[0] = 0.5; + constBuf.chromaOffset[1] = 0.5; + break; + case AVCHROMA_LOC_TOPLEFT: + constBuf.chromaOffset[0] = 0; + constBuf.chromaOffset[1] = 0; + break; + case AVCHROMA_LOC_TOP: + constBuf.chromaOffset[0] = 0.5; + constBuf.chromaOffset[1] = 0; + break; + case AVCHROMA_LOC_BOTTOMLEFT: + constBuf.chromaOffset[0] = 0; + constBuf.chromaOffset[1] = 1.0; + break; + case AVCHROMA_LOC_BOTTOM: + constBuf.chromaOffset[0] = 0.5; + constBuf.chromaOffset[1] = 1.0; + break; + } - ComPtr constantBuffer; - HRESULT hr = m_Device->CreateBuffer(&constDesc, &constData, &constantBuffer); - if (SUCCEEDED(hr)) { - m_DeviceContext->PSSetConstantBuffers(1, 1, constantBuffer.GetAddressOf()); - } - else { - SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, - "ID3D11Device::CreateBuffer() failed: %x", - hr); - return; - } + if (yuv444) { + // 4:4:4 has no subsampling + constBuf.chromaOffset[0] = 0; + constBuf.chromaOffset[1] = 0; + } + else { + // 4:2:0 has 2x2 subsampling + constBuf.chromaOffset[0] /= m_TextureWidth / 2; + constBuf.chromaOffset[1] /= m_TextureHeight / 2; + } + + D3D11_SUBRESOURCE_DATA constData = {}; + constData.pSysMem = &constBuf; + + ComPtr constantBuffer; + HRESULT hr = m_Device->CreateBuffer(&constDesc, &constData, &constantBuffer); + if (SUCCEEDED(hr)) { + m_DeviceContext->PSSetConstantBuffers(1, 1, constantBuffer.GetAddressOf()); + } + else { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, + "ID3D11Device::CreateBuffer() failed: %x", + hr); + return; } m_LastColorSpace = colorspace; diff --git a/app/streaming/video/ffmpeg-renderers/d3d11va.h b/app/streaming/video/ffmpeg-renderers/d3d11va.h index f37b815a..75da76bc 100644 --- a/app/streaming/video/ffmpeg-renderers/d3d11va.h +++ b/app/streaming/video/ffmpeg-renderers/d3d11va.h @@ -28,8 +28,6 @@ public: enum PixelShaders { GENERIC_YUV_420, - BT_601_LIMITED_YUV_420, - BT_2020_LIMITED_YUV_420, GENERIC_AYUV, GENERIC_Y410, _COUNT @@ -72,6 +70,8 @@ private: DECODER_PARAMETERS m_DecoderParams; int m_TextureAlignment; DXGI_FORMAT m_TextureFormat; + UINT m_TextureWidth; + UINT m_TextureHeight; int m_DisplayWidth; int m_DisplayHeight; int m_LastColorSpace;