diff options
Diffstat (limited to 'source')
17 files changed, 789 insertions, 375 deletions
diff --git a/source/blender/compositor/realtime_compositor/CMakeLists.txt b/source/blender/compositor/realtime_compositor/CMakeLists.txt index 2402adcadaf..b4352248b5b 100644 --- a/source/blender/compositor/realtime_compositor/CMakeLists.txt +++ b/source/blender/compositor/realtime_compositor/CMakeLists.txt @@ -3,6 +3,7 @@ set(INC . algorithms + cached_resources ../../blenkernel ../../blenlib ../../gpu @@ -10,6 +11,7 @@ set(INC ../../makesdna ../../makesrna ../../nodes + ../../render ../../gpu/intern ../../../../intern/guardedalloc ) @@ -31,6 +33,7 @@ set(SRC intern/shader_node.cc intern/shader_operation.cc intern/simple_operation.cc + intern/static_cache_manager.cc intern/static_shader_manager.cc intern/texture_pool.cc intern/utilities.cc @@ -51,6 +54,7 @@ set(SRC COM_shader_node.hh COM_shader_operation.hh COM_simple_operation.hh + COM_static_cache_manager.hh COM_static_shader_manager.hh COM_texture_pool.hh COM_utilities.hh @@ -58,12 +62,22 @@ set(SRC algorithms/intern/algorithm_parallel_reduction.cc algorithms/COM_algorithm_parallel_reduction.hh + + cached_resources/intern/morphological_distance_feather_weights.cc + cached_resources/intern/symmetric_blur_weights.cc + cached_resources/intern/symmetric_separable_blur_weights.cc + + cached_resources/COM_cached_resource.hh + cached_resources/COM_morphological_distance_feather_weights.hh + cached_resources/COM_symmetric_blur_weights.hh + cached_resources/COM_symmetric_separable_blur_weights.hh ) set(LIB bf_gpu bf_nodes bf_imbuf + bf_render bf_blenlib bf_blenkernel ) diff --git a/source/blender/compositor/realtime_compositor/COM_context.hh b/source/blender/compositor/realtime_compositor/COM_context.hh index b5c8cea641f..80fb4f70ca4 100644 --- a/source/blender/compositor/realtime_compositor/COM_context.hh +++ b/source/blender/compositor/realtime_compositor/COM_context.hh @@ -9,6 +9,7 @@ #include "GPU_texture.h" +#include "COM_static_cache_manager.hh" #include "COM_static_shader_manager.hh" #include "COM_texture_pool.hh" @@ -22,14 +23,17 @@ namespace blender::realtime_compositor { * 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 static shader manager for convenient shader - * acquisition. */ + * Finally, the class have an instance of a static shader manager and a static resource manager + * for acquiring cached shaders and resources efficiently. */ class Context { private: /* A texture pool that can be used to allocate textures for the compositor efficiently. */ TexturePool &texture_pool_; /* A static shader manager that can be used to acquire shaders for the compositor efficiently. */ StaticShaderManager shader_manager_; + /* A static cache manager that can be used to acquire cached resources for the compositor + * efficiently. */ + StaticCacheManager cache_manager_; public: Context(TexturePool &texture_pool); @@ -67,6 +71,9 @@ class Context { /* Get a reference to the static shader manager of this context. */ StaticShaderManager &shader_manager(); + + /* Get a reference to the static cache manager of this context. */ + StaticCacheManager &cache_manager(); }; } // namespace blender::realtime_compositor diff --git a/source/blender/compositor/realtime_compositor/COM_static_cache_manager.hh b/source/blender/compositor/realtime_compositor/COM_static_cache_manager.hh new file mode 100644 index 00000000000..20fbb156879 --- /dev/null +++ b/source/blender/compositor/realtime_compositor/COM_static_cache_manager.hh @@ -0,0 +1,77 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#pragma once + +#include <memory> + +#include "BLI_map.hh" +#include "BLI_math_vec_types.hh" + +#include "COM_morphological_distance_feather_weights.hh" +#include "COM_symmetric_blur_weights.hh" +#include "COM_symmetric_separable_blur_weights.hh" + +namespace blender::realtime_compositor { + +/* ------------------------------------------------------------------------------------------------- + * Static Cache Manager + * + * A static cache manager is a collection of cached resources that can be retrieved when needed and + * created if not already available. In particular, each cached resource type has its own Map in + * the class, where all instances of that cached resource type are stored and tracked. See the + * CachedResource class for more information. + * + * The manager deletes the cached resources that are no longer needed. A cached resource is said to + * be not needed when it was not used in the previous evaluation. This is done through the + * following mechanism: + * + * - Before every evaluation, do the following: + * 1. All resources whose CachedResource::needed flag is false are deleted. + * 2. The CachedResource::needed flag of all remaining resources is set to false. + * - During evaluation, when retrieving any cached resource, set its CachedResource::needed flag to + * true. + * + * In effect, any resource that was used in the previous evaluation but was not used in the current + * evaluation will be deleted before the next evaluation. This mechanism is implemented in the + * reset() method of the class, which should be called before every evaluation. */ +class StaticCacheManager { + private: + /* A map that stores all SymmetricBlurWeights cached resources. */ + Map<SymmetricBlurWeightsKey, std::unique_ptr<SymmetricBlurWeights>> symmetric_blur_weights_; + + /* A map that stores all SymmetricSeparableBlurWeights cached resources. */ + Map<SymmetricSeparableBlurWeightsKey, std::unique_ptr<SymmetricSeparableBlurWeights>> + symmetric_separable_blur_weights_; + + /* A map that stores all MorphologicalDistanceFeatherWeights cached resources. */ + Map<MorphologicalDistanceFeatherWeightsKey, std::unique_ptr<MorphologicalDistanceFeatherWeights>> + morphological_distance_feather_weights_; + + public: + /* Reset the cache manager by deleting the cached resources that are no longer needed because + * they weren't used in the last evaluation and prepare the remaining cached resources to track + * their needed status in the next evaluation. See the class description for more information. + * This should be called before every evaluation. */ + void reset(); + + /* Check if there is an available SymmetricBlurWeights cached resource with the given parameters + * in the manager, if one exists, return it, otherwise, return a newly created one and add it to + * the manager. In both cases, tag the cached resource as needed to keep it cached for the next + * evaluation. */ + SymmetricBlurWeights &get_symmetric_blur_weights(int type, float2 radius); + + /* Check if there is an available SymmetricSeparableBlurWeights cached resource with the given + * parameters in the manager, if one exists, return it, otherwise, return a newly created one and + * add it to the manager. In both cases, tag the cached resource as needed to keep it cached for + * the next evaluation. */ + SymmetricSeparableBlurWeights &get_symmetric_separable_blur_weights(int type, float radius); + + /* Check if there is an available MorphologicalDistanceFeatherWeights cached resource with the + * given parameters in the manager, if one exists, return it, otherwise, return a newly created + * one and add it to the manager. In both cases, tag the cached resource as needed to keep it + * cached for the next evaluation. */ + MorphologicalDistanceFeatherWeights &get_morphological_distance_feather_weights(int type, + int radius); +}; + +} // namespace blender::realtime_compositor diff --git a/source/blender/compositor/realtime_compositor/cached_resources/COM_cached_resource.hh b/source/blender/compositor/realtime_compositor/cached_resources/COM_cached_resource.hh new file mode 100644 index 00000000000..fe3158ef52d --- /dev/null +++ b/source/blender/compositor/realtime_compositor/cached_resources/COM_cached_resource.hh @@ -0,0 +1,31 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#pragma once + +namespace blender::realtime_compositor { + +/* ------------------------------------------------------------------------------------------------- + * Cached Resource. + * + * A cached resource is any resource that can be cached across compositor evaluations and across + * multiple operations. Cached resources are managed by an instance of a StaticCacheManager and are + * freed when they are no longer needed, a state which is represented by the `needed` member in the + * class. For more information on the caching mechanism, see the StaticCacheManager class. + * + * To add a new cached resource: + * + * - Create a derived class from CachedResource to represent the resource. + * - Create a key class that can be used in a Map to identify the resource. + * - Add a new Map to StaticCacheManager mapping the key to the resource. + * - Reset the contents of the added map in StaticCacheManager::reset. + * - Add an appropriate getter method in StaticCacheManager. + * + * See the existing cached resources for reference. */ +class CachedResource { + public: + /* A flag that represents the needed status of the cached resource. See the StaticCacheManager + * class for more information on how this member is utilized in the caching mechanism. */ + bool needed = true; +}; + +} // namespace blender::realtime_compositor diff --git a/source/blender/compositor/realtime_compositor/cached_resources/COM_morphological_distance_feather_weights.hh b/source/blender/compositor/realtime_compositor/cached_resources/COM_morphological_distance_feather_weights.hh new file mode 100644 index 00000000000..cd6827bdd6b --- /dev/null +++ b/source/blender/compositor/realtime_compositor/cached_resources/COM_morphological_distance_feather_weights.hh @@ -0,0 +1,61 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#pragma once + +#include <cstdint> + +#include "GPU_shader.h" +#include "GPU_texture.h" + +#include "COM_cached_resource.hh" + +namespace blender::realtime_compositor { + +/* ------------------------------------------------------------------------------------------------ + * Morphological Distance Feather Key. + */ +class MorphologicalDistanceFeatherWeightsKey { + public: + int type; + float radius; + + MorphologicalDistanceFeatherWeightsKey(int type, float radius); + + uint64_t hash() const; +}; + +bool operator==(const MorphologicalDistanceFeatherWeightsKey &a, + const MorphologicalDistanceFeatherWeightsKey &b); + +/* ------------------------------------------------------------------------------------------------- + * Morphological Distance Feather Weights. + * + * A cached resource that computes and caches 1D GPU textures containing the weights of the + * separable Gaussian filter of the given radius as well as an inverse distance falloff of the + * given type and radius. The weights and falloffs are symmetric, because the Gaussian and falloff + * functions are all even functions. Consequently, only the positive half of the filter is computed + * and the shader takes that into consideration. */ +class MorphologicalDistanceFeatherWeights : public CachedResource { + private: + GPUTexture *weights_texture_ = nullptr; + GPUTexture *distance_falloffs_texture_ = nullptr; + + public: + MorphologicalDistanceFeatherWeights(int type, int radius); + + ~MorphologicalDistanceFeatherWeights(); + + void compute_weights(int radius); + + void compute_distance_falloffs(int type, int radius); + + void bind_weights_as_texture(GPUShader *shader, const char *texture_name) const; + + void unbind_weights_as_texture() const; + + void bind_distance_falloffs_as_texture(GPUShader *shader, const char *texture_name) const; + + void unbind_distance_falloffs_as_texture() const; +}; + +} // namespace blender::realtime_compositor diff --git a/source/blender/compositor/realtime_compositor/cached_resources/COM_symmetric_blur_weights.hh b/source/blender/compositor/realtime_compositor/cached_resources/COM_symmetric_blur_weights.hh new file mode 100644 index 00000000000..05d3c7c6f3e --- /dev/null +++ b/source/blender/compositor/realtime_compositor/cached_resources/COM_symmetric_blur_weights.hh @@ -0,0 +1,52 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#pragma once + +#include <cstdint> + +#include "BLI_math_vec_types.hh" + +#include "GPU_shader.h" +#include "GPU_texture.h" + +#include "COM_cached_resource.hh" + +namespace blender::realtime_compositor { + +/* ------------------------------------------------------------------------------------------------ + * Symmetric Blur Weights Key. + */ +class SymmetricBlurWeightsKey { + public: + int type; + float2 radius; + + SymmetricBlurWeightsKey(int type, float2 radius); + + uint64_t hash() const; +}; + +bool operator==(const SymmetricBlurWeightsKey &a, const SymmetricBlurWeightsKey &b); + +/* ------------------------------------------------------------------------------------------------- + * Symmetric Blur Weights. + * + * A cached resource that computes and caches a 2D GPU texture containing the weights of the filter + * of the given type and radius. The filter is assumed to be symmetric, because the filter + * functions are evaluated on the normalized distance to the center. Consequently, only the upper + * right quadrant are computed and the shader takes that into consideration. */ +class SymmetricBlurWeights : public CachedResource { + private: + GPUTexture *texture_ = nullptr; + + public: + SymmetricBlurWeights(int type, float2 radius); + + ~SymmetricBlurWeights(); + + void bind_as_texture(GPUShader *shader, const char *texture_name) const; + + void unbind_as_texture() const; +}; + +} // namespace blender::realtime_compositor diff --git a/source/blender/compositor/realtime_compositor/cached_resources/COM_symmetric_separable_blur_weights.hh b/source/blender/compositor/realtime_compositor/cached_resources/COM_symmetric_separable_blur_weights.hh new file mode 100644 index 00000000000..85e75e4535d --- /dev/null +++ b/source/blender/compositor/realtime_compositor/cached_resources/COM_symmetric_separable_blur_weights.hh @@ -0,0 +1,53 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#pragma once + +#include <cstdint> + +#include "BLI_math_vec_types.hh" + +#include "GPU_shader.h" +#include "GPU_texture.h" + +#include "COM_cached_resource.hh" + +namespace blender::realtime_compositor { + +/* ------------------------------------------------------------------------------------------------ + * Symmetric Separable Blur Weights Key. + */ +class SymmetricSeparableBlurWeightsKey { + public: + int type; + float radius; + + SymmetricSeparableBlurWeightsKey(int type, float radius); + + uint64_t hash() const; +}; + +bool operator==(const SymmetricSeparableBlurWeightsKey &a, + const SymmetricSeparableBlurWeightsKey &b); + +/* ------------------------------------------------------------------------------------------------- + * Symmetric Separable Blur Weights. + * + * A cached resource that computes and caches a 1D GPU texture containing the weights of the + * separable filter of the given type and radius. The filter is assumed to be symmetric, because + * the filter functions are all even functions. Consequently, only the positive half of the filter + * is computed and the shader takes that into consideration. */ +class SymmetricSeparableBlurWeights : public CachedResource { + private: + GPUTexture *texture_ = nullptr; + + public: + SymmetricSeparableBlurWeights(int type, float radius); + + ~SymmetricSeparableBlurWeights(); + + void bind_as_texture(GPUShader *shader, const char *texture_name) const; + + void unbind_as_texture() const; +}; + +} // namespace blender::realtime_compositor diff --git a/source/blender/compositor/realtime_compositor/cached_resources/intern/morphological_distance_feather_weights.cc b/source/blender/compositor/realtime_compositor/cached_resources/intern/morphological_distance_feather_weights.cc new file mode 100644 index 00000000000..eac88b907b8 --- /dev/null +++ b/source/blender/compositor/realtime_compositor/cached_resources/intern/morphological_distance_feather_weights.cc @@ -0,0 +1,159 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include <cmath> +#include <cstdint> + +#include "BLI_array.hh" +#include "BLI_hash.hh" +#include "BLI_index_range.hh" + +#include "RE_pipeline.h" + +#include "DNA_scene_types.h" + +#include "GPU_shader.h" +#include "GPU_texture.h" + +#include "COM_morphological_distance_feather_weights.hh" + +namespace blender::realtime_compositor { + +/* -------------------------------------------------------------------- + * Morphological Distance Feather Weights Key. + */ + +MorphologicalDistanceFeatherWeightsKey::MorphologicalDistanceFeatherWeightsKey(int type, + float radius) + : type(type), radius(radius) +{ +} + +uint64_t MorphologicalDistanceFeatherWeightsKey::hash() const +{ + return get_default_hash_2(type, radius); +} + +bool operator==(const MorphologicalDistanceFeatherWeightsKey &a, + const MorphologicalDistanceFeatherWeightsKey &b) +{ + return a.type == b.type && a.radius == b.radius; +} + +/* -------------------------------------------------------------------- + * Morphological Distance Feather Weights. + */ + +MorphologicalDistanceFeatherWeights::MorphologicalDistanceFeatherWeights(int type, int radius) +{ + compute_weights(radius); + compute_distance_falloffs(type, radius); +} + +MorphologicalDistanceFeatherWeights::~MorphologicalDistanceFeatherWeights() +{ + GPU_texture_free(weights_texture_); + GPU_texture_free(distance_falloffs_texture_); +} + +void MorphologicalDistanceFeatherWeights::compute_weights(int radius) +{ + /* The size of filter is double the radius plus 1, but since the filter is symmetric, we only + * compute half of it and no doubling happens. We add 1 to make sure the filter size is always + * odd and there is a center weight. */ + const int size = radius + 1; + Array<float> weights(size); + + float sum = 0.0f; + + /* First, compute the center weight. */ + const float center_weight = RE_filter_value(R_FILTER_GAUSS, 0.0f); + weights[0] = center_weight; + sum += center_weight; + + /* Second, compute the other weights in the positive direction, making sure to add double the + * weight to the sum of weights because the filter is symmetric and we only loop over half of + * it. Skip the center weight already computed by dropping the front index. */ + const float scale = radius > 0.0f ? 1.0f / radius : 0.0f; + for (const int i : weights.index_range().drop_front(1)) { + const float weight = RE_filter_value(R_FILTER_GAUSS, i * scale); + weights[i] = weight; + sum += weight * 2.0f; + } + + /* Finally, normalize the weights. */ + for (const int i : weights.index_range()) { + weights[i] /= sum; + } + + weights_texture_ = GPU_texture_create_1d("Weights", size, 1, GPU_R16F, weights.data()); +} + +/* Computes a falloff that is equal to 1 at an input of zero and decrease to zero at an input of 1, + * with the rate of decrease depending on the falloff type. */ +static float compute_distance_falloff(int type, float x) +{ + x = 1.0f - x; + + switch (type) { + case PROP_SMOOTH: + return 3.0f * x * x - 2.0f * x * x * x; + case PROP_SPHERE: + return std::sqrt(2.0f * x - x * x); + case PROP_ROOT: + return std::sqrt(x); + case PROP_SHARP: + return x * x; + case PROP_INVSQUARE: + return x * (2.0f - x); + case PROP_LIN: + return x; + default: + BLI_assert_unreachable(); + return x; + } +} + +void MorphologicalDistanceFeatherWeights::compute_distance_falloffs(int type, int radius) +{ + /* The size of the distance falloffs is double the radius plus 1, but since the falloffs are + * symmetric, we only compute half of them and no doubling happens. We add 1 to make sure the + * falloffs size is always odd and there is a center falloff. */ + const int size = radius + 1; + Array<float> falloffs(size); + + /* Compute the distance falloffs in the positive direction only, because the falloffs are + * symmetric. */ + const float scale = radius > 0.0f ? 1.0f / radius : 0.0f; + for (const int i : falloffs.index_range()) { + falloffs[i] = compute_distance_falloff(type, i * scale); + } + + distance_falloffs_texture_ = GPU_texture_create_1d( + "Distance Factors", size, 1, GPU_R16F, falloffs.data()); +} + +void MorphologicalDistanceFeatherWeights::bind_weights_as_texture(GPUShader *shader, + const char *texture_name) const +{ + const int texture_image_unit = GPU_shader_get_texture_binding(shader, texture_name); + GPU_texture_bind(weights_texture_, texture_image_unit); +} + +void MorphologicalDistanceFeatherWeights::unbind_weights_as_texture() const +{ + GPU_texture_unbind(weights_texture_); +} + +void MorphologicalDistanceFeatherWeights::bind_distance_falloffs_as_texture( + GPUShader *shader, const char *texture_name) const +{ + const int texture_image_unit = GPU_shader_get_texture_binding(shader, texture_name); + GPU_texture_bind(distance_falloffs_texture_, texture_image_unit); +} + +void MorphologicalDistanceFeatherWeights::unbind_distance_falloffs_as_texture() const +{ + GPU_texture_unbind(distance_falloffs_texture_); +} + +} // namespace blender::realtime_compositor diff --git a/source/blender/compositor/realtime_compositor/cached_resources/intern/symmetric_blur_weights.cc b/source/blender/compositor/realtime_compositor/cached_resources/intern/symmetric_blur_weights.cc new file mode 100644 index 00000000000..a22d32a8e18 --- /dev/null +++ b/source/blender/compositor/realtime_compositor/cached_resources/intern/symmetric_blur_weights.cc @@ -0,0 +1,115 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include <cstdint> + +#include "BLI_array.hh" +#include "BLI_hash.hh" +#include "BLI_index_range.hh" +#include "BLI_math_vec_types.hh" +#include "BLI_math_vector.hh" + +#include "RE_pipeline.h" + +#include "GPU_shader.h" +#include "GPU_texture.h" + +#include "COM_symmetric_blur_weights.hh" + +namespace blender::realtime_compositor { + +/* -------------------------------------------------------------------- + * Symmetric Blur Weights Key. + */ + +SymmetricBlurWeightsKey::SymmetricBlurWeightsKey(int type, float2 radius) + : type(type), radius(radius) +{ +} + +uint64_t SymmetricBlurWeightsKey::hash() const +{ + return get_default_hash_3(type, radius.x, radius.y); +} + +bool operator==(const SymmetricBlurWeightsKey &a, const SymmetricBlurWeightsKey &b) +{ + return a.type == b.type && a.radius == b.radius; +} + +/* -------------------------------------------------------------------- + * Symmetric Blur Weights. + */ + +SymmetricBlurWeights::SymmetricBlurWeights(int type, float2 radius) +{ + /* The full size of filter is double the radius plus 1, but since the filter is symmetric, we + * only compute a single quadrant of it and so no doubling happens. We add 1 to make sure the + * filter size is always odd and there is a center weight. */ + const float2 scale = math::safe_divide(float2(1.0f), radius); + const int2 size = int2(math::ceil(radius)) + int2(1); + Array<float> weights(size.x * size.y); + + float sum = 0.0f; + + /* First, compute the center weight. */ + const float center_weight = RE_filter_value(type, 0.0f); + weights[0] = center_weight; + sum += center_weight; + + /* Then, compute the weights along the positive x axis, making sure to add double the weight to + * the sum of weights because the filter is symmetric and we only loop over the positive half + * of the x axis. Skip the center weight already computed by dropping the front index. */ + for (const int x : IndexRange(size.x).drop_front(1)) { + const float weight = RE_filter_value(type, x * scale.x); + weights[x] = weight; + sum += weight * 2.0f; + } + + /* Then, compute the weights along the positive y axis, making sure to add double the weight to + * the sum of weights because the filter is symmetric and we only loop over the positive half + * of the y axis. Skip the center weight already computed by dropping the front index. */ + for (const int y : IndexRange(size.y).drop_front(1)) { + const float weight = RE_filter_value(type, y * scale.y); + weights[size.x * y] = weight; + sum += weight * 2.0f; + } + + /* Then, compute the other weights in the upper right quadrant, making sure to add quadruple + * the weight to the sum of weights because the filter is symmetric and we only loop over one + * quadrant of it. Skip the weights along the y and x axis already computed by dropping the + * front index. */ + for (const int y : IndexRange(size.y).drop_front(1)) { + for (const int x : IndexRange(size.x).drop_front(1)) { + const float weight = RE_filter_value(type, math::length(float2(x, y) * scale)); + weights[size.x * y + x] = weight; + sum += weight * 4.0f; + } + } + + /* Finally, normalize the weights. */ + for (const int y : IndexRange(size.y)) { + for (const int x : IndexRange(size.x)) { + weights[size.x * y + x] /= sum; + } + } + + texture_ = GPU_texture_create_2d("Weights", size.x, size.y, 1, GPU_R16F, weights.data()); +} + +SymmetricBlurWeights::~SymmetricBlurWeights() +{ + GPU_texture_free(texture_); +} + +void SymmetricBlurWeights::bind_as_texture(GPUShader *shader, const char *texture_name) const +{ + const int texture_image_unit = GPU_shader_get_texture_binding(shader, texture_name); + GPU_texture_bind(texture_, texture_image_unit); +} + +void SymmetricBlurWeights::unbind_as_texture() const +{ + GPU_texture_unbind(texture_); +} + +} // namespace blender::realtime_compositor diff --git a/source/blender/compositor/realtime_compositor/cached_resources/intern/symmetric_separable_blur_weights.cc b/source/blender/compositor/realtime_compositor/cached_resources/intern/symmetric_separable_blur_weights.cc new file mode 100644 index 00000000000..b8c47d5a5d0 --- /dev/null +++ b/source/blender/compositor/realtime_compositor/cached_resources/intern/symmetric_separable_blur_weights.cc @@ -0,0 +1,93 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include <cstdint> + +#include "BLI_array.hh" +#include "BLI_hash.hh" +#include "BLI_index_range.hh" +#include "BLI_math_base.hh" + +#include "RE_pipeline.h" + +#include "GPU_shader.h" +#include "GPU_texture.h" + +#include "COM_symmetric_separable_blur_weights.hh" + +namespace blender::realtime_compositor { + +/* -------------------------------------------------------------------- + * Symmetric Separable Blur Weights Key. + */ + +SymmetricSeparableBlurWeightsKey::SymmetricSeparableBlurWeightsKey(int type, float radius) + : type(type), radius(radius) +{ +} + +uint64_t SymmetricSeparableBlurWeightsKey::hash() const +{ + return get_default_hash_2(type, radius); +} + +bool operator==(const SymmetricSeparableBlurWeightsKey &a, + const SymmetricSeparableBlurWeightsKey &b) +{ + return a.type == b.type && a.radius == b.radius; +} + +/* -------------------------------------------------------------------- + * Symmetric Separable Blur Weights. + */ + +SymmetricSeparableBlurWeights::SymmetricSeparableBlurWeights(int type, float radius) +{ + /* The size of filter is double the radius plus 1, but since the filter is symmetric, we only + * compute half of it and no doubling happens. We add 1 to make sure the filter size is always + * odd and there is a center weight. */ + const int size = math::ceil(radius) + 1; + Array<float> weights(size); + + float sum = 0.0f; + + /* First, compute the center weight. */ + const float center_weight = RE_filter_value(type, 0.0f); + weights[0] = center_weight; + sum += center_weight; + + /* Second, compute the other weights in the positive direction, making sure to add double the + * weight to the sum of weights because the filter is symmetric and we only loop over half of + * it. Skip the center weight already computed by dropping the front index. */ + const float scale = radius > 0.0f ? 1.0f / radius : 0.0f; + for (const int i : weights.index_range().drop_front(1)) { + const float weight = RE_filter_value(type, i * scale); + weights[i] = weight; + sum += weight * 2.0f; + } + + /* Finally, normalize the weights. */ + for (const int i : weights.index_range()) { + weights[i] /= sum; + } + + texture_ = GPU_texture_create_1d("Weights", size, 1, GPU_R16F, weights.data()); +} + +SymmetricSeparableBlurWeights::~SymmetricSeparableBlurWeights() +{ + GPU_texture_free(texture_); +} + +void SymmetricSeparableBlurWeights::bind_as_texture(GPUShader *shader, + const char *texture_name) const +{ + const int texture_image_unit = GPU_shader_get_texture_binding(shader, texture_name); + GPU_texture_bind(texture_, texture_image_unit); +} + +void SymmetricSeparableBlurWeights::unbind_as_texture() const +{ + GPU_texture_unbind(texture_); +} + +} // namespace blender::realtime_compositor diff --git a/source/blender/compositor/realtime_compositor/intern/context.cc b/source/blender/compositor/realtime_compositor/intern/context.cc index 924398f6747..0b123a2c271 100644 --- a/source/blender/compositor/realtime_compositor/intern/context.cc +++ b/source/blender/compositor/realtime_compositor/intern/context.cc @@ -1,6 +1,7 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ #include "COM_context.hh" +#include "COM_static_cache_manager.hh" #include "COM_static_shader_manager.hh" #include "COM_texture_pool.hh" @@ -32,4 +33,9 @@ StaticShaderManager &Context::shader_manager() return shader_manager_; } +StaticCacheManager &Context::cache_manager() +{ + return cache_manager_; +} + } // namespace blender::realtime_compositor diff --git a/source/blender/compositor/realtime_compositor/intern/evaluator.cc b/source/blender/compositor/realtime_compositor/intern/evaluator.cc index 48457bec199..1cd7d4f8951 100644 --- a/source/blender/compositor/realtime_compositor/intern/evaluator.cc +++ b/source/blender/compositor/realtime_compositor/intern/evaluator.cc @@ -28,6 +28,7 @@ Evaluator::Evaluator(Context &context, bNodeTree &node_tree) void Evaluator::evaluate() { + context_.cache_manager().reset(); context_.texture_pool().reset(); if (!is_compiled_) { diff --git a/source/blender/compositor/realtime_compositor/intern/static_cache_manager.cc b/source/blender/compositor/realtime_compositor/intern/static_cache_manager.cc new file mode 100644 index 00000000000..da78412a815 --- /dev/null +++ b/source/blender/compositor/realtime_compositor/intern/static_cache_manager.cc @@ -0,0 +1,74 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include <memory> + +#include "BLI_math_vec_types.hh" + +#include "COM_morphological_distance_feather_weights.hh" +#include "COM_symmetric_blur_weights.hh" +#include "COM_symmetric_separable_blur_weights.hh" + +#include "COM_static_cache_manager.hh" + +namespace blender::realtime_compositor { + +/* -------------------------------------------------------------------- + * Static Cache Manager. + */ + +void StaticCacheManager::reset() +{ + /* First, delete all resources that are no longer needed. */ + symmetric_blur_weights_.remove_if([](auto item) { return !item.value->needed; }); + symmetric_separable_blur_weights_.remove_if([](auto item) { return !item.value->needed; }); + morphological_distance_feather_weights_.remove_if([](auto item) { return !item.value->needed; }); + + /* Second, reset the needed status of the remaining resources to false to ready them to track + * their needed status for the next evaluation. */ + for (auto &value : symmetric_blur_weights_.values()) { + value->needed = false; + } + for (auto &value : symmetric_separable_blur_weights_.values()) { + value->needed = false; + } + for (auto &value : morphological_distance_feather_weights_.values()) { + value->needed = false; + } +} + +SymmetricBlurWeights &StaticCacheManager::get_symmetric_blur_weights(int type, float2 radius) +{ + const SymmetricBlurWeightsKey key(type, radius); + + auto &weights = *symmetric_blur_weights_.lookup_or_add_cb( + key, [&]() { return std::make_unique<SymmetricBlurWeights>(type, radius); }); + + weights.needed = true; + return weights; +} + +SymmetricSeparableBlurWeights &StaticCacheManager::get_symmetric_separable_blur_weights( + int type, float radius) +{ + const SymmetricSeparableBlurWeightsKey key(type, radius); + + auto &weights = *symmetric_separable_blur_weights_.lookup_or_add_cb( + key, [&]() { return std::make_unique<SymmetricSeparableBlurWeights>(type, radius); }); + + weights.needed = true; + return weights; +} + +MorphologicalDistanceFeatherWeights &StaticCacheManager:: + get_morphological_distance_feather_weights(int type, int radius) +{ + const MorphologicalDistanceFeatherWeightsKey key(type, radius); + + auto &weights = *morphological_distance_feather_weights_.lookup_or_add_cb( + key, [&]() { return std::make_unique<MorphologicalDistanceFeatherWeights>(type, radius); }); + + weights.needed = true; + return weights; +} + +} // namespace blender::realtime_compositor diff --git a/source/blender/draw/CMakeLists.txt b/source/blender/draw/CMakeLists.txt index 4010b491141..5902dc18165 100644 --- a/source/blender/draw/CMakeLists.txt +++ b/source/blender/draw/CMakeLists.txt @@ -24,6 +24,8 @@ set(INC ../render ../render/intern ../compositor/realtime_compositor + ../compositor/realtime_compositor/algorithms + ../compositor/realtime_compositor/cached_resources ../windowmanager ../../../intern/atomic diff --git a/source/blender/nodes/composite/CMakeLists.txt b/source/blender/nodes/composite/CMakeLists.txt index 4255a2dde21..8bcc2a393d3 100644 --- a/source/blender/nodes/composite/CMakeLists.txt +++ b/source/blender/nodes/composite/CMakeLists.txt @@ -19,6 +19,7 @@ set(INC ../../windowmanager ../../compositor/realtime_compositor ../../compositor/realtime_compositor/algorithms + ../../compositor/realtime_compositor/cached_resources ../../../../intern/guardedalloc # dna_type_offsets.h diff --git a/source/blender/nodes/composite/nodes/node_composite_blur.cc b/source/blender/nodes/composite/nodes/node_composite_blur.cc index a9372bdcfb7..63f3829ee0b 100644 --- a/source/blender/nodes/composite/nodes/node_composite_blur.cc +++ b/source/blender/nodes/composite/nodes/node_composite_blur.cc @@ -5,11 +5,7 @@ * \ingroup cmpnodes */ -#include <cstdint> - -#include "BLI_array.hh" #include "BLI_assert.h" -#include "BLI_index_range.hh" #include "BLI_math_base.hh" #include "BLI_math_vec_types.hh" #include "BLI_math_vector.hh" @@ -19,12 +15,13 @@ #include "UI_interface.h" #include "UI_resources.h" -#include "RE_pipeline.h" - +#include "GPU_shader.h" #include "GPU_state.h" #include "GPU_texture.h" #include "COM_node_operation.hh" +#include "COM_symmetric_blur_weights.hh" +#include "COM_symmetric_separable_blur_weights.hh" #include "COM_utilities.hh" #include "node_composite_util.hh" @@ -92,192 +89,7 @@ static void node_composit_buts_blur(uiLayout *layout, bContext * /*C*/, PointerR using namespace blender::realtime_compositor; -/* A helper class that computes and caches a 1D GPU texture containing the weights of the separable - * filter of the given type and radius. The filter is assumed to be symmetric, because the filter - * functions are all even functions. Consequently, only the positive half of the filter is computed - * and the shader takes that into consideration. */ -class SymmetricSeparableBlurWeights { - private: - float radius_ = 1.0f; - int type_ = R_FILTER_GAUSS; - GPUTexture *texture_ = nullptr; - - public: - ~SymmetricSeparableBlurWeights() - { - if (texture_) { - GPU_texture_free(texture_); - } - } - - /* Check if a texture containing the weights was already computed for the given filter type and - * radius. If such texture exists, do nothing, otherwise, free the already computed texture and - * recompute it with the given filter type and radius. */ - void update(float radius, int type) - { - if (texture_ && type == type_ && radius == radius_) { - return; - } - - if (texture_) { - GPU_texture_free(texture_); - } - - /* The size of filter is double the radius plus 1, but since the filter is symmetric, we only - * compute half of it and no doubling happens. We add 1 to make sure the filter size is always - * odd and there is a center weight. */ - const int size = math::ceil(radius) + 1; - Array<float> weights(size); - - float sum = 0.0f; - - /* First, compute the center weight. */ - const float center_weight = RE_filter_value(type, 0.0f); - weights[0] = center_weight; - sum += center_weight; - - /* Second, compute the other weights in the positive direction, making sure to add double the - * weight to the sum of weights because the filter is symmetric and we only loop over half of - * it. Skip the center weight already computed by dropping the front index. */ - const float scale = radius > 0.0f ? 1.0f / radius : 0.0f; - for (const int i : weights.index_range().drop_front(1)) { - const float weight = RE_filter_value(type, i * scale); - weights[i] = weight; - sum += weight * 2.0f; - } - - /* Finally, normalize the weights. */ - for (const int i : weights.index_range()) { - weights[i] /= sum; - } - - texture_ = GPU_texture_create_1d("Weights", size, 1, GPU_R16F, weights.data()); - - type_ = type; - radius_ = radius; - } - - void bind_as_texture(GPUShader *shader, const char *texture_name) - { - const int texture_image_unit = GPU_shader_get_texture_binding(shader, texture_name); - GPU_texture_bind(texture_, texture_image_unit); - } - - void unbind_as_texture() - { - GPU_texture_unbind(texture_); - } -}; - -/* A helper class that computes and caches a 2D GPU texture containing the weights of the filter of - * the given type and radius. The filter is assumed to be symmetric, because the filter functions - * are evaluated on the normalized distance to the center. Consequently, only the upper right - * quadrant are computed and the shader takes that into consideration. */ -class SymmetricBlurWeights { - private: - int type_ = R_FILTER_GAUSS; - float2 radius_ = float2(1.0f); - GPUTexture *texture_ = nullptr; - - public: - ~SymmetricBlurWeights() - { - if (texture_) { - GPU_texture_free(texture_); - } - } - - /* Check if a texture containing the weights was already computed for the given filter type and - * radius. If such texture exists, do nothing, otherwise, free the already computed texture and - * recompute it with the given filter type and radius. */ - void update(float2 radius, int type) - { - if (texture_ && type == type_ && radius == radius_) { - return; - } - - if (texture_) { - GPU_texture_free(texture_); - } - - /* The full size of filter is double the radius plus 1, but since the filter is symmetric, we - * only compute a single quadrant of it and so no doubling happens. We add 1 to make sure the - * filter size is always odd and there is a center weight. */ - const float2 scale = math::safe_divide(float2(1.0f), radius); - const int2 size = int2(math::ceil(radius)) + int2(1); - Array<float> weights(size.x * size.y); - - float sum = 0.0f; - - /* First, compute the center weight. */ - const float center_weight = RE_filter_value(type, 0.0f); - weights[0] = center_weight; - sum += center_weight; - - /* Then, compute the weights along the positive x axis, making sure to add double the weight to - * the sum of weights because the filter is symmetric and we only loop over the positive half - * of the x axis. Skip the center weight already computed by dropping the front index. */ - for (const int x : IndexRange(size.x).drop_front(1)) { - const float weight = RE_filter_value(type, x * scale.x); - weights[x] = weight; - sum += weight * 2.0f; - } - - /* Then, compute the weights along the positive y axis, making sure to add double the weight to - * the sum of weights because the filter is symmetric and we only loop over the positive half - * of the y axis. Skip the center weight already computed by dropping the front index. */ - for (const int y : IndexRange(size.y).drop_front(1)) { - const float weight = RE_filter_value(type, y * scale.y); - weights[size.x * y] = weight; - sum += weight * 2.0f; - } - - /* Then, compute the other weights in the upper right quadrant, making sure to add quadruple - * the weight to the sum of weights because the filter is symmetric and we only loop over one - * quadrant of it. Skip the weights along the y and x axis already computed by dropping the - * front index. */ - for (const int y : IndexRange(size.y).drop_front(1)) { - for (const int x : IndexRange(size.x).drop_front(1)) { - const float weight = RE_filter_value(type, math::length(float2(x, y) * scale)); - weights[size.x * y + x] = weight; - sum += weight * 4.0f; - } - } - - /* Finally, normalize the weights. */ - for (const int y : IndexRange(size.y)) { - for (const int x : IndexRange(size.x)) { - weights[size.x * y + x] /= sum; - } - } - - texture_ = GPU_texture_create_2d("Weights", size.x, size.y, 1, GPU_R16F, weights.data()); - - type_ = type; - radius_ = radius; - } - - void bind_as_texture(GPUShader *shader, const char *texture_name) - { - const int texture_image_unit = GPU_shader_get_texture_binding(shader, texture_name); - GPU_texture_bind(texture_, texture_image_unit); - } - - void unbind_as_texture() - { - GPU_texture_unbind(texture_); - } -}; - class BlurOperation : public NodeOperation { - private: - /* Cached symmetric blur weights. */ - SymmetricBlurWeights blur_weights_; - /* Cached symmetric blur weights for the separable horizontal pass. */ - SymmetricSeparableBlurWeights blur_horizontal_weights_; - /* Cached symmetric blur weights for the separable vertical pass. */ - SymmetricSeparableBlurWeights blur_vertical_weights_; - public: using NodeOperation::NodeOperation; @@ -308,13 +120,16 @@ class BlurOperation : public NodeOperation { const Result &input_image = get_input("Image"); input_image.bind_as_texture(shader, "input_tx"); - blur_weights_.update(compute_blur_radius(), node_storage(bnode()).filtertype); - blur_weights_.bind_as_texture(shader, "weights_tx"); + const float2 blur_radius = compute_blur_radius(); + + const SymmetricBlurWeights &weights = context().cache_manager().get_symmetric_blur_weights( + node_storage(bnode()).filtertype, blur_radius); + weights.bind_as_texture(shader, "weights_tx"); Domain domain = compute_domain(); if (get_extend_bounds()) { /* Add a radius amount of pixels in both sides of the image, hence the multiply by 2. */ - domain.size += int2(math::ceil(compute_blur_radius())) * 2; + domain.size += int2(math::ceil(blur_radius)) * 2; } Result &output_image = get_result("Image"); @@ -326,7 +141,7 @@ class BlurOperation : public NodeOperation { GPU_shader_unbind(); output_image.unbind_as_image(); input_image.unbind_as_texture(); - blur_weights_.unbind_as_texture(); + weights.unbind_as_texture(); } GPUTexture *execute_separable_blur_horizontal_pass() @@ -341,12 +156,16 @@ class BlurOperation : public NodeOperation { const Result &input_image = get_input("Image"); input_image.bind_as_texture(shader, "input_tx"); - blur_horizontal_weights_.update(compute_blur_radius().x, node_storage(bnode()).filtertype); - blur_horizontal_weights_.bind_as_texture(shader, "weights_tx"); + const float2 blur_radius = compute_blur_radius(); + + const SymmetricSeparableBlurWeights &weights = + context().cache_manager().get_symmetric_separable_blur_weights( + node_storage(bnode()).filtertype, blur_radius.x); + weights.bind_as_texture(shader, "weights_tx"); Domain domain = compute_domain(); if (get_extend_bounds()) { - domain.size.x += int(math::ceil(compute_blur_radius().x)) * 2; + domain.size.x += int(math::ceil(blur_radius.x)) * 2; } /* We allocate an output image of a transposed size, that is, with a height equivalent to the @@ -367,7 +186,7 @@ class BlurOperation : public NodeOperation { GPU_shader_unbind(); input_image.unbind_as_texture(); - blur_horizontal_weights_.unbind_as_texture(); + weights.unbind_as_texture(); GPU_texture_image_unbind(horizontal_pass_result); return horizontal_pass_result; @@ -386,8 +205,12 @@ class BlurOperation : public NodeOperation { const int texture_image_unit = GPU_shader_get_texture_binding(shader, "input_tx"); GPU_texture_bind(horizontal_pass_result, texture_image_unit); - blur_vertical_weights_.update(compute_blur_radius().y, node_storage(bnode()).filtertype); - blur_vertical_weights_.bind_as_texture(shader, "weights_tx"); + const float2 blur_radius = compute_blur_radius(); + + const SymmetricSeparableBlurWeights &weights = + context().cache_manager().get_symmetric_separable_blur_weights( + node_storage(bnode()).filtertype, blur_radius.y); + weights.bind_as_texture(shader, "weights_tx"); Domain domain = compute_domain(); if (get_extend_bounds()) { @@ -405,7 +228,7 @@ class BlurOperation : public NodeOperation { GPU_shader_unbind(); output_image.unbind_as_image(); - blur_vertical_weights_.unbind_as_texture(); + weights.unbind_as_texture(); GPU_texture_unbind(horizontal_pass_result); } diff --git a/source/blender/nodes/composite/nodes/node_composite_dilate.cc b/source/blender/nodes/composite/nodes/node_composite_dilate.cc index d659c46721d..6281ef7990b 100644 --- a/source/blender/nodes/composite/nodes/node_composite_dilate.cc +++ b/source/blender/nodes/composite/nodes/node_composite_dilate.cc @@ -5,25 +5,20 @@ * \ingroup cmpnodes */ -#include <cmath> - -#include "BLI_array.hh" #include "BLI_assert.h" #include "BLI_math_base.hh" - -#include "DNA_scene_types.h" +#include "BLI_math_vec_types.hh" #include "RNA_access.h" #include "UI_interface.h" #include "UI_resources.h" -#include "RE_pipeline.h" - #include "GPU_shader.h" #include "GPU_state.h" #include "GPU_texture.h" +#include "COM_morphological_distance_feather_weights.hh" #include "COM_node_operation.hh" #include "COM_utilities.hh" @@ -64,161 +59,7 @@ static void node_composit_buts_dilateerode(uiLayout *layout, bContext * /*C*/, P using namespace blender::realtime_compositor; -/* Computes a falloff that is equal to 1 at an input of zero and decrease to zero at an input of 1, - * with the rate of decrease depending on the falloff type. */ -static float compute_distance_falloff(float x, int falloff_type) -{ - x = 1.0f - x; - - switch (falloff_type) { - case PROP_SMOOTH: - return 3.0f * x * x - 2.0f * x * x * x; - case PROP_SPHERE: - return std::sqrt(2.0f * x - x * x); - case PROP_ROOT: - return std::sqrt(x); - case PROP_SHARP: - return x * x; - case PROP_INVSQUARE: - return x * (2.0f - x); - case PROP_LIN: - return x; - default: - BLI_assert_unreachable(); - return x; - } -} - -/* A helper class that computes and caches 1D GPU textures containing the weights of the separable - * Gaussian filter of the given radius as well as an inverse distance falloff of the given type and - * radius. The weights and falloffs are symmetric, because the Gaussian and falloff functions are - * all even functions. Consequently, only the positive half of the filter is computed and the - * shader takes that into consideration. */ -class SymmetricSeparableMorphologicalDistanceFeatherWeights { - private: - int radius_ = 1; - int falloff_type_ = PROP_SMOOTH; - GPUTexture *weights_texture_ = nullptr; - GPUTexture *distance_falloffs_texture_ = nullptr; - - public: - ~SymmetricSeparableMorphologicalDistanceFeatherWeights() - { - if (weights_texture_) { - GPU_texture_free(weights_texture_); - } - - if (distance_falloffs_texture_) { - GPU_texture_free(distance_falloffs_texture_); - } - } - - /* Check if textures containing the weights and distance falloffs were already computed for the - * given distance falloff type and radius. If such textures exists, do nothing, otherwise, free - * the already computed textures and recompute it with the given distance falloff type and - * radius. */ - void update(int radius, int falloff_type) - { - if (weights_texture_ && distance_falloffs_texture_ && falloff_type == falloff_type_ && - radius == radius_) { - return; - } - - radius_ = radius; - falloff_type_ = falloff_type; - - compute_weights(); - compute_distance_falloffs(); - } - - void compute_weights() - { - if (weights_texture_) { - GPU_texture_free(weights_texture_); - } - - /* The size of filter is double the radius plus 1, but since the filter is symmetric, we only - * compute half of it and no doubling happens. We add 1 to make sure the filter size is always - * odd and there is a center weight. */ - const int size = radius_ + 1; - Array<float> weights(size); - - float sum = 0.0f; - - /* First, compute the center weight. */ - const float center_weight = RE_filter_value(R_FILTER_GAUSS, 0.0f); - weights[0] = center_weight; - sum += center_weight; - - /* Second, compute the other weights in the positive direction, making sure to add double the - * weight to the sum of weights because the filter is symmetric and we only loop over half of - * it. Skip the center weight already computed by dropping the front index. */ - const float scale = radius_ > 0.0f ? 1.0f / radius_ : 0.0f; - for (const int i : weights.index_range().drop_front(1)) { - const float weight = RE_filter_value(R_FILTER_GAUSS, i * scale); - weights[i] = weight; - sum += weight * 2.0f; - } - - /* Finally, normalize the weights. */ - for (const int i : weights.index_range()) { - weights[i] /= sum; - } - - weights_texture_ = GPU_texture_create_1d("Weights", size, 1, GPU_R16F, weights.data()); - } - - void compute_distance_falloffs() - { - if (distance_falloffs_texture_) { - GPU_texture_free(distance_falloffs_texture_); - } - - /* The size of the distance falloffs is double the radius plus 1, but since the falloffs are - * symmetric, we only compute half of them and no doubling happens. We add 1 to make sure the - * falloffs size is always odd and there is a center falloff. */ - const int size = radius_ + 1; - Array<float> falloffs(size); - - /* Compute the distance falloffs in the positive direction only, because the falloffs are - * symmetric. */ - const float scale = radius_ > 0.0f ? 1.0f / radius_ : 0.0f; - for (const int i : falloffs.index_range()) { - falloffs[i] = compute_distance_falloff(i * scale, falloff_type_); - } - - distance_falloffs_texture_ = GPU_texture_create_1d( - "Distance Factors", size, 1, GPU_R16F, falloffs.data()); - } - - void bind_weights_as_texture(GPUShader *shader, const char *texture_name) - { - const int texture_image_unit = GPU_shader_get_texture_binding(shader, texture_name); - GPU_texture_bind(weights_texture_, texture_image_unit); - } - - void unbind_weights_as_texture() - { - GPU_texture_unbind(weights_texture_); - } - - void bind_distance_falloffs_as_texture(GPUShader *shader, const char *texture_name) - { - const int texture_image_unit = GPU_shader_get_texture_binding(shader, texture_name); - GPU_texture_bind(distance_falloffs_texture_, texture_image_unit); - } - - void unbind_distance_falloffs_as_texture() - { - GPU_texture_unbind(distance_falloffs_texture_); - } -}; - class DilateErodeOperation : public NodeOperation { - private: - /* Cached symmetric blur weights and distance falloffs for the distance feature method. */ - SymmetricSeparableMorphologicalDistanceFeatherWeights distance_feather_weights_; - public: using NodeOperation::NodeOperation; @@ -414,9 +255,11 @@ class DilateErodeOperation : public NodeOperation { const Result &input_image = get_input("Mask"); input_image.bind_as_texture(shader, "input_tx"); - distance_feather_weights_.update(math::abs(get_distance()), node_storage(bnode()).falloff); - distance_feather_weights_.bind_weights_as_texture(shader, "weights_tx"); - distance_feather_weights_.bind_distance_falloffs_as_texture(shader, "falloffs_tx"); + const MorphologicalDistanceFeatherWeights &weights = + context().cache_manager().get_morphological_distance_feather_weights( + node_storage(bnode()).falloff, math::abs(get_distance())); + weights.bind_weights_as_texture(shader, "weights_tx"); + weights.bind_distance_falloffs_as_texture(shader, "falloffs_tx"); /* We allocate an output image of a transposed size, that is, with a height equivalent to the * width of the input and vice versa. This is done as a performance optimization. The shader @@ -437,8 +280,8 @@ class DilateErodeOperation : public NodeOperation { GPU_shader_unbind(); input_image.unbind_as_texture(); - distance_feather_weights_.unbind_weights_as_texture(); - distance_feather_weights_.unbind_distance_falloffs_as_texture(); + weights.unbind_weights_as_texture(); + weights.unbind_distance_falloffs_as_texture(); GPU_texture_image_unbind(horizontal_pass_result); return horizontal_pass_result; @@ -453,9 +296,11 @@ class DilateErodeOperation : public NodeOperation { const int texture_image_unit = GPU_shader_get_texture_binding(shader, "input_tx"); GPU_texture_bind(horizontal_pass_result, texture_image_unit); - distance_feather_weights_.update(math::abs(get_distance()), node_storage(bnode()).falloff); - distance_feather_weights_.bind_weights_as_texture(shader, "weights_tx"); - distance_feather_weights_.bind_distance_falloffs_as_texture(shader, "falloffs_tx"); + const MorphologicalDistanceFeatherWeights &weights = + context().cache_manager().get_morphological_distance_feather_weights( + node_storage(bnode()).falloff, math::abs(get_distance())); + weights.bind_weights_as_texture(shader, "weights_tx"); + weights.bind_distance_falloffs_as_texture(shader, "falloffs_tx"); const Domain domain = compute_domain(); Result &output_image = get_result("Mask"); @@ -468,8 +313,8 @@ class DilateErodeOperation : public NodeOperation { GPU_shader_unbind(); output_image.unbind_as_image(); - distance_feather_weights_.unbind_weights_as_texture(); - distance_feather_weights_.unbind_distance_falloffs_as_texture(); + weights.unbind_weights_as_texture(); + weights.unbind_distance_falloffs_as_texture(); GPU_texture_unbind(horizontal_pass_result); } |