new vulkan based video sink
authorMatthew Waters <matthew@centricular.com>
Sat, 24 Oct 2015 06:29:05 +0000 (17:29 +1100)
committerMatthew Waters <matthew@centricular.com>
Tue, 16 Feb 2016 14:13:43 +0000 (01:13 +1100)
Currently xcb is the only winsys that is implemented and there's no redraws et
al

36 files changed:
configure.ac
ext/Makefile.am
ext/vulkan/.gitignore [new file with mode: 0644]
ext/vulkan/Makefile.am [new file with mode: 0644]
ext/vulkan/gstvulkan.c [new file with mode: 0644]
ext/vulkan/vk.h [new file with mode: 0644]
ext/vulkan/vk_fwd.h [new file with mode: 0644]
ext/vulkan/vkdevice.c [new file with mode: 0644]
ext/vulkan/vkdevice.h [new file with mode: 0644]
ext/vulkan/vkdisplay.c [new file with mode: 0644]
ext/vulkan/vkdisplay.h [new file with mode: 0644]
ext/vulkan/vkerror.c [new file with mode: 0644]
ext/vulkan/vkerror.h [new file with mode: 0644]
ext/vulkan/vkimagememory.c [new file with mode: 0644]
ext/vulkan/vkimagememory.h [new file with mode: 0644]
ext/vulkan/vkinstance.c [new file with mode: 0644]
ext/vulkan/vkinstance.h [new file with mode: 0644]
ext/vulkan/vkmemory.c [new file with mode: 0644]
ext/vulkan/vkmemory.h [new file with mode: 0644]
ext/vulkan/vkqueue.c [new file with mode: 0644]
ext/vulkan/vkqueue.h [new file with mode: 0644]
ext/vulkan/vksink.c [new file with mode: 0644]
ext/vulkan/vksink.h [new file with mode: 0644]
ext/vulkan/vkswapper.c [new file with mode: 0644]
ext/vulkan/vkswapper.h [new file with mode: 0644]
ext/vulkan/vkutils.c [new file with mode: 0644]
ext/vulkan/vkutils.h [new file with mode: 0644]
ext/vulkan/vkwindow.c [new file with mode: 0644]
ext/vulkan/vkwindow.h [new file with mode: 0644]
ext/vulkan/xcb/Makefile.am [new file with mode: 0644]
ext/vulkan/xcb/vkdisplay_xcb.c [new file with mode: 0644]
ext/vulkan/xcb/vkdisplay_xcb.h [new file with mode: 0644]
ext/vulkan/xcb/vkwindow_xcb.c [new file with mode: 0644]
ext/vulkan/xcb/vkwindow_xcb.h [new file with mode: 0644]
ext/vulkan/xcb/xcb_event_source.c [new file with mode: 0644]
ext/vulkan/xcb/xcb_event_source.h [new file with mode: 0644]

index f1e093acfb03c5e0a540920699686b13091d5c7e..c50647cca058d33a5c88ea9c2390bd14dc3d927e 100644 (file)
@@ -1464,6 +1464,55 @@ AC_SUBST(JPEG_LIBS)
 AC_SUBST(HAVE_JPEG)
 AM_CONDITIONAL(HAVE_JPEG, test "x$HAVE_JPEG" = "xyes")
 
+dnl Vulkan
+VULKAN_CONFIG_DEFINES=""
+
+PKG_CHECK_MODULES(XCB, xcb >= 1.10, HAVE_XCB=yes, HAVE_XCB=no)
+
+AM_CONDITIONAL(USE_XCB, test "x$HAVE_XCB" = "xyes")
+if test "x$HAVE_XCB" = "xyes"; then
+  VULKAN_CONFIG_DEFINES="$VULKAN_CONFIG_DEFINES
+  #define GST_VULKAN_HAVE_WINDOW_XCB 1"
+fi
+
+AC_CONFIG_COMMANDS([ext/vulkan/vkconfig.h], [
+       outfile=vkconfig.h-tmp
+       cat > $outfile <<\_______EOF
+/* vkconfig.h
+ *
+ * This is a generated file.  Please modify `configure.ac'
+ */
+
+#ifndef __GST_VULKAN_CONFIG_H__
+#define __GST_VULKAN_CONFIG_H__
+
+#include <gst/gst.h>
+
+G_BEGIN_DECLS
+
+_______EOF
+
+       cat >>$outfile <<_______EOF
+$vulkan_config_defines
+_______EOF
+
+       cat >>$outfile <<_______EOF
+
+G_END_DECLS
+
+#endif  /* __GST_VULKAN_CONFIG_H__ */
+_______EOF
+
+
+       if cmp -s $outfile ext/vulkan/vkconfig.h; then
+          AC_MSG_NOTICE([ext/vulkan/vkconfig.h is unchanged])
+         rm -f $outfile
+       else
+         mv $outfile ext/vulkan/vkconfig.h
+       fi
+],[
+vulkan_config_defines='$VULKAN_CONFIG_DEFINES'
+])
 
 dnl *** sys plug-ins ***
 
@@ -2851,6 +2900,22 @@ AG_GST_CHECK_FEATURE(QT, [Qt elements], qt, [
   ])
 ])
 
