301 lines
16 KiB
C++
301 lines
16 KiB
C++
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
|
*
|
|
* SPDX-License-Identifier: GPL-2.0-or-later */
|
|
|
|
#pragma once
|
|
|
|
#include "BLI_math_matrix_types.hh"
|
|
#include "BLI_math_vector_types.hh"
|
|
|
|
#include "GPU_shader.h"
|
|
#include "GPU_texture.h"
|
|
|
|
#include "COM_domain.hh"
|
|
#include "COM_texture_pool.hh"
|
|
|
|
namespace blender::realtime_compositor {
|
|
|
|
/* To add a new type, update the get_texture_format() and change_texture_format_precision()
|
|
* functions. */
|
|
enum class ResultType : uint8_t {
|
|
/* The following types are user facing and can be used as inputs and outputs of operations. They
|
|
* either represent the base type of the result texture or a single value result. The color type
|
|
* represents an RGBA color. And the vector type represents a generic 4-component vector, which
|
|
* can encode two 2D vectors, one 3D vector with the last component ignored, or other dimensional
|
|
* data. */
|
|
Float,
|
|
Vector,
|
|
Color,
|
|
|
|
/* The following types are for internal use only, not user facing, and can't be used as inputs
|
|
* and outputs of operations. Furthermore, they can't be single values and thus always need to be
|
|
* allocated as textures. It follows that they needn't be handled in implicit operations like
|
|
* type conversion, shader, or single value reduction operations. */
|
|
Float2,
|
|
Float3,
|
|
Int2,
|
|
};
|
|
|
|
enum class ResultPrecision : uint8_t {
|
|
Full,
|
|
Half,
|
|
};
|
|
|
|
/* ------------------------------------------------------------------------------------------------
|
|
* 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
|
|
* the number of operations that needs this result. Once each operation that needs the result no
|
|
* longer needs it, the release method is called and the reference count is decremented, until it
|
|
* reaches zero, where the result's texture is then released. Since results are eventually
|
|
* decremented to zero by the end of every evaluation, the reference count is restored before every
|
|
* 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 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, 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 result's texture or single value. */
|
|
ResultType type_;
|
|
/* The precision of the result's texture, host-side single values are always stored using full
|
|
* precision. */
|
|
ResultPrecision precision_ = ResultPrecision::Half;
|
|
/* If true, the result is a single value, otherwise, the result is a texture. */
|
|
bool is_single_value_;
|
|
/* A GPU texture storing the result data. This will be a 1x1 texture if the result is a single
|
|
* value, the value of which will be identical to that of the value member. See class description
|
|
* for more information. */
|
|
GPUTexture *texture_ = nullptr;
|
|
/* The texture pool used to allocate the texture of the result, this should be initialized during
|
|
* construction. */
|
|
TexturePool *texture_pool_ = nullptr;
|
|
/* The number of operations that currently needs this result. At the time when the result is
|
|
* computed, this member will have a value that matches initial_reference_count_. Once each
|
|
* operation that needs the result no longer needs it, the release method is called and the
|
|
* reference count is decremented, until it reaches zero, where the result's texture is then
|
|
* released. If this result have a master result, then this reference count is irrelevant and
|
|
* shadowed by the reference count of the master result. */
|
|
int reference_count_;
|
|
/* The number of operations that reference and use this result at the time when it was initially
|
|
* computed. Since reference_count_ is decremented and always becomes zero at the end of the
|
|
* evaluation, this member is used to reset the reference count of the results for later
|
|
* evaluations by calling the reset method. This member is also used to determine if this result
|
|
* should be computed by calling the should_compute method. */
|
|
int initial_reference_count_;
|
|
/* If the result is a single value, this member stores the value of the result, the value of
|
|
* which will be identical to that stored in the texture member. The active union member depends
|
|
* on the type of the result. This member is uninitialized and should not be used if the result
|
|
* is a texture. */
|
|
union {
|
|
float float_value_;
|
|
float4 vector_value_;
|
|
float4 color_value_;
|
|
};
|
|
/* 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 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.
|
|
* See that method for more information. */
|
|
Result *master_ = nullptr;
|
|
|
|
public:
|
|
/* Construct a result of the given type and precision with the given texture pool that will be
|
|
* used to allocate and release the result's texture. */
|
|
Result(ResultType type, TexturePool &texture_pool, ResultPrecision precision);
|
|
|
|
/* Identical to the standard constructor but initializes the reference count to 1. This is useful
|
|
* to construct temporary results that are created and released by the developer manually, which
|
|
* are typically used in operations that need temporary intermediate results. */
|
|
static Result Temporary(ResultType type, TexturePool &texture_pool, ResultPrecision precision);
|
|
|
|
/* Returns the appropriate texture format based on the given result type and precision. */
|
|
static eGPUTextureFormat texture_format(ResultType type, ResultPrecision precision);
|
|
|
|
/* Returns the appropriate texture format based on the result's type and precision. */
|
|
eGPUTextureFormat get_texture_format() const;
|
|
|
|
/* Declare the result to be a texture result, allocate a texture of an appropriate type with
|
|
* the size of the given domain from the result's texture pool, and set the domain of the result
|
|
* to the given domain.
|
|
*
|
|
* If the result should not be computed, that is, should_compute() returns false, yet this method
|
|
* is called, that means the result is only being allocated because the shader that computes it
|
|
* also computes another result that is actually needed, and shaders needs to have a texture
|
|
* bound to all their images units for a correct invocation, even if some of those textures are
|
|
* not needed and will eventually be discarded. In that case, since allocating the full texture
|
|
* is not needed, allocate_single_value() is called instead and the reference count is set to 1.
|
|
* This essentially allocates a dummy 1x1 texture, which works because out of bound shader writes
|
|
* to images are safe. Since this result is not referenced by any other operation, it should be
|
|
* manually released after the operation is evaluated, which is implemented by calling the
|
|
* Operation::release_unneeded_results() method. */
|
|
void allocate_texture(Domain domain);
|
|
|
|
/* Declare the result to be a single value result, allocate a texture of an appropriate
|
|
* type with size 1x1 from the result's texture pool, and set the domain to be an identity
|
|
* domain. See class description for more information. */
|
|
void allocate_single_value();
|
|
|
|
/* Allocate a single value result and set its value to zero. This is called for results whose
|
|
* value can't be computed and are considered invalid. */
|
|
void allocate_invalid();
|
|
|
|
/* Bind the texture of the result to the texture image unit with the given name in the currently
|
|
* bound given shader. This also inserts a memory barrier for texture fetches to ensure any prior
|
|
* writes to the texture are reflected before reading from it. */
|
|
void bind_as_texture(GPUShader *shader, const char *texture_name) const;
|
|
|
|
/* Bind the texture of the result to the image unit with the given name in the currently bound
|
|
* given shader. If read is true, a memory barrier will be inserted for image reads to ensure any
|
|
* prior writes to the images are reflected before reading from it. */
|
|
void bind_as_image(GPUShader *shader, const char *image_name, bool read = false) const;
|
|
|
|
/* Unbind the texture which was previously bound using bind_as_texture. */
|
|
void unbind_as_texture() const;
|
|
|
|
/* Unbind the texture which was previously bound using bind_as_image. */
|
|
void unbind_as_image() const;
|
|
|
|
/* 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. See
|
|
* the discussion above for more information. */
|
|
void pass_through(Result &target);
|
|
|
|
/* Steal the allocated data from the given source result and assign it to this result, then
|
|
* remove any references to the data from the source result. It is assumed that:
|
|
*
|
|
* - Both results are of the same type.
|
|
* - This result is not allocated but the source result is allocated.
|
|
* - Neither of the results is a proxy one, that is, has a master result.
|
|
*
|
|
* This is different from proxy results and the pass_through mechanism in that it can be used on
|
|
* temporary results. This is most useful in multi-step compositor operations where some steps
|
|
* can be optional, in that case, intermediate results can be temporary results that can
|
|
* eventually be stolen by the actual output of the operation. See the uses of the method for
|
|
* a practical example of use. */
|
|
void steal_data(Result &source);
|
|
|
|
/* Sets the transformation of the domain of the result to the given transformation. */
|
|
void set_transformation(const float3x3 &transformation);
|
|
|
|
/* Transform the result by the given transformation. This effectively pre-multiply the given
|
|
* transformation by the current transformation of the domain of the result. */
|
|
void transform(const float3x3 &transformation);
|
|
|
|
/* Get a reference to the realization options of this result. See the RealizationOptions struct
|
|
* for more information. */
|
|
RealizationOptions &get_realization_options();
|
|
|
|
/* If the result is a single value result of type float, return its float value. Otherwise, an
|
|
* uninitialized value is returned. */
|
|
float get_float_value() const;
|
|
|
|
/* If the result is a single value result of type vector, return its vector value. Otherwise, an
|
|
* uninitialized value is returned. */
|
|
float4 get_vector_value() const;
|
|
|
|
/* If the result is a single value result of type color, return its color value. Otherwise, an
|
|
* uninitialized value is returned. */
|
|
float4 get_color_value() const;
|
|
|
|
/* Same as get_float_value but returns a default value if the result is not a single value. */
|
|
float get_float_value_default(float default_value) const;
|
|
|
|
/* Same as get_vector_value but returns a default value if the result is not a single value. */
|
|
float4 get_vector_value_default(const float4 &default_value) const;
|
|
|
|
/* Same as get_color_value but returns a default value if the result is not a single value. */
|
|
float4 get_color_value_default(const float4 &default_value) const;
|
|
|
|
/* If the result is a single value result of type float, set its float value and upload it to the
|
|
* texture. Otherwise, an undefined behavior is invoked. */
|
|
void set_float_value(float value);
|
|
|
|
/* If the result is a single value result of type vector, set its vector value and upload it to
|
|
* the texture. Otherwise, an undefined behavior is invoked. */
|
|
void set_vector_value(const float4 &value);
|
|
|
|
/* If the result is a single value result of type color, set its color value and upload it to the
|
|
* texture. Otherwise, an undefined behavior is invoked. */
|
|
void set_color_value(const float4 &value);
|
|
|
|
/* Set the value of initial_reference_count_, see that member for more details. This should be
|
|
* called after constructing the result to declare the number of operations that needs it. */
|
|
void set_initial_reference_count(int count);
|
|
|
|
/* Reset the result to prepare it for a new evaluation. This should be called before evaluating
|
|
* the operation that computes this result. First, set the value of reference_count_ to the value
|
|
* of initial_reference_count_ since reference_count_ may have already been decremented to zero
|
|
* in a previous evaluation. Second, set master_ to nullptr because the result may have been
|
|
* turned into a proxy result in a previous evaluation. Other fields don't need to be reset
|
|
* because they are runtime and overwritten during evaluation. */
|
|
void reset();
|
|
|
|
/* Increment the reference count of the result by the given count. If this result have a master
|
|
* result, the reference count of the master result is incremented instead. */
|
|
void increment_reference_count(int count = 1);
|
|
|
|
/* Decrement the reference count of the result and release the its texture back into the texture
|
|
* pool if the reference count reaches zero. This should be called when an operation that used
|
|
* this result no longer needs it. If this result have a master result, the master result is
|
|
* released instead. */
|
|
void release();
|
|
|
|
/* Returns true if this result should be computed and false otherwise. The result should be
|
|
* computed if its reference count is not zero, that is, its result is used by at least one
|
|
* operation. */
|
|
bool should_compute();
|
|
|
|
/* Returns the type of the result. */
|
|
ResultType type() const;
|
|
|
|
/* Returns true if the result is a texture and false of it is a single value. */
|
|
bool is_texture() const;
|
|
|
|
/* Returns true if the result is a single value and false of it is a texture. */
|
|
bool is_single_value() const;
|
|
|
|
/* Returns true if the result is allocated. */
|
|
bool is_allocated() const;
|
|
|
|
/* Returns the allocated GPU texture of the result. */
|
|
GPUTexture *texture() const;
|
|
|
|
/* Returns the reference count of the result. If this result have a master result, then the
|
|
* reference count of the master result is returned instead. */
|
|
int reference_count() const;
|
|
|
|
/* Returns a reference to the domain of the result. See the Domain class. */
|
|
const Domain &domain() const;
|
|
};
|
|
|
|
} // namespace blender::realtime_compositor
|