Implement objects associated with VkSurface
authorRosen Zhelev <rosen.zhelev@arm.com>
Wed, 21 Jul 2021 19:19:52 +0000 (20:19 +0100)
committerRosen Zhelev <rosen.zhelev@arm.com>
Tue, 17 Aug 2021 15:42:21 +0000 (16:42 +0100)
Defines an abstract wsi::surface object to be implemented by each WSI
backend. This object is then associated with the corresponding VkSurface
object in the instance specific data.

To keep track of these objects the layer now intercepts
vkDestroySurfaceKHR, while specific surface creation entrypoints are
intercepted by individual WSI backends.

In addition this change should allow for fixing incompatibility issues
with layers that wrap VkSurface which breaks casting to the Loader's ICD
VkSurface type.

Make greater use of util::unique_ptr and allow for such pointers to be
constructable when casting derived types.

Change-Id: I163e9c47088ad9989583ebce1319b1fc05559d73
Signed-off-by: Rosen Zhelev <rosen.zhelev@arm.com>
19 files changed:
CMakeLists.txt
layer/layer.cpp
layer/private_data.cpp
layer/private_data.hpp
layer/surface_api.cpp
layer/surface_api.hpp
layer/swapchain_api.cpp
util/custom_allocator.hpp
wsi/headless/surface.cpp [new file with mode: 0644]
wsi/headless/surface.hpp [new file with mode: 0644]
wsi/headless/surface_properties.cpp
wsi/headless/surface_properties.hpp
wsi/surface.hpp [new file with mode: 0644]
wsi/surface_properties.hpp
wsi/wayland/surface.cpp [new file with mode: 0644]
wsi/wayland/surface.hpp [new file with mode: 0644]
wsi/wayland/surface_properties.cpp
wsi/wsi_factory.cpp
wsi/wsi_factory.hpp

index 031142d..4a84c88 100644 (file)
@@ -96,6 +96,7 @@ endif()
 if(BUILD_WSI_WAYLAND)
    add_library(wayland_wsi STATIC
       wsi/wayland/surface_properties.cpp
+      wsi/wayland/surface.cpp
       wsi/wayland/wl_helpers.cpp
       wsi/wayland/swapchain.cpp)
 
