Compositor: Full frame Image node

Adds full frame implementation to Image node operations.
Mostly refactored into buffer utility methods for reuse in other
operations.
No functional changes.
1.8x faster than tiled fallback.

Reviewed By: jbakker

Differential Revision: https://developer.blender.org/D11559
This commit is contained in:
Manuel Castilla 2021-06-23 17:21:17 +02:00
parent 8f4d991594
commit 35db01325f
7 changed files with 456 additions and 21 deletions

View File

@ -62,6 +62,24 @@ constexpr int COM_data_type_num_channels(const DataType datatype)
constexpr int COM_DATA_TYPE_VALUE_CHANNELS = COM_data_type_num_channels(DataType::Value);
constexpr int COM_DATA_TYPE_COLOR_CHANNELS = COM_data_type_num_channels(DataType::Color);
constexpr float COM_VALUE_ZERO[1] = {0.0f};
/**
* Utility to get data type for given number of channels.
*/
constexpr DataType COM_num_channels_data_type(const int num_channels)
{
switch (num_channels) {
case 1:
return DataType::Value;
case 3:
return DataType::Vector;
case 4:
default:
return DataType::Color;
}
}
// configurable items
// chunk size determination

View File

@ -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,6 +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");
owns_data_ = true;
this->m_state = state;
this->m_datatype = memoryProxy->getDataType();
@ -44,12 +66,44 @@ 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");
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 rcti &rect,
const bool is_a_single_elem)
{
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;
owns_data_ = false;
m_state = MemoryBufferState::Temporary;
set_strides();
}
MemoryBuffer::MemoryBuffer(const MemoryBuffer &src)
: MemoryBuffer(src.m_datatype, src.m_rect, false)
{
@ -112,31 +166,195 @@ float MemoryBuffer::get_max_value(const rcti &rect) const
MemoryBuffer::~MemoryBuffer()
{
if (this->m_buffer) {
if (this->m_buffer && owns_data_) {
MEM_freeN(this->m_buffer);
this->m_buffer = nullptr;
}
}
void MemoryBuffer::copy_from(const MemoryBuffer *src, const rcti &area)
{
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);
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);
}
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)
{
BLI_assert(!this->is_a_single_elem());
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;
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));
}
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])
@ -196,4 +414,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

View File

@ -106,6 +106,11 @@ class MemoryBuffer {
*/
bool m_is_a_single_elem;
/**
* Whether MemoryBuffer owns buffer data.
*/
bool owns_data_;
public:
/**
* \brief construct new temporarily MemoryBuffer for an area
@ -117,6 +122,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);
/**
* Copy constructor
*/
@ -223,7 +233,7 @@ 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;
}
@ -404,6 +414,53 @@ 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
@ -452,6 +509,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

View File

@ -44,6 +44,7 @@ BaseImageOperation::BaseImageOperation()
this->m_imageheight = 0;
this->m_framenumber = 0;
this->m_depthBuffer = nullptr;
depth_buffer_ = nullptr;
this->m_numberOfChannels = 0;
this->m_rd = nullptr;
this->m_viewName = nullptr;
@ -91,6 +92,9 @@ void BaseImageOperation::initExecution()
this->m_imageFloatBuffer = stackbuf->rect_float;
this->m_imageByteBuffer = stackbuf->rect;
this->m_depthBuffer = stackbuf->zbuf_float;
if (stackbuf->zbuf_float) {
depth_buffer_ = new MemoryBuffer(stackbuf->zbuf_float, 1, stackbuf->x, stackbuf->y);
}
this->m_imagewidth = stackbuf->x;
this->m_imageheight = stackbuf->y;
this->m_numberOfChannels = stackbuf->channels;
@ -102,6 +106,10 @@ void BaseImageOperation::deinitExecution()
this->m_imageFloatBuffer = nullptr;
this->m_imageByteBuffer = nullptr;
BKE_image_release_ibuf(this->m_image, this->m_buffer, nullptr);
if (depth_buffer_) {
delete depth_buffer_;
depth_buffer_ = nullptr;
}
}
void BaseImageOperation::determineResolution(unsigned int resolution[2],
@ -170,6 +178,13 @@ void ImageOperation::executePixelSampled(float output[4], float x, float y, Pixe
}
}
void ImageOperation::update_memory_buffer_partial(MemoryBuffer *output,
const rcti &area,
Span<MemoryBuffer *> UNUSED(inputs))
{
output->copy_from(m_buffer, area, true);
}
void ImageAlphaOperation::executePixelSampled(float output[4],
float x,
float y,
@ -187,6 +202,13 @@ void ImageAlphaOperation::executePixelSampled(float output[4],
}
}
void ImageAlphaOperation::update_memory_buffer_partial(MemoryBuffer *output,
const rcti &area,
Span<MemoryBuffer *> UNUSED(inputs))
{
output->copy_from(m_buffer, area, 3, COM_DATA_TYPE_VALUE_CHANNELS, 0);
}
void ImageDepthOperation::executePixelSampled(float output[4],
float x,
float y,
@ -206,4 +228,16 @@ void ImageDepthOperation::executePixelSampled(float output[4],
}
}
void ImageDepthOperation::update_memory_buffer_partial(MemoryBuffer *output,
const rcti &area,
Span<MemoryBuffer *> UNUSED(inputs))
{
if (depth_buffer_) {
output->copy_from(depth_buffer_, area);
}
else {
output->fill(area, COM_VALUE_ZERO);
}
}
} // namespace blender::compositor

