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