From 92be913a2a79bbf1c2fdbb64dd71d613631dd15f Mon Sep 17 00:00:00 2001 From: Omar Emara Date: Wed, 24 Sep 2025 13:33:24 +0200 Subject: [PATCH] Compositor: Remove XY scale from Displace node This patch removes the XY scale inputs from the Displace node. The inputs were redundant since they were just multiplied by the vector. This simplifies the node and improves performance slightly. Additionally, the Vector input was renamed to Displacement since it no longer specifies a direction. Pull Request: https://projects.blender.org/blender/blender/pulls/146356 --- .../blender/blenkernel/BKE_blender_version.h | 2 +- .../blenloader/intern/versioning_500.cc | 100 ++++++++++ source/blender/compositor/COM_result.hh | 5 +- .../shaders/compositor_displace.glsl | 3 +- .../compositor_displace_anisotropic.glsl | 3 +- .../shaders/infos/compositor_displace_info.hh | 2 - .../nodes/node_composite_displace.cc | 172 ++++++------------ .../denoise/cycles_renders/denoise_passes.png | 4 +- 8 files changed, 161 insertions(+), 130 deletions(-) diff --git a/source/blender/blenkernel/BKE_blender_version.h b/source/blender/blenkernel/BKE_blender_version.h index c11c7585203..2695dafb55a 100644 --- a/source/blender/blenkernel/BKE_blender_version.h +++ b/source/blender/blenkernel/BKE_blender_version.h @@ -27,7 +27,7 @@ /* Blender file format version. */ #define BLENDER_FILE_VERSION BLENDER_VERSION -#define BLENDER_FILE_SUBVERSION 87 +#define BLENDER_FILE_SUBVERSION 88 /* Minimum Blender version that supports reading file written with the current * version. Older Blender versions will test this and cancel loading the file, showing a warning to diff --git a/source/blender/blenloader/intern/versioning_500.cc b/source/blender/blenloader/intern/versioning_500.cc index dae9a5977c5..a01503d7fb0 100644 --- a/source/blender/blenloader/intern/versioning_500.cc +++ b/source/blender/blenloader/intern/versioning_500.cc @@ -2281,6 +2281,91 @@ static void version_dynamic_viewer_node_items(bNodeTree &ntree) } } +static void do_version_displace_node_remove_xy_scale(bNodeTree &node_tree, bNode &node) +{ + blender::bke::node_tree_set_type(node_tree); + + bNodeSocket *displacement_input = blender::bke::node_find_socket(node, SOCK_IN, "Displacement"); + bNodeSocket *x_scale_input = blender::bke::node_find_socket(node, SOCK_IN, "X Scale"); + bNodeSocket *y_scale_input = blender::bke::node_find_socket(node, SOCK_IN, "Y Scale"); + + /* Find the link going into the inputs of the node. */ + bNodeLink *displacement_link = nullptr; + bNodeLink *x_scale_link = nullptr; + bNodeLink *y_scale_link = nullptr; + LISTBASE_FOREACH (bNodeLink *, link, &node_tree.links) { + if (link->tosock == displacement_input) { + displacement_link = link; + } + if (link->tosock == x_scale_input) { + x_scale_link = link; + } + if (link->tosock == y_scale_input) { + y_scale_link = link; + } + } + + bNode *multiply_node = blender::bke::node_add_node(nullptr, node_tree, "ShaderNodeVectorMath"); + multiply_node->parent = node.parent; + multiply_node->location[0] = node.location[0] - node.width - 20.0f; + multiply_node->location[1] = node.location[1]; + multiply_node->custom1 = NODE_VECTOR_MATH_MULTIPLY; + + bNodeSocket *multiply_a_input = blender::bke::node_find_socket( + *multiply_node, SOCK_IN, "Vector"); + bNodeSocket *multiply_b_input = blender::bke::node_find_socket( + *multiply_node, SOCK_IN, "Vector_001"); + bNodeSocket *multiply_output = blender::bke::node_find_socket( + *multiply_node, SOCK_OUT, "Vector"); + + copy_v2_v2(multiply_a_input->default_value_typed()->value, + displacement_input->default_value_typed()->value); + if (displacement_link) { + version_node_add_link(node_tree, + *displacement_link->fromnode, + *displacement_link->fromsock, + *multiply_node, + *multiply_a_input); + blender::bke::node_remove_link(&node_tree, *displacement_link); + } + + version_node_add_link(node_tree, *multiply_node, *multiply_output, node, *displacement_input); + + bNode *combine_node = blender::bke::node_add_node(nullptr, node_tree, "ShaderNodeCombineXYZ"); + combine_node->parent = node.parent; + combine_node->location[0] = multiply_node->location[0] - multiply_node->width - 20.0f; + combine_node->location[1] = multiply_node->location[1]; + + bNodeSocket *combine_x_input = blender::bke::node_find_socket(*combine_node, SOCK_IN, "X"); + bNodeSocket *combine_y_input = blender::bke::node_find_socket(*combine_node, SOCK_IN, "Y"); + bNodeSocket *combine_output = blender::bke::node_find_socket(*combine_node, SOCK_OUT, "Vector"); + + version_node_add_link( + node_tree, *combine_node, *combine_output, *multiply_node, *multiply_b_input); + + combine_x_input->default_value_typed()->value = + x_scale_input->default_value_typed()->value; + if (x_scale_link) { + version_node_add_link(node_tree, + *x_scale_link->fromnode, + *x_scale_link->fromsock, + *combine_node, + *combine_x_input); + blender::bke::node_remove_link(&node_tree, *x_scale_link); + } + + combine_y_input->default_value_typed()->value = + y_scale_input->default_value_typed()->value; + if (y_scale_link) { + version_node_add_link(node_tree, + *y_scale_link->fromnode, + *y_scale_link->fromsock, + *combine_node, + *combine_y_input); + blender::bke::node_remove_link(&node_tree, *y_scale_link); + } +} + void do_versions_after_linking_500(FileData *fd, Main *bmain) { if (!MAIN_VERSION_FILE_ATLEAST(bmain, 500, 9)) { @@ -3414,6 +3499,21 @@ void blo_do_versions_500(FileData *fd, Library * /*lib*/, Main *bmain) FOREACH_NODETREE_END; } + if (!MAIN_VERSION_FILE_ATLEAST(bmain, 500, 88)) { + FOREACH_NODETREE_BEGIN (bmain, node_tree, id) { + if (node_tree->type != NTREE_COMPOSIT) { + continue; + } + version_node_input_socket_name(node_tree, CMP_NODE_DISPLACE, "Vector", "Displacement"); + LISTBASE_FOREACH (bNode *, node, &node_tree->nodes) { + if (node->type_legacy == CMP_NODE_DISPLACE) { + do_version_displace_node_remove_xy_scale(*node_tree, *node); + } + } + } + FOREACH_NODETREE_END; + } + /** * Always bump subversion in BKE_blender_version.h when adding versioning * code here, and wrap it inside a MAIN_VERSION_FILE_ATLEAST check. diff --git a/source/blender/compositor/COM_result.hh b/source/blender/compositor/COM_result.hh index feee4ee640b..c6a985b76a5 100644 --- a/source/blender/compositor/COM_result.hh +++ b/source/blender/compositor/COM_result.hh @@ -688,11 +688,8 @@ BLI_INLINE_METHOD float4 Result::sample(const float2 &coordinates, extension_mode_x, extension_mode_y); break; - /* The anisotropic sampling requires separate handling with EWA. */ - case Interpolation::Anisotropic: - BLI_assert_unreachable(); - break; case Interpolation::Bicubic: + case Interpolation::Anisotropic: math::interpolate_cubic_bspline_wrapmode_fl(buffer, pixel_value, size.x, diff --git a/source/blender/compositor/shaders/compositor_displace.glsl b/source/blender/compositor/shaders/compositor_displace.glsl index 1a252cb46cc..3a714f9be00 100644 --- a/source/blender/compositor/shaders/compositor_displace.glsl +++ b/source/blender/compositor/shaders/compositor_displace.glsl @@ -16,8 +16,7 @@ void main() /* Note that the input displacement is in pixel space, so divide by the input size to transform * it into the normalized sampler space. */ - float2 scale = float2(texture_load(x_scale_tx, texel).x, texture_load(y_scale_tx, texel).x); - float2 displacement = texture_load(displacement_tx, texel).xy * scale / float2(input_size); + float2 displacement = texture_load(displacement_tx, texel).xy / float2(input_size); float2 displaced_coordinates = coordinates - displacement; imageStore(output_img, texel, SAMPLER_FUNCTION(input_tx, displaced_coordinates)); diff --git a/source/blender/compositor/shaders/compositor_displace_anisotropic.glsl b/source/blender/compositor/shaders/compositor_displace_anisotropic.glsl index b3bd9f8c9e7..f59592e7989 100644 --- a/source/blender/compositor/shaders/compositor_displace_anisotropic.glsl +++ b/source/blender/compositor/shaders/compositor_displace_anisotropic.glsl @@ -20,8 +20,7 @@ void main() /* Note that the input displacement is in pixel space, so divide by the input size to transform * it into the normalized sampler space. */ - float2 scale = float2(texture_load(x_scale_tx, texel).x, texture_load(y_scale_tx, texel).x); - float2 displacement = texture_load(displacement_tx, texel).xy * scale / float2(input_size); + float2 displacement = texture_load(displacement_tx, texel).xy / float2(input_size); float2 displaced_coordinates = coordinates - displacement; /* Store the displaced coordinates into the shared table and issue a barrier to later compute the diff --git a/source/blender/compositor/shaders/infos/compositor_displace_info.hh b/source/blender/compositor/shaders/infos/compositor_displace_info.hh index e7ded4ed916..c0326abcfac 100644 --- a/source/blender/compositor/shaders/infos/compositor_displace_info.hh +++ b/source/blender/compositor/shaders/infos/compositor_displace_info.hh @@ -8,8 +8,6 @@ GPU_SHADER_CREATE_INFO(compositor_displace_shared) LOCAL_GROUP_SIZE(16, 16) SAMPLER(0, sampler2D, input_tx) SAMPLER(1, sampler2D, displacement_tx) -SAMPLER(2, sampler2D, x_scale_tx) -SAMPLER(3, sampler2D, y_scale_tx) IMAGE(0, SFLOAT_16_16_16_16, write, image2D, output_img) GPU_SHADER_CREATE_END() diff --git a/source/blender/nodes/composite/nodes/node_composite_displace.cc b/source/blender/nodes/composite/nodes/node_composite_displace.cc index c14b45597c0..2722d3286e4 100644 --- a/source/blender/nodes/composite/nodes/node_composite_displace.cc +++ b/source/blender/nodes/composite/nodes/node_composite_displace.cc @@ -2,13 +2,9 @@ * * SPDX-License-Identifier: GPL-2.0-or-later */ -/** \file - * \ingroup cmpnodes - */ - #include "MEM_guardedalloc.h" -#include "BLI_assert.h" +#include "BLI_math_matrix.hh" #include "BLI_math_vector.hh" #include "BLI_math_vector_types.hh" #include "BLI_utildefines.h" @@ -39,22 +35,9 @@ static void cmp_node_displace_declare(NodeDeclarationBuilder &b) b.add_input("Image") .default_value({1.0f, 1.0f, 1.0f, 1.0f}) .structure_type(StructureType::Dynamic); - b.add_input("Vector") + b.add_input("Displacement") .dimensions(2) - .default_value({1.0f, 1.0f}) - .min(0.0f) - .max(1.0f) - .subtype(PROP_TRANSLATION) - .structure_type(StructureType::Dynamic); - b.add_input("X Scale") - .default_value(0.0f) - .min(-1000.0f) - .max(1000.0f) - .structure_type(StructureType::Dynamic); - b.add_input("Y Scale") - .default_value(0.0f) - .min(-1000.0f) - .max(1000.0f) + .default_value({0.0f, 0.0f}) .structure_type(StructureType::Dynamic); PanelDeclarationBuilder &sampling_panel = b.add_panel("Sampling").default_closed(true); @@ -87,13 +70,23 @@ class DisplaceOperation : public NodeOperation { void execute() override { - if (this->is_identity()) { - const Result &input = this->get_input("Image"); - Result &output = this->get_result("Image"); + const Result &input = this->get_input("Image"); + Result &output = this->get_result("Image"); + if (input.is_single_value()) { output.share_data(input); return; } + const Result &displacement = this->get_input("Displacement"); + if (displacement.is_single_value()) { + output.share_data(input); + output.transform(math::from_location(displacement.get_single_value())); + output.get_realization_options().interpolation = this->get_interpolation(); + output.get_realization_options().extension_x = this->get_extension_mode_x(); + output.get_realization_options().extension_y = this->get_extension_mode_y(); + return; + } + if (this->context().use_gpu()) { this->execute_gpu(); } @@ -105,12 +98,10 @@ class DisplaceOperation : public NodeOperation { void execute_gpu() { const Interpolation interpolation = this->get_interpolation(); - const ExtensionMode extension_x = this->get_extension_mode_x(); - const ExtensionMode extension_y = this->get_extension_mode_y(); - gpu::Shader *shader = context().get_shader(this->get_shader_name(interpolation)); + gpu::Shader *shader = this->context().get_shader(this->get_shader_name(interpolation)); GPU_shader_bind(shader); - const Result &input_image = get_input("Image"); + const Result &input_image = this->get_input("Image"); if (interpolation == Interpolation::Anisotropic) { GPU_texture_anisotropic_filter(input_image, true); GPU_texture_mipmap_mode(input_image, true, true); @@ -120,61 +111,49 @@ class DisplaceOperation : public NodeOperation { interpolation, Interpolation::Bilinear, Interpolation::Bicubic); GPU_texture_filter_mode(input_image, use_bilinear); } + + const ExtensionMode extension_x = this->get_extension_mode_x(); + const ExtensionMode extension_y = this->get_extension_mode_y(); GPU_texture_extend_mode_x(input_image, map_extension_mode_to_extend_mode(extension_x)); GPU_texture_extend_mode_y(input_image, map_extension_mode_to_extend_mode(extension_y)); input_image.bind_as_texture(shader, "input_tx"); - const Result &input_displacement = get_input("Vector"); - input_displacement.bind_as_texture(shader, "displacement_tx"); - const Result &input_x_scale = get_input("X Scale"); - input_x_scale.bind_as_texture(shader, "x_scale_tx"); - const Result &input_y_scale = get_input("Y Scale"); - input_y_scale.bind_as_texture(shader, "y_scale_tx"); + const Result &displacement = this->get_input("Displacement"); + displacement.bind_as_texture(shader, "displacement_tx"); - const Domain domain = compute_domain(); - Result &output_image = get_result("Image"); + const Domain domain = this->compute_domain(); + Result &output_image = this->get_result("Image"); output_image.allocate_texture(domain); output_image.bind_as_image(shader, "output_img"); compute_dispatch_threads_at_least(shader, domain.size); input_image.unbind_as_texture(); - input_displacement.unbind_as_texture(); - input_x_scale.unbind_as_texture(); - input_y_scale.unbind_as_texture(); + displacement.unbind_as_texture(); output_image.unbind_as_image(); GPU_shader_unbind(); } void execute_cpu() { - const Result &image = get_input("Image"); - const Result &input_displacement = get_input("Vector"); - const Result &x_scale = get_input("X Scale"); - const Result &y_scale = get_input("Y Scale"); + const Result &image = this->get_input("Image"); + const Result &displacement = this->get_input("Displacement"); const Interpolation interpolation = this->get_interpolation(); const ExtensionMode extension_x = this->get_extension_mode_x(); const ExtensionMode extension_y = this->get_extension_mode_y(); - const Domain domain = compute_domain(); - Result &output = get_result("Image"); + const Domain domain = this->compute_domain(); + Result &output = this->get_result("Image"); output.allocate_texture(domain); const int2 size = domain.size; if (interpolation == Interpolation::Anisotropic) { - this->compute_anisotropic(size, image, output, input_displacement, x_scale, y_scale); + this->compute_anisotropic(size, image, output, displacement); } else { - this->compute_interpolation(interpolation, - size, - image, - output, - input_displacement, - x_scale, - y_scale, - extension_x, - extension_y); + this->compute_interpolation( + interpolation, size, image, output, displacement, extension_x, extension_y); } } @@ -182,15 +161,12 @@ class DisplaceOperation : public NodeOperation { const int2 &size, const Result &image, Result &output, - const Result &input_displacement, - const Result &x_scale, - const Result &y_scale, + const Result &displacement, const ExtensionMode &extension_mode_x, const ExtensionMode &extension_mode_y) const { parallel_for(size, [&](const int2 base_texel) { - const float2 coordinates = compute_coordinates( - base_texel, size, input_displacement, x_scale, y_scale); + const float2 coordinates = this->compute_coordinates(base_texel, size, displacement); output.store_pixel( base_texel, image.sample(coordinates, interpolation, extension_mode_x, extension_mode_y)); @@ -207,18 +183,8 @@ class DisplaceOperation : public NodeOperation { void compute_anisotropic(const int2 &size, const Result &image, Result &output, - const Result &input_displacement, - const Result &x_scale, - const Result &y_scale) const + const Result &displacement) const { - auto compute_anisotropic_pixel = [&](const int2 &texel, - const float2 &coordinates, - const float2 &x_gradient, - const float2 &y_gradient) { - /* Sample the input using the displaced coordinates passing in the computed gradients in - * order to utilize the anisotropic filtering capabilities of the sampler. */ - output.store_pixel(texel, image.sample_ewa_zero(coordinates, x_gradient, y_gradient)); - }; parallel_for(math::divide_ceil(size, int2(2)), [&](const int2 base_texel) { /* Compute each of the pixels in the 2x2 block, making sure to exempt out of bounds right * and upper pixels. */ @@ -230,14 +196,14 @@ class DisplaceOperation : public NodeOperation { const int2 upper_left_texel = int2(x, y + 1); const int2 upper_right_texel = int2(x + 1, y + 1); - const float2 lower_left_coordinates = compute_coordinates( - lower_left_texel, size, input_displacement, x_scale, y_scale); - const float2 lower_right_coordinates = compute_coordinates( - lower_right_texel, size, input_displacement, x_scale, y_scale); - const float2 upper_left_coordinates = compute_coordinates( - upper_left_texel, size, input_displacement, x_scale, y_scale); - const float2 upper_right_coordinates = compute_coordinates( - upper_right_texel, size, input_displacement, x_scale, y_scale); + const float2 lower_left_coordinates = this->compute_coordinates( + lower_left_texel, size, displacement); + const float2 lower_right_coordinates = this->compute_coordinates( + lower_right_texel, size, displacement); + const float2 upper_left_coordinates = this->compute_coordinates( + upper_left_texel, size, displacement); + const float2 upper_right_coordinates = this->compute_coordinates( + upper_right_texel, size, displacement); /* Compute the partial derivatives using finite difference. Divide by the input size since * sample_ewa_zero assumes derivatives with respect to texel coordinates. */ @@ -247,6 +213,14 @@ class DisplaceOperation : public NodeOperation { const float2 upper_x_gradient = (upper_right_coordinates - upper_left_coordinates) / size.x; /* Computes one of the 2x2 pixels given its texel location, coordinates, and gradients. */ + auto compute_anisotropic_pixel = [&](const int2 &texel, + const float2 &coordinates, + const float2 &x_gradient, + const float2 &y_gradient) { + /* Sample the input using the displaced coordinates passing in the computed gradients in + * order to utilize the anisotropic filtering capabilities of the sampler. */ + output.store_pixel(texel, image.sample_ewa_zero(coordinates, x_gradient, y_gradient)); + }; compute_anisotropic_pixel( lower_left_texel, lower_left_coordinates, lower_x_gradient, left_y_gradient); @@ -265,23 +239,12 @@ class DisplaceOperation : public NodeOperation { }); } - float2 compute_coordinates(const int2 &texel, - const int2 &size, - const Result &input_displacement, - const Result &x_scale, - const Result &y_scale) const + float2 compute_coordinates(const int2 &texel, const int2 &size, const Result &displacement) const { - /* Add 0.5 to evaluate the sampler at the center of the pixel and divide by the image - * size to get the coordinates into the sampler's expected [0, 1] range. */ - float2 coordinates = (float2(texel) + float2(0.5f)) / float2(size); - /* Note that the input displacement is in pixel space, so divide by the input size to * transform it into the normalized sampler space. */ - float2 scale = float2(x_scale.load_pixel_extended(texel), - y_scale.load_pixel_extended(texel)); - float2 displacement = input_displacement.load_pixel_extended(texel) * scale / - float2(size); - return coordinates - displacement; + const float2 coordinates = (float2(texel) + float2(0.5f)) / float2(size); + return coordinates - displacement.load_pixel_extended(texel) / float2(size); } const char *get_shader_name(const Interpolation &interpolation) const @@ -354,31 +317,6 @@ class DisplaceOperation : public NodeOperation { return ExtensionMode::Clip; } - - bool is_identity() - { - const Result &input_image = get_input("Image"); - if (input_image.is_single_value()) { - return true; - } - - const Result &input_displacement = get_input("Vector"); - if (input_displacement.is_single_value() && - math::is_zero(input_displacement.get_single_value())) - { - return true; - } - - const Result &input_x_scale = get_input("X Scale"); - const Result &input_y_scale = get_input("Y Scale"); - if (input_x_scale.is_single_value() && input_x_scale.get_single_value() == 0.0f && - input_y_scale.is_single_value() && input_y_scale.get_single_value() == 0.0f) - { - return true; - } - - return false; - } }; static NodeOperation *get_compositor_operation(Context &context, DNode node) diff --git a/tests/files/render/denoise/cycles_renders/denoise_passes.png b/tests/files/render/denoise/cycles_renders/denoise_passes.png index d28104a6d05..c22a5956a18 100644 --- a/tests/files/render/denoise/cycles_renders/denoise_passes.png +++ b/tests/files/render/denoise/cycles_renders/denoise_passes.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f33286b1d76fc742c2ea8e1599284cafd7368e8af40d480a554bc268bed15344 -size 7849 +oid sha256:11a35efe1fde356429ff5844010ff149e2f1559de4d272d260af3752dda12d4e +size 9489