Add SkiaVk backend to RenderEngine

This CL adds a new backend, SkiaVk, to RenderEngine.

The new functionality is to create a Vulkan device/instance (possibly
protected) and handle flush/waitFence via VkSemaphores fed to
GrBackendSemaphores.

+ make ctors of GLES/Vk RE's private so as to ensure GrContexts are
  created

Test: atest librenderengine_test
Bug: 236390072

Change-Id: I69119623b194885bcc4cf2ddc8e592576b713b19
diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.cpp b/libs/renderengine/skia/SkiaGLRenderEngine.cpp
index 347b8b7..ff598e7 100644
--- a/libs/renderengine/skia/SkiaGLRenderEngine.cpp
+++ b/libs/renderengine/skia/SkiaGLRenderEngine.cpp
@@ -193,9 +193,9 @@
     }
 
     // initialize the renderer while GL is current
-    std::unique_ptr<SkiaGLRenderEngine> engine =
-            std::make_unique<SkiaGLRenderEngine>(args, display, ctxt, placeholder, protectedContext,
-                                                 protectedPlaceholder);
+    std::unique_ptr<SkiaGLRenderEngine> engine(new SkiaGLRenderEngine(args, display, ctxt,
+                                                                      placeholder, protectedContext,
+                                                                      protectedPlaceholder));
     engine->ensureGrContextsCreated();
 
     ALOGI("OpenGL ES informations:");
diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.h b/libs/renderengine/skia/SkiaGLRenderEngine.h
index 4a37ffe..af33110 100644
--- a/libs/renderengine/skia/SkiaGLRenderEngine.h
+++ b/libs/renderengine/skia/SkiaGLRenderEngine.h
@@ -52,9 +52,6 @@
 class SkiaGLRenderEngine : public skia::SkiaRenderEngine {
 public:
     static std::unique_ptr<SkiaGLRenderEngine> create(const RenderEngineCreationArgs& args);
-    SkiaGLRenderEngine(const RenderEngineCreationArgs& args, EGLDisplay display, EGLContext ctxt,
-                       EGLSurface placeholder, EGLContext protectedContext,
-                       EGLSurface protectedPlaceholder);
     ~SkiaGLRenderEngine() override;
 
     int getContextPriority() override;
@@ -70,6 +67,9 @@
     void appendBackendSpecificInfoToDump(std::string& result) override;
 
 private:
+    SkiaGLRenderEngine(const RenderEngineCreationArgs& args, EGLDisplay display, EGLContext ctxt,
+                       EGLSurface placeholder, EGLContext protectedContext,
+                       EGLSurface protectedPlaceholder);
     bool waitGpuFence(base::borrowed_fd fenceFd);
     base::unique_fd flush();
     static EGLConfig chooseEglConfig(EGLDisplay display, int format, bool logConfig);
diff --git a/libs/renderengine/skia/SkiaRenderEngine.cpp b/libs/renderengine/skia/SkiaRenderEngine.cpp
index b9aa5ac..c3f6649 100644
--- a/libs/renderengine/skia/SkiaRenderEngine.cpp
+++ b/libs/renderengine/skia/SkiaRenderEngine.cpp
@@ -388,9 +388,11 @@
 
 void SkiaRenderEngine::mapExternalTextureBuffer(const sp<GraphicBuffer>& buffer,
                                                   bool isRenderable) {
-    // Only run this if RE is running on its own thread. This way the access to GL
-    // operations is guaranteed to be happening on the same thread.
-    if (mRenderEngineType != RenderEngineType::SKIA_GL_THREADED) {
+    // Only run this if RE is running on its own thread. This
+    // way the access to GL operations is guaranteed to be happening on the
+    // same thread.
+    if (mRenderEngineType != RenderEngineType::SKIA_GL_THREADED &&
+        mRenderEngineType != RenderEngineType::SKIA_VK_THREADED) {
         return;
     }
     // We currently don't attempt to map a buffer if the buffer contains protected content
diff --git a/libs/renderengine/skia/SkiaVkRenderEngine.cpp b/libs/renderengine/skia/SkiaVkRenderEngine.cpp
new file mode 100644
index 0000000..f9424f0
--- /dev/null
+++ b/libs/renderengine/skia/SkiaVkRenderEngine.cpp
@@ -0,0 +1,675 @@
+/*
+ * Copyright 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.
+ */
+
+// #define LOG_NDEBUG 0
+#undef LOG_TAG
+#define LOG_TAG "RenderEngine"
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+
+#include "SkiaVkRenderEngine.h"
+
+#include <GrBackendSemaphore.h>
+#include <GrContextOptions.h>
+#include <vk/GrVkExtensions.h>
+#include <vk/GrVkTypes.h>
+
+#include <android-base/stringprintf.h>
+#include <gui/TraceUtils.h>
+#include <sync/sync.h>
+#include <utils/Trace.h>
+
+#include <cstdint>
+#include <memory>
+#include <vector>
+
+#include <vulkan/vulkan.h>
+#include "log/log_main.h"
+
+namespace android {
+namespace renderengine {
+
+struct VulkanFuncs {
+    PFN_vkCreateSemaphore vkCreateSemaphore = nullptr;
+    PFN_vkImportSemaphoreFdKHR vkImportSemaphoreFdKHR = nullptr;
+    PFN_vkGetSemaphoreFdKHR vkGetSemaphoreFdKHR = nullptr;
+    PFN_vkDestroySemaphore vkDestroySemaphore = nullptr;
+
+    PFN_vkDeviceWaitIdle vkDeviceWaitIdle = nullptr;
+    PFN_vkDestroyDevice vkDestroyDevice = nullptr;
+    PFN_vkDestroyInstance vkDestroyInstance = nullptr;
+};
+
+struct VulkanInterface {
+    bool initialized = false;
+    VkInstance instance;
+    VkPhysicalDevice physicalDevice;
+    VkDevice device;
+    VkQueue queue;
+    int queueIndex;
+    uint32_t apiVersion;
+    GrVkExtensions grExtensions;
+    VkPhysicalDeviceFeatures2* physicalDeviceFeatures2 = nullptr;
+    VkPhysicalDeviceSamplerYcbcrConversionFeatures* samplerYcbcrConversionFeatures = nullptr;
+    VkPhysicalDeviceProtectedMemoryProperties* protectedMemoryFeatures = nullptr;
+    GrVkGetProc grGetProc;
+    bool isProtected;
+    bool isRealtimePriority;
+
+    VulkanFuncs funcs;
+
+    std::vector<std::string> instanceExtensionNames;
+    std::vector<std::string> deviceExtensionNames;
+
+    GrVkBackendContext getBackendContext() {
+        GrVkBackendContext backendContext;
+        backendContext.fInstance = instance;
+        backendContext.fPhysicalDevice = physicalDevice;
+        backendContext.fDevice = device;
+        backendContext.fQueue = queue;
+        backendContext.fGraphicsQueueIndex = queueIndex;
+        backendContext.fMaxAPIVersion = apiVersion;
+        backendContext.fVkExtensions = &grExtensions;
+        backendContext.fDeviceFeatures2 = physicalDeviceFeatures2;
+        backendContext.fGetProc = grGetProc;
+        backendContext.fProtectedContext = isProtected ? GrProtected::kYes : GrProtected::kNo;
+        return backendContext;
+    };
+
+    VkSemaphore createExportableSemaphore() {
+        VkExportSemaphoreCreateInfo exportInfo;
+        exportInfo.sType = VK_STRUCTURE_TYPE_EXPORT_SEMAPHORE_CREATE_INFO;
+        exportInfo.pNext = nullptr;
+        exportInfo.handleTypes = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT;
+
+        VkSemaphoreCreateInfo semaphoreInfo;
+        semaphoreInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
+        semaphoreInfo.pNext = &exportInfo;
+        semaphoreInfo.flags = 0;
+
+        VkSemaphore semaphore;
+        VkResult err = funcs.vkCreateSemaphore(device, &semaphoreInfo, nullptr, &semaphore);
+        if (VK_SUCCESS != err) {
+            ALOGE("%s: failed to create semaphore. err %d\n", __func__, err);
+            return VK_NULL_HANDLE;
+        }
+
+        return semaphore;
+    }
+
+    // syncFd cannot be <= 0
+    VkSemaphore importSemaphoreFromSyncFd(int syncFd) {
+        VkSemaphoreCreateInfo semaphoreInfo;
+        semaphoreInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
+        semaphoreInfo.pNext = nullptr;
+        semaphoreInfo.flags = 0;
+
+        VkSemaphore semaphore;
+        VkResult err = funcs.vkCreateSemaphore(device, &semaphoreInfo, nullptr, &semaphore);
+        if (VK_SUCCESS != err) {
+            ALOGE("%s: failed to create import semaphore", __func__);
+            return VK_NULL_HANDLE;
+        }
+
+        VkImportSemaphoreFdInfoKHR importInfo;
+        importInfo.sType = VK_STRUCTURE_TYPE_IMPORT_SEMAPHORE_FD_INFO_KHR;
+        importInfo.pNext = nullptr;
+        importInfo.semaphore = semaphore;
+        importInfo.flags = VK_SEMAPHORE_IMPORT_TEMPORARY_BIT;
+        importInfo.handleType = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT;
+        importInfo.fd = syncFd;
+
+        err = funcs.vkImportSemaphoreFdKHR(device, &importInfo);
+        if (VK_SUCCESS != err) {
+            funcs.vkDestroySemaphore(device, semaphore, nullptr);
+            ALOGE("%s: failed to import semaphore", __func__);
+            return VK_NULL_HANDLE;
+        }
+
+        return semaphore;
+    }
+
+    int exportSemaphoreSyncFd(VkSemaphore semaphore) {
+        int res;
+
+        VkSemaphoreGetFdInfoKHR getFdInfo;
+        getFdInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_GET_FD_INFO_KHR;
+        getFdInfo.pNext = nullptr;
+        getFdInfo.semaphore = semaphore;
+        getFdInfo.handleType = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT;
+
+        VkResult err = funcs.vkGetSemaphoreFdKHR(device, &getFdInfo, &res);
+        if (VK_SUCCESS != err) {
+            ALOGE("%s: failed to export semaphore, err: %d", __func__, err);
+            return -1;
+        }
+        return res;
+    }
+
+    void destroySemaphore(VkSemaphore semaphore) {
+        funcs.vkDestroySemaphore(device, semaphore, nullptr);
+    }
+};
+
+static GrVkGetProc sGetProc = [](const char* proc_name, VkInstance instance, VkDevice device) {
+    if (device != VK_NULL_HANDLE) {
+        return vkGetDeviceProcAddr(device, proc_name);
+    }
+    return vkGetInstanceProcAddr(instance, proc_name);
+};
+
+#define BAIL(fmt, ...)                                          \
+    {                                                           \
+        ALOGE("%s: " fmt ", bailing", __func__, ##__VA_ARGS__); \
+        return interface;                                       \
+    }
+
+#define CHECK_NONNULL(expr)       \
+    if ((expr) == nullptr) {      \
+        BAIL("[%s] null", #expr); \
+    }
+
+#define VK_CHECK(expr)                              \
+    if ((expr) != VK_SUCCESS) {                     \
+        BAIL("[%s] failed. err = %d", #expr, expr); \
+        return interface;                           \
+    }
+
+#define VK_GET_PROC(F)                                                           \
+    PFN_vk##F vk##F = (PFN_vk##F)vkGetInstanceProcAddr(VK_NULL_HANDLE, "vk" #F); \
+    CHECK_NONNULL(vk##F)
+#define VK_GET_INST_PROC(instance, F)                                      \
+    PFN_vk##F vk##F = (PFN_vk##F)vkGetInstanceProcAddr(instance, "vk" #F); \
+    CHECK_NONNULL(vk##F)
+#define VK_GET_DEV_PROC(device, F)                                     \
+    PFN_vk##F vk##F = (PFN_vk##F)vkGetDeviceProcAddr(device, "vk" #F); \
+    CHECK_NONNULL(vk##F)
+
+VulkanInterface initVulkanInterface(bool protectedContent = false) {
+    VulkanInterface interface;
+
+    VK_GET_PROC(EnumerateInstanceVersion);
+    uint32_t instanceVersion;
+    VK_CHECK(vkEnumerateInstanceVersion(&instanceVersion));
+
+    if (instanceVersion < VK_MAKE_VERSION(1, 1, 0)) {
+        return interface;
+    }
+
+    const VkApplicationInfo appInfo = {
+            VK_STRUCTURE_TYPE_APPLICATION_INFO, nullptr, "surfaceflinger", 0, "android platform", 0,
+            VK_MAKE_VERSION(1, 1, 0),
+    };
+
+    VK_GET_PROC(EnumerateInstanceExtensionProperties);
+
+    uint32_t extensionCount = 0;
+    VK_CHECK(vkEnumerateInstanceExtensionProperties(nullptr, &extensionCount, nullptr));
+    std::vector<VkExtensionProperties> instanceExtensions(extensionCount);
+    VK_CHECK(vkEnumerateInstanceExtensionProperties(nullptr, &extensionCount,
+                                                    instanceExtensions.data()));
+    std::vector<const char*> enabledInstanceExtensionNames;
+    enabledInstanceExtensionNames.reserve(instanceExtensions.size());
+    interface.instanceExtensionNames.reserve(instanceExtensions.size());
+    for (const auto& instExt : instanceExtensions) {
+        enabledInstanceExtensionNames.push_back(instExt.extensionName);
+        interface.instanceExtensionNames.push_back(instExt.extensionName);
+    }
+
+    const VkInstanceCreateInfo instanceCreateInfo = {
+            VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO,
+            nullptr,
+            0,
+            &appInfo,
+            0,
+            nullptr,
+            (uint32_t)enabledInstanceExtensionNames.size(),
+            enabledInstanceExtensionNames.data(),
+    };
+
+    VK_GET_PROC(CreateInstance);
+    VkInstance instance;
+    VK_CHECK(vkCreateInstance(&instanceCreateInfo, nullptr, &instance));
+
+    VK_GET_INST_PROC(instance, DestroyInstance);
+    interface.funcs.vkDestroyInstance = vkDestroyInstance;
+    VK_GET_INST_PROC(instance, EnumeratePhysicalDevices);
+    VK_GET_INST_PROC(instance, EnumerateDeviceExtensionProperties);
+    VK_GET_INST_PROC(instance, GetPhysicalDeviceProperties2);
+    VK_GET_INST_PROC(instance, GetPhysicalDeviceExternalSemaphoreProperties);
+    VK_GET_INST_PROC(instance, GetPhysicalDeviceQueueFamilyProperties);
+    VK_GET_INST_PROC(instance, GetPhysicalDeviceFeatures2);
+    VK_GET_INST_PROC(instance, CreateDevice);
+
+    uint32_t physdevCount;
+    VK_CHECK(vkEnumeratePhysicalDevices(instance, &physdevCount, nullptr));
+    if (physdevCount == 0) {
+        BAIL("Could not find any physical devices");
+    }
+
+    physdevCount = 1;
+    VkPhysicalDevice physicalDevice;
+    VkResult enumeratePhysDevsErr =
+            vkEnumeratePhysicalDevices(instance, &physdevCount, &physicalDevice);
+    if (enumeratePhysDevsErr != VK_SUCCESS && VK_INCOMPLETE != enumeratePhysDevsErr) {
+        BAIL("vkEnumeratePhysicalDevices failed with non-VK_INCOMPLETE error: %d",
+             enumeratePhysDevsErr);
+    }
+
+    VkPhysicalDeviceProperties2 physDevProps = {
+            VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2,
+            0,
+            {},
+    };
+    VkPhysicalDeviceProtectedMemoryProperties protMemProps = {
+            VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROTECTED_MEMORY_PROPERTIES,
+            0,
+            {},
+    };
+
+    if (protectedContent) {
+        physDevProps.pNext = &protMemProps;
+    }
+
+    vkGetPhysicalDeviceProperties2(physicalDevice, &physDevProps);
+    if (physDevProps.properties.apiVersion < VK_MAKE_VERSION(1, 1, 0)) {
+        BAIL("Could not find a Vulkan 1.1+ physical device");
+    }
+
+    // Check for syncfd support. Bail if we cannot both import and export them.
+    VkPhysicalDeviceExternalSemaphoreInfo semInfo = {
+            VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_SEMAPHORE_INFO,
+            nullptr,
+            VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT,
+    };
+    VkExternalSemaphoreProperties semProps = {
+            VK_STRUCTURE_TYPE_EXTERNAL_SEMAPHORE_PROPERTIES, nullptr, 0, 0, 0,
+    };
+    vkGetPhysicalDeviceExternalSemaphoreProperties(physicalDevice, &semInfo, &semProps);
+
+    bool sufficientSemaphoreSyncFdSupport = (semProps.exportFromImportedHandleTypes &
+                                             VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT) &&
+            (semProps.compatibleHandleTypes & VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT) &&
+            (semProps.externalSemaphoreFeatures & VK_EXTERNAL_SEMAPHORE_FEATURE_EXPORTABLE_BIT) &&
+            (semProps.externalSemaphoreFeatures & VK_EXTERNAL_SEMAPHORE_FEATURE_IMPORTABLE_BIT);
+
+    if (!sufficientSemaphoreSyncFdSupport) {
+        BAIL("Vulkan device does not support sufficient external semaphore sync fd features. "
+             "exportFromImportedHandleTypes 0x%x (needed 0x%x) "
+             "compatibleHandleTypes 0x%x (needed 0x%x) "
+             "externalSemaphoreFeatures 0x%x (needed 0x%x) ",
+             semProps.exportFromImportedHandleTypes, VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT,
+             semProps.compatibleHandleTypes, VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT,
+             semProps.externalSemaphoreFeatures,
+             VK_EXTERNAL_SEMAPHORE_FEATURE_EXPORTABLE_BIT |
+                     VK_EXTERNAL_SEMAPHORE_FEATURE_IMPORTABLE_BIT);
+    } else {
+        ALOGD("Vulkan device supports sufficient external semaphore sync fd features. "
+              "exportFromImportedHandleTypes 0x%x (needed 0x%x) "
+              "compatibleHandleTypes 0x%x (needed 0x%x) "
+              "externalSemaphoreFeatures 0x%x (needed 0x%x) ",
+              semProps.exportFromImportedHandleTypes, VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT,
+              semProps.compatibleHandleTypes, VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT,
+              semProps.externalSemaphoreFeatures,
+              VK_EXTERNAL_SEMAPHORE_FEATURE_EXPORTABLE_BIT |
+                      VK_EXTERNAL_SEMAPHORE_FEATURE_IMPORTABLE_BIT);
+    }
+
+    uint32_t queueCount;
+    vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice, &queueCount, nullptr);
+    if (queueCount == 0) {
+        BAIL("Could not find queues for physical device");
+    }
+
+    std::vector<VkQueueFamilyProperties> queueProps(queueCount);
+    vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice, &queueCount, queueProps.data());
+
+    int graphicsQueueIndex = -1;
+    for (uint32_t i = 0; i < queueCount; ++i) {
+        if (queueProps[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) {
+            graphicsQueueIndex = i;
+            break;
+        }
+    }
+
+    if (graphicsQueueIndex == -1) {
+        BAIL("Could not find a graphics queue family");
+    }
+
+    uint32_t deviceExtensionCount;
+    VK_CHECK(vkEnumerateDeviceExtensionProperties(physicalDevice, nullptr, &deviceExtensionCount,
+                                                  nullptr));
+    std::vector<VkExtensionProperties> deviceExtensions(deviceExtensionCount);
+    VK_CHECK(vkEnumerateDeviceExtensionProperties(physicalDevice, nullptr, &deviceExtensionCount,
+                                                  deviceExtensions.data()));
+
+    std::vector<const char*> enabledDeviceExtensionNames;
+    enabledDeviceExtensionNames.reserve(deviceExtensions.size());
+    interface.deviceExtensionNames.reserve(deviceExtensions.size());
+    for (const auto& devExt : deviceExtensions) {
+        enabledDeviceExtensionNames.push_back(devExt.extensionName);
+        interface.deviceExtensionNames.push_back(devExt.extensionName);
+    }
+
+    interface.grExtensions.init(sGetProc, instance, physicalDevice,
+                                enabledInstanceExtensionNames.size(),
+                                enabledInstanceExtensionNames.data(),
+                                enabledDeviceExtensionNames.size(),
+                                enabledDeviceExtensionNames.data());
+
+    if (!interface.grExtensions.hasExtension(VK_KHR_EXTERNAL_SEMAPHORE_FD_EXTENSION_NAME, 1)) {
+        BAIL("Vulkan driver doesn't support external semaphore fd");
+    }
+
+    interface.physicalDeviceFeatures2 = new VkPhysicalDeviceFeatures2;
+    interface.physicalDeviceFeatures2->sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2;
+    interface.physicalDeviceFeatures2->pNext = nullptr;
+
+    interface.samplerYcbcrConversionFeatures = new VkPhysicalDeviceSamplerYcbcrConversionFeatures;
+    interface.samplerYcbcrConversionFeatures->sType =
+            VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SAMPLER_YCBCR_CONVERSION_FEATURES;
+    interface.samplerYcbcrConversionFeatures->pNext = nullptr;
+
+    interface.physicalDeviceFeatures2->pNext = interface.samplerYcbcrConversionFeatures;
+    void** tailPnext = &interface.samplerYcbcrConversionFeatures->pNext;
+
+    if (protectedContent) {
+        interface.protectedMemoryFeatures = new VkPhysicalDeviceProtectedMemoryProperties;
+        interface.protectedMemoryFeatures->sType =
+                VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROTECTED_MEMORY_FEATURES;
+        interface.protectedMemoryFeatures->pNext = nullptr;
+        *tailPnext = interface.protectedMemoryFeatures;
+        tailPnext = &interface.protectedMemoryFeatures->pNext;
+    }
+
+    vkGetPhysicalDeviceFeatures2(physicalDevice, interface.physicalDeviceFeatures2);
+    // Looks like this would slow things down and we can't depend on it on all platforms
+    interface.physicalDeviceFeatures2->features.robustBufferAccess = VK_FALSE;
+
+    float queuePriorities[1] = {0.0f};
+    void* queueNextPtr = nullptr;
+
+    VkDeviceQueueGlobalPriorityCreateInfoEXT queuePriorityCreateInfo = {
+            VK_STRUCTURE_TYPE_DEVICE_QUEUE_GLOBAL_PRIORITY_CREATE_INFO_EXT,
+            nullptr,
+            // If queue priority is supported, RE should always have realtime priority.
+            VK_QUEUE_GLOBAL_PRIORITY_REALTIME_EXT,
+    };
+
+    if (interface.grExtensions.hasExtension(VK_EXT_GLOBAL_PRIORITY_EXTENSION_NAME, 2)) {
+        queueNextPtr = &queuePriorityCreateInfo;
+        interface.isRealtimePriority = true;
+    }
+
+    VkDeviceQueueCreateFlags deviceQueueCreateFlags =
+            (VkDeviceQueueCreateFlags)(protectedContent ? VK_DEVICE_QUEUE_CREATE_PROTECTED_BIT : 0);
+
+    const VkDeviceQueueCreateInfo queueInfo = {
+            VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO,
+            queueNextPtr,
+            deviceQueueCreateFlags,
+            (uint32_t)graphicsQueueIndex,
+            1,
+            queuePriorities,
+    };
+
+    const VkDeviceCreateInfo deviceInfo = {
+            VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO,
+            interface.physicalDeviceFeatures2,
+            0,
+            1,
+            &queueInfo,
+            0,
+            nullptr,
+            (uint32_t)enabledDeviceExtensionNames.size(),
+            enabledDeviceExtensionNames.data(),
+            nullptr,
+    };
+
+    ALOGD("Trying to create Vk device with protectedContent=%d", protectedContent);
+    VkDevice device;
+    VK_CHECK(vkCreateDevice(physicalDevice, &deviceInfo, nullptr, &device));
+    ALOGD("Trying to create Vk device with protectedContent=%d (success)", protectedContent);
+
+    VkQueue graphicsQueue;
+    VK_GET_DEV_PROC(device, GetDeviceQueue);
+    vkGetDeviceQueue(device, graphicsQueueIndex, 0, &graphicsQueue);
+
+    VK_GET_DEV_PROC(device, DeviceWaitIdle);
+    VK_GET_DEV_PROC(device, DestroyDevice);
+    interface.funcs.vkDeviceWaitIdle = vkDeviceWaitIdle;
+    interface.funcs.vkDestroyDevice = vkDestroyDevice;
+
+    VK_GET_DEV_PROC(device, CreateSemaphore);
+    VK_GET_DEV_PROC(device, ImportSemaphoreFdKHR);
+    VK_GET_DEV_PROC(device, GetSemaphoreFdKHR);
+    VK_GET_DEV_PROC(device, DestroySemaphore);
+    interface.funcs.vkCreateSemaphore = vkCreateSemaphore;
+    interface.funcs.vkImportSemaphoreFdKHR = vkImportSemaphoreFdKHR;
+    interface.funcs.vkGetSemaphoreFdKHR = vkGetSemaphoreFdKHR;
+    interface.funcs.vkDestroySemaphore = vkDestroySemaphore;
+
+    // At this point, everything's succeeded and we can continue
+    interface.initialized = true;
+    interface.instance = instance;
+    interface.physicalDevice = physicalDevice;
+    interface.device = device;
+    interface.queue = graphicsQueue;
+    interface.queueIndex = graphicsQueueIndex;
+    interface.apiVersion = physDevProps.properties.apiVersion;
+    // grExtensions already constructed
+    // feature pointers already constructed
+    interface.grGetProc = sGetProc;
+    interface.isProtected = protectedContent;
+    // funcs already initialized
+
+    ALOGD("%s: Success init Vulkan interface", __func__);
+    return interface;
+}
+
+void teardownVulkanInterface(VulkanInterface* interface) {
+    interface->initialized = false;
+
+    if (interface->device != VK_NULL_HANDLE) {
+        interface->funcs.vkDeviceWaitIdle(interface->device);
+        interface->funcs.vkDestroyDevice(interface->device, nullptr);
+        interface->device = VK_NULL_HANDLE;
+    }
+    if (interface->instance != VK_NULL_HANDLE) {
+        interface->funcs.vkDestroyInstance(interface->instance, nullptr);
+        interface->instance = VK_NULL_HANDLE;
+    }
+
+    if (interface->protectedMemoryFeatures) {
+        delete interface->protectedMemoryFeatures;
+    }
+
+    if (interface->samplerYcbcrConversionFeatures) {
+        delete interface->samplerYcbcrConversionFeatures;
+    }
+
+    if (interface->physicalDeviceFeatures2) {
+        delete interface->physicalDeviceFeatures2;
+    }
+
+    interface->samplerYcbcrConversionFeatures = nullptr;
+    interface->physicalDeviceFeatures2 = nullptr;
+    interface->protectedMemoryFeatures = nullptr;
+}
+
+static VulkanInterface sVulkanInterface;
+static VulkanInterface sProtectedContentVulkanInterface;
+
+static void sSetupVulkanInterface() {
+    if (!sVulkanInterface.initialized) {
+        sVulkanInterface = initVulkanInterface(false /* no protected content */);
+        // We will have to abort if non-protected VkDevice creation fails (then nothing works).
+        LOG_ALWAYS_FATAL_IF(!sVulkanInterface.initialized,
+                            "Could not initialize Vulkan RenderEngine!");
+    }
+    if (!sProtectedContentVulkanInterface.initialized) {
+        sProtectedContentVulkanInterface = initVulkanInterface(true /* protected content */);
+        if (!sProtectedContentVulkanInterface.initialized) {
+            ALOGE("Could not initialize protected content Vulkan RenderEngine.");
+        }
+    }
+}
+
+namespace skia {
+
+using base::StringAppendF;
+
+bool SkiaVkRenderEngine::canSupportSkiaVkRenderEngine() {
+    VulkanInterface temp = initVulkanInterface(false /* no protected content */);
+    ALOGD("SkiaVkRenderEngine::canSupportSkiaVkRenderEngine(): initialized == %s.",
+          temp.initialized ? "true" : "false");
+    return temp.initialized;
+}
+
+std::unique_ptr<SkiaVkRenderEngine> SkiaVkRenderEngine::create(
+        const RenderEngineCreationArgs& args) {
+    std::unique_ptr<SkiaVkRenderEngine> engine(new SkiaVkRenderEngine(args));
+    engine->ensureGrContextsCreated();
+
+    if (sVulkanInterface.initialized) {
+        ALOGD("SkiaVkRenderEngine::%s: successfully initialized SkiaVkRenderEngine", __func__);
+        return engine;
+    } else {
+        ALOGD("SkiaVkRenderEngine::%s: could not create SkiaVkRenderEngine. "
+              "Likely insufficient Vulkan support",
+              __func__);
+        return {};
+    }
+}
+
+SkiaVkRenderEngine::SkiaVkRenderEngine(const RenderEngineCreationArgs& args)
+      : SkiaRenderEngine(args.renderEngineType, static_cast<PixelFormat>(args.pixelFormat),
+                         args.useColorManagement, args.supportsBackgroundBlur) {}
+
+SkiaVkRenderEngine::~SkiaVkRenderEngine() {
+    finishRenderingAndAbandonContext();
+}
+
+SkiaRenderEngine::Contexts SkiaVkRenderEngine::createDirectContexts(
+        const GrContextOptions& options) {
+    sSetupVulkanInterface();
+
+    SkiaRenderEngine::Contexts contexts;
+    contexts.first = GrDirectContext::MakeVulkan(sVulkanInterface.getBackendContext(), options);
+    if (supportsProtectedContentImpl()) {
+        contexts.second =
+                GrDirectContext::MakeVulkan(sProtectedContentVulkanInterface.getBackendContext(),
+                                            options);
+    }
+
+    return contexts;
+}
+
+bool SkiaVkRenderEngine::supportsProtectedContentImpl() const {
+    return sProtectedContentVulkanInterface.initialized;
+}
+
+bool SkiaVkRenderEngine::useProtectedContextImpl(GrProtected) {
+    return true;
+}
+
+static void delete_semaphore(void* _semaphore) {
+    VkSemaphore semaphore = (VkSemaphore)_semaphore;
+    sVulkanInterface.destroySemaphore(semaphore);
+}
+
+static void delete_semaphore_protected(void* _semaphore) {
+    VkSemaphore semaphore = (VkSemaphore)_semaphore;
+    sProtectedContentVulkanInterface.destroySemaphore(semaphore);
+}
+
+static VulkanInterface& getVulkanInterface(bool protectedContext) {
+    if (protectedContext) {
+        return sProtectedContentVulkanInterface;
+    }
+    return sVulkanInterface;
+}
+
+void SkiaVkRenderEngine::waitFence(GrDirectContext* grContext, base::borrowed_fd fenceFd) {
+    if (fenceFd.get() < 0) return;
+
+    int dupedFd = dup(fenceFd.get());
+    if (dupedFd < 0) {
+        ALOGE("failed to create duplicate fence fd: %d", dupedFd);
+        sync_wait(fenceFd.get(), -1);
+        return;
+    }
+
+    base::unique_fd fenceDup(dupedFd);
+    VkSemaphore waitSemaphore =
+            getVulkanInterface(isProtected()).importSemaphoreFromSyncFd(fenceDup.release());
+    GrBackendSemaphore beSemaphore;
+    beSemaphore.initVulkan(waitSemaphore);
+    grContext->wait(1, &beSemaphore, true /* delete after wait */);
+}
+
+base::unique_fd SkiaVkRenderEngine::flushAndSubmit(GrDirectContext* grContext) {
+    VkSemaphore signalSemaphore = getVulkanInterface(isProtected()).createExportableSemaphore();
+    GrBackendSemaphore beSignalSemaphore;
+    beSignalSemaphore.initVulkan(signalSemaphore);
+    GrFlushInfo flushInfo;
+    flushInfo.fNumSemaphores = 1;
+    flushInfo.fSignalSemaphores = &beSignalSemaphore;
+    flushInfo.fFinishedProc = isProtected() ? delete_semaphore_protected : delete_semaphore;
+    flushInfo.fFinishedContext = (void*)signalSemaphore;
+    GrSemaphoresSubmitted submitted = grContext->flush(flushInfo);
+    grContext->submit(false /* no cpu sync */);
+    int drawFenceFd = -1;
+    if (GrSemaphoresSubmitted::kYes == submitted) {
+        drawFenceFd = getVulkanInterface(isProtected()).exportSemaphoreSyncFd(signalSemaphore);
+    }
+    base::unique_fd res(drawFenceFd);
+    return res;
+}
+
+int SkiaVkRenderEngine::getContextPriority() {
+    // EGL_CONTEXT_PRIORITY_REALTIME_NV
+    constexpr int kRealtimePriority = 0x3357;
+    if (getVulkanInterface(isProtected()).isRealtimePriority) {
+        return kRealtimePriority;
+    } else {
+        return 0;
+    }
+}
+
+void SkiaVkRenderEngine::appendBackendSpecificInfoToDump(std::string& result) {
+    StringAppendF(&result, "\n ------------RE Vulkan----------\n");
+    StringAppendF(&result, "\n Vulkan device initialized: %d\n", sVulkanInterface.initialized);
+    StringAppendF(&result, "\n Vulkan protected device initialized: %d\n",
+                  sProtectedContentVulkanInterface.initialized);
+
+    if (!sVulkanInterface.initialized) {
+        return;
+    }
+
+    StringAppendF(&result, "\n Instance extensions:\n");
+    for (const auto& name : sVulkanInterface.instanceExtensionNames) {
+        StringAppendF(&result, "\n %s\n", name.c_str());
+    }
+
+    StringAppendF(&result, "\n Device extensions:\n");
+    for (const auto& name : sVulkanInterface.deviceExtensionNames) {
+        StringAppendF(&result, "\n %s\n", name.c_str());
+    }
+}
+
+} // namespace skia
+} // namespace renderengine
+} // namespace android
diff --git a/libs/renderengine/skia/SkiaVkRenderEngine.h b/libs/renderengine/skia/SkiaVkRenderEngine.h
new file mode 100644
index 0000000..2e0cf45
--- /dev/null
+++ b/libs/renderengine/skia/SkiaVkRenderEngine.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright 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.
+ */
+
+#ifndef SF_SKIAVKRENDERENGINE_H_
+#define SF_SKIAVKRENDERENGINE_H_
+
+#include <vk/GrVkBackendContext.h>
+
+#include "SkiaRenderEngine.h"
+
+namespace android {
+namespace renderengine {
+namespace skia {
+
+class SkiaVkRenderEngine : public SkiaRenderEngine {
+public:
+    // Returns false if Vulkan implementation can't support SkiaVkRenderEngine.
+    static bool canSupportSkiaVkRenderEngine();
+    static std::unique_ptr<SkiaVkRenderEngine> create(const RenderEngineCreationArgs& args);
+    ~SkiaVkRenderEngine() override;
+
+    int getContextPriority() override;
+
+protected:
+    // Implementations of abstract SkiaRenderEngine functions specific to
+    // rendering backend
+    virtual SkiaRenderEngine::Contexts createDirectContexts(const GrContextOptions& options);
+    bool supportsProtectedContentImpl() const override;
+    bool useProtectedContextImpl(GrProtected isProtected) override;
+    void waitFence(GrDirectContext* grContext, base::borrowed_fd fenceFd) override;
+    base::unique_fd flushAndSubmit(GrDirectContext* context) override;
+    void appendBackendSpecificInfoToDump(std::string& result) override;
+
+private:
+    SkiaVkRenderEngine(const RenderEngineCreationArgs& args);
+    base::unique_fd flush();
+
+    GrVkBackendContext mBackendContext;
+};
+
+} // namespace skia
+} // namespace renderengine
+} // namespace android
+
+#endif