@@ -153,6 +154,7 @@ add_library(${PROJECT_NAME} SHARED
    wsi/swapchain_base.cpp
    wsi/wsi_factory.cpp
    wsi/headless/surface_properties.cpp
+   wsi/headless/surface.cpp
    wsi/headless/swapchain.cpp)
 target_compile_definitions(${PROJECT_NAME} PRIVATE ${WSI_DEFINES})
 target_include_directories(${PROJECT_NAME} PRIVATE
index cff12b5..9140131 100644 (file)
@@ -461,6 +461,7 @@ VK_LAYER_EXPORT VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL wsi_layer_vkGetInstance
    GET_PROC_ADDR(vkGetPhysicalDeviceSurfaceCapabilitiesKHR);
    GET_PROC_ADDR(vkGetPhysicalDeviceSurfaceFormatsKHR);
    GET_PROC_ADDR(vkGetPhysicalDeviceSurfacePresentModesKHR);
+   GET_PROC_ADDR(vkDestroySurfaceKHR);
    GET_PROC_ADDR(vkEnumerateDeviceExtensionProperties);
    GET_PROC_ADDR(vkEnumerateInstanceLayerProperties);
 
index c4e9a3d..b47e7a7 100644 (file)
@@ -24,6 +24,7 @@
 
 #include "private_data.hpp"
 #include "wsi/wsi_factory.hpp"
+#include "wsi/surface.hpp"
 #include "util/unordered_map.hpp"
 #include "util/log.hpp"
 
@@ -81,6 +82,7 @@ instance_private_data::instance_private_data(const instance_dispatch_table &tabl
    , SetInstanceLoaderData(set_loader_data)
    , enabled_layer_platforms(enabled_layer_platforms)
    , allocator(alloc)
+   , surfaces(alloc)
 {
 }
 
@@ -172,15 +174,60 @@ instance_private_data &instance_private_data::get(VkPhysicalDevice phys_dev)
    return get_instance_private_data(phys_dev);
 }
 
-static VkIcdWsiPlatform get_platform_of_surface(VkSurfaceKHR surface)
+VkResult instance_private_data::add_surface(VkSurfaceKHR vk_surface, util::unique_ptr<wsi::surface> &wsi_surface)
 {
-   VkIcdSurfaceBase *surface_base = reinterpret_cast<VkIcdSurfaceBase *>(surface);
-   return surface_base->platform;
+   scoped_mutex lock(surfaces_lock);
+
+   auto it = surfaces.find(vk_surface);
+   if (it != surfaces.end())
+   {
+      WSI_LOG_WARNING("Hash collision when adding new surface (%p). Old surface is replaced.",
+                      reinterpret_cast<void *>(vk_surface));
+      surfaces.erase(it);
+   }
+
+   auto result = surfaces.try_insert(std::make_pair(vk_surface, nullptr));
+   if (result.has_value())
+   {
+      assert(result->second);
+      result->first->second = wsi_surface.release();
+      return VK_SUCCESS;
+   }
+
+   return VK_ERROR_OUT_OF_HOST_MEMORY;
+}
+
+wsi::surface *instance_private_data::get_surface(VkSurfaceKHR vk_surface)
+{
+   scoped_mutex lock(surfaces_lock);
+   auto it = surfaces.find(vk_surface);
+   if (it != surfaces.end())
+   {
+      return it->second;
+   }
+
+   return nullptr;
+}
+
+void instance_private_data::remove_surface(VkSurfaceKHR vk_surface, const util::allocator &alloc)
+{
+   scoped_mutex lock(surfaces_lock);
+   auto it = surfaces.find(vk_surface);
+   if (it != surfaces.end())
+   {
+      alloc.destroy<wsi::surface>(1, it->second);
+      surfaces.erase(it);
+   }
+   /* Failing to find a surface is not an error. It could have been created by a WSI extension, which is not handled
+    * by this layer.
+    */
 }
 
 bool instance_private_data::does_layer_support_surface(VkSurfaceKHR surface)
 {
-   return enabled_layer_platforms.contains(get_platform_of_surface(surface));
+   scoped_mutex lock(surfaces_lock);
+   auto it = surfaces.find(surface);
+   return it != surfaces.end();
 }
 
 void instance_private_data::destroy(instance_private_data *instance_data)
index 205112c..eef8e35 100644 (file)
 #include "util/platform_set.hpp"
 #include "util/custom_allocator.hpp"
 #include "util/unordered_set.hpp"
+#include "util/unordered_map.hpp"
 
 #include <vulkan/vulkan.h>
 #include <vulkan/vk_layer.h>
 #include <vulkan/vk_icd.h>
+#include <vulkan/vulkan_wayland.h>
 
 #include <memory>
 #include <unordered_set>
 
 using scoped_mutex = std::lock_guard<std::mutex>;
 
+/** Forward declare stored objects */
+namespace wsi
+{
+class surface;
+}
+
 namespace layer
 {
 
@@ -57,6 +65,9 @@ namespace layer
    OPTIONAL(GetPhysicalDeviceSurfaceFormatsKHR)         \
    OPTIONAL(GetPhysicalDeviceSurfacePresentModesKHR)    \
    OPTIONAL(GetPhysicalDeviceSurfaceSupportKHR)         \
+   OPTIONAL(CreateHeadlessSurfaceEXT)                   \
+   OPTIONAL(CreateWaylandSurfaceKHR)                    \
+   OPTIONAL(DestroySurfaceKHR)                          \
    OPTIONAL(GetPhysicalDeviceImageFormatProperties2KHR) \
    OPTIONAL(GetPhysicalDeviceFormatProperties2KHR)      \
    OPTIONAL(GetPhysicalDevicePresentRectanglesKHR)
@@ -191,6 +202,39 @@ public:
    static instance_private_data &get(VkPhysicalDevice phys_dev);
 
    /**
+    * @brief Associate a VkSurface with a WSI surface object.
+    *
+    * @param vk_surface  The VkSurface object created by the Vulkan implementation.
+    * @param wsi_surface The WSI layer object representing the surface.
+    *
+    * @return VK_SUCCESS or VK_ERROR_OUT_OF_HOST_MEMORY
+    *
+    * @note On success this transfers ownership of the WSI surface. The WSI surface is then explicitly destroyed by the
+    *       user with @ref remove_surface
+    */
+   VkResult add_surface(VkSurfaceKHR vk_surface, util::unique_ptr<wsi::surface> &wsi_surface);
+
+   /**
+    * @brief Returns any associated WSI surface to the VkSurface.
+    *
+    * @param vk_surface The VkSurface object queried for association.
+    *
+    * @return nullptr or a raw pointer to the WSI surface.
+    *
+    * @note This returns a raw pointer that does not change any ownership. The user is responsible for ensuring that the
+    *       pointer is valid as it explicitly controls the lifetime of the object.
+    */
+   wsi::surface *get_surface(VkSurfaceKHR vk_surface);
+
+   /**
+    * @brief Destroys any VkSurface associated WSI surface.
+    *
+    * @param vk_surface The VkSurface to check for associations.
+    * @param alloc      The allocator to use if destroying a @ref wsi::surface object.
+    */
+   void remove_surface(VkSurfaceKHR vk_surface, const util::allocator &alloc);
+
+   /**
     * @brief Get the set of enabled platforms that are also supported by the layer.
     */
    const util::wsi_platform_set &get_enabled_platforms()
@@ -263,6 +307,19 @@ private:
    const PFN_vkSetInstanceLoaderData SetInstanceLoaderData;
    const util::wsi_platform_set enabled_layer_platforms;
    const util::allocator allocator;
+
+   /**
+    * @brief Container for all VkSurface objects tracked and supported by the Layer's WSI implementation.
+    *
+    * Uses plain pointers to store surface data as the lifetime of the object is explicitly controlled by the Vulkan
+    * application. The application may also use different but compatible host allocators on creation and destruction.
+    */
+   util::unordered_map<VkSurfaceKHR, wsi::surface *> surfaces;
+
+   /**
+    * @brief Lock for thread safe access to @ref surfaces
+    */
+   std::mutex surfaces_lock;
 };
 
 /**
index 98c0613..7ee582a 100644 (file)
@@ -39,7 +39,7 @@ VKAPI_ATTR VkResult wsi_layer_vkGetPhysicalDeviceSurfaceCapabilitiesKHR(VkPhysic
    auto &instance = layer::instance_private_data::get(physicalDevice);
    if (instance.should_layer_handle_surface(physicalDevice, surface))
    {
-      wsi::surface_properties *props = wsi::get_surface_properties(surface);
+      wsi::surface_properties *props = wsi::get_surface_properties(instance, surface);
       assert(props != nullptr);
       return props->get_surface_capabilities(physicalDevice, surface, pSurfaceCapabilities);
    }
@@ -61,7 +61,7 @@ VKAPI_ATTR VkResult wsi_layer_vkGetPhysicalDeviceSurfaceFormatsKHR(VkPhysicalDev
    auto &instance = layer::instance_private_data::get(physicalDevice);
    if (instance.should_layer_handle_surface(physicalDevice, surface))
    {
-      wsi::surface_properties *props = wsi::get_surface_properties(surface);
+      wsi::surface_properties *props = wsi::get_surface_properties(instance, surface);
       assert(props != nullptr);
       return props->get_surface_formats(physicalDevice, surface, pSurfaceFormatCount, pSurfaceFormats);
    }
@@ -81,7 +81,7 @@ VKAPI_ATTR VkResult wsi_layer_vkGetPhysicalDeviceSurfacePresentModesKHR(VkPhysic
    auto &instance = layer::instance_private_data::get(physicalDevice);
    if (instance.should_layer_handle_surface(physicalDevice, surface))
    {
-      wsi::surface_properties *props = wsi::get_surface_properties(surface);
+      wsi::surface_properties *props = wsi::get_surface_properties(instance, surface);
       assert(props != nullptr);
       return props->get_surface_present_modes(physicalDevice, surface, pPresentModeCount, pPresentModes);
    }
@@ -107,4 +107,15 @@ VKAPI_ATTR VkResult wsi_layer_vkGetPhysicalDeviceSurfaceSupportKHR(VkPhysicalDev
    return instance.disp.GetPhysicalDeviceSurfaceSupportKHR(physicalDevice, queueFamilyIndex, surface, pSupported);
 }
 
+VKAPI_ATTR void wsi_layer_vkDestroySurfaceKHR(VkInstance instance, VkSurfaceKHR surface,
+                                              const VkAllocationCallbacks *pAllocator)
+{
+   auto &instance_data = layer::instance_private_data::get(instance);
+
+   instance_data.disp.DestroySurfaceKHR(instance, surface, pAllocator);
+
+   instance_data.remove_surface(
+      surface, util::allocator{ instance_data.get_allocator(), VK_SYSTEM_ALLOCATION_SCOPE_OBJECT, pAllocator });
+}
+
 } /* extern "C" */
index 9dc4636..b514260 100644 (file)
@@ -61,4 +61,10 @@ VKAPI_ATTR VkResult wsi_layer_vkGetPhysicalDeviceSurfacePresentModesKHR(VkPhysic
 VKAPI_ATTR VkResult wsi_layer_vkGetPhysicalDeviceSurfaceSupportKHR(VkPhysicalDevice physicalDevice,
                                                                    uint32_t queueFamilyIndex, VkSurfaceKHR surface,
                                                                    VkBool32 *pSupported);
+
+/**
+ * @brief Implements vkDestroySurfaceKHR Vulkan entrypoint.
+ */
+VKAPI_ATTR void wsi_layer_vkDestroySurfaceKHR(VkInstance instance, VkSurfaceKHR surface,
+                                              const VkAllocationCallbacks *pAllocator);
 }
index f10c0e3..474b891 100644 (file)
@@ -56,7 +56,7 @@ VKAPI_ATTR VkResult wsi_layer_vkCreateSwapchainKHR(VkDevice device,
       return device_data.disp.CreateSwapchainKHR(device_data.device, pSwapchainCreateInfo, pAllocator, pSwapchain);
    }
 
-   wsi::swapchain_base *sc = wsi::allocate_surface_swapchain(surface, device_data, pAllocator);
+   auto sc = wsi::allocate_surface_swapchain(surface, device_data, pAllocator);
    if (sc == nullptr)
    {
       return VK_ERROR_OUT_OF_HOST_MEMORY;
@@ -65,20 +65,16 @@ VKAPI_ATTR VkResult wsi_layer_vkCreateSwapchainKHR(VkDevice device,
    VkResult result = sc->init(device, pSwapchainCreateInfo);
    if (result != VK_SUCCESS)
    {
-      /* Error occured during initialization, need to free allocated memory. */
-      wsi::destroy_surface_swapchain(sc, device_data, pAllocator);
       return result;
    }
 
-   auto vulkan_swapchain = reinterpret_cast<VkSwapchainKHR>(sc);
-   result = device_data.add_layer_swapchain(vulkan_swapchain);
+   result = device_data.add_layer_swapchain(reinterpret_cast<VkSwapchainKHR>(sc.get()));
    if (result != VK_SUCCESS)
    {
-      wsi::destroy_surface_swapchain(sc, device_data, pAllocator);
       return result;
    }
 
-   *pSwapchain = vulkan_swapchain;
+   *pSwapchain = reinterpret_cast<VkSwapchainKHR>(sc.release());
    return result;
 }
 
@@ -214,7 +210,7 @@ VKAPI_ATTR VkResult wsi_layer_vkGetPhysicalDevicePresentRectanglesKHR(VkPhysical
    }
 
    VkResult result;
-   wsi::surface_properties *props = wsi::get_surface_properties(surface);
+   wsi::surface_properties *props = wsi::get_surface_properties(instance, surface);
    assert(props);
 
    if (nullptr == pRects)
index 78be093..bb6a7a0 100644 (file)
@@ -246,20 +246,21 @@ void allocator::destroy(size_t num_objects, T *objects) const noexcept
  *        destroy method.
  */
 template <typename T>
-class deleter
+class deleter : public allocator
 {
 public:
-   deleter(allocator allocator)
-      : m_allocator(std::move(allocator))
+   deleter()
+      : deleter(allocator::get_generic())
+   {}
+
+   deleter(allocator alloc)
+      : allocator(std::move(alloc))
    {}
 
    void operator()(T *object)
    {
-      m_allocator.destroy<T>(1, object);
+      destroy<T>(1, object);
    }
-
-private:
-   allocator m_allocator;
 };
 
 /**
diff --git a/wsi/headless/surface.cpp b/wsi/headless/surface.cpp
new file mode 100644 (file)
index 0000000..5a7daf7
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2021 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 Implementation of a headless WSI Surface
+ */
+
+#include "surface.hpp"
+#include "swapchain.hpp"
+#include "surface_properties.hpp"
+
+namespace wsi
+{
+namespace headless
+{
+
+wsi::surface_properties &surface::get_properties()
+{
+   return surface_properties::get_instance();
+}
+
+util::unique_ptr<swapchain_base> surface::allocate_swapchain(layer::device_private_data &dev_data,
+                                                             const VkAllocationCallbacks *allocator)
+{
+   util::allocator alloc{ dev_data.get_allocator(), VK_SYSTEM_ALLOCATION_SCOPE_OBJECT, allocator };
+   return util::unique_ptr<swapchain_base>(alloc.make_unique<swapchain>(dev_data, allocator));
+}
+
+} /* namespace headless */
+} /* namespace wsi */
\ No newline at end of file
diff --git a/wsi/headless/surface.hpp b/wsi/headless/surface.hpp
new file mode 100644 (file)
index 0000000..049f7ce
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2021 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 Definitions for a headless WSI Surface
+ */
+
+#pragma once
+
+#include "wsi/surface.hpp"
+
+namespace wsi
+{
+namespace headless
+{
+
+class surface : public wsi::surface
+{
+public:
+   surface() = default;
+   wsi::surface_properties &get_properties() override;
+   util::unique_ptr<swapchain_base> allocate_swapchain(layer::device_private_data &dev_data,
+                                                       const VkAllocationCallbacks *allocator) override;
+};
+
+} /* namespace headless */
+} /* namespace wsi */
\ No newline at end of file
index e030e7b..da63ef0 100644 (file)
@@ -26,6 +26,7 @@
 #include <array>
 #include <cassert>
 #include <cstdlib>
+#include <cstring>
 #include <map>
 #include <mutex>
 
@@ -35,6 +36,7 @@
 #include <layer/private_data.hpp>
 
 #include "surface_properties.hpp"
+#include "surface.hpp"
 
 #define UNUSED(x) ((void)(x))
 
@@ -168,5 +170,38 @@ VkResult surface_properties::get_surface_present_modes(VkPhysicalDevice physical
    return res;
 }
 
+extern "C" VKAPI_ATTR VkResult VKAPI_CALL CreateHeadlessSurfaceEXT(VkInstance instance,
+                                                                   const VkHeadlessSurfaceCreateInfoEXT *pCreateInfo,
+                                                                   const VkAllocationCallbacks *pAllocator,
+                                                                   VkSurfaceKHR *pSurface)
+{
+   auto &instance_data = layer::instance_private_data::get(instance);
+   util::allocator allocator{ instance_data.get_allocator(), VK_SYSTEM_ALLOCATION_SCOPE_OBJECT, pAllocator };
+   auto wsi_surface = util::unique_ptr<wsi::surface>(allocator.make_unique<surface>());
+   if (wsi_surface == nullptr)
+   {
+      return VK_ERROR_OUT_OF_HOST_MEMORY;
+   }
+   VkResult res = instance_data.disp.CreateHeadlessSurfaceEXT(instance, pCreateInfo, pAllocator, pSurface);
+   if (res == VK_SUCCESS)
+   {
+      res = instance_data.add_surface(*pSurface, wsi_surface);
+      if (res != VK_SUCCESS)
+      {
+         instance_data.disp.DestroySurfaceKHR(instance, *pSurface, pAllocator);
+      }
+   }
+   return res;
+}
+
+PFN_vkVoidFunction surface_properties::get_proc_addr(const char *name)
+{
+   if (strcmp(name, "vkCreateHeadlessSurfaceEXT") == 0)
+   {
+      return reinterpret_cast<PFN_vkVoidFunction>(CreateHeadlessSurfaceEXT);
+   }
+   return nullptr;
+}
+
 } /* namespace headless */
 } /* namespace wsi */
