| // Copyright 2017 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "src/ui/lib/escher/vk/vulkan_instance.h" |
| |
| #include <lib/syslog/cpp/macros.h> |
| |
| #include <set> |
| |
| #include "src/ui/lib/escher/impl/vulkan_utils.h" |
| |
| namespace escher { |
| |
| template <typename FuncT> |
| static FuncT GetInstanceProcAddr(vk::Instance inst, const char *func_name) { |
| FuncT func = reinterpret_cast<FuncT>(inst.getProcAddr(func_name)); |
| FX_CHECK(func) << "Could not find Vulkan Instance ProcAddr: " << func_name; |
| return func; |
| } |
| |
| #define GET_INSTANCE_PROC_ADDR(XXX) vk##XXX = GetInstanceProcAddr<PFN_vk##XXX>(instance, "vk" #XXX) |
| |
| VulkanInstance::ProcAddrs::ProcAddrs(vk::Instance instance, bool requires_surface) { |
| GET_INSTANCE_PROC_ADDR(CreateDebugReportCallbackEXT); |
| GET_INSTANCE_PROC_ADDR(DestroyDebugReportCallbackEXT); |
| if (requires_surface) { |
| GET_INSTANCE_PROC_ADDR(GetPhysicalDeviceSurfaceSupportKHR); |
| } |
| } |
| |
| fxl::RefPtr<VulkanInstance> VulkanInstance::New(Params params) { |
| params.extension_names.insert(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME); |
| #ifdef __Fuchsia__ |
| // TODO(fxbug.dev/7234): It's quite possible that this would work on Linux if we |
| // uploaded a new Vulkan SDK to the cloud, but there are obstacles to doing |
| // this immediately, hence this workaround. Or, it may be the NVIDIA Vulkan |
| // driver itself. |
| params.extension_names.insert(VK_KHR_EXTERNAL_SEMAPHORE_CAPABILITIES_EXTENSION_NAME); |
| #endif |
| FX_DCHECK(ValidateLayers(params.layer_names)); |
| FX_DCHECK(ValidateExtensions(params.extension_names, params.layer_names)); |
| |
| // Gather names of layers/extensions to populate InstanceCreateInfo. |
| std::vector<const char *> layer_names; |
| for (auto &layer : params.layer_names) { |
| layer_names.push_back(layer.c_str()); |
| } |
| std::vector<const char *> extension_names; |
| for (auto &extension : params.extension_names) { |
| extension_names.push_back(extension.c_str()); |
| } |
| |
| uint32_t api_version = 0; |
| if (vkEnumerateInstanceVersion(&api_version) != VK_SUCCESS) { |
| api_version = VK_API_VERSION_1_0; |
| } |
| |
| vk::ApplicationInfo app_info; |
| app_info.pApplicationName = "Escher"; |
| app_info.apiVersion = api_version; |
| |
| vk::InstanceCreateInfo info; |
| info.pApplicationInfo = &app_info; |
| info.enabledLayerCount = static_cast<uint32_t>(layer_names.size()); |
| info.ppEnabledLayerNames = layer_names.data(); |
| info.enabledExtensionCount = static_cast<uint32_t>(extension_names.size()); |
| info.ppEnabledExtensionNames = extension_names.data(); |
| |
| auto result = vk::createInstance(info); |
| if (result.result != vk::Result::eSuccess) { |
| FX_LOGS(WARNING) << "Could not create Vulkan Instance: " << vk::to_string(result.result) << "."; |
| return fxl::RefPtr<VulkanInstance>(); |
| } |
| |
| return fxl::AdoptRef(new VulkanInstance(result.value, std::move(params), api_version)); |
| } |
| |
| VulkanInstance::VulkanInstance(vk::Instance instance, Params params, uint32_t api_version) |
| : instance_(instance), |
| params_(std::move(params)), |
| proc_addrs_(instance_, params_.requires_surface), |
| api_version_(api_version) { |
| // Register global debug report callback function. |
| // Do this only if extension VK_EXT_debug_report is enabled. |
| if (HasDebugReportExt()) { |
| auto callback_flags = vk::DebugReportFlagBitsEXT::eError | |
| vk::DebugReportFlagBitsEXT::eWarning | |
| vk::DebugReportFlagBitsEXT::ePerformanceWarning; |
| vk::DebugReportCallbackCreateInfoEXT create_info(callback_flags, DebugReportCallbackEntrance, |
| this); |
| auto create_callback_result = |
| vk_instance().createDebugReportCallbackEXT(create_info, nullptr, proc_addrs()); |
| FX_CHECK(create_callback_result.result == vk::Result::eSuccess); |
| vk_callback_entrance_handle_ = create_callback_result.value; |
| } |
| } |
| |
| VulkanInstance::~VulkanInstance() { |
| // Unregister global debug report callback function. |
| // Do this only if extension VK_EXT_debug_report is enabled. |
| if (HasDebugReportExt()) { |
| vk_instance().destroyDebugReportCallbackEXT(vk_callback_entrance_handle_, nullptr, |
| proc_addrs()); |
| } |
| instance_.destroy(); |
| } |
| |
| std::optional<std::string> VulkanInstance::GetValidationLayerName() { |
| const std::string kDeprecatedLayerName = "VK_LAYER_LUNARG_standard_validation"; |
| const std::string kNewLayerName = "VK_LAYER_KHRONOS_validation"; |
| |
| return ValidateLayers({kNewLayerName}) ? std::make_optional(kNewLayerName) |
| : ValidateLayers({kDeprecatedLayerName}) ? std::make_optional(kDeprecatedLayerName) |
| : std::nullopt; |
| } |
| |
| bool VulkanInstance::ValidateLayers(const std::set<std::string> &required_layer_names) { |
| auto properties = ESCHER_CHECKED_VK_RESULT(vk::enumerateInstanceLayerProperties()); |
| |
| for (auto &name : required_layer_names) { |
| auto found = |
| std::find_if(properties.begin(), properties.end(), [&name](vk::LayerProperties &layer) { |
| return !strncmp(layer.layerName, name.c_str(), VK_MAX_EXTENSION_NAME_SIZE); |
| }); |
| if (found == properties.end()) { |
| FX_LOGS(WARNING) << "Vulkan has no instance layer named: " << name; |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| // Helper for ValidateExtensions(). |
| static bool ValidateExtension(const std::string name, |
| const std::vector<vk::ExtensionProperties> &base_extensions, |
| const std::set<std::string> &required_layer_names) { |
| auto found = std::find_if(base_extensions.begin(), base_extensions.end(), |
| [&name](const vk::ExtensionProperties &extension) { |
| return !strncmp(extension.extensionName, name.c_str(), |
| VK_MAX_EXTENSION_NAME_SIZE); |
| }); |
| if (found != base_extensions.end()) |
| return true; |
| |
| // Didn't find the extension in the base list of extensions. Perhaps it is |
| // implemented in a layer. |
| for (auto &layer_name : required_layer_names) { |
| auto layer_extensions = |
| ESCHER_CHECKED_VK_RESULT(vk::enumerateInstanceExtensionProperties(layer_name)); |
| FX_LOGS(INFO) << "Looking for Vulkan instance extension: " << name |
| << " in layer: " << layer_name; |
| |
| auto found = std::find_if(layer_extensions.begin(), layer_extensions.end(), |
| [&name](vk::ExtensionProperties &extension) { |
| return !strncmp(extension.extensionName, name.c_str(), |
| VK_MAX_EXTENSION_NAME_SIZE); |
| }); |
| if (found != layer_extensions.end()) |
| return true; |
| } |
| |
| return false; |
| } |
| |
| bool VulkanInstance::ValidateExtensions(const std::set<std::string> &required_extension_names, |
| const std::set<std::string> &required_layer_names) { |
| auto extensions = ESCHER_CHECKED_VK_RESULT(vk::enumerateInstanceExtensionProperties()); |
| |
| for (auto &name : required_extension_names) { |
| if (!ValidateExtension(name, extensions, required_layer_names)) { |
| FX_LOGS(WARNING) << "Vulkan has no instance extension named: " << name; |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| VulkanInstance::DebugReportCallbackHandle VulkanInstance::RegisterDebugReportCallback( |
| VkDebugReportCallbackFn function, void *user_data) { |
| callbacks_.emplace_back(std::move(function), user_data); |
| return std::prev(callbacks_.end()); |
| } |
| |
| void VulkanInstance::DeregisterDebugReportCallback( |
| const VulkanInstance::DebugReportCallbackHandle &handle) { |
| callbacks_.erase(handle); |
| } |
| |
| VkBool32 VulkanInstance::DebugReportCallbackEntrance(VkDebugReportFlagsEXT flags, |
| VkDebugReportObjectTypeEXT objectType, |
| uint64_t object, size_t location, |
| int32_t messageCode, const char *pLayerPrefix, |
| const char *pMessage, void *pUserData) { |
| auto *instance_ptr = reinterpret_cast<VulkanInstance *>(pUserData); |
| for (const auto &callback : instance_ptr->callbacks_) { |
| callback.function(flags, objectType, object, location, messageCode, pLayerPrefix, pMessage, |
| callback.user_data); |
| } |
| return VK_FALSE; |
| } |
| |
| }; // namespace escher |