From fa7e690a91258cfcfd266d48a4b084ba734d7b5f Mon Sep 17 00:00:00 2001 From: Lukas Toenne Date: Mon, 8 Jul 2013 11:38:09 +0000 Subject: [PATCH 01/22] Partial fix for #36024, don't always reset the node editor tree pointer if the type is undefined. This can happen if the tree type is defined by addon or script, in which case the tree type would be unknown the first time the context is checked, but registered right afterward. Also unknown tree types are handled fine, they just display dummy nodes in red warning color. --- source/blender/editors/space_node/node_edit.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/source/blender/editors/space_node/node_edit.c b/source/blender/editors/space_node/node_edit.c index e10cba43d71..f8166456a2c 100644 --- a/source/blender/editors/space_node/node_edit.c +++ b/source/blender/editors/space_node/node_edit.c @@ -577,9 +577,10 @@ void snode_set_context(const bContext *C) if (!treetype || (treetype->poll && !treetype->poll(C, treetype))) { - /* invalid tree type, disable */ - snode->tree_idname[0] = '\0'; - ED_node_tree_start(snode, NULL, NULL, NULL); + /* invalid tree type, skip + * NB: not resetting the node path here, invalid bNodeTreeType + * may still be registered at a later point. + */ return; } From d75e0f320b4af13d5a35c5787a8ff992b959149e Mon Sep 17 00:00:00 2001 From: Lukas Toenne Date: Mon, 8 Jul 2013 11:38:11 +0000 Subject: [PATCH 02/22] Fix #36024. This part fixes user counting for node editor trees. The user count previously would only work correctly for node trees which are part of material, scene, etc. or linked by group nodes. Any custom pynodes tree edited directly as library data would get a 0 user count on reload and subsequently not be saved. Now the node space follows the same pattern as the image space: the node tree(s) user count gets incremented on file load and opening in the editor ensures a real user. This leads to 1 "unreal" user for the editor (dropped on reload), but seems to be the only viable solution atm. --- source/blender/blenloader/intern/readfile.c | 9 +++++---- source/blender/editors/space_node/space_node.c | 5 +++++ 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/source/blender/blenloader/intern/readfile.c b/source/blender/blenloader/intern/readfile.c index 5d30bf6ff73..a6da0762b1c 100644 --- a/source/blender/blenloader/intern/readfile.c +++ b/source/blender/blenloader/intern/readfile.c @@ -5708,8 +5708,9 @@ static void lib_link_screen(FileData *fd, Main *main) ntree = nodetree_from_id(snode->id); if (ntree) snode->nodetree = ntree; - else - snode->nodetree = newlibadr(fd, sc->id.lib, snode->nodetree); + else { + snode->nodetree = newlibadr_us(fd, sc->id.lib, snode->nodetree); + } for (path = snode->treepath.first; path; path = path->next) { if (path == snode->treepath.first) { @@ -5717,7 +5718,7 @@ static void lib_link_screen(FileData *fd, Main *main) path->nodetree = snode->nodetree; } else - path->nodetree = newlibadr(fd, sc->id.lib, path->nodetree); + path->nodetree = newlibadr_us(fd, sc->id.lib, path->nodetree); if (!path->nodetree) break; @@ -6046,7 +6047,7 @@ void blo_lib_link_screen_restore(Main *newmain, bScreen *curscreen, Scene *cursc path->nodetree = snode->nodetree; } else - path->nodetree= restore_pointer_by_name(newmain, (ID*)path->nodetree, 0); + path->nodetree= restore_pointer_by_name(newmain, (ID*)path->nodetree, 2); if (!path->nodetree) break; diff --git a/source/blender/editors/space_node/space_node.c b/source/blender/editors/space_node/space_node.c index 07fa6997e99..8d6363d3bbe 100644 --- a/source/blender/editors/space_node/space_node.c +++ b/source/blender/editors/space_node/space_node.c @@ -41,6 +41,7 @@ #include "BLI_math.h" #include "BKE_context.h" +#include "BKE_library.h" #include "BKE_screen.h" #include "BKE_node.h" @@ -83,6 +84,8 @@ void ED_node_tree_start(SpaceNode *snode, bNodeTree *ntree, ID *id, ID *from) BLI_strncpy(path->node_name, id->name + 2, sizeof(path->node_name)); BLI_addtail(&snode->treepath, path); + + id_us_ensure_real(&ntree->id); } /* update current tree */ @@ -116,6 +119,8 @@ void ED_node_tree_push(SpaceNode *snode, bNodeTree *ntree, bNode *gnode) BLI_addtail(&snode->treepath, path); + id_us_ensure_real(&ntree->id); + /* update current tree */ snode->edittree = ntree; From 0a006cce9cdf71aa02cd5cc0b8c13d046550f217 Mon Sep 17 00:00:00 2001 From: Howard Trickey Date: Mon, 8 Jul 2013 13:02:21 +0000 Subject: [PATCH 03/22] Fix bevel bugs 34445 and 35109, copying over edge data. The bugs were about not respecting edge smoothness and not respecting edge crease. This change copies the edge attributes from a beveled edge to the two outside edges of the bevel. --- source/blender/bmesh/tools/bmesh_bevel.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/source/blender/bmesh/tools/bmesh_bevel.c b/source/blender/bmesh/tools/bmesh_bevel.c index d7073ef61be..e0c11414e38 100644 --- a/source/blender/bmesh/tools/bmesh_bevel.c +++ b/source/blender/bmesh/tools/bmesh_bevel.c @@ -2223,6 +2223,7 @@ static void bevel_build_edge_polygons(BMesh *bm, BevelParams *bp, BMEdge *bme) BMVert *bmv1, *bmv2, *bmv3, *bmv4, *bmv1i, *bmv2i, *bmv3i, *bmv4i; VMesh *vm1, *vm2; EdgeHalf *e1, *e2; + BMEdge *bme1, *bme2; BMFace *f1, *f2, *f; int k, nseg, i1, i2, odd, mid; @@ -2294,6 +2295,13 @@ static void bevel_build_edge_polygons(BMesh *bm, BevelParams *bp, BMEdge *bme) bev_merge_end_uvs(bm, bv1, e1); if (!e2->is_seam && bv2->vmesh->mesh_kind == M_NONE) bev_merge_end_uvs(bm, bv2, e2); + + /* Copy edge data to first and last edge */ + bme1 = BM_edge_exists(bmv1, bmv2); + bme2 = BM_edge_exists(bmv3, bmv4); + BLI_assert(bme1 && bme2); + BM_elem_attrs_copy(bm, bm, bme, bme1); + BM_elem_attrs_copy(bm, bm, bme, bme2); } /* From 27734f5bec7b717e82128e637c805b95e2b0f889 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Mon, 8 Jul 2013 13:30:11 +0000 Subject: [PATCH 04/22] fix/improve normal calculation, noticed when checking on the previous bugfix. - normals depended on the meshes rotation, so you could rotate Suzzane and in some cases one of the eye normals would be flipped. - normals depended on the meshes placement in relation to the meshes center, now find the outer most face by each face-island center. --- source/blender/bmesh/intern/bmesh_queries.c | 113 +++++++++++ source/blender/bmesh/intern/bmesh_queries.h | 2 + source/blender/bmesh/operators/bmo_normals.c | 203 +++++++++++-------- 3 files changed, 239 insertions(+), 79 deletions(-) diff --git a/source/blender/bmesh/intern/bmesh_queries.c b/source/blender/bmesh/intern/bmesh_queries.c index be186e0441b..c0c4e0f0209 100644 --- a/source/blender/bmesh/intern/bmesh_queries.c +++ b/source/blender/bmesh/intern/bmesh_queries.c @@ -1758,6 +1758,119 @@ float BM_mesh_calc_volume(BMesh *bm, bool is_signed) return vol; } + +/** + * TODO (as we need) + * - option to walk over edges. + * - option to walk over non manifold edges. + * + * \param bm the BMesh. + * \param r_groups_array Array of ints to fill in, length of bm->totface. + * \param r_group_index index, length pairs into \a r_groups_array, size of return value + * int pairs: (array_start, array_length). + * \return The number of groups found. + */ +int BM_mesh_calc_face_groups(BMesh *bm, int *r_groups_array, int (**r_group_index)[2], + void *user_data, bool (*filter_fn)(BMEdge *, void *user_data)) +{ +#ifdef DEBUG + int group_index_len = 1; +#else + int group_index_len = 32; +#endif + + int (*group_index)[2] = MEM_mallocN(sizeof(*group_index) * group_index_len, __func__); + + int *group_array = r_groups_array; + STACK_DECLARE(group_array); + + int group_curr = 0; + + const unsigned int tot_faces = bm->totface; + unsigned int tot_touch = 0; + + BMFace **fstack; + STACK_DECLARE(fstack); + + BMIter iter; + BMFace *f; + int i; + + STACK_INIT(group_array); + + /* init the array */ + BM_ITER_MESH_INDEX (f, &iter, bm, BM_FACES_OF_MESH, i) { + BM_elem_index_set(f, i); /* set_inline */ + BM_elem_flag_disable(f, BM_ELEM_TAG); + } + bm->elem_index_dirty &= ~BM_FACE; + + /* detect groups */ + fstack = MEM_mallocN(sizeof(*fstack) * tot_faces, __func__); + + while (tot_touch != tot_faces) { + int *fg; + bool ok = false; + + BLI_assert(tot_touch < tot_faces); + + STACK_INIT(fstack); + + BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) { + if (BM_elem_flag_test(f, BM_ELEM_TAG) == false) { + BM_elem_flag_enable(f, BM_ELEM_TAG); + STACK_PUSH(fstack, f); + ok = true; + break; + } + } + + BLI_assert(ok == true); + + /* manage arrays */ + if (group_index_len == group_curr) { + group_index_len *= 2; + group_index = MEM_reallocN(group_index, sizeof(*group_index) * group_index_len); + } + + fg = group_index[group_curr]; + fg[0] = STACK_SIZE(group_array); + fg[1] = 0; + + while ((f = STACK_POP(fstack))) { + BMLoop *l_iter, *l_first; + + /* add face */ + STACK_PUSH(group_array, BM_elem_index_get(f)); + tot_touch++; + fg[1]++; + /* done */ + + /* search for other faces */ + l_iter = l_first = BM_FACE_FIRST_LOOP(f); + do { + BMLoop *l_other = l_iter->radial_next; + if ((l_other != l_iter) && filter_fn(l_iter->e, user_data)) { + if (BM_elem_flag_test(l_other->f, BM_ELEM_TAG) == false) { + BM_elem_flag_enable(l_other->f, BM_ELEM_TAG); + STACK_PUSH(fstack, l_other->f); + } + } + } while ((l_iter = l_iter->next) != l_first); + } + + group_curr++; + } + + MEM_freeN(fstack); + + /* reduce alloc to required size */ + group_index = MEM_reallocN(group_index, sizeof(*group_index) * group_curr); + *r_group_index = group_index; + + return group_curr; +} + float bmesh_subd_falloff_calc(const int falloff, float val) { switch (falloff) { diff --git a/source/blender/bmesh/intern/bmesh_queries.h b/source/blender/bmesh/intern/bmesh_queries.h index 7d599a9c8af..94dae1d1d23 100644 --- a/source/blender/bmesh/intern/bmesh_queries.h +++ b/source/blender/bmesh/intern/bmesh_queries.h @@ -115,6 +115,8 @@ bool BM_face_is_any_vert_flag_test(BMFace *f, const char hflag); bool BM_face_is_any_edge_flag_test(BMFace *f, const char hflag); float BM_mesh_calc_volume(BMesh *bm, bool is_signed); +int BM_mesh_calc_face_groups(BMesh *bm, int *r_groups_array, int (**r_group_index)[2], + void *user_data, bool (*filter_fn)(BMEdge *, void *user_data)); /* not really any good place to put this */ float bmesh_subd_falloff_calc(const int falloff, float val); diff --git a/source/blender/bmesh/operators/bmo_normals.c b/source/blender/bmesh/operators/bmo_normals.c index 88a4d4478be..7116ef82a32 100644 --- a/source/blender/bmesh/operators/bmo_normals.c +++ b/source/blender/bmesh/operators/bmo_normals.c @@ -36,8 +36,104 @@ /********* righthand faces implementation ****** */ -#define FACE_VIS 1 -#define FACE_FLAG 2 +#define FACE_FLAG (1 << 0) +#define FACE_FLIP (1 << 1) +#define FACE_TEMP (1 << 2) + +static bool bmo_recalc_normal_edge_filter_cb(BMEdge *e, void *UNUSED(user_data)) +{ + return BM_edge_is_manifold(e); +} + +/** + * Given an array of faces, recalcualte their normals. + * this functions assumes all faces in the array are connected by edges. + * + * \param bm + * \param faces Array of connected faces. + * \param faces_len Length of \a faces + * \param oflag Flag to check before doing the actual face flipping. + */ +static void bmo_recalc_face_normals_array(BMesh *bm, BMFace **faces, const int faces_len, const short oflag) +{ + float cent[3]; + float (*faces_center)[3] = MEM_mallocN(sizeof(*faces_center) * faces_len, __func__); + const float cent_fac = 1.0f / (float)faces_len; + int i, f_start_index; + const short oflag_flip = oflag | FACE_FLIP; + + float f_len_best; + BMFace *f; + + BMFace **fstack = MEM_mallocN(sizeof(*fstack) * faces_len, __func__); + STACK_DECLARE(fstack); + + zero_v3(cent); + + /* first calculate the center */ + for (i = 0; i < faces_len; i++) { + float *f_cent = faces_center[i]; + BM_face_calc_center_mean_weighted(faces[i], f_cent); + madd_v3_v3fl(cent, f_cent, cent_fac); + + BLI_assert(BMO_elem_flag_test(bm, faces[i], FACE_TEMP) == 0); + } + + f_len_best = -FLT_MAX; + + for (i = 0; i < faces_len; i++) { + float f_len_test; + + if ((f_len_test = len_squared_v3v3(faces_center[i], cent)) > f_len_best) { + f_len_best = f_len_test; + f_start_index = i; + } + } + + /* make sure the starting face has the correct winding */ + if (dot_v3v3(faces_center[f_start_index], faces[f_start_index]->no) < 0.0f) { + BMO_elem_flag_enable(bm, faces[f_start_index], FACE_FLIP); + } + + MEM_freeN(faces_center); + + /* now that we've found our starting face, make all connected faces + * have the same winding. this is done recursively, using a manual + * stack (if we use simple function recursion, we'd end up overloading + * the stack on large meshes). */ + STACK_INIT(fstack); + + STACK_PUSH(fstack, faces[f_start_index]); + BMO_elem_flag_enable(bm, faces[f_start_index], FACE_TEMP); + + while ((f = STACK_POP(fstack))) { + const bool flip_state = BMO_elem_flag_test_bool(bm, f, FACE_FLIP); + BMLoop *l_iter, *l_first; + + l_iter = l_first = BM_FACE_FIRST_LOOP(f); + do { + BMLoop *l_other = l_iter->radial_next; + + if ((l_other != l_iter) && bmo_recalc_normal_edge_filter_cb(l_iter->e, NULL)) { + if (!BMO_elem_flag_test(bm, l_other->f, FACE_TEMP)) { + BMO_elem_flag_enable(bm, l_other->f, FACE_TEMP); + BMO_elem_flag_set(bm, l_other->f, FACE_FLIP, (l_other->v == l_iter->v) != flip_state); + STACK_PUSH(fstack, l_other->f); + } + } + } while ((l_iter = l_iter->next) != l_first); + } + + MEM_freeN(fstack); + + /* apply flipping to oflag'd faces */ + for (i = 0; i < faces_len; i++) { + if (BMO_elem_flag_test(bm, faces[i], oflag_flip) == oflag_flip) { + BM_face_normal_flip(bm, faces[i]); + } + BMO_elem_flag_disable(bm, faces[i], FACE_TEMP); + } +} /* * put normal to the outside, and set the first direction flags in edges @@ -47,92 +143,41 @@ * * in case all faces were not done: start over with 'find the ultimate ...' */ -/* NOTE: BM_ELEM_TAG is used on faces to tell if they are flipped. */ - void bmo_recalc_face_normals_exec(BMesh *bm, BMOperator *op) { - BMFace **fstack; - STACK_DECLARE(fstack); - const unsigned int tot_faces = BMO_slot_buffer_count(op->slots_in, "faces"); - unsigned int tot_touch = 0; + int *groups_array = MEM_mallocN(sizeof(groups_array) * bm->totface, __func__); + int faces_len; + BMFace **faces_arr = BM_iter_as_arrayN(bm, BM_FACES_OF_MESH, NULL, &faces_len, NULL, 0); + BMFace **faces_grp = MEM_mallocN(sizeof(faces_grp) * bm->totface, __func__); + + int (*group_index)[2]; + const int group_tot = BM_mesh_calc_face_groups(bm, groups_array, &group_index, + NULL, bmo_recalc_normal_edge_filter_cb); + int i; + BMO_slot_buffer_flag_enable(bm, op->slots_in, "faces", BM_FACE, FACE_FLAG); - fstack = MEM_mallocN(sizeof(*fstack) * tot_faces, __func__); + for (i = 0; i < group_tot; i++) { + const int fg_sta = group_index[i][0]; + const int fg_len = group_index[i][1]; + int j; + bool is_calc = false; - while (tot_touch != tot_faces) { - BMOIter siter; - float f_len_best = -FLT_MAX; - BMFace *f, *f_start = NULL; - float f_start_cent[3]; - - /* find a starting face */ - BMO_ITER (f, &siter, op->slots_in, "faces", BM_FACE) { - float f_cent[3]; - float f_len_test; - - /* clear dirty flag */ - BM_elem_flag_disable(f, BM_ELEM_TAG); - - if (BMO_elem_flag_test(bm, f, FACE_VIS)) - continue; - - if (!f_start) f_start = f; - - BM_face_calc_center_bounds(f, f_cent); - - if ((f_len_test = len_squared_v3(f_cent)) > f_len_best) { - f_len_best = f_len_test; - f_start = f; - copy_v3_v3(f_start_cent, f_cent); - } + for (j = 0; j < fg_len; j++) { + faces_grp[j] = faces_arr[groups_array[fg_sta + j]]; + is_calc |= BMO_elem_flag_test_bool(bm, faces_grp[j], FACE_FLAG); } - /* check sanity (while loop ensures) */ - BLI_assert(f_start != NULL); - - /* make sure the starting face has the correct winding */ - if (dot_v3v3(f_start_cent, f_start->no) < 0.0f) { - BM_face_normal_flip(bm, f_start); - } - - /* now that we've found our starting face, make all connected faces - * have the same winding. this is done recursively, using a manual - * stack (if we use simple function recursion, we'd end up overloading - * the stack on large meshes). */ - STACK_INIT(fstack); - - STACK_PUSH(fstack, f_start); - BMO_elem_flag_enable(bm, f_start, FACE_VIS); - tot_touch++; - - while ((f = STACK_POP(fstack))) { - BMIter liter; - BMLoop *l; - - BM_ITER_ELEM (l, &liter, f, BM_LOOPS_OF_FACE) { - BMLoop *l_other = l->radial_next; - - if ((l_other == l) || l_other->radial_next != l) { - continue; - } - - if (BMO_elem_flag_test(bm, l_other->f, FACE_FLAG)) { - if (!BMO_elem_flag_test(bm, l_other->f, FACE_VIS)) { - BMO_elem_flag_enable(bm, l_other->f, FACE_VIS); - tot_touch++; - - - if (l_other->v == l->v) { - BM_face_normal_flip(bm, l_other->f); - } - - STACK_PUSH(fstack, l_other->f); - } - } - } + if (is_calc) { + bmo_recalc_face_normals_array(bm, faces_grp, fg_len, FACE_FLAG); } } - MEM_freeN(fstack); + + if (faces_arr) MEM_freeN(faces_arr); + MEM_freeN(faces_grp); + + MEM_freeN(groups_array); + MEM_freeN(group_index); } From 861f9e10f7ea9d1d38130ecbc983fe8f8dac93d0 Mon Sep 17 00:00:00 2001 From: Antony Riakiotakis Date: Mon, 8 Jul 2013 15:35:53 +0000 Subject: [PATCH 05/22] Attempt to fix #35057, disable threading if diameter of the brush becomes too small. Typically this would happen if the number of buckets is clipped to the maximum value. This avoids thread overhead. A better fix might be to do bucket-brush intersection on main thread and dispatch threads to process bucket hits as they become available. This way only one thread at most would end up being used in such cases anyway. A better task scheduler is needed for that though, leaving for after GSOC. --- .../blender/editors/sculpt_paint/paint_image_proj.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/source/blender/editors/sculpt_paint/paint_image_proj.c b/source/blender/editors/sculpt_paint/paint_image_proj.c index 553a5cbe9ac..19953723fef 100644 --- a/source/blender/editors/sculpt_paint/paint_image_proj.c +++ b/source/blender/editors/sculpt_paint/paint_image_proj.c @@ -2820,6 +2820,8 @@ static void project_paint_begin(ProjPaintState *ps) const int diameter = 2 * BKE_brush_size_get(ps->scene, ps->brush); + bool reset_threads = false; + /* ---- end defines ---- */ if (ps->source == PROJ_SRC_VIEW) @@ -3064,6 +3066,10 @@ static void project_paint_begin(ProjPaintState *ps) /* printf("\tscreenspace bucket division x:%d y:%d\n", ps->buckets_x, ps->buckets_y); */ + if (ps->buckets_x > PROJ_BUCKET_RECT_MAX || ps->buckets_y > PROJ_BUCKET_RECT_MAX) { + reset_threads = true; + } + /* really high values could cause problems since it has to allocate a few * (ps->buckets_x*ps->buckets_y) sized arrays */ CLAMP(ps->buckets_x, PROJ_BUCKET_RECT_MIN, PROJ_BUCKET_RECT_MAX); @@ -3089,6 +3095,11 @@ static void project_paint_begin(ProjPaintState *ps) ps->thread_tot = BKE_scene_num_threads(ps->scene); + /* workaround for #35057, disable threading if diameter is less than is possible for + * optimum bucket number generation */ + if (reset_threads) + ps->thread_tot = 1; + for (a = 0; a < ps->thread_tot; a++) { ps->arena_mt[a] = BLI_memarena_new(1 << 16, "project paint arena"); } From 3ce280e825cdd7bcbdd58cb10537a2aa11119ee3 Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Mon, 8 Jul 2013 17:56:51 +0000 Subject: [PATCH 06/22] Fix #35960, #36044: blender internal viewport rendering crash while editing data. Now the viewport rendering thread will lock the main thread while it is exporting objects to render data. This is not ideal if you have big scenes that might block the UI, but Cycles does the same, and it's fairly quick because the same evaluated mesh can be used as for viewport drawing. It's the only way to get things stable until the thread safe dependency graph is here. This adds a mechanism to the job system for jobs to lock the main thread, using a new 'ticket mutex lock' which is a mutex lock that gives priority to the first thread that tries to lock the mutex. Still to solve: undo/redo crashes. --- source/blender/blenlib/BLI_threads.h | 12 ++++ source/blender/blenlib/intern/threads.c | 46 ++++++++++++++ .../blender/editors/render/render_internal.c | 13 +++- .../render/extern/include/RE_pipeline.h | 1 + .../render/intern/source/convertblender.c | 20 +++--- .../blender/render/intern/source/pipeline.c | 7 ++- source/blender/windowmanager/WM_api.h | 3 + source/blender/windowmanager/intern/wm_jobs.c | 62 ++++++++++++++++--- 8 files changed, 146 insertions(+), 18 deletions(-) diff --git a/source/blender/blenlib/BLI_threads.h b/source/blender/blenlib/BLI_threads.h index 331cac3ed76..154986936a2 100644 --- a/source/blender/blenlib/BLI_threads.h +++ b/source/blender/blenlib/BLI_threads.h @@ -131,6 +131,18 @@ void BLI_rw_mutex_free(ThreadRWMutex *mutex); void BLI_rw_mutex_lock(ThreadRWMutex *mutex, int mode); void BLI_rw_mutex_unlock(ThreadRWMutex *mutex); +/* Ticket Mutex Lock + * + * This is a 'fair' mutex in that it will grant the lock to the first thread + * that requests it. */ + +typedef struct TicketMutex TicketMutex; + +TicketMutex *BLI_ticket_mutex_alloc(void); +void BLI_ticket_mutex_free(TicketMutex *ticket); +void BLI_ticket_mutex_lock(TicketMutex *ticket); +void BLI_ticket_mutex_unlock(TicketMutex *ticket); + /* ThreadedWorker * * A simple tool for dispatching work to a limited number of threads diff --git a/source/blender/blenlib/intern/threads.c b/source/blender/blenlib/intern/threads.c index e0ea3bbf685..2b6fb52c49c 100644 --- a/source/blender/blenlib/intern/threads.c +++ b/source/blender/blenlib/intern/threads.c @@ -508,6 +508,52 @@ void BLI_rw_mutex_free(ThreadRWMutex *mutex) MEM_freeN(mutex); } +/* Ticket Mutex Lock */ + +struct TicketMutex { + pthread_cond_t cond; + pthread_mutex_t mutex; + unsigned int queue_head, queue_tail; +}; + +TicketMutex *BLI_ticket_mutex_alloc(void) +{ + TicketMutex *ticket = MEM_callocN(sizeof(TicketMutex), "TicketMutex"); + + pthread_cond_init(&ticket->cond, NULL); + pthread_mutex_init(&ticket->mutex, NULL); + + return ticket; +} + +void BLI_ticket_mutex_free(TicketMutex *ticket) +{ + pthread_mutex_destroy(&ticket->mutex); + pthread_cond_destroy(&ticket->cond); + MEM_freeN(ticket); +} + +void BLI_ticket_mutex_lock(TicketMutex *ticket) +{ + unsigned int queue_me; + + pthread_mutex_lock(&ticket->mutex); + queue_me = ticket->queue_tail++; + + while (queue_me != ticket->queue_head) + pthread_cond_wait(&ticket->cond, &ticket->mutex); + + pthread_mutex_unlock(&ticket->mutex); +} + +void BLI_ticket_mutex_unlock(TicketMutex *ticket) +{ + pthread_mutex_lock(&ticket->mutex); + ticket->queue_head++; + pthread_cond_broadcast(&ticket->cond); + pthread_mutex_unlock(&ticket->mutex); +} + /* ************************************************ */ typedef struct ThreadedWorker { diff --git a/source/blender/editors/render/render_internal.c b/source/blender/editors/render/render_internal.c index 09138a5523a..03ccb2496a1 100644 --- a/source/blender/editors/render/render_internal.c +++ b/source/blender/editors/render/render_internal.c @@ -733,6 +733,7 @@ typedef struct RenderPreview { /* from wmJob */ void *owner; short *stop, *do_update; + wmJob *job; Scene *scene; ScrArea *sa; @@ -913,8 +914,15 @@ static void render_view3d_startjob(void *customdata, short *stop, short *do_upda else lay = rp->v3d->lay; RE_SetView(re, rp->viewmat); - + + /* copying blender data while main thread is locked, to avoid crashes */ + WM_job_main_thread_lock_acquire(rp->job); RE_Database_FromScene(re, rp->bmain, rp->scene, lay, 0); // 0= dont use camera view + WM_job_main_thread_lock_release(rp->job); + + /* do preprocessing like building raytree, shadows, volumes, SSS */ + RE_Database_Preprocess(re); + // printf("dbase update\n"); } else { @@ -958,7 +966,8 @@ static void render_view3d_do(RenderEngine *engine, const bContext *C, int keep_d wm_job = WM_jobs_get(CTX_wm_manager(C), CTX_wm_window(C), CTX_wm_region(C), "Render Preview", WM_JOB_EXCL_RENDER, WM_JOB_TYPE_RENDER_PREVIEW); rp = MEM_callocN(sizeof(RenderPreview), "render preview"); - + rp->job = wm_job; + /* customdata for preview thread */ rp->scene = scene; rp->engine = engine; diff --git a/source/blender/render/extern/include/RE_pipeline.h b/source/blender/render/extern/include/RE_pipeline.h index da53bc5a819..e154fd42119 100644 --- a/source/blender/render/extern/include/RE_pipeline.h +++ b/source/blender/render/extern/include/RE_pipeline.h @@ -207,6 +207,7 @@ void RE_GetViewPlane(struct Render *re, rctf *viewplane, rcti *disprect); /* make or free the dbase */ void RE_Database_FromScene(struct Render *re, struct Main *bmain, struct Scene *scene, unsigned int lay, int use_camera_view); +void RE_Database_Preprocess(struct Render *re); void RE_Database_Free(struct Render *re); /* project dbase again, when viewplane/perspective changed */ diff --git a/source/blender/render/intern/source/convertblender.c b/source/blender/render/intern/source/convertblender.c index 6e70b4bcfc9..bd2d292f633 100644 --- a/source/blender/render/intern/source/convertblender.c +++ b/source/blender/render/intern/source/convertblender.c @@ -5321,14 +5321,10 @@ void RE_Database_FromScene(Render *re, Main *bmain, Scene *scene, unsigned int l database_init_objects(re, lay, 0, 0, 0, 0); if (!re->test_break(re->tbh)) { - int tothalo; - set_material_lightgroups(re); for (sce= re->scene; sce; sce= sce->set) set_renderlayer_lightgroups(re, sce); - slurph_opt= 1; - /* for now some clumsy copying still */ re->i.totvert= re->totvert; re->i.totface= re->totvlak; @@ -5336,7 +5332,16 @@ void RE_Database_FromScene(Render *re, Main *bmain, Scene *scene, unsigned int l re->i.tothalo= re->tothalo; re->i.totlamp= re->totlamp; re->stats_draw(re->sdh, &re->i); - + } + + slurph_opt= 1; +} + +void RE_Database_Preprocess(Render *re) +{ + if (!re->test_break(re->tbh)) { + int tothalo; + /* don't sort stars */ tothalo= re->tothalo; if (!re->test_break(re->tbh)) { @@ -5392,7 +5397,6 @@ void RE_Database_FromScene(Render *re, Main *bmain, Scene *scene, unsigned int l if (!re->test_break(re->tbh)) if (re->r.mode & R_RAYTRACE) volume_precache(re); - } if (re->test_break(re->tbh)) @@ -5866,8 +5870,10 @@ void RE_Database_FromScene_Vectors(Render *re, Main *bmain, Scene *sce, unsigned RE_Database_Free(re); re->strandsurface= strandsurface; - if (!re->test_break(re->tbh)) + if (!re->test_break(re->tbh)) { RE_Database_FromScene(re, bmain, sce, lay, 1); + RE_Database_Preprocess(re); + } if (!re->test_break(re->tbh)) { int vectorlay= get_vector_renderlayers(re->scene); diff --git a/source/blender/render/intern/source/pipeline.c b/source/blender/render/intern/source/pipeline.c index dfdfe973241..848e94c8d4b 100644 --- a/source/blender/render/intern/source/pipeline.c +++ b/source/blender/render/intern/source/pipeline.c @@ -1142,10 +1142,13 @@ static void do_render_3d(Render *re) re->draw_lock(re->dlh, 1); /* make render verts/faces/halos/lamps */ - if (render_scene_needs_vector(re)) + if (render_scene_needs_vector(re)) { RE_Database_FromScene_Vectors(re, re->main, re->scene, re->lay); - else + } + else { RE_Database_FromScene(re, re->main, re->scene, re->lay, 1); + RE_Database_Preprocess(re); + } /* clear UI drawing locks */ if (re->draw_lock) diff --git a/source/blender/windowmanager/WM_api.h b/source/blender/windowmanager/WM_api.h index 230784d7273..7cb24daaaef 100644 --- a/source/blender/windowmanager/WM_api.h +++ b/source/blender/windowmanager/WM_api.h @@ -404,6 +404,9 @@ void WM_jobs_kill_type(struct wmWindowManager *wm, int job_type); int WM_jobs_has_running(struct wmWindowManager *wm); +void WM_job_main_thread_lock_acquire(struct wmJob *job); +void WM_job_main_thread_lock_release(struct wmJob *job); + /* clipboard */ char *WM_clipboard_text_get(int selection); void WM_clipboard_text_set(char *buf, int selection); diff --git a/source/blender/windowmanager/intern/wm_jobs.c b/source/blender/windowmanager/intern/wm_jobs.c index 03af5e9e8a6..c6e067dc2f9 100644 --- a/source/blender/windowmanager/intern/wm_jobs.c +++ b/source/blender/windowmanager/intern/wm_jobs.c @@ -128,8 +128,43 @@ struct wmJob { ListBase threads; double start_time; + + /* ticket mutex for main thread locking while some job accesses + * data that the main thread might modify at the same time */ + TicketMutex *main_thread_mutex; + bool main_thread_mutex_ending; }; +/* Main thread locking */ + +void WM_job_main_thread_lock_acquire(wmJob *wm_job) +{ + BLI_ticket_mutex_lock(wm_job->main_thread_mutex); + + /* if BLI_end_threads is being called to stop the job before it's finished, + * we no longer need to lock to get access to the main thread as it's + * waiting and can't respond */ + if (wm_job->main_thread_mutex_ending) + BLI_ticket_mutex_unlock(wm_job->main_thread_mutex); +} + +void WM_job_main_thread_lock_release(wmJob *wm_job) +{ + if (!wm_job->main_thread_mutex_ending) + BLI_ticket_mutex_unlock(wm_job->main_thread_mutex); +} + +static void wm_job_main_thread_yield(wmJob *wm_job, bool ending) +{ + if (ending) + wm_job->main_thread_mutex_ending = true; + + /* unlock and lock the ticket mutex. because it's a fair mutex any job that + * is waiting to acquire the lock will get it first, before we can lock */ + BLI_ticket_mutex_unlock(wm_job->main_thread_mutex); + BLI_ticket_mutex_lock(wm_job->main_thread_mutex); +} + /* finds: * if type, compare for it, otherwise any matching job */ @@ -162,13 +197,16 @@ wmJob *WM_jobs_get(wmWindowManager *wm, wmWindow *win, void *owner, const char * if (wm_job == NULL) { wm_job = MEM_callocN(sizeof(wmJob), "new job"); - + BLI_addtail(&wm->jobs, wm_job); wm_job->win = win; wm_job->owner = owner; wm_job->flag = flag; wm_job->job_type = job_type; BLI_strncpy(wm_job->name, name, sizeof(wm_job->name)); + + wm_job->main_thread_mutex = BLI_ticket_mutex_alloc(); + BLI_ticket_mutex_lock(wm_job->main_thread_mutex); } /* else: a running job, be careful */ @@ -369,12 +407,21 @@ void WM_jobs_start(wmWindowManager *wm, wmJob *wm_job) } } +static void wm_job_free(wmWindowManager *wm, wmJob *wm_job) +{ + BLI_remlink(&wm->jobs, wm_job); + BLI_ticket_mutex_unlock(wm_job->main_thread_mutex); + BLI_ticket_mutex_free(wm_job->main_thread_mutex); + MEM_freeN(wm_job); +} + /* stop job, end thread, free data completely */ static void wm_jobs_kill_job(wmWindowManager *wm, wmJob *wm_job) { if (wm_job->running) { /* signal job to end */ wm_job->stop = TRUE; + wm_job_main_thread_yield(wm_job, true); BLI_end_threads(&wm_job->threads); if (wm_job->endjob) @@ -389,9 +436,7 @@ static void wm_jobs_kill_job(wmWindowManager *wm, wmJob *wm_job) wm_job->run_free(wm_job->run_customdata); /* remove wm_job */ - BLI_remlink(&wm->jobs, wm_job); - MEM_freeN(wm_job); - + wm_job_free(wm, wm_job); } /* wait until every job ended */ @@ -483,7 +528,6 @@ void wm_jobs_timer(const bContext *C, wmWindowManager *wm, wmTimer *wt) float total_progress = 0.f; float jobs_progress = 0; - for (wm_job = wm->jobs.first; wm_job; wm_job = wm_jobnext) { wm_jobnext = wm_job->next; @@ -491,6 +535,9 @@ void wm_jobs_timer(const bContext *C, wmWindowManager *wm, wmTimer *wt) /* running threads */ if (wm_job->threads.first) { + + /* let threads get temporary lock over main thread if needed */ + wm_job_main_thread_yield(wm_job, false); /* always call note and update when ready */ if (wm_job->do_update || wm_job->ready) { @@ -522,7 +569,9 @@ void wm_jobs_timer(const bContext *C, wmWindowManager *wm, wmTimer *wt) } wm_job->running = FALSE; + wm_job_main_thread_yield(wm_job, true); BLI_end_threads(&wm_job->threads); + wm_job->main_thread_mutex_ending = false; if (wm_job->endnote) WM_event_add_notifier(C, wm_job->endnote, NULL); @@ -539,8 +588,7 @@ void wm_jobs_timer(const bContext *C, wmWindowManager *wm, wmTimer *wt) wm_job->wt = NULL; /* remove wm_job */ - BLI_remlink(&wm->jobs, wm_job); - MEM_freeN(wm_job); + wm_job_free(wm, wm_job); } } else if (wm_job->flag & WM_JOB_PROGRESS) { From 63042da52af05870ab9aacd234efeb75291bd74f Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Mon, 8 Jul 2013 18:27:32 +0000 Subject: [PATCH 07/22] Fix #36059: region overlap did not show scopes overlapping in the image editor. It's only enabled for some particular regions, this one makes sense to show. --- source/blender/editors/screen/area.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/source/blender/editors/screen/area.c b/source/blender/editors/screen/area.c index 4ddacc3254e..6c0d5ccd844 100644 --- a/source/blender/editors/screen/area.c +++ b/source/blender/editors/screen/area.c @@ -908,11 +908,19 @@ static void region_overlap_fix(ScrArea *sa, ARegion *ar) /* overlapping regions only in the following restricted cases */ static int region_is_overlap(wmWindow *win, ScrArea *sa, ARegion *ar) { - if (U.uiflag2 & USER_REGION_OVERLAP) - if (WM_is_draw_triple(win)) - if (ELEM4(sa->spacetype, SPACE_VIEW3D, SPACE_IMAGE, SPACE_SEQ, SPACE_CLIP)) + if (U.uiflag2 & USER_REGION_OVERLAP) { + if (WM_is_draw_triple(win)) { + if (ELEM(sa->spacetype, SPACE_VIEW3D, SPACE_SEQ)) { if (ELEM3(ar->regiontype, RGN_TYPE_TOOLS, RGN_TYPE_UI, RGN_TYPE_TOOL_PROPS)) return 1; + } + else if (ELEM(sa->spacetype, SPACE_IMAGE, SPACE_CLIP)) { + if (ELEM4(ar->regiontype, RGN_TYPE_TOOLS, RGN_TYPE_UI, RGN_TYPE_TOOL_PROPS, RGN_TYPE_PREVIEW)) + return 1; + } + } + } + return 0; } From 751062fc5fea7a17e9a1656391c841a6489194b2 Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Mon, 8 Jul 2013 22:26:10 +0000 Subject: [PATCH 08/22] Fix #35979, #35937, #35739: undo crashes and missing updates with blender internal viewport rendering. Lots of tweaks here, mainly: * Stop 3D viewport render and free database before undo. * Accumulate update flags rather than replace them each time it rerenders, to avoid previous updates getting lost. * Don't check against Render struct view parameters for changes, those are set in the job thread which might not run before the next update call. --- source/blender/editors/include/ED_render.h | 2 + source/blender/editors/render/render_intern.h | 2 +- .../blender/editors/render/render_internal.c | 240 +++++++++++------- source/blender/editors/render/render_update.c | 2 +- source/blender/editors/util/undo.c | 13 +- .../blender/render/extern/include/RE_engine.h | 12 +- .../render/intern/source/convertblender.c | 10 +- 7 files changed, 189 insertions(+), 92 deletions(-) diff --git a/source/blender/editors/include/ED_render.h b/source/blender/editors/include/ED_render.h index c1ddd9a6294..e8e7643164f 100644 --- a/source/blender/editors/include/ED_render.h +++ b/source/blender/editors/include/ED_render.h @@ -52,6 +52,8 @@ void ED_render_engine_changed(struct Main *bmain); void ED_render_engine_area_exit(struct ScrArea *sa); void ED_render_scene_update(struct Main *bmain, struct Scene *scene, int updated); +void ED_viewport_render_kill_jobs(const struct bContext *C); + /* render_preview.c */ /* stores rendered preview - is also used for icons */ diff --git a/source/blender/editors/render/render_intern.h b/source/blender/editors/render/render_intern.h index edd61bfc5c3..eb09606e57e 100644 --- a/source/blender/editors/render/render_intern.h +++ b/source/blender/editors/render/render_intern.h @@ -83,7 +83,7 @@ void TEXTURE_OT_envmap_clear_all(struct wmOperatorType *ot); /* render_internal.c */ void RENDER_OT_render(struct wmOperatorType *ot); -void render_view3d(struct RenderEngine *engine, const struct bContext *C); +void render_view3d_update(struct RenderEngine *engine, const struct bContext *C); void render_view3d_draw(struct RenderEngine *engine, const struct bContext *C); /* render_opengl.c uses this */ diff --git a/source/blender/editors/render/render_internal.c b/source/blender/editors/render/render_internal.c index 03ccb2496a1..15aa67feefa 100644 --- a/source/blender/editors/render/render_internal.c +++ b/source/blender/editors/render/render_internal.c @@ -728,6 +728,7 @@ void RENDER_OT_render(wmOperatorType *ot) #define PR_UPDATE_VIEW 1 #define PR_UPDATE_RENDERSIZE 2 #define PR_UPDATE_MATERIAL 4 +#define PR_UPDATE_DATABASE 8 typedef struct RenderPreview { /* from wmJob */ @@ -744,8 +745,6 @@ typedef struct RenderPreview { RenderEngine *engine; float viewmat[4][4]; - - int keep_data; } RenderPreview; static int render_view3d_disprect(Scene *scene, ARegion *ar, View3D *v3d, RegionView3D *rv3d, rcti *disprect) @@ -846,7 +845,13 @@ static void render_view3d_startjob(void *customdata, short *stop, short *do_upda float clipsta, clipend, pixsize; bool orth, restore = 0; char name[32]; - + int update_flag; + + update_flag = rp->engine->job_update_flag; + rp->engine->job_update_flag = 0; + + //printf("ma %d res %d view %d db %d\n", update_flag & PR_UPDATE_MATERIAL, update_flag & PR_UPDATE_RENDERSIZE, update_flag & PR_UPDATE_VIEW, update_flag & PR_UPDATE_DATABASE); + G.is_break = FALSE; if (false == render_view3d_get_rects(rp->ar, rp->v3d, rp->rv3d, &viewplane, rp->engine, &clipsta, &clipend, &pixsize, &orth)) @@ -861,13 +866,6 @@ static void render_view3d_startjob(void *customdata, short *stop, short *do_upda sprintf(name, "View3dPreview %p", (void *)rp->ar); re = rp->engine->re = RE_GetRender(name); - if (rp->engine->re == NULL) { - - re = rp->engine->re = RE_NewRender(name); - - rp->keep_data = 0; - } - /* set this always, rp is different for each job */ RE_test_break_cb(re, rp, render_view3d_break); RE_display_draw_cb(re, rp, render_view3d_draw_update); @@ -875,7 +873,7 @@ static void render_view3d_startjob(void *customdata, short *stop, short *do_upda rstats = RE_GetStats(re); - if (rp->keep_data == 0 || rstats->convertdone == 0 || (rp->keep_data & PR_UPDATE_RENDERSIZE)) { + if ((update_flag & (PR_UPDATE_RENDERSIZE|PR_UPDATE_DATABASE)) || rstats->convertdone == 0) { /* no osa, blur, seq, layers, etc for preview render */ rdata = rp->scene->r; rdata.mode &= ~(R_OSA | R_MBLUR | R_BORDER | R_PANORAMA); @@ -901,11 +899,7 @@ static void render_view3d_startjob(void *customdata, short *stop, short *do_upda RE_SetPixelSize(re, pixsize); - /* database free can crash on a empty Render... */ - if (rp->keep_data == 0 && rstats->convertdone) - RE_Database_Free(re); - - if (rstats->convertdone == 0) { + if ((update_flag & PR_UPDATE_DATABASE) || rstats->convertdone == 0) { unsigned int lay = rp->scene->lay; /* allow localview render for objects with lights in normal layers */ @@ -917,12 +911,17 @@ static void render_view3d_startjob(void *customdata, short *stop, short *do_upda /* copying blender data while main thread is locked, to avoid crashes */ WM_job_main_thread_lock_acquire(rp->job); + RE_Database_Free(re); RE_Database_FromScene(re, rp->bmain, rp->scene, lay, 0); // 0= dont use camera view WM_job_main_thread_lock_release(rp->job); /* do preprocessing like building raytree, shadows, volumes, SSS */ RE_Database_Preprocess(re); + /* conversion not completed, need to do it again */ + if (!rstats->convertdone) + rp->engine->job_update_flag |= PR_UPDATE_DATABASE; + // printf("dbase update\n"); } else { @@ -940,8 +939,6 @@ static void render_view3d_startjob(void *customdata, short *stop, short *do_upda /* always rotate back */ if (restore) RE_DataBase_IncrementalView(re, rp->viewmat, 1); - - rp->engine->flag &= ~RE_ENGINE_DO_UPDATE; } } @@ -952,16 +949,88 @@ static void render_view3d_free(void *customdata) MEM_freeN(rp); } -static void render_view3d_do(RenderEngine *engine, const bContext *C, int keep_data) +static bool render_view3d_flag_changed(RenderEngine *engine, const bContext *C) +{ + RegionView3D *rv3d = CTX_wm_region_view3d(C); + View3D *v3d = CTX_wm_view3d(C); + ARegion *ar = CTX_wm_region(C); + Scene *scene = CTX_data_scene(C); + Render *re; + rctf viewplane; + rcti disprect; + float clipsta, clipend; + bool orth; + int job_update_flag = 0; + char name[32]; + + /* ensure render engine exists */ + re = engine->re; + + if (!re) { + sprintf(name, "View3dPreview %p", (void *)ar); + re = engine->re = RE_GetRender(name); + if (!re) + re = engine->re = RE_NewRender(name); + + engine->update_flag |= RE_ENGINE_UPDATE_DATABASE; + } + + /* check update_flag */ + if (engine->update_flag & RE_ENGINE_UPDATE_MA) + job_update_flag |= PR_UPDATE_MATERIAL; + + if (engine->update_flag & RE_ENGINE_UPDATE_OTHER) + job_update_flag |= PR_UPDATE_MATERIAL; + + if (engine->update_flag & RE_ENGINE_UPDATE_DATABASE) + job_update_flag |= PR_UPDATE_DATABASE; + + engine->update_flag = 0; + + /* check if viewport changed */ + if (engine->last_winx != ar->winx || engine->last_winy != ar->winy) { + engine->last_winx = ar->winx; + engine->last_winy = ar->winy; + job_update_flag |= PR_UPDATE_RENDERSIZE; + } + + if (compare_m4m4(engine->last_viewmat, rv3d->viewmat, 0.00001f) == 0) { + copy_m4_m4(engine->last_viewmat, rv3d->viewmat); + job_update_flag |= PR_UPDATE_VIEW; + } + + render_view3d_get_rects(ar, v3d, rv3d, &viewplane, engine, &clipsta, &clipend, NULL, &orth); + + if (BLI_rctf_compare(&viewplane, &engine->last_viewplane, 0.00001f) == 0) { + engine->last_viewplane = viewplane; + job_update_flag |= PR_UPDATE_VIEW; + } + + render_view3d_disprect(scene, ar, v3d, rv3d, &disprect); + if (BLI_rcti_compare(&disprect, &engine->last_disprect) == 0) { + engine->last_disprect = disprect; + job_update_flag |= PR_UPDATE_RENDERSIZE; + } + + /* any changes? go ahead and rerender */ + if (job_update_flag) { + engine->job_update_flag |= job_update_flag; + return true; + } + + return false; +} + +static void render_view3d_do(RenderEngine *engine, const bContext *C) { wmJob *wm_job; RenderPreview *rp; Scene *scene = CTX_data_scene(C); - if (CTX_wm_window(C) == NULL) { - engine->flag |= RE_ENGINE_DO_UPDATE; + if (CTX_wm_window(C) == NULL) + return; + if (!render_view3d_flag_changed(engine, C)) return; - } wm_job = WM_jobs_get(CTX_wm_manager(C), CTX_wm_window(C), CTX_wm_region(C), "Render Preview", WM_JOB_EXCL_RENDER, WM_JOB_TYPE_RENDER_PREVIEW); @@ -976,7 +1045,6 @@ static void render_view3d_do(RenderEngine *engine, const bContext *C, int keep_d rp->v3d = rp->sa->spacedata.first; rp->rv3d = CTX_wm_region_view3d(C); rp->bmain = CTX_data_main(C); - rp->keep_data = keep_data; copy_m4_m4(rp->viewmat, rp->rv3d->viewmat); /* dont alloc in threads */ @@ -991,80 +1059,33 @@ static void render_view3d_do(RenderEngine *engine, const bContext *C, int keep_d WM_jobs_start(CTX_wm_manager(C), wm_job); engine->flag &= ~RE_ENGINE_DO_UPDATE; - } /* callback for render engine , on changes */ -void render_view3d(RenderEngine *engine, const bContext *C) +void render_view3d_update(RenderEngine *engine, const bContext *C) { - render_view3d_do(engine, C, 0); -} + /* this shouldn't be needed and causes too many database rebuilds, but we + * aren't actually tracking updates for all relevent datablocks so this is + * a catch-all for updates */ + engine->update_flag |= RE_ENGINE_UPDATE_DATABASE; -static int render_view3d_changed(RenderEngine *engine, const bContext *C) -{ - ARegion *ar = CTX_wm_region(C); - Render *re; - int update = 0; - char name[32]; - - sprintf(name, "View3dPreview %p", (void *)ar); - re = RE_GetRender(name); - - if (re) { - RegionView3D *rv3d = CTX_wm_region_view3d(C); - View3D *v3d = CTX_wm_view3d(C); - Scene *scene = CTX_data_scene(C); - rctf viewplane, viewplane1; - rcti disprect, disprect1; - float mat[4][4]; - float clipsta, clipend; - bool orth; - - if (engine->update_flag & RE_ENGINE_UPDATE_MA) - update |= PR_UPDATE_MATERIAL; - - if (engine->update_flag & RE_ENGINE_UPDATE_OTHER) - update |= PR_UPDATE_MATERIAL; - - engine->update_flag = 0; - - if (engine->resolution_x != ar->winx || engine->resolution_y != ar->winy) - update |= PR_UPDATE_RENDERSIZE; - - RE_GetView(re, mat); - if (compare_m4m4(mat, rv3d->viewmat, 0.00001f) == 0) { - update |= PR_UPDATE_VIEW; - } - - render_view3d_get_rects(ar, v3d, rv3d, &viewplane, engine, &clipsta, &clipend, NULL, &orth); - RE_GetViewPlane(re, &viewplane1, &disprect1); - - if (BLI_rctf_compare(&viewplane, &viewplane1, 0.00001f) == 0) - update |= PR_UPDATE_VIEW; - - render_view3d_disprect(scene, ar, v3d, rv3d, &disprect); - if (BLI_rcti_compare(&disprect, &disprect1) == 0) - update |= PR_UPDATE_RENDERSIZE; - - if (update) - engine->flag |= RE_ENGINE_DO_UPDATE; - //if (update) - // printf("changed ma %d res %d view %d\n", update & PR_UPDATE_MATERIAL, update & PR_UPDATE_RENDERSIZE, update & PR_UPDATE_VIEW); - } - - return update; + render_view3d_do(engine, C); } void render_view3d_draw(RenderEngine *engine, const bContext *C) { Render *re = engine->re; RenderResult rres; - int keep_data = render_view3d_changed(engine, C); + char name[32]; - if (engine->flag & RE_ENGINE_DO_UPDATE) - render_view3d_do(engine, C, keep_data); - - if (re == NULL) return; + render_view3d_do(engine, C); + + if (re == NULL) { + sprintf(name, "View3dPreview %p", (void *)CTX_wm_region(C)); + re = RE_GetRender(name); + + if (re == NULL) return; + } RE_AcquireResultImage(re, &rres); @@ -1114,3 +1135,52 @@ void render_view3d_draw(RenderEngine *engine, const bContext *C) RE_ReleaseResultImage(re); } + +void ED_viewport_render_kill_jobs(const bContext *C) +{ + wmWindowManager *wm = CTX_wm_manager(C); + Main *bmain = CTX_data_main(C); + bScreen *sc; + ScrArea *sa; + ARegion *ar; + + if (!wm) + return; + + /* kill all actively running jobs */ + WM_jobs_kill(wm, NULL, render_view3d_startjob); + + /* loop over 3D view render engines */ + for (sc = bmain->screen.first; sc; sc = sc->id.next) { + for (sa = sc->areabase.first; sa; sa = sa->next) { + if (sa->spacetype != SPACE_VIEW3D) + continue; + + for (ar = sa->regionbase.first; ar; ar = ar->next) { + RegionView3D *rv3d; + + if (ar->regiontype != RGN_TYPE_WINDOW) + continue; + + rv3d = ar->regiondata; + + if (rv3d->render_engine) { + /* free render database now before we change data, because + * RE_Database_Free will also loop over blender data */ + char name[32]; + Render *re; + + sprintf(name, "View3dPreview %p", (void *)ar); + re = RE_GetRender(name); + + if (re) + RE_Database_Free(re); + + /* tag render engine to update entire database */ + rv3d->render_engine->update_flag |= RE_ENGINE_UPDATE_DATABASE; + } + } + } + } +} + diff --git a/source/blender/editors/render/render_update.c b/source/blender/editors/render/render_update.c index 436aef943e4..5f74bf6576a 100644 --- a/source/blender/editors/render/render_update.c +++ b/source/blender/editors/render/render_update.c @@ -539,7 +539,7 @@ void ED_render_internal_init(void) { RenderEngineType *ret = RE_engines_find("BLENDER_RENDER"); - ret->view_update = render_view3d; + ret->view_update = render_view3d_update; ret->view_draw = render_view3d_draw; } diff --git a/source/blender/editors/util/undo.c b/source/blender/editors/util/undo.c index 7f2e5b4b81c..52f87c19dc8 100644 --- a/source/blender/editors/util/undo.c +++ b/source/blender/editors/util/undo.c @@ -59,6 +59,7 @@ #include "ED_mball.h" #include "ED_mesh.h" #include "ED_object.h" +#include "ED_render.h" #include "ED_screen.h" #include "ED_sculpt.h" #include "ED_util.h" @@ -140,9 +141,12 @@ static int ed_undo_step(bContext *C, int step, const char *undoname) SpaceImage *sima = (SpaceImage *)sa->spacedata.first; if ((obact && (obact->mode & OB_MODE_TEXTURE_PAINT)) || (sima->mode == SI_MODE_PAINT)) { - if (!ED_undo_paint_step(C, UNDO_PAINT_IMAGE, step, undoname) && undoname) - if (U.uiflag & USER_GLOBALUNDO) + if (!ED_undo_paint_step(C, UNDO_PAINT_IMAGE, step, undoname) && undoname) { + if (U.uiflag & USER_GLOBALUNDO) { + ED_viewport_render_kill_jobs(C); BKE_undo_name(C, undoname); + } + } WM_event_add_notifier(C, NC_WINDOW, NULL); return OPERATOR_FINISHED; @@ -192,6 +196,8 @@ static int ed_undo_step(bContext *C, int step, const char *undoname) /* for example, texface stores image pointers */ undo_editmode_clear(); + ED_viewport_render_kill_jobs(C); + if (undoname) BKE_undo_name(C, undoname); else @@ -363,6 +369,8 @@ int ED_undo_operator_repeat(bContext *C, struct wmOperator *op) { int retval; + ED_viewport_render_kill_jobs(C); + if (G.debug & G_DEBUG) printf("redo_cb: operator redo %s\n", op->type->name); ED_undo_pop_op(C, op); @@ -529,6 +537,7 @@ static int undo_history_exec(bContext *C, wmOperator *op) WM_event_add_notifier(C, NC_GEOM | ND_DATA, NULL); } else { + ED_viewport_render_kill_jobs(C); BKE_undo_number(C, item); WM_event_add_notifier(C, NC_SCENE | ND_LAYER_CONTENT, CTX_data_scene(C)); } diff --git a/source/blender/render/extern/include/RE_engine.h b/source/blender/render/extern/include/RE_engine.h index b6c3668512b..786c67f5305 100644 --- a/source/blender/render/extern/include/RE_engine.h +++ b/source/blender/render/extern/include/RE_engine.h @@ -70,6 +70,7 @@ struct Scene; /* RenderEngine.update_flag, used by internal now */ #define RE_ENGINE_UPDATE_MA 1 #define RE_ENGINE_UPDATE_OTHER 2 +#define RE_ENGINE_UPDATE_DATABASE 4 extern ListBase R_engines; @@ -97,7 +98,7 @@ typedef struct RenderEngine { RenderEngineType *type; void *py_instance; - int flag, update_flag; + int flag; struct Object *camera_override; int tile_x; @@ -110,6 +111,15 @@ typedef struct RenderEngine { int resolution_x, resolution_y; struct ReportList *reports; + + /* for blender internal only */ + int update_flag; + int job_update_flag; + + rctf last_viewplane; + rcti last_disprect; + float last_viewmat[4][4]; + int last_winx, last_winy; } RenderEngine; RenderEngine *RE_engine_create(RenderEngineType *type); diff --git a/source/blender/render/intern/source/convertblender.c b/source/blender/render/intern/source/convertblender.c index bd2d292f633..ce71ab188a9 100644 --- a/source/blender/render/intern/source/convertblender.c +++ b/source/blender/render/intern/source/convertblender.c @@ -4863,7 +4863,11 @@ static void init_render_object(Render *re, Object *ob, Object *par, DupliObject void RE_Database_Free(Render *re) { LampRen *lar; - + + /* will crash if we try to free empty database */ + if (!re->i.convertdone) + return; + /* statistics for debugging render memory usage */ if ((G.debug & G_DEBUG) && (G.is_rendering)) { if ((re->r.scemode & (R_BUTS_PREVIEW|R_VIEWPORT_PREVIEW))==0) { @@ -5399,8 +5403,10 @@ void RE_Database_Preprocess(Render *re) volume_precache(re); } - if (re->test_break(re->tbh)) + if (re->test_break(re->tbh)) { + re->i.convertdone = TRUE; RE_Database_Free(re); + } else re->i.convertdone = TRUE; From 8ab1fadf3d7a987cbdf378162570e9dc658b74ec Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Mon, 8 Jul 2013 22:41:12 +0000 Subject: [PATCH 09/22] Fix #36063: cycles 3D viewport was incorrectly influenced by blender internal material halo settings. --- source/blender/editors/space_view3d/drawobject.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/editors/space_view3d/drawobject.c b/source/blender/editors/space_view3d/drawobject.c index d3cf7ae9eea..d5ecdaf0596 100644 --- a/source/blender/editors/space_view3d/drawobject.c +++ b/source/blender/editors/space_view3d/drawobject.c @@ -3273,7 +3273,7 @@ static void draw_mesh_fancy(Scene *scene, ARegion *ar, View3D *v3d, RegionView3D Object *ob = base->object; Mesh *me = ob->data; Material *ma = give_current_material(ob, 1); - const short hasHaloMat = (ma && (ma->material_type == MA_TYPE_HALO)); + const short hasHaloMat = (ma && (ma->material_type == MA_TYPE_HALO) && !BKE_scene_use_new_shading_nodes(scene)); eWireDrawMode draw_wire = OBDRAW_WIRE_OFF; int /* totvert,*/ totedge, totface; DerivedMesh *dm = mesh_get_derived_final(scene, ob, scene->customdata_mask); From a2553444fa74cb2a0a43b98bf89b43cbfd1cb103 Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Mon, 8 Jul 2013 22:41:14 +0000 Subject: [PATCH 10/22] Fix #35969: blender internal and cycles not updating mesh while in edit mode. Patch for blender internal made by Campbell. --- intern/cycles/blender/blender_mesh.cpp | 3 +++ source/blender/editors/render/render_internal.c | 7 ++++++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/intern/cycles/blender/blender_mesh.cpp b/intern/cycles/blender/blender_mesh.cpp index d628fa04f92..90278f215c0 100644 --- a/intern/cycles/blender/blender_mesh.cpp +++ b/intern/cycles/blender/blender_mesh.cpp @@ -462,6 +462,9 @@ Mesh *BlenderSync::sync_mesh(BL::Object b_ob, bool object_updated, bool hide_tri mesh->name = ustring(b_ob_data.name().c_str()); if(render_layer.use_surfaces || render_layer.use_hair) { + if(preview) + b_ob.update_from_editmode(); + BL::Mesh b_mesh = object_to_mesh(b_data, b_ob, b_scene, true, !preview, need_undeformed); if(b_mesh) { diff --git a/source/blender/editors/render/render_internal.c b/source/blender/editors/render/render_internal.c index 15aa67feefa..7a2ece66ba4 100644 --- a/source/blender/editors/render/render_internal.c +++ b/source/blender/editors/render/render_internal.c @@ -982,8 +982,13 @@ static bool render_view3d_flag_changed(RenderEngine *engine, const bContext *C) if (engine->update_flag & RE_ENGINE_UPDATE_OTHER) job_update_flag |= PR_UPDATE_MATERIAL; - if (engine->update_flag & RE_ENGINE_UPDATE_DATABASE) + if (engine->update_flag & RE_ENGINE_UPDATE_DATABASE) { job_update_flag |= PR_UPDATE_DATABASE; + + /* load editmesh */ + if (scene->obedit) + ED_object_editmode_load(scene->obedit); + } engine->update_flag = 0; From b2b6d564431db71e1c9176ca541a0dd9e7f91168 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Mon, 8 Jul 2013 22:57:51 +0000 Subject: [PATCH 11/22] move keymap ui into modules, its not loaded on startup anymore. --- .../bl_ui/space_userpref_keymap.py => modules/rna_keymap_ui.py} | 0 release/scripts/startup/bl_ui/space_userpref.py | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename release/scripts/{startup/bl_ui/space_userpref_keymap.py => modules/rna_keymap_ui.py} (100%) diff --git a/release/scripts/startup/bl_ui/space_userpref_keymap.py b/release/scripts/modules/rna_keymap_ui.py similarity index 100% rename from release/scripts/startup/bl_ui/space_userpref_keymap.py rename to release/scripts/modules/rna_keymap_ui.py diff --git a/release/scripts/startup/bl_ui/space_userpref.py b/release/scripts/startup/bl_ui/space_userpref.py index a5e4b6e10bf..3e281e08983 100644 --- a/release/scripts/startup/bl_ui/space_userpref.py +++ b/release/scripts/startup/bl_ui/space_userpref.py @@ -1052,7 +1052,7 @@ class USERPREF_PT_input(Panel): row.separator() def draw(self, context): - from bl_ui.space_userpref_keymap import draw_keymaps + from rna_keymap_ui import draw_keymaps layout = self.layout From 3d847ed6e6e0a063d1676ad9c01bcc33b23b8ac4 Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Mon, 8 Jul 2013 23:31:45 +0000 Subject: [PATCH 12/22] Fix #36064: cycles direct/indirect light passes with materials that have zero RGB color components gave non-grey results when you might no expect it. What happens is that some of the color channels are zero in the direct light pass because their channel is zero in the color pass. The direct light pass is defined as lighting divided by the color pass, and we can't divide by zero. We do a division after all samples are added together to ensure that multiplication in the compositor gives the exact combined pass even with antialiasing, DoF, .. Found a simple tweak here, instead of setting such channels to zero it will set it to the average of other non-zero color channels, which makes the results look like the expected grey. --- intern/cycles/render/buffers.cpp | 2 +- intern/cycles/util/util_math.h | 36 ++++++++++++++++++++++++++++++++ 2 files changed, 37 insertions(+), 1 deletion(-) diff --git a/intern/cycles/render/buffers.cpp b/intern/cycles/render/buffers.cpp index 572cfae45cd..b509134512b 100644 --- a/intern/cycles/render/buffers.cpp +++ b/intern/cycles/render/buffers.cpp @@ -223,7 +223,7 @@ bool RenderBuffers::get_pass_rect(PassType type, float exposure, int sample, int float3 f = make_float3(in[0], in[1], in[2]); float3 f_divide = make_float3(in_divide[0], in_divide[1], in_divide[2]); - f = safe_divide_color(f*exposure, f_divide); + f = safe_divide_even_color(f*exposure, f_divide); pixels[0] = f.x; pixels[1] = f.y; diff --git a/intern/cycles/util/util_math.h b/intern/cycles/util/util_math.h index 14ebf311a4b..cde547cd77c 100644 --- a/intern/cycles/util/util_math.h +++ b/intern/cycles/util/util_math.h @@ -1100,6 +1100,42 @@ __device_inline float3 safe_divide_color(float3 a, float3 b) return make_float3(x, y, z); } +__device_inline float3 safe_divide_even_color(float3 a, float3 b) +{ + float x, y, z; + + x = (b.x != 0.0f)? a.x/b.x: 0.0f; + y = (b.y != 0.0f)? a.y/b.y: 0.0f; + z = (b.z != 0.0f)? a.z/b.z: 0.0f; + + /* try to get grey even if b is zero */ + if(b.x == 0.0f) { + if(b.y == 0.0f) { + x = z; + y = z; + } + else if(b.z == 0.0f) { + x = y; + z = y; + } + else + x = 0.5f*(y + z); + } + else if(b.y == 0.0f) { + if(b.z == 0.0f) { + y = x; + z = x; + } + else + y = 0.5f*(x + z); + } + else if(b.z == 0.0f) { + z = 0.5f*(x + y); + } + + return make_float3(x, y, z); +} + /* Rotation of point around axis and angle */ __device_inline float3 rotate_around_axis(float3 p, float3 axis, float angle) From 6c0d97fbcfaf60d6d932bb96f01e8caf30062c50 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Tue, 9 Jul 2013 00:08:18 +0000 Subject: [PATCH 13/22] fix [#35806] Unable to check "Correct UVs" option in "Loop Cut and Slide" (Keymap Editor) --- source/blender/makesrna/intern/rna_access.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/source/blender/makesrna/intern/rna_access.c b/source/blender/makesrna/intern/rna_access.c index f67561954be..5499386dcf1 100644 --- a/source/blender/makesrna/intern/rna_access.c +++ b/source/blender/makesrna/intern/rna_access.c @@ -6385,6 +6385,15 @@ bool RNA_property_equals(PointerRNA *a, PointerRNA *b, PropertyRNA *prop, bool i return equals; } + case PROP_POINTER: + { + if (!STREQ(RNA_property_identifier(prop), "rna_type")) { + PointerRNA propptr_a = RNA_property_pointer_get(a, prop); + PointerRNA propptr_b = RNA_property_pointer_get(b, prop); + return RNA_struct_equals(&propptr_a, &propptr_b, is_strict); + } + } + default: break; } From 14ab39c5e0922ad1f7b92abda7c2cc73df03006f Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Tue, 9 Jul 2013 00:13:17 +0000 Subject: [PATCH 14/22] minor improvements - calc normals only check flag when needed. - keymap, dont get name unless its needed. - keymap, avoid property lookup. - idprop debug print, include pointer, helpful for troubleshooting. --- source/blender/bmesh/intern/bmesh_queries.c | 2 +- source/blender/bmesh/operators/bmo_normals.c | 5 ++++- source/blender/editors/interface/interface_templates.c | 4 ++-- source/blender/python/generic/idprop_py_api.c | 2 +- 4 files changed, 8 insertions(+), 5 deletions(-) diff --git a/source/blender/bmesh/intern/bmesh_queries.c b/source/blender/bmesh/intern/bmesh_queries.c index c0c4e0f0209..a00bef0228b 100644 --- a/source/blender/bmesh/intern/bmesh_queries.c +++ b/source/blender/bmesh/intern/bmesh_queries.c @@ -1761,7 +1761,7 @@ float BM_mesh_calc_volume(BMesh *bm, bool is_signed) /** * TODO (as we need) - * - option to walk over edges. + * - option to walk over faces by verts. * - option to walk over non manifold edges. * * \param bm the BMesh. diff --git a/source/blender/bmesh/operators/bmo_normals.c b/source/blender/bmesh/operators/bmo_normals.c index 7116ef82a32..c4200a37748 100644 --- a/source/blender/bmesh/operators/bmo_normals.c +++ b/source/blender/bmesh/operators/bmo_normals.c @@ -166,7 +166,10 @@ void bmo_recalc_face_normals_exec(BMesh *bm, BMOperator *op) for (j = 0; j < fg_len; j++) { faces_grp[j] = faces_arr[groups_array[fg_sta + j]]; - is_calc |= BMO_elem_flag_test_bool(bm, faces_grp[j], FACE_FLAG); + + if (is_calc == false) { + is_calc = BMO_elem_flag_test_bool(bm, faces_grp[j], FACE_FLAG); + } } if (is_calc) { diff --git a/source/blender/editors/interface/interface_templates.c b/source/blender/editors/interface/interface_templates.c index 57392c60f80..e061ff35025 100644 --- a/source/blender/editors/interface/interface_templates.c +++ b/source/blender/editors/interface/interface_templates.c @@ -3018,16 +3018,16 @@ static void template_keymap_item_properties(uiLayout *layout, const char *title, /* recurse for nested properties */ if (RNA_property_type(prop) == PROP_POINTER) { PointerRNA propptr = RNA_property_pointer_get(ptr, prop); - const char *name = RNA_property_ui_name(prop); if (propptr.data && RNA_struct_is_a(propptr.type, &RNA_OperatorProperties)) { + const char *name = RNA_property_ui_name(prop); template_keymap_item_properties(layout, name, &propptr); continue; } } /* add property */ - uiItemR(flow, ptr, RNA_property_identifier(prop), 0, NULL, ICON_NONE); + uiItemFullR(flow, ptr, prop, -1, 0, 0, NULL, ICON_NONE); } RNA_STRUCT_END; } diff --git a/source/blender/python/generic/idprop_py_api.c b/source/blender/python/generic/idprop_py_api.c index 85bb8125a3a..fc4b78b5c05 100644 --- a/source/blender/python/generic/idprop_py_api.c +++ b/source/blender/python/generic/idprop_py_api.c @@ -1531,7 +1531,7 @@ void IDP_spit(IDProperty *prop) ret_str = PyObject_Repr(ret_dict); Py_DECREF(ret_dict); - printf("IDProperty: %s\n", _PyUnicode_AsString(ret_str)); + printf("IDProperty(%p): %s\n", prop, _PyUnicode_AsString(ret_str)); Py_DECREF(ret_str); From bd5cb6fb3bad20795c942f23da0263ad9e9afc13 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Tue, 9 Jul 2013 01:18:15 +0000 Subject: [PATCH 15/22] fix for own error in normal-recalc r58077, initial face flipping wasn't checking against face-island center. --- source/blender/bmesh/operators/bmo_normals.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/source/blender/bmesh/operators/bmo_normals.c b/source/blender/bmesh/operators/bmo_normals.c index c4200a37748..4291fc22793 100644 --- a/source/blender/bmesh/operators/bmo_normals.c +++ b/source/blender/bmesh/operators/bmo_normals.c @@ -56,7 +56,7 @@ static bool bmo_recalc_normal_edge_filter_cb(BMEdge *e, void *UNUSED(user_data)) */ static void bmo_recalc_face_normals_array(BMesh *bm, BMFace **faces, const int faces_len, const short oflag) { - float cent[3]; + float cent[3], tvec[3]; float (*faces_center)[3] = MEM_mallocN(sizeof(*faces_center) * faces_len, __func__); const float cent_fac = 1.0f / (float)faces_len; int i, f_start_index; @@ -91,7 +91,8 @@ static void bmo_recalc_face_normals_array(BMesh *bm, BMFace **faces, const int f } /* make sure the starting face has the correct winding */ - if (dot_v3v3(faces_center[f_start_index], faces[f_start_index]->no) < 0.0f) { + sub_v3_v3v3(tvec, faces_center[f_start_index], cent); + if (dot_v3v3(tvec, faces[f_start_index]->no) < 0.0f) { BMO_elem_flag_enable(bm, faces[f_start_index], FACE_FLIP); } From 750b30c7dda4b46eb41f0ad611bae0a739b34610 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Tue, 9 Jul 2013 04:00:56 +0000 Subject: [PATCH 16/22] fix [#36008] Can't set values higher than 1.0 in HSV colour picker (But can in RGB) - also some strange behavior --- source/blender/editors/interface/interface.c | 22 ++++++++++++++----- .../editors/interface/interface_regions.c | 8 ++++--- 2 files changed, 21 insertions(+), 9 deletions(-) diff --git a/source/blender/editors/interface/interface.c b/source/blender/editors/interface/interface.c index 9ac499fc94b..c3bc87ac647 100644 --- a/source/blender/editors/interface/interface.c +++ b/source/blender/editors/interface/interface.c @@ -2167,6 +2167,15 @@ static void ui_set_but_soft_range(uiBut *but) but->softmin = softmin; but->softmax = softmax; } + else if (but->poin && (but->pointype & UI_BUT_POIN_TYPES)) { + float value = ui_get_but_val(but); + CLAMP(value, but->hardmin, but->hardmax); + but->softmin = min_ff(but->softmin, value); + but->softmax = max_ff(but->softmax, value); + } + else { + BLI_assert(0); + } } /* ******************* Free ********************/ @@ -2368,8 +2377,12 @@ void ui_check_but(uiBut *but) ui_check_but_select(but, &value); /* only update soft range while not editing */ - if (but->rnaprop && !(but->editval || but->editstr || but->editvec)) { - ui_set_but_soft_range(but); + if (!(but->editval || but->editstr || but->editvec)) { + if ((but->rnaprop != NULL) || + (but->poin && (but->pointype & UI_BUT_POIN_TYPES))) + { + ui_set_but_soft_range(but); + } } /* test for min and max, icon sliders, etc */ @@ -2757,13 +2770,10 @@ static uiBut *ui_def_but(uiBlock *block, int type, int retval, const char *str, uiBut *but; int slen; - BLI_assert(width >= 0); - BLI_assert(height >= 0); + BLI_assert(width >= 0 && height >= 0); /* we could do some more error checks here */ if ((type & BUTTYPE) == LABEL) { - if ((poin != NULL || min != 0.0f || max != 0.0f || (a1 == 0.0f && a2 != 0.0f) || (a1 != 0.0f && a1 != 1.0f))) - printf("blah\n"); BLI_assert((poin != NULL || min != 0.0f || max != 0.0f || (a1 == 0.0f && a2 != 0.0f) || (a1 != 0.0f && a1 != 1.0f)) == FALSE); } diff --git a/source/blender/editors/interface/interface_regions.c b/source/blender/editors/interface/interface_regions.c index 9558baf0334..2e915d6357d 100644 --- a/source/blender/editors/interface/interface_regions.c +++ b/source/blender/editors/interface/interface_regions.c @@ -2116,7 +2116,7 @@ static void uiBlockPicker(uiBlock *block, float rgba[4], PointerRNA *ptr, Proper static char tip[50]; static char hexcol[128]; float rgb_gamma[3]; - float min, max, step, precision; + float softmin, softmax, hardmin, hardmax, step, precision; float *hsv = ui_block_hsv_get(block); int yco; @@ -2142,7 +2142,8 @@ static void uiBlockPicker(uiBlock *block, float rgba[4], PointerRNA *ptr, Proper /* sneaky way to check for alpha */ rgba[3] = FLT_MAX; - RNA_property_float_ui_range(ptr, prop, &min, &max, &step, &precision); + RNA_property_float_ui_range(ptr, prop, &softmin, &softmax, &step, &precision); + RNA_property_float_range(ptr, prop, &hardmin, &hardmax); RNA_property_float_get_array(ptr, prop, rgba); switch (U.color_picker_type) { @@ -2196,7 +2197,8 @@ static void uiBlockPicker(uiBlock *block, float rgba[4], PointerRNA *ptr, Proper uiButSetFunc(bt, do_hsv_rna_cb, bt, hsv); bt = uiDefButF(block, NUMSLI, 0, IFACE_("S "), 0, yco -= UI_UNIT_Y, butwidth, UI_UNIT_Y, hsv + 1, 0.0, 1.0, 10, 3, TIP_("Saturation")); uiButSetFunc(bt, do_hsv_rna_cb, bt, hsv); - bt = uiDefButF(block, NUMSLI, 0, IFACE_("V "), 0, yco -= UI_UNIT_Y, butwidth, UI_UNIT_Y, hsv + 2, 0.0, max, 10, 3, TIP_("Value")); + bt = uiDefButF(block, NUMSLI, 0, IFACE_("V "), 0, yco -= UI_UNIT_Y, butwidth, UI_UNIT_Y, hsv + 2, 0.0, softmax, 10, 3, TIP_("Value")); + bt->hardmax = hardmax; /* not common but rgb may be over 1.0 */ uiButSetFunc(bt, do_hsv_rna_cb, bt, hsv); uiBlockEndAlign(block); From d4ff53b760f51d791860b25385f2f9716740d586 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Tue, 9 Jul 2013 06:21:45 +0000 Subject: [PATCH 17/22] fix [#36066] crash when Tab out text object the way Curve.len is used at the moment is really stupid, calculate string size on save for now, but should really store the length in bytes and total number of characters. --- source/blender/blenkernel/intern/text.c | 15 +----- source/blender/blenlib/BLI_string_utf8.h | 4 +- source/blender/blenlib/intern/string_utf8.c | 48 +++++++++++--------- source/blender/blenloader/intern/writefile.c | 15 +++--- source/blender/makesdna/DNA_curve_types.h | 4 ++ 5 files changed, 40 insertions(+), 46 deletions(-) diff --git a/source/blender/blenkernel/intern/text.c b/source/blender/blenkernel/intern/text.c index 296f25e303e..aec924e15b3 100644 --- a/source/blender/blenkernel/intern/text.c +++ b/source/blender/blenkernel/intern/text.c @@ -847,19 +847,6 @@ int txt_utf8_column_to_offset(const char *str, int column) return offset; } -/* returns the real number of characters in string */ -/* not the same as BLI_strlen_utf8, which returns length for wide characters */ -static int txt_utf8_len(const char *src) -{ - int len; - - for (len = 0; *src; len++) { - src += BLI_str_utf8_size(src); - } - - return len; -} - void txt_move_up(Text *text, short sel) { TextLine **linep; @@ -2059,7 +2046,7 @@ void txt_do_undo(Text *text) text->undo_pos--; } buf[i] = 0; - linep = txt_utf8_len(buf); + linep = BLI_strlen_utf8(buf); MEM_freeN(buf); /* skip over the length that was stored again */ diff --git a/source/blender/blenlib/BLI_string_utf8.h b/source/blender/blenlib/BLI_string_utf8.h index d20cbd2a91c..adef843d2cc 100644 --- a/source/blender/blenlib/BLI_string_utf8.h +++ b/source/blender/blenlib/BLI_string_utf8.h @@ -51,8 +51,10 @@ char *BLI_str_prev_char_utf8(const char *p); /* wchar_t functions, copied from blenders own font.c originally */ size_t BLI_wstrlen_utf8(const wchar_t *src); +size_t BLI_strlen_utf8_ex(const char *strc, int *r_len_bytes); size_t BLI_strlen_utf8(const char *strc); -size_t BLI_strnlen_utf8(const char *start, const size_t maxlen); +size_t BLI_strnlen_utf8_ex(const char *strc, const size_t maxlen, int *r_len_bytes); +size_t BLI_strnlen_utf8(const char *strc, const size_t maxlen); size_t BLI_strncpy_wchar_as_utf8(char *__restrict dst, const wchar_t *__restrict src, const size_t maxcpy); size_t BLI_strncpy_wchar_from_utf8(wchar_t *__restrict dst, const char *__restrict src, const size_t maxcpy); diff --git a/source/blender/blenlib/intern/string_utf8.c b/source/blender/blenlib/intern/string_utf8.c index b8dca95ae33..ab0073a7585 100644 --- a/source/blender/blenlib/intern/string_utf8.c +++ b/source/blender/blenlib/intern/string_utf8.c @@ -245,24 +245,16 @@ size_t BLI_wstrlen_utf8(const wchar_t *src) return len; } -/* this is very close to 'BLI_str_utf8_size' functionality, perhaps we should de-duplicate */ -/* size of UTF-8 character in bytes */ -static size_t strlen_utf8_char(const char *strc) +size_t BLI_strlen_utf8_ex(const char *strc, int *r_len_bytes) { - if ((*strc & 0xe0) == 0xc0) { - if ((strc[1] & 0x80) && (strc[1] & 0x40) == 0x00) - return 2; - } - else if ((*strc & 0xf0) == 0xe0) { - if ((strc[1] & strc[2] & 0x80) && ((strc[1] | strc[2]) & 0x40) == 0x00) - return 3; - } - else if ((*strc & 0xf8) == 0xf0) { - if ((strc[1] & strc[2] & strc[3] & 0x80) && ((strc[1] | strc[2] | strc[3]) & 0x40) == 0x00) - return 4; - } + size_t len; + const char *strc_orig = strc; - return 1; + for (len = 0; *strc; len++) + strc += BLI_str_utf8_size_safe(strc); + + *r_len_bytes = (strc - strc_orig); + return len; } size_t BLI_strlen_utf8(const char *strc) @@ -270,25 +262,37 @@ size_t BLI_strlen_utf8(const char *strc) size_t len; for (len = 0; *strc; len++) - strc += strlen_utf8_char(strc); + strc += BLI_str_utf8_size_safe(strc); return len; } +size_t BLI_strnlen_utf8_ex(const char *strc, const size_t maxlen, int *r_len_bytes) +{ + size_t len; + const char *strc_orig = strc; + const char *strc_end = strc + maxlen; + + for (len = 0; *strc && strc < strc_end; len++) { + strc += BLI_str_utf8_size_safe(strc); + } + + *r_len_bytes = (strc - strc_orig); + return len; +} + /** * \param start the string to measure the length. * \param maxlen the string length (in bytes) * \return the unicode length (not in bytes!) */ -size_t BLI_strnlen_utf8(const char *start, const size_t maxlen) +size_t BLI_strnlen_utf8(const char *strc, const size_t maxlen) { - const char *strc = start; - const char *strc_end = start + maxlen; - size_t len; + const char *strc_end = strc + maxlen; for (len = 0; *strc && strc < strc_end; len++) { - strc += strlen_utf8_char(strc); + strc += BLI_str_utf8_size_safe(strc); } return len; diff --git a/source/blender/blenloader/intern/writefile.c b/source/blender/blenloader/intern/writefile.c index 9b6699f3f21..dd4361be1ff 100644 --- a/source/blender/blenloader/intern/writefile.c +++ b/source/blender/blenloader/intern/writefile.c @@ -1642,13 +1642,6 @@ static void write_mballs(WriteData *wd, ListBase *idbase) } } -static int amount_of_chars(char *str) -{ - // Since the data is saved as UTF-8 to the cu->str - // The cu->len is not same as the strlen(cu->str) - return strlen(str); -} - static void write_curves(WriteData *wd, ListBase *idbase) { Curve *cu; @@ -1666,8 +1659,12 @@ static void write_curves(WriteData *wd, ListBase *idbase) if (cu->adt) write_animdata(wd, cu->adt); if (cu->vfont) { - writedata(wd, DATA, amount_of_chars(cu->str)+1, cu->str); - writestruct(wd, DATA, "CharInfo", cu->len+1, cu->strinfo); + /* TODO, sort out 'cu->len', in editmode its character, object mode its bytes */ + int len_bytes; + int len_chars = BLI_strlen_utf8_ex(cu->str, &len_bytes); + + writedata(wd, DATA, len_bytes + 1, cu->str); + writestruct(wd, DATA, "CharInfo", len_chars + 1, cu->strinfo); writestruct(wd, DATA, "TextBox", cu->totbox, cu->tb); } else { diff --git a/source/blender/makesdna/DNA_curve_types.h b/source/blender/makesdna/DNA_curve_types.h index deb9902c35d..964fa11b0a2 100644 --- a/source/blender/makesdna/DNA_curve_types.h +++ b/source/blender/makesdna/DNA_curve_types.h @@ -215,6 +215,10 @@ typedef struct Curve { void *lastsel; /* font part */ + /* WARNING: cu->len is... + * - strlen(cu->str) object-mode (bytes). + * - BLI_strlen_utf8(cu->str) in edit-mode. + * This should be cleaned up and some point, see 'write_curves' - campbell */ short len, lines, pos, spacemode; float spacing, linedist, shear, fsize, wordspace, ulpos, ulheight; float xof, yof; From 34b301f0a421cd844a1c221d8036bf86a6e1ae4e Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Tue, 9 Jul 2013 06:34:54 +0000 Subject: [PATCH 18/22] fix error where if you tried to bridge an odd number of loops after bridging pairs, there was no way to change the options. eventually I think the operator api should support this but for now return finished. --- source/blender/editors/mesh/editmesh_tools.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/source/blender/editors/mesh/editmesh_tools.c b/source/blender/editors/mesh/editmesh_tools.c index 2c721a0a9dc..77a79e57dbc 100644 --- a/source/blender/editors/mesh/editmesh_tools.c +++ b/source/blender/editors/mesh/editmesh_tools.c @@ -4007,8 +4007,9 @@ static int edbm_bridge_edge_loops_exec(bContext *C, wmOperator *op) } if (!EDBM_op_finish(em, &bmop, op, true)) { - return OPERATOR_CANCELLED; - + /* grr, need to return finished so the user can select different options */ + //return OPERATOR_CANCELLED; + return OPERATOR_FINISHED; } else { EDBM_update_generic(em, true, true); From fba8e8861cc8773e79cbb2d21fe2f1fb177ca2b9 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Tue, 9 Jul 2013 07:50:16 +0000 Subject: [PATCH 19/22] fix for error in vertex_group_vert_select_unlocked_poll, check for non existing group wasn't right. --- source/blender/editors/object/object_vgroup.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/editors/object/object_vgroup.c b/source/blender/editors/object/object_vgroup.c index 581c3d25730..5c5352f1671 100644 --- a/source/blender/editors/object/object_vgroup.c +++ b/source/blender/editors/object/object_vgroup.c @@ -3000,7 +3000,7 @@ static int vertex_group_vert_select_unlocked_poll(bContext *C) return 0; } - if (ob->actdef != -1) { + if (ob->actdef != 0) { bDeformGroup *dg = BLI_findlink(&ob->defbase, ob->actdef - 1); if (dg) { return !(dg->flag & DG_LOCK_WEIGHT); From 139754894b995310cf018d789f7ffcda4487233d Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Tue, 9 Jul 2013 08:17:14 +0000 Subject: [PATCH 20/22] fix [#36039] Texture paint bug with face selection on subdivided object original patch by Antony Riakiotakis, made some edits to only check origindex when needed. --- .../editors/sculpt_paint/paint_image_proj.c | 44 ++++++++++++++++--- 1 file changed, 37 insertions(+), 7 deletions(-) diff --git a/source/blender/editors/sculpt_paint/paint_image_proj.c b/source/blender/editors/sculpt_paint/paint_image_proj.c index 19953723fef..017232d1856 100644 --- a/source/blender/editors/sculpt_paint/paint_image_proj.c +++ b/source/blender/editors/sculpt_paint/paint_image_proj.c @@ -245,7 +245,8 @@ typedef struct ProjPaintState { float normal_angle_inner; float normal_angle_range; /* difference between normal_angle and normal_angle_inner, for easy access */ - short is_ortho; + bool do_face_sel; /* quick access to (me->editflag & ME_EDIT_PAINT_FACE_SEL) */ + bool is_ortho; bool do_masking; /* use masking during painting. Some operations such as airbrush may disable */ bool is_texbrush; /* only to avoid running */ bool is_maskbrush; /* mask brush is applied before masking */ @@ -2809,11 +2810,13 @@ static void project_paint_begin(ProjPaintState *ps) Image *tpage_last = NULL, *tpage; /* Face vars */ + MPoly *mpoly_orig; MFace *mf; MTFace *tf; int a, i; /* generic looping vars */ int image_index = -1, face_index; + int *mpoly_origindex; MVert *mv; MemArena *arena; /* at the moment this is just ps->arena_mt[0], but use this to show were not multithreading */ @@ -2827,6 +2830,8 @@ static void project_paint_begin(ProjPaintState *ps) if (ps->source == PROJ_SRC_VIEW) ED_view3d_clipping_local(ps->rv3d, ps->ob->obmat); /* faster clipping lookups */ + ps->do_face_sel = ((((Mesh *)ps->ob->data)->editflag & ME_EDIT_PAINT_FACE_SEL) != 0); + /* paint onto the derived mesh */ /* Workaround for subsurf selection, try the display mesh first */ @@ -2835,12 +2840,17 @@ static void project_paint_begin(ProjPaintState *ps) ps->dm = mesh_create_derived_render(ps->scene, ps->ob, ps->scene->customdata_mask | CD_MASK_MTFACE); ps->dm_release = TRUE; } - else if (ps->ob->derivedFinal && CustomData_has_layer(&ps->ob->derivedFinal->faceData, CD_MTFACE)) { + else if (ps->ob->derivedFinal && + CustomData_has_layer(&ps->ob->derivedFinal->faceData, CD_MTFACE) && + (ps->do_face_sel == false || CustomData_has_layer(&ps->ob->derivedFinal->polyData, CD_ORIGINDEX))) + { ps->dm = ps->ob->derivedFinal; ps->dm_release = FALSE; } else { - ps->dm = mesh_get_derived_final(ps->scene, ps->ob, ps->scene->customdata_mask | CD_MASK_MTFACE); + ps->dm = mesh_get_derived_final( + ps->scene, ps->ob, + ps->scene->customdata_mask | CD_MASK_MTFACE | (ps->do_face_sel ? CD_ORIGINDEX : 0)); ps->dm_release = TRUE; } @@ -2860,6 +2870,15 @@ static void project_paint_begin(ProjPaintState *ps) ps->dm_totvert = ps->dm->getNumVerts(ps->dm); ps->dm_totface = ps->dm->getNumTessFaces(ps->dm); + if (ps->do_face_sel) { + mpoly_orig = ((Mesh *)ps->ob->data)->mpoly; + mpoly_origindex = ps->dm->getPolyDataArray(ps->dm, CD_ORIGINDEX); + } + else { + mpoly_orig = NULL; + mpoly_origindex = NULL; + } + /* use clone mtface? */ @@ -3129,8 +3148,8 @@ static void project_paint_begin(ProjPaintState *ps) } } - for (face_index = 0, tf = ps->dm_mtface, mf = ps->dm_mface; face_index < ps->dm_totface; mf++, tf++, face_index++) { + bool is_face_sel; #ifndef PROJ_DEBUG_NOSEAMBLEED /* add face user if we have bleed enabled, set the UV seam flags later */ @@ -3145,10 +3164,21 @@ static void project_paint_begin(ProjPaintState *ps) } #endif - tpage = project_paint_face_image(ps, ps->dm_mtface, face_index); - - if (tpage && ((((Mesh *)ps->ob->data)->editflag & ME_EDIT_PAINT_FACE_SEL) == 0 || mf->flag & ME_FACE_SEL)) { + if (ps->do_face_sel) { + int orig_index; + if (mpoly_origindex && ((orig_index = mpoly_origindex[face_index])) != ORIGINDEX_NONE) { + MPoly *mp = mpoly_orig + orig_index; + is_face_sel = ((mp->flag & ME_FACE_SEL) != 0); + } + else { + is_face_sel = ((mf->flag & ME_FACE_SEL) != 0); + } + } + else { + is_face_sel = true; + } + if (is_face_sel && (tpage = project_paint_face_image(ps, ps->dm_mtface, face_index))) { float *v1coSS, *v2coSS, *v3coSS, *v4coSS = NULL; v1coSS = ps->screenCoords[mf->v1]; From 86546ca42d5cf2373d756b49a5a91fccfeaaefac Mon Sep 17 00:00:00 2001 From: Sergey Sharybin Date: Tue, 9 Jul 2013 08:23:01 +0000 Subject: [PATCH 21/22] Fixed more threading issues with metaballs This time issue was caused by static variables used in BKE_scene_base_iter_next function. Change is not so much ultimate actually, but didn't find more clear solution for now. So the changes are: - Wrap almost all the static variables into own context- like structure, which is owned by the callee function and getting passed to the iteration function. - Recursion detection wasn't possible with such approach, so recursion detection still uses static in_next_object variable, but which is now stored in thread local storage (TLS, or thread variable if this names are more clear for you). This makes code thread-safe, but for sure final solution shall be completely different. Ideally, dependency graph shall be possible to answer on question "which object is a motherball for this metaball". This will avoid iterating via all the bases, objects and duplis just to get needed motherball. Further, metaball evaluation ideally will use the same kind of depsgraph filtering, which will get result for question like "which objects belongs to this group of metaballs". But this ideal things are to be solved in Joshua's and mind GSoC projects. Tested on linux (gcc and clang) and windows (msvc2008), hopefully no compilation error will happen. Thanks to Brecht for reviewing the change and getting feedback for other possible ways we've dicussed! --- source/blender/blenkernel/BKE_scene.h | 12 ++++- source/blender/blenkernel/intern/mball.c | 24 +++++----- source/blender/blenkernel/intern/scene.c | 57 ++++++++++++------------ source/blender/blenlib/BLI_threads.h | 7 +++ 4 files changed, 60 insertions(+), 40 deletions(-) diff --git a/source/blender/blenkernel/BKE_scene.h b/source/blender/blenkernel/BKE_scene.h index 34f34bb9951..61f665be586 100644 --- a/source/blender/blenkernel/BKE_scene.h +++ b/source/blender/blenkernel/BKE_scene.h @@ -75,7 +75,17 @@ struct Base *BKE_scene_base_add(struct Scene *sce, struct Object *ob); void BKE_scene_base_unlink(struct Scene *sce, struct Base *base); void BKE_scene_base_deselect_all(struct Scene *sce); void BKE_scene_base_select(struct Scene *sce, struct Base *selbase); -int BKE_scene_base_iter_next(struct Scene **scene, int val, struct Base **base, struct Object **ob); + +/* Scene base iteration function. + * Define struct here, so no need to bother with alloc/free it. + */ +typedef struct SceneBaseIter { + struct ListBase *duplilist; + struct DupliObject *dupob; + int fase; +} SceneBaseIter; + +int BKE_scene_base_iter_next(struct SceneBaseIter *iter, struct Scene **scene, int val, struct Base **base, struct Object **ob); void BKE_scene_base_flag_to_objects(struct Scene *scene); void BKE_scene_base_flag_from_objects(struct Scene *scene); diff --git a/source/blender/blenkernel/intern/mball.c b/source/blender/blenkernel/intern/mball.c index 31212c3a6b7..55b1a65a73e 100644 --- a/source/blender/blenkernel/intern/mball.c +++ b/source/blender/blenkernel/intern/mball.c @@ -485,14 +485,15 @@ void BKE_mball_properties_copy(Scene *scene, Object *active_object) MetaBall *active_mball = (MetaBall *)active_object->data; int basisnr, obnr; char basisname[MAX_ID_NAME], obname[MAX_ID_NAME]; - + SceneBaseIter iter; + BLI_split_name_num(basisname, &basisnr, active_object->id.name + 2, '.'); /* XXX recursion check, see scene.c, just too simple code this BKE_scene_base_iter_next() */ - if (F_ERROR == BKE_scene_base_iter_next(&sce_iter, 0, NULL, NULL)) + if (F_ERROR == BKE_scene_base_iter_next(&iter, &sce_iter, 0, NULL, NULL)) return; - while (BKE_scene_base_iter_next(&sce_iter, 1, &base, &ob)) { + while (BKE_scene_base_iter_next(&iter, &sce_iter, 1, &base, &ob)) { if (ob->type == OB_MBALL) { if (ob != active_object) { BLI_split_name_num(obname, &obnr, ob->id.name + 2, '.'); @@ -529,14 +530,15 @@ Object *BKE_mball_basis_find(Scene *scene, Object *basis) Object *ob, *bob = basis; int basisnr, obnr; char basisname[MAX_ID_NAME], obname[MAX_ID_NAME]; + SceneBaseIter iter; BLI_split_name_num(basisname, &basisnr, basis->id.name + 2, '.'); /* XXX recursion check, see scene.c, just too simple code this BKE_scene_base_iter_next() */ - if (F_ERROR == BKE_scene_base_iter_next(&sce_iter, 0, NULL, NULL)) + if (F_ERROR == BKE_scene_base_iter_next(&iter, &sce_iter, 0, NULL, NULL)) return NULL; - while (BKE_scene_base_iter_next(&sce_iter, 1, &base, &ob)) { + while (BKE_scene_base_iter_next(&iter, &sce_iter, 1, &base, &ob)) { if (ob->type == OB_MBALL) { if (ob != bob) { BLI_split_name_num(obname, &obnr, ob->id.name + 2, '.'); @@ -1655,7 +1657,8 @@ static float init_meta(PROCESS *process, Scene *scene, Object *ob) /* return //float max = 0.0f; int a, obnr, zero_size = 0; char obname[MAX_ID_NAME]; - + SceneBaseIter iter; + copy_m4_m4(obmat, ob->obmat); /* to cope with duplicators from BKE_scene_base_iter_next */ invert_m4_m4(obinv, ob->obmat); a = 0; @@ -1663,8 +1666,8 @@ static float init_meta(PROCESS *process, Scene *scene, Object *ob) /* return BLI_split_name_num(obname, &obnr, ob->id.name + 2, '.'); /* make main array */ - BKE_scene_base_iter_next(&sce_iter, 0, NULL, NULL); - while (BKE_scene_base_iter_next(&sce_iter, 1, &base, &bob)) { + BKE_scene_base_iter_next(&iter, &sce_iter, 0, NULL, NULL); + while (BKE_scene_base_iter_next(&iter, &sce_iter, 1, &base, &bob)) { if (bob->type == OB_MBALL) { zero_size = 0; @@ -2225,15 +2228,16 @@ static void mball_count(PROCESS *process, Scene *scene, Object *basis) MetaElem *ml = NULL; int basisnr, obnr; char basisname[MAX_ID_NAME], obname[MAX_ID_NAME]; + SceneBaseIter iter; BLI_split_name_num(basisname, &basisnr, basis->id.name + 2, '.'); process->totelem = 0; /* XXX recursion check, see scene.c, just too simple code this BKE_scene_base_iter_next() */ - if (F_ERROR == BKE_scene_base_iter_next(&sce_iter, 0, NULL, NULL)) + if (F_ERROR == BKE_scene_base_iter_next(&iter, &sce_iter, 0, NULL, NULL)) return; - while (BKE_scene_base_iter_next(&sce_iter, 1, &base, &ob)) { + while (BKE_scene_base_iter_next(&iter, &sce_iter, 1, &base, &ob)) { if (ob->type == OB_MBALL) { if (ob == bob) { MetaBall *mb = ob->data; diff --git a/source/blender/blenkernel/intern/scene.c b/source/blender/blenkernel/intern/scene.c index 26563afa65b..5b09f65d7db 100644 --- a/source/blender/blenkernel/intern/scene.c +++ b/source/blender/blenkernel/intern/scene.c @@ -746,17 +746,16 @@ void BKE_scene_unlink(Main *bmain, Scene *sce, Scene *newsce) /* used by metaballs * doesn't return the original duplicated object, only dupli's */ -int BKE_scene_base_iter_next(Scene **scene, int val, Base **base, Object **ob) +int BKE_scene_base_iter_next(SceneBaseIter *iter, Scene **scene, int val, Base **base, Object **ob) { - static ListBase *duplilist = NULL; - static DupliObject *dupob; - static int fase = F_START, in_next_object = 0; + static ThreadVariable int in_next_object = 0; int run_again = 1; /* init */ if (val == 0) { - fase = F_START; - dupob = NULL; + iter->fase = F_START; + iter->dupob = NULL; + iter->duplilist = NULL; /* XXX particle systems with metas+dupligroups call this recursively */ /* see bug #18725 */ @@ -774,11 +773,11 @@ int BKE_scene_base_iter_next(Scene **scene, int val, Base **base, Object **ob) run_again = 0; /* the first base */ - if (fase == F_START) { + if (iter->fase == F_START) { *base = (*scene)->base.first; if (*base) { *ob = (*base)->object; - fase = F_SCENE; + iter->fase = F_SCENE; } else { /* exception: empty scene */ @@ -787,20 +786,20 @@ int BKE_scene_base_iter_next(Scene **scene, int val, Base **base, Object **ob) if ((*scene)->base.first) { *base = (*scene)->base.first; *ob = (*base)->object; - fase = F_SCENE; + iter->fase = F_SCENE; break; } } } } else { - if (*base && fase != F_DUPLI) { + if (*base && iter->fase != F_DUPLI) { *base = (*base)->next; if (*base) { *ob = (*base)->object; } else { - if (fase == F_SCENE) { + if (iter->fase == F_SCENE) { /* (*scene) is finished, now do the set */ while ((*scene)->set) { (*scene) = (*scene)->set; @@ -816,45 +815,45 @@ int BKE_scene_base_iter_next(Scene **scene, int val, Base **base, Object **ob) } if (*base == NULL) { - fase = F_START; + iter->fase = F_START; } else { - if (fase != F_DUPLI) { + if (iter->fase != F_DUPLI) { if ( (*base)->object->transflag & OB_DUPLI) { /* groups cannot be duplicated for mballs yet, * this enters eternal loop because of * makeDispListMBall getting called inside of group_duplilist */ if ((*base)->object->dup_group == NULL) { - duplilist = object_duplilist((*scene), (*base)->object, FALSE); + iter->duplilist = object_duplilist((*scene), (*base)->object, FALSE); - dupob = duplilist->first; + iter->dupob = iter->duplilist->first; - if (!dupob) - free_object_duplilist(duplilist); + if (!iter->dupob) + free_object_duplilist(iter->duplilist); } } } /* handle dupli's */ - if (dupob) { + if (iter->dupob) { - copy_m4_m4(dupob->ob->obmat, dupob->mat); + copy_m4_m4(iter->dupob->ob->obmat, iter->dupob->mat); (*base)->flag |= OB_FROMDUPLI; - *ob = dupob->ob; - fase = F_DUPLI; + *ob = iter->dupob->ob; + iter->fase = F_DUPLI; - dupob = dupob->next; + iter->dupob = iter->dupob->next; } - else if (fase == F_DUPLI) { - fase = F_SCENE; + else if (iter->fase == F_DUPLI) { + iter->fase = F_SCENE; (*base)->flag &= ~OB_FROMDUPLI; - for (dupob = duplilist->first; dupob; dupob = dupob->next) { - copy_m4_m4(dupob->ob->obmat, dupob->omat); + for (iter->dupob = iter->duplilist->first; iter->dupob; iter->dupob = iter->dupob->next) { + copy_m4_m4(iter->dupob->ob->obmat, iter->dupob->omat); } - free_object_duplilist(duplilist); - duplilist = NULL; + free_object_duplilist(iter->duplilist); + iter->duplilist = NULL; run_again = 1; } } @@ -870,7 +869,7 @@ int BKE_scene_base_iter_next(Scene **scene, int val, Base **base, Object **ob) /* reset recursion test */ in_next_object = 0; - return fase; + return iter->fase; } Object *BKE_scene_camera_find(Scene *sc) diff --git a/source/blender/blenlib/BLI_threads.h b/source/blender/blenlib/BLI_threads.h index 154986936a2..ec558188270 100644 --- a/source/blender/blenlib/BLI_threads.h +++ b/source/blender/blenlib/BLI_threads.h @@ -182,6 +182,13 @@ int BLI_thread_queue_size(ThreadQueue *queue); void BLI_thread_queue_wait_finish(ThreadQueue *queue); void BLI_thread_queue_nowait(ThreadQueue *queue); +/* Thread Local Storage */ +#ifdef _MSC_VER +# define ThreadVariable __declspec(thread) +#else +# define ThreadVariable __thread +#endif + #ifdef __cplusplus } #endif From 80738a1828a355ffaa4d3052f38d4a1e841e8b60 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Tue, 9 Jul 2013 08:25:30 +0000 Subject: [PATCH 22/22] add checks in paintface_flush_flags for faces which have no original index values. (could give out of bounds writes) --- source/blender/editors/mesh/editface.c | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/source/blender/editors/mesh/editface.c b/source/blender/editors/mesh/editface.c index b2c7846ab6c..6fd198d9ae6 100644 --- a/source/blender/editors/mesh/editface.c +++ b/source/blender/editors/mesh/editface.c @@ -95,9 +95,11 @@ void paintface_flush_flags(Object *ob) /* loop over tessfaces */ for (i = 0; i < totface; i++) { - /* Copy flags onto the original tessface from its original poly */ - mp_orig = me->mpoly + index_array[i]; - faces[i].flag = mp_orig->flag; + if (index_array[i] != ORIGINDEX_NONE) { + /* Copy flags onto the original tessface from its original poly */ + mp_orig = me->mpoly + index_array[i]; + faces[i].flag = mp_orig->flag; + } } } @@ -107,9 +109,11 @@ void paintface_flush_flags(Object *ob) /* loop over final derived polys */ for (i = 0; i < totpoly; i++) { - /* Copy flags onto the final derived poly from the original mesh poly */ - mp_orig = me->mpoly + index_array[i]; - polys[i].flag = mp_orig->flag; + if (index_array[i] != ORIGINDEX_NONE) { + /* Copy flags onto the final derived poly from the original mesh poly */ + mp_orig = me->mpoly + index_array[i]; + polys[i].flag = mp_orig->flag; + } } } @@ -120,9 +124,11 @@ void paintface_flush_flags(Object *ob) /* loop over tessfaces */ for (i = 0; i < totface; i++) { - /* Copy flags onto the final tessface from its final poly */ - mp_orig = polys + index_array[i]; - faces[i].flag = mp_orig->flag; + if (index_array[i] != ORIGINDEX_NONE) { + /* Copy flags onto the final tessface from its final poly */ + mp_orig = polys + index_array[i]; + faces[i].flag = mp_orig->flag; + } } } }