/* * Copyright (c) 2019-2022 Arm Limited. * * SPDX-License-Identifier: MIT * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ /** * @file * @brief Implements factory methods for obtaining the specific surface and swapchain implementations. */ #include "wsi_factory.hpp" #include "surface.hpp" #if BUILD_WSI_HEADLESS #include "headless/surface_properties.hpp" #endif #include #include #include #include #include #if BUILD_WSI_WAYLAND #include #include "wayland/surface_properties.hpp" #endif namespace wsi { static struct wsi_extension { VkExtensionProperties extension; VkIcdWsiPlatform platform; } const supported_wsi_extensions[] = { #if BUILD_WSI_HEADLESS { { VK_EXT_HEADLESS_SURFACE_EXTENSION_NAME, VK_EXT_HEADLESS_SURFACE_SPEC_VERSION }, VK_ICD_WSI_PLATFORM_HEADLESS }, #endif #if BUILD_WSI_WAYLAND { { VK_KHR_WAYLAND_SURFACE_EXTENSION_NAME, VK_KHR_WAYLAND_SURFACE_SPEC_VERSION }, VK_ICD_WSI_PLATFORM_WAYLAND }, #endif }; static surface_properties *get_surface_properties(VkIcdWsiPlatform platform) { switch (platform) { #if BUILD_WSI_HEADLESS case VK_ICD_WSI_PLATFORM_HEADLESS: return &headless::surface_properties::get_instance(); #endif #if BUILD_WSI_WAYLAND case VK_ICD_WSI_PLATFORM_WAYLAND: return &wayland::surface_properties::get_instance(); #endif default: return nullptr; } } surface_properties *get_surface_properties(layer::instance_private_data &instance_data, VkSurfaceKHR surface) { auto *wsi_surface = instance_data.get_surface(surface); if (wsi_surface) { return &wsi_surface->get_properties(); } return nullptr; } util::unique_ptr allocate_surface_swapchain(VkSurfaceKHR surface, layer::device_private_data &dev_data, const VkAllocationCallbacks *pAllocator) { wsi::surface *wsi_surface = dev_data.instance_data.get_surface(surface); if (wsi_surface) { return wsi_surface->allocate_swapchain(dev_data, pAllocator); } return nullptr; } util::wsi_platform_set find_enabled_layer_platforms(const VkInstanceCreateInfo *pCreateInfo) { util::wsi_platform_set ret; for (const auto &ext_provided_by_layer : supported_wsi_extensions) { for (uint32_t i = 0; i < pCreateInfo->enabledExtensionCount; i++) { const char* ext_requested_by_user = pCreateInfo->ppEnabledExtensionNames[i]; if (strcmp(ext_requested_by_user, ext_provided_by_layer.extension.extensionName) == 0) { ret.add(ext_provided_by_layer.platform); } } } return ret; } static VkResult get_available_device_extensions(VkPhysicalDevice physical_device, util::extension_list &available_extensions) { auto &instance_data = layer::instance_private_data::get(physical_device); util::vector properties{available_extensions.get_allocator()}; uint32_t count; TRY(instance_data.disp.EnumerateDeviceExtensionProperties(physical_device, nullptr, &count, nullptr)); if (!properties.try_resize(count)) { return VK_ERROR_OUT_OF_HOST_MEMORY; } TRY(instance_data.disp.EnumerateDeviceExtensionProperties(physical_device, nullptr, &count, properties.data())); TRY(available_extensions.add(properties.data(), count)); return VK_SUCCESS; } VkResult add_extensions_required_by_layer(VkPhysicalDevice phys_dev, const util::wsi_platform_set enabled_platforms, util::extension_list &extensions_to_enable) { util::allocator allocator{extensions_to_enable.get_allocator(), VK_SYSTEM_ALLOCATION_SCOPE_COMMAND}; util::extension_list available_device_extensions{allocator}; TRY(get_available_device_extensions(phys_dev, available_device_extensions)); /* Add optional extensions independent of winsys. */ { const char *optional_extensions[] = { VK_KHR_EXTERNAL_FENCE_CAPABILITIES_EXTENSION_NAME, VK_KHR_EXTERNAL_FENCE_EXTENSION_NAME, VK_KHR_EXTERNAL_FENCE_FD_EXTENSION_NAME, VK_KHR_EXTERNAL_SEMAPHORE_CAPABILITIES_EXTENSION_NAME, VK_KHR_EXTERNAL_SEMAPHORE_EXTENSION_NAME, VK_KHR_EXTERNAL_SEMAPHORE_FD_EXTENSION_NAME, VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME, }; for (auto extension : optional_extensions) { if (available_device_extensions.contains(extension)) { TRY(extensions_to_enable.add(extension)); } } } for (const auto &wsi_ext : supported_wsi_extensions) { /* Skip iterating over platforms not enabled in the instance. */ if (!enabled_platforms.contains(wsi_ext.platform)) { continue; } util::extension_list extensions_required_by_layer{allocator}; surface_properties *props = get_surface_properties(wsi_ext.platform); if (props == nullptr) { return VK_ERROR_INITIALIZATION_FAILED; } TRY(props->get_required_device_extensions(extensions_required_by_layer)); bool supported = available_device_extensions.contains(extensions_required_by_layer); if (!supported) { /* Can we accept failure? The layer unconditionally advertises support for this platform and the loader uses * this information to enable its own support of the vkCreate*SurfaceKHR entrypoints. The rest of the Vulkan * stack may not support this extension so we cannot blindly fall back to it. * For now treat this as an error. */ return VK_ERROR_INITIALIZATION_FAILED; } TRY(extensions_to_enable.add(extensions_required_by_layer)); } return VK_SUCCESS; } void destroy_surface_swapchain(swapchain_base *swapchain, layer::device_private_data &dev_data, const VkAllocationCallbacks *pAllocator) { assert(swapchain); util::allocator alloc{ dev_data.get_allocator(), VK_SYSTEM_ALLOCATION_SCOPE_OBJECT, pAllocator }; alloc.destroy(1, swapchain); } PFN_vkVoidFunction get_proc_addr(const char *name, const layer::instance_private_data &instance_data) { /* * Note that we here assume that there are no two get_proc_addr implementations * that handle the same function name. */ for (const auto &wsi_ext : supported_wsi_extensions) { surface_properties *props = get_surface_properties(wsi_ext.platform); if (props == nullptr) { return nullptr; } PFN_vkVoidFunction func = props->get_proc_addr(name); if (props->is_surface_extension_enabled(instance_data) && func) { return func; } } return nullptr; } } // namespace wsi