/* 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 "gfxUtils.h" #include "mozilla/Preferences.h" #include "mozilla/Assertions.h" #include "mozilla/Telemetry.h" #include "mozilla/Tokenizer.h" #include "mozilla/ScopeExit.h" #include "mozilla/Unused.h" #include "mozilla/webrender/RenderThread.h" #include "nsDirectoryServiceDefs.h" #include "nsDirectoryServiceUtils.h" #include "nsIGfxInfo.h" #include "nsPrintfCString.h" #ifdef XP_WIN # include "mozilla/gfx/DeviceManagerDx.h" # include "nsWindowsHelpers.h" # include #endif #include "OGLShaderProgram.h" #include "prenv.h" #include "prsystem.h" #include "GLContext.h" #include "GLContextProvider.h" #include "gfxPrefs.h" #include "ScopedGLHelpers.h" #ifdef MOZ_WIDGET_GTK # include # ifdef MOZ_WAYLAND # include # include # endif // MOZ_WIDGET_GTK #endif // MOZ_WAYLAND namespace mozilla { namespace gl { StaticMutex GLLibraryEGL::sMutex; StaticRefPtr GLLibraryEGL::sEGLLibrary; // 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_KHR_lock_surface", "EGL_ANGLE_surface_d3d_texture_2d_share_handle", "EGL_EXT_create_context_robustness", "EGL_KHR_image", "EGL_KHR_fence_sync", "EGL_ANDROID_native_fence_sync", "EGL_ANDROID_image_crop", "EGL_ANGLE_platform_angle", "EGL_ANGLE_platform_angle_d3d", "EGL_ANGLE_d3d_share_handle_client_buffer", "EGL_KHR_create_context", "EGL_KHR_stream", "EGL_KHR_stream_consumer_gltexture", "EGL_EXT_device_query", "EGL_NV_stream_consumer_gltexture_yuv", "EGL_ANGLE_stream_producer_d3d_texture", "EGL_ANGLE_device_creation", "EGL_ANGLE_device_creation_d3d11", "EGL_KHR_surfaceless_context", "EGL_KHR_create_context_no_error", "EGL_MOZ_create_context_provoking_vertex_dont_care"}; 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... gfxPrefs::GetSingleton(); if (!gfxPrefs::UseApitrace()) { 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(); return PR_LoadLibraryWithFlags(lspec, PR_LD_LAZY | PR_LD_LOCAL); } #endif // XP_WIN static EGLDisplay GetAndInitWARPDisplay(GLLibraryEGL& egl, void* displayType) { EGLint attrib_list[] = {LOCAL_EGL_PLATFORM_ANGLE_DEVICE_TYPE_ANGLE, LOCAL_EGL_PLATFORM_ANGLE_DEVICE_TYPE_WARP_ANGLE, // Requires: LOCAL_EGL_PLATFORM_ANGLE_TYPE_ANGLE, LOCAL_EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE, LOCAL_EGL_NONE}; EGLDisplay display = egl.fGetPlatformDisplayEXT( 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 EGL_NO_DISPLAY; } if (!egl.fInitialize(display, nullptr, nullptr)) return EGL_NO_DISPLAY; return display; } static EGLDisplay GetAndInitDisplayForWebRender(GLLibraryEGL& egl, void* displayType) { #ifdef XP_WIN const EGLint attrib_list[] = {LOCAL_EGL_EXPERIMENTAL_PRESENT_PATH_ANGLE, LOCAL_EGL_EXPERIMENTAL_PRESENT_PATH_FAST_ANGLE, LOCAL_EGL_NONE}; RefPtr d3d11Device = gfx::DeviceManagerDx::Get()->GetCompositorDevice(); if (!d3d11Device) { gfxCriticalNote << "Failed to get compositor device for EGLDisplay"; return EGL_NO_DISPLAY; } EGLDeviceEXT eglDevice = egl.fCreateDeviceANGLE( LOCAL_EGL_D3D11_DEVICE_ANGLE, reinterpret_cast(d3d11Device.get()), nullptr); if (!eglDevice) { gfxCriticalNote << "Failed to get EGLDeviceEXT of D3D11Device"; return EGL_NO_DISPLAY; } // Create an EGLDisplay using the EGLDevice EGLDisplay display = egl.fGetPlatformDisplayEXT(LOCAL_EGL_PLATFORM_DEVICE_EXT, eglDevice, attrib_list); if (!display) { gfxCriticalNote << "Failed to get EGLDisplay of D3D11Device"; return EGL_NO_DISPLAY; } 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 EGL_NO_DISPLAY; } if (!egl.fInitialize(display, nullptr, nullptr)) { const EGLint err = egl.fGetError(); if (err != LOCAL_EGL_SUCCESS) { gfxCriticalError() << "Failed to initialize EGLDisplay for WebRender error: " << gfx::hexa(err); } return EGL_NO_DISPLAY; } return display; #else return EGL_NO_DISPLAY; #endif } static bool IsAccelAngleSupported(const nsCOMPtr& gfxInfo, nsACString* const out_failureId) { if (wr::RenderThread::IsInRenderThread()) { // We can only enter here with WebRender, so assert that this is a // WebRender-enabled build. #ifndef MOZ_BUILD_WEBRENDER MOZ_ASSERT(false); #endif return true; } int32_t angleSupport; nsCString failureId; gfxUtils::ThreadSafeGetFeatureStatus(gfxInfo, nsIGfxInfo::FEATURE_WEBGL_ANGLE, failureId, &angleSupport); if (failureId.IsEmpty() && angleSupport != nsIGfxInfo::FEATURE_STATUS_OK) { // This shouldn't happen, if we see this it's because we've missed // some failure paths failureId = NS_LITERAL_CSTRING("FEATURE_FAILURE_ACCL_ANGLE_NOT_OK"); } if (out_failureId->IsEmpty()) { *out_failureId = failureId; } return (angleSupport == nsIGfxInfo::FEATURE_STATUS_OK); } static EGLDisplay GetAndInitDisplay(GLLibraryEGL& egl, void* displayType) { EGLDisplay display = egl.fGetDisplay(displayType); if (display == EGL_NO_DISPLAY) return EGL_NO_DISPLAY; if (!egl.fInitialize(display, nullptr, nullptr)) return EGL_NO_DISPLAY; return display; } 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 EGLDisplay GetAndInitDisplayForAccelANGLE( GLLibraryEGL& egl, nsACString* const out_failureId) { EGLDisplay ret = 0; if (wr::RenderThread::IsInRenderThread()) { return GetAndInitDisplayForWebRender(egl, EGL_DEFAULT_DISPLAY); } FeatureState& d3d11ANGLE = gfxConfig::GetFeature(Feature::D3D11_HW_ANGLE); if (!gfxPrefs::WebGLANGLETryD3D11()) d3d11ANGLE.UserDisable("User disabled D3D11 ANGLE by pref", NS_LITERAL_CSTRING("FAILURE_ID_ANGLE_PREF")); if (gfxPrefs::WebGLANGLEForceD3D11()) 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 (gfxConfig::IsForcedOnByUser(Feature::D3D11_HW_ANGLE)) { return GetAndInitDisplay(egl, LOCAL_EGL_D3D11_ONLY_DISPLAY_ANGLE); } if (d3d11ANGLE.IsEnabled()) { ret = GetAndInitDisplay(egl, LOCAL_EGL_D3D11_ELSE_D3D9_DISPLAY_ANGLE); } if (!ret) { ret = GetAndInitDisplay(egl, EGL_DEFAULT_DISPLAY); } if (!ret && out_failureId->IsEmpty()) { *out_failureId = NS_LITERAL_CSTRING("FEATURE_FAILURE_ACCL_ANGLE_NO_DISP"); } return ret; } bool GLLibraryEGL::ReadbackEGLImage(EGLImage image, gfx::DataSourceSurface* out_surface) { StaticMutexAutoUnlock lock(sMutex); if (!mReadbackGL) { nsCString discardFailureId; mReadbackGL = gl::GLContextProvider::CreateHeadless( gl::CreateContextFlags::NONE, &discardFailureId); } ScopedTexture destTex(mReadbackGL); const GLuint target = mReadbackGL->GetPreferredEGLImageTextureTarget(); ScopedBindTexture autoTex(mReadbackGL, destTex.Texture(), target); mReadbackGL->fTexParameteri(target, LOCAL_GL_TEXTURE_WRAP_S, LOCAL_GL_CLAMP_TO_EDGE); mReadbackGL->fTexParameteri(target, LOCAL_GL_TEXTURE_WRAP_T, LOCAL_GL_CLAMP_TO_EDGE); mReadbackGL->fTexParameteri(target, LOCAL_GL_TEXTURE_MAG_FILTER, LOCAL_GL_NEAREST); mReadbackGL->fTexParameteri(target, LOCAL_GL_TEXTURE_MIN_FILTER, LOCAL_GL_NEAREST); mReadbackGL->fEGLImageTargetTexture2D(target, image); ShaderConfigOGL config = ShaderConfigFromTargetAndFormat(target, out_surface->GetFormat()); int shaderConfig = config.mFeatures; mReadbackGL->ReadTexImageHelper()->ReadTexImage( out_surface, 0, target, out_surface->GetSize(), shaderConfig); return true; } // - #if defined(XP_UNIX) # define GLES2_LIB "libGLESv2.so" # define GLES2_LIB2 "libGLESv2.so.2" #elif defined(XP_WIN) # define GLES2_LIB "libGLESv2.dll" #else # error "Platform not recognized" #endif Maybe GLLibraryEGL::GetSymbolLoader() const { auto ret = SymbolLoader(mSymbols.fGetProcAddress); ret.mLib = mGLLibrary; return Some(ret); } // - /* static */ bool GLLibraryEGL::EnsureInitialized( bool forceAccel, nsACString* const out_failureId) { if (!sEGLLibrary) { sEGLLibrary = new GLLibraryEGL(); } return sEGLLibrary->DoEnsureInitialized(forceAccel, out_failureId); } bool GLLibraryEGL::DoEnsureInitialized() { nsCString failureId; return DoEnsureInitialized(false, &failureId); } bool GLLibraryEGL::DoEnsureInitialized(bool forceAccel, nsACString* const out_failureId) { if (mInitialized && !mSymbols.fTerminate) { *out_failureId = NS_LITERAL_CSTRING("FEATURE_FAILURE_EGL_DESTROYED"); MOZ_ASSERT(false); return false; } if (mInitialized) { return true; } 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. // Try it first. Note that _46 will never be in the system // directory. So there is no point trying _46 in the system // directory. if (LoadLibrarySystem32(L"d3dcompiler_47.dll")) break; # ifdef MOZ_D3DCOMPILER_VISTA_DLL if (LoadLibraryForEGLOnWindows( NS_LITERAL_STRING(NS_STRINGIFY(MOZ_D3DCOMPILER_VISTA_DLL)))) break; # endif MOZ_ASSERT(false, "d3dcompiler DLL loading failed."); } while (false); mGLLibrary = LoadLibraryForEGLOnWindows(NS_LITERAL_STRING("libGLESv2.dll")); mEGLLibrary = LoadLibraryForEGLOnWindows(NS_LITERAL_STRING("libEGL.dll")); } #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) { printf_stderr("Attempting load of libEGL.so\n"); 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 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 = NS_LITERAL_CSTRING("FEATURE_FAILURE_EGL_LOAD_3"); 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 = NS_LITERAL_CSTRING("FEATURE_FAILURE_EGL_SYM"); return false; } } { const char internalFuncName[] = "_Z35eglQueryStringImplementationANDROIDPvi"; const auto& internalFunc = PR_FindFunctionSymbol(mEGLLibrary, internalFuncName); if (internalFunc) { *(PRFuncPtr*)&mSymbols.fQueryString = internalFunc; } } const SymbolLoader pfnLoader(mSymbols.fGetProcAddress); InitClientExtensions(); const auto fnLoadSymbols = [&](const SymLoadStruct* symbols) { if (pfnLoader.LoadSymbols(symbols)) return true; ClearSymbols(symbols); return false; }; // Check the ANGLE support the system has nsCOMPtr gfxInfo = do_GetService("@mozilla.org/gfx/info;1"); mIsANGLE = IsExtensionSupported(ANGLE_platform_angle); // Client exts are ready. (But not display exts!) if (mIsANGLE) { MOZ_ASSERT(IsExtensionSupported(ANGLE_platform_angle_d3d)); const SymLoadStruct angleSymbols[] = {SYMBOL(GetPlatformDisplayEXT), END_OF_SYMBOLS}; if (!fnLoadSymbols(angleSymbols)) { gfxCriticalError() << "Failed to load ANGLE symbols!"; return false; } MOZ_ASSERT(IsExtensionSupported(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(ANGLE_device_creation); } } mEGLDisplay = CreateDisplay(forceAccel, gfxInfo, out_failureId); if (!mEGLDisplay) { return false; } InitDisplayExtensions(); //////////////////////////////////// // Alright, load display exts. if (IsExtensionSupported(KHR_lock_surface)) { const SymLoadStruct lockSymbols[] = { SYMBOL(LockSurfaceKHR), SYMBOL(UnlockSurfaceKHR), END_OF_SYMBOLS}; if (!fnLoadSymbols(lockSymbols)) { NS_ERROR("EGL supports KHR_lock_surface without exposing its functions!"); MarkExtensionUnsupported(KHR_lock_surface); } } if (IsExtensionSupported(ANGLE_surface_d3d_texture_2d_share_handle)) { const SymLoadStruct d3dSymbols[] = {SYMBOL(QuerySurfacePointerANGLE), END_OF_SYMBOLS}; if (!fnLoadSymbols(d3dSymbols)) { NS_ERROR( "EGL supports ANGLE_surface_d3d_texture_2d_share_handle without " "exposing its functions!"); MarkExtensionUnsupported(ANGLE_surface_d3d_texture_2d_share_handle); } } if (IsExtensionSupported(KHR_fence_sync)) { const SymLoadStruct syncSymbols[] = { SYMBOL(CreateSyncKHR), SYMBOL(DestroySyncKHR), SYMBOL(ClientWaitSyncKHR), SYMBOL(GetSyncAttribKHR), END_OF_SYMBOLS}; if (!fnLoadSymbols(syncSymbols)) { NS_ERROR("EGL supports KHR_fence_sync without exposing its functions!"); MarkExtensionUnsupported(KHR_fence_sync); } } if (IsExtensionSupported(KHR_image) || IsExtensionSupported(KHR_image_base)) { const SymLoadStruct imageSymbols[] = { SYMBOL(CreateImageKHR), SYMBOL(DestroyImageKHR), END_OF_SYMBOLS}; if (!fnLoadSymbols(imageSymbols)) { NS_ERROR("EGL supports KHR_image(_base) without exposing its functions!"); MarkExtensionUnsupported(KHR_image); MarkExtensionUnsupported(KHR_image_base); MarkExtensionUnsupported(KHR_image_pixmap); } } else { MarkExtensionUnsupported(KHR_image_pixmap); } if (IsExtensionSupported(ANDROID_native_fence_sync)) { const SymLoadStruct nativeFenceSymbols[] = {SYMBOL(DupNativeFenceFDANDROID), END_OF_SYMBOLS}; if (!fnLoadSymbols(nativeFenceSymbols)) { NS_ERROR( "EGL supports ANDROID_native_fence_sync without exposing its " "functions!"); MarkExtensionUnsupported(ANDROID_native_fence_sync); } } if (IsExtensionSupported(KHR_stream)) { const SymLoadStruct streamSymbols[] = { SYMBOL(CreateStreamKHR), SYMBOL(DestroyStreamKHR), SYMBOL(QueryStreamKHR), END_OF_SYMBOLS}; if (!fnLoadSymbols(streamSymbols)) { NS_ERROR("EGL supports KHR_stream without exposing its functions!"); MarkExtensionUnsupported(KHR_stream); } } if (IsExtensionSupported(KHR_stream_consumer_gltexture)) { const SymLoadStruct streamConsumerSymbols[] = { SYMBOL(StreamConsumerGLTextureExternalKHR), SYMBOL(StreamConsumerAcquireKHR), SYMBOL(StreamConsumerReleaseKHR), END_OF_SYMBOLS}; if (!fnLoadSymbols(streamConsumerSymbols)) { NS_ERROR( "EGL supports KHR_stream_consumer_gltexture without exposing its " "functions!"); MarkExtensionUnsupported(KHR_stream_consumer_gltexture); } } if (IsExtensionSupported(EXT_device_query)) { const SymLoadStruct queryDisplaySymbols[] = {SYMBOL(QueryDisplayAttribEXT), SYMBOL(QueryDeviceAttribEXT), END_OF_SYMBOLS}; if (!fnLoadSymbols(queryDisplaySymbols)) { NS_ERROR("EGL supports EXT_device_query without exposing its functions!"); MarkExtensionUnsupported(EXT_device_query); } } if (IsExtensionSupported(NV_stream_consumer_gltexture_yuv)) { const SymLoadStruct nvStreamSymbols[] = { SYMBOL(StreamConsumerGLTextureExternalAttribsNV), END_OF_SYMBOLS}; if (!fnLoadSymbols(nvStreamSymbols)) { NS_ERROR( "EGL supports NV_stream_consumer_gltexture_yuv without exposing its " "functions!"); MarkExtensionUnsupported(NV_stream_consumer_gltexture_yuv); } } if (IsExtensionSupported(ANGLE_stream_producer_d3d_texture)) { const SymLoadStruct nvStreamSymbols[] = { SYMBOL(CreateStreamProducerD3DTextureANGLE), SYMBOL(StreamPostD3DTextureANGLE), END_OF_SYMBOLS}; if (!fnLoadSymbols(nvStreamSymbols)) { NS_ERROR( "EGL supports ANGLE_stream_producer_d3d_texture without exposing its " "functions!"); MarkExtensionUnsupported(ANGLE_stream_producer_d3d_texture); } } if (IsExtensionSupported(KHR_surfaceless_context)) { const auto vendor = fQueryString(mEGLDisplay, 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 (strcmp((const char*)vendor, "ARM") == 0) { MarkExtensionUnsupported(KHR_surfaceless_context); } } mInitialized = true; reporter.SetSuccessful(); return true; } #undef SYMBOL #undef END_OF_SYMBOLS void GLLibraryEGL::Shutdown() { if (this != sEGLLibrary) { return; } if (mEGLDisplay) { fTerminate(mEGLDisplay); mEGLDisplay = EGL_NO_DISPLAY; } mSymbols = {}; sEGLLibrary = nullptr; } EGLDisplay GLLibraryEGL::CreateDisplay(bool forceAccel, const nsCOMPtr& gfxInfo, nsACString* const out_failureId) { MOZ_ASSERT(!mInitialized); EGLDisplay chosenDisplay = nullptr; if (IsExtensionSupported(ANGLE_platform_angle_d3d)) { nsCString accelAngleFailureId; bool accelAngleSupport = IsAccelAngleSupported(gfxInfo, &accelAngleFailureId); bool shouldTryAccel = forceAccel || accelAngleSupport; bool shouldTryWARP = !forceAccel; // Only if ANGLE not supported or fails // If WARP preferred, will override ANGLE support if (gfxPrefs::WebGLANGLEForceWARP()) { shouldTryWARP = true; shouldTryAccel = false; if (accelAngleFailureId.IsEmpty()) { accelAngleFailureId = NS_LITERAL_CSTRING("FEATURE_FAILURE_FORCE_WARP"); } } // Hardware accelerated ANGLE path (supported or force accel) if (shouldTryAccel) { chosenDisplay = GetAndInitDisplayForAccelANGLE(*this, out_failureId); } // Report the acceleration status to telemetry if (!chosenDisplay) { if (accelAngleFailureId.IsEmpty()) { Telemetry::Accumulate( Telemetry::CANVAS_WEBGL_ACCL_FAILURE_ID, NS_LITERAL_CSTRING("FEATURE_FAILURE_ACCL_ANGLE_UNKNOWN")); } else { Telemetry::Accumulate(Telemetry::CANVAS_WEBGL_ACCL_FAILURE_ID, accelAngleFailureId); } } else { Telemetry::Accumulate(Telemetry::CANVAS_WEBGL_ACCL_FAILURE_ID, NS_LITERAL_CSTRING("SUCCESS")); } // Fallback to a WARP display if ANGLE fails, or if WARP is forced if (!chosenDisplay && shouldTryWARP) { chosenDisplay = GetAndInitWARPDisplay(*this, EGL_DEFAULT_DISPLAY); if (!chosenDisplay) { if (out_failureId->IsEmpty()) { *out_failureId = NS_LITERAL_CSTRING("FEATURE_FAILURE_WARP_FALLBACK"); } NS_ERROR("Fallback WARP context failed to initialize."); return nullptr; } mIsWARP = true; } } else { void* nativeDisplay = EGL_DEFAULT_DISPLAY; #ifdef MOZ_WAYLAND // Some drivers doesn't support EGL_DEFAULT_DISPLAY GdkDisplay* gdkDisplay = gdk_display_get_default(); if (!GDK_IS_X11_DISPLAY(gdkDisplay)) { static auto sGdkWaylandDisplayGetWlDisplay = (wl_display * (*)(GdkDisplay*)) dlsym(RTLD_DEFAULT, "gdk_wayland_display_get_wl_display"); nativeDisplay = sGdkWaylandDisplayGetWlDisplay(gdkDisplay); if (!nativeDisplay) { NS_WARNING("Failed to get wl_display."); return nullptr; } } #endif chosenDisplay = GetAndInitDisplay(*this, nativeDisplay); } if (!chosenDisplay) { if (out_failureId->IsEmpty()) { *out_failureId = NS_LITERAL_CSTRING("FEATURE_FAILURE_NO_DISPLAY"); } NS_WARNING("Failed to initialize a display."); return nullptr; } return chosenDisplay; } template static void MarkExtensions(const char* rawExtString, bool shouldDumpExts, const char* extType, std::bitset* const out) { MOZ_ASSERT(rawExtString); const nsDependentCString extString(rawExtString); std::vector extList; SplitByChar(extString, ' ', &extList); if (shouldDumpExts) { printf_stderr("%u EGL %s extensions: (*: recognized)\n", (uint32_t)extList.size(), extType); } MarkBitfieldByStrings(extList, shouldDumpExts, sEGLExtensionNames, out); } void GLLibraryEGL::InitClientExtensions() { 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 client extensions.\n"); } return; } MarkExtensions(rawExtString, shouldDumpExts, "client", &mAvailableExtensions); } void GLLibraryEGL::InitDisplayExtensions() { MOZ_ASSERT(mEGLDisplay); const bool shouldDumpExts = GLContext::ShouldDumpExts(); const auto rawExtString = (const char*)fQueryString(mEGLDisplay, LOCAL_EGL_EXTENSIONS); if (!rawExtString) { NS_WARNING("Failed to query EGL display extensions!."); return; } MarkExtensions(rawExtString, shouldDumpExts, "display", &mAvailableExtensions); } void GLLibraryEGL::DumpEGLConfig(EGLConfig cfg) { int attrval; int err; #define ATTR(_x) \ do { \ fGetConfigAttrib(mEGLDisplay, cfg, LOCAL_EGL_##_x, &attrval); \ if ((err = fGetError()) != 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 GLLibraryEGL::DumpEGLConfigs() { int nc = 0; fGetConfigs(mEGLDisplay, nullptr, 0, &nc); EGLConfig* ec = new EGLConfig[nc]; fGetConfigs(mEGLDisplay, ec, nc, &nc); for (int i = 0; i < nc; ++i) { printf_stderr("========= EGL Config %d ========\n", i); DumpEGLConfig(ec[i]); } delete[] ec; } static bool ShouldTrace() { static bool ret = gfxEnv::GlDebugVerbose(); 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 */