diff --git a/CMakeLists.txt b/CMakeLists.txt index 3b0d879ef..34d6f8f9d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -74,6 +74,7 @@ cmake_dependent_option(ENABLE_WAYLAND "Enable wayland-client" ON "LINUX OR FreeB cmake_dependent_option(ENABLE_XCB_RANDR "Enable xcb-randr" ON "LINUX OR FreeBSD OR OpenBSD OR NetBSD OR ANDROID OR SunOS OR GNU" OFF) cmake_dependent_option(ENABLE_XRANDR "Enable xrandr" ON "LINUX OR FreeBSD OR OpenBSD OR NetBSD OR ANDROID OR SunOS OR GNU" OFF) cmake_dependent_option(ENABLE_DRM "Enable libdrm" ON "LINUX OR FreeBSD OR OpenBSD OR NetBSD OR SunOS OR GNU" OFF) +cmake_dependent_option(ENABLE_VA "Enable libva" ON "LINUX OR FreeBSD OR OpenBSD OR NetBSD OR SunOS OR GNU" OFF) cmake_dependent_option(ENABLE_DRM_AMDGPU "Enable libdrm_amdgpu" ON "LINUX OR FreeBSD OR GNU" OFF) cmake_dependent_option(ENABLE_GIO "Enable gio-2.0" ON "LINUX OR FreeBSD OR OpenBSD OR NetBSD OR ANDROID OR SunOS OR GNU" OFF) cmake_dependent_option(ENABLE_DCONF "Enable dconf" ON "LINUX OR FreeBSD OR OpenBSD OR NetBSD OR ANDROID OR SunOS OR GNU" OFF) @@ -553,6 +554,7 @@ if(LINUX) src/detection/cpucache/cpucache_linux.c src/detection/cpuusage/cpuusage_linux.c src/detection/cursor/cursor_linux.c + src/detection/decoder/decoder_linux.c src/detection/bluetooth/bluetooth_linux.c src/detection/bluetoothradio/bluetoothradio_linux.c src/detection/disk/disk_linux.c @@ -714,6 +716,7 @@ elseif(FreeBSD) src/detection/cpucache/cpucache_shared.c src/detection/cpuusage/cpuusage_bsd.c src/detection/cursor/cursor_linux.c + src/detection/decoder/decoder_linux.c src/detection/disk/disk_bsd.c src/detection/dns/dns_linux.c src/detection/physicaldisk/physicaldisk_bsd.c @@ -808,6 +811,7 @@ elseif(NetBSD) src/detection/cpucache/cpucache_shared.c src/detection/cpuusage/cpuusage_bsd.c src/detection/cursor/cursor_linux.c + src/detection/decoder/decoder_linux.c src/detection/disk/disk_bsd.c src/detection/dns/dns_linux.c src/detection/physicaldisk/physicaldisk_nbsd.c @@ -892,6 +896,7 @@ elseif(OpenBSD) src/detection/cpucache/cpucache_shared.c src/detection/cpuusage/cpuusage_bsd.c src/detection/cursor/cursor_linux.c + src/detection/decoder/decoder_linux.c src/detection/disk/disk_bsd.c src/detection/dns/dns_linux.c src/detection/physicaldisk/physicaldisk_obsd.c @@ -1133,6 +1138,7 @@ elseif(SunOS) src/detection/cursor/cursor_linux.c src/detection/bluetooth/bluetooth_nosupport.c src/detection/bluetoothradio/bluetoothradio_nosupport.c + src/detection/decoder/decoder_linux.c src/detection/disk/disk_sunos.c src/detection/dns/dns_linux.c src/detection/physicaldisk/physicaldisk_sunos.c @@ -1216,6 +1222,7 @@ elseif(Haiku) src/detection/cursor/cursor_nosupport.c src/detection/bluetooth/bluetooth_haiku.cpp src/detection/bluetoothradio/bluetoothradio_nosupport.c + src/detection/decoder/decoder_linux.c src/detection/disk/disk_haiku.cpp src/detection/dns/dns_linux.c src/detection/physicaldisk/physicaldisk_haiku.c @@ -1286,6 +1293,7 @@ elseif(GNU) src/detection/bluetooth/bluetooth_linux.c src/detection/bluetoothradio/bluetoothradio_linux.c src/detection/disk/disk_linux.c + src/detection/decoder/decoder_linux.c src/detection/dns/dns_linux.c src/detection/physicaldisk/physicaldisk_nosupport.c src/detection/physicalmemory/physicalmemory_nosupport.c @@ -1656,6 +1664,10 @@ ff_lib_enable(DRM "libdrm" "Libdrm" ) +ff_lib_enable(VA + "libva" + "Libva" +) ff_lib_enable(DRM_AMDGPU "libdrm_amdgpu" "Libdrm_Amdgpu" diff --git a/src/detection/decoder/decoder.h b/src/detection/decoder/decoder.h index bd62077ca..656ccd286 100644 --- a/src/detection/decoder/decoder.h +++ b/src/detection/decoder/decoder.h @@ -20,7 +20,8 @@ typedef enum FF_A_PACKED FFDecoderType { FF_DECODER_TYPE_HEVC = UINT32_C(1) << 12, FF_DECODER_TYPE_VP9 = UINT32_C(1) << 13, FF_DECODER_TYPE_AV1 = UINT32_C(1) << 14, - FF_DECODER_TYPE_MAX = FF_DECODER_TYPE_AV1, + FF_DECODER_TYPE_VVC = UINT32_C(1) << 15, + FF_DECODER_TYPE_MAX = FF_DECODER_TYPE_VVC, FF_DECODER_TYPE_FORCE_UNSIGNED = UINT32_MAX, } FFDecoderType; diff --git a/src/detection/decoder/decoder_linux.c b/src/detection/decoder/decoder_linux.c new file mode 100644 index 000000000..401d7c867 --- /dev/null +++ b/src/detection/decoder/decoder_linux.c @@ -0,0 +1,282 @@ +#include "decoder.h" + +#if FF_HAVE_DRM && FF_HAVE_VA + +#include "detection/gpu/gpu.h" +#include "common/io.h" +#include "common/library.h" +#include "common/mallocHelper.h" + +#include +#include +#include +#include +#include + +static FFDecoderType ffDecoderProfileToType(VAProfile profile) { + switch (profile) { + case 11: // VAProfileH263Baseline + return FF_DECODER_TYPE_H263; + + case 12: // VAProfileJPEGBaseline + return FF_DECODER_TYPE_MJPEG; + + case 0: // VAProfileMPEG2Simple + case 1: // VAProfileMPEG2Main + return FF_DECODER_TYPE_MPEG2; + + case 2: // VAProfileMPEG4Simple + case 3: // VAProfileMPEG4AdvancedSimple + case 4: // VAProfileMPEG4Main + return FF_DECODER_TYPE_DIVX_XVID; + + case 5: // VAProfileH264Baseline + case 6: // VAProfileH264Main + case 7: // VAProfileH264High + case 13: // VAProfileH264ConstrainedBaseline + case 15: // VAProfileH264MultiviewHigh + case 16: // VAProfileH264StereoHigh + case 36: // VAProfileH264High10 + case 40: // VAProfileH264High422 + return FF_DECODER_TYPE_H264; + + case 8: // VAProfileVC1Simple + case 9: // VAProfileVC1Main + case 10: // VAProfileVC1Advanced + return FF_DECODER_TYPE_VC1; + + case 14: // VAProfileVP8Version0_3 + return FF_DECODER_TYPE_VP8; + + case 17: // VAProfileHEVCMain + case 18: // VAProfileHEVCMain10 + case 23: // VAProfileHEVCMain12 + case 24: // VAProfileHEVCMain422_10 + case 25: // VAProfileHEVCMain422_12 + case 26: // VAProfileHEVCMain444 + case 27: // VAProfileHEVCMain444_10 + case 28: // VAProfileHEVCMain444_12 + case 29: // VAProfileHEVCSccMain + case 30: // VAProfileHEVCSccMain10 + case 31: // VAProfileHEVCSccMain444 + case 34: // VAProfileHEVCSccMain444_10 + return FF_DECODER_TYPE_HEVC; + + case 19: // VAProfileVP9Profile0 + case 20: // VAProfileVP9Profile1 + case 21: // VAProfileVP9Profile2 + case 22: // VAProfileVP9Profile3 + return FF_DECODER_TYPE_VP9; + + case 32: // VAProfileAV1Profile0 + case 33: // VAProfileAV1Profile1 + case 39: // VAProfileAV1Profile2 + return FF_DECODER_TYPE_AV1; + + case 37: // VAProfileVVCMain10 + case 38: // VAProfileVVCMultilayerMain10 + return FF_DECODER_TYPE_VVC; + + default: + return FF_DECODER_TYPE_UNKNOWN; + } +} + +static bool ffDecoderEntrypointIsDecode(VAEntrypoint entrypoint) { + switch (entrypoint) { + case VAEntrypointVLD: + case VAEntrypointIDCT: + case VAEntrypointMoComp: + return true; + default: + return false; + } +} + +static bool ffDecoderProfileHasOutput( + VADisplay display, + __typeof__(vaQueryConfigEntrypoints)* ffvaQueryConfigEntrypoints, + __typeof__(vaGetConfigAttributes)* ffvaGetConfigAttributes, + int maxEntrypoints, + VAEntrypoint* entrypoints, + VAProfile profile +) { + int numEntrypoints = maxEntrypoints; + if (ffvaQueryConfigEntrypoints(display, profile, entrypoints, &numEntrypoints) != VA_STATUS_SUCCESS) { + return false; + } + + for (int i = 0; i < numEntrypoints; ++i) { + if (!ffDecoderEntrypointIsDecode(entrypoints[i])) { + continue; + } + + VAConfigAttrib attrib = { + .type = VAConfigAttribRTFormat, + .value = 0, + }; + + if (ffvaGetConfigAttributes(display, profile, entrypoints[i], &attrib, 1) == VA_STATUS_SUCCESS && + attrib.value != VA_ATTRIB_NOT_SUPPORTED && + attrib.value != 0) { + return true; + } + } + + return false; +} + +static void ffDecoderFillGpuName(const drmDevice* dev, const char* path, FFstrbuf* name) { + ffStrbufInit(name); + + switch (dev->bustype) { + case DRM_BUS_PCI: { + FFGPUResult gpu = { + .vendor = ffStrbufCreateStatic(ffGPUGetVendorString(dev->deviceinfo.pci->vendor_id)), + .name = ffStrbufCreate(), + }; + + ffGPUFillVendorAndName(0, dev->deviceinfo.pci->vendor_id, dev->deviceinfo.pci->device_id, &gpu); + ffStrbufSetF(name, "%s %s", gpu.vendor.chars, gpu.name.chars); + + ffStrbufDestroy(&gpu.vendor); + ffStrbufDestroy(&gpu.name); + break; + } + case DRM_BUS_PLATFORM: + ffStrbufSetS(name, dev->deviceinfo.platform->compatible[0]); + return; + case DRM_BUS_HOST1X: + ffStrbufSetS(name, dev->deviceinfo.host1x->compatible[0]); + return; + case DRM_BUS_USB: + ffStrbufSetF(name, "0x%04X 0x%04X", dev->deviceinfo.usb->vendor, dev->deviceinfo.usb->product); + return; + default: + ffStrbufSetStatic(name, "Unknown GPU"); + return; + } + + if (!name->length && path && *path) { + const char* base = strrchr(path, '/'); + ffStrbufSetS(name, base ? base + 1 : path); + } + + if (!name->length) { + ffStrbufSetStatic(name, "Unknown GPU"); + } +} + +const char* ffDetectDecoder(FF_A_UNUSED FFDecoderOptions* options, FFlist* result /* list of FFDecoderResult */) { + FF_LIBRARY_LOAD_MESSAGE(libdrm, "libdrm" FF_LIBRARY_EXTENSION, 2) + FF_LIBRARY_LOAD_SYMBOL_MESSAGE(libdrm, drmGetDevices) + FF_LIBRARY_LOAD_SYMBOL_MESSAGE(libdrm, drmFreeDevices) + + FF_LIBRARY_LOAD_MESSAGE(libva, "libva" FF_LIBRARY_EXTENSION, 2) + FF_LIBRARY_LOAD_SYMBOL_MESSAGE(libva, vaInitialize) + FF_LIBRARY_LOAD_SYMBOL_MESSAGE(libva, vaTerminate) + FF_LIBRARY_LOAD_SYMBOL_MESSAGE(libva, vaMaxNumProfiles) + FF_LIBRARY_LOAD_SYMBOL_MESSAGE(libva, vaMaxNumEntrypoints) + FF_LIBRARY_LOAD_SYMBOL_MESSAGE(libva, vaQueryConfigProfiles) + FF_LIBRARY_LOAD_SYMBOL_MESSAGE(libva, vaQueryConfigEntrypoints) + FF_LIBRARY_LOAD_SYMBOL_MESSAGE(libva, vaGetConfigAttributes) + + FF_LIBRARY_LOAD_MESSAGE(libvaDrm, "libva-drm" FF_LIBRARY_EXTENSION, 2) + FF_LIBRARY_LOAD_SYMBOL_MESSAGE(libvaDrm, vaGetDisplayDRM) + + drmDevicePtr devices[64]; + int numDevices = ffdrmGetDevices(devices, ARRAY_SIZE(devices)); + if (numDevices < 0) { + return "drmGetDevices() failed"; + } + if (numDevices == 0) { + return NULL; + } + + for (int i = 0; i < numDevices; ++i) { + drmDevice* dev = devices[i]; + + const char* path = NULL; + if (dev->available_nodes & (1 << DRM_NODE_RENDER)) { + path = dev->nodes[DRM_NODE_RENDER]; + } else if (dev->available_nodes & (1 << DRM_NODE_PRIMARY)) { + path = dev->nodes[DRM_NODE_PRIMARY]; + } else { + continue; + } + + FF_AUTO_CLOSE_FD int fd = open(path, O_RDWR | O_CLOEXEC); + if (fd < 0) { + fd = open(path, O_RDONLY | O_CLOEXEC); + } + if (fd < 0) { + continue; + } + + VADisplay display = ffvaGetDisplayDRM(fd); + if (!display) { + continue; + } + + int major = 0, minor = 0; + if (ffvaInitialize(display, &major, &minor) != VA_STATUS_SUCCESS) { + continue; + } + + int maxProfiles = ffvaMaxNumProfiles(display); + int maxEntrypoints = ffvaMaxNumEntrypoints(display); + if (maxProfiles <= 0 || maxEntrypoints <= 0) { + ffvaTerminate(display); + continue; + } + + FF_AUTO_FREE VAProfile* profiles = (VAProfile*) malloc(sizeof(VAProfile) * (size_t) maxProfiles); + FF_AUTO_FREE VAEntrypoint* entrypoints = (VAEntrypoint*) malloc(sizeof(VAEntrypoint) * (size_t) maxEntrypoints); + if (!profiles || !entrypoints) { + ffvaTerminate(display); + return "malloc() failed"; + } + + int numProfiles = maxProfiles; + if (ffvaQueryConfigProfiles(display, profiles, &numProfiles) != VA_STATUS_SUCCESS) { + ffvaTerminate(display); + continue; + } + + FFDecoderType types = FF_DECODER_TYPE_NONE; + for (int j = 0; j < numProfiles; ++j) { + if (!ffDecoderProfileHasOutput( + display, + ffvaQueryConfigEntrypoints, + ffvaGetConfigAttributes, + maxEntrypoints, + entrypoints, + profiles[j])) { + continue; + } + + FFDecoderType type = ffDecoderProfileToType(profiles[j]); + types = (FFDecoderType) ((uint32_t) types | (uint32_t) type); + } + + if (types != FF_DECODER_TYPE_NONE) { + FFDecoderResult* item = FF_LIST_ADD(FFDecoderResult, *result); + ffDecoderFillGpuName(dev, path, &item->gpu); + item->types = types; + } + + ffvaTerminate(display); + } + + ffdrmFreeDevices(devices, numDevices); + return NULL; +} + +#else + +const char* ffDetectDecoder(FFDecoderOptions* options, FFlist* result /* list of FFDecoderResult */) { + FF_UNUSED(options, result); + return "Fastfetch was built without DRM / VA-API headers"; +} + +#endif diff --git a/src/modules/decoder/decoder.c b/src/modules/decoder/decoder.c index 57a8d213e..74c4cc47b 100644 --- a/src/modules/decoder/decoder.c +++ b/src/modules/decoder/decoder.c @@ -33,6 +33,8 @@ static const char* ffDecoderTypeToString(FFDecoderType type) { return "VP9"; case FF_DECODER_TYPE_AV1: return "AV1"; + case FF_DECODER_TYPE_VVC: + return "VVC / H.266"; case FF_DECODER_TYPE_UNKNOWN: default: return "Unknown";