Mesh: Lines index buffer creation improvement #120720
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
Loading…
Reference in New Issue