diff options
author | Campbell Barton <ideasman42@gmail.com> | 2008-11-12 08:56:37 +0300 |
---|---|---|
committer | Campbell Barton <ideasman42@gmail.com> | 2008-11-12 08:56:37 +0300 |
commit | c63c99d49ec38c722730624542b0252d16b759a6 (patch) | |
tree | f3a4820d0464b42daa85030259efc39066fd14eb | |
parent | 58b861170c9c174f4fd85171955fe6ce4d5b7dba (diff) |
Option to have painting multi-threaded.
Each thread process the next free bucket the brush is over until they are all done.
-rw-r--r-- | source/blender/src/imagepaint.c | 647 |
1 files changed, 436 insertions, 211 deletions
diff --git a/source/blender/src/imagepaint.c b/source/blender/src/imagepaint.c index af2e3141a32..b4cdda4ab71 100644 --- a/source/blender/src/imagepaint.c +++ b/source/blender/src/imagepaint.c @@ -49,6 +49,7 @@ #include "BLI_linklist.h" #include "BLI_memarena.h" #include "PIL_time.h" +#include "BLI_threads.h" #include "IMB_imbuf.h" #include "IMB_imbuf_types.h" @@ -138,6 +139,7 @@ typedef struct ImagePaintState { //#define PROJ_DEBUG_PAINT 1 #define PROJ_DEBUG_NOSCANLINE 1 //#define PROJ_DEBUG_NOSEAMBLEED 1 +//#define PROJ_DEBUG_PRINT_THREADS 1 /* projectFaceSeamFlags options */ //#define PROJ_FACE_IGNORE 1<<0 /* When the face is hidden, backfacing or occluded */ @@ -178,6 +180,7 @@ typedef struct ProjectPaintState { /* projection painting only */ MemArena *projectArena; /* use for alocating many pixel structs and link-lists */ + MemArena *projectArena_mt[BLENDER_MAX_THREADS]; /* Same as above but use for multithreading */ LinkNode **projectBuckets; /* screen sized 2D array, each pixel has a linked list of ProjectPixel's */ LinkNode **projectFaces; /* projectBuckets alligned array linkList of faces overlapping each bucket */ char *projectBucketFlags; /* store if the bucks have been initialized */ @@ -193,7 +196,6 @@ typedef struct ProjectPaintState { ImBuf **projectImBufs; /* array of imbufs we are painting onto while, use so we can get the rect and rect_float quickly */ int projectImageTotal; /* size of projectImages array */ - int imaContextIndex; /* current image, use for context switching */ float (*projectVertScreenCos)[4]; /* verts projected into floating point screen space */ @@ -216,6 +218,12 @@ typedef struct ProjectPaintState { float viewMax2D[2]; float viewWidth; /* Calculated from viewMin2D & viewMax2D */ float viewHeight; + + /* threads */ + int thread_tot; + int min_bucket[2]; + int max_bucket[2]; + int context_bucket_x, context_bucket_y; /* must lock threads while accessing these */ } ProjectPaintState; #ifndef PROJ_DEBUG_NOSCANLINE @@ -1201,6 +1209,8 @@ static void uv_image_outset(float (*orig_uv)[2], float (*outset_uv)[2], float sc /* * Be tricky with flags, first 4 bits are PROJ_FACE_SEAM1 to 4, last 4 bits are PROJ_FACE_NOSEAM1 to 4 * 1<<i - where i is (0-3) + * + * If we're multithreadng, make sure threads are locked when this is called */ static void project_face_seams_init(ProjectPaintState *ps, int face_index, int is_quad) { @@ -1280,7 +1290,7 @@ static screen_px_from_persp( /* Only run this function once for new ProjectPixelClone's */ #define pixel_size 4 -static void project_paint_uvpixel_init(ProjectPaintState *ps, ImBuf *ibuf, int x, int y, int bucket_index, int face_index, float pixelScreenCo[4]) +static void project_paint_uvpixel_init(ProjectPaintState *ps, int thread_index, ImBuf *ibuf, int x, int y, int bucket_index, int face_index, int image_index, float pixelScreenCo[4]) { ProjectPixel *projPixel; short size; @@ -1308,7 +1318,7 @@ static void project_paint_uvpixel_init(ProjectPaintState *ps, ImBuf *ibuf, int x size = sizeof(ProjectPixel); } - projPixel = (ProjectPixel *)BLI_memarena_alloc(ps->projectArena, size); + projPixel = (ProjectPixel *)BLI_memarena_alloc(ps->projectArena_mt[thread_index], size); if (ibuf->rect_float) { projPixel->pixel = (void *) ((( float * ) ibuf->rect_float) + (( x + y * ibuf->x ) * pixel_size)); @@ -1345,12 +1355,12 @@ static void project_paint_uvpixel_init(ProjectPaintState *ps, ImBuf *ibuf, int x if (ibuf->rect_float) ((float *)projPixel->pixel)[1] = 0; else ((char *)projPixel->pixel)[1] = 0; #endif - projPixel->image_index = ps->imaContextIndex; + projPixel->image_index = image_index; BLI_linklist_prepend_arena( &ps->projectBuckets[ bucket_index ], projPixel, - ps->projectArena + ps->projectArena_mt[thread_index] ); } } @@ -1493,8 +1503,50 @@ static void scale_tri(float *origCos[4], float insetCos[4][3], float inset) VecAddf(insetCos[2], insetCos[2], cent); } -/**/ -static void project_paint_face_init(ProjectPaintState *ps, int bucket_index, int face_index, float bucket_bounds[4], ImBuf *ibuf) +static void rect_to_uvspace( + ProjectPaintState *ps, float bucket_bounds[4], + float *v1coSS, float *v2coSS, float *v3coSS, + float *uv1co, float *uv2co, float *uv3co, + float bucket_bounds_uv[4][2] + ) +{ + float uv[2]; + float w[3]; + + /* get the UV space bounding box */ + uv[0] = bucket_bounds[PROJ_BUCKET_RIGHT]; + uv[1] = bucket_bounds[PROJ_BUCKET_BOTTOM]; + if (ps->projectIsOrtho) BarycentricWeights2f(v1coSS, v2coSS, v3coSS, uv, w); + else BarycentricWeightsPersp2f(v1coSS, v2coSS, v3coSS, uv, w); + bucket_bounds_uv[0][0] = uv1co[0]*w[0] + uv2co[0]*w[1] + uv3co[0]*w[2]; + bucket_bounds_uv[0][1] = uv1co[1]*w[0] + uv2co[1]*w[1] + uv3co[1]*w[2]; + + //uv[0] = bucket_bounds[PROJ_BUCKET_RIGHT]; // set above + uv[1] = bucket_bounds[PROJ_BUCKET_TOP]; + if (ps->projectIsOrtho) BarycentricWeights2f(v1coSS, v2coSS, v3coSS, uv, w); + else BarycentricWeightsPersp2f(v1coSS, v2coSS, v3coSS, uv, w); + bucket_bounds_uv[1][0] = uv1co[0]*w[0] + uv2co[0]*w[1] + uv3co[0]*w[2]; + bucket_bounds_uv[1][1] = uv1co[1]*w[0] + uv2co[1]*w[1] + uv3co[1]*w[2]; + + + uv[0] = bucket_bounds[PROJ_BUCKET_LEFT]; + //uv[1] = bucket_bounds[PROJ_BUCKET_TOP]; // set above + if (ps->projectIsOrtho) BarycentricWeights2f(v1coSS, v2coSS, v3coSS, uv, w); + else BarycentricWeightsPersp2f(v1coSS, v2coSS, v3coSS, uv, w); + bucket_bounds_uv[2][0] = uv1co[0]*w[0] + uv2co[0]*w[1] + uv3co[0]*w[2]; + bucket_bounds_uv[2][1] = uv1co[1]*w[0] + uv2co[1]*w[1] + uv3co[1]*w[2]; + + //uv[0] = bucket_bounds[PROJ_BUCKET_LEFT]; // set above + uv[1] = bucket_bounds[PROJ_BUCKET_BOTTOM]; + if (ps->projectIsOrtho) BarycentricWeights2f(v1coSS, v2coSS, v3coSS, uv, w); + else BarycentricWeightsPersp2f(v1coSS, v2coSS, v3coSS, uv, w); + bucket_bounds_uv[3][0] = uv1co[0]*w[0] + uv2co[0]*w[1] + uv3co[0]*w[2]; + bucket_bounds_uv[3][1] = uv1co[1]*w[0] + uv2co[1]*w[1] + uv3co[1]*w[2]; +} + + +/* initialize pixels from this face where it intersects with the bucket_index, initialize pixels for removing seams */ +static void project_paint_face_init(ProjectPaintState *ps, int thread_index, int bucket_index, int face_index, int image_index, float bucket_bounds[4], ImBuf *ibuf) { /* Projection vars, to get the 3D locations into screen space */ @@ -1508,7 +1560,7 @@ static void project_paint_face_init(ProjectPaintState *ps, int bucket_index, int int min_px[2], max_px[2]; /* UV Bounds converted to int's for pixel */ int min_px_tf[2], max_px_tf[2]; /* UV Bounds converted to int's for pixel */ - int min_px_bucket[2][2], max_px_bucket[2][2]; /* Bucket Bounds converted to int's for pixel */ + int min_px_bucket[2], max_px_bucket[2]; /* Bucket Bounds converted to int's for pixel */ float *v1coSS, *v2coSS, *v3coSS, *v4coSS; /* vert co screen-space, these will be assigned to mf->v1,2,3 or mf->v1,3,4 */ float *v1co, *v2co, *v3co; /* vert co */ float *vCo[4]; /* vertex screenspace coords */ @@ -1551,43 +1603,15 @@ static void project_paint_face_init(ProjectPaintState *ps, int bucket_index, int v2coSS = ps->projectVertScreenCos[ (*(&mf->v1 + i2)) ]; v3coSS = ps->projectVertScreenCos[ (*(&mf->v1 + i3)) ]; - /* get the UV space bounding box */ - uv[0] = bucket_bounds[PROJ_BUCKET_RIGHT]; - uv[1] = bucket_bounds[PROJ_BUCKET_BOTTOM]; - if (ps->projectIsOrtho) BarycentricWeights2f(v1coSS, v2coSS, v3coSS, uv, w); - else BarycentricWeightsPersp2f(v1coSS, v2coSS, v3coSS, uv, w); - bucket_bounds_uv[0][0] = uv1co[0]*w[0] + uv2co[0]*w[1] + uv3co[0]*w[2]; - bucket_bounds_uv[0][1] = uv1co[1]*w[0] + uv2co[1]*w[1] + uv3co[1]*w[2]; - - //uv[0] = bucket_bounds[PROJ_BUCKET_RIGHT]; // set above - uv[1] = bucket_bounds[PROJ_BUCKET_TOP]; - if (ps->projectIsOrtho) BarycentricWeights2f(v1coSS, v2coSS, v3coSS, uv, w); - else BarycentricWeightsPersp2f(v1coSS, v2coSS, v3coSS, uv, w); - bucket_bounds_uv[1][0] = uv1co[0]*w[0] + uv2co[0]*w[1] + uv3co[0]*w[2]; - bucket_bounds_uv[1][1] = uv1co[1]*w[0] + uv2co[1]*w[1] + uv3co[1]*w[2]; - - - uv[0] = bucket_bounds[PROJ_BUCKET_LEFT]; - //uv[1] = bucket_bounds[PROJ_BUCKET_TOP]; // set above - if (ps->projectIsOrtho) BarycentricWeights2f(v1coSS, v2coSS, v3coSS, uv, w); - else BarycentricWeightsPersp2f(v1coSS, v2coSS, v3coSS, uv, w); - bucket_bounds_uv[2][0] = uv1co[0]*w[0] + uv2co[0]*w[1] + uv3co[0]*w[2]; - bucket_bounds_uv[2][1] = uv1co[1]*w[0] + uv2co[1]*w[1] + uv3co[1]*w[2]; - - //uv[0] = bucket_bounds[PROJ_BUCKET_LEFT]; // set above - uv[1] = bucket_bounds[PROJ_BUCKET_BOTTOM]; - if (ps->projectIsOrtho) BarycentricWeights2f(v1coSS, v2coSS, v3coSS, uv, w); - else BarycentricWeightsPersp2f(v1coSS, v2coSS, v3coSS, uv, w); - bucket_bounds_uv[3][0] = uv1co[0]*w[0] + uv2co[0]*w[1] + uv3co[0]*w[2]; - bucket_bounds_uv[3][1] = uv1co[1]*w[0] + uv2co[1]*w[1] + uv3co[1]*w[2]; + rect_to_uvspace(ps, bucket_bounds, v1coSS, v2coSS, v3coSS, uv1co, uv2co, uv3co, bucket_bounds_uv); //printf("Bounds: %f | %f | %f | %f\n", bucket_bounds[0], bucket_bounds[1], bucket_bounds[2], bucket_bounds[3]); if ( uv_image_rect(uv1co, uv2co, uv3co, NULL, min_px_tf, max_px_tf, ibuf->x, ibuf->y, 0) && - uv_image_rect(bucket_bounds_uv[0], bucket_bounds_uv[1], bucket_bounds_uv[2], bucket_bounds_uv[3], min_px_bucket[i], max_px_bucket[i], ibuf->x, ibuf->y, 1) ) + uv_image_rect(bucket_bounds_uv[0], bucket_bounds_uv[1], bucket_bounds_uv[2], bucket_bounds_uv[3], min_px_bucket, max_px_bucket, ibuf->x, ibuf->y, 1) ) { - uvpixel_rect_intersect(min_px, max_px, min_px_bucket[i], max_px_bucket[i], min_px_tf, max_px_tf); + uvpixel_rect_intersect(min_px, max_px, min_px_bucket, max_px_bucket, min_px_tf, max_px_tf); /* clip face and */ @@ -1607,7 +1631,7 @@ static void project_paint_face_init(ProjectPaintState *ps, int bucket_index, int screen_px_from_persp(ps, uv, vCo[i1],vCo[i2],vCo[i3], uv1co,uv2co,uv3co, pixelScreenCo); } - project_paint_uvpixel_init(ps, ibuf, x,y, bucket_index, face_index, pixelScreenCo); + project_paint_uvpixel_init(ps, thread_index, ibuf, x,y, bucket_index, face_index, image_index, pixelScreenCo); } } } @@ -1615,21 +1639,30 @@ static void project_paint_face_init(ProjectPaintState *ps, int bucket_index, int } while(i--); #ifndef PROJ_DEBUG_NOSEAMBLEED if (ps->projectSeamBleed > 0.0) { + int face_seam_flag; + + if (ps->thread_tot > 1) + BLI_lock_thread(LOCK_CUSTOM1); /* Other threads could be modifying these vars */ - int flag = ps->projectFaceSeamFlags[face_index]; + face_seam_flag = ps->projectFaceSeamFlags[face_index]; /* are any of our edges un-initialized? */ - if ((flag & (PROJ_FACE_SEAM1|PROJ_FACE_NOSEAM1))==0 || - (flag & (PROJ_FACE_SEAM2|PROJ_FACE_NOSEAM2))==0 || - (flag & (PROJ_FACE_SEAM3|PROJ_FACE_NOSEAM3))==0 || - (flag & (PROJ_FACE_SEAM4|PROJ_FACE_NOSEAM4))==0 + if ((face_seam_flag & (PROJ_FACE_SEAM1|PROJ_FACE_NOSEAM1))==0 || + (face_seam_flag & (PROJ_FACE_SEAM2|PROJ_FACE_NOSEAM2))==0 || + (face_seam_flag & (PROJ_FACE_SEAM3|PROJ_FACE_NOSEAM3))==0 || + (face_seam_flag & (PROJ_FACE_SEAM4|PROJ_FACE_NOSEAM4))==0 ) { project_face_seams_init(ps, face_index, mf->v4); - flag = ps->projectFaceSeamFlags[face_index]; + face_seam_flag = ps->projectFaceSeamFlags[face_index]; //printf("seams - %d %d %d %d\n", flag&PROJ_FACE_SEAM1, flag&PROJ_FACE_SEAM2, flag&PROJ_FACE_SEAM3, flag&PROJ_FACE_SEAM4); } - if (flag & (PROJ_FACE_SEAM1|PROJ_FACE_SEAM2|PROJ_FACE_SEAM3|PROJ_FACE_SEAM4)) { + if ((face_seam_flag & (PROJ_FACE_SEAM1|PROJ_FACE_SEAM2|PROJ_FACE_SEAM3|PROJ_FACE_SEAM4))==0) { + + if (ps->thread_tot > 1) + BLI_unlock_thread(LOCK_CUSTOM1); /* Other threads could be modifying these vars */ + + } else { /* we have a seam - deal with it! */ /* Now create new UV's for the seam face */ @@ -1652,6 +1685,10 @@ static void project_paint_face_init(ProjectPaintState *ps, int bucket_index, int if (outset_uv[0][0]==MAXFLOAT) /* first time initialize */ uv_image_outset(tf->uv, outset_uv, ps->projectSeamBleed, ibuf->x, ibuf->y, mf->v4); + /* ps->projectFaceSeamUVs cant be modified when threading, now this is done we can unlock */ + if (ps->thread_tot > 1) + BLI_unlock_thread(LOCK_CUSTOM1); /* Other threads could be modifying these vars */ + vCoSS[0] = ps->projectVertScreenCos[ mf->v1 ]; vCoSS[1] = ps->projectVertScreenCos[ mf->v2 ]; vCoSS[2] = ps->projectVertScreenCos[ mf->v3 ]; @@ -1670,7 +1707,7 @@ static void project_paint_face_init(ProjectPaintState *ps, int bucket_index, int if (mf->v4) fidx2 = (fidx1==3) ? 0 : fidx1+1; /* next fidx in the face (0,1,2,3) -> (1,2,3,0) */ else fidx2 = (fidx1==2) ? 0 : fidx1+1; /* next fidx in the face (0,1,2) -> (1,2,0) */ - if ( (ps->projectFaceSeamFlags[face_index] & (1<<fidx1) ) && /* 1<<fidx1 -> PROJ_FACE_SEAM# */ + if ( (face_seam_flag & (1<<fidx1) ) && /* 1<<fidx1 -> PROJ_FACE_SEAM# */ line_clip_rect2f(bucket_bounds, vCoSS[fidx1], vCoSS[fidx2], bucket_clip_edges[0], bucket_clip_edges[1]) ) { @@ -1736,7 +1773,7 @@ static void project_paint_face_init(ProjectPaintState *ps, int bucket_index, int pixelScreenCo[2] = pixelScreenCo[2]/pixelScreenCo[3]; /* Use the depth for bucket point occlusion */ } - project_paint_uvpixel_init(ps, ibuf, x, y, bucket_index, face_index, pixelScreenCo); + project_paint_uvpixel_init(ps, thread_index, ibuf, x, y, bucket_index, face_index, image_index, pixelScreenCo); } } } @@ -1778,10 +1815,10 @@ static void project_bucket_bounds(ProjectPaintState *ps, int bucket_x, int bucke } /* have bucket_bounds as an arg so we dont need to give bucket_x/y the rect function need */ -static void project_paint_bucket_init(ProjectPaintState *ps, int bucket_index, float bucket_bounds[4]) +static void project_paint_bucket_init(ProjectPaintState *ps, int thread_index, int bucket_index, float bucket_bounds[4]) { LinkNode *node; - int face_index; + int face_index, image_index; ImBuf *ibuf; MTFace *tf; @@ -1797,23 +1834,23 @@ static void project_paint_bucket_init(ProjectPaintState *ps, int bucket_index, f if (tpage_last != tf->tpage) { tpage_last = tf->tpage; - ps->imaContextIndex = -1; /* sanity check */ + image_index = -1; /* sanity check */ for (tpage_index=0; tpage_index < ps->projectImageTotal; tpage_index++) { if (ps->projectImages[tpage_index] == tpage_last) { - ps->imaContextIndex = tpage_index; + image_index = tpage_index; break; } } - if (ps->imaContextIndex==-1) { + if (image_index==-1) { printf("Error, should never happen!\n"); return; } ibuf = BKE_image_get_ibuf(tpage_last, NULL); /* TODO - this may be slow */ } - project_paint_face_init(ps, bucket_index, face_index, bucket_bounds, ibuf); + project_paint_face_init(ps, thread_index, bucket_index, face_index, image_index, bucket_bounds, ibuf); node = node->next; } while (node); @@ -1896,7 +1933,9 @@ static void project_paint_delayed_face_init(ProjectPaintState *ps, MFace *mf, MT float min[2], max[2]; int bucket_min[2], bucket_max[2]; /* for ps->projectBuckets indexing */ int i, a, bucket_x, bucket_y, bucket_index; - + + int has_x_isect = -1, has_isect = -1; /* for early loop exit */ + INIT_MINMAX2(min,max); i = mf->v4 ? 3:2; @@ -1920,6 +1959,7 @@ static void project_paint_delayed_face_init(ProjectPaintState *ps, MFace *mf, MT project_paint_rect(ps, min, max, bucket_min, bucket_max); for (bucket_y = bucket_min[1]; bucket_y < bucket_max[1]; bucket_y++) { + has_x_isect = 0; for (bucket_x = bucket_min[0]; bucket_x < bucket_max[0]; bucket_x++) { bucket_index = bucket_x + (bucket_y * ps->bucketsX); @@ -1930,8 +1970,18 @@ static void project_paint_delayed_face_init(ProjectPaintState *ps, MFace *mf, MT (void *)face_index, /* cast to a pointer to shut up the compiler */ ps->projectArena ); + + has_x_isect = has_isect = 1; + } else if (has_x_isect) { + /* assuming the face is not a bow-tie - we know we cant intersect again on the X */ + break; } } + + /* no intersection for this entire row, after some intersection above means we can quit now */ + if (has_x_isect==0 && has_isect) { + break; + } } #ifndef PROJ_DEBUG_NOSEAMBLEED @@ -1978,6 +2028,7 @@ static void project_paint_begin( ProjectPaintState *ps, short mval[2]) MTFace *tf; int a, i; /* generic looping vars */ + int image_index; /* memory sized to add to arena size */ int tot_bucketMem=0; @@ -2000,10 +2051,8 @@ static void project_paint_begin( ProjectPaintState *ps, short mval[2]) ps->dm_totvert = ps->dm->getNumVerts( ps->dm ); ps->dm_totface = ps->dm->getNumFaces( ps->dm ); - ps->bucketsX = PROJ_BUCKET_DIV; - ps->bucketsY = PROJ_BUCKET_DIV; - - ps->imaContextIndex = -1; + ps->bucketsX = G.rt ? G.rt : PROJ_BUCKET_DIV; + ps->bucketsY = G.rt ? G.rt : PROJ_BUCKET_DIV; ps->viewDir[0] = 0.0; ps->viewDir[1] = 0.0; @@ -2028,8 +2077,8 @@ static void project_paint_begin( ProjectPaintState *ps, short mval[2]) tot_faceListMem + tot_faceSeamFlagMem + tot_faceSeamUVMem + - tot_bucketVertFacesMem + (1<<16)); - + tot_bucketVertFacesMem + (1<<18)); + //BLI_memarena_use_calloc(ps->projectArena); ? ps->projectBuckets = (LinkNode **)BLI_memarena_alloc( ps->projectArena, tot_bucketMem); ps->projectFaces= (LinkNode **)BLI_memarena_alloc( ps->projectArena, tot_faceListMem); @@ -2054,6 +2103,18 @@ static void project_paint_begin( ProjectPaintState *ps, short mval[2]) memset(ps->projectFaceSeamUVs, 0, tot_faceSeamUVMem); } #endif + + /* Thread stuff */ + if (G.scene->r.mode & R_FIXED_THREADS) { + ps->thread_tot = G.scene->r.threads; + } else { + ps->thread_tot = BLI_system_thread_count(); + } + + for (a=0; a<ps->thread_tot; a++) { + ps->projectArena_mt[a] = BLI_memarena_new(1<<16); + } + Mat4Invert(ps->ob->imat, ps->ob->obmat); Mat3CpyMat4(mat, G.vd->viewinv); @@ -2169,11 +2230,11 @@ static void project_paint_begin( ProjectPaintState *ps, short mval[2]) ibuf= BKE_image_get_ibuf((Image *)tf->tpage, NULL); if (ibuf) { - ps->imaContextIndex = BLI_linklist_index(image_LinkList, tf->tpage); + image_index = BLI_linklist_index(image_LinkList, tf->tpage); - if (ps->imaContextIndex==-1) { /* MemArena dosnt have an append func */ + if (image_index==-1) { /* MemArena dosnt have an append func */ BLI_linklist_append(&image_LinkList, tf->tpage); - ps->imaContextIndex = ps->projectImageTotal; + image_index = ps->projectImageTotal; ps->projectImageTotal++; } } @@ -2205,7 +2266,13 @@ static void project_paint_begin( ProjectPaintState *ps, short mval[2]) static void project_paint_end( ProjectPaintState *ps ) { + int a; BLI_memarena_free(ps->projectArena); + + for (a=0; a<ps->thread_tot; a++) { + BLI_memarena_free(ps->projectArena_mt[a]); + } + ps->dm->release(ps->dm); } @@ -2711,7 +2778,86 @@ static int project_bucket_circle_isect(ProjectPaintState *ps, int bucket_x, int return 0; } -static int imapaint_paint_sub_stroke_project(ProjectPaintState *ps, BrushPainter *painter, short prevmval[2], short mval[2], double time, int update, float pressure) +/* Loop over all images on this mesh and update any we have touched */ +static int imapaint_refresh_tagged(ProjectPaintState *ps) +{ + int a; + int redraw = 0; + for (a=0; a < ps->projectImageTotal; a++) { + Image *ima = ps->projectImages[a]; + if (ima->id.flag & LIB_DOIT) { + imapaint_image_update(ima, BKE_image_get_ibuf(ima, NULL), 1 /*texpaint*/ ); + redraw = 1; + + ima->id.flag &= ~LIB_DOIT; /* clear for reuse */ + } + } + + return redraw; +} + + +static int bucket_iter_init(ProjectPaintState *ps, float mval_f[2]) +{ + float min_brush[2], max_brush[2]; + + min_brush[0] = mval_f[0] - (ps->brush->size/2); + min_brush[1] = mval_f[1] - (ps->brush->size/2); + + max_brush[0] = mval_f[0] + (ps->brush->size/2); + max_brush[1] = mval_f[1] + (ps->brush->size/2); + + /* offset to make this a valid bucket index */ + project_paint_rect(ps, min_brush, max_brush, ps->min_bucket, ps->max_bucket); + + /* mouse outside the model areas? */ + if (ps->min_bucket[0]==ps->max_bucket[0] || ps->min_bucket[1]==ps->max_bucket[1]) { + return 0; + } + + ps->context_bucket_x = ps->min_bucket[0]; + ps->context_bucket_y = ps->min_bucket[1]; +#ifdef PROJ_DEBUG_PRINT_THREADS + printf("Initializing Values %d %d!", ps->min_bucket[0], ps->min_bucket[1]); +#endif + return 1; +} + +static int bucket_iter_next(ProjectPaintState *ps, int *bucket_index, float bucket_bounds[4], float mval_f[2]) +{ + if (ps->thread_tot > 1) + BLI_lock_thread(LOCK_CUSTOM1); + + //printf("%d %d \n", ps->context_bucket_x, ps->context_bucket_y); + + for ( ; ps->context_bucket_y < ps->max_bucket[1]; ps->context_bucket_y++) { + for ( ; ps->context_bucket_x < ps->max_bucket[0]; ps->context_bucket_x++) { + + /* use bucket_bounds for project_bucket_circle_isect and project_paint_bucket_init*/ + project_bucket_bounds(ps, ps->context_bucket_x, ps->context_bucket_y, bucket_bounds); + + if (project_bucket_circle_isect(ps, ps->context_bucket_x, ps->context_bucket_y, mval_f, ps->brush->size * ps->brush->size, bucket_bounds)) { + *bucket_index = ps->context_bucket_x + (ps->context_bucket_y * ps->bucketsX); +#ifdef PROJ_DEBUG_PRINT_THREADS + printf(" --- %d %d \n", ps->context_bucket_x, ps->context_bucket_y); +#endif + ps->context_bucket_x++; + + if (ps->thread_tot > 1) + BLI_unlock_thread(LOCK_CUSTOM1); + + return 1; + } + } + ps->context_bucket_x = ps->min_bucket[0]; + } + + if (ps->thread_tot > 1) + BLI_unlock_thread(LOCK_CUSTOM1); + return 0; +} + +static int imapaint_paint_sub_stroke_project(ProjectPaintState *ps, BrushPainter *painter, short prevmval[2], short mval[2], double time, float pressure, int thread_index) { /* TODO - texpaint option : is there any use in projection painting from the image window??? - could be interesting */ /* TODO - floating point images */ @@ -2727,19 +2873,18 @@ static int imapaint_paint_sub_stroke_project(ProjectPaintState *ps, BrushPainter char rgba_ub[4]; float rgba_fp[4]; float brush_size_sqared; - float min[2], max[2]; /* brush bounds in screenspace */ + //float min[2], max[2]; /* brush bounds in screenspace */ int bucket_min[2], bucket_max[2]; /* brush bounds in bucket grid space */ int bucket_index; int a; int is_floatbuf = 0; short blend= ps->blend; + /* pixel values */ char *cp; float *fp; float bucket_bounds[4]; - int bucket_x, bucket_y; - /* for smear only */ float mval_ofs[2]; @@ -2747,22 +2892,8 @@ static int imapaint_paint_sub_stroke_project(ProjectPaintState *ps, BrushPainter LinkNode *smearPixels = NULL; LinkNode *smearPixels_float = NULL; MemArena *smearArena = NULL; /* mem arena for this brush projection only */ - - mval_f[0] = mval[0]; mval_f[1] = mval[1]; - - min[0] = mval_f[0] - (ps->brush->size/2); - min[1] = mval_f[1] - (ps->brush->size/2); - - max[0] = mval_f[0] + (ps->brush->size/2); - max[1] = mval_f[1] + (ps->brush->size/2); - - /* offset to make this a valid bucket index */ - project_paint_rect(ps, min, max, bucket_min, bucket_max); - - /* mouse outside the model areas? */ - if (bucket_min[0]==bucket_max[0] || bucket_min[1]==bucket_max[1]) { - return 0; - } + + mval_f[0] = mval[0]; mval_f[1] = mval[1]; if (ps->tool==PAINT_TOOL_SMEAR) { mval_ofs[0] = (float)(mval[0] - prevmval[0]); @@ -2781,130 +2912,128 @@ static int imapaint_paint_sub_stroke_project(ProjectPaintState *ps, BrushPainter /* no clamping needed, dont use screen bounds, use vert bounds */ - for (bucket_y = bucket_min[1]; bucket_y < bucket_max[1]; bucket_y++) { - for (bucket_x = bucket_min[0]; bucket_x < bucket_max[0]; bucket_x++) { - - /* use bucket_bounds for project_bucket_circle_isect and project_paint_bucket_init*/ - project_bucket_bounds(ps, bucket_x, bucket_y, bucket_bounds); - - if (project_bucket_circle_isect(ps, bucket_x, bucket_y, mval_f, brush_size_sqared, bucket_bounds)) { - - bucket_index = bucket_x + (bucket_y * ps->bucketsX); - - /* Check this bucket and its faces are initialized */ - if (ps->projectBucketFlags[bucket_index] == PROJ_BUCKET_NULL) { - /* No pixels initialized */ - project_paint_bucket_init(ps, bucket_index, bucket_bounds); - } - - /* TODO - we may want to init clone data in a seperate to project_paint_bucket_init - * so we dont go overboard and init too many clone pixels */ + //for (bucket_y = bucket_min[1]; bucket_y < bucket_max[1]; bucket_y++) { +#ifdef PROJ_DEBUG_PRINT_THREADS + printf("THREAD %d %d %d\n", ps->thread_tot, thread_index, (ps->max_bucket[0] - ps->min_bucket[0]) * (ps->max_bucket[1] - ps->min_bucket[1]) ); +#endif + while (bucket_iter_next(ps, &bucket_index, bucket_bounds, mval_f)) { + +#ifdef PROJ_DEBUG_PRINT_THREADS + printf("\t%d %d\n", thread_index, bucket_index); +#endif + + /* Check this bucket and its faces are initialized */ + if (ps->projectBucketFlags[bucket_index] == PROJ_BUCKET_NULL) { + /* No pixels initialized */ + project_paint_bucket_init(ps, thread_index, bucket_index, bucket_bounds); + } + + /* TODO - we may want to init clone data in a seperate to project_paint_bucket_init + * so we dont go overboard and init too many clone pixels */ - if ((node = ps->projectBuckets[bucket_index])) { + if ((node = ps->projectBuckets[bucket_index])) { + + do { + projPixel = (ProjectPixel *)node->link; - do { - projPixel = (ProjectPixel *)node->link; - - /*dist = Vec2Lenf(projPixel->projCo2D, mval_f);*/ /* correct but uses a sqrt */ - dist_nosqrt = Vec2Lenf_nosqrt(projPixel->projCo2D, mval_f); - - /*if (dist < s->brush->size) {*/ /* correct but uses a sqrt */ - if (dist_nosqrt < brush_size_sqared) { - - if (last_index != projPixel->image_index) { - last_index = projPixel->image_index; - ps->projectImages[last_index]->id.flag |= LIB_DOIT; - is_floatbuf = ps->projectImBufs[last_index]->rect_float ? 1 : 0; + /*dist = Vec2Lenf(projPixel->projCo2D, mval_f);*/ /* correct but uses a sqrt */ + dist_nosqrt = Vec2Lenf_nosqrt(projPixel->projCo2D, mval_f); + + /*if (dist < s->brush->size) {*/ /* correct but uses a sqrt */ + if (dist_nosqrt < brush_size_sqared) { + + if (last_index != projPixel->image_index) { + last_index = projPixel->image_index; + ps->projectImages[last_index]->id.flag |= LIB_DOIT; /* halgrind complains this is not threadsafe but probably ok? - we are only setting this flag anyway */ + is_floatbuf = ps->projectImBufs[last_index]->rect_float ? 1 : 0; + } + + dist = (float)sqrt(dist_nosqrt); + + switch(ps->tool) { + case PAINT_TOOL_CLONE: + if (is_floatbuf) { + if (((ProjectPixelCloneFloat*)projPixel)->clonepx[3]) { + alpha = brush_sample_falloff(ps->brush, dist); + fp = ((ProjectPixelCloneFloat *)projPixel)->clonepx; + if (alpha >= 1.0) { + VECCOPY((float *)projPixel->pixel, ((ProjectPixelCloneFloat *)projPixel)->clonepx ); + } else { + ((float *)projPixel->pixel)[0] = (fp[0] * alpha) + ((((float *)projPixel->pixel)[0])*(1.0-alpha)); + ((float *)projPixel->pixel)[1] = (fp[1] * alpha) + ((((float *)projPixel->pixel)[1])*(1.0-alpha)); + ((float *)projPixel->pixel)[2] = (fp[2] * alpha) + ((((float *)projPixel->pixel)[2])*(1.0-alpha)); + } } - - dist = (float)sqrt(dist_nosqrt); - - switch(ps->tool) { - case PAINT_TOOL_CLONE: - if (is_floatbuf) { - if (((ProjectPixelCloneFloat*)projPixel)->clonepx[3]) { - alpha = brush_sample_falloff(ps->brush, dist); - fp = ((ProjectPixelCloneFloat *)projPixel)->clonepx; - if (alpha >= 1.0) { - VECCOPY((float *)projPixel->pixel, ((ProjectPixelCloneFloat *)projPixel)->clonepx ); - } else { - ((float *)projPixel->pixel)[0] = (fp[0] * alpha) + ((((float *)projPixel->pixel)[0])*(1.0-alpha)); - ((float *)projPixel->pixel)[1] = (fp[1] * alpha) + ((((float *)projPixel->pixel)[1])*(1.0-alpha)); - ((float *)projPixel->pixel)[2] = (fp[2] * alpha) + ((((float *)projPixel->pixel)[2])*(1.0-alpha)); - } - } + } else { + if (((ProjectPixelClone*)projPixel)->clonepx[3]) { + alpha = brush_sample_falloff(ps->brush, dist); + cp = (char *)((ProjectPixelClone*)projPixel)->clonepx; + if (alpha >= 1.0) { + ((char *)projPixel->pixel)[0] = cp[0]; + ((char *)projPixel->pixel)[1] = cp[1]; + ((char *)projPixel->pixel)[2] = cp[2]; } else { - if (((ProjectPixelClone*)projPixel)->clonepx[3]) { - alpha = brush_sample_falloff(ps->brush, dist); - cp = (char *)((ProjectPixelClone*)projPixel)->clonepx; - if (alpha >= 1.0) { - ((char *)projPixel->pixel)[0] = cp[0]; - ((char *)projPixel->pixel)[1] = cp[1]; - ((char *)projPixel->pixel)[2] = cp[2]; - } else { - ((char *)projPixel->pixel)[0] = FTOCHAR( (((cp[0]/255.0) * alpha) + (((((char *)projPixel->pixel)[0])/255.0)*(1.0-alpha))) ); - ((char *)projPixel->pixel)[1] = FTOCHAR( (((cp[1]/255.0) * alpha) + (((((char *)projPixel->pixel)[1])/255.0)*(1.0-alpha))) ); - ((char *)projPixel->pixel)[2] = FTOCHAR( (((cp[2]/255.0) * alpha) + (((((char *)projPixel->pixel)[2])/255.0)*(1.0-alpha))) ); - } - } + ((char *)projPixel->pixel)[0] = FTOCHAR( (((cp[0]/255.0) * alpha) + (((((char *)projPixel->pixel)[0])/255.0)*(1.0-alpha))) ); + ((char *)projPixel->pixel)[1] = FTOCHAR( (((cp[1]/255.0) * alpha) + (((((char *)projPixel->pixel)[1])/255.0)*(1.0-alpha))) ); + ((char *)projPixel->pixel)[2] = FTOCHAR( (((cp[2]/255.0) * alpha) + (((((char *)projPixel->pixel)[2])/255.0)*(1.0-alpha))) ); } - break; - case PAINT_TOOL_SMEAR: - Vec2Subf(co, projPixel->projCo2D, mval_ofs); - if (screenco_pickcol(ps, co, NULL, rgba_ub, 0)) { /* Note, no interpolation here, only needed for clone, nearest should be is OK */ - brush_sample_tex(ps->brush, projPixel->projCo2D, rgba); - alpha = rgba[3]*brush_sample_falloff(ps->brush, dist); - /* drat! - this could almost be very simple if we ignore - * the fact that applying the color directly gives feedback, - * instead, collect the colors and apply after :/ */ - + } + } + break; + case PAINT_TOOL_SMEAR: + Vec2Subf(co, projPixel->projCo2D, mval_ofs); + if (screenco_pickcol(ps, co, NULL, rgba_ub, 0)) { /* Note, no interpolation here, only needed for clone, nearest should be is OK */ + brush_sample_tex(ps->brush, projPixel->projCo2D, rgba); + alpha = rgba[3]*brush_sample_falloff(ps->brush, dist); + /* drat! - this could almost be very simple if we ignore + * the fact that applying the color directly gives feedback, + * instead, collect the colors and apply after :/ */ + #if 0 /* looks OK but not correct - also would need float buffer */ - *((unsigned int *)projPixel->pixel) = IMB_blend_color( *((unsigned int *)projPixel->pixel), *((unsigned int *)rgba_ub), (int)(alpha*255), blend); + *((unsigned int *)projPixel->pixel) = IMB_blend_color( *((unsigned int *)projPixel->pixel), *((unsigned int *)rgba_ub), (int)(alpha*255), blend); #endif - - /* add to memarena instead */ - if (is_floatbuf) { - /* TODO FLOAT */ /* Smear wont do float properly yet */ - char rgba_smear[4]; - IMAPAINT_FLOAT_RGBA_TO_CHAR(rgba_smear, (float *)projPixel->pixel); - *((unsigned int *) &((ProjectPixelClone *)projPixel)->clonepx) = IMB_blend_color( *((unsigned int *)rgba_smear), *((unsigned int *)rgba_ub), (int)(alpha*255), blend); - BLI_linklist_prepend_arena( &smearPixels_float, (void *)projPixel, smearArena ); - } else { - *((unsigned int *) &((ProjectPixelClone *)projPixel)->clonepx) = IMB_blend_color( *((unsigned int *)projPixel->pixel), *((unsigned int *)rgba_ub), (int)(alpha*255), blend); - BLI_linklist_prepend_arena( &smearPixels, (void *)projPixel, smearArena ); - } - } - break; - default: - brush_sample_tex(ps->brush, projPixel->projCo2D, rgba); - alpha = rgba[3]*brush_sample_falloff(ps->brush, dist); - if (alpha > 0.0) { - if (is_floatbuf) { - rgba_fp[0] = rgba[0] * ps->brush->rgb[0]; - rgba_fp[1] = rgba[1] * ps->brush->rgb[1]; - rgba_fp[2] = rgba[2] * ps->brush->rgb[2]; - rgba_fp[3] = rgba[3]; - IMB_blend_color_float( (float *)projPixel->pixel, (float *)projPixel->pixel, rgba_fp, alpha, blend); - } else { - rgba_ub[0] = FTOCHAR(rgba[0] * ps->brush->rgb[0]); - rgba_ub[1] = FTOCHAR(rgba[1] * ps->brush->rgb[1]); - rgba_ub[2] = FTOCHAR(rgba[2] * ps->brush->rgb[2]); - rgba_ub[3] = FTOCHAR(rgba[3]); - - *((unsigned int *)projPixel->pixel) = IMB_blend_color( *((unsigned int *)projPixel->pixel), *((unsigned int *)rgba_ub), (int)(alpha*255), blend); - } - } - break; + + /* add to memarena instead */ + if (is_floatbuf) { + /* TODO FLOAT */ /* Smear wont do float properly yet */ + char rgba_smear[4]; + IMAPAINT_FLOAT_RGBA_TO_CHAR(rgba_smear, (float *)projPixel->pixel); + *((unsigned int *) &((ProjectPixelClone *)projPixel)->clonepx) = IMB_blend_color( *((unsigned int *)rgba_smear), *((unsigned int *)rgba_ub), (int)(alpha*255), blend); + BLI_linklist_prepend_arena( &smearPixels_float, (void *)projPixel, smearArena ); + } else { + *((unsigned int *) &((ProjectPixelClone *)projPixel)->clonepx) = IMB_blend_color( *((unsigned int *)projPixel->pixel), *((unsigned int *)rgba_ub), (int)(alpha*255), blend); + BLI_linklist_prepend_arena( &smearPixels, (void *)projPixel, smearArena ); + } + } + break; + default: + brush_sample_tex(ps->brush, projPixel->projCo2D, rgba); + alpha = rgba[3]*brush_sample_falloff(ps->brush, dist); + if (alpha > 0.0) { + if (is_floatbuf) { + rgba_fp[0] = rgba[0] * ps->brush->rgb[0]; + rgba_fp[1] = rgba[1] * ps->brush->rgb[1]; + rgba_fp[2] = rgba[2] * ps->brush->rgb[2]; + rgba_fp[3] = rgba[3]; + IMB_blend_color_float( (float *)projPixel->pixel, (float *)projPixel->pixel, rgba_fp, alpha, blend); + } else { + rgba_ub[0] = FTOCHAR(rgba[0] * ps->brush->rgb[0]); + rgba_ub[1] = FTOCHAR(rgba[1] * ps->brush->rgb[1]); + rgba_ub[2] = FTOCHAR(rgba[2] * ps->brush->rgb[2]); + rgba_ub[3] = FTOCHAR(rgba[3]); + *((unsigned int *)projPixel->pixel) = IMB_blend_color( *((unsigned int *)projPixel->pixel), *((unsigned int *)rgba_ub), (int)(alpha*255), blend); } - - /* done painting */ } + break; - node = node->next; - } while (node); + } + + /* done painting */ } - } + + node = node->next; + } while (node); } } @@ -2928,20 +3057,109 @@ static int imapaint_paint_sub_stroke_project(ProjectPaintState *ps, BrushPainter BLI_memarena_free(smearArena); } + /* Loop over all images on this mesh and update any we have touched */ - for (a=0; a < ps->projectImageTotal; a++) { - Image *ima = ps->projectImages[a]; - if (ima->id.flag & LIB_DOIT) { - imapaint_image_update(ima, BKE_image_get_ibuf(ima, NULL), 1 /*texpaint*/ ); - redraw = 1; - - ima->id.flag &= ~LIB_DOIT; /* clear for reuse */ - } + if (ps->thread_tot < 2) { /* only run this if we have no threads */ + redraw = imapaint_refresh_tagged(ps); } return redraw; } + +typedef struct ProjectHandle { + /* args */ + ProjectPaintState *ps; + BrushPainter *painter; + short prevmval[2]; + short mval[2]; + double time; + float pressure; + + /* thread settings */ + int thread_tot; + int thread_index; + int ready; +} ProjectHandle; + + +static void *do_projectpaint_thread(void *ph_v) +{ + ProjectHandle *ph= ph_v; + + imapaint_paint_sub_stroke_project( + ph->ps, + ph->painter, + ph->prevmval, + ph->mval, + ph->time, + ph->pressure, + + ph->thread_index + ); + + ph->ready= 1; + + return NULL; +} + +static int imapaint_paint_sub_stroke_project_mt(ProjectPaintState *ps, BrushPainter *painter, short prevmval[2], short mval[2], double time, float pressure) +{ + ProjectHandle handles[BLENDER_MAX_THREADS]; + ListBase threads; + int a; + + float mval_f[2]; + mval_f[0] = mval[0]; mval_f[1] = mval[1]; + + if (bucket_iter_init(ps, mval_f)==0) + return 0; +#ifdef PROJ_DEBUG_PRINT_THREADS + printf("Begin Threads %d\n", ps->thread_tot); +#endif + + BLI_init_threads(&threads, do_projectpaint_thread, ps->thread_tot); + + /* get the threads running */ + for(a=0; a < ps->thread_tot; a++) { +#ifdef PROJ_DEBUG_PRINT_THREADS + printf("INIT THREAD %d\n", a); +#endif + + /* set defaults in handles */ + //memset(&handles[a], 0, sizeof(BakeShade)); + + handles[a].ps = ps; + handles[a].painter = painter; + VECCOPY2D(handles[a].prevmval, prevmval); + VECCOPY2D(handles[a].mval, mval); + handles[a].time = time; + handles[a].pressure = pressure; + + /* thread spesific */ + handles[a].thread_index = a; + handles[a].ready = 0; + + BLI_insert_thread(&threads, &handles[a]); + } + + /* wait for everything to be done */ + a= 0; + while(a != ps->thread_tot) { + PIL_sleep_ms(1); + + for(a=0; a < ps->thread_tot; a++) + if(handles[a].ready==0) + break; + } + + BLI_end_threads(&threads); + + return imapaint_refresh_tagged(ps); +} + + + static void imapaint_paint_stroke(ImagePaintState *s, BrushPainter *painter, short texpaint, short *prevmval, short *mval, double time, float pressure) { Image *newimage = NULL; @@ -3027,8 +3245,14 @@ static void imapaint_paint_stroke_project(ProjectPaintState *ps, BrushPainter *p int redraw_flag = 0; /* TODO - support more brush operations, airbrush etc */ - { - redraw_flag |= imapaint_paint_sub_stroke_project(ps, painter, prevmval, mval, time, 1, pressure); + if (ps->thread_tot > 1) { + redraw_flag |= imapaint_paint_sub_stroke_project_mt(ps, painter, prevmval, mval, time, pressure); + } else { + float mval_f[2]; + mval_f[0] = mval[0]; mval_f[1] = mval[1]; + if (bucket_iter_init(ps, mval_f)) { + redraw_flag |= imapaint_paint_sub_stroke_project(ps, painter, prevmval, mval, time, pressure, 0); /* no threads */ + } } if (redraw && redraw_flag) { @@ -3044,7 +3268,6 @@ static int imapaint_paint_gp_to_stroke(float **points_gp) { bGPDstroke *gps; tGPspoint *pt; - int stroke_gp = 0; int index_gp = 0; int tot_gp = 0; float *vec_gp; @@ -3140,10 +3363,12 @@ void imagepaint_paint(short mousebutton, short texpaint) return; } - /* TODO - add UI */ + /* TODO - grease pencil stroke is verry basic now and only useful for benchmarking, should make this nicer */ + // stroke_gp = 1; + /* if (G.rt==123) { - stroke_gp = 1; - } + + }*/ /* initialize state */ memset(&s, 0, sizeof(s)); |