Realtime Compositor: Cache bokeh kernel image

This patch creates a static cached resource from bokeh kernel images,
for better performance and reusability, since it will be used by the
Defocus node as well.
This commit is contained in:
Omar Emara
2023-12-11 19:43:03 +02:00
parent 4c14557424
commit bcea221021
6 changed files with 269 additions and 31 deletions

View File

@@ -83,6 +83,7 @@ set(SRC
algorithms/COM_algorithm_symmetric_separable_blur_variable_size.hh
algorithms/COM_algorithm_transform.hh
cached_resources/intern/bokeh_kernel.cc
cached_resources/intern/cached_mask.cc
cached_resources/intern/cached_shader.cc
cached_resources/intern/cached_texture.cc
@@ -94,6 +95,7 @@ set(SRC
cached_resources/intern/symmetric_blur_weights.cc
cached_resources/intern/symmetric_separable_blur_weights.cc
cached_resources/COM_bokeh_kernel.hh
cached_resources/COM_cached_mask.hh
cached_resources/COM_cached_resource.hh
cached_resources/COM_cached_shader.hh

View File

@@ -4,6 +4,7 @@
#pragma once
#include "COM_bokeh_kernel.hh"
#include "COM_cached_mask.hh"
#include "COM_cached_shader.hh"
#include "COM_cached_texture.hh"
@@ -51,6 +52,7 @@ class StaticCacheManager {
DistortionGridContainer distortion_grids;
KeyingScreenContainer keying_screens;
CachedShaderContainer cached_shaders;
BokehKernelContainer bokeh_kernels;
/* 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

View File

@@ -0,0 +1,92 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
#include <cstdint>
#include <memory>
#include "BLI_map.hh"
#include "BLI_math_vector_types.hh"
#include "GPU_shader.h"
#include "GPU_texture.h"
#include "COM_cached_resource.hh"
namespace blender::realtime_compositor {
class Context;
/* ------------------------------------------------------------------------------------------------
* Bokeh Kernel Key.
*/
class BokehKernelKey {
public:
int2 size;
int sides;
float rotation;
float roundness;
float catadioptric;
float lens_shift;
BokehKernelKey(
int2 size, int sides, float rotation, float roundness, float catadioptric, float lens_shift);
uint64_t hash() const;
};
bool operator==(const BokehKernelKey &a, const BokehKernelKey &b);
/* -------------------------------------------------------------------------------------------------
* Bokeh Kernel.
*
* A cached resource that computes and caches a GPU texture 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:
BokehKernel(Context &context,
int2 size,
int sides,
float rotation,
float roundness,
float catadioptric,
float lens_shift);
~BokehKernel();
void bind_as_texture(GPUShader *shader, const char *texture_name) const;
void unbind_as_texture() const;
GPUTexture *texture() const;
};
/* ------------------------------------------------------------------------------------------------
* Bokeh Kernel Container.
*/
class BokehKernelContainer : CachedResourceContainer {
private:
Map<BokehKernelKey, std::unique_ptr<BokehKernel>> map_;
public:
void reset() override;
/* Check if there is an available BokehKernel cached resource with the given parameters 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. */
BokehKernel &get(Context &context,
int2 size,
int sides,
float rotation,
float roundness,
float catadioptric,
float lens_shift);
};
} // namespace blender::realtime_compositor

View File

@@ -0,0 +1,161 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#include <cstdint>
#include <memory>
#include "BLI_hash.hh"
#include "BLI_math_base.h"
#include "BLI_math_vector_types.hh"
#include "GPU_shader.h"
#include "GPU_texture.h"
#include "COM_bokeh_kernel.hh"
#include "COM_context.hh"
#include "COM_result.hh"
#include "COM_utilities.hh"
namespace blender::realtime_compositor {
/* --------------------------------------------------------------------
* Bokeh Kernel Key.
*/
BokehKernelKey::BokehKernelKey(
int2 size, int sides, float rotation, float roundness, float catadioptric, float lens_shift)
: size(size),
sides(sides),
rotation(rotation),
roundness(roundness),
catadioptric(catadioptric),
lens_shift(lens_shift)
{
}
uint64_t BokehKernelKey::hash() const
{
return get_default_hash_3(
size, size, get_default_hash(float4(rotation, roundness, catadioptric, lens_shift)));
}
bool operator==(const BokehKernelKey &a, const BokehKernelKey &b)
{
return a.size == b.size && a.sides == b.sides && a.rotation == b.rotation &&
a.roundness == b.roundness && a.catadioptric == b.catadioptric &&
a.lens_shift == b.lens_shift;
}
/* --------------------------------------------------------------------
* Bokeh Kernel.
*/
/* The exterior angle is the angle between each two consecutive vertices of the regular polygon
* from its center. */
float compute_exterior_angle(int sides)
{
return (M_PI * 2.0f) / sides;
}
float compute_rotation(float angle, int sides)
{
/* Offset the rotation such that the second vertex of the regular polygon lies on the positive
* y axis, which is 90 degrees minus the angle that it makes with the positive x axis assuming
* the first vertex lies on the positive x axis. */
const float offset = M_PI_2 - compute_exterior_angle(sides);
return angle - offset;
}
BokehKernel::BokehKernel(Context &context,
int2 size,
int sides,
float rotation,
float roundness,
float catadioptric,
float lens_shift)
{
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,
nullptr);
GPUShader *shader = context.get_shader("compositor_bokeh_image");
GPU_shader_bind(shader);
GPU_shader_uniform_1f(shader, "exterior_angle", compute_exterior_angle(sides));
GPU_shader_uniform_1f(shader, "rotation", compute_rotation(rotation, sides));
GPU_shader_uniform_1f(shader, "roundness", roundness);
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);
compute_dispatch_threads_at_least(shader, size);
GPU_texture_image_unbind(texture_);
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_;
}
/* --------------------------------------------------------------------
* Bokeh Kernel Container.
*/
void BokehKernelContainer::reset()
{
/* First, delete all resources that are no longer needed. */
map_.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 : map_.values()) {
value->needed = false;
}
}
BokehKernel &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);
auto &bokeh_kernel = *map_.lookup_or_add_cb(key, [&]() {
return std::make_unique<BokehKernel>(
context, size, sides, rotation, roundness, catadioptric, lens_shift);
});
bokeh_kernel.needed = true;
return bokeh_kernel;
}
} // namespace blender::realtime_compositor

