diff options
Diffstat (limited to 'source/blender/compositor/intern')
-rw-r--r-- | source/blender/compositor/intern/COM_BufferArea.h | 198 | ||||
-rw-r--r-- | source/blender/compositor/intern/COM_BufferRange.h | 171 | ||||
-rw-r--r-- | source/blender/compositor/intern/COM_BuffersIterator.h | 164 | ||||
-rw-r--r-- | source/blender/compositor/intern/COM_Debug.cc | 45 | ||||
-rw-r--r-- | source/blender/compositor/intern/COM_Debug.h | 17 | ||||
-rw-r--r-- | source/blender/compositor/intern/COM_FullFrameExecutionModel.cc | 1 | ||||
-rw-r--r-- | source/blender/compositor/intern/COM_MemoryBuffer.cc | 14 | ||||
-rw-r--r-- | source/blender/compositor/intern/COM_MemoryBuffer.h | 29 |
8 files changed, 638 insertions, 1 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..621ffea5bc3 --- /dev/null +++ b/source/blender/compositor/intern/COM_BufferArea.h @@ -0,0 +1,198 @@ +/* + * 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 <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 + { + if (elem_stride_ == 0) { + /* Iterate a single element. */ + return TIterator(buffer_, 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, buffer_width_, BLI_rcti_size_x(this), elem_stride_); + } + + template<typename TIterator> constexpr TIterator end_iterator() const + { + if (elem_stride_ == 0) { + /* Iterate a single element. */ + return TIterator(buffer_ + 1, 1, 1, 1); + } + + T *end_ptr = buffer_ + (intptr_t)(this->ymax - 1) * buffer_width_ * elem_stride_ + + (intptr_t)this->xmax * elem_stride_; + return TIterator(end_ptr, buffer_width_, BLI_rcti_size_x(this), 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_; + + public: + constexpr BufferAreaIterator() = default; + + constexpr BufferAreaIterator(T *current, 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) + { + } + + constexpr BufferAreaIterator &operator++() + { + current_ += elem_stride_; + if (current_ == row_end_) { + 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_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..58dbd9c6f59 --- /dev/null +++ b/source/blender/compositor/intern/COM_BuffersIterator.h @@ -0,0 +1,164 @@ +/* + * 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 { + const T *out_end_; + const T *out_row_end_; + int out_elem_stride_; + int out_row_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: + /** + * 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; + } + if (out == out_row_end_) { + out += out_rows_gap_; + out_row_end_ += out_row_stride_; + 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_width: Number of elements in an output buffer row. + * \param area: Rectangle area to be iterated in all buffers. + * \param elem_stride: Output buffer element stride. + */ + BuffersIteratorBuilder(T *output, int buffer_width, const rcti &area, int elem_stride = 1) + : area_(area), is_built_(false) + { + iterator_.out_elem_stride_ = elem_stride; + iterator_.out_row_stride_ = buffer_width * elem_stride; + iterator_.out_rows_gap_ = iterator_.out_row_stride_ - BLI_rcti_size_x(&area) * elem_stride; + iterator_.out = output + (intptr_t)area.ymin * iterator_.out_row_stride_ + + (intptr_t)area.xmin * elem_stride; + iterator_.out_row_end_ = iterator_.out + (intptr_t)BLI_rcti_size_x(&area) * elem_stride; + iterator_.out_end_ = iterator_.out_row_end_ + + (intptr_t)iterator_.out_row_stride_ * (BLI_rcti_size_y(&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, buffer_width, {0, buffer_width, 0, buffer_height}, elem_stride) + { + } + + /** + * Add an input buffer to be iterated. Its coordinates must be correlated with the output. + */ + void add_input(const T *input, int buffer_width, int elem_stride = 1) + { + BLI_assert(!is_built_); + typename Iterator::In in; + in.elem_stride = elem_stride; + in.rows_gap = buffer_width * elem_stride - BLI_rcti_size_x(&area_) * elem_stride; + in.in = input + area_.ymin * buffer_width * elem_stride + area_.xmin * elem_stride; + iterator_.ins_.append(std::move(in)); + } + + /** + * 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_Debug.cc b/source/blender/compositor/intern/COM_Debug.cc index abef4517b3e..c0460aed4a4 100644 --- a/source/blender/compositor/intern/COM_Debug.cc +++ b/source/blender/compositor/intern/COM_Debug.cc @@ -31,6 +31,8 @@ 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" @@ -50,7 +52,7 @@ std::string DebugInfo::m_current_node_name; std::string DebugInfo::m_current_op_name; DebugInfo::GroupStateMap DebugInfo::m_group_states; -static std::string operation_class_name(NodeOperation *op) +static std::string operation_class_name(const NodeOperation *op) { std::string full_name = typeid(*op).name(); /* Remove name-spaces. */ @@ -452,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 53461e13f48..23d99c7e529 100644 --- a/source/blender/compositor/intern/COM_Debug.h +++ b/source/blender/compositor/intern/COM_Debug.h @@ -30,6 +30,9 @@ 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; @@ -75,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) @@ -118,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: @@ -133,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_FullFrameExecutionModel.cc b/source/blender/compositor/intern/COM_FullFrameExecutionModel.cc index 3b0a9172871..9f6904bb306 100644 --- a/source/blender/compositor/intern/COM_FullFrameExecutionModel.cc +++ b/source/blender/compositor/intern/COM_FullFrameExecutionModel.cc @@ -104,6 +104,7 @@ void FullFrameExecutionModel::render_operation(NodeOperation *op) op->render(op_buf, areas, input_bufs); active_buffers_.set_rendered_buffer(op, std::unique_ptr<MemoryBuffer>(op_buf)); + DebugInfo::operation_rendered(op, op_buf); operation_finished(op); } diff --git a/source/blender/compositor/intern/COM_MemoryBuffer.cc b/source/blender/compositor/intern/COM_MemoryBuffer.cc index c7bddddd0e6..f4f58146de4 100644 --- a/source/blender/compositor/intern/COM_MemoryBuffer.cc +++ b/source/blender/compositor/intern/COM_MemoryBuffer.cc @@ -129,6 +129,20 @@ 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, getWidth(), area, elem_stride); + for (MemoryBuffer *input : inputs) { + builder.add_input(input->getBuffer(), input->getWidth(), input->elem_stride); + } + return builder.build(); +} + /** * Converts a single elem buffer to a full size buffer (allocates memory for all * elements in resolution). diff --git a/source/blender/compositor/intern/COM_MemoryBuffer.h b/source/blender/compositor/intern/COM_MemoryBuffer.h index 4ad0872b0b7..048ed4c5d6e 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" @@ -239,6 +242,32 @@ class MemoryBuffer { } /** + * 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 */ |