[gegl] gegl: keep track of valid region for mipmap levels



commit 3a2d6fcba89c7f91b1155358ad8db1414e6fdc1d
Author: Øyvind Kolås <pippin gimp org>
Date:   Wed Jul 9 00:56:53 2014 +0200

    gegl: keep track of valid region for mipmap levels

 gegl/buffer/gegl-cache.c                  |   40 +++++++++++----
 gegl/buffer/gegl-cache.h                  |    7 ++-
 gegl/graph/gegl-node.c                    |    8 ++--
 gegl/process/gegl-eval-manager.c          |    2 +-
 gegl/process/gegl-graph-traversal-debug.c |    2 +-
 gegl/process/gegl-graph-traversal.c       |   80 ++++++++++++++++------------
 gegl/process/gegl-graph-traversal.h       |    3 +-
 gegl/process/gegl-processor.c             |   24 ++++++---
 8 files changed, 106 insertions(+), 60 deletions(-)
---
diff --git a/gegl/buffer/gegl-cache.c b/gegl/buffer/gegl-cache.c
index ed131d2..79ce88b 100644
--- a/gegl/buffer/gegl-cache.c
+++ b/gegl/buffer/gegl-cache.c
@@ -66,10 +66,12 @@ static void
 gegl_cache_constructed (GObject *object)
 {
   GeglCache *self = GEGL_CACHE (object);
+  gint i;
 
   G_OBJECT_CLASS (gegl_cache_parent_class)->constructed (object);
 
-  self->valid_region = gegl_region_new ();
+  for (i = 0; i < GEGL_CACHE_VALID_MIPMAPS; i++)
+    self->valid_region[i] = gegl_region_new ();
 }
 
 /* expand invalidated regions to be align with coordinates divisible by 8 in both
@@ -185,10 +187,12 @@ static void
 finalize (GObject *gobject)
 {
   GeglCache *self = GEGL_CACHE (gobject);
+  gint i;
 
   g_mutex_clear (&self->mutex);
-  if (self->valid_region)
-    gegl_region_destroy (self->valid_region);
+  for (i = 0; i < GEGL_CACHE_VALID_MIPMAPS; i++)
+    if (self->valid_region[i])
+      gegl_region_destroy (self->valid_region[i]);
   G_OBJECT_CLASS (gegl_cache_parent_class)->finalize (gobject);
 }
 
@@ -257,6 +261,7 @@ void
 gegl_cache_invalidate (GeglCache           *self,
                        const GeglRectangle *roi)
 {
+  gint i;
   g_mutex_lock (&self->mutex);
 
   if (roi)
@@ -265,7 +270,8 @@ gegl_cache_invalidate (GeglCache           *self,
 
       GeglRegion *temp_region;
       temp_region = gegl_region_rectangle (&expanded);
-      gegl_region_subtract (self->valid_region, temp_region);
+      for (i = 0; i < GEGL_CACHE_VALID_MIPMAPS; i++)
+        gegl_region_subtract (self->valid_region[i], temp_region);
       gegl_region_destroy (temp_region);
       g_signal_emit (self, gegl_cache_signals[INVALIDATED], 0,
                      roi, NULL);
@@ -273,9 +279,12 @@ gegl_cache_invalidate (GeglCache           *self,
   else
     {
       GeglRectangle rect = { 0, 0, 0, 0 }; /* should probably be the extent of the cache */
-      if (self->valid_region)
-        gegl_region_destroy (self->valid_region);
-      self->valid_region = gegl_region_new ();
+      for (i = 0; i < GEGL_CACHE_VALID_MIPMAPS; i++)
+      {
+        if (self->valid_region[i])
+          gegl_region_destroy (self->valid_region[i]);
+        self->valid_region[i] = gegl_region_new ();
+      }
       g_signal_emit (self, gegl_cache_signals[INVALIDATED], 0,
                      &rect, NULL);
     }
@@ -284,13 +293,17 @@ gegl_cache_invalidate (GeglCache           *self,
 
 void
 gegl_cache_computed (GeglCache           *self,
-                     const GeglRectangle *rect)
+                     const GeglRectangle *rect,
+                     gint                 level)
 {
   g_return_if_fail (GEGL_IS_CACHE (self));
   g_return_if_fail (rect != NULL);
 
   g_mutex_lock (&self->mutex);
-  gegl_region_union_with_rect (self->valid_region, rect);
+
+  if (level <= GEGL_CACHE_VALID_MIPMAPS)
+    gegl_region_union_with_rect (self->valid_region[level], rect);
+
   g_signal_emit (self, gegl_cache_signals[COMPUTED], 0, rect, NULL);
   g_mutex_unlock (&self->mutex);
 }
