diff options
Diffstat (limited to 'source/blender/compositor')
60 files changed, 980 insertions, 116 deletions
diff --git a/source/blender/compositor/CMakeLists.txt b/source/blender/compositor/CMakeLists.txt index 000ba298c2d..ee287c65fe9 100644 --- a/source/blender/compositor/CMakeLists.txt +++ b/source/blender/compositor/CMakeLists.txt @@ -597,16 +597,16 @@ add_definitions(-DCL_USE_DEPRECATED_OPENCL_1_1_APIS) set(GENSRC_DIR ${CMAKE_CURRENT_BINARY_DIR}/operations) set(GENSRC ${GENSRC_DIR}/COM_SMAAAreaTexture.h) add_custom_command( - OUTPUT ${GENSRC} - COMMAND ${CMAKE_COMMAND} -E make_directory ${GENSRC_DIR} - COMMAND "$<TARGET_FILE:smaa_areatex>" ${GENSRC} - DEPENDS smaa_areatex + OUTPUT ${GENSRC} + COMMAND ${CMAKE_COMMAND} -E make_directory ${GENSRC_DIR} + COMMAND "$<TARGET_FILE:smaa_areatex>" ${GENSRC} + DEPENDS smaa_areatex ) add_custom_target(smaa_areatex_header - SOURCES ${GENSRC} + SOURCES ${GENSRC} ) list(APPEND SRC - ${GENSRC} + ${GENSRC} ) unset(GENSRC) unset(GENSRC_DIR) @@ -650,4 +650,3 @@ if(WITH_GTESTS) include(GTestTesting) blender_add_test_lib(bf_compositor_tests "${TEST_SRC}" "${INC};${TEST_INC}" "${INC_SYS}" "${LIB};${TEST_LIB}") endif() - diff --git a/source/blender/compositor/COM_defines.h b/source/blender/compositor/COM_defines.h index 900f29db44c..40a1e0da2a8 100644 --- a/source/blender/compositor/COM_defines.h +++ b/source/blender/compositor/COM_defines.h @@ -62,12 +62,18 @@ constexpr int COM_data_type_num_channels(const DataType datatype) } } +constexpr int COM_data_type_bytes_len(DataType data_type) +{ + return COM_data_type_num_channels(data_type) * sizeof(float); +} + constexpr int COM_DATA_TYPE_VALUE_CHANNELS = COM_data_type_num_channels(DataType::Value); constexpr int COM_DATA_TYPE_VECTOR_CHANNELS = COM_data_type_num_channels(DataType::Vector); constexpr int COM_DATA_TYPE_COLOR_CHANNELS = COM_data_type_num_channels(DataType::Color); constexpr float COM_COLOR_TRANSPARENT[4] = {0.0f, 0.0f, 0.0f, 0.0f}; constexpr float COM_VECTOR_ZERO[3] = {0.0f, 0.0f, 0.0f}; +constexpr float COM_COLOR_BLACK[4] = {0.0f, 0.0f, 0.0f, 1.0f}; constexpr float COM_VALUE_ZERO[1] = {0.0f}; constexpr float COM_VALUE_ONE[1] = {1.0f}; diff --git a/source/blender/compositor/intern/COM_BufferOperation.cc b/source/blender/compositor/intern/COM_BufferOperation.cc index 90c97f2a9c7..cafdff89c8e 100644 --- a/source/blender/compositor/intern/COM_BufferOperation.cc +++ b/source/blender/compositor/intern/COM_BufferOperation.cc @@ -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() @@ -40,20 +41,32 @@ const float *BufferOperation::get_constant_elem() return buffer_->getBuffer(); } +void BufferOperation::initExecution() +{ + if (buffer_->is_a_single_elem()) { + initMutex(); + } +} + void *BufferOperation::initializeTileData(rcti * /*rect*/) { if (buffer_->is_a_single_elem() == false) { return buffer_; } + lockMutex(); if (!inflated_buffer_) { inflated_buffer_ = buffer_->inflate(); } + unlockMutex(); return inflated_buffer_; } void BufferOperation::deinitExecution() { + if (buffer_->is_a_single_elem()) { + deinitMutex(); + } delete inflated_buffer_; } diff --git a/source/blender/compositor/intern/COM_BufferOperation.h b/source/blender/compositor/intern/COM_BufferOperation.h index 705264c37b7..b4cbc0a56b6 100644 --- a/source/blender/compositor/intern/COM_BufferOperation.h +++ b/source/blender/compositor/intern/COM_BufferOperation.h @@ -32,6 +32,7 @@ class BufferOperation : public ConstantOperation { const float *get_constant_elem() override; void *initializeTileData(rcti *rect) override; + void initExecution() 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_ConstantFolder.cc b/source/blender/compositor/intern/COM_ConstantFolder.cc index 5b48ff8fc08..445a9ce7433 100644 --- a/source/blender/compositor/intern/COM_ConstantFolder.cc +++ b/source/blender/compositor/intern/COM_ConstantFolder.cc @@ -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; } } diff --git a/source/blender/compositor/intern/COM_Debug.cc b/source/blender/compositor/intern/COM_Debug.cc index 5443974cbb0..a0333cf96cf 100644 --- a/source/blender/compositor/intern/COM_Debug.cc +++ b/source/blender/compositor/intern/COM_Debug.cc @@ -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, "}"); diff --git a/source/blender/compositor/intern/COM_MemoryBuffer.h b/source/blender/compositor/intern/COM_MemoryBuffer.h index ae12c444dc1..310e87b6a4b 100644 --- a/source/blender/compositor/intern/COM_MemoryBuffer.h +++ b/source/blender/compositor/intern/COM_MemoryBuffer.h @@ -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. */ diff --git a/source/blender/compositor/intern/COM_TiledExecutionModel.cc b/source/blender/compositor/intern/COM_TiledExecutionModel.cc index d025ce53330..a081b80349d 100644 --- a/source/blender/compositor/intern/COM_TiledExecutionModel.cc +++ b/source/blender/compositor/intern/COM_TiledExecutionModel.cc @@ -45,7 +45,7 @@ TiledExecutionModel::TiledExecutionModel(CompositorContext &context, group->determineResolution(resolution); if (border_.use_render_border) { - const rctf *render_border = border_.viewer_border; + const rctf *render_border = border_.render_border; group->setRenderBorder( render_border->xmin, render_border->xmax, render_border->ymin, render_border->ymax); } diff --git a/source/blender/compositor/nodes/COM_MaskNode.cc b/source/blender/compositor/nodes/COM_MaskNode.cc index ef171c01653..b5b23798160 100644 --- a/source/blender/compositor/nodes/COM_MaskNode.cc +++ b/source/blender/compositor/nodes/COM_MaskNode.cc @@ -41,7 +41,7 @@ void MaskNode::convertToOperations(NodeConverter &converter, NodeMask *data = (NodeMask *)editorNode->storage; Mask *mask = (Mask *)editorNode->id; - // always connect the output image + /* Always connect the output image. */ MaskOperation *operation = new MaskOperation(); if (editorNode->custom1 & CMP_NODEFLAG_MASK_FIXED) { diff --git a/source/blender/compositor/nodes/COM_MovieClipNode.cc b/source/blender/compositor/nodes/COM_MovieClipNode.cc index 50bd9b4d71b..b80071d27c7 100644 --- a/source/blender/compositor/nodes/COM_MovieClipNode.cc +++ b/source/blender/compositor/nodes/COM_MovieClipNode.cc @@ -62,7 +62,7 @@ void MovieClipNode::convertToOperations(NodeConverter &converter, } } - // always connect the output image + /* Always connect the output image. */ MovieClipOperation *operation = new MovieClipOperation(); operation->setMovieClip(movieClip); operation->setMovieClipUser(movieClipUser); diff --git a/source/blender/compositor/operations/COM_AlphaOverKeyOperation.cc b/source/blender/compositor/operations/COM_AlphaOverKeyOperation.cc index 0c656753a51..30e7fab4027 100644 --- a/source/blender/compositor/operations/COM_AlphaOverKeyOperation.cc +++ b/source/blender/compositor/operations/COM_AlphaOverKeyOperation.cc @@ -20,6 +20,11 @@ namespace blender::compositor { +AlphaOverKeyOperation::AlphaOverKeyOperation() +{ + this->flags.can_be_constant = true; +} + void AlphaOverKeyOperation::executePixelSampled(float output[4], float x, float y, @@ -50,4 +55,29 @@ void AlphaOverKeyOperation::executePixelSampled(float output[4], } } +void AlphaOverKeyOperation::update_memory_buffer_row(PixelCursor &p) +{ + for (; p.out < p.row_end; p.next()) { + const float *color1 = p.color1; + const float *over_color = p.color2; + const float value = *p.value; + + if (over_color[3] <= 0.0f) { + copy_v4_v4(p.out, color1); + } + else if (value == 1.0f && over_color[3] >= 1.0f) { + copy_v4_v4(p.out, over_color); + } + else { + const float premul = value * over_color[3]; + const float mul = 1.0f - premul; + + p.out[0] = (mul * color1[0]) + premul * over_color[0]; + p.out[1] = (mul * color1[1]) + premul * over_color[1]; + p.out[2] = (mul * color1[2]) + premul * over_color[2]; + p.out[3] = (mul * color1[3]) + value * over_color[3]; + } + } +} + } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_AlphaOverKeyOperation.h b/source/blender/compositor/operations/COM_AlphaOverKeyOperation.h index 83713d18971..960fbc98fe9 100644 --- a/source/blender/compositor/operations/COM_AlphaOverKeyOperation.h +++ b/source/blender/compositor/operations/COM_AlphaOverKeyOperation.h @@ -28,10 +28,14 @@ namespace blender::compositor { */ class AlphaOverKeyOperation : public MixBaseOperation { public: + AlphaOverKeyOperation(); + /** * The inner loop of this operation. */ void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + + void update_memory_buffer_row(PixelCursor &p) override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_AlphaOverMixedOperation.cc b/source/blender/compositor/operations/COM_AlphaOverMixedOperation.cc index c68c79d2263..0cc179ea209 100644 --- a/source/blender/compositor/operations/COM_AlphaOverMixedOperation.cc +++ b/source/blender/compositor/operations/COM_AlphaOverMixedOperation.cc @@ -23,6 +23,7 @@ namespace blender::compositor { AlphaOverMixedOperation::AlphaOverMixedOperation() { this->m_x = 0.0f; + this->flags.can_be_constant = true; } void AlphaOverMixedOperation::executePixelSampled(float output[4], @@ -56,4 +57,30 @@ void AlphaOverMixedOperation::executePixelSampled(float output[4], } } +void AlphaOverMixedOperation::update_memory_buffer_row(PixelCursor &p) +{ + for (; p.out < p.row_end; p.next()) { + const float *color1 = p.color1; + const float *over_color = p.color2; + const float value = *p.value; + + if (over_color[3] <= 0.0f) { + copy_v4_v4(p.out, color1); + } + else if (value == 1.0f && over_color[3] >= 1.0f) { + copy_v4_v4(p.out, over_color); + } + else { + const float addfac = 1.0f - this->m_x + over_color[3] * this->m_x; + const float premul = value * addfac; + const float mul = 1.0f - value * over_color[3]; + + p.out[0] = (mul * color1[0]) + premul * over_color[0]; + p.out[1] = (mul * color1[1]) + premul * over_color[1]; + p.out[2] = (mul * color1[2]) + premul * over_color[2]; + p.out[3] = (mul * color1[3]) + value * over_color[3]; + } + } +} + } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_AlphaOverMixedOperation.h b/source/blender/compositor/operations/COM_AlphaOverMixedOperation.h index e2b3af84162..2b88cd5f421 100644 --- a/source/blender/compositor/operations/COM_AlphaOverMixedOperation.h +++ b/source/blender/compositor/operations/COM_AlphaOverMixedOperation.h @@ -45,6 +45,8 @@ class AlphaOverMixedOperation : public MixBaseOperation { { this->m_x = x; } + + void update_memory_buffer_row(PixelCursor &p) override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_AlphaOverPremultiplyOperation.cc b/source/blender/compositor/operations/COM_AlphaOverPremultiplyOperation.cc index 3dd4607e273..a57e8c7f8a3 100644 --- a/source/blender/compositor/operations/COM_AlphaOverPremultiplyOperation.cc +++ b/source/blender/compositor/operations/COM_AlphaOverPremultiplyOperation.cc @@ -20,6 +20,11 @@ namespace blender::compositor { +AlphaOverPremultiplyOperation::AlphaOverPremultiplyOperation() +{ + this->flags.can_be_constant = true; +} + void AlphaOverPremultiplyOperation::executePixelSampled(float output[4], float x, float y, @@ -50,4 +55,28 @@ void AlphaOverPremultiplyOperation::executePixelSampled(float output[4], } } +void AlphaOverPremultiplyOperation::update_memory_buffer_row(PixelCursor &p) +{ + for (; p.out < p.row_end; p.next()) { + const float *color1 = p.color1; + const float *over_color = p.color2; + const float value = *p.value; + + if (over_color[3] <= 0.0f) { + copy_v4_v4(p.out, color1); + } + else if (value == 1.0f && over_color[3] >= 1.0f) { + copy_v4_v4(p.out, over_color); + } + else { + const float mul = 1.0f - value * over_color[3]; + + p.out[0] = (mul * color1[0]) + value * over_color[0]; + p.out[1] = (mul * color1[1]) + value * over_color[1]; + p.out[2] = (mul * color1[2]) + value * over_color[2]; + p.out[3] = (mul * color1[3]) + value * over_color[3]; + } + } +} + } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_AlphaOverPremultiplyOperation.h b/source/blender/compositor/operations/COM_AlphaOverPremultiplyOperation.h index f1d4b668fce..701bc07cc27 100644 --- a/source/blender/compositor/operations/COM_AlphaOverPremultiplyOperation.h +++ b/source/blender/compositor/operations/COM_AlphaOverPremultiplyOperation.h @@ -28,10 +28,14 @@ namespace blender::compositor { */ class AlphaOverPremultiplyOperation : public MixBaseOperation { public: + AlphaOverPremultiplyOperation(); + /** * The inner loop of this operation. */ void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + + void update_memory_buffer_row(PixelCursor &p) override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_BilateralBlurOperation.cc b/source/blender/compositor/operations/COM_BilateralBlurOperation.cc index 64448e2ae95..0c1bb688d4e 100644 --- a/source/blender/compositor/operations/COM_BilateralBlurOperation.cc +++ b/source/blender/compositor/operations/COM_BilateralBlurOperation.cc @@ -38,7 +38,6 @@ void BilateralBlurOperation::initExecution() { this->m_inputColorProgram = getInputSocketReader(0); this->m_inputDeterminatorProgram = getInputSocketReader(1); - this->m_space = this->m_data->sigma_space + this->m_data->iter; QualityStepHelper::initExecution(COM_QH_INCREASE); } @@ -115,4 +114,89 @@ bool BilateralBlurOperation::determineDependingAreaOfInterest(rcti *input, return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output); } +void BilateralBlurOperation::get_area_of_interest(const int UNUSED(input_idx), + const rcti &output_area, + rcti &r_input_area) +{ + const int add = ceil(this->m_space) + 1; + + r_input_area.xmax = output_area.xmax + (add); + r_input_area.xmin = output_area.xmin - (add); + r_input_area.ymax = output_area.ymax + (add); + r_input_area.ymin = output_area.ymin - (add); +} + +struct PixelCursor { + MemoryBuffer *input_determinator; + MemoryBuffer *input_color; + int step; + float sigma_color; + const float *determ_reference_color; + float temp_color[4]; + float *out; + int min_x, max_x; + int min_y, max_y; +}; + +static void blur_pixel(PixelCursor &p) +{ + float blur_divider = 0.0f; + zero_v4(p.out); + + /* TODO(sergey): This isn't really good bilateral filter, it should be + * using gaussian bell for weights. Also sigma_color doesn't seem to be + * used correct at all. + */ + for (int yi = p.min_y; yi < p.max_y; yi += p.step) { + for (int xi = p.min_x; xi < p.max_x; xi += p.step) { + p.input_determinator->read(p.temp_color, xi, yi); + /* Do not take the alpha channel into account. */ + const float delta_color = (fabsf(p.determ_reference_color[0] - p.temp_color[0]) + + fabsf(p.determ_reference_color[1] - p.temp_color[1]) + + fabsf(p.determ_reference_color[2] - p.temp_color[2])); + if (delta_color < p.sigma_color) { + /* Add this to the blur. */ + p.input_color->read(p.temp_color, xi, yi); + add_v4_v4(p.out, p.temp_color); + blur_divider += 1.0f; + } + } + } + + if (blur_divider > 0.0f) { + mul_v4_fl(p.out, 1.0f / blur_divider); + } + else { + copy_v4_v4(p.out, COM_COLOR_BLACK); + } +} + +void BilateralBlurOperation::update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) +{ + PixelCursor p = {}; + p.step = QualityStepHelper::getStep(); + p.sigma_color = this->m_data->sigma_color; + p.input_color = inputs[0]; + p.input_determinator = inputs[1]; + const float space = this->m_space; + for (int y = area.ymin; y < area.ymax; y++) { + p.out = output->get_elem(area.xmin, y); + /* This will be used as the reference color for the determinator. */ + p.determ_reference_color = p.input_determinator->get_elem(area.xmin, y); + p.min_y = floor(y - space); + p.max_y = ceil(y + space); + for (int x = area.xmin; x < area.xmax; x++) { + p.min_x = floor(x - space); + p.max_x = ceil(x + space); + + blur_pixel(p); + + p.determ_reference_color += p.input_determinator->elem_stride; + p.out += output->elem_stride; + } + } +} + } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_BilateralBlurOperation.h b/source/blender/compositor/operations/COM_BilateralBlurOperation.h index c56cef35050..517c5292827 100644 --- a/source/blender/compositor/operations/COM_BilateralBlurOperation.h +++ b/source/blender/compositor/operations/COM_BilateralBlurOperation.h @@ -18,12 +18,12 @@ #pragma once -#include "COM_NodeOperation.h" +#include "COM_MultiThreadedOperation.h" #include "COM_QualityStepHelper.h" namespace blender::compositor { -class BilateralBlurOperation : public NodeOperation, public QualityStepHelper { +class BilateralBlurOperation : public MultiThreadedOperation, public QualityStepHelper { private: SocketReader *m_inputColorProgram; SocketReader *m_inputDeterminatorProgram; @@ -55,7 +55,14 @@ class BilateralBlurOperation : public NodeOperation, public QualityStepHelper { void setData(NodeBilateralBlurData *data) { this->m_data = data; + this->m_space = data->sigma_space + data->iter; } + + void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area) override; + + void update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_BokehImageOperation.cc b/source/blender/compositor/operations/COM_BokehImageOperation.cc index 63f283b6acc..bd5b25b5af8 100644 --- a/source/blender/compositor/operations/COM_BokehImageOperation.cc +++ b/source/blender/compositor/operations/COM_BokehImageOperation.cc @@ -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) { diff --git a/source/blender/compositor/operations/COM_BokehImageOperation.h b/source/blender/compositor/operations/COM_BokehImageOperation.h index 2e0bc8a34dc..2527233fabd 100644 --- a/source/blender/compositor/operations/COM_BokehImageOperation.h +++ b/source/blender/compositor/operations/COM_BokehImageOperation.h @@ -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 diff --git a/source/blender/compositor/operations/COM_ChangeHSVOperation.cc b/source/blender/compositor/operations/COM_ChangeHSVOperation.cc index eee007ce9e6..1e3e7806968 100644 --- a/source/blender/compositor/operations/COM_ChangeHSVOperation.cc +++ b/source/blender/compositor/operations/COM_ChangeHSVOperation.cc @@ -28,6 +28,7 @@ ChangeHSVOperation::ChangeHSVOperation() this->addInputSocket(DataType::Value); this->addOutputSocket(DataType::Color); this->m_inputOperation = nullptr; + this->flags.can_be_constant = true; } void ChangeHSVOperation::initExecution() @@ -71,4 +72,26 @@ void ChangeHSVOperation::executePixelSampled(float output[4], output[3] = inputColor1[3]; } +void ChangeHSVOperation::update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) +{ + for (BuffersIterator<float> it = output->iterate_with(inputs, area); !it.is_end(); ++it) { + const float *color = it.in(0); + const float hue = *it.in(1); + it.out[0] = color[0] + (hue - 0.5f); + if (it.out[0] > 1.0f) { + it.out[0] -= 1.0f; + } + else if (it.out[0] < 0.0f) { + it.out[0] += 1.0f; + } + const float saturation = *it.in(2); + const float value = *it.in(3); + it.out[1] = color[1] * saturation; + it.out[2] = color[2] * value; + it.out[3] = color[3]; + } +} + } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_ChangeHSVOperation.h b/source/blender/compositor/operations/COM_ChangeHSVOperation.h index d38b4be3efe..e7bc3274f25 100644 --- a/source/blender/compositor/operations/COM_ChangeHSVOperation.h +++ b/source/blender/compositor/operations/COM_ChangeHSVOperation.h @@ -18,7 +18,7 @@ #pragma once -#include "COM_MixOperation.h" +#include "COM_MultiThreadedOperation.h" namespace blender::compositor { @@ -26,7 +26,7 @@ namespace blender::compositor { * this program converts an input color to an output value. * it assumes we are in sRGB color space. */ -class ChangeHSVOperation : public NodeOperation { +class ChangeHSVOperation : public MultiThreadedOperation { private: SocketReader *m_inputOperation; SocketReader *m_hueOperation; @@ -46,6 +46,10 @@ class ChangeHSVOperation : public NodeOperation { * The inner loop of this operation. */ 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 diff --git a/source/blender/compositor/operations/COM_ColorCurveOperation.cc b/source/blender/compositor/operations/COM_ColorCurveOperation.cc index cb0565a81a2..1b7ad0ea608 100644 --- a/source/blender/compositor/operations/COM_ColorCurveOperation.cc +++ b/source/blender/compositor/operations/COM_ColorCurveOperation.cc @@ -98,6 +98,36 @@ void ColorCurveOperation::deinitExecution() this->m_inputWhiteProgram = nullptr; } +void ColorCurveOperation::update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) +{ + CurveMapping *cumap = this->m_curveMapping; + float bwmul[3]; + for (BuffersIterator<float> it = output->iterate_with(inputs, area); !it.is_end(); ++it) { + /* Local versions of `cumap->black` and `cumap->white`. */ + const float *black = it.in(2); + const float *white = it.in(3); + /* Get a local `bwmul` value, it's not threadsafe using `cumap->bwmul` and others. */ + BKE_curvemapping_set_black_white_ex(black, white, bwmul); + + const float fac = *it.in(0); + const float *image = it.in(1); + if (fac >= 1.0f) { + BKE_curvemapping_evaluate_premulRGBF_ex(cumap, it.out, image, black, bwmul); + } + else if (fac <= 0.0f) { + copy_v3_v3(it.out, image); + } + else { + float col[4]; + BKE_curvemapping_evaluate_premulRGBF_ex(cumap, col, image, black, bwmul); + interp_v3_v3v3(it.out, image, col, fac); + } + it.out[3] = image[3]; + } +} + // Constant level curve mapping ConstantLevelColorCurveOperation::ConstantLevelColorCurveOperation() @@ -154,4 +184,27 @@ void ConstantLevelColorCurveOperation::deinitExecution() this->m_inputImageProgram = nullptr; } +void ConstantLevelColorCurveOperation::update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) +{ + CurveMapping *cumap = this->m_curveMapping; + for (BuffersIterator<float> it = output->iterate_with(inputs, area); !it.is_end(); ++it) { + const float fac = *it.in(0); + const float *image = it.in(1); + if (fac >= 1.0f) { + BKE_curvemapping_evaluate_premulRGBF(cumap, it.out, image); + } + else if (fac <= 0.0f) { + copy_v3_v3(it.out, image); + } + else { + float col[4]; + BKE_curvemapping_evaluate_premulRGBF(cumap, col, image); + interp_v3_v3v3(it.out, image, col, fac); + } + it.out[3] = image[3]; + } +} + } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_ColorCurveOperation.h b/source/blender/compositor/operations/COM_ColorCurveOperation.h index 6fc7759b8d2..d8271e56d1d 100644 --- a/source/blender/compositor/operations/COM_ColorCurveOperation.h +++ b/source/blender/compositor/operations/COM_ColorCurveOperation.h @@ -51,6 +51,10 @@ class ColorCurveOperation : public CurveBaseOperation { * Deinitialize the execution */ void deinitExecution() override; + + void update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) override; }; class ConstantLevelColorCurveOperation : public CurveBaseOperation { @@ -89,6 +93,10 @@ class ConstantLevelColorCurveOperation : public CurveBaseOperation { { copy_v3_v3(this->m_white, white); } + + void update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_CompositorOperation.cc b/source/blender/compositor/operations/COM_CompositorOperation.cc index 94d41b28f5d..8752d764107 100644 --- a/source/blender/compositor/operations/COM_CompositorOperation.cc +++ b/source/blender/compositor/operations/COM_CompositorOperation.cc @@ -220,6 +220,22 @@ void CompositorOperation::executeRegion(rcti *rect, unsigned int /*tileNumber*/) } } +void CompositorOperation::update_memory_buffer_partial(MemoryBuffer *UNUSED(output), + const rcti &area, + Span<MemoryBuffer *> inputs) +{ + if (!m_outputBuffer) { + return; + } + MemoryBuffer output_buf(m_outputBuffer, COM_DATA_TYPE_COLOR_CHANNELS, getWidth(), getHeight()); + output_buf.copy_from(inputs[0], area); + if (this->m_useAlphaInput) { + output_buf.copy_from(inputs[1], area, 0, COM_DATA_TYPE_VALUE_CHANNELS, 3); + } + MemoryBuffer depth_buf(m_depthBuffer, COM_DATA_TYPE_VALUE_CHANNELS, getWidth(), getHeight()); + depth_buf.copy_from(inputs[2], area); +} + void CompositorOperation::determineResolution(unsigned int resolution[2], unsigned int preferredResolution[2]) { diff --git a/source/blender/compositor/operations/COM_CompositorOperation.h b/source/blender/compositor/operations/COM_CompositorOperation.h index 65988c86cc5..66367ec8bae 100644 --- a/source/blender/compositor/operations/COM_CompositorOperation.h +++ b/source/blender/compositor/operations/COM_CompositorOperation.h @@ -20,7 +20,7 @@ #include "BLI_rect.h" #include "BLI_string.h" -#include "COM_NodeOperation.h" +#include "COM_MultiThreadedOperation.h" struct Scene; @@ -29,7 +29,7 @@ namespace blender::compositor { /** * \brief Compositor output operation */ -class CompositorOperation : public NodeOperation { +class CompositorOperation : public MultiThreadedOperation { private: const struct Scene *m_scene; /** @@ -125,6 +125,10 @@ class CompositorOperation : public NodeOperation { { this->m_active = active; } + + void update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_ConstantOperation.cc b/source/blender/compositor/operations/COM_ConstantOperation.cc index f905edbde76..33d51cca432 100644 --- a/source/blender/compositor/operations/COM_ConstantOperation.cc +++ b/source/blender/compositor/operations/COM_ConstantOperation.cc @@ -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 diff --git a/source/blender/compositor/operations/COM_ConstantOperation.h b/source/blender/compositor/operations/COM_ConstantOperation.h index 2709efeebd8..31b8d30254b 100644 --- a/source/blender/compositor/operations/COM_ConstantOperation.h +++ b/source/blender/compositor/operations/COM_ConstantOperation.h @@ -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 diff --git a/source/blender/compositor/operations/COM_CryptomatteOperation.cc b/source/blender/compositor/operations/COM_CryptomatteOperation.cc index 52ae1d6d5b5..1a86fadad76 100644 --- a/source/blender/compositor/operations/COM_CryptomatteOperation.cc +++ b/source/blender/compositor/operations/COM_CryptomatteOperation.cc @@ -57,8 +57,8 @@ void CryptomatteOperation::executePixel(float output[4], int x, int y, void *dat ::memcpy(&m3hash, &input[0], sizeof(uint32_t)); /* Since the red channel is likely to be out of display range, * setting green and blue gives more meaningful images. */ - output[1] = ((float)((m3hash << 8)) / (float)UINT32_MAX); - output[2] = ((float)((m3hash << 16)) / (float)UINT32_MAX); + output[1] = ((float)(m3hash << 8) / (float)UINT32_MAX); + output[2] = ((float)(m3hash << 16) / (float)UINT32_MAX); } for (float hash : m_objectIndex) { if (input[0] == hash) { diff --git a/source/blender/compositor/operations/COM_CurveBaseOperation.cc b/source/blender/compositor/operations/COM_CurveBaseOperation.cc index 8f655964570..3c4b27aa4cf 100644 --- a/source/blender/compositor/operations/COM_CurveBaseOperation.cc +++ b/source/blender/compositor/operations/COM_CurveBaseOperation.cc @@ -25,6 +25,7 @@ namespace blender::compositor { CurveBaseOperation::CurveBaseOperation() { this->m_curveMapping = nullptr; + this->flags.can_be_constant = true; } CurveBaseOperation::~CurveBaseOperation() diff --git a/source/blender/compositor/operations/COM_CurveBaseOperation.h b/source/blender/compositor/operations/COM_CurveBaseOperation.h index fff0f3168ba..da665e7ea60 100644 --- a/source/blender/compositor/operations/COM_CurveBaseOperation.h +++ b/source/blender/compositor/operations/COM_CurveBaseOperation.h @@ -18,12 +18,12 @@ #pragma once -#include "COM_NodeOperation.h" +#include "COM_MultiThreadedOperation.h" #include "DNA_color_types.h" namespace blender::compositor { -class CurveBaseOperation : public NodeOperation { +class CurveBaseOperation : public MultiThreadedOperation { protected: /** * Cached reference to the inputProgram diff --git a/source/blender/compositor/operations/COM_DilateErodeOperation.cc b/source/blender/compositor/operations/COM_DilateErodeOperation.cc index 2454f507664..a27148f967d 100644 --- a/source/blender/compositor/operations/COM_DilateErodeOperation.cc +++ b/source/blender/compositor/operations/COM_DilateErodeOperation.cc @@ -24,7 +24,7 @@ namespace blender::compositor { -// DilateErode Distance Threshold +/* DilateErode Distance Threshold */ DilateErodeThresholdOperation::DilateErodeThresholdOperation() { this->addInputSocket(DataType::Value); @@ -258,7 +258,7 @@ void DilateDistanceOperation::executeOpenCL(OpenCLDevice *device, device->COM_clEnqueueRange(dilateKernel, outputMemoryBuffer, 7, this); } -// Erode Distance +/* Erode Distance */ ErodeDistanceOperation::ErodeDistanceOperation() : DilateDistanceOperation() { /* pass */ @@ -318,7 +318,7 @@ void ErodeDistanceOperation::executeOpenCL(OpenCLDevice *device, device->COM_clEnqueueRange(erodeKernel, outputMemoryBuffer, 7, this); } -// Dilate step +/* Dilate step */ DilateStepOperation::DilateStepOperation() { this->addInputSocket(DataType::Value); @@ -331,7 +331,7 @@ void DilateStepOperation::initExecution() this->m_inputProgram = this->getInputSocketReader(0); } -// small helper to pass data from initializeTileData to executePixel +/* Small helper to pass data from initializeTileData to executePixel. */ struct tile_info { rcti rect; int width; @@ -370,21 +370,21 @@ void *DilateStepOperation::initializeTileData(rcti *rect) int bwidth = rect->xmax - rect->xmin; int bheight = rect->ymax - rect->ymin; - // NOTE: Cache buffer has original tilesize width, but new height. - // We have to calculate the additional rows in the first pass, - // to have valid data available for the second pass. + /* NOTE: Cache buffer has original tile-size width, but new height. + * We have to calculate the additional rows in the first pass, + * to have valid data available for the second pass. */ tile_info *result = create_cache(rect->xmin, rect->xmax, ymin, ymax); float *rectf = result->buffer; - // temp holds maxima for every step in the algorithm, buf holds a - // single row or column of input values, padded with FLT_MAX's to - // simplify the logic. + /* temp holds maxima for every step in the algorithm, buf holds a + * single row or column of input values, padded with FLT_MAX's to + * simplify the logic. */ float *temp = (float *)MEM_mallocN(sizeof(float) * (2 * window - 1), "dilate erode temp"); float *buf = (float *)MEM_mallocN(sizeof(float) * (MAX2(bwidth, bheight) + 5 * half_window), "dilate erode buf"); - // The following is based on the van Herk/Gil-Werman algorithm for morphology operations. - // first pass, horizontal dilate/erode + /* The following is based on the van Herk/Gil-Werman algorithm for morphology operations. + * first pass, horizontal dilate/erode. */ for (y = ymin; y < ymax; y++) { for (x = 0; x < bwidth + 5 * half_window; x++) { buf[x] = -FLT_MAX; @@ -409,7 +409,7 @@ void *DilateStepOperation::initializeTileData(rcti *rect) } } - // second pass, vertical dilate/erode + /* Second pass, vertical dilate/erode. */ for (x = 0; x < bwidth; x++) { for (y = 0; y < bheight + 5 * half_window; y++) { buf[y] = -FLT_MAX; @@ -475,7 +475,7 @@ bool DilateStepOperation::determineDependingAreaOfInterest(rcti *input, return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output); } -// Erode step +/* Erode step */ ErodeStepOperation::ErodeStepOperation() : DilateStepOperation() { /* pass */ @@ -500,21 +500,21 @@ void *ErodeStepOperation::initializeTileData(rcti *rect) int bwidth = rect->xmax - rect->xmin; int bheight = rect->ymax - rect->ymin; - // NOTE: Cache buffer has original tilesize width, but new height. - // We have to calculate the additional rows in the first pass, - // to have valid data available for the second pass. + /* NOTE: Cache buffer has original tile-size width, but new height. + * We have to calculate the additional rows in the first pass, + * to have valid data available for the second pass. */ tile_info *result = create_cache(rect->xmin, rect->xmax, ymin, ymax); float *rectf = result->buffer; - // temp holds maxima for every step in the algorithm, buf holds a - // single row or column of input values, padded with FLT_MAX's to - // simplify the logic. + /* temp holds maxima for every step in the algorithm, buf holds a + * single row or column of input values, padded with FLT_MAX's to + * simplify the logic. */ float *temp = (float *)MEM_mallocN(sizeof(float) * (2 * window - 1), "dilate erode temp"); float *buf = (float *)MEM_mallocN(sizeof(float) * (MAX2(bwidth, bheight) + 5 * half_window), "dilate erode buf"); - // The following is based on the van Herk/Gil-Werman algorithm for morphology operations. - // first pass, horizontal dilate/erode + /* The following is based on the van Herk/Gil-Werman algorithm for morphology operations. + * first pass, horizontal dilate/erode */ for (y = ymin; y < ymax; y++) { for (x = 0; x < bwidth + 5 * half_window; x++) { buf[x] = FLT_MAX; @@ -539,7 +539,7 @@ void *ErodeStepOperation::initializeTileData(rcti *rect) } } - // second pass, vertical dilate/erode + /* Second pass, vertical dilate/erode. */ for (x = 0; x < bwidth; x++) { for (y = 0; y < bheight + 5 * half_window; y++) { buf[y] = FLT_MAX; diff --git a/source/blender/compositor/operations/COM_GlareBaseOperation.h b/source/blender/compositor/operations/COM_GlareBaseOperation.h index 50db4e02940..6dac6f5ecc7 100644 --- a/source/blender/compositor/operations/COM_GlareBaseOperation.h +++ b/source/blender/compositor/operations/COM_GlareBaseOperation.h @@ -23,8 +23,8 @@ namespace blender::compositor { -/* utility functions used by glare, tonemap and lens distortion */ -/* soms macros for color handling */ +/* Utility functions used by glare, tone-map and lens distortion. */ +/* Some macros for color handling. */ typedef float fRGB[4]; /* TODO: replace with BLI_math_vector. */ diff --git a/source/blender/compositor/operations/COM_HueSaturationValueCorrectOperation.cc b/source/blender/compositor/operations/COM_HueSaturationValueCorrectOperation.cc index e341a88ff71..5ae868c5964 100644 --- a/source/blender/compositor/operations/COM_HueSaturationValueCorrectOperation.cc +++ b/source/blender/compositor/operations/COM_HueSaturationValueCorrectOperation.cc @@ -73,4 +73,31 @@ void HueSaturationValueCorrectOperation::deinitExecution() this->m_inputProgram = nullptr; } +void HueSaturationValueCorrectOperation::update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) +{ + float hsv[4]; + for (BuffersIterator<float> it = output->iterate_with(inputs, area); !it.is_end(); ++it) { + copy_v4_v4(hsv, it.in(0)); + + /* Adjust hue, scaling returned default 0.5 up to 1. */ + float f = BKE_curvemapping_evaluateF(this->m_curveMapping, 0, hsv[0]); + hsv[0] += f - 0.5f; + + /* Adjust saturation, scaling returned default 0.5 up to 1. */ + f = BKE_curvemapping_evaluateF(this->m_curveMapping, 1, hsv[0]); + hsv[1] *= (f * 2.0f); + + /* Adjust value, scaling returned default 0.5 up to 1. */ + f = BKE_curvemapping_evaluateF(this->m_curveMapping, 2, hsv[0]); + hsv[2] *= (f * 2.0f); + + hsv[0] = hsv[0] - floorf(hsv[0]); /* Mod 1.0. */ + CLAMP(hsv[1], 0.0f, 1.0f); + + copy_v4_v4(it.out, hsv); + } +} + } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_HueSaturationValueCorrectOperation.h b/source/blender/compositor/operations/COM_HueSaturationValueCorrectOperation.h index 703b2894bdb..6c1b66aba1f 100644 --- a/source/blender/compositor/operations/COM_HueSaturationValueCorrectOperation.h +++ b/source/blender/compositor/operations/COM_HueSaturationValueCorrectOperation.h @@ -47,6 +47,10 @@ class HueSaturationValueCorrectOperation : public CurveBaseOperation { * Deinitialize the execution */ void deinitExecution() override; + + void update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_InvertOperation.cc b/source/blender/compositor/operations/COM_InvertOperation.cc index 339e40a5d1f..4f71a1d0d1d 100644 --- a/source/blender/compositor/operations/COM_InvertOperation.cc +++ b/source/blender/compositor/operations/COM_InvertOperation.cc @@ -30,6 +30,7 @@ InvertOperation::InvertOperation() this->m_color = true; this->m_alpha = false; setResolutionInputSocketIndex(1); + this->flags.can_be_constant = true; } void InvertOperation::initExecution() { @@ -70,4 +71,31 @@ void InvertOperation::deinitExecution() this->m_inputColorProgram = nullptr; } +void InvertOperation::update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) +{ + for (BuffersIterator<float> it = output->iterate_with(inputs, area); !it.is_end(); ++it) { + const float value = *it.in(0); + const float inverted_value = 1.0f - value; + const float *color = it.in(1); + + if (this->m_color) { + it.out[0] = (1.0f - color[0]) * value + color[0] * inverted_value; + it.out[1] = (1.0f - color[1]) * value + color[1] * inverted_value; + it.out[2] = (1.0f - color[2]) * value + color[2] * inverted_value; + } + else { + copy_v3_v3(it.out, color); + } + + if (this->m_alpha) { + it.out[3] = (1.0f - color[3]) * value + color[3] * inverted_value; + } + else { + it.out[3] = color[3]; + } + } +} + } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_InvertOperation.h b/source/blender/compositor/operations/COM_InvertOperation.h index 17e5eb95f3e..a084bf5d559 100644 --- a/source/blender/compositor/operations/COM_InvertOperation.h +++ b/source/blender/compositor/operations/COM_InvertOperation.h @@ -18,11 +18,11 @@ #pragma once -#include "COM_NodeOperation.h" +#include "COM_MultiThreadedOperation.h" namespace blender::compositor { -class InvertOperation : public NodeOperation { +class InvertOperation : public MultiThreadedOperation { private: /** * Cached reference to the inputProgram @@ -59,6 +59,10 @@ class InvertOperation : public NodeOperation { { this->m_alpha = alpha; } + + void update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_MaskOperation.cc b/source/blender/compositor/operations/COM_MaskOperation.cc index c7763f08e71..84992f23924 100644 --- a/source/blender/compositor/operations/COM_MaskOperation.cc +++ b/source/blender/compositor/operations/COM_MaskOperation.cc @@ -161,4 +161,41 @@ void MaskOperation::executePixelSampled(float output[4], } } +void MaskOperation::update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> UNUSED(inputs)) +{ + Vector<MaskRasterHandle *> handles = get_non_null_handles(); + if (handles.size() == 0) { + output->fill(area, COM_VALUE_ZERO); + return; + } + + float xy[2]; + for (BuffersIterator<float> it = output->iterate_with({}, area); !it.is_end(); ++it) { + xy[0] = it.x * m_maskWidthInv + m_mask_px_ofs[0]; + xy[1] = it.y * m_maskHeightInv + m_mask_px_ofs[1]; + *it.out = 0.0f; + for (MaskRasterHandle *handle : handles) { + *it.out += BKE_maskrasterize_handle_sample(handle, xy); + } + + /* Until we get better falloff. */ + *it.out /= m_rasterMaskHandleTot; + } +} + +Vector<MaskRasterHandle *> MaskOperation::get_non_null_handles() const +{ + Vector<MaskRasterHandle *> handles; + for (int i = 0; i < m_rasterMaskHandleTot; i++) { + MaskRasterHandle *handle = m_rasterMaskHandles[i]; + if (handle == nullptr) { + continue; + } + handles.append(handle); + } + return handles; +} + } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_MaskOperation.h b/source/blender/compositor/operations/COM_MaskOperation.h index e8cd9c722df..81e344c0451 100644 --- a/source/blender/compositor/operations/COM_MaskOperation.h +++ b/source/blender/compositor/operations/COM_MaskOperation.h @@ -19,7 +19,7 @@ #pragma once #include "BLI_listbase.h" -#include "COM_NodeOperation.h" +#include "COM_MultiThreadedOperation.h" #include "DNA_mask_types.h" #include "IMB_imbuf_types.h" @@ -31,7 +31,7 @@ namespace blender::compositor { /** * Class with implementation of mask rasterization */ -class MaskOperation : public NodeOperation { +class MaskOperation : public MultiThreadedOperation { protected: Mask *m_mask; @@ -98,6 +98,13 @@ class MaskOperation : public NodeOperation { } 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; + + private: + Vector<MaskRasterHandle *> get_non_null_handles() const; }; } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_MovieClipAttributeOperation.cc b/source/blender/compositor/operations/COM_MovieClipAttributeOperation.cc index a9f187258b2..e36e93984fb 100644 --- a/source/blender/compositor/operations/COM_MovieClipAttributeOperation.cc +++ b/source/blender/compositor/operations/COM_MovieClipAttributeOperation.cc @@ -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 diff --git a/source/blender/compositor/operations/COM_MovieClipAttributeOperation.h b/source/blender/compositor/operations/COM_MovieClipAttributeOperation.h index 8507e98d08f..28c39d4dad3 100644 --- a/source/blender/compositor/operations/COM_MovieClipAttributeOperation.h +++ b/source/blender/compositor/operations/COM_MovieClipAttributeOperation.h @@ -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 diff --git a/source/blender/compositor/operations/COM_OutputFileOperation.cc b/source/blender/compositor/operations/COM_OutputFileOperation.cc index 7e896046f01..402d29893a4 100644 --- a/source/blender/compositor/operations/COM_OutputFileOperation.cc +++ b/source/blender/compositor/operations/COM_OutputFileOperation.cc @@ -160,7 +160,7 @@ int get_datatype_size(DataType datatype) static float *init_buffer(unsigned int width, unsigned int height, DataType datatype) { - // When initializing the tree during initial load the width and height can be zero. + /* When initializing the tree during initial load the width and height can be zero. */ if (width != 0 && height != 0) { int size = get_datatype_size(datatype); return (float *)MEM_callocN(width * height * size * sizeof(float), "OutputFile buffer"); @@ -294,6 +294,22 @@ void OutputSingleLayerOperation::deinitExecution() this->m_imageInput = nullptr; } +void OutputSingleLayerOperation::update_memory_buffer_partial(MemoryBuffer *UNUSED(output), + const rcti &area, + Span<MemoryBuffer *> inputs) +{ + if (!m_outputBuffer) { + return; + } + + MemoryBuffer output_buf(m_outputBuffer, + COM_data_type_num_channels(this->m_datatype), + this->getWidth(), + this->getHeight()); + const MemoryBuffer *input_image = inputs[0]; + output_buf.copy_from(input_image, area); +} + /******************************* MultiLayer *******************************/ OutputOpenExrLayer::OutputOpenExrLayer(const char *name_, DataType datatype_, bool use_layer_) @@ -444,4 +460,21 @@ void OutputOpenExrMultiLayerOperation::deinitExecution() } } +void OutputOpenExrMultiLayerOperation::update_memory_buffer_partial(MemoryBuffer *UNUSED(output), + const rcti &area, + Span<MemoryBuffer *> inputs) +{ + const MemoryBuffer *input_image = inputs[0]; + for (int i = 0; i < this->m_layers.size(); i++) { + OutputOpenExrLayer &layer = this->m_layers[i]; + if (layer.outputBuffer) { + MemoryBuffer output_buf(layer.outputBuffer, + COM_data_type_num_channels(layer.datatype), + this->getWidth(), + this->getHeight()); + output_buf.copy_from(input_image, area); + } + } +} + } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_OutputFileOperation.h b/source/blender/compositor/operations/COM_OutputFileOperation.h index 64ab4c06e7c..057cee0c43e 100644 --- a/source/blender/compositor/operations/COM_OutputFileOperation.h +++ b/source/blender/compositor/operations/COM_OutputFileOperation.h @@ -18,7 +18,7 @@ #pragma once -#include "COM_NodeOperation.h" +#include "COM_MultiThreadedOperation.h" #include "BLI_path_util.h" #include "BLI_rect.h" @@ -30,7 +30,7 @@ namespace blender::compositor { /* Writes the image to a single-layer file. */ -class OutputSingleLayerOperation : public NodeOperation { +class OutputSingleLayerOperation : public MultiThreadedOperation { protected: const RenderData *m_rd; const bNodeTree *m_tree; @@ -70,6 +70,10 @@ class OutputSingleLayerOperation : public NodeOperation { { return eCompositorPriority::Low; } + + void update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) override; }; /* extra info for OpenEXR layers */ @@ -86,7 +90,7 @@ struct OutputOpenExrLayer { }; /* Writes inputs into OpenEXR multilayer channels. */ -class OutputOpenExrMultiLayerOperation : public NodeOperation { +class OutputOpenExrMultiLayerOperation : public MultiThreadedOperation { protected: const Scene *m_scene; const RenderData *m_rd; @@ -122,6 +126,10 @@ class OutputOpenExrMultiLayerOperation : public NodeOperation { { return eCompositorPriority::Low; } + + void update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) override; }; void add_exr_channels(void *exrhandle, diff --git a/source/blender/compositor/operations/COM_SetColorOperation.cc b/source/blender/compositor/operations/COM_SetColorOperation.cc index bfe735aab15..dbe45fa60db 100644 --- a/source/blender/compositor/operations/COM_SetColorOperation.cc +++ b/source/blender/compositor/operations/COM_SetColorOperation.cc @@ -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 diff --git a/source/blender/compositor/operations/COM_SetColorOperation.h b/source/blender/compositor/operations/COM_SetColorOperation.h index f4c0948ee1b..f546d5e7668 100644 --- a/source/blender/compositor/operations/COM_SetColorOperation.h +++ b/source/blender/compositor/operations/COM_SetColorOperation.h @@ -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 diff --git a/source/blender/compositor/operations/COM_SetValueOperation.cc b/source/blender/compositor/operations/COM_SetValueOperation.cc index c12fb106afd..ef43cf64653 100644 --- a/source/blender/compositor/operations/COM_SetValueOperation.cc +++ b/source/blender/compositor/operations/COM_SetValueOperation.cc @@ -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 diff --git a/source/blender/compositor/operations/COM_SetValueOperation.h b/source/blender/compositor/operations/COM_SetValueOperation.h index f18d44d9554..726624c1c6a 100644 --- a/source/blender/compositor/operations/COM_SetValueOperation.h +++ b/source/blender/compositor/operations/COM_SetValueOperation.h @@ -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 diff --git a/source/blender/compositor/operations/COM_SplitOperation.cc b/source/blender/compositor/operations/COM_SplitOperation.cc index a4754de370d..d18ed3b8e14 100644 --- a/source/blender/compositor/operations/COM_SplitOperation.cc +++ b/source/blender/compositor/operations/COM_SplitOperation.cc @@ -79,4 +79,17 @@ void SplitOperation::determineResolution(unsigned int resolution[2], NodeOperation::determineResolution(resolution, preferredResolution); } +void SplitOperation::update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) +{ + const int percent = this->m_xSplit ? this->m_splitPercentage * this->getWidth() / 100.0f : + this->m_splitPercentage * this->getHeight() / 100.0f; + const size_t elem_bytes = COM_data_type_bytes_len(getOutputSocket()->getDataType()); + for (BuffersIterator<float> it = output->iterate_with(inputs, area); !it.is_end(); ++it) { + const bool is_image1 = this->m_xSplit ? it.x > percent : it.y > percent; + memcpy(it.out, it.in(is_image1 ? 0 : 1), elem_bytes); + } +} + } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_SplitOperation.h b/source/blender/compositor/operations/COM_SplitOperation.h index 09e48821dd0..2d09d2a07dc 100644 --- a/source/blender/compositor/operations/COM_SplitOperation.h +++ b/source/blender/compositor/operations/COM_SplitOperation.h @@ -18,11 +18,11 @@ #pragma once -#include "COM_NodeOperation.h" +#include "COM_MultiThreadedOperation.h" namespace blender::compositor { -class SplitOperation : public NodeOperation { +class SplitOperation : public MultiThreadedOperation { private: SocketReader *m_image1Input; SocketReader *m_image2Input; @@ -45,6 +45,10 @@ class SplitOperation : public NodeOperation { { this->m_xSplit = xsplit; } + + void update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_SunBeamsOperation.cc b/source/blender/compositor/operations/COM_SunBeamsOperation.cc index bd82b6397ad..ad96e3a02ba 100644 --- a/source/blender/compositor/operations/COM_SunBeamsOperation.cc +++ b/source/blender/compositor/operations/COM_SunBeamsOperation.cc @@ -30,7 +30,7 @@ SunBeamsOperation::SunBeamsOperation() this->flags.complex = true; } -void SunBeamsOperation::initExecution() +void SunBeamsOperation::calc_rays_common_data() { /* convert to pixels */ this->m_source_px[0] = this->m_data.source[0] * this->getWidth(); @@ -38,6 +38,11 @@ void SunBeamsOperation::initExecution() this->m_ray_length_px = this->m_data.ray_length * MAX2(this->getWidth(), this->getHeight()); } +void SunBeamsOperation::initExecution() +{ + calc_rays_common_data(); +} + /** * Defines a line accumulator for a specific sector, * given by the four matrix entries that rotate from buffer space into the sector @@ -140,7 +145,7 @@ template<int fxu, int fxv, int fyu, int fyv> struct BufferLineAccumulator { falloff_factor = dist_max > dist_min ? dr / (float)(dist_max - dist_min) : 0.0f; - float *iter = input->getBuffer() + COM_DATA_TYPE_COLOR_CHANNELS * (x + input->getWidth() * y); + float *iter = input->getBuffer() + input->get_coords_offset(x, y); return iter; } @@ -159,7 +164,6 @@ template<int fxu, int fxv, int fyu, int fyv> struct BufferLineAccumulator { float dist_max) { const rcti &rect = input->get_rect(); - int buffer_width = input->getWidth(); int x, y, num; float v, dv; float falloff_factor; @@ -168,9 +172,7 @@ template<int fxu, int fxv, int fyu, int fyv> struct BufferLineAccumulator { zero_v4(output); if ((int)(co[0] - source[0]) == 0 && (int)(co[1] - source[1]) == 0) { - copy_v4_v4(output, - input->getBuffer() + COM_DATA_TYPE_COLOR_CHANNELS * - ((int)source[0] + input->getWidth() * (int)source[1])); + copy_v4_v4(output, input->get_elem(source[0], source[1])); return; } @@ -210,7 +212,7 @@ template<int fxu, int fxv, int fyu, int fyv> struct BufferLineAccumulator { /* decrement u */ x -= fxu; y -= fyu; - buffer -= (fxu + fyu * buffer_width) * COM_DATA_TYPE_COLOR_CHANNELS; + buffer -= fxu * input->elem_stride + fyu * input->row_stride; /* decrement v (in steps of dv < 1) */ v_local -= dv; @@ -219,7 +221,7 @@ template<int fxu, int fxv, int fyu, int fyv> struct BufferLineAccumulator { x -= fxv; y -= fyv; - buffer -= (fxv + fyv * buffer_width) * COM_DATA_TYPE_COLOR_CHANNELS; + buffer -= fxv * input->elem_stride + fyv * input->row_stride; } } @@ -356,4 +358,39 @@ bool SunBeamsOperation::determineDependingAreaOfInterest(rcti *input, return NodeOperation::determineDependingAreaOfInterest(&rect, readOperation, output); } +void SunBeamsOperation::get_area_of_interest(const int input_idx, + const rcti &output_area, + rcti &r_input_area) +{ + BLI_assert(input_idx == 0); + UNUSED_VARS(input_idx); + calc_rays_common_data(); + + r_input_area = output_area; + /* Enlarges the rect by moving each corner toward the source. + * This is the maximum distance that pixels can influence each other + * and gives a rect that contains all possible accumulated pixels. */ + calc_ray_shift(&r_input_area, output_area.xmin, output_area.ymin, m_source_px, m_ray_length_px); + calc_ray_shift(&r_input_area, output_area.xmin, output_area.ymax, m_source_px, m_ray_length_px); + calc_ray_shift(&r_input_area, output_area.xmax, output_area.ymin, m_source_px, m_ray_length_px); + calc_ray_shift(&r_input_area, output_area.xmax, output_area.ymax, m_source_px, m_ray_length_px); +} + +void SunBeamsOperation::update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) +{ + MemoryBuffer *input = inputs[0]; + float coords[2]; + for (int y = area.ymin; y < area.ymax; y++) { + coords[1] = y; + float *out_elem = output->get_elem(area.xmin, y); + for (int x = area.xmin; x < area.xmax; x++) { + coords[0] = x; + accumulate_line(input, out_elem, coords, m_source_px, 0.0f, m_ray_length_px); + out_elem += output->elem_stride; + } + } +} + } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_SunBeamsOperation.h b/source/blender/compositor/operations/COM_SunBeamsOperation.h index d3725021cde..71fc04453fe 100644 --- a/source/blender/compositor/operations/COM_SunBeamsOperation.h +++ b/source/blender/compositor/operations/COM_SunBeamsOperation.h @@ -17,11 +17,11 @@ #pragma once -#include "COM_NodeOperation.h" +#include "COM_MultiThreadedOperation.h" namespace blender::compositor { -class SunBeamsOperation : public NodeOperation { +class SunBeamsOperation : public MultiThreadedOperation { public: SunBeamsOperation(); @@ -40,6 +40,14 @@ class SunBeamsOperation : public NodeOperation { m_data = data; } + void update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) override; + void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area) override; + + private: + void calc_rays_common_data(); + private: NodeSunBeams m_data; diff --git a/source/blender/compositor/operations/COM_TonemapOperation.cc b/source/blender/compositor/operations/COM_TonemapOperation.cc index 6bfacb0c75d..20da468eeb1 100644 --- a/source/blender/compositor/operations/COM_TonemapOperation.cc +++ b/source/blender/compositor/operations/COM_TonemapOperation.cc @@ -17,6 +17,8 @@ */ #include "COM_TonemapOperation.h" +#include "COM_ExecutionSystem.h" + #include "BLI_math.h" #include "BLI_utildefines.h" @@ -153,4 +155,126 @@ void TonemapOperation::deinitializeTileData(rcti * /*rect*/, void * /*data*/) /* pass */ } +void TonemapOperation::get_area_of_interest(const int input_idx, + const rcti &UNUSED(output_area), + rcti &r_input_area) +{ + BLI_assert(input_idx == 0); + NodeOperation *operation = getInputOperation(input_idx); + r_input_area.xmin = 0; + r_input_area.ymin = 0; + r_input_area.xmax = operation->getWidth(); + r_input_area.ymax = operation->getHeight(); +} + +struct Luminance { + float sum; + float color_sum[3]; + float log_sum; + float min; + float max; + int num_pixels; +}; + +static Luminance calc_area_luminance(const MemoryBuffer *input, const rcti &area) +{ + Luminance lum = {0}; + for (const float *elem : input->get_buffer_area(area)) { + const float lu = IMB_colormanagement_get_luminance(elem); + lum.sum += lu; + add_v3_v3(lum.color_sum, elem); + lum.log_sum += logf(MAX2(lu, 0.0f) + 1e-5f); + lum.max = MAX2(lu, lum.max); + lum.min = MIN2(lu, lum.min); + lum.num_pixels++; + } + return lum; +} + +void TonemapOperation::update_memory_buffer_started(MemoryBuffer *UNUSED(output), + const rcti &UNUSED(area), + Span<MemoryBuffer *> inputs) +{ + if (this->m_cachedInstance == nullptr) { + Luminance lum = {0}; + const MemoryBuffer *input = inputs[0]; + exec_system_->execute_work<Luminance>( + input->get_rect(), + [=](const rcti &split) { return calc_area_luminance(input, split); }, + lum, + [](Luminance &join, const Luminance &chunk) { + join.sum += chunk.sum; + add_v3_v3(join.color_sum, chunk.color_sum); + join.log_sum += chunk.log_sum; + join.max = MAX2(join.max, chunk.max); + join.min = MIN2(join.min, chunk.min); + join.num_pixels += chunk.num_pixels; + }); + + AvgLogLum *avg = new AvgLogLum(); + avg->lav = lum.sum / lum.num_pixels; + mul_v3_v3fl(avg->cav, lum.color_sum, 1.0f / lum.num_pixels); + const float max_log = log((double)lum.max + 1e-5); + const float min_log = log((double)lum.min + 1e-5); + const float avg_log = lum.log_sum / lum.num_pixels; + avg->auto_key = (max_log > min_log) ? ((max_log - avg_log) / (max_log - min_log)) : 1.0f; + const float al = exp((double)avg_log); + avg->al = (al == 0.0f) ? 0.0f : (this->m_data->key / al); + avg->igm = (this->m_data->gamma == 0.0f) ? 1 : (1.0f / this->m_data->gamma); + this->m_cachedInstance = avg; + } +} + +void TonemapOperation::update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) +{ + AvgLogLum *avg = m_cachedInstance; + const float igm = avg->igm; + const float offset = this->m_data->offset; + for (BuffersIterator<float> it = output->iterate_with(inputs, area); !it.is_end(); ++it) { + copy_v4_v4(it.out, it.in(0)); + mul_v3_fl(it.out, avg->al); + float dr = it.out[0] + offset; + float dg = it.out[1] + offset; + float db = it.out[2] + offset; + it.out[0] /= ((dr == 0.0f) ? 1.0f : dr); + it.out[1] /= ((dg == 0.0f) ? 1.0f : dg); + it.out[2] /= ((db == 0.0f) ? 1.0f : db); + if (igm != 0.0f) { + it.out[0] = powf(MAX2(it.out[0], 0.0f), igm); + it.out[1] = powf(MAX2(it.out[1], 0.0f), igm); + it.out[2] = powf(MAX2(it.out[2], 0.0f), igm); + } + } +} + +void PhotoreceptorTonemapOperation::update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) +{ + AvgLogLum *avg = m_cachedInstance; + NodeTonemap *ntm = this->m_data; + const float f = expf(-this->m_data->f); + const float m = (ntm->m > 0.0f) ? ntm->m : (0.3f + 0.7f * powf(avg->auto_key, 1.4f)); + const float ic = 1.0f - ntm->c; + const float ia = 1.0f - ntm->a; + for (BuffersIterator<float> it = output->iterate_with(inputs, area); !it.is_end(); ++it) { + copy_v4_v4(it.out, it.in(0)); + const float L = IMB_colormanagement_get_luminance(it.out); + float I_l = it.out[0] + ic * (L - it.out[0]); + float I_g = avg->cav[0] + ic * (avg->lav - avg->cav[0]); + float I_a = I_l + ia * (I_g - I_l); + it.out[0] /= (it.out[0] + powf(f * I_a, m)); + I_l = it.out[1] + ic * (L - it.out[1]); + I_g = avg->cav[1] + ic * (avg->lav - avg->cav[1]); + I_a = I_l + ia * (I_g - I_l); + it.out[1] /= (it.out[1] + powf(f * I_a, m)); + I_l = it.out[2] + ic * (L - it.out[2]); + I_g = avg->cav[2] + ic * (avg->lav - avg->cav[2]); + I_a = I_l + ia * (I_g - I_l); + it.out[2] /= (it.out[2] + powf(f * I_a, m)); + } +} + } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_TonemapOperation.h b/source/blender/compositor/operations/COM_TonemapOperation.h index 7ecb179504d..56b57730ec1 100644 --- a/source/blender/compositor/operations/COM_TonemapOperation.h +++ b/source/blender/compositor/operations/COM_TonemapOperation.h @@ -18,7 +18,7 @@ #pragma once -#include "COM_NodeOperation.h" +#include "COM_MultiThreadedOperation.h" #include "DNA_node_types.h" namespace blender::compositor { @@ -39,7 +39,7 @@ typedef struct AvgLogLum { * \brief base class of tonemap, implementing the simple tonemap * \ingroup operation */ -class TonemapOperation : public NodeOperation { +class TonemapOperation : public MultiThreadedOperation { protected: /** * \brief Cached reference to the reader @@ -85,6 +85,14 @@ class TonemapOperation : public NodeOperation { bool determineDependingAreaOfInterest(rcti *input, ReadBufferOperation *readOperation, rcti *output) override; + + void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area) override; + void update_memory_buffer_started(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) override; + virtual void update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) override; }; /** @@ -99,6 +107,10 @@ class PhotoreceptorTonemapOperation : public TonemapOperation { * The inner loop of this operation. */ void executePixel(float output[4], int x, int y, void *data) override; + + void update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_TrackPositionOperation.cc b/source/blender/compositor/operations/COM_TrackPositionOperation.cc index 993410e3e84..0f4be16a620 100644 --- a/source/blender/compositor/operations/COM_TrackPositionOperation.cc +++ b/source/blender/compositor/operations/COM_TrackPositionOperation.cc @@ -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]) { diff --git a/source/blender/compositor/operations/COM_TrackPositionOperation.h b/source/blender/compositor/operations/COM_TrackPositionOperation.h index b0b0a123bd6..f716bd97737 100644 --- a/source/blender/compositor/operations/COM_TrackPositionOperation.h +++ b/source/blender/compositor/operations/COM_TrackPositionOperation.h @@ -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 diff --git a/source/blender/compositor/operations/COM_VectorCurveOperation.cc b/source/blender/compositor/operations/COM_VectorCurveOperation.cc index 9d53ed5d8ee..c2087fd071e 100644 --- a/source/blender/compositor/operations/COM_VectorCurveOperation.cc +++ b/source/blender/compositor/operations/COM_VectorCurveOperation.cc @@ -53,4 +53,14 @@ void VectorCurveOperation::deinitExecution() this->m_inputProgram = nullptr; } +void VectorCurveOperation::update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) +{ + CurveMapping *curve_map = this->m_curveMapping; + for (BuffersIterator<float> it = output->iterate_with(inputs, area); !it.is_end(); ++it) { + BKE_curvemapping_evaluate_premulRGBF(curve_map, it.out, it.in(0)); + } +} + } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_VectorCurveOperation.h b/source/blender/compositor/operations/COM_VectorCurveOperation.h index 8cbb80e27c7..27b3ad69e17 100644 --- a/source/blender/compositor/operations/COM_VectorCurveOperation.h +++ b/source/blender/compositor/operations/COM_VectorCurveOperation.h @@ -47,6 +47,10 @@ class VectorCurveOperation : public CurveBaseOperation { * Deinitialize the execution */ void deinitExecution() override; + + void update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_WrapOperation.cc b/source/blender/compositor/operations/COM_WrapOperation.cc index d0d2fcac3ac..888602114cc 100644 --- a/source/blender/compositor/operations/COM_WrapOperation.cc +++ b/source/blender/compositor/operations/COM_WrapOperation.cc @@ -57,20 +57,20 @@ void WrapOperation::executePixelSampled(float output[4], float x, float y, Pixel MemoryBufferExtend extend_x = MemoryBufferExtend::Clip, extend_y = MemoryBufferExtend::Clip; switch (m_wrappingType) { case CMP_NODE_WRAP_NONE: - // Intentionally empty, originalXPos and originalYPos have been set before + /* Intentionally empty, originalXPos and originalYPos have been set before. */ break; case CMP_NODE_WRAP_X: - // wrap only on the x-axis + /* Wrap only on the x-axis. */ nx = this->getWrappedOriginalXPos(x); extend_x = MemoryBufferExtend::Repeat; break; case CMP_NODE_WRAP_Y: - // wrap only on the y-axis + /* Wrap only on the y-axis. */ ny = this->getWrappedOriginalYPos(y); extend_y = MemoryBufferExtend::Repeat; break; case CMP_NODE_WRAP_XY: - // wrap on both + /* Wrap on both. */ nx = this->getWrappedOriginalXPos(x); ny = this->getWrappedOriginalYPos(y); extend_x = MemoryBufferExtend::Repeat; @@ -92,7 +92,7 @@ bool WrapOperation::determineDependingAreaOfInterest(rcti *input, newInput.ymax = input->ymax; if (ELEM(m_wrappingType, CMP_NODE_WRAP_X, CMP_NODE_WRAP_XY)) { - // wrap only on the x-axis if tile is wrapping + /* Wrap only on the x-axis if tile is wrapping. */ newInput.xmin = getWrappedOriginalXPos(input->xmin); newInput.xmax = roundf(getWrappedOriginalXPos(input->xmax)); if (newInput.xmin >= newInput.xmax) { @@ -101,7 +101,7 @@ bool WrapOperation::determineDependingAreaOfInterest(rcti *input, } } if (ELEM(m_wrappingType, CMP_NODE_WRAP_Y, CMP_NODE_WRAP_XY)) { - // wrap only on the y-axis if tile is wrapping + /* Wrap only on the y-axis if tile is wrapping. */ newInput.ymin = getWrappedOriginalYPos(input->ymin); newInput.ymax = roundf(getWrappedOriginalYPos(input->ymax)); if (newInput.ymin >= newInput.ymax) { diff --git a/source/blender/compositor/operations/COM_ZCombineOperation.cc b/source/blender/compositor/operations/COM_ZCombineOperation.cc index 9d3ca7e736e..7050c3b2d83 100644 --- a/source/blender/compositor/operations/COM_ZCombineOperation.cc +++ b/source/blender/compositor/operations/COM_ZCombineOperation.cc @@ -33,6 +33,7 @@ ZCombineOperation::ZCombineOperation() this->m_depth1Reader = nullptr; this->m_image2Reader = nullptr; this->m_depth2Reader = nullptr; + this->flags.can_be_constant = true; } void ZCombineOperation::initExecution() @@ -60,6 +61,19 @@ void ZCombineOperation::executePixelSampled(float output[4], this->m_image2Reader->readSampled(output, x, y, sampler); } } + +void ZCombineOperation::update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) +{ + for (BuffersIterator<float> it = output->iterate_with(inputs, area); !it.is_end(); ++it) { + const float depth1 = *it.in(1); + const float depth2 = *it.in(3); + const float *color = (depth1 < depth2) ? it.in(0) : it.in(2); + copy_v4_v4(it.out, color); + } +} + void ZCombineAlphaOperation::executePixelSampled(float output[4], float x, float y, @@ -88,6 +102,32 @@ void ZCombineAlphaOperation::executePixelSampled(float output[4], output[3] = MAX2(color1[3], color2[3]); } +void ZCombineAlphaOperation::update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) +{ + for (BuffersIterator<float> it = output->iterate_with(inputs, area); !it.is_end(); ++it) { + const float depth1 = *it.in(1); + const float depth2 = *it.in(3); + const float *color1; + const float *color2; + if (depth1 <= depth2) { + color1 = it.in(0); + color2 = it.in(2); + } + else { + color1 = it.in(2); + color2 = it.in(0); + } + const float fac = color1[3]; + const float ifac = 1.0f - fac; + it.out[0] = fac * color1[0] + ifac * color2[0]; + it.out[1] = fac * color1[1] + ifac * color2[1]; + it.out[2] = fac * color1[2] + ifac * color2[2]; + it.out[3] = MAX2(color1[3], color2[3]); + } +} + void ZCombineOperation::deinitExecution() { this->m_image1Reader = nullptr; @@ -132,6 +172,18 @@ void ZCombineMaskOperation::executePixelSampled(float output[4], interp_v4_v4v4(output, color1, color2, 1.0f - mask[0]); } +void ZCombineMaskOperation::update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) +{ + for (BuffersIterator<float> it = output->iterate_with(inputs, area); !it.is_end(); ++it) { + const float mask = *it.in(0); + const float *color1 = it.in(1); + const float *color2 = it.in(2); + interp_v4_v4v4(it.out, color1, color2, 1.0f - mask); + } +} + void ZCombineMaskAlphaOperation::executePixelSampled(float output[4], float x, float y, @@ -154,6 +206,24 @@ void ZCombineMaskAlphaOperation::executePixelSampled(float output[4], output[3] = MAX2(color1[3], color2[3]); } +void ZCombineMaskAlphaOperation::update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) +{ + for (BuffersIterator<float> it = output->iterate_with(inputs, area); !it.is_end(); ++it) { + const float mask = *it.in(0); + const float *color1 = it.in(1); + const float *color2 = it.in(2); + const float fac = (1.0f - mask) * (1.0f - color1[3]) + mask * color2[3]; + const float mfac = 1.0f - fac; + + it.out[0] = color1[0] * mfac + color2[0] * fac; + it.out[1] = color1[1] * mfac + color2[1] * fac; + it.out[2] = color1[2] * mfac + color2[2] * fac; + it.out[3] = MAX2(color1[3], color2[3]); + } +} + void ZCombineMaskOperation::deinitExecution() { this->m_image1Reader = nullptr; diff --git a/source/blender/compositor/operations/COM_ZCombineOperation.h b/source/blender/compositor/operations/COM_ZCombineOperation.h index d0b1aee7310..acd60b6c866 100644 --- a/source/blender/compositor/operations/COM_ZCombineOperation.h +++ b/source/blender/compositor/operations/COM_ZCombineOperation.h @@ -26,7 +26,7 @@ namespace blender::compositor { * this program converts an input color to an output value. * it assumes we are in sRGB color space. */ -class ZCombineOperation : public NodeOperation { +class ZCombineOperation : public MultiThreadedOperation { protected: SocketReader *m_image1Reader; SocketReader *m_depth1Reader; @@ -46,13 +46,21 @@ class ZCombineOperation : public NodeOperation { * The inner loop of this operation. */ 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 ZCombineAlphaOperation : public ZCombineOperation { 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 ZCombineMaskOperation : public NodeOperation { +class ZCombineMaskOperation : public MultiThreadedOperation { protected: SocketReader *m_maskReader; SocketReader *m_image1Reader; @@ -64,9 +72,17 @@ class ZCombineMaskOperation : public NodeOperation { void initExecution() override; void deinitExecution() override; 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 ZCombineMaskAlphaOperation : public ZCombineMaskOperation { 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 |