From 090cc8d6e7464182e35916b862e8bd244c3594a5 Mon Sep 17 00:00:00 2001 From: Omar Emara Date: Fri, 13 Oct 2023 14:13:31 +0300 Subject: [PATCH 1/2] Realtime Compositor: Immediately realize wrapped translations This patch changes how wrapped translations are handled by the Realtime Compositor. Previously, translations were always stored on the result and delayed until automatically realized later. The wrapping status was also stored to control this later automatic realization. This patch changes that such that translations are immediately realized for the axes that has enabled wrapping. Consequently, the image will not get translated, but its content will, in a clip on one side, wrap on the opposite side manner. Another change is that wrapping information is no longer propagated to future automatic realizations, so tilling or repeating an image is no longer possible. An alternative method of repetition will be introduced in a later patch. --- .../realtime_compositor/COM_domain.hh | 8 ++-- .../algorithms/COM_algorithm_transform.hh | 13 ++++-- .../algorithms/intern/realize_on_domain.cc | 17 ++++--- .../algorithms/intern/transform.cc | 44 ++++++++++--------- .../composite/nodes/node_composite_rotate.cc | 5 ++- .../composite/nodes/node_composite_scale.cc | 3 +- .../nodes/node_composite_stabilize2d.cc | 5 ++- .../nodes/node_composite_transform.cc | 5 ++- .../nodes/node_composite_translate.cc | 13 +++--- 9 files changed, 67 insertions(+), 46 deletions(-) diff --git a/source/blender/compositor/realtime_compositor/COM_domain.hh b/source/blender/compositor/realtime_compositor/COM_domain.hh index c6a8b62a3d2..656f17f2aa3 100644 --- a/source/blender/compositor/realtime_compositor/COM_domain.hh +++ b/source/blender/compositor/realtime_compositor/COM_domain.hh @@ -23,8 +23,8 @@ enum class Interpolation : uint8_t { * Realization Options * * The options that describe how an input result prefer to be realized on some other domain. This - * is used by the Realize On Domain Operation to identify the appropriate method of realization. - * See the Domain class for more information. */ + * is used by the Realize On Domain and Transform algorithms to identify the appropriate method of + * realization. See the Domain class for more information. */ struct RealizationOptions { /* The interpolation method that should be used when performing realization. Since realizing a * result involves projecting it on a different domain, which in turn, involves sampling the @@ -34,11 +34,11 @@ struct RealizationOptions { /* If true, the result will be repeated infinitely along the horizontal axis when realizing the * result. If false, regions outside of bounds of the result along the horizontal axis will be * filled with zeros. */ - bool repeat_x = false; + bool wrap_x = false; /* If true, the result will be repeated infinitely along the vertical axis when realizing the * result. If false, regions outside of bounds of the result along the vertical axis will be * filled with zeros. */ - bool repeat_y = false; + bool wrap_y = false; }; /* ------------------------------------------------------------------------------------------------ diff --git a/source/blender/compositor/realtime_compositor/algorithms/COM_algorithm_transform.hh b/source/blender/compositor/realtime_compositor/algorithms/COM_algorithm_transform.hh index 67f99e62e0e..db7242de2e1 100644 --- a/source/blender/compositor/realtime_compositor/algorithms/COM_algorithm_transform.hh +++ b/source/blender/compositor/realtime_compositor/algorithms/COM_algorithm_transform.hh @@ -12,8 +12,8 @@ namespace blender::realtime_compositor { -/* Transforms the given result based on the given transformation and interpolation, writing the - * transformed result to the given output. +/* Transforms the given result based on the given transformation and realization options, writing + * the transformed result to the given output. * * The rotation and scale components of the transformation are realized and the size of the result * is increased/reduced to adapt to the new transformation. For instance, if the transformation is @@ -23,11 +23,16 @@ namespace blender::realtime_compositor { * 16k. * * The translation component of the transformation is delayed and only stored in the domain of the - * result to be realized later when needed. */ + * result to be realized later when needed, except if the realization options has wrapping enabled, + * in which case, the result will be translated such that it is clipped on the one side and wrapped + * on the opposite side. + * + * The empty areas around the image after rotation will either be transparent or repetitions of the + * image based on the realization options. */ void transform(Context &context, Result &input, Result &output, float3x3 transformation, - Interpolation interpolation); + RealizationOptions realization_options); } // namespace blender::realtime_compositor diff --git a/source/blender/compositor/realtime_compositor/algorithms/intern/realize_on_domain.cc b/source/blender/compositor/realtime_compositor/algorithms/intern/realize_on_domain.cc index 8e20073850e..e487e55741b 100644 --- a/source/blender/compositor/realtime_compositor/algorithms/intern/realize_on_domain.cc +++ b/source/blender/compositor/realtime_compositor/algorithms/intern/realize_on_domain.cc @@ -62,6 +62,11 @@ void realize_on_domain(Context &context, const float3x3 &input_transformation, const RealizationOptions &realization_options) { + const Domain input_domain = Domain(input.domain().size, input_transformation); + if (input_domain == domain) { + input.pass_through(output); + return; + } GPUShader *shader = context.shader_manager().get( get_realization_shader(input, realization_options)); @@ -87,16 +92,14 @@ void realize_on_domain(Context &context, realization_options.interpolation, Interpolation::Bilinear, Interpolation::Bicubic); GPU_texture_filter_mode(input.texture(), use_bilinear); - /* If the input repeats, set a repeating wrap mode for out-of-bound texture access. Otherwise, + /* If the input wraps, set a repeating wrap mode for out-of-bound texture access. Otherwise, * make out-of-bound texture access return zero by setting a clamp to border extend mode. */ GPU_texture_extend_mode_x(input.texture(), - realization_options.repeat_x ? - GPU_SAMPLER_EXTEND_MODE_REPEAT : - GPU_SAMPLER_EXTEND_MODE_CLAMP_TO_BORDER); + realization_options.wrap_x ? GPU_SAMPLER_EXTEND_MODE_REPEAT : + GPU_SAMPLER_EXTEND_MODE_CLAMP_TO_BORDER); GPU_texture_extend_mode_y(input.texture(), - realization_options.repeat_y ? - GPU_SAMPLER_EXTEND_MODE_REPEAT : - GPU_SAMPLER_EXTEND_MODE_CLAMP_TO_BORDER); + realization_options.wrap_y ? GPU_SAMPLER_EXTEND_MODE_REPEAT : + GPU_SAMPLER_EXTEND_MODE_CLAMP_TO_BORDER); input.bind_as_texture(shader, "input_tx"); diff --git a/source/blender/compositor/realtime_compositor/algorithms/intern/transform.cc b/source/blender/compositor/realtime_compositor/algorithms/intern/transform.cc index 1fc90c8f40a..55f0474254d 100644 --- a/source/blender/compositor/realtime_compositor/algorithms/intern/transform.cc +++ b/source/blender/compositor/realtime_compositor/algorithms/intern/transform.cc @@ -56,33 +56,35 @@ void transform(Context &context, Result &input, Result &output, float3x3 transformation, - Interpolation interpolation) + RealizationOptions realization_options) { - math::AngleRadian rotation; - float2 translation, scale; - math::to_loc_rot_scale(transformation, translation, rotation, scale); + /* If we are wrapping, the input is translated but the target domain remains fixed, which results + * in the input clipping on one side and wrapping on the opposite side. This mask vector can be + * multiplied to the translation component of the transformation to remove it. */ + const float2 wrap_mask = float2(realization_options.wrap_x ? 0.0f : 1.0f, + realization_options.wrap_y ? 0.0f : 1.0f); - /* Rotation and scale transformations are immediately realized. */ - if (rotation != 0.0f || scale != float2(1.0f)) { - RealizationOptions realization_options = input.get_realization_options(); - realization_options.interpolation = interpolation; + /* Compute a transformed input domain, excluding translations of wrapped axes. */ + Domain input_domain = input.domain(); + float3x3 domain_transformation = transformation; + domain_transformation.location() *= wrap_mask; + input_domain.transform(domain_transformation); - Domain input_domain = input.domain(); - input_domain.transform(transformation); + /* Realize the input on the target domain using the full transformation. */ + const Domain target_domain = compute_realized_transformation_domain(input_domain); + realize_on_domain(context, + input, + output, + target_domain, + transformation * input.domain().transformation, + realization_options); - const Domain target_domain = compute_realized_transformation_domain(input_domain); - - realize_on_domain( - context, input, output, target_domain, input_domain.transformation, realization_options); - } - else { - input.pass_through(output); - } - - /* Translation transformations are delayed and are only stored in the result. */ + /* Translate the result, but only if it doesn't wrap as explained before. */ + const float2 translation = transformation.location() * wrap_mask; const float3x3 translation_matrix = math::from_location(translation); output.transform(translation_matrix); - output.get_realization_options().interpolation = interpolation; + + output.get_realization_options().interpolation = realization_options.interpolation; } } // namespace blender::realtime_compositor diff --git a/source/blender/nodes/composite/nodes/node_composite_rotate.cc b/source/blender/nodes/composite/nodes/node_composite_rotate.cc index 974453fbb5f..f6085295e46 100644 --- a/source/blender/nodes/composite/nodes/node_composite_rotate.cc +++ b/source/blender/nodes/composite/nodes/node_composite_rotate.cc @@ -60,7 +60,10 @@ class RotateOperation : public NodeOperation { const math::AngleRadian rotation = get_input("Degr").get_float_value_default(0.0f); const float3x3 transformation = math::from_rotation(rotation); - transform(context(), input, output, transformation, get_interpolation()); + RealizationOptions realization_options = input.get_realization_options(); + realization_options.interpolation = get_interpolation(); + + transform(context(), input, output, transformation, realization_options); } Interpolation get_interpolation() diff --git a/source/blender/nodes/composite/nodes/node_composite_scale.cc b/source/blender/nodes/composite/nodes/node_composite_scale.cc index 5d892b9662a..e93e30d7276 100644 --- a/source/blender/nodes/composite/nodes/node_composite_scale.cc +++ b/source/blender/nodes/composite/nodes/node_composite_scale.cc @@ -93,8 +93,7 @@ class ScaleOperation : public NodeOperation { const float3x3 transformation = math::from_loc_rot_scale( translation, rotation, scale); - const Interpolation interpolation = input.get_realization_options().interpolation; - transform(context(), input, output, transformation, interpolation); + transform(context(), input, output, transformation, input.get_realization_options()); } float2 get_scale() diff --git a/source/blender/nodes/composite/nodes/node_composite_stabilize2d.cc b/source/blender/nodes/composite/nodes/node_composite_stabilize2d.cc index 960906a94ff..409736a7110 100644 --- a/source/blender/nodes/composite/nodes/node_composite_stabilize2d.cc +++ b/source/blender/nodes/composite/nodes/node_composite_stabilize2d.cc @@ -108,7 +108,10 @@ class Stabilize2DOperation : public NodeOperation { transformation = math::invert(transformation); } - transform(context(), input, output, transformation, get_interpolation()); + RealizationOptions realization_options = input.get_realization_options(); + realization_options.interpolation = get_interpolation(); + + transform(context(), input, output, transformation, realization_options); } Interpolation get_interpolation() diff --git a/source/blender/nodes/composite/nodes/node_composite_transform.cc b/source/blender/nodes/composite/nodes/node_composite_transform.cc index c767e660dfb..11628ad4fa7 100644 --- a/source/blender/nodes/composite/nodes/node_composite_transform.cc +++ b/source/blender/nodes/composite/nodes/node_composite_transform.cc @@ -75,7 +75,10 @@ class TransformOperation : public NodeOperation { const float3x3 transformation = math::from_loc_rot_scale( translation, rotation, scale); - transform(context(), input, output, transformation, get_interpolation()); + RealizationOptions realization_options = input.get_realization_options(); + realization_options.interpolation = get_interpolation(); + + transform(context(), input, output, transformation, realization_options); } Interpolation get_interpolation() diff --git a/source/blender/nodes/composite/nodes/node_composite_translate.cc b/source/blender/nodes/composite/nodes/node_composite_translate.cc index 4fe5ba9cd26..d78cd647157 100644 --- a/source/blender/nodes/composite/nodes/node_composite_translate.cc +++ b/source/blender/nodes/composite/nodes/node_composite_translate.cc @@ -11,6 +11,7 @@ #include "UI_interface.hh" #include "UI_resources.hh" +#include "COM_algorithm_transform.hh" #include "COM_node_operation.hh" #include "node_composite_util.hh" @@ -73,9 +74,11 @@ class TranslateOperation : public NodeOperation { const float2 translation = float2(x, y); const float3x3 transformation = math::from_location(translation); - result.transform(transformation); - result.get_realization_options().repeat_x = get_repeat_x(); - result.get_realization_options().repeat_y = get_repeat_y(); + RealizationOptions realization_options = input.get_realization_options(); + realization_options.wrap_x = get_wrap_x(); + realization_options.wrap_y = get_wrap_y(); + + transform(context(), input, result, transformation, realization_options); } bool get_use_relative() @@ -83,12 +86,12 @@ class TranslateOperation : public NodeOperation { return node_storage(bnode()).relative; } - bool get_repeat_x() + bool get_wrap_x() { return ELEM(node_storage(bnode()).wrap_axis, CMP_NODE_WRAP_X, CMP_NODE_WRAP_XY); } - bool get_repeat_y() + bool get_wrap_y() { return ELEM(node_storage(bnode()).wrap_axis, CMP_NODE_WRAP_Y, CMP_NODE_WRAP_XY); } -- 2.30.2 From 76fe32de870f3899cf1949978966177d18dfd801 Mon Sep 17 00:00:00 2001 From: Omar Emara Date: Thu, 2 Nov 2023 11:41:03 +0200 Subject: [PATCH 2/2] Fix double translations --- source/blender/compositor/realtime_compositor/COM_result.hh | 3 +++ .../algorithms/intern/realize_on_domain.cc | 1 + .../realtime_compositor/algorithms/intern/transform.cc | 5 ----- .../blender/compositor/realtime_compositor/intern/result.cc | 5 +++++ 4 files changed, 9 insertions(+), 5 deletions(-) diff --git a/source/blender/compositor/realtime_compositor/COM_result.hh b/source/blender/compositor/realtime_compositor/COM_result.hh index 067967a25fb..3986998a792 100644 --- a/source/blender/compositor/realtime_compositor/COM_result.hh +++ b/source/blender/compositor/realtime_compositor/COM_result.hh @@ -198,6 +198,9 @@ class Result { * a practical example of use. */ void steal_data(Result &source); + /* Sets the transformation of the domain of the result to the given transformation. */ + void set_transformation(const float3x3 &transformation); + /* Transform the result by the given transformation. This effectively pre-multiply the given * transformation by the current transformation of the domain of the result. */ void transform(const float3x3 &transformation); diff --git a/source/blender/compositor/realtime_compositor/algorithms/intern/realize_on_domain.cc b/source/blender/compositor/realtime_compositor/algorithms/intern/realize_on_domain.cc index e487e55741b..5948fd8573a 100644 --- a/source/blender/compositor/realtime_compositor/algorithms/intern/realize_on_domain.cc +++ b/source/blender/compositor/realtime_compositor/algorithms/intern/realize_on_domain.cc @@ -65,6 +65,7 @@ void realize_on_domain(Context &context, const Domain input_domain = Domain(input.domain().size, input_transformation); if (input_domain == domain) { input.pass_through(output); + output.set_transformation(domain.transformation); return; } diff --git a/source/blender/compositor/realtime_compositor/algorithms/intern/transform.cc b/source/blender/compositor/realtime_compositor/algorithms/intern/transform.cc index 29ff56feb05..1224528060d 100644 --- a/source/blender/compositor/realtime_compositor/algorithms/intern/transform.cc +++ b/source/blender/compositor/realtime_compositor/algorithms/intern/transform.cc @@ -79,11 +79,6 @@ void transform(Context &context, transformation * input.domain().transformation, realization_options); - /* Translate the result, but only if it doesn't wrap as explained before. */ - // const float2 translation = transformation.location() * wrap_mask; - // const float3x3 translation_matrix = math::from_location(translation); - // output.transform(translation_matrix); - output.get_realization_options().interpolation = realization_options.interpolation; } diff --git a/source/blender/compositor/realtime_compositor/intern/result.cc b/source/blender/compositor/realtime_compositor/intern/result.cc index 267fd5d4af5..f175d2633a2 100644 --- a/source/blender/compositor/realtime_compositor/intern/result.cc +++ b/source/blender/compositor/realtime_compositor/intern/result.cc @@ -179,6 +179,11 @@ void Result::steal_data(Result &source) source.texture_pool_ = nullptr; } +void Result::set_transformation(const float3x3 &transformation) +{ + domain_.transformation = transformation; +} + void Result::transform(const float3x3 &transformation) { domain_.transform(transformation); -- 2.30.2