Blender V4.5
shader_operation.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 <memory>
6#include <string>
7
8#include "BLI_assert.h"
9#include "BLI_listbase.h"
10#include "BLI_map.hh"
11#include "BLI_string_ref.hh"
12
14
15#include "GPU_context.hh"
16#include "GPU_debug.hh"
17#include "GPU_material.hh"
18#include "GPU_shader.hh"
19#include "GPU_texture.hh"
20#include "GPU_uniform_buffer.hh"
21
23
25
26#include "COM_context.hh"
28#include "COM_result.hh"
29#include "COM_scheduler.hh"
30#include "COM_shader_node.hh"
32#include "COM_utilities.hh"
33
34#include <sstream>
35
36namespace blender::compositor {
37
38using namespace nodes::derived_node_tree_types;
39
41 PixelCompileUnit &compile_unit,
42 const Schedule &schedule)
43 : PixelOperation(context, compile_unit, schedule)
44{
46 GPU_MAT_COMPOSITOR, &construct_material, &generate_code, this);
47}
48
53
55{
56 GPU_debug_group_begin("ShaderOperation");
57 const Domain domain = compute_domain();
58 for (StringRef identifier : output_sockets_to_output_identifiers_map_.values()) {
59 Result &result = get_result(identifier);
60 result.allocate_texture(domain);
61 }
62
63 GPUShader *shader = GPU_material_get_shader(material_);
64 GPU_shader_bind(shader);
65
66 bind_material_resources(shader);
67 bind_inputs(shader);
68 bind_outputs(shader);
69
71
77}
78
79void ShaderOperation::bind_material_resources(GPUShader *shader)
80{
81 /* Bind the uniform buffer of the material if it exists. It may not exist if the GPU material has
82 * no uniforms. */
83 GPUUniformBuf *ubo = GPU_material_uniform_buffer_get(material_);
84 if (ubo) {
86 }
87
88 /* Bind color band textures needed by curve and ramp nodes. */
89 ListBase textures = GPU_material_textures(material_);
91 if (texture->colorband) {
92 const int texture_image_unit = GPU_shader_get_sampler_binding(shader, texture->sampler_name);
93 GPU_texture_bind(*texture->colorband, texture_image_unit);
94 }
95 }
96}
97
98void ShaderOperation::bind_inputs(GPUShader *shader)
99{
100 /* Attributes represents the inputs of the operation and their names match those of the inputs of
101 * the operation as well as the corresponding texture samples in the shader. */
102 ListBase attributes = GPU_material_attributes(material_);
103 LISTBASE_FOREACH (GPUMaterialAttribute *, attribute, &attributes) {
104 get_input(attribute->name).bind_as_texture(shader, attribute->name);
105 }
106}
107
108void ShaderOperation::bind_outputs(GPUShader *shader)
109{
110 for (StringRefNull output_identifier : output_sockets_to_output_identifiers_map_.values()) {
111 get_result(output_identifier).bind_as_image(shader, output_identifier.c_str());
112 }
113}
114
115void ShaderOperation::construct_material(void *thunk, GPUMaterial *material)
116{
117 ShaderOperation *operation = static_cast<ShaderOperation *>(thunk);
118 operation->material_ = material;
119 for (DNode node : operation->compile_unit_) {
120 operation->shader_nodes_.add_new(node, std::make_unique<ShaderNode>(node));
121
122 operation->link_node_inputs(node);
123
124 operation->shader_nodes_.lookup(node)->compile(material);
125
126 operation->populate_results_for_node(node);
127 }
128}
129
130void ShaderOperation::link_node_inputs(DNode node)
131{
132 for (int i = 0; i < node->input_sockets().size(); i++) {
133 const DInputSocket input{node.context(), node->input_sockets()[i]};
134
135 /* The input is unavailable and unused, but it still needs to be linked as this is what the GPU
136 * material compiler expects. */
137 if (!is_socket_available(input.bsocket())) {
138 this->link_node_input_unavailable(input);
139 continue;
140 }
141
142 /* The origin socket is an input, that means the input is unlinked and . */
143 const DSocket origin = get_input_origin_socket(input);
144 if (origin->is_input()) {
145 const InputDescriptor origin_descriptor = input_descriptor_from_input_socket(
146 origin.bsocket());
147
148 if (origin_descriptor.implicit_input == ImplicitInput::None) {
149 /* No implicit input, so link a constant setter node for it that holds the input value. */
150 this->link_node_input_constant(input, DInputSocket(origin));
151 }
152 else {
153 this->link_node_input_implicit(input, DInputSocket(origin));
154 }
155 continue;
156 }
157
158 /* Otherwise, the origin socket is an output, which means it is linked. */
159 const DOutputSocket output = DOutputSocket(origin);
160
161 /* If the origin node is part of the shader operation, then the link is internal to the GPU
162 * material graph and is linked appropriately. */
163 if (compile_unit_.contains(output.node())) {
164 this->link_node_input_internal(input, output);
165 continue;
166 }
167
168 /* Otherwise, the origin node is not part of the shader operation, then the link is external to
169 * the GPU material graph and an input to the shader operation must be declared and linked to
170 * the node input. */
171 this->link_node_input_external(input, output);
172 }
173}
174
175void ShaderOperation::link_node_input_unavailable(const DInputSocket input)
176{
177 ShaderNode &node = *shader_nodes_.lookup(input.node());
178 GPUNodeStack &stack = node.get_input(input->identifier);
179
180 /* Create a constant link with some zero value. The value is arbitrary and ignored. See the
181 * method description. */
182 zero_v4(stack.vec);
183 GPUNodeLink *link = GPU_constant(stack.vec);
184
185 GPU_link(material_, "set_value", link, &stack.link);
186}
187
188/* Initializes the vector value of the given GPU node stack from the default value of the given
189 * input socket. */
191{
192 switch (input->type) {
193 case SOCK_FLOAT: {
194 const float value = input->default_value_typed<bNodeSocketValueFloat>()->value;
195 stack.vec[0] = value;
196 break;
197 }
198 case SOCK_INT: {
199 /* GPUMaterial doesn't support int, so it is stored as a float. */
200 const int value = input->default_value_typed<bNodeSocketValueInt>()->value;
201 stack.vec[0] = int(value);
202 break;
203 }
204 case SOCK_BOOLEAN: {
205 /* GPUMaterial doesn't support bool, so it is stored as a float. */
206 const bool value = input->default_value_typed<bNodeSocketValueBoolean>()->value;
207 stack.vec[0] = float(value);
208 break;
209 }
210 case SOCK_VECTOR: {
211 const float3 value = float3(input->default_value_typed<bNodeSocketValueVector>()->value);
212 copy_v3_v3(stack.vec, value);
213 break;
214 }
215 case SOCK_RGBA: {
216 const float4 value = float4(input->default_value_typed<bNodeSocketValueRGBA>()->value);
217 copy_v4_v4(stack.vec, value);
218 break;
219 }
220 default:
222 break;
223 }
224}
225
226static const char *get_set_function_name(const ResultType type)
227{
228 switch (type) {
230 return "set_value";
231 case ResultType::Int:
232 /* GPUMaterial doesn't support int, so it is passed as a float. */
233 return "set_value";
234 case ResultType::Bool:
235 /* GPUMaterial doesn't support bool, so it is passed as a float. */
236 return "set_value";
238 return "set_rgb";
240 return "set_rgba";
242 return "set_rgba";
244 /* GPUMaterial doesn't support float2, so it is passed as a float3 with z ignored. */
245 return "set_rgb";
246 case ResultType::Int2:
247 /* GPUMaterial doesn't support float2, so it is passed as a float3 with z ignored. */
248 return "set_rgb";
249 }
250
252 return nullptr;
253}
254
255void ShaderOperation::link_node_input_constant(const DInputSocket input, const DInputSocket origin)
256{
257 ShaderNode &node = *shader_nodes_.lookup(input.node());
258 GPUNodeStack &stack = node.get_input(input->identifier);
259
260 /* Create a uniform link that carry the value of the origin. */
261 initialize_input_stack_value(origin, stack);
262 GPUNodeLink *link = GPU_uniform(stack.vec);
263
264 const ResultType type = get_node_socket_result_type(origin.bsocket());
265 const char *function_name = get_set_function_name(type);
266 GPU_link(material_, function_name, link, &stack.link);
267}
268
269void ShaderOperation::link_node_input_implicit(const DInputSocket input, const DInputSocket origin)
270{
271 ShaderNode &node = *shader_nodes_.lookup(input.node());
272 GPUNodeStack &stack = node.get_input(input->identifier);
273
274 const InputDescriptor origin_descriptor = input_descriptor_from_input_socket(origin.bsocket());
275 const ImplicitInput implicit_input = origin_descriptor.implicit_input;
276
277 /* Inherit the type and implicit input of the origin input since doing implicit conversion inside
278 * the shader operation is much cheaper. */
279 InputDescriptor input_descriptor = input_descriptor_from_input_socket(input.bsocket());
280 input_descriptor.type = origin_descriptor.type;
281 input_descriptor.implicit_input = implicit_input;
282
283 /* An input was already declared for that implicit input, so no need to declare it again and we
284 * just link it. */
285 if (implicit_input_to_material_attribute_map_.contains(implicit_input)) {
286 /* But first we update the domain priority of the input descriptor to be the higher priority of
287 * the existing descriptor and the descriptor of the new input socket. That's because the same
288 * implicit input might be used in inputs inside the shader operation which have different
289 * priorities. */
290 InputDescriptor &existing_input_descriptor = this->get_input_descriptor(
292 existing_input_descriptor.domain_priority = math::min(
293 existing_input_descriptor.domain_priority, input_descriptor.domain_priority);
294
295 /* Link the attribute representing the shader operation input corresponding to the implicit
296 * input. */
297 stack.link = implicit_input_to_material_attribute_map_.lookup(implicit_input);
298 return;
299 }
300
301 const int implicit_input_index = implicit_inputs_to_input_identifiers_map_.size();
302 const std::string input_identifier = "implicit_input" + std::to_string(implicit_input_index);
303 declare_input_descriptor(input_identifier, input_descriptor);
304
305 /* Map the implicit input to the identifier of the operation input that was declared for it. */
306 implicit_inputs_to_input_identifiers_map_.add_new(implicit_input, input_identifier);
307
308 /* Add a new GPU attribute representing an input to the GPU material. Instead of using the
309 * attribute directly, we link it to an appropriate set function and use its output link instead.
310 * This is needed because the `gputype` member of the attribute is only initialized if it is
311 * linked to a GPU node. */
312 GPUNodeLink *attribute_link;
313 GPU_link(material_,
314 get_set_function_name(input_descriptor.type),
315 GPU_attribute(material_, CD_AUTO_FROM_NAME, input_identifier.c_str()),
316 &attribute_link);
317
318 /* Map the implicit input to the attribute that was created for it. */
319 implicit_input_to_material_attribute_map_.add(implicit_input, attribute_link);
320
321 /* Link the attribute representing the shader operation input corresponding to the implicit
322 * input. */
323 stack.link = attribute_link;
324}
325
326void ShaderOperation::link_node_input_internal(DInputSocket input_socket,
327 DOutputSocket output_socket)
328{
329 ShaderNode &output_node = *shader_nodes_.lookup(output_socket.node());
330 GPUNodeStack &output_stack = output_node.get_output(output_socket->identifier);
331
332 ShaderNode &input_node = *shader_nodes_.lookup(input_socket.node());
333 GPUNodeStack &input_stack = input_node.get_input(input_socket->identifier);
334
335 input_stack.link = output_stack.link;
336}
337
338void ShaderOperation::link_node_input_external(DInputSocket input_socket,
339 DOutputSocket output_socket)
340{
341
342 ShaderNode &node = *shader_nodes_.lookup(input_socket.node());
343 GPUNodeStack &stack = node.get_input(input_socket->identifier);
344
345 if (!output_to_material_attribute_map_.contains(output_socket)) {
346 /* No input was declared for that output yet, so declare it. */
347 declare_operation_input(input_socket, output_socket);
348 }
349 else {
350 /* An input was already declared for that same output socket, so no need to declare it again.
351 * But we update the domain priority of the input descriptor to be the higher priority of the
352 * existing descriptor and the descriptor of the new input socket. That's because the same
353 * output might be connected to multiple inputs inside the shader operation which have
354 * different priorities. */
355 const std::string input_identifier = outputs_to_declared_inputs_map_.lookup(output_socket);
356 InputDescriptor &input_descriptor = this->get_input_descriptor(input_identifier);
357 input_descriptor.domain_priority = math::min(
358 input_descriptor.domain_priority,
360
361 /* Increment the input's reference count. */
362 inputs_to_reference_counts_map_.lookup(input_identifier)++;
363 }
364
365 /* Link the attribute representing the shader operation input corresponding to the given output
366 * socket. */
367 stack.link = output_to_material_attribute_map_.lookup(output_socket);
368}
369
370void ShaderOperation::declare_operation_input(DInputSocket input_socket,
371 DOutputSocket output_socket)
372{
373 const int input_index = output_to_material_attribute_map_.size();
374 std::string input_identifier = "input" + std::to_string(input_index);
375
376 /* Declare the input descriptor for this input and prefer to declare its type to be the same as
377 * the type of the output socket because doing type conversion in the shader is much cheaper. */
378 InputDescriptor input_descriptor = input_descriptor_from_input_socket(input_socket.bsocket());
379 input_descriptor.type = get_node_socket_result_type(output_socket.bsocket());
380 declare_input_descriptor(input_identifier, input_descriptor);
381
382 /* Add a new GPU attribute representing an input to the GPU material. Instead of using the
383 * attribute directly, we link it to an appropriate set function and use its output link instead.
384 * This is needed because the `gputype` member of the attribute is only initialized if it is
385 * linked to a GPU node. */
386 GPUNodeLink *attribute_link;
387 GPU_link(material_,
388 get_set_function_name(input_descriptor.type),
389 GPU_attribute(material_, CD_AUTO_FROM_NAME, input_identifier.c_str()),
390 &attribute_link);
391
392 /* Map the output socket to the attribute that was created for it. */
393 output_to_material_attribute_map_.add(output_socket, attribute_link);
394
395 /* Map the identifier of the operation input to the output socket it is linked to. */
396 inputs_to_linked_outputs_map_.add_new(input_identifier, output_socket);
397
398 /* Map the output socket to the identifier of the operation input that was declared for it. */
399 outputs_to_declared_inputs_map_.add_new(output_socket, input_identifier);
400
401 /* Map the identifier of the operation input to a reference count of 1, this will later be
402 * incremented if that same output was referenced again. */
403 inputs_to_reference_counts_map_.add_new(input_identifier, 1);
404}
405
406void ShaderOperation::populate_results_for_node(DNode node)
407{
408 const DOutputSocket preview_output = find_preview_output_socket(node);
409
410 for (const bNodeSocket *output : node->output_sockets()) {
411 const DOutputSocket doutput{node.context(), output};
412
414 continue;
415 }
416
417 /* If any of the nodes linked to the output are not part of the shader operation but are part
418 * of the execution schedule, then an output result needs to be populated for it. */
419 const bool is_operation_output = is_output_linked_to_node_conditioned(
420 doutput,
421 [&](DNode node) { return schedule_.contains(node) && !compile_unit_.contains(node); });
422
423 /* If the output is used as the node preview, then an output result needs to be populated for
424 * it, and we additionally keep track of that output to later compute the previews from. */
425 const bool is_preview_output = doutput == preview_output;
426 if (is_preview_output) {
427 preview_outputs_.add(doutput);
428 }
429
430 if (is_operation_output || is_preview_output) {
431 populate_operation_result(doutput);
432 }
433 }
434}
435
436static const char *get_store_function_name(ResultType type)
437{
438 switch (type) {
440 return "node_compositor_store_output_float";
441 case ResultType::Int:
442 return "node_compositor_store_output_int";
443 case ResultType::Bool:
444 return "node_compositor_store_output_bool";
446 return "node_compositor_store_output_float3";
448 return "node_compositor_store_output_color";
450 return "node_compositor_store_output_float4";
452 return "node_compositor_store_output_float2";
453 case ResultType::Int2:
454 return "node_compositor_store_output_int2";
455 }
456
458 return nullptr;
459}
460
461void ShaderOperation::populate_operation_result(DOutputSocket output_socket)
462{
464 std::string output_identifier = "output" + std::to_string(output_id);
465
466 const ResultType result_type = get_node_socket_result_type(output_socket.bsocket());
467 const Result result = context().create_result(result_type);
468 populate_result(output_identifier, result);
469
470 /* Map the output socket to the identifier of the newly populated result. */
471 output_sockets_to_output_identifiers_map_.add_new(output_socket, output_identifier);
472
473 ShaderNode &node = *shader_nodes_.lookup(output_socket.node());
474 GPUNodeLink *output_link = node.get_output(output_socket->identifier).link;
475
476 /* Link the output node stack to an output storer storing in the appropriate result. The result
477 * is identified by its index in the operation and the index is encoded as a float to be passed
478 * to the GPU function. Additionally, create an output link from the storer node to declare as an
479 * output to the GPU material. This storer output link is a dummy link in the sense that its
480 * value is ignored since it is already written in the output, but it is used to track nodes that
481 * contribute to the output of the compositor node tree. */
482 GPUNodeLink *storer_output_link;
483 GPUNodeLink *id_link = GPU_constant((float *)&output_id);
484 const char *store_function_name = get_store_function_name(result_type);
485 GPU_link(material_, store_function_name, id_link, output_link, &storer_output_link);
486
487 /* Declare the output link of the storer node as an output of the GPU material to help the GPU
488 * code generator to track the nodes that contribute to the output of the shader. */
489 GPU_material_add_output_link_composite(material_, storer_output_link);
490}
491
492using namespace gpu::shader;
493
494void ShaderOperation::generate_code(void *thunk,
495 GPUMaterial *material,
496 GPUCodegenOutput *code_generator_output)
497{
498 ShaderOperation *operation = static_cast<ShaderOperation *>(thunk);
499 ShaderCreateInfo &shader_create_info = *reinterpret_cast<ShaderCreateInfo *>(
500 code_generator_output->create_info);
501
502 shader_create_info.local_group_size(16, 16);
503
504 /* Add implementation for implicit conversion operations inserted by the code generator. This
505 * file should include the functions [float|vec3|vec4]_from_[float|vec3|vec4]. */
506 shader_create_info.typedef_source("gpu_shader_compositor_type_conversion.glsl");
507
508 /* The source shader is a compute shader with a main function that calls the dynamically
509 * generated evaluate function. The evaluate function includes the serialized GPU material graph
510 * preceded by code that initialized the inputs of the operation. Additionally, the storer
511 * functions that writes the outputs are defined outside the evaluate function. */
512 shader_create_info.compute_source("gpu_shader_compositor_main.glsl");
513
514 /* The main function is emitted in the shader before the evaluate function, so the evaluate
515 * function needs to be forward declared here.
516 * NOTE(Metal): Metal does not require forward declarations. */
518 shader_create_info.typedef_source_generated += "void evaluate();\n";
519 }
520
521 operation->generate_code_for_outputs(shader_create_info);
522
523 shader_create_info.compute_source_generated += "void evaluate()\n{\n";
524
525 operation->generate_code_for_inputs(material, shader_create_info);
526
527 shader_create_info.compute_source_generated += code_generator_output->composite;
528
529 shader_create_info.compute_source_generated += "}\n";
530}
531
532/* Texture storers in the shader always take a [i]vec4 as an argument, so encode each type in an
533 * [i]vec4 appropriately. */
535{
536 switch (type) {
538 return "vec4(value)";
539 case ResultType::Int:
540 /* GPUMaterial doesn't support int, so it is passed as a float, and we need to convert it
541 * back to int before writing it. */
542 return "ivec4(int(value))";
543 case ResultType::Bool:
544 /* GPUMaterial doesn't support bool, so it is passed as a float and stored as an int, and we
545 * need to convert it back to bool and then to an int before writing it. */
546 return "ivec4(bool(value))";
548 return "vec4(value, 0.0)";
550 return "value";
552 return "value";
554 /* GPUMaterial doesn't support float2, so it is passed as a float3, and we need to convert it
555 * back to float2 before writing it. */
556 return "vec4(value.xy, 0.0, 0.0)";
557 case ResultType::Int2:
558 /* GPUMaterial doesn't support int2, so it is passed as a float3, and we need to convert it
559 * back to int2 before writing it. */
560 return "ivec4(ivec2(value.xy), 0, 0)";
561 }
562
564 return nullptr;
565}
566
568{
569 switch (type) {
575 return ImageType::Float2D;
576 case ResultType::Int:
577 case ResultType::Int2:
578 case ResultType::Bool:
579 return ImageType::Int2D;
580 }
581
583 return ImageType::Float2D;
584}
585
586void ShaderOperation::generate_code_for_outputs(ShaderCreateInfo &shader_create_info)
587{
588 const std::string store_float_function_header = "void store_float(const uint id, float value)";
589 /* GPUMaterial doesn't support int, so it is passed as a float. */
590 const std::string store_int_function_header = "void store_int(const uint id, float value)";
591 /* GPUMaterial doesn't support bool, so it is passed as a float. */
592 const std::string store_bool_function_header = "void store_bool(const uint id, float value)";
593 const std::string store_float3_function_header = "void store_float3(const uint id, vec3 value)";
594 const std::string store_color_function_header = "void store_color(const uint id, vec4 value)";
595 const std::string store_float4_function_header = "void store_float4(const uint id, vec4 value)";
596 /* GPUMaterial doesn't support float2, so it is passed as a float3. */
597 const std::string store_float2_function_header = "void store_float2(const uint id, vec3 value)";
598 /* GPUMaterial doesn't support int2, so it is passed as a float3. */
599 const std::string store_int2_function_header = "void store_int2(const uint id, vec3 value)";
600
601 /* The store functions are used by the node_compositor_store_output_[type] functions but are only
602 * defined later as part of the compute source, so they need to be forward declared. NOTE(Metal):
603 * Metal does not require forward declarations. */
605 shader_create_info.typedef_source_generated += store_float_function_header + ";\n";
606 shader_create_info.typedef_source_generated += store_int_function_header + ";\n";
607 shader_create_info.typedef_source_generated += store_bool_function_header + ";\n";
608 shader_create_info.typedef_source_generated += store_float3_function_header + ";\n";
609 shader_create_info.typedef_source_generated += store_color_function_header + ";\n";
610 shader_create_info.typedef_source_generated += store_float4_function_header + ";\n";
611 shader_create_info.typedef_source_generated += store_float2_function_header + ";\n";
612 shader_create_info.typedef_source_generated += store_int2_function_header + ";\n";
613 }
614
615 /* Each of the store functions is essentially a single switch case on the given ID, so start by
616 * opening the function with a curly bracket followed by opening a switch statement in each of
617 * the functions. */
618 std::stringstream store_float_function;
619 std::stringstream store_int_function;
620 std::stringstream store_bool_function;
621 std::stringstream store_float3_function;
622 std::stringstream store_color_function;
623 std::stringstream store_float4_function;
624 std::stringstream store_float2_function;
625 std::stringstream store_int2_function;
626 const std::string store_function_start = "\n{\n switch (id) {\n";
627 store_float_function << store_float_function_header << store_function_start;
628 store_int_function << store_int_function_header << store_function_start;
629 store_bool_function << store_bool_function_header << store_function_start;
630 store_float3_function << store_float3_function_header << store_function_start;
631 store_color_function << store_color_function_header << store_function_start;
632 store_float4_function << store_float4_function_header << store_function_start;
633 store_float2_function << store_float2_function_header << store_function_start;
634 store_int2_function << store_int2_function_header << store_function_start;
635
636 int output_index = 0;
638 const Result &result = get_result(output_identifier);
639
640 /* Add a write-only image for this output where its values will be written. */
641 shader_create_info.image(output_index,
642 result.get_gpu_texture_format(),
643 Qualifier::write,
645 output_identifier,
646 Frequency::PASS);
647 output_index++;
648
649 /* Add a case for the index of this output followed by a break statement. */
650 std::stringstream case_code;
651 const std::string store_expression = glsl_store_expression_from_result_type(result.type());
652 const std::string texel = ", ivec2(gl_GlobalInvocationID.xy), ";
653 case_code << " case " << StringRef(output_identifier).drop_known_prefix("output") << ":\n"
654 << " imageStore(" << output_identifier << texel << store_expression << ");\n"
655 << " break;\n";
656
657 /* Only add the case to the function with the matching type. */
658 switch (result.type()) {
660 store_float_function << case_code.str();
661 break;
662 case ResultType::Int:
663 store_int_function << case_code.str();
664 break;
665 case ResultType::Bool:
666 store_bool_function << case_code.str();
667 break;
669 store_float3_function << case_code.str();
670 break;
672 store_color_function << case_code.str();
673 break;
675 store_float4_function << case_code.str();
676 break;
678 store_float2_function << case_code.str();
679 break;
680 case ResultType::Int2:
681 store_int2_function << case_code.str();
682 break;
683 }
684 }
685
686 /* Close the previously opened switch statement as well as the function itself. */
687 const std::string store_function_end = " }\n}\n\n";
688 store_float_function << store_function_end;
689 store_int_function << store_function_end;
690 store_bool_function << store_function_end;
691 store_float3_function << store_function_end;
692 store_color_function << store_function_end;
693 store_float4_function << store_function_end;
694 store_float2_function << store_function_end;
695 store_int2_function << store_function_end;
696
697 shader_create_info.compute_source_generated +=
698 store_float_function.str() + store_int_function.str() + store_bool_function.str() +
699 store_float3_function.str() + store_color_function.str() + store_float4_function.str() +
700 store_float2_function.str() + store_int2_function.str();
701}
702
704{
705 switch (type) {
707 return "float";
708 case ResultType::Int:
709 /* GPUMaterial doesn't support int, so it is passed as a float. */
710 return "float";
711 case ResultType::Bool:
712 /* GPUMaterial doesn't support bool, so it is passed as a float. */
713 return "float";
715 return "vec3";
718 return "vec4";
720 /* GPUMaterial doesn't support float2, so it is passed as a float3 with z ignored. */
721 return "vec3";
722 case ResultType::Int2:
723 /* GPUMaterial doesn't support int2, so it is passed as a float3 with z ignored. */
724 return "vec3";
725 }
726
728 return nullptr;
729}
730
731/* Texture loaders in the shader always return an [i]vec4, so a swizzle is needed to retrieve the
732 * actual value for each type. */
734{
735 switch (type) {
737 case ResultType::Int:
738 case ResultType::Bool:
739 return "x";
741 return "xyz";
743 return "rgba";
745 return "xyzw";
747 /* GPUMaterial doesn't support float2, so it is passed as a float3 with z ignored. */
748 return "xyz";
749 case ResultType::Int2:
750 /* GPUMaterial doesn't support float2, so it is passed as a float3 with z ignored. */
751 return "xyz";
752 }
753
755 return nullptr;
756}
757
758void ShaderOperation::generate_code_for_inputs(GPUMaterial *material,
759 ShaderCreateInfo &shader_create_info)
760{
761 /* The attributes of the GPU material represents the inputs of the operation. */
762 ListBase attributes = GPU_material_attributes(material);
763
764 if (BLI_listbase_is_empty(&attributes)) {
765 return;
766 }
767
768 /* Add a texture sampler for each of the inputs with the same name as the attribute, we start
769 * counting the sampler slot location from the number of textures in the material, since some
770 * sampler slots may be reserved for things like color band textures. */
771 const ListBase textures = GPU_material_textures(material);
772 int input_slot_location = BLI_listbase_count(&textures);
773 LISTBASE_FOREACH (GPUMaterialAttribute *, attribute, &attributes) {
774 const InputDescriptor &input_descriptor = get_input_descriptor(attribute->name);
775 shader_create_info.sampler(input_slot_location,
776 gpu_image_type_from_result_type(input_descriptor.type),
777 attribute->name,
778 Frequency::PASS);
779 input_slot_location++;
780 }
781
782 /* Declare a struct called var_attrs that includes an appropriately typed member for each of the
783 * inputs. The names of the members should be the letter v followed by the ID of the attribute
784 * corresponding to the input. Such names are expected by the code generator. */
785 std::stringstream declare_attributes;
786 declare_attributes << "struct {\n";
787 LISTBASE_FOREACH (GPUMaterialAttribute *, attribute, &attributes) {
788 const InputDescriptor &input_descriptor = get_input_descriptor(attribute->name);
789 const std::string type = glsl_type_from_result_type(input_descriptor.type);
790 declare_attributes << " " << type << " v" << attribute->id << ";\n";
791 }
792 declare_attributes << "} var_attrs;\n\n";
793
794 shader_create_info.compute_source_generated += declare_attributes.str();
795
796 /* The texture loader utilities are needed to sample the input textures and initialize the
797 * attributes. */
798 shader_create_info.typedef_source("gpu_shader_compositor_texture_utilities.glsl");
799
800 /* Initialize each member of the previously declared struct by loading its corresponding texture
801 * with an appropriate swizzle and cast for its type. */
802 std::stringstream initialize_attributes;
803 LISTBASE_FOREACH (GPUMaterialAttribute *, attribute, &attributes) {
804 const InputDescriptor &input_descriptor = get_input_descriptor(attribute->name);
805 const std::string swizzle = glsl_swizzle_from_result_type(input_descriptor.type);
806 const std::string type = glsl_type_from_result_type(input_descriptor.type);
807 initialize_attributes << "var_attrs.v" << attribute->id << " = " << type << "("
808 << "texture_load(" << attribute->name
809 << ", ivec2(gl_GlobalInvocationID.xy))." << swizzle << ")"
810 << ";\n";
811 }
812 initialize_attributes << "\n";
813
814 shader_create_info.compute_source_generated += initialize_attributes.str();
815}
816
817} // namespace blender::compositor
#define BLI_assert_unreachable()
Definition BLI_assert.h:93
#define LISTBASE_FOREACH(type, var, list)
BLI_INLINE bool BLI_listbase_is_empty(const ListBase *lb)
int BLI_listbase_count(const ListBase *listbase) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
Definition listbase.cc:524
MINLINE void copy_v4_v4(float r[4], const float a[4])
MINLINE void copy_v3_v3(float r[3], const float a[3])
MINLINE void zero_v4(float r[4])
unsigned int uint
@ CD_AUTO_FROM_NAME
struct ListBase ListBase
@ SOCK_INT
@ SOCK_VECTOR
@ SOCK_BOOLEAN
@ SOCK_FLOAT
@ SOCK_RGBA
struct bNodeSocket bNodeSocket
eGPUBackendType GPU_backend_get_type()
void GPU_debug_group_end()
Definition gpu_debug.cc:33
void GPU_debug_group_begin(const char *name)
Definition gpu_debug.cc:22
GPUNodeLink * GPU_constant(const float *num)
ListBase GPU_material_attributes(const GPUMaterial *material)
void GPU_material_free_single(GPUMaterial *material)
@ GPU_MAT_COMPOSITOR
GPUShader * GPU_material_get_shader(GPUMaterial *material)
ListBase GPU_material_textures(GPUMaterial *material)
void GPU_material_add_output_link_composite(GPUMaterial *material, GPUNodeLink *link)
GPUMaterial * GPU_material_from_callbacks(eGPUMaterialEngine engine, ConstructGPUMaterialFn construct_function_cb, GPUCodegenCallbackFn generate_code_function_cb, void *thunk)
GPUNodeLink * GPU_attribute(GPUMaterial *mat, eCustomDataType type, const char *name)
bool GPU_link(GPUMaterial *mat, const char *name,...)
GPUUniformBuf * GPU_material_uniform_buffer_get(GPUMaterial *material)
GPUNodeLink * GPU_uniform(const float *num)
int GPU_shader_get_sampler_binding(GPUShader *shader, const char *name)
int GPU_shader_get_ubo_binding(GPUShader *shader, const char *name)
void GPU_shader_bind(GPUShader *shader, const blender::gpu::shader::SpecializationConstants *constants_state=nullptr)
void GPU_shader_unbind()
void GPU_texture_bind(GPUTexture *texture, int unit)
void GPU_texture_image_unbind_all()
void GPU_texture_unbind_all()
#define GPU_UBO_BLOCK_NAME
void GPU_uniformbuf_debug_unbind_all()
void GPU_uniformbuf_bind(GPUUniformBuf *ubo, int slot)
static DBVT_INLINE btScalar size(const btDbvtVolume &a)
Definition btDbvt.cpp:52
ValueIterator values() const &
Definition BLI_map.hh:884
const Value & lookup(const Key &key) const
Definition BLI_map.hh:545
void add_new(const Key &key, const Value &value)
Definition BLI_map.hh:265
int64_t size() const
Definition BLI_map.hh:976
bool contains(const Key &key) const
Definition BLI_map.hh:353
constexpr StringRef drop_known_prefix(StringRef prefix) const
Result create_result(ResultType type, ResultPrecision precision)
Result & get_result(StringRef identifier)
Definition operation.cc:39
void populate_result(StringRef identifier, Result result)
Definition operation.cc:148
Result & get_input(StringRef identifier) const
Definition operation.cc:138
virtual Domain compute_domain()
Definition operation.cc:56
InputDescriptor & get_input_descriptor(StringRef identifier)
Definition operation.cc:158
void declare_input_descriptor(StringRef identifier, InputDescriptor descriptor)
Definition operation.cc:153
Map< DOutputSocket, std::string > outputs_to_declared_inputs_map_
Map< std::string, DOutputSocket > inputs_to_linked_outputs_map_
Map< std::string, int > inputs_to_reference_counts_map_
VectorSet< DOutputSocket > preview_outputs_
Map< DOutputSocket, std::string > output_sockets_to_output_identifiers_map_
PixelOperation(Context &context, PixelCompileUnit &compile_unit, const Schedule &schedule)
Map< ImplicitInput, std::string > implicit_inputs_to_input_identifiers_map_
void bind_as_texture(GPUShader *shader, const char *texture_name) const
Definition result.cc:365
void bind_as_image(GPUShader *shader, const char *image_name, bool read=false) const
Definition result.cc:376
ShaderOperation(Context &context, PixelCompileUnit &compile_unit, const Schedule &schedule)
TEX_TEMPLATE DataVec texture(T, FltCoord, float=0.0f) RET
#define input
#define output
static void initialize_input_stack_value(const DInputSocket input, GPUNodeStack &stack)
bool is_output_linked_to_node_conditioned(DOutputSocket output, FunctionRef< bool(DNode)> condition)
Definition utilities.cc:88
DSocket get_input_origin_socket(DInputSocket input)
Definition utilities.cc:30
VectorSet< DNode > Schedule
static const char * glsl_store_expression_from_result_type(ResultType type)
void compute_dispatch_threads_at_least(GPUShader *shader, int2 threads_range, int2 local_size=int2(16))
Definition utilities.cc:170
static const char * glsl_swizzle_from_result_type(ResultType type)
static ImageType gpu_image_type_from_result_type(const ResultType type)
VectorSet< DNode > PixelCompileUnit
static const char * glsl_type_from_result_type(ResultType type)
DOutputSocket find_preview_output_socket(const DNode &node)
Definition utilities.cc:200
ResultType get_node_socket_result_type(const bNodeSocket *socket)
Definition utilities.cc:66
InputDescriptor input_descriptor_from_input_socket(const bNodeSocket *socket)
Definition utilities.cc:144
static const char * get_store_function_name(ResultType type)
bool is_socket_available(const bNodeSocket *socket)
Definition utilities.cc:25
static const char * get_set_function_name(const ResultType type)
T min(const T &a, const T &b)
VecBase< float, 4 > float4
VecBase< float, 3 > float3
std::string composite
GPUShaderCreateInfo * create_info
GPUNodeLink * link
Describe inputs & outputs, stage interfaces, resources and sources of a shader. If all data is correc...
Self & compute_source(StringRefNull filename)
Self & image(int slot, eGPUTextureFormat format, Qualifier qualifiers, ImageReadWriteType type, StringRefNull name, Frequency freq=Frequency::PASS)
Self & typedef_source(StringRefNull filename)
Self & sampler(int slot, ImageType type, StringRefNull name, Frequency freq=Frequency::PASS, GPUSamplerState sampler=GPUSamplerState::internal_sampler())
Self & local_group_size(int local_size_x, int local_size_y=1, int local_size_z=1)
i
Definition text_draw.cc:230