Speedup of regular 2D painting
authorSergey Sharybin <sergey.vfx@gmail.com>
Fri, 6 May 2016 09:48:07 +0000 (11:48 +0200)
committerSergey Sharybin <sergey.vfx@gmail.com>
Fri, 6 May 2016 09:49:09 +0000 (11:49 +0200)
Yet another commit which makes painting aware of multi-threaded systems.

source/blender/editors/sculpt_paint/paint_image_2d.c
source/blender/imbuf/IMB_imbuf.h
source/blender/imbuf/intern/allocimbuf.c
source/blender/imbuf/intern/rectop.c

index 59b50e28ce7f9c00e66975490802268476c53b04..080bd5b73c78fba74e547761a532298d121e3dee 100644 (file)
@@ -42,6 +42,7 @@
 #include "BLI_math_color_blend.h"
 #include "BLI_stack.h"
 #include "BLI_bitmap.h"
+#include "BLI_task.h"
 
 #include "BKE_context.h"
 #include "BKE_depsgraph.h"
@@ -1019,6 +1020,64 @@ static void paint_2d_convert_brushco(ImBuf *ibufb, const float pos[2], int ipos[
        ipos[1] = (int)floorf((pos[1] - ibufb->y / 2));
 }
 
+static void paint_2d_do_making_brush(ImagePaintState *s,
+                                     ImagePaintRegion *region,
+                                     unsigned short *curveb,
+                                     unsigned short *texmaskb,
+                                     ImBuf *frombuf,
+                                     float mask_max,
+                                     short blend,
+                                     int tilex, int tiley,
+                                     int tilew, int tileh)
+{
+       ImBuf tmpbuf;
+       IMB_initImBuf(&tmpbuf, IMAPAINT_TILE_SIZE, IMAPAINT_TILE_SIZE, 32, 0);
+
+       for (int ty = tiley; ty <= tileh; ty++) {
+               for (int tx = tilex; tx <= tilew; tx++) {
+                       /* retrieve original pixels + mask from undo buffer */
+                       unsigned short *mask;
+                       int origx = region->destx - tx * IMAPAINT_TILE_SIZE;
+                       int origy = region->desty - ty * IMAPAINT_TILE_SIZE;
+
+                       if (s->canvas->rect_float)
+                               tmpbuf.rect_float = image_undo_find_tile(s->image, s->canvas, tx, ty, &mask, false);
+                       else
+                               tmpbuf.rect = image_undo_find_tile(s->image, s->canvas, tx, ty, &mask, false);
+
+                       IMB_rectblend(s->canvas, &tmpbuf, frombuf, mask,
+                                     curveb, texmaskb, mask_max,
+                                     region->destx, region->desty,
+                                     origx, origy,
+                                     region->srcx, region->srcy,
+                                     region->width, region->height,
+                                     blend, ((s->brush->flag & BRUSH_ACCUMULATE) != 0));
+               }
+       }
+}
+
+typedef struct Paint2DForeachData {
+       ImagePaintState *s;
+       ImagePaintRegion *region;
+       unsigned short *curveb;
+       unsigned short *texmaskb;
+       ImBuf *frombuf;
+       float mask_max;
+       short blend;
+       int tilex;
+       int tilew;
+} Paint2DForeachData;
+
+static void paint_2d_op_foreach_do(void *data_v, const int iter)
+{
+       Paint2DForeachData *data = (Paint2DForeachData *)data_v;
+       paint_2d_do_making_brush(data->s, data->region, data->curveb,
+                                data->texmaskb, data->frombuf, data->mask_max,
+                                data->blend,
+                                data->tilex, iter,
+                                data->tilew, iter);
+}
+
 static int paint_2d_op(void *state, ImBuf *ibufb, unsigned short *curveb, unsigned short *texmaskb, const float lastpos[2], const float pos[2])
 {
        ImagePaintState *s = ((ImagePaintState *)state);
@@ -1072,45 +1131,40 @@ static int paint_2d_op(void *state, ImBuf *ibufb, unsigned short *curveb, unsign
        
                if (s->do_masking) {
                        /* masking, find original pixels tiles from undo buffer to composite over */
-                       int tilex, tiley, tilew, tileh, tx, ty;
-                       ImBuf *tmpbuf;
+                       int tilex, tiley, tilew, tileh;
 
                        imapaint_region_tiles(s->canvas, region[a].destx, region[a].desty,
                                              region[a].width, region[a].height,
                                              &tilex, &tiley, &tilew, &tileh);
 
-                       tmpbuf = IMB_allocImBuf(IMAPAINT_TILE_SIZE, IMAPAINT_TILE_SIZE, 32, 0);
-
-                       for (ty = tiley; ty <= tileh; ty++) {
-                               for (tx = tilex; tx <= tilew; tx++) {
-                                       /* retrieve original pixels + mask from undo buffer */
-                                       unsigned short *mask;
-                                       int origx = region[a].destx - tx * IMAPAINT_TILE_SIZE;
-                                       int origy = region[a].desty - ty * IMAPAINT_TILE_SIZE;
-
-                                       if (s->canvas->rect_float)
-                                               tmpbuf->rect_float = image_undo_find_tile(s->image, s->canvas, tx, ty, &mask, false);
-                                       else
-                                               tmpbuf->rect = image_undo_find_tile(s->image, s->canvas, tx, ty, &mask, false);
-
-                                       IMB_rectblend(s->canvas, tmpbuf, frombuf, mask,
-                                                     curveb, texmaskb, mask_max,
-                                                     region[a].destx, region[a].desty,
-                                                     origx, origy,
-                                                     region[a].srcx, region[a].srcy,
-                                                     region[a].width, region[a].height, blend, ((s->brush->flag & BRUSH_ACCUMULATE) != 0));
-                               }
+                       if (tiley == tileh) {
+                               paint_2d_do_making_brush(s, &region[a], curveb, texmaskb, frombuf,
+                                                        mask_max, blend, tilex, tiley, tilew, tileh);
                        }
+                       else {
+                               Paint2DForeachData data;
+                               data.s = s;
+                               data.region = &region[a];
+                               data.curveb = curveb;
+                               data.texmaskb = texmaskb;
+                               data.frombuf = frombuf;
+                               data.mask_max = mask_max;
+                               data.blend = blend;
+                               data.tilex = tilex;
+                               data.tilew = tilew;
+                               BLI_task_parallel_range(tiley, tileh + 1, &data,
+                                                       paint_2d_op_foreach_do,
+                                                       true);
 
-                       IMB_freeImBuf(tmpbuf);
+                       }
                }
                else {
                        /* no masking, composite brush directly onto canvas */
-                       IMB_rectblend(s->canvas, s->canvas, frombuf, NULL, curveb, texmaskb, mask_max,
-                                     region[a].destx, region[a].desty,
-                                     region[a].destx, region[a].desty,
-                                     region[a].srcx, region[a].srcy,
-                                     region[a].width, region[a].height, blend, false);
+                       IMB_rectblend_threaded(s->canvas, s->canvas, frombuf, NULL, curveb, texmaskb, mask_max,
+                                              region[a].destx, region[a].desty,
+                                              region[a].destx, region[a].desty,
+                                              region[a].srcx, region[a].srcy,
+                                              region[a].width, region[a].height, blend, false);
                }
        }
 
index 383a5f19c4a22bdbb159f9f799e2c183c5267088..93d2b3e0cd0f90d5bd20f30f16e11c1f5b44c5c0 100644 (file)
@@ -130,7 +130,18 @@ void IMB_freeImBuf(struct ImBuf *ibuf);
  * \attention Defined in allocimbuf.c
  */
 struct ImBuf *IMB_allocImBuf(unsigned int x, unsigned int y,
-                             unsigned char d, unsigned int flags);
+                             unsigned char planes, unsigned int flags);
+
+/**
+ * Initialize given ImBuf.
+ *
+ * Use in cases when temporary image buffer is allocated on stack.
+ *
+ * \attention Defined in allocimbuf.c
+ */
+bool IMB_initImBuf(struct ImBuf *ibuf,
+                   unsigned int x, unsigned int y,
+                   unsigned char planes, unsigned int flags);
 
 /**
  * Create a copy of a pixel buffer and wrap it to a new ImBuf
@@ -213,6 +224,10 @@ void IMB_rectblend(struct ImBuf *dbuf, struct ImBuf *obuf, struct ImBuf *sbuf,
        unsigned short *dmask, unsigned short *curvemask, unsigned short *mmask, float mask_max,
        int destx,  int desty, int origx, int origy, int srcx, int srcy,
        int width, int height, IMB_BlendMode mode, bool accumulate);
+void IMB_rectblend_threaded(struct ImBuf *dbuf, struct ImBuf *obuf, struct ImBuf *sbuf,
+       unsigned short *dmask, unsigned short *curvemask, unsigned short *mmask, float mask_max,
+       int destx,  int desty, int origx, int origy, int srcx, int srcy,
+       int width, int height, IMB_BlendMode mode, bool accumulate);
 
 /**
  *
index 988f43ff9fae2256c087265452adc5a3ddd6ce1c..ef3743d9c8a85b28b316aecabdfc0e3d58dd15b8 100644 (file)
@@ -446,49 +446,60 @@ ImBuf *IMB_allocImBuf(unsigned int x, unsigned int y, uchar planes, unsigned int
 {
        ImBuf *ibuf;
 
-       ibuf = MEM_callocN(sizeof(ImBuf), "ImBuf_struct");
+       ibuf = MEM_mallocN(sizeof(ImBuf), "ImBuf_struct");
 
        if (ibuf) {
-               ibuf->x = x;
-               ibuf->y = y;
-               ibuf->planes = planes;
-               ibuf->ftype = IMB_FTYPE_PNG;
-               ibuf->foptions.quality = 15; /* the 15 means, set compression to low ratio but not time consuming */
-               ibuf->channels = 4;  /* float option, is set to other values when buffers get assigned */
-               ibuf->ppm[0] = ibuf->ppm[1] = IMB_DPI_DEFAULT / 0.0254f; /* IMB_DPI_DEFAULT -> pixels-per-meter */
-
-               if (flags & IB_rect) {
-                       if (imb_addrectImBuf(ibuf) == false) {
-                               IMB_freeImBuf(ibuf);
-                               return NULL;
-                       }
+               if (!IMB_initImBuf(ibuf, x, y, planes, flags)) {
+                       IMB_freeImBuf(ibuf);
+                       return NULL;
                }
-               
-               if (flags & IB_rectfloat) {
-                       if (imb_addrectfloatImBuf(ibuf) == false) {
-                               IMB_freeImBuf(ibuf);
-                               return NULL;
-                       }
+       }
+
+       return (ibuf);
+}
+
+bool IMB_initImBuf(struct ImBuf *ibuf,
+                   unsigned int x, unsigned int y,
+                   unsigned char planes, unsigned int flags)
+{
+       memset(ibuf, 0, sizeof(ImBuf));
+
+       ibuf->x = x;
+       ibuf->y = y;
+       ibuf->planes = planes;
+       ibuf->ftype = IMB_FTYPE_PNG;
+       ibuf->foptions.quality = 15; /* the 15 means, set compression to low ratio but not time consuming */
+       ibuf->channels = 4;  /* float option, is set to other values when buffers get assigned */
+       ibuf->ppm[0] = ibuf->ppm[1] = IMB_DPI_DEFAULT / 0.0254f; /* IMB_DPI_DEFAULT -> pixels-per-meter */
+
+       if (flags & IB_rect) {
+               if (imb_addrectImBuf(ibuf) == false) {
+                       return false;
                }
-               
-               if (flags & IB_zbuf) {
-                       if (addzbufImBuf(ibuf) == false) {
-                               IMB_freeImBuf(ibuf);
-                               return NULL;
-                       }
+       }
+
+       if (flags & IB_rectfloat) {
+               if (imb_addrectfloatImBuf(ibuf) == false) {
+                       return false;
                }
-               
-               if (flags & IB_zbuffloat) {
-                       if (addzbuffloatImBuf(ibuf) == false) {
-                               IMB_freeImBuf(ibuf);
-                               return NULL;
-                       }
+       }
+
+       if (flags & IB_zbuf) {
+               if (addzbufImBuf(ibuf) == false) {
+                       return false;
                }
+       }
 
-               /* assign default spaces */
-               colormanage_imbuf_set_default_spaces(ibuf);
+       if (flags & IB_zbuffloat) {
+               if (addzbuffloatImBuf(ibuf) == false) {
+                       return false;
+               }
        }
-       return (ibuf);
+
+       /* assign default spaces */
+       colormanage_imbuf_set_default_spaces(ibuf);
+
+       return true;
 }
 
 /* does no zbuffers? */
index c7b347cb20cfe0c1b880f45a404919c547ae16ee..3360fd7548e90dd19836f69ca317d7efd4205b0f 100644 (file)
@@ -693,6 +693,69 @@ void IMB_rectblend(ImBuf *dbuf, ImBuf *obuf, ImBuf *sbuf, unsigned short *dmask,
        }
 }
 
+typedef struct RectBlendThreadData {
+       ImBuf *dbuf, *obuf, *sbuf;
+       unsigned short *dmask, *curvemask, *texmask;
+       float mask_max;
+       int destx, desty, origx, origy;
+       int srcx, srcy, width;
+       IMB_BlendMode mode;
+       bool accumulate;
+} RectBlendThreadData;
+
+static void rectblend_thread_do(void *data_v,
+                                int start_scanline,
+                                int num_scanlines)
+{
+       RectBlendThreadData *data = (RectBlendThreadData *)data_v;
+       IMB_rectblend(data->dbuf, data->obuf, data->sbuf,
+                     data->dmask, data->curvemask, data->texmask,
+                     data->mask_max,
+                     data->destx,
+                     data->desty + start_scanline,
+                     data->origx,
+                     data->origy + start_scanline,
+                     data->srcx,
+                     data->srcy + start_scanline,
+                     data->width, num_scanlines,
+                     data->mode, data->accumulate);
+}
+
+void IMB_rectblend_threaded(ImBuf *dbuf, ImBuf *obuf, ImBuf *sbuf,
+                            unsigned short *dmask, unsigned short *curvemask,
+                            unsigned short *texmask, float mask_max,
+                            int destx, int desty, int origx, int origy,
+                            int srcx, int srcy, int width, int height,
+                            IMB_BlendMode mode, bool accumulate)
+{
+       if (((size_t)width) * height < 64 * 64) {
+               IMB_rectblend(dbuf, obuf, sbuf, dmask, curvemask, texmask,
+                             mask_max, destx,  desty, origx, origy,
+                             srcx, srcy, width, height, mode, accumulate);
+       }
+       else {
+               RectBlendThreadData data;
+               data.dbuf = dbuf;
+               data.obuf = obuf;
+               data.sbuf = sbuf;
+               data.dmask = dmask;
+               data.curvemask = curvemask;
+               data.texmask = texmask;
+               data.mask_max = mask_max;
+               data.destx = destx;
+               data.desty = desty;
+               data.origx = origx;
+               data.origy = origy;
+               data.srcx = srcx;
+               data.srcy = srcy;
+               data.width = width;
+               data.mode = mode;
+               data.accumulate = accumulate;
+               IMB_processor_apply_threaded_scanlines(
+                   height, rectblend_thread_do, &data);
+       }
+}
+
 /* fill */
 
 void IMB_rectfill(ImBuf *drect, const float col[4])