diff options
Diffstat (limited to 'source/blender/compositor')
48 files changed, 913 insertions, 206 deletions
diff --git a/source/blender/compositor/CMakeLists.txt b/source/blender/compositor/CMakeLists.txt index ac59d832013..20b56ceb55f 100644 --- a/source/blender/compositor/CMakeLists.txt +++ b/source/blender/compositor/CMakeLists.txt @@ -59,6 +59,8 @@ set(SRC intern/COM_ChunkOrderHotspot.h intern/COM_CompositorContext.cc intern/COM_CompositorContext.h + intern/COM_ConstantFolder.cc + intern/COM_ConstantFolder.h intern/COM_Converter.cc intern/COM_Converter.h intern/COM_Debug.cc @@ -66,6 +68,7 @@ set(SRC intern/COM_Device.cc intern/COM_Device.h intern/COM_Enums.cc + intern/COM_Enums.h intern/COM_ExecutionGroup.cc intern/COM_ExecutionGroup.h intern/COM_ExecutionModel.cc @@ -82,6 +85,8 @@ set(SRC intern/COM_MetaData.h intern/COM_MultiThreadedOperation.cc intern/COM_MultiThreadedOperation.h + intern/COM_MultiThreadedRowOperation.cc + intern/COM_MultiThreadedRowOperation.h intern/COM_Node.cc intern/COM_Node.h intern/COM_NodeConverter.cc @@ -442,6 +447,8 @@ set(SRC operations/COM_BrightnessOperation.h operations/COM_ColorCorrectionOperation.cc operations/COM_ColorCorrectionOperation.h + operations/COM_ConstantOperation.cc + operations/COM_ConstantOperation.h operations/COM_GammaOperation.cc operations/COM_GammaOperation.h operations/COM_MixOperation.cc 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_ConstantFolder.cc b/source/blender/compositor/intern/COM_ConstantFolder.cc new file mode 100644 index 00000000000..a9427013f87 --- /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(!"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_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..dfcf76cdd0a 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); } @@ -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 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..89068a7b734 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(); 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_NodeOperation.cc b/source/blender/compositor/intern/COM_NodeOperation.cc index b943ab6af7f..6484c75f364 100644 --- a/source/blender/compositor/intern/COM_NodeOperation.cc +++ b/source/blender/compositor/intern/COM_NodeOperation.cc @@ -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 3036e3f55dd..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); } @@ -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, 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_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/operations/COM_ColorBalanceASCCDLOperation.cc b/source/blender/compositor/operations/COM_ColorBalanceASCCDLOperation.cc index d1d3752e402..aee8c0d52e8 100644 --- a/source/blender/compositor/operations/COM_ColorBalanceASCCDLOperation.cc +++ b/source/blender/compositor/operations/COM_ColorBalanceASCCDLOperation.cc @@ -41,6 +41,7 @@ ColorBalanceASCCDLOperation::ColorBalanceASCCDLOperation() this->m_inputValueOperation = nullptr; this->m_inputColorOperation = nullptr; this->setResolutionInputSocketIndex(1); + flags.can_be_constant = true; } void ColorBalanceASCCDLOperation::initExecution() @@ -76,6 +77,23 @@ void ColorBalanceASCCDLOperation::executePixelSampled(float output[4], output[3] = inputColor[3]; } +void ColorBalanceASCCDLOperation::update_memory_buffer_row(PixelCursor &p) +{ + for (; p.out < p.row_end; p.next()) { + const float *in_factor = p.ins[0]; + const float *in_color = p.ins[1]; + const float fac = MIN2(1.0f, in_factor[0]); + const float fac_m = 1.0f - fac; + p.out[0] = fac_m * in_color[0] + + fac * colorbalance_cdl(in_color[0], m_offset[0], m_power[0], m_slope[0]); + p.out[1] = fac_m * in_color[1] + + fac * colorbalance_cdl(in_color[1], m_offset[1], m_power[1], m_slope[1]); + p.out[2] = fac_m * in_color[2] + + fac * colorbalance_cdl(in_color[2], m_offset[2], m_power[2], m_slope[2]); + p.out[3] = in_color[3]; + } +} + void ColorBalanceASCCDLOperation::deinitExecution() { this->m_inputValueOperation = nullptr; diff --git a/source/blender/compositor/operations/COM_ColorBalanceASCCDLOperation.h b/source/blender/compositor/operations/COM_ColorBalanceASCCDLOperation.h index 5851600190f..d161ea66af2 100644 --- a/source/blender/compositor/operations/COM_ColorBalanceASCCDLOperation.h +++ b/source/blender/compositor/operations/COM_ColorBalanceASCCDLOperation.h @@ -18,7 +18,7 @@ #pragma once -#include "COM_NodeOperation.h" +#include "COM_MultiThreadedRowOperation.h" namespace blender::compositor { @@ -26,7 +26,7 @@ namespace blender::compositor { * this program converts an input color to an output value. * it assumes we are in sRGB color space. */ -class ColorBalanceASCCDLOperation : public NodeOperation { +class ColorBalanceASCCDLOperation : public MultiThreadedRowOperation { protected: /** * Prefetched reference to the inputProgram @@ -71,6 +71,8 @@ class ColorBalanceASCCDLOperation : public NodeOperation { { copy_v3_v3(this->m_slope, slope); } + + void update_memory_buffer_row(PixelCursor &p) override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_ColorBalanceLGGOperation.cc b/source/blender/compositor/operations/COM_ColorBalanceLGGOperation.cc index cac16a3f7b0..674cb79a238 100644 --- a/source/blender/compositor/operations/COM_ColorBalanceLGGOperation.cc +++ b/source/blender/compositor/operations/COM_ColorBalanceLGGOperation.cc @@ -46,6 +46,7 @@ ColorBalanceLGGOperation::ColorBalanceLGGOperation() this->m_inputValueOperation = nullptr; this->m_inputColorOperation = nullptr; this->setResolutionInputSocketIndex(1); + flags.can_be_constant = true; } void ColorBalanceLGGOperation::initExecution() @@ -81,6 +82,23 @@ void ColorBalanceLGGOperation::executePixelSampled(float output[4], output[3] = inputColor[3]; } +void ColorBalanceLGGOperation::update_memory_buffer_row(PixelCursor &p) +{ + for (; p.out < p.row_end; p.next()) { + const float *in_factor = p.ins[0]; + const float *in_color = p.ins[1]; + const float fac = MIN2(1.0f, in_factor[0]); + const float fac_m = 1.0f - fac; + p.out[0] = fac_m * in_color[0] + + fac * colorbalance_lgg(in_color[0], m_lift[0], m_gamma_inv[0], m_gain[0]); + p.out[1] = fac_m * in_color[1] + + fac * colorbalance_lgg(in_color[1], m_lift[1], m_gamma_inv[1], m_gain[1]); + p.out[2] = fac_m * in_color[2] + + fac * colorbalance_lgg(in_color[2], m_lift[2], m_gamma_inv[2], m_gain[2]); + p.out[3] = in_color[3]; + } +} + void ColorBalanceLGGOperation::deinitExecution() { this->m_inputValueOperation = nullptr; diff --git a/source/blender/compositor/operations/COM_ColorBalanceLGGOperation.h b/source/blender/compositor/operations/COM_ColorBalanceLGGOperation.h index 23f70247b66..4bc929ed76c 100644 --- a/source/blender/compositor/operations/COM_ColorBalanceLGGOperation.h +++ b/source/blender/compositor/operations/COM_ColorBalanceLGGOperation.h @@ -18,7 +18,7 @@ #pragma once -#include "COM_NodeOperation.h" +#include "COM_MultiThreadedRowOperation.h" namespace blender::compositor { @@ -26,7 +26,7 @@ namespace blender::compositor { * this program converts an input color to an output value. * it assumes we are in sRGB color space. */ -class ColorBalanceLGGOperation : public NodeOperation { +class ColorBalanceLGGOperation : public MultiThreadedRowOperation { protected: /** * Prefetched reference to the inputProgram @@ -71,6 +71,8 @@ class ColorBalanceLGGOperation : public NodeOperation { { copy_v3_v3(this->m_gamma_inv, gamma_inv); } + + void update_memory_buffer_row(PixelCursor &p) override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_ColorCorrectionOperation.cc b/source/blender/compositor/operations/COM_ColorCorrectionOperation.cc index 168e9b57eb2..b50145b106d 100644 --- a/source/blender/compositor/operations/COM_ColorCorrectionOperation.cc +++ b/source/blender/compositor/operations/COM_ColorCorrectionOperation.cc @@ -33,6 +33,7 @@ ColorCorrectionOperation::ColorCorrectionOperation() this->m_redChannelEnabled = true; this->m_greenChannelEnabled = true; this->m_blueChannelEnabled = true; + flags.can_be_constant = true; } void ColorCorrectionOperation::initExecution() { @@ -157,6 +158,86 @@ void ColorCorrectionOperation::executePixelSampled(float output[4], output[3] = inputImageColor[3]; } +void ColorCorrectionOperation::update_memory_buffer_row(PixelCursor &p) +{ + for (; p.out < p.row_end; p.next()) { + const float *in_color = p.ins[0]; + const float *in_mask = p.ins[1]; + + const float level = (in_color[0] + in_color[1] + in_color[2]) / 3.0f; + float level_shadows = 0.0f; + float level_midtones = 0.0f; + float level_highlights = 0.0f; + constexpr float MARGIN = 0.10f; + constexpr float MARGIN_DIV = 0.5f / MARGIN; + if (level < this->m_data->startmidtones - MARGIN) { + level_shadows = 1.0f; + } + else if (level < this->m_data->startmidtones + MARGIN) { + level_midtones = ((level - this->m_data->startmidtones) * MARGIN_DIV) + 0.5f; + level_shadows = 1.0f - level_midtones; + } + else if (level < this->m_data->endmidtones - MARGIN) { + level_midtones = 1.0f; + } + else if (level < this->m_data->endmidtones + MARGIN) { + level_highlights = ((level - this->m_data->endmidtones) * MARGIN_DIV) + 0.5f; + level_midtones = 1.0f - level_highlights; + } + else { + level_highlights = 1.0f; + } + float contrast = this->m_data->master.contrast; + float saturation = this->m_data->master.saturation; + float gamma = this->m_data->master.gamma; + float gain = this->m_data->master.gain; + float lift = this->m_data->master.lift; + contrast *= level_shadows * this->m_data->shadows.contrast + + level_midtones * this->m_data->midtones.contrast + + level_highlights * this->m_data->highlights.contrast; + saturation *= level_shadows * this->m_data->shadows.saturation + + level_midtones * this->m_data->midtones.saturation + + level_highlights * this->m_data->highlights.saturation; + gamma *= level_shadows * this->m_data->shadows.gamma + + level_midtones * this->m_data->midtones.gamma + + level_highlights * this->m_data->highlights.gamma; + gain *= level_shadows * this->m_data->shadows.gain + + level_midtones * this->m_data->midtones.gain + + level_highlights * this->m_data->highlights.gain; + lift += level_shadows * this->m_data->shadows.lift + + level_midtones * this->m_data->midtones.lift + + level_highlights * this->m_data->highlights.lift; + + const float inv_gamma = 1.0f / gamma; + const float luma = IMB_colormanagement_get_luminance(in_color); + + float r = luma + saturation * (in_color[0] - luma); + float g = luma + saturation * (in_color[1] - luma); + float b = luma + saturation * (in_color[2] - luma); + + r = 0.5f + (r - 0.5f) * contrast; + g = 0.5f + (g - 0.5f) * contrast; + b = 0.5f + (b - 0.5f) * contrast; + + /* Check for negative values to avoid nan. */ + r = color_correct_powf_safe(r * gain + lift, inv_gamma, r); + g = color_correct_powf_safe(g * gain + lift, inv_gamma, g); + b = color_correct_powf_safe(b * gain + lift, inv_gamma, b); + + /* Mix with mask. */ + const float value = MIN2(1.0f, in_mask[0]); + const float m_value = 1.0f - value; + r = m_value * in_color[0] + value * r; + g = m_value * in_color[1] + value * g; + b = m_value * in_color[2] + value * b; + + p.out[0] = m_redChannelEnabled ? r : in_color[0]; + p.out[1] = m_greenChannelEnabled ? g : in_color[1]; + p.out[2] = m_blueChannelEnabled ? b : in_color[2]; + p.out[3] = in_color[3]; + } +} + void ColorCorrectionOperation::deinitExecution() { this->m_inputImage = nullptr; diff --git a/source/blender/compositor/operations/COM_ColorCorrectionOperation.h b/source/blender/compositor/operations/COM_ColorCorrectionOperation.h index c5826ed0152..32b5e02e77a 100644 --- a/source/blender/compositor/operations/COM_ColorCorrectionOperation.h +++ b/source/blender/compositor/operations/COM_ColorCorrectionOperation.h @@ -18,11 +18,11 @@ #pragma once -#include "COM_NodeOperation.h" +#include "COM_MultiThreadedRowOperation.h" namespace blender::compositor { -class ColorCorrectionOperation : public NodeOperation { +class ColorCorrectionOperation : public MultiThreadedRowOperation { private: /** * Cached reference to the inputProgram @@ -69,6 +69,8 @@ class ColorCorrectionOperation : public NodeOperation { { this->m_blueChannelEnabled = enabled; } + + void update_memory_buffer_row(PixelCursor &p) override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_ColorExposureOperation.cc b/source/blender/compositor/operations/COM_ColorExposureOperation.cc index 1512ff87658..228550a31c5 100644 --- a/source/blender/compositor/operations/COM_ColorExposureOperation.cc +++ b/source/blender/compositor/operations/COM_ColorExposureOperation.cc @@ -26,6 +26,7 @@ ExposureOperation::ExposureOperation() this->addInputSocket(DataType::Value); this->addOutputSocket(DataType::Color); this->m_inputProgram = nullptr; + flags.can_be_constant = true; } void ExposureOperation::initExecution() @@ -52,6 +53,19 @@ void ExposureOperation::executePixelSampled(float output[4], output[3] = inputValue[3]; } +void ExposureOperation::update_memory_buffer_row(PixelCursor &p) +{ + for (; p.out < p.row_end; p.next()) { + const float *in_value = p.ins[0]; + const float *in_exposure = p.ins[1]; + const float exposure = pow(2, in_exposure[0]); + p.out[0] = in_value[0] * exposure; + p.out[1] = in_value[1] * exposure; + p.out[2] = in_value[2] * exposure; + p.out[3] = in_value[3]; + } +} + void ExposureOperation::deinitExecution() { this->m_inputProgram = nullptr; diff --git a/source/blender/compositor/operations/COM_ColorExposureOperation.h b/source/blender/compositor/operations/COM_ColorExposureOperation.h index 0cfaa059e41..1eb790e8d52 100644 --- a/source/blender/compositor/operations/COM_ColorExposureOperation.h +++ b/source/blender/compositor/operations/COM_ColorExposureOperation.h @@ -18,11 +18,11 @@ #pragma once -#include "COM_NodeOperation.h" +#include "COM_MultiThreadedRowOperation.h" namespace blender::compositor { -class ExposureOperation : public NodeOperation { +class ExposureOperation : public MultiThreadedRowOperation { private: /** * Cached reference to the inputProgram @@ -47,6 +47,8 @@ class ExposureOperation : public NodeOperation { * Deinitialize the execution */ void deinitExecution() override; + + void update_memory_buffer_row(PixelCursor &p) override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_ConstantOperation.cc b/source/blender/compositor/operations/COM_ConstantOperation.cc new file mode 100644 index 00000000000..f905edbde76 --- /dev/null +++ b/source/blender/compositor/operations/COM_ConstantOperation.cc @@ -0,0 +1,28 @@ +/* + * 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_ConstantOperation.h" + +namespace blender::compositor { + +ConstantOperation::ConstantOperation() +{ + flags.is_constant_operation = true; +} + +} // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_ConstantOperation.h b/source/blender/compositor/operations/COM_ConstantOperation.h new file mode 100644 index 00000000000..2709efeebd8 --- /dev/null +++ b/source/blender/compositor/operations/COM_ConstantOperation.h @@ -0,0 +1,36 @@ +/* + * 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_NodeOperation.h" + +namespace blender::compositor { + +/** + * Base class for primitive constant operations (Color/Vector/Value). The rest of operations that + * can be constant are evaluated into primitives during constant folding. + */ +class ConstantOperation : public NodeOperation { + public: + ConstantOperation(); + + virtual const float *get_constant_elem() = 0; +}; + +} // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_GammaOperation.cc b/source/blender/compositor/operations/COM_GammaOperation.cc index 343e335070a..396d3942b06 100644 --- a/source/blender/compositor/operations/COM_GammaOperation.cc +++ b/source/blender/compositor/operations/COM_GammaOperation.cc @@ -28,6 +28,7 @@ GammaOperation::GammaOperation() this->addOutputSocket(DataType::Color); this->m_inputProgram = nullptr; this->m_inputGammaProgram = nullptr; + flags.can_be_constant = true; } void GammaOperation::initExecution() { @@ -51,6 +52,20 @@ void GammaOperation::executePixelSampled(float output[4], float x, float y, Pixe output[3] = inputValue[3]; } +void GammaOperation::update_memory_buffer_row(PixelCursor &p) +{ + for (; p.out < p.row_end; p.next()) { + const float *in_value = p.ins[0]; + const float *in_gamma = p.ins[1]; + const float gamma = in_gamma[0]; + /* Check for negative to avoid nan's. */ + p.out[0] = in_value[0] > 0.0f ? powf(in_value[0], gamma) : in_value[0]; + p.out[1] = in_value[1] > 0.0f ? powf(in_value[1], gamma) : in_value[1]; + p.out[2] = in_value[2] > 0.0f ? powf(in_value[2], gamma) : in_value[2]; + p.out[3] = in_value[3]; + } +} + void GammaOperation::deinitExecution() { this->m_inputProgram = nullptr; diff --git a/source/blender/compositor/operations/COM_GammaOperation.h b/source/blender/compositor/operations/COM_GammaOperation.h index 034046106d6..713d3d8484f 100644 --- a/source/blender/compositor/operations/COM_GammaOperation.h +++ b/source/blender/compositor/operations/COM_GammaOperation.h @@ -18,11 +18,11 @@ #pragma once -#include "COM_NodeOperation.h" +#include "COM_MultiThreadedRowOperation.h" namespace blender::compositor { -class GammaOperation : public NodeOperation { +class GammaOperation : public MultiThreadedRowOperation { private: /** * Cached reference to the inputProgram @@ -47,6 +47,8 @@ class GammaOperation : public NodeOperation { * Deinitialize the execution */ void deinitExecution() override; + + void update_memory_buffer_row(PixelCursor &p) override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_IDMaskOperation.cc b/source/blender/compositor/operations/COM_IDMaskOperation.cc index 38f8b7e075f..bb11ac8b1be 100644 --- a/source/blender/compositor/operations/COM_IDMaskOperation.cc +++ b/source/blender/compositor/operations/COM_IDMaskOperation.cc @@ -25,6 +25,7 @@ IDMaskOperation::IDMaskOperation() this->addInputSocket(DataType::Value); this->addOutputSocket(DataType::Value); this->flags.complex = true; + flags.can_be_constant = true; } void *IDMaskOperation::initializeTileData(rcti *rect) diff --git a/source/blender/compositor/operations/COM_NormalizeOperation.cc b/source/blender/compositor/operations/COM_NormalizeOperation.cc index faacb429f71..f93afcaab95 100644 --- a/source/blender/compositor/operations/COM_NormalizeOperation.cc +++ b/source/blender/compositor/operations/COM_NormalizeOperation.cc @@ -36,7 +36,7 @@ void NormalizeOperation::initExecution() void NormalizeOperation::executePixel(float output[4], int x, int y, void *data) { - /* using generic two floats struct to store x: min y: mult */ + /* using generic two floats struct to store `x: min`, `y: multiply` */ NodeTwoFloats *minmult = (NodeTwoFloats *)data; this->m_imageReader->read(output, x, y, nullptr); @@ -89,7 +89,7 @@ void *NormalizeOperation::initializeTileData(rcti *rect) lockMutex(); if (this->m_cachedInstance == nullptr) { MemoryBuffer *tile = (MemoryBuffer *)this->m_imageReader->initializeTileData(rect); - /* using generic two floats struct to store x: min y: mult */ + /* using generic two floats struct to store `x: min`, `y: multiply`. */ NodeTwoFloats *minmult = new NodeTwoFloats(); float *buffer = tile->getBuffer(); diff --git a/source/blender/compositor/operations/COM_NormalizeOperation.h b/source/blender/compositor/operations/COM_NormalizeOperation.h index 93d4a0fc67d..c89ba372189 100644 --- a/source/blender/compositor/operations/COM_NormalizeOperation.h +++ b/source/blender/compositor/operations/COM_NormalizeOperation.h @@ -36,7 +36,7 @@ class NormalizeOperation : public NodeOperation { /** * \brief temporarily cache of the execution storage - * it stores x->min and y->mult + * it stores `x->min` and `y->multiply`. */ NodeTwoFloats *m_cachedInstance; diff --git a/source/blender/compositor/operations/COM_SMAAOperation.cc b/source/blender/compositor/operations/COM_SMAAOperation.cc index 9dde73269fc..c3647a39909 100644 --- a/source/blender/compositor/operations/COM_SMAAOperation.cc +++ b/source/blender/compositor/operations/COM_SMAAOperation.cc @@ -36,12 +36,12 @@ namespace blender::compositor { * * http://www.iryoku.com/smaa/ * - * This file is based on smaa-cpp: + * This file is based on SMAA-CPP: * * https://github.com/iRi-E/smaa-cpp * * Currently only SMAA 1x mode is provided, so the operation will be done - * with no spatial multisampling nor temporal supersampling. + * with no spatial multi-sampling nor temporal super-sampling. * * NOTE: This program assumes the screen coordinates are DirectX style, so * the vertical direction is upside-down. "top" and "bottom" actually mean diff --git a/source/blender/compositor/operations/COM_SetColorOperation.cc b/source/blender/compositor/operations/COM_SetColorOperation.cc index 79dee33e266..bfe735aab15 100644 --- a/source/blender/compositor/operations/COM_SetColorOperation.cc +++ b/source/blender/compositor/operations/COM_SetColorOperation.cc @@ -44,8 +44,7 @@ void SetColorOperation::determineResolution(unsigned int resolution[2], void SetColorOperation::update_memory_buffer(MemoryBuffer *output, const rcti &area, - Span<MemoryBuffer *> UNUSED(inputs), - ExecutionSystem &UNUSED(exec_system)) + Span<MemoryBuffer *> UNUSED(inputs)) { BLI_assert(output->is_a_single_elem()); float *out_elem = output->get_elem(area.xmin, area.ymin); diff --git a/source/blender/compositor/operations/COM_SetColorOperation.h b/source/blender/compositor/operations/COM_SetColorOperation.h index 2e22ef60ba4..f4c0948ee1b 100644 --- a/source/blender/compositor/operations/COM_SetColorOperation.h +++ b/source/blender/compositor/operations/COM_SetColorOperation.h @@ -18,7 +18,7 @@ #pragma once -#include "COM_NodeOperation.h" +#include "COM_ConstantOperation.h" namespace blender::compositor { @@ -26,7 +26,7 @@ namespace blender::compositor { * this program converts an input color to an output value. * it assumes we are in sRGB color space. */ -class SetColorOperation : public NodeOperation { +class SetColorOperation : public ConstantOperation { private: float m_color[4]; @@ -36,6 +36,11 @@ class SetColorOperation : public NodeOperation { */ SetColorOperation(); + const float *get_constant_elem() override + { + return m_color; + } + float getChannel1() { return this->m_color[0]; @@ -83,8 +88,7 @@ class SetColorOperation : public NodeOperation { 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/operations/COM_SetValueOperation.cc b/source/blender/compositor/operations/COM_SetValueOperation.cc index 359647c8fe3..c12fb106afd 100644 --- a/source/blender/compositor/operations/COM_SetValueOperation.cc +++ b/source/blender/compositor/operations/COM_SetValueOperation.cc @@ -44,8 +44,7 @@ void SetValueOperation::determineResolution(unsigned int resolution[2], void SetValueOperation::update_memory_buffer(MemoryBuffer *output, const rcti &area, - Span<MemoryBuffer *> UNUSED(inputs), - ExecutionSystem &UNUSED(exec_system)) + Span<MemoryBuffer *> UNUSED(inputs)) { BLI_assert(output->is_a_single_elem()); float *out_elem = output->get_elem(area.xmin, area.ymin); diff --git a/source/blender/compositor/operations/COM_SetValueOperation.h b/source/blender/compositor/operations/COM_SetValueOperation.h index acde5aa03b7..f18d44d9554 100644 --- a/source/blender/compositor/operations/COM_SetValueOperation.h +++ b/source/blender/compositor/operations/COM_SetValueOperation.h @@ -18,7 +18,7 @@ #pragma once -#include "COM_NodeOperation.h" +#include "COM_ConstantOperation.h" namespace blender::compositor { @@ -26,7 +26,7 @@ namespace blender::compositor { * this program converts an input color to an output value. * it assumes we are in sRGB color space. */ -class SetValueOperation : public NodeOperation { +class SetValueOperation : public ConstantOperation { private: float m_value; @@ -36,6 +36,11 @@ class SetValueOperation : public NodeOperation { */ SetValueOperation(); + const float *get_constant_elem() override + { + return &m_value; + } + float getValue() { return this->m_value; @@ -53,8 +58,7 @@ class SetValueOperation : public NodeOperation { unsigned int preferredResolution[2]) override; 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/operations/COM_SetVectorOperation.cc b/source/blender/compositor/operations/COM_SetVectorOperation.cc index 7152d5e61d4..7b8cf44048c 100644 --- a/source/blender/compositor/operations/COM_SetVectorOperation.cc +++ b/source/blender/compositor/operations/COM_SetVectorOperation.cc @@ -32,9 +32,9 @@ void SetVectorOperation::executePixelSampled(float output[4], float /*y*/, PixelSampler /*sampler*/) { - output[0] = this->m_x; - output[1] = this->m_y; - output[2] = this->m_z; + output[0] = vector_.x; + output[1] = vector_.y; + output[2] = vector_.z; } void SetVectorOperation::determineResolution(unsigned int resolution[2], diff --git a/source/blender/compositor/operations/COM_SetVectorOperation.h b/source/blender/compositor/operations/COM_SetVectorOperation.h index b444339fcb2..41fd06659d6 100644 --- a/source/blender/compositor/operations/COM_SetVectorOperation.h +++ b/source/blender/compositor/operations/COM_SetVectorOperation.h @@ -18,7 +18,7 @@ #pragma once -#include "COM_NodeOperation.h" +#include "COM_ConstantOperation.h" namespace blender::compositor { @@ -26,12 +26,14 @@ namespace blender::compositor { * this program converts an input color to an output value. * it assumes we are in sRGB color space. */ -class SetVectorOperation : public NodeOperation { +class SetVectorOperation : public ConstantOperation { private: - float m_x; - float m_y; - float m_z; - float m_w; + struct { + float x; + float y; + float z; + float w; + } vector_; public: /** @@ -39,37 +41,42 @@ class SetVectorOperation : public NodeOperation { */ SetVectorOperation(); + const float *get_constant_elem() override + { + return reinterpret_cast<float *>(&vector_); + } + float getX() { - return this->m_x; + return vector_.x; } void setX(float value) { - this->m_x = value; + vector_.x = value; } float getY() { - return this->m_y; + return vector_.y; } void setY(float value) { - this->m_y = value; + vector_.y = value; } float getZ() { - return this->m_z; + return vector_.z; } void setZ(float value) { - this->m_z = value; + vector_.z = value; } float getW() { - return this->m_w; + return vector_.w; } void setW(float value) { - this->m_w = value; + vector_.w = value; } /** diff --git a/source/blender/compositor/operations/COM_VectorBlurOperation.cc b/source/blender/compositor/operations/COM_VectorBlurOperation.cc index 5adcc4fdd4c..b5b5d426338 100644 --- a/source/blender/compositor/operations/COM_VectorBlurOperation.cc +++ b/source/blender/compositor/operations/COM_VectorBlurOperation.cc @@ -432,7 +432,7 @@ void antialias_tagbuf(int xsize, int ysize, char *rectmove) } } - /* 2: evaluate horizontal scanlines and calculate alphas */ + /* 2: evaluate horizontal scan-lines and calculate alphas. */ row1 = rectmove; for (y = 0; y < ysize; y++) { row1++; @@ -463,7 +463,7 @@ void antialias_tagbuf(int xsize, int ysize, char *rectmove) } } - /* 3: evaluate vertical scanlines and calculate alphas */ + /* 3: evaluate vertical scan-lines and calculate alphas */ /* use for reading a copy of the original tagged buffer */ for (x = 0; x < xsize; x++) { row1 = rectmove + x + xsize; |