Attempt to fix #35057 and #35372: slow texture painting performance.
authorBrecht Van Lommel <brechtvanlommel@pandora.be>
Wed, 15 May 2013 14:37:05 +0000 (14:37 +0000)
committerBrecht Van Lommel <brechtvanlommel@pandora.be>
Wed, 15 May 2013 14:37:05 +0000 (14:37 +0000)
After the paint refactoring for 2.67, the OpenGL texture was getting updated for
every stroke point, rather than once for every redraw. With a small brush radius
and low spacing the number of stroke points can be quite large, which might have
a big performance impact depending on the graphics card / drivers.

Also for 2D image paint, avoid redrawing the button panels and properties editor
during painting.

There is another possible cause for slowdowns with 3D texture painting which was
not fixed. Projection painting is creating and destroying threads for every stroke
point. Depending on the CPU/OS there might be a lot of overhead in doing that if
the brush size is small.

source/blender/editors/sculpt_paint/paint_image.c
source/blender/editors/sculpt_paint/paint_image_2d.c
source/blender/editors/sculpt_paint/paint_image_proj.c
source/blender/editors/sculpt_paint/paint_intern.h
source/blender/editors/sculpt_paint/paint_stroke.c
source/blender/editors/sculpt_paint/paint_vertex.c
source/blender/editors/sculpt_paint/sculpt.c
source/blender/editors/space_buttons/space_buttons.c
source/blender/editors/space_image/space_image.c
source/blender/windowmanager/WM_types.h

index 6547b33611916750c7f0f3d46974c1407c462146..7e721277f9152f556faff4fc7efd872172f1ad87 100644 (file)
@@ -478,22 +478,6 @@ void paint_brush_exit_tex(Brush *brush)
 }
 
 
