From 435adc1361d4a74caaef7ef93dc9d6cf19f8fa83 Mon Sep 17 00:00:00 2001 From: James Willcox Date: Mon, 28 Sep 2015 17:09:56 -0500 Subject: [PATCH] Bug 1174703 - Add GLContextEAGL and GLContextProviderEAGL as an OpenGL provider for iOS. r=jgilbert --- gfx/gl/GLContextEAGL.h | 79 ++++++++++ gfx/gl/GLContextProvider.h | 9 ++ gfx/gl/GLContextProviderEAGL.mm | 271 ++++++++++++++++++++++++++++++++ gfx/gl/GLContextTypes.h | 3 +- gfx/gl/moz.build | 11 ++ 5 files changed, 372 insertions(+), 1 deletion(-) create mode 100644 gfx/gl/GLContextEAGL.h create mode 100644 gfx/gl/GLContextProviderEAGL.mm diff --git a/gfx/gl/GLContextEAGL.h b/gfx/gl/GLContextEAGL.h new file mode 100644 index 000000000000..83f3687b42a8 --- /dev/null +++ b/gfx/gl/GLContextEAGL.h @@ -0,0 +1,79 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* vim: set ts=8 sts=4 et sw=4 tw=80: */ +/* 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/. */ + +#ifndef GLCONTEXTEAGL_H_ +#define GLCONTEXTEAGL_H_ + +#include "GLContext.h" + +#include +#include + +namespace mozilla { +namespace gl { + +class GLContextEAGL : public GLContext +{ + friend class GLContextProviderEAGL; + + EAGLContext* const mContext; + +public: + MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(GLContextEAGL, override) + GLContextEAGL(const SurfaceCaps& caps, EAGLContext* context, + GLContext* sharedContext, + bool isOffscreen, ContextProfile profile); + + ~GLContextEAGL(); + + virtual GLContextType GetContextType() const override { + return GLContextType::EAGL; + } + + static GLContextEAGL* Cast(GLContext* gl) { + MOZ_ASSERT(gl->GetContextType() == GLContextType::EAGL); + return static_cast(gl); + } + + bool Init() override; + + bool AttachToWindow(nsIWidget* aWidget); + + EAGLContext* GetEAGLContext() const { return mContext; } + + virtual bool MakeCurrentImpl(bool aForce) override; + + virtual bool IsCurrent() override; + + virtual bool SetupLookupFunction() override; + + virtual bool IsDoubleBuffered() const override; + + virtual bool SupportsRobustness() const override; + + virtual bool SwapBuffers() override; + + virtual GLuint GetDefaultFramebuffer() override { + return mBackbufferFB; + } + + virtual bool RenewSurface() override { + return RecreateRB(); + } + +private: + GLuint mBackbufferRB; + GLuint mBackbufferFB; + + void* mLayer; + + bool RecreateRB(); +}; + +} // namespace gl +} // namespace mozilla + +#endif // GLCONTEXTEAGL_H_ diff --git a/gfx/gl/GLContextProvider.h b/gfx/gl/GLContextProvider.h index 8b4175a96217..ac83e3e6fca3 100644 --- a/gfx/gl/GLContextProvider.h +++ b/gfx/gl/GLContextProvider.h @@ -53,6 +53,15 @@ namespace gl { #define GL_CONTEXT_PROVIDER_DEFAULT GLContextProviderEGL #endif +#if defined(MOZ_WIDGET_UIKIT) +#define GL_CONTEXT_PROVIDER_NAME GLContextProviderEAGL +#include "GLContextProviderImpl.h" +#undef GL_CONTEXT_PROVIDER_NAME +#ifndef GL_CONTEXT_PROVIDER_DEFAULT +#define GL_CONTEXT_PROVIDER_DEFAULT GLContextProviderEAGL +#endif +#endif + #ifdef MOZ_GL_PROVIDER #define GL_CONTEXT_PROVIDER_NAME MOZ_GL_PROVIDER #include "GLContextProviderImpl.h" diff --git a/gfx/gl/GLContextProviderEAGL.mm b/gfx/gl/GLContextProviderEAGL.mm new file mode 100644 index 000000000000..3b80f062e877 --- /dev/null +++ b/gfx/gl/GLContextProviderEAGL.mm @@ -0,0 +1,271 @@ +/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * 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 "GLContextProvider.h" +#include "GLContextEAGL.h" +#include "nsDebug.h" +#include "nsIWidget.h" +#include "gfxPrefs.h" +#include "gfxFailure.h" +#include "prenv.h" +#include "mozilla/Preferences.h" +#include "GeckoProfiler.h" + +#import + +namespace mozilla { +namespace gl { + +GLContextEAGL::GLContextEAGL(const SurfaceCaps& caps, EAGLContext* context, + GLContext* sharedContext, + bool isOffscreen, ContextProfile profile) + : GLContext(caps, sharedContext, isOffscreen) + , mContext(context) + , mBackbufferRB(0) + , mBackbufferFB(0) + , mLayer(nil) +{ + SetProfileVersion(ContextProfile::OpenGLES, + [context API] == kEAGLRenderingAPIOpenGLES3 ? 300 : 200); +} + +GLContextEAGL::~GLContextEAGL() +{ + MakeCurrent(); + + if (mBackbufferFB) { + fDeleteFramebuffers(1, &mBackbufferFB); + } + + if (mBackbufferRB) { + fDeleteRenderbuffers(1, &mBackbufferRB); + } + + MarkDestroyed(); + + if (mLayer) { + mLayer = nil; + } + + if (mContext) { + [EAGLContext setCurrentContext:nil]; + [mContext release]; + } +} + +bool +GLContextEAGL::Init() +{ + if (!InitWithPrefix("gl", true)) + return false; + + return true; +} + +bool +GLContextEAGL::AttachToWindow(nsIWidget* aWidget) +{ + // This should only be called once + MOZ_ASSERT(!mBackbufferFB && !mBackbufferRB); + + UIView* view = + reinterpret_cast(aWidget->GetNativeData(NS_NATIVE_WIDGET)); + + if (!view) { + MOZ_CRASH("no view!"); + } + + mLayer = [view layer]; + + fGenFramebuffers(1, &mBackbufferFB); + return RecreateRB(); +} + +bool +GLContextEAGL::RecreateRB() +{ + MakeCurrent(); + + CAEAGLLayer* layer = (CAEAGLLayer*)mLayer; + + if (mBackbufferRB) { + // It doesn't seem to be enough to just call renderbufferStorage: below, + // we apparently have to recreate the RB. + fDeleteRenderbuffers(1, &mBackbufferRB); + mBackbufferRB = 0; + } + + fGenRenderbuffers(1, &mBackbufferRB); + fBindRenderbuffer(LOCAL_GL_RENDERBUFFER, mBackbufferRB); + + [mContext renderbufferStorage:LOCAL_GL_RENDERBUFFER + fromDrawable:layer]; + + fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, mBackbufferFB); + fFramebufferRenderbuffer(LOCAL_GL_FRAMEBUFFER, LOCAL_GL_COLOR_ATTACHMENT0, + LOCAL_GL_RENDERBUFFER, mBackbufferRB); + + return LOCAL_GL_FRAMEBUFFER_COMPLETE == fCheckFramebufferStatus(LOCAL_GL_FRAMEBUFFER); +} + +bool +GLContextEAGL::MakeCurrentImpl(bool aForce) +{ + if (!aForce && [EAGLContext currentContext] == mContext) { + return true; + } + + if (mContext) { + if(![EAGLContext setCurrentContext:mContext]) { + return false; + } + } + return true; +} + +bool +GLContextEAGL::IsCurrent() { + return [EAGLContext currentContext] == mContext; +} + +bool +GLContextEAGL::SetupLookupFunction() +{ + return false; +} + +bool +GLContextEAGL::IsDoubleBuffered() const +{ + return true; +} + +bool +GLContextEAGL::SupportsRobustness() const +{ + return false; +} + +bool +GLContextEAGL::SwapBuffers() +{ + PROFILER_LABEL("GLContextEAGL", "SwapBuffers", + js::ProfileEntry::Category::GRAPHICS); + + [mContext presentRenderbuffer:LOCAL_GL_RENDERBUFFER]; + return true; +} + + +already_AddRefed +GLContextProviderEAGL::CreateWrappingExisting(void*, void*) +{ + return nullptr; +} + +static GLContextEAGL* +GetGlobalContextEAGL() +{ + return static_cast(GLContextProviderEAGL::GetGlobalContext()); +} + +static already_AddRefed +CreateEAGLContext(bool aOffscreen, GLContextEAGL* sharedContext) +{ + EAGLRenderingAPI apis[] = { kEAGLRenderingAPIOpenGLES3, kEAGLRenderingAPIOpenGLES2 }; + + // Try to create a GLES3 context if we can, otherwise fall back to GLES2 + EAGLContext* context = nullptr; + for (EAGLRenderingAPI api : apis) { + if (sharedContext) { + context = [[EAGLContext alloc] initWithAPI:api + sharegroup:sharedContext->GetEAGLContext().sharegroup]; + } else { + context = [[EAGLContext alloc] initWithAPI:api]; + } + + if (context) { + break; + } + } + + if (!context) { + return nullptr; + } + + SurfaceCaps caps = SurfaceCaps::ForRGBA(); + ContextProfile profile = ContextProfile::OpenGLES; + RefPtr glContext = new GLContextEAGL(caps, context, + sharedContext, + aOffscreen, + profile); + + if (!glContext->Init()) { + glContext = nullptr; + return nullptr; + } + + return glContext.forget(); +} + +already_AddRefed +GLContextProviderEAGL::CreateForWindow(nsIWidget* aWidget) +{ + RefPtr glContext = CreateEAGLContext(false, GetGlobalContextEAGL()); + if (!glContext) { + return nullptr; + } + + if (!GLContextEAGL::Cast(glContext)->AttachToWindow(aWidget)) { + return nullptr; + } + + return glContext.forget(); +} + +already_AddRefed +GLContextProviderEAGL::CreateHeadless(CreateContextFlags flags) +{ + return CreateEAGLContext(true, GetGlobalContextEAGL()); +} + +already_AddRefed +GLContextProviderEAGL::CreateOffscreen(const mozilla::gfx::IntSize& size, + const SurfaceCaps& caps, + CreateContextFlags flags) +{ + RefPtr glContext = CreateHeadless(flags); + if (!glContext->InitOffscreen(size, caps)) { + return nullptr; + } + + return glContext.forget(); +} + +static RefPtr gGlobalContext; + +GLContext* +GLContextProviderEAGL::GetGlobalContext() +{ + if (!gGlobalContext) { + gGlobalContext = CreateEAGLContext(true, nullptr); + if (!gGlobalContext || + !static_cast(gGlobalContext.get())->Init()) + { + MOZ_CRASH("Failed to create global context"); + } + } + + return gGlobalContext; +} + +void +GLContextProviderEAGL::Shutdown() +{ + gGlobalContext = nullptr; +} + +} /* namespace gl */ +} /* namespace mozilla */ diff --git a/gfx/gl/GLContextTypes.h b/gfx/gl/GLContextTypes.h index a9225e2d8300..c99c03ec15b7 100644 --- a/gfx/gl/GLContextTypes.h +++ b/gfx/gl/GLContextTypes.h @@ -19,7 +19,8 @@ enum class GLContextType { WGL, CGL, GLX, - EGL + EGL, + EAGL }; enum class OriginPos : uint8_t { diff --git a/gfx/gl/moz.build b/gfx/gl/moz.build index 6e062730aa38..6a0b78e62f9e 100644 --- a/gfx/gl/moz.build +++ b/gfx/gl/moz.build @@ -10,6 +10,8 @@ if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows': gl_provider = 'WGL' elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa': gl_provider = 'CGL' +elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'uikit': + gl_provider = 'EAGL' elif CONFIG['MOZ_WIDGET_GTK']: if CONFIG['MOZ_EGL_XRENDER_COMPOSITE']: gl_provider = 'EGL' @@ -103,6 +105,15 @@ if gl_provider == 'CGL': SOURCES += [ 'SharedSurfaceIO.cpp', ] +elif gl_provider == 'EAGL': + # These files include ObjC headers that are unfriendly to unified builds + SOURCES += [ + 'GLContextProviderEAGL.mm', + ] + EXPORTS += [ + 'GLContextEAGL.h', + ] + elif gl_provider == 'GLX': # GLContextProviderGLX.cpp needs to be kept out of UNIFIED_SOURCES # as it includes X11 headers which cause conflicts.