View File

@@ -18,6 +18,7 @@ void StaticCacheManager::reset()
distortion_grids.reset();
keying_screens.reset();
cached_shaders.reset();
bokeh_kernels.reset();
}
} // namespace blender::realtime_compositor

View File

@@ -14,6 +14,7 @@
#include "GPU_shader.h"
#include "COM_bokeh_kernel.hh"
#include "COM_node_operation.hh"
#include "COM_utilities.hh"
@@ -64,46 +65,25 @@ class BokehImageOperation : public NodeOperation {
void execute() override
{
GPUShader *shader = context().get_shader("compositor_bokeh_image");
GPU_shader_bind(shader);
const Domain domain = compute_domain();
GPU_shader_uniform_1f(shader, "exterior_angle", get_exterior_angle());
GPU_shader_uniform_1f(shader, "rotation", get_rotation());
GPU_shader_uniform_1f(shader, "roundness", node_storage(bnode()).rounding);
GPU_shader_uniform_1f(shader, "catadioptric", node_storage(bnode()).catadioptric);
GPU_shader_uniform_1f(shader, "lens_shift", node_storage(bnode()).lensshift);
const BokehKernel &bokeh_kernel = context().cache_manager().bokeh_kernels.get(
context(),
domain.size,
node_storage(bnode()).flaps,
node_storage(bnode()).angle,
node_storage(bnode()).rounding,
node_storage(bnode()).catadioptric,
node_storage(bnode()).lensshift);
Result &output = get_result("Image");
const Domain domain = compute_domain();
output.allocate_texture(domain);
output.bind_as_image(shader, "output_img");
compute_dispatch_threads_at_least(shader, domain.size);
output.unbind_as_image();
GPU_shader_unbind();
output.wrap_external(bokeh_kernel.texture());
}
Domain compute_domain() override
{
return Domain(int2(512));
}
/* The exterior angle is the angle between each two consecutive vertices of the regular polygon
* from its center. */
float get_exterior_angle()
{
return (M_PI * 2.0f) / node_storage(bnode()).flaps;
}
float get_rotation()
{
/* Offset the rotation such that the second vertex of the regular polygon lies on the positive
* y axis, which is 90 degrees minus the angle that it makes with the positive x axis assuming
* the first vertex lies on the positive x axis. */
const float offset = M_PI_2 - get_exterior_angle();
return node_storage(bnode()).angle - offset;
}
};
static NodeOperation *get_compositor_operation(Context &context, DNode node)