Cleanup: Use C++ threading API in sculpt/paint code

Remove the global `SculptThreadedTaskData` struct which contained
the arguments to ALL multi-threaded sculpt functions. Use the C++
threading API instead of the old task API, moving the arguments
previously stored in the shared struct to actual function arguments.

Pull Request: https://projects.blender.org/blender/blender/pulls/111525
This commit is contained in:
Hans Goudey
2023-08-25 18:18:35 +02:00
committed by Hans Goudey
parent 4151691552
commit cf9fcbf24e
21 changed files with 1519 additions and 2160 deletions

View File

@@ -103,23 +103,19 @@ struct MaskTaskData {
float view_normal[3]; float view_normal[3];
}; };
static void mask_flood_fill_task_cb(void *__restrict userdata, static void mask_flood_fill_task(MaskTaskData &data, const int i)
const int i,
const TaskParallelTLS *__restrict /*tls*/)
{ {
MaskTaskData *data = static_cast<MaskTaskData *>(userdata); PBVHNode *node = data.nodes[i];
PBVHNode *node = data->nodes[i]; const PaintMaskFloodMode mode = data.mode;
const float value = data.value;
const PaintMaskFloodMode mode = data->mode;
const float value = data->value;
bool redraw = false; bool redraw = false;
PBVHVertexIter vi; PBVHVertexIter vi;
SCULPT_undo_push_node(data->ob, node, SCULPT_UNDO_MASK); SCULPT_undo_push_node(data.ob, node, SCULPT_UNDO_MASK);
BKE_pbvh_vertex_iter_begin (data->pbvh, node, vi, PBVH_ITER_UNIQUE) { BKE_pbvh_vertex_iter_begin (data.pbvh, node, vi, PBVH_ITER_UNIQUE) {
float prevmask = *vi.mask; float prevmask = *vi.mask;
mask_flood_fill_set_elem(vi.mask, mode, value); mask_flood_fill_set_elem(vi.mask, mode, value);
if (prevmask != *vi.mask) { if (prevmask != *vi.mask) {
@@ -130,7 +126,7 @@ static void mask_flood_fill_task_cb(void *__restrict userdata,
if (redraw) { if (redraw) {
BKE_pbvh_node_mark_update_mask(node); BKE_pbvh_node_mark_update_mask(node);
if (data->multires) { if (data.multires) {
BKE_pbvh_node_mark_normals_update(node); BKE_pbvh_node_mark_normals_update(node);
} }
} }
@@ -138,6 +134,7 @@ static void mask_flood_fill_task_cb(void *__restrict userdata,
static int mask_flood_fill_exec(bContext *C, wmOperator *op) static int mask_flood_fill_exec(bContext *C, wmOperator *op)
{ {
using namespace blender;
const Scene *scene = CTX_data_scene(C); const Scene *scene = CTX_data_scene(C);
Object *ob = CTX_data_active_object(C); Object *ob = CTX_data_active_object(C);
Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
@@ -166,9 +163,11 @@ static int mask_flood_fill_exec(bContext *C, wmOperator *op)
data.mode = mode; data.mode = mode;
data.value = value; data.value = value;
TaskParallelSettings settings; threading::parallel_for(nodes.index_range(), 1, [&](const IndexRange range) {
BKE_pbvh_parallel_range_settings(&settings, true, nodes.size()); for (const int i : range) {
BLI_task_parallel_range(0, nodes.size(), &data, mask_flood_fill_task_cb, &settings); mask_flood_fill_task(data, i);
}
});
if (multires) { if (multires) {
multires_mark_as_modified(depsgraph, ob, MULTIRES_COORDS_MODIFIED); multires_mark_as_modified(depsgraph, ob, MULTIRES_COORDS_MODIFIED);

View File

@@ -293,7 +293,7 @@ Vector<PBVHNode *> pbvh_gather_generic(Object *ob, VPaint *wp, Sculpt *sd, Brush
ss->pbvh, [&](PBVHNode &node) { return SCULPT_search_sphere(&node, &data); }); ss->pbvh, [&](PBVHNode &node) { return SCULPT_search_sphere(&node, &data); });
if (use_normal) { if (use_normal) {
SCULPT_pbvh_calc_area_normal(brush, ob, nodes, true, ss->cache->sculpt_normal_symm); SCULPT_pbvh_calc_area_normal(brush, ob, nodes, ss->cache->sculpt_normal_symm);
} }
else { else {
zero_v3(ss->cache->sculpt_normal_symm); zero_v3(ss->cache->sculpt_normal_symm);

View File

@@ -1043,34 +1043,20 @@ static float wpaint_get_active_weight(const MDeformVert *dv, const WeightPaintIn
return weight; return weight;
} }
static void do_wpaint_precompute_weight_cb_ex(void *__restrict userdata,
const int n,
const TaskParallelTLS *__restrict /*tls*/)
{
SculptThreadedTaskData *data = (SculptThreadedTaskData *)userdata;
const MDeformVert *dv = &data->wpi->dvert[n];
data->wpd->precomputed_weight[n] = wpaint_get_active_weight(dv, data->wpi);
}
static void precompute_weight_values( static void precompute_weight_values(
bContext *C, Object *ob, Brush *brush, WPaintData *wpd, WeightPaintInfo *wpi, Mesh *me) Object *ob, Brush *brush, WPaintData *wpd, WeightPaintInfo *wpi, Mesh *me)
{ {
using namespace blender;
if (wpd->precomputed_weight_ready && !vwpaint::brush_use_accumulate_ex(brush, ob->mode)) { if (wpd->precomputed_weight_ready && !vwpaint::brush_use_accumulate_ex(brush, ob->mode)) {
return; return;
} }
/* threaded loop over vertices */ threading::parallel_for(IndexRange(me->totvert), 512, [&](const IndexRange range) {
SculptThreadedTaskData data; for (const int i : range) {
data.C = C; const MDeformVert *dv = &wpi->dvert[i];
data.ob = ob; wpd->precomputed_weight[i] = wpaint_get_active_weight(dv, wpi);
data.wpd = wpd; }
data.wpi = wpi; });
data.me = me;
TaskParallelSettings settings;
BLI_parallel_range_settings_defaults(&settings);
BLI_task_parallel_range(0, me->totvert, &data, do_wpaint_precompute_weight_cb_ex, &settings);
wpd->precomputed_weight_ready = true; wpd->precomputed_weight_ready = true;
} }
@@ -1079,40 +1065,42 @@ static void precompute_weight_values(
/** \name Weight paint brushes. /** \name Weight paint brushes.
* \{ */ * \{ */
static void do_wpaint_brush_blur_task_cb_ex(void *__restrict userdata, static void do_wpaint_brush_blur_task(const Scene *scene,
const int n, Object *ob,
const TaskParallelTLS *__restrict /*tls*/) const Brush *brush,
VPaint *vp,
WPaintData *wpd,
const WeightPaintInfo *wpi,
Mesh *me,
PBVHNode *node)
{ {
SculptThreadedTaskData *data = (SculptThreadedTaskData *)userdata; SculptSession *ss = ob->sculpt;
SculptSession *ss = data->ob->sculpt;
const PBVHType pbvh_type = BKE_pbvh_type(ss->pbvh); const PBVHType pbvh_type = BKE_pbvh_type(ss->pbvh);
const bool has_grids = (pbvh_type == PBVH_GRIDS); const bool has_grids = (pbvh_type == PBVH_GRIDS);
const SculptVertexPaintGeomMap *gmap = &ss->mode.wpaint.gmap; const SculptVertexPaintGeomMap *gmap = &ss->mode.wpaint.gmap;
const Brush *brush = data->brush;
const StrokeCache *cache = ss->cache; const StrokeCache *cache = ss->cache;
Scene *scene = CTX_data_scene(data->C);
float brush_size_pressure, brush_alpha_value, brush_alpha_pressure; float brush_size_pressure, brush_alpha_value, brush_alpha_pressure;
vwpaint::get_brush_alpha_data( vwpaint::get_brush_alpha_data(
scene, ss, brush, &brush_size_pressure, &brush_alpha_value, &brush_alpha_pressure); scene, ss, brush, &brush_size_pressure, &brush_alpha_value, &brush_alpha_pressure);
const bool use_normal = vwpaint::use_normal(data->vp); const bool use_normal = vwpaint::use_normal(vp);
const bool use_face_sel = (data->me->editflag & ME_EDIT_PAINT_FACE_SEL) != 0; const bool use_face_sel = (me->editflag & ME_EDIT_PAINT_FACE_SEL) != 0;
const bool use_vert_sel = (data->me->editflag & ME_EDIT_PAINT_VERT_SEL) != 0; const bool use_vert_sel = (me->editflag & ME_EDIT_PAINT_VERT_SEL) != 0;
SculptBrushTest test; SculptBrushTest test;
SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape( SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape(
ss, &test, data->brush->falloff_shape); ss, &test, brush->falloff_shape);
const float *sculpt_normal_frontface = SCULPT_brush_frontface_normal_from_falloff_shape( const float *sculpt_normal_frontface = SCULPT_brush_frontface_normal_from_falloff_shape(
ss, data->brush->falloff_shape); ss, brush->falloff_shape);
const blender::bke::AttributeAccessor attributes = data->me->attributes(); const blender::bke::AttributeAccessor attributes = me->attributes();
const blender::VArray<bool> select_vert = *attributes.lookup_or_default<bool>( const blender::VArray<bool> select_vert = *attributes.lookup_or_default<bool>(
".select_vert", ATTR_DOMAIN_POINT, false); ".select_vert", ATTR_DOMAIN_POINT, false);
/* For each vertex */ /* For each vertex */
PBVHVertexIter vd; PBVHVertexIter vd;
BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) {
/* Test to see if the vertex coordinates are within the spherical brush region. */ /* Test to see if the vertex coordinates are within the spherical brush region. */
if (!sculpt_brush_test_sq_fn(&test, vd.co)) { if (!sculpt_brush_test_sq_fn(&test, vd.co)) {
continue; continue;
@@ -1134,7 +1122,7 @@ static void do_wpaint_brush_blur_task_cb_ex(void *__restrict userdata,
total_hit_loops += face.size(); total_hit_loops += face.size();
for (const int vert : ss->corner_verts.slice(face)) { for (const int vert : ss->corner_verts.slice(face)) {
weight_final += data->wpd->precomputed_weight[vert]; weight_final += wpd->precomputed_weight[vert];
} }
} }
@@ -1147,7 +1135,7 @@ static void do_wpaint_brush_blur_task_cb_ex(void *__restrict userdata,
const float angle_cos = (use_normal && vd.no) ? dot_v3v3(sculpt_normal_frontface, vd.no) : const float angle_cos = (use_normal && vd.no) ? dot_v3v3(sculpt_normal_frontface, vd.no) :
1.0f; 1.0f;
if (!vwpaint::test_brush_angle_falloff( if (!vwpaint::test_brush_angle_falloff(
*brush, data->wpd->normal_angle_precalc, angle_cos, &brush_strength)) *brush, wpd->normal_angle_precalc, angle_cos, &brush_strength))
{ {
continue; continue;
} }
@@ -1166,23 +1154,25 @@ static void do_wpaint_brush_blur_task_cb_ex(void *__restrict userdata,
weight_final /= total_hit_loops; weight_final /= total_hit_loops;
/* Only paint visible verts */ /* Only paint visible verts */
do_weight_paint_vertex(data->vp, data->ob, data->wpi, v_index, final_alpha, weight_final); do_weight_paint_vertex(vp, ob, wpi, v_index, final_alpha, weight_final);
} }
BKE_pbvh_vertex_iter_end; BKE_pbvh_vertex_iter_end;
} }
static void do_wpaint_brush_smear_task_cb_ex(void *__restrict userdata, static void do_wpaint_brush_smear_task(const Scene *scene,
const int n, Object *ob,
const TaskParallelTLS *__restrict /*tls*/) const Brush *brush,
VPaint *vp,
WPaintData *wpd,
const WeightPaintInfo *wpi,
Mesh *me,
PBVHNode *node)
{ {
SculptThreadedTaskData *data = (SculptThreadedTaskData *)userdata; SculptSession *ss = ob->sculpt;
SculptSession *ss = data->ob->sculpt;
const PBVHType pbvh_type = BKE_pbvh_type(ss->pbvh); const PBVHType pbvh_type = BKE_pbvh_type(ss->pbvh);
const bool has_grids = (pbvh_type == PBVH_GRIDS); const bool has_grids = (pbvh_type == PBVH_GRIDS);
const SculptVertexPaintGeomMap *gmap = &ss->mode.wpaint.gmap; const SculptVertexPaintGeomMap *gmap = &ss->mode.wpaint.gmap;
const Brush *brush = data->brush;
const Scene *scene = CTX_data_scene(data->C);
const StrokeCache *cache = ss->cache; const StrokeCache *cache = ss->cache;
if (!cache->is_last_valid) { if (!cache->is_last_valid) {
return; return;
@@ -1191,9 +1181,9 @@ static void do_wpaint_brush_smear_task_cb_ex(void *__restrict userdata,
float brush_size_pressure, brush_alpha_value, brush_alpha_pressure; float brush_size_pressure, brush_alpha_value, brush_alpha_pressure;
vwpaint::get_brush_alpha_data( vwpaint::get_brush_alpha_data(
scene, ss, brush, &brush_size_pressure, &brush_alpha_value, &brush_alpha_pressure); scene, ss, brush, &brush_size_pressure, &brush_alpha_value, &brush_alpha_pressure);
const bool use_normal = vwpaint::use_normal(data->vp); const bool use_normal = vwpaint::use_normal(vp);
const bool use_face_sel = (data->me->editflag & ME_EDIT_PAINT_FACE_SEL) != 0; const bool use_face_sel = (me->editflag & ME_EDIT_PAINT_FACE_SEL) != 0;
const bool use_vert_sel = (data->me->editflag & ME_EDIT_PAINT_VERT_SEL) != 0; const bool use_vert_sel = (me->editflag & ME_EDIT_PAINT_VERT_SEL) != 0;
float brush_dir[3]; float brush_dir[3];
sub_v3_v3v3(brush_dir, cache->location, cache->last_location); sub_v3_v3v3(brush_dir, cache->location, cache->last_location);
@@ -1202,19 +1192,19 @@ static void do_wpaint_brush_smear_task_cb_ex(void *__restrict userdata,
return; return;
} }
const blender::bke::AttributeAccessor attributes = data->me->attributes(); const blender::bke::AttributeAccessor attributes = me->attributes();
const blender::VArray<bool> select_vert = *attributes.lookup_or_default<bool>( const blender::VArray<bool> select_vert = *attributes.lookup_or_default<bool>(
".select_vert", ATTR_DOMAIN_POINT, false); ".select_vert", ATTR_DOMAIN_POINT, false);
SculptBrushTest test; SculptBrushTest test;
SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape( SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape(
ss, &test, data->brush->falloff_shape); ss, &test, brush->falloff_shape);
const float *sculpt_normal_frontface = SCULPT_brush_frontface_normal_from_falloff_shape( const float *sculpt_normal_frontface = SCULPT_brush_frontface_normal_from_falloff_shape(
ss, data->brush->falloff_shape); ss, brush->falloff_shape);
/* For each vertex */ /* For each vertex */
PBVHVertexIter vd; PBVHVertexIter vd;
BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) {
/* Test to see if the vertex coordinates are within the spherical brush region. */ /* Test to see if the vertex coordinates are within the spherical brush region. */
if (!sculpt_brush_test_sq_fn(&test, vd.co)) { if (!sculpt_brush_test_sq_fn(&test, vd.co)) {
continue; continue;
@@ -1236,7 +1226,7 @@ static void do_wpaint_brush_smear_task_cb_ex(void *__restrict userdata,
const float angle_cos = (use_normal && vd.no) ? dot_v3v3(sculpt_normal_frontface, vd.no) : const float angle_cos = (use_normal && vd.no) ? dot_v3v3(sculpt_normal_frontface, vd.no) :
1.0f; 1.0f;
if (!vwpaint::test_brush_angle_falloff( if (!vwpaint::test_brush_angle_falloff(
*brush, data->wpd->normal_angle_precalc, angle_cos, &brush_strength)) *brush, wpd->normal_angle_precalc, angle_cos, &brush_strength))
{ {
continue; continue;
} }
@@ -1266,7 +1256,7 @@ static void do_wpaint_brush_smear_task_cb_ex(void *__restrict userdata,
if (stroke_dot > stroke_dot_max) { if (stroke_dot > stroke_dot_max) {
stroke_dot_max = stroke_dot; stroke_dot_max = stroke_dot;
weight_final = data->wpd->precomputed_weight[v_other_index]; weight_final = wpd->precomputed_weight[v_other_index];
do_color = true; do_color = true;
} }
} }
@@ -1281,48 +1271,50 @@ static void do_wpaint_brush_smear_task_cb_ex(void *__restrict userdata,
continue; continue;
} }
do_weight_paint_vertex( do_weight_paint_vertex(vp, ob, wpi, v_index, final_alpha, float(weight_final));
data->vp, data->ob, data->wpi, v_index, final_alpha, float(weight_final));
} }
} }
BKE_pbvh_vertex_iter_end; BKE_pbvh_vertex_iter_end;
} }
static void do_wpaint_brush_draw_task_cb_ex(void *__restrict userdata, static void do_wpaint_brush_draw_task(const Scene *scene,
const int n, Object *ob,
const TaskParallelTLS *__restrict /*tls*/) const Brush *brush,
VPaint *vp,
WPaintData *wpd,
const WeightPaintInfo *wpi,
Mesh *me,
const float strength,
PBVHNode *node)
{ {
SculptThreadedTaskData *data = (SculptThreadedTaskData *)userdata; SculptSession *ss = ob->sculpt;
SculptSession *ss = data->ob->sculpt;
const PBVHType pbvh_type = BKE_pbvh_type(ss->pbvh); const PBVHType pbvh_type = BKE_pbvh_type(ss->pbvh);
const bool has_grids = (pbvh_type == PBVH_GRIDS); const bool has_grids = (pbvh_type == PBVH_GRIDS);
const Scene *scene = CTX_data_scene(data->C);
const Brush *brush = data->brush;
const StrokeCache *cache = ss->cache; const StrokeCache *cache = ss->cache;
/* NOTE: normally `BKE_brush_weight_get(scene, brush)` is used, /* NOTE: normally `BKE_brush_weight_get(scene, brush)` is used,
* however in this case we calculate a new weight each time. */ * however in this case we calculate a new weight each time. */
const float paintweight = data->strength; const float paintweight = strength;
float brush_size_pressure, brush_alpha_value, brush_alpha_pressure; float brush_size_pressure, brush_alpha_value, brush_alpha_pressure;
vwpaint::get_brush_alpha_data( vwpaint::get_brush_alpha_data(
scene, ss, brush, &brush_size_pressure, &brush_alpha_value, &brush_alpha_pressure); scene, ss, brush, &brush_size_pressure, &brush_alpha_value, &brush_alpha_pressure);
const bool use_normal = vwpaint::use_normal(data->vp); const bool use_normal = vwpaint::use_normal(vp);
const bool use_face_sel = (data->me->editflag & ME_EDIT_PAINT_FACE_SEL) != 0; const bool use_face_sel = (me->editflag & ME_EDIT_PAINT_FACE_SEL) != 0;
const bool use_vert_sel = (data->me->editflag & ME_EDIT_PAINT_VERT_SEL) != 0; const bool use_vert_sel = (me->editflag & ME_EDIT_PAINT_VERT_SEL) != 0;
SculptBrushTest test; SculptBrushTest test;
SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape( SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape(
ss, &test, data->brush->falloff_shape); ss, &test, brush->falloff_shape);
const float *sculpt_normal_frontface = SCULPT_brush_frontface_normal_from_falloff_shape( const float *sculpt_normal_frontface = SCULPT_brush_frontface_normal_from_falloff_shape(
ss, data->brush->falloff_shape); ss, brush->falloff_shape);
const blender::bke::AttributeAccessor attributes = data->me->attributes(); const blender::bke::AttributeAccessor attributes = me->attributes();
const blender::VArray<bool> select_vert = *attributes.lookup_or_default<bool>( const blender::VArray<bool> select_vert = *attributes.lookup_or_default<bool>(
".select_vert", ATTR_DOMAIN_POINT, false); ".select_vert", ATTR_DOMAIN_POINT, false);
/* For each vertex */ /* For each vertex */
PBVHVertexIter vd; PBVHVertexIter vd;
BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) {
/* Test to see if the vertex coordinates are within the spherical brush region. */ /* Test to see if the vertex coordinates are within the spherical brush region. */
if (!sculpt_brush_test_sq_fn(&test, vd.co)) { if (!sculpt_brush_test_sq_fn(&test, vd.co)) {
continue; continue;
@@ -1342,7 +1334,7 @@ static void do_wpaint_brush_draw_task_cb_ex(void *__restrict userdata,
const float angle_cos = (use_normal && vd.no) ? dot_v3v3(sculpt_normal_frontface, vd.no) : const float angle_cos = (use_normal && vd.no) ? dot_v3v3(sculpt_normal_frontface, vd.no) :
1.0f; 1.0f;
if (!vwpaint::test_brush_angle_falloff( if (!vwpaint::test_brush_angle_falloff(
*brush, data->wpd->normal_angle_precalc, angle_cos, &brush_strength)) *brush, wpd->normal_angle_precalc, angle_cos, &brush_strength))
{ {
continue; continue;
} }
@@ -1358,42 +1350,44 @@ static void do_wpaint_brush_draw_task_cb_ex(void *__restrict userdata,
} }
} }
do_weight_paint_vertex(data->vp, data->ob, data->wpi, v_index, final_alpha, paintweight); do_weight_paint_vertex(vp, ob, wpi, v_index, final_alpha, paintweight);
} }
BKE_pbvh_vertex_iter_end; BKE_pbvh_vertex_iter_end;
} }
static void do_wpaint_brush_calc_average_weight_cb_ex(void *__restrict userdata, static WPaintAverageAccum do_wpaint_brush_calc_average_weight(Object *ob,
const int n, const Mesh *me,
const TaskParallelTLS *__restrict /*tls*/) const Brush *brush,
const VPaint *vp,
WeightPaintInfo *wpi,
PBVHNode *node)
{ {
SculptThreadedTaskData *data = (SculptThreadedTaskData *)userdata; SculptSession *ss = ob->sculpt;
SculptSession *ss = data->ob->sculpt;
StrokeCache *cache = ss->cache; StrokeCache *cache = ss->cache;
const PBVHType pbvh_type = BKE_pbvh_type(ss->pbvh); const PBVHType pbvh_type = BKE_pbvh_type(ss->pbvh);
const bool has_grids = (pbvh_type == PBVH_GRIDS); const bool has_grids = (pbvh_type == PBVH_GRIDS);
const bool use_normal = vwpaint::use_normal(data->vp); const bool use_normal = vwpaint::use_normal(vp);
const bool use_face_sel = (data->me->editflag & ME_EDIT_PAINT_FACE_SEL) != 0; const bool use_face_sel = (me->editflag & ME_EDIT_PAINT_FACE_SEL) != 0;
const bool use_vert_sel = (data->me->editflag & ME_EDIT_PAINT_VERT_SEL) != 0; const bool use_vert_sel = (me->editflag & ME_EDIT_PAINT_VERT_SEL) != 0;
WPaintAverageAccum *accum = (WPaintAverageAccum *)data->custom_data + n; WPaintAverageAccum accum{};
accum->len = 0; accum.len = 0;
accum->value = 0.0; accum.value = 0.0;
SculptBrushTest test; SculptBrushTest test;
SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape( SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape(
ss, &test, data->brush->falloff_shape); ss, &test, brush->falloff_shape);
const float *sculpt_normal_frontface = SCULPT_brush_frontface_normal_from_falloff_shape( const float *sculpt_normal_frontface = SCULPT_brush_frontface_normal_from_falloff_shape(
ss, data->brush->falloff_shape); ss, brush->falloff_shape);
const blender::bke::AttributeAccessor attributes = data->me->attributes(); const blender::bke::AttributeAccessor attributes = me->attributes();
const blender::VArray<bool> select_vert = *attributes.lookup_or_default<bool>( const blender::VArray<bool> select_vert = *attributes.lookup_or_default<bool>(
".select_vert", ATTR_DOMAIN_POINT, false); ".select_vert", ATTR_DOMAIN_POINT, false);
/* For each vertex */ /* For each vertex */
PBVHVertexIter vd; PBVHVertexIter vd;
BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) {
/* Test to see if the vertex coordinates are within the spherical brush region. */ /* Test to see if the vertex coordinates are within the spherical brush region. */
if (!sculpt_brush_test_sq_fn(&test, vd.co)) { if (!sculpt_brush_test_sq_fn(&test, vd.co)) {
continue; continue;
@@ -1402,8 +1396,7 @@ static void do_wpaint_brush_calc_average_weight_cb_ex(void *__restrict userdata,
const float angle_cos = (use_normal && vd.no) ? dot_v3v3(sculpt_normal_frontface, vd.no) : const float angle_cos = (use_normal && vd.no) ? dot_v3v3(sculpt_normal_frontface, vd.no) :
1.0f; 1.0f;
if (angle_cos <= 0.0f || if (angle_cos <= 0.0f ||
BKE_brush_curve_strength(data->brush, sqrtf(test.dist), cache->radius) <= 0.0f) BKE_brush_curve_strength(brush, sqrtf(test.dist), cache->radius) <= 0.0f) {
{
continue; continue;
} }
@@ -1414,23 +1407,32 @@ static void do_wpaint_brush_calc_average_weight_cb_ex(void *__restrict userdata,
continue; continue;
} }
const MDeformVert *dv = &data->wpi->dvert[v_index]; const MDeformVert *dv = &wpi->dvert[v_index];
accum->len += 1; accum.len += 1;
accum->value += wpaint_get_active_weight(dv, data->wpi); accum.value += wpaint_get_active_weight(dv, wpi);
} }
BKE_pbvh_vertex_iter_end; BKE_pbvh_vertex_iter_end;
return accum;
} }
static void calculate_average_weight(SculptThreadedTaskData *data, Span<PBVHNode *> nodes) static float calculate_average_weight(Object *ob,
const Mesh *me,
const Brush *brush,
VPaint *vp,
WeightPaintInfo *wpi,
Span<PBVHNode *> nodes)
{ {
WPaintAverageAccum *accum = (WPaintAverageAccum *)MEM_mallocN(sizeof(*accum) * nodes.size(), WPaintAverageAccum *accum = (WPaintAverageAccum *)MEM_mallocN(sizeof(*accum) * nodes.size(),
__func__); __func__);
data->custom_data = accum;
TaskParallelSettings settings; threading::parallel_for(nodes.index_range(), 1, [&](const IndexRange range) {
BKE_pbvh_parallel_range_settings(&settings, true, nodes.size()); for (const int i : range) {
BLI_task_parallel_range( accum[i] = do_wpaint_brush_calc_average_weight(ob, me, brush, vp, wpi, nodes[i]);
0, nodes.size(), data, do_wpaint_brush_calc_average_weight_cb_ex, &settings); }
});
float strength = 0.0f;
uint accum_len = 0; uint accum_len = 0;
double accum_weight = 0.0; double accum_weight = 0.0;
@@ -1440,15 +1442,16 @@ static void calculate_average_weight(SculptThreadedTaskData *data, Span<PBVHNode
} }
if (accum_len != 0) { if (accum_len != 0) {
accum_weight /= accum_len; accum_weight /= accum_len;
data->strength = float(accum_weight); strength = float(accum_weight);
} }
MEM_SAFE_FREE(data->custom_data); /* 'accum' */ MEM_SAFE_FREE(accum);
return strength;
} }
static void wpaint_paint_leaves(bContext *C, static void wpaint_paint_leaves(bContext *C,
Object *ob, Object *ob,
Sculpt *sd,
VPaint *vp, VPaint *vp,
WPaintData *wpd, WPaintData *wpd,
WeightPaintInfo *wpi, WeightPaintInfo *wpi,
@@ -1458,40 +1461,70 @@ static void wpaint_paint_leaves(bContext *C,
Scene *scene = CTX_data_scene(C); Scene *scene = CTX_data_scene(C);
const Brush *brush = ob->sculpt->cache->brush; const Brush *brush = ob->sculpt->cache->brush;
/* threaded loop over nodes */
SculptThreadedTaskData data = {nullptr};
data.C = C;
data.sd = sd;
data.ob = ob;
data.brush = brush;
data.nodes = nodes;
data.vp = vp;
data.wpd = wpd;
data.wpi = wpi;
data.me = me;
/* Use this so average can modify its weight without touching the brush. */ /* Use this so average can modify its weight without touching the brush. */
data.strength = BKE_brush_weight_get(scene, brush); float strength = BKE_brush_weight_get(scene, brush);
if (brush->weightpaint_tool == WPAINT_TOOL_AVERAGE) {
strength = calculate_average_weight(ob, me, brush, vp, wpi, nodes);
}
/* NOTE: current mirroring code cannot be run in parallel */ if (ME_USING_MIRROR_X_VERTEX_GROUPS(me)) {
TaskParallelSettings settings; /* NOTE: current mirroring code cannot be run in parallel */
const bool use_threading = !ME_USING_MIRROR_X_VERTEX_GROUPS(me); switch ((eBrushWeightPaintTool)brush->weightpaint_tool) {
BKE_pbvh_parallel_range_settings(&settings, use_threading, nodes.size()); case WPAINT_TOOL_AVERAGE: {
for (const int i : nodes.index_range()) {
switch ((eBrushWeightPaintTool)brush->weightpaint_tool) { do_wpaint_brush_draw_task(scene, ob, brush, vp, wpd, wpi, me, strength, nodes[i]);
case WPAINT_TOOL_AVERAGE: }
calculate_average_weight(&data, nodes); break;
BLI_task_parallel_range(0, nodes.size(), &data, do_wpaint_brush_draw_task_cb_ex, &settings); }
break; case WPAINT_TOOL_SMEAR:
case WPAINT_TOOL_SMEAR: for (const int i : nodes.index_range()) {
BLI_task_parallel_range(0, nodes.size(), &data, do_wpaint_brush_smear_task_cb_ex, &settings); do_wpaint_brush_smear_task(scene, ob, brush, vp, wpd, wpi, me, nodes[i]);
break; }
case WPAINT_TOOL_BLUR: break;
BLI_task_parallel_range(0, nodes.size(), &data, do_wpaint_brush_blur_task_cb_ex, &settings); case WPAINT_TOOL_BLUR:
break; for (const int i : nodes.index_range()) {
case WPAINT_TOOL_DRAW: do_wpaint_brush_blur_task(scene, ob, brush, vp, wpd, wpi, me, nodes[i]);
BLI_task_parallel_range(0, nodes.size(), &data, do_wpaint_brush_draw_task_cb_ex, &settings); }
break; break;
case WPAINT_TOOL_DRAW:
for (const int i : nodes.index_range()) {
do_wpaint_brush_draw_task(scene, ob, brush, vp, wpd, wpi, me, strength, nodes[i]);
}
break;
}
}
else {
switch ((eBrushWeightPaintTool)brush->weightpaint_tool) {
case WPAINT_TOOL_AVERAGE: {
threading::parallel_for(nodes.index_range(), 1, [&](const IndexRange range) {
for (const int i : range) {
do_wpaint_brush_draw_task(scene, ob, brush, vp, wpd, wpi, me, strength, nodes[i]);
}
});
break;
}
case WPAINT_TOOL_SMEAR:
threading::parallel_for(nodes.index_range(), 1, [&](const IndexRange range) {
for (const int i : range) {
do_wpaint_brush_smear_task(scene, ob, brush, vp, wpd, wpi, me, nodes[i]);
}
});
break;
case WPAINT_TOOL_BLUR:
threading::parallel_for(nodes.index_range(), 1, [&](const IndexRange range) {
for (const int i : range) {
do_wpaint_brush_blur_task(scene, ob, brush, vp, wpd, wpi, me, nodes[i]);
}
});
break;
case WPAINT_TOOL_DRAW:
threading::parallel_for(nodes.index_range(), 1, [&](const IndexRange range) {
for (const int i : range) {
do_wpaint_brush_draw_task(scene, ob, brush, vp, wpd, wpi, me, strength, nodes[i]);
}
});
break;
}
} }
} }
/** \} */ /** \} */
@@ -1665,7 +1698,7 @@ static void wpaint_do_paint(bContext *C,
Vector<PBVHNode *> nodes = vwpaint::pbvh_gather_generic(ob, wp, sd, brush); Vector<PBVHNode *> nodes = vwpaint::pbvh_gather_generic(ob, wp, sd, brush);
wpaint_paint_leaves(C, ob, sd, wp, wpd, wpi, me, nodes); wpaint_paint_leaves(C, ob, wp, wpd, wpi, me, nodes);
} }
static void wpaint_do_radial_symmetry(bContext *C, static void wpaint_do_radial_symmetry(bContext *C,
@@ -1808,7 +1841,7 @@ static void wpaint_stroke_update_step(bContext *C,
/* *** done setting up WeightPaintInfo *** */ /* *** done setting up WeightPaintInfo *** */
if (wpd->precomputed_weight) { if (wpd->precomputed_weight) {
precompute_weight_values(C, ob, brush, wpd, &wpi, mesh); precompute_weight_values(ob, brush, wpd, &wpi, mesh);
} }
wpaint_do_symmetrical_brush_actions(C, ob, wp, sd, wpd, &wpi); wpaint_do_symmetrical_brush_actions(C, ob, wp, sd, wpd, &wpi);

View File

@@ -1028,53 +1028,34 @@ bool SCULPT_check_vertex_pivot_symmetry(const float vco[3], const float pco[3],
return is_in_symmetry_area; return is_in_symmetry_area;
} }
struct NearestVertexTLSData { struct NearestVertexData {
PBVHVertRef nearest_vertex; PBVHVertRef nearest_vertex;
float nearest_vertex_distance_squared; float nearest_vertex_distance_sq;
}; };
static void do_nearest_vertex_get_task_cb(void *__restrict userdata, static void nearest_vertex_get_node(PBVH *pbvh,
const int n, const float nearest_vertex_search_co[3],
const TaskParallelTLS *__restrict tls) const float max_distance_sq,
PBVHNode *node,
NearestVertexData *nvtd)
{ {
SculptThreadedTaskData *data = static_cast<SculptThreadedTaskData *>(userdata);
SculptSession *ss = data->ob->sculpt;
NearestVertexTLSData *nvtd = static_cast<NearestVertexTLSData *>(tls->userdata_chunk);
PBVHVertexIter vd; PBVHVertexIter vd;
BKE_pbvh_vertex_iter_begin (pbvh, node, vd, PBVH_ITER_UNIQUE) {
BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { float distance_squared = len_squared_v3v3(vd.co, nearest_vertex_search_co);
float distance_squared = len_squared_v3v3(vd.co, data->nearest_vertex_search_co); if (distance_squared < nvtd->nearest_vertex_distance_sq && distance_squared < max_distance_sq)
if (distance_squared < nvtd->nearest_vertex_distance_squared &&
distance_squared < data->max_distance_squared)
{ {
nvtd->nearest_vertex = vd.vertex; nvtd->nearest_vertex = vd.vertex;
nvtd->nearest_vertex_distance_squared = distance_squared; nvtd->nearest_vertex_distance_sq = distance_squared;
} }
} }
BKE_pbvh_vertex_iter_end; BKE_pbvh_vertex_iter_end;
} }
static void nearest_vertex_get_reduce(const void *__restrict /*userdata*/,
void *__restrict chunk_join,
void *__restrict chunk)
{
NearestVertexTLSData *join = static_cast<NearestVertexTLSData *>(chunk_join);
NearestVertexTLSData *nvtd = static_cast<NearestVertexTLSData *>(chunk);
if (join->nearest_vertex.i == PBVH_REF_NONE) {
join->nearest_vertex = nvtd->nearest_vertex;
join->nearest_vertex_distance_squared = nvtd->nearest_vertex_distance_squared;
}
else if (nvtd->nearest_vertex_distance_squared < join->nearest_vertex_distance_squared) {
join->nearest_vertex = nvtd->nearest_vertex;
join->nearest_vertex_distance_squared = nvtd->nearest_vertex_distance_squared;
}
}
PBVHVertRef SCULPT_nearest_vertex_get( PBVHVertRef SCULPT_nearest_vertex_get(
Sculpt *sd, Object *ob, const float co[3], float max_distance, bool use_original) Sculpt *sd, Object *ob, const float co[3], float max_distance, bool use_original)
{ {
using namespace blender;
SculptSession *ss = ob->sculpt; SculptSession *ss = ob->sculpt;
Vector<PBVHNode *> nodes;
SculptSearchSphereData data{}; SculptSearchSphereData data{};
data.sd = sd; data.sd = sd;
@@ -1082,31 +1063,28 @@ PBVHVertRef SCULPT_nearest_vertex_get(
data.original = use_original; data.original = use_original;
data.center = co; data.center = co;
nodes = blender::bke::pbvh::search_gather( Vector<PBVHNode *> nodes = blender::bke::pbvh::search_gather(
ss->pbvh, [&](PBVHNode &node) { return SCULPT_search_sphere(&node, &data); }); ss->pbvh, [&](PBVHNode &node) { return SCULPT_search_sphere(&node, &data); });
if (nodes.is_empty()) { if (nodes.is_empty()) {
return BKE_pbvh_make_vref(PBVH_REF_NONE); return BKE_pbvh_make_vref(PBVH_REF_NONE);
} }
SculptThreadedTaskData task_data{}; const float max_distance_sq = max_distance * max_distance;
task_data.sd = sd;
task_data.ob = ob;
task_data.nodes = nodes;
task_data.max_distance_squared = max_distance * max_distance;
copy_v3_v3(task_data.nearest_vertex_search_co, co); return threading::parallel_reduce(
NearestVertexTLSData nvtd; nodes.index_range(),
nvtd.nearest_vertex.i = PBVH_REF_NONE; 1,
nvtd.nearest_vertex_distance_squared = FLT_MAX; NearestVertexData{PBVH_REF_NONE, FLT_MAX},
[&](const IndexRange range, NearestVertexData nearest) {
TaskParallelSettings settings; for (const int i : range) {
BKE_pbvh_parallel_range_settings(&settings, true, nodes.size()); nearest_vertex_get_node(ss->pbvh, co, max_distance_sq, nodes[i], &nearest);
settings.func_reduce = nearest_vertex_get_reduce; }
settings.userdata_chunk = &nvtd; return nearest;
settings.userdata_chunk_size = sizeof(NearestVertexTLSData); },
BLI_task_parallel_range(0, nodes.size(), &task_data, do_nearest_vertex_get_task_cb, &settings); [](const NearestVertexData a, const NearestVertexData b) {
return a.nearest_vertex_distance_sq < b.nearest_vertex_distance_sq ? a : b;
return nvtd.nearest_vertex; })
.nearest_vertex;
} }
bool SCULPT_is_symmetry_iteration_valid(char i, char symm) bool SCULPT_is_symmetry_iteration_valid(char i, char symm)
@@ -1482,15 +1460,91 @@ bool SCULPT_stroke_is_dynamic_topology(const SculptSession *ss, const Brush *bru
/** \name Sculpt Paint Mesh /** \name Sculpt Paint Mesh
* \{ */ * \{ */
static void paint_mesh_restore_co_task_cb(void *__restrict userdata, static void paint_mesh_restore_node(Object *ob, const SculptUndoType type, PBVHNode *node)
const int n,
const TaskParallelTLS *__restrict /*tls*/)
{ {
SculptThreadedTaskData *data = static_cast<SculptThreadedTaskData *>(userdata); SculptSession *ss = ob->sculpt;
SculptSession *ss = data->ob->sculpt;
SculptUndoNode *unode;
if (ss->bm) {
unode = SCULPT_undo_push_node(ob, node, type);
}
else {
unode = SCULPT_undo_get_node(node, type);
}
if (!unode) {
return;
}
switch (type) {
case SCULPT_UNDO_MASK: {
SculptOrigVertData orig_vert_data;
SCULPT_orig_vert_data_unode_init(&orig_vert_data, ob, unode);
PBVHVertexIter vd;
BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) {
SCULPT_orig_vert_data_update(&orig_vert_data, &vd);
*vd.mask = orig_vert_data.mask;
}
BKE_pbvh_vertex_iter_end;
BKE_pbvh_node_mark_update_mask(node);
break;
}
case SCULPT_UNDO_COLOR: {
SculptOrigVertData orig_vert_data;
SCULPT_orig_vert_data_unode_init(&orig_vert_data, ob, unode);
PBVHVertexIter vd;
BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) {
SCULPT_orig_vert_data_update(&orig_vert_data, &vd);
SCULPT_vertex_color_set(ss, vd.vertex, orig_vert_data.col);
}
BKE_pbvh_vertex_iter_end;
BKE_pbvh_node_mark_update_color(node);
break;
}
case SCULPT_UNDO_FACE_SETS: {
SculptOrigFaceData orig_face_data;
SCULPT_orig_face_data_unode_init(&orig_face_data, ob, unode);
PBVHFaceIter fd;
BKE_pbvh_face_iter_begin (ss->pbvh, node, fd) {
SCULPT_orig_face_data_update(&orig_face_data, &fd);
if (fd.face_set) {
*fd.face_set = orig_face_data.face_set;
}
}
BKE_pbvh_face_iter_end(fd);
BKE_pbvh_node_mark_update_face_sets(node);
break;
}
case SCULPT_UNDO_COORDS: {
SculptOrigVertData orig_vert_data;
SCULPT_orig_vert_data_unode_init(&orig_vert_data, ob, unode);
PBVHVertexIter vd;
BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) {
SCULPT_orig_vert_data_update(&orig_vert_data, &vd);
copy_v3_v3(vd.co, orig_vert_data.co);
if (vd.is_mesh) {
BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex);
}
}
BKE_pbvh_vertex_iter_end;
BKE_pbvh_node_mark_update(node);
break;
}
default:
break;
}
}
static void paint_mesh_restore_co(Sculpt *sd, Object *ob)
{
using namespace blender;
SculptSession *ss = ob->sculpt;
Brush *brush = BKE_paint_brush(&sd->paint);
Vector<PBVHNode *> nodes = blender::bke::pbvh::search_gather(ss->pbvh, {});
SculptUndoType type; SculptUndoType type;
switch (data->brush->sculpt_tool) { switch (brush->sculpt_tool) {
case SCULPT_TOOL_MASK: case SCULPT_TOOL_MASK:
type = SCULPT_UNDO_MASK; type = SCULPT_UNDO_MASK;
break; break;
@@ -1506,99 +1560,22 @@ static void paint_mesh_restore_co_task_cb(void *__restrict userdata,
break; break;
} }
SculptUndoNode *unode;
if (ss->bm) { if (ss->bm) {
unode = SCULPT_undo_push_node(data->ob, data->nodes[n], type); /* Disable multi-threading when dynamic-topology is enabled. Otherwise,
* new entries might be inserted by #SCULPT_undo_push_node() into the #GHash
* used internally by #BM_log_original_vert_co() by a different thread. See #33787. */
for (const int i : nodes.index_range()) {
paint_mesh_restore_node(ob, type, nodes[i]);
}
} }
else { else {
unode = SCULPT_undo_get_node(data->nodes[n], type); threading::parallel_for(nodes.index_range(), 1, [&](const IndexRange range) {
for (const int i : range) {
paint_mesh_restore_node(ob, type, nodes[i]);
}
});
} }
if (!unode) {
return;
}
switch (type) {
case SCULPT_UNDO_MASK: {
SculptOrigVertData orig_vert_data;
SCULPT_orig_vert_data_unode_init(&orig_vert_data, data->ob, unode);
PBVHVertexIter vd;
BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) {
SCULPT_orig_vert_data_update(&orig_vert_data, &vd);
*vd.mask = orig_vert_data.mask;
}
BKE_pbvh_vertex_iter_end;
BKE_pbvh_node_mark_update_mask(data->nodes[n]);
break;
}
case SCULPT_UNDO_COLOR: {
SculptOrigVertData orig_vert_data;
SCULPT_orig_vert_data_unode_init(&orig_vert_data, data->ob, unode);
PBVHVertexIter vd;
BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) {
SCULPT_orig_vert_data_update(&orig_vert_data, &vd);
SCULPT_vertex_color_set(ss, vd.vertex, orig_vert_data.col);
}
BKE_pbvh_vertex_iter_end;
BKE_pbvh_node_mark_update_color(data->nodes[n]);
break;
}
case SCULPT_UNDO_FACE_SETS: {
SculptOrigFaceData orig_face_data;
SCULPT_orig_face_data_unode_init(&orig_face_data, data->ob, unode);
PBVHFaceIter fd;
BKE_pbvh_face_iter_begin (ss->pbvh, data->nodes[n], fd) {
SCULPT_orig_face_data_update(&orig_face_data, &fd);
if (fd.face_set) {
*fd.face_set = orig_face_data.face_set;
}
}
BKE_pbvh_face_iter_end(fd);
BKE_pbvh_node_mark_update_face_sets(data->nodes[n]);
break;
}
case SCULPT_UNDO_COORDS: {
SculptOrigVertData orig_vert_data;
SCULPT_orig_vert_data_unode_init(&orig_vert_data, data->ob, unode);
PBVHVertexIter vd;
BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) {
SCULPT_orig_vert_data_update(&orig_vert_data, &vd);
copy_v3_v3(vd.co, orig_vert_data.co);
if (vd.is_mesh) {
BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex);
}
}
BKE_pbvh_vertex_iter_end;
BKE_pbvh_node_mark_update(data->nodes[n]);
break;
}
default:
break;
}
}
static void paint_mesh_restore_co(Sculpt *sd, Object *ob)
{
SculptSession *ss = ob->sculpt;
Brush *brush = BKE_paint_brush(&sd->paint);
Vector<PBVHNode *> nodes = blender::bke::pbvh::search_gather(ss->pbvh, {});
/**
* Disable multi-threading when dynamic-topology is enabled. Otherwise,
* new entries might be inserted by #SCULPT_undo_push_node() into the #GHash
* used internally by #BM_log_original_vert_co() by a different thread. See #33787.
*/
SculptThreadedTaskData data{};
data.sd = sd;
data.ob = ob;
data.brush = brush;
data.nodes = nodes;
TaskParallelSettings settings;
BKE_pbvh_parallel_range_settings(&settings, true && !ss->bm, nodes.size());
BLI_task_parallel_range(0, nodes.size(), &data, paint_mesh_restore_co_task_cb, &settings);
BKE_pbvh_node_color_buffer_free(ss->pbvh); BKE_pbvh_node_color_buffer_free(ss->pbvh);
} }
@@ -1990,7 +1967,7 @@ static float calc_symmetry_feather(Sculpt *sd, StrokeCache *cache)
* \note These are all _very_ similar, when changing one, check others. * \note These are all _very_ similar, when changing one, check others.
* \{ */ * \{ */
struct AreaNormalCenterTLSData { struct AreaNormalCenterData {
/* 0 = towards view, 1 = flipped */ /* 0 = towards view, 1 = flipped */
float area_cos[2][3]; float area_cos[2][3];
float area_nos[2][3]; float area_nos[2][3];
@@ -1998,15 +1975,16 @@ struct AreaNormalCenterTLSData {
int count_co[2]; int count_co[2];
}; };
static void calc_area_normal_and_center_task_cb(void *__restrict userdata, static void calc_area_normal_and_center_task(Object *ob,
const int n, const Brush *brush,
const TaskParallelTLS *__restrict tls) const bool use_area_nos,
const bool use_area_cos,
const bool has_bm_orco,
PBVHNode *node,
AreaNormalCenterData *anctd,
bool &r_any_vertex_sampled)
{ {
SculptThreadedTaskData *data = static_cast<SculptThreadedTaskData *>(userdata); SculptSession *ss = ob->sculpt;
SculptSession *ss = data->ob->sculpt;
AreaNormalCenterTLSData *anctd = static_cast<AreaNormalCenterTLSData *>(tls->userdata_chunk);
const bool use_area_nos = data->use_area_nos;
const bool use_area_cos = data->use_area_cos;
PBVHVertexIter vd; PBVHVertexIter vd;
SculptUndoNode *unode = nullptr; SculptUndoNode *unode = nullptr;
@@ -2015,40 +1993,40 @@ static void calc_area_normal_and_center_task_cb(void *__restrict userdata,
bool normal_test_r, area_test_r; bool normal_test_r, area_test_r;
if (ss->cache && !ss->cache->accum) { if (ss->cache && !ss->cache->accum) {
unode = SCULPT_undo_push_node(data->ob, data->nodes[n], SCULPT_UNDO_COORDS); unode = SCULPT_undo_push_node(ob, node, SCULPT_UNDO_COORDS);
use_original = (unode->co || unode->bm_entry); use_original = (unode->co || unode->bm_entry);
} }
SculptBrushTest normal_test; SculptBrushTest normal_test;
SculptBrushTestFn sculpt_brush_normal_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape( SculptBrushTestFn sculpt_brush_normal_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape(
ss, &normal_test, data->brush->falloff_shape); ss, &normal_test, brush->falloff_shape);
/* Update the test radius to sample the normal using the normal radius of the brush. */ /* Update the test radius to sample the normal using the normal radius of the brush. */
if (data->brush->ob_mode == OB_MODE_SCULPT) { if (brush->ob_mode == OB_MODE_SCULPT) {
float test_radius = sqrtf(normal_test.radius_squared); float test_radius = sqrtf(normal_test.radius_squared);
test_radius *= data->brush->normal_radius_factor; test_radius *= brush->normal_radius_factor;
normal_test.radius = test_radius; normal_test.radius = test_radius;
normal_test.radius_squared = test_radius * test_radius; normal_test.radius_squared = test_radius * test_radius;
} }
SculptBrushTest area_test; SculptBrushTest area_test;
SculptBrushTestFn sculpt_brush_area_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape( SculptBrushTestFn sculpt_brush_area_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape(
ss, &area_test, data->brush->falloff_shape); ss, &area_test, brush->falloff_shape);
if (data->brush->ob_mode == OB_MODE_SCULPT) { if (brush->ob_mode == OB_MODE_SCULPT) {
float test_radius = sqrtf(area_test.radius_squared); float test_radius = sqrtf(area_test.radius_squared);
/* Layer brush produces artifacts with normal and area radius */ /* Layer brush produces artifacts with normal and area radius */
/* Enable area radius control only on Scrape for now */ /* Enable area radius control only on Scrape for now */
if (ELEM(data->brush->sculpt_tool, SCULPT_TOOL_SCRAPE, SCULPT_TOOL_FILL) && if (ELEM(brush->sculpt_tool, SCULPT_TOOL_SCRAPE, SCULPT_TOOL_FILL) &&
data->brush->area_radius_factor > 0.0f) brush->area_radius_factor > 0.0f)
{ {
test_radius *= data->brush->area_radius_factor; test_radius *= brush->area_radius_factor;
if (ss->cache && data->brush->flag2 & BRUSH_AREA_RADIUS_PRESSURE) { if (ss->cache && brush->flag2 & BRUSH_AREA_RADIUS_PRESSURE) {
test_radius *= ss->cache->pressure; test_radius *= ss->cache->pressure;
} }
} }
else { else {
test_radius *= data->brush->normal_radius_factor; test_radius *= brush->normal_radius_factor;
} }
area_test.radius = test_radius; area_test.radius = test_radius;
area_test.radius_squared = test_radius * test_radius; area_test.radius_squared = test_radius * test_radius;
@@ -2056,13 +2034,12 @@ static void calc_area_normal_and_center_task_cb(void *__restrict userdata,
/* When the mesh is edited we can't rely on original coords /* When the mesh is edited we can't rely on original coords
* (original mesh may not even have verts in brush radius). */ * (original mesh may not even have verts in brush radius). */
if (use_original && data->has_bm_orco) { if (use_original && has_bm_orco) {
float(*orco_coords)[3]; float(*orco_coords)[3];
int(*orco_tris)[3]; int(*orco_tris)[3];
int orco_tris_num; int orco_tris_num;
BKE_pbvh_node_get_bm_orco_data( BKE_pbvh_node_get_bm_orco_data(node, &orco_tris, &orco_tris_num, &orco_coords, nullptr);
data->nodes[n], &orco_tris, &orco_tris_num, &orco_coords, nullptr);
for (int i = 0; i < orco_tris_num; i++) { for (int i = 0; i < orco_tris_num; i++) {
const float *co_tri[3] = { const float *co_tri[3] = {
@@ -2112,7 +2089,7 @@ static void calc_area_normal_and_center_task_cb(void *__restrict userdata,
} }
} }
else { else {
BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) {
float co[3]; float co[3];
/* For bm_vert only. */ /* For bm_vert only. */
@@ -2145,7 +2122,7 @@ static void calc_area_normal_and_center_task_cb(void *__restrict userdata,
float no[3]; float no[3];
int flip_index; int flip_index;
data->any_vertex_sampled = true; r_any_vertex_sampled = true;
if (use_original) { if (use_original) {
copy_v3_v3(no, no_s); copy_v3_v3(no, no_s);
@@ -2189,50 +2166,48 @@ static void calc_area_normal_and_center_task_cb(void *__restrict userdata,
} }
} }
static void calc_area_normal_and_center_reduce(const void *__restrict /*userdata*/, static AreaNormalCenterData calc_area_normal_and_center_reduce(const AreaNormalCenterData &a,
void *__restrict chunk_join, const AreaNormalCenterData &b)
void *__restrict chunk)
{ {
AreaNormalCenterTLSData *join = static_cast<AreaNormalCenterTLSData *>(chunk_join); AreaNormalCenterData joined{};
AreaNormalCenterTLSData *anctd = static_cast<AreaNormalCenterTLSData *>(chunk);
/* For flatten center. */ /* For flatten center. */
add_v3_v3(join->area_cos[0], anctd->area_cos[0]); add_v3_v3v3(joined.area_cos[0], a.area_cos[0], b.area_cos[0]);
add_v3_v3(join->area_cos[1], anctd->area_cos[1]); add_v3_v3v3(joined.area_cos[1], a.area_cos[1], b.area_cos[1]);
/* For area normal. */ /* For area normal. */
add_v3_v3(join->area_nos[0], anctd->area_nos[0]); add_v3_v3v3(joined.area_nos[0], a.area_nos[0], b.area_nos[0]);
add_v3_v3(join->area_nos[1], anctd->area_nos[1]); add_v3_v3v3(joined.area_nos[1], a.area_nos[1], b.area_nos[1]);
/* Weights. */ /* Weights. */
add_v2_v2_int(join->count_no, anctd->count_no); add_v2_v2v2_int(joined.count_no, a.count_no, b.count_no);
add_v2_v2_int(join->count_co, anctd->count_co); add_v2_v2v2_int(joined.count_co, a.count_co, b.count_co);
return joined;
} }
void SCULPT_calc_area_center(Sculpt *sd, Object *ob, Span<PBVHNode *> nodes, float r_area_co[3]) void SCULPT_calc_area_center(Sculpt *sd, Object *ob, Span<PBVHNode *> nodes, float r_area_co[3])
{ {
using namespace blender;
const Brush *brush = BKE_paint_brush(&sd->paint); const Brush *brush = BKE_paint_brush(&sd->paint);
SculptSession *ss = ob->sculpt; SculptSession *ss = ob->sculpt;
const bool has_bm_orco = ss->bm && SCULPT_stroke_is_dynamic_topology(ss, brush); const bool has_bm_orco = ss->bm && SCULPT_stroke_is_dynamic_topology(ss, brush);
int n; int n;
/* Intentionally set 'sd' to nullptr since we share logic with vertex paint. */ bool any_vertex_sampled = false;
SculptThreadedTaskData data{};
data.sd = nullptr;
data.ob = ob;
data.brush = brush;
data.nodes = nodes;
data.has_bm_orco = has_bm_orco;
data.use_area_cos = true;
AreaNormalCenterTLSData anctd = {{{0}}}; const AreaNormalCenterData anctd = threading::parallel_reduce(
nodes.index_range(),
TaskParallelSettings settings; 1,
BKE_pbvh_parallel_range_settings(&settings, true, nodes.size()); AreaNormalCenterData{},
settings.func_reduce = calc_area_normal_and_center_reduce; [&](const IndexRange range, AreaNormalCenterData anctd) {
settings.userdata_chunk = &anctd; for (const int i : range) {
settings.userdata_chunk_size = sizeof(AreaNormalCenterTLSData); calc_area_normal_and_center_task(
BLI_task_parallel_range(0, nodes.size(), &data, calc_area_normal_and_center_task_cb, &settings); ob, brush, false, true, has_bm_orco, nodes[i], &anctd, any_vertex_sampled);
}
return anctd;
},
calc_area_normal_and_center_reduce);
/* For flatten center. */ /* For flatten center. */
for (n = 0; n < ARRAY_SIZE(anctd.area_cos); n++) { for (n = 0; n < ARRAY_SIZE(anctd.area_cos); n++) {
@@ -2258,33 +2233,32 @@ void SCULPT_calc_area_center(Sculpt *sd, Object *ob, Span<PBVHNode *> nodes, flo
void SCULPT_calc_area_normal(Sculpt *sd, Object *ob, Span<PBVHNode *> nodes, float r_area_no[3]) void SCULPT_calc_area_normal(Sculpt *sd, Object *ob, Span<PBVHNode *> nodes, float r_area_no[3])
{ {
const Brush *brush = BKE_paint_brush(&sd->paint); const Brush *brush = BKE_paint_brush(&sd->paint);
SCULPT_pbvh_calc_area_normal(brush, ob, nodes, true, r_area_no); SCULPT_pbvh_calc_area_normal(brush, ob, nodes, r_area_no);
} }
bool SCULPT_pbvh_calc_area_normal( bool SCULPT_pbvh_calc_area_normal(const Brush *brush,
const Brush *brush, Object *ob, Span<PBVHNode *> nodes, bool use_threading, float r_area_no[3]) Object *ob,
Span<PBVHNode *> nodes,
float r_area_no[3])
{ {
using namespace blender;
SculptSession *ss = ob->sculpt; SculptSession *ss = ob->sculpt;
const bool has_bm_orco = ss->bm && SCULPT_stroke_is_dynamic_topology(ss, brush); const bool has_bm_orco = ss->bm && SCULPT_stroke_is_dynamic_topology(ss, brush);
/* Intentionally set 'sd' to nullptr since this is used for vertex paint too. */ bool any_vertex_sampled = false;
SculptThreadedTaskData data{};
data.sd = nullptr;
data.ob = ob;
data.brush = brush;
data.nodes = nodes;
data.has_bm_orco = has_bm_orco;
data.use_area_nos = true;
data.any_vertex_sampled = false;
AreaNormalCenterTLSData anctd = {{{0}}}; const AreaNormalCenterData anctd = threading::parallel_reduce(
nodes.index_range(),
TaskParallelSettings settings; 1,
BKE_pbvh_parallel_range_settings(&settings, use_threading, nodes.size()); AreaNormalCenterData{},
settings.func_reduce = calc_area_normal_and_center_reduce; [&](const IndexRange range, AreaNormalCenterData anctd) {
settings.userdata_chunk = &anctd; for (const int i : range) {
settings.userdata_chunk_size = sizeof(AreaNormalCenterTLSData); calc_area_normal_and_center_task(
BLI_task_parallel_range(0, nodes.size(), &data, calc_area_normal_and_center_task_cb, &settings); ob, brush, true, false, has_bm_orco, nodes[i], &anctd, any_vertex_sampled);
}
return anctd;
},
calc_area_normal_and_center_reduce);
/* For area normal. */ /* For area normal. */
for (int i = 0; i < ARRAY_SIZE(anctd.area_nos); i++) { for (int i = 0; i < ARRAY_SIZE(anctd.area_nos); i++) {
@@ -2293,35 +2267,32 @@ bool SCULPT_pbvh_calc_area_normal(
} }
} }
return data.any_vertex_sampled; return any_vertex_sampled;
} }
void SCULPT_calc_area_normal_and_center( void SCULPT_calc_area_normal_and_center(
Sculpt *sd, Object *ob, Span<PBVHNode *> nodes, float r_area_no[3], float r_area_co[3]) Sculpt *sd, Object *ob, Span<PBVHNode *> nodes, float r_area_no[3], float r_area_co[3])
{ {
using namespace blender;
const Brush *brush = BKE_paint_brush(&sd->paint); const Brush *brush = BKE_paint_brush(&sd->paint);
SculptSession *ss = ob->sculpt; SculptSession *ss = ob->sculpt;
const bool has_bm_orco = ss->bm && SCULPT_stroke_is_dynamic_topology(ss, brush); const bool has_bm_orco = ss->bm && SCULPT_stroke_is_dynamic_topology(ss, brush);
int n; int n;
/* Intentionally set 'sd' to nullptr since this is used for vertex paint too. */ bool any_vertex_sampled = false;
SculptThreadedTaskData data{};
data.sd = nullptr;
data.ob = ob;
data.brush = brush;
data.nodes = nodes;
data.has_bm_orco = has_bm_orco;
data.use_area_cos = true;
data.use_area_nos = true;
AreaNormalCenterTLSData anctd = {{{0}}}; const AreaNormalCenterData anctd = threading::parallel_reduce(
nodes.index_range(),
TaskParallelSettings settings; 1,
BKE_pbvh_parallel_range_settings(&settings, true, nodes.size()); AreaNormalCenterData{},
settings.func_reduce = calc_area_normal_and_center_reduce; [&](const IndexRange range, AreaNormalCenterData anctd) {
settings.userdata_chunk = &anctd; for (const int i : range) {
settings.userdata_chunk_size = sizeof(AreaNormalCenterTLSData); calc_area_normal_and_center_task(
BLI_task_parallel_range(0, nodes.size(), &data, calc_area_normal_and_center_task_cb, &settings); ob, brush, true, true, has_bm_orco, nodes[i], &anctd, any_vertex_sampled);
}
return anctd;
},
calc_area_normal_and_center_reduce);
/* For flatten center. */ /* For flatten center. */
for (n = 0; n < ARRAY_SIZE(anctd.area_cos); n++) { for (n = 0; n < ARRAY_SIZE(anctd.area_cos); n++) {
@@ -3278,26 +3249,20 @@ float SCULPT_brush_plane_offset_get(Sculpt *sd, SculptSession *ss)
/** \name Sculpt Gravity Brush /** \name Sculpt Gravity Brush
* \{ */ * \{ */
static void do_gravity_task_cb_ex(void *__restrict userdata, static void do_gravity_task(SculptSession *ss,
const int n, const Brush *brush,
const TaskParallelTLS *__restrict tls) const float *offset,
PBVHNode *node)
{ {
SculptThreadedTaskData *data = static_cast<SculptThreadedTaskData *>(userdata);
SculptSession *ss = data->ob->sculpt;
const Brush *brush = data->brush;
float *offset = data->offset;
PBVHVertexIter vd; PBVHVertexIter vd;
float(*proxy)[3]; float(*proxy)[3] = BKE_pbvh_node_add_proxy(ss->pbvh, node)->co;
proxy = BKE_pbvh_node_add_proxy(ss->pbvh, data->nodes[n])->co;
SculptBrushTest test; SculptBrushTest test;
SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape( SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape(
ss, &test, data->brush->falloff_shape); ss, &test, brush->falloff_shape);
const int thread_id = BLI_task_parallel_thread_id(tls); const int thread_id = BLI_task_parallel_thread_id(nullptr);
BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) {
if (!sculpt_brush_test_sq_fn(&test, vd.co)) { if (!sculpt_brush_test_sq_fn(&test, vd.co)) {
continue; continue;
} }
@@ -3323,6 +3288,7 @@ static void do_gravity_task_cb_ex(void *__restrict userdata,
static void do_gravity(Sculpt *sd, Object *ob, Span<PBVHNode *> nodes, float bstrength) static void do_gravity(Sculpt *sd, Object *ob, Span<PBVHNode *> nodes, float bstrength)
{ {
using namespace blender;
SculptSession *ss = ob->sculpt; SculptSession *ss = ob->sculpt;
Brush *brush = BKE_paint_brush(&sd->paint); Brush *brush = BKE_paint_brush(&sd->paint);
@@ -3335,17 +3301,11 @@ static void do_gravity(Sculpt *sd, Object *ob, Span<PBVHNode *> nodes, float bst
mul_v3_v3v3(offset, gravity_vector, ss->cache->scale); mul_v3_v3v3(offset, gravity_vector, ss->cache->scale);
mul_v3_fl(offset, bstrength); mul_v3_fl(offset, bstrength);
/* Threaded loop over nodes. */ threading::parallel_for(nodes.index_range(), 1, [&](const IndexRange range) {
SculptThreadedTaskData data{}; for (const int i : range) {
data.sd = sd; do_gravity_task(ss, brush, offset, nodes[i]);
data.ob = ob; }
data.brush = brush; });
data.nodes = nodes;
data.offset = offset;
TaskParallelSettings settings;
BKE_pbvh_parallel_range_settings(&settings, true, nodes.size());
BLI_task_parallel_range(0, nodes.size(), &data, do_gravity_task_cb_ex, &settings);
} }
/** \} */ /** \} */
@@ -3455,41 +3415,38 @@ static void sculpt_topology_update(Sculpt *sd,
mul_m4_v3(ob->object_to_world, location); mul_m4_v3(ob->object_to_world, location);
} }
static void do_brush_action_task_cb(void *__restrict userdata, static void do_brush_action_task(Object *ob, const Brush *brush, PBVHNode *node)
const int n,
const TaskParallelTLS *__restrict /*tls*/)
{ {
SculptThreadedTaskData *data = static_cast<SculptThreadedTaskData *>(userdata); SculptSession *ss = ob->sculpt;
SculptSession *ss = data->ob->sculpt;
bool need_coords = ss->cache->supports_gravity; bool need_coords = ss->cache->supports_gravity;
if (data->brush->sculpt_tool == SCULPT_TOOL_DRAW_FACE_SETS) { if (brush->sculpt_tool == SCULPT_TOOL_DRAW_FACE_SETS) {
BKE_pbvh_node_mark_update_face_sets(data->nodes[n]); BKE_pbvh_node_mark_update_face_sets(node);
/* Draw face sets in smooth mode moves the vertices. */ /* Draw face sets in smooth mode moves the vertices. */
if (ss->cache->alt_smooth) { if (ss->cache->alt_smooth) {
need_coords = true; need_coords = true;
} }
else { else {
SCULPT_undo_push_node(data->ob, data->nodes[n], SCULPT_UNDO_FACE_SETS); SCULPT_undo_push_node(ob, node, SCULPT_UNDO_FACE_SETS);
} }
} }
else if (data->brush->sculpt_tool == SCULPT_TOOL_MASK) { else if (brush->sculpt_tool == SCULPT_TOOL_MASK) {
SCULPT_undo_push_node(data->ob, data->nodes[n], SCULPT_UNDO_MASK); SCULPT_undo_push_node(ob, node, SCULPT_UNDO_MASK);
BKE_pbvh_node_mark_update_mask(data->nodes[n]); BKE_pbvh_node_mark_update_mask(node);
} }
else if (SCULPT_tool_is_paint(data->brush->sculpt_tool)) { else if (SCULPT_tool_is_paint(brush->sculpt_tool)) {
SCULPT_undo_push_node(data->ob, data->nodes[n], SCULPT_UNDO_COLOR); SCULPT_undo_push_node(ob, node, SCULPT_UNDO_COLOR);
BKE_pbvh_node_mark_update_color(data->nodes[n]); BKE_pbvh_node_mark_update_color(node);
} }
else { else {
need_coords = true; need_coords = true;
} }
if (need_coords) { if (need_coords) {
SCULPT_undo_push_node(data->ob, data->nodes[n], SCULPT_UNDO_COORDS); SCULPT_undo_push_node(ob, node, SCULPT_UNDO_COORDS);
BKE_pbvh_node_mark_update(data->nodes[n]); BKE_pbvh_node_mark_update(node);
} }
} }
@@ -3499,6 +3456,7 @@ static void do_brush_action(Sculpt *sd,
UnifiedPaintSettings *ups, UnifiedPaintSettings *ups,
PaintModeSettings *paint_mode_settings) PaintModeSettings *paint_mode_settings)
{ {
using namespace blender;
SculptSession *ss = ob->sculpt; SculptSession *ss = ob->sculpt;
Vector<PBVHNode *> nodes, texnodes; Vector<PBVHNode *> nodes, texnodes;
@@ -3599,15 +3557,11 @@ static void do_brush_action(Sculpt *sd,
float location[3]; float location[3];
if (!use_pixels) { if (!use_pixels) {
SculptThreadedTaskData task_data{}; threading::parallel_for(nodes.index_range(), 1, [&](const IndexRange range) {
task_data.sd = sd; for (const int i : range) {
task_data.ob = ob; do_brush_action_task(ob, brush, nodes[i]);
task_data.brush = brush; }
task_data.nodes = nodes; });
TaskParallelSettings settings;
BKE_pbvh_parallel_range_settings(&settings, true, nodes.size());
BLI_task_parallel_range(0, nodes.size(), &task_data, do_brush_action_task_cb, &settings);
} }
if (sculpt_brush_needs_normal(ss, sd, brush)) { if (sculpt_brush_needs_normal(ss, sd, brush)) {
@@ -5194,7 +5148,7 @@ bool SCULPT_cursor_geometry_info_update(bContext *C,
} }
/* Calculate the sampled normal. */ /* Calculate the sampled normal. */
if (SCULPT_pbvh_calc_area_normal(brush, ob, nodes, true, sampled_normal)) { if (SCULPT_pbvh_calc_area_normal(brush, ob, nodes, sampled_normal)) {
copy_v3_v3(out->normal, sampled_normal); copy_v3_v3(out->normal, sampled_normal);
copy_v3_v3(ss->cursor_sampled_normal, sampled_normal); copy_v3_v3(ss->cursor_sampled_normal, sampled_normal);
} }
@@ -6018,63 +5972,42 @@ static void sculpt_pose_fake_neighbors_free(SculptSession *ss)
MEM_SAFE_FREE(ss->fake_neighbors.fake_neighbor_index); MEM_SAFE_FREE(ss->fake_neighbors.fake_neighbor_index);
} }
struct NearestVertexFakeNeighborTLSData { struct NearestVertexFakeNeighborData {
PBVHVertRef nearest_vertex; PBVHVertRef nearest_vertex;
float nearest_vertex_distance_squared; float nearest_vertex_distance_sq;
int current_topology_id; int current_topology_id;
}; };
static void do_fake_neighbor_search_task_cb(void *__restrict userdata, static void do_fake_neighbor_search_task(SculptSession *ss,
const int n, const float nearest_vertex_search_co[3],
const TaskParallelTLS *__restrict tls) const float max_distance_sq,
PBVHNode *node,
NearestVertexFakeNeighborData *nvtd)
{ {
SculptThreadedTaskData *data = static_cast<SculptThreadedTaskData *>(userdata);
SculptSession *ss = data->ob->sculpt;
NearestVertexFakeNeighborTLSData *nvtd = static_cast<NearestVertexFakeNeighborTLSData *>(
tls->userdata_chunk);
PBVHVertexIter vd; PBVHVertexIter vd;
BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) {
BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) {
int vd_topology_id = SCULPT_vertex_island_get(ss, vd.vertex); int vd_topology_id = SCULPT_vertex_island_get(ss, vd.vertex);
if (vd_topology_id != nvtd->current_topology_id && if (vd_topology_id != nvtd->current_topology_id &&
ss->fake_neighbors.fake_neighbor_index[vd.index] == FAKE_NEIGHBOR_NONE) ss->fake_neighbors.fake_neighbor_index[vd.index] == FAKE_NEIGHBOR_NONE)
{ {
float distance_squared = len_squared_v3v3(vd.co, data->nearest_vertex_search_co); float distance_squared = len_squared_v3v3(vd.co, nearest_vertex_search_co);
if (distance_squared < nvtd->nearest_vertex_distance_squared && if (distance_squared < nvtd->nearest_vertex_distance_sq &&
distance_squared < data->max_distance_squared) distance_squared < max_distance_sq) {
{
nvtd->nearest_vertex = vd.vertex; nvtd->nearest_vertex = vd.vertex;
nvtd->nearest_vertex_distance_squared = distance_squared; nvtd->nearest_vertex_distance_sq = distance_squared;
} }
} }
} }
BKE_pbvh_vertex_iter_end; BKE_pbvh_vertex_iter_end;
} }
static void fake_neighbor_search_reduce(const void *__restrict /*userdata*/,
void *__restrict chunk_join,
void *__restrict chunk)
{
NearestVertexFakeNeighborTLSData *join = static_cast<NearestVertexFakeNeighborTLSData *>(
chunk_join);
NearestVertexFakeNeighborTLSData *nvtd = static_cast<NearestVertexFakeNeighborTLSData *>(chunk);
if (join->nearest_vertex.i == PBVH_REF_NONE) {
join->nearest_vertex = nvtd->nearest_vertex;
join->nearest_vertex_distance_squared = nvtd->nearest_vertex_distance_squared;
}
else if (nvtd->nearest_vertex_distance_squared < join->nearest_vertex_distance_squared) {
join->nearest_vertex = nvtd->nearest_vertex;
join->nearest_vertex_distance_squared = nvtd->nearest_vertex_distance_squared;
}
}
static PBVHVertRef SCULPT_fake_neighbor_search(Sculpt *sd, static PBVHVertRef SCULPT_fake_neighbor_search(Sculpt *sd,
Object *ob, Object *ob,
const PBVHVertRef vertex, const PBVHVertRef vertex,
float max_distance) float max_distance)
{ {
using namespace blender;
SculptSession *ss = ob->sculpt; SculptSession *ss = ob->sculpt;
Vector<PBVHNode *> nodes;
SculptSearchSphereData data{}; SculptSearchSphereData data{};
data.ss = ss; data.ss = ss;
@@ -6083,32 +6016,43 @@ static PBVHVertRef SCULPT_fake_neighbor_search(Sculpt *sd,
data.original = false; data.original = false;
data.center = SCULPT_vertex_co_get(ss, vertex); data.center = SCULPT_vertex_co_get(ss, vertex);
nodes = blender::bke::pbvh::search_gather( Vector<PBVHNode *> nodes = blender::bke::pbvh::search_gather(
ss->pbvh, [&](PBVHNode &node) { return SCULPT_search_sphere(&node, &data); }); ss->pbvh, [&](PBVHNode &node) { return SCULPT_search_sphere(&node, &data); });
if (nodes.is_empty()) { if (nodes.is_empty()) {
return BKE_pbvh_make_vref(PBVH_REF_NONE); return BKE_pbvh_make_vref(PBVH_REF_NONE);
} }
SculptThreadedTaskData task_data{}; const float3 nearest_vertex_search_co = SCULPT_vertex_co_get(ss, vertex);
task_data.sd = sd; const float max_distance_sq = max_distance * max_distance;
task_data.ob = ob;
task_data.nodes = nodes;
task_data.max_distance_squared = max_distance * max_distance;
copy_v3_v3(task_data.nearest_vertex_search_co, SCULPT_vertex_co_get(ss, vertex)); NearestVertexFakeNeighborData nvtd;
NearestVertexFakeNeighborTLSData nvtd;
nvtd.nearest_vertex.i = -1; nvtd.nearest_vertex.i = -1;
nvtd.nearest_vertex_distance_squared = FLT_MAX; nvtd.nearest_vertex_distance_sq = FLT_MAX;
nvtd.current_topology_id = SCULPT_vertex_island_get(ss, vertex); nvtd.current_topology_id = SCULPT_vertex_island_get(ss, vertex);
TaskParallelSettings settings; nvtd = threading::parallel_reduce(
BKE_pbvh_parallel_range_settings(&settings, true, nodes.size()); nodes.index_range(),
settings.func_reduce = fake_neighbor_search_reduce; 1,
settings.userdata_chunk = &nvtd; nvtd,
settings.userdata_chunk_size = sizeof(NearestVertexFakeNeighborTLSData); [&](const IndexRange range, NearestVertexFakeNeighborData nvtd) {
BLI_task_parallel_range(0, nodes.size(), &task_data, do_fake_neighbor_search_task_cb, &settings); for (const int i : range) {
do_fake_neighbor_search_task(
ss, nearest_vertex_search_co, max_distance_sq, nodes[i], &nvtd);
}
return nvtd;
},
[](const NearestVertexFakeNeighborData &a, const NearestVertexFakeNeighborData &b) {
NearestVertexFakeNeighborData joined = a;
if (joined.nearest_vertex.i == PBVH_REF_NONE) {
joined.nearest_vertex = b.nearest_vertex;
joined.nearest_vertex_distance_sq = b.nearest_vertex_distance_sq;
}
else if (b.nearest_vertex_distance_sq < joined.nearest_vertex_distance_sq) {
joined.nearest_vertex = b.nearest_vertex;
joined.nearest_vertex_distance_sq = b.nearest_vertex_distance_sq;
}
return joined;
});
return nvtd.nearest_vertex; return nvtd.nearest_vertex;
} }

View File

@@ -647,23 +647,18 @@ static float sculpt_boundary_displacement_from_grab_delta_get(SculptSession *ss,
return dist_signed_to_plane_v3(pos, plane); return dist_signed_to_plane_v3(pos, plane);
} }
/* Deformation tasks callbacks. */ static void do_boundary_brush_bend_task(Object *ob, const Brush *brush, PBVHNode *node)
static void do_boundary_brush_bend_task_cb_ex(void *__restrict userdata,
const int n,
const TaskParallelTLS *__restrict /*tls*/)
{ {
SculptThreadedTaskData *data = static_cast<SculptThreadedTaskData *>(userdata); SculptSession *ss = ob->sculpt;
SculptSession *ss = data->ob->sculpt;
const int symm_area = ss->cache->mirror_symmetry_pass; const int symm_area = ss->cache->mirror_symmetry_pass;
SculptBoundary *boundary = ss->cache->boundaries[symm_area]; SculptBoundary *boundary = ss->cache->boundaries[symm_area];
const ePaintSymmetryFlags symm = SCULPT_mesh_symmetry_xyz_get(data->ob); const ePaintSymmetryFlags symm = SCULPT_mesh_symmetry_xyz_get(ob);
const Brush *brush = data->brush;
const float strength = ss->cache->bstrength; const float strength = ss->cache->bstrength;
PBVHVertexIter vd; PBVHVertexIter vd;
SculptOrigVertData orig_data; SculptOrigVertData orig_data;
SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n], SCULPT_UNDO_COORDS); SCULPT_orig_vert_data_init(&orig_data, ob, node, SCULPT_UNDO_COORDS);
const float disp = strength * sculpt_boundary_displacement_from_grab_delta_get(ss, boundary); const float disp = strength * sculpt_boundary_displacement_from_grab_delta_get(ss, boundary);
float angle_factor = disp / ss->cache->radius; float angle_factor = disp / ss->cache->radius;
@@ -673,10 +668,9 @@ static void do_boundary_brush_bend_task_cb_ex(void *__restrict userdata,
} }
const float angle = angle_factor * M_PI; const float angle = angle_factor * M_PI;
AutomaskingNodeData automask_data; AutomaskingNodeData automask_data;
SCULPT_automasking_node_begin( SCULPT_automasking_node_begin(ob, ss, ss->cache->automasking, &automask_data, node);
data->ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]);
BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) {
if (boundary->edit_info[vd.index].propagation_steps_num == -1) { if (boundary->edit_info[vd.index].propagation_steps_num == -1) {
continue; continue;
} }
@@ -707,29 +701,24 @@ static void do_boundary_brush_bend_task_cb_ex(void *__restrict userdata,
BKE_pbvh_vertex_iter_end; BKE_pbvh_vertex_iter_end;
} }
static void do_boundary_brush_slide_task_cb_ex(void *__restrict userdata, static void do_boundary_brush_slide_task(Object *ob, const Brush *brush, PBVHNode *node)
const int n,
const TaskParallelTLS *__restrict /*tls*/)
{ {
SculptThreadedTaskData *data = static_cast<SculptThreadedTaskData *>(userdata); SculptSession *ss = ob->sculpt;
SculptSession *ss = data->ob->sculpt;
const int symm_area = ss->cache->mirror_symmetry_pass; const int symm_area = ss->cache->mirror_symmetry_pass;
SculptBoundary *boundary = ss->cache->boundaries[symm_area]; SculptBoundary *boundary = ss->cache->boundaries[symm_area];
const ePaintSymmetryFlags symm = SCULPT_mesh_symmetry_xyz_get(data->ob); const ePaintSymmetryFlags symm = SCULPT_mesh_symmetry_xyz_get(ob);
const Brush *brush = data->brush;
const float strength = ss->cache->bstrength; const float strength = ss->cache->bstrength;
PBVHVertexIter vd; PBVHVertexIter vd;
SculptOrigVertData orig_data; SculptOrigVertData orig_data;
SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n], SCULPT_UNDO_COORDS); SCULPT_orig_vert_data_init(&orig_data, ob, node, SCULPT_UNDO_COORDS);
const float disp = sculpt_boundary_displacement_from_grab_delta_get(ss, boundary); const float disp = sculpt_boundary_displacement_from_grab_delta_get(ss, boundary);
AutomaskingNodeData automask_data; AutomaskingNodeData automask_data;
SCULPT_automasking_node_begin( SCULPT_automasking_node_begin(ob, ss, ss->cache->automasking, &automask_data, node);
data->ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]);
BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) {
if (boundary->edit_info[vd.index].propagation_steps_num == -1) { if (boundary->edit_info[vd.index].propagation_steps_num == -1) {
continue; continue;
} }
@@ -758,29 +747,24 @@ static void do_boundary_brush_slide_task_cb_ex(void *__restrict userdata,
BKE_pbvh_vertex_iter_end; BKE_pbvh_vertex_iter_end;
} }
static void do_boundary_brush_inflate_task_cb_ex(void *__restrict userdata, static void do_boundary_brush_inflate_task(Object *ob, const Brush *brush, PBVHNode *node)
const int n,
const TaskParallelTLS *__restrict /*tls*/)
{ {
SculptThreadedTaskData *data = static_cast<SculptThreadedTaskData *>(userdata); SculptSession *ss = ob->sculpt;
SculptSession *ss = data->ob->sculpt;
const int symm_area = ss->cache->mirror_symmetry_pass; const int symm_area = ss->cache->mirror_symmetry_pass;
SculptBoundary *boundary = ss->cache->boundaries[symm_area]; SculptBoundary *boundary = ss->cache->boundaries[symm_area];
const ePaintSymmetryFlags symm = SCULPT_mesh_symmetry_xyz_get(data->ob); const ePaintSymmetryFlags symm = SCULPT_mesh_symmetry_xyz_get(ob);
const Brush *brush = data->brush;
const float strength = ss->cache->bstrength; const float strength = ss->cache->bstrength;
PBVHVertexIter vd; PBVHVertexIter vd;
SculptOrigVertData orig_data; SculptOrigVertData orig_data;
SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n], SCULPT_UNDO_COORDS); SCULPT_orig_vert_data_init(&orig_data, ob, node, SCULPT_UNDO_COORDS);
AutomaskingNodeData automask_data; AutomaskingNodeData automask_data;
SCULPT_automasking_node_begin( SCULPT_automasking_node_begin(ob, ss, ss->cache->automasking, &automask_data, node);
data->ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]);
const float disp = sculpt_boundary_displacement_from_grab_delta_get(ss, boundary); const float disp = sculpt_boundary_displacement_from_grab_delta_get(ss, boundary);
BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) {
if (boundary->edit_info[vd.index].propagation_steps_num == -1) { if (boundary->edit_info[vd.index].propagation_steps_num == -1) {
continue; continue;
} }
@@ -809,27 +793,22 @@ static void do_boundary_brush_inflate_task_cb_ex(void *__restrict userdata,
BKE_pbvh_vertex_iter_end; BKE_pbvh_vertex_iter_end;
} }
static void do_boundary_brush_grab_task_cb_ex(void *__restrict userdata, static void do_boundary_brush_grab_task(Object *ob, const Brush *brush, PBVHNode *node)
const int n,
const TaskParallelTLS *__restrict /*tls*/)
{ {
SculptThreadedTaskData *data = static_cast<SculptThreadedTaskData *>(userdata); SculptSession *ss = ob->sculpt;
SculptSession *ss = data->ob->sculpt;
const int symm_area = ss->cache->mirror_symmetry_pass; const int symm_area = ss->cache->mirror_symmetry_pass;
SculptBoundary *boundary = ss->cache->boundaries[symm_area]; SculptBoundary *boundary = ss->cache->boundaries[symm_area];
const ePaintSymmetryFlags symm = SCULPT_mesh_symmetry_xyz_get(data->ob); const ePaintSymmetryFlags symm = SCULPT_mesh_symmetry_xyz_get(ob);
const Brush *brush = data->brush;
const float strength = ss->cache->bstrength; const float strength = ss->cache->bstrength;
PBVHVertexIter vd; PBVHVertexIter vd;
SculptOrigVertData orig_data; SculptOrigVertData orig_data;
SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n], SCULPT_UNDO_COORDS); SCULPT_orig_vert_data_init(&orig_data, ob, node, SCULPT_UNDO_COORDS);
AutomaskingNodeData automask_data; AutomaskingNodeData automask_data;
SCULPT_automasking_node_begin( SCULPT_automasking_node_begin(ob, ss, ss->cache->automasking, &automask_data, node);
data->ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]);
BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) {
if (boundary->edit_info[vd.index].propagation_steps_num == -1) { if (boundary->edit_info[vd.index].propagation_steps_num == -1) {
continue; continue;
} }
@@ -857,25 +836,20 @@ static void do_boundary_brush_grab_task_cb_ex(void *__restrict userdata,
BKE_pbvh_vertex_iter_end; BKE_pbvh_vertex_iter_end;
} }
static void do_boundary_brush_twist_task_cb_ex(void *__restrict userdata, static void do_boundary_brush_twist_task(Object *ob, const Brush *brush, PBVHNode *node)
const int n,
const TaskParallelTLS *__restrict /*tls*/)
{ {
SculptThreadedTaskData *data = static_cast<SculptThreadedTaskData *>(userdata); SculptSession *ss = ob->sculpt;
SculptSession *ss = data->ob->sculpt;
const int symm_area = ss->cache->mirror_symmetry_pass; const int symm_area = ss->cache->mirror_symmetry_pass;
SculptBoundary *boundary = ss->cache->boundaries[symm_area]; SculptBoundary *boundary = ss->cache->boundaries[symm_area];
const ePaintSymmetryFlags symm = SCULPT_mesh_symmetry_xyz_get(data->ob); const ePaintSymmetryFlags symm = SCULPT_mesh_symmetry_xyz_get(ob);
const Brush *brush = data->brush;
const float strength = ss->cache->bstrength; const float strength = ss->cache->bstrength;
PBVHVertexIter vd; PBVHVertexIter vd;
SculptOrigVertData orig_data; SculptOrigVertData orig_data;
SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n], SCULPT_UNDO_COORDS); SCULPT_orig_vert_data_init(&orig_data, ob, node, SCULPT_UNDO_COORDS);
AutomaskingNodeData automask_data; AutomaskingNodeData automask_data;
SCULPT_automasking_node_begin( SCULPT_automasking_node_begin(ob, ss, ss->cache->automasking, &automask_data, node);
data->ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]);
const float disp = strength * sculpt_boundary_displacement_from_grab_delta_get(ss, boundary); const float disp = strength * sculpt_boundary_displacement_from_grab_delta_get(ss, boundary);
float angle_factor = disp / ss->cache->radius; float angle_factor = disp / ss->cache->radius;
@@ -885,7 +859,7 @@ static void do_boundary_brush_twist_task_cb_ex(void *__restrict userdata,
} }
const float angle = angle_factor * M_PI; const float angle = angle_factor * M_PI;
BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) {
if (boundary->edit_info[vd.index].propagation_steps_num == -1) { if (boundary->edit_info[vd.index].propagation_steps_num == -1) {
continue; continue;
} }
@@ -916,24 +890,20 @@ static void do_boundary_brush_twist_task_cb_ex(void *__restrict userdata,
BKE_pbvh_vertex_iter_end; BKE_pbvh_vertex_iter_end;
} }
static void do_boundary_brush_smooth_task_cb_ex(void *__restrict userdata, static void do_boundary_brush_smooth_task(Object *ob, const Brush *brush, PBVHNode *node)
const int n,
const TaskParallelTLS *__restrict /*tls*/)
{ {
SculptThreadedTaskData *data = static_cast<SculptThreadedTaskData *>(userdata); SculptSession *ss = ob->sculpt;
SculptSession *ss = data->ob->sculpt;
const int symmetry_pass = ss->cache->mirror_symmetry_pass; const int symmetry_pass = ss->cache->mirror_symmetry_pass;
const SculptBoundary *boundary = ss->cache->boundaries[symmetry_pass]; const SculptBoundary *boundary = ss->cache->boundaries[symmetry_pass];
const ePaintSymmetryFlags symm = SCULPT_mesh_symmetry_xyz_get(data->ob); const ePaintSymmetryFlags symm = SCULPT_mesh_symmetry_xyz_get(ob);
const Brush *brush = data->brush;
const float strength = ss->cache->bstrength; const float strength = ss->cache->bstrength;
PBVHVertexIter vd; PBVHVertexIter vd;
SculptOrigVertData orig_data; SculptOrigVertData orig_data;
SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n], SCULPT_UNDO_COORDS); SCULPT_orig_vert_data_init(&orig_data, ob, node, SCULPT_UNDO_COORDS);
BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) {
if (boundary->edit_info[vd.index].propagation_steps_num == -1) { if (boundary->edit_info[vd.index].propagation_steps_num == -1) {
continue; continue;
} }
@@ -977,6 +947,7 @@ static void do_boundary_brush_smooth_task_cb_ex(void *__restrict userdata,
void SCULPT_do_boundary_brush(Sculpt *sd, Object *ob, Span<PBVHNode *> nodes) void SCULPT_do_boundary_brush(Sculpt *sd, Object *ob, Span<PBVHNode *> nodes)
{ {
using namespace blender;
SculptSession *ss = ob->sculpt; SculptSession *ss = ob->sculpt;
Brush *brush = BKE_paint_brush(&sd->paint); Brush *brush = BKE_paint_brush(&sd->paint);
@@ -1026,39 +997,48 @@ void SCULPT_do_boundary_brush(Sculpt *sd, Object *ob, Span<PBVHNode *> nodes)
return; return;
} }
SculptThreadedTaskData data{};
data.sd = sd;
data.ob = ob;
data.brush = brush;
data.nodes = nodes;
TaskParallelSettings settings;
BKE_pbvh_parallel_range_settings(&settings, true, nodes.size());
switch (brush->boundary_deform_type) { switch (brush->boundary_deform_type) {
case BRUSH_BOUNDARY_DEFORM_BEND: case BRUSH_BOUNDARY_DEFORM_BEND:
BLI_task_parallel_range( threading::parallel_for(nodes.index_range(), 1, [&](const IndexRange range) {
0, nodes.size(), &data, do_boundary_brush_bend_task_cb_ex, &settings); for (const int i : range) {
do_boundary_brush_bend_task(ob, brush, nodes[i]);
}
});
break; break;
case BRUSH_BOUNDARY_DEFORM_EXPAND: case BRUSH_BOUNDARY_DEFORM_EXPAND:
BLI_task_parallel_range( threading::parallel_for(nodes.index_range(), 1, [&](const IndexRange range) {
0, nodes.size(), &data, do_boundary_brush_slide_task_cb_ex, &settings); for (const int i : range) {
do_boundary_brush_slide_task(ob, brush, nodes[i]);
}
});
break; break;
case BRUSH_BOUNDARY_DEFORM_INFLATE: case BRUSH_BOUNDARY_DEFORM_INFLATE:
BLI_task_parallel_range( threading::parallel_for(nodes.index_range(), 1, [&](const IndexRange range) {
0, nodes.size(), &data, do_boundary_brush_inflate_task_cb_ex, &settings); for (const int i : range) {
do_boundary_brush_inflate_task(ob, brush, nodes[i]);
}
});
break; break;
case BRUSH_BOUNDARY_DEFORM_GRAB: case BRUSH_BOUNDARY_DEFORM_GRAB:
BLI_task_parallel_range( threading::parallel_for(nodes.index_range(), 1, [&](const IndexRange range) {
0, nodes.size(), &data, do_boundary_brush_grab_task_cb_ex, &settings); for (const int i : range) {
do_boundary_brush_grab_task(ob, brush, nodes[i]);
}
});
break; break;
case BRUSH_BOUNDARY_DEFORM_TWIST: case BRUSH_BOUNDARY_DEFORM_TWIST:
BLI_task_parallel_range( threading::parallel_for(nodes.index_range(), 1, [&](const IndexRange range) {
0, nodes.size(), &data, do_boundary_brush_twist_task_cb_ex, &settings); for (const int i : range) {
do_boundary_brush_twist_task(ob, brush, nodes[i]);
}
});
break; break;
case BRUSH_BOUNDARY_DEFORM_SMOOTH: case BRUSH_BOUNDARY_DEFORM_SMOOTH:
BLI_task_parallel_range( threading::parallel_for(nodes.index_range(), 1, [&](const IndexRange range) {
0, nodes.size(), &data, do_boundary_brush_smooth_task_cb_ex, &settings); for (const int i : range) {
do_boundary_brush_smooth_task(ob, brush, nodes[i]);
}
});
break; break;
} }
} }

View File

@@ -14,6 +14,7 @@
#include "BLI_math_geom.h" #include "BLI_math_geom.h"
#include "BLI_math_matrix.h" #include "BLI_math_matrix.h"
#include "BLI_math_vector.h" #include "BLI_math_vector.h"
#include "BLI_math_vector.hh"
#include "BLI_span.hh" #include "BLI_span.hh"
#include "BLI_task.h" #include "BLI_task.h"
#include "BLI_utildefines.h" #include "BLI_utildefines.h"
@@ -243,30 +244,23 @@ static void sculpt_project_v3_normal_align(SculptSession *ss,
/** \name Sculpt Draw Brush /** \name Sculpt Draw Brush
* \{ */ * \{ */
static void do_draw_brush_task_cb_ex(void *__restrict userdata, static void do_draw_brush_task(Object *ob, const Brush *brush, const float *offset, PBVHNode *node)
const int n,
const TaskParallelTLS *__restrict tls)
{ {
SculptThreadedTaskData *data = static_cast<SculptThreadedTaskData *>(userdata); SculptSession *ss = ob->sculpt;
SculptSession *ss = data->ob->sculpt;
const Brush *brush = data->brush;
const float *offset = data->offset;
PBVHVertexIter vd; PBVHVertexIter vd;
float(*proxy)[3]; float(*proxy)[3];
proxy = BKE_pbvh_node_add_proxy(ss->pbvh, data->nodes[n])->co; proxy = BKE_pbvh_node_add_proxy(ss->pbvh, node)->co;
SculptBrushTest test; SculptBrushTest test;
SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape( SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape(
ss, &test, data->brush->falloff_shape); ss, &test, brush->falloff_shape);
const int thread_id = BLI_task_parallel_thread_id(tls); const int thread_id = BLI_task_parallel_thread_id(nullptr);
AutomaskingNodeData automask_data; AutomaskingNodeData automask_data;
SCULPT_automasking_node_begin( SCULPT_automasking_node_begin(ob, ss, ss->cache->automasking, &automask_data, node);
data->ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]);
BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) {
if (!sculpt_brush_test_sq_fn(&test, vd.co)) { if (!sculpt_brush_test_sq_fn(&test, vd.co)) {
continue; continue;
} }
@@ -314,6 +308,7 @@ static void do_draw_brush_task_cb_ex(void *__restrict userdata,
void SCULPT_do_draw_brush(Sculpt *sd, Object *ob, Span<PBVHNode *> nodes) void SCULPT_do_draw_brush(Sculpt *sd, Object *ob, Span<PBVHNode *> nodes)
{ {
using namespace blender;
SculptSession *ss = ob->sculpt; SculptSession *ss = ob->sculpt;
Brush *brush = BKE_paint_brush(&sd->paint); Brush *brush = BKE_paint_brush(&sd->paint);
float offset[3]; float offset[3];
@@ -330,17 +325,11 @@ void SCULPT_do_draw_brush(Sculpt *sd, Object *ob, Span<PBVHNode *> nodes)
* initialize before threads so they can do curve mapping. */ * initialize before threads so they can do curve mapping. */
BKE_curvemapping_init(brush->curve); BKE_curvemapping_init(brush->curve);
/* Threaded loop over nodes. */ threading::parallel_for(nodes.index_range(), 1, [&](const IndexRange range) {
SculptThreadedTaskData data{}; for (const int i : range) {
data.sd = sd; do_draw_brush_task(ob, brush, offset, nodes[i]);
data.ob = ob; }
data.brush = brush; });
data.nodes = nodes;
data.offset = offset;
TaskParallelSettings settings;
BKE_pbvh_parallel_range_settings(&settings, true, nodes.size());
BLI_task_parallel_range(0, nodes.size(), &data, do_draw_brush_task_cb_ex, &settings);
} }
/** \} */ /** \} */
@@ -349,34 +338,28 @@ void SCULPT_do_draw_brush(Sculpt *sd, Object *ob, Span<PBVHNode *> nodes)
/** \name Sculpt Fill Brush /** \name Sculpt Fill Brush
* \{ */ * \{ */
static void do_fill_brush_task_cb_ex(void *__restrict userdata, static void do_fill_brush_task(
const int n, Object *ob, const Brush *brush, const float *area_no, const float *area_co, PBVHNode *node)
const TaskParallelTLS *__restrict tls)
{ {
SculptThreadedTaskData *data = static_cast<SculptThreadedTaskData *>(userdata); SculptSession *ss = ob->sculpt;
SculptSession *ss = data->ob->sculpt;
const Brush *brush = data->brush;
const float *area_no = data->area_no;
const float *area_co = data->area_co;
PBVHVertexIter vd; PBVHVertexIter vd;
float(*proxy)[3]; float(*proxy)[3];
const float bstrength = ss->cache->bstrength; const float bstrength = ss->cache->bstrength;
proxy = BKE_pbvh_node_add_proxy(ss->pbvh, data->nodes[n])->co; proxy = BKE_pbvh_node_add_proxy(ss->pbvh, node)->co;
SculptBrushTest test; SculptBrushTest test;
SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape( SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape(
ss, &test, data->brush->falloff_shape); ss, &test, brush->falloff_shape);
const int thread_id = BLI_task_parallel_thread_id(tls); const int thread_id = BLI_task_parallel_thread_id(nullptr);
plane_from_point_normal_v3(test.plane_tool, area_co, area_no); plane_from_point_normal_v3(test.plane_tool, area_co, area_no);
AutomaskingNodeData automask_data; AutomaskingNodeData automask_data;
SCULPT_automasking_node_begin( SCULPT_automasking_node_begin(ob, ss, ss->cache->automasking, &automask_data, node);
data->ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]);
BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) {
if (!sculpt_brush_test_sq_fn(&test, vd.co)) { if (!sculpt_brush_test_sq_fn(&test, vd.co)) {
continue; continue;
} }
@@ -418,6 +401,7 @@ static void do_fill_brush_task_cb_ex(void *__restrict userdata,
void SCULPT_do_fill_brush(Sculpt *sd, Object *ob, Span<PBVHNode *> nodes) void SCULPT_do_fill_brush(Sculpt *sd, Object *ob, Span<PBVHNode *> nodes)
{ {
using namespace blender;
SculptSession *ss = ob->sculpt; SculptSession *ss = ob->sculpt;
Brush *brush = BKE_paint_brush(&sd->paint); Brush *brush = BKE_paint_brush(&sd->paint);
@@ -441,46 +425,34 @@ void SCULPT_do_fill_brush(Sculpt *sd, Object *ob, Span<PBVHNode *> nodes)
mul_v3_fl(temp, displace); mul_v3_fl(temp, displace);
add_v3_v3(area_co, temp); add_v3_v3(area_co, temp);
SculptThreadedTaskData data{}; threading::parallel_for(nodes.index_range(), 1, [&](const IndexRange range) {
data.sd = sd; for (const int i : range) {
data.ob = ob; do_fill_brush_task(ob, brush, area_no, area_co, nodes[i]);
data.brush = brush; }
data.nodes = nodes; });
data.area_no = area_no;
data.area_co = area_co;
TaskParallelSettings settings;
BKE_pbvh_parallel_range_settings(&settings, true, nodes.size());
BLI_task_parallel_range(0, nodes.size(), &data, do_fill_brush_task_cb_ex, &settings);
} }
static void do_scrape_brush_task_cb_ex(void *__restrict userdata, static void do_scrape_brush_task(
const int n, Object *ob, const Brush *brush, const float *area_no, const float *area_co, PBVHNode *node)
const TaskParallelTLS *__restrict tls)
{ {
SculptThreadedTaskData *data = static_cast<SculptThreadedTaskData *>(userdata); SculptSession *ss = ob->sculpt;
SculptSession *ss = data->ob->sculpt;
const Brush *brush = data->brush;
const float *area_no = data->area_no;
const float *area_co = data->area_co;
PBVHVertexIter vd; PBVHVertexIter vd;
float(*proxy)[3]; float(*proxy)[3];
const float bstrength = ss->cache->bstrength; const float bstrength = ss->cache->bstrength;
proxy = BKE_pbvh_node_add_proxy(ss->pbvh, data->nodes[n])->co; proxy = BKE_pbvh_node_add_proxy(ss->pbvh, node)->co;
SculptBrushTest test; SculptBrushTest test;
SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape( SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape(
ss, &test, data->brush->falloff_shape); ss, &test, brush->falloff_shape);
const int thread_id = BLI_task_parallel_thread_id(tls); const int thread_id = BLI_task_parallel_thread_id(nullptr);
plane_from_point_normal_v3(test.plane_tool, area_co, area_no); plane_from_point_normal_v3(test.plane_tool, area_co, area_no);
AutomaskingNodeData automask_data; AutomaskingNodeData automask_data;
SCULPT_automasking_node_begin( SCULPT_automasking_node_begin(ob, ss, ss->cache->automasking, &automask_data, node);
data->ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]);
BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) {
if (!sculpt_brush_test_sq_fn(&test, vd.co)) { if (!sculpt_brush_test_sq_fn(&test, vd.co)) {
continue; continue;
} }
@@ -522,6 +494,7 @@ static void do_scrape_brush_task_cb_ex(void *__restrict userdata,
void SCULPT_do_scrape_brush(Sculpt *sd, Object *ob, Span<PBVHNode *> nodes) void SCULPT_do_scrape_brush(Sculpt *sd, Object *ob, Span<PBVHNode *> nodes)
{ {
using namespace blender;
SculptSession *ss = ob->sculpt; SculptSession *ss = ob->sculpt;
Brush *brush = BKE_paint_brush(&sd->paint); Brush *brush = BKE_paint_brush(&sd->paint);
@@ -545,17 +518,11 @@ void SCULPT_do_scrape_brush(Sculpt *sd, Object *ob, Span<PBVHNode *> nodes)
mul_v3_fl(temp, displace); mul_v3_fl(temp, displace);
add_v3_v3(area_co, temp); add_v3_v3(area_co, temp);
SculptThreadedTaskData data{}; threading::parallel_for(nodes.index_range(), 1, [&](const IndexRange range) {
data.sd = sd; for (const int i : range) {
data.ob = ob; do_scrape_brush_task(ob, brush, area_no, area_co, nodes[i]);
data.brush = brush; }
data.nodes = nodes; });
data.area_no = area_no;
data.area_co = area_co;
TaskParallelSettings settings;
BKE_pbvh_parallel_range_settings(&settings, true, nodes.size());
BLI_task_parallel_range(0, nodes.size(), &data, do_scrape_brush_task_cb_ex, &settings);
} }
/** \} */ /** \} */
@@ -564,27 +531,25 @@ void SCULPT_do_scrape_brush(Sculpt *sd, Object *ob, Span<PBVHNode *> nodes)
/** \name Sculpt Clay Thumb Brush /** \name Sculpt Clay Thumb Brush
* \{ */ * \{ */
static void do_clay_thumb_brush_task_cb_ex(void *__restrict userdata, static void do_clay_thumb_brush_task(Object *ob,
const int n, const Brush *brush,
const TaskParallelTLS *__restrict tls) float (*mat)[4],
const float *area_no_sp,
const float *area_co,
const float bstrength,
PBVHNode *node)
{ {
SculptThreadedTaskData *data = static_cast<SculptThreadedTaskData *>(userdata); SculptSession *ss = ob->sculpt;
SculptSession *ss = data->ob->sculpt;
const Brush *brush = data->brush;
float(*mat)[4] = data->mat;
const float *area_no_sp = data->area_no_sp;
const float *area_co = data->area_co;
PBVHVertexIter vd; PBVHVertexIter vd;
float(*proxy)[3]; float(*proxy)[3];
const float bstrength = data->clay_strength;
proxy = BKE_pbvh_node_add_proxy(ss->pbvh, data->nodes[n])->co; proxy = BKE_pbvh_node_add_proxy(ss->pbvh, node)->co;
SculptBrushTest test; SculptBrushTest test;
SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape( SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape(
ss, &test, data->brush->falloff_shape); ss, &test, brush->falloff_shape);
const int thread_id = BLI_task_parallel_thread_id(tls); const int thread_id = BLI_task_parallel_thread_id(nullptr);
float plane_tilt[4]; float plane_tilt[4];
float normal_tilt[3]; float normal_tilt[3];
@@ -599,10 +564,9 @@ static void do_clay_thumb_brush_task_cb_ex(void *__restrict userdata,
plane_from_point_normal_v3(plane_tilt, area_co, normal_tilt); plane_from_point_normal_v3(plane_tilt, area_co, normal_tilt);
AutomaskingNodeData automask_data; AutomaskingNodeData automask_data;
SCULPT_automasking_node_begin( SCULPT_automasking_node_begin(ob, ss, ss->cache->automasking, &automask_data, node);
data->ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]);
BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) {
if (!sculpt_brush_test_sq_fn(&test, vd.co)) { if (!sculpt_brush_test_sq_fn(&test, vd.co)) {
continue; continue;
} }
@@ -655,6 +619,7 @@ float SCULPT_clay_thumb_get_stabilized_pressure(StrokeCache *cache)
void SCULPT_do_clay_thumb_brush(Sculpt *sd, Object *ob, Span<PBVHNode *> nodes) void SCULPT_do_clay_thumb_brush(Sculpt *sd, Object *ob, Span<PBVHNode *> nodes)
{ {
using namespace blender;
SculptSession *ss = ob->sculpt; SculptSession *ss = ob->sculpt;
Brush *brush = BKE_paint_brush(&sd->paint); Brush *brush = BKE_paint_brush(&sd->paint);
@@ -723,19 +688,11 @@ void SCULPT_do_clay_thumb_brush(Sculpt *sd, Object *ob, Span<PBVHNode *> nodes)
float clay_strength = ss->cache->bstrength * float clay_strength = ss->cache->bstrength *
SCULPT_clay_thumb_get_stabilized_pressure(ss->cache); SCULPT_clay_thumb_get_stabilized_pressure(ss->cache);
SculptThreadedTaskData data{}; threading::parallel_for(nodes.index_range(), 1, [&](const IndexRange range) {
data.sd = sd; for (const int i : range) {
data.ob = ob; do_clay_thumb_brush_task(ob, brush, mat, area_no_sp, area_co, clay_strength, nodes[i]);
data.brush = brush; }
data.nodes = nodes; });
data.area_no_sp = area_no_sp;
data.area_co = ss->cache->location;
data.mat = mat;
data.clay_strength = clay_strength;
TaskParallelSettings settings;
BKE_pbvh_parallel_range_settings(&settings, true, nodes.size());
BLI_task_parallel_range(0, nodes.size(), &data, do_clay_thumb_brush_task_cb_ex, &settings);
} }
/** \} */ /** \} */
@@ -744,34 +701,28 @@ void SCULPT_do_clay_thumb_brush(Sculpt *sd, Object *ob, Span<PBVHNode *> nodes)
/** \name Sculpt Flatten Brush /** \name Sculpt Flatten Brush
* \{ */ * \{ */
static void do_flatten_brush_task_cb_ex(void *__restrict userdata, static void do_flatten_brush_task(
const int n, Object *ob, const Brush *brush, const float *area_no, const float *area_co, PBVHNode *node)
const TaskParallelTLS *__restrict tls)
{ {
SculptThreadedTaskData *data = static_cast<SculptThreadedTaskData *>(userdata); SculptSession *ss = ob->sculpt;
SculptSession *ss = data->ob->sculpt;
const Brush *brush = data->brush;
const float *area_no = data->area_no;
const float *area_co = data->area_co;
PBVHVertexIter vd; PBVHVertexIter vd;
float(*proxy)[3]; float(*proxy)[3];
const float bstrength = ss->cache->bstrength; const float bstrength = ss->cache->bstrength;
proxy = BKE_pbvh_node_add_proxy(ss->pbvh, data->nodes[n])->co; proxy = BKE_pbvh_node_add_proxy(ss->pbvh, node)->co;
SculptBrushTest test; SculptBrushTest test;
SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape( SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape(
ss, &test, data->brush->falloff_shape); ss, &test, brush->falloff_shape);
const int thread_id = BLI_task_parallel_thread_id(tls); const int thread_id = BLI_task_parallel_thread_id(nullptr);
plane_from_point_normal_v3(test.plane_tool, area_co, area_no); plane_from_point_normal_v3(test.plane_tool, area_co, area_no);
AutomaskingNodeData automask_data; AutomaskingNodeData automask_data;
SCULPT_automasking_node_begin( SCULPT_automasking_node_begin(ob, ss, ss->cache->automasking, &automask_data, node);
data->ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]);
BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) {
if (!sculpt_brush_test_sq_fn(&test, vd.co)) { if (!sculpt_brush_test_sq_fn(&test, vd.co)) {
continue; continue;
} }
@@ -808,6 +759,7 @@ static void do_flatten_brush_task_cb_ex(void *__restrict userdata,
void SCULPT_do_flatten_brush(Sculpt *sd, Object *ob, Span<PBVHNode *> nodes) void SCULPT_do_flatten_brush(Sculpt *sd, Object *ob, Span<PBVHNode *> nodes)
{ {
using namespace blender;
SculptSession *ss = ob->sculpt; SculptSession *ss = ob->sculpt;
Brush *brush = BKE_paint_brush(&sd->paint); Brush *brush = BKE_paint_brush(&sd->paint);
@@ -830,17 +782,11 @@ void SCULPT_do_flatten_brush(Sculpt *sd, Object *ob, Span<PBVHNode *> nodes)
mul_v3_fl(temp, displace); mul_v3_fl(temp, displace);
add_v3_v3(area_co, temp); add_v3_v3(area_co, temp);
SculptThreadedTaskData data{}; threading::parallel_for(nodes.index_range(), 1, [&](const IndexRange range) {
data.sd = sd; for (const int i : range) {
data.ob = ob; do_flatten_brush_task(ob, brush, area_no, area_co, nodes[i]);
data.brush = brush; }
data.nodes = nodes; });
data.area_no = area_no;
data.area_co = area_co;
TaskParallelSettings settings;
BKE_pbvh_parallel_range_settings(&settings, true, nodes.size());
BLI_task_parallel_range(0, nodes.size(), &data, do_flatten_brush_task_cb_ex, &settings);
} }
/** \} */ /** \} */
@@ -850,19 +796,17 @@ void SCULPT_do_flatten_brush(Sculpt *sd, Object *ob, Span<PBVHNode *> nodes)
* \{ */ * \{ */
struct ClaySampleData { struct ClaySampleData {
float plane_dist[2]; blender::float2 plane_dist;
}; };
static void calc_clay_surface_task_cb(void *__restrict userdata, static void calc_clay_surface_task_cb(Object *ob,
const int n, const Brush *brush,
const TaskParallelTLS *__restrict tls) const float *area_no,
const float *area_co,
PBVHNode *node,
ClaySampleData *csd)
{ {
SculptThreadedTaskData *data = static_cast<SculptThreadedTaskData *>(userdata); SculptSession *ss = ob->sculpt;
SculptSession *ss = data->ob->sculpt;
const Brush *brush = data->brush;
ClaySampleData *csd = static_cast<ClaySampleData *>(tls->userdata_chunk);
const float *area_no = data->area_no;
const float *area_co = data->area_co;
float plane[4]; float plane[4];
PBVHVertexIter vd; PBVHVertexIter vd;
@@ -881,7 +825,7 @@ static void calc_clay_surface_task_cb(void *__restrict userdata,
return; return;
} }
BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) {
if (!sculpt_brush_test_sq_fn(&test, vd.co)) { if (!sculpt_brush_test_sq_fn(&test, vd.co)) {
continue; continue;
} }
@@ -898,44 +842,28 @@ static void calc_clay_surface_task_cb(void *__restrict userdata,
} }
} }
static void calc_clay_surface_reduce(const void *__restrict /*userdata*/, static void do_clay_brush_task(
void *__restrict chunk_join, Object *ob, const Brush *brush, const float *area_no, const float *area_co, PBVHNode *node)
void *__restrict chunk)
{ {
ClaySampleData *join = static_cast<ClaySampleData *>(chunk_join); SculptSession *ss = ob->sculpt;
ClaySampleData *csd = static_cast<ClaySampleData *>(chunk);
join->plane_dist[0] = MIN2(csd->plane_dist[0], join->plane_dist[0]);
join->plane_dist[1] = MIN2(csd->plane_dist[1], join->plane_dist[1]);
}
static void do_clay_brush_task_cb_ex(void *__restrict userdata,
const int n,
const TaskParallelTLS *__restrict tls)
{
SculptThreadedTaskData *data = static_cast<SculptThreadedTaskData *>(userdata);
SculptSession *ss = data->ob->sculpt;
const Brush *brush = data->brush;
const float *area_no = data->area_no;
const float *area_co = data->area_co;
PBVHVertexIter vd; PBVHVertexIter vd;
float(*proxy)[3]; float(*proxy)[3];
const float bstrength = fabsf(ss->cache->bstrength); const float bstrength = fabsf(ss->cache->bstrength);
proxy = BKE_pbvh_node_add_proxy(ss->pbvh, data->nodes[n])->co; proxy = BKE_pbvh_node_add_proxy(ss->pbvh, node)->co;
SculptBrushTest test; SculptBrushTest test;
SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape( SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape(
ss, &test, data->brush->falloff_shape); ss, &test, brush->falloff_shape);
const int thread_id = BLI_task_parallel_thread_id(tls); const int thread_id = BLI_task_parallel_thread_id(nullptr);
plane_from_point_normal_v3(test.plane_tool, area_co, area_no); plane_from_point_normal_v3(test.plane_tool, area_co, area_no);
AutomaskingNodeData automask_data; AutomaskingNodeData automask_data;
SCULPT_automasking_node_begin( SCULPT_automasking_node_begin(ob, ss, ss->cache->automasking, &automask_data, node);
data->ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]);
BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) {
if (!sculpt_brush_test_sq_fn(&test, vd.co)) { if (!sculpt_brush_test_sq_fn(&test, vd.co)) {
continue; continue;
} }
@@ -970,6 +898,7 @@ static void do_clay_brush_task_cb_ex(void *__restrict userdata,
void SCULPT_do_clay_brush(Sculpt *sd, Object *ob, Span<PBVHNode *> nodes) void SCULPT_do_clay_brush(Sculpt *sd, Object *ob, Span<PBVHNode *> nodes)
{ {
using namespace blender;
SculptSession *ss = ob->sculpt; SculptSession *ss = ob->sculpt;
Brush *brush = BKE_paint_brush(&sd->paint); Brush *brush = BKE_paint_brush(&sd->paint);
@@ -986,24 +915,19 @@ void SCULPT_do_clay_brush(Sculpt *sd, Object *ob, Span<PBVHNode *> nodes)
SCULPT_calc_brush_plane(sd, ob, nodes, area_no, area_co); SCULPT_calc_brush_plane(sd, ob, nodes, area_no, area_co);
SculptThreadedTaskData sample_data{}; ClaySampleData csd = threading::parallel_reduce(
sample_data.sd = nullptr; nodes.index_range(),
sample_data.ob = ob; 1,
sample_data.brush = brush; ClaySampleData{},
sample_data.nodes = nodes; [&](const IndexRange range, ClaySampleData csd) {
sample_data.area_no = area_no; for (const int i : range) {
sample_data.area_co = ss->cache->location; calc_clay_surface_task_cb(ob, brush, area_no, area_co, nodes[i], &csd);
}
ClaySampleData csd = {{0}}; return csd;
},
TaskParallelSettings sample_settings; [](const ClaySampleData &a, const ClaySampleData &b) {
BKE_pbvh_parallel_range_settings(&sample_settings, true, nodes.size()); return ClaySampleData{math::min(a.plane_dist, b.plane_dist)};
sample_settings.func_reduce = calc_clay_surface_reduce; });
sample_settings.userdata_chunk = &csd;
sample_settings.userdata_chunk_size = sizeof(ClaySampleData);
BLI_task_parallel_range(
0, nodes.size(), &sample_data, calc_clay_surface_task_cb, &sample_settings);
float d_offset = (csd.plane_dist[0] + csd.plane_dist[1]); float d_offset = (csd.plane_dist[0] + csd.plane_dist[1]);
d_offset = min_ff(radius, d_offset); d_offset = min_ff(radius, d_offset);
@@ -1019,29 +943,21 @@ void SCULPT_do_clay_brush(Sculpt *sd, Object *ob, Span<PBVHNode *> nodes)
copy_v3_v3(area_co, ss->cache->location); copy_v3_v3(area_co, ss->cache->location);
add_v3_v3(area_co, temp); add_v3_v3(area_co, temp);
SculptThreadedTaskData data{}; threading::parallel_for(nodes.index_range(), 1, [&](const IndexRange range) {
data.sd = sd; for (const int i : range) {
data.ob = ob; do_clay_brush_task(ob, brush, area_no, area_co, nodes[i]);
data.brush = brush; }
data.nodes = nodes; });
data.area_no = area_no;
data.area_co = area_co;
TaskParallelSettings settings;
BKE_pbvh_parallel_range_settings(&settings, true, nodes.size());
BLI_task_parallel_range(0, nodes.size(), &data, do_clay_brush_task_cb_ex, &settings);
} }
static void do_clay_strips_brush_task_cb_ex(void *__restrict userdata, static void do_clay_strips_brush_task(Object *ob,
const int n, const Brush *brush,
const TaskParallelTLS *__restrict tls) const float (*mat)[4],
const float *area_no_sp,
const float *area_co,
PBVHNode *node)
{ {
SculptThreadedTaskData *data = static_cast<SculptThreadedTaskData *>(userdata); SculptSession *ss = ob->sculpt;
SculptSession *ss = data->ob->sculpt;
const Brush *brush = data->brush;
float(*mat)[4] = data->mat;
const float *area_no_sp = data->area_no_sp;
const float *area_co = data->area_co;
PBVHVertexIter vd; PBVHVertexIter vd;
SculptBrushTest test; SculptBrushTest test;
@@ -1049,17 +965,16 @@ static void do_clay_strips_brush_task_cb_ex(void *__restrict userdata,
const bool flip = (ss->cache->bstrength < 0.0f); const bool flip = (ss->cache->bstrength < 0.0f);
const float bstrength = flip ? -ss->cache->bstrength : ss->cache->bstrength; const float bstrength = flip ? -ss->cache->bstrength : ss->cache->bstrength;
proxy = BKE_pbvh_node_add_proxy(ss->pbvh, data->nodes[n])->co; proxy = BKE_pbvh_node_add_proxy(ss->pbvh, node)->co;
SCULPT_brush_test_init(ss, &test); SCULPT_brush_test_init(ss, &test);
plane_from_point_normal_v3(test.plane_tool, area_co, area_no_sp); plane_from_point_normal_v3(test.plane_tool, area_co, area_no_sp);
const int thread_id = BLI_task_parallel_thread_id(tls); const int thread_id = BLI_task_parallel_thread_id(nullptr);
AutomaskingNodeData automask_data; AutomaskingNodeData automask_data;
SCULPT_automasking_node_begin( SCULPT_automasking_node_begin(ob, ss, ss->cache->automasking, &automask_data, node);
data->ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]);
BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) {
if (!SCULPT_brush_test_cube(&test, vd.co, mat, brush->tip_roundness, brush->tip_scale_x)) { if (!SCULPT_brush_test_cube(&test, vd.co, mat, brush->tip_roundness, brush->tip_scale_x)) {
continue; continue;
} }
@@ -1102,6 +1017,7 @@ static void do_clay_strips_brush_task_cb_ex(void *__restrict userdata,
void SCULPT_do_clay_strips_brush(Sculpt *sd, Object *ob, Span<PBVHNode *> nodes) void SCULPT_do_clay_strips_brush(Sculpt *sd, Object *ob, Span<PBVHNode *> nodes)
{ {
using namespace blender;
SculptSession *ss = ob->sculpt; SculptSession *ss = ob->sculpt;
Brush *brush = BKE_paint_brush(&sd->paint); Brush *brush = BKE_paint_brush(&sd->paint);
@@ -1141,7 +1057,7 @@ void SCULPT_do_clay_strips_brush(Sculpt *sd, Object *ob, Span<PBVHNode *> nodes)
add_v3_v3(area_co, temp); add_v3_v3(area_co, temp);
/* Clay Strips uses a cube test with falloff in the XY axis (not in Z) and a plane to deform the /* Clay Strips uses a cube test with falloff in the XY axis (not in Z) and a plane to deform the
* vertices. When in Add mode, vertices that are below the plane and inside the cube are move * vertices. When in Add mode, vertices that are below the plane and inside the cube are moved
* towards the plane. In this situation, there may be cases where a vertex is outside the cube * towards the plane. In this situation, there may be cases where a vertex is outside the cube
* but below the plane, so won't be deformed, causing artifacts. In order to prevent these * but below the plane, so won't be deformed, causing artifacts. In order to prevent these
* artifacts, this displaces the test cube space in relation to the plane in order to * artifacts, this displaces the test cube space in relation to the plane in order to
@@ -1174,29 +1090,20 @@ void SCULPT_do_clay_strips_brush(Sculpt *sd, Object *ob, Span<PBVHNode *> nodes)
invert_m4_m4(mat, tmat); invert_m4_m4(mat, tmat);
SculptThreadedTaskData data{}; threading::parallel_for(nodes.index_range(), 1, [&](const IndexRange range) {
data.sd = sd; for (const int i : range) {
data.ob = ob; do_clay_strips_brush_task(ob, brush, mat, area_no_sp, area_co, nodes[i]);
data.brush = brush; }
data.nodes = nodes; });
data.area_no_sp = area_no_sp;
data.area_co = area_co;
data.mat = mat;
TaskParallelSettings settings;
BKE_pbvh_parallel_range_settings(&settings, true, nodes.size());
BLI_task_parallel_range(0, nodes.size(), &data, do_clay_strips_brush_task_cb_ex, &settings);
} }
static void do_snake_hook_brush_task_cb_ex(void *__restrict userdata, static void do_snake_hook_brush_task(Object *ob,
const int n, const Brush *brush,
const TaskParallelTLS *__restrict tls) SculptProjectVector *spvc,
const float *grab_delta,
PBVHNode *node)
{ {
SculptThreadedTaskData *data = static_cast<SculptThreadedTaskData *>(userdata); SculptSession *ss = ob->sculpt;
SculptSession *ss = data->ob->sculpt;
const Brush *brush = data->brush;
SculptProjectVector *spvc = data->spvc;
const float *grab_delta = data->grab_delta;
PBVHVertexIter vd; PBVHVertexIter vd;
float(*proxy)[3]; float(*proxy)[3];
@@ -1209,21 +1116,20 @@ static void do_snake_hook_brush_task_cb_ex(void *__restrict userdata,
const bool do_elastic = brush->snake_hook_deform_type == BRUSH_SNAKE_HOOK_DEFORM_ELASTIC; const bool do_elastic = brush->snake_hook_deform_type == BRUSH_SNAKE_HOOK_DEFORM_ELASTIC;
proxy = BKE_pbvh_node_add_proxy(ss->pbvh, data->nodes[n])->co; proxy = BKE_pbvh_node_add_proxy(ss->pbvh, node)->co;
SculptBrushTest test; SculptBrushTest test;
SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape( SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape(
ss, &test, data->brush->falloff_shape); ss, &test, brush->falloff_shape);
const int thread_id = BLI_task_parallel_thread_id(tls); const int thread_id = BLI_task_parallel_thread_id(nullptr);
KelvinletParams params; KelvinletParams params;
BKE_kelvinlet_init_params(&params, ss->cache->radius, bstrength, 1.0f, 0.4f); BKE_kelvinlet_init_params(&params, ss->cache->radius, bstrength, 1.0f, 0.4f);
AutomaskingNodeData automask_data; AutomaskingNodeData automask_data;
SCULPT_automasking_node_begin( SCULPT_automasking_node_begin(ob, ss, ss->cache->automasking, &automask_data, node);
data->ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]);
BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) {
if (!do_elastic && !sculpt_brush_test_sq_fn(&test, vd.co)) { if (!do_elastic && !sculpt_brush_test_sq_fn(&test, vd.co)) {
continue; continue;
} }
@@ -1306,6 +1212,7 @@ static void do_snake_hook_brush_task_cb_ex(void *__restrict userdata,
void SCULPT_do_snake_hook_brush(Sculpt *sd, Object *ob, Span<PBVHNode *> nodes) void SCULPT_do_snake_hook_brush(Sculpt *sd, Object *ob, Span<PBVHNode *> nodes)
{ {
using namespace blender;
SculptSession *ss = ob->sculpt; SculptSession *ss = ob->sculpt;
Brush *brush = BKE_paint_brush(&sd->paint); Brush *brush = BKE_paint_brush(&sd->paint);
const float bstrength = ss->cache->bstrength; const float bstrength = ss->cache->bstrength;
@@ -1328,47 +1235,35 @@ void SCULPT_do_snake_hook_brush(Sculpt *sd, Object *ob, Span<PBVHNode *> nodes)
sculpt_project_v3_cache_init(&spvc, grab_delta); sculpt_project_v3_cache_init(&spvc, grab_delta);
} }
SculptThreadedTaskData data{}; threading::parallel_for(nodes.index_range(), 1, [&](const IndexRange range) {
data.sd = sd; for (const int i : range) {
data.ob = ob; do_snake_hook_brush_task(ob, brush, &spvc, grab_delta, nodes[i]);
data.brush = brush; }
data.nodes = nodes; });
data.spvc = &spvc;
data.grab_delta = grab_delta;
TaskParallelSettings settings;
BKE_pbvh_parallel_range_settings(&settings, true, nodes.size());
BLI_task_parallel_range(0, nodes.size(), &data, do_snake_hook_brush_task_cb_ex, &settings);
} }
static void do_thumb_brush_task_cb_ex(void *__restrict userdata, static void do_thumb_brush_task(Object *ob, const Brush *brush, const float *cono, PBVHNode *node)
const int n,
const TaskParallelTLS *__restrict tls)
{ {
SculptThreadedTaskData *data = static_cast<SculptThreadedTaskData *>(userdata); SculptSession *ss = ob->sculpt;
SculptSession *ss = data->ob->sculpt;
const Brush *brush = data->brush;
const float *cono = data->cono;
PBVHVertexIter vd; PBVHVertexIter vd;
SculptOrigVertData orig_data; SculptOrigVertData orig_data;
float(*proxy)[3]; float(*proxy)[3];
const float bstrength = ss->cache->bstrength; const float bstrength = ss->cache->bstrength;
SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n], SCULPT_UNDO_COORDS); SCULPT_orig_vert_data_init(&orig_data, ob, node, SCULPT_UNDO_COORDS);
proxy = BKE_pbvh_node_add_proxy(ss->pbvh, data->nodes[n])->co; proxy = BKE_pbvh_node_add_proxy(ss->pbvh, node)->co;
SculptBrushTest test; SculptBrushTest test;
SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape( SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape(
ss, &test, data->brush->falloff_shape); ss, &test, brush->falloff_shape);
const int thread_id = BLI_task_parallel_thread_id(tls); const int thread_id = BLI_task_parallel_thread_id(nullptr);
AutomaskingNodeData automask_data; AutomaskingNodeData automask_data;
SCULPT_automasking_node_begin( SCULPT_automasking_node_begin(ob, ss, ss->cache->automasking, &automask_data, node);
data->ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]);
BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) {
SCULPT_orig_vert_data_update(&orig_data, &vd); SCULPT_orig_vert_data_update(&orig_data, &vd);
if (!sculpt_brush_test_sq_fn(&test, orig_data.co)) { if (!sculpt_brush_test_sq_fn(&test, orig_data.co)) {
@@ -1398,6 +1293,7 @@ static void do_thumb_brush_task_cb_ex(void *__restrict userdata,
void SCULPT_do_thumb_brush(Sculpt *sd, Object *ob, Span<PBVHNode *> nodes) void SCULPT_do_thumb_brush(Sculpt *sd, Object *ob, Span<PBVHNode *> nodes)
{ {
using namespace blender;
SculptSession *ss = ob->sculpt; SculptSession *ss = ob->sculpt;
Brush *brush = BKE_paint_brush(&sd->paint); Brush *brush = BKE_paint_brush(&sd->paint);
float grab_delta[3]; float grab_delta[3];
@@ -1408,46 +1304,35 @@ void SCULPT_do_thumb_brush(Sculpt *sd, Object *ob, Span<PBVHNode *> nodes)
cross_v3_v3v3(tmp, ss->cache->sculpt_normal_symm, grab_delta); cross_v3_v3v3(tmp, ss->cache->sculpt_normal_symm, grab_delta);
cross_v3_v3v3(cono, tmp, ss->cache->sculpt_normal_symm); cross_v3_v3v3(cono, tmp, ss->cache->sculpt_normal_symm);
SculptThreadedTaskData data{}; threading::parallel_for(nodes.index_range(), 1, [&](const IndexRange range) {
data.sd = sd; for (const int i : range) {
data.ob = ob; do_thumb_brush_task(ob, brush, cono, nodes[i]);
data.brush = brush; }
data.nodes = nodes; });
data.cono = cono;
TaskParallelSettings settings;
BKE_pbvh_parallel_range_settings(&settings, true, nodes.size());
BLI_task_parallel_range(0, nodes.size(), &data, do_thumb_brush_task_cb_ex, &settings);
} }
static void do_rotate_brush_task_cb_ex(void *__restrict userdata, static void do_rotate_brush_task(Object *ob, const Brush *brush, const float angle, PBVHNode *node)
const int n,
const TaskParallelTLS *__restrict tls)
{ {
SculptThreadedTaskData *data = static_cast<SculptThreadedTaskData *>(userdata); SculptSession *ss = ob->sculpt;
SculptSession *ss = data->ob->sculpt;
const Brush *brush = data->brush;
const float angle = data->angle;
PBVHVertexIter vd; PBVHVertexIter vd;
SculptOrigVertData orig_data; SculptOrigVertData orig_data;
float(*proxy)[3]; float(*proxy)[3];
const float bstrength = ss->cache->bstrength; const float bstrength = ss->cache->bstrength;
SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n], SCULPT_UNDO_COORDS); SCULPT_orig_vert_data_init(&orig_data, ob, node, SCULPT_UNDO_COORDS);
proxy = BKE_pbvh_node_add_proxy(ss->pbvh, data->nodes[n])->co; proxy = BKE_pbvh_node_add_proxy(ss->pbvh, node)->co;
SculptBrushTest test; SculptBrushTest test;
SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape( SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape(
ss, &test, data->brush->falloff_shape); ss, &test, brush->falloff_shape);
const int thread_id = BLI_task_parallel_thread_id(tls); const int thread_id = BLI_task_parallel_thread_id(nullptr);
AutomaskingNodeData automask_data; AutomaskingNodeData automask_data;
SCULPT_automasking_node_begin( SCULPT_automasking_node_begin(ob, ss, ss->cache->automasking, &automask_data, node);
data->ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]);
BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) {
SCULPT_orig_vert_data_update(&orig_data, &vd); SCULPT_orig_vert_data_update(&orig_data, &vd);
if (!sculpt_brush_test_sq_fn(&test, orig_data.co)) { if (!sculpt_brush_test_sq_fn(&test, orig_data.co)) {
@@ -1483,32 +1368,23 @@ static void do_rotate_brush_task_cb_ex(void *__restrict userdata,
void SCULPT_do_rotate_brush(Sculpt *sd, Object *ob, Span<PBVHNode *> nodes) void SCULPT_do_rotate_brush(Sculpt *sd, Object *ob, Span<PBVHNode *> nodes)
{ {
using namespace blender;
SculptSession *ss = ob->sculpt; SculptSession *ss = ob->sculpt;
Brush *brush = BKE_paint_brush(&sd->paint); Brush *brush = BKE_paint_brush(&sd->paint);
static const int flip[8] = {1, -1, -1, 1, -1, 1, 1, -1}; static const int flip[8] = {1, -1, -1, 1, -1, 1, 1, -1};
const float angle = ss->cache->vertex_rotation * flip[ss->cache->mirror_symmetry_pass]; const float angle = ss->cache->vertex_rotation * flip[ss->cache->mirror_symmetry_pass];
SculptThreadedTaskData data{}; threading::parallel_for(nodes.index_range(), 1, [&](const IndexRange range) {
data.sd = sd; for (const int i : range) {
data.ob = ob; do_rotate_brush_task(ob, brush, angle, nodes[i]);
data.brush = brush; }
data.nodes = nodes; });
data.angle = angle;
TaskParallelSettings settings;
BKE_pbvh_parallel_range_settings(&settings, true, nodes.size());
BLI_task_parallel_range(0, nodes.size(), &data, do_rotate_brush_task_cb_ex, &settings);
} }
static void do_layer_brush_task_cb_ex(void *__restrict userdata, static void do_layer_brush_task(Object *ob, Sculpt *sd, const Brush *brush, PBVHNode *node)
const int n,
const TaskParallelTLS *__restrict tls)
{ {
SculptThreadedTaskData *data = static_cast<SculptThreadedTaskData *>(userdata); SculptSession *ss = ob->sculpt;
SculptSession *ss = data->ob->sculpt;
Sculpt *sd = data->sd;
const Brush *brush = data->brush;
const bool use_persistent_base = !ss->bm && ss->attrs.persistent_co && const bool use_persistent_base = !ss->bm && ss->attrs.persistent_co &&
brush->flag & BRUSH_PERSISTENT; brush->flag & BRUSH_PERSISTENT;
@@ -1516,18 +1392,17 @@ static void do_layer_brush_task_cb_ex(void *__restrict userdata,
PBVHVertexIter vd; PBVHVertexIter vd;
SculptOrigVertData orig_data; SculptOrigVertData orig_data;
const float bstrength = ss->cache->bstrength; const float bstrength = ss->cache->bstrength;
SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n], SCULPT_UNDO_COORDS); SCULPT_orig_vert_data_init(&orig_data, ob, node, SCULPT_UNDO_COORDS);
SculptBrushTest test; SculptBrushTest test;
SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape( SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape(
ss, &test, data->brush->falloff_shape); ss, &test, brush->falloff_shape);
const int thread_id = BLI_task_parallel_thread_id(tls); const int thread_id = BLI_task_parallel_thread_id(nullptr);
AutomaskingNodeData automask_data; AutomaskingNodeData automask_data;
SCULPT_automasking_node_begin( SCULPT_automasking_node_begin(ob, ss, ss->cache->automasking, &automask_data, node);
data->ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]);
BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) {
SCULPT_orig_vert_data_update(&orig_data, &vd); SCULPT_orig_vert_data_update(&orig_data, &vd);
if (!sculpt_brush_test_sq_fn(&test, orig_data.co)) { if (!sculpt_brush_test_sq_fn(&test, orig_data.co)) {
@@ -1606,6 +1481,7 @@ static void do_layer_brush_task_cb_ex(void *__restrict userdata,
void SCULPT_do_layer_brush(Sculpt *sd, Object *ob, Span<PBVHNode *> nodes) void SCULPT_do_layer_brush(Sculpt *sd, Object *ob, Span<PBVHNode *> nodes)
{ {
using namespace blender;
SculptSession *ss = ob->sculpt; SculptSession *ss = ob->sculpt;
Brush *brush = BKE_paint_brush(&sd->paint); Brush *brush = BKE_paint_brush(&sd->paint);
@@ -1614,41 +1490,32 @@ void SCULPT_do_layer_brush(Sculpt *sd, Object *ob, Span<PBVHNode *> nodes)
__func__); __func__);
} }
SculptThreadedTaskData data{}; threading::parallel_for(nodes.index_range(), 1, [&](const IndexRange range) {
data.sd = sd; for (const int i : range) {
data.ob = ob; do_layer_brush_task(ob, sd, brush, nodes[i]);
data.brush = brush; }
data.nodes = nodes; });
TaskParallelSettings settings;
BKE_pbvh_parallel_range_settings(&settings, true, nodes.size());
BLI_task_parallel_range(0, nodes.size(), &data, do_layer_brush_task_cb_ex, &settings);
} }
static void do_inflate_brush_task_cb_ex(void *__restrict userdata, static void do_inflate_brush_task(Object *ob, const Brush *brush, PBVHNode *node)
const int n,
const TaskParallelTLS *__restrict tls)
{ {
SculptThreadedTaskData *data = static_cast<SculptThreadedTaskData *>(userdata); SculptSession *ss = ob->sculpt;
SculptSession *ss = data->ob->sculpt;
const Brush *brush = data->brush;
PBVHVertexIter vd; PBVHVertexIter vd;
float(*proxy)[3]; float(*proxy)[3];
const float bstrength = ss->cache->bstrength; const float bstrength = ss->cache->bstrength;
proxy = BKE_pbvh_node_add_proxy(ss->pbvh, data->nodes[n])->co; proxy = BKE_pbvh_node_add_proxy(ss->pbvh, node)->co;
SculptBrushTest test; SculptBrushTest test;
SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape( SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape(
ss, &test, data->brush->falloff_shape); ss, &test, brush->falloff_shape);
const int thread_id = BLI_task_parallel_thread_id(tls); const int thread_id = BLI_task_parallel_thread_id(nullptr);
AutomaskingNodeData automask_data; AutomaskingNodeData automask_data;
SCULPT_automasking_node_begin( SCULPT_automasking_node_begin(ob, ss, ss->cache->automasking, &automask_data, node);
data->ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]);
BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) {
if (!sculpt_brush_test_sq_fn(&test, vd.co)) { if (!sculpt_brush_test_sq_fn(&test, vd.co)) {
continue; continue;
} }
@@ -1685,44 +1552,35 @@ static void do_inflate_brush_task_cb_ex(void *__restrict userdata,
void SCULPT_do_inflate_brush(Sculpt *sd, Object *ob, Span<PBVHNode *> nodes) void SCULPT_do_inflate_brush(Sculpt *sd, Object *ob, Span<PBVHNode *> nodes)
{ {
using namespace blender;
Brush *brush = BKE_paint_brush(&sd->paint); Brush *brush = BKE_paint_brush(&sd->paint);
SculptThreadedTaskData data{}; threading::parallel_for(nodes.index_range(), 1, [&](const IndexRange range) {
data.sd = sd; for (const int i : range) {
data.ob = ob; do_inflate_brush_task(ob, brush, nodes[i]);
data.brush = brush; }
data.nodes = nodes; });
TaskParallelSettings settings;
BKE_pbvh_parallel_range_settings(&settings, true, nodes.size());
BLI_task_parallel_range(0, nodes.size(), &data, do_inflate_brush_task_cb_ex, &settings);
} }
static void do_nudge_brush_task_cb_ex(void *__restrict userdata, static void do_nudge_brush_task(Object *ob, const Brush *brush, const float *cono, PBVHNode *node)
const int n,
const TaskParallelTLS *__restrict tls)
{ {
SculptThreadedTaskData *data = static_cast<SculptThreadedTaskData *>(userdata); SculptSession *ss = ob->sculpt;
SculptSession *ss = data->ob->sculpt;
const Brush *brush = data->brush;
const float *cono = data->cono;
PBVHVertexIter vd; PBVHVertexIter vd;
float(*proxy)[3]; float(*proxy)[3];
const float bstrength = ss->cache->bstrength; const float bstrength = ss->cache->bstrength;
proxy = BKE_pbvh_node_add_proxy(ss->pbvh, data->nodes[n])->co; proxy = BKE_pbvh_node_add_proxy(ss->pbvh, node)->co;
SculptBrushTest test; SculptBrushTest test;
SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape( SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape(
ss, &test, data->brush->falloff_shape); ss, &test, brush->falloff_shape);
const int thread_id = BLI_task_parallel_thread_id(tls); const int thread_id = BLI_task_parallel_thread_id(nullptr);
AutomaskingNodeData automask_data; AutomaskingNodeData automask_data;
SCULPT_automasking_node_begin( SCULPT_automasking_node_begin(ob, ss, ss->cache->automasking, &automask_data, node);
data->ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]);
BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) {
if (!sculpt_brush_test_sq_fn(&test, vd.co)) { if (!sculpt_brush_test_sq_fn(&test, vd.co)) {
continue; continue;
} }
@@ -1750,6 +1608,7 @@ static void do_nudge_brush_task_cb_ex(void *__restrict userdata,
void SCULPT_do_nudge_brush(Sculpt *sd, Object *ob, Span<PBVHNode *> nodes) void SCULPT_do_nudge_brush(Sculpt *sd, Object *ob, Span<PBVHNode *> nodes)
{ {
using namespace blender;
SculptSession *ss = ob->sculpt; SculptSession *ss = ob->sculpt;
Brush *brush = BKE_paint_brush(&sd->paint); Brush *brush = BKE_paint_brush(&sd->paint);
float grab_delta[3]; float grab_delta[3];
@@ -1760,16 +1619,11 @@ void SCULPT_do_nudge_brush(Sculpt *sd, Object *ob, Span<PBVHNode *> nodes)
cross_v3_v3v3(tmp, ss->cache->sculpt_normal_symm, grab_delta); cross_v3_v3v3(tmp, ss->cache->sculpt_normal_symm, grab_delta);
cross_v3_v3v3(cono, tmp, ss->cache->sculpt_normal_symm); cross_v3_v3v3(cono, tmp, ss->cache->sculpt_normal_symm);
SculptThreadedTaskData data{}; threading::parallel_for(nodes.index_range(), 1, [&](const IndexRange range) {
data.sd = sd; for (const int i : range) {
data.ob = ob; do_nudge_brush_task(ob, brush, cono, nodes[i]);
data.brush = brush; }
data.nodes = nodes; });
data.cono = cono;
TaskParallelSettings settings;
BKE_pbvh_parallel_range_settings(&settings, true, nodes.size());
BLI_task_parallel_range(0, nodes.size(), &data, do_nudge_brush_task_cb_ex, &settings);
} }
/** \} */ /** \} */
@@ -1781,32 +1635,29 @@ void SCULPT_do_nudge_brush(Sculpt *sd, Object *ob, Span<PBVHNode *> nodes)
/** /**
* Used for 'SCULPT_TOOL_CREASE' and 'SCULPT_TOOL_BLOB' * Used for 'SCULPT_TOOL_CREASE' and 'SCULPT_TOOL_BLOB'
*/ */
static void do_crease_brush_task_cb_ex(void *__restrict userdata, static void do_crease_brush_task(Object *ob,
const int n, const Brush *brush,
const TaskParallelTLS *__restrict tls) SculptProjectVector *spvc,
const float flippedbstrength,
const float *offset,
PBVHNode *node)
{ {
SculptThreadedTaskData *data = static_cast<SculptThreadedTaskData *>(userdata); SculptSession *ss = ob->sculpt;
SculptSession *ss = data->ob->sculpt;
const Brush *brush = data->brush;
SculptProjectVector *spvc = data->spvc;
const float flippedbstrength = data->flippedbstrength;
const float *offset = data->offset;
PBVHVertexIter vd; PBVHVertexIter vd;
float(*proxy)[3]; float(*proxy)[3];
proxy = BKE_pbvh_node_add_proxy(ss->pbvh, data->nodes[n])->co; proxy = BKE_pbvh_node_add_proxy(ss->pbvh, node)->co;
SculptBrushTest test; SculptBrushTest test;
SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape( SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape(
ss, &test, data->brush->falloff_shape); ss, &test, brush->falloff_shape);
const int thread_id = BLI_task_parallel_thread_id(tls); const int thread_id = BLI_task_parallel_thread_id(nullptr);
AutomaskingNodeData automask_data; AutomaskingNodeData automask_data;
SCULPT_automasking_node_begin( SCULPT_automasking_node_begin(ob, ss, ss->cache->automasking, &automask_data, node);
data->ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]);
BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) {
if (!sculpt_brush_test_sq_fn(&test, vd.co)) { if (!sculpt_brush_test_sq_fn(&test, vd.co)) {
continue; continue;
} }
@@ -1850,6 +1701,7 @@ static void do_crease_brush_task_cb_ex(void *__restrict userdata,
void SCULPT_do_crease_brush(Sculpt *sd, Object *ob, Span<PBVHNode *> nodes) void SCULPT_do_crease_brush(Sculpt *sd, Object *ob, Span<PBVHNode *> nodes)
{ {
using namespace blender;
SculptSession *ss = ob->sculpt; SculptSession *ss = ob->sculpt;
const Scene *scene = ss->cache->vc->scene; const Scene *scene = ss->cache->vc->scene;
Brush *brush = BKE_paint_brush(&sd->paint); Brush *brush = BKE_paint_brush(&sd->paint);
@@ -1885,40 +1737,30 @@ void SCULPT_do_crease_brush(Sculpt *sd, Object *ob, Span<PBVHNode *> nodes)
* point. Without this we get a 'flat' surface surrounding the pinch. */ * point. Without this we get a 'flat' surface surrounding the pinch. */
sculpt_project_v3_cache_init(&spvc, ss->cache->sculpt_normal_symm); sculpt_project_v3_cache_init(&spvc, ss->cache->sculpt_normal_symm);
/* Threaded loop over nodes. */ threading::parallel_for(nodes.index_range(), 1, [&](const IndexRange range) {
SculptThreadedTaskData data{}; for (const int i : range) {
data.sd = sd; do_crease_brush_task(ob, brush, &spvc, flippedbstrength, offset, nodes[i]);
data.ob = ob; }
data.brush = brush; });
data.nodes = nodes;
data.spvc = &spvc;
data.offset = offset;
data.flippedbstrength = flippedbstrength;
TaskParallelSettings settings;
BKE_pbvh_parallel_range_settings(&settings, true, nodes.size());
BLI_task_parallel_range(0, nodes.size(), &data, do_crease_brush_task_cb_ex, &settings);
} }
static void do_pinch_brush_task_cb_ex(void *__restrict userdata, static void do_pinch_brush_task(Object *ob,
const int n, const Brush *brush,
const TaskParallelTLS *__restrict tls) const float (*stroke_xz)[3],
PBVHNode *node)
{ {
SculptThreadedTaskData *data = static_cast<SculptThreadedTaskData *>(userdata); SculptSession *ss = ob->sculpt;
SculptSession *ss = data->ob->sculpt;
const Brush *brush = data->brush;
float(*stroke_xz)[3] = data->stroke_xz;
PBVHVertexIter vd; PBVHVertexIter vd;
float(*proxy)[3]; float(*proxy)[3];
const float bstrength = ss->cache->bstrength; const float bstrength = ss->cache->bstrength;
proxy = BKE_pbvh_node_add_proxy(ss->pbvh, data->nodes[n])->co; proxy = BKE_pbvh_node_add_proxy(ss->pbvh, node)->co;
SculptBrushTest test; SculptBrushTest test;
SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape( SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape(
ss, &test, data->brush->falloff_shape); ss, &test, brush->falloff_shape);
const int thread_id = BLI_task_parallel_thread_id(tls); const int thread_id = BLI_task_parallel_thread_id(nullptr);
float x_object_space[3]; float x_object_space[3];
float z_object_space[3]; float z_object_space[3];
@@ -1926,10 +1768,9 @@ static void do_pinch_brush_task_cb_ex(void *__restrict userdata,
copy_v3_v3(z_object_space, stroke_xz[1]); copy_v3_v3(z_object_space, stroke_xz[1]);
AutomaskingNodeData automask_data; AutomaskingNodeData automask_data;
SCULPT_automasking_node_begin( SCULPT_automasking_node_begin(ob, ss, ss->cache->automasking, &automask_data, node);
data->ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]);
BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) {
if (!sculpt_brush_test_sq_fn(&test, vd.co)) { if (!sculpt_brush_test_sq_fn(&test, vd.co)) {
continue; continue;
} }
@@ -1975,6 +1816,7 @@ static void do_pinch_brush_task_cb_ex(void *__restrict userdata,
void SCULPT_do_pinch_brush(Sculpt *sd, Object *ob, Span<PBVHNode *> nodes) void SCULPT_do_pinch_brush(Sculpt *sd, Object *ob, Span<PBVHNode *> nodes)
{ {
using namespace blender;
SculptSession *ss = ob->sculpt; SculptSession *ss = ob->sculpt;
Brush *brush = BKE_paint_brush(&sd->paint); Brush *brush = BKE_paint_brush(&sd->paint);
@@ -2008,48 +1850,40 @@ void SCULPT_do_pinch_brush(Sculpt *sd, Object *ob, Span<PBVHNode *> nodes)
normalize_v3_v3(stroke_xz[0], mat[0]); normalize_v3_v3(stroke_xz[0], mat[0]);
normalize_v3_v3(stroke_xz[1], mat[2]); normalize_v3_v3(stroke_xz[1], mat[2]);
SculptThreadedTaskData data{}; threading::parallel_for(nodes.index_range(), 1, [&](const IndexRange range) {
data.sd = sd; for (const int i : range) {
data.ob = ob; do_pinch_brush_task(ob, brush, stroke_xz, nodes[i]);
data.brush = brush; }
data.nodes = nodes; });
data.stroke_xz = stroke_xz;
TaskParallelSettings settings;
BKE_pbvh_parallel_range_settings(&settings, true, nodes.size());
BLI_task_parallel_range(0, nodes.size(), &data, do_pinch_brush_task_cb_ex, &settings);
} }
static void do_grab_brush_task_cb_ex(void *__restrict userdata, static void do_grab_brush_task(Object *ob,
const int n, const Brush *brush,
const TaskParallelTLS *__restrict tls) const float *grab_delta,
PBVHNode *node)
{ {
SculptThreadedTaskData *data = static_cast<SculptThreadedTaskData *>(userdata); SculptSession *ss = ob->sculpt;
SculptSession *ss = data->ob->sculpt;
const Brush *brush = data->brush;
const float *grab_delta = data->grab_delta;
PBVHVertexIter vd; PBVHVertexIter vd;
SculptOrigVertData orig_data; SculptOrigVertData orig_data;
float(*proxy)[3]; float(*proxy)[3];
const float bstrength = ss->cache->bstrength; const float bstrength = ss->cache->bstrength;
SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n], SCULPT_UNDO_COORDS); SCULPT_orig_vert_data_init(&orig_data, ob, node, SCULPT_UNDO_COORDS);
proxy = BKE_pbvh_node_add_proxy(ss->pbvh, data->nodes[n])->co; proxy = BKE_pbvh_node_add_proxy(ss->pbvh, node)->co;
SculptBrushTest test; SculptBrushTest test;
SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape( SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape(
ss, &test, data->brush->falloff_shape); ss, &test, brush->falloff_shape);
const int thread_id = BLI_task_parallel_thread_id(tls); const int thread_id = BLI_task_parallel_thread_id(nullptr);
const bool grab_silhouette = brush->flag2 & BRUSH_GRAB_SILHOUETTE; const bool grab_silhouette = brush->flag2 & BRUSH_GRAB_SILHOUETTE;
AutomaskingNodeData automask_data; AutomaskingNodeData automask_data;
SCULPT_automasking_node_begin( SCULPT_automasking_node_begin(ob, ss, ss->cache->automasking, &automask_data, node);
data->ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]);
BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) {
SCULPT_orig_vert_data_update(&orig_data, &vd); SCULPT_orig_vert_data_update(&orig_data, &vd);
if (!sculpt_brush_test_sq_fn(&test, orig_data.co)) { if (!sculpt_brush_test_sq_fn(&test, orig_data.co)) {
@@ -2090,6 +1924,7 @@ static void do_grab_brush_task_cb_ex(void *__restrict userdata,
void SCULPT_do_grab_brush(Sculpt *sd, Object *ob, Span<PBVHNode *> nodes) void SCULPT_do_grab_brush(Sculpt *sd, Object *ob, Span<PBVHNode *> nodes)
{ {
using namespace blender;
SculptSession *ss = ob->sculpt; SculptSession *ss = ob->sculpt;
Brush *brush = BKE_paint_brush(&sd->paint); Brush *brush = BKE_paint_brush(&sd->paint);
float grab_delta[3]; float grab_delta[3];
@@ -2100,26 +1935,19 @@ void SCULPT_do_grab_brush(Sculpt *sd, Object *ob, Span<PBVHNode *> nodes)
sculpt_project_v3_normal_align(ss, ss->cache->normal_weight, grab_delta); sculpt_project_v3_normal_align(ss, ss->cache->normal_weight, grab_delta);
} }
SculptThreadedTaskData data{}; threading::parallel_for(nodes.index_range(), 1, [&](const IndexRange range) {
data.sd = sd; for (const int i : range) {
data.ob = ob; do_grab_brush_task(ob, brush, grab_delta, nodes[i]);
data.brush = brush; }
data.nodes = nodes; });
data.grab_delta = grab_delta;
TaskParallelSettings settings;
BKE_pbvh_parallel_range_settings(&settings, true, nodes.size());
BLI_task_parallel_range(0, nodes.size(), &data, do_grab_brush_task_cb_ex, &settings);
} }
static void do_elastic_deform_brush_task_cb_ex(void *__restrict userdata, static void do_elastic_deform_brush_task(Object *ob,
const int n, const Brush *brush,
const TaskParallelTLS *__restrict /*tls*/) const float *grab_delta,
PBVHNode *node)
{ {
SculptThreadedTaskData *data = static_cast<SculptThreadedTaskData *>(userdata); SculptSession *ss = ob->sculpt;
SculptSession *ss = data->ob->sculpt;
const Brush *brush = data->brush;
const float *grab_delta = data->grab_delta;
const float *location = ss->cache->location; const float *location = ss->cache->location;
PBVHVertexIter vd; PBVHVertexIter vd;
@@ -2128,12 +1956,11 @@ static void do_elastic_deform_brush_task_cb_ex(void *__restrict userdata,
const float bstrength = ss->cache->bstrength; const float bstrength = ss->cache->bstrength;
SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n], SCULPT_UNDO_COORDS); SCULPT_orig_vert_data_init(&orig_data, ob, node, SCULPT_UNDO_COORDS);
AutomaskingNodeData automask_data; AutomaskingNodeData automask_data;
SCULPT_automasking_node_begin( SCULPT_automasking_node_begin(ob, ss, ss->cache->automasking, &automask_data, node);
data->ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]);
proxy = BKE_pbvh_node_add_proxy(ss->pbvh, data->nodes[n])->co; proxy = BKE_pbvh_node_add_proxy(ss->pbvh, node)->co;
float dir; float dir;
if (ss->cache->mouse[0] > ss->cache->initial_mouse[0]) { if (ss->cache->mouse[0] > ss->cache->initial_mouse[0]) {
@@ -2155,7 +1982,7 @@ static void do_elastic_deform_brush_task_cb_ex(void *__restrict userdata,
BKE_kelvinlet_init_params( BKE_kelvinlet_init_params(
&params, ss->cache->radius, force, 1.0f, brush->elastic_deform_volume_preservation); &params, ss->cache->radius, force, 1.0f, brush->elastic_deform_volume_preservation);
BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) {
SCULPT_orig_vert_data_update(&orig_data, &vd); SCULPT_orig_vert_data_update(&orig_data, &vd);
SCULPT_automasking_node_update(ss, &automask_data, &vd); SCULPT_automasking_node_update(ss, &automask_data, &vd);
@@ -2204,6 +2031,7 @@ static void do_elastic_deform_brush_task_cb_ex(void *__restrict userdata,
void SCULPT_do_elastic_deform_brush(Sculpt *sd, Object *ob, Span<PBVHNode *> nodes) void SCULPT_do_elastic_deform_brush(Sculpt *sd, Object *ob, Span<PBVHNode *> nodes)
{ {
using namespace blender;
SculptSession *ss = ob->sculpt; SculptSession *ss = ob->sculpt;
Brush *brush = BKE_paint_brush(&sd->paint); Brush *brush = BKE_paint_brush(&sd->paint);
float grab_delta[3]; float grab_delta[3];
@@ -2214,16 +2042,11 @@ void SCULPT_do_elastic_deform_brush(Sculpt *sd, Object *ob, Span<PBVHNode *> nod
sculpt_project_v3_normal_align(ss, ss->cache->normal_weight, grab_delta); sculpt_project_v3_normal_align(ss, ss->cache->normal_weight, grab_delta);
} }
SculptThreadedTaskData data{}; threading::parallel_for(nodes.index_range(), 1, [&](const IndexRange range) {
data.sd = sd; for (const int i : range) {
data.ob = ob; do_elastic_deform_brush_task(ob, brush, grab_delta, nodes[i]);
data.brush = brush; }
data.nodes = nodes; });
data.grab_delta = grab_delta;
TaskParallelSettings settings;
BKE_pbvh_parallel_range_settings(&settings, true, nodes.size());
BLI_task_parallel_range(0, nodes.size(), &data, do_elastic_deform_brush_task_cb_ex, &settings);
} }
/** \} */ /** \} */
@@ -2232,33 +2055,30 @@ void SCULPT_do_elastic_deform_brush(Sculpt *sd, Object *ob, Span<PBVHNode *> nod
/** \name Sculpt Draw Sharp Brush /** \name Sculpt Draw Sharp Brush
* \{ */ * \{ */
static void do_draw_sharp_brush_task_cb_ex(void *__restrict userdata, static void do_draw_sharp_brush_task(Object *ob,
const int n, const Brush *brush,
const TaskParallelTLS *__restrict tls) const float *offset,
PBVHNode *node)
{ {
SculptThreadedTaskData *data = static_cast<SculptThreadedTaskData *>(userdata); SculptSession *ss = ob->sculpt;
SculptSession *ss = data->ob->sculpt;
const Brush *brush = data->brush;
const float *offset = data->offset;
PBVHVertexIter vd; PBVHVertexIter vd;
SculptOrigVertData orig_data; SculptOrigVertData orig_data;
float(*proxy)[3]; float(*proxy)[3];
SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n], SCULPT_UNDO_COORDS); SCULPT_orig_vert_data_init(&orig_data, ob, node, SCULPT_UNDO_COORDS);
proxy = BKE_pbvh_node_add_proxy(ss->pbvh, data->nodes[n])->co; proxy = BKE_pbvh_node_add_proxy(ss->pbvh, node)->co;
SculptBrushTest test; SculptBrushTest test;
SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape( SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape(
ss, &test, data->brush->falloff_shape); ss, &test, brush->falloff_shape);
const int thread_id = BLI_task_parallel_thread_id(tls); const int thread_id = BLI_task_parallel_thread_id(nullptr);
AutomaskingNodeData automask_data; AutomaskingNodeData automask_data;
SCULPT_automasking_node_begin( SCULPT_automasking_node_begin(ob, ss, ss->cache->automasking, &automask_data, node);
data->ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]);
BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) {
SCULPT_orig_vert_data_update(&orig_data, &vd); SCULPT_orig_vert_data_update(&orig_data, &vd);
if (!sculpt_brush_test_sq_fn(&test, orig_data.co)) { if (!sculpt_brush_test_sq_fn(&test, orig_data.co)) {
continue; continue;
@@ -2288,6 +2108,7 @@ static void do_draw_sharp_brush_task_cb_ex(void *__restrict userdata,
void SCULPT_do_draw_sharp_brush(Sculpt *sd, Object *ob, Span<PBVHNode *> nodes) void SCULPT_do_draw_sharp_brush(Sculpt *sd, Object *ob, Span<PBVHNode *> nodes)
{ {
using namespace blender;
SculptSession *ss = ob->sculpt; SculptSession *ss = ob->sculpt;
Brush *brush = BKE_paint_brush(&sd->paint); Brush *brush = BKE_paint_brush(&sd->paint);
float offset[3]; float offset[3];
@@ -2304,17 +2125,11 @@ void SCULPT_do_draw_sharp_brush(Sculpt *sd, Object *ob, Span<PBVHNode *> nodes)
* initialize before threads so they can do curve mapping. */ * initialize before threads so they can do curve mapping. */
BKE_curvemapping_init(brush->curve); BKE_curvemapping_init(brush->curve);
/* Threaded loop over nodes. */ threading::parallel_for(nodes.index_range(), 1, [&](const IndexRange range) {
SculptThreadedTaskData data{}; for (const int i : range) {
data.sd = sd; do_draw_sharp_brush_task(ob, brush, offset, nodes[i]);
data.ob = ob; }
data.brush = brush; });
data.nodes = nodes;
data.offset = offset;
TaskParallelSettings settings;
BKE_pbvh_parallel_range_settings(&settings, true, nodes.size());
BLI_task_parallel_range(0, nodes.size(), &data, do_draw_sharp_brush_task_cb_ex, &settings);
} }
/** \} */ /** \} */
@@ -2323,32 +2138,27 @@ void SCULPT_do_draw_sharp_brush(Sculpt *sd, Object *ob, Span<PBVHNode *> nodes)
/** \name Sculpt Topology Brush /** \name Sculpt Topology Brush
* \{ */ * \{ */
static void do_topology_slide_task_cb_ex(void *__restrict userdata, static void do_topology_slide_task(Object *ob, const Brush *brush, PBVHNode *node)
const int n,
const TaskParallelTLS *__restrict tls)
{ {
SculptThreadedTaskData *data = static_cast<SculptThreadedTaskData *>(userdata); SculptSession *ss = ob->sculpt;
SculptSession *ss = data->ob->sculpt;
const Brush *brush = data->brush;
PBVHVertexIter vd; PBVHVertexIter vd;
SculptOrigVertData orig_data; SculptOrigVertData orig_data;
float(*proxy)[3]; float(*proxy)[3];
SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n], SCULPT_UNDO_COORDS); SCULPT_orig_vert_data_init(&orig_data, ob, node, SCULPT_UNDO_COORDS);
proxy = BKE_pbvh_node_add_proxy(ss->pbvh, data->nodes[n])->co; proxy = BKE_pbvh_node_add_proxy(ss->pbvh, node)->co;
SculptBrushTest test; SculptBrushTest test;
SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape( SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape(
ss, &test, data->brush->falloff_shape); ss, &test, brush->falloff_shape);
const int thread_id = BLI_task_parallel_thread_id(tls); const int thread_id = BLI_task_parallel_thread_id(nullptr);
AutomaskingNodeData automask_data; AutomaskingNodeData automask_data;
SCULPT_automasking_node_begin( SCULPT_automasking_node_begin(ob, ss, ss->cache->automasking, &automask_data, node);
data->ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]);
BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) {
SCULPT_orig_vert_data_update(&orig_data, &vd); SCULPT_orig_vert_data_update(&orig_data, &vd);
if (!sculpt_brush_test_sq_fn(&test, orig_data.co)) { if (!sculpt_brush_test_sq_fn(&test, orig_data.co)) {
continue; continue;
@@ -2488,32 +2298,27 @@ void SCULPT_relax_vertex(SculptSession *ss,
add_v3_v3v3(r_final_pos, vd->co, final_disp); add_v3_v3v3(r_final_pos, vd->co, final_disp);
} }
static void do_topology_relax_task_cb_ex(void *__restrict userdata, static void do_topology_relax_task(Object *ob, const Brush *brush, PBVHNode *node)
const int n,
const TaskParallelTLS *__restrict tls)
{ {
SculptThreadedTaskData *data = static_cast<SculptThreadedTaskData *>(userdata); SculptSession *ss = ob->sculpt;
SculptSession *ss = data->ob->sculpt;
const Brush *brush = data->brush;
const float bstrength = ss->cache->bstrength; const float bstrength = ss->cache->bstrength;
PBVHVertexIter vd; PBVHVertexIter vd;
SculptOrigVertData orig_data; SculptOrigVertData orig_data;
SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n], SCULPT_UNDO_COORDS); SCULPT_orig_vert_data_init(&orig_data, ob, node, SCULPT_UNDO_COORDS);
BKE_pbvh_node_add_proxy(ss->pbvh, data->nodes[n]); BKE_pbvh_node_add_proxy(ss->pbvh, node);
SculptBrushTest test; SculptBrushTest test;
SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape( SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape(
ss, &test, data->brush->falloff_shape); ss, &test, brush->falloff_shape);
const int thread_id = BLI_task_parallel_thread_id(tls); const int thread_id = BLI_task_parallel_thread_id(nullptr);
AutomaskingNodeData automask_data; AutomaskingNodeData automask_data;
SCULPT_automasking_node_begin( SCULPT_automasking_node_begin(ob, ss, ss->cache->automasking, &automask_data, node);
data->ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]);
BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) {
SCULPT_orig_vert_data_update(&orig_data, &vd); SCULPT_orig_vert_data_update(&orig_data, &vd);
if (!sculpt_brush_test_sq_fn(&test, orig_data.co)) { if (!sculpt_brush_test_sq_fn(&test, orig_data.co)) {
continue; continue;
@@ -2541,6 +2346,7 @@ static void do_topology_relax_task_cb_ex(void *__restrict userdata,
void SCULPT_do_slide_relax_brush(Sculpt *sd, Object *ob, Span<PBVHNode *> nodes) void SCULPT_do_slide_relax_brush(Sculpt *sd, Object *ob, Span<PBVHNode *> nodes)
{ {
using namespace blender;
SculptSession *ss = ob->sculpt; SculptSession *ss = ob->sculpt;
Brush *brush = BKE_paint_brush(&sd->paint); Brush *brush = BKE_paint_brush(&sd->paint);
@@ -2550,22 +2356,24 @@ void SCULPT_do_slide_relax_brush(Sculpt *sd, Object *ob, Span<PBVHNode *> nodes)
BKE_curvemapping_init(brush->curve); BKE_curvemapping_init(brush->curve);
SculptThreadedTaskData data{};
data.sd = sd;
data.ob = ob;
data.brush = brush;
data.nodes = nodes;
TaskParallelSettings settings; TaskParallelSettings settings;
BKE_pbvh_parallel_range_settings(&settings, true, nodes.size()); BKE_pbvh_parallel_range_settings(&settings, true, nodes.size());
if (ss->cache->alt_smooth) { if (ss->cache->alt_smooth) {
SCULPT_boundary_info_ensure(ob); SCULPT_boundary_info_ensure(ob);
for (int i = 0; i < 4; i++) { for (int i = 0; i < 4; i++) {
BLI_task_parallel_range(0, nodes.size(), &data, do_topology_relax_task_cb_ex, &settings); threading::parallel_for(nodes.index_range(), 1, [&](const IndexRange range) {
for (const int i : range) {
do_topology_relax_task(ob, brush, nodes[i]);
}
});
} }
} }
else { else {
BLI_task_parallel_range(0, nodes.size(), &data, do_topology_slide_task_cb_ex, &settings); threading::parallel_for(nodes.index_range(), 1, [&](const IndexRange range) {
for (const int i : range) {
do_topology_slide_task(ob, brush, nodes[i]);
}
});
} }
} }
@@ -2575,28 +2383,23 @@ void SCULPT_do_slide_relax_brush(Sculpt *sd, Object *ob, Span<PBVHNode *> nodes)
/** \name Sculpt Multires Displacement Eraser Brush /** \name Sculpt Multires Displacement Eraser Brush
* \{ */ * \{ */
static void do_displacement_eraser_brush_task_cb_ex(void *__restrict userdata, static void do_displacement_eraser_brush_task(Object *ob, const Brush *brush, PBVHNode *node)
const int n,
const TaskParallelTLS *__restrict tls)
{ {
SculptThreadedTaskData *data = static_cast<SculptThreadedTaskData *>(userdata); SculptSession *ss = ob->sculpt;
SculptSession *ss = data->ob->sculpt;
const Brush *brush = data->brush;
const float bstrength = clamp_f(ss->cache->bstrength, 0.0f, 1.0f); const float bstrength = clamp_f(ss->cache->bstrength, 0.0f, 1.0f);
float(*proxy)[3] = BKE_pbvh_node_add_proxy(ss->pbvh, data->nodes[n])->co; float(*proxy)[3] = BKE_pbvh_node_add_proxy(ss->pbvh, node)->co;
SculptBrushTest test; SculptBrushTest test;
SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape( SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape(
ss, &test, data->brush->falloff_shape); ss, &test, brush->falloff_shape);
const int thread_id = BLI_task_parallel_thread_id(tls); const int thread_id = BLI_task_parallel_thread_id(nullptr);
AutomaskingNodeData automask_data; AutomaskingNodeData automask_data;
SCULPT_automasking_node_begin( SCULPT_automasking_node_begin(ob, ss, ss->cache->automasking, &automask_data, node);
data->ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]);
PBVHVertexIter vd; PBVHVertexIter vd;
BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) {
if (!sculpt_brush_test_sq_fn(&test, vd.co)) { if (!sculpt_brush_test_sq_fn(&test, vd.co)) {
continue; continue;
} }
@@ -2628,20 +2431,15 @@ static void do_displacement_eraser_brush_task_cb_ex(void *__restrict userdata,
void SCULPT_do_displacement_eraser_brush(Sculpt *sd, Object *ob, Span<PBVHNode *> nodes) void SCULPT_do_displacement_eraser_brush(Sculpt *sd, Object *ob, Span<PBVHNode *> nodes)
{ {
using namespace blender;
Brush *brush = BKE_paint_brush(&sd->paint); Brush *brush = BKE_paint_brush(&sd->paint);
BKE_curvemapping_init(brush->curve); BKE_curvemapping_init(brush->curve);
/* Threaded loop over nodes. */ threading::parallel_for(nodes.index_range(), 1, [&](const IndexRange range) {
SculptThreadedTaskData data{}; for (const int i : range) {
data.sd = sd; do_displacement_eraser_brush_task(ob, brush, nodes[i]);
data.ob = ob; }
data.brush = brush; });
data.nodes = nodes;
TaskParallelSettings settings;
BKE_pbvh_parallel_range_settings(&settings, true, nodes.size());
BLI_task_parallel_range(
0, nodes.size(), &data, do_displacement_eraser_brush_task_cb_ex, &settings);
} }
/** \} */ /** \} */
@@ -2650,26 +2448,21 @@ void SCULPT_do_displacement_eraser_brush(Sculpt *sd, Object *ob, Span<PBVHNode *
/** \name Sculpt Multires Displacement Smear Brush /** \name Sculpt Multires Displacement Smear Brush
* \{ */ * \{ */
static void do_displacement_smear_brush_task_cb_ex(void *__restrict userdata, static void do_displacement_smear_brush_task(Object *ob, const Brush *brush, PBVHNode *node)
const int n,
const TaskParallelTLS *__restrict tls)
{ {
SculptThreadedTaskData *data = static_cast<SculptThreadedTaskData *>(userdata); SculptSession *ss = ob->sculpt;
SculptSession *ss = data->ob->sculpt;
const Brush *brush = data->brush;
const float bstrength = clamp_f(ss->cache->bstrength, 0.0f, 1.0f); const float bstrength = clamp_f(ss->cache->bstrength, 0.0f, 1.0f);
SculptBrushTest test; SculptBrushTest test;
SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape( SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape(
ss, &test, data->brush->falloff_shape); ss, &test, brush->falloff_shape);
const int thread_id = BLI_task_parallel_thread_id(tls); const int thread_id = BLI_task_parallel_thread_id(nullptr);
AutomaskingNodeData automask_data; AutomaskingNodeData automask_data;
SCULPT_automasking_node_begin( SCULPT_automasking_node_begin(ob, ss, ss->cache->automasking, &automask_data, node);
data->ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]);
PBVHVertexIter vd; PBVHVertexIter vd;
BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) {
if (!sculpt_brush_test_sq_fn(&test, vd.co)) { if (!sculpt_brush_test_sq_fn(&test, vd.co)) {
continue; continue;
} }
@@ -2745,14 +2538,10 @@ static void do_displacement_smear_brush_task_cb_ex(void *__restrict userdata,
BKE_pbvh_vertex_iter_end; BKE_pbvh_vertex_iter_end;
} }
static void do_displacement_smear_store_prev_disp_task_cb_ex( static void do_displacement_smear_store_prev_disp_task(SculptSession *ss, PBVHNode *node)
void *__restrict userdata, const int n, const TaskParallelTLS *__restrict /*tls*/)
{ {
SculptThreadedTaskData *data = static_cast<SculptThreadedTaskData *>(userdata);
SculptSession *ss = data->ob->sculpt;
PBVHVertexIter vd; PBVHVertexIter vd;
BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) {
sub_v3_v3v3(ss->cache->prev_displacement[vd.index], sub_v3_v3v3(ss->cache->prev_displacement[vd.index],
SCULPT_vertex_co_get(ss, vd.vertex), SCULPT_vertex_co_get(ss, vd.vertex),
ss->cache->limit_surface_co[vd.index]); ss->cache->limit_surface_co[vd.index]);
@@ -2762,6 +2551,7 @@ static void do_displacement_smear_store_prev_disp_task_cb_ex(
void SCULPT_do_displacement_smear_brush(Sculpt *sd, Object *ob, Span<PBVHNode *> nodes) void SCULPT_do_displacement_smear_brush(Sculpt *sd, Object *ob, Span<PBVHNode *> nodes)
{ {
using namespace blender;
Brush *brush = BKE_paint_brush(&sd->paint); Brush *brush = BKE_paint_brush(&sd->paint);
SculptSession *ss = ob->sculpt; SculptSession *ss = ob->sculpt;
@@ -2782,19 +2572,17 @@ void SCULPT_do_displacement_smear_brush(Sculpt *sd, Object *ob, Span<PBVHNode *>
ss->cache->limit_surface_co[i]); ss->cache->limit_surface_co[i]);
} }
} }
/* Threaded loop over nodes. */
SculptThreadedTaskData data{};
data.sd = sd;
data.ob = ob;
data.brush = brush;
data.nodes = nodes;
TaskParallelSettings settings; threading::parallel_for(nodes.index_range(), 1, [&](const IndexRange range) {
BKE_pbvh_parallel_range_settings(&settings, true, nodes.size()); for (const int i : range) {
BLI_task_parallel_range( do_displacement_smear_store_prev_disp_task(ss, nodes[i]);
0, nodes.size(), &data, do_displacement_smear_store_prev_disp_task_cb_ex, &settings); }
BLI_task_parallel_range( });
0, nodes.size(), &data, do_displacement_smear_brush_task_cb_ex, &settings); threading::parallel_for(nodes.index_range(), 1, [&](const IndexRange range) {
for (const int i : range) {
do_displacement_smear_brush_task(ob, brush, nodes[i]);
}
});
} }
/** \} */ /** \} */
@@ -2803,14 +2591,10 @@ void SCULPT_do_displacement_smear_brush(Sculpt *sd, Object *ob, Span<PBVHNode *>
/** \name Sculpt Topology Rake (Shared Utility) /** \name Sculpt Topology Rake (Shared Utility)
* \{ */ * \{ */
static void do_topology_rake_bmesh_task_cb_ex(void *__restrict userdata, static void do_topology_rake_bmesh_task(
const int n, Object *ob, Sculpt *sd, const Brush *brush, const float strength, PBVHNode *node)
const TaskParallelTLS *__restrict tls)
{ {
SculptThreadedTaskData *data = static_cast<SculptThreadedTaskData *>(userdata); SculptSession *ss = ob->sculpt;
SculptSession *ss = data->ob->sculpt;
Sculpt *sd = data->sd;
const Brush *brush = data->brush;
float direction[3]; float direction[3];
copy_v3_v3(direction, ss->cache->grab_delta_symmetry); copy_v3_v3(direction, ss->cache->grab_delta_symmetry);
@@ -2826,19 +2610,18 @@ static void do_topology_rake_bmesh_task_cb_ex(void *__restrict userdata,
return; return;
} }
const float bstrength = clamp_f(data->strength, 0.0f, 1.0f); const float bstrength = clamp_f(strength, 0.0f, 1.0f);
SculptBrushTest test; SculptBrushTest test;
SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape( SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape(
ss, &test, data->brush->falloff_shape); ss, &test, brush->falloff_shape);
const int thread_id = BLI_task_parallel_thread_id(tls); const int thread_id = BLI_task_parallel_thread_id(nullptr);
AutomaskingNodeData automask_data; AutomaskingNodeData automask_data;
SCULPT_automasking_node_begin( SCULPT_automasking_node_begin(ob, ss, ss->cache->automasking, &automask_data, node);
data->ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]);
PBVHVertexIter vd; PBVHVertexIter vd;
BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) {
if (!sculpt_brush_test_sq_fn(&test, vd.co)) { if (!sculpt_brush_test_sq_fn(&test, vd.co)) {
continue; continue;
} }
@@ -2876,6 +2659,7 @@ static void do_topology_rake_bmesh_task_cb_ex(void *__restrict userdata,
void SCULPT_bmesh_topology_rake(Sculpt *sd, Object *ob, Span<PBVHNode *> nodes, float bstrength) void SCULPT_bmesh_topology_rake(Sculpt *sd, Object *ob, Span<PBVHNode *> nodes, float bstrength)
{ {
using namespace blender;
Brush *brush = BKE_paint_brush(&sd->paint); Brush *brush = BKE_paint_brush(&sd->paint);
const float strength = clamp_f(bstrength, 0.0f, 1.0f); const float strength = clamp_f(bstrength, 0.0f, 1.0f);
@@ -2887,18 +2671,11 @@ void SCULPT_bmesh_topology_rake(Sculpt *sd, Object *ob, Span<PBVHNode *> nodes,
const float factor = iterations * strength / count; const float factor = iterations * strength / count;
for (iteration = 0; iteration <= count; iteration++) { for (iteration = 0; iteration <= count; iteration++) {
threading::parallel_for(nodes.index_range(), 1, [&](const IndexRange range) {
SculptThreadedTaskData data{}; for (const int i : range) {
data.sd = sd; do_topology_rake_bmesh_task(ob, sd, brush, factor, nodes[i]);
data.ob = ob; }
data.brush = brush; });
data.nodes = nodes;
data.strength = factor;
TaskParallelSettings settings;
BKE_pbvh_parallel_range_settings(&settings, true, nodes.size());
BLI_task_parallel_range(0, nodes.size(), &data, do_topology_rake_bmesh_task_cb_ex, &settings);
} }
} }
@@ -2908,27 +2685,22 @@ void SCULPT_bmesh_topology_rake(Sculpt *sd, Object *ob, Span<PBVHNode *> nodes,
/** \name Sculpt Mask Brush /** \name Sculpt Mask Brush
* \{ */ * \{ */
static void do_mask_brush_draw_task_cb_ex(void *__restrict userdata, static void do_mask_brush_draw_task(Object *ob, const Brush *brush, PBVHNode *node)
const int n,
const TaskParallelTLS *__restrict tls)
{ {
SculptThreadedTaskData *data = static_cast<SculptThreadedTaskData *>(userdata); SculptSession *ss = ob->sculpt;
SculptSession *ss = data->ob->sculpt;
const Brush *brush = data->brush;
const float bstrength = ss->cache->bstrength; const float bstrength = ss->cache->bstrength;
PBVHVertexIter vd; PBVHVertexIter vd;
SculptBrushTest test; SculptBrushTest test;
SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape( SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape(
ss, &test, data->brush->falloff_shape); ss, &test, brush->falloff_shape);
const int thread_id = BLI_task_parallel_thread_id(tls); const int thread_id = BLI_task_parallel_thread_id(nullptr);
AutomaskingNodeData automask_data; AutomaskingNodeData automask_data;
SCULPT_automasking_node_begin( SCULPT_automasking_node_begin(ob, ss, ss->cache->automasking, &automask_data, node);
data->ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]);
BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) {
if (!sculpt_brush_test_sq_fn(&test, vd.co)) { if (!sculpt_brush_test_sq_fn(&test, vd.co)) {
continue; continue;
} }
@@ -2958,18 +2730,13 @@ static void do_mask_brush_draw_task_cb_ex(void *__restrict userdata,
void SCULPT_do_mask_brush_draw(Sculpt *sd, Object *ob, Span<PBVHNode *> nodes) void SCULPT_do_mask_brush_draw(Sculpt *sd, Object *ob, Span<PBVHNode *> nodes)
{ {
Brush *brush = BKE_paint_brush(&sd->paint); using namespace blender;
const Brush *brush = BKE_paint_brush(&sd->paint);
/* Threaded loop over nodes. */ threading::parallel_for(nodes.index_range(), 1, [&](const IndexRange range) {
SculptThreadedTaskData data{}; for (const int i : range) {
data.sd = sd; do_mask_brush_draw_task(ob, brush, nodes[i]);
data.ob = ob; }
data.brush = brush; });
data.nodes = nodes;
TaskParallelSettings settings;
BKE_pbvh_parallel_range_settings(&settings, true, nodes.size());
BLI_task_parallel_range(0, nodes.size(), &data, do_mask_brush_draw_task_cb_ex, &settings);
} }
void SCULPT_do_mask_brush(Sculpt *sd, Object *ob, Span<PBVHNode *> nodes) void SCULPT_do_mask_brush(Sculpt *sd, Object *ob, Span<PBVHNode *> nodes)

View File

@@ -291,17 +291,17 @@ static void cloth_brush_add_deformation_constraint(SculptClothSimulation *cloth_
cloth_brush_reallocate_constraints(cloth_sim); cloth_brush_reallocate_constraints(cloth_sim);
} }
static void do_cloth_brush_build_constraints_task_cb_ex(void *__restrict userdata, static void do_cloth_brush_build_constraints_task(Object *ob,
const int n, const Brush *brush,
const TaskParallelTLS *__restrict /*tls*/) SculptClothSimulation *cloth_sim,
float *cloth_sim_initial_location,
float cloth_sim_radius,
PBVHNode *node)
{ {
SculptThreadedTaskData *data = static_cast<SculptThreadedTaskData *>(userdata); SculptSession *ss = ob->sculpt;
SculptSession *ss = data->ob->sculpt;
const Brush *brush = data->brush;
PBVHNode *node = data->nodes[n];
const int node_index = POINTER_AS_INT(BLI_ghash_lookup(data->cloth_sim->node_state_index, node)); const int node_index = POINTER_AS_INT(BLI_ghash_lookup(cloth_sim->node_state_index, node));
if (data->cloth_sim->node_state[node_index] != SCULPT_CLOTH_NODE_UNINITIALIZED) { if (cloth_sim->node_state[node_index] != SCULPT_CLOTH_NODE_UNINITIALIZED) {
/* The simulation already contains constraints for this node. */ /* The simulation already contains constraints for this node. */
return; return;
} }
@@ -330,11 +330,11 @@ static void do_cloth_brush_build_constraints_task_cb_ex(void *__restrict userdat
/* Only limit the constraint creation to a radius when the simulation is local. */ /* Only limit the constraint creation to a radius when the simulation is local. */
const float cloth_sim_radius_squared = brush->cloth_simulation_area_type == const float cloth_sim_radius_squared = brush->cloth_simulation_area_type ==
BRUSH_CLOTH_SIMULATION_AREA_LOCAL ? BRUSH_CLOTH_SIMULATION_AREA_LOCAL ?
data->cloth_sim_radius * data->cloth_sim_radius : cloth_sim_radius * cloth_sim_radius :
FLT_MAX; FLT_MAX;
BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) { BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) {
const float len_squared = len_squared_v3v3(vd.co, data->cloth_sim_initial_location); const float len_squared = len_squared_v3v3(vd.co, cloth_sim_initial_location);
if (len_squared < cloth_sim_radius_squared) { if (len_squared < cloth_sim_radius_squared) {
SculptVertexNeighborIter ni; SculptVertexNeighborIter ni;
@@ -348,8 +348,8 @@ static void do_cloth_brush_build_constraints_task_cb_ex(void *__restrict userdat
} }
SCULPT_VERTEX_NEIGHBORS_ITER_END(ni); SCULPT_VERTEX_NEIGHBORS_ITER_END(ni);
if (data->cloth_sim->softbody_strength > 0.0f) { if (cloth_sim->softbody_strength > 0.0f) {
cloth_brush_add_softbody_constraint(data->cloth_sim, node_index, vd.index, 1.0f); cloth_brush_add_softbody_constraint(cloth_sim, node_index, vd.index, 1.0f);
} }
/* As we don't know the order of the neighbor vertices, we create all possible combinations /* As we don't know the order of the neighbor vertices, we create all possible combinations
@@ -360,14 +360,10 @@ static void do_cloth_brush_build_constraints_task_cb_ex(void *__restrict userdat
for (int c_i = 0; c_i < tot_indices; c_i++) { for (int c_i = 0; c_i < tot_indices; c_i++) {
for (int c_j = 0; c_j < tot_indices; c_j++) { for (int c_j = 0; c_j < tot_indices; c_j++) {
if (c_i != c_j && !cloth_brush_sim_has_length_constraint( if (c_i != c_j && !cloth_brush_sim_has_length_constraint(
data->cloth_sim, build_indices[c_i], build_indices[c_j])) cloth_sim, build_indices[c_i], build_indices[c_j]))
{ {
cloth_brush_add_length_constraint(ss, cloth_brush_add_length_constraint(
data->cloth_sim, ss, cloth_sim, node_index, build_indices[c_i], build_indices[c_j], use_persistent);
node_index,
build_indices[c_i],
build_indices[c_j],
use_persistent);
} }
} }
} }
@@ -381,7 +377,7 @@ static void do_cloth_brush_build_constraints_task_cb_ex(void *__restrict userdat
/* With plane falloff the strength of the constraints is set when applying the /* With plane falloff the strength of the constraints is set when applying the
* deformation forces. */ * deformation forces. */
cloth_brush_add_deformation_constraint( cloth_brush_add_deformation_constraint(
data->cloth_sim, node_index, vd.index, CLOTH_DEFORMATION_GRAB_STRENGTH); cloth_sim, node_index, vd.index, CLOTH_DEFORMATION_GRAB_STRENGTH);
} }
else if (len_squared < radius_squared) { else if (len_squared < radius_squared) {
/* With radial falloff deformation constraints are created with different strengths and /* With radial falloff deformation constraints are created with different strengths and
@@ -389,22 +385,22 @@ static void do_cloth_brush_build_constraints_task_cb_ex(void *__restrict userdat
const float fade = BKE_brush_curve_strength( const float fade = BKE_brush_curve_strength(
brush, sqrtf(len_squared), ss->cache->radius); brush, sqrtf(len_squared), ss->cache->radius);
cloth_brush_add_deformation_constraint( cloth_brush_add_deformation_constraint(
data->cloth_sim, node_index, vd.index, fade * CLOTH_DEFORMATION_GRAB_STRENGTH); cloth_sim, node_index, vd.index, fade * CLOTH_DEFORMATION_GRAB_STRENGTH);
} }
} }
else if (brush->cloth_deform_type == BRUSH_CLOTH_DEFORM_SNAKE_HOOK) { else if (brush->cloth_deform_type == BRUSH_CLOTH_DEFORM_SNAKE_HOOK) {
/* Cloth Snake Hook creates deformation constraint with fixed strength because the strength /* Cloth Snake Hook creates deformation constraint with fixed strength because the strength
* is controlled per iteration using cloth_sim->deformation_strength. */ * is controlled per iteration using cloth_sim->deformation_strength. */
cloth_brush_add_deformation_constraint( cloth_brush_add_deformation_constraint(
data->cloth_sim, node_index, vd.index, CLOTH_DEFORMATION_SNAKEHOOK_STRENGTH); cloth_sim, node_index, vd.index, CLOTH_DEFORMATION_SNAKEHOOK_STRENGTH);
} }
} }
else if (data->cloth_sim->deformation_pos) { else if (cloth_sim->deformation_pos) {
/* Any other tool that target the cloth simulation handle the falloff in /* Any other tool that target the cloth simulation handle the falloff in
* their own code when modifying the deformation coordinates of the simulation, so * their own code when modifying the deformation coordinates of the simulation, so
* deformation constraints are created with a fixed strength for all vertices. */ * deformation constraints are created with a fixed strength for all vertices. */
cloth_brush_add_deformation_constraint( cloth_brush_add_deformation_constraint(
data->cloth_sim, node_index, vd.index, CLOTH_DEFORMATION_TARGET_STRENGTH); cloth_sim, node_index, vd.index, CLOTH_DEFORMATION_TARGET_STRENGTH);
} }
if (pin_simulation_boundary) { if (pin_simulation_boundary) {
@@ -414,7 +410,7 @@ static void do_cloth_brush_build_constraints_task_cb_ex(void *__restrict userdat
if (sim_falloff < 1.0f) { if (sim_falloff < 1.0f) {
/* Create constraints with more strength the closer the vertex is to the simulation /* Create constraints with more strength the closer the vertex is to the simulation
* boundary. */ * boundary. */
cloth_brush_add_pin_constraint(data->cloth_sim, node_index, vd.index, 1.0f - sim_falloff); cloth_brush_add_pin_constraint(cloth_sim, node_index, vd.index, 1.0f - sim_falloff);
} }
} }
} }
@@ -429,17 +425,17 @@ static void cloth_brush_apply_force_to_vertex(SculptSession * /*ss*/,
madd_v3_v3fl(cloth_sim->acceleration[vertex_index], force, 1.0f / cloth_sim->mass); madd_v3_v3fl(cloth_sim->acceleration[vertex_index], force, 1.0f / cloth_sim->mass);
} }
static void do_cloth_brush_apply_forces_task_cb_ex(void *__restrict userdata, static void do_cloth_brush_apply_forces_task(Object *ob,
const int n, const Sculpt *sd,
const TaskParallelTLS *__restrict tls) const Brush *brush,
const float *offset,
const float *grab_delta,
float (*imat)[4],
float *area_co,
PBVHNode *node)
{ {
SculptThreadedTaskData *data = static_cast<SculptThreadedTaskData *>(userdata); SculptSession *ss = ob->sculpt;
SculptSession *ss = data->ob->sculpt;
const Brush *brush = data->brush;
SculptClothSimulation *cloth_sim = ss->cache->cloth_sim; SculptClothSimulation *cloth_sim = ss->cache->cloth_sim;
const float *offset = data->offset;
const float *grab_delta = data->grab_delta;
float(*imat)[4] = data->mat;
const bool use_falloff_plane = brush->cloth_force_falloff_type == const bool use_falloff_plane = brush->cloth_force_falloff_type ==
BRUSH_CLOTH_FORCE_FALLOFF_PLANE; BRUSH_CLOTH_FORCE_FALLOFF_PLANE;
@@ -449,8 +445,8 @@ static void do_cloth_brush_apply_forces_task_cb_ex(void *__restrict userdata,
SculptBrushTest test; SculptBrushTest test;
SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape( SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape(
ss, &test, data->brush->falloff_shape); ss, &test, brush->falloff_shape);
const int thread_id = BLI_task_parallel_thread_id(tls); const int thread_id = BLI_task_parallel_thread_id(nullptr);
/* For Pinch Perpendicular Deform Type. */ /* For Pinch Perpendicular Deform Type. */
float x_object_space[3]; float x_object_space[3];
@@ -465,20 +461,20 @@ static void do_cloth_brush_apply_forces_task_cb_ex(void *__restrict userdata,
float plane_normal[3]; float plane_normal[3];
if (use_falloff_plane) { if (use_falloff_plane) {
normalize_v3_v3(plane_normal, grab_delta); normalize_v3_v3(plane_normal, grab_delta);
plane_from_point_normal_v3(deform_plane, data->area_co, plane_normal); plane_from_point_normal_v3(deform_plane, area_co, plane_normal);
} }
/* Gravity */ /* Gravity */
float gravity[3] = {0.0f}; float gravity[3] = {0.0f};
if (ss->cache->supports_gravity) { if (ss->cache->supports_gravity) {
madd_v3_v3fl(gravity, ss->cache->gravity_direction, -data->sd->gravity_factor); madd_v3_v3fl(gravity, ss->cache->gravity_direction, -sd->gravity_factor);
} }
AutomaskingNodeData automask_data; AutomaskingNodeData automask_data;
SCULPT_automasking_node_begin( SCULPT_automasking_node_begin(
data->ob, ss, SCULPT_automasking_active_cache_get(ss), &automask_data, data->nodes[n]); ob, ss, SCULPT_automasking_active_cache_get(ss), &automask_data, node);
BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) {
SCULPT_automasking_node_update(ss, &automask_data, &vd); SCULPT_automasking_node_update(ss, &automask_data, &vd);
float force[3]; float force[3];
@@ -722,30 +718,27 @@ static void cloth_brush_solve_collision(Object *object,
} }
} }
static void do_cloth_brush_solve_simulation_task_cb_ex(void *__restrict userdata, static void do_cloth_brush_solve_simulation_task(Object *ob,
const int n, const Brush *brush,
const TaskParallelTLS *__restrict /*tls*/) SculptClothSimulation *cloth_sim,
const float time_step,
PBVHNode *node)
{ {
SculptThreadedTaskData *data = static_cast<SculptThreadedTaskData *>(userdata); SculptSession *ss = ob->sculpt;
SculptSession *ss = data->ob->sculpt;
const Brush *brush = data->brush;
PBVHNode *node = data->nodes[n];
PBVHVertexIter vd; PBVHVertexIter vd;
SculptClothSimulation *cloth_sim = data->cloth_sim;
const float time_step = data->cloth_time_step;
const int node_index = POINTER_AS_INT(BLI_ghash_lookup(data->cloth_sim->node_state_index, node)); const int node_index = POINTER_AS_INT(BLI_ghash_lookup(cloth_sim->node_state_index, node));
if (data->cloth_sim->node_state[node_index] != SCULPT_CLOTH_NODE_ACTIVE) { if (cloth_sim->node_state[node_index] != SCULPT_CLOTH_NODE_ACTIVE) {
return; return;
} }
AutomaskingCache *automasking = SCULPT_automasking_active_cache_get(ss); AutomaskingCache *automasking = SCULPT_automasking_active_cache_get(ss);
AutomaskingNodeData automask_data; AutomaskingNodeData automask_data;
SCULPT_automasking_node_begin( SCULPT_automasking_node_begin(
data->ob, ss, SCULPT_automasking_active_cache_get(ss), &automask_data, data->nodes[n]); ob, ss, SCULPT_automasking_active_cache_get(ss), &automask_data, node);
BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) {
SCULPT_automasking_node_update(ss, &automask_data, &vd); SCULPT_automasking_node_update(ss, &automask_data, &vd);
float sim_location[3]; float sim_location[3];
@@ -775,7 +768,7 @@ static void do_cloth_brush_solve_simulation_task_cb_ex(void *__restrict userdata
madd_v3_v3fl(cloth_sim->pos[i], cloth_sim->acceleration[i], mask_v); madd_v3_v3fl(cloth_sim->pos[i], cloth_sim->acceleration[i], mask_v);
if (cloth_sim->collider_list != nullptr) { if (cloth_sim->collider_list != nullptr) {
cloth_brush_solve_collision(data->ob, cloth_sim, i); cloth_brush_solve_collision(ob, cloth_sim, i);
} }
copy_v3_v3(cloth_sim->last_iteration_pos[i], cloth_sim->pos[i]); copy_v3_v3(cloth_sim->last_iteration_pos[i], cloth_sim->pos[i]);
@@ -908,32 +901,24 @@ void SCULPT_cloth_brush_do_simulation_step(Sculpt *sd,
SculptClothSimulation *cloth_sim, SculptClothSimulation *cloth_sim,
Span<PBVHNode *> nodes) Span<PBVHNode *> nodes)
{ {
using namespace blender;
SculptSession *ss = ob->sculpt; SculptSession *ss = ob->sculpt;
Brush *brush = BKE_paint_brush(&sd->paint); Brush *brush = BKE_paint_brush(&sd->paint);
/* Update the constraints. */ /* Update the constraints. */
cloth_brush_satisfy_constraints(ss, brush, cloth_sim); cloth_brush_satisfy_constraints(ss, brush, cloth_sim);
/* Solve the simulation and write the final step to the mesh. */ threading::parallel_for(nodes.index_range(), 1, [&](const IndexRange range) {
SculptThreadedTaskData solve_simulation_data{}; for (const int i : range) {
solve_simulation_data.sd = sd; do_cloth_brush_solve_simulation_task(
solve_simulation_data.ob = ob; ob, brush, cloth_sim, CLOTH_SIMULATION_TIME_STEP, nodes[i]);
solve_simulation_data.brush = brush; }
solve_simulation_data.nodes = nodes; });
solve_simulation_data.cloth_time_step = CLOTH_SIMULATION_TIME_STEP;
solve_simulation_data.cloth_sim = cloth_sim;
TaskParallelSettings settings;
BKE_pbvh_parallel_range_settings(&settings, true, nodes.size());
BLI_task_parallel_range(0,
nodes.size(),
&solve_simulation_data,
do_cloth_brush_solve_simulation_task_cb_ex,
&settings);
} }
static void cloth_brush_apply_brush_foces(Sculpt *sd, Object *ob, Span<PBVHNode *> nodes) static void cloth_brush_apply_brush_foces(Sculpt *sd, Object *ob, Span<PBVHNode *> nodes)
{ {
using namespace blender;
SculptSession *ss = ob->sculpt; SculptSession *ss = ob->sculpt;
Brush *brush = BKE_paint_brush(&sd->paint); Brush *brush = BKE_paint_brush(&sd->paint);
@@ -941,26 +926,14 @@ static void cloth_brush_apply_brush_foces(Sculpt *sd, Object *ob, Span<PBVHNode
float mat[4][4]; float mat[4][4];
float area_no[3]; float area_no[3];
float area_co[3]; float area_co[3];
float imat[4][4];
float offset[3]; float offset[3];
SculptThreadedTaskData apply_forces_data{};
apply_forces_data.sd = sd;
apply_forces_data.ob = ob;
apply_forces_data.brush = brush;
apply_forces_data.nodes = nodes;
apply_forces_data.area_no = area_no;
apply_forces_data.area_co = area_co;
apply_forces_data.mat = imat;
BKE_curvemapping_init(brush->curve); BKE_curvemapping_init(brush->curve);
/* Initialize the grab delta. */ /* Initialize the grab delta. */
copy_v3_v3(grab_delta, ss->cache->grab_delta_symmetry); copy_v3_v3(grab_delta, ss->cache->grab_delta_symmetry);
normalize_v3(grab_delta); normalize_v3(grab_delta);
apply_forces_data.grab_delta = grab_delta;
if (is_zero_v3(ss->cache->grab_delta_symmetry)) { if (is_zero_v3(ss->cache->grab_delta_symmetry)) {
return; return;
} }
@@ -971,8 +944,6 @@ static void cloth_brush_apply_brush_foces(Sculpt *sd, Object *ob, Span<PBVHNode
mul_v3_v3fl(offset, ss->cache->sculpt_normal_symm, ss->cache->radius); mul_v3_v3fl(offset, ss->cache->sculpt_normal_symm, ss->cache->radius);
mul_v3_v3(offset, ss->cache->scale); mul_v3_v3(offset, ss->cache->scale);
mul_v3_fl(offset, 2.0f); mul_v3_fl(offset, 2.0f);
apply_forces_data.offset = offset;
} }
if (brush->cloth_deform_type == BRUSH_CLOTH_DEFORM_PINCH_PERPENDICULAR || if (brush->cloth_deform_type == BRUSH_CLOTH_DEFORM_PINCH_PERPENDICULAR ||
@@ -991,10 +962,6 @@ static void cloth_brush_apply_brush_foces(Sculpt *sd, Object *ob, Span<PBVHNode
mat[3][3] = 1.0f; mat[3][3] = 1.0f;
normalize_m4(mat); normalize_m4(mat);
apply_forces_data.area_co = area_co;
apply_forces_data.area_no = area_no;
apply_forces_data.mat = mat;
/* Update matrix for the cursor preview. */ /* Update matrix for the cursor preview. */
if (ss->cache->mirror_symmetry_pass == 0) { if (ss->cache->mirror_symmetry_pass == 0) {
copy_m4_m4(ss->cache->stroke_local_mat, mat); copy_m4_m4(ss->cache->stroke_local_mat, mat);
@@ -1010,10 +977,11 @@ static void cloth_brush_apply_brush_foces(Sculpt *sd, Object *ob, Span<PBVHNode
} }
} }
TaskParallelSettings settings; threading::parallel_for(nodes.index_range(), 1, [&](const IndexRange range) {
BKE_pbvh_parallel_range_settings(&settings, true, nodes.size()); for (const int i : range) {
BLI_task_parallel_range( do_cloth_brush_apply_forces_task(ob, sd, brush, offset, grab_delta, mat, area_co, nodes[i]);
0, nodes.size(), &apply_forces_data, do_cloth_brush_apply_forces_task_cb_ex, &settings); }
});
} }
/* Allocates nodes state and initializes them to Uninitialized, so constraints can be created for /* Allocates nodes state and initializes them to Uninitialized, so constraints can be created for
@@ -1089,31 +1057,20 @@ void SCULPT_cloth_brush_ensure_nodes_constraints(
float initial_location[3], float initial_location[3],
const float radius) const float radius)
{ {
using namespace blender;
Brush *brush = BKE_paint_brush(&sd->paint); Brush *brush = BKE_paint_brush(&sd->paint);
/* TODO: Multi-threaded needs to be disabled for this task until implementing the optimization of /* TODO: Multi-threaded needs to be disabled for this task until implementing the optimization of
* storing the constraints per node. */ * storing the constraints per node. */
/* Currently all constrains are added to the same global array which can't be accessed from /* Currently all constrains are added to the same global array which can't be accessed from
* different threads. */ * different threads. */
TaskParallelSettings settings;
BKE_pbvh_parallel_range_settings(&settings, false, nodes.size());
cloth_sim->created_length_constraints = BLI_edgeset_new("created length constraints"); cloth_sim->created_length_constraints = BLI_edgeset_new("created length constraints");
SculptThreadedTaskData build_constraints_data{}; for (const int i : nodes.index_range()) {
build_constraints_data.sd = sd; do_cloth_brush_build_constraints_task(
build_constraints_data.ob = ob; ob, brush, cloth_sim, initial_location, radius, nodes[i]);
build_constraints_data.brush = brush; }
build_constraints_data.nodes = nodes;
build_constraints_data.cloth_sim = cloth_sim;
build_constraints_data.cloth_sim_initial_location = initial_location;
build_constraints_data.cloth_sim_radius = radius;
BLI_task_parallel_range(0,
nodes.size(),
&build_constraints_data,
do_cloth_brush_build_constraints_task_cb_ex,
&settings);
BLI_edgeset_free(cloth_sim->created_length_constraints); BLI_edgeset_free(cloth_sim->created_length_constraints);
} }
@@ -1399,18 +1356,16 @@ static void cloth_filter_apply_forces_to_vertices(const int v_index,
cloth_brush_apply_force_to_vertex(nullptr, filter_cache->cloth_sim, final_force, v_index); cloth_brush_apply_force_to_vertex(nullptr, filter_cache->cloth_sim, final_force, v_index);
} }
static void cloth_filter_apply_forces_task_cb(void *__restrict userdata, static void cloth_filter_apply_forces_task(Object *ob,
const int i, Sculpt *sd,
const TaskParallelTLS *__restrict /*tls*/) const eSculptClothFilterType filter_type,
const float filter_strength,
PBVHNode *node)
{ {
SculptThreadedTaskData *data = static_cast<SculptThreadedTaskData *>(userdata); SculptSession *ss = ob->sculpt;
Sculpt *sd = data->sd;
SculptSession *ss = data->ob->sculpt;
PBVHNode *node = data->nodes[i];
SculptClothSimulation *cloth_sim = ss->filter_cache->cloth_sim; SculptClothSimulation *cloth_sim = ss->filter_cache->cloth_sim;
const eSculptClothFilterType filter_type = eSculptClothFilterType(data->filter_type);
const bool is_deformation_filter = cloth_filter_is_deformation_filter(filter_type); const bool is_deformation_filter = cloth_filter_is_deformation_filter(filter_type);
float sculpt_gravity[3] = {0.0f}; float sculpt_gravity[3] = {0.0f};
@@ -1420,10 +1375,10 @@ static void cloth_filter_apply_forces_task_cb(void *__restrict userdata,
else { else {
sculpt_gravity[2] = -1.0f; sculpt_gravity[2] = -1.0f;
} }
mul_v3_fl(sculpt_gravity, sd->gravity_factor * data->filter_strength); mul_v3_fl(sculpt_gravity, sd->gravity_factor * filter_strength);
AutomaskingNodeData automask_data; AutomaskingNodeData automask_data;
SCULPT_automasking_node_begin( SCULPT_automasking_node_begin(
data->ob, ss, SCULPT_automasking_active_cache_get(ss), &automask_data, node); ob, ss, SCULPT_automasking_active_cache_get(ss), &automask_data, node);
PBVHVertexIter vd; PBVHVertexIter vd;
BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) { BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) {
@@ -1447,30 +1402,30 @@ static void cloth_filter_apply_forces_task_cb(void *__restrict userdata,
if (ss->filter_cache->orientation == SCULPT_FILTER_ORIENTATION_VIEW) { if (ss->filter_cache->orientation == SCULPT_FILTER_ORIENTATION_VIEW) {
/* When using the view orientation apply gravity in the -Y axis, this way objects will /* When using the view orientation apply gravity in the -Y axis, this way objects will
* fall down instead of backwards. */ * fall down instead of backwards. */
force[1] = -data->filter_strength * fade; force[1] = -filter_strength * fade;
} }
else { else {
force[2] = -data->filter_strength * fade; force[2] = -filter_strength * fade;
} }
SCULPT_filter_to_object_space(force, ss->filter_cache); SCULPT_filter_to_object_space(force, ss->filter_cache);
break; break;
case CLOTH_FILTER_INFLATE: { case CLOTH_FILTER_INFLATE: {
float normal[3]; float normal[3];
SCULPT_vertex_normal_get(ss, vd.vertex, normal); SCULPT_vertex_normal_get(ss, vd.vertex, normal);
mul_v3_v3fl(force, normal, fade * data->filter_strength); mul_v3_v3fl(force, normal, fade * filter_strength);
} break; } break;
case CLOTH_FILTER_EXPAND: case CLOTH_FILTER_EXPAND:
cloth_sim->length_constraint_tweak[vd.index] += fade * data->filter_strength * 0.01f; cloth_sim->length_constraint_tweak[vd.index] += fade * filter_strength * 0.01f;
zero_v3(force); zero_v3(force);
break; break;
case CLOTH_FILTER_PINCH: case CLOTH_FILTER_PINCH:
sub_v3_v3v3(force, ss->filter_cache->cloth_sim_pinch_point, vd.co); sub_v3_v3v3(force, ss->filter_cache->cloth_sim_pinch_point, vd.co);
normalize_v3(force); normalize_v3(force);
mul_v3_fl(force, fade * data->filter_strength); mul_v3_fl(force, fade * filter_strength);
break; break;
case CLOTH_FILTER_SCALE: case CLOTH_FILTER_SCALE:
unit_m3(transform); unit_m3(transform);
scale_m3_fl(transform, 1.0f + (fade * data->filter_strength)); scale_m3_fl(transform, 1.0f + (fade * filter_strength));
copy_v3_v3(temp, cloth_sim->init_pos[vd.index]); copy_v3_v3(temp, cloth_sim->init_pos[vd.index]);
mul_m3_v3(transform, temp); mul_m3_v3(transform, temp);
sub_v3_v3v3(disp, temp, cloth_sim->init_pos[vd.index]); sub_v3_v3v3(disp, temp, cloth_sim->init_pos[vd.index]);
@@ -1493,6 +1448,7 @@ static void cloth_filter_apply_forces_task_cb(void *__restrict userdata,
static int sculpt_cloth_filter_modal(bContext *C, wmOperator *op, const wmEvent *event) static int sculpt_cloth_filter_modal(bContext *C, wmOperator *op, const wmEvent *event)
{ {
using namespace blender;
Object *ob = CTX_data_active_object(C); Object *ob = CTX_data_active_object(C);
Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C); Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C);
SculptSession *ss = ob->sculpt; SculptSession *ss = ob->sculpt;
@@ -1526,17 +1482,15 @@ static int sculpt_cloth_filter_modal(bContext *C, wmOperator *op, const wmEvent
copy_v3_v3(ss->filter_cache->cloth_sim->pos[i], SCULPT_vertex_co_get(ss, vertex)); copy_v3_v3(ss->filter_cache->cloth_sim->pos[i], SCULPT_vertex_co_get(ss, vertex));
} }
SculptThreadedTaskData data{}; threading::parallel_for(ss->filter_cache->nodes.index_range(), 1, [&](const IndexRange range) {
data.sd = sd; for (const int i : range) {
data.ob = ob; cloth_filter_apply_forces_task(ob,
data.nodes = ss->filter_cache->nodes; sd,
data.filter_type = filter_type; eSculptClothFilterType(filter_type),
data.filter_strength = filter_strength; filter_strength,
ss->filter_cache->nodes[i]);
TaskParallelSettings settings; }
BKE_pbvh_parallel_range_settings(&settings, true, ss->filter_cache->nodes.size()); });
BLI_task_parallel_range(
0, ss->filter_cache->nodes.size(), &data, cloth_filter_apply_forces_task_cb, &settings);
/* Activate all nodes. */ /* Activate all nodes. */
SCULPT_cloth_sim_activate_nodes(ss->filter_cache->cloth_sim, ss->filter_cache->nodes); SCULPT_cloth_sim_activate_nodes(ss->filter_cache->cloth_sim, ss->filter_cache->nodes);

View File

@@ -1262,13 +1262,8 @@ static void sculpt_expand_cancel(bContext *C, wmOperator * /*op*/)
/** /**
* Callback to update mask data per PBVH node. * Callback to update mask data per PBVH node.
*/ */
static void sculpt_expand_mask_update_task_cb(void *__restrict userdata, static void sculpt_expand_mask_update_task(SculptSession *ss, PBVHNode *node)
const int i,
const TaskParallelTLS *__restrict /*tls*/)
{ {
SculptThreadedTaskData *data = static_cast<SculptThreadedTaskData *>(userdata);
SculptSession *ss = data->ob->sculpt;
PBVHNode *node = data->nodes[i];
ExpandCache *expand_cache = ss->expand_cache; ExpandCache *expand_cache = ss->expand_cache;
bool any_changed = false; bool any_changed = false;
@@ -1342,13 +1337,8 @@ static void sculpt_expand_face_sets_update(SculptSession *ss, ExpandCache *expan
/** /**
* Callback to update vertex colors per PBVH node. * Callback to update vertex colors per PBVH node.
*/ */
static void sculpt_expand_colors_update_task_cb(void *__restrict userdata, static void sculpt_expand_colors_update_task(SculptSession *ss, PBVHNode *node)
const int i,
const TaskParallelTLS *__restrict /*tls*/)
{ {
SculptThreadedTaskData *data = static_cast<SculptThreadedTaskData *>(userdata);
SculptSession *ss = data->ob->sculpt;
PBVHNode *node = data->nodes[i];
ExpandCache *expand_cache = ss->expand_cache; ExpandCache *expand_cache = ss->expand_cache;
bool any_changed = false; bool any_changed = false;
@@ -1476,8 +1466,8 @@ static void sculpt_expand_face_sets_restore(SculptSession *ss, ExpandCache *expa
static void sculpt_expand_update_for_vertex(bContext *C, Object *ob, const PBVHVertRef vertex) static void sculpt_expand_update_for_vertex(bContext *C, Object *ob, const PBVHVertRef vertex)
{ {
using namespace blender;
SculptSession *ss = ob->sculpt; SculptSession *ss = ob->sculpt;
Sculpt *sd = CTX_data_tool_settings(C)->sculpt;
ExpandCache *expand_cache = ss->expand_cache; ExpandCache *expand_cache = ss->expand_cache;
int vertex_i = BKE_pbvh_vertex_to_index(ss->pbvh, vertex); int vertex_i = BKE_pbvh_vertex_to_index(ss->pbvh, vertex);
@@ -1501,26 +1491,23 @@ static void sculpt_expand_update_for_vertex(bContext *C, Object *ob, const PBVHV
sculpt_expand_face_sets_restore(ss, expand_cache); sculpt_expand_face_sets_restore(ss, expand_cache);
} }
/* Update the mesh sculpt data. */
SculptThreadedTaskData data{};
data.sd = sd;
data.ob = ob;
data.nodes = expand_cache->nodes;
TaskParallelSettings settings;
BKE_pbvh_parallel_range_settings(&settings, true, expand_cache->nodes.size());
switch (expand_cache->target) { switch (expand_cache->target) {
case SCULPT_EXPAND_TARGET_MASK: case SCULPT_EXPAND_TARGET_MASK:
BLI_task_parallel_range( threading::parallel_for(expand_cache->nodes.index_range(), 1, [&](const IndexRange range) {
0, expand_cache->nodes.size(), &data, sculpt_expand_mask_update_task_cb, &settings); for (const int i : range) {
sculpt_expand_mask_update_task(ss, expand_cache->nodes[i]);
}
});
break; break;
case SCULPT_EXPAND_TARGET_FACE_SETS: case SCULPT_EXPAND_TARGET_FACE_SETS:
sculpt_expand_face_sets_update(ss, expand_cache); sculpt_expand_face_sets_update(ss, expand_cache);
break; break;
case SCULPT_EXPAND_TARGET_COLORS: case SCULPT_EXPAND_TARGET_COLORS:
BLI_task_parallel_range( threading::parallel_for(expand_cache->nodes.index_range(), 1, [&](const IndexRange range) {
0, expand_cache->nodes.size(), &data, sculpt_expand_colors_update_task_cb, &settings); for (const int i : range) {
sculpt_expand_colors_update_task(ss, expand_cache->nodes[i]);
}
});
break; break;
} }

View File

@@ -113,32 +113,27 @@ int ED_sculpt_face_sets_active_update_and_get(bContext *C, Object *ob, const flo
/* Draw Face Sets Brush. */ /* Draw Face Sets Brush. */
static void do_draw_face_sets_brush_task_cb_ex(void *__restrict userdata, static void do_draw_face_sets_brush_task(Object *ob, const Brush *brush, PBVHNode *node)
const int n,
const TaskParallelTLS *__restrict tls)
{ {
using namespace blender; using namespace blender;
SculptThreadedTaskData *data = static_cast<SculptThreadedTaskData *>(userdata); SculptSession *ss = ob->sculpt;
SculptSession *ss = data->ob->sculpt;
const Brush *brush = data->brush;
const float bstrength = ss->cache->bstrength; const float bstrength = ss->cache->bstrength;
PBVHVertexIter vd; PBVHVertexIter vd;
SculptBrushTest test; SculptBrushTest test;
SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape( SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape(
ss, &test, data->brush->falloff_shape); ss, &test, brush->falloff_shape);
const int thread_id = BLI_task_parallel_thread_id(tls); const int thread_id = BLI_task_parallel_thread_id(nullptr);
const Span<float3> positions( const Span<float3> positions(
reinterpret_cast<const float3 *>(SCULPT_mesh_deformed_positions_get(ss)), reinterpret_cast<const float3 *>(SCULPT_mesh_deformed_positions_get(ss)),
SCULPT_vertex_count_get(ss)); SCULPT_vertex_count_get(ss));
AutomaskingNodeData automask_data; AutomaskingNodeData automask_data;
SCULPT_automasking_node_begin( SCULPT_automasking_node_begin(ob, ss, ss->cache->automasking, &automask_data, node);
data->ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]);
bool changed = false; bool changed = false;
BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) {
SCULPT_automasking_node_update(ss, &automask_data, &vd); SCULPT_automasking_node_update(ss, &automask_data, &vd);
if (BKE_pbvh_type(ss->pbvh) == PBVH_FACES) { if (BKE_pbvh_type(ss->pbvh) == PBVH_FACES) {
@@ -196,37 +191,35 @@ static void do_draw_face_sets_brush_task_cb_ex(void *__restrict userdata,
BKE_pbvh_vertex_iter_end; BKE_pbvh_vertex_iter_end;
if (changed) { if (changed) {
SCULPT_undo_push_node(data->ob, data->nodes[n], SCULPT_UNDO_FACE_SETS); SCULPT_undo_push_node(ob, node, SCULPT_UNDO_FACE_SETS);
} }
} }
static void do_relax_face_sets_brush_task_cb_ex(void *__restrict userdata, static void do_relax_face_sets_brush_task(Object *ob,
const int n, const Brush *brush,
const TaskParallelTLS *__restrict tls) const int iteration,
PBVHNode *node)
{ {
SculptThreadedTaskData *data = static_cast<SculptThreadedTaskData *>(userdata); SculptSession *ss = ob->sculpt;
SculptSession *ss = data->ob->sculpt;
const Brush *brush = data->brush;
float bstrength = ss->cache->bstrength; float bstrength = ss->cache->bstrength;
PBVHVertexIter vd; PBVHVertexIter vd;
SculptBrushTest test; SculptBrushTest test;
SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape( SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape(
ss, &test, data->brush->falloff_shape); ss, &test, brush->falloff_shape);
const bool relax_face_sets = !(ss->cache->iteration_count % 3 == 0); const bool relax_face_sets = !(ss->cache->iteration_count % 3 == 0);
/* This operations needs a strength tweak as the relax deformation is too weak by default. */ /* This operations needs a strength tweak as the relax deformation is too weak by default. */
if (relax_face_sets && data->iteration < 2) { if (relax_face_sets && iteration < 2) {
bstrength *= 1.5f; bstrength *= 1.5f;
} }
const int thread_id = BLI_task_parallel_thread_id(tls); const int thread_id = BLI_task_parallel_thread_id(nullptr);
AutomaskingNodeData automask_data; AutomaskingNodeData automask_data;
SCULPT_automasking_node_begin( SCULPT_automasking_node_begin(ob, ss, ss->cache->automasking, &automask_data, node);
data->ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]);
BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) {
SCULPT_automasking_node_update(ss, &automask_data, &vd); SCULPT_automasking_node_update(ss, &automask_data, &vd);
if (!sculpt_brush_test_sq_fn(&test, vd.co)) { if (!sculpt_brush_test_sq_fn(&test, vd.co)) {
@@ -257,6 +250,7 @@ static void do_relax_face_sets_brush_task_cb_ex(void *__restrict userdata,
void SCULPT_do_draw_face_sets_brush(Sculpt *sd, Object *ob, Span<PBVHNode *> nodes) void SCULPT_do_draw_face_sets_brush(Sculpt *sd, Object *ob, Span<PBVHNode *> nodes)
{ {
using namespace blender;
SculptSession *ss = ob->sculpt; SculptSession *ss = ob->sculpt;
Brush *brush = BKE_paint_brush(&sd->paint); Brush *brush = BKE_paint_brush(&sd->paint);
@@ -268,25 +262,24 @@ void SCULPT_do_draw_face_sets_brush(Sculpt *sd, Object *ob, Span<PBVHNode *> nod
BKE_curvemapping_init(brush->curve); BKE_curvemapping_init(brush->curve);
/* Threaded loop over nodes. */
SculptThreadedTaskData data{};
data.sd = sd;
data.ob = ob;
data.brush = brush;
data.nodes = nodes;
TaskParallelSettings settings; TaskParallelSettings settings;
BKE_pbvh_parallel_range_settings(&settings, true, nodes.size()); BKE_pbvh_parallel_range_settings(&settings, true, nodes.size());
if (ss->cache->alt_smooth) { if (ss->cache->alt_smooth) {
SCULPT_boundary_info_ensure(ob); SCULPT_boundary_info_ensure(ob);
for (int i = 0; i < 4; i++) { for (int i = 0; i < 4; i++) {
data.iteration = i; threading::parallel_for(nodes.index_range(), 1, [&](const IndexRange range) {
BLI_task_parallel_range( for (const int i : range) {
0, nodes.size(), &data, do_relax_face_sets_brush_task_cb_ex, &settings); do_relax_face_sets_brush_task(ob, brush, i, nodes[i]);
}
});
} }
} }
else { else {
BLI_task_parallel_range(0, nodes.size(), &data, do_draw_face_sets_brush_task_cb_ex, &settings); threading::parallel_for(nodes.index_range(), 1, [&](const IndexRange range) {
for (const int i : range) {
do_draw_face_sets_brush_task(ob, brush, nodes[i]);
}
});
} }
} }

View File

@@ -72,24 +72,22 @@ static EnumPropertyItem prop_color_filter_types[] = {
{0, nullptr, 0, nullptr, nullptr}, {0, nullptr, 0, nullptr, nullptr},
}; };
static void color_filter_task_cb(void *__restrict userdata, static void color_filter_task(Object *ob,
const int n, const int mode,
const TaskParallelTLS *__restrict /*tls*/) const float filter_strength,
const float *filter_fill_color,
PBVHNode *node)
{ {
SculptThreadedTaskData *data = static_cast<SculptThreadedTaskData *>(userdata); SculptSession *ss = ob->sculpt;
SculptSession *ss = data->ob->sculpt;
const int mode = data->filter_type;
SculptOrigVertData orig_data; SculptOrigVertData orig_data;
SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n], SCULPT_UNDO_COLOR); SCULPT_orig_vert_data_init(&orig_data, ob, node, SCULPT_UNDO_COLOR);
AutomaskingNodeData automask_data; AutomaskingNodeData automask_data;
SCULPT_automasking_node_begin( SCULPT_automasking_node_begin(ob, ss, ss->filter_cache->automasking, &automask_data, node);
data->ob, ss, ss->filter_cache->automasking, &automask_data, data->nodes[n]);
PBVHVertexIter vd; PBVHVertexIter vd;
BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) {
SCULPT_orig_vert_data_update(&orig_data, &vd); SCULPT_orig_vert_data_update(&orig_data, &vd);
SCULPT_automasking_node_update(ss, &automask_data, &vd); SCULPT_automasking_node_update(ss, &automask_data, &vd);
@@ -98,7 +96,7 @@ static void color_filter_task_cb(void *__restrict userdata,
float brightness, contrast, gain, delta, offset; float brightness, contrast, gain, delta, offset;
float fade = vd.mask ? *vd.mask : 0.0f; float fade = vd.mask ? *vd.mask : 0.0f;
fade = 1.0f - fade; fade = 1.0f - fade;
fade *= data->filter_strength; fade *= filter_strength;
fade *= SCULPT_automasking_factor_get( fade *= SCULPT_automasking_factor_get(
ss->filter_cache->automasking, ss, vd.vertex, &automask_data); ss->filter_cache->automasking, ss, vd.vertex, &automask_data);
if (fade == 0.0f) { if (fade == 0.0f) {
@@ -111,7 +109,7 @@ static void color_filter_task_cb(void *__restrict userdata,
switch (mode) { switch (mode) {
case COLOR_FILTER_FILL: { case COLOR_FILTER_FILL: {
float fill_color_rgba[4]; float fill_color_rgba[4];
copy_v3_v3(fill_color_rgba, data->filter_fill_color); copy_v3_v3(fill_color_rgba, filter_fill_color);
fill_color_rgba[3] = 1.0f; fill_color_rgba[3] = 1.0f;
fade = clamp_f(fade, 0.0f, 1.0f); fade = clamp_f(fade, 0.0f, 1.0f);
mul_v4_fl(fill_color_rgba, fade); mul_v4_fl(fill_color_rgba, fade);
@@ -223,7 +221,7 @@ static void color_filter_task_cb(void *__restrict userdata,
SCULPT_vertex_color_set(ss, vd.vertex, final_color); SCULPT_vertex_color_set(ss, vd.vertex, final_color);
} }
BKE_pbvh_vertex_iter_end; BKE_pbvh_vertex_iter_end;
BKE_pbvh_node_mark_update_color(data->nodes[n]); BKE_pbvh_node_mark_update_color(node);
} }
static void sculpt_color_presmooth_init(SculptSession *ss) static void sculpt_color_presmooth_init(SculptSession *ss)
@@ -269,7 +267,7 @@ static void sculpt_color_presmooth_init(SculptSession *ss)
static void sculpt_color_filter_apply(bContext *C, wmOperator *op, Object *ob) static void sculpt_color_filter_apply(bContext *C, wmOperator *op, Object *ob)
{ {
Sculpt *sd = CTX_data_tool_settings(C)->sculpt; using namespace blender;
SculptSession *ss = ob->sculpt; SculptSession *ss = ob->sculpt;
const int mode = RNA_enum_get(op->ptr, "type"); const int mode = RNA_enum_get(op->ptr, "type");
@@ -283,20 +281,11 @@ static void sculpt_color_filter_apply(bContext *C, wmOperator *op, Object *ob)
sculpt_color_presmooth_init(ss); sculpt_color_presmooth_init(ss);
} }
SculptThreadedTaskData data{}; threading::parallel_for(ss->filter_cache->nodes.index_range(), 1, [&](const IndexRange range) {
data.sd = sd; for (const int i : range) {
data.ob = ob; color_filter_task(ob, mode, filter_strength, fill_color, ss->filter_cache->nodes[i]);
data.nodes = ss->filter_cache->nodes; }
data.filter_type = mode; });
data.filter_strength = filter_strength;
data.filter_fill_color = fill_color;
TaskParallelSettings settings;
BLI_parallel_range_settings_defaults(&settings);
BKE_pbvh_parallel_range_settings(&settings, true, ss->filter_cache->nodes.size());
BLI_task_parallel_range(
0, ss->filter_cache->nodes.size(), &data, color_filter_task_cb, &settings);
SCULPT_flush_update_step(C, SCULPT_UPDATE_COLOR); SCULPT_flush_update_step(C, SCULPT_UPDATE_COLOR);
} }

View File

@@ -60,16 +60,10 @@ static EnumPropertyItem prop_mask_filter_types[] = {
{0, nullptr, 0, nullptr, nullptr}, {0, nullptr, 0, nullptr, nullptr},
}; };
static void mask_filter_task_cb(void *__restrict userdata, static void mask_filter_task(SculptSession *ss, const int mode, float *prev_mask, PBVHNode *node)
const int i,
const TaskParallelTLS *__restrict /*tls*/)
{ {
SculptThreadedTaskData *data = static_cast<SculptThreadedTaskData *>(userdata);
SculptSession *ss = data->ob->sculpt;
PBVHNode *node = data->nodes[i];
bool update = false; bool update = false;
const int mode = data->filter_type;
float contrast = 0.0f; float contrast = 0.0f;
PBVHVertexIter vd; PBVHVertexIter vd;
@@ -110,7 +104,7 @@ static void mask_filter_task_cb(void *__restrict userdata,
case MASK_FILTER_GROW: case MASK_FILTER_GROW:
max = 0.0f; max = 0.0f;
SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vd.vertex, ni) { SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vd.vertex, ni) {
float vmask_f = data->prev_mask[ni.index]; float vmask_f = prev_mask[ni.index];
if (vmask_f > max) { if (vmask_f > max) {
max = vmask_f; max = vmask_f;
} }
@@ -121,7 +115,7 @@ static void mask_filter_task_cb(void *__restrict userdata,
case MASK_FILTER_SHRINK: case MASK_FILTER_SHRINK:
min = 1.0f; min = 1.0f;
SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vd.vertex, ni) { SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vd.vertex, ni) {
float vmask_f = data->prev_mask[ni.index]; float vmask_f = prev_mask[ni.index];
if (vmask_f < min) { if (vmask_f < min) {
min = vmask_f; min = vmask_f;
} }
@@ -158,10 +152,10 @@ static void mask_filter_task_cb(void *__restrict userdata,
static int sculpt_mask_filter_exec(bContext *C, wmOperator *op) static int sculpt_mask_filter_exec(bContext *C, wmOperator *op)
{ {
using namespace blender;
Object *ob = CTX_data_active_object(C); Object *ob = CTX_data_active_object(C);
Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C); Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C);
const Scene *scene = CTX_data_scene(C); const Scene *scene = CTX_data_scene(C);
Sculpt *sd = CTX_data_tool_settings(C)->sculpt;
int filter_type = RNA_enum_get(op->ptr, "filter_type"); int filter_type = RNA_enum_get(op->ptr, "filter_type");
MultiresModifierData *mmd = BKE_sculpt_multires_active(scene, ob); MultiresModifierData *mmd = BKE_sculpt_multires_active(scene, ob);
@@ -203,16 +197,11 @@ static int sculpt_mask_filter_exec(bContext *C, wmOperator *op)
} }
} }
SculptThreadedTaskData data{}; threading::parallel_for(nodes.index_range(), 1, [&](const IndexRange range) {
data.sd = sd; for (const int i : range) {
data.ob = ob; mask_filter_task(ss, filter_type, prev_mask, nodes[i]);
data.nodes = nodes; }
data.filter_type = filter_type; });
data.prev_mask = prev_mask;
TaskParallelSettings settings;
BKE_pbvh_parallel_range_settings(&settings, true, nodes.size());
BLI_task_parallel_range(0, nodes.size(), &data, mask_filter_task_cb, &settings);
if (ELEM(filter_type, MASK_FILTER_GROW, MASK_FILTER_SHRINK)) { if (ELEM(filter_type, MASK_FILTER_GROW, MASK_FILTER_SHRINK)) {
MEM_freeN(prev_mask); MEM_freeN(prev_mask);
@@ -226,21 +215,18 @@ static int sculpt_mask_filter_exec(bContext *C, wmOperator *op)
return OPERATOR_FINISHED; return OPERATOR_FINISHED;
} }
void SCULPT_mask_filter_smooth_apply(Sculpt *sd, void SCULPT_mask_filter_smooth_apply(Sculpt * /*sd*/,
Object *ob, Object *ob,
Span<PBVHNode *> nodes, Span<PBVHNode *> nodes,
const int smooth_iterations) const int smooth_iterations)
{ {
SculptThreadedTaskData data{}; using namespace blender;
data.sd = sd;
data.ob = ob;
data.nodes = nodes;
data.filter_type = MASK_FILTER_SMOOTH;
for (int i = 0; i < smooth_iterations; i++) { for (int i = 0; i < smooth_iterations; i++) {
TaskParallelSettings settings; threading::parallel_for(nodes.index_range(), 1, [&](const IndexRange range) {
BKE_pbvh_parallel_range_settings(&settings, true, nodes.size()); for (const int i : range) {
BLI_task_parallel_range(0, nodes.size(), &data, mask_filter_task_cb, &settings); mask_filter_task(ob->sculpt, MASK_FILTER_SMOOTH, nullptr, nodes[i]);
}
});
} }
} }

View File

@@ -98,16 +98,6 @@ void SCULPT_filter_zero_disabled_axis_components(float r_v[3], FilterCache *filt
SCULPT_filter_to_object_space(r_v, filter_cache); SCULPT_filter_to_object_space(r_v, filter_cache);
} }
static void filter_cache_init_task_cb(void *__restrict userdata,
const int i,
const TaskParallelTLS *__restrict /*tls*/)
{
SculptThreadedTaskData *data = static_cast<SculptThreadedTaskData *>(userdata);
PBVHNode *node = data->nodes[i];
SCULPT_undo_push_node(data->ob, node, SculptUndoType(data->filter_undo_type));
}
void SCULPT_filter_cache_init(bContext *C, void SCULPT_filter_cache_init(bContext *C,
Object *ob, Object *ob,
Sculpt *sd, Sculpt *sd,
@@ -116,6 +106,7 @@ void SCULPT_filter_cache_init(bContext *C,
float area_normal_radius, float area_normal_radius,
float start_strength) float start_strength)
{ {
using namespace blender;
SculptSession *ss = ob->sculpt; SculptSession *ss = ob->sculpt;
PBVH *pbvh = ob->sculpt->pbvh; PBVH *pbvh = ob->sculpt->pbvh;
@@ -147,16 +138,9 @@ void SCULPT_filter_cache_init(bContext *C,
BKE_pbvh_update_normals(ss->pbvh, nullptr); BKE_pbvh_update_normals(ss->pbvh, nullptr);
} }
SculptThreadedTaskData data{}; for (const int i : ss->filter_cache->nodes.index_range()) {
data.sd = sd; SCULPT_undo_push_node(ob, ss->filter_cache->nodes[i], SculptUndoType(undo_type));
data.ob = ob; }
data.nodes = ss->filter_cache->nodes;
data.filter_undo_type = undo_type;
TaskParallelSettings settings;
BKE_pbvh_parallel_range_settings(&settings, true, ss->filter_cache->nodes.size());
BLI_task_parallel_range(
0, ss->filter_cache->nodes.size(), &data, filter_cache_init_task_cb, &settings);
/* Setup orientation matrices. */ /* Setup orientation matrices. */
copy_m4_m4(ss->filter_cache->obmat, ob->object_to_world); copy_m4_m4(ss->filter_cache->obmat, ob->object_to_world);
@@ -207,7 +191,7 @@ void SCULPT_filter_cache_init(bContext *C,
pbvh, [&](PBVHNode &node) { return SCULPT_search_sphere(&node, &search_data2); }); pbvh, [&](PBVHNode &node) { return SCULPT_search_sphere(&node, &search_data2); });
if (BKE_paint_brush(&sd->paint) && if (BKE_paint_brush(&sd->paint) &&
SCULPT_pbvh_calc_area_normal(brush, ob, nodes, true, ss->filter_cache->initial_normal)) SCULPT_pbvh_calc_area_normal(brush, ob, nodes, ss->filter_cache->initial_normal))
{ {
copy_v3_v3(ss->last_normal, ss->filter_cache->initial_normal); copy_v3_v3(ss->last_normal, ss->filter_cache->initial_normal);
} }
@@ -363,18 +347,15 @@ static bool sculpt_mesh_filter_is_continuous(eSculptMeshFilterType type)
MESH_FILTER_RELAX_FACE_SETS); MESH_FILTER_RELAX_FACE_SETS);
} }
static void mesh_filter_task_cb(void *__restrict userdata, static void mesh_filter_task(Object *ob,
const int i, const eSculptMeshFilterType filter_type,
const TaskParallelTLS *__restrict /*tls*/) const float filter_strength,
PBVHNode *node)
{ {
SculptThreadedTaskData *data = static_cast<SculptThreadedTaskData *>(userdata); SculptSession *ss = ob->sculpt;
SculptSession *ss = data->ob->sculpt;
PBVHNode *node = data->nodes[i];
const eSculptMeshFilterType filter_type = eSculptMeshFilterType(data->filter_type);
SculptOrigVertData orig_data; SculptOrigVertData orig_data;
SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[i], SCULPT_UNDO_COORDS); SCULPT_orig_vert_data_init(&orig_data, ob, node, SCULPT_UNDO_COORDS);
/* When using the relax face sets meshes filter, /* When using the relax face sets meshes filter,
* each 3 iterations, do a whole mesh relax to smooth the contents of the Face Set. */ * each 3 iterations, do a whole mesh relax to smooth the contents of the Face Set. */
@@ -382,7 +363,7 @@ static void mesh_filter_task_cb(void *__restrict userdata,
* boundaries. */ * boundaries. */
const bool relax_face_sets = !(ss->filter_cache->iteration_count % 3 == 0); const bool relax_face_sets = !(ss->filter_cache->iteration_count % 3 == 0);
AutomaskingNodeData automask_data; AutomaskingNodeData automask_data;
SCULPT_automasking_node_begin(data->ob, ss, ss->filter_cache->automasking, &automask_data, node); SCULPT_automasking_node_begin(ob, ss, ss->filter_cache->automasking, &automask_data, node);
PBVHVertexIter vd; PBVHVertexIter vd;
BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) { BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) {
@@ -392,7 +373,7 @@ static void mesh_filter_task_cb(void *__restrict userdata,
float orig_co[3], val[3], avg[3], disp[3], disp2[3], transform[3][3], final_pos[3]; float orig_co[3], val[3], avg[3], disp[3], disp2[3], transform[3][3], final_pos[3];
float fade = vd.mask ? *vd.mask : 0.0f; float fade = vd.mask ? *vd.mask : 0.0f;
fade = 1.0f - fade; fade = 1.0f - fade;
fade *= data->filter_strength; fade *= filter_strength;
fade *= SCULPT_automasking_factor_get( fade *= SCULPT_automasking_factor_get(
ss->filter_cache->automasking, ss, vd.vertex, &automask_data); ss->filter_cache->automasking, ss, vd.vertex, &automask_data);
@@ -674,25 +655,22 @@ static void mesh_filter_sharpen_init(SculptSession *ss,
} }
} }
static void mesh_filter_surface_smooth_displace_task_cb(void *__restrict userdata, static void mesh_filter_surface_smooth_displace_task(Object *ob,
const int i, const float filter_strength,
const TaskParallelTLS *__restrict /*tls*/) PBVHNode *node)
{ {
SculptThreadedTaskData *data = static_cast<SculptThreadedTaskData *>(userdata); SculptSession *ss = ob->sculpt;
SculptSession *ss = data->ob->sculpt;
PBVHNode *node = data->nodes[i];
PBVHVertexIter vd; PBVHVertexIter vd;
AutomaskingNodeData automask_data; AutomaskingNodeData automask_data;
SCULPT_automasking_node_begin( SCULPT_automasking_node_begin(ob, ss, ss->filter_cache->automasking, &automask_data, node);
data->ob, ss, ss->filter_cache->automasking, &automask_data, data->nodes[i]);
BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) { BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) {
SCULPT_automasking_node_update(ss, &automask_data, &vd); SCULPT_automasking_node_update(ss, &automask_data, &vd);
float fade = vd.mask ? *vd.mask : 0.0f; float fade = vd.mask ? *vd.mask : 0.0f;
fade = 1.0f - fade; fade = 1.0f - fade;
fade *= data->filter_strength; fade *= filter_strength;
fade *= SCULPT_automasking_factor_get( fade *= SCULPT_automasking_factor_get(
ss->filter_cache->automasking, ss, vd.vertex, &automask_data); ss->filter_cache->automasking, ss, vd.vertex, &automask_data);
if (fade == 0.0f) { if (fade == 0.0f) {
@@ -759,6 +737,7 @@ static void sculpt_mesh_update_status_bar(bContext *C, wmOperator *op)
static void sculpt_mesh_filter_apply(bContext *C, wmOperator *op) static void sculpt_mesh_filter_apply(bContext *C, wmOperator *op)
{ {
using namespace blender;
Object *ob = CTX_data_active_object(C); Object *ob = CTX_data_active_object(C);
SculptSession *ss = ob->sculpt; SculptSession *ss = ob->sculpt;
Sculpt *sd = CTX_data_tool_settings(C)->sculpt; Sculpt *sd = CTX_data_tool_settings(C)->sculpt;
@@ -767,24 +746,19 @@ static void sculpt_mesh_filter_apply(bContext *C, wmOperator *op)
SCULPT_vertex_random_access_ensure(ss); SCULPT_vertex_random_access_ensure(ss);
SculptThreadedTaskData data{}; threading::parallel_for(ss->filter_cache->nodes.index_range(), 1, [&](const IndexRange range) {
data.sd = sd; for (const int i : range) {
data.ob = ob; mesh_filter_task(
data.nodes = ss->filter_cache->nodes; ob, eSculptMeshFilterType(filter_type), filter_strength, ss->filter_cache->nodes[i]);
data.filter_type = filter_type; }
data.filter_strength = filter_strength; });
TaskParallelSettings settings;
BKE_pbvh_parallel_range_settings(&settings, true, ss->filter_cache->nodes.size());
BLI_task_parallel_range(
0, ss->filter_cache->nodes.size(), &data, mesh_filter_task_cb, &settings);
if (filter_type == MESH_FILTER_SURFACE_SMOOTH) { if (filter_type == MESH_FILTER_SURFACE_SMOOTH) {
BLI_task_parallel_range(0, threading::parallel_for(ss->filter_cache->nodes.index_range(), 1, [&](const IndexRange range) {
ss->filter_cache->nodes.size(), for (const int i : range) {
&data, mesh_filter_surface_smooth_displace_task(ob, filter_strength, ss->filter_cache->nodes[i]);
mesh_filter_surface_smooth_displace_task_cb, }
&settings); });
} }
ss->filter_cache->iteration_count++; ss->filter_cache->iteration_count++;

View File

@@ -242,127 +242,6 @@ struct SculptRakeData {
float angle; float angle;
}; };
/**
* Generic thread data. The size of this struct has gotten a little out of hand;
* normally we would split it up, but it might be better to see if we can't eliminate it
* altogether after moving to C++ (where we'll be able to use lambdas).
*/
struct SculptThreadedTaskData {
bContext *C;
Sculpt *sd;
Object *ob;
const Brush *brush;
Span<PBVHNode *> nodes;
VPaint *vp;
WPaintData *wpd;
WeightPaintInfo *wpi;
unsigned int *lcol;
Mesh *me;
/* For passing generic params. */
void *custom_data;
/* Data specific to some callbacks. */
/* NOTE: even if only one or two of those are used at a time,
* keeping them separated, names help figuring out
* what it is, and memory overhead is ridiculous anyway. */
float flippedbstrength;
float angle;
float strength;
bool smooth_mask;
bool has_bm_orco;
SculptProjectVector *spvc;
float *offset;
float *grab_delta;
float *cono;
float *area_no;
float *area_no_sp;
float *area_co;
float (*mat)[4];
float (*vertCos)[3];
/* When true, the displacement stored in the proxies will be applied to the original coordinates
* instead of to the current coordinates. */
bool use_proxies_orco;
/* X and Z vectors aligned to the stroke direction for operations where perpendicular vectors to
* the stroke direction are needed. */
float (*stroke_xz)[3];
int filter_type;
float filter_strength;
float *filter_fill_color;
bool use_area_cos;
bool use_area_nos;
/* 0=towards view, 1=flipped */
float (*area_cos)[3];
float (*area_nos)[3];
int *count_no;
int *count_co;
bool any_vertex_sampled;
float *wet_mix_sampled_color;
float *prev_mask;
float *pose_factor;
float *pose_initial_co;
int pose_chain_segment;
float multiplane_scrape_angle;
float multiplane_scrape_planes[2][4];
float max_distance_squared;
float nearest_vertex_search_co[3];
/* Stabilized strength for the Clay Thumb brush. */
float clay_strength;
int mask_expand_update_it;
bool mask_expand_invert_mask;
bool mask_expand_use_normals;
bool mask_expand_keep_prev_mask;
bool mask_expand_create_face_set;
float transform_mats[8][4][4];
float elastic_transform_mat[4][4];
float elastic_transform_pivot[3];
float elastic_transform_pivot_init[3];
float elastic_transform_radius;
/* Boundary brush */
float boundary_deform_strength;
float cloth_time_step;
SculptClothSimulation *cloth_sim;
float *cloth_sim_initial_location;
float cloth_sim_radius;
/* Mask By Color Tool */
float mask_by_color_threshold;
bool mask_by_color_invert;
bool mask_by_color_preserve_mask;
/* Index of the vertex that is going to be used as a reference for the colors. */
PBVHVertRef mask_by_color_vertex;
float *mask_by_color_floodfill;
int face_set;
int filter_undo_type;
int mask_init_mode;
int mask_init_seed;
ThreadMutex mutex;
int iteration;
};
/*************** Brush testing declarations ****************/ /*************** Brush testing declarations ****************/
struct SculptBrushTest { struct SculptBrushTest {
float radius_squared; float radius_squared;
@@ -1595,7 +1474,6 @@ void SCULPT_relax_vertex(SculptSession *ss,
bool SCULPT_pbvh_calc_area_normal(const Brush *brush, bool SCULPT_pbvh_calc_area_normal(const Brush *brush,
Object *ob, Object *ob,
Span<PBVHNode *> nodes, Span<PBVHNode *> nodes,
bool use_threading,
float r_area_no[3]); float r_area_no[3]);
/** /**

View File

@@ -74,15 +74,16 @@ static void sculpt_mask_expand_cancel(bContext *C, wmOperator *op)
ED_workspace_status_text(C, nullptr); ED_workspace_status_text(C, nullptr);
} }
static void sculpt_expand_task_cb(void *__restrict userdata, static void sculpt_expand_task(Object *ob,
const int i, const int update_it,
const TaskParallelTLS *__restrict /*tls*/) const bool mask_expand_use_normals,
const bool mask_expand_create_face_set,
const bool mask_expand_keep_prev_mask,
const bool mask_expand_invert_mask,
PBVHNode *node)
{ {
SculptThreadedTaskData *data = static_cast<SculptThreadedTaskData *>(userdata); SculptSession *ss = ob->sculpt;
SculptSession *ss = data->ob->sculpt;
PBVHNode *node = data->nodes[i];
PBVHVertexIter vd; PBVHVertexIter vd;
int update_it = data->mask_expand_update_it;
PBVHVertRef active_vertex = SCULPT_active_vertex_get(ss); PBVHVertRef active_vertex = SCULPT_active_vertex_get(ss);
int active_vertex_i = BKE_pbvh_vertex_to_index(ss->pbvh, active_vertex); int active_vertex_i = BKE_pbvh_vertex_to_index(ss->pbvh, active_vertex);
@@ -92,7 +93,7 @@ static void sculpt_expand_task_cb(void *__restrict userdata,
BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_ALL) { BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_ALL) {
int vi = vd.index; int vi = vd.index;
float final_mask = *vd.mask; float final_mask = *vd.mask;
if (data->mask_expand_use_normals) { if (mask_expand_use_normals) {
if (ss->filter_cache->normal_factor[active_vertex_i] < if (ss->filter_cache->normal_factor[active_vertex_i] <
ss->filter_cache->normal_factor[vd.index]) { ss->filter_cache->normal_factor[vd.index]) {
final_mask = 1.0f; final_mask = 1.0f;
@@ -111,7 +112,7 @@ static void sculpt_expand_task_cb(void *__restrict userdata,
} }
} }
if (data->mask_expand_create_face_set) { if (mask_expand_create_face_set) {
if (final_mask == 1.0f) { if (final_mask == 1.0f) {
SCULPT_vertex_face_set_set(ss, vd.vertex, ss->filter_cache->new_face_set); SCULPT_vertex_face_set_set(ss, vd.vertex, ss->filter_cache->new_face_set);
face_sets_changed = true; face_sets_changed = true;
@@ -120,11 +121,11 @@ static void sculpt_expand_task_cb(void *__restrict userdata,
} }
else { else {
if (data->mask_expand_keep_prev_mask) { if (mask_expand_keep_prev_mask) {
final_mask = MAX2(ss->filter_cache->prev_mask[vd.index], final_mask); final_mask = MAX2(ss->filter_cache->prev_mask[vd.index], final_mask);
} }
if (data->mask_expand_invert_mask) { if (mask_expand_invert_mask) {
final_mask = 1.0f - final_mask; final_mask = 1.0f - final_mask;
} }
@@ -137,12 +138,13 @@ static void sculpt_expand_task_cb(void *__restrict userdata,
BKE_pbvh_vertex_iter_end; BKE_pbvh_vertex_iter_end;
if (face_sets_changed) { if (face_sets_changed) {
SCULPT_undo_push_node(data->ob, node, SCULPT_UNDO_FACE_SETS); SCULPT_undo_push_node(ob, node, SCULPT_UNDO_FACE_SETS);
} }
} }
static int sculpt_mask_expand_modal(bContext *C, wmOperator *op, const wmEvent *event) static int sculpt_mask_expand_modal(bContext *C, wmOperator *op, const wmEvent *event)
{ {
using namespace blender;
Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C); Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C);
Object *ob = CTX_data_active_object(C); Object *ob = CTX_data_active_object(C);
SculptSession *ss = ob->sculpt; SculptSession *ss = ob->sculpt;
@@ -261,20 +263,24 @@ static int sculpt_mask_expand_modal(bContext *C, wmOperator *op, const wmEvent *
ss->face_sets[i] = ss->filter_cache->prev_face_set[i]; ss->face_sets[i] = ss->filter_cache->prev_face_set[i];
} }
} }
SculptThreadedTaskData data{};
data.sd = sd;
data.ob = ob;
data.nodes = ss->filter_cache->nodes;
data.mask_expand_update_it = mask_expand_update_it;
data.mask_expand_use_normals = RNA_boolean_get(op->ptr, "use_normals");
data.mask_expand_invert_mask = RNA_boolean_get(op->ptr, "invert");
data.mask_expand_keep_prev_mask = RNA_boolean_get(op->ptr, "keep_previous_mask");
data.mask_expand_create_face_set = RNA_boolean_get(op->ptr, "create_face_set");
TaskParallelSettings settings; const bool use_normals = RNA_boolean_get(op->ptr, "use_normals");
BKE_pbvh_parallel_range_settings(&settings, true, ss->filter_cache->nodes.size()); const bool invert_mask = RNA_boolean_get(op->ptr, "invert");
BLI_task_parallel_range( const bool keep_prev_mask = RNA_boolean_get(op->ptr, "keep_previous_mask");
0, ss->filter_cache->nodes.size(), &data, sculpt_expand_task_cb, &settings); const bool create_face_set = RNA_boolean_get(op->ptr, "create_face_set");
threading::parallel_for(ss->filter_cache->nodes.index_range(), 1, [&](const IndexRange range) {
for (const int i : range) {
sculpt_expand_task(ob,
mask_expand_update_it,
use_normals,
create_face_set,
keep_prev_mask,
invert_mask,
ss->filter_cache->nodes[i]);
}
});
ss->filter_cache->mask_update_current_it = mask_expand_update_it; ss->filter_cache->mask_update_current_it = mask_expand_update_it;
} }
@@ -330,6 +336,7 @@ static bool mask_expand_floodfill_cb(
static int sculpt_mask_expand_invoke(bContext *C, wmOperator *op, const wmEvent *event) static int sculpt_mask_expand_invoke(bContext *C, wmOperator *op, const wmEvent *event)
{ {
using namespace blender;
Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C); Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C);
Object *ob = CTX_data_active_object(C); Object *ob = CTX_data_active_object(C);
SculptSession *ss = ob->sculpt; SculptSession *ss = ob->sculpt;
@@ -438,20 +445,20 @@ static int sculpt_mask_expand_invoke(bContext *C, wmOperator *op, const wmEvent
MEM_SAFE_FREE(ss->filter_cache->edge_factor); MEM_SAFE_FREE(ss->filter_cache->edge_factor);
} }
SculptThreadedTaskData data{}; const bool invert_mask = RNA_boolean_get(op->ptr, "invert");
data.sd = sd; const bool keep_prev_mask = RNA_boolean_get(op->ptr, "keep_previous_mask");
data.ob = ob;
data.nodes = ss->filter_cache->nodes;
data.mask_expand_update_it = 0;
data.mask_expand_use_normals = RNA_boolean_get(op->ptr, "use_normals");
data.mask_expand_invert_mask = RNA_boolean_get(op->ptr, "invert");
data.mask_expand_keep_prev_mask = RNA_boolean_get(op->ptr, "keep_previous_mask");
data.mask_expand_create_face_set = RNA_boolean_get(op->ptr, "create_face_set");
TaskParallelSettings settings; threading::parallel_for(ss->filter_cache->nodes.index_range(), 1, [&](const IndexRange range) {
BKE_pbvh_parallel_range_settings(&settings, true, ss->filter_cache->nodes.size()); for (const int i : range) {
BLI_task_parallel_range( sculpt_expand_task(ob,
0, ss->filter_cache->nodes.size(), &data, sculpt_expand_task_cb, &settings); 0,
use_normals,
create_face_set,
keep_prev_mask,
invert_mask,
ss->filter_cache->nodes[i]);
}
});
const char *status_str = TIP_( const char *status_str = TIP_(
"Move the mouse to expand the mask from the active vertex. LMB: confirm mask, ESC/RMB: " "Move the mouse to expand the mask from the active vertex. LMB: confirm mask, ESC/RMB: "

View File

@@ -73,17 +73,11 @@ static EnumPropertyItem prop_sculpt_mask_init_mode_types[] = {
{0, nullptr, 0, nullptr, nullptr}, {0, nullptr, 0, nullptr, nullptr},
}; };
static void mask_init_task_cb(void *__restrict userdata, static void mask_init_task(Object *ob, const int mode, const int seed, PBVHNode *node)
const int i,
const TaskParallelTLS *__restrict /*tls*/)
{ {
SculptThreadedTaskData *data = static_cast<SculptThreadedTaskData *>(userdata); SculptSession *ss = ob->sculpt;
SculptSession *ss = data->ob->sculpt;
PBVHNode *node = data->nodes[i];
PBVHVertexIter vd; PBVHVertexIter vd;
const int mode = data->mask_init_mode; SCULPT_undo_push_node(ob, node, SCULPT_UNDO_MASK);
const int seed = data->mask_init_seed;
SCULPT_undo_push_node(data->ob, node, SCULPT_UNDO_MASK);
BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) { BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) {
switch (mode) { switch (mode) {
case SCULPT_MASK_INIT_RANDOM_PER_VERTEX: case SCULPT_MASK_INIT_RANDOM_PER_VERTEX:
@@ -100,11 +94,12 @@ static void mask_init_task_cb(void *__restrict userdata,
} }
} }
BKE_pbvh_vertex_iter_end; BKE_pbvh_vertex_iter_end;
BKE_pbvh_node_mark_update_mask(data->nodes[i]); BKE_pbvh_node_mark_update_mask(node);
} }
static int sculpt_mask_init_exec(bContext *C, wmOperator *op) static int sculpt_mask_init_exec(bContext *C, wmOperator *op)
{ {
using namespace blender;
Object *ob = CTX_data_active_object(C); Object *ob = CTX_data_active_object(C);
SculptSession *ss = ob->sculpt; SculptSession *ss = ob->sculpt;
Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
@@ -129,15 +124,13 @@ static int sculpt_mask_init_exec(bContext *C, wmOperator *op)
SCULPT_topology_islands_ensure(ob); SCULPT_topology_islands_ensure(ob);
} }
SculptThreadedTaskData data{}; const int mask_init_seed = PIL_check_seconds_timer();
data.ob = ob;
data.nodes = nodes;
data.mask_init_mode = mode;
data.mask_init_seed = PIL_check_seconds_timer();
TaskParallelSettings settings; threading::parallel_for(nodes.index_range(), 1, [&](const IndexRange range) {
BKE_pbvh_parallel_range_settings(&settings, true, nodes.size()); for (const int i : range) {
BLI_task_parallel_range(0, nodes.size(), &data, mask_init_task_cb, &settings); mask_init_task(ob, mode, mask_init_seed, nodes[i]);
}
});
multires_stitch_grids(ob); multires_stitch_grids(ob);

View File

@@ -35,23 +35,20 @@ struct MultiplaneScrapeSampleData {
int area_count[2]; int area_count[2];
}; };
static void calc_multiplane_scrape_surface_task_cb(void *__restrict userdata, static void calc_multiplane_scrape_surface_task(Object *ob,
const int n, const Brush *brush,
const TaskParallelTLS *__restrict tls) const float (*mat)[4],
PBVHNode *node,
MultiplaneScrapeSampleData *mssd)
{ {
SculptThreadedTaskData *data = static_cast<SculptThreadedTaskData *>(userdata); SculptSession *ss = ob->sculpt;
SculptSession *ss = data->ob->sculpt;
const Brush *brush = data->brush;
MultiplaneScrapeSampleData *mssd = static_cast<MultiplaneScrapeSampleData *>(
tls->userdata_chunk);
float(*mat)[4] = data->mat;
PBVHVertexIter vd; PBVHVertexIter vd;
SculptBrushTest test; SculptBrushTest test;
SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape( SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape(
ss, &test, brush->falloff_shape); ss, &test, brush->falloff_shape);
const int thread_id = BLI_task_parallel_thread_id(tls); const int thread_id = BLI_task_parallel_thread_id(nullptr);
/* Apply the brush normal radius to the test before sampling. */ /* Apply the brush normal radius to the test before sampling. */
float test_radius = sqrtf(test.radius_squared); float test_radius = sqrtf(test.radius_squared);
@@ -59,10 +56,9 @@ static void calc_multiplane_scrape_surface_task_cb(void *__restrict userdata,
test.radius_squared = test_radius * test_radius; test.radius_squared = test_radius * test_radius;
AutomaskingNodeData automask_data; AutomaskingNodeData automask_data;
SCULPT_automasking_node_begin( SCULPT_automasking_node_begin(ob, ss, ss->cache->automasking, &automask_data, node);
data->ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]);
BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) {
if (!sculpt_brush_test_sq_fn(&test, vd.co)) { if (!sculpt_brush_test_sq_fn(&test, vd.co)) {
continue; continue;
@@ -101,51 +97,30 @@ static void calc_multiplane_scrape_surface_task_cb(void *__restrict userdata,
} }
} }
static void calc_multiplane_scrape_surface_reduce(const void *__restrict /*userdata*/, static void do_multiplane_scrape_brush_task(Object *ob,
void *__restrict chunk_join, const Brush *brush,
void *__restrict chunk) const float (*mat)[4],
const float (*scrape_planes)[4],
const float angle,
PBVHNode *node)
{ {
MultiplaneScrapeSampleData *join = static_cast<MultiplaneScrapeSampleData *>(chunk_join); SculptSession *ss = ob->sculpt;
MultiplaneScrapeSampleData *mssd = static_cast<MultiplaneScrapeSampleData *>(chunk);
add_v3_v3(join->area_cos[0], mssd->area_cos[0]);
add_v3_v3(join->area_cos[1], mssd->area_cos[1]);
add_v3_v3(join->area_nos[0], mssd->area_nos[0]);
add_v3_v3(join->area_nos[1], mssd->area_nos[1]);
join->area_count[0] += mssd->area_count[0];
join->area_count[1] += mssd->area_count[1];
}
static void do_multiplane_scrape_brush_task_cb_ex(void *__restrict userdata,
const int n,
const TaskParallelTLS *__restrict tls)
{
SculptThreadedTaskData *data = static_cast<SculptThreadedTaskData *>(userdata);
SculptSession *ss = data->ob->sculpt;
const Brush *brush = data->brush;
float(*mat)[4] = data->mat;
float(*scrape_planes)[4] = data->multiplane_scrape_planes;
float angle = data->multiplane_scrape_angle;
PBVHVertexIter vd; PBVHVertexIter vd;
float(*proxy)[3]; float(*proxy)[3];
const float bstrength = fabsf(ss->cache->bstrength); const float bstrength = fabsf(ss->cache->bstrength);
proxy = BKE_pbvh_node_add_proxy(ss->pbvh, data->nodes[n])->co; proxy = BKE_pbvh_node_add_proxy(ss->pbvh, node)->co;
SculptBrushTest test; SculptBrushTest test;
SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape( SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape(
ss, &test, data->brush->falloff_shape); ss, &test, brush->falloff_shape);
const int thread_id = BLI_task_parallel_thread_id(tls); const int thread_id = BLI_task_parallel_thread_id(nullptr);
AutomaskingNodeData automask_data; AutomaskingNodeData automask_data;
SCULPT_automasking_node_begin( SCULPT_automasking_node_begin(ob, ss, ss->cache->automasking, &automask_data, node);
data->ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]);
BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) {
if (!sculpt_brush_test_sq_fn(&test, vd.co)) { if (!sculpt_brush_test_sq_fn(&test, vd.co)) {
continue; continue;
@@ -215,6 +190,7 @@ static void do_multiplane_scrape_brush_task_cb_ex(void *__restrict userdata,
void SCULPT_do_multiplane_scrape_brush(Sculpt *sd, Object *ob, Span<PBVHNode *> nodes) void SCULPT_do_multiplane_scrape_brush(Sculpt *sd, Object *ob, Span<PBVHNode *> nodes)
{ {
using namespace blender;
SculptSession *ss = ob->sculpt; SculptSession *ss = ob->sculpt;
Brush *brush = BKE_paint_brush(&sd->paint); Brush *brush = BKE_paint_brush(&sd->paint);
@@ -278,23 +254,29 @@ void SCULPT_do_multiplane_scrape_brush(Sculpt *sd, Object *ob, Span<PBVHNode *>
if (brush->flag2 & BRUSH_MULTIPLANE_SCRAPE_DYNAMIC) { if (brush->flag2 & BRUSH_MULTIPLANE_SCRAPE_DYNAMIC) {
/* Sample the individual normal and area center of the two areas at both sides of the cursor. /* Sample the individual normal and area center of the two areas at both sides of the cursor.
*/ */
SculptThreadedTaskData sample_data{}; const MultiplaneScrapeSampleData mssd = threading::parallel_reduce(
sample_data.sd = nullptr; nodes.index_range(),
sample_data.ob = ob; 1,
sample_data.brush = brush; MultiplaneScrapeSampleData{},
sample_data.nodes = nodes; [&](const IndexRange range, MultiplaneScrapeSampleData mssd) {
sample_data.mat = mat; for (const int i : range) {
calc_multiplane_scrape_surface_task(ob, brush, mat, nodes[i], &mssd);
}
return mssd;
},
[](const MultiplaneScrapeSampleData &a, const MultiplaneScrapeSampleData &b) {
MultiplaneScrapeSampleData joined = a;
MultiplaneScrapeSampleData mssd = {{{0}}}; add_v3_v3v3(joined.area_cos[0], a.area_cos[0], b.area_cos[0]);
add_v3_v3v3(joined.area_cos[1], a.area_cos[1], b.area_cos[1]);
TaskParallelSettings sample_settings; add_v3_v3v3(joined.area_nos[0], a.area_nos[0], b.area_nos[0]);
BKE_pbvh_parallel_range_settings(&sample_settings, true, nodes.size()); add_v3_v3v3(joined.area_nos[1], a.area_nos[1], b.area_nos[1]);
sample_settings.func_reduce = calc_multiplane_scrape_surface_reduce;
sample_settings.userdata_chunk = &mssd;
sample_settings.userdata_chunk_size = sizeof(MultiplaneScrapeSampleData);
BLI_task_parallel_range( joined.area_count[0] = a.area_count[0] + b.area_count[0];
0, nodes.size(), &sample_data, calc_multiplane_scrape_surface_task_cb, &sample_settings); joined.area_count[1] = a.area_count[1] + b.area_count[1];
return joined;
});
float sampled_plane_normals[2][3]; float sampled_plane_normals[2][3];
float sampled_plane_co[2][3]; float sampled_plane_co[2][3];
@@ -348,14 +330,6 @@ void SCULPT_do_multiplane_scrape_brush(Sculpt *sd, Object *ob, Span<PBVHNode *>
} }
} }
SculptThreadedTaskData data{};
data.sd = sd;
data.ob = ob;
data.brush = brush;
data.nodes = nodes;
data.mat = mat;
data.multiplane_scrape_angle = ss->cache->multiplane_scrape_angle;
/* Calculate the final left and right scrape planes. */ /* Calculate the final left and right scrape planes. */
float plane_no[3]; float plane_no[3];
float plane_no_rot[3]; float plane_no_rot[3];
@@ -363,24 +337,28 @@ void SCULPT_do_multiplane_scrape_brush(Sculpt *sd, Object *ob, Span<PBVHNode *>
float mat_inv[4][4]; float mat_inv[4][4];
invert_m4_m4(mat_inv, mat); invert_m4_m4(mat_inv, mat);
float multiplane_scrape_planes[2][4];
mul_v3_mat3_m4v3(plane_no, mat, area_no); mul_v3_mat3_m4v3(plane_no, mat, area_no);
rotate_v3_v3v3fl( rotate_v3_v3v3fl(
plane_no_rot, plane_no, y_axis, DEG2RADF(-ss->cache->multiplane_scrape_angle * 0.5f)); plane_no_rot, plane_no, y_axis, DEG2RADF(-ss->cache->multiplane_scrape_angle * 0.5f));
mul_v3_mat3_m4v3(plane_no, mat_inv, plane_no_rot); mul_v3_mat3_m4v3(plane_no, mat_inv, plane_no_rot);
normalize_v3(plane_no); normalize_v3(plane_no);
plane_from_point_normal_v3(data.multiplane_scrape_planes[1], area_co, plane_no); plane_from_point_normal_v3(multiplane_scrape_planes[1], area_co, plane_no);
mul_v3_mat3_m4v3(plane_no, mat, area_no); mul_v3_mat3_m4v3(plane_no, mat, area_no);
rotate_v3_v3v3fl( rotate_v3_v3v3fl(
plane_no_rot, plane_no, y_axis, DEG2RADF(ss->cache->multiplane_scrape_angle * 0.5f)); plane_no_rot, plane_no, y_axis, DEG2RADF(ss->cache->multiplane_scrape_angle * 0.5f));
mul_v3_mat3_m4v3(plane_no, mat_inv, plane_no_rot); mul_v3_mat3_m4v3(plane_no, mat_inv, plane_no_rot);
normalize_v3(plane_no); normalize_v3(plane_no);
plane_from_point_normal_v3(data.multiplane_scrape_planes[0], area_co, plane_no); plane_from_point_normal_v3(multiplane_scrape_planes[0], area_co, plane_no);
TaskParallelSettings settings; threading::parallel_for(nodes.index_range(), 1, [&](const IndexRange range) {
BKE_pbvh_parallel_range_settings(&settings, true, nodes.size()); for (const int i : range) {
BLI_task_parallel_range( do_multiplane_scrape_brush_task(
0, nodes.size(), &data, do_multiplane_scrape_brush_task_cb_ex, &settings); ob, brush, mat, multiplane_scrape_planes, ss->cache->multiplane_scrape_angle, nodes[i]);
}
});
} }
void SCULPT_multiplane_scrape_preview_draw(const uint gpuattr, void SCULPT_multiplane_scrape_preview_draw(const uint gpuattr,

View File

@@ -730,23 +730,21 @@ struct MaskByColorContiguousFloodFillData {
float initial_color[3]; float initial_color[3];
}; };
static void do_mask_by_color_contiguous_update_nodes_cb(void *__restrict userdata, static void do_mask_by_color_contiguous_update_node(Object *ob,
const int n, const float *mask_by_color_floodfill,
const TaskParallelTLS *__restrict /*tls*/) const bool invert,
const bool preserve_mask,
PBVHNode *node)
{ {
SculptThreadedTaskData *data = static_cast<SculptThreadedTaskData *>(userdata); SculptSession *ss = ob->sculpt;
SculptSession *ss = data->ob->sculpt;
SCULPT_undo_push_node(data->ob, data->nodes[n], SCULPT_UNDO_MASK); SCULPT_undo_push_node(ob, node, SCULPT_UNDO_MASK);
bool update_node = false; bool update_node = false;
const bool invert = data->mask_by_color_invert;
const bool preserve_mask = data->mask_by_color_preserve_mask;
PBVHVertexIter vd; PBVHVertexIter vd;
BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) {
const float current_mask = *vd.mask; const float current_mask = *vd.mask;
const float new_mask = data->mask_by_color_floodfill[vd.index]; const float new_mask = mask_by_color_floodfill[vd.index];
*vd.mask = sculpt_mask_by_color_final_mask_get(current_mask, new_mask, invert, preserve_mask); *vd.mask = sculpt_mask_by_color_final_mask_get(current_mask, new_mask, invert, preserve_mask);
if (current_mask == *vd.mask) { if (current_mask == *vd.mask) {
continue; continue;
@@ -755,11 +753,11 @@ static void do_mask_by_color_contiguous_update_nodes_cb(void *__restrict userdat
} }
BKE_pbvh_vertex_iter_end; BKE_pbvh_vertex_iter_end;
if (update_node) { if (update_node) {
BKE_pbvh_node_mark_update_mask(data->nodes[n]); BKE_pbvh_node_mark_update_mask(node);
} }
} }
static bool sculpt_mask_by_color_contiguous_floodfill_cb( static bool sculpt_mask_by_color_contiguous_floodfill(
SculptSession *ss, PBVHVertRef from_v, PBVHVertRef to_v, bool is_duplicate, void *userdata) SculptSession *ss, PBVHVertRef from_v, PBVHVertRef to_v, bool is_duplicate, void *userdata)
{ {
int from_v_i = BKE_pbvh_vertex_to_index(ss->pbvh, from_v); int from_v_i = BKE_pbvh_vertex_to_index(ss->pbvh, from_v);
@@ -790,6 +788,7 @@ static void sculpt_mask_by_color_contiguous(Object *object,
const bool invert, const bool invert,
const bool preserve_mask) const bool preserve_mask)
{ {
using namespace blender;
SculptSession *ss = object->sculpt; SculptSession *ss = object->sculpt;
const int totvert = SCULPT_vertex_count_get(ss); const int totvert = SCULPT_vertex_count_get(ss);
@@ -815,47 +814,38 @@ static void sculpt_mask_by_color_contiguous(Object *object,
copy_v3_v3(ffd.initial_color, color); copy_v3_v3(ffd.initial_color, color);
SCULPT_floodfill_execute(ss, &flood, sculpt_mask_by_color_contiguous_floodfill_cb, &ffd); SCULPT_floodfill_execute(ss, &flood, sculpt_mask_by_color_contiguous_floodfill, &ffd);
SCULPT_floodfill_free(&flood); SCULPT_floodfill_free(&flood);
Vector<PBVHNode *> nodes = blender::bke::pbvh::search_gather(ss->pbvh, {}); Vector<PBVHNode *> nodes = blender::bke::pbvh::search_gather(ss->pbvh, {});
SculptThreadedTaskData data{}; threading::parallel_for(nodes.index_range(), 1, [&](const IndexRange range) {
data.ob = object; for (const int i : range) {
data.nodes = nodes; do_mask_by_color_contiguous_update_node(object, new_mask, invert, preserve_mask, nodes[i]);
data.mask_by_color_floodfill = new_mask; }
data.mask_by_color_vertex = vertex; });
data.mask_by_color_threshold = threshold;
data.mask_by_color_invert = invert;
data.mask_by_color_preserve_mask = preserve_mask;
TaskParallelSettings settings;
BKE_pbvh_parallel_range_settings(&settings, true, nodes.size());
BLI_task_parallel_range(
0, nodes.size(), &data, do_mask_by_color_contiguous_update_nodes_cb, &settings);
MEM_freeN(new_mask); MEM_freeN(new_mask);
} }
static void do_mask_by_color_task_cb(void *__restrict userdata, static void do_mask_by_color_task(Object *ob,
const int n, const float threshold,
const TaskParallelTLS *__restrict /*tls*/) const bool invert,
const bool preserve_mask,
const PBVHVertRef mask_by_color_vertex,
PBVHNode *node)
{ {
SculptThreadedTaskData *data = static_cast<SculptThreadedTaskData *>(userdata); SculptSession *ss = ob->sculpt;
SculptSession *ss = data->ob->sculpt;
SCULPT_undo_push_node(data->ob, data->nodes[n], SCULPT_UNDO_MASK); SCULPT_undo_push_node(ob, node, SCULPT_UNDO_MASK);
bool update_node = false; bool update_node = false;
const float threshold = data->mask_by_color_threshold;
const bool invert = data->mask_by_color_invert;
const bool preserve_mask = data->mask_by_color_preserve_mask;
float active_color[4]; float active_color[4];
SCULPT_vertex_color_get(ss, data->mask_by_color_vertex, active_color); SCULPT_vertex_color_get(ss, mask_by_color_vertex, active_color);
PBVHVertexIter vd; PBVHVertexIter vd;
BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) {
float col[4]; float col[4];
SCULPT_vertex_color_get(ss, vd.vertex, col); SCULPT_vertex_color_get(ss, vd.vertex, col);
@@ -870,7 +860,7 @@ static void do_mask_by_color_task_cb(void *__restrict userdata,
} }
BKE_pbvh_vertex_iter_end; BKE_pbvh_vertex_iter_end;
if (update_node) { if (update_node) {
BKE_pbvh_node_mark_update_mask(data->nodes[n]); BKE_pbvh_node_mark_update_mask(node);
} }
} }
@@ -880,21 +870,16 @@ static void sculpt_mask_by_color_full_mesh(Object *object,
const bool invert, const bool invert,
const bool preserve_mask) const bool preserve_mask)
{ {
using namespace blender;
SculptSession *ss = object->sculpt; SculptSession *ss = object->sculpt;
Vector<PBVHNode *> nodes = blender::bke::pbvh::search_gather(ss->pbvh, {}); Vector<PBVHNode *> nodes = blender::bke::pbvh::search_gather(ss->pbvh, {});
SculptThreadedTaskData data{}; threading::parallel_for(nodes.index_range(), 1, [&](const IndexRange range) {
data.ob = object; for (const int i : range) {
data.nodes = nodes; do_mask_by_color_task(object, threshold, invert, preserve_mask, vertex, nodes[i]);
data.mask_by_color_vertex = vertex; }
data.mask_by_color_threshold = threshold; });
data.mask_by_color_invert = invert;
data.mask_by_color_preserve_mask = preserve_mask;
TaskParallelSettings settings;
BKE_pbvh_parallel_range_settings(&settings, true, nodes.size());
BLI_task_parallel_range(0, nodes.size(), &data, do_mask_by_color_task_cb, &settings);
} }
static int sculpt_mask_by_color_invoke(bContext *C, wmOperator *op, const wmEvent *event) static int sculpt_mask_by_color_invoke(bContext *C, wmOperator *op, const wmEvent *event)
@@ -1004,36 +989,24 @@ enum CavityBakeSettingsSource {
AUTOMASK_SETTINGS_BRUSH AUTOMASK_SETTINGS_BRUSH
}; };
struct AutomaskBakeTaskData { static void sculpt_bake_cavity_exec_task(Object *ob,
SculptSession *ss; AutomaskingCache *automasking,
AutomaskingCache *automasking; const CavityBakeMixMode mode,
Span<PBVHNode *> nodes; const float factor,
CavityBakeMixMode mode; PBVHNode *node)
float factor;
Object *ob;
};
static void sculpt_bake_cavity_exec_task_cb(void *__restrict userdata,
const int n,
const TaskParallelTLS *__restrict /*tls*/)
{ {
AutomaskBakeTaskData *tdata = static_cast<AutomaskBakeTaskData *>(userdata); SculptSession *ss = ob->sculpt;
SculptSession *ss = tdata->ss;
PBVHNode *node = tdata->nodes[n];
PBVHVertexIter vd; PBVHVertexIter vd;
const CavityBakeMixMode mode = tdata->mode;
const float factor = tdata->factor;
SCULPT_undo_push_node(tdata->ob, node, SCULPT_UNDO_MASK); SCULPT_undo_push_node(ob, node, SCULPT_UNDO_MASK);
AutomaskingNodeData automask_data; AutomaskingNodeData automask_data;
SCULPT_automasking_node_begin(tdata->ob, ss, tdata->automasking, &automask_data, node); SCULPT_automasking_node_begin(ob, ss, automasking, &automask_data, node);
BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) { BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) {
SCULPT_automasking_node_update(ss, &automask_data, &vd); SCULPT_automasking_node_update(ss, &automask_data, &vd);
float automask = SCULPT_automasking_factor_get( float automask = SCULPT_automasking_factor_get(automasking, ss, vd.vertex, &automask_data);
tdata->automasking, ss, vd.vertex, &automask_data);
float mask; float mask;
switch (mode) { switch (mode) {
@@ -1068,6 +1041,7 @@ static void sculpt_bake_cavity_exec_task_cb(void *__restrict userdata,
static int sculpt_bake_cavity_exec(bContext *C, wmOperator *op) static int sculpt_bake_cavity_exec(bContext *C, wmOperator *op)
{ {
using namespace blender;
Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C); Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C);
Object *ob = CTX_data_active_object(C); Object *ob = CTX_data_active_object(C);
SculptSession *ss = ob->sculpt; SculptSession *ss = ob->sculpt;
@@ -1087,10 +1061,7 @@ static int sculpt_bake_cavity_exec(bContext *C, wmOperator *op)
Vector<PBVHNode *> nodes = blender::bke::pbvh::search_gather(ss->pbvh, {}); Vector<PBVHNode *> nodes = blender::bke::pbvh::search_gather(ss->pbvh, {});
AutomaskBakeTaskData tdata; /* Set up automasking settings. */
/* Set up automasking settings.
*/
Sculpt sd2 = *sd; Sculpt sd2 = *sd;
CavityBakeSettingsSource src = (CavityBakeSettingsSource)RNA_enum_get(op->ptr, CavityBakeSettingsSource src = (CavityBakeSettingsSource)RNA_enum_get(op->ptr,
@@ -1150,18 +1121,15 @@ static int sculpt_bake_cavity_exec(bContext *C, wmOperator *op)
SCULPT_stroke_id_next(ob); SCULPT_stroke_id_next(ob);
tdata.ob = ob; AutomaskingCache *automasking = SCULPT_automasking_cache_init(&sd2, &brush2, ob);
tdata.mode = mode;
tdata.factor = factor;
tdata.ss = ss;
tdata.nodes = nodes;
tdata.automasking = SCULPT_automasking_cache_init(&sd2, &brush2, ob);
TaskParallelSettings settings; threading::parallel_for(nodes.index_range(), 1, [&](const IndexRange range) {
BKE_pbvh_parallel_range_settings(&settings, true, nodes.size()); for (const int i : range) {
BLI_task_parallel_range(0, nodes.size(), &tdata, sculpt_bake_cavity_exec_task_cb, &settings); sculpt_bake_cavity_exec_task(ob, automasking, mode, factor, nodes[i]);
}
});
SCULPT_automasking_cache_free(tdata.automasking); SCULPT_automasking_cache_free(automasking);
BKE_pbvh_update_vertex_data(ss->pbvh, PBVH_UpdateMask); BKE_pbvh_update_vertex_data(ss->pbvh, PBVH_UpdateMask);
SCULPT_undo_push_end(ob); SCULPT_undo_push_end(ob);

View File

@@ -35,27 +35,22 @@
using blender::Vector; using blender::Vector;
static void do_color_smooth_task_cb_exec(void *__restrict userdata, static void do_color_smooth_task(Object *ob, const Brush *brush, PBVHNode *node)
const int n,
const TaskParallelTLS *__restrict tls)
{ {
SculptThreadedTaskData *data = static_cast<SculptThreadedTaskData *>(userdata); SculptSession *ss = ob->sculpt;
SculptSession *ss = data->ob->sculpt;
const Brush *brush = data->brush;
const float bstrength = ss->cache->bstrength; const float bstrength = ss->cache->bstrength;
PBVHVertexIter vd; PBVHVertexIter vd;
SculptBrushTest test; SculptBrushTest test;
SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape( SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape(
ss, &test, data->brush->falloff_shape); ss, &test, brush->falloff_shape);
const int thread_id = BLI_task_parallel_thread_id(tls); const int thread_id = BLI_task_parallel_thread_id(nullptr);
AutomaskingNodeData automask_data; AutomaskingNodeData automask_data;
SCULPT_automasking_node_begin( SCULPT_automasking_node_begin(ob, ss, ss->cache->automasking, &automask_data, node);
data->ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]);
BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) {
if (!sculpt_brush_test_sq_fn(&test, vd.co)) { if (!sculpt_brush_test_sq_fn(&test, vd.co)) {
continue; continue;
} }
@@ -84,27 +79,27 @@ static void do_color_smooth_task_cb_exec(void *__restrict userdata,
BKE_pbvh_vertex_iter_end; BKE_pbvh_vertex_iter_end;
} }
static void do_paint_brush_task_cb_ex(void *__restrict userdata, static void do_paint_brush_task(Object *ob,
const int n, const Brush *brush,
const TaskParallelTLS *__restrict tls) const float (*mat)[4],
float *wet_mix_sampled_color,
PBVHNode *node)
{ {
SculptThreadedTaskData *data = static_cast<SculptThreadedTaskData *>(userdata); SculptSession *ss = ob->sculpt;
SculptSession *ss = data->ob->sculpt;
const Brush *brush = data->brush;
const float bstrength = fabsf(ss->cache->bstrength); const float bstrength = fabsf(ss->cache->bstrength);
PBVHVertexIter vd; PBVHVertexIter vd;
PBVHColorBufferNode *color_buffer; PBVHColorBufferNode *color_buffer;
SculptOrigVertData orig_data; SculptOrigVertData orig_data;
SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n], SCULPT_UNDO_COLOR); SCULPT_orig_vert_data_init(&orig_data, ob, node, SCULPT_UNDO_COLOR);
color_buffer = BKE_pbvh_node_color_buffer_get(data->nodes[n]); color_buffer = BKE_pbvh_node_color_buffer_get(node);
SculptBrushTest test; SculptBrushTest test;
SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape( SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape(
ss, &test, data->brush->falloff_shape); ss, &test, brush->falloff_shape);
const int thread_id = BLI_task_parallel_thread_id(tls); const int thread_id = BLI_task_parallel_thread_id(nullptr);
float brush_color[4] = {0.0f, 0.0f, 0.0f, 1.0f}; float brush_color[4] = {0.0f, 0.0f, 0.0f, 1.0f};
@@ -115,8 +110,7 @@ static void do_paint_brush_task_cb_ex(void *__restrict userdata,
IMB_colormanagement_srgb_to_scene_linear_v3(brush_color, brush_color); IMB_colormanagement_srgb_to_scene_linear_v3(brush_color, brush_color);
AutomaskingNodeData automask_data; AutomaskingNodeData automask_data;
SCULPT_automasking_node_begin( SCULPT_automasking_node_begin(ob, ss, ss->cache->automasking, &automask_data, node);
data->ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]);
if (brush->flag & BRUSH_USE_GRADIENT) { if (brush->flag & BRUSH_USE_GRADIENT) {
switch (brush->gradient_stroke_mode) { switch (brush->gradient_stroke_mode) {
@@ -136,14 +130,14 @@ static void do_paint_brush_task_cb_ex(void *__restrict userdata,
} }
} }
BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) {
SCULPT_orig_vert_data_update(&orig_data, &vd); SCULPT_orig_vert_data_update(&orig_data, &vd);
bool affect_vertex = false; bool affect_vertex = false;
float distance_to_stroke_location = 0.0f; float distance_to_stroke_location = 0.0f;
if (brush->tip_roundness < 1.0f) { if (brush->tip_roundness < 1.0f) {
affect_vertex = SCULPT_brush_test_cube( affect_vertex = SCULPT_brush_test_cube(
&test, vd.co, data->mat, brush->tip_roundness, brush->tip_scale_x); &test, vd.co, mat, brush->tip_roundness, brush->tip_scale_x);
distance_to_stroke_location = ss->cache->radius * test.dist; distance_to_stroke_location = ss->cache->radius * test.dist;
} }
else { else {
@@ -185,7 +179,7 @@ static void do_paint_brush_task_cb_ex(void *__restrict userdata,
float buffer_color[4]; float buffer_color[4];
mul_v4_v4fl(paint_color, brush_color, fade * ss->cache->paint_brush.flow); mul_v4_v4fl(paint_color, brush_color, fade * ss->cache->paint_brush.flow);
mul_v4_v4fl(wet_mix_color, data->wet_mix_sampled_color, fade * ss->cache->paint_brush.flow); mul_v4_v4fl(wet_mix_color, wet_mix_sampled_color, fade * ss->cache->paint_brush.flow);
/* Interpolate with the wet_mix color for wet paint mixing. */ /* Interpolate with the wet_mix color for wet paint mixing. */
blend_color_interpolate_float( blend_color_interpolate_float(
@@ -207,28 +201,26 @@ static void do_paint_brush_task_cb_ex(void *__restrict userdata,
BKE_pbvh_vertex_iter_end; BKE_pbvh_vertex_iter_end;
} }
struct SampleWetPaintTLSData { struct SampleWetPaintData {
int tot_samples; int tot_samples;
float color[4]; float color[4];
}; };
static void do_sample_wet_paint_task_cb(void *__restrict userdata, static void do_sample_wet_paint_task(SculptSession *ss,
const int n, const Brush *brush,
const TaskParallelTLS *__restrict tls) PBVHNode *node,
SampleWetPaintData *swptd)
{ {
SculptThreadedTaskData *data = static_cast<SculptThreadedTaskData *>(userdata);
SculptSession *ss = data->ob->sculpt;
SampleWetPaintTLSData *swptd = static_cast<SampleWetPaintTLSData *>(tls->userdata_chunk);
PBVHVertexIter vd; PBVHVertexIter vd;
SculptBrushTest test; SculptBrushTest test;
SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape( SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape(
ss, &test, data->brush->falloff_shape); ss, &test, brush->falloff_shape);
test.radius *= data->brush->wet_paint_radius_factor; test.radius *= brush->wet_paint_radius_factor;
test.radius_squared = test.radius * test.radius; test.radius_squared = test.radius * test.radius;
BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) {
if (!sculpt_brush_test_sq_fn(&test, vd.co)) { if (!sculpt_brush_test_sq_fn(&test, vd.co)) {
continue; continue;
} }
@@ -242,23 +234,13 @@ static void do_sample_wet_paint_task_cb(void *__restrict userdata,
BKE_pbvh_vertex_iter_end; BKE_pbvh_vertex_iter_end;
} }
static void sample_wet_paint_reduce(const void *__restrict /*userdata*/,
void *__restrict chunk_join,
void *__restrict chunk)
{
SampleWetPaintTLSData *join = static_cast<SampleWetPaintTLSData *>(chunk_join);
SampleWetPaintTLSData *swptd = static_cast<SampleWetPaintTLSData *>(chunk);
join->tot_samples += swptd->tot_samples;
add_v4_v4(join->color, swptd->color);
}
void SCULPT_do_paint_brush(PaintModeSettings *paint_mode_settings, void SCULPT_do_paint_brush(PaintModeSettings *paint_mode_settings,
Sculpt *sd, Sculpt *sd,
Object *ob, Object *ob,
Span<PBVHNode *> nodes, Span<PBVHNode *> nodes,
Span<PBVHNode *> texnodes) Span<PBVHNode *> texnodes)
{ {
using namespace blender;
if (SCULPT_use_image_paint_brush(paint_mode_settings, ob)) { if (SCULPT_use_image_paint_brush(paint_mode_settings, ob)) {
SCULPT_do_paint_brush_image(paint_mode_settings, sd, ob, texnodes); SCULPT_do_paint_brush_image(paint_mode_settings, sd, ob, texnodes);
return; return;
@@ -294,16 +276,11 @@ void SCULPT_do_paint_brush(PaintModeSettings *paint_mode_settings,
/* Smooth colors mode. */ /* Smooth colors mode. */
if (ss->cache->alt_smooth) { if (ss->cache->alt_smooth) {
SculptThreadedTaskData data{}; threading::parallel_for(nodes.index_range(), 1, [&](const IndexRange range) {
data.sd = sd; for (const int i : range) {
data.ob = ob; do_color_smooth_task(ob, brush, nodes[i]);
data.brush = brush; }
data.nodes = nodes; });
data.mat = mat;
TaskParallelSettings settings;
BKE_pbvh_parallel_range_settings(&settings, true, nodes.size());
BLI_task_parallel_range(0, nodes.size(), &data, do_color_smooth_task_cb_exec, &settings);
return; return;
} }
@@ -312,23 +289,22 @@ void SCULPT_do_paint_brush(PaintModeSettings *paint_mode_settings,
/* Wet paint color sampling. */ /* Wet paint color sampling. */
float wet_color[4] = {0.0f}; float wet_color[4] = {0.0f};
if (ss->cache->paint_brush.wet_mix > 0.0f) { if (ss->cache->paint_brush.wet_mix > 0.0f) {
SculptThreadedTaskData task_data{}; const SampleWetPaintData swptd = threading::parallel_reduce(
task_data.sd = sd; nodes.index_range(),
task_data.ob = ob; 1,
task_data.nodes = nodes; SampleWetPaintData{},
task_data.brush = brush; [&](const IndexRange range, SampleWetPaintData swptd) {
for (const int i : range) {
SampleWetPaintTLSData swptd; do_sample_wet_paint_task(ss, brush, nodes[i], &swptd);
swptd.tot_samples = 0; }
zero_v4(swptd.color); return swptd;
},
TaskParallelSettings settings_sample; [](const SampleWetPaintData &a, const SampleWetPaintData &b) {
BKE_pbvh_parallel_range_settings(&settings_sample, true, nodes.size()); SampleWetPaintData joined{};
settings_sample.func_reduce = sample_wet_paint_reduce; joined.tot_samples = a.tot_samples + b.tot_samples;
settings_sample.userdata_chunk = &swptd; add_v4_v4v4(joined.color, a.color, b.color);
settings_sample.userdata_chunk_size = sizeof(SampleWetPaintTLSData); return joined;
BLI_task_parallel_range( });
0, nodes.size(), &task_data, do_sample_wet_paint_task_cb, &settings_sample);
if (swptd.tot_samples > 0 && is_finite_v4(swptd.color)) { if (swptd.tot_samples > 0 && is_finite_v4(swptd.color)) {
copy_v4_v4(wet_color, swptd.color); copy_v4_v4(wet_color, swptd.color);
@@ -347,35 +323,24 @@ void SCULPT_do_paint_brush(PaintModeSettings *paint_mode_settings,
} }
} }
/* Threaded loop over nodes. */ threading::parallel_for(nodes.index_range(), 1, [&](const IndexRange range) {
SculptThreadedTaskData data{}; for (const int i : range) {
data.sd = sd; do_paint_brush_task(ob, brush, mat, wet_color, nodes[i]);
data.ob = ob; }
data.brush = brush; });
data.nodes = nodes;
data.wet_mix_sampled_color = wet_color;
data.mat = mat;
TaskParallelSettings settings;
BKE_pbvh_parallel_range_settings(&settings, true, nodes.size());
BLI_task_parallel_range(0, nodes.size(), &data, do_paint_brush_task_cb_ex, &settings);
} }
static void do_smear_brush_task_cb_exec(void *__restrict userdata, static void do_smear_brush_task(Object *ob, const Brush *brush, PBVHNode *node)
const int n,
const TaskParallelTLS *__restrict tls)
{ {
SculptThreadedTaskData *data = static_cast<SculptThreadedTaskData *>(userdata); SculptSession *ss = ob->sculpt;
SculptSession *ss = data->ob->sculpt;
const Brush *brush = data->brush;
const float bstrength = ss->cache->bstrength; const float bstrength = ss->cache->bstrength;
PBVHVertexIter vd; PBVHVertexIter vd;
SculptBrushTest test; SculptBrushTest test;
SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape( SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape(
ss, &test, data->brush->falloff_shape); ss, &test, brush->falloff_shape);
const int thread_id = BLI_task_parallel_thread_id(tls); const int thread_id = BLI_task_parallel_thread_id(nullptr);
float brush_delta[3]; float brush_delta[3];
@@ -387,10 +352,9 @@ static void do_smear_brush_task_cb_exec(void *__restrict userdata,
} }
AutomaskingNodeData automask_data; AutomaskingNodeData automask_data;
SCULPT_automasking_node_begin( SCULPT_automasking_node_begin(ob, ss, ss->cache->automasking, &automask_data, node);
data->ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]);
BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) {
if (!sculpt_brush_test_sq_fn(&test, vd.co)) { if (!sculpt_brush_test_sq_fn(&test, vd.co)) {
continue; continue;
} }
@@ -514,22 +478,20 @@ static void do_smear_brush_task_cb_exec(void *__restrict userdata,
BKE_pbvh_vertex_iter_end; BKE_pbvh_vertex_iter_end;
} }
static void do_smear_store_prev_colors_task_cb_exec(void *__restrict userdata, static void do_smear_store_prev_colors_task(SculptSession *ss,
const int n, PBVHNode *node,
const TaskParallelTLS *__restrict /*tls*/) float (*prev_colors)[4])
{ {
SculptThreadedTaskData *data = static_cast<SculptThreadedTaskData *>(userdata);
SculptSession *ss = data->ob->sculpt;
PBVHVertexIter vd; PBVHVertexIter vd;
BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) {
SCULPT_vertex_color_get(ss, vd.vertex, ss->cache->prev_colors[vd.index]); SCULPT_vertex_color_get(ss, vd.vertex, prev_colors[vd.index]);
} }
BKE_pbvh_vertex_iter_end; BKE_pbvh_vertex_iter_end;
} }
void SCULPT_do_smear_brush(Sculpt *sd, Object *ob, Span<PBVHNode *> nodes) void SCULPT_do_smear_brush(Sculpt *sd, Object *ob, Span<PBVHNode *> nodes)
{ {
using namespace blender;
Brush *brush = BKE_paint_brush(&sd->paint); Brush *brush = BKE_paint_brush(&sd->paint);
SculptSession *ss = ob->sculpt; SculptSession *ss = ob->sculpt;
@@ -550,23 +512,25 @@ void SCULPT_do_smear_brush(Sculpt *sd, Object *ob, Span<PBVHNode *> nodes)
BKE_curvemapping_init(brush->curve); BKE_curvemapping_init(brush->curve);
SculptThreadedTaskData data{};
data.sd = sd;
data.ob = ob;
data.brush = brush;
data.nodes = nodes;
TaskParallelSettings settings;
BKE_pbvh_parallel_range_settings(&settings, true, nodes.size());
/* Smooth colors mode. */ /* Smooth colors mode. */
if (ss->cache->alt_smooth) { if (ss->cache->alt_smooth) {
BLI_task_parallel_range(0, nodes.size(), &data, do_color_smooth_task_cb_exec, &settings); threading::parallel_for(nodes.index_range(), 1, [&](const IndexRange range) {
for (const int i : range) {
do_color_smooth_task(ob, brush, nodes[i]);
}
});
} }
else { else {
/* Smear mode. */ /* Smear mode. */
BLI_task_parallel_range( threading::parallel_for(nodes.index_range(), 1, [&](const IndexRange range) {
0, nodes.size(), &data, do_smear_store_prev_colors_task_cb_exec, &settings); for (const int i : range) {
BLI_task_parallel_range(0, nodes.size(), &data, do_smear_brush_task_cb_exec, &settings); do_smear_store_prev_colors_task(ss, nodes[i], ss->cache->prev_colors);
}
});
threading::parallel_for(nodes.index_range(), 1, [&](const IndexRange range) {
for (const int i : range) {
do_smear_brush_task(ob, brush, nodes[i]);
}
});
} }
} }

View File

@@ -137,27 +137,22 @@ static void pose_solve_scale_chain(SculptPoseIKChain *ik_chain, const float scal
} }
} }
static void do_pose_brush_task_cb_ex(void *__restrict userdata, static void do_pose_brush_task(Object *ob, const Brush *brush, PBVHNode *node)
const int n,
const TaskParallelTLS *__restrict /*tls*/)
{ {
SculptThreadedTaskData *data = static_cast<SculptThreadedTaskData *>(userdata); SculptSession *ss = ob->sculpt;
SculptSession *ss = data->ob->sculpt;
SculptPoseIKChain *ik_chain = ss->cache->pose_ik_chain; SculptPoseIKChain *ik_chain = ss->cache->pose_ik_chain;
SculptPoseIKChainSegment *segments = ik_chain->segments; SculptPoseIKChainSegment *segments = ik_chain->segments;
const Brush *brush = data->brush;
PBVHVertexIter vd; PBVHVertexIter vd;
float disp[3], new_co[3]; float disp[3], new_co[3];
float final_pos[3]; float final_pos[3];
SculptOrigVertData orig_data; SculptOrigVertData orig_data;
SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n], SCULPT_UNDO_COORDS); SCULPT_orig_vert_data_init(&orig_data, ob, node, SCULPT_UNDO_COORDS);
AutomaskingNodeData automask_data; AutomaskingNodeData automask_data;
SCULPT_automasking_node_begin( SCULPT_automasking_node_begin(ob, ss, ss->cache->automasking, &automask_data, node);
data->ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]);
BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) {
SCULPT_orig_vert_data_update(&orig_data, &vd); SCULPT_orig_vert_data_update(&orig_data, &vd);
SCULPT_automasking_node_update(ss, &automask_data, &vd); SCULPT_automasking_node_update(ss, &automask_data, &vd);
@@ -203,35 +198,36 @@ static void do_pose_brush_task_cb_ex(void *__restrict userdata,
BKE_pbvh_vertex_iter_end; BKE_pbvh_vertex_iter_end;
} }
struct PoseGrowFactorTLSData { struct PoseGrowFactorData {
float pos_avg[3]; float pos_avg[3];
int pos_count; int pos_count;
}; };
static void pose_brush_grow_factor_task_cb_ex(void *__restrict userdata, static void pose_brush_grow_factor_task(Object *ob,
const int n, const float pose_initial_co[3],
const TaskParallelTLS *__restrict tls) const float *prev_mask,
float *pose_factor,
PBVHNode *node,
PoseGrowFactorData *gftd)
{ {
SculptThreadedTaskData *data = static_cast<SculptThreadedTaskData *>(userdata); SculptSession *ss = ob->sculpt;
PoseGrowFactorTLSData *gftd = static_cast<PoseGrowFactorTLSData *>(tls->userdata_chunk); const char symm = SCULPT_mesh_symmetry_xyz_get(ob);
SculptSession *ss = data->ob->sculpt;
const char symm = SCULPT_mesh_symmetry_xyz_get(data->ob);
PBVHVertexIter vd; PBVHVertexIter vd;
BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) {
SculptVertexNeighborIter ni; SculptVertexNeighborIter ni;
float max = 0.0f; float max = 0.0f;
/* Grow the factor. */ /* Grow the factor. */
SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vd.vertex, ni) { SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vd.vertex, ni) {
float vmask_f = data->prev_mask[ni.index]; float vmask_f = prev_mask[ni.index];
max = MAX2(vmask_f, max); max = MAX2(vmask_f, max);
} }
SCULPT_VERTEX_NEIGHBORS_ITER_END(ni); SCULPT_VERTEX_NEIGHBORS_ITER_END(ni);
/* Keep the count of the vertices that where added to the factors in this grow iteration. */ /* Keep the count of the vertices that where added to the factors in this grow iteration. */
if (max > data->prev_mask[vd.index]) { if (max > prev_mask[vd.index]) {
data->pose_factor[vd.index] = max; pose_factor[vd.index] = max;
if (SCULPT_check_vertex_pivot_symmetry(vd.co, data->pose_initial_co, symm)) { if (SCULPT_check_vertex_pivot_symmetry(vd.co, pose_initial_co, symm)) {
add_v3_v3(gftd->pos_avg, vd.co); add_v3_v3(gftd->pos_avg, vd.co);
gftd->pos_count++; gftd->pos_count++;
} }
@@ -245,16 +241,15 @@ static void pose_brush_grow_factor_reduce(const void *__restrict /*userdata*/,
void *__restrict chunk_join, void *__restrict chunk_join,
void *__restrict chunk) void *__restrict chunk)
{ {
PoseGrowFactorTLSData *join = static_cast<PoseGrowFactorTLSData *>(chunk_join); PoseGrowFactorData *join = static_cast<PoseGrowFactorData *>(chunk_join);
PoseGrowFactorTLSData *gftd = static_cast<PoseGrowFactorTLSData *>(chunk); PoseGrowFactorData *gftd = static_cast<PoseGrowFactorData *>(chunk);
add_v3_v3(join->pos_avg, gftd->pos_avg); add_v3_v3(join->pos_avg, gftd->pos_avg);
join->pos_count += gftd->pos_count; join->pos_count += gftd->pos_count;
} }
/* Grow the factor until its boundary is near to the offset pose origin or outside the target /* Grow the factor until its boundary is near to the offset pose origin or outside the target
* distance. */ * distance. */
static void sculpt_pose_grow_pose_factor(Sculpt *sd, static void sculpt_pose_grow_pose_factor(Object *ob,
Object *ob,
SculptSession *ss, SculptSession *ss,
float pose_origin[3], float pose_origin[3],
float pose_target[3], float pose_target[3],
@@ -262,35 +257,40 @@ static void sculpt_pose_grow_pose_factor(Sculpt *sd,
float *r_pose_origin, float *r_pose_origin,
float *pose_factor) float *pose_factor)
{ {
using namespace blender;
PBVH *pbvh = ob->sculpt->pbvh; PBVH *pbvh = ob->sculpt->pbvh;
Vector<PBVHNode *> nodes = blender::bke::pbvh::search_gather(pbvh, {}); Vector<PBVHNode *> nodes = blender::bke::pbvh::search_gather(pbvh, {});
SculptThreadedTaskData data{}; PoseGrowFactorData gftd;
data.sd = sd;
data.ob = ob;
data.nodes = nodes;
data.pose_factor = pose_factor;
data.pose_initial_co = pose_target;
TaskParallelSettings settings;
PoseGrowFactorTLSData gftd;
gftd.pos_count = 0; gftd.pos_count = 0;
zero_v3(gftd.pos_avg); zero_v3(gftd.pos_avg);
BKE_pbvh_parallel_range_settings(&settings, true, nodes.size());
settings.func_reduce = pose_brush_grow_factor_reduce;
settings.userdata_chunk = &gftd;
settings.userdata_chunk_size = sizeof(PoseGrowFactorTLSData);
bool grow_next_iteration = true; bool grow_next_iteration = true;
float prev_len = FLT_MAX; float prev_len = FLT_MAX;
data.prev_mask = static_cast<float *>( float *prev_mask = static_cast<float *>(
MEM_malloc_arrayN(SCULPT_vertex_count_get(ss), sizeof(float), __func__)); MEM_malloc_arrayN(SCULPT_vertex_count_get(ss), sizeof(float), __func__));
while (grow_next_iteration) { while (grow_next_iteration) {
zero_v3(gftd.pos_avg); zero_v3(gftd.pos_avg);
gftd.pos_count = 0; gftd.pos_count = 0;
memcpy(data.prev_mask, pose_factor, SCULPT_vertex_count_get(ss) * sizeof(float)); memcpy(prev_mask, pose_factor, SCULPT_vertex_count_get(ss) * sizeof(float));
BLI_task_parallel_range(0, nodes.size(), &data, pose_brush_grow_factor_task_cb_ex, &settings);
gftd = threading::parallel_reduce(
nodes.index_range(),
1,
gftd,
[&](const IndexRange range, PoseGrowFactorData gftd) {
for (const int i : range) {
pose_brush_grow_factor_task(ob, pose_target, prev_mask, pose_factor, nodes[i], &gftd);
}
return gftd;
},
[](const PoseGrowFactorData &a, const PoseGrowFactorData &b) {
PoseGrowFactorData joined;
add_v3_v3v3(joined.pos_avg, a.pos_avg, b.pos_avg);
joined.pos_count = a.pos_count + b.pos_count;
return joined;
});
if (gftd.pos_count != 0) { if (gftd.pos_count != 0) {
mul_v3_fl(gftd.pos_avg, 1.0f / float(gftd.pos_count)); mul_v3_fl(gftd.pos_avg, 1.0f / float(gftd.pos_count));
@@ -305,7 +305,7 @@ static void sculpt_pose_grow_pose_factor(Sculpt *sd,
} }
else { else {
grow_next_iteration = false; grow_next_iteration = false;
memcpy(pose_factor, data.prev_mask, SCULPT_vertex_count_get(ss) * sizeof(float)); memcpy(pose_factor, prev_mask, SCULPT_vertex_count_get(ss) * sizeof(float));
} }
} }
else { else {
@@ -321,7 +321,7 @@ static void sculpt_pose_grow_pose_factor(Sculpt *sd,
if (r_pose_origin) { if (r_pose_origin) {
copy_v3_v3(r_pose_origin, gftd.pos_avg); copy_v3_v3(r_pose_origin, gftd.pos_avg);
} }
memcpy(pose_factor, data.prev_mask, SCULPT_vertex_count_get(ss) * sizeof(float)); memcpy(pose_factor, prev_mask, SCULPT_vertex_count_get(ss) * sizeof(float));
} }
} }
} }
@@ -332,7 +332,7 @@ static void sculpt_pose_grow_pose_factor(Sculpt *sd,
grow_next_iteration = false; grow_next_iteration = false;
} }
} }
MEM_freeN(data.prev_mask); MEM_freeN(prev_mask);
} }
static bool sculpt_pose_brush_is_vertex_inside_brush_radius(const float vertex[3], static bool sculpt_pose_brush_is_vertex_inside_brush_radius(const float vertex[3],
@@ -574,29 +574,25 @@ void SCULPT_pose_calc_pose_data(Sculpt *sd,
*/ */
if (pose_offset != 0.0f && r_pose_factor) { if (pose_offset != 0.0f && r_pose_factor) {
sculpt_pose_grow_pose_factor( sculpt_pose_grow_pose_factor(
sd, ob, ss, fdata.pose_origin, fdata.pose_origin, 0, nullptr, r_pose_factor); ob, ss, fdata.pose_origin, fdata.pose_origin, 0, nullptr, r_pose_factor);
} }
} }
static void pose_brush_init_task_cb_ex(void *__restrict userdata, static void pose_brush_init_task(SculptSession *ss, float *pose_factor, PBVHNode *node)
const int n,
const TaskParallelTLS *__restrict /*tls*/)
{ {
SculptThreadedTaskData *data = static_cast<SculptThreadedTaskData *>(userdata);
SculptSession *ss = data->ob->sculpt;
PBVHVertexIter vd; PBVHVertexIter vd;
BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) {
SculptVertexNeighborIter ni; SculptVertexNeighborIter ni;
float avg = 0.0f; float avg = 0.0f;
int total = 0; int total = 0;
SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vd.vertex, ni) { SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vd.vertex, ni) {
avg += data->pose_factor[ni.index]; avg += pose_factor[ni.index];
total++; total++;
} }
SCULPT_VERTEX_NEIGHBORS_ITER_END(ni); SCULPT_VERTEX_NEIGHBORS_ITER_END(ni);
if (total > 0) { if (total > 0) {
data->pose_factor[vd.index] = avg / total; pose_factor[vd.index] = avg / total;
} }
} }
BKE_pbvh_vertex_iter_end; BKE_pbvh_vertex_iter_end;
@@ -705,8 +701,7 @@ static SculptPoseIKChain *pose_ik_chain_init_topology(Sculpt *sd,
for (int i = 1; i < ik_chain->tot_segments; i++) { for (int i = 1; i < ik_chain->tot_segments; i++) {
/* Grow the factors to get the new segment origin. */ /* Grow the factors to get the new segment origin. */
sculpt_pose_grow_pose_factor(sd, sculpt_pose_grow_pose_factor(ob,
ob,
ss, ss,
nullptr, nullptr,
next_chain_segment_target, next_chain_segment_target,
@@ -979,27 +974,24 @@ SculptPoseIKChain *SCULPT_pose_ik_chain_init(Sculpt *sd,
void SCULPT_pose_brush_init(Sculpt *sd, Object *ob, SculptSession *ss, Brush *br) void SCULPT_pose_brush_init(Sculpt *sd, Object *ob, SculptSession *ss, Brush *br)
{ {
using namespace blender;
PBVH *pbvh = ob->sculpt->pbvh; PBVH *pbvh = ob->sculpt->pbvh;
Vector<PBVHNode *> nodes = blender::bke::pbvh::search_gather(pbvh, {}); Vector<PBVHNode *> nodes = blender::bke::pbvh::search_gather(pbvh, {});
SculptThreadedTaskData data{};
data.sd = sd;
data.ob = ob;
data.brush = br;
data.nodes = nodes;
/* Init the IK chain that is going to be used to deform the vertices. */ /* Init the IK chain that is going to be used to deform the vertices. */
ss->cache->pose_ik_chain = SCULPT_pose_ik_chain_init( ss->cache->pose_ik_chain = SCULPT_pose_ik_chain_init(
sd, ob, ss, br, ss->cache->true_location, ss->cache->radius); sd, ob, ss, br, ss->cache->true_location, ss->cache->radius);
/* Smooth the weights of each segment for cleaner deformation. */ /* Smooth the weights of each segment for cleaner deformation. */
for (int ik = 0; ik < ss->cache->pose_ik_chain->tot_segments; ik++) { for (int ik = 0; ik < ss->cache->pose_ik_chain->tot_segments; ik++) {
data.pose_factor = ss->cache->pose_ik_chain->segments[ik].weights; float *pose_factor = ss->cache->pose_ik_chain->segments[ik].weights;
for (int i = 0; i < br->pose_smooth_iterations; i++) { for (int i = 0; i < br->pose_smooth_iterations; i++) {
TaskParallelSettings settings; threading::parallel_for(nodes.index_range(), 1, [&](const IndexRange range) {
BKE_pbvh_parallel_range_settings(&settings, true, nodes.size()); for (const int i : range) {
BLI_task_parallel_range(0, nodes.size(), &data, pose_brush_init_task_cb_ex, &settings); pose_brush_init_task(ss, pose_factor, nodes[i]);
}
});
} }
} }
} }
@@ -1129,6 +1121,7 @@ static void sculpt_pose_align_pivot_local_space(float r_mat[4][4],
void SCULPT_do_pose_brush(Sculpt *sd, Object *ob, Span<PBVHNode *> nodes) void SCULPT_do_pose_brush(Sculpt *sd, Object *ob, Span<PBVHNode *> nodes)
{ {
using namespace blender;
SculptSession *ss = ob->sculpt; SculptSession *ss = ob->sculpt;
Brush *brush = BKE_paint_brush(&sd->paint); Brush *brush = BKE_paint_brush(&sd->paint);
const ePaintSymmetryFlags symm = SCULPT_mesh_symmetry_xyz_get(ob); const ePaintSymmetryFlags symm = SCULPT_mesh_symmetry_xyz_get(ob);
@@ -1212,15 +1205,11 @@ void SCULPT_do_pose_brush(Sculpt *sd, Object *ob, Span<PBVHNode *> nodes)
} }
} }
SculptThreadedTaskData data{}; threading::parallel_for(nodes.index_range(), 1, [&](const IndexRange range) {
data.sd = sd; for (const int i : range) {
data.ob = ob; do_pose_brush_task(ob, brush, nodes[i]);
data.brush = brush; }
data.nodes = nodes; });
TaskParallelSettings settings;
BKE_pbvh_parallel_range_settings(&settings, true, nodes.size());
BLI_task_parallel_range(0, nodes.size(), &data, do_pose_brush_task_cb_ex, &settings);
} }
void SCULPT_pose_ik_chain_free(SculptPoseIKChain *ik_chain) void SCULPT_pose_ik_chain_free(SculptPoseIKChain *ik_chain)

View File

@@ -177,14 +177,12 @@ void SCULPT_neighbor_color_average(SculptSession *ss, float result[4], PBVHVertR
} }
} }
static void do_enhance_details_brush_task_cb_ex(void *__restrict userdata, static void do_enhance_details_brush_task(Object *ob,
const int n, Sculpt *sd,
const TaskParallelTLS *__restrict tls) const Brush *brush,
PBVHNode *node)
{ {
SculptThreadedTaskData *data = static_cast<SculptThreadedTaskData *>(userdata); SculptSession *ss = ob->sculpt;
SculptSession *ss = data->ob->sculpt;
Sculpt *sd = data->sd;
const Brush *brush = data->brush;
PBVHVertexIter vd; PBVHVertexIter vd;
@@ -193,14 +191,13 @@ static void do_enhance_details_brush_task_cb_ex(void *__restrict userdata,
SculptBrushTest test; SculptBrushTest test;
SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape( SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape(
ss, &test, data->brush->falloff_shape); ss, &test, brush->falloff_shape);
const int thread_id = BLI_task_parallel_thread_id(tls); const int thread_id = BLI_task_parallel_thread_id(nullptr);
AutomaskingNodeData automask_data; AutomaskingNodeData automask_data;
SCULPT_automasking_node_begin( SCULPT_automasking_node_begin(ob, ss, ss->cache->automasking, &automask_data, node);
data->ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]);
BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) {
if (!sculpt_brush_test_sq_fn(&test, vd.co)) { if (!sculpt_brush_test_sq_fn(&test, vd.co)) {
continue; continue;
} }
@@ -231,6 +228,7 @@ static void do_enhance_details_brush_task_cb_ex(void *__restrict userdata,
static void SCULPT_enhance_details_brush(Sculpt *sd, Object *ob, Span<PBVHNode *> nodes) static void SCULPT_enhance_details_brush(Sculpt *sd, Object *ob, Span<PBVHNode *> nodes)
{ {
using namespace blender;
SculptSession *ss = ob->sculpt; SculptSession *ss = ob->sculpt;
Brush *brush = BKE_paint_brush(&sd->paint); Brush *brush = BKE_paint_brush(&sd->paint);
@@ -251,27 +249,21 @@ static void SCULPT_enhance_details_brush(Sculpt *sd, Object *ob, Span<PBVHNode *
} }
} }
SculptThreadedTaskData data{}; threading::parallel_for(nodes.index_range(), 1, [&](const IndexRange range) {
data.sd = sd; for (const int i : range) {
data.ob = ob; do_enhance_details_brush_task(ob, sd, brush, nodes[i]);
data.brush = brush; }
data.nodes = nodes; });
TaskParallelSettings settings;
BKE_pbvh_parallel_range_settings(&settings, true, nodes.size());
BLI_task_parallel_range(0, nodes.size(), &data, do_enhance_details_brush_task_cb_ex, &settings);
} }
static void do_smooth_brush_task_cb_ex(void *__restrict userdata, static void do_smooth_brush_task(Object *ob,
const int n, Sculpt *sd,
const TaskParallelTLS *__restrict tls) const Brush *brush,
const bool smooth_mask,
float bstrength,
PBVHNode *node)
{ {
SculptThreadedTaskData *data = static_cast<SculptThreadedTaskData *>(userdata); SculptSession *ss = ob->sculpt;
SculptSession *ss = data->ob->sculpt;
Sculpt *sd = data->sd;
const Brush *brush = data->brush;
const bool smooth_mask = data->smooth_mask;
float bstrength = data->strength;
PBVHVertexIter vd; PBVHVertexIter vd;
@@ -279,14 +271,13 @@ static void do_smooth_brush_task_cb_ex(void *__restrict userdata,
SculptBrushTest test; SculptBrushTest test;
SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape( SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape(
ss, &test, data->brush->falloff_shape); ss, &test, brush->falloff_shape);
const int thread_id = BLI_task_parallel_thread_id(tls); const int thread_id = BLI_task_parallel_thread_id(nullptr);
AutomaskingNodeData automask_data; AutomaskingNodeData automask_data;
SCULPT_automasking_node_begin( SCULPT_automasking_node_begin(ob, ss, ss->cache->automasking, &automask_data, node);
data->ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]);
BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) {
if (!sculpt_brush_test_sq_fn(&test, vd.co)) { if (!sculpt_brush_test_sq_fn(&test, vd.co)) {
continue; continue;
} }
@@ -327,6 +318,7 @@ static void do_smooth_brush_task_cb_ex(void *__restrict userdata,
void SCULPT_smooth( void SCULPT_smooth(
Sculpt *sd, Object *ob, Span<PBVHNode *> nodes, float bstrength, const bool smooth_mask) Sculpt *sd, Object *ob, Span<PBVHNode *> nodes, float bstrength, const bool smooth_mask)
{ {
using namespace blender;
SculptSession *ss = ob->sculpt; SculptSession *ss = ob->sculpt;
Brush *brush = BKE_paint_brush(&sd->paint); Brush *brush = BKE_paint_brush(&sd->paint);
@@ -345,18 +337,11 @@ void SCULPT_smooth(
for (iteration = 0; iteration <= count; iteration++) { for (iteration = 0; iteration <= count; iteration++) {
const float strength = (iteration != count) ? 1.0f : last; const float strength = (iteration != count) ? 1.0f : last;
threading::parallel_for(nodes.index_range(), 1, [&](const IndexRange range) {
SculptThreadedTaskData data{}; for (const int i : range) {
data.sd = sd; do_smooth_brush_task(ob, sd, brush, smooth_mask, strength, nodes[i]);
data.ob = ob; }
data.brush = brush; });
data.nodes = nodes;
data.smooth_mask = smooth_mask;
data.strength = strength;
TaskParallelSettings settings;
BKE_pbvh_parallel_range_settings(&settings, true, nodes.size());
BLI_task_parallel_range(0, nodes.size(), &data, do_smooth_brush_task_cb_ex, &settings);
} }
} }
@@ -430,12 +415,9 @@ void SCULPT_surface_smooth_displace_step(SculptSession *ss,
} }
} }
static void SCULPT_do_surface_smooth_brush_laplacian_task_cb_ex( static void do_surface_smooth_brush_laplacian_task(Object *ob, const Brush *brush, PBVHNode *node)
void *__restrict userdata, const int n, const TaskParallelTLS *__restrict tls)
{ {
SculptThreadedTaskData *data = static_cast<SculptThreadedTaskData *>(userdata); SculptSession *ss = ob->sculpt;
SculptSession *ss = data->ob->sculpt;
const Brush *brush = data->brush;
const float bstrength = ss->cache->bstrength; const float bstrength = ss->cache->bstrength;
float alpha = brush->surface_smooth_shape_preservation; float alpha = brush->surface_smooth_shape_preservation;
@@ -444,15 +426,14 @@ static void SCULPT_do_surface_smooth_brush_laplacian_task_cb_ex(
SculptBrushTest test; SculptBrushTest test;
SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape( SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape(
ss, &test, data->brush->falloff_shape); ss, &test, brush->falloff_shape);
const int thread_id = BLI_task_parallel_thread_id(tls); const int thread_id = BLI_task_parallel_thread_id(nullptr);
SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n], SCULPT_UNDO_COORDS); SCULPT_orig_vert_data_init(&orig_data, ob, node, SCULPT_UNDO_COORDS);
AutomaskingNodeData automask_data; AutomaskingNodeData automask_data;
SCULPT_automasking_node_begin( SCULPT_automasking_node_begin(ob, ss, ss->cache->automasking, &automask_data, node);
data->ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]);
BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) {
SCULPT_orig_vert_data_update(&orig_data, &vd); SCULPT_orig_vert_data_update(&orig_data, &vd);
if (!sculpt_brush_test_sq_fn(&test, vd.co)) { if (!sculpt_brush_test_sq_fn(&test, vd.co)) {
continue; continue;
@@ -482,12 +463,9 @@ static void SCULPT_do_surface_smooth_brush_laplacian_task_cb_ex(
BKE_pbvh_vertex_iter_end; BKE_pbvh_vertex_iter_end;
} }
static void SCULPT_do_surface_smooth_brush_displace_task_cb_ex( static void do_surface_smooth_brush_displace_task(Object *ob, const Brush *brush, PBVHNode *node)
void *__restrict userdata, const int n, const TaskParallelTLS *__restrict tls)
{ {
SculptThreadedTaskData *data = static_cast<SculptThreadedTaskData *>(userdata); SculptSession *ss = ob->sculpt;
SculptSession *ss = data->ob->sculpt;
const Brush *brush = data->brush;
const float bstrength = ss->cache->bstrength; const float bstrength = ss->cache->bstrength;
const float beta = brush->surface_smooth_current_vertex; const float beta = brush->surface_smooth_current_vertex;
@@ -495,13 +473,12 @@ static void SCULPT_do_surface_smooth_brush_displace_task_cb_ex(
SculptBrushTest test; SculptBrushTest test;
SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape( SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape(
ss, &test, data->brush->falloff_shape); ss, &test, brush->falloff_shape);
const int thread_id = BLI_task_parallel_thread_id(tls); const int thread_id = BLI_task_parallel_thread_id(nullptr);
AutomaskingNodeData automask_data; AutomaskingNodeData automask_data;
SCULPT_automasking_node_begin( SCULPT_automasking_node_begin(ob, ss, ss->cache->automasking, &automask_data, node);
data->ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]);
BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) {
if (!sculpt_brush_test_sq_fn(&test, vd.co)) { if (!sculpt_brush_test_sq_fn(&test, vd.co)) {
continue; continue;
} }
@@ -526,21 +503,21 @@ static void SCULPT_do_surface_smooth_brush_displace_task_cb_ex(
void SCULPT_do_surface_smooth_brush(Sculpt *sd, Object *ob, Span<PBVHNode *> nodes) void SCULPT_do_surface_smooth_brush(Sculpt *sd, Object *ob, Span<PBVHNode *> nodes)
{ {
using namespace blender;
Brush *brush = BKE_paint_brush(&sd->paint); Brush *brush = BKE_paint_brush(&sd->paint);
/* Threaded loop over nodes. */
SculptThreadedTaskData data{};
data.sd = sd;
data.ob = ob;
data.brush = brush;
data.nodes = nodes;
TaskParallelSettings settings; TaskParallelSettings settings;
BKE_pbvh_parallel_range_settings(&settings, true, nodes.size()); BKE_pbvh_parallel_range_settings(&settings, true, nodes.size());
for (int i = 0; i < brush->surface_smooth_iterations; i++) { for (int i = 0; i < brush->surface_smooth_iterations; i++) {
BLI_task_parallel_range( threading::parallel_for(nodes.index_range(), 1, [&](const IndexRange range) {
0, nodes.size(), &data, SCULPT_do_surface_smooth_brush_laplacian_task_cb_ex, &settings); for (const int i : range) {
BLI_task_parallel_range( do_surface_smooth_brush_laplacian_task(ob, brush, nodes[i]);
0, nodes.size(), &data, SCULPT_do_surface_smooth_brush_displace_task_cb_ex, &settings); }
});
threading::parallel_for(nodes.index_range(), 1, [&](const IndexRange range) {
for (const int i : range) {
do_surface_smooth_brush_displace_task(ob, brush, nodes[i]);
}
});
} }
} }

View File

@@ -137,21 +137,16 @@ static void sculpt_transform_matrices_init(SculptSession *ss,
} }
} }
static void sculpt_transform_task_cb(void *__restrict userdata, static void sculpt_transform_task(Object *ob, const float transform_mats[8][4][4], PBVHNode *node)
const int i,
const TaskParallelTLS *__restrict /*tls*/)
{ {
SculptSession *ss = ob->sculpt;
SculptThreadedTaskData *data = static_cast<SculptThreadedTaskData *>(userdata);
SculptSession *ss = data->ob->sculpt;
PBVHNode *node = data->nodes[i];
SculptOrigVertData orig_data; SculptOrigVertData orig_data;
SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[i], SCULPT_UNDO_COORDS); SCULPT_orig_vert_data_init(&orig_data, ob, node, SCULPT_UNDO_COORDS);
PBVHVertexIter vd; PBVHVertexIter vd;
SCULPT_undo_push_node(data->ob, node, SCULPT_UNDO_COORDS); SCULPT_undo_push_node(ob, node, SCULPT_UNDO_COORDS);
BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) { BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) {
SCULPT_orig_vert_data_update(&orig_data, &vd); SCULPT_orig_vert_data_update(&orig_data, &vd);
float *start_co; float *start_co;
@@ -170,7 +165,7 @@ static void sculpt_transform_task_cb(void *__restrict userdata,
} }
copy_v3_v3(transformed_co, start_co); copy_v3_v3(transformed_co, start_co);
mul_m4_v3(data->transform_mats[int(symm_area)], transformed_co); mul_m4_v3(transform_mats[int(symm_area)], transformed_co);
sub_v3_v3v3(disp, transformed_co, start_co); sub_v3_v3v3(disp, transformed_co, start_co);
mul_v3_fl(disp, 1.0f - fade); mul_v3_fl(disp, 1.0f - fade);
add_v3_v3v3(vd.co, start_co, disp); add_v3_v3v3(vd.co, start_co, disp);
@@ -184,40 +179,38 @@ static void sculpt_transform_task_cb(void *__restrict userdata,
BKE_pbvh_node_mark_update(node); BKE_pbvh_node_mark_update(node);
} }
static void sculpt_transform_all_vertices(Sculpt *sd, Object *ob) static void sculpt_transform_all_vertices(Object *ob)
{ {
using namespace blender;
SculptSession *ss = ob->sculpt; SculptSession *ss = ob->sculpt;
const ePaintSymmetryFlags symm = SCULPT_mesh_symmetry_xyz_get(ob); const ePaintSymmetryFlags symm = SCULPT_mesh_symmetry_xyz_get(ob);
SculptThreadedTaskData data{}; float transform_mats[8][4][4];
data.sd = sd;
data.ob = ob;
data.nodes = ss->filter_cache->nodes;
sculpt_transform_matrices_init( sculpt_transform_matrices_init(
ss, symm, ss->filter_cache->transform_displacement_mode, data.transform_mats); ss, symm, ss->filter_cache->transform_displacement_mode, transform_mats);
/* Regular transform applies all symmetry passes at once as it is split by symmetry areas /* Regular transform applies all symmetry passes at once as it is split by symmetry areas
* (each vertex can only be transformed once by the transform matrix of its area). */ * (each vertex can only be transformed once by the transform matrix of its area). */
TaskParallelSettings settings; threading::parallel_for(ss->filter_cache->nodes.index_range(), 1, [&](const IndexRange range) {
BKE_pbvh_parallel_range_settings(&settings, true, ss->filter_cache->nodes.size()); for (const int i : range) {
BLI_task_parallel_range( sculpt_transform_task(ob, transform_mats, ss->filter_cache->nodes[i]);
0, ss->filter_cache->nodes.size(), &data, sculpt_transform_task_cb, &settings); }
});
} }
static void sculpt_elastic_transform_task_cb(void *__restrict userdata, static void sculpt_elastic_transform_task(Object *ob,
const int i, const float transform_radius,
const TaskParallelTLS *__restrict /*tls*/) const float elastic_transform_mat[4][4],
const float elastic_transform_pivot[3],
PBVHNode *node)
{ {
SculptThreadedTaskData *data = static_cast<SculptThreadedTaskData *>(userdata); SculptSession *ss = ob->sculpt;
SculptSession *ss = data->ob->sculpt;
PBVHNode *node = data->nodes[i];
float(*proxy)[3] = BKE_pbvh_node_add_proxy(ss->pbvh, data->nodes[i])->co; float(*proxy)[3] = BKE_pbvh_node_add_proxy(ss->pbvh, node)->co;
SculptOrigVertData orig_data; SculptOrigVertData orig_data;
SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[i], SCULPT_UNDO_COORDS); SCULPT_orig_vert_data_init(&orig_data, ob, node, SCULPT_UNDO_COORDS);
KelvinletParams params; KelvinletParams params;
/* TODO(pablodp606): These parameters can be exposed if needed as transform strength and volume /* TODO(pablodp606): These parameters can be exposed if needed as transform strength and volume
@@ -226,10 +219,9 @@ static void sculpt_elastic_transform_task_cb(void *__restrict userdata,
const float force = 1.0f; const float force = 1.0f;
const float shear_modulus = 1.0f; const float shear_modulus = 1.0f;
const float poisson_ratio = 0.4f; const float poisson_ratio = 0.4f;
BKE_kelvinlet_init_params( BKE_kelvinlet_init_params(&params, transform_radius, force, shear_modulus, poisson_ratio);
&params, data->elastic_transform_radius, force, shear_modulus, poisson_ratio);
SCULPT_undo_push_node(data->ob, node, SCULPT_UNDO_COORDS); SCULPT_undo_push_node(ob, node, SCULPT_UNDO_COORDS);
PBVHVertexIter vd; PBVHVertexIter vd;
BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) { BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) {
@@ -239,11 +231,11 @@ static void sculpt_elastic_transform_task_cb(void *__restrict userdata,
copy_v3_v3(orig_co, orig_data.co); copy_v3_v3(orig_co, orig_data.co);
copy_v3_v3(transformed_co, vd.co); copy_v3_v3(transformed_co, vd.co);
mul_m4_v3(data->elastic_transform_mat, transformed_co); mul_m4_v3(elastic_transform_mat, transformed_co);
sub_v3_v3v3(disp, transformed_co, vd.co); sub_v3_v3v3(disp, transformed_co, vd.co);
float final_disp[3]; float final_disp[3];
BKE_kelvinlet_grab_triscale(final_disp, &params, vd.co, data->elastic_transform_pivot, disp); BKE_kelvinlet_grab_triscale(final_disp, &params, vd.co, elastic_transform_pivot, disp);
mul_v3_fl(final_disp, 20.0f * (1.0f - fade)); mul_v3_fl(final_disp, 20.0f * (1.0f - fade));
copy_v3_v3(proxy[vd.i], final_disp); copy_v3_v3(proxy[vd.i], final_disp);
@@ -259,20 +251,16 @@ static void sculpt_elastic_transform_task_cb(void *__restrict userdata,
static void sculpt_transform_radius_elastic(Sculpt *sd, Object *ob, const float transform_radius) static void sculpt_transform_radius_elastic(Sculpt *sd, Object *ob, const float transform_radius)
{ {
using namespace blender;
SculptSession *ss = ob->sculpt; SculptSession *ss = ob->sculpt;
BLI_assert(ss->filter_cache->transform_displacement_mode == BLI_assert(ss->filter_cache->transform_displacement_mode ==
SCULPT_TRANSFORM_DISPLACEMENT_INCREMENTAL); SCULPT_TRANSFORM_DISPLACEMENT_INCREMENTAL);
const ePaintSymmetryFlags symm = SCULPT_mesh_symmetry_xyz_get(ob); const ePaintSymmetryFlags symm = SCULPT_mesh_symmetry_xyz_get(ob);
SculptThreadedTaskData data{}; float transform_mats[8][4][4];
data.sd = sd;
data.ob = ob;
data.nodes = ss->filter_cache->nodes;
data.elastic_transform_radius = transform_radius;
sculpt_transform_matrices_init( sculpt_transform_matrices_init(
ss, symm, ss->filter_cache->transform_displacement_mode, data.transform_mats); ss, symm, ss->filter_cache->transform_displacement_mode, transform_mats);
TaskParallelSettings settings; TaskParallelSettings settings;
BKE_pbvh_parallel_range_settings(&settings, true, ss->filter_cache->nodes.size()); BKE_pbvh_parallel_range_settings(&settings, true, ss->filter_cache->nodes.size());
@@ -281,13 +269,24 @@ static void sculpt_transform_radius_elastic(Sculpt *sd, Object *ob, const float
* displacement proxies as all vertices are modified by all symmetry passes. */ * displacement proxies as all vertices are modified by all symmetry passes. */
for (ePaintSymmetryFlags symmpass = PAINT_SYMM_NONE; symmpass <= symm; symmpass++) { for (ePaintSymmetryFlags symmpass = PAINT_SYMM_NONE; symmpass <= symm; symmpass++) {
if (SCULPT_is_symmetry_iteration_valid(symmpass, symm)) { if (SCULPT_is_symmetry_iteration_valid(symmpass, symm)) {
flip_v3_v3(data.elastic_transform_pivot, ss->pivot_pos, symmpass); float elastic_transform_pivot[3];
flip_v3_v3(data.elastic_transform_pivot_init, ss->init_pivot_pos, symmpass); flip_v3_v3(elastic_transform_pivot, ss->pivot_pos, symmpass);
float elastic_transform_pivot_init[3];
flip_v3_v3(elastic_transform_pivot_init, ss->init_pivot_pos, symmpass);
const int symm_area = SCULPT_get_vertex_symm_area(data.elastic_transform_pivot); const int symm_area = SCULPT_get_vertex_symm_area(elastic_transform_pivot);
copy_m4_m4(data.elastic_transform_mat, data.transform_mats[symm_area]); float elastic_transform_mat[4][4];
BLI_task_parallel_range( copy_m4_m4(elastic_transform_mat, transform_mats[symm_area]);
0, ss->filter_cache->nodes.size(), &data, sculpt_elastic_transform_task_cb, &settings); threading::parallel_for(
ss->filter_cache->nodes.index_range(), 1, [&](const IndexRange range) {
for (const int i : range) {
sculpt_elastic_transform_task(ob,
transform_radius,
elastic_transform_mat,
elastic_transform_pivot,
ss->filter_cache->nodes[i]);
}
});
} }
} }
SCULPT_combine_transform_proxies(sd, ob); SCULPT_combine_transform_proxies(sd, ob);
@@ -304,7 +303,7 @@ void ED_sculpt_update_modal_transform(bContext *C, Object *ob)
switch (sd->transform_mode) { switch (sd->transform_mode) {
case SCULPT_TRANSFORM_MODE_ALL_VERTICES: { case SCULPT_TRANSFORM_MODE_ALL_VERTICES: {
sculpt_transform_all_vertices(sd, ob); sculpt_transform_all_vertices(ob);
break; break;
} }
case SCULPT_TRANSFORM_MODE_RADIUS_ELASTIC: { case SCULPT_TRANSFORM_MODE_RADIUS_ELASTIC: {