index 737f2ca..539b234 100644 (file)
@@ -45,6 +45,8 @@ public:
    VkResult get_surface_present_modes(VkPhysicalDevice physical_device, VkSurfaceKHR surface,
                                       uint32_t *pPresentModeCount, VkPresentModeKHR *pPresentModes) override;
 
+   PFN_vkVoidFunction get_proc_addr(const char *name) override;
+
    static surface_properties &get_instance();
 };
 
diff --git a/wsi/surface.hpp b/wsi/surface.hpp
new file mode 100644 (file)
index 0000000..c879cce
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2021 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 Vulkan WSI surface interfaces.
+ */
+
+#pragma once
+
+#include <vulkan/vulkan.h>
+#include "surface_properties.hpp"
+#include "swapchain_base.hpp"
+
+namespace wsi
+{
+
+/**
+ * @brief A generic WSI representation of a VkSurface.
+ *
+ * The association between these objects and VkSurfaces is kept in the VkInstance private data.
+ */
+class surface
+{
+public:
+   virtual ~surface() = default;
+
+   /**
+    * @brief Returns a @ref surface_properties implementation that can be specific to the VkSurface represented.
+    */
+   virtual surface_properties &get_properties() = 0;
+
+   /**
+    * @brief Allocates a swapchain for the VkSurface type represented.
+    *
+    * @param dev_data  The VkDevice associated private date.
+    * @param allocator Allocation callbacks to use for host memory.
+    *
+    * @return nullptr on failure otherwise a constructed swapchain.
+    */
+   virtual util::unique_ptr<swapchain_base> allocate_swapchain(layer::device_private_data &dev_data,
+                                                               const VkAllocationCallbacks *allocator) = 0;
+};
+
+} /* namespace wsi */
\ No newline at end of file
index 477fb18..b9d032f 100644 (file)
@@ -71,11 +71,10 @@ public:
 
    /**
     * @brief Implements vkGetProcAddr for entrypoints specific to the surface type.
+    *
+    * At least the specific VkSurface creation entrypoint must be intercepted.
     */
