[Bf-blender-cvs] [f6f9582] temp_custom_loop_normals: Use BLI_task for some split normals code - wrong solution

Bastien Montagne noreply at git.blender.org
Fri Aug 8 19:18:58 CEST 2014


Commit: f6f9582d2771dc8eced248f7175ec080f33cf731
Author: Bastien Montagne
Date:   Fri Aug 8 14:20:11 2014 +0200
Branches: temp_custom_loop_normals
https://developer.blender.org/rBf6f9582d2771dc8eced248f7175ec080f33cf731

Use BLI_task for some split normals code - wrong solution

Looks like tasks are not suited for many little elements
(this code is at least two times slower as mono-thread previous one!).
And probably things like calling `BLI_stack_new()` in each task instead of
once globally does not help either. :/

Will refactor into a few worker tasks feeded with a queue...

===================================================================

M	source/blender/blenkernel/intern/mesh_evaluate.c

===================================================================

diff --git a/source/blender/blenkernel/intern/mesh_evaluate.c b/source/blender/blenkernel/intern/mesh_evaluate.c
index 396ed2b..c8acead 100644
--- a/source/blender/blenkernel/intern/mesh_evaluate.c
+++ b/source/blender/blenkernel/intern/mesh_evaluate.c
@@ -47,6 +47,7 @@
 #include "BLI_linklist_stack.h"
 #include "BLI_alloca.h"
 #include "BLI_stack.h"
+#include "BLI_task.h"
 
 #include "BKE_customdata.h"
 #include "BKE_mesh.h"
@@ -485,6 +486,336 @@ void BKE_lnor_space_custom_normal_to_data(MLoopNorSpace *lnor_space, const float
 	}
 }
 
