Remove test mode from VirtualCameraRenderThread.
Instead of rendering the input in the render thread of virtual camera
connect the test client with dedicated thread for input rendering
when creating a test camera.
Rendering input in test mode was slowing down the virtual camera render
thread, causing it to fail tests requiring high fps on some targets.
This cl also moves test pattern rendering to be done on GPU and cleans
up corresponding shader.
Bug: 338251124
Test: atest virtual_camera_tests
Test: manual with OpenCamera
Test: atest CtsVirtualDevicesCameraCtsTestCases
Change-Id: I4bcefd91701f2f9e195eb71c0189efbb674e58f8
diff --git a/services/camera/virtualcamera/util/EglDisplayContext.cc b/services/camera/virtualcamera/util/EglDisplayContext.cc
index 6d343a2..166ac75 100644
--- a/services/camera/virtualcamera/util/EglDisplayContext.cc
+++ b/services/camera/virtualcamera/util/EglDisplayContext.cc
@@ -30,8 +30,9 @@
namespace companion {
namespace virtualcamera {
-EglDisplayContext::EglDisplayContext()
+EglDisplayContext::EglDisplayContext(std::shared_ptr<ANativeWindow> nativeWindow)
: mEglDisplay(EGL_NO_DISPLAY),
+ mEglSurface(EGL_NO_SURFACE),
mEglContext(EGL_NO_CONTEXT),
mEglConfig(nullptr) {
EGLBoolean result;
@@ -52,8 +53,10 @@
EGLint numConfigs = 0;
EGLint configAttribs[] = {
- EGL_SURFACE_TYPE, EGL_PBUFFER_BIT, EGL_RENDERABLE_TYPE,
- EGL_OPENGL_ES2_BIT, EGL_RED_SIZE, 8, EGL_GREEN_SIZE, 8, EGL_BLUE_SIZE, 8,
+ EGL_SURFACE_TYPE,
+ nativeWindow == nullptr ? EGL_PBUFFER_BIT : EGL_WINDOW_BIT,
+ EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, EGL_RED_SIZE, 8, EGL_GREEN_SIZE,
+ 8, EGL_BLUE_SIZE, 8,
// no alpha
EGL_NONE};
@@ -72,6 +75,14 @@
return;
}
+ if (nativeWindow != nullptr) {
+ mEglSurface = eglCreateWindowSurface(mEglDisplay, mEglConfig,
+ nativeWindow.get(), NULL);
+ if (mEglSurface == EGL_NO_SURFACE) {
+ ALOGE("eglCreateWindowSurface error: %#x", eglGetError());
+ }
+ }
+
if (!makeCurrent()) {
ALOGE(
"Failed to set newly initialized EGLContext and EGLDisplay connection "
@@ -82,6 +93,9 @@
}
EglDisplayContext::~EglDisplayContext() {
+ if (mEglSurface != EGL_NO_SURFACE) {
+ eglDestroySurface(mEglDisplay, mEglSurface);
+ }
if (mEglDisplay != EGL_NO_DISPLAY) {
eglTerminate(mEglDisplay);
}
@@ -99,8 +113,14 @@
return mEglContext != EGL_NO_CONTEXT && mEglDisplay != EGL_NO_DISPLAY;
}
+void EglDisplayContext::swapBuffers() const {
+ if (mEglSurface != EGL_NO_SURFACE) {
+ eglSwapBuffers(mEglDisplay, mEglSurface);
+ }
+}
+
bool EglDisplayContext::makeCurrent() {
- if (!eglMakeCurrent(mEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, mEglContext)) {
+ if (!eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext)) {
ALOGE("eglMakeCurrent failed: %#x", eglGetError());
return false;
}
diff --git a/services/camera/virtualcamera/util/EglDisplayContext.h b/services/camera/virtualcamera/util/EglDisplayContext.h
index 402ca3c..6dc3080 100644
--- a/services/camera/virtualcamera/util/EglDisplayContext.h
+++ b/services/camera/virtualcamera/util/EglDisplayContext.h
@@ -17,7 +17,10 @@
#ifndef ANDROID_COMPANION_VIRTUALCAMERA_EGLDISPLAYCONTEXT_H
#define ANDROID_COMPANION_VIRTUALCAMERA_EGLDISPLAYCONTEXT_H
+#include <memory>
+
#include "EGL/egl.h"
+#include "system/window.h"
namespace android {
namespace companion {
@@ -30,7 +33,7 @@
// out of scope.
class EglDisplayContext {
public:
- EglDisplayContext();
+ EglDisplayContext(std::shared_ptr<ANativeWindow> nativeWindow = nullptr);
~EglDisplayContext();
// Sets EGLDisplay & EGLContext for current thread.
@@ -44,8 +47,13 @@
// EGLDisplay & EGLContext.
bool isInitialized() const;
+ void swapBuffers() const;
+
private:
+ std::shared_ptr<ANativeWindow> mNativeWindow;
+
EGLDisplay mEglDisplay;
+ EGLSurface mEglSurface;
EGLContext mEglContext;
EGLConfig mEglConfig;
};
diff --git a/services/camera/virtualcamera/util/EglProgram.cc b/services/camera/virtualcamera/util/EglProgram.cc
index 7554a67..61f5005 100644
--- a/services/camera/virtualcamera/util/EglProgram.cc
+++ b/services/camera/virtualcamera/util/EglProgram.cc
@@ -35,19 +35,28 @@
constexpr char kGlExtYuvTarget[] = "GL_EXT_YUV_target";
-constexpr char kIdentityVertexShader[] = R"(
- attribute vec4 vPosition;
+constexpr char kJuliaFractalVertexShader[] = R"(#version 300 es
+ in vec4 aPosition;
+ in vec2 aTextureCoord;
+ out vec2 vFractalCoord;
+ out vec2 vUVCoord;
void main() {
- gl_Position = vPosition;
+ gl_Position = aPosition;
+ vUVCoord = aTextureCoord;
+ vFractalCoord = vec2(aTextureCoord.x - 0.5, aTextureCoord.y - 0.5) * 4.0;
})";
-constexpr char kJuliaFractalFragmentShader[] = R"(
+constexpr char kJuliaFractalFragmentShader[] = R"(#version 300 es
+ #extension GL_EXT_YUV_target : require
precision mediump float;
- uniform vec2 uResolution;
- uniform vec2 uC;
- uniform vec2 uUV;
+
const float kIter = 64.0;
+ in vec2 vFractalCoord;
+ in vec2 vUVCoord;
+ out vec4 fragColor;
+ uniform vec2 uC;
+
vec2 imSq(vec2 n){
return vec2(pow(n.x,2.0)-pow(n.y,2.0), 2.0*n.x*n.y);
}
@@ -62,9 +71,8 @@
}
void main() {
- vec2 uv = vec2(gl_FragCoord.x / uResolution.x - 0.5, gl_FragCoord.y / uResolution.y - 0.5);
- float juliaVal = julia(uv * 4.0, uC);
- gl_FragColor = vec4( juliaVal,uUV.x,uUV.y,0.0);
+ float juliaVal = julia(vFractalCoord, uC);
+ fragColor = vec4(yuv_2_rgb(vec3(juliaVal, vUVCoord.x, vUVCoord.y), itu_601_full_range), 0.0);
})";
constexpr char kExternalTextureVertexShader[] = R"(#version 300 es
@@ -200,17 +208,28 @@
}
EglTestPatternProgram::EglTestPatternProgram() {
- if (initialize(kIdentityVertexShader, kJuliaFractalFragmentShader)) {
+ if (initialize(kJuliaFractalVertexShader, kJuliaFractalFragmentShader)) {
ALOGV("Successfully initialized EGL shaders for test pattern program.");
} else {
ALOGE("Test pattern EGL shader program initialization failed.");
}
+
+ mCHandle = glGetUniformLocation(mProgram, "uC");
+ mPositionHandle = glGetAttribLocation(mProgram, "aPosition");
+ mTextureCoordHandle = glGetAttribLocation(mProgram, "aTextureCoord");
+
+ // Pass vertex array to draw.
+ glEnableVertexAttribArray(mPositionHandle);
+ // Prepare the triangle coordinate data.
+ glVertexAttribPointer(mPositionHandle, kCoordsPerVertex, GL_FLOAT, false,
+ kSquareCoords.size(), kSquareCoords.data());
+
+ glEnableVertexAttribArray(mTextureCoordHandle);
+ glVertexAttribPointer(mTextureCoordHandle, 2, GL_FLOAT, false,
+ kTextureCoords.size(), kTextureCoords.data());
}
-bool EglTestPatternProgram::draw(int width, int height, int frameNumber) {
- glViewport(0, 0, static_cast<GLsizei>(width), static_cast<GLsizei>(height));
- checkEglError("glViewport");
-
+bool EglTestPatternProgram::draw(int frameNumber) {
// Load compiled shader.
glUseProgram(mProgram);
checkEglError("glUseProgram");
@@ -219,28 +238,8 @@
float time = float(frameNumber) / 120.0f;
const std::complex<float> c(std::sin(time) * 0.78f, std::cos(time) * 0.78f);
- // Pass uniform values to the shader.
- int resolutionHandle = glGetUniformLocation(mProgram, "uResolution");
- checkEglError("glGetUniformLocation -> uResolution");
- glUniform2f(resolutionHandle, static_cast<float>(width),
- static_cast<float>(height));
- checkEglError("glUniform2f -> uResolution");
-
// Pass "C" constant value determining the Julia set to the shader.
- int cHandle = glGetUniformLocation(mProgram, "uC");
- glUniform2f(cHandle, c.imag(), c.real());
-
- // Pass chroma value to the shader.
- int uvHandle = glGetUniformLocation(mProgram, "uUV");
- glUniform2f(uvHandle, (c.imag() + 1.f) / 2.f, (c.real() + 1.f) / 2.f);
-
- // Pass vertex array to draw.
- int positionHandle = glGetAttribLocation(mProgram, "vPosition");
- glEnableVertexAttribArray(positionHandle);
-
- // Prepare the triangle coordinate data.
- glVertexAttribPointer(positionHandle, kCoordsPerVertex, GL_FLOAT, false,
- kSquareCoords.size(), kSquareCoords.data());
+ glUniform2f(mCHandle, c.imag(), c.real());
// Draw triangle strip forming a square filling the viewport.
glDrawElements(GL_TRIANGLES, kDrawOrder.size(), GL_UNSIGNED_BYTE,
diff --git a/services/camera/virtualcamera/util/EglProgram.h b/services/camera/virtualcamera/util/EglProgram.h
index c695cbb..d09f11e 100644
--- a/services/camera/virtualcamera/util/EglProgram.h
+++ b/services/camera/virtualcamera/util/EglProgram.h
@@ -46,7 +46,12 @@
public:
EglTestPatternProgram();
- bool draw(int width, int height, int frameNumber);
+ bool draw(int frameNumber);
+
+ private:
+ int mPositionHandle = -1;
+ int mTextureCoordHandle = -1;
+ int mCHandle = -1;
};
// Shader program to draw texture.
diff --git a/services/camera/virtualcamera/util/TestPatternHelper.cc b/services/camera/virtualcamera/util/TestPatternHelper.cc
deleted file mode 100644
index 274996a..0000000
--- a/services/camera/virtualcamera/util/TestPatternHelper.cc
+++ /dev/null
@@ -1,152 +0,0 @@
-/*
- * 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.
- */
-
-// #define LOG_NDEBUG 0
-
-#define LOG_TAG "TestPatternHelper"
-
-#include "TestPatternHelper.h"
-
-#include <complex>
-#include <cstdint>
-
-#include "log/log.h"
-#include "nativebase/nativebase.h"
-#include "system/graphics.h"
-#include "ui/GraphicBuffer.h"
-#include "utils/Errors.h"
-
-namespace android {
-namespace companion {
-namespace virtualcamera {
-
-namespace {
-
-using namespace std::chrono_literals;
-
-static constexpr std::chrono::milliseconds kAcquireFenceTimeout = 500ms;
-
-uint8_t julia(const std::complex<float> n, const std::complex<float> c) {
- std::complex<float> z = n;
- for (int i = 0; i < 64; i++) {
- z = z * z + c;
- if (std::abs(z) > 2.0) return i * 4;
- }
- return 0xff;
-}
-
-uint8_t pixelToFractal(const int x, const int y, const int width,
- const int height, const std::complex<float> c) {
- std::complex<float> n(float(x) / float(width) - 0.5,
- float(y) / float(height) - 0.5);
- return julia(n * 5.f, c);
-}
-
-void renderTestPatternYcbCr420(const android_ycbcr& ycbr, const int width,
- const int height, const int frameNumber) {
- float time = float(frameNumber) / 120.0f;
- const std::complex<float> c(std::sin(time), std::cos(time));
-
- uint8_t* y = reinterpret_cast<uint8_t*>(ycbr.y);
- uint8_t* cb = reinterpret_cast<uint8_t*>(ycbr.cb);
- uint8_t* cr = reinterpret_cast<uint8_t*>(ycbr.cr);
-
- for (int row = 0; row < height; row++) {
- for (int col = 0; col < width; col++) {
- y[row * ycbr.ystride + col] =
- pixelToFractal(col, row, width, height, c * 0.78f);
- }
- }
-
- int cWidth = width / 2;
- int cHeight = height / 2;
- for (int row = 0; row < cHeight; row++) {
- for (int col = 0; col < cWidth; col++) {
- cb[row * ycbr.cstride + col * ycbr.chroma_step] =
- static_cast<uint8_t>((float(col) / float(cWidth)) * 255.f);
- cr[row * ycbr.cstride + col * ycbr.chroma_step] =
- static_cast<uint8_t>((float(row) / float(cHeight)) * 255.f);
- }
- }
-}
-
-} // namespace
-
-void renderTestPatternYCbCr420(sp<Surface> surface, int frameNumber) {
- if (surface == nullptr) {
- ALOGE("%s: null surface, skipping render", __func__);
- return;
- }
-
- ANativeWindowBuffer* buffer;
- int fenceFd;
- int ret = ANativeWindow_dequeueBuffer(surface.get(), &buffer, &fenceFd);
- if (ret != NO_ERROR) {
- ALOGE(
- "%s: Error while deuqueing buffer from surface, "
- "ANativeWindow_dequeueBuffer returned %d",
- __func__, ret);
- return;
- }
-
- if (buffer == nullptr) {
- ALOGE("%s: ANativeWindowBuffer is null after dequeing", __func__);
- return;
- }
-
- sp<Fence> fence = sp<Fence>::make(fenceFd);
- if (fence->isValid()) {
- ret = fence->wait(kAcquireFenceTimeout.count());
- if (ret != NO_ERROR) {
- ALOGE("%s: Timeout while waiting for the fence to clear", __func__);
- ANativeWindow_queueBuffer(surface.get(), buffer, fence->dup());
- return;
- }
- }
-
- sp<GraphicBuffer> gBuffer = GraphicBuffer::from(buffer);
- android_ycbcr ycbr;
-
- ret = gBuffer->lockAsyncYCbCr(GraphicBuffer::USAGE_SW_WRITE_OFTEN, &ycbr,
- fence->dup());
- if (ret != NO_ERROR) {
- ALOGE("%s: Failed to lock buffer retrieved from surface, ret %d", __func__,
- ret);
- return;
- }
-
- renderTestPatternYcbCr420(ycbr, gBuffer->getWidth(), gBuffer->getHeight(),
- frameNumber);
-
- ret = gBuffer->unlock();
- if (ret != NO_ERROR) {
- ALOGE("%s: Failed to unlock buffer, ret %d", __func__, ret);
- return;
- }
-
- ret = ANativeWindow_queueBuffer(surface.get(), buffer, /*fenceFd=*/-1);
- if (ret != NO_ERROR) {
- ALOGE(
- "%s: Error while queing buffer to surface, ANativeWindow_queueBuffer "
- "returned %d",
- __func__, ret);
- return;
- }
-}
-
-} // namespace virtualcamera
-} // namespace companion
-} // namespace android
diff --git a/services/camera/virtualcamera/util/TestPatternHelper.h b/services/camera/virtualcamera/util/TestPatternHelper.h
deleted file mode 100644
index f842b29..0000000
--- a/services/camera/virtualcamera/util/TestPatternHelper.h
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * 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.
- */
-
-#ifndef ANDROID_COMPANION_VIRTUALCAMERA_TESTPATTERNHELPER_H
-#define ANDROID_COMPANION_VIRTUALCAMERA_TESTPATTERNHELPER_H
-
-#include "gui/Surface.h"
-
-namespace android {
-namespace companion {
-namespace virtualcamera {
-
-// Helper function for rendering test pattern into Surface.
-void renderTestPatternYCbCr420(sp<Surface> surface, int frameNumber);
-
-} // namespace virtualcamera
-} // namespace companion
-} // namespace android
-
-#endif // ANDROID_COMPANION_VIRTUALCAMERA_TESTPATTERNHELPER_H