-    virtual PFN_vkVoidFunction get_proc_addr(const char *name)
-    {
-       return nullptr;
-    }
+   virtual PFN_vkVoidFunction get_proc_addr(const char *name) = 0;
 };
 
 } /* namespace wsi */
diff --git a/wsi/wayland/surface.cpp b/wsi/wayland/surface.cpp
new file mode 100644 (file)
index 0000000..f70d3d5
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2021 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 Implementation of a Wayland WSI Surface
+ */
+
+#include "surface.hpp"
+#include "swapchain.hpp"
+#include "surface_properties.hpp"
+
+namespace wsi
+{
+namespace wayland
+{
+
+wsi::surface_properties &surface::get_properties()
+{
+   return surface_properties::get_instance();
+}
+
+util::unique_ptr<swapchain_base> surface::allocate_swapchain(layer::device_private_data &dev_data,
+                                                             const VkAllocationCallbacks *allocator)
+{
+   util::allocator alloc{ dev_data.get_allocator(), VK_SYSTEM_ALLOCATION_SCOPE_OBJECT, allocator };
+   return util::unique_ptr<swapchain_base>(alloc.make_unique<swapchain>(dev_data, allocator));
+}
+
+} // namespace wayland
+} // namespace wsi
\ No newline at end of file
diff --git a/wsi/wayland/surface.hpp b/wsi/wayland/surface.hpp
new file mode 100644 (file)
index 0000000..12c1462
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2021 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 Definitions for a Wayland WSI Surface
+ */
+
+#pragma once
+
+#include "wsi/surface.hpp"
+
+namespace wsi
+{
+namespace wayland
+{
+
+class surface : public wsi::surface
+{
+public:
+   surface() = default;
+
+   wsi::surface_properties &get_properties() override;
+   util::unique_ptr<swapchain_base> allocate_swapchain(layer::device_private_data &dev_data,
+                                                       const VkAllocationCallbacks *allocator) override;
+};
+
+} // namespace wayland
+} // namespace wsi
\ No newline at end of file
index 45ea124..2da5e1d 100644 (file)
@@ -33,6 +33,7 @@
 #include <array>
 #include <cstring>
 #include "surface_properties.hpp"
