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