[gegl] sampler: make size of cache in sampler base class adaptive



commit de26038e19883243fb465ddf290a05eb23d91bae
Author: Øyvind Kolås <pippin gimp org>
Date:   Mon Jun 16 04:09:55 2014 +0200

    sampler: make size of cache in sampler base class adaptive
    
    Implement an adaptive caching policy; that meets the needs of affine
    transformations. Running averages are kept of x/y deltas as well as the
    magnitudes of the deltas of coordinates leading to cache misses.
    
    The initial rectangle used is a small one; subsequent rectangles have extents
    up to 64 pixels wide/tall; when tracked deltas are more diagonal than axis
    aligned smaller more square regions are tracked.

 gegl/buffer/gegl-buffer.h          |   10 +
 gegl/buffer/gegl-sampler-cubic.c   |    8 +-
 gegl/buffer/gegl-sampler-linear.c  |    8 +-
 gegl/buffer/gegl-sampler-lohalo.c  |   89 +++++----
 gegl/buffer/gegl-sampler-nearest.c |    8 +-
 gegl/buffer/gegl-sampler-nohalo.c  |   89 +++++----
 gegl/buffer/gegl-sampler.c         |  356 +++++++++++++-----------------------
 gegl/buffer/gegl-sampler.h         |  167 ++++++++++-------
 perf/test-rotate.c                 |    2 +-
 9 files changed, 348 insertions(+), 389 deletions(-)
---
diff --git a/gegl/buffer/gegl-buffer.h b/gegl/buffer/gegl-buffer.h
index 5b7b982..e07e4f3 100644
--- a/gegl/buffer/gegl-buffer.h
+++ b/gegl/buffer/gegl-buffer.h
@@ -442,6 +442,16 @@ void            gegl_buffer_sample_cleanup    (GeglBuffer *buffer);
  */
 GeglSamplerType gegl_sampler_type_from_string (const gchar *string);
 
+typedef void (*GeglSamplerGetFun)  (GeglSampler     *self,
+                                    gdouble          x,
+                                    gdouble          y,
+                                    GeglMatrix2     *scale,
+                                    void            *output,
+                                    GeglAbyssPolicy  repeat_mode);
+
+GeglSamplerGetFun gegl_sampler_get_fun (GeglSampler *sampler);
+
+
 /**
  * gegl_buffer_sampler_new: (skip)
  * @buffer: buffer to create a new sampler for
diff --git a/gegl/buffer/gegl-sampler-cubic.c b/gegl/buffer/gegl-sampler-cubic.c
index 96824f3..156a327 100644
--- a/gegl/buffer/gegl-sampler-cubic.c
+++ b/gegl/buffer/gegl-sampler-cubic.c
@@ -113,10 +113,10 @@ gegl_sampler_cubic_init (GeglSamplerCubic *self)
    * right into left, and if the context_rect does not stretch far
    * enough on the left, pixel lookups will fail.
    */
-  GEGL_SAMPLER (self)->context_rect[0].x = -2;
-  GEGL_SAMPLER (self)->context_rect[0].y = -2;
-  GEGL_SAMPLER (self)->context_rect[0].width = 5;
-  GEGL_SAMPLER (self)->context_rect[0].height = 5;
+  GEGL_SAMPLER (self)->level[0].context_rect.x = -2;
+  GEGL_SAMPLER (self)->level[0].context_rect.y = -2;
+  GEGL_SAMPLER (self)->level[0].context_rect.width = 5;
+  GEGL_SAMPLER (self)->level[0].context_rect.height = 5;
   GEGL_SAMPLER (self)->interpolate_format = babl_format ("RaGaBaA float");
 
   self->b=1.0;
diff --git a/gegl/buffer/gegl-sampler-linear.c b/gegl/buffer/gegl-sampler-linear.c
index 8f415e7..92a881f 100644
--- a/gegl/buffer/gegl-sampler-linear.c
+++ b/gegl/buffer/gegl-sampler-linear.c
@@ -66,10 +66,10 @@ gegl_sampler_linear_class_init (GeglSamplerLinearClass *klass)
 static void
 gegl_sampler_linear_init (GeglSamplerLinear *self)
 {
-  GEGL_SAMPLER (self)->context_rect[0].x      = -1 -   LINEAR_EXTRA_ELBOW_ROOM;
-  GEGL_SAMPLER (self)->context_rect[0].y      = -1 -   LINEAR_EXTRA_ELBOW_ROOM;
-  GEGL_SAMPLER (self)->context_rect[0].width  =  3 + 2*LINEAR_EXTRA_ELBOW_ROOM;
-  GEGL_SAMPLER (self)->context_rect[0].height =  3 + 2*LINEAR_EXTRA_ELBOW_ROOM;
+  GEGL_SAMPLER (self)->level[0].context_rect.x      = -1 -   LINEAR_EXTRA_ELBOW_ROOM;
+  GEGL_SAMPLER (self)->level[0].context_rect.y      = -1 -   LINEAR_EXTRA_ELBOW_ROOM;
+  GEGL_SAMPLER (self)->level[0].context_rect.width  =  3 + 2*LINEAR_EXTRA_ELBOW_ROOM;
+  GEGL_SAMPLER (self)->level[0].context_rect.height =  3 + 2*LINEAR_EXTRA_ELBOW_ROOM;
   GEGL_SAMPLER (self)->interpolate_format = babl_format ("RaGaBaA float");
 }
 
