Realtime Compositor: Implement Simple Star Glare node

This patch implements the Simple Star Glare node. This is only an approximation
of the existing implementation in the CPU compositor, an approximation that
removes the row-column dependency in the original algorithm, yielding an order
of magnitude faster computations. The difference due to the approximation is
readily visible in artificial test cases, but is less visible in actual use
cases, so it was agreed that this approximation is worthwhile.

For the future, we can look into approximating this further using a closed form
IIR recursive filter with parallel interconnection and block-based parallelism.
Which is expected to yield another order of magnitude faster computations.

The different passes can potentially be combined into a single shader with some
preprocessor tricks, but doing that complicated that code in a way that makes
it difficult to experiment with future optimizations, so I decided to leave it
as is for now.

Differential Revision: https://developer.blender.org/D16724

Reviewed By: Clement Foucault
This commit is contained in:
Omar Emara 2022-12-09 17:04:34 +02:00
parent 0d21b9e2da
commit 5ee116d448
8 changed files with 534 additions and 5 deletions

View File

@ -102,6 +102,10 @@ set(GLSL_SRC
shaders/compositor_glare_ghost_base.glsl
shaders/compositor_glare_highlights.glsl
shaders/compositor_glare_mix.glsl
shaders/compositor_glare_simple_star_anti_diagonal_pass.glsl
shaders/compositor_glare_simple_star_diagonal_pass.glsl
shaders/compositor_glare_simple_star_horizontal_pass.glsl
shaders/compositor_glare_simple_star_vertical_pass.glsl
shaders/compositor_image_crop.glsl
shaders/compositor_morphological_distance.glsl
shaders/compositor_morphological_distance_feather.glsl
@ -135,6 +139,7 @@ set(GLSL_SRC
shaders/library/gpu_shader_compositor_gamma.glsl
shaders/library/gpu_shader_compositor_hue_correct.glsl
shaders/library/gpu_shader_compositor_hue_saturation_value.glsl
shaders/library/gpu_shader_compositor_image_diagonals.glsl
shaders/library/gpu_shader_compositor_invert.glsl
shaders/library/gpu_shader_compositor_luminance_matte.glsl
shaders/library/gpu_shader_compositor_main.glsl

View File

@ -0,0 +1,55 @@
#pragma BLENDER_REQUIRE(gpu_shader_compositor_image_diagonals.glsl)
#pragma BLENDER_REQUIRE(gpu_shader_compositor_texture_utilities.glsl)
void main()
{
ivec2 size = imageSize(anti_diagonal_img);
int index = int(gl_GlobalInvocationID.x);
int anti_diagonal_length = compute_anti_diagonal_length(size, index);
ivec2 start = compute_anti_diagonal_start(size, index);
ivec2 direction = get_anti_diagonal_direction();
ivec2 end = start + (anti_diagonal_length - 1) * direction;
/* For each iteration, apply a causal filter followed by a non causal filters along the anti
* diagonal mapped to the current thread invocation. */
for (int i = 0; i < iterations; i++) {
/* Causal Pass:
* Sequentially apply a causal filter running from the start of the anti diagonal to its end by
* mixing the value of the pixel in the anti diagonal with the average value of the previous
* output and next input in the same anti diagonal. */
for (int j = 0; j < anti_diagonal_length; j++) {
ivec2 texel = start + j * direction;
vec4 previous_output = imageLoad(anti_diagonal_img, texel - i * direction);
vec4 current_input = imageLoad(anti_diagonal_img, texel);
vec4 next_input = imageLoad(anti_diagonal_img, texel + i * direction);
vec4 neighbour_average = (previous_output + next_input) / 2.0;
vec4 causal_output = mix(current_input, neighbour_average, fade_factor);
imageStore(anti_diagonal_img, texel, causal_output);
}
/* Non Causal Pass:
* Sequentially apply a non causal filter running from the end of the diagonal to its start by
* mixing the value of the pixel in the diagonal with the average value of the previous output
* and next input in the same diagonal. */
for (int j = 0; j < anti_diagonal_length; j++) {
ivec2 texel = end - j * direction;
vec4 previous_output = imageLoad(anti_diagonal_img, texel + i * direction);
vec4 current_input = imageLoad(anti_diagonal_img, texel);
vec4 next_input = imageLoad(anti_diagonal_img, texel - i * direction);
vec4 neighbour_average = (previous_output + next_input) / 2.0;
vec4 non_causal_output = mix(current_input, neighbour_average, fade_factor);
imageStore(anti_diagonal_img, texel, non_causal_output);
}
}
/* For each pixel in the anti diagonal mapped to the current invocation thread, add the result of
* the diagonal pass to the vertical pass. */
for (int j = 0; j < anti_diagonal_length; j++) {
ivec2 texel = start + j * direction;
vec4 horizontal = texture_load(diagonal_tx, texel);
vec4 vertical = imageLoad(anti_diagonal_img, texel);
imageStore(anti_diagonal_img, texel, horizontal + vertical);
}
}

View File

@ -0,0 +1,45 @@
#pragma BLENDER_REQUIRE(gpu_shader_compositor_image_diagonals.glsl)
void main()
{
ivec2 size = imageSize(diagonal_img);
int index = int(gl_GlobalInvocationID.x);
int diagonal_length = compute_diagonal_length(size, index);
ivec2 start = compute_diagonal_start(size, index);
ivec2 direction = get_diagonal_direction();
ivec2 end = start + (diagonal_length - 1) * direction;
/* For each iteration, apply a causal filter followed by a non causal filters along the diagonal
* mapped to the current thread invocation. */
for (int i = 0; i < iterations; i++) {
/* Causal Pass:
* Sequentially apply a causal filter running from the start of the diagonal to its end by
* mixing the value of the pixel in the diagonal with the average value of the previous output
* and next input in the same diagonal. */
for (int j = 0; j < diagonal_length; j++) {
ivec2 texel = start + j * direction;
vec4 previous_output = imageLoad(diagonal_img, texel - i * direction);
vec4 current_input = imageLoad(diagonal_img, texel);
vec4 next_input = imageLoad(diagonal_img, texel + i * direction);
vec4 neighbour_average = (previous_output + next_input) / 2.0;
vec4 causal_output = mix(current_input, neighbour_average, fade_factor);
imageStore(diagonal_img, texel, causal_output);
}
/* Non Causal Pass:
* Sequentially apply a non causal filter running from the end of the diagonal to its start by
* mixing the value of the pixel in the diagonal with the average value of the previous output
* and next input in the same diagonal. */
for (int j = 0; j < diagonal_length; j++) {
ivec2 texel = end - j * direction;
vec4 previous_output = imageLoad(diagonal_img, texel + i * direction);
vec4 current_input = imageLoad(diagonal_img, texel);
vec4 next_input = imageLoad(diagonal_img, texel - i * direction);
vec4 neighbour_average = (previous_output + next_input) / 2.0;
vec4 non_causal_output = mix(current_input, neighbour_average, fade_factor);
imageStore(diagonal_img, texel, non_causal_output);
}
}
}

View File

@ -0,0 +1,38 @@
void main()
{
int width = imageSize(horizontal_img).x;
/* For each iteration, apply a causal filter followed by a non causal filters along the row
* mapped to the current thread invocation. */
for (int i = 0; i < iterations; i++) {
/* Causal Pass:
* Sequentially apply a causal filter running from left to right by mixing the value of the
* pixel in the row with the average value of the previous output and next input in the same
* row. */
for (int x = 0; x < width; x++) {
ivec2 texel = ivec2(x, gl_GlobalInvocationID.x);
vec4 previous_output = imageLoad(horizontal_img, texel - ivec2(i, 0));
vec4 current_input = imageLoad(horizontal_img, texel);
vec4 next_input = imageLoad(horizontal_img, texel + ivec2(i, 0));
vec4 neighbour_average = (previous_output + next_input) / 2.0;
vec4 causal_output = mix(current_input, neighbour_average, fade_factor);
imageStore(horizontal_img, texel, causal_output);
}
/* Non Causal Pass:
* Sequentially apply a non causal filter running from right to left by mixing the value of the
* pixel in the row with the average value of the previous output and next input in the same
* row. */
for (int x = width - 1; x >= 0; x--) {
ivec2 texel = ivec2(x, gl_GlobalInvocationID.x);
vec4 previous_output = imageLoad(horizontal_img, texel + ivec2(i, 0));
vec4 current_input = imageLoad(horizontal_img, texel);
vec4 next_input = imageLoad(horizontal_img, texel - ivec2(i, 0));
vec4 neighbour_average = (previous_output + next_input) / 2.0;
vec4 non_causal_output = mix(current_input, neighbour_average, fade_factor);
imageStore(horizontal_img, texel, non_causal_output);
}
}
}

View File

@ -0,0 +1,49 @@
#pragma BLENDER_REQUIRE(gpu_shader_compositor_texture_utilities.glsl)
void main()
{
int height = imageSize(vertical_img).y;
/* For each iteration, apply a causal filter followed by a non causal filters along the column
* mapped to the current thread invocation. */
for (int i = 0; i < iterations; i++) {
/* Causal Pass:
* Sequentially apply a causal filter running from bottom to top by mixing the value of the
* pixel in the column with the average value of the previous output and next input in the same
* column. */
for (int y = 0; y < height; y++) {
ivec2 texel = ivec2(gl_GlobalInvocationID.x, y);
vec4 previous_output = imageLoad(vertical_img, texel - ivec2(0, i));
vec4 current_input = imageLoad(vertical_img, texel);
vec4 next_input = imageLoad(vertical_img, texel + ivec2(0, i));
vec4 neighbour_average = (previous_output + next_input) / 2.0;
vec4 causal_output = mix(current_input, neighbour_average, fade_factor);
imageStore(vertical_img, texel, causal_output);
}
/* Non Causal Pass:
* Sequentially apply a non causal filter running from top to bottom by mixing the value of the
* pixel in the column with the average value of the previous output and next input in the same
* column. */
for (int y = height - 1; y >= 0; y--) {
ivec2 texel = ivec2(gl_GlobalInvocationID.x, y);
vec4 previous_output = imageLoad(vertical_img, texel + ivec2(0, i));
vec4 current_input = imageLoad(vertical_img, texel);
vec4 next_input = imageLoad(vertical_img, texel - ivec2(0, i));
vec4 neighbour_average = (previous_output + next_input) / 2.0;
vec4 non_causal_output = mix(current_input, neighbour_average, fade_factor);
imageStore(vertical_img, texel, non_causal_output);
}
}
/* For each pixel in the column mapped to the current invocation thread, add the result of the
* horizontal pass to the vertical pass. */
for (int y = 0; y < height; y++) {
ivec2 texel = ivec2(gl_GlobalInvocationID.x, y);
vec4 horizontal = texture_load(horizontal_tx, texel);
vec4 vertical = imageLoad(vertical_img, texel);
imageStore(vertical_img, texel, horizontal + vertical);
}
}

View File

@ -44,3 +44,41 @@ GPU_SHADER_CREATE_INFO(compositor_glare_ghost_accumulate)
.image(0, GPU_RGBA16F, Qualifier::READ_WRITE, ImageType::FLOAT_2D, "accumulated_ghost_img")
.compute_source("compositor_glare_ghost_accumulate.glsl")
.do_static_compilation(true);
/* -----------
* Simple Star
* ----------- */
GPU_SHADER_CREATE_INFO(compositor_glare_simple_star_horizontal_pass)
.local_group_size(16)
.push_constant(Type::INT, "iterations")
.push_constant(Type::FLOAT, "fade_factor")
.image(0, GPU_RGBA16F, Qualifier::READ_WRITE, ImageType::FLOAT_2D, "horizontal_img")
.compute_source("compositor_glare_simple_star_horizontal_pass.glsl")
.do_static_compilation(true);
GPU_SHADER_CREATE_INFO(compositor_glare_simple_star_vertical_pass)
.local_group_size(16)
.push_constant(Type::INT, "iterations")
.push_constant(Type::FLOAT, "fade_factor")
.sampler(0, ImageType::FLOAT_2D, "horizontal_tx")
.image(0, GPU_RGBA16F, Qualifier::READ_WRITE, ImageType::FLOAT_2D, "vertical_img")
.compute_source("compositor_glare_simple_star_vertical_pass.glsl")
.do_static_compilation(true);
GPU_SHADER_CREATE_INFO(compositor_glare_simple_star_diagonal_pass)
.local_group_size(16)
.push_constant(Type::INT, "iterations")
.push_constant(Type::FLOAT, "fade_factor")
.image(0, GPU_RGBA16F, Qualifier::READ_WRITE, ImageType::FLOAT_2D, "diagonal_img")
.compute_source("compositor_glare_simple_star_diagonal_pass.glsl")
.do_static_compilation(true);
GPU_SHADER_CREATE_INFO(compositor_glare_simple_star_anti_diagonal_pass)
.local_group_size(16)
.push_constant(Type::INT, "iterations")
.push_constant(Type::FLOAT, "fade_factor")
.sampler(0, ImageType::FLOAT_2D, "diagonal_tx")
.image(0, GPU_RGBA16F, Qualifier::READ_WRITE, ImageType::FLOAT_2D, "anti_diagonal_img")
.compute_source("compositor_glare_simple_star_anti_diagonal_pass.glsl")
.do_static_compilation(true);

View File

@ -0,0 +1,170 @@
/* Computes the number of diagonals in the matrix of the given size, where the diagonals are
* indexed from the upper left corner to the lower right corner such that their start is at the
* left and bottom edges of the matrix as shown in the diagram below. The numbers in the diagram
* denote the index of the diagonal. The number of diagonals is then intuitively the number of
* values on the left and bottom edges, which is equal to:
*
* Number Of Diagonals => width + height - 1
*
* Notice that the minus one is due to the shared value in the corner.
*
* Width = 6
* +---+---+---+---+---+---+
* | 0 | 1 | 2 | 3 | 4 | 5 |
* +---+---+---+---+---+---+
* | 1 | 2 | 3 | 4 | 5 | 6 | Height = 3
* +---+---+---+---+---+---+
* | 2 | 3 | 4 | 5 | 6 | 7 |
* +---+---+---+---+---+---+
*/
int compute_number_of_diagonals(ivec2 size)
{
return size.x + size.y - 1;
}
/* Computes the number of values in the diagonal of the given index in the matrix with the given
* size, where the diagonals are indexed from the upper left corner to the lower right corner such
* that their start is at the left and bottom edges of the matrix as shown in the diagram below.
* The numbers in the diagram denote the index of the diagonal and its length.
*
* Width = 6
* +---+---+---+---+---+---+
* 1 | 0 | 1 | 2 | 3 | 4 | 5 |
* +---+---+---+---+---+---+
* 2 | 1 | 2 | 3 | 4 | 5 | 6 | Height = 3
* +---+---+---+---+---+---+
* | 2 | 3 | 4 | 5 | 6 | 7 |
* +---+---+---+---+---+---+
* 3 3 3 3 2 1
*
* To derive the length of the diagonal from the index, we note that the lengths of the diagonals
* start at 1 and linearly increase up to the length of the longest diagonal, then remain constant
* until it linearly decrease to 1 at the end. The length of the longest diagonal is intuitively
* the smaller of the width and height of the matrix. The linearly increasing and constant parts of
* the sequence can be described using the following compact equation:
*
* Length => min(Longest Length, index + 1)
*
* While the constant and deceasing end parts of the sequence can be described using the following
* compact equation:
*
* Length => min(Longest Length, Number Of Diagonals - index)
*
* All three parts of the sequence can then be combined using the minimum operation because they
* all share the same maximum value, that is, the longest length:
*
* Length => min(Longest Length, index + 1, Number Of Diagonals - index)
*
*/
int compute_diagonal_length(ivec2 size, int diagonal_index)
{
int length_of_longest_diagonal = min(size.x, size.y);
int start_sequence = diagonal_index + 1;
int end_sequence = compute_number_of_diagonals(size) - diagonal_index;
return min(length_of_longest_diagonal, min(start_sequence, end_sequence));
}
/* Computes the position of the start of the diagonal of the given index in the matrix with the
* given size, where the diagonals are indexed from the upper left corner to the lower right corner
* such that their start is at the left and bottom edges of the matrix as shown in the diagram
* below. The numbers in the diagram denote the index of the diagonal and the position of its
* start.
*
* Width = 6
* +-----+-----+-----+-----+-----+-----+
* (0, 2) | 0 | 1 | 2 | 3 | 4 | 5 |
* +-----+-----+-----+-----+-----+-----+
* (0, 1) | 1 | 2 | 3 | 4 | 5 | 6 | Height = 3
* +-----+-----+-----+-----+-----+-----+
* | 2 | 3 | 4 | 5 | 6 | 7 |
* +-----+-----+-----+-----+-----+-----+
* (0, 0) (1,0) (2,0) (3,0) (4,0) (5,0)
*
* To derive the start position from the index, we consider each axis separately. For the X
* position, indices up to (height - 1) have zero x positions, while other indices linearly
* increase from (height) to the end. Which can be described using the compact equation:
*
* X => max(0, index - (height - 1))
*
* For the Y position, indices up to (height - 1) linearly decrease from (height - 1) to zero,
* while other indices are zero. Which can be described using the compact equation:
*
* Y => max(0, (height - 1) - index)
*
*/
ivec2 compute_diagonal_start(ivec2 size, int index)
{
return ivec2(max(0, index - (size.y - 1)), max(0, (size.y - 1) - index));
}
/* Computes a direction vector such that when added to the position of a value in a matrix will
* yield the position of the next value in the same diagonal. According to the choice of the start
* of the diagonal in compute_diagonal_start, this is (1, 1). */
ivec2 get_diagonal_direction()
{
return ivec2(1);
}
/* Computes the number of values in the anti diagonal of the given index in the matrix with the
* given size, where the anti diagonals are indexed from the lower left corner to the upper right
* corner such that that their start is at the bottom and right edges of the matrix as shown in the
* diagram below. The numbers in the diagram denote the index of the anti diagonal and its length.
*
* Width = 6
* +---+---+---+---+---+---+
* | 2 | 3 | 4 | 5 | 6 | 7 | 1
* +---+---+---+---+---+---+
* Height = 3 | 1 | 2 | 3 | 4 | 5 | 6 | 2
* +---+---+---+---+---+---+
* | 0 | 1 | 2 | 3 | 4 | 5 |
* +---+---+---+---+---+---+
* 1 2 3 3 3 3
*
* The length of the anti diagonal is identical to the length of the diagonal of the same index, as
* can be seen by comparing the above diagram with the one in the compute_diagonal_length function,
* since the anti diagonals are merely flipped diagonals. */
int compute_anti_diagonal_length(ivec2 size, int diagonal_index)
{
return compute_diagonal_length(size, diagonal_index);
}
/* Computes the position of the start of the anti diagonal of the given index in the matrix with
* the given size, where the anti diagonals are indexed from the lower left corner to the upper
* right corner such that their start is at the bottom and right edges of the matrix as shown in
* the diagram below. The numbers in the diagram denote the index of the anti diagonal and the
* position of its start.
*
* Width = 6
* +-----+-----+-----+-----+-----+-----+
* | 2 | 3 | 4 | 5 | 6 | 7 | (5,2)
* +-----+-----+-----+-----+-----+-----+
* Height = 3 | 1 | 2 | 3 | 4 | 5 | 6 | (5,1)
* +-----+-----+-----+-----+-----+-----+
* | 0 | 1 | 2 | 3 | 4 | 5 |
* +-----+-----+-----+-----+-----+-----+
* (0,0) (1,0) (2,0) (3,0) (4,0) (5,0)
*
* To derive the start position from the index, we consider each axis separately. For the X
* position, indices up to (width - 1) linearly increase from zero, while other indices are all
* (width - 1). Which can be described using the compact equation:
*
* X => min((width - 1), index)
*
* For the Y position, indices up to (width - 1) are zero, while other indices linearly increase
* from zero to (height - 1). Which can be described using the compact equation:
*
* Y => max(0, index - (width - 1))
*
*/
ivec2 compute_anti_diagonal_start(ivec2 size, int index)
{
return ivec2(min(size.x - 1, index), max(0, index - (size.x - 1)));
}
/* Computes a direction vector such that when added to the position of a value in a matrix will
* yield the position of the next value in the same anti diagonal. According to the choice of the
* start of the anti diagonal in compute_anti_diagonal_start, this is (-1, 1). */
ivec2 get_anti_diagonal_direction()
{
return ivec2(-1, 1);
}

View File

@ -9,6 +9,7 @@
#include "BLI_assert.h"
#include "BLI_index_range.hh"
#include "BLI_math_base.hh"
#include "BLI_math_vec_types.hh"
#include "DNA_scene_types.h"
@ -127,10 +128,10 @@ class GlareOperation : public NodeOperation {
return true;
}
/* Only the ghost operation is currently supported. */
/* Only the ghost and simple star operations are currently supported. */
switch (node_storage(bnode()).type) {
case CMP_NODE_GLARE_SIMPLE_STAR:
return true;
return false;
case CMP_NODE_GLARE_FOG_GLOW:
return true;
case CMP_NODE_GLARE_STREAKS:
@ -198,11 +199,139 @@ class GlareOperation : public NodeOperation {
* Simple Star Glare.
* ------------------ */
/* Not yet implemented. Unreachable code due to the is_identity method. */
Result execute_simple_star(Result &highlights_result)
{
BLI_assert_unreachable();
return Result(ResultType::Color, texture_pool());
if (node_storage(bnode()).star_45) {
return execute_simple_star_diagonal(highlights_result);
}
else {
return execute_simple_star_axis_aligned(highlights_result);
}
}
Result execute_simple_star_axis_aligned(Result &highlights_result)
{
Result horizontal_pass_result = execute_simple_star_horizontal_pass(highlights_result);
/* The vertical pass is applied in-plane, but the highlights result is no longer needed,
* so just use it as the pass result. */
Result &vertical_pass_result = highlights_result;
GPUShader *shader = shader_manager().get("compositor_glare_simple_star_vertical_pass");
GPU_shader_bind(shader);
GPU_shader_uniform_1i(shader, "iterations", get_number_of_iterations());
GPU_shader_uniform_1f(shader, "fade_factor", node_storage(bnode()).fade);
horizontal_pass_result.bind_as_texture(shader, "horizontal_tx");
vertical_pass_result.bind_as_image(shader, "vertical_img");
/* Dispatch a thread for each column in the image. */
const int width = get_glare_size().x;
compute_dispatch_threads_at_least(shader, int2(width, 1));
horizontal_pass_result.unbind_as_texture();
vertical_pass_result.unbind_as_image();
GPU_shader_unbind();
horizontal_pass_result.release();
return vertical_pass_result;
}
Result execute_simple_star_horizontal_pass(Result &highlights_result)
{
/* The horizontal pass is applied in-plane, so copy the highlights to a new image since the
* highlights result is still needed by the vertical pass. */
const int2 glare_size = get_glare_size();
Result horizontal_pass_result = Result::Temporary(ResultType::Color, texture_pool());
horizontal_pass_result.allocate_texture(glare_size);
GPU_memory_barrier(GPU_BARRIER_TEXTURE_UPDATE);
GPU_texture_copy(horizontal_pass_result.texture(), highlights_result.texture());
GPUShader *shader = shader_manager().get("compositor_glare_simple_star_horizontal_pass");
GPU_shader_bind(shader);
GPU_shader_uniform_1i(shader, "iterations", get_number_of_iterations());
GPU_shader_uniform_1f(shader, "fade_factor", node_storage(bnode()).fade);
horizontal_pass_result.bind_as_image(shader, "horizontal_img");
/* Dispatch a thread for each row in the image. */
compute_dispatch_threads_at_least(shader, int2(glare_size.y, 1));
horizontal_pass_result.unbind_as_image();
GPU_shader_unbind();
return horizontal_pass_result;
}
Result execute_simple_star_diagonal(Result &highlights_result)
{
Result diagonal_pass_result = execute_simple_star_diagonal_pass(highlights_result);
/* The anti-diagonal pass is applied in-plane, but the highlights result is no longer needed,
* so just use it as the pass result. */
Result &anti_diagonal_pass_result = highlights_result;
GPUShader *shader = shader_manager().get("compositor_glare_simple_star_anti_diagonal_pass");
GPU_shader_bind(shader);
GPU_shader_uniform_1i(shader, "iterations", get_number_of_iterations());
GPU_shader_uniform_1f(shader, "fade_factor", node_storage(bnode()).fade);
diagonal_pass_result.bind_as_texture(shader, "diagonal_tx");
anti_diagonal_pass_result.bind_as_image(shader, "anti_diagonal_img");
/* Dispatch a thread for each diagonal in the image. */
compute_dispatch_threads_at_least(shader, int2(compute_simple_star_diagonals_count(), 1));
diagonal_pass_result.unbind_as_texture();
anti_diagonal_pass_result.unbind_as_image();
GPU_shader_unbind();
diagonal_pass_result.release();
return anti_diagonal_pass_result;
}
Result execute_simple_star_diagonal_pass(Result &highlights_result)
{
/* The diagonal pass is applied in-plane, so copy the highlights to a new image since the
* highlights result is still needed by the anti-diagonal pass. */
const int2 glare_size = get_glare_size();
Result diagonal_pass_result = Result::Temporary(ResultType::Color, texture_pool());
diagonal_pass_result.allocate_texture(glare_size);
GPU_memory_barrier(GPU_BARRIER_TEXTURE_UPDATE);
GPU_texture_copy(diagonal_pass_result.texture(), highlights_result.texture());
GPUShader *shader = shader_manager().get("compositor_glare_simple_star_diagonal_pass");
GPU_shader_bind(shader);
GPU_shader_uniform_1i(shader, "iterations", get_number_of_iterations());
GPU_shader_uniform_1f(shader, "fade_factor", node_storage(bnode()).fade);
diagonal_pass_result.bind_as_image(shader, "diagonal_img");
/* Dispatch a thread for each diagonal in the image. */
compute_dispatch_threads_at_least(shader, int2(compute_simple_star_diagonals_count(), 1));
diagonal_pass_result.unbind_as_image();
GPU_shader_unbind();
return diagonal_pass_result;
}
/* The Star 45 option of the Simple Star mode of glare is applied on the diagonals of the image.
* This method computes the number of diagonals in the glare image. For more information on the
* used equation, see the compute_number_of_diagonals function in the following shader library
* file: gpu_shader_compositor_image_diagonals.glsl */
int compute_simple_star_diagonals_count()
{
const int2 size = get_glare_size();
return size.x + size.y - 1;
}
/* ---------------