Compositor: Buffer iterators

Currently we mostly iterate buffer areas using x/y loops or through
utility methods extending from base classes.

To simplify code in simple operations this commit adds wrappers for
specifying buffer areas and their iterators for raw buffers with any
element stride:
- BufferRange: Specifies a range of contiguous buffer elements from a
 given element index.
- BufferRangeIterator: Iterates elements in a BufferRange.
- BufferArea: Specifies a rectangle area of elements in a 2D buffer.
- BufferAreaIterator: Iterates elements in a BufferArea.
- BuffersIterator: Simultaneously iterates an area of elements in an
 output buffer and any number of input buffers.
- BuffersIteratorBuilder: Helper for building BuffersIterator adding
 buffers one by one.
For iterating areas coordinates it adds `XRange` and `YRange` methods
that return `IndexRange`.

Reviewed By: jbakker

Differential Revision: https://developer.blender.org/D11882
This commit is contained in:
Manuel Castilla 2021-07-19 17:55:46 +02:00
parent 582c5530b6
commit 45b46e5de9
7 changed files with 602 additions and 0 deletions

View File

@ -49,8 +49,11 @@ set(SRC
COM_compositor.h
COM_defines.h
intern/COM_BufferArea.h
intern/COM_BufferOperation.cc
intern/COM_BufferOperation.h
intern/COM_BufferRange.h
intern/COM_BuffersIterator.h
intern/COM_CPUDevice.cc
intern/COM_CPUDevice.h
intern/COM_ChunkOrder.cc

View File

@ -18,6 +18,9 @@
#pragma once
#include "BLI_index_range.hh"
#include "BLI_rect.h"
namespace blender::compositor {
enum class eExecutionModel {
@ -109,4 +112,24 @@ constexpr float COM_PREVIEW_SIZE = 140.f;
constexpr float COM_RULE_OF_THIRDS_DIVIDER = 100.0f;
constexpr float COM_BLUR_BOKEH_PIXELS = 512;
constexpr IndexRange XRange(const rcti &area)
{
return IndexRange(area.xmin, area.xmax - area.xmin);
}
constexpr IndexRange YRange(const rcti &area)
{
return IndexRange(area.ymin, area.ymax - area.ymin);
}
constexpr IndexRange XRange(const rcti *area)
{
return XRange(*area);
}
constexpr IndexRange YRange(const rcti *area)
{
return YRange(*area);
}
} // namespace blender::compositor

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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).

View File

@ -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"
@ -238,6 +241,32 @@ class MemoryBuffer {
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