new vulkan based video sink

Currently xcb is the only winsys that is implemented and there's no redraws et
al
This commit is contained in:
Matthew Waters 2015-10-24 17:29:05 +11:00
parent 60005b4a12
commit 5de6dd9f40
36 changed files with 5776 additions and 2 deletions

View file

@ -1464,6 +1464,55 @@ AC_SUBST(JPEG_LIBS)
AC_SUBST(HAVE_JPEG)
AM_CONDITIONAL(HAVE_JPEG, test "x$HAVE_JPEG" = "xyes")
dnl Vulkan
VULKAN_CONFIG_DEFINES=""
PKG_CHECK_MODULES(XCB, xcb >= 1.10, HAVE_XCB=yes, HAVE_XCB=no)
AM_CONDITIONAL(USE_XCB, test "x$HAVE_XCB" = "xyes")
if test "x$HAVE_XCB" = "xyes"; then
VULKAN_CONFIG_DEFINES="$VULKAN_CONFIG_DEFINES
#define GST_VULKAN_HAVE_WINDOW_XCB 1"
fi
AC_CONFIG_COMMANDS([ext/vulkan/vkconfig.h], [
outfile=vkconfig.h-tmp
cat > $outfile <<\_______EOF
/* vkconfig.h
*
* This is a generated file. Please modify `configure.ac'
*/
#ifndef __GST_VULKAN_CONFIG_H__
#define __GST_VULKAN_CONFIG_H__
#include <gst/gst.h>
G_BEGIN_DECLS
_______EOF
cat >>$outfile <<_______EOF
$vulkan_config_defines
_______EOF
cat >>$outfile <<_______EOF
G_END_DECLS
#endif /* __GST_VULKAN_CONFIG_H__ */
_______EOF
if cmp -s $outfile ext/vulkan/vkconfig.h; then
AC_MSG_NOTICE([ext/vulkan/vkconfig.h is unchanged])
rm -f $outfile
else
mv $outfile ext/vulkan/vkconfig.h
fi
],[
vulkan_config_defines='$VULKAN_CONFIG_DEFINES'
])
dnl *** sys plug-ins ***
@ -2851,6 +2900,22 @@ AG_GST_CHECK_FEATURE(QT, [Qt elements], qt, [
])
])
dnl *** Vulkan ***
translit(dnm, m, l) AM_CONDITIONAL(USE_VULKAN, true)
AG_GST_CHECK_FEATURE(VULKAN, [Vulkan elements], vulkan, [
HAVE_VULKAN=no
AC_CHECK_HEADER(vulkan/vulkan.h, [
AC_CHECK_LIB(vulkan, vkCreateDevice, [
VULKAN_LIBS="-lvulkan"
AC_SUBST(VULKAN_LIBS)
dnl TODO check platform support (x11, win32, wayland, android, etc)
if test "x$HAVE_XCB" = "xyes"; then
HAVE_VULKAN=yes
fi
], [])
], [])
])
dnl *** libvisual ***
translit(dnm, m, l) AM_CONDITIONAL(USE_LIBVISUAL, true)
AG_GST_CHECK_FEATURE(LIBVISUAL, [libvisual visualization library], libvisual, [
@ -3636,6 +3701,8 @@ ext/teletextdec/Makefile
ext/gme/Makefile
ext/spc/Makefile
ext/timidity/Makefile
ext/vulkan/Makefile
ext/vulkan/xcb/Makefile
ext/webp/Makefile
ext/x265/Makefile
ext/xvid/Makefile

View file

@ -406,6 +406,12 @@ else
DTLS_DIR=
endif
if USE_VULKAN
VULKAN_DIR=vulkan
else
VULKAN_DIR=
endif
SUBDIRS=\
$(VOAACENC_DIR) \
$(ASSRENDER_DIR) \
@ -474,7 +480,8 @@ SUBDIRS=\
$(HLS_DIR) \
$(WEBP_DIR) \
$(X265_DIR) \
$(DTLS_DIR)
$(DTLS_DIR) \
$(VULKAN_DIR)
DIST_SUBDIRS = \
assrender \
@ -541,6 +548,7 @@ DIST_SUBDIRS = \
rtmp \
webp \
x265 \
dtls
dtls \
vulkan
include $(top_srcdir)/common/parallel-subdirs.mak

1
ext/vulkan/.gitignore vendored Normal file
View file

@ -0,0 +1 @@
vkconfig.h

58
ext/vulkan/Makefile.am Normal file
View file

@ -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)

63
ext/vulkan/gstvulkan.c Normal file
View file

@ -0,0 +1,63 @@
/*
* GStreamer
* Copyright (C) 2015 Matthew Waters <matthew@centricular.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
/**
* SECTION:plugin-vulkan
*
* Cross-platform Vulkan plugin.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "vksink.h"
#if GST_VULKAN_HAVE_WINDOW_X11
#include <X11/Xlib.h>
#endif
#define GST_CAT_DEFAULT gst_gl_gstgl_debug
GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
/* Register filters that make up the gstgl plugin */
static gboolean
plugin_init (GstPlugin * plugin)
{
GST_DEBUG_CATEGORY_INIT (gst_gl_gstgl_debug, "gstvulkan", 0, "gstvulkan");
#if GST_VULKAN_HAVE_WINDOW_X11
if (g_getenv ("GST_VULKAN_XINITTHREADS"))
XInitThreads ();
#endif
if (!gst_element_register (plugin, "vulkansink",
GST_RANK_NONE, GST_TYPE_VULKAN_SINK)) {
return FALSE;
}
return TRUE;
}
GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
GST_VERSION_MINOR,
vulkan,
"Vulkan plugin",
plugin_init, VERSION, GST_LICENSE, GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)

45
ext/vulkan/vk.h Normal file
View file

@ -0,0 +1,45 @@
/*
* GStreamer
* Copyright (C) 2015 Matthew Waters <matthew@centricular.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef _VK_H_
#define _VK_H_
#include <gst/gst.h>
#define VK_PROTOTYPES
#include <vulkan/vulkan.h>
#include <vulkan/vk_debug_report_lunarg.h>
#include <vulkan/vk_ext_khr_swapchain.h>
#include <vulkan/vk_ext_khr_device_swapchain.h>
#include "vkconfig.h"
#include "vk_fwd.h"
#include "vkerror.h"
#include "vkinstance.h"
#include "vkdevice.h"
#include "vkqueue.h"
#include "vkdisplay.h"
#include "vkwindow.h"
#include "vkswapper.h"
#include "vkmemory.h"
#include "vkimagememory.h"
#endif /* _VK_H_ */

58
ext/vulkan/vk_fwd.h Normal file
View file

@ -0,0 +1,58 @@
/*
* GStreamer
* Copyright (C) 2015 Matthew Waters <matthew@centricular.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef _VK_FWD_H_
#define _VK_FWD_H_
#include <gst/gst.h>
G_BEGIN_DECLS
typedef struct _GstVulkanInstance GstVulkanInstance;
typedef struct _GstVulkanInstanceClass GstVulkanInstanceClass;
typedef struct _GstVulkanDevice GstVulkanDevice;
typedef struct _GstVulkanDeviceClass GstVulkanDeviceClass;
typedef struct _GstVulkanQueue GstVulkanQueue;
typedef struct _GstVulkanQueueClass GstVulkanQueueClass;
typedef struct _GstVulkanDisplay GstVulkanDisplay;
typedef struct _GstVulkanDisplayClass GstVulkanDisplayClass;
typedef struct _GstVulkanDisplayPrivate GstVulkanDisplayPrivate;
typedef struct _GstVulkanWindow GstVulkanWindow;
typedef struct _GstVulkanWindowClass GstVulkanWindowClass;
typedef struct _GstVulkanWindowPrivate GstVulkanWindowPrivate;
typedef struct _GstVulkanSwapper GstVulkanSwapper;
typedef struct _GstVulkanSwapperClass GstVulkanSwapperClass;
typedef struct _GstVulkanMemory GstVulkanMemory;
typedef struct _GstVulkanMemoryAllocator GstVulkanMemoryAllocator;
typedef struct _GstVulkanMemoryAllocatorClass GstVulkanMemoryAllocatorClass;
typedef struct _GstVulkanImageMemory GstVulkanImageMemory;
typedef struct _GstVulkanImageMemoryAllocator GstVulkanImageMemoryAllocator;
typedef struct _GstVulkanImageMemoryAllocatorClass GstVulkanImageMemoryAllocatorClass;
G_END_DECLS
#endif /* _VK_FWD_H_ */

401
ext/vulkan/vkdevice.c Normal file
View file

@ -0,0 +1,401 @@
/*
* GStreamer
* Copyright (C) 2015 Matthew Waters <matthew@centricular.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "vkdevice.h"
#include "vkutils.h"
#include <string.h>
static const char *device_validation_layers[] = {
"Threading",
"MemTracker",
"ObjectTracker",
"DrawState",
"ParamChecker",
"ShaderChecker",
"Swapchain",
"DeviceLimits",
"Image",
};
#define GST_CAT_DEFAULT gst_vulkan_device_debug
GST_DEBUG_CATEGORY (GST_CAT_DEFAULT);
G_DEFINE_TYPE_WITH_CODE (GstVulkanDevice, gst_vulkan_device, GST_TYPE_OBJECT,
GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "vulkandevice", 0,
"Vulkan Device"));
static void gst_vulkan_device_finalize (GObject * object);
GstVulkanDevice *
gst_vulkan_device_new (GstVulkanInstance * instance)
{
GstVulkanDevice *device = g_object_new (GST_TYPE_VULKAN_DEVICE, NULL);
device->instance = gst_object_ref (instance);
/* FIXME: select this externally */
device->device_index = 0;
return device;
}
static void
gst_vulkan_device_init (GstVulkanDevice * device)
{
}
static void
gst_vulkan_device_class_init (GstVulkanDeviceClass * device_class)
{
GObjectClass *gobject_class = (GObjectClass *) device_class;
gobject_class->finalize = gst_vulkan_device_finalize;
}
static void
gst_vulkan_device_finalize (GObject * object)
{
GstVulkanDevice *device = GST_VULKAN_DEVICE (object);
if (device->cmd_pool.handle)
vkDestroyCommandPool (device->device, device->cmd_pool);
device->cmd_pool.handle = 0;
if (device->device)
vkDestroyDevice (device->device);
device->device = NULL;
if (device->instance)
gst_object_unref (device->instance);
device->instance = NULL;
}
static const gchar *
_device_type_to_string (VkPhysicalDeviceType type)
{
switch (type) {
case VK_PHYSICAL_DEVICE_TYPE_OTHER:
return "other";
case VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU:
return "integrated";
case VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU:
return "discrete";
case VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU:
return "virtual";
case VK_PHYSICAL_DEVICE_TYPE_CPU:
return "CPU";
default:
return "unknown";
}
}
static gboolean
_physical_device_info (GstVulkanDevice * device, GError ** error)
{
VkPhysicalDeviceProperties props;
VkPhysicalDevice gpu;
VkResult err;
gpu = gst_vulkan_device_get_physical_device (device);
err = vkGetPhysicalDeviceProperties (gpu, &props);
if (gst_vulkan_error_to_g_error (err, error,
"vkGetPhysicalDeviceProperties") < 0)
return FALSE;
GST_INFO_OBJECT (device, "device name %s type %s api version %u, "
"driver version %u vendor ID 0x%x, device ID 0x%x", props.deviceName,
_device_type_to_string (props.deviceType), props.apiVersion,
props.driverVersion, props.vendorId, props.deviceId);
return TRUE;
}
gboolean
gst_vulkan_device_open (GstVulkanDevice * device, GError ** error)
{
const char *extension_names[64];
uint32_t enabled_extension_count = 0;
uint32_t device_extension_count = 0;
VkExtensionProperties *device_extensions = NULL;
uint32_t enabled_layer_count = 0;
uint32_t device_layer_count = 0;
VkLayerProperties *device_layers;
gboolean have_swapchain_ext;
gboolean validation_found;
VkPhysicalDevice gpu;
VkResult err;
guint i;
g_return_val_if_fail (GST_IS_VULKAN_DEVICE (device), FALSE);
if (!_physical_device_info (device, error))
return FALSE;
gpu = gst_vulkan_device_get_physical_device (device);
/* Look for validation layers */
err = vkEnumerateDeviceLayerProperties (gpu, &device_layer_count, NULL);
if (gst_vulkan_error_to_g_error (err, error,
"vkEnumerateDeviceLayerProperties") < 0)
return FALSE;
device_layers = g_new0 (VkLayerProperties, device_layer_count);
err =
vkEnumerateDeviceLayerProperties (gpu, &device_layer_count,
device_layers);
if (gst_vulkan_error_to_g_error (err, error,
"vkEnumerateDeviceLayerProperties") < 0) {
g_free (device_layers);
return FALSE;
}
validation_found =
_check_for_all_layers (G_N_ELEMENTS (device_validation_layers),
device_validation_layers, device_layer_count, device_layers);
g_free (device_layers);
device_layers = NULL;
if (!validation_found) {
g_error ("vkEnumerateDeviceLayerProperties failed to find"
"a required validation layer.\n\n"
"Please look at the Getting Started guide for additional "
"information.\nvkCreateDevice Failure");
return FALSE;
}
enabled_layer_count = G_N_ELEMENTS (device_validation_layers);
err =
vkEnumerateDeviceExtensionProperties (gpu, NULL,
&device_extension_count, NULL);
if (gst_vulkan_error_to_g_error (err, error,
"vkEnumerateDeviceExtensionProperties") < 0)
return FALSE;
have_swapchain_ext = 0;
enabled_extension_count = 0;
memset (extension_names, 0, sizeof (extension_names));
device_extensions = g_new0 (VkExtensionProperties, device_extension_count);
err = vkEnumerateDeviceExtensionProperties (gpu, NULL,
&device_extension_count, device_extensions);
if (gst_vulkan_error_to_g_error (err, error,
"vkEnumerateDeviceExtensionProperties") < 0) {
g_free (device_extensions);
return FALSE;
}
for (uint32_t i = 0; i < device_extension_count; i++) {
if (!strcmp ("VK_EXT_KHR_device_swapchain", device_extensions[i].extName)) {
have_swapchain_ext = TRUE;
extension_names[enabled_extension_count++] =
"VK_EXT_KHR_device_swapchain";
}
g_assert (enabled_extension_count < 64);
}
if (!have_swapchain_ext) {
g_error ("vkEnumerateDeviceExtensionProperties failed to find the "
"\"VK_EXT_KHR_device_swapchain\" extension.\n\nDo you have a compatible "
"Vulkan installable client driver (ICD) installed?\nPlease "
"look at the Getting Started guide for additional "
"information.\nvkCreateInstance Failure");
}
g_free (device_extensions);
err = vkGetPhysicalDeviceProperties (gpu, &device->gpu_props);
if (gst_vulkan_error_to_g_error (err, error,
"vkGetPhysicalDeviceProperties") < 0)
return FALSE;
err = vkGetPhysicalDeviceMemoryProperties (gpu, &device->memory_properties);
if (gst_vulkan_error_to_g_error (err, error,
"vkGetPhysicalDeviceProperties") < 0)
return FALSE;
err = vkGetPhysicalDeviceFeatures (gpu, &device->gpu_features);
if (gst_vulkan_error_to_g_error (err, error,
"vkGetPhysicalDeviceFeatures") < 0)
return FALSE;
/* Call with NULL data to get count */
err =
vkGetPhysicalDeviceQueueFamilyProperties (gpu, &device->n_queue_families,
NULL);
if (gst_vulkan_error_to_g_error (err, error,
"vkGetPhysicalDeviceQueueFamilyProperties") < 0)
return FALSE;
g_assert (device->n_queue_families >= 1);
device->queue_family_props =
g_new0 (VkQueueFamilyProperties, device->n_queue_families);
err =
vkGetPhysicalDeviceQueueFamilyProperties (gpu, &device->n_queue_families,
device->queue_family_props);
if (gst_vulkan_error_to_g_error (err, error,
"vkGetPhysicalDeviceQueueFamilyProperties") < 0)
return FALSE;
/* FIXME: allow overriding/selecting */
for (i = 0; i < device->n_queue_families; i++) {
if (device->queue_family_props[i].queueFlags & VK_QUEUE_GRAPHICS_BIT)
break;
}
if (i >= device->n_queue_families) {
g_set_error (error, GST_VULKAN_ERROR,
GST_VULKAN_ERROR_INITIALIZATION_FAILED,
"Failed to find a compatible queue family");
return FALSE;
}
device->queue_family_id = i;
device->n_queues = 1;
{
const VkDeviceQueueCreateInfo queue_info = {
.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO,
.pNext = NULL,
.queueFamilyIndex = device->queue_family_id,
.queueCount = device->n_queues,
};
VkDeviceCreateInfo device_info = {
.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO,
.pNext = NULL,
.queueRecordCount = 1,
.pRequestedQueues = &queue_info,
.layerCount = enabled_layer_count,
.ppEnabledLayerNames = (const char *const *) device_validation_layers,
.extensionCount = enabled_extension_count,
.ppEnabledExtensionNames = (const char *const *) extension_names,
.pEnabledFeatures = NULL, // If specific features are required, pass them in here
};
err = vkCreateDevice (gpu, &device_info, &device->device);
if (gst_vulkan_error_to_g_error (err, error, "vkCreateDevice") < 0)
return FALSE;
}
{
const VkCmdPoolCreateInfo cmd_pool_info = {
.sType = VK_STRUCTURE_TYPE_CMD_POOL_CREATE_INFO,
.pNext = NULL,
.queueFamilyIndex = device->queue_family_id,
.flags = 0,
};
err =
vkCreateCommandPool (device->device, &cmd_pool_info, &device->cmd_pool);
if (gst_vulkan_error_to_g_error (err, error, "vkCreateCommandPool") < 0)
return FALSE;
}
return TRUE;
}
GstVulkanQueue *
gst_vulkan_device_get_queue (GstVulkanDevice * device, guint32 queue_family,
guint32 queue_i, GError ** error)
{
GstVulkanQueue *ret;
VkResult err;
g_return_val_if_fail (GST_IS_VULKAN_DEVICE (device), NULL);
g_return_val_if_fail (device->device != NULL, NULL);
g_return_val_if_fail (queue_family < device->n_queues, NULL);
g_return_val_if_fail (queue_i <
device->queue_family_props[queue_family].queueCount, NULL);
ret = g_object_new (GST_TYPE_VULKAN_QUEUE, NULL);
ret->device = gst_object_ref (device);
ret->family = queue_family;
ret->index = queue_i;
err = vkGetDeviceQueue (device->device, queue_family, queue_i, &ret->queue);
if (gst_vulkan_error_to_g_error (err, error, "vkGetDeviceQueue") < 0) {
gst_object_unref (ret);
return NULL;
}
return ret;
}
gpointer
gst_vulkan_device_get_proc_address (GstVulkanDevice * device,
const gchar * name)
{
g_return_val_if_fail (GST_IS_VULKAN_DEVICE (device), NULL);
g_return_val_if_fail (device->device != NULL, NULL);
g_return_val_if_fail (name != NULL, NULL);
GST_TRACE_OBJECT (device, "%s", name);
return vkGetDeviceProcAddr (device->device, name);
}
GstVulkanInstance *
gst_vulkan_device_get_instance (GstVulkanDevice * device)
{
g_return_val_if_fail (GST_IS_VULKAN_DEVICE (device), NULL);
return device->instance ? gst_object_ref (device->instance) : NULL;
}
VkPhysicalDevice
gst_vulkan_device_get_physical_device (GstVulkanDevice * device)
{
g_return_val_if_fail (GST_IS_VULKAN_DEVICE (device), NULL);
if (device->instance->physical_devices == NULL)
return NULL;
if (device->device_index >= device->instance->n_physical_devices)
return NULL;
return device->instance->physical_devices[device->device_index];
}
gboolean
gst_vulkan_device_create_cmd_buffer (GstVulkanDevice * device,
VkCmdBuffer * cmd, GError ** error)
{
VkResult err;
const VkCmdBufferCreateInfo cmd_info = {
.sType = VK_STRUCTURE_TYPE_CMD_BUFFER_CREATE_INFO,
.pNext = NULL,
.cmdPool = device->cmd_pool,
.level = VK_CMD_BUFFER_LEVEL_PRIMARY,
.flags = 0,
};
err = vkCreateCommandBuffer (device->device, &cmd_info, cmd);
if (gst_vulkan_error_to_g_error (err, error, "vkCreateCommandBuffer") < 0)
return FALSE;
GST_LOG_OBJECT (device, "created cmd buffer %p", cmd);
return TRUE;
}
void
gst_vulkan_device_close (GstVulkanDevice * device)
{
g_return_if_fail (GST_IS_VULKAN_DEVICE (device));
g_return_if_fail (device->device != NULL);
g_free (device->queue_family_props);
}

82
ext/vulkan/vkdevice.h Normal file
View file

@ -0,0 +1,82 @@
/*
* GStreamer
* Copyright (C) 2015 Matthew Waters <matthew@centricular.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef _VK_DEVICE_H_
#define _VK_DEVICE_H_
#include <gst/gst.h>
#include "vk.h"
G_BEGIN_DECLS
#define GST_TYPE_VULKAN_DEVICE (gst_vulkan_device_get_type())
#define GST_VULKAN_DEVICE(o) (G_TYPE_CHECK_INSTANCE_CAST((o), GST_TYPE_VULKAN_DEVICE, GstVulkanDevice))
#define GST_VULKAN_DEVICE_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), GST_TYPE_VULKAN_DEVICE, GstVulkanDeviceClass))
#define GST_IS_VULKAN_DEVICE(o) (G_TYPE_CHECK_INSTANCE_TYPE((o), GST_TYPE_VULKAN_DEVICE))
#define GST_IS_VULKAN_DEVICE_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE((k), GST_TYPE_VULKAN_DEVICE))
#define GST_VULKAN_DEVICE_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS((o), GST_TYPE_VULKAN_DEVICE, GstVulkanDeviceClass))
GType gst_vulkan_device_get_type (void);
struct _GstVulkanDevice
{
GstObject parent;
GstVulkanInstance *instance;
guint device_index;
VkDevice device; /* hides a pointer */
VkPhysicalDeviceProperties gpu_props;
VkPhysicalDeviceFeatures gpu_features;
VkPhysicalDeviceMemoryProperties memory_properties;
VkQueueFamilyProperties *queue_family_props;
guint32 n_queue_families;
guint32 queue_family_id;
guint32 n_queues;
VkCmdPool cmd_pool;
};
struct _GstVulkanDeviceClass
{
GstObjectClass parent_class;
};
GstVulkanDevice * gst_vulkan_device_new (GstVulkanInstance * instance);
GstVulkanInstance * gst_vulkan_device_get_instance (GstVulkanDevice * device);
gboolean gst_vulkan_device_open (GstVulkanDevice * device,
GError ** error);
void gst_vulkan_device_close (GstVulkanDevice * device);
gpointer gst_vulkan_device_get_proc_address (GstVulkanDevice * device,
const gchar * name);
GstVulkanQueue * gst_vulkan_device_get_queue (GstVulkanDevice * device,
guint32 queue_family,
guint32 queue_i,
GError ** error);
VkPhysicalDevice gst_vulkan_device_get_physical_device (GstVulkanDevice * device);
gboolean gst_vulkan_device_create_cmd_buffer (GstVulkanDevice * device,
VkCmdBuffer * cmd,
GError ** error);
G_END_DECLS
#endif /* _VK_DEVICE_H_ */

