Properly handle memory layout of input buffer for test camera

Bug: 301023410
Test: manually with OpenCamera
Test: atest virtual_camera_tests
Test: atest VirtualCameraTest

Change-Id: I32c0074f7d6cf2f098525e48686dafb0970d4298
diff --git a/services/camera/virtualcamera/VirtualCameraRenderThread.cc b/services/camera/virtualcamera/VirtualCameraRenderThread.cc
index 25cc270..50b5709 100644
--- a/services/camera/virtualcamera/VirtualCameraRenderThread.cc
+++ b/services/camera/virtualcamera/VirtualCameraRenderThread.cc
@@ -14,6 +14,7 @@
  * limitations under the License.
  */
 
+#include "hardware/gralloc.h"
 #define LOG_TAG "VirtualCameraRenderThread"
 #include "VirtualCameraRenderThread.h"
 
@@ -392,6 +393,11 @@
       EglTextureProgram::TextureFormat::RGBA);
   mEglSurfaceTexture = std::make_unique<EglSurfaceTexture>(
       mInputSurfaceSize.width, mInputSurfaceSize.height);
+
+  sp<Surface> inputSurface = mEglSurfaceTexture->getSurface();
+  if (mTestMode) {
+    inputSurface->connect(NATIVE_WINDOW_API_CPU, false, nullptr);
+  }
   mInputSurfacePromise.set_value(mEglSurfaceTexture->getSurface());
 
   while (std::unique_ptr<ProcessCaptureRequestTask> task = dequeueTask()) {
diff --git a/services/camera/virtualcamera/util/TestPatternHelper.cc b/services/camera/virtualcamera/util/TestPatternHelper.cc
index a00a1b8..274996a 100644
--- a/services/camera/virtualcamera/util/TestPatternHelper.cc
+++ b/services/camera/virtualcamera/util/TestPatternHelper.cc
@@ -15,6 +15,7 @@
  */
 
 // #define LOG_NDEBUG 0
+
 #define LOG_TAG "TestPatternHelper"
 
 #include "TestPatternHelper.h"
@@ -23,6 +24,9 @@
 #include <cstdint>
 
 #include "log/log.h"
+#include "nativebase/nativebase.h"
+#include "system/graphics.h"
+#include "ui/GraphicBuffer.h"
 #include "utils/Errors.h"
 
 namespace android {
@@ -31,6 +35,10 @@
 
 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++) {
@@ -40,72 +48,103 @@
   return 0xff;
 }
 
-uint8_t pixelToFractal(const int x, const int y, const std::complex<float> c) {
-  std::complex<float> n(float(x) / 640.0f - 0.5, float(y) / 480.0f - 0.5);
+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(uint8_t* data_ptr, const int width,
+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_data = data_ptr;
-  uint8_t* uv_data = static_cast<uint8_t*>(y_data + width * height);
+  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 i = 0; i < width; ++i) {
-    for (int j = 0; j < height; ++j) {
-      y_data[j * width + i] = pixelToFractal(i, j, c * 0.78f);
-      if ((i & 1) && (j & 1)) {
-        uv_data[((j / 2) * (width / 2) + i / 2) * 2] =
-            static_cast<uint8_t>((float(i) / float(width)) * 255.f);
-        uv_data[((j / 2) * (width / 2) + i / 2) * 2 + 1] =
-            static_cast<uint8_t>((float(j) / float(height)) * 255.f);
-      }
+  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
 
-// This is just to see some meaningfull image in the buffer for testing, only
-// works with YcbCr420.
-void renderTestPatternYCbCr420(const std::shared_ptr<AHardwareBuffer> buffer,
-                               const int frameNumber, const int fence) {
-  AHardwareBuffer_Planes planes_info;
-
-  AHardwareBuffer_Desc hwBufferDesc;
-  AHardwareBuffer_describe(buffer.get(), &hwBufferDesc);
-
-  const int width = hwBufferDesc.width;
-  const int height = hwBufferDesc.height;
-
-  int result = AHardwareBuffer_lockPlanes(buffer.get(),
-                                          AHARDWAREBUFFER_USAGE_CPU_READ_RARELY,
-                                          fence, nullptr, &planes_info);
-  if (result != OK) {
-    ALOGE("%s: Failed to lock planes: %d", __func__, result);
+void renderTestPatternYCbCr420(sp<Surface> surface, int frameNumber) {
+  if (surface == nullptr) {
+    ALOGE("%s: null surface, skipping render", __func__);
     return;
   }
 
-  renderTestPatternYcbCr420(
-      reinterpret_cast<uint8_t*>(planes_info.planes[0].data), width, height,
-      frameNumber);
+  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;
+  }
 
-  AHardwareBuffer_unlock(buffer.get(), nullptr);
-}
+  if (buffer == nullptr) {
+    ALOGE("%s: ANativeWindowBuffer is null after dequeing", __func__);
+    return;
+  }
 
-void renderTestPatternYCbCr420(sp<Surface> surface, int frameNumber) {
-  ANativeWindow_Buffer buffer;
-  surface->lock(&buffer, nullptr);
+  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;
+    }
+  }
 
-  ALOGV("buffer: %dx%d stride %d, pixfmt %d", buffer.width, buffer.height,
-        buffer.stride, buffer.format);
+  sp<GraphicBuffer> gBuffer = GraphicBuffer::from(buffer);
+  android_ycbcr ycbr;
 
-  renderTestPatternYcbCr420(reinterpret_cast<uint8_t*>(buffer.bits),
-                            buffer.width, buffer.height, frameNumber);
+  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;
+  }
 
-  surface->unlockAndPost();
+  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
diff --git a/services/camera/virtualcamera/util/TestPatternHelper.h b/services/camera/virtualcamera/util/TestPatternHelper.h
index aca1cdd..f842b29 100644
--- a/services/camera/virtualcamera/util/TestPatternHelper.h
+++ b/services/camera/virtualcamera/util/TestPatternHelper.h
@@ -17,20 +17,12 @@
 #ifndef ANDROID_COMPANION_VIRTUALCAMERA_TESTPATTERNHELPER_H
 #define ANDROID_COMPANION_VIRTUALCAMERA_TESTPATTERNHELPER_H
 
-#include <memory>
-
-#include "android/hardware_buffer.h"
 #include "gui/Surface.h"
 
 namespace android {
 namespace companion {
 namespace virtualcamera {
 
-// Helper function filling hardware buffer with test pattern for debugging /
-// testing purposes.
-void renderTestPatternYCbCr420(std::shared_ptr<AHardwareBuffer> buffer,
-                               int frameNumber, int fence = -1);
-
 // Helper function for rendering test pattern into Surface.
 void renderTestPatternYCbCr420(sp<Surface> surface, int frameNumber);