support EGL_ANDROID_native_fence_sync via virtio-gpu

regardless of api transport type, we can now support native fence sync
via virtio-gpu

Bug: 156130048
Change-Id: I93801428ede7fdcf1cf28240a4e4f560912f4a4a
diff --git a/system/OpenglSystemCommon/EmulatorFeatureInfo.h b/system/OpenglSystemCommon/EmulatorFeatureInfo.h
index 626048f..f1505fe 100644
--- a/system/OpenglSystemCommon/EmulatorFeatureInfo.h
+++ b/system/OpenglSystemCommon/EmulatorFeatureInfo.h
@@ -105,6 +105,9 @@
 // Vulkan free memory sync
 static const char kVulkanFreeMemorySync[] = "ANDROID_EMU_vulkan_free_memory_sync";
 
+// virtio-gpu syncfd support
+static const char kVirtioGpuNativeSync[] = "ANDROID_EMU_virtio_gpu_native_sync";
+
 // Struct describing available emulator features
 struct EmulatorFeatureInfo {
 
@@ -123,7 +126,8 @@
         hasAsyncUnmapBuffer (false),
         hasVirtioGpuNext (false),
         hasSharedSlotsHostMemoryAllocator(false),
-        hasVulkanFreeMemorySync(false)
+        hasVulkanFreeMemorySync(false),
+        hasVirtioGpuNativeSync(false)
     { }
 
     SyncImpl syncImpl;
@@ -141,6 +145,7 @@
     bool hasVirtioGpuNext;
     bool hasSharedSlotsHostMemoryAllocator;
     bool hasVulkanFreeMemorySync;
+    bool hasVirtioGpuNativeSync;
 };
 
 enum HostConnectionType {
diff --git a/system/OpenglSystemCommon/HostConnection.cpp b/system/OpenglSystemCommon/HostConnection.cpp
index b9c97c9..7a888d0 100644
--- a/system/OpenglSystemCommon/HostConnection.cpp
+++ b/system/OpenglSystemCommon/HostConnection.cpp
@@ -354,7 +354,9 @@
     m_checksumHelper(),
     m_glExtensions(),
     m_grallocOnly(true),
-    m_noHostError(false) { }
+    m_noHostError(false),
+    m_rendernodeFd(-1),
+    m_rendernodeFdOwned(false) { }
 
 HostConnection::~HostConnection()
 {
@@ -370,6 +372,10 @@
     delete m_glEnc;
     delete m_gl2Enc;
     delete m_rcEnc;
+
+    if (m_rendernodeFdOwned) {
+        close(m_rendernodeFd);
+    }
 }
 
 // static
@@ -459,6 +465,8 @@
             con->m_connectionType = HOST_CONNECTION_VIRTIO_GPU;
             con->m_grallocType = GRALLOC_TYPE_MINIGBM;
             con->m_stream = stream;
+            con->m_rendernodeFdOwned = false;
+            con->m_rendernodeFdOwned = stream->getRendernodeFd();
             MinigbmGralloc* m = new MinigbmGralloc;
             m->setFd(stream->getRendernodeFd());
             con->m_grallocHelper = m;
@@ -481,6 +489,8 @@
             con->m_connectionType = HOST_CONNECTION_VIRTIO_GPU_PIPE;
             con->m_grallocType = getGrallocTypeFromProperty();
             con->m_stream = stream;
+            con->m_rendernodeFdOwned = false;
+            con->m_rendernodeFdOwned = stream->getRendernodeFd();
             switch (con->m_grallocType) {
                 case GRALLOC_TYPE_RANCHU:
                     con->m_grallocHelper = &m_goldfishGralloc;
@@ -615,6 +625,7 @@
         queryAndSetVirtioGpuNext(m_rcEnc);
         queryHasSharedSlotsHostMemoryAllocator(m_rcEnc);
         queryAndSetVulkanFreeMemorySync(m_rcEnc);
+        queryAndSetVirtioGpuNativeSync(m_rcEnc);
         if (m_processPipe) {
             m_processPipe->processPipeInit(m_connectionType, m_rcEnc);
         }
@@ -622,6 +633,30 @@
     return m_rcEnc;
 }
 
+int HostConnection::getOrCreateRendernodeFd() {
+    if (m_rendernodeFd >= 0) return m_rendernodeFd;
+#ifdef __Fuchsia__
+    return -1;
+#else
+#ifdef VIRTIO_GPU
+    m_rendernodeFd = VirtioGpuPipeStream::openRendernode();
+    if (m_rendernodeFd < 0) {
+        ALOGE("%s: failed to create secondary "
+              "rendernode for host connection. "
+              "error: %s (%d)\n", __FUNCTION__,
+              strerror(errno), errno);
+        return -1;
+    }
+
+    // Remember to close it on exit
+    m_rendernodeFdOwned = true;
+    return m_rendernodeFd;
+#else
+    return -1;
+#endif
+#endif
+}
+
 gl_client_context_t *HostConnection::s_getGLContext()
 {
     EGLThreadInfo *ti = getEGLThreadInfo();
@@ -832,3 +867,11 @@
         rcEnc->featureInfo()->hasVulkanFreeMemorySync = true;
     }
 }
