| /* |
| * Copyright 2015 Google Inc. |
| * |
| * Use of this source code is governed by a BSD-style license that can be |
| * found in the LICENSE file. |
| */ |
| |
| #include "SkAutoMalloc.h" |
| #include "vk/GrVkBackendContext.h" |
| #include "vk/GrVkExtensions.h" |
| #include "vk/GrVkInterface.h" |
| #include "vk/GrVkUtil.h" |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // Helper code to set up Vulkan context objects |
| |
| #ifdef SK_ENABLE_VK_LAYERS |
| const char* kDebugLayerNames[] = { |
| // elements of VK_LAYER_LUNARG_standard_validation |
| "VK_LAYER_GOOGLE_threading", |
| "VK_LAYER_LUNARG_parameter_validation", |
| "VK_LAYER_LUNARG_object_tracker", |
| "VK_LAYER_LUNARG_image", |
| "VK_LAYER_LUNARG_core_validation", |
| "VK_LAYER_LUNARG_swapchain", |
| "VK_LAYER_GOOGLE_unique_objects", |
| // not included in standard_validation |
| //"VK_LAYER_LUNARG_api_dump", |
| //"VK_LAYER_LUNARG_vktrace", |
| //"VK_LAYER_LUNARG_screenshot", |
| }; |
| #endif |
| |
| // the minimum version of Vulkan supported |
| #ifdef SK_BUILD_FOR_ANDROID |
| const uint32_t kGrVkMinimumVersion = VK_MAKE_VERSION(1, 0, 3); |
| #else |
| const uint32_t kGrVkMinimumVersion = VK_MAKE_VERSION(1, 0, 8); |
| #endif |
| |
| #define ACQUIRE_VK_PROC(name, instance, device) \ |
| PFN_vk##name grVk##name = \ |
| reinterpret_cast<PFN_vk##name>(getProc("vk" #name, instance, device)); \ |
| if (grVk##name == nullptr) { \ |
| SkDebugf("Function ptr for vk%s could not be acquired\n", #name); \ |
| return nullptr; \ |
| } |
| |
| // Create the base Vulkan objects needed by the GrVkGpu object |
| const GrVkBackendContext* GrVkBackendContext::Create(uint32_t* presentQueueIndexPtr, |
| CanPresentFn canPresent, |
| GrVkInterface::GetProc getProc) { |
| if (!getProc) { |
| return nullptr; |
| } |
| SkASSERT(getProc); |
| |
| VkPhysicalDevice physDev; |
| VkDevice device; |
| VkInstance inst; |
| VkResult err; |
| |
| const VkApplicationInfo app_info = { |
| VK_STRUCTURE_TYPE_APPLICATION_INFO, // sType |
| nullptr, // pNext |
| "vktest", // pApplicationName |
| 0, // applicationVersion |
| "vktest", // pEngineName |
| 0, // engineVerison |
| kGrVkMinimumVersion, // apiVersion |
| }; |
| |
| GrVkExtensions extensions(getProc); |
| extensions.initInstance(kGrVkMinimumVersion); |
| |
| SkTArray<const char*> instanceLayerNames; |
| SkTArray<const char*> instanceExtensionNames; |
| uint32_t extensionFlags = 0; |
| #ifdef SK_ENABLE_VK_LAYERS |
| for (size_t i = 0; i < SK_ARRAY_COUNT(kDebugLayerNames); ++i) { |
| if (extensions.hasInstanceLayer(kDebugLayerNames[i])) { |
| instanceLayerNames.push_back(kDebugLayerNames[i]); |
| } |
| } |
| if (extensions.hasInstanceExtension(VK_EXT_DEBUG_REPORT_EXTENSION_NAME)) { |
| instanceExtensionNames.push_back(VK_EXT_DEBUG_REPORT_EXTENSION_NAME); |
| extensionFlags |= kEXT_debug_report_GrVkExtensionFlag; |
| } |
| #endif |
| |
| if (extensions.hasInstanceExtension(VK_KHR_SURFACE_EXTENSION_NAME)) { |
| instanceExtensionNames.push_back(VK_KHR_SURFACE_EXTENSION_NAME); |
| extensionFlags |= kKHR_surface_GrVkExtensionFlag; |
| } |
| if (extensions.hasInstanceExtension(VK_KHR_SWAPCHAIN_EXTENSION_NAME)) { |
| instanceExtensionNames.push_back(VK_KHR_SWAPCHAIN_EXTENSION_NAME); |
| extensionFlags |= kKHR_swapchain_GrVkExtensionFlag; |
| } |
| #ifdef SK_BUILD_FOR_WIN |
| if (extensions.hasInstanceExtension(VK_KHR_WIN32_SURFACE_EXTENSION_NAME)) { |
| instanceExtensionNames.push_back(VK_KHR_WIN32_SURFACE_EXTENSION_NAME); |
| extensionFlags |= kKHR_win32_surface_GrVkExtensionFlag; |
| } |
| #elif defined(SK_BUILD_FOR_ANDROID) |
| if (extensions.hasInstanceExtension(VK_KHR_ANDROID_SURFACE_EXTENSION_NAME)) { |
| instanceExtensionNames.push_back(VK_KHR_ANDROID_SURFACE_EXTENSION_NAME); |
| extensionFlags |= kKHR_android_surface_GrVkExtensionFlag; |
| } |
| #elif defined(SK_BUILD_FOR_UNIX) && !defined(__Fuchsia__) |
| if (extensions.hasInstanceExtension(VK_KHR_XCB_SURFACE_EXTENSION_NAME)) { |
| instanceExtensionNames.push_back(VK_KHR_XCB_SURFACE_EXTENSION_NAME); |
| extensionFlags |= kKHR_xcb_surface_GrVkExtensionFlag; |
| } |
| #endif |
| |
| const VkInstanceCreateInfo instance_create = { |
| VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO, // sType |
| nullptr, // pNext |
| 0, // flags |
| &app_info, // pApplicationInfo |
| (uint32_t) instanceLayerNames.count(), // enabledLayerNameCount |
| instanceLayerNames.begin(), // ppEnabledLayerNames |
| (uint32_t) instanceExtensionNames.count(), // enabledExtensionNameCount |
| instanceExtensionNames.begin(), // ppEnabledExtensionNames |
| }; |
| |
| ACQUIRE_VK_PROC(CreateInstance, VK_NULL_HANDLE, VK_NULL_HANDLE); |
| err = grVkCreateInstance(&instance_create, nullptr, &inst); |
| if (err < 0) { |
| SkDebugf("vkCreateInstance failed: %d\n", err); |
| return nullptr; |
| } |
| |
| ACQUIRE_VK_PROC(DestroyInstance, inst, VK_NULL_HANDLE); |
| ACQUIRE_VK_PROC(EnumeratePhysicalDevices, 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); |
| grVkDestroyInstance(inst, nullptr); |
| return nullptr; |
| } |
| if (!gpuCount) { |
| SkDebugf("vkEnumeratePhysicalDevices returned no supported devices.\n"); |
| grVkDestroyInstance(inst, nullptr); |
| return nullptr; |
| } |
| // Just returning the first physical device instead of getting the whole array. |
| // TODO: find best match for our needs |
| gpuCount = 1; |
| err = grVkEnumeratePhysicalDevices(inst, &gpuCount, &physDev); |
| if (err) { |
| SkDebugf("vkEnumeratePhysicalDevices failed: %d\n", err); |
| grVkDestroyInstance(inst, nullptr); |
| return nullptr; |
| } |
| |
| // query to get the initial queue props size |
| uint32_t queueCount; |
| grVkGetPhysicalDeviceQueueFamilyProperties(physDev, &queueCount, nullptr); |
| if (!queueCount) { |
| SkDebugf("vkGetPhysicalDeviceQueueFamilyProperties returned no queues.\n"); |
| grVkDestroyInstance(inst, nullptr); |
| return nullptr; |
| } |
| |
| 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"); |
| grVkDestroyInstance(inst, nullptr); |
| return nullptr; |
| } |
| |
| // 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"); |
| grVkDestroyInstance(inst, nullptr); |
| return nullptr; |
| } |
| *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; |
| } |
| |
| extensions.initDevice(kGrVkMinimumVersion, inst, physDev); |
| |
| SkTArray<const char*> deviceLayerNames; |
| SkTArray<const char*> deviceExtensionNames; |
| #ifdef SK_ENABLE_VK_LAYERS |
| for (size_t i = 0; i < SK_ARRAY_COUNT(kDebugLayerNames); ++i) { |
| if (extensions.hasDeviceLayer(kDebugLayerNames[i])) { |
| deviceLayerNames.push_back(kDebugLayerNames[i]); |
| } |
| } |
| #endif |
| if (extensions.hasDeviceExtension(VK_KHR_SWAPCHAIN_EXTENSION_NAME)) { |
| deviceExtensionNames.push_back(VK_KHR_SWAPCHAIN_EXTENSION_NAME); |
| extensionFlags |= kKHR_swapchain_GrVkExtensionFlag; |
| } |
| if (extensions.hasDeviceExtension("VK_NV_glsl_shader")) { |
| deviceExtensionNames.push_back("VK_NV_glsl_shader"); |
| extensionFlags |= kNV_glsl_shader_GrVkExtensionFlag; |
| } |
| |
| // query to get the physical device properties |
| VkPhysicalDeviceFeatures deviceFeatures; |
| 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; |
| |
| uint32_t featureFlags = 0; |
| if (deviceFeatures.geometryShader) { |
| featureFlags |= kGeometryShader_GrVkFeatureFlag; |
| } |
| if (deviceFeatures.dualSrcBlend) { |
| featureFlags |= kDualSrcBlend_GrVkFeatureFlag; |
| } |
| if (deviceFeatures.sampleRateShading) { |
| featureFlags |= kSampleRateShading_GrVkFeatureFlag; |
| } |
| |
| 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 |
| 0, // 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 |
| nullptr, // pNext |
| 0, // VkDeviceCreateFlags |
| queueInfoCount, // queueCreateInfoCount |
| queueInfo, // pQueueCreateInfos |
| (uint32_t) deviceLayerNames.count(), // layerCount |
| deviceLayerNames.begin(), // ppEnabledLayerNames |
| (uint32_t) deviceExtensionNames.count(), // extensionCount |
| deviceExtensionNames.begin(), // ppEnabledExtensionNames |
| &deviceFeatures // ppEnabledFeatures |
| }; |
| |
| err = grVkCreateDevice(physDev, &deviceInfo, nullptr, &device); |
| if (err) { |
| SkDebugf("CreateDevice failed: %d\n", err); |
| grVkDestroyInstance(inst, nullptr); |
| return nullptr; |
| } |
| |
| auto interface = |
| sk_make_sp<GrVkInterface>(getProc, inst, device, extensionFlags); |
| if (!interface->validate(extensionFlags)) { |
| SkDebugf("Vulkan interface validation failed\n"); |
| grVkDeviceWaitIdle(device); |
| grVkDestroyDevice(device, nullptr); |
| grVkDestroyInstance(inst, nullptr); |
| return nullptr; |
| } |
| |
| VkQueue queue; |
| grVkGetDeviceQueue(device, graphicsQueueIndex, 0, &queue); |
| |
| GrVkBackendContext* ctx = new GrVkBackendContext(); |
| ctx->fInstance = inst; |
| ctx->fPhysicalDevice = physDev; |
| ctx->fDevice = device; |
| ctx->fQueue = queue; |
| ctx->fGraphicsQueueIndex = graphicsQueueIndex; |
| ctx->fMinAPIVersion = kGrVkMinimumVersion; |
| ctx->fExtensions = extensionFlags; |
| ctx->fFeatures = featureFlags; |
| ctx->fInterface.reset(interface.release()); |
| ctx->fOwnsInstanceAndDevice = true; |
| |
| return ctx; |
| } |
| |
| GrVkBackendContext::~GrVkBackendContext() { |
| if (fInterface == nullptr || !fOwnsInstanceAndDevice) { |
| return; |
| } |
| |
| fInterface->fFunctions.fDeviceWaitIdle(fDevice); |
| fInterface->fFunctions.fDestroyDevice(fDevice, nullptr); |
| fDevice = VK_NULL_HANDLE; |
| fInterface->fFunctions.fDestroyInstance(fInstance, nullptr); |
| fInstance = VK_NULL_HANDLE; |
| } |