summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorGravatar bunnei2021-12-03 17:11:12 -0800
committerGravatar GitHub2021-12-03 17:11:12 -0800
commite482dd82b9a071fbd633252f42b2bb4c05fb9b5f (patch)
treec5acf9189a1cfda88fed5ecc73767d82b0383295 /src
parentMerge pull request #7489 from Morph1984/steady-clock (diff)
parentvideo_core/cmake: link against libva explicitly ... (diff)
downloadyuzu-e482dd82b9a071fbd633252f42b2bb4c05fb9b5f.tar.gz
yuzu-e482dd82b9a071fbd633252f42b2bb4c05fb9b5f.tar.xz
yuzu-e482dd82b9a071fbd633252f42b2bb4c05fb9b5f.zip
Merge pull request #7467 from liushuyu/fix-linux-decoding
video_core/codecs: more robust ffmpeg hwdecoder selection logic
Diffstat (limited to '')
-rw-r--r--src/video_core/CMakeLists.txt1
-rw-r--r--src/video_core/command_classes/codecs/codec.cpp115
2 files changed, 50 insertions, 66 deletions
diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt
index 91a30fef7..6a6325e38 100644
--- a/src/video_core/CMakeLists.txt
+++ b/src/video_core/CMakeLists.txt
@@ -3,6 +3,7 @@ add_subdirectory(host_shaders)
3if(LIBVA_FOUND) 3if(LIBVA_FOUND)
4 set_source_files_properties(command_classes/codecs/codec.cpp 4 set_source_files_properties(command_classes/codecs/codec.cpp
5 PROPERTIES COMPILE_DEFINITIONS LIBVA_FOUND=1) 5 PROPERTIES COMPILE_DEFINITIONS LIBVA_FOUND=1)
6 list(APPEND FFmpeg_LIBRARIES ${LIBVA_LIBRARIES})
6endif() 7endif()
7 8
8add_library(video_core STATIC 9add_library(video_core STATIC
diff --git a/src/video_core/command_classes/codecs/codec.cpp b/src/video_core/command_classes/codecs/codec.cpp
index 02d309170..2a532b883 100644
--- a/src/video_core/command_classes/codecs/codec.cpp
+++ b/src/video_core/command_classes/codecs/codec.cpp
@@ -17,12 +17,28 @@
17 17
18extern "C" { 18extern "C" {
19#include <libavutil/opt.h> 19#include <libavutil/opt.h>
20#ifdef LIBVA_FOUND
21// for querying VAAPI driver information
22#include <libavutil/hwcontext_vaapi.h>
23#endif
20} 24}
21 25
22namespace Tegra { 26namespace Tegra {
23namespace { 27namespace {
24constexpr AVPixelFormat PREFERRED_GPU_FMT = AV_PIX_FMT_NV12; 28constexpr AVPixelFormat PREFERRED_GPU_FMT = AV_PIX_FMT_NV12;
25constexpr AVPixelFormat PREFERRED_CPU_FMT = AV_PIX_FMT_YUV420P; 29constexpr AVPixelFormat PREFERRED_CPU_FMT = AV_PIX_FMT_YUV420P;
30constexpr std::array PREFERRED_GPU_DECODERS = {
31 AV_HWDEVICE_TYPE_CUDA,
32#ifdef _WIN32
33 AV_HWDEVICE_TYPE_D3D11VA,
34 AV_HWDEVICE_TYPE_DXVA2,
35#elif defined(__linux__)
36 AV_HWDEVICE_TYPE_VAAPI,
37 AV_HWDEVICE_TYPE_VDPAU,
38#endif
39 // last resort for Linux Flatpak (w/ NVIDIA)
40 AV_HWDEVICE_TYPE_VULKAN,
41};
26 42
27void AVPacketDeleter(AVPacket* ptr) { 43void AVPacketDeleter(AVPacket* ptr) {
28 av_packet_free(&ptr); 44 av_packet_free(&ptr);
@@ -61,83 +77,50 @@ Codec::~Codec() {
61 av_buffer_unref(&av_gpu_decoder); 77 av_buffer_unref(&av_gpu_decoder);
62} 78}
63 79
64#ifdef LIBVA_FOUND 80// List all the currently available hwcontext in ffmpeg
65// List all the currently loaded Linux modules 81static std::vector<AVHWDeviceType> ListSupportedContexts() {
66static std::vector<std::string> ListLinuxKernelModules() { 82 std::vector<AVHWDeviceType> contexts{};
67 using FILEPtr = std::unique_ptr<FILE, decltype(&std::fclose)>; 83 AVHWDeviceType current_device_type = AV_HWDEVICE_TYPE_NONE;
68 auto module_listing = FILEPtr{fopen("/proc/modules", "rt"), std::fclose}; 84 do {
69 std::vector<std::string> modules{}; 85 current_device_type = av_hwdevice_iterate_types(current_device_type);
70 if (!module_listing) { 86 contexts.push_back(current_device_type);
71 LOG_WARNING(Service_NVDRV, "Could not open /proc/modules to collect available modules"); 87 } while (current_device_type != AV_HWDEVICE_TYPE_NONE);
72 return modules; 88 return contexts;
73 }
74 char* buffer = nullptr;
75 size_t buf_len = 0;
76 while (getline(&buffer, &buf_len, module_listing.get()) != -1) {
77 // format for the module listing file (sysfs)
78 // <name> <module_size> <depended_by_count> <depended_by_names> <status> <load_address>
79 auto line = std::string(buffer);
80 // we are only interested in module names
81 auto name_pos = line.find_first_of(" ");
82 if (name_pos == std::string::npos) {
83 continue;
84 }
85 modules.push_back(line.erase(name_pos));
86 }
87 free(buffer);
88 return modules;
89} 89}
90#endif
91 90
92bool Codec::CreateGpuAvDevice() { 91bool Codec::CreateGpuAvDevice() {
93#if defined(LIBVA_FOUND) 92 static constexpr auto HW_CONFIG_METHOD = AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX;
94 static constexpr std::array<const char*, 3> VAAPI_DRIVERS = { 93 static const auto supported_contexts = ListSupportedContexts();
95 "i915", 94 for (const auto& type : PREFERRED_GPU_DECODERS) {
96 "iHD", 95 if (std::none_of(supported_contexts.begin(), supported_contexts.end(),
97 "amdgpu", 96 [&type](const auto& context) { return context == type; })) {
98 }; 97 LOG_DEBUG(Service_NVDRV, "{} explicitly unsupported", av_hwdevice_get_type_name(type));
99 AVDictionary* hwdevice_options = nullptr;
100 const auto loaded_modules = ListLinuxKernelModules();
101 av_dict_set(&hwdevice_options, "connection_type", "drm", 0);
102 for (const auto& driver : VAAPI_DRIVERS) {
103 // first check if the target driver is loaded in the kernel
104 bool found = std::any_of(loaded_modules.begin(), loaded_modules.end(),
105 [&driver](const auto& module) { return module == driver; });
106 if (!found) {
107 LOG_DEBUG(Service_NVDRV, "Kernel driver {} is not loaded, trying the next one", driver);
108 continue; 98 continue;
109 } 99 }
110 av_dict_set(&hwdevice_options, "kernel_driver", driver, 0);
111 const int hwdevice_error = av_hwdevice_ctx_create(&av_gpu_decoder, AV_HWDEVICE_TYPE_VAAPI,
112 nullptr, hwdevice_options, 0);
113 if (hwdevice_error >= 0) {
114 LOG_INFO(Service_NVDRV, "Using VA-API with {}", driver);
115 av_dict_free(&hwdevice_options);
116 av_codec_ctx->pix_fmt = AV_PIX_FMT_VAAPI;
117 return true;
118 }
119 LOG_DEBUG(Service_NVDRV, "VA-API av_hwdevice_ctx_create failed {}", hwdevice_error);
120 }
121 LOG_DEBUG(Service_NVDRV, "VA-API av_hwdevice_ctx_create failed for all drivers");
122 av_dict_free(&hwdevice_options);
123#endif
124 static constexpr auto HW_CONFIG_METHOD = AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX;
125 static constexpr std::array GPU_DECODER_TYPES{
126#ifdef linux
127 AV_HWDEVICE_TYPE_VDPAU,
128#endif
129 AV_HWDEVICE_TYPE_CUDA,
130#ifdef _WIN32
131 AV_HWDEVICE_TYPE_D3D11VA,
132#endif
133 };
134 for (const auto& type : GPU_DECODER_TYPES) {
135 const int hwdevice_res = av_hwdevice_ctx_create(&av_gpu_decoder, type, nullptr, nullptr, 0); 100 const int hwdevice_res = av_hwdevice_ctx_create(&av_gpu_decoder, type, nullptr, nullptr, 0);
136 if (hwdevice_res < 0) { 101 if (hwdevice_res < 0) {
137 LOG_DEBUG(Service_NVDRV, "{} av_hwdevice_ctx_create failed {}", 102 LOG_DEBUG(Service_NVDRV, "{} av_hwdevice_ctx_create failed {}",
138 av_hwdevice_get_type_name(type), hwdevice_res); 103 av_hwdevice_get_type_name(type), hwdevice_res);
139 continue; 104 continue;
140 } 105 }
106#ifdef LIBVA_FOUND
107 if (type == AV_HWDEVICE_TYPE_VAAPI) {
108 // we need to determine if this is an impersonated VAAPI driver
109 AVHWDeviceContext* hwctx =
110 static_cast<AVHWDeviceContext*>(static_cast<void*>(av_gpu_decoder->data));
111 AVVAAPIDeviceContext* vactx = static_cast<AVVAAPIDeviceContext*>(hwctx->hwctx);
112 const char* vendor_name = vaQueryVendorString(vactx->display);
113 if (strstr(vendor_name, "VDPAU backend")) {
114 // VDPAU impersonated VAAPI impl's are super buggy, we need to skip them
115 LOG_DEBUG(Service_NVDRV, "Skipping vdapu impersonated VAAPI driver");
116 continue;
117 } else {
118 // according to some user testing, certain vaapi driver (Intel?) could be buggy
119 // so let's log the driver name which may help the developers/supporters
120 LOG_DEBUG(Service_NVDRV, "Using VAAPI driver: {}", vendor_name);
121 }
122 }
123#endif
141 for (int i = 0;; i++) { 124 for (int i = 0;; i++) {
142 const AVCodecHWConfig* config = avcodec_get_hw_config(av_codec, i); 125 const AVCodecHWConfig* config = avcodec_get_hw_config(av_codec, i);
143 if (!config) { 126 if (!config) {