From 0037411f55ec3da4cfad79575d5531869ae5dc38 Mon Sep 17 00:00:00 2001 From: Omar Emara Date: Tue, 11 Oct 2022 13:22:52 +0200 Subject: Realtime Compositor: Implement parallel reduction This patch implements generic parallel reduction for the realtime compositor and implements the Levels operation as an example. This patch also introduces the notion of a "Compositor Algorithm", which is a reusable operation that can be used to construct other operations. Differential Revision: https://developer.blender.org/D16184 Reviewed By: Clement Foucault --- .../compositor/realtime_compositor/CMakeLists.txt | 4 + .../algorithms/COM_algorithm_parallel_reduction.hh | 58 ++++++ .../intern/algorithm_parallel_reduction.cc | 203 +++++++++++++++++++++ 3 files changed, 265 insertions(+) create mode 100644 source/blender/compositor/realtime_compositor/algorithms/COM_algorithm_parallel_reduction.hh create mode 100644 source/blender/compositor/realtime_compositor/algorithms/intern/algorithm_parallel_reduction.cc (limited to 'source/blender/compositor/realtime_compositor') diff --git a/source/blender/compositor/realtime_compositor/CMakeLists.txt b/source/blender/compositor/realtime_compositor/CMakeLists.txt index 1f1333332f5..5473a253241 100644 --- a/source/blender/compositor/realtime_compositor/CMakeLists.txt +++ b/source/blender/compositor/realtime_compositor/CMakeLists.txt @@ -53,6 +53,10 @@ set(SRC COM_static_shader_manager.hh COM_texture_pool.hh COM_utilities.hh + + algorithms/intern/algorithm_parallel_reduction.cc + + algorithms/COM_algorithm_parallel_reduction.hh ) set(LIB diff --git a/source/blender/compositor/realtime_compositor/algorithms/COM_algorithm_parallel_reduction.hh b/source/blender/compositor/realtime_compositor/algorithms/COM_algorithm_parallel_reduction.hh new file mode 100644 index 00000000000..9d0851eff84 --- /dev/null +++ b/source/blender/compositor/realtime_compositor/algorithms/COM_algorithm_parallel_reduction.hh @@ -0,0 +1,58 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#pragma once + +#include "BLI_math_vec_types.hh" + +#include "GPU_texture.h" + +#include "COM_context.hh" + +namespace blender::realtime_compositor { + +/* -------------------------------------------------------------------- + * Sum Reductions. + */ + +/* Computes the sum of the red channel of all pixels in the given texture. */ +float sum_red(Context &context, GPUTexture *texture); + +/* Computes the sum of the green channel of all pixels in the given texture. */ +float sum_green(Context &context, GPUTexture *texture); + +/* Computes the sum of the blue channel of all pixels in the given texture. */ +float sum_blue(Context &context, GPUTexture *texture); + +/* Computes the sum of the luminance of all pixels in the given texture, using the given luminance + * coefficients to compute the luminance. */ +float sum_luminance(Context &context, GPUTexture *texture, float3 luminance_coefficients); + +/* -------------------------------------------------------------------- + * Sum Of Squared Difference Reductions. + */ + +/* Computes the sum of the squared difference between the red channel of all pixels in the given + * texture and the given subtrahend. This can be used to compute the standard deviation if the + * given subtrahend is the mean. */ +float sum_red_squared_difference(Context &context, GPUTexture *texture, float subtrahend); + +/* Computes the sum of the squared difference between the green channel of all pixels in the given + * texture and the given subtrahend. This can be used to compute the standard deviation if the + * given subtrahend is the mean. */ +float sum_green_squared_difference(Context &context, GPUTexture *texture, float subtrahend); + +/* Computes the sum of the squared difference between the blue channel of all pixels in the given + * texture and the given subtrahend. This can be used to compute the standard deviation if the + * given subtrahend is the mean. */ +float sum_blue_squared_difference(Context &context, GPUTexture *texture, float subtrahend); + +/* Computes the sum of the squared difference between the luminance of all pixels in the given + * texture and the given subtrahend, using the given luminance coefficients to compute the + * luminance. This can be used to compute the standard deviation if the given subtrahend is the + * mean. */ +float sum_luminance_squared_difference(Context &context, + GPUTexture *texture, + float3 luminance_coefficients, + float subtrahend); + +} // namespace blender::realtime_compositor diff --git a/source/blender/compositor/realtime_compositor/algorithms/intern/algorithm_parallel_reduction.cc b/source/blender/compositor/realtime_compositor/algorithms/intern/algorithm_parallel_reduction.cc new file mode 100644 index 00000000000..7b571f381c6 --- /dev/null +++ b/source/blender/compositor/realtime_compositor/algorithms/intern/algorithm_parallel_reduction.cc @@ -0,0 +1,203 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "BLI_math_vec_types.hh" +#include "BLI_math_vector.hh" + +#include "MEM_guardedalloc.h" + +#include "GPU_compute.h" +#include "GPU_shader.h" +#include "GPU_texture.h" + +#include "COM_context.hh" +#include "COM_utilities.hh" + +namespace blender::realtime_compositor { + +/* Reduces the given texture into a single value and returns it. The return value should be freed + * by a call to MEM_freeN. The return value is either a pointer to a float, or a pointer to an + * array of floats that represents a vector. This depends on the given format, which should be + * compatible with the reduction shader. + * + * The given reduction shader should be bound when calling the function and the shader is expected + * to be derived from the compositor_parallel_reduction.glsl shader, see that file for more + * information. Also see the compositor_parallel_reduction_info.hh file for example shader + * definitions. */ +static float *parallel_reduction_dispatch(Context &context, + GPUTexture *texture, + GPUShader *shader, + eGPUTextureFormat format) +{ + GPU_shader_uniform_1b(shader, "is_initial_reduction", true); + + GPUTexture *texture_to_reduce = texture; + int2 size_to_reduce = int2(GPU_texture_width(texture), GPU_texture_height(texture)); + + /* Dispatch the reduction shader until the texture reduces to a single pixel. */ + while (size_to_reduce != int2(1)) { + const int2 reduced_size = math::divide_ceil(size_to_reduce, int2(16)); + GPUTexture *reduced_texture = context.texture_pool().acquire(reduced_size, format); + + GPU_memory_barrier(GPU_BARRIER_TEXTURE_FETCH); + const int texture_image_unit = GPU_shader_get_texture_binding(shader, "input_tx"); + GPU_texture_bind(texture_to_reduce, texture_image_unit); + + const int image_unit = GPU_shader_get_texture_binding(shader, "output_img"); + GPU_texture_image_bind(reduced_texture, image_unit); + + GPU_compute_dispatch(shader, reduced_size.x, reduced_size.y, 1); + + GPU_texture_image_unbind(reduced_texture); + GPU_texture_unbind(texture_to_reduce); + + /* Release the input texture only if it is not the source texture, since the source texture is + * not acquired or owned by the function. */ + if (texture_to_reduce != texture) { + context.texture_pool().release(texture_to_reduce); + } + + texture_to_reduce = reduced_texture; + size_to_reduce = reduced_size; + + GPU_shader_uniform_1b(shader, "is_initial_reduction", false); + } + + GPU_memory_barrier(GPU_BARRIER_TEXTURE_UPDATE); + float *pixel = static_cast(GPU_texture_read(texture_to_reduce, GPU_DATA_FLOAT, 0)); + + /* Release the final texture only if it is not the source texture, since the source texture is + * not acquired or owned by the function. */ + if (texture_to_reduce != texture) { + context.texture_pool().release(texture_to_reduce); + } + + return pixel; +} + +/* -------------------------------------------------------------------- + * Sum Reductions. + */ + +float sum_red(Context &context, GPUTexture *texture) +{ + GPUShader *shader = context.shader_manager().get("compositor_sum_red"); + GPU_shader_bind(shader); + + float *reduced_value = parallel_reduction_dispatch(context, texture, shader, GPU_R32F); + const float sum = *reduced_value; + MEM_freeN(reduced_value); + GPU_shader_unbind(); + + return sum; +} + +float sum_green(Context &context, GPUTexture *texture) +{ + GPUShader *shader = context.shader_manager().get("compositor_sum_green"); + GPU_shader_bind(shader); + + float *reduced_value = parallel_reduction_dispatch(context, texture, shader, GPU_R32F); + const float sum = *reduced_value; + MEM_freeN(reduced_value); + GPU_shader_unbind(); + + return sum; +} + +float sum_blue(Context &context, GPUTexture *texture) +{ + GPUShader *shader = context.shader_manager().get("compositor_sum_blue"); + GPU_shader_bind(shader); + + float *reduced_value = parallel_reduction_dispatch(context, texture, shader, GPU_R32F); + const float sum = *reduced_value; + MEM_freeN(reduced_value); + GPU_shader_unbind(); + + return sum; +} + +float sum_luminance(Context &context, GPUTexture *texture, float3 luminance_coefficients) +{ + GPUShader *shader = context.shader_manager().get("compositor_sum_luminance"); + GPU_shader_bind(shader); + + GPU_shader_uniform_3fv(shader, "luminance_coefficients", luminance_coefficients); + + float *reduced_value = parallel_reduction_dispatch(context, texture, shader, GPU_R32F); + const float sum = *reduced_value; + MEM_freeN(reduced_value); + GPU_shader_unbind(); + + return sum; +} + +/* -------------------------------------------------------------------- + * Sum Of Squared Difference Reductions. + */ + +float sum_red_squared_difference(Context &context, GPUTexture *texture, float subtrahend) +{ + GPUShader *shader = context.shader_manager().get("compositor_sum_red_squared_difference"); + GPU_shader_bind(shader); + + GPU_shader_uniform_1f(shader, "subtrahend", subtrahend); + + float *reduced_value = parallel_reduction_dispatch(context, texture, shader, GPU_R32F); + const float sum = *reduced_value; + MEM_freeN(reduced_value); + GPU_shader_unbind(); + + return sum; +} + +float sum_green_squared_difference(Context &context, GPUTexture *texture, float subtrahend) +{ + GPUShader *shader = context.shader_manager().get("compositor_sum_green_squared_difference"); + GPU_shader_bind(shader); + + GPU_shader_uniform_1f(shader, "subtrahend", subtrahend); + + float *reduced_value = parallel_reduction_dispatch(context, texture, shader, GPU_R32F); + const float sum = *reduced_value; + MEM_freeN(reduced_value); + GPU_shader_unbind(); + + return sum; +} + +float sum_blue_squared_difference(Context &context, GPUTexture *texture, float subtrahend) +{ + GPUShader *shader = context.shader_manager().get("compositor_sum_blue_squared_difference"); + GPU_shader_bind(shader); + + GPU_shader_uniform_1f(shader, "subtrahend", subtrahend); + + float *reduced_value = parallel_reduction_dispatch(context, texture, shader, GPU_R32F); + const float sum = *reduced_value; + MEM_freeN(reduced_value); + GPU_shader_unbind(); + + return sum; +} + +float sum_luminance_squared_difference(Context &context, + GPUTexture *texture, + float3 luminance_coefficients, + float subtrahend) +{ + GPUShader *shader = context.shader_manager().get("compositor_sum_luminance_squared_difference"); + GPU_shader_bind(shader); + + GPU_shader_uniform_3fv(shader, "luminance_coefficients", luminance_coefficients); + GPU_shader_uniform_1f(shader, "subtrahend", subtrahend); + + float *reduced_value = parallel_reduction_dispatch(context, texture, shader, GPU_R32F); + const float sum = *reduced_value; + MEM_freeN(reduced_value); + GPU_shader_unbind(); + + return sum; +} + +} // namespace blender::realtime_compositor -- cgit v1.2.3