obs-nvenc-test.cpp 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561
  1. #include <string_view>
  2. #include <unordered_map>
  3. #include <vector>
  4. #include <chrono>
  5. #include <future>
  6. #include <cstring>
  7. #include <ffnvcodec/nvEncodeAPI.h>
  8. #include <ffnvcodec/dynlink_loader.h>
  9. /*
  10. * Utility to check for NVENC support and capabilities.
  11. * Will check all GPUs and return INI-formatted results based on highest capability of all devices.
  12. */
  13. using namespace std;
  14. using namespace std::chrono_literals;
  15. static CudaFunctions *cu = nullptr;
  16. static NvencFunctions *nvenc = nullptr;
  17. NV_ENCODE_API_FUNCTION_LIST nv = {NV_ENCODE_API_FUNCTION_LIST_VER};
  18. static constexpr uint32_t NVENC_CONFIGURED_VERSION = (NVENCAPI_MAJOR_VERSION << 4) | NVENCAPI_MINOR_VERSION;
  19. /* NVML stuff */
  20. #define NVML_SUCCESS 0
  21. #define NVML_DEVICE_UUID_V2_BUFFER_SIZE 96
  22. #define NVML_DEVICE_NAME_V2_BUFFER_SIZE 96
  23. #define NVML_SYSTEM_DRIVER_VERSION_BUFFER_SIZE 80
  24. typedef int nvmlReturn_t;
  25. typedef struct nvmlDevice *nvmlDevice_t;
  26. typedef enum nvmlEncoderType {
  27. NVML_ENCODER_QUERY_H264,
  28. NVML_ENCODER_QUERY_HEVC,
  29. NVML_ENCODER_QUERY_AV1,
  30. NVML_ENCODER_QUERY_UNKNOWN
  31. } nvmlEncoderType_t;
  32. typedef nvmlReturn_t (*NVML_GET_DRIVER_VER_FUNC)(char *, unsigned int);
  33. typedef nvmlReturn_t (*NVML_INIT_V2)();
  34. typedef nvmlReturn_t (*NVML_SHUTDOWN)();
  35. typedef nvmlReturn_t (*NVML_GET_HANDLE_BY_BUS_ID)(const char *, nvmlDevice_t *);
  36. typedef nvmlReturn_t (*NVML_GET_DEVICE_UUID)(nvmlDevice_t, char *, unsigned);
  37. typedef nvmlReturn_t (*NVML_GET_DEVICE_NAME)(nvmlDevice_t, char *, unsigned);
  38. typedef nvmlReturn_t (*NVML_GET_DEVICE_PCIE_GEN)(nvmlDevice_t, unsigned *);
  39. typedef nvmlReturn_t (*NVML_GET_DEVICE_PCIE_WIDTH)(nvmlDevice_t, unsigned *);
  40. typedef nvmlReturn_t (*NVML_GET_DEVICE_NAME)(nvmlDevice_t, char *, unsigned);
  41. typedef nvmlReturn_t (*NVML_GET_DEVICE_ARCHITECTURE)(nvmlDevice_t, unsigned *);
  42. typedef nvmlReturn_t (*NVML_GET_ENCODER_SESSIONS)(nvmlDevice_t, unsigned *, void *);
  43. typedef nvmlReturn_t (*NVML_GET_ENCODER_CAPACITY)(nvmlDevice_t, nvmlEncoderType, unsigned *);
  44. typedef nvmlReturn_t (*NVML_GET_ENCODER_UTILISATION)(nvmlDevice_t, unsigned *, unsigned *);
  45. /* Only Kepler is defined in NVIDIA's documentation, but it's also the main one we care about. */
  46. constexpr uint32_t NVML_DEVICE_ARCH_KEPLER = 2;
  47. const unordered_map<uint32_t, const string_view> arch_to_name = {
  48. {NVML_DEVICE_ARCH_KEPLER, "Kepler"},
  49. {3, "Kepler"},
  50. {4, "Maxwell"},
  51. {5, "Volta"},
  52. {6, "Turing"},
  53. {7, "Ampere"},
  54. {8, "Ada"},
  55. {9, "Hopper"},
  56. {10, "Blackwell"},
  57. };
  58. /* List of capabilities to be queried per codec */
  59. static const vector<pair<NV_ENC_CAPS, string>> capabilities = {
  60. {NV_ENC_CAPS_NUM_MAX_BFRAMES, "bframes"},
  61. {NV_ENC_CAPS_SUPPORT_LOSSLESS_ENCODE, "lossless"},
  62. {NV_ENC_CAPS_SUPPORT_LOOKAHEAD, "lookahead"},
  63. {NV_ENC_CAPS_SUPPORT_TEMPORAL_AQ, "temporal_aq"},
  64. {NV_ENC_CAPS_SUPPORT_DYN_BITRATE_CHANGE, "dynamic_bitrate"},
  65. {NV_ENC_CAPS_SUPPORT_10BIT_ENCODE, "10bit"},
  66. {NV_ENC_CAPS_SUPPORT_BFRAME_REF_MODE, "bref"},
  67. {NV_ENC_CAPS_NUM_ENCODER_ENGINES, "engines"},
  68. {NV_ENC_CAPS_SUPPORT_YUV444_ENCODE, "yuv_444"},
  69. {NV_ENC_CAPS_WIDTH_MAX, "max_width"},
  70. {NV_ENC_CAPS_HEIGHT_MAX, "max_height"},
  71. #if NVENCAPI_MAJOR_VERSION > 12 || NVENCAPI_MINOR_VERSION >= 2
  72. /* SDK 12.2+ features */
  73. {NV_ENC_CAPS_SUPPORT_TEMPORAL_FILTER, "temporal_filter"},
  74. {NV_ENC_CAPS_SUPPORT_LOOKAHEAD_LEVEL, "lookahead_level"},
  75. {NV_ENC_CAPS_SUPPORT_UNIDIRECTIONAL_B, "unidirectional_b"},
  76. #endif
  77. #if NVENCAPI_MAJOR_VERSION >= 13
  78. /* SDK 13.0+ features */
  79. {NV_ENC_CAPS_SUPPORT_YUV422_ENCODE, "yuv_422"},
  80. #endif
  81. };
  82. static const vector<pair<string_view, GUID>> codecs = {{"h264", NV_ENC_CODEC_H264_GUID},
  83. {"hevc", NV_ENC_CODEC_HEVC_GUID},
  84. {"av1", NV_ENC_CODEC_AV1_GUID}};
  85. typedef unordered_map<string, unordered_map<string, int>> codec_caps_map;
  86. struct device_info {
  87. string pci_id;
  88. string nvml_uuid;
  89. string cuda_uuid;
  90. string name;
  91. uint32_t architecture;
  92. uint32_t pcie_gen;
  93. uint32_t pcie_width;
  94. uint32_t encoder_sessions;
  95. uint32_t utilisation;
  96. uint32_t sample_period;
  97. uint32_t capacity_h264;
  98. uint32_t capacity_hevc;
  99. uint32_t capacity_av1;
  100. codec_caps_map caps;
  101. };
  102. /* RAII wrappers to make my life a little easier. */
  103. struct NVML {
  104. NVML_INIT_V2 init;
  105. NVML_SHUTDOWN shutdown;
  106. NVML_GET_DRIVER_VER_FUNC getDriverVersion;
  107. NVML_GET_HANDLE_BY_BUS_ID getDeviceHandleByPCIBusId;
  108. NVML_GET_DEVICE_UUID getDeviceUUID;
  109. NVML_GET_DEVICE_NAME getDeviceName;
  110. NVML_GET_DEVICE_PCIE_GEN getDevicePCIeGen;
  111. NVML_GET_DEVICE_PCIE_WIDTH getDevicePCIeWidth;
  112. NVML_GET_DEVICE_ARCHITECTURE getDeviceArchitecture;
  113. NVML_GET_ENCODER_SESSIONS getEncoderSessions;
  114. NVML_GET_ENCODER_CAPACITY getEncoderCapacity;
  115. NVML_GET_ENCODER_UTILISATION getEncoderUtilisation;
  116. NVML() = default;
  117. ~NVML()
  118. {
  119. if (initialised && shutdown)
  120. shutdown();
  121. }
  122. bool Init()
  123. {
  124. if (!load_nvml_lib()) {
  125. printf("reason=nvml_lib\n");
  126. return false;
  127. }
  128. init = (NVML_INIT_V2)load_nvml_func("nvmlInit_v2");
  129. shutdown = (NVML_SHUTDOWN)load_nvml_func("nvmlShutdown");
  130. getDriverVersion = (NVML_GET_DRIVER_VER_FUNC)load_nvml_func("nvmlSystemGetDriverVersion");
  131. getDeviceHandleByPCIBusId =
  132. (NVML_GET_HANDLE_BY_BUS_ID)load_nvml_func("nvmlDeviceGetHandleByPciBusId_v2");
  133. getDeviceUUID = (NVML_GET_DEVICE_UUID)load_nvml_func("nvmlDeviceGetUUID");
  134. getDeviceName = (NVML_GET_DEVICE_NAME)load_nvml_func("nvmlDeviceGetName");
  135. getDevicePCIeGen = (NVML_GET_DEVICE_PCIE_GEN)load_nvml_func("nvmlDeviceGetCurrPcieLinkGeneration");
  136. getDevicePCIeWidth = (NVML_GET_DEVICE_PCIE_WIDTH)load_nvml_func("nvmlDeviceGetCurrPcieLinkWidth");
  137. getDeviceArchitecture = (NVML_GET_DEVICE_ARCHITECTURE)load_nvml_func("nvmlDeviceGetArchitecture");
  138. getEncoderSessions = (NVML_GET_ENCODER_SESSIONS)load_nvml_func("nvmlDeviceGetEncoderSessions");
  139. getEncoderCapacity = (NVML_GET_ENCODER_CAPACITY)load_nvml_func("nvmlDeviceGetEncoderCapacity");
  140. getEncoderUtilisation = (NVML_GET_ENCODER_UTILISATION)load_nvml_func("nvmlDeviceGetEncoderUtilization");
  141. if (!init || !shutdown || !getDriverVersion || !getDeviceHandleByPCIBusId || !getDeviceUUID ||
  142. !getDeviceName || !getDevicePCIeGen || !getDevicePCIeWidth || !getEncoderSessions ||
  143. !getEncoderCapacity || !getEncoderUtilisation || !getDeviceArchitecture) {
  144. return false;
  145. }
  146. nvmlReturn_t res = init();
  147. if (res != 0) {
  148. printf("reason=nvml_init_%d\n", res);
  149. return false;
  150. }
  151. initialised = true;
  152. return true;
  153. }
  154. private:
  155. bool initialised = false;
  156. static inline void *nvml_lib = nullptr;
  157. bool load_nvml_lib()
  158. {
  159. #ifdef _WIN32
  160. nvml_lib = LoadLibraryA("nvml.dll");
  161. #else
  162. nvml_lib = dlopen("libnvidia-ml.so.1", RTLD_LAZY);
  163. #endif
  164. return nvml_lib != nullptr;
  165. }
  166. static void *load_nvml_func(const char *func)
  167. {
  168. #ifdef _WIN32
  169. void *func_ptr = (void *)GetProcAddress((HMODULE)nvml_lib, func);
  170. #else
  171. void *func_ptr = dlsym(nvml_lib, func);
  172. #endif
  173. return func_ptr;
  174. }
  175. };
  176. struct CUDACtx {
  177. CUcontext ctx;
  178. CUDACtx() = default;
  179. ~CUDACtx() { cu->cuCtxDestroy(ctx); }
  180. bool Init(int adapter_idx)
  181. {
  182. CUdevice dev;
  183. if (cu->cuDeviceGet(&dev, adapter_idx) != CUDA_SUCCESS)
  184. return false;
  185. return cu->cuCtxCreate(&ctx, 0, dev) == CUDA_SUCCESS;
  186. }
  187. string GetPCIBusId()
  188. {
  189. CUdevice dev;
  190. string bus_id;
  191. bus_id.resize(16);
  192. cu->cuCtxGetDevice(&dev);
  193. cu->cuDeviceGetPCIBusId(bus_id.data(), (int)bus_id.capacity(), dev);
  194. return bus_id;
  195. }
  196. string GetUUID()
  197. {
  198. CUdevice dev;
  199. CUuuid uuid;
  200. string uuid_str;
  201. cu->cuCtxGetDevice(&dev);
  202. cu->cuDeviceGetUuid_v2(&uuid, dev);
  203. uuid_str.resize(32);
  204. for (size_t idx = 0; idx < 16; idx++) {
  205. sprintf(uuid_str.data() + idx * 2, "%02x", uuid.bytes[idx] & 0xFF);
  206. }
  207. return uuid_str;
  208. }
  209. };
  210. struct NVSession {
  211. void *ptr = nullptr;
  212. NVSession() = default;
  213. ~NVSession() { nv.nvEncDestroyEncoder(ptr); }
  214. NVENCSTATUS OpenSession(const CUDACtx &ctx)
  215. {
  216. NV_ENC_OPEN_ENCODE_SESSION_EX_PARAMS params = {};
  217. params.version = NV_ENC_OPEN_ENCODE_SESSION_EX_PARAMS_VER;
  218. params.apiVersion = NVENCAPI_VERSION;
  219. params.device = ctx.ctx;
  220. params.deviceType = NV_ENC_DEVICE_TYPE_CUDA;
  221. return nv.nvEncOpenEncodeSessionEx(&params, &ptr);
  222. }
  223. };
  224. static bool init_nvenc()
  225. {
  226. if (nvenc_load_functions(&nvenc, nullptr)) {
  227. printf("reason=nvenc_lib\n");
  228. return false;
  229. }
  230. NVENCSTATUS res = nvenc->NvEncodeAPICreateInstance(&nv);
  231. if (res != NV_ENC_SUCCESS) {
  232. printf("reason=nvenc_init_%d\n", res);
  233. return false;
  234. }
  235. return true;
  236. }
  237. static bool init_cuda()
  238. {
  239. if (cuda_load_functions(&cu, nullptr)) {
  240. printf("reason=cuda_lib\n");
  241. return false;
  242. }
  243. CUresult res = cu->cuInit(0);
  244. if (res != CUDA_SUCCESS) {
  245. printf("reason=cuda_init_%d\n", res);
  246. return false;
  247. }
  248. return true;
  249. }
  250. static bool get_adapter_caps(int adapter_idx, codec_caps_map &caps, device_info &device_info, NVML &nvml,
  251. bool &session_limit)
  252. {
  253. CUDACtx cudaCtx;
  254. NVSession nvSession;
  255. if (!cudaCtx.Init(adapter_idx))
  256. return false;
  257. device_info.pci_id = cudaCtx.GetPCIBusId();
  258. device_info.cuda_uuid = cudaCtx.GetUUID();
  259. nvmlDevice_t dev;
  260. if (nvml.getDeviceHandleByPCIBusId(device_info.pci_id.data(), &dev) == NVML_SUCCESS) {
  261. char uuid[NVML_DEVICE_UUID_V2_BUFFER_SIZE];
  262. nvml.getDeviceUUID(dev, uuid, sizeof(uuid));
  263. device_info.nvml_uuid = uuid;
  264. char name[NVML_DEVICE_NAME_V2_BUFFER_SIZE];
  265. nvml.getDeviceName(dev, name, sizeof(name));
  266. device_info.name = name;
  267. nvml.getDevicePCIeGen(dev, &device_info.pcie_gen);
  268. nvml.getDevicePCIeWidth(dev, &device_info.pcie_width);
  269. nvml.getEncoderSessions(dev, &device_info.encoder_sessions, nullptr);
  270. nvml.getDeviceArchitecture(dev, &device_info.architecture);
  271. nvml.getEncoderUtilisation(dev, &device_info.utilisation, &device_info.sample_period);
  272. nvml.getEncoderCapacity(dev, NVML_ENCODER_QUERY_H264, &device_info.capacity_h264);
  273. nvml.getEncoderCapacity(dev, NVML_ENCODER_QUERY_HEVC, &device_info.capacity_hevc);
  274. nvml.getEncoderCapacity(dev, NVML_ENCODER_QUERY_AV1, &device_info.capacity_av1);
  275. }
  276. auto res = nvSession.OpenSession(cudaCtx);
  277. session_limit = session_limit || res == NV_ENC_ERR_INCOMPATIBLE_CLIENT_KEY;
  278. if (res != NV_ENC_SUCCESS)
  279. return false;
  280. uint32_t guid_count = 0;
  281. if (nv.nvEncGetEncodeGUIDCount(nvSession.ptr, &guid_count) != NV_ENC_SUCCESS)
  282. return false;
  283. vector<GUID> guids;
  284. guids.resize(guid_count);
  285. NVENCSTATUS stat = nv.nvEncGetEncodeGUIDs(nvSession.ptr, guids.data(), guid_count, &guid_count);
  286. if (stat != NV_ENC_SUCCESS)
  287. return false;
  288. NV_ENC_CAPS_PARAM param = {NV_ENC_CAPS_PARAM_VER};
  289. for (uint32_t i = 0; i < guid_count; i++) {
  290. GUID *guid = &guids[i];
  291. std::string codec_name = "unknown";
  292. for (const auto &[name, codec_guid] : codecs) {
  293. if (memcmp(&codec_guid, guid, sizeof(GUID)) == 0) {
  294. codec_name = name;
  295. break;
  296. }
  297. }
  298. caps[codec_name]["codec_supported"] = 1;
  299. device_info.caps[codec_name]["codec_supported"] = 1;
  300. for (const auto &[cap, name] : capabilities) {
  301. int v;
  302. param.capsToQuery = cap;
  303. if (nv.nvEncGetEncodeCaps(nvSession.ptr, *guid, &param, &v) != NV_ENC_SUCCESS)
  304. continue;
  305. device_info.caps[codec_name][name] = v;
  306. caps[codec_name][name] = std::max(v, caps[codec_name][name]);
  307. }
  308. #if NVENCAPI_MAJOR_VERSION > 12 || NVENCAPI_MINOR_VERSION >= 2
  309. /* Explicitly check if UHQ tuning is supported since temporal filtering query is true for all codecs. */
  310. NV_ENC_PRESET_CONFIG preset_config = {};
  311. preset_config.version = NV_ENC_PRESET_CONFIG_VER;
  312. preset_config.presetCfg.version = NV_ENC_CONFIG_VER;
  313. NVENCSTATUS res = nv.nvEncGetEncodePresetConfigEx(nvSession.ptr, *guid, NV_ENC_PRESET_P7_GUID,
  314. NV_ENC_TUNING_INFO_ULTRA_HIGH_QUALITY,
  315. &preset_config);
  316. device_info.caps[codec_name]["uhq"] = res == NV_ENC_SUCCESS ? 1 : 0;
  317. caps[codec_name]["uhq"] = std::max(device_info.caps[codec_name]["uhq"], caps[codec_name]["uhq"]);
  318. #endif
  319. }
  320. return true;
  321. }
  322. bool nvenc_checks(codec_caps_map &caps, vector<device_info> &device_infos)
  323. {
  324. /* NVENC API init */
  325. if (!init_nvenc())
  326. return false;
  327. /* CUDA init */
  328. if (!init_cuda())
  329. return false;
  330. NVML nvml;
  331. if (!nvml.Init())
  332. return false;
  333. /* --------------------------------------------------------- */
  334. /* obtain adapter compatibility information */
  335. uint32_t nvenc_ver;
  336. int cuda_driver_ver;
  337. int cuda_devices = 0;
  338. int nvenc_devices = 0;
  339. char driver_ver[NVML_SYSTEM_DRIVER_VERSION_BUFFER_SIZE];
  340. bool session_limit = false;
  341. /* NVIDIA driver version */
  342. if (nvml.getDriverVersion(driver_ver, sizeof(driver_ver)) == NVML_SUCCESS) {
  343. printf("driver_ver=%s\n", driver_ver);
  344. } else {
  345. // Treat this as a non-fatal failure
  346. printf("driver_ver=0.0\n");
  347. }
  348. /* CUDA driver version and devices */
  349. if (cu->cuDriverGetVersion(&cuda_driver_ver) == CUDA_SUCCESS) {
  350. printf("cuda_ver=%d.%d\n", cuda_driver_ver / 1000, cuda_driver_ver % 1000);
  351. } else {
  352. printf("reason=no_cuda_version\n");
  353. return false;
  354. }
  355. if (cu->cuDeviceGetCount(&cuda_devices) == CUDA_SUCCESS && cuda_devices) {
  356. printf("cuda_devices=%d\n", cuda_devices);
  357. } else {
  358. printf("reason=no_devices\n");
  359. return false;
  360. }
  361. /* NVENC API version */
  362. if (nvenc->NvEncodeAPIGetMaxSupportedVersion(&nvenc_ver) == NV_ENC_SUCCESS) {
  363. printf("nvenc_ver=%d.%d\n", nvenc_ver >> 4, nvenc_ver & 0xf);
  364. } else {
  365. printf("reason=no_nvenc_version\n");
  366. return false;
  367. }
  368. device_infos.resize(cuda_devices);
  369. for (int idx = 0; idx < cuda_devices; idx++) {
  370. if (get_adapter_caps(idx, caps, device_infos[idx], nvml, session_limit))
  371. nvenc_devices++;
  372. }
  373. if (session_limit) {
  374. printf("reason=session_limit\n");
  375. return false;
  376. }
  377. if (nvenc_ver < NVENC_CONFIGURED_VERSION) {
  378. printf("reason=outdated_driver\n");
  379. return false;
  380. }
  381. printf("nvenc_devices=%d\n", nvenc_devices);
  382. if (!nvenc_devices) {
  383. printf("reason=no_supported_devices\n");
  384. return false;
  385. }
  386. uint32_t latest_architecture = 0;
  387. string_view architecture = "Unknown";
  388. for (auto &info : device_infos)
  389. latest_architecture = std::max(info.architecture, latest_architecture);
  390. if (arch_to_name.count(latest_architecture))
  391. architecture = arch_to_name.at(latest_architecture);
  392. printf("latest_architecture=%u\n"
  393. "latest_architecture_name=%s\n",
  394. latest_architecture, architecture.data());
  395. return true;
  396. }
  397. int check_thread()
  398. {
  399. int ret = 0;
  400. codec_caps_map caps;
  401. vector<device_info> device_infos;
  402. caps["h264"]["codec_supported"] = 0;
  403. caps["hevc"]["codec_supported"] = 0;
  404. caps["av1"]["codec_supported"] = 0;
  405. printf("[general]\n");
  406. if (nvenc_checks(caps, device_infos)) {
  407. printf("nvenc_supported=true\n");
  408. } else {
  409. printf("nvenc_supported=false\n");
  410. ret = 1;
  411. }
  412. /* Global capabilities, based on highest supported across all devices */
  413. for (const auto &[codec, codec_caps] : caps) {
  414. printf("\n[%s]\n", codec.c_str());
  415. for (const auto &[name, value] : codec_caps) {
  416. printf("%s=%d\n", name.c_str(), value);
  417. }
  418. }
  419. /* Per-device info (mostly for debugging) */
  420. for (size_t idx = 0; idx < device_infos.size(); idx++) {
  421. const auto &info = device_infos[idx];
  422. string_view architecture = "Unknown";
  423. if (arch_to_name.count(info.architecture))
  424. architecture = arch_to_name.at(info.architecture);
  425. printf("\n[device.%zu]\n"
  426. "pci_id=%s\n"
  427. "nvml_uuid=%s\n"
  428. "cuda_uuid=%s\n"
  429. "name=%s\n"
  430. "architecture=%u\n"
  431. "architecture_name=%s\n"
  432. "pcie_link_width=%d\n"
  433. "pcie_link_gen=%d\n"
  434. "encoder_sessions=%u\n"
  435. "utilisation=%u\n"
  436. "sample_period=%u\n"
  437. "capacity_h264=%u\n"
  438. "capacity_hevc=%u\n"
  439. "capacity_av1=%u\n",
  440. idx, info.pci_id.c_str(), info.nvml_uuid.c_str(), info.cuda_uuid.c_str(), info.name.c_str(),
  441. info.architecture, architecture.data(), info.pcie_width, info.pcie_gen, info.encoder_sessions,
  442. info.utilisation, info.sample_period, info.capacity_h264, info.capacity_hevc, info.capacity_av1);
  443. for (const auto &[codec, codec_caps] : info.caps) {
  444. printf("\n[device.%zu.%s]\n", idx, codec.c_str());
  445. for (const auto &[name, value] : codec_caps) {
  446. printf("%s=%d\n", name.c_str(), value);
  447. }
  448. }
  449. }
  450. return ret;
  451. }
  452. int main(int, char **)
  453. {
  454. future<int> f = async(launch::async, check_thread);
  455. future_status status = f.wait_for(2.5s);
  456. if (status == future_status::timeout)
  457. exit(1);
  458. return f.get();
  459. }