diff --git a/source/blender/compositor/realtime_compositor/CMakeLists.txt b/source/blender/compositor/realtime_compositor/CMakeLists.txt index 7d7448a448f..0881982c6d3 100644 --- a/source/blender/compositor/realtime_compositor/CMakeLists.txt +++ b/source/blender/compositor/realtime_compositor/CMakeLists.txt @@ -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 diff --git a/source/blender/compositor/realtime_compositor/shaders/compositor_glare_simple_star_anti_diagonal_pass.glsl b/source/blender/compositor/realtime_compositor/shaders/compositor_glare_simple_star_anti_diagonal_pass.glsl new file mode 100644 index 00000000000..7cfec6c4d95 --- /dev/null +++ b/source/blender/compositor/realtime_compositor/shaders/compositor_glare_simple_star_anti_diagonal_pass.glsl @@ -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); + } +} diff --git a/source/blender/compositor/realtime_compositor/shaders/compositor_glare_simple_star_diagonal_pass.glsl b/source/blender/compositor/realtime_compositor/shaders/compositor_glare_simple_star_diagonal_pass.glsl new file mode 100644 index 00000000000..c8a703ae67c --- /dev/null +++ b/source/blender/compositor/realtime_compositor/shaders/compositor_glare_simple_star_diagonal_pass.glsl @@ -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); + } + } +} diff --git a/source/blender/compositor/realtime_compositor/shaders/compositor_glare_simple_star_horizontal_pass.glsl b/source/blender/compositor/realtime_compositor/shaders/compositor_glare_simple_star_horizontal_pass.glsl new file mode 100644 index 00000000000..94d935a4c1d --- /dev/null +++ b/source/blender/compositor/realtime_compositor/shaders/compositor_glare_simple_star_horizontal_pass.glsl @@ -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); + } + } +} diff --git a/source/blender/compositor/realtime_compositor/shaders/compositor_glare_simple_star_vertical_pass.glsl b/source/blender/compositor/realtime_compositor/shaders/compositor_glare_simple_star_vertical_pass.glsl new file mode 100644 index 00000000000..abc92883c17 --- /dev/null +++ b/source/blender/compositor/realtime_compositor/shaders/compositor_glare_simple_star_vertical_pass.glsl @@ -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); + } +} diff --git a/source/blender/compositor/realtime_compositor/shaders/infos/compositor_glare_info.hh b/source/blender/compositor/realtime_compositor/shaders/infos/compositor_glare_info.hh index 064e6ddddb7..0e078373dfe 100644 --- a/source/blender/compositor/realtime_compositor/shaders/infos/compositor_glare_info.hh +++ b/source/blender/compositor/realtime_compositor/shaders/infos/compositor_glare_info.hh @@ -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); diff --git a/source/blender/compositor/realtime_compositor/shaders/library/gpu_shader_compositor_image_diagonals.glsl b/source/blender/compositor/realtime_compositor/shaders/library/gpu_shader_compositor_image_diagonals.glsl new file mode 100644 index 00000000000..dbeb8721dbe --- /dev/null +++ b/source/blender/compositor/realtime_compositor/shaders/library/gpu_shader_compositor_image_diagonals.glsl @@ -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); +} diff --git a/source/blender/nodes/composite/nodes/node_composite_glare.cc b/source/blender/nodes/composite/nodes/node_composite_glare.cc index 78991b93b00..51772222bcf 100644 --- a/source/blender/nodes/composite/nodes/node_composite_glare.cc +++ b/source/blender/nodes/composite/nodes/node_composite_glare.cc @@ -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; } /* ---------------