[gnome-photos/wip/rishi/edit-mode: 17/26] base-item: Split the loading and editing graphs
- From: Debarshi Ray <debarshir src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-photos/wip/rishi/edit-mode: 17/26] base-item: Split the loading and editing graphs
- Date: Mon, 1 Jun 2015 07:12:38 +0000 (UTC)
commit 76d0b1b0d63f0f1ebc87acf03880d4ec1c365350
Author: Debarshi Ray <debarshir gnome org>
Date: Tue May 26 10:49:54 2015 +0200
base-item: Split the loading and editing graphs
Adding and removing operations to the graph in one thread and then
processing it in another is causing intermittent crashes due to what
looks like threading and/or memory issues. However, we can not move
the entire processing to the main thread either. Loading big, high-res
image files in the main thread will badly freeze the UI.
Therefore, we use a small graph (gegl:load -> gegl:buffer-sink) that
we process in a separate thread to load the image into a GeglBuffer.
Then we use the GeglBuffer to drive a separate graph with the editing
operations (gegl:buffer-source -> PhotosPipeline). This graph is
processed in an idle handler in the main thread. This gives us both
asynchronous loading of data, and the ability to reliably tinker with
the editing graph.
Using a GeglBuffer in this way to hold the two graphs together seems
like a good solution to our problem. GeglBuffers have received enough
beating to ensure that they can be used from different threads.
In case you are wondering, the GIMP uses separate plug-in processes to
load the images, which are then fed to the "core" using pipes. That is
why the issue of asynchronous loading doesn't arise there.
src/photos-base-item.c | 187 +++++++++++++++++++++++++++++++++++------------
1 files changed, 139 insertions(+), 48 deletions(-)
---
diff --git a/src/photos-base-item.c b/src/photos-base-item.c
index 482d239..df67f6f 100644
--- a/src/photos-base-item.c
+++ b/src/photos-base-item.c
@@ -57,8 +57,12 @@ struct _PhotosBaseItemPrivate
{
cairo_surface_t *surface;
GdkPixbuf *original_icon;
- GeglNode *graph;
+ GeglNode *buffer_sink;
+ GeglNode *buffer_source;
+ GeglNode *edit_graph;
+ GeglNode *load_graph;
GeglNode *load;
+ GeglProcessor *processor;
GMutex mutex_download;
GMutex mutex;
GQuark equipment;
@@ -727,12 +731,11 @@ photos_base_item_file_query_info (GObject *source_object, GAsyncResult *res, gpo
}
-static GeglNode *
-photos_base_item_load (PhotosBaseItem *self, GCancellable *cancellable, GError **error)
+static GeglBuffer *
+photos_base_item_load_buffer (PhotosBaseItem *self, GCancellable *cancellable, GError **error)
{
PhotosBaseItemPrivate *priv = self->priv;
- GeglNode *output;
- GeglNode *ret_val = NULL;
+ GeglBuffer *ret_val = NULL;
gchar *path = NULL;
path = photos_base_item_download (self, cancellable, error);
@@ -740,10 +743,8 @@ photos_base_item_load (PhotosBaseItem *self, GCancellable *cancellable, GError *
goto out;
gegl_node_set (priv->load, "path", path, NULL);
- output = photos_pipeline_get_output (priv->pipeline);
- gegl_node_process (output);
-
- ret_val = g_object_ref (output);
+ gegl_node_set (priv->buffer_sink, "buffer", &ret_val, NULL);
+ gegl_node_process (priv->buffer_sink);
out:
g_free (path);
@@ -752,26 +753,26 @@ photos_base_item_load (PhotosBaseItem *self, GCancellable *cancellable, GError *
static void
-photos_base_item_load_in_thread_func (GTask *task,
- gpointer source_object,
- gpointer task_data,
- GCancellable *cancellable)
+photos_base_item_load_buffer_in_thread_func (GTask *task,
+ gpointer source_object,
+ gpointer task_data,
+ GCancellable *cancellable)
{
PhotosBaseItem *self = PHOTOS_BASE_ITEM (source_object);
PhotosBaseItemPrivate *priv = self->priv;
- GeglNode *node;
+ GeglBuffer *buffer;
GError *error = NULL;
g_mutex_lock (&priv->mutex);
- node = photos_base_item_load (self, cancellable, &error);
+ buffer = photos_base_item_load_buffer (self, cancellable, &error);
if (error != NULL)
{
g_task_return_error (task, error);
goto out;
}
- g_task_return_pointer (task, node, g_object_unref);
+ g_task_return_pointer (task, buffer, g_object_unref);
out:
g_mutex_unlock (&priv->mutex);
@@ -779,39 +780,141 @@ photos_base_item_load_in_thread_func (GTask *task,
static void
-photos_base_item_process (PhotosBaseItem *self, GCancellable *cancellable, GError **error)
+photos_base_item_load_buffer_async (PhotosBaseItem *self,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
{
PhotosBaseItemPrivate *priv = self->priv;
- GeglNode *output;
+ GTask *task;
- output = photos_pipeline_get_output (priv->pipeline);
- gegl_node_process (output);
+ g_return_if_fail (PHOTOS_IS_BASE_ITEM (self));
+
+ if (priv->load_graph == NULL)
+ {
+ priv->load_graph = gegl_node_new ();
+ priv->load = gegl_node_new_child (priv->load_graph, "operation", "gegl:load", NULL);
+ priv->buffer_sink = gegl_node_new_child (priv->load_graph, "operation", "gegl:buffer-sink", NULL);
+ gegl_node_link (priv->load, priv->buffer_sink);
+
+ }
+
+ if (priv->edit_graph == NULL)
+ {
+ GeglNode *graph;
+
+ priv->edit_graph = gegl_node_new ();
+ priv->buffer_source = gegl_node_new_child (priv->edit_graph, "operation", "gegl:buffer-source", NULL);
+ priv->pipeline = photos_pipeline_new (priv->edit_graph);
+ graph = photos_pipeline_get_graph (priv->pipeline);
+ gegl_node_link (priv->buffer_source, graph);
+ }
+
+ task = g_task_new (self, cancellable, callback, user_data);
+ g_task_set_check_cancellable (task, TRUE);
+ g_task_set_source_tag (task, photos_base_item_load_buffer_async);
+
+ g_task_run_in_thread (task, photos_base_item_load_buffer_in_thread_func);
+ g_object_unref (task);
+}
+
+
+static GeglBuffer *
+photos_base_item_load_buffer_finish (PhotosBaseItem *self, GAsyncResult *res, GError **error)
+{
+ GTask *task = G_TASK (res);
+
+ g_return_val_if_fail (g_task_is_valid (res, self), NULL);
+ g_return_val_if_fail (g_task_get_source_tag (task) == photos_base_item_load_buffer_async, NULL);
+ g_return_val_if_fail (error == NULL || *error == NULL, NULL);
+
+ return g_task_propagate_pointer (task, error);
}
static void
-photos_base_item_process_in_thread_func (GTask *task,
- gpointer source_object,
- gpointer task_data,
- GCancellable *cancellable)
+photos_base_item_load_process (GObject *source_object, GAsyncResult *res, gpointer user_data)
{
- PhotosBaseItem *self = PHOTOS_BASE_ITEM (source_object);
- PhotosBaseItemPrivate *priv = self->priv;
- GError *error = NULL;
+ GTask *task = G_TASK (user_data);
+ PhotosBaseItem *self;
+ GeglNode *graph;
+ GError *error;
- g_mutex_lock (&priv->mutex);
+ self = PHOTOS_BASE_ITEM (g_task_get_source_object (task));
- photos_base_item_process (self, cancellable, &error);
+ error = NULL;
+ photos_base_item_process_finish (self, res, &error);
if (error != NULL)
{
g_task_return_error (task, error);
goto out;
}
- g_task_return_pointer (task, NULL, NULL);
+ graph = photos_pipeline_get_graph (self->priv->pipeline);
+ g_task_return_pointer (task, g_object_ref (graph), g_object_unref);
out:
- g_mutex_unlock (&priv->mutex);
+ g_object_unref (task);
+}
+
+
+static void
+photos_base_item_load_load_buffer (GObject *source_object, GAsyncResult *res, gpointer user_data)
+{
+ GTask *task = G_TASK (user_data);
+ PhotosBaseItem *self;
+ PhotosBaseItemPrivate *priv;
+ GCancellable *cancellable;
+ GeglBuffer *buffer = NULL;
+ GeglNode *output;
+ GError *error;
+
+ self = PHOTOS_BASE_ITEM (g_task_get_source_object (task));
+ priv = self->priv;
+
+ cancellable = g_task_get_cancellable (task);
+
+ error = NULL;
+ buffer = photos_base_item_load_buffer_finish (self, res, &error);
+ if (error != NULL)
+ {
+ g_task_return_error (task, error);
+ goto out;
+ }
+
+ gegl_node_set (priv->buffer_source, "buffer", buffer, NULL);
+
+ g_clear_object (&priv->processor);
+ output = photos_pipeline_get_output (priv->pipeline);
+ priv->processor = gegl_node_new_processor (output, NULL);
+
+ photos_base_item_process_async (self, cancellable, photos_base_item_load_process, g_object_ref (task));
+
+ out:
+ g_clear_object (&buffer);
+ g_object_unref (task);
+}
+
+
+static gboolean
+photos_base_item_process_idle (gpointer user_data)
+{
+ GTask *task = G_TASK (user_data);
+ PhotosBaseItem *self;
+ GCancellable *cancellable;
+
+ self = PHOTOS_BASE_ITEM (g_task_get_source_object (task));
+ cancellable = g_task_get_cancellable (task);
+
+ if (g_cancellable_is_cancelled (cancellable))
+ goto done;
+
+ if (gegl_processor_work (self->priv->processor, NULL))
+ return G_SOURCE_CONTINUE;
+
+ done:
+ g_task_return_pointer (task, NULL, NULL);
+ return G_SOURCE_REMOVE;
}
@@ -1005,7 +1108,9 @@ photos_base_item_dispose (GObject *object)
PhotosBaseItemPrivate *priv = self->priv;
g_clear_pointer (&priv->surface, (GDestroyNotify) cairo_surface_destroy);
- g_clear_object (&priv->graph);
+ g_clear_object (&priv->edit_graph);
+ g_clear_object (&priv->load_graph);
+ g_clear_object (&priv->processor);
g_clear_object (&priv->original_icon);
g_clear_object (&priv->watcher);
g_clear_object (&priv->pipeline);
@@ -1420,29 +1525,15 @@ photos_base_item_load_async (PhotosBaseItem *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
- PhotosBaseItemPrivate *priv = self->priv;
GTask *task;
g_return_if_fail (PHOTOS_IS_BASE_ITEM (self));
- if (priv->graph == NULL)
- {
- GeglNode *graph;
-
- priv->graph = gegl_node_new ();
- priv->load = gegl_node_new_child (priv->graph, "operation", "gegl:load", NULL);
-
- priv->pipeline = photos_pipeline_new (priv->graph);
- graph = photos_pipeline_get_graph (priv->pipeline);
-
- gegl_node_link_many (priv->load, graph, NULL);
- }
-
task = g_task_new (self, cancellable, callback, user_data);
g_task_set_check_cancellable (task, TRUE);
g_task_set_source_tag (task, photos_base_item_load_async);
- g_task_run_in_thread (task, photos_base_item_load_in_thread_func);
+ photos_base_item_load_buffer_async (self, cancellable, photos_base_item_load_load_buffer, g_object_ref
(task));
g_object_unref (task);
}
@@ -1506,7 +1597,7 @@ photos_base_item_process_async (PhotosBaseItem *self,
g_task_set_check_cancellable (task, TRUE);
g_task_set_source_tag (task, photos_base_item_process_async);
- g_task_run_in_thread (task, photos_base_item_process_in_thread_func);
+ g_idle_add_full (G_PRIORITY_DEFAULT_IDLE, photos_base_item_process_idle, g_object_ref (task),
g_object_unref);
g_object_unref (task);
}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]