@@ -306,10 +319,17 @@ gegl_buffer_list_valid_rectangles (GeglBuffer     *buffer,
                                    gint           *n_rectangles)
 {
   GeglCache *cache;
+  gint level = 0; /* should be an argument */
   g_return_val_if_fail (GEGL_IS_CACHE (buffer), FALSE);
   cache = GEGL_CACHE (buffer);
 
-  gegl_region_get_rectangles (cache->valid_region, rectangles, n_rectangles);
+  if (level < 0)
+    level = 0;
+  if (level >= GEGL_CACHE_VALID_MIPMAPS)
+    level = GEGL_CACHE_VALID_MIPMAPS-1;
+
+  gegl_region_get_rectangles (cache->valid_region[level],
+                              rectangles, n_rectangles);
 
   return TRUE;
 }
diff --git a/gegl/buffer/gegl-cache.h b/gegl/buffer/gegl-cache.h
index 38f1a50..d56b059 100644
--- a/gegl/buffer/gegl-cache.h
+++ b/gegl/buffer/gegl-cache.h
@@ -33,11 +33,13 @@ G_BEGIN_DECLS
 
 typedef struct _GeglCacheClass GeglCacheClass;
 
+#define GEGL_CACHE_VALID_MIPMAPS 8
+
 struct _GeglCache
 {
   GeglBuffer    parent_instance;
 
-  GeglRegion   *valid_region;
+  GeglRegion   *valid_region[GEGL_CACHE_VALID_MIPMAPS];
   GMutex        mutex;
 };
 
@@ -50,7 +52,8 @@ GType    gegl_cache_get_type    (void) G_GNUC_CONST;
 void     gegl_cache_invalidate  (GeglCache           *self,
                                  const GeglRectangle *roi);
 void     gegl_cache_computed    (GeglCache           *self,
-                                 const GeglRectangle *rect);
+                                 const GeglRectangle *rect,
+                                 gint                 level);
 
 G_END_DECLS
 
diff --git a/gegl/graph/gegl-node.c b/gegl/graph/gegl-node.c
index 05381d7..8fc8edc 100644
--- a/gegl/graph/gegl-node.c
+++ b/gegl/graph/gegl-node.c
@@ -1037,15 +1037,15 @@ gegl_node_blit (GeglNode            *self,
           if (scale != 1.0)
             {
               const GeglRectangle unscaled_roi = _gegl_get_required_for_scale (format, roi, scale);
+              gint  level = gegl_mipmap_rendering_enabled()?gegl_level_from_scale (scale):0;
 
-              gegl_node_blit_buffer2 (self, buffer, &unscaled_roi,
-                gegl_mipmap_rendering_enabled()?gegl_level_from_scale (scale):0);
-              gegl_cache_computed (cache, &unscaled_roi);
+              gegl_node_blit_buffer2 (self, buffer, &unscaled_roi, level);
+              gegl_cache_computed (cache, &unscaled_roi, level);
             }
           else
             {
               gegl_node_blit_buffer (self, buffer, roi);
-              gegl_cache_computed (cache, roi);
+              gegl_cache_computed (cache, roi, 0);
             }
         }
 
