Bug 1640053 - Implement full EGL support in glxtest, r=stransky,aosmond

1. On Wayland, use `get_egl_status()` by default
2. On X11/EGL use `x11_egltest()`, avoiding runtime dependencies on GLX
3. Avoid dlopening libgl/libgles on EGL if not needed
4. Some Wayland/X11 `ifdef` cleanups
5. Don't throw warnings when on mesa and using pci device detection

Depends on D101383

Differential Revision: https://phabricator.services.mozilla.com/D100638
This commit is contained in:
Robert Mader 2021-01-14 21:24:24 +00:00
Родитель 05284fa0b0
Коммит db1a1fe941
5 изменённых файлов: 143 добавлений и 97 удалений

Просмотреть файл

@ -32,6 +32,7 @@
#include "mozilla/Monitor.h"
#include "mozilla/Preferences.h"
#include "mozilla/StaticPrefs_layers.h"
#include "nsAppRunner.h"
#include "nsIGfxInfo.h"
#include "nsMathUtils.h"
#include "nsUnicharUtils.h"
@ -95,7 +96,11 @@ gfxPlatformGtk::gfxPlatformGtk() {
}
#endif
if (IsWaylandDisplay() || (mIsX11Display && PR_GetEnv("MOZ_X11_EGL"))) {
bool useEGLOnX11 = false;
#ifdef MOZ_X11
useEGLOnX11 = IsX11EGLEnabled();
#endif
if (IsWaylandDisplay() || useEGLOnX11) {
gfxVars::SetUseEGL(true);
}
}

Просмотреть файл

