Add tizen platform in vulkan-wsi-layer 10/255710/3
authorTianhao Ni <tianhao.ni@samsung.com>
Tue, 23 Mar 2021 01:11:09 +0000 (09:11 +0800)
committerTianhao Ni <tianhao.ni@samsung.com>
Tue, 23 Mar 2021 05:35:07 +0000 (13:35 +0800)
Change-Id: I34f8c7b909c54329e67ea8658b86f168c29ce28c
Signed-off-by: Tianhao Ni <tianhao.ni@samsung.com>
CMakeLists.txt
packaging/vulkan-wsi-layer.manifest [new file with mode: 0644]
packaging/vulkan-wsi-layer.spec [new file with mode: 0644]
wsi/tizen/surface_properties.cpp [new file with mode: 0644]
wsi/tizen/surface_properties.hpp [new file with mode: 0644]
wsi/tizen/swapchain.cpp [new file with mode: 0644]
wsi/tizen/swapchain.hpp [new file with mode: 0644]
wsi/tizen/swapchain_wl_helpers.cpp [new file with mode: 0644]
wsi/tizen/swapchain_wl_helpers.hpp [new file with mode: 0644]
wsi/wsi_factory.cpp

index dd13783..23cf287 100644 (file)
@@ -50,6 +50,8 @@ endif()
 option(BUILD_WSI_WAYLAND "Build with support for VK_KHR_wayland_surface" OFF)
 set(SELECT_EXTERNAL_ALLOCATOR "none" CACHE STRING "Select an external system allocator (none, ion)")
 
+option(BUILD_WSI_TIZEN "Build with support for VK_KHR_wayland_surface(For Tizen platform)" OFF)
+
 if(BUILD_WSI_WAYLAND)
    if(SELECT_EXTERNAL_ALLOCATOR STREQUAL "none")
       message(FATAL_ERROR "Wayland only supported with an external allocator.")
@@ -57,6 +59,10 @@ if(BUILD_WSI_WAYLAND)
    set(BUILD_DRM_UTILS True)
 endif()
 
+if(BUILD_WSI_TIZEN)
+   set(BUILD_DRM_UTILS True)
+endif()
+
 # DRM Utilities
 if(BUILD_DRM_UTILS)
    add_library(drm_utils STATIC util/drm/drm_utils.cpp)
@@ -136,6 +142,49 @@ if(BUILD_WSI_WAYLAND)
    target_compile_options(wayland_wsi INTERFACE "-DBUILD_WSI_WAYLAND=1")
    target_link_libraries(wayland_wsi drm_utils wsialloc ${WAYLAND_CLIENT_LDFLAGS})
    list(APPEND LINK_WSI_LIBS wayland_wsi)
+elseif(BUILD_WSI_TIZEN)
+   add_library(tizen_wsi STATIC
+      wsi/tizen/surface_properties.cpp
+      wsi/tizen/swapchain_wl_helpers.cpp
+      wsi/tizen/swapchain.cpp)
+
+   pkg_check_modules(WAYLAND_CLIENT REQUIRED wayland-client)
+   message(STATUS "Using Wayland client include directories: ${WAYLAND_CLIENT_INCLUDE_DIRS}")
+   message(STATUS "Using Wayland client cflags: ${WAYLAND_CLIENT_CFLAGS}")
+   message(STATUS "Using Wayland client ldflags: ${WAYLAND_CLIENT_LDFLAGS}")
+
+   pkg_check_modules(WAYLAND_SCANNER REQUIRED wayland-scanner)
+   pkg_get_variable(WAYLAND_SCANNER_EXEC wayland-scanner wayland_scanner)
+   message(STATUS "Using wayland-scanner : ${WAYLAND_SCANNER_EXEC}")
+
+   pkg_check_modules(WAYLAND_PROTOCOLS REQUIRED wayland-protocols)
+   pkg_get_variable(WAYLAND_PROTOCOLS_DIR wayland-protocols pkgdatadir)
+   message(STATUS "Using wayland protocols dir : ${WAYLAND_PROTOCOLS_DIR}")
+
+   add_custom_target(wayland_generated_files
+      COMMAND ${WAYLAND_SCANNER_EXEC} client-header
+      ${WAYLAND_PROTOCOLS_DIR}/unstable/linux-dmabuf/linux-dmabuf-unstable-v1.xml
+      ${CMAKE_CURRENT_BINARY_DIR}/linux-dmabuf-unstable-v1-client-protocol.h
+      COMMAND ${WAYLAND_SCANNER_EXEC} code
+      ${WAYLAND_PROTOCOLS_DIR}/unstable/linux-dmabuf/linux-dmabuf-unstable-v1.xml
+      ${CMAKE_CURRENT_BINARY_DIR}/linux-dmabuf-unstable-v1-protocol.c
+      BYPRODUCTS linux-dmabuf-unstable-v1-protocol.c linux-dmabuf-unstable-v1-client-protocol.h)
+
+   target_sources(tizen_wsi PRIVATE
+      ${CMAKE_CURRENT_BINARY_DIR}/linux-dmabuf-unstable-v1-protocol.c
+      ${CMAKE_CURRENT_BINARY_DIR}/linux-dmabuf-unstable-v1-client-protocol.h)
+   add_dependencies(tizen_wsi wayland_generated_files)
+
+   target_include_directories(tizen_wsi PRIVATE
+      ${PROJECT_SOURCE_DIR}
+      ${VULKAN_CXX_INCLUDE}
+      ${WAYLAND_CLIENT_INCLUDE_DIRS}
+      ${CMAKE_CURRENT_BINARY_DIR})
+
+   target_compile_options(tizen_wsi PRIVATE ${WAYLAND_CLIENT_CFLAGS})
+   target_compile_options(tizen_wsi INTERFACE "-DBUILD_WSI_TIZEN=1")
+   target_link_libraries(tizen_wsi dlog tbm tpl-egl drm_utils ${WAYLAND_CLIENT_LDFLAGS})
+   list(APPEND LINK_WSI_LIBS tizen_wsi)
 else()
    list(APPEND JSON_COMMANDS COMMAND sed -i '/VK_KHR_wayland_surface/d' ${CMAKE_CURRENT_BINARY_DIR}/VkLayer_window_system_integration.json)
 endif()