diff --git a/gegl/process/gegl-eval-manager.c b/gegl/process/gegl-eval-manager.c
index 48ceaf0..9cd4afc 100644
--- a/gegl/process/gegl-eval-manager.c
+++ b/gegl/process/gegl-eval-manager.c
@@ -127,7 +127,7 @@ gegl_eval_manager_apply (GeglEvalManager     *self,
   GEGL_INSTRUMENT_END ("gegl", "prepare-graph");
 
   GEGL_INSTRUMENT_START();
-  gegl_graph_prepare_request (self->traversal, roi);
+  gegl_graph_prepare_request (self->traversal, roi, level);
   GEGL_INSTRUMENT_END ("gegl", "prepare-request");
 
   GEGL_INSTRUMENT_START();
diff --git a/gegl/process/gegl-graph-traversal-debug.c b/gegl/process/gegl-graph-traversal-debug.c
index 0b08346..7331b68 100644
--- a/gegl/process/gegl-graph-traversal-debug.c
+++ b/gegl/process/gegl-graph-traversal-debug.c
@@ -76,7 +76,7 @@ gegl_graph_dump_request (GeglNode            *node,
   GList              *list_iter = NULL;
 
   gegl_graph_prepare (path);
-  gegl_graph_prepare_request (path, roi);
+  gegl_graph_prepare_request (path, roi, 0);
 
   for (list_iter = path->dfs_path; list_iter; list_iter = list_iter->next)
   {
diff --git a/gegl/process/gegl-graph-traversal.c b/gegl/process/gegl-graph-traversal.c
index abe5bf7..eff8bc4 100644
--- a/gegl/process/gegl-graph-traversal.c
+++ b/gegl/process/gegl-graph-traversal.c
@@ -203,9 +203,11 @@ gegl_graph_prepare (GeglGraphTraversal *path)
  * the area that needs to be rendered from each node in the
  * graph to fulfill this request.
  */
+
 void
 gegl_graph_prepare_request (GeglGraphTraversal  *path,
-                            const GeglRectangle *request_roi)
+                            const GeglRectangle *request_roi,
+                            gint                 level)
 {
   GList *list_iter = NULL;
   static const GeglRectangle empty_rect = {0, 0, 0, 0};
@@ -261,49 +263,59 @@ gegl_graph_prepare_request (GeglGraphTraversal  *path,
       if (request->width == 0 || request->height == 0)
         {
           gegl_operation_context_set_result_rect (context, &empty_rect);
+          continue;
         }
-      else if (node->cache &&
-               gegl_region_rect_in (node->cache->valid_region, request) == GEGL_OVERLAP_RECTANGLE_IN)
+      
+      if (node->cache)
         {
-          /* This node is cached and the cache fulfills our need rect */
-          context->cached = TRUE;
-          gegl_operation_context_set_result_rect (context, &empty_rect);
+          gint i;
+          for (i = level; i >=0 && !context->cached; i--)
+          {
+            if (gegl_region_rect_in (node->cache->valid_region[level], request) == GEGL_OVERLAP_RECTANGLE_IN)
+            {
+              /* This node is cached and the cache fulfills our need rect */
+              context->cached = TRUE;
+              gegl_operation_context_set_result_rect (context, &empty_rect);
+            }
+          }
+          if (context->cached)
+            continue;
         }
-      else
-        {
-          /* Expand request if the operation has a minimum processing requirement */
-          GeglRectangle full_request = gegl_operation_get_cached_region (operation, request);
 
-          gegl_operation_context_set_need_rect (context, &full_request);
+      {
+        /* Expand request if the operation has a minimum processing requirement */
+        GeglRectangle full_request = gegl_operation_get_cached_region (operation, request);
 
-          /* FIXME: We could trim this down based on the cache, instead of being all or nothing */
-          gegl_operation_context_set_result_rect (context, request);
+        gegl_operation_context_set_need_rect (context, &full_request);
 
-          for (input_pads = node->input_pads; input_pads; input_pads = input_pads->next)
-            {
-              GeglPad *source_pad = gegl_pad_get_connected_to (input_pads->data);
+        /* FIXME: We could trim this down based on the cache, instead of being all or nothing */
+        gegl_operation_context_set_result_rect (context, request);
 
-              if (source_pad)
-                {
-                  GeglNode             *source_node    = gegl_pad_get_node (source_pad);
-                  GeglOperationContext *source_context = g_hash_table_lookup (path->contexts, source_node);
-                  const gchar          *pad_name       = gegl_pad_get_name (input_pads->data);
+        for (input_pads = node->input_pads; input_pads; input_pads = input_pads->next)
+          {
+            GeglPad *source_pad = gegl_pad_get_connected_to (input_pads->data);
 
-                  GeglRectangle rect, current_need, new_need;
+            if (source_pad)
+              {
+                GeglNode             *source_node    = gegl_pad_get_node (source_pad);
+                GeglOperationContext *source_context = g_hash_table_lookup (path->contexts, source_node);
+                const gchar          *pad_name       = gegl_pad_get_name (input_pads->data);
 
-                  /* Combine this need rect with any existing request */
-                  rect = gegl_operation_get_required_for_output (operation, pad_name, &full_request);
-                  current_need = *gegl_operation_context_get_need_rect (source_context);
+                GeglRectangle rect, current_need, new_need;
 
-                  gegl_rectangle_bounding_box (&new_need, &rect, &current_need);
+                /* Combine this need rect with any existing request */
+                rect = gegl_operation_get_required_for_output (operation, pad_name, &full_request);
+                current_need = *gegl_operation_context_get_need_rect (source_context);
 
-                  /* Limit request to the nodes output */
-                  gegl_rectangle_intersect (&new_need, &source_node->have_rect, &new_need);
+                gegl_rectangle_bounding_box (&new_need, &rect, &current_need);
 
-                  gegl_operation_context_set_need_rect (source_context, &new_need);
-                }
-            }
-        }
+                /* Limit request to the nodes output */
+                gegl_rectangle_intersect (&new_need, &source_node->have_rect, &new_need);
+
+                gegl_operation_context_set_need_rect (source_context, &new_need);
+              }
+          }
+      }
     }
 }
 
@@ -417,12 +429,12 @@ gegl_graph_process (GeglGraphTraversal *path,
                   gegl_operation_context_set_object (context, "input", G_OBJECT 
(gegl_graph_get_shared_empty(path)));
                 }
 
-              context->level = level; // XXX: get rid of context->level member?
+              context->level = level;
               gegl_operation_process (operation, context, "output", &context->need_rect, context->level);
               operation_result = GEGL_BUFFER (gegl_operation_context_get_object (context, "output"));
 
               if (operation_result && operation_result == (GeglBuffer *)operation->node->cache)
-                gegl_cache_computed (operation->node->cache, &context->need_rect);
+                gegl_cache_computed (operation->node->cache, &context->need_rect, level);
             }
         }
       else
