Mesh: Lines index buffer creation improvement #120720

Merged
Hans Goudey merged 66 commits from HooglyBoogly/blender:mesh-extract-lines-fix into main 2024-04-30 15:55:01 +02:00
4 changed files with 84 additions and 121 deletions
Showing only changes of commit fab3c88425 - Show all commits

View File

@ -234,11 +234,6 @@ static void accumululate_material_counts_mesh(
});
}
static bool single_material(const Span<int> tris_num_by_material)
{
return tris_num_by_material.count(0) == tris_num_by_material.size() - 1;
}
/* Count how many triangles for each material. */
static Array<int> mesh_render_data_mat_tri_len_build(const MeshRenderData &mr)
{
@ -263,16 +258,65 @@ static Array<int> mesh_render_data_mat_tri_len_build(const MeshRenderData &mr)
return std::move(tris_num_by_material);
}
static std::optional<Array<int>> sort_face_tris_by_material_mesh(
const MeshRenderData &mr, MutableSpan<int> material_tri_starts)
static Array<int> sort_face_tris_by_material_bmesh(const MeshRenderData &mr,
MutableSpan<int> material_tri_starts)
{
BMesh &bm = *mr.bm;
Array<int> face_tri_offsets(bm.totface);
#ifndef NDEBUG
face_tri_offsets.fill(-1);
#endif
const int mat_last = mr.materials_num - 1;
BMIter iter;
BMFace *face;
int i;
BM_ITER_MESH_INDEX (face, &iter, &bm, BM_FACES_OF_MESH, i) {
if (BM_elem_flag_test(face, BM_ELEM_HIDDEN)) {
continue;
}
const int mat = std::clamp(int(face->mat_nr), 0, mat_last);
face_tri_offsets[i] = material_tri_starts[mat];
material_tri_starts[mat] += face->len - 2;
}
return face_tri_offsets;
}
static bool mesh_is_single_material(const Span<int> tris_num_by_material)
{
return tris_num_by_material.count(0) == tris_num_by_material.size() - 1;
}
static std::optional<Array<int>> sort_face_tris_by_material_mesh(
const MeshRenderData &mr,
const Span<int> tris_num_by_material,
MutableSpan<int> material_tri_starts)
{
const bool single_material = mesh_is_single_material(tris_num_by_material);
if (single_material && mr.hide_poly.is_empty()) {
return std::nullopt;
}
const OffsetIndices faces = mr.faces;
const Span<bool> hide_poly = mr.hide_poly;
const Span<int> material_indices = mr.material_indices;
Array<int> face_tri_offsets(faces.size());
#ifndef NDEBUG
face_tri_offsets.fill(-1);
#endif
// TODO: Multithread this loop somehow?
if (single_material) {
int offset = 0;
for (const int face : faces.index_range()) {
if (hide_poly[face]) {
continue;
}
face_tri_offsets[face] = offset;
offset += bke::mesh::face_triangles_num(faces[face].size());
}
}
const int mat_last = mr.materials_num - 1;
if (hide_poly.is_empty()) {
@ -285,13 +329,11 @@ static std::optional<Array<int>> sort_face_tris_by_material_mesh(
else {
for (const int face : faces.index_range()) {
if (hide_poly[face]) {
face_tri_offsets[face] = -1;
}
else {
const int mat = std::clamp(material_indices[face], 0, mat_last);
face_tri_offsets[face] = material_tri_starts[mat];
material_tri_starts[mat] += bke::mesh::face_triangles_num(faces[face].size());
continue;
}
const int mat = std::clamp(material_indices[face], 0, mat_last);
face_tri_offsets[face] = material_tri_starts[mat];
material_tri_starts[mat] += bke::mesh::face_triangles_num(faces[face].size());
}
}
@ -310,40 +352,11 @@ static SortedFaceData mesh_render_data_faces_sorted_build(const MeshRenderData &
cache.visible_tris_num = material_tri_starts.last();
/* Sort per material. */
const int mat_last = mr.materials_num - 1;
if (mr.extract_type == MR_EXTRACT_BMESH) {
cache.face_tri_offsets.emplace();
cache.face_tri_offsets->reinitialize(mr.faces_num);
MutableSpan<int> face_tri_offsets = cache.face_tri_offsets->as_mutable_span();
BMIter iter;
BMFace *f;
int i;
BM_ITER_MESH_INDEX (f, &iter, mr.bm, BM_FACES_OF_MESH, i) {
if (!BM_elem_flag_test(f, BM_ELEM_HIDDEN)) {
const int mat = std::clamp(int(f->mat_nr), 0, mat_last);
face_tri_offsets[i] = material_tri_starts[mat];
material_tri_starts[mat] += f->len - 2;
}
else {
face_tri_offsets[i] = -1;
}
}
cache.face_tri_offsets = sort_face_tris_by_material_bmesh(mr, material_tri_starts);
}
else {
if (single_material(tris_num_by_material)) {
if (mr.hide_poly.is_empty()) {
cache.visible_tris_num = mr.corner_tris_num;
}
else {
cache.visible_tris_num = *std::find_if(tris_num_by_material.begin(),
tris_num_by_material.end(),
[](const int tri_count) { return tri_count != 0; });
}
}
else {
cache.face_tri_offsets = sort_face_tris_by_material_mesh(mr, material_tri_starts);
}
cache.face_tri_offsets = sort_face_tris_by_material_mesh(mr, material_tri_starts);
}
return cache;
}

View File

@ -20,42 +20,13 @@ namespace blender::draw {
/** \name Extract Triangles Indices (multi material)
* \{ */
static inline void copy_tris_to_vbo(const Span<int3> corner_tris,
const bool hidden,
const IndexRange mesh_tris,
const IndexRange ibo_tris,
const uint32_t restart_value,
MutableSpan<uint3> data)
{
if (hidden) {
data.slice(ibo_tris).cast<uint32_t>().fill(restart_value);
}
else {
data.slice(ibo_tris).copy_from(corner_tris.slice(mesh_tris).cast<uint3>());
}
}
static bool use_corner_tris_directly(const MeshRenderData &mr)
{
if (mr.face_sorted->face_tri_offsets.has_value()) {
/* Faces are uploaded sorted into contiguous chunks of matching
* materials which may not match the original face order. */
return false;
}
if (!mr.hide_poly.is_empty()) {
/* Values for hidden faces must be changed. */
return false;
}
return true;
}
static void extract_tris_mesh(const MeshRenderData &mr, GPUIndexBuf &ibo)
{
const OffsetIndices faces = mr.faces;
const Span<int3> corner_tris = mr.corner_tris;
if (use_corner_tris_directly(mr)) {
/* There are no hidden faces and no reordering is necessary to group faces with the same
if (mr.face_sorted->face_tri_offsets.has_value()) {
/* There are no hidden faces and no reordering is necessary to group triangles with the same
* material. The corner indices from #Mesh::corner_tris() can be copied directly to the GPU. */
GPU_indexbuf_build_in_place_from_memory(&ibo,
GPU_PRIM_TRIS,
@ -72,34 +43,21 @@ static void extract_tris_mesh(const MeshRenderData &mr, GPUIndexBuf &ibo)
GPUIndexBufBuilder builder;
GPU_indexbuf_init(&builder, GPU_PRIM_TRIS, mr.face_sorted->visible_tris_num, mr.corners_num);
MutableSpan<uint3> data = GPU_indexbuf_get_data(&builder).cast<uint3>();
const uint32_t restart_value = GPU_indexbuf_get_restart_value(&builder);
if (!mr.face_sorted->face_tri_offsets.has_value()) {
/* No reordering for grouping faces with the same material, but there are hidden faces. */
threading::parallel_for(corner_tris.index_range(), 2048, [&](const IndexRange range) {
for (const int face : range) {
const IndexRange tris = bke::mesh::face_triangles_range(faces, face);
copy_tris_to_vbo(corner_tris, hide_poly[face], tris, tris, restart_value, data);
}
});
return;
}
const Span<int> face_tri_offsets = mr.face_sorted->face_tri_offsets->as_span();
threading::parallel_for(corner_tris.index_range(), 2048, [&](const IndexRange range) {
for (const int face : range) {
const IndexRange mesh_tris = bke::mesh::face_triangles_range(faces, face);
copy_tris_to_vbo(corner_tris,
hide_poly.is_empty() ? false : hide_poly[face],
mesh_tris,
IndexRange(face_tri_offsets[face], mesh_tris.size()),
restart_value,
data);
if (!hide_poly.is_empty() && hide_poly[face]) {
continue;
}
const IndexRange mesh_range = bke::mesh::face_triangles_range(faces, face);
const Span<uint3> mesh_tris = corner_tris.slice(mesh_range).cast<uint3>();
MutableSpan<uint3> ibo_tris = data.slice(face_tri_offsets[face], mesh_tris.size());
ibo_tris.copy_from(mesh_tris);
}
});
// TODO: Track if restart indices used.
GPU_indexbuf_build_in_place_ex(&builder, 0, mr.corners_num, true, &ibo);
GPU_indexbuf_build_in_place_ex(&builder, 0, mr.face_sorted->visible_tris_num, false, &ibo);
}
static void extract_tris_bmesh(const MeshRenderData &mr, GPUIndexBuf &ibo)
@ -111,35 +69,26 @@ static void extract_tris_bmesh(const MeshRenderData &mr, GPUIndexBuf &ibo)
BMesh &bm = *mr.bm;
BMLoop *(*looptris)[3] = mr.edit_bmesh->looptris;
const Span<int> face_tri_offsets = mr.face_sorted->face_tri_offsets ?
mr.face_sorted->face_tri_offsets->as_span() :
Span<int>();
const Span<int> face_tri_offsets = *mr.face_sorted->face_tri_offsets;
threading::parallel_for(IndexRange(bm.totface), 1024, [&](const IndexRange range) {
for (const int face_index : range) {
const BMFace &face = *BM_face_at_index(&bm, face_index);
if (BM_elem_flag_test(&face, BM_ELEM_HIDDEN)) {
continue;
}
const int loop_index = BM_elem_index_get(BM_FACE_FIRST_LOOP(&face));
const IndexRange bm_tris(poly_to_tri_count(face_index, loop_index),
bke::mesh::face_triangles_num(face.len));
const IndexRange ibo_tris(face_tri_offsets.is_empty() ? bm_tris.start() :
face_tri_offsets[face_index],
bm_tris.size());
if (BM_elem_flag_test(&face, BM_ELEM_HIDDEN)) {
data.slice(ibo_tris).fill(uint3(restart_value));
}
else {
for (const int i : bm_tris.index_range()) {
data[ibo_tris[i]] = uint3(BM_elem_index_get(looptris[bm_tris[i]][0]),
BM_elem_index_get(looptris[bm_tris[i]][1]),
BM_elem_index_get(looptris[bm_tris[i]][2]));
}
const IndexRange ibo_tris(face_tri_offsets[face_index], bm_tris.size());
for (const int i : bm_tris.index_range()) {
data[ibo_tris[i]] = uint3(BM_elem_index_get(looptris[bm_tris[i]][0]),
BM_elem_index_get(looptris[bm_tris[i]][1]),
BM_elem_index_get(looptris[bm_tris[i]][2]));
}
}
});
// TODO: Track if restart indices used.
GPU_indexbuf_build_in_place_ex(&builder, 0, mr.corners_num, true, &ibo);
GPU_indexbuf_build_in_place_ex(&builder, 0, mr.face_sorted->visible_tris_num, false, &ibo);
}
static void extract_tris_finish(const MeshRenderData &mr, MeshBatchCache &cache, GPUIndexBuf &ibo)

View File

@ -917,7 +917,7 @@ Vector<Object *> ED_undo_editmode_objects_from_view_layer(const Scene *scene,
}
}
}
BLI_assert(object_data.is_empty());
BLI_assert(!object_data.is_empty());
BLI_assert(objects[0] == baseact->object);
return objects;
}

View File

@ -528,12 +528,13 @@ void GPU_indexbuf_build_in_place_from_memory(GPUIndexBuf *ibo,
const int32_t index_max,
const bool uses_restart_indices)
{
uint32_t *copy = static_cast<uint32_t *>(
MEM_malloc_arrayN(data_len, sizeof(uint32_t), __func__));
const uint32_t indices_num = data_len * indices_per_primitive(prim_type);
/* TODO: This copy is meant to be temporary. The data should be uploaded directly to the GPU here
* rather than copied to an array owned by the IBO first. */
memcpy(copy, data, sizeof(uint32_t) * data_len);
unwrap(ibo)->init(data_len, copy, index_min, index_max, prim_type, uses_restart_indices);
uint32_t *copy = static_cast<uint32_t *>(
MEM_malloc_arrayN(indices_num, sizeof(uint32_t), __func__));
memcpy(copy, data, sizeof(uint32_t) * indices_num);
unwrap(ibo)->init(indices_num, copy, index_min, index_max, prim_type, uses_restart_indices);
}
void GPU_indexbuf_create_subrange_in_place(GPUIndexBuf *elem,