Compositor: Support Image node in new CPU compositor

This patch supports the Image node in the new CPU compositor.
This commit is contained in:
Omar Emara
2024-08-19 18:38:50 +03:00
parent 0d4e2ea40d
commit 5930e0404a
9 changed files with 146 additions and 49 deletions

View File

@@ -68,6 +68,7 @@ set(SRC
COM_utilities.hh
algorithms/intern/deriche_gaussian_blur.cc
algorithms/intern/extract_alpha.cc
algorithms/intern/jump_flooding.cc
algorithms/intern/morphological_blur.cc
algorithms/intern/morphological_distance.cc
@@ -83,6 +84,7 @@ set(SRC
algorithms/intern/van_vliet_gaussian_blur.cc
algorithms/COM_algorithm_deriche_gaussian_blur.hh
algorithms/COM_algorithm_extract_alpha.hh
algorithms/COM_algorithm_jump_flooding.hh
algorithms/COM_algorithm_morphological_blur.hh
algorithms/COM_algorithm_morphological_distance.hh

View File

@@ -179,6 +179,9 @@ class Result {
/* Returns the type of the given GPU texture format. */
static ResultType type(eGPUTextureFormat format);
/* Returns the float type of the result given the channels count. */
static ResultType float_type(const int channels_count);
/* Implicit conversion to the internal GPU texture. */
operator GPUTexture *() const;

View File

@@ -0,0 +1,16 @@
/* SPDX-FileCopyrightText: 2024 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
#include "COM_context.hh"
#include "COM_result.hh"
namespace blender::realtime_compositor {
/* Extracts the alpha channel from the given input and write it to the given output. The output
* will be allocated internally and is thus expected not to be previously allocated. */
void extract_alpha(Context &context, Result &input, Result &output);
} // namespace blender::realtime_compositor

View File

@@ -0,0 +1,53 @@
/* SPDX-FileCopyrightText: 2024 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#include "BLI_index_range.hh"
#include "BLI_task.hh"
#include "GPU_shader.hh"
#include "COM_context.hh"
#include "COM_result.hh"
#include "COM_utilities.hh"
#include "COM_algorithm_extract_alpha.hh"
namespace blender::realtime_compositor {
static void extract_alpha_gpu(Context &context, Result &input, Result &output)
{
GPUShader *shader = context.get_shader("compositor_convert_color_to_alpha");
GPU_shader_bind(shader);
input.bind_as_texture(shader, "input_tx");
output.allocate_texture(input.domain());
output.bind_as_image(shader, "output_img");
compute_dispatch_threads_at_least(shader, input.domain().size);
GPU_shader_unbind();
input.unbind_as_texture();
output.unbind_as_image();
}
static void extract_alpha_cpu(Result &input, Result &output)
{
output.allocate_texture(input.domain());
parallel_for(input.domain().size, [&](const int2 texel) {
output.store_pixel(texel, float4(input.load_pixel(texel).w));
});
}
void extract_alpha(Context &context, Result &input, Result &output)
{
if (context.use_gpu()) {
extract_alpha_gpu(context, input, output);
}
else {
extract_alpha_cpu(input, output);
}
}
} // namespace blender::realtime_compositor

View File

@@ -15,6 +15,7 @@
#include "DNA_image_types.h"
#include "COM_cached_resource.hh"
#include "COM_result.hh"
namespace blender::realtime_compositor {
@@ -38,18 +39,21 @@ bool operator==(const CachedImageKey &a, const CachedImageKey &b);
/* -------------------------------------------------------------------------------------------------
* Cached Image.
*
* A cached resource that computes and caches a GPU texture containing the contents of the image
* with the given image user. */
* A cached resource that computes and caches a result containing the contents of the image with
* the given image user. */
class CachedImage : public CachedResource {
public:
Result result;
private:
/* For GPU, the result wraps an external GPU texture that is generated by the IMB module and
* stored in this member to be freed when the cached resource is deleted. */
GPUTexture *texture_ = nullptr;
public:
CachedImage(Context &context, Image *image, ImageUser *image_user, const char *pass_name);
~CachedImage();
GPUTexture *texture();
};
/* ------------------------------------------------------------------------------------------------
@@ -68,10 +72,7 @@ class CachedImageContainer : CachedResourceContainer {
* CachedImage cached resource with the given image user and pass_name in the 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. */
GPUTexture *get(Context &context,
Image *image,
const ImageUser *image_user,
const char *pass_name);
Result *get(Context &context, Image *image, const ImageUser *image_user, const char *pass_name);
};
} // namespace blender::realtime_compositor

View File

