| /* |
| * Copyright (C) 2023 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #include <vector> |
| #include <math.h> |
| |
| #define GL_GLEXT_PROTOTYPES |
| #define EGL_EGLEXT_PROTOTYPES |
| #include <EGL/egl.h> |
| #include <EGL/eglext.h> |
| #include <GLES2/gl2.h> |
| #include <GLES2/gl2ext.h> |
| #undef EGL_EGLEXT_PROTOTYPES |
| #undef GL_GLEXT_PROTOTYPES |
| |
| #include "abc3d.h" |
| #include "debug.h" |
| |
| namespace android { |
| namespace hardware { |
| namespace camera { |
| namespace provider { |
| namespace implementation { |
| namespace abc3d { |
| namespace { |
| constexpr char kTag[] = "abc3d"; |
| |
| float dot3(const float a3[], const float b3[]) { |
| return a3[0] * b3[0] + a3[1] * b3[1] + a3[2] * b3[2]; |
| } |
| |
| /* |
| * https://registry.khronos.org/OpenGL-Refpages/gl2.1/xhtml/gluLookAt.xml |
| * https://registry.khronos.org/OpenGL-Refpages/gl2.1/xhtml/glTranslate.xml |
| * This function takes `m44` (where zzz (assumed zero) and ooo (assumed one) |
| * are ignored) and multiplies it by a translation matrix. |
| * |
| * m44 translate |
| * [ s0 s1 s2 zzz ] [ 1 0 0 -eyeX ] [ s0 s1 s2 -dot3(m44[0:2], eye3) ] |
| * [ up0 up1 up2 zzz ] * [ 0 1 0 -eyeY ] = [ up0 up1 up2 -dot3(m44[4:6], eye3) ] |
| * [ b0 b1 b2 zzz ] [ 0 0 1 -eyeZ ] [ b0 b1 b2 -dot3(m44[8:10], eye3) ] |
| * [ zzz zzz zzz ooo ] [ 0 0 0 1 ] [ 0 0 0 1 ] |
| */ |
| void lookAtEyeCoordinates(float m44[], const float eye3[]) { |
| m44[3] = -dot3(&m44[0], eye3); |
| m44[7] = -dot3(&m44[4], eye3); |
| m44[11] = -dot3(&m44[8], eye3); |
| m44[12] = 0; |
| m44[13] = 0; |
| m44[14] = 0; |
| m44[15] = 1; |
| } |
| } // namespace |
| |
| #define RETURN_CTOR_FAILED(S) \ |
| ALOGE("%s:%s:%d %s failed", kTag, __func__, __LINE__, S); return; |
| |
| AutoImageKHR::AutoImageKHR(const EGLDisplay display, const EGLClientBuffer clientBuf) |
| : mEglDisplay(display) { |
| static const EGLint imageAttrs[] = {EGL_IMAGE_PRESERVED_KHR, EGL_TRUE, EGL_NONE}; |
| mEglImage = eglCreateImageKHR( |
| display, EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID, clientBuf, imageAttrs); |
| if (mEglImage == EGL_NO_IMAGE_KHR) { |
| RETURN_CTOR_FAILED("eglCreateImageKHR"); |
| } |
| } |
| |
| AutoImageKHR::AutoImageKHR(AutoImageKHR&& rhs) noexcept |
| : mEglDisplay(rhs.mEglDisplay) |
| , mEglImage(std::exchange(rhs.mEglImage, EGL_NO_IMAGE_KHR)) {} |
| |
| AutoImageKHR& AutoImageKHR::operator=(AutoImageKHR&& rhs) noexcept { |
| if (this != &rhs) { |
| mEglDisplay = rhs.mEglDisplay; |
| mEglImage = std::exchange(rhs.mEglImage, EGL_NO_IMAGE_KHR); |
| } |
| return *this; |
| } |
| |
| AutoImageKHR::~AutoImageKHR() { |
| if (mEglImage != EGL_NO_IMAGE_KHR) { |
| eglDestroyImageKHR(mEglDisplay, mEglImage); |
| } |
| } |
| |
| EglCurrentContext::EglCurrentContext(const EGLDisplay display) |
| : mEglDisplay(display) {} |
| |
| EglCurrentContext::EglCurrentContext(EglCurrentContext&& rhs) noexcept |
| : mEglDisplay(std::exchange(rhs.mEglDisplay, EGL_NO_DISPLAY)) {} |
| |
| EglCurrentContext& EglCurrentContext::operator=(EglCurrentContext&& rhs) noexcept { |
| if (this != &rhs) { |
| mEglDisplay = std::exchange(rhs.mEglDisplay, EGL_NO_DISPLAY); |
| } |
| return *this; |
| } |
| |
| EglCurrentContext::~EglCurrentContext() { |
| if (mEglDisplay != EGL_NO_DISPLAY) { |
| LOG_ALWAYS_FATAL_IF(!eglMakeCurrent(mEglDisplay, EGL_NO_SURFACE, |
| EGL_NO_SURFACE, EGL_NO_CONTEXT)); |
| } |
| } |
| |
| EglContext::EglContext(EglContext&& rhs) noexcept |
| : mEglDisplay(std::exchange(rhs.mEglDisplay, EGL_NO_DISPLAY)) |
| , mEglContext(std::exchange(rhs.mEglContext, EGL_NO_CONTEXT)) |
| , mEglSurface(std::exchange(rhs.mEglSurface, EGL_NO_SURFACE)) {} |
| |
| EglContext& EglContext::operator=(EglContext&& rhs) noexcept { |
| if (this != &rhs) { |
| mEglDisplay = std::exchange(rhs.mEglDisplay, EGL_NO_DISPLAY); |
| mEglContext = std::exchange(rhs.mEglContext, EGL_NO_CONTEXT); |
| mEglSurface = std::exchange(rhs.mEglSurface, EGL_NO_SURFACE); |
| } |
| return *this; |
| } |
| |
| EglContext::~EglContext() { |
| clear(); |
| } |
| |
| void EglContext::clear() { |
| if (mEglSurface != EGL_NO_SURFACE) { |
| eglDestroySurface(mEglDisplay, mEglSurface); |
| mEglSurface = EGL_NO_SURFACE; |
| } |
| if (mEglContext != EGL_NO_CONTEXT) { |
| eglDestroyContext(mEglDisplay, mEglContext); |
| mEglContext = EGL_NO_CONTEXT; |
| } |
| if (mEglDisplay != EGL_NO_DISPLAY) { |
| eglTerminate(mEglDisplay); |
| mEglDisplay = EGL_NO_DISPLAY; |
| } |
| } |
| |
| EglCurrentContext EglContext::init() { |
| if (mEglContext != EGL_NO_CONTEXT) { |
| LOG_ALWAYS_FATAL_IF(!eglMakeCurrent(mEglDisplay, mEglSurface, |
| mEglSurface, mEglContext)); |
| return EglCurrentContext(mEglDisplay); |
| } |
| |
| const EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY); |
| if (display == EGL_NO_DISPLAY) { |
| return EglCurrentContext(FAILURE(EGL_NO_DISPLAY)); |
| } |
| |
| EGLint major, minor; |
| if (!eglInitialize(display, &major, &minor)) { |
| return EglCurrentContext(FAILURE(EGL_NO_DISPLAY)); |
| } |
| ALOGD("%s:%d: Initialized EGL, version %d.%d", __func__, __LINE__, |
| static_cast<int>(major), static_cast<int>(minor)); |
| |
| static const EGLint configAttrs[] = { |
| EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, |
| EGL_CONFIG_CAVEAT, EGL_NONE, |
| EGL_RED_SIZE, 8, |
| EGL_GREEN_SIZE, 8, |
| EGL_BLUE_SIZE, 8, |
| EGL_ALPHA_SIZE, 8, |
| EGL_NONE |
| }; |
| |
| EGLint numConfigs = 1; |
| EGLConfig config = EGL_NO_CONFIG_KHR; |
| if (!eglChooseConfig(display, configAttrs, &config, 1, &numConfigs) || |
| (config == EGL_NO_CONFIG_KHR) || (numConfigs != 1)) { |
| eglTerminate(display); |
| return EglCurrentContext(FAILURE(EGL_NO_DISPLAY)); |
| } |
| |
| static const EGLint contextAttrs[] = { |
| EGL_CONTEXT_CLIENT_VERSION, 2, |
| EGL_NONE |
| }; |
| const EGLContext context = eglCreateContext(display, config, |
| EGL_NO_CONTEXT, contextAttrs); |
| if (context == EGL_NO_CONTEXT) { |
| eglTerminate(display); |
| return EglCurrentContext(FAILURE(EGL_NO_DISPLAY)); |
| } |
| |
| EGLSurface surface = EGL_NO_SURFACE; |
| if (!eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, context)) { |
| // EGL_KHR_surfaceless_context is not supported |
| const EGLint surfaceAttrs[] = { |
| EGL_WIDTH, 1, |
| EGL_HEIGHT, 1, |
| EGL_NONE |
| }; |
| surface = eglCreatePbufferSurface(display, config, surfaceAttrs); |
| if (surface == EGL_NO_SURFACE) { |
| eglDestroyContext(display, context); |
| eglTerminate(display); |
| return EglCurrentContext(FAILURE(EGL_NO_DISPLAY)); |
| } |
| |
| if (!eglMakeCurrent(display, surface, surface, context)) { |
| eglDestroySurface(display, surface); |
| eglDestroyContext(display, context); |
| eglTerminate(display); |
| return EglCurrentContext(FAILURE(EGL_NO_DISPLAY)); |
| } |
| } |
| |
| mEglDisplay = display; |
| mEglContext = context; |
| mEglSurface = surface; |
| |
| return EglCurrentContext(display); |
| } |
| |
| EglCurrentContext EglContext::getCurrentContext() { |
| if (mEglContext == EGL_NO_CONTEXT) { |
| return EglCurrentContext(EGL_NO_DISPLAY); |
| } else if (eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext)) { |
| return EglCurrentContext(mEglDisplay); |
| } else { |
| return EglCurrentContext(FAILURE(EGL_NO_DISPLAY)); |
| } |
| } |
| |
| AutoTexture::AutoTexture(const GLenum target) { |
| glGenTextures(1, &mTex); |
| if (mTex) { |
| glBindTexture(target, mTex); |
| } else { |
| RETURN_CTOR_FAILED("glGenTextures"); |
| } |
| } |
| |
| AutoTexture::AutoTexture(const GLenum target, |
| const GLint internalformat, |
| const GLsizei width, |
| const GLsizei height, |
| const GLenum format, |
| const GLenum type, |
| const void* data) { |
| glGenTextures(1, &mTex); |
| if (mTex) { |
| glBindTexture(target, mTex); |
| glTexImage2D(target, 0, internalformat, width, height, 0, format, type, data); |
| } else { |
| RETURN_CTOR_FAILED("glGenTextures"); |
| } |
| } |
| |
| AutoTexture::AutoTexture(AutoTexture&& rhs) noexcept |
| : mTex(std::exchange(rhs.mTex, 0)) {} |
| |
| AutoTexture& AutoTexture::operator=(AutoTexture&& rhs) noexcept { |
| if (this != &rhs) { |
| mTex = std::exchange(rhs.mTex, 0); |
| } |
| return *this; |
| } |
| |
| AutoTexture::~AutoTexture() { |
| clear(); |
| } |
| |
| void AutoTexture::clear() { |
| if (mTex) { |
| glDeleteTextures(1, &mTex); |
| mTex = 0; |
| } |
| } |
| |
| AutoFrameBuffer::AutoFrameBuffer() { |
| glGenFramebuffers(1, &mFBO); |
| if (mFBO) { |
| glBindFramebuffer(GL_FRAMEBUFFER, mFBO); |
| } else { |
| RETURN_CTOR_FAILED("glGenFramebuffers"); |
| } |
| } |
| |
| AutoFrameBuffer::~AutoFrameBuffer() { |
| if (mFBO) { |
| glDeleteFramebuffers(1, &mFBO); |
| } |
| } |
| |
| AutoShader::AutoShader(AutoShader&& rhs) noexcept |
| : mShader(std::exchange(rhs.mShader, 0)) {} |
| |
| AutoShader& AutoShader::operator=(AutoShader&& rhs) noexcept { |
| if (this != &rhs) { |
| mShader = std::exchange(rhs.mShader, 0); |
| } |
| return *this; |
| } |
| |
| AutoShader::~AutoShader() { |
| if (mShader) { |
| glDeleteShader(mShader); |
| } |
| } |
| |
| GLuint AutoShader::compile(const GLenum type, const char* text) { |
| const GLuint shader = glCreateShader(type); |
| if (!shader) { |
| return FAILURE(0); |
| } |
| |
| glShaderSource(shader, 1, &text, nullptr); |
| glCompileShader(shader); |
| GLint compiled = 0; |
| glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled); |
| if (!compiled) { |
| GLint infoLen = 0; |
| glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLen); |
| if(infoLen > 1) { |
| std::vector<char> msg(infoLen + 1); |
| glGetShaderInfoLog(shader, infoLen, nullptr, msg.data()); |
| msg[infoLen] = 0; |
| ALOGE("%s:%d: error compiling shader '%s' (type=%d): '%s'", |
| __func__, __LINE__, text, type, msg.data()); |
| } |
| glDeleteShader(shader); |
| return FAILURE(0); |
| } |
| |
| if (mShader) { |
| glDeleteShader(mShader); |
| } |
| |
| mShader = shader; |
| return shader; |
| } |
| |
| AutoProgram::AutoProgram(AutoProgram&& rhs) noexcept |
| : mProgram(std::exchange(rhs.mProgram, 0)) {} |
| |
| AutoProgram& AutoProgram::operator=(AutoProgram&& rhs) noexcept { |
| if (this != &rhs) { |
| mProgram = std::exchange(rhs.mProgram, 0); |
| } |
| return *this; |
| } |
| |
| AutoProgram::~AutoProgram() { |
| clear(); |
| } |
| |
| void AutoProgram::clear() { |
| if (mProgram) { |
| glDeleteProgram(mProgram); |
| mProgram = 0; |
| } |
| } |
| |
| bool AutoProgram::link(const GLuint vertexShader, |
| const GLuint fragmentShader) { |
| const GLuint program = glCreateProgram(); |
| if (!program) { |
| return FAILURE(false); |
| } |
| |
| glAttachShader(program, vertexShader); |
| glAttachShader(program, fragmentShader); |
| glLinkProgram(program); |
| |
| GLint linked = 0; |
| glGetProgramiv(program, GL_LINK_STATUS, &linked); |
| if (!linked) { |
| GLint infoLen = 0; |
| glGetProgramiv(program, GL_INFO_LOG_LENGTH, &infoLen); |
| if(infoLen > 1) { |
| std::vector<char> msg(infoLen + 1); |
| glGetProgramInfoLog(program, infoLen, nullptr, msg.data()); |
| msg[infoLen] = 0; |
| ALOGE("%s:%d: error linking shaders: '%s'", |
| __func__, __LINE__, msg.data()); |
| } |
| |
| glDeleteProgram(program); |
| return FAILURE(false); |
| } |
| |
| if (mProgram) { |
| glDeleteProgram(mProgram); |
| } |
| |
| mProgram = program; |
| return true; |
| } |
| |
| GLint AutoProgram::getAttribLocation(const char* name) const { |
| if (mProgram > 0) { |
| const GLint result = glGetAttribLocation(mProgram, name); |
| return (result >= 0) ? result : FAILURE(-1); |
| } else { |
| return FAILURE(-1); |
| } |
| } |
| |
| GLint AutoProgram::getUniformLocation(const char* name) const { |
| if (mProgram > 0) { |
| const GLint result = glGetUniformLocation(mProgram, name); |
| return (result >= 0) ? result : FAILURE(-1); |
| } else { |
| return FAILURE(-1); |
| } |
| } |
| |
| // https://registry.khronos.org/OpenGL-Refpages/gl2.1/xhtml/glFrustum.xml |
| void frustum(float m44[], |
| const double left, const double right, |
| const double bottom, const double top, |
| const double near, const double far) { |
| const double invWidth = 1.0 / (right - left); |
| const double invHeight = 1.0 / (top - bottom); |
| const double invDepth = 1.0 / (far - near); |
| const double near2 = 2 * near; |
| |
| m44[0] = near2 * invWidth; |
| m44[1] = 0; |
| m44[2] = (right + left) * invWidth; |
| m44[3] = 0; |
| |
| m44[4] = 0; |
| m44[5] = near2 * invHeight; |
| m44[6] = (top + bottom) * invHeight; |
| m44[7] = 0; |
| |
| m44[8] = 0; |
| m44[9] = 0; |
| m44[10] = -(far + near) * invDepth; |
| m44[11] = -far * near2 * invDepth; |
| |
| m44[12] = 0; |
| m44[13] = 0; |
| m44[14] = -1; |
| m44[15] = 0; |
| } |
| |
| /* |
| * https://registry.khronos.org/OpenGL-Refpages/gl2.1/xhtml/gluLookAt.xml |
| * https://en.wikipedia.org/wiki/Rotation_matrix#Basic_rotations |
| * |
| * Here we calculate {Side, Up, Backwards} from Euler angles in the XYZ order: |
| * |
| * [ 1, 0, 0 ] [ cosY, 0, sinY ] [ cosZ, -sinZ, 0 ] [ sx, ux, bx ] |
| * [ 0, cosX, -sinX ] * [ 0, 1, 0 ] * [ sinZ, cosZ, 0 ] = [ sy, uy, by ] |
| * [ 0, sinX, cosX ] [ -sinY, 0, cosY ] [ 0, 0, 1 ] [ sz, uz, bz ] |
| * |
| * We calculate `backwards` because the camera looks into the negative Z |
| * direction, so instead of calculating camera's forward and negating it twice, |
| * let's call it `backwards`. |
| * |
| * After multiplying the first two: |
| * [ cosY, 0, sinY ] |
| * [ sinX * sinY, cosX, -sinX * cosY ] |
| * [ -cosX * sinY, sinX, cosX * cosY ] |
| * |
| * The final result: |
| * [ cosY * cosZ, -cosY * sinZ, sinY ] |
| * [ sinX * sinY * cosZ + cosX * sinZ, -sinX * sinY * sinZ + cosX * cosZ, -sinX * cosY ] |
| * [ -cosX * sinY * cosZ + sinX * sinZ, cosX * sinY * sinZ + sinX * cosZ, cosX * cosY ] |
| * |
| * {Side, Up, Backwards} are the columns in the matrix above. |
| */ |
| void lookAtXyzRot(float m44[], const float eye3[], const float rot3[]) { |
| const double sinX = sin(rot3[0]); |
| const double cosX = cos(rot3[0]); |
| const double sinY = sin(rot3[1]); |
| const double cosY = cos(rot3[1]); |
| const double sinZ = sin(rot3[2]); |
| const double cosZ = cos(rot3[2]); |
| |
| m44[0] = cosY * cosZ; |
| m44[1] = sinX * sinY * cosZ + cosX * sinZ; |
| m44[2] = -cosX * sinY * cosZ + sinX * sinZ; |
| m44[4] = -cosY * sinZ; |
| m44[5] = -sinX * sinY * sinZ + cosX * cosZ; |
| m44[6] = cosX * sinY * sinZ + sinX * cosZ; |
| m44[8] = sinY; |
| m44[9] = -sinX * cosY; |
| m44[10] = cosX * cosY; |
| lookAtEyeCoordinates(m44, eye3); |
| } |
| |
| void mulM44(float m44[], const float lhs44[], const float rhs44[]) { |
| m44[0] = lhs44[0] * rhs44[0] + lhs44[1] * rhs44[4] + lhs44[2] * rhs44[8] + lhs44[3] * rhs44[12]; |
| m44[1] = lhs44[0] * rhs44[1] + lhs44[1] * rhs44[5] + lhs44[2] * rhs44[9] + lhs44[3] * rhs44[13]; |
| m44[2] = lhs44[0] * rhs44[2] + lhs44[1] * rhs44[6] + lhs44[2] * rhs44[10] + lhs44[3] * rhs44[14]; |
| m44[3] = lhs44[0] * rhs44[3] + lhs44[1] * rhs44[7] + lhs44[2] * rhs44[11] + lhs44[3] * rhs44[15]; |
| |
| m44[4] = lhs44[4] * rhs44[0] + lhs44[5] * rhs44[4] + lhs44[6] * rhs44[8] + lhs44[7] * rhs44[12]; |
| m44[5] = lhs44[4] * rhs44[1] + lhs44[5] * rhs44[5] + lhs44[6] * rhs44[9] + lhs44[7] * rhs44[13]; |
| m44[6] = lhs44[4] * rhs44[2] + lhs44[5] * rhs44[6] + lhs44[6] * rhs44[10] + lhs44[7] * rhs44[14]; |
| m44[7] = lhs44[4] * rhs44[3] + lhs44[5] * rhs44[7] + lhs44[6] * rhs44[11] + lhs44[7] * rhs44[15]; |
| |
| m44[8] = lhs44[8] * rhs44[0] + lhs44[9] * rhs44[4] + lhs44[10] * rhs44[8] + lhs44[11] * rhs44[12]; |
| m44[9] = lhs44[8] * rhs44[1] + lhs44[9] * rhs44[5] + lhs44[10] * rhs44[9] + lhs44[11] * rhs44[13]; |
| m44[10] = lhs44[8] * rhs44[2] + lhs44[9] * rhs44[6] + lhs44[10] * rhs44[10] + lhs44[11] * rhs44[14]; |
| m44[11] = lhs44[8] * rhs44[3] + lhs44[9] * rhs44[7] + lhs44[10] * rhs44[11] + lhs44[11] * rhs44[15]; |
| |
| m44[12] = lhs44[12] * rhs44[0] + lhs44[13] * rhs44[4] + lhs44[14] * rhs44[8] + lhs44[15] * rhs44[12]; |
| m44[13] = lhs44[12] * rhs44[1] + lhs44[13] * rhs44[5] + lhs44[14] * rhs44[9] + lhs44[15] * rhs44[13]; |
| m44[14] = lhs44[12] * rhs44[2] + lhs44[13] * rhs44[6] + lhs44[14] * rhs44[10] + lhs44[15] * rhs44[14]; |
| m44[15] = lhs44[12] * rhs44[3] + lhs44[13] * rhs44[7] + lhs44[14] * rhs44[11] + lhs44[15] * rhs44[15]; |
| } |
| |
| } // namespace abc3d |
| } // namespace implementation |
| } // namespace provider |
| } // namespace camera |
| } // namespace hardware |
| } // namespace android |