Mesh: Draw triangle index buffer creation improvements #119130

Merged
Hans Goudey merged 48 commits from HooglyBoogly/blender:mesh-extract-tris-fix into main 2024-04-11 04:49:38 +02:00
8 changed files with 241 additions and 223 deletions

View File

@ -225,8 +225,9 @@ struct SortedFaceData {
Array<int> tris_num_by_material;
/**
* The first triangle index for each face, sorted into slices by material.
* May be empty if the mesh only has a single material.
*/
Array<int> face_tri_offsets;
std::optional<Array<int>> face_tri_offsets;
};
/**

View File

@ -611,7 +611,6 @@ void mesh_buffer_cache_create_requested(TaskGraph *task_graph,
*/
const bool do_hq_normals = (scene->r.perf_flag & SCE_PERF_HQ_NORMALS) != 0 ||
GPU_use_hq_normals_workaround();
const bool override_single_mat = mesh_render_mat_len_get(object, mesh) <= 1;
/* Create an array containing all the extractors that needs to be executed. */
ExtractorRunDatas extractors;
@ -621,8 +620,7 @@ void mesh_buffer_cache_create_requested(TaskGraph *task_graph,
#define EXTRACT_ADD_REQUESTED(type, name) \
do { \
if (DRW_##type##_requested(mbuflist->type.name)) { \
const MeshExtract *extractor = mesh_extract_override_get( \
&extract_##name, do_hq_normals, override_single_mat); \
const MeshExtract *extractor = mesh_extract_override_get(&extract_##name, do_hq_normals); \
extractors.append(extractor); \
} \
} while (0)

View File

@ -189,9 +189,10 @@ static void accumululate_material_counts_mesh(
const MeshRenderData &mr, threading::EnumerableThreadSpecific<Array<int>> &all_tri_counts)
{
const OffsetIndices faces = mr.faces;
if (mr.material_indices.is_empty()) {
if (mr.use_hide && !mr.hide_poly.is_empty()) {
const Span hide_poly = mr.hide_poly;
const Span<bool> hide_poly = mr.hide_poly;
const Span material_indices = mr.material_indices;
if (material_indices.is_empty()) {
if (!hide_poly.is_empty()) {
all_tri_counts.local().first() = threading::parallel_reduce(
faces.index_range(),
4096,
@ -212,13 +213,12 @@ static void accumululate_material_counts_mesh(
return;
}
const Span material_indices = mr.material_indices;
threading::parallel_for(material_indices.index_range(), 1024, [&](const IndexRange range) {
Array<int> &tri_counts = all_tri_counts.local();
const int last_index = tri_counts.size() - 1;
if (mr.use_hide && !mr.hide_poly.is_empty()) {
if (!hide_poly.is_empty()) {
for (const int i : range) {
if (!mr.hide_poly[i]) {
if (!hide_poly[i]) {
const int mat = std::clamp(material_indices[i], 0, last_index);
tri_counts[mat] += bke::mesh::face_triangles_num(faces[i].size());
}
@ -257,64 +257,109 @@ static Array<int> mesh_render_data_mat_tri_len_build(const MeshRenderData &mr)
return std::move(tris_num_by_material);
}
static void mesh_render_data_faces_sorted_build(MeshRenderData &mr, MeshBufferCache &cache)
static Array<int> calc_face_tri_starts_bmesh(const MeshRenderData &mr,
MutableSpan<int> material_tri_starts)
{
cache.face_sorted.tris_num_by_material = mesh_render_data_mat_tri_len_build(mr);
const Span<int> tris_num_by_material = cache.face_sorted.tris_num_by_material;
BMesh &bm = *mr.bm;
Array<int> face_tri_offsets(bm.totface);
#ifndef NDEBUG
face_tri_offsets.fill(-1);
#endif
/* Apply offset. */
int visible_tris_num = 0;
Array<int, 32> mat_tri_offs(mr.materials_num);
{
for (int i = 0; i < mr.materials_num; i++) {
mat_tri_offs[i] = visible_tris_num;
visible_tris_num += tris_num_by_material[i];
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;
}
cache.face_sorted.visible_tris_num = visible_tris_num;
cache.face_sorted.face_tri_offsets.reinitialize(mr.faces_num);
MutableSpan<int> face_tri_offsets = cache.face_sorted.face_tri_offsets;
return face_tri_offsets;
}
static bool mesh_is_single_material(const OffsetIndices<int> material_tri_starts)
{
const int used_materials = std::count_if(
material_tri_starts.index_range().begin(),
material_tri_starts.index_range().end(),
[&](const int i) { return material_tri_starts[i].size() > 0; });
return used_materials == 1;
}
static std::optional<Array<int>> calc_face_tri_starts_mesh(const MeshRenderData &mr,
MutableSpan<int> material_tri_starts)
{
const bool single_material = mesh_is_single_material(material_tri_starts.as_span());
if (single_material && mr.hide_poly.is_empty()) {
return std::nullopt;
}
const OffsetIndices faces = mr.faces;
const Span<bool> hide_poly = mr.hide_poly;
Array<int> face_tri_offsets(faces.size());
#ifndef NDEBUG
face_tri_offsets.fill(-1);
#endif
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());
}
return face_tri_offsets;
}
const Span<int> material_indices = mr.material_indices;
const int mat_last = mr.materials_num - 1;
for (const int face : faces.index_range()) {
if (!hide_poly.is_empty() && hide_poly[face]) {
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());
}
return face_tri_offsets;
}
static SortedFaceData mesh_render_data_faces_sorted_build(const MeshRenderData &mr)
{
SortedFaceData cache;
cache.tris_num_by_material = mesh_render_data_mat_tri_len_build(mr);
const Span<int> tris_num_by_material = cache.tris_num_by_material;
Array<int, 32> material_tri_starts(mr.materials_num + 1);
material_tri_starts.as_mutable_span().drop_back(1).copy_from(tris_num_by_material);
offset_indices::accumulate_counts_to_offsets(material_tri_starts);
cache.visible_tris_num = material_tri_starts.last();
/* Sort per material. */
int mat_last = mr.materials_num - 1;
if (mr.extract_type == MR_EXTRACT_BMESH) {
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 = clamp_i(f->mat_nr, 0, mat_last);
face_tri_offsets[i] = mat_tri_offs[mat];
mat_tri_offs[mat] += f->len - 2;
}
else {
face_tri_offsets[i] = -1;
}
}
cache.face_tri_offsets = calc_face_tri_starts_bmesh(mr, material_tri_starts);
}
else {
for (int i = 0; i < mr.faces_num; i++) {
if (!(mr.use_hide && !mr.hide_poly.is_empty() && mr.hide_poly[i])) {
const int mat = mr.material_indices.is_empty() ?
0 :
clamp_i(mr.material_indices[i], 0, mat_last);
face_tri_offsets[i] = mat_tri_offs[mat];
mat_tri_offs[mat] += mr.faces[i].size() - 2;
}
else {
face_tri_offsets[i] = -1;
}
}
cache.face_tri_offsets = calc_face_tri_starts_mesh(mr, material_tri_starts);
}
return cache;
}
static void mesh_render_data_faces_sorted_ensure(MeshRenderData &mr, MeshBufferCache &cache)
{
if (!cache.face_sorted.face_tri_offsets.is_empty()) {
if (cache.face_sorted.visible_tris_num > 0) {
return;
}
mesh_render_data_faces_sorted_build(mr, cache);
cache.face_sorted = mesh_render_data_faces_sorted_build(mr);
}
void mesh_render_data_update_faces_sorted(MeshRenderData &mr,

View File

@ -64,26 +64,13 @@ static const MeshExtract *mesh_extract_override_hq_normals(const MeshExtract *ex
return extractor;
}
static const MeshExtract *mesh_extract_override_single_material(const MeshExtract *extractor)
{
if (extractor == &extract_tris) {
return &extract_tris_single_mat;
}
return extractor;
}
const MeshExtract *mesh_extract_override_get(const MeshExtract *extractor,
const bool do_hq_normals,
const bool do_single_mat)
const bool do_hq_normals)
{
if (do_hq_normals) {
extractor = mesh_extract_override_hq_normals(extractor);
}
if (do_single_mat) {
extractor = mesh_extract_override_single_material(extractor);
}
return extractor;
}

View File

@ -320,9 +320,7 @@ struct EditLoopData {
void *mesh_extract_buffer_get(const MeshExtract *extractor, MeshBufferList *mbuflist);
eMRIterType mesh_extract_iter_type(const MeshExtract *ext);
const MeshExtract *mesh_extract_override_get(const MeshExtract *extractor,
bool do_hq_normals,
bool do_single_mat);
const MeshExtract *mesh_extract_override_get(const MeshExtract *extractor, bool do_hq_normals);
void mesh_render_data_face_flag(const MeshRenderData &mr,
const BMFace *efa,
BMUVOffsets offsets,
@ -340,7 +338,6 @@ template<typename GPUType>
void extract_vert_normals(const MeshRenderData &mr, MutableSpan<GPUType> normals);
extern const MeshExtract extract_tris;
extern const MeshExtract extract_tris_single_mat;
extern const MeshExtract extract_lines;
extern const MeshExtract extract_lines_with_lines_loose;
extern const MeshExtract extract_lines_loose_only;

View File

@ -16,83 +16,84 @@
namespace blender::draw {
static void extract_tris_mat_task_reduce(void *_userdata_to, void *_userdata_from)
{
GPUIndexBufBuilder *elb_to = static_cast<GPUIndexBufBuilder *>(_userdata_to);
GPUIndexBufBuilder *elb_from = static_cast<GPUIndexBufBuilder *>(_userdata_from);
GPU_indexbuf_join(elb_to, elb_from);
}
/* ---------------------------------------------------------------------- */
/** \name Extract Triangles Indices (multi material)
* \{ */
static void extract_tris_init(const MeshRenderData &mr,
MeshBatchCache & /*cache*/,
void * /*ibo*/,
void *tls_data)
static void extract_tris_mesh(const MeshRenderData &mr, gpu::IndexBuf &ibo)
{
GPUIndexBufBuilder *elb = static_cast<GPUIndexBufBuilder *>(tls_data);
GPU_indexbuf_init(elb, GPU_PRIM_TRIS, mr.face_sorted->visible_tris_num, mr.corners_num);
}
static void extract_tris_iter_face_bm(const MeshRenderData &mr,
const BMFace *f,
const int f_index,
void *_data)
{
int tri_offset = mr.face_sorted->face_tri_offsets[f_index];
if (tri_offset == -1) {
const Span<int3> corner_tris = mr.corner_tris;
if (!mr.face_sorted->face_tri_offsets) {
/* 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. */
BLI_assert(mr.face_sorted->visible_tris_num == corner_tris.size());
GPU_indexbuf_build_in_place_from_memory(&ibo,
GPU_PRIM_TRIS,
corner_tris.cast<uint32_t>().data(),
corner_tris.size(),
0,
mr.corners_num,
false);
return;
}
GPUIndexBufBuilder *elb = static_cast<GPUIndexBufBuilder *>(_data);
int tri_first_index_real = poly_to_tri_count(f_index, BM_elem_index_get(f->l_first));
const OffsetIndices faces = mr.faces;
const Span<bool> hide_poly = mr.hide_poly;
Span<std::array<BMLoop *, 3>> looptris = mr.edit_bmesh->looptris;
int tri_len = f->len - 2;
for (int offs = 0; offs < tri_len; offs++) {
const std::array<BMLoop *, 3> &elt = looptris[tri_first_index_real + offs];
int tri_index = tri_offset + offs;
GPU_indexbuf_set_tri_verts(elb,
tri_index,
BM_elem_index_get(elt[0]),
BM_elem_index_get(elt[1]),
BM_elem_index_get(elt[2]));
}
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 Span<int> face_tri_offsets = mr.face_sorted->face_tri_offsets->as_span();
threading::parallel_for(faces.index_range(), 2048, [&](const IndexRange range) {
for (const int face : range) {
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);
}
});
GPU_indexbuf_build_in_place_ex(&builder, 0, mr.face_sorted->visible_tris_num, false, &ibo);
}
static void extract_tris_iter_face_mesh(const MeshRenderData &mr,
const int face_index,
void *_data)
static void extract_tris_bmesh(const MeshRenderData &mr, gpu::IndexBuf &ibo)
{
int tri_offset = mr.face_sorted->face_tri_offsets[face_index];
if (tri_offset == -1) {
return;
}
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 IndexRange face = mr.faces[face_index];
BMesh &bm = *mr.bm;
const Span<std::array<BMLoop *, 3>> looptris = mr.edit_bmesh->looptris;
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[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]));
}
}
});
GPUIndexBufBuilder *elb = static_cast<GPUIndexBufBuilder *>(_data);
int tri_first_index_real = poly_to_tri_count(face_index, face.start());
int tri_len = face.size() - 2;
for (int offs = 0; offs < tri_len; offs++) {
const int3 &tri = mr.corner_tris[tri_first_index_real + offs];
int tri_index = tri_offset + offs;
GPU_indexbuf_set_tri_verts(elb, tri_index, tri[0], tri[1], tri[2]);
}
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,
void *buf,
void *_data)
gpu::IndexBuf &ibo)
{
gpu::IndexBuf *ibo = static_cast<gpu::IndexBuf *>(buf);
GPUIndexBufBuilder *elb = static_cast<GPUIndexBufBuilder *>(_data);
GPU_indexbuf_build_in_place(elb, ibo);
/* Create ibo sub-ranges. Always do this to avoid error when the standard surface batch
* is created before the surfaces-per-material. */
if (mr.use_final_mesh && cache.tris_per_mat) {
@ -107,12 +108,29 @@ static void extract_tris_finish(const MeshRenderData &mr,
/* Multiply by 3 because these are triangle indices. */
const int start = mat_start * 3;
const int len = mat_tri_len * 3;
GPU_indexbuf_create_subrange_in_place(cache.tris_per_mat[i], ibo, start, len);
GPU_indexbuf_create_subrange_in_place(cache.tris_per_mat[i], &ibo, start, len);
mat_start += mat_tri_len;
}
}
}
static void extract_tris_init(const MeshRenderData &mr,
MeshBatchCache &cache,
void *ibo_v,
void * /*tls_data*/)
{
gpu::IndexBuf &ibo = *static_cast<gpu::IndexBuf *>(ibo_v);
if (mr.extract_type == MR_EXTRACT_MESH) {
extract_tris_mesh(mr, ibo);
}
else {
extract_tris_bmesh(mr, ibo);
}
extract_tris_finish(mr, cache, ibo);
}
static void extract_tris_init_subdiv(const DRWSubdivCache &subdiv_cache,
const MeshRenderData & /*mr*/,
MeshBatchCache &cache,
@ -144,101 +162,7 @@ constexpr MeshExtract create_extractor_tris()
MeshExtract extractor = {nullptr};
extractor.init = extract_tris_init;
extractor.init_subdiv = extract_tris_init_subdiv;
extractor.iter_face_bm = extract_tris_iter_face_bm;
extractor.iter_face_mesh = extract_tris_iter_face_mesh;
extractor.task_reduce = extract_tris_mat_task_reduce;
extractor.finish = extract_tris_finish;
extractor.data_type = MR_DATA_CORNER_TRI | MR_DATA_POLYS_SORTED;
extractor.data_size = sizeof(GPUIndexBufBuilder);
extractor.use_threading = true;
extractor.mesh_buffer_offset = offsetof(MeshBufferList, ibo.tris);
return extractor;
}
/** \} */
/** \name Extract Triangles Indices (single material)
* \{ */
static void extract_tris_single_mat_init(const MeshRenderData &mr,
MeshBatchCache & /*cache*/,
void * /*ibo*/,
void *tls_data)
{
GPUIndexBufBuilder *elb = static_cast<GPUIndexBufBuilder *>(tls_data);
GPU_indexbuf_init(elb, GPU_PRIM_TRIS, mr.corner_tris_num, mr.corners_num);
}
static void extract_tris_single_mat_iter_looptri_bm(const MeshRenderData & /*mr*/,
BMLoop **elt,
const int elt_index,
void *_data)
{
GPUIndexBufBuilder *elb = static_cast<GPUIndexBufBuilder *>(_data);
if (!BM_elem_flag_test(elt[0]->f, BM_ELEM_HIDDEN)) {
GPU_indexbuf_set_tri_verts(elb,
elt_index,
BM_elem_index_get(elt[0]),
BM_elem_index_get(elt[1]),
BM_elem_index_get(elt[2]));
}
else {
GPU_indexbuf_set_tri_restart(elb, elt_index);
}
}
static void extract_tris_single_mat_iter_corner_tri_mesh(const MeshRenderData &mr,
const int3 &tri,
const int tri_index,
void *_data)
{
GPUIndexBufBuilder *elb = static_cast<GPUIndexBufBuilder *>(_data);
const int face_i = mr.corner_tri_faces[tri_index];
const bool hidden = mr.use_hide && !mr.hide_poly.is_empty() && mr.hide_poly[face_i];
if (hidden) {
GPU_indexbuf_set_tri_restart(elb, tri_index);
}
else {
GPU_indexbuf_set_tri_verts(elb, tri_index, tri[0], tri[1], tri[2]);
}
}
static void extract_tris_single_mat_finish(const MeshRenderData &mr,
MeshBatchCache &cache,
void *buf,
void *_data)
{
gpu::IndexBuf *ibo = static_cast<gpu::IndexBuf *>(buf);
GPUIndexBufBuilder *elb = static_cast<GPUIndexBufBuilder *>(_data);
GPU_indexbuf_build_in_place(elb, ibo);
/* Create ibo sub-ranges. Always do this to avoid error when the standard surface batch
* is created before the surfaces-per-material. */
if (mr.use_final_mesh && cache.tris_per_mat) {
for (int i = 0; i < mr.materials_num; i++) {
/* These IBOs have not been queried yet but we create them just in case they are needed
* later since they are not tracked by mesh_buffer_cache_create_requested(). */
if (cache.tris_per_mat[i] == nullptr) {
cache.tris_per_mat[i] = GPU_indexbuf_calloc();
}
/* Multiply by 3 because these are triangle indices. */
const int len = mr.corner_tris_num * 3;
GPU_indexbuf_create_subrange_in_place(cache.tris_per_mat[i], ibo, 0, len);
}
}
}
constexpr MeshExtract create_extractor_tris_single_mat()
{
MeshExtract extractor = {nullptr};
extractor.init = extract_tris_single_mat_init;
extractor.init_subdiv = extract_tris_init_subdiv;
extractor.iter_looptri_bm = extract_tris_single_mat_iter_looptri_bm;
extractor.iter_corner_tri_mesh = extract_tris_single_mat_iter_corner_tri_mesh;
extractor.task_reduce = extract_tris_mat_task_reduce;
extractor.finish = extract_tris_single_mat_finish;
extractor.data_type = MR_DATA_NONE;
extractor.data_size = sizeof(GPUIndexBufBuilder);
extractor.use_threading = true;
extractor.mesh_buffer_offset = offsetof(MeshBufferList, ibo.tris);
return extractor;
@ -247,6 +171,5 @@ constexpr MeshExtract create_extractor_tris_single_mat()
/** \} */
const MeshExtract extract_tris = create_extractor_tris();
const MeshExtract extract_tris_single_mat = create_extractor_tris_single_mat();
} // namespace blender::draw

View File

@ -10,6 +10,8 @@
#pragma once
#include "BLI_span.hh"
#include "GPU_primitive.hh"
#define GPU_TRACK_INDEX_RANGE 1
@ -154,6 +156,8 @@ blender::gpu::IndexBuf *GPU_indexbuf_build_on_device(uint index_len);
void GPU_indexbuf_init_build_on_device(blender::gpu::IndexBuf *elem, uint index_len);
blender::MutableSpan<uint32_t> GPU_indexbuf_get_data(GPUIndexBufBuilder *);
/*
* Thread safe.
*
@ -180,6 +184,26 @@ void GPU_indexbuf_set_tri_restart(GPUIndexBufBuilder *builder, uint elem);
blender::gpu::IndexBuf *GPU_indexbuf_build(GPUIndexBufBuilder *);
void GPU_indexbuf_build_in_place(GPUIndexBufBuilder *, blender::gpu::IndexBuf *);
void GPU_indexbuf_build_in_place_ex(GPUIndexBufBuilder *builder,
uint index_min,
uint index_max,
bool uses_restart_indices,
blender::gpu::IndexBuf *elem);
/**
HooglyBoogly marked this conversation as resolved Outdated

Please add documentation on the function and explain when to use it.

Please add documentation on the function and explain when to use it.
* Fill an IBO by uploading the referenced data directly to the GPU, bypassing the separate storage
* in the IBO. This should be used whenever the equivalent indices already exist in a contiguous
* array on the CPU.
*
* \todo The optimization to avoid the local copy currently isn't implemented.
*/
void GPU_indexbuf_build_in_place_from_memory(blender::gpu::IndexBuf *ibo,
GPUPrimType prim_type,
const uint32_t *data,
int32_t data_len,
int32_t index_min,
int32_t index_max,
bool uses_restart_indices);
void GPU_indexbuf_bind_as_ssbo(blender::gpu::IndexBuf *elem, int binding);

View File

@ -10,6 +10,7 @@
#include "MEM_guardedalloc.h"
#include "BLI_array_utils.hh"
#include "BLI_math_base.h"
#include "BLI_utildefines.h"
@ -96,6 +97,11 @@ void GPU_indexbuf_init_build_on_device(IndexBuf *elem, uint index_len)
elem_->init_build_on_device(index_len);
}
blender::MutableSpan<uint32_t> GPU_indexbuf_get_data(GPUIndexBufBuilder *builder)
{
return {builder->data, builder->max_index_len};
}
void GPU_indexbuf_join(GPUIndexBufBuilder *builder_to, const GPUIndexBufBuilder *builder_from)
{
BLI_assert(builder_to->data == builder_from->data);
@ -492,6 +498,43 @@ void GPU_indexbuf_build_in_place(GPUIndexBufBuilder *builder, IndexBuf *elem)
builder->data = nullptr;
}
void GPU_indexbuf_build_in_place_ex(GPUIndexBufBuilder *builder,
const uint index_min,
const uint index_max,
const bool uses_restart_indices,
IndexBuf *elem)
{
BLI_assert(builder->data != nullptr);
/* Transfer data ownership to IndexBuf.
* It will be uploaded upon first use. */
elem->init(builder->max_index_len,
builder->data,
index_min,
index_max,
builder->prim_type,
uses_restart_indices);
builder->data = nullptr;
}
void GPU_indexbuf_build_in_place_from_memory(IndexBuf *ibo,
const GPUPrimType prim_type,
const uint32_t *data,
const int32_t data_len,
const int32_t index_min,
const int32_t index_max,
const bool uses_restart_indices)
{
const uint32_t indices_num = data_len * indices_per_primitive(prim_type);
/* TODO: The need for 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. */
uint32_t *copy = static_cast<uint32_t *>(
MEM_malloc_arrayN(indices_num, sizeof(uint32_t), __func__));
threading::memory_bandwidth_bound_task(sizeof(uint32_t) * indices_num * 2, [&]() {
array_utils::copy(Span(data, indices_num), MutableSpan(copy, indices_num));
});
ibo->init(indices_num, copy, index_min, index_max, prim_type, uses_restart_indices);
}
void GPU_indexbuf_create_subrange_in_place(IndexBuf *elem,
IndexBuf *elem_src,
uint start,