blob: eb7a9d5bfa203a202356229bc68e1e3ca5f2f538 [file] [log] [blame]
/*
* 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 <include/gpu/ganesh/vk/GrVkDirectContext.h>
#include <android-base/stringprintf.h>
#include <gui/TraceUtils.h>
#include <sync/sync.h>
#include <utils/Trace.h>
#include <cstdint>
#include <memory>
#include <sstream>
#include <string>
#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) {}
};
namespace {
void onVkDeviceFault(void* callbackContext, const std::string& description,
const std::vector<VkDeviceFaultAddressInfoEXT>& addressInfos,
const std::vector<VkDeviceFaultVendorInfoEXT>& vendorInfos,
const std::vector<std::byte>& vendorBinaryData);
} // anonymous namespace
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;
VkPhysicalDeviceFaultFeaturesEXT* deviceFaultFeatures = 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;
backendContext.fDeviceLostContext = this; // VulkanInterface is long-lived
backendContext.fDeviceLostProc = onVkDeviceFault;
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);
}
};
namespace {
void onVkDeviceFault(void* callbackContext, const std::string& description,
const std::vector<VkDeviceFaultAddressInfoEXT>& addressInfos,
const std::vector<VkDeviceFaultVendorInfoEXT>& vendorInfos,
const std::vector<std::byte>& vendorBinaryData) {
VulkanInterface* interface = static_cast<VulkanInterface*>(callbackContext);
const std::string protectedStr = interface->isProtected ? "protected" : "non-protected";
// The final crash string should contain as much differentiating info as possible, up to 1024
// bytes. As this final message is constructed, the same information is also dumped to the logs
// but in a more verbose format. Building the crash string is unsightly, so the clearer logging
// statement is always placed first to give context.
ALOGE("VK_ERROR_DEVICE_LOST (%s context): %s", protectedStr.c_str(), description.c_str());
std::stringstream crashMsg;
crashMsg << "VK_ERROR_DEVICE_LOST (" << protectedStr;
if (!addressInfos.empty()) {
ALOGE("%zu VkDeviceFaultAddressInfoEXT:", addressInfos.size());
crashMsg << ", " << addressInfos.size() << " address info (";
for (VkDeviceFaultAddressInfoEXT addressInfo : addressInfos) {
ALOGE(" addressType: %d", (int)addressInfo.addressType);
ALOGE(" reportedAddress: %" PRIu64, addressInfo.reportedAddress);
ALOGE(" addressPrecision: %" PRIu64, addressInfo.addressPrecision);
crashMsg << addressInfo.addressType << ":"
<< addressInfo.reportedAddress << ":"
<< addressInfo.addressPrecision << ", ";
}
crashMsg.seekp(-2, crashMsg.cur); // Move back to overwrite trailing ", "
crashMsg << ")";
}
if (!vendorInfos.empty()) {
ALOGE("%zu VkDeviceFaultVendorInfoEXT:", vendorInfos.size());
crashMsg << ", " << vendorInfos.size() << " vendor info (";
for (VkDeviceFaultVendorInfoEXT vendorInfo : vendorInfos) {
ALOGE(" description: %s", vendorInfo.description);
ALOGE(" vendorFaultCode: %" PRIu64, vendorInfo.vendorFaultCode);
ALOGE(" vendorFaultData: %" PRIu64, vendorInfo.vendorFaultData);
// Omit descriptions for individual vendor info structs in the crash string, as the
// fault code and fault data fields should be enough for clustering, and the verbosity
// isn't worth it. Additionally, vendors may just set the general description field of
// the overall fault to the description of the first element in this list, and that
// overall description will be placed at the end of the crash string.
crashMsg << vendorInfo.vendorFaultCode << ":"
<< vendorInfo.vendorFaultData << ", ";
}
crashMsg.seekp(-2, crashMsg.cur); // Move back to overwrite trailing ", "
crashMsg << ")";
}
if (!vendorBinaryData.empty()) {
// TODO: b/322830575 - Log in base64, or dump directly to a file that gets put in bugreports
ALOGE("%zu bytes of vendor-specific binary data (please notify Android's Core Graphics"
" Stack team if you observe this message).",
vendorBinaryData.size());
crashMsg << ", " << vendorBinaryData.size() << " bytes binary";
}
crashMsg << "): " << description;
LOG_ALWAYS_FATAL("%s", crashMsg.str().c_str());
};
} // anonymous namespace
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) {
const nsecs_t timeBefore = systemTime();
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");
}
if (physDevProps.properties.deviceType == VK_PHYSICAL_DEVICE_TYPE_CPU) {
// TODO: b/326633110 - SkiaVK is not working correctly on swiftshader path.
BAIL("CPU implementations of Vulkan is not supported");
}
// 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;
}
if (interface.grExtensions.hasExtension(VK_EXT_DEVICE_FAULT_EXTENSION_NAME, 1)) {
interface.deviceFaultFeatures = new VkPhysicalDeviceFaultFeaturesEXT;
interface.deviceFaultFeatures->sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FAULT_FEATURES_EXT;
interface.deviceFaultFeatures->pNext = nullptr;
*tailPnext = interface.deviceFaultFeatures;
tailPnext = &interface.deviceFaultFeatures->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;
if (protectedContent && !interface.protectedMemoryFeatures->protectedMemory) {
BAIL("Protected memory not supported");
}
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
const nsecs_t timeAfter = systemTime();
const float initTimeMs = static_cast<float>(timeAfter - timeBefore) / 1.0E6;
ALOGD("%s: Success init Vulkan interface in %f ms", __func__, initTimeMs);
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;
}
if (interface->deviceFaultFeatures) {
delete interface->deviceFaultFeatures;
}
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.");
}
}
}
bool RenderEngine::canSupport(GraphicsApi graphicsApi) {
switch (graphicsApi) {
case GraphicsApi::GL:
return true;
case GraphicsApi::VK: {
if (!sVulkanInterface.initialized) {
sVulkanInterface = initVulkanInterface(false /* no protected content */);
ALOGD("%s: initialized == %s.", __func__,
sVulkanInterface.initialized ? "true" : "false");
}
return sVulkanInterface.initialized;
}
}
}
namespace skia {
using base::StringAppendF;
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.threaded, static_cast<PixelFormat>(args.pixelFormat),
args.supportsBackgroundBlur) {}
SkiaVkRenderEngine::~SkiaVkRenderEngine() {
finishRenderingAndAbandonContext();
}
SkiaRenderEngine::Contexts SkiaVkRenderEngine::createDirectContexts(
const GrContextOptions& options) {
sSetupVulkanInterface();
SkiaRenderEngine::Contexts contexts;
contexts.first = GrDirectContexts::MakeVulkan(sVulkanInterface.getBackendContext(), options);
if (supportsProtectedContentImpl()) {
contexts.second =
GrDirectContexts::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