+#include "surface.hpp"
 #include "layer/private_data.hpp"
 #include "wl_helpers.hpp"
 #include "wl_object_owner.hpp"
@@ -281,13 +282,40 @@ VkBool32 GetPhysicalDeviceWaylandPresentationSupportKHR(VkPhysicalDevice physica
    return VK_TRUE;
 }
 
+extern "C" VkResult CreateWaylandSurfaceKHR(VkInstance instance, const VkWaylandSurfaceCreateInfoKHR *pCreateInfo,
+                                            const VkAllocationCallbacks *pAllocator, VkSurfaceKHR *pSurface)
+{
+   auto &instance_data = layer::instance_private_data::get(instance);
+   util::allocator allocator{ instance_data.get_allocator(), VK_SYSTEM_ALLOCATION_SCOPE_OBJECT, pAllocator };
+   auto wsi_surface = util::unique_ptr<wsi::surface>(allocator.make_unique<surface>());
+   if (wsi_surface == nullptr)
+   {
+      return VK_ERROR_OUT_OF_HOST_MEMORY;
+   }
+   VkResult res = instance_data.disp.CreateWaylandSurfaceKHR(instance, pCreateInfo, pAllocator, pSurface);
+   if (res == VK_SUCCESS)
+   {
+      res = instance_data.add_surface(*pSurface, wsi_surface);
+      if (res != VK_SUCCESS)
+      {
+         instance_data.disp.DestroySurfaceKHR(instance, *pSurface, pAllocator);
+      }
+   }
+   return res;
+}
+
 PFN_vkVoidFunction surface_properties::get_proc_addr(const char *name)
 {
    if (strcmp(name, "vkGetPhysicalDeviceWaylandPresentationSupportKHR") == 0)
    {
       return reinterpret_cast<PFN_vkVoidFunction>(GetPhysicalDeviceWaylandPresentationSupportKHR);
    }
+   else if (strcmp(name, "vkCreateWaylandSurfaceKHR") == 0)
+   {
+      return reinterpret_cast<PFN_vkVoidFunction>(CreateWaylandSurfaceKHR);
+   }
    return nullptr;
 }
