Initial sources for the vulkan-wsi-layer project
authorMatteo Franchin <matteo.franchin@arm.com>
Fri, 24 May 2019 14:57:50 +0000 (15:57 +0100)
committerRaymond Smith <raymond.smith@arm.com>
Fri, 31 May 2019 14:48:05 +0000 (15:48 +0100)
This commit adds the initial sources for the vulkan-wsi-layer project:
a Vulkan layer which implements some of the Vulkan window system
integration extensions such as VK_KHR_swapchain.
The layer is designed to be GPU vendor agnostic when used as part of the
Vulkan ICD/loader architecture.

The project currently implements support for VK_EXT_headless_surface and
its dependencies. We hope to extend support for further platforms such
as Wayland and direct-to-display rendering in the future.

This initial commit collects contributions from different individuals
employed by Arm.

More information on the project (building instructions, how to
contribute, etc.) can be found on the README.md file at the top of the
project tree.

Signed-off-by: Matteo Franchin <matteo.franchin@arm.com>
22 files changed:
.clang-format [new file with mode: 0644]
CMakeLists.txt [new file with mode: 0644]
DCO.txt [new file with mode: 0644]
LICENSE [new file with mode: 0644]
README.md [new file with mode: 0644]
layer/VkLayer_window_system_integration.json [new file with mode: 0644]
layer/layer.cpp [new file with mode: 0644]
layer/private_data.cpp [new file with mode: 0644]
layer/private_data.hpp [new file with mode: 0644]
layer/surface_api.cpp [new file with mode: 0644]
layer/surface_api.hpp [new file with mode: 0644]
layer/swapchain_api.cpp [new file with mode: 0644]
layer/swapchain_api.hpp [new file with mode: 0644]
util/timed_semaphore.cpp [new file with mode: 0644]
util/timed_semaphore.hpp [new file with mode: 0644]
wsi/headless/surface_properties.cpp [new file with mode: 0644]
wsi/headless/surface_properties.hpp [new file with mode: 0644]
wsi/headless/swapchain.cpp [new file with mode: 0644]
wsi/headless/swapchain.hpp [new file with mode: 0644]
wsi/surface_properties.hpp [new file with mode: 0644]
wsi/swapchain_base.cpp [new file with mode: 0644]
wsi/swapchain_base.hpp [new file with mode: 0644]

