/* -*- 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 #include #include #include #include #include #include #include "nsCRTGlue.h" #include "nsExceptionHandler.h" #include "nsUnicharUtils.h" #include "prenv.h" #include "nsPrintfCString.h" #include "nsWhitespaceTokenizer.h" #include "mozilla/Telemetry.h" #include "mozilla/gfx/Logging.h" #include "GfxInfoX11.h" #include #ifdef MOZ_WAYLAND # include "mozilla/widget/nsWaylandDisplay.h" # include "mozilla/widget/DMABufLibWrapper.h" #endif #define EXIT_STATUS_BUFFER_TOO_SMALL 2 #ifdef DEBUG bool fire_glxtest_process(); #endif namespace mozilla::widget { #ifdef DEBUG NS_IMPL_ISUPPORTS_INHERITED(GfxInfo, GfxInfoBase, nsIGfxInfoDebug) #endif // these global variables will be set when firing the glxtest process int glxtest_pipe = -1; pid_t glxtest_pid = 0; nsresult GfxInfo::Init() { mGLMajorVersion = 0; mGLMinorVersion = 0; mHasTextureFromPixmap = false; mIsMesa = false; mIsAccelerated = true; mIsWayland = false; mIsWaylandDRM = 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::IsWaylandDRM, mIsWaylandDRM); CrashReporter::AnnotateCrashReport( CrashReporter::Annotation::DesktopEnvironment, mDesktopEnvironment); 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); } } void GfxInfo::GetData() { GfxInfoBase::GetData(); // to understand this function, see bug 639842. We retrieve the OpenGL driver // information in a separate process to protect against bad drivers. // if glxtest_pipe == -1, that means that we already read the information if (glxtest_pipe == -1) return; enum { buf_size = 2048 }; char buf[buf_size]; ssize_t bytesread = read(glxtest_pipe, &buf, buf_size - 1); // -1 because we'll append a zero close(glxtest_pipe); glxtest_pipe = -1; // bytesread < 0 would mean that the above read() call failed. // This should never happen. If it did, the outcome would be to blocklist // anyway. if (bytesread < 0) { bytesread = 0; } else if (bytesread == buf_size - 1) { gfxCriticalNote << "glxtest: read from pipe exceeded buffer size"; } // let buf be a zero-terminated string buf[bytesread] = 0; // Wait for the glxtest process to finish. This serves 2 purposes: // * avoid having a zombie glxtest process laying around // * get the glxtest process status info. int glxtest_status = 0; bool wait_for_glxtest_process = true; bool waiting_for_glxtest_process_failed = false; int waitpid_errno = 0; while (wait_for_glxtest_process) { wait_for_glxtest_process = false; if (waitpid(glxtest_pid, &glxtest_status, 0) == -1) { waitpid_errno = errno; if (waitpid_errno == EINTR) { wait_for_glxtest_process = true; } else { // 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. waiting_for_glxtest_process_failed = (waitpid_errno != ECHILD); } } } int exit_code = EXIT_FAILURE; bool exited_with_error_code = false; if (!waiting_for_glxtest_process_failed && WIFEXITED(glxtest_status)) { exit_code = WEXITSTATUS(glxtest_status); exited_with_error_code = exit_code != EXIT_SUCCESS; } bool received_signal = !waiting_for_glxtest_process_failed && WIFSIGNALED(glxtest_status); bool error = waiting_for_glxtest_process_failed || exited_with_error_code || received_signal; bool errorLog = false; nsCString glVendor; nsCString glRenderer; nsCString glVersion; nsCString textureFromPixmap; // 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 screenInfo; nsCString adapterRam; nsCString drmRenderDevice; AutoTArray pciVendors; AutoTArray pciDevices; nsCString* stringToFill = nullptr; bool logString = false; char* bufptr = buf; 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, "DRI_DRIVER")) { stringToFill = &driDriver; } else if (!strcmp(line, "SCREEN_INFO")) { stringToFill = &screenInfo; } 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, "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); // 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") || !mIsAccelerated) { // Fallback to reporting swrast if GLX_MESA_query_renderer tells us // we're using an unaccelerated context. CopyUTF16toUTF8(GfxDriverInfo::GetDriverVendor(DriverVendor::MesaSWRast), mDriverVendor); mIsAccelerated = false; } 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; } } 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 (!screenInfo.IsEmpty()) { PRInt32 start = 0; PRInt32 loc = screenInfo.Find(";", PR_FALSE, start); while (loc != kNotFound) { int isDefault = 0; nsCString line(screenInfo.get() + start, loc - start); ScreenInfo info; if (sscanf(line.get(), "%ux%u:%u", &info.mWidth, &info.mHeight, &isDefault) == 3) { info.mIsDefault = isDefault != 0; mScreenInfo.AppendElement(info); } start = loc + 1; loc = screenInfo.Find(";", PR_FALSE, start); } } 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", "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.Length() == 1) { mVendorId = pciVendors[0]; } else if (pciVendors.IsEmpty()) { gfxCriticalNote << "No GPUs detected via PCI"; } else { gfxCriticalNote << "More than 1 GPU detected via PCI, cannot deduce vendor"; } } // 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 { gfxCriticalNote << "More than 1 GPU from same vendor detected via " "PCI, cannot deduce device"; 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"; } 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(); } } // Fallback to GL_VENDOR and GL_RENDERER. if (mVendorId.IsEmpty()) { mVendorId.Assign(glVendor.get()); } if (mDeviceId.IsEmpty()) { mDeviceId.Assign(glRenderer.get()); } mAdapterDescription.Assign(glRenderer); #ifdef MOZ_WAYLAND mIsWayland = gdk_display_get_default() && !GDK_IS_X11_DISPLAY(gdk_display_get_default()); if (mIsWayland) { mIsWaylandDRM = GetDMABufDevice()->IsDMABufVAAPIEnabled() || GetDMABufDevice()->IsDMABufWebGLEnabled() || GetDMABufDevice()->IsDMABufTexturesEnabled(); } #endif // 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. const char* windowEnv = getenv("XDG_SESSION_TYPE"); mIsXWayland = windowEnv && strcmp(windowEnv, "wayland") == 0; // Make a best effort guess at the desktop environment in use. Sadly there // does not appear to be a standard way to do this, so we check a few // different environment variables and search for relevant keywords. // // Note that some users manually change these values. Some applications check // the environment variable like we are here, and either not work or restrict // functionality. There may be some heroics we could go through to determine // the truth, but for the moment, this is the best we can do. This is // something to keep in mind when updating the blocklist. const char* desktopEnv = getenv("XDG_CURRENT_DESKTOP"); if (!desktopEnv) { desktopEnv = getenv("DESKTOP_SESSION"); } if (desktopEnv) { std::string currentDesktop(desktopEnv); for (auto& c : currentDesktop) { c = std::tolower(c); } if (currentDesktop.find("budgie") != std::string::npos) { // We need to check for Budgie first, because it might incorporate GNOME // into the environment variable value. CopyUTF16toUTF8( GfxDriverInfo::GetDesktopEnvironment(DesktopEnvironment::Budgie), mDesktopEnvironment); } else if (currentDesktop.find("gnome") != std::string::npos) { CopyUTF16toUTF8( GfxDriverInfo::GetDesktopEnvironment(DesktopEnvironment::GNOME), mDesktopEnvironment); } else if (currentDesktop.find("kde") != std::string::npos) { CopyUTF16toUTF8( GfxDriverInfo::GetDesktopEnvironment(DesktopEnvironment::KDE), mDesktopEnvironment); } else if (currentDesktop.find("xfce") != std::string::npos) { CopyUTF16toUTF8( GfxDriverInfo::GetDesktopEnvironment(DesktopEnvironment::XFCE), mDesktopEnvironment); } else if (currentDesktop.find("cinnamon") != std::string::npos) { CopyUTF16toUTF8( GfxDriverInfo::GetDesktopEnvironment(DesktopEnvironment::Cinnamon), mDesktopEnvironment); } else if (currentDesktop.find("enlightenment") != std::string::npos) { CopyUTF16toUTF8(GfxDriverInfo::GetDesktopEnvironment( DesktopEnvironment::Enlightenment), mDesktopEnvironment); } else if (currentDesktop.find("lxde") != std::string::npos || currentDesktop.find("lubuntu") != std::string::npos) { CopyUTF16toUTF8( GfxDriverInfo::GetDesktopEnvironment(DesktopEnvironment::LXDE), mDesktopEnvironment); } else if (currentDesktop.find("openbox") != std::string::npos) { CopyUTF16toUTF8( GfxDriverInfo::GetDesktopEnvironment(DesktopEnvironment::Openbox), mDesktopEnvironment); } else if (currentDesktop.find("i3") != std::string::npos) { CopyUTF16toUTF8( GfxDriverInfo::GetDesktopEnvironment(DesktopEnvironment::i3), mDesktopEnvironment); } else if (currentDesktop.find("mate") != std::string::npos) { CopyUTF16toUTF8( GfxDriverInfo::GetDesktopEnvironment(DesktopEnvironment::Mate), mDesktopEnvironment); } else if (currentDesktop.find("unity") != std::string::npos) { CopyUTF16toUTF8( GfxDriverInfo::GetDesktopEnvironment(DesktopEnvironment::Unity), mDesktopEnvironment); } else if (currentDesktop.find("pantheon") != std::string::npos) { CopyUTF16toUTF8( GfxDriverInfo::GetDesktopEnvironment(DesktopEnvironment::Pantheon), mDesktopEnvironment); } else if (currentDesktop.find("lxqt") != std::string::npos) { CopyUTF16toUTF8( GfxDriverInfo::GetDesktopEnvironment(DesktopEnvironment::LXQT), mDesktopEnvironment); } else if (currentDesktop.find("deepin") != std::string::npos) { CopyUTF16toUTF8( GfxDriverInfo::GetDesktopEnvironment(DesktopEnvironment::Deepin), mDesktopEnvironment); } else if (currentDesktop.find("dwm") != std::string::npos) { CopyUTF16toUTF8( GfxDriverInfo::GetDesktopEnvironment(DesktopEnvironment::Dwm), mDesktopEnvironment); } } if (mDesktopEnvironment.IsEmpty()) { if (getenv("GNOME_DESKTOP_SESSION_ID")) { CopyUTF16toUTF8( GfxDriverInfo::GetDesktopEnvironment(DesktopEnvironment::GNOME), mDesktopEnvironment); } else if (getenv("KDE_FULL_SESSION")) { CopyUTF16toUTF8( GfxDriverInfo::GetDesktopEnvironment(DesktopEnvironment::KDE), mDesktopEnvironment); } else if (getenv("MATE_DESKTOP_SESSION_ID")) { CopyUTF16toUTF8( GfxDriverInfo::GetDesktopEnvironment(DesktopEnvironment::Mate), mDesktopEnvironment); } else if (getenv("LXQT_SESSION_CONFIG")) { CopyUTF16toUTF8( GfxDriverInfo::GetDesktopEnvironment(DesktopEnvironment::LXQT), mDesktopEnvironment); } else { CopyUTF16toUTF8( GfxDriverInfo::GetDesktopEnvironment(DesktopEnvironment::Unknown), mDesktopEnvironment); } } if (error || errorLog) { if (!mAdapterDescription.IsEmpty()) { mAdapterDescription.AppendLiteral(" (See failure log)"); } else { mAdapterDescription.AppendLiteral("See failure log"); } mGlxTestError = true; } if (error) { nsAutoCString msg("glxtest: process failed"); if (waiting_for_glxtest_process_failed) { msg.AppendPrintf(" (waitpid failed with errno=%d for pid %d)", waitpid_errno, glxtest_pid); } if (exited_with_error_code) { if (exit_code == EXIT_STATUS_BUFFER_TOO_SMALL) { msg.AppendLiteral(" (buffer too small)"); } else { msg.AppendPrintf(" (exited with status %d)", WEXITSTATUS(glxtest_status)); } } if (received_signal) { msg.AppendPrintf(" (received signal %d)", WTERMSIG(glxtest_status)); } gfxCriticalNote << msg.get(); } AddCrashReportAnnotations(); } const nsTArray& 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, DesktopEnvironment::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 baseline (ported from old blocklist) APPEND_TO_DRIVER_BLOCKLIST_EXT( OperatingSystem::Linux, ScreenSizeStatus::All, BatteryStatus::All, DesktopEnvironment::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 // Intel Mesa baseline, chosen arbitrarily. APPEND_TO_DRIVER_BLOCKLIST( OperatingSystem::Linux, DeviceFamily::IntelAll, nsIGfxInfo::FEATURE_WEBRENDER, nsIGfxInfo::FEATURE_BLOCKED_DRIVER_VERSION, DRIVER_LESS_THAN, V(18, 0, 0, 0), "FEATURE_FAILURE_WEBRENDER_OLD_MESA", "Mesa 18.0.0.0"); // Nvidia Mesa baseline, see bug 1563859. APPEND_TO_DRIVER_BLOCKLIST_EXT( OperatingSystem::Linux, ScreenSizeStatus::All, BatteryStatus::All, DesktopEnvironment::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 Nvidia devices not using Mesa for now. APPEND_TO_DRIVER_BLOCKLIST_EXT( OperatingSystem::Linux, ScreenSizeStatus::All, BatteryStatus::All, DesktopEnvironment::All, WindowProtocol::All, DriverVendor::NonMesaAll, DeviceFamily::NvidiaAll, nsIGfxInfo::FEATURE_WEBRENDER, nsIGfxInfo::FEATURE_BLOCKED_DEVICE, DRIVER_COMPARISON_IGNORED, V(0, 0, 0, 0), "FEATURE_FAILURE_WEBRENDER_NO_LINUX_NVIDIA", ""); // ATI Mesa baseline, chosen arbitrarily. APPEND_TO_DRIVER_BLOCKLIST_EXT( OperatingSystem::Linux, ScreenSizeStatus::All, BatteryStatus::All, DesktopEnvironment::All, WindowProtocol::All, DriverVendor::MesaAll, DeviceFamily::AtiAll, nsIGfxInfo::FEATURE_WEBRENDER, nsIGfxInfo::FEATURE_BLOCKED_DRIVER_VERSION, DRIVER_LESS_THAN, V(18, 0, 0, 0), "FEATURE_FAILURE_WEBRENDER_OLD_MESA", "Mesa 18.0.0.0"); // Disable on all ATI devices not using Mesa for now. APPEND_TO_DRIVER_BLOCKLIST_EXT( OperatingSystem::Linux, ScreenSizeStatus::All, BatteryStatus::All, DesktopEnvironment::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", ""); // Bug 1673939 - Garbled text on RS880 GPUs with Mesa drivers. APPEND_TO_DRIVER_BLOCKLIST_EXT( OperatingSystem::Linux, ScreenSizeStatus::All, BatteryStatus::All, DesktopEnvironment::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", ""); //////////////////////////////////// // FEATURE_WEBRENDER - ALLOWLIST // Intel Mesa baseline, chosen arbitrarily. APPEND_TO_DRIVER_BLOCKLIST_EXT( OperatingSystem::Linux, ScreenSizeStatus::All, BatteryStatus::All, DesktopEnvironment::GNOME, WindowProtocol::X11, DriverVendor::MesaAll, DeviceFamily::IntelRolloutWebRender, nsIGfxInfo::FEATURE_WEBRENDER, nsIGfxInfo::FEATURE_ALLOW_ALWAYS, DRIVER_GREATER_THAN_OR_EQUAL, V(18, 0, 0, 0), "FEATURE_ROLLOUT_INTEL_GNOME_X11_MESA", "Mesa 18.0.0.0"); APPEND_TO_DRIVER_BLOCKLIST_EXT( OperatingSystem::Linux, ScreenSizeStatus::All, BatteryStatus::All, DesktopEnvironment::GNOME, WindowProtocol::Wayland, DriverVendor::MesaAll, DeviceFamily::IntelRolloutWebRender, nsIGfxInfo::FEATURE_WEBRENDER, nsIGfxInfo::FEATURE_ALLOW_ALWAYS, DRIVER_GREATER_THAN_OR_EQUAL, V(18, 0, 0, 0), "FEATURE_ROLLOUT_INTEL_GNOME_WAYLAND_MESA", "Mesa 18.0.0.0"); // ATI Mesa baseline, chosen arbitrarily. APPEND_TO_DRIVER_BLOCKLIST_EXT( OperatingSystem::Linux, ScreenSizeStatus::All, BatteryStatus::All, DesktopEnvironment::GNOME, WindowProtocol::X11, DriverVendor::MesaAll, DeviceFamily::AtiRolloutWebRender, nsIGfxInfo::FEATURE_WEBRENDER, nsIGfxInfo::FEATURE_ALLOW_ALWAYS, DRIVER_GREATER_THAN_OR_EQUAL, V(18, 0, 0, 0), "FEATURE_ROLLOUT_ATI_GNOME_X11_MESA", "Mesa 18.0.0.0"); APPEND_TO_DRIVER_BLOCKLIST_EXT( OperatingSystem::Linux, ScreenSizeStatus::All, BatteryStatus::All, DesktopEnvironment::GNOME, WindowProtocol::Wayland, DriverVendor::MesaAll, DeviceFamily::AtiRolloutWebRender, nsIGfxInfo::FEATURE_WEBRENDER, nsIGfxInfo::FEATURE_ALLOW_ALWAYS, DRIVER_GREATER_THAN_OR_EQUAL, V(18, 0, 0, 0), "FEATURE_ROLLOUT_ATI_GNOME_WAYLAND_MESA", "Mesa 18.0.0.0"); #ifdef EARLY_BETA_OR_EARLIER // Intel Mesa baseline, chosen arbitrarily. APPEND_TO_DRIVER_BLOCKLIST_EXT( OperatingSystem::Linux, ScreenSizeStatus::All, BatteryStatus::All, DesktopEnvironment::All, WindowProtocol::All, DriverVendor::MesaAll, DeviceFamily::IntelRolloutWebRender, nsIGfxInfo::FEATURE_WEBRENDER, nsIGfxInfo::FEATURE_ALLOW_ALWAYS, DRIVER_GREATER_THAN_OR_EQUAL, V(18, 0, 0, 0), "FEATURE_ROLLOUT_EARLY_BETA_INTEL_MESA", "Mesa 18.0.0.0"); // ATI Mesa baseline, chosen arbitrarily. APPEND_TO_DRIVER_BLOCKLIST_EXT( OperatingSystem::Linux, ScreenSizeStatus::All, BatteryStatus::All, DesktopEnvironment::All, WindowProtocol::All, DriverVendor::MesaAll, DeviceFamily::AtiRolloutWebRender, nsIGfxInfo::FEATURE_WEBRENDER, nsIGfxInfo::FEATURE_ALLOW_ALWAYS, DRIVER_GREATER_THAN_OR_EQUAL, V(18, 0, 0, 0), "FEATURE_ROLLOUT_EARLY_BETA_ATI_MESA", "Mesa 18.0.0.0"); #endif #ifdef NIGHTLY_BUILD // Intel Mesa baseline, chosen arbitrarily. APPEND_TO_DRIVER_BLOCKLIST_EXT( OperatingSystem::Linux, ScreenSizeStatus::All, BatteryStatus::All, DesktopEnvironment::All, WindowProtocol::All, DriverVendor::MesaAll, DeviceFamily::IntelRolloutWebRender, nsIGfxInfo::FEATURE_WEBRENDER, nsIGfxInfo::FEATURE_ALLOW_QUALIFIED, DRIVER_GREATER_THAN_OR_EQUAL, V(18, 0, 0, 0), "FEATURE_ROLLOUT_NIGHTLY_INTEL_MESA", "Mesa 18.0.0.0"); // Nvidia Mesa baseline, see bug 1563859. APPEND_TO_DRIVER_BLOCKLIST_EXT( OperatingSystem::Linux, ScreenSizeStatus::All, BatteryStatus::All, DesktopEnvironment::All, WindowProtocol::All, DriverVendor::MesaAll, DeviceFamily::NvidiaRolloutWebRender, nsIGfxInfo::FEATURE_WEBRENDER, nsIGfxInfo::FEATURE_ALLOW_QUALIFIED, DRIVER_GREATER_THAN_OR_EQUAL, V(18, 2, 0, 0), "FEATURE_ROLLOUT_NIGHTLY_NVIDIA_MESA", "Mesa 18.2.0.0"); // ATI Mesa baseline, chosen arbitrarily. APPEND_TO_DRIVER_BLOCKLIST_EXT( OperatingSystem::Linux, ScreenSizeStatus::All, BatteryStatus::All, DesktopEnvironment::All, WindowProtocol::All, DriverVendor::MesaAll, DeviceFamily::AtiRolloutWebRender, nsIGfxInfo::FEATURE_WEBRENDER, nsIGfxInfo::FEATURE_ALLOW_QUALIFIED, DRIVER_GREATER_THAN_OR_EQUAL, V(18, 0, 0, 0), "FEATURE_ROLLOUT_NIGHTLY_ATI_MESA", "Mesa 18.0.0.0"); #endif //////////////////////////////////// // FEATURE_WEBRENDER_SOFTWARE - ALLOWLIST #ifdef EARLY_BETA_OR_EARLIER # if defined(_M_IX86) || defined(_M_X64) || defined(__i386__) || \ defined(__i386) || defined(__amd64__) APPEND_TO_DRIVER_BLOCKLIST_EXT( OperatingSystem::Linux, ScreenSizeStatus::SmallAndMedium, BatteryStatus::All, DesktopEnvironment::All, WindowProtocol::All, DriverVendor::NonMesaAll, DeviceFamily::All, nsIGfxInfo::FEATURE_WEBRENDER_SOFTWARE, nsIGfxInfo::FEATURE_ALLOW_ALWAYS, DRIVER_COMPARISON_IGNORED, V(0, 0, 0, 0), "FEATURE_ROLLOUT_NIGHTLY_SOFTWARE_WR_NON_MESA_S_M_SCRN", ""); APPEND_TO_DRIVER_BLOCKLIST_EXT( OperatingSystem::Linux, ScreenSizeStatus::SmallAndMedium, BatteryStatus::All, DesktopEnvironment::All, WindowProtocol::All, DriverVendor::HardwareMesaAll, DeviceFamily::All, nsIGfxInfo::FEATURE_WEBRENDER_SOFTWARE, nsIGfxInfo::FEATURE_ALLOW_ALWAYS, DRIVER_COMPARISON_IGNORED, V(0, 0, 0, 0), "FEATURE_ROLLOUT_NIGHTLY_SOFTWARE_WR_HW_MESA_S_M_SCRN", ""); # endif #endif //////////////////////////////////// APPEND_TO_DRIVER_BLOCKLIST_EXT( OperatingSystem::Linux, ScreenSizeStatus::All, BatteryStatus::All, DesktopEnvironment::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", ""); } 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 (!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& 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 all features by default. *aStatus = nsIGfxInfo::FEATURE_BLOCKED_DEVICE; aFailureId = "FEATURE_FAILURE_GLXTEST_FAILED"; 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 *aStatus = nsIGfxInfo::FEATURE_BLOCKED_DEVICE; aFailureId = "FEATURE_FAILURE_OPENGL_1"; 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; } return GfxInfoBase::GetFeatureStatusImpl( aFeature, aStatus, aSuggestedDriverVersion, aDriverInfo, aFailureId, &os); } 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) { if (mIsWaylandDRM) { aWindowProtocol = GfxDriverInfo::GetWindowProtocol(WindowProtocol::WaylandDRM); } else { 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::GetDesktopEnvironment(nsAString& aDesktopEnvironment) { GetData(); AppendASCIItoUTF16(mDesktopEnvironment, aDesktopEnvironment); 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::GetDisplayInfo(nsTArray& aDisplayInfo) { GetData(); for (auto screenInfo : mScreenInfo) { nsString infoString; infoString.AppendPrintf("%dx%d %s", screenInfo.mWidth, screenInfo.mHeight, screenInfo.mIsDefault ? "default" : ""); aDisplayInfo.AppendElement(infoString); } return aDisplayInfo.IsEmpty() ? NS_ERROR_FAILURE : NS_OK; } NS_IMETHODIMP GfxInfo::GetDisplayWidth(nsTArray& aDisplayWidth) { for (auto screenInfo : mScreenInfo) { aDisplayWidth.AppendElement((uint32_t)screenInfo.mWidth); } return NS_OK; } NS_IMETHODIMP GfxInfo::GetDisplayHeight(nsTArray& aDisplayHeight) { for (auto screenInfo : mScreenInfo) { aDisplayHeight.AppendElement((uint32_t)screenInfo.mHeight); } return NS_OK; } 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; } NS_IMETHODIMP GfxInfo::FireTestProcess() { // If the pid is zero, then we have never run the test process to query for // driver information. This would normally be run on startup, but we need to // manually invoke it for XPC shell tests. if (glxtest_pid == 0) { fire_glxtest_process(); } return NS_OK; } #endif } // namespace mozilla::widget