+
 } // namespace wayland
 } // namespace wsi
index a34f1fb..0a85a15 100644 (file)
@@ -28,8 +28,8 @@
  */
 
 #include "wsi_factory.hpp"
+#include "surface.hpp"
 #include "headless/surface_properties.hpp"
-#include "headless/swapchain.hpp"
 
 #include <cassert>
 #include <cstdlib>
@@ -41,7 +41,6 @@
 #if BUILD_WSI_WAYLAND
 #include <vulkan/vulkan_wayland.h>
 #include "wayland/surface_properties.hpp"
-#include "wayland/swapchain.hpp"
 #endif
 
 namespace wsi
@@ -73,36 +72,27 @@ static surface_properties *get_surface_properties(VkIcdWsiPlatform platform)
    }
 }
 
-surface_properties *get_surface_properties(VkSurfaceKHR surface)
+surface_properties *get_surface_properties(layer::instance_private_data &instance_data, VkSurfaceKHR surface)
 {
-   VkIcdSurfaceBase *surface_base = reinterpret_cast<VkIcdSurfaceBase *>(surface);
+   auto *wsi_surface = instance_data.get_surface(surface);
 
-   return get_surface_properties(surface_base->platform);
-}
+   if (wsi_surface)
+   {
+      return &wsi_surface->get_properties();
+   }
 