-static void paint_redraw(const bContext *C, PaintOperation *pop, int final)
-{
-       if (pop->mode == PAINT_MODE_2D) {
-               paint_2d_redraw(C, pop->custom_paint, final);
-       }
-       else {
-               if (final) {
-                       /* compositor listener deals with updating */
-                       WM_event_add_notifier(C, NC_IMAGE | NA_EDITED, NULL);
-               }
-               else {
-                       ED_region_tag_redraw(CTX_wm_region(C));
-               }
-       }
-}
-
 static PaintOperation *texture_paint_init(bContext *C, wmOperator *op, const wmEvent *event)
 {
        Scene *scene = CTX_data_scene(C);
@@ -547,7 +531,7 @@ static void paint_stroke_update_step(bContext *C, struct PaintStroke *stroke, Po
 
        float mouse[2];
        float pressure;
-       int redraw, eraser;
+       int eraser;
 
        RNA_float_get_array(itemptr, "mouse", mouse);
        pressure = RNA_float_get(itemptr, "pressure");
@@ -559,10 +543,10 @@ static void paint_stroke_update_step(bContext *C, struct PaintStroke *stroke, Po
                BKE_brush_size_set(scene, brush, max_ff(1.0f, startsize * pressure));
 
        if (pop->mode == PAINT_MODE_3D_PROJECT) {
-               redraw = paint_proj_stroke(C, pop->custom_paint, pop->prevmouse, mouse);
+               paint_proj_stroke(C, pop->custom_paint, pop->prevmouse, mouse);
        }
        else {
-               redraw = paint_2d_stroke(pop->custom_paint, pop->prevmouse, mouse, eraser);
+               paint_2d_stroke(pop->custom_paint, pop->prevmouse, mouse, eraser);
        }
 
        pop->prevmouse[0] = mouse[0];
@@ -571,11 +555,18 @@ static void paint_stroke_update_step(bContext *C, struct PaintStroke *stroke, Po
        /* restore brush values */
        BKE_brush_alpha_set(scene, brush, startalpha);
        BKE_brush_size_set(scene, brush, startsize);
+}
 
+static void paint_stroke_redraw(const bContext *C, struct PaintStroke *stroke, bool final)
+{
+       PaintOperation *pop = paint_stroke_mode_data(stroke);
 
-       if (redraw)
-               paint_redraw(C, pop, 0);
-
+       if (pop->mode == PAINT_MODE_3D_PROJECT) {
+               paint_proj_redraw(C, pop->custom_paint, final);
+       }
+       else {
+               paint_2d_redraw(C, pop->custom_paint, final);
+       }
 }
 
 static void paint_stroke_done(const bContext *C, struct PaintStroke *stroke)
@@ -584,8 +575,6 @@ static void paint_stroke_done(const bContext *C, struct PaintStroke *stroke)
        ToolSettings *settings = scene->toolsettings;
        PaintOperation *pop = paint_stroke_mode_data(stroke);
 
-       paint_redraw(C, pop, 1);
-
        settings->imapaint.flag &= ~IMAGEPAINT_DRAWING;
 
        if (pop->mode == PAINT_MODE_3D_PROJECT) {
@@ -629,6 +618,7 @@ static int paint_invoke(bContext *C, wmOperator *op, const wmEvent *event)
 
        stroke = op->customdata = paint_stroke_new(C, NULL, paint_stroke_test_start,
                                          paint_stroke_update_step,
+                                         paint_stroke_redraw,
                                          paint_stroke_done, event->type);
        paint_stroke_set_mode_data(stroke, pop);
        /* add modal handler */
index 956dcc858f84e6736a111b92a83b9c4e38f3018d..640796fa26cad6079543e0a99de1c91278174530 100644 (file)
@@ -153,6 +153,8 @@ typedef struct ImagePaintState {
        int faceindex;
        float uv[2];
        int do_facesel;
+
+       bool need_redraw;
 } ImagePaintState;
 
 
@@ -1016,17 +1018,16 @@ static void paint_2d_canvas_free(ImagePaintState *s)
                image_undo_remove_masks();
 }
 
-int paint_2d_stroke(void *ps, const float prev_mval[2], const float mval[2], int eraser)
+void paint_2d_stroke(void *ps, const float prev_mval[2], const float mval[2], int eraser)
 {
        float newuv[2], olduv[2];
-       int redraw = 0;
        ImagePaintState *s = ps;
        BrushPainter *painter = s->painter;
        ImBuf *ibuf = BKE_image_acquire_ibuf(s->image, s->sima ? &s->sima->iuser : NULL, NULL);
        const bool is_data = (ibuf && ibuf->colormanage_flag & IMB_COLORMANAGE_IS_DATA);
 
        if (!ibuf)
-               return 0;
+               return;
 
        s->blend = s->brush->blend;
        if (eraser)
@@ -1064,19 +1065,10 @@ int paint_2d_stroke(void *ps, const float prev_mval[2], const float mval[2], int
 
        brush_painter_2d_refresh_cache(s, painter, newuv);
 
-       if (paint_2d_op(s, painter->cache.ibuf, painter->cache.mask, olduv, newuv)) {
-               imapaint_image_update(s->sima, s->image, ibuf, false);
-               BKE_image_release_ibuf(s->image, ibuf, NULL);
-               redraw |= 1;
-       }
-       else {
-               BKE_image_release_ibuf(s->image, ibuf, NULL);
-       }
-
-       if (redraw)
-               imapaint_clear_partial_redraw();
+       if (paint_2d_op(s, painter->cache.ibuf, painter->cache.mask, olduv, newuv))
+               s->need_redraw = true;
 
-       return redraw;
+       BKE_image_release_ibuf(s->image, ibuf, NULL);
 }
 
 void *paint_2d_new_stroke(bContext *C, wmOperator *op)
@@ -1116,10 +1108,24 @@ void *paint_2d_new_stroke(bContext *C, wmOperator *op)
        return s;
 }
 
-void paint_2d_redraw(const bContext *C, void *ps, int final)
+void paint_2d_redraw(const bContext *C, void *ps, bool final)
 {
        ImagePaintState *s = ps;
 
+       if (s->need_redraw) {
+               ImBuf *ibuf = BKE_image_acquire_ibuf(s->image, s->sima ? &s->sima->iuser : NULL, NULL);
+
+               imapaint_image_update(s->sima, s->image, ibuf, false);
+               imapaint_clear_partial_redraw();
+
+               BKE_image_release_ibuf(s->image, ibuf, NULL);
+
+               s->need_redraw = false;
+       }
+       else if (!final) {
+               return;
+       }
+
        if (final) {
                if (s->image && !(s->sima && s->sima->lock))
                        GPU_free_image(s->image);
@@ -1131,7 +1137,7 @@ void paint_2d_redraw(const bContext *C, void *ps, int final)
                if (!s->sima || !s->sima->lock)
                        ED_region_tag_redraw(CTX_wm_region(C));
                else
-                       WM_event_add_notifier(C, NC_IMAGE | NA_EDITED, s->image);
+                       WM_event_add_notifier(C, NC_IMAGE | NA_PAINTING, s->image);
        }
 }
 
index d7cea395409b2aca3f7dd015ad613af051ee8900..dff033b361313435f4b3be6102af53ad9cfe5589 100644 (file)
@@ -264,12 +264,14 @@ typedef struct ProjPaintState {
        Image *reproject_image;
        ImBuf *reproject_ibuf;
 
-
        /* threads */
        int thread_tot;
        int bucketMin[2];
        int bucketMax[2];
        int context_bucket_x, context_bucket_y; /* must lock threads while accessing these */
+
+       /* redraw */
+       bool need_redraw;
 } ProjPaintState;
 
 typedef union pixelPointer {
@@ -4044,10 +4046,10 @@ static int project_paint_op(void *state, const float lastpos[2], const float pos
 }
 
 
-int paint_proj_stroke(bContext *C, void *pps, const float prev_pos[2], const float pos[2])
+void paint_proj_stroke(bContext *C, void *pps, const float prev_pos[2], const float pos[2])
 {
        ProjPaintState *ps = pps;
-       int a, redraw;
+       int a;
 
        /* clone gets special treatment here to avoid going through image initialization */
        if (ps->tool == PAINT_TOOL_CLONE && ps->mode == BRUSH_STROKE_INVERT) {
@@ -4059,22 +4061,21 @@ int paint_proj_stroke(bContext *C, void *pps, const float prev_pos[2], const flo
                view3d_operator_needs_opengl(C);
 
                if (!ED_view3d_autodist(scene, ps->ar, v3d, mval_i, cursor, false))
-                       return 0;
+                       return;
 
                ED_region_tag_redraw(ps->ar);
 
-               return 0;
+               return;
        }
 
-       for (a = 0; a < ps->image_tot; a++)
-               partial_redraw_array_init(ps->projImages[a].partRedrawRect);
-
-       redraw = project_paint_op(ps, prev_pos, pos) ? 1 : 0;
-
-       if (project_image_refresh_tagged(ps))
-               return redraw;
+       /* continue adding to existing partial redraw rects until redraw */
+       if (!ps->need_redraw) {
+               for (a = 0; a < ps->image_tot; a++)
+                       partial_redraw_array_init(ps->projImages[a].partRedrawRect);
+       }
 
-       return 0;
+       if (project_paint_op(ps, prev_pos, pos))
+               ps->need_redraw = true;
 }
 
 
@@ -4195,6 +4196,28 @@ void *paint_proj_new_stroke(bContext *C, Object *ob, const float mouse[2], int m
        return ps;
 }
 
+void paint_proj_redraw(const bContext *C, void *pps, bool final)
+{
+       ProjPaintState *ps = pps;
+
+       if (ps->need_redraw) {
+               project_image_refresh_tagged(ps);
+
+               ps->need_redraw = false;
+       }
+       else if (!final) {
+               return;
+       }
+
+       if (final) {
+               /* compositor listener deals with updating */
+               WM_event_add_notifier(C, NC_IMAGE | NA_EDITED, NULL);
+       }
+       else {
+               ED_region_tag_redraw(CTX_wm_region(C));
+       }
+}
+
 void paint_proj_stroke_done(void *pps)
 {
        ProjPaintState *ps = pps;
index a42304397377220d319165014479aeb5bae61f8f..81c33338b5c5f24d91fbd1b0413bf9ad70080945 100644 (file)
@@ -60,11 +60,13 @@ enum PaintMode;
 typedef int (*StrokeGetLocation)(struct bContext *C, float location[3], const float mouse[2]);
 typedef int (*StrokeTestStart)(struct bContext *C, struct wmOperator *op, const float mouse[2]);
 typedef void (*StrokeUpdateStep)(struct bContext *C, struct PaintStroke *stroke, struct PointerRNA *itemptr);
+typedef void (*StrokeRedraw)(const struct bContext *C, struct PaintStroke *stroke, bool final);
 typedef void (*StrokeDone)(const struct bContext *C, struct PaintStroke *stroke);
 
 struct PaintStroke *paint_stroke_new(struct bContext *C,
                                      StrokeGetLocation get_location, StrokeTestStart test_start,
-                                     StrokeUpdateStep update_step, StrokeDone done, int event_type);
+                                     StrokeUpdateStep update_step, StrokeRedraw redraw,
+                                     StrokeDone done, int event_type);
 void paint_stroke_data_free(struct wmOperator *op);
 
 bool paint_space_stroke_enabled(struct Brush *br, enum PaintMode mode);
@@ -139,11 +141,12 @@ void imapaint_dirty_region(struct Image *ima, struct ImBuf *ibuf, int x, int y,
 void imapaint_region_tiles(struct ImBuf *ibuf, int x, int y, int w, int h, int *tx, int *ty, int *tw, int *th);
 int get_imapaint_zoom(struct bContext *C, float *zoomx, float *zoomy);
 void *paint_2d_new_stroke(struct bContext *, struct wmOperator *);
-void paint_2d_redraw(const bContext *C, void *ps, int final);
+void paint_2d_redraw(const bContext *C, void *ps, bool final);
 void paint_2d_stroke_done(void *ps);
-int paint_2d_stroke(void *ps, const float prev_mval[2], const float mval[2], int eraser);
+void paint_2d_stroke(void *ps, const float prev_mval[2], const float mval[2], int eraser);
 void *paint_proj_new_stroke(struct bContext *C, struct Object *ob, const float mouse[2], int mode);
-int paint_proj_stroke(struct bContext *C, void *ps, const float prevmval_i[2], const float mval_i[2]);
+void paint_proj_stroke(struct bContext *C, void *ps, const float prevmval_i[2], const float mval_i[2]);
+void paint_proj_redraw(const bContext *C, void *pps, bool final);
 void paint_proj_stroke_done(void *ps);
 void paint_brush_init_tex(struct Brush *brush);
 void paint_brush_exit_tex(struct Brush *brush);
index 95cbc4b78b4ebef536ec5b9a484a476f7e64052f..90c0d7e3a7a893bfdafd920f04a5534ef6ba463b 100644 (file)
@@ -101,6 +101,7 @@ typedef struct PaintStroke {
        StrokeGetLocation get_location;
        StrokeTestStart test_start;
        StrokeUpdateStep update_step;
+       StrokeRedraw redraw;
        StrokeDone done;
 } PaintStroke;
 
@@ -425,6 +426,7 @@ PaintStroke *paint_stroke_new(bContext *C,
                               StrokeGetLocation get_location,
                               StrokeTestStart test_start,
                               StrokeUpdateStep update_step,
+                              StrokeRedraw redraw,
                               StrokeDone done, int event_type)
 {
        PaintStroke *stroke = MEM_callocN(sizeof(PaintStroke), "PaintStroke");
@@ -437,6 +439,7 @@ PaintStroke *paint_stroke_new(bContext *C,
        stroke->get_location = get_location;
        stroke->test_start = test_start;
        stroke->update_step = update_step;
+       stroke->redraw = redraw;
        stroke->done = done;
        stroke->event_type = event_type; /* for modal, return event */
        
@@ -456,8 +459,13 @@ static void stroke_done(struct bContext *C, struct wmOperator *op)
 {
        struct PaintStroke *stroke = op->customdata;
 
-       if (stroke->stroke_started && stroke->done)
-               stroke->done(C, stroke);
+       if (stroke->stroke_started) {
+               if (stroke->redraw)
+                       stroke->redraw(C, stroke, true);
+
+               if (stroke->done)
+                       stroke->done(C, stroke);
+       }
 
        if (stroke->timer) {
                WM_event_remove_timer(
@@ -611,6 +619,7 @@ int paint_stroke_modal(bContext *C, wmOperator *op, const wmEvent *event)
        float mouse[2];
        int first = 0;
        float zoomx, zoomy;
+       bool redraw = false;
 
        paint_stroke_add_sample(p, stroke, event->mval[0], event->mval[1]);
        paint_stroke_sample_average(stroke, &sample_average);
@@ -661,17 +670,14 @@ int paint_stroke_modal(bContext *C, wmOperator *op, const wmEvent *event)
                if (stroke->stroke_started) {
                        if (paint_smooth_stroke(stroke, mouse, &sample_average, mode)) {
                                if (paint_space_stroke_enabled(stroke->brush, mode)) {
-                                       if (!paint_space_stroke(C, op, event, mouse)) {
-                                               //ED_region_tag_redraw(ar);
-                                       }
+                                       if (paint_space_stroke(C, op, event, mouse))
+                                               redraw = true;
                                }
                                else {
                                        paint_brush_stroke_add_step(C, op, event, mouse);
+                                       redraw = true;
                                }
                        }
-                       else {
-                               ; //ED_region_tag_redraw(ar);
-                       }
                }
        }
 
@@ -684,7 +690,14 @@ int paint_stroke_modal(bContext *C, wmOperator *op, const wmEvent *event)
            !(stroke->brush->flag & BRUSH_SMOOTH_STROKE))
        {
                paint_brush_stroke_add_step(C, op, event, mouse);
+               redraw = true;
        }
+
+       /* do updates for redraw. if event is inbetween mousemove there are more
+        * coming, so postpone potentially slow redraw updates until all are done */
+       if (event->type != INBETWEEN_MOUSEMOVE)
+               if (redraw && stroke->redraw)
+                       stroke->redraw(C, stroke, false);
        
        return OPERATOR_RUNNING_MODAL;
 }
index 034110a29bee07115f2f58659efb16a2002879c3..3a24fcd652f8168d328d3e9a9ef5b1d6976b1005 100644 (file)
@@ -2561,7 +2561,7 @@ static int wpaint_invoke(bContext *C, wmOperator *op, const wmEvent *event)
        int retval;
 
        op->customdata = paint_stroke_new(C, NULL, wpaint_stroke_test_start,
-                                         wpaint_stroke_update_step,
+                                         wpaint_stroke_update_step, NULL,
                                          wpaint_stroke_done, event->type);
        
        /* add modal handler */
@@ -3091,7 +3091,7 @@ static int vpaint_invoke(bContext *C, wmOperator *op, const wmEvent *event)
        int retval;
 
        op->customdata = paint_stroke_new(C, NULL, vpaint_stroke_test_start,
-                                         vpaint_stroke_update_step,
+                                         vpaint_stroke_update_step, NULL,
                                          vpaint_stroke_done, event->type);
        
        /* add modal handler */
index a7bc58daf546a7b6d86ee99e966b3f25bf209014..fce520d04fa47cd815a96315470c7fa5e87b751e 100644 (file)
@@ -4399,7 +4399,7 @@ static int sculpt_brush_stroke_invoke(bContext *C, wmOperator *op, const wmEvent
 
        stroke = paint_stroke_new(C, sculpt_stroke_get_location,
                                  sculpt_stroke_test_start,
-                                 sculpt_stroke_update_step,
+                                 sculpt_stroke_update_step, NULL,
                                  sculpt_stroke_done, event->type);
 
        op->customdata = stroke;
@@ -4428,7 +4428,7 @@ static int sculpt_brush_stroke_exec(bContext *C, wmOperator *op)
                return OPERATOR_CANCELLED;
 
        op->customdata = paint_stroke_new(C, sculpt_stroke_get_location, sculpt_stroke_test_start,
-                                         sculpt_stroke_update_step, sculpt_stroke_done, 0);
+                                         sculpt_stroke_update_step, NULL, sculpt_stroke_done, 0);
 
        /* frees op->customdata */
        paint_stroke_exec(C, op);
index 34604f9d5f828d1f5c658e03c9973a741613610b..02b06e08eed17675263a1acf0f13b8b2a25e52b5 100644 (file)
@@ -346,8 +346,10 @@ static void buttons_area_listener(ScrArea *sa, wmNotifier *wmn)
                        break;
                case NC_TEXTURE:
                case NC_IMAGE:
-                       ED_area_tag_redraw(sa);
-                       sbuts->preview = 1;
+                       if (wmn->action != NA_PAINTING) {
+                               ED_area_tag_redraw(sa);
+                               sbuts->preview = 1;
+                       }
                        break;
                case NC_SPACE:
                        if (wmn->data == ND_SPACE_PROPERTIES)
index 30564d52343d10f5f4bf3441e9129a129e25db87..679c1632eb1f9687c5c3ca347f695e45aedf3f1a 100644 (file)
@@ -454,9 +454,11 @@ static void image_listener(ScrArea *sa, wmNotifier *wmn)
                        break;
                case NC_IMAGE:
                        if (wmn->reference == sima->image || !wmn->reference) {
-                               image_scopes_tag_refresh(sa);
-                               ED_area_tag_refresh(sa);
-                               ED_area_tag_redraw(sa);
+                               if (wmn->action != NA_PAINTING) {
+                                       image_scopes_tag_refresh(sa);
+                                       ED_area_tag_refresh(sa);
+                                       ED_area_tag_redraw(sa);
+                               }
                        }
                        break;
                case NC_SPACE:
@@ -737,6 +739,10 @@ static void image_main_area_listener(ARegion *ar, wmNotifier *wmn)
                        if (wmn->action == NA_EDITED)
                                ED_region_tag_redraw(ar);
                        break;
+               case NC_IMAGE:
+                       if (wmn->action == NA_PAINTING)
+                               ED_region_tag_redraw(ar);
+                       break;
        }
 }
 
@@ -829,7 +835,8 @@ static void image_scope_area_listener(ARegion *ar, wmNotifier *wmn)
                        }
                        break;
                case NC_IMAGE:
-                       ED_region_tag_redraw(ar);
+                       if (wmn->action != NA_PAINTING)
+                               ED_region_tag_redraw(ar);
                        break;
                case NC_NODE:
                        ED_region_tag_redraw(ar);
index c823810fc757a4708dbca8577dd44650388f3f5e..cb0600771665282ff4eb4320d95d844a01ed2a96 100644 (file)
@@ -380,6 +380,7 @@ typedef struct wmNotifier {
 #define NA_REMOVED                     4
 #define NA_RENAME                      5
 #define NA_SELECTED                    6
+#define NA_PAINTING                    7
 
 /* ************** Gesture Manager data ************** */