Changeset View
Changeset View
Standalone View
Standalone View
source/blender/blenlib/intern/task_range.cc
- This file was moved from source/blender/blenkernel/intern/pbvh_parallel.cc.
| /* | /* | ||||
| * This program is free software; you can redistribute it and/or | * This program is free software; you can redistribute it and/or | ||||
| * modify it under the terms of the GNU General Public License | * modify it under the terms of the GNU General Public License | ||||
| * as published by the Free Software Foundation; either version 2 | * as published by the Free Software Foundation; either version 2 | ||||
| * of the License, or (at your option) any later version. | * of the License, or (at your option) any later version. | ||||
| * | * | ||||
| * This program is distributed in the hope that it will be useful, | * This program is distributed in the hope that it will be useful, | ||||
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||||
| * GNU General Public License for more details. | * GNU General Public License for more details. | ||||
| * | * | ||||
| * You should have received a copy of the GNU General Public License | * You should have received a copy of the GNU General Public License | ||||
| * along with this program; if not, write to the Free Software Foundation, | * along with this program; if not, write to the Free Software Foundation, | ||||
| * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | ||||
| */ | */ | ||||
| /** \file | |||||
| * \ingroup bli | |||||
| * | |||||
| * Task parallel range functions. | |||||
| */ | |||||
| #include <stdlib.h> | |||||
| #include "MEM_guardedalloc.h" | #include "MEM_guardedalloc.h" | ||||
| #include "DNA_listBase.h" | |||||
| #include "BLI_task.h" | #include "BLI_task.h" | ||||
| #include "BLI_threads.h" | #include "BLI_threads.h" | ||||
| #include "BKE_pbvh.h" | |||||
| #include "atomic_ops.h" | #include "atomic_ops.h" | ||||
| #ifdef WITH_TBB | #ifdef WITH_TBB | ||||
| /* Quiet top level deprecation message, unrelated to API usage here. */ | /* Quiet top level deprecation message, unrelated to API usage here. */ | ||||
| # define TBB_SUPPRESS_DEPRECATED_MESSAGES 1 | # define TBB_SUPPRESS_DEPRECATED_MESSAGES 1 | ||||
| # include <tbb/tbb.h> | # include <tbb/tbb.h> | ||||
| #endif | |||||
| #ifdef WITH_TBB | |||||
| /* Functor for running TBB parallel_for and parallel_reduce. */ | /* Functor for running TBB parallel_for and parallel_reduce. */ | ||||
| struct PBVHTask { | struct RangeTask { | ||||
| PBVHParallelRangeFunc func; | TaskParallelRangeFunc func; | ||||
| void *userdata; | void *userdata; | ||||
| const PBVHParallelSettings *settings; | const TaskParallelSettings *settings; | ||||
| void *userdata_chunk; | void *userdata_chunk; | ||||
| /* Root constructor. */ | /* Root constructor. */ | ||||
| PBVHTask(PBVHParallelRangeFunc func, void *userdata, const PBVHParallelSettings *settings) | RangeTask(TaskParallelRangeFunc func, void *userdata, const TaskParallelSettings *settings) | ||||
| : func(func), userdata(userdata), settings(settings) | : func(func), userdata(userdata), settings(settings) | ||||
| { | { | ||||
| init_chunk(settings->userdata_chunk); | init_chunk(settings->userdata_chunk); | ||||
| } | } | ||||
| /* Copy constructor. */ | /* Copy constructor. */ | ||||
| PBVHTask(const PBVHTask &other) | RangeTask(const RangeTask &other) | ||||
| : func(other.func), userdata(other.userdata), settings(other.settings) | : func(other.func), userdata(other.userdata), settings(other.settings) | ||||
| { | { | ||||
| init_chunk(other.userdata_chunk); | init_chunk(settings->userdata_chunk); | ||||
| } | } | ||||
| /* Splitting constructor for parallel reduce. */ | /* Splitting constructor for parallel reduce. */ | ||||
| PBVHTask(PBVHTask &other, tbb::split) | RangeTask(RangeTask &other, tbb::split) | ||||
| : func(other.func), userdata(other.userdata), settings(other.settings) | : func(other.func), userdata(other.userdata), settings(other.settings) | ||||
| { | { | ||||
| init_chunk(settings->userdata_chunk); | init_chunk(settings->userdata_chunk); | ||||
| } | } | ||||
| ~PBVHTask() | ~RangeTask() | ||||
| { | { | ||||
| if (settings->func_free != NULL) { | |||||
| settings->func_free(userdata, userdata_chunk); | |||||
| } | |||||
| MEM_SAFE_FREE(userdata_chunk); | MEM_SAFE_FREE(userdata_chunk); | ||||
| } | } | ||||
| void init_chunk(void *from_chunk) | void init_chunk(void *from_chunk) | ||||
| { | { | ||||
| if (from_chunk) { | if (from_chunk) { | ||||
| userdata_chunk = MEM_mallocN(settings->userdata_chunk_size, "PBVHTask"); | userdata_chunk = MEM_mallocN(settings->userdata_chunk_size, "RangeTask"); | ||||
| memcpy(userdata_chunk, from_chunk, settings->userdata_chunk_size); | memcpy(userdata_chunk, from_chunk, settings->userdata_chunk_size); | ||||
| } | } | ||||
| else { | else { | ||||
| userdata_chunk = NULL; | userdata_chunk = NULL; | ||||
| } | } | ||||
| } | } | ||||
| void operator()(const tbb::blocked_range<int> &r) const | void operator()(const tbb::blocked_range<int> &r) const | ||||
| { | { | ||||
| TaskParallelTLS tls; | TaskParallelTLS tls; | ||||
| tls.thread_id = get_thread_id(); | |||||
| tls.userdata_chunk = userdata_chunk; | tls.userdata_chunk = userdata_chunk; | ||||
| for (int i = r.begin(); i != r.end(); ++i) { | for (int i = r.begin(); i != r.end(); ++i) { | ||||
| func(userdata, i, &tls); | func(userdata, i, &tls); | ||||
| } | } | ||||
| } | } | ||||
| void join(const PBVHTask &other) | void join(const RangeTask &other) | ||||
| { | { | ||||
| settings->func_reduce(userdata, userdata_chunk, other.userdata_chunk); | settings->func_reduce(userdata, userdata_chunk, other.userdata_chunk); | ||||
| } | } | ||||
| int get_thread_id() const | |||||
| { | |||||
| /* Get a unique thread ID for texture nodes. In the future we should get rid | |||||
| * of the thread ID and change texture evaluation to not require per-thread | |||||
| * storage that can't be efficiently allocated on the stack. */ | |||||
| static tbb::enumerable_thread_specific<int> pbvh_thread_id(-1); | |||||
| static int pbvh_thread_id_counter = 0; | |||||
| int &thread_id = pbvh_thread_id.local(); | |||||
| if (thread_id == -1) { | |||||
| thread_id = atomic_fetch_and_add_int32(&pbvh_thread_id_counter, 1); | |||||
| if (thread_id >= BLENDER_MAX_THREADS) { | |||||
| BLI_assert(!"Maximum number of threads exceeded for sculpting"); | |||||
| thread_id = thread_id % BLENDER_MAX_THREADS; | |||||
| } | |||||
| } | |||||
| return thread_id; | |||||
| } | |||||
| }; | }; | ||||
| #endif | #endif | ||||
| void BKE_pbvh_parallel_range(const int start, | void BLI_task_parallel_range(const int start, | ||||
| const int stop, | const int stop, | ||||
| void *userdata, | void *userdata, | ||||
| PBVHParallelRangeFunc func, | TaskParallelRangeFunc func, | ||||
| const struct PBVHParallelSettings *settings) | const TaskParallelSettings *settings) | ||||
| { | { | ||||
| #ifdef WITH_TBB | #ifdef WITH_TBB | ||||
| /* Multithreading. */ | /* Multithreading. */ | ||||
| if (settings->use_threading) { | TaskScheduler *scheduler = BLI_task_scheduler_legacy_get(); | ||||
| PBVHTask task(func, userdata, settings); | if (settings->use_threading && BLI_task_scheduler_tbb_num_threads(scheduler) > 1) { | ||||
| RangeTask task(func, userdata, settings); | |||||
| const size_t grainsize = MAX2(settings->min_iter_per_thread, 1); | |||||
| const tbb::blocked_range<int> range(start, stop, grainsize); | |||||
| if (settings->func_reduce) { | if (settings->func_reduce) { | ||||
| parallel_reduce(tbb::blocked_range<int>(start, stop), task); | parallel_reduce(range, task); | ||||
| if (settings->userdata_chunk) { | if (settings->userdata_chunk) { | ||||
| memcpy(settings->userdata_chunk, task.userdata_chunk, settings->userdata_chunk_size); | memcpy(settings->userdata_chunk, task.userdata_chunk, settings->userdata_chunk_size); | ||||
| } | } | ||||
| } | } | ||||
| else { | else { | ||||
| parallel_for(tbb::blocked_range<int>(start, stop), task); | parallel_for(range, task); | ||||
| } | } | ||||
| return; | return; | ||||
| } | } | ||||
| #endif | #endif | ||||
| /* Single threaded. Nothing to reduce as everything is accumulated into the | /* Single threaded. Nothing to reduce as everything is accumulated into the | ||||
| * main userdata chunk directly. */ | * main userdata chunk directly. */ | ||||
| TaskParallelTLS tls; | TaskParallelTLS tls; | ||||
| tls.thread_id = 0; | |||||
| tls.userdata_chunk = settings->userdata_chunk; | tls.userdata_chunk = settings->userdata_chunk; | ||||
| for (int i = start; i < stop; i++) { | for (int i = start; i < stop; i++) { | ||||
| func(userdata, i, &tls); | func(userdata, i, &tls); | ||||
| } | } | ||||
| if (settings->func_free != NULL) { | |||||
| settings->func_free(userdata, settings->userdata_chunk); | |||||
| } | |||||
| } | |||||
| int BLI_task_parallel_thread_id(const TaskParallelTLS *UNUSED(tls)) | |||||
| { | |||||
| #ifdef WITH_TBB | |||||
| /* Get a unique thread ID for texture nodes. In the future we should get rid | |||||
| * of the thread ID and change texture evaluation to not require per-thread | |||||
| * storage that can't be efficiently allocated on the stack. */ | |||||
| static tbb::enumerable_thread_specific<int> tbb_thread_id(-1); | |||||
| static int tbb_thread_id_counter = 0; | |||||
| int &thread_id = tbb_thread_id.local(); | |||||
| if (thread_id == -1) { | |||||
| thread_id = atomic_fetch_and_add_int32(&tbb_thread_id_counter, 1); | |||||
| if (thread_id >= BLENDER_MAX_THREADS) { | |||||
| BLI_assert(!"Maximum number of threads exceeded for sculpting"); | |||||
| thread_id = thread_id % BLENDER_MAX_THREADS; | |||||
| } | |||||
| } | |||||
| return thread_id; | |||||
| #else | |||||
| return 0; | |||||
| #endif | |||||
| } | } | ||||