-template <typename swapchain_type>
-static swapchain_base *allocate_swapchain(layer::device_private_data &dev_data, const VkAllocationCallbacks *pAllocator)
-{
-   util::allocator alloc{ dev_data.get_allocator(), VK_SYSTEM_ALLOCATION_SCOPE_INSTANCE, pAllocator };
-   return alloc.create<swapchain_type>(1, dev_data, pAllocator);
+   return nullptr;
 }
 
-swapchain_base *allocate_surface_swapchain(VkSurfaceKHR surface, layer::device_private_data &dev_data,
-                                           const VkAllocationCallbacks *pAllocator)
+util::unique_ptr<swapchain_base> allocate_surface_swapchain(VkSurfaceKHR surface, layer::device_private_data &dev_data,
+                                                            const VkAllocationCallbacks *pAllocator)
 {
-   VkIcdSurfaceBase *surface_base = reinterpret_cast<VkIcdSurfaceBase *>(surface);
-
-   switch (surface_base->platform)
+   wsi::surface *wsi_surface = dev_data.instance_data.get_surface(surface);
+   if (wsi_surface)
    {
-   case VK_ICD_WSI_PLATFORM_HEADLESS:
-      return allocate_swapchain<wsi::headless::swapchain>(dev_data, pAllocator);
-#if BUILD_WSI_WAYLAND
-   case VK_ICD_WSI_PLATFORM_WAYLAND:
-      return allocate_swapchain<wsi::wayland::swapchain>(dev_data, pAllocator);
-#endif
-   default:
-      return nullptr;
+      return wsi_surface->allocate_swapchain(dev_data, pAllocator);
    }
+   return nullptr;
 }
 
 util::wsi_platform_set find_enabled_layer_platforms(const VkInstanceCreateInfo *pCreateInfo)