246
ext/vulkan/vkdisplay.c Normal file
View file

@ -0,0 +1,246 @@
/*
* GStreamer
* Copyright (C) 2007 David A. Schleef <ds@schleef.org>
* Copyright (C) 2008 Julien Isorce <julien.isorce@gmail.com>
* Copyright (C) 2008 Filippo Argiolas <filippo.argiolas@gmail.com>
* Copyright (C) 2013 Matthew Waters <ystreet00@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "vkdisplay.h"
#if GST_VULKAN_HAVE_WINDOW_X11
#include "x11/vkdisplay_x11.h"
#endif
#if GST_VULKAN_HAVE_WINDOW_XCB
#include "xcb/vkdisplay_xcb.h"
#endif
#define GST_CAT_DEFAULT gst_vulkan_display_debug
GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
static void
_init_debug (void)
{
static volatile gsize _init = 0;
if (g_once_init_enter (&_init)) {
GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "vulkandisplay", 0,
"Vulkan display");
g_once_init_leave (&_init, 1);
}
}
G_DEFINE_TYPE_WITH_CODE (GstVulkanDisplay, gst_vulkan_display, GST_TYPE_OBJECT,
_init_debug ());
#define GST_VULKAN_DISPLAY_GET_PRIVATE(o) \
(G_TYPE_INSTANCE_GET_PRIVATE((o), GST_TYPE_VULKAN_DISPLAY, GstVulkanDisplayPrivate))
enum
{
SIGNAL_0,
CREATE_CONTEXT,
LAST_SIGNAL
};
/* static guint gst_vulkan_display_signals[LAST_SIGNAL] = { 0 }; */
static void gst_vulkan_display_finalize (GObject * object);
static gpointer gst_vulkan_display_default_get_handle (GstVulkanDisplay *
display);
static gpointer gst_vulkan_display_default_get_platform_handle (GstVulkanDisplay
* display);
static GstVulkanWindow
* gst_vulkan_display_default_create_window (GstVulkanDisplay * display);
struct _GstVulkanDisplayPrivate
{
gint dummy;
};
static void
gst_vulkan_display_class_init (GstVulkanDisplayClass * klass)
{
g_type_class_add_private (klass, sizeof (GstVulkanDisplayPrivate));
klass->get_handle = gst_vulkan_display_default_get_handle;
klass->get_platform_handle = gst_vulkan_display_default_get_platform_handle;
klass->create_window = gst_vulkan_display_default_create_window;
G_OBJECT_CLASS (klass)->finalize = gst_vulkan_display_finalize;
}
static void
gst_vulkan_display_init (GstVulkanDisplay * display)
{
display->priv = GST_VULKAN_DISPLAY_GET_PRIVATE (display);
display->type = GST_VULKAN_DISPLAY_TYPE_ANY;
}
static void
gst_vulkan_display_finalize (GObject * object)
{
G_OBJECT_CLASS (gst_vulkan_display_parent_class)->finalize (object);
}
/**
* gst_vulkan_display_new:
*
* Returns: (transfer full): a new #GstVulkanDisplay
*
* Since: 1.10
*/
GstVulkanDisplay *
gst_vulkan_display_new (void)
{
GstVulkanDisplay *display = NULL;
const gchar *user_choice, *platform_choice;
_init_debug ();
user_choice = g_getenv ("GST_GL_WINDOW");
platform_choice = g_getenv ("GST_GL_PLATFORM");
GST_INFO ("creating a display, user choice:%s (platform: %s)",
GST_STR_NULL (user_choice), GST_STR_NULL (platform_choice));
#if GST_VULKAN_HAVE_WINDOW_XCB
if (!display && (!user_choice || g_strstr_len (user_choice, 3, "xcb")))
display = GST_VULKAN_DISPLAY (gst_vulkan_display_xcb_new (NULL));
#endif
#if GST_VULKAN_HAVE_WINDOW_X11
if (!display && (!user_choice || g_strstr_len (user_choice, 3, "x11")))
display = GST_VULKAN_DISPLAY (gst_vulkan_display_x11_new (NULL));
#endif
if (!display) {
/* subclass returned a NULL window */
GST_WARNING ("Could not create display. user specified %s "
"(platform: %s), creating dummy",
GST_STR_NULL (user_choice), GST_STR_NULL (platform_choice));
return g_object_new (GST_TYPE_VULKAN_DISPLAY, NULL);
}
return display;
}
/**
* gst_vulkan_display_get_handle:
* @display: a #GstVulkanDisplay
*
* Returns: the winsys specific handle of @display
*
* Since: 1.10
*/
gpointer
gst_vulkan_display_get_handle (GstVulkanDisplay * display)
{
GstVulkanDisplayClass *klass;
g_return_val_if_fail (GST_IS_VULKAN_DISPLAY (display), NULL);
klass = GST_VULKAN_DISPLAY_GET_CLASS (display);
g_return_val_if_fail (klass->get_handle != NULL, NULL);
return klass->get_handle (display);
}
static gpointer
gst_vulkan_display_default_get_handle (GstVulkanDisplay * display)
{
return 0;
}
/**
* gst_vulkan_display_get_platform_handle:
* @display: a #GstVulkanDisplay
*
* Returns: the winsys specific handle of @display for use with the
* VK_EXT_KHR_swapchain extension.
*
* Since: 1.10
*/
gpointer
gst_vulkan_display_get_platform_handle (GstVulkanDisplay * display)
{
GstVulkanDisplayClass *klass;
g_return_val_if_fail (GST_IS_VULKAN_DISPLAY (display), NULL);
klass = GST_VULKAN_DISPLAY_GET_CLASS (display);
g_return_val_if_fail (klass->get_handle != NULL, NULL);
return klass->get_platform_handle (display);
}
static gpointer
gst_vulkan_display_default_get_platform_handle (GstVulkanDisplay * display)
{
return (gpointer) gst_vulkan_display_get_handle (display);
}
/**
* gst_vulkan_display_get_handle_type:
* @display: a #GstVulkanDisplay
*
* Returns: the #GstVulkanDisplayType of @display
*
* Since: 1.10
*/
GstVulkanDisplayType
gst_vulkan_display_get_handle_type (GstVulkanDisplay * display)
{
g_return_val_if_fail (GST_IS_VULKAN_DISPLAY (display),
GST_VULKAN_DISPLAY_TYPE_NONE);
return display->type;
}
/**
* gst_vulkan_display_create_window:
* @display: a #GstVulkanDisplay
*
* Returns: a new #GstVulkanWindow for @display or %NULL.
*/
GstVulkanWindow *
gst_vulkan_display_create_window (GstVulkanDisplay * display)
{
GstVulkanDisplayClass *klass;
GstVulkanWindow *window;
g_return_val_if_fail (GST_IS_VULKAN_DISPLAY (display), NULL);
klass = GST_VULKAN_DISPLAY_GET_CLASS (display);
g_return_val_if_fail (klass->create_window != NULL, NULL);
window = klass->create_window (display);
if (window) {
GST_OBJECT_LOCK (display);
display->windows = g_list_prepend (display->windows, window);
GST_OBJECT_UNLOCK (display);
}
return window;
}
static GstVulkanWindow *
gst_vulkan_display_default_create_window (GstVulkanDisplay * display)
{
return gst_vulkan_window_new (display);
}

91
ext/vulkan/vkdisplay.h Normal file
View file

@ -0,0 +1,91 @@
/*
* GStreamer
* Copyright (C) 2007 David A. Schleef <ds@schleef.org>
* Copyright (C) 2008 Julien Isorce <julien.isorce@gmail.com>
* Copyright (C) 2008 Filippo Argiolas <filippo.argiolas@gmail.com>
* Copyright (C) 2013 Matthew Waters <ystreet00@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef __GST_VULKAN_DISPLAY_H__
#define __GST_VULKAN_DISPLAY_H__
#include <gst/gst.h>
#include <vk.h>
G_BEGIN_DECLS
#define GST_TYPE_VULKAN_DISPLAY (gst_vulkan_display_get_type())
#define GST_VULKAN_DISPLAY(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_VULKAN_DISPLAY,GstVulkanDisplay))
#define GST_VULKAN_DISPLAY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), GST_TYPE_VULKAN_DISPLAY,GstVulkanDisplayClass))
#define GST_IS_VULKAN_DISPLAY(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_VULKAN_DISPLAY))
#define GST_IS_VULKAN_DISPLAY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), GST_TYPE_VULKAN_DISPLAY))
#define GST_VULKAN_DISPLAY_CAST(obj) ((GstVulkanDisplay*)(obj))
#define GST_VULKAN_DISPLAY_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS((o), GST_TYPE_VULKAN_DISPLAY, GstVulkanDisplayClass))
GType gst_vulkan_display_get_type (void);
typedef enum
{
GST_VULKAN_DISPLAY_TYPE_NONE = 0,
GST_VULKAN_DISPLAY_TYPE_X11 = (1 << 0),
GST_VULKAN_DISPLAY_TYPE_XCB = (1 << 1),
GST_VULKAN_DISPLAY_TYPE_WAYLAND = (1 << 2),
GST_VULKAN_DISPLAY_TYPE_MIR = (1 << 3),
GST_VULKAN_DISPLAY_TYPE_WIN32 = (1 << 4),
GST_VULKAN_DISPLAY_TYPE_ANY = G_MAXUINT32
} GstVulkanDisplayType;
/**
* GstVulkanDisplay:
*
* The contents of a #GstVulkanDisplay are private and should only be accessed
* through the provided API
*/
struct _GstVulkanDisplay
{
/* <private> */
GstObject object;
GstVulkanDisplayType type;
/* <protected> */
GList *windows; /* OBJECT lock */
GstVulkanDisplayPrivate *priv;
};
struct _GstVulkanDisplayClass
{
GstObjectClass object_class;
gpointer (*get_handle) (GstVulkanDisplay * display);
gpointer (*get_platform_handle) (GstVulkanDisplay * display); /* default chains up to @get_handle */
GstVulkanWindow * (*create_window) (GstVulkanDisplay * display);
};
GstVulkanDisplay *gst_vulkan_display_new (void);
gpointer gst_vulkan_display_get_handle (GstVulkanDisplay * display);
GstVulkanDisplayType gst_vulkan_display_get_handle_type (GstVulkanDisplay * display);
gpointer gst_vulkan_display_get_platform_handle (GstVulkanDisplay * display);
GstVulkanWindow * gst_vulkan_display_create_window (GstVulkanDisplay * display);
G_END_DECLS
#endif /* __GST_VULKAN_DISPLAY_H__ */

129
ext/vulkan/vkerror.c Normal file
View file

@ -0,0 +1,129 @@
/*
* GStreamer
* Copyright (C) 2015 Matthew Waters <matthew@centricular.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <glib/gprintf.h>
#include "vkerror.h"
/* *INDENT-OFF* */
static const struct
{
VkResult result;
const char *str;
} vk_result_string_map[] = {
{VK_ERROR_OUT_OF_HOST_MEMORY, "Out Of Host Memory"},
{VK_ERROR_OUT_OF_DEVICE_MEMORY, "Out of Device Memory"},
{VK_ERROR_INITIALIZATION_FAILED, "Initialization Failed"},
{VK_ERROR_DEVICE_LOST, "Device Lost"},
{VK_ERROR_MEMORY_MAP_FAILED, "Map Failed"},
{VK_ERROR_LAYER_NOT_PRESENT, "Layer Not Present"},
{VK_ERROR_EXTENSION_NOT_PRESENT, "Extension Not Present"},
{VK_ERROR_INCOMPATIBLE_DRIVER, "Incompatible Driver"},
};
static const struct
{
VkResult result;
GstVulkanError gst_enum;
} vk_result_gst_error_map[] = {
{VK_ERROR_OUT_OF_HOST_MEMORY, GST_VULKAN_ERROR_OUT_OF_HOST_MEMORY},
{VK_ERROR_OUT_OF_DEVICE_MEMORY, VK_ERROR_OUT_OF_DEVICE_MEMORY},
{VK_ERROR_INITIALIZATION_FAILED, GST_VULKAN_ERROR_INITIALIZATION_FAILED},
{VK_ERROR_DEVICE_LOST, GST_VULKAN_ERROR_DEVICE_LOST},
{VK_ERROR_MEMORY_MAP_FAILED, GST_VULKAN_ERROR_MEMORY_MAP_FAILED},
{VK_ERROR_LAYER_NOT_PRESENT, GST_VULKAN_ERROR_LAYER_NOT_PRESENT},
{VK_ERROR_EXTENSION_NOT_PRESENT, GST_VULKAN_ERROR_EXTENSION_NOT_PRESENT},
{VK_ERROR_INCOMPATIBLE_DRIVER, GST_VULKAN_ERROR_INCOMPATIBLE_DRIVER},
};
/* *INDENT-ON* */
GQuark
gst_vulkan_error_quark (void)
{
return g_quark_from_static_string ("gst-vulkan-error");
}
static const char *
_vk_result_to_string (VkResult result)
{
int i;
if (result >= 0)
return NULL;
if (result < VK_RESULT_BEGIN_RANGE)
return "Unknown Error";
for (i = 0; i < G_N_ELEMENTS (vk_result_string_map); i++) {
if (result == vk_result_string_map[i].result)
return vk_result_string_map[i].str;
}
return "Unknown Error";
}
static GstVulkanError
_vk_result_to_g_error_enum (VkResult result)
{
int i;
if (result >= 0)
return 0;
if (result < VK_RESULT_BEGIN_RANGE)
return 0;
for (i = 0; i < G_N_ELEMENTS (vk_result_gst_error_map); i++) {
if (result == vk_result_gst_error_map[i].result)
return vk_result_gst_error_map[i].gst_enum;
}
return GST_VULKAN_ERROR_FAILED;
}
VkResult
gst_vulkan_error_to_g_error (VkResult result, GError ** error,
const char *format, ...)
{
GstVulkanError gst_enum;
const char *result_str;
gchar *string;
va_list args;
if (error == NULL)
/* we don't have an error to set */
return result;
result_str = _vk_result_to_string (result);
if (result_str == NULL)
return result;
gst_enum = _vk_result_to_g_error_enum (result);
va_start (args, format);
g_vasprintf (&string, format, args);
va_end (args);
g_set_error (error, GST_VULKAN_ERROR, gst_enum, "%s: %s", result_str, string);
return result;
}

50
ext/vulkan/vkerror.h Normal file
View file

@ -0,0 +1,50 @@
/*
* GStreamer
* Copyright (C) 2015 Matthew Waters <matthew@centricular.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef _VK_ERROR_H_
#define _VK_ERROR_H_
#include <gst/gst.h>
#include <vulkan/vulkan.h>
G_BEGIN_DECLS
#define GST_VULKAN_ERROR (gst_vulkan_error_quark ())
GQuark gst_vulkan_error_quark (void);
typedef enum
{
GST_VULKAN_ERROR_FAILED = 0,
GST_VULKAN_ERROR_OUT_OF_HOST_MEMORY = -1,
GST_VULKAN_ERROR_OUT_OF_DEVICE_MEMORY = -2,
GST_VULKAN_ERROR_INITIALIZATION_FAILED = -3,
GST_VULKAN_ERROR_DEVICE_LOST = -4,
GST_VULKAN_ERROR_MEMORY_MAP_FAILED = -5,
GST_VULKAN_ERROR_LAYER_NOT_PRESENT = -6,
GST_VULKAN_ERROR_EXTENSION_NOT_PRESENT = -7,
GST_VULKAN_ERROR_INCOMPATIBLE_DRIVER = -8,
} GstVulkanError;
/* only fills error iff error != NULL and result < 0 */
VkResult gst_vulkan_error_to_g_error (VkResult result, GError ** error, const char * format, ...) G_GNUC_PRINTF (3, 4);
G_END_DECLS
#endif /* _VK_INSTANCE_H_ */

578
ext/vulkan/vkimagememory.c Normal file
View file