diff --git a/.clang-format b/.clang-format
new file mode 100644 (file)
index 0000000..6c234cb
--- /dev/null
@@ -0,0 +1,109 @@
+BasedOnStyle: LLVM
+
+AccessModifierOffset: -3
+
+AlignEscapedNewlinesLeft: true
+
+AlignTrailingComments: true
+
+AllowAllParametersOfDeclarationOnNextLine: false
+
+AllowShortBlocksOnASingleLine: false
+
+AllowShortCaseLabelsOnASingleLine: false
+
+AllowShortFunctionsOnASingleLine: None
+
+AllowShortIfStatementsOnASingleLine: false
+
+AllowShortLoopsOnASingleLine: false
+
+AlwaysBreakAfterDefinitionReturnType: false
+
+AlwaysBreakBeforeMultilineStrings: false
+
+AlwaysBreakTemplateDeclarations: true
+
+BinPackArguments: true
+
+BinPackParameters: true
+
+BreakBeforeBinaryOperators: None
+
+BreakBeforeBraces: Allman
+
+BreakBeforeTernaryOperators: false
+
+BreakConstructorInitializersBeforeComma: true
+
+ColumnLimit: 120
+
+CommentPragmas: '^ *'
+
+ConstructorInitializerAllOnOneLineOrOnePerLine: false
+
+ConstructorInitializerIndentWidth: 3
+
+ContinuationIndentWidth: 3
+
+Cpp11BracedListStyle: false
+
+DisableFormat: false
+
+ForEachMacros: ['']
+
+IndentCaseLabels: false
+
+IndentWidth: 3
+
+IndentWrappedFunctionNames: false
+
+KeepEmptyLinesAtTheStartOfBlocks: true
+
+Language: Cpp
+
+MaxEmptyLinesToKeep: 1
+
+NamespaceIndentation: None
+
+PenaltyBreakBeforeFirstCallParameter: 19
+
+PenaltyBreakComment: 300
+
+PenaltyBreakFirstLessLess: 120
+
+PenaltyBreakString: 1000
+
+PenaltyExcessCharacter: 1000000
+
+PenaltyReturnTypeOnItsOwnLine: 1000000000
+
+PointerAlignment: Right
+
+SortIncludes: false
+
+SpaceAfterCStyleCast: false
+
+SpaceBeforeAssignmentOperators: true
+
+SpaceBeforeParens: ControlStatements
+
+SpaceInEmptyParentheses: false
+
+SpacesBeforeTrailingComments: 1
+
+SpacesInAngles: false
+
+SpacesInCStyleCastParentheses: false
+
+SpacesInContainerLiterals: false
+
+SpacesInParentheses: false
+
+SpacesInSquareBrackets: false
+
+Standard: Cpp11
+
+TabWidth: 3
+
+UseTab: Never
diff --git a/CMakeLists.txt b/CMakeLists.txt
new file mode 100644 (file)
index 0000000..ac18c51
--- /dev/null
@@ -0,0 +1,53 @@
+# Copyright (c) 2019 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.
+
+cmake_minimum_required(VERSION 2.8.11)
+project(VkLayer_window_system_integration CXX)
+
+find_package(PkgConfig REQUIRED)
+pkg_check_modules(VULKAN_PKG_CONFIG vulkan)
+
+# Disable C++ exceptions.
+set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-exceptions")
+
+if(NOT DEFINED VULKAN_CXX_INCLUDE)
+   set(VULKAN_CXX_INCLUDE ${VULKAN_PKG_CONFIG_INCLUDEDIR})
+endif()
+
+if(DEFINED VULKAN_CXX_INCLUDE)
+   message(STATUS "Using Vulkan include directories: ${VULKAN_CXX_INCLUDE}")
+   separate_arguments(VULKAN_CXX_INCLUDE)
+else()
+   message(FATAL_ERROR "Either vulkan.pc must be available or VULKAN_CXX_INCLUDE must be defined")
+endif()
+
+add_library(${PROJECT_NAME} SHARED
+   layer/layer.cpp
+   layer/private_data.cpp
+   layer/surface_api.cpp
+   layer/swapchain_api.cpp
+   util/timed_semaphore.cpp
+   wsi/swapchain_base.cpp
+   wsi/headless/surface_properties.cpp
+   wsi/headless/swapchain.cpp)
+set_target_properties(${PROJECT_NAME} PROPERTIES CXX_STANDARD 11)
+target_include_directories(${PROJECT_NAME} PRIVATE ${PROJECT_SOURCE_DIR} ${VULKAN_CXX_INCLUDE})
diff --git a/DCO.txt b/DCO.txt
new file mode 100644 (file)
index 0000000..8201f99
--- /dev/null
+++ b/DCO.txt
@@ -0,0 +1,37 @@
+Developer Certificate of Origin
+Version 1.1
+
+Copyright (C) 2004, 2006 The Linux Foundation and its contributors.
+1 Letterman Drive
+Suite D4700
+San Francisco, CA, 94129
+
+Everyone is permitted to copy and distribute verbatim copies of this
+license document, but changing it is not allowed.
+
+
+Developer's Certificate of Origin 1.1
+
+By making a contribution to this project, I certify that:
+
+(a) The contribution was created in whole or in part by me and I
+    have the right to submit it under the open source license
+    indicated in the file; or
+
+(b) The contribution is based upon previous work that, to the best
+    of my knowledge, is covered under an appropriate open source
+    license and I have the right under that license to submit that
+    work with modifications, whether created in whole or in part
+    by me, under the same open source license (unless I am
+    permitted to submit under a different license), as indicated
+    in the file; or
+
+(c) The contribution was provided directly to me by some other
+    person who certified (a), (b) or (c) and I have not modified
+    it.
+
+(d) I understand and agree that this project and the contribution
+    are public and that a record of the contribution (including all
+    personal information I submit with it, including my sign-off) is
+    maintained indefinitely and may be redistributed consistent with
+    this project or the open source license(s) involved.
diff --git a/LICENSE b/LICENSE
new file mode 100644 (file)
index 0000000..fad2221
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,21 @@
+Copyright (c) 2019 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.
diff --git a/README.md b/README.md
new file mode 100644 (file)
index 0000000..d62835f
--- /dev/null
+++ b/README.md
@@ -0,0 +1,93 @@
+# Vulkan® Window System Integration Layer
+
+## Introduction
+
+This project is a Vulkan® layer which implements some of the Vulkan® window system
+integration extensions such as `VK_KHR_swapchain`. The layer is designed to be
+GPU vendor agnostic when used as part of the Vulkan® ICD/loader architecture.
+
+Our vision for the project is to become the de facto implementation for Vulkan®
+window system integration extensions so that they need not be implemented in the
+ICD; instead, the implementation of these extensions are shared across vendors
+for mutual benefit.
+
+The project currently implements support for `VK_EXT_headless_surface` and
+its dependencies. We hope to extend support for further platforms such as Wayland
+and direct-to-display rendering in the future.
+
+## Building
+
+### Dependencies
+
+* [CMake](https://cmake.org) version 2.8.11 or above.
+* C++11 compiler.
+* Vulkan® loader and associated headers with support for the
+  `VK_EXT_headless_surface` extension.
+
+### Building the Vulkan® loader
+
+This step is not necessary if your system already has a loader and associated
+headers with support for the `VK_EXT_headless_surface` extension. We include
+these instructions for completeness.
+
+```
+git clone https://github.com/KhronosGroup/Vulkan-Loader.git
+mkdir Vulkan-Loader/build
+cd Vulkan-Loader/build
+../scripts/update_deps.py
+make -C Vulkan-Headers install
+cmake -C helper.cmake .. -DBUILD_WSI_HEADLESS_SUPPORT=ON
+make
+make install
+```
+
+### Building the layer
+
+The layer requires a version of the loader and headers that includes support for
+the `VK_EXT_headless_surface` extension. By default, the build system will use
+the system Vulkan® headers as reported by `pkg-config`. This may be overriden by
+specifying `VULKAN_CXX_INCLUDE` in the CMake configuration, for example:
+
+```
+cmake . -DVULKAN_CXX_INCLUDE="path/to/vulkan-headers"
+```
+
+If the loader and associated headers already meet the requirements of the layer
+then the build steps are straightforward:
+
+```
+cmake . -Bbuild
+make -C build
+```
+
+## Installation
+
+Copy the shared library `libVkLayer_window_system_integration.so` and JSON
+configuration `VkLayer_window_system_integration.json` into a Vulkan®
+[implicit layer directory](https://vulkan.lunarg.com/doc/view/1.0.39.0/windows/layers.html#user-content-configuring-layers-on-linux).
+
+## Contributing
+
+We are open for contributions.
+
+ * The software is provided under the MIT license. Contributions to this project
+   are accepted under the same license.
+ * Please also ensure that each commit in the series has at least one
+   `Signed-off-by:` line, using your real name and email address. The names in
+   the `Signed-off-by:` and `Author:` lines must match. If anyone else
+   contributes to the commit, they must also add their own `Signed-off-by:`
+   line. By adding this line the contributor certifies the contribution is made
+   under the terms of the [Developer Certificate of Origin (DCO)](DCO.txt).
+ * Questions, bug reports, et cetera are raised and discussed on the issues page.
+ * Please make merge requests into the master branch.
+ * Code should be formatted with clang-format using the project's .clang-format
+   configuration.
+
+Contributors are expected to abide by the
+[freedesktop.org code of conduct](https://www.freedesktop.org/wiki/CodeOfConduct/).
+
+## Khronos® Conformance
+
+This software is based on a published Khronos® Specification and is expected to
+pass the relevant parts of the Khronos® Conformance Testing Process when used as
+part of a conformant Vulkan® implementation.
diff --git a/layer/VkLayer_window_system_integration.json b/layer/VkLayer_window_system_integration.json
new file mode 100644 (file)
index 0000000..22d8d7b
--- /dev/null
@@ -0,0 +1,37 @@
+{
+    "file_format_version" : "1.0.0",
+    "layer" : {
+        "name": "VK_LAYER_window_system_integration",
+        "type": "GLOBAL",
+        "library_path": "./libVkLayer_window_system_integration.so",
+        "api_version": "1.0.68",
+        "implementation_version": "1",
+        "description": "Window system integration layer",
+        "functions": {
+            "vkGetInstanceProcAddr": "wsi_layer_vkGetInstanceProcAddr",
+            "vkGetDeviceProcAddr": "wsi_layer_vkGetDeviceProcAddr"
+        },
+        "instance_extensions": [
+            {
+                "name" : "VK_EXT_headless_surface",
+                "spec_version" : 1
+            },
+            {
+                "name" : "VK_KHR_surface",
+                "spec_version" : 1
+            }
+        ],
+        "device_extensions": [
+            {
+                "name" : "VK_KHR_swapchain",
+                "spec_version" : 1
+            }
+        ],
+        "enable_environment": {
+            "ENABLE_HEADLESS_SURFACE": "1"
+        },
+        "disable_environment": {
+            "DISABLE_HEADLESS_SURFACE": "1"
+        }
+    }
+}
diff --git a/layer/layer.cpp b/layer/layer.cpp
new file mode 100644 (file)
index 0000000..5b66173
--- /dev/null
@@ -0,0 +1,304 @@
+/*
+ * Copyright (c) 2016-2019 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.
+ */
+
+#include <cassert>
+#include <cstdio>
+#include <cstring>
+#include <vulkan/vk_layer.h>
+
+#include <wsi/surface_properties.hpp>
+
+#include "private_data.hpp"
+#include "surface_api.hpp"
+#include "swapchain_api.hpp"
+
+#define VK_LAYER_API_VERSION VK_MAKE_VERSION(1, 0, VK_HEADER_VERSION)
+
+namespace layer
+{
+
+static const VkLayerProperties global_layer = {
+   "VK_LAYER_window_system_integration",
+   VK_LAYER_API_VERSION,
+   1,
+   "Window system integration layer",
+};
+static const VkExtensionProperties device_extension[] = { { VK_KHR_SWAPCHAIN_EXTENSION_NAME,
+                                                            VK_KHR_SWAPCHAIN_SPEC_VERSION } };
+static const VkExtensionProperties instance_extension[] = { { VK_KHR_SURFACE_EXTENSION_NAME,
+                                                              VK_KHR_SURFACE_SPEC_VERSION } };
+
+VKAPI_ATTR VkResult extension_properties(const uint32_t count, const VkExtensionProperties *layer_ext, uint32_t *pCount,
+                                         VkExtensionProperties *pProp)
+{
+   uint32_t size;
+
+   if (pProp == NULL || layer_ext == NULL)
+   {
+      *pCount = count;
+      return VK_SUCCESS;
+   }
+
+   size = *pCount < count ? *pCount : count;
+   memcpy(pProp, layer_ext, size * sizeof(VkLayerProperties));
+   *pCount = size;
+   if (size < count)
+   {
+      return VK_INCOMPLETE;
+   }
+
+   return VK_SUCCESS;
+}
+
+VKAPI_ATTR VkResult layer_properties(const uint32_t count, const VkLayerProperties *layer_prop, uint32_t *pCount,
+                                     VkLayerProperties *pProp)
+{
+   uint32_t size;
+
+   if (pProp == NULL || layer_prop == NULL)
+   {
+      *pCount = count;
+      return VK_SUCCESS;
+   }
+
+   size = *pCount < count ? *pCount : count;
+   memcpy(pProp, layer_prop, size * sizeof(VkLayerProperties));
+   *pCount = size;
+   if (size < count)
+   {
+      return VK_INCOMPLETE;
+   }
+
+   return VK_SUCCESS;
+}
+
+VKAPI_ATTR VkLayerInstanceCreateInfo *get_chain_info(const VkInstanceCreateInfo *pCreateInfo, VkLayerFunction func)
+{
+   VkLayerInstanceCreateInfo *chain_info = (VkLayerInstanceCreateInfo *)pCreateInfo->pNext;
+   while (chain_info &&
+          !(chain_info->sType == VK_STRUCTURE_TYPE_LOADER_INSTANCE_CREATE_INFO && chain_info->function == func))
+   {
+      chain_info = (VkLayerInstanceCreateInfo *)chain_info->pNext;
+   }
+
+   return chain_info;
+}
+
+VKAPI_ATTR VkLayerDeviceCreateInfo *get_chain_info(const VkDeviceCreateInfo *pCreateInfo, VkLayerFunction func)
+{
+   VkLayerDeviceCreateInfo *chain_info = (VkLayerDeviceCreateInfo *)pCreateInfo->pNext;
+   while (chain_info &&
+          !(chain_info->sType == VK_STRUCTURE_TYPE_LOADER_DEVICE_CREATE_INFO && chain_info->function == func))
+   {
+      chain_info = (VkLayerDeviceCreateInfo *)chain_info->pNext;
+   }
+
+   return chain_info;
+}
+
+/*
+ * This is where we get our initialisation and construct our dispatch table. All layers must implement the function.
+ * If you wish to intercept any device functions at all you need to implement vkCreateDevice.
+ */
+VKAPI_ATTR VkResult create_instance(const VkInstanceCreateInfo *pCreateInfo, const VkAllocationCallbacks *pAllocator,
+                                    VkInstance *pInstance)
+{
+   VkLayerInstanceCreateInfo *layerCreateInfo = get_chain_info(pCreateInfo, VK_LAYER_LINK_INFO);
+   PFN_vkSetInstanceLoaderData loader_callback =
+      get_chain_info(pCreateInfo, VK_LOADER_DATA_CALLBACK)->u.pfnSetInstanceLoaderData;
+
+   if (nullptr == layerCreateInfo || nullptr == layerCreateInfo->u.pLayerInfo)
+   {
+      return VK_ERROR_INITIALIZATION_FAILED;
+   }
+
+   /* Retrieve the vkGetInstanceProcAddr and the vkCreateInstance function pointers for the next layer in the chain. */
+   PFN_vkGetInstanceProcAddr fpGetInstanceProcAddr = layerCreateInfo->u.pLayerInfo->pfnNextGetInstanceProcAddr;
+   PFN_vkCreateInstance fpCreateInstance = (PFN_vkCreateInstance)fpGetInstanceProcAddr(nullptr, "vkCreateInstance");
+   if (nullptr == fpCreateInstance)
+   {
+      return VK_ERROR_INITIALIZATION_FAILED;
+   }
+
+   /* Advance the link info for the next element on the chain. */
+   layerCreateInfo->u.pLayerInfo = layerCreateInfo->u.pLayerInfo->pNext;
+
+   /* Now call create instance on the chain further down the list. */
+   VkResult ret = fpCreateInstance(pCreateInfo, pAllocator, pInstance);
+
+   instance_private_data::create(*pInstance, fpGetInstanceProcAddr, loader_callback);
+   return ret;
+}
+
+VKAPI_ATTR VkResult create_device(VkPhysicalDevice physicalDevice, const VkDeviceCreateInfo *pCreateInfo,
+                                  const VkAllocationCallbacks *pAllocator, VkDevice *pDevice)
+{
+   VkLayerDeviceCreateInfo *layerCreateInfo = get_chain_info(pCreateInfo, VK_LAYER_LINK_INFO);
+   PFN_vkSetDeviceLoaderData loader_callback =
+      get_chain_info(pCreateInfo, VK_LOADER_DATA_CALLBACK)->u.pfnSetDeviceLoaderData;
+
+   if (nullptr == layerCreateInfo || nullptr == layerCreateInfo->u.pLayerInfo)
+   {
+      return VK_ERROR_INITIALIZATION_FAILED;
+   }
+
+   /* Retrieve the vkGetDeviceProcAddr and the vkCreateDevice function pointers for the next layer in the chain. */
+   PFN_vkGetInstanceProcAddr fpGetInstanceProcAddr = layerCreateInfo->u.pLayerInfo->pfnNextGetInstanceProcAddr;
+   PFN_vkGetDeviceProcAddr fpGetDeviceProcAddr = layerCreateInfo->u.pLayerInfo->pfnNextGetDeviceProcAddr;
+   PFN_vkCreateDevice fpCreateDevice = (PFN_vkCreateDevice)fpGetInstanceProcAddr(VK_NULL_HANDLE, "vkCreateDevice");
+   if (nullptr == fpCreateDevice)
+   {
+      return VK_ERROR_INITIALIZATION_FAILED;
+   }
+
+   /* Advance the link info for the next element on the chain. */
+   layerCreateInfo->u.pLayerInfo = layerCreateInfo->u.pLayerInfo->pNext;
+
+   /* Now call create device on the chain further down the list. */
+   VkResult ret = fpCreateDevice(physicalDevice, pCreateInfo, pAllocator, pDevice);
+
+   device_private_data::create(*pDevice, fpGetDeviceProcAddr, physicalDevice, loader_callback);
+
+   return ret;
+}
+
+} /* namespace layer */
+
+extern "C"
+{
+   VK_LAYER_EXPORT PFN_vkVoidFunction VKAPI_CALL
+   wsi_layer_vkGetDeviceProcAddr(VkDevice device, const char *funcName);
+
+   VK_LAYER_EXPORT VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL
+   wsi_layer_vkGetInstanceProcAddr(VkInstance instance, const char *funcName);
+
+   /* Clean up the dispatch table for this instance. */
+   VK_LAYER_EXPORT VKAPI_ATTR void VKAPI_CALL
+   wsi_layer_vkDestroyInstance(VkInstance instance, const VkAllocationCallbacks *pAllocator)
+   {
+      layer::instance_private_data::destroy(instance);
+   }
+
+   VK_LAYER_EXPORT VKAPI_ATTR void VKAPI_CALL
+   wsi_layer_vkDestroyDevice(VkDevice device, const VkAllocationCallbacks *pAllocator)
+   {
+      layer::device_private_data::destroy(device);
+   }
+
+   VK_LAYER_EXPORT VKAPI_ATTR VkResult VKAPI_CALL
+   wsi_layer_vkCreateInstance(const VkInstanceCreateInfo *pCreateInfo, const VkAllocationCallbacks *pAllocator, VkInstance *pInstance)
+   {
+      return layer::create_instance(pCreateInfo, pAllocator, pInstance);
+   }
+
+   VK_LAYER_EXPORT VKAPI_ATTR VkResult VKAPI_CALL
+   wsi_layer_vkCreateDevice(VkPhysicalDevice physicalDevice, const VkDeviceCreateInfo *pCreateInfo,
+                            const VkAllocationCallbacks *pAllocator, VkDevice *pDevice)
+   {
+      return layer::create_device(physicalDevice, pCreateInfo, pAllocator, pDevice);
+   }
+
+   VK_LAYER_EXPORT VKAPI_ATTR VkResult VKAPI_CALL
+   vkNegotiateLoaderLayerInterfaceVersion(VkNegotiateLayerInterface *pVersionStruct)
+   {
+      assert(pVersionStruct);
+      assert(pVersionStruct->sType == LAYER_NEGOTIATE_INTERFACE_STRUCT);
+
+      /* 2 is the minimum interface version which would utilize this function. */
+      assert(pVersionStruct->loaderLayerInterfaceVersion >= 2);
+
+      /* Set our requested interface version. Set to 2 for now to separate us from newer versions. */
+      pVersionStruct->loaderLayerInterfaceVersion = 2;
+
+      /* Fill in struct values. */
+      pVersionStruct->pfnGetInstanceProcAddr = &wsi_layer_vkGetInstanceProcAddr;
+      pVersionStruct->pfnGetDeviceProcAddr = &wsi_layer_vkGetDeviceProcAddr;
+      pVersionStruct->pfnGetPhysicalDeviceProcAddr = nullptr;
+
+      return VK_SUCCESS;
+   }
+
+   VK_LAYER_EXPORT VKAPI_ATTR VkResult VKAPI_CALL
+   wsi_layer_vkEnumerateDeviceExtensionProperties(VkPhysicalDevice physicalDevice, const char *pLayerName,
+                                                  uint32_t *pCount, VkExtensionProperties *pProperties)
+   {
+      if (pLayerName && !strcmp(pLayerName, layer::global_layer.layerName))
+         return layer::extension_properties(1, layer::device_extension, pCount, pProperties);
+
+      assert(physicalDevice);
+      return layer::instance_private_data::get(layer::get_key(physicalDevice))
+         .disp.EnumerateDeviceExtensionProperties(physicalDevice, pLayerName, pCount, pProperties);
+   }
+
+   VK_LAYER_EXPORT VKAPI_ATTR VkResult VKAPI_CALL
+   wsi_layer_vkEnumerateInstanceExtensionProperties(const char *pLayerName, uint32_t *pCount, VkExtensionProperties *pProperties)
+   {
+      if (pLayerName && !strcmp(pLayerName, layer::global_layer.layerName))
+         return layer::extension_properties(1, layer::instance_extension, pCount, pProperties);
+
+      return VK_ERROR_LAYER_NOT_PRESENT;
+   }
+
+   VK_LAYER_EXPORT VKAPI_ATTR VkResult VKAPI_CALL
+   wsi_layer_vkEnumerateInstanceLayerProperties(uint32_t *pCount, VkLayerProperties *pProperties)
+   {
+      return layer::layer_properties(1, &layer::global_layer, pCount, pProperties);
+   }
+
+   #define GET_PROC_ADDR(func)      \
+   if (!strcmp(funcName, #func)) \
+      return (PFN_vkVoidFunction)&wsi_layer_##func;
+
+   VK_LAYER_EXPORT PFN_vkVoidFunction VKAPI_CALL
+   wsi_layer_vkGetDeviceProcAddr(VkDevice device, const char *funcName)
+   {
+      GET_PROC_ADDR(vkCreateSwapchainKHR);
+      GET_PROC_ADDR(vkDestroySwapchainKHR);
+      GET_PROC_ADDR(vkGetSwapchainImagesKHR);
+      GET_PROC_ADDR(vkAcquireNextImageKHR);
+      GET_PROC_ADDR(vkQueuePresentKHR);
+
+      return layer::device_private_data::get(layer::get_key(device)).disp.GetDeviceProcAddr(device, funcName);
+   }
+
+   VK_LAYER_EXPORT VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL
+   wsi_layer_vkGetInstanceProcAddr(VkInstance instance, const char *funcName)
+   {
+      GET_PROC_ADDR(vkGetDeviceProcAddr);
+      GET_PROC_ADDR(vkGetInstanceProcAddr);
+      GET_PROC_ADDR(vkCreateInstance);
+      GET_PROC_ADDR(vkDestroyInstance);
+      GET_PROC_ADDR(vkCreateDevice);
+      GET_PROC_ADDR(vkDestroyDevice);
+      GET_PROC_ADDR(vkGetPhysicalDeviceSurfaceSupportKHR);
+      GET_PROC_ADDR(vkGetPhysicalDeviceSurfaceCapabilitiesKHR);
+      GET_PROC_ADDR(vkGetPhysicalDeviceSurfaceFormatsKHR);
+      GET_PROC_ADDR(vkGetPhysicalDeviceSurfacePresentModesKHR);
+      GET_PROC_ADDR(vkEnumerateDeviceExtensionProperties);
+      GET_PROC_ADDR(vkEnumerateInstanceExtensionProperties);
+      GET_PROC_ADDR(vkEnumerateInstanceLayerProperties);
+
+      return layer::instance_private_data::get(layer::get_key(instance)).disp.GetInstanceProcAddr(instance, funcName);
+   }
+} /* extern "C" */
diff --git a/layer/private_data.cpp b/layer/private_data.cpp
new file mode 100644 (file)
index 0000000..4d15769
--- /dev/null
@@ -0,0 +1,112 @@
+/*
+ * Copyright (c) 2018-2019 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.
+ */
+
+#include <cassert>
+#include <map>
+#include <mutex>
+
+#include "private_data.hpp"
+
+using scoped_mutex = std::lock_guard<std::mutex>;
+
+namespace layer
+{
+
+static std::mutex g_data_lock;
+static std::map<void *, instance_private_data *> g_instance_data;
+static std::map<void *, device_private_data *> g_device_data;
+
+instance_private_data::instance_private_data(VkInstance inst, PFN_vkGetInstanceProcAddr get_proc,
+                                             PFN_vkSetInstanceLoaderData set_loader_data)
+   : disp(inst, get_proc)
+   , SetInstanceLoaderData(set_loader_data)
+{
+}
+
+instance_private_data &instance_private_data::create(VkInstance inst, PFN_vkGetInstanceProcAddr get_proc,
+                                                     PFN_vkSetInstanceLoaderData set_loader_data)
+{
+   instance_private_data *inst_data = new instance_private_data(inst, get_proc, set_loader_data);
+   scoped_mutex lock(g_data_lock);
+   g_instance_data[get_key(inst)] = inst_data;
+   return *inst_data;
+}
+
+instance_private_data &instance_private_data::get(void *key)
+{
+   scoped_mutex lock(g_data_lock);
+   instance_private_data *data = g_instance_data[key];
+   assert(data);
+   return *data;
+}
+
+void instance_private_data::destroy(VkInstance inst)
+{
+   instance_private_data *data;
+   {
+      scoped_mutex lock(g_data_lock);
+      data = g_instance_data[get_key(inst)];
+      assert(data);
+      g_instance_data.erase(get_key(inst));
+   }
+   delete data;
+}
+
+device_private_data::device_private_data(VkDevice dev, PFN_vkGetDeviceProcAddr get_proc,
+                                         instance_private_data &inst_data, PFN_vkSetDeviceLoaderData set_loader_data)
+   : disp(dev, get_proc)
+   , instance_data(inst_data)
+   , SetDeviceLoaderData(set_loader_data)
+{
+}
+
+device_private_data &device_private_data::create(VkDevice dev, PFN_vkGetDeviceProcAddr get_proc,
+                                                 VkPhysicalDevice phys_dev, PFN_vkSetDeviceLoaderData set_loader_data)
+{
+   device_private_data *dev_data =
+      new device_private_data(dev, get_proc, instance_private_data::get(get_key(phys_dev)), set_loader_data);
+   scoped_mutex lock(g_data_lock);
+   g_device_data[get_key(dev)] = dev_data;
+}
+
+device_private_data &device_private_data::get(void *key)
+{
+   scoped_mutex lock(g_data_lock);
+   device_private_data *data = g_device_data[key];
+   assert(data);
+   return *data;
+}
+
+void device_private_data::destroy(VkDevice dev)
+{
+   device_private_data *data;
+   {
+      scoped_mutex lock(g_data_lock);
+      data = g_device_data[get_key(dev)];
+      g_device_data.erase(get_key(dev));
+   }
+   delete data;
+}
+
+} /* namespace layer */
diff --git a/layer/private_data.hpp b/layer/private_data.hpp
new file mode 100644 (file)
index 0000000..3596ad7
--- /dev/null
@@ -0,0 +1,129 @@
+/*
+ * Copyright (c) 2018-2019 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.
+ */
+
+#pragma once
+
+#include "vulkan/vulkan.h"
+#include "vulkan/vk_layer.h"
+
+#define DISPATCH_TABLE_ENTRY(x) PFN_vk##x x;
+
+#define INSTANCE_ENTRYPOINTS_LIST(V)         \
+   V(GetInstanceProcAddr)                    \
+   V(GetPhysicalDeviceProperties)            \
+   V(GetPhysicalDeviceImageFormatProperties) \
+   V(EnumerateDeviceExtensionProperties)
+
+namespace layer
+{
+
+template <typename DispatchableType>
+inline void *get_key(DispatchableType dispatchable_object)
+{
+   return *(void **)dispatchable_object;
+}
+
+struct instance_dispatch_table
+{
+   instance_dispatch_table(VkInstance inst, PFN_vkGetInstanceProcAddr get_proc)
+   {
+#define DISPATCH_INIT(x) x = (PFN_vk##x)get_proc(inst, "vk" #x);
+      INSTANCE_ENTRYPOINTS_LIST(DISPATCH_INIT);
+#undef DISPATCH_INIT
+   }
+
+   INSTANCE_ENTRYPOINTS_LIST(DISPATCH_TABLE_ENTRY)
+};
+
+#define DEVICE_ENTRYPOINTS_LIST(V) \
+   V(GetDeviceProcAddr)            \
+   V(GetDeviceQueue)               \
+   V(QueueSubmit)                  \
+   V(QueueWaitIdle)                \
+   V(CreateCommandPool)            \
+   V(DestroyCommandPool)           \
+   V(AllocateCommandBuffers)       \
+   V(FreeCommandBuffers)           \
+   V(ResetCommandBuffer)           \
+   V(BeginCommandBuffer)           \
+   V(EndCommandBuffer)             \
+   V(CreateImage)                  \
+   V(DestroyImage)                 \
+   V(GetImageMemoryRequirements)   \
+   V(BindImageMemory)              \
+   V(AllocateMemory)               \
+   V(FreeMemory)                   \
+   V(CreateFence)                  \
+   V(DestroyFence)                 \
+   V(ResetFences)                  \
+   V(WaitForFences)
+
+struct device_dispatch_table
+{
+   device_dispatch_table(VkDevice dev, PFN_vkGetDeviceProcAddr get_proc)
+   {
+#define DISPATCH_INIT(x) x = (PFN_vk##x)get_proc(dev, "vk" #x);
+      DEVICE_ENTRYPOINTS_LIST(DISPATCH_INIT);
+#undef DISPATCH_INIT
+   }
+
+   DEVICE_ENTRYPOINTS_LIST(DISPATCH_TABLE_ENTRY)
+};
+
+class instance_private_data
+{
+public:
+   instance_private_data() = delete;
+   static instance_private_data &create(VkInstance inst, PFN_vkGetInstanceProcAddr get_proc,
+                                        PFN_vkSetInstanceLoaderData set_loader_data);
+   static instance_private_data &get(void *key);
+   static void destroy(VkInstance inst);
+
+   instance_dispatch_table disp;
+   PFN_vkSetInstanceLoaderData SetInstanceLoaderData;
+
+private:
+   instance_private_data(VkInstance inst, PFN_vkGetInstanceProcAddr get_proc,
+                         PFN_vkSetInstanceLoaderData set_loader_data);
+};
+
+class device_private_data
+{
+public:
+   device_private_data() = delete;
+   static device_private_data &create(VkDevice dev, PFN_vkGetDeviceProcAddr get_proc, VkPhysicalDevice phys_dev,
+                                      PFN_vkSetDeviceLoaderData set_loader_data);
+   static device_private_data &get(void *key);
+   static void destroy(VkDevice dev);
+
+   device_dispatch_table disp;
+   instance_private_data &instance_data;
+   PFN_vkSetDeviceLoaderData SetDeviceLoaderData;
+
+private:
+   device_private_data(VkDevice dev, PFN_vkGetDeviceProcAddr get_proc, instance_private_data &inst_data,
+                       PFN_vkSetDeviceLoaderData set_loader_data);
+};
+
+} /* namespace layer */
diff --git a/layer/surface_api.cpp b/layer/surface_api.cpp
new file mode 100644 (file)
index 0000000..9b798a2
--- /dev/null
@@ -0,0 +1,91 @@
+/*
+ * Copyright (c) 2016-2017, 2019 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.
+ */
+
+#include <cassert>
+
+#include <wsi/headless/surface_properties.hpp>
+#include "surface_api.hpp"
+
+extern "C"
+{
+
+   /**
+    * @brief Implements vkGetPhysicalDeviceSurfaceCapabilitiesKHR Vulkan entrypoint.
+    */
+   VKAPI_ATTR VkResult wsi_layer_vkGetPhysicalDeviceSurfaceCapabilitiesKHR(
+      VkPhysicalDevice physicalDevice, VkSurfaceKHR surface, VkSurfaceCapabilitiesKHR *pSurfaceCapabilities)
+   {
+      assert(physicalDevice);
+      assert(surface);
+      assert(pSurfaceCapabilities);
+
+      return wsi::headless::surface_properties::get_surface_capabilities(physicalDevice, surface, pSurfaceCapabilities);
+   }
+
+   /**
+    * @brief Implements vkGetPhysicalDeviceSurfaceFormatsKHR Vulkan entrypoint.
+    */
+   VKAPI_ATTR VkResult wsi_layer_vkGetPhysicalDeviceSurfaceFormatsKHR(VkPhysicalDevice physicalDevice,
+                                                                      VkSurfaceKHR surface,
+                                                                      uint32_t *pSurfaceFormatCount,
+                                                                      VkSurfaceFormatKHR *pSurfaceFormats)
+   {
+      assert(physicalDevice);
+      assert(surface);
+
+      return wsi::headless::surface_properties::get_surface_formats(physicalDevice, surface, pSurfaceFormatCount, pSurfaceFormats);
+   }
+
+   /**
+    * @brief Implements vkGetPhysicalDeviceSurfacePresentModesKHR Vulkan entrypoint.
+    */
+   VKAPI_ATTR VkResult wsi_layer_vkGetPhysicalDeviceSurfacePresentModesKHR(VkPhysicalDevice physicalDevice,
+                                                                           VkSurfaceKHR surface,
+                                                                           uint32_t *pPresentModeCount,
+                                                                           VkPresentModeKHR *pPresentModes)
+   {
+      assert(physicalDevice);
+      assert(surface);
+      assert(pPresentModeCount);
+
+      return wsi::headless::surface_properties::get_surface_present_modes(physicalDevice, surface, pPresentModeCount, pPresentModes);
+   }
+
+   /**
+    * @brief Implements vkGetPhysicalDeviceSurfaceSupportKHR Vulkan entrypoint.
+    */
+   VKAPI_ATTR VkResult wsi_layer_vkGetPhysicalDeviceSurfaceSupportKHR(VkPhysicalDevice physicalDevice,
+                                                                      uint32_t queueFamilyIndex, VkSurfaceKHR surface,
+                                                                      VkBool32 *pSupported)
+   {
+      assert(physicalDevice);
+      assert(surface);
+      assert(pSupported);
+
+      /* We assume that presentation to surface is supported by default */
+      *pSupported = VK_TRUE;
+      return VK_SUCCESS;
+   }
+
+} /* extern "C" */
diff --git a/layer/surface_api.hpp b/layer/surface_api.hpp
new file mode 100644 (file)
index 0000000..a5b2346
--- /dev/null
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 2018-2019 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 surface_api.hpp
+ *
+ * @brief Contains the Vulkan entrypoints for the VkSurfaceKHR.
+ */
+#pragma once
+
+#include <vulkan/vulkan.h>
+
+extern "C" {
+
+   /**
+    * @brief Implements vkGetPhysicalDeviceSurfaceCapabilitiesKHR Vulkan entrypoint.
+    */
+   VKAPI_ATTR VkResult wsi_layer_vkGetPhysicalDeviceSurfaceCapabilitiesKHR(VkPhysicalDevice physicalDevice,
+                                                                          VkSurfaceKHR surface,
+                                                                          VkSurfaceCapabilitiesKHR *pSurfaceCapabilities);
+
+   /**
+    * @brief Implements vkGetPhysicalDeviceSurfaceFormatsKHR Vulkan entrypoint.
+    */
+   VKAPI_ATTR VkResult wsi_layer_vkGetPhysicalDeviceSurfaceFormatsKHR(VkPhysicalDevice physicalDevice, VkSurfaceKHR surface,
+                                                                     uint32_t *pSurfaceFormatCount,
+                                                                     VkSurfaceFormatKHR *pSurfaceFormats);
+
+   /**
+    * @brief Implements vkGetPhysicalDeviceSurfacePresentModesKHR Vulkan entrypoint.
+    */
+   VKAPI_ATTR VkResult
+   wsi_layer_vkGetPhysicalDeviceSurfacePresentModesKHR(VkPhysicalDevice physicalDevice, VkSurfaceKHR surface,
+                                                      uint32_t *pPresentModeCount, VkPresentModeKHR *pPresentModes);
+
+   /**
+    * @brief Implements vkGetPhysicalDeviceSurfaceSupportKHR Vulkan entrypoint.
+    */
+   VKAPI_ATTR VkResult wsi_layer_vkGetPhysicalDeviceSurfaceSupportKHR(VkPhysicalDevice physicalDevice,
+                                                                     uint32_t queueFamilyIndex, VkSurfaceKHR surface,
+                                                                     VkBool32 *pSupported);
+
+}
diff --git a/layer/swapchain_api.cpp b/layer/swapchain_api.cpp
new file mode 100644 (file)
index 0000000..fdf55e9
--- /dev/null
@@ -0,0 +1,180 @@
+/*
+ * Copyright (c) 2017, 2019 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 swapchain_api.cpp
+ *
+ * @brief Contains the Vulkan entrypoints for the swapchain.
+ */
+
+#include <cassert>
+#include <cstdlib>
+#include <new>
+#include <vulkan/vk_icd.h>
+
+#include <wsi/headless/swapchain.hpp>
+
+#include "private_data.hpp"
+#include "swapchain_api.hpp"
+
+extern "C"
+{
+
+   VKAPI_ATTR VkResult wsi_layer_vkCreateSwapchainKHR(VkDevice device,
+                                                      const VkSwapchainCreateInfoKHR *pSwapchainCreateInfo,
+                                                      const VkAllocationCallbacks *pAllocator,
+                                                      VkSwapchainKHR *pSwapchain)
+   {
+      assert(pSwapchain != nullptr);
+
+      wsi::swapchain_base *sc = nullptr;
+
+      VkIcdSurfaceBase *surface_base = reinterpret_cast<VkIcdSurfaceBase *>(pSwapchainCreateInfo->surface);
+      assert(VK_ICD_WSI_PLATFORM_HEADLESS == (int)surface_base->platform);
+
+      void *memory = nullptr;
+      if (pAllocator)
+      {
+         memory = static_cast<wsi::headless::swapchain *>(
+            pAllocator->pfnAllocation(pAllocator->pUserData, sizeof(wsi::headless::swapchain),
+                                      alignof(wsi::headless::swapchain), VK_SYSTEM_ALLOCATION_SCOPE_INSTANCE));
+      }
+      else
+      {
+         memory = static_cast<wsi::headless::swapchain *>(malloc(sizeof(wsi::headless::swapchain)));
+      }
+
+      if (memory)
+      {
+         sc = new (memory) wsi::headless::swapchain(layer::device_private_data::get(layer::get_key(device)), pAllocator);
+      }
+
+      if (sc == nullptr)
+      {
+         return VK_ERROR_OUT_OF_HOST_MEMORY;
+      }
+
+      VkResult result = sc->init(device, pSwapchainCreateInfo);
+      if (result != VK_SUCCESS)
+      {
+         /* Error occured during initialization, need to free allocated memory. */
+         sc->~swapchain_base();
+
+         if (pAllocator != nullptr)
+         {
+            pAllocator->pfnFree(pAllocator->pUserData, reinterpret_cast<void *>(sc));
+         }
+         else
+         {
+            free(reinterpret_cast<void *>(sc));
+         }
+
+         return result;
+      }
+
+      *pSwapchain = reinterpret_cast<VkSwapchainKHR>(sc);
+
+      return result;
+   }
+
+   VKAPI_ATTR void wsi_layer_vkDestroySwapchainKHR(VkDevice device, VkSwapchainKHR swapc,
+                                                   const VkAllocationCallbacks *pAllocator)
+   {
+      assert(swapc != VK_NULL_HANDLE);
+
+      wsi::swapchain_base *sc = reinterpret_cast<wsi::swapchain_base *>(swapc);
+
+      sc->~swapchain_base();
+
+      if (pAllocator != nullptr)
+      {
+         pAllocator->pfnFree(pAllocator->pUserData, reinterpret_cast<void *>(swapc));
+      }
+      else
+      {
+         free(reinterpret_cast<void *>(swapc));
+      }
+   }
+
+   VKAPI_ATTR VkResult wsi_layer_vkGetSwapchainImagesKHR(VkDevice device, VkSwapchainKHR swapc,
+                                                         uint32_t *pSwapchainImageCount, VkImage *pSwapchainImages)
+   {
+      assert(pSwapchainImageCount != nullptr);
+      assert(swapc != VK_NULL_HANDLE);
+
+      wsi::swapchain_base *sc = reinterpret_cast<wsi::swapchain_base *>(swapc);
+
+      return sc->get_swapchain_images(pSwapchainImageCount, pSwapchainImages);
+   }
+
+   VKAPI_ATTR VkResult wsi_layer_vkAcquireNextImageKHR(VkDevice device, VkSwapchainKHR swapc, uint64_t timeout,
+                                                       VkSemaphore semaphore, VkFence fence, uint32_t *pImageIndex)
+   {
+      assert(swapc != VK_NULL_HANDLE);
+      assert(semaphore != VK_NULL_HANDLE || fence != VK_NULL_HANDLE);
+      assert(pImageIndex != nullptr);
+
+      wsi::swapchain_base *sc = reinterpret_cast<wsi::swapchain_base *>(swapc);
+
+      return sc->acquire_next_image(timeout, semaphore, fence, pImageIndex);
+   }
+
+   VKAPI_ATTR VkResult wsi_layer_vkQueuePresentKHR(VkQueue queue, const VkPresentInfoKHR *pPresentInfo)
+   {
+      assert(queue != VK_NULL_HANDLE);
+      assert(pPresentInfo != nullptr);
+
+      uint32_t resultMask = 0;
+
+      for (uint32_t i = 0; i < pPresentInfo->swapchainCount; ++i)
+      {
+         wsi::swapchain_base *sc = reinterpret_cast<wsi::swapchain_base *>(pPresentInfo->pSwapchains[i]);
+         assert(sc != nullptr);
+
+         VkResult res = sc->queue_present(queue, pPresentInfo, pPresentInfo->pImageIndices[i]);
+
+         if (pPresentInfo->pResults != nullptr)
+         {
+            pPresentInfo->pResults[i] = res;
+         }
+
+         if (res == VK_ERROR_DEVICE_LOST)
+            resultMask |= (1u << 1);
+         else if (res == VK_ERROR_SURFACE_LOST_KHR)
+            resultMask |= (1u << 2);
+         else if (res == VK_ERROR_OUT_OF_DATE_KHR)
+            resultMask |= (1u << 3);
+      }
+
+      if (resultMask & (1u << 1))
+         return VK_ERROR_DEVICE_LOST;
+      else if (resultMask & (1u << 2))
+         return VK_ERROR_SURFACE_LOST_KHR;
+      else if (resultMask & (1u << 3))
+         return VK_ERROR_OUT_OF_DATE_KHR;
+
+      return VK_SUCCESS;
+   }
+
+} /* extern "C" */
diff --git a/layer/swapchain_api.hpp b/layer/swapchain_api.hpp
new file mode 100644 (file)
index 0000000..231001b
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2018-2019 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 swapchain_api.hpp
+ *
+ * @brief Contains the Vulkan entrypoints for the swapchain.
+ */
+
+#pragma once
+
+#include <vulkan/vulkan.h>
+
+extern "C"
+{
+
+   VKAPI_ATTR VkResult wsi_layer_vkCreateSwapchainKHR(VkDevice device, const VkSwapchainCreateInfoKHR *pSwapchainCreateInfo,
+                                                      const VkAllocationCallbacks *pAllocator, VkSwapchainKHR *pSwapchain);
+
+   VKAPI_ATTR void wsi_layer_vkDestroySwapchainKHR(VkDevice device, VkSwapchainKHR swapc,
+                                                   const VkAllocationCallbacks *pAllocator);
+
+   VKAPI_ATTR VkResult wsi_layer_vkGetSwapchainImagesKHR(VkDevice device, VkSwapchainKHR swapc,
+                                                         uint32_t *pSwapchainImageCount, VkImage *pSwapchainImages);
+
+   VKAPI_ATTR VkResult wsi_layer_vkAcquireNextImageKHR(VkDevice device, VkSwapchainKHR swapc, uint64_t timeout,
+                                                       VkSemaphore semaphore, VkFence fence, uint32_t *pImageIndex);
+
+   VKAPI_ATTR VkResult wsi_layer_vkQueuePresentKHR(VkQueue queue, const VkPresentInfoKHR *pPresentInfo);
+
+}
diff --git a/util/timed_semaphore.cpp b/util/timed_semaphore.cpp
new file mode 100644 (file)
index 0000000..138cc9c
--- /dev/null
@@ -0,0 +1,169 @@
+/*
+ * Copyright (c) 2017, 2019 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.
+ */
+
+#include <cassert>
+#include <cerrno>
+
+#include "timed_semaphore.hpp"
+
+namespace util
+{
+
+VkResult timed_semaphore::init(unsigned count)
+{
+   int res;
+
+   m_count = count;
+
+   pthread_condattr_t attr;
+   res = pthread_condattr_init(&attr);
+   /* the only failure that can occur is ENOMEM */
+   assert(res == 0 || res == ENOMEM);
+   if (res != 0)
+   {
+      return VK_ERROR_OUT_OF_HOST_MEMORY;
+   }
+
+   res = pthread_condattr_setclock(&attr, CLOCK_MONOTONIC);
+   /* only programming error can cause _setclock to fail */
+   assert(res == 0);
+
+   res = pthread_cond_init(&m_cond, &attr);
+   /* the only failure that can occur that is not programming error is ENOMEM */
+   assert(res == 0 || res == ENOMEM);
+   if (res != 0)
+   {
+      pthread_condattr_destroy(&attr);
+      return VK_ERROR_OUT_OF_HOST_MEMORY;
+   }
+
+   res = pthread_condattr_destroy(&attr);
+   /* only programming error can cause _destroy to fail */
+   assert(res == 0);
+
+   res = pthread_mutex_init(&m_mutex, NULL);
+   /* only programming errors can result in failure */
+   assert(res == 0);
+
+   initialized = true;
+
+   return VK_SUCCESS;
+}
+
+timed_semaphore::~timed_semaphore()
+{
+   int res;
+   (void)res; /* unused when NDEBUG */
+
+   if (initialized)
+   {
+      res = pthread_cond_destroy(&m_cond);
+      assert(res == 0); /* only programming error (EBUSY, EINVAL) */
+
+      res = pthread_mutex_destroy(&m_mutex);
+      assert(res == 0); /* only programming error (EBUSY, EINVAL) */
+   }
+}
+
+VkResult timed_semaphore::wait(uint64_t timeout)
+{
+   VkResult retval = VK_SUCCESS;
+   int res;
+
+   assert(initialized);
+
+   res = pthread_mutex_lock(&m_mutex);
+   assert(res == 0); /* only fails with programming error (EINVAL) */
+
+   if (m_count == 0)
+   {
+      switch (timeout)
+      {
+      case 0:
+         retval = VK_NOT_READY;
+         break;
+      case UINT64_MAX:
+         res = pthread_cond_wait(&m_cond, &m_mutex);
+         assert(res == 0); /* only fails with programming error (EINVAL) */
+
+         break;
+      default:
+         struct timespec diff = { /* narrowing casts */
+                                  static_cast<time_t>(timeout / (1000 * 1000 * 1000)),
+                                  static_cast<long>(timeout % (1000 * 1000 * 1000))
+         };
+
+         struct timespec now;
+         res = clock_gettime(CLOCK_MONOTONIC, &now);
+         assert(res == 0); /* only fails with programming error (EINVAL, EFAULT, EPERM) */
+
+         /* add diff to now, handling overflow */
+         struct timespec end = { now.tv_sec + diff.tv_sec, now.tv_nsec + diff.tv_nsec };
+
+         if (end.tv_nsec >= 1000 * 1000 * 1000)
+         {
+            end.tv_nsec -= 1000 * 1000 * 1000;
+            end.tv_sec++;
+         }
+
+         res = pthread_cond_timedwait(&m_cond, &m_mutex, &end);
+         /* only fails with programming error, other than timeout */
+         assert(res == 0 || res == ETIMEDOUT);
+         if (res != 0)
+         {
+            retval = VK_TIMEOUT;
+         }
+      }
+   }
+   if (retval == VK_SUCCESS)
+   {
+      assert(m_count > 0);
+      m_count--;
+   }
+   res = pthread_mutex_unlock(&m_mutex);
+   assert(res == 0); /* only fails with programming error (EPERM) */
+
+   return retval;
+}
+
+void timed_semaphore::post()
+{
+   int res;
+   (void)res; /* unused when NDEBUG */
+
+   assert(initialized);
+
+   res = pthread_mutex_lock(&m_mutex);
+   assert(res == 0); /* only fails with programming error (EINVAL) */
+
+   m_count++;
+
+   res = pthread_cond_signal(&m_cond);
+   assert(res == 0); /* only fails with programming error (EINVAL) */
+
+   res = pthread_mutex_unlock(&m_mutex);
+   assert(res == 0); /* only fails with programming error (EPERM) */
+}
+
+} /* namespace util */
diff --git a/util/timed_semaphore.hpp b/util/timed_semaphore.hpp
new file mode 100644 (file)
index 0000000..5988550
--- /dev/null
@@ -0,0 +1,115 @@
+/*
+ * Copyright (c) 2017, 2019 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 timed_semaphore.hpp
+ *
+ * @brief Contains the class definition for a semaphore with a relative timedwait
+ *
+ * sem_timedwait takes an absolute time, based on CLOCK_REALTIME. Simply
+ * taking the current time and adding on a relative timeout is not correct,
+ * as the system time may change, resulting in an incorrect timeout period
+ * (potentially by a significant amount).
+ *
+ * We therefore have to re-engineer semaphores using condition variables.
+ *
+ * This code does not use the C++ standard library to avoid exceptions.
+ */
+
+#pragma once
+
+extern "C"
+{
+#include <pthread.h>
+}
+
+#include <vulkan/vulkan.h>
+
+namespace util
+{
+
+/**
+ * brief semaphore with a safe relative timed wait
+ *
+ * sem_timedwait takes an absolute time, based on CLOCK_REALTIME. Simply
+ * taking the current time and adding on a relative timeout is not correct,
+ * as the system time may change, resulting in an incorrect timeout period
+ * (potentially by a significant amount).
+ *
+ * We therefore have to re-engineer semaphores using condition variables.
+ *
+ * This code does not use the C++ standard library to avoid exceptions.
+ */
+class timed_semaphore
+{
+public:
+   /* copying not implemented */
+   timed_semaphore &operator=(const timed_semaphore &) = delete;
+   timed_semaphore(const timed_semaphore &) = delete;
+
+   ~timed_semaphore();
+   timed_semaphore()
+      : initialized(false){};
+
+   /**
+    * @brief initializes the semaphore
+    *
+    * @param count initial value of the semaphore
+    * @retval VK_ERROR_OUT_OF_HOST_MEMORY out of memory condition from pthread calls
+    * @retval VK_SUCCESS on success
+    */
+   VkResult init(unsigned count);
+
+   /**
+    * @brief decrement semaphore, waiting (with timeout) if the value is 0
+    *
+    * @param timeout time to wait (ns). 0 doesn't block, UINT64_MAX waits indefinately.
+    * @retval VK_TIMEOUT timeout was non-zero and reached the timeout
+    * @retval VK_NOT_READY timeout was zero and count is 0
+    * @retval VK_SUCCESS on success
+    */
+   VkResult wait(uint64_t timeout);
+
+   /**
+    * @brief increment semaphore, potentially unblocking a waiting thread
+    */
+   void post();
+
+private:
+   /**
+    * @brief true if the semaphore has been initialized
+    *
+    * Determines if the destructor should cleanup the mutex and cond.
+    */
+   bool initialized;
+   /**
+    * @brief semaphore value
+    */
+   unsigned m_count;
+
+   pthread_mutex_t m_mutex;
+   pthread_cond_t m_cond;
+};
+
+} /* namespace util */
diff --git a/wsi/headless/surface_properties.cpp b/wsi/headless/surface_properties.cpp
new file mode 100644 (file)
index 0000000..a3f69b9
--- /dev/null
@@ -0,0 +1,165 @@
+/*
+ * Copyright (c) 2017-2019 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.
+ */
+
+#include <algorithm>
+#include <array>
+#include <cassert>
+#include <cstdlib>
+#include <map>
+#include <mutex>
+
+#include <vulkan/vk_icd.h>
+#include <vulkan/vulkan.h>
+
+#include <layer/private_data.hpp>
+
+#include "surface_properties.hpp"
+
+#define UNUSED(x) ((void)(x))
+
+namespace wsi
+{
+namespace headless
+{
+
+VkResult surface_properties::get_surface_capabilities(VkPhysicalDevice physical_device, VkSurfaceKHR surface,
+                                                      VkSurfaceCapabilitiesKHR *surface_capabilities)
+{
+   UNUSED(surface);
+   /* Image count limits */
+   surface_capabilities->minImageCount = 1;
+   /* There is no maximum theoretically speaking */
+   surface_capabilities->maxImageCount = UINT32_MAX;
+
+   /* Surface extents */
+   surface_capabilities->currentExtent = { 0xffffffff, 0xffffffff };
+   surface_capabilities->minImageExtent = { 1, 1 };
+   /* Ask the device for max */
+   VkPhysicalDeviceProperties dev_props;
+   layer::instance_private_data::get(layer::get_key(physical_device)).disp.GetPhysicalDeviceProperties(physical_device, &dev_props);
+
+   surface_capabilities->maxImageExtent = { dev_props.limits.maxImageDimension2D,
+                                            dev_props.limits.maxImageDimension2D };
+   surface_capabilities->maxImageArrayLayers = 1;
+
+   /* Surface transforms */
+   surface_capabilities->supportedTransforms = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR;
+   surface_capabilities->currentTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR;
+
+   /* Composite alpha */
+   surface_capabilities->supportedCompositeAlpha = static_cast<VkCompositeAlphaFlagBitsKHR>(
+      VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR | VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR |
+      VK_COMPOSITE_ALPHA_PRE_MULTIPLIED_BIT_KHR | VK_COMPOSITE_ALPHA_POST_MULTIPLIED_BIT_KHR);
+
+   /* Image usage flags */
+   surface_capabilities->supportedUsageFlags =
+      VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT |
+      VK_IMAGE_USAGE_STORAGE_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT;
+
+   return VK_SUCCESS;
+}
+
+VkResult surface_properties::get_surface_formats(VkPhysicalDevice physical_device, VkSurfaceKHR surface,
+                                                 uint32_t *surface_format_count, VkSurfaceFormatKHR *surface_formats)
+{
+   UNUSED(surface);
+
+   VkResult res = VK_SUCCESS;
+   /* Construct a list of all formats supported by the driver - for color attachment */
+   VkFormat formats[VK_FORMAT_RANGE_SIZE];
+   uint32_t format_count = 0;
+
+   for (int id = 0; id < VK_FORMAT_RANGE_SIZE; id++)
+   {
+      VkImageFormatProperties image_format_props;
+
+      res = layer::instance_private_data::get(layer::get_key(physical_device))
+               .disp.GetPhysicalDeviceImageFormatProperties(
+                  physical_device, static_cast<VkFormat>(id), VK_IMAGE_TYPE_2D, VK_IMAGE_TILING_OPTIMAL,
+                  VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT, &image_format_props);
+
+      if (res != VK_ERROR_FORMAT_NOT_SUPPORTED)
+      {
+         formats[format_count] = static_cast<VkFormat>(id);
+         format_count++;
+      }
+   }
+   assert(format_count > 0);
+   assert(surface_format_count != nullptr);
+   res = VK_SUCCESS;
+   if (nullptr == surface_formats)
+   {
+      *surface_format_count = format_count;
+   }
+   else
+   {
+      if (format_count > *surface_format_count)
+      {
+         res = VK_INCOMPLETE;
+      }
+
+      *surface_format_count = std::min(*surface_format_count, format_count);
+      for (uint32_t i = 0; i < *surface_format_count; ++i)
+      {
+         surface_formats[i].format = formats[i];
+         surface_formats[i].colorSpace = VK_COLORSPACE_SRGB_NONLINEAR_KHR;
+      }
+   }
+
+   return res;
+}
+
+VkResult surface_properties::get_surface_present_modes(VkPhysicalDevice physical_device, VkSurfaceKHR surface,
+                                                       uint32_t *present_mode_count, VkPresentModeKHR *present_modes)
+{
+   UNUSED(physical_device);
+   UNUSED(surface);
+
+   VkResult res = VK_SUCCESS;
+   static const std::array<VkPresentModeKHR, 2> modes = { VK_PRESENT_MODE_FIFO_KHR, VK_PRESENT_MODE_FIFO_RELAXED_KHR };
+
+   assert(present_mode_count != nullptr);
+
+   if (nullptr == present_modes)
+   {
+      *present_mode_count = modes.size();
+   }
+   else
+   {
+      if (modes.size() > *present_mode_count)
+      {
+         res = VK_INCOMPLETE;
+      }
+      *present_mode_count = std::min(*present_mode_count, static_cast<uint32_t>(modes.size()));
+      for (uint32_t i = 0; i < *present_mode_count; ++i)
+      {
+         present_modes[i] = modes[i];
+      }
+   }
+
+   return res;
+}
+
+} /* namespace headless */
+} /* namespace wsi */
diff --git a/wsi/headless/surface_properties.hpp b/wsi/headless/surface_properties.hpp
new file mode 100644 (file)
index 0000000..2f97f26
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2017-2019 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.
+ */
+
+#pragma once
+
+#include <vulkan/vk_icd.h>
+#include <vulkan/vulkan.h>
+#include <wsi/surface_properties.hpp>
+
+namespace wsi
+{
+namespace headless
+{
+
+struct surface_properties
+{
+   static VkResult get_surface_capabilities(VkPhysicalDevice physical_device, VkSurfaceKHR surface,
+                                            VkSurfaceCapabilitiesKHR *pSurfaceCapabilities);
+
+   static VkResult get_surface_formats(VkPhysicalDevice physical_device, VkSurfaceKHR surface, uint32_t *surfaceFormatCount,
+                                       VkSurfaceFormatKHR *surfaceFormats);
+
+   static VkResult get_surface_present_modes(VkPhysicalDevice physical_device, VkSurfaceKHR surface,
+                                             uint32_t *pPresentModeCount, VkPresentModeKHR *pPresentModes);
+};
+
+} /* namespace headless */
+} /* namespace wsi */
diff --git a/wsi/headless/swapchain.cpp b/wsi/headless/swapchain.cpp
new file mode 100644 (file)
index 0000000..4f09886
--- /dev/null
@@ -0,0 +1,178 @@
+/*
+ * Copyright (c) 2017-2019 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 swapchain.cpp
+ *
+ * @brief Contains the implementation for a headless swapchain.
+ */
+
+#include <cassert>
+#include <cstdlib>
+
+#include <util/timed_semaphore.hpp>
+
+#include "swapchain.hpp"
+
+namespace wsi
+{
+namespace headless
+{
+
+struct image_data
+{
+   /* Device memory backing the image. */
+   VkDeviceMemory memory;
+};
+
+swapchain::swapchain(layer::device_private_data &dev_data, const VkAllocationCallbacks *pAllocator)
+   : wsi::swapchain_base(dev_data, pAllocator)
+{
+}
+
+swapchain::~swapchain()
+{
+   /* Call the base's teardown */
+   teardown();
+}
+
+VkResult swapchain::create_image(const VkImageCreateInfo &image_create, wsi::swapchain_image &image)
+{
+   VkResult res = VK_SUCCESS;
+   res = m_device_data.disp.CreateImage(m_device, &image_create, nullptr, &image.image);
+   assert(VK_SUCCESS == res);
+
+   VkMemoryRequirements memory_requirements;
+   m_device_data.disp.GetImageMemoryRequirements(m_device, image.image, &memory_requirements);
+
+   /* Find a memory type */
+   size_t mem_type_idx = 0;
+   for (; mem_type_idx < 8 * sizeof(memory_requirements.memoryTypeBits); ++mem_type_idx)
+   {
+      if (memory_requirements.memoryTypeBits & (1u << mem_type_idx))
+      {
+         break;
+      }
+   }
+
+   assert(mem_type_idx <= 8 * sizeof(memory_requirements.memoryTypeBits) - 1);
+
+   VkMemoryAllocateInfo mem_info = {};
+   mem_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
+   mem_info.allocationSize = memory_requirements.size;
+   mem_info.memoryTypeIndex = mem_type_idx;
+   image_data *data = nullptr;
+
+   /* Create image_data */
+   if (m_alloc_callbacks != nullptr)
+   {
+      data = static_cast<image_data *>(m_alloc_callbacks->pfnAllocation(
+         m_alloc_callbacks->pUserData, sizeof(image_data), 0, VK_SYSTEM_ALLOCATION_SCOPE_OBJECT));
+   }
+   else
+   {
+      data = static_cast<image_data *>(malloc(sizeof(image_data)));
+   }
+
+   if (data == nullptr)
+   {
+      m_device_data.disp.DestroyImage(m_device, image.image, m_alloc_callbacks);
+      return VK_ERROR_OUT_OF_HOST_MEMORY;
+   }
+   image.data = reinterpret_cast<void *>(data);
+   image.status = wsi::swapchain_image::FREE;
+
+   res = m_device_data.disp.AllocateMemory(m_device, &mem_info, nullptr, &data->memory);
+   assert(VK_SUCCESS == res);
+   if (res != VK_SUCCESS)
+   {
+      destroy_image(image);
+      return res;
+   }
+
+   res = m_device_data.disp.BindImageMemory(m_device, image.image, data->memory, 0);
+   assert(VK_SUCCESS == res);
+   if (res != VK_SUCCESS)
+   {
+      destroy_image(image);
+      return res;
+   }
+
+   /* Initialize presentation fence. */
+   VkFenceCreateInfo fence_info = { VK_STRUCTURE_TYPE_FENCE_CREATE_INFO, nullptr, 0 };
+   res = m_device_data.disp.CreateFence(m_device, &fence_info, nullptr, &image.present_fence);
+   if (res != VK_SUCCESS)
+   {
+      destroy_image(image);
+      return res;
+   }
+   return res;
+}
+
+void swapchain::present_image(uint32_t pending_index)
+{
+   unpresent_image(pending_index);
+}
+
+void swapchain::destroy_image(wsi::swapchain_image &image)
+{
+   if (image.status != wsi::swapchain_image::INVALID)
+   {
+      if (image.present_fence != VK_NULL_HANDLE)
+      {
+         m_device_data.disp.DestroyFence(m_device, image.present_fence, nullptr);
+         image.present_fence = VK_NULL_HANDLE;
+      }
+
+      if (image.image != VK_NULL_HANDLE)
+      {
+         m_device_data.disp.DestroyImage(m_device, image.image, m_alloc_callbacks);
+         image.image = VK_NULL_HANDLE;
+      }
+   }
+
+   if (image.data != nullptr)
+   {
+      auto *data = reinterpret_cast<image_data *>(image.data);
+      if (data->memory != VK_NULL_HANDLE)
+      {
+         m_device_data.disp.FreeMemory(m_device, data->memory, nullptr);
+         data->memory = VK_NULL_HANDLE;
+      }
+      if (m_alloc_callbacks != nullptr)
+      {
+         m_alloc_callbacks->pfnFree(m_alloc_callbacks->pUserData, data);
+      }
+      else
+      {
+         free(data);
+      }
+      image.data = nullptr;
+   }
+
+   image.status = wsi::swapchain_image::INVALID;
+}
+
+} /* namespace headless */
+} /* namespace wsi */
diff --git a/wsi/headless/swapchain.hpp b/wsi/headless/swapchain.hpp
new file mode 100644 (file)
index 0000000..c25d5c7
--- /dev/null
@@ -0,0 +1,94 @@
+/*
+ * Copyright (c) 2017-2019 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 swapchain.hpp
+ *
+ * @brief Contains the class definition for a headless swapchain.
+ */
+
+#pragma once
+
+#include <vulkan/vk_icd.h>
+#include <vulkan/vulkan.h>
+#include <wsi/swapchain_base.hpp>
+
+namespace wsi
+{
+namespace headless
+{
+
+/**
+ * @brief Headless swapchain class.
+ *
+ * This class is mostly empty, because all the swapchain stuff is handled by the swapchain class,
+ * which we inherit. This class only provides a way to create an image and page-flip ops.
+ */
+class swapchain : public wsi::swapchain_base
+{
+public:
+   explicit swapchain(layer::device_private_data &dev_data, const VkAllocationCallbacks *pAllocator);
+
+   ~swapchain();
+
+protected:
+   /**
+    * @brief Platform specific init
+    */
+   VkResult init_platform(VkDevice device, const VkSwapchainCreateInfoKHR *pSwapchainCreateInfo)
+   {
+      return VK_SUCCESS;
+   };
+
+   /**
+    * @brief Creates a new swapchain image.
+    *
+    * @param image_create_info Data to be used to create the image.
+    *
+    * @param image Handle to the image.
+    *
+    * @return If image creation is successful returns VK_SUCCESS, otherwise
+    * will return VK_ERROR_OUT_OF_DEVICE_MEMORY or VK_ERROR_INITIALIZATION_FAILED
+    * depending on the error that occured.
+    */
+   VkResult create_image(const VkImageCreateInfo &image_create_info, wsi::swapchain_image &image);
+
+   /**
+    * @brief Method to perform a present - just calls unpresent_image on headless
+    *
+    * @param pendingIndex Index of the pending image to be presented.
+    *
+    */
+   void present_image(uint32_t pendingIndex);
+
+   /**
+    * @brief Method to release a swapchain image
+    *
+    * @param image Handle to the image about to be released.
+    */
+    void destroy_image(wsi::swapchain_image &image);
+};
+
+} /* namespace headless */
+} /* namespace wsi */
diff --git a/wsi/surface_properties.hpp b/wsi/surface_properties.hpp
new file mode 100644 (file)
index 0000000..b4b585b
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2017-2019 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 surface_properties.hpp
+ *
+ * @brief Vulkan WSI surface query interfaces.
+ */
+
+#pragma once
+
+#include <vulkan/vulkan.h>
+
+namespace wsi
+{
+
+/**
+ * @brief The base surface property query interface.
+ */
+template <typename T>
+struct surface_properties
+{
+   static VkResult get_surface_capabilities(VkPhysicalDevice physical_device, VkSurfaceKHR surface,
+                                            VkSurfaceCapabilitiesKHR *surface_capabilities)
+   {
+      return T::get_surface_capabilities(physical_device, surface, surface_capabilities);
+   }
+
+   static VkResult get_surface_formats(VkPhysicalDevice physical_device, VkSurfaceKHR surface,
+                                       uint32_t *surface_format_count, VkSurfaceFormatKHR *surface_formats)
+   {
+      return T::get_surface_formats(physical_device, surface, surface_format_count, surface_formats);
+   }
+
+   static VkResult get_surface_present_modes(VkPhysicalDevice physical_device, VkSurfaceKHR surface,
+                                             uint32_t *present_mode_count, VkPresentModeKHR *present_modes)
+   {
+      return T::get_surface_present_modes(physical_device, surface, present_mode_count, present_modes);
+   }
+};
+
+} /* namespace wsi */
diff --git a/wsi/swapchain_base.cpp b/wsi/swapchain_base.cpp
new file mode 100644 (file)
index 0000000..a9731b7
--- /dev/null
@@ -0,0 +1,684 @@
+/*
+ * Copyright (c) 2017-2019 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 swapchain_base.cpp
+ *
+ * @brief Contains the implementation for the swapchain.
+ *
+ * This file contains much of the swapchain implementation,
+ * that is not specific to how images are created or presented.
+ */
+
+#include <array>
+#include <cassert>
+#include <cerrno>
+#include <cstdio>
+#include <cstdlib>
+
+#include <unistd.h>
+#include <vulkan/vulkan.h>
+
+#include "swapchain_base.hpp"
+
+#if VULKAN_WSI_DEBUG > 0
+#define WSI_PRINT_ERROR(...) fprintf(stderr, ##__VA_ARGS__)
+#else
+#define WSI_PRINT_ERROR(...) (void)0
+#endif
+
+namespace wsi
+{
+
+/**
+ * @brief Per swapchain thread function that handles page flipping.
+ * This thread should be running for the lifetime of the swapchain.
+ * The thread simply calls the implementation's present_image() method.
+ * There are 3 main cases we cover here:
+ *
+ * 1. On the first present of the swapchain if the swapchain has
+ *    an ancestor we must wait for it to finish presenting.
+ * 2. The normal use case where we do page flipping, in this
+ *    case change the currently PRESENTED image with the oldest
+ *    PENDING image.
+ * 3. If the enqueued image is marked as FREE it means the
+ *    descendant of the swapchain has started presenting so we
+ *    should release the image and continue.
+ *
+ * The function always waits on the page_flip_semaphore of the
+ * swapchain. Once it passes that we must wait for the fence of the
+ * oldest pending image to be signalled, this means that the gpu has
+ * finished rendering to it and we can present it. From there on the
+ * logic splits into the above 3 cases and if an image has been
+ * presented then the old one is marked as FREE and the free_image
+ * semaphore of the swapchain will be posted.
+ **/
+__attribute__((noreturn)) void *page_flip_thread(void *ptr)
+{
+   auto *sc = static_cast<swapchain_base *>(ptr);
+   wsi::swapchain_image *sc_images = sc->m_swapchain_images;
+   VkResult vk_res = VK_SUCCESS;
+   uint64_t timeout = UINT64_MAX;
+
+   while (true)
+   {
+      /* Waiting for the page_flip_semaphore which will be signalled once there is an
+       * image to display.*/
+      sem_wait(&sc->m_page_flip_semaphore);
+
+      /* We want to present the oldest queued for present image from our present queue,
+       * which we can find at the sc->pending_buffer_pool.head index. */
+      uint32_t pending_index = sc->m_pending_buffer_pool.ring[sc->m_pending_buffer_pool.head];
+      sc->m_pending_buffer_pool.head = (sc->m_pending_buffer_pool.head + 1) % sc->m_pending_buffer_pool.size;
+
+      /* We wait for the fence of the oldest pending image to be signalled. */
+      vk_res = sc->m_device_data.disp.WaitForFences(sc->m_device, 1, &sc_images[pending_index].present_fence, VK_TRUE,
+                                                    timeout);
+      if (vk_res != VK_SUCCESS)
+      {
+         sc->m_is_valid = false;
+         sc->m_free_image_semaphore.post();
+         continue;
+      }
+
+      /* If the descendant has started presenting the queue_present operation has marked the image
+       * as FREE so we simply release it and continue. */
+      if (sc_images[pending_index].status == swapchain_image::FREE)
+      {
+         sc->destroy_image(sc_images[pending_index]);
+         sc->m_free_image_semaphore.post();
+         continue;
+      }
+
+      /* First present of the swapchain. If it has an ancestor, wait until all the pending buffers
+       * from the ancestor have finished page flipping before we set mode. */
+      if (sc->m_first_present)
+      {
+         if (sc->m_ancestor != VK_NULL_HANDLE)
+         {
+            auto *ancestor = reinterpret_cast<swapchain_base *>(sc->m_ancestor);
+            ancestor->wait_for_pending_buffers();
+         }
+
+         sem_post(&sc->m_start_present_semaphore);
+
+         sc->present_image(pending_index);
+
+         sc->m_first_present = false;
+      }
+      /* The swapchain has already started presenting. */
+      else
+      {
+         sc->present_image(pending_index);
+      }
+   }
+}
+
+void swapchain_base::unpresent_image(uint32_t presented_index)
+{
+   m_swapchain_images[presented_index].status = swapchain_image::FREE;
+
+   if (m_descendant != VK_NULL_HANDLE)
+   {
+      destroy_image(m_swapchain_images[presented_index]);
+   }
+
+   m_free_image_semaphore.post();
+}
+
+swapchain_base::swapchain_base(layer::device_private_data &dev_data, const VkAllocationCallbacks *allocator)
+   : m_device_data(dev_data)
+   , m_thread_sem_defined(false)
+   , m_first_present(true)
+   , m_pending_buffer_pool{ nullptr, 0, 0, 0 }
+   , m_num_swapchain_images(0)
+   , m_swapchain_images(nullptr)
+   , m_alloc_callbacks(allocator)
+   , m_surface(VK_NULL_HANDLE)
+   , m_present_mode(VK_PRESENT_MODE_IMMEDIATE_KHR)
+   , m_descendant(VK_NULL_HANDLE)
+   , m_ancestor(VK_NULL_HANDLE)
+   , m_device(VK_NULL_HANDLE)
+   , m_queue(VK_NULL_HANDLE)
+{
+}
+
+VkResult swapchain_base::init(VkDevice device, const VkSwapchainCreateInfoKHR *swapchain_create_info)
+{
+   assert(device != VK_NULL_HANDLE);
+   assert(swapchain_create_info != nullptr);
+   assert(swapchain_create_info->surface != VK_NULL_HANDLE);
+
+   int res;
+   VkResult result;
+
+   m_device = device;
+   m_surface = swapchain_create_info->surface;
+
+   /* Check presentMode has a compatible value with swapchain - everything else should be taken care at image creation.*/
+   static const std::array<VkPresentModeKHR, 2> present_modes = { VK_PRESENT_MODE_FIFO_KHR, VK_PRESENT_MODE_FIFO_RELAXED_KHR };
+   bool present_mode_found = false;
+   for (uint32_t i = 0; i < present_modes.size() && !present_mode_found; i++)
+   {
+      if (swapchain_create_info->presentMode == present_modes[i])
+      {
+         present_mode_found = true;
+      }
+   }
+
+   if (!present_mode_found)
+   {
+      return VK_ERROR_INITIALIZATION_FAILED;
+   }
+
+   m_num_swapchain_images = swapchain_create_info->minImageCount;
+
+   size_t images_alloc_size = sizeof(swapchain_image) * m_num_swapchain_images;
+   if (m_alloc_callbacks != nullptr)
+   {
+      m_swapchain_images = static_cast<swapchain_image *>(m_alloc_callbacks->pfnAllocation(
+         m_alloc_callbacks->pUserData, images_alloc_size, alignof(swapchain_image), VK_SYSTEM_ALLOCATION_SCOPE_OBJECT));
+   }
+   else
+   {
+      m_swapchain_images = static_cast<swapchain_image *>(malloc(images_alloc_size));
+   }
+
+   if (m_swapchain_images == nullptr)
+   {
+      m_num_swapchain_images = 0;
+      return VK_ERROR_OUT_OF_HOST_MEMORY;
+   }
+
+   /* We have allocated images, we can call the platform init function if something needs to be done. */
+   result = init_platform(device, swapchain_create_info);
+   if (result != VK_SUCCESS)
+   {
+      return result;
+   }
+
+   for (uint32_t i = 0; i < m_num_swapchain_images; ++i)
+   {
+      /* Init image to invalid values. */
+      m_swapchain_images[i].image = VK_NULL_HANDLE;
+      m_swapchain_images[i].present_fence = VK_NULL_HANDLE;
+      m_swapchain_images[i].status = swapchain_image::INVALID;
+      m_swapchain_images[i].data = nullptr;
+   }
+
+   /* Initialize ring buffer. */
+   if (m_alloc_callbacks != nullptr)
+   {
+      m_pending_buffer_pool.ring = static_cast<uint32_t *>(
+         m_alloc_callbacks->pfnAllocation(m_alloc_callbacks->pUserData, sizeof(uint32_t) * m_num_swapchain_images,
+                                          alignof(uint32_t), VK_SYSTEM_ALLOCATION_SCOPE_OBJECT));
+   }
+   else
+   {
+      m_pending_buffer_pool.ring = static_cast<uint32_t *>(malloc(sizeof(uint32_t) * m_num_swapchain_images));
+   }
+
+   if (m_pending_buffer_pool.ring == nullptr)
+   {
+      return VK_ERROR_OUT_OF_HOST_MEMORY;
+   }
+
+   m_pending_buffer_pool.head = 0;
+   m_pending_buffer_pool.tail = 0;
+   m_pending_buffer_pool.size = m_num_swapchain_images;
+
+   VkImageCreateInfo image_create_info = {};
+   image_create_info.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
+   image_create_info.pNext = nullptr;
+   image_create_info.imageType = VK_IMAGE_TYPE_2D;
+   image_create_info.format = swapchain_create_info->imageFormat;
+   image_create_info.extent = { swapchain_create_info->imageExtent.width, swapchain_create_info->imageExtent.height, 1 };
+   image_create_info.mipLevels = 1;
+   image_create_info.arrayLayers = swapchain_create_info->imageArrayLayers;
+   image_create_info.samples = VK_SAMPLE_COUNT_1_BIT;
+   image_create_info.tiling = VK_IMAGE_TILING_OPTIMAL;
+   image_create_info.usage = swapchain_create_info->imageUsage;
+   image_create_info.flags = 0;
+   image_create_info.sharingMode = swapchain_create_info->imageSharingMode;
+   image_create_info.queueFamilyIndexCount = swapchain_create_info->queueFamilyIndexCount;
+   image_create_info.pQueueFamilyIndices = swapchain_create_info->pQueueFamilyIndices;
+   image_create_info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
+
+   result = m_free_image_semaphore.init(m_num_swapchain_images);
+   if (result != VK_SUCCESS)
+   {
+      assert(result == VK_ERROR_OUT_OF_HOST_MEMORY);
+      return result;
+   }
+
+   for (unsigned i = 0; i < m_num_swapchain_images; i++)
+   {
+      result = create_image(image_create_info, m_swapchain_images[i]);
+      if (result != VK_SUCCESS)
+      {
+         return result;
+      }
+   }
+
+   m_device_data.disp.GetDeviceQueue(m_device, 0, 0, &m_queue);
+   result = m_device_data.SetDeviceLoaderData(m_device, m_queue);
+   if (VK_SUCCESS != result)
+   {
+      return result;
+   }
+
+   /* Setup semaphore for signaling pageflip thread */
+   res = sem_init(&m_page_flip_semaphore, 0, 0);
+
+   /* Only programming error can cause this to fail. */
+   assert(res == 0);
+   if (res != 0)
+   {
+      return VK_ERROR_OUT_OF_HOST_MEMORY;
+   }
+
+   res = sem_init(&m_start_present_semaphore, 0, 0);
+   /* Only programming error can cause this to fail. */
+   assert(res == 0);
+   if (res != 0)
+   {
+      return VK_ERROR_OUT_OF_HOST_MEMORY;
+   }
+
+   m_thread_sem_defined = true;
+
+   /* Launch page flipping thread */
+   res = pthread_create(&m_page_flip_thread, NULL, page_flip_thread, static_cast<void *>(this));
+   if (res < 0)
+   {
+      return VK_ERROR_OUT_OF_HOST_MEMORY;
+   }
+   /* Release the swapchain images of the old swapchain in order
+    * to free up memory for new swapchain. This is necessary especially
+    * on platform with limited display memory size.
+    *
+    * NB: This must be done last in initialization, when the rest of
+    * the swapchain is valid.
+    */
+   if (swapchain_create_info->oldSwapchain != VK_NULL_HANDLE)
+   {
+      /* Set ancestor. */
+      m_ancestor = swapchain_create_info->oldSwapchain;
+
+      auto *ancestor = reinterpret_cast<swapchain_base *>(m_ancestor);
+      ancestor->deprecate(reinterpret_cast<VkSwapchainKHR>(this));
+   }
+
+   m_is_valid = true;
+
+   return VK_SUCCESS;
+}
+
+void swapchain_base::teardown()
+{
+   /* This method will block until all resources associated with this swapchain
+    * are released. Images in the ACQUIRED or FREE state can be freed
+    * immediately. For images in the PRESENTED state, we will block until the
+    * presentation engine is finished with them. */
+
+   int res;
+   bool descendent_started_presenting = false;
+
+   if (m_descendant != VK_NULL_HANDLE)
+   {
+      auto *desc = reinterpret_cast<swapchain_base *>(m_descendant);
+      for (uint32_t i = 0; i < desc->m_num_swapchain_images; ++i)
+      {
+         if (desc->m_swapchain_images[i].status == swapchain_image::PRESENTED ||
+             desc->m_swapchain_images[i].status == swapchain_image::PENDING)
+         {
+            /* Here we wait for the start_present_semaphore, once this semaphore is up,
+             * the descendant has finished waiting, we don't want to delete vkImages and vkFences
+             * and semaphores before the waiting is done. */
+            sem_wait(&desc->m_start_present_semaphore);
+
+            descendent_started_presenting = true;
+            break;
+         }
+      }
+   }
+
+   /* If descendant started presenting, there is no pending buffer in the swapchain. */
+   if (descendent_started_presenting == false)
+   {
+      wait_for_pending_buffers();
+   }
+
+   /* Make sure the vkFences are done signaling. */
+   m_device_data.disp.QueueWaitIdle(m_queue);
+
+   /* We are safe to destroy everything. */
+
+   if (m_thread_sem_defined)
+   {
+      res = pthread_cancel(m_page_flip_thread);
+      if (res != 0)
+      {
+         WSI_PRINT_ERROR("pthread_cancel failed for page_flip_thread %lu with %d\n", m_page_flip_thread, res);
+      }
+
+      res = pthread_join(m_page_flip_thread, NULL);
+      if (res != 0)
+      {
+         WSI_PRINT_ERROR("pthread_join failed for page_flip_thread %lu with %d\n", m_page_flip_thread, res);
+      }
+
+      res = sem_destroy(&m_page_flip_semaphore);
+      if (res != 0)
+      {
+         WSI_PRINT_ERROR("sem_destroy failed for page_flip_semaphore with %d\n", errno);
+      }
+
+      res = sem_destroy(&m_start_present_semaphore);
+      if (res != 0)
+      {
+         WSI_PRINT_ERROR("sem_destroy failed for start_present_semaphore with %d\n", errno);
+      }
+   }
+
+   if (m_descendant != VK_NULL_HANDLE)
+   {
+      auto *sc = reinterpret_cast<swapchain_base *>(m_descendant);
+      sc->clear_ancestor();
+   }
+
+   if (m_ancestor != VK_NULL_HANDLE)
+   {
+      auto *sc = reinterpret_cast<swapchain_base *>(m_ancestor);
+      sc->clear_descendant();
+   }
+
+   /* Release the images array. */
+   if (m_swapchain_images != nullptr)
+   {
+
+      for (uint32_t i = 0; i < m_num_swapchain_images; ++i)
+      {
+         /* Call implementation specific release */
+         destroy_image(m_swapchain_images[i]);
+      }
+
+      if (m_alloc_callbacks != nullptr)
+      {
+         m_alloc_callbacks->pfnFree(m_alloc_callbacks->pUserData, m_swapchain_images);
+      }
+      else
+      {
+         free(m_swapchain_images);
+      }
+   }
+
+   /* Free ring buffer. */
+   if (m_pending_buffer_pool.ring != nullptr)
+   {
+      if (m_alloc_callbacks != nullptr)
+      {
+         m_alloc_callbacks->pfnFree(m_alloc_callbacks->pUserData, m_pending_buffer_pool.ring);
+      }
+      else
+      {
+         free(m_pending_buffer_pool.ring);
+      }
+   }
+}
+
+VkResult swapchain_base::acquire_next_image(uint64_t timeout, VkSemaphore semaphore, VkFence fence, uint32_t *image_index)
+{
+   VkResult retval = wait_for_free_buffer(timeout);
+   if (retval != VK_SUCCESS)
+   {
+      return retval;
+   }
+
+   if (!m_is_valid)
+   {
+      return VK_ERROR_OUT_OF_HOST_MEMORY;
+   }
+
+   uint32_t i;
+   for (i = 0; i < m_num_swapchain_images; ++i)
+   {
+      if (m_swapchain_images[i].status == swapchain_image::FREE)
+      {
+         m_swapchain_images[i].status = swapchain_image::ACQUIRED;
+         *image_index = i;
+         break;
+      }
+   }
+
+   assert(i < m_num_swapchain_images);
+
+   if (VK_NULL_HANDLE != semaphore || VK_NULL_HANDLE != fence)
+   {
+      VkSubmitInfo submit = { VK_STRUCTURE_TYPE_SUBMIT_INFO };
+
+      if (VK_NULL_HANDLE != semaphore)
+      {
+         submit.signalSemaphoreCount = 1;
+         submit.pSignalSemaphores = &semaphore;
+      }
+
+      submit.commandBufferCount = 0;
+      submit.pCommandBuffers = nullptr;
+      retval = m_device_data.disp.QueueSubmit(m_queue, 1, &submit, fence);
+      assert(retval == VK_SUCCESS);
+   }
+
+   return retval;
+}
+
+VkResult swapchain_base::get_swapchain_images(uint32_t *swapchain_image_count, VkImage *swapchain_images)
+{
+   if (swapchain_images == nullptr)
+   {
+      /* Return the number of swapchain images. */
+      *swapchain_image_count = m_num_swapchain_images;
+
+      return VK_SUCCESS;
+   }
+   else
+   {
+      assert(m_num_swapchain_images > 0);
+      assert(*swapchain_image_count > 0);
+
+      /* Populate array, write actual number of images returned. */
+      uint32_t current_image = 0;
+
+      do
+      {
+         swapchain_images[current_image] = m_swapchain_images[current_image].image;
+
+         current_image++;
+
+         if (current_image == m_num_swapchain_images)
+         {
+            *swapchain_image_count = current_image;
+
+            return VK_SUCCESS;
+         }
+
+      } while (current_image < *swapchain_image_count);
+
+      /* If swapchain_image_count is smaller than the number of presentable images
+       * in the swapchain, VK_INCOMPLETE must be returned instead of VK_SUCCESS. */
+      *swapchain_image_count = current_image;
+
+      return VK_INCOMPLETE;
+   }
+}
+
+VkResult swapchain_base::queue_present(VkQueue queue, const VkPresentInfoKHR *present_info, const uint32_t image_index)
+{
+   VkResult result;
+   bool descendent_started_presenting = false;
+
+   if (m_descendant != VK_NULL_HANDLE)
+   {
+      auto *desc = reinterpret_cast<swapchain_base *>(m_descendant);
+      for (uint32_t i = 0; i < desc->m_num_swapchain_images; ++i)
+      {
+         if (desc->m_swapchain_images[i].status == swapchain_image::PRESENTED ||
+             desc->m_swapchain_images[i].status == swapchain_image::PENDING)
+         {
+            descendent_started_presenting = true;
+            break;
+         }
+      }
+   }
+
+   /* When the semaphore that comes in is signalled, we know that all work is done. So, we do not
+    * want to block any future Vulkan queue work on it. So, we pass in BOTTOM_OF_PIPE bit as the
+    * wait flag.
+    */
+   VkPipelineStageFlags pipeline_stage_flags = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT;
+
+   VkSubmitInfo submit_info = { VK_STRUCTURE_TYPE_SUBMIT_INFO,
+                               NULL,
+                               present_info->waitSemaphoreCount,
+                               present_info->pWaitSemaphores,
+                               &pipeline_stage_flags,
+                               0,
+                               NULL,
+                               0,
+                               NULL };
+
+   assert(m_swapchain_images[image_index].status == swapchain_image::ACQUIRED);
+   result = m_device_data.disp.ResetFences(m_device, 1, &m_swapchain_images[image_index].present_fence);
+   if (result != VK_SUCCESS)
+   {
+      return result;
+   }
+
+   result = m_device_data.disp.QueueSubmit(queue, 1, &submit_info, m_swapchain_images[image_index].present_fence);
+   if (result != VK_SUCCESS)
+   {
+      return result;
+   }
+
+   /* If the descendant has started presenting, we should release the image
+    * however we do not want to block inside the main thread so we mark it
+    * as free and let the page flip thread take care of it. */
+   if (descendent_started_presenting)
+   {
+      m_swapchain_images[image_index].status = swapchain_image::FREE;
+
+      m_pending_buffer_pool.ring[m_pending_buffer_pool.tail] = image_index;
+      m_pending_buffer_pool.tail = (m_pending_buffer_pool.tail + 1) % m_pending_buffer_pool.size;
+
+      sem_post(&m_page_flip_semaphore);
+
+      return VK_ERROR_OUT_OF_DATE_KHR;
+   }
+
+   m_swapchain_images[image_index].status = swapchain_image::PENDING;
+
+   m_pending_buffer_pool.ring[m_pending_buffer_pool.tail] = image_index;
+   m_pending_buffer_pool.tail = (m_pending_buffer_pool.tail + 1) % m_pending_buffer_pool.size;
+
+   sem_post(&m_page_flip_semaphore);
+
+   return VK_SUCCESS;
+}
+
+void swapchain_base::deprecate(VkSwapchainKHR descendant)
+{
+   for (unsigned i = 0; i < m_num_swapchain_images; i++)
+   {
+      if (m_swapchain_images[i].status == swapchain_image::FREE)
+      {
+         destroy_image(m_swapchain_images[i]);
+      }
+   }
+
+   /* Set its descendant. */
+   m_descendant = descendant;
+}
+
+void swapchain_base::wait_for_pending_buffers()
+{
+   int num_acquired_images = 0;
+   int wait;
+
+   for (uint32_t i = 0; i < m_num_swapchain_images; ++i)
+   {
+      if (m_swapchain_images[i].status == swapchain_image::ACQUIRED)
+      {
+         ++num_acquired_images;
+      }
+   }
+
+   /* Once all the pending buffers are flipped, the swapchain should have images
+    * in ACQUIRED (application fails to queue them back for presentation), FREE
+    * and one and only one in PRESENTED. */
+   wait = m_num_swapchain_images - num_acquired_images - 1;
+
+   while (wait > 0)
+   {
+      /* Take down one free image semaphore. */
+      wait_for_free_buffer(UINT64_MAX);
+      --wait;
+   }
+}
+
+void swapchain_base::clear_ancestor()
+{
+   m_ancestor = VK_NULL_HANDLE;
+}
+
+void swapchain_base::clear_descendant()
+{
+   m_descendant = VK_NULL_HANDLE;
+}
+
+VkResult swapchain_base::wait_for_free_buffer(uint64_t timeout)
+{
+   VkResult retval;
+   /* first see if a buffer is already marked as free */
+   retval = m_free_image_semaphore.wait(0);
+   if (retval == VK_NOT_READY)
+   {
+      /* if not, we still have work to do even if timeout==0 -
+       * the swapchain implementation may be able to get a buffer without
+       * waiting */
+
+      retval = get_free_buffer(&timeout);
+      if (retval == VK_SUCCESS)
+      {
+         /* the sub-implementation has done it's thing, so re-check the
+          * semaphore */
+         retval = m_free_image_semaphore.wait(timeout);
+      }
+   }
+
+   return retval;
+}
+
+#undef WSI_PRINT_ERROR
+
+} /* namespace wsi */
diff --git a/wsi/swapchain_base.hpp b/wsi/swapchain_base.hpp
new file mode 100644 (file)
index 0000000..1b827a3
--- /dev/null
@@ -0,0 +1,361 @@
+/*
+ * Copyright (c) 2017-2019 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 swapchain_base.hpp
+ *
+ * @brief Contains the class definition for a base swapchain.
+ */
+
+#pragma once
+
+#include <pthread.h>
+#include <semaphore.h>
+#include <vulkan/vulkan.h>
+
+#include <layer/private_data.hpp>
+#include <util/timed_semaphore.hpp>
+
+namespace wsi
+{
+
+/* Forward declare the page flip thread function so we can befriend it. */
+void *page_flip_thread(void *ptr);
+
+struct swapchain_image
+{
+   enum status
+   {
+      INVALID,
+      ACQUIRED,
+      PENDING,
+      PRESENTED,
+      FREE,
+   };
+
+   /* Implementation specific data */
+   void *data;
+
+   VkImage image;
+   status status;
+
+   VkFence present_fence;
+};
+
+/**
+ * @brief Base swapchain class
+ *
+ * - the swapchain implementations inherit from this class.
+ * - the VkSwapchain will hold a pointer to this class.
+ * - much of the swapchain implementation is done by this class, as the only things needed
+ *   in the implementation are how to create a presentable image and how to present an image.
+ */
+class swapchain_base
+{
+public:
+   swapchain_base(layer::device_private_data &dev_data, const VkAllocationCallbacks *allocator);
+
+   virtual ~swapchain_base()
+   {
+      /* nop */
+   }
+
+   /**
+    * @brief Create swapchain.
+    *
+    * Perform all swapchain initialization, create presentable images etc.
+    */
+   VkResult init(VkDevice device, const VkSwapchainCreateInfoKHR *swapchain_create_info);
+
+   /**
+    * @brief Acquires a free image.
+    *
+    * Current implementation blocks until a free image is available.
+    *
+    * @param timeout Unused since we block until a free image is available.
+    *
+    * @param semaphore A semaphore signaled once an image is acquired.
+    *
+    * @param fence A fence signaled once an image is acquired.
+    *
+    * @param pImageIndex The index of the acquired image.
+    *
+    * @return VK_SUCCESS on completion.
+    */
+   VkResult acquire_next_image(uint64_t timeout, VkSemaphore semaphore, VkFence fence, uint32_t *image_index);
+
+   /**
+    * @brief Gets the number of swapchain images or a number of at most
+    * m_num_swapchain_images images.
+    *
+    * @param pSwapchainImageCount Used to return number of images in
+    * the swapchain if second parameter is nullptr or represents the
+    * number of images to be returned in second parameter.
+    *
+    * @param pSwapchainImage Array of VkImage handles.
+    *
+    * @return If number of requested images is less than the number of available
+    * images in the swapchain returns VK_INCOMPLETE otherwise VK_SUCCESS.
+    */
+   VkResult get_swapchain_images(uint32_t *swapchain_image_count, VkImage *swapchain_image);
+
+   /**
+    * @brief Submits a present request for the supplied image.
+    *
+    * @param queue The queue to which the submission will be made to.
+    *
+    * @param pPresentInfo Information about the swapchain and image to be presented.
+    *
+    * @param imageIndex The index of the image to be presented.
+    *
+    * @return If queue submission fails returns error of vkQueueSubmit, if the
+    * swapchain has a descendant who started presenting returns VK_ERROR_OUT_OF_DATE_KHR,
+    * otherwise returns VK_SUCCESS.
+    */
+   VkResult queue_present(VkQueue queue, const VkPresentInfoKHR *present_info, const uint32_t image_index);
+
+protected:
+   friend void *page_flip_thread(void *ptr);
+
+   layer::device_private_data &m_device_data;
+
+   /**
+    * @brief Handle to the page flip thread.
+    */
+   pthread_t m_page_flip_thread;
+
+   /**
+    * @brief In case we encounter threading or drm errors we need a way to
+    * notify the user of the failure. When this flag is false, acquire_next_image
+    * will return an error code.
+    */
+   bool m_is_valid;
+
+   struct ring_buffer
+   {
+      /* Ring buffer to hold the image indexes. */
+      uint32_t *ring;
+      /* Head of the ring. */
+      uint32_t head;
+      /* End of the ring. */
+      uint32_t tail;
+      /* Size of the ring. */
+      uint32_t size;
+   };
+   /**
+    * @brief A semaphore to be signalled once a page flip even occurs.
+    */
+   sem_t m_page_flip_semaphore;
+
+   /**
+    * @brief A semaphore to be signalled once the swapchain has one frame on screen.
+    */
+   sem_t m_start_present_semaphore;
+
+   /**
+    * @brief Defines if the pthread_t and sem_t members of the class are defined.
+    *
+    * As they are opaque types theer's no known invalid value that we ca initialize to,
+    * and therefore determine if we need to cleanup.
+    */
+   bool m_thread_sem_defined;
+
+   /**
+    * @brief A flag to track if it is the first present for the chain.
+    */
+   bool m_first_present;
+
+   /**
+    * @brief In order to present the images in a FIFO order we implement
+    * a ring buffer to hold the images queued for presentation. Since the
+    * two pointers (head and tail) are used by different
+    * therads and we do not allow the application to acquire more images
+    * than we have we eliminate race conditions.
+    */
+   ring_buffer m_pending_buffer_pool;
+
+   /**
+    * @brief The number of swapchain images.
+    */
+   uint32_t m_num_swapchain_images;
+
+   /**
+    * @brief Array of images in the swapchain.
+    */
+   swapchain_image *m_swapchain_images;
+
+   /**
+    * @brief User provided memory allocation callbacks.
+    */
+   const VkAllocationCallbacks *m_alloc_callbacks;
+
+   /**
+    * @brief Handle to the surface object this swapchain will present images to.
+    */
+   VkSurfaceKHR m_surface;
+
+   /**
+    * @brief present mode to use for this swapchain
+    */
+   VkPresentModeKHR m_present_mode;
+
+   /**
+    * @brief Descendant of this swapchain.
+    * Used to check whether or not a descendant of this swapchain has started
+    * presenting images to the surface already. If it has, any calls to queuePresent
+    * for this swapchain will return VK_ERROR_OUT_OF_DATE_KHR.
+    */
+   VkSwapchainKHR m_descendant;
+
+   /**
+    * @brief Ancestor of this swapchain.
+    * Used to check whether the ancestor swapchain has completed all of its
+    * pending page flips (this is required before this swapchain presents for the
+    * first time.
+    */
+   VkSwapchainKHR m_ancestor;
+
+   /**
+    *  @brief Handle to the logical device the swapchain is created for.
+    */
+   VkDevice m_device;
+
+   /**
+    *  @brief Handle to the queue used for signalling submissions
+    */
+   VkQueue m_queue;
+
+   /*
+    * @brief Method to wait on all pending buffers to be displayed.
+    */
+   void wait_for_pending_buffers();
+
+   /**
+    * @brief Remove cached ancestor.
+    */
+   void clear_ancestor();
+
+   /**
+    * @brief Remove cached descendant.
+    */
+   void clear_descendant();
+
+   /**
+    * @brief Deprecate this swapchain.
+    *
+    * If an application replaces an old swapchain with a new one, the older swapchain
+    * needs to be deprecated. This method releases all the FREE images and sets the
+    * descendant of the swapchain. We do not need to care about images in other states
+    * at this point since they will be released by the page flip thread.
+    *
+    * @param descendant Handle to the descendant swapchain.
+    */
+   void deprecate(VkSwapchainKHR descendant);
+
+   /**
+    * @brief Platform specific initialization
+    */
+   virtual VkResult init_platform(VkDevice device, const VkSwapchainCreateInfoKHR *swapchain_create_info) = 0;
+
+   /**
+    * @brief Base swapchain teardown.
+    *
+    * Even though the inheritance gives us a nice way to defer display specific allocation
+    * and presentation outside of the base class, it however robs the children classes - which
+    * also happen to do some of their state setting - the oppurtunity to do the last clean up
+    * call, as the base class' destructor is called at the end. This method provides a way to do it.
+    * The destructor is a virtual function and much of the swapchain teardown happens in this method
+    * which gets called from the child's destructor.
+    */
+   void teardown();
+
+   /**
+    * @brief Creates a new swapchain image.
+    *
+    * @param image_create_info Data to be used to create the image.
+    *
+    * @param image Handle to the image.
+    *
+    * @return If image creation is successful returns VK_SUCCESS, otherwise
+    * will return VK_ERROR_OUT_OF_DEVICE_MEMORY or VK_ERROR_INITIALIZATION_FAILED
+    * depending on the error that occured.
+    */
+   virtual VkResult create_image(const VkImageCreateInfo &image_create_info, swapchain_image &image) = 0;
+
+   /**
+    * @brief Method to present and image
+    *
+    * @param pending_index Index of the pending image to be presented.
+    *
+    */
+   virtual void present_image(uint32_t pending_index) = 0;
+
+   /**
+    * @brief Transition a presented image to free.
+    *
+    * Called by swapchain implementation when a new image has been presented.
+    *
+    * @param presented_index Index of the image to be marked as free.
+    */
+   void unpresent_image(uint32_t presented_index);
+
+   /**
+    * @brief Method to release a swapchain image
+    *
+    * @param image Handle to the image about to be released.
+    */
+   virtual void destroy_image(swapchain_image &image){};
+
+   /**
+    * @brief Hook for any actions to free up a buffer for acquire
+    *
+    * @param[in,out] timeout time to wait, in nanoseconds. 0 doesn't block,
+    *                        UINT64_MAX waits indefinately. The timeout should
+    *                        be updated if a sleep is required - this can
+    *                        be set to 0 if the semaphore is now not expected
+    *                        block.
+    */
+   virtual VkResult get_free_buffer(uint64_t *timeout)
+   {
+      return VK_SUCCESS;
+   }
+
+private:
+   /**
+    * @brief Wait for a buffer to become free.
+    */
+   VkResult wait_for_free_buffer(uint64_t timeout);
+
+   /**
+    * @brief A semaphore to be signalled once a free image becomes available.
+    *
+    * Uses a custom semaphore implementation that uses a condition variable.
+    * it is slower, but has a safe timedwait implementation.
+    *
+    * This is kept private as waiting should be done via wait_for_free_buffer().
+    */
+   util::timed_semaphore m_free_image_semaphore;
+};
+
+} /* namespace wsi */