@ -21,27 +21,29 @@
#include <cstdio>
#include <cstdlib>
#include <unistd.h>
#include <dlfcn.h>
#include "nscore.h"
#include <fcntl.h>
#include <unistd.h>
#include "mozilla/Unused.h"
#include "nsAppRunner.h" // for IsWaylandDisabled on IsX11EGLEnabled
#include "stdint.h"
#ifdef __SUNPRO_CC
# include <stdio.h>
#endif
#include "X11/Xlib.h"
#include "X11/Xutil.h"
#include "mozilla/Unused.h"
#ifdef MOZ_X11
# include "X11/Xlib.h"
# include "X11/Xutil.h"
#endif
#ifdef MOZ_WAYLAND
# include "nsAppRunner.h" // for IsWaylandDisabled
# include "mozilla/widget/mozwayland.h"
# include "mozilla/widget/xdg-output-unstable-v1-client-protocol.h"
#endif
#ifdef MOZ_X11
// stuff from glx.h
typedef struct __GLXcontextRec* GLXContext;
typedef XID GLXPixmap;
@ -52,10 +54,11 @@ typedef XID GLXFBConfigID;
typedef XID GLXContextID;
typedef XID GLXWindow;
typedef XID GLXPbuffer;
#define GLX_RGBA 4
#define GLX_RED_SIZE 8
#define GLX_GREEN_SIZE 9
#define GLX_BLUE_SIZE 10
# define GLX_RGBA 4
# define GLX_RED_SIZE 8
# define GLX_GREEN_SIZE 9
# define GLX_BLUE_SIZE 10
#endif
// stuff from gl.h
typedef uint8_t GLubyte;
@ -89,6 +92,17 @@ typedef uint32_t GLenum;
#define EGL_CONTEXT_CLIENT_VERSION 0x3098
#define EGL_NO_CONTEXT nullptr
// Open libGL and load needed symbols
#if defined(__OpenBSD__) || defined(__NetBSD__)
# define LIBGL_FILENAME "libGL.so"
# define LIBGLES_FILENAME "libGLESv2.so"
# define LIBEGL_FILENAME "libEGL.so"
#else
# define LIBGL_FILENAME "libGL.so.1"
# define LIBGLES_FILENAME "libGLESv2.so.2"
# define LIBEGL_FILENAME "libEGL.so.1"
#endif
#define EXIT_FAILURE_BUFFER_TOO_SMALL 2
namespace mozilla {
@ -150,6 +164,7 @@ static void record_flush() {
mozilla::Unused << write(write_end_of_the_pipe, glxtest_buf, glxtest_length);
}
#ifdef MOZ_X11
static int x_error_handler(Display*, XErrorEvent* ev) {
record_value(
"ERROR\nX error, error_code=%d, "
@ -159,6 +174,7 @@ static int x_error_handler(Display*, XErrorEvent* ev) {
_exit(EXIT_FAILURE);
return 0;
}
#endif
// childgltest is declared inside extern "C" so that the name is not mangled.
// The name is used in build/valgrind/x86_64-pc-linux-gnu.sup to suppress
@ -166,6 +182,24 @@ static int x_error_handler(Display*, XErrorEvent* ev) {
// care about leaking memory
extern "C" {
static void close_logging() {
// we want to redirect to /dev/null stdout, stderr, and while we're at it,
// any PR logging file descriptors. To that effect, we redirect all positive
// file descriptors up to what open() returns here. In particular, 1 is stdout
// and 2 is stderr.
int fd = open("/dev/null", O_WRONLY);
for (int i = 1; i < fd; i++) {
dup2(fd, i);
}
close(fd);
if (getenv("MOZ_AVOID_OPENGL_ALTOGETHER")) {
const char* msg = "ERROR\nMOZ_AVOID_OPENGL_ALTOGETHER envvar set";
mozilla::Unused << write(write_end_of_the_pipe, msg, strlen(msg));
exit(EXIT_FAILURE);
}
}
#define PCI_FILL_IDENT 0x0001
#define PCI_FILL_CLASS 0x0020
#define PCI_BASE_CLASS_DISPLAY 0x03
@ -289,30 +323,30 @@ static void get_gles_status(EGLDisplay dpy,
return;
}
void* libgles = dlopen("libGLESv2.so.2", RTLD_LAZY);
if (!libgles) {
libgles = dlopen("libGLESv2.so", RTLD_LAZY);
}
if (!libgles) {
record_error("libGLESv2 missing");
return;
}
typedef GLubyte* (*PFNGLGETSTRING)(GLenum);
PFNGLGETSTRING glGetString =
cast<PFNGLGETSTRING>(eglGetProcAddress("glGetString"));
// Implementations disagree about whether eglGetProcAddress or dlsym
// should be used for getting functions from the actual API, see
// https://github.com/anholt/libepoxy/commit/14f24485e33816139398d1bd170d617703473738
void* libgl = nullptr;
if (!glGetString) {
// Implementations disagree about whether eglGetProcAddress or dlsym
// should be used for getting functions from the actual API, see
// https://github.com/anholt/libepoxy/commit/14f24485e33816139398d1bd170d617703473738
glGetString = cast<PFNGLGETSTRING>(dlsym(libgles, "glGetString"));
}
libgl = dlopen(LIBGL_FILENAME, RTLD_LAZY);
if (!libgl) {
libgl = dlopen(LIBGLES_FILENAME, RTLD_LAZY);
if (!libgl) {
record_error("Unable to load " LIBGL_FILENAME " or " LIBGLES_FILENAME);
return;
}
}
if (!glGetString) {
dlclose(libgles);
record_error("libGLESv2 glGetString missing");
return;
glGetString = cast<PFNGLGETSTRING>(dlsym(libgl, "glGetString"));
if (!glGetString) {
dlclose(libgl);
record_error("libGL or libGLESv2 glGetString missing");
return;
}
}
EGLint config_attrs[] = {EGL_RED_SIZE, 8, EGL_GREEN_SIZE, 8,
@ -336,14 +370,13 @@ static void get_gles_status(EGLDisplay dpy,
record_error("libGLESv2 glGetString returned null");
}
dlclose(libgles);
if (libgl) {
dlclose(libgl);
}
}
static void get_egl_status(EGLNativeDisplayType native_dpy, bool gles_test) {
void* libegl = dlopen("libEGL.so.1", RTLD_LAZY);
if (!libegl) {
libegl = dlopen("libEGL.so", RTLD_LAZY);
}
void* libegl = dlopen(LIBEGL_FILENAME, RTLD_LAZY);
if (!libegl) {
record_warning("libEGL missing");
return;
@ -410,13 +443,25 @@ static void get_egl_status(EGLNativeDisplayType native_dpy, bool gles_test) {
dlclose(libegl);
}
#ifdef MOZ_X11
static void get_x11_screen_info(Display* dpy) {
int screenCount = ScreenCount(dpy);
int defaultScreen = DefaultScreen(dpy);
if (screenCount != 0) {
record_value("SCREEN_INFO\n");
for (int idx = 0; idx < screenCount; idx++) {
Screen* scrn = ScreenOfDisplay(dpy, idx);
int current_height = scrn->height;
int current_width = scrn->width;
record_value("%dx%d:%d%s", current_width, current_height,
idx == defaultScreen ? 1 : 0,
idx == screenCount - 1 ? ";\n" : ";");
}
}
}
static void get_glx_status(int* gotGlxInfo, int* gotDriDriver) {
///// Open libGL and load needed symbols /////
#if defined(__OpenBSD__) || defined(__NetBSD__)
# define LIBGL_FILENAME "libGL.so"
#else
# define LIBGL_FILENAME "libGL.so.1"
#endif
void* libgl = dlopen(LIBGL_FILENAME, RTLD_LAZY);
if (!libgl) {
record_error(LIBGL_FILENAME " missing");
@ -568,21 +613,7 @@ static void get_glx_status(int* gotGlxInfo, int* gotDriDriver) {
}
// Get monitor information
int screenCount = ScreenCount(dpy);
int defaultScreen = DefaultScreen(dpy);
if (screenCount != 0) {
record_value("SCREEN_INFO\n");
for (int idx = 0; idx < screenCount; idx++) {
Screen* scrn = ScreenOfDisplay(dpy, idx);
int current_height = scrn->height;
int current_width = scrn->width;
record_value("%dx%d:%d%s", current_width, current_height,
idx == defaultScreen ? 1 : 0,
idx == screenCount - 1 ? ";\n" : ";");
}
}
get_x11_screen_info(dpy);
///// Clean up. Indeed, the parent process might fail to kill us (e.g. if it
///// doesn't need to check GL info) so we might be staying alive for longer
@ -595,35 +626,49 @@ static void get_glx_status(int* gotGlxInfo, int* gotDriDriver) {
XDestroyWindow(dpy, window);
XFreeColormap(dpy, swa.colormap);
#ifdef NS_FREE_PERMANENT_DATA // conditionally defined in nscore.h, don't
// forget to #include it above
# ifdef NS_FREE_PERMANENT_DATA // conditionally defined in nscore.h, don't
// forget to #include it above
XCloseDisplay(dpy);
#else
# else
// This XSync call wanted to be instead:
// XCloseDisplay(dpy);
// but this can cause 1-minute stalls on certain setups using Nouveau, see bug
// 973192
XSync(dpy, False);
#endif
# endif
dlclose(libgl);
}
static void close_logging() {
// we want to redirect to /dev/null stdout, stderr, and while we're at it,
// any PR logging file descriptors. To that effect, we redirect all positive
// file descriptors up to what open() returns here. In particular, 1 is stdout
// and 2 is stderr.
int fd = open("/dev/null", O_WRONLY);
for (int i = 1; i < fd; i++) dup2(fd, i);
close(fd);
static bool x11_egltest() {
get_egl_status(nullptr, true);
if (getenv("MOZ_AVOID_OPENGL_ALTOGETHER")) {
const char* msg = "ERROR\nMOZ_AVOID_OPENGL_ALTOGETHER envvar set";
mozilla::Unused << write(write_end_of_the_pipe, msg, strlen(msg));
exit(EXIT_FAILURE);
Display* dpy = XOpenDisplay(nullptr);
if (!dpy) {
return false;
}
XSetErrorHandler(x_error_handler);
get_x11_screen_info(dpy);
XCloseDisplay(dpy);
return true;
}
static void glxtest() {
int gotGlxInfo = 0;
int gotDriDriver = 0;
get_glx_status(&gotGlxInfo, &gotDriDriver);
if (!gotGlxInfo) {
get_egl_status(nullptr, true);
} 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);
}
}
#endif
#ifdef MOZ_WAYLAND
typedef void (*print_info_t)(void* info);
@ -937,20 +982,6 @@ static bool wayland_egltest() {
}
#endif
static void glxtest() {
int gotGlxInfo = 0;
int gotDriDriver = 0;
get_glx_status(&gotGlxInfo, &gotDriDriver);
if (!gotGlxInfo) {
get_egl_status(nullptr, true);
} 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);
}
}
int childgltest() {
enum { bufsize = 2048 };
char buf[bufsize];
@ -963,15 +994,21 @@ int childgltest() {
// Get a list of all GPUs from the PCI bus.
get_pci_status();
// TODO: --display command line argument is not properly handled
// NOTE: prefers X for now because eglQueryRendererIntegerMESA does not
// exist yet
bool result = false;
#ifdef MOZ_WAYLAND
if (IsWaylandDisabled() || getenv("DISPLAY") || !wayland_egltest())
if (!IsWaylandDisabled()) {
result = wayland_egltest();
}
#endif
{
#ifdef MOZ_X11
// TODO: --display command line argument is not properly handled
if (!result && IsX11EGLEnabled()) {
result = x11_egltest();
}
if (!result) {
glxtest();
}
#endif
// Finally write buffered data to the pipe.
record_flush();

Просмотреть файл

@ -4124,6 +4124,13 @@ 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;

Просмотреть файл

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

Просмотреть файл

@ -304,16 +304,10 @@ void GfxInfo::GetData() {
if (!mesaVendor.IsEmpty()) {
mVendorId = mesaVendor;
} else {
NS_WARNING(
"Failed to get Mesa vendor ID! GLX_MESA_query_renderer unsupported?");
}
if (!mesaDevice.IsEmpty()) {
mDeviceId = mesaDevice;
} else {
NS_WARNING(
"Failed to get Mesa device ID! GLX_MESA_query_renderer unsupported?");
}
} else if (glVendor.EqualsLiteral("NVIDIA Corporation")) {
CopyUTF16toUTF8(GfxDriverInfo::GetDeviceVendor(DeviceVendor::NVIDIA),