diff options
Diffstat (limited to 'source/blender/compositor/intern')
29 files changed, 665 insertions, 178 deletions
diff --git a/source/blender/compositor/intern/COM_BufferOperation.cc b/source/blender/compositor/intern/COM_BufferOperation.cc index 8464d01801f..90c97f2a9c7 100644 --- a/source/blender/compositor/intern/COM_BufferOperation.cc +++ b/source/blender/compositor/intern/COM_BufferOperation.cc @@ -23,6 +23,7 @@ namespace blender::compositor { BufferOperation::BufferOperation(MemoryBuffer *buffer, DataType data_type) { buffer_ = buffer; + inflated_buffer_ = nullptr; /* TODO: Implement a MemoryBuffer get_size() method returning a Size2d type. Shorten following * code to: set_resolution(buffer.get_size()) */ unsigned int resolution[2]; @@ -30,11 +31,30 @@ BufferOperation::BufferOperation(MemoryBuffer *buffer, DataType data_type) resolution[1] = buffer->getHeight(); setResolution(resolution); addOutputSocket(data_type); + flags.is_constant_operation = buffer_->is_a_single_elem(); +} + +const float *BufferOperation::get_constant_elem() +{ + BLI_assert(buffer_->is_a_single_elem()); + return buffer_->getBuffer(); } void *BufferOperation::initializeTileData(rcti * /*rect*/) { - return buffer_; + if (buffer_->is_a_single_elem() == false) { + return buffer_; + } + + if (!inflated_buffer_) { + inflated_buffer_ = buffer_->inflate(); + } + return inflated_buffer_; +} + +void BufferOperation::deinitExecution() +{ + delete inflated_buffer_; } void BufferOperation::executePixelSampled(float output[4], float x, float y, PixelSampler sampler) diff --git a/source/blender/compositor/intern/COM_BufferOperation.h b/source/blender/compositor/intern/COM_BufferOperation.h index f87cd4db94e..705264c37b7 100644 --- a/source/blender/compositor/intern/COM_BufferOperation.h +++ b/source/blender/compositor/intern/COM_BufferOperation.h @@ -18,18 +18,21 @@ #pragma once -#include "COM_NodeOperation.h" +#include "COM_ConstantOperation.h" namespace blender::compositor { -class BufferOperation : public NodeOperation { +class BufferOperation : public ConstantOperation { private: MemoryBuffer *buffer_; + MemoryBuffer *inflated_buffer_; public: BufferOperation(MemoryBuffer *buffer, DataType data_type); + const float *get_constant_elem() override; void *initializeTileData(rcti *rect) override; + void deinitExecution() override; void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; void executePixelFiltered(float output[4], float x, float y, float dx[2], float dy[2]) override; }; diff --git a/source/blender/compositor/intern/COM_CompositorContext.cc b/source/blender/compositor/intern/COM_CompositorContext.cc index 61e299c045e..a93820b66dc 100644 --- a/source/blender/compositor/intern/COM_CompositorContext.cc +++ b/source/blender/compositor/intern/COM_CompositorContext.cc @@ -53,7 +53,7 @@ eExecutionModel CompositorContext::get_execution_model() const case 0: return eExecutionModel::Tiled; default: - BLI_assert(!"Invalid execution mode"); + BLI_assert_msg(0, "Invalid execution mode"); } } return eExecutionModel::Tiled; diff --git a/source/blender/compositor/intern/COM_ConstantFolder.cc b/source/blender/compositor/intern/COM_ConstantFolder.cc new file mode 100644 index 00000000000..f20324de342 --- /dev/null +++ b/source/blender/compositor/intern/COM_ConstantFolder.cc @@ -0,0 +1,191 @@ +/* + * 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. + * + * Copyright 2021, Blender Foundation. + */ + +#include "BLI_rect.h" + +#include "COM_ConstantFolder.h" +#include "COM_ConstantOperation.h" +#include "COM_SetColorOperation.h" +#include "COM_SetValueOperation.h" +#include "COM_SetVectorOperation.h" +#include "COM_WorkScheduler.h" + +namespace blender::compositor { + +using Link = NodeOperationBuilder::Link; + +/** + * \param operations_builder: Contains all operations to fold. + * \param exec_system: Execution system. + */ +ConstantFolder::ConstantFolder(NodeOperationBuilder &operations_builder) + : operations_builder_(operations_builder) +{ + BLI_rcti_init(&max_area_, INT_MIN, INT_MAX, INT_MIN, INT_MAX); + BLI_rcti_init(&first_elem_area_, 0, 1, 0, 1); +} + +static bool is_constant_foldable(NodeOperation *operation) +{ + if (operation->get_flags().can_be_constant && !operation->get_flags().is_constant_operation) { + for (int i = 0; i < operation->getNumberOfInputSockets(); i++) { + if (!operation->get_input_operation(i)->get_flags().is_constant_operation) { + return false; + } + } + return true; + } + return false; +} + +static Vector<NodeOperation *> find_constant_foldable_operations(Span<NodeOperation *> operations) +{ + Vector<NodeOperation *> foldable_ops; + for (NodeOperation *op : operations) { + if (is_constant_foldable(op)) { + foldable_ops.append(op); + } + } + return foldable_ops; +} + +static ConstantOperation *create_constant_operation(DataType data_type, const float *constant_elem) +{ + switch (data_type) { + case DataType::Color: { + SetColorOperation *color_op = new SetColorOperation(); + color_op->setChannels(constant_elem); + return color_op; + } + case DataType::Vector: { + SetVectorOperation *vector_op = new SetVectorOperation(); + vector_op->setVector(constant_elem); + return vector_op; + } + case DataType::Value: { + SetValueOperation *value_op = new SetValueOperation(); + value_op->setValue(*constant_elem); + return value_op; + } + default: { + BLI_assert_msg(0, "Non implemented data type"); + return nullptr; + } + } +} + +ConstantOperation *ConstantFolder::fold_operation(NodeOperation *operation) +{ + const DataType data_type = operation->getOutputSocket()->getDataType(); + MemoryBuffer fold_buf(data_type, first_elem_area_); + Vector<MemoryBuffer *> input_bufs = get_constant_input_buffers(operation); + operation->render(&fold_buf, {first_elem_area_}, input_bufs); + + MemoryBuffer *constant_buf = create_constant_buffer(data_type); + constant_buf->copy_from(&fold_buf, first_elem_area_); + ConstantOperation *constant_op = create_constant_operation(data_type, constant_buf->getBuffer()); + operations_builder_.replace_operation_with_constant(operation, constant_op); + constant_buffers_.add_new(constant_op, constant_buf); + return constant_op; +} + +MemoryBuffer *ConstantFolder::create_constant_buffer(const DataType data_type) +{ + /* Create a single elem buffer with maximum area possible so readers can read any coordinate + * returning always same element. */ + return new MemoryBuffer(data_type, max_area_, true); +} + +Vector<MemoryBuffer *> ConstantFolder::get_constant_input_buffers(NodeOperation *operation) +{ + const int num_inputs = operation->getNumberOfInputSockets(); + Vector<MemoryBuffer *> inputs_bufs(num_inputs); + for (int i = 0; i < num_inputs; i++) { + BLI_assert(operation->get_input_operation(i)->get_flags().is_constant_operation); + ConstantOperation *constant_op = static_cast<ConstantOperation *>( + operation->get_input_operation(i)); + MemoryBuffer *constant_buf = constant_buffers_.lookup_or_add_cb(constant_op, [=] { + MemoryBuffer *buf = create_constant_buffer(constant_op->getOutputSocket()->getDataType()); + constant_op->render(buf, {first_elem_area_}, {}); + return buf; + }); + inputs_bufs[i] = constant_buf; + } + return inputs_bufs; +} + +/** Returns constant operations resulted from folded operations. */ +Vector<ConstantOperation *> ConstantFolder::try_fold_operations(Span<NodeOperation *> operations) +{ + Vector<NodeOperation *> foldable_ops = find_constant_foldable_operations(operations); + if (foldable_ops.size() == 0) { + return Vector<ConstantOperation *>(); + } + + Vector<ConstantOperation *> new_folds; + for (NodeOperation *op : foldable_ops) { + ConstantOperation *constant_op = fold_operation(op); + new_folds.append(constant_op); + } + return new_folds; +} + +/** + * Evaluate operations with constant elements into primitive constant operations. + */ +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(); + while (last_folds.size() > 0) { + Vector<NodeOperation *> ops_to_fold; + for (ConstantOperation *fold : last_folds) { + get_operation_output_operations(fold, ops_to_fold); + } + last_folds = try_fold_operations(ops_to_fold); + folds_count += last_folds.size(); + } + WorkScheduler::stop(); + + delete_constant_buffers(); + + return folds_count; +} + +void ConstantFolder::delete_constant_buffers() +{ + for (MemoryBuffer *buf : constant_buffers_.values()) { + delete buf; + } + constant_buffers_.clear(); +} + +void ConstantFolder::get_operation_output_operations(NodeOperation *operation, + Vector<NodeOperation *> &r_outputs) +{ + const Vector<Link> &links = operations_builder_.get_links(); + for (const Link &link : links) { + if (&link.from()->getOperation() == operation) { + r_outputs.append(&link.to()->getOperation()); + } + } +} + +} // namespace blender::compositor diff --git a/source/blender/compositor/intern/COM_ConstantFolder.h b/source/blender/compositor/intern/COM_ConstantFolder.h new file mode 100644 index 00000000000..2432e859a5a --- /dev/null +++ b/source/blender/compositor/intern/COM_ConstantFolder.h @@ -0,0 +1,64 @@ +/* + * 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. + * + * Copyright 2021, Blender Foundation. + */ + +#pragma once + +#include "BLI_map.hh" +#include "BLI_set.hh" +#include "BLI_vector.hh" + +#include "COM_NodeOperationBuilder.h" +#include "COM_defines.h" + +namespace blender::compositor { + +class NodeOperation; +class ConstantOperation; +class MemoryBuffer; + +/** + * Evaluates all operations with constant elements into primitive constant operations + * (Value/Vector/Color). + */ +class ConstantFolder { + private: + NodeOperationBuilder &operations_builder_; + + /** Constant operations buffers. */ + Map<ConstantOperation *, MemoryBuffer *> constant_buffers_; + + rcti max_area_; + rcti first_elem_area_; + + public: + ConstantFolder(NodeOperationBuilder &operations_builder); + int fold_operations(); + + private: + Vector<ConstantOperation *> try_fold_operations(Span<NodeOperation *> operations); + ConstantOperation *fold_operation(NodeOperation *operation); + + MemoryBuffer *create_constant_buffer(DataType data_type); + Vector<MemoryBuffer *> get_constant_input_buffers(NodeOperation *operation); + void delete_constant_buffers(); + + void get_operation_output_operations(NodeOperation *operation, + Vector<NodeOperation *> &r_outputs); +}; + +} // namespace blender::compositor diff --git a/source/blender/compositor/intern/COM_Converter.cc b/source/blender/compositor/intern/COM_Converter.cc index af593b2e1b5..18973bb5a00 100644 --- a/source/blender/compositor/intern/COM_Converter.cc +++ b/source/blender/compositor/intern/COM_Converter.cc @@ -460,6 +460,9 @@ void COM_convert_resolution(NodeOperationBuilder &builder, NodeOperationOutput *fromSocket, NodeOperationInput *toSocket) { + /* Data type conversions are executed before resolutions to ensure convert operations have + * resolution. This method have to ensure same datatypes are linked for new operations. */ + BLI_assert(fromSocket->getDataType() == toSocket->getDataType()); ResizeMode mode = toSocket->getResizeMode(); NodeOperation *toOperation = &toSocket->getOperation(); @@ -515,7 +518,7 @@ void COM_convert_resolution(NodeOperationBuilder &builder, NodeOperation *first = nullptr; ScaleOperation *scaleOperation = nullptr; if (doScale) { - scaleOperation = new ScaleOperation(); + scaleOperation = new ScaleOperation(fromSocket->getDataType()); scaleOperation->getInputSocket(1)->setResizeMode(ResizeMode::None); scaleOperation->getInputSocket(2)->setResizeMode(ResizeMode::None); first = scaleOperation; @@ -535,7 +538,7 @@ void COM_convert_resolution(NodeOperationBuilder &builder, builder.addOperation(scaleOperation); } - TranslateOperation *translateOperation = new TranslateOperation(); + TranslateOperation *translateOperation = new TranslateOperation(toSocket->getDataType()); translateOperation->getInputSocket(1)->setResizeMode(ResizeMode::None); translateOperation->getInputSocket(2)->setResizeMode(ResizeMode::None); if (!first) { diff --git a/source/blender/compositor/intern/COM_Debug.cc b/source/blender/compositor/intern/COM_Debug.cc index 4cf7e09a7d8..abef4517b3e 100644 --- a/source/blender/compositor/intern/COM_Debug.cc +++ b/source/blender/compositor/intern/COM_Debug.cc @@ -37,6 +37,7 @@ extern "C" { #include "COM_Node.h" #include "COM_ReadBufferOperation.h" +#include "COM_SetValueOperation.h" #include "COM_ViewerOperation.h" #include "COM_WriteBufferOperation.h" @@ -49,6 +50,15 @@ std::string DebugInfo::m_current_node_name; std::string DebugInfo::m_current_op_name; DebugInfo::GroupStateMap DebugInfo::m_group_states; +static std::string operation_class_name(NodeOperation *op) +{ + std::string full_name = typeid(*op).name(); + /* Remove name-spaces. */ + size_t pos = full_name.find_last_of(':'); + BLI_assert(pos != std::string::npos); + return full_name.substr(pos + 1); +} + std::string DebugInfo::node_name(const Node *node) { NodeNameMap::const_iterator it = m_node_names.find(node); @@ -135,15 +145,23 @@ int DebugInfo::graphviz_operation(const ExecutionSystem *system, len += snprintf(str + len, maxlen > len ? maxlen - len : 0, "|"); } + if (COM_GRAPHVIZ_SHOW_NODE_NAME) { + std::string op_node_name = operation->get_name(); + if (!op_node_name.empty()) { + len += snprintf( + str + len, maxlen > len ? maxlen - len : 0, "%s\\n", (op_node_name + " Node").c_str()); + } + } + len += snprintf(str + len, maxlen > len ? maxlen - len : 0, - "%s\\n(%s)", - m_op_names[operation].c_str(), - typeid(*operation).name()); + "%s\\n", + operation_class_name(operation).c_str()); len += snprintf(str + len, maxlen > len ? maxlen - len : 0, - " (%u,%u)", + "#%d (%u,%u)", + operation->get_id(), operation->getWidth(), operation->getHeight()); @@ -159,7 +177,13 @@ int DebugInfo::graphviz_operation(const ExecutionSystem *system, len += snprintf(str + len, maxlen > len ? maxlen - len : 0, "<OUT_%p>", socket); switch (socket->getDataType()) { case DataType::Value: - len += snprintf(str + len, maxlen > len ? maxlen - len : 0, "Value"); + if (typeid(*operation) == typeid(SetValueOperation)) { + const float value = ((SetValueOperation *)operation)->getValue(); + len += snprintf(str + len, maxlen > len ? maxlen - len : 0, "Value\\n%12.4g", value); + } + else { + len += snprintf(str + len, maxlen > len ? maxlen - len : 0, "Value"); + } break; case DataType::Vector: len += snprintf(str + len, maxlen > len ? maxlen - len : 0, "Vector"); @@ -401,7 +425,7 @@ bool DebugInfo::graphviz_system(const ExecutionSystem *system, char *str, int ma return (len < maxlen); } -void DebugInfo::graphviz(const ExecutionSystem *system) +void DebugInfo::graphviz(const ExecutionSystem *system, StringRefNull name) { if (!COM_EXPORT_GRAPHVIZ) { return; @@ -411,7 +435,12 @@ void DebugInfo::graphviz(const ExecutionSystem *system) char basename[FILE_MAX]; char filename[FILE_MAX]; - BLI_snprintf(basename, sizeof(basename), "compositor_%d.dot", m_file_index); + if (name.is_empty()) { + BLI_snprintf(basename, sizeof(basename), "compositor_%d.dot", m_file_index); + } + else { + BLI_strncpy(basename, (name + ".dot").c_str(), sizeof(basename)); + } BLI_join_dirfile(filename, sizeof(filename), BKE_tempdir_session(), basename); m_file_index++; diff --git a/source/blender/compositor/intern/COM_Debug.h b/source/blender/compositor/intern/COM_Debug.h index 0de3a5e39dc..53461e13f48 100644 --- a/source/blender/compositor/intern/COM_Debug.h +++ b/source/blender/compositor/intern/COM_Debug.h @@ -28,6 +28,8 @@ namespace blender::compositor { static constexpr bool COM_EXPORT_GRAPHVIZ = false; +static constexpr bool COM_GRAPHVIZ_SHOW_NODE_NAME = false; + class Node; class ExecutionSystem; class ExecutionGroup; @@ -116,7 +118,7 @@ class DebugInfo { } }; - static void graphviz(const ExecutionSystem *system); + static void graphviz(const ExecutionSystem *system, StringRefNull name = ""); protected: static int graphviz_operation(const ExecutionSystem *system, diff --git a/source/blender/compositor/intern/COM_ExecutionModel.cc b/source/blender/compositor/intern/COM_ExecutionModel.cc index 4d7f62e091b..b75b277e92c 100644 --- a/source/blender/compositor/intern/COM_ExecutionModel.cc +++ b/source/blender/compositor/intern/COM_ExecutionModel.cc @@ -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 diff --git a/source/blender/compositor/intern/COM_ExecutionModel.h b/source/blender/compositor/intern/COM_ExecutionModel.h index 9e8466b9282..452861ae4be 100644 --- a/source/blender/compositor/intern/COM_ExecutionModel.h +++ b/source/blender/compositor/intern/COM_ExecutionModel.h @@ -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 diff --git a/source/blender/compositor/intern/COM_ExecutionSystem.cc b/source/blender/compositor/intern/COM_ExecutionSystem.cc index a12ec774032..60caf22be1b 100644 --- a/source/blender/compositor/intern/COM_ExecutionSystem.cc +++ b/source/blender/compositor/intern/COM_ExecutionSystem.cc @@ -63,8 +63,11 @@ 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); + NodeOperationBuilder builder(&m_context, editingtree, this); builder.convertToOperations(this); } @@ -76,13 +79,16 @@ ExecutionSystem::ExecutionSystem(RenderData *rd, execution_model_ = new FullFrameExecutionModel(m_context, active_buffers_, m_operations); break; default: - BLI_assert(!"Non implemented execution model"); + BLI_assert_msg(0, "Non implemented execution model"); break; } } 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 diff --git a/source/blender/compositor/intern/COM_ExecutionSystem.h b/source/blender/compositor/intern/COM_ExecutionSystem.h index e106209651c..38c3432a8ec 100644 --- a/source/blender/compositor/intern/COM_ExecutionSystem.h +++ b/source/blender/compositor/intern/COM_ExecutionSystem.h @@ -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; diff --git a/source/blender/compositor/intern/COM_FullFrameExecutionModel.cc b/source/blender/compositor/intern/COM_FullFrameExecutionModel.cc index 21075bb7255..3b0a9172871 100644 --- a/source/blender/compositor/intern/COM_FullFrameExecutionModel.cc +++ b/source/blender/compositor/intern/COM_FullFrameExecutionModel.cc @@ -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) @@ -60,10 +49,10 @@ void FullFrameExecutionModel::execute(ExecutionSystem &exec_system) const bNodeTree *node_tree = this->context_.getbNodeTree(); node_tree->stats_draw(node_tree->sdh, TIP_("Compositing | Initializing execution")); - DebugInfo::graphviz(&exec_system); + DebugInfo::graphviz(&exec_system, "compositor_prior_rendering"); determine_areas_to_render_and_reads(); - render_operations(exec_system); + render_operations(); } void FullFrameExecutionModel::determine_areas_to_render_and_reads() @@ -101,20 +90,18 @@ MemoryBuffer *FullFrameExecutionModel::create_operation_buffer(NodeOperation *op BLI_rcti_init(&op_rect, 0, op->getWidth(), 0, op->getHeight()); const DataType data_type = op->getOutputSocket(0)->getDataType(); - /* TODO: We should check if the operation is constant instead of is_set_operation. Finding a way - * to know if an operation is constant has to be implemented yet. */ - const bool is_a_single_elem = op->get_flags().is_set_operation; + const bool is_a_single_elem = op->get_flags().is_constant_operation; return new MemoryBuffer(data_type, op_rect, is_a_single_elem); } -void FullFrameExecutionModel::render_operation(NodeOperation *op, ExecutionSystem &exec_system) +void FullFrameExecutionModel::render_operation(NodeOperation *op) { Vector<MemoryBuffer *> input_bufs = get_input_buffers(op); const bool has_outputs = op->getNumberOfOutputSockets() > 0; MemoryBuffer *op_buf = has_outputs ? create_operation_buffer(op) : nullptr; Span<rcti> areas = active_buffers_.get_areas_to_render(op); - op->render(op_buf, areas, input_bufs, exec_system); + op->render(op_buf, areas, input_bufs); active_buffers_.set_rendered_buffer(op, std::unique_ptr<MemoryBuffer>(op_buf)); operation_finished(op); @@ -123,7 +110,7 @@ void FullFrameExecutionModel::render_operation(NodeOperation *op, ExecutionSyste /** * Render output operations in order of priority. */ -void FullFrameExecutionModel::render_operations(ExecutionSystem &exec_system) +void FullFrameExecutionModel::render_operations() { const bool is_rendering = context_.isRendering(); @@ -131,8 +118,8 @@ void FullFrameExecutionModel::render_operations(ExecutionSystem &exec_system) for (eCompositorPriority priority : priorities_) { for (NodeOperation *op : operations_) { if (op->isOutputOperation(is_rendering) && op->getRenderPriority() == priority) { - render_output_dependencies(op, exec_system); - render_operation(op, exec_system); + render_output_dependencies(op); + render_operation(op); } } } @@ -166,14 +153,13 @@ static Vector<NodeOperation *> get_operation_dependencies(NodeOperation *operati return dependencies; } -void FullFrameExecutionModel::render_output_dependencies(NodeOperation *output_op, - ExecutionSystem &exec_system) +void FullFrameExecutionModel::render_output_dependencies(NodeOperation *output_op) { BLI_assert(output_op->isOutputOperation(context_.isRendering())); Vector<NodeOperation *> dependencies = get_operation_dependencies(output_op); for (NodeOperation *op : dependencies) { if (!active_buffers_.is_operation_rendered(op)) { - render_operation(op, exec_system); + render_operation(op); } } } @@ -266,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. */ diff --git a/source/blender/compositor/intern/COM_FullFrameExecutionModel.h b/source/blender/compositor/intern/COM_FullFrameExecutionModel.h index e68ad93b407..f75d4f1afdc 100644 --- a/source/blender/compositor/intern/COM_FullFrameExecutionModel.h +++ b/source/blender/compositor/intern/COM_FullFrameExecutionModel.h @@ -50,27 +50,20 @@ 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(ExecutionSystem &exec_system); - void render_output_dependencies(NodeOperation *output_op, ExecutionSystem &exec_system); + void render_operations(); + void render_output_dependencies(NodeOperation *output_op); Vector<MemoryBuffer *> get_input_buffers(NodeOperation *op); MemoryBuffer *create_operation_buffer(NodeOperation *op); - void render_operation(NodeOperation *op, ExecutionSystem &exec_system); + void render_operation(NodeOperation *op); void operation_finished(NodeOperation *operation); diff --git a/source/blender/compositor/intern/COM_MemoryBuffer.cc b/source/blender/compositor/intern/COM_MemoryBuffer.cc index 44d3f059374..c7bddddd0e6 100644 --- a/source/blender/compositor/intern/COM_MemoryBuffer.cc +++ b/source/blender/compositor/intern/COM_MemoryBuffer.cc @@ -129,6 +129,18 @@ void MemoryBuffer::clear() memset(m_buffer, 0, buffer_len() * m_num_channels * sizeof(float)); } +/** + * Converts a single elem buffer to a full size buffer (allocates memory for all + * elements in resolution). + */ +MemoryBuffer *MemoryBuffer::inflate() const +{ + BLI_assert(is_a_single_elem()); + MemoryBuffer *inflated = new MemoryBuffer(this->m_datatype, this->m_rect, false); + inflated->copy_from(this, this->m_rect); + return inflated; +} + float MemoryBuffer::get_max_value() const { float result = this->m_buffer[0]; diff --git a/source/blender/compositor/intern/COM_MemoryBuffer.h b/source/blender/compositor/intern/COM_MemoryBuffer.h index fdfd1c1c37b..4ad0872b0b7 100644 --- a/source/blender/compositor/intern/COM_MemoryBuffer.h +++ b/source/blender/compositor/intern/COM_MemoryBuffer.h @@ -247,6 +247,8 @@ class MemoryBuffer { return this->m_buffer; } + MemoryBuffer *inflate() const; + inline void wrap_pixel(int &x, int &y, MemoryBufferExtend extend_x, MemoryBufferExtend extend_y) { const int w = getWidth(); @@ -262,11 +264,14 @@ class MemoryBuffer { x = 0; } if (x >= w) { - x = w; + x = w - 1; } break; case MemoryBufferExtend::Repeat: - x = (x >= 0.0f ? (x % w) : (x % w) + w); + x %= w; + if (x < 0) { + x += w; + } break; } @@ -278,13 +283,19 @@ class MemoryBuffer { y = 0; } if (y >= h) { - y = h; + y = h - 1; } break; case MemoryBufferExtend::Repeat: - y = (y >= 0.0f ? (y % h) : (y % h) + h); + y %= h; + if (y < 0) { + y += h; + } break; } + + x = x + m_rect.xmin; + y = y + m_rect.ymin; } inline void wrap_pixel(float &x, @@ -305,11 +316,14 @@ class MemoryBuffer { x = 0.0f; } if (x >= w) { - x = w; + x = w - 1; } break; case MemoryBufferExtend::Repeat: x = fmodf(x, w); + if (x < 0.0f) { + x += w; + } break; } @@ -321,13 +335,19 @@ class MemoryBuffer { y = 0.0f; } if (y >= h) { - y = h; + y = h - 1; } break; case MemoryBufferExtend::Repeat: y = fmodf(y, h); + if (y < 0.0f) { + y += h; + } break; } + + x = x + m_rect.xmin; + y = y + m_rect.ymin; } inline void read(float *result, diff --git a/source/blender/compositor/intern/COM_MultiThreadedOperation.cc b/source/blender/compositor/intern/COM_MultiThreadedOperation.cc index e6e98d69b36..7ccf6f76d9f 100644 --- a/source/blender/compositor/intern/COM_MultiThreadedOperation.cc +++ b/source/blender/compositor/intern/COM_MultiThreadedOperation.cc @@ -12,12 +12,11 @@ MultiThreadedOperation::MultiThreadedOperation() void MultiThreadedOperation::update_memory_buffer(MemoryBuffer *output, const rcti &area, - Span<MemoryBuffer *> inputs, - ExecutionSystem &exec_system) + Span<MemoryBuffer *> inputs) { for (current_pass_ = 0; current_pass_ < num_passes_; current_pass_++) { update_memory_buffer_started(output, area, inputs); - exec_system.execute_work(area, [=](const rcti &split_rect) { + exec_system_->execute_work(area, [=](const rcti &split_rect) { update_memory_buffer_partial(output, split_rect, inputs); }); update_memory_buffer_finished(output, area, inputs); diff --git a/source/blender/compositor/intern/COM_MultiThreadedOperation.h b/source/blender/compositor/intern/COM_MultiThreadedOperation.h index ad09c4df089..a7e574ca745 100644 --- a/source/blender/compositor/intern/COM_MultiThreadedOperation.h +++ b/source/blender/compositor/intern/COM_MultiThreadedOperation.h @@ -64,8 +64,7 @@ class MultiThreadedOperation : public NodeOperation { private: void update_memory_buffer(MemoryBuffer *output, const rcti &area, - Span<MemoryBuffer *> inputs, - ExecutionSystem &exec_system) override; + Span<MemoryBuffer *> inputs) override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/intern/COM_MultiThreadedRowOperation.cc b/source/blender/compositor/intern/COM_MultiThreadedRowOperation.cc new file mode 100644 index 00000000000..6bf318bb96b --- /dev/null +++ b/source/blender/compositor/intern/COM_MultiThreadedRowOperation.cc @@ -0,0 +1,50 @@ +/* + * 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. + * + * Copyright 2021, Blender Foundation. + */ + +#include "COM_MultiThreadedRowOperation.h" + +namespace blender::compositor { + +MultiThreadedRowOperation::PixelCursor::PixelCursor(const int num_inputs) + : out(nullptr), out_stride(0), row_end(nullptr), ins(num_inputs), in_strides(num_inputs) +{ +} + +void MultiThreadedRowOperation::update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) +{ + BLI_assert(output != nullptr); + const int width = BLI_rcti_size_x(&area); + PixelCursor p(inputs.size()); + p.out_stride = output->elem_stride; + for (int i = 0; i < p.in_strides.size(); i++) { + p.in_strides[i] = inputs[i]->elem_stride; + } + + for (int y = area.ymin; y < area.ymax; y++) { + p.out = output->get_elem(area.xmin, y); + for (int i = 0; i < p.ins.size(); i++) { + p.ins[i] = inputs[i]->get_elem(area.xmin, y); + } + p.row_end = p.out + width * p.out_stride; + update_memory_buffer_row(p); + } +} + +} // namespace blender::compositor diff --git a/source/blender/compositor/intern/COM_MultiThreadedRowOperation.h b/source/blender/compositor/intern/COM_MultiThreadedRowOperation.h new file mode 100644 index 00000000000..3daa9eec474 --- /dev/null +++ b/source/blender/compositor/intern/COM_MultiThreadedRowOperation.h @@ -0,0 +1,60 @@ +/* + * 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. + * + * Copyright 2021, Blender Foundation. + */ + +#pragma once + +#include "COM_MultiThreadedOperation.h" + +namespace blender::compositor { + +/** + * Executes buffer updates per row. To be inherited only by operations with correlated coordinates + * between inputs and output. + */ +class MultiThreadedRowOperation : public MultiThreadedOperation { + protected: + struct PixelCursor { + float *out; + int out_stride; + const float *row_end; + Array<const float *> ins; + Array<int> in_strides; + + public: + PixelCursor(int num_inputs); + + void next() + { + BLI_assert(out < row_end); + out += out_stride; + for (int i = 0; i < ins.size(); i++) { + ins[i] += in_strides[i]; + } + } + }; + + protected: + virtual void update_memory_buffer_row(PixelCursor &p) = 0; + + private: + void update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) final; +}; + +} // namespace blender::compositor diff --git a/source/blender/compositor/intern/COM_NodeGraph.cc b/source/blender/compositor/intern/COM_NodeGraph.cc index fbe56dd4b5a..205fbcc0440 100644 --- a/source/blender/compositor/intern/COM_NodeGraph.cc +++ b/source/blender/compositor/intern/COM_NodeGraph.cc @@ -174,7 +174,7 @@ void NodeGraph::add_bNodeLink(const NodeRange &node_range, bNodeLink *b_nodelink return; } - /* Note: a DNA input socket can have multiple NodeInput in the compositor tree! (proxies) + /* NOTE: a DNA input socket can have multiple NodeInput in the compositor tree! (proxies) * The output then gets linked to each one of them. */ diff --git a/source/blender/compositor/intern/COM_NodeOperation.cc b/source/blender/compositor/intern/COM_NodeOperation.cc index b943ab6af7f..4e115cb3f2f 100644 --- a/source/blender/compositor/intern/COM_NodeOperation.cc +++ b/source/blender/compositor/intern/COM_NodeOperation.cc @@ -218,7 +218,7 @@ void NodeOperation::get_area_of_interest(NodeOperation *input_op, return; } } - BLI_assert(!"input_op is not an input operation."); + BLI_assert_msg(0, "input_op is not an input operation."); } /** @@ -226,18 +226,16 @@ void NodeOperation::get_area_of_interest(NodeOperation *input_op, * \param output_buf: Buffer to write result to. * \param areas: Areas within this operation bounds to render. * \param inputs_bufs: Inputs operations buffers. - * \param exec_system: Execution system. */ void NodeOperation::render(MemoryBuffer *output_buf, Span<rcti> areas, - Span<MemoryBuffer *> inputs_bufs, - ExecutionSystem &exec_system) + Span<MemoryBuffer *> inputs_bufs) { if (get_flags().is_fullframe_operation) { - render_full_frame(output_buf, areas, inputs_bufs, exec_system); + render_full_frame(output_buf, areas, inputs_bufs); } else { - render_full_frame_fallback(output_buf, areas, inputs_bufs, exec_system); + render_full_frame_fallback(output_buf, areas, inputs_bufs); } } @@ -246,12 +244,11 @@ void NodeOperation::render(MemoryBuffer *output_buf, */ void NodeOperation::render_full_frame(MemoryBuffer *output_buf, Span<rcti> areas, - Span<MemoryBuffer *> inputs_bufs, - ExecutionSystem &exec_system) + Span<MemoryBuffer *> inputs_bufs) { initExecution(); for (const rcti &area : areas) { - update_memory_buffer(output_buf, area, inputs_bufs, exec_system); + update_memory_buffer(output_buf, area, inputs_bufs); } deinitExecution(); } @@ -261,8 +258,7 @@ void NodeOperation::render_full_frame(MemoryBuffer *output_buf, */ void NodeOperation::render_full_frame_fallback(MemoryBuffer *output_buf, Span<rcti> areas, - Span<MemoryBuffer *> inputs_bufs, - ExecutionSystem &exec_system) + Span<MemoryBuffer *> inputs_bufs) { Vector<NodeOperationOutput *> orig_input_links = replace_inputs_with_buffers(inputs_bufs); @@ -274,7 +270,7 @@ void NodeOperation::render_full_frame_fallback(MemoryBuffer *output_buf, } else { for (const rcti &rect : areas) { - exec_system.execute_work(rect, [=](const rcti &split_rect) { + exec_system_->execute_work(rect, [=](const rcti &split_rect) { rcti tile_rect = split_rect; if (is_output_operation) { executeRegion(&tile_rect, 0); @@ -328,6 +324,7 @@ Vector<NodeOperationOutput *> NodeOperation::replace_inputs_with_buffers( BufferOperation *buffer_op = new BufferOperation(inputs_bufs[i], input_socket->getDataType()); orig_links[i] = input_socket->getLink(); input_socket->setLink(buffer_op->getOutputSocket()); + buffer_op->initExecution(); } return orig_links; } @@ -340,6 +337,7 @@ void NodeOperation::remove_buffers_and_restore_original_inputs( NodeOperation *buffer_op = get_input_operation(i); BLI_assert(buffer_op != nullptr); BLI_assert(typeid(*buffer_op) == typeid(BufferOperation)); + buffer_op->deinitExecution(); NodeOperationInput *input_socket = getInputSocket(i); input_socket->setLink(original_inputs_links[i]); delete buffer_op; @@ -443,6 +441,12 @@ std::ostream &operator<<(std::ostream &os, const NodeOperationFlags &node_operat if (node_operation_flags.is_fullframe_operation) { os << "full_frame,"; } + if (node_operation_flags.is_constant_operation) { + os << "contant_operation,"; + } + if (node_operation_flags.can_be_constant) { + os << "can_be_constant,"; + } return os; } diff --git a/source/blender/compositor/intern/COM_NodeOperation.h b/source/blender/compositor/intern/COM_NodeOperation.h index 5ae0aae67d5..fb9ec1e7a83 100644 --- a/source/blender/compositor/intern/COM_NodeOperation.h +++ b/source/blender/compositor/intern/COM_NodeOperation.h @@ -221,6 +221,7 @@ struct NodeOperationFlags { /** * Is this a set operation (value, color, vector). + * TODO: To be replaced by is_constant_operation flag once tiled implementation is removed. */ bool is_set_operation : 1; bool is_write_buffer_operation : 1; @@ -242,6 +243,17 @@ struct NodeOperationFlags { */ bool is_fullframe_operation : 1; + /** + * Whether operation is a primitive constant operation (Color/Vector/Value). + */ + bool is_constant_operation : 1; + + /** + * Whether operation have constant elements/pixels values when all its inputs are constant + * operations. + */ + bool can_be_constant : 1; + NodeOperationFlags() { complex = false; @@ -258,6 +270,8 @@ struct NodeOperationFlags { is_preview_operation = false; use_datatype_conversion = true; is_fullframe_operation = false; + is_constant_operation = false; + can_be_constant = false; } }; @@ -316,6 +330,8 @@ class NodeOperation { */ NodeOperationFlags flags; + ExecutionSystem *exec_system_; + public: virtual ~NodeOperation() { @@ -402,6 +418,12 @@ class NodeOperation { { this->m_btree = tree; } + + void set_execution_system(ExecutionSystem *system) + { + exec_system_ = system; + } + virtual void initExecution(); /** @@ -569,18 +591,14 @@ class NodeOperation { /** \name Full Frame Methods * \{ */ - void render(MemoryBuffer *output_buf, - Span<rcti> areas, - Span<MemoryBuffer *> inputs_bufs, - ExecutionSystem &exec_system); + void render(MemoryBuffer *output_buf, Span<rcti> areas, Span<MemoryBuffer *> inputs_bufs); /** * Executes operation updating output memory buffer. Single-threaded calls. */ virtual void update_memory_buffer(MemoryBuffer *UNUSED(output), const rcti &UNUSED(area), - Span<MemoryBuffer *> UNUSED(inputs), - ExecutionSystem &UNUSED(exec_system)) + Span<MemoryBuffer *> UNUSED(inputs)) { } @@ -678,13 +696,11 @@ class NodeOperation { void render_full_frame(MemoryBuffer *output_buf, Span<rcti> areas, - Span<MemoryBuffer *> inputs_bufs, - ExecutionSystem &exec_system); + Span<MemoryBuffer *> inputs_bufs); void render_full_frame_fallback(MemoryBuffer *output_buf, Span<rcti> areas, - Span<MemoryBuffer *> inputs, - ExecutionSystem &exec_system); + Span<MemoryBuffer *> inputs); void render_tile(MemoryBuffer *output_buf, rcti *tile_rect); Vector<NodeOperationOutput *> replace_inputs_with_buffers(Span<MemoryBuffer *> inputs_bufs); void remove_buffers_and_restore_original_inputs( diff --git a/source/blender/compositor/intern/COM_NodeOperationBuilder.cc b/source/blender/compositor/intern/COM_NodeOperationBuilder.cc index 1beb83bb477..10a91bbcd3e 100644 --- a/source/blender/compositor/intern/COM_NodeOperationBuilder.cc +++ b/source/blender/compositor/intern/COM_NodeOperationBuilder.cc @@ -36,12 +36,15 @@ #include "COM_ViewerOperation.h" #include "COM_WriteBufferOperation.h" +#include "COM_ConstantFolder.h" #include "COM_NodeOperationBuilder.h" /* own include */ namespace blender::compositor { -NodeOperationBuilder::NodeOperationBuilder(const CompositorContext *context, bNodeTree *b_nodetree) - : m_context(context), m_current_node(nullptr), m_active_viewer(nullptr) +NodeOperationBuilder::NodeOperationBuilder(const CompositorContext *context, + bNodeTree *b_nodetree, + ExecutionSystem *system) + : m_context(context), exec_system_(system), m_current_node(nullptr), m_active_viewer(nullptr) { m_graph.from_bNodeTree(*context, b_nodetree); } @@ -79,7 +82,7 @@ void NodeOperationBuilder::convertToOperations(ExecutionSystem *system) if (!op_from || op_to_list.is_empty()) { /* XXX allow this? error/debug message? */ // BLI_assert(false); - /* XXX note: this can happen with certain nodes (e.g. OutputFile) + /* XXX NOTE: this can happen with certain nodes (e.g. OutputFile) * which only generate operations in certain circumstances (rendering) * just let this pass silently for now ... */ @@ -97,6 +100,15 @@ void NodeOperationBuilder::convertToOperations(ExecutionSystem *system) add_datatype_conversions(); + if (m_context->get_execution_model() == eExecutionModel::FullFrame) { + /* Copy operations to system. Needed for graphviz. */ + system->set_operations(m_operations, {}); + + DebugInfo::graphviz(system, "compositor_prior_folding"); + ConstantFolder folder(*this); + folder.fold_operations(); + } + determineResolutions(); if (m_context->get_execution_model() == eExecutionModel::Tiled) { @@ -130,6 +142,29 @@ void NodeOperationBuilder::addOperation(NodeOperation *operation) operation->set_name(m_current_node->getbNode()->name); } operation->set_execution_model(m_context->get_execution_model()); + operation->set_execution_system(exec_system_); +} + +void NodeOperationBuilder::replace_operation_with_constant(NodeOperation *operation, + ConstantOperation *constant_operation) +{ + BLI_assert(constant_operation->getNumberOfInputSockets() == 0); + int i = 0; + while (i < m_links.size()) { + Link &link = m_links[i]; + if (&link.to()->getOperation() == operation) { + link.to()->setLink(nullptr); + m_links.remove(i); + continue; + } + + if (&link.from()->getOperation() == operation) { + link.to()->setLink(constant_operation->getOutputSocket()); + m_links[i] = Link(constant_operation->getOutputSocket(), link.to()); + } + i++; + } + addOperation(constant_operation); } void NodeOperationBuilder::mapInputSocket(NodeInput *node_socket, @@ -138,7 +173,7 @@ void NodeOperationBuilder::mapInputSocket(NodeInput *node_socket, BLI_assert(m_current_node); BLI_assert(node_socket->getNode() == m_current_node); - /* note: this maps operation sockets to node sockets. + /* NOTE: this maps operation sockets to node sockets. * for resolving links the map will be inverted first in convertToOperations, * to get a list of links for each node input socket. */ @@ -284,7 +319,7 @@ void NodeOperationBuilder::add_datatype_conversions() void NodeOperationBuilder::add_operation_input_constants() { - /* Note: unconnected inputs cached first to avoid modifying + /* NOTE: unconnected inputs cached first to avoid modifying * m_operations while iterating over it */ Vector<NodeOperationInput *> pending_inputs; @@ -537,7 +572,7 @@ void NodeOperationBuilder::add_output_buffers(NodeOperation *operation, void NodeOperationBuilder::add_complex_operation_buffers() { - /* note: complex ops and get cached here first, since adding operations + /* NOTE: complex ops and get cached here first, since adding operations * will invalidate iterators over the main m_operations */ Vector<NodeOperation *> complex_ops; diff --git a/source/blender/compositor/intern/COM_NodeOperationBuilder.h b/source/blender/compositor/intern/COM_NodeOperationBuilder.h index b2fb822af25..1f76765c846 100644 --- a/source/blender/compositor/intern/COM_NodeOperationBuilder.h +++ b/source/blender/compositor/intern/COM_NodeOperationBuilder.h @@ -41,6 +41,7 @@ class NodeOperationOutput; class PreviewOperation; class WriteBufferOperation; class ViewerOperation; +class ConstantOperation; class NodeOperationBuilder { public: @@ -67,6 +68,7 @@ class NodeOperationBuilder { private: const CompositorContext *m_context; NodeGraph m_graph; + ExecutionSystem *exec_system_; Vector<NodeOperation *> m_operations; Vector<Link> m_links; @@ -86,7 +88,9 @@ class NodeOperationBuilder { ViewerOperation *m_active_viewer; public: - NodeOperationBuilder(const CompositorContext *context, bNodeTree *b_nodetree); + NodeOperationBuilder(const CompositorContext *context, + bNodeTree *b_nodetree, + ExecutionSystem *system); const CompositorContext &context() const { @@ -96,6 +100,8 @@ class NodeOperationBuilder { void convertToOperations(ExecutionSystem *system); void addOperation(NodeOperation *operation); + void replace_operation_with_constant(NodeOperation *operation, + ConstantOperation *constant_operation); /** Map input socket of the current node to an operation socket */ void mapInputSocket(NodeInput *node_socket, NodeOperationInput *operation_socket); diff --git a/source/blender/compositor/intern/COM_OpenCLDevice.cc b/source/blender/compositor/intern/COM_OpenCLDevice.cc index 0f6ed0dbd2b..3409c8fa3bc 100644 --- a/source/blender/compositor/intern/COM_OpenCLDevice.cc +++ b/source/blender/compositor/intern/COM_OpenCLDevice.cc @@ -110,7 +110,7 @@ const cl_image_format *OpenCLDevice::determineImageFormat(MemoryBuffer *memoryBu return &IMAGE_FORMAT_COLOR; break; default: - BLI_assert(!"Unsupported num_channels."); + BLI_assert_msg(0, "Unsupported num_channels."); } return &IMAGE_FORMAT_COLOR; diff --git a/source/blender/compositor/intern/COM_WorkScheduler.cc b/source/blender/compositor/intern/COM_WorkScheduler.cc index 157ded943d6..8e49bf34b51 100644 --- a/source/blender/compositor/intern/COM_WorkScheduler.cc +++ b/source/blender/compositor/intern/COM_WorkScheduler.cc @@ -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); diff --git a/source/blender/compositor/intern/COM_WorkScheduler.h b/source/blender/compositor/intern/COM_WorkScheduler.h index be88859be7c..297943aa63b 100644 --- a/source/blender/compositor/intern/COM_WorkScheduler.h +++ b/source/blender/compositor/intern/COM_WorkScheduler.h @@ -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 diff --git a/source/blender/compositor/intern/COM_compositor.cc b/source/blender/compositor/intern/COM_compositor.cc index 5839f53976b..c05234f3bd0 100644 --- a/source/blender/compositor/intern/COM_compositor.cc +++ b/source/blender/compositor/intern/COM_compositor.cc @@ -70,7 +70,7 @@ void COM_execute(RenderData *render_data, const ColorManagedDisplaySettings *displaySettings, const char *viewName) { - /* Initialize mutex, TODO this mutex init is actually not thread safe and + /* Initialize mutex, TODO: this mutex init is actually not thread safe and * should be done somewhere as part of blender startup, all the other * initializations can be done lazily. */ if (!g_compositor.is_initialized) { |