+
+void HostConnection::queryAndSetVirtioGpuNativeSync(ExtendedRCEncoderContext* rcEnc) {
+    std::string glExtensions = queryGLExtensions(rcEnc);
+    if (glExtensions.find(kVirtioGpuNativeSync) != std::string::npos) {
+        rcEnc->featureInfo()->hasVirtioGpuNativeSync = true;
+    }
+}
+
diff --git a/system/OpenglSystemCommon/HostConnection.h b/system/OpenglSystemCommon/HostConnection.h
index 02c8681..6acefee 100644
--- a/system/OpenglSystemCommon/HostConnection.h
+++ b/system/OpenglSystemCommon/HostConnection.h
@@ -55,6 +55,7 @@
     bool hasNativeSync() const { return m_featureInfo.syncImpl >= SYNC_IMPL_NATIVE_SYNC_V2; }
     bool hasNativeSyncV3() const { return m_featureInfo.syncImpl >= SYNC_IMPL_NATIVE_SYNC_V3; }
     bool hasNativeSyncV4() const { return m_featureInfo.syncImpl >= SYNC_IMPL_NATIVE_SYNC_V4; }
+    bool hasVirtioGpuNativeSync() const { return m_featureInfo.hasVirtioGpuNativeSync; }
     bool hasHostCompositionV1() const {
         return m_featureInfo.hostComposition == HOST_COMPOSITION_V1; }
     bool hasHostCompositionV2() const {
@@ -152,6 +153,12 @@
     GL2Encoder *gl2Encoder();
     goldfish_vk::VkEncoder *vkEncoder();
     ExtendedRCEncoderContext *rcEncoder();
+
+    // Returns rendernode fd, in case the stream is virtio-gpu based.
+    // Otherwise, attempts to create a rendernode fd assuming
+    // virtio-gpu is available.
+    int getOrCreateRendernodeFd();
+
     ChecksumCalculator *checksumHelper() { return &m_checksumHelper; }
     Gralloc *grallocHelper() { return m_grallocHelper; }
 
@@ -206,6 +213,7 @@
     void queryAndSetVirtioGpuNext(ExtendedRCEncoderContext *rcEnc);
     void queryHasSharedSlotsHostMemoryAllocator(ExtendedRCEncoderContext *rcEnc);
     void queryAndSetVulkanFreeMemorySync(ExtendedRCEncoderContext *rcEnc);
+    void queryAndSetVirtioGpuNativeSync(ExtendedRCEncoderContext *rcEnc);
 
 private:
     HostConnectionType m_connectionType;
@@ -226,6 +234,8 @@
 #else
     mutable android::Mutex m_lock;
 #endif
+    int m_rendernodeFd;
+    bool m_rendernodeFdOwned;
 };
 
 #endif
