Welcome to mirror list, hosted at ThFree Co, Russian Federation.

COM_shader_operation.hh « realtime_compositor « compositor « blender « source - git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
blob: d03e52ac8f20e829bb7c4e6a0418d07075b002fd (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
/* SPDX-License-Identifier: GPL-2.0-or-later */

#pragma once

#include <memory>

#include "BLI_map.hh"
#include "BLI_string_ref.hh"
#include "BLI_vector_set.hh"

#include "GPU_material.h"
#include "GPU_shader.h"

#include "gpu_shader_create_info.hh"

#include "NOD_derived_node_tree.hh"

#include "COM_context.hh"
#include "COM_operation.hh"
#include "COM_scheduler.hh"

namespace blender::realtime_compositor {

using namespace nodes::derived_node_tree_types;

/* A type representing a contiguous subset of the node execution schedule that will be compiled
 * into a Shader Operation. */
using ShaderCompileUnit = VectorSet<DNode>;

/* ------------------------------------------------------------------------------------------------
 * Shader Operation
 *
 * An operation that evaluates a shader compiled from a contiguous subset of the node execution
 * schedule using the GPU material compiler, see GPU_material.h for more information. The subset
 * of the node execution schedule is called a shader compile unit, see the discussion in
 * COM_compile_state.hh for more information.
 *
 * Consider the following node graph with a node execution schedule denoted by the number on each
 * node. The compiler may decide to compile a subset of the execution schedule into a shader
 * operation, in this case, the nodes from 3 to 5 were compiled together into a shader operation.
 * This subset is called the shader compile unit. See the discussion in COM_evaluator.hh for more
 * information on the compilation process. Each of the nodes inside the compile unit implements a
 * Shader Node which is instantiated, stored in shader_nodes_, and used during compilation. See the
 * discussion in COM_shader_node.hh for more information. Links that are internal to the shader
 * operation are established between the input and outputs of the shader nodes, for instance, the
 * links between nodes 3 and 4 as well as those between nodes 4 and 5. However, links that cross
 * the boundary of the shader operation needs special handling.
 *
 *                                        Shader Operation
 *                   +------------------------------------------------------+
 * .------------.    |  .------------.  .------------.      .------------.  |  .------------.
 * |   Node 1   |    |  |   Node 3   |  |   Node 4   |      |   Node 5   |  |  |   Node 6   |
 * |            |----|--|            |--|            |------|            |--|--|            |
 * |            |  .-|--|            |  |            |  .---|            |  |  |            |
 * '------------'  | |  '------------'  '------------'  |   '------------'  |  '------------'
 *                 | +----------------------------------|-------------------+
 * .------------.  |                                    |
 * |   Node 2   |  |                                    |
 * |            |--'------------------------------------'
 * |            |
 * '------------'
 *
 * Links from nodes that are not part of the shader operation to nodes that are part of the shader
 * operation are considered inputs of the operation itself and are declared as such. For instance,
 * the link from node 1 to node 3 is declared as an input to the operation, and the same applies
 * for the links from node 2 to nodes 3 and 5. Note, however, that only one input is declared for
 * each distinct output socket, so both links from node 2 share the same input of the operation.
 * An input to the operation is declared for a distinct output socket as follows:
 *
 * - A texture is added to the shader, which will be bound to the result of the output socket
 *   during evaluation.
 * - A GPU attribute is added to the GPU material for that output socket and is linked to the GPU
 *   input stack of the inputs linked to the output socket.
 * - Code is emitted to initialize the values of the attributes by sampling the textures
 *   corresponding to each of the inputs.
 * - The newly added attribute is mapped to the output socket in output_to_material_attribute_map_
 *   to share that same attributes for all inputs linked to the same output socket.
 *
 * Links from nodes that are part of the shader operation to nodes that are not part of the shader
 * operation are considered outputs of the operation itself and are declared as such. For instance,
 * the link from node 5 to node 6 is declared as an output to the operation. An output to the
 * operation is declared for an output socket as follows:
 *
 * - An image is added in the shader where the output value will be written.
 * - A storer GPU material node that stores the value of the output is added and linked to the GPU
 *   output stack of the output. The storer will store the value in the image identified by the
 *   index of the output given to the storer.
 * - The storer functions are generated dynamically to map each index with its appropriate image.
 *
 * The GPU material code generator source is used to construct a compute shader that is then
 * dispatched during operation evaluation after binding the inputs, outputs, and any necessary
 * resources. */
class ShaderOperation : public Operation {
 private:
  /* The compile unit that will be compiled into this shader operation. */
  ShaderCompileUnit compile_unit_;
  /* The GPU material backing the operation. This is created and compiled during construction and
   * freed during destruction. */
  GPUMaterial *material_;
  /* A map that associates each node in the compile unit with an instance of its shader node. */
  Map<DNode, std::unique_ptr<ShaderNode>> shader_nodes_;
  /* A map that associates the identifier of each input of the operation with the output socket it
   * is linked to. This is needed to help the compiler establish links between operations. */
  Map<std::string, DOutputSocket> inputs_to_linked_outputs_map_;
  /* A map that associates the output socket that provides the result of an output of the operation
   * with the identifier of that output. This is needed to help the compiler establish links
   * between operations. */
  Map<DOutputSocket, std::string> output_sockets_to_output_identifiers_map_;
  /* A map that associates the output socket of a node that is not part of the shader operation to
   * the attribute that was created for it. This is used to share the same attribute with all
   * inputs that are linked to the same output socket. */
  Map<DOutputSocket, GPUNodeLink *> output_to_material_attribute_map_;

 public:
  /* Construct and compile a GPU material from the given shader compile unit by calling
   * GPU_material_from_callbacks with the appropriate callbacks. */
  ShaderOperation(Context &context, ShaderCompileUnit &compile_unit);

  /* Free the GPU material. */
  ~ShaderOperation();

  /* Allocate the output results, bind the shader and all its needed resources, then dispatch the
   * shader. */
  void execute() override;

  /* Get the identifier of the operation output corresponding to the given output socket. This is
   * called by the compiler to identify the operation output that provides the result for an input
   * by providing the output socket that the input is linked to. See
   * output_sockets_to_output_identifiers_map_ for more information. */
  StringRef get_output_identifier_from_output_socket(DOutputSocket output_socket);

  /* Get a reference to the inputs to linked outputs map of the operation. This is called by the
   * compiler to identify the output that each input of the operation is linked to for correct
   * input mapping. See inputs_to_linked_outputs_map_ for more information. */
  Map<std::string, DOutputSocket> &get_inputs_to_linked_outputs_map();

  /* Compute and set the initial reference counts of all the results of the operation. The
   * reference counts of the results are the number of operations that use those results, which is
   * computed as the number of inputs whose node is part of the schedule and is linked to the
   * output corresponding to each of the results of the operation. The node execution schedule is
   * given as an input. */
  void compute_results_reference_counts(const Schedule &schedule);

 private:
  /* Bind the uniform buffer of the GPU material as well as any color band textures needed by the
   * GPU material.  The compiled shader of the material is given as an argument and assumed to be
   * bound. */
  void bind_material_resources(GPUShader *shader);

  /* Bind the input results of the operation to the appropriate textures in the GPU material. The
   * attributes stored in output_to_material_attribute_map_ have names that match the texture
   * samplers in the shader as well as the identifiers of the operation inputs that they correspond
   * to. The compiled shader of the material is given as an argument and assumed to be bound. */
  void bind_inputs(GPUShader *shader);

  /* Bind the output results of the operation to the appropriate images in the GPU material. The
   * name of the images in the shader match the identifier of their corresponding outputs. The
   * compiled shader of the material is given as an argument and assumed to be bound. */
  void bind_outputs(GPUShader *shader);

  /* A static callback method of interface ConstructGPUMaterialFn that is passed to
   * GPU_material_from_callbacks to construct the GPU material graph. The thunk parameter will be a
   * pointer to the instance of ShaderOperation that is being compiled. The method goes over the
   * compile unit and does the following for each node:
   *
   * - Instantiate a ShaderNode from the node and add it to shader_nodes_.
   * - Link the inputs of the node if needed. The inputs are either linked to other nodes in the
   *   GPU material graph or are exposed as inputs to the shader operation itself if they are
   *   linked to nodes that are not part of the shader operation.
   * - Call the compile method of the shader node to actually add and link the GPU material graph
   *   nodes.
   * - If any of the outputs of the node are linked to nodes that are not part of the shader
   *   operation, they are exposed as outputs to the shader operation itself. */
  static void construct_material(void *thunk, GPUMaterial *material);

  /* Link the inputs of the node if needed. Unlinked inputs are ignored as they will be linked by
   * the node compile method. If the input is linked to a node that is not part of the shader
   * operation, the input will be exposed as an input to the shader operation and linked to it.
   * While if the input is linked to a node that is part of the shader operation, then it is linked
   * to that node in the GPU material node graph. */
  void link_node_inputs(DNode node, GPUMaterial *material);

  /* Given the input socket of a node that is part of the shader operation which is linked to the
   * given output socket of a node that is also part of the shader operation, just link the output
   * link of the GPU node stack of the output socket to the input link of the GPU node stack of the
   * input socket. This essentially establishes the needed links in the GPU material node graph. */
  void link_node_input_internal(DInputSocket input_socket, DOutputSocket output_socket);

  /* Given the input socket of a node that is part of the shader operation which is linked to the
   * given output socket of a node that is not part of the shader operation, declare a new
   * operation input and link it to the input link of the GPU node stack of the input socket. An
   * operation input is only declared if no input was already declared for that same output socket
   * before. */
  void link_node_input_external(DInputSocket input_socket,
                                DOutputSocket output_socket,
                                GPUMaterial *material);

  /* Given the input socket of a node that is part of the shader operation which is linked to the
   * given output socket of a node that is not part of the shader operation, declare a new input to
   * the operation that is represented in the GPU material by a newly created GPU attribute. It is
   * assumed that no operation input was declared for this same output socket before. In the
   * generate_code_for_inputs method, a texture will be added in the shader for each of the
   * declared inputs, having the same name as the attribute. Additionally, code will be emitted to
   * initialize the attributes by sampling their corresponding textures. */
  void declare_operation_input(DInputSocket input_socket,
                               DOutputSocket output_socket,
                               GPUMaterial *material);

  /* Populate the output results of the shader operation for output sockets of the given node that
   * are linked to nodes outside of the shader operation. */
  void populate_results_for_node(DNode node, GPUMaterial *material);

  /* Given the output socket of a node that is part of the shader operation which is linked to an
   * input socket of a node that is not part of the shader operation, declare a new output to the
   * operation and link it to an output storer passing in the index of the output. In the
   * generate_code_for_outputs method, an image will be added in the shader for each of the
   * declared outputs. Additionally, code will be emitted to define the storer functions that store
   * the value in the appropriate image identified by the given index. */
  void populate_operation_result(DOutputSocket output_socket, GPUMaterial *material);

  /* A static callback method of interface GPUCodegenCallbackFn that is passed to
   * GPU_material_from_callbacks to create the shader create info of the GPU material. The thunk
   * parameter will be a pointer to the instance of ShaderOperation that is being compiled.
   *
   * This method first generates the necessary code to load the inputs and store the outputs. Then,
   * it creates a compute shader from the generated sources. Finally, it adds the necessary GPU
   * resources to the shader. */
  static void generate_code(void *thunk, GPUMaterial *material, GPUCodegenOutput *code_generator);

  /* Add an image in the shader for each of the declared outputs. Additionally, emit code to define
   * the storer functions that store the given value in the appropriate image identified by the
   * given index. */
  void generate_code_for_outputs(gpu::shader::ShaderCreateInfo &shader_create_info);

  /* Add a texture will in the shader for each of the declared inputs/attributes in the operation,
   * having the same name as the attribute. Additionally, emit code to initialize the attributes by
   * sampling their corresponding textures. */
  void generate_code_for_inputs(GPUMaterial *material,
                                gpu::shader::ShaderCreateInfo &shader_create_info);
};

}  // namespace blender::realtime_compositor