diff --git a/packaging/vulkan-wsi-layer.manifest b/packaging/vulkan-wsi-layer.manifest
new file mode 100644 (file)
index 0000000..97e8c31
--- /dev/null
@@ -0,0 +1,5 @@
+<manifest>
+       <request>
+               <domain name="_"/>
+       </request>
+</manifest>
diff --git a/packaging/vulkan-wsi-layer.spec b/packaging/vulkan-wsi-layer.spec
new file mode 100644 (file)
index 0000000..b525253
--- /dev/null
@@ -0,0 +1,49 @@
+Name:          vulkan-wsi-layer
+Version:       1.0.5
+Release:       0
+Summary:       Vulkan WSI (Window System Integration) Layer for Tizen
+License:       MIT
+Group:         Graphics & UI Framework/Hardware Adaptation
+
+Source:                %{name}-%{version}.tar.gz
+
+BuildRequires: autoconf > 2.64
+BuildRequires: automake >= 1.11
+BuildRequires: libtool >= 2.2
+
+BuildRequires: cmake
+BuildRequires: pkgconfig(tpl-egl)
+BuildRequires: pkgconfig(libtbm)
+BuildRequires: pkgconfig(libtdm)
+BuildRequires: pkgconfig(vulkan)
+BuildRequires: pkgconfig(wayland-client)
+BuildRequires: pkgconfig(wayland-server)
+BuildRequires: pkgconfig(wayland-protocols)
+BuildRequires: pkgconfig(libtbm)
+BuildRequires: pkgconfig(libdrm)
+BuildRequires: pkgconfig(dlog)
+BuildRequires: vulkan-headers
+
+#%define _unpackaged_files_terminate_build 0
+%global TZ_SYS_RO_SHARE  %{?TZ_SYS_RO_SHARE:%TZ_SYS_RO_SHARE}%{!?TZ_SYS_RO_SHARE:/usr/share}
+
+%description
+Vulkan WSI (Window System Integration) Layer for Tizen
+
+
+%prep
+%setup -q
+
+%build
+cmake . -Bbuild -DCMAKE_INSTALL_PREFIX=/usr -DBUILD_WSI_TIZEN=ON
+make -C build
+
+%install
+mkdir -p %{buildroot}/usr/local/etc/vulkan/implicit_layer.d/
+cp build/VkLayer_window_system_integration.json  %{buildroot}/usr/local/etc/vulkan/implicit_layer.d/
+cp build/libVkLayer_window_system_integration.so %{buildroot}/usr/local/etc/vulkan/implicit_layer.d/
+
+%files -n %{name}
+%defattr(-,root,root,-)
+/usr/local/etc/vulkan/implicit_layer.d/*
+
diff --git a/wsi/tizen/surface_properties.cpp b/wsi/tizen/surface_properties.cpp
new file mode 100644 (file)
index 0000000..28fc5cb
--- /dev/null
@@ -0,0 +1,206 @@
+/*
+ * Copyright (c) 2017-2019, 2021 Arm Limited.
+ *
+ * SPDX-License-Identifier: MIT
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+extern "C" {
+#include <wayland-client.h>
+#include <linux-dmabuf-unstable-v1-client-protocol.h>
+}
+
+#include <cassert>
+#include <cstdlib>
+#include <algorithm>
+#include <array>
+#include <string.h>
+#include "surface_properties.hpp"
+#include "layer/private_data.hpp"
+
+#define NELEMS(x) (sizeof(x) / sizeof(x[0]))
+
+namespace wsi
+{
+namespace tizen
+{
+
+surface_properties &surface_properties::get_instance()
+{
+   static surface_properties instance;
+   return instance;
+}
+
+VkResult surface_properties::get_surface_capabilities(VkPhysicalDevice physical_device, VkSurfaceKHR surface,
+                                                      VkSurfaceCapabilitiesKHR *pSurfaceCapabilities)
+{
+   /* Image count limits */
+   pSurfaceCapabilities->minImageCount = 2;
+   /* There is no maximum theoretically speaking */
+   pSurfaceCapabilities->maxImageCount = UINT32_MAX;
+
+   /* Surface extents */
+   pSurfaceCapabilities->currentExtent = { 0xffffffff, 0xffffffff };
+   pSurfaceCapabilities->minImageExtent = { 1, 1 };
+
+   /* TODO: Ask the device for max - for now setting the max from the GPU, may be ask the display somehow*/
+   VkPhysicalDeviceProperties dev_props;
+   layer::instance_private_data::get(physical_device).disp.GetPhysicalDeviceProperties(physical_device, &dev_props);
+
+   pSurfaceCapabilities->maxImageExtent = { dev_props.limits.maxImageDimension2D,
+                                            dev_props.limits.maxImageDimension2D };
+   pSurfaceCapabilities->maxImageArrayLayers = 1;
+
+   /* Surface transforms */
+   pSurfaceCapabilities->supportedTransforms = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR;
+   pSurfaceCapabilities->currentTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR;
+
+   /* TODO: Composite alpha */
+   pSurfaceCapabilities->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 */
+   pSurfaceCapabilities->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 *surfaceFormatCount, VkSurfaceFormatKHR *surfaceFormats)
+{
+
+   VkResult res = VK_SUCCESS;
+   /* TODO: Hardcoding a list of sensible formats, may be query it from compositor later. */
+   static std::array<const VkFormat, 2> formats = { VK_FORMAT_R8G8B8A8_UNORM, VK_FORMAT_R8G8B8A8_SRGB };
+
+   assert(surfaceFormatCount != nullptr);
+   res = VK_SUCCESS;
+   if (nullptr == surfaceFormats)
+   {
+      *surfaceFormatCount = formats.size();
+   }
+   else
+   {
+      if (formats.size() > *surfaceFormatCount)
+      {
+         res = VK_INCOMPLETE;
+      }
+
+      *surfaceFormatCount = std::min(*surfaceFormatCount, static_cast<uint32_t>(formats.size()));
+      for (uint32_t i = 0; i < *surfaceFormatCount; ++i)
+      {
+         surfaceFormats[i].format = formats[i];
+         surfaceFormats[i].colorSpace = VK_COLORSPACE_SRGB_NONLINEAR_KHR;
+      }
+   }
+
+   return res;
+}
+
+VkResult surface_properties::get_surface_present_modes(VkPhysicalDevice physical_device, VkSurfaceKHR surface,
+                                                       uint32_t *pPresentModeCount, VkPresentModeKHR *pPresentModes)
+{
+
+   VkResult res = VK_SUCCESS;
+   /* TODO: Check that FIFO is okay on Wayland */
+   static std::array<const VkPresentModeKHR, 2> modes = {
+      VK_PRESENT_MODE_FIFO_KHR,
+      VK_PRESENT_MODE_MAILBOX_KHR,
+   };
+
+   assert(pPresentModeCount != nullptr);
+
+   if (nullptr == pPresentModes)
+   {
+      *pPresentModeCount = modes.size();
+   }
+   else
+   {
+      if (modes.size() > *pPresentModeCount)
+      {
+         res = VK_INCOMPLETE;
+      }
+      *pPresentModeCount = std::min(*pPresentModeCount, static_cast<uint32_t>(modes.size()));
+      for (uint32_t i = 0; i < *pPresentModeCount; ++i)
+      {
+         pPresentModes[i] = modes[i];
+      }
+   }
+
+   return res;
+}
+
+static const char *required_device_extensions[] = {
+   VK_EXT_IMAGE_DRM_FORMAT_MODIFIER_EXTENSION_NAME,
+   VK_KHR_BIND_MEMORY_2_EXTENSION_NAME,
+   VK_KHR_IMAGE_FORMAT_LIST_EXTENSION_NAME,
+   VK_KHR_SAMPLER_YCBCR_CONVERSION_EXTENSION_NAME,
+   VK_KHR_MAINTENANCE1_EXTENSION_NAME,
+   VK_KHR_GET_MEMORY_REQUIREMENTS_2_EXTENSION_NAME,
+   VK_EXT_EXTERNAL_MEMORY_DMA_BUF_EXTENSION_NAME,
+   VK_KHR_EXTERNAL_MEMORY_FD_EXTENSION_NAME,
+   VK_KHR_EXTERNAL_MEMORY_EXTENSION_NAME,
+};
+
+static std::unique_ptr<util::extension_list> populate_device_extensions()
+{
+   std::unique_ptr<util::extension_list> ret(new util::extension_list(util::allocator::get_generic()));
+   ret->add(required_device_extensions, NELEMS(required_device_extensions));
+
+   return ret;
+}
+
+const util::extension_list &surface_properties::get_required_device_extensions()
+{
+   static std::unique_ptr<util::extension_list> device_extensions = populate_device_extensions();
+   return *device_extensions;
+}
+
+bool surface_properties::physical_device_supported(VkPhysicalDevice dev)
+{
+   static util::extension_list device_extensions{util::allocator::get_generic()};
+   device_extensions.add(dev);
+
+   static util::extension_list required_extensions{util::allocator::get_generic()};
+   required_extensions.add(required_device_extensions, NELEMS(required_device_extensions));
+
+   return device_extensions.contains(required_extensions);
+}
+
+/* TODO: Check for zwp_linux_dmabuf_v1 protocol in display */
+VkBool32 GetPhysicalDeviceWaylandPresentationSupportKHR(VkPhysicalDevice physical_device, uint32_t queue_index,
+                                                        struct wl_display *display)
+{
+   return VK_TRUE;
+}
+
+PFN_vkVoidFunction surface_properties::get_proc_addr(const char *name)
+{
+   if (strcmp(name, "vkGetPhysicalDeviceWaylandPresentationSupportKHR") == 0)
+   {
+      return reinterpret_cast<PFN_vkVoidFunction>(GetPhysicalDeviceWaylandPresentationSupportKHR);
+   }
+   return nullptr;
+}
+} // namespace tizen
+} // namespace wsi
diff --git a/wsi/tizen/surface_properties.hpp b/wsi/tizen/surface_properties.hpp
new file mode 100644 (file)
index 0000000..444c6a3
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2017-2019, 2021 Arm Limited.
+ *
+ * SPDX-License-Identifier: MIT
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#pragma once
+
+#include "wsi/surface_properties.hpp"
+
+namespace wsi
+{
+namespace tizen
+{
+
+class surface_properties : public wsi::surface_properties
+{
+public:
+   static surface_properties &get_instance();
+
+   VkResult get_surface_capabilities(VkPhysicalDevice physical_device, VkSurfaceKHR surface,
+                                     VkSurfaceCapabilitiesKHR *pSurfaceCapabilities) override;
+   VkResult get_surface_formats(VkPhysicalDevice physical_device, VkSurfaceKHR surface, uint32_t *surfaceFormatCount,
+                                VkSurfaceFormatKHR *surfaceFormats) override;
+   VkResult get_surface_present_modes(VkPhysicalDevice physical_device, VkSurfaceKHR surface,
+                                      uint32_t *pPresentModeCount, VkPresentModeKHR *pPresentModes) override;
+
+   const util::extension_list &get_required_device_extensions() override;
+
+   bool physical_device_supported(VkPhysicalDevice dev);
+
+   PFN_vkVoidFunction get_proc_addr(const char *name) override;
+};
+
+} // namespace tizen
+} // namespace wsi
diff --git a/wsi/tizen/swapchain.cpp b/wsi/tizen/swapchain.cpp
new file mode 100644 (file)
index 0000000..edc6453
--- /dev/null
@@ -0,0 +1,694 @@
+/*
+ * Copyright (c) 2017-2019, 2021 Arm Limited.
+ *
+ * SPDX-License-Identifier: MIT
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#define VK_USE_PLATFORM_WAYLAND_KHR 1
+
+#include "swapchain.hpp"
+#include "swapchain_wl_helpers.hpp"
+
+#include <cstring>
+#include <cassert>
+#include <unistd.h>
+#include <cstdlib>
+#include <cerrno>
+#include <cstdio>
+#include <climits>
+#include <drm_fourcc.h>
+
+#include "util/drm/drm_utils.hpp"
+
+#if VULKAN_WSI_DEBUG > 0
+#define WSI_PRINT_ERROR(...) fprintf(stderr, __FILE__, __LINE__, __func__, ##__VA_ARGS__)
+#else
+#define WSI_PRINT_ERROR(...) (void)0
+#endif
+
+namespace wsi
+{
+namespace tizen
+{
+
+struct swapchain::wayland_image_data
+{
+   int buffer_fd;
+   int stride;
+   uint32_t offset;
+
+   wl_buffer *buffer;
+   VkDeviceMemory memory;
+};
+
+swapchain::swapchain(layer::device_private_data &dev_data, const VkAllocationCallbacks *pAllocator)
+   : swapchain_base(dev_data, pAllocator)
+   , m_display(nullptr)
+   , m_surface(nullptr)
+   , m_dmabuf_interface(nullptr)
+   , m_surface_queue(nullptr)
+   , m_buffer_queue(nullptr)
+   , m_present_pending(false)
+{
+}
+
+swapchain::~swapchain()
+{
+   //int res;
+   teardown();
+   if (m_dmabuf_interface != nullptr)
+   {
+      zwp_linux_dmabuf_v1_destroy(m_dmabuf_interface);
+   }
+   /*
+   res = wsialloc_delete(&m_wsi_allocator);
+   if (res != 0)
+   {
+      WSI_PRINT_ERROR("error deleting the allocator: %d\n", res);
+   }*/
+   if (m_surface_queue != nullptr)
+   {
+      wl_event_queue_destroy(m_surface_queue);
+   }
+   if (m_buffer_queue != nullptr)
+   {
+      wl_event_queue_destroy(m_buffer_queue);
+   }
+}
+
+static void roundtrip_cb_done(void *data, wl_callback *cb, uint32_t cb_data)
+{
+   (void)cb_data;
+
+   bool *cb_recvd = reinterpret_cast<bool *>(data);
+   assert(cb_recvd);
+
+   *cb_recvd = true;
+}
+
+int swapchain::roundtrip()
+{
+   int res;
+   const wl_callback_listener listener = { roundtrip_cb_done };
+   bool cb_recvd = false;
+
+   wl_callback *cb = wl_display_sync(m_display);
+   if (!cb)
+   {
+      WSI_PRINT_ERROR("failed to create wl_display::sync callback\n");
+      res = -1;
+      goto exit;
+   }
+
+   wl_proxy_set_queue((wl_proxy *)cb, m_surface_queue);
+
+   res = wl_callback_add_listener(cb, &listener, &cb_recvd);
+   if (res == -1)
+   {
+      WSI_PRINT_ERROR("error setting wl_display::sync callback listener\n");
+      goto exit;
+   }
+
+   res = wl_display_flush(m_display);
+   if (res == -1)
+   {
+      WSI_PRINT_ERROR("error performing a flush on the display\n");
+      goto exit;
+   }
+   do
+   {
+      res = dispatch_queue(m_display, m_surface_queue, 1000);
+   } while (res > 0 && !cb_recvd);
+
+   if (res < 0)
+   {
+      WSI_PRINT_ERROR("error dispatching on the surface queue\n");
+      goto exit;
+   }
+   else if (res == 0)
+   {
+      WSI_PRINT_ERROR("timeout waiting for roundtrip callback\n");
+      goto exit;
+   }
+
+exit:
+   if (cb)
+   {
+      wl_callback_destroy(cb);
+   }
+   return res;
+}
+
+struct display_queue
+{
+   wl_display *display;
+   wl_event_queue *queue;
+};
+
+VkResult swapchain::init_platform(VkDevice device, const VkSwapchainCreateInfoKHR *pSwapchainCreateInfo)
+{
+   VkIcdSurfaceWayland *vk_surf = reinterpret_cast<VkIcdSurfaceWayland *>(pSwapchainCreateInfo->surface);
+
+   m_display = vk_surf->display;
+   m_surface = vk_surf->surface;
+#if 0
+   m_surface_queue = wl_display_create_queue(m_display);
+   if (m_surface_queue == nullptr)
+   {
+      WSI_PRINT_ERROR("Failed to create wl surface display_queue.\n");
+      return VK_ERROR_INITIALIZATION_FAILED;
+   }
+
+   m_buffer_queue = wl_display_create_queue(m_display);
+   if (m_buffer_queue == nullptr)
+   {
+      WSI_PRINT_ERROR("Failed to create wl buffer display_queue.\n");
+      return VK_ERROR_INITIALIZATION_FAILED;
+   }
+
+   wl_registry *registry = wl_display_get_registry(m_display);
+   if (registry == nullptr)
+   {
+      WSI_PRINT_ERROR("Failed to get wl display registry.\n");
+      return VK_ERROR_INITIALIZATION_FAILED;
+   }
+
+   wl_proxy_set_queue((struct wl_proxy *)registry, m_surface_queue);
+
+   const wl_registry_listener registry_listener = { registry_handler };
+   int res = wl_registry_add_listener(registry, &registry_listener, &m_dmabuf_interface);
+   if (res < 0)
+   {
+      WSI_PRINT_ERROR("Failed to add registry listener.\n");
+      return VK_ERROR_INITIALIZATION_FAILED;
+   }
+
+   res = roundtrip();
+   if (res < 0)
+   {
+      WSI_PRINT_ERROR("Roundtrip failed.\n");
+      return VK_ERROR_INITIALIZATION_FAILED;
+   }
+
+   /* we should have the dma_buf interface by now */
+   assert(m_dmabuf_interface);
+
+   wl_registry_destroy(registry);
+
+   res = wsialloc_new(-1, &m_wsi_allocator);
+   if (res != 0)
+   {
+      WSI_PRINT_ERROR("Failed to create wsi allocator.\n");
+      return VK_ERROR_INITIALIZATION_FAILED;
+   }
+#endif
+   return VK_SUCCESS;
+}
+
+static void create_succeeded(void *data, struct zwp_linux_buffer_params_v1 *params, struct wl_buffer *buffer)
+{
+   struct wl_buffer **wayland_buffer = (struct wl_buffer **)data;
+   *wayland_buffer = buffer;
+}
+
+static const struct zwp_linux_buffer_params_v1_listener params_listener = { create_succeeded, NULL };
+
+static void buffer_release(void *data, struct wl_buffer *wayl_buffer)
+{
+   swapchain *sc = (swapchain *)data;
+   sc->release_buffer(wayl_buffer);
+}
+
+void swapchain::release_buffer(struct wl_buffer *wayl_buffer)
+{
+   uint32_t i;
+   for (i = 0; i < m_swapchain_images.size(); i++)
+   {
+      wayland_image_data *data;
+      data = (wayland_image_data *)m_swapchain_images[i].data;
+      if (data->buffer == wayl_buffer)
+      {
+         unpresent_image(i);
+         break;
+      }
+   }
+
+   /* check we found a buffer to unpresent */
+   assert(i < m_swapchain_images.size());
+}
+
+static struct wl_buffer_listener buffer_listener = { buffer_release };
+
+VkResult swapchain::allocate_image(const VkImageCreateInfo &image_create_info, wayland_image_data *image_data,
+                                   VkImage *image)
+{
+   VkResult result = VK_SUCCESS;
+
+   image_data->buffer = nullptr;
+   image_data->buffer_fd = -1;
+   image_data->memory = VK_NULL_HANDLE;
+
+   VkExternalImageFormatPropertiesKHR external_props = {};
+   external_props.sType = VK_STRUCTURE_TYPE_EXTERNAL_IMAGE_FORMAT_PROPERTIES_KHR;
+
+   VkImageFormatProperties2KHR format_props = {};
+   format_props.sType = VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR;
+   format_props.pNext = &external_props;
+   {
+      VkPhysicalDeviceExternalImageFormatInfoKHR external_info = {};
+      external_info.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_IMAGE_FORMAT_INFO_KHR;
+      external_info.handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_DMA_BUF_BIT_EXT;
+
+      VkPhysicalDeviceImageDrmFormatModifierInfoEXT drm_mod_info = {};
+      drm_mod_info.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_DRM_FORMAT_MODIFIER_INFO_EXT;
+      drm_mod_info.pNext = &external_info;
+      drm_mod_info.drmFormatModifier = DRM_FORMAT_MOD_LINEAR;
+      drm_mod_info.sharingMode = image_create_info.sharingMode;
+      drm_mod_info.queueFamilyIndexCount = image_create_info.queueFamilyIndexCount;
+      drm_mod_info.pQueueFamilyIndices = image_create_info.pQueueFamilyIndices;
+
+      VkPhysicalDeviceImageFormatInfo2KHR info = {};
+      info.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_FORMAT_INFO_2_KHR;
+      info.pNext = &drm_mod_info;
+      info.format = image_create_info.format;
+      info.type = image_create_info.imageType;
+      info.tiling = VK_IMAGE_TILING_DRM_FORMAT_MODIFIER_EXT;
+      info.usage = image_create_info.usage;
+      info.flags = image_create_info.flags;
+
+      result = m_device_data.instance_data.disp.GetPhysicalDeviceImageFormatProperties2KHR(m_device_data.physical_device,
+                                                                                           &info, &format_props);
+   }
+   if (result != VK_SUCCESS)
+   {
+      WSI_PRINT_ERROR("Failed to get physical device format support.\n");
+      return result;
+   }
+   if (format_props.imageFormatProperties.maxExtent.width < image_create_info.extent.width ||
+       format_props.imageFormatProperties.maxExtent.height < image_create_info.extent.height ||
+       format_props.imageFormatProperties.maxExtent.depth < image_create_info.extent.depth)
+   {
+      WSI_PRINT_ERROR("Physical device does not support required extent.\n");
+      return VK_ERROR_INITIALIZATION_FAILED;
+   }
+   if (format_props.imageFormatProperties.maxMipLevels < image_create_info.mipLevels ||
+       format_props.imageFormatProperties.maxArrayLayers < image_create_info.arrayLayers)
+   {
+      WSI_PRINT_ERROR("Physical device does not support required array layers or mip levels.\n");
+      return VK_ERROR_INITIALIZATION_FAILED;
+   }
+   if ((format_props.imageFormatProperties.sampleCounts & image_create_info.samples) != image_create_info.samples)
+   {
+      WSI_PRINT_ERROR("Physical device does not support required sample count.\n");
+      return VK_ERROR_INITIALIZATION_FAILED;
+   }
+
+   if (external_props.externalMemoryProperties.externalMemoryFeatures & VK_EXTERNAL_MEMORY_FEATURE_EXPORTABLE_BIT_KHR)
+   {
+      /* TODO: Handle exportable images which use ICD allocated memory in preference to an external allocator. */
+   }
+   if (!(external_props.externalMemoryProperties.externalMemoryFeatures &
+         VK_EXTERNAL_MEMORY_FEATURE_IMPORTABLE_BIT_KHR))
+   {
+      WSI_PRINT_ERROR("Export/Import not supported.\n");
+      return VK_ERROR_INITIALIZATION_FAILED;
+   }
+   else
+   {
+      /* TODO: Handle Dedicated allocation bit. */
+      uint32_t fourcc = util::drm::vk_to_drm_format(image_create_info.format);
+/*
+      int res =
+         wsialloc_alloc(&m_wsi_allocator, fourcc, image_create_info.extent.width, image_create_info.extent.height,
+                        &image_data->stride, &image_data->buffer_fd, &image_data->offset, nullptr);
+      if (res != 0)
+      {
+         WSI_PRINT_ERROR("Failed allocation of DMA Buffer.\n");
+         return VK_ERROR_OUT_OF_HOST_MEMORY;
+      }
+*/
+      {
+         assert(image_data->stride >= 0);
+         VkSubresourceLayout image_layout = {};
+         image_layout.offset = image_data->offset;
+         image_layout.rowPitch = static_cast<uint32_t>(image_data->stride);
+         VkImageDrmFormatModifierExplicitCreateInfoEXT drm_mod_info = {};
+         drm_mod_info.sType = VK_STRUCTURE_TYPE_IMAGE_DRM_FORMAT_MODIFIER_EXPLICIT_CREATE_INFO_EXT;
+         drm_mod_info.pNext = image_create_info.pNext;
+         drm_mod_info.drmFormatModifier = DRM_FORMAT_MOD_LINEAR;
+         drm_mod_info.drmFormatModifierPlaneCount = 1;
+         drm_mod_info.pPlaneLayouts = &image_layout;
+
+         VkExternalMemoryImageCreateInfoKHR external_info = {};
+         external_info.sType = VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO_KHR;
+         external_info.pNext = &drm_mod_info;
+         external_info.handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_DMA_BUF_BIT_EXT;
+
+         VkImageCreateInfo image_info = image_create_info;
+         image_info.pNext = &external_info;
+         image_info.tiling = VK_IMAGE_TILING_DRM_FORMAT_MODIFIER_EXT;
+         result = m_device_data.disp.CreateImage(m_device, &image_info, get_allocation_callbacks(), image);
+      }
+      if (result != VK_SUCCESS)
+      {
+         WSI_PRINT_ERROR("Image creation failed.\n");
+         return result;
+      }
+      {
+         VkMemoryFdPropertiesKHR mem_props = {};
+         mem_props.sType = VK_STRUCTURE_TYPE_MEMORY_FD_PROPERTIES_KHR;
+
+         result = m_device_data.disp.GetMemoryFdPropertiesKHR(m_device, VK_EXTERNAL_MEMORY_HANDLE_TYPE_DMA_BUF_BIT_EXT,
+                                                              image_data->buffer_fd, &mem_props);
+         if (result != VK_SUCCESS)
+         {
+            WSI_PRINT_ERROR("Error querying Fd properties.\n");
+            return result;
+         }
+
+         uint32_t mem_idx;
+         for (mem_idx = 0; mem_idx < VK_MAX_MEMORY_TYPES; mem_idx++)
+         {
+            if (mem_props.memoryTypeBits & (1 << mem_idx))
+            {
+               break;
+            }
+         }
+         off_t dma_buf_size = lseek(image_data->buffer_fd, 0, SEEK_END);
+         if (dma_buf_size < 0)
+         {
+            WSI_PRINT_ERROR("Failed to get DMA Buf size.\n");
+            return VK_ERROR_OUT_OF_HOST_MEMORY;
+         }
+
+         VkImportMemoryFdInfoKHR import_mem_info = {};
+         import_mem_info.sType = VK_STRUCTURE_TYPE_IMPORT_MEMORY_FD_INFO_KHR;
+         import_mem_info.handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_DMA_BUF_BIT_EXT;
+         import_mem_info.fd = image_data->buffer_fd;
+
+         VkMemoryAllocateInfo alloc_info = {};
+         alloc_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
+         alloc_info.pNext = &import_mem_info;
+         alloc_info.allocationSize = static_cast<uint64_t>(dma_buf_size);
+         alloc_info.memoryTypeIndex = mem_idx;
+
+         result = m_device_data.disp.AllocateMemory(m_device, &alloc_info, get_allocation_callbacks(), &image_data->memory);
+      }
+      if (result != VK_SUCCESS)
+      {
+         WSI_PRINT_ERROR("Failed to import memory.\n");
+         return result;
+      }
+      result = m_device_data.disp.BindImageMemory(m_device, *image, image_data->memory, 0);
+   }
+
+   return result;
+}
+
+VkResult swapchain::create_image(const VkImageCreateInfo &image_create_info, swapchain_image &image)
+{
+   uint32_t fourcc = util::drm::vk_to_drm_format(image_create_info.format);
+
+   int res;
+   VkResult result = VK_SUCCESS;
+
+   wayland_image_data *image_data = nullptr;
+   VkFenceCreateInfo fenceInfo = { VK_STRUCTURE_TYPE_FENCE_CREATE_INFO, nullptr, 0 };
+
+   /* Create image_data */
+   if (get_allocation_callbacks() != nullptr)
+   {
+      image_data = static_cast<wayland_image_data *>(
+         get_allocation_callbacks()->pfnAllocation(get_allocation_callbacks()->pUserData, sizeof(wayland_image_data),
+                                                   alignof(wayland_image_data), VK_SYSTEM_ALLOCATION_SCOPE_OBJECT));
+   }
+   else
+   {
+      image_data = static_cast<wayland_image_data *>(malloc(sizeof(wayland_image_data)));
+   }
+   if (image_data == nullptr)
+   {
+      result = VK_ERROR_OUT_OF_HOST_MEMORY;
+      goto out;
+   }
+
+   image.data = reinterpret_cast<void *>(image_data);
+   image.status = swapchain_image::FREE;
+   result = allocate_image(image_create_info, image_data, &image.image);
+   if (result != VK_SUCCESS)
+   {
+      WSI_PRINT_ERROR("Failed to allocate image.\n");
+      goto out;
+   }
+
+   /* create a wl_buffer using the dma_buf protocol */
+   struct zwp_linux_buffer_params_v1 *params;
+   params = zwp_linux_dmabuf_v1_create_params(m_dmabuf_interface);
+   zwp_linux_buffer_params_v1_add(params, image_data->buffer_fd, 0, image_data->offset, image_data->stride, 0, 0);
+   wl_proxy_set_queue((struct wl_proxy *)params, m_surface_queue);
+   res = zwp_linux_buffer_params_v1_add_listener(params, &params_listener, &image_data->buffer);
+   if (res < 0)
+   {
+      result = VK_ERROR_INITIALIZATION_FAILED;
+      goto out;
+   }
+   zwp_linux_buffer_params_v1_create(params, image_create_info.extent.width, image_create_info.extent.height, fourcc,
+                                     0);
+
+   /* TODO: don't roundtrip - we should be able to send the create request now,
+    * and only wait for it on first present. only do this once, not for all buffers created */
+   res = roundtrip();
+   if (res < 0)
+   {
+      result = VK_ERROR_INITIALIZATION_FAILED;
+      goto out;
+   }
+
+   /* should now have a wl_buffer */
+   assert(image_data->buffer);
+   zwp_linux_buffer_params_v1_destroy(params);
+   wl_proxy_set_queue((struct wl_proxy *)image_data->buffer, m_buffer_queue);
+   res = wl_buffer_add_listener(image_data->buffer, &buffer_listener, this);
+   if (res < 0)
+   {
+      result = VK_ERROR_INITIALIZATION_FAILED;
+      goto out;
+   }
+
+   /* Initialize presentation fence. */
+   result = m_device_data.disp.CreateFence(m_device, &fenceInfo, get_allocation_callbacks(), &image.present_fence);
+
+out:
+   if (result != VK_SUCCESS)
+   {
+      destroy_image(image);
+      return result;
+   }
+   return result;
+}
+
+static void frame_done(void *data, wl_callback *cb, uint32_t cb_data)
+{
+   (void)cb_data;
+
+   bool *present_pending = reinterpret_cast<bool *>(data);
+   assert(present_pending);
+
+   *present_pending = false;
+
+   wl_callback_destroy(cb);
+}
+
+void swapchain::present_image(uint32_t pendingIndex)
+{
+   int res;
+   wayland_image_data *image_data = reinterpret_cast<wayland_image_data *>(m_swapchain_images[pendingIndex].data);
+   /* if a frame is already pending, wait for a hint to present again */
+   if (m_present_pending)
+   {
+      assert(m_present_mode == VK_PRESENT_MODE_FIFO_KHR);
+      do
+      {
+         /* block waiting for the compositor to return the wl_surface::frame
+          * callback. We may want to change this to timeout after a period of
+          * time if the compositor isn't responding (perhaps because the
+          * window is hidden).
+          */
+         res = dispatch_queue(m_display, m_surface_queue, -1);
+      } while (res > 0 && m_present_pending);
+
+      if (res <= 0)
+      {
+         WSI_PRINT_ERROR("error waiting for Wayland compositor frame hint\n");
+         m_is_valid = false;
+         /* try to present anyway */
+      }
+   }
+
+   wl_surface_attach(m_surface, image_data->buffer, 0, 0);
+   /* TODO: work out damage */
+   wl_surface_damage(m_surface, 0, 0, INT32_MAX, INT32_MAX);
+
+   if (m_present_mode == VK_PRESENT_MODE_FIFO_KHR)
+   {
+      /* request a hint when we can present the _next_ frame */
+      wl_callback *cb = wl_surface_frame(m_surface);
+      if (cb)
+      {
+         wl_proxy_set_queue((wl_proxy *)cb, m_surface_queue);
+         static const wl_callback_listener frame_listener = { frame_done };
+         m_present_pending = true;
+         wl_callback_add_listener(cb, &frame_listener, &m_present_pending);
+      }
+   }
+   else
+   {
+      assert(m_present_mode == VK_PRESENT_MODE_MAILBOX_KHR);
+      /* weston only _queues_ wl_buffer::release events. This means when the
+       * compositor flushes the client it only sends the events if some other events
+       * have been posted.
+       *
+       * As such we have to request a sync callback - we discard it straight away
+       * as we don't actually need the callback, but it means the
+       * wl_buffer::release event is actually sent.
+       */
+      wl_callback *cb = wl_display_sync(m_display);
+      assert(cb);
+      if (cb)
+      {
+         wl_callback_destroy(cb);
+      }
+   }
+
+   wl_surface_commit(m_surface);
+   res = wl_display_flush(m_display);
+   if (res < 0)
+   {
+      WSI_PRINT_ERROR("error flushing the display\n");
+      /* Setting the swapchain as invalid */
+      m_is_valid = false;
+   }
+}
+
+void swapchain::destroy_image(swapchain_image &image)
+{
+   if (image.status != swapchain_image::INVALID)
+   {
+      if (image.present_fence != VK_NULL_HANDLE)
+      {
+         m_device_data.disp.DestroyFence(m_device, image.present_fence, get_allocation_callbacks());
+         image.present_fence = VK_NULL_HANDLE;
+      }
+
+      if (image.image != VK_NULL_HANDLE)
+      {
+         m_device_data.disp.DestroyImage(m_device, image.image, get_allocation_callbacks());
+         image.image = VK_NULL_HANDLE;
+      }
+   }
+   if (image.data != nullptr)
+   {
+      auto image_data = reinterpret_cast<wayland_image_data *>(image.data);
+      if (image_data->buffer != nullptr)
+      {
+         wl_buffer_destroy(image_data->buffer);
+      }
+      if (image_data->memory != VK_NULL_HANDLE)
+      {
+         m_device_data.disp.FreeMemory(m_device, image_data->memory, get_allocation_callbacks());
+      }
+      else if (image_data->buffer_fd >= 0)
+      {
+         close(image_data->buffer_fd);
+      }
+
+      if (get_allocation_callbacks() != nullptr)
+      {
+         get_allocation_callbacks()->pfnFree(get_allocation_callbacks()->pUserData, image_data);
+      }
+      else
+      {
+         free(image_data);
+      }
+      image.data = nullptr;
+   }
+
+   image.status = swapchain_image::INVALID;
+}
+
+bool swapchain::free_image_found()
+{
+   for (auto &img : m_swapchain_images)
+   {
+      if (img.status == swapchain_image::FREE)
+      {
+         return true;
+      }
+   }
+   return false;
+}
+
+VkResult swapchain::get_free_buffer(uint64_t *timeout)
+{
+   int ms_timeout, res;
+
+   if (*timeout >= INT_MAX * 1000llu * 1000llu)
+   {
+      ms_timeout = INT_MAX;
+   }
+   else
+   {
+      ms_timeout = *timeout / 1000llu / 1000llu;
+   }
+
+   /* The current dispatch_queue implementation will return if any
+    * events are returned, even if no events are dispatched to the buffer
+    * queue. Therefore dispatch repeatedly until a buffer has been freed.
+    */
+   do
+   {
+      res = dispatch_queue(m_display, m_buffer_queue, ms_timeout);
+   } while (!free_image_found() && res > 0);
+
+   if (res > 0)
+   {
+      *timeout = 0;
+      return VK_SUCCESS;
+   }
+   else if (res == 0)
+   {
+      if (*timeout == 0)
+      {
+         return VK_NOT_READY;
+      }
+      else
+      {
+         return VK_TIMEOUT;
+      }
+   }
+   else
+   {
+      return VK_ERROR_DEVICE_LOST;
+   }
+}
+
+} // namespace wayland
+} // namespace wsi
diff --git a/wsi/tizen/swapchain.hpp b/wsi/tizen/swapchain.hpp
new file mode 100644 (file)
index 0000000..7151b87
--- /dev/null
@@ -0,0 +1,136 @@
+/*
+ * Copyright (c) 2017-2019, 2021 Arm Limited.
+ *
+ * SPDX-License-Identifier: MIT
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#pragma once
+
+#include "wsi/swapchain_base.hpp"
+
+extern "C" {
+#include <vulkan/vk_icd.h>
+//#include <util/wsialloc/wsialloc.h>
+#include <wayland-client.h>
+#include <linux-dmabuf-unstable-v1-client-protocol.h>
+}
+
+namespace wsi
+{
+namespace tizen
+{
+
+class swapchain : public wsi::swapchain_base
+{
+public:
+   explicit swapchain(layer::device_private_data &dev_data, const VkAllocationCallbacks *allocator);
+
+   ~swapchain();
+
+   /* TODO: make the buffer destructor a friend? so this can be protected */
+   void release_buffer(struct wl_buffer *wl_buffer);
+
+protected:
+   /**
+    * @brief Initialize platform specifics.
+    */
+   VkResult init_platform(VkDevice device, const VkSwapchainCreateInfoKHR *pSwapchainCreateInfo) override;
+
+   /**
+    * @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 occurred.
+    */
+   VkResult create_image(const VkImageCreateInfo &image_create_info, swapchain_image &image) override;
+
+   /**
+    * @brief Method to present an image
+    *
+    * @param pendingIndex Index of the pending image to be presented.
+    */
+   void present_image(uint32_t pendingIndex) override;
+
+   /**
+    * @brief Method to release a swapchain image
+    *
+    * @param image Handle to the image about to be released.
+    */
+   void destroy_image(swapchain_image &image) override;
+
+   /**
+    * @brief Method to perform a rountrip to the Wayland compositor
+    *
+    * @return -1 on error. Otherwise non-negative.
+    */
+   int roundtrip();
+
+   /**
+    * @brief Method to check if there are any free images
+    *
+    * @return true if any images are free, otherwise false.
+    */
+   bool free_image_found();
+
+   /**
+    * @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 indefinitely. The timeout should
+    *                        be updated if a sleep is required - this can
+    *                        be set to 0 if the semaphore is now not expected
+    *                        block.
+    */
+   VkResult get_free_buffer(uint64_t *timeout) override;
+
+private:
+   struct wayland_image_data;
+   VkResult allocate_image(const VkImageCreateInfo &image_create_info, wayland_image_data *image_data, VkImage *image);
+
+   struct wl_display *m_display;
+   struct wl_surface *m_surface;
+   struct zwp_linux_dmabuf_v1 *m_dmabuf_interface;
+
+   /* The queue on which we dispatch the swapchain related events, mostly frame completion */
+   struct wl_event_queue *m_surface_queue;
+   /* The queue on which we dispatch buffer related events, mostly buffer_release */
+   struct wl_event_queue *m_buffer_queue;
+
+   /**
+    * @brief Handle to the WSI allocator.
+    */
+   //wsialloc_allocator m_wsi_allocator;
+
+   /**
+    * @brief true when waiting for the server hint to present a buffer
+    *
+    * true if a buffer has been presented and we've not had a wl_surface::frame
+    * callback to indicate the server is ready for the next buffer.
+    */
+   bool m_present_pending;
+};
+} // namespace wayland
+} // namespace wsi
diff --git a/wsi/tizen/swapchain_wl_helpers.cpp b/wsi/tizen/swapchain_wl_helpers.cpp
new file mode 100644 (file)
index 0000000..197ca04
--- /dev/null
@@ -0,0 +1,130 @@
+/*
+ * Copyright (c) 2017-2019, 2021 Arm Limited.
+ *
+ * SPDX-License-Identifier: MIT
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "swapchain_wl_helpers.hpp"
+
+#include <wayland-client.h>
+#include <linux-dmabuf-unstable-v1-client-protocol.h>
+#include <string.h>
+#include <assert.h>
+#include <poll.h>
+#include <errno.h>
+
+extern "C" {
+
+   void registry_handler(void *data, struct wl_registry *wl_registry, uint32_t name, const char *interface,
+                         uint32_t version)
+   {
+      zwp_linux_dmabuf_v1 **dmabuf_interface = (zwp_linux_dmabuf_v1 **)data;
+
+      if (!strcmp(interface, "zwp_linux_dmabuf_v1"))
+      {
+         *dmabuf_interface =
+            (zwp_linux_dmabuf_v1 *)wl_registry_bind(wl_registry, name, &zwp_linux_dmabuf_v1_interface, version);
+         assert(*dmabuf_interface);
+      }
+   }
+
+   int dispatch_queue(struct wl_display *display, struct wl_event_queue *queue, int timeout)
+   {
+      int err;
+      struct pollfd pfd = {};
+      int retval;
+
+      /* Before we sleep, dispatch any pending events. prepare_read_queue will return 0 whilst there are pending
+       * events to dispatch on the queue. */
+      while (0 != wl_display_prepare_read_queue(display, queue))
+      {
+         /* dispatch_queue_pending returns -1 on error, or the number of events dispatched otherwise. If we
+          * already dispatched some events, then we might not need to sleep, as we might have just dispatched
+          * the event we want, so return immediately. */
+         err = wl_display_dispatch_queue_pending(display, queue);
+         if (err)
+         {
+            return (0 > err) ? -1 : 1;
+         }
+      }
+
+      /* wl_display_read_events performs a non-blocking read. */
+      pfd.fd = wl_display_get_fd(display);
+      pfd.events = POLLIN;
+      while (true)
+      {
+         /* Timeout is given in milliseconds. A return value of 0, or -1 with errno set to EINTR means that we
+          * should retry as the timeout was exceeded or we were interrupted by a signal, respectively. A
+          * return value of 1 means that something happened, and we should inspect the pollfd structure to see
+          * just what that was.
+          */
+         err = poll(&pfd, 1, timeout);
+         if (0 == err)
+         {
+            /* Timeout. */
+            wl_display_cancel_read(display);
+            return 0;
+         }
+         else if (-1 == err)
+         {
+            if (EINTR == errno)
+            {
+               /* Interrupted by a signal; restart. This resets the timeout. */
+               continue;
+            }
+            else
+            {
+               /* Something else bad happened; abort. */
+               wl_display_cancel_read(display);
+               return -1;
+            }
+         }
+         else
+         {
+            if (POLLIN == pfd.revents)
+            {
+               /* We have data to read, and no errors; proceed to read_events. */
+               break;
+            }
+            else
+            {
+               /* An error occurred, e.g. file descriptor was closed from underneath us. */
+               wl_display_cancel_read(display);
+               return -1;
+            }
+         }
+      }
+
+      /* Actually read the events from the display. A failure in read_events calls cancel_read internally for us,
+       * so we don't need to do that here. */
+      err = wl_display_read_events(display);
+      if (0 != err)
+      {
+         return -1;
+      }
+
+      /* Finally, if we read any events relevant to our queue, we can dispatch them. */
+      err = wl_display_dispatch_queue_pending(display, queue);
+      retval = err < 0 ? -1 : 1;
+
+      return retval;
+   }
+}
diff --git a/wsi/tizen/swapchain_wl_helpers.hpp b/wsi/tizen/swapchain_wl_helpers.hpp
new file mode 100644 (file)
index 0000000..5318682
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2017-2019, 2021 Arm Limited.
+ *
+ * SPDX-License-Identifier: MIT
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#pragma once
+
+#include <wayland-client.h>
+
+extern "C" {
+   void registry_handler(void *data, struct wl_registry *wl_registry, uint32_t name, const char *interface,
+                         uint32_t version);
+
+   /**
+    * @brief Dispatch events from a Wayland event queue
+    *
+    * Dispatch events from a given Wayland display event queue, including calling event handlers, and flush out any
+    * requests the event handlers may have written. Specification of a timeout allows the wait to be bounded. If any
+    * events are already pending dispatch (have been read from the display by another thread or event queue), they
+    * will be dispatched and the function will return immediately, without waiting for new events to arrive.
+    *
+    * @param  display Wayland display to dispatch events from
+    * @param  queue   Event queue to dispatch events from; other event queues will not have their handlers called from
+    *                 within this function
+    * @param  timeout Maximum time to wait for events to arrive, in milliseconds
+    * @return         1 if one or more events were dispatched on this queue, 0 if the timeout was reached without any
+    *                 events being dispatched, or -1 on error.
+    */
+   int dispatch_queue(struct wl_display *display, struct wl_event_queue *queue, int timeout);
+}
index c5351c5..5da2394 100644 (file)
 #include <vulkan/vulkan_wayland.h>
 #include "wayland/surface_properties.hpp"
 #include "wayland/swapchain.hpp"
