Compositor: Fix crash when executing works in constant folding
Work scheduler needed initialization and execution models are not created during constant folding. This moves work execution method to execution system.
This commit is contained in:
parent
f49f406f67
commit
1657fa039d
|
@ -23,6 +23,7 @@
|
|||
#include "COM_SetColorOperation.h"
|
||||
#include "COM_SetValueOperation.h"
|
||||
#include "COM_SetVectorOperation.h"
|
||||
#include "COM_WorkScheduler.h"
|
||||
|
||||
namespace blender::compositor {
|
||||
|
||||
|
@ -147,6 +148,7 @@ Vector<ConstantOperation *> ConstantFolder::try_fold_operations(Span<NodeOperati
|
|||
*/
|
||||
int ConstantFolder::fold_operations()
|
||||
{
|
||||
WorkScheduler::start(operations_builder_.context());
|
||||
Vector<ConstantOperation *> last_folds = try_fold_operations(
|
||||
operations_builder_.get_operations());
|
||||
int folds_count = last_folds.size();
|
||||
|
@ -158,6 +160,7 @@ int ConstantFolder::fold_operations()
|
|||
last_folds = try_fold_operations(ops_to_fold);
|
||||
folds_count += last_folds.size();
|
||||
}
|
||||
WorkScheduler::stop();
|
||||
|
||||
delete_constant_buffers();
|
||||
|
||||
|
|
|
@ -39,10 +39,4 @@ ExecutionModel::ExecutionModel(CompositorContext &context, Span<NodeOperation *>
|
|||
border_.render_border = &rd->border;
|
||||
}
|
||||
|
||||
bool ExecutionModel::is_breaked() const
|
||||
{
|
||||
const bNodeTree *btree = context_.getbNodeTree();
|
||||
return btree->test_break(btree->tbh);
|
||||
}
|
||||
|
||||
} // namespace blender::compositor
|
||||
|
|
|
@ -67,15 +67,6 @@ class ExecutionModel {
|
|||
|
||||
virtual void execute(ExecutionSystem &exec_system) = 0;
|
||||
|
||||
virtual void execute_work(const rcti &UNUSED(work_rect),
|
||||
std::function<void(const rcti &split_rect)> UNUSED(work_func))
|
||||
{
|
||||
BLI_assert(!"Method not supported by current execution model");
|
||||
}
|
||||
|
||||
protected:
|
||||
bool is_breaked() const;
|
||||
|
||||
#ifdef WITH_CXX_GUARDEDALLOC
|
||||
MEM_CXX_CLASS_ALLOC_FUNCS("COM:BaseExecutionModel")
|
||||
#endif
|
||||
|
|
|
@ -63,6 +63,9 @@ ExecutionSystem::ExecutionSystem(RenderData *rd,
|
|||
this->m_context.setViewSettings(viewSettings);
|
||||
this->m_context.setDisplaySettings(displaySettings);
|
||||
|
||||
BLI_mutex_init(&work_mutex_);
|
||||
BLI_condition_init(&work_finished_cond_);
|
||||
|
||||
{
|
||||
NodeOperationBuilder builder(&m_context, editingtree, this);
|
||||
builder.convertToOperations(this);
|
||||
|
@ -83,6 +86,9 @@ ExecutionSystem::ExecutionSystem(RenderData *rd,
|
|||
|
||||
ExecutionSystem::~ExecutionSystem()
|
||||
{
|
||||
BLI_condition_end(&work_finished_cond_);
|
||||
BLI_mutex_end(&work_mutex_);
|
||||
|
||||
delete execution_model_;
|
||||
|
||||
for (NodeOperation *operation : m_operations) {
|
||||
|
@ -109,10 +115,74 @@ void ExecutionSystem::execute()
|
|||
execution_model_->execute(*this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Multi-threadedly execute given work function passing work_rect splits as argument.
|
||||
*/
|
||||
void ExecutionSystem::execute_work(const rcti &work_rect,
|
||||
std::function<void(const rcti &split_rect)> work_func)
|
||||
{
|
||||
execution_model_->execute_work(work_rect, work_func);
|
||||
if (is_breaked()) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Split work vertically to maximize continuous memory. */
|
||||
const int work_height = BLI_rcti_size_y(&work_rect);
|
||||
const int num_sub_works = MIN2(WorkScheduler::get_num_cpu_threads(), work_height);
|
||||
const int split_height = num_sub_works == 0 ? 0 : work_height / num_sub_works;
|
||||
int remaining_height = work_height - split_height * num_sub_works;
|
||||
|
||||
Vector<WorkPackage> sub_works(num_sub_works);
|
||||
int sub_work_y = work_rect.ymin;
|
||||
int num_sub_works_finished = 0;
|
||||
for (int i = 0; i < num_sub_works; i++) {
|
||||
int sub_work_height = split_height;
|
||||
|
||||
/* Distribute remaining height between sub-works. */
|
||||
if (remaining_height > 0) {
|
||||
sub_work_height++;
|
||||
remaining_height--;
|
||||
}
|
||||
|
||||
WorkPackage &sub_work = sub_works[i];
|
||||
sub_work.type = eWorkPackageType::CustomFunction;
|
||||
sub_work.execute_fn = [=, &work_func, &work_rect]() {
|
||||
if (is_breaked()) {
|
||||
return;
|
||||
}
|
||||
rcti split_rect;
|
||||
BLI_rcti_init(
|
||||
&split_rect, work_rect.xmin, work_rect.xmax, sub_work_y, sub_work_y + sub_work_height);
|
||||
work_func(split_rect);
|
||||
};
|
||||
sub_work.executed_fn = [&]() {
|
||||
BLI_mutex_lock(&work_mutex_);
|
||||
num_sub_works_finished++;
|
||||
if (num_sub_works_finished == num_sub_works) {
|
||||
BLI_condition_notify_one(&work_finished_cond_);
|
||||
}
|
||||
BLI_mutex_unlock(&work_mutex_);
|
||||
};
|
||||
WorkScheduler::schedule(&sub_work);
|
||||
sub_work_y += sub_work_height;
|
||||
}
|
||||
BLI_assert(sub_work_y == work_rect.ymax);
|
||||
|
||||
WorkScheduler::finish();
|
||||
|
||||
/* Ensure all sub-works finished.
|
||||
* TODO: This a workaround for WorkScheduler::finish() not waiting all works on queue threading
|
||||
* model. Sync code should be removed once it's fixed. */
|
||||
BLI_mutex_lock(&work_mutex_);
|
||||
if (num_sub_works_finished < num_sub_works) {
|
||||
BLI_condition_wait(&work_finished_cond_, &work_mutex_);
|
||||
}
|
||||
BLI_mutex_unlock(&work_mutex_);
|
||||
}
|
||||
|
||||
bool ExecutionSystem::is_breaked() const
|
||||
{
|
||||
const bNodeTree *btree = m_context.getbNodeTree();
|
||||
return btree->test_break(btree->tbh);
|
||||
}
|
||||
|
||||
} // namespace blender::compositor
|
||||
|
|
|
@ -150,7 +150,9 @@ class ExecutionSystem {
|
|||
*/
|
||||
ExecutionModel *execution_model_;
|
||||
|
||||
private: // methods
|
||||
ThreadMutex work_mutex_;
|
||||
ThreadCondition work_finished_cond_;
|
||||
|
||||
public:
|
||||
/**
|
||||
* \brief Create a new ExecutionSystem and initialize it with the
|
||||
|
@ -199,6 +201,8 @@ class ExecutionSystem {
|
|||
|
||||
void execute_work(const rcti &work_rect, std::function<void(const rcti &split_rect)> work_func);
|
||||
|
||||
bool is_breaked() const;
|
||||
|
||||
private:
|
||||
/* allow the DebugInfo class to look at internals */
|
||||
friend class DebugInfo;
|
||||
|
|
|
@ -35,24 +35,13 @@ FullFrameExecutionModel::FullFrameExecutionModel(CompositorContext &context,
|
|||
Span<NodeOperation *> operations)
|
||||
: ExecutionModel(context, operations),
|
||||
active_buffers_(shared_buffers),
|
||||
num_operations_finished_(0),
|
||||
work_mutex_(),
|
||||
work_finished_cond_()
|
||||
num_operations_finished_(0)
|
||||
{
|
||||
priorities_.append(eCompositorPriority::High);
|
||||
if (!context.isFastCalculation()) {
|
||||
priorities_.append(eCompositorPriority::Medium);
|
||||
priorities_.append(eCompositorPriority::Low);
|
||||
}
|
||||
|
||||
BLI_mutex_init(&work_mutex_);
|
||||
BLI_condition_init(&work_finished_cond_);
|
||||
}
|
||||
|
||||
FullFrameExecutionModel::~FullFrameExecutionModel()
|
||||
{
|
||||
BLI_condition_end(&work_finished_cond_);
|
||||
BLI_mutex_end(&work_mutex_);
|
||||
}
|
||||
|
||||
void FullFrameExecutionModel::execute(ExecutionSystem &exec_system)
|
||||
|
@ -263,70 +252,6 @@ void FullFrameExecutionModel::get_output_render_area(NodeOperation *output_op, r
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Multi-threadedly execute given work function passing work_rect splits as argument.
|
||||
*/
|
||||
void FullFrameExecutionModel::execute_work(const rcti &work_rect,
|
||||
std::function<void(const rcti &split_rect)> work_func)
|
||||
{
|
||||
if (is_breaked()) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Split work vertically to maximize continuous memory. */
|
||||
const int work_height = BLI_rcti_size_y(&work_rect);
|
||||
const int num_sub_works = MIN2(WorkScheduler::get_num_cpu_threads(), work_height);
|
||||
const int split_height = num_sub_works == 0 ? 0 : work_height / num_sub_works;
|
||||
int remaining_height = work_height - split_height * num_sub_works;
|
||||
|
||||
Vector<WorkPackage> sub_works(num_sub_works);
|
||||
int sub_work_y = work_rect.ymin;
|
||||
int num_sub_works_finished = 0;
|
||||
for (int i = 0; i < num_sub_works; i++) {
|
||||
int sub_work_height = split_height;
|
||||
|
||||
/* Distribute remaining height between sub-works. */
|
||||
if (remaining_height > 0) {
|
||||
sub_work_height++;
|
||||
remaining_height--;
|
||||
}
|
||||
|
||||
WorkPackage &sub_work = sub_works[i];
|
||||
sub_work.type = eWorkPackageType::CustomFunction;
|
||||
sub_work.execute_fn = [=, &work_func, &work_rect]() {
|
||||
if (is_breaked()) {
|
||||
return;
|
||||
}
|
||||
rcti split_rect;
|
||||
BLI_rcti_init(
|
||||
&split_rect, work_rect.xmin, work_rect.xmax, sub_work_y, sub_work_y + sub_work_height);
|
||||
work_func(split_rect);
|
||||
};
|
||||
sub_work.executed_fn = [&]() {
|
||||
BLI_mutex_lock(&work_mutex_);
|
||||
num_sub_works_finished++;
|
||||
if (num_sub_works_finished == num_sub_works) {
|
||||
BLI_condition_notify_one(&work_finished_cond_);
|
||||
}
|
||||
BLI_mutex_unlock(&work_mutex_);
|
||||
};
|
||||
WorkScheduler::schedule(&sub_work);
|
||||
sub_work_y += sub_work_height;
|
||||
}
|
||||
BLI_assert(sub_work_y == work_rect.ymax);
|
||||
|
||||
WorkScheduler::finish();
|
||||
|
||||
/* Ensure all sub-works finished.
|
||||
* TODO: This a workaround for WorkScheduler::finish() not waiting all works on queue threading
|
||||
* model. Sync code should be removed once it's fixed. */
|
||||
BLI_mutex_lock(&work_mutex_);
|
||||
if (num_sub_works_finished < num_sub_works) {
|
||||
BLI_condition_wait(&work_finished_cond_, &work_mutex_);
|
||||
}
|
||||
BLI_mutex_unlock(&work_mutex_);
|
||||
}
|
||||
|
||||
void FullFrameExecutionModel::operation_finished(NodeOperation *operation)
|
||||
{
|
||||
/* Report inputs reads so that buffers may be freed/reused. */
|
||||
|
|
|
@ -50,20 +50,13 @@ class FullFrameExecutionModel : public ExecutionModel {
|
|||
*/
|
||||
Vector<eCompositorPriority> priorities_;
|
||||
|
||||
ThreadMutex work_mutex_;
|
||||
ThreadCondition work_finished_cond_;
|
||||
|
||||
public:
|
||||
FullFrameExecutionModel(CompositorContext &context,
|
||||
SharedOperationBuffers &shared_buffers,
|
||||
Span<NodeOperation *> operations);
|
||||
~FullFrameExecutionModel();
|
||||
|
||||
void execute(ExecutionSystem &exec_system) override;
|
||||
|
||||
void execute_work(const rcti &work_rect,
|
||||
std::function<void(const rcti &split_rect)> work_func) override;
|
||||
|
||||
private:
|
||||
void determine_areas_to_render_and_reads();
|
||||
void render_operations();
|
||||
|
|
|
@ -126,7 +126,7 @@ static void *thread_execute_gpu(void *data)
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
static void opencl_start(CompositorContext &context)
|
||||
static void opencl_start(const CompositorContext &context)
|
||||
{
|
||||
if (context.getHasActiveOpenCLDevices()) {
|
||||
g_work_scheduler.opencl.queue = BLI_thread_queue_init();
|
||||
|
@ -458,7 +458,7 @@ void WorkScheduler::schedule(WorkPackage *package)
|
|||
}
|
||||
}
|
||||
|
||||
void WorkScheduler::start(CompositorContext &context)
|
||||
void WorkScheduler::start(const CompositorContext &context)
|
||||
{
|
||||
if (COM_is_opencl_enabled()) {
|
||||
opencl_start(context);
|
||||
|
|
|
@ -65,7 +65,7 @@ struct WorkScheduler {
|
|||
* for every device a thread is created.
|
||||
* \see initialize Initialization and query of the number of devices
|
||||
*/
|
||||
static void start(CompositorContext &context);
|
||||
static void start(const CompositorContext &context);
|
||||
|
||||
/**
|
||||
* \brief stop the execution
|
||||
|
|
Loading…
Reference in New Issue