From 5de6dd9f40629562acf90e35e1fa58464d66617d Mon Sep 17 00:00:00 2001 From: Matthew Waters Date: Sat, 24 Oct 2015 17:29:05 +1100 Subject: [PATCH] new vulkan based video sink Currently xcb is the only winsys that is implemented and there's no redraws et al --- configure.ac | 67 +++ ext/Makefile.am | 12 +- ext/vulkan/.gitignore | 1 + ext/vulkan/Makefile.am | 58 ++ ext/vulkan/gstvulkan.c | 63 ++ ext/vulkan/vk.h | 45 ++ ext/vulkan/vk_fwd.h | 58 ++ ext/vulkan/vkdevice.c | 401 +++++++++++++ ext/vulkan/vkdevice.h | 82 +++ ext/vulkan/vkdisplay.c | 246 ++++++++ ext/vulkan/vkdisplay.h | 91 +++ ext/vulkan/vkerror.c | 129 ++++ ext/vulkan/vkerror.h | 50 ++ ext/vulkan/vkimagememory.c | 578 ++++++++++++++++++ ext/vulkan/vkimagememory.h | 116 ++++ ext/vulkan/vkinstance.c | 315 ++++++++++ ext/vulkan/vkinstance.h | 65 ++ ext/vulkan/vkmemory.c | 264 +++++++++ ext/vulkan/vkmemory.h | 94 +++ ext/vulkan/vkqueue.c | 65 ++ ext/vulkan/vkqueue.h | 52 ++ ext/vulkan/vksink.c | 491 +++++++++++++++ ext/vulkan/vksink.h | 73 +++ ext/vulkan/vkswapper.c | 957 ++++++++++++++++++++++++++++++ ext/vulkan/vkswapper.h | 91 +++ ext/vulkan/vkutils.c | 46 ++ ext/vulkan/vkutils.h | 33 ++ ext/vulkan/vkwindow.c | 247 ++++++++ ext/vulkan/vkwindow.h | 100 ++++ ext/vulkan/xcb/Makefile.am | 29 + ext/vulkan/xcb/vkdisplay_xcb.c | 165 ++++++ ext/vulkan/xcb/vkdisplay_xcb.h | 84 +++ ext/vulkan/xcb/vkwindow_xcb.c | 243 ++++++++ ext/vulkan/xcb/vkwindow_xcb.h | 86 +++ ext/vulkan/xcb/xcb_event_source.c | 251 ++++++++ ext/vulkan/xcb/xcb_event_source.h | 30 + 36 files changed, 5776 insertions(+), 2 deletions(-) create mode 100644 ext/vulkan/.gitignore create mode 100644 ext/vulkan/Makefile.am create mode 100644 ext/vulkan/gstvulkan.c create mode 100644 ext/vulkan/vk.h create mode 100644 ext/vulkan/vk_fwd.h create mode 100644 ext/vulkan/vkdevice.c create mode 100644 ext/vulkan/vkdevice.h create mode 100644 ext/vulkan/vkdisplay.c create mode 100644 ext/vulkan/vkdisplay.h create mode 100644 ext/vulkan/vkerror.c create mode 100644 ext/vulkan/vkerror.h create mode 100644 ext/vulkan/vkimagememory.c create mode 100644 ext/vulkan/vkimagememory.h create mode 100644 ext/vulkan/vkinstance.c create mode 100644 ext/vulkan/vkinstance.h create mode 100644 ext/vulkan/vkmemory.c create mode 100644 ext/vulkan/vkmemory.h create mode 100644 ext/vulkan/vkqueue.c create mode 100644 ext/vulkan/vkqueue.h create mode 100644 ext/vulkan/vksink.c create mode 100644 ext/vulkan/vksink.h create mode 100644 ext/vulkan/vkswapper.c create mode 100644 ext/vulkan/vkswapper.h create mode 100644 ext/vulkan/vkutils.c create mode 100644 ext/vulkan/vkutils.h create mode 100644 ext/vulkan/vkwindow.c create mode 100644 ext/vulkan/vkwindow.h create mode 100644 ext/vulkan/xcb/Makefile.am create mode 100644 ext/vulkan/xcb/vkdisplay_xcb.c create mode 100644 ext/vulkan/xcb/vkdisplay_xcb.h create mode 100644 ext/vulkan/xcb/vkwindow_xcb.c create mode 100644 ext/vulkan/xcb/vkwindow_xcb.h create mode 100644 ext/vulkan/xcb/xcb_event_source.c create mode 100644 ext/vulkan/xcb/xcb_event_source.h diff --git a/configure.ac b/configure.ac index f1e093acfb..c50647cca0 100644 --- a/configure.ac +++ b/configure.ac @@ -1464,6 +1464,55 @@ AC_SUBST(JPEG_LIBS) AC_SUBST(HAVE_JPEG) AM_CONDITIONAL(HAVE_JPEG, test "x$HAVE_JPEG" = "xyes") +dnl Vulkan +VULKAN_CONFIG_DEFINES="" + +PKG_CHECK_MODULES(XCB, xcb >= 1.10, HAVE_XCB=yes, HAVE_XCB=no) + +AM_CONDITIONAL(USE_XCB, test "x$HAVE_XCB" = "xyes") +if test "x$HAVE_XCB" = "xyes"; then + VULKAN_CONFIG_DEFINES="$VULKAN_CONFIG_DEFINES + #define GST_VULKAN_HAVE_WINDOW_XCB 1" +fi + +AC_CONFIG_COMMANDS([ext/vulkan/vkconfig.h], [ + outfile=vkconfig.h-tmp + cat > $outfile <<\_______EOF +/* vkconfig.h + * + * This is a generated file. Please modify `configure.ac' + */ + +#ifndef __GST_VULKAN_CONFIG_H__ +#define __GST_VULKAN_CONFIG_H__ + +#include + +G_BEGIN_DECLS + +_______EOF + + cat >>$outfile <<_______EOF +$vulkan_config_defines +_______EOF + + cat >>$outfile <<_______EOF + +G_END_DECLS + +#endif /* __GST_VULKAN_CONFIG_H__ */ +_______EOF + + + if cmp -s $outfile ext/vulkan/vkconfig.h; then + AC_MSG_NOTICE([ext/vulkan/vkconfig.h is unchanged]) + rm -f $outfile + else + mv $outfile ext/vulkan/vkconfig.h + fi +],[ +vulkan_config_defines='$VULKAN_CONFIG_DEFINES' +]) dnl *** sys plug-ins *** @@ -2851,6 +2900,22 @@ AG_GST_CHECK_FEATURE(QT, [Qt elements], qt, [ ]) ]) +dnl *** Vulkan *** +translit(dnm, m, l) AM_CONDITIONAL(USE_VULKAN, true) +AG_GST_CHECK_FEATURE(VULKAN, [Vulkan elements], vulkan, [ + HAVE_VULKAN=no + AC_CHECK_HEADER(vulkan/vulkan.h, [ + AC_CHECK_LIB(vulkan, vkCreateDevice, [ + VULKAN_LIBS="-lvulkan" + AC_SUBST(VULKAN_LIBS) + dnl TODO check platform support (x11, win32, wayland, android, etc) + if test "x$HAVE_XCB" = "xyes"; then + HAVE_VULKAN=yes + fi + ], []) + ], []) +]) + dnl *** libvisual *** translit(dnm, m, l) AM_CONDITIONAL(USE_LIBVISUAL, true) AG_GST_CHECK_FEATURE(LIBVISUAL, [libvisual visualization library], libvisual, [ @@ -3636,6 +3701,8 @@ ext/teletextdec/Makefile ext/gme/Makefile ext/spc/Makefile ext/timidity/Makefile +ext/vulkan/Makefile +ext/vulkan/xcb/Makefile ext/webp/Makefile ext/x265/Makefile ext/xvid/Makefile diff --git a/ext/Makefile.am b/ext/Makefile.am index d75f119087..f97ef44c87 100644 --- a/ext/Makefile.am +++ b/ext/Makefile.am @@ -406,6 +406,12 @@ else DTLS_DIR= endif +if USE_VULKAN +VULKAN_DIR=vulkan +else +VULKAN_DIR= +endif + SUBDIRS=\ $(VOAACENC_DIR) \ $(ASSRENDER_DIR) \ @@ -474,7 +480,8 @@ SUBDIRS=\ $(HLS_DIR) \ $(WEBP_DIR) \ $(X265_DIR) \ - $(DTLS_DIR) + $(DTLS_DIR) \ + $(VULKAN_DIR) DIST_SUBDIRS = \ assrender \ @@ -541,6 +548,7 @@ DIST_SUBDIRS = \ rtmp \ webp \ x265 \ - dtls + dtls \ + vulkan include $(top_srcdir)/common/parallel-subdirs.mak diff --git a/ext/vulkan/.gitignore b/ext/vulkan/.gitignore new file mode 100644 index 0000000000..dc690c8947 --- /dev/null +++ b/ext/vulkan/.gitignore @@ -0,0 +1 @@ +vkconfig.h diff --git a/ext/vulkan/Makefile.am b/ext/vulkan/Makefile.am new file mode 100644 index 0000000000..6c87b8c432 --- /dev/null +++ b/ext/vulkan/Makefile.am @@ -0,0 +1,58 @@ +plugin_LTLIBRARIES = libgstvulkan.la + +SUBDIRS = +DIST_SUBDIRS = xcb +DISTCLEANFILES = vkconfig.h + +libgstvulkan_la_SOURCES = \ + gstvulkan.c \ + vkdevice.c \ + vkdisplay.c \ + vkerror.c \ + vkimagememory.c \ + vkinstance.c \ + vkmemory.c \ + vkqueue.c \ + vksink.c \ + vkswapper.c \ + vkutils.c \ + vkwindow.c + +noinst_HEADERS = \ + vk.h \ + vk_fwd.h \ + vkdevice.h \ + vkdisplay.h \ + vkerror.h \ + vkimagememory.h \ + vkinstance.h \ + vkmemory.h \ + vkqueue.h \ + vksink.h \ + vkswapper.h \ + vkutils.h \ + vkwindow.h + +libgstvulkan_la_CFLAGS = \ + -I$(top_srcdir)/gst-libs \ + -I$(top_builddir)/gst-libs \ + $(GST_CFLAGS) \ + $(GST_BASE_CFLAGS) \ + $(GST_PLUGINS_BASE_CFLAGS) \ + $(VULKAN_CFLAGS) + +libgstvulkan_la_LIBADD = \ + $(GST_BASE_LIBS) \ + $(GST_PLUGINS_BASE_LIBS) \ + -lgstvideo-$(GST_API_VERSION) \ + $(VULKAN_LIBS) + +if USE_XCB +SUBDIRS += xcb +libgstvulkan_la_LIBADD += xcb/libgstvulkan-xcb.la +endif + +libgstvulkan_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) +libgstvulkan_la_LIBTOOLFLAGS = $(GST_PLUGIN_LIBTOOLFLAGS) + + diff --git a/ext/vulkan/gstvulkan.c b/ext/vulkan/gstvulkan.c new file mode 100644 index 0000000000..1fcc77be9c --- /dev/null +++ b/ext/vulkan/gstvulkan.c @@ -0,0 +1,63 @@ +/* + * GStreamer + * Copyright (C) 2015 Matthew Waters + * + * 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 +#endif + +#define GST_CAT_DEFAULT gst_gl_gstgl_debug +GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT); + +/* Register filters that make up the gstgl plugin */ +static gboolean +plugin_init (GstPlugin * plugin) +{ + GST_DEBUG_CATEGORY_INIT (gst_gl_gstgl_debug, "gstvulkan", 0, "gstvulkan"); + +#if GST_VULKAN_HAVE_WINDOW_X11 + if (g_getenv ("GST_VULKAN_XINITTHREADS")) + XInitThreads (); +#endif + + if (!gst_element_register (plugin, "vulkansink", + GST_RANK_NONE, GST_TYPE_VULKAN_SINK)) { + return FALSE; + } + + return TRUE; +} + +GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, + GST_VERSION_MINOR, + vulkan, + "Vulkan plugin", + plugin_init, VERSION, GST_LICENSE, GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN) diff --git a/ext/vulkan/vk.h b/ext/vulkan/vk.h new file mode 100644 index 0000000000..9e98fe17c1 --- /dev/null +++ b/ext/vulkan/vk.h @@ -0,0 +1,45 @@ +/* + * GStreamer + * Copyright (C) 2015 Matthew Waters + * + * 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 + +#define VK_PROTOTYPES +#include +#include +#include +#include + +#include "vkconfig.h" +#include "vk_fwd.h" + +#include "vkerror.h" +#include "vkinstance.h" +#include "vkdevice.h" +#include "vkqueue.h" +#include "vkdisplay.h" +#include "vkwindow.h" +#include "vkswapper.h" +#include "vkmemory.h" +#include "vkimagememory.h" + +#endif /* _VK_H_ */ diff --git a/ext/vulkan/vk_fwd.h b/ext/vulkan/vk_fwd.h new file mode 100644 index 0000000000..59b307d492 --- /dev/null +++ b/ext/vulkan/vk_fwd.h @@ -0,0 +1,58 @@ +/* + * GStreamer + * Copyright (C) 2015 Matthew Waters + * + * 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 + +G_BEGIN_DECLS + +typedef struct _GstVulkanInstance GstVulkanInstance; +typedef struct _GstVulkanInstanceClass GstVulkanInstanceClass; + +typedef struct _GstVulkanDevice GstVulkanDevice; +typedef struct _GstVulkanDeviceClass GstVulkanDeviceClass; + +typedef struct _GstVulkanQueue GstVulkanQueue; +typedef struct _GstVulkanQueueClass GstVulkanQueueClass; + +typedef struct _GstVulkanDisplay GstVulkanDisplay; +typedef struct _GstVulkanDisplayClass GstVulkanDisplayClass; +typedef struct _GstVulkanDisplayPrivate GstVulkanDisplayPrivate; + +typedef struct _GstVulkanWindow GstVulkanWindow; +typedef struct _GstVulkanWindowClass GstVulkanWindowClass; +typedef struct _GstVulkanWindowPrivate GstVulkanWindowPrivate; + +typedef struct _GstVulkanSwapper GstVulkanSwapper; +typedef struct _GstVulkanSwapperClass GstVulkanSwapperClass; + +typedef struct _GstVulkanMemory GstVulkanMemory; +typedef struct _GstVulkanMemoryAllocator GstVulkanMemoryAllocator; +typedef struct _GstVulkanMemoryAllocatorClass GstVulkanMemoryAllocatorClass; + +typedef struct _GstVulkanImageMemory GstVulkanImageMemory; +typedef struct _GstVulkanImageMemoryAllocator GstVulkanImageMemoryAllocator; +typedef struct _GstVulkanImageMemoryAllocatorClass GstVulkanImageMemoryAllocatorClass; + +G_END_DECLS + +#endif /* _VK_FWD_H_ */ diff --git a/ext/vulkan/vkdevice.c b/ext/vulkan/vkdevice.c new file mode 100644 index 0000000000..0c57196e89 --- /dev/null +++ b/ext/vulkan/vkdevice.c @@ -0,0 +1,401 @@ +/* + * GStreamer + * Copyright (C) 2015 Matthew Waters + * + * 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 + +static const char *device_validation_layers[] = { + "Threading", + "MemTracker", + "ObjectTracker", + "DrawState", + "ParamChecker", + "ShaderChecker", + "Swapchain", + "DeviceLimits", + "Image", +}; + +#define GST_CAT_DEFAULT gst_vulkan_device_debug +GST_DEBUG_CATEGORY (GST_CAT_DEFAULT); + +G_DEFINE_TYPE_WITH_CODE (GstVulkanDevice, gst_vulkan_device, GST_TYPE_OBJECT, + GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "vulkandevice", 0, + "Vulkan Device")); + +static void gst_vulkan_device_finalize (GObject * object); + +GstVulkanDevice * +gst_vulkan_device_new (GstVulkanInstance * instance) +{ + GstVulkanDevice *device = g_object_new (GST_TYPE_VULKAN_DEVICE, NULL); + + device->instance = gst_object_ref (instance); + /* FIXME: select this externally */ + device->device_index = 0; + + return device; +} + +static void +gst_vulkan_device_init (GstVulkanDevice * device) +{ +} + +static void +gst_vulkan_device_class_init (GstVulkanDeviceClass * device_class) +{ + GObjectClass *gobject_class = (GObjectClass *) device_class; + + gobject_class->finalize = gst_vulkan_device_finalize; +} + +static void +gst_vulkan_device_finalize (GObject * object) +{ + GstVulkanDevice *device = GST_VULKAN_DEVICE (object); + + if (device->cmd_pool.handle) + vkDestroyCommandPool (device->device, device->cmd_pool); + device->cmd_pool.handle = 0; + + if (device->device) + vkDestroyDevice (device->device); + device->device = NULL; + + if (device->instance) + gst_object_unref (device->instance); + device->instance = NULL; +} + +static const gchar * +_device_type_to_string (VkPhysicalDeviceType type) +{ + switch (type) { + case VK_PHYSICAL_DEVICE_TYPE_OTHER: + return "other"; + case VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU: + return "integrated"; + case VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU: + return "discrete"; + case VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU: + return "virtual"; + case VK_PHYSICAL_DEVICE_TYPE_CPU: + return "CPU"; + default: + return "unknown"; + } +} + +static gboolean +_physical_device_info (GstVulkanDevice * device, GError ** error) +{ + VkPhysicalDeviceProperties props; + VkPhysicalDevice gpu; + VkResult err; + + gpu = gst_vulkan_device_get_physical_device (device); + + err = vkGetPhysicalDeviceProperties (gpu, &props); + if (gst_vulkan_error_to_g_error (err, error, + "vkGetPhysicalDeviceProperties") < 0) + return FALSE; + + GST_INFO_OBJECT (device, "device name %s type %s api version %u, " + "driver version %u vendor ID 0x%x, device ID 0x%x", props.deviceName, + _device_type_to_string (props.deviceType), props.apiVersion, + props.driverVersion, props.vendorId, props.deviceId); + + return TRUE; +} + +gboolean +gst_vulkan_device_open (GstVulkanDevice * device, GError ** error) +{ + const char *extension_names[64]; + uint32_t enabled_extension_count = 0; + uint32_t device_extension_count = 0; + VkExtensionProperties *device_extensions = NULL; + uint32_t enabled_layer_count = 0; + uint32_t device_layer_count = 0; + VkLayerProperties *device_layers; + gboolean have_swapchain_ext; + gboolean validation_found; + VkPhysicalDevice gpu; + VkResult err; + guint i; + + g_return_val_if_fail (GST_IS_VULKAN_DEVICE (device), FALSE); + + if (!_physical_device_info (device, error)) + return FALSE; + + gpu = gst_vulkan_device_get_physical_device (device); + + /* Look for validation layers */ + err = vkEnumerateDeviceLayerProperties (gpu, &device_layer_count, NULL); + if (gst_vulkan_error_to_g_error (err, error, + "vkEnumerateDeviceLayerProperties") < 0) + return FALSE; + + device_layers = g_new0 (VkLayerProperties, device_layer_count); + err = + vkEnumerateDeviceLayerProperties (gpu, &device_layer_count, + device_layers); + if (gst_vulkan_error_to_g_error (err, error, + "vkEnumerateDeviceLayerProperties") < 0) { + g_free (device_layers); + return FALSE; + } + + validation_found = + _check_for_all_layers (G_N_ELEMENTS (device_validation_layers), + device_validation_layers, device_layer_count, device_layers); + g_free (device_layers); + device_layers = NULL; + if (!validation_found) { + g_error ("vkEnumerateDeviceLayerProperties failed to find" + "a required validation layer.\n\n" + "Please look at the Getting Started guide for additional " + "information.\nvkCreateDevice Failure"); + return FALSE; + } + enabled_layer_count = G_N_ELEMENTS (device_validation_layers); + + err = + vkEnumerateDeviceExtensionProperties (gpu, NULL, + &device_extension_count, NULL); + if (gst_vulkan_error_to_g_error (err, error, + "vkEnumerateDeviceExtensionProperties") < 0) + return FALSE; + + have_swapchain_ext = 0; + enabled_extension_count = 0; + memset (extension_names, 0, sizeof (extension_names)); + device_extensions = g_new0 (VkExtensionProperties, device_extension_count); + err = vkEnumerateDeviceExtensionProperties (gpu, NULL, + &device_extension_count, device_extensions); + if (gst_vulkan_error_to_g_error (err, error, + "vkEnumerateDeviceExtensionProperties") < 0) { + g_free (device_extensions); + return FALSE; + } + + for (uint32_t i = 0; i < device_extension_count; i++) { + if (!strcmp ("VK_EXT_KHR_device_swapchain", device_extensions[i].extName)) { + have_swapchain_ext = TRUE; + extension_names[enabled_extension_count++] = + "VK_EXT_KHR_device_swapchain"; + } + g_assert (enabled_extension_count < 64); + } + if (!have_swapchain_ext) { + g_error ("vkEnumerateDeviceExtensionProperties failed to find the " + "\"VK_EXT_KHR_device_swapchain\" extension.\n\nDo you have a compatible " + "Vulkan installable client driver (ICD) installed?\nPlease " + "look at the Getting Started guide for additional " + "information.\nvkCreateInstance Failure"); + } + g_free (device_extensions); + + err = vkGetPhysicalDeviceProperties (gpu, &device->gpu_props); + if (gst_vulkan_error_to_g_error (err, error, + "vkGetPhysicalDeviceProperties") < 0) + return FALSE; + + err = vkGetPhysicalDeviceMemoryProperties (gpu, &device->memory_properties); + if (gst_vulkan_error_to_g_error (err, error, + "vkGetPhysicalDeviceProperties") < 0) + return FALSE; + + err = vkGetPhysicalDeviceFeatures (gpu, &device->gpu_features); + if (gst_vulkan_error_to_g_error (err, error, + "vkGetPhysicalDeviceFeatures") < 0) + return FALSE; + + /* Call with NULL data to get count */ + err = + vkGetPhysicalDeviceQueueFamilyProperties (gpu, &device->n_queue_families, + NULL); + if (gst_vulkan_error_to_g_error (err, error, + "vkGetPhysicalDeviceQueueFamilyProperties") < 0) + return FALSE; + g_assert (device->n_queue_families >= 1); + + device->queue_family_props = + g_new0 (VkQueueFamilyProperties, device->n_queue_families); + err = + vkGetPhysicalDeviceQueueFamilyProperties (gpu, &device->n_queue_families, + device->queue_family_props); + if (gst_vulkan_error_to_g_error (err, error, + "vkGetPhysicalDeviceQueueFamilyProperties") < 0) + return FALSE; + + /* FIXME: allow overriding/selecting */ + for (i = 0; i < device->n_queue_families; i++) { + if (device->queue_family_props[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) + break; + } + if (i >= device->n_queue_families) { + g_set_error (error, GST_VULKAN_ERROR, + GST_VULKAN_ERROR_INITIALIZATION_FAILED, + "Failed to find a compatible queue family"); + return FALSE; + } + device->queue_family_id = i; + device->n_queues = 1; + + { + const VkDeviceQueueCreateInfo queue_info = { + .sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO, + .pNext = NULL, + .queueFamilyIndex = device->queue_family_id, + .queueCount = device->n_queues, + }; + VkDeviceCreateInfo device_info = { + .sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO, + .pNext = NULL, + .queueRecordCount = 1, + .pRequestedQueues = &queue_info, + .layerCount = enabled_layer_count, + .ppEnabledLayerNames = (const char *const *) device_validation_layers, + .extensionCount = enabled_extension_count, + .ppEnabledExtensionNames = (const char *const *) extension_names, + .pEnabledFeatures = NULL, // If specific features are required, pass them in here + }; + + err = vkCreateDevice (gpu, &device_info, &device->device); + if (gst_vulkan_error_to_g_error (err, error, "vkCreateDevice") < 0) + return FALSE; + } + { + const VkCmdPoolCreateInfo cmd_pool_info = { + .sType = VK_STRUCTURE_TYPE_CMD_POOL_CREATE_INFO, + .pNext = NULL, + .queueFamilyIndex = device->queue_family_id, + .flags = 0, + }; + err = + vkCreateCommandPool (device->device, &cmd_pool_info, &device->cmd_pool); + if (gst_vulkan_error_to_g_error (err, error, "vkCreateCommandPool") < 0) + return FALSE; + } + + return TRUE; +} + +GstVulkanQueue * +gst_vulkan_device_get_queue (GstVulkanDevice * device, guint32 queue_family, + guint32 queue_i, GError ** error) +{ + GstVulkanQueue *ret; + VkResult err; + + g_return_val_if_fail (GST_IS_VULKAN_DEVICE (device), NULL); + g_return_val_if_fail (device->device != NULL, NULL); + g_return_val_if_fail (queue_family < device->n_queues, NULL); + g_return_val_if_fail (queue_i < + device->queue_family_props[queue_family].queueCount, NULL); + + ret = g_object_new (GST_TYPE_VULKAN_QUEUE, NULL); + ret->device = gst_object_ref (device); + ret->family = queue_family; + ret->index = queue_i; + + err = vkGetDeviceQueue (device->device, queue_family, queue_i, &ret->queue); + if (gst_vulkan_error_to_g_error (err, error, "vkGetDeviceQueue") < 0) { + gst_object_unref (ret); + return NULL; + } + + return ret; +} + +gpointer +gst_vulkan_device_get_proc_address (GstVulkanDevice * device, + const gchar * name) +{ + g_return_val_if_fail (GST_IS_VULKAN_DEVICE (device), NULL); + g_return_val_if_fail (device->device != NULL, NULL); + g_return_val_if_fail (name != NULL, NULL); + + GST_TRACE_OBJECT (device, "%s", name); + + return vkGetDeviceProcAddr (device->device, name); +} + +GstVulkanInstance * +gst_vulkan_device_get_instance (GstVulkanDevice * device) +{ + g_return_val_if_fail (GST_IS_VULKAN_DEVICE (device), NULL); + + return device->instance ? gst_object_ref (device->instance) : NULL; +} + +VkPhysicalDevice +gst_vulkan_device_get_physical_device (GstVulkanDevice * device) +{ + g_return_val_if_fail (GST_IS_VULKAN_DEVICE (device), NULL); + + if (device->instance->physical_devices == NULL) + return NULL; + if (device->device_index >= device->instance->n_physical_devices) + return NULL; + + return device->instance->physical_devices[device->device_index]; +} + +gboolean +gst_vulkan_device_create_cmd_buffer (GstVulkanDevice * device, + VkCmdBuffer * cmd, GError ** error) +{ + VkResult err; + + const VkCmdBufferCreateInfo cmd_info = { + .sType = VK_STRUCTURE_TYPE_CMD_BUFFER_CREATE_INFO, + .pNext = NULL, + .cmdPool = device->cmd_pool, + .level = VK_CMD_BUFFER_LEVEL_PRIMARY, + .flags = 0, + }; + + err = vkCreateCommandBuffer (device->device, &cmd_info, cmd); + if (gst_vulkan_error_to_g_error (err, error, "vkCreateCommandBuffer") < 0) + return FALSE; + + GST_LOG_OBJECT (device, "created cmd buffer %p", cmd); + + return TRUE; +} + +void +gst_vulkan_device_close (GstVulkanDevice * device) +{ + g_return_if_fail (GST_IS_VULKAN_DEVICE (device)); + g_return_if_fail (device->device != NULL); + + g_free (device->queue_family_props); +} diff --git a/ext/vulkan/vkdevice.h b/ext/vulkan/vkdevice.h new file mode 100644 index 0000000000..fa1591eb14 --- /dev/null +++ b/ext/vulkan/vkdevice.h @@ -0,0 +1,82 @@ +/* + * GStreamer + * Copyright (C) 2015 Matthew Waters + * + * 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 +#include "vk.h" + +G_BEGIN_DECLS + +#define GST_TYPE_VULKAN_DEVICE (gst_vulkan_device_get_type()) +#define GST_VULKAN_DEVICE(o) (G_TYPE_CHECK_INSTANCE_CAST((o), GST_TYPE_VULKAN_DEVICE, GstVulkanDevice)) +#define GST_VULKAN_DEVICE_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), GST_TYPE_VULKAN_DEVICE, GstVulkanDeviceClass)) +#define GST_IS_VULKAN_DEVICE(o) (G_TYPE_CHECK_INSTANCE_TYPE((o), GST_TYPE_VULKAN_DEVICE)) +#define GST_IS_VULKAN_DEVICE_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE((k), GST_TYPE_VULKAN_DEVICE)) +#define GST_VULKAN_DEVICE_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS((o), GST_TYPE_VULKAN_DEVICE, GstVulkanDeviceClass)) +GType gst_vulkan_device_get_type (void); + +struct _GstVulkanDevice +{ + GstObject parent; + + GstVulkanInstance *instance; + + guint device_index; + VkDevice device; /* hides a pointer */ + VkPhysicalDeviceProperties gpu_props; + VkPhysicalDeviceFeatures gpu_features; + VkPhysicalDeviceMemoryProperties memory_properties; + + VkQueueFamilyProperties *queue_family_props; + guint32 n_queue_families; + + guint32 queue_family_id; + guint32 n_queues; + + VkCmdPool cmd_pool; +}; + +struct _GstVulkanDeviceClass +{ + GstObjectClass parent_class; +}; + +GstVulkanDevice * gst_vulkan_device_new (GstVulkanInstance * instance); +GstVulkanInstance * gst_vulkan_device_get_instance (GstVulkanDevice * device); +gboolean gst_vulkan_device_open (GstVulkanDevice * device, + GError ** error); +void gst_vulkan_device_close (GstVulkanDevice * device); + +gpointer gst_vulkan_device_get_proc_address (GstVulkanDevice * device, + const gchar * name); +GstVulkanQueue * gst_vulkan_device_get_queue (GstVulkanDevice * device, + guint32 queue_family, + guint32 queue_i, + GError ** error); +VkPhysicalDevice gst_vulkan_device_get_physical_device (GstVulkanDevice * device); +gboolean gst_vulkan_device_create_cmd_buffer (GstVulkanDevice * device, + VkCmdBuffer * cmd, + GError ** error); + +G_END_DECLS + +#endif /* _VK_DEVICE_H_ */ diff --git a/ext/vulkan/vkdisplay.c b/ext/vulkan/vkdisplay.c new file mode 100644 index 0000000000..aa8d729851 --- /dev/null +++ b/ext/vulkan/vkdisplay.c @@ -0,0 +1,246 @@ +/* + * GStreamer + * Copyright (C) 2007 David A. Schleef + * Copyright (C) 2008 Julien Isorce + * Copyright (C) 2008 Filippo Argiolas + * Copyright (C) 2013 Matthew Waters + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "vkdisplay.h" + +#if GST_VULKAN_HAVE_WINDOW_X11 +#include "x11/vkdisplay_x11.h" +#endif +#if GST_VULKAN_HAVE_WINDOW_XCB +#include "xcb/vkdisplay_xcb.h" +#endif + +#define GST_CAT_DEFAULT gst_vulkan_display_debug +GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT); + +static void +_init_debug (void) +{ + static volatile gsize _init = 0; + + if (g_once_init_enter (&_init)) { + GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "vulkandisplay", 0, + "Vulkan display"); + g_once_init_leave (&_init, 1); + } +} + +G_DEFINE_TYPE_WITH_CODE (GstVulkanDisplay, gst_vulkan_display, GST_TYPE_OBJECT, + _init_debug ()); + +#define GST_VULKAN_DISPLAY_GET_PRIVATE(o) \ + (G_TYPE_INSTANCE_GET_PRIVATE((o), GST_TYPE_VULKAN_DISPLAY, GstVulkanDisplayPrivate)) + +enum +{ + SIGNAL_0, + CREATE_CONTEXT, + LAST_SIGNAL +}; + +/* static guint gst_vulkan_display_signals[LAST_SIGNAL] = { 0 }; */ + +static void gst_vulkan_display_finalize (GObject * object); +static gpointer gst_vulkan_display_default_get_handle (GstVulkanDisplay * + display); +static gpointer gst_vulkan_display_default_get_platform_handle (GstVulkanDisplay + * display); +static GstVulkanWindow + * gst_vulkan_display_default_create_window (GstVulkanDisplay * display); + +struct _GstVulkanDisplayPrivate +{ + gint dummy; +}; + +static void +gst_vulkan_display_class_init (GstVulkanDisplayClass * klass) +{ + g_type_class_add_private (klass, sizeof (GstVulkanDisplayPrivate)); + + klass->get_handle = gst_vulkan_display_default_get_handle; + klass->get_platform_handle = gst_vulkan_display_default_get_platform_handle; + klass->create_window = gst_vulkan_display_default_create_window; + + G_OBJECT_CLASS (klass)->finalize = gst_vulkan_display_finalize; +} + +static void +gst_vulkan_display_init (GstVulkanDisplay * display) +{ + display->priv = GST_VULKAN_DISPLAY_GET_PRIVATE (display); + display->type = GST_VULKAN_DISPLAY_TYPE_ANY; +} + +static void +gst_vulkan_display_finalize (GObject * object) +{ + G_OBJECT_CLASS (gst_vulkan_display_parent_class)->finalize (object); +} + +/** + * gst_vulkan_display_new: + * + * Returns: (transfer full): a new #GstVulkanDisplay + * + * Since: 1.10 + */ +GstVulkanDisplay * +gst_vulkan_display_new (void) +{ + GstVulkanDisplay *display = NULL; + const gchar *user_choice, *platform_choice; + + _init_debug (); + + user_choice = g_getenv ("GST_GL_WINDOW"); + platform_choice = g_getenv ("GST_GL_PLATFORM"); + GST_INFO ("creating a display, user choice:%s (platform: %s)", + GST_STR_NULL (user_choice), GST_STR_NULL (platform_choice)); + +#if GST_VULKAN_HAVE_WINDOW_XCB + if (!display && (!user_choice || g_strstr_len (user_choice, 3, "xcb"))) + display = GST_VULKAN_DISPLAY (gst_vulkan_display_xcb_new (NULL)); +#endif +#if GST_VULKAN_HAVE_WINDOW_X11 + if (!display && (!user_choice || g_strstr_len (user_choice, 3, "x11"))) + display = GST_VULKAN_DISPLAY (gst_vulkan_display_x11_new (NULL)); +#endif + if (!display) { + /* subclass returned a NULL window */ + GST_WARNING ("Could not create display. user specified %s " + "(platform: %s), creating dummy", + GST_STR_NULL (user_choice), GST_STR_NULL (platform_choice)); + + return g_object_new (GST_TYPE_VULKAN_DISPLAY, NULL); + } + + return display; +} + +/** + * gst_vulkan_display_get_handle: + * @display: a #GstVulkanDisplay + * + * Returns: the winsys specific handle of @display + * + * Since: 1.10 + */ +gpointer +gst_vulkan_display_get_handle (GstVulkanDisplay * display) +{ + GstVulkanDisplayClass *klass; + + g_return_val_if_fail (GST_IS_VULKAN_DISPLAY (display), NULL); + klass = GST_VULKAN_DISPLAY_GET_CLASS (display); + g_return_val_if_fail (klass->get_handle != NULL, NULL); + + return klass->get_handle (display); +} + +static gpointer +gst_vulkan_display_default_get_handle (GstVulkanDisplay * display) +{ + return 0; +} + +/** + * gst_vulkan_display_get_platform_handle: + * @display: a #GstVulkanDisplay + * + * Returns: the winsys specific handle of @display for use with the + * VK_EXT_KHR_swapchain extension. + * + * Since: 1.10 + */ +gpointer +gst_vulkan_display_get_platform_handle (GstVulkanDisplay * display) +{ + GstVulkanDisplayClass *klass; + + g_return_val_if_fail (GST_IS_VULKAN_DISPLAY (display), NULL); + klass = GST_VULKAN_DISPLAY_GET_CLASS (display); + g_return_val_if_fail (klass->get_handle != NULL, NULL); + + return klass->get_platform_handle (display); +} + +static gpointer +gst_vulkan_display_default_get_platform_handle (GstVulkanDisplay * display) +{ + return (gpointer) gst_vulkan_display_get_handle (display); +} + +/** + * gst_vulkan_display_get_handle_type: + * @display: a #GstVulkanDisplay + * + * Returns: the #GstVulkanDisplayType of @display + * + * Since: 1.10 + */ +GstVulkanDisplayType +gst_vulkan_display_get_handle_type (GstVulkanDisplay * display) +{ + g_return_val_if_fail (GST_IS_VULKAN_DISPLAY (display), + GST_VULKAN_DISPLAY_TYPE_NONE); + + return display->type; +} + +/** + * gst_vulkan_display_create_window: + * @display: a #GstVulkanDisplay + * + * Returns: a new #GstVulkanWindow for @display or %NULL. + */ +GstVulkanWindow * +gst_vulkan_display_create_window (GstVulkanDisplay * display) +{ + GstVulkanDisplayClass *klass; + GstVulkanWindow *window; + + g_return_val_if_fail (GST_IS_VULKAN_DISPLAY (display), NULL); + klass = GST_VULKAN_DISPLAY_GET_CLASS (display); + g_return_val_if_fail (klass->create_window != NULL, NULL); + + window = klass->create_window (display); + + if (window) { + GST_OBJECT_LOCK (display); + display->windows = g_list_prepend (display->windows, window); + GST_OBJECT_UNLOCK (display); + } + + return window; +} + +static GstVulkanWindow * +gst_vulkan_display_default_create_window (GstVulkanDisplay * display) +{ + return gst_vulkan_window_new (display); +} diff --git a/ext/vulkan/vkdisplay.h b/ext/vulkan/vkdisplay.h new file mode 100644 index 0000000000..371eec9073 --- /dev/null +++ b/ext/vulkan/vkdisplay.h @@ -0,0 +1,91 @@ +/* + * GStreamer + * Copyright (C) 2007 David A. Schleef + * Copyright (C) 2008 Julien Isorce + * Copyright (C) 2008 Filippo Argiolas + * Copyright (C) 2013 Matthew Waters + * + * 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 + +#include + +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 +{ + /* */ + GstObject object; + + GstVulkanDisplayType type; + + /* */ + GList *windows; /* OBJECT lock */ + + GstVulkanDisplayPrivate *priv; +}; + +struct _GstVulkanDisplayClass +{ + GstObjectClass object_class; + + gpointer (*get_handle) (GstVulkanDisplay * display); + gpointer (*get_platform_handle) (GstVulkanDisplay * display); /* default chains up to @get_handle */ + GstVulkanWindow * (*create_window) (GstVulkanDisplay * display); +}; + +GstVulkanDisplay *gst_vulkan_display_new (void); + +gpointer gst_vulkan_display_get_handle (GstVulkanDisplay * display); +GstVulkanDisplayType gst_vulkan_display_get_handle_type (GstVulkanDisplay * display); +gpointer gst_vulkan_display_get_platform_handle (GstVulkanDisplay * display); +GstVulkanWindow * gst_vulkan_display_create_window (GstVulkanDisplay * display); + +G_END_DECLS + +#endif /* __GST_VULKAN_DISPLAY_H__ */ diff --git a/ext/vulkan/vkerror.c b/ext/vulkan/vkerror.c new file mode 100644 index 0000000000..fca2d6a6ad --- /dev/null +++ b/ext/vulkan/vkerror.c @@ -0,0 +1,129 @@ +/* + * GStreamer + * Copyright (C) 2015 Matthew Waters + * + * 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 + +#include "vkerror.h" + +/* *INDENT-OFF* */ +static const struct +{ + VkResult result; + const char *str; +} vk_result_string_map[] = { + {VK_ERROR_OUT_OF_HOST_MEMORY, "Out Of Host Memory"}, + {VK_ERROR_OUT_OF_DEVICE_MEMORY, "Out of Device Memory"}, + {VK_ERROR_INITIALIZATION_FAILED, "Initialization Failed"}, + {VK_ERROR_DEVICE_LOST, "Device Lost"}, + {VK_ERROR_MEMORY_MAP_FAILED, "Map Failed"}, + {VK_ERROR_LAYER_NOT_PRESENT, "Layer Not Present"}, + {VK_ERROR_EXTENSION_NOT_PRESENT, "Extension Not Present"}, + {VK_ERROR_INCOMPATIBLE_DRIVER, "Incompatible Driver"}, +}; + +static const struct +{ + VkResult result; + GstVulkanError gst_enum; +} vk_result_gst_error_map[] = { + {VK_ERROR_OUT_OF_HOST_MEMORY, GST_VULKAN_ERROR_OUT_OF_HOST_MEMORY}, + {VK_ERROR_OUT_OF_DEVICE_MEMORY, VK_ERROR_OUT_OF_DEVICE_MEMORY}, + {VK_ERROR_INITIALIZATION_FAILED, GST_VULKAN_ERROR_INITIALIZATION_FAILED}, + {VK_ERROR_DEVICE_LOST, GST_VULKAN_ERROR_DEVICE_LOST}, + {VK_ERROR_MEMORY_MAP_FAILED, GST_VULKAN_ERROR_MEMORY_MAP_FAILED}, + {VK_ERROR_LAYER_NOT_PRESENT, GST_VULKAN_ERROR_LAYER_NOT_PRESENT}, + {VK_ERROR_EXTENSION_NOT_PRESENT, GST_VULKAN_ERROR_EXTENSION_NOT_PRESENT}, + {VK_ERROR_INCOMPATIBLE_DRIVER, GST_VULKAN_ERROR_INCOMPATIBLE_DRIVER}, +}; +/* *INDENT-ON* */ + +GQuark +gst_vulkan_error_quark (void) +{ + return g_quark_from_static_string ("gst-vulkan-error"); +} + +static const char * +_vk_result_to_string (VkResult result) +{ + int i; + + if (result >= 0) + return NULL; + if (result < VK_RESULT_BEGIN_RANGE) + return "Unknown Error"; + + for (i = 0; i < G_N_ELEMENTS (vk_result_string_map); i++) { + if (result == vk_result_string_map[i].result) + return vk_result_string_map[i].str; + } + + return "Unknown Error"; +} + +static GstVulkanError +_vk_result_to_g_error_enum (VkResult result) +{ + int i; + + if (result >= 0) + return 0; + if (result < VK_RESULT_BEGIN_RANGE) + return 0; + + for (i = 0; i < G_N_ELEMENTS (vk_result_gst_error_map); i++) { + if (result == vk_result_gst_error_map[i].result) + return vk_result_gst_error_map[i].gst_enum; + } + + return GST_VULKAN_ERROR_FAILED; +} + +VkResult +gst_vulkan_error_to_g_error (VkResult result, GError ** error, + const char *format, ...) +{ + GstVulkanError gst_enum; + const char *result_str; + gchar *string; + va_list args; + + if (error == NULL) + /* we don't have an error to set */ + return result; + + result_str = _vk_result_to_string (result); + if (result_str == NULL) + return result; + + gst_enum = _vk_result_to_g_error_enum (result); + + va_start (args, format); + g_vasprintf (&string, format, args); + va_end (args); + + g_set_error (error, GST_VULKAN_ERROR, gst_enum, "%s: %s", result_str, string); + + return result; +} diff --git a/ext/vulkan/vkerror.h b/ext/vulkan/vkerror.h new file mode 100644 index 0000000000..6f5e4cca86 --- /dev/null +++ b/ext/vulkan/vkerror.h @@ -0,0 +1,50 @@ +/* + * GStreamer + * Copyright (C) 2015 Matthew Waters + * + * 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 +#include + +G_BEGIN_DECLS + +#define GST_VULKAN_ERROR (gst_vulkan_error_quark ()) +GQuark gst_vulkan_error_quark (void); + +typedef enum +{ + GST_VULKAN_ERROR_FAILED = 0, + GST_VULKAN_ERROR_OUT_OF_HOST_MEMORY = -1, + GST_VULKAN_ERROR_OUT_OF_DEVICE_MEMORY = -2, + GST_VULKAN_ERROR_INITIALIZATION_FAILED = -3, + GST_VULKAN_ERROR_DEVICE_LOST = -4, + GST_VULKAN_ERROR_MEMORY_MAP_FAILED = -5, + GST_VULKAN_ERROR_LAYER_NOT_PRESENT = -6, + GST_VULKAN_ERROR_EXTENSION_NOT_PRESENT = -7, + GST_VULKAN_ERROR_INCOMPATIBLE_DRIVER = -8, +} GstVulkanError; + +/* only fills error iff error != NULL and result < 0 */ +VkResult gst_vulkan_error_to_g_error (VkResult result, GError ** error, const char * format, ...) G_GNUC_PRINTF (3, 4); + +G_END_DECLS + +#endif /* _VK_INSTANCE_H_ */ diff --git a/ext/vulkan/vkimagememory.c b/ext/vulkan/vkimagememory.c new file mode 100644 index 0000000000..e5edf9062b --- /dev/null +++ b/ext/vulkan/vkimagememory.c @@ -0,0 +1,578 @@ +/* + * GStreamer + * Copyright (C) 2015 Matthew Waters + * + * 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); +} diff --git a/ext/vulkan/vkimagememory.h b/ext/vulkan/vkimagememory.h new file mode 100644 index 0000000000..077c30f1da --- /dev/null +++ b/ext/vulkan/vkimagememory.h @@ -0,0 +1,116 @@ +/* + * GStreamer + * Copyright (C) 2015 Matthew Waters + * + * 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 +#include +#include + +#include + +#include + +G_BEGIN_DECLS + +#define GST_TYPE_VULKAN_IMAGE_MEMORY_ALLOCATOR (gst_vulkan_image_memory_allocator_get_type()) +GType gst_vulkan_image_memory_allocator_get_type(void); + +#define GST_IS_VULKAN_IMAGE_MEMORY_ALLOCATOR(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_VULKAN_IMAGE_MEMORY_ALLOCATOR)) +#define GST_IS_VULKAN_IMAGE_MEMORY_ALLOCATOR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_VULKAN_IMAGE_MEMORY_ALLOCATOR)) +#define GST_VULKAN_IMAGE_MEMORY_ALLOCATOR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_VULKAN_MEMORY_ALLOCATOR, GstVulkanImageMemoryAllocatorClass)) +#define GST_VULKAN_IMAGE_MEMORY_ALLOCATOR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_VULKAN_MEMORY_ALLOCATOR, GstVulkanImageMemoryAllocator)) +#define GST_VULKAN_IMAGE_MEMORY_ALLOCATOR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_VULKAN_MEMORY_ALLOCATOR, GstVulkanImageMemoryAllocatorClass)) +#define GST_VULKAN_IMAGE_MEMORY_ALLOCATOR_CAST(obj) ((GstVulkanImageMemoryAllocator *)(obj)) + +#define GST_VULKAN_IMAGE_MEMORY_ALLOCATOR_NAME "VulkanImage" + +struct _GstVulkanImageMemory +{ + GstMemory parent; + + GstVulkanDevice * device; + + VkImage image; + VkImageLayout image_layout; + VkImageView view; + GstVulkanMemory *vk_mem; + + VkImageCreateInfo create_info; + VkMemoryRequirements requirements; + VkImageFormatProperties format_properties; + + GMutex lock; + gboolean wrapped; + GDestroyNotify notify; + gpointer user_data; +}; + +/** + * GstVulkanImageMemoryAllocator + * + * Opaque #GstVulkanImageMemoryAllocator struct + */ +struct _GstVulkanImageMemoryAllocator +{ + GstAllocator parent; +}; + +/** + * GstVulkanImageMemoryAllocatorClass: + * + * The #GstVulkanImageMemoryAllocatorClass only contains private data + */ +struct _GstVulkanImageMemoryAllocatorClass +{ + GstAllocatorClass parent_class; +}; + +void gst_vulkan_image_memory_init_once (void); +gboolean gst_is_vulkan_image_memory (GstMemory * mem); + +GstMemory * gst_vulkan_image_memory_alloc (GstVulkanDevice * device, + VkFormat format, + gsize width, + gsize height, + VkImageTiling tiling, + VkImageUsageFlags usage, + VkMemoryPropertyFlags mem_prop_flags); + +GstMemory * gst_vulkan_image_memory_wrapped (GstVulkanDevice * device, + VkImage image, + VkFormat format, + gsize width, + gsize height, + VkImageTiling tiling, + VkImageUsageFlags usage, + gpointer user_data, + GDestroyNotify notify); +gboolean gst_vulkan_image_memory_set_layout (GstVulkanImageMemory * vk_mem, + VkImageLayout, + VkImageMemoryBarrier * barrier); + +VkFormat gst_vulkan_format_from_video_format (GstVideoFormat v_format, + guint plane); + +G_END_DECLS + +#endif /* _VK_IMAGE_MEMORY_H_ */ diff --git a/ext/vulkan/vkinstance.c b/ext/vulkan/vkinstance.c new file mode 100644 index 0000000000..900429d686 --- /dev/null +++ b/ext/vulkan/vkinstance.c @@ -0,0 +1,315 @@ +/* + * GStreamer + * Copyright (C) 2015 Matthew Waters + * + * 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 + +#define APP_SHORT_NAME "GStreamer" + +static const char *instance_validation_layers[] = { + /* FIXME: until the loader stops segfaulting/hanging we don't have any + * validation layers for the instance */ + "Threading", + "MemTracker", + "ObjectTracker", + "DrawState", + "ParamChecker", + "ShaderChecker", + "Swapchain", + "DeviceLimits", + "Image", +}; + +#define GST_CAT_DEFAULT gst_vulkan_instance_debug +GST_DEBUG_CATEGORY (GST_CAT_DEFAULT); +GST_DEBUG_CATEGORY (GST_VULKAN_DEBUG_CAT); + +#define gst_vulkan_instance_parent_class parent_class +G_DEFINE_TYPE_WITH_CODE (GstVulkanInstance, gst_vulkan_instance, + GST_TYPE_OBJECT, GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, + "vulkaninstance", 0, "Vulkan Instance"); + GST_DEBUG_CATEGORY_INIT (GST_VULKAN_DEBUG_CAT, + "vulkandebug", 0, "Vulkan Debug")); + +static void gst_vulkan_instance_finalize (GObject * object); + +GstVulkanInstance * +gst_vulkan_instance_new (void) +{ + return g_object_new (GST_TYPE_VULKAN_INSTANCE, NULL); +} + +static void +gst_vulkan_instance_init (GstVulkanInstance * instance) +{ +} + +static void +gst_vulkan_instance_class_init (GstVulkanInstanceClass * instance_class) +{ + gst_vulkan_memory_init_once (); + gst_vulkan_image_memory_init_once (); + + G_OBJECT_CLASS (instance_class)->finalize = gst_vulkan_instance_finalize; +} + +static void +gst_vulkan_instance_finalize (GObject * object) +{ + GstVulkanInstance *instance = GST_VULKAN_INSTANCE (object); + + if (instance->instance) + vkDestroyInstance (instance->instance); + instance->instance = NULL; +} + +static VkBool32 +_gst_vk_debug_callback (VkFlags msgFlags, VkDbgObjectType objType, + uint64_t srcObject, size_t location, int32_t msgCode, + const char *pLayerPrefix, const char *pMsg, void *pUserData) +{ + if (msgFlags & VK_DBG_REPORT_ERROR_BIT) { + GST_CAT_ERROR (GST_VULKAN_DEBUG_CAT, "[%s] Code %d : %s", pLayerPrefix, + msgCode, pMsg); + } else if (msgFlags & VK_DBG_REPORT_WARN_BIT) { + GST_CAT_WARNING (GST_VULKAN_DEBUG_CAT, "[%s] Code %d : %s", pLayerPrefix, + msgCode, pMsg); + } else if (msgFlags & VK_DBG_REPORT_INFO_BIT) { + GST_CAT_LOG (GST_VULKAN_DEBUG_CAT, "[%s] Code %d : %s", pLayerPrefix, + msgCode, pMsg); + } else if (msgFlags & VK_DBG_REPORT_PERF_WARN_BIT) { + GST_CAT_FIXME (GST_VULKAN_DEBUG_CAT, "[%s] Code %d : %s", pLayerPrefix, + msgCode, pMsg); + } else if (msgFlags & VK_DBG_REPORT_DEBUG_BIT) { + GST_CAT_TRACE (GST_VULKAN_DEBUG_CAT, "[%s] Code %d : %s", pLayerPrefix, + msgCode, pMsg); + } else { + return FALSE; + } + + /* + * false indicates that layer should not bail-out of an + * API call that had validation failures. This may mean that the + * app dies inside the driver due to invalid parameter(s). + * That's what would happen without validation layers, so we'll + * keep that behavior here. + */ + return FALSE; +} + +gboolean +gst_vulkan_instance_open (GstVulkanInstance * instance, GError ** error) +{ + VkExtensionProperties *instance_extensions; + char *extension_names[64]; /* FIXME: make dynamic */ + VkLayerProperties *instance_layers; + uint32_t instance_extension_count = 0; + uint32_t enabled_extension_count = 0; + uint32_t instance_layer_count = 0; + uint32_t enabled_layer_count = 0; + gboolean validation_found; + gboolean have_swapchain_ext = FALSE; + VkResult err; + + /* Look for validation layers */ + err = vkEnumerateInstanceLayerProperties (&instance_layer_count, NULL); + if (gst_vulkan_error_to_g_error (err, error, + "vKEnumerateInstanceLayerProperties") < 0) + return FALSE; + + instance_layers = g_new0 (VkLayerProperties, instance_layer_count); + err = + vkEnumerateInstanceLayerProperties (&instance_layer_count, + instance_layers); + if (gst_vulkan_error_to_g_error (err, error, + "vKEnumerateInstanceLayerProperties") < 0) { + g_free (instance_layers); + return FALSE; + } + + /* TODO: allow outside selection */ + validation_found = + _check_for_all_layers (G_N_ELEMENTS (instance_validation_layers), + instance_validation_layers, instance_layer_count, instance_layers); + if (!validation_found) { + g_error ("vkEnumerateInstanceLayerProperties failed to find" + "required validation layer.\n\n" + "Please look at the Getting Started guide for additional " + "information.\nvkCreateInstance Failure"); + } + enabled_layer_count = G_N_ELEMENTS (instance_validation_layers); + + err = + vkEnumerateInstanceExtensionProperties (NULL, &instance_extension_count, + NULL); + if (gst_vulkan_error_to_g_error (err, error, + "vkEnumerateInstanceExtensionProperties") < 0) { + g_free (instance_layers); + return FALSE; + } + + memset (extension_names, 0, sizeof (extension_names)); + instance_extensions = + g_new0 (VkExtensionProperties, instance_extension_count); + err = + vkEnumerateInstanceExtensionProperties (NULL, &instance_extension_count, + instance_extensions); + if (gst_vulkan_error_to_g_error (err, error, + "vkEnumerateInstanceExtensionProperties") < 0) { + g_free (instance_layers); + g_free (instance_extensions); + return FALSE; + } + + /* TODO: allow outside selection */ + for (uint32_t i = 0; i < instance_extension_count; i++) { + if (!g_strcmp0 ("VK_EXT_KHR_swapchain", instance_extensions[i].extName)) { + have_swapchain_ext = TRUE; + extension_names[enabled_extension_count++] = + (gchar *) "VK_EXT_KHR_swapchain"; + } + if (!g_strcmp0 (VK_DEBUG_REPORT_EXTENSION_NAME, + instance_extensions[i].extName)) { + extension_names[enabled_extension_count++] = + (gchar *) VK_DEBUG_REPORT_EXTENSION_NAME; + } + g_assert (enabled_extension_count < 64); + } + if (!have_swapchain_ext) { + g_error ("vkEnumerateInstanceExtensionProperties failed to find the " + "\"VK_EXT_KHR_swapchain\" extension.\n\nDo you have a compatible " + "Vulkan installable client driver (ICD) installed?\nPlease " + "look at the Getting Started guide for additional " + "information.\nvkCreateInstance Failure"); + } + + { + const VkApplicationInfo app = { + .sType = VK_STRUCTURE_TYPE_APPLICATION_INFO, + .pNext = NULL, + .pAppName = APP_SHORT_NAME, + .appVersion = 0, + .pEngineName = APP_SHORT_NAME, + .engineVersion = 0, + .apiVersion = VK_API_VERSION, + }; + VkInstanceCreateInfo inst_info = { + .sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO, + .pNext = NULL, + .pAppInfo = &app, + .pAllocCb = NULL, + .layerCount = enabled_layer_count, + .ppEnabledLayerNames = (const char *const *) instance_validation_layers, + .extensionCount = enabled_extension_count, + .ppEnabledExtensionNames = (const char *const *) extension_names, + }; + + err = vkCreateInstance (&inst_info, &instance->instance); + if (gst_vulkan_error_to_g_error (err, error, "vkCreateInstance") < 0) { + g_free (instance_layers); + g_free (instance_extensions); + return FALSE; + } + } + + g_free (instance_layers); + g_free (instance_extensions); + + err = + vkEnumeratePhysicalDevices (instance->instance, + &instance->n_physical_devices, NULL); + if (gst_vulkan_error_to_g_error (err, error, + "vkEnumeratePhysicalDevices") < 0) + return FALSE; + g_assert (instance->n_physical_devices > 0); + instance->physical_devices = + g_new0 (VkPhysicalDevice, instance->n_physical_devices); + err = + vkEnumeratePhysicalDevices (instance->instance, + &instance->n_physical_devices, instance->physical_devices); + if (gst_vulkan_error_to_g_error (err, error, + "vkEnumeratePhysicalDevices") < 0) + return FALSE; + + instance->dbgCreateMsgCallback = (PFN_vkDbgCreateMsgCallback) + gst_vulkan_instance_get_proc_address (instance, "vkDbgCreateMsgCallback"); + if (!instance->dbgCreateMsgCallback) { + g_set_error (error, GST_VULKAN_ERROR, GST_VULKAN_ERROR_FAILED, + "Failed to retreive vkDbgCreateMsgCallback"); + return FALSE; + } + instance->dbgDestroyMsgCallback = (PFN_vkDbgDestroyMsgCallback) + gst_vulkan_instance_get_proc_address (instance, + "vkDbgDestroyMsgCallback"); + if (!instance->dbgDestroyMsgCallback) { + g_set_error (error, GST_VULKAN_ERROR, GST_VULKAN_ERROR_FAILED, + "Failed to retreive vkDbgDestroyMsgCallback"); + return FALSE; + } + instance->dbgBreakCallback = + (PFN_vkDbgMsgCallback) gst_vulkan_instance_get_proc_address (instance, + "vkDbgBreakCallback"); + if (!instance->dbgBreakCallback) { + g_set_error (error, GST_VULKAN_ERROR, GST_VULKAN_ERROR_FAILED, + "Failed to retreive vkDbgBreakCallback"); + return FALSE; + } + + err = instance->dbgCreateMsgCallback (instance->instance, + VK_DBG_REPORT_ERROR_BIT | VK_DBG_REPORT_WARN_BIT | VK_DBG_REPORT_INFO_BIT + | VK_DBG_REPORT_DEBUG_BIT | VK_DBG_REPORT_PERF_WARN_BIT, + _gst_vk_debug_callback, NULL, &instance->msg_callback); + if (gst_vulkan_error_to_g_error (err, error, "vkDbgCreateMsgCallback") < 0) + return FALSE; + + return TRUE; +} + +gpointer +gst_vulkan_instance_get_proc_address (GstVulkanInstance * instance, + const gchar * name) +{ + g_return_val_if_fail (GST_IS_VULKAN_INSTANCE (instance), NULL); + g_return_val_if_fail (instance->instance != NULL, NULL); + g_return_val_if_fail (name != NULL, NULL); + + GST_TRACE_OBJECT (instance, "%s", name); + + return vkGetInstanceProcAddr (instance->instance, name); +} + +void +gst_vulkan_instance_close (GstVulkanInstance * instance) +{ + g_return_if_fail (GST_IS_VULKAN_INSTANCE (instance)); + g_return_if_fail (instance->instance != NULL); + + if (instance->dbgDestroyMsgCallback) + instance->dbgDestroyMsgCallback (instance->instance, + instance->msg_callback); + + g_free (instance->physical_devices); +} diff --git a/ext/vulkan/vkinstance.h b/ext/vulkan/vkinstance.h new file mode 100644 index 0000000000..cb25007c3d --- /dev/null +++ b/ext/vulkan/vkinstance.h @@ -0,0 +1,65 @@ +/* + * GStreamer + * Copyright (C) 2015 Matthew Waters + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef _VK_INSTANCE_H_ +#define _VK_INSTANCE_H_ + +#include "vk.h" + +G_BEGIN_DECLS + +#define GST_TYPE_VULKAN_INSTANCE (gst_vulkan_instance_get_type()) +#define GST_VULKAN_INSTANCE(o) (G_TYPE_CHECK_INSTANCE_CAST((o), GST_TYPE_VULKAN_INSTANCE, GstVulkanInstance)) +#define GST_VULKAN_INSTANCE_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), GST_TYPE_VULKAN_INSTANCE, GstVulkanInstanceClass)) +#define GST_IS_VULKAN_INSTANCE(o) (G_TYPE_CHECK_INSTANCE_TYPE((o), GST_TYPE_VULKAN_INSTANCE)) +#define GST_IS_VULKAN_INSTANCE_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE((k), GST_TYPE_VULKAN_INSTANCE)) +#define GST_VULKAN_INSTANCE_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS((o), GST_TYPE_VULKAN_INSTANCE, GstVulkanInstanceClass)) +GType gst_vulkan_instance_get_type (void); + +struct _GstVulkanInstance +{ + GstObject parent; + + VkInstance instance; /* hides a pointer */ + VkPhysicalDevice *physical_devices; /* hides a pointer */ + guint32 n_physical_devices; + + VkDbgMsgCallback msg_callback; + PFN_vkDbgCreateMsgCallback dbgCreateMsgCallback; + PFN_vkDbgDestroyMsgCallback dbgDestroyMsgCallback; + PFN_vkDbgMsgCallback dbgBreakCallback; +}; + +struct _GstVulkanInstanceClass +{ + GstObjectClass parent_class; +}; + +GstVulkanInstance * gst_vulkan_instance_new (void); +gboolean gst_vulkan_instance_open (GstVulkanInstance * instance, + GError ** error); +void gst_vulkan_instance_close (GstVulkanInstance * instance); + +gpointer gst_vulkan_instance_get_proc_address (GstVulkanInstance * instance, + const gchar * name); + +G_END_DECLS + +#endif /* _VK_INSTANCE_H_ */ diff --git a/ext/vulkan/vkmemory.c b/ext/vulkan/vkmemory.c new file mode 100644 index 0000000000..9360b836e8 --- /dev/null +++ b/ext/vulkan/vkmemory.c @@ -0,0 +1,264 @@ +/* + * GStreamer + * Copyright (C) 2015 Matthew Waters + * + * 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 + +#include "vkmemory.h" + +/** + * SECTION:vkmemory + * @short_description: memory subclass for Vulkan device memory + * @see_also: #GstMemory, #GstAllocator + * + * GstVulkanMemory is a #GstMemory subclass providing support for the mapping of + * Vulkan device memory. + */ + +#define GST_CAT_DEFUALT GST_CAT_VULKAN_MEMORY +GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFUALT); + +static GstAllocator *_vulkan_memory_allocator; + +static void +_vk_mem_init (GstVulkanMemory * mem, GstAllocator * allocator, + GstMemory * parent, GstVulkanDevice * device, guint32 memory_type_index, + GstAllocationParams * params, gsize size, + VkMemoryPropertyFlags mem_prop_flags, gpointer user_data, + GDestroyNotify notify) +{ + gsize align = gst_memory_alignment, offset = 0, maxsize = size; + GstMemoryFlags flags = 0; + + if (params) { + flags = params->flags; + align |= params->align; + offset = params->prefix; + maxsize += params->prefix + params->padding; + if ((maxsize & align) != 0) + maxsize += ~(maxsize & align) + 1; + } + + gst_memory_init (GST_MEMORY_CAST (mem), flags, allocator, parent, maxsize, + align, offset, size); + + mem->device = gst_object_ref (device); + mem->alloc_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOC_INFO; + mem->alloc_info.pNext = NULL; + mem->alloc_info.allocationSize = (VkDeviceSize) mem->mem.maxsize; + mem->alloc_info.memoryTypeIndex = memory_type_index; + mem->properties = mem_prop_flags; + mem->notify = notify; + mem->user_data = user_data; + + g_mutex_init (&mem->lock); + + GST_CAT_DEBUG (GST_CAT_VULKAN_MEMORY, "new GL buffer memory:%p size:%" + G_GSIZE_FORMAT, mem, maxsize); +} + +static GstVulkanMemory * +_vk_mem_new (GstAllocator * allocator, GstMemory * parent, + GstVulkanDevice * device, guint32 memory_type_index, + GstAllocationParams * params, gsize size, + VkMemoryPropertyFlags mem_props_flags, gpointer user_data, + GDestroyNotify notify) +{ + GstVulkanMemory *mem = g_slice_new0 (GstVulkanMemory); + GError *error = NULL; + VkResult err; + + _vk_mem_init (mem, allocator, parent, device, memory_type_index, params, + size, mem_props_flags, user_data, notify); + + err = vkAllocMemory (device->device, &mem->alloc_info, &mem->mem_ptr); + if (gst_vulkan_error_to_g_error (err, &error, "vkAllocMemory") < 0) { + GST_CAT_ERROR (GST_CAT_VULKAN_MEMORY, "Failed to allocate device memory %s", + error->message); + gst_memory_unref ((GstMemory *) mem); + g_clear_error (&error); + return NULL; + } + + return mem; +} + +static gpointer +_vk_mem_map_full (GstVulkanMemory * mem, GstMapInfo * info, gsize size) +{ + gpointer data; + VkResult err; + GError *error = NULL; + + if ((mem->properties & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) == 0) { + GST_CAT_ERROR (GST_CAT_VULKAN_MEMORY, "Cannot map host-invisible memory"); + return NULL; + } + + err = vkMapMemory (mem->device->device, mem->mem_ptr, 0, size, 0, &data); + if (gst_vulkan_error_to_g_error (err, &error, "vkMapMemory") < 0) { + GST_CAT_ERROR (GST_CAT_VULKAN_MEMORY, "Failed to map device memory %s", + error->message); + g_clear_error (&error); + return NULL; + } + + return data; +} + +static void +_vk_mem_unmap_full (GstVulkanMemory * mem, GstMapInfo * info) +{ + vkUnmapMemory (mem->device->device, mem->mem_ptr); +} + +static GstMemory * +_vk_mem_copy (GstVulkanMemory * src, gssize offset, gssize size) +{ + return NULL; +} + +static GstMemory * +_vk_mem_share (GstVulkanMemory * mem, gssize offset, gssize size) +{ + return NULL; +} + +static gboolean +_vk_mem_is_span (GstVulkanMemory * mem1, GstVulkanMemory * mem2, gsize * offset) +{ + return FALSE; +} + +static GstMemory * +_vk_mem_alloc (GstAllocator * allocator, gsize size, + GstAllocationParams * params) +{ + g_critical ("Subclass should override GstAllocatorClass::alloc() function"); + + return NULL; +} + +static void +_vk_mem_free (GstAllocator * allocator, GstMemory * memory) +{ + GstVulkanMemory *mem = (GstVulkanMemory *) memory; + + GST_CAT_TRACE (GST_CAT_VULKAN_MEMORY, "freeing buffer memory:%p " + "id:%" G_GUINT64_FORMAT, mem, mem->mem_ptr.handle); + + g_mutex_clear (&mem->lock); + + if (mem->notify) + mem->notify (mem->user_data); + + vkFreeMemory (mem->device->device, mem->mem_ptr); + + gst_object_unref (mem->device); +} + +/** + * gst_vulkan_memory_alloc: + * @device:a #GstVulkanDevice + * @memory_type_index: the Vulkan memory type index + * @params: a #GstAllocationParams + * @size: the size to allocate + * + * Allocated a new #GstVulkanMemory. + * + * Returns: a #GstMemory object backed by a vulkan device memory + */ +GstMemory * +gst_vulkan_memory_alloc (GstVulkanDevice * device, guint32 memory_type_index, + GstAllocationParams * params, gsize size, VkMemoryPropertyFlags mem_flags) +{ + GstVulkanMemory *mem; + + mem = _vk_mem_new (_vulkan_memory_allocator, NULL, device, memory_type_index, + params, size, mem_flags, NULL, NULL); + + return (GstMemory *) mem; +} + +G_DEFINE_TYPE (GstVulkanMemoryAllocator, gst_vulkan_memory_allocator, + GST_TYPE_ALLOCATOR); + +static void +gst_vulkan_memory_allocator_class_init (GstVulkanMemoryAllocatorClass * klass) +{ + GstAllocatorClass *allocator_class = (GstAllocatorClass *) klass; + + allocator_class->alloc = _vk_mem_alloc; + allocator_class->free = _vk_mem_free; +} + +static void +gst_vulkan_memory_allocator_init (GstVulkanMemoryAllocator * allocator) +{ + GstAllocator *alloc = GST_ALLOCATOR_CAST (allocator); + + alloc->mem_type = GST_VULKAN_MEMORY_ALLOCATOR_NAME; + alloc->mem_map_full = (GstMemoryMapFullFunction) _vk_mem_map_full; + alloc->mem_unmap_full = (GstMemoryUnmapFullFunction) _vk_mem_unmap_full; + alloc->mem_copy = (GstMemoryCopyFunction) _vk_mem_copy; + alloc->mem_share = (GstMemoryShareFunction) _vk_mem_share; + alloc->mem_is_span = (GstMemoryIsSpanFunction) _vk_mem_is_span; +} + +/** + * gst_vulkan_memory_init_once: + * + * Initializes the Vulkan memory allocator. It is safe to call this function + * multiple times. This must be called before any other #GstVulkanMemory operation. + */ +void +gst_vulkan_memory_init_once (void) +{ + static volatile gsize _init = 0; + + if (g_once_init_enter (&_init)) { + GST_DEBUG_CATEGORY_INIT (GST_CAT_VULKAN_MEMORY, "vulkanmemory", 0, + "Vulkan Memory"); + + _vulkan_memory_allocator = + g_object_new (gst_vulkan_memory_allocator_get_type (), NULL); + + gst_allocator_register (GST_VULKAN_MEMORY_ALLOCATOR_NAME, + gst_object_ref (_vulkan_memory_allocator)); + g_once_init_leave (&_init, 1); + } +} + +/** + * gst_is_vulkan_memory: + * @mem:a #GstMemory + * + * Returns: whether the memory at @mem is a #GstVulkanMemory + */ +gboolean +gst_is_vulkan_memory (GstMemory * mem) +{ + return mem != NULL && mem->allocator != NULL && + g_type_is_a (G_OBJECT_TYPE (mem->allocator), + GST_TYPE_VULKAN_MEMORY_ALLOCATOR); +} diff --git a/ext/vulkan/vkmemory.h b/ext/vulkan/vkmemory.h new file mode 100644 index 0000000000..6983e83530 --- /dev/null +++ b/ext/vulkan/vkmemory.h @@ -0,0 +1,94 @@ +/* + * GStreamer + * Copyright (C) 2015 Matthew Waters + * + * 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 +#include +#include + +#include + +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; + + /* */ + GMutex lock; + + /* */ + GDestroyNotify notify; + gpointer user_data; + + VkMemoryAllocInfo alloc_info; + VkMemoryPropertyFlags properties; +}; + +/** + * GstVulkanMemoryAllocator + * + * Opaque #GstVulkanMemoryAllocator struct + */ +struct _GstVulkanMemoryAllocator +{ + GstAllocator parent; +}; + +/** + * GstVulkanMemoryAllocatorClass: + * + * The #GstVulkanMemoryAllocatorClass only contains private data + */ +struct _GstVulkanMemoryAllocatorClass +{ + GstAllocatorClass parent_class; +}; + +void gst_vulkan_memory_init_once (void); +gboolean gst_is_vulkan_memory (GstMemory * mem); + +GstMemory * gst_vulkan_memory_alloc (GstVulkanDevice * device, + guint32 memory_type_index, + GstAllocationParams * params, + gsize size, + VkMemoryPropertyFlags mem_prop_flags); + +G_END_DECLS + +#endif /* _GST_VULKAN_BASE_BUFFER_H_ */ diff --git a/ext/vulkan/vkqueue.c b/ext/vulkan/vkqueue.c new file mode 100644 index 0000000000..a8a2611b42 --- /dev/null +++ b/ext/vulkan/vkqueue.c @@ -0,0 +1,65 @@ +/* + * GStreamer + * Copyright (C) 2015 Matthew Waters + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "vkqueue.h" + +#define GST_CAT_DEFAULT gst_vulkan_queue_debug +GST_DEBUG_CATEGORY (GST_CAT_DEFAULT); + +G_DEFINE_TYPE_WITH_CODE (GstVulkanQueue, gst_vulkan_queue, GST_TYPE_OBJECT, + GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "vulkanqueue", 0, + "Vulkan Queue")); + +static void gst_vulkan_queue_dispose (GObject * object); + +static void +gst_vulkan_queue_init (GstVulkanQueue * device) +{ +} + +static void +gst_vulkan_queue_class_init (GstVulkanQueueClass * device_class) +{ + GObjectClass *gobject_class = (GObjectClass *) device_class; + + gobject_class->dispose = gst_vulkan_queue_dispose; +} + +static void +gst_vulkan_queue_dispose (GObject * object) +{ + GstVulkanQueue *queue = GST_VULKAN_QUEUE (object); + + if (queue->device) + gst_object_unref (queue->device); + queue->device = NULL; +} + +GstVulkanDevice * +gst_vulkan_queue_get_device (GstVulkanQueue * queue) +{ + g_return_val_if_fail (GST_IS_VULKAN_QUEUE (queue), NULL); + + return queue->device ? gst_object_ref (queue->device) : NULL; +} diff --git a/ext/vulkan/vkqueue.h b/ext/vulkan/vkqueue.h new file mode 100644 index 0000000000..dccb175a82 --- /dev/null +++ b/ext/vulkan/vkqueue.h @@ -0,0 +1,52 @@ +/* + * GStreamer + * Copyright (C) 2015 Matthew Waters + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef _VK_QUEUE_H_ +#define _VK_QUEUE_H_ + +#include "vk.h" + +#define GST_TYPE_VULKAN_QUEUE (gst_vulkan_queue_get_type()) +#define GST_VULKAN_QUEUE(o) (G_TYPE_CHECK_INSTANCE_CAST((o), GST_TYPE_VULKAN_QUEUE, GstVulkanQueue)) +#define GST_VULKAN_QUEUE_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), GST_TYPE_VULKAN_QUEUE, GstVulkanQueueClass)) +#define GST_IS_VULKAN_QUEUE(o) (G_TYPE_CHECK_INSTANCE_TYPE((o), GST_TYPE_VULKAN_QUEUE)) +#define GST_IS_VULKAN_QUEUE_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE((k), GST_TYPE_VULKAN_QUEUE)) +#define GST_VULKAN_QUEUE_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS((o), GST_TYPE_VULKAN_QUEUE, GstVulkanQueueClass)) +GType gst_vulkan_queue_get_type (void); + +struct _GstVulkanQueue +{ + GstObject parent; + + GstVulkanDevice *device; + + VkQueue queue; /* hides a pointer */ + guint32 family; + guint32 index; +}; + +struct _GstVulkanQueueClass +{ + GstObjectClass parent_class; +}; + +GstVulkanDevice * gst_vulkan_queue_get_device (GstVulkanQueue * queue); + +#endif /* _VK_QUEUE_H_ */ diff --git a/ext/vulkan/vksink.c b/ext/vulkan/vksink.c new file mode 100644 index 0000000000..30a46ea63d --- /dev/null +++ b/ext/vulkan/vksink.c @@ -0,0 +1,491 @@ +/* + * GStreamer + * Copyright (C) 2015 Matthew Waters + * + * 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 + +#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 "); + + gst_element_class_add_pad_template (element_class, + gst_static_pad_template_get (&gst_vulkan_sink_template)); + + gobject_class->finalize = gst_vulkan_sink_finalize; + + gstelement_class->change_state = gst_vulkan_sink_change_state; + gstelement_class->set_context = gst_vulkan_sink_set_context; + gstbasesink_class->query = GST_DEBUG_FUNCPTR (gst_vulkan_sink_query); + gstbasesink_class->set_caps = gst_vulkan_sink_set_caps; + gstbasesink_class->get_caps = gst_vulkan_sink_get_caps; + gstbasesink_class->get_times = gst_vulkan_sink_get_times; + gstbasesink_class->prepare = gst_vulkan_sink_prepare; + + gstvideosink_class->show_frame = + GST_DEBUG_FUNCPTR (gst_vulkan_sink_show_frame); +} + +static void +gst_vulkan_sink_init (GstVulkanSink * vk_sink) +{ + vk_sink->force_aspect_ratio = DEFAULT_FORCE_ASPECT_RATIO; + vk_sink->par_n = DEFAULT_PIXEL_ASPECT_RATIO_N; + vk_sink->par_d = DEFAULT_PIXEL_ASPECT_RATIO_D; + +// g_mutex_init (&vk_sink->drawing_lock); +} + +static void +gst_vulkan_sink_finalize (GObject * object) +{ +// GstVulkanSink *vk_sink = GST_VULKAN_SINK (object); +// g_mutex_clear (&vk_sink->drawing_lock); + +// GST_DEBUG ("finalized"); + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static void +gst_vulkan_sink_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec) +{ + GstVulkanSink *vk_sink = GST_VULKAN_SINK (object); + + switch (prop_id) { + case PROP_FORCE_ASPECT_RATIO: + vk_sink->force_aspect_ratio = g_value_get_boolean (value); + break; + case PROP_PIXEL_ASPECT_RATIO: + vk_sink->par_n = gst_value_get_fraction_numerator (value); + vk_sink->par_d = gst_value_get_fraction_denominator (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gst_vulkan_sink_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec) +{ + GstVulkanSink *vk_sink = GST_VULKAN_SINK (object); + + switch (prop_id) { + case PROP_FORCE_ASPECT_RATIO: + g_value_set_boolean (value, vk_sink->force_aspect_ratio); + break; + case PROP_PIXEL_ASPECT_RATIO: + gst_value_set_fraction (value, vk_sink->par_n, vk_sink->par_d); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static gboolean +gst_vulkan_sink_query (GstBaseSink * bsink, GstQuery * query) +{ +// GstVulkanSink *vk_sink = GST_VULKAN_SINK (bsink); + gboolean res = FALSE; + + switch (GST_QUERY_TYPE (query)) { + default: + res = GST_BASE_SINK_CLASS (parent_class)->query (bsink, query); + break; + } + + return res; +} + +static void +gst_vulkan_sink_set_context (GstElement * element, GstContext * context) +{ +// GstVulkanSink *vk_sink = GST_VULKAN_SINK (element); + + GST_ELEMENT_CLASS (parent_class)->set_context (element, context); +} + +static GstStateChangeReturn +gst_vulkan_sink_change_state (GstElement * element, GstStateChange transition) +{ + GstVulkanSink *vk_sink = GST_VULKAN_SINK (element); + GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS; + GError *error = NULL; + + GST_DEBUG ("changing state: %s => %s", + gst_element_state_get_name (GST_STATE_TRANSITION_CURRENT (transition)), + gst_element_state_get_name (GST_STATE_TRANSITION_NEXT (transition))); + + switch (transition) { + case GST_STATE_CHANGE_NULL_TO_READY: + vk_sink->instance = gst_vulkan_instance_new (); + if (!gst_vulkan_instance_open (vk_sink->instance, &error)) { + GST_ELEMENT_ERROR (vk_sink, RESOURCE, NOT_FOUND, + ("Failed to create vulkan instance"), ("%s", error->message)); + return GST_STATE_CHANGE_FAILURE; + } + vk_sink->device = gst_vulkan_device_new (vk_sink->instance); + if (!gst_vulkan_device_open (vk_sink->device, &error)) { + GST_ELEMENT_ERROR (vk_sink, RESOURCE, NOT_FOUND, + ("Failed to create vulkan device"), ("%s", error->message)); + return GST_STATE_CHANGE_FAILURE; + } + + if (!(vk_sink->display = gst_vulkan_display_new ())) { + GST_ELEMENT_ERROR (vk_sink, RESOURCE, NOT_FOUND, + ("Failed to connect to the window sysytem"), (NULL)); + return GST_STATE_CHANGE_FAILURE; + } + + if (!(vk_sink->window = + gst_vulkan_display_create_window (vk_sink->display))) { + GST_ELEMENT_ERROR (vk_sink, RESOURCE, NOT_FOUND, + ("Failed to create a window"), (NULL)); + return GST_STATE_CHANGE_FAILURE; + } + + if (!gst_vulkan_window_open (vk_sink->window, &error)) { + GST_ELEMENT_ERROR (vk_sink, RESOURCE, NOT_FOUND, + ("Failed to open window"), ("%s", error->message)); + return GST_STATE_CHANGE_FAILURE; + } + + if (!(vk_sink->swapper = + gst_vulkan_swapper_new (vk_sink->device, vk_sink->window))) { + GST_ELEMENT_ERROR (vk_sink, RESOURCE, NOT_FOUND, + ("Failed to create a swapper"), (NULL)); + return GST_STATE_CHANGE_FAILURE; + } + break; + case GST_STATE_CHANGE_READY_TO_PAUSED: + break; + case GST_STATE_CHANGE_PAUSED_TO_PLAYING: + break; + default: + break; + } + + ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); + if (ret == GST_STATE_CHANGE_FAILURE) + return ret; + + switch (transition) { + case GST_STATE_CHANGE_PLAYING_TO_PAUSED: + break; + case GST_STATE_CHANGE_PAUSED_TO_READY: + break; + case GST_STATE_CHANGE_READY_TO_NULL: + gst_object_unref (vk_sink->swapper); + vk_sink->swapper = NULL; + gst_object_unref (vk_sink->display); + vk_sink->display = NULL; + gst_vulkan_window_close (vk_sink->window); + gst_object_unref (vk_sink->window); + vk_sink->window = NULL; + gst_vulkan_device_close (vk_sink->device); + gst_object_unref (vk_sink->device); + vk_sink->device = NULL; + gst_vulkan_instance_close (vk_sink->instance); + gst_object_unref (vk_sink->instance); + vk_sink->instance = NULL; + break; + default: + break; + } + + return ret; +} + +static void +gst_vulkan_sink_get_times (GstBaseSink * bsink, GstBuffer * buf, + GstClockTime * start, GstClockTime * end) +{ + GstVulkanSink *vk_sink = GST_VULKAN_SINK (bsink); + + if (GST_BUFFER_TIMESTAMP_IS_VALID (buf)) { + *start = GST_BUFFER_TIMESTAMP (buf); + if (GST_BUFFER_DURATION_IS_VALID (buf)) + *end = *start + GST_BUFFER_DURATION (buf); + else { + if (GST_VIDEO_INFO_FPS_N (&vk_sink->v_info) > 0) { + *end = *start + + gst_util_uint64_scale_int (GST_SECOND, + GST_VIDEO_INFO_FPS_D (&vk_sink->v_info), + GST_VIDEO_INFO_FPS_N (&vk_sink->v_info)); + } + } + } +} + +static GstCaps * +gst_vulkan_sink_get_caps (GstBaseSink * bsink, GstCaps * filter) +{ + GstVulkanSink *vk_sink = GST_VULKAN_SINK (bsink); + GstCaps *tmp = NULL; + GstCaps *result = NULL; + GError *error = NULL; + + if (vk_sink->swapper) { + if (!(result = + gst_vulkan_swapper_get_supported_caps (vk_sink->swapper, &error))) { + GST_ELEMENT_ERROR (bsink, RESOURCE, NOT_FOUND, ("%s", error->message), + (NULL)); + g_clear_error (&error); + return NULL; + } + return result; + } + + tmp = gst_pad_get_pad_template_caps (GST_BASE_SINK_PAD (bsink)); + + if (filter) { + GST_DEBUG_OBJECT (bsink, "intersecting with filter caps %" GST_PTR_FORMAT, + filter); + + result = gst_caps_intersect_full (filter, tmp, GST_CAPS_INTERSECT_FIRST); + gst_caps_unref (tmp); + } else { + result = tmp; + } + + GST_DEBUG_OBJECT (bsink, "returning caps: %" GST_PTR_FORMAT, result); + + return result; +} + +static gboolean +_configure_display_from_info (GstVulkanSink * vk_sink, GstVideoInfo * vinfo) +{ + guint display_ratio_num, display_ratio_den; + gint display_par_n, display_par_d; + gint par_n, par_d; + gint width, height; + gboolean ok; + + width = GST_VIDEO_INFO_WIDTH (vinfo); + height = GST_VIDEO_INFO_HEIGHT (vinfo); + + par_n = GST_VIDEO_INFO_PAR_N (vinfo); + par_d = GST_VIDEO_INFO_PAR_D (vinfo); + + if (!par_n) + par_n = 1; + + /* get display's PAR */ + if (vk_sink->par_n != 0 && vk_sink->par_d != 0) { + display_par_n = vk_sink->par_n; + display_par_d = vk_sink->par_d; + } else { + display_par_n = 1; + display_par_d = 1; + } + + ok = gst_video_calculate_display_ratio (&display_ratio_num, + &display_ratio_den, width, height, par_n, par_d, display_par_n, + display_par_d); + + if (!ok) + return FALSE; + + GST_TRACE ("PAR: %u/%u DAR:%u/%u", par_n, par_d, display_par_n, + display_par_d); + + if (height % display_ratio_den == 0) { + GST_DEBUG ("keeping video height"); + GST_VIDEO_SINK_WIDTH (vk_sink) = (guint) + gst_util_uint64_scale_int (height, display_ratio_num, + display_ratio_den); + GST_VIDEO_SINK_HEIGHT (vk_sink) = height; + } else if (width % display_ratio_num == 0) { + GST_DEBUG ("keeping video width"); + GST_VIDEO_SINK_WIDTH (vk_sink) = width; + GST_VIDEO_SINK_HEIGHT (vk_sink) = (guint) + gst_util_uint64_scale_int (width, display_ratio_den, display_ratio_num); + } else { + GST_DEBUG ("approximating while keeping video height"); + GST_VIDEO_SINK_WIDTH (vk_sink) = (guint) + gst_util_uint64_scale_int (height, display_ratio_num, + display_ratio_den); + GST_VIDEO_SINK_HEIGHT (vk_sink) = height; + } + GST_DEBUG ("scaling to %dx%d", GST_VIDEO_SINK_WIDTH (vk_sink), + GST_VIDEO_SINK_HEIGHT (vk_sink)); + + return TRUE; +} + +static gboolean +gst_vulkan_sink_set_caps (GstBaseSink * bsink, GstCaps * caps) +{ + GstVulkanSink *vk_sink = GST_VULKAN_SINK (bsink); + GError *error = NULL; + GstVideoInfo v_info; + + GST_DEBUG_OBJECT (bsink, "set caps with %" GST_PTR_FORMAT, caps); + + if (!gst_video_info_from_caps (&v_info, caps)) + return FALSE; + + if (!_configure_display_from_info (vk_sink, &v_info)) + return FALSE; + + if (!gst_vulkan_swapper_set_caps (vk_sink->swapper, caps, &error)) { + GST_ELEMENT_ERROR (vk_sink, RESOURCE, NOT_FOUND, + ("Failed to configure caps"), ("%s", error->message)); + g_clear_error (&error); + return FALSE; + } + + vk_sink->v_info = v_info; + + return TRUE; +} + +static GstFlowReturn +gst_vulkan_sink_prepare (GstBaseSink * bsink, GstBuffer * buf) +{ + GstVulkanSink *vk_sink = GST_VULKAN_SINK (bsink); + + GST_TRACE_OBJECT (vk_sink, "preparing buffer %" GST_PTR_FORMAT, buf); + + if (GST_VIDEO_SINK_WIDTH (vk_sink) < 1 || GST_VIDEO_SINK_HEIGHT (vk_sink) < 1) { + return GST_FLOW_NOT_NEGOTIATED; + } + + return GST_FLOW_OK; +} + +static GstFlowReturn +gst_vulkan_sink_show_frame (GstVideoSink * vsink, GstBuffer * buf) +{ + GstVulkanSink *vk_sink = GST_VULKAN_SINK (vsink); + GError *error = NULL; + + GST_TRACE_OBJECT (vk_sink, "rendering buffer %" GST_PTR_FORMAT, buf); + + if (!gst_vulkan_swapper_render_buffer (vk_sink->swapper, buf, &error)) { + GST_ELEMENT_ERROR (vk_sink, RESOURCE, NOT_FOUND, + ("Failed to render buffer"), ("%s", error->message)); + g_clear_error (&error); + return GST_FLOW_ERROR; + } + + return GST_FLOW_OK; +} diff --git a/ext/vulkan/vksink.h b/ext/vulkan/vksink.h new file mode 100644 index 0000000000..b90624a317 --- /dev/null +++ b/ext/vulkan/vksink.h @@ -0,0 +1,73 @@ +/* + * GStreamer + * Copyright (C) 2015 Matthew Waters + * + * 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 +#include +#include +#include "vk.h" + +G_BEGIN_DECLS + +#define GST_TYPE_VULKAN_SINK (gst_vulkan_sink_get_type()) +#define GST_VULKAN_SINK(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_VULKAN_SINK,GstVulkanSink)) +#define GST_VULKAN_SINK_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_VULKAN_SINK,GstVulkanSinkClass)) +#define GST_IS_VULKAN_SINK(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_VULKAN_SINK)) +#define GST_IS_VULKAN_SINK_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_VULKAN_SINK)) + +typedef struct _GstVulkanSink GstVulkanSink; +typedef struct _GstVulkanSinkClass GstVulkanSinkClass; + +struct _GstVulkanSink +{ + GstVideoSink video_sink; + + GstVulkanInstance *instance; + GstVulkanDevice *device; + + GstVulkanDisplay *display; + GstVulkanWindow *window; + + GstVulkanSwapper *swapper; + + /* properties */ + gboolean force_aspect_ratio; + gint par_n; + gint par_d; + + /* stream configuration */ + GstVideoInfo v_info; + + /* runtime variables */ + gint to_quit; +}; + +struct _GstVulkanSinkClass +{ + GstVideoSinkClass video_sink_class; +}; + +GType gst_vulkan_sink_get_type(void); + +G_END_DECLS + +#endif diff --git a/ext/vulkan/vkswapper.c b/ext/vulkan/vkswapper.c new file mode 100644 index 0000000000..4dbcec8062 --- /dev/null +++ b/ext/vulkan/vkswapper.c @@ -0,0 +1,957 @@ +/* + * GStreamer + * Copyright (C) 2015 Matthew Waters + * + * 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 + +#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; + } +} diff --git a/ext/vulkan/vkswapper.h b/ext/vulkan/vkswapper.h new file mode 100644 index 0000000000..9737ccdf3b --- /dev/null +++ b/ext/vulkan/vkswapper.h @@ -0,0 +1,91 @@ +/* + * GStreamer + * Copyright (C) 2015 Matthew Waters + * + * 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 + +#include +#include +#include + +G_BEGIN_DECLS + +#define GST_TYPE_VULKAN_SWAPPER (gst_vulkan_swapper_get_type()) +#define GST_VULKAN_SWAPPER(o) (G_TYPE_CHECK_INSTANCE_CAST((o), GST_TYPE_VULKAN_SWAPPER, GstVulkanSwapper)) +#define GST_VULKAN_SWAPPER_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), GST_TYPE_VULKAN_SWAPPER, GstVulkanSwapperClass)) +#define GST_IS_VULKAN_SWAPPER(o) (G_TYPE_CHECK_INSTANCE_TYPE((o), GST_TYPE_VULKAN_SWAPPER)) +#define GST_IS_VULKAN_SWAPPER_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE((k), GST_TYPE_VULKAN_SWAPPER)) +#define GST_VULKAN_SWAPPER_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS((o), GST_TYPE_VULKAN_SWAPPER, GstVulkanSwapperClass)) +GType gst_vulkan_swapper_get_type (void); + +struct _GstVulkanSwapper +{ + GstObject parent; + + GstVulkanDevice *device; + GstVulkanWindow *window; + GstVulkanQueue *queue; + + VkSurfacePropertiesKHR surf_props; + VkSurfaceFormatKHR *surf_formats; + guint32 n_surf_formats; + VkPresentModeKHR *surf_present_modes; + guint32 n_surf_present_modes; + + VkSwapchainKHR swap_chain; + GstVulkanImageMemory **swap_chain_images; + guint32 n_swap_chain_images; + + GstCaps *caps; + GstVideoInfo v_info; + + PFN_vkGetPhysicalDeviceSurfaceSupportKHR GetPhysicalDeviceSurfaceSupportKHR; + PFN_vkGetSurfacePropertiesKHR GetSurfacePropertiesKHR; + PFN_vkGetSurfaceFormatsKHR GetSurfaceFormatsKHR; + PFN_vkGetSurfacePresentModesKHR GetSurfacePresentModesKHR; + PFN_vkCreateSwapchainKHR CreateSwapchainKHR; + PFN_vkDestroySwapchainKHR DestroySwapchainKHR; + PFN_vkGetSwapchainImagesKHR GetSwapchainImagesKHR; + PFN_vkAcquireNextImageKHR AcquireNextImageKHR; + PFN_vkQueuePresentKHR QueuePresentKHR; +}; + +struct _GstVulkanSwapperClass +{ + GstObjectClass parent_class; +}; + +GstVulkanSwapper * gst_vulkan_swapper_new (GstVulkanDevice * device, + GstVulkanWindow * window); + +GstCaps * gst_vulkan_swapper_get_supported_caps (GstVulkanSwapper * swapper, + GError ** error); +gboolean gst_vulkan_swapper_set_caps (GstVulkanSwapper * swapper, + GstCaps * caps, + GError ** error); +gboolean gst_vulkan_swapper_render_buffer (GstVulkanSwapper * swapper, + GstBuffer * buffer, + GError ** error); + +G_END_DECLS + +#endif /* _VK_INSTANCE_H_ */ diff --git a/ext/vulkan/vkutils.c b/ext/vulkan/vkutils.c new file mode 100644 index 0000000000..602b643c37 --- /dev/null +++ b/ext/vulkan/vkutils.c @@ -0,0 +1,46 @@ +/* + * GStreamer + * Copyright (C) 2015 Matthew Waters + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "vkutils.h" + +gboolean +_check_for_all_layers (uint32_t check_count, const char **check_names, + uint32_t layer_count, VkLayerProperties * layers) +{ + uint32_t i, j; + + for (i = 0; i < check_count; i++) { + gboolean found = FALSE; + for (j = 0; j < layer_count; j++) { + if (g_strcmp0 (check_names[i], layers[j].layerName) == 0) { + found = TRUE; + } + } + if (!found) { + GST_ERROR ("Cannot find layer: %s", check_names[i]); + return FALSE; + } + } + return TRUE; +} diff --git a/ext/vulkan/vkutils.h b/ext/vulkan/vkutils.h new file mode 100644 index 0000000000..c07abb8f62 --- /dev/null +++ b/ext/vulkan/vkutils.h @@ -0,0 +1,33 @@ +/* + * GStreamer + * Copyright (C) 2015 Matthew Waters + * + * 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 +#include "vk.h" + +G_BEGIN_DECLS + +gboolean _check_for_all_layers (uint32_t check_count, const char ** check_names, + uint32_t layer_count, VkLayerProperties * layers); + +G_END_DECLS + +#endif /*_VK_UTILS_H_ */ diff --git a/ext/vulkan/vkwindow.c b/ext/vulkan/vkwindow.c new file mode 100644 index 0000000000..1b8dddbe5c --- /dev/null +++ b/ext/vulkan/vkwindow.c @@ -0,0 +1,247 @@ +/* + * GStreamer + * Copyright (C) 2012 Matthew Waters + * + * 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 +#include + +#include "vkwindow.h" + +#if GST_VULKAN_HAVE_WINDOW_X11 +#include "x11/vkwindow_x11.h" +#endif +#if GST_VULKAN_HAVE_WINDOW_XCB +#include "xcb/vkwindow_xcb.h" +#endif + +#define GST_CAT_DEFAULT gst_vulkan_window_debug +GST_DEBUG_CATEGORY (GST_CAT_DEFAULT); + +#define gst_vulkan_window_parent_class parent_class +G_DEFINE_ABSTRACT_TYPE (GstVulkanWindow, gst_vulkan_window, GST_TYPE_OBJECT); + +#define GST_VULKAN_WINDOW_GET_PRIVATE(o) \ + (G_TYPE_INSTANCE_GET_PRIVATE((o), GST_TYPE_VULKAN_WINDOW, GstVulkanWindowPrivate)) + +struct _GstVulkanWindowPrivate +{ + guint surface_width; + guint surface_height; +}; + +static void gst_vulkan_window_finalize (GObject * object); + +typedef struct _GstVulkanDummyWindow +{ + GstVulkanWindow parent; + + guintptr handle; +} GstVulkanDummyWindow; + +typedef struct _GstVulkanDummyWindowCass +{ + GstVulkanWindowClass parent; +} GstVulkanDummyWindowClass; + +GstVulkanDummyWindow *gst_vulkan_dummy_window_new (void); + +enum +{ + SIGNAL_0, + LAST_SIGNAL +}; + +/* static guint gst_vulkan_window_signals[LAST_SIGNAL] = { 0 }; */ + +GQuark +gst_vulkan_window_error_quark (void) +{ + return g_quark_from_static_string ("gst-gl-window-error-quark"); +} + +static gboolean +gst_vulkan_window_default_open (GstVulkanWindow * window, GError ** error) +{ + return TRUE; +} + +static void +gst_vulkan_window_default_close (GstVulkanWindow * window) +{ +} + +static void +_init_debug (void) +{ + static volatile gsize _init = 0; + + if (g_once_init_enter (&_init)) { + GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "vulkanwindow", 0, + "Vulkan Window"); + g_once_init_leave (&_init, 1); + } +} + +static void +gst_vulkan_window_init (GstVulkanWindow * window) +{ +} + +static void +gst_vulkan_window_class_init (GstVulkanWindowClass * klass) +{ + g_type_class_add_private (klass, sizeof (GstVulkanWindowPrivate)); + + klass->open = GST_DEBUG_FUNCPTR (gst_vulkan_window_default_open); + klass->close = GST_DEBUG_FUNCPTR (gst_vulkan_window_default_close); + + G_OBJECT_CLASS (klass)->finalize = gst_vulkan_window_finalize; + + _init_debug (); +} + +/** + * gst_vulkan_window_new: + * @display: a #GstGLDisplay + * + * Returns: (transfer full): a new #GstVulkanWindow using @display's connection + * + * Since: 1.4 + */ +GstVulkanWindow * +gst_vulkan_window_new (GstVulkanDisplay * display) +{ + GstVulkanWindow *window = NULL; + const gchar *user_choice; + + g_return_val_if_fail (display != NULL, NULL); + + _init_debug (); + + user_choice = g_getenv ("GST_VULKAN_WINDOW"); + GST_INFO ("creating a window, user choice:%s", user_choice); +#if GST_VULKAN_HAVE_WINDOW_X11 + if (!window && (!user_choice || g_strstr_len (user_choice, 3, "x11"))) + window = GST_VULKAN_WINDOW (gst_vulkan_window_x11_new (display)); +#endif +#if GST_VULKAN_HAVE_WINDOW_XCB + if (!window && (!user_choice || g_strstr_len (user_choice, 3, "xcb"))) + window = GST_VULKAN_WINDOW (gst_vulkan_window_xcb_new (display)); +#endif + if (!window) { + /* subclass returned a NULL window */ + GST_WARNING ("Could not create window. user specified %s, creating dummy" + " window", user_choice ? user_choice : "(null)"); + + window = GST_VULKAN_WINDOW (gst_vulkan_dummy_window_new ()); + } + + window->display = gst_object_ref (display); + + return window; +} + +static void +gst_vulkan_window_finalize (GObject * object) +{ + GstVulkanWindow *window = GST_VULKAN_WINDOW (object); + + gst_object_unref (window->display); + + G_OBJECT_CLASS (gst_vulkan_window_parent_class)->finalize (object); +} + +GstVulkanDisplay * +gst_vulkan_window_get_display (GstVulkanWindow * window) +{ + g_return_val_if_fail (GST_IS_VULKAN_WINDOW (window), NULL); + + return gst_object_ref (window->display); +} + +gpointer +gst_vulkan_window_get_platform_handle (GstVulkanWindow * window) +{ + GstVulkanWindowClass *klass; + + g_return_val_if_fail (GST_IS_VULKAN_WINDOW (window), NULL); + klass = GST_VULKAN_WINDOW_GET_CLASS (window); + g_return_val_if_fail (klass->get_platform_handle != NULL, NULL); + + return klass->get_platform_handle (window); +} + +gboolean +gst_vulkan_window_open (GstVulkanWindow * window, GError ** error) +{ + GstVulkanWindowClass *klass; + + g_return_val_if_fail (GST_IS_VULKAN_WINDOW (window), FALSE); + klass = GST_VULKAN_WINDOW_GET_CLASS (window); + g_return_val_if_fail (klass->open != NULL, FALSE); + + return klass->open (window, error); +} + +void +gst_vulkan_window_close (GstVulkanWindow * window) +{ + GstVulkanWindowClass *klass; + + g_return_if_fail (GST_IS_VULKAN_WINDOW (window)); + klass = GST_VULKAN_WINDOW_GET_CLASS (window); + g_return_if_fail (klass->close != NULL); + + return klass->close (window); +} + +GType gst_vulkan_dummy_window_get_type (void); +G_DEFINE_TYPE (GstVulkanDummyWindow, gst_vulkan_dummy_window, + GST_TYPE_VULKAN_WINDOW); + +static void +gst_vulkan_dummy_window_class_init (GstVulkanDummyWindowClass * klass) +{ +} + +static void +gst_vulkan_dummy_window_init (GstVulkanDummyWindow * dummy) +{ + dummy->handle = 0; +} + +GstVulkanDummyWindow * +gst_vulkan_dummy_window_new (void) +{ + return g_object_new (gst_vulkan_dummy_window_get_type (), NULL); +} diff --git a/ext/vulkan/vkwindow.h b/ext/vulkan/vkwindow.h new file mode 100644 index 0000000000..2d8b894449 --- /dev/null +++ b/ext/vulkan/vkwindow.h @@ -0,0 +1,100 @@ +/* + * GStreamer + * Copyright (C) 2008 Julien Isorce + * Copyright (C) 2012 Matthew Waters + * + * 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 + +#include + +G_BEGIN_DECLS + +#define GST_TYPE_VULKAN_WINDOW (gst_vulkan_window_get_type()) +#define GST_VULKAN_WINDOW(o) (G_TYPE_CHECK_INSTANCE_CAST((o), GST_TYPE_VULKAN_WINDOW, GstVulkanWindow)) +#define GST_VULKAN_WINDOW_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), GST_TYPE_VULKAN_WINDOW, GstVulkanWindowClass)) +#define GST_IS_VULKAN_WINDOW(o) (G_TYPE_CHECK_INSTANCE_TYPE((o), GST_TYPE_VULKAN_WINDOW)) +#define GST_IS_VULKAN_WINDOW_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE((k), GST_TYPE_VULKAN_WINDOW)) +#define GST_VULKAN_WINDOW_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS((o), GST_TYPE_VULKAN_WINDOW, GstVulkanWindowClass)) +GType gst_vulkan_window_get_type (void); + +#define GST_VULKAN_WINDOW_LOCK(w) g_mutex_lock(&GST_VULKAN_WINDOW(w)->lock) +#define GST_VULKAN_WINDOW_UNLOCK(w) g_mutex_unlock(&GST_VULKAN_WINDOW(w)->lock) +#define GST_VULKAN_WINDOW_GET_LOCK(w) (&GST_VULKAN_WINDOW(w)->lock) + +#define GST_VULKAN_WINDOW_ERROR (gst_vulkan_window_error_quark ()) +GQuark gst_vulkan_window_error_quark (void); + +typedef enum +{ + GST_VULKAN_WINDOW_ERROR_FAILED, + GST_VULKAN_WINDOW_ERROR_OLD_LIBS, + GST_VULKAN_WINDOW_ERROR_RESOURCE_UNAVAILABLE, +} GstVulkanWindowError; + +/** + * GstVulkanWindow: + * + * #GstVulkanWindow is an opaque struct and should only be accessed through the + * provided api. + */ +struct _GstVulkanWindow { + /*< private >*/ + GstObject parent; + + GstVulkanDisplay *display; + + GMutex lock; + + GstVulkanWindowPrivate *priv; + + gpointer _reserved[GST_PADDING]; +}; + +/** + * GstVulkanWindowClass: + * @parent_class: Parent class + * @open: open the connection to the display + * @close: close the connection to the display + */ +struct _GstVulkanWindowClass { + GstObjectClass parent_class; + + gboolean (*open) (GstVulkanWindow *window, GError **error); + void (*close) (GstVulkanWindow *window); + + gpointer (*get_platform_handle) (GstVulkanWindow *window); + + /*< private >*/ + gpointer _reserved[GST_PADDING]; +}; + +GstVulkanWindow * gst_vulkan_window_new (GstVulkanDisplay *display); + +GstVulkanDisplay * gst_vulkan_window_get_display (GstVulkanWindow *window); +gpointer gst_vulkan_window_get_platform_handle (GstVulkanWindow *window); + +gboolean gst_vulkan_window_open (GstVulkanWindow * window, GError ** error); +void gst_vulkan_window_close (GstVulkanWindow * window); + +G_END_DECLS + +#endif /* __GST_VULKAN_WINDOW_H__ */ diff --git a/ext/vulkan/xcb/Makefile.am b/ext/vulkan/xcb/Makefile.am new file mode 100644 index 0000000000..7debcff9e1 --- /dev/null +++ b/ext/vulkan/xcb/Makefile.am @@ -0,0 +1,29 @@ +## Process this file with automake to produce Makefile.in + +noinst_LTLIBRARIES = libgstvulkan-xcb.la + +libgstvulkan_xcb_la_SOURCES = \ + vkdisplay_xcb.c \ + vkwindow_xcb.c \ + xcb_event_source.c + +noinst_HEADERS = \ + vkdisplay_xcb.h \ + vkwindow_xcb.h \ + xcb_event_source.h + +libgstvulkan_xcb_la_CFLAGS = \ + -I$(top_srcdir)/gst-libs \ + -I$(top_srcdir)/ext/vulkan \ + -I$(top_builddir)/gst-libs \ + $(GST_PLUGINS_BASE_CFLAGS) \ + $(GST_BASE_CFLAGS) \ + $(GST_CFLAGS) \ + $(XCB_CFLAGS) + +libgstvulkan_xcb_la_LIBADD = \ + $(XCB_LIBS) + +libgstvulkan_xcb_la_LDFLAGS = \ + $(GST_LIB_LDFLAGS) \ + $(GST_ALL_LDFLAGS) diff --git a/ext/vulkan/xcb/vkdisplay_xcb.c b/ext/vulkan/xcb/vkdisplay_xcb.c new file mode 100644 index 0000000000..568cf3d289 --- /dev/null +++ b/ext/vulkan/xcb/vkdisplay_xcb.c @@ -0,0 +1,165 @@ +/* + * GStreamer + * Copyright (C) 2015 Matthew Waters + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "vkdisplay_xcb.h" +#include "xcb_event_source.h" + +#define GST_CAT_DEFAULT gst_vulkan_display_debug +GST_DEBUG_CATEGORY_STATIC (gst_vulkan_display_debug); + +G_DEFINE_TYPE (GstVulkanDisplayXCB, gst_vulkan_display_xcb, + GST_TYPE_VULKAN_DISPLAY); + +static void gst_vulkan_display_xcb_finalize (GObject * object); +static gpointer gst_vulkan_display_xcb_get_handle (GstVulkanDisplay * display); +static gpointer gst_vulkan_display_xcb_get_platform_handle (GstVulkanDisplay * + display); + +static void +gst_vulkan_display_xcb_class_init (GstVulkanDisplayXCBClass * klass) +{ + GST_VULKAN_DISPLAY_CLASS (klass)->get_handle = + GST_DEBUG_FUNCPTR (gst_vulkan_display_xcb_get_handle); + GST_VULKAN_DISPLAY_CLASS (klass)->get_platform_handle = + GST_DEBUG_FUNCPTR (gst_vulkan_display_xcb_get_platform_handle); + + G_OBJECT_CLASS (klass)->finalize = gst_vulkan_display_xcb_finalize; +} + +static void +gst_vulkan_display_xcb_init (GstVulkanDisplayXCB * display_xcb) +{ + GstVulkanDisplay *display = (GstVulkanDisplay *) display_xcb; + + display->type = GST_VULKAN_DISPLAY_TYPE_XCB; + display_xcb->foreign_display = FALSE; +} + +static void +gst_vulkan_display_xcb_finalize (GObject * object) +{ + GstVulkanDisplayXCB *display_xcb = GST_VULKAN_DISPLAY_XCB (object); + + if (!display_xcb->foreign_display && display_xcb->platform_handle.connection) + xcb_disconnect (display_xcb->platform_handle.connection); + display_xcb->platform_handle.connection = NULL; + + if (display_xcb->event_source) { + g_source_destroy (display_xcb->event_source); + g_source_unref (display_xcb->event_source); + } + display_xcb->event_source = NULL; + + G_OBJECT_CLASS (gst_vulkan_display_xcb_parent_class)->finalize (object); +} + +static xcb_screen_t * +_get_screen_from_connection (xcb_connection_t * connection, int screen_no) +{ + const xcb_setup_t *setup; + xcb_screen_iterator_t iter; + + setup = xcb_get_setup (connection); + iter = xcb_setup_roots_iterator (setup); + while (screen_no-- > 0) + xcb_screen_next (&iter); + + return iter.data; +} + +/** + * gst_vulkan_display_xcb_new: + * @name: (allow-none): a display name + * + * Create a new #GstVulkanDisplayXCB from the xcb display name. See XOpenDisplay() + * for details on what is a valid name. + * + * Returns: (transfer full): a new #GstVulkanDisplayXCB or %NULL + */ +GstVulkanDisplayXCB * +gst_vulkan_display_xcb_new (const gchar * name) +{ + xcb_connection_t *connection; + GstVulkanDisplayXCB *ret; + int screen_no = 0; + + GST_DEBUG_CATEGORY_GET (gst_vulkan_display_debug, "gldisplay"); + + ret = g_object_new (GST_TYPE_VULKAN_DISPLAY_XCB, NULL); + + ret->platform_handle.connection = connection = xcb_connect (NULL, &screen_no); + if (connection == NULL || xcb_connection_has_error (connection)) { + GST_ERROR_OBJECT (ret, + "Failed to open XCB display connection with name, \'%s\'", name); + gst_object_unref (ret); + return NULL; + } + + ret->screen = _get_screen_from_connection (connection, screen_no); + ret->platform_handle.root = ret->screen->root; + ret->event_source = xcb_event_source_new (ret); + + return ret; +} + +/** + * gst_vulkan_display_xcb_new_with_display: + * @display: an existing, xcb display + * + * Creates a new display connection from a XCB Display. + * + * Returns: (transfer full): a new #GstVulkanDisplayXCB + */ +GstVulkanDisplayXCB * +gst_vulkan_display_xcb_new_with_connection (xcb_connection_t * connection, + int screen_no) +{ + GstVulkanDisplayXCB *ret; + + g_return_val_if_fail (connection != NULL, NULL); + + GST_DEBUG_CATEGORY_GET (gst_vulkan_display_debug, "gldisplay"); + + ret = g_object_new (GST_TYPE_VULKAN_DISPLAY_XCB, NULL); + + ret->platform_handle.connection = connection; + ret->screen = _get_screen_from_connection (connection, screen_no); + ret->platform_handle.root = ret->screen->root; + ret->foreign_display = TRUE; + + return ret; +} + +static gpointer +gst_vulkan_display_xcb_get_handle (GstVulkanDisplay * display) +{ + return (gpointer) GST_VULKAN_DISPLAY_XCB (display)->platform_handle. + connection; +} + +static gpointer +gst_vulkan_display_xcb_get_platform_handle (GstVulkanDisplay * display) +{ + return &GST_VULKAN_DISPLAY_XCB (display)->platform_handle; +} diff --git a/ext/vulkan/xcb/vkdisplay_xcb.h b/ext/vulkan/xcb/vkdisplay_xcb.h new file mode 100644 index 0000000000..8770b0a269 --- /dev/null +++ b/ext/vulkan/xcb/vkdisplay_xcb.h @@ -0,0 +1,84 @@ +/* + * GStreamer + * Copyright (C) 2013 Matthew Waters + * + * 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 + +#include + +#include + +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; + + /* */ + gboolean foreign_display; + + struct _GstVkPlatformHandleXCBKHR platform_handle; + xcb_screen_t *screen; + + GSource *event_source; +}; + +struct _GstVulkanDisplayXCBClass +{ + GstVulkanDisplayClass object_class; +}; + +GstVulkanDisplayXCB * gst_vulkan_display_xcb_new (const gchar * name); +GstVulkanDisplayXCB * gst_vulkan_display_xcb_new_with_connection (xcb_connection_t * connection, + int screen_no); + +G_END_DECLS + +#endif /* __GST_VULKAN_DISPLAY_XCB_H__ */ diff --git a/ext/vulkan/xcb/vkwindow_xcb.c b/ext/vulkan/xcb/vkwindow_xcb.c new file mode 100644 index 0000000000..4ce51021c4 --- /dev/null +++ b/ext/vulkan/xcb/vkwindow_xcb.c @@ -0,0 +1,243 @@ +/* + * GStreamer + * Copyright (C) 2015 Matthew Waters + * + * 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 +#include + +#include "vkwindow_xcb.h" +#include "vkdisplay_xcb.h" + +#define GST_VULKAN_WINDOW_XCB_GET_PRIVATE(o) \ + (G_TYPE_INSTANCE_GET_PRIVATE((o), GST_TYPE_VULKAN_WINDOW_XCB, GstVulkanWindowXCBPrivate)) + +#define GST_CAT_DEFAULT gst_vulkan_window_xcb_debug +GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT); + +static void +_init_debug (void) +{ + static volatile gsize _init = 0; + + if (g_once_init_enter (&_init)) { + GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "vulkanwindowxcb", 0, + "Vulkan XCB Window"); + g_once_init_leave (&_init, 1); + } +} + +#define gst_vulkan_window_xcb_parent_class parent_class +G_DEFINE_TYPE_WITH_CODE (GstVulkanWindowXCB, gst_vulkan_window_xcb, + GST_TYPE_VULKAN_WINDOW, _init_debug ()); + +gboolean gst_vulkan_window_xcb_handle_event (GstVulkanWindowXCB * window_xcb); + +enum +{ + PROP_0, +}; + +struct _GstVulkanWindowXCBPrivate +{ + gboolean activate; + gboolean activate_result; + + gint preferred_width; + gint preferred_height; + + xcb_intern_atom_reply_t *atom_wm_delete_window; +}; + +static gpointer gst_vulkan_window_xcb_get_platform_window (GstVulkanWindow * + window); +gboolean gst_vulkan_window_xcb_open (GstVulkanWindow * window, GError ** error); +void gst_vulkan_window_xcb_close (GstVulkanWindow * window); + +static void +gst_vulkan_window_xcb_finalize (GObject * object) +{ + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static void +gst_vulkan_window_xcb_class_init (GstVulkanWindowXCBClass * klass) +{ + GObjectClass *obj_class = G_OBJECT_CLASS (klass); + GstVulkanWindowClass *window_class = (GstVulkanWindowClass *) klass; + + g_type_class_add_private (klass, sizeof (GstVulkanWindowXCBPrivate)); + + obj_class->finalize = gst_vulkan_window_xcb_finalize; + + window_class->open = GST_DEBUG_FUNCPTR (gst_vulkan_window_xcb_open); + window_class->close = GST_DEBUG_FUNCPTR (gst_vulkan_window_xcb_close); + window_class->get_platform_handle = gst_vulkan_window_xcb_get_platform_window; +} + +static void +gst_vulkan_window_xcb_init (GstVulkanWindowXCB * window) +{ + window->priv = GST_VULKAN_WINDOW_XCB_GET_PRIVATE (window); +} + +/* Must be called in the gl thread */ +GstVulkanWindowXCB * +gst_vulkan_window_xcb_new (GstVulkanDisplay * display) +{ + _init_debug (); + + if ((gst_vulkan_display_get_handle_type (display) & + GST_VULKAN_DISPLAY_TYPE_XCB) + == GST_VULKAN_DISPLAY_TYPE_NONE) { + GST_INFO ("Wrong display type %u for this window type %u", display->type, + GST_VULKAN_DISPLAY_TYPE_XCB); + return NULL; + } + + return g_object_new (GST_TYPE_VULKAN_WINDOW_XCB, NULL); +} + +static void +gst_vulkan_window_xcb_show (GstVulkanWindow * window) +{ + GstVulkanWindowXCB *window_xcb = GST_VULKAN_WINDOW_XCB (window); + GstVulkanDisplayXCB *display_xcb = GST_VULKAN_DISPLAY_XCB (window->display); + xcb_connection_t *connection = display_xcb->platform_handle.connection; + + if (!window_xcb->visible) { + xcb_map_window (connection, window_xcb->win_id); + window_xcb->visible = TRUE; + } +} + +static void +gst_vulkan_window_xcb_hide (GstVulkanWindow * window) +{ + GstVulkanWindowXCB *window_xcb = GST_VULKAN_WINDOW_XCB (window); + GstVulkanDisplayXCB *display_xcb = GST_VULKAN_DISPLAY_XCB (window->display); + xcb_connection_t *connection = display_xcb->platform_handle.connection; + + if (window_xcb->visible) { + xcb_unmap_window (connection, window_xcb->win_id); + window_xcb->visible = FALSE; + } +} + +gboolean +gst_vulkan_window_xcb_create_window (GstVulkanWindowXCB * window_xcb) +{ + GstVulkanDisplayXCB *display_xcb; + xcb_connection_t *connection; + xcb_screen_t *screen; + xcb_window_t root_window; + uint32_t value_mask, value_list[32]; + xcb_intern_atom_cookie_t cookie, cookie2; + xcb_intern_atom_reply_t *reply, *reply2; +// const gchar *title = "OpenGL renderer"; + gint x = 0, y = 0, width = 320, height = 240; + + display_xcb = + GST_VULKAN_DISPLAY_XCB (GST_VULKAN_WINDOW (window_xcb)->display); + connection = GST_VULKAN_DISPLAY_XCB_CONNECTION (display_xcb); + root_window = GST_VULKAN_DISPLAY_XCB_ROOT_WINDOW (display_xcb); + screen = GST_VULKAN_DISPLAY_XCB_SCREEN (display_xcb); + + window_xcb->win_id = xcb_generate_id (connection); + + value_mask = XCB_CW_BACK_PIXEL | XCB_CW_EVENT_MASK; + value_list[0] = screen->black_pixel; + value_list[1] = XCB_EVENT_MASK_KEY_RELEASE | XCB_EVENT_MASK_EXPOSURE; + + xcb_create_window (connection, XCB_COPY_FROM_PARENT, window_xcb->win_id, + root_window, x, y, width, height, 0, XCB_WINDOW_CLASS_INPUT_OUTPUT, + screen->root_visual, value_mask, value_list); + + GST_LOG_OBJECT (window_xcb, "gl window id: %p", + (gpointer) (guintptr) window_xcb->win_id); + GST_LOG_OBJECT (window_xcb, "gl window props: x:%d y:%d", x, y); + + /* Magic code that will send notification when window is destroyed */ + cookie = xcb_intern_atom (connection, 1, 12, "WM_PROTOCOLS"); + reply = xcb_intern_atom_reply (connection, cookie, 0); + + cookie2 = xcb_intern_atom (connection, 0, 16, "WM_DELETE_WINDOW"); + reply2 = xcb_intern_atom_reply (connection, cookie2, 0); + + xcb_change_property (connection, XCB_PROP_MODE_REPLACE, window_xcb->win_id, + reply->atom, 4, 32, 1, &reply2->atom); + g_free (reply); + g_free (reply2); + + gst_vulkan_window_xcb_show (GST_VULKAN_WINDOW (window_xcb)); + + return TRUE; +} + +static gpointer +gst_vulkan_window_xcb_get_platform_window (GstVulkanWindow * window) +{ + return &GST_VULKAN_WINDOW_XCB (window)->win_id; +} + +gboolean +gst_vulkan_window_xcb_open (GstVulkanWindow * window, GError ** error) +{ + GstVulkanWindowXCB *window_xcb = GST_VULKAN_WINDOW_XCB (window); + GstVulkanDisplayXCB *display_xcb = (GstVulkanDisplayXCB *) window->display; + xcb_connection_t *connection; + + connection = display_xcb->platform_handle.connection; + if (connection == NULL) { + g_set_error (error, GST_VULKAN_WINDOW_ERROR, + GST_VULKAN_WINDOW_ERROR_RESOURCE_UNAVAILABLE, + "Failed to connect to X display server with XCB"); + goto failure; + } + + if (!GST_VULKAN_WINDOW_CLASS (parent_class)->open (window, error)) + return FALSE; + + return gst_vulkan_window_xcb_create_window (window_xcb); + +failure: + return FALSE; +} + +void +gst_vulkan_window_xcb_close (GstVulkanWindow * window) +{ + GstVulkanWindowXCB *window_xcb = GST_VULKAN_WINDOW_XCB (window); + GstVulkanDisplayXCB *display_xcb = (GstVulkanDisplayXCB *) window->display; + xcb_connection_t *connection = + GST_VULKAN_DISPLAY_XCB_CONNECTION (display_xcb); + + if (connection) { + gst_vulkan_window_xcb_hide (window); + + g_free (window_xcb->priv->atom_wm_delete_window); + window_xcb->priv->atom_wm_delete_window = NULL; + GST_DEBUG ("display receiver closed"); + } + + GST_VULKAN_WINDOW_CLASS (parent_class)->close (window); +} diff --git a/ext/vulkan/xcb/vkwindow_xcb.h b/ext/vulkan/xcb/vkwindow_xcb.h new file mode 100644 index 0000000000..fc9c70ddac --- /dev/null +++ b/ext/vulkan/xcb/vkwindow_xcb.h @@ -0,0 +1,86 @@ +/* + * GStreamer + * Copyright (C) 2012 Matthew Waters + * + * 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 + +#include + +G_BEGIN_DECLS + +#define GST_TYPE_VULKAN_WINDOW_XCB (gst_vulkan_window_xcb_get_type()) +#define GST_VULKAN_WINDOW_XCB(o) (G_TYPE_CHECK_INSTANCE_CAST((o), GST_TYPE_VULKAN_WINDOW_XCB, GstVulkanWindowXCB)) +#define GST_VULKAN_WINDOW_XCB_CLASS(k) (G_TYPE_CHECK_CLASS((k), GST_TYPE_VULKAN_WINDOW_XCB, GstVulkanWindowXCBClass)) +#define GST_IS_VULKAN_WINDOW_XCB(o) (G_TYPE_CHECK_INSTANCE_TYPE((o), GST_TYPE_VULKAN_WINDOW_XCB)) +#define GST_IS_VULKAN_WINDOW_XCB_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE((k), GST_TYPE_VULKAN_WINDOW_XCB)) +#define GST_VULKAN_WINDOW_XCB_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS((o), GST_TYPE_VULKAN_WINDOW_XCB, GstVulkanWindowXCBClass)) + +typedef struct _GstVulkanWindowXCB GstVulkanWindowXCB; +typedef struct _GstVulkanWindowXCBPrivate GstVulkanWindowXCBPrivate; +typedef struct _GstVulkanWindowXCBClass GstVulkanWindowXCBClass; + +/** + * GstVulkanWindowXCB: + * + * Opaque #GstVulkanWindowXCB object + */ +struct _GstVulkanWindowXCB +{ + /*< private >*/ + GstVulkanWindow parent; + + /* X window */ + xcb_window_t win_id; + + gint visible :1; + + /*< private >*/ + GstVulkanWindowXCBPrivate *priv; + + gpointer _reserved[GST_PADDING]; +}; + +/** + * GstVulkanWindowXCBClass: + * + * Opaque #GstVulkanWindowXCBClass object + */ +struct _GstVulkanWindowXCBClass { + /*< private >*/ + GstVulkanWindowClass parent_class; + + /*< private >*/ + gpointer _reserved[GST_PADDING_LARGE]; +}; + +GType gst_vulkan_window_xcb_get_type (void); + +GstVulkanWindowXCB * gst_vulkan_window_xcb_new (GstVulkanDisplay * display); + +void gst_vulkan_window_xcb_trap_x_errors (void); +gint gst_vulkan_window_xcb_untrap_x_errors (void); + +gboolean gst_vulkan_window_xcb_create_window (GstVulkanWindowXCB * window_xcb); + +G_END_DECLS + +#endif /* __GST_VULKAN_WINDOW_XCB_H__ */ diff --git a/ext/vulkan/xcb/xcb_event_source.c b/ext/vulkan/xcb/xcb_event_source.c new file mode 100644 index 0000000000..f848841bf2 --- /dev/null +++ b/ext/vulkan/xcb/xcb_event_source.c @@ -0,0 +1,251 @@ +/* + * GStreamer + * Copyright (C) 2012 Matthew Waters + * + * 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 +#include + +#include "xcb_event_source.h" +#include "vkdisplay_xcb.h" +#include "vkwindow_xcb.h" + +static gint +_compare_xcb_window (GstVulkanWindowXCB * window_xcb, xcb_window_t * window_id) +{ + return window_xcb->win_id - *window_id; +} + +static GstVulkanWindowXCB * +_find_window_from_xcb_window (GstVulkanDisplayXCB * display_xcb, + xcb_window_t window_id) +{ + GstVulkanDisplay *display = GST_VULKAN_DISPLAY (display_xcb); + GstVulkanWindowXCB *ret = NULL; + GList *l; + + if (!window_id) + return NULL; + + GST_OBJECT_LOCK (display); + l = g_list_find_custom (display->windows, &window_id, + (GCompareFunc) _compare_xcb_window); + if (l) + ret = gst_object_ref (l->data); + GST_OBJECT_UNLOCK (display); + + return ret; +} + +static gboolean +_xcb_handle_event (GstVulkanDisplayXCB * display_xcb) +{ + xcb_connection_t *connection = + GST_VULKAN_DISPLAY_XCB_CONNECTION (display_xcb); + xcb_generic_event_t *event; + gboolean ret = TRUE; + + while ((event = xcb_poll_for_event (connection))) { + uint8_t event_code = event->response_type & 0x7f; + + switch (event_code) { + case XCB_CLIENT_MESSAGE:{ + xcb_client_message_event_t *client_event; + xcb_intern_atom_cookie_t cookie; + xcb_intern_atom_reply_t *reply; + + client_event = (xcb_client_message_event_t *) event; + cookie = xcb_intern_atom (connection, 0, 16, "WM_DELETE_WINDOW"); + reply = xcb_intern_atom_reply (connection, cookie, 0); + + if (client_event->data.data32[0] == reply->atom) { + GstVulkanWindowXCB *window_xcb; + + window_xcb = + _find_window_from_xcb_window (display_xcb, client_event->window); + /* TODO: actually quit */ + ret = FALSE; +#if 0 + if (display->close) + display->close (display->close_data); +#endif + gst_object_unref (window_xcb); + } + + g_free (reply); + break; + } +#if 0 + case CreateNotify: + case ConfigureNotify: +#if 0 + gst_vulkan_window_resize (window, event.xconfigure.width, + event.xconfigure.height); +#endif + break; + case DestroyNotify: + break; + + case Expose: + /* non-zero means that other Expose follows + * so just wait for the last one + * in theory we should not receive non-zero because + * we have no sub areas here but just in case */ + if (event.xexpose.count != 0) { + break; + } +#if 0 + /* We need to redraw on expose */ + if (window->draw) { + context = gst_vulkan_window_get_context (window); + context_class = GST_VULKAN_CONTEXT_GET_CLASS (context); + + window->draw (window->draw_data); + context_class->swap_buffers (context); + + gst_object_unref (context); + } +#endif + break; + case VisibilityNotify: + /* actually nothing to do here */ + break; +#if 0 + case KeyPress: + case KeyRelease: + keysym = XkbKeycodeToKeysym (window_xcb->device, + event.xkey.keycode, 0, 0); + key_str = XKeysymToString (keysym); + key_data = g_slice_new (struct key_event); + key_data->window = window; + key_data->key_str = XKeysymToString (keysym); + key_data->event_type = + event.type == KeyPress ? "key-press" : "key-release"; + GST_DEBUG ("input event key %d pressed over window at %d,%d (%s)", + event.xkey.keycode, event.xkey.x, event.xkey.y, key_str); + g_main_context_invoke (window->navigation_context, + (GSourceFunc) gst_vulkan_window_key_event_cb, key_data); + break; + case ButtonPress: + case ButtonRelease: + GST_DEBUG ("input event mouse button %d pressed over window at %d,%d", + event.xbutton.button, event.xbutton.x, event.xbutton.y); + mouse_data = g_slice_new (struct mouse_event); + mouse_data->window = window; + mouse_data->event_type = + event.type == + ButtonPress ? "mouse-button-press" : "mouse-button-release"; + mouse_data->button = event.xbutton.button; + mouse_data->posx = (double) event.xbutton.x; + mouse_data->posy = (double) event.xbutton.y; + + g_main_context_invoke (window->navigation_context, + (GSourceFunc) gst_vulkan_window_mouse_event_cb, mouse_data); + break; + case MotionNotify: + GST_DEBUG ("input event pointer moved over window at %d,%d", + event.xmotion.x, event.xmotion.y); + mouse_data = g_slice_new (struct mouse_event); + mouse_data->window = window; + mouse_data->event_type = "mouse-move"; + mouse_data->button = 0; + mouse_data->posx = (double) event.xbutton.x; + mouse_data->posy = (double) event.xbutton.y; + + g_main_context_invoke (window->navigation_context, (GSourceFunc) + gst_vulkan_window_mouse_event_cb, mouse_data); + break; +#endif +#endif + default: + GST_DEBUG ("unhandled XCB type: %u", event_code); + break; + } + } + + return ret; +} + +typedef struct _XCBEventSource +{ + GSource source; + GPollFD pfd; + uint32_t mask; + GstVulkanDisplayXCB *display_xcb; +} XCBEventSource; + +static gboolean +xcb_event_source_prepare (GSource * base, gint * timeout) +{ + *timeout = -1; + return FALSE; +} + +static gboolean +xcb_event_source_check (GSource * base) +{ + XCBEventSource *source = (XCBEventSource *) base; + gboolean retval; + + retval = source->pfd.revents; + + return retval; +} + +static gboolean +xcb_event_source_dispatch (GSource * base, GSourceFunc callback, gpointer data) +{ + XCBEventSource *source = (XCBEventSource *) base; + + gboolean ret = _xcb_handle_event (source->display_xcb); + + if (callback) + callback (data); + + return ret; +} + +static GSourceFuncs xcb_event_source_funcs = { + xcb_event_source_prepare, + xcb_event_source_check, + xcb_event_source_dispatch, + NULL +}; + +GSource * +xcb_event_source_new (GstVulkanDisplayXCB * display_xcb) +{ + xcb_connection_t *connection; + XCBEventSource *source; + + connection = GST_VULKAN_DISPLAY_XCB_CONNECTION (display_xcb); + g_return_val_if_fail (connection != NULL, NULL); + + source = (XCBEventSource *) + g_source_new (&xcb_event_source_funcs, sizeof (XCBEventSource)); + source->display_xcb = display_xcb; + source->pfd.fd = xcb_get_file_descriptor (connection); + source->pfd.events = G_IO_IN | G_IO_ERR; + g_source_add_poll (&source->source, &source->pfd); + + return &source->source; +} diff --git a/ext/vulkan/xcb/xcb_event_source.h b/ext/vulkan/xcb/xcb_event_source.h new file mode 100644 index 0000000000..cd9b41c34b --- /dev/null +++ b/ext/vulkan/xcb/xcb_event_source.h @@ -0,0 +1,30 @@ +/* + * GStreamer + * Copyright (C) 2012 Matthew Waters + * + * 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 +#include "vkdisplay_xcb.h" + +GSource * +xcb_event_source_new (GstVulkanDisplayXCB *display_xcb); + +#endif /* __VULKAN_XCB_EVENT_SOURCE_H__ */