master ohmu/fastfetch-openbsd / src / detection / codec / codec_windows.cpp
ohmu  ·  2026-10-15
  1extern "C" {
  2#include "codec.h"
  3#include "common/debug.h"
  4#include "common/library.h"
  5#include "common/windows/com.h"
  6#include "common/windows/unicode.h"
  7#include "common/windows/nt.h"
  8}
  9
 10#include <d3d11.h>
 11#include <d3d12.h>
 12#include <dxgi.h>
 13#include <initguid.h>
 14#include <dxva.h>
 15#include <d3d12video.h>
 16#include <mfapi.h>
 17
 18typedef struct D3D12_FEATURE_DATA_VIDEO_ENCODER_CODEC {
 19    UINT NodeIndex;
 20    D3D12_VIDEO_ENCODER_CODEC Codec;
 21    BOOL IsSupported;
 22} D3D12_FEATURE_DATA_VIDEO_ENCODER_CODEC;
 23
 24HRESULT MFTEnum2(
 25    _In_ GUID guidCategory,
 26    _In_ UINT32 Flags,
 27    _In_ const MFT_REGISTER_TYPE_INFO* pInputType,
 28    _In_ const MFT_REGISTER_TYPE_INFO* pOutputType,
 29    _In_opt_ IMFAttributes* pAttributes,
 30    _Out_ IMFActivate*** pppMFTActivate,
 31    _Out_ UINT32* pnumMFTActivate);
 32
 33// clang-format off
 34#ifndef MFT_ENUM_ADAPTER_LUID
 35// {1D39518C-E220-4DA8-A07F-BA172552D6B1}
 36DEFINE_GUID(MFT_ENUM_ADAPTER_LUID,
 37    0x1d39518c, 0xe220, 0x4da8, 0xa0, 0x7f, 0xba, 0x17, 0x25, 0x52, 0xd6, 0xb1);
 38#endif
 39// clang-format on
 40
 41static const DXGI_FORMAT FF_NATIVE_CODEC_FORMATS[] = {
 42    DXGI_FORMAT_420_OPAQUE,
 43    DXGI_FORMAT_NV12,
 44    DXGI_FORMAT_P010,
 45    DXGI_FORMAT_P016,
 46    DXGI_FORMAT_YUY2,
 47    DXGI_FORMAT_Y210,
 48    DXGI_FORMAT_Y216,
 49    DXGI_FORMAT_AYUV,
 50    DXGI_FORMAT_Y410,
 51    DXGI_FORMAT_Y416,
 52};
 53
 54static FFCodecType ffCodecProfileToTypeDx11(const GUID& profile) {
 55    // clang-format off
 56    if (IsEqualGUID(profile, DXVA_ModeH261_A) ||
 57        IsEqualGUID(profile, DXVA_ModeH261_B)) {
 58        return FF_CODEC_TYPE_H261;
 59    }
 60
 61    if (IsEqualGUID(profile, DXVA_ModeH263_A) ||
 62        IsEqualGUID(profile, DXVA_ModeH263_B) ||
 63        IsEqualGUID(profile, DXVA_ModeH263_C) ||
 64        IsEqualGUID(profile, DXVA_ModeH263_D) ||
 65        IsEqualGUID(profile, DXVA_ModeH263_E) ||
 66        IsEqualGUID(profile, DXVA_ModeH263_F)) {
 67        return FF_CODEC_TYPE_H263;
 68    }
 69
 70    if (IsEqualGUID(profile, DXVA_ModeMJPEG_VLD_420) ||
 71        IsEqualGUID(profile, DXVA_ModeMJPEG_VLD_422) ||
 72        IsEqualGUID(profile, DXVA_ModeMJPEG_VLD_444) ||
 73        IsEqualGUID(profile, DXVA_ModeMJPEG_VLD_4444) ||
 74        IsEqualGUID(profile, DXVA_ModeJPEG_VLD_420) ||
 75        IsEqualGUID(profile, DXVA_ModeJPEG_VLD_422) ||
 76        IsEqualGUID(profile, DXVA_ModeJPEG_VLD_444)) {
 77        return FF_CODEC_TYPE_MJPEG;
 78    }
 79
 80    if (IsEqualGUID(profile, DXVA_ModeMPEG1_A) ||
 81        IsEqualGUID(profile, DXVA_ModeMPEG1_VLD)) {
 82        return FF_CODEC_TYPE_MPEG1;
 83    }
 84
 85    if (IsEqualGUID(profile, DXVA_ModeMPEG2_A) ||
 86        IsEqualGUID(profile, DXVA_ModeMPEG2_B) ||
 87        IsEqualGUID(profile, DXVA_ModeMPEG2_C) ||
 88        IsEqualGUID(profile, DXVA_ModeMPEG2_D) ||
 89        IsEqualGUID(profile, DXVA_ModeMPEG2and1_VLD)) {
 90        return FF_CODEC_TYPE_MPEG2;
 91    }
 92
 93    if (IsEqualGUID(profile, DXVA_ModeMPEG4pt2_VLD_Simple) ||
 94        IsEqualGUID(profile, DXVA_ModeMPEG4pt2_VLD_AdvSimple_NoGMC) ||
 95        IsEqualGUID(profile, DXVA_ModeMPEG4pt2_VLD_AdvSimple_GMC)) {
 96        return FF_CODEC_TYPE_DIVX_XVID;
 97    }
 98
 99    if (IsEqualGUID(profile, DXVA_ModeH264_E) ||
100        IsEqualGUID(profile, DXVA_ModeH264_F) ||
101        IsEqualGUID(profile, DXVA_ModeH264_VLD_WithFMOASO_NoFGT) ||
102        IsEqualGUID(profile, DXVA_ModeH264_VLD_Stereo_Progressive_NoFGT) ||
103        IsEqualGUID(profile, DXVA_ModeH264_VLD_Stereo_NoFGT) ||
104        IsEqualGUID(profile, DXVA_ModeH264_VLD_Multiview_NoFGT)) {
105        return FF_CODEC_TYPE_H264;
106    }
107
108    if (IsEqualGUID(profile, DXVA_ModeWMV8_A) ||
109        IsEqualGUID(profile, DXVA_ModeWMV8_B)) {
110        return FF_CODEC_TYPE_WMV8;
111    }
112
113    if (IsEqualGUID(profile, DXVA_ModeWMV9_A) ||
114        IsEqualGUID(profile, DXVA_ModeWMV9_B) ||
115        IsEqualGUID(profile, DXVA_ModeWMV9_C)) {
116        return FF_CODEC_TYPE_WMV9;
117    }
118
119    if (IsEqualGUID(profile, DXVA_ModeVC1_A) ||
120        IsEqualGUID(profile, DXVA_ModeVC1_B) ||
121        IsEqualGUID(profile, DXVA_ModeVC1_C) ||
122        IsEqualGUID(profile, DXVA_ModeVC1_D) ||
123        IsEqualGUID(profile, DXVA_ModeVC1_D2010)) {
124        return FF_CODEC_TYPE_VC1;
125    }
126
127    if (IsEqualGUID(profile, DXVA_ModeVP8_VLD)) {
128        return FF_CODEC_TYPE_VP8;
129    }
130
131    if (IsEqualGUID(profile, DXVA_ModeHEVC_VLD_Main) ||
132        IsEqualGUID(profile, DXVA_ModeHEVC_VLD_Main10) ||
133        IsEqualGUID(profile, DXVA_ModeHEVC_VLD_Main12) ||
134        IsEqualGUID(profile, DXVA_ModeHEVC_VLD_Main10_422) ||
135        IsEqualGUID(profile, DXVA_ModeHEVC_VLD_Main12_422) ||
136        IsEqualGUID(profile, DXVA_ModeHEVC_VLD_Main_444) ||
137        IsEqualGUID(profile, DXVA_ModeHEVC_VLD_Main10_Ext) ||
138        IsEqualGUID(profile, DXVA_ModeHEVC_VLD_Main10_444) ||
139        IsEqualGUID(profile, DXVA_ModeHEVC_VLD_Main12_444) ||
140        IsEqualGUID(profile, DXVA_ModeHEVC_VLD_Main16)) {
141        return FF_CODEC_TYPE_HEVC;
142    }
143
144    if (IsEqualGUID(profile, DXVA_ModeVP9_VLD_Profile0) ||
145        IsEqualGUID(profile, DXVA_ModeVP9_VLD_10bit_Profile2)) {
146        return FF_CODEC_TYPE_VP9;
147    }
148
149    if (IsEqualGUID(profile, DXVA_ModeAV1_VLD_Profile0) ||
150        IsEqualGUID(profile, DXVA_ModeAV1_VLD_Profile1) ||
151        IsEqualGUID(profile, DXVA_ModeAV1_VLD_Profile2) ||
152        IsEqualGUID(profile, DXVA_ModeAV1_VLD_12bit_Profile2) ||
153        IsEqualGUID(profile, DXVA_ModeAV1_VLD_12bit_Profile2_420)) {
154        return FF_CODEC_TYPE_AV1;
155    }
156    // clang-format on
157
158    return FF_CODEC_TYPE_UNKNOWN;
159}
160
161static FFCodecType ffCodecProfileToTypeDx12(const GUID& profile) {
162    // clang-format off
163    if (IsEqualGUID(profile, D3D12_VIDEO_DECODE_PROFILE_MPEG1_AND_MPEG2)) {
164        return FFCodecType(FF_CODEC_TYPE_MPEG1 | FF_CODEC_TYPE_MPEG2);
165    }
166
167    if (IsEqualGUID(profile, D3D12_VIDEO_DECODE_PROFILE_MPEG2)) {
168        return FF_CODEC_TYPE_MPEG2;
169    }
170
171    if (IsEqualGUID(profile, D3D12_VIDEO_DECODE_PROFILE_MPEG4PT2_SIMPLE) ||
172        IsEqualGUID(profile, D3D12_VIDEO_DECODE_PROFILE_MPEG4PT2_ADVSIMPLE_NOGMC)) {
173        return FF_CODEC_TYPE_DIVX_XVID;
174    }
175
176    if (IsEqualGUID(profile, D3D12_VIDEO_DECODE_PROFILE_H264) ||
177        IsEqualGUID(profile, D3D12_VIDEO_DECODE_PROFILE_H264_STEREO_PROGRESSIVE) ||
178        IsEqualGUID(profile, D3D12_VIDEO_DECODE_PROFILE_H264_STEREO) ||
179        IsEqualGUID(profile, D3D12_VIDEO_DECODE_PROFILE_H264_MULTIVIEW)) {
180        return FF_CODEC_TYPE_H264;
181    }
182
183    if (IsEqualGUID(profile, D3D12_VIDEO_DECODE_PROFILE_VC1) ||
184        IsEqualGUID(profile, D3D12_VIDEO_DECODE_PROFILE_VC1_D2010)) {
185        return FF_CODEC_TYPE_VC1;
186    }
187
188    if (IsEqualGUID(profile, D3D12_VIDEO_DECODE_PROFILE_VP8)) {
189        return FF_CODEC_TYPE_VP8;
190    }
191
192    if (IsEqualGUID(profile, D3D12_VIDEO_DECODE_PROFILE_HEVC_MAIN) ||
193        IsEqualGUID(profile, D3D12_VIDEO_DECODE_PROFILE_HEVC_MAIN10)) {
194        return FF_CODEC_TYPE_HEVC;
195    }
196
197    if (IsEqualGUID(profile, D3D12_VIDEO_DECODE_PROFILE_VP9) ||
198        IsEqualGUID(profile, D3D12_VIDEO_DECODE_PROFILE_VP9_10BIT_PROFILE2)) {
199        return FF_CODEC_TYPE_VP9;
200    }
201
202    if (IsEqualGUID(profile, D3D12_VIDEO_DECODE_PROFILE_AV1_PROFILE0) ||
203        IsEqualGUID(profile, D3D12_VIDEO_DECODE_PROFILE_AV1_PROFILE1) ||
204        IsEqualGUID(profile, D3D12_VIDEO_DECODE_PROFILE_AV1_PROFILE2) ||
205        IsEqualGUID(profile, D3D12_VIDEO_DECODE_PROFILE_AV1_12BIT_PROFILE2) ||
206        IsEqualGUID(profile, D3D12_VIDEO_DECODE_PROFILE_AV1_12BIT_PROFILE2_420)) {
207        return FF_CODEC_TYPE_AV1;
208    }
209    // clang-format on
210
211    return FF_CODEC_TYPE_UNKNOWN;
212}
213
214static bool ffCodecProfileHasNativeOutput(ID3D11VideoDevice* videoDevice, const GUID& profile) {
215    for (uint32_t i = 0; i < ARRAY_SIZE(FF_NATIVE_CODEC_FORMATS); ++i) {
216        BOOL supported = FALSE;
217        if (SUCCEEDED(videoDevice->CheckVideoDecoderFormat(&profile, FF_NATIVE_CODEC_FORMATS[i], &supported)) && supported) {
218            return true;
219        }
220    }
221
222    return false;
223}
224
225static bool ffCodecProfileHasNativeOutput(ID3D12VideoDevice* videoDevice, const GUID& profile, UINT nodeIndex) {
226    for (uint32_t i = 0; i < ARRAY_SIZE(FF_NATIVE_CODEC_FORMATS); ++i) {
227        D3D12_FEATURE_DATA_VIDEO_DECODE_SUPPORT support = {
228            .NodeIndex = nodeIndex,
229            .Configuration = {
230                .DecodeProfile = profile,
231                .BitstreamEncryption = D3D12_BITSTREAM_ENCRYPTION_TYPE_NONE,
232                .InterlaceType = D3D12_VIDEO_FRAME_CODED_INTERLACE_TYPE_NONE,
233            },
234            .Width = 1920,
235            .Height = 1080,
236            .DecodeFormat = FF_NATIVE_CODEC_FORMATS[i],
237            .FrameRate = {
238                .Numerator = 30,
239                .Denominator = 1,
240            },
241            .BitRate = 10000000,
242            .SupportFlags = D3D12_VIDEO_DECODE_SUPPORT_FLAG_NONE,
243            .ConfigurationFlags = D3D12_VIDEO_DECODE_CONFIGURATION_FLAG_NONE,
244            .DecodeTier = D3D12_VIDEO_DECODE_TIER_NOT_SUPPORTED,
245        };
246
247        if (SUCCEEDED(videoDevice->CheckFeatureSupport(D3D12_FEATURE_VIDEO_DECODE_SUPPORT, &support, sizeof(support))) &&
248            (support.SupportFlags & D3D12_VIDEO_DECODE_SUPPORT_FLAG_SUPPORTED)) {
249            return true;
250        }
251    }
252
253    return false;
254}
255
256static bool ffCodecEncoderSupportedD3d12(ID3D12VideoDevice* videoDevice, D3D12_VIDEO_ENCODER_CODEC codec, UINT nodeIndex) {
257    D3D12_FEATURE_DATA_VIDEO_ENCODER_CODEC encoderCodec = {
258        .NodeIndex = nodeIndex,
259        .Codec = codec,
260        .IsSupported = FALSE,
261    };
262
263    return SUCCEEDED(videoDevice->CheckFeatureSupport(
264               D3D12_FEATURE_VIDEO_ENCODER_CODEC,
265               &encoderCodec,
266               sizeof(encoderCodec))) &&
267        encoderCodec.IsSupported;
268}
269
270typedef struct FFCodecMftEncoderSubtype {
271    const GUID* subtype;
272    FFCodecType codecType;
273} FFCodecMftEncoderSubtype;
274
275static const FFCodecMftEncoderSubtype FF_D3D11VA_MFT_ENCODER_SUBTYPES[] = {
276    { &MFVideoFormat_H264, FF_CODEC_TYPE_H264 },
277    { &MFVideoFormat_HEVC, FF_CODEC_TYPE_HEVC },
278    { &MFVideoFormat_AV1, FF_CODEC_TYPE_AV1 },
279};
280
281static FFCodecType ffDetectD3d11vaDecoders(IDXGIAdapter1* adapter, typeof(&D3D11CreateDevice) ffD3D11CreateDevice) {
282    FF_AUTO_RELEASE_COM_OBJECT ID3D11Device* d3dDevice = nullptr;
283    D3D_FEATURE_LEVEL featureLevel;
284    if (FAILED(ffD3D11CreateDevice(
285            adapter,
286            D3D_DRIVER_TYPE_UNKNOWN,
287            nullptr,
288            D3D11_CREATE_DEVICE_VIDEO_SUPPORT,
289            nullptr,
290            0,
291            D3D11_SDK_VERSION,
292            &d3dDevice,
293            &featureLevel,
294            nullptr)) ||
295        !d3dDevice) {
296        return FF_CODEC_TYPE_NONE;
297    }
298
299    FF_AUTO_RELEASE_COM_OBJECT ID3D11VideoDevice* videoDevice = nullptr;
300    if (FAILED(d3dDevice->QueryInterface(__uuidof(ID3D11VideoDevice), (void**) &videoDevice)) || !videoDevice) {
301        return FF_CODEC_TYPE_NONE;
302    }
303
304    FFCodecType decoders = FF_CODEC_TYPE_NONE;
305    UINT profileCount = videoDevice->GetVideoDecoderProfileCount();
306    for (UINT profileIndex = 0; profileIndex < profileCount; ++profileIndex) {
307        GUID profile;
308        if (FAILED(videoDevice->GetVideoDecoderProfile(profileIndex, &profile))) {
309            continue;
310        }
311
312        FFCodecType type = ffCodecProfileToTypeDx11(profile);
313
314        if ((decoders & type) || !ffCodecProfileHasNativeOutput(videoDevice, profile)) {
315            continue;
316        }
317
318        decoders = (FFCodecType) (((uint32_t) decoders) | ((uint32_t) type));
319    }
320    return decoders;
321}
322
323static FFCodecType ffDetectD3d11MftEncoders(const LUID& adapterLuid, typeof(&MFCreateAttributes) ffMFCreateAttributes, typeof(&MFTEnum2) ffMFTEnum2) {
324    FF_AUTO_RELEASE_COM_OBJECT IMFAttributes* attributes = nullptr;
325    if (FAILED(ffMFCreateAttributes(&attributes, 1)) || !attributes) {
326        return FF_CODEC_TYPE_NONE;
327    }
328
329    if (FAILED(attributes->SetBlob(MFT_ENUM_ADAPTER_LUID, (const UINT8*) &adapterLuid, sizeof(adapterLuid)))) {
330        return FF_CODEC_TYPE_NONE;
331    }
332
333    FFCodecType encoders = FF_CODEC_TYPE_NONE;
334
335    for (uint32_t subtypeIndex = 0; subtypeIndex < ARRAY_SIZE(FF_D3D11VA_MFT_ENCODER_SUBTYPES); ++subtypeIndex) {
336        const FFCodecMftEncoderSubtype& subtype = FF_D3D11VA_MFT_ENCODER_SUBTYPES[subtypeIndex];
337        if (encoders & subtype.codecType) {
338            continue;
339        }
340
341        MFT_REGISTER_TYPE_INFO outputType = {
342            .guidMajorType = MFMediaType_Video,
343            .guidSubtype = *subtype.subtype,
344        };
345
346        IMFActivate** activateList = nullptr;
347        UINT32 activateCount = 0;
348
349        if (SUCCEEDED(ffMFTEnum2(
350                MFT_CATEGORY_VIDEO_ENCODER,
351                MFT_ENUM_FLAG_HARDWARE | MFT_ENUM_FLAG_SORTANDFILTER,
352                nullptr,
353                &outputType,
354                attributes,
355                &activateList,
356                &activateCount)) &&
357            activateCount > 0) {
358            encoders = (FFCodecType) (((uint32_t) encoders) | ((uint32_t) subtype.codecType));
359        }
360
361        for (uint32_t i = 0; i < activateCount; ++i) {
362            activateList[i]->Release();
363        }
364        CoTaskMemFree(activateList);
365    }
366
367    return encoders;
368}
369
370static FFCodecType ffCodecEncoderToType(D3D12_VIDEO_ENCODER_CODEC codec) {
371    switch (codec) {
372        case D3D12_VIDEO_ENCODER_CODEC_H264:
373            return FF_CODEC_TYPE_H264;
374        case D3D12_VIDEO_ENCODER_CODEC_HEVC:
375            return FF_CODEC_TYPE_HEVC;
376        case D3D12_VIDEO_ENCODER_CODEC_AV1:
377            return FF_CODEC_TYPE_AV1;
378        default:
379            return FF_CODEC_TYPE_UNKNOWN;
380    }
381}
382
383template <typename Func>
384static void ffEnumHardwareAdapters(IDXGIFactory1* factory, Func&& onAdapter) {
385    for (UINT adapterIndex = 0;; ++adapterIndex) {
386        FF_AUTO_RELEASE_COM_OBJECT IDXGIAdapter1* adapter = nullptr;
387        HRESULT adapterStatus = factory->EnumAdapters1(adapterIndex, &adapter);
388        if (adapterStatus == DXGI_ERROR_NOT_FOUND) {
389            break;
390        }
391        if (FAILED(adapterStatus) || !adapter) {
392            continue;
393        }
394
395        DXGI_ADAPTER_DESC1 desc;
396        if (FAILED(adapter->GetDesc1(&desc))) {
397            continue;
398        }
399        if (desc.Flags & DXGI_ADAPTER_FLAG_SOFTWARE) {
400            continue;
401        }
402
403        onAdapter(adapter, desc);
404    }
405}
406
407const char* detectD3d11va(FFCodecOptions* options, FFlist* result /*list of FFCodecResult*/, IDXGIFactory1* factory) {
408    FF_LIBRARY_LOAD_MESSAGE(d3d11, "d3d11" FF_LIBRARY_EXTENSION, 1)
409    FF_LIBRARY_LOAD_SYMBOL_MESSAGE(d3d11, D3D11CreateDevice)
410    FF_LIBRARY_LOAD_MESSAGE(mfplat, "mfplat" FF_LIBRARY_EXTENSION, 1)
411    FF_LIBRARY_LOAD_SYMBOL_MESSAGE(mfplat, MFCreateAttributes)
412    FF_LIBRARY_LOAD_SYMBOL_LAZY(mfplat, MFTEnum2) // Not available on Windows 8.1
413
414    ffEnumHardwareAdapters(factory, [&](IDXGIAdapter1* adapter, const DXGI_ADAPTER_DESC1& desc) {
415        FFCodecType decoders = (options->showType & FF_CODEC_SHOW_TYPE_DECODER)
416            ? ffDetectD3d11vaDecoders(adapter, ffD3D11CreateDevice)
417            : FF_CODEC_TYPE_NONE;
418        FFCodecType encoders = ffMFTEnum2 && (options->showType & FF_CODEC_SHOW_TYPE_ENCODER)
419            ? ffDetectD3d11MftEncoders(desc.AdapterLuid, ffMFCreateAttributes, ffMFTEnum2)
420            : FF_CODEC_TYPE_NONE;
421
422        if (decoders == FF_CODEC_TYPE_NONE && encoders == FF_CODEC_TYPE_NONE) {
423            return;
424        }
425
426        FFCodecResult* gpuResult = FF_LIST_ADD(FFCodecResult, *result);
427        ffStrbufInitWS(&gpuResult->gpu, desc.Description);
428        gpuResult->decoders = decoders;
429        gpuResult->encoders = encoders;
430        gpuResult->platformApi = ffMFTEnum2 ? "D3D11VA+MFT" : "D3D11VA";
431    });
432
433    return nullptr;
434}
435
436const char* detectD3d12va(FFCodecOptions* options, FFlist* result /*list of FFCodecResult*/, IDXGIFactory1* factory) {
437    FF_LIBRARY_LOAD_MESSAGE(d3d12, "d3d12" FF_LIBRARY_EXTENSION, 1)
438    FF_LIBRARY_LOAD_SYMBOL_MESSAGE(d3d12, D3D12CreateDevice)
439
440    static const GUID FF_D3D12_DECODE_PROFILES[] = {
441        D3D12_VIDEO_DECODE_PROFILE_MPEG2,
442        D3D12_VIDEO_DECODE_PROFILE_MPEG1_AND_MPEG2,
443        D3D12_VIDEO_DECODE_PROFILE_H264,
444        D3D12_VIDEO_DECODE_PROFILE_H264_STEREO_PROGRESSIVE,
445        D3D12_VIDEO_DECODE_PROFILE_H264_STEREO,
446        D3D12_VIDEO_DECODE_PROFILE_H264_MULTIVIEW,
447        D3D12_VIDEO_DECODE_PROFILE_VC1,
448        D3D12_VIDEO_DECODE_PROFILE_VC1_D2010,
449        D3D12_VIDEO_DECODE_PROFILE_MPEG4PT2_SIMPLE,
450        D3D12_VIDEO_DECODE_PROFILE_MPEG4PT2_ADVSIMPLE_NOGMC,
451        D3D12_VIDEO_DECODE_PROFILE_HEVC_MAIN,
452        D3D12_VIDEO_DECODE_PROFILE_HEVC_MAIN10,
453        D3D12_VIDEO_DECODE_PROFILE_VP9,
454        D3D12_VIDEO_DECODE_PROFILE_VP9_10BIT_PROFILE2,
455        D3D12_VIDEO_DECODE_PROFILE_VP8,
456        D3D12_VIDEO_DECODE_PROFILE_AV1_PROFILE0,
457        D3D12_VIDEO_DECODE_PROFILE_AV1_PROFILE1,
458        D3D12_VIDEO_DECODE_PROFILE_AV1_PROFILE2,
459        D3D12_VIDEO_DECODE_PROFILE_AV1_12BIT_PROFILE2,
460        D3D12_VIDEO_DECODE_PROFILE_AV1_12BIT_PROFILE2_420,
461    };
462
463    static const D3D12_VIDEO_ENCODER_CODEC FF_D3D12_ENCODER_CODECS[] = {
464        D3D12_VIDEO_ENCODER_CODEC_H264,
465        D3D12_VIDEO_ENCODER_CODEC_HEVC,
466        D3D12_VIDEO_ENCODER_CODEC_AV1,
467    };
468
469    const uint32_t resultLengthBefore = result->length;
470
471    ffEnumHardwareAdapters(factory, [&](IDXGIAdapter1* adapter, const DXGI_ADAPTER_DESC1& desc) {
472        FF_AUTO_RELEASE_COM_OBJECT ID3D12Device* d3dDevice = nullptr;
473        if (FAILED(ffD3D12CreateDevice(
474                adapter,
475                D3D_FEATURE_LEVEL_11_0,
476                __uuidof(ID3D12Device),
477                (void**) &d3dDevice)) ||
478            !d3dDevice) {
479            return;
480        }
481
482        FF_AUTO_RELEASE_COM_OBJECT ID3D12VideoDevice* videoDevice = nullptr;
483        if (FAILED(d3dDevice->QueryInterface(__uuidof(ID3D12VideoDevice), (void**) &videoDevice)) || !videoDevice) {
484            return;
485        }
486
487        FFCodecType decoders = FF_CODEC_TYPE_NONE;
488        if (options->showType & FF_CODEC_SHOW_TYPE_DECODER) {
489            for (uint32_t profileIndex = 0; profileIndex < ARRAY_SIZE(FF_D3D12_DECODE_PROFILES); ++profileIndex) {
490                const GUID& profile = FF_D3D12_DECODE_PROFILES[profileIndex];
491                FFCodecType codecType = ffCodecProfileToTypeDx12(profile);
492
493                if ((decoders & codecType) || !ffCodecProfileHasNativeOutput(videoDevice, profile, 0)) {
494                    continue;
495                }
496
497                decoders = (FFCodecType) (((uint32_t) decoders) | ((uint32_t) codecType));
498            }
499        }
500
501        FFCodecType encoders = FF_CODEC_TYPE_NONE;
502        if (options->showType & FF_CODEC_SHOW_TYPE_ENCODER) {
503            for (uint32_t codecIndex = 0; codecIndex < ARRAY_SIZE(FF_D3D12_ENCODER_CODECS); ++codecIndex) {
504                D3D12_VIDEO_ENCODER_CODEC codec = FF_D3D12_ENCODER_CODECS[codecIndex];
505                FFCodecType codecType = ffCodecEncoderToType(codec);
506
507                if ((encoders & codecType) || !ffCodecEncoderSupportedD3d12(videoDevice, codec, 0)) {
508                    continue;
509                }
510
511                encoders = (FFCodecType) (((uint32_t) encoders) | ((uint32_t) codecType));
512            }
513        }
514
515        if (decoders == FF_CODEC_TYPE_NONE && encoders == FF_CODEC_TYPE_NONE) {
516            return;
517        }
518
519        FFCodecResult* gpuResult = FF_LIST_ADD(FFCodecResult, *result);
520        ffStrbufInitWS(&gpuResult->gpu, desc.Description);
521        gpuResult->decoders = decoders;
522        gpuResult->encoders = encoders;
523        gpuResult->platformApi = "D3D12VA";
524    });
525
526    if (result->length == resultLengthBefore) {
527        FF_DEBUG("No D3D12 video acceleration support");
528        return "No D3D12 video acceleration support";
529    }
530
531    return nullptr;
532}
533
534const char* ffDetectCodecNative(FFCodecOptions* options, FFlist* result /*list of FFCodecResult*/) {
535    FF_LIBRARY_LOAD_MESSAGE(dxgi, "dxgi" FF_LIBRARY_EXTENSION, 1)
536    FF_LIBRARY_LOAD_SYMBOL_MESSAGE(dxgi, CreateDXGIFactory1)
537
538    const char* error = ffInitCom();
539    if (error) {
540        return error;
541    }
542
543    FF_AUTO_RELEASE_COM_OBJECT IDXGIFactory1* factory = nullptr;
544    HRESULT hr = ffCreateDXGIFactory1(__uuidof(IDXGIFactory1), (void**) &factory);
545    if (FAILED(hr) || !factory) {
546        FF_DEBUG("CreateDXGIFactory1() failed: %s", ffDebugHResult(hr));
547        return "CreateDXGIFactory1() failed";
548    }
549
550    if (ffIsWindows11OrGreater()) {
551        // D3D12 video encoding is supported only on Windows 11
552        if (detectD3d12va(options, result, factory) == nullptr) {
553            return nullptr;
554        }
555    }
556    return detectD3d11va(options, result, factory);
557}