Blender V5.0
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 gpu::Shader *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(gpu::Shader *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. */
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(gpu::Shader *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(gpu::Shader *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 float4 value = float4(input->default_value_typed<bNodeSocketValueVector>()->value);
212 copy_v4_v4(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 case SOCK_MENU: {
221 /* GPUMaterial doesn't support int, so it is stored as a float. */
222 const int32_t value = input->default_value_typed<bNodeSocketValueMenu>()->value;
223 stack.vec[0] = int(value);
224 break;
225 }
226 case SOCK_STRING:
227 /* Single only types do not support GPU code path. */
230 break;
231 default:
233 break;
234 }
235}
236
237static const char *get_set_function_name(const ResultType type)
238{
239 switch (type) {
241 return "set_value";
242 case ResultType::Int:
243 /* GPUMaterial doesn't support int, so it is passed as a float. */
244 return "set_value";
245 case ResultType::Bool:
246 /* GPUMaterial doesn't support bool, so it is passed as a float. */
247 return "set_value";
249 return "set_rgb";
251 return "set_rgba";
253 return "set_rgba";
255 /* GPUMaterial doesn't support float2, so it is passed as a float3 with z ignored. */
256 return "set_rgb";
257 case ResultType::Int2:
258 /* GPUMaterial doesn't support float2, so it is passed as a float3 with z ignored. */
259 return "set_rgb";
260 case ResultType::Menu:
261 /* GPUMaterial doesn't support int, so it is passed as a float. */
262 return "set_value";
264 /* Single only types do not support GPU code path. */
267 break;
268 }
269
271 return nullptr;
272}
273
274void ShaderOperation::link_node_input_constant(const DInputSocket input, const DInputSocket origin)
275{
276 ShaderNode &node = *shader_nodes_.lookup(input.node());
277 GPUNodeStack &stack = node.get_input(input->identifier);
278
279 /* Create a constant or a uniform link that carry the value of the origin. Use a constant for
280 * socket types that rarely change like booleans and menus, while use a uniform for socket type
281 * that might change a lot to avoid excessive shader recompilation. */
282 initialize_input_stack_value(origin, stack);
283 const bool use_as_constant = ELEM(origin->type, SOCK_BOOLEAN, SOCK_MENU);
284 GPUNodeLink *link = use_as_constant ? GPU_constant(stack.vec) : GPU_uniform(stack.vec);
285
286 const ResultType type = get_node_socket_result_type(origin.bsocket());
287 const char *function_name = get_set_function_name(type);
288 GPU_link(material_, function_name, link, &stack.link);
289}
290
291void ShaderOperation::link_node_input_implicit(const DInputSocket input, const DInputSocket origin)
292{
293 ShaderNode &node = *shader_nodes_.lookup(input.node());
294 GPUNodeStack &stack = node.get_input(input->identifier);
295
296 const InputDescriptor origin_descriptor = input_descriptor_from_input_socket(origin.bsocket());
297 const ImplicitInput implicit_input = origin_descriptor.implicit_input;
298
299 /* Inherit the type and implicit input of the origin input since doing implicit conversion inside
300 * the shader operation is much cheaper. */
301 InputDescriptor input_descriptor = input_descriptor_from_input_socket(input.bsocket());
302 input_descriptor.type = origin_descriptor.type;
303 input_descriptor.implicit_input = implicit_input;
304
305 /* An input was already declared for that implicit input, so no need to declare it again and we
306 * just link it. */
307 if (implicit_input_to_material_attribute_map_.contains(implicit_input)) {
308 /* But first we update the domain priority of the input descriptor to be the higher priority of
309 * the existing descriptor and the descriptor of the new input socket. That's because the same
310 * implicit input might be used in inputs inside the shader operation which have different
311 * priorities. */
312 InputDescriptor &existing_input_descriptor = this->get_input_descriptor(
314 existing_input_descriptor.domain_priority = math::min(
315 existing_input_descriptor.domain_priority, input_descriptor.domain_priority);
316
317 /* Link the attribute representing the shader operation input corresponding to the implicit
318 * input. */
319 stack.link = implicit_input_to_material_attribute_map_.lookup(implicit_input);
320 return;
321 }
322
323 const int implicit_input_index = implicit_inputs_to_input_identifiers_map_.size();
324 const std::string input_identifier = "implicit_input" + std::to_string(implicit_input_index);
325 declare_input_descriptor(input_identifier, input_descriptor);
326
327 /* Map the implicit input to the identifier of the operation input that was declared for it. */
328 implicit_inputs_to_input_identifiers_map_.add_new(implicit_input, input_identifier);
329
330 /* Add a new GPU attribute representing an input to the GPU material. Instead of using the
331 * attribute directly, we link it to an appropriate set function and use its output link instead.
332 * This is needed because the `gputype` member of the attribute is only initialized if it is
333 * linked to a GPU node. */
334 GPUNodeLink *attribute_link;
335 GPU_link(material_,
336 get_set_function_name(input_descriptor.type),
337 GPU_attribute(material_, CD_AUTO_FROM_NAME, input_identifier.c_str()),
338 &attribute_link);
339
340 /* Map the implicit input to the attribute that was created for it. */
341 implicit_input_to_material_attribute_map_.add(implicit_input, attribute_link);
342
343 /* Link the attribute representing the shader operation input corresponding to the implicit
344 * input. */
345 stack.link = attribute_link;
346}
347
348void ShaderOperation::link_node_input_internal(DInputSocket input_socket,
349 DOutputSocket output_socket)
350{
351 ShaderNode &output_node = *shader_nodes_.lookup(output_socket.node());
352 GPUNodeStack &output_stack = output_node.get_output(output_socket->identifier);
353
354 ShaderNode &input_node = *shader_nodes_.lookup(input_socket.node());
355 GPUNodeStack &input_stack = input_node.get_input(input_socket->identifier);
356
357 input_stack.link = output_stack.link;
358}
359
360void ShaderOperation::link_node_input_external(DInputSocket input_socket,
361 DOutputSocket output_socket)
362{
363
364 ShaderNode &node = *shader_nodes_.lookup(input_socket.node());
365 GPUNodeStack &stack = node.get_input(input_socket->identifier);
366
367 if (!output_to_material_attribute_map_.contains(output_socket)) {
368 /* No input was declared for that output yet, so declare it. */
369 declare_operation_input(input_socket, output_socket);
370 }
371 else {
372 /* An input was already declared for that same output socket, so no need to declare it again.
373 * But we update the domain priority of the input descriptor to be the higher priority of the
374 * existing descriptor and the descriptor of the new input socket. That's because the same
375 * output might be connected to multiple inputs inside the shader operation which have
376 * different priorities. */
377 const std::string input_identifier = outputs_to_declared_inputs_map_.lookup(output_socket);
378 InputDescriptor &input_descriptor = this->get_input_descriptor(input_identifier);
379 input_descriptor.domain_priority = math::min(
380 input_descriptor.domain_priority,
382
383 /* Increment the input's reference count. */
384 inputs_to_reference_counts_map_.lookup(input_identifier)++;
385 }
386
387 /* Link the attribute representing the shader operation input corresponding to the given output
388 * socket. */
389 stack.link = output_to_material_attribute_map_.lookup(output_socket);
390}
391
392void ShaderOperation::declare_operation_input(DInputSocket input_socket,
393 DOutputSocket output_socket)
394{
395 const int input_index = output_to_material_attribute_map_.size();
396 std::string input_identifier = "input" + std::to_string(input_index);
397
398 /* Declare the input descriptor for this input and prefer to declare its type to be the same as
399 * the type of the output socket because doing type conversion in the shader is much cheaper. */
400 InputDescriptor input_descriptor = input_descriptor_from_input_socket(input_socket.bsocket());
401 input_descriptor.type = get_node_socket_result_type(output_socket.bsocket());
402 declare_input_descriptor(input_identifier, input_descriptor);
403
404 /* Add a new GPU attribute representing an input to the GPU material. Instead of using the
405 * attribute directly, we link it to an appropriate set function and use its output link instead.
406 * This is needed because the `gputype` member of the attribute is only initialized if it is
407 * linked to a GPU node. */
408 GPUNodeLink *attribute_link;
409 GPU_link(material_,
410 get_set_function_name(input_descriptor.type),
411 GPU_attribute(material_, CD_AUTO_FROM_NAME, input_identifier.c_str()),
412 &attribute_link);
413
414 /* Map the output socket to the attribute that was created for it. */
415 output_to_material_attribute_map_.add(output_socket, attribute_link);
416
417 /* Map the identifier of the operation input to the output socket it is linked to. */
418 inputs_to_linked_outputs_map_.add_new(input_identifier, output_socket);
419
420 /* Map the output socket to the identifier of the operation input that was declared for it. */
421 outputs_to_declared_inputs_map_.add_new(output_socket, input_identifier);
422
423 /* Map the identifier of the operation input to a reference count of 1, this will later be
424 * incremented if that same output was referenced again. */
425 inputs_to_reference_counts_map_.add_new(input_identifier, 1);
426}
427
428void ShaderOperation::populate_results_for_node(DNode node)
429{
430 const DOutputSocket preview_output = find_preview_output_socket(node);
431
432 for (const bNodeSocket *output : node->output_sockets()) {
433 const DOutputSocket doutput{node.context(), output};
434
436 continue;
437 }
438
439 /* If any of the nodes linked to the output are not part of the shader operation but are part
440 * of the execution schedule, then an output result needs to be populated for it. */
441 const bool is_operation_output = is_output_linked_to_node_conditioned(
442 doutput,
443 [&](DNode node) { return schedule_.contains(node) && !compile_unit_.contains(node); });
444
445 /* If the output is used as the node preview, then an output result needs to be populated for
446 * it, and we additionally keep track of that output to later compute the previews from. */
447 const bool is_preview_output = doutput == preview_output;
448 if (is_preview_output) {
449 preview_outputs_.add(doutput);
450 }
451
452 if (is_operation_output || is_preview_output) {
453 populate_operation_result(doutput);
454 }
455 }
456}
457
458static const char *get_store_function_name(ResultType type)
459{
460 switch (type) {
462 return "node_compositor_store_output_float";
463 case ResultType::Int:
464 return "node_compositor_store_output_int";
465 case ResultType::Bool:
466 return "node_compositor_store_output_bool";
468 return "node_compositor_store_output_float3";
470 return "node_compositor_store_output_color";
472 return "node_compositor_store_output_float4";
474 return "node_compositor_store_output_float2";
475 case ResultType::Int2:
476 return "node_compositor_store_output_int2";
477 case ResultType::Menu:
478 return "node_compositor_store_output_menu";
480 /* Single only types do not support GPU code path. */
483 break;
484 }
485
487 return nullptr;
488}
489
490void ShaderOperation::populate_operation_result(DOutputSocket output_socket)
491{
493 std::string output_identifier = "output" + std::to_string(output_id);
494
495 const ResultType result_type = get_node_socket_result_type(output_socket.bsocket());
496 const Result result = context().create_result(result_type);
497 populate_result(output_identifier, result);
498
499 /* Map the output socket to the identifier of the newly populated result. */
500 output_sockets_to_output_identifiers_map_.add_new(output_socket, output_identifier);
501
502 ShaderNode &node = *shader_nodes_.lookup(output_socket.node());
503 GPUNodeLink *output_link = node.get_output(output_socket->identifier).link;
504
505 /* Link the output node stack to an output storer storing in the appropriate result. The result
506 * is identified by its index in the operation and the index is encoded as a float to be passed
507 * to the GPU function. Additionally, create an output link from the storer node to declare as an
508 * output to the GPU material. This storer output link is a dummy link in the sense that its
509 * value is ignored since it is already written in the output, but it is used to track nodes that
510 * contribute to the output of the compositor node tree. */
511 GPUNodeLink *storer_output_link;
512 GPUNodeLink *id_link = GPU_constant((float *)&output_id);
513 const char *store_function_name = get_store_function_name(result_type);
514 GPU_link(material_, store_function_name, id_link, output_link, &storer_output_link);
515
516 /* Declare the output link of the storer node as an output of the GPU material to help the GPU
517 * code generator to track the nodes that contribute to the output of the shader. */
518 GPU_material_add_output_link_composite(material_, storer_output_link);
519}
520
521using namespace gpu::shader;
522
523void ShaderOperation::generate_code(void *thunk,
524 GPUMaterial *material,
525 GPUCodegenOutput *code_generator_output)
526{
527 ShaderOperation *operation = static_cast<ShaderOperation *>(thunk);
528 ShaderCreateInfo &shader_create_info = *reinterpret_cast<ShaderCreateInfo *>(
529 code_generator_output->create_info);
530
531 shader_create_info.local_group_size(16, 16);
532
533 /* Add implementation for implicit conversion operations inserted by the code generator. This
534 * file should include the functions [float|vec3|vec4]_from_[float|vec3|vec4]. */
535 shader_create_info.typedef_source("gpu_shader_compositor_type_conversion.glsl");
536
537 /* The source shader is a compute shader with a main function that calls the dynamically
538 * generated evaluate function. The evaluate function includes the serialized GPU material graph
539 * preceded by code that initialized the inputs of the operation. Additionally, the storer
540 * functions that writes the outputs are defined outside the evaluate function. */
541 shader_create_info.compute_source("gpu_shader_compositor_main.glsl");
542
543 std::string store_code = operation->generate_code_for_outputs(shader_create_info);
544 shader_create_info.generated_sources.append(
545 {"gpu_shader_compositor_store.glsl", {}, store_code});
546
547 std::string eval_code;
548 eval_code += "void evaluate()\n{\n";
549
550 eval_code += operation->generate_code_for_inputs(material, shader_create_info);
551
552 eval_code += code_generator_output->composite.serialized;
553
554 eval_code += "}\n";
555
556 shader_create_info.generated_sources.append({"gpu_shader_compositor_eval.glsl",
557 code_generator_output->composite.dependencies,
558 eval_code});
559}
560
561/* Texture storers in the shader always take a [i]vec4 as an argument, so encode each type in an
562 * [i]vec4 appropriately. */
564{
565 switch (type) {
567 return "vec4(value)";
568 case ResultType::Int:
569 /* GPUMaterial doesn't support int, so it is passed as a float, and we need to convert it
570 * back to int before writing it. */
571 return "ivec4(int(value))";
572 case ResultType::Bool:
573 /* GPUMaterial doesn't support bool, so it is passed as a float and stored as an int, and we
574 * need to convert it back to bool and then to an int before writing it. */
575 return "ivec4(bool(value))";
577 return "vec4(value, 0.0)";
579 return "value";
581 return "value";
583 /* GPUMaterial doesn't support float2, so it is passed as a float3, and we need to convert it
584 * back to float2 before writing it. */
585 return "vec4(value.xy, 0.0, 0.0)";
586 case ResultType::Int2:
587 /* GPUMaterial doesn't support int2, so it is passed as a float3, and we need to convert it
588 * back to int2 before writing it. */
589 return "ivec4(ivec2(value.xy), 0, 0)";
590 case ResultType::Menu:
591 /* GPUMaterial doesn't support int, so it is passed as a float, and we need to convert it
592 * back to int before writing it. */
593 return "ivec4(int(value))";
595 /* Single only types do not support GPU code path. */
598 break;
599 }
600
602 return nullptr;
603}
604
606{
607 switch (type) {
613 return ImageType::Float2D;
614 case ResultType::Int:
615 case ResultType::Int2:
616 case ResultType::Bool:
617 case ResultType::Menu:
618 return ImageType::Int2D;
620 /* Single only types do not support GPU code path. */
623 break;
624 }
625
627 return ImageType::Float2D;
628}
629
630std::string ShaderOperation::generate_code_for_outputs(ShaderCreateInfo &shader_create_info)
631{
632 const std::string store_float_function_header = "void store_float(const uint id, float value)";
633 /* GPUMaterial doesn't support int, so it is passed as a float. */
634 const std::string store_int_function_header = "void store_int(const uint id, float value)";
635 /* GPUMaterial doesn't support bool, so it is passed as a float. */
636 const std::string store_bool_function_header = "void store_bool(const uint id, float value)";
637 const std::string store_float3_function_header = "void store_float3(const uint id, vec3 value)";
638 const std::string store_color_function_header = "void store_color(const uint id, vec4 value)";
639 const std::string store_float4_function_header = "void store_float4(const uint id, vec4 value)";
640 /* GPUMaterial doesn't support float2, so it is passed as a float3. */
641 const std::string store_float2_function_header = "void store_float2(const uint id, vec3 value)";
642 /* GPUMaterial doesn't support int2, so it is passed as a float3. */
643 const std::string store_int2_function_header = "void store_int2(const uint id, vec3 value)";
644 /* GPUMaterial doesn't support int, so it is passed as a float. */
645 const std::string store_menu_function_header = "void store_menu(const uint id, float value)";
646
647 /* Each of the store functions is essentially a single switch case on the given ID, so start by
648 * opening the function with a curly bracket followed by opening a switch statement in each of
649 * the functions. */
650 std::stringstream store_float_function;
651 std::stringstream store_int_function;
652 std::stringstream store_bool_function;
653 std::stringstream store_float3_function;
654 std::stringstream store_color_function;
655 std::stringstream store_float4_function;
656 std::stringstream store_float2_function;
657 std::stringstream store_int2_function;
658 std::stringstream store_menu_function;
659 const std::string store_function_start = "\n{\n switch (id) {\n";
660 store_float_function << store_float_function_header << store_function_start;
661 store_int_function << store_int_function_header << store_function_start;
662 store_bool_function << store_bool_function_header << store_function_start;
663 store_float3_function << store_float3_function_header << store_function_start;
664 store_color_function << store_color_function_header << store_function_start;
665 store_float4_function << store_float4_function_header << store_function_start;
666 store_float2_function << store_float2_function_header << store_function_start;
667 store_int2_function << store_int2_function_header << store_function_start;
668 store_menu_function << store_menu_function_header << store_function_start;
669
670 int output_index = 0;
672 const Result &result = get_result(output_identifier);
673
674 /* Add a write-only image for this output where its values will be written. */
675 shader_create_info.image(output_index,
676 result.get_gpu_texture_format(),
677 Qualifier::write,
679 output_identifier,
680 Frequency::PASS);
681 output_index++;
682
683 /* Add a case for the index of this output followed by a break statement. */
684 std::stringstream case_code;
685 const std::string store_expression = glsl_store_expression_from_result_type(result.type());
686 const std::string texel = ", ivec2(gl_GlobalInvocationID.xy), ";
687 case_code << " case " << StringRef(output_identifier).drop_known_prefix("output") << ":\n"
688 << " imageStore(" << output_identifier << texel << store_expression << ");\n"
689 << " break;\n";
690
691 /* Only add the case to the function with the matching type. */
692 switch (result.type()) {
694 store_float_function << case_code.str();
695 break;
696 case ResultType::Int:
697 store_int_function << case_code.str();
698 break;
699 case ResultType::Bool:
700 store_bool_function << case_code.str();
701 break;
703 store_float3_function << case_code.str();
704 break;
706 store_color_function << case_code.str();
707 break;
709 store_float4_function << case_code.str();
710 break;
712 store_float2_function << case_code.str();
713 break;
714 case ResultType::Int2:
715 store_int2_function << case_code.str();
716 break;
717 case ResultType::Menu:
718 store_menu_function << case_code.str();
719 break;
721 /* Single only types do not support GPU code path. */
724 break;
725 }
726 }
727
728 /* Close the previously opened switch statement as well as the function itself. */
729 const std::string store_function_end = " }\n}\n\n";
730 store_float_function << store_function_end;
731 store_int_function << store_function_end;
732 store_bool_function << store_function_end;
733 store_float3_function << store_function_end;
734 store_color_function << store_function_end;
735 store_float4_function << store_function_end;
736 store_float2_function << store_function_end;
737 store_int2_function << store_function_end;
738 store_menu_function << store_function_end;
739
740 return store_float_function.str() + store_int_function.str() + store_bool_function.str() +
741 store_float3_function.str() + store_color_function.str() + store_float4_function.str() +
742 store_float2_function.str() + store_int2_function.str() + store_menu_function.str();
743}
744
746{
747 switch (type) {
749 return "float";
750 case ResultType::Int:
751 /* GPUMaterial doesn't support int, so it is passed as a float. */
752 return "float";
753 case ResultType::Bool:
754 /* GPUMaterial doesn't support bool, so it is passed as a float. */
755 return "float";
757 return "vec3";
760 return "vec4";
762 /* GPUMaterial doesn't support float2, so it is passed as a float3 with z ignored. */
763 return "vec3";
764 case ResultType::Int2:
765 /* GPUMaterial doesn't support int2, so it is passed as a float3 with z ignored. */
766 return "vec3";
767 case ResultType::Menu:
768 /* GPUMaterial doesn't support int, so it is passed as a float. */
769 return "float";
771 /* Single only types do not support GPU code path. */
774 break;
775 }
776
778 return nullptr;
779}
780
781/* Texture loaders in the shader always return an [i]vec4, so a swizzle is needed to retrieve the
782 * actual value for each type. */
784{
785 switch (type) {
787 case ResultType::Int:
788 case ResultType::Bool:
789 case ResultType::Menu:
790 return "x";
792 return "xyz";
794 return "rgba";
796 return "xyzw";
798 /* GPUMaterial doesn't support float2, so it is passed as a float3 with z ignored. */
799 return "xyz";
800 case ResultType::Int2:
801 /* GPUMaterial doesn't support float2, so it is passed as a float3 with z ignored. */
802 return "xyz";
804 /* Single only types do not support GPU code path. */
807 break;
808 }
809
811 return nullptr;
812}
813
814std::string ShaderOperation::generate_code_for_inputs(GPUMaterial *material,
815 ShaderCreateInfo &shader_create_info)
816{
817 /* The attributes of the GPU material represents the inputs of the operation. */
818 ListBase attributes = GPU_material_attributes(material);
819
820 if (BLI_listbase_is_empty(&attributes)) {
821 return "";
822 }
823
824 std::string code;
825
826 /* Add a texture sampler for each of the inputs with the same name as the attribute, we start
827 * counting the sampler slot location from the number of textures in the material, since some
828 * sampler slots may be reserved for things like color band textures. */
829 const ListBase textures = GPU_material_textures(material);
830 int input_slot_location = BLI_listbase_count(&textures);
831 LISTBASE_FOREACH (GPUMaterialAttribute *, attribute, &attributes) {
832 const InputDescriptor &input_descriptor = get_input_descriptor(attribute->name);
833 shader_create_info.sampler(input_slot_location,
834 gpu_image_type_from_result_type(input_descriptor.type),
835 attribute->name,
836 Frequency::PASS);
837 input_slot_location++;
838 }
839
840 /* Declare a struct called var_attrs that includes an appropriately typed member for each of the
841 * inputs. The names of the members should be the letter v followed by the ID of the attribute
842 * corresponding to the input. Such names are expected by the code generator. */
843 std::stringstream declare_attributes;
844 declare_attributes << "struct {\n";
845 LISTBASE_FOREACH (GPUMaterialAttribute *, attribute, &attributes) {
846 const InputDescriptor &input_descriptor = get_input_descriptor(attribute->name);
847 const std::string type = glsl_type_from_result_type(input_descriptor.type);
848 declare_attributes << " " << type << " v" << attribute->id << ";\n";
849 }
850 declare_attributes << "} var_attrs;\n\n";
851
852 code += declare_attributes.str();
853
854 /* The texture loader utilities are needed to sample the input textures and initialize the
855 * attributes. */
856 shader_create_info.typedef_source("gpu_shader_compositor_texture_utilities.glsl");
857
858 /* Initialize each member of the previously declared struct by loading its corresponding texture
859 * with an appropriate swizzle and cast for its type. */
860 std::stringstream initialize_attributes;
861 LISTBASE_FOREACH (GPUMaterialAttribute *, attribute, &attributes) {
862 const InputDescriptor &input_descriptor = get_input_descriptor(attribute->name);
863 const std::string swizzle = glsl_swizzle_from_result_type(input_descriptor.type);
864 const std::string type = glsl_type_from_result_type(input_descriptor.type);
865 initialize_attributes << "var_attrs.v" << attribute->id << " = " << type << "("
866 << "texture_load(" << attribute->name
867 << ", ivec2(gl_GlobalInvocationID.xy))." << swizzle << ")"
868 << ";\n";
869 }
870 initialize_attributes << "\n";
871
872 code += initialize_attributes.str();
873
874 return code;
875}
876
877} // namespace blender::compositor
#define BLI_assert_unreachable()
Definition BLI_assert.h:93
#define BLI_assert(a)
Definition BLI_assert.h:46
#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 zero_v4(float r[4])
unsigned int uint
#define ELEM(...)
@ CD_AUTO_FROM_NAME
struct ListBase ListBase
@ SOCK_INT
@ SOCK_VECTOR
@ SOCK_BOOLEAN
@ SOCK_FLOAT
@ SOCK_STRING
@ SOCK_RGBA
@ SOCK_MENU
struct bNodeSocket bNodeSocket
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)
blender::gpu::Shader * GPU_material_get_shader(GPUMaterial *material)
void GPU_material_free_single(GPUMaterial *material)
@ GPU_MAT_COMPOSITOR
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)
blender::gpu::UniformBuf * GPU_material_uniform_buffer_get(GPUMaterial *material)
GPUNodeLink * GPU_attribute(GPUMaterial *mat, eCustomDataType type, const char *name)
bool GPU_link(GPUMaterial *mat, const char *name,...)
GPUNodeLink * GPU_uniform(const float *num)
int GPU_shader_get_ubo_binding(blender::gpu::Shader *shader, const char *name)
int GPU_shader_get_sampler_binding(blender::gpu::Shader *shader, const char *name)
void GPU_shader_bind(blender::gpu::Shader *shader, const blender::gpu::shader::SpecializationConstants *constants_state=nullptr)
void GPU_shader_unbind()
void GPU_texture_image_unbind_all()
void GPU_texture_unbind_all()
void GPU_texture_bind(blender::gpu::Texture *texture, int unit)
void GPU_uniformbuf_bind(blender::gpu::UniformBuf *ubo, int slot)
#define GPU_UBO_BLOCK_NAME
void GPU_uniformbuf_debug_unbind_all()
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_
static bool is_single_value_only_type(ResultType type)
Definition result.cc:44
void bind_as_texture(gpu::Shader *shader, const char *texture_name) const
Definition result.cc:487
void bind_as_image(gpu::Shader *shader, const char *image_name, bool read=false) const
Definition result.cc:498
ShaderOperation(Context &context, PixelCompileUnit &compile_unit, const Schedule &schedule)
nullptr float
#define input
#define output
TEX_TEMPLATE DataVec texture(T, FltCoord, float=0.0f) RET
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:113
void compute_dispatch_threads_at_least(gpu::Shader *shader, int2 threads_range, int2 local_size=int2(16))
Definition utilities.cc:196
DSocket get_input_origin_socket(DInputSocket input)
Definition utilities.cc:32
VectorSet< DNode > Schedule
static const char * glsl_store_expression_from_result_type(ResultType type)
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:226
ResultType get_node_socket_result_type(const bNodeSocket *socket)
Definition utilities.cc:102
InputDescriptor input_descriptor_from_input_socket(const bNodeSocket *socket)
Definition utilities.cc:169
static const char * get_store_function_name(ResultType type)
bool is_socket_available(const bNodeSocket *socket)
Definition utilities.cc:27
static const char * get_set_function_name(const ResultType type)
T min(const T &a, const T &b)
VecBase< float, 4 > float4
GPUGraphOutput composite
GPUShaderCreateInfo * create_info
std::string serialized
blender::Vector< blender::StringRefNull > dependencies
GPUNodeLink * link
Describe inputs & outputs, stage interfaces, resources and sources of a shader. If all data is correc...
Self & compute_source(StringRefNull filename)
GeneratedSourceList generated_sources
Self & typedef_source(StringRefNull filename)
Self & sampler(int slot, ImageType type, StringRefNull name, Frequency freq=Frequency::PASS, GPUSamplerState sampler=GPUSamplerState::internal_sampler())
Self & image(int slot, TextureFormat format, Qualifier qualifiers, ImageReadWriteType type, StringRefNull name, Frequency freq=Frequency::PASS)
Self & local_group_size(int local_size_x, int local_size_y=1, int local_size_z=1)
i
Definition text_draw.cc:230