View File

@ -21,7 +21,7 @@
#include "BKE_image.h"
#include "BLI_listbase.h"
#include "BLI_utildefines.h"
#include "COM_NodeOperation.h"
#include "COM_MultiThreadedOperation.h"
#include "MEM_guardedalloc.h"
#include "RE_pipeline.h"
@ -32,14 +32,17 @@ namespace blender::compositor {
/**
* \brief Base class for all image operations
*/
class BaseImageOperation : public NodeOperation {
class BaseImageOperation : public MultiThreadedOperation {
protected:
ImBuf *m_buffer;
Image *m_image;
ImageUser *m_imageUser;
/* TODO: Remove raw buffers when removing Tiled implementation. */
float *m_imageFloatBuffer;
unsigned int *m_imageByteBuffer;
float *m_depthBuffer;
MemoryBuffer *depth_buffer_;
int m_imageheight;
int m_imagewidth;
int m_framenumber;
@ -87,6 +90,10 @@ class ImageOperation : public BaseImageOperation {
*/
ImageOperation();
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
void update_memory_buffer_partial(MemoryBuffer *output,
const rcti &area,
Span<MemoryBuffer *> inputs) override;
};
class ImageAlphaOperation : public BaseImageOperation {
public:
@ -95,6 +102,10 @@ class ImageAlphaOperation : public BaseImageOperation {
*/
ImageAlphaOperation();
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
void update_memory_buffer_partial(MemoryBuffer *output,
const rcti &area,
Span<MemoryBuffer *> inputs) override;
};
class ImageDepthOperation : public BaseImageOperation {
public:
@ -103,6 +114,10 @@ class ImageDepthOperation : public BaseImageOperation {
*/
ImageDepthOperation();
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
void update_memory_buffer_partial(MemoryBuffer *output,
const rcti &area,
Span<MemoryBuffer *> inputs) override;
};
} // namespace blender::compositor

View File

@ -51,6 +51,13 @@ ImBuf *MultilayerBaseOperation::getImBuf()
return nullptr;
}
void MultilayerBaseOperation::update_memory_buffer_partial(MemoryBuffer *output,
const rcti &area,
Span<MemoryBuffer *> UNUSED(inputs))
{
output->copy_from(m_buffer, area);
}
std::unique_ptr<MetaData> MultilayerColorOperation::getMetaData()
{
BLI_assert(this->m_buffer);

View File

@ -37,6 +37,10 @@ class MultilayerBaseOperation : public BaseImageOperation {
* Constructor
*/
MultilayerBaseOperation(RenderLayer *render_layer, RenderPass *render_pass, int view);
void update_memory_buffer_partial(MemoryBuffer *output,
const rcti &area,
Span<MemoryBuffer *> inputs) override;
};
class MultilayerColorOperation : public MultilayerBaseOperation {