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 ***
])
])
+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, [
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
DTLS_DIR=
endif
+if USE_VULKAN
+VULKAN_DIR=vulkan
+else
+VULKAN_DIR=
+endif
+
SUBDIRS=\
$(VOAACENC_DIR) \
$(ASSRENDER_DIR) \
$(HLS_DIR) \
$(WEBP_DIR) \
$(X265_DIR) \
- $(DTLS_DIR)
+ $(DTLS_DIR) \
+ $(VULKAN_DIR)
DIST_SUBDIRS = \
assrender \
rtmp \
webp \
x265 \
- dtls
+ dtls \
+ vulkan
include $(top_srcdir)/common/parallel-subdirs.mak
--- /dev/null
+vkconfig.h
--- /dev/null
+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)
+
+
--- /dev/null
+/*
+ * 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)
--- /dev/null
+/*
+ * 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_ */
--- /dev/null
+/*
+ * 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_ */
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+/*
+ * 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_ */
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+/*
+ * 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__ */
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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_ */
--- /dev/null
+/*
+ * 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, ¶ms,
+ 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, ¶ms,
+ 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, ¶ms,
+ 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);
+}
--- /dev/null
+/*
+ * 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_ */
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+/*
+ * 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_ */
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+/*
+ * 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_ */
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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_ */
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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, ©_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;
+ }
+}
--- /dev/null
+/*
+ * 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_ */
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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_ */
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+/*
+ * 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__ */
--- /dev/null
+## 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)
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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__ */
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+/*
+ * 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__ */
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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__ */