Compositor: Support Image node in new CPU compositor
This patch supports the Image node in the new CPU compositor.
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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. */
|
||||
|
||||
@@ -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. */
|
||||
|
||||
Reference in New Issue
Block a user