summaryrefslogtreecommitdiffstats
path: root/widget/gtk/GfxInfo.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'widget/gtk/GfxInfo.cpp')
-rw-r--r--widget/gtk/GfxInfo.cpp1406
1 files changed, 1406 insertions, 0 deletions
diff --git a/widget/gtk/GfxInfo.cpp b/widget/gtk/GfxInfo.cpp
new file mode 100644
index 0000000000..0c1c544b8f
--- /dev/null
+++ b/widget/gtk/GfxInfo.cpp
@@ -0,0 +1,1406 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=8 et :
+ */
+/* 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 "GfxInfo.h"
+
+#include <cctype>
+#include <errno.h>
+#include <unistd.h>
+#include <string>
+#include <poll.h>
+#include <sys/types.h>
+#include <sys/utsname.h>
+#include <sys/wait.h>
+#include <glib.h>
+#include <fcntl.h>
+
+#include "mozilla/gfx/Logging.h"
+#include "mozilla/SSE.h"
+#include "mozilla/Telemetry.h"
+#include "mozilla/XREAppData.h"
+#include "mozilla/ScopeExit.h"
+#include "mozilla/GUniquePtr.h"
+#include "mozilla/StaticPrefs_media.h"
+#include "nsCRTGlue.h"
+#include "nsExceptionHandler.h"
+#include "nsPrintfCString.h"
+#include "nsString.h"
+#include "nsStringFwd.h"
+#include "nsUnicharUtils.h"
+#include "nsWhitespaceTokenizer.h"
+#include "prenv.h"
+#include "WidgetUtilsGtk.h"
+#include "MediaCodecsSupport.h"
+#include "nsAppRunner.h"
+
+// How long we wait for data from glxtest/vaapi test process in milliseconds.
+#define GFX_TEST_TIMEOUT 4000
+#define VAAPI_TEST_TIMEOUT 2000
+
+#define GLX_PROBE_BINARY u"glxtest"_ns
+#define VAAPI_PROBE_BINARY u"vaapitest"_ns
+
+namespace mozilla::widget {
+
+#ifdef DEBUG
+NS_IMPL_ISUPPORTS_INHERITED(GfxInfo, GfxInfoBase, nsIGfxInfoDebug)
+#endif
+
+int GfxInfo::sGLXTestPipe = -1;
+pid_t GfxInfo::sGLXTestPID = 0;
+
+// bits to use decoding codec information returned from glxtest
+constexpr int CODEC_HW_H264 = 1 << 4;
+constexpr int CODEC_HW_VP8 = 1 << 5;
+constexpr int CODEC_HW_VP9 = 1 << 6;
+constexpr int CODEC_HW_AV1 = 1 << 7;
+
+nsresult GfxInfo::Init() {
+ mGLMajorVersion = 0;
+ mGLMinorVersion = 0;
+ mHasTextureFromPixmap = false;
+ mIsMesa = false;
+ mIsAccelerated = true;
+ mIsWayland = false;
+ mIsXWayland = false;
+ mHasMultipleGPUs = false;
+ mGlxTestError = false;
+ return GfxInfoBase::Init();
+}
+
+void GfxInfo::AddCrashReportAnnotations() {
+ CrashReporter::AnnotateCrashReport(CrashReporter::Annotation::AdapterVendorID,
+ mVendorId);
+ CrashReporter::AnnotateCrashReport(CrashReporter::Annotation::AdapterDeviceID,
+ mDeviceId);
+ CrashReporter::AnnotateCrashReport(
+ CrashReporter::Annotation::AdapterDriverVendor, mDriverVendor);
+ CrashReporter::AnnotateCrashReport(
+ CrashReporter::Annotation::AdapterDriverVersion, mDriverVersion);
+ CrashReporter::AnnotateCrashReport(CrashReporter::Annotation::IsWayland,
+ mIsWayland);
+ CrashReporter::AnnotateCrashReport(
+ CrashReporter::Annotation::DesktopEnvironment,
+ GetDesktopEnvironmentIdentifier());
+
+ if (mHasMultipleGPUs) {
+ nsAutoCString note;
+ note.AppendLiteral("Has dual GPUs.");
+ if (!mSecondaryVendorId.IsEmpty()) {
+ note.AppendLiteral(" GPU #2: AdapterVendorID2: ");
+ note.Append(mSecondaryVendorId);
+ note.AppendLiteral(", AdapterDeviceID2: ");
+ note.Append(mSecondaryDeviceId);
+ }
+ CrashReporter::AppendAppNotesToCrashReport(note);
+ }
+}
+
+static bool MakeFdNonBlocking(int fd) {
+ return fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) | O_NONBLOCK) != -1;
+}
+
+static bool ManageChildProcess(const char* aProcessName, int* aPID, int* aPipe,
+ int aTimeout, char** aData) {
+ // Don't try anything if we failed before
+ if (*aPID < 0) {
+ return false;
+ }
+
+ GIOChannel* channel = nullptr;
+ *aData = nullptr;
+
+ auto free = mozilla::MakeScopeExit([&] {
+ if (channel) {
+ g_io_channel_unref(channel);
+ }
+ if (*aPipe >= 0) {
+ close(*aPipe);
+ *aPipe = -1;
+ }
+ });
+
+ const TimeStamp deadline =
+ TimeStamp::Now() + TimeDuration::FromMilliseconds(aTimeout);
+
+ struct pollfd pfd {};
+ pfd.fd = *aPipe;
+ pfd.events = POLLIN;
+
+ while (poll(&pfd, 1, aTimeout) != 1) {
+ if (errno != EAGAIN && errno != EINTR) {
+ gfxCriticalNote << "ManageChildProcess(" << aProcessName
+ << "): poll failed: " << strerror(errno) << "\n";
+ return false;
+ }
+ if (TimeStamp::Now() > deadline) {
+ gfxCriticalNote << "ManageChildProcess(" << aProcessName
+ << "): process hangs\n";
+ return false;
+ }
+ }
+
+ channel = g_io_channel_unix_new(*aPipe);
+ MakeFdNonBlocking(*aPipe);
+
+ GUniquePtr<GError> error;
+ gsize length = 0;
+ int ret;
+ do {
+ error = nullptr;
+ ret = g_io_channel_read_to_end(channel, aData, &length,
+ getter_Transfers(error));
+ } while (ret == G_IO_STATUS_AGAIN && TimeStamp::Now() < deadline);
+ if (error || ret != G_IO_STATUS_NORMAL) {
+ gfxCriticalNote << "ManageChildProcess(" << aProcessName
+ << "): failed to read data from child process: ";
+ if (error) {
+ gfxCriticalNote << error->message;
+ } else {
+ gfxCriticalNote << "timeout";
+ }
+ return false;
+ }
+
+ int status = 0;
+ int pid = *aPID;
+ *aPID = -1;
+
+ while (true) {
+ int ret = waitpid(pid, &status, WNOHANG);
+ if (ret > 0) {
+ break;
+ }
+ if (ret < 0) {
+ if (errno == ECHILD) {
+ // Bug 718629
+ // ECHILD happens when the glxtest process got reaped got reaped after a
+ // PR_CreateProcess as per bug 227246. This shouldn't matter, as we
+ // still seem to get the data from the pipe, and if we didn't, the
+ // outcome would be to blocklist anyway.
+ return true;
+ }
+ if (errno != EAGAIN && errno != EINTR) {
+ gfxCriticalNote << "ManageChildProcess(" << aProcessName
+ << "): waitpid failed: " << strerror(errno) << "\n";
+ return false;
+ }
+ }
+ if (TimeStamp::Now() > deadline) {
+ gfxCriticalNote << "ManageChildProcess(" << aProcessName
+ << "): process hangs\n";
+ return false;
+ }
+ // Wait 50ms to another waitpid() check.
+ usleep(50000);
+ }
+
+ return WIFEXITED(status) && WEXITSTATUS(status) == EXIT_SUCCESS;
+}
+
+// to understand this function, see bug 639842. We retrieve the OpenGL driver
+// information in a separate process to protect against bad drivers.
+void GfxInfo::GetData() {
+ if (mInitialized) {
+ return;
+ }
+ mInitialized = true;
+
+ // In some cases (xpcshell test, Profile manager etc.)
+ // FireGLXTestProcess() is not fired in advance
+ // so we call it here.
+ GfxInfo::FireGLXTestProcess();
+
+ GfxInfoBase::GetData();
+
+ char* glxData = nullptr;
+ auto free = mozilla::MakeScopeExit([&] { g_free((void*)glxData); });
+
+ bool error = !ManageChildProcess("glxtest", &sGLXTestPID, &sGLXTestPipe,
+ GFX_TEST_TIMEOUT, &glxData);
+ if (error) {
+ gfxCriticalNote << "glxtest: ManageChildProcess failed\n";
+ }
+
+ nsCString glVendor;
+ nsCString glRenderer;
+ nsCString glVersion;
+ nsCString textureFromPixmap;
+ nsCString testType;
+
+ // Available if GLX_MESA_query_renderer is supported.
+ nsCString mesaVendor;
+ nsCString mesaDevice;
+ nsCString mesaAccelerated;
+ // Available if using a DRI-based libGL stack.
+ nsCString driDriver;
+ nsCString adapterRam;
+
+ nsCString drmRenderDevice;
+
+ nsCString ddxDriver;
+
+ AutoTArray<nsCString, 2> pciVendors;
+ AutoTArray<nsCString, 2> pciDevices;
+
+ nsCString* stringToFill = nullptr;
+ bool logString = false;
+ bool errorLog = false;
+
+ char* bufptr = glxData;
+
+ while (true) {
+ char* line = NS_strtok("\n", &bufptr);
+ if (!line) break;
+ if (stringToFill) {
+ stringToFill->Assign(line);
+ stringToFill = nullptr;
+ } else if (logString) {
+ gfxCriticalNote << "glxtest: " << line;
+ logString = false;
+ } else if (!strcmp(line, "VENDOR")) {
+ stringToFill = &glVendor;
+ } else if (!strcmp(line, "RENDERER")) {
+ stringToFill = &glRenderer;
+ } else if (!strcmp(line, "VERSION")) {
+ stringToFill = &glVersion;
+ } else if (!strcmp(line, "TFP")) {
+ stringToFill = &textureFromPixmap;
+ } else if (!strcmp(line, "MESA_VENDOR_ID")) {
+ stringToFill = &mesaVendor;
+ } else if (!strcmp(line, "MESA_DEVICE_ID")) {
+ stringToFill = &mesaDevice;
+ } else if (!strcmp(line, "MESA_ACCELERATED")) {
+ stringToFill = &mesaAccelerated;
+ } else if (!strcmp(line, "MESA_VRAM")) {
+ stringToFill = &adapterRam;
+ } else if (!strcmp(line, "DDX_DRIVER")) {
+ stringToFill = &ddxDriver;
+ } else if (!strcmp(line, "DRI_DRIVER")) {
+ stringToFill = &driDriver;
+ } else if (!strcmp(line, "PCI_VENDOR_ID")) {
+ stringToFill = pciVendors.AppendElement();
+ } else if (!strcmp(line, "PCI_DEVICE_ID")) {
+ 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")) {
+ logString = true;
+ errorLog = true;
+ }
+ }
+
+ MOZ_ASSERT(pciDevices.Length() == pciVendors.Length(),
+ "Missing PCI vendors/devices");
+
+ size_t pciLen = std::min(pciVendors.Length(), pciDevices.Length());
+ mHasMultipleGPUs = pciLen > 1;
+
+ if (!strcmp(textureFromPixmap.get(), "TRUE")) mHasTextureFromPixmap = true;
+
+ // only useful for Linux kernel version check for FGLRX driver.
+ // assumes X client == X server, which is sad.
+ struct utsname unameobj {};
+ if (uname(&unameobj) >= 0) {
+ mOS.Assign(unameobj.sysname);
+ mOSRelease.Assign(unameobj.release);
+ }
+
+ const char* spoofedVendor = PR_GetEnv("MOZ_GFX_SPOOF_GL_VENDOR");
+ if (spoofedVendor) glVendor.Assign(spoofedVendor);
+ const char* spoofedRenderer = PR_GetEnv("MOZ_GFX_SPOOF_GL_RENDERER");
+ if (spoofedRenderer) glRenderer.Assign(spoofedRenderer);
+ const char* spoofedVersion = PR_GetEnv("MOZ_GFX_SPOOF_GL_VERSION");
+ if (spoofedVersion) glVersion.Assign(spoofedVersion);
+ const char* spoofedOS = PR_GetEnv("MOZ_GFX_SPOOF_OS");
+ if (spoofedOS) mOS.Assign(spoofedOS);
+ const char* spoofedOSRelease = PR_GetEnv("MOZ_GFX_SPOOF_OS_RELEASE");
+ if (spoofedOSRelease) mOSRelease.Assign(spoofedOSRelease);
+
+ // Scan the GL_VERSION string for the GL and driver versions.
+ nsCWhitespaceTokenizer tokenizer(glVersion);
+ while (tokenizer.hasMoreTokens()) {
+ nsCString token(tokenizer.nextToken());
+ unsigned int major = 0, minor = 0, revision = 0, patch = 0;
+ if (sscanf(token.get(), "%u.%u.%u.%u", &major, &minor, &revision, &patch) >=
+ 2) {
+ // A survey of GL_VENDOR strings indicates that the first version is
+ // always the GL version, the second is usually the driver version.
+ if (mGLMajorVersion == 0) {
+ mGLMajorVersion = major;
+ mGLMinorVersion = minor;
+ } else if (mDriverVersion.IsEmpty()) { // Not already spoofed.
+ mDriverVersion =
+ nsPrintfCString("%u.%u.%u.%u", major, minor, revision, patch);
+ }
+ }
+ }
+
+ if (mGLMajorVersion == 0) {
+ NS_WARNING("Failed to parse GL version!");
+ }
+
+ mDrmRenderDevice = std::move(drmRenderDevice);
+ mTestType = std::move(testType);
+
+ // Mesa always exposes itself in the GL_VERSION string, but not always the
+ // GL_VENDOR string.
+ mIsMesa = glVersion.Find("Mesa") != -1;
+
+ // We need to use custom driver vendor IDs for mesa so we can treat them
+ // differently than the proprietary drivers.
+ if (mIsMesa) {
+ mIsAccelerated = !mesaAccelerated.Equals("FALSE");
+ // Process software rasterizers before the DRI driver string; we may be
+ // forcing software rasterization on a DRI-accelerated X server by using
+ // LIBGL_ALWAYS_SOFTWARE or a similar restriction.
+ if (strcasestr(glRenderer.get(), "llvmpipe")) {
+ CopyUTF16toUTF8(
+ GfxDriverInfo::GetDriverVendor(DriverVendor::MesaLLVMPipe),
+ mDriverVendor);
+ mIsAccelerated = false;
+ } else if (strcasestr(glRenderer.get(), "softpipe")) {
+ CopyUTF16toUTF8(
+ GfxDriverInfo::GetDriverVendor(DriverVendor::MesaSoftPipe),
+ mDriverVendor);
+ mIsAccelerated = false;
+ } else if (strcasestr(glRenderer.get(), "software rasterizer")) {
+ CopyUTF16toUTF8(GfxDriverInfo::GetDriverVendor(DriverVendor::MesaSWRast),
+ mDriverVendor);
+ mIsAccelerated = false;
+ } else if (strcasestr(driDriver.get(), "vmwgfx")) {
+ CopyUTF16toUTF8(GfxDriverInfo::GetDriverVendor(DriverVendor::MesaVM),
+ mDriverVendor);
+ mIsAccelerated = false;
+ } else if (!mIsAccelerated) {
+ CopyUTF16toUTF8(
+ GfxDriverInfo::GetDriverVendor(DriverVendor::MesaSWUnknown),
+ mDriverVendor);
+ } else if (!driDriver.IsEmpty()) {
+ mDriverVendor = nsPrintfCString("mesa/%s", driDriver.get());
+ } else {
+ // Some other mesa configuration where we couldn't get enough info.
+ NS_WARNING("Failed to detect Mesa driver being used!");
+ CopyUTF16toUTF8(GfxDriverInfo::GetDriverVendor(DriverVendor::MesaUnknown),
+ mDriverVendor);
+ }
+
+ if (!mesaVendor.IsEmpty()) {
+ mVendorId = mesaVendor;
+ }
+
+ if (!mesaDevice.IsEmpty()) {
+ mDeviceId = mesaDevice;
+ }
+
+ if (!mIsAccelerated && mVendorId.IsEmpty()) {
+ mVendorId.Assign(glVendor.get());
+ }
+
+ if (!mIsAccelerated && mDeviceId.IsEmpty()) {
+ mDeviceId.Assign(glRenderer.get());
+ }
+ } else if (glVendor.EqualsLiteral("NVIDIA Corporation")) {
+ CopyUTF16toUTF8(GfxDriverInfo::GetDeviceVendor(DeviceVendor::NVIDIA),
+ mVendorId);
+ mDriverVendor.AssignLiteral("nvidia/unknown");
+ // TODO: Use NV-CONTROL X11 extension to query Device ID and VRAM.
+ } else if (glVendor.EqualsLiteral("ATI Technologies Inc.")) {
+ CopyUTF16toUTF8(GfxDriverInfo::GetDeviceVendor(DeviceVendor::ATI),
+ mVendorId);
+ mDriverVendor.AssignLiteral("ati/unknown");
+ // TODO: Look into ways to find the device ID on FGLRX.
+ } else {
+ NS_WARNING("Failed to detect GL vendor!");
+ }
+
+ if (!adapterRam.IsEmpty()) {
+ mAdapterRAM = (uint32_t)atoi(adapterRam.get());
+ }
+
+ // If we have the DRI driver, we can derive the vendor ID from that if needed.
+ if (mVendorId.IsEmpty() && !driDriver.IsEmpty()) {
+ const char* nvidiaDrivers[] = {"nouveau", "tegra", nullptr};
+ for (size_t i = 0; nvidiaDrivers[i]; ++i) {
+ if (driDriver.Equals(nvidiaDrivers[i])) {
+ CopyUTF16toUTF8(GfxDriverInfo::GetDeviceVendor(DeviceVendor::NVIDIA),
+ mVendorId);
+ break;
+ }
+ }
+
+ if (mVendorId.IsEmpty()) {
+ const char* intelDrivers[] = {"iris", "crocus", "i915", "i965",
+ "i810", "intel", nullptr};
+ for (size_t i = 0; intelDrivers[i]; ++i) {
+ if (driDriver.Equals(intelDrivers[i])) {
+ CopyUTF16toUTF8(GfxDriverInfo::GetDeviceVendor(DeviceVendor::Intel),
+ mVendorId);
+ break;
+ }
+ }
+ }
+
+ if (mVendorId.IsEmpty()) {
+ const char* amdDrivers[] = {"r600", "r200", "r100",
+ "radeon", "radeonsi", nullptr};
+ for (size_t i = 0; amdDrivers[i]; ++i) {
+ if (driDriver.Equals(amdDrivers[i])) {
+ CopyUTF16toUTF8(GfxDriverInfo::GetDeviceVendor(DeviceVendor::ATI),
+ mVendorId);
+ break;
+ }
+ }
+ }
+
+ if (mVendorId.IsEmpty()) {
+ if (driDriver.EqualsLiteral("freedreno")) {
+ CopyUTF16toUTF8(GfxDriverInfo::GetDeviceVendor(DeviceVendor::Qualcomm),
+ mVendorId);
+ }
+ }
+ }
+
+ // If we still don't have a vendor ID, we can try the PCI vendor list.
+ if (mVendorId.IsEmpty()) {
+ if (pciVendors.IsEmpty()) {
+ gfxCriticalNote << "No GPUs detected via PCI\n";
+ } else {
+ for (size_t i = 0; i < pciVendors.Length(); ++i) {
+ if (mVendorId.IsEmpty()) {
+ mVendorId = pciVendors[i];
+ } else if (mVendorId != pciVendors[i]) {
+ gfxCriticalNote << "More than 1 GPU vendor detected via PCI, cannot "
+ "deduce vendor\n";
+ mVendorId.Truncate();
+ break;
+ }
+ }
+ }
+ }
+
+ // If we know the vendor ID, but didn't get a device ID, we can guess from the
+ // PCI device list.
+ if (mDeviceId.IsEmpty() && !mVendorId.IsEmpty()) {
+ for (size_t i = 0; i < pciLen; ++i) {
+ if (mVendorId.Equals(pciVendors[i])) {
+ if (mDeviceId.IsEmpty()) {
+ mDeviceId = pciDevices[i];
+ } else if (mDeviceId != pciDevices[i]) {
+ gfxCriticalNote << "More than 1 GPU from same vendor detected via "
+ "PCI, cannot deduce device\n";
+ mDeviceId.Truncate();
+ break;
+ }
+ }
+ }
+ }
+
+ // Assuming we know the vendor, we should check for a secondary card.
+ if (!mVendorId.IsEmpty()) {
+ if (pciLen > 2) {
+ gfxCriticalNote
+ << "More than 2 GPUs detected via PCI, secondary GPU is arbitrary\n";
+ }
+ for (size_t i = 0; i < pciLen; ++i) {
+ if (!mVendorId.Equals(pciVendors[i]) ||
+ (!mDeviceId.IsEmpty() && !mDeviceId.Equals(pciDevices[i]))) {
+ mSecondaryVendorId = pciVendors[i];
+ mSecondaryDeviceId = pciDevices[i];
+ break;
+ }
+ }
+ }
+
+ // If we couldn't choose, log them.
+ if (mVendorId.IsEmpty()) {
+ for (size_t i = 0; i < pciLen; ++i) {
+ gfxCriticalNote << "PCI candidate " << pciVendors[i].get() << "/"
+ << pciDevices[i].get() << "\n";
+ }
+ }
+
+ // Fallback to GL_VENDOR and GL_RENDERER.
+ if (mVendorId.IsEmpty()) {
+ mVendorId.Assign(glVendor.get());
+ }
+ if (mDeviceId.IsEmpty()) {
+ mDeviceId.Assign(glRenderer.get());
+ }
+
+ mAdapterDescription.Assign(glRenderer);
+
+ // Make a best effort guess at whether or not we are using the XWayland compat
+ // layer. For all intents and purposes, we should otherwise believe we are
+ // using X11.
+ mIsWayland = GdkIsWaylandDisplay();
+ mIsXWayland = IsXWaylandProtocol();
+
+ if (!ddxDriver.IsEmpty()) {
+ PRInt32 start = 0;
+ PRInt32 loc = ddxDriver.Find(";", start);
+ while (loc != kNotFound) {
+ nsCString line(ddxDriver.get() + start, loc - start);
+ mDdxDrivers.AppendElement(std::move(line));
+
+ start = loc + 1;
+ loc = ddxDriver.Find(";", start);
+ }
+ }
+
+ if (error || errorLog || mTestType.IsEmpty()) {
+ if (!mAdapterDescription.IsEmpty()) {
+ mAdapterDescription.AppendLiteral(" (See failure log)");
+ } else {
+ mAdapterDescription.AppendLiteral("See failure log");
+ }
+
+ mGlxTestError = true;
+ }
+
+ AddCrashReportAnnotations();
+}
+
+int GfxInfo::FireTestProcess(const nsAString& aBinaryFile, int* aOutPipe,
+ const char** aStringArgs) {
+ nsCOMPtr<nsIFile> appFile;
+ nsresult rv = XRE_GetBinaryPath(getter_AddRefs(appFile));
+ if (NS_FAILED(rv)) {
+ gfxCriticalNote << "Couldn't find application file.\n";
+ return false;
+ }
+ nsCOMPtr<nsIFile> exePath;
+ rv = appFile->GetParent(getter_AddRefs(exePath));
+ if (NS_FAILED(rv)) {
+ gfxCriticalNote << "Couldn't get application directory.\n";
+ return false;
+ }
+ exePath->Append(aBinaryFile);
+
+#define MAX_ARGS 8
+ char* argv[MAX_ARGS + 2];
+
+ argv[0] = strdup(exePath->NativePath().get());
+ for (int i = 0; i < MAX_ARGS; i++) {
+ if (aStringArgs[i]) {
+ argv[i + 1] = strdup(aStringArgs[i]);
+ } else {
+ argv[i + 1] = nullptr;
+ break;
+ }
+ }
+
+ // Use G_SPAWN_LEAVE_DESCRIPTORS_OPEN | G_SPAWN_DO_NOT_REAP_CHILD flags
+ // to g_spawn_async_with_pipes() run posix_spawn() directly.
+ int pid;
+ GUniquePtr<GError> err;
+ g_spawn_async_with_pipes(
+ nullptr, argv, nullptr,
+ GSpawnFlags(G_SPAWN_LEAVE_DESCRIPTORS_OPEN | G_SPAWN_DO_NOT_REAP_CHILD),
+ nullptr, nullptr, &pid, nullptr, aOutPipe, nullptr,
+ getter_Transfers(err));
+ if (err) {
+ gfxCriticalNote << "FireTestProcess failed: " << err->message << "\n";
+ pid = 0;
+ }
+ for (auto& arg : argv) {
+ if (!arg) {
+ break;
+ }
+ free(arg);
+ }
+ return pid;
+}
+
+bool GfxInfo::FireGLXTestProcess() {
+ if (sGLXTestPID != 0) {
+ return true;
+ }
+
+ int pfd[2];
+ if (pipe(pfd) == -1) {
+ gfxCriticalNote << "FireGLXTestProcess failed to create pipe\n";
+ return false;
+ }
+ sGLXTestPipe = pfd[0];
+
+ auto pipeID = std::to_string(pfd[1]);
+ const char* args[] = {"-f", pipeID.c_str(),
+ IsWaylandEnabled() ? "-w" : nullptr, nullptr};
+ sGLXTestPID = FireTestProcess(GLX_PROBE_BINARY, nullptr, args);
+ // Set pid to -1 to avoid further test launch.
+ if (!sGLXTestPID) {
+ sGLXTestPID = -1;
+ }
+ close(pfd[1]);
+ return true;
+}
+
+void GfxInfo::GetDataVAAPI() {
+ if (mIsVAAPISupported.isSome()) {
+ return;
+ }
+ mIsVAAPISupported = Some(false);
+
+#ifdef MOZ_ENABLE_VAAPI
+ char* vaapiData = nullptr;
+ auto free = mozilla::MakeScopeExit([&] { g_free((void*)vaapiData); });
+
+ int vaapiPipe = -1;
+ int vaapiPID = 0;
+ const char* args[] = {"-d", mDrmRenderDevice.get(), nullptr};
+ vaapiPID = FireTestProcess(VAAPI_PROBE_BINARY, &vaapiPipe, args);
+ if (!vaapiPID) {
+ return;
+ }
+
+ if (!ManageChildProcess("vaapitest", &vaapiPID, &vaapiPipe,
+ VAAPI_TEST_TIMEOUT, &vaapiData)) {
+ gfxCriticalNote << "vaapitest: ManageChildProcess failed\n";
+ return;
+ }
+
+ char* bufptr = vaapiData;
+ char* line;
+ while ((line = NS_strtok("\n", &bufptr))) {
+ if (!strcmp(line, "VAAPI_SUPPORTED")) {
+ line = NS_strtok("\n", &bufptr);
+ if (!line) {
+ gfxCriticalNote << "vaapitest: Failed to get VAAPI support\n";
+ return;
+ }
+ mIsVAAPISupported = Some(!strcmp(line, "TRUE"));
+ } else if (!strcmp(line, "VAAPI_HWCODECS")) {
+ line = NS_strtok("\n", &bufptr);
+ if (!line) {
+ gfxCriticalNote << "vaapitest: Failed to get VAAPI codecs\n";
+ return;
+ }
+
+ std::istringstream(line) >> mVAAPISupportedCodecs;
+ if (mVAAPISupportedCodecs & CODEC_HW_H264) {
+ media::MCSInfo::AddSupport(
+ media::MediaCodecsSupport::H264HardwareDecode);
+ }
+ if (mVAAPISupportedCodecs & CODEC_HW_VP8) {
+ media::MCSInfo::AddSupport(
+ media::MediaCodecsSupport::VP8HardwareDecode);
+ }
+ if (mVAAPISupportedCodecs & CODEC_HW_VP9) {
+ media::MCSInfo::AddSupport(
+ media::MediaCodecsSupport::VP9HardwareDecode);
+ }
+ if (mVAAPISupportedCodecs & CODEC_HW_AV1) {
+ media::MCSInfo::AddSupport(
+ media::MediaCodecsSupport::AV1HardwareDecode);
+ }
+ } else if (!strcmp(line, "WARNING") || !strcmp(line, "ERROR")) {
+ gfxCriticalNote << "vaapitest: " << line;
+ line = NS_strtok("\n", &bufptr);
+ if (line) {
+ gfxCriticalNote << "vaapitest: " << line << "\n";
+ }
+ return;
+ }
+ }
+#endif
+}
+
+const nsTArray<GfxDriverInfo>& GfxInfo::GetGfxDriverInfo() {
+ if (!sDriverInfo->Length()) {
+ // Mesa 10.0 provides the GLX_MESA_query_renderer extension, which allows us
+ // to query device IDs backing a GL context for blocklisting.
+ APPEND_TO_DRIVER_BLOCKLIST_EXT(
+ OperatingSystem::Linux, ScreenSizeStatus::All, BatteryStatus::All,
+ WindowProtocol::All, DriverVendor::MesaAll, DeviceFamily::All,
+ GfxDriverInfo::allFeatures, nsIGfxInfo::FEATURE_BLOCKED_DRIVER_VERSION,
+ DRIVER_LESS_THAN, V(10, 0, 0, 0), "FEATURE_FAILURE_OLD_MESA",
+ "Mesa 10.0");
+
+ // NVIDIA Mesa baseline (see bug 1714391).
+ APPEND_TO_DRIVER_BLOCKLIST_EXT(
+ OperatingSystem::Linux, ScreenSizeStatus::All, BatteryStatus::All,
+ WindowProtocol::All, DriverVendor::MesaNouveau, DeviceFamily::All,
+ GfxDriverInfo::allFeatures, nsIGfxInfo::FEATURE_BLOCKED_DRIVER_VERSION,
+ DRIVER_LESS_THAN, V(11, 0, 0, 0), "FEATURE_FAILURE_OLD_NV_MESA",
+ "Mesa 11.0");
+
+ // NVIDIA baseline (ported from old blocklist)
+ APPEND_TO_DRIVER_BLOCKLIST_EXT(
+ OperatingSystem::Linux, ScreenSizeStatus::All, BatteryStatus::All,
+ WindowProtocol::All, DriverVendor::NonMesaAll, DeviceFamily::NvidiaAll,
+ GfxDriverInfo::allFeatures, nsIGfxInfo::FEATURE_BLOCKED_DRIVER_VERSION,
+ DRIVER_LESS_THAN, V(257, 21, 0, 0), "FEATURE_FAILURE_OLD_NVIDIA",
+ "NVIDIA 257.21");
+
+ // fglrx baseline (chosen arbitrarily as 2013-07-22 release).
+ APPEND_TO_DRIVER_BLOCKLIST(
+ OperatingSystem::Linux, DeviceFamily::AtiAll,
+ GfxDriverInfo::allFeatures, nsIGfxInfo::FEATURE_BLOCKED_DRIVER_VERSION,
+ DRIVER_LESS_THAN, V(13, 15, 100, 1), "FEATURE_FAILURE_OLD_FGLRX",
+ "fglrx 13.15.100.1");
+
+ ////////////////////////////////////
+ // FEATURE_WEBRENDER
+
+ // All Mesa software drivers, they should get Software WebRender instead.
+ APPEND_TO_DRIVER_BLOCKLIST_EXT(
+ OperatingSystem::Linux, ScreenSizeStatus::All, BatteryStatus::All,
+ WindowProtocol::All, DriverVendor::SoftwareMesaAll, DeviceFamily::All,
+ nsIGfxInfo::FEATURE_WEBRENDER, nsIGfxInfo::FEATURE_BLOCKED_DEVICE,
+ DRIVER_COMPARISON_IGNORED, V(0, 0, 0, 0), "FEATURE_FAILURE_SOFTWARE_GL",
+ "");
+
+ // Older generation Intel devices do not perform well with WebRender.
+ APPEND_TO_DRIVER_BLOCKLIST(
+ OperatingSystem::Linux, DeviceFamily::IntelWebRenderBlocked,
+ nsIGfxInfo::FEATURE_WEBRENDER, nsIGfxInfo::FEATURE_BLOCKED_DEVICE,
+ DRIVER_COMPARISON_IGNORED, V(0, 0, 0, 0), "INTEL_DEVICE_GEN5_OR_OLDER",
+ "");
+
+ // Nvidia Mesa baseline, see bug 1563859.
+ APPEND_TO_DRIVER_BLOCKLIST_EXT(
+ OperatingSystem::Linux, ScreenSizeStatus::All, BatteryStatus::All,
+ WindowProtocol::All, DriverVendor::MesaAll, DeviceFamily::NvidiaAll,
+ nsIGfxInfo::FEATURE_WEBRENDER,
+ nsIGfxInfo::FEATURE_BLOCKED_DRIVER_VERSION, DRIVER_LESS_THAN,
+ V(18, 2, 0, 0), "FEATURE_FAILURE_WEBRENDER_OLD_MESA", "Mesa 18.2.0.0");
+
+ // Disable on all older Nvidia drivers due to stability issues.
+ APPEND_TO_DRIVER_BLOCKLIST_EXT(
+ OperatingSystem::Linux, ScreenSizeStatus::All, BatteryStatus::All,
+ WindowProtocol::All, DriverVendor::NonMesaAll, DeviceFamily::NvidiaAll,
+ nsIGfxInfo::FEATURE_WEBRENDER, nsIGfxInfo::FEATURE_BLOCKED_DEVICE,
+ DRIVER_LESS_THAN, V(470, 82, 0, 0),
+ "FEATURE_FAILURE_WEBRENDER_OLD_NVIDIA", "470.82.0");
+
+ // Older generation NVIDIA devices do not perform well with WebRender.
+ APPEND_TO_DRIVER_BLOCKLIST(
+ OperatingSystem::Linux, DeviceFamily::NvidiaWebRenderBlocked,
+ nsIGfxInfo::FEATURE_WEBRENDER, nsIGfxInfo::FEATURE_BLOCKED_DEVICE,
+ DRIVER_COMPARISON_IGNORED, V(0, 0, 0, 0),
+ "NVIDIA_EARLY_TESLA_AND_C67_C68", "");
+
+ // Mesa baseline, chosen arbitrarily. Linux users are generally good about
+ // updating their Mesa libraries so we don't want to arbitarily support
+ // WebRender on old drivers with outstanding bugs to work around.
+ APPEND_TO_DRIVER_BLOCKLIST_EXT(
+ OperatingSystem::Linux, ScreenSizeStatus::All, BatteryStatus::All,
+ WindowProtocol::All, DriverVendor::MesaAll, DeviceFamily::All,
+ nsIGfxInfo::FEATURE_WEBRENDER,
+ nsIGfxInfo::FEATURE_BLOCKED_DRIVER_VERSION, DRIVER_LESS_THAN,
+ V(17, 0, 0, 0), "FEATURE_FAILURE_WEBRENDER_OLD_MESA", "Mesa 17.0.0.0");
+
+ // Mesa baseline for non-Intel/NVIDIA/ATI devices. These other devices will
+ // often have less mature drivers so let's block older Mesa versions.
+ APPEND_TO_DRIVER_BLOCKLIST_EXT(
+ OperatingSystem::Linux, ScreenSizeStatus::All, BatteryStatus::All,
+ WindowProtocol::All, DriverVendor::MesaNonIntelNvidiaAtiAll,
+ DeviceFamily::All, nsIGfxInfo::FEATURE_WEBRENDER,
+ nsIGfxInfo::FEATURE_BLOCKED_DRIVER_VERSION, DRIVER_LESS_THAN,
+ V(22, 2, 0, 0), "FEATURE_FAILURE_WEBRENDER_OLD_MESA_OTHER",
+ "Mesa 22.2.0.0");
+
+ // Bug 1690568 / Bug 1393793 - Require Mesa 17.3.0+ for devices using the
+ // AMD r600 driver to avoid shader compilation issues.
+ APPEND_TO_DRIVER_BLOCKLIST_EXT(
+ OperatingSystem::Linux, ScreenSizeStatus::All, BatteryStatus::All,
+ WindowProtocol::All, DriverVendor::MesaR600, DeviceFamily::All,
+ nsIGfxInfo::FEATURE_WEBRENDER,
+ nsIGfxInfo::FEATURE_BLOCKED_DRIVER_VERSION, DRIVER_LESS_THAN,
+ V(17, 3, 0, 0), "FEATURE_FAILURE_WEBRENDER_OLD_MESA_R600",
+ "Mesa 17.3.0.0");
+
+ // Disable on all ATI devices not using Mesa for now.
+ APPEND_TO_DRIVER_BLOCKLIST_EXT(
+ OperatingSystem::Linux, ScreenSizeStatus::All, BatteryStatus::All,
+ WindowProtocol::All, DriverVendor::NonMesaAll, DeviceFamily::AtiAll,
+ nsIGfxInfo::FEATURE_WEBRENDER, nsIGfxInfo::FEATURE_BLOCKED_DEVICE,
+ DRIVER_COMPARISON_IGNORED, V(0, 0, 0, 0),
+ "FEATURE_FAILURE_WEBRENDER_NO_LINUX_ATI", "");
+
+ // Disable R600 GPUs with Mesa drivers.
+ // Bug 1673939 - Garbled text on RS880 GPUs with Mesa drivers.
+ APPEND_TO_DRIVER_BLOCKLIST_EXT(
+ OperatingSystem::Linux, ScreenSizeStatus::All, BatteryStatus::All,
+ WindowProtocol::All, DriverVendor::MesaAll, DeviceFamily::AmdR600,
+ nsIGfxInfo::FEATURE_WEBRENDER, nsIGfxInfo::FEATURE_BLOCKED_DEVICE,
+ DRIVER_COMPARISON_IGNORED, V(0, 0, 0, 0),
+ "FEATURE_FAILURE_WEBRENDER_BUG_1673939",
+ "https://gitlab.freedesktop.org/mesa/mesa/-/issues/3720");
+
+ // Bug 1635186 - Poor performance with video playing in a background window
+ // on XWayland. Keep in sync with FEATURE_X11_EGL below to only enable them
+ // together by default. Only Mesa and Nvidia binary drivers are expected
+ // on Wayland rigth now.
+ APPEND_TO_DRIVER_BLOCKLIST_EXT(
+ OperatingSystem::Linux, ScreenSizeStatus::All, BatteryStatus::All,
+ WindowProtocol::XWayland, DriverVendor::MesaAll, DeviceFamily::All,
+ nsIGfxInfo::FEATURE_WEBRENDER,
+ nsIGfxInfo::FEATURE_BLOCKED_DRIVER_VERSION, DRIVER_LESS_THAN,
+ V(21, 0, 0, 0), "FEATURE_FAILURE_WEBRENDER_BUG_1635186",
+ "Mesa 21.0.0.0");
+
+ // Bug 1815481 - Disable mesa drivers in virtual machines.
+ APPEND_TO_DRIVER_BLOCKLIST_EXT(
+ OperatingSystem::Linux, ScreenSizeStatus::All, BatteryStatus::All,
+ WindowProtocol::All, DriverVendor::MesaVM, DeviceFamily::All,
+ nsIGfxInfo::FEATURE_WEBRENDER, nsIGfxInfo::FEATURE_BLOCKED_DEVICE,
+ DRIVER_COMPARISON_IGNORED, V(0, 0, 0, 0),
+ "FEATURE_FAILURE_WEBRENDER_MESA_VM", "");
+ // Disable hardware mesa drivers in virtual machines due to instability.
+ APPEND_TO_DRIVER_BLOCKLIST_EXT(
+ OperatingSystem::Linux, ScreenSizeStatus::All, BatteryStatus::All,
+ WindowProtocol::All, DriverVendor::MesaVM, DeviceFamily::All,
+ nsIGfxInfo::FEATURE_WEBGL_USE_HARDWARE,
+ nsIGfxInfo::FEATURE_BLOCKED_DEVICE, DRIVER_COMPARISON_IGNORED,
+ V(0, 0, 0, 0), "FEATURE_FAILURE_WEBGL_MESA_VM", "");
+
+ ////////////////////////////////////
+ // FEATURE_WEBRENDER_COMPOSITOR
+ APPEND_TO_DRIVER_BLOCKLIST(
+ OperatingSystem::Linux, DeviceFamily::All,
+ nsIGfxInfo::FEATURE_WEBRENDER_COMPOSITOR,
+ nsIGfxInfo::FEATURE_BLOCKED_DEVICE, DRIVER_COMPARISON_IGNORED,
+ V(0, 0, 0, 0), "FEATURE_FAILURE_WEBRENDER_COMPOSITOR_DISABLED", "");
+
+ ////////////////////////////////////
+ // FEATURE_X11_EGL
+ APPEND_TO_DRIVER_BLOCKLIST_EXT(
+ OperatingSystem::Linux, ScreenSizeStatus::All, BatteryStatus::All,
+ WindowProtocol::All, DriverVendor::MesaAll, DeviceFamily::All,
+ nsIGfxInfo::FEATURE_X11_EGL, nsIGfxInfo::FEATURE_BLOCKED_DRIVER_VERSION,
+ DRIVER_LESS_THAN, V(17, 0, 0, 0), "FEATURE_X11_EGL_OLD_MESA",
+ "Mesa 17.0.0.0");
+
+ APPEND_TO_DRIVER_BLOCKLIST_EXT(
+ OperatingSystem::Linux, ScreenSizeStatus::All, BatteryStatus::All,
+ WindowProtocol::All, DriverVendor::MesaAll, DeviceFamily::NvidiaAll,
+ nsIGfxInfo::FEATURE_X11_EGL, nsIGfxInfo::FEATURE_BLOCKED_DRIVER_VERSION,
+ DRIVER_LESS_THAN, V(18, 2, 0, 0), "FEATURE_X11_EGL_OLD_MESA_NOUVEAU",
+ "Mesa 18.2.0.0");
+
+ APPEND_TO_DRIVER_BLOCKLIST_EXT(
+ OperatingSystem::Linux, ScreenSizeStatus::All, BatteryStatus::All,
+ WindowProtocol::All, DriverVendor::NonMesaAll, DeviceFamily::NvidiaAll,
+ nsIGfxInfo::FEATURE_X11_EGL, nsIGfxInfo::FEATURE_BLOCKED_DRIVER_VERSION,
+ DRIVER_LESS_THAN, V(470, 82, 0, 0),
+ "FEATURE_ROLLOUT_X11_EGL_NVIDIA_BINARY", "470.82.0");
+
+ // Disable on all AMD devices not using Mesa.
+ APPEND_TO_DRIVER_BLOCKLIST_EXT(
+ OperatingSystem::Linux, ScreenSizeStatus::All, BatteryStatus::All,
+ WindowProtocol::All, DriverVendor::NonMesaAll, DeviceFamily::AtiAll,
+ nsIGfxInfo::FEATURE_X11_EGL, nsIGfxInfo::FEATURE_BLOCKED_DEVICE,
+ DRIVER_COMPARISON_IGNORED, V(0, 0, 0, 0),
+ "FEATURE_FAILURE_X11_EGL_NO_LINUX_ATI", "");
+
+ ////////////////////////////////////
+ // FEATURE_DMABUF
+ // Disabled due to high volume crash tracked in bug 1788573.
+ APPEND_TO_DRIVER_BLOCKLIST_EXT(
+ OperatingSystem::Linux, ScreenSizeStatus::All, BatteryStatus::All,
+ WindowProtocol::All, DriverVendor::NonMesaAll, DeviceFamily::NvidiaAll,
+ nsIGfxInfo::FEATURE_DMABUF, nsIGfxInfo::FEATURE_BLOCKED_DEVICE,
+ DRIVER_COMPARISON_IGNORED, V(0, 0, 0, 0), "FEATURE_FAILURE_BUG_1788573",
+ "");
+
+ ////////////////////////////////////
+ // FEATURE_DMABUF_SURFACE_EXPORT
+ // Disabled due to:
+ // https://gitlab.freedesktop.org/mesa/mesa/-/issues/6666
+ // https://gitlab.freedesktop.org/mesa/mesa/-/issues/6796
+ APPEND_TO_DRIVER_BLOCKLIST_EXT(
+ OperatingSystem::Linux, ScreenSizeStatus::All, BatteryStatus::All,
+ WindowProtocol::All, DriverVendor::MesaAll, DeviceFamily::AtiAll,
+ nsIGfxInfo::FEATURE_DMABUF_SURFACE_EXPORT,
+ nsIGfxInfo::FEATURE_BLOCKED_DEVICE, DRIVER_COMPARISON_IGNORED,
+ V(0, 0, 0, 0), "FEATURE_FAILURE_BROKEN_DRIVER", "");
+
+ // Disabled due to:
+ // https://gitlab.freedesktop.org/mesa/mesa/-/issues/6688
+ APPEND_TO_DRIVER_BLOCKLIST_EXT(
+ OperatingSystem::Linux, ScreenSizeStatus::All, BatteryStatus::All,
+ WindowProtocol::All, DriverVendor::MesaAll, DeviceFamily::IntelAll,
+ nsIGfxInfo::FEATURE_DMABUF_SURFACE_EXPORT,
+ nsIGfxInfo::FEATURE_BLOCKED_DEVICE, DRIVER_COMPARISON_IGNORED,
+ V(0, 0, 0, 0), "FEATURE_FAILURE_BROKEN_DRIVER", "");
+
+ // Disabled due to:
+ // https://gitlab.freedesktop.org/mesa/mesa/-/issues/6988
+ APPEND_TO_DRIVER_BLOCKLIST_EXT(
+ OperatingSystem::Linux, ScreenSizeStatus::All, BatteryStatus::All,
+ WindowProtocol::All, DriverVendor::MesaAll, DeviceFamily::QualcommAll,
+ nsIGfxInfo::FEATURE_DMABUF_SURFACE_EXPORT,
+ nsIGfxInfo::FEATURE_BLOCKED_DEVICE, DRIVER_COMPARISON_IGNORED,
+ V(0, 0, 0, 0), "FEATURE_FAILURE_BROKEN_DRIVER", "");
+
+ ////////////////////////////////////
+ // FEATURE_HARDWARE_VIDEO_DECODING
+ APPEND_TO_DRIVER_BLOCKLIST_EXT(
+ OperatingSystem::Linux, ScreenSizeStatus::All, BatteryStatus::All,
+ WindowProtocol::All, DriverVendor::MesaAll, DeviceFamily::All,
+ nsIGfxInfo::FEATURE_HARDWARE_VIDEO_DECODING,
+ nsIGfxInfo::FEATURE_BLOCKED_DRIVER_VERSION, DRIVER_LESS_THAN,
+ V(21, 0, 0, 0), "FEATURE_HARDWARE_VIDEO_DECODING_MESA",
+ "Mesa 21.0.0.0");
+
+ // Disable on all NVIDIA hardware
+ APPEND_TO_DRIVER_BLOCKLIST_EXT(
+ OperatingSystem::Linux, ScreenSizeStatus::All, BatteryStatus::All,
+ WindowProtocol::All, DriverVendor::All, DeviceFamily::NvidiaAll,
+ nsIGfxInfo::FEATURE_HARDWARE_VIDEO_DECODING,
+ nsIGfxInfo::FEATURE_BLOCKED_DEVICE, DRIVER_COMPARISON_IGNORED,
+ V(0, 0, 0, 0), "FEATURE_HARDWARE_VIDEO_DECODING_NO_LINUX_NVIDIA", "");
+
+ // Disable on all AMD devices not using Mesa.
+ APPEND_TO_DRIVER_BLOCKLIST_EXT(
+ OperatingSystem::Linux, ScreenSizeStatus::All, BatteryStatus::All,
+ WindowProtocol::All, DriverVendor::NonMesaAll, DeviceFamily::AtiAll,
+ nsIGfxInfo::FEATURE_HARDWARE_VIDEO_DECODING,
+ nsIGfxInfo::FEATURE_BLOCKED_DEVICE, DRIVER_COMPARISON_IGNORED,
+ V(0, 0, 0, 0), "FEATURE_HARDWARE_VIDEO_DECODING_NO_LINUX_AMD", "");
+
+ // Disable on r600 driver due to decoding artifacts (Bug 1824307)
+ APPEND_TO_DRIVER_BLOCKLIST_EXT(
+ OperatingSystem::Linux, ScreenSizeStatus::All, BatteryStatus::All,
+ WindowProtocol::All, DriverVendor::MesaR600, DeviceFamily::All,
+ nsIGfxInfo::FEATURE_HARDWARE_VIDEO_DECODING,
+ nsIGfxInfo::FEATURE_BLOCKED_DEVICE, DRIVER_COMPARISON_IGNORED,
+ V(0, 0, 0, 0), "FEATURE_HARDWARE_VIDEO_DECODING_NO_R600", "");
+
+ // Disable on Release/late Beta on AMD
+#if !defined(EARLY_BETA_OR_EARLIER)
+ APPEND_TO_DRIVER_BLOCKLIST(OperatingSystem::Linux, DeviceFamily::AtiAll,
+ nsIGfxInfo::FEATURE_HARDWARE_VIDEO_DECODING,
+ nsIGfxInfo::FEATURE_BLOCKED_DEVICE,
+ DRIVER_COMPARISON_IGNORED, V(0, 0, 0, 0),
+ "FEATURE_HARDWARE_VIDEO_DECODING_DISABLE", "");
+#endif
+ ////////////////////////////////////
+ // FEATURE_HW_DECODED_VIDEO_ZERO_COPY - ALLOWLIST
+ APPEND_TO_DRIVER_BLOCKLIST2(OperatingSystem::Linux, DeviceFamily::All,
+ nsIGfxInfo::FEATURE_HW_DECODED_VIDEO_ZERO_COPY,
+ nsIGfxInfo::FEATURE_ALLOW_ALWAYS,
+ DRIVER_COMPARISON_IGNORED, V(0, 0, 0, 0),
+ "FEATURE_ROLLOUT_ALL");
+
+ // Disable on all AMD devices using Mesa (Bug 1802844).
+ APPEND_TO_DRIVER_BLOCKLIST_EXT(
+ OperatingSystem::Linux, ScreenSizeStatus::All, BatteryStatus::All,
+ WindowProtocol::All, DriverVendor::MesaAll, DeviceFamily::AtiAll,
+ nsIGfxInfo::FEATURE_HW_DECODED_VIDEO_ZERO_COPY,
+ nsIGfxInfo::FEATURE_BLOCKED_DEVICE, DRIVER_COMPARISON_IGNORED,
+ V(0, 0, 0, 0), "FEATURE_HARDWARE_VIDEO_ZERO_COPY_LINUX_AMD_DISABLE",
+ "");
+
+ ////////////////////////////////////
+ // FEATURE_WEBRENDER_PARTIAL_PRESENT
+ APPEND_TO_DRIVER_BLOCKLIST_EXT(
+ OperatingSystem::Linux, ScreenSizeStatus::All, BatteryStatus::All,
+ WindowProtocol::X11, DriverVendor::NonMesaAll, DeviceFamily::NvidiaAll,
+ nsIGfxInfo::FEATURE_WEBRENDER_PARTIAL_PRESENT,
+ nsIGfxInfo::FEATURE_BLOCKED_DEVICE, DRIVER_COMPARISON_IGNORED,
+ V(0, 0, 0, 0), "FEATURE_ROLLOUT_WR_PARTIAL_PRESENT_NVIDIA_BINARY", "");
+
+ ////////////////////////////////////
+
+ APPEND_TO_DRIVER_BLOCKLIST_EXT(
+ OperatingSystem::Linux, ScreenSizeStatus::All, BatteryStatus::All,
+ WindowProtocol::All, DriverVendor::MesaNouveau, DeviceFamily::All,
+ nsIGfxInfo::FEATURE_THREADSAFE_GL, nsIGfxInfo::FEATURE_BLOCKED_DEVICE,
+ DRIVER_COMPARISON_IGNORED, V(0, 0, 0, 0),
+ "FEATURE_FAILURE_THREADSAFE_GL_NOUVEAU", "");
+
+ // Disabled due to high volume crash tracked in bug 1788573.
+ APPEND_TO_DRIVER_BLOCKLIST_EXT(
+ OperatingSystem::Linux, ScreenSizeStatus::All, BatteryStatus::All,
+ WindowProtocol::All, DriverVendor::NonMesaAll, DeviceFamily::NvidiaAll,
+ nsIGfxInfo::FEATURE_THREADSAFE_GL, nsIGfxInfo::FEATURE_BLOCKED_DEVICE,
+ DRIVER_COMPARISON_IGNORED, V(0, 0, 0, 0), "FEATURE_FAILURE_BUG_1788573",
+ "");
+ }
+ return *sDriverInfo;
+}
+
+bool GfxInfo::DoesWindowProtocolMatch(const nsAString& aBlocklistWindowProtocol,
+ const nsAString& aWindowProtocol) {
+ if (mIsWayland &&
+ aBlocklistWindowProtocol.Equals(
+ GfxDriverInfo::GetWindowProtocol(WindowProtocol::WaylandAll),
+ nsCaseInsensitiveStringComparator)) {
+ return true;
+ }
+ if (!mIsWayland &&
+ aBlocklistWindowProtocol.Equals(
+ GfxDriverInfo::GetWindowProtocol(WindowProtocol::X11All),
+ nsCaseInsensitiveStringComparator)) {
+ return true;
+ }
+ return GfxInfoBase::DoesWindowProtocolMatch(aBlocklistWindowProtocol,
+ aWindowProtocol);
+}
+
+bool GfxInfo::DoesDriverVendorMatch(const nsAString& aBlocklistVendor,
+ const nsAString& aDriverVendor) {
+ if (mIsMesa) {
+ if (aBlocklistVendor.Equals(
+ GfxDriverInfo::GetDriverVendor(DriverVendor::MesaAll),
+ nsCaseInsensitiveStringComparator)) {
+ return true;
+ }
+ if (mIsAccelerated &&
+ aBlocklistVendor.Equals(
+ GfxDriverInfo::GetDriverVendor(DriverVendor::HardwareMesaAll),
+ nsCaseInsensitiveStringComparator)) {
+ return true;
+ }
+ if (!mIsAccelerated &&
+ aBlocklistVendor.Equals(
+ GfxDriverInfo::GetDriverVendor(DriverVendor::SoftwareMesaAll),
+ nsCaseInsensitiveStringComparator)) {
+ return true;
+ }
+ if (aBlocklistVendor.Equals(GfxDriverInfo::GetDriverVendor(
+ DriverVendor::MesaNonIntelNvidiaAtiAll),
+ nsCaseInsensitiveStringComparator)) {
+ return !mVendorId.Equals("0x8086") && !mVendorId.Equals("0x10de") &&
+ !mVendorId.Equals("0x1002");
+ }
+ }
+ if (!mIsMesa && aBlocklistVendor.Equals(
+ GfxDriverInfo::GetDriverVendor(DriverVendor::NonMesaAll),
+ nsCaseInsensitiveStringComparator)) {
+ return true;
+ }
+ return GfxInfoBase::DoesDriverVendorMatch(aBlocklistVendor, aDriverVendor);
+}
+
+nsresult GfxInfo::GetFeatureStatusImpl(
+ int32_t aFeature, int32_t* aStatus, nsAString& aSuggestedDriverVersion,
+ const nsTArray<GfxDriverInfo>& aDriverInfo, nsACString& aFailureId,
+ OperatingSystem* aOS /* = nullptr */)
+
+{
+ NS_ENSURE_ARG_POINTER(aStatus);
+ *aStatus = nsIGfxInfo::FEATURE_STATUS_UNKNOWN;
+ aSuggestedDriverVersion.SetIsVoid(true);
+ OperatingSystem os = OperatingSystem::Linux;
+ if (aOS) *aOS = os;
+
+ if (sShutdownOccurred) {
+ return NS_OK;
+ }
+
+ GetData();
+
+ if (mGlxTestError) {
+ // If glxtest failed, block most features by default.
+ if (OnlyAllowFeatureOnKnownConfig(aFeature)) {
+ *aStatus = nsIGfxInfo::FEATURE_BLOCKED_DEVICE;
+ aFailureId = "FEATURE_FAILURE_GLXTEST_FAILED";
+ } else {
+ *aStatus = nsIGfxInfo::FEATURE_STATUS_OK;
+ }
+ return NS_OK;
+ }
+
+ if (mGLMajorVersion == 1) {
+ // We're on OpenGL 1. In most cases that indicates really old hardware.
+ // We better block them, rather than rely on them to fail gracefully,
+ // because they don't! see bug 696636
+ if (OnlyAllowFeatureOnKnownConfig(aFeature)) {
+ *aStatus = nsIGfxInfo::FEATURE_BLOCKED_DEVICE;
+ aFailureId = "FEATURE_FAILURE_OPENGL_1";
+ } else {
+ *aStatus = nsIGfxInfo::FEATURE_STATUS_OK;
+ }
+ return NS_OK;
+ }
+
+ // Blocklist software GL implementations from using layers acceleration.
+ // On the test infrastructure, we'll force-enable layers acceleration.
+ if (aFeature == nsIGfxInfo::FEATURE_OPENGL_LAYERS && !mIsAccelerated &&
+ !PR_GetEnv("MOZ_LAYERS_ALLOW_SOFTWARE_GL")) {
+ *aStatus = nsIGfxInfo::FEATURE_BLOCKED_DEVICE;
+ aFailureId = "FEATURE_FAILURE_SOFTWARE_GL";
+ return NS_OK;
+ }
+
+ if (aFeature == nsIGfxInfo::FEATURE_WEBRENDER) {
+ // Don't try Webrender on devices where we are guaranteed to fail.
+ if (mGLMajorVersion < 3) {
+ *aStatus = nsIGfxInfo::FEATURE_BLOCKED_DEVICE;
+ aFailureId = "FEATURE_FAILURE_OPENGL_LESS_THAN_3";
+ return NS_OK;
+ }
+
+ // Bug 1710400: Disable Webrender on the deprecated Intel DDX driver
+ for (const nsCString& driver : mDdxDrivers) {
+ if (strcasestr(driver.get(), "Intel")) {
+ *aStatus = nsIGfxInfo::FEATURE_BLOCKED_DEVICE;
+ aFailureId = "FEATURE_FAILURE_DDX_INTEL";
+ return NS_OK;
+ }
+ }
+ }
+
+ const struct {
+ int32_t mFeature;
+ int32_t mCodec;
+ } kFeatureToCodecs[] = {{nsIGfxInfo::FEATURE_H264_HW_DECODE, CODEC_HW_H264},
+ {nsIGfxInfo::FEATURE_VP8_HW_DECODE, CODEC_HW_VP8},
+ {nsIGfxInfo::FEATURE_VP9_HW_DECODE, CODEC_HW_VP9},
+ {nsIGfxInfo::FEATURE_AV1_HW_DECODE, CODEC_HW_AV1}};
+
+ for (const auto& pair : kFeatureToCodecs) {
+ if (aFeature != pair.mFeature) {
+ continue;
+ }
+ if (mVAAPISupportedCodecs & pair.mCodec) {
+ *aStatus = nsIGfxInfo::FEATURE_STATUS_OK;
+ } else {
+ *aStatus = nsIGfxInfo::FEATURE_BLOCKED_PLATFORM_TEST;
+ aFailureId = "FEATURE_FAILURE_VIDEO_DECODING_MISSING";
+ }
+ return NS_OK;
+ }
+
+ auto ret = GfxInfoBase::GetFeatureStatusImpl(
+ aFeature, aStatus, aSuggestedDriverVersion, aDriverInfo, aFailureId, &os);
+
+ // Probe VA-API on supported devices only
+ if (aFeature == nsIGfxInfo::FEATURE_HARDWARE_VIDEO_DECODING) {
+ if (!StaticPrefs::media_hardware_video_decoding_enabled_AtStartup()) {
+ return ret;
+ }
+ bool probeHWDecode = false;
+#ifdef MOZ_WAYLAND
+ probeHWDecode =
+ mIsAccelerated &&
+ (*aStatus == nsIGfxInfo::FEATURE_STATUS_OK ||
+ StaticPrefs::media_hardware_video_decoding_force_enabled_AtStartup() ||
+ StaticPrefs::media_ffmpeg_vaapi_enabled_AtStartup());
+#endif
+ if (probeHWDecode) {
+ GetDataVAAPI();
+ } else {
+ mIsVAAPISupported = Some(false);
+ }
+ if (!mIsVAAPISupported.value()) {
+ *aStatus = nsIGfxInfo::FEATURE_BLOCKED_PLATFORM_TEST;
+ aFailureId = "FEATURE_FAILURE_VIDEO_DECODING_TEST_FAILED";
+ }
+ }
+
+ return ret;
+}
+
+NS_IMETHODIMP
+GfxInfo::GetD2DEnabled(bool* aEnabled) { return NS_ERROR_FAILURE; }
+
+NS_IMETHODIMP
+GfxInfo::GetDWriteEnabled(bool* aEnabled) { return NS_ERROR_FAILURE; }
+
+NS_IMETHODIMP
+GfxInfo::GetDWriteVersion(nsAString& aDwriteVersion) {
+ return NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP GfxInfo::GetHasBattery(bool* aHasBattery) {
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+GfxInfo::GetEmbeddedInFirefoxReality(bool* aEmbeddedInFirefoxReality) {
+ return NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+GfxInfo::GetCleartypeParameters(nsAString& aCleartypeParams) {
+ return NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+GfxInfo::GetWindowProtocol(nsAString& aWindowProtocol) {
+ GetData();
+ if (mIsWayland) {
+ aWindowProtocol = GfxDriverInfo::GetWindowProtocol(WindowProtocol::Wayland);
+ } else if (mIsXWayland) {
+ aWindowProtocol =
+ GfxDriverInfo::GetWindowProtocol(WindowProtocol::XWayland);
+ } else {
+ aWindowProtocol = GfxDriverInfo::GetWindowProtocol(WindowProtocol::X11);
+ }
+ Telemetry::ScalarSet(Telemetry::ScalarID::GFX_LINUX_WINDOW_PROTOCOL,
+ aWindowProtocol);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+GfxInfo::GetTestType(nsAString& aTestType) {
+ GetData();
+ AppendASCIItoUTF16(mTestType, aTestType);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+GfxInfo::GetAdapterDescription(nsAString& aAdapterDescription) {
+ GetData();
+ AppendASCIItoUTF16(mAdapterDescription, aAdapterDescription);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+GfxInfo::GetAdapterDescription2(nsAString& aAdapterDescription) {
+ return NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+GfxInfo::GetAdapterRAM(uint32_t* aAdapterRAM) {
+ GetData();
+ *aAdapterRAM = mAdapterRAM;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+GfxInfo::GetAdapterRAM2(uint32_t* aAdapterRAM) { return NS_ERROR_FAILURE; }
+
+NS_IMETHODIMP
+GfxInfo::GetAdapterDriver(nsAString& aAdapterDriver) {
+ aAdapterDriver.Truncate();
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+GfxInfo::GetAdapterDriver2(nsAString& aAdapterDriver) {
+ return NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+GfxInfo::GetAdapterDriverVendor(nsAString& aAdapterDriverVendor) {
+ GetData();
+ CopyASCIItoUTF16(mDriverVendor, aAdapterDriverVendor);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+GfxInfo::GetAdapterDriverVendor2(nsAString& aAdapterDriverVendor) {
+ return NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+GfxInfo::GetAdapterDriverVersion(nsAString& aAdapterDriverVersion) {
+ GetData();
+ CopyASCIItoUTF16(mDriverVersion, aAdapterDriverVersion);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+GfxInfo::GetAdapterDriverVersion2(nsAString& aAdapterDriverVersion) {
+ return NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+GfxInfo::GetAdapterDriverDate(nsAString& aAdapterDriverDate) {
+ aAdapterDriverDate.Truncate();
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+GfxInfo::GetAdapterDriverDate2(nsAString& aAdapterDriverDate) {
+ return NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+GfxInfo::GetAdapterVendorID(nsAString& aAdapterVendorID) {
+ GetData();
+ CopyUTF8toUTF16(mVendorId, aAdapterVendorID);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+GfxInfo::GetAdapterVendorID2(nsAString& aAdapterVendorID) {
+ GetData();
+ CopyUTF8toUTF16(mSecondaryVendorId, aAdapterVendorID);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+GfxInfo::GetAdapterDeviceID(nsAString& aAdapterDeviceID) {
+ GetData();
+ CopyUTF8toUTF16(mDeviceId, aAdapterDeviceID);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+GfxInfo::GetAdapterDeviceID2(nsAString& aAdapterDeviceID) {
+ GetData();
+ CopyUTF8toUTF16(mSecondaryDeviceId, aAdapterDeviceID);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+GfxInfo::GetAdapterSubsysID(nsAString& aAdapterSubsysID) {
+ return NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+GfxInfo::GetAdapterSubsysID2(nsAString& aAdapterSubsysID) {
+ return NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+GfxInfo::GetIsGPU2Active(bool* aIsGPU2Active) {
+ // This is never the case, as the active GPU should be the primary GPU.
+ *aIsGPU2Active = false;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+GfxInfo::GetDrmRenderDevice(nsACString& aDrmRenderDevice) {
+ GetData();
+ aDrmRenderDevice.Assign(mDrmRenderDevice);
+ return NS_OK;
+}
+
+#ifdef DEBUG
+
+// Implement nsIGfxInfoDebug
+// We don't support spoofing anything on Linux
+
+NS_IMETHODIMP GfxInfo::SpoofVendorID(const nsAString& aVendorID) {
+ GetData();
+ CopyUTF16toUTF8(aVendorID, mVendorId);
+ mIsAccelerated = true;
+ return NS_OK;
+}
+
+NS_IMETHODIMP GfxInfo::SpoofDeviceID(const nsAString& aDeviceID) {
+ GetData();
+ CopyUTF16toUTF8(aDeviceID, mDeviceId);
+ return NS_OK;
+}
+
+NS_IMETHODIMP GfxInfo::SpoofDriverVersion(const nsAString& aDriverVersion) {
+ GetData();
+ CopyUTF16toUTF8(aDriverVersion, mDriverVersion);
+ return NS_OK;
+}
+
+NS_IMETHODIMP GfxInfo::SpoofOSVersion(uint32_t aVersion) {
+ // We don't support OS versioning on Linux. There's just "Linux".
+ return NS_OK;
+}
+
+#endif
+
+} // namespace mozilla::widget