diff --git a/system/OpenglSystemCommon/VirtioGpuPipeStream.cpp b/system/OpenglSystemCommon/VirtioGpuPipeStream.cpp
index d3618f3..238d555 100644
--- a/system/OpenglSystemCommon/VirtioGpuPipeStream.cpp
+++ b/system/OpenglSystemCommon/VirtioGpuPipeStream.cpp
@@ -74,7 +74,7 @@
 int VirtioGpuPipeStream::connect(const char* serviceName)
 {
     if (m_fd < 0) {
-        m_fd = drmOpenRender(RENDERNODE_MINOR);
+        m_fd = VirtioGpuPipeStream::openRendernode();
         if (m_fd < 0) {
             ERR("%s: failed with fd %d (%s)", __func__, m_fd, strerror(errno));
             return -1;
@@ -154,6 +154,15 @@
     return 0;
 }
 
+int VirtioGpuPipeStream::openRendernode() {
+    int fd = drmOpenRender(RENDERNODE_MINOR);
+    if (fd < 0) {
+            ERR("%s: failed with fd %d (%s)", __func__, fd, strerror(errno));
+        return -1;
+    }
+    return fd;
+}
+
 uint64_t VirtioGpuPipeStream::initProcessPipe() {
     connect("pipe:GLProcessPipe");
     int32_t confirmInt = 100;
diff --git a/system/OpenglSystemCommon/VirtioGpuPipeStream.h b/system/OpenglSystemCommon/VirtioGpuPipeStream.h
index 3f3c537..91c76fc 100644
--- a/system/OpenglSystemCommon/VirtioGpuPipeStream.h
+++ b/system/OpenglSystemCommon/VirtioGpuPipeStream.h
@@ -33,6 +33,7 @@
     explicit VirtioGpuPipeStream(size_t bufsize = 10000);
     ~VirtioGpuPipeStream();
     int connect(const char* serviceName = 0);
+    static int openRendernode();
     uint64_t initProcessPipe();
 
     virtual void *allocBuffer(size_t minSize);
diff --git a/system/egl/Android.mk b/system/egl/Android.mk
index 6e239b2..a5e1c1f 100644
--- a/system/egl/Android.mk
+++ b/system/egl/Android.mk
@@ -23,7 +23,12 @@
 ifneq (true,$(GOLDFISH_OPENGL_BUILD_FOR_HOST))
 
 LOCAL_SHARED_LIBRARIES += libdl
-endif
+ifeq (true,$(BUILD_EMULATOR_VULKAN))
+LOCAL_CFLAGS += -DVIRTIO_GPU
+LOCAL_C_INCLUDES += external/libdrm
+LOCAL_SHARED_LIBRARIES += libdrm
+endif # BUILD_EMULATOR_VULKAN
+endif # GOLDFISH_OPENGL_BUILD_FOR_HOST
 
 ifneq (true,$(GOLDFISH_OPENGL_BUILD_FOR_HOST))
 ifdef IS_AT_LEAST_OPM1
diff --git a/system/egl/CMakeLists.txt b/system/egl/CMakeLists.txt
index 08db0c8..301ef62 100644
--- a/system/egl/CMakeLists.txt
+++ b/system/egl/CMakeLists.txt
@@ -1,7 +1,7 @@
 # This is an autogenerated file! Do not edit!
 # instead run make from .../device/generic/goldfish-opengl
 # which will re-generate this file.
-android_validate_sha256("${GOLDFISH_DEVICE_ROOT}/system/egl/Android.mk" "597fba46fce0876a62ef3c0bc8f3a0264503214b62b0b0da092ee90fe60f09e1")
+android_validate_sha256("${GOLDFISH_DEVICE_ROOT}/system/egl/Android.mk" "729a65bbc7934fa79a239b55b801e4ebaa44d02c2deeab7ab43e42196376c7c7")
 set(EGL_emulation_src eglDisplay.cpp egl.cpp ClientAPIExts.cpp)
 android_add_library(TARGET EGL_emulation SHARED LICENSE Apache-2.0 SRC eglDisplay.cpp egl.cpp ClientAPIExts.cpp)
 target_include_directories(EGL_emulation PRIVATE ${GOLDFISH_DEVICE_ROOT}/system/OpenglSystemCommon/bionic-include ${GOLDFISH_DEVICE_ROOT}/system/OpenglSystemCommon ${GOLDFISH_DEVICE_ROOT}/bionic/libc/private ${GOLDFISH_DEVICE_ROOT}/bionic/libc/platform ${GOLDFISH_DEVICE_ROOT}/system/vulkan_enc ${GOLDFISH_DEVICE_ROOT}/android-emu ${GOLDFISH_DEVICE_ROOT}/shared/gralloc_cb/include ${GOLDFISH_DEVICE_ROOT}/shared/GoldfishAddressSpace/include ${GOLDFISH_DEVICE_ROOT}/system/renderControl_enc ${GOLDFISH_DEVICE_ROOT}/system/GLESv2_enc ${GOLDFISH_DEVICE_ROOT}/system/GLESv1_enc ${GOLDFISH_DEVICE_ROOT}/shared/OpenglCodecCommon ${GOLDFISH_DEVICE_ROOT}/shared/qemupipe/include-types ${GOLDFISH_DEVICE_ROOT}/shared/qemupipe/include ${GOLDFISH_DEVICE_ROOT}/./host/include/libOpenglRender ${GOLDFISH_DEVICE_ROOT}/./system/include ${GOLDFISH_DEVICE_ROOT}/./../../../external/qemu/android/android-emugl/guest)
diff --git a/system/egl/egl.cpp b/system/egl/egl.cpp
index 834207b..fbd2405 100644
--- a/system/egl/egl.cpp
+++ b/system/egl/egl.cpp
@@ -43,6 +43,12 @@
 
 #include <GLES3/gl31.h>
 
+#ifdef VIRTIO_GPU
+#include <drm/virtgpu_drm.h>
+#include <xf86drm.h>
+#include <poll.h>
+#endif // VIRTIO_GPU
+
 #if PLATFORM_SDK_VERSION < 18
 #define override
 #endif
@@ -494,6 +500,101 @@
     return sync_handle;
 }
 
+// our cmd
+#define VIRTIO_GPU_NATIVE_SYNC_CREATE_EXPORT_FD 0x9000
+#define VIRTIO_GPU_NATIVE_SYNC_CREATE_IMPORT_FD 0x9001
+
+// createNativeSync_virtioGpu()
+// creates an OpenGL sync object on the host
+// using rcCreateSyncKHR.
+// If necessary, a native fence FD will be exported or imported.
+// Returns a handle to the host-side FenceSync object.
+static uint64_t createNativeSync_virtioGpu(
+    EGLenum type,
+    const EGLint* attrib_list,
+    int num_actual_attribs,
+    bool destroy_when_signaled,
+    int fd_in,
+    int* fd_out) {
+#ifndef VIRTIO_GPU
+    ALOGE("%s: Error: called with no virtio-gpu support built in\n", __func__);
+    return 0;
+#else
+    DEFINE_HOST_CONNECTION;
+
+    uint64_t sync_handle;
+    uint64_t thread_handle;
+
+    EGLint* actual_attribs =
+        (EGLint*)(num_actual_attribs == 0 ? NULL : attrib_list);
+
+    // Create a normal sync obj
+    rcEnc->rcCreateSyncKHR(rcEnc, type,
+                           actual_attribs,
+                           num_actual_attribs * sizeof(EGLint),
+                           destroy_when_signaled,
+                           &sync_handle,
+                           &thread_handle);
+
+    // Import fence fd; dup and close
+    if (type == EGL_SYNC_NATIVE_FENCE_ANDROID && fd_in >= 0) {
+        int importedFd = dup(fd_in);
+
+        if (importedFd < 0) {
+            ALOGE("%s: error: failed to dup imported fd. original: %d errno %d\n",
+                  __func__, fd_in, errno);
+        }
+
+        *fd_out = importedFd;
+
+        if (close(fd_in)) {
+            ALOGE("%s: error: failed to close imported fd. original: %d errno %d\n",
+                  __func__, fd_in, errno);
+        }
+
+    } else if (type == EGL_SYNC_NATIVE_FENCE_ANDROID && fd_in < 0) {
+        // Export fence fd
+
+        uint32_t sync_handle_lo = (uint32_t)sync_handle;
+        uint32_t sync_handle_hi = (uint32_t)(sync_handle >> 32);
+
+        uint32_t cmdDwords[3] = {
+            VIRTIO_GPU_NATIVE_SYNC_CREATE_EXPORT_FD,
+            sync_handle_lo,
+            sync_handle_hi,
+        };
+
+        drm_virtgpu_execbuffer createSyncExport = {
+            .flags = VIRTGPU_EXECBUF_FENCE_FD_OUT,
+            .size = 3 * sizeof(uint32_t),
+            .command = (uint64_t)(cmdDwords),
+            .bo_handles = 0,
+            .num_bo_handles = 0,
+            .fence_fd = -1,
+        };
+
+        int queue_work_err =
+            drmIoctl(
+                hostCon->getOrCreateRendernodeFd(),
+                DRM_IOCTL_VIRTGPU_EXECBUFFER, &createSyncExport);
+
+        if (queue_work_err) {
+            ERR("%s: failed with %d executing command buffer (%s)",  __func__,
+                queue_work_err, strerror(errno));
+            return 0;
+        }
+
+        *fd_out = createSyncExport.fence_fd;
+
+        DPRINT("virtio-gpu: got native fence fd=%d queue_work_err=%d",
+               *fd_out, queue_work_err);
+
+    }
+
+    return sync_handle;
+#endif
+}
+
 // createGoldfishOpenGLNativeSync() is for creating host-only sync objects
 // that are needed by only this goldfish opengl driver,
 // such as in swapBuffers().
@@ -544,7 +645,16 @@
     eglWaitClient();
     nativeWindow->queueBuffer(nativeWindow, buffer);
 #else
-    if (rcEnc->hasNativeSync()) {
+    if (rcEnc->hasVirtioGpuNativeSync()) {
+        rcEnc->rcFlushWindowColorBufferAsync(rcEnc, rcSurface);
+        createNativeSync_virtioGpu(EGL_SYNC_NATIVE_FENCE_ANDROID,
+                     NULL /* empty attrib list */,
+                     0 /* 0 attrib count */,
+                     true /* destroy when signaled. this is host-only
+                             and there will only be one waiter */,
+                     -1 /* we want a new fd */,
+                     &presentFenceFd);
+    } else if (rcEnc->hasNativeSync()) {
         rcEnc->rcFlushWindowColorBufferAsync(rcEnc, rcSurface);
         createGoldfishOpenGLNativeSync(&presentFenceFd);
     } else {
@@ -2097,7 +2207,8 @@
     if ((type != EGL_SYNC_FENCE_KHR &&
          type != EGL_SYNC_NATIVE_FENCE_ANDROID) ||
         (type != EGL_SYNC_FENCE_KHR &&
-         !rcEnc->hasNativeSync())) {
+         !rcEnc->hasNativeSync() &&
+         !rcEnc->hasVirtioGpuNativeSync())) {
         setErrorReturn(EGL_BAD_ATTRIBUTE, EGL_NO_SYNC_KHR);
     }
 
@@ -2150,14 +2261,23 @@
     uint64_t sync_handle = 0;
     int newFenceFd = -1;
 
-    if (rcEnc->hasNativeSync()) {
+    if (rcEnc->hasVirtioGpuNativeSync()) {
         sync_handle =
-            createNativeSync(type, attrib_list, num_actual_attribs,
-                             false /* don't destroy when signaled on the host;
-                                      let the guest clean this up,
-                                      because the guest called eglCreateSyncKHR. */,
-                             inputFenceFd,
-                             &newFenceFd);
+            createNativeSync_virtioGpu(
+                type, attrib_list, num_actual_attribs,
+                false /* don't destroy when signaled on the host;
+                         let the guest clean this up,
+                         because the guest called eglCreateSyncKHR. */,
+                inputFenceFd, &newFenceFd);
+    } else if (rcEnc->hasNativeSync()) {
+        sync_handle =
+            createNativeSync(
+                type, attrib_list, num_actual_attribs,
+                false /* don't destroy when signaled on the host;
+                         let the guest clean this up,
+                         because the guest called eglCreateSyncKHR. */,
+                inputFenceFd,
+                &newFenceFd);
 
     } else {
         // Just trigger a glFinish if the native sync on host
@@ -2170,17 +2290,21 @@
     if (type == EGL_SYNC_NATIVE_FENCE_ANDROID) {
         syncRes->type = EGL_SYNC_NATIVE_FENCE_ANDROID;
 
-        if (inputFenceFd < 0) {
+        if (rcEnc->hasVirtioGpuNativeSync()) {
             syncRes->android_native_fence_fd = newFenceFd;
         } else {
-            DPRINT("has input fence fd %d",
-                    inputFenceFd);
-            syncRes->android_native_fence_fd = inputFenceFd;
+            if (inputFenceFd < 0) {
+                syncRes->android_native_fence_fd = newFenceFd;
+            } else {
+                DPRINT("has input fence fd %d",
+                        inputFenceFd);
+                syncRes->android_native_fence_fd = inputFenceFd;
+            }
         }
     } else {
         syncRes->type = EGL_SYNC_FENCE_KHR;
         syncRes->android_native_fence_fd = -1;
-        if (!rcEnc->hasNativeSync()) {
+        if (!rcEnc->hasNativeSync() && !rcEnc->hasVirtioGpuNativeSync()) {
             syncRes->status = EGL_SIGNALED_KHR;
         }
     }
@@ -2206,7 +2330,7 @@
 
     if (sync) {
         DEFINE_HOST_CONNECTION;
-        if (rcEnc->hasNativeSync()) {
+        if (rcEnc->hasVirtioGpuNativeSync() || rcEnc->hasNativeSync()) {
             rcEnc->rcDestroySyncKHR(rcEnc, sync->handle);
         }
         delete sync;
@@ -2233,7 +2357,7 @@
     DEFINE_HOST_CONNECTION;
 
     EGLint retval;
-    if (rcEnc->hasNativeSync()) {
+    if (rcEnc->hasVirtioGpuNativeSync() || rcEnc->hasNativeSync()) {
         retval = rcEnc->rcClientWaitSyncKHR
             (rcEnc, sync->handle, flags, timeout);
     } else {
@@ -2272,7 +2396,7 @@
         } else {
             // ask the host again
             DEFINE_HOST_CONNECTION;
-            if (rcEnc->hasNativeSyncV4()) {
+            if (rcEnc->hasVirtioGpuNativeSync() || rcEnc->hasNativeSyncV4()) {
                 if (rcEnc->rcIsSyncSignaled(rcEnc, sync->handle)) {
                     sync->status = EGL_SIGNALED_KHR;
                 }
@@ -2317,7 +2441,7 @@
     }
 
     DEFINE_HOST_CONNECTION;
-    if (rcEnc->hasNativeSyncV3()) {
+    if (rcEnc->hasVirtioGpuNativeSync() || rcEnc->hasNativeSyncV3()) {
         EGLSync_t* sync = (EGLSync_t*)eglsync;
         rcEnc->rcWaitSyncKHR(rcEnc, sync->handle, flags);
     }
diff --git a/system/egl/eglDisplay.cpp b/system/egl/eglDisplay.cpp
index b3f14da..2508f48 100644
--- a/system/egl/eglDisplay.cpp
+++ b/system/egl/eglDisplay.cpp
@@ -357,11 +357,11 @@
 
         std::string dynamicEGLExtensions;
 
-        if (hcon->rcEncoder()->hasNativeSync() &&
+        if ((hcon->rcEncoder()->hasVirtioGpuNativeSync() || hcon->rcEncoder()->hasNativeSync()) &&
             !strstr(initialEGLExts, kDynamicEGLExtNativeSync)) {
             dynamicEGLExtensions += kDynamicEGLExtNativeSync;
 
-            if (hcon->rcEncoder()->hasNativeSyncV3()) {
+            if (hcon->rcEncoder()->hasVirtioGpuNativeSync() || hcon->rcEncoder()->hasNativeSyncV3()) {
                 dynamicEGLExtensions += kDynamicEGLExtWaitSync;
             }
         }