Blender V4.3
van_vliet_gaussian_blur.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2024 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
5#include "BLI_assert.h"
6#include "BLI_math_base.hh"
7#include "BLI_math_vector.hh"
8
9#include "GPU_shader.hh"
10
11#include "COM_context.hh"
12#include "COM_result.hh"
13#include "COM_utilities.hh"
14
17
19
20/* Sum all four of the causal and non causal outputs of the first and second filters and write the
21 * sum to the output. This is because the Van Vliet filter is implemented as a bank of 2 parallel
22 * second order filters, meaning its output is the sum of the causal and non causal filters of both
23 * filters. The output is expected not to be allocated as it will be allocated internally.
24 *
25 * The output is allocated and written transposed, that is, with a height equivalent to the width
26 * of the input and vice versa. This is done as a performance optimization. The blur pass will
27 * blur the image horizontally and write it to the intermediate output transposed. Then the
28 * vertical pass will execute the same horizontal blur shader, but since its input is transposed,
29 * it will effectively do a vertical blur and write to the output transposed, effectively undoing
30 * the transposition in the horizontal pass. This is done to improve spatial cache locality in the
31 * shader and to avoid having two separate shaders for each blur pass. */
33 Result &first_causal_input,
34 Result &first_non_causal_input,
35 Result &second_causal_input,
36 Result &second_non_causal_input,
37 Result &output)
38{
39 GPUShader *shader = context.get_shader("compositor_van_vliet_gaussian_blur_sum");
40 GPU_shader_bind(shader);
41
42 first_causal_input.bind_as_texture(shader, "first_causal_input_tx");
43 first_non_causal_input.bind_as_texture(shader, "first_non_causal_input_tx");
44 second_causal_input.bind_as_texture(shader, "second_causal_input_tx");
45 second_non_causal_input.bind_as_texture(shader, "second_non_causal_input_tx");
46
47 const Domain domain = first_causal_input.domain();
48 const int2 transposed_domain = int2(domain.size.y, domain.size.x);
49 output.allocate_texture(transposed_domain);
50 output.bind_as_image(shader, "output_img");
51
52 compute_dispatch_threads_at_least(shader, domain.size);
53
55 first_causal_input.unbind_as_texture();
56 first_non_causal_input.unbind_as_texture();
57 second_causal_input.unbind_as_texture();
58 second_non_causal_input.unbind_as_texture();
59 output.unbind_as_image();
60}
61
62static void blur_pass(Context &context, Result &input, Result &output, float sigma)
63{
64 GPUShader *shader = context.get_shader("compositor_van_vliet_gaussian_blur");
65 GPU_shader_bind(shader);
66
67 const VanVlietGaussianCoefficients &coefficients =
68 context.cache_manager().van_vliet_gaussian_coefficients.get(context, sigma);
69
71 shader, "first_feedback_coefficients", float2(coefficients.first_feedback_coefficients()));
73 "first_causal_feedforward_coefficients",
76 "first_non_causal_feedforward_coefficients",
79 shader, "second_feedback_coefficients", float2(coefficients.second_feedback_coefficients()));
81 "second_causal_feedforward_coefficients",
84 "second_non_causal_feedforward_coefficients",
87 "first_causal_boundary_coefficient",
88 float(coefficients.first_causal_boundary_coefficient()));
90 "first_non_causal_boundary_coefficient",
91 float(coefficients.first_non_causal_boundary_coefficient()));
93 "second_causal_boundary_coefficient",
94 float(coefficients.second_causal_boundary_coefficient()));
96 "second_non_causal_boundary_coefficient",
97 float(coefficients.second_non_causal_boundary_coefficient()));
98
99 input.bind_as_texture(shader, "input_tx");
100
101 const Domain domain = input.domain();
102
103 Result first_causal_result = context.create_result(ResultType::Color);
104 first_causal_result.allocate_texture(domain);
105 first_causal_result.bind_as_image(shader, "first_causal_output_img");
106
107 Result first_non_causal_result = context.create_result(ResultType::Color);
108 first_non_causal_result.allocate_texture(domain);
109 first_non_causal_result.bind_as_image(shader, "first_non_causal_output_img");
110
111 Result second_causal_result = context.create_result(ResultType::Color);
112 second_causal_result.allocate_texture(domain);
113 second_causal_result.bind_as_image(shader, "second_causal_output_img");
114
115 Result second_non_causal_result = context.create_result(ResultType::Color);
116 second_non_causal_result.allocate_texture(domain);
117 second_non_causal_result.bind_as_image(shader, "second_non_causal_output_img");
118
119 /* The second dispatch dimension is 4 dispatches, one for the first causal filter, one for the
120 * first non causal filter, one for the second causal filter, and one for the second non causal
121 * filter. */
122 compute_dispatch_threads_at_least(shader, int2(domain.size.y, 4), int2(64, 4));
123
125 input.unbind_as_texture();
126 first_causal_result.unbind_as_image();
127 first_non_causal_result.unbind_as_image();
128 second_causal_result.unbind_as_image();
129 second_non_causal_result.unbind_as_image();
130
132 first_causal_result,
133 first_non_causal_result,
134 second_causal_result,
135 second_non_causal_result,
136 output);
137 first_causal_result.release();
138 first_non_causal_result.release();
139 second_causal_result.release();
140 second_non_causal_result.release();
141}
142
143void van_vliet_gaussian_blur(Context &context, Result &input, Result &output, float2 sigma)
144{
145 BLI_assert_msg(math::reduce_max(sigma) >= 32.0f,
146 "Van Vliet filter is less accurate for sigma values less than 32. Use Deriche "
147 "filter instead or direct convolution instead.");
148
149 Result horizontal_pass_result = context.create_result(ResultType::Color);
150 blur_pass(context, input, horizontal_pass_result, sigma.x);
151 blur_pass(context, horizontal_pass_result, output, sigma.y);
152 horizontal_pass_result.release();
153}
154
155} // namespace blender::realtime_compositor
#define BLI_assert_msg(a, msg)
Definition BLI_assert.h:57
void GPU_shader_uniform_2fv(GPUShader *sh, const char *name, const float data[2])
void GPU_shader_uniform_1f(GPUShader *sh, const char *name, float value)
void GPU_shader_bind(GPUShader *shader)
void GPU_shader_unbind()
struct GPUShader GPUShader
void bind_as_image(GPUShader *shader, const char *image_name, bool read=false) const
Definition result.cc:264
const Domain & domain() const
Definition result.cc:712
void allocate_texture(Domain domain, bool from_pool=true)
Definition result.cc:204
void bind_as_texture(GPUShader *shader, const char *texture_name) const
Definition result.cc:253
T reduce_max(const VecBase< T, Size > &a)
static void blur_pass(Context &context, Result &input, Result &output, float sigma)
void van_vliet_gaussian_blur(Context &context, Result &input, Result &output, float2 sigma)
static void sum_causal_and_non_causal_results(Context &context, Result &causal_input, Result &non_causal_input, Result &output)
void compute_dispatch_threads_at_least(GPUShader *shader, int2 threads_range, int2 local_size=int2(16))
Definition utilities.cc:131
VecBase< int32_t, 2 > int2
VecBase< float, 2 > float2