summaryrefslogtreecommitdiffstats
path: root/gfx/gl/GLContextProviderEAGL.mm
diff options
context:
space:
mode:
Diffstat (limited to 'gfx/gl/GLContextProviderEAGL.mm')
-rw-r--r--gfx/gl/GLContextProviderEAGL.mm281
1 files changed, 281 insertions, 0 deletions
diff --git a/gfx/gl/GLContextProviderEAGL.mm b/gfx/gl/GLContextProviderEAGL.mm
new file mode 100644
index 000000000..784a3e29e
--- /dev/null
+++ b/gfx/gl/GLContextProviderEAGL.mm
@@ -0,0 +1,281 @@
+/* -*- 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 "mozilla/widget/CompositorWidget.h"
+#include "GeckoProfiler.h"
+
+#import <UIKit/UIKit.h>
+
+namespace mozilla {
+namespace gl {
+
+using namespace mozilla::widget;
+
+GLContextEAGL::GLContextEAGL(CreateContextFlags flags, const SurfaceCaps& caps,
+ EAGLContext* context, GLContext* sharedContext,
+ bool isOffscreen, ContextProfile profile)
+ : GLContext(flags, 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<UIView*>(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::SwapBuffers()
+{
+ PROFILER_LABEL("GLContextEAGL", "SwapBuffers",
+ js::ProfileEntry::Category::GRAPHICS);
+
+ [mContext presentRenderbuffer:LOCAL_GL_RENDERBUFFER];
+ return true;
+}
+
+
+already_AddRefed<GLContext>
+GLContextProviderEAGL::CreateWrappingExisting(void*, void*)
+{
+ return nullptr;
+}
+
+static GLContextEAGL*
+GetGlobalContextEAGL()
+{
+ return static_cast<GLContextEAGL*>(GLContextProviderEAGL::GetGlobalContext());
+}
+
+static already_AddRefed<GLContext>
+CreateEAGLContext(CreateContextFlags flags, 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<GLContextEAGL> glContext = new GLContextEAGL(flags, caps, context,
+ sharedContext,
+ aOffscreen,
+ profile);
+
+ if (!glContext->Init()) {
+ glContext = nullptr;
+ return nullptr;
+ }
+
+ return glContext.forget();
+}
+
+already_AddRefed<GLContext>
+GLContextProviderEAGL::CreateForCompositorWidget(CompositorWidget* aCompositorWidget, bool aForceAccelerated)
+{
+ return CreateForWindow(aCompositorWidget->RealWidget(), aForceAccelerated);
+}
+
+already_AddRefed<GLContext>
+GLContextProviderEAGL::CreateForWindow(nsIWidget* aWidget, bool aForceAccelerated)
+{
+ RefPtr<GLContext> glContext = CreateEAGLContext(CreateContextFlags::NONE, false,
+ GetGlobalContextEAGL());
+ if (!glContext) {
+ return nullptr;
+ }
+
+ if (!GLContextEAGL::Cast(glContext)->AttachToWindow(aWidget)) {
+ return nullptr;
+ }
+
+ return glContext.forget();
+}
+
+already_AddRefed<GLContext>
+GLContextProviderEAGL::CreateHeadless(CreateContextFlags flags,
+ nsACString* const out_failureId)
+{
+ return CreateEAGLContext(flags, true, GetGlobalContextEAGL());
+}
+
+already_AddRefed<GLContext>
+GLContextProviderEAGL::CreateOffscreen(const mozilla::gfx::IntSize& size,
+ const SurfaceCaps& caps,
+ CreateContextFlags flags,
+ nsACString* const out_failureId)
+{
+ RefPtr<GLContext> glContext = CreateHeadless(flags, out_failureId);
+ if (!glContext->InitOffscreen(size, caps)) {
+ return nullptr;
+ }
+
+ return glContext.forget();
+}
+
+static RefPtr<GLContext> gGlobalContext;
+
+GLContext*
+GLContextProviderEAGL::GetGlobalContext()
+{
+ static bool triedToCreateContext = false;
+ if (!triedToCreateContext) {
+ triedToCreateContext = true;
+
+ MOZ_RELEASE_ASSERT(!gGlobalContext, "GFX: Global GL context already initialized.");
+ RefPtr<GLContext> temp = CreateHeadless(CreateContextFlags::NONE);
+ gGlobalContext = temp;
+
+ if (!gGlobalContext) {
+ MOZ_CRASH("Failed to create global context");
+ }
+ }
+
+ return gGlobalContext;
+}
+
+void
+GLContextProviderEAGL::Shutdown()
+{
+ gGlobalContext = nullptr;
+}
+
+} /* namespace gl */
+} /* namespace mozilla */