@@ -192,6 +192,7 @@ CachedImage::CachedImage(Context &context,
Image *image,
ImageUser *image_user,
const char *pass_name)
: result(context)
{
/* We can't retrieve the needed image buffer yet, because we still need to assign the pass index
* to the image user in order to acquire the image buffer corresponding to the given pass name.
@@ -212,8 +213,32 @@ CachedImage::CachedImage(Context &context,
ImBuf *image_buffer = BKE_image_acquire_ibuf(image, &image_user_for_pass, nullptr);
ImBuf *linear_image_buffer = compute_linear_buffer(image_buffer);
texture_ = IMB_create_gpu_texture("Image Texture", linear_image_buffer, true, true);
GPU_texture_update_mipmap_chain(texture_);
const bool use_half_float = linear_image_buffer->flags & IB_halffloat;
this->result.set_precision(use_half_float ? ResultPrecision::Half : ResultPrecision::Full);
/* At the user level, vector images are always treated as color, so there are only two possible
* options, float images and color images. 3-channel images should then be converted to 4-channel
* images below. */
const bool is_single_channel = linear_image_buffer->channels == 1;
this->result.set_type(is_single_channel ? ResultType::Float : ResultType::Color);
/* For GPU, we wrap the texture returned by IMB module and free it ourselves in destructor. For
* CPU, we allocate the result and copy to it from the image buffer. */
if (context.use_gpu()) {
texture_ = IMB_create_gpu_texture("Image Texture", linear_image_buffer, true, true);
GPU_texture_update_mipmap_chain(texture_);
this->result.wrap_external(texture_);
}
else {
const int2 size = int2(image_buffer->x, image_buffer->y);
const int channels_count = linear_image_buffer->channels;
Result buffer_result(context, Result::float_type(channels_count), ResultPrecision::Full);
buffer_result.wrap_external(linear_image_buffer->float_buffer.data, size);
this->result.allocate_texture(size, false);
parallel_for(size, [&](const int2 texel) {
this->result.store_pixel(texel, buffer_result.load_pixel(texel));
});
}
IMB_freeImBuf(linear_image_buffer);
BKE_image_release_ibuf(image, image_buffer, nullptr);
@@ -221,14 +246,10 @@ CachedImage::CachedImage(Context &context,
CachedImage::~CachedImage()
{
this->result.release();
GPU_TEXTURE_FREE_SAFE(texture_);
}
GPUTexture *CachedImage::texture()
{
return texture_;
}
/* --------------------------------------------------------------------
* Cached Image Container.
*/
@@ -250,10 +271,10 @@ void CachedImageContainer::reset()
}
}
GPUTexture *CachedImageContainer::get(Context &context,
Image *image,
const ImageUser *image_user,
const char *pass_name)
Result *CachedImageContainer::get(Context &context,
Image *image,
const ImageUser *image_user,
const char *pass_name)
{
if (!image || !image_user) {
return nullptr;
@@ -279,7 +300,7 @@ GPUTexture *CachedImageContainer::get(Context &context,
});
cached_image.needed = true;
return cached_image.texture();
return &cached_image.result;
}
} // namespace blender::realtime_compositor

View File

@@ -171,6 +171,25 @@ ResultType Result::type(eGPUTextureFormat format)
return ResultType::Color;
}
ResultType Result::float_type(const int channels_count)
{
switch (channels_count) {
case 1:
return ResultType::Float;
case 2:
return ResultType::Float2;
case 3:
return ResultType::Float3;
case 4:
return ResultType::Color;
default:
break;
}
BLI_assert_unreachable();
return ResultType::Color;
}
Result::operator GPUTexture *() const
{
BLI_assert(storage_type_ == ResultStorageType::GPU);

View File

@@ -619,9 +619,9 @@ class CryptoMatteOperation : public BaseCryptoMatteOperation {
continue;
}
GPUTexture *pass_texture = context().cache_manager().cached_images.get(
Result *pass_result = context().cache_manager().cached_images.get(
context(), image, &image_user_for_layer, render_pass->name);
layers.append(pass_texture);
layers.append(*pass_result);
}
/* If we already found Cryptomatte layers, no need to check other render layers. */

View File

@@ -38,6 +38,7 @@
#include "GPU_shader.hh"
#include "GPU_texture.hh"
#include "COM_algorithm_extract_alpha.hh"
#include "COM_node_operation.hh"
#include "COM_utilities.hh"
@@ -456,41 +457,22 @@ class ImageOperation : public NodeOperation {
return;
}
GPUTexture *image_texture = context().cache_manager().cached_images.get(
Result *cached_image = context().cache_manager().cached_images.get(
context(), get_image(), get_image_user(), get_pass_name(identifier));
Result &result = get_result(identifier);
if (!image_texture) {
if (!cached_image) {
result.allocate_invalid();
return;
}
const ResultPrecision precision = Result::precision(GPU_texture_format(image_texture));
/* Alpha is mot an actual pass, but one that is extracted from the combined pass. So we need to
* extract it using a shader. */
if (identifier != "Alpha") {
result.set_precision(precision);
result.wrap_external(image_texture);
return;
/* Alpha is not an actual pass, but one that is extracted from the combined pass. */
if (identifier == "Alpha") {
extract_alpha(context(), *cached_image, result);
}
else {
cached_image->pass_through(result);
}
GPUShader *shader = context().get_shader("compositor_convert_color_to_alpha", precision);
GPU_shader_bind(shader);
const int input_unit = GPU_shader_get_sampler_binding(shader, "input_tx");
GPU_texture_bind(image_texture, input_unit);
const int2 size = int2(GPU_texture_width(image_texture), GPU_texture_height(image_texture));
result.allocate_texture(Domain(size));
result.bind_as_image(shader, "output_img");
compute_dispatch_threads_at_least(shader, size);
GPU_shader_unbind();
GPU_texture_unbind(image_texture);
result.unbind_as_image();
}
/* Get the name of the pass corresponding to the output with the given identifier. */