diff options
| author | 2021-11-24 17:23:57 -0700 | |
|---|---|---|
| committer | 2021-11-24 17:23:57 -0700 | |
| commit | 72aa418b0b412855683633d2799da1eb190ab6d5 (patch) | |
| tree | e155403e75b3a7b1040109e6a3a71720c0de01cd /src/video_core/command_classes | |
| parent | Merge pull request #7404 from Kewlan/per-game-framerate-cap (diff) | |
| download | yuzu-72aa418b0b412855683633d2799da1eb190ab6d5.tar.gz yuzu-72aa418b0b412855683633d2799da1eb190ab6d5.tar.xz yuzu-72aa418b0b412855683633d2799da1eb190ab6d5.zip | |
video_core/codecs: fix multiple decoding issues on Linux ...
* when someone installed Intel video drivers on an AMD system, the
decoder will select the Intel VA-API decoding driver and yuzu will
crash due to incorrect driver selection; the fix will check if the
currently about-to-use driver is loaded in the kernel
* when using NVIDIA driver on Linux with a ffmpeg that does not have
CUDA capability enabled, the decoder will crash; the fix simply
making the decoder prefers the VDPAU driver over CUDA on Linux
Diffstat (limited to 'src/video_core/command_classes')
| -rw-r--r-- | src/video_core/command_classes/codecs/codec.cpp | 49 |
1 files changed, 47 insertions, 2 deletions
diff --git a/src/video_core/command_classes/codecs/codec.cpp b/src/video_core/command_classes/codecs/codec.cpp index 916277811..403ce30fe 100644 --- a/src/video_core/command_classes/codecs/codec.cpp +++ b/src/video_core/command_classes/codecs/codec.cpp | |||
| @@ -2,6 +2,7 @@ | |||
| 2 | // Licensed under GPLv2 or any later version | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include <cstdio> | ||
| 5 | #include <fstream> | 6 | #include <fstream> |
| 6 | #include <vector> | 7 | #include <vector> |
| 7 | #include "common/assert.h" | 8 | #include "common/assert.h" |
| @@ -59,6 +60,36 @@ Codec::~Codec() { | |||
| 59 | av_buffer_unref(&av_gpu_decoder); | 60 | av_buffer_unref(&av_gpu_decoder); |
| 60 | } | 61 | } |
| 61 | 62 | ||
| 63 | #ifdef LIBVA_FOUND | ||
| 64 | // List all the currently loaded Linux modules | ||
| 65 | static std::vector<std::string> ListLinuxKernelModules() { | ||
| 66 | std::vector<std::string> modules{}; | ||
| 67 | auto module_listing = fopen("/proc/modules", "rt"); | ||
| 68 | char* buffer = nullptr; | ||
| 69 | size_t buf_len = 0; | ||
| 70 | if (!module_listing) { | ||
| 71 | LOG_WARNING(Service_NVDRV, "Could not open /proc/modules to collect available modules"); | ||
| 72 | return modules; | ||
| 73 | } | ||
| 74 | while (getline(&buffer, &buf_len, module_listing) != -1) { | ||
| 75 | // format for the module listing file (sysfs) | ||
| 76 | // <name> <module_size> <depended_by_count> <depended_by_names> <status> <load_address> | ||
| 77 | auto line = std::string(buffer); | ||
| 78 | // we are only interested in module names | ||
| 79 | auto name_pos = line.find_first_of(" "); | ||
| 80 | if (name_pos == std::string::npos) { | ||
| 81 | continue; | ||
| 82 | } | ||
| 83 | modules.push_back(line.erase(name_pos + 1)); | ||
| 84 | } | ||
| 85 | if (buffer) { | ||
| 86 | free(buffer); | ||
| 87 | } | ||
| 88 | fclose(module_listing); | ||
| 89 | return modules; | ||
| 90 | } | ||
| 91 | #endif | ||
| 92 | |||
| 62 | bool Codec::CreateGpuAvDevice() { | 93 | bool Codec::CreateGpuAvDevice() { |
| 63 | #if defined(LIBVA_FOUND) | 94 | #if defined(LIBVA_FOUND) |
| 64 | static constexpr std::array<const char*, 3> VAAPI_DRIVERS = { | 95 | static constexpr std::array<const char*, 3> VAAPI_DRIVERS = { |
| @@ -67,8 +98,21 @@ bool Codec::CreateGpuAvDevice() { | |||
| 67 | "amdgpu", | 98 | "amdgpu", |
| 68 | }; | 99 | }; |
| 69 | AVDictionary* hwdevice_options = nullptr; | 100 | AVDictionary* hwdevice_options = nullptr; |
| 101 | auto loaded_modules = ListLinuxKernelModules(); | ||
| 70 | av_dict_set(&hwdevice_options, "connection_type", "drm", 0); | 102 | av_dict_set(&hwdevice_options, "connection_type", "drm", 0); |
| 71 | for (const auto& driver : VAAPI_DRIVERS) { | 103 | for (const auto& driver : VAAPI_DRIVERS) { |
| 104 | bool found = false; | ||
| 105 | // first check if the target driver is loaded in the kernel | ||
| 106 | for (const auto& module : loaded_modules) { | ||
| 107 | if (module == driver) { | ||
| 108 | found = true; | ||
| 109 | break; | ||
| 110 | } | ||
| 111 | } | ||
| 112 | if (!found) { | ||
| 113 | LOG_DEBUG(Service_NVDRV, "Kernel driver {} is not loaded, trying the next one", driver); | ||
| 114 | continue; | ||
| 115 | } | ||
| 72 | av_dict_set(&hwdevice_options, "kernel_driver", driver, 0); | 116 | av_dict_set(&hwdevice_options, "kernel_driver", driver, 0); |
| 73 | const int hwdevice_error = av_hwdevice_ctx_create(&av_gpu_decoder, AV_HWDEVICE_TYPE_VAAPI, | 117 | const int hwdevice_error = av_hwdevice_ctx_create(&av_gpu_decoder, AV_HWDEVICE_TYPE_VAAPI, |
| 74 | nullptr, hwdevice_options, 0); | 118 | nullptr, hwdevice_options, 0); |
| @@ -85,11 +129,12 @@ bool Codec::CreateGpuAvDevice() { | |||
| 85 | #endif | 129 | #endif |
| 86 | static constexpr auto HW_CONFIG_METHOD = AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX; | 130 | static constexpr auto HW_CONFIG_METHOD = AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX; |
| 87 | static constexpr std::array GPU_DECODER_TYPES{ | 131 | static constexpr std::array GPU_DECODER_TYPES{ |
| 132 | #ifdef linux | ||
| 133 | AV_HWDEVICE_TYPE_VDPAU, | ||
| 134 | #endif | ||
| 88 | AV_HWDEVICE_TYPE_CUDA, | 135 | AV_HWDEVICE_TYPE_CUDA, |
| 89 | #ifdef _WIN32 | 136 | #ifdef _WIN32 |
| 90 | AV_HWDEVICE_TYPE_D3D11VA, | 137 | AV_HWDEVICE_TYPE_D3D11VA, |
| 91 | #else | ||
| 92 | AV_HWDEVICE_TYPE_VDPAU, | ||
| 93 | #endif | 138 | #endif |
| 94 | }; | 139 | }; |
| 95 | for (const auto& type : GPU_DECODER_TYPES) { | 140 | for (const auto& type : GPU_DECODER_TYPES) { |