+dnl *** Vulkan ***
+translit(dnm, m, l) AM_CONDITIONAL(USE_VULKAN, true)
+AG_GST_CHECK_FEATURE(VULKAN, [Vulkan elements], vulkan, [
+  HAVE_VULKAN=no
+  AC_CHECK_HEADER(vulkan/vulkan.h, [
+    AC_CHECK_LIB(vulkan, vkCreateDevice, [
+      VULKAN_LIBS="-lvulkan"
+      AC_SUBST(VULKAN_LIBS)
+      dnl TODO check platform support (x11, win32, wayland, android, etc)
+      if test "x$HAVE_XCB" = "xyes"; then
+        HAVE_VULKAN=yes
+      fi
+    ], [])
+  ], [])
+])
+
 dnl *** libvisual ***
 translit(dnm, m, l) AM_CONDITIONAL(USE_LIBVISUAL, true)
 AG_GST_CHECK_FEATURE(LIBVISUAL, [libvisual visualization library], libvisual, [
@@ -3636,6 +3701,8 @@ ext/teletextdec/Makefile
 ext/gme/Makefile
 ext/spc/Makefile
 ext/timidity/Makefile
+ext/vulkan/Makefile
+ext/vulkan/xcb/Makefile
 ext/webp/Makefile
 ext/x265/Makefile
 ext/xvid/Makefile
index d75f119087e3b862ab3d1f3f776d2b1aa3b9cc4a..f97ef44c879f98437f056db4cbcd1c22089537d2 100644 (file)
@@ -406,6 +406,12 @@ else
 DTLS_DIR=
 endif
 
+if USE_VULKAN
+VULKAN_DIR=vulkan
+else
+VULKAN_DIR=
+endif
+
 SUBDIRS=\
        $(VOAACENC_DIR) \
        $(ASSRENDER_DIR) \
@@ -474,7 +480,8 @@ SUBDIRS=\
        $(HLS_DIR) \
        $(WEBP_DIR) \
        $(X265_DIR) \
-       $(DTLS_DIR)
+       $(DTLS_DIR) \
+       $(VULKAN_DIR)
 
 DIST_SUBDIRS = \
        assrender \
@@ -541,6 +548,7 @@ DIST_SUBDIRS = \
        rtmp \
        webp \
        x265 \
-       dtls
+       dtls \
+       vulkan
 
 include $(top_srcdir)/common/parallel-subdirs.mak
diff --git a/ext/vulkan/.gitignore b/ext/vulkan/.gitignore
new file mode 100644 (file)
index 0000000..dc690c8
--- /dev/null
@@ -0,0 +1 @@
+vkconfig.h
diff --git a/ext/vulkan/Makefile.am b/ext/vulkan/Makefile.am
new file mode 100644 (file)
index 0000000..6c87b8c
--- /dev/null
@@ -0,0 +1,58 @@
+plugin_LTLIBRARIES = libgstvulkan.la
+
+SUBDIRS =
+DIST_SUBDIRS = xcb
+DISTCLEANFILES = vkconfig.h
+
+libgstvulkan_la_SOURCES = \
+       gstvulkan.c \
+       vkdevice.c \
+       vkdisplay.c \
+       vkerror.c \
+       vkimagememory.c \
+       vkinstance.c \
+       vkmemory.c \
+       vkqueue.c \
+       vksink.c \
+       vkswapper.c \
+       vkutils.c \
+       vkwindow.c
+
+noinst_HEADERS = \
+       vk.h \
+       vk_fwd.h \
+       vkdevice.h \
+       vkdisplay.h \
+       vkerror.h \
+       vkimagememory.h \
+       vkinstance.h \
+       vkmemory.h \
+       vkqueue.h \
+       vksink.h \
+       vkswapper.h \
+       vkutils.h \
+       vkwindow.h
+
+libgstvulkan_la_CFLAGS = \
+       -I$(top_srcdir)/gst-libs \
+       -I$(top_builddir)/gst-libs \
+       $(GST_CFLAGS) \
+       $(GST_BASE_CFLAGS) \
+       $(GST_PLUGINS_BASE_CFLAGS) \
+       $(VULKAN_CFLAGS)
+
+libgstvulkan_la_LIBADD = \
+       $(GST_BASE_LIBS) \
+       $(GST_PLUGINS_BASE_LIBS) \
+       -lgstvideo-$(GST_API_VERSION) \
+       $(VULKAN_LIBS)
+
+if USE_XCB
+SUBDIRS += xcb
+libgstvulkan_la_LIBADD += xcb/libgstvulkan-xcb.la
+endif
+
+libgstvulkan_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS)
+libgstvulkan_la_LIBTOOLFLAGS = $(GST_PLUGIN_LIBTOOLFLAGS)
+
+
diff --git a/ext/vulkan/gstvulkan.c b/ext/vulkan/gstvulkan.c
new file mode 100644 (file)
index 0000000..1fcc77b
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+ * GStreamer
+ * Copyright (C) 2015 Matthew Waters <matthew@centricular.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+/**
+ * SECTION:plugin-vulkan
+ *
+ * Cross-platform Vulkan plugin.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "vksink.h"
+
+#if GST_VULKAN_HAVE_WINDOW_X11
+#include <X11/Xlib.h>
+#endif
+
+#define GST_CAT_DEFAULT gst_gl_gstgl_debug
+GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
+
+/* Register filters that make up the gstgl plugin */
+static gboolean
+plugin_init (GstPlugin * plugin)
+{
+  GST_DEBUG_CATEGORY_INIT (gst_gl_gstgl_debug, "gstvulkan", 0, "gstvulkan");
+
+#if GST_VULKAN_HAVE_WINDOW_X11
+  if (g_getenv ("GST_VULKAN_XINITTHREADS"))
+    XInitThreads ();
+#endif
+
+  if (!gst_element_register (plugin, "vulkansink",
+          GST_RANK_NONE, GST_TYPE_VULKAN_SINK)) {
+    return FALSE;
+  }
+
+  return TRUE;
+}
+
+GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
+    GST_VERSION_MINOR,
+    vulkan,
+    "Vulkan plugin",
+    plugin_init, VERSION, GST_LICENSE, GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)
diff --git a/ext/vulkan/vk.h b/ext/vulkan/vk.h
new file mode 100644 (file)
index 0000000..9e98fe1
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * GStreamer
+ * Copyright (C) 2015 Matthew Waters <matthew@centricular.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef _VK_H_
+#define _VK_H_
+
+#include <gst/gst.h>
+
+#define VK_PROTOTYPES
+#include <vulkan/vulkan.h>
+#include <vulkan/vk_debug_report_lunarg.h>
+#include <vulkan/vk_ext_khr_swapchain.h>
+#include <vulkan/vk_ext_khr_device_swapchain.h>
+
+#include "vkconfig.h"
+#include "vk_fwd.h"
+
+#include "vkerror.h"
+#include "vkinstance.h"
+#include "vkdevice.h"
+#include "vkqueue.h"
+#include "vkdisplay.h"
+#include "vkwindow.h"
+#include "vkswapper.h"
+#include "vkmemory.h"
+#include "vkimagememory.h"
+
+#endif /* _VK_H_ */
diff --git a/ext/vulkan/vk_fwd.h b/ext/vulkan/vk_fwd.h
new file mode 100644 (file)
index 0000000..59b307d
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+ * GStreamer
+ * Copyright (C) 2015 Matthew Waters <matthew@centricular.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef _VK_FWD_H_
+#define _VK_FWD_H_
+
+#include <gst/gst.h>
+
+G_BEGIN_DECLS
+
+typedef struct _GstVulkanInstance GstVulkanInstance;
+typedef struct _GstVulkanInstanceClass GstVulkanInstanceClass;
+
+typedef struct _GstVulkanDevice GstVulkanDevice;
+typedef struct _GstVulkanDeviceClass GstVulkanDeviceClass;
+
+typedef struct _GstVulkanQueue GstVulkanQueue;
+typedef struct _GstVulkanQueueClass GstVulkanQueueClass;
+
+typedef struct _GstVulkanDisplay GstVulkanDisplay;
+typedef struct _GstVulkanDisplayClass GstVulkanDisplayClass;
+typedef struct _GstVulkanDisplayPrivate GstVulkanDisplayPrivate;
+
+typedef struct _GstVulkanWindow GstVulkanWindow;
+typedef struct _GstVulkanWindowClass GstVulkanWindowClass;
+typedef struct _GstVulkanWindowPrivate GstVulkanWindowPrivate;
+
+typedef struct _GstVulkanSwapper GstVulkanSwapper;
+typedef struct _GstVulkanSwapperClass GstVulkanSwapperClass;
+
+typedef struct _GstVulkanMemory GstVulkanMemory;
+typedef struct _GstVulkanMemoryAllocator GstVulkanMemoryAllocator;
+typedef struct _GstVulkanMemoryAllocatorClass GstVulkanMemoryAllocatorClass;
+
+typedef struct _GstVulkanImageMemory GstVulkanImageMemory;
+typedef struct _GstVulkanImageMemoryAllocator GstVulkanImageMemoryAllocator;
+typedef struct _GstVulkanImageMemoryAllocatorClass GstVulkanImageMemoryAllocatorClass;
+
+G_END_DECLS
+
+#endif /* _VK_FWD_H_ */
diff --git a/ext/vulkan/vkdevice.c b/ext/vulkan/vkdevice.c
new file mode 100644 (file)
index 0000000..0c57196
--- /dev/null
@@ -0,0 +1,401 @@
+/*
+ * GStreamer
+ * Copyright (C) 2015 Matthew Waters <matthew@centricular.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "vkdevice.h"
+#include "vkutils.h"
+
+#include <string.h>
+
+static const char *device_validation_layers[] = {
+  "Threading",
+  "MemTracker",
+  "ObjectTracker",
+  "DrawState",
+  "ParamChecker",
+  "ShaderChecker",
+  "Swapchain",
+  "DeviceLimits",
+  "Image",
+};
+
+#define GST_CAT_DEFAULT gst_vulkan_device_debug
+GST_DEBUG_CATEGORY (GST_CAT_DEFAULT);
+
+G_DEFINE_TYPE_WITH_CODE (GstVulkanDevice, gst_vulkan_device, GST_TYPE_OBJECT,
+    GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "vulkandevice", 0,
+        "Vulkan Device"));
+
+static void gst_vulkan_device_finalize (GObject * object);
+
+GstVulkanDevice *
+gst_vulkan_device_new (GstVulkanInstance * instance)
+{
+  GstVulkanDevice *device = g_object_new (GST_TYPE_VULKAN_DEVICE, NULL);
+
+  device->instance = gst_object_ref (instance);
+  /* FIXME: select this externally */
+  device->device_index = 0;
+
+  return device;
+}
+
+static void
+gst_vulkan_device_init (GstVulkanDevice * device)
+{
+}
+
+static void
+gst_vulkan_device_class_init (GstVulkanDeviceClass * device_class)
+{
+  GObjectClass *gobject_class = (GObjectClass *) device_class;
+
+  gobject_class->finalize = gst_vulkan_device_finalize;
+}
+
+static void
+gst_vulkan_device_finalize (GObject * object)
+{
+  GstVulkanDevice *device = GST_VULKAN_DEVICE (object);
+
+  if (device->cmd_pool.handle)
+    vkDestroyCommandPool (device->device, device->cmd_pool);
+  device->cmd_pool.handle = 0;
+
+  if (device->device)
+    vkDestroyDevice (device->device);
+  device->device = NULL;
+
+  if (device->instance)
+    gst_object_unref (device->instance);
+  device->instance = NULL;
+}
+
+static const gchar *
+_device_type_to_string (VkPhysicalDeviceType type)
+{
+  switch (type) {
+    case VK_PHYSICAL_DEVICE_TYPE_OTHER:
+      return "other";
+    case VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU:
+      return "integrated";
+    case VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU:
+      return "discrete";
+    case VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU:
+      return "virtual";
+    case VK_PHYSICAL_DEVICE_TYPE_CPU:
+      return "CPU";
+    default:
+      return "unknown";
+  }
+}
+
+static gboolean
+_physical_device_info (GstVulkanDevice * device, GError ** error)
+{
+  VkPhysicalDeviceProperties props;
+  VkPhysicalDevice gpu;
+  VkResult err;
+
+  gpu = gst_vulkan_device_get_physical_device (device);
+
+  err = vkGetPhysicalDeviceProperties (gpu, &props);
+  if (gst_vulkan_error_to_g_error (err, error,
+          "vkGetPhysicalDeviceProperties") < 0)
+    return FALSE;
+
+  GST_INFO_OBJECT (device, "device name %s type %s api version %u, "
+      "driver version %u vendor ID 0x%x, device ID 0x%x", props.deviceName,
+      _device_type_to_string (props.deviceType), props.apiVersion,
+      props.driverVersion, props.vendorId, props.deviceId);
+
+  return TRUE;
+}
+
+gboolean
+gst_vulkan_device_open (GstVulkanDevice * device, GError ** error)
+{
+  const char *extension_names[64];
+  uint32_t enabled_extension_count = 0;
+  uint32_t device_extension_count = 0;
+  VkExtensionProperties *device_extensions = NULL;
+  uint32_t enabled_layer_count = 0;
+  uint32_t device_layer_count = 0;
+  VkLayerProperties *device_layers;
+  gboolean have_swapchain_ext;
+  gboolean validation_found;
+  VkPhysicalDevice gpu;
+  VkResult err;
+  guint i;
+
+  g_return_val_if_fail (GST_IS_VULKAN_DEVICE (device), FALSE);
+
+  if (!_physical_device_info (device, error))
+    return FALSE;
+
+  gpu = gst_vulkan_device_get_physical_device (device);
+
+  /* Look for validation layers */
+  err = vkEnumerateDeviceLayerProperties (gpu, &device_layer_count, NULL);
+  if (gst_vulkan_error_to_g_error (err, error,
+          "vkEnumerateDeviceLayerProperties") < 0)
+    return FALSE;
+
+  device_layers = g_new0 (VkLayerProperties, device_layer_count);
+  err =
+      vkEnumerateDeviceLayerProperties (gpu, &device_layer_count,
+      device_layers);
+  if (gst_vulkan_error_to_g_error (err, error,
+          "vkEnumerateDeviceLayerProperties") < 0) {
+    g_free (device_layers);
+    return FALSE;
+  }
+
+  validation_found =
+      _check_for_all_layers (G_N_ELEMENTS (device_validation_layers),
+      device_validation_layers, device_layer_count, device_layers);
+  g_free (device_layers);
+  device_layers = NULL;
+  if (!validation_found) {
+    g_error ("vkEnumerateDeviceLayerProperties failed to find"
+        "a required validation layer.\n\n"
+        "Please look at the Getting Started guide for additional "
+        "information.\nvkCreateDevice Failure");
+    return FALSE;
+  }
+  enabled_layer_count = G_N_ELEMENTS (device_validation_layers);
+
+  err =
+      vkEnumerateDeviceExtensionProperties (gpu, NULL,
+      &device_extension_count, NULL);
+  if (gst_vulkan_error_to_g_error (err, error,
+          "vkEnumerateDeviceExtensionProperties") < 0)
+    return FALSE;
+
+  have_swapchain_ext = 0;
+  enabled_extension_count = 0;
+  memset (extension_names, 0, sizeof (extension_names));
+  device_extensions = g_new0 (VkExtensionProperties, device_extension_count);
+  err = vkEnumerateDeviceExtensionProperties (gpu, NULL,
+      &device_extension_count, device_extensions);
+  if (gst_vulkan_error_to_g_error (err, error,
+          "vkEnumerateDeviceExtensionProperties") < 0) {
+    g_free (device_extensions);
+    return FALSE;
+  }
+
+  for (uint32_t i = 0; i < device_extension_count; i++) {
+    if (!strcmp ("VK_EXT_KHR_device_swapchain", device_extensions[i].extName)) {
+      have_swapchain_ext = TRUE;
+      extension_names[enabled_extension_count++] =
+          "VK_EXT_KHR_device_swapchain";
+    }
+    g_assert (enabled_extension_count < 64);
+  }
+  if (!have_swapchain_ext) {
+    g_error ("vkEnumerateDeviceExtensionProperties failed to find the "
+        "\"VK_EXT_KHR_device_swapchain\" extension.\n\nDo you have a compatible "
+        "Vulkan installable client driver (ICD) installed?\nPlease "
+        "look at the Getting Started guide for additional "
+        "information.\nvkCreateInstance Failure");
+  }
+  g_free (device_extensions);
+
+  err = vkGetPhysicalDeviceProperties (gpu, &device->gpu_props);
+  if (gst_vulkan_error_to_g_error (err, error,
+          "vkGetPhysicalDeviceProperties") < 0)
+    return FALSE;
+
+  err = vkGetPhysicalDeviceMemoryProperties (gpu, &device->memory_properties);
+  if (gst_vulkan_error_to_g_error (err, error,
+          "vkGetPhysicalDeviceProperties") < 0)
+    return FALSE;
+
+  err = vkGetPhysicalDeviceFeatures (gpu, &device->gpu_features);
+  if (gst_vulkan_error_to_g_error (err, error,
+          "vkGetPhysicalDeviceFeatures") < 0)
+    return FALSE;
+
+  /* Call with NULL data to get count */
+  err =
+      vkGetPhysicalDeviceQueueFamilyProperties (gpu, &device->n_queue_families,
+      NULL);
+  if (gst_vulkan_error_to_g_error (err, error,
+          "vkGetPhysicalDeviceQueueFamilyProperties") < 0)
+    return FALSE;
+  g_assert (device->n_queue_families >= 1);
+
+  device->queue_family_props =
+      g_new0 (VkQueueFamilyProperties, device->n_queue_families);
+  err =
+      vkGetPhysicalDeviceQueueFamilyProperties (gpu, &device->n_queue_families,
+      device->queue_family_props);
+  if (gst_vulkan_error_to_g_error (err, error,
+          "vkGetPhysicalDeviceQueueFamilyProperties") < 0)
+    return FALSE;
+
+  /* FIXME: allow overriding/selecting */
+  for (i = 0; i < device->n_queue_families; i++) {
+    if (device->queue_family_props[i].queueFlags & VK_QUEUE_GRAPHICS_BIT)
+      break;
+  }
+  if (i >= device->n_queue_families) {
+    g_set_error (error, GST_VULKAN_ERROR,
+        GST_VULKAN_ERROR_INITIALIZATION_FAILED,
+        "Failed to find a compatible queue family");
+    return FALSE;
+  }
+  device->queue_family_id = i;
+  device->n_queues = 1;
+
+  {
+    const VkDeviceQueueCreateInfo queue_info = {
+      .sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO,
+      .pNext = NULL,
+      .queueFamilyIndex = device->queue_family_id,
+      .queueCount = device->n_queues,
+    };
+    VkDeviceCreateInfo device_info = {
+      .sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO,
+      .pNext = NULL,
+      .queueRecordCount = 1,
+      .pRequestedQueues = &queue_info,
+      .layerCount = enabled_layer_count,
+      .ppEnabledLayerNames = (const char *const *) device_validation_layers,
+      .extensionCount = enabled_extension_count,
+      .ppEnabledExtensionNames = (const char *const *) extension_names,
+      .pEnabledFeatures = NULL, // If specific features are required, pass them in here
+    };
+
+    err = vkCreateDevice (gpu, &device_info, &device->device);
+    if (gst_vulkan_error_to_g_error (err, error, "vkCreateDevice") < 0)
+      return FALSE;
+  }
+  {
+    const VkCmdPoolCreateInfo cmd_pool_info = {
+      .sType = VK_STRUCTURE_TYPE_CMD_POOL_CREATE_INFO,
+      .pNext = NULL,
+      .queueFamilyIndex = device->queue_family_id,
+      .flags = 0,
+    };
+    err =
+        vkCreateCommandPool (device->device, &cmd_pool_info, &device->cmd_pool);
+    if (gst_vulkan_error_to_g_error (err, error, "vkCreateCommandPool") < 0)
+      return FALSE;
+  }
+
+  return TRUE;
+}
+
+GstVulkanQueue *
+gst_vulkan_device_get_queue (GstVulkanDevice * device, guint32 queue_family,
+    guint32 queue_i, GError ** error)
+{
+  GstVulkanQueue *ret;
+  VkResult err;
+
+  g_return_val_if_fail (GST_IS_VULKAN_DEVICE (device), NULL);
+  g_return_val_if_fail (device->device != NULL, NULL);
+  g_return_val_if_fail (queue_family < device->n_queues, NULL);
+  g_return_val_if_fail (queue_i <
+      device->queue_family_props[queue_family].queueCount, NULL);
+
+  ret = g_object_new (GST_TYPE_VULKAN_QUEUE, NULL);
+  ret->device = gst_object_ref (device);
+  ret->family = queue_family;
+  ret->index = queue_i;
+
+  err = vkGetDeviceQueue (device->device, queue_family, queue_i, &ret->queue);
+  if (gst_vulkan_error_to_g_error (err, error, "vkGetDeviceQueue") < 0) {
+    gst_object_unref (ret);
+    return NULL;
+  }
+
+  return ret;
+}
+
+gpointer
+gst_vulkan_device_get_proc_address (GstVulkanDevice * device,
+    const gchar * name)
+{
+  g_return_val_if_fail (GST_IS_VULKAN_DEVICE (device), NULL);
+  g_return_val_if_fail (device->device != NULL, NULL);
+  g_return_val_if_fail (name != NULL, NULL);
+
+  GST_TRACE_OBJECT (device, "%s", name);
+
+  return vkGetDeviceProcAddr (device->device, name);
+}
+
+GstVulkanInstance *
+gst_vulkan_device_get_instance (GstVulkanDevice * device)
+{
+  g_return_val_if_fail (GST_IS_VULKAN_DEVICE (device), NULL);
+
+  return device->instance ? gst_object_ref (device->instance) : NULL;
+}
+
+VkPhysicalDevice
+gst_vulkan_device_get_physical_device (GstVulkanDevice * device)
+{
+  g_return_val_if_fail (GST_IS_VULKAN_DEVICE (device), NULL);
+
+  if (device->instance->physical_devices == NULL)
+    return NULL;
+  if (device->device_index >= device->instance->n_physical_devices)
+    return NULL;
+
+  return device->instance->physical_devices[device->device_index];
+}
+
+gboolean
+gst_vulkan_device_create_cmd_buffer (GstVulkanDevice * device,
+    VkCmdBuffer * cmd, GError ** error)
+{
+  VkResult err;
+
+  const VkCmdBufferCreateInfo cmd_info = {
+    .sType = VK_STRUCTURE_TYPE_CMD_BUFFER_CREATE_INFO,
+    .pNext = NULL,
+    .cmdPool = device->cmd_pool,
+    .level = VK_CMD_BUFFER_LEVEL_PRIMARY,
+    .flags = 0,
+  };
+
+  err = vkCreateCommandBuffer (device->device, &cmd_info, cmd);
+  if (gst_vulkan_error_to_g_error (err, error, "vkCreateCommandBuffer") < 0)
+    return FALSE;
+
+  GST_LOG_OBJECT (device, "created cmd buffer %p", cmd);
+
+  return TRUE;
+}
+
+void
+gst_vulkan_device_close (GstVulkanDevice * device)
+{
+  g_return_if_fail (GST_IS_VULKAN_DEVICE (device));
+  g_return_if_fail (device->device != NULL);
+
+  g_free (device->queue_family_props);
+}
diff --git a/ext/vulkan/vkdevice.h b/ext/vulkan/vkdevice.h
new file mode 100644 (file)
index 0000000..fa1591e
--- /dev/null
@@ -0,0 +1,82 @@
+/*
+ * GStreamer
+ * Copyright (C) 2015 Matthew Waters <matthew@centricular.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef _VK_DEVICE_H_
+#define _VK_DEVICE_H_
+
+#include <gst/gst.h>
+#include "vk.h"
+
+G_BEGIN_DECLS
+
+#define GST_TYPE_VULKAN_DEVICE         (gst_vulkan_device_get_type())
+#define GST_VULKAN_DEVICE(o)           (G_TYPE_CHECK_INSTANCE_CAST((o), GST_TYPE_VULKAN_DEVICE, GstVulkanDevice))
+#define GST_VULKAN_DEVICE_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST((k), GST_TYPE_VULKAN_DEVICE, GstVulkanDeviceClass))
+#define GST_IS_VULKAN_DEVICE(o)        (G_TYPE_CHECK_INSTANCE_TYPE((o), GST_TYPE_VULKAN_DEVICE))
+#define GST_IS_VULKAN_DEVICE_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE((k), GST_TYPE_VULKAN_DEVICE))
+#define GST_VULKAN_DEVICE_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS((o), GST_TYPE_VULKAN_DEVICE, GstVulkanDeviceClass))
+GType gst_vulkan_device_get_type       (void);
+
+struct _GstVulkanDevice
+{
+  GstObject parent;
+
+  GstVulkanInstance *instance;
+
+  guint device_index;
+  VkDevice device; /* hides a pointer */
+  VkPhysicalDeviceProperties gpu_props;
+  VkPhysicalDeviceFeatures gpu_features;
+  VkPhysicalDeviceMemoryProperties memory_properties;
+
+  VkQueueFamilyProperties *queue_family_props;
+  guint32 n_queue_families;
+
+  guint32 queue_family_id;
+  guint32 n_queues;
+
+  VkCmdPool cmd_pool;
+};
+
+struct _GstVulkanDeviceClass
+{
+  GstObjectClass parent_class;
+};
+
+GstVulkanDevice *   gst_vulkan_device_new                   (GstVulkanInstance * instance);
+GstVulkanInstance * gst_vulkan_device_get_instance           (GstVulkanDevice * device);
+gboolean            gst_vulkan_device_open                  (GstVulkanDevice * device,
+                                                             GError ** error);
+void                gst_vulkan_device_close                 (GstVulkanDevice * device);
+
+gpointer            gst_vulkan_device_get_proc_address      (GstVulkanDevice * device,
+                                                             const gchar * name);
+GstVulkanQueue *    gst_vulkan_device_get_queue             (GstVulkanDevice * device,
+                                                             guint32 queue_family,
+                                                             guint32 queue_i,
+                                                             GError ** error);
+VkPhysicalDevice    gst_vulkan_device_get_physical_device   (GstVulkanDevice * device);
+gboolean            gst_vulkan_device_create_cmd_buffer     (GstVulkanDevice * device,
+                                                             VkCmdBuffer * cmd,
+                                                             GError ** error);
+
+G_END_DECLS
+
+#endif /* _VK_DEVICE_H_ */
diff --git a/ext/vulkan/vkdisplay.c b/ext/vulkan/vkdisplay.c
new file mode 100644 (file)
index 0000000..aa8d729
--- /dev/null
@@ -0,0 +1,246 @@
+/*
+ * GStreamer
+ * Copyright (C) 2007 David A. Schleef <ds@schleef.org>
+ * Copyright (C) 2008 Julien Isorce <julien.isorce@gmail.com>
+ * Copyright (C) 2008 Filippo Argiolas <filippo.argiolas@gmail.com>
+ * Copyright (C) 2013 Matthew Waters <ystreet00@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "vkdisplay.h"
+
+#if GST_VULKAN_HAVE_WINDOW_X11
+#include "x11/vkdisplay_x11.h"
+#endif
+#if GST_VULKAN_HAVE_WINDOW_XCB
+#include "xcb/vkdisplay_xcb.h"
+#endif
+
+#define GST_CAT_DEFAULT gst_vulkan_display_debug
+GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
+
+static void
+_init_debug (void)
+{
+  static volatile gsize _init = 0;
+
+  if (g_once_init_enter (&_init)) {
+    GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "vulkandisplay", 0,
+        "Vulkan display");
+    g_once_init_leave (&_init, 1);
+  }
+}
+
+G_DEFINE_TYPE_WITH_CODE (GstVulkanDisplay, gst_vulkan_display, GST_TYPE_OBJECT,
+    _init_debug ());
+
+#define GST_VULKAN_DISPLAY_GET_PRIVATE(o) \
+  (G_TYPE_INSTANCE_GET_PRIVATE((o), GST_TYPE_VULKAN_DISPLAY, GstVulkanDisplayPrivate))
+
+enum
+{
+  SIGNAL_0,
+  CREATE_CONTEXT,
+  LAST_SIGNAL
+};
+
+/* static guint gst_vulkan_display_signals[LAST_SIGNAL] = { 0 }; */
+
+static void gst_vulkan_display_finalize (GObject * object);
+static gpointer gst_vulkan_display_default_get_handle (GstVulkanDisplay *
+    display);
+static gpointer gst_vulkan_display_default_get_platform_handle (GstVulkanDisplay
+    * display);
+static GstVulkanWindow
+    * gst_vulkan_display_default_create_window (GstVulkanDisplay * display);
+
+struct _GstVulkanDisplayPrivate
+{
+  gint dummy;
+};
+
+static void
+gst_vulkan_display_class_init (GstVulkanDisplayClass * klass)
+{
+  g_type_class_add_private (klass, sizeof (GstVulkanDisplayPrivate));
+
+  klass->get_handle = gst_vulkan_display_default_get_handle;
+  klass->get_platform_handle = gst_vulkan_display_default_get_platform_handle;
+  klass->create_window = gst_vulkan_display_default_create_window;
+
+  G_OBJECT_CLASS (klass)->finalize = gst_vulkan_display_finalize;
+}
+
+static void
+gst_vulkan_display_init (GstVulkanDisplay * display)
+{
+  display->priv = GST_VULKAN_DISPLAY_GET_PRIVATE (display);
+  display->type = GST_VULKAN_DISPLAY_TYPE_ANY;
+}
+
+static void
+gst_vulkan_display_finalize (GObject * object)
+{
+  G_OBJECT_CLASS (gst_vulkan_display_parent_class)->finalize (object);
+}
+
+/**
+ * gst_vulkan_display_new:
+ *
+ * Returns: (transfer full): a new #GstVulkanDisplay
+ *
+ * Since: 1.10
+ */
+GstVulkanDisplay *
+gst_vulkan_display_new (void)
+{
+  GstVulkanDisplay *display = NULL;
+  const gchar *user_choice, *platform_choice;
+
+  _init_debug ();
+
+  user_choice = g_getenv ("GST_GL_WINDOW");
+  platform_choice = g_getenv ("GST_GL_PLATFORM");
+  GST_INFO ("creating a display, user choice:%s (platform: %s)",
+      GST_STR_NULL (user_choice), GST_STR_NULL (platform_choice));
+
+#if GST_VULKAN_HAVE_WINDOW_XCB
+  if (!display && (!user_choice || g_strstr_len (user_choice, 3, "xcb")))
+    display = GST_VULKAN_DISPLAY (gst_vulkan_display_xcb_new (NULL));
+#endif
+#if GST_VULKAN_HAVE_WINDOW_X11
+  if (!display && (!user_choice || g_strstr_len (user_choice, 3, "x11")))
+    display = GST_VULKAN_DISPLAY (gst_vulkan_display_x11_new (NULL));
+#endif
+  if (!display) {
+    /* subclass returned a NULL window */
+    GST_WARNING ("Could not create display. user specified %s "
+        "(platform: %s), creating dummy",
+        GST_STR_NULL (user_choice), GST_STR_NULL (platform_choice));
+
+    return g_object_new (GST_TYPE_VULKAN_DISPLAY, NULL);
+  }
+
+  return display;
+}
+
+/**
+ * gst_vulkan_display_get_handle:
+ * @display: a #GstVulkanDisplay
+ *
+ * Returns: the winsys specific handle of @display
+ *
+ * Since: 1.10
+ */
+gpointer
+gst_vulkan_display_get_handle (GstVulkanDisplay * display)
+{
+  GstVulkanDisplayClass *klass;
+
+  g_return_val_if_fail (GST_IS_VULKAN_DISPLAY (display), NULL);
+  klass = GST_VULKAN_DISPLAY_GET_CLASS (display);
+  g_return_val_if_fail (klass->get_handle != NULL, NULL);
+
+  return klass->get_handle (display);
+}
+
+static gpointer
+gst_vulkan_display_default_get_handle (GstVulkanDisplay * display)
+{
+  return 0;
+}
+
+/**
+ * gst_vulkan_display_get_platform_handle:
+ * @display: a #GstVulkanDisplay
+ *
+ * Returns: the winsys specific handle of @display for use with the
+ * VK_EXT_KHR_swapchain extension.
+ *
+ * Since: 1.10
+ */
+gpointer
+gst_vulkan_display_get_platform_handle (GstVulkanDisplay * display)
+{
+  GstVulkanDisplayClass *klass;
+
+  g_return_val_if_fail (GST_IS_VULKAN_DISPLAY (display), NULL);
+  klass = GST_VULKAN_DISPLAY_GET_CLASS (display);
+  g_return_val_if_fail (klass->get_handle != NULL, NULL);
+
+  return klass->get_platform_handle (display);
+}
+
+static gpointer
+gst_vulkan_display_default_get_platform_handle (GstVulkanDisplay * display)
+{
+  return (gpointer) gst_vulkan_display_get_handle (display);
+}
+
+/**
+ * gst_vulkan_display_get_handle_type:
+ * @display: a #GstVulkanDisplay
+ *
+ * Returns: the #GstVulkanDisplayType of @display
+ *
+ * Since: 1.10
+ */
+GstVulkanDisplayType
+gst_vulkan_display_get_handle_type (GstVulkanDisplay * display)
+{
+  g_return_val_if_fail (GST_IS_VULKAN_DISPLAY (display),
+      GST_VULKAN_DISPLAY_TYPE_NONE);
+
+  return display->type;
+}
+
+/**
+ * gst_vulkan_display_create_window:
+ * @display: a #GstVulkanDisplay
+ *
+ * Returns: a new #GstVulkanWindow for @display or %NULL.
+ */
+GstVulkanWindow *
+gst_vulkan_display_create_window (GstVulkanDisplay * display)
+{
+  GstVulkanDisplayClass *klass;
+  GstVulkanWindow *window;
+
+  g_return_val_if_fail (GST_IS_VULKAN_DISPLAY (display), NULL);
+  klass = GST_VULKAN_DISPLAY_GET_CLASS (display);
+  g_return_val_if_fail (klass->create_window != NULL, NULL);
+
+  window = klass->create_window (display);
+
+  if (window) {
+    GST_OBJECT_LOCK (display);
+    display->windows = g_list_prepend (display->windows, window);
+    GST_OBJECT_UNLOCK (display);
+  }
+
+  return window;
+}
+
+static GstVulkanWindow *
+gst_vulkan_display_default_create_window (GstVulkanDisplay * display)
+{
+  return gst_vulkan_window_new (display);
+}
diff --git a/ext/vulkan/vkdisplay.h b/ext/vulkan/vkdisplay.h
new file mode 100644 (file)
index 0000000..371eec9
--- /dev/null
@@ -0,0 +1,91 @@
+/*
+ * GStreamer
+ * Copyright (C) 2007 David A. Schleef <ds@schleef.org>
+ * Copyright (C) 2008 Julien Isorce <julien.isorce@gmail.com>
+ * Copyright (C) 2008 Filippo Argiolas <filippo.argiolas@gmail.com>
+ * Copyright (C) 2013 Matthew Waters <ystreet00@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef __GST_VULKAN_DISPLAY_H__
+#define __GST_VULKAN_DISPLAY_H__
+
+#include <gst/gst.h>
+
+#include <vk.h>
+
+G_BEGIN_DECLS
+
+#define GST_TYPE_VULKAN_DISPLAY             (gst_vulkan_display_get_type())
+#define GST_VULKAN_DISPLAY(obj)             (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_VULKAN_DISPLAY,GstVulkanDisplay))
+#define GST_VULKAN_DISPLAY_CLASS(klass)     (G_TYPE_CHECK_CLASS_CAST((klass), GST_TYPE_VULKAN_DISPLAY,GstVulkanDisplayClass))
+#define GST_IS_VULKAN_DISPLAY(obj)          (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_VULKAN_DISPLAY))
+#define GST_IS_VULKAN_DISPLAY_CLASS(klass)  (G_TYPE_CHECK_CLASS_TYPE((klass), GST_TYPE_VULKAN_DISPLAY))
+#define GST_VULKAN_DISPLAY_CAST(obj)        ((GstVulkanDisplay*)(obj))
+#define GST_VULKAN_DISPLAY_GET_CLASS(o)     (G_TYPE_INSTANCE_GET_CLASS((o), GST_TYPE_VULKAN_DISPLAY, GstVulkanDisplayClass))
+GType gst_vulkan_display_get_type (void);
+
+typedef enum
+{
+  GST_VULKAN_DISPLAY_TYPE_NONE = 0,
+  GST_VULKAN_DISPLAY_TYPE_X11 = (1 << 0),
+  GST_VULKAN_DISPLAY_TYPE_XCB = (1 << 1),
+  GST_VULKAN_DISPLAY_TYPE_WAYLAND = (1 << 2),
+  GST_VULKAN_DISPLAY_TYPE_MIR = (1 << 3),
+  GST_VULKAN_DISPLAY_TYPE_WIN32 = (1 << 4),
+
+  GST_VULKAN_DISPLAY_TYPE_ANY = G_MAXUINT32
+} GstVulkanDisplayType;
+
+/**
+ * GstVulkanDisplay:
+ *
+ * The contents of a #GstVulkanDisplay are private and should only be accessed
+ * through the provided API
+ */
+struct _GstVulkanDisplay
+{
+  /* <private> */
+  GstObject                 object;
+
+  GstVulkanDisplayType      type;
+
+  /* <protected> */
+  GList                    *windows;        /* OBJECT lock */
+
+  GstVulkanDisplayPrivate  *priv;
+};
+
+struct _GstVulkanDisplayClass
+{
+  GstObjectClass object_class;
+
+  gpointer          (*get_handle)           (GstVulkanDisplay * display);
+  gpointer          (*get_platform_handle)  (GstVulkanDisplay * display); /* default chains up to @get_handle */
+  GstVulkanWindow * (*create_window)        (GstVulkanDisplay * display);
+};
+
+GstVulkanDisplay *gst_vulkan_display_new (void);
+
+gpointer             gst_vulkan_display_get_handle             (GstVulkanDisplay * display);
+GstVulkanDisplayType gst_vulkan_display_get_handle_type        (GstVulkanDisplay * display);
+gpointer             gst_vulkan_display_get_platform_handle    (GstVulkanDisplay * display);
+GstVulkanWindow *    gst_vulkan_display_create_window          (GstVulkanDisplay * display);
+
+G_END_DECLS
+
+#endif /* __GST_VULKAN_DISPLAY_H__ */
diff --git a/ext/vulkan/vkerror.c b/ext/vulkan/vkerror.c
new file mode 100644 (file)
index 0000000..fca2d6a
--- /dev/null
@@ -0,0 +1,129 @@
+/*
+ * GStreamer
+ * Copyright (C) 2015 Matthew Waters <matthew@centricular.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <glib/gprintf.h>
+
+#include "vkerror.h"
+
+/* *INDENT-OFF* */
+static const struct 
+{
+  VkResult result;
+  const char *str;
+} vk_result_string_map[] = {
+  {VK_ERROR_OUT_OF_HOST_MEMORY, "Out Of Host Memory"},
+  {VK_ERROR_OUT_OF_DEVICE_MEMORY, "Out of Device Memory"},
+  {VK_ERROR_INITIALIZATION_FAILED, "Initialization Failed"},
+  {VK_ERROR_DEVICE_LOST, "Device Lost"},
+  {VK_ERROR_MEMORY_MAP_FAILED, "Map Failed"},
+  {VK_ERROR_LAYER_NOT_PRESENT, "Layer Not Present"},
+  {VK_ERROR_EXTENSION_NOT_PRESENT, "Extension Not Present"},
+  {VK_ERROR_INCOMPATIBLE_DRIVER, "Incompatible Driver"},
+};
+
+static const struct
+{
+  VkResult result;
+  GstVulkanError gst_enum;
+} vk_result_gst_error_map[] = {
+  {VK_ERROR_OUT_OF_HOST_MEMORY, GST_VULKAN_ERROR_OUT_OF_HOST_MEMORY},
+  {VK_ERROR_OUT_OF_DEVICE_MEMORY, VK_ERROR_OUT_OF_DEVICE_MEMORY},
+  {VK_ERROR_INITIALIZATION_FAILED, GST_VULKAN_ERROR_INITIALIZATION_FAILED},
+  {VK_ERROR_DEVICE_LOST, GST_VULKAN_ERROR_DEVICE_LOST},
+  {VK_ERROR_MEMORY_MAP_FAILED, GST_VULKAN_ERROR_MEMORY_MAP_FAILED},
+  {VK_ERROR_LAYER_NOT_PRESENT, GST_VULKAN_ERROR_LAYER_NOT_PRESENT},
+  {VK_ERROR_EXTENSION_NOT_PRESENT, GST_VULKAN_ERROR_EXTENSION_NOT_PRESENT},
+  {VK_ERROR_INCOMPATIBLE_DRIVER, GST_VULKAN_ERROR_INCOMPATIBLE_DRIVER},
+};
+/* *INDENT-ON* */
+
+GQuark
+gst_vulkan_error_quark (void)
+{
+  return g_quark_from_static_string ("gst-vulkan-error");
+}
+
+static const char *
+_vk_result_to_string (VkResult result)
+{
+  int i;
+
+  if (result >= 0)
+    return NULL;
+  if (result < VK_RESULT_BEGIN_RANGE)
+    return "Unknown Error";
+
+  for (i = 0; i < G_N_ELEMENTS (vk_result_string_map); i++) {
+    if (result == vk_result_string_map[i].result)
+      return vk_result_string_map[i].str;
+  }
+
+  return "Unknown Error";
+}
+
+static GstVulkanError
+_vk_result_to_g_error_enum (VkResult result)
+{
+  int i;
+
+  if (result >= 0)
+    return 0;
+  if (result < VK_RESULT_BEGIN_RANGE)
+    return 0;
+
+  for (i = 0; i < G_N_ELEMENTS (vk_result_gst_error_map); i++) {
+    if (result == vk_result_gst_error_map[i].result)
+      return vk_result_gst_error_map[i].gst_enum;
+  }
+
+  return GST_VULKAN_ERROR_FAILED;
+}
+
+VkResult
+gst_vulkan_error_to_g_error (VkResult result, GError ** error,
+    const char *format, ...)
+{
+  GstVulkanError gst_enum;
+  const char *result_str;
+  gchar *string;
+  va_list args;
+
+  if (error == NULL)
+    /* we don't have an error to set */
+    return result;
+
+  result_str = _vk_result_to_string (result);
+  if (result_str == NULL)
+    return result;
+
+  gst_enum = _vk_result_to_g_error_enum (result);
+
+  va_start (args, format);
+  g_vasprintf (&string, format, args);
+  va_end (args);
+
+  g_set_error (error, GST_VULKAN_ERROR, gst_enum, "%s: %s", result_str, string);
+
+  return result;
+}
diff --git a/ext/vulkan/vkerror.h b/ext/vulkan/vkerror.h
new file mode 100644 (file)
index 0000000..6f5e4cc
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * GStreamer
+ * Copyright (C) 2015 Matthew Waters <matthew@centricular.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef _VK_ERROR_H_
+#define _VK_ERROR_H_
+
+#include <gst/gst.h>
+#include <vulkan/vulkan.h>
+
+G_BEGIN_DECLS
+
+#define GST_VULKAN_ERROR (gst_vulkan_error_quark ())
+GQuark gst_vulkan_error_quark (void);
+
+typedef enum
+{
+  GST_VULKAN_ERROR_FAILED = 0,
+  GST_VULKAN_ERROR_OUT_OF_HOST_MEMORY = -1,
+  GST_VULKAN_ERROR_OUT_OF_DEVICE_MEMORY = -2,
+  GST_VULKAN_ERROR_INITIALIZATION_FAILED = -3,
+  GST_VULKAN_ERROR_DEVICE_LOST = -4,
+  GST_VULKAN_ERROR_MEMORY_MAP_FAILED = -5,
+  GST_VULKAN_ERROR_LAYER_NOT_PRESENT = -6,
+  GST_VULKAN_ERROR_EXTENSION_NOT_PRESENT = -7,
+  GST_VULKAN_ERROR_INCOMPATIBLE_DRIVER = -8,
+} GstVulkanError;
+
+/* only fills error iff error != NULL and result < 0 */
+VkResult gst_vulkan_error_to_g_error (VkResult result, GError ** error, const char * format, ...) G_GNUC_PRINTF (3, 4);
+
+G_END_DECLS
+
+#endif /* _VK_INSTANCE_H_ */
diff --git a/ext/vulkan/vkimagememory.c b/ext/vulkan/vkimagememory.c
new file mode 100644 (file)
index 0000000..e5edf90
--- /dev/null
@@ -0,0 +1,578 @@
+/*
+ * GStreamer
+ * Copyright (C) 2015 Matthew Waters <matthew@centricular.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "vkimagememory.h"
+
+/**
+ * SECTION:vkimagememory
+ * @short_description: memory subclass for Vulkan image memory
+ * @see_also: #GstMemory, #GstAllocator
+ *
+ * GstVulkanImageMemory is a #GstMemory subclass providing support for the
+ * mapping of Vulkan device memory.
+ */
+
+#define GST_CAT_DEFUALT GST_CAT_VULKAN_IMAGE_MEMORY
+GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFUALT);
+
+static GstAllocator *_vulkan_image_memory_allocator;
+
+VkFormat
+gst_vulkan_format_from_video_format (GstVideoFormat v_format, guint plane)
+{
+  guint n_plane_components;
+
+  switch (v_format) {
+    case GST_VIDEO_FORMAT_RGBx:
+    case GST_VIDEO_FORMAT_BGRx:
+    case GST_VIDEO_FORMAT_xRGB:
+    case GST_VIDEO_FORMAT_xBGR:
+    case GST_VIDEO_FORMAT_RGBA:
+    case GST_VIDEO_FORMAT_BGRA:
+    case GST_VIDEO_FORMAT_ARGB:
+    case GST_VIDEO_FORMAT_ABGR:
+    case GST_VIDEO_FORMAT_AYUV:
+      n_plane_components = 4;
+      break;
+    case GST_VIDEO_FORMAT_RGB:
+    case GST_VIDEO_FORMAT_BGR:
+      n_plane_components = 3;
+      break;
+    case GST_VIDEO_FORMAT_RGB16:
+    case GST_VIDEO_FORMAT_BGR16:
+      return GST_VIDEO_GL_TEXTURE_TYPE_RGB16;
+    case GST_VIDEO_FORMAT_GRAY16_BE:
+    case GST_VIDEO_FORMAT_GRAY16_LE:
+    case GST_VIDEO_FORMAT_YUY2:
+    case GST_VIDEO_FORMAT_UYVY:
+      n_plane_components = 2;
+      break;
+    case GST_VIDEO_FORMAT_NV12:
+    case GST_VIDEO_FORMAT_NV21:
+      n_plane_components = plane == 0 ? 1 : 2;
+      break;
+    case GST_VIDEO_FORMAT_GRAY8:
+    case GST_VIDEO_FORMAT_Y444:
+    case GST_VIDEO_FORMAT_Y42B:
+    case GST_VIDEO_FORMAT_Y41B:
+    case GST_VIDEO_FORMAT_I420:
+    case GST_VIDEO_FORMAT_YV12:
+      n_plane_components = 1;
+      break;
+    default:
+      n_plane_components = 4;
+      g_assert_not_reached ();
+      break;
+  }
+
+  switch (n_plane_components) {
+    case 4:
+      return VK_FORMAT_R8G8B8A8_UNORM;
+    case 3:
+      return VK_FORMAT_R8G8B8_UNORM;
+    case 2:
+      return VK_FORMAT_R8G8_UNORM;
+    case 1:
+      return VK_FORMAT_R8_UNORM;
+    default:
+      g_assert_not_reached ();
+      return VK_FORMAT_R8G8B8A8_UNORM;
+  }
+}
+
+static void
+_view_create_info (VkImage image, VkFormat format, VkImageViewCreateInfo * info)
+{
+  info->sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
+  info->pNext = NULL;
+  info->image = image;
+  info->format = format;
+  info->channels.r = VK_CHANNEL_SWIZZLE_R;
+  info->channels.g = VK_CHANNEL_SWIZZLE_G;
+  info->channels.b = VK_CHANNEL_SWIZZLE_B;
+  info->channels.a = VK_CHANNEL_SWIZZLE_A;
+  info->subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
+  info->subresourceRange.baseMipLevel = 0;
+  info->subresourceRange.mipLevels = 1;
+  info->subresourceRange.baseArrayLayer = 0;
+  info->subresourceRange.arraySize = 1;
+  info->viewType = VK_IMAGE_VIEW_TYPE_2D;
+  info->flags = 0;
+}
+
+static gboolean
+_find_memory_type_index_with_type_properties (GstVulkanDevice * device,
+    guint32 typeBits, VkFlags properties, guint32 * typeIndex)
+{
+  guint32 i;
+
+  /* Search memtypes to find first index with those properties */
+  for (i = 0; i < 32; i++) {
+    if ((typeBits & 1) == 1) {
+      /* Type is available, does it match user properties? */
+      if ((device->memory_properties.memoryTypes[i].
+              propertyFlags & properties) == properties) {
+        *typeIndex = i;
+        return TRUE;
+      }
+    }
+    typeBits >>= 1;
+  }
+
+  return FALSE;
+}
+
+static gboolean
+_create_info_from_args (VkImageCreateInfo * info, VkFormat format, gsize width,
+    gsize height, VkImageTiling tiling, VkImageUsageFlags usage)
+{
+  /* FIXME: validate these */
+
+  info->sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
+  info->pNext = NULL;
+  info->imageType = VK_IMAGE_TYPE_2D;
+  info->format = format;
+  info->extent.width = (gint32) width;
+  info->extent.height = (gint32) height;
+  info->extent.depth = 1;
+  info->mipLevels = 1;
+  info->arraySize = 1;
+  info->samples = 1;
+  info->tiling = tiling;
+  info->usage = usage;
+  info->sharingMode = VK_SHARING_MODE_EXCLUSIVE;
+  info->queueFamilyCount = 0;
+  info->pQueueFamilyIndices = NULL;
+  info->initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
+
+  return TRUE;
+}
+
+static void
+_vk_image_mem_init (GstVulkanImageMemory * mem, GstAllocator * allocator,
+    GstMemory * parent, GstVulkanDevice * device, GstAllocationParams * params,
+    gsize size, gpointer user_data, GDestroyNotify notify)
+{
+  gsize align = gst_memory_alignment, offset = 0, maxsize = size;
+  GstMemoryFlags flags = 0;
+
+  if (params) {
+    flags = params->flags;
+    align |= params->align;
+    offset = params->prefix;
+    maxsize += params->prefix + params->padding + align;
+  }
+
+  gst_memory_init (GST_MEMORY_CAST (mem), flags, allocator, parent, maxsize,
+      align, offset, size);
+
+  mem->device = gst_object_ref (device);
+  mem->image_layout = VK_IMAGE_LAYOUT_UNDEFINED;
+  mem->wrapped = FALSE;
+  mem->notify = notify;
+  mem->user_data = user_data;
+
+  g_mutex_init (&mem->lock);
+
+  GST_CAT_DEBUG (GST_CAT_VULKAN_IMAGE_MEMORY, "new GL buffer memory:%p size:%"
+      G_GSIZE_FORMAT, mem, maxsize);
+}
+
+static GstVulkanImageMemory *
+_vk_image_mem_new_alloc (GstAllocator * allocator, GstMemory * parent,
+    GstVulkanDevice * device, VkFormat format, gsize width, gsize height,
+    VkImageTiling tiling, VkImageUsageFlags usage,
+    VkMemoryPropertyFlags mem_prop_flags, gpointer user_data,
+    GDestroyNotify notify)
+{
+  GstVulkanImageMemory *mem = g_new0 (GstVulkanImageMemory, 1);
+  GstAllocationParams params = { 0, };
+  VkImageViewCreateInfo view_info;
+  VkImageCreateInfo image_info;
+  guint32 memory_type_index;
+  VkPhysicalDevice gpu;
+  GError *error = NULL;
+  VkImage image;
+  VkResult err;
+
+  gpu = gst_vulkan_device_get_physical_device (device);
+  if (!_create_info_from_args (&image_info, format, width, height, tiling,
+          usage)) {
+    GST_CAT_ERROR (GST_CAT_VULKAN_IMAGE_MEMORY, "Incorrect image parameters");
+    goto error;
+  }
+
+  err = vkCreateImage (device->device, &image_info, &image);
+  if (gst_vulkan_error_to_g_error (err, &error, "vkCreateImage") < 0)
+    goto vk_error;
+
+  err =
+      vkGetImageMemoryRequirements (device->device, image, &mem->requirements);
+  if (gst_vulkan_error_to_g_error (err, &error,
+          "vkGetImageMemoryRequirements") < 0) {
+    vkDestroyImage (device->device, image);
+    goto vk_error;
+  }
+
+  params.align = mem->requirements.alignment;
+  _vk_image_mem_init (mem, allocator, parent, device, &params,
+      mem->requirements.size, user_data, notify);
+  mem->create_info = image_info;
+  mem->image = image;
+
+  err = vkGetPhysicalDeviceImageFormatProperties (gpu, format, VK_IMAGE_TYPE_2D,
+      tiling, usage, 0, &mem->format_properties);
+  if (gst_vulkan_error_to_g_error (err, &error,
+          "vkGetPhysicalDeviceImageFormatProperties") < 0)
+    goto vk_error;
+
+  if (!_find_memory_type_index_with_type_properties (device,
+          mem->requirements.memoryTypeBits, mem_prop_flags,
+          &memory_type_index)) {
+    GST_CAT_ERROR (GST_CAT_VULKAN_IMAGE_MEMORY,
+        "Could not find suitable memory type");
+    goto error;
+  }
+
+  mem->vk_mem = (GstVulkanMemory *)
+      gst_vulkan_memory_alloc (device, memory_type_index, &params,
+      mem->requirements.size, mem_prop_flags);
+  if (!mem->vk_mem) {
+    GST_CAT_ERROR (GST_CAT_VULKAN_IMAGE_MEMORY,
+        "Failed to allocate device memory");
+    goto error;
+  }
+
+  err =
+      vkBindImageMemory (device->device, mem->image, mem->vk_mem->mem_ptr,
+      0 /* offset */ );
+  if (gst_vulkan_error_to_g_error (err, &error, "vkBindImageMemory") < 0)
+    goto vk_error;
+
+  if (usage & (VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_STORAGE_BIT |
+          VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT)) {
+    _view_create_info (mem->image, format, &view_info);
+    err = vkCreateImageView (device->device, &view_info, &mem->view);
+    if (gst_vulkan_error_to_g_error (err, &error, "vkCreateImageView") < 0)
+      goto vk_error;
+  }
+
+  return mem;
+
+vk_error:
+  {
+    GST_CAT_ERROR (GST_CAT_VULKAN_IMAGE_MEMORY,
+        "Failed to allocate image memory %s", error->message);
+    g_clear_error (&error);
+    goto error;
+  }
+
+error:
+  {
+    gst_memory_unref ((GstMemory *) mem);
+    return NULL;
+  }
+}
+
+static GstVulkanImageMemory *
+_vk_image_mem_new_wrapped (GstAllocator * allocator, GstMemory * parent,
+    GstVulkanDevice * device, VkImage image, VkFormat format, gsize width,
+    gsize height, VkImageTiling tiling, VkImageUsageFlags usage,
+    gpointer user_data, GDestroyNotify notify)
+{
+  GstVulkanImageMemory *mem = g_new0 (GstVulkanImageMemory, 1);
+  GstAllocationParams params = { 0, };
+  VkImageViewCreateInfo view_info;
+  VkPhysicalDevice gpu;
+  GError *error = NULL;
+  VkResult err;
+
+  gpu = gst_vulkan_device_get_physical_device (device);
+  mem->image = image;
+
+  err =
+      vkGetImageMemoryRequirements (device->device, mem->image,
+      &mem->requirements);
+  if (gst_vulkan_error_to_g_error (err, &error,
+          "vkGetImageMemoryRequirements") < 0) {
+    vkDestroyImage (device->device, image);
+    goto vk_error;
+  }
+
+  params.flags = GST_MEMORY_FLAG_NOT_MAPPABLE | GST_MEMORY_FLAG_READONLY;
+  _vk_image_mem_init (mem, allocator, parent, device, &params,
+      mem->requirements.size, user_data, notify);
+  mem->wrapped = TRUE;
+
+  if (!_create_info_from_args (&mem->create_info, format, width, height, tiling,
+          usage)) {
+    GST_CAT_ERROR (GST_CAT_VULKAN_IMAGE_MEMORY, "Incorrect image parameters");
+    goto error;
+  }
+
+  err = vkGetPhysicalDeviceImageFormatProperties (gpu, format, VK_IMAGE_TYPE_2D,
+      tiling, usage, 0, &mem->format_properties);
+  if (gst_vulkan_error_to_g_error (err, &error,
+          "vkGetPhysicalDeviceImageFormatProperties") < 0)
+    goto vk_error;
+
+  if (usage & (VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_STORAGE_BIT |
+          VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT)) {
+    _view_create_info (mem->image, format, &view_info);
+    err = vkCreateImageView (device->device, &view_info, &mem->view);
+    if (gst_vulkan_error_to_g_error (err, &error, "vkCreateImageView") < 0)
+      goto vk_error;
+  }
+
+  return mem;
+
+vk_error:
+  {
+    GST_CAT_ERROR (GST_CAT_VULKAN_IMAGE_MEMORY,
+        "Failed to allocate image memory %s", error->message);
+    g_clear_error (&error);
+    goto error;
+  }
+
+error:
+  {
+    gst_memory_unref ((GstMemory *) mem);
+    return NULL;
+  }
+}
+
+static gpointer
+_vk_image_mem_map_full (GstVulkanImageMemory * mem, GstMapInfo * info,
+    gsize size)
+{
+  GstMapInfo *vk_map_info = g_new0 (GstMapInfo, 1);
+  info->user_data[0] = vk_map_info;
+
+  /* FIXME: possible layout transformation needed */
+
+  if (!mem->vk_mem)
+    return NULL;
+
+  if (!gst_memory_map ((GstMemory *) mem->vk_mem, vk_map_info, info->flags))
+    return NULL;
+
+  return vk_map_info->data;
+}
+
+static void
+_vk_image_mem_unmap_full (GstVulkanImageMemory * mem, GstMapInfo * info)
+{
+  gst_memory_unmap ((GstMemory *) mem->vk_mem, info->user_data[0]);
+
+  g_free (info->user_data[0]);
+}
+
+static GstMemory *
+_vk_image_mem_copy (GstVulkanImageMemory * src, gssize offset, gssize size)
+{
+  return NULL;
+}
+
+static GstMemory *
+_vk_image_mem_share (GstVulkanImageMemory * mem, gssize offset, gssize size)
+{
+  return NULL;
+}
+
+static gboolean
+_vk_image_mem_is_span (GstVulkanImageMemory * mem1, GstVulkanImageMemory * mem2,
+    gsize * offset)
+{
+  return FALSE;
+}
+
+static GstMemory *
+_vk_image_mem_alloc (GstAllocator * allocator, gsize size,
+    GstAllocationParams * params)
+{
+  g_critical ("Subclass should override GstAllocatorClass::alloc() function");
+
+  return NULL;
+}
+
+static void
+_vk_image_mem_free (GstAllocator * allocator, GstMemory * memory)
+{
+  GstVulkanImageMemory *mem = (GstVulkanImageMemory *) memory;
+
+  GST_CAT_TRACE (GST_CAT_VULKAN_IMAGE_MEMORY, "freeing image memory:%p "
+      "id:%" G_GUINT64_FORMAT, mem, mem->image.handle);
+
+  if (mem->image.handle && !mem->wrapped)
+    vkDestroyImage (mem->device->device, mem->image);
+
+  if (mem->view.handle)
+    vkDestroyImageView (mem->device->device, mem->view);
+
+  if (mem->vk_mem)
+    gst_memory_unref ((GstMemory *) mem->vk_mem);
+
+  if (mem->notify)
+    mem->notify (mem->user_data);
+
+  gst_object_unref (mem->device);
+}
+
+gboolean
+gst_vulkan_image_memory_set_layout (GstVulkanImageMemory * vk_mem,
+    VkImageLayout image_layout, VkImageMemoryBarrier * barrier)
+{
+  /* validate vk_mem->usage with image_layout */
+
+  barrier->sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
+  barrier->pNext = NULL;
+  barrier->outputMask = 0;
+  barrier->inputMask = 0;
+  barrier->oldLayout = vk_mem->image_layout;
+  barrier->newLayout = image_layout;
+  barrier->image = vk_mem->image;
+  barrier->subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR;
+  barrier->subresourceRange.baseMipLevel = 0;
+  barrier->subresourceRange.mipLevels = 1;
+  barrier->subresourceRange.baseArrayLayer = 0;
+  barrier->subresourceRange.arraySize = 0;
+
+  if (image_layout == VK_IMAGE_LAYOUT_TRANSFER_DESTINATION_OPTIMAL) {
+    /* Make sure anything that was copying from this image has completed */
+    barrier->inputMask = VK_MEMORY_INPUT_TRANSFER_BIT;
+  }
+
+  if (image_layout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) {
+    /* Make sure any Copy or CPU writes to image are flushed */
+    barrier->outputMask =
+        VK_MEMORY_OUTPUT_HOST_WRITE_BIT | VK_MEMORY_OUTPUT_TRANSFER_BIT;
+  }
+
+  /* FIXME: what if the barrier is never submitted or is submitted out of order? */
+  vk_mem->image_layout = image_layout;
+
+  return TRUE;
+}
+
+/**
+ * gst_vulkan_image_memory_alloc:
+ * @device:a #GstVulkanDevice
+ * @memory_type_index: the Vulkan memory type index
+ * @params: a #GstAllocationParams
+ * @size: the size to allocate
+ *
+ * Allocated a new #GstVulkanImageMemory.
+ *
+ * Returns: a #GstMemory object backed by a vulkan device memory
+ */
+GstMemory *
+gst_vulkan_image_memory_alloc (GstVulkanDevice * device, VkFormat format,
+    gsize width, gsize height, VkImageTiling tiling, VkImageUsageFlags usage,
+    VkMemoryPropertyFlags mem_prop_flags)
+{
+  GstVulkanImageMemory *mem;
+
+  mem = _vk_image_mem_new_alloc (_vulkan_image_memory_allocator, NULL, device,
+      format, width, height, tiling, usage, mem_prop_flags, NULL, NULL);
+
+  return (GstMemory *) mem;
+}
+
+GstMemory *
+gst_vulkan_image_memory_wrapped (GstVulkanDevice * device, VkImage image,
+    VkFormat format, gsize width, gsize height, VkImageTiling tiling,
+    VkImageUsageFlags usage, gpointer user_data, GDestroyNotify notify)
+{
+  GstVulkanImageMemory *mem;
+
+  mem = _vk_image_mem_new_wrapped (_vulkan_image_memory_allocator, NULL, device,
+      image, format, width, height, tiling, usage, user_data, notify);
+
+  return (GstMemory *) mem;
+}
+
+G_DEFINE_TYPE (GstVulkanImageMemoryAllocator, gst_vulkan_image_memory_allocator,
+    GST_TYPE_ALLOCATOR);
+
+static void
+gst_vulkan_image_memory_allocator_class_init (GstVulkanImageMemoryAllocatorClass
+    * klass)
+{
+  GstAllocatorClass *allocator_class = (GstAllocatorClass *) klass;
+
+  allocator_class->alloc = _vk_image_mem_alloc;
+  allocator_class->free = _vk_image_mem_free;
+}
+
+static void
+gst_vulkan_image_memory_allocator_init (GstVulkanImageMemoryAllocator *
+    allocator)
+{
+  GstAllocator *alloc = GST_ALLOCATOR_CAST (allocator);
+
+  alloc->mem_type = GST_VULKAN_IMAGE_MEMORY_ALLOCATOR_NAME;
+  alloc->mem_map_full = (GstMemoryMapFullFunction) _vk_image_mem_map_full;
+  alloc->mem_unmap_full = (GstMemoryUnmapFullFunction) _vk_image_mem_unmap_full;
+  alloc->mem_copy = (GstMemoryCopyFunction) _vk_image_mem_copy;
+  alloc->mem_share = (GstMemoryShareFunction) _vk_image_mem_share;
+  alloc->mem_is_span = (GstMemoryIsSpanFunction) _vk_image_mem_is_span;
+}
+
+/**
+ * gst_vulkan_image_memory_init_once:
+ *
+ * Initializes the Vulkan memory allocator. It is safe to call this function
+ * multiple times.  This must be called before any other #GstVulkanImageMemory operation.
+ */
+void
+gst_vulkan_image_memory_init_once (void)
+{
+  static volatile gsize _init = 0;
+
+  if (g_once_init_enter (&_init)) {
+    GST_DEBUG_CATEGORY_INIT (GST_CAT_VULKAN_IMAGE_MEMORY, "vulkanimagememory",
+        0, "Vulkan Image Memory");
+
+    _vulkan_image_memory_allocator =
+        g_object_new (gst_vulkan_image_memory_allocator_get_type (), NULL);
+
+    gst_allocator_register (GST_VULKAN_IMAGE_MEMORY_ALLOCATOR_NAME,
+        gst_object_ref (_vulkan_image_memory_allocator));
+    g_once_init_leave (&_init, 1);
+  }
+}
+
+/**
+ * gst_is_vulkan_image_memory:
+ * @mem:a #GstMemory
+ * 
+ * Returns: whether the memory at @mem is a #GstVulkanImageMemory
+ */
+gboolean
+gst_is_vulkan_image_memory (GstMemory * mem)
+{
+  return mem != NULL && mem->allocator != NULL &&
+      g_type_is_a (G_OBJECT_TYPE (mem->allocator),
+      GST_TYPE_VULKAN_IMAGE_MEMORY_ALLOCATOR);
+}
diff --git a/ext/vulkan/vkimagememory.h b/ext/vulkan/vkimagememory.h
new file mode 100644 (file)
index 0000000..077c30f
--- /dev/null
@@ -0,0 +1,116 @@
+/*
+ * GStreamer
+ * Copyright (C) 2015 Matthew Waters <matthew@centricular.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef _VK_IMAGE_MEMORY_H_
+#define _VK_IMAGE_MEMORY_H_
+
+#include <gst/gst.h>
+#include <gst/gstallocator.h>
+#include <gst/gstmemory.h>
+
+#include <gst/video/video.h>
+
+#include <vk.h>
+
+G_BEGIN_DECLS
+
+#define GST_TYPE_VULKAN_IMAGE_MEMORY_ALLOCATOR (gst_vulkan_image_memory_allocator_get_type())
+GType gst_vulkan_image_memory_allocator_get_type(void);
+
+#define GST_IS_VULKAN_IMAGE_MEMORY_ALLOCATOR(obj)              (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_VULKAN_IMAGE_MEMORY_ALLOCATOR))
+#define GST_IS_VULKAN_IMAGE_MEMORY_ALLOCATOR_CLASS(klass)      (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_VULKAN_IMAGE_MEMORY_ALLOCATOR))
+#define GST_VULKAN_IMAGE_MEMORY_ALLOCATOR_GET_CLASS(obj)       (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_VULKAN_MEMORY_ALLOCATOR, GstVulkanImageMemoryAllocatorClass))
+#define GST_VULKAN_IMAGE_MEMORY_ALLOCATOR(obj)                 (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_VULKAN_MEMORY_ALLOCATOR, GstVulkanImageMemoryAllocator))
+#define GST_VULKAN_IMAGE_MEMORY_ALLOCATOR_CLASS(klass)         (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_VULKAN_MEMORY_ALLOCATOR, GstVulkanImageMemoryAllocatorClass))
+#define GST_VULKAN_IMAGE_MEMORY_ALLOCATOR_CAST(obj)            ((GstVulkanImageMemoryAllocator *)(obj))
+
+#define GST_VULKAN_IMAGE_MEMORY_ALLOCATOR_NAME "VulkanImage"
+
+struct _GstVulkanImageMemory
+{
+  GstMemory parent;
+
+  GstVulkanDevice * device;
+
+  VkImage image;
+  VkImageLayout image_layout;
+  VkImageView view;
+  GstVulkanMemory *vk_mem;
+
+  VkImageCreateInfo create_info;
+  VkMemoryRequirements requirements;
+  VkImageFormatProperties format_properties;
+
+  GMutex lock;
+  gboolean wrapped;
+  GDestroyNotify notify;
+  gpointer user_data;
+};
+
+/**
+ * GstVulkanImageMemoryAllocator
+ *
+ * Opaque #GstVulkanImageMemoryAllocator struct
+ */
+struct _GstVulkanImageMemoryAllocator
+{
+  GstAllocator parent;
+};
+
+/**
+ * GstVulkanImageMemoryAllocatorClass:
+ *
+ * The #GstVulkanImageMemoryAllocatorClass only contains private data
+ */
+struct _GstVulkanImageMemoryAllocatorClass
+{
+  GstAllocatorClass parent_class;
+};
+
+void            gst_vulkan_image_memory_init_once       (void);
+gboolean        gst_is_vulkan_image_memory              (GstMemory * mem);
+
+GstMemory *     gst_vulkan_image_memory_alloc           (GstVulkanDevice * device,
+                                                         VkFormat format,
+                                                         gsize width,
+                                                         gsize height,
+                                                         VkImageTiling tiling,
+                                                         VkImageUsageFlags usage,
+                                                         VkMemoryPropertyFlags mem_prop_flags);
+
+GstMemory *     gst_vulkan_image_memory_wrapped         (GstVulkanDevice * device,
+                                                         VkImage image,
+                                                         VkFormat format,
+                                                         gsize width,
+                                                         gsize height,
+                                                         VkImageTiling tiling,
+                                                         VkImageUsageFlags usage,
+                                                         gpointer user_data,
+                                                         GDestroyNotify notify);
+gboolean        gst_vulkan_image_memory_set_layout      (GstVulkanImageMemory * vk_mem,
+                                                         VkImageLayout,
+                                                         VkImageMemoryBarrier * barrier);
+
+VkFormat gst_vulkan_format_from_video_format (GstVideoFormat v_format,
+                                              guint plane);
+
+G_END_DECLS
+
+#endif /* _VK_IMAGE_MEMORY_H_ */
diff --git a/ext/vulkan/vkinstance.c b/ext/vulkan/vkinstance.c
new file mode 100644 (file)
index 0000000..900429d
--- /dev/null
@@ -0,0 +1,315 @@
+/*
+ * GStreamer
+ * Copyright (C) 2015 Matthew Waters <matthew@centricular.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "vkinstance.h"
+#include "vkutils.h"
+
+#include <string.h>
+
+#define APP_SHORT_NAME "GStreamer"
+
+static const char *instance_validation_layers[] = {
+  /* FIXME: until the loader stops segfaulting/hanging we don't have any
+   * validation layers for the instance */
+  "Threading",
+  "MemTracker",
+  "ObjectTracker",
+  "DrawState",
+  "ParamChecker",
+  "ShaderChecker",
+  "Swapchain",
+  "DeviceLimits",
+  "Image",
+};
+
+#define GST_CAT_DEFAULT gst_vulkan_instance_debug
+GST_DEBUG_CATEGORY (GST_CAT_DEFAULT);
+GST_DEBUG_CATEGORY (GST_VULKAN_DEBUG_CAT);
+
+#define gst_vulkan_instance_parent_class parent_class
+G_DEFINE_TYPE_WITH_CODE (GstVulkanInstance, gst_vulkan_instance,
+    GST_TYPE_OBJECT, GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT,
+        "vulkaninstance", 0, "Vulkan Instance");
+    GST_DEBUG_CATEGORY_INIT (GST_VULKAN_DEBUG_CAT,
+        "vulkandebug", 0, "Vulkan Debug"));
+
+static void gst_vulkan_instance_finalize (GObject * object);
+
+GstVulkanInstance *
+gst_vulkan_instance_new (void)
+{
+  return g_object_new (GST_TYPE_VULKAN_INSTANCE, NULL);
+}
+
+static void
+gst_vulkan_instance_init (GstVulkanInstance * instance)
+{
+}
+
+static void
+gst_vulkan_instance_class_init (GstVulkanInstanceClass * instance_class)
+{
+  gst_vulkan_memory_init_once ();
+  gst_vulkan_image_memory_init_once ();
+
+  G_OBJECT_CLASS (instance_class)->finalize = gst_vulkan_instance_finalize;
+}
+
+static void
+gst_vulkan_instance_finalize (GObject * object)
+{
+  GstVulkanInstance *instance = GST_VULKAN_INSTANCE (object);
+
+  if (instance->instance)
+    vkDestroyInstance (instance->instance);
+  instance->instance = NULL;
+}
+
+static VkBool32
+_gst_vk_debug_callback (VkFlags msgFlags, VkDbgObjectType objType,
+    uint64_t srcObject, size_t location, int32_t msgCode,
+    const char *pLayerPrefix, const char *pMsg, void *pUserData)
+{
+  if (msgFlags & VK_DBG_REPORT_ERROR_BIT) {
+    GST_CAT_ERROR (GST_VULKAN_DEBUG_CAT, "[%s] Code %d : %s", pLayerPrefix,
+        msgCode, pMsg);
+  } else if (msgFlags & VK_DBG_REPORT_WARN_BIT) {
+    GST_CAT_WARNING (GST_VULKAN_DEBUG_CAT, "[%s] Code %d : %s", pLayerPrefix,
+        msgCode, pMsg);
+  } else if (msgFlags & VK_DBG_REPORT_INFO_BIT) {
+    GST_CAT_LOG (GST_VULKAN_DEBUG_CAT, "[%s] Code %d : %s", pLayerPrefix,
+        msgCode, pMsg);
+  } else if (msgFlags & VK_DBG_REPORT_PERF_WARN_BIT) {
+    GST_CAT_FIXME (GST_VULKAN_DEBUG_CAT, "[%s] Code %d : %s", pLayerPrefix,
+        msgCode, pMsg);
+  } else if (msgFlags & VK_DBG_REPORT_DEBUG_BIT) {
+    GST_CAT_TRACE (GST_VULKAN_DEBUG_CAT, "[%s] Code %d : %s", pLayerPrefix,
+        msgCode, pMsg);
+  } else {
+    return FALSE;
+  }
+
+  /*
+   * false indicates that layer should not bail-out of an
+   * API call that had validation failures. This may mean that the
+   * app dies inside the driver due to invalid parameter(s).
+   * That's what would happen without validation layers, so we'll
+   * keep that behavior here.
+   */
+  return FALSE;
+}
+
+gboolean
+gst_vulkan_instance_open (GstVulkanInstance * instance, GError ** error)
+{
+  VkExtensionProperties *instance_extensions;
+  char *extension_names[64];    /* FIXME: make dynamic */
+  VkLayerProperties *instance_layers;
+  uint32_t instance_extension_count = 0;
+  uint32_t enabled_extension_count = 0;
+  uint32_t instance_layer_count = 0;
+  uint32_t enabled_layer_count = 0;
+  gboolean validation_found;
+  gboolean have_swapchain_ext = FALSE;
+  VkResult err;
+
+  /* Look for validation layers */
+  err = vkEnumerateInstanceLayerProperties (&instance_layer_count, NULL);
+  if (gst_vulkan_error_to_g_error (err, error,
+          "vKEnumerateInstanceLayerProperties") < 0)
+    return FALSE;
+
+  instance_layers = g_new0 (VkLayerProperties, instance_layer_count);
+  err =
+      vkEnumerateInstanceLayerProperties (&instance_layer_count,
+      instance_layers);
+  if (gst_vulkan_error_to_g_error (err, error,
+          "vKEnumerateInstanceLayerProperties") < 0) {
+    g_free (instance_layers);
+    return FALSE;
+  }
+
+  /* TODO: allow outside selection */
+  validation_found =
+      _check_for_all_layers (G_N_ELEMENTS (instance_validation_layers),
+      instance_validation_layers, instance_layer_count, instance_layers);
+  if (!validation_found) {
+    g_error ("vkEnumerateInstanceLayerProperties failed to find"
+        "required validation layer.\n\n"
+        "Please look at the Getting Started guide for additional "
+        "information.\nvkCreateInstance Failure");
+  }
+  enabled_layer_count = G_N_ELEMENTS (instance_validation_layers);
+
+  err =
+      vkEnumerateInstanceExtensionProperties (NULL, &instance_extension_count,
+      NULL);
+  if (gst_vulkan_error_to_g_error (err, error,
+          "vkEnumerateInstanceExtensionProperties") < 0) {
+    g_free (instance_layers);
+    return FALSE;
+  }
+
+  memset (extension_names, 0, sizeof (extension_names));
+  instance_extensions =
+      g_new0 (VkExtensionProperties, instance_extension_count);
+  err =
+      vkEnumerateInstanceExtensionProperties (NULL, &instance_extension_count,
+      instance_extensions);
+  if (gst_vulkan_error_to_g_error (err, error,
+          "vkEnumerateInstanceExtensionProperties") < 0) {
+    g_free (instance_layers);
+    g_free (instance_extensions);
+    return FALSE;
+  }
+
+  /* TODO: allow outside selection */
+  for (uint32_t i = 0; i < instance_extension_count; i++) {
+    if (!g_strcmp0 ("VK_EXT_KHR_swapchain", instance_extensions[i].extName)) {
+      have_swapchain_ext = TRUE;
+      extension_names[enabled_extension_count++] =
+          (gchar *) "VK_EXT_KHR_swapchain";
+    }
+    if (!g_strcmp0 (VK_DEBUG_REPORT_EXTENSION_NAME,
+            instance_extensions[i].extName)) {
+      extension_names[enabled_extension_count++] =
+          (gchar *) VK_DEBUG_REPORT_EXTENSION_NAME;
+    }
+    g_assert (enabled_extension_count < 64);
+  }
+  if (!have_swapchain_ext) {
+    g_error ("vkEnumerateInstanceExtensionProperties failed to find the "
+        "\"VK_EXT_KHR_swapchain\" extension.\n\nDo you have a compatible "
+        "Vulkan installable client driver (ICD) installed?\nPlease "
+        "look at the Getting Started guide for additional "
+        "information.\nvkCreateInstance Failure");
+  }
+
+  {
+    const VkApplicationInfo app = {
+      .sType = VK_STRUCTURE_TYPE_APPLICATION_INFO,
+      .pNext = NULL,
+      .pAppName = APP_SHORT_NAME,
+      .appVersion = 0,
+      .pEngineName = APP_SHORT_NAME,
+      .engineVersion = 0,
+      .apiVersion = VK_API_VERSION,
+    };
+    VkInstanceCreateInfo inst_info = {
+      .sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO,
+      .pNext = NULL,
+      .pAppInfo = &app,
+      .pAllocCb = NULL,
+      .layerCount = enabled_layer_count,
+      .ppEnabledLayerNames = (const char *const *) instance_validation_layers,
+      .extensionCount = enabled_extension_count,
+      .ppEnabledExtensionNames = (const char *const *) extension_names,
+    };
+
+    err = vkCreateInstance (&inst_info, &instance->instance);
+    if (gst_vulkan_error_to_g_error (err, error, "vkCreateInstance") < 0) {
+      g_free (instance_layers);
+      g_free (instance_extensions);
+      return FALSE;
+    }
+  }
+
+  g_free (instance_layers);
+  g_free (instance_extensions);
+
+  err =
+      vkEnumeratePhysicalDevices (instance->instance,
+      &instance->n_physical_devices, NULL);
+  if (gst_vulkan_error_to_g_error (err, error,
+          "vkEnumeratePhysicalDevices") < 0)
+    return FALSE;
+  g_assert (instance->n_physical_devices > 0);
+  instance->physical_devices =
+      g_new0 (VkPhysicalDevice, instance->n_physical_devices);
+  err =
+      vkEnumeratePhysicalDevices (instance->instance,
+      &instance->n_physical_devices, instance->physical_devices);
+  if (gst_vulkan_error_to_g_error (err, error,
+          "vkEnumeratePhysicalDevices") < 0)
+    return FALSE;
+
+  instance->dbgCreateMsgCallback = (PFN_vkDbgCreateMsgCallback)
+      gst_vulkan_instance_get_proc_address (instance, "vkDbgCreateMsgCallback");
+  if (!instance->dbgCreateMsgCallback) {
+    g_set_error (error, GST_VULKAN_ERROR, GST_VULKAN_ERROR_FAILED,
+        "Failed to retreive vkDbgCreateMsgCallback");
+    return FALSE;
+  }
+  instance->dbgDestroyMsgCallback = (PFN_vkDbgDestroyMsgCallback)
+      gst_vulkan_instance_get_proc_address (instance,
+      "vkDbgDestroyMsgCallback");
+  if (!instance->dbgDestroyMsgCallback) {
+    g_set_error (error, GST_VULKAN_ERROR, GST_VULKAN_ERROR_FAILED,
+        "Failed to retreive vkDbgDestroyMsgCallback");
+    return FALSE;
+  }
+  instance->dbgBreakCallback =
+      (PFN_vkDbgMsgCallback) gst_vulkan_instance_get_proc_address (instance,
+      "vkDbgBreakCallback");
+  if (!instance->dbgBreakCallback) {
+    g_set_error (error, GST_VULKAN_ERROR, GST_VULKAN_ERROR_FAILED,
+        "Failed to retreive vkDbgBreakCallback");
+    return FALSE;
+  }
+
+  err = instance->dbgCreateMsgCallback (instance->instance,
+      VK_DBG_REPORT_ERROR_BIT | VK_DBG_REPORT_WARN_BIT | VK_DBG_REPORT_INFO_BIT
+      | VK_DBG_REPORT_DEBUG_BIT | VK_DBG_REPORT_PERF_WARN_BIT,
+      _gst_vk_debug_callback, NULL, &instance->msg_callback);
+  if (gst_vulkan_error_to_g_error (err, error, "vkDbgCreateMsgCallback") < 0)
+    return FALSE;
+
+  return TRUE;
+}
+
+gpointer
+gst_vulkan_instance_get_proc_address (GstVulkanInstance * instance,
+    const gchar * name)
+{
+  g_return_val_if_fail (GST_IS_VULKAN_INSTANCE (instance), NULL);
+  g_return_val_if_fail (instance->instance != NULL, NULL);
+  g_return_val_if_fail (name != NULL, NULL);
+
+  GST_TRACE_OBJECT (instance, "%s", name);
+
+  return vkGetInstanceProcAddr (instance->instance, name);
+}
+
+void
+gst_vulkan_instance_close (GstVulkanInstance * instance)
+{
+  g_return_if_fail (GST_IS_VULKAN_INSTANCE (instance));
+  g_return_if_fail (instance->instance != NULL);
+
+  if (instance->dbgDestroyMsgCallback)
+    instance->dbgDestroyMsgCallback (instance->instance,
+        instance->msg_callback);
+
+  g_free (instance->physical_devices);
+}
diff --git a/ext/vulkan/vkinstance.h b/ext/vulkan/vkinstance.h
new file mode 100644 (file)
index 0000000..cb25007
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ * GStreamer
+ * Copyright (C) 2015 Matthew Waters <matthew@centricular.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef _VK_INSTANCE_H_
+#define _VK_INSTANCE_H_
+
+#include "vk.h"
+
+G_BEGIN_DECLS
+
+#define GST_TYPE_VULKAN_INSTANCE         (gst_vulkan_instance_get_type())
+#define GST_VULKAN_INSTANCE(o)           (G_TYPE_CHECK_INSTANCE_CAST((o), GST_TYPE_VULKAN_INSTANCE, GstVulkanInstance))
+#define GST_VULKAN_INSTANCE_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST((k), GST_TYPE_VULKAN_INSTANCE, GstVulkanInstanceClass))
+#define GST_IS_VULKAN_INSTANCE(o)        (G_TYPE_CHECK_INSTANCE_TYPE((o), GST_TYPE_VULKAN_INSTANCE))
+#define GST_IS_VULKAN_INSTANCE_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE((k), GST_TYPE_VULKAN_INSTANCE))
+#define GST_VULKAN_INSTANCE_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS((o), GST_TYPE_VULKAN_INSTANCE, GstVulkanInstanceClass))
+GType gst_vulkan_instance_get_type       (void);
+
+struct _GstVulkanInstance
+{
+  GstObject parent;
+
+  VkInstance instance; /* hides a pointer */
+  VkPhysicalDevice *physical_devices; /* hides a pointer */
+  guint32 n_physical_devices;
+
+  VkDbgMsgCallback msg_callback;
+  PFN_vkDbgCreateMsgCallback dbgCreateMsgCallback;
+  PFN_vkDbgDestroyMsgCallback dbgDestroyMsgCallback;
+  PFN_vkDbgMsgCallback dbgBreakCallback;
+};
+
+struct _GstVulkanInstanceClass
+{
+  GstObjectClass parent_class;
+};
+
+GstVulkanInstance * gst_vulkan_instance_new                 (void);
+gboolean            gst_vulkan_instance_open                (GstVulkanInstance * instance,
+                                                             GError ** error);
+void                gst_vulkan_instance_close               (GstVulkanInstance * instance);
+
+gpointer            gst_vulkan_instance_get_proc_address    (GstVulkanInstance * instance,
+                                                             const gchar * name);
+
+G_END_DECLS
+
+#endif /* _VK_INSTANCE_H_ */
diff --git a/ext/vulkan/vkmemory.c b/ext/vulkan/vkmemory.c
new file mode 100644 (file)
index 0000000..9360b83
--- /dev/null
@@ -0,0 +1,264 @@
+/*
+ * GStreamer
+ * Copyright (C) 2015 Matthew Waters <matthew@centricular.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <string.h>
+
+#include "vkmemory.h"
+
+/**
+ * SECTION:vkmemory
+ * @short_description: memory subclass for Vulkan device memory
+ * @see_also: #GstMemory, #GstAllocator
+ *
+ * GstVulkanMemory is a #GstMemory subclass providing support for the mapping of
+ * Vulkan device memory.  
+ */
+
+#define GST_CAT_DEFUALT GST_CAT_VULKAN_MEMORY
+GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFUALT);
+
+static GstAllocator *_vulkan_memory_allocator;
+
+static void
+_vk_mem_init (GstVulkanMemory * mem, GstAllocator * allocator,
+    GstMemory * parent, GstVulkanDevice * device, guint32 memory_type_index,
+    GstAllocationParams * params, gsize size,
+    VkMemoryPropertyFlags mem_prop_flags, gpointer user_data,
+    GDestroyNotify notify)
+{
+  gsize align = gst_memory_alignment, offset = 0, maxsize = size;
+  GstMemoryFlags flags = 0;
+
+  if (params) {
+    flags = params->flags;
+    align |= params->align;
+    offset = params->prefix;
+    maxsize += params->prefix + params->padding;
+    if ((maxsize & align) != 0)
+      maxsize += ~(maxsize & align) + 1;
+  }
+
+  gst_memory_init (GST_MEMORY_CAST (mem), flags, allocator, parent, maxsize,
+      align, offset, size);
+
+  mem->device = gst_object_ref (device);
+  mem->alloc_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOC_INFO;
+  mem->alloc_info.pNext = NULL;
+  mem->alloc_info.allocationSize = (VkDeviceSize) mem->mem.maxsize;
+  mem->alloc_info.memoryTypeIndex = memory_type_index;
+  mem->properties = mem_prop_flags;
+  mem->notify = notify;
+  mem->user_data = user_data;
+
+  g_mutex_init (&mem->lock);
+
+  GST_CAT_DEBUG (GST_CAT_VULKAN_MEMORY, "new GL buffer memory:%p size:%"
+      G_GSIZE_FORMAT, mem, maxsize);
+}
+
+static GstVulkanMemory *
+_vk_mem_new (GstAllocator * allocator, GstMemory * parent,
+    GstVulkanDevice * device, guint32 memory_type_index,
+    GstAllocationParams * params, gsize size,
+    VkMemoryPropertyFlags mem_props_flags, gpointer user_data,
+    GDestroyNotify notify)
+{
+  GstVulkanMemory *mem = g_slice_new0 (GstVulkanMemory);
+  GError *error = NULL;
+  VkResult err;
+
+  _vk_mem_init (mem, allocator, parent, device, memory_type_index, params,
+      size, mem_props_flags, user_data, notify);
+
+  err = vkAllocMemory (device->device, &mem->alloc_info, &mem->mem_ptr);
+  if (gst_vulkan_error_to_g_error (err, &error, "vkAllocMemory") < 0) {
+    GST_CAT_ERROR (GST_CAT_VULKAN_MEMORY, "Failed to allocate device memory %s",
+        error->message);
+    gst_memory_unref ((GstMemory *) mem);
+    g_clear_error (&error);
+    return NULL;
+  }
+
+  return mem;
+}
+
+static gpointer
+_vk_mem_map_full (GstVulkanMemory * mem, GstMapInfo * info, gsize size)
+{
+  gpointer data;
+  VkResult err;
+  GError *error = NULL;
+
+  if ((mem->properties & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) == 0) {
+    GST_CAT_ERROR (GST_CAT_VULKAN_MEMORY, "Cannot map host-invisible memory");
+    return NULL;
+  }
+
+  err = vkMapMemory (mem->device->device, mem->mem_ptr, 0, size, 0, &data);
+  if (gst_vulkan_error_to_g_error (err, &error, "vkMapMemory") < 0) {
+    GST_CAT_ERROR (GST_CAT_VULKAN_MEMORY, "Failed to map device memory %s",
+        error->message);
+    g_clear_error (&error);
+    return NULL;
+  }
+
+  return data;
+}
+
+static void
+_vk_mem_unmap_full (GstVulkanMemory * mem, GstMapInfo * info)
+{
+  vkUnmapMemory (mem->device->device, mem->mem_ptr);
+}
+
+static GstMemory *
+_vk_mem_copy (GstVulkanMemory * src, gssize offset, gssize size)
+{
+  return NULL;
+}
+
+static GstMemory *
+_vk_mem_share (GstVulkanMemory * mem, gssize offset, gssize size)
+{
+  return NULL;
+}
+
+static gboolean
+_vk_mem_is_span (GstVulkanMemory * mem1, GstVulkanMemory * mem2, gsize * offset)
+{
+  return FALSE;
+}
+
+static GstMemory *
+_vk_mem_alloc (GstAllocator * allocator, gsize size,
+    GstAllocationParams * params)
+{
+  g_critical ("Subclass should override GstAllocatorClass::alloc() function");
+
+  return NULL;
+}
+
+static void
+_vk_mem_free (GstAllocator * allocator, GstMemory * memory)
+{
+  GstVulkanMemory *mem = (GstVulkanMemory *) memory;
+
+  GST_CAT_TRACE (GST_CAT_VULKAN_MEMORY, "freeing buffer memory:%p "
+      "id:%" G_GUINT64_FORMAT, mem, mem->mem_ptr.handle);
+
+  g_mutex_clear (&mem->lock);
+
+  if (mem->notify)
+    mem->notify (mem->user_data);
+
+  vkFreeMemory (mem->device->device, mem->mem_ptr);
+
+  gst_object_unref (mem->device);
+}
+
+/**
+ * gst_vulkan_memory_alloc:
+ * @device:a #GstVulkanDevice
+ * @memory_type_index: the Vulkan memory type index
+ * @params: a #GstAllocationParams
+ * @size: the size to allocate
+ *
+ * Allocated a new #GstVulkanMemory.
+ *
+ * Returns: a #GstMemory object backed by a vulkan device memory
+ */
+GstMemory *
+gst_vulkan_memory_alloc (GstVulkanDevice * device, guint32 memory_type_index,
+    GstAllocationParams * params, gsize size, VkMemoryPropertyFlags mem_flags)
+{
+  GstVulkanMemory *mem;
+
+  mem = _vk_mem_new (_vulkan_memory_allocator, NULL, device, memory_type_index,
+      params, size, mem_flags, NULL, NULL);
+
+  return (GstMemory *) mem;
+}
+
+G_DEFINE_TYPE (GstVulkanMemoryAllocator, gst_vulkan_memory_allocator,
+    GST_TYPE_ALLOCATOR);
+
+static void
+gst_vulkan_memory_allocator_class_init (GstVulkanMemoryAllocatorClass * klass)
+{
+  GstAllocatorClass *allocator_class = (GstAllocatorClass *) klass;
+
+  allocator_class->alloc = _vk_mem_alloc;
+  allocator_class->free = _vk_mem_free;
+}
+
+static void
+gst_vulkan_memory_allocator_init (GstVulkanMemoryAllocator * allocator)
+{
+  GstAllocator *alloc = GST_ALLOCATOR_CAST (allocator);
+
+  alloc->mem_type = GST_VULKAN_MEMORY_ALLOCATOR_NAME;
+  alloc->mem_map_full = (GstMemoryMapFullFunction) _vk_mem_map_full;
+  alloc->mem_unmap_full = (GstMemoryUnmapFullFunction) _vk_mem_unmap_full;
+  alloc->mem_copy = (GstMemoryCopyFunction) _vk_mem_copy;
+  alloc->mem_share = (GstMemoryShareFunction) _vk_mem_share;
+  alloc->mem_is_span = (GstMemoryIsSpanFunction) _vk_mem_is_span;
+}
+
+/**
+ * gst_vulkan_memory_init_once:
+ *
+ * Initializes the Vulkan memory allocator. It is safe to call this function
+ * multiple times.  This must be called before any other #GstVulkanMemory operation.
+ */
+void
+gst_vulkan_memory_init_once (void)
+{
+  static volatile gsize _init = 0;
+
+  if (g_once_init_enter (&_init)) {
+    GST_DEBUG_CATEGORY_INIT (GST_CAT_VULKAN_MEMORY, "vulkanmemory", 0,
+        "Vulkan Memory");
+
+    _vulkan_memory_allocator =
+        g_object_new (gst_vulkan_memory_allocator_get_type (), NULL);
+
+    gst_allocator_register (GST_VULKAN_MEMORY_ALLOCATOR_NAME,
+        gst_object_ref (_vulkan_memory_allocator));
+    g_once_init_leave (&_init, 1);
+  }
+}
+
+/**
+ * gst_is_vulkan_memory:
+ * @mem:a #GstMemory
+ * 
+ * Returns: whether the memory at @mem is a #GstVulkanMemory
+ */
+gboolean
+gst_is_vulkan_memory (GstMemory * mem)
+{
+  return mem != NULL && mem->allocator != NULL &&
+      g_type_is_a (G_OBJECT_TYPE (mem->allocator),
+      GST_TYPE_VULKAN_MEMORY_ALLOCATOR);
+}
diff --git a/ext/vulkan/vkmemory.h b/ext/vulkan/vkmemory.h
new file mode 100644 (file)
index 0000000..6983e83
--- /dev/null
@@ -0,0 +1,94 @@
+/*
+ * GStreamer
+ * Copyright (C) 2015 Matthew Waters <matthew@centricular.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef _GST_VULKAN_BASE_BUFFER_H_
+#define _GST_VULKAN_BASE_BUFFER_H_
+
+#include <gst/gst.h>
+#include <gst/gstallocator.h>
+#include <gst/gstmemory.h>
+
+#include <vk.h>
+
+G_BEGIN_DECLS
+
+#define GST_TYPE_VULKAN_MEMORY_ALLOCATOR (gst_vulkan_memory_allocator_get_type())
+GType gst_vulkan_memory_allocator_get_type(void);
+
+#define GST_IS_VULKAN_MEMORY_ALLOCATOR(obj)              (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_VULKAN_MEMORY_ALLOCATOR))
+#define GST_IS_VULKAN_MEMORY_ALLOCATOR_CLASS(klass)      (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_VULKAN_MEMORY_ALLOCATOR))
+#define GST_VULKAN_MEMORY_ALLOCATOR_GET_CLASS(obj)       (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_VULKAN_MEMORY_ALLOCATOR, GstVulkanMemoryAllocatorClass))
+#define GST_VULKAN_MEMORY_ALLOCATOR(obj)                 (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_VULKAN_MEMORY_ALLOCATOR, GstVulkanMemoryAllocator))
+#define GST_VULKAN_MEMORY_ALLOCATOR_CLASS(klass)         (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_VULKAN_MEMORY_ALLOCATOR, GstVulkanMemoryAllocatorClass))
+#define GST_VULKAN_MEMORY_ALLOCATOR_CAST(obj)            ((GstVulkanMemoryAllocator *)(obj))
+
+#define GST_VULKAN_MEMORY_ALLOCATOR_NAME "Vulkan"
+
+struct _GstVulkanMemory
+{
+  GstMemory             mem;
+
+  GstVulkanDevice      *device;
+
+  VkDeviceMemory        mem_ptr;
+
+  /* <protected> */
+  GMutex                lock;
+
+  /* <private> */
+  GDestroyNotify        notify;
+  gpointer              user_data;
+
+  VkMemoryAllocInfo     alloc_info;
+  VkMemoryPropertyFlags properties;
+};
+
+/**
+ * GstVulkanMemoryAllocator
+ *
+ * Opaque #GstVulkanMemoryAllocator struct
+ */
+struct _GstVulkanMemoryAllocator
+{
+  GstAllocator parent;
+};
+
+/**
+ * GstVulkanMemoryAllocatorClass:
+ *
+ * The #GstVulkanMemoryAllocatorClass only contains private data
+ */
+struct _GstVulkanMemoryAllocatorClass
+{
+  GstAllocatorClass parent_class;
+};
+
+void            gst_vulkan_memory_init_once     (void);
+gboolean        gst_is_vulkan_memory            (GstMemory * mem);
+
+GstMemory *     gst_vulkan_memory_alloc         (GstVulkanDevice * device,
+                                                 guint32 memory_type_index,
+                                                 GstAllocationParams * params,
+                                                 gsize size,
+                                                 VkMemoryPropertyFlags mem_prop_flags);
+
+G_END_DECLS
+
+#endif /* _GST_VULKAN_BASE_BUFFER_H_ */
diff --git a/ext/vulkan/vkqueue.c b/ext/vulkan/vkqueue.c
new file mode 100644 (file)
index 0000000..a8a2611
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ * GStreamer
+ * Copyright (C) 2015 Matthew Waters <matthew@centricular.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "vkqueue.h"
+
+#define GST_CAT_DEFAULT gst_vulkan_queue_debug
+GST_DEBUG_CATEGORY (GST_CAT_DEFAULT);
+
+G_DEFINE_TYPE_WITH_CODE (GstVulkanQueue, gst_vulkan_queue, GST_TYPE_OBJECT,
+    GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "vulkanqueue", 0,
+        "Vulkan Queue"));
+
+static void gst_vulkan_queue_dispose (GObject * object);
+
+static void
+gst_vulkan_queue_init (GstVulkanQueue * device)
+{
+}
+
+static void
+gst_vulkan_queue_class_init (GstVulkanQueueClass * device_class)
+{
+  GObjectClass *gobject_class = (GObjectClass *) device_class;
+
+  gobject_class->dispose = gst_vulkan_queue_dispose;
+}
+
+static void
+gst_vulkan_queue_dispose (GObject * object)
+{
+  GstVulkanQueue *queue = GST_VULKAN_QUEUE (object);
+
+  if (queue->device)
+    gst_object_unref (queue->device);
+  queue->device = NULL;
+}
+
+GstVulkanDevice *
+gst_vulkan_queue_get_device (GstVulkanQueue * queue)
+{
+  g_return_val_if_fail (GST_IS_VULKAN_QUEUE (queue), NULL);
+
+  return queue->device ? gst_object_ref (queue->device) : NULL;
+}
diff --git a/ext/vulkan/vkqueue.h b/ext/vulkan/vkqueue.h
new file mode 100644 (file)
index 0000000..dccb175
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * GStreamer
+ * Copyright (C) 2015 Matthew Waters <matthew@centricular.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef _VK_QUEUE_H_
+#define _VK_QUEUE_H_
+
+#include "vk.h"
+
+#define GST_TYPE_VULKAN_QUEUE         (gst_vulkan_queue_get_type())
+#define GST_VULKAN_QUEUE(o)           (G_TYPE_CHECK_INSTANCE_CAST((o), GST_TYPE_VULKAN_QUEUE, GstVulkanQueue))
+#define GST_VULKAN_QUEUE_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST((k), GST_TYPE_VULKAN_QUEUE, GstVulkanQueueClass))
+#define GST_IS_VULKAN_QUEUE(o)        (G_TYPE_CHECK_INSTANCE_TYPE((o), GST_TYPE_VULKAN_QUEUE))
+#define GST_IS_VULKAN_QUEUE_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE((k), GST_TYPE_VULKAN_QUEUE))
+#define GST_VULKAN_QUEUE_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS((o), GST_TYPE_VULKAN_QUEUE, GstVulkanQueueClass))
+GType gst_vulkan_queue_get_type       (void);
+
+struct _GstVulkanQueue
+{
+  GstObject parent;
+
+  GstVulkanDevice *device;
+
+  VkQueue queue; /* hides a pointer */
+  guint32 family;
+  guint32 index;
+};
+
+struct _GstVulkanQueueClass
+{
+  GstObjectClass parent_class;
+};
+
+GstVulkanDevice * gst_vulkan_queue_get_device (GstVulkanQueue * queue);
+
+#endif /* _VK_QUEUE_H_ */
diff --git a/ext/vulkan/vksink.c b/ext/vulkan/vksink.c
new file mode 100644 (file)
index 0000000..30a46ea
--- /dev/null
@@ -0,0 +1,491 @@
+/*
+ * GStreamer
+ * Copyright (C) 2015 Matthew Waters <matthew@centricular.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+/**
+ * SECTION:element-vulkansink
+ *
+ * vulkansink renders video frames to a drawable on a local or remote
+ * display using Vulkan.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+//#include <gst/video/videooverlay.h>
+
+#include "vksink.h"
+#include "vkdevice.h"
+
+GST_DEBUG_CATEGORY (gst_debug_vulkan_sink);
+#define GST_CAT_DEFAULT gst_debug_vulkan_sink
+
+#define DEFAULT_FORCE_ASPECT_RATIO TRUE
+#define DEFAULT_PIXEL_ASPECT_RATIO_N 0
+#define DEFAULT_PIXEL_ASPECT_RATIO_D 1
+
+static void gst_vulkan_sink_finalize (GObject * object);
+static void gst_vulkan_sink_set_property (GObject * object, guint prop_id,
+    const GValue * value, GParamSpec * param_spec);
+static void gst_vulkan_sink_get_property (GObject * object, guint prop_id,
+    GValue * value, GParamSpec * param_spec);
+
+static gboolean gst_vulkan_sink_query (GstBaseSink * bsink, GstQuery * query);
+static void gst_vulkan_sink_set_context (GstElement * element,
+    GstContext * context);
+
+static GstStateChangeReturn
+gst_vulkan_sink_change_state (GstElement * element, GstStateChange transition);
+
+static void gst_vulkan_sink_get_times (GstBaseSink * bsink, GstBuffer * buf,
+    GstClockTime * start, GstClockTime * end);
+static gboolean gst_vulkan_sink_set_caps (GstBaseSink * bsink, GstCaps * caps);
+static GstCaps *gst_vulkan_sink_get_caps (GstBaseSink * bsink,
+    GstCaps * filter);
+static GstFlowReturn gst_vulkan_sink_prepare (GstBaseSink * bsink,
+    GstBuffer * buf);
+static GstFlowReturn gst_vulkan_sink_show_frame (GstVideoSink * bsink,
+    GstBuffer * buf);
+
+static GstStaticPadTemplate gst_vulkan_sink_template =
+GST_STATIC_PAD_TEMPLATE ("sink",
+    GST_PAD_SINK,
+    GST_PAD_ALWAYS,
+    GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE ("RGBA")));
+
+enum
+{
+  PROP_0,
+  PROP_FORCE_ASPECT_RATIO,
+  PROP_PIXEL_ASPECT_RATIO,
+};
+
+enum
+{
+  SIGNAL_0,
+  LAST_SIGNAL
+};
+
+/* static guint gst_vulkan_sink_signals[LAST_SIGNAL] = { 0 }; */
+
+#define gst_vulkan_sink_parent_class parent_class
+G_DEFINE_TYPE_WITH_CODE (GstVulkanSink, gst_vulkan_sink,
+    GST_TYPE_VIDEO_SINK, GST_DEBUG_CATEGORY_INIT (gst_debug_vulkan_sink,
+        "vulkansink", 0, "Vulkan Video Sink"));
+
+static void
+gst_vulkan_sink_class_init (GstVulkanSinkClass * klass)
+{
+  GObjectClass *gobject_class;
+  GstElementClass *gstelement_class;
+  GstBaseSinkClass *gstbasesink_class;
+  GstVideoSinkClass *gstvideosink_class;
+  GstElementClass *element_class;
+
+  gobject_class = (GObjectClass *) klass;
+  gstelement_class = (GstElementClass *) klass;
+  gstbasesink_class = (GstBaseSinkClass *) klass;
+  gstvideosink_class = (GstVideoSinkClass *) klass;
+  element_class = GST_ELEMENT_CLASS (klass);
+
+  gobject_class->set_property = gst_vulkan_sink_set_property;
+  gobject_class->get_property = gst_vulkan_sink_get_property;
+
+  g_object_class_install_property (gobject_class, PROP_FORCE_ASPECT_RATIO,
+      g_param_spec_boolean ("force-aspect-ratio", "Force aspect ratio",
+          "When enabled, scaling will respect original aspect ratio",
+          DEFAULT_FORCE_ASPECT_RATIO,
+          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+  g_object_class_install_property (gobject_class, PROP_PIXEL_ASPECT_RATIO,
+      gst_param_spec_fraction ("pixel-aspect-ratio", "Pixel Aspect Ratio",
+          "The pixel aspect ratio of the device", 0, 1, G_MAXINT, 1, 1, 1,
+          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+  gst_element_class_set_metadata (element_class, "Vulkan video sink",
+      "Sink/Video", "A videosink based on OpenGL",
+      "Matthew Waters <matthew@centricular.com>");
+
+  gst_element_class_add_pad_template (element_class,
+      gst_static_pad_template_get (&gst_vulkan_sink_template));
+
+  gobject_class->finalize = gst_vulkan_sink_finalize;
+
+  gstelement_class->change_state = gst_vulkan_sink_change_state;
+  gstelement_class->set_context = gst_vulkan_sink_set_context;
+  gstbasesink_class->query = GST_DEBUG_FUNCPTR (gst_vulkan_sink_query);
+  gstbasesink_class->set_caps = gst_vulkan_sink_set_caps;
+  gstbasesink_class->get_caps = gst_vulkan_sink_get_caps;
+  gstbasesink_class->get_times = gst_vulkan_sink_get_times;
+  gstbasesink_class->prepare = gst_vulkan_sink_prepare;
+
+  gstvideosink_class->show_frame =
+      GST_DEBUG_FUNCPTR (gst_vulkan_sink_show_frame);
+}
+
+static void
+gst_vulkan_sink_init (GstVulkanSink * vk_sink)
+{
+  vk_sink->force_aspect_ratio = DEFAULT_FORCE_ASPECT_RATIO;
+  vk_sink->par_n = DEFAULT_PIXEL_ASPECT_RATIO_N;
+  vk_sink->par_d = DEFAULT_PIXEL_ASPECT_RATIO_D;
+
+//  g_mutex_init (&vk_sink->drawing_lock);
+}
+
+static void
+gst_vulkan_sink_finalize (GObject * object)
+{
+//  GstVulkanSink *vk_sink = GST_VULKAN_SINK (object);
+//  g_mutex_clear (&vk_sink->drawing_lock);
+
+//  GST_DEBUG ("finalized");
+  G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+static void
+gst_vulkan_sink_set_property (GObject * object, guint prop_id,
+    const GValue * value, GParamSpec * pspec)
+{
+  GstVulkanSink *vk_sink = GST_VULKAN_SINK (object);
+
+  switch (prop_id) {
+    case PROP_FORCE_ASPECT_RATIO:
+      vk_sink->force_aspect_ratio = g_value_get_boolean (value);
+      break;
+    case PROP_PIXEL_ASPECT_RATIO:
+      vk_sink->par_n = gst_value_get_fraction_numerator (value);
+      vk_sink->par_d = gst_value_get_fraction_denominator (value);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+  }
+}
+
+static void
+gst_vulkan_sink_get_property (GObject * object, guint prop_id,
+    GValue * value, GParamSpec * pspec)
+{
+  GstVulkanSink *vk_sink = GST_VULKAN_SINK (object);
+
+  switch (prop_id) {
+    case PROP_FORCE_ASPECT_RATIO:
+      g_value_set_boolean (value, vk_sink->force_aspect_ratio);
+      break;
+    case PROP_PIXEL_ASPECT_RATIO:
+      gst_value_set_fraction (value, vk_sink->par_n, vk_sink->par_d);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+  }
+}
+
+static gboolean
+gst_vulkan_sink_query (GstBaseSink * bsink, GstQuery * query)
+{
+//  GstVulkanSink *vk_sink = GST_VULKAN_SINK (bsink);
+  gboolean res = FALSE;
+
+  switch (GST_QUERY_TYPE (query)) {
+    default:
+      res = GST_BASE_SINK_CLASS (parent_class)->query (bsink, query);
+      break;
+  }
+
+  return res;
+}
+
+static void
+gst_vulkan_sink_set_context (GstElement * element, GstContext * context)
+{
+//  GstVulkanSink *vk_sink = GST_VULKAN_SINK (element);
+
+  GST_ELEMENT_CLASS (parent_class)->set_context (element, context);
+}
+
+static GstStateChangeReturn
+gst_vulkan_sink_change_state (GstElement * element, GstStateChange transition)
+{
+  GstVulkanSink *vk_sink = GST_VULKAN_SINK (element);
+  GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
+  GError *error = NULL;
+
+  GST_DEBUG ("changing state: %s => %s",
+      gst_element_state_get_name (GST_STATE_TRANSITION_CURRENT (transition)),
+      gst_element_state_get_name (GST_STATE_TRANSITION_NEXT (transition)));
+
+  switch (transition) {
+    case GST_STATE_CHANGE_NULL_TO_READY:
+      vk_sink->instance = gst_vulkan_instance_new ();
+      if (!gst_vulkan_instance_open (vk_sink->instance, &error)) {
+        GST_ELEMENT_ERROR (vk_sink, RESOURCE, NOT_FOUND,
+            ("Failed to create vulkan instance"), ("%s", error->message));
+        return GST_STATE_CHANGE_FAILURE;
+      }
+      vk_sink->device = gst_vulkan_device_new (vk_sink->instance);
+      if (!gst_vulkan_device_open (vk_sink->device, &error)) {
+        GST_ELEMENT_ERROR (vk_sink, RESOURCE, NOT_FOUND,
+            ("Failed to create vulkan device"), ("%s", error->message));
+        return GST_STATE_CHANGE_FAILURE;
+      }
+
+      if (!(vk_sink->display = gst_vulkan_display_new ())) {
+        GST_ELEMENT_ERROR (vk_sink, RESOURCE, NOT_FOUND,
+            ("Failed to connect to the window sysytem"), (NULL));
+        return GST_STATE_CHANGE_FAILURE;
+      }
+
+      if (!(vk_sink->window =
+              gst_vulkan_display_create_window (vk_sink->display))) {
+        GST_ELEMENT_ERROR (vk_sink, RESOURCE, NOT_FOUND,
+            ("Failed to create a window"), (NULL));
+        return GST_STATE_CHANGE_FAILURE;
+      }
+
+      if (!gst_vulkan_window_open (vk_sink->window, &error)) {
+        GST_ELEMENT_ERROR (vk_sink, RESOURCE, NOT_FOUND,
+            ("Failed to open window"), ("%s", error->message));
+        return GST_STATE_CHANGE_FAILURE;
+      }
+
+      if (!(vk_sink->swapper =
+              gst_vulkan_swapper_new (vk_sink->device, vk_sink->window))) {
+        GST_ELEMENT_ERROR (vk_sink, RESOURCE, NOT_FOUND,
+            ("Failed to create a swapper"), (NULL));
+        return GST_STATE_CHANGE_FAILURE;
+      }
+      break;
+    case GST_STATE_CHANGE_READY_TO_PAUSED:
+      break;
+    case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
+      break;
+    default:
+      break;
+  }
+
+  ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
+  if (ret == GST_STATE_CHANGE_FAILURE)
+    return ret;
+
+  switch (transition) {
+    case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
+      break;
+    case GST_STATE_CHANGE_PAUSED_TO_READY:
+      break;
+    case GST_STATE_CHANGE_READY_TO_NULL:
+      gst_object_unref (vk_sink->swapper);
+      vk_sink->swapper = NULL;
+      gst_object_unref (vk_sink->display);
+      vk_sink->display = NULL;
+      gst_vulkan_window_close (vk_sink->window);
+      gst_object_unref (vk_sink->window);
+      vk_sink->window = NULL;
+      gst_vulkan_device_close (vk_sink->device);
+      gst_object_unref (vk_sink->device);
+      vk_sink->device = NULL;
+      gst_vulkan_instance_close (vk_sink->instance);
+      gst_object_unref (vk_sink->instance);
+      vk_sink->instance = NULL;
+      break;
+    default:
+      break;
+  }
+
+  return ret;
+}
+
+static void
+gst_vulkan_sink_get_times (GstBaseSink * bsink, GstBuffer * buf,
+    GstClockTime * start, GstClockTime * end)
+{
+  GstVulkanSink *vk_sink = GST_VULKAN_SINK (bsink);
+
+  if (GST_BUFFER_TIMESTAMP_IS_VALID (buf)) {
+    *start = GST_BUFFER_TIMESTAMP (buf);
+    if (GST_BUFFER_DURATION_IS_VALID (buf))
+      *end = *start + GST_BUFFER_DURATION (buf);
+    else {
+      if (GST_VIDEO_INFO_FPS_N (&vk_sink->v_info) > 0) {
+        *end = *start +
+            gst_util_uint64_scale_int (GST_SECOND,
+            GST_VIDEO_INFO_FPS_D (&vk_sink->v_info),
+            GST_VIDEO_INFO_FPS_N (&vk_sink->v_info));
+      }
+    }
+  }
+}
+
+static GstCaps *
+gst_vulkan_sink_get_caps (GstBaseSink * bsink, GstCaps * filter)
+{
+  GstVulkanSink *vk_sink = GST_VULKAN_SINK (bsink);
+  GstCaps *tmp = NULL;
+  GstCaps *result = NULL;
+  GError *error = NULL;
+
+  if (vk_sink->swapper) {
+    if (!(result =
+            gst_vulkan_swapper_get_supported_caps (vk_sink->swapper, &error))) {
+      GST_ELEMENT_ERROR (bsink, RESOURCE, NOT_FOUND, ("%s", error->message),
+          (NULL));
+      g_clear_error (&error);
+      return NULL;
+    }
+    return result;
+  }
+
+  tmp = gst_pad_get_pad_template_caps (GST_BASE_SINK_PAD (bsink));
+
+  if (filter) {
+    GST_DEBUG_OBJECT (bsink, "intersecting with filter caps %" GST_PTR_FORMAT,
+        filter);
+
+    result = gst_caps_intersect_full (filter, tmp, GST_CAPS_INTERSECT_FIRST);
+    gst_caps_unref (tmp);
+  } else {
+    result = tmp;
+  }
+
+  GST_DEBUG_OBJECT (bsink, "returning caps: %" GST_PTR_FORMAT, result);
+
+  return result;
+}
+
+static gboolean
+_configure_display_from_info (GstVulkanSink * vk_sink, GstVideoInfo * vinfo)
+{
+  guint display_ratio_num, display_ratio_den;
+  gint display_par_n, display_par_d;
+  gint par_n, par_d;
+  gint width, height;
+  gboolean ok;
+
+  width = GST_VIDEO_INFO_WIDTH (vinfo);
+  height = GST_VIDEO_INFO_HEIGHT (vinfo);
+
+  par_n = GST_VIDEO_INFO_PAR_N (vinfo);
+  par_d = GST_VIDEO_INFO_PAR_D (vinfo);
+
+  if (!par_n)
+    par_n = 1;
+
+  /* get display's PAR */
+  if (vk_sink->par_n != 0 && vk_sink->par_d != 0) {
+    display_par_n = vk_sink->par_n;
+    display_par_d = vk_sink->par_d;
+  } else {
+    display_par_n = 1;
+    display_par_d = 1;
+  }
+
+  ok = gst_video_calculate_display_ratio (&display_ratio_num,
+      &display_ratio_den, width, height, par_n, par_d, display_par_n,
+      display_par_d);
+
+  if (!ok)
+    return FALSE;
+
+  GST_TRACE ("PAR: %u/%u DAR:%u/%u", par_n, par_d, display_par_n,
+      display_par_d);
+
+  if (height % display_ratio_den == 0) {
+    GST_DEBUG ("keeping video height");
+    GST_VIDEO_SINK_WIDTH (vk_sink) = (guint)
+        gst_util_uint64_scale_int (height, display_ratio_num,
+        display_ratio_den);
+    GST_VIDEO_SINK_HEIGHT (vk_sink) = height;
+  } else if (width % display_ratio_num == 0) {
+    GST_DEBUG ("keeping video width");
+    GST_VIDEO_SINK_WIDTH (vk_sink) = width;
+    GST_VIDEO_SINK_HEIGHT (vk_sink) = (guint)
+        gst_util_uint64_scale_int (width, display_ratio_den, display_ratio_num);
+  } else {
+    GST_DEBUG ("approximating while keeping video height");
+    GST_VIDEO_SINK_WIDTH (vk_sink) = (guint)
+        gst_util_uint64_scale_int (height, display_ratio_num,
+        display_ratio_den);
+    GST_VIDEO_SINK_HEIGHT (vk_sink) = height;
+  }
+  GST_DEBUG ("scaling to %dx%d", GST_VIDEO_SINK_WIDTH (vk_sink),
+      GST_VIDEO_SINK_HEIGHT (vk_sink));
+
+  return TRUE;
+}
+
+static gboolean
+gst_vulkan_sink_set_caps (GstBaseSink * bsink, GstCaps * caps)
+{
+  GstVulkanSink *vk_sink = GST_VULKAN_SINK (bsink);
+  GError *error = NULL;
+  GstVideoInfo v_info;
+
+  GST_DEBUG_OBJECT (bsink, "set caps with %" GST_PTR_FORMAT, caps);
+
+  if (!gst_video_info_from_caps (&v_info, caps))
+    return FALSE;
+
+  if (!_configure_display_from_info (vk_sink, &v_info))
+    return FALSE;
+
+  if (!gst_vulkan_swapper_set_caps (vk_sink->swapper, caps, &error)) {
+    GST_ELEMENT_ERROR (vk_sink, RESOURCE, NOT_FOUND,
+        ("Failed to configure caps"), ("%s", error->message));
+    g_clear_error (&error);
+    return FALSE;
+  }
+
+  vk_sink->v_info = v_info;
+
+  return TRUE;
+}
+
+static GstFlowReturn
+gst_vulkan_sink_prepare (GstBaseSink * bsink, GstBuffer * buf)
+{
+  GstVulkanSink *vk_sink = GST_VULKAN_SINK (bsink);
+
+  GST_TRACE_OBJECT (vk_sink, "preparing buffer %" GST_PTR_FORMAT, buf);
+
+  if (GST_VIDEO_SINK_WIDTH (vk_sink) < 1 || GST_VIDEO_SINK_HEIGHT (vk_sink) < 1) {
+    return GST_FLOW_NOT_NEGOTIATED;
+  }
+
+  return GST_FLOW_OK;
+}
+
+static GstFlowReturn
+gst_vulkan_sink_show_frame (GstVideoSink * vsink, GstBuffer * buf)
+{
+  GstVulkanSink *vk_sink = GST_VULKAN_SINK (vsink);
+  GError *error = NULL;
+
+  GST_TRACE_OBJECT (vk_sink, "rendering buffer %" GST_PTR_FORMAT, buf);
+
+  if (!gst_vulkan_swapper_render_buffer (vk_sink->swapper, buf, &error)) {
+    GST_ELEMENT_ERROR (vk_sink, RESOURCE, NOT_FOUND,
+        ("Failed to render buffer"), ("%s", error->message));
+    g_clear_error (&error);
+    return GST_FLOW_ERROR;
+  }
+
+  return GST_FLOW_OK;
+}
diff --git a/ext/vulkan/vksink.h b/ext/vulkan/vksink.h
new file mode 100644 (file)
index 0000000..b90624a
--- /dev/null
@@ -0,0 +1,73 @@
+/*
+ * GStreamer
+ * Copyright (C) 2015 Matthew Waters <matthew@centricular.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef _VK_SINK_H_
+#define _VK_SINK_H_
+
+#include <gst/gst.h>
+#include <gst/video/gstvideosink.h>
+#include <gst/video/video.h>
+#include "vk.h"
+
+G_BEGIN_DECLS
+
+#define GST_TYPE_VULKAN_SINK            (gst_vulkan_sink_get_type())
+#define GST_VULKAN_SINK(obj)            (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_VULKAN_SINK,GstVulkanSink))
+#define GST_VULKAN_SINK_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_VULKAN_SINK,GstVulkanSinkClass))
+#define GST_IS_VULKAN_SINK(obj)         (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_VULKAN_SINK))
+#define GST_IS_VULKAN_SINK_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_VULKAN_SINK))
+
+typedef struct _GstVulkanSink GstVulkanSink;
+typedef struct _GstVulkanSinkClass GstVulkanSinkClass;
+
+struct _GstVulkanSink
+{
+  GstVideoSink video_sink;
+
+  GstVulkanInstance *instance;
+  GstVulkanDevice *device;
+
+  GstVulkanDisplay *display;
+  GstVulkanWindow *window;
+
+  GstVulkanSwapper *swapper;
+
+  /* properties */
+  gboolean force_aspect_ratio;
+  gint par_n;
+  gint par_d;
+
+  /* stream configuration */
+  GstVideoInfo v_info;
+
+  /* runtime variables */
+  gint to_quit;
+};
+
+struct _GstVulkanSinkClass
+{
+    GstVideoSinkClass video_sink_class;
+};
+
+GType gst_vulkan_sink_get_type(void);
+
+G_END_DECLS
+
+#endif
diff --git a/ext/vulkan/vkswapper.c b/ext/vulkan/vkswapper.c
new file mode 100644 (file)
index 0000000..4dbcec8
--- /dev/null
@@ -0,0 +1,957 @@
+/*
+ * GStreamer
+ * Copyright (C) 2015 Matthew Waters <matthew@centricular.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <string.h>
+
+#include "vkswapper.h"
+
+#define GST_CAT_DEFAULT gst_vulkan_swapper_debug
+GST_DEBUG_CATEGORY (GST_CAT_DEFAULT);
+
+#define gst_vulkan_swapper_parent_class parent_class
+G_DEFINE_TYPE_WITH_CODE (GstVulkanSwapper, gst_vulkan_swapper,
+    GST_TYPE_OBJECT, GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT,
+        "vulkanswapper", 0, "Vulkan Swapper"));
+
+static gboolean
+_get_function_table (GstVulkanSwapper * swapper)
+{
+  GstVulkanDevice *device = swapper->device;
+  GstVulkanInstance *instance = gst_vulkan_device_get_instance (device);
+
+#define GET_PROC_ADDRESS_REQUIRED(obj, type, name) \
+  G_STMT_START { \
+    obj->G_PASTE (, name) = G_PASTE(G_PASTE(gst_vulkan_, type), _get_proc_address) (type, "vk" G_STRINGIFY(name)); \
+    if (!obj->G_PASTE(, name)) { \
+      GST_ERROR_OBJECT (obj, "Failed to find required function vk" G_STRINGIFY(name)); \
+      return FALSE; \
+    } \
+  } G_STMT_END
+
+  GET_PROC_ADDRESS_REQUIRED (swapper, instance,
+      GetPhysicalDeviceSurfaceSupportKHR);
+  GET_PROC_ADDRESS_REQUIRED (swapper, device, GetSurfacePropertiesKHR);
+  GET_PROC_ADDRESS_REQUIRED (swapper, device, GetSurfaceFormatsKHR);
+  GET_PROC_ADDRESS_REQUIRED (swapper, device, GetSurfacePresentModesKHR);
+  GET_PROC_ADDRESS_REQUIRED (swapper, device, CreateSwapchainKHR);
+  GET_PROC_ADDRESS_REQUIRED (swapper, device, DestroySwapchainKHR);
+  GET_PROC_ADDRESS_REQUIRED (swapper, device, GetSwapchainImagesKHR);
+  GET_PROC_ADDRESS_REQUIRED (swapper, device, AcquireNextImageKHR);
+  GET_PROC_ADDRESS_REQUIRED (swapper, device, QueuePresentKHR);
+
+  return TRUE;
+
+#undef GET_PROC_ADDRESS_REQUIRED
+}
+
+static VkPlatformKHR
+_gst_display_type_to_vk_platform (GstVulkanDisplayType dtype)
+{
+  VkPlatformKHR ret = -1;
+
+  if (dtype == GST_VULKAN_DISPLAY_TYPE_ANY)
+    return -1;
+  if (dtype == GST_VULKAN_DISPLAY_TYPE_NONE)
+    return -1;
+
+#if GST_VULKAN_HAVE_WINDOW_X11
+  if ((dtype & GST_VULKAN_DISPLAY_TYPE_X11) == dtype)
+    ret = VK_PLATFORM_X11_KHR;
+#endif
+
+#if GST_VULKAN_HAVE_WINDOW_XCB
+  if ((dtype & GST_VULKAN_DISPLAY_TYPE_XCB) == dtype)
+    ret = VK_PLATFORM_XCB_KHR;
+#endif
+
+  return ret;
+}
+
+static gboolean
+_get_window_surface_description (GstVulkanSwapper * swapper,
+    VkSurfaceDescriptionWindowKHR * desc)
+{
+  GstVulkanDisplay *display = gst_vulkan_window_get_display (swapper->window);
+  GstVulkanDisplayType dtype = gst_vulkan_display_get_handle_type (display);
+
+  g_return_val_if_fail (desc != NULL, FALSE);
+
+  desc->sType = VK_STRUCTURE_TYPE_SURFACE_DESCRIPTION_WINDOW_KHR;
+  desc->pNext = NULL;
+  desc->platform = _gst_display_type_to_vk_platform (dtype);
+  if (desc->platform == -1) {
+    GST_ERROR_OBJECT (swapper, "Failed to retrieve platform from display");
+    return FALSE;
+  }
+
+  desc->pPlatformHandle = gst_vulkan_display_get_platform_handle (display);
+  desc->pPlatformWindow =
+      gst_vulkan_window_get_platform_handle (swapper->window);
+
+  gst_object_unref (display);
+
+  /* XXX: might be a little too strict */
+  return desc->pPlatformHandle != NULL && desc->pPlatformWindow != NULL;
+}
+
+static GstVideoFormat
+_vk_format_to_video_format (VkFormat format)
+{
+  switch (format) {
+      /* double check endianess */
+    case VK_FORMAT_R8G8B8A8_UNORM:
+      return GST_VIDEO_FORMAT_RGBA;
+    case VK_FORMAT_R8G8B8_UNORM:
+      return GST_VIDEO_FORMAT_RGB;
+    case VK_FORMAT_B8G8R8A8_UNORM:
+      return GST_VIDEO_FORMAT_BGRA;
+    case VK_FORMAT_B8G8R8_UNORM:
+      return GST_VIDEO_FORMAT_BGR;
+    default:
+      return GST_VIDEO_FORMAT_UNKNOWN;
+  }
+}
+
+static VkFormat
+_vk_format_from_video_format (GstVideoFormat v_format)
+{
+  switch (v_format) {
+    case GST_VIDEO_FORMAT_RGBA:
+      return VK_FORMAT_R8G8B8A8_UNORM;
+    case GST_VIDEO_FORMAT_RGB:
+      return VK_FORMAT_R8G8B8_UNORM;
+    case GST_VIDEO_FORMAT_BGRA:
+      return VK_FORMAT_B8G8R8A8_UNORM;
+    case GST_VIDEO_FORMAT_BGR:
+      return VK_FORMAT_B8G8R8_UNORM;
+    default:
+      return VK_FORMAT_UNDEFINED;
+  }
+}
+
+static VkColorSpaceKHR
+_vk_color_space_from_video_info (GstVideoInfo * v_info)
+{
+  return VK_COLORSPACE_SRGB_NONLINEAR_KHR;
+}
+
+static void
+_add_vk_format_to_list (GValue * list, VkFormat format)
+{
+  GstVideoFormat v_format;
+  const gchar *format_str;
+
+  v_format = _vk_format_to_video_format (format);
+  if (v_format) {
+    GValue item = G_VALUE_INIT;
+
+    g_value_init (&item, G_TYPE_STRING);
+    format_str = gst_video_format_to_string (v_format);
+    g_value_set_string (&item, format_str);
+    gst_value_list_append_value (list, &item);
+    g_value_unset (&item);
+  }
+}
+
+static gboolean
+_vulkan_swapper_retrieve_surface_properties (GstVulkanSwapper * swapper,
+    GError ** error)
+{
+  VkSurfaceDescriptionWindowKHR surface_desc;
+  VkDevice device = swapper->device->device;
+  guint32 i, present_queue = -1, graphics_queue = -1;
+  VkPhysicalDevice gpu;
+  VkResult err;
+
+  if (swapper->surf_formats)
+    return TRUE;
+
+  gpu = gst_vulkan_device_get_physical_device (swapper->device);
+
+  if (!_get_window_surface_description (swapper,
+          (VkSurfaceDescriptionWindowKHR *) & surface_desc)) {
+    g_set_error (error, GST_VULKAN_ERROR,
+        GST_VULKAN_ERROR_INITIALIZATION_FAILED,
+        "Failed to retrieve platform description");
+    return FALSE;
+  }
+
+  for (i = 0; i < swapper->device->n_queues; i++) {
+    VkBool32 supports_present;
+
+    swapper->GetPhysicalDeviceSurfaceSupportKHR (gpu, i,
+        (VkSurfaceDescriptionKHR *) & surface_desc, &supports_present);
+    if ((swapper->device->
+            queue_family_props[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) != 0) {
+      if (supports_present) {
+        /* found one that supports both */
+        graphics_queue = present_queue = i;
+        break;
+      }
+      if (graphics_queue != -1)
+        graphics_queue = i;
+    } else if (supports_present) {
+      if (present_queue != -1)
+        present_queue = i;
+    }
+  }
+
+  if (graphics_queue != present_queue) {
+    g_set_error (error, GST_VULKAN_ERROR,
+        GST_VULKAN_ERROR_INITIALIZATION_FAILED,
+        "Failed to find a compatible present/graphics queue");
+    return FALSE;
+  }
+
+  if (!(swapper->queue = gst_vulkan_device_get_queue (swapper->device,
+              swapper->device->queue_family_id, graphics_queue, error)))
+    return FALSE;
+
+  err =
+      swapper->GetSurfacePropertiesKHR (device,
+      (VkSurfaceDescriptionKHR *) & surface_desc, &swapper->surf_props);
+  if (gst_vulkan_error_to_g_error (err, error, "vkGetSurfacePropertiesKHR") < 0)
+    return FALSE;
+
+  err =
+      swapper->GetSurfaceFormatsKHR (device,
+      (VkSurfaceDescriptionKHR *) & surface_desc, &swapper->n_surf_formats,
+      NULL);
+  if (gst_vulkan_error_to_g_error (err, error, "vkGetSurfaceFormatsKHR") < 0)
+    return FALSE;
+
+  swapper->surf_formats = g_new0 (VkSurfaceFormatKHR, swapper->n_surf_formats);
+  err =
+      swapper->GetSurfaceFormatsKHR (device,
+      (VkSurfaceDescriptionKHR *) & surface_desc, &swapper->n_surf_formats,
+      swapper->surf_formats);
+  if (gst_vulkan_error_to_g_error (err, error, "vkGetSurfaceFormatsKHR") < 0)
+    return FALSE;
+
+  err =
+      swapper->GetSurfacePresentModesKHR (device,
+      (VkSurfaceDescriptionKHR *) & surface_desc,
+      &swapper->n_surf_present_modes, NULL);
+  if (gst_vulkan_error_to_g_error (err, error,
+          "vkGetSurfacePresentModesKHR") < 0)
+    return FALSE;
+
+  swapper->surf_present_modes =
+      g_new0 (VkPresentModeKHR, swapper->n_surf_present_modes);
+  err =
+      swapper->GetSurfacePresentModesKHR (device,
+      (VkSurfaceDescriptionKHR *) & surface_desc,
+      &swapper->n_surf_present_modes, swapper->surf_present_modes);
+  if (gst_vulkan_error_to_g_error (err, error,
+          "vkGetSurfacePresentModesKHR") < 0)
+    return FALSE;
+
+  return TRUE;
+}
+
+static void
+gst_vulkan_swapper_finalize (GObject * object)
+{
+  GstVulkanSwapper *swapper = GST_VULKAN_SWAPPER (object);
+  int i;
+
+  if (swapper->swap_chain_images) {
+    for (i = 0; i < swapper->n_swap_chain_images; i++) {
+      gst_memory_unref ((GstMemory *) swapper->swap_chain_images[i]);
+      swapper->swap_chain_images[i] = NULL;
+    }
+    g_free (swapper->swap_chain_images);
+  }
+  swapper->swap_chain_images = NULL;
+
+  if (swapper->swap_chain.handle)
+    swapper->DestroySwapchainKHR (swapper->device->device, swapper->swap_chain);
+  swapper->swap_chain.handle = 0;
+
+  if (swapper->queue)
+    gst_object_unref (swapper->queue);
+  swapper->queue = NULL;
+
+  if (swapper->device)
+    gst_object_unref (swapper->device);
+  swapper->device = NULL;
+
+  if (swapper->window)
+    gst_object_unref (swapper->window);
+  swapper->window = NULL;
+
+  g_free (swapper->surf_present_modes);
+  swapper->surf_present_modes = NULL;
+
+  g_free (swapper->surf_formats);
+  swapper->surf_formats = NULL;
+
+  gst_caps_replace (&swapper->caps, NULL);
+}
+
+static void
+gst_vulkan_swapper_init (GstVulkanSwapper * swapper)
+{
+}
+
+static void
+gst_vulkan_swapper_class_init (GstVulkanSwapperClass * klass)
+{
+  G_OBJECT_CLASS (klass)->finalize = gst_vulkan_swapper_finalize;
+}
+
+GstVulkanSwapper *
+gst_vulkan_swapper_new (GstVulkanDevice * device, GstVulkanWindow * window)
+{
+  GstVulkanSwapper *swapper;
+
+  swapper = g_object_new (GST_TYPE_VULKAN_SWAPPER, NULL);
+  swapper->device = gst_object_ref (device);
+  swapper->window = gst_object_ref (window);
+
+  if (!_get_function_table (swapper)) {
+    gst_object_unref (swapper);
+    return NULL;
+  }
+
+  return swapper;
+}
+
+GstCaps *
+gst_vulkan_swapper_get_supported_caps (GstVulkanSwapper * swapper,
+    GError ** error)
+{
+  GstStructure *s;
+  GstCaps *caps;
+
+  g_return_val_if_fail (GST_IS_VULKAN_SWAPPER (swapper), NULL);
+
+  if (!_vulkan_swapper_retrieve_surface_properties (swapper, error))
+    return NULL;
+
+  caps = gst_caps_new_empty_simple ("video/x-raw");
+  s = gst_caps_get_structure (caps, 0);
+
+  {
+    int i;
+    GValue list = G_VALUE_INIT;
+
+    g_value_init (&list, GST_TYPE_LIST);
+
+    if (swapper->n_surf_formats
+        && swapper->surf_formats[0].format == VK_FORMAT_UNDEFINED) {
+      _add_vk_format_to_list (&list, VK_FORMAT_B8G8R8A8_UNORM);
+    } else {
+      for (i = 0; i < swapper->n_surf_formats; i++) {
+        _add_vk_format_to_list (&list, swapper->surf_formats[i].format);
+      }
+    }
+
+    gst_structure_set_value (s, "format", &list);
+    g_value_unset (&list);
+  }
+  {
+    guint32 max_dim = swapper->device->gpu_props.limits.maxImageDimension2D;
+
+    gst_structure_set (s, "width", GST_TYPE_INT_RANGE, 1, (gint) max_dim,
+        "height", GST_TYPE_INT_RANGE, 1, (gint) max_dim, "pixel-aspect-ratio",
+        GST_TYPE_FRACTION, 1, 1, "framerate", GST_TYPE_FRACTION_RANGE, 0, 1,
+        G_MAXINT, 1, NULL);
+  }
+
+  GST_INFO_OBJECT (swapper, "Probed the following caps %" GST_PTR_FORMAT, caps);
+
+  return caps;
+}
+
+static gboolean
+_swapper_set_image_layout_with_cmd (GstVulkanSwapper * swapper, VkCmdBuffer cmd,
+    GstVulkanImageMemory * image, VkImageLayout new_image_layout,
+    GError ** error)
+{
+  VkPipelineStageFlags src_stages = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
+  VkPipelineStageFlags dest_stages = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
+  VkImageMemoryBarrier image_memory_barrier;
+  VkImageMemoryBarrier *pmemory_barrier = &image_memory_barrier;
+
+  gst_vulkan_image_memory_set_layout (image, new_image_layout,
+      &image_memory_barrier);
+
+  vkCmdPipelineBarrier (cmd, src_stages, dest_stages, FALSE, 1,
+      (const void *const *) &pmemory_barrier);
+
+  return TRUE;
+}
+
+static gboolean
+_new_fence (GstVulkanDevice * device, VkFence * fence, GError ** error)
+{
+  VkFenceCreateInfo fence_info;
+  VkResult err;
+
+  fence_info.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
+  fence_info.pNext = NULL;
+  fence_info.flags = 0;
+
+  err = vkCreateFence (device->device, &fence_info, fence);
+  if (gst_vulkan_error_to_g_error (err, error, "vkCreateFence") < 0)
+    return FALSE;
+
+  return TRUE;
+}
+
+static gboolean
+_swapper_set_image_layout (GstVulkanSwapper * swapper,
+    GstVulkanImageMemory * image, VkImageLayout new_image_layout,
+    GError ** error)
+{
+  VkCmdBuffer cmd;
+  VkFence fence;
+  VkResult err;
+
+  if (!gst_vulkan_device_create_cmd_buffer (swapper->device, &cmd, error))
+    return FALSE;
+
+  if (!_new_fence (swapper->device, &fence, error))
+    return FALSE;
+
+  {
+    VkCmdBufferBeginInfo cmd_buf_info = {
+      .sType = VK_STRUCTURE_TYPE_CMD_BUFFER_BEGIN_INFO,
+      .pNext = NULL,
+      .flags = VK_CMD_BUFFER_OPTIMIZE_SMALL_BATCH_BIT |
+          VK_CMD_BUFFER_OPTIMIZE_ONE_TIME_SUBMIT_BIT,
+      .renderPass = {VK_NULL_HANDLE}
+      ,
+      .subpass = 0,
+      .framebuffer = {VK_NULL_HANDLE}
+      ,
+    };
+    err = vkBeginCommandBuffer (cmd, &cmd_buf_info);
+    if (gst_vulkan_error_to_g_error (err, error, "vkBeginCommandBuffer") < 0)
+      return FALSE;
+  }
+
+  if (!_swapper_set_image_layout_with_cmd (swapper, cmd, image,
+          new_image_layout, error))
+    return FALSE;
+
+  err = vkEndCommandBuffer (cmd);
+  if (gst_vulkan_error_to_g_error (err, error, "vkEndCommandBuffer") < 0)
+    return FALSE;
+
+  err = vkQueueSubmit (swapper->queue->queue, 1, &cmd, fence);
+  if (gst_vulkan_error_to_g_error (err, error, "vkQueueSubmit") < 0)
+    return FALSE;
+
+  err = vkWaitForFences (swapper->device->device, 1, &fence, TRUE, -1);
+  if (gst_vulkan_error_to_g_error (err, error, "vkWaitForFences") < 0)
+    return FALSE;
+  vkDestroyCommandBuffer (swapper->device->device, cmd);
+
+  vkDestroyFence (swapper->device->device, fence);
+
+  return TRUE;
+}
+
+static gboolean
+_allocate_swapchain (GstVulkanSwapper * swapper, GstCaps * caps,
+    GError ** error)
+{
+  VkSurfaceTransformFlagsKHR preTransform;
+  VkSurfaceDescriptionWindowKHR surface_desc;
+  VkPresentModeKHR present_mode;
+  VkImageUsageFlags usage = 0;
+  VkColorSpaceKHR color_space;
+  VkImage *swap_chain_images;
+  VkExtent2D swapchain_dims;
+  guint32 n_images_wanted;
+  VkFormat format;
+  VkResult err;
+  guint32 i;
+
+  /* width and height are either both -1, or both not -1. */
+  if (swapper->surf_props.currentExtent.width == -1) {
+    /* If the surface size is undefined, the size is set to
+     * the size of the images requested. */
+    swapchain_dims.width = 320;
+    swapchain_dims.height = 240;
+  } else {
+    /* If the surface size is defined, the swap chain size must match */
+    swapchain_dims = swapper->surf_props.currentExtent;
+  }
+
+  /* If mailbox mode is available, use it, as is the lowest-latency non-
+   * tearing mode.  If not, try IMMEDIATE which will usually be available,
+   * and is fastest (though it tears).  If not, fall back to FIFO which is
+   * always available. */
+  present_mode = VK_PRESENT_MODE_FIFO_KHR;
+  for (size_t i = 0; i < swapper->n_surf_present_modes; i++) {
+    if (swapper->surf_present_modes[i] == VK_PRESENT_MODE_MAILBOX_KHR) {
+      present_mode = VK_PRESENT_MODE_MAILBOX_KHR;
+      break;
+    }
+    if ((present_mode != VK_PRESENT_MODE_MAILBOX_KHR) &&
+        (swapper->surf_present_modes[i] == VK_PRESENT_MODE_IMMEDIATE_KHR)) {
+      present_mode = VK_PRESENT_MODE_IMMEDIATE_KHR;
+    }
+  }
+
+  /* Determine the number of VkImage's to use in the swap chain (we desire to
+   * own only 1 image at a time, besides the images being displayed and
+   * queued for display): */
+  n_images_wanted = swapper->surf_props.minImageCount + 1;
+  if ((swapper->surf_props.maxImageCount > 0) &&
+      (n_images_wanted > swapper->surf_props.maxImageCount)) {
+    /* Application must settle for fewer images than desired: */
+    n_images_wanted = swapper->surf_props.maxImageCount;
+  }
+
+  if (swapper->surf_props.
+      supportedTransforms & VK_SURFACE_TRANSFORM_NONE_BIT_KHR) {
+    preTransform = VK_SURFACE_TRANSFORM_NONE_KHR;
+  } else {
+    preTransform = swapper->surf_props.currentTransform;
+  }
+
+  if (!_get_window_surface_description (swapper, &surface_desc)) {
+    g_set_error (error, GST_VULKAN_ERROR,
+        GST_VULKAN_ERROR_INITIALIZATION_FAILED,
+        "Failed to retrieve platform description");
+    return FALSE;
+  }
+
+  format =
+      _vk_format_from_video_format (GST_VIDEO_INFO_FORMAT (&swapper->v_info));
+  color_space = _vk_color_space_from_video_info (&swapper->v_info);
+
+  if ((swapper->surf_props.supportedUsageFlags &
+          VK_IMAGE_USAGE_TRANSFER_DESTINATION_BIT) != 0) {
+    usage |= VK_IMAGE_USAGE_TRANSFER_DESTINATION_BIT;
+  } else {
+    g_set_error (error, GST_VULKAN_ERROR,
+        GST_VULKAN_ERROR_INITIALIZATION_FAILED,
+        "Incorrect usage flags available for the swap images");
+    return FALSE;
+  }
+  if ((swapper->
+          surf_props.supportedUsageFlags & VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT)
+      != 0) {
+    usage |= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
+  } else {
+    g_set_error (error, GST_VULKAN_ERROR,
+        GST_VULKAN_ERROR_INITIALIZATION_FAILED,
+        "Incorrect usage flags available for the swap images");
+    return FALSE;
+  }
+
+  {
+    const VkSwapchainCreateInfoKHR swap_chain_info = {
+      .sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR,
+      .pNext = NULL,
+      .pSurfaceDescription = (const VkSurfaceDescriptionKHR *) &surface_desc,
+      .minImageCount = n_images_wanted,
+      .imageFormat = format,
+      .imageColorSpace = color_space,
+      .imageExtent = {
+            .width = swapchain_dims.width,
+            .height = swapchain_dims.height,
+          },
+      .imageUsageFlags = usage,
+      .preTransform = preTransform,
+      .imageArraySize = 1,
+      .sharingMode = VK_SHARING_MODE_EXCLUSIVE,
+      .queueFamilyCount = 0,
+      .pQueueFamilyIndices = NULL,
+      .presentMode = present_mode,
+      .oldSwapchain = swapper->swap_chain,
+      .clipped = TRUE,
+    };
+
+    err =
+        swapper->CreateSwapchainKHR (swapper->device->device, &swap_chain_info,
+        &swapper->swap_chain);
+    if (gst_vulkan_error_to_g_error (err, error, "vkCreateSwapchainKHR") < 0)
+      return FALSE;
+  }
+
+  err =
+      swapper->GetSwapchainImagesKHR (swapper->device->device,
+      swapper->swap_chain, &swapper->n_swap_chain_images, NULL);
+  if (gst_vulkan_error_to_g_error (err, error, "vkGetSwapchainImagesKHR") < 0)
+    return FALSE;
+
+  swap_chain_images = g_new0 (VkImage, swapper->n_swap_chain_images);
+  err =
+      swapper->GetSwapchainImagesKHR (swapper->device->device,
+      swapper->swap_chain, &swapper->n_swap_chain_images, swap_chain_images);
+  if (gst_vulkan_error_to_g_error (err, error, "vkGetSwapchainImagesKHR") < 0) {
+    g_free (swap_chain_images);
+    return FALSE;
+  }
+
+  swapper->swap_chain_images =
+      g_new0 (GstVulkanImageMemory *, swapper->n_swap_chain_images);
+  for (i = 0; i < swapper->n_swap_chain_images; i++) {
+    swapper->swap_chain_images[i] = (GstVulkanImageMemory *)
+        gst_vulkan_image_memory_wrapped (swapper->device, swap_chain_images[i],
+        format, swapchain_dims.width, swapchain_dims.height,
+        VK_IMAGE_TILING_OPTIMAL, usage, NULL, NULL);
+
+    if (!_swapper_set_image_layout (swapper, swapper->swap_chain_images[i],
+            VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, error)) {
+      g_free (swap_chain_images);
+      return FALSE;
+    }
+  }
+
+  g_free (swap_chain_images);
+  return TRUE;
+}
+
+static gboolean
+_swapchain_resize (GstVulkanSwapper * swapper, GError ** error)
+{
+  int i;
+
+  if (!swapper->queue) {
+    if (!_vulkan_swapper_retrieve_surface_properties (swapper, error)) {
+      return FALSE;
+    }
+  }
+
+  if (swapper->swap_chain_images) {
+    for (i = 0; i < swapper->n_swap_chain_images; i++) {
+      if (swapper->swap_chain_images[i])
+        gst_memory_unref ((GstMemory *) swapper->swap_chain_images[i]);
+    }
+    g_free (swapper->swap_chain_images);
+  }
+
+  return _allocate_swapchain (swapper, swapper->caps, error);
+}
+
+gboolean
+gst_vulkan_swapper_set_caps (GstVulkanSwapper * swapper, GstCaps * caps,
+    GError ** error)
+{
+  if (!gst_video_info_from_caps (&swapper->v_info, caps)) {
+    g_set_error (error, GST_VULKAN_ERROR,
+        GST_VULKAN_ERROR_INITIALIZATION_FAILED,
+        "Failed to geto GstVideoInfo from caps");
+    return FALSE;
+  }
+
+  gst_caps_replace (&swapper->caps, caps);
+
+  return _swapchain_resize (swapper, error);
+}
+
+struct cmd_data
+{
+  VkCmdBuffer cmd;
+  VkFence fence;
+  GDestroyNotify notify;
+  gpointer data;
+};
+
+static gboolean
+_build_render_buffer_cmd (GstVulkanSwapper * swapper, guint32 swap_idx,
+    GstBuffer * buffer, struct cmd_data *cmd_data, GError ** error)
+{
+  const VkImageSubresource subres = {
+    .aspect = VK_IMAGE_ASPECT_COLOR,
+    .mipLevel = 0,
+    .arrayLayer = 0,
+  };
+  GstVulkanImageMemory *swap_mem, *staging;
+  GstMapInfo staging_map_info;
+  VkSubresourceLayout layout;
+  GstVideoFrame vframe;
+  guint8 *src, *dest;
+  VkCmdBuffer cmd;
+  guint32 wt, ht;
+  VkResult err;
+  gsize h;
+
+  g_return_val_if_fail (swap_idx < swapper->n_swap_chain_images, FALSE);
+  swap_mem = swapper->swap_chain_images[swap_idx];
+
+  if (!gst_vulkan_device_create_cmd_buffer (swapper->device, &cmd, error))
+    return FALSE;
+
+  if (!gst_video_frame_map (&vframe, &swapper->v_info, buffer, GST_MAP_READ)) {
+    g_set_error (error, GST_VULKAN_ERROR, GST_VULKAN_ERROR_MEMORY_MAP_FAILED,
+        "Failed to map buffer");
+    return FALSE;
+  }
+
+  staging =
+      (GstVulkanImageMemory *) gst_vulkan_image_memory_alloc (swapper->device,
+      swap_mem->create_info.format, GST_VIDEO_FRAME_COMP_WIDTH (&vframe, 0),
+      GST_VIDEO_FRAME_COMP_HEIGHT (&vframe, 0), VK_IMAGE_TILING_LINEAR,
+      VK_IMAGE_USAGE_TRANSFER_SOURCE_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT);
+
+  if (!staging) {
+    g_set_error (error, GST_VULKAN_ERROR, GST_VULKAN_ERROR_MEMORY_MAP_FAILED,
+        "Failed to create staging memory");
+    gst_video_frame_unmap (&vframe);
+    return FALSE;
+  }
+
+  if (!gst_memory_map ((GstMemory *) staging, &staging_map_info, GST_MAP_WRITE)) {
+    g_set_error (error, GST_VULKAN_ERROR, GST_VULKAN_ERROR_MEMORY_MAP_FAILED,
+        "Failed to map swap image");
+    gst_video_frame_unmap (&vframe);
+    gst_memory_unref ((GstMemory *) staging);
+    return FALSE;
+  }
+
+  err = vkGetImageSubresourceLayout (swapper->device->device, staging->image,
+      &subres, &layout);
+  if (gst_vulkan_error_to_g_error (err, error,
+          "vkGetImageSubresourceLayout") < 0) {
+    gst_video_frame_unmap (&vframe);
+    gst_memory_unmap ((GstMemory *) staging, &staging_map_info);
+    gst_memory_unref ((GstMemory *) staging);
+    return FALSE;
+  }
+
+  /* FIXME: multi-planar formats */
+  dest = staging_map_info.data;
+  dest += layout.offset;
+  src = vframe.data[0];
+  for (h = 0; h < GST_VIDEO_FRAME_COMP_HEIGHT (&vframe, 0); h++) {
+    /* FIXME: memcpy */
+    memcpy (dest, src, GST_VIDEO_FRAME_PLANE_STRIDE (&vframe, 0));
+    dest += layout.rowPitch;
+    src += GST_VIDEO_FRAME_PLANE_STRIDE (&vframe, 0);
+    g_assert (dest - staging_map_info.data - layout.offset <= layout.size);
+  }
+  gst_video_frame_unmap (&vframe);
+  gst_memory_unmap ((GstMemory *) staging, &staging_map_info);
+
+  {
+    VkCmdBufferBeginInfo cmd_buf_info = {
+      .sType = VK_STRUCTURE_TYPE_CMD_BUFFER_BEGIN_INFO,
+      .pNext = NULL,
+      .flags = VK_CMD_BUFFER_OPTIMIZE_SMALL_BATCH_BIT |
+          VK_CMD_BUFFER_OPTIMIZE_ONE_TIME_SUBMIT_BIT,
+      .renderPass = {VK_NULL_HANDLE}
+      ,
+      .subpass = 0,
+      .framebuffer = {VK_NULL_HANDLE}
+      ,
+    };
+    err = vkBeginCommandBuffer (cmd, &cmd_buf_info);
+    if (gst_vulkan_error_to_g_error (err, error, "vkBeginCommandBuffer") < 0)
+      return FALSE;
+  }
+
+  if (!_swapper_set_image_layout_with_cmd (swapper, cmd, swap_mem,
+          VK_IMAGE_LAYOUT_TRANSFER_DESTINATION_OPTIMAL, error)) {
+    return FALSE;
+  }
+
+  if (!_swapper_set_image_layout_with_cmd (swapper, cmd, staging,
+          VK_IMAGE_LAYOUT_TRANSFER_SOURCE_OPTIMAL, error)) {
+    return FALSE;
+  }
+#define SUBRESOURCE_COPY(res,aspect_,mip,layer,size) \
+  G_STMT_START { \
+    res.aspect = aspect_; \
+    res.mipLevel = mip; \
+    res.arrayLayer = layer; \
+    res.arraySize = size; \
+  } G_STMT_END
+#define OFFSET3D(offset,x_,y_,z_) \
+  G_STMT_START { \
+    offset.x = x_; \
+    offset.y = y_; \
+    offset.z = z_; \
+  } G_STMT_END
+#define EXTENT3D(extent,w,h,d) \
+  G_STMT_START { \
+    extent.width = w; \
+    extent.height = h; \
+    extent.depth = d; \
+  } G_STMT_END
+
+  /* FIXME: center rect */
+#if 0
+  /* XXX: doesn't work with LunarG's example driver. According to LunarG,
+   * it's not implemented */
+  {
+    VkImageBlit blit_image = { 0, };
+
+    SUBRESOURCE_COPY (blit_image.srcSubresource, VK_IMAGE_ASPECT_COLOR, 0, 0,
+        1);
+    OFFSET3D (blit_image.srcOffset, 0, 0, 0);
+    EXTENT3D (blit_image.extent, GST_VIDEO_INFO_WIDTH (&swapper->v_info),
+        GST_VIDEO_INFO_HEIGHT (&swapper->v_info), 1);
+    SUBRESOURCE_COPY (blit_image.destSubresource, VK_IMAGE_ASPECT_COLOR, 0, 0,
+        1);
+    OFFSET3D (blit_image.destOffset, 0, 0, 0);
+    EXTENT3D (blit_image.extent, swap_mem->create_info.extent.width,
+        swap_mem->create_info.extent.height, 1);
+
+    /* FIXME: copy */
+    vkCmdBlitImage (cmd, staging->image,
+        VK_IMAGE_LAYOUT_TRANSFER_SOURCE_OPTIMAL, swap_mem->image,
+        VK_IMAGE_LAYOUT_GENERAL, 1, &blit_image, VK_TEX_FILTER_LINEAR);
+  }
+#else
+  wt = MIN (swap_mem->create_info.extent.width,
+      GST_VIDEO_INFO_WIDTH (&swapper->v_info));
+  ht = MIN (swap_mem->create_info.extent.height,
+      GST_VIDEO_INFO_HEIGHT (&swapper->v_info));
+
+  {
+    VkImageCopy copy_image = { 0, };
+
+    SUBRESOURCE_COPY (copy_image.srcSubresource, VK_IMAGE_ASPECT_COLOR, 0, 0,
+        1);
+    OFFSET3D (copy_image.srcOffset, 0, 0, 0);
+    SUBRESOURCE_COPY (copy_image.destSubresource, VK_IMAGE_ASPECT_COLOR, 0, 0,
+        1);
+    OFFSET3D (copy_image.destOffset, 0, 0, 0);
+    EXTENT3D (copy_image.extent, wt, ht, 1);
+
+    /* FIXME: copy */
+    vkCmdCopyImage (cmd, staging->image,
+        VK_IMAGE_LAYOUT_TRANSFER_SOURCE_OPTIMAL, swap_mem->image,
+        VK_IMAGE_LAYOUT_GENERAL, 1, &copy_image);
+  }
+#endif
+
+  if (!_swapper_set_image_layout_with_cmd (swapper, cmd, staging,
+          VK_IMAGE_LAYOUT_PRESENT_SOURCE_KHR, error)) {
+    return FALSE;
+  }
+
+  err = vkEndCommandBuffer (cmd);
+  if (gst_vulkan_error_to_g_error (err, error, "vkEndCommandBuffer") < 0)
+    return FALSE;
+
+  cmd_data->cmd = cmd;
+  cmd_data->notify = (GDestroyNotify) gst_memory_unref;
+  cmd_data->data = staging;
+
+  if (!_new_fence (swapper->device, &cmd_data->fence, error)) {
+    return FALSE;
+  }
+
+  /* FIXME: staging frame is leaked */
+  err =
+      vkQueueSubmit (swapper->queue->queue, 1, &cmd_data->cmd, cmd_data->fence);
+  if (gst_vulkan_error_to_g_error (err, error, "vkQueueSubmit") < 0) {
+    return FALSE;
+  }
+
+  return TRUE;
+}
+
+gboolean
+gst_vulkan_swapper_render_buffer (GstVulkanSwapper * swapper,
+    GstBuffer * buffer, GError ** error)
+{
+  VkSemaphore semaphore = { 0, };
+  VkSemaphoreCreateInfo semaphore_info = {
+    .sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO,
+    .pNext = NULL,
+    .flags = VK_FENCE_CREATE_SIGNALED_BIT,
+  };
+  VkPresentInfoKHR present;
+  struct cmd_data cmd_data = { 0, };
+  guint32 swap_idx;
+  VkResult err;
+
+reacquire:
+  err = vkCreateSemaphore (swapper->device->device, &semaphore_info,
+      &semaphore);
+  if (gst_vulkan_error_to_g_error (err, error, "vkCreateSemaphore") < 0)
+    goto error;
+
+  err =
+      swapper->AcquireNextImageKHR (swapper->device->device,
+      swapper->swap_chain, -1, semaphore, &swap_idx);
+  /* TODO: Deal with the VK_SUBOPTIMAL_KHR and VK_ERROR_OUT_OF_DATE_KHR */
+  if (err == VK_ERROR_OUT_OF_DATE_KHR) {
+    vkDestroySemaphore (swapper->device->device, semaphore);
+    if (!_swapchain_resize (swapper, error))
+      return FALSE;
+    goto reacquire;
+  } else if (gst_vulkan_error_to_g_error (err, error,
+          "vkAcquireNextImageKHR") < 0) {
+    goto error;
+  }
+
+  if (!_build_render_buffer_cmd (swapper, swap_idx, buffer, &cmd_data, error))
+    goto error;
+
+  vkQueueWaitSemaphore (swapper->queue->queue, semaphore);
+
+  present.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
+  present.pNext = NULL;
+  present.swapchainCount = 1;
+  present.swapchains = &swapper->swap_chain;
+  present.imageIndices = &swap_idx;
+
+  err = swapper->QueuePresentKHR (swapper->queue->queue, &present);
+  if (err == VK_ERROR_OUT_OF_DATE_KHR) {
+    vkDestroySemaphore (swapper->device->device, semaphore);
+    if (!_swapchain_resize (swapper, error))
+      return FALSE;
+    /* FIXME: correct? */
+    return TRUE;
+  } else if (gst_vulkan_error_to_g_error (err, error, "vkQueuePresentKHR") < 0)
+    goto error;
+
+  err = vkWaitForFences (swapper->device->device, 1, &cmd_data.fence, TRUE, -1);
+  if (gst_vulkan_error_to_g_error (err, error, "vkWaitForFences") < 0)
+    goto error;
+
+  if (semaphore.handle)
+    vkDestroySemaphore (swapper->device->device, semaphore);
+  if (cmd_data.cmd)
+    vkDestroyCommandBuffer (swapper->device->device, cmd_data.cmd);
+  if (cmd_data.fence.handle)
+    vkDestroyFence (swapper->device->device, cmd_data.fence);
+  if (cmd_data.notify)
+    cmd_data.notify (cmd_data.data);
+  return TRUE;
+
+error:
+  {
+    if (semaphore.handle)
+      vkDestroySemaphore (swapper->device->device, semaphore);
+    if (cmd_data.cmd)
+      vkDestroyCommandBuffer (swapper->device->device, cmd_data.cmd);
+    if (cmd_data.fence.handle)
+      vkDestroyFence (swapper->device->device, cmd_data.fence);
+    if (cmd_data.notify)
+      cmd_data.notify (cmd_data.data);
+    return FALSE;
+  }
+}
diff --git a/ext/vulkan/vkswapper.h b/ext/vulkan/vkswapper.h
new file mode 100644 (file)
index 0000000..9737ccd
--- /dev/null
@@ -0,0 +1,91 @@
+/*
+ * GStreamer
+ * Copyright (C) 2015 Matthew Waters <matthew@centricular.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef _VK_SWAPPER_H_
+#define _VK_SWAPPER_H_
+
+#include <gst/video/video.h>
+
+#include <vk.h>
+#include <vulkan/vk_ext_khr_swapchain.h>
+#include <vulkan/vk_ext_khr_device_swapchain.h>
+
+G_BEGIN_DECLS
+
+#define GST_TYPE_VULKAN_SWAPPER         (gst_vulkan_swapper_get_type())
+#define GST_VULKAN_SWAPPER(o)           (G_TYPE_CHECK_INSTANCE_CAST((o), GST_TYPE_VULKAN_SWAPPER, GstVulkanSwapper))
+#define GST_VULKAN_SWAPPER_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST((k), GST_TYPE_VULKAN_SWAPPER, GstVulkanSwapperClass))
+#define GST_IS_VULKAN_SWAPPER(o)        (G_TYPE_CHECK_INSTANCE_TYPE((o), GST_TYPE_VULKAN_SWAPPER))
+#define GST_IS_VULKAN_SWAPPER_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE((k), GST_TYPE_VULKAN_SWAPPER))
+#define GST_VULKAN_SWAPPER_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS((o), GST_TYPE_VULKAN_SWAPPER, GstVulkanSwapperClass))
+GType gst_vulkan_swapper_get_type       (void);
+
+struct _GstVulkanSwapper
+{
+  GstObject parent;
+
+  GstVulkanDevice *device;
+  GstVulkanWindow *window;
+  GstVulkanQueue *queue;
+
+  VkSurfacePropertiesKHR surf_props;
+  VkSurfaceFormatKHR *surf_formats;
+  guint32 n_surf_formats;
+  VkPresentModeKHR *surf_present_modes;
+  guint32 n_surf_present_modes;
+
+  VkSwapchainKHR swap_chain;
+  GstVulkanImageMemory **swap_chain_images;
+  guint32 n_swap_chain_images;
+
+  GstCaps *caps;
+  GstVideoInfo v_info;
+
+  PFN_vkGetPhysicalDeviceSurfaceSupportKHR GetPhysicalDeviceSurfaceSupportKHR;
+  PFN_vkGetSurfacePropertiesKHR GetSurfacePropertiesKHR;
+  PFN_vkGetSurfaceFormatsKHR GetSurfaceFormatsKHR;
+  PFN_vkGetSurfacePresentModesKHR GetSurfacePresentModesKHR;
+  PFN_vkCreateSwapchainKHR CreateSwapchainKHR;
+  PFN_vkDestroySwapchainKHR DestroySwapchainKHR;
+  PFN_vkGetSwapchainImagesKHR GetSwapchainImagesKHR;
+  PFN_vkAcquireNextImageKHR AcquireNextImageKHR;
+  PFN_vkQueuePresentKHR QueuePresentKHR;
+};
+
+struct _GstVulkanSwapperClass
+{
+  GstObjectClass parent_class;
+};
+
+GstVulkanSwapper *  gst_vulkan_swapper_new                      (GstVulkanDevice * device,
+                                                                 GstVulkanWindow * window);
+
+GstCaps *           gst_vulkan_swapper_get_supported_caps       (GstVulkanSwapper * swapper,
+                                                                 GError ** error);
+gboolean            gst_vulkan_swapper_set_caps                 (GstVulkanSwapper * swapper,
+                                                                 GstCaps * caps,
+                                                                 GError ** error);
+gboolean            gst_vulkan_swapper_render_buffer            (GstVulkanSwapper * swapper,
+                                                                 GstBuffer * buffer,
+                                                                 GError ** error);
+
+G_END_DECLS
+
+#endif /* _VK_INSTANCE_H_ */
diff --git a/ext/vulkan/vkutils.c b/ext/vulkan/vkutils.c
new file mode 100644 (file)
index 0000000..602b643
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * GStreamer
+ * Copyright (C) 2015 Matthew Waters <matthew@centricular.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "vkutils.h"
+
+gboolean
+_check_for_all_layers (uint32_t check_count, const char **check_names,
+    uint32_t layer_count, VkLayerProperties * layers)
+{
+  uint32_t i, j;
+
+  for (i = 0; i < check_count; i++) {
+    gboolean found = FALSE;
+    for (j = 0; j < layer_count; j++) {
+      if (g_strcmp0 (check_names[i], layers[j].layerName) == 0) {
+        found = TRUE;
+      }
+    }
+    if (!found) {
+      GST_ERROR ("Cannot find layer: %s", check_names[i]);
+      return FALSE;
+    }
+  }
+  return TRUE;
+}
diff --git a/ext/vulkan/vkutils.h b/ext/vulkan/vkutils.h
new file mode 100644 (file)
index 0000000..c07abb8
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ * GStreamer
+ * Copyright (C) 2015 Matthew Waters <matthew@centricular.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+#ifndef _VK_UTILS_H_
+#define _VK_UTILS_H_
+
+#include <gst/gst.h>
+#include "vk.h"
+
+G_BEGIN_DECLS
+
+gboolean _check_for_all_layers (uint32_t check_count, const char ** check_names,
+    uint32_t layer_count, VkLayerProperties * layers);
+
+G_END_DECLS
+
+#endif /*_VK_UTILS_H_ */
diff --git a/ext/vulkan/vkwindow.c b/ext/vulkan/vkwindow.c
new file mode 100644 (file)
index 0000000..1b8dddb
--- /dev/null
@@ -0,0 +1,247 @@
+/*
+ * GStreamer
+ * Copyright (C) 2012 Matthew Waters <ystreet00@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+/**
+ * SECTION:gstglwindow
+ * @short_description: window/surface abstraction
+ * @title: GstVulkanWindow
+ * @see_also: #GstGLContext, #GstGLDisplay
+ *
+ * GstVulkanWindow represents a window that elements can render into.  A window can
+ * either be a user visible window (onscreen) or hidden (offscreen).
+ */
+
+#if HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <gmodule.h>
+#include <stdio.h>
+
+#include "vkwindow.h"
+
+#if GST_VULKAN_HAVE_WINDOW_X11
+#include "x11/vkwindow_x11.h"
+#endif
+#if GST_VULKAN_HAVE_WINDOW_XCB
+#include "xcb/vkwindow_xcb.h"
+#endif
+
+#define GST_CAT_DEFAULT gst_vulkan_window_debug
+GST_DEBUG_CATEGORY (GST_CAT_DEFAULT);
+
+#define gst_vulkan_window_parent_class parent_class
+G_DEFINE_ABSTRACT_TYPE (GstVulkanWindow, gst_vulkan_window, GST_TYPE_OBJECT);
+
+#define GST_VULKAN_WINDOW_GET_PRIVATE(o) \
+  (G_TYPE_INSTANCE_GET_PRIVATE((o), GST_TYPE_VULKAN_WINDOW, GstVulkanWindowPrivate))
+
+struct _GstVulkanWindowPrivate
+{
+  guint surface_width;
+  guint surface_height;
+};
+
+static void gst_vulkan_window_finalize (GObject * object);
+
+typedef struct _GstVulkanDummyWindow
+{
+  GstVulkanWindow parent;
+
+  guintptr handle;
+} GstVulkanDummyWindow;
+
+typedef struct _GstVulkanDummyWindowCass
+{
+  GstVulkanWindowClass parent;
+} GstVulkanDummyWindowClass;
+
+GstVulkanDummyWindow *gst_vulkan_dummy_window_new (void);
+
+enum
+{
+  SIGNAL_0,
+  LAST_SIGNAL
+};
+
+/* static guint gst_vulkan_window_signals[LAST_SIGNAL] = { 0 }; */
+
+GQuark
+gst_vulkan_window_error_quark (void)
+{
+  return g_quark_from_static_string ("gst-gl-window-error-quark");
+}
+
+static gboolean
+gst_vulkan_window_default_open (GstVulkanWindow * window, GError ** error)
+{
+  return TRUE;
+}
+
+static void
+gst_vulkan_window_default_close (GstVulkanWindow * window)
+{
+}
+
+static void
+_init_debug (void)
+{
+  static volatile gsize _init = 0;
+
+  if (g_once_init_enter (&_init)) {
+    GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "vulkanwindow", 0,
+        "Vulkan Window");
+    g_once_init_leave (&_init, 1);
+  }
+}
+
+static void
+gst_vulkan_window_init (GstVulkanWindow * window)
+{
+}
+
+static void
+gst_vulkan_window_class_init (GstVulkanWindowClass * klass)
+{
+  g_type_class_add_private (klass, sizeof (GstVulkanWindowPrivate));
+
+  klass->open = GST_DEBUG_FUNCPTR (gst_vulkan_window_default_open);
+  klass->close = GST_DEBUG_FUNCPTR (gst_vulkan_window_default_close);
+
+  G_OBJECT_CLASS (klass)->finalize = gst_vulkan_window_finalize;
+
+  _init_debug ();
+}
+
+/**
+ * gst_vulkan_window_new:
+ * @display: a #GstGLDisplay
+ *
+ * Returns: (transfer full): a new #GstVulkanWindow using @display's connection
+ *
+ * Since: 1.4
+ */
+GstVulkanWindow *
+gst_vulkan_window_new (GstVulkanDisplay * display)
+{
+  GstVulkanWindow *window = NULL;
+  const gchar *user_choice;
+
+  g_return_val_if_fail (display != NULL, NULL);
+
+  _init_debug ();
+
+  user_choice = g_getenv ("GST_VULKAN_WINDOW");
+  GST_INFO ("creating a window, user choice:%s", user_choice);
+#if GST_VULKAN_HAVE_WINDOW_X11
+  if (!window && (!user_choice || g_strstr_len (user_choice, 3, "x11")))
+    window = GST_VULKAN_WINDOW (gst_vulkan_window_x11_new (display));
+#endif
+#if GST_VULKAN_HAVE_WINDOW_XCB
+  if (!window && (!user_choice || g_strstr_len (user_choice, 3, "xcb")))
+    window = GST_VULKAN_WINDOW (gst_vulkan_window_xcb_new (display));
+#endif
+  if (!window) {
+    /* subclass returned a NULL window */
+    GST_WARNING ("Could not create window. user specified %s, creating dummy"
+        " window", user_choice ? user_choice : "(null)");
+
+    window = GST_VULKAN_WINDOW (gst_vulkan_dummy_window_new ());
+  }
+
+  window->display = gst_object_ref (display);
+
+  return window;
+}
+
+static void
+gst_vulkan_window_finalize (GObject * object)
+{
+  GstVulkanWindow *window = GST_VULKAN_WINDOW (object);
+
+  gst_object_unref (window->display);
+
+  G_OBJECT_CLASS (gst_vulkan_window_parent_class)->finalize (object);
+}
+
+GstVulkanDisplay *
+gst_vulkan_window_get_display (GstVulkanWindow * window)
+{
+  g_return_val_if_fail (GST_IS_VULKAN_WINDOW (window), NULL);
+
+  return gst_object_ref (window->display);
+}
+
+gpointer
+gst_vulkan_window_get_platform_handle (GstVulkanWindow * window)
+{
+  GstVulkanWindowClass *klass;
+
+  g_return_val_if_fail (GST_IS_VULKAN_WINDOW (window), NULL);
+  klass = GST_VULKAN_WINDOW_GET_CLASS (window);
+  g_return_val_if_fail (klass->get_platform_handle != NULL, NULL);
+
+  return klass->get_platform_handle (window);
+}
+
+gboolean
+gst_vulkan_window_open (GstVulkanWindow * window, GError ** error)
+{
+  GstVulkanWindowClass *klass;
+
+  g_return_val_if_fail (GST_IS_VULKAN_WINDOW (window), FALSE);
+  klass = GST_VULKAN_WINDOW_GET_CLASS (window);
+  g_return_val_if_fail (klass->open != NULL, FALSE);
+
+  return klass->open (window, error);
+}
+
+void
+gst_vulkan_window_close (GstVulkanWindow * window)
+{
+  GstVulkanWindowClass *klass;
+
+  g_return_if_fail (GST_IS_VULKAN_WINDOW (window));
+  klass = GST_VULKAN_WINDOW_GET_CLASS (window);
+  g_return_if_fail (klass->close != NULL);
+
+  return klass->close (window);
+}
+
+GType gst_vulkan_dummy_window_get_type (void);
+G_DEFINE_TYPE (GstVulkanDummyWindow, gst_vulkan_dummy_window,
+    GST_TYPE_VULKAN_WINDOW);
+
+static void
+gst_vulkan_dummy_window_class_init (GstVulkanDummyWindowClass * klass)
+{
+}
+
+static void
+gst_vulkan_dummy_window_init (GstVulkanDummyWindow * dummy)
+{
+  dummy->handle = 0;
+}
+
+GstVulkanDummyWindow *
+gst_vulkan_dummy_window_new (void)
+{
+  return g_object_new (gst_vulkan_dummy_window_get_type (), NULL);
+}
diff --git a/ext/vulkan/vkwindow.h b/ext/vulkan/vkwindow.h
new file mode 100644 (file)
index 0000000..2d8b894
--- /dev/null
@@ -0,0 +1,100 @@
+/*
+ * GStreamer
+ * Copyright (C) 2008 Julien Isorce <julien.isorce@gmail.com>
+ * Copyright (C) 2012 Matthew Waters <ystreet00@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef __GST_VULKAN_WINDOW_H__
+#define __GST_VULKAN_WINDOW_H__
+
+#include <gst/gst.h>
+
+#include <vk.h>
+
+G_BEGIN_DECLS
+
+#define GST_TYPE_VULKAN_WINDOW         (gst_vulkan_window_get_type())
+#define GST_VULKAN_WINDOW(o)           (G_TYPE_CHECK_INSTANCE_CAST((o), GST_TYPE_VULKAN_WINDOW, GstVulkanWindow))
+#define GST_VULKAN_WINDOW_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST((k), GST_TYPE_VULKAN_WINDOW, GstVulkanWindowClass))
+#define GST_IS_VULKAN_WINDOW(o)        (G_TYPE_CHECK_INSTANCE_TYPE((o), GST_TYPE_VULKAN_WINDOW))
+#define GST_IS_VULKAN_WINDOW_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE((k), GST_TYPE_VULKAN_WINDOW))
+#define GST_VULKAN_WINDOW_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS((o), GST_TYPE_VULKAN_WINDOW, GstVulkanWindowClass))
+GType gst_vulkan_window_get_type     (void);
+
+#define GST_VULKAN_WINDOW_LOCK(w) g_mutex_lock(&GST_VULKAN_WINDOW(w)->lock)
+#define GST_VULKAN_WINDOW_UNLOCK(w) g_mutex_unlock(&GST_VULKAN_WINDOW(w)->lock)
+#define GST_VULKAN_WINDOW_GET_LOCK(w) (&GST_VULKAN_WINDOW(w)->lock)
+
+#define GST_VULKAN_WINDOW_ERROR (gst_vulkan_window_error_quark ())
+GQuark gst_vulkan_window_error_quark (void);
+
+typedef enum
+{
+  GST_VULKAN_WINDOW_ERROR_FAILED,
+  GST_VULKAN_WINDOW_ERROR_OLD_LIBS,
+  GST_VULKAN_WINDOW_ERROR_RESOURCE_UNAVAILABLE,
+} GstVulkanWindowError;
+
+/**
+ * GstVulkanWindow:
+ *
+ * #GstVulkanWindow is an opaque struct and should only be accessed through the
+ * provided api.
+ */
+struct _GstVulkanWindow {
+  /*< private >*/
+  GstObject parent;
+
+  GstVulkanDisplay       *display;
+
+  GMutex                  lock;
+
+  GstVulkanWindowPrivate *priv;
+
+  gpointer                _reserved[GST_PADDING];
+};
+
+/**
+ * GstVulkanWindowClass:
+ * @parent_class: Parent class
+ * @open: open the connection to the display
+ * @close: close the connection to the display
+ */
+struct _GstVulkanWindowClass {
+  GstObjectClass parent_class;
+
+  gboolean (*open)               (GstVulkanWindow *window, GError **error);
+  void     (*close)              (GstVulkanWindow *window);
+
+  gpointer (*get_platform_handle) (GstVulkanWindow *window);
+
+  /*< private >*/
+  gpointer _reserved[GST_PADDING];
+};
+
+GstVulkanWindow *  gst_vulkan_window_new                    (GstVulkanDisplay *display);
+
+GstVulkanDisplay * gst_vulkan_window_get_display            (GstVulkanWindow *window);
+gpointer           gst_vulkan_window_get_platform_handle    (GstVulkanWindow *window);
+
+gboolean           gst_vulkan_window_open                   (GstVulkanWindow * window, GError ** error);
+void               gst_vulkan_window_close                  (GstVulkanWindow * window);
+
+G_END_DECLS
+
+#endif /* __GST_VULKAN_WINDOW_H__ */
diff --git a/ext/vulkan/xcb/Makefile.am b/ext/vulkan/xcb/Makefile.am
new file mode 100644 (file)
index 0000000..7debcff
--- /dev/null
@@ -0,0 +1,29 @@
+## Process this file with automake to produce Makefile.in
+
+noinst_LTLIBRARIES = libgstvulkan-xcb.la
+
+libgstvulkan_xcb_la_SOURCES = \
+       vkdisplay_xcb.c \
+       vkwindow_xcb.c \
+       xcb_event_source.c
+
+noinst_HEADERS = \
+       vkdisplay_xcb.h \
+       vkwindow_xcb.h \
+       xcb_event_source.h
+
+libgstvulkan_xcb_la_CFLAGS = \
+       -I$(top_srcdir)/gst-libs \
+       -I$(top_srcdir)/ext/vulkan \
+       -I$(top_builddir)/gst-libs \
+       $(GST_PLUGINS_BASE_CFLAGS) \
+       $(GST_BASE_CFLAGS) \
+       $(GST_CFLAGS) \
+       $(XCB_CFLAGS)
+
+libgstvulkan_xcb_la_LIBADD = \
+       $(XCB_LIBS)
+
+libgstvulkan_xcb_la_LDFLAGS = \
+       $(GST_LIB_LDFLAGS) \
+       $(GST_ALL_LDFLAGS)
diff --git a/ext/vulkan/xcb/vkdisplay_xcb.c b/ext/vulkan/xcb/vkdisplay_xcb.c
new file mode 100644 (file)
index 0000000..568cf3d
--- /dev/null
@@ -0,0 +1,165 @@
+/*
+ * GStreamer
+ * Copyright (C) 2015 Matthew Waters <matthew@centricular.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "vkdisplay_xcb.h"
+#include "xcb_event_source.h"
+
+#define GST_CAT_DEFAULT gst_vulkan_display_debug
+GST_DEBUG_CATEGORY_STATIC (gst_vulkan_display_debug);
+
+G_DEFINE_TYPE (GstVulkanDisplayXCB, gst_vulkan_display_xcb,
+    GST_TYPE_VULKAN_DISPLAY);
+
+static void gst_vulkan_display_xcb_finalize (GObject * object);
+static gpointer gst_vulkan_display_xcb_get_handle (GstVulkanDisplay * display);
+static gpointer gst_vulkan_display_xcb_get_platform_handle (GstVulkanDisplay *
+    display);
+
+static void
+gst_vulkan_display_xcb_class_init (GstVulkanDisplayXCBClass * klass)
+{
+  GST_VULKAN_DISPLAY_CLASS (klass)->get_handle =
+      GST_DEBUG_FUNCPTR (gst_vulkan_display_xcb_get_handle);
+  GST_VULKAN_DISPLAY_CLASS (klass)->get_platform_handle =
+      GST_DEBUG_FUNCPTR (gst_vulkan_display_xcb_get_platform_handle);
+
+  G_OBJECT_CLASS (klass)->finalize = gst_vulkan_display_xcb_finalize;
+}
+
+static void
+gst_vulkan_display_xcb_init (GstVulkanDisplayXCB * display_xcb)
+{
+  GstVulkanDisplay *display = (GstVulkanDisplay *) display_xcb;
+
+  display->type = GST_VULKAN_DISPLAY_TYPE_XCB;
+  display_xcb->foreign_display = FALSE;
+}
+
+static void
+gst_vulkan_display_xcb_finalize (GObject * object)
+{
+  GstVulkanDisplayXCB *display_xcb = GST_VULKAN_DISPLAY_XCB (object);
+
+  if (!display_xcb->foreign_display && display_xcb->platform_handle.connection)
+    xcb_disconnect (display_xcb->platform_handle.connection);
+  display_xcb->platform_handle.connection = NULL;
+
+  if (display_xcb->event_source) {
+    g_source_destroy (display_xcb->event_source);
+    g_source_unref (display_xcb->event_source);
+  }
+  display_xcb->event_source = NULL;
+
+  G_OBJECT_CLASS (gst_vulkan_display_xcb_parent_class)->finalize (object);
+}
+
+static xcb_screen_t *
+_get_screen_from_connection (xcb_connection_t * connection, int screen_no)
+{
+  const xcb_setup_t *setup;
+  xcb_screen_iterator_t iter;
+
+  setup = xcb_get_setup (connection);
+  iter = xcb_setup_roots_iterator (setup);
+  while (screen_no-- > 0)
+    xcb_screen_next (&iter);
+
+  return iter.data;
+}
+
+/**
+ * gst_vulkan_display_xcb_new:
+ * @name: (allow-none): a display name
+ *
+ * Create a new #GstVulkanDisplayXCB from the xcb display name.  See XOpenDisplay()
+ * for details on what is a valid name.
+ *
+ * Returns: (transfer full): a new #GstVulkanDisplayXCB or %NULL
+ */
+GstVulkanDisplayXCB *
+gst_vulkan_display_xcb_new (const gchar * name)
+{
+  xcb_connection_t *connection;
+  GstVulkanDisplayXCB *ret;
+  int screen_no = 0;
+
+  GST_DEBUG_CATEGORY_GET (gst_vulkan_display_debug, "gldisplay");
+
+  ret = g_object_new (GST_TYPE_VULKAN_DISPLAY_XCB, NULL);
+
+  ret->platform_handle.connection = connection = xcb_connect (NULL, &screen_no);
+  if (connection == NULL || xcb_connection_has_error (connection)) {
+    GST_ERROR_OBJECT (ret,
+        "Failed to open XCB display connection with name, \'%s\'", name);
+    gst_object_unref (ret);
+    return NULL;
+  }
+
+  ret->screen = _get_screen_from_connection (connection, screen_no);
+  ret->platform_handle.root = ret->screen->root;
+  ret->event_source = xcb_event_source_new (ret);
+
+  return ret;
+}
+
+/**
+ * gst_vulkan_display_xcb_new_with_display:
+ * @display: an existing, xcb display
+ *
+ * Creates a new display connection from a XCB Display.
+ *
+ * Returns: (transfer full): a new #GstVulkanDisplayXCB
+ */
+GstVulkanDisplayXCB *
+gst_vulkan_display_xcb_new_with_connection (xcb_connection_t * connection,
+    int screen_no)
+{
+  GstVulkanDisplayXCB *ret;
+
+  g_return_val_if_fail (connection != NULL, NULL);
+
+  GST_DEBUG_CATEGORY_GET (gst_vulkan_display_debug, "gldisplay");
+
+  ret = g_object_new (GST_TYPE_VULKAN_DISPLAY_XCB, NULL);
+
+  ret->platform_handle.connection = connection;
+  ret->screen = _get_screen_from_connection (connection, screen_no);
+  ret->platform_handle.root = ret->screen->root;
+  ret->foreign_display = TRUE;
+
+  return ret;
+}
+
+static gpointer
+gst_vulkan_display_xcb_get_handle (GstVulkanDisplay * display)
+{
+  return (gpointer) GST_VULKAN_DISPLAY_XCB (display)->platform_handle.
+      connection;
+}
+
+static gpointer
+gst_vulkan_display_xcb_get_platform_handle (GstVulkanDisplay * display)
+{
+  return &GST_VULKAN_DISPLAY_XCB (display)->platform_handle;
+}
diff --git a/ext/vulkan/xcb/vkdisplay_xcb.h b/ext/vulkan/xcb/vkdisplay_xcb.h
new file mode 100644 (file)
index 0000000..8770b0a
--- /dev/null
@@ -0,0 +1,84 @@
+/*
+ * GStreamer
+ * Copyright (C) 2013 Matthew Waters <ystreet00@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef __GST_VULKAN_DISPLAY_XCB_H__
+#define __GST_VULKAN_DISPLAY_XCB_H__
+
+#include <gst/gst.h>
+
+#include <xcb/xcb.h>
+
+#include <vk.h>
+
+G_BEGIN_DECLS
+
+GType gst_vulkan_display_xcb_get_type (void);
+
+#define GST_TYPE_VULKAN_DISPLAY_XCB             (gst_vulkan_display_xcb_get_type())
+#define GST_VULKAN_DISPLAY_XCB(obj)             (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_VULKAN_DISPLAY_XCB,GstVulkanDisplayXCB))
+#define GST_VULKAN_DISPLAY_XCB_CLASS(klass)     (G_TYPE_CHECK_CLASS_CAST((klass), GST_TYPE_VULKAN_DISPLAY_XCB,GstVulkanDisplayXCBClass))
+#define GST_IS_VULKAN_DISPLAY_XCB(obj)          (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_VULKAN_DISPLAY_XCB))
+#define GST_IS_VULKAN_DISPLAY_XCB_CLASS(klass)  (G_TYPE_CHECK_CLASS_TYPE((klass), GST_TYPE_VULKAN_DISPLAY_XCB))
+#define GST_VULKAN_DISPLAY_XCB_CAST(obj)        ((GstVulkanDisplayXCB*)(obj))
+
+typedef struct _GstVulkanDisplayXCB GstVulkanDisplayXCB;
+typedef struct _GstVulkanDisplayXCBClass GstVulkanDisplayXCBClass;
+
+#define GST_VULKAN_DISPLAY_XCB_CONNECTION(d) (GST_VULKAN_DISPLAY_XCB(d)->platform_handle.connection)
+#define GST_VULKAN_DISPLAY_XCB_ROOT_WINDOW(d) (GST_VULKAN_DISPLAY_XCB(d)->platform_handle.root)
+#define GST_VULKAN_DISPLAY_XCB_SCREEN(d) (GST_VULKAN_DISPLAY_XCB(d)->screen)
+
+struct _GstVkPlatformHandleXCBKHR
+{
+  xcb_connection_t* connection;
+  xcb_window_t      root;
+};
+
+/**
+ * GstVulkanDisplayXCB:
+ *
+ * the contents of a #GstVulkanDisplayXCB are private and should only be accessed
+ * through the provided API
+ */
+struct _GstVulkanDisplayXCB
+{
+  GstVulkanDisplay          parent;
+
+  /* <private> */
+  gboolean foreign_display;
+
+  struct _GstVkPlatformHandleXCBKHR platform_handle;
+  xcb_screen_t *screen;
+
+  GSource *event_source;
+};
+
+struct _GstVulkanDisplayXCBClass
+{
+  GstVulkanDisplayClass object_class;
+};
+
+GstVulkanDisplayXCB * gst_vulkan_display_xcb_new                    (const gchar * name);
+GstVulkanDisplayXCB * gst_vulkan_display_xcb_new_with_connection    (xcb_connection_t * connection,
+                                                                     int screen_no);
+
+G_END_DECLS
+
+#endif /* __GST_VULKAN_DISPLAY_XCB_H__ */
diff --git a/ext/vulkan/xcb/vkwindow_xcb.c b/ext/vulkan/xcb/vkwindow_xcb.c
new file mode 100644 (file)
index 0000000..4ce5102
--- /dev/null
@@ -0,0 +1,243 @@
+/*
+ * GStreamer
+ * Copyright (C) 2015 Matthew Waters <matthew@centricular.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <gst/gst.h>
+#include <locale.h>
+
+#include "vkwindow_xcb.h"
+#include "vkdisplay_xcb.h"
+
+#define GST_VULKAN_WINDOW_XCB_GET_PRIVATE(o)  \
+  (G_TYPE_INSTANCE_GET_PRIVATE((o), GST_TYPE_VULKAN_WINDOW_XCB, GstVulkanWindowXCBPrivate))
+
+#define GST_CAT_DEFAULT gst_vulkan_window_xcb_debug
+GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
+
+static void
+_init_debug (void)
+{
+  static volatile gsize _init = 0;
+
+  if (g_once_init_enter (&_init)) {
+    GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "vulkanwindowxcb", 0,
+        "Vulkan XCB Window");
+    g_once_init_leave (&_init, 1);
+  }
+}
+
+#define gst_vulkan_window_xcb_parent_class parent_class
+G_DEFINE_TYPE_WITH_CODE (GstVulkanWindowXCB, gst_vulkan_window_xcb,
+    GST_TYPE_VULKAN_WINDOW, _init_debug ());
+
+gboolean gst_vulkan_window_xcb_handle_event (GstVulkanWindowXCB * window_xcb);
+
+enum
+{
+  PROP_0,
+};
+
+struct _GstVulkanWindowXCBPrivate
+{
+  gboolean activate;
+  gboolean activate_result;
+
+  gint preferred_width;
+  gint preferred_height;
+
+  xcb_intern_atom_reply_t *atom_wm_delete_window;
+};
+
+static gpointer gst_vulkan_window_xcb_get_platform_window (GstVulkanWindow *
+    window);
+gboolean gst_vulkan_window_xcb_open (GstVulkanWindow * window, GError ** error);
+void gst_vulkan_window_xcb_close (GstVulkanWindow * window);
+
+static void
+gst_vulkan_window_xcb_finalize (GObject * object)
+{
+  G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+static void
+gst_vulkan_window_xcb_class_init (GstVulkanWindowXCBClass * klass)
+{
+  GObjectClass *obj_class = G_OBJECT_CLASS (klass);
+  GstVulkanWindowClass *window_class = (GstVulkanWindowClass *) klass;
+
+  g_type_class_add_private (klass, sizeof (GstVulkanWindowXCBPrivate));
+
+  obj_class->finalize = gst_vulkan_window_xcb_finalize;
+
+  window_class->open = GST_DEBUG_FUNCPTR (gst_vulkan_window_xcb_open);
+  window_class->close = GST_DEBUG_FUNCPTR (gst_vulkan_window_xcb_close);
+  window_class->get_platform_handle = gst_vulkan_window_xcb_get_platform_window;
+}
+
+static void
+gst_vulkan_window_xcb_init (GstVulkanWindowXCB * window)
+{
+  window->priv = GST_VULKAN_WINDOW_XCB_GET_PRIVATE (window);
+}
+
+/* Must be called in the gl thread */
+GstVulkanWindowXCB *
+gst_vulkan_window_xcb_new (GstVulkanDisplay * display)
+{
+  _init_debug ();
+
+  if ((gst_vulkan_display_get_handle_type (display) &
+          GST_VULKAN_DISPLAY_TYPE_XCB)
+      == GST_VULKAN_DISPLAY_TYPE_NONE) {
+    GST_INFO ("Wrong display type %u for this window type %u", display->type,
+        GST_VULKAN_DISPLAY_TYPE_XCB);
+    return NULL;
+  }
+
+  return g_object_new (GST_TYPE_VULKAN_WINDOW_XCB, NULL);
+}
+
+static void
+gst_vulkan_window_xcb_show (GstVulkanWindow * window)
+{
+  GstVulkanWindowXCB *window_xcb = GST_VULKAN_WINDOW_XCB (window);
+  GstVulkanDisplayXCB *display_xcb = GST_VULKAN_DISPLAY_XCB (window->display);
+  xcb_connection_t *connection = display_xcb->platform_handle.connection;
+
+  if (!window_xcb->visible) {
+    xcb_map_window (connection, window_xcb->win_id);
+    window_xcb->visible = TRUE;
+  }
+}
+
+static void
+gst_vulkan_window_xcb_hide (GstVulkanWindow * window)
+{
+  GstVulkanWindowXCB *window_xcb = GST_VULKAN_WINDOW_XCB (window);
+  GstVulkanDisplayXCB *display_xcb = GST_VULKAN_DISPLAY_XCB (window->display);
+  xcb_connection_t *connection = display_xcb->platform_handle.connection;
+
+  if (window_xcb->visible) {
+    xcb_unmap_window (connection, window_xcb->win_id);
+    window_xcb->visible = FALSE;
+  }
+}
+
+gboolean
+gst_vulkan_window_xcb_create_window (GstVulkanWindowXCB * window_xcb)
+{
+  GstVulkanDisplayXCB *display_xcb;
+  xcb_connection_t *connection;
+  xcb_screen_t *screen;
+  xcb_window_t root_window;
+  uint32_t value_mask, value_list[32];
+  xcb_intern_atom_cookie_t cookie, cookie2;
+  xcb_intern_atom_reply_t *reply, *reply2;
+//  const gchar *title = "OpenGL renderer";
+  gint x = 0, y = 0, width = 320, height = 240;
+
+  display_xcb =
+      GST_VULKAN_DISPLAY_XCB (GST_VULKAN_WINDOW (window_xcb)->display);
+  connection = GST_VULKAN_DISPLAY_XCB_CONNECTION (display_xcb);
+  root_window = GST_VULKAN_DISPLAY_XCB_ROOT_WINDOW (display_xcb);
+  screen = GST_VULKAN_DISPLAY_XCB_SCREEN (display_xcb);
+
+  window_xcb->win_id = xcb_generate_id (connection);
+
+  value_mask = XCB_CW_BACK_PIXEL | XCB_CW_EVENT_MASK;
+  value_list[0] = screen->black_pixel;
+  value_list[1] = XCB_EVENT_MASK_KEY_RELEASE | XCB_EVENT_MASK_EXPOSURE;
+
+  xcb_create_window (connection, XCB_COPY_FROM_PARENT, window_xcb->win_id,
+      root_window, x, y, width, height, 0, XCB_WINDOW_CLASS_INPUT_OUTPUT,
+      screen->root_visual, value_mask, value_list);
+
+  GST_LOG_OBJECT (window_xcb, "gl window id: %p",
+      (gpointer) (guintptr) window_xcb->win_id);
+  GST_LOG_OBJECT (window_xcb, "gl window props: x:%d y:%d", x, y);
+
+  /* Magic code that will send notification when window is destroyed */
+  cookie = xcb_intern_atom (connection, 1, 12, "WM_PROTOCOLS");
+  reply = xcb_intern_atom_reply (connection, cookie, 0);
+
+  cookie2 = xcb_intern_atom (connection, 0, 16, "WM_DELETE_WINDOW");
+  reply2 = xcb_intern_atom_reply (connection, cookie2, 0);
+
+  xcb_change_property (connection, XCB_PROP_MODE_REPLACE, window_xcb->win_id,
+      reply->atom, 4, 32, 1, &reply2->atom);
+  g_free (reply);
+  g_free (reply2);
+
+  gst_vulkan_window_xcb_show (GST_VULKAN_WINDOW (window_xcb));
+
+  return TRUE;
+}
+
+static gpointer
+gst_vulkan_window_xcb_get_platform_window (GstVulkanWindow * window)
+{
+  return &GST_VULKAN_WINDOW_XCB (window)->win_id;
+}
+
+gboolean
+gst_vulkan_window_xcb_open (GstVulkanWindow * window, GError ** error)
+{
+  GstVulkanWindowXCB *window_xcb = GST_VULKAN_WINDOW_XCB (window);
+  GstVulkanDisplayXCB *display_xcb = (GstVulkanDisplayXCB *) window->display;
+  xcb_connection_t *connection;
+
+  connection = display_xcb->platform_handle.connection;
+  if (connection == NULL) {
+    g_set_error (error, GST_VULKAN_WINDOW_ERROR,
+        GST_VULKAN_WINDOW_ERROR_RESOURCE_UNAVAILABLE,
+        "Failed to connect to X display server with XCB");
+    goto failure;
+  }
+
+  if (!GST_VULKAN_WINDOW_CLASS (parent_class)->open (window, error))
+    return FALSE;
+
+  return gst_vulkan_window_xcb_create_window (window_xcb);
+
+failure:
+  return FALSE;
+}
+
+void
+gst_vulkan_window_xcb_close (GstVulkanWindow * window)
+{
+  GstVulkanWindowXCB *window_xcb = GST_VULKAN_WINDOW_XCB (window);
+  GstVulkanDisplayXCB *display_xcb = (GstVulkanDisplayXCB *) window->display;
+  xcb_connection_t *connection =
+      GST_VULKAN_DISPLAY_XCB_CONNECTION (display_xcb);
+
+  if (connection) {
+    gst_vulkan_window_xcb_hide (window);
+
+    g_free (window_xcb->priv->atom_wm_delete_window);
+    window_xcb->priv->atom_wm_delete_window = NULL;
+    GST_DEBUG ("display receiver closed");
+  }
+
+  GST_VULKAN_WINDOW_CLASS (parent_class)->close (window);
+}
diff --git a/ext/vulkan/xcb/vkwindow_xcb.h b/ext/vulkan/xcb/vkwindow_xcb.h
new file mode 100644 (file)
index 0000000..fc9c70d
--- /dev/null
@@ -0,0 +1,86 @@
+/*
+ * GStreamer
+ * Copyright (C) 2012 Matthew Waters <ystreet00@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef __GST_VULKAN_WINDOW_XCB_H__
+#define __GST_VULKAN_WINDOW_XCB_H__
+
+#include <xcb/xcb.h>
+
+#include <vk.h>
+
+G_BEGIN_DECLS
+
+#define GST_TYPE_VULKAN_WINDOW_XCB         (gst_vulkan_window_xcb_get_type())
+#define GST_VULKAN_WINDOW_XCB(o)           (G_TYPE_CHECK_INSTANCE_CAST((o), GST_TYPE_VULKAN_WINDOW_XCB, GstVulkanWindowXCB))
+#define GST_VULKAN_WINDOW_XCB_CLASS(k)     (G_TYPE_CHECK_CLASS((k), GST_TYPE_VULKAN_WINDOW_XCB, GstVulkanWindowXCBClass))
+#define GST_IS_VULKAN_WINDOW_XCB(o)        (G_TYPE_CHECK_INSTANCE_TYPE((o), GST_TYPE_VULKAN_WINDOW_XCB))
+#define GST_IS_VULKAN_WINDOW_XCB_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE((k), GST_TYPE_VULKAN_WINDOW_XCB))
+#define GST_VULKAN_WINDOW_XCB_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS((o), GST_TYPE_VULKAN_WINDOW_XCB, GstVulkanWindowXCBClass))
+
+typedef struct _GstVulkanWindowXCB        GstVulkanWindowXCB;
+typedef struct _GstVulkanWindowXCBPrivate GstVulkanWindowXCBPrivate;
+typedef struct _GstVulkanWindowXCBClass   GstVulkanWindowXCBClass;
+
+/**
+ * GstVulkanWindowXCB:
+ *
+ * Opaque #GstVulkanWindowXCB object
+ */
+struct _GstVulkanWindowXCB
+{
+  /*< private >*/
+  GstVulkanWindow parent;
+
+  /* X window */
+  xcb_window_t win_id;
+
+  gint          visible :1;
+
+  /*< private >*/
+  GstVulkanWindowXCBPrivate *priv;
+  
+  gpointer _reserved[GST_PADDING];
+};
+
+/**
+ * GstVulkanWindowXCBClass:
+ *
+ * Opaque #GstVulkanWindowXCBClass object
+ */
+struct _GstVulkanWindowXCBClass {
+  /*< private >*/
+  GstVulkanWindowClass parent_class;
+
+  /*< private >*/
+  gpointer _reserved[GST_PADDING_LARGE];
+};
+
+GType gst_vulkan_window_xcb_get_type     (void);
+
+GstVulkanWindowXCB * gst_vulkan_window_xcb_new (GstVulkanDisplay * display);
+
+void gst_vulkan_window_xcb_trap_x_errors (void);
+gint gst_vulkan_window_xcb_untrap_x_errors (void);
+
+gboolean gst_vulkan_window_xcb_create_window (GstVulkanWindowXCB * window_xcb);
+
+G_END_DECLS
+
+#endif /* __GST_VULKAN_WINDOW_XCB_H__ */
diff --git a/ext/vulkan/xcb/xcb_event_source.c b/ext/vulkan/xcb/xcb_event_source.c
new file mode 100644 (file)
index 0000000..f848841
--- /dev/null
@@ -0,0 +1,251 @@
+/*
+ * GStreamer
+ * Copyright (C) 2012 Matthew Waters <ystreet00@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdint.h>
+#include <stdlib.h>
+
+#include "xcb_event_source.h"
+#include "vkdisplay_xcb.h"
+#include "vkwindow_xcb.h"
+
+static gint
+_compare_xcb_window (GstVulkanWindowXCB * window_xcb, xcb_window_t * window_id)
+{
+  return window_xcb->win_id - *window_id;
+}
+
+static GstVulkanWindowXCB *
+_find_window_from_xcb_window (GstVulkanDisplayXCB * display_xcb,
+    xcb_window_t window_id)
+{
+  GstVulkanDisplay *display = GST_VULKAN_DISPLAY (display_xcb);
+  GstVulkanWindowXCB *ret = NULL;
+  GList *l;
+
+  if (!window_id)
+    return NULL;
+
+  GST_OBJECT_LOCK (display);
+  l = g_list_find_custom (display->windows, &window_id,
+      (GCompareFunc) _compare_xcb_window);
+  if (l)
+    ret = gst_object_ref (l->data);
+  GST_OBJECT_UNLOCK (display);
+
+  return ret;
+}
+
+static gboolean
+_xcb_handle_event (GstVulkanDisplayXCB * display_xcb)
+{
+  xcb_connection_t *connection =
+      GST_VULKAN_DISPLAY_XCB_CONNECTION (display_xcb);
+  xcb_generic_event_t *event;
+  gboolean ret = TRUE;
+
+  while ((event = xcb_poll_for_event (connection))) {
+    uint8_t event_code = event->response_type & 0x7f;
+
+    switch (event_code) {
+      case XCB_CLIENT_MESSAGE:{
+        xcb_client_message_event_t *client_event;
+        xcb_intern_atom_cookie_t cookie;
+        xcb_intern_atom_reply_t *reply;
+
+        client_event = (xcb_client_message_event_t *) event;
+        cookie = xcb_intern_atom (connection, 0, 16, "WM_DELETE_WINDOW");
+        reply = xcb_intern_atom_reply (connection, cookie, 0);
+
+        if (client_event->data.data32[0] == reply->atom) {
+          GstVulkanWindowXCB *window_xcb;
+
+          window_xcb =
+              _find_window_from_xcb_window (display_xcb, client_event->window);
+          /* TODO: actually quit */
+          ret = FALSE;
+#if 0
+          if (display->close)
+            display->close (display->close_data);
+#endif
+          gst_object_unref (window_xcb);
+        }
+
+        g_free (reply);
+        break;
+      }
+#if 0
+      case CreateNotify:
+      case ConfigureNotify:
+#if 0
+        gst_vulkan_window_resize (window, event.xconfigure.width,
+            event.xconfigure.height);
+#endif
+        break;
+      case DestroyNotify:
+        break;
+
+      case Expose:
+        /* non-zero means that other Expose follows
+         * so just wait for the last one
+         * in theory we should not receive non-zero because
+         * we have no sub areas here but just in case */
+        if (event.xexpose.count != 0) {
+          break;
+        }
+#if 0
+        /* We need to redraw on expose */
+        if (window->draw) {
+          context = gst_vulkan_window_get_context (window);
+          context_class = GST_VULKAN_CONTEXT_GET_CLASS (context);
+
+          window->draw (window->draw_data);
+          context_class->swap_buffers (context);
+
+          gst_object_unref (context);
+        }
+#endif
+        break;
+      case VisibilityNotify:
+        /* actually nothing to do here */
+        break;
+#if 0
+      case KeyPress:
+      case KeyRelease:
+        keysym = XkbKeycodeToKeysym (window_xcb->device,
+            event.xkey.keycode, 0, 0);
+        key_str = XKeysymToString (keysym);
+        key_data = g_slice_new (struct key_event);
+        key_data->window = window;
+        key_data->key_str = XKeysymToString (keysym);
+        key_data->event_type =
+            event.type == KeyPress ? "key-press" : "key-release";
+        GST_DEBUG ("input event key %d pressed over window at %d,%d (%s)",
+            event.xkey.keycode, event.xkey.x, event.xkey.y, key_str);
+        g_main_context_invoke (window->navigation_context,
+            (GSourceFunc) gst_vulkan_window_key_event_cb, key_data);
+        break;
+      case ButtonPress:
+      case ButtonRelease:
+        GST_DEBUG ("input event mouse button %d pressed over window at %d,%d",
+            event.xbutton.button, event.xbutton.x, event.xbutton.y);
+        mouse_data = g_slice_new (struct mouse_event);
+        mouse_data->window = window;
+        mouse_data->event_type =
+            event.type ==
+            ButtonPress ? "mouse-button-press" : "mouse-button-release";
+        mouse_data->button = event.xbutton.button;
+        mouse_data->posx = (double) event.xbutton.x;
+        mouse_data->posy = (double) event.xbutton.y;
+
+        g_main_context_invoke (window->navigation_context,
+            (GSourceFunc) gst_vulkan_window_mouse_event_cb, mouse_data);
+        break;
+      case MotionNotify:
+        GST_DEBUG ("input event pointer moved over window at %d,%d",
+            event.xmotion.x, event.xmotion.y);
+        mouse_data = g_slice_new (struct mouse_event);
+        mouse_data->window = window;
+        mouse_data->event_type = "mouse-move";
+        mouse_data->button = 0;
+        mouse_data->posx = (double) event.xbutton.x;
+        mouse_data->posy = (double) event.xbutton.y;
+
+        g_main_context_invoke (window->navigation_context, (GSourceFunc)
+            gst_vulkan_window_mouse_event_cb, mouse_data);
+        break;
+#endif
+#endif
+      default:
+        GST_DEBUG ("unhandled XCB type: %u", event_code);
+        break;
+    }
+  }
+
+  return ret;
+}
+
+typedef struct _XCBEventSource
+{
+  GSource source;
+  GPollFD pfd;
+  uint32_t mask;
+  GstVulkanDisplayXCB *display_xcb;
+} XCBEventSource;
+
+static gboolean
+xcb_event_source_prepare (GSource * base, gint * timeout)
+{
+  *timeout = -1;
+  return FALSE;
+}
+
+static gboolean
+xcb_event_source_check (GSource * base)
+{
+  XCBEventSource *source = (XCBEventSource *) base;
+  gboolean retval;
+
+  retval = source->pfd.revents;
+
+  return retval;
+}
+
+static gboolean
+xcb_event_source_dispatch (GSource * base, GSourceFunc callback, gpointer data)
+{
+  XCBEventSource *source = (XCBEventSource *) base;
+
+  gboolean ret = _xcb_handle_event (source->display_xcb);
+
+  if (callback)
+    callback (data);
+
+  return ret;
+}
+
+static GSourceFuncs xcb_event_source_funcs = {
+  xcb_event_source_prepare,
+  xcb_event_source_check,
+  xcb_event_source_dispatch,
+  NULL
+};
+
+GSource *
+xcb_event_source_new (GstVulkanDisplayXCB * display_xcb)
+{
+  xcb_connection_t *connection;
+  XCBEventSource *source;
+
+  connection = GST_VULKAN_DISPLAY_XCB_CONNECTION (display_xcb);
+  g_return_val_if_fail (connection != NULL, NULL);
+
+  source = (XCBEventSource *)
+      g_source_new (&xcb_event_source_funcs, sizeof (XCBEventSource));
+  source->display_xcb = display_xcb;
+  source->pfd.fd = xcb_get_file_descriptor (connection);
+  source->pfd.events = G_IO_IN | G_IO_ERR;
+  g_source_add_poll (&source->source, &source->pfd);
+
+  return &source->source;
+}
diff --git a/ext/vulkan/xcb/xcb_event_source.h b/ext/vulkan/xcb/xcb_event_source.h
new file mode 100644 (file)
index 0000000..cd9b41c
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+ * GStreamer
+ * Copyright (C) 2012 Matthew Waters <ystreet00@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef __VULKAN_XCB_EVENT_SOURCE_H__
+#define __VULKAN_XCB_EVENT_SOURCE_H__
+
+#include <glib-object.h>
+#include "vkdisplay_xcb.h"
+
+GSource *
+xcb_event_source_new (GstVulkanDisplayXCB *display_xcb);
+
+#endif /* __VULKAN_XCB_EVENT_SOURCE_H__ */