Refactor: Compositor: Use generic container for results

This patch refactors the Result class in the compositor to use
GMutableSpan and std::variant to wrap the result's data. This reduces
the complexity of the code and slightly optimizes performance. This will
also make it easier to add new types and interface with other code like
multi-function procedures.

Pull Request: https://projects.blender.org/blender/blender/pulls/134112
This commit is contained in:
Omar Emara
2025-02-06 13:49:44 +01:00
committed by Omar Emara
parent 2f641739aa
commit 31444fe5b1
10 changed files with 195 additions and 432 deletions

View File

@@ -6,8 +6,11 @@
#include <type_traits>
#include <utility>
#include <variant>
#include "BLI_assert.h"
#include "BLI_cpp_type.hh"
#include "BLI_generic_span.hh"
#include "BLI_math_interp.hh"
#include "BLI_math_matrix_types.hh"
#include "BLI_math_vector.h"
@@ -29,7 +32,7 @@ class DerivedResources;
/* Make sure to update the format related static methods in the Result class. */
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
* either represent the base type of the result's image 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. */
@@ -56,10 +59,8 @@ enum class ResultPrecision : uint8_t {
enum class ResultStorageType : uint8_t {
/* Stored as a GPUTexture on the GPU. */
GPU,
/* Stored as a contiguous float buffer the CPU. */
FloatCPU,
/* Stored as a contiguous integer buffer the CPU. */
IntegerCPU,
/* Stored as a buffer on the CPU and wrapped in a GMutableSpan. */
CPU,
};
/* ------------------------------------------------------------------------------------------------
@@ -73,11 +74,11 @@ enum class ResultStorageType : uint8_t {
* pool of the context referenced by the result or it can be allocated directly from the GPU
* module, see the allocation method for more information.
*
* Results are reference counted and their textures are released once their reference count reaches
* Results are reference counted and their data 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
* reaches zero, where the result's data 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.
@@ -96,9 +97,9 @@ enum class ResultStorageType : uint8_t {
* transformation on its domain whatsoever. Proxy results can be created by calling the
* pass_through method, see that method for more details.
*
* A result can wrap an external texture that is not allocated nor managed by the result. This is
* set up by a call to the wrap_external method. In that case, when the reference count eventually
* reach zero, the texture will not be freed.
* A result can wrap external data that is not allocated nor managed by the result. This is set up
* by a call to the wrap_external method. In that case, when the reference count eventually reach
* zero, the data will not be freed.
*
* A result may store resources that are computed and cached in case they are needed by multiple
* operations. Those are called Derived Resources and can be accessed using the derived_resources
@@ -108,27 +109,27 @@ class Result {
/* The context that the result was created within, this should be initialized during
* construction. */
Context *context_ = nullptr;
/* The base type of the result's texture or single value. */
/* The base type of the result's image or single value. */
ResultType type_ = ResultType::Float;
/* The precision of the result's texture, host-side single values are always stored using full
* precision. */
/* The precision of the result's data. Only relevant for GPU textures. CPU buffers and 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. */
/* If true, the result is a single value, otherwise, the result is an image. */
bool is_single_value_ = false;
/* The type of storage used to hold the data. Used to correctly interpret the data union. */
ResultStorageType storage_type_ = ResultStorageType::GPU;
/* A texture storing the result pixel data, either stored in a GPU texture or a raw contagious
* array on CPU. 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. */
/* Stores the result's pixel data, either stored in a GPU texture or a buffer that is wrapped in
* a GMutableSpan on CPU. This will represent a 1x1 image 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. */
union {
GPUTexture *gpu_texture_ = nullptr;
float *float_texture_;
int *integer_texture_;
GMutableSpan cpu_data_;
};
/* 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
* reference count is decremented, until it reaches zero, where the result's data 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_ = 1;
@@ -139,30 +140,22 @@ class Result {
* should be computed by calling the should_compute method. */
int initial_reference_count_ = 1;
/* 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
* which will be identical to that stored in the data_ member. The active variant 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_ = float4(0.0f);
float2 float2_value_;
float3 float3_value_;
int int_value_;
int2 int2_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. */
* is not a single value. */
std::variant<float, float2, float3, float4, int, int2> single_value_ = 0.0f;
/* The domain of the result. This only matters if the result was not a single value. 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. */
* case, calls to 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;
/* If true, then the result wraps an external texture that is not allocated nor managed by the
* result. This is set up by a call to the wrap_external method. In that case, when the reference
* count eventually reach zero, the texture will not be freed. */
/* If true, then the result wraps external data that is not allocated nor managed by the result.
* This is set up by a call to the wrap_external method. In that case, when the reference count
* eventually reach zero, the data will not be freed. */
bool is_external_ = false;
/* If true, the GPU texture that holds the data was allocated from the texture pool of the
* context and should be released back into the pool instead of being freed. For CPU storage,
@@ -206,6 +199,8 @@ class Result {
/* Implicit conversion to the internal GPU texture. */
operator GPUTexture *() const;
const CPPType &get_cpp_type() const;
/* Returns the appropriate texture format based on the result's type and precision. */
eGPUTextureFormat get_gpu_texture_format() const;
@@ -234,12 +229,12 @@ class Result {
void allocate_texture(Domain domain, bool from_pool = true);
/* Declare the result to be a single value result, allocate a texture of an appropriate type with
* size 1x1 from the texture pool, and set the domain to be an identity domain. See class
* description for more information. */
* size 1x1 from the texture pool, and set the domain to be an identity domain. The value is zero
* initialized. 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. */
/* Allocate a single value result whose value is zero. This is called for results whose value
* can't be computed and are considered invalid. */
void allocate_invalid();
/* Bind the GPU texture of the result to the texture image unit with the given name in the
@@ -290,11 +285,8 @@ class Result {
* is assumed to have a lifetime that covers the evaluation of the compositor. */
void wrap_external(GPUTexture *texture);
/* Identical to GPU variant of wrap_external but wraps a float buffer instead. */
void wrap_external(float *texture, int2 size);
/* Identical to GPU variant of wrap_external but wraps an integer buffer instead. */
void wrap_external(int *texture, int2 size);
/* Identical to GPU variant of wrap_external but wraps a CPU buffer instead. */
void wrap_external(void *data, int2 size);
/* Identical to GPU variant of wrap_external but wraps whatever the given result has instead. */
void wrap_external(const Result &result);
@@ -355,7 +347,7 @@ class Result {
/* Sets the precision of the result. */
void set_precision(ResultPrecision precision);
/* Returns true if the result is a single value and false of it is a texture. */
/* Returns true if the result is a single value and false of it is an image. */
bool is_single_value() const;
/* Returns true if the result is allocated. */
@@ -371,15 +363,9 @@ class Result {
/* Computes the number of channels of the result based on its type. */
int64_t channels_count() const;
/* Returns a reference to the allocate float data. */
float *float_texture() const;
GPUTexture *gpu_texture() const;
/* Returns a reference to the allocate integer data. */
int *integer_texture() const;
/* Returns a reference to the allocated CPU data. The returned data is untyped, use the
* float_texture() or the integer_texture() methods for typed data. */
void *data() const;
GMutableSpan cpu_data() const;
/* Gets the single value stored in the result. Assumes the result stores a value of the given
* template type. */
@@ -392,7 +378,7 @@ class Result {
template<typename T> T get_single_value_default(const T &default_value) const;
/* Sets the single value of the result to the given value, which also involves setting the single
* pixel in the texture to that value. See the class description for more information. Assumes
* pixel in the image to that value. See the class description for more information. Assumes
* the result stores a value of the given template type. */
template<typename T> void set_single_value(const T &value);
@@ -464,46 +450,13 @@ class Result {
/* Return true if the provided template type is an int or an int vector. */
template<typename T> static constexpr bool is_int_type();
/* Returns the number of channels in the provided template type, this is 1 for scalar types, and
* the number of elements for vector types. */
template<typename T> static constexpr int get_type_channels_count();
/* Return true if the provided template type is supported by the class. */
template<typename T> static constexpr bool is_supported_type();
/* Allocates the texture data for the given size, either on the GPU or CPU based on the result's
/* Allocates the image data for the given size, either on the GPU or CPU based on the result's
* context. See the allocate_texture method for information about the from_pool argument. */
void allocate_data(int2 size, bool from_pool);
/* Get the index of the start of pixel at the given texel position in its result buffer. Asserts
* if the template type doesn't match the result's type. */
template<typename T> int64_t get_pixel_index(const int2 &texel) const;
/* Same as get_pixel_index but can be used when the type of the result is not known at compile
* time. */
int64_t get_pixel_index(const int2 &texel) const;
/* Get a pointer to the float pixel at the given texel position. Asserts if the template type
* doesn't match the result's type. */
template<typename T>
std::conditional_t<Result::is_int_type<T>(), int, float> *get_pixel(const int2 &texel) const;
/* Get a pointer to the float pixel at the given texel position. */
float *get_float_pixel(const int2 &texel) const;
/* Get a pointer to the integer pixel at the given texel position. */
int *get_integer_pixel(const int2 &texel) const;
/* Copy the float pixel from the source pointer to the target pointer, assuming the given
* channels count. */
static void copy_pixel(float *target, const float *source, const int channels_count);
/* Copy the integer pixel from the source pointer to the target pointer, assuming the given
* channels count. */
static void copy_pixel(int *target, const int *source, const int channels_count);
/* Copy the float pixel from the source pointer to the target pointer. */
void copy_pixel(float *target, const float *source) const;
};
/* -------------------------------------------------------------------- */
@@ -533,65 +486,23 @@ inline int64_t Result::channels_count() const
return 4;
}
inline float *Result::float_texture() const
inline GPUTexture *Result::gpu_texture() const
{
BLI_assert(storage_type_ == ResultStorageType::FloatCPU);
return float_texture_;
BLI_assert(storage_type_ == ResultStorageType::GPU);
return gpu_texture_;
}
inline int *Result::integer_texture() const
inline GMutableSpan Result::cpu_data() const
{
BLI_assert(storage_type_ == ResultStorageType::IntegerCPU);
return integer_texture_;
}
inline void *Result::data() const
{
switch (storage_type_) {
case ResultStorageType::FloatCPU:
return this->float_texture();
case ResultStorageType::IntegerCPU:
return this->integer_texture();
case ResultStorageType::GPU:
break;
}
BLI_assert_unreachable();
return nullptr;
BLI_assert(storage_type_ == ResultStorageType::CPU);
return cpu_data_;
}
template<typename T> inline const T &Result::get_single_value() const
{
BLI_assert(this->is_single_value());
static_assert(Result::is_supported_type<T>());
if constexpr (std::is_same_v<T, float>) {
BLI_assert(type_ == ResultType::Float);
return float_value_;
}
else if constexpr (std::is_same_v<T, int>) {
BLI_assert(type_ == ResultType::Int);
return int_value_;
}
else if constexpr (std::is_same_v<T, float2>) {
BLI_assert(type_ == ResultType::Float2);
return float2_value_;
}
else if constexpr (std::is_same_v<T, float3>) {
BLI_assert(type_ == ResultType::Float3);
return float3_value_;
}
else if constexpr (std::is_same_v<T, float4>) {
BLI_assert(ELEM(type_, ResultType::Color, ResultType::Vector));
return color_value_;
}
else if constexpr (std::is_same_v<T, int2>) {
BLI_assert(type_ == ResultType::Int2);
return int2_value_;
}
else {
return T(0);
}
return std::get<T>(single_value_);
}
template<typename T> inline T &Result::get_single_value()
@@ -611,48 +522,30 @@ template<typename T> inline void Result::set_single_value(const T &value)
{
BLI_assert(this->is_allocated());
BLI_assert(this->is_single_value());
static_assert(Result::is_supported_type<T>());
this->get_single_value<T>() = value;
single_value_ = value;
switch (storage_type_) {
case ResultStorageType::GPU:
if constexpr (Result::is_int_type<T>()) {
if constexpr (std::is_scalar_v<T>) {
GPU_texture_update(gpu_texture_, GPU_DATA_INT, &value);
GPU_texture_update(this->gpu_texture(), GPU_DATA_INT, &value);
}
else {
GPU_texture_update(gpu_texture_, GPU_DATA_INT, value);
GPU_texture_update(this->gpu_texture(), GPU_DATA_INT, value);
}
}
else {
if constexpr (std::is_scalar_v<T>) {
GPU_texture_update(gpu_texture_, GPU_DATA_FLOAT, &value);
GPU_texture_update(this->gpu_texture(), GPU_DATA_FLOAT, &value);
}
else {
GPU_texture_update(gpu_texture_, GPU_DATA_FLOAT, value);
GPU_texture_update(this->gpu_texture(), GPU_DATA_FLOAT, value);
}
}
break;
case ResultStorageType::FloatCPU:
case ResultStorageType::IntegerCPU:
if constexpr (Result::is_int_type<T>()) {
if constexpr (std::is_scalar_v<T>) {
Result::copy_pixel(
this->integer_texture(), &value, Result::get_type_channels_count<T>());
}
else {
Result::copy_pixel(this->integer_texture(), value, Result::get_type_channels_count<T>());
}
}
else {
if constexpr (std::is_scalar_v<T>) {
Result::copy_pixel(this->float_texture(), &value, Result::get_type_channels_count<T>());
}
else {
Result::copy_pixel(this->float_texture(), value, Result::get_type_channels_count<T>());
}
}
case ResultStorageType::CPU:
this->cpu_data().typed<T>()[0] = value;
break;
}
}
@@ -668,12 +561,7 @@ template<typename T, bool CouldBeSingleValue> inline T Result::load_pixel(const
BLI_assert(!this->is_single_value());
}
if constexpr (std::is_scalar_v<T>) {
return *this->get_pixel<T>(texel);
}
else {
return T(this->get_pixel<T>(texel));
}
return this->cpu_data().typed<T>()[this->get_pixel_index(texel)];
}
template<typename T, bool CouldBeSingleValue>
@@ -689,12 +577,7 @@ inline T Result::load_pixel_extended(const int2 &texel) const
}
const int2 clamped_texel = math::clamp(texel, int2(0), domain_.size - int2(1));
if constexpr (std::is_scalar_v<T>) {
return *this->get_pixel<T>(clamped_texel);
}
else {
return T(this->get_pixel<T>(clamped_texel));
}
return this->cpu_data().typed<T>()[this->get_pixel_index(clamped_texel)];
}
template<typename T, bool CouldBeSingleValue>
@@ -713,12 +596,7 @@ inline T Result::load_pixel_fallback(const int2 &texel, const T &fallback) const
return fallback;
}
if constexpr (std::is_scalar_v<T>) {
return *this->get_pixel<T>(texel);
}
else {
return T(this->get_pixel<T>(texel));
}
return this->cpu_data().typed<T>()[this->get_pixel_index(texel)];
}
template<typename T, bool CouldBeSingleValue>
@@ -731,42 +609,37 @@ inline float4 Result::load_pixel_generic_type(const int2 &texel) const
{
float4 pixel_value = float4(0.0f, 0.0f, 0.0f, 1.0f);
if (is_single_value_) {
this->copy_pixel(pixel_value, float_texture_);
this->get_cpp_type().copy_assign(this->cpu_data().data(), pixel_value);
}
else {
this->copy_pixel(pixel_value, this->get_float_pixel(texel));
this->get_cpp_type().copy_assign(this->cpu_data()[this->get_pixel_index(texel)], pixel_value);
}
return pixel_value;
}
template<typename T> inline void Result::store_pixel(const int2 &texel, const T &pixel_value)
{
if constexpr (std::is_scalar_v<T>) {
*this->get_pixel<T>(texel) = pixel_value;
}
else {
Result::copy_pixel(
this->get_pixel<T>(texel), pixel_value, Result::get_type_channels_count<T>());
}
this->cpu_data().typed<T>()[this->get_pixel_index(texel)] = pixel_value;
}
inline void Result::store_pixel_generic_type(const int2 &texel, const float4 &pixel_value)
{
this->copy_pixel(this->get_float_pixel(texel), pixel_value);
this->get_cpp_type().copy_assign(pixel_value, this->cpu_data()[this->get_pixel_index(texel)]);
}
inline float4 Result::sample_nearest_zero(const float2 &coordinates) const
{
float4 pixel_value = float4(0.0f, 0.0f, 0.0f, 1.0f);
if (is_single_value_) {
this->copy_pixel(pixel_value, float_texture_);
this->get_cpp_type().copy_assign(this->cpu_data().data(), pixel_value);
return pixel_value;
}
const int2 size = domain_.size;
const float2 texel_coordinates = coordinates * float2(size);
math::interpolate_nearest_border_fl(this->float_texture(),
const float *buffer = static_cast<const float *>(this->cpu_data().data());
math::interpolate_nearest_border_fl(buffer,
pixel_value,
size.x,
size.y,
@@ -782,15 +655,16 @@ inline float4 Result::sample_nearest_wrap(const float2 &coordinates,
{
float4 pixel_value = float4(0.0f, 0.0f, 0.0f, 1.0f);
if (is_single_value_) {
this->copy_pixel(pixel_value, float_texture_);
this->get_cpp_type().copy_assign(this->cpu_data().data(), pixel_value);
return pixel_value;
}
const int2 size = domain_.size;
const float2 texel_coordinates = coordinates * float2(size);
const float *buffer = static_cast<const float *>(this->cpu_data().data());
math::interpolate_nearest_wrapmode_fl(
this->float_texture(),
buffer,
pixel_value,
size.x,
size.y,
@@ -808,15 +682,16 @@ inline float4 Result::sample_bilinear_wrap(const float2 &coordinates,
{
float4 pixel_value = float4(0.0f, 0.0f, 0.0f, 1.0f);
if (is_single_value_) {
this->copy_pixel(pixel_value, float_texture_);
this->get_cpp_type().copy_assign(this->cpu_data().data(), pixel_value);
return pixel_value;
}
const int2 size = domain_.size;
const float2 texel_coordinates = coordinates * float2(size) - 0.5f;
const float *buffer = static_cast<const float *>(this->cpu_data().data());
math::interpolate_bilinear_wrapmode_fl(
this->float_texture(),
buffer,
pixel_value,
size.x,
size.y,
@@ -832,15 +707,16 @@ inline float4 Result::sample_cubic_wrap(const float2 &coordinates, bool wrap_x,
{
float4 pixel_value = float4(0.0f, 0.0f, 0.0f, 1.0f);
if (is_single_value_) {
this->copy_pixel(pixel_value, float_texture_);
this->get_cpp_type().copy_assign(this->cpu_data().data(), pixel_value);
return pixel_value;
}
const int2 size = domain_.size;
const float2 texel_coordinates = coordinates * float2(size) - 0.5f;
const float *buffer = static_cast<const float *>(this->cpu_data().data());
math::interpolate_cubic_bspline_wrapmode_fl(
this->float_texture(),
buffer,
pixel_value,
size.x,
size.y,
@@ -856,14 +732,15 @@ inline float4 Result::sample_bilinear_zero(const float2 &coordinates) const
{
float4 pixel_value = float4(0.0f, 0.0f, 0.0f, 1.0f);
if (is_single_value_) {
this->copy_pixel(pixel_value, float_texture_);
this->get_cpp_type().copy_assign(this->cpu_data().data(), pixel_value);
return pixel_value;
}
const int2 size = domain_.size;
const float2 texel_coordinates = (coordinates * float2(size)) - 0.5f;
math::interpolate_bilinear_border_fl(this->float_texture(),
const float *buffer = static_cast<const float *>(this->cpu_data().data());
math::interpolate_bilinear_border_fl(buffer,
pixel_value,
size.x,
size.y,
@@ -877,14 +754,15 @@ inline float4 Result::sample_nearest_extended(const float2 &coordinates) const
{
float4 pixel_value = float4(0.0f, 0.0f, 0.0f, 1.0f);
if (is_single_value_) {
this->copy_pixel(pixel_value, float_texture_);
this->get_cpp_type().copy_assign(this->cpu_data().data(), pixel_value);
return pixel_value;
}
const int2 size = domain_.size;
const float2 texel_coordinates = coordinates * float2(size);
math::interpolate_nearest_fl(this->float_texture(),
const float *buffer = static_cast<const float *>(this->cpu_data().data());
math::interpolate_nearest_fl(buffer,
pixel_value,
size.x,
size.y,
@@ -898,14 +776,15 @@ inline float4 Result::sample_bilinear_extended(const float2 &coordinates) const
{
float4 pixel_value = float4(0.0f, 0.0f, 0.0f, 1.0f);
if (is_single_value_) {
this->copy_pixel(pixel_value, float_texture_);
this->get_cpp_type().copy_assign(this->cpu_data().data(), pixel_value);
return pixel_value;
}
const int2 size = domain_.size;
const float2 texel_coordinates = (coordinates * float2(size)) - 0.5f;
math::interpolate_bilinear_fl(this->float_texture(),
const float *buffer = static_cast<const float *>(this->cpu_data().data());
math::interpolate_bilinear_fl(buffer,
pixel_value,
size.x,
size.y,
@@ -934,7 +813,7 @@ inline float4 Result::sample_ewa_extended(const float2 &coordinates,
float4 pixel_value = float4(0.0f, 0.0f, 0.0f, 1.0f);
if (is_single_value_) {
this->copy_pixel(pixel_value, float_texture_);
this->get_cpp_type().copy_assign(this->cpu_data().data(), pixel_value);
return pixel_value;
}
@@ -971,7 +850,7 @@ inline float4 Result::sample_ewa_zero(const float2 &coordinates,
float4 pixel_value = float4(0.0f, 0.0f, 0.0f, 1.0f);
if (is_single_value_) {
this->copy_pixel(pixel_value, float_texture_);
this->get_cpp_type().copy_assign(this->cpu_data().data(), pixel_value);
return pixel_value;
}
@@ -999,123 +878,12 @@ template<typename T> constexpr bool Result::is_int_type()
}
}
template<typename T> constexpr int Result::get_type_channels_count()
{
if constexpr (std::is_scalar_v<T>) {
return 1;
}
else {
return T::type_length;
}
}
template<typename T> constexpr bool Result::is_supported_type()
{
return is_same_any_v<T, float, int, float2, float3, float4, int2>;
}
template<typename T> inline int64_t Result::get_pixel_index(const int2 &texel) const
{
BLI_assert(!is_single_value_);
BLI_assert(this->is_allocated());
BLI_assert(texel.x >= 0 && texel.y >= 0 && texel.x < domain_.size.x && texel.y < domain_.size.y);
static_assert(Result::is_supported_type<T>());
constexpr int channels_count = Result::get_type_channels_count<T>();
BLI_assert(this->channels_count() == channels_count);
return (int64_t(texel.y) * domain_.size.x + texel.x) * channels_count;
}
inline int64_t Result::get_pixel_index(const int2 &texel) const
{
BLI_assert(!is_single_value_);
BLI_assert(this->is_allocated());
BLI_assert(texel.x >= 0 && texel.y >= 0 && texel.x < domain_.size.x && texel.y < domain_.size.y);
return (int64_t(texel.y) * domain_.size.x + texel.x) * this->channels_count();
}
template<typename T>
inline std::conditional_t<Result::is_int_type<T>(), int, float> *Result::get_pixel(
const int2 &texel) const
{
if constexpr (Result::is_int_type<T>()) {
return this->integer_texture() + this->get_pixel_index<T>(texel);
}
else {
return this->float_texture() + this->get_pixel_index<T>(texel);
}
}
inline float *Result::get_float_pixel(const int2 &texel) const
{
BLI_assert(storage_type_ == ResultStorageType::FloatCPU);
return float_texture_ + this->get_pixel_index(texel);
}
inline int *Result::get_integer_pixel(const int2 &texel) const
{
BLI_assert(storage_type_ == ResultStorageType::IntegerCPU);
return integer_texture_ + this->get_pixel_index(texel);
}
inline void Result::copy_pixel(float *target, const float *source, const int channels_count)
{
switch (channels_count) {
case 1:
*target = *source;
break;
case 2:
copy_v2_v2(target, source);
break;
case 3:
copy_v3_v3(target, source);
break;
case 4:
copy_v4_v4(target, source);
break;
default:
BLI_assert_unreachable();
break;
}
}
inline void Result::copy_pixel(int *target, const int *source, const int channels_count)
{
switch (channels_count) {
case 1:
*target = *source;
break;
case 2:
copy_v2_v2_int(target, source);
break;
default:
BLI_assert_unreachable();
break;
}
}
inline void Result::copy_pixel(float *target, const float *source) const
{
switch (type_) {
case ResultType::Float:
*target = *source;
break;
case ResultType::Float2:
copy_v2_v2(target, source);
break;
case ResultType::Float3:
copy_v3_v3(target, source);
break;
case ResultType::Vector:
case ResultType::Color:
copy_v4_v4(target, source);
break;
case ResultType::Int:
case ResultType::Int2:
BLI_assert_unreachable();
break;
}
return int64_t(texel.y) * domain_.size.x + texel.x;
}
} // namespace blender::compositor

View File

@@ -81,7 +81,7 @@ DenoisedAuxiliaryPass::DenoisedAuxiliaryPass(Context &context,
this->denoised_buffer = static_cast<float *>(GPU_texture_read(pass, GPU_DATA_FLOAT, 0));
}
else {
this->denoised_buffer = static_cast<float *>(MEM_dupallocN(pass.float_texture()));
this->denoised_buffer = static_cast<float *>(MEM_dupallocN(pass.cpu_data().data()));
}
const int width = pass.domain().size.x;

View File

@@ -170,8 +170,7 @@ void MultiFunctionProcedureOperation::execute()
add_single_value_input_parameter(parameter_builder, input);
}
else {
const GSpan span{get_cpp_type(input.type()), input.data(), size};
parameter_builder.add_readonly_single_input(span);
parameter_builder.add_readonly_single_input(input.cpu_data());
}
}
else {
@@ -181,8 +180,7 @@ void MultiFunctionProcedureOperation::execute()
}
else {
output.allocate_texture(domain);
const GMutableSpan span{get_cpp_type(output.type()), output.data(), size};
parameter_builder.add_uninitialized_single_output(span);
parameter_builder.add_uninitialized_single_output(output.cpu_data());
}
}
}

View File

@@ -38,7 +38,7 @@ void ReduceToSingleValueOperation::execute()
need_to_free_pixel = true;
}
else {
pixel = input.float_texture();
pixel = input.cpu_data().data();
}
Result &result = get_result();

View File

@@ -2,11 +2,17 @@
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#include <cstdint>
#include <variant>
#include "MEM_guardedalloc.h"
#include "BLI_assert.h"
#include "BLI_cpp_type.hh"
#include "BLI_generic_span.hh"
#include "BLI_math_matrix_types.hh"
#include "BLI_math_vector_types.hh"
#include "BLI_utildefines.h"
#include "GPU_shader.hh"
#include "GPU_state.hh"
@@ -212,8 +218,30 @@ ResultType Result::float_type(const int channels_count)
Result::operator GPUTexture *() const
{
BLI_assert(storage_type_ == ResultStorageType::GPU);
return gpu_texture_;
return this->gpu_texture();
}
const CPPType &Result::get_cpp_type() const
{
switch (this->type()) {
case ResultType::Float:
return CPPType::get<float>();
case ResultType::Int:
return CPPType::get<int>();
case ResultType::Vector:
return CPPType::get<float4>();
case ResultType::Color:
return CPPType::get<float4>();
case ResultType::Float2:
return CPPType::get<float2>();
case ResultType::Float3:
return CPPType::get<float3>();
case ResultType::Int2:
return CPPType::get<int2>();
}
BLI_assert_unreachable();
return CPPType::get<float>();
}
eGPUTextureFormat Result::get_gpu_texture_format() const
@@ -238,39 +266,42 @@ void Result::allocate_texture(Domain domain, bool from_pool)
void Result::allocate_single_value()
{
/* Single values are stored in 1x1 textures as well as the single value members. Further, they
/* Single values are stored in 1x1 image as well as the single value members. Further, they
* are always allocated from the pool. */
is_single_value_ = true;
this->allocate_data(int2(1), true);
domain_ = Domain::identity();
/* It is important that we initialize single values because the variant member that stores single
* values need to have its type initialized. */
switch (type_) {
case ResultType::Float:
this->set_single_value(0.0f);
break;
case ResultType::Vector:
this->set_single_value(float4(0.0f));
break;
case ResultType::Color:
this->set_single_value(float4(0.0f));
break;
case ResultType::Float2:
this->set_single_value(float2(0.0f));
break;
case ResultType::Float3:
this->set_single_value(float3(0.0f));
break;
case ResultType::Int:
this->set_single_value(0);
break;
case ResultType::Int2:
this->set_single_value(int2(0));
break;
}
}
void Result::allocate_invalid()
{
allocate_single_value();
switch (type_) {
case ResultType::Float:
set_single_value(0.0f);
break;
case ResultType::Vector:
set_single_value(float4(0.0f));
break;
case ResultType::Color:
set_single_value(float4(0.0f));
break;
case ResultType::Float2:
set_single_value(float2(0.0f));
break;
case ResultType::Float3:
set_single_value(float3(0.0f));
break;
case ResultType::Int:
set_single_value(0);
break;
case ResultType::Int2:
set_single_value(int2(0));
break;
}
this->allocate_single_value();
}
void Result::bind_as_texture(GPUShader *shader, const char *texture_name) const
@@ -281,7 +312,7 @@ void Result::bind_as_texture(GPUShader *shader, const char *texture_name) const
GPU_memory_barrier(GPU_BARRIER_TEXTURE_FETCH);
const int texture_image_unit = GPU_shader_get_sampler_binding(shader, texture_name);
GPU_texture_bind(gpu_texture_, texture_image_unit);
GPU_texture_bind(this->gpu_texture(), texture_image_unit);
}
void Result::bind_as_image(GPUShader *shader, const char *image_name, bool read) const
@@ -294,19 +325,19 @@ void Result::bind_as_image(GPUShader *shader, const char *image_name, bool read)
}
const int image_unit = GPU_shader_get_sampler_binding(shader, image_name);
GPU_texture_image_bind(gpu_texture_, image_unit);
GPU_texture_image_bind(this->gpu_texture(), image_unit);
}
void Result::unbind_as_texture() const
{
BLI_assert(storage_type_ == ResultStorageType::GPU);
GPU_texture_unbind(gpu_texture_);
GPU_texture_unbind(this->gpu_texture());
}
void Result::unbind_as_image() const
{
BLI_assert(storage_type_ == ResultStorageType::GPU);
GPU_texture_image_unbind(gpu_texture_);
GPU_texture_image_unbind(this->gpu_texture());
}
void Result::pass_through(Result &target)
@@ -354,24 +385,14 @@ void Result::wrap_external(GPUTexture *texture)
domain_ = Domain(int2(GPU_texture_width(texture), GPU_texture_height(texture)));
}
void Result::wrap_external(float *texture, int2 size)
void Result::wrap_external(void *data, int2 size)
{
BLI_assert(!this->is_allocated());
BLI_assert(!master_);
float_texture_ = texture;
storage_type_ = ResultStorageType::FloatCPU;
is_external_ = true;
domain_ = Domain(size);
}
void Result::wrap_external(int *texture, int2 size)
{
BLI_assert(!this->is_allocated());
BLI_assert(!master_);
integer_texture_ = texture;
storage_type_ = ResultStorageType::IntegerCPU;
const int64_t array_size = int64_t(size.x) * int64_t(size.y);
cpu_data_ = GMutableSpan(this->get_cpp_type(), data, array_size);
storage_type_ = ResultStorageType::CPU;
is_external_ = true;
domain_ = Domain(size);
}
@@ -468,20 +489,16 @@ void Result::free()
switch (storage_type_) {
case ResultStorageType::GPU:
if (is_from_pool_) {
context_->texture_pool().release(gpu_texture_);
context_->texture_pool().release(this->gpu_texture());
}
else {
GPU_texture_free(gpu_texture_);
GPU_texture_free(this->gpu_texture());
}
gpu_texture_ = nullptr;
break;
case ResultStorageType::FloatCPU:
MEM_freeN(float_texture_);
float_texture_ = nullptr;
break;
case ResultStorageType::IntegerCPU:
MEM_freeN(integer_texture_);
integer_texture_ = nullptr;
case ResultStorageType::CPU:
MEM_freeN(this->cpu_data().data());
cpu_data_ = GMutableSpan();
break;
}
@@ -535,11 +552,9 @@ bool Result::is_allocated() const
{
switch (storage_type_) {
case ResultStorageType::GPU:
return gpu_texture_ != nullptr;
case ResultStorageType::FloatCPU:
return float_texture_ != nullptr;
case ResultStorageType::IntegerCPU:
return integer_texture_ != nullptr;
return this->gpu_texture();
case ResultStorageType::CPU:
return this->cpu_data().data();
}
return false;
@@ -557,6 +572,7 @@ int Result::reference_count() const
void Result::allocate_data(int2 size, bool from_pool)
{
if (context_->use_gpu()) {
storage_type_ = ResultStorageType::GPU;
is_from_pool_ = from_pool;
if (from_pool) {
gpu_texture_ = context_->texture_pool().acquire(size, this->get_gpu_texture_format());
@@ -572,23 +588,18 @@ void Result::allocate_data(int2 size, bool from_pool)
}
}
else {
switch (type_) {
case ResultType::Float:
case ResultType::Vector:
case ResultType::Color:
case ResultType::Float2:
case ResultType::Float3:
float_texture_ = static_cast<float *>(MEM_malloc_arrayN(
int64_t(size.x) * int64_t(size.y), this->channels_count() * sizeof(float), __func__));
storage_type_ = ResultStorageType::FloatCPU;
break;
case ResultType::Int:
case ResultType::Int2:
integer_texture_ = static_cast<int *>(MEM_malloc_arrayN(
int64_t(size.x) * int64_t(size.y), this->channels_count() * sizeof(int), __func__));
storage_type_ = ResultStorageType::IntegerCPU;
break;
}
storage_type_ = ResultStorageType::CPU;
const CPPType &cpp_type = this->get_cpp_type();
const int64_t item_size = cpp_type.size();
const int64_t alignment = cpp_type.alignment();
const int64_t array_size = int64_t(size.x) * int64_t(size.y);
const int64_t memory_size = array_size * item_size;
void *data = MEM_mallocN_aligned(memory_size, alignment, AT);
cpp_type.default_construct_n(data, array_size);
cpu_data_ = GMutableSpan(cpp_type, data, array_size);
}
}

View File

@@ -140,7 +140,7 @@ class ConvertColorSpaceOperation : public NodeOperation {
});
IMB_colormanagement_processor_apply(color_processor,
output_image.float_texture(),
static_cast<float *>(output_image.cpu_data().data()),
domain.size.x,
domain.size.y,
input_image.channels_count(),

View File

@@ -148,8 +148,8 @@ class DenoiseOperation : public NodeOperation {
temporary_buffers_to_free.append(input_color);
}
else {
input_color = input_image.float_texture();
output_color = output_image.float_texture();
input_color = static_cast<float *>(input_image.cpu_data().data());
output_color = static_cast<float *>(output_image.cpu_data().data());
}
oidn::FilterRef filter = device.newFilter("RT");
filter.setImage("color", input_color, oidn::Format::Float3, width, height, 0, pixel_stride);
@@ -179,7 +179,7 @@ class DenoiseOperation : public NodeOperation {
temporary_buffers_to_free.append(albedo);
}
else {
albedo = input_albedo.float_texture();
albedo = static_cast<float *>(input_albedo.cpu_data().data());
}
}
@@ -207,7 +207,7 @@ class DenoiseOperation : public NodeOperation {
temporary_buffers_to_free.append(normal);
}
else {
normal = input_normal.float_texture();
normal = static_cast<float *>(input_normal.cpu_data().data());
}
}

View File

@@ -659,14 +659,7 @@ class FileOutputOperation : public NodeOperation {
}
else {
/* Copy the result into a new buffer. */
const int64_t buffer_size = int64_t(size.x) * size.y * result.channels_count();
buffer = static_cast<float *>(
MEM_malloc_arrayN(buffer_size, sizeof(float), "File Output Buffer Copy."));
threading::parallel_for(IndexRange(buffer_size), 1024, [&](const IndexRange sub_range) {
for (const int64_t i : sub_range) {
buffer[i] = result.float_texture()[i];
}
});
buffer = static_cast<float *>(MEM_dupallocN(result.cpu_data().data()));
}
}
@@ -750,15 +743,7 @@ class FileOutputOperation : public NodeOperation {
}
else {
/* Copy the result into a new buffer. */
const int2 size = result.domain().size;
const int64_t buffer_size = int64_t(size.x) * size.y * result.channels_count();
buffer = static_cast<float *>(
MEM_malloc_arrayN(buffer_size, sizeof(float), "File Output Buffer Copy."));
threading::parallel_for(IndexRange(buffer_size), 1024, [&](const IndexRange sub_range) {
for (const int64_t i : sub_range) {
buffer[i] = result.float_texture()[i];
}
});
buffer = static_cast<float *>(MEM_dupallocN(result.cpu_data().data()));
}
const int2 size = result.domain().size;

View File

@@ -1982,7 +1982,7 @@ class GlareOperation : public NodeOperation {
highlights_buffer = static_cast<float *>(GPU_texture_read(highlights, GPU_DATA_FLOAT, 0));
}
else {
highlights_buffer = highlights.float_texture();
highlights_buffer = static_cast<float *>(highlights.cpu_data().data());
}
/* Zero pad the image to the required spatial domain size, storing each channel in planar
@@ -2060,8 +2060,9 @@ class GlareOperation : public NodeOperation {
/* For GPU, write the output to the exist highlights_buffer then upload to the result after,
* while for CPU, write to the result directly. */
float *output = this->context().use_gpu() ? highlights_buffer :
fog_glow_result.float_texture();
float *output = this->context().use_gpu() ?
highlights_buffer :
static_cast<float *>(fog_glow_result.cpu_data().data());
/* Copy the result to the output. */
threading::parallel_for(IndexRange(image_size.y), 1, [&](const IndexRange sub_y_range) {

View File

@@ -507,7 +507,7 @@ class Context : public compositor::Context {
MEM_malloc_arrayN(rr->rectx * rr->recty, 4 * sizeof(float), __func__));
IMB_assign_float_buffer(ibuf, data, IB_TAKE_OWNERSHIP);
std::memcpy(
data, output_result_.float_texture(), rr->rectx * rr->recty * 4 * sizeof(float));
data, output_result_.cpu_data().data(), rr->rectx * rr->recty * 4 * sizeof(float));
}
}
@@ -580,7 +580,7 @@ class Context : public compositor::Context {
}
else {
std::memcpy(image_buffer->float_buffer.data,
viewer_output_result_.float_texture(),
viewer_output_result_.cpu_data().data(),
size.x * size.y * 4 * sizeof(float));
}