Source code

Revision control

Copy as Markdown

Other Tools

/* 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 "GLLibraryEGL.h"
#include "gfxConfig.h"
#include "gfxCrashReporterUtils.h"
#include "gfxEnv.h"
#include "gfxUtils.h"
#include "mozilla/Preferences.h"
#include "mozilla/Assertions.h"
#include "mozilla/gfx/gfxVars.h"
#include "mozilla/gfx/Logging.h"
#include "mozilla/Telemetry.h"
#include "mozilla/Tokenizer.h"
#include "mozilla/ScopeExit.h"
#include "mozilla/StaticPrefs_gfx.h"
#include "mozilla/StaticPrefs_webgl.h"
#include "mozilla/Unused.h"
#include "nsDirectoryServiceDefs.h"
#include "nsDirectoryServiceUtils.h"
#include "nsPrintfCString.h"
#ifdef XP_WIN
# include "mozilla/gfx/DeviceManagerDx.h"
# include "nsWindowsHelpers.h"
# include "prerror.h"
# include <d3d11.h>
#endif
#include "OGLShaderProgram.h"
#include "prenv.h"
#include "prsystem.h"
#include "GLContext.h"
#include "GLContextProvider.h"
#include "GLLibraryLoader.h"
#include "GLReadTexImageHelper.h"
#include "ScopedGLHelpers.h"
#ifdef MOZ_WIDGET_GTK
# include "mozilla/WidgetUtilsGtk.h"
# include "mozilla/widget/DMABufLibWrapper.h"
# ifdef MOZ_WAYLAND
# include "mozilla/widget/nsWaylandDisplay.h"
# endif // MOZ_WIDGET_GTK
# include <gdk/gdk.h>
#endif // MOZ_WAYLAND
#include <mutex> // for call_once
namespace mozilla {
namespace gl {
StaticMutex GLLibraryEGL::sMutex;
StaticRefPtr<GLLibraryEGL> GLLibraryEGL::sInstance;
// should match the order of EGLExtensions, and be null-terminated.
static const char* sEGLLibraryExtensionNames[] = {
"EGL_ANDROID_get_native_client_buffer",
"EGL_ANGLE_device_creation",
"EGL_ANGLE_device_creation_d3d11",
"EGL_ANGLE_platform_angle",
"EGL_ANGLE_platform_angle_d3d",
"EGL_EXT_device_enumeration",
"EGL_EXT_device_query",
"EGL_EXT_platform_device",
"EGL_MESA_platform_surfaceless"};
// should match the order of EGLExtensions, and be null-terminated.
static const char* sEGLExtensionNames[] = {
"EGL_KHR_image_base",
"EGL_KHR_image_pixmap",
"EGL_KHR_gl_texture_2D_image",
"EGL_ANGLE_surface_d3d_texture_2d_share_handle",
"EGL_EXT_create_context_robustness",
"EGL_KHR_image",
"EGL_KHR_fence_sync",
"EGL_KHR_wait_sync",
"EGL_ANDROID_native_fence_sync",
"EGL_ANDROID_image_crop",
"EGL_ANGLE_d3d_share_handle_client_buffer",
"EGL_KHR_create_context",
"EGL_KHR_stream",
"EGL_KHR_stream_consumer_gltexture",
"EGL_NV_stream_consumer_gltexture_yuv",
"EGL_ANGLE_stream_producer_d3d_texture",
"EGL_KHR_surfaceless_context",
"EGL_KHR_create_context_no_error",
"EGL_MOZ_create_context_provoking_vertex_dont_care",
"EGL_EXT_swap_buffers_with_damage",
"EGL_KHR_swap_buffers_with_damage",
"EGL_EXT_buffer_age",
"EGL_KHR_partial_update",
"EGL_NV_robustness_video_memory_purge",
"EGL_EXT_image_dma_buf_import",
"EGL_EXT_image_dma_buf_import_modifiers",
"EGL_MESA_image_dma_buf_export",
"EGL_KHR_no_config_context",
};
PRLibrary* LoadApitraceLibrary() {
const char* path = nullptr;
#ifdef ANDROID
// We only need to explicitly dlopen egltrace
// on android as we can use LD_PRELOAD or other tricks
// on other platforms. We look for it in /data/local
// as that's writeable by all users.
path = "/data/local/tmp/egltrace.so";
#endif
if (!path) return nullptr;
// Initialization of gfx prefs here is only needed during the unit tests...
if (!StaticPrefs::gfx_apitrace_enabled_AtStartup()) {
return nullptr;
}
static PRLibrary* sApitraceLibrary = nullptr;
if (sApitraceLibrary) return sApitraceLibrary;
nsAutoCString logFile;
Preferences::GetCString("gfx.apitrace.logfile", logFile);
if (logFile.IsEmpty()) {
logFile = "firefox.trace";
}
// The firefox process can't write to /data/local, but it can write
// to $GRE_HOME/
nsAutoCString logPath;
logPath.AppendPrintf("%s/%s", getenv("GRE_HOME"), logFile.get());
#ifndef XP_WIN // Windows is missing setenv and forbids PR_LoadLibrary.
// apitrace uses the TRACE_FILE environment variable to determine where
// to log trace output to
printf_stderr("Logging GL tracing output to %s", logPath.get());
setenv("TRACE_FILE", logPath.get(), false);
printf_stderr("Attempting load of %s\n", path);
sApitraceLibrary = PR_LoadLibrary(path);
#endif
return sApitraceLibrary;
}
#ifdef XP_WIN
// see the comment in GLLibraryEGL::EnsureInitialized() for the rationale here.
static PRLibrary* LoadLibraryForEGLOnWindows(const nsAString& filename) {
nsAutoString path(gfx::gfxVars::GREDirectory());
path.Append(PR_GetDirectorySeparator());
path.Append(filename);
PRLibSpec lspec;
lspec.type = PR_LibSpec_PathnameU;
lspec.value.pathname_u = path.get();
PRLibrary* lib = PR_LoadLibraryWithFlags(lspec, PR_LD_LAZY | PR_LD_LOCAL);
if (!lib) {
gfxCriticalNote << "Failed to load " << path.get() << " " << PR_GetError()
<< " " << PR_GetOSError();
}
return lib;
}
#endif // XP_WIN
static std::shared_ptr<EglDisplay> GetAndInitDisplay(
GLLibraryEGL& egl, void* displayType,
const StaticMutexAutoLock& aProofOfLock) {
const auto display = egl.fGetDisplay(displayType);
if (!display) return nullptr;
return EglDisplay::Create(egl, display, false, aProofOfLock);
}
#ifdef MOZ_WIDGET_GTK
static std::shared_ptr<EglDisplay> GetAndInitDeviceDisplay(
GLLibraryEGL& egl, const StaticMutexAutoLock& aProofOfLock) {
nsAutoCString drmRenderDevice(gfx::gfxVars::DrmRenderDevice());
if (drmRenderDevice.IsEmpty() ||
!egl.IsExtensionSupported(EGLLibExtension::EXT_platform_device) ||
!egl.IsExtensionSupported(EGLLibExtension::EXT_device_enumeration)) {
return nullptr;
}
EGLint maxDevices;
if (!egl.fQueryDevicesEXT(0, nullptr, &maxDevices)) {
return nullptr;
}
std::vector<EGLDeviceEXT> devices(maxDevices);
EGLint numDevices;
if (!egl.fQueryDevicesEXT(devices.size(), devices.data(), &numDevices)) {
return nullptr;
}
devices.resize(numDevices);
EGLDisplay display = EGL_NO_DISPLAY;
for (const auto& device : devices) {
const char* renderNodeString =
egl.fQueryDeviceStringEXT(device, LOCAL_EGL_DRM_RENDER_NODE_FILE_EXT);
if (renderNodeString &&
strcmp(renderNodeString, drmRenderDevice.get()) == 0) {
const EGLAttrib attrib_list[] = {LOCAL_EGL_NONE};
display = egl.fGetPlatformDisplay(LOCAL_EGL_PLATFORM_DEVICE_EXT, device,
attrib_list);
break;
}
}
if (!display) {
return nullptr;
}
return EglDisplay::Create(egl, display, true, aProofOfLock);
}
static std::shared_ptr<EglDisplay> GetAndInitSoftwareDisplay(
GLLibraryEGL& egl, const StaticMutexAutoLock& aProofOfLock) {
if (!egl.IsExtensionSupported(EGLLibExtension::EXT_platform_device) ||
!egl.IsExtensionSupported(EGLLibExtension::EXT_device_enumeration)) {
return nullptr;
}
EGLint maxDevices;
if (!egl.fQueryDevicesEXT(0, nullptr, &maxDevices)) {
return nullptr;
}
std::vector<EGLDeviceEXT> devices(maxDevices);
EGLint numDevices;
if (!egl.fQueryDevicesEXT(devices.size(), devices.data(), &numDevices)) {
return nullptr;
}
devices.resize(numDevices);
EGLDisplay display = EGL_NO_DISPLAY;
for (const auto& device : devices) {
const char* renderNodeString =
egl.fQueryDeviceStringEXT(device, LOCAL_EGL_DRM_RENDER_NODE_FILE_EXT);
// We are looking for a device with no file
if (!renderNodeString || *renderNodeString == 0) {
const EGLAttrib attrib_list[] = {LOCAL_EGL_NONE};
display = egl.fGetPlatformDisplay(LOCAL_EGL_PLATFORM_DEVICE_EXT, device,
attrib_list);
break;
}
}
if (!display) {
return nullptr;
}
return EglDisplay::Create(egl, display, true, aProofOfLock);
}
static std::shared_ptr<EglDisplay> GetAndInitSurfacelessDisplay(
GLLibraryEGL& egl, const StaticMutexAutoLock& aProofOfLock) {
if (!egl.IsExtensionSupported(EGLLibExtension::MESA_platform_surfaceless)) {
return nullptr;
}
const EGLAttrib attrib_list[] = {LOCAL_EGL_NONE};
const EGLDisplay display = egl.fGetPlatformDisplay(
LOCAL_EGL_PLATFORM_SURFACELESS_MESA, EGL_DEFAULT_DISPLAY, attrib_list);
if (display == EGL_NO_DISPLAY) {
return nullptr;
}
return EglDisplay::Create(egl, display, true, aProofOfLock);
}
#endif
static auto EglDebugLayersEnabled() {
EGLAttrib ret = LOCAL_EGL_FALSE;
if (StaticPrefs::gfx_direct3d11_enable_debug_layer_AtStartup()) {
ret = LOCAL_EGL_TRUE;
}
return ret;
}
static std::shared_ptr<EglDisplay> GetAndInitWARPDisplay(
GLLibraryEGL& egl, void* displayType,
const StaticMutexAutoLock& aProofOfLock) {
const EGLAttrib attrib_list[] = {
LOCAL_EGL_PLATFORM_ANGLE_DEVICE_TYPE_ANGLE,
LOCAL_EGL_PLATFORM_ANGLE_DEVICE_TYPE_WARP_ANGLE,
LOCAL_EGL_PLATFORM_ANGLE_DEBUG_LAYERS_ENABLED_ANGLE,
EglDebugLayersEnabled(),
// Requires:
LOCAL_EGL_PLATFORM_ANGLE_TYPE_ANGLE,
LOCAL_EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE, LOCAL_EGL_NONE};
const EGLDisplay display = egl.fGetPlatformDisplay(
LOCAL_EGL_PLATFORM_ANGLE_ANGLE, displayType, attrib_list);
if (display == EGL_NO_DISPLAY) {
const EGLint err = egl.fGetError();
if (err != LOCAL_EGL_SUCCESS) {
gfxCriticalError() << "Unexpected GL error: " << gfx::hexa(err);
MOZ_CRASH("GFX: Unexpected GL error.");
}
return nullptr;
}
return EglDisplay::Create(egl, display, true, aProofOfLock);
}
std::shared_ptr<EglDisplay> GLLibraryEGL::CreateDisplay(
ID3D11Device* const d3d11Device) {
StaticMutexAutoLock lock(sMutex);
EGLDeviceEXT eglDevice =
fCreateDeviceANGLE(LOCAL_EGL_D3D11_DEVICE_ANGLE, d3d11Device, nullptr);
if (!eglDevice) {
gfxCriticalNote << "Failed to get EGLDeviceEXT of D3D11Device";
return nullptr;
}
const char* features[] = {"allowES3OnFL10_0", nullptr};
// Create an EGLDisplay using the EGLDevice
const EGLAttrib attrib_list[] = {LOCAL_EGL_FEATURE_OVERRIDES_ENABLED_ANGLE,
reinterpret_cast<EGLAttrib>(features),
LOCAL_EGL_NONE};
const auto display = fGetPlatformDisplay(LOCAL_EGL_PLATFORM_DEVICE_EXT,
eglDevice, attrib_list);
if (!display) {
gfxCriticalNote << "Failed to get EGLDisplay of D3D11Device";
return nullptr;
}
if (!display) {
const EGLint err = fGetError();
if (err != LOCAL_EGL_SUCCESS) {
gfxCriticalError() << "Unexpected GL error: " << gfx::hexa(err);
MOZ_CRASH("GFX: Unexpected GL error.");
}
return nullptr;
}
const auto ret = EglDisplay::Create(*this, display, false, lock);
if (!ret) {
const EGLint err = fGetError();
if (err != LOCAL_EGL_SUCCESS) {
gfxCriticalError()
<< "Failed to initialize EGLDisplay for WebRender error: "
<< gfx::hexa(err);
}
return nullptr;
}
return ret;
}
static bool IsAccelAngleSupported(nsACString* const out_failureId) {
if (!gfx::gfxVars::AllowWebglAccelAngle()) {
if (out_failureId->IsEmpty()) {
*out_failureId = "FEATURE_FAILURE_ACCL_ANGLE_NOT_OK"_ns;
}
return false;
}
return true;
}
class AngleErrorReporting {
public:
AngleErrorReporting() : mFailureId(nullptr) {
// No static constructor
}
void SetFailureId(nsACString* const aFailureId) { mFailureId = aFailureId; }
void logError(const char* errorMessage) {
if (!mFailureId) {
return;
}
nsCString str(errorMessage);
Tokenizer tokenizer(str);
// Parse "ANGLE Display::initialize error " << error.getID() << ": "
// << error.getMessage()
nsCString currWord;
Tokenizer::Token intToken;
if (tokenizer.CheckWord("ANGLE") && tokenizer.CheckWhite() &&
tokenizer.CheckWord("Display") && tokenizer.CheckChar(':') &&
tokenizer.CheckChar(':') && tokenizer.CheckWord("initialize") &&
tokenizer.CheckWhite() && tokenizer.CheckWord("error") &&
tokenizer.CheckWhite() &&
tokenizer.Check(Tokenizer::TOKEN_INTEGER, intToken)) {
*mFailureId = "FAILURE_ID_ANGLE_ID_";
mFailureId->AppendPrintf("%" PRIu64, intToken.AsInteger());
} else {
*mFailureId = "FAILURE_ID_ANGLE_UNKNOWN";
}
}
private:
nsACString* mFailureId;
};
AngleErrorReporting gAngleErrorReporter;
static std::shared_ptr<EglDisplay> GetAndInitDisplayForAccelANGLE(
GLLibraryEGL& egl, nsACString* const out_failureId,
const StaticMutexAutoLock& aProofOfLock) {
gfx::FeatureState& d3d11ANGLE =
gfx::gfxConfig::GetFeature(gfx::Feature::D3D11_HW_ANGLE);
if (!StaticPrefs::webgl_angle_try_d3d11()) {
d3d11ANGLE.UserDisable("User disabled D3D11 ANGLE by pref",
"FAILURE_ID_ANGLE_PREF"_ns);
}
if (StaticPrefs::webgl_angle_force_d3d11()) {
d3d11ANGLE.UserForceEnable(
"User force-enabled D3D11 ANGLE on disabled hardware");
}
gAngleErrorReporter.SetFailureId(out_failureId);
auto guardShutdown = mozilla::MakeScopeExit([&] {
gAngleErrorReporter.SetFailureId(nullptr);
// NOTE: Ideally we should be calling ANGLEPlatformShutdown after the
// ANGLE display is destroyed. However gAngleErrorReporter
// will live longer than the ANGLE display so we're fine.
});
if (gfx::gfxConfig::IsForcedOnByUser(gfx::Feature::D3D11_HW_ANGLE)) {
return GetAndInitDisplay(egl, LOCAL_EGL_D3D11_ONLY_DISPLAY_ANGLE,
aProofOfLock);
}
std::shared_ptr<EglDisplay> ret;
if (d3d11ANGLE.IsEnabled()) {
ret = GetAndInitDisplay(egl, LOCAL_EGL_D3D11_ELSE_D3D9_DISPLAY_ANGLE,
aProofOfLock);
}
if (!ret) {
ret = GetAndInitDisplay(egl, EGL_DEFAULT_DISPLAY, aProofOfLock);
}
if (!ret && out_failureId->IsEmpty()) {
*out_failureId = "FEATURE_FAILURE_ACCL_ANGLE_NO_DISP"_ns;
}
return ret;
}
// -
#if defined(XP_UNIX)
# define GLES2_LIB "libGLESv2.so"
# define GLES2_LIB2 "libGLESv2.so.2"
# define GL_LIB "libGL.so"
# define GL_LIB2 "libGL.so.1"
#elif defined(XP_WIN)
# define GLES2_LIB "libGLESv2.dll"
#else
# error "Platform not recognized"
#endif
Maybe<SymbolLoader> GLLibraryEGL::GetSymbolLoader() const {
auto ret = SymbolLoader(mSymbols.fGetProcAddress);
ret.mLib = mGLLibrary;
return Some(ret);
}
// -
/* static */
RefPtr<GLLibraryEGL> GLLibraryEGL::Get(nsACString* const out_failureId) {
StaticMutexAutoLock lock(sMutex);
if (!sInstance) {
sInstance = new GLLibraryEGL;
if (NS_WARN_IF(!sInstance->Init(out_failureId))) {
sInstance = nullptr;
}
}
return sInstance;
}
/* static */ void GLLibraryEGL::Shutdown() {
StaticMutexAutoLock lock(sMutex);
sInstance = nullptr;
}
bool GLLibraryEGL::Init(nsACString* const out_failureId) {
MOZ_RELEASE_ASSERT(!mSymbols.fTerminate);
mozilla::ScopedGfxFeatureReporter reporter("EGL");
#ifdef XP_WIN
if (!mEGLLibrary) {
// On Windows, the GLESv2, EGL and DXSDK libraries are shipped with libxul
// and we should look for them there. We have to load the libs in this
// order, because libEGL.dll depends on libGLESv2.dll which depends on the
// DXSDK libraries. This matters especially for WebRT apps which are in a
// different directory. See bug 760323 and bug 749459
// Also note that we intentionally leak the libs we load.
do {
// Windows 8.1+ has d3dcompiler_47.dll in the system directory.
if (LoadLibrarySystem32(L"d3dcompiler_47.dll")) break;
MOZ_ASSERT(false, "d3dcompiler DLL loading failed.");
} while (false);
mGLLibrary = LoadLibraryForEGLOnWindows(u"libGLESv2.dll"_ns);
mEGLLibrary = LoadLibraryForEGLOnWindows(u"libEGL.dll"_ns);
}
#else // !Windows
// On non-Windows (Android) we use system copies of libEGL. We look for
// the APITrace lib, libEGL.so, and libEGL.so.1 in that order.
# if defined(ANDROID)
if (!mEGLLibrary) mEGLLibrary = LoadApitraceLibrary();
# endif
if (!mEGLLibrary) {
mEGLLibrary = PR_LoadLibrary("libEGL.so");
}
# if defined(XP_UNIX)
if (!mEGLLibrary) {
mEGLLibrary = PR_LoadLibrary("libEGL.so.1");
}
# endif
# ifdef APITRACE_LIB
if (!mGLLibrary) {
mGLLibrary = PR_LoadLibrary(APITRACE_LIB);
}
# endif
# ifdef GL_LIB
if (!mGLLibrary) {
mGLLibrary = PR_LoadLibrary(GL_LIB);
}
# endif
# ifdef GL_LIB2
if (!mGLLibrary) {
mGLLibrary = PR_LoadLibrary(GL_LIB2);
}
# endif
if (!mGLLibrary) {
mGLLibrary = PR_LoadLibrary(GLES2_LIB);
}
# ifdef GLES2_LIB2
if (!mGLLibrary) {
mGLLibrary = PR_LoadLibrary(GLES2_LIB2);
}
# endif
#endif // !Windows
if (!mEGLLibrary || !mGLLibrary) {
NS_WARNING("Couldn't load EGL LIB.");
*out_failureId = "FEATURE_FAILURE_EGL_LOAD_3"_ns;
return false;
}
#define SYMBOL(X) \
{ \
(PRFuncPtr*)&mSymbols.f##X, { \
{ "egl" #X } \
} \
}
#define END_OF_SYMBOLS \
{ \
nullptr, {} \
}
SymLoadStruct earlySymbols[] = {SYMBOL(GetDisplay),
SYMBOL(Terminate),
SYMBOL(GetCurrentSurface),
SYMBOL(GetCurrentContext),
SYMBOL(MakeCurrent),
SYMBOL(DestroyContext),
SYMBOL(CreateContext),
SYMBOL(DestroySurface),
SYMBOL(CreateWindowSurface),
SYMBOL(CreatePbufferSurface),
SYMBOL(CreatePbufferFromClientBuffer),
SYMBOL(CreatePixmapSurface),
SYMBOL(BindAPI),
SYMBOL(Initialize),
SYMBOL(ChooseConfig),
SYMBOL(GetError),
SYMBOL(GetConfigs),
SYMBOL(GetConfigAttrib),
SYMBOL(WaitNative),
SYMBOL(GetProcAddress),
SYMBOL(SwapBuffers),
SYMBOL(CopyBuffers),
SYMBOL(QueryString),
SYMBOL(QueryContext),
SYMBOL(BindTexImage),
SYMBOL(ReleaseTexImage),
SYMBOL(SwapInterval),
SYMBOL(QuerySurface),
END_OF_SYMBOLS};
{
const SymbolLoader libLoader(*mEGLLibrary);
if (!libLoader.LoadSymbols(earlySymbols)) {
NS_WARNING(
"Couldn't find required entry points in EGL library (early init)");
*out_failureId = "FEATURE_FAILURE_EGL_SYM"_ns;
return false;
}
}
{
const char internalFuncName[] =
"_Z35eglQueryStringImplementationANDROIDPvi";
const auto& internalFunc =
PR_FindFunctionSymbol(mEGLLibrary, internalFuncName);
if (internalFunc) {
*(PRFuncPtr*)&mSymbols.fQueryString = internalFunc;
}
}
// -
InitLibExtensions();
const SymbolLoader pfnLoader(mSymbols.fGetProcAddress);
const auto fnLoadSymbols = [&](const SymLoadStruct* symbols) {
const bool shouldWarn = gfxEnv::MOZ_GL_SPEW();
if (pfnLoader.LoadSymbols(symbols, shouldWarn)) return true;
ClearSymbols(symbols);
return false;
};
// Check the ANGLE support the system has
mIsANGLE = IsExtensionSupported(EGLLibExtension::ANGLE_platform_angle);
// Client exts are ready. (But not display exts!)
if (mIsANGLE) {
MOZ_ASSERT(IsExtensionSupported(EGLLibExtension::ANGLE_platform_angle_d3d));
const SymLoadStruct angleSymbols[] = {SYMBOL(GetPlatformDisplay),
END_OF_SYMBOLS};
if (!fnLoadSymbols(angleSymbols)) {
gfxCriticalError() << "Failed to load ANGLE symbols!";
return false;
}
MOZ_ASSERT(IsExtensionSupported(EGLLibExtension::ANGLE_platform_angle_d3d));
const SymLoadStruct createDeviceSymbols[] = {
SYMBOL(CreateDeviceANGLE), SYMBOL(ReleaseDeviceANGLE), END_OF_SYMBOLS};
if (!fnLoadSymbols(createDeviceSymbols)) {
NS_ERROR(
"EGL supports ANGLE_device_creation without exposing its functions!");
MarkExtensionUnsupported(EGLLibExtension::ANGLE_device_creation);
}
}
// ANDROID_get_native_client_buffer isn't necessarily enumerated in lib exts,
// but it is one.
{
const SymLoadStruct symbols[] = {SYMBOL(GetNativeClientBufferANDROID),
END_OF_SYMBOLS};
if (fnLoadSymbols(symbols)) {
mAvailableExtensions[UnderlyingValue(
EGLLibExtension::ANDROID_get_native_client_buffer)] = true;
}
}
// -
// Load possible display ext symbols.
{
const SymLoadStruct symbols[] = {SYMBOL(QuerySurfacePointerANGLE),
END_OF_SYMBOLS};
(void)fnLoadSymbols(symbols);
}
{
const SymLoadStruct symbols[] = {
SYMBOL(CreateSyncKHR), SYMBOL(DestroySyncKHR),
SYMBOL(ClientWaitSyncKHR), SYMBOL(GetSyncAttribKHR), END_OF_SYMBOLS};
(void)fnLoadSymbols(symbols);
}
{
const SymLoadStruct symbols[] = {SYMBOL(CreateImageKHR),
SYMBOL(DestroyImageKHR), END_OF_SYMBOLS};
(void)fnLoadSymbols(symbols);
}
{
const SymLoadStruct symbols[] = {SYMBOL(WaitSyncKHR), END_OF_SYMBOLS};
(void)fnLoadSymbols(symbols);
}
{
const SymLoadStruct symbols[] = {SYMBOL(DupNativeFenceFDANDROID),
END_OF_SYMBOLS};
(void)fnLoadSymbols(symbols);
}
{
const SymLoadStruct symbols[] = {SYMBOL(CreateStreamKHR),
SYMBOL(DestroyStreamKHR),
SYMBOL(QueryStreamKHR), END_OF_SYMBOLS};
(void)fnLoadSymbols(symbols);
}
{
const SymLoadStruct symbols[] = {SYMBOL(StreamConsumerGLTextureExternalKHR),
SYMBOL(StreamConsumerAcquireKHR),
SYMBOL(StreamConsumerReleaseKHR),
END_OF_SYMBOLS};
(void)fnLoadSymbols(symbols);
}
{
const SymLoadStruct symbols[] = {
SYMBOL(QueryDisplayAttribEXT), SYMBOL(QueryDeviceAttribEXT),
SYMBOL(QueryDeviceStringEXT), END_OF_SYMBOLS};
(void)fnLoadSymbols(symbols);
}
{
const SymLoadStruct symbols[] = {
SYMBOL(StreamConsumerGLTextureExternalAttribsNV), END_OF_SYMBOLS};
(void)fnLoadSymbols(symbols);
}
{
const SymLoadStruct symbols[] = {
SYMBOL(CreateStreamProducerD3DTextureANGLE),
SYMBOL(StreamPostD3DTextureANGLE), END_OF_SYMBOLS};
(void)fnLoadSymbols(symbols);
}
{
const SymLoadStruct symbols[] = {
{(PRFuncPtr*)&mSymbols.fSwapBuffersWithDamage,
{{"eglSwapBuffersWithDamageEXT"}}},
END_OF_SYMBOLS};
(void)fnLoadSymbols(symbols);
}
{
const SymLoadStruct symbols[] = {
{(PRFuncPtr*)&mSymbols.fSwapBuffersWithDamage,
{{"eglSwapBuffersWithDamageKHR"}}},
END_OF_SYMBOLS};
(void)fnLoadSymbols(symbols);
}
{
const SymLoadStruct symbols[] = {
{(PRFuncPtr*)&mSymbols.fSetDamageRegion, {{"eglSetDamageRegionKHR"}}},
END_OF_SYMBOLS};
(void)fnLoadSymbols(symbols);
}
{
const SymLoadStruct symbols[] = {SYMBOL(GetPlatformDisplay),
END_OF_SYMBOLS};
(void)fnLoadSymbols(symbols);
}
{
const SymLoadStruct symbols[] = {SYMBOL(ExportDMABUFImageQueryMESA),
SYMBOL(ExportDMABUFImageMESA),
END_OF_SYMBOLS};
(void)fnLoadSymbols(symbols);
}
{
const SymLoadStruct symbols[] = {SYMBOL(QueryDevicesEXT), END_OF_SYMBOLS};
(void)fnLoadSymbols(symbols);
}
return true;
}
// -
template <size_t N>
static void MarkExtensions(const char* rawExtString, bool shouldDumpExts,
const char* extType, const char* const (&names)[N],
std::bitset<N>* const out) {
MOZ_ASSERT(rawExtString);
const nsDependentCString extString(rawExtString);
std::vector<nsCString> extList;
SplitByChar(extString, ' ', &extList);
if (shouldDumpExts) {
printf_stderr("%u EGL %s extensions: (*: recognized)\n",
(uint32_t)extList.size(), extType);
}
MarkBitfieldByStrings(extList, shouldDumpExts, names, out);
}
// -
// static
std::shared_ptr<EglDisplay> EglDisplay::Create(
GLLibraryEGL& lib, const EGLDisplay display, const bool isWarp,
const StaticMutexAutoLock& aProofOfLock) {
// Retrieve the EglDisplay if it already exists
{
const auto itr = lib.mActiveDisplays.find(display);
if (itr != lib.mActiveDisplays.end()) {
const auto ret = itr->second.lock();
if (ret) {
return ret;
}
}
}
if (!lib.fInitialize(display, nullptr, nullptr)) {
return nullptr;
}
static std::once_flag sMesaLeakFlag;
std::call_once(sMesaLeakFlag, MesaMemoryLeakWorkaround);
const auto ret =
std::make_shared<EglDisplay>(PrivateUseOnly{}, lib, display, isWarp);
lib.mActiveDisplays.insert({display, ret});
return ret;
}
EglDisplay::EglDisplay(const PrivateUseOnly&, GLLibraryEGL& lib,
const EGLDisplay disp, const bool isWarp)
: mLib(&lib), mDisplay(disp), mIsWARP(isWarp) {
const bool shouldDumpExts = GLContext::ShouldDumpExts();
auto rawExtString =
(const char*)mLib->fQueryString(mDisplay, LOCAL_EGL_EXTENSIONS);
if (!rawExtString) {
NS_WARNING("Failed to query EGL display extensions!.");
rawExtString = "";
}
MarkExtensions(rawExtString, shouldDumpExts, "display", sEGLExtensionNames,
&mAvailableExtensions);
// -
if (!HasKHRImageBase()) {
MarkExtensionUnsupported(EGLExtension::KHR_image_pixmap);
}
if (IsExtensionSupported(EGLExtension::KHR_surfaceless_context)) {
const auto vendor =
(const char*)mLib->fQueryString(mDisplay, LOCAL_EGL_VENDOR);
// Bug 1464610: Mali T720 (Amazon Fire 8 HD) claims to support this
// extension, but if you actually eglMakeCurrent() with EGL_NO_SURFACE, it
// fails to render anything when a real surface is provided later on. We
// only have the EGL vendor available here, so just avoid using this
// extension on all Mali devices.
if (vendor && (strcmp(vendor, "ARM") == 0)) {
MarkExtensionUnsupported(EGLExtension::KHR_surfaceless_context);
}
}
// ANDROID_native_fence_sync isn't necessarily enumerated in display ext,
// but it is one.
if (mLib->mSymbols.fDupNativeFenceFDANDROID) {
mAvailableExtensions[UnderlyingValue(
EGLExtension::ANDROID_native_fence_sync)] = true;
}
}
EglDisplay::~EglDisplay() {
StaticMutexAutoLock lock(GLLibraryEGL::sMutex);
fTerminate();
mLib->mActiveDisplays.erase(mDisplay);
}
// -
std::shared_ptr<EglDisplay> GLLibraryEGL::DefaultDisplay(
nsACString* const out_failureId) {
StaticMutexAutoLock lock(sMutex);
auto ret = mDefaultDisplay.lock();
if (ret) return ret;
ret = CreateDisplayLocked(false, out_failureId, lock);
mDefaultDisplay = ret;
return ret;
}
std::shared_ptr<EglDisplay> GLLibraryEGL::CreateDisplay(
const bool forceAccel, nsACString* const out_failureId) {
StaticMutexAutoLock lock(sMutex);
return CreateDisplayLocked(forceAccel, out_failureId, lock);
}
std::shared_ptr<EglDisplay> GLLibraryEGL::CreateDisplayLocked(
const bool forceAccel, nsACString* const out_failureId,
const StaticMutexAutoLock& aProofOfLock) {
std::shared_ptr<EglDisplay> ret;
if (IsExtensionSupported(EGLLibExtension::ANGLE_platform_angle_d3d)) {
nsCString accelAngleFailureId;
bool accelAngleSupport = IsAccelAngleSupported(&accelAngleFailureId);
bool shouldTryAccel = forceAccel || accelAngleSupport;
bool shouldTryWARP = !forceAccel; // Only if ANGLE not supported or fails
// If WARP preferred, will override ANGLE support
if (StaticPrefs::webgl_angle_force_warp()) {
shouldTryWARP = true;
shouldTryAccel = false;
if (accelAngleFailureId.IsEmpty()) {
accelAngleFailureId = "FEATURE_FAILURE_FORCE_WARP"_ns;
}
}
// Hardware accelerated ANGLE path (supported or force accel)
if (shouldTryAccel) {
ret = GetAndInitDisplayForAccelANGLE(*this, out_failureId, aProofOfLock);
}
// Report the acceleration status to telemetry
if (!ret) {
if (accelAngleFailureId.IsEmpty()) {
Telemetry::Accumulate(Telemetry::CANVAS_WEBGL_ACCL_FAILURE_ID,
"FEATURE_FAILURE_ACCL_ANGLE_UNKNOWN"_ns);
} else {
Telemetry::Accumulate(Telemetry::CANVAS_WEBGL_ACCL_FAILURE_ID,
accelAngleFailureId);
}
} else {
Telemetry::Accumulate(Telemetry::CANVAS_WEBGL_ACCL_FAILURE_ID,
"SUCCESS"_ns);
}
// Fallback to a WARP display if ANGLE fails, or if WARP is forced
if (!ret && shouldTryWARP) {
ret = GetAndInitWARPDisplay(*this, EGL_DEFAULT_DISPLAY, aProofOfLock);
if (!ret) {
if (out_failureId->IsEmpty()) {
*out_failureId = "FEATURE_FAILURE_WARP_FALLBACK"_ns;
}
NS_ERROR("Fallback WARP context failed to initialize.");
return nullptr;
}
}
} else {
void* nativeDisplay = EGL_DEFAULT_DISPLAY;
#ifdef MOZ_WIDGET_GTK
if (!ret && !gfx::gfxVars::WebglUseHardware()) {
// Initialize a swrast egl device such as llvmpipe
ret = GetAndInitSoftwareDisplay(*this, aProofOfLock);
}
// Initialize the display the normal way
if (!ret && !gdk_display_get_default()) {
ret = GetAndInitDeviceDisplay(*this, aProofOfLock);
if (!ret) {
ret = GetAndInitSurfacelessDisplay(*this, aProofOfLock);
}
}
# ifdef MOZ_WAYLAND
else if (!ret && widget::GdkIsWaylandDisplay()) {
// Wayland does not support EGL_DEFAULT_DISPLAY
nativeDisplay = widget::WaylandDisplayGetWLDisplay();
if (!nativeDisplay) {
NS_WARNING("Failed to get wl_display.");
return nullptr;
}
}
# endif
#endif
if (!ret) {
ret = GetAndInitDisplay(*this, nativeDisplay, aProofOfLock);
}
}
if (!ret) {
if (out_failureId->IsEmpty()) {
*out_failureId = "FEATURE_FAILURE_NO_DISPLAY"_ns;
}
NS_WARNING("Failed to initialize a display.");
return nullptr;
}
return ret;
}
void GLLibraryEGL::InitLibExtensions() {
const bool shouldDumpExts = GLContext::ShouldDumpExts();
const char* rawExtString = nullptr;
#ifndef ANDROID
// Bug 1209612: Crashes on a number of android drivers.
// Ideally we would only blocklist this there, but for now we don't need the
// client extension list on ANDROID (we mostly need it on ANGLE), and we'd
// rather not crash.
rawExtString = (const char*)fQueryString(nullptr, LOCAL_EGL_EXTENSIONS);
#endif
if (!rawExtString) {
if (shouldDumpExts) {
printf_stderr("No EGL lib extensions.\n");
}
return;
}
MarkExtensions(rawExtString, shouldDumpExts, "lib", sEGLLibraryExtensionNames,
&mAvailableExtensions);
}
void EglDisplay::DumpEGLConfig(EGLConfig cfg) const {
#define ATTR(_x) \
do { \
int attrval = 0; \
mLib->fGetConfigAttrib(mDisplay, cfg, LOCAL_EGL_##_x, &attrval); \
const auto err = mLib->fGetError(); \
if (err != 0x3000) { \
printf_stderr(" %s: ERROR (0x%04x)\n", #_x, err); \
} else { \
printf_stderr(" %s: %d (0x%04x)\n", #_x, attrval, attrval); \
} \
} while (0)
printf_stderr("EGL Config: %d [%p]\n", (int)(intptr_t)cfg, cfg);
ATTR(BUFFER_SIZE);
ATTR(ALPHA_SIZE);
ATTR(BLUE_SIZE);
ATTR(GREEN_SIZE);
ATTR(RED_SIZE);
ATTR(DEPTH_SIZE);
ATTR(STENCIL_SIZE);
ATTR(CONFIG_CAVEAT);
ATTR(CONFIG_ID);
ATTR(LEVEL);
ATTR(MAX_PBUFFER_HEIGHT);
ATTR(MAX_PBUFFER_PIXELS);
ATTR(MAX_PBUFFER_WIDTH);
ATTR(NATIVE_RENDERABLE);
ATTR(NATIVE_VISUAL_ID);
ATTR(NATIVE_VISUAL_TYPE);
ATTR(PRESERVED_RESOURCES);
ATTR(SAMPLES);
ATTR(SAMPLE_BUFFERS);
ATTR(SURFACE_TYPE);
ATTR(TRANSPARENT_TYPE);
ATTR(TRANSPARENT_RED_VALUE);
ATTR(TRANSPARENT_GREEN_VALUE);
ATTR(TRANSPARENT_BLUE_VALUE);
ATTR(BIND_TO_TEXTURE_RGB);
ATTR(BIND_TO_TEXTURE_RGBA);
ATTR(MIN_SWAP_INTERVAL);
ATTR(MAX_SWAP_INTERVAL);
ATTR(LUMINANCE_SIZE);
ATTR(ALPHA_MASK_SIZE);
ATTR(COLOR_BUFFER_TYPE);
ATTR(RENDERABLE_TYPE);
ATTR(CONFORMANT);
#undef ATTR
}
void EglDisplay::DumpEGLConfigs() const {
int nc = 0;
mLib->fGetConfigs(mDisplay, nullptr, 0, &nc);
std::vector<EGLConfig> ec(nc);
mLib->fGetConfigs(mDisplay, ec.data(), ec.size(), &nc);
for (int i = 0; i < nc; ++i) {
printf_stderr("========= EGL Config %d ========\n", i);
DumpEGLConfig(ec[i]);
}
}
static bool ShouldTrace() {
static bool ret = gfxEnv::MOZ_GL_DEBUG_VERBOSE();
return ret;
}
void BeforeEGLCall(const char* glFunction) {
if (ShouldTrace()) {
printf_stderr("[egl] > %s\n", glFunction);
}
}
void AfterEGLCall(const char* glFunction) {
if (ShouldTrace()) {
printf_stderr("[egl] < %s\n", glFunction);
}
}
} /* namespace gl */
} /* namespace mozilla */