@ -0,0 +1,578 @@
/*
* GStreamer
* Copyright (C) 2015 Matthew Waters <matthew@centricular.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "vkimagememory.h"
/**
* SECTION:vkimagememory
* @short_description: memory subclass for Vulkan image memory
* @see_also: #GstMemory, #GstAllocator
*
* GstVulkanImageMemory is a #GstMemory subclass providing support for the
* mapping of Vulkan device memory.
*/
#define GST_CAT_DEFUALT GST_CAT_VULKAN_IMAGE_MEMORY
GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFUALT);
static GstAllocator *_vulkan_image_memory_allocator;
VkFormat
gst_vulkan_format_from_video_format (GstVideoFormat v_format, guint plane)
{
guint n_plane_components;
switch (v_format) {
case GST_VIDEO_FORMAT_RGBx:
case GST_VIDEO_FORMAT_BGRx:
case GST_VIDEO_FORMAT_xRGB:
case GST_VIDEO_FORMAT_xBGR:
case GST_VIDEO_FORMAT_RGBA:
case GST_VIDEO_FORMAT_BGRA:
case GST_VIDEO_FORMAT_ARGB:
case GST_VIDEO_FORMAT_ABGR:
case GST_VIDEO_FORMAT_AYUV:
n_plane_components = 4;
break;
case GST_VIDEO_FORMAT_RGB:
case GST_VIDEO_FORMAT_BGR:
n_plane_components = 3;
break;
case GST_VIDEO_FORMAT_RGB16:
case GST_VIDEO_FORMAT_BGR16:
return GST_VIDEO_GL_TEXTURE_TYPE_RGB16;
case GST_VIDEO_FORMAT_GRAY16_BE:
case GST_VIDEO_FORMAT_GRAY16_LE:
case GST_VIDEO_FORMAT_YUY2:
case GST_VIDEO_FORMAT_UYVY:
n_plane_components = 2;
break;
case GST_VIDEO_FORMAT_NV12:
case GST_VIDEO_FORMAT_NV21:
n_plane_components = plane == 0 ? 1 : 2;
break;
case GST_VIDEO_FORMAT_GRAY8:
case GST_VIDEO_FORMAT_Y444:
case GST_VIDEO_FORMAT_Y42B:
case GST_VIDEO_FORMAT_Y41B:
case GST_VIDEO_FORMAT_I420:
case GST_VIDEO_FORMAT_YV12:
n_plane_components = 1;
break;
default:
n_plane_components = 4;
g_assert_not_reached ();
break;
}
switch (n_plane_components) {
case 4:
return VK_FORMAT_R8G8B8A8_UNORM;
case 3:
return VK_FORMAT_R8G8B8_UNORM;
case 2:
return VK_FORMAT_R8G8_UNORM;
case 1:
return VK_FORMAT_R8_UNORM;
default:
g_assert_not_reached ();
return VK_FORMAT_R8G8B8A8_UNORM;
}
}
static void
_view_create_info (VkImage image, VkFormat format, VkImageViewCreateInfo * info)
{
info->sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
info->pNext = NULL;
info->image = image;
info->format = format;
info->channels.r = VK_CHANNEL_SWIZZLE_R;
info->channels.g = VK_CHANNEL_SWIZZLE_G;
info->channels.b = VK_CHANNEL_SWIZZLE_B;
info->channels.a = VK_CHANNEL_SWIZZLE_A;
info->subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
info->subresourceRange.baseMipLevel = 0;
info->subresourceRange.mipLevels = 1;
info->subresourceRange.baseArrayLayer = 0;
info->subresourceRange.arraySize = 1;
info->viewType = VK_IMAGE_VIEW_TYPE_2D;
info->flags = 0;
}
static gboolean
_find_memory_type_index_with_type_properties (GstVulkanDevice * device,
guint32 typeBits, VkFlags properties, guint32 * typeIndex)
{
guint32 i;
/* Search memtypes to find first index with those properties */
for (i = 0; i < 32; i++) {
if ((typeBits & 1) == 1) {
/* Type is available, does it match user properties? */
if ((device->memory_properties.memoryTypes[i].
propertyFlags & properties) == properties) {
*typeIndex = i;
return TRUE;
}
}
typeBits >>= 1;
}
return FALSE;
}
static gboolean
_create_info_from_args (VkImageCreateInfo * info, VkFormat format, gsize width,
gsize height, VkImageTiling tiling, VkImageUsageFlags usage)
{
/* FIXME: validate these */
info->sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
info->pNext = NULL;
info->imageType = VK_IMAGE_TYPE_2D;
info->format = format;
info->extent.width = (gint32) width;
info->extent.height = (gint32) height;
info->extent.depth = 1;
info->mipLevels = 1;
info->arraySize = 1;
info->samples = 1;
info->tiling = tiling;
info->usage = usage;
info->sharingMode = VK_SHARING_MODE_EXCLUSIVE;
info->queueFamilyCount = 0;
info->pQueueFamilyIndices = NULL;
info->initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
return TRUE;
}
static void
_vk_image_mem_init (GstVulkanImageMemory * mem, GstAllocator * allocator,
GstMemory * parent, GstVulkanDevice * device, GstAllocationParams * params,
gsize size, gpointer user_data, GDestroyNotify notify)
{
gsize align = gst_memory_alignment, offset = 0, maxsize = size;
GstMemoryFlags flags = 0;
if (params) {
flags = params->flags;
align |= params->align;
offset = params->prefix;
maxsize += params->prefix + params->padding + align;
}
gst_memory_init (GST_MEMORY_CAST (mem), flags, allocator, parent, maxsize,
align, offset, size);
mem->device = gst_object_ref (device);
mem->image_layout = VK_IMAGE_LAYOUT_UNDEFINED;
mem->wrapped = FALSE;
mem->notify = notify;
mem->user_data = user_data;
g_mutex_init (&mem->lock);
GST_CAT_DEBUG (GST_CAT_VULKAN_IMAGE_MEMORY, "new GL buffer memory:%p size:%"
G_GSIZE_FORMAT, mem, maxsize);
}
static GstVulkanImageMemory *
_vk_image_mem_new_alloc (GstAllocator * allocator, GstMemory * parent,
GstVulkanDevice * device, VkFormat format, gsize width, gsize height,
VkImageTiling tiling, VkImageUsageFlags usage,
VkMemoryPropertyFlags mem_prop_flags, gpointer user_data,
GDestroyNotify notify)
{
GstVulkanImageMemory *mem = g_new0 (GstVulkanImageMemory, 1);
GstAllocationParams params = { 0, };
VkImageViewCreateInfo view_info;
VkImageCreateInfo image_info;
guint32 memory_type_index;
VkPhysicalDevice gpu;
GError *error = NULL;
VkImage image;
VkResult err;
gpu = gst_vulkan_device_get_physical_device (device);
if (!_create_info_from_args (&image_info, format, width, height, tiling,
usage)) {
GST_CAT_ERROR (GST_CAT_VULKAN_IMAGE_MEMORY, "Incorrect image parameters");
goto error;
}
err = vkCreateImage (device->device, &image_info, &image);
if (gst_vulkan_error_to_g_error (err, &error, "vkCreateImage") < 0)
goto vk_error;
err =
vkGetImageMemoryRequirements (device->device, image, &mem->requirements);
if (gst_vulkan_error_to_g_error (err, &error,
"vkGetImageMemoryRequirements") < 0) {
vkDestroyImage (device->device, image);
goto vk_error;
}
params.align = mem->requirements.alignment;
_vk_image_mem_init (mem, allocator, parent, device, &params,
mem->requirements.size, user_data, notify);
mem->create_info = image_info;
mem->image = image;
err = vkGetPhysicalDeviceImageFormatProperties (gpu, format, VK_IMAGE_TYPE_2D,
tiling, usage, 0, &mem->format_properties);
if (gst_vulkan_error_to_g_error (err, &error,
"vkGetPhysicalDeviceImageFormatProperties") < 0)
goto vk_error;
if (!_find_memory_type_index_with_type_properties (device,
mem->requirements.memoryTypeBits, mem_prop_flags,
&memory_type_index)) {
GST_CAT_ERROR (GST_CAT_VULKAN_IMAGE_MEMORY,
"Could not find suitable memory type");
goto error;
}
mem->vk_mem = (GstVulkanMemory *)
gst_vulkan_memory_alloc (device, memory_type_index, &params,
mem->requirements.size, mem_prop_flags);
if (!mem->vk_mem) {
GST_CAT_ERROR (GST_CAT_VULKAN_IMAGE_MEMORY,
"Failed to allocate device memory");
goto error;
}
err =
vkBindImageMemory (device->device, mem->image, mem->vk_mem->mem_ptr,
0 /* offset */ );
if (gst_vulkan_error_to_g_error (err, &error, "vkBindImageMemory") < 0)
goto vk_error;
if (usage & (VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_STORAGE_BIT |
VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT)) {
_view_create_info (mem->image, format, &view_info);
err = vkCreateImageView (device->device, &view_info, &mem->view);
if (gst_vulkan_error_to_g_error (err, &error, "vkCreateImageView") < 0)
goto vk_error;
}
return mem;
vk_error:
{
GST_CAT_ERROR (GST_CAT_VULKAN_IMAGE_MEMORY,
"Failed to allocate image memory %s", error->message);
g_clear_error (&error);
goto error;
}
error:
{
gst_memory_unref ((GstMemory *) mem);
return NULL;
}
}
static GstVulkanImageMemory *
_vk_image_mem_new_wrapped (GstAllocator * allocator, GstMemory * parent,
GstVulkanDevice * device, VkImage image, VkFormat format, gsize width,
gsize height, VkImageTiling tiling, VkImageUsageFlags usage,
gpointer user_data, GDestroyNotify notify)
{
GstVulkanImageMemory *mem = g_new0 (GstVulkanImageMemory, 1);
GstAllocationParams params = { 0, };
VkImageViewCreateInfo view_info;
VkPhysicalDevice gpu;
GError *error = NULL;
VkResult err;
gpu = gst_vulkan_device_get_physical_device (device);
mem->image = image;
err =
vkGetImageMemoryRequirements (device->device, mem->image,
&mem->requirements);
if (gst_vulkan_error_to_g_error (err, &error,
"vkGetImageMemoryRequirements") < 0) {
vkDestroyImage (device->device, image);
goto vk_error;
}
params.flags = GST_MEMORY_FLAG_NOT_MAPPABLE | GST_MEMORY_FLAG_READONLY;
_vk_image_mem_init (mem, allocator, parent, device, &params,
mem->requirements.size, user_data, notify);
mem->wrapped = TRUE;
if (!_create_info_from_args (&mem->create_info, format, width, height, tiling,
usage)) {
GST_CAT_ERROR (GST_CAT_VULKAN_IMAGE_MEMORY, "Incorrect image parameters");
goto error;
}
err = vkGetPhysicalDeviceImageFormatProperties (gpu, format, VK_IMAGE_TYPE_2D,
tiling, usage, 0, &mem->format_properties);
if (gst_vulkan_error_to_g_error (err, &error,
"vkGetPhysicalDeviceImageFormatProperties") < 0)
goto vk_error;
if (usage & (VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_STORAGE_BIT |
VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT)) {
_view_create_info (mem->image, format, &view_info);
err = vkCreateImageView (device->device, &view_info, &mem->view);
if (gst_vulkan_error_to_g_error (err, &error, "vkCreateImageView") < 0)
goto vk_error;
}
return mem;
vk_error:
{
GST_CAT_ERROR (GST_CAT_VULKAN_IMAGE_MEMORY,
"Failed to allocate image memory %s", error->message);
g_clear_error (&error);
goto error;
}
error:
{
gst_memory_unref ((GstMemory *) mem);
return NULL;
}
}
static gpointer
_vk_image_mem_map_full (GstVulkanImageMemory * mem, GstMapInfo * info,
gsize size)
{
GstMapInfo *vk_map_info = g_new0 (GstMapInfo, 1);
info->user_data[0] = vk_map_info;
/* FIXME: possible layout transformation needed */
if (!mem->vk_mem)
return NULL;
if (!gst_memory_map ((GstMemory *) mem->vk_mem, vk_map_info, info->flags))
return NULL;
return vk_map_info->data;
}
static void
_vk_image_mem_unmap_full (GstVulkanImageMemory * mem, GstMapInfo * info)
{
gst_memory_unmap ((GstMemory *) mem->vk_mem, info->user_data[0]);
g_free (info->user_data[0]);
}
static GstMemory *
_vk_image_mem_copy (GstVulkanImageMemory * src, gssize offset, gssize size)
{
return NULL;
}
static GstMemory *
_vk_image_mem_share (GstVulkanImageMemory * mem, gssize offset, gssize size)
{
return NULL;
}
static gboolean
_vk_image_mem_is_span (GstVulkanImageMemory * mem1, GstVulkanImageMemory * mem2,
gsize * offset)
{
return FALSE;
}
static GstMemory *
_vk_image_mem_alloc (GstAllocator * allocator, gsize size,
GstAllocationParams * params)
{
g_critical ("Subclass should override GstAllocatorClass::alloc() function");
return NULL;
}
static void
_vk_image_mem_free (GstAllocator * allocator, GstMemory * memory)
{
GstVulkanImageMemory *mem = (GstVulkanImageMemory *) memory;
GST_CAT_TRACE (GST_CAT_VULKAN_IMAGE_MEMORY, "freeing image memory:%p "
"id:%" G_GUINT64_FORMAT, mem, mem->image.handle);
if (mem->image.handle && !mem->wrapped)
vkDestroyImage (mem->device->device, mem->image);
if (mem->view.handle)
vkDestroyImageView (mem->device->device, mem->view);
if (mem->vk_mem)
gst_memory_unref ((GstMemory *) mem->vk_mem);
if (mem->notify)
mem->notify (mem->user_data);
gst_object_unref (mem->device);
}
gboolean
gst_vulkan_image_memory_set_layout (GstVulkanImageMemory * vk_mem,
VkImageLayout image_layout, VkImageMemoryBarrier * barrier)
{
/* validate vk_mem->usage with image_layout */
barrier->sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
barrier->pNext = NULL;
barrier->outputMask = 0;
barrier->inputMask = 0;
barrier->oldLayout = vk_mem->image_layout;
barrier->newLayout = image_layout;
barrier->image = vk_mem->image;
barrier->subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR;
barrier->subresourceRange.baseMipLevel = 0;
barrier->subresourceRange.mipLevels = 1;
barrier->subresourceRange.baseArrayLayer = 0;
barrier->subresourceRange.arraySize = 0;
if (image_layout == VK_IMAGE_LAYOUT_TRANSFER_DESTINATION_OPTIMAL) {
/* Make sure anything that was copying from this image has completed */
barrier->inputMask = VK_MEMORY_INPUT_TRANSFER_BIT;
}
if (image_layout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) {
/* Make sure any Copy or CPU writes to image are flushed */
barrier->outputMask =
VK_MEMORY_OUTPUT_HOST_WRITE_BIT | VK_MEMORY_OUTPUT_TRANSFER_BIT;
}
/* FIXME: what if the barrier is never submitted or is submitted out of order? */
vk_mem->image_layout = image_layout;
return TRUE;
}
/**
* gst_vulkan_image_memory_alloc:
* @device:a #GstVulkanDevice
* @memory_type_index: the Vulkan memory type index
* @params: a #GstAllocationParams
* @size: the size to allocate
*
* Allocated a new #GstVulkanImageMemory.
*
* Returns: a #GstMemory object backed by a vulkan device memory
*/
GstMemory *
gst_vulkan_image_memory_alloc (GstVulkanDevice * device, VkFormat format,
gsize width, gsize height, VkImageTiling tiling, VkImageUsageFlags usage,
VkMemoryPropertyFlags mem_prop_flags)
{
GstVulkanImageMemory *mem;
mem = _vk_image_mem_new_alloc (_vulkan_image_memory_allocator, NULL, device,
format, width, height, tiling, usage, mem_prop_flags, NULL, NULL);
return (GstMemory *) mem;
}
GstMemory *
gst_vulkan_image_memory_wrapped (GstVulkanDevice * device, VkImage image,
VkFormat format, gsize width, gsize height, VkImageTiling tiling,
VkImageUsageFlags usage, gpointer user_data, GDestroyNotify notify)
{
GstVulkanImageMemory *mem;
mem = _vk_image_mem_new_wrapped (_vulkan_image_memory_allocator, NULL, device,
image, format, width, height, tiling, usage, user_data, notify);
return (GstMemory *) mem;
}
G_DEFINE_TYPE (GstVulkanImageMemoryAllocator, gst_vulkan_image_memory_allocator,
GST_TYPE_ALLOCATOR);
static void
gst_vulkan_image_memory_allocator_class_init (GstVulkanImageMemoryAllocatorClass
* klass)
{
GstAllocatorClass *allocator_class = (GstAllocatorClass *) klass;
allocator_class->alloc = _vk_image_mem_alloc;
allocator_class->free = _vk_image_mem_free;
}
static void
gst_vulkan_image_memory_allocator_init (GstVulkanImageMemoryAllocator *
allocator)
{
GstAllocator *alloc = GST_ALLOCATOR_CAST (allocator);
alloc->mem_type = GST_VULKAN_IMAGE_MEMORY_ALLOCATOR_NAME;
alloc->mem_map_full = (GstMemoryMapFullFunction) _vk_image_mem_map_full;
alloc->mem_unmap_full = (GstMemoryUnmapFullFunction) _vk_image_mem_unmap_full;
alloc->mem_copy = (GstMemoryCopyFunction) _vk_image_mem_copy;
alloc->mem_share = (GstMemoryShareFunction) _vk_image_mem_share;
alloc->mem_is_span = (GstMemoryIsSpanFunction) _vk_image_mem_is_span;
}
/**
* gst_vulkan_image_memory_init_once:
*
* Initializes the Vulkan memory allocator. It is safe to call this function
* multiple times. This must be called before any other #GstVulkanImageMemory operation.
*/
void
gst_vulkan_image_memory_init_once (void)
{
static volatile gsize _init = 0;
if (g_once_init_enter (&_init)) {
GST_DEBUG_CATEGORY_INIT (GST_CAT_VULKAN_IMAGE_MEMORY, "vulkanimagememory",
0, "Vulkan Image Memory");
_vulkan_image_memory_allocator =
g_object_new (gst_vulkan_image_memory_allocator_get_type (), NULL);
gst_allocator_register (GST_VULKAN_IMAGE_MEMORY_ALLOCATOR_NAME,
gst_object_ref (_vulkan_image_memory_allocator));
g_once_init_leave (&_init, 1);
}
}
/**
* gst_is_vulkan_image_memory:
* @mem:a #GstMemory
*
* Returns: whether the memory at @mem is a #GstVulkanImageMemory
*/
gboolean
gst_is_vulkan_image_memory (GstMemory * mem)
{
return mem != NULL && mem->allocator != NULL &&
g_type_is_a (G_OBJECT_TYPE (mem->allocator),
GST_TYPE_VULKAN_IMAGE_MEMORY_ALLOCATOR);
}

116
ext/vulkan/vkimagememory.h Normal file
View file

@ -0,0 +1,116 @@
/*
* GStreamer
* Copyright (C) 2015 Matthew Waters <matthew@centricular.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef _VK_IMAGE_MEMORY_H_
#define _VK_IMAGE_MEMORY_H_
#include <gst/gst.h>
#include <gst/gstallocator.h>
#include <gst/gstmemory.h>
#include <gst/video/video.h>
#include <vk.h>
G_BEGIN_DECLS
#define GST_TYPE_VULKAN_IMAGE_MEMORY_ALLOCATOR (gst_vulkan_image_memory_allocator_get_type())
GType gst_vulkan_image_memory_allocator_get_type(void);
#define GST_IS_VULKAN_IMAGE_MEMORY_ALLOCATOR(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_VULKAN_IMAGE_MEMORY_ALLOCATOR))
#define GST_IS_VULKAN_IMAGE_MEMORY_ALLOCATOR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_VULKAN_IMAGE_MEMORY_ALLOCATOR))
#define GST_VULKAN_IMAGE_MEMORY_ALLOCATOR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_VULKAN_MEMORY_ALLOCATOR, GstVulkanImageMemoryAllocatorClass))
#define GST_VULKAN_IMAGE_MEMORY_ALLOCATOR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_VULKAN_MEMORY_ALLOCATOR, GstVulkanImageMemoryAllocator))
#define GST_VULKAN_IMAGE_MEMORY_ALLOCATOR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_VULKAN_MEMORY_ALLOCATOR, GstVulkanImageMemoryAllocatorClass))
#define GST_VULKAN_IMAGE_MEMORY_ALLOCATOR_CAST(obj) ((GstVulkanImageMemoryAllocator *)(obj))
#define GST_VULKAN_IMAGE_MEMORY_ALLOCATOR_NAME "VulkanImage"
struct _GstVulkanImageMemory
{
GstMemory parent;
GstVulkanDevice * device;
VkImage image;
VkImageLayout image_layout;
VkImageView view;
GstVulkanMemory *vk_mem;
VkImageCreateInfo create_info;
VkMemoryRequirements requirements;
VkImageFormatProperties format_properties;
GMutex lock;
gboolean wrapped;
GDestroyNotify notify;
gpointer user_data;
};
/**
* GstVulkanImageMemoryAllocator
*
* Opaque #GstVulkanImageMemoryAllocator struct
*/
struct _GstVulkanImageMemoryAllocator
{
GstAllocator parent;
};
/**
* GstVulkanImageMemoryAllocatorClass:
*
* The #GstVulkanImageMemoryAllocatorClass only contains private data
*/
struct _GstVulkanImageMemoryAllocatorClass
{
GstAllocatorClass parent_class;
};
void gst_vulkan_image_memory_init_once (void);
gboolean gst_is_vulkan_image_memory (GstMemory * mem);
GstMemory * gst_vulkan_image_memory_alloc (GstVulkanDevice * device,
VkFormat format,
gsize width,
gsize height,
VkImageTiling tiling,
VkImageUsageFlags usage,
VkMemoryPropertyFlags mem_prop_flags);
GstMemory * gst_vulkan_image_memory_wrapped (GstVulkanDevice * device,
VkImage image,
VkFormat format,
gsize width,
gsize height,
VkImageTiling tiling,
VkImageUsageFlags usage,
gpointer user_data,
GDestroyNotify notify);
gboolean gst_vulkan_image_memory_set_layout (GstVulkanImageMemory * vk_mem,
VkImageLayout,
VkImageMemoryBarrier * barrier);
VkFormat gst_vulkan_format_from_video_format (GstVideoFormat v_format,
guint plane);
G_END_DECLS
#endif /* _VK_IMAGE_MEMORY_H_ */

315
ext/vulkan/vkinstance.c Normal file
View file

@ -0,0 +1,315 @@
/*
* GStreamer
* Copyright (C) 2015 Matthew Waters <matthew@centricular.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "vkinstance.h"
#include "vkutils.h"
#include <string.h>
#define APP_SHORT_NAME "GStreamer"
static const char *instance_validation_layers[] = {
/* FIXME: until the loader stops segfaulting/hanging we don't have any
* validation layers for the instance */
"Threading",
"MemTracker",
"ObjectTracker",
"DrawState",
"ParamChecker",
"ShaderChecker",
"Swapchain",
"DeviceLimits",
"Image",
};
#define GST_CAT_DEFAULT gst_vulkan_instance_debug
GST_DEBUG_CATEGORY (GST_CAT_DEFAULT);
GST_DEBUG_CATEGORY (GST_VULKAN_DEBUG_CAT);
#define gst_vulkan_instance_parent_class parent_class
G_DEFINE_TYPE_WITH_CODE (GstVulkanInstance, gst_vulkan_instance,
GST_TYPE_OBJECT, GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT,
"vulkaninstance", 0, "Vulkan Instance");
GST_DEBUG_CATEGORY_INIT (GST_VULKAN_DEBUG_CAT,
"vulkandebug", 0, "Vulkan Debug"));
static void gst_vulkan_instance_finalize (GObject * object);
GstVulkanInstance *
gst_vulkan_instance_new (void)
{
return g_object_new (GST_TYPE_VULKAN_INSTANCE, NULL);
}
static void
gst_vulkan_instance_init (GstVulkanInstance * instance)
{
}
static void
gst_vulkan_instance_class_init (GstVulkanInstanceClass * instance_class)
{
gst_vulkan_memory_init_once ();
gst_vulkan_image_memory_init_once ();
G_OBJECT_CLASS (instance_class)->finalize = gst_vulkan_instance_finalize;
}
static void
gst_vulkan_instance_finalize (GObject * object)
{
GstVulkanInstance *instance = GST_VULKAN_INSTANCE (object);
if (instance->instance)
vkDestroyInstance (instance->instance);
instance->instance = NULL;
}
static VkBool32
_gst_vk_debug_callback (VkFlags msgFlags, VkDbgObjectType objType,
uint64_t srcObject, size_t location, int32_t msgCode,
const char *pLayerPrefix, const char *pMsg, void *pUserData)
{
if (msgFlags & VK_DBG_REPORT_ERROR_BIT) {
GST_CAT_ERROR (GST_VULKAN_DEBUG_CAT, "[%s] Code %d : %s", pLayerPrefix,
msgCode, pMsg);
} else if (msgFlags & VK_DBG_REPORT_WARN_BIT) {
GST_CAT_WARNING (GST_VULKAN_DEBUG_CAT, "[%s] Code %d : %s", pLayerPrefix,
msgCode, pMsg);
} else if (msgFlags & VK_DBG_REPORT_INFO_BIT) {
GST_CAT_LOG (GST_VULKAN_DEBUG_CAT, "[%s] Code %d : %s", pLayerPrefix,
msgCode, pMsg);
} else if (msgFlags & VK_DBG_REPORT_PERF_WARN_BIT) {
GST_CAT_FIXME (GST_VULKAN_DEBUG_CAT, "[%s] Code %d : %s", pLayerPrefix,
msgCode, pMsg);
} else if (msgFlags & VK_DBG_REPORT_DEBUG_BIT) {
GST_CAT_TRACE (GST_VULKAN_DEBUG_CAT, "[%s] Code %d : %s", pLayerPrefix,
msgCode, pMsg);
} else {
return FALSE;
}
/*
* false indicates that layer should not bail-out of an
* API call that had validation failures. This may mean that the
* app dies inside the driver due to invalid parameter(s).
* That's what would happen without validation layers, so we'll
* keep that behavior here.
*/
return FALSE;
}
gboolean
gst_vulkan_instance_open (GstVulkanInstance * instance, GError ** error)
{
VkExtensionProperties *instance_extensions;
char *extension_names[64]; /* FIXME: make dynamic */
VkLayerProperties *instance_layers;
uint32_t instance_extension_count = 0;
uint32_t enabled_extension_count = 0;
uint32_t instance_layer_count = 0;
uint32_t enabled_layer_count = 0;
gboolean validation_found;
gboolean have_swapchain_ext = FALSE;
VkResult err;
/* Look for validation layers */
err = vkEnumerateInstanceLayerProperties (&instance_layer_count, NULL);
if (gst_vulkan_error_to_g_error (err, error,
"vKEnumerateInstanceLayerProperties") < 0)
return FALSE;
instance_layers = g_new0 (VkLayerProperties, instance_layer_count);
err =
vkEnumerateInstanceLayerProperties (&instance_layer_count,
instance_layers);
if (gst_vulkan_error_to_g_error (err, error,
"vKEnumerateInstanceLayerProperties") < 0) {
g_free (instance_layers);
return FALSE;
}
/* TODO: allow outside selection */
validation_found =
_check_for_all_layers (G_N_ELEMENTS (instance_validation_layers),
instance_validation_layers, instance_layer_count, instance_layers);
if (!validation_found) {
g_error ("vkEnumerateInstanceLayerProperties failed to find"
"required validation layer.\n\n"
"Please look at the Getting Started guide for additional "
"information.\nvkCreateInstance Failure");
}
enabled_layer_count = G_N_ELEMENTS (instance_validation_layers);
err =
vkEnumerateInstanceExtensionProperties (NULL, &instance_extension_count,
NULL);
if (gst_vulkan_error_to_g_error (err, error,
"vkEnumerateInstanceExtensionProperties") < 0) {
g_free (instance_layers);
return FALSE;
}
memset (extension_names, 0, sizeof (extension_names));
instance_extensions =
g_new0 (VkExtensionProperties, instance_extension_count);
err =
vkEnumerateInstanceExtensionProperties (NULL, &instance_extension_count,
instance_extensions);
if (gst_vulkan_error_to_g_error (err, error,
"vkEnumerateInstanceExtensionProperties") < 0) {
g_free (instance_layers);
g_free (instance_extensions);
return FALSE;
}
/* TODO: allow outside selection */
for (uint32_t i = 0; i < instance_extension_count; i++) {
if (!g_strcmp0 ("VK_EXT_KHR_swapchain", instance_extensions[i].extName)) {
have_swapchain_ext = TRUE;
extension_names[enabled_extension_count++] =
(gchar *) "VK_EXT_KHR_swapchain";
}
if (!g_strcmp0 (VK_DEBUG_REPORT_EXTENSION_NAME,
instance_extensions[i].extName)) {
extension_names[enabled_extension_count++] =
(gchar *) VK_DEBUG_REPORT_EXTENSION_NAME;
}
g_assert (enabled_extension_count < 64);
}
if (!have_swapchain_ext) {
g_error ("vkEnumerateInstanceExtensionProperties failed to find the "
"\"VK_EXT_KHR_swapchain\" extension.\n\nDo you have a compatible "
"Vulkan installable client driver (ICD) installed?\nPlease "
"look at the Getting Started guide for additional "
"information.\nvkCreateInstance Failure");
}
{
const VkApplicationInfo app = {
.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO,
.pNext = NULL,
.pAppName = APP_SHORT_NAME,
.appVersion = 0,
.pEngineName = APP_SHORT_NAME,
.engineVersion = 0,
.apiVersion = VK_API_VERSION,
};
VkInstanceCreateInfo inst_info = {
.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO,
.pNext = NULL,
.pAppInfo = &app,
.pAllocCb = NULL,
.layerCount = enabled_layer_count,
.ppEnabledLayerNames = (const char *const *) instance_validation_layers,
.extensionCount = enabled_extension_count,
.ppEnabledExtensionNames = (const char *const *) extension_names,
};
err = vkCreateInstance (&inst_info, &instance->instance);
if (gst_vulkan_error_to_g_error (err, error, "vkCreateInstance") < 0) {
g_free (instance_layers);
g_free (instance_extensions);
return FALSE;
}
}
g_free (instance_layers);
g_free (instance_extensions);
err =
vkEnumeratePhysicalDevices (instance->instance,
&instance->n_physical_devices, NULL);
if (gst_vulkan_error_to_g_error (err, error,
"vkEnumeratePhysicalDevices") < 0)
return FALSE;
g_assert (instance->n_physical_devices > 0);
instance->physical_devices =
g_new0 (VkPhysicalDevice, instance->n_physical_devices);
err =
vkEnumeratePhysicalDevices (instance->instance,
&instance->n_physical_devices, instance->physical_devices);
if (gst_vulkan_error_to_g_error (err, error,
"vkEnumeratePhysicalDevices") < 0)
return FALSE;
instance->dbgCreateMsgCallback = (PFN_vkDbgCreateMsgCallback)
gst_vulkan_instance_get_proc_address (instance, "vkDbgCreateMsgCallback");
if (!instance->dbgCreateMsgCallback) {
g_set_error (error, GST_VULKAN_ERROR, GST_VULKAN_ERROR_FAILED,
"Failed to retreive vkDbgCreateMsgCallback");
return FALSE;
}
instance->dbgDestroyMsgCallback = (PFN_vkDbgDestroyMsgCallback)
gst_vulkan_instance_get_proc_address (instance,
"vkDbgDestroyMsgCallback");
if (!instance->dbgDestroyMsgCallback) {
g_set_error (error, GST_VULKAN_ERROR, GST_VULKAN_ERROR_FAILED,
"Failed to retreive vkDbgDestroyMsgCallback");
return FALSE;
}
instance->dbgBreakCallback =
(PFN_vkDbgMsgCallback) gst_vulkan_instance_get_proc_address (instance,
"vkDbgBreakCallback");
if (!instance->dbgBreakCallback) {
g_set_error (error, GST_VULKAN_ERROR, GST_VULKAN_ERROR_FAILED,
"Failed to retreive vkDbgBreakCallback");
return FALSE;
}
err = instance->dbgCreateMsgCallback (instance->instance,
VK_DBG_REPORT_ERROR_BIT | VK_DBG_REPORT_WARN_BIT | VK_DBG_REPORT_INFO_BIT
| VK_DBG_REPORT_DEBUG_BIT | VK_DBG_REPORT_PERF_WARN_BIT,
_gst_vk_debug_callback, NULL, &instance->msg_callback);
if (gst_vulkan_error_to_g_error (err, error, "vkDbgCreateMsgCallback") < 0)
return FALSE;
return TRUE;
}
gpointer
gst_vulkan_instance_get_proc_address (GstVulkanInstance * instance,
const gchar * name)
{
g_return_val_if_fail (GST_IS_VULKAN_INSTANCE (instance), NULL);
g_return_val_if_fail (instance->instance != NULL, NULL);
g_return_val_if_fail (name != NULL, NULL);
GST_TRACE_OBJECT (instance, "%s", name);
return vkGetInstanceProcAddr (instance->instance, name);
}
void
gst_vulkan_instance_close (GstVulkanInstance * instance)
{
g_return_if_fail (GST_IS_VULKAN_INSTANCE (instance));
g_return_if_fail (instance->instance != NULL);
if (instance->dbgDestroyMsgCallback)
instance->dbgDestroyMsgCallback (instance->instance,
instance->msg_callback);
g_free (instance->physical_devices);
}

