Bug 1680512 - Prefer EGL for glxtest detection and fallback to GLX if unavailable. r=rmader

As we make the transition to using EGL over GLX, we will need our
detection code to be sufficient without EGL to determine the device in
use. This patch makes us always use the EGL testing code over the GLX
testing code, regardless of the pref/envvar setting.

At the very least, we need to know the vendor ID of the device in use.
We can determine this if there is only one GPU on the PCI list, if we
get a driver name from Mesa, or if it is a proprietary driver (i.e.
NVIDIA) which includes its name in the vendor ID. If we know the vendor
ID, we can usually derive the device ID from the PCI list.

We now also track which path glxtest took. If we successfully did the
test via EGL, then we will allow the pref/envvar to use EGL instead of
GLX. If the test reverted to GLX, then it will use GLX regardless of the
pref/envvar. This is necessary because we need to know if the libraries
are available or not -- some systems may be missing one or the other.

Differential Revision: https://phabricator.services.mozilla.com/D102933
This commit is contained in:
Andrew Osmond 2021-01-25 19:41:04 +00:00
parent 48c01f0a8b
commit cb82d29e76
15 changed files with 108 additions and 39 deletions

View File

@ -158,6 +158,9 @@ class MockGfxInfo final : public nsIGfxInfo {
NS_IMETHOD GetDesktopEnvironment(nsAString& aDesktopEnvironment) override {
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHOD GetTestType(nsAString& aTestType) override {
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHOD GetAdapterDescription(nsAString& aAdapterDescription) override {
return NS_ERROR_NOT_IMPLEMENTED;
}

View File

@ -31,6 +31,7 @@
#include "mozilla/gfx/Logging.h"
#include "mozilla/Monitor.h"
#include "mozilla/Preferences.h"
#include "mozilla/StaticPrefs_gfx.h"
#include "mozilla/StaticPrefs_layers.h"
#include "nsAppRunner.h"
#include "nsIGfxInfo.h"
@ -80,6 +81,15 @@ static void screen_resolution_changed(GdkScreen* aScreen, GParamSpec* aPspec,
sDPI = 0;
}
#if defined(MOZ_X11)
// TODO(aosmond): The envvar is deprecated. We should remove it once EGL is the
// default in release.
static bool IsX11EGLEnvvarEnabled() {
const char* eglPref = PR_GetEnv("MOZ_X11_EGL");
return (eglPref && *eglPref);
}
#endif
gfxPlatformGtk::gfxPlatformGtk() {
if (!gfxPlatform::IsHeadless()) {
gtk_init(nullptr, nullptr);
@ -98,7 +108,17 @@ gfxPlatformGtk::gfxPlatformGtk() {
bool useEGLOnX11 = false;
#ifdef MOZ_X11
useEGLOnX11 = IsX11EGLEnabled();
useEGLOnX11 =
StaticPrefs::gfx_prefer_x11_egl_AtStartup() || IsX11EGLEnvvarEnabled();
if (useEGLOnX11) {
nsCOMPtr<nsIGfxInfo> gfxInfo = services::GetGfxInfo();
nsAutoString testType;
gfxInfo->GetTestType(testType);
// We can only use X11/EGL if we actually found the EGL library and
// successfully use it to determine system information in glxtest.
useEGLOnX11 = testType == u"EGL";
}
#endif
if (IsWaylandDisplay() || useEGLOnX11) {
gfxVars::SetUseEGL(true);

View File

@ -4447,6 +4447,12 @@
value: false
mirror: always
# Prefer EGL over GLX if available.
- name: gfx.prefer-x11-egl
type: bool
value: false
mirror: once
- name: gfx.testing.device-fail
type: RelaxedAtomicBool
value: false

View File

@ -243,14 +243,14 @@ static void close_logging() {
#define PCI_FILL_CLASS 0x0020
#define PCI_BASE_CLASS_DISPLAY 0x03
static void get_pci_status() {
static int get_pci_status() {
void* libpci = dlopen("libpci.so.3", RTLD_LAZY);
if (!libpci) {
libpci = dlopen("libpci.so", RTLD_LAZY);
}
if (!libpci) {
record_warning("libpci missing");
return;
return 0;
}
typedef struct pci_dev {
@ -295,23 +295,25 @@ static void get_pci_status() {
if (!pci_alloc || !pci_cleanup || !pci_scan_bus || !pci_fill_info) {
dlclose(libpci);
record_warning("libpci missing methods");
return;
return 0;
}
pci_access* pacc = pci_alloc();
if (!pacc) {
dlclose(libpci);
record_warning("libpci alloc failed");
return;
return 0;
}
pci_init(pacc);
pci_scan_bus(pacc);
int count = 0;
for (pci_dev* dev = pacc->devices; dev; dev = dev->next) {
pci_fill_info(dev, PCI_FILL_IDENT | PCI_FILL_CLASS);
if (dev->device_class >> 8 == PCI_BASE_CLASS_DISPLAY && dev->vendor_id &&
dev->device_id) {
++count;
record_value("PCI_VENDOR_ID\n0x%04x\nPCI_DEVICE_ID\n0x%04x\n",
dev->vendor_id, dev->device_id);
}
@ -319,6 +321,7 @@ static void get_pci_status() {
pci_cleanup(pacc);
dlclose(libpci);
return count;
}
#ifdef MOZ_WAYLAND
@ -404,7 +407,7 @@ static char* get_render_name(const char* name) {
}
#endif
static void get_gles_status(EGLDisplay dpy,
static bool get_gles_status(EGLDisplay dpy,
PFNEGLGETPROCADDRESS eglGetProcAddress) {
typedef EGLBoolean (*PFNEGLCHOOSECONFIGPROC)(
EGLDisplay dpy, EGLint const* attrib_list, EGLConfig* configs,
@ -444,7 +447,7 @@ static void get_gles_status(EGLDisplay dpy,
if (!eglChooseConfig || !eglCreateContext || !eglCreatePbufferSurface ||
!eglMakeCurrent) {
record_error("libEGL missing methods for GLES test");
return;
return false;
}
typedef GLubyte* (*PFNGLGETSTRING)(GLenum);
@ -460,8 +463,8 @@ static void get_gles_status(EGLDisplay dpy,
if (!libgl) {
libgl = dlopen(LIBGLES_FILENAME, RTLD_LAZY);
if (!libgl) {
record_error("Unable to load " LIBGL_FILENAME " or " LIBGLES_FILENAME);
return;
record_warning(LIBGL_FILENAME " and " LIBGLES_FILENAME " missing");
return false;
}
}
@ -469,7 +472,7 @@ static void get_gles_status(EGLDisplay dpy,
if (!glGetString) {
dlclose(libgl);
record_error("libGL or libGLESv2 glGetString missing");
return;
return false;
}
}
@ -519,13 +522,15 @@ static void get_gles_status(EGLDisplay dpy,
if (libgl) {
dlclose(libgl);
}
return true;
}
static void get_egl_status(EGLNativeDisplayType native_dpy, bool gles_test) {
static bool get_egl_status(EGLNativeDisplayType native_dpy, bool gles_test,
bool require_driver) {
void* libegl = dlopen(LIBEGL_FILENAME, RTLD_LAZY);
if (!libegl) {
record_warning("libEGL missing");
return;
return false;
}
PFNEGLGETPROCADDRESS eglGetProcAddress =
@ -534,7 +539,7 @@ static void get_egl_status(EGLNativeDisplayType native_dpy, bool gles_test) {
if (!eglGetProcAddress) {
dlclose(libegl);
record_error("no eglGetProcAddress");
return;
return false;
}
typedef EGLDisplay (*PFNEGLGETDISPLAYPROC)(void* native_display);
@ -553,25 +558,21 @@ static void get_egl_status(EGLNativeDisplayType native_dpy, bool gles_test) {
if (!eglGetDisplay || !eglInitialize || !eglTerminate) {
dlclose(libegl);
record_error("libEGL missing methods");
return;
return false;
}
EGLDisplay dpy = eglGetDisplay(native_dpy);
if (!dpy) {
dlclose(libegl);
record_warning("libEGL no display");
return;
return false;
}
EGLint major, minor;
if (!eglInitialize(dpy, &major, &minor)) {
dlclose(libegl);
record_warning("libEGL initialize failed");
return;
}
if (gles_test) {
get_gles_status(dpy, eglGetProcAddress);
return false;
}
typedef const char* (*PFNEGLGETDISPLAYDRIVERNAMEPROC)(EGLDisplay dpy);
@ -579,14 +580,30 @@ static void get_egl_status(EGLNativeDisplayType native_dpy, bool gles_test) {
cast<PFNEGLGETDISPLAYDRIVERNAMEPROC>(
eglGetProcAddress("eglGetDisplayDriverName"));
if (eglGetDisplayDriverName) {
// TODO(aosmond): If the driver name is empty, we probably aren't using Mesa
// and instead a proprietary GL, most likely NVIDIA's. The PCI device list
// in combination with the vendor name is very likely sufficient to identify
// the device.
const char* driDriver = eglGetDisplayDriverName(dpy);
if (driDriver) {
record_value("DRI_DRIVER\n%s\n", driDriver);
}
} else if (require_driver) {
record_warning("libEGL missing eglGetDisplayDriverName");
eglTerminate(dpy);
dlclose(libegl);
return false;
}
if (gles_test && !get_gles_status(dpy, eglGetProcAddress)) {
eglTerminate(dpy);
dlclose(libegl);
return false;
}
eglTerminate(dpy);
dlclose(libegl);
return true;
}
#ifdef MOZ_X11
@ -786,8 +803,10 @@ static void get_glx_status(int* gotGlxInfo, int* gotDriDriver) {
dlclose(libgl);
}
static bool x11_egltest() {
get_egl_status(nullptr, true);
static bool x11_egltest(int pci_count) {
if (!get_egl_status(nullptr, true, pci_count != 1)) {
return false;
}
Display* dpy = XOpenDisplay(nullptr);
if (!dpy) {
@ -798,6 +817,7 @@ static bool x11_egltest() {
get_x11_screen_info(dpy);
XCloseDisplay(dpy);
record_value("TEST_TYPE\nEGL\n");
return true;
}
@ -807,12 +827,14 @@ static void glxtest() {
get_glx_status(&gotGlxInfo, &gotDriDriver);
if (!gotGlxInfo) {
get_egl_status(nullptr, true);
get_egl_status(nullptr, true, false);
} else if (!gotDriDriver) {
// If we failed to get the driver name from X, try via
// EGL_MESA_query_driver. We are probably using Wayland.
get_egl_status(nullptr, false);
get_egl_status(nullptr, false, true);
}
record_value("TEST_TYPE\nGLX\n");
}
#endif
@ -1141,10 +1163,11 @@ static bool wayland_egltest() {
return false;
}
get_egl_status((EGLNativeDisplayType)dpy, true);
get_egl_status((EGLNativeDisplayType)dpy, true, false);
get_wayland_screen_info(dpy);
wl_display_disconnect(dpy);
record_value("TEST_TYPE\nEGL\n");
return true;
}
#endif
@ -1159,7 +1182,7 @@ int childgltest() {
glxtest_bufsize = bufsize;
// Get a list of all GPUs from the PCI bus.
get_pci_status();
int pci_count = get_pci_status();
bool result = false;
#ifdef MOZ_WAYLAND
@ -1169,8 +1192,8 @@ int childgltest() {
#endif
#ifdef MOZ_X11
// TODO: --display command line argument is not properly handled
if (!result && IsX11EGLEnabled()) {
result = x11_egltest();
if (!result) {
result = x11_egltest(pci_count);
}
if (!result) {
glxtest();

View File

@ -4124,13 +4124,6 @@ bool IsWaylandDisabled() {
}
#endif
#if defined(MOZ_X11)
bool IsX11EGLEnabled() {
const char* eglPref = PR_GetEnv("MOZ_X11_EGL");
return (eglPref && *eglPref);
}
#endif
namespace mozilla::startup {
Result<nsCOMPtr<nsIFile>, nsresult> GetIncompleteStartupFile(nsIFile* aProfLD) {
nsCOMPtr<nsIFile> crashFile;

View File

@ -162,8 +162,5 @@ void setASanReporterPath(nsIFile* aDir);
#ifdef MOZ_WAYLAND
bool IsWaylandDisabled();
#endif
#ifdef MOZ_X11
bool IsX11EGLEnabled();
#endif
#endif // nsAppRunner_h__

View File

@ -157,6 +157,7 @@ void GfxInfo::GetData() {
nsCString glRenderer;
nsCString glVersion;
nsCString textureFromPixmap;
nsCString testType;
// Available if GLX_MESA_query_renderer is supported.
nsCString mesaVendor;
@ -211,6 +212,8 @@ void GfxInfo::GetData() {
stringToFill = pciDevices.AppendElement();
} else if (!strcmp(line, "DRM_RENDERDEVICE")) {
stringToFill = &drmRenderDevice;
} else if (!strcmp(line, "TEST_TYPE")) {
stringToFill = &testType;
} else if (!strcmp(line, "WARNING")) {
logString = true;
} else if (!strcmp(line, "ERROR")) {
@ -270,6 +273,7 @@ void GfxInfo::GetData() {
}
mDrmRenderDevice = std::move(drmRenderDevice);
mTestType = std::move(testType);
// Mesa always exposes itself in the GL_VERSION string, but not always the
// GL_VENDOR string.
@ -582,7 +586,7 @@ void GfxInfo::GetData() {
}
}
if (error || errorLog) {
if (error || errorLog || mTestType.IsEmpty()) {
if (!mAdapterDescription.IsEmpty()) {
mAdapterDescription.AppendLiteral(" (See failure log)");
} else {
@ -959,6 +963,13 @@ GfxInfo::GetDesktopEnvironment(nsAString& aDesktopEnvironment) {
return NS_OK;
}
NS_IMETHODIMP
GfxInfo::GetTestType(nsAString& aTestType) {
GetData();
AppendASCIItoUTF16(mTestType, aTestType);
return NS_OK;
}
NS_IMETHODIMP
GfxInfo::GetAdapterDescription(nsAString& aAdapterDescription) {
GetData();

View File

@ -27,6 +27,7 @@ class GfxInfo final : public GfxInfoBase {
NS_IMETHOD GetCleartypeParameters(nsAString& aCleartypeParams) override;
NS_IMETHOD GetWindowProtocol(nsAString& aWindowProtocol) override;
NS_IMETHOD GetDesktopEnvironment(nsAString& aDesktopEnvironment) override;
NS_IMETHOD GetTestType(nsAString& aTestType) override;
NS_IMETHOD GetAdapterDescription(nsAString& aAdapterDescription) override;
NS_IMETHOD GetAdapterDriver(nsAString& aAdapterDriver) override;
NS_IMETHOD GetAdapterVendorID(nsAString& aAdapterVendorID) override;
@ -89,6 +90,7 @@ class GfxInfo final : public GfxInfoBase {
nsCString mOS;
nsCString mOSRelease;
nsAutoCStringN<16> mDesktopEnvironment;
nsCString mTestType;
nsCString mSecondaryVendorId;
nsCString mSecondaryDeviceId;

View File

@ -156,6 +156,9 @@ GfxInfo::GetDesktopEnvironment(nsAString& aDesktopEnvironment) {
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
GfxInfo::GetTestType(nsAString& aTestType) { return NS_ERROR_NOT_IMPLEMENTED; }
void GfxInfo::EnsureInitialized() {
if (mInitialized) return;

View File

@ -36,6 +36,7 @@ class GfxInfo : public GfxInfoBase {
NS_IMETHOD GetCleartypeParameters(nsAString& aCleartypeParams) override;
NS_IMETHOD GetWindowProtocol(nsAString& aWindowProtocol) override;
NS_IMETHOD GetDesktopEnvironment(nsAString& aDesktopEnvironment) override;
NS_IMETHOD GetTestType(nsAString& aTestType) override;
NS_IMETHOD GetAdapterDescription(nsAString& aAdapterDescription) override;
NS_IMETHOD GetAdapterDriver(nsAString& aAdapterDriver) override;
NS_IMETHOD GetAdapterVendorID(nsAString& aAdapterVendorID) override;

View File

@ -28,6 +28,7 @@ class GfxInfo : public GfxInfoBase {
NS_IMETHOD GetHasBattery(bool* aHasBattery) override;
NS_IMETHOD GetWindowProtocol(nsAString& aWindowProtocol) override;
NS_IMETHOD GetDesktopEnvironment(nsAString& aDesktopEnvironment) override;
NS_IMETHOD GetTestType(nsAString& aTestType) override;
NS_IMETHOD GetCleartypeParameters(nsAString& aCleartypeParams) override;
NS_IMETHOD GetAdapterDescription(nsAString& aAdapterDescription) override;
NS_IMETHOD GetAdapterDriver(nsAString& aAdapterDriver) override;

View File

@ -210,6 +210,10 @@ GfxInfo::GetWindowProtocol(nsAString& aWindowProtocol) { return NS_ERROR_NOT_IMP
NS_IMETHODIMP
GfxInfo::GetDesktopEnvironment(nsAString& aDesktopEnvironment) { return NS_ERROR_NOT_IMPLEMENTED; }
/* readonly attribute DOMString testType; */
NS_IMETHODIMP
GfxInfo::GetTestType(nsAString& aTestType) { return NS_ERROR_NOT_IMPLEMENTED; }
/* readonly attribute DOMString adapterDescription; */
NS_IMETHODIMP
GfxInfo::GetAdapterDescription(nsAString& aAdapterDescription) {

View File

@ -26,6 +26,7 @@ interface nsIGfxInfo : nsISupports
*/
readonly attribute AString windowProtocol;
readonly attribute AString desktopEnvironment;
readonly attribute AString testType;
/*
* These are valid across all platforms.

View File

@ -169,6 +169,9 @@ GfxInfo::GetDesktopEnvironment(nsAString& aDesktopEnvironment) {
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
GfxInfo::GetTestType(nsAString& aTestType) { return NS_ERROR_NOT_IMPLEMENTED; }
static nsresult GetKeyValue(const WCHAR* keyLocation, const WCHAR* keyName,
uint32_t& destValue, int type) {
MOZ_ASSERT(type == REG_DWORD || type == REG_QWORD);

View File

@ -30,6 +30,7 @@ class GfxInfo : public GfxInfoBase {
NS_IMETHOD GetCleartypeParameters(nsAString& aCleartypeParams) override;
NS_IMETHOD GetWindowProtocol(nsAString& aWindowProtocol) override;
NS_IMETHOD GetDesktopEnvironment(nsAString& aDesktopEnvironment) override;
NS_IMETHOD GetTestType(nsAString& aTestType) override;
NS_IMETHOD GetAdapterDescription(nsAString& aAdapterDescription) override;
NS_IMETHOD GetAdapterDriver(nsAString& aAdapterDriver) override;
NS_IMETHOD GetAdapterVendorID(nsAString& aAdapterVendorID) override;