demos: GH692 Added a supporting of wayland wsi
authorMun Gwan-gyeong <elongbug@gmail.com>
Sun, 26 Jun 2016 20:34:44 +0000 (05:34 +0900)
committerDustin Graves <dustin@lunarg.com>
Wed, 29 Jun 2016 23:03:48 +0000 (17:03 -0600)
Added a supporting of wayland wsi to cube, tri and smoke.

Change-Id: Ifa291d334cb1ed54018573c970078051c6867d96
Signed-off-by: Mun Gwan-gyeong <elongbug@gmail.com>
cmake/FindWayland.cmake [new file with mode: 0644]
demos/CMakeLists.txt
demos/cube.c
demos/smoke/CMakeLists.txt
demos/smoke/Main.cpp
demos/smoke/ShellWayland.cpp [new file with mode: 0644]
demos/smoke/ShellWayland.h [new file with mode: 0644]
demos/tri.c

diff --git a/cmake/FindWayland.cmake b/cmake/FindWayland.cmake
new file mode 100644 (file)
index 0000000..f93218b
--- /dev/null
@@ -0,0 +1,66 @@
+# Try to find Wayland on a Unix system
+#
+# This will define:
+#
+#   WAYLAND_FOUND       - True if Wayland is found
+#   WAYLAND_LIBRARIES   - Link these to use Wayland
+#   WAYLAND_INCLUDE_DIR - Include directory for Wayland
+#   WAYLAND_DEFINITIONS - Compiler flags for using Wayland
+#
+# In addition the following more fine grained variables will be defined:
+#
+#   WAYLAND_CLIENT_FOUND  WAYLAND_CLIENT_INCLUDE_DIR  WAYLAND_CLIENT_LIBRARIES
+#   WAYLAND_SERVER_FOUND  WAYLAND_SERVER_INCLUDE_DIR  WAYLAND_SERVER_LIBRARIES
+#   WAYLAND_EGL_FOUND     WAYLAND_EGL_INCLUDE_DIR     WAYLAND_EGL_LIBRARIES
+#
+# Copyright (c) 2013 Martin Gräßlin <mgraesslin@kde.org>
+#
+# Redistribution and use is allowed according to the terms of the BSD license.
+# For details see the accompanying COPYING-CMAKE-SCRIPTS file.
+
+IF (NOT WIN32)
+  IF (WAYLAND_INCLUDE_DIR AND WAYLAND_LIBRARIES)
+    # In the cache already
+    SET(WAYLAND_FIND_QUIETLY TRUE)
+  ENDIF ()
+
+  # Use pkg-config to get the directories and then use these values
+  # in the FIND_PATH() and FIND_LIBRARY() calls
+  FIND_PACKAGE(PkgConfig)
+  PKG_CHECK_MODULES(PKG_WAYLAND QUIET wayland-client wayland-server wayland-egl wayland-cursor)
+
+  SET(WAYLAND_DEFINITIONS ${PKG_WAYLAND_CFLAGS})
+
+  FIND_PATH(WAYLAND_CLIENT_INCLUDE_DIR  NAMES wayland-client.h HINTS ${PKG_WAYLAND_INCLUDE_DIRS})
+  FIND_PATH(WAYLAND_SERVER_INCLUDE_DIR  NAMES wayland-server.h HINTS ${PKG_WAYLAND_INCLUDE_DIRS})
+  FIND_PATH(WAYLAND_EGL_INCLUDE_DIR     NAMES wayland-egl.h    HINTS ${PKG_WAYLAND_INCLUDE_DIRS})
+  FIND_PATH(WAYLAND_CURSOR_INCLUDE_DIR  NAMES wayland-cursor.h HINTS ${PKG_WAYLAND_INCLUDE_DIRS})
+
+  FIND_LIBRARY(WAYLAND_CLIENT_LIBRARIES NAMES wayland-client   HINTS ${PKG_WAYLAND_LIBRARY_DIRS})
+  FIND_LIBRARY(WAYLAND_SERVER_LIBRARIES NAMES wayland-server   HINTS ${PKG_WAYLAND_LIBRARY_DIRS})
+  FIND_LIBRARY(WAYLAND_EGL_LIBRARIES    NAMES wayland-egl      HINTS ${PKG_WAYLAND_LIBRARY_DIRS})
+  FIND_LIBRARY(WAYLAND_CURSOR_LIBRARIES NAMES wayland-cursor   HINTS ${PKG_WAYLAND_LIBRARY_DIRS})
+
+  set(WAYLAND_INCLUDE_DIR ${WAYLAND_CLIENT_INCLUDE_DIR} ${WAYLAND_SERVER_INCLUDE_DIR} ${WAYLAND_EGL_INCLUDE_DIR} ${WAYLAND_CURSOR_INCLUDE_DIR})
+
+  set(WAYLAND_LIBRARIES ${WAYLAND_CLIENT_LIBRARIES} ${WAYLAND_SERVER_LIBRARIES} ${WAYLAND_EGL_LIBRARIES} ${WAYLAND_CURSOR_LIBRARIES})
+
+  list(REMOVE_DUPLICATES WAYLAND_INCLUDE_DIR)
+
+  include(FindPackageHandleStandardArgs)
+
+  FIND_PACKAGE_HANDLE_STANDARD_ARGS(WAYLAND_CLIENT  DEFAULT_MSG  WAYLAND_CLIENT_LIBRARIES  WAYLAND_CLIENT_INCLUDE_DIR)
+  FIND_PACKAGE_HANDLE_STANDARD_ARGS(WAYLAND_SERVER  DEFAULT_MSG  WAYLAND_SERVER_LIBRARIES  WAYLAND_SERVER_INCLUDE_DIR)
+  FIND_PACKAGE_HANDLE_STANDARD_ARGS(WAYLAND_EGL     DEFAULT_MSG  WAYLAND_EGL_LIBRARIES     WAYLAND_EGL_INCLUDE_DIR)
+  FIND_PACKAGE_HANDLE_STANDARD_ARGS(WAYLAND_CURSOR  DEFAULT_MSG  WAYLAND_CURSOR_LIBRARIES  WAYLAND_CURSOR_INCLUDE_DIR)
+  FIND_PACKAGE_HANDLE_STANDARD_ARGS(WAYLAND         DEFAULT_MSG  WAYLAND_LIBRARIES         WAYLAND_INCLUDE_DIR)
+
+  MARK_AS_ADVANCED(
+        WAYLAND_INCLUDE_DIR         WAYLAND_LIBRARIES
+        WAYLAND_CLIENT_INCLUDE_DIR  WAYLAND_CLIENT_LIBRARIES
+        WAYLAND_SERVER_INCLUDE_DIR  WAYLAND_SERVER_LIBRARIES
+        WAYLAND_EGL_INCLUDE_DIR     WAYLAND_EGL_LIBRARIES
+        WAYLAND_CURSOR_INCLUDE_DIR  WAYLAND_CURSOR_LIBRARIES
+  )
+
+ENDIF ()
index 652efb7..26e86fb 100644 (file)
@@ -1,6 +1,13 @@
 if(NOT WIN32)
