summaryrefslogtreecommitdiffstats
path: root/video
diff options
context:
space:
mode:
authorwm4 <wm4@nowhere>2019-07-06 04:11:44 +0200
committerwm4 <wm4@nowhere>2019-09-19 20:37:05 +0200
commita17337910c9c61ba0b4959fec3cf476493107d77 (patch)
treefb43ca23cf6f814f941564893f6d3294f5646aec /video
parentc42a21c9fad9b0cb9bdb377779f7034ed06ec30a (diff)
downloadmpv-a17337910c9c61ba0b4959fec3cf476493107d77.tar.bz2
mpv-a17337910c9c61ba0b4959fec3cf476493107d77.tar.xz
vo_gpu: hwdec_vaegl: silence confusing message during probing
During probing on a system with AMD GPU, mpv used to output the following messages if hardware decoding was enabled: [ffmpeg] AVHWFramesContext: Failed to create surface: 2 (resource allocation failed). [ffmpeg] AVHWFramesContext: Unable to allocate a surface from internal buffer pool. This commit removed the message, with hopefully no other side effects. Long explanations follow, better don't read them, it's just tedious drivel about the details. People should learn to write concise commit messages, not drone on and on endlessly all while they have no fucking point. The code probes supported hardware pixel format, and checks whether they can be mapped as textures. av_hwdevice_get_hwframe_constraints() returns a list of hardware pixel formats in the valid_sw_formats field (the "sw" means software, but they're still hardware pixel formats, makes sense). This contained the format yuv420p, even though this is not a valid hardware format. Trying to create a surface of this type results in VA surface creation failure, upon which FFmpeg prints the error messages above. We'd be fine with this, except FFmpeg has a global log callback, and there's no way to suppress these messages without creating other issues. It turns out that FFmpeg's vaapi implementation returns all formats from vaQueryImageFormats() if no "hwconfig" is provided. This list includes yuv420p, which is probably supported for surface upload/download, but not as native format. Following FFmpeg's logic, it should not appear in the valid_sw_formats list, because formats for transfers are returned by another roundabout API. Idiotically, there doesn't seem to be any vaapi call that determines whether a format is a valid surface format. All mechanisms to do this are bound to a VAConfigID (= video codec or video processor), all while the actual surface creation API strangely does not take a VAConfigID (a big WTF). Also, calling the vaCreateSurfaces() API ourselves for probing is out of the question, because that functions is utterly and idiotically complex. Look at the FFmpeg code and how much effort it requires to setup a complete set of attributes - we can't duplicate this. So the only way left to do this is the most idiotic and tedious way: enumerating all VAProfile (and VAEntrypoints) to create all possible VAConfigIDs. Each of the VAConfigIDs is associated with a list of formats, which FFmpeg can return (by passing the ID along with the "hwconfig"), and which is probed separately. Note that VAConfigID actually refers to a dynamic instance of something, and creating a VAConfigID takes not only the VAProfile and the VAEntrypoint, but also an arbitrary attribute array. In theory, this means our attempt to get to know all possible configurations cannot work, but in practice this attribute array seems to be pointless for decoding and video processing, and FFmpeg doesn't use it (though the encoding path does use it). This probably just makes it _barely_ OK to do it this way. Could we discard all this probing shit, and somehow do it another way? Probably not. The EGL API for mapping surfaces doesn't even seem to provide a way to enumerate supported formats, we may not even know whether DRM/dmabuf interop is actually supported (AFAIR the EGL extensions are present even if they don't work), nor do we know whether the VAAPI driver supports this interop (not sure). So actually trying is the only way. Further, mpv initializes the decoder on a another thread, where you can't just access OpenGL state. This suckage is mostly to be blamed on OpenGL itself and its crazy thread boundedness. In theory, this could be done anyway (see how software decoding "direct rendering" tries to get around this). But to make it worse, the decoder never cares about the list of supported formats determined by this code; instead, f_autoconvert.c tries to deal with it and insert a video processor (well, good luck with this crap, I bet it doesn't even work). So this whole endeavor might be pointless, other than the fact that failed probing can disable use of vaapi (which is correct and necessary). But if you have a shovel, you don't use it to smash the flat end on the heap of shit that's piled up before you, or do you? While this method probably works, it's still orgasmically tedious. It was tedious before: we had to create a real surface, create a GL texture, map the surface with it, then destroy everything again. But the added code is tedious on its own. Highlights include the need to malloc a FFmpeg struct just to pass a single damn integer, the need to enumerate "entrypoints" for each VA profile, even though all profiles have exactly 1 entrypoint, and the kind of obnoxious way how vaapi requires you to preallocate arrays for returned things, even they could for example reasonably be returned as immutable arrays or have some other simpler API. The main grand fuckup is of course that vaapi requires a VAConfigID to query surface properties, but not for creating surfaces. This awkwardness even affected the FFmpeg API design, which has a "hwconfig" concept that is only used by vaapi (vaapi is only 1 out of 10 hardware decoding APIs supported by the FFmpeg hwcontext stuff). Maybe I'm just missing something. It's as if vaapi required setting radioactive shit on fire. Look how clean the native D3D11 code is instead. (Even the ANGLE code manages to avoid being this fucked up. Or the VDPAU code, despite supporting multiple mapping methods.) Another only barely related change is that the valid_sw_formats field can be NULL, and the API explicitly documents this. Technically, the mpv code was buggy for not checking this, although until now the FFmpeg implementation so far could not return it when we still passed NULL for the hwconfig parameter.
Diffstat (limited to 'video')
-rw-r--r--video/out/hwdec/hwdec_vaapi.c49
1 files changed, 47 insertions, 2 deletions
diff --git a/video/out/hwdec/hwdec_vaapi.c b/video/out/hwdec/hwdec_vaapi.c
index 35164ca541..9640f166dd 100644
--- a/video/out/hwdec/hwdec_vaapi.c
+++ b/video/out/hwdec/hwdec_vaapi.c
@@ -394,7 +394,8 @@ static void try_format_config(struct ra_hwdec *hw, AVVAAPIHWConfig *hwconfig)
MP_WARN(hw, "failed to retrieve libavutil frame constraints\n");
return;
}
- for (int n = 0; fc->valid_sw_formats[n] != AV_PIX_FMT_NONE; n++)
+ for (int n = 0; fc->valid_sw_formats &&
+ fc->valid_sw_formats[n] != AV_PIX_FMT_NONE; n++)
try_format_pixfmt(hw, fc->valid_sw_formats[n]);
av_hwframe_constraints_free(&fc);
}
@@ -402,10 +403,54 @@ static void try_format_config(struct ra_hwdec *hw, AVVAAPIHWConfig *hwconfig)
static void determine_working_formats(struct ra_hwdec *hw)
{
struct priv_owner *p = hw->priv;
+ VAStatus status;
+ VAProfile *profiles = NULL;
+ VAEntrypoint *entrypoints = NULL;
p->probing_formats = true;
- try_format_config(hw, NULL);
+ AVVAAPIHWConfig *hwconfig = av_hwdevice_hwconfig_alloc(p->ctx->av_device_ref);
+ if (!hwconfig) {
+ MP_WARN(hw, "Could not allocate FFmpeg AVVAAPIHWConfig\n");
+ goto done;
+ }
+
+ profiles = talloc_zero_array(NULL, VAProfile, vaMaxNumProfiles(p->display));
+ entrypoints = talloc_zero_array(NULL, VAEntrypoint,
+ vaMaxNumEntrypoints(p->display));
+ int num_profiles = 0;
+ status = vaQueryConfigProfiles(p->display, profiles, &num_profiles);
+ if (!CHECK_VA_STATUS(hw, "vaQueryConfigProfiles()"))
+ num_profiles = 0;
+
+ for (int n = 0; n < num_profiles; n++) {
+ VAProfile profile = profiles[n];
+ int num_ep = 0;
+ status = vaQueryConfigEntrypoints(p->display, profile, entrypoints,
+ &num_ep);
+ if (!CHECK_VA_STATUS(hw, "vaQueryConfigEntrypoints()"))
+ continue;
+ for (int ep = 0; ep < num_ep; ep++) {
+ VAConfigID config = VA_INVALID_ID;
+ status = vaCreateConfig(p->display, profile, entrypoints[ep],
+ NULL, 0, &config);
+ if (status != VA_STATUS_SUCCESS) {
+ MP_VERBOSE(hw, "vaCreateConfig(): '%s' for profile %d",
+ vaErrorStr(status), (int)profile);
+ continue;
+ }
+
+ hwconfig->config_id = config;
+ try_format_config(hw, hwconfig);
+
+ vaDestroyConfig(p->display, config);
+ }
+ }
+
+done:
+ av_free(hwconfig);
+ talloc_free(profiles);
+ talloc_free(entrypoints);
p->probing_formats = false;