65
ext/vulkan/vkinstance.h Normal file
View file

@ -0,0 +1,65 @@
/*
* GStreamer
* Copyright (C) 2015 Matthew Waters <matthew@centricular.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef _VK_INSTANCE_H_
#define _VK_INSTANCE_H_
#include "vk.h"
G_BEGIN_DECLS
#define GST_TYPE_VULKAN_INSTANCE (gst_vulkan_instance_get_type())
#define GST_VULKAN_INSTANCE(o) (G_TYPE_CHECK_INSTANCE_CAST((o), GST_TYPE_VULKAN_INSTANCE, GstVulkanInstance))
#define GST_VULKAN_INSTANCE_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), GST_TYPE_VULKAN_INSTANCE, GstVulkanInstanceClass))
#define GST_IS_VULKAN_INSTANCE(o) (G_TYPE_CHECK_INSTANCE_TYPE((o), GST_TYPE_VULKAN_INSTANCE))
#define GST_IS_VULKAN_INSTANCE_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE((k), GST_TYPE_VULKAN_INSTANCE))
#define GST_VULKAN_INSTANCE_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS((o), GST_TYPE_VULKAN_INSTANCE, GstVulkanInstanceClass))
GType gst_vulkan_instance_get_type (void);
struct _GstVulkanInstance
{
GstObject parent;
VkInstance instance; /* hides a pointer */
VkPhysicalDevice *physical_devices; /* hides a pointer */
guint32 n_physical_devices;
VkDbgMsgCallback msg_callback;
PFN_vkDbgCreateMsgCallback dbgCreateMsgCallback;
PFN_vkDbgDestroyMsgCallback dbgDestroyMsgCallback;
PFN_vkDbgMsgCallback dbgBreakCallback;
};
struct _GstVulkanInstanceClass
{
GstObjectClass parent_class;
};
GstVulkanInstance * gst_vulkan_instance_new (void);
gboolean gst_vulkan_instance_open (GstVulkanInstance * instance,
GError ** error);
void gst_vulkan_instance_close (GstVulkanInstance * instance);
gpointer gst_vulkan_instance_get_proc_address (GstVulkanInstance * instance,
const gchar * name);
G_END_DECLS
#endif /* _VK_INSTANCE_H_ */

264
ext/vulkan/vkmemory.c Normal file
View file

@ -0,0 +1,264 @@
/*
* GStreamer
* Copyright (C) 2015 Matthew Waters <matthew@centricular.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <string.h>
#include "vkmemory.h"
/**
* SECTION:vkmemory
* @short_description: memory subclass for Vulkan device memory
* @see_also: #GstMemory, #GstAllocator
*
* GstVulkanMemory is a #GstMemory subclass providing support for the mapping of
* Vulkan device memory.
*/
#define GST_CAT_DEFUALT GST_CAT_VULKAN_MEMORY
GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFUALT);
static GstAllocator *_vulkan_memory_allocator;
static void
_vk_mem_init (GstVulkanMemory * mem, GstAllocator * allocator,
GstMemory * parent, GstVulkanDevice * device, guint32 memory_type_index,
GstAllocationParams * params, gsize size,
VkMemoryPropertyFlags mem_prop_flags, gpointer user_data,
GDestroyNotify notify)
{
gsize align = gst_memory_alignment, offset = 0, maxsize = size;
GstMemoryFlags flags = 0;
if (params) {
flags = params->flags;
align |= params->align;
offset = params->prefix;
maxsize += params->prefix + params->padding;
if ((maxsize & align) != 0)
maxsize += ~(maxsize & align) + 1;
}
gst_memory_init (GST_MEMORY_CAST (mem), flags, allocator, parent, maxsize,
align, offset, size);
mem->device = gst_object_ref (device);
mem->alloc_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOC_INFO;
mem->alloc_info.pNext = NULL;
mem->alloc_info.allocationSize = (VkDeviceSize) mem->mem.maxsize;
mem->alloc_info.memoryTypeIndex = memory_type_index;
mem->properties = mem_prop_flags;
mem->notify = notify;
mem->user_data = user_data;
g_mutex_init (&mem->lock);
GST_CAT_DEBUG (GST_CAT_VULKAN_MEMORY, "new GL buffer memory:%p size:%"
G_GSIZE_FORMAT, mem, maxsize);
}
static GstVulkanMemory *
_vk_mem_new (GstAllocator * allocator, GstMemory * parent,
GstVulkanDevice * device, guint32 memory_type_index,
GstAllocationParams * params, gsize size,
VkMemoryPropertyFlags mem_props_flags, gpointer user_data,
GDestroyNotify notify)
{
GstVulkanMemory *mem = g_slice_new0 (GstVulkanMemory);
GError *error = NULL;
VkResult err;
_vk_mem_init (mem, allocator, parent, device, memory_type_index, params,
size, mem_props_flags, user_data, notify);
err = vkAllocMemory (device->device, &mem->alloc_info, &mem->mem_ptr);
if (gst_vulkan_error_to_g_error (err, &error, "vkAllocMemory") < 0) {
GST_CAT_ERROR (GST_CAT_VULKAN_MEMORY, "Failed to allocate device memory %s",
error->message);
gst_memory_unref ((GstMemory *) mem);
g_clear_error (&error);
return NULL;
}
return mem;
}
static gpointer
_vk_mem_map_full (GstVulkanMemory * mem, GstMapInfo * info, gsize size)
{
gpointer data;
VkResult err;
GError *error = NULL;
if ((mem->properties & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) == 0) {
GST_CAT_ERROR (GST_CAT_VULKAN_MEMORY, "Cannot map host-invisible memory");
return NULL;
}
err = vkMapMemory (mem->device->device, mem->mem_ptr, 0, size, 0, &data);
if (gst_vulkan_error_to_g_error (err, &error, "vkMapMemory") < 0) {
GST_CAT_ERROR (GST_CAT_VULKAN_MEMORY, "Failed to map device memory %s",
error->message);
g_clear_error (&error);
return NULL;
}
return data;
}
static void
_vk_mem_unmap_full (GstVulkanMemory * mem, GstMapInfo * info)
{
vkUnmapMemory (mem->device->device, mem->mem_ptr);
}
static GstMemory *
_vk_mem_copy (GstVulkanMemory * src, gssize offset, gssize size)
{
return NULL;
}
static GstMemory *
_vk_mem_share (GstVulkanMemory * mem, gssize offset, gssize size)
{
return NULL;
}
static gboolean
_vk_mem_is_span (GstVulkanMemory * mem1, GstVulkanMemory * mem2, gsize * offset)
{
return FALSE;
}
static GstMemory *
_vk_mem_alloc (GstAllocator * allocator, gsize size,
GstAllocationParams * params)
{
g_critical ("Subclass should override GstAllocatorClass::alloc() function");
return NULL;
}
static void
_vk_mem_free (GstAllocator * allocator, GstMemory * memory)
{
GstVulkanMemory *mem = (GstVulkanMemory *) memory;
GST_CAT_TRACE (GST_CAT_VULKAN_MEMORY, "freeing buffer memory:%p "
"id:%" G_GUINT64_FORMAT, mem, mem->mem_ptr.handle);
g_mutex_clear (&mem->lock);
if (mem->notify)
mem->notify (mem->user_data);
vkFreeMemory (mem->device->device, mem->mem_ptr);
gst_object_unref (mem->device);
}
/**
* gst_vulkan_memory_alloc:
* @device:a #GstVulkanDevice
* @memory_type_index: the Vulkan memory type index
* @params: a #GstAllocationParams
* @size: the size to allocate
*
* Allocated a new #GstVulkanMemory.
*
* Returns: a #GstMemory object backed by a vulkan device memory
*/
GstMemory *
gst_vulkan_memory_alloc (GstVulkanDevice * device, guint32 memory_type_index,
GstAllocationParams * params, gsize size, VkMemoryPropertyFlags mem_flags)
{
GstVulkanMemory *mem;
mem = _vk_mem_new (_vulkan_memory_allocator, NULL, device, memory_type_index,
params, size, mem_flags, NULL, NULL);
return (GstMemory *) mem;
}
G_DEFINE_TYPE (GstVulkanMemoryAllocator, gst_vulkan_memory_allocator,
GST_TYPE_ALLOCATOR);
static void
gst_vulkan_memory_allocator_class_init (GstVulkanMemoryAllocatorClass * klass)
{
GstAllocatorClass *allocator_class = (GstAllocatorClass *) klass;
allocator_class->alloc = _vk_mem_alloc;
allocator_class->free = _vk_mem_free;
}
static void
gst_vulkan_memory_allocator_init (GstVulkanMemoryAllocator * allocator)
{
GstAllocator *alloc = GST_ALLOCATOR_CAST (allocator);
alloc->mem_type = GST_VULKAN_MEMORY_ALLOCATOR_NAME;
alloc->mem_map_full = (GstMemoryMapFullFunction) _vk_mem_map_full;
alloc->mem_unmap_full = (GstMemoryUnmapFullFunction) _vk_mem_unmap_full;
alloc->mem_copy = (GstMemoryCopyFunction) _vk_mem_copy;
alloc->mem_share = (GstMemoryShareFunction) _vk_mem_share;
alloc->mem_is_span = (GstMemoryIsSpanFunction) _vk_mem_is_span;
}
/**
* gst_vulkan_memory_init_once:
*
* Initializes the Vulkan memory allocator. It is safe to call this function
* multiple times. This must be called before any other #GstVulkanMemory operation.
*/
void
gst_vulkan_memory_init_once (void)
{
static volatile gsize _init = 0;
if (g_once_init_enter (&_init)) {
GST_DEBUG_CATEGORY_INIT (GST_CAT_VULKAN_MEMORY, "vulkanmemory", 0,
"Vulkan Memory");
_vulkan_memory_allocator =
g_object_new (gst_vulkan_memory_allocator_get_type (), NULL);
gst_allocator_register (GST_VULKAN_MEMORY_ALLOCATOR_NAME,
gst_object_ref (_vulkan_memory_allocator));
g_once_init_leave (&_init, 1);
}
}
/**
* gst_is_vulkan_memory:
* @mem:a #GstMemory
*
* Returns: whether the memory at @mem is a #GstVulkanMemory
*/
gboolean
gst_is_vulkan_memory (GstMemory * mem)
{
return mem != NULL && mem->allocator != NULL &&
g_type_is_a (G_OBJECT_TYPE (mem->allocator),
GST_TYPE_VULKAN_MEMORY_ALLOCATOR);
}

94
ext/vulkan/vkmemory.h Normal file
View file

@ -0,0 +1,94 @@
/*
* GStreamer
* Copyright (C) 2015 Matthew Waters <matthew@centricular.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef _GST_VULKAN_BASE_BUFFER_H_
#define _GST_VULKAN_BASE_BUFFER_H_
#include <gst/gst.h>
#include <gst/gstallocator.h>
#include <gst/gstmemory.h>
#include <vk.h>
G_BEGIN_DECLS
#define GST_TYPE_VULKAN_MEMORY_ALLOCATOR (gst_vulkan_memory_allocator_get_type())
GType gst_vulkan_memory_allocator_get_type(void);
#define GST_IS_VULKAN_MEMORY_ALLOCATOR(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_VULKAN_MEMORY_ALLOCATOR))
#define GST_IS_VULKAN_MEMORY_ALLOCATOR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_VULKAN_MEMORY_ALLOCATOR))
#define GST_VULKAN_MEMORY_ALLOCATOR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_VULKAN_MEMORY_ALLOCATOR, GstVulkanMemoryAllocatorClass))
#define GST_VULKAN_MEMORY_ALLOCATOR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_VULKAN_MEMORY_ALLOCATOR, GstVulkanMemoryAllocator))
#define GST_VULKAN_MEMORY_ALLOCATOR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_VULKAN_MEMORY_ALLOCATOR, GstVulkanMemoryAllocatorClass))
#define GST_VULKAN_MEMORY_ALLOCATOR_CAST(obj) ((GstVulkanMemoryAllocator *)(obj))
#define GST_VULKAN_MEMORY_ALLOCATOR_NAME "Vulkan"
struct _GstVulkanMemory
{
GstMemory mem;
GstVulkanDevice *device;
VkDeviceMemory mem_ptr;
/* <protected> */
GMutex lock;
/* <private> */
GDestroyNotify notify;
gpointer user_data;
VkMemoryAllocInfo alloc_info;
VkMemoryPropertyFlags properties;
};
/**
* GstVulkanMemoryAllocator
*
* Opaque #GstVulkanMemoryAllocator struct
*/
struct _GstVulkanMemoryAllocator
{
GstAllocator parent;
};
/**
* GstVulkanMemoryAllocatorClass:
*
* The #GstVulkanMemoryAllocatorClass only contains private data
*/
struct _GstVulkanMemoryAllocatorClass
{
GstAllocatorClass parent_class;
};
void gst_vulkan_memory_init_once (void);
gboolean gst_is_vulkan_memory (GstMemory * mem);
GstMemory * gst_vulkan_memory_alloc (GstVulkanDevice * device,
guint32 memory_type_index,
GstAllocationParams * params,
gsize size,
VkMemoryPropertyFlags mem_prop_flags);
G_END_DECLS
#endif /* _GST_VULKAN_BASE_BUFFER_H_ */

65
ext/vulkan/vkqueue.c Normal file
View file

@ -0,0 +1,65 @@
/*
* GStreamer
* Copyright (C) 2015 Matthew Waters <matthew@centricular.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "vkqueue.h"
#define GST_CAT_DEFAULT gst_vulkan_queue_debug
GST_DEBUG_CATEGORY (GST_CAT_DEFAULT);
G_DEFINE_TYPE_WITH_CODE (GstVulkanQueue, gst_vulkan_queue, GST_TYPE_OBJECT,
GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "vulkanqueue", 0,
"Vulkan Queue"));
static void gst_vulkan_queue_dispose (GObject * object);
static void
gst_vulkan_queue_init (GstVulkanQueue * device)
{
}
static void
gst_vulkan_queue_class_init (GstVulkanQueueClass * device_class)
{
GObjectClass *gobject_class = (GObjectClass *) device_class;
gobject_class->dispose = gst_vulkan_queue_dispose;
}
static void
gst_vulkan_queue_dispose (GObject * object)
{
GstVulkanQueue *queue = GST_VULKAN_QUEUE (object);
if (queue->device)
gst_object_unref (queue->device);
queue->device = NULL;
}
GstVulkanDevice *
gst_vulkan_queue_get_device (GstVulkanQueue * queue)
{
g_return_val_if_fail (GST_IS_VULKAN_QUEUE (queue), NULL);
return queue->device ? gst_object_ref (queue->device) : NULL;
}

52
ext/vulkan/vkqueue.h Normal file
View file

@ -0,0 +1,52 @@
/*
* GStreamer
* Copyright (C) 2015 Matthew Waters <matthew@centricular.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef _VK_QUEUE_H_
#define _VK_QUEUE_H_
#include "vk.h"
#define GST_TYPE_VULKAN_QUEUE (gst_vulkan_queue_get_type())
#define GST_VULKAN_QUEUE(o) (G_TYPE_CHECK_INSTANCE_CAST((o), GST_TYPE_VULKAN_QUEUE, GstVulkanQueue))
#define GST_VULKAN_QUEUE_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), GST_TYPE_VULKAN_QUEUE, GstVulkanQueueClass))
#define GST_IS_VULKAN_QUEUE(o) (G_TYPE_CHECK_INSTANCE_TYPE((o), GST_TYPE_VULKAN_QUEUE))
#define GST_IS_VULKAN_QUEUE_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE((k), GST_TYPE_VULKAN_QUEUE))
#define GST_VULKAN_QUEUE_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS((o), GST_TYPE_VULKAN_QUEUE, GstVulkanQueueClass))
GType gst_vulkan_queue_get_type (void);
struct _GstVulkanQueue
{
GstObject parent;
GstVulkanDevice *device;
VkQueue queue; /* hides a pointer */
guint32 family;
guint32 index;
};
struct _GstVulkanQueueClass
{
GstObjectClass parent_class;
};
GstVulkanDevice * gst_vulkan_queue_get_device (GstVulkanQueue * queue);
#endif /* _VK_QUEUE_H_ */

491
ext/vulkan/vksink.c Normal file
View file

