Compositor: Full frame input nodes

Adds full frame implementation to "Bokeh Image" node, "Track Position"
node, `SetVectorOperation` and `MovieClipAttribute`.
The other nodes in "Input" submenu are implemented separately.

`MovieClipAttribute` needs resolution to calculate its constant value, it can't be constant folded,
which requires it to be a `ConstantOperation`. Now `ConstantOperation` contemplate this case
and any operation that is always constant without depending on inputs should implement it.
If in the future an operation needs to get an input constant element during
`determineResolution` it must first determine its input resolution.

The nodes have no functional changes.

Reviewed By: jbakker

Differential Revision: https://developer.blender.org/D12090
This commit is contained in:
Manuel Castilla 2021-08-10 15:24:28 +02:00
parent 5deb3229a0
commit 1a9b9dd64d
16 changed files with 144 additions and 40 deletions

View File

@ -32,6 +32,7 @@ BufferOperation::BufferOperation(MemoryBuffer *buffer, DataType data_type)
setResolution(resolution);
addOutputSocket(data_type);
flags.is_constant_operation = buffer_->is_a_single_elem();
flags.is_fullframe_operation = false;
}
const float *BufferOperation::get_constant_elem()

View File

@ -44,7 +44,9 @@ static bool is_constant_foldable(NodeOperation *operation)
{
if (operation->get_flags().can_be_constant && !operation->get_flags().is_constant_operation) {
for (int i = 0; i < operation->getNumberOfInputSockets(); i++) {
if (!operation->get_input_operation(i)->get_flags().is_constant_operation) {
NodeOperation *input = operation->get_input_operation(i);
if (!input->get_flags().is_constant_operation ||
!static_cast<ConstantOperation *>(input)->can_get_constant_elem()) {
return false;
}
}

View File

@ -178,21 +178,27 @@ 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:
if (typeid(*operation) == typeid(SetValueOperation)) {
const float value = ((SetValueOperation *)operation)->getValue();
case DataType::Value: {
ConstantOperation *constant = operation->get_flags().is_constant_operation ?
static_cast<ConstantOperation *>(operation) :
nullptr;
if (constant && constant->can_get_constant_elem()) {
const float value = *constant->get_constant_elem();
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:
}
case DataType::Vector: {
len += snprintf(str + len, maxlen > len ? maxlen - len : 0, "Vector");
break;
case DataType::Color:
}
case DataType::Color: {
len += snprintf(str + len, maxlen > len ? maxlen - len : 0, "Color");
break;
}
}
}
len += snprintf(str + len, maxlen > len ? maxlen - len : 0, "}");

View File

@ -260,6 +260,11 @@ class MemoryBuffer {
return this->m_num_channels;
}
uint8_t get_elem_bytes_len() const
{
return this->m_num_channels * sizeof(float);
}
/**
* Get all buffer elements as a range with no offsets.
*/

View File

@ -110,6 +110,31 @@ void BokehImageOperation::executePixelSampled(float output[4],
output[3] = (insideBokehMax + insideBokehMed + insideBokehMin) / 3.0f;
}
void BokehImageOperation::update_memory_buffer_partial(MemoryBuffer *output,
const rcti &area,
Span<MemoryBuffer *> UNUSED(inputs))
{
const float shift = this->m_data->lensshift;
const float shift2 = shift / 2.0f;
const float distance = this->m_circularDistance;
for (BuffersIterator<float> it = output->iterate_with({}, area); !it.is_end(); ++it) {
const float insideBokehMax = isInsideBokeh(distance, it.x, it.y);
const float insideBokehMed = isInsideBokeh(distance - fabsf(shift2 * distance), it.x, it.y);
const float insideBokehMin = isInsideBokeh(distance - fabsf(shift * distance), it.x, it.y);
if (shift < 0) {
it.out[0] = insideBokehMax;
it.out[1] = insideBokehMed;
it.out[2] = insideBokehMin;
}
else {
it.out[0] = insideBokehMin;
it.out[1] = insideBokehMed;
it.out[2] = insideBokehMax;
}
it.out[3] = (insideBokehMax + insideBokehMed + insideBokehMin) / 3.0f;
}
}
void BokehImageOperation::deinitExecution()
{
if (this->m_deleteData) {

View File

@ -18,7 +18,7 @@
#pragma once
#include "COM_NodeOperation.h"
#include "COM_MultiThreadedOperation.h"
namespace blender::compositor {
@ -49,7 +49,7 @@ namespace blender::compositor {
* With a simple compare it can be detected if the evaluated pixel is between the outer and inner
*edge.
*/
class BokehImageOperation : public NodeOperation {
class BokehImageOperation : public MultiThreadedOperation {
private:
/**
* \brief Settings of the bokeh image
@ -151,6 +151,10 @@ class BokehImageOperation : public NodeOperation {
{
this->m_deleteData = true;
}
void update_memory_buffer_partial(MemoryBuffer *output,
const rcti &area,
Span<MemoryBuffer *> inputs) override;
};
} // namespace blender::compositor

View File

@ -22,7 +22,24 @@ namespace blender::compositor {
ConstantOperation::ConstantOperation()
{
needs_resolution_to_get_constant_ = false;
flags.is_constant_operation = true;
flags.is_fullframe_operation = true;
}
bool ConstantOperation::can_get_constant_elem() const
{
return !needs_resolution_to_get_constant_ || this->flags.is_resolution_set;
}
void ConstantOperation::update_memory_buffer(MemoryBuffer *output,
const rcti &area,
Span<MemoryBuffer *> UNUSED(inputs))
{
BLI_assert(output->is_a_single_elem());
const float *constant = get_constant_elem();
float *out = output->get_elem(area.xmin, area.ymin);
memcpy(out, constant, output->get_elem_bytes_len());
}
} // namespace blender::compositor

View File

@ -22,15 +22,27 @@
namespace blender::compositor {
/* TODO(manzanilla): After removing tiled implementation, implement a default #determineResolution
* for all constant operations and make all initialization and deinitilization methods final. */
/**
* Base class for primitive constant operations (Color/Vector/Value). The rest of operations that
* can be constant are evaluated into primitives during constant folding.
* Base class for operations that are always constant. Operations that can be constant only when
* all their inputs are so, are evaluated into primitive constants (Color/Vector/Value) during
* constant folding.
*/
class ConstantOperation : public NodeOperation {
protected:
bool needs_resolution_to_get_constant_;
public:
ConstantOperation();
/** May require resolution to be already determined. */
virtual const float *get_constant_elem() = 0;
bool can_get_constant_elem() const;
void update_memory_buffer(MemoryBuffer *output,
const rcti &area,
Span<MemoryBuffer *> inputs) final;
};
} // namespace blender::compositor

View File

@ -29,10 +29,21 @@ MovieClipAttributeOperation::MovieClipAttributeOperation()
this->m_framenumber = 0;
this->m_attribute = MCA_X;
this->m_invert = false;
needs_resolution_to_get_constant_ = true;
is_value_calculated_ = false;
}
void MovieClipAttributeOperation::initExecution()
{
if (!is_value_calculated_) {
calc_value();
}
}
void MovieClipAttributeOperation::calc_value()
{
BLI_assert(this->get_flags().is_resolution_set);
is_value_calculated_ = true;
if (this->m_clip == nullptr) {
return;
}
@ -83,4 +94,12 @@ void MovieClipAttributeOperation::determineResolution(unsigned int resolution[2]
resolution[1] = preferredResolution[1];
}
const float *MovieClipAttributeOperation::get_constant_elem()
{
if (!is_value_calculated_) {
calc_value();
}
return &m_value;
}
} // namespace blender::compositor

View File

@ -18,6 +18,7 @@
#pragma once
#include "COM_ConstantOperation.h"
#include "COM_NodeOperation.h"
#include "DNA_movieclip_types.h"
@ -33,13 +34,14 @@ typedef enum MovieClipAttribute {
* this program converts an input color to an output value.
* it assumes we are in sRGB color space.
*/
class MovieClipAttributeOperation : public NodeOperation {
class MovieClipAttributeOperation : public ConstantOperation {
private:
MovieClip *m_clip;
float m_value;
int m_framenumber;
bool m_invert;
MovieClipAttribute m_attribute;
bool is_value_calculated_;
public:
/**
@ -56,6 +58,8 @@ class MovieClipAttributeOperation : public NodeOperation {
void determineResolution(unsigned int resolution[2],
unsigned int preferredResolution[2]) override;
const float *get_constant_elem() override;
void setMovieClip(MovieClip *clip)
{
this->m_clip = clip;
@ -72,6 +76,9 @@ class MovieClipAttributeOperation : public NodeOperation {
{
this->m_invert = invert;
}
private:
void calc_value();
};
} // namespace blender::compositor

View File

@ -24,7 +24,6 @@ SetColorOperation::SetColorOperation()
{
this->addOutputSocket(DataType::Color);
flags.is_set_operation = true;
flags.is_fullframe_operation = true;
}
void SetColorOperation::executePixelSampled(float output[4],
@ -42,13 +41,4 @@ void SetColorOperation::determineResolution(unsigned int resolution[2],
resolution[1] = preferredResolution[1];
}
void SetColorOperation::update_memory_buffer(MemoryBuffer *output,
const rcti &area,
Span<MemoryBuffer *> UNUSED(inputs))
{
BLI_assert(output->is_a_single_elem());
float *out_elem = output->get_elem(area.xmin, area.ymin);
copy_v4_v4(out_elem, m_color);
}
} // namespace blender::compositor

View File

@ -85,10 +85,6 @@ class SetColorOperation : public ConstantOperation {
void determineResolution(unsigned int resolution[2],
unsigned int preferredResolution[2]) override;
void update_memory_buffer(MemoryBuffer *output,
const rcti &area,
Span<MemoryBuffer *> inputs) override;
};
} // namespace blender::compositor

View File

@ -24,7 +24,6 @@ SetValueOperation::SetValueOperation()
{
this->addOutputSocket(DataType::Value);
flags.is_set_operation = true;
flags.is_fullframe_operation = true;
}
void SetValueOperation::executePixelSampled(float output[4],
@ -42,13 +41,4 @@ void SetValueOperation::determineResolution(unsigned int resolution[2],
resolution[1] = preferredResolution[1];
}
void SetValueOperation::update_memory_buffer(MemoryBuffer *output,
const rcti &area,
Span<MemoryBuffer *> UNUSED(inputs))
{
BLI_assert(output->is_a_single_elem());
float *out_elem = output->get_elem(area.xmin, area.ymin);
*out_elem = m_value;
}
} // namespace blender::compositor

View File

@ -56,9 +56,6 @@ class SetValueOperation : public ConstantOperation {
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
void determineResolution(unsigned int resolution[2],
unsigned int preferredResolution[2]) override;
void update_memory_buffer(MemoryBuffer *output,
const rcti &area,
Span<MemoryBuffer *> inputs) override;
};
} // namespace blender::compositor

View File

@ -42,14 +42,24 @@ TrackPositionOperation::TrackPositionOperation()
this->m_relativeFrame = 0;
this->m_speed_output = false;
flags.is_set_operation = true;
is_track_position_calculated_ = false;
}
void TrackPositionOperation::initExecution()
{
if (!is_track_position_calculated_) {
calc_track_position();
}
}
void TrackPositionOperation::calc_track_position()
{
is_track_position_calculated_ = true;
MovieTracking *tracking = nullptr;
MovieClipUser user = {0};
MovieTrackingObject *object;
track_position_ = 0;
zero_v2(this->m_markerPos);
zero_v2(this->m_relativePos);
@ -114,6 +124,14 @@ void TrackPositionOperation::initExecution()
}
}
}
track_position_ = this->m_markerPos[this->m_axis] - this->m_relativePos[this->m_axis];
if (this->m_axis == 0) {
track_position_ *= this->m_width;
}
else {
track_position_ *= this->m_height;
}
}
void TrackPositionOperation::executePixelSampled(float output[4],
@ -131,6 +149,14 @@ void TrackPositionOperation::executePixelSampled(float output[4],
}
}
const float *TrackPositionOperation::get_constant_elem()
{
if (!is_track_position_calculated_) {
calc_track_position();
}
return &track_position_;
}
void TrackPositionOperation::determineResolution(unsigned int resolution[2],
unsigned int preferredResolution[2])
{

View File

@ -20,7 +20,7 @@
#include <string.h>
#include "COM_NodeOperation.h"
#include "COM_ConstantOperation.h"
#include "DNA_movieclip_types.h"
#include "DNA_tracking_types.h"
@ -33,7 +33,7 @@ namespace blender::compositor {
/**
* Class with implementation of green screen gradient rasterization
*/
class TrackPositionOperation : public NodeOperation {
class TrackPositionOperation : public ConstantOperation {
protected:
MovieClip *m_movieClip;
int m_framenumber;
@ -47,6 +47,8 @@ class TrackPositionOperation : public NodeOperation {
int m_width, m_height;
float m_markerPos[2];
float m_relativePos[2];
float track_position_;
bool is_track_position_calculated_;
/**
* Determine the output resolution. The resolution is retrieved from the Renderer
@ -93,6 +95,11 @@ class TrackPositionOperation : public NodeOperation {
void initExecution() override;
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
const float *get_constant_elem() override;
private:
void calc_track_position();
};
} // namespace blender::compositor