From 216a321319f2f75538422e3e0281de38a7b5101a Mon Sep 17 00:00:00 2001 From: Matthew Waters Date: Tue, 1 Dec 2015 16:28:28 +1100 Subject: [PATCH] vulkan: implement quitting and resizing the window As before, only xcb has been implemented. --- ext/vulkan/vkdisplay.c | 83 ++++++++++++++++++++++++++++++++++++++- ext/vulkan/vkdisplay.h | 7 ++++ ext/vulkan/vksink.h | 3 -- ext/vulkan/vkswapper.c | 79 ++++++++++++++++++++++++++++++------- ext/vulkan/vkswapper.h | 7 ++++ ext/vulkan/vkwindow.c | 41 +++++++++++++++++-- ext/vulkan/vkwindow.h | 2 + ext/vulkan/xcb/vkdisplay_xcb.c | 28 +++++-------- ext/vulkan/xcb/vkwindow_xcb.c | 4 +- ext/vulkan/xcb/xcb_event_source.c | 43 ++++++++++++-------- 10 files changed, 239 insertions(+), 58 deletions(-) diff --git a/ext/vulkan/vkdisplay.c b/ext/vulkan/vkdisplay.c index aa8d729..ad9f1d0 100644 --- a/ext/vulkan/vkdisplay.c +++ b/ext/vulkan/vkdisplay.c @@ -74,9 +74,39 @@ static GstVulkanWindow struct _GstVulkanDisplayPrivate { - gint dummy; + GThread *event_thread; + + GMutex thread_lock; + GCond thread_cond; }; +static gpointer +_event_thread_main (GstVulkanDisplay * display) +{ + g_mutex_lock (&display->priv->thread_lock); + + display->main_context = g_main_context_new (); + display->main_loop = g_main_loop_new (display->main_context, FALSE); + + g_cond_broadcast (&display->priv->thread_cond); + g_mutex_unlock (&display->priv->thread_lock); + + g_main_loop_run (display->main_loop); + + g_mutex_lock (&display->priv->thread_lock); + + g_main_loop_unref (display->main_loop); + g_main_context_unref (display->main_context); + + display->main_loop = NULL; + display->main_context = NULL; + + g_cond_broadcast (&display->priv->thread_cond); + g_mutex_unlock (&display->priv->thread_lock); + + return NULL; +} + static void gst_vulkan_display_class_init (GstVulkanDisplayClass * klass) { @@ -94,11 +124,42 @@ gst_vulkan_display_init (GstVulkanDisplay * display) { display->priv = GST_VULKAN_DISPLAY_GET_PRIVATE (display); display->type = GST_VULKAN_DISPLAY_TYPE_ANY; + + g_mutex_init (&display->priv->thread_lock); + g_cond_init (&display->priv->thread_cond); + + display->priv->event_thread = g_thread_new ("vkdisplay-event", + (GThreadFunc) _event_thread_main, display); + + g_mutex_lock (&display->priv->thread_lock); + while (!display->main_loop) + g_cond_wait (&display->priv->thread_cond, &display->priv->thread_lock); + g_mutex_unlock (&display->priv->thread_lock); } static void gst_vulkan_display_finalize (GObject * object) { + GstVulkanDisplay *display = GST_VULKAN_DISPLAY (object); + + g_mutex_lock (&display->priv->thread_lock); + if (display->main_context && display->event_source) { + g_source_destroy (display->event_source); + g_source_unref (display->event_source); + } + display->event_source = NULL; + + if (display->main_loop) + g_main_loop_quit (display->main_loop); + + while (display->main_loop) + g_cond_wait (&display->priv->thread_cond, &display->priv->thread_lock); + + if (display->priv->event_thread) + g_thread_unref (display->priv->event_thread); + display->priv->event_thread = NULL; + g_mutex_unlock (&display->priv->thread_lock); + G_OBJECT_CLASS (gst_vulkan_display_parent_class)->finalize (object); } @@ -136,7 +197,7 @@ gst_vulkan_display_new (void) "(platform: %s), creating dummy", GST_STR_NULL (user_choice), GST_STR_NULL (platform_choice)); - return g_object_new (GST_TYPE_VULKAN_DISPLAY, NULL); + display = g_object_new (GST_TYPE_VULKAN_DISPLAY, NULL); } return display; @@ -244,3 +305,21 @@ gst_vulkan_display_default_create_window (GstVulkanDisplay * display) { return gst_vulkan_window_new (display); } + +gboolean +gst_vulkan_display_remove_window (GstVulkanDisplay * display, + GstVulkanWindow * window) +{ + gboolean ret = FALSE; + GList *l; + + GST_OBJECT_LOCK (display); + l = g_list_find (display->windows, window); + if (l) { + display->windows = g_list_delete_link (display->windows, l); + ret = TRUE; + } + GST_OBJECT_UNLOCK (display); + + return ret; +} diff --git a/ext/vulkan/vkdisplay.h b/ext/vulkan/vkdisplay.h index 371eec9..d48eecc 100644 --- a/ext/vulkan/vkdisplay.h +++ b/ext/vulkan/vkdisplay.h @@ -66,7 +66,11 @@ struct _GstVulkanDisplay /* */ GList *windows; /* OBJECT lock */ + GMainContext *main_context; + GMainLoop *main_loop; + GSource *event_source; + /* */ GstVulkanDisplayPrivate *priv; }; @@ -86,6 +90,9 @@ GstVulkanDisplayType gst_vulkan_display_get_handle_type (GstVulkanDisplay gpointer gst_vulkan_display_get_platform_handle (GstVulkanDisplay * display); GstVulkanWindow * gst_vulkan_display_create_window (GstVulkanDisplay * display); +/* GstVulkanWindow usage only */ +gboolean gst_vulkan_display_remove_window (GstVulkanDisplay * display, GstVulkanWindow * window); + G_END_DECLS #endif /* __GST_VULKAN_DISPLAY_H__ */ diff --git a/ext/vulkan/vksink.h b/ext/vulkan/vksink.h index b90624a..9f95cf3 100644 --- a/ext/vulkan/vksink.h +++ b/ext/vulkan/vksink.h @@ -56,9 +56,6 @@ struct _GstVulkanSink /* stream configuration */ GstVideoInfo v_info; - - /* runtime variables */ - gint to_quit; }; struct _GstVulkanSinkClass diff --git a/ext/vulkan/vkswapper.c b/ext/vulkan/vkswapper.c index 4dbcec8..7d8bf44 100644 --- a/ext/vulkan/vkswapper.c +++ b/ext/vulkan/vkswapper.c @@ -40,11 +40,16 @@ _get_function_table (GstVulkanSwapper * swapper) GstVulkanDevice *device = swapper->device; GstVulkanInstance *instance = gst_vulkan_device_get_instance (device); + if (!instance) { + GST_ERROR_OBJECT (swapper, "Failed to get instance from the device"); + return FALSE; + } #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)); \ + gst_object_unref (instance); \ return FALSE; \ } \ } G_STMT_END @@ -60,6 +65,8 @@ _get_function_table (GstVulkanSwapper * swapper) GET_PROC_ADDRESS_REQUIRED (swapper, device, AcquireNextImageKHR); GET_PROC_ADDRESS_REQUIRED (swapper, device, QueuePresentKHR); + gst_object_unref (instance); + return TRUE; #undef GET_PROC_ADDRESS_REQUIRED @@ -102,6 +109,7 @@ _get_window_surface_description (GstVulkanSwapper * swapper, desc->platform = _gst_display_type_to_vk_platform (dtype); if (desc->platform == -1) { GST_ERROR_OBJECT (swapper, "Failed to retrieve platform from display"); + gst_object_unref (display); return FALSE; } @@ -202,8 +210,8 @@ _vulkan_swapper_retrieve_surface_properties (GstVulkanSwapper * swapper, swapper->GetPhysicalDeviceSurfaceSupportKHR (gpu, i, (VkSurfaceDescriptionKHR *) & surface_desc, &supports_present); - if ((swapper->device-> - queue_family_props[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) != 0) { + 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; @@ -270,6 +278,14 @@ _vulkan_swapper_retrieve_surface_properties (GstVulkanSwapper * swapper, return TRUE; } +static gboolean +_on_window_close (GstVulkanWindow * window, GstVulkanSwapper * swapper) +{ + g_atomic_int_set (&swapper->to_quit, 1); + + return TRUE; +} + static void gst_vulkan_swapper_finalize (GObject * object) { @@ -297,6 +313,9 @@ gst_vulkan_swapper_finalize (GObject * object) gst_object_unref (swapper->device); swapper->device = NULL; + g_signal_handler_disconnect (swapper->window, swapper->close_id); + swapper->close_id = 0; + if (swapper->window) gst_object_unref (swapper->window); swapper->window = NULL; @@ -335,6 +354,9 @@ gst_vulkan_swapper_new (GstVulkanDevice * device, GstVulkanWindow * window) return NULL; } + swapper->close_id = g_signal_connect (swapper->window, "close", + (GCallback) _on_window_close, swapper); + return swapper; } @@ -491,6 +513,19 @@ _allocate_swapchain (GstVulkanSwapper * swapper, GstCaps * caps, VkResult err; guint32 i; + 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; + } + + err = + swapper->GetSurfacePropertiesKHR (swapper->device->device, + (VkSurfaceDescriptionKHR *) & surface_desc, &swapper->surf_props); + if (gst_vulkan_error_to_g_error (err, error, "vkGetSurfacePropertiesKHR") < 0) + return FALSE; + /* 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 @@ -528,20 +563,13 @@ _allocate_swapchain (GstVulkanSwapper * swapper, GstCaps * caps, n_images_wanted = swapper->surf_props.maxImageCount; } - if (swapper->surf_props. - supportedTransforms & VK_SURFACE_TRANSFORM_NONE_BIT_KHR) { + 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); @@ -555,8 +583,8 @@ _allocate_swapchain (GstVulkanSwapper * swapper, GstCaps * caps, "Incorrect usage flags available for the swap images"); return FALSE; } - if ((swapper-> - surf_props.supportedUsageFlags & VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT) + if ((swapper->surf_props. + supportedUsageFlags & VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT) != 0) { usage |= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; } else { @@ -887,6 +915,12 @@ gst_vulkan_swapper_render_buffer (GstVulkanSwapper * swapper, guint32 swap_idx; VkResult err; + if (g_atomic_int_get (&swapper->to_quit)) { + g_set_error (error, GST_VULKAN_ERROR, GST_VULKAN_ERROR_DEVICE_LOST, + "Output window was closed"); + return FALSE; + } + reacquire: err = vkCreateSemaphore (swapper->device->device, &semaphore_info, &semaphore); @@ -898,6 +932,8 @@ reacquire: 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) { + GST_DEBUG_OBJECT (swapper, "out of date frame acquired"); + vkDestroySemaphore (swapper->device->device, semaphore); if (!_swapchain_resize (swapper, error)) return FALSE; @@ -920,11 +956,24 @@ reacquire: err = swapper->QueuePresentKHR (swapper->queue->queue, &present); if (err == VK_ERROR_OUT_OF_DATE_KHR) { + GST_DEBUG_OBJECT (swapper, "out of date frame submitted"); + vkDestroySemaphore (swapper->device->device, semaphore); + if (!_swapchain_resize (swapper, error)) return FALSE; - /* FIXME: correct? */ - return TRUE; + + if (cmd_data.cmd) + vkDestroyCommandBuffer (swapper->device->device, cmd_data.cmd); + cmd_data.cmd = NULL; + if (cmd_data.fence.handle) + vkDestroyFence (swapper->device->device, cmd_data.fence); + cmd_data.fence.handle = 0; + if (cmd_data.notify) + cmd_data.notify (cmd_data.data); + cmd_data.notify = NULL; + + goto reacquire; } else if (gst_vulkan_error_to_g_error (err, error, "vkQueuePresentKHR") < 0) goto error; diff --git a/ext/vulkan/vkswapper.h b/ext/vulkan/vkswapper.h index 9737ccd..e090b4b 100644 --- a/ext/vulkan/vkswapper.h +++ b/ext/vulkan/vkswapper.h @@ -67,6 +67,13 @@ struct _GstVulkanSwapper PFN_vkGetSwapchainImagesKHR GetSwapchainImagesKHR; PFN_vkAcquireNextImageKHR AcquireNextImageKHR; PFN_vkQueuePresentKHR QueuePresentKHR; + + /* */ + /* runtime variables */ + gint to_quit; + + /* signal handlers */ + gulong close_id; }; struct _GstVulkanSwapperClass diff --git a/ext/vulkan/vkwindow.c b/ext/vulkan/vkwindow.c index 1b8dddb..d62e79d 100644 --- a/ext/vulkan/vkwindow.c +++ b/ext/vulkan/vkwindow.c @@ -78,10 +78,23 @@ GstVulkanDummyWindow *gst_vulkan_dummy_window_new (void); enum { SIGNAL_0, + SIGNAL_CLOSE, LAST_SIGNAL }; -/* static guint gst_vulkan_window_signals[LAST_SIGNAL] = { 0 }; */ +static guint gst_vulkan_window_signals[LAST_SIGNAL] = { 0 }; + +static gboolean +_accum_logical_and (GSignalInvocationHint * ihint, GValue * return_accu, + const GValue * handler_return, gpointer data) +{ + gboolean val = g_value_get_boolean (handler_return); + gboolean val2 = g_value_get_boolean (return_accu); + + g_value_set_boolean (return_accu, val && val2); + + return TRUE; +} GQuark gst_vulkan_window_error_quark (void) @@ -115,6 +128,9 @@ _init_debug (void) static void gst_vulkan_window_init (GstVulkanWindow * window) { + window->priv = + G_TYPE_INSTANCE_GET_PRIVATE (window, GST_TYPE_VULKAN_WINDOW, + GstVulkanWindowPrivate); } static void @@ -125,6 +141,10 @@ gst_vulkan_window_class_init (GstVulkanWindowClass * klass) klass->open = GST_DEBUG_FUNCPTR (gst_vulkan_window_default_open); klass->close = GST_DEBUG_FUNCPTR (gst_vulkan_window_default_close); + gst_vulkan_window_signals[SIGNAL_CLOSE] = + g_signal_new ("close", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, 0, + (GSignalAccumulator) _accum_logical_and, NULL, NULL, G_TYPE_BOOLEAN, 0); + G_OBJECT_CLASS (klass)->finalize = gst_vulkan_window_finalize; _init_debug (); @@ -136,7 +156,7 @@ gst_vulkan_window_class_init (GstVulkanWindowClass * klass) * * Returns: (transfer full): a new #GstVulkanWindow using @display's connection * - * Since: 1.4 + * Since: 1.10 */ GstVulkanWindow * gst_vulkan_window_new (GstVulkanDisplay * display) @@ -217,12 +237,27 @@ void gst_vulkan_window_close (GstVulkanWindow * window) { GstVulkanWindowClass *klass; + gboolean to_close; 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); + g_signal_emit (window, gst_vulkan_window_signals[SIGNAL_CLOSE], 0, &to_close); + + if (to_close) + klass->close (window); +} + +void +gst_vulkan_window_resize (GstVulkanWindow * window, gint width, gint height) +{ + g_return_if_fail (GST_IS_VULKAN_WINDOW (window)); + + window->priv->surface_width = width; + window->priv->surface_height = height; + + /* XXX: possibly queue a resize/redraw */ } GType gst_vulkan_dummy_window_get_type (void); diff --git a/ext/vulkan/vkwindow.h b/ext/vulkan/vkwindow.h index 2d8b894..65eb40b 100644 --- a/ext/vulkan/vkwindow.h +++ b/ext/vulkan/vkwindow.h @@ -95,6 +95,8 @@ gpointer gst_vulkan_window_get_platform_handle (GstVulkanWindow *wi gboolean gst_vulkan_window_open (GstVulkanWindow * window, GError ** error); void gst_vulkan_window_close (GstVulkanWindow * window); +void gst_vulkan_window_resize (GstVulkanWindow * window, gint width, gint height); + G_END_DECLS #endif /* __GST_VULKAN_WINDOW_H__ */ diff --git a/ext/vulkan/xcb/vkdisplay_xcb.c b/ext/vulkan/xcb/vkdisplay_xcb.c index 568cf3d..c1d761c 100644 --- a/ext/vulkan/xcb/vkdisplay_xcb.c +++ b/ext/vulkan/xcb/vkdisplay_xcb.c @@ -61,17 +61,11 @@ gst_vulkan_display_xcb_finalize (GObject * object) { GstVulkanDisplayXCB *display_xcb = GST_VULKAN_DISPLAY_XCB (object); + G_OBJECT_CLASS (gst_vulkan_display_xcb_parent_class)->finalize (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 * @@ -106,25 +100,23 @@ gst_vulkan_display_xcb_new (const gchar * name) 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); + 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); + GST_ERROR ("Failed to open XCB display connection with name, \'%s\'", name); 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); + ret = gst_vulkan_display_xcb_new_with_connection (connection, screen_no); + GST_VULKAN_DISPLAY (ret)->event_source = xcb_event_source_new (ret); + g_source_attach (GST_VULKAN_DISPLAY (ret)->event_source, + GST_VULKAN_DISPLAY (ret)->main_context); + ret->foreign_display = FALSE; return ret; } /** - * gst_vulkan_display_xcb_new_with_display: + * gst_vulkan_display_xcb_new_with_connection: * @display: an existing, xcb display * * Creates a new display connection from a XCB Display. diff --git a/ext/vulkan/xcb/vkwindow_xcb.c b/ext/vulkan/xcb/vkwindow_xcb.c index 4ce5102..9576a7e 100644 --- a/ext/vulkan/xcb/vkwindow_xcb.c +++ b/ext/vulkan/xcb/vkwindow_xcb.c @@ -166,7 +166,9 @@ gst_vulkan_window_xcb_create_window (GstVulkanWindowXCB * window_xcb) 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; + value_list[1] = + XCB_EVENT_MASK_KEY_RELEASE | XCB_EVENT_MASK_EXPOSURE | + XCB_EVENT_MASK_STRUCTURE_NOTIFY; xcb_create_window (connection, XCB_COPY_FROM_PARENT, window_xcb->win_id, root_window, x, y, width, height, 0, XCB_WINDOW_CLASS_INPUT_OUTPUT, diff --git a/ext/vulkan/xcb/xcb_event_source.c b/ext/vulkan/xcb/xcb_event_source.c index f848841..b11bc12 100644 --- a/ext/vulkan/xcb/xcb_event_source.c +++ b/ext/vulkan/xcb/xcb_event_source.c @@ -82,29 +82,38 @@ _xcb_handle_event (GstVulkanDisplayXCB * display_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); + + if (window_xcb) { + GST_INFO_OBJECT (window_xcb, "Close requested"); + + gst_vulkan_window_close (GST_VULKAN_WINDOW (window_xcb)); + gst_vulkan_display_remove_window (GST_VULKAN_DISPLAY (display_xcb), + GST_VULKAN_WINDOW (window_xcb)); + 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: + case XCB_CONFIGURE_NOTIFY:{ + xcb_configure_notify_event_t *configure_event; + GstVulkanWindowXCB *window_xcb; + + configure_event = (xcb_configure_notify_event_t *) event; + window_xcb = + _find_window_from_xcb_window (display_xcb, configure_event->window); + + if (window_xcb) { + gst_vulkan_window_resize (GST_VULKAN_WINDOW (window_xcb), + configure_event->width, configure_event->height); + + gst_object_unref (window_xcb); + } break; + } +#if 0 case Expose: /* non-zero means that other Expose follows * so just wait for the last one @@ -180,6 +189,8 @@ _xcb_handle_event (GstVulkanDisplayXCB * display_xcb) GST_DEBUG ("unhandled XCB type: %u", event_code); break; } + + g_free (event); } return ret; -- 2.7.4