/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- * 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 "gfxFailure.h" #include "prenv.h" #include "mozilla/Preferences.h" #include "mozilla/layers/CompositorOptions.h" #include "mozilla/widget/CompositorWidget.h" #include "GeckoProfiler.h" #import namespace mozilla { namespace gl { using namespace mozilla::widget; GLContextEAGL::GLContextEAGL(CreateContextFlags flags, const SurfaceCaps& caps, EAGLContext* context, GLContext* sharedContext, bool isOffscreen) : GLContext(flags, caps, sharedContext, isOffscreen), mContext(context) {} 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::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() const { if (mContext) { if (![EAGLContext setCurrentContext:mContext]) { return false; } } return true; } bool GLContextEAGL::IsCurrentImpl() const { return [EAGLContext currentContext] == mContext; } static PRFuncPtr GLAPIENTRY GetLoadedProcAddress(const char* const name) { PRLibrary* lib = nullptr; const auto& ret = PR_FindFunctionSymbolAndLibrary(name, &leakedLibRef); if (lib) { PR_UnloadLibrary(lib); } return ret; } Maybe GLContextEAGL::GetSymbolLoader() const { return Some(SymbolLoader(&GetLoadedProcAddress)); } bool GLContextEAGL::IsDoubleBuffered() const { return true; } bool GLContextEAGL::SwapBuffers() { AUTO_PROFILER_LABEL("GLContextEAGL::SwapBuffers", GRAPHICS); [mContext presentRenderbuffer:LOCAL_GL_RENDERBUFFER]; return true; } void GLContextEAGL::GetWSIInfo(nsCString* const out) const { out->AppendLiteral("EAGL"); } already_AddRefed GLContextProviderEAGL::CreateWrappingExisting(void*, void*) { return nullptr; } static GLContextEAGL* GetGlobalContextEAGL() { return static_cast(GLContextProviderEAGL::GetGlobalContext()); } static already_AddRefed 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; } RefPtr glContext = new GLContextEAGL(flags, SurfaceCaps::ForRGBA(), context, sharedContext, aOffscreen); if (!glContext->Init()) { glContext = nullptr; return nullptr; } return glContext.forget(); } already_AddRefed GLContextProviderEAGL::CreateForCompositorWidget( CompositorWidget* aCompositorWidget, bool aWebRender, bool aForceAccelerated) { if (!aCompositorWidget) { MOZ_ASSERT(false); return nullptr; } return CreateForWindow(aCompositorWidget->RealWidget(), aWebRender, aForceAccelerated); } already_AddRefed GLContextProviderEAGL::CreateForWindow(nsIWidget* aWidget, bool aWebRender, bool aForceAccelerated) { RefPtr glContext = CreateEAGLContext(CreateContextFlags::NONE, false, GetGlobalContextEAGL()); if (!glContext) { return nullptr; } if (!GLContextEAGL::Cast(glContext)->AttachToWindow(aWidget)) { return nullptr; } return glContext.forget(); } already_AddRefed GLContextProviderEAGL::CreateHeadless(CreateContextFlags flags, nsACString* const out_failureId) { return CreateEAGLContext(flags, true, GetGlobalContextEAGL()); } already_AddRefed GLContextProviderEAGL::CreateOffscreen( const mozilla::gfx::IntSize& size, const SurfaceCaps& caps, CreateContextFlags flags, nsACString* const out_failureId) { RefPtr glContext = CreateHeadless(flags, out_failureId); if (!glContext->InitOffscreen(size, caps)) { return nullptr; } return glContext.forget(); } static RefPtr gGlobalContext; GLContext* GLContextProviderEAGL::GetGlobalContext() { static bool triedToCreateContext = false; if (!triedToCreateContext) { triedToCreateContext = true; MOZ_RELEASE_ASSERT(!gGlobalContext, "GFX: Global GL context already initialized."); RefPtr 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 */