diff --git a/gegl/buffer/gegl-sampler-lohalo.c b/gegl/buffer/gegl-sampler-lohalo.c
index 822c961..0acf80e 100644
--- a/gegl/buffer/gegl-sampler-lohalo.c
+++ b/gegl/buffer/gegl-sampler-lohalo.c
@@ -374,45 +374,56 @@ gegl_sampler_lohalo_class_init (GeglSamplerLohaloClass *klass)
 static void
 gegl_sampler_lohalo_init (GeglSamplerLohalo *self)
 {
-  GEGL_SAMPLER (self)->context_rect[0].x   = -LOHALO_OFFSET_0;
-  GEGL_SAMPLER (self)->context_rect[0].y   = -LOHALO_OFFSET_0;
-  GEGL_SAMPLER (self)->context_rect[0].width  = LOHALO_SIZE_0;
-  GEGL_SAMPLER (self)->context_rect[0].height = LOHALO_SIZE_0;
-
-  GEGL_SAMPLER (self)->context_rect[1].x   = -LOHALO_OFFSET_1;
-  GEGL_SAMPLER (self)->context_rect[1].y   = -LOHALO_OFFSET_1;
-  GEGL_SAMPLER (self)->context_rect[1].width  = LOHALO_SIZE_1;
-  GEGL_SAMPLER (self)->context_rect[1].height = LOHALO_SIZE_1;
-
-  GEGL_SAMPLER (self)->context_rect[2].x   = -LOHALO_OFFSET_2;
-  GEGL_SAMPLER (self)->context_rect[2].y   = -LOHALO_OFFSET_2;
-  GEGL_SAMPLER (self)->context_rect[2].width  = LOHALO_SIZE_2;
-  GEGL_SAMPLER (self)->context_rect[2].height = LOHALO_SIZE_2;
-
-  GEGL_SAMPLER (self)->context_rect[3].x   = -LOHALO_OFFSET_3;
-  GEGL_SAMPLER (self)->context_rect[3].y   = -LOHALO_OFFSET_3;
-  GEGL_SAMPLER (self)->context_rect[3].width  = LOHALO_SIZE_3;
-  GEGL_SAMPLER (self)->context_rect[3].height = LOHALO_SIZE_3;
-
-  GEGL_SAMPLER (self)->context_rect[4].x   = -LOHALO_OFFSET_4;
-  GEGL_SAMPLER (self)->context_rect[4].y   = -LOHALO_OFFSET_4;
-  GEGL_SAMPLER (self)->context_rect[4].width  = LOHALO_SIZE_4;
-  GEGL_SAMPLER (self)->context_rect[4].height = LOHALO_SIZE_4;
-
-  GEGL_SAMPLER (self)->context_rect[5].x   = -LOHALO_OFFSET_5;
-  GEGL_SAMPLER (self)->context_rect[5].y   = -LOHALO_OFFSET_5;
-  GEGL_SAMPLER (self)->context_rect[5].width  = LOHALO_SIZE_5;
-  GEGL_SAMPLER (self)->context_rect[5].height = LOHALO_SIZE_5;
-
-  GEGL_SAMPLER (self)->context_rect[6].x   = -LOHALO_OFFSET_6;
-  GEGL_SAMPLER (self)->context_rect[6].y   = -LOHALO_OFFSET_6;
-  GEGL_SAMPLER (self)->context_rect[6].width  = LOHALO_SIZE_6;
-  GEGL_SAMPLER (self)->context_rect[6].height = LOHALO_SIZE_6;
-
-  GEGL_SAMPLER (self)->context_rect[7].x   = -LOHALO_OFFSET_7;
-  GEGL_SAMPLER (self)->context_rect[7].y   = -LOHALO_OFFSET_7;
-  GEGL_SAMPLER (self)->context_rect[7].width  = LOHALO_SIZE_7;
-  GEGL_SAMPLER (self)->context_rect[7].height = LOHALO_SIZE_7;
+  GeglSampler *sampler = GEGL_SAMPLER (self);
+  GeglSamplerLevel *level;
+
+  level = &sampler->level[0];
+  level->context_rect.x   = -LOHALO_OFFSET_0;
+  level->context_rect.y   = -LOHALO_OFFSET_0;
+  level->context_rect.width  = LOHALO_SIZE_0;
+  level->context_rect.height = LOHALO_SIZE_0;
+
+  level = &sampler->level[1];
+  level->context_rect.x   = -LOHALO_OFFSET_1;
+  level->context_rect.y   = -LOHALO_OFFSET_1;
+  level->context_rect.width  = LOHALO_SIZE_1;
+  level->context_rect.height = LOHALO_SIZE_1;
+
+  level = &sampler->level[2];
+  level->context_rect.x   = -LOHALO_OFFSET_2;
+  level->context_rect.y   = -LOHALO_OFFSET_2;
+  level->context_rect.width  = LOHALO_SIZE_2;
+  level->context_rect.height = LOHALO_SIZE_2;
+
+  level = &sampler->level[3];
+  level->context_rect.x   = -LOHALO_OFFSET_3;
+  level->context_rect.y   = -LOHALO_OFFSET_3;
+  level->context_rect.width  = LOHALO_SIZE_3;
+  level->context_rect.height = LOHALO_SIZE_3;
+
+  level = &sampler->level[4];
+  level->context_rect.x   = -LOHALO_OFFSET_4;
+  level->context_rect.y   = -LOHALO_OFFSET_4;
+  level->context_rect.width  = LOHALO_SIZE_4;
+  level->context_rect.height = LOHALO_SIZE_4;
+
+  level = &sampler->level[5];
+  level->context_rect.x   = -LOHALO_OFFSET_5;
+  level->context_rect.y   = -LOHALO_OFFSET_5;
+  level->context_rect.width  = LOHALO_SIZE_5;
+  level->context_rect.height = LOHALO_SIZE_5;
+
+  level = &sampler->level[6];
+  level->context_rect.x   = -LOHALO_OFFSET_6;
+  level->context_rect.y   = -LOHALO_OFFSET_6;
+  level->context_rect.width  = LOHALO_SIZE_6;
+  level->context_rect.height = LOHALO_SIZE_6;
+
+  level = &sampler->level[7];
+  level->context_rect.x   = -LOHALO_OFFSET_7;
+  level->context_rect.y   = -LOHALO_OFFSET_7;
+  level->context_rect.width  = LOHALO_SIZE_7;
+  level->context_rect.height = LOHALO_SIZE_7;
 
   GEGL_SAMPLER (self)->interpolate_format = babl_format ("RaGaBaA float");
 }