+#elif BUILD_WSI_TIZEN
+#include <vulkan/vulkan_wayland.h>
+#include "tizen/surface_properties.hpp"
+#include "tizen/swapchain.hpp"
 #endif
 
 namespace wsi
@@ -53,7 +57,7 @@ static struct wsi_extension
    VkIcdWsiPlatform platform;
 } const supported_wsi_extensions[] = {
    { { VK_EXT_HEADLESS_SURFACE_EXTENSION_NAME, VK_EXT_HEADLESS_SURFACE_SPEC_VERSION }, VK_ICD_WSI_PLATFORM_HEADLESS },
-#if BUILD_WSI_WAYLAND
+#if BUILD_WSI_WAYLAND || BUILD_WSI_TIZEN
    { { VK_KHR_WAYLAND_SURFACE_EXTENSION_NAME, VK_KHR_WAYLAND_SURFACE_SPEC_VERSION }, VK_ICD_WSI_PLATFORM_WAYLAND },
 #endif
 };
@@ -67,6 +71,9 @@ static surface_properties *get_surface_properties(VkIcdWsiPlatform platform)
 #if BUILD_WSI_WAYLAND
    case VK_ICD_WSI_PLATFORM_WAYLAND:
       return &wayland::surface_properties::get_instance();
+#elif BUILD_WSI_TIZEN
+   case VK_ICD_WSI_PLATFORM_WAYLAND:
+      return &tizen::surface_properties::get_instance();
 #endif
    default:
       return nullptr;
