Blender V4.3
utilities.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2023 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
5#include "BLI_assert.h"
6#include "BLI_function_ref.hh"
7#include "BLI_index_range.hh"
8#include "BLI_math_color.h"
9#include "BLI_math_vector.hh"
11#include "BLI_task.hh"
12#include "BLI_utildefines.h"
13
15#include "IMB_imbuf.hh"
16
17#include "DNA_node_types.h"
18
21
22#include "GPU_compute.hh"
23#include "GPU_shader.hh"
24
25#include "COM_operation.hh"
26#include "COM_result.hh"
27#include "COM_utilities.hh"
28
30
31using namespace nodes::derived_node_tree_types;
33
35{
36 /* The input is unlinked. Return the socket itself. */
37 if (!input->is_logically_linked()) {
38 return input;
39 }
40
41 /* Only a single origin socket is guaranteed to exist. */
42 DSocket socket;
43 input.foreach_origin_socket([&](const DSocket origin) { socket = origin; });
44 return socket;
45}
46
47DOutputSocket get_output_linked_to_input(DInputSocket input)
48{
49 /* Get the origin socket of this input, which will be an output socket if the input is linked
50 * to an output. */
51 const DSocket origin = get_input_origin_socket(input);
52
53 /* If the origin socket is an input, that means the input is unlinked, so return a null output
54 * socket. */
55 if (origin->is_input()) {
56 return DOutputSocket();
57 }
58
59 /* Now that we know the origin is an output, return a derived output from it. */
60 return DOutputSocket(origin);
61}
62
64{
65 switch (socket->type) {
66 case SOCK_FLOAT:
67 return ResultType::Float;
68 case SOCK_VECTOR:
69 return ResultType::Vector;
70 case SOCK_RGBA:
71 return ResultType::Color;
72 default:
74 return ResultType::Float;
75 }
76}
77
78bool is_output_linked_to_node_conditioned(DOutputSocket output, FunctionRef<bool(DNode)> condition)
79{
80 bool condition_satisfied = false;
81 output.foreach_target_socket(
82 [&](DInputSocket target, const TargetSocketPathInfo & /*path_info*/) {
83 if (condition(target.node())) {
84 condition_satisfied = true;
85 return;
86 }
87 });
88 return condition_satisfied;
89}
90
92 FunctionRef<bool(DInputSocket)> condition)
93{
94 int count = 0;
95 output.foreach_target_socket(
96 [&](DInputSocket target, const TargetSocketPathInfo & /*path_info*/) {
97 if (condition(target)) {
98 count++;
99 }
100 });
101 return count;
102}
103
104bool is_pixel_node(DNode node)
105{
106 return node->typeinfo->get_compositor_shader_node;
107}
108
110{
111 using namespace nodes;
112 InputDescriptor input_descriptor;
113 input_descriptor.type = get_node_socket_result_type(socket);
114 const NodeDeclaration *node_declaration = socket->owner_node().declaration();
115 /* Not every node has a declaration, in which case we assume the default values for the rest of
116 * the properties. */
117 if (!node_declaration) {
118 return input_descriptor;
119 }
120 const SocketDeclaration *socket_declaration = node_declaration->inputs[socket->index()];
121 input_descriptor.domain_priority = socket_declaration->compositor_domain_priority();
122 input_descriptor.expects_single_value = socket_declaration->compositor_expects_single_value();
123
124 input_descriptor.realization_options.realize_on_operation_domain = bool(
125 socket_declaration->compositor_realization_options() &
126 CompositorInputRealizationOptions::RealizeOnOperationDomain);
127
128 return input_descriptor;
129}
130
131void compute_dispatch_threads_at_least(GPUShader *shader, int2 threads_range, int2 local_size)
132{
133 /* If the threads range is divisible by the local size, dispatch the number of needed groups,
134 * which is their division. If it is not divisible, then dispatch an extra group to cover the
135 * remaining invocations, which means the actual threads range of the dispatch will be a bit
136 * larger than the given one. */
137 const int2 groups_to_dispatch = math::divide_ceil(threads_range, local_size);
138 GPU_compute_dispatch(shader, groups_to_dispatch.x, groups_to_dispatch.y, 1);
139}
140
141bool is_node_preview_needed(const DNode &node)
142{
143 if (!(node->flag & NODE_PREVIEW)) {
144 return false;
145 }
146
147 if (node->flag & NODE_HIDDEN) {
148 return false;
149 }
150
151 /* Only compute previews for nodes in the active context. */
152 if (node.context()->instance_key().value !=
153 node.context()->derived_tree().active_context().instance_key().value)
154 {
155 return false;
156 }
157
158 return true;
159}
160
161DOutputSocket find_preview_output_socket(const DNode &node)
162{
163 if (!is_node_preview_needed(node)) {
164 return DOutputSocket();
165 }
166
167 for (const bNodeSocket *output : node->output_sockets()) {
168 if (output->is_logically_linked()) {
169 return DOutputSocket(node.context(), output);
170 }
171 }
172
173 return DOutputSocket();
174}
175
176/* Given the size of a result, compute a lower resolution size for a preview. The greater dimension
177 * will be assigned an arbitrarily chosen size of 128, while the other dimension will get the size
178 * that maintains the same aspect ratio. */
180{
181 const int greater_dimension_size = 128;
182 if (size.x > size.y) {
183 return int2(greater_dimension_size, int(greater_dimension_size * (float(size.y) / size.x)));
184 }
185 else {
186 return int2(int(greater_dimension_size * (float(size.x) / size.y)), greater_dimension_size);
187 }
188}
189
190void compute_preview_from_result(Context &context, const DNode &node, Result &input_result)
191{
192 /* Initialize node tree previews if not already initialized. */
193 bNodeTree *root_tree = const_cast<bNodeTree *>(
194 &node.context()->derived_tree().root_context().btree());
195 if (!root_tree->previews) {
196 root_tree->previews = bke::node_instance_hash_new("node previews");
197 }
198
199 const int2 preview_size = compute_preview_size(input_result.domain().size);
200 node->runtime->preview_xsize = preview_size.x;
201 node->runtime->preview_ysize = preview_size.y;
202
204 root_tree->previews, node.instance_key(), preview_size.x, preview_size.y, true);
205
206 GPUShader *shader = context.get_shader("compositor_compute_preview");
207 GPU_shader_bind(shader);
208
209 if (input_result.type() == ResultType::Float) {
210 GPU_texture_swizzle_set(input_result, "rrr1");
211 }
212
213 input_result.bind_as_texture(shader, "input_tx");
214
215 Result preview_result = context.create_result(ResultType::Color);
216 preview_result.allocate_texture(Domain(preview_size));
217 preview_result.bind_as_image(shader, "preview_img");
218
219 compute_dispatch_threads_at_least(shader, preview_size);
220
221 input_result.unbind_as_texture();
222 preview_result.unbind_as_image();
224
226 float *preview_pixels = static_cast<float *>(
227 GPU_texture_read(preview_result, GPU_DATA_FLOAT, 0));
228 preview_result.release();
229
231 &context.get_scene().view_settings, &context.get_scene().display_settings);
232
233 threading::parallel_for(IndexRange(preview_size.y), 1, [&](const IndexRange sub_y_range) {
234 for (const int64_t y : sub_y_range) {
235 for (const int64_t x : IndexRange(preview_size.x)) {
236 const int index = (y * preview_size.x + x) * 4;
237 IMB_colormanagement_processor_apply_v4(color_processor, preview_pixels + index);
238 rgba_float_to_uchar(preview->ibuf->byte_buffer.data + index, preview_pixels + index);
239 }
240 }
241 });
242
243 /* Restore original swizzle mask set above. */
244 if (input_result.type() == ResultType::Float) {
245 GPU_texture_swizzle_set(input_result, "rgba");
246 }
247
248 IMB_colormanagement_processor_free(color_processor);
249 MEM_freeN(preview_pixels);
250}
251
252void parallel_for(const int2 range, FunctionRef<void(int2)> function)
253{
254 threading::parallel_for(IndexRange(range.y), 1, [&](const IndexRange sub_y_range) {
255 for (const int64_t y : sub_y_range) {
256 for (const int64_t x : IndexRange(range.x)) {
257 function(int2(x, y));
258 }
259 }
260 });
261}
262
263} // namespace blender::realtime_compositor
#define BLI_assert_unreachable()
Definition BLI_assert.h:97
@ NODE_HIDDEN
@ NODE_PREVIEW
@ SOCK_VECTOR
@ SOCK_FLOAT
@ SOCK_RGBA
void GPU_compute_dispatch(GPUShader *shader, uint groups_x_len, uint groups_y_len, uint groups_z_len)
void GPU_shader_bind(GPUShader *shader)
void GPU_shader_unbind()
void GPU_memory_barrier(eGPUBarrier barrier)
Definition gpu_state.cc:374
@ GPU_BARRIER_TEXTURE_FETCH
Definition GPU_state.hh:37
void * GPU_texture_read(GPUTexture *texture, eGPUDataFormat data_format, int mip_level)
@ GPU_DATA_FLOAT
void GPU_texture_swizzle_set(GPUTexture *texture, const char swizzle[4])
ColormanageProcessor * IMB_colormanagement_display_processor_new(const ColorManagedViewSettings *view_settings, const ColorManagedDisplaySettings *display_settings)
void IMB_colormanagement_processor_free(ColormanageProcessor *cm_processor)
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
static ResultType type(eGPUTextureFormat format)
Definition result.cc:148
void bind_as_texture(GPUShader *shader, const char *texture_name) const
Definition result.cc:253
int count
void MEM_freeN(void *vmemh)
Definition mallocn.cc:105
bNodePreview * node_preview_verify(bNodeInstanceHash *previews, bNodeInstanceKey key, int xsize, int ysize, bool create)
Definition node.cc:3273
bNodeInstanceHash * node_instance_hash_new(const char *info)
Definition node.cc:4067
VecBase< T, Size > divide_ceil(const VecBase< T, Size > &a, const VecBase< T, Size > &b)
bool is_pixel_node(DNode node)
Definition utilities.cc:104
bool is_node_preview_needed(const DNode &node)
Definition utilities.cc:141
static int2 compute_preview_size(int2 size)
Definition utilities.cc:179
bool is_output_linked_to_node_conditioned(DOutputSocket output, FunctionRef< bool(DNode)> condition)
Definition utilities.cc:78
void compute_preview_from_result(Context &context, const DNode &node, Result &input_result)
Definition utilities.cc:190
DOutputSocket get_output_linked_to_input(DInputSocket input)
Definition utilities.cc:47
int number_of_inputs_linked_to_output_conditioned(DOutputSocket output, FunctionRef< bool(DInputSocket)> condition)
Definition utilities.cc:91
DOutputSocket find_preview_output_socket(const DNode &node)
Definition utilities.cc:161
ResultType get_node_socket_result_type(const bNodeSocket *socket)
Definition utilities.cc:63
InputDescriptor input_descriptor_from_input_socket(const bNodeSocket *socket)
Definition utilities.cc:109
void compute_dispatch_threads_at_least(GPUShader *shader, int2 threads_range, int2 local_size=int2(16))
Definition utilities.cc:131
DSocket get_input_origin_socket(DInputSocket input)
Definition utilities.cc:34
void parallel_for(const IndexRange range, const int64_t grain_size, const Function &function, const TaskSizeHints &size_hints=detail::TaskSizeHints_Static(1))
Definition BLI_task.hh:95
VecBase< int32_t, 2 > int2
NodeInstanceHashHandle * previews