--- /dev/null
+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
--- /dev/null
+# 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})
--- /dev/null
+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.
--- /dev/null
+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.
--- /dev/null
+# 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.
--- /dev/null
+{
+ "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"
+ }
+ }
+}
--- /dev/null
+/*
+ * 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" */
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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" */
--- /dev/null
+/*
+ * 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);
+
+}
--- /dev/null
+/*
+ * 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" */
--- /dev/null
+/*
+ * 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);
+
+}
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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 */