-    find_package(XCB REQUIRED)
-    find_package(X11 REQUIRED)
+    if (BUILD_WSI_XCB_SUPPORT)
+        find_package(XCB REQUIRED)
+    endif()
+    if (BUILD_WSI_XLIB_SUPPORT)
+        find_package(X11 REQUIRED)
+    endif()
+    if (BUILD_WSI_WAYLAND_SUPPORT)
+        find_package(Wayland REQUIRED)
+    endif()
 endif()
 
 file(GLOB TEXTURES
@@ -80,12 +87,21 @@ else()
 endif()
 
 if(NOT WIN32)
-    include_directories (
-       ${XCB_INCLUDE_DIRS}
-       ${X11_INCLUDE_DIRS}
-       "${PROJECT_SOURCE_DIR}/icd/common"
-       )
-    link_libraries(${XCB_LIBRARIES} ${X11_LIBRARIES} vulkan m)
+    if(BUILD_WSI_XCB_SUPPORT)
+        include_directories(${XCB_INCLUDE_DIRS})
+        link_libraries(${XCB_LIBRARIES})
+    endif()
+    if(BUILD_WSI_XLIB_SUPPORT)
+        include_directories(${X11_INCLUDE_DIRS})
+        link_libraries(${X11_LIBRARIES})
+    endif()
+    if(BUILD_WSI_WAYLAND_SUPPORT)
+        include_directories(${WAYLAND_CLIENT_INCLUDE_DIR})
+        link_libraries(${WAYLAND_CLIENT_LIBRARIES})
+    endif()
+
+    include_directories ("${PROJECT_SOURCE_DIR}/icd/common")
+    link_libraries(vulkan m)
 endif()
 if(WIN32)
     include_directories (
index f669962..f675ca5 100644 (file)
@@ -1,25 +1,26 @@
-    /*
- * Copyright (c) 2015-2016 The Khronos Group Inc.
- * Copyright (c) 2015-2016 Valve Corporation
- * Copyright (c) 2015-2016 LunarG, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- * Author: Chia-I Wu <olv@lunarg.com>
- * Author: Courtney Goeltzenleuchter <courtney@LunarG.com>
- * Author: Ian Elliott <ian@LunarG.com>
- * Author: Jon Ashburn <jon@lunarg.com>
- */
+/*
+* Copyright (c) 2015-2016 The Khronos Group Inc.
+* Copyright (c) 2015-2016 Valve Corporation
+* Copyright (c) 2015-2016 LunarG, Inc.
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*
+* Author: Chia-I Wu <olv@lunarg.com>
+* Author: Courtney Goeltzenleuchter <courtney@LunarG.com>
+* Author: Ian Elliott <ian@LunarG.com>
+* Author: Jon Ashburn <jon@lunarg.com>
+* Author: Gwan-gyeong Mun <elongbug@gmail.com>
+*/
 
 #define _GNU_SOURCE
 #include <stdio.h>
 #define U_ASSERT_ONLY
 #endif
 
+#if defined(__GNUC__)
+#define UNUSED __attribute__((unused))
+#else
+#define UNUSED
+#endif
+
 #ifdef _WIN32
 #define ERR_EXIT(err_msg, err_class)                                           \
     do {                                                                       \
@@ -301,6 +308,13 @@ struct demo {
     xcb_screen_t *screen;
     xcb_window_t xcb_window;
     xcb_intern_atom_reply_t *atom_wm_delete_window;
+#elif defined(VK_USE_PLATFORM_WAYLAND_KHR)
+    struct wl_display *display;
+    struct wl_registry *registry;
+    struct wl_compositor *compositor;
+    struct wl_surface *window;
+    struct wl_shell *shell;
+    struct wl_shell_surface *shell_surface;
 #elif defined(VK_USE_PLATFORM_ANDROID_KHR)
     ANativeWindow* window;
 #endif
@@ -1932,6 +1946,13 @@ static void demo_cleanup(struct demo *demo) {
     xcb_destroy_window(demo->connection, demo->xcb_window);
     xcb_disconnect(demo->connection);
     free(demo->atom_wm_delete_window);
+#elif defined(VK_USE_PLATFORM_WAYLAND_KHR)
+    wl_shell_surface_destroy(demo->shell_surface);
+    wl_surface_destroy(demo->window);
+    wl_shell_destroy(demo->shell);
+    wl_compositor_destroy(demo->compositor);
+    wl_registry_destroy(demo->registry);
+    wl_display_disconnect(demo->display);
 #endif
 }
 
@@ -2293,6 +2314,59 @@ static void demo_create_xcb_window(struct demo *demo) {
     xcb_configure_window(demo->connection, demo->xcb_window,
                          XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y, coords);
 }
+#elif defined(VK_USE_PLATFORM_WAYLAND_KHR)
+static void demo_run(struct demo *demo) {
+    while (!demo->quit) {
+        // Wait for work to finish before updating MVP.
+        vkDeviceWaitIdle(demo->device);
+        demo_update_data_buffer(demo);
+
+        demo_draw(demo);
+
+        // Wait for work to finish before updating MVP.
+        vkDeviceWaitIdle(demo->device);
+        demo->curFrame++;
+        if (demo->frameCount != INT32_MAX && demo->curFrame == demo->frameCount)
+            demo->quit = true;
+    }
+}
+
+static void handle_ping(void *data UNUSED,
+                        struct wl_shell_surface *shell_surface,
+                        uint32_t serial) {
+    wl_shell_surface_pong(shell_surface, serial);
+}
+
+static void handle_configure(void *data UNUSED,
+                             struct wl_shell_surface *shell_surface UNUSED,
+                             uint32_t edges UNUSED, int32_t width UNUSED,
+                             int32_t height UNUSED) {}
+
+static void handle_popup_done(void *data UNUSED,
+                              struct wl_shell_surface *shell_surface UNUSED) {}
+
+static const struct wl_shell_surface_listener shell_surface_listener = {
+    handle_ping, handle_configure, handle_popup_done};
+
+static void demo_create_window(struct demo *demo) {
+    demo->window = wl_compositor_create_surface(demo->compositor);
+    if (!demo->window) {
+        printf("Can not create wayland_surface from compositor!\n");
+        fflush(stdout);
+        exit(1);
+    }
+
+    demo->shell_surface = wl_shell_get_shell_surface(demo->shell, demo->window);
+    if (!demo->shell_surface) {
+        printf("Can not get shell_surface from wayland_surface!\n");
+        fflush(stdout);
+        exit(1);
+    }
+    wl_shell_surface_add_listener(demo->shell_surface, &shell_surface_listener,
+                                  demo);
+    wl_shell_surface_set_toplevel(demo->shell_surface);
+    wl_shell_surface_set_title(demo->shell_surface, APP_SHORT_NAME);
+}
 #elif defined(VK_USE_PLATFORM_ANDROID_KHR)
 static void demo_run(struct demo *demo) {
     if (!demo->prepared)
@@ -2454,6 +2528,14 @@ static void demo_init_vk(struct demo *demo) {
                     VK_KHR_XCB_SURFACE_EXTENSION_NAME;
             }
 #endif
+#if defined(VK_USE_PLATFORM_WAYLAND_KHR)
+            if (!strcmp(VK_KHR_WAYLAND_SURFACE_EXTENSION_NAME,
+                        instance_extensions[i].extensionName)) {
+                platformSurfaceExtFound = 1;
+                demo->extension_names[demo->enabled_extension_count++] =
+                    VK_KHR_WAYLAND_SURFACE_EXTENSION_NAME;
+            }
+#endif
 #if defined(VK_USE_PLATFORM_ANDROID_KHR)
             if (!strcmp(VK_KHR_ANDROID_SURFACE_EXTENSION_NAME,
                         instance_extensions[i].extensionName)) {
@@ -2501,6 +2583,14 @@ static void demo_init_vk(struct demo *demo) {
                  "look at the Getting Started guide for additional "
                  "information.\n",
                  "vkCreateInstance Failure");
+#elif defined(VK_USE_PLATFORM_WAYLAND_KHR)
+        ERR_EXIT("vkEnumerateInstanceExtensionProperties failed to find "
+                 "the " VK_KHR_WAYLAND_SURFACE_EXTENSION_NAME
+                 " extension.\n\nDo you have a compatible "
+                 "Vulkan installable client driver (ICD) installed?\nPlease "
+                 "look at the Getting Started guide for additional "
+                 "information.\n",
+                 "vkCreateInstance Failure");
 #elif defined(VK_USE_PLATFORM_ANDROID_KHR)
         ERR_EXIT("vkEnumerateInstanceExtensionProperties failed to find "
                  "the " VK_KHR_ANDROID_SURFACE_EXTENSION_NAME
@@ -2758,6 +2848,16 @@ static void demo_init_vk_swapchain(struct demo *demo) {
 
     err =
         vkCreateWin32SurfaceKHR(demo->inst, &createInfo, NULL, &demo->surface);
+#elif defined(VK_USE_PLATFORM_WAYLAND_KHR) && !defined(VK_USE_PLATFORM_XCB_KHR)
+    VkWaylandSurfaceCreateInfoKHR createInfo;
+    createInfo.sType = VK_STRUCTURE_TYPE_WAYLAND_SURFACE_CREATE_INFO_KHR;
+    createInfo.pNext = NULL;
+    createInfo.flags = 0;
+    createInfo.display = demo->display;
+    createInfo.surface = demo->window;
+
+    err = vkCreateWaylandSurfaceKHR(demo->inst, &createInfo, NULL,
+                                    &demo->surface);
 #elif defined(VK_USE_PLATFORM_ANDROID_KHR)
     VkAndroidSurfaceCreateInfoKHR createInfo;
     createInfo.sType = VK_STRUCTURE_TYPE_ANDROID_SURFACE_CREATE_INFO_KHR;
@@ -2889,6 +2989,29 @@ static void demo_init_vk_swapchain(struct demo *demo) {
     vkGetPhysicalDeviceMemoryProperties(demo->gpu, &demo->memory_properties);
 }
 
+#if defined(VK_USE_PLATFORM_WAYLAND_KHR) && !defined(VK_USE_PLATFORM_XCB_KHR)
+static void registry_handle_global(void *data, struct wl_registry *registry,
+                                   uint32_t name, const char *interface,
+                                   uint32_t version UNUSED) {
+    struct demo *demo = data;
+    if (strcmp(interface, "wl_compositor") == 0) {
+        demo->compositor =
+            wl_registry_bind(registry, name, &wl_compositor_interface, 3);
+        /* Todo: When xdg_shell protocol has stablized, we should move wl_shell
+         * tp xdg_shell */
+    } else if (strcmp(interface, "wl_shell") == 0) {
+        demo->shell = wl_registry_bind(registry, name, &wl_shell_interface, 1);
+    }
+}
+
+static void registry_handle_global_remove(void *data UNUSED,
+                                          struct wl_registry *registry UNUSED,
+                                          uint32_t name UNUSED) {}
+
+static const struct wl_registry_listener registry_listener = {
+    registry_handle_global, registry_handle_global_remove};
+#endif
+
 static void demo_init_connection(struct demo *demo) {
 #if defined(VK_USE_PLATFORM_XCB_KHR)
     const xcb_setup_t *setup;
@@ -2909,6 +3032,19 @@ static void demo_init_connection(struct demo *demo) {
         xcb_screen_next(&iter);
 
     demo->screen = iter.data;
+#elif defined(VK_USE_PLATFORM_WAYLAND_KHR)
+    demo->display = wl_display_connect(NULL);
+
+    if (demo->display == NULL) {
+        printf("Cannot find a compatible Vulkan installable client driver "
+               "(ICD).\nExiting ...\n");
+        fflush(stdout);
+        exit(1);
+    }
+
+    demo->registry = wl_display_get_registry(demo->display);
+    wl_registry_add_listener(demo->registry, &registry_listener, demo);
+    wl_display_dispatch(demo->display);
 #endif
 }
 
@@ -3140,19 +3276,27 @@ int main(int argc, char **argv) {
     struct demo demo;
 
     demo_init(&demo, argc, argv);
+#if defined(VK_USE_PLATFORM_XLIB_KHR) | defined(VK_USE_PLATFORM_XCB_KHR)
     if (demo.use_xlib)
         demo_create_xlib_window(&demo);
     else
         demo_create_xcb_window(&demo);
+#elif defined(VK_USE_PLATFORM_WAYLAND_KHR)
+    demo_create_window(&demo);
+#endif
 
     demo_init_vk_swapchain(&demo);
 
     demo_prepare(&demo);
 
+#if defined(VK_USE_PLATFORM_XLIB_KHR) | defined(VK_USE_PLATFORM_XCB_KHR)
     if (demo.use_xlib)
         demo_run_xlib(&demo);
     else
         demo_run_xcb(&demo);
+#elif defined(VK_USE_PLATFORM_WAYLAND_KHR)
+    demo_run(&demo);
+#endif
 
     demo_cleanup(&demo);
 
index 04aae19..4dc90cd 100644 (file)
@@ -62,12 +62,21 @@ if(WIN32)
 else()
     list(APPEND libraries PRIVATE -ldl -lrt)
 
-    find_package(XCB REQUIRED)
+    if(BUILD_WSI_XCB_SUPPORT)
+        find_package(XCB REQUIRED)
 
-    list(APPEND sources ShellXcb.cpp ShellXcb.h)
-    list(APPEND definitions PRIVATE -DVK_USE_PLATFORM_XCB_KHR)
-    list(APPEND includes PRIVATE ${XCB_INCLUDES})
-    list(APPEND libraries PRIVATE ${XCB_LIBRARIES})
+        list(APPEND sources ShellXcb.cpp ShellXcb.h)
+        list(APPEND definitions PRIVATE -DVK_USE_PLATFORM_XCB_KHR)
+        list(APPEND includes PRIVATE ${XCB_INCLUDES})
+        list(APPEND libraries PRIVATE ${XCB_LIBRARIES})
+    elseif(BUILD_WSI_WAYLAND_SUPPORT)
+        find_package(Wayland REQUIRED)
+
+        list(APPEND sources ShellWayland.cpp ShellWayland.h)
+        list(APPEND definitions PRIVATE -DVK_USE_PLATFORM_WAYLAND_KHR)
+        list(APPEND includes PRIVATE ${WAYLAND_CLIENT_INCLUDE_DIR})
+        list(APPEND libraries PRIVATE ${WAYLAND_CLIENT_LIBRARIES})
+    endif()
 endif()
 
 set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/demos)
index 7118c8e..a59ff1b 100644 (file)
@@ -45,6 +45,21 @@ int main(int argc, char **argv)
     return 0;
 }
 
+#elif defined(VK_USE_PLATFORM_WAYLAND_KHR)
+
+#include "ShellWayland.h"
+
+int main(int argc, char **argv) {
+    Game *game = create_game(argc, argv);
+    {
+        ShellWayland shell(*game);
+        shell.run();
+    }
+    delete game;
+
+    return 0;
+}
+
 #elif defined(VK_USE_PLATFORM_ANDROID_KHR)
 
 #include <android/log.h>
diff --git a/demos/smoke/ShellWayland.cpp b/demos/smoke/ShellWayland.cpp
new file mode 100644 (file)
index 0000000..0d4bcbc
--- /dev/null
@@ -0,0 +1,290 @@
+/*
+ * Copyright (C) 2016 Google, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <cassert>
+#include <dlfcn.h>
+#include <sstream>
+#include <time.h>
+
+#include "Game.h"
+#include "Helpers.h"
+#include "ShellWayland.h"
+#include <stdio.h>
+#include <string.h>
+
+/* Unused attribute / variable MACRO.
+   Some methods of classes' heirs do not need all fuction parameters.
+   This triggers warning on GCC platfoms. This macro will silence them.
+*/
+#if defined(__GNUC__)
+#define UNUSED __attribute__((unused))
+#else
+#define UNUSED
+#endif
+
+namespace {
+
+class PosixTimer {
+  public:
+    PosixTimer() { reset(); }
+
+    void reset() { clock_gettime(CLOCK_MONOTONIC, &start_); }
+
+    double get() const {
+        struct timespec now;
+        clock_gettime(CLOCK_MONOTONIC, &now);
+
+        constexpr long one_s_in_ns = 1000 * 1000 * 1000;
+        constexpr double one_s_in_ns_d = static_cast<double>(one_s_in_ns);
+
+        time_t s = now.tv_sec - start_.tv_sec;
+        long ns;
+        if (now.tv_nsec > start_.tv_nsec) {
+            ns = now.tv_nsec - start_.tv_nsec;
+        } else {
+            assert(s > 0);
+            s--;
+            ns = one_s_in_ns - (start_.tv_nsec - now.tv_nsec);
+        }
+
+        return static_cast<double>(s) + static_cast<double>(ns) / one_s_in_ns_d;
+    }
+
+  private:
+    struct timespec start_;
+};
+
+} // namespace
+
+const struct wl_registry_listener ShellWayland::registry_listener_ = {
+    ShellWayland::handle_global, ShellWayland::handle_global_remove};
+
+const struct wl_shell_surface_listener ShellWayland::shell_surface_listener_ = {
+    ShellWayland::handle_ping, ShellWayland::handle_configure,
+    ShellWayland::handle_popup_done};
+
+void ShellWayland::handle_global(void *data, struct wl_registry *registry,
+                                 uint32_t id, const char *interface,
+                                 uint32_t version UNUSED) {
+    ShellWayland *_this = static_cast<ShellWayland *>(data);
+
+    if (!strcmp(interface, "wl_compositor"))
+        _this->compositor_ = static_cast<struct wl_compositor *>(
+            wl_registry_bind(registry, id, &wl_compositor_interface, 3));
+    /* Todo: When xdg_shell protocol has stablized, we should move wl_shell tp
+     * xdg_shell */
+    else if (!strcmp(interface, "wl_shell"))
+        _this->shell_ = static_cast<struct wl_shell *>(
+            wl_registry_bind(registry, id, &wl_shell_interface, 1));
+}
+
+void ShellWayland::handle_global_remove(void *data UNUSED,
+                                        struct wl_registry *registry UNUSED,
+                                        uint32_t name UNUSED) {}
+
+void ShellWayland::handle_ping(void *data UNUSED,
+                               struct wl_shell_surface *shell_surface,
+                               uint32_t serial) {
+    wl_shell_surface_pong(shell_surface, serial);
+}
+
+void ShellWayland::handle_configure(
+    void *data UNUSED, struct wl_shell_surface *shell_surface UNUSED,
+    uint32_t edges UNUSED, int32_t width UNUSED, int32_t height UNUSED) {}
+
+void ShellWayland::handle_popup_done(
+    void *data UNUSED, struct wl_shell_surface *shell_surface UNUSED) {}
+
+ShellWayland::ShellWayland(Game &game) : Shell(game) {
+    instance_extensions_.push_back(VK_KHR_WAYLAND_SURFACE_EXTENSION_NAME);
+
+    init_connection();
+    init_vk();
+}
+
+ShellWayland::~ShellWayland() {
+    cleanup_vk();
+    dlclose(lib_handle_);
+
+    if (shell_surface_)
+        wl_shell_surface_destroy(shell_surface_);
+    if (surface_)
+        wl_surface_destroy(surface_);
+    if (shell_)
+        wl_shell_destroy(shell_);
+    if (compositor_)
+        wl_compositor_destroy(compositor_);
+    if (registry_)
+        wl_registry_destroy(registry_);
+    if (display_)
+        wl_display_disconnect(display_);
+}
+
+void ShellWayland::init_connection() {
+    try {
+        display_ = wl_display_connect(NULL);
+        if (!display_)
+            throw std::runtime_error("failed to connect to the display server");
+
+        registry_ = wl_display_get_registry(display_);
+        if (!registry_)
+            throw std::runtime_error("failed to get registry");
+
+        wl_registry_add_listener(registry_, &registry_listener_, this);
+        wl_display_roundtrip(display_);
+
+        if (!compositor_)
+            throw std::runtime_error("failed to bind compositor");
+
+        if (!shell_)
+            throw std::runtime_error("failed to bind shell");
+    } catch (...) {
+        if (shell_)
+            wl_shell_destroy(shell_);
+        if (compositor_)
+            wl_compositor_destroy(compositor_);
+        if (registry_)
+            wl_registry_destroy(registry_);
+        if (display_)
+            wl_display_disconnect(display_);
+
+        throw;
+    }
+}
+
+void ShellWayland::create_window() {
+    surface_ = wl_compositor_create_surface(compositor_);
+    if (!surface_)
+        throw std::runtime_error("failed to create surface");
+
+    shell_surface_ = wl_shell_get_shell_surface(shell_, surface_);
+    if (!shell_surface_)
+        throw std::runtime_error("failed to shell_surface");
+
+    wl_shell_surface_add_listener(shell_surface_, &shell_surface_listener_,
+                                  this);
+    // set title
+    wl_shell_surface_set_title(shell_surface_, settings_.name.c_str());
+    wl_shell_surface_set_toplevel(shell_surface_);
+}
+
+PFN_vkGetInstanceProcAddr ShellWayland::load_vk() {
+    const char filename[] = "libvulkan.so";
+    void *handle, *symbol;
+
+#ifdef UNINSTALLED_LOADER
+    handle = dlopen(UNINSTALLED_LOADER, RTLD_LAZY);
+    if (!handle)
+        handle = dlopen(filename, RTLD_LAZY);
+#else
+    handle = dlopen(filename, RTLD_LAZY);
+#endif
+
+    if (handle)
+        symbol = dlsym(handle, "vkGetInstanceProcAddr");
+
+    if (!handle || !symbol) {
+        std::stringstream ss;
+        ss << "failed to load " << dlerror();
+
+        if (handle)
+            dlclose(handle);
+
+        throw std::runtime_error(ss.str());
+    }
+
+    lib_handle_ = handle;
+
+    return reinterpret_cast<PFN_vkGetInstanceProcAddr>(symbol);
+}
+
+bool ShellWayland::can_present(VkPhysicalDevice phy, uint32_t queue_family) {
+    return vk::GetPhysicalDeviceWaylandPresentationSupportKHR(phy, queue_family,
+                                                              display_);
+}
+
+VkSurfaceKHR ShellWayland::create_surface(VkInstance instance) {
+    VkWaylandSurfaceCreateInfoKHR surface_info = {};
+    surface_info.sType = VK_STRUCTURE_TYPE_WAYLAND_SURFACE_CREATE_INFO_KHR;
+    surface_info.display = display_;
+    surface_info.surface = surface_;
+
+    VkSurfaceKHR surface;
+    vk::assert_success(vk::CreateWaylandSurfaceKHR(instance, &surface_info,
+                                                   nullptr, &surface));
+
+    return surface;
+}
+
+void ShellWayland::loop_wait() {
+    while (true) {
+        if (quit_)
+            break;
+
+        acquire_back_buffer();
+        present_back_buffer();
+    }
+}
+
+void ShellWayland::loop_poll() {
+    PosixTimer timer;
+
+    double current_time = timer.get();
+    double profile_start_time = current_time;
+    int profile_present_count = 0;
+
+    while (true) {
+        if (quit_)
+            break;
+
+        acquire_back_buffer();
+
+        double t = timer.get();
+        add_game_time(static_cast<float>(t - current_time));
+
+        present_back_buffer();
+
+        current_time = t;
+
+        profile_present_count++;
+        if (current_time - profile_start_time >= 5.0) {
+            const double fps =
+                profile_present_count / (current_time - profile_start_time);
+            std::stringstream ss;
+            ss << profile_present_count << " presents in "
+               << current_time - profile_start_time << " seconds "
+               << "(FPS: " << fps << ")";
+            log(LOG_INFO, ss.str().c_str());
+
+            profile_start_time = current_time;
+            profile_present_count = 0;
+        }
+    }
+}
+
+void ShellWayland::run() {
+    create_window();
+    create_context();
+    resize_swapchain(settings_.initial_width, settings_.initial_height);
+
+    quit_ = false;
+    if (settings_.animate)
+        loop_poll();
+    else
+        loop_wait();
+
+    destroy_context();
+}
diff --git a/demos/smoke/ShellWayland.h b/demos/smoke/ShellWayland.h
new file mode 100644 (file)
index 0000000..ab7f279
--- /dev/null
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2016 Google, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SHELL_WAYLAND_H
+#define SHELL_WAYLAND_H
+
+#include "Shell.h"
+
+class ShellWayland : public Shell {
+  public:
+    ShellWayland(Game &game);
+    ~ShellWayland();
+
+    void run();
+    void quit() { quit_ = true; }
+
+  private:
+    void init_connection();
+
+    PFN_vkGetInstanceProcAddr load_vk();
+    bool can_present(VkPhysicalDevice phy, uint32_t queue_family);
+
+    void create_window();
+    VkSurfaceKHR create_surface(VkInstance instance);
+
+    void loop_wait();
+    void loop_poll();
+
+    void *lib_handle_;
+    bool quit_;
+
+    struct wl_display *display_;
+    struct wl_registry *registry_;
+    struct wl_compositor *compositor_;
+    struct wl_shell *shell_;
+    struct wl_surface *surface_;
+    struct wl_shell_surface *shell_surface_;
+
+    static void handle_global(void *data, struct wl_registry *registry,
+                              uint32_t id, const char *interface,
+                              uint32_t version);
+    static void handle_global_remove(void *data, struct wl_registry *registry,
+                                     uint32_t name);
+    static void handle_ping(void *data, struct wl_shell_surface *shell_surface,
+                            uint32_t serial);
+    static void handle_configure(void *data,
+                                 struct wl_shell_surface *shell_surface,
+                                 uint32_t edges, int32_t width, int32_t height);
+    static void handle_popup_done(void *data,
+                                  struct wl_shell_surface *shell_surface);
+
+    static const struct wl_registry_listener registry_listener_;
+    static const struct wl_shell_surface_listener shell_surface_listener_;
+};
+
+#endif // SHELL_WAYLAND_H
index ed2f128..91c76ab 100644 (file)
@@ -21,6 +21,7 @@
  * Author: Ian Elliott <ian@LunarG.com>
  * Author: Jon Ashburn <jon@lunarg.com>
  * Author: Piers Daniell <pdaniell@nvidia.com>
+ * Author: Gwan-gyeong Mun <elongbug@gmail.com>
  */
 /*
  * Draw a textured triangle with depth testing.  This is written against Intel
 #define U_ASSERT_ONLY
 #endif
 
+#if defined(__GNUC__)
+#define UNUSED __attribute__((unused))
+#else
+#define UNUSED
+#endif
+
 #ifdef _WIN32
 #define ERR_EXIT(err_msg, err_class)                                           \
     do {                                                                       \
@@ -150,6 +157,13 @@ struct demo {
     xcb_screen_t *screen;
     xcb_window_t window;
     xcb_intern_atom_reply_t *atom_wm_delete_window;
+#elif defined(VK_USE_PLATFORM_WAYLAND_KHR)
+    struct wl_display *display;
+    struct wl_registry *registry;
+    struct wl_compositor *compositor;
+    struct wl_surface *window;
+    struct wl_shell *shell;
+    struct wl_shell_surface *shell_surface;
 #elif defined(VK_USE_PLATFORM_ANDROID_KHR)
     ANativeWindow* window;
 #endif
@@ -1764,6 +1778,63 @@ static void demo_create_window(struct demo *demo) {
 
     xcb_map_window(demo->connection, demo->window);
 }
+#elif defined(VK_USE_PLATFORM_WAYLAND_KHR)
+static void demo_run(struct demo *demo) {
+
+    while (!demo->quit) {
+        demo_draw(demo);
+
+        if (demo->depthStencil > 0.99f)
+            demo->depthIncrement = -0.001f;
+        if (demo->depthStencil < 0.8f)
+            demo->depthIncrement = 0.001f;
+
+        demo->depthStencil += demo->depthIncrement;
+
+        // Wait for work to finish before updating MVP.
+        vkDeviceWaitIdle(demo->device);
+        demo->curFrame++;
+        if (demo->frameCount != INT32_MAX && demo->curFrame == demo->frameCount)
+            demo->quit = true;
+    }
+}
+
+static void handle_ping(void *data UNUSED,
+                        struct wl_shell_surface *shell_surface,
+                        uint32_t serial) {
+    wl_shell_surface_pong(shell_surface, serial);
+}
+
+static void handle_configure(void *data UNUSED,
+                             struct wl_shell_surface *shell_surface UNUSED,
+                             uint32_t edges UNUSED, int32_t width UNUSED,
+                             int32_t height UNUSED) {}
+
+static void handle_popup_done(void *data UNUSED,
+                              struct wl_shell_surface *shell_surface UNUSED) {}
+
+static const struct wl_shell_surface_listener shell_surface_listener = {
+    handle_ping, handle_configure, handle_popup_done};
+
+static void demo_create_window(struct demo *demo) {
+    demo->window = wl_compositor_create_surface(demo->compositor);
+    if (!demo->window) {
+        printf("Can not create wayland_surface from compositor!\n");
+        fflush(stdout);
+        exit(1);
+    }
+
+    demo->shell_surface = wl_shell_get_shell_surface(demo->shell, demo->window);
+    if (!demo->shell_surface) {
+        printf("Can not get shell_surface from wayland_surface!\n");
+        fflush(stdout);
+        exit(1);
+    }
+    wl_shell_surface_add_listener(demo->shell_surface, &shell_surface_listener,
+                                  demo);
+    wl_shell_surface_set_toplevel(demo->shell_surface);
+    wl_shell_surface_set_title(demo->shell_surface, APP_SHORT_NAME);
+}
 #elif defined(VK_USE_PLATFORM_ANDROID_KHR)
 static void demo_run(struct demo *demo) {
     if (!demo->prepared)
@@ -1910,6 +1981,13 @@ static void demo_init_vk(struct demo *demo) {
                 demo->extension_names[demo->enabled_extension_count++] =
                     VK_KHR_XCB_SURFACE_EXTENSION_NAME;
             }
+#elif defined(VK_USE_PLATFORM_WAYLAND_KHR)
+            if (!strcmp(VK_KHR_WAYLAND_SURFACE_EXTENSION_NAME,
+                        instance_extensions[i].extensionName)) {
+                platformSurfaceExtFound = 1;
+                demo->extension_names[demo->enabled_extension_count++] =
+                    VK_KHR_WAYLAND_SURFACE_EXTENSION_NAME;
+            }
 #elif defined(VK_USE_PLATFORM_ANDROID_KHR)
             if (!strcmp(VK_KHR_ANDROID_SURFACE_EXTENSION_NAME,
                         instance_extensions[i].extensionName)) {
@@ -1957,6 +2035,14 @@ static void demo_init_vk(struct demo *demo) {
                  "look at the Getting Started guide for additional "
                  "information.\n",
                  "vkCreateInstance Failure");
+#elif defined(VK_USE_PLATFORM_WAYLAND_KHR)
+        ERR_EXIT("vkEnumerateInstanceExtensionProperties failed to find "
+                 "the " VK_KHR_WAYLAND_SURFACE_EXTENSION_NAME
+                 " extension.\n\nDo you have a compatible "
+                 "Vulkan installable client driver (ICD) installed?\nPlease "
+                 "look at the Getting Started guide for additional "
+                 "information.\n",
+                 "vkCreateInstance Failure");
 #elif defined(VK_USE_PLATFORM_ANDROID_KHR)
         ERR_EXIT("vkEnumerateInstanceExtensionProperties failed to find "
                  "the " VK_KHR_ANDROID_SURFACE_EXTENSION_NAME
@@ -2203,6 +2289,16 @@ static void demo_init_vk_swapchain(struct demo *demo) {
     createInfo.window = demo->window;
 
     err = vkCreateXcbSurfaceKHR(demo->inst, &createInfo, NULL, &demo->surface);
+#elif defined(VK_USE_PLATFORM_WAYLAND_KHR)
+    VkWaylandSurfaceCreateInfoKHR createInfo;
+    createInfo.sType = VK_STRUCTURE_TYPE_WAYLAND_SURFACE_CREATE_INFO_KHR;
+    createInfo.pNext = NULL;
+    createInfo.flags = 0;
+    createInfo.display = demo->display;
+    createInfo.surface = demo->window;
+
+    err = vkCreateWaylandSurfaceKHR(demo->inst, &createInfo, NULL,
+                                    &demo->surface);
 #elif defined(VK_USE_PLATFORM_ANDROID_KHR)
     VkAndroidSurfaceCreateInfoKHR createInfo;
     createInfo.sType = VK_STRUCTURE_TYPE_ANDROID_SURFACE_CREATE_INFO_KHR;
@@ -2302,6 +2398,29 @@ static void demo_init_vk_swapchain(struct demo *demo) {
     vkGetPhysicalDeviceMemoryProperties(demo->gpu, &demo->memory_properties);
 }
 
+#if defined(VK_USE_PLATFORM_WAYLAND_KHR) && !defined(VK_USE_PLATFORM_XCB_KHR)
+static void registry_handle_global(void *data, struct wl_registry *registry,
+                                   uint32_t name, const char *interface,
+                                   uint32_t version UNUSED) {
+    struct demo *demo = data;
+    if (strcmp(interface, "wl_compositor") == 0) {
+        demo->compositor =
+            wl_registry_bind(registry, name, &wl_compositor_interface, 3);
+        /* Todo: When xdg_shell protocol has stablized, we should move wl_shell
+         * tp xdg_shell */
+    } else if (strcmp(interface, "wl_shell") == 0) {
+        demo->shell = wl_registry_bind(registry, name, &wl_shell_interface, 1);
+    }
+}
+
+static void registry_handle_global_remove(void *data UNUSED,
+                                          struct wl_registry *registry UNUSED,
+                                          uint32_t name UNUSED) {}
+
+static const struct wl_registry_listener registry_listener = {
+    registry_handle_global, registry_handle_global_remove};
+#endif
+
 static void demo_init_connection(struct demo *demo) {
 #if defined(VK_USE_PLATFORM_XCB_KHR)
     const xcb_setup_t *setup;
@@ -2322,6 +2441,19 @@ static void demo_init_connection(struct demo *demo) {
         xcb_screen_next(&iter);
 
     demo->screen = iter.data;
+#elif defined(VK_USE_PLATFORM_WAYLAND_KHR)
+    demo->display = wl_display_connect(NULL);
+
+    if (demo->display == NULL) {
+        printf("Cannot find a compatible Vulkan installable client driver "
+               "(ICD).\nExiting ...\n");
+        fflush(stdout);
+        exit(1);
+    }
+
+    demo->registry = wl_display_get_registry(demo->display);
+    wl_registry_add_listener(demo->registry, &registry_listener, demo);
+    wl_display_dispatch(demo->display);
 #endif // _WIN32
 }
 
@@ -2426,6 +2558,13 @@ static void demo_cleanup(struct demo *demo) {
     xcb_destroy_window(demo->connection, demo->window);
     xcb_disconnect(demo->connection);
     free(demo->atom_wm_delete_window);
+#elif defined(VK_USE_PLATFORM_WAYLAND_KHR)
+    wl_shell_surface_destroy(demo->shell_surface);
+    wl_surface_destroy(demo->window);
+    wl_shell_destroy(demo->shell);
+    wl_compositor_destroy(demo->compositor);
+    wl_registry_destroy(demo->registry);
+    wl_display_disconnect(demo->display);
 #endif
 }
 
@@ -2635,7 +2774,7 @@ void android_main(struct android_app *app)
 
 }
 
-#elif defined(VK_USE_PLATFORM_XCB_KHR)
+#elif defined(VK_USE_PLATFORM_XCB_KHR) | defined(VK_USE_PLATFORM_WAYLAND_KHR)
 
 int main(const int argc, const char *argv[]) {
     struct demo demo;