diff options
author | Omar Emara <mail@OmarEmara.dev> | 2022-06-15 09:13:58 +0300 |
---|---|---|
committer | Omar Emara <mail@OmarEmara.dev> | 2022-06-15 09:13:58 +0300 |
commit | 2bb58991ad6e2dfa5807a4022c3d136d3d0e6e18 (patch) | |
tree | 71dbfa55806c09af4ddea5805c1adf304329f924 | |
parent | 08a14259472774c057862c84d1fe49c6a64c41c6 (diff) |
Realtime Compositor: Cleanup comments complete renames
22 files changed, 223 insertions, 177 deletions
diff --git a/source/blender/compositor/realtime_compositor/COM_compile_state.hh b/source/blender/compositor/realtime_compositor/COM_compile_state.hh index ba2dbe9f13e..3e2d27515f8 100644 --- a/source/blender/compositor/realtime_compositor/COM_compile_state.hh +++ b/source/blender/compositor/realtime_compositor/COM_compile_state.hh @@ -18,9 +18,9 @@ using namespace nodes::derived_node_tree_types; /* ------------------------------------------------------------------------------------------------ * Compile State * - * This is a utility class used to track the state of compilation when compiling the node tree. In - * particular, it tracks two important pieces of information, each of which is described in one of - * the following sections. + * The compile state a utility class used to track the state of compilation when compiling the node + * tree. In particular, it tracks two important pieces of information, each of which is described + * in one of the following sections. * * First, it stores a mapping between all nodes and the operations they were compiled into. The * mapping are stored independently depending on the type of the operation in the node_operations_ @@ -96,7 +96,7 @@ using namespace nodes::derived_node_tree_types; * domain is not an identity domain. Identity domains corresponds to single value results, so those * are always compatible with any domain. The domain of the compile unit is computed and set in * the add_node_to_shader_compile_unit method. When processing a node, the computed domain of node - * is compared to compile unit domain in the should_compile_shader_compile_unit method, noting + * is compared to the compile unit domain in the should_compile_shader_compile_unit method, noting * that identity domains are always compatible. Node domains are computed in the * compute_shader_node_domain method, which is analogous to Operation::compute_domain for nodes * that are not yet compiled. */ @@ -111,10 +111,10 @@ class CompileState { * more information. */ Map<DNode, NodeOperation *> node_operations_; Map<DNode, ShaderOperation *> shader_operations_; - /* A contiguous subset of the node execution schedule that contains the unit of nodes that will + /* A contiguous subset of the node execution schedule that contains the group of nodes that will * be compiled together into a Shader Operation. See the discussion in COM_evaluator.hh for * more information. */ - SubSchedule shader_compile_unit_; + ShaderCompileUnit shader_compile_unit_; /* The domain of the shader compile unit. */ Domain shader_compile_unit_domain_ = Domain::identity(); @@ -142,8 +142,8 @@ class CompileState { * the give node. */ void add_node_to_shader_compile_unit(DNode node); - /* Get the sub-schedule representing the compile unit. */ - SubSchedule &get_shader_compile_unit_sub_schedule(); + /* Get a reference to the shader compile unit. */ + ShaderCompileUnit &get_shader_compile_unit(); /* Clear the compile unit. This should be called once the compile unit is compiled to ready it to * track the next potential compile unit. */ diff --git a/source/blender/compositor/realtime_compositor/COM_context.hh b/source/blender/compositor/realtime_compositor/COM_context.hh index 68220bb50bb..08cb4008c8f 100644 --- a/source/blender/compositor/realtime_compositor/COM_context.hh +++ b/source/blender/compositor/realtime_compositor/COM_context.hh @@ -17,11 +17,12 @@ namespace blender::realtime_compositor { /* ------------------------------------------------------------------------------------------------ * Context * - * An abstract class which is used by operations to access data intrinsic to the compositor engine. - * The compositor engine should implement the class to provide the necessary functionalities for - * operations. The class also provides a reference to the texture pool which should be implemented - * by the compositor engine and provided during construction. Finally, the class have an instance - * of a shader pool for convenient shader acquisition. */ + * A Context is an abstract class that is implemented by the caller of the evaluator to provide the + * necessary data and functionalities for the correct operation of the evaluator. This includes + * providing input data like render passes and the active scene, as well as references to the data + * where the output of the evaluator will be written. The class also provides a reference to the + * texture pool which should be implemented by the caller and provided during construction. + * Finally, the class have an instance of a shader pool for convenient shader acquisition. */ class Context { private: /* A texture pool that can be used to allocate textures for the compositor efficiently. */ diff --git a/source/blender/compositor/realtime_compositor/COM_domain.hh b/source/blender/compositor/realtime_compositor/COM_domain.hh index dd763f3ca6e..dd669d5df6e 100644 --- a/source/blender/compositor/realtime_compositor/COM_domain.hh +++ b/source/blender/compositor/realtime_compositor/COM_domain.hh @@ -21,8 +21,8 @@ enum class Interpolation : uint8_t { * Realization Options * * The options that describe how an input result prefer to be realized on some other domain. This - * is used by the Realize On Domain Processor Operation to identify the appropriate method of - * realization. See the Domain class for more information. */ + * is used by the Realize On Domain Operation to identify the appropriate method of realization. + * See the Domain class for more information. */ class RealizationOptions { public: /* The interpolation method that should be used when performing realization. Since realizing a @@ -43,28 +43,56 @@ class RealizationOptions { /* ------------------------------------------------------------------------------------------------ * Domain * - * A domain is a rectangular area of a certain size in pixels that is transformed by a certain - * transformation in pixel space relative to some reference space. + * The compositor is designed in such a way as to allow compositing in an infinite virtual + * compositing space. Consequently, any result of an operation is not only represented by its image + * output, but also by its transformation in that virtual space. The transformation of the result + * together with the dimension of its image is stored and represented by a Domain. In the figure + * below, two results of different domains are illustrated on the virtual compositing space. One of + * the results is centered in space with an image dimension of 800px × 600px, and the other result + * is scaled down and translated such that it lies in the upper right quadrant of the space with an + * image dimension of 800px × 400px. The position of the domain is in pixel space, and the domain + * is considered centered if it has an identity transformation. Note that both results have the + * same resolution, but occupy different areas of the virtual compositing space. * - * Any result computed by an operation resides in a domain. The size of the domain of the result is - * the size of its texture. The transformation of the domain of the result is typically an identity - * transformation, indicating that the result is centered in space. But a transformation operation - * like the rotate, translate, or transform operations will adjust the transformation to make the - * result reside somewhere different in space. The domain of a single value result is irrelevant - * and always set to an identity domain. + * y + * ^ + * 800px x 600px | + * .---------------------|---------------------. + * | | 800px x 600px | + * | | .-------------. | + * | | | | | + * | | '-------------' | + * ------|---------------------|---------------------|------> x + * | | | + * | | | + * | | | + * | | | + * '---------------------|---------------------' + * | * - * An operation operates in a certain domain called the operation domain, it follows that the - * operation only cares about the inputs whose domain is inside or at least intersects the - * operation domain. To abstract away the different domains of the inputs, any input that have a - * different domain than the operation domain is realized on the operation domain through a - * Realize On Domain Processor Operation, except inputs whose descriptor sets skip_realization or - * expects_single_value, see InputDescriptor for more information. The realization process simply - * projects the input domain on the operation domain, copies the area of input that intersects the - * operation domain, and fill the rest with zeros or repetitions of the input domain; depending on - * the realization_options, see the RealizationOptions class for more information. This process is - * illustrated below, assuming no repetition in either directions. It follows that operations - * should expect all their inputs to have the same domain and consequently size, except for inputs - * that explicitly skip realization. + * By default, results have domains of identity transformations, that is, they are centered in + * space, but a transformation operation like the rotate, translate, or transform operations will + * adjust the transformation to make the result reside somewhere different in space. The domain of + * a single value result is irrelevant and always set to an identity domain. + * + * An operation is typically only concerned about a subset of the virtual compositing space, this + * subset is represented by a domain which is called the Operation Domain. It follows that before + * the operation itself is executed, inputs will typically be realized on the operation domain to + * be in the same domain and have the same dimension as that of the operation domain. This process + * is called Domain Realization and is implemented using an operation called the Realize On Domain + * Operation. Realization involves projecting the result onto the target domain, copying the area + * of the result that intersects the target domain, and filling the rest with zeros or repetitions + * of the result depending on the realization options that can be set by the user. Consequently, + * operations can generally expect their inputs to have the same dimension and can operate on them + * directly and transparently. For instance, if an operation takes both results illustrated in + * the figure above, and the operation has an operation domain that matches the bigger domain, the + * result with the bigger domain will not need to be realized because it already has a domain that + * matches that of the operation domain, but the result with the smaller domain will have to be + * realized into a new result that has the same domain as the domain of the bigger result. Assuming + * no repetition, the output of the realization will be an all zeros image with dimension 800px × + * 600px with a small scaled version of the smaller result copied into the upper right quadrant of + * the image. The following figure illustrates the realization process on a different set of + * results * * Realized Result * +-------------+ +-------------+ @@ -78,38 +106,37 @@ class RealizationOptions { * | Input | * +-----------+ * - * Each operation can define an arbitrary operation domain, but in most cases, the operation domain - * is inferred from the inputs. By default, the operation domain is computed as follows. Typically, - * one input of the operation is said to be the domain input and the operation domain is inferred - * from it. The domain input is determined to be the non-single value input that have the highest - * domain priority, a zero value being the highest priority. If all inputs are single values, then - * the operation domain is irrelevant and an identity domain is set. See Operation::compute_domain - * for more information. If multiple inputs have the same domain priority, the first of which will - * be considered to be the domain input. + * An operation can operate in an arbitrary operation domain, but in most cases, the operation + * domain is inferred from the inputs of the operation. In particular, one of the inputs is said to + * be the Domain Input of the operation and the operation domain is inferred from its domain. It + * follows that this particular input will not need realization, because it already has the correct + * domain. The domain input selection mechanism is as follows. Each of the inputs are assigned a + * value by the developer called the Domain Priority, the domain input is then chosen as the + * non-single value input with the highest domain priority, zero being the highest priority. See + * Operation::compute_domain for more information. * * The aforementioned logic for operation domain computation is only a default that works for most * cases, but an operation can override the compute_domain method to implement a different logic. * For instance, output nodes have an operation domain the same size as the viewport and with an * identity transformation, their operation domain doesn't depend on the inputs at all. * - * For instance, a filter operation have two inputs, a factor and a color, the latter of which - * has a domain priority of 0 and the former has a domain priority of 1. If the color input is not - * a single value, then the domain of this operation is computed to be the same size and - * transformation as the color input, because it has the highest priority. And if the factor input - * have a different size and/or transformation from the computed domain of the operation, it will - * be projected and realized on it to have the same size as described above. It follows that the - * color input, will not need to be realized because it already has the same size and - * transformation as the domain of the operation, because the operation domain is inferred from it. - * On the other hand, if the color input is a single value input, then the operation domain will be - * the same as the domain of the factor input, because it has the second highest domain priority. - * Finally, if both inputs are single value inputs, the operation domain will be an identity and is - * irrelevant. */ + * For instance, a filter operation has two inputs, a factor and a color, the latter of which is + * assigned a domain priority of 0 and the former is assigned a domain priority of 1. If the color + * input is not a single value input, then the color input is considered to be the domain input of + * the operation and the operation domain is computed to be the same domain as the color input, + * because it has the highest priority. It follows that if the factor input has a different domain + * than the computed domain of the operation, it will be projected and realized on it to have the + * same domain as described above. On the other hand, if the color input is a single value input, + * then the factor input is considered to be the domain input and the operation domain will be the + * same as the domain of the factor input, because it has the second highest domain priority. + * Finally, if both inputs are single value inputs, the operation domain will be an identity domain + * and is irrelevant, because the output will be a domain-less single value. */ class Domain { public: /* The size of the domain in pixels. */ int2 size; /* The 2D transformation of the domain defining its translation in pixels, rotation, and scale in - * 2D space. */ + * the virtual compositing space. */ float3x3 transformation; /* The options that describe how this domain prefer to be realized on some other domain. See the * RealizationOptions class for more information. */ @@ -131,7 +158,7 @@ class Domain { /* Compare the size and transformation of the domain. The realization_options are not compared * because they only describe the method of realization on another domain, which is not technically - * a proprty of the domain itself. */ + * a property of the domain itself. */ bool operator==(const Domain &a, const Domain &b); /* Inverse of the above equality operator. */ diff --git a/source/blender/compositor/realtime_compositor/COM_evaluator.hh b/source/blender/compositor/realtime_compositor/COM_evaluator.hh index cbb6e6bcfd5..e5012482a55 100644 --- a/source/blender/compositor/realtime_compositor/COM_evaluator.hh +++ b/source/blender/compositor/realtime_compositor/COM_evaluator.hh @@ -23,15 +23,16 @@ using namespace nodes::derived_node_tree_types; /* ------------------------------------------------------------------------------------------------ * Evaluator * - * The evaluator is the main class of the compositor. It is constructed from a compositor node - * tree and a compositor context. Upon calling the evaluate method, the evaluator will check if the - * node tree is already compiled into an operations stream, and if it is, it will go over it and - * evaluate the operations in order. It is then the responsibility of the caller to call the reset - * method when the node tree changes to invalidate the operations stream. A reset is also required - * if the resources used by the node tree change in structure, for instances, a change in the - * dimensions of an image used by the node tree. This is necessary because the evaluator compiles - * the node tree into an operations stream that is specifically optimized for the structure of the - * resources used by the node tree. + * The evaluator is the main class of the compositor and the entry point of its execution. The + * evaluator compiles the compositor node tree and evaluates it to compute its output. It is + * constructed from a compositor node tree and a compositor context. Upon calling the evaluate + * method, the evaluator will check if the node tree is already compiled into an operations stream, + * and if it is, it will go over it and evaluate the operations in order. It is then the + * responsibility of the caller to call the reset method when the node tree changes to invalidate + * the operations stream. A reset is also required if the resources used by the node tree change, + * for instances, when the dimensions of an image used by the node tree changes. This is necessary + * because the evaluator compiles the node tree into an operations stream that is specifically + * optimized for the structure of the resources used by the node tree. * * Otherwise, if the node tree is not yet compiled, the evaluator will compile it into an * operations stream, evaluating the operations in the process. It should be noted that operations diff --git a/source/blender/compositor/realtime_compositor/COM_input_descriptor.hh b/source/blender/compositor/realtime_compositor/COM_input_descriptor.hh index 542d31ec76b..215a92ab3b4 100644 --- a/source/blender/compositor/realtime_compositor/COM_input_descriptor.hh +++ b/source/blender/compositor/realtime_compositor/COM_input_descriptor.hh @@ -25,7 +25,7 @@ class InputDescriptor { int domain_priority = 0; /* If true, the input expects a single value, and if a non-single value is provided, a default * single value will be used instead, see the get_<type>_value_default methods in the Result - * class. It follows that this also imply skip_realization, because we don't need to realize a + * class. It follows that this also implies skip_realization, because we don't need to realize a * result that will be discarded anyways. If false, the input can work with both single and * non-single values. */ bool expects_single_value = false; diff --git a/source/blender/compositor/realtime_compositor/COM_input_single_value_operation.hh b/source/blender/compositor/realtime_compositor/COM_input_single_value_operation.hh index ad2bd17f16d..bbcd336ee11 100644 --- a/source/blender/compositor/realtime_compositor/COM_input_single_value_operation.hh +++ b/source/blender/compositor/realtime_compositor/COM_input_single_value_operation.hh @@ -24,7 +24,7 @@ class InputSingleValueOperation : public Operation { private: /* The identifier of the output. */ static const StringRef output_identifier_; - /* The input socket whose value the operation will set to its result. */ + /* The input socket whose value will be computed as the operation's result. */ DInputSocket input_socket_; public: diff --git a/source/blender/compositor/realtime_compositor/COM_node_operation.hh b/source/blender/compositor/realtime_compositor/COM_node_operation.hh index 2bf502729ed..94bc4dfd39d 100644 --- a/source/blender/compositor/realtime_compositor/COM_node_operation.hh +++ b/source/blender/compositor/realtime_compositor/COM_node_operation.hh @@ -19,12 +19,12 @@ using namespace nodes::derived_node_tree_types; /* ------------------------------------------------------------------------------------------------ * Node Operation * - * The operation class that nodes should implement and instantiate in the get_compositor_operation - * function of the bNodeType, passing the inputs given to that function to the constructor. This - * class essentially just implements a default constructor that populates output results for all - * outputs of the node as well as input descriptors for all inputs of the nodes based on their - * socket declaration. The class also provides some utility methods for easier implementation of - * nodes. */ + * A node operation is a subclass of operation that nodes should implement and instantiate in the + * get_compositor_operation function of bNodeType, passing the inputs given to that function to the + * constructor. This class essentially just implements a default constructor that populates output + * results for all outputs of the node as well as input descriptors for all inputs of the nodes + * based on their socket declaration. The class also provides some utility methods for easier + * implementation of nodes. */ class NodeOperation : public Operation { private: /* The node that this operation represents. */ diff --git a/source/blender/compositor/realtime_compositor/COM_operation.hh b/source/blender/compositor/realtime_compositor/COM_operation.hh index 21483bbf26b..b512a368c59 100644 --- a/source/blender/compositor/realtime_compositor/COM_operation.hh +++ b/source/blender/compositor/realtime_compositor/COM_operation.hh @@ -27,14 +27,16 @@ using ProcessorsVector = Vector<std::unique_ptr<SimpleOperation>>; /* ------------------------------------------------------------------------------------------------ * Operation * - * The operation is the basic unit of the compositor. Operations have a number of inputs and - * outputs that are declared during construction and are identified by string identifiers. Inputs - * are declared by calling declare_input_descriptor providing an appropriate descriptor. Those - * inputs are mapped to results computed by other operations linked to them. Such mappings are - * established by the compiler during compilation by calling the map_input_to_result method. - * Outputs are populated by calling the populate_result method, providing a result of an - * appropriate type. Those results are then allocated and computed by derived classes in their - * execute method. + * The operation is the basic unit of the compositor. The evaluator compiles the compositor node + * tree into an ordered stream of operations which are then executed in order during evaluation. + * The operation class can be sub-classed to implement a new operation. Operations have a number of + * inputs and outputs that are declared during construction and are identified by string + * identifiers. Inputs are declared by calling declare_input_descriptor providing an appropriate + * descriptor. Those inputs are mapped to the results computed by other operations whose outputs + * are linked to the inputs. Such mappings are established by the compiler during compilation by + * calling the map_input_to_result method. Outputs are populated by calling the populate_result + * method, providing a result of an appropriate type. Upon execution, the operation allocates a + * result for each of its outputs and computes their value based on its inputs and options. * * Each input may have one or more input processors, which are simple operations that process the * inputs before the operation is executed, see the discussion in COM_simple_operation.hh for more @@ -110,7 +112,7 @@ class Operation { /* Add and evaluate any needed input processors, which essentially just involves calling the * add_and_evaluate_input_processor method with the needed processors. This is called before * executing the operation to prepare its inputs. The class defines a default implementation - * which adds typically necessary processors, but derived classes can override the method to have + * which adds typically needed processors, but derived classes can override the method to have * a different implementation, extend the implementation, or remove it entirely. */ virtual void add_and_evaluate_input_processors(); diff --git a/source/blender/compositor/realtime_compositor/COM_result.hh b/source/blender/compositor/realtime_compositor/COM_result.hh index 2e483112a3a..2ac28b99346 100644 --- a/source/blender/compositor/realtime_compositor/COM_result.hh +++ b/source/blender/compositor/realtime_compositor/COM_result.hh @@ -24,12 +24,12 @@ enum class ResultType : uint8_t { /* ------------------------------------------------------------------------------------------------ * Result * - * A class that represents an output of an operation, which either stores a single value or a - * texture. An operation will output a single value result if that value would have been constant - * over the whole texture. Single value results are stored in 1x1 textures to make them easily - * accessible in shaders. But the same value is also stored in the value union member of the result - * for any host-side processing. The texture of the result is allocated from the texture pool - * referenced by the result. + * A result represents the computed value of an output of an operation. A result can either + * represent an image or a single value. A result is typed, and can be of type color, vector, or + * float. Single value results are stored in 1x1 textures to make them easily accessible in + * shaders. But the same value is also stored in the value union member of the result for any + * host-side processing. The texture of the result is allocated from the texture pool referenced by + * the result. * * Results are reference counted and their textures are released once their reference count reaches * zero. After constructing a result, the set_initial_reference_count method is called to declare @@ -40,15 +40,19 @@ enum class ResultType : uint8_t { * evaluation to its initial reference count by calling the reset method, which is why a separate * member initial_reference_count_ is stored to keep track of the initial value. * - * A result reside in a certain domain defined by its texture size and transformation, see the - * discussion in COM_domain.hh for more information. + * A result not only represents an image, but also the area it occupies in the virtual compositing + * space. This area is called the Domain of the result, see the discussion in COM_domain.hh for + * more information. * - * A result can be a proxy result that merely wraps another master result, in which case, the - * result's value is the value of its master and all reference counting is delegated to the master. - * However, a proxy result can have a different domain from its master, which is what transform - * operations do, they output a proxy result from their input and transform its domain in someway. - * Proxy results can be created by calling the pass_through method, see that method for more - * details. */ + * A result can be a proxy result that merely wraps another master result, in which case, it shares + * its values and delegates all reference counting to it. While a proxy result shares the value of + * the master result, it can have a different domain. Consequently, transformation operations are + * implemented using proxy results, where their results are proxy results of their inputs but with + * their domains transformed based on their options. Moreover, proxy results can also be used as + * the results of identity operations, that is, operations that do nothing to their inputs in + * certain configurations. In which case, the proxy result is left as is with no extra + * transformation on its domain whatsoever. Proxy results can be created by calling the + * pass_through method, see that method for more details. */ class Result { private: /* The base type of the texture or the type of the single value. */ @@ -87,7 +91,7 @@ class Result { /* The domain of the result. This only matters if the result was a texture. See the discussion in * COM_domain.hh for more information. */ Domain domain_ = Domain::identity(); - /* If not nullptr, then this result wraps and uses the texture of another master result. In this + /* If not nullptr, then this result wraps and shares the value of another master result. In this * case, calls to texture-related methods like increment_reference_count and release should * operate on the master result as opposed to this result. This member is typically set upon * calling the pass_through method, which sets this result to be the master of a target result. @@ -128,18 +132,15 @@ class Result { /* Unbind the texture which was previously bound using bind_as_image. */ void unbind_as_image() const; - /* Pass this result through to a target result. This method makes the target result a copy of - * this result, essentially having identical values between the two and consequently sharing the - * underlying texture. An exception is the initial reference count, whose value is retained and - * not copied, because it is a property of the original result and is needed for correctly + /* Pass this result through to a target result, in which case, the target result becomes a proxy + * result with this result as its master result. This is done by making the target result a copy + * of this result, essentially having identical values between the two and consequently sharing + * the underlying texture. An exception is the initial reference count, whose value is retained + * and not copied, because it is a property of the original result and is needed for correctly * resetting the result before the next evaluation. Additionally, this result is set to be the * master of the target result, by setting the master member of the target. Finally, the - * reference count of the result is incremented by the reference count of the target result. This - * is typically called in the allocate method of an operation whose input texture will not change - * and can be passed to the output directly. It should be noted that such operations can still - * adjust other properties of the result, like its domain. So for instance, the transform - * operation passes its input through to its output because it will not change it, however, it - * may adjusts its domain. */ + * reference count of the result is incremented by the reference count of the target result. See + * the discussion above for more information. */ void pass_through(Result &target); /* Transform the result by the given transformation. This effectively pre-multiply the given diff --git a/source/blender/compositor/realtime_compositor/COM_shader_node.hh b/source/blender/compositor/realtime_compositor/COM_shader_node.hh index 54df41942a9..ab5565ba88d 100644 --- a/source/blender/compositor/realtime_compositor/COM_shader_node.hh +++ b/source/blender/compositor/realtime_compositor/COM_shader_node.hh @@ -17,10 +17,14 @@ using namespace nodes::derived_node_tree_types; /* ------------------------------------------------------------------------------------------------ * Shader Node * - * A class that represents a node in a GPU material. The GPU node stacks for inputs and outputs are - * stored and populated during construction. Derived class should implement the compile method to - * implement the node and link it to the GPU material. The GPU material compiler is expected to - * initialize the input links of the node before invoking the compile method. */ + * A shader node encapsulates a compositor node tree that is capable of being used together with + * other shader nodes to construct a Shader Operation using the GPU material compiler. A GPU node + * stack for each of the node inputs and outputs is stored and populated during construction in + * order to represent the node as a GPU node inside the GPU material graph, see GPU_material.h for + * more information. Derived classes should implement the compile method to add the node and link + * it to the GPU material given to the method. The compiler is expected to initialize the input + * links of the node before invoking the compile method. See the discussion in + * COM_shader_operation.hh for more information. */ class ShaderNode { private: /* The node that this operation represents. */ diff --git a/source/blender/compositor/realtime_compositor/COM_shader_operation.hh b/source/blender/compositor/realtime_compositor/COM_shader_operation.hh index 6c19027bb7e..3303e579336 100644 --- a/source/blender/compositor/realtime_compositor/COM_shader_operation.hh +++ b/source/blender/compositor/realtime_compositor/COM_shader_operation.hh @@ -21,8 +21,9 @@ namespace blender::realtime_compositor { using namespace nodes::derived_node_tree_types; -/* A type representing a contiguous subset of the node execution schedule. */ -using SubSchedule = VectorSet<DNode>; +/* A type representing a contiguous subset of the node execution schedule that will be compiled + * into a Shader Operation. */ +using ShaderCompileUnit = VectorSet<DNode>; /* A type representing a map that associates the identifier of each input of the operation with the * output socket it is linked to. */ using InputsToLinkedOutputsMap = Map<StringRef, DOutputSocket>; @@ -33,18 +34,21 @@ using OutputSocketsToOutputIdentifiersMap = Map<DOutputSocket, StringRef>; /* ------------------------------------------------------------------------------------------------ * Shader Operation * - * An operation that compiles a contiguous subset of the node execution schedule into a single - * shader using the GPU material compiler. + * 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 of each + * 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. - * See the discussion in COM_evaluator.hh for more information on the compilation process. Each of - * the nodes inside the sub-schedule 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 <-> 4 and nodes 4 - * <-> 5. However, links that cross the boundary of the shader operation needs special handling. + * 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 * +------------------------------------------------------+ @@ -66,6 +70,7 @@ using OutputSocketsToOutputIdentifiersMap = Map<DOutputSocket, StringRef>; * 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. * Once an input is declared for a distinct output socket: + * * 1. A material texture is added to the GPU material. This texture will be bound to the result of * the output socket during evaluation. * 2. The newly added texture is mapped to the output socket in output_to_material_texture_map_ @@ -79,6 +84,7 @@ using OutputSocketsToOutputIdentifiersMap = Map<DOutputSocket, StringRef>; * 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. Once an output is * declared for an output socket: + * * 1. A material image is added to the GPU material. This image will be bound to the result of * the operation output during evaluation. This is the image where the result of that output * will be written. @@ -92,13 +98,12 @@ using OutputSocketsToOutputIdentifiersMap = Map<DOutputSocket, StringRef>; * outputs, and any necessary resources. */ class ShaderOperation : public Operation { private: - /* The execution sub-schedule that will be compiled into this shader operation. */ - SubSchedule sub_schedule_; + /* 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 construction. */ + * freed during destruction. */ GPUMaterial *material_; - /* A map that associates each node in the execution sub-schedule with an instance of its shader - * node. */ + /* 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. See the above discussion for more information. */ @@ -112,9 +117,9 @@ class ShaderOperation : public Operation { Map<DOutputSocket, GPUMaterialTexture *> output_to_material_texture_map_; public: - /* Construct and compile a GPU material from the give node execution sub-schedule by calling + /* 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, SubSchedule &sub_schedule); + ShaderOperation(Context &context, ShaderCompileUnit &compile_unit); /* Free the GPU material. */ ~ShaderOperation(); @@ -137,7 +142,8 @@ class ShaderOperation : public Operation { /* 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 result. The node execution schedule is given as an input. */ + * 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: @@ -169,8 +175,8 @@ class ShaderOperation : public Operation { /* A static callback method of interface GPUMaterialCompileFn that is passed to * GPU_material_from_callbacks to compile the GPU material. The thunk parameter will be a pointer - * to the instance of ShaderOperation that is being compiled. The method goes over the - * execution sub-schedule and does the following for each node: + * 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 diff --git a/source/blender/compositor/realtime_compositor/COM_shader_pool.hh b/source/blender/compositor/realtime_compositor/COM_shader_pool.hh index c06c871b19e..e7db398b972 100644 --- a/source/blender/compositor/realtime_compositor/COM_shader_pool.hh +++ b/source/blender/compositor/realtime_compositor/COM_shader_pool.hh @@ -12,8 +12,8 @@ namespace blender::realtime_compositor { /* ------------------------------------------------------------------------------------------------- * Shader Pool * - * A pool of shaders identified by their info name that can be reused throughout the evaluation of - * the compositor and are only freed when the shader pool is destroyed. */ + * A shader pool is a pool of shaders identified by their info name that can be reused throughout + * the evaluation of the compositor and are only freed when the shader pool is destroyed. */ class ShaderPool { private: /* The set of shaders identified by their info name that are currently available in the pool to diff --git a/source/blender/compositor/realtime_compositor/COM_simple_operation.hh b/source/blender/compositor/realtime_compositor/COM_simple_operation.hh index 0b5974f3092..2f743da50d6 100644 --- a/source/blender/compositor/realtime_compositor/COM_simple_operation.hh +++ b/source/blender/compositor/realtime_compositor/COM_simple_operation.hh @@ -12,8 +12,10 @@ namespace blender::realtime_compositor { /* ------------------------------------------------------------------------------------------------ * Simple Operation * - * A simple operation is an operation that takes exactly one input and computes exactly one - * output. */ + * A simple operation is an operation that takes exactly one input and computes exactly one output. + * Moreover, the output is guaranteed to only have a single user, that is, its reference count will + * be one. Such operations can be attached to the inputs of operations to pre-process the inputs to + * prepare them before the operation is executed.*/ class SimpleOperation : public Operation { private: /* The identifier of the output. This is constant for all operations. */ @@ -25,11 +27,11 @@ class SimpleOperation : public Operation { using Operation::Operation; /* Get a reference to the output result of the operation, this essentially calls the super - * get_result with the output identifier of the operation. */ + * get_result method with the output identifier of the operation. */ Result &get_result(); /* Map the input of the operation to the given result, this essentially calls the super - * map_input_to_result with the input identifier of the operation. */ + * map_input_to_result method with the input identifier of the operation. */ void map_input_to_result(Result *result); protected: @@ -37,25 +39,25 @@ class SimpleOperation : public Operation { void add_and_evaluate_input_processors() override; /* Get a reference to the input result of the operation, this essentially calls the super - * get_result with the input identifier of the operation. */ + * get_result method with the input identifier of the operation. */ Result &get_input(); /* Switch the result mapped to the input with the given result, this essentially calls the super - * switch_result_mapped_to_input with the input identifier of the operation. */ + * switch_result_mapped_to_input method with the input identifier of the operation. */ void switch_result_mapped_to_input(Result *result); /* Populate the result of the operation, this essentially calls the super populate_result method * with the output identifier of the operation and sets the initial reference count of the result - * to 1, since the result of a operation operation is guaranteed to have a single user. */ + * to 1, since the result of an operation operation is guaranteed to have a single user. */ void populate_result(Result result); /* Declare the descriptor of the input of the operation to be the given descriptor, this - * essentially calls the super declare_input_descriptor with the input identifier of the + * essentially calls the super declare_input_descriptor method with the input identifier of the * operation. */ void declare_input_descriptor(InputDescriptor descriptor); /* Get a reference to the descriptor of the input, this essentially calls the super - * get_input_descriptor with the input identifier of the operation. */ + * get_input_descriptor method with the input identifier of the operation. */ InputDescriptor &get_input_descriptor(); }; diff --git a/source/blender/compositor/realtime_compositor/COM_texture_pool.hh b/source/blender/compositor/realtime_compositor/COM_texture_pool.hh index cbb8a2f0186..cc6641d288f 100644 --- a/source/blender/compositor/realtime_compositor/COM_texture_pool.hh +++ b/source/blender/compositor/realtime_compositor/COM_texture_pool.hh @@ -36,12 +36,15 @@ bool operator==(const TexturePoolKey &a, const TexturePoolKey &b); /* ------------------------------------------------------------------------------------------------ * Texture Pool * - * A pool of textures that can be used to allocate textures that can be reused transparently - * throughout the evaluation of the compositor. This texture pool only pools textures throughout a - * single evaluation of the compositor and will reset after the evaluation without freeing any - * textures. Cross-evaluation pooling and freeing of unused textures is the responsibility of the - * back-end texture pool used by the allocate_texture method. In the case of the viewport - * compositor engine, this would be the global DRWTexturePool of the draw manager. */ + * A texture pool allows the allocation and reuse of textures throughout the execution of the + * compositor to avoid memory fragmentation and texture allocation overheads. The texture pool + * delegates the actual texture allocation to an allocate_texture method that should be implemented + * by the caller of the compositor evaluator, allowing a more agnostic and flexible execution that + * can be controlled by the caller. If the compositor is expected to execute frequently, like on + * every redraw, then the allocation method should use a persistent texture pool to allow + * cross-evaluation texture pooling, for instance, by using the DRWTexturePool. But if the + * evaluator is expected to execute infrequently, the allocated textures can just be freed when the + * evaluator is done, that is, when the pool is destructed. */ class TexturePool { private: /* The set of textures in the pool that are available to acquire for each distinct texture @@ -51,14 +54,14 @@ class TexturePool { public: /* Check if there is an available texture with the given specification in the pool, if such * texture exists, return it, otherwise, return a newly allocated texture. Expect the texture to - * be uncleared and contains garbage data. */ + * be uncleared and possibly contains garbage data. */ GPUTexture *acquire(int2 size, eGPUTextureFormat format); /* Shorthand for acquire with GPU_RGBA16F format. */ GPUTexture *acquire_color(int2 size); - /* Shorthand for acquire with GPU_RGBA16F format. Identical to acquire_color because vector - * are stored in RGBA textures because RGB texture have limited support. */ + /* Shorthand for acquire with GPU_RGBA16F format. Identical to acquire_color because vectors + * are stored in RGBA textures, due to the limited support for RGB textures. */ GPUTexture *acquire_vector(int2 size); /* Shorthand for acquire with GPU_R16F format. */ @@ -68,16 +71,15 @@ class TexturePool { * the texture to be one that was acquired using the same texture pool. */ void release(GPUTexture *texture); - /* Reset the texture pool by clearing all available textures. The textures are not freed. If they - * are not needed, they should be freed by the back-end texture pool used by the allocate_texture - * method. This should be called after the compositor is done evaluating. */ + /* Reset the texture pool by clearing all available textures without freeing the textures. If the + * textures will no longer be needed, they should be freed in the destructor. This should be + * called after the compositor is done evaluating. */ void reset(); private: /* Returns a newly allocated texture with the given specification. This method should be - * implemented by the compositor engine and should use a global texture pool that is persistent - * across evaluations and capable of freeing unused textures. In the case of the viewport - * compositor engine, this would be the global DRWTexturePool of the draw manager. */ + * implemented by the caller of the compositor evaluator. See the class description for more + * information. */ virtual GPUTexture *allocate_texture(int2 size, eGPUTextureFormat format) = 0; }; diff --git a/source/blender/compositor/realtime_compositor/COM_utilities.hh b/source/blender/compositor/realtime_compositor/COM_utilities.hh index 9f5e88d1fbe..f73f8db8e7c 100644 --- a/source/blender/compositor/realtime_compositor/COM_utilities.hh +++ b/source/blender/compositor/realtime_compositor/COM_utilities.hh @@ -48,8 +48,8 @@ InputDescriptor input_descriptor_from_input_socket(const InputSocketRef *socket) * both dimensions is as small as possible but at least covers the entirety of global_size assuming * the shader has a local group size given by local_size. That means that the number of invocations * might be a bit larger than global_size, so shaders has to put that into consideration. A default - * local size of 16x16 is assumed, which is the optimal local size of image processing when no - * local memory is used. */ + * local size of 16x16 is assumed, which is the optimal local size for many image processing + * shaders. */ void compute_dispatch_global(GPUShader *shader, int2 global_size, int2 local_size = int2(16)); } // namespace blender::realtime_compositor diff --git a/source/blender/compositor/realtime_compositor/intern/compile_state.cc b/source/blender/compositor/realtime_compositor/intern/compile_state.cc index ae83d630f07..9fde6568103 100644 --- a/source/blender/compositor/realtime_compositor/intern/compile_state.cc +++ b/source/blender/compositor/realtime_compositor/intern/compile_state.cc @@ -68,7 +68,7 @@ void CompileState::add_node_to_shader_compile_unit(DNode node) } } -SubSchedule &CompileState::get_shader_compile_unit_sub_schedule() +ShaderCompileUnit &CompileState::get_shader_compile_unit() { return shader_compile_unit_; } diff --git a/source/blender/compositor/realtime_compositor/intern/evaluator.cc b/source/blender/compositor/realtime_compositor/intern/evaluator.cc index 07e62d26e33..f43ec372063 100644 --- a/source/blender/compositor/realtime_compositor/intern/evaluator.cc +++ b/source/blender/compositor/realtime_compositor/intern/evaluator.cc @@ -141,11 +141,11 @@ void Evaluator::map_node_operation_inputs_to_their_results(DNode node, void Evaluator::compile_and_evaluate_shader_compile_unit(CompileState &compile_state) { /* Compile the shader compile unit into a shader operation. */ - SubSchedule &sub_schedule = compile_state.get_shader_compile_unit_sub_schedule(); - ShaderOperation *operation = new ShaderOperation(context_, sub_schedule); + ShaderCompileUnit &compile_unit = compile_state.get_shader_compile_unit(); + ShaderOperation *operation = new ShaderOperation(context_, compile_unit); - /* Map each of the nodes in the sub-schedule to the compiled operation. */ - for (DNode node : sub_schedule) { + /* Map each of the nodes in the compile unit to the compiled operation. */ + for (DNode node : compile_unit) { compile_state.map_node_to_shader_operation(node, operation); } diff --git a/source/blender/compositor/realtime_compositor/intern/reduce_to_single_value_operation.cc b/source/blender/compositor/realtime_compositor/intern/reduce_to_single_value_operation.cc index 161535cbd7c..cfa08ffc639 100644 --- a/source/blender/compositor/realtime_compositor/intern/reduce_to_single_value_operation.cc +++ b/source/blender/compositor/realtime_compositor/intern/reduce_to_single_value_operation.cc @@ -55,7 +55,7 @@ SimpleOperation *ReduceToSingleValueOperation::construct_if_needed(Context &cont return nullptr; } - /* The input is a full sized texture can can't be reduced to a single value, the operation is not + /* The input is a full sized texture and can't be reduced to a single value, the operation is not * needed. */ if (input_result.domain().size != int2(1)) { return nullptr; diff --git a/source/blender/compositor/realtime_compositor/intern/result.cc b/source/blender/compositor/realtime_compositor/intern/result.cc index 8a027805f0e..c6704001f09 100644 --- a/source/blender/compositor/realtime_compositor/intern/result.cc +++ b/source/blender/compositor/realtime_compositor/intern/result.cc @@ -38,7 +38,7 @@ void Result::allocate_texture(Domain domain) void Result::allocate_single_value() { is_single_value_ = true; - /* Single values are stored in 1x1 textures. */ + /* Single values are stored in 1x1 textures as well as the single value members. */ const int2 texture_size{1, 1}; switch (type_) { case ResultType::Float: @@ -100,7 +100,7 @@ void Result::pass_through(Result &target) /* Increment the reference count of the master by the original reference count of the target. */ increment_reference_count(target.reference_count()); - /* Make the target an exact copy of this result, but keep the initial reference count as this is + /* Make the target an exact copy of this result, but keep the initial reference count, as this is * a property of the original result and is needed for correctly resetting the result before the * next evaluation. */ const int initial_reference_count = target.initial_reference_count_; diff --git a/source/blender/compositor/realtime_compositor/intern/scheduler.cc b/source/blender/compositor/realtime_compositor/intern/scheduler.cc index 5f4c4013b17..ea7838079ec 100644 --- a/source/blender/compositor/realtime_compositor/intern/scheduler.cc +++ b/source/blender/compositor/realtime_compositor/intern/scheduler.cc @@ -100,7 +100,7 @@ using NeededBuffers = Map<DNode, int>; * - The node tree is actually a graph that allows output sharing, which is not something that was * taken into consideration in this implementation because it is difficult to correctly consider. * - Each node may allocate any number of internal buffers, which is not taken into account in this - * implementation because it really affects the output and is done by very few nodes. + * implementation because it rarely affects the output and is done by very few nodes. * - The compiler may decide to compiler the schedule differently depending on runtime information * which we can merely speculate at scheduling-time as described above. */ static NeededBuffers compute_number_of_needed_buffers(DNode output_node) diff --git a/source/blender/compositor/realtime_compositor/intern/shader_operation.cc b/source/blender/compositor/realtime_compositor/intern/shader_operation.cc index fc399b61759..e06107a66c2 100644 --- a/source/blender/compositor/realtime_compositor/intern/shader_operation.cc +++ b/source/blender/compositor/realtime_compositor/intern/shader_operation.cc @@ -28,8 +28,8 @@ namespace blender::realtime_compositor { using namespace nodes::derived_node_tree_types; -ShaderOperation::ShaderOperation(Context &context, SubSchedule &sub_schedule) - : Operation(context), sub_schedule_(sub_schedule) +ShaderOperation::ShaderOperation(Context &context, ShaderCompileUnit &compile_unit) + : Operation(context), compile_unit_(compile_unit) { material_ = GPU_material_from_callbacks( &setup_material, &compile_material, &generate_material, this); @@ -131,7 +131,7 @@ void ShaderOperation::setup_material(void *UNUSED(thunk), GPUMaterial *material) void ShaderOperation::compile_material(void *thunk, GPUMaterial *material) { ShaderOperation *operation = static_cast<ShaderOperation *>(thunk); - for (DNode node : operation->sub_schedule_) { + for (DNode node : operation->compile_unit_) { /* Instantiate a shader node for the node and add it to the shader_nodes_ map. */ ShaderNode *shader_node = node->typeinfo()->get_compositor_shader_node(node); operation->shader_nodes_.add_new(node, std::unique_ptr<ShaderNode>(shader_node)); @@ -161,7 +161,7 @@ void ShaderOperation::link_node_inputs(DNode node, GPUMaterial *material) /* If the origin node is part of the shader operation, then just map the output stack link to * the input stack link. */ - if (sub_schedule_.contains(output.node())) { + if (compile_unit_.contains(output.node())) { map_node_input(input, output); continue; } @@ -253,7 +253,7 @@ void ShaderOperation::populate_results_for_node(DNode node, GPUMaterial *materia /* If any of the nodes linked to the output are not part of the shader operation, then an * output result needs to be populated. */ const bool need_to_populate_result = is_output_linked_to_node_conditioned( - output, [&](DNode node) { return !sub_schedule_.contains(node); }); + output, [&](DNode node) { return !compile_unit_.contains(node); }); if (need_to_populate_result) { populate_operation_result(output, material); diff --git a/source/blender/compositor/realtime_compositor/intern/texture_pool.cc b/source/blender/compositor/realtime_compositor/intern/texture_pool.cc index 47cf73b1cae..1568970a030 100644 --- a/source/blender/compositor/realtime_compositor/intern/texture_pool.cc +++ b/source/blender/compositor/realtime_compositor/intern/texture_pool.cc @@ -43,8 +43,8 @@ bool operator==(const TexturePoolKey &a, const TexturePoolKey &b) GPUTexture *TexturePool::acquire(int2 size, eGPUTextureFormat format) { - /* Check if there is an available texture with the required specification, and one exists, return - * it. */ + /* Check if there is an available texture with the required specification, and if one exists, + * return it. */ const TexturePoolKey key = TexturePoolKey(size, format); Vector<GPUTexture *> &available_textures = textures_.lookup_or_add_default(key); if (!available_textures.is_empty()) { |