@ -0,0 +1,491 @@
/*
* GStreamer
* Copyright (C) 2015 Matthew Waters <matthew@centricular.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
/**
* SECTION:element-vulkansink
*
* vulkansink renders video frames to a drawable on a local or remote
* display using Vulkan.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
//#include <gst/video/videooverlay.h>
#include "vksink.h"
#include "vkdevice.h"
GST_DEBUG_CATEGORY (gst_debug_vulkan_sink);
#define GST_CAT_DEFAULT gst_debug_vulkan_sink
#define DEFAULT_FORCE_ASPECT_RATIO TRUE
#define DEFAULT_PIXEL_ASPECT_RATIO_N 0
#define DEFAULT_PIXEL_ASPECT_RATIO_D 1
static void gst_vulkan_sink_finalize (GObject * object);
static void gst_vulkan_sink_set_property (GObject * object, guint prop_id,
const GValue * value, GParamSpec * param_spec);
static void gst_vulkan_sink_get_property (GObject * object, guint prop_id,
GValue * value, GParamSpec * param_spec);
static gboolean gst_vulkan_sink_query (GstBaseSink * bsink, GstQuery * query);
static void gst_vulkan_sink_set_context (GstElement * element,
GstContext * context);
static GstStateChangeReturn
gst_vulkan_sink_change_state (GstElement * element, GstStateChange transition);
static void gst_vulkan_sink_get_times (GstBaseSink * bsink, GstBuffer * buf,
GstClockTime * start, GstClockTime * end);
static gboolean gst_vulkan_sink_set_caps (GstBaseSink * bsink, GstCaps * caps);
static GstCaps *gst_vulkan_sink_get_caps (GstBaseSink * bsink,
GstCaps * filter);
static GstFlowReturn gst_vulkan_sink_prepare (GstBaseSink * bsink,
GstBuffer * buf);
static GstFlowReturn gst_vulkan_sink_show_frame (GstVideoSink * bsink,
GstBuffer * buf);
static GstStaticPadTemplate gst_vulkan_sink_template =
GST_STATIC_PAD_TEMPLATE ("sink",
GST_PAD_SINK,
GST_PAD_ALWAYS,
GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE ("RGBA")));
enum
{
PROP_0,
PROP_FORCE_ASPECT_RATIO,
PROP_PIXEL_ASPECT_RATIO,
};
enum
{
SIGNAL_0,
LAST_SIGNAL
};
/* static guint gst_vulkan_sink_signals[LAST_SIGNAL] = { 0 }; */
#define gst_vulkan_sink_parent_class parent_class
G_DEFINE_TYPE_WITH_CODE (GstVulkanSink, gst_vulkan_sink,
GST_TYPE_VIDEO_SINK, GST_DEBUG_CATEGORY_INIT (gst_debug_vulkan_sink,
"vulkansink", 0, "Vulkan Video Sink"));
static void
gst_vulkan_sink_class_init (GstVulkanSinkClass * klass)
{
GObjectClass *gobject_class;
GstElementClass *gstelement_class;
GstBaseSinkClass *gstbasesink_class;
GstVideoSinkClass *gstvideosink_class;
GstElementClass *element_class;
gobject_class = (GObjectClass *) klass;
gstelement_class = (GstElementClass *) klass;
gstbasesink_class = (GstBaseSinkClass *) klass;
gstvideosink_class = (GstVideoSinkClass *) klass;
element_class = GST_ELEMENT_CLASS (klass);
gobject_class->set_property = gst_vulkan_sink_set_property;
gobject_class->get_property = gst_vulkan_sink_get_property;
g_object_class_install_property (gobject_class, PROP_FORCE_ASPECT_RATIO,
g_param_spec_boolean ("force-aspect-ratio", "Force aspect ratio",
"When enabled, scaling will respect original aspect ratio",
DEFAULT_FORCE_ASPECT_RATIO,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
g_object_class_install_property (gobject_class, PROP_PIXEL_ASPECT_RATIO,
gst_param_spec_fraction ("pixel-aspect-ratio", "Pixel Aspect Ratio",
"The pixel aspect ratio of the device", 0, 1, G_MAXINT, 1, 1, 1,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
gst_element_class_set_metadata (element_class, "Vulkan video sink",
"Sink/Video", "A videosink based on OpenGL",
"Matthew Waters <matthew@centricular.com>");
gst_element_class_add_pad_template (element_class,
gst_static_pad_template_get (&gst_vulkan_sink_template));
gobject_class->finalize = gst_vulkan_sink_finalize;
gstelement_class->change_state = gst_vulkan_sink_change_state;
gstelement_class->set_context = gst_vulkan_sink_set_context;
gstbasesink_class->query = GST_DEBUG_FUNCPTR (gst_vulkan_sink_query);
gstbasesink_class->set_caps = gst_vulkan_sink_set_caps;
gstbasesink_class->get_caps = gst_vulkan_sink_get_caps;
gstbasesink_class->get_times = gst_vulkan_sink_get_times;
gstbasesink_class->prepare = gst_vulkan_sink_prepare;
gstvideosink_class->show_frame =
GST_DEBUG_FUNCPTR (gst_vulkan_sink_show_frame);
}
static void
gst_vulkan_sink_init (GstVulkanSink * vk_sink)
{
vk_sink->force_aspect_ratio = DEFAULT_FORCE_ASPECT_RATIO;
vk_sink->par_n = DEFAULT_PIXEL_ASPECT_RATIO_N;
vk_sink->par_d = DEFAULT_PIXEL_ASPECT_RATIO_D;
// g_mutex_init (&vk_sink->drawing_lock);
}
static void
gst_vulkan_sink_finalize (GObject * object)
{
// GstVulkanSink *vk_sink = GST_VULKAN_SINK (object);
// g_mutex_clear (&vk_sink->drawing_lock);
// GST_DEBUG ("finalized");
G_OBJECT_CLASS (parent_class)->finalize (object);
}
static void
gst_vulkan_sink_set_property (GObject * object, guint prop_id,
const GValue * value, GParamSpec * pspec)
{
GstVulkanSink *vk_sink = GST_VULKAN_SINK (object);
switch (prop_id) {
case PROP_FORCE_ASPECT_RATIO:
vk_sink->force_aspect_ratio = g_value_get_boolean (value);
break;
case PROP_PIXEL_ASPECT_RATIO:
vk_sink->par_n = gst_value_get_fraction_numerator (value);
vk_sink->par_d = gst_value_get_fraction_denominator (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
gst_vulkan_sink_get_property (GObject * object, guint prop_id,
GValue * value, GParamSpec * pspec)
{
GstVulkanSink *vk_sink = GST_VULKAN_SINK (object);
switch (prop_id) {
case PROP_FORCE_ASPECT_RATIO:
g_value_set_boolean (value, vk_sink->force_aspect_ratio);
break;
case PROP_PIXEL_ASPECT_RATIO:
gst_value_set_fraction (value, vk_sink->par_n, vk_sink->par_d);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static gboolean
gst_vulkan_sink_query (GstBaseSink * bsink, GstQuery * query)
{
// GstVulkanSink *vk_sink = GST_VULKAN_SINK (bsink);
gboolean res = FALSE;
switch (GST_QUERY_TYPE (query)) {
default:
res = GST_BASE_SINK_CLASS (parent_class)->query (bsink, query);
break;
}
return res;
}
static void
gst_vulkan_sink_set_context (GstElement * element, GstContext * context)
{
// GstVulkanSink *vk_sink = GST_VULKAN_SINK (element);
GST_ELEMENT_CLASS (parent_class)->set_context (element, context);
}
static GstStateChangeReturn
gst_vulkan_sink_change_state (GstElement * element, GstStateChange transition)
{
GstVulkanSink *vk_sink = GST_VULKAN_SINK (element);
GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
GError *error = NULL;
GST_DEBUG ("changing state: %s => %s",
gst_element_state_get_name (GST_STATE_TRANSITION_CURRENT (transition)),
gst_element_state_get_name (GST_STATE_TRANSITION_NEXT (transition)));
switch (transition) {
case GST_STATE_CHANGE_NULL_TO_READY:
vk_sink->instance = gst_vulkan_instance_new ();
if (!gst_vulkan_instance_open (vk_sink->instance, &error)) {
GST_ELEMENT_ERROR (vk_sink, RESOURCE, NOT_FOUND,
("Failed to create vulkan instance"), ("%s", error->message));
return GST_STATE_CHANGE_FAILURE;
}
vk_sink->device = gst_vulkan_device_new (vk_sink->instance);
if (!gst_vulkan_device_open (vk_sink->device, &error)) {
GST_ELEMENT_ERROR (vk_sink, RESOURCE, NOT_FOUND,
("Failed to create vulkan device"), ("%s", error->message));
return GST_STATE_CHANGE_FAILURE;
}
if (!(vk_sink->display = gst_vulkan_display_new ())) {
GST_ELEMENT_ERROR (vk_sink, RESOURCE, NOT_FOUND,
("Failed to connect to the window sysytem"), (NULL));
return GST_STATE_CHANGE_FAILURE;
}
if (!(vk_sink->window =
gst_vulkan_display_create_window (vk_sink->display))) {
GST_ELEMENT_ERROR (vk_sink, RESOURCE, NOT_FOUND,
("Failed to create a window"), (NULL));
return GST_STATE_CHANGE_FAILURE;
}
if (!gst_vulkan_window_open (vk_sink->window, &error)) {
GST_ELEMENT_ERROR (vk_sink, RESOURCE, NOT_FOUND,
("Failed to open window"), ("%s", error->message));
return GST_STATE_CHANGE_FAILURE;
}
if (!(vk_sink->swapper =
gst_vulkan_swapper_new (vk_sink->device, vk_sink->window))) {
GST_ELEMENT_ERROR (vk_sink, RESOURCE, NOT_FOUND,
("Failed to create a swapper"), (NULL));
return GST_STATE_CHANGE_FAILURE;
}
break;
case GST_STATE_CHANGE_READY_TO_PAUSED:
break;
case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
break;
default:
break;
}
ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
if (ret == GST_STATE_CHANGE_FAILURE)
return ret;
switch (transition) {
case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
break;
case GST_STATE_CHANGE_PAUSED_TO_READY:
break;
case GST_STATE_CHANGE_READY_TO_NULL:
gst_object_unref (vk_sink->swapper);
vk_sink->swapper = NULL;
gst_object_unref (vk_sink->display);
vk_sink->display = NULL;
gst_vulkan_window_close (vk_sink->window);
gst_object_unref (vk_sink->window);
vk_sink->window = NULL;
gst_vulkan_device_close (vk_sink->device);
gst_object_unref (vk_sink->device);
vk_sink->device = NULL;
gst_vulkan_instance_close (vk_sink->instance);
gst_object_unref (vk_sink->instance);
vk_sink->instance = NULL;
break;
default:
break;
}
return ret;
}
static void
gst_vulkan_sink_get_times (GstBaseSink * bsink, GstBuffer * buf,
GstClockTime * start, GstClockTime * end)
{
GstVulkanSink *vk_sink = GST_VULKAN_SINK (bsink);
if (GST_BUFFER_TIMESTAMP_IS_VALID (buf)) {
*start = GST_BUFFER_TIMESTAMP (buf);
if (GST_BUFFER_DURATION_IS_VALID (buf))
*end = *start + GST_BUFFER_DURATION (buf);
else {
if (GST_VIDEO_INFO_FPS_N (&vk_sink->v_info) > 0) {
*end = *start +
gst_util_uint64_scale_int (GST_SECOND,
GST_VIDEO_INFO_FPS_D (&vk_sink->v_info),
GST_VIDEO_INFO_FPS_N (&vk_sink->v_info));
}
}
}
}
static GstCaps *
gst_vulkan_sink_get_caps (GstBaseSink * bsink, GstCaps * filter)
{
GstVulkanSink *vk_sink = GST_VULKAN_SINK (bsink);
GstCaps *tmp = NULL;
GstCaps *result = NULL;
GError *error = NULL;
if (vk_sink->swapper) {
if (!(result =
gst_vulkan_swapper_get_supported_caps (vk_sink->swapper, &error))) {
GST_ELEMENT_ERROR (bsink, RESOURCE, NOT_FOUND, ("%s", error->message),
(NULL));
g_clear_error (&error);
return NULL;
}
return result;
}
tmp = gst_pad_get_pad_template_caps (GST_BASE_SINK_PAD (bsink));
if (filter) {
GST_DEBUG_OBJECT (bsink, "intersecting with filter caps %" GST_PTR_FORMAT,
filter);
result = gst_caps_intersect_full (filter, tmp, GST_CAPS_INTERSECT_FIRST);
gst_caps_unref (tmp);
} else {
result = tmp;
}
GST_DEBUG_OBJECT (bsink, "returning caps: %" GST_PTR_FORMAT, result);
return result;
}
static gboolean
_configure_display_from_info (GstVulkanSink * vk_sink, GstVideoInfo * vinfo)
{
guint display_ratio_num, display_ratio_den;
gint display_par_n, display_par_d;
gint par_n, par_d;
gint width, height;
gboolean ok;
width = GST_VIDEO_INFO_WIDTH (vinfo);
height = GST_VIDEO_INFO_HEIGHT (vinfo);
par_n = GST_VIDEO_INFO_PAR_N (vinfo);
par_d = GST_VIDEO_INFO_PAR_D (vinfo);
if (!par_n)
par_n = 1;
/* get display's PAR */
if (vk_sink->par_n != 0 && vk_sink->par_d != 0) {
display_par_n = vk_sink->par_n;
display_par_d = vk_sink->par_d;
} else {
display_par_n = 1;
display_par_d = 1;
}
ok = gst_video_calculate_display_ratio (&display_ratio_num,
&display_ratio_den, width, height, par_n, par_d, display_par_n,
display_par_d);
if (!ok)
return FALSE;
GST_TRACE ("PAR: %u/%u DAR:%u/%u", par_n, par_d, display_par_n,
display_par_d);
if (height % display_ratio_den == 0) {
GST_DEBUG ("keeping video height");
GST_VIDEO_SINK_WIDTH (vk_sink) = (guint)
gst_util_uint64_scale_int (height, display_ratio_num,
display_ratio_den);
GST_VIDEO_SINK_HEIGHT (vk_sink) = height;
} else if (width % display_ratio_num == 0) {
GST_DEBUG ("keeping video width");
GST_VIDEO_SINK_WIDTH (vk_sink) = width;
GST_VIDEO_SINK_HEIGHT (vk_sink) = (guint)
gst_util_uint64_scale_int (width, display_ratio_den, display_ratio_num);
} else {
GST_DEBUG ("approximating while keeping video height");
GST_VIDEO_SINK_WIDTH (vk_sink) = (guint)
gst_util_uint64_scale_int (height, display_ratio_num,
display_ratio_den);
GST_VIDEO_SINK_HEIGHT (vk_sink) = height;
}
GST_DEBUG ("scaling to %dx%d", GST_VIDEO_SINK_WIDTH (vk_sink),
GST_VIDEO_SINK_HEIGHT (vk_sink));
return TRUE;
}
static gboolean
gst_vulkan_sink_set_caps (GstBaseSink * bsink, GstCaps * caps)
{
GstVulkanSink *vk_sink = GST_VULKAN_SINK (bsink);
GError *error = NULL;
GstVideoInfo v_info;
GST_DEBUG_OBJECT (bsink, "set caps with %" GST_PTR_FORMAT, caps);
if (!gst_video_info_from_caps (&v_info, caps))
return FALSE;
if (!_configure_display_from_info (vk_sink, &v_info))
return FALSE;
if (!gst_vulkan_swapper_set_caps (vk_sink->swapper, caps, &error)) {
GST_ELEMENT_ERROR (vk_sink, RESOURCE, NOT_FOUND,
("Failed to configure caps"), ("%s", error->message));
g_clear_error (&error);
return FALSE;
}
vk_sink->v_info = v_info;
return TRUE;
}
static GstFlowReturn
gst_vulkan_sink_prepare (GstBaseSink * bsink, GstBuffer * buf)
{
GstVulkanSink *vk_sink = GST_VULKAN_SINK (bsink);
GST_TRACE_OBJECT (vk_sink, "preparing buffer %" GST_PTR_FORMAT, buf);
if (GST_VIDEO_SINK_WIDTH (vk_sink) < 1 || GST_VIDEO_SINK_HEIGHT (vk_sink) < 1) {
return GST_FLOW_NOT_NEGOTIATED;
}
return GST_FLOW_OK;
}
static GstFlowReturn
gst_vulkan_sink_show_frame (GstVideoSink * vsink, GstBuffer * buf)
{
GstVulkanSink *vk_sink = GST_VULKAN_SINK (vsink);
GError *error = NULL;
GST_TRACE_OBJECT (vk_sink, "rendering buffer %" GST_PTR_FORMAT, buf);
if (!gst_vulkan_swapper_render_buffer (vk_sink->swapper, buf, &error)) {
GST_ELEMENT_ERROR (vk_sink, RESOURCE, NOT_FOUND,
("Failed to render buffer"), ("%s", error->message));
g_clear_error (&error);
return GST_FLOW_ERROR;
}
return GST_FLOW_OK;
}

73
ext/vulkan/vksink.h Normal file
View file

@ -0,0 +1,73 @@
/*
* GStreamer
* Copyright (C) 2015 Matthew Waters <matthew@centricular.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef _VK_SINK_H_
#define _VK_SINK_H_
#include <gst/gst.h>
#include <gst/video/gstvideosink.h>
#include <gst/video/video.h>
#include "vk.h"
G_BEGIN_DECLS
#define GST_TYPE_VULKAN_SINK (gst_vulkan_sink_get_type())
#define GST_VULKAN_SINK(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_VULKAN_SINK,GstVulkanSink))
#define GST_VULKAN_SINK_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_VULKAN_SINK,GstVulkanSinkClass))
#define GST_IS_VULKAN_SINK(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_VULKAN_SINK))
#define GST_IS_VULKAN_SINK_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_VULKAN_SINK))
typedef struct _GstVulkanSink GstVulkanSink;
typedef struct _GstVulkanSinkClass GstVulkanSinkClass;
struct _GstVulkanSink
{
GstVideoSink video_sink;
GstVulkanInstance *instance;
GstVulkanDevice *device;
GstVulkanDisplay *display;
GstVulkanWindow *window;
GstVulkanSwapper *swapper;
/* properties */
gboolean force_aspect_ratio;
gint par_n;
gint par_d;
/* stream configuration */
GstVideoInfo v_info;
/* runtime variables */
gint to_quit;
};
struct _GstVulkanSinkClass
{
GstVideoSinkClass video_sink_class;
};
GType gst_vulkan_sink_get_type(void);
G_END_DECLS
#endif

957
ext/vulkan/vkswapper.c Normal file
View file

@ -0,0 +1,957 @@
/*
* GStreamer
* Copyright (C) 2015 Matthew Waters <matthew@centricular.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <string.h>
#include "vkswapper.h"
#define GST_CAT_DEFAULT gst_vulkan_swapper_debug
GST_DEBUG_CATEGORY (GST_CAT_DEFAULT);
#define gst_vulkan_swapper_parent_class parent_class
G_DEFINE_TYPE_WITH_CODE (GstVulkanSwapper, gst_vulkan_swapper,
GST_TYPE_OBJECT, GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT,
"vulkanswapper", 0, "Vulkan Swapper"));
static gboolean
_get_function_table (GstVulkanSwapper * swapper)
{
GstVulkanDevice *device = swapper->device;
GstVulkanInstance *instance = gst_vulkan_device_get_instance (device);
#define GET_PROC_ADDRESS_REQUIRED(obj, type, name) \
G_STMT_START { \
obj->G_PASTE (, name) = G_PASTE(G_PASTE(gst_vulkan_, type), _get_proc_address) (type, "vk" G_STRINGIFY(name)); \
if (!obj->G_PASTE(, name)) { \
GST_ERROR_OBJECT (obj, "Failed to find required function vk" G_STRINGIFY(name)); \
return FALSE; \
} \
} G_STMT_END
GET_PROC_ADDRESS_REQUIRED (swapper, instance,
GetPhysicalDeviceSurfaceSupportKHR);
GET_PROC_ADDRESS_REQUIRED (swapper, device, GetSurfacePropertiesKHR);
GET_PROC_ADDRESS_REQUIRED (swapper, device, GetSurfaceFormatsKHR);
GET_PROC_ADDRESS_REQUIRED (swapper, device, GetSurfacePresentModesKHR);
GET_PROC_ADDRESS_REQUIRED (swapper, device, CreateSwapchainKHR);
GET_PROC_ADDRESS_REQUIRED (swapper, device, DestroySwapchainKHR);
GET_PROC_ADDRESS_REQUIRED (swapper, device, GetSwapchainImagesKHR);
GET_PROC_ADDRESS_REQUIRED (swapper, device, AcquireNextImageKHR);
GET_PROC_ADDRESS_REQUIRED (swapper, device, QueuePresentKHR);
return TRUE;
#undef GET_PROC_ADDRESS_REQUIRED
}
static VkPlatformKHR
_gst_display_type_to_vk_platform (GstVulkanDisplayType dtype)
{
VkPlatformKHR ret = -1;
if (dtype == GST_VULKAN_DISPLAY_TYPE_ANY)
return -1;
if (dtype == GST_VULKAN_DISPLAY_TYPE_NONE)
return -1;
#if GST_VULKAN_HAVE_WINDOW_X11
if ((dtype & GST_VULKAN_DISPLAY_TYPE_X11) == dtype)
ret = VK_PLATFORM_X11_KHR;
#endif
#if GST_VULKAN_HAVE_WINDOW_XCB
if ((dtype & GST_VULKAN_DISPLAY_TYPE_XCB) == dtype)
ret = VK_PLATFORM_XCB_KHR;
#endif
return ret;
}
static gboolean
_get_window_surface_description (GstVulkanSwapper * swapper,
VkSurfaceDescriptionWindowKHR * desc)
{
GstVulkanDisplay *display = gst_vulkan_window_get_display (swapper->window);
GstVulkanDisplayType dtype = gst_vulkan_display_get_handle_type (display);
g_return_val_if_fail (desc != NULL, FALSE);
desc->sType = VK_STRUCTURE_TYPE_SURFACE_DESCRIPTION_WINDOW_KHR;
desc->pNext = NULL;
desc->platform = _gst_display_type_to_vk_platform (dtype);
if (desc->platform == -1) {
GST_ERROR_OBJECT (swapper, "Failed to retrieve platform from display");
return FALSE;
}
desc->pPlatformHandle = gst_vulkan_display_get_platform_handle (display);
desc->pPlatformWindow =
gst_vulkan_window_get_platform_handle (swapper->window);
gst_object_unref (display);
/* XXX: might be a little too strict */
return desc->pPlatformHandle != NULL && desc->pPlatformWindow != NULL;
}
static GstVideoFormat
_vk_format_to_video_format (VkFormat format)
{
switch (format) {
/* double check endianess */
case VK_FORMAT_R8G8B8A8_UNORM:
return GST_VIDEO_FORMAT_RGBA;
case VK_FORMAT_R8G8B8_UNORM:
return GST_VIDEO_FORMAT_RGB;
case VK_FORMAT_B8G8R8A8_UNORM:
return GST_VIDEO_FORMAT_BGRA;
case VK_FORMAT_B8G8R8_UNORM:
return GST_VIDEO_FORMAT_BGR;
default:
return GST_VIDEO_FORMAT_UNKNOWN;
}
}
static VkFormat
_vk_format_from_video_format (GstVideoFormat v_format)
{
switch (v_format) {
case GST_VIDEO_FORMAT_RGBA:
return VK_FORMAT_R8G8B8A8_UNORM;
case GST_VIDEO_FORMAT_RGB:
return VK_FORMAT_R8G8B8_UNORM;
case GST_VIDEO_FORMAT_BGRA:
return VK_FORMAT_B8G8R8A8_UNORM;
case GST_VIDEO_FORMAT_BGR:
return VK_FORMAT_B8G8R8_UNORM;
default:
return VK_FORMAT_UNDEFINED;
}
}
static VkColorSpaceKHR
_vk_color_space_from_video_info (GstVideoInfo * v_info)
{
return VK_COLORSPACE_SRGB_NONLINEAR_KHR;
}
static void
_add_vk_format_to_list (GValue * list, VkFormat format)
{
GstVideoFormat v_format;
const gchar *format_str;
v_format = _vk_format_to_video_format (format);
if (v_format) {
GValue item = G_VALUE_INIT;
g_value_init (&item, G_TYPE_STRING);
format_str = gst_video_format_to_string (v_format);
g_value_set_string (&item, format_str);
gst_value_list_append_value (list, &item);
g_value_unset (&item);
}
}
static gboolean
_vulkan_swapper_retrieve_surface_properties (GstVulkanSwapper * swapper,
GError ** error)
{
VkSurfaceDescriptionWindowKHR surface_desc;
VkDevice device = swapper->device->device;
guint32 i, present_queue = -1, graphics_queue = -1;
VkPhysicalDevice gpu;
VkResult err;
if (swapper->surf_formats)
return TRUE;
gpu = gst_vulkan_device_get_physical_device (swapper->device);
if (!_get_window_surface_description (swapper,
(VkSurfaceDescriptionWindowKHR *) & surface_desc)) {
g_set_error (error, GST_VULKAN_ERROR,
GST_VULKAN_ERROR_INITIALIZATION_FAILED,
"Failed to retrieve platform description");
return FALSE;
}
for (i = 0; i < swapper->device->n_queues; i++) {
VkBool32 supports_present;
swapper->GetPhysicalDeviceSurfaceSupportKHR (gpu, i,
(VkSurfaceDescriptionKHR *) & surface_desc, &supports_present);
if ((swapper->device->
queue_family_props[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) != 0) {
if (supports_present) {
/* found one that supports both */
graphics_queue = present_queue = i;
break;
}
if (graphics_queue != -1)
graphics_queue = i;
} else if (supports_present) {
if (present_queue != -1)
present_queue = i;
}
}
if (graphics_queue != present_queue) {
g_set_error (error, GST_VULKAN_ERROR,
GST_VULKAN_ERROR_INITIALIZATION_FAILED,
"Failed to find a compatible present/graphics queue");
return FALSE;
}
if (!(swapper->queue = gst_vulkan_device_get_queue (swapper->device,
swapper->device->queue_family_id, graphics_queue, error)))
return FALSE;
err =
swapper->GetSurfacePropertiesKHR (device,
(VkSurfaceDescriptionKHR *) & surface_desc, &swapper->surf_props);
if (gst_vulkan_error_to_g_error (err, error, "vkGetSurfacePropertiesKHR") < 0)
return FALSE;
err =
swapper->GetSurfaceFormatsKHR (device,
(VkSurfaceDescriptionKHR *) & surface_desc, &swapper->n_surf_formats,
NULL);
if (gst_vulkan_error_to_g_error (err, error, "vkGetSurfaceFormatsKHR") < 0)
return FALSE;
swapper->surf_formats = g_new0 (VkSurfaceFormatKHR, swapper->n_surf_formats);
err =
swapper->GetSurfaceFormatsKHR (device,
(VkSurfaceDescriptionKHR *) & surface_desc, &swapper->n_surf_formats,
swapper->surf_formats);
if (gst_vulkan_error_to_g_error (err, error, "vkGetSurfaceFormatsKHR") < 0)
return FALSE;
err =
swapper->GetSurfacePresentModesKHR (device,
(VkSurfaceDescriptionKHR *) & surface_desc,
&swapper->n_surf_present_modes, NULL);
if (gst_vulkan_error_to_g_error (err, error,
"vkGetSurfacePresentModesKHR") < 0)
return FALSE;
swapper->surf_present_modes =
g_new0 (VkPresentModeKHR, swapper->n_surf_present_modes);
err =
swapper->GetSurfacePresentModesKHR (device,
(VkSurfaceDescriptionKHR *) & surface_desc,
&swapper->n_surf_present_modes, swapper->surf_present_modes);
if (gst_vulkan_error_to_g_error (err, error,
"vkGetSurfacePresentModesKHR") < 0)
return FALSE;
return TRUE;
}
static void
gst_vulkan_swapper_finalize (GObject * object)
{
GstVulkanSwapper *swapper = GST_VULKAN_SWAPPER (object);
int i;
if (swapper->swap_chain_images) {
for (i = 0; i < swapper->n_swap_chain_images; i++) {
gst_memory_unref ((GstMemory *) swapper->swap_chain_images[i]);
swapper->swap_chain_images[i] = NULL;
}
g_free (swapper->swap_chain_images);
}
swapper->swap_chain_images = NULL;
if (swapper->swap_chain.handle)
swapper->DestroySwapchainKHR (swapper->device->device, swapper->swap_chain);
swapper->swap_chain.handle = 0;
if (swapper->queue)
gst_object_unref (swapper->queue);
swapper->queue = NULL;
if (swapper->device)
gst_object_unref (swapper->device);
swapper->device = NULL;
if (swapper->window)
gst_object_unref (swapper->window);
swapper->window = NULL;
g_free (swapper->surf_present_modes);
swapper->surf_present_modes = NULL;
g_free (swapper->surf_formats);
swapper->surf_formats = NULL;
gst_caps_replace (&swapper->caps, NULL);
}
static void
gst_vulkan_swapper_init (GstVulkanSwapper * swapper)
{
}
static void
gst_vulkan_swapper_class_init (GstVulkanSwapperClass * klass)
{
G_OBJECT_CLASS (klass)->finalize = gst_vulkan_swapper_finalize;
}
GstVulkanSwapper *
gst_vulkan_swapper_new (GstVulkanDevice * device, GstVulkanWindow * window)
{
GstVulkanSwapper *swapper;
swapper = g_object_new (GST_TYPE_VULKAN_SWAPPER, NULL);
swapper->device = gst_object_ref (device);
swapper->window = gst_object_ref (window);
if (!_get_function_table (swapper)) {
gst_object_unref (swapper);
return NULL;
}
return swapper;
}
GstCaps *
gst_vulkan_swapper_get_supported_caps (GstVulkanSwapper * swapper,
GError ** error)
{
GstStructure *s;
GstCaps *caps;
g_return_val_if_fail (GST_IS_VULKAN_SWAPPER (swapper), NULL);
if (!_vulkan_swapper_retrieve_surface_properties (swapper, error))
return NULL;
caps = gst_caps_new_empty_simple ("video/x-raw");
s = gst_caps_get_structure (caps, 0);
{
int i;
GValue list = G_VALUE_INIT;
g_value_init (&list, GST_TYPE_LIST);
if (swapper->n_surf_formats
&& swapper->surf_formats[0].format == VK_FORMAT_UNDEFINED) {
_add_vk_format_to_list (&list, VK_FORMAT_B8G8R8A8_UNORM);
} else {
for (i = 0; i < swapper->n_surf_formats; i++) {
_add_vk_format_to_list (&list, swapper->surf_formats[i].format);
}
}
gst_structure_set_value (s, "format", &list);
g_value_unset (&list);
}
{
guint32 max_dim = swapper->device->gpu_props.limits.maxImageDimension2D;
gst_structure_set (s, "width", GST_TYPE_INT_RANGE, 1, (gint) max_dim,
"height", GST_TYPE_INT_RANGE, 1, (gint) max_dim, "pixel-aspect-ratio",
GST_TYPE_FRACTION, 1, 1, "framerate", GST_TYPE_FRACTION_RANGE, 0, 1,
G_MAXINT, 1, NULL);
}
GST_INFO_OBJECT (swapper, "Probed the following caps %" GST_PTR_FORMAT, caps);
return caps;
}
static gboolean
_swapper_set_image_layout_with_cmd (GstVulkanSwapper * swapper, VkCmdBuffer cmd,
GstVulkanImageMemory * image, VkImageLayout new_image_layout,
GError ** error)
{
VkPipelineStageFlags src_stages = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
VkPipelineStageFlags dest_stages = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
VkImageMemoryBarrier image_memory_barrier;
VkImageMemoryBarrier *pmemory_barrier = &image_memory_barrier;
gst_vulkan_image_memory_set_layout (image, new_image_layout,
&image_memory_barrier);
vkCmdPipelineBarrier (cmd, src_stages, dest_stages, FALSE, 1,
(const void *const *) &pmemory_barrier);
return TRUE;
}
static gboolean
_new_fence (GstVulkanDevice * device, VkFence * fence, GError ** error)
{
VkFenceCreateInfo fence_info;
VkResult err;
fence_info.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
fence_info.pNext = NULL;
fence_info.flags = 0;
err = vkCreateFence (device->device, &fence_info, fence);
if (gst_vulkan_error_to_g_error (err, error, "vkCreateFence") < 0)
return FALSE;
return TRUE;
}
static gboolean
_swapper_set_image_layout (GstVulkanSwapper * swapper,
GstVulkanImageMemory * image, VkImageLayout new_image_layout,
GError ** error)
{
VkCmdBuffer cmd;
VkFence fence;
VkResult err;
if (!gst_vulkan_device_create_cmd_buffer (swapper->device, &cmd, error))
return FALSE;
if (!_new_fence (swapper->device, &fence, error))
return FALSE;
{
VkCmdBufferBeginInfo cmd_buf_info = {
.sType = VK_STRUCTURE_TYPE_CMD_BUFFER_BEGIN_INFO,
.pNext = NULL,
.flags = VK_CMD_BUFFER_OPTIMIZE_SMALL_BATCH_BIT |
VK_CMD_BUFFER_OPTIMIZE_ONE_TIME_SUBMIT_BIT,
.renderPass = {VK_NULL_HANDLE}
,
.subpass = 0,
.framebuffer = {VK_NULL_HANDLE}
,
};
err = vkBeginCommandBuffer (cmd, &cmd_buf_info);
if (gst_vulkan_error_to_g_error (err, error, "vkBeginCommandBuffer") < 0)
return FALSE;
}
if (!_swapper_set_image_layout_with_cmd (swapper, cmd, image,
new_image_layout, error))
return FALSE;
err = vkEndCommandBuffer (cmd);
if (gst_vulkan_error_to_g_error (err, error, "vkEndCommandBuffer") < 0)
return FALSE;
err = vkQueueSubmit (swapper->queue->queue, 1, &cmd, fence);
if (gst_vulkan_error_to_g_error (err, error, "vkQueueSubmit") < 0)
return FALSE;
err = vkWaitForFences (swapper->device->device, 1, &fence, TRUE, -1);
if (gst_vulkan_error_to_g_error (err, error, "vkWaitForFences") < 0)
return FALSE;
vkDestroyCommandBuffer (swapper->device->device, cmd);
vkDestroyFence (swapper->device->device, fence);
return TRUE;
}
static gboolean
_allocate_swapchain (GstVulkanSwapper * swapper, GstCaps * caps,
GError ** error)
{
VkSurfaceTransformFlagsKHR preTransform;
VkSurfaceDescriptionWindowKHR surface_desc;
VkPresentModeKHR present_mode;
VkImageUsageFlags usage = 0;
VkColorSpaceKHR color_space;
VkImage *swap_chain_images;
VkExtent2D swapchain_dims;
guint32 n_images_wanted;
VkFormat format;
VkResult err;
guint32 i;
/* width and height are either both -1, or both not -1. */
if (swapper->surf_props.currentExtent.width == -1) {
/* If the surface size is undefined, the size is set to
* the size of the images requested. */
swapchain_dims.width = 320;
swapchain_dims.height = 240;
} else {
/* If the surface size is defined, the swap chain size must match */
swapchain_dims = swapper->surf_props.currentExtent;
}
/* If mailbox mode is available, use it, as is the lowest-latency non-
* tearing mode. If not, try IMMEDIATE which will usually be available,
* and is fastest (though it tears). If not, fall back to FIFO which is
* always available. */
present_mode = VK_PRESENT_MODE_FIFO_KHR;
for (size_t i = 0; i < swapper->n_surf_present_modes; i++) {
if (swapper->surf_present_modes[i] == VK_PRESENT_MODE_MAILBOX_KHR) {
present_mode = VK_PRESENT_MODE_MAILBOX_KHR;
break;
}
if ((present_mode != VK_PRESENT_MODE_MAILBOX_KHR) &&
(swapper->surf_present_modes[i] == VK_PRESENT_MODE_IMMEDIATE_KHR)) {
present_mode = VK_PRESENT_MODE_IMMEDIATE_KHR;
}
}
/* Determine the number of VkImage's to use in the swap chain (we desire to
* own only 1 image at a time, besides the images being displayed and
* queued for display): */
n_images_wanted = swapper->surf_props.minImageCount + 1;
if ((swapper->surf_props.maxImageCount > 0) &&
(n_images_wanted > swapper->surf_props.maxImageCount)) {
/* Application must settle for fewer images than desired: */
n_images_wanted = swapper->surf_props.maxImageCount;
}
if (swapper->surf_props.
supportedTransforms & VK_SURFACE_TRANSFORM_NONE_BIT_KHR) {
preTransform = VK_SURFACE_TRANSFORM_NONE_KHR;
} else {
preTransform = swapper->surf_props.currentTransform;
}
if (!_get_window_surface_description (swapper, &surface_desc)) {
g_set_error (error, GST_VULKAN_ERROR,
GST_VULKAN_ERROR_INITIALIZATION_FAILED,
"Failed to retrieve platform description");
return FALSE;
}
format =
_vk_format_from_video_format (GST_VIDEO_INFO_FORMAT (&swapper->v_info));
color_space = _vk_color_space_from_video_info (&swapper->v_info);
if ((swapper->surf_props.supportedUsageFlags &
VK_IMAGE_USAGE_TRANSFER_DESTINATION_BIT) != 0) {
usage |= VK_IMAGE_USAGE_TRANSFER_DESTINATION_BIT;
} else {
g_set_error (error, GST_VULKAN_ERROR,
GST_VULKAN_ERROR_INITIALIZATION_FAILED,
"Incorrect usage flags available for the swap images");
return FALSE;
}
if ((swapper->
surf_props.supportedUsageFlags & VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT)
!= 0) {
usage |= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
} else {
g_set_error (error, GST_VULKAN_ERROR,
GST_VULKAN_ERROR_INITIALIZATION_FAILED,
"Incorrect usage flags available for the swap images");
return FALSE;
}
{
const VkSwapchainCreateInfoKHR swap_chain_info = {
.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR,
.pNext = NULL,
.pSurfaceDescription = (const VkSurfaceDescriptionKHR *) &surface_desc,
.minImageCount = n_images_wanted,
.imageFormat = format,
.imageColorSpace = color_space,
.imageExtent = {
.width = swapchain_dims.width,
.height = swapchain_dims.height,
},
.imageUsageFlags = usage,
.preTransform = preTransform,
.imageArraySize = 1,
.sharingMode = VK_SHARING_MODE_EXCLUSIVE,
.queueFamilyCount = 0,
.pQueueFamilyIndices = NULL,
.presentMode = present_mode,
.oldSwapchain = swapper->swap_chain,
.clipped = TRUE,
};
err =
swapper->CreateSwapchainKHR (swapper->device->device, &swap_chain_info,
&swapper->swap_chain);
if (gst_vulkan_error_to_g_error (err, error, "vkCreateSwapchainKHR") < 0)
return FALSE;
}
err =
swapper->GetSwapchainImagesKHR (swapper->device->device,
swapper->swap_chain, &swapper->n_swap_chain_images, NULL);
if (gst_vulkan_error_to_g_error (err, error, "vkGetSwapchainImagesKHR") < 0)
return FALSE;
swap_chain_images = g_new0 (VkImage, swapper->n_swap_chain_images);
err =
swapper->GetSwapchainImagesKHR (swapper->device->device,
swapper->swap_chain, &swapper->n_swap_chain_images, swap_chain_images);
if (gst_vulkan_error_to_g_error (err, error, "vkGetSwapchainImagesKHR") < 0) {
g_free (swap_chain_images);
return FALSE;
}
swapper->swap_chain_images =
g_new0 (GstVulkanImageMemory *, swapper->n_swap_chain_images);
for (i = 0; i < swapper->n_swap_chain_images; i++) {
swapper->swap_chain_images[i] = (GstVulkanImageMemory *)
gst_vulkan_image_memory_wrapped (swapper->device, swap_chain_images[i],
format, swapchain_dims.width, swapchain_dims.height,
VK_IMAGE_TILING_OPTIMAL, usage, NULL, NULL);
if (!_swapper_set_image_layout (swapper, swapper->swap_chain_images[i],
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, error)) {
g_free (swap_chain_images);
return FALSE;
}
}
g_free (swap_chain_images);
return TRUE;
}
static gboolean
_swapchain_resize (GstVulkanSwapper * swapper, GError ** error)
{
int i;
if (!swapper->queue) {
if (!_vulkan_swapper_retrieve_surface_properties (swapper, error)) {
return FALSE;
}
}
if (swapper->swap_chain_images) {
for (i = 0; i < swapper->n_swap_chain_images; i++) {
if (swapper->swap_chain_images[i])
gst_memory_unref ((GstMemory *) swapper->swap_chain_images[i]);
}
g_free (swapper->swap_chain_images);
}
return _allocate_swapchain (swapper, swapper->caps, error);
}
gboolean
gst_vulkan_swapper_set_caps (GstVulkanSwapper * swapper, GstCaps * caps,
GError ** error)
{
if (!gst_video_info_from_caps (&swapper->v_info, caps)) {
g_set_error (error, GST_VULKAN_ERROR,
GST_VULKAN_ERROR_INITIALIZATION_FAILED,
"Failed to geto GstVideoInfo from caps");
return FALSE;
}
gst_caps_replace (&swapper->caps, caps);
return _swapchain_resize (swapper, error);
}
struct cmd_data
{
VkCmdBuffer cmd;
VkFence fence;
GDestroyNotify notify;
gpointer data;
};
static gboolean
_build_render_buffer_cmd (GstVulkanSwapper * swapper, guint32 swap_idx,
GstBuffer * buffer, struct cmd_data *cmd_data, GError ** error)
{
const VkImageSubresource subres = {
.aspect = VK_IMAGE_ASPECT_COLOR,
.mipLevel = 0,
.arrayLayer = 0,
};
GstVulkanImageMemory *swap_mem, *staging;
GstMapInfo staging_map_info;
VkSubresourceLayout layout;
GstVideoFrame vframe;
guint8 *src, *dest;
VkCmdBuffer cmd;
guint32 wt, ht;
VkResult err;
gsize h;
g_return_val_if_fail (swap_idx < swapper->n_swap_chain_images, FALSE);
swap_mem = swapper->swap_chain_images[swap_idx];
if (!gst_vulkan_device_create_cmd_buffer (swapper->device, &cmd, error))
return FALSE;
if (!gst_video_frame_map (&vframe, &swapper->v_info, buffer, GST_MAP_READ)) {
g_set_error (error, GST_VULKAN_ERROR, GST_VULKAN_ERROR_MEMORY_MAP_FAILED,
"Failed to map buffer");
return FALSE;
}
staging =
(GstVulkanImageMemory *) gst_vulkan_image_memory_alloc (swapper->device,
swap_mem->create_info.format, GST_VIDEO_FRAME_COMP_WIDTH (&vframe, 0),
GST_VIDEO_FRAME_COMP_HEIGHT (&vframe, 0), VK_IMAGE_TILING_LINEAR,
VK_IMAGE_USAGE_TRANSFER_SOURCE_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT);
if (!staging) {
g_set_error (error, GST_VULKAN_ERROR, GST_VULKAN_ERROR_MEMORY_MAP_FAILED,
"Failed to create staging memory");
gst_video_frame_unmap (&vframe);
return FALSE;
}
if (!gst_memory_map ((GstMemory *) staging, &staging_map_info, GST_MAP_WRITE)) {
g_set_error (error, GST_VULKAN_ERROR, GST_VULKAN_ERROR_MEMORY_MAP_FAILED,
"Failed to map swap image");
gst_video_frame_unmap (&vframe);
gst_memory_unref ((GstMemory *) staging);
return FALSE;
}
err = vkGetImageSubresourceLayout (swapper->device->device, staging->image,
&subres, &layout);
if (gst_vulkan_error_to_g_error (err, error,
"vkGetImageSubresourceLayout") < 0) {
gst_video_frame_unmap (&vframe);
gst_memory_unmap ((GstMemory *) staging, &staging_map_info);
gst_memory_unref ((GstMemory *) staging);
return FALSE;
}
/* FIXME: multi-planar formats */
dest = staging_map_info.data;
dest += layout.offset;
src = vframe.data[0];
for (h = 0; h < GST_VIDEO_FRAME_COMP_HEIGHT (&vframe, 0); h++) {
/* FIXME: memcpy */
memcpy (dest, src, GST_VIDEO_FRAME_PLANE_STRIDE (&vframe, 0));
dest += layout.rowPitch;
src += GST_VIDEO_FRAME_PLANE_STRIDE (&vframe, 0);
g_assert (dest - staging_map_info.data - layout.offset <= layout.size);
}
gst_video_frame_unmap (&vframe);
gst_memory_unmap ((GstMemory *) staging, &staging_map_info);
{
VkCmdBufferBeginInfo cmd_buf_info = {
.sType = VK_STRUCTURE_TYPE_CMD_BUFFER_BEGIN_INFO,
.pNext = NULL,
.flags = VK_CMD_BUFFER_OPTIMIZE_SMALL_BATCH_BIT |
VK_CMD_BUFFER_OPTIMIZE_ONE_TIME_SUBMIT_BIT,
.renderPass = {VK_NULL_HANDLE}
,
.subpass = 0,
.framebuffer = {VK_NULL_HANDLE}
,
};
err = vkBeginCommandBuffer (cmd, &cmd_buf_info);
if (gst_vulkan_error_to_g_error (err, error, "vkBeginCommandBuffer") < 0)
return FALSE;
}
if (!_swapper_set_image_layout_with_cmd (swapper, cmd, swap_mem,
VK_IMAGE_LAYOUT_TRANSFER_DESTINATION_OPTIMAL, error)) {
return FALSE;
}
if (!_swapper_set_image_layout_with_cmd (swapper, cmd, staging,
VK_IMAGE_LAYOUT_TRANSFER_SOURCE_OPTIMAL, error)) {
return FALSE;
}
#define SUBRESOURCE_COPY(res,aspect_,mip,layer,size) \
G_STMT_START { \
res.aspect = aspect_; \
res.mipLevel = mip; \
res.arrayLayer = layer; \
res.arraySize = size; \
} G_STMT_END
#define OFFSET3D(offset,x_,y_,z_) \
G_STMT_START { \
offset.x = x_; \
offset.y = y_; \
offset.z = z_; \
} G_STMT_END
#define EXTENT3D(extent,w,h,d) \
G_STMT_START { \
extent.width = w; \
extent.height = h; \
extent.depth = d; \
} G_STMT_END
/* FIXME: center rect */
#if 0
/* XXX: doesn't work with LunarG's example driver. According to LunarG,
* it's not implemented */
{
VkImageBlit blit_image = { 0, };
SUBRESOURCE_COPY (blit_image.srcSubresource, VK_IMAGE_ASPECT_COLOR, 0, 0,
1);
OFFSET3D (blit_image.srcOffset, 0, 0, 0);
EXTENT3D (blit_image.extent, GST_VIDEO_INFO_WIDTH (&swapper->v_info),
GST_VIDEO_INFO_HEIGHT (&swapper->v_info), 1);
SUBRESOURCE_COPY (blit_image.destSubresource, VK_IMAGE_ASPECT_COLOR, 0, 0,
1);
OFFSET3D (blit_image.destOffset, 0, 0, 0);
EXTENT3D (blit_image.extent, swap_mem->create_info.extent.width,
swap_mem->create_info.extent.height, 1);
/* FIXME: copy */
vkCmdBlitImage (cmd, staging->image,
VK_IMAGE_LAYOUT_TRANSFER_SOURCE_OPTIMAL, swap_mem->image,
VK_IMAGE_LAYOUT_GENERAL, 1, &blit_image, VK_TEX_FILTER_LINEAR);
}
#else
wt = MIN (swap_mem->create_info.extent.width,
GST_VIDEO_INFO_WIDTH (&swapper->v_info));
ht = MIN (swap_mem->create_info.extent.height,
GST_VIDEO_INFO_HEIGHT (&swapper->v_info));
{
VkImageCopy copy_image = { 0, };
SUBRESOURCE_COPY (copy_image.srcSubresource, VK_IMAGE_ASPECT_COLOR, 0, 0,
1);
OFFSET3D (copy_image.srcOffset, 0, 0, 0);
SUBRESOURCE_COPY (copy_image.destSubresource, VK_IMAGE_ASPECT_COLOR, 0, 0,
1);
OFFSET3D (copy_image.destOffset, 0, 0, 0);
EXTENT3D (copy_image.extent, wt, ht, 1);
/* FIXME: copy */
vkCmdCopyImage (cmd, staging->image,
VK_IMAGE_LAYOUT_TRANSFER_SOURCE_OPTIMAL, swap_mem->image,
VK_IMAGE_LAYOUT_GENERAL, 1, &copy_image);
}
#endif
if (!_swapper_set_image_layout_with_cmd (swapper, cmd, staging,
VK_IMAGE_LAYOUT_PRESENT_SOURCE_KHR, error)) {
return FALSE;
}
err = vkEndCommandBuffer (cmd);
if (gst_vulkan_error_to_g_error (err, error, "vkEndCommandBuffer") < 0)
return FALSE;
cmd_data->cmd = cmd;
cmd_data->notify = (GDestroyNotify) gst_memory_unref;
cmd_data->data = staging;
if (!_new_fence (swapper->device, &cmd_data->fence, error)) {
return FALSE;
}
/* FIXME: staging frame is leaked */
err =
vkQueueSubmit (swapper->queue->queue, 1, &cmd_data->cmd, cmd_data->fence);
if (gst_vulkan_error_to_g_error (err, error, "vkQueueSubmit") < 0) {
return FALSE;
}
return TRUE;
}
gboolean
gst_vulkan_swapper_render_buffer (GstVulkanSwapper * swapper,
GstBuffer * buffer, GError ** error)
{
VkSemaphore semaphore = { 0, };
VkSemaphoreCreateInfo semaphore_info = {
.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO,
.pNext = NULL,
.flags = VK_FENCE_CREATE_SIGNALED_BIT,
};
VkPresentInfoKHR present;
struct cmd_data cmd_data = { 0, };
guint32 swap_idx;
VkResult err;
reacquire:
err = vkCreateSemaphore (swapper->device->device, &semaphore_info,
&semaphore);
if (gst_vulkan_error_to_g_error (err, error, "vkCreateSemaphore") < 0)
goto error;
err =
swapper->AcquireNextImageKHR (swapper->device->device,
swapper->swap_chain, -1, semaphore, &swap_idx);
/* TODO: Deal with the VK_SUBOPTIMAL_KHR and VK_ERROR_OUT_OF_DATE_KHR */
if (err == VK_ERROR_OUT_OF_DATE_KHR) {
vkDestroySemaphore (swapper->device->device, semaphore);
if (!_swapchain_resize (swapper, error))
return FALSE;
goto reacquire;
} else if (gst_vulkan_error_to_g_error (err, error,
"vkAcquireNextImageKHR") < 0) {
goto error;
}
if (!_build_render_buffer_cmd (swapper, swap_idx, buffer, &cmd_data, error))
goto error;
vkQueueWaitSemaphore (swapper->queue->queue, semaphore);
present.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
present.pNext = NULL;
present.swapchainCount = 1;
present.swapchains = &swapper->swap_chain;
present.imageIndices = &swap_idx;
err = swapper->QueuePresentKHR (swapper->queue->queue, &present);
if (err == VK_ERROR_OUT_OF_DATE_KHR) {
vkDestroySemaphore (swapper->device->device, semaphore);
if (!_swapchain_resize (swapper, error))
return FALSE;
/* FIXME: correct? */
return TRUE;
} else if (gst_vulkan_error_to_g_error (err, error, "vkQueuePresentKHR") < 0)
goto error;
err = vkWaitForFences (swapper->device->device, 1, &cmd_data.fence, TRUE, -1);
if (gst_vulkan_error_to_g_error (err, error, "vkWaitForFences") < 0)
goto error;
if (semaphore.handle)
vkDestroySemaphore (swapper->device->device, semaphore);
if (cmd_data.cmd)
vkDestroyCommandBuffer (swapper->device->device, cmd_data.cmd);
if (cmd_data.fence.handle)
vkDestroyFence (swapper->device->device, cmd_data.fence);
if (cmd_data.notify)
cmd_data.notify (cmd_data.data);
return TRUE;
error:
{
if (semaphore.handle)
vkDestroySemaphore (swapper->device->device, semaphore);
if (cmd_data.cmd)
vkDestroyCommandBuffer (swapper->device->device, cmd_data.cmd);
if (cmd_data.fence.handle)
vkDestroyFence (swapper->device->device, cmd_data.fence);
if (cmd_data.notify)
cmd_data.notify (cmd_data.data);
return FALSE;
}
}

91
ext/vulkan/vkswapper.h Normal file
View file

@ -0,0 +1,91 @@
/*
* GStreamer
* Copyright (C) 2015 Matthew Waters <matthew@centricular.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef _VK_SWAPPER_H_
#define _VK_SWAPPER_H_
#include <gst/video/video.h>
#include <vk.h>
#include <vulkan/vk_ext_khr_swapchain.h>
#include <vulkan/vk_ext_khr_device_swapchain.h>
G_BEGIN_DECLS
#define GST_TYPE_VULKAN_SWAPPER (gst_vulkan_swapper_get_type())
#define GST_VULKAN_SWAPPER(o) (G_TYPE_CHECK_INSTANCE_CAST((o), GST_TYPE_VULKAN_SWAPPER, GstVulkanSwapper))
#define GST_VULKAN_SWAPPER_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), GST_TYPE_VULKAN_SWAPPER, GstVulkanSwapperClass))
#define GST_IS_VULKAN_SWAPPER(o) (G_TYPE_CHECK_INSTANCE_TYPE((o), GST_TYPE_VULKAN_SWAPPER))
#define GST_IS_VULKAN_SWAPPER_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE((k), GST_TYPE_VULKAN_SWAPPER))
#define GST_VULKAN_SWAPPER_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS((o), GST_TYPE_VULKAN_SWAPPER, GstVulkanSwapperClass))
GType gst_vulkan_swapper_get_type (void);
struct _GstVulkanSwapper
{
GstObject parent;
GstVulkanDevice *device;
GstVulkanWindow *window;
GstVulkanQueue *queue;
VkSurfacePropertiesKHR surf_props;
VkSurfaceFormatKHR *surf_formats;
guint32 n_surf_formats;
VkPresentModeKHR *surf_present_modes;
guint32 n_surf_present_modes;
VkSwapchainKHR swap_chain;
GstVulkanImageMemory **swap_chain_images;
guint32 n_swap_chain_images;
GstCaps *caps;
GstVideoInfo v_info;
PFN_vkGetPhysicalDeviceSurfaceSupportKHR GetPhysicalDeviceSurfaceSupportKHR;
PFN_vkGetSurfacePropertiesKHR GetSurfacePropertiesKHR;
PFN_vkGetSurfaceFormatsKHR GetSurfaceFormatsKHR;
PFN_vkGetSurfacePresentModesKHR GetSurfacePresentModesKHR;
PFN_vkCreateSwapchainKHR CreateSwapchainKHR;
PFN_vkDestroySwapchainKHR DestroySwapchainKHR;
PFN_vkGetSwapchainImagesKHR GetSwapchainImagesKHR;
PFN_vkAcquireNextImageKHR AcquireNextImageKHR;
PFN_vkQueuePresentKHR QueuePresentKHR;
};
struct _GstVulkanSwapperClass
{
GstObjectClass parent_class;
};
GstVulkanSwapper * gst_vulkan_swapper_new (GstVulkanDevice * device,
GstVulkanWindow * window);
GstCaps * gst_vulkan_swapper_get_supported_caps (GstVulkanSwapper * swapper,
GError ** error);
gboolean gst_vulkan_swapper_set_caps (GstVulkanSwapper * swapper,
GstCaps * caps,
GError ** error);
gboolean gst_vulkan_swapper_render_buffer (GstVulkanSwapper * swapper,
GstBuffer * buffer,
GError ** error);
G_END_DECLS
#endif /* _VK_INSTANCE_H_ */

46
ext/vulkan/vkutils.c Normal file
View file

@ -0,0 +1,46 @@
/*
* GStreamer
* Copyright (C) 2015 Matthew Waters <matthew@centricular.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "vkutils.h"
gboolean
_check_for_all_layers (uint32_t check_count, const char **check_names,
uint32_t layer_count, VkLayerProperties * layers)
{
uint32_t i, j;
for (i = 0; i < check_count; i++) {
gboolean found = FALSE;
for (j = 0; j < layer_count; j++) {
if (g_strcmp0 (check_names[i], layers[j].layerName) == 0) {
found = TRUE;
}
}
if (!found) {
GST_ERROR ("Cannot find layer: %s", check_names[i]);
return FALSE;
}
}
return TRUE;
}

33
ext/vulkan/vkutils.h Normal file
View file

@ -0,0 +1,33 @@
/*
* GStreamer
* Copyright (C) 2015 Matthew Waters <matthew@centricular.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef _VK_UTILS_H_
#define _VK_UTILS_H_
#include <gst/gst.h>
#include "vk.h"
G_BEGIN_DECLS
gboolean _check_for_all_layers (uint32_t check_count, const char ** check_names,
uint32_t layer_count, VkLayerProperties * layers);
G_END_DECLS
#endif /*_VK_UTILS_H_ */

247
ext/vulkan/vkwindow.c Normal file
View file

@ -0,0 +1,247 @@
/*
* GStreamer
* Copyright (C) 2012 Matthew Waters <ystreet00@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
/**
* SECTION:gstglwindow
* @short_description: window/surface abstraction
* @title: GstVulkanWindow
* @see_also: #GstGLContext, #GstGLDisplay
*
* GstVulkanWindow represents a window that elements can render into. A window can
* either be a user visible window (onscreen) or hidden (offscreen).
*/
#if HAVE_CONFIG_H
# include "config.h"
#endif
#include <gmodule.h>
#include <stdio.h>
#include "vkwindow.h"
#if GST_VULKAN_HAVE_WINDOW_X11
#include "x11/vkwindow_x11.h"
#endif
#if GST_VULKAN_HAVE_WINDOW_XCB
#include "xcb/vkwindow_xcb.h"
#endif
#define GST_CAT_DEFAULT gst_vulkan_window_debug
GST_DEBUG_CATEGORY (GST_CAT_DEFAULT);
#define gst_vulkan_window_parent_class parent_class
G_DEFINE_ABSTRACT_TYPE (GstVulkanWindow, gst_vulkan_window, GST_TYPE_OBJECT);
#define GST_VULKAN_WINDOW_GET_PRIVATE(o) \
(G_TYPE_INSTANCE_GET_PRIVATE((o), GST_TYPE_VULKAN_WINDOW, GstVulkanWindowPrivate))
struct _GstVulkanWindowPrivate
{
guint surface_width;
guint surface_height;
};
static void gst_vulkan_window_finalize (GObject * object);
typedef struct _GstVulkanDummyWindow
{
GstVulkanWindow parent;
guintptr handle;
} GstVulkanDummyWindow;
typedef struct _GstVulkanDummyWindowCass
{
GstVulkanWindowClass parent;
} GstVulkanDummyWindowClass;
GstVulkanDummyWindow *gst_vulkan_dummy_window_new (void);
enum
{
SIGNAL_0,
LAST_SIGNAL
};
/* static guint gst_vulkan_window_signals[LAST_SIGNAL] = { 0 }; */
GQuark
gst_vulkan_window_error_quark (void)
{
return g_quark_from_static_string ("gst-gl-window-error-quark");
}
static gboolean
gst_vulkan_window_default_open (GstVulkanWindow * window, GError ** error)
{
return TRUE;
}
static void
gst_vulkan_window_default_close (GstVulkanWindow * window)
{
}
static void
_init_debug (void)
{
static volatile gsize _init = 0;
if (g_once_init_enter (&_init)) {
GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "vulkanwindow", 0,
"Vulkan Window");
g_once_init_leave (&_init, 1);
}
}
static void
gst_vulkan_window_init (GstVulkanWindow * window)
{
}
static void
gst_vulkan_window_class_init (GstVulkanWindowClass * klass)
{
g_type_class_add_private (klass, sizeof (GstVulkanWindowPrivate));
klass->open = GST_DEBUG_FUNCPTR (gst_vulkan_window_default_open);
klass->close = GST_DEBUG_FUNCPTR (gst_vulkan_window_default_close);
G_OBJECT_CLASS (klass)->finalize = gst_vulkan_window_finalize;
_init_debug ();
}
/**
* gst_vulkan_window_new:
* @display: a #GstGLDisplay
*
* Returns: (transfer full): a new #GstVulkanWindow using @display's connection
*
* Since: 1.4
*/
GstVulkanWindow *
gst_vulkan_window_new (GstVulkanDisplay * display)
{
GstVulkanWindow *window = NULL;
const gchar *user_choice;
g_return_val_if_fail (display != NULL, NULL);
_init_debug ();
user_choice = g_getenv ("GST_VULKAN_WINDOW");
GST_INFO ("creating a window, user choice:%s", user_choice);
#if GST_VULKAN_HAVE_WINDOW_X11
if (!window && (!user_choice || g_strstr_len (user_choice, 3, "x11")))
window = GST_VULKAN_WINDOW (gst_vulkan_window_x11_new (display));
#endif
#if GST_VULKAN_HAVE_WINDOW_XCB
if (!window && (!user_choice || g_strstr_len (user_choice, 3, "xcb")))
window = GST_VULKAN_WINDOW (gst_vulkan_window_xcb_new (display));
#endif
if (!window) {
/* subclass returned a NULL window */
GST_WARNING ("Could not create window. user specified %s, creating dummy"
" window", user_choice ? user_choice : "(null)");
window = GST_VULKAN_WINDOW (gst_vulkan_dummy_window_new ());
}
window->display = gst_object_ref (display);
return window;
}
static void
gst_vulkan_window_finalize (GObject * object)
{
GstVulkanWindow *window = GST_VULKAN_WINDOW (object);
gst_object_unref (window->display);
G_OBJECT_CLASS (gst_vulkan_window_parent_class)->finalize (object);
}
GstVulkanDisplay *
gst_vulkan_window_get_display (GstVulkanWindow * window)
{
g_return_val_if_fail (GST_IS_VULKAN_WINDOW (window), NULL);
return gst_object_ref (window->display);
}
gpointer
gst_vulkan_window_get_platform_handle (GstVulkanWindow * window)
{
GstVulkanWindowClass *klass;
g_return_val_if_fail (GST_IS_VULKAN_WINDOW (window), NULL);
klass = GST_VULKAN_WINDOW_GET_CLASS (window);
g_return_val_if_fail (klass->get_platform_handle != NULL, NULL);
return klass->get_platform_handle (window);
}
gboolean
gst_vulkan_window_open (GstVulkanWindow * window, GError ** error)
{
GstVulkanWindowClass *klass;
g_return_val_if_fail (GST_IS_VULKAN_WINDOW (window), FALSE);
klass = GST_VULKAN_WINDOW_GET_CLASS (window);
g_return_val_if_fail (klass->open != NULL, FALSE);
return klass->open (window, error);
}
void
gst_vulkan_window_close (GstVulkanWindow * window)
{
GstVulkanWindowClass *klass;
g_return_if_fail (GST_IS_VULKAN_WINDOW (window));
klass = GST_VULKAN_WINDOW_GET_CLASS (window);
g_return_if_fail (klass->close != NULL);
return klass->close (window);
}
GType gst_vulkan_dummy_window_get_type (void);
G_DEFINE_TYPE (GstVulkanDummyWindow, gst_vulkan_dummy_window,
GST_TYPE_VULKAN_WINDOW);
static void
gst_vulkan_dummy_window_class_init (GstVulkanDummyWindowClass * klass)
{
}
static void
gst_vulkan_dummy_window_init (GstVulkanDummyWindow * dummy)
{
dummy->handle = 0;
}
GstVulkanDummyWindow *
gst_vulkan_dummy_window_new (void)
{
return g_object_new (gst_vulkan_dummy_window_get_type (), NULL);
}

100
ext/vulkan/vkwindow.h Normal file
View file

@ -0,0 +1,100 @@
/*
* GStreamer
* Copyright (C) 2008 Julien Isorce <julien.isorce@gmail.com>
* Copyright (C) 2012 Matthew Waters <ystreet00@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef __GST_VULKAN_WINDOW_H__
#define __GST_VULKAN_WINDOW_H__
#include <gst/gst.h>
#include <vk.h>
G_BEGIN_DECLS
#define GST_TYPE_VULKAN_WINDOW (gst_vulkan_window_get_type())
#define GST_VULKAN_WINDOW(o) (G_TYPE_CHECK_INSTANCE_CAST((o), GST_TYPE_VULKAN_WINDOW, GstVulkanWindow))
#define GST_VULKAN_WINDOW_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), GST_TYPE_VULKAN_WINDOW, GstVulkanWindowClass))
#define GST_IS_VULKAN_WINDOW(o) (G_TYPE_CHECK_INSTANCE_TYPE((o), GST_TYPE_VULKAN_WINDOW))
#define GST_IS_VULKAN_WINDOW_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE((k), GST_TYPE_VULKAN_WINDOW))
#define GST_VULKAN_WINDOW_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS((o), GST_TYPE_VULKAN_WINDOW, GstVulkanWindowClass))
GType gst_vulkan_window_get_type (void);
#define GST_VULKAN_WINDOW_LOCK(w) g_mutex_lock(&GST_VULKAN_WINDOW(w)->lock)
#define GST_VULKAN_WINDOW_UNLOCK(w) g_mutex_unlock(&GST_VULKAN_WINDOW(w)->lock)
#define GST_VULKAN_WINDOW_GET_LOCK(w) (&GST_VULKAN_WINDOW(w)->lock)
#define GST_VULKAN_WINDOW_ERROR (gst_vulkan_window_error_quark ())
GQuark gst_vulkan_window_error_quark (void);
typedef enum
{
GST_VULKAN_WINDOW_ERROR_FAILED,
GST_VULKAN_WINDOW_ERROR_OLD_LIBS,
GST_VULKAN_WINDOW_ERROR_RESOURCE_UNAVAILABLE,
} GstVulkanWindowError;
/**
* GstVulkanWindow:
*
* #GstVulkanWindow is an opaque struct and should only be accessed through the
* provided api.
*/
struct _GstVulkanWindow {
/*< private >*/
GstObject parent;
GstVulkanDisplay *display;
GMutex lock;
GstVulkanWindowPrivate *priv;
gpointer _reserved[GST_PADDING];
};
/**
* GstVulkanWindowClass:
* @parent_class: Parent class
* @open: open the connection to the display
* @close: close the connection to the display
*/
struct _GstVulkanWindowClass {
GstObjectClass parent_class;
gboolean (*open) (GstVulkanWindow *window, GError **error);
void (*close) (GstVulkanWindow *window);
gpointer (*get_platform_handle) (GstVulkanWindow *window);
/*< private >*/
gpointer _reserved[GST_PADDING];
};
GstVulkanWindow * gst_vulkan_window_new (GstVulkanDisplay *display);
GstVulkanDisplay * gst_vulkan_window_get_display (GstVulkanWindow *window);
gpointer gst_vulkan_window_get_platform_handle (GstVulkanWindow *window);
gboolean gst_vulkan_window_open (GstVulkanWindow * window, GError ** error);
void gst_vulkan_window_close (GstVulkanWindow * window);
G_END_DECLS
#endif /* __GST_VULKAN_WINDOW_H__ */

View file

@ -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)

View file

@ -0,0 +1,165 @@
/*
* GStreamer
* Copyright (C) 2015 Matthew Waters <matthew@centricular.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "vkdisplay_xcb.h"
#include "xcb_event_source.h"
#define GST_CAT_DEFAULT gst_vulkan_display_debug
GST_DEBUG_CATEGORY_STATIC (gst_vulkan_display_debug);
G_DEFINE_TYPE (GstVulkanDisplayXCB, gst_vulkan_display_xcb,
GST_TYPE_VULKAN_DISPLAY);
static void gst_vulkan_display_xcb_finalize (GObject * object);
static gpointer gst_vulkan_display_xcb_get_handle (GstVulkanDisplay * display);
static gpointer gst_vulkan_display_xcb_get_platform_handle (GstVulkanDisplay *
display);
static void
gst_vulkan_display_xcb_class_init (GstVulkanDisplayXCBClass * klass)
{
GST_VULKAN_DISPLAY_CLASS (klass)->get_handle =
GST_DEBUG_FUNCPTR (gst_vulkan_display_xcb_get_handle);
GST_VULKAN_DISPLAY_CLASS (klass)->get_platform_handle =
GST_DEBUG_FUNCPTR (gst_vulkan_display_xcb_get_platform_handle);
G_OBJECT_CLASS (klass)->finalize = gst_vulkan_display_xcb_finalize;
}
static void
gst_vulkan_display_xcb_init (GstVulkanDisplayXCB * display_xcb)
{
GstVulkanDisplay *display = (GstVulkanDisplay *) display_xcb;
display->type = GST_VULKAN_DISPLAY_TYPE_XCB;
display_xcb->foreign_display = FALSE;
}
static void
gst_vulkan_display_xcb_finalize (GObject * object)
{
GstVulkanDisplayXCB *display_xcb = GST_VULKAN_DISPLAY_XCB (object);
if (!display_xcb->foreign_display && display_xcb->platform_handle.connection)
xcb_disconnect (display_xcb->platform_handle.connection);
display_xcb->platform_handle.connection = NULL;
if (display_xcb->event_source) {
g_source_destroy (display_xcb->event_source);
g_source_unref (display_xcb->event_source);
}
display_xcb->event_source = NULL;
G_OBJECT_CLASS (gst_vulkan_display_xcb_parent_class)->finalize (object);
}
static xcb_screen_t *
_get_screen_from_connection (xcb_connection_t * connection, int screen_no)
{
const xcb_setup_t *setup;
xcb_screen_iterator_t iter;
setup = xcb_get_setup (connection);
iter = xcb_setup_roots_iterator (setup);
while (screen_no-- > 0)
xcb_screen_next (&iter);
return iter.data;
}
/**
* gst_vulkan_display_xcb_new:
* @name: (allow-none): a display name
*
* Create a new #GstVulkanDisplayXCB from the xcb display name. See XOpenDisplay()
* for details on what is a valid name.
*
* Returns: (transfer full): a new #GstVulkanDisplayXCB or %NULL
*/
GstVulkanDisplayXCB *
gst_vulkan_display_xcb_new (const gchar * name)
{
xcb_connection_t *connection;
GstVulkanDisplayXCB *ret;
int screen_no = 0;
GST_DEBUG_CATEGORY_GET (gst_vulkan_display_debug, "gldisplay");
ret = g_object_new (GST_TYPE_VULKAN_DISPLAY_XCB, NULL);
ret->platform_handle.connection = connection = xcb_connect (NULL, &screen_no);
if (connection == NULL || xcb_connection_has_error (connection)) {
GST_ERROR_OBJECT (ret,
"Failed to open XCB display connection with name, \'%s\'", name);
gst_object_unref (ret);
return NULL;
}
ret->screen = _get_screen_from_connection (connection, screen_no);
ret->platform_handle.root = ret->screen->root;
ret->event_source = xcb_event_source_new (ret);
return ret;
}
/**
* gst_vulkan_display_xcb_new_with_display:
* @display: an existing, xcb display
*
* Creates a new display connection from a XCB Display.
*
* Returns: (transfer full): a new #GstVulkanDisplayXCB
*/
GstVulkanDisplayXCB *
gst_vulkan_display_xcb_new_with_connection (xcb_connection_t * connection,
int screen_no)
{
GstVulkanDisplayXCB *ret;
g_return_val_if_fail (connection != NULL, NULL);
GST_DEBUG_CATEGORY_GET (gst_vulkan_display_debug, "gldisplay");
ret = g_object_new (GST_TYPE_VULKAN_DISPLAY_XCB, NULL);
ret->platform_handle.connection = connection;
ret->screen = _get_screen_from_connection (connection, screen_no);
ret->platform_handle.root = ret->screen->root;
ret->foreign_display = TRUE;
return ret;
}
static gpointer
gst_vulkan_display_xcb_get_handle (GstVulkanDisplay * display)
{
return (gpointer) GST_VULKAN_DISPLAY_XCB (display)->platform_handle.
connection;
}
static gpointer
gst_vulkan_display_xcb_get_platform_handle (GstVulkanDisplay * display)
{
return &GST_VULKAN_DISPLAY_XCB (display)->platform_handle;
}

View file

@ -0,0 +1,84 @@
/*
* GStreamer
* Copyright (C) 2013 Matthew Waters <ystreet00@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef __GST_VULKAN_DISPLAY_XCB_H__
#define __GST_VULKAN_DISPLAY_XCB_H__
#include <gst/gst.h>
#include <xcb/xcb.h>
#include <vk.h>
G_BEGIN_DECLS
GType gst_vulkan_display_xcb_get_type (void);
#define GST_TYPE_VULKAN_DISPLAY_XCB (gst_vulkan_display_xcb_get_type())
#define GST_VULKAN_DISPLAY_XCB(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_VULKAN_DISPLAY_XCB,GstVulkanDisplayXCB))
#define GST_VULKAN_DISPLAY_XCB_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), GST_TYPE_VULKAN_DISPLAY_XCB,GstVulkanDisplayXCBClass))
#define GST_IS_VULKAN_DISPLAY_XCB(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_VULKAN_DISPLAY_XCB))
#define GST_IS_VULKAN_DISPLAY_XCB_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), GST_TYPE_VULKAN_DISPLAY_XCB))
#define GST_VULKAN_DISPLAY_XCB_CAST(obj) ((GstVulkanDisplayXCB*)(obj))
typedef struct _GstVulkanDisplayXCB GstVulkanDisplayXCB;
typedef struct _GstVulkanDisplayXCBClass GstVulkanDisplayXCBClass;
#define GST_VULKAN_DISPLAY_XCB_CONNECTION(d) (GST_VULKAN_DISPLAY_XCB(d)->platform_handle.connection)
#define GST_VULKAN_DISPLAY_XCB_ROOT_WINDOW(d) (GST_VULKAN_DISPLAY_XCB(d)->platform_handle.root)
#define GST_VULKAN_DISPLAY_XCB_SCREEN(d) (GST_VULKAN_DISPLAY_XCB(d)->screen)
struct _GstVkPlatformHandleXCBKHR
{
xcb_connection_t* connection;
xcb_window_t root;
};
/**
* GstVulkanDisplayXCB:
*
* the contents of a #GstVulkanDisplayXCB are private and should only be accessed
* through the provided API
*/
struct _GstVulkanDisplayXCB
{
GstVulkanDisplay parent;
/* <private> */
gboolean foreign_display;
struct _GstVkPlatformHandleXCBKHR platform_handle;
xcb_screen_t *screen;
GSource *event_source;
};
struct _GstVulkanDisplayXCBClass
{
GstVulkanDisplayClass object_class;
};
GstVulkanDisplayXCB * gst_vulkan_display_xcb_new (const gchar * name);
GstVulkanDisplayXCB * gst_vulkan_display_xcb_new_with_connection (xcb_connection_t * connection,
int screen_no);
G_END_DECLS
#endif /* __GST_VULKAN_DISPLAY_XCB_H__ */

View file

@ -0,0 +1,243 @@
/*
* GStreamer
* Copyright (C) 2015 Matthew Waters <matthew@centricular.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <gst/gst.h>
#include <locale.h>
#include "vkwindow_xcb.h"
#include "vkdisplay_xcb.h"
#define GST_VULKAN_WINDOW_XCB_GET_PRIVATE(o) \
(G_TYPE_INSTANCE_GET_PRIVATE((o), GST_TYPE_VULKAN_WINDOW_XCB, GstVulkanWindowXCBPrivate))
#define GST_CAT_DEFAULT gst_vulkan_window_xcb_debug
GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
static void
_init_debug (void)
{
static volatile gsize _init = 0;
if (g_once_init_enter (&_init)) {
GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "vulkanwindowxcb", 0,
"Vulkan XCB Window");
g_once_init_leave (&_init, 1);
}
}
#define gst_vulkan_window_xcb_parent_class parent_class
G_DEFINE_TYPE_WITH_CODE (GstVulkanWindowXCB, gst_vulkan_window_xcb,
GST_TYPE_VULKAN_WINDOW, _init_debug ());
gboolean gst_vulkan_window_xcb_handle_event (GstVulkanWindowXCB * window_xcb);
enum
{
PROP_0,
};
struct _GstVulkanWindowXCBPrivate
{
gboolean activate;
gboolean activate_result;
gint preferred_width;
gint preferred_height;
xcb_intern_atom_reply_t *atom_wm_delete_window;
};
static gpointer gst_vulkan_window_xcb_get_platform_window (GstVulkanWindow *
window);
gboolean gst_vulkan_window_xcb_open (GstVulkanWindow * window, GError ** error);
void gst_vulkan_window_xcb_close (GstVulkanWindow * window);
static void
gst_vulkan_window_xcb_finalize (GObject * object)
{
G_OBJECT_CLASS (parent_class)->finalize (object);
}
static void
gst_vulkan_window_xcb_class_init (GstVulkanWindowXCBClass * klass)
{
GObjectClass *obj_class = G_OBJECT_CLASS (klass);
GstVulkanWindowClass *window_class = (GstVulkanWindowClass *) klass;
g_type_class_add_private (klass, sizeof (GstVulkanWindowXCBPrivate));
obj_class->finalize = gst_vulkan_window_xcb_finalize;
window_class->open = GST_DEBUG_FUNCPTR (gst_vulkan_window_xcb_open);
window_class->close = GST_DEBUG_FUNCPTR (gst_vulkan_window_xcb_close);
window_class->get_platform_handle = gst_vulkan_window_xcb_get_platform_window;
}
static void
gst_vulkan_window_xcb_init (GstVulkanWindowXCB * window)
{
window->priv = GST_VULKAN_WINDOW_XCB_GET_PRIVATE (window);
}
/* Must be called in the gl thread */
GstVulkanWindowXCB *
gst_vulkan_window_xcb_new (GstVulkanDisplay * display)
{
_init_debug ();
if ((gst_vulkan_display_get_handle_type (display) &
GST_VULKAN_DISPLAY_TYPE_XCB)
== GST_VULKAN_DISPLAY_TYPE_NONE) {
GST_INFO ("Wrong display type %u for this window type %u", display->type,
GST_VULKAN_DISPLAY_TYPE_XCB);
return NULL;
}
return g_object_new (GST_TYPE_VULKAN_WINDOW_XCB, NULL);
}
static void
gst_vulkan_window_xcb_show (GstVulkanWindow * window)
{
GstVulkanWindowXCB *window_xcb = GST_VULKAN_WINDOW_XCB (window);
GstVulkanDisplayXCB *display_xcb = GST_VULKAN_DISPLAY_XCB (window->display);
xcb_connection_t *connection = display_xcb->platform_handle.connection;
if (!window_xcb->visible) {
xcb_map_window (connection, window_xcb->win_id);
window_xcb->visible = TRUE;
}
}
static void
gst_vulkan_window_xcb_hide (GstVulkanWindow * window)
{
GstVulkanWindowXCB *window_xcb = GST_VULKAN_WINDOW_XCB (window);
GstVulkanDisplayXCB *display_xcb = GST_VULKAN_DISPLAY_XCB (window->display);
xcb_connection_t *connection = display_xcb->platform_handle.connection;
if (window_xcb->visible) {
xcb_unmap_window (connection, window_xcb->win_id);
window_xcb->visible = FALSE;
}
}
gboolean
gst_vulkan_window_xcb_create_window (GstVulkanWindowXCB * window_xcb)
{
GstVulkanDisplayXCB *display_xcb;
xcb_connection_t *connection;
xcb_screen_t *screen;
xcb_window_t root_window;
uint32_t value_mask, value_list[32];
xcb_intern_atom_cookie_t cookie, cookie2;
xcb_intern_atom_reply_t *reply, *reply2;
// const gchar *title = "OpenGL renderer";
gint x = 0, y = 0, width = 320, height = 240;
display_xcb =
GST_VULKAN_DISPLAY_XCB (GST_VULKAN_WINDOW (window_xcb)->display);
connection = GST_VULKAN_DISPLAY_XCB_CONNECTION (display_xcb);
root_window = GST_VULKAN_DISPLAY_XCB_ROOT_WINDOW (display_xcb);
screen = GST_VULKAN_DISPLAY_XCB_SCREEN (display_xcb);
window_xcb->win_id = xcb_generate_id (connection);
value_mask = XCB_CW_BACK_PIXEL | XCB_CW_EVENT_MASK;
value_list[0] = screen->black_pixel;
value_list[1] = XCB_EVENT_MASK_KEY_RELEASE | XCB_EVENT_MASK_EXPOSURE;
xcb_create_window (connection, XCB_COPY_FROM_PARENT, window_xcb->win_id,
root_window, x, y, width, height, 0, XCB_WINDOW_CLASS_INPUT_OUTPUT,
screen->root_visual, value_mask, value_list);
GST_LOG_OBJECT (window_xcb, "gl window id: %p",
(gpointer) (guintptr) window_xcb->win_id);
GST_LOG_OBJECT (window_xcb, "gl window props: x:%d y:%d", x, y);
/* Magic code that will send notification when window is destroyed */
cookie = xcb_intern_atom (connection, 1, 12, "WM_PROTOCOLS");
reply = xcb_intern_atom_reply (connection, cookie, 0);
cookie2 = xcb_intern_atom (connection, 0, 16, "WM_DELETE_WINDOW");
reply2 = xcb_intern_atom_reply (connection, cookie2, 0);
xcb_change_property (connection, XCB_PROP_MODE_REPLACE, window_xcb->win_id,
reply->atom, 4, 32, 1, &reply2->atom);
g_free (reply);
g_free (reply2);
gst_vulkan_window_xcb_show (GST_VULKAN_WINDOW (window_xcb));
return TRUE;
}
static gpointer
gst_vulkan_window_xcb_get_platform_window (GstVulkanWindow * window)
{
return &GST_VULKAN_WINDOW_XCB (window)->win_id;
}
gboolean
gst_vulkan_window_xcb_open (GstVulkanWindow * window, GError ** error)
{
GstVulkanWindowXCB *window_xcb = GST_VULKAN_WINDOW_XCB (window);
GstVulkanDisplayXCB *display_xcb = (GstVulkanDisplayXCB *) window->display;
xcb_connection_t *connection;
connection = display_xcb->platform_handle.connection;
if (connection == NULL) {
g_set_error (error, GST_VULKAN_WINDOW_ERROR,
GST_VULKAN_WINDOW_ERROR_RESOURCE_UNAVAILABLE,
"Failed to connect to X display server with XCB");
goto failure;
}
if (!GST_VULKAN_WINDOW_CLASS (parent_class)->open (window, error))
return FALSE;
return gst_vulkan_window_xcb_create_window (window_xcb);
failure:
return FALSE;
}
void
gst_vulkan_window_xcb_close (GstVulkanWindow * window)
{
GstVulkanWindowXCB *window_xcb = GST_VULKAN_WINDOW_XCB (window);
GstVulkanDisplayXCB *display_xcb = (GstVulkanDisplayXCB *) window->display;
xcb_connection_t *connection =
GST_VULKAN_DISPLAY_XCB_CONNECTION (display_xcb);
if (connection) {
gst_vulkan_window_xcb_hide (window);
g_free (window_xcb->priv->atom_wm_delete_window);
window_xcb->priv->atom_wm_delete_window = NULL;
GST_DEBUG ("display receiver closed");
}
GST_VULKAN_WINDOW_CLASS (parent_class)->close (window);
}

View file

@ -0,0 +1,86 @@
/*
* GStreamer
* Copyright (C) 2012 Matthew Waters <ystreet00@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef __GST_VULKAN_WINDOW_XCB_H__
#define __GST_VULKAN_WINDOW_XCB_H__
#include <xcb/xcb.h>
#include <vk.h>
G_BEGIN_DECLS
#define GST_TYPE_VULKAN_WINDOW_XCB (gst_vulkan_window_xcb_get_type())
#define GST_VULKAN_WINDOW_XCB(o) (G_TYPE_CHECK_INSTANCE_CAST((o), GST_TYPE_VULKAN_WINDOW_XCB, GstVulkanWindowXCB))
#define GST_VULKAN_WINDOW_XCB_CLASS(k) (G_TYPE_CHECK_CLASS((k), GST_TYPE_VULKAN_WINDOW_XCB, GstVulkanWindowXCBClass))
#define GST_IS_VULKAN_WINDOW_XCB(o) (G_TYPE_CHECK_INSTANCE_TYPE((o), GST_TYPE_VULKAN_WINDOW_XCB))
#define GST_IS_VULKAN_WINDOW_XCB_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE((k), GST_TYPE_VULKAN_WINDOW_XCB))
#define GST_VULKAN_WINDOW_XCB_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS((o), GST_TYPE_VULKAN_WINDOW_XCB, GstVulkanWindowXCBClass))
typedef struct _GstVulkanWindowXCB GstVulkanWindowXCB;
typedef struct _GstVulkanWindowXCBPrivate GstVulkanWindowXCBPrivate;
typedef struct _GstVulkanWindowXCBClass GstVulkanWindowXCBClass;
/**
* GstVulkanWindowXCB:
*
* Opaque #GstVulkanWindowXCB object
*/
struct _GstVulkanWindowXCB
{
/*< private >*/
GstVulkanWindow parent;
/* X window */
xcb_window_t win_id;
gint visible :1;
/*< private >*/
GstVulkanWindowXCBPrivate *priv;
gpointer _reserved[GST_PADDING];
};
/**
* GstVulkanWindowXCBClass:
*
* Opaque #GstVulkanWindowXCBClass object
*/
struct _GstVulkanWindowXCBClass {
/*< private >*/
GstVulkanWindowClass parent_class;
/*< private >*/
gpointer _reserved[GST_PADDING_LARGE];
};
GType gst_vulkan_window_xcb_get_type (void);
GstVulkanWindowXCB * gst_vulkan_window_xcb_new (GstVulkanDisplay * display);
void gst_vulkan_window_xcb_trap_x_errors (void);
gint gst_vulkan_window_xcb_untrap_x_errors (void);
gboolean gst_vulkan_window_xcb_create_window (GstVulkanWindowXCB * window_xcb);
G_END_DECLS
#endif /* __GST_VULKAN_WINDOW_XCB_H__ */

View file

@ -0,0 +1,251 @@
/*
* GStreamer
* Copyright (C) 2012 Matthew Waters <ystreet00@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <stdint.h>
#include <stdlib.h>
#include "xcb_event_source.h"
#include "vkdisplay_xcb.h"
#include "vkwindow_xcb.h"
static gint
_compare_xcb_window (GstVulkanWindowXCB * window_xcb, xcb_window_t * window_id)
{
return window_xcb->win_id - *window_id;
}
static GstVulkanWindowXCB *
_find_window_from_xcb_window (GstVulkanDisplayXCB * display_xcb,
xcb_window_t window_id)
{
GstVulkanDisplay *display = GST_VULKAN_DISPLAY (display_xcb);
GstVulkanWindowXCB *ret = NULL;
GList *l;
if (!window_id)
return NULL;
GST_OBJECT_LOCK (display);
l = g_list_find_custom (display->windows, &window_id,
(GCompareFunc) _compare_xcb_window);
if (l)
ret = gst_object_ref (l->data);
GST_OBJECT_UNLOCK (display);
return ret;
}
static gboolean
_xcb_handle_event (GstVulkanDisplayXCB * display_xcb)
{
xcb_connection_t *connection =
GST_VULKAN_DISPLAY_XCB_CONNECTION (display_xcb);
xcb_generic_event_t *event;
gboolean ret = TRUE;
while ((event = xcb_poll_for_event (connection))) {
uint8_t event_code = event->response_type & 0x7f;
switch (event_code) {
case XCB_CLIENT_MESSAGE:{
xcb_client_message_event_t *client_event;
xcb_intern_atom_cookie_t cookie;
xcb_intern_atom_reply_t *reply;
client_event = (xcb_client_message_event_t *) event;
cookie = xcb_intern_atom (connection, 0, 16, "WM_DELETE_WINDOW");
reply = xcb_intern_atom_reply (connection, cookie, 0);
if (client_event->data.data32[0] == reply->atom) {
GstVulkanWindowXCB *window_xcb;
window_xcb =
_find_window_from_xcb_window (display_xcb, client_event->window);
/* TODO: actually quit */
ret = FALSE;
#if 0
if (display->close)
display->close (display->close_data);
#endif
gst_object_unref (window_xcb);
}
g_free (reply);
break;
}
#if 0
case CreateNotify:
case ConfigureNotify:
#if 0
gst_vulkan_window_resize (window, event.xconfigure.width,
event.xconfigure.height);
#endif
break;
case DestroyNotify:
break;
case Expose:
/* non-zero means that other Expose follows
* so just wait for the last one
* in theory we should not receive non-zero because
* we have no sub areas here but just in case */
if (event.xexpose.count != 0) {
break;
}
#if 0
/* We need to redraw on expose */
if (window->draw) {
context = gst_vulkan_window_get_context (window);
context_class = GST_VULKAN_CONTEXT_GET_CLASS (context);
window->draw (window->draw_data);
context_class->swap_buffers (context);
gst_object_unref (context);
}
#endif
break;
case VisibilityNotify:
/* actually nothing to do here */
break;
#if 0
case KeyPress:
case KeyRelease:
keysym = XkbKeycodeToKeysym (window_xcb->device,
event.xkey.keycode, 0, 0);
key_str = XKeysymToString (keysym);
key_data = g_slice_new (struct key_event);
key_data->window = window;
key_data->key_str = XKeysymToString (keysym);
key_data->event_type =
event.type == KeyPress ? "key-press" : "key-release";
GST_DEBUG ("input event key %d pressed over window at %d,%d (%s)",
event.xkey.keycode, event.xkey.x, event.xkey.y, key_str);
g_main_context_invoke (window->navigation_context,
(GSourceFunc) gst_vulkan_window_key_event_cb, key_data);
break;
case ButtonPress:
case ButtonRelease:
GST_DEBUG ("input event mouse button %d pressed over window at %d,%d",
event.xbutton.button, event.xbutton.x, event.xbutton.y);
mouse_data = g_slice_new (struct mouse_event);
mouse_data->window = window;
mouse_data->event_type =
event.type ==
ButtonPress ? "mouse-button-press" : "mouse-button-release";
mouse_data->button = event.xbutton.button;
mouse_data->posx = (double) event.xbutton.x;
mouse_data->posy = (double) event.xbutton.y;
g_main_context_invoke (window->navigation_context,
(GSourceFunc) gst_vulkan_window_mouse_event_cb, mouse_data);
break;
case MotionNotify:
GST_DEBUG ("input event pointer moved over window at %d,%d",
event.xmotion.x, event.xmotion.y);
mouse_data = g_slice_new (struct mouse_event);
mouse_data->window = window;
mouse_data->event_type = "mouse-move";
mouse_data->button = 0;
mouse_data->posx = (double) event.xbutton.x;
mouse_data->posy = (double) event.xbutton.y;
g_main_context_invoke (window->navigation_context, (GSourceFunc)
gst_vulkan_window_mouse_event_cb, mouse_data);
break;
#endif
#endif
default:
GST_DEBUG ("unhandled XCB type: %u", event_code);
break;
}
}
return ret;
}
typedef struct _XCBEventSource
{
GSource source;
GPollFD pfd;
uint32_t mask;
GstVulkanDisplayXCB *display_xcb;
} XCBEventSource;
static gboolean
xcb_event_source_prepare (GSource * base, gint * timeout)
{
*timeout = -1;
return FALSE;
}
static gboolean
xcb_event_source_check (GSource * base)
{
XCBEventSource *source = (XCBEventSource *) base;
gboolean retval;
retval = source->pfd.revents;
return retval;
}
static gboolean
xcb_event_source_dispatch (GSource * base, GSourceFunc callback, gpointer data)
{
XCBEventSource *source = (XCBEventSource *) base;
gboolean ret = _xcb_handle_event (source->display_xcb);
if (callback)
callback (data);
return ret;
}
static GSourceFuncs xcb_event_source_funcs = {
xcb_event_source_prepare,
xcb_event_source_check,
xcb_event_source_dispatch,
NULL
};
GSource *
xcb_event_source_new (GstVulkanDisplayXCB * display_xcb)
{
xcb_connection_t *connection;
XCBEventSource *source;
connection = GST_VULKAN_DISPLAY_XCB_CONNECTION (display_xcb);
g_return_val_if_fail (connection != NULL, NULL);
source = (XCBEventSource *)
g_source_new (&xcb_event_source_funcs, sizeof (XCBEventSource));
source->display_xcb = display_xcb;
source->pfd.fd = xcb_get_file_descriptor (connection);
source->pfd.events = G_IO_IN | G_IO_ERR;
g_source_add_poll (&source->source, &source->pfd);
return &source->source;
}

View file

@ -0,0 +1,30 @@
/*
* GStreamer
* Copyright (C) 2012 Matthew Waters <ystreet00@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef __VULKAN_XCB_EVENT_SOURCE_H__
#define __VULKAN_XCB_EVENT_SOURCE_H__
#include <glib-object.h>
#include "vkdisplay_xcb.h"
GSource *
xcb_event_source_new (GstVulkanDisplayXCB *display_xcb);
#endif /* __VULKAN_XCB_EVENT_SOURCE_H__ */