Bug 1453501 - Allow GLContextEGL to be used without a surface r=jgilbert
Right now GLContextProviderEGL requires the widget to have a valid
EGLSurface when creating a non-offscreen GLContext. This patch falls
back to a dummy pbuffer surface or EGL_NO_SURFACE if supported, allowing
the GLContext creation to succeed. This will give us some more flexibility
on Android where the widget surface is not always readily available.
Additinally, we use the fallback surface any time MakeCurrent() is
called without a valid surface. This is needed to allow things like
Compositor shutdown when there is no widget surface available.
MozReview-Commit-ID: 1kbLIGNiOkV
--- a/gfx/gl/GLContextEGL.h
+++ b/gfx/gl/GLContextEGL.h
@@ -112,16 +112,17 @@ public:
protected:
friend class GLContextProviderEGL;
friend class GLContextEGLFactory;
public:
const EGLConfig mConfig;
protected:
EGLSurface mSurface;
+ EGLSurface mFallbackSurface;
public:
const EGLContext mContext;
protected:
EGLSurface mSurfaceOverride;
RefPtr<gfxASurface> mThebesSurface;
bool mBound;
bool mIsPBuffer;
--- a/gfx/gl/GLContextProviderEGL.cpp
+++ b/gfx/gl/GLContextProviderEGL.cpp
@@ -126,20 +126,43 @@ DestroySurface(EGLSurface oldSurface) {
sEGLLibrary.fMakeCurrent(EGL_DISPLAY(),
EGL_NO_SURFACE, EGL_NO_SURFACE,
EGL_NO_CONTEXT);
sEGLLibrary.fDestroySurface(EGL_DISPLAY(), oldSurface);
}
}
static EGLSurface
+CreateFallbackSurface(const EGLConfig& config) {
+ if (sEGLLibrary.IsExtensionSupported(GLLibraryEGL::KHR_surfaceless_context)) {
+ // We don't need a PBuffer surface in this case
+ return EGL_NO_SURFACE;
+ }
+
+ nsTArray<EGLint> pbattrs(2 + MOZ_ARRAY_LENGTH(kTerminationAttribs));
+
+ pbattrs.Clear();
+ pbattrs.AppendElement(LOCAL_EGL_WIDTH); pbattrs.AppendElement(16);
+ pbattrs.AppendElement(LOCAL_EGL_HEIGHT); pbattrs.AppendElement(16);
+
+ for (const auto& cur : kTerminationAttribs) {
+ pbattrs.AppendElement(cur);
+ }
+
+ EGLSurface surface = sEGLLibrary.fCreatePbufferSurface(EGL_DISPLAY(), config, &pbattrs[0]);
+ MOZ_ASSERT(surface);
+
+ return surface;
+}
+
+static EGLSurface
CreateSurfaceFromNativeWindow(EGLNativeWindowType window, const EGLConfig& config) {
- EGLSurface newSurface = nullptr;
+ MOZ_ASSERT(window);
+ EGLSurface newSurface = EGL_NO_SURFACE;
- MOZ_ASSERT(window);
#ifdef MOZ_WIDGET_ANDROID
JNIEnv* const env = jni::GetEnvForThread();
ANativeWindow* const nativeWindow = ANativeWindow_fromSurface(
env, reinterpret_cast<jobject>(window));
newSurface = sEGLLibrary.fCreateWindowSurface(
sEGLLibrary.fGetDisplay(EGL_DEFAULT_DISPLAY),
config, nativeWindow, 0);
ANativeWindow_release(nativeWindow);
@@ -163,17 +186,16 @@ private:
GLContextEGLFactory(){}
~GLContextEGLFactory(){}
};
already_AddRefed<GLContext>
GLContextEGLFactory::Create(EGLNativeWindowType aWindow,
bool aWebRender)
{
- MOZ_ASSERT(aWindow);
nsCString discardFailureId;
if (!sEGLLibrary.EnsureInitialized(false, &discardFailureId)) {
gfxCriticalNote << "Failed to load EGL library 3!";
return nullptr;
}
bool doubleBuffered = true;
@@ -188,21 +210,19 @@ GLContextEGLFactory::Create(EGLNativeWin
}
} else {
if (!CreateConfig(&config, aWebRender)) {
gfxCriticalNote << "Failed to create EGLConfig!";
return nullptr;
}
}
- EGLSurface surface = mozilla::gl::CreateSurfaceFromNativeWindow(aWindow, config);
-
- if (!surface) {
- gfxCriticalNote << "Failed to create EGLSurface!";
- return nullptr;
+ EGLSurface surface = EGL_NO_SURFACE;
+ if (aWindow) {
+ surface = mozilla::gl::CreateSurfaceFromNativeWindow(aWindow, config);
}
CreateContextFlags flags = CreateContextFlags::NONE;
if (aWebRender) {
flags |= CreateContextFlags::PREFER_ES3;
}
SurfaceCaps caps = SurfaceCaps::Any();
RefPtr<GLContextEGL> gl = GLContextEGL::CreateGLContext(flags, caps, false, config,
@@ -223,16 +243,17 @@ GLContextEGLFactory::Create(EGLNativeWin
}
GLContextEGL::GLContextEGL(CreateContextFlags flags, const SurfaceCaps& caps,
bool isOffscreen, EGLConfig config, EGLSurface surface,
EGLContext context)
: GLContext(flags, caps, nullptr, isOffscreen, false)
, mConfig(config)
, mSurface(surface)
+ , mFallbackSurface(CreateFallbackSurface(config))
, mContext(context)
, mSurfaceOverride(EGL_NO_SURFACE)
, mThebesSurface(nullptr)
, mBound(false)
, mIsPBuffer(false)
, mIsDoubleBuffered(false)
, mCanBindToTexture(false)
, mShareWithEGLImage(false)
@@ -254,16 +275,17 @@ GLContextEGL::~GLContextEGL()
#ifdef DEBUG
printf_stderr("Destroying context %p surface %p on display %p\n", mContext, mSurface, EGL_DISPLAY());
#endif
sEGLLibrary.fDestroyContext(EGL_DISPLAY(), mContext);
mozilla::gl::DestroySurface(mSurface);
+ mozilla::gl::DestroySurface(mFallbackSurface);
}
bool
GLContextEGL::Init()
{
#if defined(ANDROID)
// We can't use LoadApitraceLibrary here because the GLContext
// expects its own handle to the GL library
@@ -351,18 +373,22 @@ GLContextEGL::SetEGLSurfaceOverride(EGLS
mSurfaceOverride = surf;
DebugOnly<bool> ok = MakeCurrent(true);
MOZ_ASSERT(ok);
}
bool
GLContextEGL::MakeCurrentImpl() const
{
- const EGLSurface surface = (mSurfaceOverride != EGL_NO_SURFACE) ? mSurfaceOverride
- : mSurface;
+ EGLSurface surface = (mSurfaceOverride != EGL_NO_SURFACE) ? mSurfaceOverride
+ : mSurface;
+ if (!surface) {
+ surface = mFallbackSurface;
+ }
+
const bool succeeded = sEGLLibrary.fMakeCurrent(EGL_DISPLAY(), surface, surface,
mContext);
if (!succeeded) {
const auto eglError = sEGLLibrary.fGetError();
if (eglError == LOCAL_EGL_CONTEXT_LOST) {
mContextLost = true;
NS_WARNING("EGL context has been lost.");
} else {
@@ -386,20 +412,22 @@ bool
GLContextEGL::RenewSurface(CompositorWidget* aWidget) {
if (!mOwnsContext) {
return false;
}
// unconditionally release the surface and create a new one. Don't try to optimize this away.
// If we get here, then by definition we know that we want to get a new surface.
ReleaseSurface();
MOZ_ASSERT(aWidget);
- mSurface = mozilla::gl::CreateSurfaceFromNativeWindow(GET_NATIVE_WINDOW_FROM_COMPOSITOR_WIDGET(aWidget), mConfig);
- if (!mSurface) {
- return false;
+
+ void* nativeWindow = GET_NATIVE_WINDOW_FROM_COMPOSITOR_WIDGET(aWidget);
+ if (nativeWindow) {
+ mSurface = mozilla::gl::CreateSurfaceFromNativeWindow(nativeWindow, mConfig);
}
+
return MakeCurrent(true);
}
void
GLContextEGL::ReleaseSurface() {
if (mOwnsContext) {
mozilla::gl::DestroySurface(mSurface);
}
--- a/gfx/gl/GLLibraryEGL.cpp
+++ b/gfx/gl/GLLibraryEGL.cpp
@@ -56,16 +56,17 @@ static const char* sEGLExtensionNames[]
"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"
};
#if defined(ANDROID)
static PRLibrary* LoadApitraceLibrary()
{
// Initialization of gfx prefs here is only needed during the unit tests...
gfxPrefs::GetSingleton();
--- a/gfx/gl/GLLibraryEGL.h
+++ b/gfx/gl/GLLibraryEGL.h
@@ -70,17 +70,17 @@ public:
void InitClientExtensions();
void InitDisplayExtensions();
/**
* Known GL extensions that can be queried by
* IsExtensionSupported. The results of this are cached, and as
* such it's safe to use this even in performance critical code.
* If you add to this array, remember to add to the string names
- * in GLContext.cpp.
+ * in GLLibraryEGL.cpp.
*/
enum EGLExtensions {
KHR_image_base,
KHR_image_pixmap,
KHR_gl_texture_2D_image,
KHR_lock_surface,
ANGLE_surface_d3d_texture_2d_share_handle,
EXT_create_context_robustness,
@@ -94,16 +94,17 @@ public:
KHR_create_context,
KHR_stream,
KHR_stream_consumer_gltexture,
EXT_device_query,
NV_stream_consumer_gltexture_yuv,
ANGLE_stream_producer_d3d_texture,
ANGLE_device_creation,
ANGLE_device_creation_d3d11,
+ KHR_surfaceless_context,
Extensions_Max
};
bool IsExtensionSupported(EGLExtensions aKnownExtension) const {
return mAvailableExtensions[aKnownExtension];
}
void MarkExtensionUnsupported(EGLExtensions aKnownExtension) {