Bug 1758473 - Move VA-API test into glxtest, r=stransky

Some VA-API drivers are so broken that trying to use them
crashes Firefox. This is nothing entirely new for GPU drivers
and which is why we have `glxtest`.

Thus move the test from `VAAPIUtils` there. This has the
additional benefit of only doing the test once.

Given the importance of GL-accelerated rendering these days,
we don't want failing VA-API drivers to disable hardware
Webrender and WebGL. Thus fork the VA-API test into its
own process.

Differential Revision: https://phabricator.services.mozilla.com/D148981
This commit is contained in:
Robert Mader 2022-06-21 08:45:27 +00:00
parent 6f93e32018
commit 324edb90ae
8 changed files with 232 additions and 182 deletions

View File

@ -67,7 +67,6 @@
# include <gdk/gdkwayland.h>
# include "mozilla/widget/nsWaylandDisplay.h"
# include "mozilla/widget/DMABufLibWrapper.h"
# include "mozilla/widget/VAAPIUtils.h"
# include "mozilla/StaticPrefs_widget.h"
#endif
@ -260,13 +259,6 @@ void gfxPlatformGtk::InitVAAPIConfig() {
feature.ForceDisable(FeatureStatus::Unavailable, "Requires EGL",
"FEATURE_FAILURE_REQUIRES_EGL"_ns);
}
if (feature.IsEnabled()) {
if (!VAAPIIsSupported()) {
feature.ForceDisable(FeatureStatus::Failed, "Failed to configure",
failureId);
}
}
#else
feature.DisableByDefault(FeatureStatus::Unavailable,
"Wayland support missing",

View File

@ -42,6 +42,8 @@
#ifdef MOZ_WAYLAND
# include "mozilla/widget/mozwayland.h"
# include "mozilla/widget/xdg-output-unstable-v1-client-protocol.h"
# include "prlink.h"
# include "va/va.h"
#endif
#ifdef MOZ_X11
@ -175,6 +177,8 @@ static char* glxtest_buf = nullptr;
static int glxtest_bufsize = 0;
static int glxtest_length = 0;
static char* glxtest_render_device_path = nullptr;
// C++ standard collides with C standard in that it doesn't allow casting void*
// to function pointer types. So the work-around is to convert first to size_t.
// http://www.trilithium.com/johan/2004/12/problem-with-dlsym/
@ -339,6 +343,11 @@ static int get_pci_status() {
}
#ifdef MOZ_WAYLAND
static void set_render_device_path(const char* render_device_path) {
record_value("DRM_RENDERDEVICE\n%s\n", render_device_path);
glxtest_render_device_path = strdup(render_device_path);
}
static bool device_has_name(const drmDevice* device, const char* name) {
for (size_t i = 0; i < DRM_NODE_MAX; i++) {
if (!(device->available_nodes & (1 << i))) {
@ -408,7 +417,7 @@ static bool get_render_name(const char* name) {
} else if (!(match->available_nodes & (1 << DRM_NODE_RENDER))) {
record_warning("DRM device has no render node");
} else {
record_value("DRM_RENDERDEVICE\n%s\n", match->nodes[DRM_NODE_RENDER]);
set_render_device_path(match->nodes[DRM_NODE_RENDER]);
record_value(
"MESA_VENDOR_ID\n0x%04x\n"
"MESA_DEVICE_ID\n0x%04x\n",
@ -552,7 +561,7 @@ static bool get_gles_status(EGLDisplay dpy,
const char* renderNodeString =
eglQueryDeviceStringEXT(device, EGL_DRM_RENDER_NODE_FILE_EXT);
if (renderNodeString) {
record_value("DRM_RENDERDEVICE\n%s\n", renderNodeString);
set_render_device_path(renderNodeString);
}
}
#endif
@ -926,6 +935,203 @@ static void wayland_egltest() {
wl_display_disconnect(dpy);
record_value("TEST_TYPE\nEGL\n");
}
static constexpr struct {
VAProfile mVAProfile;
nsLiteralCString mName;
} kVAAPiProfileName[] = {
# define MAP(v) \
{ VAProfile##v, nsLiteralCString(#v) }
MAP(H264ConstrainedBaseline),
MAP(H264Main),
MAP(H264High),
MAP(VP8Version0_3),
MAP(VP9Profile0),
MAP(VP9Profile2),
MAP(AV1Profile0),
MAP(AV1Profile1),
# undef MAP
};
static const char* VAProfileName(VAProfile aVAProfile) {
for (const auto& profile : kVAAPiProfileName) {
if (profile.mVAProfile == aVAProfile) {
return profile.mName.get();
}
}
return nullptr;
}
int childvaapitest() {
int renderDeviceFD = -1;
VAProfile* profiles = nullptr;
VAEntrypoint* entryPoints = nullptr;
PRLibrary* libDrm = nullptr;
VADisplay display = nullptr;
auto autoRelease = mozilla::MakeScopeExit([&] {
if (renderDeviceFD > -1) {
close(renderDeviceFD);
}
delete[] profiles;
delete[] entryPoints;
if (display) {
vaTerminate(display);
}
if (libDrm) {
PR_UnloadLibrary(libDrm);
}
});
renderDeviceFD = open(glxtest_render_device_path, O_RDWR);
if (renderDeviceFD == -1) {
return 3;
}
PRLibSpec lspec;
lspec.type = PR_LibSpec_Pathname;
const char* libName = "libva-drm.so.2";
lspec.value.pathname = libName;
libDrm = PR_LoadLibraryWithFlags(lspec, PR_LD_NOW | PR_LD_LOCAL);
if (!libDrm) {
return 4;
}
static auto sVaGetDisplayDRM =
(void* (*)(int fd))PR_FindSymbol(libDrm, "vaGetDisplayDRM");
if (!sVaGetDisplayDRM) {
return 5;
}
display = sVaGetDisplayDRM(renderDeviceFD);
if (!display) {
return 6;
}
int major, minor;
VAStatus status = vaInitialize(display, &major, &minor);
if (status != VA_STATUS_SUCCESS) {
return 7;
}
int maxProfiles = vaMaxNumProfiles(display);
int maxEntryPoints = vaMaxNumEntrypoints(display);
if (MOZ_UNLIKELY(maxProfiles <= 0 || maxEntryPoints <= 0)) {
return 8;
}
profiles = new VAProfile[maxProfiles];
int numProfiles = 0;
status = vaQueryConfigProfiles(display, profiles, &numProfiles);
if (status != VA_STATUS_SUCCESS) {
return 9;
}
numProfiles = std::min(numProfiles, maxProfiles);
entryPoints = new VAEntrypoint[maxEntryPoints];
for (int p = 0; p < numProfiles; p++) {
VAProfile profile = profiles[p];
// Check only supported profiles
if (!VAProfileName(profile)) {
continue;
}
int numEntryPoints = 0;
status = vaQueryConfigEntrypoints(display, profile, entryPoints,
&numEntryPoints);
if (status != VA_STATUS_SUCCESS) {
continue;
}
numEntryPoints = std::min(numEntryPoints, maxEntryPoints);
for (int entry = 0; entry < numEntryPoints; entry++) {
if (entryPoints[entry] != VAEntrypointVLD) {
continue;
}
VAConfigID config = VA_INVALID_ID;
status = vaCreateConfig(display, profile, entryPoints[entry], nullptr, 0,
&config);
if (status == VA_STATUS_SUCCESS) {
vaDestroyConfig(display, config);
return 0;
}
}
}
return 10;
}
static void vaapitest() {
if (!glxtest_render_device_path) {
return;
}
pid_t vaapitest_pid = fork();
if (vaapitest_pid == 0) {
int vaapirv = childvaapitest();
_exit(vaapirv);
} else if (vaapitest_pid > 0) {
int vaapitest_status = 0;
bool wait_for_vaapitest_process = true;
while (wait_for_vaapitest_process) {
if (waitpid(vaapitest_pid, &vaapitest_status, 0) == -1) {
wait_for_vaapitest_process = false;
record_warning(
"VA-API test failed: waiting for VA-API process failed.");
} else if (WIFEXITED(vaapitest_status) || WIFSIGNALED(vaapitest_status)) {
wait_for_vaapitest_process = false;
}
}
if (WIFEXITED(vaapitest_status)) {
switch (WEXITSTATUS(vaapitest_status)) {
case 0:
record_value("VAAPI_SUPPORTED\nTRUE\n");
break;
case 3:
record_warning(
"VA-API test failed: opening render device path failed.");
break;
case 4:
record_warning(
"VA-API test failed: missing or old libva-drm library.");
break;
case 5:
record_warning("VA-API test failed: missing vaGetDisplayDRM.");
break;
case 6:
record_warning("VA-API test failed: failed to get vaGetDisplayDRM.");
break;
case 7:
record_warning(
"VA-API test failed: failed to initialise VAAPI connection.");
break;
case 8:
record_warning(
"VA-API test failed: wrong VAAPI profiles/entry point nums.");
break;
case 9:
record_warning("VA-API test failed: vaQueryConfigProfiles() failed.");
break;
case 10:
record_warning(
"VA-API test failed: no supported VAAPI profile found.");
break;
default:
record_warning(
"VA-API test failed: Something unexpected went wrong.");
break;
}
} else {
record_warning(
"VA-API test failed: process crashed. Please check your VA-API "
"drivers.");
}
} else {
record_warning("VA-API test failed: Could not fork process.");
}
}
#endif
int childgltest() {
@ -954,6 +1160,10 @@ int childgltest() {
#endif
}
#ifdef MOZ_WAYLAND
vaapitest();
#endif
// Finally write buffered data to the pipe.
record_flush();

View File

@ -160,6 +160,14 @@ if CONFIG["MOZ_X11"] or CONFIG["MOZ_WAYLAND"]:
"glxtest.cpp",
]
if CONFIG["MOZ_WAYLAND"]:
LOCAL_INCLUDES += [
"/media/mozva",
]
USE_LIBS += [
"mozva",
]
if CONFIG["MOZ_INSTRUMENT_EVENT_LOOP"]:
UNIFIED_SOURCES += [
"EventTracer.cpp",

View File

@ -51,6 +51,7 @@ nsresult GfxInfo::Init() {
mIsXWayland = false;
mHasMultipleGPUs = false;
mGlxTestError = false;
mIsVAAPISupported = false;
return GfxInfoBase::Init();
}
@ -162,6 +163,7 @@ void GfxInfo::GetData() {
nsCString adapterRam;
nsCString drmRenderDevice;
nsCString isVAAPISupported;
nsCString ddxDriver;
@ -207,6 +209,8 @@ void GfxInfo::GetData() {
stringToFill = pciDevices.AppendElement();
} else if (!strcmp(line, "DRM_RENDERDEVICE")) {
stringToFill = &drmRenderDevice;
} else if (!strcmp(line, "VAAPI_SUPPORTED")) {
stringToFill = &isVAAPISupported;
} else if (!strcmp(line, "TEST_TYPE")) {
stringToFill = &testType;
} else if (!strcmp(line, "WARNING")) {
@ -268,6 +272,7 @@ void GfxInfo::GetData() {
}
mDrmRenderDevice = std::move(drmRenderDevice);
mIsVAAPISupported = isVAAPISupported.Equals("TRUE");
mTestType = std::move(testType);
// Mesa always exposes itself in the GL_VERSION string, but not always the
@ -975,6 +980,12 @@ nsresult GfxInfo::GetFeatureStatusImpl(
}
}
if (aFeature == nsIGfxInfo::FEATURE_VAAPI && !mIsVAAPISupported) {
*aStatus = nsIGfxInfo::FEATURE_BLOCKED_DEVICE;
aFailureId = "FEATURE_FAILURE_VAAPI_TEST_FAILED";
return NS_OK;
}
return GfxInfoBase::GetFeatureStatusImpl(
aFeature, aStatus, aSuggestedDriverVersion, aDriverInfo, aFailureId, &os);
}

View File

@ -111,6 +111,7 @@ class GfxInfo final : public GfxInfoBase {
bool mIsXWayland;
bool mHasMultipleGPUs;
bool mGlxTestError;
bool mIsVAAPISupported;
void AddCrashReportAnnotations();
};

View File

@ -1,153 +0,0 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim:set ts=2 sw=2 sts=2 et cindent: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "VAAPIUtils.h"
#include "va/va.h"
#include "prlink.h"
#include "mozilla/ScopeExit.h"
#include "DMABufLibWrapper.h"
namespace mozilla::widget {
static constexpr struct {
VAProfile mVAProfile;
nsLiteralCString mName;
} kVAAPiProfileName[] = {
#define MAP(v) \
{ VAProfile##v, nsLiteralCString(#v) }
MAP(H264ConstrainedBaseline),
MAP(H264Main),
MAP(H264High),
MAP(VP8Version0_3),
MAP(VP9Profile0),
MAP(VP9Profile2),
MAP(AV1Profile0),
MAP(AV1Profile1),
#undef MAP
};
static const char* VAProfileName(VAProfile aVAProfile) {
for (const auto& profile : kVAAPiProfileName) {
if (profile.mVAProfile == aVAProfile) {
return profile.mName.get();
}
}
return nullptr;
}
static nsTArray<VAProfile> VAAPIGetAcceleratedProfilesInternal() {
LOGDMABUF(("VAAPIGetAcceleratedProfiles()"));
VAProfile* profiles = nullptr;
VAEntrypoint* entryPoints = nullptr;
PRLibrary* libDrm = nullptr;
VADisplay display = nullptr;
nsTArray<VAProfile> supportedProfiles;
auto autoRelease = mozilla::MakeScopeExit([&] {
delete[] profiles;
delete[] entryPoints;
if (display) {
vaTerminate(display);
}
if (libDrm) {
PR_UnloadLibrary(libDrm);
}
});
PRLibSpec lspec;
lspec.type = PR_LibSpec_Pathname;
const char* libName = "libva-drm.so.2";
lspec.value.pathname = libName;
libDrm = PR_LoadLibraryWithFlags(lspec, PR_LD_NOW | PR_LD_LOCAL);
if (!libDrm) {
LOGDMABUF((" Missing or old %s library.", libName));
return supportedProfiles;
}
static auto sVaGetDisplayDRM =
(void* (*)(int fd))PR_FindSymbol(libDrm, "vaGetDisplayDRM");
if (!sVaGetDisplayDRM) {
LOGDMABUF((" Missing vaGetDisplayDRM()"));
return supportedProfiles;
}
display = sVaGetDisplayDRM(widget::GetDMABufDevice()->GetDRMFd());
if (!display) {
LOGDMABUF((" Failed to get vaGetDisplayDRM()"));
return supportedProfiles;
}
int major, minor;
VAStatus status = vaInitialize(display, &major, &minor);
if (status != VA_STATUS_SUCCESS) {
LOGDMABUF(
(" Failed to initialise VAAPI connection: %s.", vaErrorStr(status)));
return supportedProfiles;
}
LOGDMABUF((" Initialised VAAPI connection: version %d.%d", major, minor));
int maxProfiles = vaMaxNumProfiles(display);
int maxEntryPoints = vaMaxNumEntrypoints(display);
if (MOZ_UNLIKELY(maxProfiles <= 0 || maxEntryPoints <= 0)) {
LOGDMABUF((" Wrong profiles/entry point nums."));
return supportedProfiles;
}
profiles = new VAProfile[maxProfiles];
int numProfiles = 0;
status = vaQueryConfigProfiles(display, profiles, &numProfiles);
if (status != VA_STATUS_SUCCESS) {
LOGDMABUF((" vaQueryConfigProfiles() failed %s", vaErrorStr(status)));
return supportedProfiles;
}
numProfiles = std::min(numProfiles, maxProfiles);
entryPoints = new VAEntrypoint[maxEntryPoints];
for (int p = 0; p < numProfiles; p++) {
VAProfile profile = profiles[p];
// Check only supported profiles
if (!VAProfileName(profile)) {
continue;
}
int numEntryPoints = 0;
status = vaQueryConfigEntrypoints(display, profile, entryPoints,
&numEntryPoints);
if (status != VA_STATUS_SUCCESS) {
LOGDMABUF((" vaQueryConfigEntrypoints() failed: '%s' for profile %d",
vaErrorStr(status), (int)profile));
continue;
}
numEntryPoints = std::min(numEntryPoints, maxEntryPoints);
for (int entry = 0; entry < numEntryPoints; entry++) {
if (entryPoints[entry] != VAEntrypointVLD) {
continue;
}
VAConfigID config = VA_INVALID_ID;
status = vaCreateConfig(display, profile, entryPoints[entry], nullptr, 0,
&config);
if (status == VA_STATUS_SUCCESS) {
LOGDMABUF((" Supported profile %s:", VAProfileName(profile)));
supportedProfiles.AppendElement(profile);
vaDestroyConfig(display, config);
break;
}
}
}
return supportedProfiles;
}
static const nsTArray<VAProfile>& VAAPIGetAcceleratedProfiles() {
static const nsTArray<VAProfile> profiles =
VAAPIGetAcceleratedProfilesInternal();
return profiles;
}
bool VAAPIIsSupported() { return !VAAPIGetAcceleratedProfiles().IsEmpty(); }
} // namespace mozilla::widget

View File

@ -1,11 +0,0 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim:set ts=2 sw=2 sts=2 et cindent: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
namespace mozilla::widget {
bool VAAPIIsSupported();
}

View File

@ -94,7 +94,6 @@ if CONFIG["MOZ_WAYLAND"]:
"MozContainerWayland.cpp",
"nsClipboardWayland.cpp",
"nsWaylandDisplay.cpp",
"VAAPIUtils.cpp",
"WaylandBuffer.cpp",
"WindowSurfaceWaylandMultiBuffer.cpp",
]
@ -103,15 +102,8 @@ if CONFIG["MOZ_WAYLAND"]:
"DMABufSurface.h",
"MozContainerWayland.h",
"nsWaylandDisplay.h",
"VAAPIUtils.h",
"WaylandBuffer.h",
]
LOCAL_INCLUDES += [
"/media/mozva",
]
USE_LIBS += [
"mozva",
]
if CONFIG["MOZ_X11"] or CONFIG["MOZ_WAYLAND"]:
UNIFIED_SOURCES += [