Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCampbell Barton <ideasman42@gmail.com>2021-06-29 09:18:26 +0300
committerCampbell Barton <ideasman42@gmail.com>2021-06-30 09:53:55 +0300
commit2d4ec90497443dc28e342c539e65010c7f4a04bb (patch)
tree62d29a8c932d8b94f43fddf818c581432fb287a2 /source/blender/editors/transform/transform_mode_rotate.c
parent501d2443d03cce18985fab3ffad5d23238748f3e (diff)
Transform: support multi-threading for most modes
Multi-threading support for transform modes: bevel-weight, crease, push-pull, rotate, shear, shrink-fatten, skin-resize, to-sphere, trackball & translate. This is done using a parallel loop over transform data. From testing a 1.5million polygon mesh on a 32 core system the overall performance gains were between ~20-28% To ensure the code is thread-safe arguments to shared data are const. Reviewed By: mano-wii
Diffstat (limited to 'source/blender/editors/transform/transform_mode_rotate.c')
-rw-r--r--source/blender/editors/transform/transform_mode_rotate.c217
1 files changed, 163 insertions, 54 deletions
diff --git a/source/blender/editors/transform/transform_mode_rotate.c b/source/blender/editors/transform/transform_mode_rotate.c
index 066cdf20e93..44a29cfac45 100644
--- a/source/blender/editors/transform/transform_mode_rotate.c
+++ b/source/blender/editors/transform/transform_mode_rotate.c
@@ -24,6 +24,7 @@
#include <stdlib.h>
#include "BLI_math.h"
+#include "BLI_task.h"
#include "BKE_context.h"
#include "BKE_unit.h"
@@ -37,6 +38,140 @@
#include "transform_snap.h"
/* -------------------------------------------------------------------- */
+/** \name Transform (Rotation) Matrix Cache
+ * \{ */
+
+struct RotateMatrixCache {
+ /**
+ * Counter for needed updates (when we need to update to non-default matrix,
+ * we also need another update on next iteration to go back to default matrix,
+ * hence the '2' value used here, instead of a mere boolean).
+ */
+ short do_update_matrix;
+ float mat[3][3];
+};
+
+static void rmat_cache_init(struct RotateMatrixCache *rmc, const float angle, const float axis[3])
+{
+ axis_angle_normalized_to_mat3(rmc->mat, axis, angle);
+ rmc->do_update_matrix = 0;
+}
+
+static void rmat_cache_reset(struct RotateMatrixCache *rmc)
+{
+ rmc->do_update_matrix = 2;
+}
+
+static void rmat_cache_update(struct RotateMatrixCache *rmc,
+ const float axis[3],
+ const float angle)
+{
+ if (rmc->do_update_matrix > 0) {
+ axis_angle_normalized_to_mat3(rmc->mat, axis, angle);
+ rmc->do_update_matrix--;
+ }
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Transform (Rotation) Element
+ * \{ */
+
+/**
+ * \note Small arrays / data-structures should be stored copied for faster memory access.
+ */
+struct TransDataArgs_Rotate {
+ const TransInfo *t;
+ const TransDataContainer *tc;
+ const float axis[3];
+ float angle;
+ float angle_step;
+ bool is_large_rotation;
+};
+
+struct TransDataArgs_RotateTLS {
+ struct RotateMatrixCache rmc;
+};
+
+static void transdata_elem_rotate(const TransInfo *t,
+ const TransDataContainer *tc,
+ TransData *td,
+ const float axis[3],
+ const float angle,
+ const float angle_step,
+ const bool is_large_rotation,
+ struct RotateMatrixCache *rmc)
+{
+ float axis_buffer[3];
+ const float *axis_final = axis;
+
+ float angle_final = angle;
+ if (t->con.applyRot) {
+ copy_v3_v3(axis_buffer, axis);
+ axis_final = axis_buffer;
+ t->con.applyRot(t, tc, td, axis_buffer, NULL);
+ angle_final = angle * td->factor;
+ /* Even though final angle might be identical to orig value,
+ * we have to update the rotation matrix in that case... */
+ rmat_cache_reset(rmc);
+ }
+ else if (t->flag & T_PROP_EDIT) {
+ angle_final = angle * td->factor;
+ }
+
+ /* Rotation is very likely to be above 180°, we need to do rotation by steps.
+ * Note that this is only needed when doing 'absolute' rotation
+ * (i.e. from initial rotation again, typically when using numinput).
+ * regular incremental rotation (from mouse/widget/...) will be called often enough,
+ * hence steps are small enough to be properly handled without that complicated trick.
+ * Note that we can only do that kind of stepped rotation if we have initial rotation values
+ * (and access to some actual rotation value storage).
+ * Otherwise, just assume it's useless (e.g. in case of mesh/UV/etc. editing).
+ * Also need to be in Euler rotation mode, the others never allow more than one turn anyway.
+ */
+ if (is_large_rotation && td->ext != NULL && td->ext->rotOrder == ROT_MODE_EUL) {
+ copy_v3_v3(td->ext->rot, td->ext->irot);
+ for (float angle_progress = angle_step; fabsf(angle_progress) < fabsf(angle_final);
+ angle_progress += angle_step) {
+ axis_angle_normalized_to_mat3(rmc->mat, axis_final, angle_progress);
+ ElementRotation(t, tc, td, rmc->mat, t->around);
+ }
+ rmat_cache_reset(rmc);
+ }
+ else if (angle_final != angle) {
+ rmat_cache_reset(rmc);
+ }
+
+ rmat_cache_update(rmc, axis_final, angle_final);
+
+ ElementRotation(t, tc, td, rmc->mat, t->around);
+}
+
+static void transdata_elem_rotate_fn(void *__restrict iter_data_v,
+ const int iter,
+ const TaskParallelTLS *__restrict tls)
+{
+ struct TransDataArgs_Rotate *data = iter_data_v;
+ struct TransDataArgs_RotateTLS *tls_data = tls->userdata_chunk;
+
+ TransData *td = &data->tc->data[iter];
+ if (td->flag & TD_SKIP) {
+ return;
+ }
+ transdata_elem_rotate(data->t,
+ data->tc,
+ td,
+ data->axis,
+ data->angle,
+ data->angle_step,
+ data->is_large_rotation,
+ &tls_data->rmc);
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
/** \name Transform (Rotation)
* \{ */
@@ -115,12 +250,9 @@ static float large_rotation_limit(float angle)
static void applyRotationValue(TransInfo *t,
float angle,
- float axis[3],
+ const float axis[3],
const bool is_large_rotation)
{
- float mat[3][3];
- int i;
-
const float angle_sign = angle < 0.0f ? -1.0f : 1.0f;
/* We cannot use something too close to 180°, or 'continuous' rotation may fail
* due to computing error... */
@@ -132,60 +264,37 @@ static void applyRotationValue(TransInfo *t,
angle = large_rotation_limit(angle);
}
- axis_angle_normalized_to_mat3(mat, axis, angle);
- /* Counter for needed updates (when we need to update to non-default matrix,
- * we also need another update on next iteration to go back to default matrix,
- * hence the '2' value used here, instead of a mere boolean). */
- short do_update_matrix = 0;
+ struct RotateMatrixCache rmc = {0};
+ rmat_cache_init(&rmc, angle, axis);
FOREACH_TRANS_DATA_CONTAINER (t, tc) {
- TransData *td = tc->data;
- for (i = 0; i < tc->data_len; i++, td++) {
- if (td->flag & TD_SKIP) {
- continue;
- }
-
- float angle_final = angle;
- if (t->con.applyRot) {
- t->con.applyRot(t, tc, td, axis, NULL);
- angle_final = angle * td->factor;
- /* Even though final angle might be identical to orig value,
- * we have to update the rotation matrix in that case... */
- do_update_matrix = 2;
- }
- else if (t->flag & T_PROP_EDIT) {
- angle_final = angle * td->factor;
- }
-
- /* Rotation is very likely to be above 180°, we need to do rotation by steps.
- * Note that this is only needed when doing 'absolute' rotation
- * (i.e. from initial rotation again, typically when using numinput).
- * regular incremental rotation (from mouse/widget/...) will be called often enough,
- * hence steps are small enough to be properly handled without that complicated trick.
- * Note that we can only do that kind of stepped rotation if we have initial rotation values
- * (and access to some actual rotation value storage).
- * Otherwise, just assume it's useless (e.g. in case of mesh/UV/etc. editing).
- * Also need to be in Euler rotation mode, the others never allow more than one turn anyway.
- */
- if (is_large_rotation && td->ext != NULL && td->ext->rotOrder == ROT_MODE_EUL) {
- copy_v3_v3(td->ext->rot, td->ext->irot);
- for (float angle_progress = angle_step; fabsf(angle_progress) < fabsf(angle_final);
- angle_progress += angle_step) {
- axis_angle_normalized_to_mat3(mat, axis, angle_progress);
- ElementRotation(t, tc, td, mat, t->around);
+ if (tc->data_len < TRANSDATA_THREAD_LIMIT) {
+ TransData *td = tc->data;
+ for (int i = 0; i < tc->data_len; i++, td++) {
+ if (td->flag & TD_SKIP) {
+ continue;
}
- do_update_matrix = 2;
+ transdata_elem_rotate(t, tc, td, axis, angle, angle_step, is_large_rotation, &rmc);
}
- else if (angle_final != angle) {
- do_update_matrix = 2;
- }
-
- if (do_update_matrix > 0) {
- axis_angle_normalized_to_mat3(mat, axis, angle_final);
- do_update_matrix--;
- }
-
- ElementRotation(t, tc, td, mat, t->around);
+ }
+ else {
+ struct TransDataArgs_Rotate data = {
+ .t = t,
+ .tc = tc,
+ .axis = {UNPACK3(axis)},
+ .angle = angle,
+ .angle_step = angle_step,
+ .is_large_rotation = is_large_rotation,
+ };
+ struct TransDataArgs_RotateTLS tls_data = {
+ .rmc = rmc,
+ };
+
+ TaskParallelSettings settings;
+ BLI_parallel_range_settings_defaults(&settings);
+ settings.userdata_chunk = &tls_data;
+ settings.userdata_chunk_size = sizeof(tls_data);
+ BLI_task_parallel_range(0, tc->data_len, &data, transdata_elem_rotate_fn, &settings);
}
}
}