1/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- 2 * This Source Code Form is subject to the terms of the Mozilla Public 3 * License, v. 2.0. If a copy of the MPL was not distributed with this 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 5 6#include "GLContextProvider.h" 7#include "GLContextEAGL.h" 8#include "nsDebug.h" 9#include "nsIWidget.h" 10#include "gfxFailure.h" 11#include "prenv.h" 12#include "mozilla/Preferences.h" 13#include "mozilla/layers/CompositorOptions.h" 14#include "mozilla/widget/CompositorWidget.h" 15#include "GeckoProfiler.h" 16 17#import <UIKit/UIKit.h> 18 19namespace mozilla { 20namespace gl { 21 22using namespace mozilla::widget; 23 24GLContextEAGL::GLContextEAGL(CreateContextFlags flags, const SurfaceCaps& caps, 25 EAGLContext* context, GLContext* sharedContext, bool isOffscreen) 26 : GLContext(flags, caps, sharedContext, isOffscreen), mContext(context) {} 27 28GLContextEAGL::~GLContextEAGL() { 29 MakeCurrent(); 30 31 if (mBackbufferFB) { 32 fDeleteFramebuffers(1, &mBackbufferFB); 33 } 34 35 if (mBackbufferRB) { 36 fDeleteRenderbuffers(1, &mBackbufferRB); 37 } 38 39 MarkDestroyed(); 40 41 if (mLayer) { 42 mLayer = nil; 43 } 44 45 if (mContext) { 46 [EAGLContext setCurrentContext:nil]; 47 [mContext release]; 48 } 49} 50 51bool GLContextEAGL::AttachToWindow(nsIWidget* aWidget) { 52 // This should only be called once 53 MOZ_ASSERT(!mBackbufferFB && !mBackbufferRB); 54 55 UIView* view = reinterpret_cast<UIView*>(aWidget->GetNativeData(NS_NATIVE_WIDGET)); 56 57 if (!view) { 58 MOZ_CRASH("no view!"); 59 } 60 61 mLayer = [view layer]; 62 63 fGenFramebuffers(1, &mBackbufferFB); 64 return RecreateRB(); 65} 66 67bool GLContextEAGL::RecreateRB() { 68 MakeCurrent(); 69 70 CAEAGLLayer* layer = (CAEAGLLayer*)mLayer; 71 72 if (mBackbufferRB) { 73 // It doesn't seem to be enough to just call renderbufferStorage: below, 74 // we apparently have to recreate the RB. 75 fDeleteRenderbuffers(1, &mBackbufferRB); 76 mBackbufferRB = 0; 77 } 78 79 fGenRenderbuffers(1, &mBackbufferRB); 80 fBindRenderbuffer(LOCAL_GL_RENDERBUFFER, mBackbufferRB); 81 82 [mContext renderbufferStorage:LOCAL_GL_RENDERBUFFER fromDrawable:layer]; 83 84 fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, mBackbufferFB); 85 fFramebufferRenderbuffer(LOCAL_GL_FRAMEBUFFER, LOCAL_GL_COLOR_ATTACHMENT0, LOCAL_GL_RENDERBUFFER, 86 mBackbufferRB); 87 88 return LOCAL_GL_FRAMEBUFFER_COMPLETE == fCheckFramebufferStatus(LOCAL_GL_FRAMEBUFFER); 89} 90 91bool GLContextEAGL::MakeCurrentImpl() const { 92 if (mContext) { 93 if (![EAGLContext setCurrentContext:mContext]) { 94 return false; 95 } 96 } 97 return true; 98} 99 100bool GLContextEAGL::IsCurrentImpl() const { return [EAGLContext currentContext] == mContext; } 101 102static PRFuncPtr GLAPIENTRY GetLoadedProcAddress(const char* const name) { 103 PRLibrary* lib = nullptr; 104 const auto& ret = PR_FindFunctionSymbolAndLibrary(name, &leakedLibRef); 105 if (lib) { 106 PR_UnloadLibrary(lib); 107 } 108 return ret; 109} 110 111Maybe<SymbolLoader> GLContextEAGL::GetSymbolLoader() const { 112 return Some(SymbolLoader(&GetLoadedProcAddress)); 113} 114 115bool GLContextEAGL::IsDoubleBuffered() const { return true; } 116 117bool GLContextEAGL::SwapBuffers() { 118 AUTO_PROFILER_LABEL("GLContextEAGL::SwapBuffers", GRAPHICS); 119 120 [mContext presentRenderbuffer:LOCAL_GL_RENDERBUFFER]; 121 return true; 122} 123 124void GLContextEAGL::GetWSIInfo(nsCString* const out) const { out->AppendLiteral("EAGL"); } 125 126already_AddRefed<GLContext> GLContextProviderEAGL::CreateWrappingExisting(void*, void*) { 127 return nullptr; 128} 129 130static GLContextEAGL* GetGlobalContextEAGL() { 131 return static_cast<GLContextEAGL*>(GLContextProviderEAGL::GetGlobalContext()); 132} 133 134static already_AddRefed<GLContext> CreateEAGLContext(CreateContextFlags flags, bool aOffscreen, 135 GLContextEAGL* sharedContext) { 136 EAGLRenderingAPI apis[] = {kEAGLRenderingAPIOpenGLES3, kEAGLRenderingAPIOpenGLES2}; 137 138 // Try to create a GLES3 context if we can, otherwise fall back to GLES2 139 EAGLContext* context = nullptr; 140 for (EAGLRenderingAPI api : apis) { 141 if (sharedContext) { 142 context = [[EAGLContext alloc] initWithAPI:api 143 sharegroup:sharedContext->GetEAGLContext().sharegroup]; 144 } else { 145 context = [[EAGLContext alloc] initWithAPI:api]; 146 } 147 148 if (context) { 149 break; 150 } 151 } 152 153 if (!context) { 154 return nullptr; 155 } 156 157 RefPtr<GLContextEAGL> glContext = 158 new GLContextEAGL(flags, SurfaceCaps::ForRGBA(), context, sharedContext, aOffscreen); 159 if (!glContext->Init()) { 160 glContext = nullptr; 161 return nullptr; 162 } 163 164 return glContext.forget(); 165} 166 167already_AddRefed<GLContext> GLContextProviderEAGL::CreateForCompositorWidget( 168 CompositorWidget* aCompositorWidget, bool aWebRender, bool aForceAccelerated) { 169 if (!aCompositorWidget) { 170 MOZ_ASSERT(false); 171 return nullptr; 172 } 173 174 RefPtr<GLContext> glContext = 175 CreateEAGLContext(CreateContextFlags::NONE, false, GetGlobalContextEAGL()); 176 if (!glContext) { 177 return nullptr; 178 } 179 180 if (!GLContextEAGL::Cast(glContext)->AttachToWindow(aCompositorWidget->RealWidget())) { 181 return nullptr; 182 } 183 184 return glContext.forget(); 185} 186 187already_AddRefed<GLContext> GLContextProviderEAGL::CreateHeadless(CreateContextFlags flags, 188 nsACString* const out_failureId) { 189 return CreateEAGLContext(flags, true, GetGlobalContextEAGL()); 190} 191 192already_AddRefed<GLContext> GLContextProviderEAGL::CreateOffscreen( 193 const mozilla::gfx::IntSize& size, const SurfaceCaps& caps, CreateContextFlags flags, 194 nsACString* const out_failureId) { 195 RefPtr<GLContext> glContext = CreateHeadless(flags, out_failureId); 196 if (!glContext->InitOffscreen(size, caps)) { 197 return nullptr; 198 } 199 200 return glContext.forget(); 201} 202 203static RefPtr<GLContext> gGlobalContext; 204 205GLContext* GLContextProviderEAGL::GetGlobalContext() { 206 static bool triedToCreateContext = false; 207 if (!triedToCreateContext) { 208 triedToCreateContext = true; 209 210 MOZ_RELEASE_ASSERT(!gGlobalContext, "GFX: Global GL context already initialized."); 211 RefPtr<GLContext> temp = CreateHeadless(CreateContextFlags::NONE); 212 gGlobalContext = temp; 213 214 if (!gGlobalContext) { 215 MOZ_CRASH("Failed to create global context"); 216 } 217 } 218 219 return gGlobalContext; 220} 221 222void GLContextProviderEAGL::Shutdown() { gGlobalContext = nullptr; } 223 224} /* namespace gl */ 225} /* namespace mozilla */ 226