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:
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -18,6 +18,7 @@ void StaticCacheManager::reset()
|
||||
distortion_grids.reset();
|
||||
keying_screens.reset();
|
||||
cached_shaders.reset();
|
||||
bokeh_kernels.reset();
|
||||
}
|
||||
|
||||
} // namespace blender::realtime_compositor
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user