GPU: Thread safe index buffer builders.

Current index builder is designed to be used in a single thread.
This makes all index buffer extractions single threaded.
This patch adds a thread safe solution enabling multithreaded
building of index buffers.

To reduce locking the solution would provide a task/thread local
index buffer builder (called sub builder).
When a thread is finished this thread local index buffer builder
can be joined with the initial index buffer builder.

`GPU_indexbuf_subbuilder_init`: Initialized a sub builder. The
index list is shared between the parent and sub buffer, but the
counters are localized. Ensuring that updating counters would
not need any locking.

`GPU_indexbuf_subbuilder_finish`: merge the information of the
sub builder back to the parent builder. Needs to be invoked outside
the worker thread, or when sure that all worker threads have been
finished. Internal the function is not thread safe.

For testing purposes the extract_points extractor has been migrated to
the new API. Herefore changes to the mesh extractor were needed.

* When creating tasks, the task number of current task is stored in
  ExtractTaskData including the total number of tasks.
* Adding two functions  in `MeshExtract`.
** `task_init` will initialize the task specific userdata.
** `task_finish` should merge back the task specific userdata back.
* adding task_id parameter to the iteration functions so they can
  access the correct task data without any need for locking.

There is no noticeable change in end user performance.

Reviewed By: mano-wii

Differential Revision: https://developer.blender.org/D11499
This commit is contained in:
Jeroen Bakker 2021-06-08 16:35:33 +02:00 committed by Jeroen Bakker
parent 08b0de45f3
commit 259b9c73d0
7 changed files with 241 additions and 57 deletions

View File