@@ -195,7 +185,7 @@ void destroy_surface_swapchain(swapchain_base *swapchain, layer::device_private_
 {
    assert(swapchain);
 
-   util::allocator alloc{ swapchain->get_allocator(), VK_SYSTEM_ALLOCATION_SCOPE_INSTANCE, pAllocator };
+   util::allocator alloc{ dev_data.get_allocator(), VK_SYSTEM_ALLOCATION_SCOPE_OBJECT, pAllocator };
    alloc.destroy(1, swapchain);
 }
 
index 1331d63..b913489 100644 (file)
@@ -41,11 +41,12 @@ namespace wsi
 /**
  * @brief Obtains the surface properties for the specific surface type.
  *
- * @param surface The surface for which to get the properties.
+ * @param instance_data The instance specific data.
+ * @param surface       The surface for which to get the properties.
  *
  * @return nullptr if surface type is unsupported.
  */
-surface_properties *get_surface_properties(VkSurfaceKHR surface);
+surface_properties *get_surface_properties(layer::instance_private_data &instance_data, VkSurfaceKHR surface);
 
 /**
  * @brief Allocates a surface specific swapchain.
@@ -56,8 +57,8 @@ surface_properties *get_surface_properties(VkSurfaceKHR surface);
  *
  * @return nullptr on failure.
  */
-swapchain_base *allocate_surface_swapchain(VkSurfaceKHR surface, layer::device_private_data &dev_data,
-                                           const VkAllocationCallbacks *pAllocator);
+util::unique_ptr<swapchain_base> allocate_surface_swapchain(VkSurfaceKHR surface, layer::device_private_data &dev_data,
+                                                            const VkAllocationCallbacks *pAllocator);
 
 /**
  * @brief Destroys a swapchain and frees memory. Used with @ref allocate_surface_swapchain.