1 /*
2  * Copyright 2020 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include <android/log.h>
18 #include <android/native_window.h>
19 #include <android/native_window_jni.h>
20 #include <EGL/egl.h>
21 #include <EGL/eglext.h>
22 #include <EGL/eglplatform.h>
23 #include <GLES2/gl2.h>
24 #include <GLES2/gl2ext.h>
25 #include <jni.h>
26 
27 #include <cassert>
28 #include <iomanip>
29 #include <sstream>
30 #include <string>
31 #include <utility>
32 #include <vector>
33 
34 namespace {
35     auto constexpr LOG_TAG = "OpenGLRendererJni";
36 
GLErrorString(GLenum error)37     std::string GLErrorString(GLenum error) {
38         switch (error) {
39             case GL_NO_ERROR:
40                 return "GL_NO_ERROR";
41             case GL_INVALID_ENUM:
42                 return "GL_INVALID_ENUM";
43             case GL_INVALID_VALUE:
44                 return "GL_INVALID_VALUE";
45             case GL_INVALID_OPERATION:
46                 return "GL_INVALID_OPERATION";
47             case GL_STACK_OVERFLOW_KHR:
48                 return "GL_STACK_OVERFLOW";
49             case GL_STACK_UNDERFLOW_KHR:
50                 return "GL_STACK_UNDERFLOW";
51             case GL_OUT_OF_MEMORY:
52                 return "GL_OUT_OF_MEMORY";
53             case GL_INVALID_FRAMEBUFFER_OPERATION:
54                 return "GL_INVALID_FRAMEBUFFER_OPERATION";
55             default: {
56                 std::ostringstream oss;
57                 oss << "<Unknown GL Error 0x" << std::setfill('0') <<
58                     std::setw(4) << std::right << std::hex << error << ">";
59                 return oss.str();
60             }
61         }
62     }
63 
EGLErrorString(EGLenum error)64     std::string EGLErrorString(EGLenum error) {
65         switch (error) {
66             case EGL_SUCCESS:
67                 return "EGL_SUCCESS";
68             case EGL_NOT_INITIALIZED:
69                 return "EGL_NOT_INITIALIZED";
70             case EGL_BAD_ACCESS:
71                 return "EGL_BAD_ACCESS";
72             case EGL_BAD_ALLOC:
73                 return "EGL_BAD_ALLOC";
74             case EGL_BAD_ATTRIBUTE:
75                 return "EGL_BAD_ATTRIBUTE";
76             case EGL_BAD_CONTEXT:
77                 return "EGL_BAD_CONTEXT";
78             case EGL_BAD_CONFIG:
79                 return "EGL_BAD_CONFIG";
80             case EGL_BAD_CURRENT_SURFACE:
81                 return "EGL_BAD_CURRENT_SURFACE";
82             case EGL_BAD_DISPLAY:
83                 return "EGL_BAD_DISPLAY";
84             case EGL_BAD_SURFACE:
85                 return "EGL_BAD_SURFACE";
86             case EGL_BAD_MATCH:
87                 return "EGL_BAD_MATCH";
88             case EGL_BAD_PARAMETER:
89                 return "EGL_BAD_PARAMETER";
90             case EGL_BAD_NATIVE_PIXMAP:
91                 return "EGL_BAD_NATIVE_PIXMAP";
92             case EGL_BAD_NATIVE_WINDOW:
93                 return "EGL_BAD_NATIVE_WINDOW";
94             case EGL_CONTEXT_LOST:
95                 return "EGL_CONTEXT_LOST";
96             default: {
97                 std::ostringstream oss;
98                 oss << "<Unknown EGL Error 0x" << std::setfill('0') <<
99                     std::setw(4) << std::right << std::hex << error << ">";
100                 return oss.str();
101             }
102         }
103     }
104 }
105 
106 #ifdef NDEBUG
107 #define CHECK_GL(gl_func) [&]() { return gl_func; }()
108 #else
109 namespace {
110     class CheckGlErrorOnExit {
111     public:
CheckGlErrorOnExit(std::string glFunStr,unsigned int lineNum)112         explicit CheckGlErrorOnExit(std::string glFunStr, unsigned int lineNum) :
113                 mGlFunStr(std::move(glFunStr)),
114                 mLineNum(lineNum) {}
115 
~CheckGlErrorOnExit()116         ~CheckGlErrorOnExit() {
117             GLenum err = glGetError();
118             if (err != GL_NO_ERROR) {
119                 __android_log_assert(nullptr, LOG_TAG, "OpenGL Error: %s at %s [%s:%d]",
120                                      GLErrorString(err).c_str(), mGlFunStr.c_str(), __FILE__,
121                                      mLineNum);
122             }
123         }
124 
125         CheckGlErrorOnExit(const CheckGlErrorOnExit &) = delete;
126 
127         CheckGlErrorOnExit &operator=(const CheckGlErrorOnExit &) = delete;
128 
129     private:
130         std::string mGlFunStr;
131         unsigned int mLineNum;
132     };  // class CheckGlErrorOnExit
133 }   // namespace
134 #define CHECK_GL(glFunc)                                                    \
135   [&]() {                                                                   \
136     auto assertOnExit = CheckGlErrorOnExit(#glFunc, __LINE__);              \
137     return glFunc;                                                          \
138   }()
139 #endif
140 
141 namespace {
142     // Must be kept in sync with constants with same name in OpenGLRenderer.java
143     enum RendererDynamicRange : jint {
144         RENDERER_DYN_RNG_SDR = 1,     // Equivalent to DynamicRange.ENCODING_SDR
145         RENDERER_DYN_RNG_HDR_HLG = 3  // Equivalent to DynamicRange.ENCODING_HLG
146     };
147 
148     constexpr char VERTEX_SHADER_SRC[] = R"SRC(
149       attribute vec4 position;
150       attribute vec4 texCoords;
151       uniform mat4 mvpTransform;
152       uniform mat4 texTransform;
153       varying vec2 fragCoord;
154       void main() {
155         fragCoord = (texTransform * texCoords).xy;
156         gl_Position = mvpTransform * position;
157       }
158 )SRC";
159 
160     constexpr char FRAGMENT_SHADER_SRC[] = R"SRC(
161       #extension GL_OES_EGL_image_external : require
162       precision mediump float;
163       uniform samplerExternalOES sampler;
164       varying vec2 fragCoord;
165       void main() {
166         gl_FragColor = texture2D(sampler, fragCoord);
167       }
168 )SRC";
169 
170     constexpr char HDR_VERTEX_SHADER_SRC[] =
171 R"SRC(#version 300 es
172       in vec4 position;
173       in vec4 texCoords;
174       uniform mat4 mvpTransform;
175       uniform mat4 texTransform;
176       out vec2 fragCoord;
177       void main() {
178         fragCoord = (texTransform * texCoords).xy;
179         gl_Position = mvpTransform * position;
180       }
181 )SRC";
182 
183     constexpr char HDR_FRAGMENT_SHADER_SRC[] =
184 R"SRC(#version 300 es
185       #extension GL_OES_EGL_image_external : require
186       #extension GL_EXT_YUV_target : require
187       precision mediump float;
188       uniform __samplerExternal2DY2YEXT sampler;
189       in vec2 fragCoord;
190       out vec4 outColor;
191 
192       vec3 yuvToRgb(vec3 yuv) {
193         const vec3 yuvOffset = vec3(0.0625, 0.5, 0.5);
194         const mat3 yuvToRgbColorTransform = mat3(
195           1.1689f, 1.1689f, 1.1689f,
196           0.0000f, -0.1881f, 2.1502f,
197           1.6853f, -0.6530f, 0.0000f
198         );
199         return clamp(yuvToRgbColorTransform * (yuv - yuvOffset), 0.0, 1.0);
200       }
201 
202       void main() {
203         vec3 srcYuv = texture(sampler, fragCoord).xyz;
204         outColor = vec4(yuvToRgb(srcYuv), 1.0);
205       }
206 )SRC";
207 
208     struct NativeContext {
209         EGLDisplay display;
210         EGLConfig config;
211         EGLContext context;
212         std::pair<ANativeWindow *, EGLSurface> windowSurface;
213         EGLSurface pbufferSurface;
214         GLuint program;
215         GLint positionHandle;
216         GLint texCoordsHandle;
217         GLint samplerHandle;
218         GLint mvpTransformHandle;
219         GLint texTransformHandle;
220         GLuint textureId;
221         bool supportsHdr;
222 
NativeContext__anon928358260311::NativeContext223         NativeContext()
224                 : display(EGL_NO_DISPLAY),
225                   config(nullptr),
226                   context(EGL_NO_CONTEXT),
227                   windowSurface(std::make_pair(nullptr, EGL_NO_SURFACE)),
228                   pbufferSurface(EGL_NO_SURFACE),
229                   program(0),
230                   positionHandle(-1),
231                   texCoordsHandle(1),
232                   samplerHandle(-1),
233                   mvpTransformHandle(-1),
234                   texTransformHandle(-1),
235                   textureId(0),
236                   supportsHdr(false) {}
237     };
238 
ShaderTypeString(GLenum shaderType)239     const char *ShaderTypeString(GLenum shaderType) {
240         switch (shaderType) {
241             case GL_VERTEX_SHADER:
242                 return "GL_VERTEX_SHADER";
243             case GL_FRAGMENT_SHADER:
244                 return "GL_FRAGMENT_SHADER";
245             default:
246                 return "<Unknown shader type>";
247         }
248     }
249 
250     // Returns a handle to the shader
CompileShader(GLenum shaderType,const char * shaderSrc)251     GLuint CompileShader(GLenum shaderType, const char *shaderSrc) {
252         GLuint shader = CHECK_GL(glCreateShader(shaderType));
253         assert(shader);
254         CHECK_GL(glShaderSource(shader, 1, &shaderSrc, /*length=*/nullptr));
255         CHECK_GL(glCompileShader(shader));
256         GLint compileStatus = 0;
257         CHECK_GL(glGetShaderiv(shader, GL_COMPILE_STATUS, &compileStatus));
258         if (!compileStatus) {
259             GLint logLength = 0;
260             CHECK_GL(glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &logLength));
261             std::vector<char> logBuffer(logLength);
262             if (logLength > 0) {
263                 CHECK_GL(glGetShaderInfoLog(shader, logLength, /*length=*/nullptr,
264                                             &logBuffer[0]));
265             }
266             __android_log_print(ANDROID_LOG_ERROR, LOG_TAG,
267                                 "Unable to compile %s shader:\n %s.",
268                                 ShaderTypeString(shaderType),
269                                 logLength > 0 ? &logBuffer[0] : "(unknown error)");
270             CHECK_GL(glDeleteShader(shader));
271             shader = 0;
272         }
273         assert(shader);
274         return shader;
275     }
276 
277     // Returns a handle to the output program
CreateGlProgram(RendererDynamicRange dynamicRange)278     GLuint CreateGlProgram(RendererDynamicRange dynamicRange) {
279 
280         GLuint vertexShader = CompileShader(GL_VERTEX_SHADER,
281                                             dynamicRange != RENDERER_DYN_RNG_SDR
282                                             ? HDR_VERTEX_SHADER_SRC : VERTEX_SHADER_SRC);
283         assert(vertexShader);
284 
285         GLuint fragmentShader = CompileShader(GL_FRAGMENT_SHADER,
286                                               dynamicRange != RENDERER_DYN_RNG_SDR
287                                               ? HDR_FRAGMENT_SHADER_SRC : FRAGMENT_SHADER_SRC);
288         assert(fragmentShader);
289 
290         GLuint program = CHECK_GL(glCreateProgram());
291         assert(program);
292         CHECK_GL(glAttachShader(program, vertexShader));
293         CHECK_GL(glAttachShader(program, fragmentShader));
294         CHECK_GL(glLinkProgram(program));
295         GLint linkStatus = 0;
296         CHECK_GL(glGetProgramiv(program, GL_LINK_STATUS, &linkStatus));
297         if (!linkStatus) {
298             GLint logLength = 0;
299             CHECK_GL(glGetProgramiv(program, GL_INFO_LOG_LENGTH, &logLength));
300             std::vector<char> logBuffer(logLength);
301             if (logLength > 0) {
302                 CHECK_GL(glGetProgramInfoLog(program, logLength, /*length=*/nullptr,
303                                              &logBuffer[0]));
304             }
305             __android_log_print(ANDROID_LOG_ERROR, LOG_TAG,
306                                 "Unable to link program:\n %s.",
307                                 logLength > 0 ? &logBuffer[0] : "(unknown error)");
308             CHECK_GL(glDeleteProgram(program));
309             program = 0;
310         }
311         assert(program);
312         return program;
313     }
314 
DestroySurface(NativeContext * nativeContext)315     void DestroySurface(NativeContext *nativeContext) {
316         if (nativeContext->windowSurface.first) {
317             eglMakeCurrent(nativeContext->display, nativeContext->pbufferSurface,
318                            nativeContext->pbufferSurface, nativeContext->context);
319             eglDestroySurface(nativeContext->display,
320                               nativeContext->windowSurface.second);
321             nativeContext->windowSurface.second = nullptr;
322             ANativeWindow_release(nativeContext->windowSurface.first);
323             nativeContext->windowSurface.first = nullptr;
324         }
325     }
326 
ThrowException(JNIEnv * env,const char * exceptionName,const char * msg)327     void ThrowException(JNIEnv *env, const char *exceptionName, const char *msg) {
328         jclass exClass = env->FindClass(exceptionName);
329         assert(exClass != nullptr);
330 
331         [[maybe_unused]] jint throwSuccess = env->ThrowNew(exClass, msg);
332         assert(throwSuccess == JNI_OK);
333     }
334 
InitContext(JNIEnv * env,NativeContext * nativeContext,RendererDynamicRange dynamicRange,int bitDepth)335     bool InitContext(JNIEnv *env, NativeContext *nativeContext,
336                       RendererDynamicRange dynamicRange,
337                       int bitDepth) {
338         EGLDisplay eglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
339         assert(eglDisplay != EGL_NO_DISPLAY);
340 
341         nativeContext->display = eglDisplay;
342 
343         EGLint majorVer;
344         EGLint minorVer;
345         EGLBoolean initSuccess = eglInitialize(eglDisplay, &majorVer, &minorVer);
346         if (initSuccess != EGL_TRUE) {
347             ThrowException(env, "java/lang/RuntimeException",
348                            "EGL Error: eglInitialize failed.");
349             return false;
350         }
351 
352         // Print debug EGL information
353         const char *eglVendorString = eglQueryString(eglDisplay, EGL_VENDOR);
354         const char *eglVersionString = eglQueryString(eglDisplay, EGL_VERSION);
355         __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, "EGL Initialized [Vendor: %s, Version: %s]",
356                             eglVendorString == nullptr ? "Unknown" : eglVendorString,
357                             eglVersionString == nullptr
358                             ? "Unknown" : eglVersionString);
359 
360         int renderType = dynamicRange != RENDERER_DYN_RNG_SDR
361                 ? EGL_OPENGL_ES3_BIT : EGL_OPENGL_ES2_BIT;
362 
363         // TODO(b/319277249): It will crash on older Samsung devices for HDR video 10-bi
364         //  because EGLExt.EGL_RECORDABLE_ANDROID is only supported from OneUI 6.1. We need to
365         //  check by GPU Driver version when new OS is release.
366         int recordableAndroid = dynamicRange != RENDERER_DYN_RNG_SDR
367                                 ? EGL_DONT_CARE : EGL_TRUE;
368         int configAttribs[] = { EGL_RED_SIZE, bitDepth,
369                                 EGL_GREEN_SIZE, bitDepth,
370                                 EGL_BLUE_SIZE, bitDepth,
371                                 EGL_ALPHA_SIZE, 32 - (bitDepth * 3),
372                                 EGL_DEPTH_SIZE, 0,
373                                 EGL_STENCIL_SIZE, 0,
374                                 EGL_RENDERABLE_TYPE,renderType,
375                                 EGL_SURFACE_TYPE,EGL_WINDOW_BIT | EGL_PBUFFER_BIT,
376                                 EGL_RECORDABLE_ANDROID,recordableAndroid,
377                                 EGL_NONE};
378         EGLConfig eglConfig;
379         EGLint numConfigs;
380         EGLint configSize = 1;
381         EGLBoolean chooseConfigSuccess =
382                 eglChooseConfig(eglDisplay, static_cast<EGLint *>(configAttribs), &eglConfig,
383                                 configSize, &numConfigs);
384         if (chooseConfigSuccess != EGL_TRUE) {
385             ThrowException(env, "java/lang/IllegalArgumentException",
386                            "EGL Error: eglChooseConfig failed. ");
387             return false;
388         }
389 
390         assert(numConfigs > 0);
391 
392         nativeContext->config = eglConfig;
393 
394         int clientVer = dynamicRange != RENDERER_DYN_RNG_SDR ? 3 : 2;
395         int contextAttribs[] = {EGL_CONTEXT_CLIENT_VERSION, clientVer, EGL_NONE};
396         EGLContext eglContext = eglCreateContext(
397                 eglDisplay, eglConfig, EGL_NO_CONTEXT, static_cast<EGLint *>(contextAttribs));
398         assert(eglContext != EGL_NO_CONTEXT);
399 
400         nativeContext->context = eglContext;
401 
402         // Create 1x1 pixmap to use as a surface until one is set.
403         int pbufferAttribs[] = {EGL_WIDTH, 1, EGL_HEIGHT, 1, EGL_NONE};
404         EGLSurface eglPbuffer =
405                 eglCreatePbufferSurface(eglDisplay, eglConfig, pbufferAttribs);
406         assert(eglPbuffer != EGL_NO_SURFACE);
407 
408         nativeContext->pbufferSurface = eglPbuffer;
409 
410         eglMakeCurrent(eglDisplay, eglPbuffer, eglPbuffer, eglContext);
411 
412         //Print debug OpenGL information
413         const GLubyte *glVendorString = CHECK_GL(glGetString(GL_VENDOR));
414         const GLubyte *glVersionString = CHECK_GL(glGetString(GL_VERSION));
415         const GLubyte *glslVersionString = CHECK_GL(glGetString(GL_SHADING_LANGUAGE_VERSION));
416         const GLubyte *glRendererString = CHECK_GL(glGetString(GL_RENDERER));
417         __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, "OpenGL Initialized [Vendor: %s, "
418                                                         "Version: %s, GLSL Version: %s, Renderer: "
419                                                         "%s]",
420                             glVendorString == nullptr ? "Unknown" : (const char *) glVendorString,
421                             glVersionString == nullptr ? "Unknown" : (const char *) glVersionString,
422                             glslVersionString == nullptr ? "Unknown"
423                                                          : (const char *) glslVersionString,
424                             glRendererString == nullptr ? "Unknown"
425                                                         : (const char *) glRendererString);
426 
427         // Check for YUV target extension
428         const char *glExtensions =
429                 reinterpret_cast<const char*>(CHECK_GL(glGetString(GL_EXTENSIONS)));
430         bool hasYuvExtension = (strstr(glExtensions, "GL_EXT_YUV_target") != nullptr);
431 
432         // Check if OpenGL version is ES3.0 or greater
433         GLuint major, minor;
434         sscanf(reinterpret_cast<const char*>(glVersionString), "%d.%d", &major, &minor);
435         GLuint verNum = (major * 100 + minor * 10);
436 
437         nativeContext->supportsHdr = hasYuvExtension && verNum >= 300;
438 
439         nativeContext->program = CreateGlProgram(dynamicRange);
440         assert(nativeContext->program);
441 
442         nativeContext->positionHandle =
443                 CHECK_GL(glGetAttribLocation(nativeContext->program, "position"));
444         assert(nativeContext->positionHandle != -1);
445 
446         nativeContext->texCoordsHandle =
447                 CHECK_GL(glGetAttribLocation(nativeContext->program, "texCoords"));
448         assert(nativeContext->texCoordsHandle != -1);
449 
450         nativeContext->samplerHandle =
451                 CHECK_GL(glGetUniformLocation(nativeContext->program, "sampler"));
452         assert(nativeContext->samplerHandle != -1);
453 
454         nativeContext->mvpTransformHandle =
455                 CHECK_GL(glGetUniformLocation(nativeContext->program, "mvpTransform"));
456         assert(nativeContext->mvpTransformHandle != -1);
457 
458         nativeContext->texTransformHandle =
459                 CHECK_GL(glGetUniformLocation(nativeContext->program, "texTransform"));
460         assert(nativeContext->texTransformHandle != -1);
461 
462         CHECK_GL(glGenTextures(1, &(nativeContext->textureId)));
463 
464         return nativeContext;
465     }
466 
ConnectOutputSurface(NativeContext * nativeContext,ANativeWindow * nativeWindow,RendererDynamicRange dynamicRange)467     void ConnectOutputSurface(NativeContext *nativeContext, ANativeWindow *nativeWindow,
468                               RendererDynamicRange dynamicRange) {
469         std::vector<GLint> surfaceAttribs;
470         const char* eglExtensions = eglQueryString(nativeContext->display, EGL_EXTENSIONS);
471         if (dynamicRange == RENDERER_DYN_RNG_HDR_HLG) {
472             if (strstr(eglExtensions, "EGL_EXT_gl_colorspace_bt2020_hlg") != nullptr) {
473                 surfaceAttribs.push_back(EGL_GL_COLORSPACE);
474                 surfaceAttribs.push_back(EGL_GL_COLORSPACE_BT2020_HLG_EXT);
475             } else {
476                 __android_log_print(ANDROID_LOG_WARN, LOG_TAG,
477                                     "Dynamic range uses HLG encoding, but device does not "
478                                     "support EGL_EXT_gl_colorspace_bt2020_hlg. Fallback to default "
479                                     "colorspace.");
480             }
481             // TODO(b/303675500): Add path for PQ (EGL_EXT_gl_colorspace_bt2020_pq) output for
482             //  HDR10/HDR10+
483         }
484         surfaceAttribs.push_back(EGL_NONE);
485 
486         EGLSurface surface =
487                 eglCreateWindowSurface(nativeContext->display, nativeContext->config,
488                                        nativeWindow, &surfaceAttribs[0]);
489         assert(surface != EGL_NO_SURFACE);
490 
491         nativeContext->windowSurface = std::make_pair(nativeWindow, surface);
492 
493         eglMakeCurrent(nativeContext->display, surface, surface,
494                        nativeContext->context);
495 
496         CHECK_GL(glViewport(0, 0, ANativeWindow_getWidth(nativeWindow),
497                             ANativeWindow_getHeight(nativeWindow)));
498         CHECK_GL(glScissor(0, 0, ANativeWindow_getWidth(nativeWindow),
499                            ANativeWindow_getHeight(nativeWindow)));
500     }
501 
ClearContext(NativeContext * nativeContext)502     void ClearContext(NativeContext *nativeContext) {
503         if (nativeContext->program) {
504             CHECK_GL(glDeleteProgram(nativeContext->program));
505             nativeContext->program = 0;
506         }
507 
508         DestroySurface(nativeContext);
509 
510         if (nativeContext->pbufferSurface != EGL_NO_SURFACE) {
511             eglDestroySurface(nativeContext->display, nativeContext->pbufferSurface);
512             nativeContext->pbufferSurface = EGL_NO_SURFACE;
513         }
514 
515         if (nativeContext->display != EGL_NO_DISPLAY) {
516             eglMakeCurrent(nativeContext->display, EGL_NO_SURFACE, EGL_NO_SURFACE,
517                            EGL_NO_CONTEXT);
518         }
519 
520         if (nativeContext->context != EGL_NO_CONTEXT) {
521             eglDestroyContext(nativeContext->display, nativeContext->context);
522             nativeContext->context = EGL_NO_CONTEXT;
523         }
524 
525         if (nativeContext->display != EGL_NO_DISPLAY) {
526             eglTerminate(nativeContext->display);
527             nativeContext->display = EGL_NO_DISPLAY;
528         }
529     }
530 
531 }  // namespace
532 
533 extern "C" {
534 JNIEXPORT jlong JNICALL
Java_androidx_camera_integration_core_OpenGLRenderer_initContext(JNIEnv * env,jclass clazz)535 Java_androidx_camera_integration_core_OpenGLRenderer_initContext(
536         JNIEnv *env, jclass clazz) {
537 
538     auto *nativeContext = new NativeContext();
539     if (InitContext(env, nativeContext, RENDERER_DYN_RNG_SDR, /*bitDepth=*/8)) {
540         return reinterpret_cast<jlong>(nativeContext);
541     } else {
542         return 0;
543     }
544 }
545 
546 JNIEXPORT jboolean JNICALL
Java_androidx_camera_integration_core_OpenGLRenderer_setWindowSurface(JNIEnv * env,jclass clazz,jlong context,jobject jsurface,jint jdynamicRange)547 Java_androidx_camera_integration_core_OpenGLRenderer_setWindowSurface(
548         JNIEnv *env, jclass clazz, jlong context, jobject jsurface,
549         jint jdynamicRange) {
550     auto *nativeContext = reinterpret_cast<NativeContext *>(context);
551     auto dynamicRange = static_cast<RendererDynamicRange>(jdynamicRange);
552 
553     // Destroy previously connected surface
554     DestroySurface(nativeContext);
555 
556     // Null surface may have just been passed in to destroy previous surface.
557     if (!jsurface) {
558         return JNI_FALSE;
559     }
560 
561     ANativeWindow *nativeWindow = ANativeWindow_fromSurface(env, jsurface);
562     if (nativeWindow == nullptr) {
563         __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, "Failed to set window surface: Unable to "
564                                                         "acquire native window.");
565         return JNI_FALSE;
566     }
567 
568     ConnectOutputSurface(nativeContext, nativeWindow, dynamicRange);
569 
570     return JNI_TRUE;
571 }
572 
573 JNIEXPORT jint JNICALL
Java_androidx_camera_integration_core_OpenGLRenderer_getTexName(JNIEnv * env,jclass clazz,jlong context)574 Java_androidx_camera_integration_core_OpenGLRenderer_getTexName(
575         JNIEnv *env, jclass clazz, jlong context) {
576     auto *nativeContext = reinterpret_cast<NativeContext *>(context);
577     return nativeContext->textureId;
578 }
579 
580 JNIEXPORT jboolean JNICALL
Java_androidx_camera_integration_core_OpenGLRenderer_renderTexture(JNIEnv * env,jclass clazz,jlong context,jlong timestampNs,jfloatArray jmvpTransformArray,jboolean mvpDirty,jfloatArray jtexTransformArray)581 Java_androidx_camera_integration_core_OpenGLRenderer_renderTexture(
582         JNIEnv *env, jclass clazz, jlong context, jlong timestampNs,
583         jfloatArray jmvpTransformArray, jboolean mvpDirty, jfloatArray jtexTransformArray) {
584     auto *nativeContext = reinterpret_cast<NativeContext *>(context);
585 
586     // We use two triangles drawn with GL_TRIANGLE_STRIP to create the surface which will be
587     // textured with the camera frame. This could also be done with a quad (GL_QUADS) on a
588     // different version of OpenGL or with a scaled single triangle in which we would inscribe
589     // the camera texture.
590     //
591     //                       (-1,-1)         (1,-1)
592     //                          +---------------+
593     //                          | \_            |
594     //                          |    \_         |
595     //                          |       +       |
596     //                          |         \_    |
597     //                          |            \_ |
598     //                          +---------------+
599     //                       (-1,1)           (1,1)
600     constexpr GLfloat vertices[] = {
601             -1.0f, 1.0f, // Lower-left
602             1.0f, 1.0f, // Lower-right
603             -1.0f, -1.0f, // Upper-left (notice order here. We're drawing triangles, not a quad.)
604             1.0f, -1.0f  // Upper-right
605     };
606     constexpr GLfloat texCoords[] = {
607             0.0f, 0.0f, // Lower-left
608             1.0f, 0.0f, // Lower-right
609             0.0f, 1.0f, // Upper-left (order must match the vertices)
610             1.0f, 1.0f,  // Upper-right
611     };
612 
613     GLint vertexComponents = 2;
614     GLenum vertexType = GL_FLOAT;
615     GLboolean normalized = GL_FALSE;
616     GLsizei vertexStride = 0;
617     CHECK_GL(glVertexAttribPointer(nativeContext->positionHandle,
618                                    vertexComponents, vertexType, normalized,
619                                    vertexStride, vertices));
620     CHECK_GL(glEnableVertexAttribArray(nativeContext->positionHandle));
621 
622     CHECK_GL(glVertexAttribPointer(nativeContext->texCoordsHandle,
623                                    vertexComponents, vertexType, normalized,
624                                    vertexStride, texCoords));
625     CHECK_GL(glEnableVertexAttribArray(nativeContext->texCoordsHandle));
626 
627     CHECK_GL(glUseProgram(nativeContext->program));
628 
629     GLsizei numMatrices = 1;
630     GLboolean transpose = GL_FALSE;
631     // Only re-upload MVP to GPU if it is dirty
632     if (mvpDirty) {
633         GLfloat *mvpTransformArray =
634                 env->GetFloatArrayElements(jmvpTransformArray, nullptr);
635         CHECK_GL(glUniformMatrix4fv(nativeContext->mvpTransformHandle, numMatrices,
636                                     transpose, mvpTransformArray));
637         env->ReleaseFloatArrayElements(jmvpTransformArray, mvpTransformArray,
638                                        JNI_ABORT);
639     }
640 
641     CHECK_GL(glUniform1i(nativeContext->samplerHandle, 0));
642 
643     numMatrices = 1;
644     transpose = GL_FALSE;
645     GLfloat *texTransformArray =
646             env->GetFloatArrayElements(jtexTransformArray, nullptr);
647     CHECK_GL(glUniformMatrix4fv(nativeContext->texTransformHandle, numMatrices,
648                                 transpose, texTransformArray));
649     env->ReleaseFloatArrayElements(jtexTransformArray, texTransformArray,
650                                    JNI_ABORT);
651 
652     CHECK_GL(glBindTexture(GL_TEXTURE_EXTERNAL_OES, nativeContext->textureId));
653 
654     // Required to use a left-handed coordinate system in order to match our world-space
655     //
656     //                    ________+x
657     //                  /|
658     //                 / |
659     //              +z/  |
660     //                   | +y
661     //
662     glFrontFace(GL_CW);
663 
664     // This will typically fail if the EGL surface has been detached abnormally. In that case we
665     // will return JNI_FALSE below.
666     glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
667 
668     // Check that all GL operations completed successfully. If not, log an error and return.
669     GLenum glError = glGetError();
670     if (glError != GL_NO_ERROR) {
671         __android_log_print(ANDROID_LOG_ERROR, LOG_TAG,
672                             "Failed to draw frame due to OpenGL error: %s",
673                             GLErrorString(glError).c_str());
674         return JNI_FALSE;
675     }
676 
677 // Only attempt to set presentation time if EGL_EGLEXT_PROTOTYPES is defined.
678 // Otherwise, we'll ignore the timestamp.
679 #ifdef EGL_EGLEXT_PROTOTYPES
680     eglPresentationTimeANDROID(nativeContext->display,
681                                nativeContext->windowSurface.second, timestampNs);
682 #endif  // EGL_EGLEXT_PROTOTYPES
683     EGLBoolean swapped = eglSwapBuffers(nativeContext->display,
684                                         nativeContext->windowSurface.second);
685     if (!swapped) {
686         EGLenum eglError = eglGetError();
687         __android_log_print(ANDROID_LOG_ERROR, LOG_TAG,
688                             "Failed to swap buffers with EGL error: %s",
689                             EGLErrorString(eglError).c_str());
690         return JNI_FALSE;
691     }
692 
693     return JNI_TRUE;
694 }
695 
696 JNIEXPORT void JNICALL
Java_androidx_camera_integration_core_OpenGLRenderer_closeContext(JNIEnv * env,jclass clazz,jlong context)697 Java_androidx_camera_integration_core_OpenGLRenderer_closeContext(
698         JNIEnv *env, jclass clazz, jlong context) {
699     auto *nativeContext = reinterpret_cast<NativeContext *>(context);
700 
701     ClearContext(nativeContext);
702 
703     delete nativeContext;
704 }
705 
706 JNIEXPORT void JNICALL
Java_androidx_camera_integration_core_OpenGLRenderer_updateRenderedDynamicRange(JNIEnv * env,jclass clazz,jlong context,jint jdynamicRange,jint bitDepth)707 Java_androidx_camera_integration_core_OpenGLRenderer_updateRenderedDynamicRange(
708         JNIEnv *env, jclass clazz, jlong context, jint jdynamicRange, jint bitDepth) {
709     auto *nativeContext = reinterpret_cast<NativeContext *>(context);
710     auto dynamicRange = static_cast<RendererDynamicRange>(jdynamicRange);
711 
712     auto *nativeWindow = nativeContext->windowSurface.first;
713     if (nativeWindow != nullptr) {
714         ANativeWindow_acquire(nativeWindow);
715     }
716     ClearContext(nativeContext);
717 
718     InitContext(env, nativeContext, dynamicRange, bitDepth);
719     if (nativeWindow != nullptr) {
720         ConnectOutputSurface(nativeContext, nativeWindow, dynamicRange);
721     }
722 }
723 
724 JNIEXPORT jboolean JNICALL
Java_androidx_camera_integration_core_OpenGLRenderer_supportsHdr(JNIEnv * env,jclass clazz,jlong context)725 Java_androidx_camera_integration_core_OpenGLRenderer_supportsHdr(
726         JNIEnv *env, jclass clazz, jlong context) {
727     auto *nativeContext = reinterpret_cast<NativeContext *>(context);
728     return nativeContext->supportsHdr ? JNI_TRUE : JNI_TRUE;
729 }
730 }  // extern "C"
731 
732 #undef CHECK_GL
733