Caffe2 - C++ API
A deep learning, cross platform ML framework
AndroidGLContext.cc
1 #include "AndroidGLContext.h"
2 #include "caffe2/core/logging.h"
3 #include "gl3stub.h"
4 #include <regex>
5 
6 namespace {
7 
8 static const std::unordered_map<std::string, GL_Renderer>& renderer_map() {
9  static std::unordered_map<std::string, GL_Renderer> m = {
10  {"Adreno", Adreno},
11  {"Mali", Mali},
12  {"NVIDIA", Tegra} /*, {"PowerVR", PowerVR} */};
13  return m;
14 }
15 
16 } // namespace
17 
18 EGLContext AndroidGLContext::create_opengl_thread_context() {
19  EGLSurface surface = EGL_NO_SURFACE;
20  EGLContext context = EGL_NO_CONTEXT;
21  EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
22  if (display == EGL_NO_DISPLAY) {
23  // We failed to get a display
24  CAFFE_THROW("Problem with OpenGL context");
25  return context;
26  }
27 
28  EGLint major;
29  EGLint minor;
30  eglInitialize(display, &major, &minor);
31 
32  const EGLint configAttr[] = {EGL_RENDERABLE_TYPE,
33  EGL_OPENGL_ES2_BIT,
34  EGL_SURFACE_TYPE,
35  EGL_PBUFFER_BIT, // we create a pixelbuffer surface
36  EGL_NONE};
37 
38  EGLint numConfig;
39  EGLConfig eglConfig;
40  if (!eglChooseConfig(display, configAttr, &eglConfig, 1, &numConfig)) {
41  // We failed to find a suitable config
42  eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
43  eglTerminate(display);
44  display = EGL_NO_DISPLAY;
45  CAFFE_THROW("Problem with OpenGL context");
46  return context;
47  }
48 
49  const EGLint ctxAttr[] = {EGL_CONTEXT_CLIENT_VERSION,
50  2, // very important!
51  EGL_NONE};
52 
53  // Create an EGL context based on the chosen configuration.
54  context = eglCreateContext(display, eglConfig, EGL_NO_CONTEXT, ctxAttr);
55 
56  // We need a surface. For most mixed JNI/Java based apps it is suggested
57  // that we pass a Java surface through JNI and extract the surface
58  // Pure NDK apps get passed the android_app structure which includes a surface
59  // We want our own OpenGL context for the current thread.
60  // Here we create a fake 1x1 'pixel buffer' surface.
61  // We don't expecting to run vertex or fragment shaders.
62 
63  const EGLint surfaceAttr[] = {EGL_WIDTH, 1, EGL_HEIGHT, 1, EGL_NONE};
64 
65  surface = eglCreatePbufferSurface(display, eglConfig, surfaceAttr);
66 
67  // Bind context, draw and surface to current thread
68  eglMakeCurrent(display, surface, surface, context);
69 
70  // Bind the API for this context. In our case we want to use OpenGL_ES
71  eglBindAPI(EGL_OPENGL_ES_API);
72  return context;
73 }
74 
75 bool AndroidGLContext::opengl_thread_context_exists() {
76  return eglGetCurrentContext() != EGL_NO_CONTEXT;
77 }
78 
79 bool AndroidGLContext::release_opengl_thread_context() {
80  EGLContext display = eglGetCurrentDisplay();
81  if (display != EGL_NO_DISPLAY) {
82  if (_eglcontext != EGL_NO_CONTEXT) {
83  eglDestroyContext(display, _eglcontext);
84  _eglcontext = EGL_NO_CONTEXT;
85  }
86  EGLSurface surface = eglGetCurrentSurface(EGL_DRAW);
87  if (surface != EGL_NO_SURFACE) {
88  eglDestroySurface(display, surface);
89  surface = EGL_NO_SURFACE;
90  }
91  surface = eglGetCurrentSurface(EGL_READ);
92  if (surface != EGL_NO_SURFACE) {
93  eglDestroySurface(display, surface);
94  surface = EGL_NO_SURFACE;
95  }
96  eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
97  eglTerminate(display);
98  display = EGL_NO_DISPLAY;
99  }
100  eglReleaseThread();
101  return true;
102 }
103 
104 void AndroidGLContext::init_gles3() {
105  if (!gl3stubInit()) {
106  CAFFE_THROW("OpenGL ES 3 not initialized");
107  } else {
108  LOG(INFO) << "OpenGL ES 3 successfully enabled";
109  }
110 }
111 
112 GL_Renderer AndroidGLContext::get_platform() {
113  std::string rendererStr((const char*)glGetString(GL_RENDERER));
114  std::regex regStr("^[A-Za-z]*");
115  std::smatch matchs;
116  if (std::regex_search(rendererStr, matchs, regStr)) {
117  const std::string renderer = *matchs.begin();
118  auto found = renderer_map().find(renderer);
119  if (found != renderer_map().end()) {
120  return found->second;
121  }
122  }
123  CAFFE_THROW("Unsupported GPU renderer");
124 }
125 
126 AndroidGLContext::AndroidGLContext() {
127  if (!opengl_thread_context_exists()) {
128  _eglcontext = create_opengl_thread_context();
129  LOG(INFO) << "New EGLContext created";
130 
131  if (!supportOpenGLES3(&half_float_supported)) {
132  CAFFE_THROW("OpenGL ES 3 not supported");
133  }
134 
135  if (!isSupportedDevice()) {
136  LOG(ERROR) << "Device not fully supported";
137  }
138  } else {
139  _eglcontext = EGL_NO_CONTEXT;
140  LOG(INFO) << "Reusing EGLContext, make sure OpenGL ES 3 is supported";
141  }
142  static std::once_flag once;
143  std::call_once(once, [&]() { init_gles3(); });
144 }
145 
146 AndroidGLContext::~AndroidGLContext() {
147  if (_eglcontext != EGL_NO_CONTEXT) {
148  release_opengl_thread_context();
149  }
150 }
151 
152 void AndroidGLContext::set_context() {}
153 
154 void AndroidGLContext::reset_context() {}
155 
156 void AndroidGLContext::flush_context() {}