@ -24,11 +24,15 @@
*/
#include "MEM_guardedalloc.h"
#include <optional>
#include "atomic_ops.h"
#include "DNA_mesh_types.h"
#include "DNA_scene_types.h"
#include "BLI_array.hh"
#include "BLI_math_bits.h"
#include "BLI_task.h"
#include "BLI_vector.hh"
@ -53,6 +57,8 @@ namespace blender::draw {
/* ---------------------------------------------------------------------- */
/** \name Mesh Elements Extract Struct
* \{ */
using TaskId = int;
using TaskLen = int;
struct ExtractorRunData {
/* Extractor where this run data belongs to. */
@ -62,11 +68,23 @@ struct ExtractorRunData {
/* User data during iteration. Created in MeshExtract.init and passed along to other MeshExtract
* functions. */
void *user_data = nullptr;
std::optional<Array<void *>> task_user_datas;
ExtractorRunData(const MeshExtract *extractor) : extractor(extractor)
{
}
void init_task_user_datas(const TaskLen task_len)
{
task_user_datas = Array<void *>(task_len);
}
void *&operator[](const TaskId task_id)
{
BLI_assert(task_user_datas);
return (*task_user_datas)[task_id];
}
#ifdef WITH_CXX_GUARDEDALLOC
MEM_CXX_CLASS_ALLOC_FUNCS("DRAW:ExtractorRunData")
#endif
@ -111,7 +129,7 @@ class ExtractorRunDatas : public Vector<ExtractorRunData> {
}
}
eMRIterType iter_types()
eMRIterType iter_types() const
{
eMRIterType iter_type = static_cast<eMRIterType>(0);
@ -122,6 +140,13 @@ class ExtractorRunDatas : public Vector<ExtractorRunData> {
return iter_type;
}
const uint iter_types_len() const
{
const eMRIterType iter_type = iter_types();
uint bits = static_cast<uint>(iter_type);
return count_bits_i(bits);
}
eMRDataType data_types()
{
eMRDataType data_type = static_cast<eMRDataType>(0);
@ -158,7 +183,8 @@ BLI_INLINE void extract_init(const MeshRenderData *mr,
BLI_INLINE void extract_iter_looptri_bm(const MeshRenderData *mr,
const ExtractTriBMesh_Params *params,
const ExtractorRunDatas &all_extractors)
const ExtractorRunDatas &all_extractors,
const TaskId task_id)
{
ExtractorRunDatas extractors;
all_extractors.filter_into(extractors, MR_ITER_LOOPTRI);
@ -166,7 +192,7 @@ BLI_INLINE void extract_iter_looptri_bm(const MeshRenderData *mr,
EXTRACT_TRIS_LOOPTRI_FOREACH_BM_BEGIN(elt, elt_index, params)
{
for (ExtractorRunData &run_data : extractors) {
run_data.extractor->iter_looptri_bm(mr, elt, elt_index, run_data.user_data);
run_data.extractor->iter_looptri_bm(mr, elt, elt_index, run_data[task_id]);
}
}
EXTRACT_TRIS_LOOPTRI_FOREACH_BM_END;
@ -174,7 +200,8 @@ BLI_INLINE void extract_iter_looptri_bm(const MeshRenderData *mr,
BLI_INLINE void extract_iter_looptri_mesh(const MeshRenderData *mr,
const ExtractTriMesh_Params *params,
const ExtractorRunDatas &all_extractors)
const ExtractorRunDatas &all_extractors,
const TaskId task_id)
{
ExtractorRunDatas extractors;
all_extractors.filter_into(extractors, MR_ITER_LOOPTRI);
@ -182,7 +209,7 @@ BLI_INLINE void extract_iter_looptri_mesh(const MeshRenderData *mr,
EXTRACT_TRIS_LOOPTRI_FOREACH_MESH_BEGIN(mlt, mlt_index, params)
{
for (ExtractorRunData &run_data : extractors) {
run_data.extractor->iter_looptri_mesh(mr, mlt, mlt_index, run_data.user_data);
run_data.extractor->iter_looptri_mesh(mr, mlt, mlt_index, run_data[task_id]);
}
}
EXTRACT_TRIS_LOOPTRI_FOREACH_MESH_END;
@ -190,7 +217,8 @@ BLI_INLINE void extract_iter_looptri_mesh(const MeshRenderData *mr,
BLI_INLINE void extract_iter_poly_bm(const MeshRenderData *mr,
const ExtractPolyBMesh_Params *params,
const ExtractorRunDatas &all_extractors)
const ExtractorRunDatas &all_extractors,
const TaskId task_id)
{
ExtractorRunDatas extractors;
all_extractors.filter_into(extractors, MR_ITER_POLY);
@ -198,7 +226,7 @@ BLI_INLINE void extract_iter_poly_bm(const MeshRenderData *mr,
EXTRACT_POLY_FOREACH_BM_BEGIN(f, f_index, params, mr)
{
for (ExtractorRunData &run_data : extractors) {
run_data.extractor->iter_poly_bm(mr, f, f_index, run_data.user_data);
run_data.extractor->iter_poly_bm(mr, f, f_index, run_data[task_id]);
}
}
EXTRACT_POLY_FOREACH_BM_END;
@ -206,7 +234,8 @@ BLI_INLINE void extract_iter_poly_bm(const MeshRenderData *mr,
BLI_INLINE void extract_iter_poly_mesh(const MeshRenderData *mr,
const ExtractPolyMesh_Params *params,
const ExtractorRunDatas &all_extractors)
const ExtractorRunDatas &all_extractors,
const TaskId task_id)
{
ExtractorRunDatas extractors;
all_extractors.filter_into(extractors, MR_ITER_POLY);
@ -214,7 +243,7 @@ BLI_INLINE void extract_iter_poly_mesh(const MeshRenderData *mr,
EXTRACT_POLY_FOREACH_MESH_BEGIN(mp, mp_index, params, mr)
{
for (ExtractorRunData &run_data : extractors) {
run_data.extractor->iter_poly_mesh(mr, mp, mp_index, run_data.user_data);
run_data.extractor->iter_poly_mesh(mr, mp, mp_index, run_data[task_id]);
}
}
EXTRACT_POLY_FOREACH_MESH_END;
@ -222,7 +251,8 @@ BLI_INLINE void extract_iter_poly_mesh(const MeshRenderData *mr,
BLI_INLINE void extract_iter_ledge_bm(const MeshRenderData *mr,
const ExtractLEdgeBMesh_Params *params,
const ExtractorRunDatas &all_extractors)
const ExtractorRunDatas &all_extractors,
const TaskId task_id)
{
ExtractorRunDatas extractors;
all_extractors.filter_into(extractors, MR_ITER_LEDGE);
@ -230,7 +260,7 @@ BLI_INLINE void extract_iter_ledge_bm(const MeshRenderData *mr,
EXTRACT_LEDGE_FOREACH_BM_BEGIN(eed, ledge_index, params)
{
for (ExtractorRunData &run_data : extractors) {
run_data.extractor->iter_ledge_bm(mr, eed, ledge_index, run_data.user_data);
run_data.extractor->iter_ledge_bm(mr, eed, ledge_index, run_data[task_id]);
}
}
EXTRACT_LEDGE_FOREACH_BM_END;
@ -238,7 +268,8 @@ BLI_INLINE void extract_iter_ledge_bm(const MeshRenderData *mr,
BLI_INLINE void extract_iter_ledge_mesh(const MeshRenderData *mr,
const ExtractLEdgeMesh_Params *params,
const ExtractorRunDatas &all_extractors)
const ExtractorRunDatas &all_extractors,
const TaskId task_id)
{
ExtractorRunDatas extractors;
all_extractors.filter_into(extractors, MR_ITER_LEDGE);
@ -246,7 +277,7 @@ BLI_INLINE void extract_iter_ledge_mesh(const MeshRenderData *mr,
EXTRACT_LEDGE_FOREACH_MESH_BEGIN(med, ledge_index, params, mr)
{
for (ExtractorRunData &run_data : extractors) {
run_data.extractor->iter_ledge_mesh(mr, med, ledge_index, run_data.user_data);
run_data.extractor->iter_ledge_mesh(mr, med, ledge_index, run_data[task_id]);
}
}
EXTRACT_LEDGE_FOREACH_MESH_END;
@ -254,7 +285,8 @@ BLI_INLINE void extract_iter_ledge_mesh(const MeshRenderData *mr,
BLI_INLINE void extract_iter_lvert_bm(const MeshRenderData *mr,
const ExtractLVertBMesh_Params *params,
const ExtractorRunDatas &all_extractors)
const ExtractorRunDatas &all_extractors,
const TaskId task_id)
{
ExtractorRunDatas extractors;
all_extractors.filter_into(extractors, MR_ITER_LVERT);
@ -262,7 +294,7 @@ BLI_INLINE void extract_iter_lvert_bm(const MeshRenderData *mr,
EXTRACT_LVERT_FOREACH_BM_BEGIN(eve, lvert_index, params)
{
for (ExtractorRunData &run_data : extractors) {
run_data.extractor->iter_lvert_bm(mr, eve, lvert_index, run_data.user_data);
run_data.extractor->iter_lvert_bm(mr, eve, lvert_index, run_data[task_id]);
}
}
EXTRACT_LVERT_FOREACH_BM_END;
@ -270,7 +302,8 @@ BLI_INLINE void extract_iter_lvert_bm(const MeshRenderData *mr,
BLI_INLINE void extract_iter_lvert_mesh(const MeshRenderData *mr,
const ExtractLVertMesh_Params *params,
const ExtractorRunDatas &all_extractors)
const ExtractorRunDatas &all_extractors,
const TaskId task_id)
{
ExtractorRunDatas extractors;
all_extractors.filter_into(extractors, MR_ITER_LVERT);
@ -278,7 +311,7 @@ BLI_INLINE void extract_iter_lvert_mesh(const MeshRenderData *mr,
EXTRACT_LVERT_FOREACH_MESH_BEGIN(mv, lvert_index, params, mr)
{
for (ExtractorRunData &run_data : extractors) {
run_data.extractor->iter_lvert_mesh(mr, mv, lvert_index, run_data.user_data);
run_data.extractor->iter_lvert_mesh(mr, mv, lvert_index, run_data[task_id]);
}
}
EXTRACT_LVERT_FOREACH_MESH_END;
@ -296,6 +329,35 @@ BLI_INLINE void extract_finish(const MeshRenderData *mr,
}
}
BLI_INLINE void extract_task_init(ExtractorRunDatas &extractors, const TaskLen task_len)
{
for (ExtractorRunData &run_data : extractors) {
run_data.init_task_user_datas(task_len);
const MeshExtract *extractor = run_data.extractor;
for (TaskId task_id = 0; task_id < task_len; task_id++) {
void *user_task_data = run_data.user_data;
if (extractor->task_init) {
user_task_data = extractor->task_init(run_data.user_data);
}
run_data[task_id] = user_task_data;
}
}
}
BLI_INLINE void extract_task_finish(ExtractorRunDatas &extractors, const TaskLen task_len)
{
for (ExtractorRunData &run_data : extractors) {
const MeshExtract *extractor = run_data.extractor;
if (extractor->task_finish) {
for (TaskId task_id = 0; task_id < task_len; task_id++) {
void *task_user_data = run_data[task_id];
extractor->task_finish(run_data.user_data, task_user_data);
run_data[task_id] = nullptr;
}
}
}
}
/* Single Thread. */
BLI_INLINE void extract_run_single_threaded(const MeshRenderData *mr,
struct MeshBatchCache *cache,
@ -303,7 +365,11 @@ BLI_INLINE void extract_run_single_threaded(const MeshRenderData *mr,
eMRIterType iter_type,
MeshBufferCache *mbc)
{
const TaskLen task_len = 1;
const TaskId task_id = 0;
extract_init(mr, cache, extractors, mbc);
extract_task_init(extractors, task_len);
bool is_mesh = mr->extract_type != MR_EXTRACT_BMESH;
if (iter_type & MR_ITER_LOOPTRI) {
@ -312,14 +378,14 @@ BLI_INLINE void extract_run_single_threaded(const MeshRenderData *mr,
params.mlooptri = mr->mlooptri;
params.tri_range[0] = 0;
params.tri_range[1] = mr->tri_len;
extract_iter_looptri_mesh(mr, &params, extractors);
extract_iter_looptri_mesh(mr, &params, extractors, task_id);
}
else {
ExtractTriBMesh_Params params;
params.looptris = mr->edit_bmesh->looptris;
params.tri_range[0] = 0;
params.tri_range[1] = mr->tri_len;
extract_iter_looptri_bm(mr, &params, extractors);
extract_iter_looptri_bm(mr, &params, extractors, task_id);
}
}
if (iter_type & MR_ITER_POLY) {
@ -327,13 +393,13 @@ BLI_INLINE void extract_run_single_threaded(const MeshRenderData *mr,
ExtractPolyMesh_Params params;
params.poly_range[0] = 0;
params.poly_range[1] = mr->poly_len;
extract_iter_poly_mesh(mr, &params, extractors);
extract_iter_poly_mesh(mr, &params, extractors, task_id);
}
else {
ExtractPolyBMesh_Params params;
params.poly_range[0] = 0;
params.poly_range[1] = mr->poly_len;
extract_iter_poly_bm(mr, &params, extractors);
extract_iter_poly_bm(mr, &params, extractors, task_id);
}
}
if (iter_type & MR_ITER_LEDGE) {
@ -342,14 +408,14 @@ BLI_INLINE void extract_run_single_threaded(const MeshRenderData *mr,
params.ledge = mr->ledges;
params.ledge_range[0] = 0;
params.ledge_range[1] = mr->edge_loose_len;
extract_iter_ledge_mesh(mr, &params, extractors);
extract_iter_ledge_mesh(mr, &params, extractors, task_id);
}
else {
ExtractLEdgeBMesh_Params params;
params.ledge = mr->ledges;
params.ledge_range[0] = 0;
params.ledge_range[1] = mr->edge_loose_len;
extract_iter_ledge_bm(mr, &params, extractors);
extract_iter_ledge_bm(mr, &params, extractors, task_id);
}
}
if (iter_type & MR_ITER_LVERT) {
@ -358,16 +424,17 @@ BLI_INLINE void extract_run_single_threaded(const MeshRenderData *mr,
params.lvert = mr->lverts;
params.lvert_range[0] = 0;
params.lvert_range[1] = mr->vert_loose_len;
extract_iter_lvert_mesh(mr, &params, extractors);
extract_iter_lvert_mesh(mr, &params, extractors, task_id);
}
else {
ExtractLVertBMesh_Params params;
params.lvert = mr->lverts;
params.lvert_range[0] = 0;
params.lvert_range[1] = mr->vert_loose_len;
extract_iter_lvert_bm(mr, &params, extractors);
extract_iter_lvert_bm(mr, &params, extractors, task_id);
}
}
extract_task_finish(extractors, task_len);
extract_finish(mr, cache, extractors);
}
@ -390,6 +457,13 @@ struct ExtractTaskData {
MeshBufferCache *mbc = nullptr;
int32_t *task_counter = nullptr;
/* Total number of tasks that are created for multi threaded extraction.
* (= 1 for single threaded extractors). */
uint task_len;
/* Task id of the extraction task. Must never exceed task_len. (= 0 for single threaded
* extractors). */
uint task_id = 0;
eMRIterType iter_type;
int start = 0;
int end = INT_MAX;
@ -399,8 +473,14 @@ struct ExtractTaskData {
struct MeshBatchCache *cache,
ExtractorRunDatas *extractors,
MeshBufferCache *mbc,
int32_t *task_counter)
: mr(mr), cache(cache), extractors(extractors), mbc(mbc), task_counter(task_counter)
int32_t *task_counter,
const uint task_len)
: mr(mr),
cache(cache),
extractors(extractors),
mbc(mbc),
task_counter(task_counter),
task_len(task_len)
{
iter_type = extractors->iter_types();
};
@ -417,17 +497,6 @@ struct ExtractTaskData {
#endif
};
static ExtractTaskData *extract_extract_iter_task_data_create_mesh(const MeshRenderData *mr,
MeshBatchCache *cache,
ExtractorRunDatas *extractors,
MeshBufferCache *mbc,
int32_t *task_counter)
{
ExtractTaskData *taskdata = new ExtractTaskData(mr, cache, extractors, mbc, task_counter);
return taskdata;
}
static void extract_task_data_free(void *data)
{
ExtractTaskData *task_data = static_cast<ExtractTaskData *>(data);
@ -445,7 +514,8 @@ BLI_INLINE void mesh_extract_iter(const MeshRenderData *mr,
const eMRIterType iter_type,
int start,
int end,
ExtractorRunDatas &extractors)
ExtractorRunDatas &extractors,
const TaskId task_id)
{
switch (mr->extract_type) {
case MR_EXTRACT_BMESH:
@ -454,27 +524,27 @@ BLI_INLINE void mesh_extract_iter(const MeshRenderData *mr,
params.looptris = mr->edit_bmesh->looptris;
params.tri_range[0] = start;
params.tri_range[1] = min_ii(mr->tri_len, end);
extract_iter_looptri_bm(mr, &params, extractors);
extract_iter_looptri_bm(mr, &params, extractors, task_id);
}
if (iter_type & MR_ITER_POLY) {
ExtractPolyBMesh_Params params;
params.poly_range[0] = start;
params.poly_range[1] = min_ii(mr->poly_len, end);
extract_iter_poly_bm(mr, &params, extractors);
extract_iter_poly_bm(mr, &params, extractors, task_id);
}
if (iter_type & MR_ITER_LEDGE) {
ExtractLEdgeBMesh_Params params;
params.ledge = mr->ledges;
params.ledge_range[0] = start;
params.ledge_range[1] = min_ii(mr->edge_loose_len, end);
extract_iter_ledge_bm(mr, &params, extractors);
extract_iter_ledge_bm(mr, &params, extractors, task_id);
}
if (iter_type & MR_ITER_LVERT) {
ExtractLVertBMesh_Params params;
params.lvert = mr->lverts;
params.lvert_range[0] = start;
params.lvert_range[1] = min_ii(mr->vert_loose_len, end);
extract_iter_lvert_bm(mr, &params, extractors);
extract_iter_lvert_bm(mr, &params, extractors, task_id);
}
break;
case MR_EXTRACT_MAPPED:
@ -484,27 +554,27 @@ BLI_INLINE void mesh_extract_iter(const MeshRenderData *mr,
params.mlooptri = mr->mlooptri;
params.tri_range[0] = start;
params.tri_range[1] = min_ii(mr->tri_len, end);
extract_iter_looptri_mesh(mr, &params, extractors);
extract_iter_looptri_mesh(mr, &params, extractors, task_id);
}
if (iter_type & MR_ITER_POLY) {
ExtractPolyMesh_Params params;
params.poly_range[0] = start;
params.poly_range[1] = min_ii(mr->poly_len, end);
extract_iter_poly_mesh(mr, &params, extractors);
extract_iter_poly_mesh(mr, &params, extractors, task_id);
}
if (iter_type & MR_ITER_LEDGE) {
ExtractLEdgeMesh_Params params;
params.ledge = mr->ledges;
params.ledge_range[0] = start;
params.ledge_range[1] = min_ii(mr->edge_loose_len, end);
extract_iter_ledge_mesh(mr, &params, extractors);
extract_iter_ledge_mesh(mr, &params, extractors, task_id);
}
if (iter_type & MR_ITER_LVERT) {
ExtractLVertMesh_Params params;
params.lvert = mr->lverts;
params.lvert_range[0] = start;
params.lvert_range[1] = min_ii(mr->vert_loose_len, end);
extract_iter_lvert_mesh(mr, &params, extractors);
extract_iter_lvert_mesh(mr, &params, extractors, task_id);
}
break;
}
@ -513,16 +583,19 @@ BLI_INLINE void mesh_extract_iter(const MeshRenderData *mr,
static void extract_task_init(ExtractTaskData *data)
{
extract_init(data->mr, data->cache, *data->extractors, data->mbc);
extract_task_init(*data->extractors, data->task_len);
}
static void extract_task_run(void *__restrict taskdata)
{
ExtractTaskData *data = (ExtractTaskData *)taskdata;
mesh_extract_iter(data->mr, data->iter_type, data->start, data->end, *data->extractors);
mesh_extract_iter(
data->mr, data->iter_type, data->start, data->end, *data->extractors, data->task_id);
/* If this is the last task, we do the finish function. */
int remainin_tasks = atomic_sub_and_fetch_int32(data->task_counter, 1);
if (remainin_tasks == 0) {
extract_task_finish(*data->extractors, data->task_len);
extract_finish(data->mr, data->cache, *data->extractors);
}
}
@ -668,7 +741,8 @@ static void extract_range_task_create(struct TaskGraph *task_graph,
int length)
{
taskdata = new ExtractTaskData(*taskdata);
atomic_add_and_fetch_int32(taskdata->task_counter, 1);
taskdata->task_id = atomic_fetch_and_add_int32(taskdata->task_counter, 1);
BLI_assert(taskdata->task_id < taskdata->task_len);
taskdata->iter_type = type;
taskdata->start = start;
taskdata->end = start + length;
@ -900,8 +974,8 @@ static void mesh_buffer_cache_create_requested(struct TaskGraph *task_graph,
if (!extractor->use_threading) {
ExtractorRunDatas *single_threaded_extractors = new ExtractorRunDatas();
single_threaded_extractors->append(extractor);
ExtractTaskData *taskdata = extract_extract_iter_task_data_create_mesh(
mr, cache, single_threaded_extractors, mbc, nullptr);
ExtractTaskData *taskdata = new ExtractTaskData(
mr, cache, single_threaded_extractors, mbc, nullptr, 1);
struct TaskNode *task_node = extract_single_threaded_task_node_create(task_graph,
taskdata);
BLI_task_graph_edge_create(task_node_mesh_render_data, task_node);
@ -920,13 +994,19 @@ static void mesh_buffer_cache_create_requested(struct TaskGraph *task_graph,
*/
int num_threads = BLI_task_scheduler_num_threads();
num_threads -= single_threaded_extractors_len % num_threads;
const int max_multithreaded_task_len = multi_threaded_extractors->iter_types_len() +
num_threads;
UserDataInitTaskData *user_data_init_task_data = new UserDataInitTaskData();
struct TaskNode *task_node_user_data_init = user_data_init_task_node_create(
task_graph, user_data_init_task_data);
user_data_init_task_data->td = extract_extract_iter_task_data_create_mesh(
mr, cache, multi_threaded_extractors, mbc, &user_data_init_task_data->task_counter);
user_data_init_task_data->td = new ExtractTaskData(mr,
cache,
multi_threaded_extractors,
mbc,
&user_data_init_task_data->task_counter,
max_multithreaded_task_len);
extract_task_in_ranges_create(
task_graph, task_node_user_data_init, user_data_init_task_data->td, num_threads);
@ -941,8 +1021,7 @@ static void mesh_buffer_cache_create_requested(struct TaskGraph *task_graph,
else {
/* Run all requests on the same thread. */
ExtractorRunDatas *extractors_copy = new ExtractorRunDatas(extractors);
ExtractTaskData *taskdata = extract_extract_iter_task_data_create_mesh(
mr, cache, extractors_copy, mbc, nullptr);
ExtractTaskData *taskdata = new ExtractTaskData(mr, cache, extractors_copy, mbc, nullptr, 1);
struct TaskNode *task_node = extract_single_threaded_task_node_create(task_graph, taskdata);
BLI_task_graph_edge_create(task_node_mesh_render_data, task_node);

View File

@ -406,7 +406,6 @@ typedef void(ExtractLVertMeshFn)(const MeshRenderData *mr,
/* ---------------------------------------------------------------------- */
/** \name Mesh Elements Extract Struct
* \{ */
/* TODO(jbakker): move parameters inside a struct. */
typedef void *(ExtractInitFn)(const MeshRenderData *mr,
struct MeshBatchCache *cache,
@ -415,10 +414,14 @@ typedef void(ExtractFinishFn)(const MeshRenderData *mr,
struct MeshBatchCache *cache,
void *buffer,
void *data);
typedef void *(ExtractTaskInitFn)(void *userdata);
typedef void(ExtractTaskFinishFn)(void *userdata, void *task_userdata);
typedef struct MeshExtract {
/** Executed on main thread and return user data for iteration functions. */
ExtractInitFn *init;
/** Task local data. */
ExtractTaskInitFn *task_init;
/** Executed on one (or more if use_threading) worker thread(s). */
ExtractTriBMeshFn *iter_looptri_bm;
ExtractTriMeshFn *iter_looptri_mesh;
@ -429,6 +432,7 @@ typedef struct MeshExtract {
ExtractLVertBMeshFn *iter_lvert_bm;
ExtractLVertMeshFn *iter_lvert_mesh;
/** Executed on one worker thread after all elements iterations. */
ExtractTaskFinishFn *task_finish;
ExtractFinishFn *finish;
/** Used to request common data. */
eMRDataType data_type;

View File

@ -41,6 +41,15 @@ static void *extract_points_init(const MeshRenderData *mr,
return elb;
}
static void *extract_points_task_init(void *_userdata)
{
GPUIndexBufBuilder *elb = static_cast<GPUIndexBufBuilder *>(_userdata);
GPUIndexBufBuilder *sub_builder = static_cast<GPUIndexBufBuilder *>(
MEM_mallocN(sizeof(*sub_builder), __func__));
GPU_indexbuf_subbuilder_init(elb, sub_builder);
return sub_builder;
}
BLI_INLINE void vert_set_bm(GPUIndexBufBuilder *elb, BMVert *eve, int l_index)
{
const int v_index = BM_elem_index_get(eve);
@ -137,6 +146,14 @@ static void extract_points_iter_lvert_mesh(const MeshRenderData *mr,
vert_set_mesh(elb, mr, mr->lverts[lvert_index], offset + lvert_index);
}
static void extract_points_task_finish(void *_userdata, void *_task_userdata)
{
GPUIndexBufBuilder *elb = static_cast<GPUIndexBufBuilder *>(_userdata);
GPUIndexBufBuilder *sub_builder = static_cast<GPUIndexBufBuilder *>(_task_userdata);
GPU_indexbuf_subbuilder_finish(elb, sub_builder);
MEM_freeN(sub_builder);
}
static void extract_points_finish(const MeshRenderData *UNUSED(mr),
struct MeshBatchCache *UNUSED(cache),
void *buf,
@ -152,15 +169,17 @@ constexpr MeshExtract create_extractor_points()
{
MeshExtract extractor = {0};
extractor.init = extract_points_init;
extractor.task_init = extract_points_task_init;
extractor.iter_poly_bm = extract_points_iter_poly_bm;
extractor.iter_poly_mesh = extract_points_iter_poly_mesh;
extractor.iter_ledge_bm = extract_points_iter_ledge_bm;
extractor.iter_ledge_mesh = extract_points_iter_ledge_mesh;
extractor.iter_lvert_bm = extract_points_iter_lvert_bm;
extractor.iter_lvert_mesh = extract_points_iter_lvert_mesh;
extractor.task_finish = extract_points_task_finish;
extractor.finish = extract_points_finish;
extractor.use_threading = true;
extractor.data_type = MR_DATA_NONE;
extractor.use_threading = false;
extractor.mesh_buffer_offset = offsetof(MeshBufferCache, ibo.points);
return extractor;
}

View File

@ -394,6 +394,8 @@ if(WITH_GTESTS)
if(WITH_OPENGL_DRAW_TESTS)
set(TEST_SRC
tests/gpu_testing.cc
tests/gpu_index_buffer_test.cc
tests/gpu_shader_test.cc
tests/gpu_testing.hh

View File

@ -44,6 +44,7 @@ typedef struct GPUIndexBufBuilder {
uint index_max;
GPUPrimType prim_type;
uint32_t *data;
const struct GPUIndexBufBuilder *parent;
} GPUIndexBufBuilder;
/* supports all primitive types. */
@ -53,6 +54,19 @@ void GPU_indexbuf_init_ex(GPUIndexBufBuilder *, GPUPrimType, uint index_len, uin
void GPU_indexbuf_init(GPUIndexBufBuilder *, GPUPrimType, uint prim_len, uint vertex_len);
GPUIndexBuf *GPU_indexbuf_build_on_device(uint index_len);
/*
* Thread safe sub builders.
*
* Note that `GPU_indexbuf_subbuilder_init` and `GPU_indexbuf_subbuilder_finish` are not thread
* safe and should be called when no threads are active. The pattern is to create a subbuilder for
* each thread/task. Each thread/task would update their sub builder. When all thread are completed
* the sub-builders can then be merged back to the parent builder.
*/
void GPU_indexbuf_subbuilder_init(const GPUIndexBufBuilder *parent_builder,
GPUIndexBufBuilder *sub_builder);
void GPU_indexbuf_subbuilder_finish(GPUIndexBufBuilder *builder,
const GPUIndexBufBuilder *parent_builder);
void GPU_indexbuf_add_generic_vert(GPUIndexBufBuilder *, uint v);
void GPU_indexbuf_add_primitive_restart(GPUIndexBufBuilder *);

View File

@ -25,6 +25,7 @@
#include "MEM_guardedalloc.h"
#include "BLI_math_base.h"
#include "BLI_utildefines.h"
#include "gpu_backend.hh"
@ -56,6 +57,7 @@ void GPU_indexbuf_init_ex(GPUIndexBufBuilder *builder,
builder->index_max = 0;
builder->prim_type = prim_type;
builder->data = (uint *)MEM_callocN(builder->max_index_len * sizeof(uint), "GPUIndexBuf data");
builder->parent = nullptr;
}
void GPU_indexbuf_init(GPUIndexBufBuilder *builder,
@ -78,6 +80,23 @@ GPUIndexBuf *GPU_indexbuf_build_on_device(uint index_len)
return elem_;
}
void GPU_indexbuf_subbuilder_init(const GPUIndexBufBuilder *parent_builder,
GPUIndexBufBuilder *sub_builder)
{
BLI_assert(parent_builder->parent == nullptr);
memcpy(sub_builder, parent_builder, sizeof(GPUIndexBufBuilder));
sub_builder->parent = parent_builder;
}
void GPU_indexbuf_subbuilder_finish(GPUIndexBufBuilder *parent_builder,
const GPUIndexBufBuilder *sub_builder)
{
BLI_assert(parent_builder == sub_builder->parent);
parent_builder->index_len = max_uu(parent_builder->index_len, sub_builder->index_len);
parent_builder->index_min = min_uu(parent_builder->index_min, sub_builder->index_min);
parent_builder->index_max = max_uu(parent_builder->index_max, sub_builder->index_max);
}
void GPU_indexbuf_add_generic_vert(GPUIndexBufBuilder *builder, uint v)
{
#if TRUST_NO_ONE

View File

@ -0,0 +1,47 @@
/* Apache License, Version 2.0 */
#include "testing/testing.h"
#include "MEM_guardedalloc.h"
#include "GPU_index_buffer.h"
#include "gpu_testing.hh"
namespace blender::gpu::tests {
TEST_F(GPUTest, gpu_index_buffer_subbuilders)
{
const uint num_subbuilders = 10;
const uint verts_per_subbuilders = 100;
const uint vertex_len = num_subbuilders * verts_per_subbuilders;
GPUIndexBufBuilder builder;
GPU_indexbuf_init(&builder, GPU_PRIM_POINTS, vertex_len, vertex_len);
GPUIndexBufBuilder subbuilders[num_subbuilders];
for (int subbuilder_index = 0; subbuilder_index < num_subbuilders; subbuilder_index++) {
GPU_indexbuf_subbuilder_init(&builder, &subbuilders[subbuilder_index]);
}
for (int subbuilder_index = 0; subbuilder_index < num_subbuilders; subbuilder_index++) {
GPUIndexBufBuilder &subbuilder = subbuilders[subbuilder_index];
for (int subbuilder_vert_index = 0; subbuilder_vert_index < verts_per_subbuilders;
subbuilder_vert_index++) {
int vert_index_to_update = subbuilder_index * verts_per_subbuilders + subbuilder_vert_index;
GPU_indexbuf_set_point_vert(&subbuilder, vert_index_to_update, vert_index_to_update);
}
}
for (int subbuilder_index = 0; subbuilder_index < num_subbuilders; subbuilder_index++) {
EXPECT_EQ(builder.index_len, subbuilder_index * verts_per_subbuilders);
GPU_indexbuf_subbuilder_finish(&builder, &subbuilders[subbuilder_index]);
EXPECT_EQ(builder.index_len, (subbuilder_index + 1) * verts_per_subbuilders);
}
GPUIndexBuf *index_buffer = GPU_indexbuf_build(&builder);
EXPECT_NE(index_buffer, nullptr);
GPU_INDEXBUF_DISCARD_SAFE(index_buffer);
}
} // namespace blender::gpu::tests