Update a default HIDL EVS HAL implementation

This CL modifies a default implementation of HIDL EVS HAL v1.1 to
properly emulate IEvsCamera and generate a test pattern (SMPTE color
bars) on the cuttlefish.

Bug: 147743625
Test: launch_cvd --gpu_mode=gfxstream && atest VtsHalEvsV1_1TargetTest
Change-Id: I36b141c250efcc27e9a455d504fe897c69349ad9
diff --git a/automotive/evs/1.1/default/GlWrapper.cpp b/automotive/evs/1.1/default/GlWrapper.cpp
new file mode 100644
index 0000000..357b67c
--- /dev/null
+++ b/automotive/evs/1.1/default/GlWrapper.cpp
@@ -0,0 +1,450 @@
+/*
+ * Copyright (C) 2022 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 "GlWrapper.h"
+
+#include <ui/DisplayMode.h>
+#include <ui/DisplayState.h>
+#include <ui/GraphicBuffer.h>
+
+#include <fcntl.h>
+#include <stdio.h>
+#include <sys/ioctl.h>
+#include <utility>
+
+using android::GraphicBuffer;
+using android::sp;
+
+namespace {
+
+// Defines a default color to clear the screen in RGBA format
+constexpr float kDefaultColorInRgba[] = {0.1f, 0.5f, 0.1f, 1.0f};
+
+// Defines the size of the preview area relative to the entire display
+constexpr float kDisplayAreaRatio = 0.8f;
+
+constexpr const char vertexShaderSource[] =
+        ""
+        "#version 300 es                    \n"
+        "layout(location = 0) in vec4 pos;  \n"
+        "layout(location = 1) in vec2 tex;  \n"
+        "out vec2 uv;                       \n"
+        "void main()                        \n"
+        "{                                  \n"
+        "   gl_Position = pos;              \n"
+        "   uv = tex;                       \n"
+        "}                                  \n";
+
+constexpr const char pixelShaderSource[] =
+        "#version 300 es                    \n"
+        "precision mediump float;           \n"
+        "uniform sampler2D tex;             \n"
+        "in vec2 uv;                        \n"
+        "out vec4 color;                    \n"
+        "void main()                        \n"
+        "{                                  \n"
+        "    vec4 texel = texture(tex, uv); \n"
+        "    color = texel;                 \n"
+        "}                                  \n";
+
+const char* getEGLError(void) {
+    switch (eglGetError()) {
+        case EGL_SUCCESS:
+            return "EGL_SUCCESS";
+        case EGL_NOT_INITIALIZED:
+            return "EGL_NOT_INITIALIZED";
+        case EGL_BAD_ACCESS:
+            return "EGL_BAD_ACCESS";
+        case EGL_BAD_ALLOC:
+            return "EGL_BAD_ALLOC";
+        case EGL_BAD_ATTRIBUTE:
+            return "EGL_BAD_ATTRIBUTE";
+        case EGL_BAD_CONTEXT:
+            return "EGL_BAD_CONTEXT";
+        case EGL_BAD_CONFIG:
+            return "EGL_BAD_CONFIG";
+        case EGL_BAD_CURRENT_SURFACE:
+            return "EGL_BAD_CURRENT_SURFACE";
+        case EGL_BAD_DISPLAY:
+            return "EGL_BAD_DISPLAY";
+        case EGL_BAD_SURFACE:
+            return "EGL_BAD_SURFACE";
+        case EGL_BAD_MATCH:
+            return "EGL_BAD_MATCH";
+        case EGL_BAD_PARAMETER:
+            return "EGL_BAD_PARAMETER";
+        case EGL_BAD_NATIVE_PIXMAP:
+            return "EGL_BAD_NATIVE_PIXMAP";
+        case EGL_BAD_NATIVE_WINDOW:
+            return "EGL_BAD_NATIVE_WINDOW";
+        case EGL_CONTEXT_LOST:
+            return "EGL_CONTEXT_LOST";
+        default:
+            return "Unknown error";
+    }
+}
+
+// Given shader source, load and compile it
+GLuint loadShader(GLenum type, const char* shaderSrc) {
+    // Create the shader object
+    GLuint shader = glCreateShader(type);
+    if (shader == 0) {
+        LOG(ERROR) << "glCreateSharder() failed with error = " << glGetError();
+        return 0;
+    }
+
+    // Load and compile the shader
+    glShaderSource(shader, 1, &shaderSrc, nullptr);
+    glCompileShader(shader);
+
+    // Verify the compilation worked as expected
+    GLint compiled = 0;
+    glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled);
+    if (!compiled) {
+        LOG(ERROR) << "Error compiling shader";
+
+        GLint size = 0;
+        glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &size);
+        if (size > 0) {
+            // Get and report the error message
+            char infoLog[size];
+            glGetShaderInfoLog(shader, size, nullptr, infoLog);
+            LOG(ERROR) << "  msg:" << std::endl << infoLog;
+        }
+
+        glDeleteShader(shader);
+        return 0;
+    }
+
+    return shader;
+}
+
+// Create a program object given vertex and pixels shader source
+GLuint buildShaderProgram(const char* vtxSrc, const char* pxlSrc) {
+    GLuint program = glCreateProgram();
+    if (program == 0) {
+        LOG(ERROR) << "Failed to allocate program object";
+        return 0;
+    }
+
+    // Compile the shaders and bind them to this program
+    GLuint vertexShader = loadShader(GL_VERTEX_SHADER, vtxSrc);
+    if (vertexShader == 0) {
+        LOG(ERROR) << "Failed to load vertex shader";
+        glDeleteProgram(program);
+        return 0;
+    }
+    GLuint pixelShader = loadShader(GL_FRAGMENT_SHADER, pxlSrc);
+    if (pixelShader == 0) {
+        LOG(ERROR) << "Failed to load pixel shader";
+        glDeleteProgram(program);
+        glDeleteShader(vertexShader);
+        return 0;
+    }
+    glAttachShader(program, vertexShader);
+    glAttachShader(program, pixelShader);
+
+    // Link the program
+    glLinkProgram(program);
+    GLint linked = 0;
+    glGetProgramiv(program, GL_LINK_STATUS, &linked);
+    if (!linked) {
+        LOG(ERROR) << "Error linking program";
+        GLint size = 0;
+        glGetProgramiv(program, GL_INFO_LOG_LENGTH, &size);
+        if (size > 0) {
+            // Get and report the error message
+            char* infoLog = (char*)malloc(size);
+            glGetProgramInfoLog(program, size, nullptr, infoLog);
+            LOG(ERROR) << "  msg:  " << infoLog;
+            free(infoLog);
+        }
+
+        glDeleteProgram(program);
+        glDeleteShader(vertexShader);
+        glDeleteShader(pixelShader);
+        return 0;
+    }
+
+    return program;
+}
+
+}  // namespace
+
+namespace android::hardware::automotive::evs::V1_1::implementation {
+
+// Main entry point
+bool GlWrapper::initialize(const sp<IAutomotiveDisplayProxyService>& service, uint64_t displayId) {
+    LOG(DEBUG) << __FUNCTION__;
+
+    if (!service) {
+        LOG(WARNING) << "IAutomotiveDisplayProxyService is invalid.";
+        return false;
+    }
+
+    // We will use the first display in the list as the primary.
+    service->getDisplayInfo(displayId, [this](auto dpyConfig, auto dpyState) {
+        ui::DisplayMode* pConfig = reinterpret_cast<ui::DisplayMode*>(dpyConfig.data());
+        mWidth = pConfig->resolution.getWidth();
+        mHeight = pConfig->resolution.getHeight();
+
+        ui::DisplayState* pState = reinterpret_cast<ui::DisplayState*>(dpyState.data());
+        if (pState->orientation != ui::ROTATION_0 && pState->orientation != ui::ROTATION_180) {
+            // rotate
+            std::swap(mWidth, mHeight);
+        }
+
+        LOG(DEBUG) << "Display resolution is " << mWidth << " x " << mHeight;
+    });
+
+    mGfxBufferProducer = service->getIGraphicBufferProducer(displayId);
+    if (mGfxBufferProducer == nullptr) {
+        LOG(ERROR) << "Failed to get IGraphicBufferProducer from IAutomotiveDisplayProxyService.";
+        return false;
+    }
+
+    mSurfaceHolder = getSurfaceFromHGBP(mGfxBufferProducer);
+    if (mSurfaceHolder == nullptr) {
+        LOG(ERROR) << "Failed to get a Surface from HGBP.";
+        return false;
+    }
+
+    mWindow = getNativeWindow(mSurfaceHolder.get());
+    if (mWindow == nullptr) {
+        LOG(ERROR) << "Failed to get a native window from Surface.";
+        return false;
+    }
+
+    // Set up our OpenGL ES context associated with the default display
+    mDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
+    if (mDisplay == EGL_NO_DISPLAY) {
+        LOG(ERROR) << "Failed to get egl display";
+        return false;
+    }
+
+    EGLint major = 3;
+    EGLint minor = 0;
+    if (!eglInitialize(mDisplay, &major, &minor)) {
+        LOG(ERROR) << "Failed to initialize EGL: " << getEGLError();
+        return false;
+    }
+
+    const EGLint config_attribs[] = {
+            // Tag                  Value
+            EGL_RED_SIZE, 8, EGL_GREEN_SIZE, 8, EGL_BLUE_SIZE, 8, EGL_DEPTH_SIZE, 0, EGL_NONE};
+
+    // Pick the default configuration without constraints (is this good enough?)
+    EGLConfig egl_config = {0};
+    EGLint numConfigs = -1;
+    eglChooseConfig(mDisplay, config_attribs, &egl_config, 1, &numConfigs);
+    if (numConfigs != 1) {
+        LOG(ERROR) << "Didn't find a suitable format for our display window";
+        return false;
+    }
+
+    // Create the EGL render target surface
+    mSurface = eglCreateWindowSurface(mDisplay, egl_config, mWindow, nullptr);
+    if (mSurface == EGL_NO_SURFACE) {
+        LOG(ERROR) << "eglCreateWindowSurface failed: " << getEGLError();
+        ;
+        return false;
+    }
+
+    // Create the EGL context
+    // NOTE:  Our shader is (currently at least) written to require version 3, so this
+    //        is required.
+    const EGLint context_attribs[] = {EGL_CONTEXT_CLIENT_VERSION, 3, EGL_NONE};
+    mContext = eglCreateContext(mDisplay, egl_config, EGL_NO_CONTEXT, context_attribs);
+    if (mContext == EGL_NO_CONTEXT) {
+        LOG(ERROR) << "Failed to create OpenGL ES Context: " << getEGLError();
+        return false;
+    }
+
+    // Activate our render target for drawing
+    if (!eglMakeCurrent(mDisplay, mSurface, mSurface, mContext)) {
+        LOG(ERROR) << "Failed to make the OpenGL ES Context current: " << getEGLError();
+        return false;
+    }
+
+    // Create the shader program for our simple pipeline
+    mShaderProgram = buildShaderProgram(vertexShaderSource, pixelShaderSource);
+    if (!mShaderProgram) {
+        LOG(ERROR) << "Failed to build shader program: " << getEGLError();
+        return false;
+    }
+
+    // Create a GL texture that will eventually wrap our externally created texture surface(s)
+    glGenTextures(1, &mTextureMap);
+    if (mTextureMap <= 0) {
+        LOG(ERROR) << "Didn't get a texture handle allocated: " << getEGLError();
+        return false;
+    }
+
+    // Turn off mip-mapping for the created texture surface
+    // (the inbound camera imagery doesn't have MIPs)
+    glBindTexture(GL_TEXTURE_2D, mTextureMap);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+    glBindTexture(GL_TEXTURE_2D, 0);
+
+    return true;
+}
+
+void GlWrapper::shutdown() {
+    // Drop our device textures
+    if (mKHRimage != EGL_NO_IMAGE_KHR) {
+        eglDestroyImageKHR(mDisplay, mKHRimage);
+        mKHRimage = EGL_NO_IMAGE_KHR;
+    }
+
+    // Release all GL resources
+    eglMakeCurrent(mDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
+    eglDestroySurface(mDisplay, mSurface);
+    eglDestroyContext(mDisplay, mContext);
+    eglTerminate(mDisplay);
+    mSurface = EGL_NO_SURFACE;
+    mContext = EGL_NO_CONTEXT;
+    mDisplay = EGL_NO_DISPLAY;
+
+    // Release the window
+    mSurfaceHolder = nullptr;
+}
+
+void GlWrapper::showWindow(sp<IAutomotiveDisplayProxyService>& service, uint64_t id) {
+    if (service != nullptr) {
+        service->showWindow(id);
+    } else {
+        LOG(ERROR) << "IAutomotiveDisplayProxyService is not available.";
+    }
+}
+
+void GlWrapper::hideWindow(sp<IAutomotiveDisplayProxyService>& service, uint64_t id) {
+    if (service != nullptr) {
+        service->hideWindow(id);
+    } else {
+        LOG(ERROR) << "IAutomotiveDisplayProxyService is not available.";
+    }
+}
+
+bool GlWrapper::updateImageTexture(const V1_0::BufferDesc& buffer) {
+    BufferDesc newBuffer = {
+            .buffer =
+                    {
+                            .nativeHandle = buffer.memHandle,
+                    },
+            .pixelSize = buffer.pixelSize,
+            .bufferId = buffer.bufferId,
+    };
+    AHardwareBuffer_Desc* pDesc =
+            reinterpret_cast<AHardwareBuffer_Desc*>(&newBuffer.buffer.description);
+    *pDesc = {
+            .width = buffer.width,
+            .height = buffer.height,
+            .layers = 1,
+            .format = buffer.format,
+            .usage = buffer.usage,
+    };
+    return updateImageTexture(newBuffer);
+}
+
+bool GlWrapper::updateImageTexture(const BufferDesc& aFrame) {
+    // If we haven't done it yet, create an "image" object to wrap the gralloc buffer
+    if (mKHRimage == EGL_NO_IMAGE_KHR) {
+        // create a temporary GraphicBuffer to wrap the provided handle
+        const AHardwareBuffer_Desc* pDesc =
+                reinterpret_cast<const AHardwareBuffer_Desc*>(&aFrame.buffer.description);
+        sp<GraphicBuffer> pGfxBuffer = new GraphicBuffer(
+                pDesc->width, pDesc->height, pDesc->format, pDesc->layers, pDesc->usage,
+                pDesc->stride,
+                const_cast<native_handle_t*>(aFrame.buffer.nativeHandle.getNativeHandle()),
+                false /* keep ownership */
+        );
+        if (pGfxBuffer.get() == nullptr) {
+            LOG(ERROR) << "Failed to allocate GraphicBuffer to wrap our native handle";
+            return false;
+        }
+
+        // Get a GL compatible reference to the graphics buffer we've been given
+        EGLint eglImageAttributes[] = {EGL_IMAGE_PRESERVED_KHR, EGL_TRUE, EGL_NONE};
+        EGLClientBuffer cbuf = static_cast<EGLClientBuffer>(pGfxBuffer->getNativeBuffer());
+        mKHRimage = eglCreateImageKHR(mDisplay, EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID, cbuf,
+                                      eglImageAttributes);
+        if (mKHRimage == EGL_NO_IMAGE_KHR) {
+            LOG(ERROR) << "Error creating EGLImage: " << getEGLError();
+            return false;
+        }
+
+        // Update the texture handle we already created to refer to this gralloc buffer
+        glActiveTexture(GL_TEXTURE0);
+        glBindTexture(GL_TEXTURE_2D, mTextureMap);
+        glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, static_cast<GLeglImageOES>(mKHRimage));
+    }
+
+    return true;
+}
+
+void GlWrapper::renderImageToScreen() {
+    // Set the viewport
+    glViewport(0, 0, mWidth, mHeight);
+
+    // Clear the color buffer
+    glClearColor(kDefaultColorInRgba[0], kDefaultColorInRgba[1],
+                 kDefaultColorInRgba[2], kDefaultColorInRgba[3]);
+    glClear(GL_COLOR_BUFFER_BIT);
+
+    // Select our screen space simple texture shader
+    glUseProgram(mShaderProgram);
+
+    // Bind the texture and assign it to the shader's sampler
+    glActiveTexture(GL_TEXTURE0);
+    glBindTexture(GL_TEXTURE_2D, mTextureMap);
+    GLint sampler = glGetUniformLocation(mShaderProgram, "tex");
+    glUniform1i(sampler, 0);
+
+    // We want our image to show up opaque regardless of alpha values
+    glDisable(GL_BLEND);
+
+    // Draw a rectangle on the screen
+    GLfloat vertsCarPos[] = {
+            -kDisplayAreaRatio,  kDisplayAreaRatio, 0.0f,  // left top in window space
+             kDisplayAreaRatio,  kDisplayAreaRatio, 0.0f,  // right top
+            -kDisplayAreaRatio, -kDisplayAreaRatio, 0.0f,  // left bottom
+             kDisplayAreaRatio, -kDisplayAreaRatio, 0.0f   // right bottom
+    };
+
+    // NOTE:  We didn't flip the image in the texture, so V=0 is actually the top of the image
+    GLfloat vertsCarTex[] = {
+            0.0f, 0.0f,  // left top
+            1.0f, 0.0f,  // right top
+            0.0f, 1.0f,  // left bottom
+            1.0f, 1.0f   // right bottom
+    };
+    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, vertsCarPos);
+    glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, vertsCarTex);
+    glEnableVertexAttribArray(0);
+    glEnableVertexAttribArray(1);
+
+    glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
+
+    // Clean up and flip the rendered result to the front so it is visible
+    glDisableVertexAttribArray(0);
+    glDisableVertexAttribArray(1);
+
+    glFinish();
+
+    eglSwapBuffers(mDisplay, mSurface);
+}
+
+}  // namespace android::hardware::automotive::evs::V1_1::implementation