diff --git a/gegl/buffer/gegl-sampler-nearest.c b/gegl/buffer/gegl-sampler-nearest.c
index 865cb88..e40a495 100644
--- a/gegl/buffer/gegl-sampler-nearest.c
+++ b/gegl/buffer/gegl-sampler-nearest.c
@@ -65,10 +65,10 @@ gegl_sampler_nearest_class_init (GeglSamplerNearestClass *klass)
 static void
 gegl_sampler_nearest_init (GeglSamplerNearest *self)
 {
-  GEGL_SAMPLER (self)->context_rect[0].x = 0;
-  GEGL_SAMPLER (self)->context_rect[0].y = 0;
-  GEGL_SAMPLER (self)->context_rect[0].width = 1;
-  GEGL_SAMPLER (self)->context_rect[0].height = 1;
+  GEGL_SAMPLER (self)->level[0].context_rect.x = 0;
+  GEGL_SAMPLER (self)->level[0].context_rect.y = 0;
+  GEGL_SAMPLER (self)->level[0].context_rect.width = 1;
+  GEGL_SAMPLER (self)->level[0].context_rect.height = 1;
   GEGL_SAMPLER (self)->interpolate_format = babl_format ("RaGaBaA float");
 }
 
diff --git a/gegl/buffer/gegl-sampler-nohalo.c b/gegl/buffer/gegl-sampler-nohalo.c
index 0c49c4d..eeef4af 100644
--- a/gegl/buffer/gegl-sampler-nohalo.c
+++ b/gegl/buffer/gegl-sampler-nohalo.c
@@ -457,45 +457,56 @@ gegl_sampler_nohalo_class_init (GeglSamplerNohaloClass *klass)
 static void
 gegl_sampler_nohalo_init (GeglSamplerNohalo *self)
 {
-  GEGL_SAMPLER (self)->context_rect[0].x   = -NOHALO_OFFSET_0;
-  GEGL_SAMPLER (self)->context_rect[0].y   = -NOHALO_OFFSET_0;
-  GEGL_SAMPLER (self)->context_rect[0].width  = NOHALO_SIZE_0;
-  GEGL_SAMPLER (self)->context_rect[0].height = NOHALO_SIZE_0;
-
-  GEGL_SAMPLER (self)->context_rect[1].x   = -NOHALO_OFFSET_1;
-  GEGL_SAMPLER (self)->context_rect[1].y   = -NOHALO_OFFSET_1;
-  GEGL_SAMPLER (self)->context_rect[1].width  = NOHALO_SIZE_1;
-  GEGL_SAMPLER (self)->context_rect[1].height = NOHALO_SIZE_1;
-
-  GEGL_SAMPLER (self)->context_rect[2].x   = -NOHALO_OFFSET_2;
-  GEGL_SAMPLER (self)->context_rect[2].y   = -NOHALO_OFFSET_2;
-  GEGL_SAMPLER (self)->context_rect[2].width  = NOHALO_SIZE_2;
-  GEGL_SAMPLER (self)->context_rect[2].height = NOHALO_SIZE_2;
-
-  GEGL_SAMPLER (self)->context_rect[3].x   = -NOHALO_OFFSET_3;
-  GEGL_SAMPLER (self)->context_rect[3].y   = -NOHALO_OFFSET_3;
-  GEGL_SAMPLER (self)->context_rect[3].width  = NOHALO_SIZE_3;
-  GEGL_SAMPLER (self)->context_rect[3].height = NOHALO_SIZE_3;
-
-  GEGL_SAMPLER (self)->context_rect[4].x   = -NOHALO_OFFSET_4;
-  GEGL_SAMPLER (self)->context_rect[4].y   = -NOHALO_OFFSET_4;
-  GEGL_SAMPLER (self)->context_rect[4].width  = NOHALO_SIZE_4;
-  GEGL_SAMPLER (self)->context_rect[4].height = NOHALO_SIZE_4;
-
-  GEGL_SAMPLER (self)->context_rect[5].x   = -NOHALO_OFFSET_5;
-  GEGL_SAMPLER (self)->context_rect[5].y   = -NOHALO_OFFSET_5;
-  GEGL_SAMPLER (self)->context_rect[5].width  = NOHALO_SIZE_5;
-  GEGL_SAMPLER (self)->context_rect[5].height = NOHALO_SIZE_5;
-
-  GEGL_SAMPLER (self)->context_rect[6].x   = -NOHALO_OFFSET_6;
-  GEGL_SAMPLER (self)->context_rect[6].y   = -NOHALO_OFFSET_6;
-  GEGL_SAMPLER (self)->context_rect[6].width  = NOHALO_SIZE_6;
-  GEGL_SAMPLER (self)->context_rect[6].height = NOHALO_SIZE_6;
-
-  GEGL_SAMPLER (self)->context_rect[7].x   = -NOHALO_OFFSET_7;
-  GEGL_SAMPLER (self)->context_rect[7].y   = -NOHALO_OFFSET_7;
-  GEGL_SAMPLER (self)->context_rect[7].width  = NOHALO_SIZE_7;
-  GEGL_SAMPLER (self)->context_rect[7].height = NOHALO_SIZE_7;
+  GeglSampler *sampler = GEGL_SAMPLER (self);
+  GeglSamplerLevel *level;
+
+  level = &sampler->level[0];
+  level->context_rect.x   = -NOHALO_OFFSET_0;
+  level->context_rect.y   = -NOHALO_OFFSET_0;
+  level->context_rect.width  = NOHALO_SIZE_0;
+  level->context_rect.height = NOHALO_SIZE_0;
+
+  level = &sampler->level[1];
+  level->context_rect.x   = -NOHALO_OFFSET_1;
+  level->context_rect.y   = -NOHALO_OFFSET_1;
+  level->context_rect.width  = NOHALO_SIZE_1;
+  level->context_rect.height = NOHALO_SIZE_1;
+
+  level = &sampler->level[2];
+  level->context_rect.x   = -NOHALO_OFFSET_2;
+  level->context_rect.y   = -NOHALO_OFFSET_2;
+  level->context_rect.width  = NOHALO_SIZE_2;
+  level->context_rect.height = NOHALO_SIZE_2;
+
+  level = &sampler->level[3];
+  level->context_rect.x   = -NOHALO_OFFSET_3;
+  level->context_rect.y   = -NOHALO_OFFSET_3;
+  level->context_rect.width  = NOHALO_SIZE_3;
+  level->context_rect.height = NOHALO_SIZE_3;
+
+  level = &sampler->level[4];
+  level->context_rect.x   = -NOHALO_OFFSET_4;
+  level->context_rect.y   = -NOHALO_OFFSET_4;
+  level->context_rect.width  = NOHALO_SIZE_4;
+  level->context_rect.height = NOHALO_SIZE_4;
+
+  level = &sampler->level[5];
+  level->context_rect.x   = -NOHALO_OFFSET_5;
+  level->context_rect.y   = -NOHALO_OFFSET_5;
+  level->context_rect.width  = NOHALO_SIZE_5;
+  level->context_rect.height = NOHALO_SIZE_5;
+
+  level = &sampler->level[6];
+  level->context_rect.x   = -NOHALO_OFFSET_6;
+  level->context_rect.y   = -NOHALO_OFFSET_6;
+  level->context_rect.width  = NOHALO_SIZE_6;
+  level->context_rect.height = NOHALO_SIZE_6;
+
+  level = &sampler->level[7];
+  level->context_rect.x   = -NOHALO_OFFSET_7;
+  level->context_rect.y   = -NOHALO_OFFSET_7;
+  level->context_rect.width  = NOHALO_SIZE_7;
+  level->context_rect.height = NOHALO_SIZE_7;
 
   GEGL_SAMPLER (self)->interpolate_format = babl_format ("RaGaBaA float");
 }
