Compositor: Allow non-pooled result allocation

This patch allows the result to be allocated using new textures as
opposed to using pooled texture. This is useful to allow the result
class to be used for persistent data like cached resources which can't
be pooled since it spans multiple evaluations.
This commit is contained in:
Omar Emara
2024-08-13 11:30:48 +03:00
parent 588e016d4e
commit 10f64edb3a
6 changed files with 71 additions and 68 deletions

View File

@@ -49,8 +49,9 @@ enum class ResultPrecision : uint8_t {
* represent an image or a single value. A result is typed, and can be of type color, vector, or
* float. Single value results are stored in 1x1 textures to make them easily accessible in
* shaders. But the same value is also stored in the value union member of the result for any
* host-side processing. The texture of the result is allocated from the texture pool of the
* context referenced by the result.
* host-side processing. The GPU texture of the result can either be allocated from the texture
* 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
* zero. After constructing a result, the set_initial_reference_count method is called to declare
@@ -129,6 +130,10 @@ class Result {
* 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. */
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,
* this is irrelevant. */
bool is_from_pool_ = false;
public:
/* Stores extra information about the result such as image meta data that can eventually be
@@ -155,8 +160,13 @@ class Result {
eGPUTextureFormat get_texture_format() const;
/* Declare the result to be a texture result, allocate a texture of an appropriate type with
* the size of the given domain from the texture pool, and set the domain of the result to the
* given domain.
* the size of the given domain, and set the domain of the result to the given domain.
*
* If from_pool is true, the texture will be allocated from the texture pool of the context,
* otherwise, a new texture will be allocated. Pooling should not be be used for persistent
* results that might span more than one evaluation, like cached resources. While pooling should
* be used for most other cases where the result will be allocated then later released in the
* same evaluation.
*
* If the result should not be computed, that is, should_compute() returns false, yet this method
* is called, that means the result is only being allocated because the shader that computes it
@@ -168,7 +178,7 @@ class Result {
* to images are safe. Since this result is not referenced by any other operation, it should be
* manually released after the operation is evaluated, which is implemented by calling the
* Operation::release_unneeded_results() method. */
void allocate_texture(Domain domain);
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

View File

@@ -14,6 +14,7 @@
#include "GPU_texture.hh"
#include "COM_cached_resource.hh"
#include "COM_result.hh"
namespace blender::realtime_compositor {
@@ -42,11 +43,11 @@ bool operator==(const BokehKernelKey &a, const BokehKernelKey &b);
/* -------------------------------------------------------------------------------------------------
* Bokeh Kernel.
*
* A cached resource that computes and caches a GPU texture containing the unnormalized convolution
* A cached resource that computes and caches a result containing the unnormalized convolution
* kernel, which when convolved with an image emulates a bokeh lens with the given parameters. */
class BokehKernel : public CachedResource {
private:
GPUTexture *texture_ = nullptr;
public:
Result result;
public:
BokehKernel(Context &context,
@@ -58,12 +59,6 @@ class BokehKernel : public CachedResource {
float lens_shift);
~BokehKernel();
void bind_as_texture(GPUShader *shader, const char *texture_name) const;
void unbind_as_texture() const;
GPUTexture *texture() const;
};
/* ------------------------------------------------------------------------------------------------
@@ -80,13 +75,13 @@ class BokehKernelContainer : CachedResourceContainer {
* container, if one exists, return it, otherwise, return a newly created one and add it to the
* container. In both cases, tag the cached resource as needed to keep it cached for the next
* evaluation. */
BokehKernel &get(Context &context,
int2 size,
int sides,
float rotation,
float roundness,
float catadioptric,
float lens_shift);
Result &get(Context &context,
int2 size,
int sides,
float rotation,
float roundness,
float catadioptric,
float lens_shift);
};
} // namespace blender::realtime_compositor

View File

@@ -10,7 +10,6 @@
#include "BLI_math_vector_types.hh"
#include "GPU_shader.hh"
#include "GPU_texture.hh"
#include "COM_bokeh_kernel.hh"
#include "COM_context.hh"
@@ -74,16 +73,8 @@ BokehKernel::BokehKernel(Context &context,
float roundness,
float catadioptric,
float lens_shift)
: result(context.create_result(ResultType::Color))
{
texture_ = GPU_texture_create_2d(
"Bokeh Kernel",
size.x,
size.y,
1,
Result::texture_format(ResultType::Color, context.get_precision()),
GPU_TEXTURE_USAGE_SHADER_READ | GPU_TEXTURE_USAGE_SHADER_WRITE,
nullptr);
GPUShader *shader = context.get_shader("compositor_bokeh_image");
GPU_shader_bind(shader);
@@ -93,34 +84,18 @@ BokehKernel::BokehKernel(Context &context,
GPU_shader_uniform_1f(shader, "catadioptric", catadioptric);
GPU_shader_uniform_1f(shader, "lens_shift", lens_shift);
const int image_unit = GPU_shader_get_sampler_binding(shader, "output_img");
GPU_texture_image_bind(texture_, image_unit);
this->result.allocate_texture(Domain(size), false);
this->result.bind_as_image(shader, "output_img");
compute_dispatch_threads_at_least(shader, size);
GPU_texture_image_unbind(texture_);
this->result.unbind_as_image();
GPU_shader_unbind();
}
BokehKernel::~BokehKernel()
{
GPU_texture_free(texture_);
}
void BokehKernel::bind_as_texture(GPUShader *shader, const char *texture_name) const
{
const int texture_image_unit = GPU_shader_get_sampler_binding(shader, texture_name);
GPU_texture_bind(texture_, texture_image_unit);
}
void BokehKernel::unbind_as_texture() const
{
GPU_texture_unbind(texture_);
}
GPUTexture *BokehKernel::texture() const
{
return texture_;
this->result.release();
}
/* --------------------------------------------------------------------
@@ -139,13 +114,13 @@ void BokehKernelContainer::reset()
}
}
BokehKernel &BokehKernelContainer::get(Context &context,
int2 size,
int sides,
float rotation,
float roundness,
float catadioptric,
float lens_shift)
Result &BokehKernelContainer::get(Context &context,
int2 size,
int sides,
float rotation,
float roundness,
float catadioptric,
float lens_shift)
{
const BokehKernelKey key(size, sides, rotation, roundness, catadioptric, lens_shift);
@@ -155,7 +130,7 @@ BokehKernel &BokehKernelContainer::get(Context &context,
});
bokeh_kernel.needed = true;
return bokeh_kernel;
return bokeh_kernel.result;
}
} // namespace blender::realtime_compositor

View File

@@ -171,7 +171,7 @@ eGPUTextureFormat Result::get_texture_format() const
return Result::texture_format(type_, precision_);
}
void Result::allocate_texture(Domain domain)
void Result::allocate_texture(Domain domain, bool from_pool)
{
/* The result is not actually needed, so allocate a dummy single value texture instead. See the
* method description for more information. */
@@ -183,7 +183,19 @@ void Result::allocate_texture(Domain domain)
is_single_value_ = false;
if (context_->use_gpu()) {
texture_ = context_->texture_pool().acquire(domain.size, get_texture_format());
is_from_pool_ = from_pool;
if (from_pool) {
texture_ = context_->texture_pool().acquire(domain.size, get_texture_format());
}
else {
texture_ = GPU_texture_create_2d("Compositor Texture",
domain.size.x,
domain.size.y,
1,
this->get_texture_format(),
GPU_TEXTURE_USAGE_GENERAL,
nullptr);
}
}
else {
/* TODO: Host side allocation. */
@@ -197,6 +209,7 @@ void Result::allocate_single_value()
/* Single values are stored in 1x1 textures as well as the single value members. */
const int2 texture_size{1, 1};
if (context_->use_gpu()) {
is_from_pool_ = true;
texture_ = context_->texture_pool().acquire(texture_size, get_texture_format());
}
else {
@@ -277,6 +290,7 @@ void Result::steal_data(Result &source)
BLI_assert(master_ == nullptr && source.master_ == nullptr);
is_single_value_ = source.is_single_value_;
is_from_pool_ = source.is_from_pool_;
texture_ = source.texture_;
context_ = source.context_;
domain_ = source.domain_;
@@ -419,12 +433,21 @@ void Result::release()
/* Decrement the reference count, and if it reaches zero, release the texture back into the
* texture pool. */
reference_count_--;
if (reference_count_ == 0) {
if (!is_external_) {
context_->texture_pool().release(texture_);
}
texture_ = nullptr;
if (reference_count_ != 0) {
return;
}
if (is_external_) {
return;
}
if (is_from_pool_) {
context_->texture_pool().release(texture_);
}
else {
GPU_texture_free(texture_);
}
texture_ = nullptr;
}
bool Result::should_compute()

View File

@@ -66,7 +66,7 @@ class BokehImageOperation : public NodeOperation {
{
const Domain domain = compute_domain();
const BokehKernel &bokeh_kernel = context().cache_manager().bokeh_kernels.get(
const Result &bokeh_kernel = context().cache_manager().bokeh_kernels.get(
context(),
domain.size,
node_storage(bnode()).flaps,

View File

@@ -124,7 +124,7 @@ class DefocusOperation : public NodeOperation {
const int sides = is_circle ? 3 : node_storage(bnode()).bktype;
const float rotation = node_storage(bnode()).rotation;
const float roundness = is_circle ? 1.0f : 0.0f;
const BokehKernel &bokeh_kernel = context().cache_manager().bokeh_kernels.get(
const Result &bokeh_kernel = context().cache_manager().bokeh_kernels.get(
context(), kernel_size, sides, rotation, roundness, 0.0f, 0.0f);
GPUShader *shader = context().get_shader("compositor_defocus_blur");