diff options
author | Hans Goudey <h.goudey@me.com> | 2021-06-08 19:48:18 +0300 |
---|---|---|
committer | Hans Goudey <h.goudey@me.com> | 2021-06-08 19:48:18 +0300 |
commit | 4aae9881636b44942912d26b5ab2d19c4a7b329f (patch) | |
tree | 16f3772307277a73be0dfb8d77ee12e571b5746e /source | |
parent | bd75d9f44cad4edb3b24b5a54495cc1898d32b5e (diff) | |
parent | 22ee056c3abb0b58ca482d57afa51ae61cbed575 (diff) |
Merge branch 'master' into temp-geometry-nodes-curve-deform-node
Diffstat (limited to 'source')
67 files changed, 2220 insertions, 1192 deletions
diff --git a/source/blender/blenkernel/BKE_node.h b/source/blender/blenkernel/BKE_node.h index 89d9ebdc5b3..48c3d9b2d97 100644 --- a/source/blender/blenkernel/BKE_node.h +++ b/source/blender/blenkernel/BKE_node.h @@ -1432,7 +1432,8 @@ int ntreeTexExecTree(struct bNodeTree *ntree, #define GEO_NODE_MESH_TO_CURVE 1052 #define GEO_NODE_DELETE_GEOMETRY 1053 #define GEO_NODE_CURVE_LENGTH 1054 -#define GEO_NODE_CURVE_DEFORM 1055 +#define GEO_NODE_SELECT_BY_MATERIAL 1055 +#define GEO_NODE_CURVE_DEFORM 1056 /** \} */ diff --git a/source/blender/blenkernel/intern/curve_bevel.c b/source/blender/blenkernel/intern/curve_bevel.c index 51418a6485f..7f2cdfa59d3 100644 --- a/source/blender/blenkernel/intern/curve_bevel.c +++ b/source/blender/blenkernel/intern/curve_bevel.c @@ -56,7 +56,9 @@ static CurveBevelFillType curve_bevel_get_fill_type(const Curve *curve) return (curve->flag & CU_FRONT) ? FRONT : BACK; } -static void bevel_quarter_fill(Curve *curve, float *quarter_coords_x, float *quarter_coords_y) +static void bevel_quarter_fill(const Curve *curve, + float *quarter_coords_x, + float *quarter_coords_y) { if (curve->bevel_mode == CU_BEV_MODE_ROUND) { float angle = 0.0f; diff --git a/source/blender/blenkernel/intern/editmesh_tangent.c b/source/blender/blenkernel/intern/editmesh_tangent.c index d849f4ab37d..088a2087a96 100644 --- a/source/blender/blenkernel/intern/editmesh_tangent.c +++ b/source/blender/blenkernel/intern/editmesh_tangent.c @@ -362,7 +362,7 @@ void BKE_editmesh_loop_tangent_calc(BMEditMesh *em, /* Calculation */ if (em->tottri != 0) { TaskPool *task_pool; - task_pool = BLI_task_pool_create(NULL, TASK_PRIORITY_LOW); + task_pool = BLI_task_pool_create(NULL, TASK_PRIORITY_LOW, TASK_ISOLATION_ON); tangent_mask_curr = 0; /* Calculate tangent layers */ diff --git a/source/blender/blenkernel/intern/lib_override.c b/source/blender/blenkernel/intern/lib_override.c index c93971e7b11..6b2ffa3b944 100644 --- a/source/blender/blenkernel/intern/lib_override.c +++ b/source/blender/blenkernel/intern/lib_override.c @@ -2335,7 +2335,8 @@ bool BKE_lib_override_library_main_operations_create(Main *bmain, const bool for } struct LibOverrideOpCreateData create_pool_data = {.bmain = bmain, .changed = false}; - TaskPool *task_pool = BLI_task_pool_create(&create_pool_data, TASK_PRIORITY_HIGH); + TaskPool *task_pool = BLI_task_pool_create( + &create_pool_data, TASK_PRIORITY_HIGH, TASK_ISOLATION_ON); FOREACH_MAIN_ID_BEGIN (bmain, id) { if (!ID_IS_LINKED(id) && ID_IS_OVERRIDE_LIBRARY_REAL(id) && diff --git a/source/blender/blenkernel/intern/mesh_evaluate.c b/source/blender/blenkernel/intern/mesh_evaluate.c index 616ec79c099..345546bc9cf 100644 --- a/source/blender/blenkernel/intern/mesh_evaluate.c +++ b/source/blender/blenkernel/intern/mesh_evaluate.c @@ -1715,7 +1715,8 @@ void BKE_mesh_normals_loop_split(const MVert *mverts, loop_split_generator(NULL, &common_data); } else { - TaskPool *task_pool = BLI_task_pool_create(&common_data, TASK_PRIORITY_HIGH); + TaskPool *task_pool = BLI_task_pool_create( + &common_data, TASK_PRIORITY_HIGH, TASK_ISOLATION_ON); loop_split_generator(task_pool, &common_data); diff --git a/source/blender/blenkernel/intern/mesh_tangent.c b/source/blender/blenkernel/intern/mesh_tangent.c index 2e22e521a13..6a7ff0851f5 100644 --- a/source/blender/blenkernel/intern/mesh_tangent.c +++ b/source/blender/blenkernel/intern/mesh_tangent.c @@ -656,7 +656,7 @@ void BKE_mesh_calc_loop_tangent_ex(const MVert *mvert, /* Calculation */ if (looptri_len != 0) { - TaskPool *task_pool = BLI_task_pool_create(NULL, TASK_PRIORITY_LOW); + TaskPool *task_pool = BLI_task_pool_create(NULL, TASK_PRIORITY_LOW, TASK_ISOLATION_ON); tangent_mask_curr = 0; /* Calculate tangent layers */ diff --git a/source/blender/blenkernel/intern/node.cc b/source/blender/blenkernel/intern/node.cc index 8e4bf4ea92f..fa6f8926d2a 100644 --- a/source/blender/blenkernel/intern/node.cc +++ b/source/blender/blenkernel/intern/node.cc @@ -5079,6 +5079,7 @@ static void registerGeometryNodes() register_node_type_geo_point_translate(); register_node_type_geo_points_to_volume(); register_node_type_geo_sample_texture(); + register_node_type_geo_select_by_material(); register_node_type_geo_subdivide(); register_node_type_geo_subdivision_surface(); register_node_type_geo_switch(); diff --git a/source/blender/blenkernel/intern/ocean.c b/source/blender/blenkernel/intern/ocean.c index 9d53dad8d03..9b9ed0adcf4 100644 --- a/source/blender/blenkernel/intern/ocean.c +++ b/source/blender/blenkernel/intern/ocean.c @@ -663,7 +663,7 @@ void BKE_ocean_simulate(struct Ocean *o, float t, float scale, float chop_amount osd.scale = scale; osd.chop_amount = chop_amount; - pool = BLI_task_pool_create(&osd, TASK_PRIORITY_HIGH); + pool = BLI_task_pool_create(&osd, TASK_PRIORITY_HIGH, TASK_ISOLATION_ON); BLI_rw_mutex_lock(&o->oceanmutex, THREAD_LOCK_WRITE); diff --git a/source/blender/blenkernel/intern/particle.c b/source/blender/blenkernel/intern/particle.c index a873ecec6f1..3ae5d039125 100644 --- a/source/blender/blenkernel/intern/particle.c +++ b/source/blender/blenkernel/intern/particle.c @@ -3179,7 +3179,7 @@ void psys_cache_child_paths(ParticleSimulationData *sim, return; } - task_pool = BLI_task_pool_create(&ctx, TASK_PRIORITY_LOW); + task_pool = BLI_task_pool_create(&ctx, TASK_PRIORITY_LOW, TASK_ISOLATION_ON); totchild = ctx.totchild; totparent = ctx.totparent; diff --git a/source/blender/blenkernel/intern/particle_distribute.c b/source/blender/blenkernel/intern/particle_distribute.c index ad617b4198b..6cae6cd6fa2 100644 --- a/source/blender/blenkernel/intern/particle_distribute.c +++ b/source/blender/blenkernel/intern/particle_distribute.c @@ -1330,7 +1330,7 @@ static void distribute_particles_on_dm(ParticleSimulationData *sim, int from) return; } - task_pool = BLI_task_pool_create(&ctx, TASK_PRIORITY_LOW); + task_pool = BLI_task_pool_create(&ctx, TASK_PRIORITY_LOW, TASK_ISOLATION_ON); totpart = (from == PART_FROM_CHILD ? sim->psys->totchild : sim->psys->totpart); psys_tasks_create(&ctx, 0, totpart, &tasks, &numtasks); diff --git a/source/blender/blenlib/BLI_task.h b/source/blender/blenlib/BLI_task.h index 9e61686b37a..339bb256819 100644 --- a/source/blender/blenlib/BLI_task.h +++ b/source/blender/blenlib/BLI_task.h @@ -67,17 +67,55 @@ typedef enum TaskPriority { TASK_PRIORITY_HIGH, } TaskPriority; +/** + * Task isolation helps avoid unexpected task scheduling decisions that can lead to bugs if wrong + * assumptions were made. Typically that happens when doing "nested threading", i.e. one thread + * schedules a bunch of main-tasks and those spawn new subtasks. + * + * What can happen is that when a main-task waits for its subtasks to complete on other threads, + * another main-task is scheduled within the already running main-task. Generally, this is good, + * because it leads to better performance. However, sometimes code (often unintentionally) makes + * the assumption that at most one main-task runs on a thread at a time. + * + * The bugs often show themselves in two ways: + * - Deadlock, when a main-task holds a mutex while waiting for its subtasks to complete. + * - Data corruption, when a main-task makes wrong assumptions about a threadlocal variable. + * + * Task isolation can avoid these bugs by making sure that a main-task does not start executing + * another main-task while waiting for its subtasks. More precisely, a function that runs in an + * isolated region is only allowed to run subtasks that were spawned in the same isolated region. + * + * Unfortunately, incorrect use of task isolation can lead to deadlocks itself. This can happen + * when threading primitives are used that separate spawning tasks from executing them. The problem + * occurs when a task is spawned in one isolated region while the tasks are waited for in another + * isolated region. In this setup, the thread that is waiting for the spawned tasks to complete + * cannot run the tasks itself. On a single thread, that causes a deadlock already. When there are + * multiple threads, another thread will typically run the task and avoid the deadlock. However, if + * this situation happens on all threads at the same time, all threads will deadlock. This happened + * in T88598. + */ +typedef enum TaskIsolation { + /* Do not use task isolation. Always use this when tasks are pushed recursively. */ + TASK_ISOLATION_OFF, + /* Run each task in its own isolated region. */ + TASK_ISOLATION_ON, +} TaskIsolation; + typedef struct TaskPool TaskPool; typedef void (*TaskRunFunction)(TaskPool *__restrict pool, void *taskdata); typedef void (*TaskFreeFunction)(TaskPool *__restrict pool, void *taskdata); /* Regular task pool that immediately starts executing tasks as soon as they * are pushed, either on the current or another thread. */ -TaskPool *BLI_task_pool_create(void *userdata, TaskPriority priority); +TaskPool *BLI_task_pool_create(void *userdata, + TaskPriority priority, + TaskIsolation task_isolation); /* Background: always run tasks in a background thread, never immediately * execute them. For running background jobs. */ -TaskPool *BLI_task_pool_create_background(void *userdata, TaskPriority priority); +TaskPool *BLI_task_pool_create_background(void *userdata, + TaskPriority priority, + TaskIsolation task_isolation); /* Background Serial: run tasks one after the other in the background, * without parallelization between the tasks. */ @@ -87,7 +125,9 @@ TaskPool *BLI_task_pool_create_background_serial(void *userdata, TaskPriority pr * as threads can't immediately start working. But it can be used if the data * structures the threads operate on are not fully initialized until all tasks * are created. */ -TaskPool *BLI_task_pool_create_suspended(void *userdata, TaskPriority priority); +TaskPool *BLI_task_pool_create_suspended(void *userdata, + TaskPriority priority, + TaskIsolation task_isolation); /* No threads: immediately executes tasks on the same thread. For debugging. */ TaskPool *BLI_task_pool_create_no_threads(void *userdata); diff --git a/source/blender/blenlib/intern/task_iterator.c b/source/blender/blenlib/intern/task_iterator.c index 38271e5823f..85cd9718ed4 100644 --- a/source/blender/blenlib/intern/task_iterator.c +++ b/source/blender/blenlib/intern/task_iterator.c @@ -223,7 +223,7 @@ static void task_parallel_iterator_do(const TaskParallelSettings *settings, void *userdata_chunk_array = NULL; const bool use_userdata_chunk = (userdata_chunk_size != 0) && (userdata_chunk != NULL); - TaskPool *task_pool = BLI_task_pool_create(state, TASK_PRIORITY_HIGH); + TaskPool *task_pool = BLI_task_pool_create(state, TASK_PRIORITY_HIGH, TASK_ISOLATION_ON); if (use_userdata_chunk) { userdata_chunk_array = MALLOCA(userdata_chunk_size * num_tasks); @@ -398,7 +398,7 @@ void BLI_task_parallel_mempool(BLI_mempool *mempool, return; } - task_pool = BLI_task_pool_create(&state, TASK_PRIORITY_HIGH); + task_pool = BLI_task_pool_create(&state, TASK_PRIORITY_HIGH, TASK_ISOLATION_ON); num_threads = BLI_task_scheduler_num_threads(); /* The idea here is to prevent creating task for each of the loop iterations diff --git a/source/blender/blenlib/intern/task_pool.cc b/source/blender/blenlib/intern/task_pool.cc index 6404f5264cc..d72674c1c00 100644 --- a/source/blender/blenlib/intern/task_pool.cc +++ b/source/blender/blenlib/intern/task_pool.cc @@ -22,6 +22,7 @@ #include <cstdlib> #include <memory> +#include <thread> #include <utility> #include "MEM_guardedalloc.h" @@ -111,15 +112,7 @@ class Task { Task &operator=(const Task &other) = delete; Task &operator=(Task &&other) = delete; - /* Execute task. */ - void operator()() const - { -#ifdef WITH_TBB - tbb::this_task_arena::isolate([this] { run(pool, taskdata); }); -#else - run(pool, taskdata); -#endif - } + void operator()() const; }; /* TBB Task Group. @@ -163,13 +156,16 @@ enum TaskPoolType { struct TaskPool { TaskPoolType type; bool use_threads; + TaskIsolation task_isolation; ThreadMutex user_mutex; void *userdata; - /* TBB task pool. */ #ifdef WITH_TBB + /* TBB task pool. */ TBBTaskGroup tbb_group; + /* This is used to detect a common way to accidentally create a deadlock with task isolation. */ + std::thread::id task_pool_create_thread_id; #endif volatile bool is_suspended; BLI_mempool *suspended_mempool; @@ -180,6 +176,36 @@ struct TaskPool { volatile bool background_is_canceling; }; +/* Execute task. */ +void Task::operator()() const +{ +#ifdef WITH_TBB + if (pool->task_isolation == TASK_ISOLATION_ON) { + tbb::this_task_arena::isolate([this] { run(pool, taskdata); }); + return; + } +#endif + run(pool, taskdata); +} + +static void assert_on_valid_thread(TaskPool *pool) +{ + /* TODO: Remove this `return` to enable the check. */ + return; +#ifdef DEBUG +# ifdef WITH_TBB + if (pool->task_isolation == TASK_ISOLATION_ON) { + const std::thread::id current_id = std::this_thread::get_id(); + /* This task pool is modified from different threads. To avoid deadlocks, `TASK_ISOLATION_OFF` + * has to be used. Task isolation can still be used in a more fine-grained way within the + * tasks, but should not be enabled for the entire task pool. */ + BLI_assert(pool->task_pool_create_thread_id == current_id); + } +# endif +#endif + UNUSED_VARS_NDEBUG(pool); +} + /* TBB Task Pool. * * Task pool using the TBB scheduler for tasks. When building without TBB @@ -365,7 +391,10 @@ static void background_task_pool_free(TaskPool *pool) /* Task Pool */ -static TaskPool *task_pool_create_ex(void *userdata, TaskPoolType type, TaskPriority priority) +static TaskPool *task_pool_create_ex(void *userdata, + TaskPoolType type, + TaskPriority priority, + TaskIsolation task_isolation) { const bool use_threads = BLI_task_scheduler_num_threads() > 1 && type != TASK_POOL_NO_THREADS; @@ -381,6 +410,11 @@ static TaskPool *task_pool_create_ex(void *userdata, TaskPoolType type, TaskPrio pool->type = type; pool->use_threads = use_threads; + pool->task_isolation = task_isolation; + +#ifdef WITH_TBB + pool->task_pool_create_thread_id = std::this_thread::get_id(); +#endif pool->userdata = userdata; BLI_mutex_init(&pool->user_mutex); @@ -403,9 +437,9 @@ static TaskPool *task_pool_create_ex(void *userdata, TaskPoolType type, TaskPrio /** * Create a normal task pool. Tasks will be executed as soon as they are added. */ -TaskPool *BLI_task_pool_create(void *userdata, TaskPriority priority) +TaskPool *BLI_task_pool_create(void *userdata, TaskPriority priority, TaskIsolation task_isolation) { - return task_pool_create_ex(userdata, TASK_POOL_TBB, priority); + return task_pool_create_ex(userdata, TASK_POOL_TBB, priority, task_isolation); } /** @@ -420,9 +454,11 @@ TaskPool *BLI_task_pool_create(void *userdata, TaskPriority priority) * they could end never being executed, since the 'fallback' background thread is already * busy with parent task in single-threaded context). */ -TaskPool *BLI_task_pool_create_background(void *userdata, TaskPriority priority) +TaskPool *BLI_task_pool_create_background(void *userdata, + TaskPriority priority, + TaskIsolation task_isolation) { - return task_pool_create_ex(userdata, TASK_POOL_BACKGROUND, priority); + return task_pool_create_ex(userdata, TASK_POOL_BACKGROUND, priority, task_isolation); } /** @@ -430,9 +466,11 @@ TaskPool *BLI_task_pool_create_background(void *userdata, TaskPriority priority) * for until BLI_task_pool_work_and_wait() is called. This helps reducing threading * overhead when pushing huge amount of small initial tasks from the main thread. */ -TaskPool *BLI_task_pool_create_suspended(void *userdata, TaskPriority priority) +TaskPool *BLI_task_pool_create_suspended(void *userdata, + TaskPriority priority, + TaskIsolation task_isolation) { - return task_pool_create_ex(userdata, TASK_POOL_TBB_SUSPENDED, priority); + return task_pool_create_ex(userdata, TASK_POOL_TBB_SUSPENDED, priority, task_isolation); } /** @@ -441,7 +479,8 @@ TaskPool *BLI_task_pool_create_suspended(void *userdata, TaskPriority priority) */ TaskPool *BLI_task_pool_create_no_threads(void *userdata) { - return task_pool_create_ex(userdata, TASK_POOL_NO_THREADS, TASK_PRIORITY_HIGH); + return task_pool_create_ex( + userdata, TASK_POOL_NO_THREADS, TASK_PRIORITY_HIGH, TASK_ISOLATION_ON); } /** @@ -450,7 +489,7 @@ TaskPool *BLI_task_pool_create_no_threads(void *userdata) */ TaskPool *BLI_task_pool_create_background_serial(void *userdata, TaskPriority priority) { - return task_pool_create_ex(userdata, TASK_POOL_BACKGROUND_SERIAL, priority); + return task_pool_create_ex(userdata, TASK_POOL_BACKGROUND_SERIAL, priority, TASK_ISOLATION_ON); } void BLI_task_pool_free(TaskPool *pool) @@ -478,6 +517,8 @@ void BLI_task_pool_push(TaskPool *pool, bool free_taskdata, TaskFreeFunction freedata) { + assert_on_valid_thread(pool); + Task task(pool, run, taskdata, free_taskdata, freedata); switch (pool->type) { @@ -495,6 +536,8 @@ void BLI_task_pool_push(TaskPool *pool, void BLI_task_pool_work_and_wait(TaskPool *pool) { + assert_on_valid_thread(pool); + switch (pool->type) { case TASK_POOL_TBB: case TASK_POOL_TBB_SUSPENDED: diff --git a/source/blender/blenlib/tests/BLI_linklist_lockfree_test.cc b/source/blender/blenlib/tests/BLI_linklist_lockfree_test.cc index e9810aed179..8be89d66062 100644 --- a/source/blender/blenlib/tests/BLI_linklist_lockfree_test.cc +++ b/source/blender/blenlib/tests/BLI_linklist_lockfree_test.cc @@ -81,7 +81,7 @@ TEST(LockfreeLinkList, InsertMultipleConcurrent) LockfreeLinkList list; BLI_linklist_lockfree_init(&list); /* Initialize task scheduler and pool. */ - TaskPool *pool = BLI_task_pool_create_suspended(&list, TASK_PRIORITY_HIGH); + TaskPool *pool = BLI_task_pool_create_suspended(&list, TASK_PRIORITY_HIGH, TASK_ISOLATION_ON); /* Push tasks to the pool. */ for (int i = 0; i < num_nodes; ++i) { BLI_task_pool_push(pool, concurrent_insert, POINTER_FROM_INT(i), false, nullptr); diff --git a/source/blender/blenloader/intern/versioning_300.c b/source/blender/blenloader/intern/versioning_300.c index 284a30a280d..93e2af1b68e 100644 --- a/source/blender/blenloader/intern/versioning_300.c +++ b/source/blender/blenloader/intern/versioning_300.c @@ -159,6 +159,33 @@ static void version_switch_node_input_prefix(Main *bmain) FOREACH_NODETREE_END; } +static void version_node_socket_name(bNodeTree *ntree, + const int node_type, + const char *old_name, + const char *new_name) +{ + LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { + if (node->type == node_type) { + LISTBASE_FOREACH (bNodeSocket *, socket, &node->inputs) { + if (STREQ(socket->name, old_name)) { + strcpy(socket->name, new_name); + } + if (STREQ(socket->identifier, old_name)) { + strcpy(socket->identifier, new_name); + } + } + LISTBASE_FOREACH (bNodeSocket *, socket, &node->outputs) { + if (STREQ(socket->name, old_name)) { + strcpy(socket->name, new_name); + } + if (STREQ(socket->identifier, old_name)) { + strcpy(socket->identifier, new_name); + } + } + } + } +} + /* NOLINTNEXTLINE: readability-function-size */ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain) { @@ -211,5 +238,11 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain) */ { /* Keep this block, even when empty. */ + FOREACH_NODETREE_BEGIN (bmain, ntree, id) { + if (ntree->type == NTREE_GEOMETRY) { + version_node_socket_name(ntree, GEO_NODE_BOUNDING_BOX, "Mesh", "Bounding Box"); + } + } + FOREACH_NODETREE_END; } } diff --git a/source/blender/bmesh/intern/bmesh_mesh_normals.c b/source/blender/bmesh/intern/bmesh_mesh_normals.c index e5501100ce3..f0a791bae19 100644 --- a/source/blender/bmesh/intern/bmesh_mesh_normals.c +++ b/source/blender/bmesh/intern/bmesh_mesh_normals.c @@ -35,8 +35,6 @@ #include "BKE_global.h" #include "BKE_mesh.h" -#include "atomic_ops.h" - #include "intern/bmesh_private.h" /* -------------------------------------------------------------------- */ @@ -59,19 +57,28 @@ typedef struct BMEdgesCalcVectorsData { float (*edgevec)[3]; } BMEdgesCalcVectorsData; -static void mesh_edges_calc_vectors_cb(void *userdata, MempoolIterData *mp_e) +static void bm_edge_calc_vectors_cb(void *userdata, MempoolIterData *mp_e) { - BMEdgesCalcVectorsData *data = userdata; BMEdge *e = (BMEdge *)mp_e; - - if (e->l) { - const float *v1_co = data->vcos ? data->vcos[BM_elem_index_get(e->v1)] : e->v1->co; - const float *v2_co = data->vcos ? data->vcos[BM_elem_index_get(e->v2)] : e->v2->co; - sub_v3_v3v3(data->edgevec[BM_elem_index_get(e)], v2_co, v1_co); - normalize_v3(data->edgevec[BM_elem_index_get(e)]); + /* The edge vector will not be needed when the edge has no radial. */ + if (e->l != NULL) { + float(*edgevec)[3] = userdata; + float *e_diff = edgevec[BM_elem_index_get(e)]; + sub_v3_v3v3(e_diff, e->v2->co, e->v1->co); + normalize_v3(e_diff); } - else { - /* the edge vector will not be needed when the edge has no radial */ +} + +static void bm_edge_calc_vectors_with_coords_cb(void *userdata, MempoolIterData *mp_e) +{ + BMEdge *e = (BMEdge *)mp_e; + /* The edge vector will not be needed when the edge has no radial. */ + if (e->l != NULL) { + BMEdgesCalcVectorsData *data = userdata; + float *e_diff = data->edgevec[BM_elem_index_get(e)]; + sub_v3_v3v3( + e_diff, data->vcos[BM_elem_index_get(e->v2)], data->vcos[BM_elem_index_get(e->v1)]); + normalize_v3(e_diff); } } @@ -79,118 +86,123 @@ static void bm_mesh_edges_calc_vectors(BMesh *bm, float (*edgevec)[3], const flo { BM_mesh_elem_index_ensure(bm, BM_EDGE | (vcos ? BM_VERT : 0)); - BMEdgesCalcVectorsData data = { - .vcos = vcos, - .edgevec = edgevec, - }; - - BM_iter_parallel( - bm, BM_EDGES_OF_MESH, mesh_edges_calc_vectors_cb, &data, bm->totedge >= BM_OMP_LIMIT); + if (vcos == NULL) { + BM_iter_parallel( + bm, BM_EDGES_OF_MESH, bm_edge_calc_vectors_cb, edgevec, bm->totedge >= BM_OMP_LIMIT); + } + else { + BMEdgesCalcVectorsData data = { + .edgevec = edgevec, + .vcos = vcos, + }; + BM_iter_parallel(bm, + BM_EDGES_OF_MESH, + bm_edge_calc_vectors_with_coords_cb, + &data, + bm->totedge >= BM_OMP_LIMIT); + } } -typedef struct BMVertsCalcNormalsData { +typedef struct BMVertsCalcNormalsWithCoordsData { /* Read-only data. */ const float (*fnos)[3]; const float (*edgevec)[3]; const float (*vcos)[3]; - /* Read-write data, protected by an atomic-based fake spin-lock like system. */ + /* Write data. */ float (*vnos)[3]; -} BMVertsCalcNormalsData; +} BMVertsCalcNormalsWithCoordsData; -static void mesh_verts_calc_normals_accum( - BMFace *f, - const float *f_no, - const float (*edgevec)[3], - - /* Read-write data, protected by an atomic-based fake spin-lock like system. */ - float (*vnos)[3]) +BLI_INLINE void bm_vert_calc_normals_accum_loop(const BMLoop *l_iter, + const float (*edgevec)[3], + const float f_no[3], + float v_no[3]) { -#define FLT_EQ_NONAN(_fa, _fb) (*((const uint32_t *)&_fa) == *((const uint32_t *)&_fb)) - - BMLoop *l_first, *l_iter; - l_iter = l_first = BM_FACE_FIRST_LOOP(f); - do { - const float *e1diff, *e2diff; - float dotprod; - float fac; - - /* calculate the dot product of the two edges that - * meet at the loop's vertex */ - e1diff = edgevec[BM_elem_index_get(l_iter->prev->e)]; - e2diff = edgevec[BM_elem_index_get(l_iter->e)]; - dotprod = dot_v3v3(e1diff, e2diff); - - /* edge vectors are calculated from e->v1 to e->v2, so - * adjust the dot product if one but not both loops - * actually runs from from e->v2 to e->v1 */ - if ((l_iter->prev->e->v1 == l_iter->prev->v) ^ (l_iter->e->v1 == l_iter->v)) { - dotprod = -dotprod; - } - - fac = saacos(-dotprod); - - if (fac != fac) { /* NAN detection. */ - /* Degenerated case, nothing to do here, just ignore that vertex. */ - continue; - } + /* Calculate the dot product of the two edges that meet at the loop's vertex. */ + const float *e1diff = edgevec[BM_elem_index_get(l_iter->prev->e)]; + const float *e2diff = edgevec[BM_elem_index_get(l_iter->e)]; + /* Edge vectors are calculated from e->v1 to e->v2, so adjust the dot product if one but not + * both loops actually runs from from e->v2 to e->v1. */ + float dotprod = dot_v3v3(e1diff, e2diff); + if ((l_iter->prev->e->v1 == l_iter->prev->v) ^ (l_iter->e->v1 == l_iter->v)) { + dotprod = -dotprod; + } + const float fac = saacos(-dotprod); + /* NAN detection, otherwise this is a degenerated case, ignore that vertex in this case. */ + if (fac == fac) { /* NAN detection. */ + madd_v3_v3fl(v_no, f_no, fac); + } +} - /* accumulate weighted face normal into the vertex's normal */ - float *v_no = vnos ? vnos[BM_elem_index_get(l_iter->v)] : l_iter->v->no; - - /* This block is a lockless threadsafe madd_v3_v3fl. - * It uses the first float of the vector as a sort of cheap spin-lock, - * assuming FLT_MAX is a safe 'illegal' value that cannot be set here otherwise. - * It also assumes that collisions between threads are highly unlikely, - * else performances would be quite bad here. */ - float virtual_lock = v_no[0]; - while (true) { - /* This loops until following conditions are met: - * - v_no[0] has same value as virtual_lock (i.e. it did not change since last try). - * - v_no[0] was not FLT_MAX, i.e. it was not locked by another thread. - */ - const float vl = atomic_cas_float(&v_no[0], virtual_lock, FLT_MAX); - if (FLT_EQ_NONAN(vl, virtual_lock) && vl != FLT_MAX) { - break; +static void bm_vert_calc_normals_impl(const float (*edgevec)[3], BMVert *v) +{ + float *v_no = v->no; + zero_v3(v_no); + BMEdge *e_first = v->e; + if (e_first != NULL) { + BMEdge *e_iter = e_first; + do { + BMLoop *l_first = e_iter->l; + if (l_first != NULL) { + BMLoop *l_iter = l_first; + do { + if (l_iter->v == v) { + bm_vert_calc_normals_accum_loop(l_iter, edgevec, l_iter->f->no, v_no); + } + } while ((l_iter = l_iter->radial_next) != l_first); } - virtual_lock = vl; + } while ((e_iter = BM_DISK_EDGE_NEXT(e_iter, v)) != e_first); + + if (LIKELY(normalize_v3(v_no) != 0.0f)) { + return; } - BLI_assert(v_no[0] == FLT_MAX); - /* Now we own that normal value, and can change it. - * But first scalar of the vector must not be changed yet, it's our lock! */ - virtual_lock += f_no[0] * fac; - v_no[1] += f_no[1] * fac; - v_no[2] += f_no[2] * fac; - /* Second atomic operation to 'release' - * our lock on that vector and set its first scalar value. */ - /* Note that we do not need to loop here, since we 'locked' v_no[0], - * nobody should have changed it in the mean time. */ - virtual_lock = atomic_cas_float(&v_no[0], FLT_MAX, virtual_lock); - BLI_assert(virtual_lock == FLT_MAX); - - } while ((l_iter = l_iter->next) != l_first); - -#undef FLT_EQ_NONAN + } + /* Fallback normal. */ + normalize_v3_v3(v_no, v->co); } -static void mesh_verts_calc_normals_accum_cb(void *userdata, MempoolIterData *mp_f) +static void bm_vert_calc_normals_cb(void *userdata, MempoolIterData *mp_v) { - BMVertsCalcNormalsData *data = userdata; - BMFace *f = (BMFace *)mp_f; - const float *f_no = data->fnos ? data->fnos[BM_elem_index_get(f)] : f->no; - mesh_verts_calc_normals_accum(f, f_no, data->edgevec, data->vnos); + const float(*edgevec)[3] = userdata; + BMVert *v = (BMVert *)mp_v; + bm_vert_calc_normals_impl(edgevec, v); } -static void mesh_verts_calc_normals_normalize_cb(void *userdata, MempoolIterData *mp_v) +static void bm_vert_calc_normals_with_coords(BMVert *v, BMVertsCalcNormalsWithCoordsData *data) { - BMVertsCalcNormalsData *data = userdata; - BMVert *v = (BMVert *)mp_v; + float *v_no = data->vnos[BM_elem_index_get(v)]; + zero_v3(v_no); + + /* Loop over edges. */ + BMEdge *e_first = v->e; + if (e_first != NULL) { + BMEdge *e_iter = e_first; + do { + BMLoop *l_first = e_iter->l; + if (l_first != NULL) { + BMLoop *l_iter = l_first; + do { + if (l_iter->v == v) { + bm_vert_calc_normals_accum_loop( + l_iter, data->edgevec, data->fnos[BM_elem_index_get(l_iter->f)], v_no); + } + } while ((l_iter = l_iter->radial_next) != l_first); + } + } while ((e_iter = BM_DISK_EDGE_NEXT(e_iter, v)) != e_first); - float *v_no = data->vnos ? data->vnos[BM_elem_index_get(v)] : v->no; - if (UNLIKELY(normalize_v3(v_no) == 0.0f)) { - const float *v_co = data->vcos ? data->vcos[BM_elem_index_get(v)] : v->co; - normalize_v3_v3(v_no, v_co); + if (LIKELY(normalize_v3(v_no) != 0.0f)) { + return; + } } + /* Fallback normal. */ + normalize_v3_v3(v_no, data->vcos[BM_elem_index_get(v)]); +} + +static void bm_vert_calc_normals_with_coords_cb(void *userdata, MempoolIterData *mp_v) +{ + BMVertsCalcNormalsWithCoordsData *data = userdata; + BMVert *v = (BMVert *)mp_v; + bm_vert_calc_normals_with_coords(v, data); } static void bm_mesh_verts_calc_normals(BMesh *bm, @@ -201,29 +213,34 @@ static void bm_mesh_verts_calc_normals(BMesh *bm, { BM_mesh_elem_index_ensure(bm, (BM_EDGE | BM_FACE) | ((vnos || vcos) ? BM_VERT : 0)); - BMVertsCalcNormalsData data = { - .fnos = fnos, - .edgevec = edgevec, - .vcos = vcos, - .vnos = vnos, - }; - - BM_iter_parallel( - bm, BM_FACES_OF_MESH, mesh_verts_calc_normals_accum_cb, &data, bm->totface >= BM_OMP_LIMIT); - - /* normalize the accumulated vertex normals */ - BM_iter_parallel(bm, - BM_VERTS_OF_MESH, - mesh_verts_calc_normals_normalize_cb, - &data, - bm->totvert >= BM_OMP_LIMIT); + if (vcos == NULL) { + BM_iter_parallel(bm, + BM_VERTS_OF_MESH, + bm_vert_calc_normals_cb, + (void *)edgevec, + bm->totvert >= BM_OMP_LIMIT); + } + else { + BLI_assert(!ELEM(NULL, fnos, vnos)); + BMVertsCalcNormalsWithCoordsData data = { + .edgevec = edgevec, + .fnos = fnos, + .vcos = vcos, + .vnos = vnos, + }; + BM_iter_parallel(bm, + BM_VERTS_OF_MESH, + bm_vert_calc_normals_with_coords_cb, + &data, + bm->totvert >= BM_OMP_LIMIT); + } } -static void mesh_faces_calc_normals_cb(void *UNUSED(userdata), MempoolIterData *mp_f) +static void bm_face_calc_normals_cb(void *UNUSED(userdata), MempoolIterData *mp_f) { BMFace *f = (BMFace *)mp_f; - BM_face_normal_update(f); + BM_face_calc_normal(f, f->no); } /** @@ -235,27 +252,13 @@ void BM_mesh_normals_update(BMesh *bm) { float(*edgevec)[3] = MEM_mallocN(sizeof(*edgevec) * bm->totedge, __func__); - /* Parallel mempool iteration does not allow generating indices inline anymore... */ + /* Parallel mempool iteration does not allow generating indices inline anymore. */ BM_mesh_elem_index_ensure(bm, (BM_EDGE | BM_FACE)); - /* calculate all face normals */ + /* Calculate all face normals. */ BM_iter_parallel( - bm, BM_FACES_OF_MESH, mesh_faces_calc_normals_cb, NULL, bm->totface >= BM_OMP_LIMIT); - - /* Zero out vertex normals */ - BMIter viter; - BMVert *v; - int i; - - BM_ITER_MESH_INDEX (v, &viter, bm, BM_VERTS_OF_MESH, i) { - BM_elem_index_set(v, i); /* set_inline */ - zero_v3(v->no); - } - bm->elem_index_dirty &= ~BM_VERT; + bm, BM_FACES_OF_MESH, bm_face_calc_normals_cb, NULL, bm->totface >= BM_OMP_LIMIT); - /* Compute normalized direction vectors for each edge. - * Directions will be used for calculating the weights of the face normals on the vertex normals. - */ bm_mesh_edges_calc_vectors(bm, edgevec, NULL); /* Add weighted face normals to vertices, and normalize vert normals. */ @@ -269,14 +272,14 @@ void BM_mesh_normals_update(BMesh *bm) /** \name Update Vertex & Face Normals (Partial Updates) * \{ */ -static void mesh_faces_parallel_range_calc_normals_cb( +static void bm_partial_faces_parallel_range_calc_normals_cb( void *userdata, const int iter, const TaskParallelTLS *__restrict UNUSED(tls)) { BMFace *f = ((BMFace **)userdata)[iter]; - BM_face_normal_update(f); + BM_face_calc_normal(f, f->no); } -static void mesh_edges_parallel_range_calc_vectors_cb( +static void bm_partial_edges_parallel_range_calc_vectors_cb( void *userdata, const int iter, const TaskParallelTLS *__restrict UNUSED(tls)) { BMEdge *e = ((BMEdge **)((void **)userdata)[0])[iter]; @@ -285,21 +288,12 @@ static void mesh_edges_parallel_range_calc_vectors_cb( normalize_v3(r_edgevec); } -static void mesh_verts_parallel_range_calc_normals_accum_cb( - void *userdata, const int iter, const TaskParallelTLS *__restrict UNUSED(tls)) -{ - BMFace *f = ((BMFace **)((void **)userdata)[0])[iter]; - const float(*edgevec)[3] = (float(*)[3])((void **)userdata)[1]; - mesh_verts_calc_normals_accum(f, f->no, edgevec, NULL); -} - -static void mesh_verts_parallel_range_calc_normals_normalize_cb( +static void bm_partial_verts_parallel_range_calc_normal_cb( void *userdata, const int iter, const TaskParallelTLS *__restrict UNUSED(tls)) { - BMVert *v = ((BMVert **)userdata)[iter]; - if (UNLIKELY(normalize_v3(v->no) == 0.0f)) { - normalize_v3_v3(v->no, v->co); - } + BMVert *v = ((BMVert **)((void **)userdata)[0])[iter]; + const float(*edgevec)[3] = (const float(*)[3])((void **)userdata)[1]; + bm_vert_calc_normals_impl(edgevec, v); } /** @@ -316,21 +310,15 @@ void BM_mesh_normals_update_with_partial(BMesh *bm, const BMPartialUpdate *bmpin const int verts_len = bmpinfo->verts_len; const int edges_len = bmpinfo->edges_len; const int faces_len = bmpinfo->faces_len; - const int faces_len_normal_calc_accumulate = bmpinfo->faces_len_normal_calc_accumulate; float(*edgevec)[3] = MEM_mallocN(sizeof(*edgevec) * edges_len, __func__); - for (int i = 0; i < verts_len; i++) { - zero_v3(verts[i]->no); - } - TaskParallelSettings settings; BLI_parallel_range_settings_defaults(&settings); - { - /* Faces. */ - BLI_task_parallel_range( - 0, faces_len, faces, mesh_faces_parallel_range_calc_normals_cb, &settings); - } + + /* Faces. */ + BLI_task_parallel_range( + 0, faces_len, faces, bm_partial_faces_parallel_range_calc_normals_cb, &settings); /* Temporarily override the edge indices, * storing the correct indices in the case they're not dirty. @@ -366,19 +354,12 @@ void BM_mesh_normals_update_with_partial(BMesh *bm, const BMPartialUpdate *bmpin * normals. */ void *data[2] = {edges, edgevec}; BLI_task_parallel_range( - 0, edges_len, data, mesh_edges_parallel_range_calc_vectors_cb, &settings); - - /* Add weighted face normals to vertices. */ - data[0] = faces; - BLI_task_parallel_range(0, - faces_len_normal_calc_accumulate, - data, - mesh_verts_parallel_range_calc_normals_accum_cb, - &settings); + 0, edges_len, data, bm_partial_edges_parallel_range_calc_vectors_cb, &settings); - /* Normalize the accumulated vertex normals. */ + /* Calculate vertex normals. */ + data[0] = verts; BLI_task_parallel_range( - 0, verts_len, verts, mesh_verts_parallel_range_calc_normals_normalize_cb, &settings); + 0, verts_len, data, bm_partial_verts_parallel_range_calc_normal_cb, &settings); } if (edge_index_value != NULL) { diff --git a/source/blender/bmesh/intern/bmesh_mesh_partial_update.c b/source/blender/bmesh/intern/bmesh_mesh_partial_update.c index 2290e58fe6c..7b01b61d4fa 100644 --- a/source/blender/bmesh/intern/bmesh_mesh_partial_update.c +++ b/source/blender/bmesh/intern/bmesh_mesh_partial_update.c @@ -182,8 +182,6 @@ BMPartialUpdate *BM_mesh_partial_create_from_verts(BMesh *bm, } } - const int faces_len_direct = bmpinfo->faces_len; - if (params->do_normals) { /* - Extend to all faces vertices: * Any changes to the faces normal needs to update all surrounding vertices. @@ -219,31 +217,13 @@ BMPartialUpdate *BM_mesh_partial_create_from_verts(BMesh *bm, BMEdge *e_iter = e_first; do { if (e_iter->l) { - if (!partial_elem_edge_ensure(bmpinfo, edges_tag, e_iter)) { - continue; - } - - /* These faces need to be taken into account when weighting vertex normals - * but aren't needed for tessellation nor do their normals need to be recalculated. - * These faces end up between `faces_len` and `faces_len_normal_calc_accumulate` - * in the faces array. */ - BMLoop *l_first_radial = e_iter->l; - BMLoop *l_iter_radial = l_first_radial; - /* Loop over radial loops. */ - do { - if (l_iter_radial->v == v) { - partial_elem_face_ensure(bmpinfo, faces_tag, l_iter_radial->f); - } - } while ((l_iter_radial = l_iter_radial->radial_next) != l_first_radial); + partial_elem_edge_ensure(bmpinfo, edges_tag, e_iter); } } while ((e_iter = BM_DISK_EDGE_NEXT(e_iter, v)) != e_first); } while ((l_iter = l_iter->next) != l_first); } } - bmpinfo->faces_len_normal_calc_accumulate = bmpinfo->faces_len; - bmpinfo->faces_len = faces_len_direct; - if (verts_tag) { MEM_freeN(verts_tag); } diff --git a/source/blender/bmesh/intern/bmesh_mesh_partial_update.h b/source/blender/bmesh/intern/bmesh_mesh_partial_update.h index c0c9b275fa4..b31ec127744 100644 --- a/source/blender/bmesh/intern/bmesh_mesh_partial_update.h +++ b/source/blender/bmesh/intern/bmesh_mesh_partial_update.h @@ -49,11 +49,6 @@ typedef struct BMPartialUpdate { int verts_len, verts_len_alloc; int edges_len, edges_len_alloc; int faces_len, faces_len_alloc; - /** - * Faces at the end of `faces` that don't need to have the normals recalculated - * but must be included when waiting the vertex normals. - */ - int faces_len_normal_calc_accumulate; /** Store the parameters used in creation so invalid use can be asserted. */ BMPartialUpdate_Params params; diff --git a/source/blender/compositor/intern/COM_MemoryProxy.h b/source/blender/compositor/intern/COM_MemoryProxy.h index 931fd8d2622..6814afada74 100644 --- a/source/blender/compositor/intern/COM_MemoryProxy.h +++ b/source/blender/compositor/intern/COM_MemoryProxy.h @@ -18,6 +18,10 @@ #pragma once +#ifdef WITH_CXX_GUARDEDALLOC +# include "MEM_guardedalloc.h" +#endif + #include "COM_defines.h" namespace blender::compositor { diff --git a/source/blender/compositor/intern/COM_WorkPackage.h b/source/blender/compositor/intern/COM_WorkPackage.h index 4d503022120..f0f53f300a5 100644 --- a/source/blender/compositor/intern/COM_WorkPackage.h +++ b/source/blender/compositor/intern/COM_WorkPackage.h @@ -18,6 +18,10 @@ #pragma once +#ifdef WITH_CXX_GUARDEDALLOC +# include "MEM_guardedalloc.h" +#endif + #include "COM_Enums.h" #include "BLI_rect.h" diff --git a/source/blender/compositor/intern/COM_WorkScheduler.cc b/source/blender/compositor/intern/COM_WorkScheduler.cc index 157ded943d6..cd0139fd18e 100644 --- a/source/blender/compositor/intern/COM_WorkScheduler.cc +++ b/source/blender/compositor/intern/COM_WorkScheduler.cc @@ -411,7 +411,8 @@ static void threading_model_task_schedule(WorkPackage *package) static void threading_model_task_start() { BLI_thread_local_create(g_thread_device); - g_work_scheduler.task.pool = BLI_task_pool_create(nullptr, TASK_PRIORITY_HIGH); + g_work_scheduler.task.pool = BLI_task_pool_create( + nullptr, TASK_PRIORITY_HIGH, TASK_ISOLATION_ON); } static void threading_model_task_finish() diff --git a/source/blender/depsgraph/intern/eval/deg_eval.cc b/source/blender/depsgraph/intern/eval/deg_eval.cc index 620e86550cc..2107e075139 100644 --- a/source/blender/depsgraph/intern/eval/deg_eval.cc +++ b/source/blender/depsgraph/intern/eval/deg_eval.cc @@ -353,7 +353,8 @@ static TaskPool *deg_evaluate_task_pool_create(DepsgraphEvalState *state) return BLI_task_pool_create_no_threads(state); } - return BLI_task_pool_create_suspended(state, TASK_PRIORITY_HIGH); + /* TODO: Disable task isolation. */ + return BLI_task_pool_create_suspended(state, TASK_PRIORITY_HIGH, TASK_ISOLATION_ON); } /** diff --git a/source/blender/draw/CMakeLists.txt b/source/blender/draw/CMakeLists.txt index c07832959ac..3938242eb6e 100644 --- a/source/blender/draw/CMakeLists.txt +++ b/source/blender/draw/CMakeLists.txt @@ -54,6 +54,10 @@ set(SRC intern/draw_cache_extract_mesh_extractors.c intern/draw_cache_extract_mesh_render_data.c intern/draw_cache_extract_mesh.cc + intern/mesh_extractors/extract_mesh_ibo_edituv.cc + intern/mesh_extractors/extract_mesh_ibo_fdots.cc + intern/mesh_extractors/extract_mesh_ibo_lines_adjacency.cc + intern/mesh_extractors/extract_mesh_ibo_lines_paint_mask.cc intern/mesh_extractors/extract_mesh_ibo_lines.cc intern/mesh_extractors/extract_mesh_ibo_points.cc intern/mesh_extractors/extract_mesh_ibo_tris.cc diff --git a/source/blender/draw/intern/draw_cache_extract_mesh.cc b/source/blender/draw/intern/draw_cache_extract_mesh.cc index 8d6064179ad..7b2c0da4dd9 100644 --- a/source/blender/draw/intern/draw_cache_extract_mesh.cc +++ b/source/blender/draw/intern/draw_cache_extract_mesh.cc @@ -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, ¶ms, extractors); + extract_iter_looptri_mesh(mr, ¶ms, 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, ¶ms, extractors); + extract_iter_looptri_bm(mr, ¶ms, 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, ¶ms, extractors); + extract_iter_poly_mesh(mr, ¶ms, extractors, task_id); } else { ExtractPolyBMesh_Params params; params.poly_range[0] = 0; params.poly_range[1] = mr->poly_len; - extract_iter_poly_bm(mr, ¶ms, extractors); + extract_iter_poly_bm(mr, ¶ms, 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, ¶ms, extractors); + extract_iter_ledge_mesh(mr, ¶ms, 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, ¶ms, extractors); + extract_iter_ledge_bm(mr, ¶ms, 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, ¶ms, extractors); + extract_iter_lvert_mesh(mr, ¶ms, 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, ¶ms, extractors); + extract_iter_lvert_bm(mr, ¶ms, 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, ¶ms, extractors); + extract_iter_looptri_bm(mr, ¶ms, 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, ¶ms, extractors); + extract_iter_poly_bm(mr, ¶ms, 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, ¶ms, extractors); + extract_iter_ledge_bm(mr, ¶ms, 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, ¶ms, extractors); + extract_iter_lvert_bm(mr, ¶ms, 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, ¶ms, extractors); + extract_iter_looptri_mesh(mr, ¶ms, 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, ¶ms, extractors); + extract_iter_poly_mesh(mr, ¶ms, 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, ¶ms, extractors); + extract_iter_ledge_mesh(mr, ¶ms, 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, ¶ms, extractors); + extract_iter_lvert_mesh(mr, ¶ms, 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); diff --git a/source/blender/draw/intern/draw_cache_extract_mesh_extractors.c b/source/blender/draw/intern/draw_cache_extract_mesh_extractors.c index 5bea8b085d8..b79f80866ec 100644 --- a/source/blender/draw/intern/draw_cache_extract_mesh_extractors.c +++ b/source/blender/draw/intern/draw_cache_extract_mesh_extractors.c @@ -106,648 +106,6 @@ const MeshExtract *mesh_extract_override_get(const MeshExtract *extractor, /** \} */ /* ---------------------------------------------------------------------- */ -/** \name Extract Face-dots Indices - * \{ */ - -static void *extract_fdots_init(const MeshRenderData *mr, - struct MeshBatchCache *UNUSED(cache), - void *UNUSED(buf)) -{ - GPUIndexBufBuilder *elb = MEM_mallocN(sizeof(*elb), __func__); - GPU_indexbuf_init(elb, GPU_PRIM_POINTS, mr->poly_len, mr->poly_len); - return elb; -} - -static void extract_fdots_iter_poly_bm(const MeshRenderData *UNUSED(mr), - BMFace *f, - const int f_index, - void *elb) -{ - if (!BM_elem_flag_test(f, BM_ELEM_HIDDEN)) { - GPU_indexbuf_set_point_vert(elb, f_index, f_index); - } - else { - GPU_indexbuf_set_point_restart(elb, f_index); - } -} - -static void extract_fdots_iter_poly_mesh(const MeshRenderData *mr, - const MPoly *mp, - const int mp_index, - void *elb) -{ - if (mr->use_subsurf_fdots) { - /* Check #ME_VERT_FACEDOT. */ - const MLoop *mloop = mr->mloop; - const int ml_index_end = mp->loopstart + mp->totloop; - for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) { - const MLoop *ml = &mloop[ml_index]; - const MVert *mv = &mr->mvert[ml->v]; - if ((mv->flag & ME_VERT_FACEDOT) && !(mr->use_hide && (mp->flag & ME_HIDE))) { - GPU_indexbuf_set_point_vert(elb, mp_index, mp_index); - return; - } - } - GPU_indexbuf_set_point_restart(elb, mp_index); - } - else { - if (!(mr->use_hide && (mp->flag & ME_HIDE))) { - GPU_indexbuf_set_point_vert(elb, mp_index, mp_index); - } - else { - GPU_indexbuf_set_point_restart(elb, mp_index); - } - } -} - -static void extract_fdots_finish(const MeshRenderData *UNUSED(mr), - struct MeshBatchCache *UNUSED(cache), - void *buf, - void *elb) -{ - GPUIndexBuf *ibo = buf; - GPU_indexbuf_build_in_place(elb, ibo); - MEM_freeN(elb); -} - -const MeshExtract extract_fdots = { - .init = extract_fdots_init, - .iter_poly_bm = extract_fdots_iter_poly_bm, - .iter_poly_mesh = extract_fdots_iter_poly_mesh, - .finish = extract_fdots_finish, - .data_type = 0, - .use_threading = false, - .mesh_buffer_offset = offsetof(MeshBufferCache, ibo.fdots), -}; - -/** \} */ - -/* ---------------------------------------------------------------------- */ -/** \name Extract Paint Mask Line Indices - * \{ */ - -typedef struct MeshExtract_LinePaintMask_Data { - GPUIndexBufBuilder elb; - /** One bit per edge set if face is selected. */ - BLI_bitmap select_map[0]; -} MeshExtract_LinePaintMask_Data; - -static void *extract_lines_paint_mask_init(const MeshRenderData *mr, - struct MeshBatchCache *UNUSED(cache), - void *UNUSED(ibo)) -{ - size_t bitmap_size = BLI_BITMAP_SIZE(mr->edge_len); - MeshExtract_LinePaintMask_Data *data = MEM_callocN(sizeof(*data) + bitmap_size, __func__); - GPU_indexbuf_init(&data->elb, GPU_PRIM_LINES, mr->edge_len, mr->loop_len); - return data; -} - -static void extract_lines_paint_mask_iter_poly_mesh(const MeshRenderData *mr, - const MPoly *mp, - const int UNUSED(mp_index), - void *_data) -{ - MeshExtract_LinePaintMask_Data *data = _data; - const MLoop *mloop = mr->mloop; - const int ml_index_end = mp->loopstart + mp->totloop; - for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) { - const MLoop *ml = &mloop[ml_index]; - - const int e_index = ml->e; - const MEdge *me = &mr->medge[e_index]; - if (!((mr->use_hide && (me->flag & ME_HIDE)) || - ((mr->extract_type == MR_EXTRACT_MAPPED) && (mr->e_origindex) && - (mr->e_origindex[e_index] == ORIGINDEX_NONE)))) { - - const int ml_index_last = mp->totloop + mp->loopstart - 1; - const int ml_index_other = (ml_index == ml_index_last) ? mp->loopstart : (ml_index + 1); - if (mp->flag & ME_FACE_SEL) { - if (BLI_BITMAP_TEST_AND_SET_ATOMIC(data->select_map, e_index)) { - /* Hide edge as it has more than 2 selected loop. */ - GPU_indexbuf_set_line_restart(&data->elb, e_index); - } - else { - /* First selected loop. Set edge visible, overwriting any unselected loop. */ - GPU_indexbuf_set_line_verts(&data->elb, e_index, ml_index, ml_index_other); - } - } - else { - /* Set these unselected loop only if this edge has no other selected loop. */ - if (!BLI_BITMAP_TEST(data->select_map, e_index)) { - GPU_indexbuf_set_line_verts(&data->elb, e_index, ml_index, ml_index_other); - } - } - } - else { - GPU_indexbuf_set_line_restart(&data->elb, e_index); - } - } -} - -static void extract_lines_paint_mask_finish(const MeshRenderData *UNUSED(mr), - struct MeshBatchCache *UNUSED(cache), - void *buf, - void *_data) -{ - GPUIndexBuf *ibo = buf; - MeshExtract_LinePaintMask_Data *data = _data; - GPU_indexbuf_build_in_place(&data->elb, ibo); - MEM_freeN(data); -} - -const MeshExtract extract_lines_paint_mask = { - .init = extract_lines_paint_mask_init, - .iter_poly_mesh = extract_lines_paint_mask_iter_poly_mesh, - .finish = extract_lines_paint_mask_finish, - .data_type = 0, - .use_threading = false, - .mesh_buffer_offset = offsetof(MeshBufferCache, ibo.lines_paint_mask)}; - -/** \} */ - -/* ---------------------------------------------------------------------- */ -/** \name Extract Line Adjacency Indices - * \{ */ - -#define NO_EDGE INT_MAX - -typedef struct MeshExtract_LineAdjacency_Data { - GPUIndexBufBuilder elb; - EdgeHash *eh; - bool is_manifold; - /* Array to convert vert index to any loop index of this vert. */ - uint vert_to_loop[0]; -} MeshExtract_LineAdjacency_Data; - -static void *extract_lines_adjacency_init(const MeshRenderData *mr, - struct MeshBatchCache *UNUSED(cache), - void *UNUSED(buf)) -{ - /* Similar to poly_to_tri_count(). - * There is always (loop + triangle - 1) edges inside a polygon. - * Accumulate for all polys and you get : */ - uint tess_edge_len = mr->loop_len + mr->tri_len - mr->poly_len; - - size_t vert_to_loop_size = sizeof(uint) * mr->vert_len; - - MeshExtract_LineAdjacency_Data *data = MEM_callocN(sizeof(*data) + vert_to_loop_size, __func__); - GPU_indexbuf_init(&data->elb, GPU_PRIM_LINES_ADJ, tess_edge_len, mr->loop_len); - data->eh = BLI_edgehash_new_ex(__func__, tess_edge_len); - data->is_manifold = true; - return data; -} - -BLI_INLINE void lines_adjacency_triangle( - uint v1, uint v2, uint v3, uint l1, uint l2, uint l3, MeshExtract_LineAdjacency_Data *data) -{ - GPUIndexBufBuilder *elb = &data->elb; - /* Iterate around the triangle's edges. */ - for (int e = 0; e < 3; e++) { - SHIFT3(uint, v3, v2, v1); - SHIFT3(uint, l3, l2, l1); - - bool inv_indices = (v2 > v3); - void **pval; - bool value_is_init = BLI_edgehash_ensure_p(data->eh, v2, v3, &pval); - int v_data = POINTER_AS_INT(*pval); - if (!value_is_init || v_data == NO_EDGE) { - /* Save the winding order inside the sign bit. Because the - * Edge-hash sort the keys and we need to compare winding later. */ - int value = (int)l1 + 1; /* 0 cannot be signed so add one. */ - *pval = POINTER_FROM_INT((inv_indices) ? -value : value); - /* Store loop indices for remaining non-manifold edges. */ - data->vert_to_loop[v2] = l2; - data->vert_to_loop[v3] = l3; - } - else { - /* HACK Tag as not used. Prevent overhead of BLI_edgehash_remove. */ - *pval = POINTER_FROM_INT(NO_EDGE); - bool inv_opposite = (v_data < 0); - uint l_opposite = (uint)abs(v_data) - 1; - /* TODO Make this part thread-safe. */ - if (inv_opposite == inv_indices) { - /* Don't share edge if triangles have non matching winding. */ - GPU_indexbuf_add_line_adj_verts(elb, l1, l2, l3, l1); - GPU_indexbuf_add_line_adj_verts(elb, l_opposite, l2, l3, l_opposite); - data->is_manifold = false; - } - else { - GPU_indexbuf_add_line_adj_verts(elb, l1, l2, l3, l_opposite); - } - } - } -} - -static void extract_lines_adjacency_iter_looptri_bm(const MeshRenderData *UNUSED(mr), - BMLoop **elt, - const int UNUSED(elt_index), - void *data) -{ - if (!BM_elem_flag_test(elt[0]->f, BM_ELEM_HIDDEN)) { - lines_adjacency_triangle(BM_elem_index_get(elt[0]->v), - BM_elem_index_get(elt[1]->v), - BM_elem_index_get(elt[2]->v), - BM_elem_index_get(elt[0]), - BM_elem_index_get(elt[1]), - BM_elem_index_get(elt[2]), - data); - } -} - -static void extract_lines_adjacency_iter_looptri_mesh(const MeshRenderData *mr, - const MLoopTri *mlt, - const int UNUSED(elt_index), - void *data) -{ - const MPoly *mp = &mr->mpoly[mlt->poly]; - if (!(mr->use_hide && (mp->flag & ME_HIDE))) { - lines_adjacency_triangle(mr->mloop[mlt->tri[0]].v, - mr->mloop[mlt->tri[1]].v, - mr->mloop[mlt->tri[2]].v, - mlt->tri[0], - mlt->tri[1], - mlt->tri[2], - data); - } -} - -static void extract_lines_adjacency_finish(const MeshRenderData *UNUSED(mr), - struct MeshBatchCache *cache, - void *buf, - void *_data) -{ - GPUIndexBuf *ibo = buf; - MeshExtract_LineAdjacency_Data *data = _data; - /* Create edges for remaining non manifold edges. */ - EdgeHashIterator *ehi = BLI_edgehashIterator_new(data->eh); - for (; !BLI_edgehashIterator_isDone(ehi); BLI_edgehashIterator_step(ehi)) { - uint v2, v3, l1, l2, l3; - int v_data = POINTER_AS_INT(BLI_edgehashIterator_getValue(ehi)); - if (v_data != NO_EDGE) { - BLI_edgehashIterator_getKey(ehi, &v2, &v3); - l1 = (uint)abs(v_data) - 1; - if (v_data < 0) { /* inv_opposite */ - SWAP(uint, v2, v3); - } - l2 = data->vert_to_loop[v2]; - l3 = data->vert_to_loop[v3]; - GPU_indexbuf_add_line_adj_verts(&data->elb, l1, l2, l3, l1); - data->is_manifold = false; - } - } - BLI_edgehashIterator_free(ehi); - BLI_edgehash_free(data->eh, NULL); - - cache->is_manifold = data->is_manifold; - - GPU_indexbuf_build_in_place(&data->elb, ibo); - MEM_freeN(data); -} - -#undef NO_EDGE - -const MeshExtract extract_lines_adjacency = { - .init = extract_lines_adjacency_init, - .iter_looptri_bm = extract_lines_adjacency_iter_looptri_bm, - .iter_looptri_mesh = extract_lines_adjacency_iter_looptri_mesh, - .finish = extract_lines_adjacency_finish, - .data_type = 0, - .use_threading = false, - .mesh_buffer_offset = offsetof(MeshBufferCache, ibo.lines_adjacency)}; - -/** \} */ - -/* ---------------------------------------------------------------------- */ -/** \name Extract Edit UV Triangles Indices - * \{ */ - -typedef struct MeshExtract_EditUvElem_Data { - GPUIndexBufBuilder elb; - bool sync_selection; -} MeshExtract_EditUvElem_Data; - -static void *extract_edituv_tris_init(const MeshRenderData *mr, - struct MeshBatchCache *UNUSED(cache), - void *UNUSED(ibo)) -{ - MeshExtract_EditUvElem_Data *data = MEM_callocN(sizeof(*data), __func__); - GPU_indexbuf_init(&data->elb, GPU_PRIM_TRIS, mr->tri_len, mr->loop_len); - data->sync_selection = (mr->toolsettings->uv_flag & UV_SYNC_SELECTION) != 0; - return data; -} - -BLI_INLINE void edituv_tri_add( - MeshExtract_EditUvElem_Data *data, bool hidden, bool selected, int v1, int v2, int v3) -{ - if (!hidden && (data->sync_selection || selected)) { - GPU_indexbuf_add_tri_verts(&data->elb, v1, v2, v3); - } -} - -static void extract_edituv_tris_iter_looptri_bm(const MeshRenderData *UNUSED(mr), - BMLoop **elt, - const int UNUSED(elt_index), - void *data) -{ - edituv_tri_add(data, - BM_elem_flag_test(elt[0]->f, BM_ELEM_HIDDEN), - BM_elem_flag_test(elt[0]->f, BM_ELEM_SELECT), - BM_elem_index_get(elt[0]), - BM_elem_index_get(elt[1]), - BM_elem_index_get(elt[2])); -} - -static void extract_edituv_tris_iter_looptri_mesh(const MeshRenderData *mr, - const MLoopTri *mlt, - const int UNUSED(elt_index), - void *data) -{ - const MPoly *mp = &mr->mpoly[mlt->poly]; - edituv_tri_add(data, - (mp->flag & ME_HIDE) != 0, - (mp->flag & ME_FACE_SEL) != 0, - mlt->tri[0], - mlt->tri[1], - mlt->tri[2]); -} - -static void extract_edituv_tris_finish(const MeshRenderData *UNUSED(mr), - struct MeshBatchCache *UNUSED(cache), - void *buf, - void *data) -{ - GPUIndexBuf *ibo = buf; - MeshExtract_EditUvElem_Data *extract_data = data; - GPU_indexbuf_build_in_place(&extract_data->elb, ibo); - MEM_freeN(extract_data); -} - -const MeshExtract extract_edituv_tris = { - .init = extract_edituv_tris_init, - .iter_looptri_bm = extract_edituv_tris_iter_looptri_bm, - .iter_looptri_mesh = extract_edituv_tris_iter_looptri_mesh, - .finish = extract_edituv_tris_finish, - .data_type = 0, - .use_threading = false, - .mesh_buffer_offset = offsetof(MeshBufferCache, ibo.edituv_tris)}; - -/** \} */ - -/* ---------------------------------------------------------------------- */ -/** \name Extract Edit UV Line Indices around faces - * \{ */ - -static void *extract_edituv_lines_init(const MeshRenderData *mr, - struct MeshBatchCache *UNUSED(cache), - void *UNUSED(ibo)) -{ - MeshExtract_EditUvElem_Data *data = MEM_callocN(sizeof(*data), __func__); - GPU_indexbuf_init(&data->elb, GPU_PRIM_LINES, mr->loop_len, mr->loop_len); - - data->sync_selection = (mr->toolsettings->uv_flag & UV_SYNC_SELECTION) != 0; - return data; -} - -BLI_INLINE void edituv_edge_add( - MeshExtract_EditUvElem_Data *data, bool hidden, bool selected, int v1, int v2) -{ - if (!hidden && (data->sync_selection || selected)) { - GPU_indexbuf_add_line_verts(&data->elb, v1, v2); - } -} - -static void extract_edituv_lines_iter_poly_bm(const MeshRenderData *UNUSED(mr), - BMFace *f, - const int UNUSED(f_index), - void *data) -{ - BMLoop *l_iter, *l_first; - l_iter = l_first = BM_FACE_FIRST_LOOP(f); - do { - const int l_index = BM_elem_index_get(l_iter); - - edituv_edge_add(data, - BM_elem_flag_test_bool(f, BM_ELEM_HIDDEN), - BM_elem_flag_test_bool(f, BM_ELEM_SELECT), - l_index, - BM_elem_index_get(l_iter->next)); - } while ((l_iter = l_iter->next) != l_first); -} - -static void extract_edituv_lines_iter_poly_mesh(const MeshRenderData *mr, - const MPoly *mp, - const int UNUSED(mp_index), - void *data) -{ - const MLoop *mloop = mr->mloop; - const int ml_index_end = mp->loopstart + mp->totloop; - for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) { - const MLoop *ml = &mloop[ml_index]; - - const int ml_index_last = mp->totloop + mp->loopstart - 1; - const int ml_index_next = (ml_index == ml_index_last) ? mp->loopstart : (ml_index + 1); - const bool real_edge = (mr->e_origindex == NULL || mr->e_origindex[ml->e] != ORIGINDEX_NONE); - edituv_edge_add(data, - (mp->flag & ME_HIDE) != 0 || !real_edge, - (mp->flag & ME_FACE_SEL) != 0, - ml_index, - ml_index_next); - } -} - -static void extract_edituv_lines_finish(const MeshRenderData *UNUSED(mr), - struct MeshBatchCache *UNUSED(cache), - void *buf, - void *data) -{ - GPUIndexBuf *ibo = buf; - MeshExtract_EditUvElem_Data *extract_data = data; - GPU_indexbuf_build_in_place(&extract_data->elb, ibo); - MEM_freeN(extract_data); -} - -const MeshExtract extract_edituv_lines = { - .init = extract_edituv_lines_init, - .iter_poly_bm = extract_edituv_lines_iter_poly_bm, - .iter_poly_mesh = extract_edituv_lines_iter_poly_mesh, - .finish = extract_edituv_lines_finish, - .data_type = 0, - .use_threading = false, - .mesh_buffer_offset = offsetof(MeshBufferCache, ibo.edituv_lines)}; - -/** \} */ - -/* ---------------------------------------------------------------------- */ -/** \name Extract Edit UV Points Indices - * \{ */ - -static void *extract_edituv_points_init(const MeshRenderData *mr, - struct MeshBatchCache *UNUSED(cache), - void *UNUSED(ibo)) -{ - MeshExtract_EditUvElem_Data *data = MEM_callocN(sizeof(*data), __func__); - GPU_indexbuf_init(&data->elb, GPU_PRIM_POINTS, mr->loop_len, mr->loop_len); - - data->sync_selection = (mr->toolsettings->uv_flag & UV_SYNC_SELECTION) != 0; - return data; -} - -BLI_INLINE void edituv_point_add(MeshExtract_EditUvElem_Data *data, - bool hidden, - bool selected, - int v1) -{ - if (!hidden && (data->sync_selection || selected)) { - GPU_indexbuf_add_point_vert(&data->elb, v1); - } -} - -static void extract_edituv_points_iter_poly_bm(const MeshRenderData *UNUSED(mr), - BMFace *f, - const int UNUSED(f_index), - void *data) -{ - BMLoop *l_iter, *l_first; - l_iter = l_first = BM_FACE_FIRST_LOOP(f); - do { - const int l_index = BM_elem_index_get(l_iter); - - edituv_point_add( - data, BM_elem_flag_test(f, BM_ELEM_HIDDEN), BM_elem_flag_test(f, BM_ELEM_SELECT), l_index); - } while ((l_iter = l_iter->next) != l_first); -} - -static void extract_edituv_points_iter_poly_mesh(const MeshRenderData *mr, - const MPoly *mp, - const int UNUSED(mp_index), - void *data) -{ - const MLoop *mloop = mr->mloop; - const int ml_index_end = mp->loopstart + mp->totloop; - for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) { - const MLoop *ml = &mloop[ml_index]; - - const bool real_vert = (mr->extract_type == MR_EXTRACT_MAPPED && (mr->v_origindex) && - mr->v_origindex[ml->v] != ORIGINDEX_NONE); - edituv_point_add( - data, ((mp->flag & ME_HIDE) != 0) || !real_vert, (mp->flag & ME_FACE_SEL) != 0, ml_index); - } -} - -static void extract_edituv_points_finish(const MeshRenderData *UNUSED(mr), - struct MeshBatchCache *UNUSED(cache), - void *buf, - void *data) -{ - GPUIndexBuf *ibo = buf; - MeshExtract_EditUvElem_Data *extract_data = data; - GPU_indexbuf_build_in_place(&extract_data->elb, ibo); - MEM_freeN(extract_data); -} - -const MeshExtract extract_edituv_points = { - .init = extract_edituv_points_init, - .iter_poly_bm = extract_edituv_points_iter_poly_bm, - .iter_poly_mesh = extract_edituv_points_iter_poly_mesh, - .finish = extract_edituv_points_finish, - .data_type = 0, - .use_threading = false, - .mesh_buffer_offset = offsetof(MeshBufferCache, ibo.edituv_points)}; - -/** \} */ - -/* ---------------------------------------------------------------------- */ -/** \name Extract Edit UV Face-dots Indices - * \{ */ - -static void *extract_edituv_fdots_init(const MeshRenderData *mr, - struct MeshBatchCache *UNUSED(cache), - void *UNUSED(ibo)) -{ - MeshExtract_EditUvElem_Data *data = MEM_callocN(sizeof(*data), __func__); - GPU_indexbuf_init(&data->elb, GPU_PRIM_POINTS, mr->poly_len, mr->poly_len); - - data->sync_selection = (mr->toolsettings->uv_flag & UV_SYNC_SELECTION) != 0; - return data; -} - -BLI_INLINE void edituv_facedot_add(MeshExtract_EditUvElem_Data *data, - bool hidden, - bool selected, - int face_index) -{ - if (!hidden && (data->sync_selection || selected)) { - GPU_indexbuf_set_point_vert(&data->elb, face_index, face_index); - } - else { - GPU_indexbuf_set_point_restart(&data->elb, face_index); - } -} - -static void extract_edituv_fdots_iter_poly_bm(const MeshRenderData *UNUSED(mr), - BMFace *f, - const int f_index, - void *data) -{ - edituv_facedot_add(data, - BM_elem_flag_test_bool(f, BM_ELEM_HIDDEN), - BM_elem_flag_test_bool(f, BM_ELEM_SELECT), - f_index); -} - -static void extract_edituv_fdots_iter_poly_mesh(const MeshRenderData *mr, - const MPoly *mp, - const int mp_index, - void *data) -{ - if (mr->use_subsurf_fdots) { - /* Check #ME_VERT_FACEDOT. */ - const MLoop *mloop = mr->mloop; - const int ml_index_end = mp->loopstart + mp->totloop; - for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) { - const MLoop *ml = &mloop[ml_index]; - - const bool real_fdot = (mr->extract_type == MR_EXTRACT_MAPPED && mr->p_origindex && - mr->p_origindex[mp_index] != ORIGINDEX_NONE); - const bool subd_fdot = (!mr->use_subsurf_fdots || - (mr->mvert[ml->v].flag & ME_VERT_FACEDOT) != 0); - edituv_facedot_add(data, - ((mp->flag & ME_HIDE) != 0) || !real_fdot || !subd_fdot, - (mp->flag & ME_FACE_SEL) != 0, - mp_index); - } - } - else { - const bool real_fdot = (mr->extract_type == MR_EXTRACT_MAPPED && mr->p_origindex && - mr->p_origindex[mp_index] != ORIGINDEX_NONE); - edituv_facedot_add( - data, ((mp->flag & ME_HIDE) != 0) || !real_fdot, (mp->flag & ME_FACE_SEL) != 0, mp_index); - } -} - -static void extract_edituv_fdots_finish(const MeshRenderData *UNUSED(mr), - struct MeshBatchCache *UNUSED(cache), - void *buf, - void *_data) -{ - GPUIndexBuf *ibo = buf; - MeshExtract_EditUvElem_Data *data = _data; - GPU_indexbuf_build_in_place(&data->elb, ibo); - MEM_freeN(data); -} - -const MeshExtract extract_edituv_fdots = { - .init = extract_edituv_fdots_init, - .iter_poly_bm = extract_edituv_fdots_iter_poly_bm, - .iter_poly_mesh = extract_edituv_fdots_iter_poly_mesh, - .finish = extract_edituv_fdots_finish, - .data_type = 0, - .use_threading = false, - .mesh_buffer_offset = offsetof(MeshBufferCache, ibo.edituv_fdots)}; - -/** \} */ - -/* ---------------------------------------------------------------------- */ /** \name Extract Position and Vertex Normal * \{ */ diff --git a/source/blender/draw/intern/draw_cache_extract_mesh_private.h b/source/blender/draw/intern/draw_cache_extract_mesh_private.h index a1249203060..e4ea3e44843 100644 --- a/source/blender/draw/intern/draw_cache_extract_mesh_private.h +++ b/source/blender/draw/intern/draw_cache_extract_mesh_private.h @@ -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; diff --git a/source/blender/draw/intern/draw_cache_impl_curve.cc b/source/blender/draw/intern/draw_cache_impl_curve.cc index ee6a47e3dc6..9ca452cdacc 100644 --- a/source/blender/draw/intern/draw_cache_impl_curve.cc +++ b/source/blender/draw/intern/draw_cache_impl_curve.cc @@ -843,8 +843,8 @@ static void curve_create_edit_data_and_handles(CurveRenderData *rdata, int edges_len_capacity = curve_render_data_overlay_edges_len_get(rdata) * 2; int vbo_len_used = 0; -#define DRW_TEST_ASSIGN_VBO(v) (v = (DRW_vbo_requested(v) ? (v) : NULL)) -#define DRW_TEST_ASSIGN_IBO(v) (v = (DRW_ibo_requested(v) ? (v) : NULL)) +#define DRW_TEST_ASSIGN_VBO(v) (v = (DRW_vbo_requested(v) ? (v) : nullptr)) +#define DRW_TEST_ASSIGN_IBO(v) (v = (DRW_ibo_requested(v) ? (v) : nullptr)) if (DRW_TEST_ASSIGN_VBO(vbo_pos)) { GPU_vertbuf_init_with_format(vbo_pos, &format_pos); diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_edituv.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_edituv.cc new file mode 100644 index 00000000000..20b0ec738ee --- /dev/null +++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_edituv.cc @@ -0,0 +1,396 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2021 by Blender Foundation. + * All rights reserved. + */ + +/** \file + * \ingroup draw + */ + +#include "draw_cache_extract_mesh_private.h" + +#include "BLI_vector.hh" + +#include "MEM_guardedalloc.h" + +namespace blender::draw { +/* ---------------------------------------------------------------------- */ +/** \name Extract Edit UV Triangles Indices + * \{ */ + +struct MeshExtract_EditUvElem_Data { + GPUIndexBufBuilder elb; + bool sync_selection; +}; + +static void *extract_edituv_tris_init(const MeshRenderData *mr, + struct MeshBatchCache *UNUSED(cache), + void *UNUSED(ibo)) +{ + MeshExtract_EditUvElem_Data *data = static_cast<MeshExtract_EditUvElem_Data *>( + MEM_callocN(sizeof(*data), __func__)); + GPU_indexbuf_init(&data->elb, GPU_PRIM_TRIS, mr->tri_len, mr->loop_len); + data->sync_selection = (mr->toolsettings->uv_flag & UV_SYNC_SELECTION) != 0; + return data; +} + +BLI_INLINE void edituv_tri_add( + MeshExtract_EditUvElem_Data *data, bool hidden, bool selected, int v1, int v2, int v3) +{ + if (!hidden && (data->sync_selection || selected)) { + GPU_indexbuf_add_tri_verts(&data->elb, v1, v2, v3); + } +} + +static void extract_edituv_tris_iter_looptri_bm(const MeshRenderData *UNUSED(mr), + BMLoop **elt, + const int UNUSED(elt_index), + void *_data) +{ + MeshExtract_EditUvElem_Data *data = static_cast<MeshExtract_EditUvElem_Data *>(_data); + edituv_tri_add(data, + BM_elem_flag_test(elt[0]->f, BM_ELEM_HIDDEN), + BM_elem_flag_test(elt[0]->f, BM_ELEM_SELECT), + BM_elem_index_get(elt[0]), + BM_elem_index_get(elt[1]), + BM_elem_index_get(elt[2])); +} + +static void extract_edituv_tris_iter_looptri_mesh(const MeshRenderData *mr, + const MLoopTri *mlt, + const int UNUSED(elt_index), + void *_data) +{ + MeshExtract_EditUvElem_Data *data = static_cast<MeshExtract_EditUvElem_Data *>(_data); + const MPoly *mp = &mr->mpoly[mlt->poly]; + edituv_tri_add(data, + (mp->flag & ME_HIDE) != 0, + (mp->flag & ME_FACE_SEL) != 0, + mlt->tri[0], + mlt->tri[1], + mlt->tri[2]); +} + +static void extract_edituv_tris_finish(const MeshRenderData *UNUSED(mr), + struct MeshBatchCache *UNUSED(cache), + void *buf, + void *_data) +{ + MeshExtract_EditUvElem_Data *data = static_cast<MeshExtract_EditUvElem_Data *>(_data); + GPUIndexBuf *ibo = static_cast<GPUIndexBuf *>(buf); + GPU_indexbuf_build_in_place(&data->elb, ibo); + MEM_freeN(data); +} + +constexpr MeshExtract create_extractor_edituv_tris() +{ + MeshExtract extractor = {0}; + extractor.init = extract_edituv_tris_init; + extractor.iter_looptri_bm = extract_edituv_tris_iter_looptri_bm; + extractor.iter_looptri_mesh = extract_edituv_tris_iter_looptri_mesh; + extractor.finish = extract_edituv_tris_finish; + extractor.data_type = MR_DATA_NONE; + extractor.use_threading = false; + extractor.mesh_buffer_offset = offsetof(MeshBufferCache, ibo.edituv_tris); + return extractor; +} + +/** \} */ + +/* ---------------------------------------------------------------------- */ +/** \name Extract Edit UV Line Indices around faces + * \{ */ + +static void *extract_edituv_lines_init(const MeshRenderData *mr, + struct MeshBatchCache *UNUSED(cache), + void *UNUSED(ibo)) +{ + MeshExtract_EditUvElem_Data *data = static_cast<MeshExtract_EditUvElem_Data *>( + MEM_callocN(sizeof(*data), __func__)); + GPU_indexbuf_init(&data->elb, GPU_PRIM_LINES, mr->loop_len, mr->loop_len); + data->sync_selection = (mr->toolsettings->uv_flag & UV_SYNC_SELECTION) != 0; + return data; +} + +BLI_INLINE void edituv_edge_add( + MeshExtract_EditUvElem_Data *data, bool hidden, bool selected, int v1, int v2) +{ + if (!hidden && (data->sync_selection || selected)) { + GPU_indexbuf_add_line_verts(&data->elb, v1, v2); + } +} + +static void extract_edituv_lines_iter_poly_bm(const MeshRenderData *UNUSED(mr), + BMFace *f, + const int UNUSED(f_index), + void *_data) +{ + MeshExtract_EditUvElem_Data *data = static_cast<MeshExtract_EditUvElem_Data *>(_data); + BMLoop *l_iter, *l_first; + l_iter = l_first = BM_FACE_FIRST_LOOP(f); + do { + const int l_index = BM_elem_index_get(l_iter); + + edituv_edge_add(data, + BM_elem_flag_test_bool(f, BM_ELEM_HIDDEN), + BM_elem_flag_test_bool(f, BM_ELEM_SELECT), + l_index, + BM_elem_index_get(l_iter->next)); + } while ((l_iter = l_iter->next) != l_first); +} + +static void extract_edituv_lines_iter_poly_mesh(const MeshRenderData *mr, + const MPoly *mp, + const int UNUSED(mp_index), + void *_data) +{ + MeshExtract_EditUvElem_Data *data = static_cast<MeshExtract_EditUvElem_Data *>(_data); + const MLoop *mloop = mr->mloop; + const int ml_index_end = mp->loopstart + mp->totloop; + for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) { + const MLoop *ml = &mloop[ml_index]; + + const int ml_index_last = mp->totloop + mp->loopstart - 1; + const int ml_index_next = (ml_index == ml_index_last) ? mp->loopstart : (ml_index + 1); + const bool real_edge = (mr->e_origindex == nullptr || + mr->e_origindex[ml->e] != ORIGINDEX_NONE); + edituv_edge_add(data, + (mp->flag & ME_HIDE) != 0 || !real_edge, + (mp->flag & ME_FACE_SEL) != 0, + ml_index, + ml_index_next); + } +} + +static void extract_edituv_lines_finish(const MeshRenderData *UNUSED(mr), + struct MeshBatchCache *UNUSED(cache), + void *buf, + void *_data) +{ + MeshExtract_EditUvElem_Data *data = static_cast<MeshExtract_EditUvElem_Data *>(_data); + GPUIndexBuf *ibo = static_cast<GPUIndexBuf *>(buf); + GPU_indexbuf_build_in_place(&data->elb, ibo); + MEM_freeN(data); +} + +constexpr MeshExtract create_extractor_edituv_lines() +{ + MeshExtract extractor = {0}; + extractor.init = extract_edituv_lines_init; + extractor.iter_poly_bm = extract_edituv_lines_iter_poly_bm; + extractor.iter_poly_mesh = extract_edituv_lines_iter_poly_mesh; + extractor.finish = extract_edituv_lines_finish; + extractor.data_type = MR_DATA_NONE; + extractor.use_threading = false; + extractor.mesh_buffer_offset = offsetof(MeshBufferCache, ibo.edituv_lines); + return extractor; +} + +/** \} */ + +/* ---------------------------------------------------------------------- */ +/** \name Extract Edit UV Points Indices + * \{ */ + +static void *extract_edituv_points_init(const MeshRenderData *mr, + struct MeshBatchCache *UNUSED(cache), + void *UNUSED(ibo)) +{ + MeshExtract_EditUvElem_Data *data = static_cast<MeshExtract_EditUvElem_Data *>( + MEM_callocN(sizeof(*data), __func__)); + GPU_indexbuf_init(&data->elb, GPU_PRIM_POINTS, mr->loop_len, mr->loop_len); + data->sync_selection = (mr->toolsettings->uv_flag & UV_SYNC_SELECTION) != 0; + return data; +} + +BLI_INLINE void edituv_point_add(MeshExtract_EditUvElem_Data *data, + bool hidden, + bool selected, + int v1) +{ + if (!hidden && (data->sync_selection || selected)) { + GPU_indexbuf_add_point_vert(&data->elb, v1); + } +} + +static void extract_edituv_points_iter_poly_bm(const MeshRenderData *UNUSED(mr), + BMFace *f, + const int UNUSED(f_index), + void *_data) +{ + MeshExtract_EditUvElem_Data *data = static_cast<MeshExtract_EditUvElem_Data *>(_data); + BMLoop *l_iter, *l_first; + l_iter = l_first = BM_FACE_FIRST_LOOP(f); + do { + const int l_index = BM_elem_index_get(l_iter); + + edituv_point_add( + data, BM_elem_flag_test(f, BM_ELEM_HIDDEN), BM_elem_flag_test(f, BM_ELEM_SELECT), l_index); + } while ((l_iter = l_iter->next) != l_first); +} + +static void extract_edituv_points_iter_poly_mesh(const MeshRenderData *mr, + const MPoly *mp, + const int UNUSED(mp_index), + void *_data) +{ + MeshExtract_EditUvElem_Data *data = static_cast<MeshExtract_EditUvElem_Data *>(_data); + const MLoop *mloop = mr->mloop; + const int ml_index_end = mp->loopstart + mp->totloop; + for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) { + const MLoop *ml = &mloop[ml_index]; + + const bool real_vert = (mr->extract_type == MR_EXTRACT_MAPPED && (mr->v_origindex) && + mr->v_origindex[ml->v] != ORIGINDEX_NONE); + edituv_point_add( + data, ((mp->flag & ME_HIDE) != 0) || !real_vert, (mp->flag & ME_FACE_SEL) != 0, ml_index); + } +} + +static void extract_edituv_points_finish(const MeshRenderData *UNUSED(mr), + struct MeshBatchCache *UNUSED(cache), + void *buf, + void *_data) +{ + MeshExtract_EditUvElem_Data *data = static_cast<MeshExtract_EditUvElem_Data *>(_data); + GPUIndexBuf *ibo = static_cast<GPUIndexBuf *>(buf); + GPU_indexbuf_build_in_place(&data->elb, ibo); + MEM_freeN(data); +} + +constexpr MeshExtract create_extractor_edituv_points() +{ + MeshExtract extractor = {0}; + extractor.init = extract_edituv_points_init; + extractor.iter_poly_bm = extract_edituv_points_iter_poly_bm; + extractor.iter_poly_mesh = extract_edituv_points_iter_poly_mesh; + extractor.finish = extract_edituv_points_finish; + extractor.data_type = MR_DATA_NONE; + extractor.use_threading = false; + extractor.mesh_buffer_offset = offsetof(MeshBufferCache, ibo.edituv_points); + return extractor; +} + +/** \} */ + +/* ---------------------------------------------------------------------- */ +/** \name Extract Edit UV Face-dots Indices + * \{ */ + +static void *extract_edituv_fdots_init(const MeshRenderData *mr, + struct MeshBatchCache *UNUSED(cache), + void *UNUSED(ibo)) +{ + MeshExtract_EditUvElem_Data *data = static_cast<MeshExtract_EditUvElem_Data *>( + MEM_callocN(sizeof(*data), __func__)); + GPU_indexbuf_init(&data->elb, GPU_PRIM_POINTS, mr->poly_len, mr->poly_len); + data->sync_selection = (mr->toolsettings->uv_flag & UV_SYNC_SELECTION) != 0; + return data; +} + +BLI_INLINE void edituv_facedot_add(MeshExtract_EditUvElem_Data *data, + bool hidden, + bool selected, + int face_index) +{ + if (!hidden && (data->sync_selection || selected)) { + GPU_indexbuf_set_point_vert(&data->elb, face_index, face_index); + } + else { + GPU_indexbuf_set_point_restart(&data->elb, face_index); + } +} + +static void extract_edituv_fdots_iter_poly_bm(const MeshRenderData *UNUSED(mr), + BMFace *f, + const int f_index, + void *_data) +{ + MeshExtract_EditUvElem_Data *data = static_cast<MeshExtract_EditUvElem_Data *>(_data); + edituv_facedot_add(data, + BM_elem_flag_test_bool(f, BM_ELEM_HIDDEN), + BM_elem_flag_test_bool(f, BM_ELEM_SELECT), + f_index); +} + +static void extract_edituv_fdots_iter_poly_mesh(const MeshRenderData *mr, + const MPoly *mp, + const int mp_index, + void *_data) +{ + MeshExtract_EditUvElem_Data *data = static_cast<MeshExtract_EditUvElem_Data *>(_data); + if (mr->use_subsurf_fdots) { + /* Check #ME_VERT_FACEDOT. */ + const MLoop *mloop = mr->mloop; + const int ml_index_end = mp->loopstart + mp->totloop; + for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) { + const MLoop *ml = &mloop[ml_index]; + + const bool real_fdot = (mr->extract_type == MR_EXTRACT_MAPPED && mr->p_origindex && + mr->p_origindex[mp_index] != ORIGINDEX_NONE); + const bool subd_fdot = (!mr->use_subsurf_fdots || + (mr->mvert[ml->v].flag & ME_VERT_FACEDOT) != 0); + edituv_facedot_add(data, + ((mp->flag & ME_HIDE) != 0) || !real_fdot || !subd_fdot, + (mp->flag & ME_FACE_SEL) != 0, + mp_index); + } + } + else { + const bool real_fdot = (mr->extract_type == MR_EXTRACT_MAPPED && mr->p_origindex && + mr->p_origindex[mp_index] != ORIGINDEX_NONE); + edituv_facedot_add( + data, ((mp->flag & ME_HIDE) != 0) || !real_fdot, (mp->flag & ME_FACE_SEL) != 0, mp_index); + } +} + +static void extract_edituv_fdots_finish(const MeshRenderData *UNUSED(mr), + struct MeshBatchCache *UNUSED(cache), + void *buf, + void *_data) +{ + MeshExtract_EditUvElem_Data *data = static_cast<MeshExtract_EditUvElem_Data *>(_data); + GPUIndexBuf *ibo = static_cast<GPUIndexBuf *>(buf); + GPU_indexbuf_build_in_place(&data->elb, ibo); + MEM_freeN(data); +} + +constexpr MeshExtract create_extractor_edituv_fdots() +{ + MeshExtract extractor = {0}; + extractor.init = extract_edituv_fdots_init; + extractor.iter_poly_bm = extract_edituv_fdots_iter_poly_bm; + extractor.iter_poly_mesh = extract_edituv_fdots_iter_poly_mesh; + extractor.finish = extract_edituv_fdots_finish; + extractor.data_type = MR_DATA_NONE; + extractor.use_threading = false; + extractor.mesh_buffer_offset = offsetof(MeshBufferCache, ibo.edituv_fdots); + return extractor; +} + +/** \} */ + +} // namespace blender::draw + +extern "C" { +const MeshExtract extract_edituv_tris = blender::draw::create_extractor_edituv_tris(); +const MeshExtract extract_edituv_lines = blender::draw::create_extractor_edituv_lines(); +const MeshExtract extract_edituv_points = blender::draw::create_extractor_edituv_points(); +const MeshExtract extract_edituv_fdots = blender::draw::create_extractor_edituv_fdots(); +} + +/** \} */ diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_fdots.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_fdots.cc new file mode 100644 index 00000000000..9bd918dc9a5 --- /dev/null +++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_fdots.cc @@ -0,0 +1,119 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2021 by Blender Foundation. + * All rights reserved. + */ + +/** \file + * \ingroup draw + */ + +#include "draw_cache_extract_mesh_private.h" + +#include "BLI_vector.hh" + +#include "MEM_guardedalloc.h" + +namespace blender::draw { +/* ---------------------------------------------------------------------- */ +/** \name Extract Face-dots Indices + * \{ */ + +static void *extract_fdots_init(const MeshRenderData *mr, + struct MeshBatchCache *UNUSED(cache), + void *UNUSED(buf)) +{ + GPUIndexBufBuilder *elb = static_cast<GPUIndexBufBuilder *>(MEM_mallocN(sizeof(*elb), __func__)); + GPU_indexbuf_init(elb, GPU_PRIM_POINTS, mr->poly_len, mr->poly_len); + return elb; +} + +static void extract_fdots_iter_poly_bm(const MeshRenderData *UNUSED(mr), + BMFace *f, + const int f_index, + void *_userdata) +{ + GPUIndexBufBuilder *elb = static_cast<GPUIndexBufBuilder *>(_userdata); + if (!BM_elem_flag_test(f, BM_ELEM_HIDDEN)) { + GPU_indexbuf_set_point_vert(elb, f_index, f_index); + } + else { + GPU_indexbuf_set_point_restart(elb, f_index); + } +} + +static void extract_fdots_iter_poly_mesh(const MeshRenderData *mr, + const MPoly *mp, + const int mp_index, + void *_userdata) +{ + GPUIndexBufBuilder *elb = static_cast<GPUIndexBufBuilder *>(_userdata); + if (mr->use_subsurf_fdots) { + /* Check #ME_VERT_FACEDOT. */ + const MLoop *mloop = mr->mloop; + const int ml_index_end = mp->loopstart + mp->totloop; + for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) { + const MLoop *ml = &mloop[ml_index]; + const MVert *mv = &mr->mvert[ml->v]; + if ((mv->flag & ME_VERT_FACEDOT) && !(mr->use_hide && (mp->flag & ME_HIDE))) { + GPU_indexbuf_set_point_vert(elb, mp_index, mp_index); + return; + } + } + GPU_indexbuf_set_point_restart(elb, mp_index); + } + else { + if (!(mr->use_hide && (mp->flag & ME_HIDE))) { + GPU_indexbuf_set_point_vert(elb, mp_index, mp_index); + } + else { + GPU_indexbuf_set_point_restart(elb, mp_index); + } + } +} + +static void extract_fdots_finish(const MeshRenderData *UNUSED(mr), + struct MeshBatchCache *UNUSED(cache), + void *buf, + void *_userdata) +{ + GPUIndexBufBuilder *elb = static_cast<GPUIndexBufBuilder *>(_userdata); + GPUIndexBuf *ibo = static_cast<GPUIndexBuf *>(buf); + GPU_indexbuf_build_in_place(elb, ibo); + MEM_freeN(elb); +} + +constexpr MeshExtract create_extractor_fdots() +{ + MeshExtract extractor = {0}; + extractor.init = extract_fdots_init; + extractor.iter_poly_bm = extract_fdots_iter_poly_bm; + extractor.iter_poly_mesh = extract_fdots_iter_poly_mesh; + extractor.finish = extract_fdots_finish; + extractor.data_type = MR_DATA_NONE; + extractor.use_threading = false; + extractor.mesh_buffer_offset = offsetof(MeshBufferCache, ibo.fdots); + return extractor; +} + +/** \} */ +} // namespace blender::draw + +extern "C" { +const MeshExtract extract_fdots = blender::draw::create_extractor_fdots(); +} + +/** \} */ diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_lines.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_lines.cc index ccc382c1b06..6237529902b 100644 --- a/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_lines.cc +++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_lines.cc @@ -73,7 +73,7 @@ static void extract_lines_iter_poly_mesh(const MeshRenderData *mr, /* Using poly & loop iterator would complicate accessing the adjacent loop. */ const MLoop *mloop = mr->mloop; const MEdge *medge = mr->medge; - if (mr->use_hide || (mr->extract_type == MR_EXTRACT_MAPPED) || (mr->e_origindex != NULL)) { + if (mr->use_hide || (mr->extract_type == MR_EXTRACT_MAPPED) || (mr->e_origindex != nullptr)) { const int ml_index_last = mp->loopstart + (mp->totloop - 1); int ml_index = ml_index_last, ml_index_next = mp->loopstart; do { diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_lines_adjacency.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_lines_adjacency.cc new file mode 100644 index 00000000000..6140ae86c96 --- /dev/null +++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_lines_adjacency.cc @@ -0,0 +1,198 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2021 by Blender Foundation. + * All rights reserved. + */ + +/** \file + * \ingroup draw + */ + +#include "draw_cache_extract_mesh_private.h" + +#include "BLI_edgehash.h" +#include "BLI_vector.hh" + +#include "MEM_guardedalloc.h" + +namespace blender::draw { + +/* ---------------------------------------------------------------------- */ +/** \name Extract Line Adjacency Indices + * \{ */ + +#define NO_EDGE INT_MAX + +struct MeshExtract_LineAdjacency_Data { + GPUIndexBufBuilder elb; + EdgeHash *eh; + bool is_manifold; + /* Array to convert vert index to any loop index of this vert. */ + uint vert_to_loop[0]; +}; + +static void *extract_lines_adjacency_init(const MeshRenderData *mr, + struct MeshBatchCache *UNUSED(cache), + void *UNUSED(buf)) +{ + /* Similar to poly_to_tri_count(). + * There is always (loop + triangle - 1) edges inside a polygon. + * Accumulate for all polys and you get : */ + uint tess_edge_len = mr->loop_len + mr->tri_len - mr->poly_len; + + size_t vert_to_loop_size = sizeof(uint) * mr->vert_len; + + MeshExtract_LineAdjacency_Data *data = static_cast<MeshExtract_LineAdjacency_Data *>( + MEM_callocN(sizeof(*data) + vert_to_loop_size, __func__)); + GPU_indexbuf_init(&data->elb, GPU_PRIM_LINES_ADJ, tess_edge_len, mr->loop_len); + data->eh = BLI_edgehash_new_ex(__func__, tess_edge_len); + data->is_manifold = true; + return data; +} + +BLI_INLINE void lines_adjacency_triangle( + uint v1, uint v2, uint v3, uint l1, uint l2, uint l3, MeshExtract_LineAdjacency_Data *data) +{ + GPUIndexBufBuilder *elb = &data->elb; + /* Iterate around the triangle's edges. */ + for (int e = 0; e < 3; e++) { + SHIFT3(uint, v3, v2, v1); + SHIFT3(uint, l3, l2, l1); + + bool inv_indices = (v2 > v3); + void **pval; + bool value_is_init = BLI_edgehash_ensure_p(data->eh, v2, v3, &pval); + int v_data = POINTER_AS_INT(*pval); + if (!value_is_init || v_data == NO_EDGE) { + /* Save the winding order inside the sign bit. Because the + * Edge-hash sort the keys and we need to compare winding later. */ + int value = (int)l1 + 1; /* 0 cannot be signed so add one. */ + *pval = POINTER_FROM_INT((inv_indices) ? -value : value); + /* Store loop indices for remaining non-manifold edges. */ + data->vert_to_loop[v2] = l2; + data->vert_to_loop[v3] = l3; + } + else { + /* HACK Tag as not used. Prevent overhead of BLI_edgehash_remove. */ + *pval = POINTER_FROM_INT(NO_EDGE); + bool inv_opposite = (v_data < 0); + uint l_opposite = (uint)abs(v_data) - 1; + /* TODO Make this part thread-safe. */ + if (inv_opposite == inv_indices) { + /* Don't share edge if triangles have non matching winding. */ + GPU_indexbuf_add_line_adj_verts(elb, l1, l2, l3, l1); + GPU_indexbuf_add_line_adj_verts(elb, l_opposite, l2, l3, l_opposite); + data->is_manifold = false; + } + else { + GPU_indexbuf_add_line_adj_verts(elb, l1, l2, l3, l_opposite); + } + } + } +} + +static void extract_lines_adjacency_iter_looptri_bm(const MeshRenderData *UNUSED(mr), + BMLoop **elt, + const int UNUSED(elt_index), + void *_data) +{ + MeshExtract_LineAdjacency_Data *data = static_cast<MeshExtract_LineAdjacency_Data *>(_data); + if (!BM_elem_flag_test(elt[0]->f, BM_ELEM_HIDDEN)) { + lines_adjacency_triangle(BM_elem_index_get(elt[0]->v), + BM_elem_index_get(elt[1]->v), + BM_elem_index_get(elt[2]->v), + BM_elem_index_get(elt[0]), + BM_elem_index_get(elt[1]), + BM_elem_index_get(elt[2]), + data); + } +} + +static void extract_lines_adjacency_iter_looptri_mesh(const MeshRenderData *mr, + const MLoopTri *mlt, + const int UNUSED(elt_index), + void *_data) +{ + MeshExtract_LineAdjacency_Data *data = static_cast<MeshExtract_LineAdjacency_Data *>(_data); + const MPoly *mp = &mr->mpoly[mlt->poly]; + if (!(mr->use_hide && (mp->flag & ME_HIDE))) { + lines_adjacency_triangle(mr->mloop[mlt->tri[0]].v, + mr->mloop[mlt->tri[1]].v, + mr->mloop[mlt->tri[2]].v, + mlt->tri[0], + mlt->tri[1], + mlt->tri[2], + data); + } +} + +static void extract_lines_adjacency_finish(const MeshRenderData *UNUSED(mr), + struct MeshBatchCache *cache, + void *buf, + void *_data) +{ + GPUIndexBuf *ibo = static_cast<GPUIndexBuf *>(buf); + MeshExtract_LineAdjacency_Data *data = static_cast<MeshExtract_LineAdjacency_Data *>(_data); + /* Create edges for remaining non manifold edges. */ + EdgeHashIterator *ehi = BLI_edgehashIterator_new(data->eh); + for (; !BLI_edgehashIterator_isDone(ehi); BLI_edgehashIterator_step(ehi)) { + uint v2, v3, l1, l2, l3; + int v_data = POINTER_AS_INT(BLI_edgehashIterator_getValue(ehi)); + if (v_data != NO_EDGE) { + BLI_edgehashIterator_getKey(ehi, &v2, &v3); + l1 = (uint)abs(v_data) - 1; + if (v_data < 0) { /* inv_opposite */ + SWAP(uint, v2, v3); + } + l2 = data->vert_to_loop[v2]; + l3 = data->vert_to_loop[v3]; + GPU_indexbuf_add_line_adj_verts(&data->elb, l1, l2, l3, l1); + data->is_manifold = false; + } + } + BLI_edgehashIterator_free(ehi); + BLI_edgehash_free(data->eh, nullptr); + + cache->is_manifold = data->is_manifold; + + GPU_indexbuf_build_in_place(&data->elb, ibo); + MEM_freeN(data); +} + +#undef NO_EDGE + +/** \} */ + +constexpr MeshExtract create_extractor_lines_adjacency() +{ + MeshExtract extractor = {0}; + extractor.init = extract_lines_adjacency_init; + extractor.iter_looptri_bm = extract_lines_adjacency_iter_looptri_bm; + extractor.iter_looptri_mesh = extract_lines_adjacency_iter_looptri_mesh; + extractor.finish = extract_lines_adjacency_finish; + extractor.data_type = MR_DATA_NONE; + extractor.use_threading = false; + extractor.mesh_buffer_offset = offsetof(MeshBufferCache, ibo.lines_adjacency); + return extractor; +} + +} // namespace blender::draw + +extern "C" { +const MeshExtract extract_lines_adjacency = blender::draw::create_extractor_lines_adjacency(); +} + +/** \} */ diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_lines_paint_mask.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_lines_paint_mask.cc new file mode 100644 index 00000000000..6bbd0188f65 --- /dev/null +++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_lines_paint_mask.cc @@ -0,0 +1,127 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2021 by Blender Foundation. + * All rights reserved. + */ + +/** \file + * \ingroup draw + */ + +#include "draw_cache_extract_mesh_private.h" + +#include "BLI_bitmap.h" +#include "BLI_vector.hh" +#include "atomic_ops.h" + +#include "MEM_guardedalloc.h" + +namespace blender::draw { +/* ---------------------------------------------------------------------- */ +/** \name Extract Paint Mask Line Indices + * \{ */ + +struct MeshExtract_LinePaintMask_Data { + GPUIndexBufBuilder elb; + /** One bit per edge set if face is selected. */ + BLI_bitmap select_map[0]; +}; + +static void *extract_lines_paint_mask_init(const MeshRenderData *mr, + struct MeshBatchCache *UNUSED(cache), + void *UNUSED(ibo)) +{ + size_t bitmap_size = BLI_BITMAP_SIZE(mr->edge_len); + MeshExtract_LinePaintMask_Data *data = static_cast<MeshExtract_LinePaintMask_Data *>( + MEM_callocN(sizeof(*data) + bitmap_size, __func__)); + GPU_indexbuf_init(&data->elb, GPU_PRIM_LINES, mr->edge_len, mr->loop_len); + return data; +} + +static void extract_lines_paint_mask_iter_poly_mesh(const MeshRenderData *mr, + const MPoly *mp, + const int UNUSED(mp_index), + void *_data) +{ + MeshExtract_LinePaintMask_Data *data = static_cast<MeshExtract_LinePaintMask_Data *>(_data); + const MLoop *mloop = mr->mloop; + const int ml_index_end = mp->loopstart + mp->totloop; + for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) { + const MLoop *ml = &mloop[ml_index]; + + const int e_index = ml->e; + const MEdge *me = &mr->medge[e_index]; + if (!((mr->use_hide && (me->flag & ME_HIDE)) || + ((mr->extract_type == MR_EXTRACT_MAPPED) && (mr->e_origindex) && + (mr->e_origindex[e_index] == ORIGINDEX_NONE)))) { + + const int ml_index_last = mp->totloop + mp->loopstart - 1; + const int ml_index_other = (ml_index == ml_index_last) ? mp->loopstart : (ml_index + 1); + if (mp->flag & ME_FACE_SEL) { + if (BLI_BITMAP_TEST_AND_SET_ATOMIC(data->select_map, e_index)) { + /* Hide edge as it has more than 2 selected loop. */ + GPU_indexbuf_set_line_restart(&data->elb, e_index); + } + else { + /* First selected loop. Set edge visible, overwriting any unselected loop. */ + GPU_indexbuf_set_line_verts(&data->elb, e_index, ml_index, ml_index_other); + } + } + else { + /* Set these unselected loop only if this edge has no other selected loop. */ + if (!BLI_BITMAP_TEST(data->select_map, e_index)) { + GPU_indexbuf_set_line_verts(&data->elb, e_index, ml_index, ml_index_other); + } + } + } + else { + GPU_indexbuf_set_line_restart(&data->elb, e_index); + } + } +} + +static void extract_lines_paint_mask_finish(const MeshRenderData *UNUSED(mr), + struct MeshBatchCache *UNUSED(cache), + void *buf, + void *_data) +{ + MeshExtract_LinePaintMask_Data *data = static_cast<MeshExtract_LinePaintMask_Data *>(_data); + GPUIndexBuf *ibo = static_cast<GPUIndexBuf *>(buf); + GPU_indexbuf_build_in_place(&data->elb, ibo); + MEM_freeN(data); +} + +/** \} */ + +constexpr MeshExtract create_extractor_lines_paint_mask() +{ + MeshExtract extractor = {0}; + extractor.init = extract_lines_paint_mask_init; + extractor.iter_poly_mesh = extract_lines_paint_mask_iter_poly_mesh; + extractor.finish = extract_lines_paint_mask_finish; + extractor.data_type = MR_DATA_NONE; + extractor.use_threading = false; + extractor.mesh_buffer_offset = offsetof(MeshBufferCache, ibo.lines_paint_mask); + return extractor; +} + +} // namespace blender::draw + +extern "C" { +const MeshExtract extract_lines_paint_mask = blender::draw::create_extractor_lines_paint_mask(); +} + +/** \} */ diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_points.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_points.cc index d749a3ab8d1..9220198d799 100644 --- a/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_points.cc +++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_points.cc @@ -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; } diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_tris.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_tris.cc index 688f04450e9..aab9ae8c228 100644 --- a/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_tris.cc +++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_tris.cc @@ -137,7 +137,7 @@ static void extract_tris_finish(const MeshRenderData *mr, for (int i = 0; i < mr->mat_len; 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 (mbc_final->tris_per_mat[i] == NULL) { + if (mbc_final->tris_per_mat[i] == nullptr) { mbc_final->tris_per_mat[i] = GPU_indexbuf_calloc(); } /* Multiply by 3 because these are triangle indices. */ diff --git a/source/blender/editors/interface/interface_widgets.c b/source/blender/editors/interface/interface_widgets.c index 8b861fab418..fe6a8b0d1a6 100644 --- a/source/blender/editors/interface/interface_widgets.c +++ b/source/blender/editors/interface/interface_widgets.c @@ -1941,7 +1941,7 @@ static bool widget_draw_text_underline_calc_position(const char *UNUSED(str), const rcti *glyph_step_bounds, const int UNUSED(glyph_advance_x), const rctf *glyph_bounds, - const int glyph_bearing[2], + const int UNUSED(glyph_bearing[2]), void *user_data) { struct UnderlineData *ul_data = user_data; diff --git a/source/blender/editors/mesh/editmesh_undo.c b/source/blender/editors/mesh/editmesh_undo.c index 112de68b52c..274f4cdbb6c 100644 --- a/source/blender/editors/mesh/editmesh_undo.c +++ b/source/blender/editors/mesh/editmesh_undo.c @@ -626,7 +626,7 @@ static void *undomesh_from_editmesh(UndoMesh *um, BMEditMesh *em, Key *key, Undo # ifdef USE_ARRAY_STORE_THREAD if (um_arraystore.task_pool == NULL) { - um_arraystore.task_pool = BLI_task_pool_create_background(NULL, TASK_PRIORITY_LOW); + um_arraystore.task_pool = BLI_task_pool_create_background(NULL, TASK_PRIORITY_LOW, true); } struct UMArrayData *um_data = MEM_mallocN(sizeof(*um_data), __func__); diff --git a/source/blender/editors/object/object_add.c b/source/blender/editors/object/object_add.c index ef500be0133..12aaa9c2d9f 100644 --- a/source/blender/editors/object/object_add.c +++ b/source/blender/editors/object/object_add.c @@ -3483,7 +3483,17 @@ static int object_add_named_exec(bContext *C, wmOperator *op) } /* prepare dupli */ - basen = object_add_duplicate_internal(bmain, scene, view_layer, ob, dupflag, 0); + basen = object_add_duplicate_internal( + bmain, + scene, + view_layer, + ob, + dupflag, + /* Sub-process flag because the new-ID remapping (#BKE_libblock_relink_to_newid()) in this + * function will only work if the object is already linked in the view layer, which is not + * the case here. So we have to do the new-ID relinking ourselves (#copy_object_set_idnew()). + */ + LIB_ID_DUPLICATE_IS_SUBPROCESS); if (basen == NULL) { BKE_report(op->reports, RPT_ERROR, "Object could not be duplicated"); diff --git a/source/blender/editors/render/render_opengl.c b/source/blender/editors/render/render_opengl.c index 48f937fb4ec..cfc07de3f6c 100644 --- a/source/blender/editors/render/render_opengl.c +++ b/source/blender/editors/render/render_opengl.c @@ -859,7 +859,7 @@ static bool screen_opengl_render_init(bContext *C, wmOperator *op) oglrender->task_pool = BLI_task_pool_create_background_serial(oglrender, TASK_PRIORITY_LOW); } else { - oglrender->task_pool = BLI_task_pool_create(oglrender, TASK_PRIORITY_LOW); + oglrender->task_pool = BLI_task_pool_create(oglrender, TASK_PRIORITY_LOW, TASK_ISOLATION_ON); } oglrender->pool_ok = true; BLI_spin_init(&oglrender->reports_lock); diff --git a/source/blender/editors/sculpt_paint/paint_image_proj.c b/source/blender/editors/sculpt_paint/paint_image_proj.c index 530689ce049..f03eeeb3c75 100644 --- a/source/blender/editors/sculpt_paint/paint_image_proj.c +++ b/source/blender/editors/sculpt_paint/paint_image_proj.c @@ -5571,7 +5571,7 @@ static bool project_paint_op(void *state, const float lastpos[2], const float po } if (ps->thread_tot > 1) { - task_pool = BLI_task_pool_create_suspended(NULL, TASK_PRIORITY_HIGH); + task_pool = BLI_task_pool_create_suspended(NULL, TASK_PRIORITY_HIGH, TASK_ISOLATION_ON); } image_pool = BKE_image_pool_new(); diff --git a/source/blender/editors/space_clip/clip_editor.c b/source/blender/editors/space_clip/clip_editor.c index 2da13646a8b..9aa6a993c13 100644 --- a/source/blender/editors/space_clip/clip_editor.c +++ b/source/blender/editors/space_clip/clip_editor.c @@ -904,7 +904,7 @@ static void start_prefetch_threads(MovieClip *clip, queue.do_update = do_update; queue.progress = progress; - TaskPool *task_pool = BLI_task_pool_create(&queue, TASK_PRIORITY_LOW); + TaskPool *task_pool = BLI_task_pool_create(&queue, TASK_PRIORITY_LOW, TASK_ISOLATION_ON); for (int i = 0; i < tot_thread; i++) { BLI_task_pool_push(task_pool, prefetch_task_func, clip, false, NULL); } diff --git a/source/blender/editors/space_clip/clip_ops.c b/source/blender/editors/space_clip/clip_ops.c index 0c54a042a1a..cd60ebf3031 100644 --- a/source/blender/editors/space_clip/clip_ops.c +++ b/source/blender/editors/space_clip/clip_ops.c @@ -1431,7 +1431,7 @@ static void do_sequence_proxy(void *pjv, queue.do_update = do_update; queue.progress = progress; - TaskPool *task_pool = BLI_task_pool_create(&queue, TASK_PRIORITY_LOW); + TaskPool *task_pool = BLI_task_pool_create(&queue, TASK_PRIORITY_LOW, TASK_ISOLATION_ON); handles = MEM_callocN(sizeof(ProxyThread) * tot_thread, "proxy threaded handles"); for (int i = 0; i < tot_thread; i++) { ProxyThread *handle = &handles[i]; diff --git a/source/blender/editors/space_file/filelist.c b/source/blender/editors/space_file/filelist.c index 37a32164cfc..8ea44e5c3ee 100644 --- a/source/blender/editors/space_file/filelist.c +++ b/source/blender/editors/space_file/filelist.c @@ -1553,7 +1553,8 @@ static void filelist_cache_preview_freef(TaskPool *__restrict UNUSED(pool), void static void filelist_cache_preview_ensure_running(FileListEntryCache *cache) { if (!cache->previews_pool) { - cache->previews_pool = BLI_task_pool_create_background(cache, TASK_PRIORITY_LOW); + cache->previews_pool = BLI_task_pool_create_background( + cache, TASK_PRIORITY_LOW, TASK_ISOLATION_ON); cache->previews_done = BLI_thread_queue_init(); IMB_thumb_locks_acquire(); diff --git a/source/blender/editors/space_graph/graph_select.c b/source/blender/editors/space_graph/graph_select.c index 4ab4ef518fb..b9c235873a7 100644 --- a/source/blender/editors/space_graph/graph_select.c +++ b/source/blender/editors/space_graph/graph_select.c @@ -512,16 +512,19 @@ static rctf initialize_box_select_coords(const bAnimContext *ac, const rctf *rec return rectf; } -static ListBase initialize_box_select_anim_data(const SpaceGraph *sipo, bAnimContext *ac) +static int initialize_animdata_selection_filter(const SpaceGraph *sipo) { - ListBase anim_data = {NULL, NULL}; - int filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_NODUPLIS); if (sipo->flag & SIPO_SELCUVERTSONLY) { filter |= ANIMFILTER_FOREDIT | ANIMFILTER_SELEDIT; } - ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype); + return filter; +} +static ListBase initialize_box_select_anim_data(const int filter, bAnimContext *ac) +{ + ListBase anim_data = {NULL, NULL}; + ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype); return anim_data; } @@ -573,8 +576,11 @@ static void initialize_box_select_key_editing_data(const SpaceGraph *sipo, * which means that they may be inadvertently moved as well. However, incl_handles overrides * this, and allow handles to be considered independently too. * Also, for convenience, handles should get same status as keyframe (if it was within bounds). + * + * This function returns true if there was any change in the selection of a key (selecting or + * deselecting any key returns true, otherwise it returns false). */ -static void box_select_graphkeys(bAnimContext *ac, +static bool box_select_graphkeys(bAnimContext *ac, const rctf *rectf_view, short mode, short selectmode, @@ -583,7 +589,8 @@ static void box_select_graphkeys(bAnimContext *ac, { const rctf rectf = initialize_box_select_coords(ac, rectf_view); SpaceGraph *sipo = (SpaceGraph *)ac->sl; - ListBase anim_data = initialize_box_select_anim_data(sipo, ac); + const int filter = initialize_animdata_selection_filter(sipo); + ListBase anim_data = initialize_box_select_anim_data(filter, ac); rctf scaled_rectf; KeyframeEditData ked; int mapping_flag; @@ -597,6 +604,9 @@ static void box_select_graphkeys(bAnimContext *ac, /* Try selecting the keyframes. */ bAnimListElem *ale = NULL; + /* This variable will be set to true if any key is selected or deselected. */ + bool any_key_selection_changed = false; + /* First loop over data, doing box select. try selecting keys only. */ for (ale = anim_data.first; ale; ale = ale->next) { AnimData *adt = ANIM_nla_mapping_get(ac, ale); @@ -634,7 +644,7 @@ static void box_select_graphkeys(bAnimContext *ac, if (ANIM_fcurve_keyframes_loop(&ked, fcu, NULL, ok_cb, NULL)) { /* select keyframes that are in the appropriate places */ ANIM_fcurve_keyframes_loop(&ked, fcu, ok_cb, select_cb, NULL); - + any_key_selection_changed = true; /* Only change selection of channel when the visibility of keyframes * doesn't depend on this. */ if ((sipo->flag & SIPO_SELCUVERTSONLY) == 0) { @@ -653,6 +663,125 @@ static void box_select_graphkeys(bAnimContext *ac, /* Cleanup. */ ANIM_animdata_freelist(&anim_data); + + return any_key_selection_changed; +} + +/* This function is used to set all the keyframes of a given curve as selectable + * by the "select_cb" function inside of "box_select_graphcurves". + */ +static short ok_bezier_always_ok(KeyframeEditData *UNUSED(ked), BezTriple *UNUSED(bezt)) +{ + return KEYFRAME_OK_KEY | KEYFRAME_OK_H1 | KEYFRAME_OK_H2; +} + +/* Checks whether the given rectangle intersects the given fcurve's calculated curve (i.e. not + * only keyframes, but also all the interpolated values). This is done by sampling the curve at + * different points between the xmin and the xmax of the rectangle. + */ +static bool rectf_curve_intersection( + const float offset, const float unit_scale, const rctf *rectf, AnimData *adt, FCurve *fcu) +{ + /* 30 sampling points. This worked well in tests. */ + const float num_steps = 30.0f; + const float step = (rectf->xmax - rectf->xmin) / num_steps; + + /* Remap the range at which to evaluate the fcurves. This enables us to avoid remapping + * the keys themselves. */ + const float mapped_max = BKE_nla_tweakedit_remap(adt, rectf->xmax, NLATIME_CONVERT_UNMAP); + const float mapped_min = BKE_nla_tweakedit_remap(adt, rectf->xmin, NLATIME_CONVERT_UNMAP); + const float eval_step = (mapped_max - mapped_min) / num_steps; + + float x = rectf->xmin; + float eval_x = mapped_min; + /* Sample points on the given fcurve in the interval defined by the + * mapped_min and mapped_max of the selected rectangle. + * For each point, check if it is inside of the selection box. If it is, then select + * all the keyframes of the curve, the curve, and stop the loop. + */ + while (x < rectf->xmax) { + const float fcurve_y = (evaluate_fcurve(fcu, eval_x) + offset) * unit_scale; + /* Since rectf->xmin <= x < rectf->xmax is always true, there is no need to keep comparing the + * X-coordinate to the rectangle in every iteration. Therefore we do the comparisons manually + * instead of using BLI_rctf_isect_pt_v(rectf, current_point). + */ + if (rectf->ymin <= fcurve_y && fcurve_y <= rectf->ymax) { + return true; + } + x += step; + eval_x += eval_step; + } + return false; +} + +/* Perform a box selection of the curves themselves. This means this function tries + * to select a curve by sampling it at various points instead of trying to select the + * keyframes directly. + * The selection actions done to a curve are actually done on all the keyframes of the curve. + * Note: This function is only called if no keyframe is in the seletion area. + */ +static void box_select_graphcurves(bAnimContext *ac, + const rctf *rectf_view, + const short mode, + const short selectmode, + const bool incl_handles, + void *data) +{ + const SpaceGraph *sipo = (SpaceGraph *)ac->sl; + const int filter = initialize_animdata_selection_filter(sipo); + ListBase anim_data = initialize_box_select_anim_data(filter, ac); + rctf scaled_rectf; + KeyframeEditData ked; + int mapping_flag; + initialize_box_select_key_editing_data( + sipo, incl_handles, mode, ac, data, &scaled_rectf, &ked, &mapping_flag); + + FCurve *last_selected_curve = NULL; + + /* Go through all the curves and try selecting them. This function is only called + * if no keyframe is in the seletion area, so we only have to check if the curve + * intersects the area in order to check if the selection/deselection must happen. + */ + + LISTBASE_FOREACH (bAnimListElem *, ale, &anim_data) { + AnimData *adt = ANIM_nla_mapping_get(ac, ale); + FCurve *fcu = (FCurve *)ale->key_data; + float offset; + const float unit_scale = ANIM_unit_mapping_get_factor( + ac->scene, ale->id, fcu, mapping_flag, &offset); + + const rctf rectf = initialize_box_select_coords(ac, rectf_view); + + /* scaled_rectf is declared at the top of the block because it is required by the + * initialize_box_select_key_editing_data function (which does + * data_xxx->rectf_scaled = scaled_rectf). The below assignment therefore modifies the + * data we use to iterate over the curves (ked). + */ + scaled_rectf.xmin = rectf.xmin; + scaled_rectf.xmax = rectf.xmax; + scaled_rectf.ymin = rectf.ymin / unit_scale - offset; + scaled_rectf.ymax = rectf.ymax / unit_scale - offset; + + const KeyframeEditFunc select_cb = ANIM_editkeyframes_select(selectmode); + if (rectf_curve_intersection(offset, unit_scale, &rectf, adt, fcu)) { + if ((selectmode & SELECT_ADD) || (selectmode & SELECT_REPLACE)) { + fcu->flag |= FCURVE_SELECTED; + last_selected_curve = fcu; + } + else { + fcu->flag &= ~FCURVE_SELECTED; + } + ANIM_fcurve_keyframes_loop(&ked, fcu, ok_bezier_always_ok, select_cb, NULL); + } + } + + /* Make sure that one of the selected curves is active in the end. */ + if (last_selected_curve != NULL) { + ANIM_set_active_channel( + ac, ac->data, ac->datatype, filter, last_selected_curve, ANIMTYPE_FCURVE); + } + + ANIM_animdata_freelist(&anim_data); } /* ------------------- */ @@ -726,7 +855,12 @@ static int graphkeys_box_select_exec(bContext *C, wmOperator *op) BLI_rctf_rcti_copy(&rect_fl, &rect); /* Apply box_select action. */ - box_select_graphkeys(&ac, &rect_fl, mode, selectmode, incl_handles, NULL); + const bool any_key_selection_changed = box_select_graphkeys( + &ac, &rect_fl, mode, selectmode, incl_handles, NULL); + const bool use_curve_selection = RNA_boolean_get(op->ptr, "use_curve_selection"); + if (use_curve_selection && !any_key_selection_changed) { + box_select_graphcurves(&ac, &rect_fl, mode, selectmode, incl_handles, NULL); + } /* Send notifier that keyframe selection has changed. */ WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_SELECTED, NULL); @@ -767,6 +901,14 @@ void GRAPH_OT_select_box(wmOperatorType *ot) ot->srna, "tweak", 0, "Tweak", "Operator has been activated using a tweak event"); RNA_def_property_flag(prop, PROP_SKIP_SAVE); + prop = RNA_def_boolean( + ot->srna, + "use_curve_selection", + 1, + "Select Curves", + "Allow selecting all the keyframes of a curve by selecting the calculated fcurve"); + RNA_def_property_flag(prop, PROP_SKIP_SAVE); + WM_operator_properties_gesture_box(ot); WM_operator_properties_select_operation_simple(ot); } @@ -815,7 +957,13 @@ static int graphkeys_lassoselect_exec(bContext *C, wmOperator *op) BLI_rctf_rcti_copy(&rect_fl, &rect); /* Apply box_select action. */ - box_select_graphkeys(&ac, &rect_fl, BEZT_OK_REGION_LASSO, selectmode, incl_handles, &data_lasso); + const bool any_key_selection_changed = box_select_graphkeys( + &ac, &rect_fl, BEZT_OK_REGION_LASSO, selectmode, incl_handles, &data_lasso); + const bool use_curve_selection = RNA_boolean_get(op->ptr, "use_curve_selection"); + if (use_curve_selection && !any_key_selection_changed) { + box_select_graphcurves( + &ac, &rect_fl, BEZT_OK_REGION_LASSO, selectmode, incl_handles, &data_lasso); + } MEM_freeN((void *)data_lasso.mcoords); @@ -845,6 +993,13 @@ void GRAPH_OT_select_lasso(wmOperatorType *ot) /* Properties. */ WM_operator_properties_gesture_lasso(ot); WM_operator_properties_select_operation_simple(ot); + PropertyRNA *prop = RNA_def_boolean( + ot->srna, + "use_curve_selection", + 1, + "Select Curves", + "Allow selecting all the keyframes of a curve by selecting the curve itself"); + RNA_def_property_flag(prop, PROP_SKIP_SAVE); } /* ------------------- */ @@ -894,7 +1049,12 @@ static int graph_circle_select_exec(bContext *C, wmOperator *op) } /* Apply box_select action. */ - box_select_graphkeys(&ac, &rect_fl, BEZT_OK_REGION_CIRCLE, selectmode, incl_handles, &data); + const bool any_key_selection_changed = box_select_graphkeys( + &ac, &rect_fl, BEZT_OK_REGION_CIRCLE, selectmode, incl_handles, &data); + const bool use_curve_selection = RNA_boolean_get(op->ptr, "use_curve_selection"); + if (use_curve_selection && !any_key_selection_changed) { + box_select_graphcurves(&ac, &rect_fl, BEZT_OK_REGION_CIRCLE, selectmode, incl_handles, &data); + } /* Send notifier that keyframe selection has changed. */ WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_SELECTED, NULL); @@ -920,6 +1080,13 @@ void GRAPH_OT_select_circle(wmOperatorType *ot) /* properties */ WM_operator_properties_gesture_circle(ot); WM_operator_properties_select_operation_simple(ot); + PropertyRNA *prop = RNA_def_boolean( + ot->srna, + "use_curve_selection", + 1, + "Select Curves", + "Allow selecting all the keyframes of a curve by selecting the curve itself"); + RNA_def_property_flag(prop, PROP_SKIP_SAVE); } /* ******************** Column Select Operator **************************** */ diff --git a/source/blender/gpencil_modifiers/intern/lineart/MOD_lineart.h b/source/blender/gpencil_modifiers/intern/lineart/MOD_lineart.h index 712e92d017d..861085d3e16 100644 --- a/source/blender/gpencil_modifiers/intern/lineart/MOD_lineart.h +++ b/source/blender/gpencil_modifiers/intern/lineart/MOD_lineart.h @@ -23,6 +23,7 @@ #pragma once +#include "BLI_linklist.h" #include "BLI_listbase.h" #include "BLI_math.h" /* Needed here for inline functions. */ #include "BLI_threads.h" @@ -226,6 +227,7 @@ typedef struct LineartRenderBuffer { int tile_count_x, tile_count_y; double width_per_tile, height_per_tile; double view_projection[4][4]; + double view[4][4]; struct LineartBoundingArea *initial_bounding_areas; unsigned int bounding_area_count; @@ -310,7 +312,7 @@ typedef struct LineartRenderBuffer { #define DBL_TRIANGLE_LIM 1e-8 #define DBL_EDGE_LIM 1e-9 -#define LRT_MEMORY_POOL_64MB (1 << 26) +#define LRT_MEMORY_POOL_1MB (1 << 20) typedef enum eLineartTriangleFlags { LRT_CULL_DONT_CARE = 0, @@ -343,6 +345,41 @@ typedef struct LineartRenderTaskInfo { } LineartRenderTaskInfo; +struct BMesh; + +typedef struct LineartObjectInfo { + struct LineartObjectInfo *next; + struct Object *original_ob; + struct Mesh *original_me; + double model_view_proj[4][4]; + double model_view[4][4]; + double normal[4][4]; + LineartElementLinkNode *v_reln; + int usage; + int global_i_offset; + + bool free_use_mesh; + + /* Threads will add lines inside here, when all threads are done, we combine those into the + * ones in LineartRenderBuffer. */ + ListBase contour; + ListBase intersection; + ListBase crease; + ListBase material; + ListBase edge_mark; + ListBase floating; + +} LineartObjectInfo; + +typedef struct LineartObjectLoadTaskInfo { + struct LineartRenderBuffer *rb; + struct Depsgraph *dg; + /* LinkNode styled list */ + LineartObjectInfo *pending; + /* Used to spread the load across several threads. This can not overflow. */ + long unsigned int total_faces; +} LineartObjectLoadTaskInfo; + /** * Bounding area diagram: * \code{.txt} diff --git a/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c b/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c index 82ca1131cc1..98b24be0d54 100644 --- a/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c +++ b/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c @@ -29,6 +29,8 @@ #include "BLI_task.h" #include "BLI_utildefines.h" +#include "PIL_time.h" + #include "BKE_camera.h" #include "BKE_collection.h" #include "BKE_customdata.h" @@ -480,7 +482,7 @@ static void lineart_main_occlusion_begin(LineartRenderBuffer *rb) rb->material.last = rb->material.first; rb->edge_mark.last = rb->edge_mark.first; - TaskPool *tp = BLI_task_pool_create(NULL, TASK_PRIORITY_HIGH); + TaskPool *tp = BLI_task_pool_create(NULL, TASK_PRIORITY_HIGH, TASK_ISOLATION_ON); for (i = 0; i < thread_count; i++) { rti[i].thread_id = i; @@ -1478,6 +1480,49 @@ static void lineart_add_edge_to_list(LineartRenderBuffer *rb, LineartEdge *e) } } +static void lineart_add_edge_to_list_thread(LineartObjectInfo *obi, LineartEdge *e) +{ + +#define LRT_ASSIGN_EDGE(name) \ + lineart_prepend_edge_direct(&obi->name.first, e); \ + if (!obi->name.last) { \ + obi->name.last = e; \ + } + switch (e->flags) { + case LRT_EDGE_FLAG_CONTOUR: + LRT_ASSIGN_EDGE(contour); + break; + case LRT_EDGE_FLAG_CREASE: + LRT_ASSIGN_EDGE(crease); + break; + case LRT_EDGE_FLAG_MATERIAL: + LRT_ASSIGN_EDGE(material); + break; + case LRT_EDGE_FLAG_EDGE_MARK: + LRT_ASSIGN_EDGE(edge_mark); + break; + case LRT_EDGE_FLAG_INTERSECTION: + LRT_ASSIGN_EDGE(intersection); + break; + } +#undef LRT_ASSIGN_EDGE +} + +static void lineart_finalize_object_edge_list(LineartRenderBuffer *rb, LineartObjectInfo *obi) +{ +#define LRT_OBI_TO_RB(name) \ + if (obi->name.last) { \ + ((LineartEdge *)obi->name.last)->next = rb->name.first; \ + rb->name.first = obi->name.first; \ + } + LRT_OBI_TO_RB(contour); + LRT_OBI_TO_RB(crease); + LRT_OBI_TO_RB(material); + LRT_OBI_TO_RB(edge_mark); + LRT_OBI_TO_RB(intersection); +#undef LRT_OBI_TO_RB +} + static void lineart_triangle_adjacent_assign(LineartTriangle *tri, LineartTriangleAdjacent *ta, LineartEdge *e) @@ -1493,13 +1538,7 @@ static void lineart_triangle_adjacent_assign(LineartTriangle *tri, } } -static void lineart_geometry_object_load(Depsgraph *dg, - Object *ob, - double (*mv_mat)[4], - double (*mvp_mat)[4], - LineartRenderBuffer *rb, - int override_usage, - int *global_vindex) +static void lineart_geometry_object_load(LineartObjectInfo *obi, LineartRenderBuffer *rb) { BMesh *bm; BMVert *v; @@ -1507,250 +1546,244 @@ static void lineart_geometry_object_load(Depsgraph *dg, BMEdge *e; BMLoop *loop; LineartEdge *la_e; + LineartEdgeSegment *la_s; LineartTriangle *tri; LineartTriangleAdjacent *orta; - double new_mvp[4][4], new_mv[4][4], normal[4][4]; - float imat[4][4]; + double(*model_view_proj)[4] = obi->model_view_proj, (*model_view)[4] = obi->model_view, + (*normal)[4] = obi->normal; LineartElementLinkNode *eln; LineartVert *orv; LineartEdge *o_la_e; + LineartEdgeSegment *o_la_s; LineartTriangle *ort; Object *orig_ob; int CanFindFreestyle = 0; - int i, global_i = (*global_vindex); - Mesh *use_mesh; + int i; float use_crease = 0; - int usage = override_usage ? override_usage : ob->lineart.usage; + int usage = obi->usage; -#define LRT_MESH_FINISH \ - BM_mesh_free(bm); \ - if (ob->type != OB_MESH) { \ - BKE_mesh_free(use_mesh); \ - MEM_freeN(use_mesh); \ + if (obi->original_me->edit_mesh) { + /* Do not use edit_mesh directly because we will modify it, so create a copy. */ + bm = BM_mesh_copy(obi->original_me->edit_mesh->bm); + } + else { + const BMAllocTemplate allocsize = BMALLOC_TEMPLATE_FROM_ME(((Mesh *)(obi->original_me))); + bm = BM_mesh_create(&allocsize, + &((struct BMeshCreateParams){ + .use_toolflags = true, + })); + BM_mesh_bm_from_me(bm, + obi->original_me, + &((struct BMeshFromMeshParams){ + .calc_face_normal = true, + })); } - if (usage == OBJECT_LRT_EXCLUDE) { - return; + if (obi->free_use_mesh) { + BKE_mesh_free(obi->original_me); + MEM_freeN(obi->original_me); } - if (ELEM(ob->type, OB_MESH, OB_MBALL, OB_CURVE, OB_SURF, OB_FONT)) { + if (rb->remove_doubles) { + BMEditMesh *em = BKE_editmesh_create(bm, false); + BMOperator findop, weldop; - if (ob->type == OB_MESH) { - use_mesh = DEG_get_evaluated_object(dg, ob)->data; - } - else { - use_mesh = BKE_mesh_new_from_object(NULL, ob, false, false); - } + /* See bmesh_opdefines.c and bmesh_operators.c for op names and argument formatting. */ + BMO_op_initf(bm, &findop, BMO_FLAG_DEFAULTS, "find_doubles verts=%av dist=%f", 0.0001); - /* In case we can not get any mesh geometry data from the object */ - if (!use_mesh) { - return; - } + BMO_op_exec(bm, &findop); - /* First we need to prepare the matrix used for transforming this specific object. */ - mul_m4db_m4db_m4fl_uniq(new_mvp, mvp_mat, ob->obmat); - mul_m4db_m4db_m4fl_uniq(new_mv, mv_mat, ob->obmat); + /* Weld the vertices. */ + BMO_op_init(bm, &weldop, BMO_FLAG_DEFAULTS, "weld_verts"); + BMO_slot_copy(&findop, slots_out, "targetmap.out", &weldop, slots_in, "targetmap"); + BMO_op_exec(bm, &weldop); - invert_m4_m4(imat, ob->obmat); - transpose_m4(imat); - copy_m4d_m4(normal, imat); + BMO_op_finish(bm, &findop); + BMO_op_finish(bm, &weldop); - if (use_mesh->edit_mesh) { - /* Do not use edit_mesh directly because we will modify it, so create a copy. */ - bm = BM_mesh_copy(use_mesh->edit_mesh->bm); - } - else { - const BMAllocTemplate allocsize = BMALLOC_TEMPLATE_FROM_ME(((Mesh *)(use_mesh))); - bm = BM_mesh_create(&allocsize, - &((struct BMeshCreateParams){ - .use_toolflags = true, - })); - BM_mesh_bm_from_me(bm, - use_mesh, - &((struct BMeshFromMeshParams){ - .calc_face_normal = true, - })); - } + MEM_freeN(em); + } - if (rb->remove_doubles) { - BMEditMesh *em = BKE_editmesh_create(bm, false); - BMOperator findop, weldop; + BM_mesh_elem_hflag_disable_all(bm, BM_FACE | BM_EDGE, BM_ELEM_TAG, false); + BM_mesh_triangulate( + bm, MOD_TRIANGULATE_QUAD_FIXED, MOD_TRIANGULATE_NGON_BEAUTY, 4, false, NULL, NULL, NULL); + BM_mesh_normals_update(bm); + BM_mesh_elem_table_ensure(bm, BM_VERT | BM_EDGE | BM_FACE); + BM_mesh_elem_index_ensure(bm, BM_VERT | BM_EDGE | BM_FACE); - /* See bmesh_opdefines.c and bmesh_operators.c for op names and argument formatting. */ - BMO_op_initf(bm, &findop, BMO_FLAG_DEFAULTS, "find_doubles verts=%av dist=%f", 0.0001); + if (CustomData_has_layer(&bm->edata, CD_FREESTYLE_EDGE)) { + CanFindFreestyle = 1; + } - BMO_op_exec(bm, &findop); + /* Only allocate memory for verts and tris as we don't know how many lines we will generate + * yet. */ + orv = lineart_mem_acquire_thread(&rb->render_data_pool, sizeof(LineartVert) * bm->totvert); + ort = lineart_mem_acquire_thread(&rb->render_data_pool, bm->totface * rb->triangle_size); - /* Weld the vertices. */ - BMO_op_init(bm, &weldop, BMO_FLAG_DEFAULTS, "weld_verts"); - BMO_slot_copy(&findop, slots_out, "targetmap.out", &weldop, slots_in, "targetmap"); - BMO_op_exec(bm, &weldop); + orig_ob = obi->original_ob; - BMO_op_finish(bm, &findop); - BMO_op_finish(bm, &weldop); + BLI_spin_lock(&rb->lock_task); + eln = lineart_list_append_pointer_pool_sized_thread( + &rb->vertex_buffer_pointers, &rb->render_data_pool, orv, sizeof(LineartElementLinkNode)); + BLI_spin_unlock(&rb->lock_task); - MEM_freeN(em); - } + eln->element_count = bm->totvert; + eln->object_ref = orig_ob; + obi->v_reln = eln; - BM_mesh_elem_hflag_disable_all(bm, BM_FACE | BM_EDGE, BM_ELEM_TAG, false); - BM_mesh_triangulate( - bm, MOD_TRIANGULATE_QUAD_FIXED, MOD_TRIANGULATE_NGON_BEAUTY, 4, false, NULL, NULL, NULL); - BM_mesh_normals_update(bm); - BM_mesh_elem_table_ensure(bm, BM_VERT | BM_EDGE | BM_FACE); - BM_mesh_elem_index_ensure(bm, BM_VERT | BM_EDGE | BM_FACE); + if (orig_ob->lineart.flags & OBJECT_LRT_OWN_CREASE) { + use_crease = cosf(M_PI - orig_ob->lineart.crease_threshold); + } + else { + use_crease = rb->crease_threshold; + } - if (CustomData_has_layer(&bm->edata, CD_FREESTYLE_EDGE)) { - CanFindFreestyle = 1; - } + /* FIXME(Yiming): Hack for getting clean 3D text, the seam that extruded text object creates + * erroneous detection on creases. Future configuration should allow options. */ + if (orig_ob->type == OB_FONT) { + eln->flags |= LRT_ELEMENT_BORDER_ONLY; + } - /* Only allocate memory for verts and tris as we don't know how many lines we will generate - * yet. */ - orv = lineart_mem_acquire(&rb->render_data_pool, sizeof(LineartVert) * bm->totvert); - ort = lineart_mem_acquire(&rb->render_data_pool, bm->totface * rb->triangle_size); + BLI_spin_lock(&rb->lock_task); + eln = lineart_list_append_pointer_pool_sized_thread( + &rb->triangle_buffer_pointers, &rb->render_data_pool, ort, sizeof(LineartElementLinkNode)); + BLI_spin_unlock(&rb->lock_task); - orig_ob = ob->id.orig_id ? (Object *)ob->id.orig_id : ob; + eln->element_count = bm->totface; + eln->object_ref = orig_ob; + eln->flags |= (usage == OBJECT_LRT_NO_INTERSECTION ? LRT_ELEMENT_NO_INTERSECTION : 0); - eln = lineart_list_append_pointer_pool_sized( - &rb->vertex_buffer_pointers, &rb->render_data_pool, orv, sizeof(LineartElementLinkNode)); - eln->element_count = bm->totvert; - eln->object_ref = orig_ob; + /* Note this memory is not from pool, will be deleted after culling. */ + orta = MEM_callocN(sizeof(LineartTriangleAdjacent) * bm->totface, "LineartTriangleAdjacent"); + /* Link is minimal so we use pool anyway. */ + BLI_spin_lock(&rb->lock_task); + lineart_list_append_pointer_pool_thread( + &rb->triangle_adjacent_pointers, &rb->render_data_pool, orta); + BLI_spin_unlock(&rb->lock_task); - if (ob->lineart.flags & OBJECT_LRT_OWN_CREASE) { - use_crease = cosf(M_PI - ob->lineart.crease_threshold); - } - else { - use_crease = rb->crease_threshold; - } - - /* FIXME(Yiming): Hack for getting clean 3D text, the seam that extruded text object creates - * erroneous detection on creases. Future configuration should allow options. */ - if (ob->type == OB_FONT) { - eln->flags |= LRT_ELEMENT_BORDER_ONLY; - } - - eln = lineart_list_append_pointer_pool_sized( - &rb->triangle_buffer_pointers, &rb->render_data_pool, ort, sizeof(LineartElementLinkNode)); - eln->element_count = bm->totface; - eln->object_ref = orig_ob; - eln->flags |= (usage == OBJECT_LRT_NO_INTERSECTION ? LRT_ELEMENT_NO_INTERSECTION : 0); - - /* Note this memory is not from pool, will be deleted after culling. */ - orta = MEM_callocN(sizeof(LineartTriangleAdjacent) * bm->totface, "LineartTriangleAdjacent"); - /* Link is minimal so we use pool anyway. */ - lineart_list_append_pointer_pool(&rb->triangle_adjacent_pointers, &rb->render_data_pool, orta); - - for (i = 0; i < bm->totvert; i++) { - v = BM_vert_at_index(bm, i); - lineart_vert_transform(v, i, orv, new_mv, new_mvp); - orv[i].index = i + global_i; - } - /* Register a global index increment. See #lineart_triangle_share_edge() and - * #lineart_main_load_geometries() for detailed. It's okay that global_vindex might eventually - * overflow, in such large scene it's virtually impossible for two vertex of the same numeric - * index to come close together. */ - (*global_vindex) += bm->totvert; - - tri = ort; - for (i = 0; i < bm->totface; i++) { - f = BM_face_at_index(bm, i); - - loop = f->l_first; - tri->v[0] = &orv[BM_elem_index_get(loop->v)]; - loop = loop->next; - tri->v[1] = &orv[BM_elem_index_get(loop->v)]; - loop = loop->next; - tri->v[2] = &orv[BM_elem_index_get(loop->v)]; - - /* Transparency bit assignment. */ - Material *mat = BKE_object_material_get(ob, f->mat_nr + 1); - tri->transparency_mask = ((mat && (mat->lineart.flags & LRT_MATERIAL_TRANSPARENCY_ENABLED)) ? - mat->lineart.transparency_mask : - 0); - - double gn[3]; - copy_v3db_v3fl(gn, f->no); - mul_v3_mat3_m4v3_db(tri->gn, normal, gn); - normalize_v3_db(tri->gn); - - if (usage == OBJECT_LRT_INTERSECTION_ONLY) { - tri->flags |= LRT_TRIANGLE_INTERSECTION_ONLY; - } - else if (ELEM(usage, OBJECT_LRT_NO_INTERSECTION, OBJECT_LRT_OCCLUSION_ONLY)) { - tri->flags |= LRT_TRIANGLE_NO_INTERSECTION; - } + for (i = 0; i < bm->totvert; i++) { + v = BM_vert_at_index(bm, i); + lineart_vert_transform(v, i, orv, model_view, model_view_proj); + orv[i].index = i; + } + /* Register a global index increment. See #lineart_triangle_share_edge() and + * #lineart_main_load_geometries() for detailed. It's okay that global_vindex might eventually + * overflow, in such large scene it's virtually impossible for two vertex of the same numeric + * index to come close together. */ + obi->global_i_offset = bm->totvert; - /* Re-use this field to refer to adjacent info, will be cleared after culling stage. */ - tri->intersecting_verts = (void *)&orta[i]; + tri = ort; + for (i = 0; i < bm->totface; i++) { + f = BM_face_at_index(bm, i); - tri = (LineartTriangle *)(((uchar *)tri) + rb->triangle_size); + loop = f->l_first; + tri->v[0] = &orv[BM_elem_index_get(loop->v)]; + loop = loop->next; + tri->v[1] = &orv[BM_elem_index_get(loop->v)]; + loop = loop->next; + tri->v[2] = &orv[BM_elem_index_get(loop->v)]; + + /* Transparency bit assignment. */ + Material *mat = BKE_object_material_get(orig_ob, f->mat_nr + 1); + tri->transparency_mask = ((mat && (mat->lineart.flags & LRT_MATERIAL_TRANSPARENCY_ENABLED)) ? + mat->lineart.transparency_mask : + 0); + + double gn[3]; + copy_v3db_v3fl(gn, f->no); + mul_v3_mat3_m4v3_db(tri->gn, normal, gn); + normalize_v3_db(tri->gn); + + if (usage == OBJECT_LRT_INTERSECTION_ONLY) { + tri->flags |= LRT_TRIANGLE_INTERSECTION_ONLY; } + else if (usage == OBJECT_LRT_NO_INTERSECTION || usage == OBJECT_LRT_OCCLUSION_ONLY) { + tri->flags |= LRT_TRIANGLE_NO_INTERSECTION; + } + + /* Re-use this field to refer to adjacent info, will be cleared after culling stage. */ + tri->intersecting_verts = (void *)&orta[i]; - /* Use BM_ELEM_TAG in f->head.hflag to store needed faces in the first iteration. */ + tri = (LineartTriangle *)(((uchar *)tri) + rb->triangle_size); + } - int allocate_la_e = 0; - for (i = 0; i < bm->totedge; i++) { - e = BM_edge_at_index(bm, i); + /* Use BM_ELEM_TAG in f->head.hflag to store needed faces in the first iteration. */ - /* Because e->head.hflag is char, so line type flags should not exceed positive 7 bits. */ - char eflag = lineart_identify_feature_line( - rb, e, ort, orv, use_crease, ob->type == OB_FONT, CanFindFreestyle, bm); - if (eflag) { - /* Only allocate for feature lines (instead of all lines) to save memory. */ - allocate_la_e++; - } - /* Here we just use bm's flag for when loading actual lines, then we don't need to call - * lineart_identify_feature_line() again, e->head.hflag deleted after loading anyway. Always - * set the flag, so hflag stays 0 for lines that are not feature lines. */ - e->head.hflag = eflag; + int allocate_la_e = 0; + for (i = 0; i < bm->totedge; i++) { + e = BM_edge_at_index(bm, i); + + /* Because e->head.hflag is char, so line type flags should not exceed positive 7 bits. */ + char eflag = lineart_identify_feature_line( + rb, e, ort, orv, use_crease, orig_ob->type == OB_FONT, CanFindFreestyle, bm); + if (eflag) { + /* Only allocate for feature lines (instead of all lines) to save memory. */ + allocate_la_e++; } + /* Here we just use bm's flag for when loading actual lines, then we don't need to call + * lineart_identify_feature_line() again, e->head.hflag deleted after loading anyway. Always + * set the flag, so hflag stays 0 for lines that are not feature lines. */ + e->head.hflag = eflag; + } - o_la_e = lineart_mem_acquire(&rb->render_data_pool, sizeof(LineartEdge) * allocate_la_e); - eln = lineart_list_append_pointer_pool_sized( - &rb->line_buffer_pointers, &rb->render_data_pool, o_la_e, sizeof(LineartElementLinkNode)); - eln->element_count = allocate_la_e; - eln->object_ref = orig_ob; + o_la_e = lineart_mem_acquire_thread(&rb->render_data_pool, sizeof(LineartEdge) * allocate_la_e); + o_la_s = lineart_mem_acquire_thread(&rb->render_data_pool, + sizeof(LineartEdgeSegment) * allocate_la_e); + BLI_spin_lock(&rb->lock_task); + eln = lineart_list_append_pointer_pool_sized_thread( + &rb->line_buffer_pointers, &rb->render_data_pool, o_la_e, sizeof(LineartElementLinkNode)); + BLI_spin_unlock(&rb->lock_task); + eln->element_count = allocate_la_e; + eln->object_ref = orig_ob; - la_e = o_la_e; - for (i = 0; i < bm->totedge; i++) { - e = BM_edge_at_index(bm, i); + la_e = o_la_e; + la_s = o_la_s; + for (i = 0; i < bm->totedge; i++) { + e = BM_edge_at_index(bm, i); - /* Not a feature line, so we skip. */ - if (!e->head.hflag) { - continue; - } + /* Not a feature line, so we skip. */ + if (!e->head.hflag) { + continue; + } - la_e->v1 = &orv[BM_elem_index_get(e->v1)]; - la_e->v2 = &orv[BM_elem_index_get(e->v2)]; - la_e->v1_obindex = la_e->v1->index - global_i; - la_e->v2_obindex = la_e->v2->index - global_i; - if (e->l) { - int findex = BM_elem_index_get(e->l->f); - la_e->t1 = lineart_triangle_from_index(rb, ort, findex); - lineart_triangle_adjacent_assign(la_e->t1, &orta[findex], la_e); - if (e->l->radial_next && e->l->radial_next != e->l) { - findex = BM_elem_index_get(e->l->radial_next->f); - la_e->t2 = lineart_triangle_from_index(rb, ort, findex); - lineart_triangle_adjacent_assign(la_e->t2, &orta[findex], la_e); - } + la_e->v1 = &orv[BM_elem_index_get(e->v1)]; + la_e->v2 = &orv[BM_elem_index_get(e->v2)]; + la_e->v1_obindex = la_e->v1->index; + la_e->v2_obindex = la_e->v2->index; + if (e->l) { + int findex = BM_elem_index_get(e->l->f); + la_e->t1 = lineart_triangle_from_index(rb, ort, findex); + lineart_triangle_adjacent_assign(la_e->t1, &orta[findex], la_e); + if (e->l->radial_next && e->l->radial_next != e->l) { + findex = BM_elem_index_get(e->l->radial_next->f); + la_e->t2 = lineart_triangle_from_index(rb, ort, findex); + lineart_triangle_adjacent_assign(la_e->t2, &orta[findex], la_e); } - la_e->flags = e->head.hflag; - la_e->object_ref = orig_ob; - - LineartEdgeSegment *es = lineart_mem_acquire(&rb->render_data_pool, - sizeof(LineartEdgeSegment)); - BLI_addtail(&la_e->segments, es); - if (ELEM(usage, OBJECT_LRT_INHERIT, OBJECT_LRT_INCLUDE, OBJECT_LRT_NO_INTERSECTION)) { - lineart_add_edge_to_list(rb, la_e); - } - - la_e++; + } + la_e->flags = e->head.hflag; + la_e->object_ref = orig_ob; + BLI_addtail(&la_e->segments, la_s); + if (usage == OBJECT_LRT_INHERIT || usage == OBJECT_LRT_INCLUDE || + usage == OBJECT_LRT_NO_INTERSECTION) { + lineart_add_edge_to_list_thread(obi, la_e); } - LRT_MESH_FINISH + la_e++; + la_s++; } -#undef LRT_MESH_FINISH + /* always free bm as it's a copy from before threading */ + BM_mesh_free(bm); +} + +static void lineart_object_load_worker(TaskPool *__restrict UNUSED(pool), + LineartObjectLoadTaskInfo *olti) +{ + LineartRenderBuffer *rb = olti->rb; + for (LineartObjectInfo *obi = olti->pending; obi; obi = obi->next) { + lineart_geometry_object_load(obi, rb); + } } static bool _lineart_object_not_in_source_collection(Collection *source, Object *ob) @@ -1830,6 +1863,24 @@ static int lineart_usage_check(Collection *c, Object *ob, LineartRenderBuffer *_ return OBJECT_LRT_INHERIT; } +static void lineart_geometry_load_assign_thread(LineartObjectLoadTaskInfo *olti_list, + LineartObjectInfo *obi, + int thread_count, + int this_face_count) +{ + LineartObjectLoadTaskInfo *use_olti = olti_list; + long unsigned int min_face = use_olti->total_faces; + for (int i = 0; i < thread_count; i++) { + if (olti_list[i].total_faces < min_face) { + min_face = olti_list[i].total_faces; + use_olti = &olti_list[i]; + } + } + use_olti->total_faces += this_face_count; + obi->next = use_olti->pending; + use_olti->pending = obi; +} + static void lineart_main_load_geometries( Depsgraph *depsgraph, Scene *scene, @@ -1845,6 +1896,12 @@ static void lineart_main_load_geometries( int fit = BKE_camera_sensor_fit(cam->sensor_fit, rb->w, rb->h); double asp = ((double)rb->w / (double)rb->h); + double t_start; + + if (G.debug_value == 4000) { + t_start = PIL_check_seconds_timer(); + } + if (cam->type == CAM_PERSP) { if (fit == CAMERA_SENSOR_FIT_VERT && asp > 1) { sensor *= asp; @@ -1866,6 +1923,7 @@ static void lineart_main_load_geometries( copy_m4_m4_db(rb->view_projection, proj); unit_m4_db(view); + copy_m4_m4_db(rb->view, view); BLI_listbase_clear(&rb->triangle_buffer_pointers); BLI_listbase_clear(&rb->vertex_buffer_pointers); @@ -1878,16 +1936,90 @@ static void lineart_main_load_geometries( flags |= DEG_ITER_OBJECT_FLAG_DUPLI; } - /* This is to serialize vertex index in the whole scene, so lineart_triangle_share_edge() can - * work properly from the lack of triangle adjacent info. */ - int global_i = 0; + int thread_count = rb->thread_count; + + /* This memory is in render buffer memory pool. so we don't need to free those after loading. */ + LineartObjectLoadTaskInfo *olti = lineart_mem_acquire( + &rb->render_data_pool, sizeof(LineartObjectLoadTaskInfo) * thread_count); DEG_OBJECT_ITER_BEGIN (depsgraph, ob, flags) { - int usage = lineart_usage_check(scene->master_collection, ob, rb); + LineartObjectInfo *obi = lineart_mem_acquire(&rb->render_data_pool, sizeof(LineartObjectInfo)); + obi->usage = lineart_usage_check(scene->master_collection, ob, rb); + Mesh *use_mesh; + + if (obi->usage == OBJECT_LRT_EXCLUDE) { + continue; + } + + Object *use_ob = DEG_get_evaluated_object(depsgraph, ob); + + if (!(use_ob->type == OB_MESH || use_ob->type == OB_MBALL || use_ob->type == OB_CURVE || + use_ob->type == OB_SURF || use_ob->type == OB_FONT)) { + continue; + } + if (use_ob->type == OB_MESH) { + use_mesh = use_ob->data; + } + else { + use_mesh = BKE_mesh_new_from_object(NULL, use_ob, false, true); + } + + /* In case we still can not get any mesh geometry data from the object */ + if (!use_mesh) { + continue; + } + + if (ob->type != OB_MESH) { + obi->free_use_mesh = true; + } - lineart_geometry_object_load(depsgraph, ob, view, proj, rb, usage, &global_i); + /* Prepare the matrix used for transforming this specific object (instance). */ + mul_m4db_m4db_m4fl_uniq(obi->model_view_proj, rb->view_projection, ob->obmat); + mul_m4db_m4db_m4fl_uniq(obi->model_view, rb->view, ob->obmat); + float imat[4][4]; + invert_m4_m4(imat, ob->obmat); + transpose_m4(imat); + copy_m4d_m4(obi->normal, imat); + + obi->original_me = use_mesh; + obi->original_ob = (ob->id.orig_id ? (Object *)ob->id.orig_id : (Object *)ob); + lineart_geometry_load_assign_thread(olti, obi, thread_count, use_mesh->totpoly); } DEG_OBJECT_ITER_END; + + TaskPool *tp = BLI_task_pool_create(NULL, TASK_PRIORITY_HIGH, TASK_ISOLATION_ON); + + for (int i = 0; i < thread_count; i++) { + olti[i].rb = rb; + olti[i].dg = depsgraph; + BLI_task_pool_push(tp, (TaskRunFunction)lineart_object_load_worker, &olti[i], 0, NULL); + } + BLI_task_pool_work_and_wait(tp); + BLI_task_pool_free(tp); + + /* The step below is to serialize vertex index in the whole scene, so + * lineart_triangle_share_edge() can work properly from the lack of triangle adjacent info. */ + int global_i = 0; + + for (int i = 0; i < thread_count; i++) { + for (LineartObjectInfo *obi = olti[i].pending; obi; obi = obi->next) { + if (!obi->v_reln) { + continue; + } + LineartVert *v = (LineartVert *)obi->v_reln->pointer; + int v_count = obi->v_reln->element_count; + for (int vi = 0; vi < v_count; vi++) { + v[vi].index += global_i; + } + global_i += v_count; + lineart_finalize_object_edge_list(rb, obi); + } + } + + if (G.debug_value == 4000) { + double t_elapsed = PIL_check_seconds_timer() - t_start; + printf("Line art loading time: %lf\n", t_elapsed); + } } /** @@ -2531,7 +2663,6 @@ static LineartEdge *lineart_triangle_intersect(LineartRenderBuffer *rb, } } } - return result; } @@ -3693,7 +3824,11 @@ bool MOD_lineart_compute_feature_lines(Depsgraph *depsgraph, LineartGpencilModif Scene *scene = DEG_get_evaluated_scene(depsgraph); int intersections_only = 0; /* Not used right now, but preserve for future. */ - BKE_scene_camera_switch_update(scene); + double t_start; + + if (G.debug_value == 4000) { + t_start = PIL_check_seconds_timer(); + } if (!scene->camera) { return false; @@ -3788,6 +3923,9 @@ bool MOD_lineart_compute_feature_lines(Depsgraph *depsgraph, LineartGpencilModif if (G.debug_value == 4000) { lineart_count_and_print_render_buffer_memory(rb); + + double t_elapsed = PIL_check_seconds_timer() - t_start; + printf("Line art total time: %lf\n", t_elapsed); } return true; diff --git a/source/blender/gpencil_modifiers/intern/lineart/lineart_intern.h b/source/blender/gpencil_modifiers/intern/lineart/lineart_intern.h index 9ed98b38f07..e457d4a83a0 100644 --- a/source/blender/gpencil_modifiers/intern/lineart/lineart_intern.h +++ b/source/blender/gpencil_modifiers/intern/lineart/lineart_intern.h @@ -43,6 +43,13 @@ void *lineart_list_append_pointer_pool_sized(ListBase *h, struct LineartStaticMemPool *smp, void *data, int size); +void *lineart_list_append_pointer_pool_thread(ListBase *h, + struct LineartStaticMemPool *smp, + void *data); +void *lineart_list_append_pointer_pool_sized_thread(ListBase *h, + LineartStaticMemPool *smp, + void *data, + int size); void *list_push_pointer_static(ListBase *h, struct LineartStaticMemPool *smp, void *p); void *list_push_pointer_static_sized(ListBase *h, struct LineartStaticMemPool *smp, diff --git a/source/blender/gpencil_modifiers/intern/lineart/lineart_util.c b/source/blender/gpencil_modifiers/intern/lineart/lineart_util.c index d05f931f75d..47cca0ecd61 100644 --- a/source/blender/gpencil_modifiers/intern/lineart/lineart_util.c +++ b/source/blender/gpencil_modifiers/intern/lineart/lineart_util.c @@ -62,6 +62,31 @@ void *lineart_list_append_pointer_pool_sized(ListBase *h, BLI_addtail(h, lip); return lip; } +void *lineart_list_append_pointer_pool_thread(ListBase *h, LineartStaticMemPool *smp, void *data) +{ + LinkData *lip; + if (h == NULL) { + return 0; + } + lip = lineart_mem_acquire_thread(smp, sizeof(LinkData)); + lip->data = data; + BLI_addtail(h, lip); + return lip; +} +void *lineart_list_append_pointer_pool_sized_thread(ListBase *h, + LineartStaticMemPool *smp, + void *data, + int size) +{ + LinkData *lip; + if (h == NULL) { + return 0; + } + lip = lineart_mem_acquire_thread(smp, size); + lip->data = data; + BLI_addtail(h, lip); + return lip; +} void *lineart_list_pop_pointer_no_free(ListBase *h) { @@ -82,10 +107,10 @@ void lineart_list_remove_pointer_item_no_free(ListBase *h, LinkData *lip) LineartStaticMemPoolNode *lineart_mem_new_static_pool(LineartStaticMemPool *smp, size_t size) { size_t set_size = size; - if (set_size < LRT_MEMORY_POOL_64MB) { - set_size = LRT_MEMORY_POOL_64MB; /* Prevent too many small allocations. */ + if (set_size < LRT_MEMORY_POOL_1MB) { + set_size = LRT_MEMORY_POOL_1MB; /* Prevent too many small allocations. */ } - size_t total_size = size + sizeof(LineartStaticMemPoolNode); + size_t total_size = set_size + sizeof(LineartStaticMemPoolNode); LineartStaticMemPoolNode *smpn = MEM_callocN(total_size, "mempool"); smpn->size = total_size; smpn->used_byte = sizeof(LineartStaticMemPoolNode); @@ -211,7 +236,7 @@ void lineart_count_and_print_render_buffer_memory(LineartRenderBuffer *rb) LISTBASE_FOREACH (LineartStaticMemPoolNode *, smpn, &rb->render_data_pool.pools) { count_this++; - sum_this += LRT_MEMORY_POOL_64MB; + sum_this += LRT_MEMORY_POOL_1MB; } printf("LANPR Memory allocated %zu Standalone nodes, total %zu Bytes.\n", count_this, sum_this); total += sum_this; diff --git a/source/blender/gpu/CMakeLists.txt b/source/blender/gpu/CMakeLists.txt index cf6009c2881..8468985309f 100644 --- a/source/blender/gpu/CMakeLists.txt +++ b/source/blender/gpu/CMakeLists.txt @@ -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 diff --git a/source/blender/gpu/GPU_index_buffer.h b/source/blender/gpu/GPU_index_buffer.h index 4e8d854c7ce..03d60c60b4b 100644 --- a/source/blender/gpu/GPU_index_buffer.h +++ b/source/blender/gpu/GPU_index_buffer.h @@ -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 *); diff --git a/source/blender/gpu/intern/gpu_index_buffer.cc b/source/blender/gpu/intern/gpu_index_buffer.cc index 9f283a3a944..3cdcaac5544 100644 --- a/source/blender/gpu/intern/gpu_index_buffer.cc +++ b/source/blender/gpu/intern/gpu_index_buffer.cc @@ -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 diff --git a/source/blender/gpu/tests/gpu_index_buffer_test.cc b/source/blender/gpu/tests/gpu_index_buffer_test.cc new file mode 100644 index 00000000000..ebc110056e3 --- /dev/null +++ b/source/blender/gpu/tests/gpu_index_buffer_test.cc @@ -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 diff --git a/source/blender/imbuf/intern/imageprocess.c b/source/blender/imbuf/intern/imageprocess.c index 7ada0130059..1fe3a7717fb 100644 --- a/source/blender/imbuf/intern/imageprocess.c +++ b/source/blender/imbuf/intern/imageprocess.c @@ -374,7 +374,7 @@ void IMB_processor_apply_threaded( int total_tasks = (buffer_lines + lines_per_task - 1) / lines_per_task; int i, start_line; - task_pool = BLI_task_pool_create(do_thread, TASK_PRIORITY_LOW); + task_pool = BLI_task_pool_create(do_thread, TASK_PRIORITY_LOW, TASK_ISOLATION_ON); handles = MEM_callocN(handle_size * total_tasks, "processor apply threaded handles"); @@ -432,7 +432,7 @@ void IMB_processor_apply_threaded_scanlines(int total_scanlines, data.scanlines_per_task = scanlines_per_task; data.total_scanlines = total_scanlines; const int total_tasks = (total_scanlines + scanlines_per_task - 1) / scanlines_per_task; - TaskPool *task_pool = BLI_task_pool_create(&data, TASK_PRIORITY_LOW); + TaskPool *task_pool = BLI_task_pool_create(&data, TASK_PRIORITY_LOW, TASK_ISOLATION_ON); for (int i = 0, start_line = 0; i < total_tasks; i++) { BLI_task_pool_push( task_pool, processor_apply_scanline_func, POINTER_FROM_INT(start_line), false, NULL); diff --git a/source/blender/modifiers/intern/MOD_nodes_evaluator.cc b/source/blender/modifiers/intern/MOD_nodes_evaluator.cc index 10ef2f4d8eb..7f80afde99b 100644 --- a/source/blender/modifiers/intern/MOD_nodes_evaluator.cc +++ b/source/blender/modifiers/intern/MOD_nodes_evaluator.cc @@ -380,8 +380,7 @@ class GeometryNodesEvaluator { void execute() { - /* Disable threading until T88598 is resolved. */ - task_pool_ = BLI_task_pool_create_no_threads(this); + task_pool_ = BLI_task_pool_create(this, TASK_PRIORITY_HIGH, TASK_ISOLATION_OFF); this->create_states_for_reachable_nodes(); this->forward_group_inputs(); @@ -1475,6 +1474,13 @@ Vector<GMutablePointer> NodeParamsProvider::extract_multi_input(StringRef identi } BLI_assert_unreachable(); }); + if (ret_values.is_empty()) { + /* If the socket is not linked, we just use the value from the socket itself. */ + BLI_assert(multi_value.items.size() == 1); + MultiInputValueItem &item = multi_value.items[0]; + BLI_assert(item.origin == socket); + ret_values.append({*input_state.type, item.value}); + } multi_value.items.clear(); return ret_values; } diff --git a/source/blender/nodes/CMakeLists.txt b/source/blender/nodes/CMakeLists.txt index 3d1ec86556c..006f6d6e90a 100644 --- a/source/blender/nodes/CMakeLists.txt +++ b/source/blender/nodes/CMakeLists.txt @@ -190,6 +190,7 @@ set(SRC geometry/nodes/node_geo_point_separate.cc geometry/nodes/node_geo_point_translate.cc geometry/nodes/node_geo_points_to_volume.cc + geometry/nodes/node_geo_select_by_material.cc geometry/nodes/node_geo_subdivide.cc geometry/nodes/node_geo_subdivision_surface.cc geometry/nodes/node_geo_switch.cc diff --git a/source/blender/nodes/NOD_geometry.h b/source/blender/nodes/NOD_geometry.h index ff8e8d55d9b..14fcee748a9 100644 --- a/source/blender/nodes/NOD_geometry.h +++ b/source/blender/nodes/NOD_geometry.h @@ -79,6 +79,7 @@ void register_node_type_geo_point_separate(void); void register_node_type_geo_point_translate(void); void register_node_type_geo_points_to_volume(void); void register_node_type_geo_sample_texture(void); +void register_node_type_geo_select_by_material(void); void register_node_type_geo_subdivide(void); void register_node_type_geo_subdivision_surface(void); void register_node_type_geo_switch(void); diff --git a/source/blender/nodes/NOD_static_types.h b/source/blender/nodes/NOD_static_types.h index e6b20844400..58ce695dca0 100644 --- a/source/blender/nodes/NOD_static_types.h +++ b/source/blender/nodes/NOD_static_types.h @@ -317,6 +317,7 @@ DefNode(GeometryNode, GEO_NODE_POINT_SCALE, def_geo_point_scale, "POINT_SCALE", DefNode(GeometryNode, GEO_NODE_POINT_SEPARATE, 0, "POINT_SEPARATE", PointSeparate, "Point Separate", "") DefNode(GeometryNode, GEO_NODE_POINT_TRANSLATE, def_geo_point_translate, "POINT_TRANSLATE", PointTranslate, "Point Translate", "") DefNode(GeometryNode, GEO_NODE_POINTS_TO_VOLUME, def_geo_points_to_volume, "POINTS_TO_VOLUME", PointsToVolume, "Points to Volume", "") +DefNode(GeometryNode, GEO_NODE_SELECT_BY_MATERIAL, 0, "SELECT_BY_MATERIAL", SelectByMaterial, "Select by Material", "") DefNode(GeometryNode, GEO_NODE_SUBDIVIDE, 0, "SUBDIVIDE", Subdivide, "Subdivide", "") DefNode(GeometryNode, GEO_NODE_SUBDIVISION_SURFACE, 0, "SUBDIVISION_SURFACE", SubdivisionSurface, "Subdivision Surface", "") DefNode(GeometryNode, GEO_NODE_SWITCH, def_geo_switch, "SWITCH", Switch, "Switch", "") diff --git a/source/blender/nodes/geometry/nodes/node_geo_bounding_box.cc b/source/blender/nodes/geometry/nodes/node_geo_bounding_box.cc index b6fa4c0d48f..83d3558a7cd 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_bounding_box.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_bounding_box.cc @@ -14,6 +14,7 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +#include "BKE_spline.hh" #include "BKE_volume.h" #include "node_geometry_util.hh" @@ -24,7 +25,7 @@ static bNodeSocketTemplate geo_node_bounding_box_in[] = { }; static bNodeSocketTemplate geo_node_bounding_box_out[] = { - {SOCK_GEOMETRY, N_("Mesh")}, + {SOCK_GEOMETRY, N_("Bounding Box")}, {SOCK_VECTOR, N_("Min")}, {SOCK_VECTOR, N_("Max")}, {-1, ""}, @@ -81,6 +82,28 @@ static void compute_min_max_from_volume_and_transforms(const VolumeComponent &vo #endif } +static void compute_min_max_from_curve_and_transforms(const CurveComponent &curve_component, + Span<float4x4> transforms, + float3 &r_min, + float3 &r_max) +{ + const CurveEval *curve = curve_component.get_for_read(); + if (curve == nullptr) { + return; + } + for (const SplinePtr &spline : curve->splines()) { + Span<float3> positions = spline->evaluated_positions(); + + for (const float4x4 &transform : transforms) { + for (const int i : positions.index_range()) { + const float3 position = positions[i]; + const float3 transformed_position = transform * position; + minmax_v3v3_v3(r_min, r_max, transformed_position); + } + } + } +} + static void compute_geometry_set_instances_boundbox(const GeometrySet &geometry_set, float3 &r_min, float3 &r_max) @@ -104,6 +127,10 @@ static void compute_geometry_set_instances_boundbox(const GeometrySet &geometry_ compute_min_max_from_volume_and_transforms( *set.get_component_for_read<VolumeComponent>(), transforms, r_min, r_max); } + if (set.has<CurveComponent>()) { + compute_min_max_from_curve_and_transforms( + *set.get_component_for_read<CurveComponent>(), transforms, r_min, r_max); + } } } @@ -122,7 +149,7 @@ static void geo_node_bounding_box_exec(GeoNodeExecParams params) } if (min == float3(FLT_MAX)) { - params.set_output("Mesh", GeometrySet()); + params.set_output("Bounding Box", GeometrySet()); params.set_output("Min", float3(0)); params.set_output("Max", float3(0)); } @@ -131,7 +158,7 @@ static void geo_node_bounding_box_exec(GeoNodeExecParams params) const float3 center = min + scale / 2.0f; Mesh *mesh = create_cube_mesh(1.0f); transform_mesh(mesh, center, float3(0), scale); - params.set_output("Mesh", GeometrySet::create_with_mesh(mesh)); + params.set_output("Bounding Box", GeometrySet::create_with_mesh(mesh)); params.set_output("Min", min); params.set_output("Max", max); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_circle.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_circle.cc index f19d533d0b0..2915a17d2c8 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_circle.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_circle.cc @@ -120,6 +120,7 @@ static Mesh *create_circle_mesh(const float radius, 0, circle_corner_total(fill_type, verts_num), circle_face_total(fill_type, verts_num)); + BKE_id_material_eval_ensure_default_slot(&mesh->id); MutableSpan<MVert> verts{mesh->mvert, mesh->totvert}; MutableSpan<MLoop> loops{mesh->mloop, mesh->totloop}; MutableSpan<MEdge> edges{mesh->medge, mesh->totedge}; @@ -215,7 +216,6 @@ static void geo_node_mesh_primitive_circle_exec(GeoNodeExecParams params) } Mesh *mesh = create_circle_mesh(radius, verts_num, fill_type); - BKE_id_material_eval_ensure_default_slot(&mesh->id); BLI_assert(BKE_mesh_is_valid(mesh)); diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cone.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cone.cc index 4c1521aa6f1..925ed0f8da8 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cone.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cone.cc @@ -309,6 +309,7 @@ Mesh *create_cylinder_or_cone_mesh(const float radius_top, 0, corner_total(fill_type, verts_num, top_is_point, bottom_is_point), face_total(fill_type, verts_num, top_is_point, bottom_is_point)); + BKE_id_material_eval_ensure_default_slot(&mesh->id); MutableSpan<MVert> verts{mesh->mvert, mesh->totvert}; MutableSpan<MLoop> loops{mesh->mloop, mesh->totloop}; MutableSpan<MEdge> edges{mesh->medge, mesh->totedge}; @@ -562,7 +563,6 @@ static void geo_node_mesh_primitive_cone_exec(GeoNodeExecParams params) Mesh *mesh = create_cylinder_or_cone_mesh( radius_top, radius_bottom, depth, verts_num, fill_type); - BKE_id_material_eval_ensure_default_slot(&mesh->id); /* Transform the mesh so that the base of the cone is at the origin. */ BKE_mesh_translate(mesh, float3(0.0f, 0.0f, depth * 0.5f), false); diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cube.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cube.cc index b34913df843..9651301cb34 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cube.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cube.cc @@ -54,6 +54,7 @@ Mesh *create_cube_mesh(const float size) BMeshToMeshParams params{}; params.calc_object_remap = false; Mesh *mesh = (Mesh *)BKE_id_new_nomain(ID_ME, nullptr); + BKE_id_material_eval_ensure_default_slot(&mesh->id); BM_mesh_bm_to_me(nullptr, bm, mesh, ¶ms); BM_mesh_free(bm); @@ -65,7 +66,6 @@ static void geo_node_mesh_primitive_cube_exec(GeoNodeExecParams params) const float size = params.extract_input<float>("Size"); Mesh *mesh = create_cube_mesh(size); - BKE_id_material_eval_ensure_default_slot(&mesh->id); params.set_output("Geometry", GeometrySet::create_with_mesh(mesh)); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cylinder.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cylinder.cc index c7b9fb920f8..1767f765da4 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cylinder.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cylinder.cc @@ -76,7 +76,6 @@ static void geo_node_mesh_primitive_cylinder_exec(GeoNodeExecParams params) /* The cylinder is a special case of the cone mesh where the top and bottom radius are equal. */ Mesh *mesh = create_cylinder_or_cone_mesh(radius, radius, depth, verts_num, fill_type); - BKE_id_material_eval_ensure_default_slot(&mesh->id); params.set_output("Geometry", GeometrySet::create_with_mesh(mesh)); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_ico_sphere.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_ico_sphere.cc index 5f7d8150022..a3a1b72006c 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_ico_sphere.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_ico_sphere.cc @@ -56,6 +56,7 @@ static Mesh *create_ico_sphere_mesh(const int subdivisions, const float radius) BMeshToMeshParams params{}; params.calc_object_remap = false; Mesh *mesh = (Mesh *)BKE_id_new_nomain(ID_ME, nullptr); + BKE_id_material_eval_ensure_default_slot(&mesh->id); BM_mesh_bm_to_me(nullptr, bm, mesh, ¶ms); BM_mesh_free(bm); @@ -68,7 +69,6 @@ static void geo_node_mesh_primitive_ico_sphere_exec(GeoNodeExecParams params) const float radius = params.extract_input<float>("Radius"); Mesh *mesh = create_ico_sphere_mesh(subdivisions, radius); - BKE_id_material_eval_ensure_default_slot(&mesh->id); params.set_output("Geometry", GeometrySet::create_with_mesh(mesh)); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_line.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_line.cc index d93c4e39fda..e841455e58c 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_line.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_line.cc @@ -111,6 +111,7 @@ static Mesh *create_line_mesh(const float3 start, const float3 delta, const int } Mesh *mesh = BKE_mesh_new_nomain(count, count - 1, 0, 0, 0); + BKE_id_material_eval_ensure_default_slot(&mesh->id); MutableSpan<MVert> verts{mesh->mvert, mesh->totvert}; MutableSpan<MEdge> edges{mesh->medge, mesh->totedge}; @@ -166,7 +167,6 @@ static void geo_node_mesh_primitive_line_exec(GeoNodeExecParams params) const int count = params.extract_input<int>("Count"); mesh = create_line_mesh(start, delta, count); } - BKE_id_material_eval_ensure_default_slot(&mesh->id); params.set_output("Geometry", GeometrySet::create_with_mesh(mesh)); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_uv_sphere.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_uv_sphere.cc index 7d340679269..599c59e4a2e 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_uv_sphere.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_uv_sphere.cc @@ -267,6 +267,7 @@ static Mesh *create_uv_sphere_mesh(const float radius, const int segments, const 0, sphere_corner_total(segments, rings), sphere_face_total(segments, rings)); + BKE_id_material_eval_ensure_default_slot(&mesh->id); MutableSpan<MVert> verts{mesh->mvert, mesh->totvert}; MutableSpan<MLoop> loops{mesh->mloop, mesh->totloop}; MutableSpan<MEdge> edges{mesh->medge, mesh->totedge}; @@ -297,7 +298,6 @@ static void geo_node_mesh_primitive_uv_sphere_exec(GeoNodeExecParams params) const float radius = params.extract_input<float>("Radius"); Mesh *mesh = create_uv_sphere_mesh(radius, segments_num, rings_num); - BKE_id_material_eval_ensure_default_slot(&mesh->id); params.set_output("Geometry", GeometrySet::create_with_mesh(mesh)); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_select_by_material.cc b/source/blender/nodes/geometry/nodes/node_geo_select_by_material.cc new file mode 100644 index 00000000000..51be90d316e --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_select_by_material.cc @@ -0,0 +1,97 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "node_geometry_util.hh" + +#include "UI_interface.h" +#include "UI_resources.h" + +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" + +#include "BLI_task.hh" + +#include "BKE_material.h" + +static bNodeSocketTemplate geo_node_select_by_material_in[] = { + {SOCK_GEOMETRY, N_("Geometry")}, + {SOCK_MATERIAL, N_("Material")}, + {SOCK_STRING, N_("Selection")}, + {-1, ""}, +}; + +static bNodeSocketTemplate geo_node_select_by_material_out[] = { + {SOCK_GEOMETRY, N_("Geometry")}, + {-1, ""}, +}; + +namespace blender::nodes { + +static void select_mesh_by_material(const Mesh &mesh, + const Material *material, + const MutableSpan<bool> r_selection) +{ + BLI_assert(mesh.totpoly == r_selection.size()); + Vector<int> material_indices; + for (const int i : IndexRange(mesh.totcol)) { + if (mesh.mat[i] == material) { + material_indices.append(i); + } + } + parallel_for(r_selection.index_range(), 1024, [&](IndexRange range) { + for (const int i : range) { + r_selection[i] = material_indices.contains(mesh.mpoly[i].mat_nr); + } + }); +} + +static void geo_node_select_by_material_exec(GeoNodeExecParams params) +{ + Material *material = params.extract_input<Material *>("Material"); + const std::string selection_name = params.extract_input<std::string>("Selection"); + + GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry"); + geometry_set = geometry_set_realize_instances(geometry_set); + + if (geometry_set.has<MeshComponent>()) { + MeshComponent &mesh_component = geometry_set.get_component_for_write<MeshComponent>(); + const Mesh *mesh = mesh_component.get_for_read(); + if (mesh != nullptr) { + OutputAttribute_Typed<bool> selection = + mesh_component.attribute_try_get_for_output_only<bool>(selection_name, ATTR_DOMAIN_FACE); + if (selection) { + select_mesh_by_material(*mesh, material, selection.as_span()); + selection.save(); + } + } + } + + params.set_output("Geometry", std::move(geometry_set)); +} + +} // namespace blender::nodes + +void register_node_type_geo_select_by_material() +{ + static bNodeType ntype; + + geo_node_type_base( + &ntype, GEO_NODE_SELECT_BY_MATERIAL, "Select by Material", NODE_CLASS_GEOMETRY, 0); + node_type_socket_templates( + &ntype, geo_node_select_by_material_in, geo_node_select_by_material_out); + ntype.geometry_node_execute = blender::nodes::geo_node_select_by_material_exec; + nodeRegisterType(&ntype); +} diff --git a/source/blender/sequencer/intern/render.c b/source/blender/sequencer/intern/render.c index 0bad66c3d7a..594e91c1deb 100644 --- a/source/blender/sequencer/intern/render.c +++ b/source/blender/sequencer/intern/render.c @@ -544,9 +544,9 @@ static void sequencer_image_crop_transform_init(void *handle_v, handle->tot_line = tot_line; } -static void *sequencer_image_crop_transform_do_thread(void *data_v) +static void sequencer_image_crop_transform_interpolation_coefs( + const ImageTransformThreadData *data, float r_start_uv[2], float r_add_x[2], float r_add_y[2]) { - const ImageTransformThreadData *data = (ImageTransformThreadData *)data_v; const StripTransform *transform = data->seq->strip->transform; const float scale_x = transform->scale_x * data->image_scale_factor; const float scale_y = transform->scale_y * data->image_scale_factor; @@ -563,6 +563,40 @@ static void *sequencer_image_crop_transform_do_thread(void *data_v) transform_pivot_set_m3(transform_matrix, pivot); invert_m3(transform_matrix); + float orig[2]; + orig[0] = 0.0; + orig[1] = data->start_line; + mul_v2_m3v2(r_start_uv, transform_matrix, orig); + + float uv_min[2]; + uv_min[0] = 0; + uv_min[1] = 0; + mul_v2_m3v2(uv_min, transform_matrix, uv_min); + + float uv_max_x[2]; + uv_max_x[0] = data->ibuf_out->x; + uv_max_x[1] = 0; + mul_v2_m3v2(r_add_x, transform_matrix, uv_max_x); + sub_v2_v2(r_add_x, uv_min); + mul_v2_fl(r_add_x, 1.0 / data->ibuf_out->x); + + float uv_max_y[2]; + uv_max_y[0] = 0; + uv_max_y[1] = data->ibuf_out->y; + mul_v2_m3v2(r_add_y, transform_matrix, uv_max_y); + sub_v2_v2(r_add_y, uv_min); + mul_v2_fl(r_add_y, 1.0 / data->ibuf_out->y); +} + +static void *sequencer_image_crop_transform_do_thread(void *data_v) +{ + const ImageTransformThreadData *data = data_v; + + float last_uv[2]; + float add_x[2]; + float add_y[2]; + sequencer_image_crop_transform_interpolation_coefs(data_v, last_uv, add_x, add_y); + /* Image crop is done by offsetting image boundary limits. */ const StripCrop *c = data->seq->strip->crop; const int left = c->left * data->crop_scale_factor; @@ -575,10 +609,13 @@ static void *sequencer_image_crop_transform_do_thread(void *data_v) const float source_pixel_range_min[2] = {left, bottom}; const int width = data->ibuf_out->x; + + float uv[2]; for (int yi = data->start_line; yi < data->start_line + data->tot_line; yi++) { + copy_v2_v2(uv, last_uv); + add_v2_v2(last_uv, add_y); for (int xi = 0; xi < width; xi++) { - float uv[2] = {xi, yi}; - mul_v2_m3v2(uv, transform_matrix, uv); + add_v2_v2(uv, add_x); if (source_pixel_range_min[0] >= uv[0] || uv[0] >= source_pixel_range_max[0] || source_pixel_range_min[1] >= uv[1] || uv[1] >= source_pixel_range_max[1]) { diff --git a/source/tools b/source/tools -Subproject 01f51a0e551ab730f0934dc6488613690ac4bf8 +Subproject f99d29ae3e6ad44d45d79309454c45f8088781a |