diff --git a/gegl/buffer/gegl-sampler.c b/gegl/buffer/gegl-sampler.c
index 755160e..224f520 100644
--- a/gegl/buffer/gegl-sampler.c
+++ b/gegl/buffer/gegl-sampler.c
@@ -110,17 +110,21 @@ gegl_sampler_class_init (GeglSamplerClass *klass)
 }
 
 static void
-gegl_sampler_init (GeglSampler *self)
+gegl_sampler_init (GeglSampler *sampler)
 {
-  int i = 0;
-  self->buffer = NULL;
+  gint i = 0;
+  sampler->buffer = NULL;
   do {
     GeglRectangle context_rect      = {0,0,1,1};
     GeglRectangle sampler_rectangle = {0,0,0,0};
-    self->sampler_buffer[i]         = NULL;
-    self->context_rect[i]           = context_rect;
-    self->sampler_rectangle[i]      = sampler_rectangle;
+    sampler->level[i].sampler_buffer = NULL;
+    sampler->level[i].context_rect   = context_rect;
+    sampler->level[i].sampler_rectangle = sampler_rectangle;
   } while ( ++i<GEGL_SAMPLER_MIPMAP_LEVELS );
+
+  sampler->level[0].sampler_buffer =
+    g_malloc0 (GEGL_SAMPLER_MAXIMUM_WIDTH *
+               GEGL_SAMPLER_MAXIMUM_HEIGHT * 16);
 }
 
 void
@@ -158,17 +162,9 @@ gegl_sampler_prepare (GeglSampler *self)
    * This makes the cache rect invalid, in case the data in the buffer
    * has changed:
    */
-  self->sampler_rectangle[0].width = 0;
-  self->sampler_rectangle[0].height = 0;
+  self->level[0].sampler_rectangle.width = 0;
+  self->level[0].sampler_rectangle.height = 0;
 
-#if 0
-  if (self->cache_buffer) /* Force a regetting of the region, even
-                             though the cached getter may be valid. */
-    {
-      g_free (self->cache_buffer);
-      self->cache_buffer = NULL;
-    }
-#endif
   self->get = klass->get; /* cache the sampler in the instance */
 }
 
@@ -191,10 +187,10 @@ finalize (GObject *gobject)
   GeglSampler *sampler = GEGL_SAMPLER (gobject);
   int i = 0;
   do {
-    if (sampler->sampler_buffer[i])
+    if (sampler->level[i].sampler_buffer)
       {
-        g_free (sampler->sampler_buffer[i]);
-        sampler->sampler_buffer[i] = NULL;
+        g_free (sampler->level[i].sampler_buffer);
+        sampler->level[i].sampler_buffer = NULL;
       }
   } while ( ++i<GEGL_SAMPLER_MIPMAP_LEVELS );
   G_OBJECT_CLASS (gegl_sampler_parent_class)->finalize (gobject);
@@ -211,254 +207,151 @@ dispose (GObject *gobject)
   G_OBJECT_CLASS (gegl_sampler_parent_class)->dispose (gobject);
 }
 
