diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/video_core/command_classes/codecs/codec.cpp | 88 |
1 files changed, 25 insertions, 63 deletions
diff --git a/src/video_core/command_classes/codecs/codec.cpp b/src/video_core/command_classes/codecs/codec.cpp index 2c0d8da64..2a532b883 100644 --- a/src/video_core/command_classes/codecs/codec.cpp +++ b/src/video_core/command_classes/codecs/codec.cpp | |||
| @@ -17,6 +17,10 @@ | |||
| 17 | 17 | ||
| 18 | extern "C" { | 18 | extern "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 | ||
| 22 | namespace Tegra { | 26 | namespace Tegra { |
| @@ -29,6 +33,7 @@ constexpr std::array PREFERRED_GPU_DECODERS = { | |||
| 29 | AV_HWDEVICE_TYPE_D3D11VA, | 33 | AV_HWDEVICE_TYPE_D3D11VA, |
| 30 | AV_HWDEVICE_TYPE_DXVA2, | 34 | AV_HWDEVICE_TYPE_DXVA2, |
| 31 | #elif defined(__linux__) | 35 | #elif defined(__linux__) |
| 36 | AV_HWDEVICE_TYPE_VAAPI, | ||
| 32 | AV_HWDEVICE_TYPE_VDPAU, | 37 | AV_HWDEVICE_TYPE_VDPAU, |
| 33 | #endif | 38 | #endif |
| 34 | // last resort for Linux Flatpak (w/ NVIDIA) | 39 | // last resort for Linux Flatpak (w/ NVIDIA) |
| @@ -78,79 +83,18 @@ static std::vector<AVHWDeviceType> ListSupportedContexts() { | |||
| 78 | AVHWDeviceType current_device_type = AV_HWDEVICE_TYPE_NONE; | 83 | AVHWDeviceType current_device_type = AV_HWDEVICE_TYPE_NONE; |
| 79 | do { | 84 | do { |
| 80 | current_device_type = av_hwdevice_iterate_types(current_device_type); | 85 | current_device_type = av_hwdevice_iterate_types(current_device_type); |
| 81 | // filter out VA-API since we will try that first if supported | 86 | contexts.push_back(current_device_type); |
| 82 | if (current_device_type != AV_HWDEVICE_TYPE_VAAPI) { | ||
| 83 | contexts.push_back(current_device_type); | ||
| 84 | } | ||
| 85 | } while (current_device_type != AV_HWDEVICE_TYPE_NONE); | 87 | } while (current_device_type != AV_HWDEVICE_TYPE_NONE); |
| 86 | return contexts; | 88 | return contexts; |
| 87 | } | 89 | } |
| 88 | 90 | ||
| 89 | #ifdef LIBVA_FOUND | ||
| 90 | // List all the currently loaded Linux modules | ||
| 91 | static std::vector<std::string> ListLinuxKernelModules() { | ||
| 92 | using FILEPtr = std::unique_ptr<FILE, decltype(&std::fclose)>; | ||
| 93 | auto module_listing = FILEPtr{fopen("/proc/modules", "rt"), std::fclose}; | ||
| 94 | std::vector<std::string> modules{}; | ||
| 95 | if (!module_listing) { | ||
| 96 | LOG_WARNING(Service_NVDRV, "Could not open /proc/modules to collect available modules"); | ||
| 97 | return modules; | ||
| 98 | } | ||
| 99 | char* buffer = nullptr; | ||
| 100 | size_t buf_len = 0; | ||
| 101 | while (getline(&buffer, &buf_len, module_listing.get()) != -1) { | ||
| 102 | // format for the module listing file (sysfs) | ||
| 103 | // <name> <module_size> <depended_by_count> <depended_by_names> <status> <load_address> | ||
| 104 | auto line = std::string(buffer); | ||
| 105 | // we are only interested in module names | ||
| 106 | auto name_pos = line.find_first_of(" "); | ||
| 107 | if (name_pos == std::string::npos) { | ||
| 108 | continue; | ||
| 109 | } | ||
| 110 | modules.push_back(line.erase(name_pos)); | ||
| 111 | } | ||
| 112 | free(buffer); | ||
| 113 | return modules; | ||
| 114 | } | ||
| 115 | #endif | ||
| 116 | |||
| 117 | bool Codec::CreateGpuAvDevice() { | 91 | bool Codec::CreateGpuAvDevice() { |
| 118 | #if defined(LIBVA_FOUND) | ||
| 119 | static constexpr std::array<const char*, 3> VAAPI_DRIVERS = { | ||
| 120 | "i915", | ||
| 121 | "iHD", | ||
| 122 | "amdgpu", | ||
| 123 | }; | ||
| 124 | AVDictionary* hwdevice_options = nullptr; | ||
| 125 | const auto loaded_modules = ListLinuxKernelModules(); | ||
| 126 | av_dict_set(&hwdevice_options, "connection_type", "drm", 0); | ||
| 127 | for (const auto& driver : VAAPI_DRIVERS) { | ||
| 128 | // first check if the target driver is loaded in the kernel | ||
| 129 | bool found = std::any_of(loaded_modules.begin(), loaded_modules.end(), | ||
| 130 | [&driver](const auto& module) { return module == driver; }); | ||
| 131 | if (!found) { | ||
| 132 | LOG_DEBUG(Service_NVDRV, "Kernel driver {} is not loaded, trying the next one", driver); | ||
| 133 | continue; | ||
| 134 | } | ||
| 135 | av_dict_set(&hwdevice_options, "kernel_driver", driver, 0); | ||
| 136 | const int hwdevice_error = av_hwdevice_ctx_create(&av_gpu_decoder, AV_HWDEVICE_TYPE_VAAPI, | ||
| 137 | nullptr, hwdevice_options, 0); | ||
| 138 | if (hwdevice_error >= 0) { | ||
| 139 | LOG_INFO(Service_NVDRV, "Using VA-API with {}", driver); | ||
| 140 | av_dict_free(&hwdevice_options); | ||
| 141 | av_codec_ctx->pix_fmt = AV_PIX_FMT_VAAPI; | ||
| 142 | return true; | ||
| 143 | } | ||
| 144 | LOG_DEBUG(Service_NVDRV, "VA-API av_hwdevice_ctx_create failed {}", hwdevice_error); | ||
| 145 | } | ||
| 146 | LOG_DEBUG(Service_NVDRV, "VA-API av_hwdevice_ctx_create failed for all drivers"); | ||
| 147 | av_dict_free(&hwdevice_options); | ||
| 148 | #endif | ||
| 149 | static constexpr auto HW_CONFIG_METHOD = AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX; | 92 | static constexpr auto HW_CONFIG_METHOD = AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX; |
| 150 | static const auto supported_contexts = ListSupportedContexts(); | 93 | static const auto supported_contexts = ListSupportedContexts(); |
| 151 | for (const auto& type : PREFERRED_GPU_DECODERS) { | 94 | for (const auto& type : PREFERRED_GPU_DECODERS) { |
| 152 | if (std::none_of(supported_contexts.begin(), supported_contexts.end(), | 95 | if (std::none_of(supported_contexts.begin(), supported_contexts.end(), |
| 153 | [&type](const auto& context) { return context == type; })) { | 96 | [&type](const auto& context) { return context == type; })) { |
| 97 | LOG_DEBUG(Service_NVDRV, "{} explicitly unsupported", av_hwdevice_get_type_name(type)); | ||
| 154 | continue; | 98 | continue; |
| 155 | } | 99 | } |
| 156 | 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); |
| @@ -159,6 +103,24 @@ bool Codec::CreateGpuAvDevice() { | |||
| 159 | av_hwdevice_get_type_name(type), hwdevice_res); | 103 | av_hwdevice_get_type_name(type), hwdevice_res); |
| 160 | continue; | 104 | continue; |
| 161 | } | 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 | ||
| 162 | for (int i = 0;; i++) { | 124 | for (int i = 0;; i++) { |
| 163 | const AVCodecHWConfig* config = avcodec_get_hw_config(av_codec, i); | 125 | const AVCodecHWConfig* config = avcodec_get_hw_config(av_codec, i); |
| 164 | if (!config) { | 126 | if (!config) { |