Sculpt: Remove color buffer from sculpt BVH tree

Part of #118145.
Instead of storing a separately allocated array for each BVH node
for the temporary "color buffer" colors meant for mixing during a
stroke, just store an array the size of the whole mesh. Though this
is wasteful in terms of memory usage, plenty of other brushes store
mesh-sized arrays already, and it should make more sense as BVH
nodes get smaller too. After this commit, the BVH tree has no
specific code for color attributes anymore.
This commit is contained in:
Hans Goudey
2024-08-01 13:58:45 -04:00
parent 90e7331d10
commit d282b1735e
5 changed files with 20 additions and 34 deletions

View File

@@ -59,10 +59,6 @@ struct PBVH_GPU_Args;
} // namespace draw::pbvh
} // namespace blender
struct PBVHColorBufferNode {
float (*color)[4] = nullptr;
};
namespace blender::bke::pbvh {
class Tree;
@@ -160,8 +156,6 @@ class Node {
BMVert **bm_orvert_ = nullptr;
int bm_tot_ortri_ = 0;
/* Used to store the brush color during a stroke and composite it over the original color */
PBVHColorBufferNode color_buffer_;
pixels::NodeData *pixels_ = nullptr;
/* Used to flash colors of updated node bounding boxes in
@@ -688,9 +682,6 @@ blender::Span<blender::float3> BKE_pbvh_get_vert_positions(const blender::bke::p
blender::MutableSpan<blender::float3> BKE_pbvh_get_vert_positions(blender::bke::pbvh::Tree &pbvh);
blender::Span<blender::float3> BKE_pbvh_get_vert_normals(const blender::bke::pbvh::Tree &pbvh);
PBVHColorBufferNode *BKE_pbvh_node_color_buffer_get(blender::bke::pbvh::Node *node);
void BKE_pbvh_node_color_buffer_free(blender::bke::pbvh::Tree &pbvh);
void BKE_pbvh_ensure_node_loops(blender::bke::pbvh::Tree &pbvh,
blender::Span<blender::int3> corner_tris);
int BKE_pbvh_debug_draw_gen_get(blender::bke::pbvh::Node &node);

View File

@@ -2691,24 +2691,6 @@ bool BKE_pbvh_is_deformed(const blender::bke::pbvh::Tree &pbvh)
}
/* Proxies */
PBVHColorBufferNode *BKE_pbvh_node_color_buffer_get(blender::bke::pbvh::Node *node)
{
if (!node->color_buffer_.color) {
node->color_buffer_.color = static_cast<float(*)[4]>(
MEM_callocN(sizeof(float[4]) * node->unique_verts_num_, "Color buffer"));
}
return &node->color_buffer_;
}
void BKE_pbvh_node_color_buffer_free(blender::bke::pbvh::Tree &pbvh)
{
blender::Vector<blender::bke::pbvh::Node *> nodes = search_gather(pbvh, {});
for (blender::bke::pbvh::Node *node : nodes) {
MEM_SAFE_FREE(node->color_buffer_.color);
}
}
void pbvh_vertex_iter_init(blender::bke::pbvh::Tree &pbvh,
blender::bke::pbvh::Node *node,
PBVHVertexIter *vi,

View File

@@ -1701,8 +1701,6 @@ static void restore_from_undo_step(const Sculpt &sd, Object &object)
/* Disable multi-threading when dynamic-topology is enabled. Otherwise,
* new entries might be inserted by #undo::push_node() into the #GHash
* used internally by #BM_log_original_vert_co() by a different thread. See #33787. */
BKE_pbvh_node_color_buffer_free(*ss.pbvh);
}
} // namespace undo
@@ -6035,7 +6033,6 @@ static void sculpt_stroke_done(const bContext *C, PaintStroke * /*stroke*/)
brush = BKE_paint_brush(&sd.paint);
}
BKE_pbvh_node_color_buffer_free(*ss.pbvh);
MEM_delete(ss.cache);
ss.cache = nullptr;
@@ -6741,6 +6738,7 @@ void scatter_data_vert_bmesh(const Span<T> node_data,
template void gather_data_mesh<float>(Span<float>, Span<int>, MutableSpan<float>);
template void gather_data_mesh<float3>(Span<float3>, Span<int>, MutableSpan<float3>);
template void gather_data_mesh<float4>(Span<float4>, Span<int>, MutableSpan<float4>);
template void gather_data_grids<float>(const SubdivCCG &,
Span<float>,
Span<int>,
@@ -6758,6 +6756,7 @@ template void gather_data_vert_bmesh<float3>(Span<float3>,
template void scatter_data_mesh<float>(Span<float>, Span<int>, MutableSpan<float>);
template void scatter_data_mesh<float3>(Span<float3>, Span<int>, MutableSpan<float3>);
template void scatter_data_mesh<float4>(Span<float4>, Span<int>, MutableSpan<float4>);
template void scatter_data_grids<float>(const SubdivCCG &,
Span<float>,
Span<int>,

View File

@@ -347,6 +347,12 @@ struct StrokeCache {
/* Position of the mouse event in screen space, not modified by the stroke type. */
float2 mouse_event;
/**
* Used by the color attribute paint brush tool to store the brush color during a stroke and
* composite it over the original color.
*/
Array<float4> mix_colors;
Array<float4> prev_colors;
GArray<> prev_colors_vpaint;

View File

@@ -251,6 +251,7 @@ struct LocalData {
Vector<float> distances;
Vector<float4> colors;
Vector<float4> new_colors;
Vector<float4> mix_colors;
Vector<Vector<int>> vert_neighbors;
};
@@ -346,6 +347,7 @@ static void do_paint_brush_task(Object &object,
const float4 wet_mix_sampled_color,
bke::pbvh::Node &node,
LocalData &tls,
const MutableSpan<float4> mix_colors,
bke::GSpanAttributeWriter &color_attribute)
{
const SculptSession &ss = *object.sculpt;
@@ -410,7 +412,7 @@ static void do_paint_brush_task(Object &object,
const Span<float4> orig_colors = orig_color_data_get_mesh(object, node);
PBVHColorBufferNode *color_buffer = BKE_pbvh_node_color_buffer_get(&node);
MutableSpan<float4> color_buffer = gather_data_mesh(mix_colors.as_span(), verts, tls.mix_colors);
if (brush.flag & BRUSH_USE_GRADIENT) {
switch (brush.gradient_stroke_mode) {
@@ -449,17 +451,19 @@ static void do_paint_brush_task(Object &object,
/* Interpolate with the wet_mix color for wet paint mixing. */
blend_color_interpolate_float(
paint_color, paint_color, wet_mix_color, ss.cache->paint_brush.wet_mix);
blend_color_mix_float(color_buffer->color[i], color_buffer->color[i], paint_color);
blend_color_mix_float(color_buffer[i], color_buffer[i], paint_color);
/* Final mix over the original color using brush alpha. We apply auto-making again
* at this point to avoid washing out non-binary masking modes like cavity masking. */
float automasking = auto_mask.is_empty() ? 1.0f : auto_mask[i];
const float4 buffer_color = float4(color_buffer->color[i]) * alpha * automasking;
const float4 buffer_color = float4(color_buffer[i]) * alpha * automasking;
IMB_blend_color_float(new_colors[i], orig_colors[i], buffer_color, IMB_BlendMode(brush.blend));
new_colors[i] = math::clamp(new_colors[i], 0.0f, 1.0f);
}
scatter_data_mesh(color_buffer.as_span(), verts, mix_colors);
for (const int i : verts.index_range()) {
color_vert_set(faces,
corner_verts,
@@ -496,7 +500,6 @@ static void do_sample_wet_paint_task(const Object &object,
tls.factors.resize(verts.size());
const MutableSpan<float> factors = tls.factors;
fill_factor_from_hide(mesh, verts, factors);
tls.distances.resize(verts.size());
@@ -634,6 +637,10 @@ void do_paint_brush(PaintModeSettings &paint_mode_settings,
}
}
if (ss.cache->mix_colors.is_empty()) {
ss.cache->mix_colors = Array<float4>(mesh.verts_num, float4(0));
}
threading::EnumerableThreadSpecific<LocalData> all_tls;
threading::parallel_for(nodes.index_range(), 1, [&](const IndexRange range) {
LocalData &tls = all_tls.local();
@@ -649,6 +656,7 @@ void do_paint_brush(PaintModeSettings &paint_mode_settings,
wet_color,
*nodes[i],
tls,
ss.cache->mix_colors,
color_attribute);
}
});