Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'source/blender/compositor/intern')
-rw-r--r--source/blender/compositor/intern/COM_BufferOperation.cc22
-rw-r--r--source/blender/compositor/intern/COM_BufferOperation.h7
-rw-r--r--source/blender/compositor/intern/COM_CompositorContext.cc2
-rw-r--r--source/blender/compositor/intern/COM_ConstantFolder.cc191
-rw-r--r--source/blender/compositor/intern/COM_ConstantFolder.h64
-rw-r--r--source/blender/compositor/intern/COM_Converter.cc7
-rw-r--r--source/blender/compositor/intern/COM_Debug.cc43
-rw-r--r--source/blender/compositor/intern/COM_Debug.h4
-rw-r--r--source/blender/compositor/intern/COM_ExecutionModel.cc6
-rw-r--r--source/blender/compositor/intern/COM_ExecutionModel.h9
-rw-r--r--source/blender/compositor/intern/COM_ExecutionSystem.cc76
-rw-r--r--source/blender/compositor/intern/COM_ExecutionSystem.h6
-rw-r--r--source/blender/compositor/intern/COM_FullFrameExecutionModel.cc100
-rw-r--r--source/blender/compositor/intern/COM_FullFrameExecutionModel.h13
-rw-r--r--source/blender/compositor/intern/COM_MemoryBuffer.cc12
-rw-r--r--source/blender/compositor/intern/COM_MemoryBuffer.h32
-rw-r--r--source/blender/compositor/intern/COM_MultiThreadedOperation.cc5
-rw-r--r--source/blender/compositor/intern/COM_MultiThreadedOperation.h3
-rw-r--r--source/blender/compositor/intern/COM_MultiThreadedRowOperation.cc50
-rw-r--r--source/blender/compositor/intern/COM_MultiThreadedRowOperation.h60
-rw-r--r--source/blender/compositor/intern/COM_NodeGraph.cc2
-rw-r--r--source/blender/compositor/intern/COM_NodeOperation.cc28
-rw-r--r--source/blender/compositor/intern/COM_NodeOperation.h36
-rw-r--r--source/blender/compositor/intern/COM_NodeOperationBuilder.cc47
-rw-r--r--source/blender/compositor/intern/COM_NodeOperationBuilder.h8
-rw-r--r--source/blender/compositor/intern/COM_OpenCLDevice.cc2
-rw-r--r--source/blender/compositor/intern/COM_WorkScheduler.cc4
-rw-r--r--source/blender/compositor/intern/COM_WorkScheduler.h2
-rw-r--r--source/blender/compositor/intern/COM_compositor.cc2
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) {