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:
authorManuel Castilla <manzanillawork@gmail.com>2021-07-22 22:55:42 +0300
committerManuel Castilla <manzanillawork@gmail.com>2021-07-22 22:55:42 +0300
commit585a891ab2e73c45a7225fa854cf92c559c897fb (patch)
tree07d930f19e5eafc759ed693eb3d6fa506b3a3bdc /source/blender/compositor/intern
parent89b5731f19cc4c5ac15950c3c2ed8c7440c3c80a (diff)
parent1a91c5732032621ff58677178a93f5e6a9d2b8f8 (diff)
Merge branch 'master' into compositor-full-frame
Diffstat (limited to 'source/blender/compositor/intern')
-rw-r--r--source/blender/compositor/intern/COM_BufferArea.h215
-rw-r--r--source/blender/compositor/intern/COM_BufferOperation.cc18
-rw-r--r--source/blender/compositor/intern/COM_BufferOperation.h4
-rw-r--r--source/blender/compositor/intern/COM_BufferRange.h171
-rw-r--r--source/blender/compositor/intern/COM_BuffersIterator.h195
-rw-r--r--source/blender/compositor/intern/COM_CompositorContext.cc2
-rw-r--r--source/blender/compositor/intern/COM_CompositorContext.h8
-rw-r--r--source/blender/compositor/intern/COM_ConstantFolder.cc165
-rw-r--r--source/blender/compositor/intern/COM_ConstantFolder.h40
-rw-r--r--source/blender/compositor/intern/COM_Converter.cc7
-rw-r--r--source/blender/compositor/intern/COM_Debug.cc77
-rw-r--r--source/blender/compositor/intern/COM_Debug.h19
-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.cc252
-rw-r--r--source/blender/compositor/intern/COM_FullFrameExecutionModel.h26
-rw-r--r--source/blender/compositor/intern/COM_MemoryBuffer.cc334
-rw-r--r--source/blender/compositor/intern/COM_MemoryBuffer.h137
-rw-r--r--source/blender/compositor/intern/COM_MemoryProxy.h4
-rw-r--r--source/blender/compositor/intern/COM_MetaData.cc2
-rw-r--r--source/blender/compositor/intern/COM_MultiThreadedOperation.cc18
-rw-r--r--source/blender/compositor/intern/COM_MultiThreadedOperation.h31
-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.cc33
-rw-r--r--source/blender/compositor/intern/COM_NodeOperation.h38
-rw-r--r--source/blender/compositor/intern/COM_NodeOperationBuilder.cc48
-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_SharedOperationBuffers.cc5
-rw-r--r--source/blender/compositor/intern/COM_SharedOperationBuffers.h1
-rw-r--r--source/blender/compositor/intern/COM_WorkPackage.h4
-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
38 files changed, 1645 insertions, 436 deletions
diff --git a/source/blender/compositor/intern/COM_BufferArea.h b/source/blender/compositor/intern/COM_BufferArea.h
new file mode 100644
index 00000000000..6f7756ecbfc
--- /dev/null
+++ b/source/blender/compositor/intern/COM_BufferArea.h
@@ -0,0 +1,215 @@
+/*
+ * 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_assert.h"
+#include "BLI_rect.h"
+#include <iterator>
+
+namespace blender::compositor {
+
+/* Forward declarations. */
+template<typename T> class BufferAreaIterator;
+
+/**
+ * A rectangle area of buffer elements.
+ */
+template<typename T> class BufferArea : rcti {
+ public:
+ using Iterator = BufferAreaIterator<T>;
+ using ConstIterator = BufferAreaIterator<const T>;
+
+ private:
+ T *buffer_;
+ /* Number of elements in a buffer row. */
+ int buffer_width_;
+ /* Buffer element stride. */
+ int elem_stride_;
+
+ public:
+ constexpr BufferArea() = default;
+
+ /**
+ * Create a buffer area containing given rectangle area.
+ */
+ constexpr BufferArea(T *buffer, int buffer_width, const rcti &area, int elem_stride = 1)
+ : rcti(area), buffer_(buffer), buffer_width_(buffer_width), elem_stride_(elem_stride)
+ {
+ }
+
+ /**
+ * Create a buffer area containing whole buffer with no offsets.
+ */
+ constexpr BufferArea(T *buffer, int buffer_width, int buffer_height, int elem_stride = 1)
+ : buffer_(buffer), buffer_width_(buffer_width), elem_stride_(elem_stride)
+ {
+ BLI_rcti_init(this, 0, buffer_width, 0, buffer_height);
+ }
+
+ constexpr friend bool operator==(const BufferArea &a, const BufferArea &b)
+ {
+ return a.buffer_ == b.buffer_ && BLI_rcti_compare(&a, &b) && a.elem_stride_ == b.elem_stride_;
+ }
+
+ constexpr const rcti &get_rect() const
+ {
+ return *this;
+ }
+
+ /**
+ * Number of elements in a row.
+ */
+ constexpr int width() const
+ {
+ return BLI_rcti_size_x(this);
+ }
+
+ /**
+ * Number of elements in a column.
+ */
+ constexpr int height() const
+ {
+ return BLI_rcti_size_y(this);
+ }
+
+ constexpr Iterator begin()
+ {
+ return begin_iterator<Iterator>();
+ }
+
+ constexpr Iterator end()
+ {
+ return end_iterator<Iterator>();
+ }
+
+ constexpr ConstIterator begin() const
+ {
+ return begin_iterator<ConstIterator>();
+ }
+
+ constexpr ConstIterator end() const
+ {
+ return end_iterator<ConstIterator>();
+ }
+
+ private:
+ template<typename TIterator> constexpr TIterator begin_iterator() const
+ {
+ T *end_ptr = get_end_ptr();
+ if (elem_stride_ == 0) {
+ /* Iterate a single element. */
+ return TIterator(buffer_, end_ptr, 1, 1, 1);
+ }
+
+ T *begin_ptr = buffer_ + (intptr_t)this->ymin * buffer_width_ * elem_stride_ +
+ (intptr_t)this->xmin * elem_stride_;
+ return TIterator(begin_ptr, end_ptr, buffer_width_, BLI_rcti_size_x(this), elem_stride_);
+ }
+
+ template<typename TIterator> constexpr TIterator end_iterator() const
+ {
+ T *end_ptr = get_end_ptr();
+ if (elem_stride_ == 0) {
+ /* Iterate a single element. */
+ return TIterator(end_ptr, end_ptr, 1, 1, 1);
+ }
+
+ return TIterator(end_ptr, end_ptr, buffer_width_, BLI_rcti_size_x(this), elem_stride_);
+ }
+
+ T *get_end_ptr() const
+ {
+ if (elem_stride_ == 0) {
+ return buffer_ + 1;
+ }
+ return buffer_ + (intptr_t)(this->ymax - 1) * buffer_width_ * elem_stride_ +
+ (intptr_t)this->xmax * elem_stride_;
+ }
+};
+
+template<typename T> class BufferAreaIterator {
+ public:
+ using iterator_category = std::input_iterator_tag;
+ using value_type = T *;
+ using pointer = T *const *;
+ using reference = T *const &;
+ using difference_type = std::ptrdiff_t;
+
+ private:
+ int elem_stride_;
+ int row_stride_;
+ /* Stride between a row end and the next row start. */
+ int rows_gap_;
+ T *current_;
+ const T *row_end_;
+ const T *end_;
+
+ public:
+ constexpr BufferAreaIterator() = default;
+
+ constexpr BufferAreaIterator(
+ T *current, const T *end, int buffer_width, int area_width, int elem_stride = 1)
+ : elem_stride_(elem_stride),
+ row_stride_(buffer_width * elem_stride),
+ rows_gap_(row_stride_ - area_width * elem_stride),
+ current_(current),
+ row_end_(current + area_width * elem_stride),
+ end_(end)
+ {
+ }
+
+ constexpr BufferAreaIterator &operator++()
+ {
+ current_ += elem_stride_;
+ BLI_assert(current_ <= row_end_);
+ if (current_ == row_end_) {
+ BLI_assert(current_ <= end_);
+ if (current_ == end_) {
+ return *this;
+ }
+ current_ += rows_gap_;
+ row_end_ += row_stride_;
+ }
+ return *this;
+ }
+
+ constexpr BufferAreaIterator operator++(int) const
+ {
+ BufferAreaIterator copied_iterator = *this;
+ ++copied_iterator;
+ return copied_iterator;
+ }
+
+ constexpr friend bool operator!=(const BufferAreaIterator &a, const BufferAreaIterator &b)
+ {
+ return a.current_ != b.current_;
+ }
+
+ constexpr friend bool operator==(const BufferAreaIterator &a, const BufferAreaIterator &b)
+ {
+ return a.current_ == b.current_;
+ }
+
+ constexpr T *operator*() const
+ {
+ return current_;
+ }
+};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/intern/COM_BufferOperation.cc b/source/blender/compositor/intern/COM_BufferOperation.cc
index 561a754a500..ae587fa87c4 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) : ConstantOperation()
{
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,10 @@ BufferOperation::BufferOperation(MemoryBuffer *buffer, DataType data_type) : Con
resolution[1] = buffer->getHeight();
setResolution(resolution);
addOutputSocket(data_type);
-
flags.is_constant_operation = buffer_->is_a_single_elem();
}
-float *BufferOperation::get_constant_elem()
+const float *BufferOperation::get_constant_elem()
{
BLI_assert(buffer_->is_a_single_elem());
return buffer_->getBuffer();
@@ -42,7 +42,19 @@ float *BufferOperation::get_constant_elem()
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 2fa90b872ae..705264c37b7 100644
--- a/source/blender/compositor/intern/COM_BufferOperation.h
+++ b/source/blender/compositor/intern/COM_BufferOperation.h
@@ -25,12 +25,14 @@ namespace blender::compositor {
class BufferOperation : public ConstantOperation {
private:
MemoryBuffer *buffer_;
+ MemoryBuffer *inflated_buffer_;
public:
BufferOperation(MemoryBuffer *buffer, DataType data_type);
- float *get_constant_elem();
+ 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_BufferRange.h b/source/blender/compositor/intern/COM_BufferRange.h
new file mode 100644
index 00000000000..ffdf1f2f1e5
--- /dev/null
+++ b/source/blender/compositor/intern/COM_BufferRange.h
@@ -0,0 +1,171 @@
+/*
+ * 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_assert.h"
+#include "BLI_rect.h"
+
+#include <iterator>
+
+namespace blender::compositor {
+
+/* Forward declarations. */
+template<typename T> class BufferRangeIterator;
+
+/**
+ * A range of buffer elements.
+ */
+template<typename T> class BufferRange {
+ public:
+ using Iterator = BufferRangeIterator<T>;
+ using ConstIterator = BufferRangeIterator<const T>;
+
+ private:
+ T *start_;
+ /* Number of elements in the range. */
+ int64_t size_;
+ /* Buffer element stride. */
+ int elem_stride_;
+
+ public:
+ constexpr BufferRange() = default;
+
+ /**
+ * Create a buffer range of elements from a given element index.
+ */
+ constexpr BufferRange(T *buffer, int64_t start_elem_index, int64_t size, int elem_stride = 1)
+ : start_(buffer + start_elem_index * elem_stride), size_(size), elem_stride_(elem_stride)
+ {
+ }
+
+ constexpr friend bool operator==(const BufferRange &a, const BufferRange &b)
+ {
+ return a.start_ == b.start_ && a.size_ == b.size_ && a.elem_stride_ == b.elem_stride_;
+ }
+
+ /**
+ * Access an element in the range. Index is relative to range start.
+ */
+ constexpr T *operator[](int64_t index) const
+ {
+ BLI_assert(index >= 0);
+ BLI_assert(index < this->size());
+ return start_ + index * elem_stride_;
+ }
+
+ /**
+ * Get the number of elements in the range.
+ */
+ constexpr int64_t size() const
+ {
+ return size_;
+ }
+
+ constexpr Iterator begin()
+ {
+ return begin_iterator<Iterator>();
+ }
+
+ constexpr Iterator end()
+ {
+ return end_iterator<Iterator>();
+ }
+
+ constexpr ConstIterator begin() const
+ {
+ return begin_iterator<ConstIterator>();
+ }
+
+ constexpr ConstIterator end() const
+ {
+ return end_iterator<ConstIterator>();
+ }
+
+ private:
+ template<typename TIterator> constexpr TIterator begin_iterator() const
+ {
+ if (elem_stride_ == 0) {
+ /* Iterate a single element. */
+ return TIterator(start_, 1);
+ }
+
+ return TIterator(start_, elem_stride_);
+ }
+
+ template<typename TIterator> constexpr TIterator end_iterator() const
+ {
+ if (elem_stride_ == 0) {
+ /* Iterate a single element. */
+ return TIterator(start_ + 1, 1);
+ }
+
+ return TIterator(start_ + size_ * elem_stride_, elem_stride_);
+ }
+};
+
+template<typename T> class BufferRangeIterator {
+ public:
+ using iterator_category = std::input_iterator_tag;
+ using value_type = T *;
+ using pointer = T *const *;
+ using reference = T *const &;
+ using difference_type = std::ptrdiff_t;
+
+ private:
+ T *current_;
+ int elem_stride_;
+
+ public:
+ constexpr BufferRangeIterator() = default;
+
+ constexpr BufferRangeIterator(T *current, int elem_stride = 1)
+ : current_(current), elem_stride_(elem_stride)
+ {
+ }
+
+ constexpr BufferRangeIterator &operator++()
+ {
+ current_ += elem_stride_;
+ return *this;
+ }
+
+ constexpr BufferRangeIterator operator++(int) const
+ {
+ BufferRangeIterator copied_iterator = *this;
+ ++copied_iterator;
+ return copied_iterator;
+ }
+
+ constexpr friend bool operator!=(const BufferRangeIterator &a, const BufferRangeIterator &b)
+ {
+ return a.current_ != b.current_;
+ }
+
+ constexpr friend bool operator==(const BufferRangeIterator &a, const BufferRangeIterator &b)
+ {
+ return a.current_ == b.current_;
+ }
+
+ constexpr T *operator*() const
+ {
+ return current_;
+ }
+};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/intern/COM_BuffersIterator.h b/source/blender/compositor/intern/COM_BuffersIterator.h
new file mode 100644
index 00000000000..bfe0b7a3d45
--- /dev/null
+++ b/source/blender/compositor/intern/COM_BuffersIterator.h
@@ -0,0 +1,195 @@
+/*
+ * 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_rect.h"
+#include "BLI_vector.hh"
+
+namespace blender::compositor {
+
+/**
+ * Builds an iterator for simultaneously iterating an area of elements in an output buffer and any
+ * number of input buffers. It's not a standard C++ iterator and it does not support neither
+ * deference, equality or postfix increment operators.
+ */
+template<typename T> class BuffersIteratorBuilder {
+ public:
+ class Iterator {
+ int x_start_;
+ int x_end_;
+ const T *out_end_;
+ int out_elem_stride_;
+ /* Stride between an output row end and the next row start. */
+ int out_rows_gap_;
+
+ struct In {
+ int elem_stride;
+ int rows_gap;
+ const T *in;
+ };
+ Vector<In, 6> ins_;
+
+ friend class BuffersIteratorBuilder;
+
+ public:
+ int x;
+ int y;
+ /** Current output element. */
+ T *out;
+
+ public:
+ /**
+ * Get current element from an input.
+ */
+ const T *in(int input_index) const
+ {
+ BLI_assert(input_index < ins_.size());
+ return ins_[input_index].in;
+ }
+
+ int get_num_inputs() const
+ {
+ return ins_.size();
+ }
+
+ /**
+ * Has the end of the area been reached.
+ */
+ bool is_end() const
+ {
+ return out >= out_end_;
+ }
+
+ /**
+ * Go to the next element in the area.
+ */
+ void next()
+ {
+ out += out_elem_stride_;
+ for (In &in : ins_) {
+ in.in += in.elem_stride;
+ }
+ x++;
+ if (x == x_end_) {
+ x = x_start_;
+ y++;
+ out += out_rows_gap_;
+ for (In &in : ins_) {
+ in.in += in.rows_gap;
+ }
+ }
+ }
+
+ Iterator &operator++()
+ {
+ this->next();
+ return *this;
+ }
+ };
+
+ private:
+ Iterator iterator_;
+ rcti area_;
+ bool is_built_;
+
+ public:
+ /**
+ * Create a buffers iterator builder to iterate given output buffer area.
+ * \param output: Output buffer.
+ * \param buffer_area: Whole output buffer area (may have offset position).
+ * \param iterated_area: Area to be iterated in all buffers.
+ * \param elem_stride: Output buffer element stride.
+ */
+ BuffersIteratorBuilder(T *output,
+ const rcti &buffer_area,
+ const rcti &iterated_area,
+ int elem_stride = 1)
+ : area_(iterated_area), is_built_(false)
+ {
+ BLI_assert(BLI_rcti_inside_rcti(&buffer_area, &iterated_area));
+ iterator_.x = iterated_area.xmin;
+ iterator_.y = iterated_area.ymin;
+ iterator_.x_start_ = iterated_area.xmin;
+ iterator_.x_end_ = iterated_area.xmax;
+
+ iterator_.out_elem_stride_ = elem_stride;
+ const int buffer_width = BLI_rcti_size_x(&buffer_area);
+ intptr_t out_row_stride = buffer_width * elem_stride;
+ iterator_.out_rows_gap_ = out_row_stride - BLI_rcti_size_x(&iterated_area) * elem_stride;
+ const int out_start_x = iterated_area.xmin - buffer_area.xmin;
+ const int out_start_y = iterated_area.ymin - buffer_area.ymin;
+ iterator_.out = output + (intptr_t)out_start_y * out_row_stride +
+ (intptr_t)out_start_x * elem_stride;
+ const T *out_row_end_ = iterator_.out +
+ (intptr_t)BLI_rcti_size_x(&iterated_area) * elem_stride;
+ iterator_.out_end_ = out_row_end_ +
+ (intptr_t)out_row_stride * (BLI_rcti_size_y(&iterated_area) - 1);
+ }
+
+ /**
+ * Create a buffers iterator builder to iterate given output buffer with no offsets.
+ */
+ BuffersIteratorBuilder(T *output, int buffer_width, int buffer_height, int elem_stride = 1)
+ : BuffersIteratorBuilder(output,
+ {0, buffer_width, 0, buffer_height},
+ {0, buffer_width, 0, buffer_height},
+ elem_stride)
+ {
+ }
+
+ /**
+ * Add an input buffer to be iterated. It must contain iterated area.
+ */
+ void add_input(const T *input, const rcti &buffer_area, int elem_stride = 1)
+ {
+ BLI_assert(!is_built_);
+ BLI_assert(BLI_rcti_inside_rcti(&buffer_area, &area_));
+ typename Iterator::In in;
+ in.elem_stride = elem_stride;
+ const int buffer_width = BLI_rcti_size_x(&buffer_area);
+ in.rows_gap = buffer_width * elem_stride - BLI_rcti_size_x(&area_) * elem_stride;
+ const int in_start_x = area_.xmin - buffer_area.xmin;
+ const int in_start_y = area_.ymin - buffer_area.ymin;
+ in.in = input + in_start_y * buffer_width * elem_stride + in_start_x * elem_stride;
+ iterator_.ins_.append(std::move(in));
+ }
+
+ /**
+ * Add an input buffer to be iterated with no offsets. It must contain iterated area.
+ */
+ void add_input(const T *input, int buffer_width, int elem_stride = 1)
+ {
+ rcti buffer_area;
+ BLI_rcti_init(&buffer_area, 0, buffer_width, 0, area_.ymax);
+ add_input(input, buffer_area, elem_stride);
+ }
+
+ /**
+ * Build the iterator.
+ */
+ BuffersIteratorBuilder::Iterator build()
+ {
+ is_built_ = true;
+ return iterator_;
+ }
+};
+
+template<typename T> using BuffersIterator = typename BuffersIteratorBuilder<T>::Iterator;
+
+} // namespace blender::compositor
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_CompositorContext.h b/source/blender/compositor/intern/COM_CompositorContext.h
index 56251511576..403ec62e359 100644
--- a/source/blender/compositor/intern/COM_CompositorContext.h
+++ b/source/blender/compositor/intern/COM_CompositorContext.h
@@ -171,7 +171,7 @@ class CompositorContext {
}
/**
- * \brief set view settings of color color management
+ * \brief set view settings of color management
*/
void setViewSettings(const ColorManagedViewSettings *viewSettings)
{
@@ -179,7 +179,7 @@ class CompositorContext {
}
/**
- * \brief get view settings of color color management
+ * \brief get view settings of color management
*/
const ColorManagedViewSettings *getViewSettings() const
{
@@ -187,7 +187,7 @@ class CompositorContext {
}
/**
- * \brief set display settings of color color management
+ * \brief set display settings of color management
*/
void setDisplaySettings(const ColorManagedDisplaySettings *displaySettings)
{
@@ -195,7 +195,7 @@ class CompositorContext {
}
/**
- * \brief get display settings of color color management
+ * \brief get display settings of color management
*/
const ColorManagedDisplaySettings *getDisplaySettings() const
{
diff --git a/source/blender/compositor/intern/COM_ConstantFolder.cc b/source/blender/compositor/intern/COM_ConstantFolder.cc
index a2b13fc3563..15450572958 100644
--- a/source/blender/compositor/intern/COM_ConstantFolder.cc
+++ b/source/blender/compositor/intern/COM_ConstantFolder.cc
@@ -13,59 +13,52 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
- * Copyright 2011, Blender Foundation.
+ * Copyright 2021, Blender Foundation.
*/
-#include "COM_ConstantFolder.h"
#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 <limits>
+#include "COM_WorkScheduler.h"
namespace blender::compositor {
+using Link = NodeOperationBuilder::Link;
+
/**
- * \param operations: Container of operations to fold. Folded operations will be replaced
- * with constant operations.
- * \param output_links_map: Operations output links. Folded operations links will be relinked to
- * their constant operation result.
+ * \param operations_builder: Contains all operations to fold.
* \param exec_system: Execution system.
*/
-ConstantFolder::ConstantFolder(Vector<NodeOperation *> &operations,
- Map<NodeOperation *, Set<NodeOperation *>> &output_links,
- ExecutionSystem &exec_system)
- : all_operations_(operations), output_links_(output_links), exec_system_(exec_system)
+ConstantFolder::ConstantFolder(NodeOperationBuilder &operations_builder)
+ : operations_builder_(operations_builder)
{
- BLI_rcti_init(&max_area_,
- std::numeric_limits<int>::min(),
- std::numeric_limits<int>::max(),
- std::numeric_limits<int>::min(),
- std::numeric_limits<int>::max());
+ 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 *op)
+static bool is_constant_foldable(NodeOperation *operation)
{
- if (!op->get_flags().can_be_constant || op->get_flags().is_constant_operation) {
- return false;
- }
- for (int i = 0; i < op->getNumberOfInputSockets(); i++) {
- if (!op->get_input_operation(i)->get_flags().is_constant_operation) {
- return false;
+ 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 true;
+ return false;
}
-static Vector<NodeOperation *> find_constant_foldable_operations(Span<NodeOperation *> operations)
+static Set<NodeOperation *> find_constant_foldable_operations(Span<NodeOperation *> operations)
{
- Vector<NodeOperation *> foldable_ops;
+ Set<NodeOperation *> foldable_ops;
for (NodeOperation *op : operations) {
if (is_constant_foldable(op)) {
- foldable_ops.append(op);
+ foldable_ops.add(op);
}
}
return foldable_ops;
@@ -90,84 +83,45 @@ static ConstantOperation *create_constant_operation(DataType data_type, const fl
return value_op;
}
default: {
- BLI_assert(!"Non implemented data type");
+ BLI_assert_msg(0, "Non implemented data type");
return nullptr;
}
}
}
-ConstantOperation *ConstantFolder::fold_operation(NodeOperation *op)
+ConstantOperation *ConstantFolder::fold_operation(NodeOperation *operation)
{
- const DataType data_type = op->getOutputSocket()->getDataType();
- MemoryBuffer *fold_buf = create_constant_buffer(data_type);
- Vector<MemoryBuffer *> inputs_bufs = get_constant_inputs_buffers(op);
- op->render(fold_buf, {first_elem_area_}, inputs_bufs, exec_system_);
-
- ConstantOperation *constant_op = create_constant_operation(data_type, fold_buf->get_elem(0, 0));
- all_operations_.append(constant_op);
- constants_buffers_.add_new(constant_op, fold_buf);
- relink_operation_outputs_to_constant(op, constant_op);
- remove_folded_operation(op);
+ 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;
}
-void ConstantFolder::relink_operation_outputs_to_constant(NodeOperation *from_op,
- ConstantOperation *to_op)
-{
- if (!output_links_.contains(from_op)) {
- return;
- }
- Set<NodeOperation *> outputs = output_links_.pop(from_op);
- for (NodeOperation *out : outputs) {
- for (int i = 0; i < out->getNumberOfInputSockets(); i++) {
- NodeOperationInput *socket = out->getInputSocket(i);
- NodeOperationOutput *link = socket->getLink();
- if (link && &link->getOperation() == from_op) {
- socket->setLink(to_op->getOutputSocket());
- /* TODO: As resolutions are determined before constant folding we need to manually set
- * constant operations resolutions. Once tiled implementation is removed constant folding
- * should be done first and this code can be removed. */
- uint temp[2];
- uint resolution[2] = {out->getWidth(), out->getHeight()};
- to_op->getOutputSocket()->determineResolution(temp, resolution);
- }
- }
- }
- output_links_.add_new(to_op, std::move(outputs));
-}
-
-void ConstantFolder::remove_folded_operation(NodeOperation *op)
-{
- output_links_.remove(op);
- folded_operations_.add(op);
- for (int i = 0; i < op->getNumberOfInputSockets(); i++) {
- NodeOperation *input = op->get_input_operation(i);
- BLI_assert(output_links_.contains(input));
- Set<NodeOperation *> &input_outputs = output_links_.lookup(input);
- input_outputs.remove(op);
- if (input_outputs.size() == 0) {
- output_links_.remove(input);
- folded_operations_.add(input);
- }
- }
-}
-
-MemoryBuffer *ConstantFolder::create_constant_buffer(DataType data_type)
+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_inputs_buffers(NodeOperation *op)
+Vector<MemoryBuffer *> ConstantFolder::get_constant_input_buffers(NodeOperation *operation)
{
- const int num_inputs = op->getNumberOfInputSockets();
+ const int num_inputs = operation->getNumberOfInputSockets();
Vector<MemoryBuffer *> inputs_bufs(num_inputs);
for (int i = 0; i < num_inputs; i++) {
- ConstantOperation *constant_op = static_cast<ConstantOperation *>(op->get_input_operation(i));
- MemoryBuffer *constant_buf = constants_buffers_.lookup_or_add_cb(constant_op, [=] {
+ 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_}, {}, exec_system_);
+ constant_op->render(buf, {first_elem_area_}, {});
return buf;
});
inputs_bufs[i] = constant_buf;
@@ -175,13 +129,14 @@ Vector<MemoryBuffer *> ConstantFolder::get_constant_inputs_buffers(NodeOperation
return inputs_bufs;
}
-/** Returns constant operations resulted from folding. */
+/** 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);
+ Set<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);
@@ -191,42 +146,46 @@ Vector<ConstantOperation *> ConstantFolder::try_fold_operations(Span<NodeOperati
}
/**
- * Evaluate operations that have constant elements values into primitive constant operations.
+ * Evaluate operations with constant elements into primitive constant operations.
*/
int ConstantFolder::fold_operations()
{
- Vector<ConstantOperation *> last_folds = try_fold_operations(all_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) {
- Set<NodeOperation *> &outputs = output_links_.lookup(fold);
- ops_to_fold.extend(outputs.begin(), outputs.end());
+ get_operation_output_operations(fold, ops_to_fold);
}
last_folds = try_fold_operations(ops_to_fold);
folds_count += last_folds.size();
}
- delete_constants_buffers();
- delete_folded_operations();
+ WorkScheduler::stop();
+
+ delete_constant_buffers();
return folds_count;
}
-void ConstantFolder::delete_constants_buffers()
+void ConstantFolder::delete_constant_buffers()
{
- for (MemoryBuffer *buf : constants_buffers_.values()) {
+ for (MemoryBuffer *buf : constant_buffers_.values()) {
delete buf;
}
- constants_buffers_.clear();
+ constant_buffers_.clear();
}
-void ConstantFolder::delete_folded_operations()
+void ConstantFolder::get_operation_output_operations(NodeOperation *operation,
+ Vector<NodeOperation *> &r_outputs)
{
- for (NodeOperation *op : folded_operations_) {
- all_operations_.remove_first_occurrence_and_reorder(op);
- delete op;
+ const Vector<Link> &links = operations_builder_.get_links();
+ for (const Link &link : links) {
+ if (&link.from()->getOperation() == operation) {
+ r_outputs.append(&link.to()->getOperation());
+ }
}
- folded_operations_.clear();
}
} // namespace blender::compositor
diff --git a/source/blender/compositor/intern/COM_ConstantFolder.h b/source/blender/compositor/intern/COM_ConstantFolder.h
index 3f8b3a0abb3..2432e859a5a 100644
--- a/source/blender/compositor/intern/COM_ConstantFolder.h
+++ b/source/blender/compositor/intern/COM_ConstantFolder.h
@@ -13,7 +13,7 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
- * Copyright 2011, Blender Foundation.
+ * Copyright 2021, Blender Foundation.
*/
#pragma once
@@ -22,47 +22,43 @@
#include "BLI_set.hh"
#include "BLI_vector.hh"
-#include "COM_ExecutionSystem.h"
+#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:
- Vector<NodeOperation *> &all_operations_;
- Map<NodeOperation *, Set<NodeOperation *>> &output_links_;
- ExecutionSystem &exec_system_;
-
- /**
- * Operations that has been folded (evaluated into constant operations). They are deleted
- * when folding is finished.
- */
- Set<NodeOperation *> folded_operations_;
+ NodeOperationBuilder &operations_builder_;
- /** Created constant operations buffers during folding. */
- Map<ConstantOperation *, MemoryBuffer *> constants_buffers_;
+ /** Constant operations buffers. */
+ Map<ConstantOperation *, MemoryBuffer *> constant_buffers_;
rcti max_area_;
rcti first_elem_area_;
public:
- ConstantFolder(Vector<NodeOperation *> &operations,
- Map<NodeOperation *, Set<NodeOperation *>> &output_links,
- ExecutionSystem &exec_system);
+ ConstantFolder(NodeOperationBuilder &operations_builder);
int fold_operations();
private:
Vector<ConstantOperation *> try_fold_operations(Span<NodeOperation *> operations);
- ConstantOperation *fold_operation(NodeOperation *op);
- void relink_operation_outputs_to_constant(NodeOperation *from_op, ConstantOperation *to_op);
- void remove_folded_operation(NodeOperation *op);
+ ConstantOperation *fold_operation(NodeOperation *operation);
+
MemoryBuffer *create_constant_buffer(DataType data_type);
- Vector<MemoryBuffer *> get_constant_inputs_buffers(NodeOperation *op);
- void delete_folded_operations();
- void delete_constants_buffers();
+ 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 f5af7cf9147..c0460aed4a4 100644
--- a/source/blender/compositor/intern/COM_Debug.cc
+++ b/source/blender/compositor/intern/COM_Debug.cc
@@ -31,12 +31,15 @@ extern "C" {
#include "BKE_appdir.h"
#include "BKE_node.h"
#include "DNA_node_types.h"
+#include "IMB_imbuf.h"
+#include "IMB_imbuf_types.h"
}
#include "COM_ExecutionSystem.h"
#include "COM_Node.h"
#include "COM_ReadBufferOperation.h"
+#include "COM_SetValueOperation.h"
#include "COM_ViewerOperation.h"
#include "COM_WriteBufferOperation.h"
@@ -49,6 +52,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(const 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 +147,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 +179,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");
@@ -428,4 +454,45 @@ void DebugInfo::graphviz(const ExecutionSystem *system, StringRefNull name)
}
}
+static std::string get_operations_export_dir()
+{
+ return std::string(BKE_tempdir_session()) + "COM_operations" + SEP_STR;
+}
+
+void DebugInfo::export_operation(const NodeOperation *op, MemoryBuffer *render)
+{
+ ImBuf *ibuf = IMB_allocFromBuffer(nullptr,
+ render->getBuffer(),
+ render->getWidth(),
+ render->getHeight(),
+ render->get_num_channels());
+
+ const std::string file_name = operation_class_name(op) + "_" + std::to_string(op->get_id()) +
+ ".png";
+ const std::string path = get_operations_export_dir() + file_name;
+ BLI_make_existing_file(path.c_str());
+ IMB_saveiff(ibuf, path.c_str(), ibuf->flags);
+ IMB_freeImBuf(ibuf);
+}
+
+void DebugInfo::delete_operation_exports()
+{
+ const std::string dir = get_operations_export_dir();
+ if (BLI_exists(dir.c_str())) {
+ struct direntry *file_list;
+ int num_files = BLI_filelist_dir_contents(dir.c_str(), &file_list);
+ for (int i = 0; i < num_files; i++) {
+ direntry *file = &file_list[i];
+ const eFileAttributes file_attrs = BLI_file_attributes(file->path);
+ if (file_attrs & FILE_ATTR_ANY_LINK) {
+ continue;
+ }
+
+ if (BLI_is_file(file->path) && BLI_path_extension_check(file->path, ".png")) {
+ BLI_delete(file->path, false, false);
+ }
+ }
+ }
+}
+
} // namespace blender::compositor
diff --git a/source/blender/compositor/intern/COM_Debug.h b/source/blender/compositor/intern/COM_Debug.h
index a2fbab45a2c..23d99c7e529 100644
--- a/source/blender/compositor/intern/COM_Debug.h
+++ b/source/blender/compositor/intern/COM_Debug.h
@@ -28,6 +28,11 @@
namespace blender::compositor {
static constexpr bool COM_EXPORT_GRAPHVIZ = false;
+static constexpr bool COM_GRAPHVIZ_SHOW_NODE_NAME = false;
+
+/* Saves operations results to image files. */
+static constexpr bool COM_EXPORT_OPERATION_BUFFERS = false;
+
class Node;
class ExecutionSystem;
class ExecutionGroup;
@@ -73,6 +78,9 @@ class DebugInfo {
m_group_states[execution_group] = EG_WAIT;
}
}
+ if (COM_EXPORT_OPERATION_BUFFERS) {
+ delete_operation_exports();
+ }
};
static void node_added(const Node *node)
@@ -116,6 +124,14 @@ class DebugInfo {
}
};
+ static void operation_rendered(const NodeOperation *op, MemoryBuffer *render)
+ {
+ /* Don't export constant operations as there are too many and it's rarely useful. */
+ if (COM_EXPORT_OPERATION_BUFFERS && render && !render->is_a_single_elem()) {
+ export_operation(op, render);
+ }
+ }
+
static void graphviz(const ExecutionSystem *system, StringRefNull name = "");
protected:
@@ -131,6 +147,9 @@ class DebugInfo {
const char *name, const char *color, const char *style, char *str, int maxlen);
static int graphviz_legend(char *str, int maxlen, bool has_execution_groups);
static bool graphviz_system(const ExecutionSystem *system, char *str, int maxlen);
+
+ static void export_operation(const NodeOperation *op, MemoryBuffer *render);
+ static void delete_operation_exports();
};
} // namespace blender::compositor
diff --git a/source/blender/compositor/intern/COM_ExecutionModel.cc b/source/blender/compositor/intern/COM_ExecutionModel.cc
index 7dee3bd4bbc..08ec0585739 100644
--- a/source/blender/compositor/intern/COM_ExecutionModel.cc
+++ b/source/blender/compositor/intern/COM_ExecutionModel.cc
@@ -45,10 +45,4 @@ ExecutionModel::ExecutionModel(eExecutionModel model,
}
}
-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 128739576b7..f2ff8f9af28 100644
--- a/source/blender/compositor/intern/COM_ExecutionModel.h
+++ b/source/blender/compositor/intern/COM_ExecutionModel.h
@@ -69,15 +69,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 b39600f12de..00b14ff18e3 100644
--- a/source/blender/compositor/intern/COM_FullFrameExecutionModel.cc
+++ b/source/blender/compositor/intern/COM_FullFrameExecutionModel.cc
@@ -21,6 +21,7 @@
#include "COM_Debug.h"
#include "COM_ExecutionGroup.h"
#include "COM_ReadBufferOperation.h"
+#include "COM_ViewerOperation.h"
#include "COM_WorkScheduler.h"
#include "BLI_map.hh"
@@ -39,34 +40,13 @@ FullFrameExecutionModel::FullFrameExecutionModel(CompositorContext &context,
Vector<NodeOperation *> &operations)
: ExecutionModel(eExecutionModel::FullFrame, context, operations),
active_buffers_(shared_buffers),
- num_operations_finished_(0),
- priorities_(),
- 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_);
-
- /* Get operations output links to other operations. */
- for (NodeOperation *op : operations_) {
- for (int i = 0; i < op->getNumberOfInputSockets(); i++) {
- NodeOperation *input_op = op->get_input_operation(i);
- Set<NodeOperation *> &links = output_links_.lookup_or_add_default(input_op);
- links.add(op);
- }
- }
-}
-
-FullFrameExecutionModel::~FullFrameExecutionModel()
-{
- BLI_condition_end(&work_finished_cond_);
- BLI_mutex_end(&work_mutex_);
}
void FullFrameExecutionModel::execute(ExecutionSystem &exec_system)
@@ -74,15 +54,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, "compositor_prior_folding");
- ConstantFolder folder(operations_, output_links_, exec_system);
- folder.fold_operations();
- DebugInfo::graphviz(&exec_system, "compositor_after_folding");
+ DebugInfo::graphviz(&exec_system, "compositor_prior_rendering");
determine_areas_to_render_and_reads();
- clamp_operations_to_rendered_areas();
- DebugInfo::graphviz(&exec_system, "compositor_after_clamping");
- render_operations(exec_system);
+ render_operations();
}
void FullFrameExecutionModel::determine_areas_to_render_and_reads()
@@ -103,26 +78,6 @@ void FullFrameExecutionModel::determine_areas_to_render_and_reads()
}
}
-void FullFrameExecutionModel::clamp_operations_to_rendered_areas()
-{
- for (NodeOperation *op : operations_) {
- rcti bounds = active_buffers_.get_render_bounds(op);
- op->set_canvas_area(bounds);
- }
-}
-
-void FullFrameExecutionModel::ensure_inputs_rendered(NodeOperation *op,
- ExecutionSystem &exec_system)
-{
- const int num_inputs = op->getNumberOfInputSockets();
- for (int i = 0; i < num_inputs; i++) {
- NodeOperation *input_op = op->get_input_operation(i);
- if (!active_buffers_.is_operation_rendered(input_op)) {
- render_operation(input_op, exec_system);
- }
- }
-}
-
Vector<MemoryBuffer *> FullFrameExecutionModel::get_input_buffers(NodeOperation *op)
{
const int num_inputs = op->getNumberOfInputSockets();
@@ -138,22 +93,22 @@ MemoryBuffer *FullFrameExecutionModel::create_operation_buffer(NodeOperation *op
{
const DataType data_type = op->getOutputSocket(0)->getDataType();
const bool is_a_single_elem = op->get_flags().is_constant_operation;
- return new MemoryBuffer(data_type, op->get_canvas_area(), is_a_single_elem);
+ 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)
{
- if (active_buffers_.is_operation_rendered(op)) {
- return;
- }
-
- ensure_inputs_rendered(op, exec_system);
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);
+ if (op->getWidth() > 0 && op->getHeight() > 0) {
+ Span<rcti> areas = active_buffers_.get_areas_to_render(op);
+ op->render(op_buf, areas, input_bufs);
+ DebugInfo::operation_rendered(op, op_buf);
+ }
+ /* Even if operation has no resolution set the empty buffer. It will be clipped with a
+ * TranslateOperation from convert resolutions if linked to an operation with resolution. */
active_buffers_.set_rendered_buffer(op, std::unique_ptr<MemoryBuffer>(op_buf));
operation_finished(op);
@@ -162,15 +117,22 @@ 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();
WorkScheduler::start(this->context_);
for (eCompositorPriority priority : priorities_) {
for (NodeOperation *op : operations_) {
- if (op->isOutputOperation(is_rendering) && op->getRenderPriority() == priority) {
- render_operation(op, exec_system);
+ const bool has_size = op->getWidth() > 0 && op->getHeight() > 0;
+ const bool is_priority_output = op->isOutputOperation(is_rendering) &&
+ op->getRenderPriority() == priority;
+ if (is_priority_output && has_size) {
+ render_output_dependencies(op);
+ render_operation(op);
+ }
+ else if (is_priority_output && !has_size && op->isActiveViewerOutput()) {
+ static_cast<ViewerOperation *>(op)->clear_display_buffer();
}
}
}
@@ -178,48 +140,98 @@ void FullFrameExecutionModel::render_operations(ExecutionSystem &exec_system)
}
/**
- * Determines all input operations areas needed to render given operation area.
- * \param operation: Renderer operation.
- * \param render_area: Area within given operation bounds to render.
+ * Returns all dependencies from inputs to outputs. A dependency may be repeated when
+ * several operations depend on it.
*/
-void FullFrameExecutionModel::determine_areas_to_render(NodeOperation *operation,
- const rcti &render_area)
+static Vector<NodeOperation *> get_operation_dependencies(NodeOperation *operation)
{
- if (active_buffers_.is_area_registered(operation, render_area)) {
- return;
+ /* Get dependencies from outputs to inputs. */
+ Vector<NodeOperation *> dependencies;
+ Vector<NodeOperation *> next_outputs;
+ next_outputs.append(operation);
+ while (next_outputs.size() > 0) {
+ Vector<NodeOperation *> outputs(next_outputs);
+ next_outputs.clear();
+ for (NodeOperation *output : outputs) {
+ for (int i = 0; i < output->getNumberOfInputSockets(); i++) {
+ next_outputs.append(output->get_input_operation(i));
+ }
+ }
+ dependencies.extend(next_outputs);
}
- active_buffers_.register_area(operation, render_area);
+ /* Reverse to get dependencies from inputs to outputs. */
+ std::reverse(dependencies.begin(), dependencies.end());
- const int num_inputs = operation->getNumberOfInputSockets();
- for (int i = 0; i < num_inputs; i++) {
- NodeOperation *input_op = operation->get_input_operation(i);
- rcti input_op_rect = input_op->get_canvas_area();
- rcti input_area;
- operation->get_area_of_interest(input_op, render_area, input_area);
+ return dependencies;
+}
+
+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);
+ }
+ }
+}
+
+/**
+ * Determines all operations areas needed to render given output area.
+ */
+void FullFrameExecutionModel::determine_areas_to_render(NodeOperation *output_op,
+ const rcti &output_area)
+{
+ BLI_assert(output_op->isOutputOperation(context_.isRendering()));
+
+ Vector<std::pair<NodeOperation *, const rcti>> stack;
+ stack.append({output_op, output_area});
+ while (stack.size() > 0) {
+ std::pair<NodeOperation *, rcti> pair = stack.pop_last();
+ NodeOperation *operation = pair.first;
+ const rcti &render_area = pair.second;
+ if (active_buffers_.is_area_registered(operation, render_area)) {
+ continue;
+ }
- /* Ensure area of interest is within operation bounds, cropping areas outside. */
- BLI_rcti_isect(&input_area, &input_op_rect, &input_area);
+ active_buffers_.register_area(operation, render_area);
- determine_areas_to_render(input_op, input_area);
+ const int num_inputs = operation->getNumberOfInputSockets();
+ for (int i = 0; i < num_inputs; i++) {
+ NodeOperation *input_op = operation->get_input_operation(i);
+ rcti input_op_rect, input_area;
+ BLI_rcti_init(&input_op_rect, 0, input_op->getWidth(), 0, input_op->getHeight());
+ operation->get_area_of_interest(input_op, render_area, input_area);
+
+ /* Ensure area of interest is within operation bounds, cropping areas outside. */
+ BLI_rcti_isect(&input_area, &input_op_rect, &input_area);
+
+ stack.append({input_op, input_area});
+ }
}
}
/**
- * Determines the reads given operation and its inputs will receive (i.e: Number of dependent
+ * Determines reads to receive by operations in output operation tree (i.e: Number of dependent
* operations each operation has).
*/
-void FullFrameExecutionModel::determine_reads(NodeOperation *operation)
+void FullFrameExecutionModel::determine_reads(NodeOperation *output_op)
{
- if (active_buffers_.has_registered_reads(operation)) {
- return;
- }
+ BLI_assert(output_op->isOutputOperation(context_.isRendering()));
- const int num_inputs = operation->getNumberOfInputSockets();
- for (int i = 0; i < num_inputs; i++) {
- NodeOperation *input_op = operation->get_input_operation(i);
- determine_reads(input_op);
- active_buffers_.register_read(input_op);
+ Vector<NodeOperation *> stack;
+ stack.append(output_op);
+ while (stack.size() > 0) {
+ NodeOperation *operation = stack.pop_last();
+ const int num_inputs = operation->getNumberOfInputSockets();
+ for (int i = 0; i < num_inputs; i++) {
+ NodeOperation *input_op = operation->get_input_operation(i);
+ if (!active_buffers_.has_registered_reads(input_op)) {
+ stack.append(input_op);
+ }
+ active_buffers_.register_read(input_op);
+ }
}
}
@@ -253,70 +265,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 4255cb217d1..f2151206864 100644
--- a/source/blender/compositor/intern/COM_FullFrameExecutionModel.h
+++ b/source/blender/compositor/intern/COM_FullFrameExecutionModel.h
@@ -53,40 +53,26 @@ class FullFrameExecutionModel : public ExecutionModel {
*/
Vector<eCompositorPriority> priorities_;
- ThreadMutex work_mutex_;
- ThreadCondition work_finished_cond_;
-
- /**
- * Operations output links to other operations.
- */
- Map<NodeOperation *, Set<NodeOperation *>> output_links_;
-
public:
FullFrameExecutionModel(CompositorContext &context,
SharedOperationBuffers &shared_buffers,
- Vector<NodeOperation *> &operations);
- ~FullFrameExecutionModel();
+ Span<NodeOperation *> operations);
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 clamp_operations_to_rendered_areas();
- void render_operations(ExecutionSystem &exec_system);
-
- void ensure_inputs_rendered(NodeOperation *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);
void get_output_render_area(NodeOperation *output_op, rcti &r_area);
- void determine_areas_to_render(NodeOperation *operation, const rcti &render_area);
- void determine_reads(NodeOperation *operation);
+ void determine_areas_to_render(NodeOperation *output_op, const rcti &output_area);
+ void determine_reads(NodeOperation *output_op);
void update_progress_bar();
diff --git a/source/blender/compositor/intern/COM_MemoryBuffer.cc b/source/blender/compositor/intern/COM_MemoryBuffer.cc
index 4085de0c96e..6b954072a9a 100644
--- a/source/blender/compositor/intern/COM_MemoryBuffer.cc
+++ b/source/blender/compositor/intern/COM_MemoryBuffer.cc
@@ -18,10 +18,31 @@
#include "COM_MemoryBuffer.h"
+#include "IMB_colormanagement.h"
+#include "IMB_imbuf_types.h"
#include "MEM_guardedalloc.h"
+#define ASSERT_BUFFER_CONTAINS_AREA(buf, area) \
+ BLI_assert(BLI_rcti_inside_rcti(&(buf)->get_rect(), &(area)))
+
+#define ASSERT_BUFFER_CONTAINS_AREA_AT_COORDS(buf, area, x, y) \
+ BLI_assert((buf)->get_rect().xmin <= (x)); \
+ BLI_assert((buf)->get_rect().ymin <= (y)); \
+ BLI_assert((buf)->get_rect().xmax >= (x) + BLI_rcti_size_x(&(area))); \
+ BLI_assert((buf)->get_rect().ymax >= (y) + BLI_rcti_size_y(&(area)))
+
+#define ASSERT_VALID_ELEM_SIZE(buf, channel_offset, elem_size) \
+ BLI_assert((buf)->get_num_channels() <= (channel_offset) + (elem_size))
+
namespace blender::compositor {
+static rcti create_rect(const int width, const int height)
+{
+ rcti rect;
+ BLI_rcti_init(&rect, 0, width, 0, height);
+ return rect;
+}
+
MemoryBuffer::MemoryBuffer(MemoryProxy *memoryProxy, const rcti &rect, MemoryBufferState state)
{
m_rect = rect;
@@ -30,7 +51,7 @@ MemoryBuffer::MemoryBuffer(MemoryProxy *memoryProxy, const rcti &rect, MemoryBuf
this->m_num_channels = COM_data_type_num_channels(memoryProxy->getDataType());
this->m_buffer = (float *)MEM_mallocN_aligned(
sizeof(float) * buffer_len() * this->m_num_channels, 16, "COM_MemoryBuffer");
- is_owner_ = true;
+ owns_data_ = true;
this->m_state = state;
this->m_datatype = memoryProxy->getDataType();
@@ -45,26 +66,39 @@ MemoryBuffer::MemoryBuffer(DataType dataType, const rcti &rect, bool is_a_single
this->m_num_channels = COM_data_type_num_channels(dataType);
this->m_buffer = (float *)MEM_mallocN_aligned(
sizeof(float) * buffer_len() * this->m_num_channels, 16, "COM_MemoryBuffer");
- is_owner_ = true;
+ owns_data_ = true;
this->m_state = MemoryBufferState::Temporary;
this->m_datatype = dataType;
set_strides();
}
+/**
+ * Construct MemoryBuffer from a float buffer. MemoryBuffer is not responsible for
+ * freeing it.
+ */
+MemoryBuffer::MemoryBuffer(
+ float *buffer, int num_channels, int width, int height, bool is_a_single_elem)
+ : MemoryBuffer(buffer, num_channels, create_rect(width, height), is_a_single_elem)
+{
+}
+
+/**
+ * Construct MemoryBuffer from a float buffer area. MemoryBuffer is not responsible for
+ * freeing given buffer.
+ */
MemoryBuffer::MemoryBuffer(float *buffer,
const int num_channels,
- const int width,
- const int height,
+ const rcti &rect,
const bool is_a_single_elem)
{
- BLI_rcti_init(&m_rect, 0, width, 0, height);
+ m_rect = rect;
m_is_a_single_elem = is_a_single_elem;
m_memoryProxy = nullptr;
m_num_channels = num_channels;
m_datatype = COM_num_channels_data_type(num_channels);
m_buffer = buffer;
- is_owner_ = false;
+ owns_data_ = false;
m_state = MemoryBufferState::Temporary;
set_strides();
@@ -95,6 +129,32 @@ void MemoryBuffer::clear()
memset(m_buffer, 0, buffer_len() * m_num_channels * sizeof(float));
}
+BuffersIterator<float> MemoryBuffer::iterate_with(Span<MemoryBuffer *> inputs)
+{
+ return iterate_with(inputs, m_rect);
+}
+
+BuffersIterator<float> MemoryBuffer::iterate_with(Span<MemoryBuffer *> inputs, const rcti &area)
+{
+ BuffersIteratorBuilder<float> builder(m_buffer, m_rect, area, elem_stride);
+ for (MemoryBuffer *input : inputs) {
+ builder.add_input(input->getBuffer(), input->get_rect(), input->elem_stride);
+ }
+ return builder.build();
+}
+
+/**
+ * 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];
@@ -132,33 +192,197 @@ float MemoryBuffer::get_max_value(const rcti &rect) const
MemoryBuffer::~MemoryBuffer()
{
- if (this->m_buffer && is_owner_) {
+ if (this->m_buffer && owns_data_) {
MEM_freeN(this->m_buffer);
this->m_buffer = nullptr;
}
}
-void MemoryBuffer::fill_from(const MemoryBuffer &src)
+void MemoryBuffer::copy_from(const MemoryBuffer *src, const rcti &area)
{
- BLI_assert(!this->is_a_single_elem());
+ copy_from(src, area, area.xmin, area.ymin);
+}
+
+void MemoryBuffer::copy_from(const MemoryBuffer *src,
+ const rcti &area,
+ const int to_x,
+ const int to_y)
+{
+ BLI_assert(this->get_num_channels() == src->get_num_channels());
+ copy_from(src, area, 0, src->get_num_channels(), to_x, to_y, 0);
+}
+
+void MemoryBuffer::copy_from(const MemoryBuffer *src,
+ const rcti &area,
+ const int channel_offset,
+ const int elem_size,
+ const int to_channel_offset)
+{
+ copy_from(src, area, channel_offset, elem_size, area.xmin, area.ymin, to_channel_offset);
+}
+
+void MemoryBuffer::copy_from(const MemoryBuffer *src,
+ const rcti &area,
+ const int channel_offset,
+ const int elem_size,
+ const int to_x,
+ const int to_y,
+ const int to_channel_offset)
+{
+ if (this->is_a_single_elem()) {
+ copy_single_elem_from(src, channel_offset, elem_size, to_channel_offset);
+ }
+ else if (!src->is_a_single_elem() && elem_size == src->get_num_channels() &&
+ elem_size == this->get_num_channels()) {
+ BLI_assert(to_channel_offset == 0);
+ BLI_assert(channel_offset == 0);
+ copy_rows_from(src, area, to_x, to_y);
+ }
+ else {
+ copy_elems_from(src, area, channel_offset, elem_size, to_x, to_y, to_channel_offset);
+ }
+}
+
+void MemoryBuffer::copy_from(const uchar *src, const rcti &area)
+{
+ copy_from(src, area, 0, this->get_num_channels(), this->get_num_channels(), 0);
+}
+
+void MemoryBuffer::copy_from(const uchar *src,
+ const rcti &area,
+ const int channel_offset,
+ const int elem_size,
+ const int elem_stride,
+ const int to_channel_offset)
+{
+ copy_from(
+ src, area, channel_offset, elem_size, elem_stride, area.xmin, area.ymin, to_channel_offset);
+}
+
+void MemoryBuffer::copy_from(const uchar *src,
+ const rcti &area,
+ const int channel_offset,
+ const int elem_size,
+ const int elem_stride,
+ const int to_x,
+ const int to_y,
+ const int to_channel_offset)
+{
+ ASSERT_BUFFER_CONTAINS_AREA_AT_COORDS(this, area, to_x, to_y);
+ ASSERT_VALID_ELEM_SIZE(this, to_channel_offset, elem_size);
- unsigned int otherY;
- unsigned int minX = MAX2(this->m_rect.xmin, src.m_rect.xmin);
- unsigned int maxX = MIN2(this->m_rect.xmax, src.m_rect.xmax);
- unsigned int minY = MAX2(this->m_rect.ymin, src.m_rect.ymin);
- unsigned int maxY = MIN2(this->m_rect.ymax, src.m_rect.ymax);
- int offset;
- int otherOffset;
+ const int width = BLI_rcti_size_x(&area);
+ const int height = BLI_rcti_size_y(&area);
+ const int src_row_stride = width * elem_stride;
+ const uchar *const src_start = src + area.ymin * src_row_stride + channel_offset;
+ for (int y = 0; y < height; y++) {
+ const uchar *from_elem = src_start + y * src_row_stride;
+ float *to_elem = &this->get_value(to_x, to_y + y, to_channel_offset);
+ const float *row_end = to_elem + width * this->elem_stride;
+ while (to_elem < row_end) {
+ for (int i = 0; i < elem_size; i++) {
+ to_elem[i] = ((float)from_elem[i]) * (1.0f / 255.0f);
+ }
+ to_elem += this->elem_stride;
+ from_elem += elem_stride;
+ }
+ }
+}
+
+static void colorspace_to_scene_linear(MemoryBuffer *buf, const rcti &area, ColorSpace *colorspace)
+{
+ const int width = BLI_rcti_size_x(&area);
+ const int height = BLI_rcti_size_y(&area);
+ float *out = buf->get_elem(area.xmin, area.ymin);
+ /* If area allows continuous memory do conversion in one step. Otherwise per row. */
+ if (buf->getWidth() == width) {
+ IMB_colormanagement_colorspace_to_scene_linear(
+ out, width, height, buf->get_num_channels(), colorspace, false);
+ }
+ else {
+ for (int y = 0; y < height; y++) {
+ IMB_colormanagement_colorspace_to_scene_linear(
+ out, width, 1, buf->get_num_channels(), colorspace, false);
+ out += buf->row_stride;
+ }
+ }
+}
+
+void MemoryBuffer::copy_from(const ImBuf *src, const rcti &area, const bool ensure_linear_space)
+{
+ copy_from(src, area, 0, this->get_num_channels(), 0, ensure_linear_space);
+}
+
+void MemoryBuffer::copy_from(const ImBuf *src,
+ const rcti &area,
+ const int channel_offset,
+ const int elem_size,
+ const int to_channel_offset,
+ const bool ensure_linear_space)
+{
+ copy_from(src,
+ area,
+ channel_offset,
+ elem_size,
+ area.xmin,
+ area.ymin,
+ to_channel_offset,
+ ensure_linear_space);
+}
- for (otherY = minY; otherY < maxY; otherY++) {
- otherOffset = src.get_coords_offset(minX, otherY);
- offset = this->get_coords_offset(minX, otherY);
- memcpy(&this->m_buffer[offset],
- &src.m_buffer[otherOffset],
- (maxX - minX) * this->m_num_channels * sizeof(float));
+void MemoryBuffer::copy_from(const ImBuf *src,
+ const rcti &area,
+ const int channel_offset,
+ const int elem_size,
+ const int to_x,
+ const int to_y,
+ const int to_channel_offset,
+ const bool ensure_linear_space)
+{
+ if (src->rect_float) {
+ const MemoryBuffer mem_buf(src->rect_float, src->channels, src->x, src->y, false);
+ copy_from(&mem_buf, area, channel_offset, elem_size, to_x, to_y, to_channel_offset);
+ }
+ else if (src->rect) {
+ const uchar *uc_buf = (uchar *)src->rect;
+ const int elem_stride = src->channels;
+ copy_from(uc_buf, area, channel_offset, elem_size, elem_stride, to_x, to_y, to_channel_offset);
+ if (ensure_linear_space) {
+ colorspace_to_scene_linear(this, area, src->rect_colorspace);
+ }
+ }
+ else {
+ /* Empty ImBuf source. Fill destination with empty values. */
+ const float *zero_elem = new float[elem_size]{0};
+ fill(area, to_channel_offset, zero_elem, elem_size);
+ delete[] zero_elem;
}
}
+void MemoryBuffer::fill(const rcti &area, const float *value)
+{
+ fill(area, 0, value, this->get_num_channels());
+}
+
+void MemoryBuffer::fill(const rcti &area,
+ const int channel_offset,
+ const float *value,
+ const int value_size)
+{
+ const MemoryBuffer single_elem(const_cast<float *>(value), value_size, this->get_rect(), true);
+ copy_from(&single_elem, area, 0, value_size, area.xmin, area.ymin, channel_offset);
+}
+
+void MemoryBuffer::fill_from(const MemoryBuffer &src)
+{
+ rcti overlap;
+ overlap.xmin = MAX2(this->m_rect.xmin, src.m_rect.xmin);
+ overlap.xmax = MIN2(this->m_rect.xmax, src.m_rect.xmax);
+ overlap.ymin = MAX2(this->m_rect.ymin, src.m_rect.ymin);
+ overlap.ymax = MIN2(this->m_rect.ymax, src.m_rect.ymax);
+ copy_from(&src, overlap);
+}
+
void MemoryBuffer::writePixel(int x, int y, const float color[4])
{
if (x >= this->m_rect.xmin && x < this->m_rect.xmax && y >= this->m_rect.ymin &&
@@ -216,4 +440,70 @@ void MemoryBuffer::readEWA(float *result, const float uv[2], const float derivat
}
}
+void MemoryBuffer::copy_single_elem_from(const MemoryBuffer *src,
+ const int channel_offset,
+ const int elem_size,
+ const int to_channel_offset)
+{
+ ASSERT_VALID_ELEM_SIZE(this, to_channel_offset, elem_size);
+ ASSERT_VALID_ELEM_SIZE(src, channel_offset, elem_size);
+ BLI_assert(this->is_a_single_elem());
+
+ float *to_elem = &this->get_value(
+ this->get_rect().xmin, this->get_rect().ymin, to_channel_offset);
+ const float *from_elem = &src->get_value(
+ src->get_rect().xmin, src->get_rect().ymin, channel_offset);
+ const int elem_bytes = elem_size * sizeof(float);
+ memcpy(to_elem, from_elem, elem_bytes);
+}
+
+void MemoryBuffer::copy_rows_from(const MemoryBuffer *src,
+ const rcti &area,
+ const int to_x,
+ const int to_y)
+{
+ ASSERT_BUFFER_CONTAINS_AREA(src, area);
+ ASSERT_BUFFER_CONTAINS_AREA_AT_COORDS(this, area, to_x, to_y);
+ BLI_assert(this->get_num_channels() == src->get_num_channels());
+ BLI_assert(!this->is_a_single_elem());
+ BLI_assert(!src->is_a_single_elem());
+
+ const int width = BLI_rcti_size_x(&area);
+ const int height = BLI_rcti_size_y(&area);
+ const int row_bytes = this->get_num_channels() * width * sizeof(float);
+ for (int y = 0; y < height; y++) {
+ float *to_row = this->get_elem(to_x, to_y + y);
+ const float *from_row = src->get_elem(area.xmin, area.ymin + y);
+ memcpy(to_row, from_row, row_bytes);
+ }
+}
+
+void MemoryBuffer::copy_elems_from(const MemoryBuffer *src,
+ const rcti &area,
+ const int channel_offset,
+ const int elem_size,
+ const int to_x,
+ const int to_y,
+ const int to_channel_offset)
+{
+ ASSERT_BUFFER_CONTAINS_AREA(src, area);
+ ASSERT_BUFFER_CONTAINS_AREA_AT_COORDS(this, area, to_x, to_y);
+ ASSERT_VALID_ELEM_SIZE(this, to_channel_offset, elem_size);
+ ASSERT_VALID_ELEM_SIZE(src, channel_offset, elem_size);
+
+ const int width = BLI_rcti_size_x(&area);
+ const int height = BLI_rcti_size_y(&area);
+ const int elem_bytes = elem_size * sizeof(float);
+ for (int y = 0; y < height; y++) {
+ float *to_elem = &this->get_value(to_x, to_y + y, to_channel_offset);
+ const float *from_elem = &src->get_value(area.xmin, area.ymin + y, channel_offset);
+ const float *row_end = to_elem + width * this->elem_stride;
+ while (to_elem < row_end) {
+ memcpy(to_elem, from_elem, elem_bytes);
+ to_elem += this->elem_stride;
+ from_elem += src->elem_stride;
+ }
+ }
+}
+
} // namespace blender::compositor
diff --git a/source/blender/compositor/intern/COM_MemoryBuffer.h b/source/blender/compositor/intern/COM_MemoryBuffer.h
index fc40708eeb9..9f68d508b2b 100644
--- a/source/blender/compositor/intern/COM_MemoryBuffer.h
+++ b/source/blender/compositor/intern/COM_MemoryBuffer.h
@@ -18,6 +18,9 @@
#pragma once
+#include "COM_BufferArea.h"
+#include "COM_BufferRange.h"
+#include "COM_BuffersIterator.h"
#include "COM_ExecutionGroup.h"
#include "COM_MemoryProxy.h"
@@ -34,7 +37,7 @@ enum class MemoryBufferState {
/** \brief memory has been allocated on creator device and CPU machine,
* but kernel has not been executed */
Default = 0,
- /** \brief chunk is consolidated from other chunks. special state.*/
+ /** \brief chunk is consolidated from other chunks. special state. */
Temporary = 6,
};
@@ -109,7 +112,7 @@ class MemoryBuffer {
/**
* Whether MemoryBuffer owns buffer data.
*/
- bool is_owner_;
+ bool owns_data_;
public:
/**
@@ -122,6 +125,11 @@ class MemoryBuffer {
*/
MemoryBuffer(DataType datatype, const rcti &rect, bool is_a_single_elem = false);
+ MemoryBuffer(
+ float *buffer, int num_channels, int width, int height, bool is_a_single_elem = false);
+
+ MemoryBuffer(float *buffer, int num_channels, const rcti &rect, bool is_a_single_elem = false);
+
/**
* Construct MemoryBuffer from an existent float buffer. MemoryBuffer is not responsible for
* freeing given buffer.
@@ -235,12 +243,38 @@ class MemoryBuffer {
return is_a_single_elem() ? 1 : getHeight();
}
- uint8_t get_num_channels()
+ uint8_t get_num_channels() const
{
return this->m_num_channels;
}
/**
+ * Get all buffer elements as a range with no offsets.
+ */
+ BufferRange<float> as_range()
+ {
+ return BufferRange<float>(m_buffer, 0, buffer_len(), elem_stride);
+ }
+
+ BufferRange<const float> as_range() const
+ {
+ return BufferRange<const float>(m_buffer, 0, buffer_len(), elem_stride);
+ }
+
+ BufferArea<float> get_buffer_area(const rcti &area)
+ {
+ return BufferArea<float>(m_buffer, getWidth(), area, elem_stride);
+ }
+
+ BufferArea<const float> get_buffer_area(const rcti &area) const
+ {
+ return BufferArea<const float>(m_buffer, getWidth(), area, elem_stride);
+ }
+
+ BuffersIterator<float> iterate_with(Span<MemoryBuffer *> inputs);
+ BuffersIterator<float> iterate_with(Span<MemoryBuffer *> inputs, const rcti &area);
+
+ /**
* \brief get the data of this MemoryBuffer
* \note buffer should already be available in memory
*/
@@ -249,6 +283,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();
@@ -264,11 +300,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;
}
@@ -280,13 +319,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,
@@ -307,11 +352,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;
}
@@ -323,13 +371,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,
@@ -416,11 +470,58 @@ class MemoryBuffer {
return this->m_state == MemoryBufferState::Temporary;
}
+ void copy_from(const MemoryBuffer *src, const rcti &area);
+ void copy_from(const MemoryBuffer *src, const rcti &area, int to_x, int to_y);
+ void copy_from(const MemoryBuffer *src,
+ const rcti &area,
+ int channel_offset,
+ int elem_size,
+ int to_channel_offset);
+ void copy_from(const MemoryBuffer *src,
+ const rcti &area,
+ int channel_offset,
+ int elem_size,
+ int to_x,
+ int to_y,
+ int to_channel_offset);
+ void copy_from(const uchar *src, const rcti &area);
+ void copy_from(const uchar *src,
+ const rcti &area,
+ int channel_offset,
+ int elem_size,
+ int elem_stride,
+ int to_channel_offset);
+ void copy_from(const uchar *src,
+ const rcti &area,
+ int channel_offset,
+ int elem_size,
+ int elem_stride,
+ int to_x,
+ int to_y,
+ int to_channel_offset);
+ void copy_from(const struct ImBuf *src, const rcti &area, bool ensure_linear_space = false);
+ void copy_from(const struct ImBuf *src,
+ const rcti &area,
+ int channel_offset,
+ int elem_size,
+ int to_channel_offset,
+ bool ensure_linear_space = false);
+ void copy_from(const struct ImBuf *src,
+ const rcti &src_area,
+ int channel_offset,
+ int elem_size,
+ int to_x,
+ int to_y,
+ int to_channel_offset,
+ bool ensure_linear_space = false);
+
+ void fill(const rcti &area, const float *value);
+ void fill(const rcti &area, int channel_offset, const float *value, int value_size);
/**
* \brief add the content from otherBuffer to this MemoryBuffer
* \param otherBuffer: source buffer
*
- * \note take care when running this on a new buffer since it wont fill in
+ * \note take care when running this on a new buffer since it won't fill in
* uninitialized values in areas where the buffers don't overlap.
*/
void fill_from(const MemoryBuffer &src);
@@ -464,6 +565,22 @@ class MemoryBuffer {
return get_memory_width() * get_memory_height();
}
+ void copy_single_elem_from(const MemoryBuffer *src,
+ int channel_offset,
+ int elem_size,
+ const int to_channel_offset);
+ void copy_rows_from(const MemoryBuffer *src,
+ const rcti &src_area,
+ const int to_x,
+ const int to_y);
+ void copy_elems_from(const MemoryBuffer *src,
+ const rcti &area,
+ const int channel_offset,
+ const int elem_size,
+ const int to_x,
+ const int to_y,
+ const int to_channel_offset);
+
#ifdef WITH_CXX_GUARDEDALLOC
MEM_CXX_CLASS_ALLOC_FUNCS("COM:MemoryBuffer")
#endif
diff --git a/source/blender/compositor/intern/COM_MemoryProxy.h b/source/blender/compositor/intern/COM_MemoryProxy.h
index 931fd8d2622..6814afada74 100644
--- a/source/blender/compositor/intern/COM_MemoryProxy.h
+++ b/source/blender/compositor/intern/COM_MemoryProxy.h
@@ -18,6 +18,10 @@
#pragma once
+#ifdef WITH_CXX_GUARDEDALLOC
+# include "MEM_guardedalloc.h"
+#endif
+
#include "COM_defines.h"
namespace blender::compositor {
diff --git a/source/blender/compositor/intern/COM_MetaData.cc b/source/blender/compositor/intern/COM_MetaData.cc
index 88bfa385514..a6fb84dfb87 100644
--- a/source/blender/compositor/intern/COM_MetaData.cc
+++ b/source/blender/compositor/intern/COM_MetaData.cc
@@ -41,7 +41,7 @@ void MetaData::addCryptomatteEntry(const blender::StringRef layer_name,
/* Replace the hash neutral cryptomatte keys with hashed versions.
*
* When a conversion happens it will also add the cryptomatte name key with the given
- * `layer_name`.*/
+ * `layer_name`. */
void MetaData::replaceHashNeutralCryptomatteKeys(const blender::StringRef layer_name)
{
std::string cryptomatte_hash = entries_.pop_default(META_DATA_KEY_CRYPTOMATTE_HASH, "");
diff --git a/source/blender/compositor/intern/COM_MultiThreadedOperation.cc b/source/blender/compositor/intern/COM_MultiThreadedOperation.cc
index c54c2edccb0..7ccf6f76d9f 100644
--- a/source/blender/compositor/intern/COM_MultiThreadedOperation.cc
+++ b/source/blender/compositor/intern/COM_MultiThreadedOperation.cc
@@ -5,21 +5,21 @@ namespace blender::compositor {
MultiThreadedOperation::MultiThreadedOperation()
{
- m_num_passes = 1;
+ num_passes_ = 1;
+ current_pass_ = 0;
flags.is_fullframe_operation = true;
}
void MultiThreadedOperation::update_memory_buffer(MemoryBuffer *output,
- const rcti &output_area,
- blender::Span<MemoryBuffer *> inputs,
- ExecutionSystem &exec_system)
+ const rcti &area,
+ Span<MemoryBuffer *> inputs)
{
- for (int current_pass = 0; current_pass < m_num_passes; current_pass++) {
- update_memory_buffer_started(output, output_area, inputs, exec_system, current_pass);
- exec_system.execute_work(output_area, [=, &exec_system](const rcti &split_rect) {
- update_memory_buffer_partial(output, split_rect, inputs, exec_system, current_pass);
+ 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) {
+ update_memory_buffer_partial(output, split_rect, inputs);
});
- update_memory_buffer_finished(output, output_area, inputs, exec_system, current_pass);
+ 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 e86b1d303f9..a7e574ca745 100644
--- a/source/blender/compositor/intern/COM_MultiThreadedOperation.h
+++ b/source/blender/compositor/intern/COM_MultiThreadedOperation.h
@@ -27,7 +27,11 @@ class MultiThreadedOperation : public NodeOperation {
/**
* Number of execution passes.
*/
- int m_num_passes;
+ int num_passes_;
+ /**
+ * Current execution pass.
+ */
+ int current_pass_;
protected:
MultiThreadedOperation();
@@ -36,38 +40,31 @@ class MultiThreadedOperation : public NodeOperation {
* Called before an update memory buffer pass is executed. Single-threaded calls.
*/
virtual void update_memory_buffer_started(MemoryBuffer *UNUSED(output),
- const rcti &UNUSED(output_rect),
- blender::Span<MemoryBuffer *> UNUSED(inputs),
- ExecutionSystem &UNUSED(exec_system),
- int UNUSED(current_pass))
+ const rcti &UNUSED(area),
+ Span<MemoryBuffer *> UNUSED(inputs))
{
}
/**
- * Executes operation updating output memory buffer on output_rect area. Multi-threaded calls.
+ * Executes operation updating a memory buffer area. Multi-threaded calls.
*/
virtual void update_memory_buffer_partial(MemoryBuffer *output,
- const rcti &output_rect,
- blender::Span<MemoryBuffer *> inputs,
- ExecutionSystem &exec_system,
- int current_pass) = 0;
+ const rcti &area,
+ Span<MemoryBuffer *> inputs) = 0;
/**
* Called after an update memory buffer pass is executed. Single-threaded calls.
*/
virtual void update_memory_buffer_finished(MemoryBuffer *UNUSED(output),
- const rcti &UNUSED(output_rect),
- blender::Span<MemoryBuffer *> UNUSED(inputs),
- ExecutionSystem &UNUSED(exec_system),
- int UNUSED(current_pass))
+ const rcti &UNUSED(area),
+ Span<MemoryBuffer *> UNUSED(inputs))
{
}
private:
void update_memory_buffer(MemoryBuffer *output,
- const rcti &output_rect,
- blender::Span<MemoryBuffer *> inputs,
- ExecutionSystem &exec_system) override;
+ const rcti &area,
+ 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 c52fca81b12..43710e62496 100644
--- a/source/blender/compositor/intern/COM_NodeOperation.cc
+++ b/source/blender/compositor/intern/COM_NodeOperation.cc
@@ -211,12 +211,12 @@ bool NodeOperation::determineDependingAreaOfInterest(rcti *input,
* caller must clamp it.
* TODO: See if it's possible to use parameter overloading (input_id for example).
*
- * \param input_op_idx: Input operation index for which we want to calculate the area being read.
+ * \param input_idx: Input operation index for which we want to calculate the area being read.
* \param output_area: Area being rendered by this operation.
* \param r_input_area: Returned input operation area that needs to be read in order to render
* given output area.
*/
-void NodeOperation::get_area_of_interest(const int input_op_idx,
+void NodeOperation::get_area_of_interest(const int input_idx,
const rcti &output_area,
rcti &r_input_area)
{
@@ -226,7 +226,7 @@ void NodeOperation::get_area_of_interest(const int input_op_idx,
else {
/* Non full-frame operations never implement this method. To ensure correctness assume
* whole area is used. */
- NodeOperation *input_op = getInputOperation(input_op_idx);
+ NodeOperation *input_op = getInputOperation(input_idx);
BLI_rcti_init(&r_input_area, 0, input_op->getWidth(), 0, input_op->getHeight());
}
}
@@ -241,7 +241,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.");
}
/**
@@ -249,18 +249,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);
}
}
@@ -269,12 +267,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();
}
@@ -284,8 +281,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);
@@ -299,7 +295,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);
@@ -353,6 +349,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;
}
@@ -362,8 +359,10 @@ void NodeOperation::remove_buffers_and_restore_original_inputs(
{
BLI_assert(original_inputs_links.size() == getNumberOfInputSockets());
for (int i = 0; i < original_inputs_links.size(); i++) {
- BLI_assert(typeid(*getInputOperation(i)) == typeid(BufferOperation));
-
+ 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);
delete &input_socket->getLink()->getOperation();
input_socket->setLink(original_inputs_links[i]);
diff --git a/source/blender/compositor/intern/COM_NodeOperation.h b/source/blender/compositor/intern/COM_NodeOperation.h
index 2ed509d1250..9667d82c289 100644
--- a/source/blender/compositor/intern/COM_NodeOperation.h
+++ b/source/blender/compositor/intern/COM_NodeOperation.h
@@ -221,7 +221,7 @@ struct NodeOperationFlags {
/**
* Is this a set operation (value, color, vector).
- * TODO: To be replaced by is_constant flag once tiled implementation is removed.
+ * 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;
@@ -313,6 +313,11 @@ class NodeOperation {
eExecutionModel execution_model_;
/**
+ * Compositor execution model.
+ */
+ eExecutionModel execution_model_;
+
+ /**
* Width of the output of this operation.
*/
unsigned int m_width;
@@ -337,11 +342,18 @@ class NodeOperation {
*/
NodeOperationFlags flags;
+ ExecutionSystem *exec_system_;
+
public:
virtual ~NodeOperation()
{
}
+ void set_execution_model(const eExecutionModel model)
+ {
+ execution_model_ = model;
+ }
+
void set_name(const std::string name)
{
m_name = name;
@@ -431,6 +443,12 @@ class NodeOperation {
{
this->m_btree = tree;
}
+
+ void set_execution_system(ExecutionSystem *system)
+ {
+ exec_system_ = system;
+ }
+
virtual void initExecution();
/**
@@ -598,25 +616,21 @@ 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(output_area),
- Span<MemoryBuffer *> UNUSED(inputs),
- ExecutionSystem &UNUSED(exec_system))
+ const rcti &UNUSED(area),
+ Span<MemoryBuffer *> UNUSED(inputs))
{
}
/**
* Get input operation area being read by this operation on rendering given output area.
*/
- virtual void get_area_of_interest(int input_op_idx, const rcti &output_area, rcti &r_input_area);
+ virtual void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area);
void get_area_of_interest(NodeOperation *input_op, const rcti &output_area, rcti &r_input_area);
/** \} */
@@ -707,13 +721,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 c81a5a2bd98..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) {
@@ -129,6 +141,30 @@ void NodeOperationBuilder::addOperation(NodeOperation *operation)
if (m_current_node) {
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,
@@ -137,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.
*/
@@ -283,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;
@@ -536,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_SharedOperationBuffers.cc b/source/blender/compositor/intern/COM_SharedOperationBuffers.cc
index b2c43147025..02e4ff546ce 100644
--- a/source/blender/compositor/intern/COM_SharedOperationBuffers.cc
+++ b/source/blender/compositor/intern/COM_SharedOperationBuffers.cc
@@ -26,7 +26,7 @@ SharedOperationBuffers::SharedOperationBuffers() : buffers_()
{
}
SharedOperationBuffers::BufferData::BufferData()
- : buffer(nullptr), render_areas(), render_bounds({0}), registered_reads(0), received_reads(0)
+ : buffer(nullptr), registered_reads(0), received_reads(0), is_rendered(false)
{
}
@@ -99,7 +99,7 @@ const rcti &SharedOperationBuffers::get_render_bounds(NodeOperation *op)
*/
bool SharedOperationBuffers::is_operation_rendered(NodeOperation *op)
{
- return get_buffer_data(op).buffer != nullptr;
+ return get_buffer_data(op).is_rendered;
}
/**
@@ -112,6 +112,7 @@ void SharedOperationBuffers::set_rendered_buffer(NodeOperation *op,
BLI_assert(buf_data.received_reads == 0);
BLI_assert(buf_data.buffer == nullptr);
buf_data.buffer = std::move(buffer);
+ buf_data.is_rendered = true;
}
/**
diff --git a/source/blender/compositor/intern/COM_SharedOperationBuffers.h b/source/blender/compositor/intern/COM_SharedOperationBuffers.h
index 24c80102f9c..ddef724ff4a 100644
--- a/source/blender/compositor/intern/COM_SharedOperationBuffers.h
+++ b/source/blender/compositor/intern/COM_SharedOperationBuffers.h
@@ -43,6 +43,7 @@ class SharedOperationBuffers {
rcti render_bounds;
int registered_reads;
int received_reads;
+ bool is_rendered;
} BufferData;
blender::Map<NodeOperation *, BufferData> buffers_;
diff --git a/source/blender/compositor/intern/COM_WorkPackage.h b/source/blender/compositor/intern/COM_WorkPackage.h
index 4d503022120..f0f53f300a5 100644
--- a/source/blender/compositor/intern/COM_WorkPackage.h
+++ b/source/blender/compositor/intern/COM_WorkPackage.h
@@ -18,6 +18,10 @@
#pragma once
+#ifdef WITH_CXX_GUARDEDALLOC
+# include "MEM_guardedalloc.h"
+#endif
+
#include "COM_Enums.h"
#include "BLI_rect.h"
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) {