Loading...
Searching...
No Matches
GpuFormats.hpp
1#pragma once
2#include <Media/Libav.hpp>
3
4#include <QtGlobal>
5
6#include <cstdlib>
7#include <string>
8#include <string_view>
9#include <vector>
10
11#if defined(__linux__)
12#include <unistd.h>
13#endif
14
15#if SCORE_HAS_LIBAV
16extern "C" {
17#include <libavcodec/avcodec.h>
18#include <libavformat/avformat.h>
19#include <libavutil/pixdesc.h>
20#include <libavutil/version.h>
21#if __has_include(<libavutil/hwcontext.h>)
22#include <libavutil/hwcontext.h>
23#endif
24struct AVCodecContext;
25}
26
27namespace Video
28{
29
30inline bool hardwareDecoderIsAvailable(AVPixelFormat p) noexcept
31{
32 switch(p)
33 {
34#if defined(__linux__)
35 case AV_PIX_FMT_DRM_PRIME: {
36 static const bool ok = avcodec_find_decoder_by_name("h264_v4l2m2m");
37 return ok;
38 }
39 case AV_PIX_FMT_VAAPI: {
40 // FFmpeg 7+ removed dedicated VAAPI decoders (mjpeg_vaapi, etc.).
41 // VAAPI now uses the generic decoder with hw_device_ctx.
42 // Check if the VAAPI device type is supported instead.
43 static const bool ok = avcodec_find_decoder_by_name("mjpeg_vaapi")
44 || av_hwdevice_find_type_by_name("vaapi") != AV_HWDEVICE_TYPE_NONE;
45 return ok;
46 }
47 case AV_PIX_FMT_VDPAU: {
48 static const bool ok = avcodec_find_decoder_by_name("h264_vdpau");
49 return ok;
50 }
51#endif
52#if defined(_WIN32)
53 case AV_PIX_FMT_DXVA2_VLD:
54 return true;
55 case AV_PIX_FMT_D3D11:
56 return true;
57#if LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(58, 29, 100)
58 case AV_PIX_FMT_D3D12:
59 return true;
60#endif
61#endif
62#if defined(__APPLE__)
63 case AV_PIX_FMT_VIDEOTOOLBOX:
64 return true;
65#endif
66 // Cross-platform pix formats
67 case AV_PIX_FMT_CUDA: {
68 static const bool ok = avcodec_find_decoder_by_name("mjpeg_cuvid")
69 || avcodec_find_decoder_by_name("h264_cuvid");
70 return ok;
71 }
72 case AV_PIX_FMT_QSV: {
73 static const bool ok = avcodec_find_decoder_by_name("mjpeg_qsv")
74 || avcodec_find_decoder_by_name("h264_qsv");
75 return ok;
76 }
77 case AV_PIX_FMT_VULKAN: {
78 static const bool ok
79 = av_hwdevice_find_type_by_name("vulkan") != AV_HWDEVICE_TYPE_NONE;
80 return ok;
81 }
82 default:
83 return false;
84 }
85}
86
87inline constexpr bool formatIsHardwareDecoded(AVPixelFormat fmt) noexcept
88{
89#if LIBAVUTIL_VERSION_MAJOR < 57
90 return false;
91#else
92 switch(fmt)
93 {
94 case AV_PIX_FMT_VAAPI:
95 case AV_PIX_FMT_VDPAU:
96 case AV_PIX_FMT_DXVA2_VLD:
97 case AV_PIX_FMT_D3D11:
98#if defined(_WIN32) && LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(58, 29, 100)
99 case AV_PIX_FMT_D3D12:
100#endif
101 case AV_PIX_FMT_CUDA:
102 case AV_PIX_FMT_QSV:
103 case AV_PIX_FMT_VIDEOTOOLBOX:
104 case AV_PIX_FMT_DRM_PRIME:
105 case AV_PIX_FMT_VULKAN:
106 return true;
107 default:
108 return false;
109 }
110#endif
111}
112
113inline constexpr bool formatNeedsDecoding(AVPixelFormat fmt) noexcept
114{
115 // They all get translated to some NV12 or something like that
116 if(formatIsHardwareDecoded(fmt))
117 return false;
118
119 switch(fmt)
120 {
121 case AV_PIX_FMT_YUV420P:
122 case AV_PIX_FMT_RGB24:
123 case AV_PIX_FMT_BGR24:
124 case AV_PIX_FMT_RGB48LE:
125 case AV_PIX_FMT_BGR48LE:
126 case AV_PIX_FMT_NV12:
127 case AV_PIX_FMT_NV21:
128 case AV_PIX_FMT_NV16:
129 case AV_PIX_FMT_P010LE:
130 case AV_PIX_FMT_P016LE:
131 case AV_PIX_FMT_YUVJ420P:
132 case AV_PIX_FMT_YUVJ422P:
133 case AV_PIX_FMT_YUVJ440P:
134 case AV_PIX_FMT_YUV422P:
135 case AV_PIX_FMT_YUV440P:
136 case AV_PIX_FMT_YUV444P:
137 case AV_PIX_FMT_YUVJ444P:
138 case AV_PIX_FMT_YUVA420P:
139 case AV_PIX_FMT_YUVA444P:
140 case AV_PIX_FMT_UYVY422:
141 case AV_PIX_FMT_YUYV422:
142 case AV_PIX_FMT_RGB0:
143 case AV_PIX_FMT_RGBA:
144 case AV_PIX_FMT_BGR0:
145 case AV_PIX_FMT_BGRA:
146 case AV_PIX_FMT_ARGB:
147 case AV_PIX_FMT_ABGR:
148 case AV_PIX_FMT_RGBA64LE:
149 case AV_PIX_FMT_BGRA64LE:
150 case AV_PIX_FMT_X2RGB10LE:
151 case AV_PIX_FMT_GBRP:
152 case AV_PIX_FMT_GBRAP:
153 case AV_PIX_FMT_YA8:
154 case AV_PIX_FMT_YA16LE:
155
156#if LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(56, 19, 100)
157 case AV_PIX_FMT_YUV420P10LE:
158 case AV_PIX_FMT_YUV420P12LE:
159 case AV_PIX_FMT_YUV422P10LE:
160 case AV_PIX_FMT_YUV422P12LE:
161 case AV_PIX_FMT_YUV444P10LE:
162 case AV_PIX_FMT_YUV444P12LE:
163 case AV_PIX_FMT_YUVA444P10LE:
164 case AV_PIX_FMT_YUVA444P12LE:
165 case AV_PIX_FMT_GBRP10LE:
166 case AV_PIX_FMT_GBRP12LE:
167 case AV_PIX_FMT_GBRP16LE:
168 case AV_PIX_FMT_GBRAP10LE:
169 case AV_PIX_FMT_GBRAP12LE:
170 case AV_PIX_FMT_GBRAP16LE:
171 case AV_PIX_FMT_GBRPF32LE:
172 case AV_PIX_FMT_GBRAPF32LE:
173 case AV_PIX_FMT_GRAYF32:
174 case AV_PIX_FMT_NV24:
175 case AV_PIX_FMT_NV42:
176 case AV_PIX_FMT_Y210LE:
177#endif
178
179#if LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(57, 17, 100)
180 case AV_PIX_FMT_X2BGR10LE:
181 case AV_PIX_FMT_P210LE:
182 case AV_PIX_FMT_P410LE:
183#endif
184
185 // These guards must match GPUVideoDecoderFactory.cpp: a format whose decoder
186 // is compiled in but whose case here is compiled out gets routed through
187 // swscale even though it has a direct GPU path.
188#if LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(58, 29, 100)
189 case AV_PIX_FMT_RGBAF32LE:
190 case AV_PIX_FMT_VUYA:
191 case AV_PIX_FMT_VUYX:
192 case AV_PIX_FMT_P012LE:
193 case AV_PIX_FMT_RGBAF16LE:
194#if QT_VERSION >= QT_VERSION_CHECK(6, 4, 0)
195 case AV_PIX_FMT_XV30LE:
196#endif
197#endif
198
199#if LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(60, 8, 100)
200 case AV_PIX_FMT_GRAYF16:
201#endif
202
203 case AV_PIX_FMT_GRAY8:
204 case AV_PIX_FMT_GRAY16:
205 return false;
206
207 // Other formats get rgb'd
208 default:
209 return true;
210 }
211}
212
213#if LIBAVUTIL_VERSION_MAJOR >= 57
214// Get hardware pix format
215struct HWAccelFormats
216{
217 AVPixelFormat format{AV_PIX_FMT_NONE};
218 AVHWDeviceType device{AV_HWDEVICE_TYPE_NONE};
219};
220
221inline constexpr HWAccelFormats ffmpegHardwareDecodingFormats(AVPixelFormat p) noexcept
222{
223 switch(p)
224 {
225#if defined(__linux__)
226 case AV_PIX_FMT_DRM_PRIME:
227 if(!hardwareDecoderIsAvailable(AV_PIX_FMT_DRM_PRIME))
228 return {};
229 return {AV_PIX_FMT_DRM_PRIME, AV_HWDEVICE_TYPE_DRM};
230 case AV_PIX_FMT_VAAPI:
231 if(!hardwareDecoderIsAvailable(AV_PIX_FMT_VAAPI))
232 return {};
233 return {AV_PIX_FMT_VAAPI, AV_HWDEVICE_TYPE_VAAPI};
234 case AV_PIX_FMT_VDPAU:
235 if(!hardwareDecoderIsAvailable(AV_PIX_FMT_VDPAU))
236 return {};
237 return {AV_PIX_FMT_VDPAU, AV_HWDEVICE_TYPE_VDPAU};
238#endif
239#if defined(_WIN32)
240 case AV_PIX_FMT_DXVA2_VLD:
241 if(!hardwareDecoderIsAvailable(AV_PIX_FMT_DXVA2_VLD))
242 return {};
243 return {AV_PIX_FMT_DXVA2_VLD, AV_HWDEVICE_TYPE_DXVA2};
244 case AV_PIX_FMT_D3D11:
245 if(!hardwareDecoderIsAvailable(AV_PIX_FMT_D3D11))
246 return {};
247 return {AV_PIX_FMT_D3D11, AV_HWDEVICE_TYPE_D3D11VA};
248#if LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(58, 29, 100)
249 case AV_PIX_FMT_D3D12:
250 if(!hardwareDecoderIsAvailable(AV_PIX_FMT_D3D12))
251 return {};
252 return {AV_PIX_FMT_D3D12, AV_HWDEVICE_TYPE_D3D12VA};
253#endif
254#endif
255#if defined(__APPLE__)
256 case AV_PIX_FMT_VIDEOTOOLBOX:
257 if(!hardwareDecoderIsAvailable(AV_PIX_FMT_VIDEOTOOLBOX))
258 return {};
259 return {AV_PIX_FMT_VIDEOTOOLBOX, AV_HWDEVICE_TYPE_VIDEOTOOLBOX};
260#endif
261 // Cross-platform pix formats
262 case AV_PIX_FMT_CUDA:
263 if(!hardwareDecoderIsAvailable(AV_PIX_FMT_CUDA))
264 return {};
265 return {AV_PIX_FMT_CUDA, AV_HWDEVICE_TYPE_CUDA};
266 case AV_PIX_FMT_QSV:
267 if(!hardwareDecoderIsAvailable(AV_PIX_FMT_QSV))
268 return {};
269 return {AV_PIX_FMT_QSV, AV_HWDEVICE_TYPE_QSV};
270 case AV_PIX_FMT_VULKAN:
271 if(!hardwareDecoderIsAvailable(AV_PIX_FMT_VULKAN))
272 return {};
273 return {AV_PIX_FMT_VULKAN, AV_HWDEVICE_TYPE_VULKAN};
274 // case AV_PIX_FMT_OPENCL:
275 // return {AV_PIX_FMT_OPENCL, AV_HWDEVICE_TYPE_OPENCL};
276 default:
277 return {};
278 }
279}
280
281inline constexpr bool ffmpegCanDoHardwareDecoding(AVCodecID id) noexcept
282{
283 switch(id)
284 {
285 case AV_CODEC_ID_AV1:
286 case AV_CODEC_ID_H264:
287 case AV_CODEC_ID_HEVC:
288 case AV_CODEC_ID_MJPEG:
289 case AV_CODEC_ID_MPEG1VIDEO:
290 case AV_CODEC_ID_MPEG2VIDEO:
291 case AV_CODEC_ID_MPEG4:
292 case AV_CODEC_ID_VC1:
293 case AV_CODEC_ID_VP8:
294 case AV_CODEC_ID_VP9:
295 case AV_CODEC_ID_WMV1:
296 case AV_CODEC_ID_WMV2:
297 case AV_CODEC_ID_WMV3:
298 case AV_CODEC_ID_PRORES:
299 return true;
300 default:
301 return false;
302 }
303}
305inline std::string hwCodecName(const char* codec_name, AVHWDeviceType device)
306{
307 std::string name{codec_name};
308 switch(device)
309 {
310 case AV_HWDEVICE_TYPE_CUDA:
311 return name + "_cuvid";
312 case AV_HWDEVICE_TYPE_QSV:
313 return name + "_qsv";
314 case AV_HWDEVICE_TYPE_VDPAU:
315 return name + "_vdpau";
316 case AV_HWDEVICE_TYPE_VAAPI:
317 return name;
318 case AV_HWDEVICE_TYPE_DRM:
319 return name + "_v4l2m2m";
320 case AV_HWDEVICE_TYPE_DXVA2:
321 case AV_HWDEVICE_TYPE_D3D11VA:
322#if defined(_WIN32) && LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(58, 29, 100)
323 case AV_HWDEVICE_TYPE_D3D12VA:
324#endif
325 case AV_HWDEVICE_TYPE_VIDEOTOOLBOX:
326 case AV_HWDEVICE_TYPE_VULKAN:
327 return name;
328 default:
329 return {};
330 }
331}
332
335inline bool isVulkanComputeCodec(AVCodecID id)
336{
337 switch(id)
338 {
339 case AV_CODEC_ID_PRORES:
340 case AV_CODEC_ID_FFV1:
341 return true;
342 default:
343 return false;
344 }
345}
346
349 AVCodecID codec_id, AVPixelFormat pix_fmt, uint32_t gpuVendorId = 0)
350{
351 (void)gpuVendorId;
352
353 // The Vulkan compute-shader decoders (not Vulkan Video) produce wrong
354 // pixels: part of every frame comes out full-scale wrong. This check is
355 // the gate for both an explicitly requested accel and the automatic
356 // selection, so refusing here sends these codecs to the software decoder
357 // on every path.
358 if(pix_fmt == AV_PIX_FMT_VULKAN && isVulkanComputeCodec(codec_id))
359 return false;
360
361 auto hwInfo = ffmpegHardwareDecodingFormats(pix_fmt);
362 if(hwInfo.device == AV_HWDEVICE_TYPE_NONE)
363 return false;
364
365 const AVCodec* codec = avcodec_find_decoder(codec_id);
366 if(!codec)
367 return false;
368
369 auto dedicated = hwCodecName(codec->name, hwInfo.device);
370 if(!dedicated.empty() && dedicated != codec->name)
371 {
372 if(avcodec_find_decoder_by_name(dedicated.c_str()))
373 return true;
374 return false;
375 }
376
377 for(int i = 0;; i++)
378 {
379 const AVCodecHWConfig* config = avcodec_get_hw_config(codec, i);
380 if(!config)
381 break;
382 if(config->pix_fmt == pix_fmt
383 && (config->methods & AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX))
384 return true;
385 }
386 return false;
387}
388
390namespace GpuVendor
391{
392inline constexpr uint32_t NVIDIA = 0x10DE;
393inline constexpr uint32_t Intel = 0x8086;
394inline constexpr uint32_t AMD = 0x1002;
395inline constexpr uint32_t Apple = 0x106B;
396inline constexpr uint32_t Broadcom = 0x14E4; // RPi VideoCore
397inline constexpr uint32_t ARM = 0x13B5; // Mali
398inline constexpr uint32_t Qualcomm = 0x5143;
399inline constexpr uint32_t Samsung = 0x144D;
400}
401
406inline std::vector<AVPixelFormat> selectHardwareAccelerations(
407 int graphicsApi, AVCodecID codec_id, uint32_t gpuVendorId = 0)
408{
409 struct Candidate { AVPixelFormat fmt; };
410 std::vector<Candidate> candidates;
411
412 switch(graphicsApi)
413 {
414#if defined(_WIN32)
415 case 3: // D3D11
416 candidates = {{AV_PIX_FMT_D3D11}, {AV_PIX_FMT_CUDA}, {AV_PIX_FMT_QSV}};
417 break;
418 case 5: // D3D12
419#if LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(58, 29, 100)
420 candidates = {{AV_PIX_FMT_D3D12}, {AV_PIX_FMT_D3D11}, {AV_PIX_FMT_CUDA}, {AV_PIX_FMT_QSV}};
421#else
422 candidates = {{AV_PIX_FMT_D3D11}, {AV_PIX_FMT_CUDA}, {AV_PIX_FMT_QSV}};
423#endif
424 break;
425#endif
426#if defined(__APPLE__)
427 case 4: // Metal
428 candidates = {{AV_PIX_FMT_VIDEOTOOLBOX}};
429 break;
430#endif
431 case 2: // Vulkan
432 {
433#if defined(__linux__)
434 if(gpuVendorId == GpuVendor::NVIDIA)
435 {
436 candidates = {{AV_PIX_FMT_VULKAN}, {AV_PIX_FMT_CUDA}, {AV_PIX_FMT_VAAPI}, {AV_PIX_FMT_QSV}};
437 }
438 else if(gpuVendorId == GpuVendor::Intel)
439 {
440 candidates = {{AV_PIX_FMT_VAAPI}, {AV_PIX_FMT_VULKAN}, {AV_PIX_FMT_QSV}};
441 }
442 else if(gpuVendorId == GpuVendor::AMD)
443 {
444 candidates = {{AV_PIX_FMT_VAAPI}, {AV_PIX_FMT_VULKAN}};
445 }
446 else if(gpuVendorId == GpuVendor::Broadcom
447 || gpuVendorId == GpuVendor::ARM
448 || gpuVendorId == GpuVendor::Qualcomm)
449 {
450 candidates = {{AV_PIX_FMT_DRM_PRIME}, {AV_PIX_FMT_VULKAN}};
451 }
452 else
453 {
454 candidates = {{AV_PIX_FMT_VAAPI}, {AV_PIX_FMT_VULKAN}, {AV_PIX_FMT_CUDA}, {AV_PIX_FMT_QSV}};
455 }
456#elif defined(_WIN32)
457 candidates = {
458 {AV_PIX_FMT_VULKAN}, {AV_PIX_FMT_D3D11},
459#if LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(58, 29, 100)
460 {AV_PIX_FMT_D3D12},
461#endif
462 {AV_PIX_FMT_CUDA}, {AV_PIX_FMT_QSV}};
463#elif defined(__APPLE__)
464 candidates = {{AV_PIX_FMT_VIDEOTOOLBOX}};
465#else
466 candidates = {{AV_PIX_FMT_CUDA}, {AV_PIX_FMT_QSV}};
467#endif
468 break;
469 }
470 case 1: // OpenGL
471 {
472#if defined(__linux__)
473 if(gpuVendorId == GpuVendor::Broadcom
474 || gpuVendorId == GpuVendor::ARM
475 || gpuVendorId == GpuVendor::Qualcomm)
476 {
477 candidates = {{AV_PIX_FMT_DRM_PRIME}};
478 }
479 else
480 {
481 candidates = {{AV_PIX_FMT_VAAPI}, {AV_PIX_FMT_CUDA}, {AV_PIX_FMT_QSV}};
482 }
483#elif defined(_WIN32)
484 candidates = {{AV_PIX_FMT_D3D11}, {AV_PIX_FMT_CUDA}, {AV_PIX_FMT_QSV}};
485#elif defined(__APPLE__)
486 candidates = {{AV_PIX_FMT_VIDEOTOOLBOX}};
487#endif
488 break;
489 }
490 default:
491 {
492#if defined(__linux__)
493#if defined(__arm__) || defined(__aarch64__)
494 candidates = {{AV_PIX_FMT_DRM_PRIME}, {AV_PIX_FMT_VAAPI}};
495#else
496 candidates = {{AV_PIX_FMT_VAAPI}, {AV_PIX_FMT_CUDA}, {AV_PIX_FMT_QSV}};
497#endif
498#elif defined(_WIN32)
499 candidates = {{AV_PIX_FMT_D3D11}, {AV_PIX_FMT_CUDA}, {AV_PIX_FMT_QSV}};
500#elif defined(__APPLE__)
501 candidates = {{AV_PIX_FMT_VIDEOTOOLBOX}};
502#endif
503 break;
504 }
505 }
506
507 std::vector<AVPixelFormat> result;
508 for(auto& c : candidates)
509 {
510 if(hardwareDecoderIsAvailable(c.fmt)
511 && codecSupportsHWPixelFormat(codec_id, c.fmt, gpuVendorId))
512 result.push_back(c.fmt);
513 }
514 return result;
515}
516
518inline AVPixelFormat selectHardwareAcceleration(
519 int graphicsApi, AVCodecID codec_id, uint32_t gpuVendorId = 0)
520{
521 auto fmts = selectHardwareAccelerations(graphicsApi, codec_id, gpuVendorId);
522 return fmts.empty() ? AV_PIX_FMT_NONE : fmts.front();
523}
524
525#if defined(__linux__)
532inline std::string preferredVAAPIRenderNode() noexcept
533{
534 for(int i = 128; i < 128 + 16; i++)
535 {
536 char link[64], drv[256];
537 snprintf(link, sizeof link, "/sys/class/drm/renderD%d/device/driver", i);
538 ssize_t n = readlink(link, drv, sizeof(drv) - 1);
539 if(n <= 0)
540 continue;
541 drv[n] = 0;
542 std::string_view driver{drv};
543 if(auto slash = driver.rfind('/'); slash != std::string_view::npos)
544 driver.remove_prefix(slash + 1);
545 if(driver != "nvidia")
546 {
547 char dev[64];
548 snprintf(dev, sizeof dev, "/dev/dri/renderD%d", i);
549 return dev;
550 }
551 }
552 return {};
553}
554#endif
555
562inline std::string hardwareDecodingDeviceName(AVHWDeviceType device) noexcept
563{
564 if(const char* env = getenv("SCORE_VIDEO_HW_DEVICE"); env && *env)
565 return env;
566#if defined(__linux__)
567 if(device == AV_HWDEVICE_TYPE_VAAPI)
568 return preferredVAAPIRenderNode();
569#endif
570 return {};
571}
572
574inline int createHardwareDevice(AVBufferRef** ctx, AVHWDeviceType device) noexcept
575{
576 const std::string name = hardwareDecodingDeviceName(device);
577 return av_hwdevice_ctx_create(
578 ctx, device, name.empty() ? nullptr : name.c_str(), nullptr, 0);
579}
580
581#endif
582}
583
584#endif
GpuVendor
Definition GpuCapabilities.hpp:50
AVPixelFormat selectHardwareAcceleration(score::gfx::GraphicsApi api, AVCodecID codec_id, QRhi *rhi)
Picks the best HW accel for the given API/codec, using QRhi for vendor detection.
Definition HWAccelSetup.cpp:36
std::string hwCodecName(const char *codec_name, AVHWDeviceType device)
Delegates to Video::hwCodecName (score-plugin-gfx wrapper)
Definition HWAccelSetup.cpp:18
bool codecSupportsHWPixelFormat(AVCodecID codec_id, AVPixelFormat pix_fmt)
Delegates to Video::codecSupportsHWPixelFormat (score-plugin-gfx wrapper)
Definition HWAccelSetup.cpp:27