in tools/gpu/vk/VkTestUtils.cpp [603:961]
bool CreateVkBackendContext(PFN_vkGetInstanceProcAddr getInstProc,
skgpu::VulkanBackendContext* ctx,
skgpu::VulkanExtensions* extensions,
VkPhysicalDeviceFeatures2* features,
VkDebugReportCallbackEXT* debugCallback,
uint32_t* presentQueueIndexPtr,
const CanPresentFn& canPresent,
bool isProtected) {
VkResult err;
ACQUIRE_VK_INST_PROC_NOCHECK(EnumerateInstanceVersion, VK_NULL_HANDLE);
uint32_t instanceVersion = 0;
if (!grVkEnumerateInstanceVersion) {
instanceVersion = VK_MAKE_VERSION(1, 0, 0);
} else {
err = grVkEnumerateInstanceVersion(&instanceVersion);
if (err) {
SkDebugf("failed to enumerate instance version. Err: %d\n", err);
return false;
}
}
SkASSERT(instanceVersion >= VK_MAKE_VERSION(1, 0, 0));
if (isProtected && instanceVersion < VK_MAKE_VERSION(1, 1, 0)) {
SkDebugf("protected requires vk instance version 1.1\n");
return false;
}
uint32_t apiVersion = VK_MAKE_VERSION(1, 0, 0);
if (instanceVersion >= VK_MAKE_VERSION(1, 1, 0)) {
// If the instance version is 1.0 we must have the apiVersion also be 1.0. However, if the
// instance version is 1.1 or higher, we can set the apiVersion to be whatever the highest
// api we may use in skia (technically it can be arbitrary). So for now we set it to 1.1
// since that is the highest vulkan version.
apiVersion = VK_MAKE_VERSION(1, 1, 0);
}
instanceVersion = std::min(instanceVersion, apiVersion);
STArray<2, VkPhysicalDevice> physDevs;
VkDevice device;
VkInstance inst = VK_NULL_HANDLE;
const VkApplicationInfo app_info = {
VK_STRUCTURE_TYPE_APPLICATION_INFO, // sType
nullptr, // pNext
"vktest", // pApplicationName
0, // applicationVersion
"vktest", // pEngineName
0, // engineVerison
apiVersion, // apiVersion
};
TArray<VkLayerProperties> instanceLayers;
TArray<VkExtensionProperties> instanceExtensions;
if (!init_instance_extensions_and_layers(getInstProc, instanceVersion,
&instanceExtensions,
&instanceLayers)) {
return false;
}
TArray<const char*> instanceLayerNames;
TArray<const char*> instanceExtensionNames;
for (int i = 0; i < instanceLayers.size(); ++i) {
instanceLayerNames.push_back(instanceLayers[i].layerName);
}
for (int i = 0; i < instanceExtensions.size(); ++i) {
instanceExtensionNames.push_back(instanceExtensions[i].extensionName);
}
const VkInstanceCreateInfo instance_create = {
VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO, // sType
nullptr, // pNext
0, // flags
&app_info, // pApplicationInfo
(uint32_t) instanceLayerNames.size(), // enabledLayerNameCount
instanceLayerNames.begin(), // ppEnabledLayerNames
(uint32_t) instanceExtensionNames.size(), // enabledExtensionNameCount
instanceExtensionNames.begin(), // ppEnabledExtensionNames
};
bool hasDebugExtension = false;
ACQUIRE_VK_INST_PROC(CreateInstance, VK_NULL_HANDLE);
err = grVkCreateInstance(&instance_create, nullptr, &inst);
if (err < 0) {
SkDebugf("vkCreateInstance failed: %d\n", err);
return false;
}
ACQUIRE_VK_INST_PROC(GetDeviceProcAddr, inst);
auto getProc = [getInstProc, grVkGetDeviceProcAddr](const char* proc_name,
VkInstance instance, VkDevice device) {
if (device != VK_NULL_HANDLE) {
return grVkGetDeviceProcAddr(device, proc_name);
}
return getInstProc(instance, proc_name);
};
#ifdef SK_ENABLE_VK_LAYERS
*debugCallback = VK_NULL_HANDLE;
for (int i = 0; i < instanceExtensionNames.size() && !hasDebugExtension; ++i) {
if (!strcmp(instanceExtensionNames[i], VK_EXT_DEBUG_REPORT_EXTENSION_NAME)) {
hasDebugExtension = true;
}
}
if (hasDebugExtension) {
// Setup callback creation information
VkDebugReportCallbackCreateInfoEXT callbackCreateInfo;
callbackCreateInfo.sType = VK_STRUCTURE_TYPE_DEBUG_REPORT_CREATE_INFO_EXT;
callbackCreateInfo.pNext = nullptr;
callbackCreateInfo.flags = VK_DEBUG_REPORT_ERROR_BIT_EXT |
VK_DEBUG_REPORT_WARNING_BIT_EXT |
// VK_DEBUG_REPORT_INFORMATION_BIT_EXT |
// VK_DEBUG_REPORT_DEBUG_BIT_EXT |
VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT;
callbackCreateInfo.pfnCallback = &DebugReportCallback;
callbackCreateInfo.pUserData = nullptr;
ACQUIRE_VK_PROC(CreateDebugReportCallbackEXT, inst, VK_NULL_HANDLE);
// Register the callback
grVkCreateDebugReportCallbackEXT(inst, &callbackCreateInfo, nullptr, debugCallback);
}
#endif
ACQUIRE_VK_PROC(EnumeratePhysicalDevices, inst, VK_NULL_HANDLE);
ACQUIRE_VK_PROC(GetPhysicalDeviceProperties, inst, VK_NULL_HANDLE);
ACQUIRE_VK_PROC(GetPhysicalDeviceQueueFamilyProperties, inst, VK_NULL_HANDLE);
ACQUIRE_VK_PROC(GetPhysicalDeviceFeatures, inst, VK_NULL_HANDLE);
ACQUIRE_VK_PROC(CreateDevice, inst, VK_NULL_HANDLE);
ACQUIRE_VK_PROC(GetDeviceQueue, inst, VK_NULL_HANDLE);
ACQUIRE_VK_PROC(DeviceWaitIdle, inst, VK_NULL_HANDLE);
ACQUIRE_VK_PROC(DestroyDevice, inst, VK_NULL_HANDLE);
uint32_t gpuCount;
err = grVkEnumeratePhysicalDevices(inst, &gpuCount, nullptr);
if (err) {
SkDebugf("vkEnumeratePhysicalDevices failed: %d\n", err);
destroy_instance(getInstProc, inst, debugCallback, hasDebugExtension);
return false;
}
if (!gpuCount) {
SkDebugf("vkEnumeratePhysicalDevices returned no supported devices.\n");
destroy_instance(getInstProc, inst, debugCallback, hasDebugExtension);
return false;
}
// Allocate enough storage for all available physical devices. We should be able to just ask for
// the first one, but a bug in RenderDoc (https://github.com/baldurk/renderdoc/issues/2766)
// will smash the stack if we do that.
physDevs.resize(gpuCount);
err = grVkEnumeratePhysicalDevices(inst, &gpuCount, physDevs.data());
if (err) {
SkDebugf("vkEnumeratePhysicalDevices failed: %d\n", err);
destroy_instance(getInstProc, inst, debugCallback, hasDebugExtension);
return false;
}
// We just use the first physical device.
// TODO: find best match for our needs
VkPhysicalDevice physDev = physDevs.front();
VkPhysicalDeviceProperties physDeviceProperties;
grVkGetPhysicalDeviceProperties(physDev, &physDeviceProperties);
uint32_t physDeviceVersion = std::min(physDeviceProperties.apiVersion, apiVersion);
if (isProtected && physDeviceVersion < VK_MAKE_VERSION(1, 1, 0)) {
SkDebugf("protected requires vk physical device version 1.1\n");
destroy_instance(getInstProc, inst, debugCallback, hasDebugExtension);
return false;
}
// query to get the initial queue props size
uint32_t queueCount;
grVkGetPhysicalDeviceQueueFamilyProperties(physDev, &queueCount, nullptr);
if (!queueCount) {
SkDebugf("vkGetPhysicalDeviceQueueFamilyProperties returned no queues.\n");
destroy_instance(getInstProc, inst, debugCallback, hasDebugExtension);
return false;
}
SkAutoMalloc queuePropsAlloc(queueCount * sizeof(VkQueueFamilyProperties));
// now get the actual queue props
VkQueueFamilyProperties* queueProps = (VkQueueFamilyProperties*)queuePropsAlloc.get();
grVkGetPhysicalDeviceQueueFamilyProperties(physDev, &queueCount, queueProps);
// iterate to find the graphics queue
uint32_t graphicsQueueIndex = queueCount;
for (uint32_t i = 0; i < queueCount; i++) {
if (queueProps[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) {
graphicsQueueIndex = i;
break;
}
}
if (graphicsQueueIndex == queueCount) {
SkDebugf("Could not find any supported graphics queues.\n");
destroy_instance(getInstProc, inst, debugCallback, hasDebugExtension);
return false;
}
// iterate to find the present queue, if needed
uint32_t presentQueueIndex = queueCount;
if (presentQueueIndexPtr && canPresent) {
for (uint32_t i = 0; i < queueCount; i++) {
if (canPresent(inst, physDev, i)) {
presentQueueIndex = i;
break;
}
}
if (presentQueueIndex == queueCount) {
SkDebugf("Could not find any supported present queues.\n");
destroy_instance(getInstProc, inst, debugCallback, hasDebugExtension);
return false;
}
*presentQueueIndexPtr = presentQueueIndex;
} else {
// Just setting this so we end up make a single queue for graphics since there was no
// request for a present queue.
presentQueueIndex = graphicsQueueIndex;
}
TArray<VkLayerProperties> deviceLayers;
TArray<VkExtensionProperties> deviceExtensions;
if (!init_device_extensions_and_layers(getProc, physDeviceVersion,
inst, physDev,
&deviceExtensions,
&deviceLayers)) {
destroy_instance(getInstProc, inst, debugCallback, hasDebugExtension);
return false;
}
TArray<const char*> deviceLayerNames;
TArray<const char*> deviceExtensionNames;
for (int i = 0; i < deviceLayers.size(); ++i) {
deviceLayerNames.push_back(deviceLayers[i].layerName);
}
for (int i = 0; i < deviceExtensions.size(); ++i) {
deviceExtensionNames.push_back(deviceExtensions[i].extensionName);
}
extensions->init(getProc, inst, physDev,
(uint32_t) instanceExtensionNames.size(),
instanceExtensionNames.begin(),
(uint32_t) deviceExtensionNames.size(),
deviceExtensionNames.begin());
memset(features, 0, sizeof(VkPhysicalDeviceFeatures2));
features->sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2;
features->pNext = nullptr;
VkPhysicalDeviceFeatures* deviceFeatures = &features->features;
void* pointerToFeatures = nullptr;
if (physDeviceVersion >= VK_MAKE_VERSION(1, 1, 0) ||
extensions->hasExtension(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME, 1)) {
if (!setup_features(getProc, inst, physDev, physDeviceVersion, extensions, features,
isProtected)) {
destroy_instance(getInstProc, inst, debugCallback, hasDebugExtension);
return false;
}
// If we set the pNext of the VkDeviceCreateInfo to our VkPhysicalDeviceFeatures2 struct,
// the device creation will use that instead of the ppEnabledFeatures.
pointerToFeatures = features;
} else {
grVkGetPhysicalDeviceFeatures(physDev, deviceFeatures);
}
// this looks like it would slow things down,
// and we can't depend on it on all platforms
deviceFeatures->robustBufferAccess = VK_FALSE;
VkDeviceQueueCreateFlags flags = isProtected ? VK_DEVICE_QUEUE_CREATE_PROTECTED_BIT : 0;
float queuePriorities[1] = { 0.0 };
// Here we assume no need for swapchain queue
// If one is needed, the client will need its own setup code
const VkDeviceQueueCreateInfo queueInfo[2] = {
{
VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO, // sType
nullptr, // pNext
flags, // VkDeviceQueueCreateFlags
graphicsQueueIndex, // queueFamilyIndex
1, // queueCount
queuePriorities, // pQueuePriorities
},
{
VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO, // sType
nullptr, // pNext
0, // VkDeviceQueueCreateFlags
presentQueueIndex, // queueFamilyIndex
1, // queueCount
queuePriorities, // pQueuePriorities
}
};
uint32_t queueInfoCount = (presentQueueIndex != graphicsQueueIndex) ? 2 : 1;
const VkDeviceCreateInfo deviceInfo = {
VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO, // sType
pointerToFeatures, // pNext
0, // VkDeviceCreateFlags
queueInfoCount, // queueCreateInfoCount
queueInfo, // pQueueCreateInfos
(uint32_t) deviceLayerNames.size(), // layerCount
deviceLayerNames.begin(), // ppEnabledLayerNames
(uint32_t) deviceExtensionNames.size(), // extensionCount
deviceExtensionNames.begin(), // ppEnabledExtensionNames
pointerToFeatures ? nullptr : deviceFeatures // ppEnabledFeatures
};
{
#if defined(SK_ENABLE_SCOPED_LSAN_SUPPRESSIONS)
// skia:8712
__lsan::ScopedDisabler lsanDisabler;
#endif
err = grVkCreateDevice(physDev, &deviceInfo, nullptr, &device);
}
if (err) {
SkDebugf("CreateDevice failed: %d\n", err);
destroy_instance(getInstProc, inst, debugCallback, hasDebugExtension);
return false;
}
VkQueue queue;
if (isProtected) {
ACQUIRE_VK_PROC(GetDeviceQueue2, inst, device);
SkASSERT(grVkGetDeviceQueue2 != nullptr);
VkDeviceQueueInfo2 queue_info2 = {
VK_STRUCTURE_TYPE_DEVICE_QUEUE_INFO_2, // sType
nullptr, // pNext
VK_DEVICE_QUEUE_CREATE_PROTECTED_BIT, // flags
graphicsQueueIndex, // queueFamilyIndex
0 // queueIndex
};
grVkGetDeviceQueue2(device, &queue_info2, &queue);
} else {
grVkGetDeviceQueue(device, graphicsQueueIndex, 0, &queue);
}
skgpu::VulkanInterface interface = skgpu::VulkanInterface(
getProc, inst, device, instanceVersion, physDeviceVersion, extensions);
SkASSERT(interface.validate(instanceVersion, physDeviceVersion, extensions));
sk_sp<skgpu::VulkanMemoryAllocator> memoryAllocator = VkTestMemoryAllocator::Make(
inst, physDev, device, physDeviceVersion, extensions, &interface);
ctx->fInstance = inst;
ctx->fPhysicalDevice = physDev;
ctx->fDevice = device;
ctx->fQueue = queue;
ctx->fGraphicsQueueIndex = graphicsQueueIndex;
ctx->fMaxAPIVersion = apiVersion;
ctx->fVkExtensions = extensions;
ctx->fDeviceFeatures2 = features;
ctx->fGetProc = getProc;
ctx->fProtectedContext = skgpu::Protected(isProtected);
ctx->fMemoryAllocator = memoryAllocator;
return true;
}