diff --git a/gegl/process/gegl-graph-traversal.h b/gegl/process/gegl-graph-traversal.h
index 413b2a0..20cbbc3 100644
--- a/gegl/process/gegl-graph-traversal.h
+++ b/gegl/process/gegl-graph-traversal.h
@@ -27,7 +27,8 @@ void                gegl_graph_free             (GeglGraphTraversal  *path);
 
 void                gegl_graph_prepare          (GeglGraphTraversal  *path);
 void                gegl_graph_prepare_request  (GeglGraphTraversal  *path,
-                                                 const GeglRectangle *roi);
+                                                 const GeglRectangle *roi,
+                                                 gint                 level);
 GeglBuffer         *gegl_graph_process          (GeglGraphTraversal  *path,
                                                  gint                 level);
 
diff --git a/gegl/process/gegl-processor.c b/gegl/process/gegl-processor.c
index 6487d74..0f23bd9 100644
--- a/gegl/process/gegl-processor.c
+++ b/gegl/process/gegl-processor.c
@@ -483,10 +483,20 @@ render_rectangle (GeglProcessor *processor)
 
       if (buffered)
         {
-          /* only do work if the rectangle is not completely inside the valid
-           * region of the cache */
-          if (gegl_region_rect_in (cache->valid_region, dr) !=
-              GEGL_OVERLAP_RECTANGLE_IN)
+          gboolean found_full = FALSE;
+          for (gint level = processor->level; level >= 0; level--)
+          {
+            if (gegl_region_rect_in (cache->valid_region[level], dr) == GEGL_OVERLAP_RECTANGLE_IN)
+            {
+              found_full = TRUE;
+              break;
+            }
+            /* XXX: dr should be adjusted to be the bounding box of not-found
+             * in cache if there is partial hits
+             */
+          }
+
+          if (!found_full)
             {
               /* create a buffer and initialise it */
               guchar *buf;
@@ -505,7 +515,7 @@ render_rectangle (GeglProcessor *processor)
               gegl_buffer_set (GEGL_BUFFER (cache), dr, processor->level, format, buf, GEGL_AUTO_ROWSTRIDE);
 
               /* tells the cache that the rectangle (dr) has been computed */
-              gegl_cache_computed (cache, dr);
+              gegl_cache_computed (cache, dr, processor->level);
 
               /* release the buffer */
               g_free (buf);
@@ -593,7 +603,7 @@ gegl_processor_progress (GeglProcessor *processor)
     }
   else
     {
-      valid_region = gegl_node_get_cache (processor->input)->valid_region;
+      valid_region = gegl_node_get_cache (processor->input)->valid_region[processor->level];
     }
 
   wanted = rect_area (&(processor->rectangle));
@@ -633,7 +643,7 @@ gegl_processor_render (GeglProcessor *processor,
   else
     {
       g_return_val_if_fail (processor->input != NULL, FALSE);
-      valid_region = gegl_node_get_cache (processor->input)->valid_region;
+      valid_region = gegl_node_get_cache (processor->input)->valid_region[processor->level];
     }
 
   {


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]