-/*
- * Gets a pointer to the center pixel, within a buffer that has a
- * rowstride of GEGL_SAMPLER_MAXIMUM_WIDTH px * bpp.
- */
-gfloat *
-gegl_sampler_get_ptr (GeglSampler *const sampler,
-                      const gint         x,
-                      const gint         y,
-                      GeglAbyssPolicy    repeat_mode)
+GeglRectangle _gegl_sampler_compute_rectangle (GeglSampler *sampler,
+                                               gint         x,
+                                               gint         y,
+                                               gint         level_no)
 {
-  guchar *buffer_ptr;
-  gint    dx;
-  gint    dy;
-  gint    sof;
+  GeglRectangle rectangle;
+  GeglSamplerLevel *level = &sampler->level[level_no];
 
-  const gint bpp =
-    babl_format_get_bytes_per_pixel (sampler->interpolate_format);
+  if (level->last_x || level->last_y)
+  {
+    gint x_delta = x - level->last_x;
+    gint y_delta = y - level->last_y;
+    gint max_delta_squared = 60 * 60;
 
-  const gint maximum_width  = GEGL_SAMPLER_MAXIMUM_WIDTH;
-  const gint maximum_height = GEGL_SAMPLER_MAXIMUM_HEIGHT;
-  g_assert (sampler->context_rect[0].width  <= maximum_width);
-  g_assert (sampler->context_rect[0].height <= maximum_height);
-
-  if ((sampler->sampler_buffer[0] == NULL)
-      ||
-      (x + sampler->context_rect[0].x < sampler->sampler_rectangle[0].x)
-      ||
-      (y + sampler->context_rect[0].y < sampler->sampler_rectangle[0].y)
-      ||
-      (x + sampler->context_rect[0].x + sampler->context_rect[0].width >
-       sampler->sampler_rectangle[0].x + sampler->sampler_rectangle[0].width)
-      ||
-      (y + sampler->context_rect[0].y + sampler->context_rect[0].height >
-       sampler->sampler_rectangle[0].y + sampler->sampler_rectangle[0].height))
+    if (x_delta * x_delta < max_delta_squared &&
+        y_delta * y_delta < max_delta_squared)
     {
-      /*
-       * fetch_rectangle will become the value of
-       * sampler->sampler_rectangle[0]:
-       */
-      GeglRectangle fetch_rectangle;
-
-      /*
-       * Always request the same amount of pixels:
-       */
-      if (sampler->sampler_buffer[0] == NULL)
-        {
-          sampler->sampler_buffer[0] =
-            g_malloc0 (maximum_width * maximum_height * bpp);
-        }
-
-      /*
-       * Override the fetch rectangle needed by the sampler in order
-       * to prevent constant buffer creation, adding some elbow room
-       * at the top and left so that buffers are not constantly
-       * re-created when things are "slanted the wrong way", taking
-       * into account that it is more likely that further access is to
-       * the right or down of our currently requested
-       * position. Consequently, we move the top left corner of the
-       * context_rect, which has the effect of leaving elbow room there.
-       *
-       * Note that transform-core now works hard to have scanlines go
-       * toward the interior of the buffer.
-       *
-       * For operations like resize and left-right and top-bottom
-       * reflections, there is no reason to add elbow room. This means
-       * that the following code will need to have a dual personality
-       * sooner or later. Eliminating the elbow room for such
-       * operations (and making tiles elongated rectangles) gives a
-       * big speedup.
-       */
-      fetch_rectangle.width  = maximum_width;
-      fetch_rectangle.height = maximum_height;
-      fetch_rectangle.x =
-        x + sampler->context_rect[0].x -
-        (maximum_width  - sampler->context_rect[0].width ) / (gint) 4;
-      fetch_rectangle.y =
-        y + sampler->context_rect[0].y -
-        (maximum_height - sampler->context_rect[0].height) / (gint) 4;
-
-      gegl_buffer_get (sampler->buffer,
-                       &fetch_rectangle,
-                       1.0,
-                       sampler->interpolate_format,
-                       sampler->sampler_buffer[0],
-                       GEGL_AUTO_ROWSTRIDE,
-                       repeat_mode);
-
-      sampler->sampler_rectangle[0] = fetch_rectangle;
+      level->x_delta = level->x_delta * 0.99 + x_delta * 0.01;
+      level->y_delta = level->y_delta * 0.99 + y_delta * 0.01;
+      if (x_delta < 0) x_delta = -x_delta;
+      if (y_delta < 0) y_delta = -y_delta;
+      level->x_magnitude = level->x_magnitude  * 0.99 + x_delta * 0.01;
+      level->y_magnitude = level->y_magnitude  * 0.99 + y_delta * 0.01;
     }
+  }
+  level->last_x = x;
+  level->last_y = y;
 
-  dx         = x - sampler->sampler_rectangle[0].x;
-  dy         = y - sampler->sampler_rectangle[0].y;
-  buffer_ptr = (guchar *)sampler->sampler_buffer[0];
-  sof        = ( dx + dy * sampler->sampler_rectangle[0].width ) * bpp;
-
-  return (gfloat*)(buffer_ptr+sof);
-}
+  if (level->x_magnitude > 0.001 || level->y_magnitude > 0.001)
+    {
+      gfloat magnitude = sqrtf (level->x_magnitude * level->x_magnitude +
+                                level->y_magnitude * level->y_magnitude);
+      gfloat new_magnitude;
 
-gfloat *
-gegl_sampler_get_from_buffer (GeglSampler *const sampler,
-                              const gint         x,
-                              const gint         y,
-                              GeglAbyssPolicy    repeat_mode)
-{
-  guchar *buffer_ptr;
-  gint    dx;
-  gint    dy;
-  gint    sof;
+      if (level->x_magnitude > level->y_magnitude)
+        new_magnitude =
+          4 + 60 * (level->x_magnitude - level->y_magnitude) / level->x_magnitude;
+      else
+        new_magnitude =
+          4 + 60 * (level->y_magnitude - level->x_magnitude) / level->y_magnitude;
+
+      rectangle.width = 
+        (level->x_magnitude / magnitude) * new_magnitude
+        + level->context_rect.width + 2;
+      rectangle.height = 
+        (level->y_magnitude / magnitude) * new_magnitude
+        + level->context_rect.height + 2;
+
+      /* todo: if both xmag and ymag are small - but similar in magnitude 
+         we should increase the size of the cache if it would fit, thus
+         perhaps working better on small local non-linear access patterns
+       */
 
-  const gint bpp =
-    babl_format_get_bytes_per_pixel (sampler->interpolate_format);
+      if (rectangle.width >= GEGL_SAMPLER_MAXIMUM_WIDTH)
+        rectangle.width = GEGL_SAMPLER_MAXIMUM_WIDTH;
+      if (rectangle.height >= GEGL_SAMPLER_MAXIMUM_HEIGHT)
+        rectangle.height = GEGL_SAMPLER_MAXIMUM_HEIGHT;
 
-  const gint maximum_width  = GEGL_SAMPLER_MAXIMUM_WIDTH;
-  const gint maximum_height = GEGL_SAMPLER_MAXIMUM_HEIGHT;
-  g_assert (sampler->context_rect[0].width  <= maximum_width);
-  g_assert (sampler->context_rect[0].height <= maximum_height);
-
-  if ((sampler->sampler_buffer[0] == NULL)
-      ||
-      (x < sampler->sampler_rectangle[0].x)
-      ||
-      (y < sampler->sampler_rectangle[0].y)
-      ||
-      (x >=
-       sampler->sampler_rectangle[0].x + sampler->sampler_rectangle[0].width)
-      ||
-      (y >=
-       sampler->sampler_rectangle[0].y + sampler->sampler_rectangle[0].height))
-    {
-      /*
-       * fetch_rectangle will become the value of
-       * sampler->sampler_rectangle:
-       */
-      GeglRectangle fetch_rectangle;
 
-      /*
-       * Always request the same amount of pixels:
+      /* align rectangle corner we've likely entered with sampled pixel
        */
-      if (sampler->sampler_buffer[0] == NULL)
-        {
-          sampler->sampler_buffer[0] =
-            g_malloc0 (maximum_width * maximum_height * bpp);
-        }
-
-      fetch_rectangle.width  = maximum_width;
-      fetch_rectangle.height = maximum_height;
-      fetch_rectangle.x = x -
-        (maximum_width  - sampler->context_rect[0].width ) / (gint) 4;
-      fetch_rectangle.y = y -
-        (maximum_height - sampler->context_rect[0].height) / (gint) 4;
+      if (level->x_delta >=0)
+        rectangle.x = x + level->context_rect.x
+                        - (rectangle.width - level->context_rect.x)/10;
+      else
+        rectangle.x = x + level->context_rect.x
+                        - (rectangle.width - level->context_rect.width) * 9/10;
 
-      gegl_buffer_get (sampler->buffer,
-                       &fetch_rectangle,
-                       1.0,
-                       sampler->interpolate_format,
-                       sampler->sampler_buffer[0],
-                       GEGL_AUTO_ROWSTRIDE,
-                       repeat_mode);
+      if (level->y_delta >=0)
+        rectangle.y = y + level->context_rect.y
+                        - (rectangle.height - level->context_rect.y)/10;
+      else
+        rectangle.y = y + level->context_rect.y 
+                        - (rectangle.height - level->context_rect.height) * 9/10;
+    }
+  else
+    {
+      rectangle.width  = level->context_rect.width + 2;
+      rectangle.height = level->context_rect.height + 2;
 
-      sampler->sampler_rectangle[0] = fetch_rectangle;
+      rectangle.x = x + level->context_rect.x
+                       - (rectangle.width - level->context_rect.x)/4;
+      rectangle.y = y + level->context_rect.y
+                        - (rectangle.height - level->context_rect.y)/4;
     }
 
-  dx         = x - sampler->sampler_rectangle[0].x;
-  dy         = y - sampler->sampler_rectangle[0].y;
-  buffer_ptr = (guchar *)sampler->sampler_buffer[0];
-  sof        = ( dx + dy * sampler->sampler_rectangle[0].width ) * bpp;
+  g_assert (level->context_rect.width  <= rectangle.width);
+  g_assert (level->context_rect.height <= rectangle.height);
 
-  return (gfloat*)(buffer_ptr+sof);
+  return rectangle;
 }
 
+
 gfloat *
-gegl_sampler_get_from_mipmap (GeglSampler *const sampler,
-                              const gint         x,
-                              const gint         y,
-                              const gint         level,
-                              GeglAbyssPolicy    repeat_mode)
+gegl_sampler_get_from_mipmap (GeglSampler    *sampler,
+                              gint            x,
+                              gint            y,
+                              gint            level_no,
+                              GeglAbyssPolicy repeat_mode)
 {
+  GeglSamplerLevel *level = &sampler->level[level_no];
   guchar *buffer_ptr;
   gint    dx;
   gint    dy;
   gint    sof;
 
-  const gdouble scale = 1. / ( (gdouble) (1<<level) );
-
-  const gint bpp =
-    babl_format_get_bytes_per_pixel (sampler->interpolate_format);
+  const gdouble scale = 1. / ( (gdouble) (1<<level_no) );
 
   const gint maximum_width  = GEGL_SAMPLER_MAXIMUM_WIDTH;
   const gint maximum_height = GEGL_SAMPLER_MAXIMUM_HEIGHT;
-  g_assert (level >= 0 && level < GEGL_SAMPLER_MIPMAP_LEVELS);
-  g_assert (sampler->context_rect[level].width  <= maximum_width);
-  g_assert (sampler->context_rect[level].height <= maximum_height);
-
-  if ((sampler->sampler_buffer[level] == NULL)
-      ||
-      (x + sampler->context_rect[level].x < sampler->sampler_rectangle[level].x)
-      ||
-      (y + sampler->context_rect[level].y < sampler->sampler_rectangle[level].y)
-      ||
-      (x + sampler->context_rect[level].x +
-       sampler->context_rect[level].width >
-       sampler->sampler_rectangle[level].x +
-       sampler->sampler_rectangle[level].width)
-      ||
-      (y + sampler->context_rect[level].y +
-       sampler->context_rect[level].height >
-       sampler->sampler_rectangle[level].y +
-       sampler->sampler_rectangle[level].height))
+  g_assert (level_no >= 0 && level_no < GEGL_SAMPLER_MIPMAP_LEVELS);
+  g_assert (level->context_rect.width  <= maximum_width);
+  g_assert (level->context_rect.height <= maximum_height);
+
+  if ((level->sampler_buffer == NULL)
+   || (x + level->context_rect.x < level->sampler_rectangle.x)
+   || (y + level->context_rect.y < level->sampler_rectangle.y)
+   || (x + level->context_rect.x + level->context_rect.width
+     > level->sampler_rectangle.x + level->sampler_rectangle.width)
+   || (y + level->context_rect.y + level->context_rect.height
+     > level->sampler_rectangle.y + level->sampler_rectangle.height))
     {
       /*
        * fetch_rectangle will become the value of
        * sampler->sampler_rectangle[level]:
        */
-      GeglRectangle fetch_rectangle;
+      level->sampler_rectangle = _gegl_sampler_compute_rectangle (sampler, x, y, 
+                                                                  level_no);
 
-      /*
-       * Always request the same amount of pixels:
-       */
-      if (sampler->sampler_buffer[level] == NULL)
-        {
-          sampler->sampler_buffer[level] =
-            g_malloc0 (maximum_width * maximum_height * bpp);
-        }
-
-      fetch_rectangle.width  = maximum_width;
-      fetch_rectangle.height = maximum_height;
-      fetch_rectangle.x =
-        x + sampler->context_rect[level].x -
-        (maximum_width  - sampler->context_rect[level].width ) / (gint) 4;
-      fetch_rectangle.y =
-        y + sampler->context_rect[level].y -
-        (maximum_height - sampler->context_rect[level].height) / (gint) 4;
+      level->sampler_buffer =
+        g_malloc0 (GEGL_SAMPLER_ROWSTRIDE * GEGL_SAMPLER_MAXIMUM_HEIGHT);
 
       gegl_buffer_get (sampler->buffer,
-                       &fetch_rectangle,
+                       &level->sampler_rectangle,
                        scale,
                        sampler->interpolate_format,
-                       sampler->sampler_buffer[level],
-                       GEGL_AUTO_ROWSTRIDE,
+                       level->sampler_buffer,
+                       GEGL_SAMPLER_ROWSTRIDE,
                        repeat_mode);
-
-      sampler->sampler_rectangle[level] = fetch_rectangle;
     }
 
-  dx         = x - sampler->sampler_rectangle[level].x;
-  dy         = y - sampler->sampler_rectangle[level].y;
-  buffer_ptr = (guchar *)sampler->sampler_buffer[level];
-  sof        = ( dx + dy * sampler->sampler_rectangle[level].width ) * bpp;
+  dx         = x - level->sampler_rectangle.x;
+  dy         = y - level->sampler_rectangle.y;
+  buffer_ptr = (guchar *)level->sampler_buffer;
+  sof        = ( dx + dy * GEGL_SAMPLER_MAXIMUM_WIDTH) * GEGL_SAMPLER_BPP;
 
   return (gfloat*)(buffer_ptr+sof);
 }
@@ -604,7 +497,7 @@ gegl_buffer_sampler_new (GeglBuffer      *buffer,
 const GeglRectangle*
 gegl_sampler_get_context_rect (GeglSampler *sampler)
 {
-  return &(sampler->context_rect[0]);
+  return &(sampler->level[0].context_rect);
 }
 
 static void
@@ -613,6 +506,7 @@ buffer_contents_changed (GeglBuffer          *buffer,
                          gpointer             userdata)
 {
   GeglSampler *self = GEGL_SAMPLER (userdata);
+  int i;
 
   /*
    * Invalidate all mipmap levels by setting the width and height of the
@@ -621,7 +515,13 @@ buffer_contents_changed (GeglBuffer          *buffer,
    * XXX: it might be faster to only invalidate rects that intersect
    *      changed_rect
    */
-  memset (self->sampler_rectangle, 0, sizeof (self->sampler_rectangle));
+  for (i = 0; i < GEGL_SAMPLER_MIPMAP_LEVELS; i++)
+    memset (&self->level[i].sampler_rectangle, 0, sizeof (self->level[0].sampler_rectangle));
 
   return;
 }
+
+GeglSamplerGetFun gegl_sampler_get_fun (GeglSampler *sampler)
+{
+  return sampler->get;
+}
diff --git a/gegl/buffer/gegl-sampler.h b/gegl/buffer/gegl-sampler.h
index 69f9f64..6efeb5a 100644
--- a/gegl/buffer/gegl-sampler.h
+++ b/gegl/buffer/gegl-sampler.h
@@ -40,101 +40,128 @@ G_BEGIN_DECLS
  * twice as wide as they are tall.
  */
 
-//128 20.42 23.23
-// 96 26.91 30.39
-// 64 34.32 41.52
-// 48 38.66 46.67
-// 32 43.17 53.78
-// 24 45.38 56.11
-// 20 45.18 56.70
-// 20 45.41 56.32
-// 19 45.75 57.81
-// 19 45.81 57.69
-// 18 47.16 60.02
-// 18 47.19 60.36
-// 17 46.43 58.73
-// 17 46.61 58.85
-// 16 46.44 58.73
-//  8 43.25 54
-//  4 33.11 39
-
-#define GEGL_SAMPLER_MAXIMUM_HEIGHT (27)
+#define GEGL_SAMPLER_MAXIMUM_HEIGHT (64)
 #define GEGL_SAMPLER_MAXIMUM_WIDTH (GEGL_SAMPLER_MAXIMUM_HEIGHT)
+#define GEGL_SAMPLER_BPP 16
+#define GEGL_SAMPLER_ROWSTRIDE (GEGL_SAMPLER_MAXIMUM_WIDTH * GEGL_SAMPLER_BPP)
 
 typedef struct _GeglSamplerClass GeglSamplerClass;
 
+typedef struct GeglSamplerLevel
+{
+  GeglRectangle  context_rect;
+  gpointer       sampler_buffer;
+  GeglRectangle  sampler_rectangle;
+
+  gint           last_x;
+  gint           last_y;
+  float          x_delta;
+  float          y_delta;
+  float          x_magnitude;
+  float          y_magnitude;
+} GeglSamplerLevel;
+
 struct _GeglSampler
 {
-  GObject       parent_instance;
-  void (* get) (GeglSampler     *self,
-                gdouble          x,
-                gdouble          y,
-                GeglMatrix2     *scale,
-                void            *output,
-                GeglAbyssPolicy  repeat_mode);
-  /* we cache the getter in the instance, (being able to return the
-     function pointer itself and cache it outside the calling loop
-     would be even quicker.
-   */
+  GObject           parent_instance;
+  GeglSamplerGetFun get;
 
   /*< private >*/
   GeglBuffer    *buffer;
   const Babl    *format;
   const Babl    *interpolate_format;
   const Babl    *fish;
-  GeglRectangle  context_rect[GEGL_SAMPLER_MIPMAP_LEVELS];
-  gpointer       sampler_buffer[GEGL_SAMPLER_MIPMAP_LEVELS];
-  GeglRectangle  sampler_rectangle[GEGL_SAMPLER_MIPMAP_LEVELS];
-  gdouble        x; /* mirrors the currently requested */
-  gdouble        y; /* coordinates in the instance     */
 
-  gpointer       padding[8]; /* eat from the padding if adding to the struct */
+  GeglSamplerLevel level[GEGL_SAMPLER_MIPMAP_LEVELS];
 };
 
+typedef void (*GeglSamplerGetFun)  (GeglSampler     *self,
+                                    gdouble          x,
+                                    gdouble          y,
+                                    GeglMatrix2     *scale,
+                                    void            *output,
+                                    GeglAbyssPolicy  repeat_mode);
+
 struct _GeglSamplerClass
 {
   GObjectClass  parent_class;
 
   void (* prepare)   (GeglSampler     *self);
-  void (* get)       (GeglSampler     *self,
-                      gdouble          x,
-                      gdouble          y,
-                      GeglMatrix2     *scale,
-                      void            *output,
-                      GeglAbyssPolicy  repeat_mode);
- void  (*set_buffer) (GeglSampler     *self,
-                      GeglBuffer      *buffer);
-
- gpointer       padding[8]; /* eat from the padding if adding to the struct */
+  GeglSamplerGetFun   get;
+  void  (*set_buffer) (GeglSampler     *self,
+                       GeglBuffer      *buffer);
 };
 
 GType gegl_sampler_get_type    (void) G_GNUC_CONST;
 
 /* virtual method invokers */
-void  gegl_sampler_prepare     (GeglSampler *self);
-void  gegl_sampler_set_buffer  (GeglSampler *self,
-                                GeglBuffer  *buffer);
-
-void  gegl_sampler_get         (GeglSampler   *self,
-                                gdouble        x,
-                                gdouble        y,
-                                GeglMatrix2   *scale,
-                                void          *output,
-                                GeglAbyssPolicy repeat_mode);
-
-gfloat * gegl_sampler_get_from_buffer (GeglSampler *const sampler,
-                                       const gint         x,
-                                       const gint         y,
-                                       GeglAbyssPolicy    repeat_mode);
-gfloat * gegl_sampler_get_from_mipmap (GeglSampler *const sampler,
-                                       const gint         x,
-                                       const gint         y,
-                                       const gint         level,
-                                       GeglAbyssPolicy    repeat_mode);
-gfloat * gegl_sampler_get_ptr         (GeglSampler *const sampler,
-                                       const gint         x,
-                                       const gint         y,
-                                       GeglAbyssPolicy    repeat_mode);
+void  gegl_sampler_prepare             (GeglSampler *self);
+void  gegl_sampler_set_buffer          (GeglSampler *self,
+                                        GeglBuffer  *buffer);
+
+GeglSamplerGetFun gegl_sampler_get_fun (GeglSampler    *sampler);
+
+gfloat * gegl_sampler_get_from_buffer (GeglSampler     *sampler,
+                                       gint             x,
+                                       gint             y,
+                                       GeglAbyssPolicy  repeat_mode);
+gfloat * gegl_sampler_get_from_mipmap (GeglSampler     *sampler,
+                                       gint             x,
+                                       gint             y,
+                                       gint             level,
+                                       GeglAbyssPolicy  repeat_mode);
+gfloat * _gegl_sampler_get_ptr        (GeglSampler     *sampler,
+                                       gint             x,
+                                       gint             y,
+                                       GeglAbyssPolicy  repeat_mode);
+
+GeglRectangle _gegl_sampler_compute_rectangle (GeglSampler *sampler,
+                                               gint         x,
+                                               gint         y,
+                                               gint         level);
+
+/*
+ * Gets a pointer to the center pixel, within a buffer that has a
+ * rowstride of GEGL_SAMPLER_MAXIMUM_WIDTH * 16 (16 is the bpp of RaGaBaA
+ * float).
+ *
+ * inlining this function gives a 4-5% performance gain for affine ops for
+ * linear/cubic sampling.
+ */
+static inline gfloat *
+gegl_sampler_get_ptr (GeglSampler    *sampler,
+                      gint            x,
+                      gint            y,
+                      GeglAbyssPolicy repeat_mode)
+{
+  GeglSamplerLevel *level = &sampler->level[0];
+  if ((x + level->context_rect.x < level->sampler_rectangle.x)
+   || (y + level->context_rect.y < level->sampler_rectangle.y)
+   || (x + level->context_rect.x + level->context_rect.width
+     > level->sampler_rectangle.x + level->sampler_rectangle.width)
+   || (y + level->context_rect.y + level->context_rect.height
+     > level->sampler_rectangle.y + level->sampler_rectangle.height))
+    {
+      level->sampler_rectangle = _gegl_sampler_compute_rectangle (sampler, x, y, 0);
+
+      gegl_buffer_get (sampler->buffer,
+                       &level->sampler_rectangle,
+                       1.0,
+                       sampler->interpolate_format,
+                       level->sampler_buffer,
+                       GEGL_SAMPLER_MAXIMUM_WIDTH * GEGL_SAMPLER_BPP,
+                       repeat_mode);
+    }
+
+  {
+    gint    dx         = x - level->sampler_rectangle.x;
+    gint    dy         = y - level->sampler_rectangle.y;
+    gint    sof        = (dx + dy * GEGL_SAMPLER_MAXIMUM_WIDTH) * GEGL_SAMPLER_BPP;
+    guchar *buffer_ptr = (guchar *)level->sampler_buffer;
+
+    return (gfloat*)(buffer_ptr+sof);
+  }
+}
 
 G_END_DECLS
 
diff --git a/perf/test-rotate.c b/perf/test-rotate.c
index e21923b..628f911 100644
--- a/perf/test-rotate.c
+++ b/perf/test-rotate.c
@@ -9,7 +9,7 @@ main (gint    argc,
 
   gegl_init (&argc, &argv);
 
-  buffer = test_buffer (2048, 1024, babl_format ("RGBA float"));
+  buffer = test_buffer (1024, 1024, babl_format ("RGBA float"));
 
   gegl = gegl_node_new ();
   source = gegl_node_new_child (gegl, "operation", "gegl:buffer-source", "buffer", buffer, NULL);



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