712 lines
27 KiB
C++
712 lines
27 KiB
C++
/*
|
|
* 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, 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 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.
|
|
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, 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.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) {
|
|
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(false /* no cpu sync */);
|
|
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
|