@@ -104,6 +111,9 @@ swapchain_base *allocate_surface_swapchain(VkSurfaceKHR surface, layer::device_p
 #if BUILD_WSI_WAYLAND
    case VK_ICD_WSI_PLATFORM_WAYLAND:
       return allocate_swapchain<wsi::wayland::swapchain>(dev_data, pAllocator);
+#elif BUILD_WSI_TIZEN
+   case VK_ICD_WSI_PLATFORM_WAYLAND:
+      return allocate_swapchain<wsi::tizen::swapchain>(dev_data, pAllocator);
 #endif
    default:
       return nullptr;
@@ -130,6 +140,7 @@ util::wsi_platform_set find_enabled_layer_platforms(const VkInstanceCreateInfo *
 VkResult add_extensions_required_by_layer(VkPhysicalDevice phys_dev, const util::wsi_platform_set enabled_platforms,
                                           util::extension_list &extensions_to_enable)
 {
+#if 0
    util::allocator allocator{extensions_to_enable.get_allocator(), VK_SYSTEM_ALLOCATION_SCOPE_COMMAND};
    util::extension_list device_extensions{allocator};
    VkResult res = device_extensions.add(phys_dev);
@@ -137,7 +148,9 @@ VkResult add_extensions_required_by_layer(VkPhysicalDevice phys_dev, const util:
    {
       return res;
    }
-
+#else
+   VkResult res;
+#endif
    for (const auto &wsi_ext : supported_wsi_extensions)
    {
       /* Skip iterating over platforms not enabled in the instance. */
@@ -148,6 +161,7 @@ VkResult add_extensions_required_by_layer(VkPhysicalDevice phys_dev, const util:
 
       surface_properties *props = get_surface_properties(wsi_ext.platform);
       const auto &extensions_required_by_layer = props->get_required_device_extensions();
+#if 0
       bool supported = device_extensions.contains(extensions_required_by_layer);
       if (!supported)
       {
@@ -158,6 +172,7 @@ VkResult add_extensions_required_by_layer(VkPhysicalDevice phys_dev, const util:
           */
          return VK_ERROR_INITIALIZATION_FAILED;
       }
+#endif
 
       res = extensions_to_enable.add(extensions_required_by_layer);
       if (res != VK_SUCCESS)