DrawManager: Cache material offsets.

When using multiple materials in a single mesh the most time is spend in
counting the offsets of each material for the sorting.

This patch moves the counting of the offsets to render mesh data and
caches it as long as the geometry doesn't change.

This patch doesn't include multithreading of this code.

Reviewed By: mano-wii

Differential Revision: https://developer.blender.org/D11612
This commit is contained in:
Jeroen Bakker 2021-06-15 15:31:17 +02:00 committed by Jeroen Bakker
parent 7c8b9c7a9a
commit 174ed69c1b
Notes: blender-bot 2023-02-14 08:33:26 +01:00
Referenced by issue #88550, Mesh Optimization Project Progress
6 changed files with 136 additions and 47 deletions

View File

@ -83,8 +83,9 @@ typedef enum eMRDataType {
MR_DATA_LOOPTRI = 1 << 3,
/** Force loop normals calculation. */
MR_DATA_TAN_LOOP_NOR = 1 << 4,
MR_DATA_MAT_OFFSETS = 1 << 5,
} eMRDataType;
ENUM_OPERATORS(eMRDataType, MR_DATA_TAN_LOOP_NOR)
ENUM_OPERATORS(eMRDataType, MR_DATA_MAT_OFFSETS)
#ifdef __cplusplus
extern "C" {
@ -166,6 +167,12 @@ typedef struct MeshBufferExtractionCache {
int *verts;
int *edges;
} loose_geom;
struct {
int *tri;
int visible_tri_len;
} mat_offsets;
} MeshBufferExtractionCache;
typedef enum DRWBatchFlag {

View File

@ -498,11 +498,15 @@ static struct TaskNode *extract_task_node_create(struct TaskGraph *task_graph,
* \{ */
struct MeshRenderDataUpdateTaskData {
MeshRenderData *mr = nullptr;
MeshBufferExtractionCache *cache = nullptr;
eMRIterType iter_type;
eMRDataType data_flag;
MeshRenderDataUpdateTaskData(MeshRenderData *mr, eMRIterType iter_type, eMRDataType data_flag)
: mr(mr), iter_type(iter_type), data_flag(data_flag)
MeshRenderDataUpdateTaskData(MeshRenderData *mr,
MeshBufferExtractionCache *cache,
eMRIterType iter_type,
eMRDataType data_flag)
: mr(mr), cache(cache), iter_type(iter_type), data_flag(data_flag)
{
}
@ -533,15 +537,17 @@ static void mesh_extract_render_data_node_exec(void *__restrict task_data)
mesh_render_data_update_normals(mr, data_flag);
mesh_render_data_update_looptris(mr, iter_type, data_flag);
mesh_render_data_update_mat_offsets(mr, update_task_data->cache, data_flag);
}
static struct TaskNode *mesh_extract_render_data_node_create(struct TaskGraph *task_graph,
MeshRenderData *mr,
MeshBufferExtractionCache *cache,
const eMRIterType iter_type,
const eMRDataType data_flag)
{
MeshRenderDataUpdateTaskData *task_data = new MeshRenderDataUpdateTaskData(
mr, iter_type, data_flag);
mr, cache, iter_type, data_flag);
struct TaskNode *task_node = BLI_task_graph_node_create(
task_graph,
@ -702,7 +708,7 @@ static void mesh_buffer_cache_create_requested(struct TaskGraph *task_graph,
#endif
struct TaskNode *task_node_mesh_render_data = mesh_extract_render_data_node_create(
task_graph, mr, iter_type, data_flag);
task_graph, mr, extraction_cache, iter_type, data_flag);
/* Simple heuristic. */
const bool use_thread = (mr->loop_len + mr->loop_loose_len) > MIM_RANGE_LEN;

View File

@ -94,6 +94,10 @@ typedef struct MeshRenderData {
float (*loop_normals)[3];
float (*poly_normals)[3];
int *lverts, *ledges;
struct {
int *tri;
int visible_tri_len;
} mat_offsets;
} MeshRenderData;
BLI_INLINE BMFace *bm_original_face_get(const MeshRenderData *mr, int idx)
@ -238,6 +242,9 @@ MeshRenderData *mesh_render_data_create(Mesh *me,
const eMRIterType iter_type);
void mesh_render_data_free(MeshRenderData *mr);
void mesh_render_data_update_normals(MeshRenderData *mr, const eMRDataType data_flag);
void mesh_render_data_update_mat_offsets(MeshRenderData *mr,
MeshBufferExtractionCache *cache,
const eMRDataType data_flag);
void mesh_render_data_update_looptris(MeshRenderData *mr,
const eMRIterType iter_type,
const eMRDataType data_flag);

View File

@ -166,6 +166,110 @@ static void mesh_render_data_ledges_bm(const MeshRenderData *mr,
/** \} */
/* ---------------------------------------------------------------------- */
/** \name Material Offsets
*
* Material offsets contains the offset of a material after sorting tris based on their material.
*
* \{ */
static void mesh_render_data_mat_offset_load(MeshRenderData *mr,
const MeshBufferExtractionCache *cache);
static void mesh_render_data_mat_offset_ensure(MeshRenderData *mr,
MeshBufferExtractionCache *cache);
static void mesh_render_data_mat_offset_build(MeshRenderData *mr,
MeshBufferExtractionCache *cache);
static void mesh_render_data_mat_offset_build_bm(MeshRenderData *mr,
MeshBufferExtractionCache *cache);
static void mesh_render_data_mat_offset_build_mesh(MeshRenderData *mr,
MeshBufferExtractionCache *cache);
static void mesh_render_data_mat_offset_apply_offset(MeshRenderData *mr,
MeshBufferExtractionCache *cache);
void mesh_render_data_update_mat_offsets(MeshRenderData *mr,
MeshBufferExtractionCache *cache,
const eMRDataType data_flag)
{
if (data_flag & MR_DATA_MAT_OFFSETS) {
mesh_render_data_mat_offset_ensure(mr, cache);
mesh_render_data_mat_offset_load(mr, cache);
}
}
static void mesh_render_data_mat_offset_load(MeshRenderData *mr,
const MeshBufferExtractionCache *cache)
{
mr->mat_offsets.tri = cache->mat_offsets.tri;
mr->mat_offsets.visible_tri_len = cache->mat_offsets.visible_tri_len;
}
static void mesh_render_data_mat_offset_ensure(MeshRenderData *mr,
MeshBufferExtractionCache *cache)
{
if (cache->mat_offsets.tri) {
return;
}
mesh_render_data_mat_offset_build(mr, cache);
}
static void mesh_render_data_mat_offset_build(MeshRenderData *mr, MeshBufferExtractionCache *cache)
{
size_t mat_tri_idx_size = sizeof(int) * mr->mat_len;
cache->mat_offsets.tri = MEM_callocN(mat_tri_idx_size, __func__);
/* Count how many triangles for each material. */
if (mr->extract_type == MR_EXTRACT_BMESH) {
mesh_render_data_mat_offset_build_bm(mr, cache);
}
else {
mesh_render_data_mat_offset_build_mesh(mr, cache);
}
mesh_render_data_mat_offset_apply_offset(mr, cache);
}
static void mesh_render_data_mat_offset_build_bm(MeshRenderData *mr,
MeshBufferExtractionCache *cache)
{
int *mat_tri_len = cache->mat_offsets.tri;
BMIter iter;
BMFace *efa;
BM_ITER_MESH (efa, &iter, mr->bm, BM_FACES_OF_MESH) {
if (!BM_elem_flag_test(efa, BM_ELEM_HIDDEN)) {
int mat = min_ii(efa->mat_nr, mr->mat_len - 1);
mat_tri_len[mat] += efa->len - 2;
}
}
}
static void mesh_render_data_mat_offset_build_mesh(MeshRenderData *mr,
MeshBufferExtractionCache *cache)
{
int *mat_tri_len = cache->mat_offsets.tri;
const MPoly *mp = mr->mpoly;
for (int mp_index = 0; mp_index < mr->poly_len; mp_index++, mp++) {
if (!(mr->use_hide && (mp->flag & ME_HIDE))) {
int mat = min_ii(mp->mat_nr, mr->mat_len - 1);
mat_tri_len[mat] += mp->totloop - 2;
}
}
}
static void mesh_render_data_mat_offset_apply_offset(MeshRenderData *mr,
MeshBufferExtractionCache *cache)
{
int *mat_tri_len = cache->mat_offsets.tri;
int ofs = mat_tri_len[0];
mat_tri_len[0] = 0;
for (int i = 1; i < mr->mat_len; i++) {
int tmp = mat_tri_len[i];
mat_tri_len[i] = ofs;
ofs += tmp;
}
cache->mat_offsets.visible_tri_len = ofs;
}
/** \} */
/* ---------------------------------------------------------------------- */
/** \name Mesh/BMesh Interface (indirect, partially cached access to complex data).
* \{ */

View File

@ -725,6 +725,8 @@ static void mesh_buffer_extraction_cache_clear(MeshBufferExtractionCache *extrac
MEM_SAFE_FREE(extraction_cache->loose_geom.edges);
extraction_cache->loose_geom.edge_len = 0;
extraction_cache->loose_geom.vert_len = 0;
MEM_SAFE_FREE(extraction_cache->mat_offsets.tri);
}
static void mesh_batch_cache_clear(Mesh *me)

View File

@ -33,7 +33,7 @@ namespace blender::draw {
struct MeshExtract_Tri_Data {
GPUIndexBufBuilder elb;
int *tri_mat_start;
const int *tri_mat_start;
int *tri_mat_end;
};
@ -43,45 +43,9 @@ static void extract_tris_init(const MeshRenderData *mr,
void *tls_data)
{
MeshExtract_Tri_Data *data = static_cast<MeshExtract_Tri_Data *>(tls_data);
size_t mat_tri_idx_size = sizeof(int) * mr->mat_len;
data->tri_mat_start = static_cast<int *>(MEM_callocN(mat_tri_idx_size, __func__));
data->tri_mat_end = static_cast<int *>(MEM_callocN(mat_tri_idx_size, __func__));
int *mat_tri_len = data->tri_mat_start;
/* Count how many triangle for each material. */
if (mr->extract_type == MR_EXTRACT_BMESH) {
BMIter iter;
BMFace *efa;
BM_ITER_MESH (efa, &iter, mr->bm, BM_FACES_OF_MESH) {
if (!BM_elem_flag_test(efa, BM_ELEM_HIDDEN)) {
int mat = min_ii(efa->mat_nr, mr->mat_len - 1);
mat_tri_len[mat] += efa->len - 2;
}
}
}
else {
const MPoly *mp = mr->mpoly;
for (int mp_index = 0; mp_index < mr->poly_len; mp_index++, mp++) {
if (!(mr->use_hide && (mp->flag & ME_HIDE))) {
int mat = min_ii(mp->mat_nr, mr->mat_len - 1);
mat_tri_len[mat] += mp->totloop - 2;
}
}
}
/* Accumulate triangle lengths per material to have correct offsets. */
int ofs = mat_tri_len[0];
mat_tri_len[0] = 0;
for (int i = 1; i < mr->mat_len; i++) {
int tmp = mat_tri_len[i];
mat_tri_len[i] = ofs;
ofs += tmp;
}
memcpy(data->tri_mat_end, mat_tri_len, mat_tri_idx_size);
int visible_tri_tot = ofs;
GPU_indexbuf_init(&data->elb, GPU_PRIM_TRIS, visible_tri_tot, mr->loop_len);
data->tri_mat_start = mr->mat_offsets.tri;
data->tri_mat_end = static_cast<int *>(MEM_dupallocN(data->tri_mat_start));
GPU_indexbuf_init(&data->elb, GPU_PRIM_TRIS, mr->mat_offsets.visible_tri_len, mr->loop_len);
}
static void extract_tris_iter_looptri_bm(const MeshRenderData *mr,
@ -146,7 +110,6 @@ static void extract_tris_finish(const MeshRenderData *mr,
GPU_indexbuf_create_subrange_in_place(mbc_final->tris_per_mat[i], ibo, start, len);
}
}
MEM_freeN(data->tri_mat_start);
MEM_freeN(data->tri_mat_end);
}
@ -157,7 +120,7 @@ constexpr MeshExtract create_extractor_tris()
extractor.iter_looptri_bm = extract_tris_iter_looptri_bm;
extractor.iter_looptri_mesh = extract_tris_iter_looptri_mesh;
extractor.finish = extract_tris_finish;
extractor.data_type = MR_DATA_NONE;
extractor.data_type = MR_DATA_MAT_OFFSETS;
extractor.data_size = sizeof(MeshExtract_Tri_Data);
extractor.use_threading = false;
extractor.mesh_buffer_offset = offsetof(MeshBufferCache, ibo.tris);