diff options
Diffstat (limited to 'source/blender/compositor')
22 files changed, 934 insertions, 57 deletions
diff --git a/source/blender/compositor/operations/COM_CropOperation.cc b/source/blender/compositor/operations/COM_CropOperation.cc index f12d93bc8d3..12833660fcb 100644 --- a/source/blender/compositor/operations/COM_CropOperation.cc +++ b/source/blender/compositor/operations/COM_CropOperation.cc @@ -95,6 +95,22 @@ void CropOperation::executePixelSampled(float output[4], float x, float y, Pixel } } +void CropOperation::update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) +{ + rcti crop_area; + BLI_rcti_init(&crop_area, m_xmin, m_xmax, m_ymin, m_ymax); + for (BuffersIterator<float> it = output->iterate_with(inputs, area); !it.is_end(); ++it) { + if (BLI_rcti_isect_pt(&crop_area, it.x, it.y)) { + copy_v4_v4(it.out, it.in(0)); + } + else { + zero_v4(it.out); + } + } +} + CropImageOperation::CropImageOperation() : CropBaseOperation() { /* pass */ @@ -114,6 +130,18 @@ bool CropImageOperation::determineDependingAreaOfInterest(rcti *input, return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output); } +void CropImageOperation::get_area_of_interest(const int input_idx, + const rcti &output_area, + rcti &r_input_area) +{ + BLI_assert(input_idx == 0); + UNUSED_VARS_NDEBUG(input_idx); + r_input_area.xmax = output_area.xmax + this->m_xmin; + r_input_area.xmin = output_area.xmin + this->m_xmin; + r_input_area.ymax = output_area.ymax + this->m_ymin; + r_input_area.ymin = output_area.ymin + this->m_ymin; +} + void CropImageOperation::determineResolution(unsigned int resolution[2], unsigned int preferredResolution[2]) { @@ -136,4 +164,21 @@ void CropImageOperation::executePixelSampled(float output[4], } } +void CropImageOperation::update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) +{ + rcti op_area; + BLI_rcti_init(&op_area, 0, getWidth(), 0, getHeight()); + const MemoryBuffer *input = inputs[0]; + for (BuffersIterator<float> it = output->iterate_with({}, area); !it.is_end(); ++it) { + if (BLI_rcti_isect_pt(&op_area, it.x, it.y)) { + input->read_elem_checked(it.x + this->m_xmin, it.y + this->m_ymin, it.out); + } + else { + zero_v4(it.out); + } + } +} + } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_CropOperation.h b/source/blender/compositor/operations/COM_CropOperation.h index acdff79a77c..57caa4e5834 100644 --- a/source/blender/compositor/operations/COM_CropOperation.h +++ b/source/blender/compositor/operations/COM_CropOperation.h @@ -18,11 +18,11 @@ #pragma once -#include "COM_NodeOperation.h" +#include "COM_MultiThreadedOperation.h" namespace blender::compositor { -class CropBaseOperation : public NodeOperation { +class CropBaseOperation : public MultiThreadedOperation { protected: SocketReader *m_inputOperation; NodeTwoXYs *m_settings; @@ -53,6 +53,10 @@ class CropOperation : public CropBaseOperation { public: CropOperation(); 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 CropImageOperation : public CropBaseOperation { @@ -65,6 +69,11 @@ class CropImageOperation : public CropBaseOperation { void determineResolution(unsigned int resolution[2], unsigned int preferredResolution[2]) override; void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + + 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_DisplaceOperation.cc b/source/blender/compositor/operations/COM_DisplaceOperation.cc index 9f3f5cfe489..a4c01fda7ca 100644 --- a/source/blender/compositor/operations/COM_DisplaceOperation.cc +++ b/source/blender/compositor/operations/COM_DisplaceOperation.cc @@ -32,20 +32,30 @@ DisplaceOperation::DisplaceOperation() this->flags.complex = true; this->m_inputColorProgram = nullptr; - this->m_inputVectorProgram = nullptr; - this->m_inputScaleXProgram = nullptr; - this->m_inputScaleYProgram = nullptr; } void DisplaceOperation::initExecution() { this->m_inputColorProgram = this->getInputSocketReader(0); - this->m_inputVectorProgram = this->getInputSocketReader(1); - this->m_inputScaleXProgram = this->getInputSocketReader(2); - this->m_inputScaleYProgram = this->getInputSocketReader(3); + NodeOperation *vector = this->getInputSocketReader(1); + NodeOperation *scale_x = this->getInputSocketReader(2); + NodeOperation *scale_y = this->getInputSocketReader(3); + if (execution_model_ == eExecutionModel::Tiled) { + vector_read_fn_ = [=](float x, float y, float *out) { + vector->readSampled(out, x, y, PixelSampler::Bilinear); + }; + scale_x_read_fn_ = [=](float x, float y, float *out) { + scale_x->readSampled(out, x, y, PixelSampler::Nearest); + }; + scale_y_read_fn_ = [=](float x, float y, float *out) { + scale_y->readSampled(out, x, y, PixelSampler::Nearest); + }; + } this->m_width_x4 = this->getWidth() * 4; this->m_height_x4 = this->getHeight() * 4; + input_vector_width_ = vector->getWidth(); + input_vector_height_ = vector->getHeight(); } void DisplaceOperation::executePixelSampled(float output[4], @@ -69,8 +79,8 @@ void DisplaceOperation::executePixelSampled(float output[4], bool DisplaceOperation::read_displacement( float x, float y, float xscale, float yscale, const float origin[2], float &r_u, float &r_v) { - float width = m_inputVectorProgram->getWidth(); - float height = m_inputVectorProgram->getHeight(); + float width = input_vector_width_; + float height = input_vector_height_; if (x < 0.0f || x >= width || y < 0.0f || y >= height) { r_u = 0.0f; r_v = 0.0f; @@ -78,7 +88,7 @@ bool DisplaceOperation::read_displacement( } float col[4]; - m_inputVectorProgram->readSampled(col, x, y, PixelSampler::Bilinear); + vector_read_fn_(x, y, col); r_u = origin[0] - col[0] * xscale; r_v = origin[1] - col[1] * yscale; return true; @@ -90,9 +100,9 @@ void DisplaceOperation::pixelTransform(const float xy[2], float r_uv[2], float r float uv[2]; /* temporary variables for derivative estimation */ int num; - m_inputScaleXProgram->readSampled(col, xy[0], xy[1], PixelSampler::Nearest); + scale_x_read_fn_(xy[0], xy[1], col); float xs = col[0]; - m_inputScaleYProgram->readSampled(col, xy[0], xy[1], PixelSampler::Nearest); + scale_y_read_fn_(xy[0], xy[1], col); float ys = col[0]; /* clamp x and y displacement to triple image resolution - * to prevent hangs from huge values mistakenly plugged in eg. z buffers */ @@ -146,9 +156,9 @@ void DisplaceOperation::pixelTransform(const float xy[2], float r_uv[2], float r void DisplaceOperation::deinitExecution() { this->m_inputColorProgram = nullptr; - this->m_inputVectorProgram = nullptr; - this->m_inputScaleXProgram = nullptr; - this->m_inputScaleYProgram = nullptr; + vector_read_fn_ = nullptr; + scale_x_read_fn_ = nullptr; + scale_y_read_fn_ = nullptr; } bool DisplaceOperation::determineDependingAreaOfInterest(rcti *input, @@ -195,4 +205,61 @@ bool DisplaceOperation::determineDependingAreaOfInterest(rcti *input, return false; } +void DisplaceOperation::get_area_of_interest(const int input_idx, + const rcti &output_area, + rcti &r_input_area) +{ + switch (input_idx) { + case 0: { + r_input_area.xmin = 0; + r_input_area.ymin = 0; + r_input_area.xmax = getInputOperation(input_idx)->getWidth(); + r_input_area.ymax = getInputOperation(input_idx)->getHeight(); + break; + } + case 1: { + r_input_area = output_area; + expand_area_for_sampler(r_input_area, PixelSampler::Bilinear); + break; + } + default: { + r_input_area = output_area; + break; + } + } +} + +void DisplaceOperation::update_memory_buffer_started(MemoryBuffer *UNUSED(output), + const rcti &UNUSED(area), + Span<MemoryBuffer *> inputs) +{ + MemoryBuffer *vector = inputs[1]; + MemoryBuffer *scale_x = inputs[2]; + MemoryBuffer *scale_y = inputs[3]; + vector_read_fn_ = [=](float x, float y, float *out) { vector->read_elem_bilinear(x, y, out); }; + scale_x_read_fn_ = [=](float x, float y, float *out) { scale_x->read_elem_checked(x, y, out); }; + scale_y_read_fn_ = [=](float x, float y, float *out) { scale_y->read_elem_checked(x, y, out); }; +} + +void DisplaceOperation::update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) +{ + const MemoryBuffer *input_color = inputs[0]; + for (BuffersIterator<float> it = output->iterate_with({}, area); !it.is_end(); ++it) { + const float xy[2] = {(float)it.x, (float)it.y}; + float uv[2]; + float deriv[2][2]; + + pixelTransform(xy, uv, deriv); + if (is_zero_v2(deriv[0]) && is_zero_v2(deriv[1])) { + input_color->read_elem_bilinear(uv[0], uv[1], it.out); + } + else { + /* EWA filtering (without nearest it gets blurry with NO distortion). */ + input_color->read_elem_filtered(uv[0], uv[1], deriv[0], deriv[1], it.out); + } + } +} + } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_DisplaceOperation.h b/source/blender/compositor/operations/COM_DisplaceOperation.h index fd82692f687..5be914ab672 100644 --- a/source/blender/compositor/operations/COM_DisplaceOperation.h +++ b/source/blender/compositor/operations/COM_DisplaceOperation.h @@ -18,23 +18,27 @@ #pragma once -#include "COM_NodeOperation.h" +#include "COM_MultiThreadedOperation.h" namespace blender::compositor { -class DisplaceOperation : public NodeOperation { +class DisplaceOperation : public MultiThreadedOperation { private: /** * Cached reference to the inputProgram */ SocketReader *m_inputColorProgram; - SocketReader *m_inputVectorProgram; - SocketReader *m_inputScaleXProgram; - SocketReader *m_inputScaleYProgram; float m_width_x4; float m_height_x4; + int input_vector_width_; + int input_vector_height_; + + std::function<void(float x, float y, float *out)> vector_read_fn_; + std::function<void(float x, float y, float *out)> scale_x_read_fn_; + std::function<void(float x, float y, float *out)> scale_y_read_fn_; + public: DisplaceOperation(); @@ -62,6 +66,14 @@ class DisplaceOperation : public NodeOperation { */ void deinitExecution() 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; + void update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) override; + private: bool read_displacement( float x, float y, float xscale, float yscale, const float origin[2], float &r_u, float &r_v); diff --git a/source/blender/compositor/operations/COM_DisplaceSimpleOperation.cc b/source/blender/compositor/operations/COM_DisplaceSimpleOperation.cc index f4b77f5d32c..e1c531bd49e 100644 --- a/source/blender/compositor/operations/COM_DisplaceSimpleOperation.cc +++ b/source/blender/compositor/operations/COM_DisplaceSimpleOperation.cc @@ -132,4 +132,56 @@ bool DisplaceSimpleOperation::determineDependingAreaOfInterest(rcti *input, return false; } +void DisplaceSimpleOperation::get_area_of_interest(const int input_idx, + const rcti &output_area, + rcti &r_input_area) +{ + switch (input_idx) { + case 0: { + r_input_area.xmin = 0; + r_input_area.ymin = 0; + r_input_area.xmax = getInputOperation(input_idx)->getWidth(); + r_input_area.ymax = getInputOperation(input_idx)->getHeight(); + break; + } + default: { + r_input_area = output_area; + break; + } + } +} + +void DisplaceSimpleOperation::update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) +{ + const float width = this->getWidth(); + const float height = this->getHeight(); + const MemoryBuffer *input_color = inputs[0]; + for (BuffersIterator<float> it = output->iterate_with(inputs.drop_front(1), area); !it.is_end(); + ++it) { + float scale_x = *it.in(1); + float scale_y = *it.in(2); + + /* Clamp x and y displacement to triple image resolution - + * to prevent hangs from huge values mistakenly plugged in eg. z buffers. */ + CLAMP(scale_x, -m_width_x4, m_width_x4); + CLAMP(scale_y, -m_height_x4, m_height_x4); + + /* Main displacement in pixel space. */ + const float *vector = it.in(0); + const float p_dx = vector[0] * scale_x; + const float p_dy = vector[1] * scale_y; + + /* Displaced pixel in uv coords, for image sampling. */ + /* Clamp nodes to avoid glitches. */ + float u = it.x - p_dx + 0.5f; + float v = it.y - p_dy + 0.5f; + CLAMP(u, 0.0f, width - 1.0f); + CLAMP(v, 0.0f, height - 1.0f); + + input_color->read_elem_checked(u, v, it.out); + } +} + } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_DisplaceSimpleOperation.h b/source/blender/compositor/operations/COM_DisplaceSimpleOperation.h index 15e6fcd0523..99f52155466 100644 --- a/source/blender/compositor/operations/COM_DisplaceSimpleOperation.h +++ b/source/blender/compositor/operations/COM_DisplaceSimpleOperation.h @@ -18,11 +18,11 @@ #pragma once -#include "COM_NodeOperation.h" +#include "COM_MultiThreadedOperation.h" namespace blender::compositor { -class DisplaceSimpleOperation : public NodeOperation { +class DisplaceSimpleOperation : public MultiThreadedOperation { private: /** * Cached reference to the inputProgram @@ -59,6 +59,11 @@ class DisplaceSimpleOperation : public NodeOperation { * Deinitialize the execution */ void deinitExecution() override; + + 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_FlipOperation.cc b/source/blender/compositor/operations/COM_FlipOperation.cc index 8afbec4ddbe..d0dc6c0b570 100644 --- a/source/blender/compositor/operations/COM_FlipOperation.cc +++ b/source/blender/compositor/operations/COM_FlipOperation.cc @@ -75,4 +75,42 @@ bool FlipOperation::determineDependingAreaOfInterest(rcti *input, return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output); } +void FlipOperation::get_area_of_interest(const int input_idx, + const rcti &output_area, + rcti &r_input_area) +{ + BLI_assert(input_idx == 0); + UNUSED_VARS_NDEBUG(input_idx); + if (this->m_flipX) { + const int w = (int)this->getWidth() - 1; + r_input_area.xmax = (w - output_area.xmin) + 1; + r_input_area.xmin = (w - output_area.xmax) - 1; + } + else { + r_input_area.xmin = output_area.xmin; + r_input_area.xmax = output_area.xmax; + } + if (this->m_flipY) { + const int h = (int)this->getHeight() - 1; + r_input_area.ymax = (h - output_area.ymin) + 1; + r_input_area.ymin = (h - output_area.ymax) - 1; + } + else { + r_input_area.ymin = output_area.ymin; + r_input_area.ymax = output_area.ymax; + } +} + +void FlipOperation::update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) +{ + const MemoryBuffer *input_img = inputs[0]; + for (BuffersIterator<float> it = output->iterate_with({}, area); !it.is_end(); ++it) { + const int nx = this->m_flipX ? ((int)this->getWidth() - 1) - it.x : it.x; + const int ny = this->m_flipY ? ((int)this->getHeight() - 1) - it.y : it.y; + input_img->read_elem(nx, ny, it.out); + } +} + } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_FlipOperation.h b/source/blender/compositor/operations/COM_FlipOperation.h index f26d587fde6..dba7f82c341 100644 --- a/source/blender/compositor/operations/COM_FlipOperation.h +++ b/source/blender/compositor/operations/COM_FlipOperation.h @@ -18,11 +18,11 @@ #pragma once -#include "COM_NodeOperation.h" +#include "COM_MultiThreadedOperation.h" namespace blender::compositor { -class FlipOperation : public NodeOperation { +class FlipOperation : public MultiThreadedOperation { private: SocketReader *m_inputOperation; bool m_flipX; @@ -45,6 +45,11 @@ class FlipOperation : public NodeOperation { { this->m_flipY = flipY; } + + 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_MapUVOperation.cc b/source/blender/compositor/operations/COM_MapUVOperation.cc index 74e3d965d41..ad047c619f8 100644 --- a/source/blender/compositor/operations/COM_MapUVOperation.cc +++ b/source/blender/compositor/operations/COM_MapUVOperation.cc @@ -34,10 +34,26 @@ MapUVOperation::MapUVOperation() this->m_inputColorProgram = nullptr; } +void MapUVOperation::init_data() +{ + NodeOperation *image_input = get_input_operation(0); + image_width_ = image_input->getWidth(); + image_height_ = image_input->getHeight(); + + NodeOperation *uv_input = get_input_operation(1); + uv_width_ = uv_input->getWidth(); + uv_height_ = uv_input->getHeight(); +} + void MapUVOperation::initExecution() { this->m_inputColorProgram = this->getInputSocketReader(0); this->m_inputUVProgram = this->getInputSocketReader(1); + if (execution_model_ == eExecutionModel::Tiled) { + uv_input_read_fn_ = [=](float x, float y, float *out) { + this->m_inputUVProgram->readSampled(out, x, y, PixelSampler::Bilinear); + }; + } } void MapUVOperation::executePixelSampled(float output[4], @@ -81,9 +97,7 @@ void MapUVOperation::executePixelSampled(float output[4], bool MapUVOperation::read_uv(float x, float y, float &r_u, float &r_v, float &r_alpha) { - float width = m_inputUVProgram->getWidth(); - float height = m_inputUVProgram->getHeight(); - if (x < 0.0f || x >= width || y < 0.0f || y >= height) { + if (x < 0.0f || x >= uv_width_ || y < 0.0f || y >= uv_height_) { r_u = 0.0f; r_v = 0.0f; r_alpha = 0.0f; @@ -91,9 +105,9 @@ bool MapUVOperation::read_uv(float x, float y, float &r_u, float &r_v, float &r_ } float vector[3]; - m_inputUVProgram->readSampled(vector, x, y, PixelSampler::Bilinear); - r_u = vector[0] * m_inputColorProgram->getWidth(); - r_v = vector[1] * m_inputColorProgram->getHeight(); + uv_input_read_fn_(x, y, vector); + r_u = vector[0] * image_width_; + r_v = vector[1] * image_height_; r_alpha = vector[2]; return true; } @@ -186,4 +200,75 @@ bool MapUVOperation::determineDependingAreaOfInterest(rcti *input, return false; } +void MapUVOperation::get_area_of_interest(const int input_idx, + const rcti &output_area, + rcti &r_input_area) +{ + switch (input_idx) { + case 0: { + r_input_area.xmin = 0; + r_input_area.xmax = image_width_; + r_input_area.ymin = 0; + r_input_area.ymax = image_height_; + break; + } + case 1: { + r_input_area = output_area; + expand_area_for_sampler(r_input_area, PixelSampler::Bilinear); + break; + } + } +} + +void MapUVOperation::update_memory_buffer_started(MemoryBuffer *UNUSED(output), + const rcti &UNUSED(area), + Span<MemoryBuffer *> inputs) +{ + const MemoryBuffer *uv_input = inputs[1]; + uv_input_read_fn_ = [=](float x, float y, float *out) { + uv_input->read_elem_bilinear(x, y, out); + }; +} + +void MapUVOperation::update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) +{ + const MemoryBuffer *input_image = inputs[0]; + for (BuffersIterator<float> it = output->iterate_with({}, area); !it.is_end(); ++it) { + float xy[2] = {(float)it.x, (float)it.y}; + float uv[2]; + float deriv[2][2]; + float alpha; + pixelTransform(xy, uv, deriv, alpha); + if (alpha == 0.0f) { + zero_v4(it.out); + continue; + } + + /* EWA filtering. */ + input_image->read_elem_filtered(uv[0], uv[1], deriv[0], deriv[1], it.out); + + /* UV to alpha threshold. */ + const float threshold = this->m_alpha * 0.05f; + /* XXX alpha threshold is used to fade out pixels on boundaries with invalid derivatives. + * this calculation is not very well defined, should be looked into if it becomes a problem ... + */ + const float du = len_v2(deriv[0]); + const float dv = len_v2(deriv[1]); + const float factor = 1.0f - threshold * (du / image_width_ + dv / image_height_); + if (factor < 0.0f) { + alpha = 0.0f; + } + else { + alpha *= factor; + } + + /* "premul" */ + if (alpha < 1.0f) { + mul_v4_fl(it.out, alpha); + } + } +} + } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_MapUVOperation.h b/source/blender/compositor/operations/COM_MapUVOperation.h index eb5f7d49122..65fbcb461c9 100644 --- a/source/blender/compositor/operations/COM_MapUVOperation.h +++ b/source/blender/compositor/operations/COM_MapUVOperation.h @@ -18,11 +18,11 @@ #pragma once -#include "COM_NodeOperation.h" +#include "COM_MultiThreadedOperation.h" namespace blender::compositor { -class MapUVOperation : public NodeOperation { +class MapUVOperation : public MultiThreadedOperation { private: /** * Cached reference to the inputProgram @@ -30,8 +30,15 @@ class MapUVOperation : public NodeOperation { SocketReader *m_inputUVProgram; SocketReader *m_inputColorProgram; + int uv_width_; + int uv_height_; + int image_width_; + int image_height_; + float m_alpha; + std::function<void(float x, float y, float *out)> uv_input_read_fn_; + public: MapUVOperation(); @@ -49,6 +56,8 @@ class MapUVOperation : public NodeOperation { void pixelTransform(const float xy[2], float r_uv[2], float r_deriv[2][2], float &r_alpha); + void init_data() override; + /** * Initialize the execution */ @@ -64,6 +73,14 @@ class MapUVOperation : public NodeOperation { this->m_alpha = alpha; } + 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; + void update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) override; + private: bool read_uv(float x, float y, float &r_u, float &r_v, float &r_alpha); }; diff --git a/source/blender/compositor/operations/COM_MovieDistortionOperation.cc b/source/blender/compositor/operations/COM_MovieDistortionOperation.cc index c8e045ea117..d3424959061 100644 --- a/source/blender/compositor/operations/COM_MovieDistortionOperation.cc +++ b/source/blender/compositor/operations/COM_MovieDistortionOperation.cc @@ -128,4 +128,51 @@ bool MovieDistortionOperation::determineDependingAreaOfInterest(rcti *input, return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output); } +void MovieDistortionOperation::get_area_of_interest(const int input_idx, + const rcti &output_area, + rcti &r_input_area) +{ + BLI_assert(input_idx == 0); + UNUSED_VARS_NDEBUG(input_idx); + r_input_area.xmin = output_area.xmin - m_margin[0]; + r_input_area.ymin = output_area.ymin - m_margin[1]; + r_input_area.xmax = output_area.xmax + m_margin[0]; + r_input_area.ymax = output_area.ymax + m_margin[1]; +} + +void MovieDistortionOperation::update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) +{ + const MemoryBuffer *input_img = inputs[0]; + if (this->m_distortion == nullptr) { + output->copy_from(input_img, area); + return; + } + + /* `float overscan = 0.0f;` */ + const float pixel_aspect = this->m_pixel_aspect; + const float w = (float)this->m_width /* `/ (1 + overscan)` */; + const float h = (float)this->m_height /* `/ (1 + overscan)` */; + const float aspx = w / (float)this->m_calibration_width; + const float aspy = h / (float)this->m_calibration_height; + float xy[2]; + float distorted_xy[2]; + for (BuffersIterator<float> it = output->iterate_with({}, area); !it.is_end(); ++it) { + xy[0] = (it.x /* `- 0.5 * overscan * w` */) / aspx; + xy[1] = (it.y /* `- 0.5 * overscan * h` */) / aspy / pixel_aspect; + + if (this->m_apply) { + BKE_tracking_distortion_undistort_v2(this->m_distortion, xy, distorted_xy); + } + else { + BKE_tracking_distortion_distort_v2(this->m_distortion, xy, distorted_xy); + } + + const float u = distorted_xy[0] * aspx /* `+ 0.5 * overscan * w` */; + const float v = (distorted_xy[1] * aspy /* `+ 0.5 * overscan * h` */) * pixel_aspect; + input_img->read_elem_bilinear(u, v, it.out); + } +} + } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_MovieDistortionOperation.h b/source/blender/compositor/operations/COM_MovieDistortionOperation.h index 631a62f7ebf..69c2f9c269c 100644 --- a/source/blender/compositor/operations/COM_MovieDistortionOperation.h +++ b/source/blender/compositor/operations/COM_MovieDistortionOperation.h @@ -18,7 +18,7 @@ #pragma once -#include "COM_NodeOperation.h" +#include "COM_MultiThreadedOperation.h" #include "DNA_movieclip_types.h" #include "MEM_guardedalloc.h" @@ -26,7 +26,7 @@ namespace blender::compositor { -class MovieDistortionOperation : public NodeOperation { +class MovieDistortionOperation : public MultiThreadedOperation { private: SocketReader *m_inputOperation; MovieClip *m_movieClip; @@ -58,6 +58,11 @@ class MovieDistortionOperation : 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_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_PlaneCornerPinOperation.cc b/source/blender/compositor/operations/COM_PlaneCornerPinOperation.cc index 3577860b93d..d2a06ddd7c4 100644 --- a/source/blender/compositor/operations/COM_PlaneCornerPinOperation.cc +++ b/source/blender/compositor/operations/COM_PlaneCornerPinOperation.cc @@ -16,6 +16,7 @@ */ #include "COM_PlaneCornerPinOperation.h" +#include "COM_ConstantOperation.h" #include "COM_ReadBufferOperation.h" #include "MEM_guardedalloc.h" @@ -28,6 +29,11 @@ namespace blender::compositor { +constexpr int LOWER_LEFT_CORNER_INDEX = 0; +constexpr int LOWER_RIGHT_CORNER_INDEX = 1; +constexpr int UPPER_RIGHT_CORNER_INDEX = 2; +constexpr int UPPER_LEFT_CORNER_INDEX = 3; + static bool check_corners(float corners[4][2]) { int i, next, prev; @@ -58,6 +64,7 @@ static bool check_corners(float corners[4][2]) return true; } +/* TODO(manzanilla): to be removed with tiled implementation. */ static void readCornersFromSockets(rcti *rect, SocketReader *readers[4], float corners[4][2]) { for (int i = 0; i < 4; i++) { @@ -87,6 +94,53 @@ static void readCornersFromSockets(rcti *rect, SocketReader *readers[4], float c } } +static void set_default_corner(const int corner_idx, float r_corner[2]) +{ + BLI_assert(corner_idx >= 0 && corner_idx < 4); + switch (corner_idx) { + case LOWER_LEFT_CORNER_INDEX: + r_corner[0] = 0.0f; + r_corner[1] = 0.0f; + break; + case LOWER_RIGHT_CORNER_INDEX: + r_corner[0] = 1.0f; + r_corner[1] = 0.0f; + break; + case UPPER_RIGHT_CORNER_INDEX: + r_corner[0] = 1.0f; + r_corner[1] = 1.0f; + break; + case UPPER_LEFT_CORNER_INDEX: + r_corner[0] = 0.0f; + r_corner[1] = 1.0f; + break; + } +} + +static void read_input_corners(NodeOperation *op, const int first_input_idx, float r_corners[4][2]) +{ + for (const int i : IndexRange(4)) { + NodeOperation *input = op->get_input_operation(i + first_input_idx); + if (input->get_flags().is_constant_operation) { + ConstantOperation *corner_input = static_cast<ConstantOperation *>(input); + copy_v2_v2(r_corners[i], corner_input->get_constant_elem()); + } + else { + set_default_corner(i, r_corners[i]); + } + } + + /* Convexity check: concave corners need to be prevented, otherwise + * #BKE_tracking_homography_between_two_quads will freeze. */ + if (!check_corners(r_corners)) { + /* Revert to default corners. There could be a more elegant solution, + * this prevents freezing at least. */ + for (const int i : IndexRange(4)) { + set_default_corner(i, r_corners[i]); + } + } +} + /* ******** PlaneCornerPinMaskOperation ******** */ PlaneCornerPinMaskOperation::PlaneCornerPinMaskOperation() : m_corners_ready(false) @@ -103,6 +157,17 @@ PlaneCornerPinMaskOperation::PlaneCornerPinMaskOperation() : m_corners_ready(fal flags.complex = true; } +void PlaneCornerPinMaskOperation::init_data() +{ + if (execution_model_ == eExecutionModel::FullFrame) { + float corners[4][2]; + read_input_corners(this, 0, corners); + calculateCorners(corners, true, 0); + } +} + +/* TODO(manzanilla): to be removed with tiled implementation. Same for #deinitExecution and do the + * same on #PlaneCornerPinWarpImageOperation. */ void PlaneCornerPinMaskOperation::initExecution() { PlaneDistortMaskOperation::initExecution(); @@ -147,10 +212,22 @@ void *PlaneCornerPinMaskOperation::initializeTileData(rcti *rect) void PlaneCornerPinMaskOperation::determineResolution(unsigned int resolution[2], unsigned int preferredResolution[2]) { + if (execution_model_ == eExecutionModel::FullFrame) { + /* Determine inputs resolution. */ + PlaneDistortMaskOperation::determineResolution(resolution, preferredResolution); + } resolution[0] = preferredResolution[0]; resolution[1] = preferredResolution[1]; } +void PlaneCornerPinMaskOperation::get_area_of_interest(const int UNUSED(input_idx), + const rcti &UNUSED(output_area), + rcti &r_input_area) +{ + /* All corner inputs are used as constants. */ + r_input_area = COM_SINGLE_ELEM_AREA; +} + /* ******** PlaneCornerPinWarpImageOperation ******** */ PlaneCornerPinWarpImageOperation::PlaneCornerPinWarpImageOperation() : m_corners_ready(false) @@ -161,6 +238,15 @@ PlaneCornerPinWarpImageOperation::PlaneCornerPinWarpImageOperation() : m_corners addInputSocket(DataType::Vector); } +void PlaneCornerPinWarpImageOperation::init_data() +{ + if (execution_model_ == eExecutionModel::FullFrame) { + float corners[4][2]; + read_input_corners(this, 1, corners); + calculateCorners(corners, true, 0); + } +} + void PlaneCornerPinWarpImageOperation::initExecution() { PlaneDistortWarpImageOperation::initExecution(); @@ -227,4 +313,17 @@ bool PlaneCornerPinWarpImageOperation::determineDependingAreaOfInterest( #endif } +void PlaneCornerPinWarpImageOperation::get_area_of_interest(const int input_idx, + const rcti &output_area, + rcti &r_input_area) +{ + if (input_idx == 0) { + PlaneDistortWarpImageOperation::get_area_of_interest(input_idx, output_area, r_input_area); + } + else { + /* Corner inputs are used as constants. */ + r_input_area = COM_SINGLE_ELEM_AREA; + } +} + } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_PlaneCornerPinOperation.h b/source/blender/compositor/operations/COM_PlaneCornerPinOperation.h index 91c0cd9e16b..2831e937147 100644 --- a/source/blender/compositor/operations/COM_PlaneCornerPinOperation.h +++ b/source/blender/compositor/operations/COM_PlaneCornerPinOperation.h @@ -31,11 +31,13 @@ namespace blender::compositor { class PlaneCornerPinMaskOperation : public PlaneDistortMaskOperation { private: + /* TODO(manzanilla): to be removed with tiled implementation. */ bool m_corners_ready; public: PlaneCornerPinMaskOperation(); + void init_data() override; void initExecution() override; void deinitExecution() override; @@ -43,6 +45,8 @@ class PlaneCornerPinMaskOperation : public PlaneDistortMaskOperation { void determineResolution(unsigned int resolution[2], unsigned int preferredResolution[2]) override; + + void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area) override; }; class PlaneCornerPinWarpImageOperation : public PlaneDistortWarpImageOperation { @@ -52,6 +56,7 @@ class PlaneCornerPinWarpImageOperation : public PlaneDistortWarpImageOperation { public: PlaneCornerPinWarpImageOperation(); + void init_data() override; void initExecution() override; void deinitExecution() override; @@ -60,6 +65,8 @@ class PlaneCornerPinWarpImageOperation : public PlaneDistortWarpImageOperation { 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; }; } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_PlaneDistortCommonOperation.cc b/source/blender/compositor/operations/COM_PlaneDistortCommonOperation.cc index 4edcc206f5b..a80cbbe942a 100644 --- a/source/blender/compositor/operations/COM_PlaneDistortCommonOperation.cc +++ b/source/blender/compositor/operations/COM_PlaneDistortCommonOperation.cc @@ -85,8 +85,9 @@ void PlaneDistortWarpImageOperation::calculateCorners(const float corners[4][2], { PlaneDistortBaseOperation::calculateCorners(corners, normalized, sample); - const int width = this->m_pixelReader->getWidth(); - const int height = this->m_pixelReader->getHeight(); + const NodeOperation *image = get_input_operation(0); + const int width = image->getWidth(); + const int height = image->getHeight(); float frame_corners[4][2] = { {0.0f, 0.0f}, {(float)width, 0.0f}, {(float)width, (float)height}, {0.0f, (float)height}}; MotionSample *sample_data = &this->m_samples[sample]; @@ -127,6 +128,34 @@ void PlaneDistortWarpImageOperation::executePixelSampled(float output[4], } } +void PlaneDistortWarpImageOperation::update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) +{ + const MemoryBuffer *input_img = inputs[0]; + float uv[2]; + float deriv[2][2]; + BuffersIterator<float> it = output->iterate_with({}, area); + if (this->m_motion_blur_samples == 1) { + for (; !it.is_end(); ++it) { + warpCoord(it.x, it.y, this->m_samples[0].perspectiveMatrix, uv, deriv); + input_img->read_elem_filtered(uv[0], uv[1], deriv[0], deriv[1], it.out); + } + } + else { + for (; !it.is_end(); ++it) { + zero_v4(it.out); + for (const int sample : IndexRange(this->m_motion_blur_samples)) { + float color[4]; + warpCoord(it.x, it.y, this->m_samples[sample].perspectiveMatrix, uv, deriv); + input_img->read_elem_filtered(uv[0], uv[1], deriv[0], deriv[1], color); + add_v4_v4(it.out, color); + } + mul_v4_fl(it.out, 1.0f / (float)this->m_motion_blur_samples); + } + } +} + bool PlaneDistortWarpImageOperation::determineDependingAreaOfInterest( rcti *input, ReadBufferOperation *readOperation, rcti *output) { @@ -157,6 +186,51 @@ bool PlaneDistortWarpImageOperation::determineDependingAreaOfInterest( return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output); } +void PlaneDistortWarpImageOperation::get_area_of_interest(const int input_idx, + const rcti &output_area, + rcti &r_input_area) +{ + if (input_idx != 0) { + r_input_area = output_area; + return; + } + + /* TODO: figure out the area needed for warping and EWA filtering. */ + r_input_area.xmin = 0; + r_input_area.ymin = 0; + r_input_area.xmax = get_input_operation(0)->getWidth(); + r_input_area.ymax = get_input_operation(0)->getHeight(); + +/* Old implemention but resulting coordinates are way out of input operation bounds and in some + * cases the area result may incorrectly cause cropping. */ +#if 0 + float min[2], max[2]; + INIT_MINMAX2(min, max); + for (int sample = 0; sample < this->m_motion_blur_samples; sample++) { + float UVs[4][2]; + float deriv[2][2]; + MotionSample *sample_data = &this->m_samples[sample]; + /* TODO(sergey): figure out proper way to do this. */ + warpCoord( + output_area.xmin - 2, output_area.ymin - 2, sample_data->perspectiveMatrix, UVs[0], deriv); + warpCoord( + output_area.xmax + 2, output_area.ymin - 2, sample_data->perspectiveMatrix, UVs[1], deriv); + warpCoord( + output_area.xmax + 2, output_area.ymax + 2, sample_data->perspectiveMatrix, UVs[2], deriv); + warpCoord( + output_area.xmin - 2, output_area.ymax + 2, sample_data->perspectiveMatrix, UVs[3], deriv); + for (int i = 0; i < 4; i++) { + minmax_v2v2_v2(min, max, UVs[i]); + } + } + + r_input_area.xmin = min[0] - 1; + r_input_area.ymin = min[1] - 1; + r_input_area.xmax = max[0] + 1; + r_input_area.ymax = max[1] + 1; +#endif +} + /* ******** PlaneDistort Mask ******** */ PlaneDistortMaskOperation::PlaneDistortMaskOperation() : PlaneDistortBaseOperation() @@ -219,4 +293,41 @@ void PlaneDistortMaskOperation::executePixelSampled(float output[4], } } +void PlaneDistortMaskOperation::update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> UNUSED(inputs)) +{ + for (BuffersIterator<float> it = output->iterate_with({}, area); !it.is_end(); ++it) { + int inside_count = 0; + for (const int motion_sample : IndexRange(this->m_motion_blur_samples)) { + MotionSample &sample = this->m_samples[motion_sample]; + inside_count += get_jitter_samples_inside_count(it.x, it.y, sample); + } + *it.out = (float)inside_count / (this->m_osa * this->m_motion_blur_samples); + } +} + +int PlaneDistortMaskOperation::get_jitter_samples_inside_count(int x, + int y, + MotionSample &sample_data) +{ + float point[2]; + int inside_count = 0; + for (int sample = 0; sample < this->m_osa; sample++) { + point[0] = x + this->m_jitter[sample][0]; + point[1] = y + this->m_jitter[sample][1]; + if (isect_point_tri_v2(point, + sample_data.frameSpaceCorners[0], + sample_data.frameSpaceCorners[1], + sample_data.frameSpaceCorners[2]) || + isect_point_tri_v2(point, + sample_data.frameSpaceCorners[0], + sample_data.frameSpaceCorners[2], + sample_data.frameSpaceCorners[3])) { + inside_count++; + } + } + return inside_count; +} + } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_PlaneDistortCommonOperation.h b/source/blender/compositor/operations/COM_PlaneDistortCommonOperation.h index cc6e4d00d71..3ef9c1dfab8 100644 --- a/source/blender/compositor/operations/COM_PlaneDistortCommonOperation.h +++ b/source/blender/compositor/operations/COM_PlaneDistortCommonOperation.h @@ -20,7 +20,7 @@ #include <string.h> -#include "COM_NodeOperation.h" +#include "COM_MultiThreadedOperation.h" #include "DNA_movieclip_types.h" #include "DNA_tracking_types.h" @@ -32,7 +32,7 @@ namespace blender::compositor { #define PLANE_DISTORT_MAX_SAMPLES 64 -class PlaneDistortBaseOperation : public NodeOperation { +class PlaneDistortBaseOperation : public MultiThreadedOperation { protected: struct MotionSample { float frameSpaceCorners[4][2]; /* Corners coordinates in pixel space. */ @@ -78,6 +78,11 @@ class PlaneDistortWarpImageOperation : public PlaneDistortBaseOperation { 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_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) override; }; class PlaneDistortMaskOperation : public PlaneDistortBaseOperation { @@ -91,6 +96,13 @@ class PlaneDistortMaskOperation : public PlaneDistortBaseOperation { void initExecution() 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; + + private: + int get_jitter_samples_inside_count(int x, int y, MotionSample &sample_data); }; } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_PlaneTrackOperation.cc b/source/blender/compositor/operations/COM_PlaneTrackOperation.cc index 0884f2ad979..bf24f843ca2 100644 --- a/source/blender/compositor/operations/COM_PlaneTrackOperation.cc +++ b/source/blender/compositor/operations/COM_PlaneTrackOperation.cc @@ -101,18 +101,40 @@ void PlaneTrackCommon::determineResolution(unsigned int resolution[2], /* ******** PlaneTrackMaskOperation ******** */ +void PlaneTrackMaskOperation::init_data() +{ + PlaneDistortMaskOperation::init_data(); + if (execution_model_ == eExecutionModel::FullFrame) { + PlaneTrackCommon::read_and_calculate_corners(this); + } +} + +/* TODO(manzanilla): to be removed with tiled implementation. */ void PlaneTrackMaskOperation::initExecution() { PlaneDistortMaskOperation::initExecution(); - PlaneTrackCommon::read_and_calculate_corners(this); + if (execution_model_ == eExecutionModel::Tiled) { + PlaneTrackCommon::read_and_calculate_corners(this); + } } /* ******** PlaneTrackWarpImageOperation ******** */ +void PlaneTrackWarpImageOperation::init_data() +{ + PlaneDistortWarpImageOperation::init_data(); + if (execution_model_ == eExecutionModel::FullFrame) { + PlaneTrackCommon::read_and_calculate_corners(this); + } +} + +/* TODO(manzanilla): to be removed with tiled implementation. */ void PlaneTrackWarpImageOperation::initExecution() { PlaneDistortWarpImageOperation::initExecution(); - PlaneTrackCommon::read_and_calculate_corners(this); + if (execution_model_ == eExecutionModel::Tiled) { + PlaneTrackCommon::read_and_calculate_corners(this); + } } } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_PlaneTrackOperation.h b/source/blender/compositor/operations/COM_PlaneTrackOperation.h index 3bae230aa06..d2027755162 100644 --- a/source/blender/compositor/operations/COM_PlaneTrackOperation.h +++ b/source/blender/compositor/operations/COM_PlaneTrackOperation.h @@ -73,6 +73,8 @@ class PlaneTrackMaskOperation : public PlaneDistortMaskOperation, public PlaneTr { } + void init_data() override; + void initExecution() override; void determineResolution(unsigned int resolution[2], @@ -92,6 +94,8 @@ class PlaneTrackWarpImageOperation : public PlaneDistortWarpImageOperation, { } + void init_data() override; + void initExecution() override; void determineResolution(unsigned int resolution[2], diff --git a/source/blender/compositor/operations/COM_ProjectorLensDistortionOperation.cc b/source/blender/compositor/operations/COM_ProjectorLensDistortionOperation.cc index 93702d3f0cf..fcab5dd5751 100644 --- a/source/blender/compositor/operations/COM_ProjectorLensDistortionOperation.cc +++ b/source/blender/compositor/operations/COM_ProjectorLensDistortionOperation.cc @@ -17,6 +17,8 @@ */ #include "COM_ProjectorLensDistortionOperation.h" +#include "COM_ConstantOperation.h" + #include "BLI_math.h" #include "BLI_utildefines.h" @@ -32,6 +34,20 @@ ProjectorLensDistortionOperation::ProjectorLensDistortionOperation() this->m_dispersionAvailable = false; this->m_dispersion = 0.0f; } + +void ProjectorLensDistortionOperation::init_data() +{ + if (execution_model_ == eExecutionModel::FullFrame) { + NodeOperation *dispersion_input = get_input_operation(1); + if (dispersion_input->get_flags().is_constant_operation) { + this->m_dispersion = + static_cast<ConstantOperation *>(dispersion_input)->get_constant_elem()[0]; + } + this->m_kr = 0.25f * max_ff(min_ff(this->m_dispersion, 1.0f), 0.0f); + this->m_kr2 = this->m_kr * 20; + } +} + void ProjectorLensDistortionOperation::initExecution() { this->initMutex(); @@ -97,6 +113,7 @@ bool ProjectorLensDistortionOperation::determineDependingAreaOfInterest( return false; } +/* TODO(manzanilla): to be removed with tiled implementation. */ void ProjectorLensDistortionOperation::updateDispersion() { if (this->m_dispersionAvailable) { @@ -114,4 +131,41 @@ void ProjectorLensDistortionOperation::updateDispersion() this->unlockMutex(); } +void ProjectorLensDistortionOperation::get_area_of_interest(const int input_idx, + const rcti &output_area, + rcti &r_input_area) +{ + if (input_idx == 1) { + /* Dispersion input is used as constant only. */ + r_input_area = COM_SINGLE_ELEM_AREA; + return; + } + + r_input_area.ymax = output_area.ymax; + r_input_area.ymin = output_area.ymin; + r_input_area.xmin = output_area.xmin - this->m_kr2 - 2; + r_input_area.xmax = output_area.xmax + this->m_kr2 + 2; +} + +void ProjectorLensDistortionOperation::update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) +{ + const MemoryBuffer *input_image = inputs[0]; + const float height = this->getHeight(); + const float width = this->getWidth(); + float color[4]; + for (BuffersIterator<float> it = output->iterate_with({}, area); !it.is_end(); ++it) { + const float v = (it.y + 0.5f) / height; + const float u = (it.x + 0.5f) / width; + input_image->read_elem_bilinear((u * width + this->m_kr2) - 0.5f, v * height - 0.5f, color); + it.out[0] = color[0]; + input_image->read_elem(it.x, it.y, color); + it.out[1] = color[1]; + input_image->read_elem_bilinear((u * width - this->m_kr2) - 0.5f, v * height - 0.5f, color); + it.out[2] = color[2]; + it.out[3] = 1.0f; + } +} + } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_ProjectorLensDistortionOperation.h b/source/blender/compositor/operations/COM_ProjectorLensDistortionOperation.h index bce61d3de15..7c7626bf271 100644 --- a/source/blender/compositor/operations/COM_ProjectorLensDistortionOperation.h +++ b/source/blender/compositor/operations/COM_ProjectorLensDistortionOperation.h @@ -18,12 +18,12 @@ #pragma once -#include "COM_NodeOperation.h" +#include "COM_MultiThreadedOperation.h" #include "DNA_node_types.h" namespace blender::compositor { -class ProjectorLensDistortionOperation : public NodeOperation { +class ProjectorLensDistortionOperation : public MultiThreadedOperation { private: /** * Cached reference to the inputProgram @@ -31,6 +31,7 @@ class ProjectorLensDistortionOperation : public NodeOperation { SocketReader *m_inputProgram; float m_dispersion; + /* TODO(manzanilla): to be removed with tiled implementation. */ bool m_dispersionAvailable; float m_kr, m_kr2; @@ -43,6 +44,7 @@ class ProjectorLensDistortionOperation : public NodeOperation { */ void executePixel(float output[4], int x, int y, void *data) override; + void init_data() override; /** * Initialize the execution */ @@ -59,6 +61,11 @@ class ProjectorLensDistortionOperation : public NodeOperation { rcti *output) override; void updateDispersion(); + + 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_ScreenLensDistortionOperation.cc b/source/blender/compositor/operations/COM_ScreenLensDistortionOperation.cc index 634fe66b0dd..f9ba2ef69ad 100644 --- a/source/blender/compositor/operations/COM_ScreenLensDistortionOperation.cc +++ b/source/blender/compositor/operations/COM_ScreenLensDistortionOperation.cc @@ -17,6 +17,7 @@ */ #include "COM_ScreenLensDistortionOperation.h" +#include "COM_ConstantOperation.h" #include "BLI_math.h" #include "BLI_rand.h" @@ -53,6 +54,35 @@ void ScreenLensDistortionOperation::setDispersion(float dispersion) m_dispersion_const = true; } +void ScreenLensDistortionOperation::init_data() +{ + this->m_cx = 0.5f * (float)getWidth(); + this->m_cy = 0.5f * (float)getHeight(); + + switch (execution_model_) { + case eExecutionModel::FullFrame: { + NodeOperation *distortion_op = get_input_operation(1); + NodeOperation *dispersion_op = get_input_operation(2); + if (!m_distortion_const && distortion_op->get_flags().is_constant_operation) { + m_distortion = static_cast<ConstantOperation *>(distortion_op)->get_constant_elem()[0]; + } + if (!m_dispersion_const && distortion_op->get_flags().is_constant_operation) { + m_dispersion = static_cast<ConstantOperation *>(dispersion_op)->get_constant_elem()[0]; + } + updateVariables(m_distortion, m_dispersion); + break; + } + case eExecutionModel::Tiled: { + /* If both are constant, init variables once. */ + if (m_distortion_const && m_dispersion_const) { + updateVariables(m_distortion, m_dispersion); + m_variables_ready = true; + } + break; + } + } +} + void ScreenLensDistortionOperation::initExecution() { this->m_inputProgram = this->getInputSocketReader(0); @@ -61,15 +91,6 @@ void ScreenLensDistortionOperation::initExecution() uint rng_seed = (uint)(PIL_check_seconds_timer_i() & UINT_MAX); rng_seed ^= (uint)POINTER_AS_INT(m_inputProgram); this->m_rng = BLI_rng_new(rng_seed); - - this->m_cx = 0.5f * (float)getWidth(); - this->m_cy = 0.5f * (float)getHeight(); - - /* if both are constant, init variables once */ - if (m_distortion_const && m_dispersion_const) { - updateVariables(m_distortion, m_dispersion); - m_variables_ready = true; - } } void *ScreenLensDistortionOperation::initializeTileData(rcti * /*rect*/) @@ -130,7 +151,7 @@ bool ScreenLensDistortionOperation::get_delta(float r_sq, return false; } -void ScreenLensDistortionOperation::accumulate(MemoryBuffer *buffer, +void ScreenLensDistortionOperation::accumulate(const MemoryBuffer *buffer, int a, int b, float r_sq, @@ -154,7 +175,14 @@ void ScreenLensDistortionOperation::accumulate(MemoryBuffer *buffer, float xy[2]; distort_uv(uv, t, xy); - buffer->readBilinear(color, xy[0], xy[1]); + switch (execution_model_) { + case eExecutionModel::Tiled: + buffer->readBilinear(color, xy[0], xy[1]); + break; + case eExecutionModel::FullFrame: + buffer->read_elem_bilinear(xy[0], xy[1], color); + break; + } sum[a] += (1.0f - tz) * color[a]; sum[b] += (tz)*color[b]; @@ -354,4 +382,143 @@ void ScreenLensDistortionOperation::updateVariables(float distortion, float disp mul_v3_v3fl(m_k4, m_k, 4.0f); } +void ScreenLensDistortionOperation::get_area_of_interest(const int input_idx, + const rcti &UNUSED(output_area), + rcti &r_input_area) +{ + if (input_idx != 0) { + /* Dispersion and distorsion inputs are used as constants only. */ + r_input_area = COM_SINGLE_ELEM_AREA; + } + + /* XXX the original method of estimating the area-of-interest does not work + * it assumes a linear increase/decrease of mapped coordinates, which does not + * yield correct results for the area and leaves uninitialized buffer areas. + * So now just use the full image area, which may not be as efficient but works at least ... + */ +#if 1 + NodeOperation *image = getInputOperation(0); + r_input_area.xmax = image->getWidth(); + r_input_area.xmin = 0; + r_input_area.ymax = image->getHeight(); + r_input_area.ymin = 0; + +#else /* Original method in tiled implementation. */ + rcti newInput; + const float margin = 2; + + BLI_rcti_init_minmax(&newInput); + + if (m_dispersion_const && m_distortion_const) { + /* update from fixed distortion/dispersion */ +# define UPDATE_INPUT(x, y) \ + { \ + float coords[6]; \ + determineUV(coords, x, y); \ + newInput.xmin = min_ffff(newInput.xmin, coords[0], coords[2], coords[4]); \ + newInput.ymin = min_ffff(newInput.ymin, coords[1], coords[3], coords[5]); \ + newInput.xmax = max_ffff(newInput.xmax, coords[0], coords[2], coords[4]); \ + newInput.ymax = max_ffff(newInput.ymax, coords[1], coords[3], coords[5]); \ + } \ + (void)0 + + UPDATE_INPUT(input->xmin, input->xmax); + UPDATE_INPUT(input->xmin, input->ymax); + UPDATE_INPUT(input->xmax, input->ymax); + UPDATE_INPUT(input->xmax, input->ymin); + +# undef UPDATE_INPUT + } + else { + /* use maximum dispersion 1.0 if not const */ + float dispersion = m_dispersion_const ? m_dispersion : 1.0f; + +# define UPDATE_INPUT(x, y, distortion) \ + { \ + float coords[6]; \ + updateVariables(distortion, dispersion); \ + determineUV(coords, x, y); \ + newInput.xmin = min_ffff(newInput.xmin, coords[0], coords[2], coords[4]); \ + newInput.ymin = min_ffff(newInput.ymin, coords[1], coords[3], coords[5]); \ + newInput.xmax = max_ffff(newInput.xmax, coords[0], coords[2], coords[4]); \ + newInput.ymax = max_ffff(newInput.ymax, coords[1], coords[3], coords[5]); \ + } \ + (void)0 + + if (m_distortion_const) { + /* update from fixed distortion */ + UPDATE_INPUT(input->xmin, input->xmax, m_distortion); + UPDATE_INPUT(input->xmin, input->ymax, m_distortion); + UPDATE_INPUT(input->xmax, input->ymax, m_distortion); + UPDATE_INPUT(input->xmax, input->ymin, m_distortion); + } + else { + /* update from min/max distortion (-1..1) */ + UPDATE_INPUT(input->xmin, input->xmax, -1.0f); + UPDATE_INPUT(input->xmin, input->ymax, -1.0f); + UPDATE_INPUT(input->xmax, input->ymax, -1.0f); + UPDATE_INPUT(input->xmax, input->ymin, -1.0f); + + UPDATE_INPUT(input->xmin, input->xmax, 1.0f); + UPDATE_INPUT(input->xmin, input->ymax, 1.0f); + UPDATE_INPUT(input->xmax, input->ymax, 1.0f); + UPDATE_INPUT(input->xmax, input->ymin, 1.0f); + +# undef UPDATE_INPUT + } + } + + newInput.xmin -= margin; + newInput.ymin -= margin; + newInput.xmax += margin; + newInput.ymax += margin; + + operation = getInputOperation(0); + if (operation->determineDependingAreaOfInterest(&newInput, readOperation, output)) { + return true; + } + return false; +#endif +} + +void ScreenLensDistortionOperation::update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) +{ + const MemoryBuffer *input_image = inputs[0]; + for (BuffersIterator<float> it = output->iterate_with({}, area); !it.is_end(); ++it) { + float xy[2] = {(float)it.x, (float)it.y}; + float uv[2]; + get_uv(xy, uv); + const float uv_dot = len_squared_v2(uv); + + float delta[3][2]; + const bool valid_r = get_delta(uv_dot, m_k4[0], uv, delta[0]); + const bool valid_g = get_delta(uv_dot, m_k4[1], uv, delta[1]); + const bool valid_b = get_delta(uv_dot, m_k4[2], uv, delta[2]); + if (!(valid_r && valid_g && valid_b)) { + zero_v4(it.out); + continue; + } + + int count[3] = {0, 0, 0}; + float sum[4] = {0, 0, 0, 0}; + accumulate(input_image, 0, 1, uv_dot, uv, delta, sum, count); + accumulate(input_image, 1, 2, uv_dot, uv, delta, sum, count); + + if (count[0]) { + it.out[0] = 2.0f * sum[0] / (float)count[0]; + } + if (count[1]) { + it.out[1] = 2.0f * sum[1] / (float)count[1]; + } + if (count[2]) { + it.out[2] = 2.0f * sum[2] / (float)count[2]; + } + + /* Set alpha. */ + it.out[3] = 1.0f; + } +} + } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_ScreenLensDistortionOperation.h b/source/blender/compositor/operations/COM_ScreenLensDistortionOperation.h index 98872bfe142..616fc8883b0 100644 --- a/source/blender/compositor/operations/COM_ScreenLensDistortionOperation.h +++ b/source/blender/compositor/operations/COM_ScreenLensDistortionOperation.h @@ -18,14 +18,14 @@ #pragma once -#include "COM_NodeOperation.h" +#include "COM_MultiThreadedOperation.h" #include "DNA_node_types.h" struct RNG; namespace blender::compositor { -class ScreenLensDistortionOperation : public NodeOperation { +class ScreenLensDistortionOperation : public MultiThreadedOperation { private: /** * Cached reference to the inputProgram @@ -50,6 +50,8 @@ class ScreenLensDistortionOperation : public NodeOperation { public: ScreenLensDistortionOperation(); + void init_data() override; + /** * The inner loop of this operation. */ @@ -84,6 +86,11 @@ class ScreenLensDistortionOperation : public NodeOperation { 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_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) override; + private: void determineUV(float result[6], float x, float y) const; void updateVariables(float distortion, float dispersion); @@ -91,7 +98,7 @@ class ScreenLensDistortionOperation : public NodeOperation { void get_uv(const float xy[2], float uv[2]) const; void distort_uv(const float uv[2], float t, float xy[2]) const; bool get_delta(float r_sq, float k4, const float uv[2], float delta[2]) const; - void accumulate(MemoryBuffer *buffer, + void accumulate(const MemoryBuffer *buffer, int a, int b, float r_sq, |