+typedef struct LoopSplitTaskDataCommon {
+	/* Common to all instances, read/write.
+	 * Note we do not need to protect it, though, since two different tasks will *always* affect different
+	 * elements in the arrays. */
+	MLoopsNorSpaces *lnors_spaces;
+	float (*loopnors)[3];
+	short (*clnors_data)[2];
+
+	/* Common to all instances, and read-only. */
+	const MVert *mverts;
+	const MEdge *medges;
+	const MLoop *mloops;
+	const MPoly *mpolys;
+	const int (*edge_to_loops)[2];
+	const int *loop_to_poly;
+	const float (*polynors)[3];
+} LoopSplitTaskDataCommon;
+
+typedef struct LoopSplitTaskData {
+	/* Specific to each instance (each task). */
+	MLoopNorSpace *lnor_space;  /* We have to create those outside of tasks, since afaik memarena is not threadsafe. */
+	float (*lnor)[3];
+	const MLoop *ml_curr;
+	const MLoop *ml_prev;
+	int ml_curr_index;
+	int ml_prev_index;
+	const int *e2l_prev;
+	int mp_index;
+
+	LoopSplitTaskDataCommon *common;
+} LoopSplitTaskData;
+
+static void exec_split_loop_nor_single(TaskPool *UNUSED(pool), void *taskdata, int UNUSED(threadid))
+{
+	LoopSplitTaskData *data = (LoopSplitTaskData *)taskdata;
+
+	MLoopsNorSpaces *lnors_spaces = data->common->lnors_spaces;
+	short (*clnors_data)[2] = data->common->clnors_data;
+
+	const MVert *mverts = data->common->mverts;
+	const MEdge *medges = data->common->medges;
+	const float (*polynors)[3] = data->common->polynors;
+
+	MLoopNorSpace *lnor_space = data->lnor_space;
+	float (*lnor)[3] = data->lnor;
+	const MLoop *ml_curr = data->ml_curr;
+	const MLoop *ml_prev = data->ml_prev;
+	const int ml_curr_index = data->ml_curr_index;
+#if 0  /* Not needed for 'single' loop. */
+	const int ml_prev_index = data->ml_prev_index;
+	const int *e2l_prev = data->e2l_prev;
+#endif
+	const int mp_index = data->mp_index;
+
+	/* Simple case (both edges around that vertex are sharp in current polygon),
+	 * this loop just takes its poly normal.
+	 */
+	copy_v3_v3(*lnor, polynors[mp_index]);
+
+	/* printf("BASIC: handling loop %d / edge %d / vert %d\n", ml_curr_index, ml_curr->e, ml_curr->v); */
+
+	/* If needed, generate this (simple!) lnor space. */
+	if (lnors_spaces) {
+		float vec_curr[3], vec_prev[3];
+
+		const unsigned int mv_pivot_index = ml_curr->v;  /* The vertex we are "fanning" around! */
+		const MVert *mv_pivot = &mverts[mv_pivot_index];
+		const MEdge *me_curr = &medges[ml_curr->e];
+		const MVert *mv_2 = (me_curr->v1 == mv_pivot_index) ? &mverts[me_curr->v2] : &mverts[me_curr->v1];
+		const MEdge *me_prev = &medges[ml_prev->e];
+		const MVert *mv_3 = (me_prev->v1 == mv_pivot_index) ? &mverts[me_prev->v2] : &mverts[me_prev->v1];
+
+		sub_v3_v3v3(vec_curr, mv_2->co, mv_pivot->co);
+		normalize_v3(vec_curr);
+		sub_v3_v3v3(vec_prev, mv_3->co, mv_pivot->co);
+		normalize_v3(vec_prev);
+
+		BKE_lnor_space_define(lnor_space, *lnor, vec_curr, vec_prev, NULL);
+		/* We know there is only one loop in this space, no need to create a linklist in this case... */
+		BKE_lnor_space_add_loop(lnors_spaces, lnor_space, ml_curr_index, false);
+
+		if (clnors_data) {
+			BKE_lnor_space_custom_data_to_normal(lnor_space, *lnor, clnors_data[ml_curr_index]);
+		}
+	}
+}
+
+#define INDEX_UNSET INT_MIN
+#define INDEX_INVALID -1
+/* See comment about edge_to_loops below. */
+#define IS_EDGE_SHARP(_e2l) (ELEM((_e2l)[1], INDEX_UNSET, INDEX_INVALID))
+
+static void exec_split_loop_nor_fan(TaskPool *UNUSED(pool), void *taskdata, int UNUSED(threadid))
+{
+	LoopSplitTaskData *data = (LoopSplitTaskData *)taskdata;
+
+	MLoopsNorSpaces *lnors_spaces = data->common->lnors_spaces;
+	float (*loopnors)[3] = data->common->loopnors;
+	short (*clnors_data)[2] = data->common->clnors_data;
+
+	const MVert *mverts = data->common->mverts;
+	const MEdge *medges = data->common->medges;
+	const MLoop *mloops = data->common->mloops;
+	const MPoly *mpolys = data->common->mpolys;
+	const int (*edge_to_loops)[2] = data->common->edge_to_loops;
+	const int *loop_to_poly = data->common->loop_to_poly;
+	const float (*polynors)[3] = data->common->polynors;
+
+	MLoopNorSpace *lnor_space = data->lnor_space;
+#if 0  /* Not needed for 'fan' loops. */
+	float (*lnor)[3] = data->lnor;
+#endif
+	const MLoop *ml_curr = data->ml_curr;
+	const MLoop *ml_prev = data->ml_prev;
+	const int ml_curr_index = data->ml_curr_index;
+	const int ml_prev_index = data->ml_prev_index;
+	const int mp_index = data->mp_index;
+	const int *e2l_prev = data->e2l_prev;
+
+	/* Gah... We have to fan around current vertex, until we find the other non-smooth edge,
+	 * and accumulate face normals into the vertex!
+	 * Note in case this vertex has only one sharp edges, this is a waste because the normal is the same as
+	 * the vertex normal, but I do not see any easy way to detect that (would need to count number
+	 * of sharp edges per vertex, I doubt the additional memory usage would be worth it, especially as
+	 * it should not be a common case in real-life meshes anyway).
+	 */
+	const unsigned int mv_pivot_index = ml_curr->v;  /* The vertex we are "fanning" around! */
+	const MVert *mv_pivot = &mverts[mv_pivot_index];
+	const MEdge *me_org = &medges[ml_curr->e];  /* ml_curr would be mlfan_prev if we needed that one */
+	const int *e2lfan_curr;
+	float vec_curr[3], vec_prev[3], vec_org[3];
+	const MLoop *mlfan_curr, *mlfan_next;
+	const MPoly *mpfan_next;
+	float lnor[3] = {0.0f, 0.0f, 0.0f};
+	/* mlfan_vert_index: the loop of our current edge might not be the loop of our current vertex! */
+	int mlfan_curr_index, mlfan_vert_index, mpfan_curr_index;
+
+	/* We validate clnors data on the fly - cheapest way to do! */
+	int clnors_avg[2] = {0, 0};
+	short (*clnor_ref)[2] = NULL;
+	int clnors_nbr = 0;
+	bool clnors_invalid = false;
+
+	/* Temp loop normal stack. */
+	BLI_SMALLSTACK_DECLARE(normal, float *);
+	/* Temp clnors stack. */
+	BLI_SMALLSTACK_DECLARE(clnors, short *);
+	/* Temp edge vectors stack, only used when computing lnor spaces. */
+	BLI_Stack *edge_vectors = lnors_spaces ? BLI_stack_new(sizeof(float[3]), __func__) : NULL;
+
+	e2lfan_curr = e2l_prev;
+	mlfan_curr = ml_prev;
+	mlfan_curr_index = ml_prev_index;
+	mlfan_vert_index = ml_curr_index;
+	mpfan_curr_index = mp_index;
+
+	BLI_assert(mlfan_curr_index >= 0);
+	BLI_assert(mlfan_vert_index >= 0);
+	BLI_assert(mpfan_curr_index >= 0);
+
+	BLI_assert((edge_vectors == NULL) || BLI_stack_is_empty(edge_vectors));
+
+	/* Only need to compute previous edge's vector once, then we can just reuse old current one! */
+	{
+		const MVert *mv_2 = (me_org->v1 == mv_pivot_index) ? &mverts[me_org->v2] : &mverts[me_org->v1];
+
+		sub_v3_v3v3(vec_org, mv_2->co, mv_pivot->co);
+		normalize_v3(vec_org);
+		copy_v3_v3(vec_prev, vec_org);
+
+		if (lnors_spaces) {
+			BLI_stack_push(edge_vectors, vec_org);
+		}
+	}
+
+	/* printf("FAN: vert %d, start edge %d\n", mv_pivot_index, ml_curr->e); */
+
+	while (true) {
+		const MEdge *me_curr = &medges[mlfan_curr->e];
+		/* Compute edge vectors.
+		 * NOTE: We could pre-compute those into an array, in the first iteration, instead of computing them
+		 *       twice (or more) here. However, time gained is not worth memory and time lost,
+		 *       given the fact that this code should not be called that much in real-life meshes...
+		 */
+		{
+			const MVert *mv_2 = (me_curr->v1 == mv_pivot_index) ? &mverts[me_curr->v2] : &mverts[me_curr->v1];
+
+			sub_v3_v3v3(vec_curr, mv_2->co, mv_pivot->co);
+			normalize_v3(vec_curr);
+		}
+
+		/* printf("\thandling edge %d / loop %d\n", mlfan_curr->e, mlfan_curr_index); */
+
+		{
+			/* Code similar to accumulate_vertex_normals_poly. */
+			/* Calculate angle between the two poly edges incident on this vertex. */
+			const float fac = saacos(dot_v3v3(vec_curr, vec_prev));
+			/* Accumulate */
+			madd_v3_v3fl(lnor, polynors[mpfan_curr_index], fac);
+
+			if (clnors_data) {
+				/* Accumulate all clnors, if they are not all equal we have to fix that! */
+				short (*clnor)[2] = &clnors_data[mlfan_vert_index];
+				if (clnors_nbr) {
+					clnors_invalid |= ((*clnor_ref)[0] != (*clnor)[0] || (*clnor_ref)[1] != (*clnor)[1]);
+				}
+				else {
+					clnor_ref = clnor;
+				}
+				clnors_avg[0] += (*clnor)[0];
+				clnors_avg[1] += (*clnor)[1];
+				clnors_nbr++;
+				/* We store here a pointer to all custom lnors processed. */
+				BLI_SMALLSTACK_PUSH(clnors, (short *)*clnor);
+			}
+		}
+
+		/* We store here a pointer to all loop-normals processed. */
+		BLI_SMALLSTACK_PUSH(normal, (float *)(loopnors[mlfan_vert_index]));
+
+		if (lnors_spaces) {
+			/* Assign current lnor space to current 'vertex' loop. */
+			BKE_lnor_space_add_loop(lnors_spaces, lnor_space, mlfan_vert_index, true);
+			if (me_curr != me_org) {
+				/* We store here all edges-normalized vectors processed. */
+				BLI_stack_push(edge_vectors, vec_curr);
+			}
+		}
+
+		if (IS_EDGE_SHARP(e2lfan_curr) || (me_curr == me_org)) {
+			/* Current edge is sharp and we have finished with this fan of faces around this vert,
+			 * or this vert is smooth, and we have completed a full turn around it.
+			 */
+			/* printf("FAN: Finished!\n"); */
+			break;
+		}
+
+		copy_v3_v3(vec_prev, vec_curr);
+
+		/* Warning! This is rather complex!
+		 * We have to find our next edge around the vertex (fan mode).
+		 * First we find the next loop, which is either previous or next to mlfan_curr_index, depending
+		 * whether both loops using current edge are in the same direction or not, and whether
+		 * mlfan_curr_index actually uses the vertex we are fanning around!
+		 * mlfan_curr_index is the index of mlfan_next here, and mlfan_next is not the real next one
+		 * (i.e. not the future mlfan_curr)...
+		 */
+		mlfan_curr_index = (e2lfan_curr[0] == mlfan_curr_index) ? e2lfan_

@@ Diff output truncated at 10240 characters. @@




More information about the Bf-blender-cvs mailing list