|  | /* | 
|  | * 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; | 
|  | }; | 
|  |  | 
|  | // Ref-Count a semaphore | 
|  | struct DestroySemaphoreInfo { | 
|  | VkSemaphore mSemaphore; | 
|  | // We need to make sure we don't delete the VkSemaphore until it is done being used by both Skia | 
|  | // (including by the GPU) and inside SkiaVkRenderEngine. So we always start with two refs, one | 
|  | // owned by Skia and one owned by the SkiaVkRenderEngine. The refs are decremented each time | 
|  | // delete_semaphore* is called with this object. Skia will call destroy_semaphore* once it is | 
|  | // done with the semaphore and the GPU has finished work on the semaphore. SkiaVkRenderEngine | 
|  | // calls delete_semaphore* after sending the semaphore to Skia and exporting it if need be. | 
|  | int mRefs = 2; | 
|  |  | 
|  | DestroySemaphoreInfo(VkSemaphore semaphore) : mSemaphore(semaphore) {} | 
|  | }; | 
|  |  | 
|  | 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; | 
|  | VkPhysicalDeviceProtectedMemoryFeatures* 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, GetPhysicalDeviceQueueFamilyProperties2); | 
|  | 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; | 
|  | vkGetPhysicalDeviceQueueFamilyProperties2(physicalDevice, &queueCount, nullptr); | 
|  | if (queueCount == 0) { | 
|  | BAIL("Could not find queues for physical device"); | 
|  | } | 
|  |  | 
|  | std::vector<VkQueueFamilyProperties2> queueProps(queueCount); | 
|  | std::vector<VkQueueFamilyGlobalPriorityPropertiesEXT> queuePriorityProps(queueCount); | 
|  | VkQueueGlobalPriorityKHR queuePriority = VK_QUEUE_GLOBAL_PRIORITY_MEDIUM_KHR; | 
|  | // Even though we don't yet know if the VK_EXT_global_priority extension is available, | 
|  | // we can safely add the request to the pNext chain, and if the extension is not | 
|  | // available, it will be ignored. | 
|  | for (uint32_t i = 0; i < queueCount; ++i) { | 
|  | queuePriorityProps[i].sType = VK_STRUCTURE_TYPE_QUEUE_FAMILY_GLOBAL_PRIORITY_PROPERTIES_EXT; | 
|  | queuePriorityProps[i].pNext = nullptr; | 
|  | queueProps[i].pNext = &queuePriorityProps[i]; | 
|  | } | 
|  | vkGetPhysicalDeviceQueueFamilyProperties2(physicalDevice, &queueCount, queueProps.data()); | 
|  |  | 
|  | int graphicsQueueIndex = -1; | 
|  | for (uint32_t i = 0; i < queueCount; ++i) { | 
|  | // Look at potential answers to the VK_EXT_global_priority query.  If answers were | 
|  | // provided, we may adjust the queuePriority. | 
|  | if (queueProps[i].queueFamilyProperties.queueFlags & VK_QUEUE_GRAPHICS_BIT) { | 
|  | for (uint32_t j = 0; j < queuePriorityProps[i].priorityCount; j++) { | 
|  | if (queuePriorityProps[i].priorities[j] > queuePriority) { | 
|  | queuePriority = queuePriorityProps[i].priorities[j]; | 
|  | } | 
|  | } | 
|  | if (queuePriority == VK_QUEUE_GLOBAL_PRIORITY_REALTIME_KHR) { | 
|  | interface.isRealtimePriority = true; | 
|  | } | 
|  | 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 VkPhysicalDeviceProtectedMemoryFeatures; | 
|  | 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. | 
|  | queuePriority, | 
|  | }; | 
|  |  | 
|  | if (interface.grExtensions.hasExtension(VK_EXT_GLOBAL_PRIORITY_EXTENSION_NAME, 2)) { | 
|  | queueNextPtr = &queuePriorityCreateInfo; | 
|  | } | 
|  |  | 
|  | 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, GetDeviceQueue2); | 
|  | const VkDeviceQueueInfo2 deviceQueueInfo2 = {VK_STRUCTURE_TYPE_DEVICE_QUEUE_INFO_2, nullptr, | 
|  | deviceQueueCreateFlags, | 
|  | (uint32_t)graphicsQueueIndex, 0}; | 
|  | vkGetDeviceQueue2(device, &deviceQueueInfo2, &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.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) { | 
|  | DestroySemaphoreInfo* info = reinterpret_cast<DestroySemaphoreInfo*>(semaphore); | 
|  | --info->mRefs; | 
|  | if (!info->mRefs) { | 
|  | sVulkanInterface.destroySemaphore(info->mSemaphore); | 
|  | delete info; | 
|  | } | 
|  | } | 
|  |  | 
|  | static void delete_semaphore_protected(void* semaphore) { | 
|  | DestroySemaphoreInfo* info = reinterpret_cast<DestroySemaphoreInfo*>(semaphore); | 
|  | --info->mRefs; | 
|  | if (!info->mRefs) { | 
|  | sProtectedContentVulkanInterface.destroySemaphore(info->mSemaphore); | 
|  | delete info; | 
|  | } | 
|  | } | 
|  |  | 
|  | 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) { | 
|  | VulkanInterface& vi = getVulkanInterface(isProtected()); | 
|  | VkSemaphore semaphore = vi.createExportableSemaphore(); | 
|  |  | 
|  | GrBackendSemaphore backendSemaphore; | 
|  | backendSemaphore.initVulkan(semaphore); | 
|  |  | 
|  | GrFlushInfo flushInfo; | 
|  | DestroySemaphoreInfo* destroySemaphoreInfo = nullptr; | 
|  | if (semaphore != VK_NULL_HANDLE) { | 
|  | destroySemaphoreInfo = new DestroySemaphoreInfo(semaphore); | 
|  | flushInfo.fNumSemaphores = 1; | 
|  | flushInfo.fSignalSemaphores = &backendSemaphore; | 
|  | flushInfo.fFinishedProc = isProtected() ? delete_semaphore_protected : delete_semaphore; | 
|  | flushInfo.fFinishedContext = destroySemaphoreInfo; | 
|  | } | 
|  | GrSemaphoresSubmitted submitted = grContext->flush(flushInfo); | 
|  | grContext->submit(GrSyncCpu::kNo); | 
|  | int drawFenceFd = -1; | 
|  | if (semaphore != VK_NULL_HANDLE) { | 
|  | if (GrSemaphoresSubmitted::kYes == submitted) { | 
|  | drawFenceFd = vi.exportSemaphoreSyncFd(semaphore); | 
|  | } | 
|  | // Now that drawFenceFd has been created, we can delete our reference to this semaphore | 
|  | flushInfo.fFinishedProc(destroySemaphoreInfo); | 
|  | } | 
|  | 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 |