Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'source/blender/gpu/shaders')
-rw-r--r--source/blender/gpu/shaders/compositor/compositor_blur.glsl21
-rw-r--r--source/blender/gpu/shaders/compositor/compositor_blur_variable_size.glsl71
-rw-r--r--source/blender/gpu/shaders/compositor/compositor_normalize.glsl10
-rw-r--r--source/blender/gpu/shaders/compositor/compositor_parallel_reduction.glsl98
-rw-r--r--source/blender/gpu/shaders/compositor/compositor_tone_map_photoreceptor.glsl22
-rw-r--r--source/blender/gpu/shaders/compositor/compositor_tone_map_simple.glsl26
-rw-r--r--source/blender/gpu/shaders/compositor/infos/compositor_blur_variable_size_info.hh15
-rw-r--r--source/blender/gpu/shaders/compositor/infos/compositor_normalize_info.hh12
-rw-r--r--source/blender/gpu/shaders/compositor/infos/compositor_parallel_reduction_info.hh149
-rw-r--r--source/blender/gpu/shaders/compositor/infos/compositor_tone_map_photoreceptor_info.hh16
-rw-r--r--source/blender/gpu/shaders/compositor/infos/compositor_tone_map_simple_info.hh13
-rw-r--r--source/blender/gpu/shaders/gpu_shader_icon_frag.glsl42
-rw-r--r--source/blender/gpu/shaders/gpu_shader_icon_vert.glsl37
-rw-r--r--source/blender/gpu/shaders/infos/gpu_interface_info.hh3
-rw-r--r--source/blender/gpu/shaders/infos/gpu_shader_icon_info.hh22
15 files changed, 552 insertions, 5 deletions
diff --git a/source/blender/gpu/shaders/compositor/compositor_blur.glsl b/source/blender/gpu/shaders/compositor/compositor_blur.glsl
index 4f981c84f59..c7ac620f99b 100644
--- a/source/blender/gpu/shaders/compositor/compositor_blur.glsl
+++ b/source/blender/gpu/shaders/compositor/compositor_blur.glsl
@@ -18,13 +18,24 @@ vec4 load_input(ivec2 texel)
}
/* Given the texel in the range [-radius, radius] in both axis, load the appropriate weight from
- * the weights texture, where the texel (0, 0) is considered the center of weights texture. */
+ * the weights texture, where the given texel (0, 0) corresponds the center of weights texture.
+ * Note that we load the weights texture inverted along both directions to maintain the shape of
+ * the weights if it was not symmetrical. To understand why inversion makes sense, consider a 1D
+ * weights texture whose right half is all ones and whose left half is all zeros. Further, consider
+ * that we are blurring a single white pixel on a black background. When computing the value of a
+ * pixel that is to the right of the white pixel, the white pixel will be in the left region of the
+ * search window, and consequently, without inversion, a zero will be sampled from the left side of
+ * the weights texture and result will be zero. However, what we expect is that pixels to the right
+ * of the white pixel will be white, that is, they should sample a weight of 1 from the right side
+ * of the weights texture, hence the need for inversion. */
vec4 load_weight(ivec2 texel)
{
- /* Add the radius to transform the texel into the range [0, radius * 2], then divide by the upper
- * bound plus one to transform the texel into the normalized range [0, 1] needed to sample the
- * weights sampler. Finally, also add 0.5 to sample at the center of the pixels. */
- return texture(weights_tx, (texel + vec2(radius + 0.5)) / (radius * 2 + 1));
+ /* Add the radius to transform the texel into the range [0, radius * 2], with an additional 0.5
+ * to sample at the center of the pixels, then divide by the upper bound plus one to transform
+ * the texel into the normalized range [0, 1] needed to sample the weights sampler. Finally,
+ * invert the textures coordinates by subtracting from 1 to maintain the shape of the weights as
+ * mentioned in the function description. */
+ return texture(weights_tx, 1.0 - ((texel + vec2(radius + 0.5)) / (radius * 2 + 1)));
}
void main()
diff --git a/source/blender/gpu/shaders/compositor/compositor_blur_variable_size.glsl b/source/blender/gpu/shaders/compositor/compositor_blur_variable_size.glsl
new file mode 100644
index 00000000000..9383bbf9825
--- /dev/null
+++ b/source/blender/gpu/shaders/compositor/compositor_blur_variable_size.glsl
@@ -0,0 +1,71 @@
+#pragma BLENDER_REQUIRE(gpu_shader_common_math_utils.glsl)
+#pragma BLENDER_REQUIRE(gpu_shader_compositor_texture_utilities.glsl)
+
+/* Given the texel in the range [-radius, radius] in both axis, load the appropriate weight from
+ * the weights texture, where the given texel (0, 0) corresponds the center of weights texture.
+ * Note that we load the weights texture inverted along both directions to maintain the shape of
+ * the weights if it was not symmetrical. To understand why inversion makes sense, consider a 1D
+ * weights texture whose right half is all ones and whose left half is all zeros. Further, consider
+ * that we are blurring a single white pixel on a black background. When computing the value of a
+ * pixel that is to the right of the white pixel, the white pixel will be in the left region of the
+ * search window, and consequently, without inversion, a zero will be sampled from the left side of
+ * the weights texture and result will be zero. However, what we expect is that pixels to the right
+ * of the white pixel will be white, that is, they should sample a weight of 1 from the right side
+ * of the weights texture, hence the need for inversion. */
+vec4 load_weight(ivec2 texel, float radius)
+{
+ /* The center zero texel is always assigned a unit weight regardless of the corresponding weight
+ * in the weights texture. That's to guarantee that at last the center pixel will be accumulated
+ * even if the weights texture is zero at its center. */
+ if (texel == ivec2(0)) {
+ return vec4(1.0);
+ }
+
+ /* Add the radius to transform the texel into the range [0, radius * 2], with an additional 0.5
+ * to sample at the center of the pixels, then divide by the upper bound plus one to transform
+ * the texel into the normalized range [0, 1] needed to sample the weights sampler. Finally,
+ * invert the textures coordinates by subtracting from 1 to maintain the shape of the weights as
+ * mentioned in the function description. */
+ return texture(weights_tx, 1.0 - ((texel + vec2(radius + 0.5)) / (radius * 2 + 1)));
+}
+
+void main()
+{
+ ivec2 texel = ivec2(gl_GlobalInvocationID.xy);
+
+ /* The mask input is treated as a boolean. If it is zero, then no blurring happens for this
+ * pixel. Otherwise, the pixel is blurred normally and the mask value is irrelevant. */
+ float mask = texture_load(mask_tx, texel).x;
+ if (mask == 0.0) {
+ imageStore(output_img, texel, texture_load(input_tx, texel));
+ return;
+ }
+
+ float center_size = texture_load(size_tx, texel).x * base_size;
+
+ /* Go over the window of the given search radius and accumulate the colors multiplied by their
+ * respective weights as well as the weights themselves, but only if both the size of the center
+ * pixel and the size of the candidate pixel are less than both the x and y distances of the
+ * candidate pixel. */
+ vec4 accumulated_color = vec4(0.0);
+ vec4 accumulated_weight = vec4(0.0);
+ for (int y = -search_radius; y <= search_radius; y++) {
+ for (int x = -search_radius; x <= search_radius; x++) {
+ float candidate_size = texture_load(size_tx, texel + ivec2(x, y)).x * base_size;
+
+ /* Skip accumulation if either the x or y distances of the candidate pixel are larger than
+ * either the center or candidate pixel size. Note that the max and min functions here denote
+ * "either" in the aforementioned description. */
+ float size = min(center_size, candidate_size);
+ if (max(abs(x), abs(y)) > size) {
+ continue;
+ }
+
+ vec4 weight = load_weight(ivec2(x, y), size);
+ accumulated_color += texture_load(input_tx, texel + ivec2(x, y)) * weight;
+ accumulated_weight += weight;
+ }
+ }
+
+ imageStore(output_img, texel, safe_divide(accumulated_color, accumulated_weight));
+}
diff --git a/source/blender/gpu/shaders/compositor/compositor_normalize.glsl b/source/blender/gpu/shaders/compositor/compositor_normalize.glsl
new file mode 100644
index 00000000000..53dfeb01730
--- /dev/null
+++ b/source/blender/gpu/shaders/compositor/compositor_normalize.glsl
@@ -0,0 +1,10 @@
+#pragma BLENDER_REQUIRE(gpu_shader_compositor_texture_utilities.glsl)
+
+void main()
+{
+ ivec2 texel = ivec2(gl_GlobalInvocationID.xy);
+ float value = texture_load(input_tx, texel).x;
+ float normalized_value = (value - minimum) * scale;
+ float clamped_value = clamp(normalized_value, 0.0, 1.0);
+ imageStore(output_img, texel, vec4(clamped_value));
+}
diff --git a/source/blender/gpu/shaders/compositor/compositor_parallel_reduction.glsl b/source/blender/gpu/shaders/compositor/compositor_parallel_reduction.glsl
new file mode 100644
index 00000000000..f6f84aa24c1
--- /dev/null
+++ b/source/blender/gpu/shaders/compositor/compositor_parallel_reduction.glsl
@@ -0,0 +1,98 @@
+#pragma BLENDER_REQUIRE(gpu_shader_compositor_texture_utilities.glsl)
+
+/* This shader reduces the given texture into a smaller texture of a size equal to the number of
+ * work groups. In particular, each work group reduces its contents into a single value and writes
+ * that value to a single pixel in the output image. The shader can be dispatched multiple times to
+ * eventually reduce the image into a single pixel.
+ *
+ * The shader works by loading the whole data of each work group into a linear array, then it
+ * reduces the second half of the array onto the first half of the array, then it reduces the
+ * second quarter of the array onto the first quarter or the array, and so on until only one
+ * element remains. The following figure illustrates the process for sum reduction on 8 elements.
+ *
+ * .---. .---. .---. .---. .---. .---. .---. .---.
+ * | 0 | | 1 | | 2 | | 3 | | 4 | | 5 | | 6 | | 7 | Original data.
+ * '---' '---' '---' '---' '---' '---' '---' '---'
+ * |.____|_____|_____|_____| | | |
+ * || |.____|_____|___________| | |
+ * || || |.____|_________________| |
+ * || || || |.______________________| <--First reduction. Stride = 4.
+ * || || || ||
+ * .---. .---. .---. .----.
+ * | 4 | | 6 | | 8 | | 10 | <--Data after first reduction.
+ * '---' '---' '---' '----'
+ * |.____|_____| |
+ * || |.__________| <--Second reduction. Stride = 2.
+ * || ||
+ * .----. .----.
+ * | 12 | | 16 | <--Data after second reduction.
+ * '----' '----'
+ * |.____|
+ * || <--Third reduction. Stride = 1.
+ * .----.
+ * | 28 |
+ * '----' <--Data after third reduction.
+ *
+ *
+ * The shader is generic enough to implement many types of reductions. This is done by using macros
+ * that the developer should define to implement a certain reduction operation. Those include,
+ * TYPE, IDENTITY, INITIALIZE, LOAD, and REDUCE. See the implementation below for more information
+ * as well as the compositor_parallel_reduction_info.hh for example reductions operations. */
+
+/* Doing the reduction in shared memory is faster, so create a shared array where the whole data
+ * of the work group will be loaded and reduced. The 2D structure of the work group is irrelevant
+ * for reduction, so we just load the data in a 1D array to simplify reduction. The developer is
+ * expected to define the TYPE macro to be a float or a vec4, depending on the type of data being
+ * reduced. */
+const uint reduction_size = gl_WorkGroupSize.x * gl_WorkGroupSize.y;
+shared TYPE reduction_data[reduction_size];
+
+void main()
+{
+ /* Load the data from the texture, while returning IDENTITY for out of bound coordinates. The
+ * developer is expected to define the IDENTITY macro to be a vec4 that does not affect the
+ * output of the reduction. For instance, sum reductions have an identity of vec4(0.0), while
+ * max value reductions have an identity of vec4(FLT_MIN). */
+ vec4 value = texture_load(input_tx, ivec2(gl_GlobalInvocationID.xy), IDENTITY);
+
+ /* Initialize the shared array given the previously loaded value. This step can be different
+ * depending on whether this is the initial reduction pass or a latter one. Indeed, the input
+ * texture for the initial reduction is the source texture itself, while the input texture to a
+ * latter reduction pass is an intermediate texture after one or more reductions have happened.
+ * This is significant because the data being reduced might be computed from the original data
+ * and different from it, for instance, when summing the luminance of an image, the original data
+ * is a vec4 color, while the reduced data is a float luminance value. So for the initial
+ * reduction pass, the luminance will be computed from the color, reduced, then stored into an
+ * intermediate float texture. On the other hand, for latter reduction passes, the luminance will
+ * be loaded directly and reduced without extra processing. So the developer is expected to
+ * define the INITIALIZE and LOAD macros to be expressions that derive the needed value from the
+ * loaded value for the initial reduction pass and latter ones respectively. */
+ reduction_data[gl_LocalInvocationIndex] = is_initial_reduction ? INITIALIZE(value) : LOAD(value);
+
+ /* Reduce the reduction data by half on every iteration until only one element remains. See the
+ * above figure for an intuitive understanding of the stride value. */
+ for (uint stride = reduction_size / 2; stride > 0; stride /= 2) {
+ barrier();
+
+ /* Only the threads up to the current stride should be active as can be seen in the diagram
+ * above. */
+ if (gl_LocalInvocationIndex >= stride) {
+ continue;
+ }
+
+ /* Reduce each two elements that are stride apart, writing the result to the element with the
+ * lower index, as can be seen in the diagram above. The developer is expected to define the
+ * REDUCE macro to be a commutative and associative binary operator suitable for parallel
+ * reduction. */
+ reduction_data[gl_LocalInvocationIndex] = REDUCE(
+ reduction_data[gl_LocalInvocationIndex], reduction_data[gl_LocalInvocationIndex + stride]);
+ }
+
+ /* Finally, the result of the reduction is available as the first element in the reduction data,
+ * write it to the pixel corresponding to the work group, making sure only the one thread writes
+ * it. */
+ barrier();
+ if (gl_LocalInvocationIndex == 0) {
+ imageStore(output_img, ivec2(gl_WorkGroupID.xy), vec4(reduction_data[0]));
+ }
+}
diff --git a/source/blender/gpu/shaders/compositor/compositor_tone_map_photoreceptor.glsl b/source/blender/gpu/shaders/compositor/compositor_tone_map_photoreceptor.glsl
new file mode 100644
index 00000000000..167006585ca
--- /dev/null
+++ b/source/blender/gpu/shaders/compositor/compositor_tone_map_photoreceptor.glsl
@@ -0,0 +1,22 @@
+#pragma BLENDER_REQUIRE(gpu_shader_compositor_texture_utilities.glsl)
+
+/* Tone mapping based on equation (1) and the trilinear interpolation between equations (6) and (7)
+ * from Reinhard, Erik, and Kate Devlin. "Dynamic range reduction inspired by photoreceptor
+ * physiology." IEEE transactions on visualization and computer graphics 11.1 (2005): 13-24. */
+void main()
+{
+ ivec2 texel = ivec2(gl_GlobalInvocationID.xy);
+
+ vec4 input_color = texture_load(input_tx, texel);
+ float input_luminance = dot(input_color.rgb, luminance_coefficients);
+
+ /* Trilinear interpolation between equations (6) and (7) from Reinhard's 2005 paper. */
+ vec4 local_adaptation_level = mix(vec4(input_luminance), input_color, chromatic_adaptation);
+ vec4 adaptation_level = mix(global_adaptation_level, local_adaptation_level, light_adaptation);
+
+ /* Equation (1) from Reinhard's 2005 paper, assuming Vmax is 1. */
+ vec4 semi_saturation = pow(intensity * adaptation_level, vec4(contrast));
+ vec4 tone_mapped_color = input_color / (input_color + semi_saturation);
+
+ imageStore(output_img, texel, vec4(tone_mapped_color.rgb, input_color.a));
+}
diff --git a/source/blender/gpu/shaders/compositor/compositor_tone_map_simple.glsl b/source/blender/gpu/shaders/compositor/compositor_tone_map_simple.glsl
new file mode 100644
index 00000000000..ce42d021dd1
--- /dev/null
+++ b/source/blender/gpu/shaders/compositor/compositor_tone_map_simple.glsl
@@ -0,0 +1,26 @@
+#pragma BLENDER_REQUIRE(gpu_shader_compositor_texture_utilities.glsl)
+#pragma BLENDER_REQUIRE(gpu_shader_common_math_utils.glsl)
+
+/* Tone mapping based on equation (3) from Reinhard, Erik, et al. "Photographic tone reproduction
+ * for digital images." Proceedings of the 29th annual conference on Computer graphics and
+ * interactive techniques. 2002. */
+void main()
+{
+ ivec2 texel = ivec2(gl_GlobalInvocationID.xy);
+
+ vec4 input_color = texture_load(input_tx, texel);
+
+ /* Equation (2) from Reinhard's 2002 paper. */
+ vec4 scaled_color = input_color * luminance_scale;
+
+ /* Equation (3) from Reinhard's 2002 paper, but with the 1 replaced with the blend factor for
+ * more flexibility. See ToneMapOperation::compute_luminance_scale_blend_factor. */
+ vec4 denominator = luminance_scale_blend_factor + scaled_color;
+ vec4 tone_mapped_color = safe_divide(scaled_color, denominator);
+
+ if (inverse_gamma != 0.0) {
+ tone_mapped_color = pow(max(tone_mapped_color, vec4(0.0)), vec4(inverse_gamma));
+ }
+
+ imageStore(output_img, texel, vec4(tone_mapped_color.rgb, input_color.a));
+}
diff --git a/source/blender/gpu/shaders/compositor/infos/compositor_blur_variable_size_info.hh b/source/blender/gpu/shaders/compositor/infos/compositor_blur_variable_size_info.hh
new file mode 100644
index 00000000000..05b6385fd1e
--- /dev/null
+++ b/source/blender/gpu/shaders/compositor/infos/compositor_blur_variable_size_info.hh
@@ -0,0 +1,15 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#include "gpu_shader_create_info.hh"
+
+GPU_SHADER_CREATE_INFO(compositor_blur_variable_size)
+ .local_group_size(16, 16)
+ .push_constant(Type::FLOAT, "base_size")
+ .push_constant(Type::INT, "search_radius")
+ .sampler(0, ImageType::FLOAT_2D, "input_tx")
+ .sampler(1, ImageType::FLOAT_2D, "weights_tx")
+ .sampler(2, ImageType::FLOAT_2D, "size_tx")
+ .sampler(3, ImageType::FLOAT_2D, "mask_tx")
+ .image(0, GPU_RGBA16F, Qualifier::WRITE, ImageType::FLOAT_2D, "output_img")
+ .compute_source("compositor_blur_variable_size.glsl")
+ .do_static_compilation(true);
diff --git a/source/blender/gpu/shaders/compositor/infos/compositor_normalize_info.hh b/source/blender/gpu/shaders/compositor/infos/compositor_normalize_info.hh
new file mode 100644
index 00000000000..02fdc424014
--- /dev/null
+++ b/source/blender/gpu/shaders/compositor/infos/compositor_normalize_info.hh
@@ -0,0 +1,12 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#include "gpu_shader_create_info.hh"
+
+GPU_SHADER_CREATE_INFO(compositor_normalize)
+ .local_group_size(16, 16)
+ .push_constant(Type::FLOAT, "minimum")
+ .push_constant(Type::FLOAT, "scale")
+ .sampler(0, ImageType::FLOAT_2D, "input_tx")
+ .image(0, GPU_R16F, Qualifier::WRITE, ImageType::FLOAT_2D, "output_img")
+ .compute_source("compositor_normalize.glsl")
+ .do_static_compilation(true);
diff --git a/source/blender/gpu/shaders/compositor/infos/compositor_parallel_reduction_info.hh b/source/blender/gpu/shaders/compositor/infos/compositor_parallel_reduction_info.hh
new file mode 100644
index 00000000000..e2252b14758
--- /dev/null
+++ b/source/blender/gpu/shaders/compositor/infos/compositor_parallel_reduction_info.hh
@@ -0,0 +1,149 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#include "gpu_shader_create_info.hh"
+
+GPU_SHADER_CREATE_INFO(compositor_parallel_reduction_shared)
+ .local_group_size(16, 16)
+ .push_constant(Type::BOOL, "is_initial_reduction")
+ .sampler(0, ImageType::FLOAT_2D, "input_tx")
+ .compute_source("compositor_parallel_reduction.glsl");
+
+/* --------------------------------------------------------------------
+ * Sum Reductions.
+ */
+
+GPU_SHADER_CREATE_INFO(compositor_sum_shared)
+ .additional_info("compositor_parallel_reduction_shared")
+ .define("IDENTITY", "vec4(0.0)")
+ .define("REDUCE(lhs, rhs)", "lhs + rhs");
+
+GPU_SHADER_CREATE_INFO(compositor_sum_float_shared)
+ .additional_info("compositor_sum_shared")
+ .image(0, GPU_R32F, Qualifier::WRITE, ImageType::FLOAT_2D, "output_img")
+ .define("TYPE", "float")
+ .define("LOAD(value)", "value.x");
+
+GPU_SHADER_CREATE_INFO(compositor_sum_red)
+ .additional_info("compositor_sum_float_shared")
+ .define("INITIALIZE(value)", "value.r")
+ .do_static_compilation(true);
+
+GPU_SHADER_CREATE_INFO(compositor_sum_green)
+ .additional_info("compositor_sum_float_shared")
+ .define("INITIALIZE(value)", "value.g")
+ .do_static_compilation(true);
+
+GPU_SHADER_CREATE_INFO(compositor_sum_blue)
+ .additional_info("compositor_sum_float_shared")
+ .define("INITIALIZE(value)", "value.b")
+ .do_static_compilation(true);
+
+GPU_SHADER_CREATE_INFO(compositor_sum_luminance)
+ .additional_info("compositor_sum_float_shared")
+ .push_constant(Type::VEC3, "luminance_coefficients")
+ .define("INITIALIZE(value)", "dot(value.rgb, luminance_coefficients)")
+ .do_static_compilation(true);
+
+GPU_SHADER_CREATE_INFO(compositor_sum_log_luminance)
+ .additional_info("compositor_sum_float_shared")
+ .push_constant(Type::VEC3, "luminance_coefficients")
+ .define("INITIALIZE(value)", "log(max(dot(value.rgb, luminance_coefficients), 1e-5))")
+ .do_static_compilation(true);
+
+GPU_SHADER_CREATE_INFO(compositor_sum_color)
+ .additional_info("compositor_sum_shared")
+ .image(0, GPU_RGBA32F, Qualifier::WRITE, ImageType::FLOAT_2D, "output_img")
+ .define("TYPE", "vec4")
+ .define("INITIALIZE(value)", "value")
+ .define("LOAD(value)", "value")
+ .do_static_compilation(true);
+
+/* --------------------------------------------------------------------
+ * Sum Of Squared Difference Reductions.
+ */
+
+GPU_SHADER_CREATE_INFO(compositor_sum_squared_difference_float_shared)
+ .additional_info("compositor_parallel_reduction_shared")
+ .image(0, GPU_R32F, Qualifier::WRITE, ImageType::FLOAT_2D, "output_img")
+ .push_constant(Type::FLOAT, "subtrahend")
+ .define("TYPE", "float")
+ .define("IDENTITY", "vec4(subtrahend)")
+ .define("LOAD(value)", "value.x")
+ .define("REDUCE(lhs, rhs)", "lhs + rhs");
+
+GPU_SHADER_CREATE_INFO(compositor_sum_red_squared_difference)
+ .additional_info("compositor_sum_squared_difference_float_shared")
+ .define("INITIALIZE(value)", "pow(value.r - subtrahend, 2.0)")
+ .do_static_compilation(true);
+
+GPU_SHADER_CREATE_INFO(compositor_sum_green_squared_difference)
+ .additional_info("compositor_sum_squared_difference_float_shared")
+ .define("INITIALIZE(value)", "pow(value.g - subtrahend, 2.0)")
+ .do_static_compilation(true);
+
+GPU_SHADER_CREATE_INFO(compositor_sum_blue_squared_difference)
+ .additional_info("compositor_sum_squared_difference_float_shared")
+ .define("INITIALIZE(value)", "pow(value.b - subtrahend, 2.0)")
+ .do_static_compilation(true);
+
+GPU_SHADER_CREATE_INFO(compositor_sum_luminance_squared_difference)
+ .additional_info("compositor_sum_squared_difference_float_shared")
+ .push_constant(Type::VEC3, "luminance_coefficients")
+ .define("INITIALIZE(value)", "pow(dot(value.rgb, luminance_coefficients) - subtrahend, 2.0)")
+ .do_static_compilation(true);
+
+/* --------------------------------------------------------------------
+ * Maximum Reductions.
+ */
+
+GPU_SHADER_CREATE_INFO(compositor_maximum_luminance)
+ .additional_info("compositor_parallel_reduction_shared")
+ .typedef_source("common_math_lib.glsl")
+ .image(0, GPU_R32F, Qualifier::WRITE, ImageType::FLOAT_2D, "output_img")
+ .push_constant(Type::VEC3, "luminance_coefficients")
+ .define("TYPE", "float")
+ .define("IDENTITY", "vec4(FLT_MIN)")
+ .define("INITIALIZE(value)", "dot(value.rgb, luminance_coefficients)")
+ .define("LOAD(value)", "value.x")
+ .define("REDUCE(lhs, rhs)", "max(lhs, rhs)")
+ .do_static_compilation(true);
+
+GPU_SHADER_CREATE_INFO(compositor_maximum_float_in_range)
+ .additional_info("compositor_parallel_reduction_shared")
+ .image(0, GPU_R32F, Qualifier::WRITE, ImageType::FLOAT_2D, "output_img")
+ .push_constant(Type::FLOAT, "lower_bound")
+ .push_constant(Type::FLOAT, "upper_bound")
+ .define("TYPE", "float")
+ .define("IDENTITY", "vec4(lower_bound)")
+ .define("INITIALIZE(v)", "((v.x <= upper_bound) && (v.x >= lower_bound)) ? v.x : lower_bound")
+ .define("LOAD(value)", "value.x")
+ .define("REDUCE(lhs, rhs)", "((rhs > lhs) && (rhs <= upper_bound)) ? rhs : lhs")
+ .do_static_compilation(true);
+
+/* --------------------------------------------------------------------
+ * Minimum Reductions.
+ */
+
+GPU_SHADER_CREATE_INFO(compositor_minimum_luminance)
+ .additional_info("compositor_parallel_reduction_shared")
+ .typedef_source("common_math_lib.glsl")
+ .image(0, GPU_R32F, Qualifier::WRITE, ImageType::FLOAT_2D, "output_img")
+ .push_constant(Type::VEC3, "luminance_coefficients")
+ .define("TYPE", "float")
+ .define("IDENTITY", "vec4(FLT_MAX)")
+ .define("INITIALIZE(value)", "dot(value.rgb, luminance_coefficients)")
+ .define("LOAD(value)", "value.x")
+ .define("REDUCE(lhs, rhs)", "min(lhs, rhs)")
+ .do_static_compilation(true);
+
+GPU_SHADER_CREATE_INFO(compositor_minimum_float_in_range)
+ .additional_info("compositor_parallel_reduction_shared")
+ .image(0, GPU_R32F, Qualifier::WRITE, ImageType::FLOAT_2D, "output_img")
+ .push_constant(Type::FLOAT, "lower_bound")
+ .push_constant(Type::FLOAT, "upper_bound")
+ .define("TYPE", "float")
+ .define("IDENTITY", "vec4(upper_bound)")
+ .define("INITIALIZE(v)", "((v.x <= upper_bound) && (v.x >= lower_bound)) ? v.x : upper_bound")
+ .define("LOAD(value)", "value.x")
+ .define("REDUCE(lhs, rhs)", "((rhs < lhs) && (rhs >= lower_bound)) ? rhs : lhs")
+ .do_static_compilation(true);
diff --git a/source/blender/gpu/shaders/compositor/infos/compositor_tone_map_photoreceptor_info.hh b/source/blender/gpu/shaders/compositor/infos/compositor_tone_map_photoreceptor_info.hh
new file mode 100644
index 00000000000..a460c9d58a6
--- /dev/null
+++ b/source/blender/gpu/shaders/compositor/infos/compositor_tone_map_photoreceptor_info.hh
@@ -0,0 +1,16 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#include "gpu_shader_create_info.hh"
+
+GPU_SHADER_CREATE_INFO(compositor_tone_map_photoreceptor)
+ .local_group_size(16, 16)
+ .push_constant(Type::VEC4, "global_adaptation_level")
+ .push_constant(Type::FLOAT, "contrast")
+ .push_constant(Type::FLOAT, "intensity")
+ .push_constant(Type::FLOAT, "chromatic_adaptation")
+ .push_constant(Type::FLOAT, "light_adaptation")
+ .push_constant(Type::VEC3, "luminance_coefficients")
+ .sampler(0, ImageType::FLOAT_2D, "input_tx")
+ .image(0, GPU_RGBA16F, Qualifier::WRITE, ImageType::FLOAT_2D, "output_img")
+ .compute_source("compositor_tone_map_photoreceptor.glsl")
+ .do_static_compilation(true);
diff --git a/source/blender/gpu/shaders/compositor/infos/compositor_tone_map_simple_info.hh b/source/blender/gpu/shaders/compositor/infos/compositor_tone_map_simple_info.hh
new file mode 100644
index 00000000000..2b220af9460
--- /dev/null
+++ b/source/blender/gpu/shaders/compositor/infos/compositor_tone_map_simple_info.hh
@@ -0,0 +1,13 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#include "gpu_shader_create_info.hh"
+
+GPU_SHADER_CREATE_INFO(compositor_tone_map_simple)
+ .local_group_size(16, 16)
+ .push_constant(Type::FLOAT, "luminance_scale")
+ .push_constant(Type::FLOAT, "luminance_scale_blend_factor")
+ .push_constant(Type::FLOAT, "inverse_gamma")
+ .sampler(0, ImageType::FLOAT_2D, "input_tx")
+ .image(0, GPU_RGBA16F, Qualifier::WRITE, ImageType::FLOAT_2D, "output_img")
+ .compute_source("compositor_tone_map_simple.glsl")
+ .do_static_compilation(true);
diff --git a/source/blender/gpu/shaders/gpu_shader_icon_frag.glsl b/source/blender/gpu/shaders/gpu_shader_icon_frag.glsl
new file mode 100644
index 00000000000..4452349f23c
--- /dev/null
+++ b/source/blender/gpu/shaders/gpu_shader_icon_frag.glsl
@@ -0,0 +1,42 @@
+/**
+ * Draw the icons, leaving a semi-transparent rectangle on top of the icon.
+ *
+ * The top-left corner of the rectangle is rounded and drawned with anti-alias.
+ * The anti-alias is done by transitioning from the outer to the inner radius of
+ * the rounded corner, and the rectangle sides.
+ */
+
+void main()
+{
+ /* Top-left rounded corner parameters. */
+ const float circle_radius_outer = 0.1;
+ const float circle_radius_inner = 0.075;
+
+ /**
+ * Add a bit transparency to see a bit of the icon, without
+ * getting on the way of readability. */
+ const float mask_transparency = 0.25;
+
+ vec2 circle_center = vec2(circle_radius_outer - text_width, 0.5);
+ fragColor = texture(image, texCoord_interp) * color;
+
+ /* radius in icon space (1 is the icon width). */
+ float radius = length(mask_coord_interp - circle_center);
+ float mask = smoothstep(circle_radius_inner, circle_radius_outer, radius);
+
+ bool lower_half = mask_coord_interp.y < circle_center.y;
+ bool right_half = mask_coord_interp.x > circle_center.x;
+
+ if (right_half && mask_coord_interp.y < circle_center.y + circle_radius_outer) {
+ mask = smoothstep(circle_center.y + circle_radius_inner,
+ circle_center.y + circle_radius_outer,
+ mask_coord_interp.y);
+ }
+ if (lower_half && mask_coord_interp.x > circle_center.x - circle_radius_outer) {
+ mask = smoothstep(circle_center.x - circle_radius_inner,
+ circle_center.x - circle_radius_outer,
+ mask_coord_interp.x);
+ }
+
+ fragColor = mix(vec4(0.0), fragColor, max(mask_transparency, mask));
+}
diff --git a/source/blender/gpu/shaders/gpu_shader_icon_vert.glsl b/source/blender/gpu/shaders/gpu_shader_icon_vert.glsl
new file mode 100644
index 00000000000..25f64bfe0b6
--- /dev/null
+++ b/source/blender/gpu/shaders/gpu_shader_icon_vert.glsl
@@ -0,0 +1,37 @@
+/**
+ * Simple shader that just draw one icon at the specified location
+ * does not need any vertex input (producing less call to immBegin/End)
+ */
+
+void main()
+{
+ vec2 uv;
+ vec2 co;
+
+ if (gl_VertexID == 0) {
+ co = rect_geom.xw;
+ uv = rect_icon.xw;
+ mask_coord_interp = vec2(0, 1);
+ }
+ else if (gl_VertexID == 1) {
+ co = rect_geom.xy;
+ uv = rect_icon.xy;
+ mask_coord_interp = vec2(0, 0);
+ }
+ else if (gl_VertexID == 2) {
+ co = rect_geom.zw;
+ uv = rect_icon.zw;
+ mask_coord_interp = vec2(1, 1);
+ }
+ else {
+ co = rect_geom.zy;
+ uv = rect_icon.zy;
+ mask_coord_interp = vec2(1, 0);
+ }
+
+ /* Put origin in lower right corner. */
+ mask_coord_interp.x -= 1;
+
+ gl_Position = ModelViewProjectionMatrix * vec4(co, 0.0f, 1.0f);
+ texCoord_interp = uv;
+}
diff --git a/source/blender/gpu/shaders/infos/gpu_interface_info.hh b/source/blender/gpu/shaders/infos/gpu_interface_info.hh
index d77c65e48a7..060def16f81 100644
--- a/source/blender/gpu/shaders/infos/gpu_interface_info.hh
+++ b/source/blender/gpu/shaders/infos/gpu_interface_info.hh
@@ -18,3 +18,6 @@ GPU_SHADER_INTERFACE_INFO(smooth_radii_outline_iface, "").smooth(Type::VEC4, "ra
GPU_SHADER_INTERFACE_INFO(flat_color_smooth_tex_coord_interp_iface, "")
.flat(Type::VEC4, "finalColor")
.smooth(Type::VEC2, "texCoord_interp");
+GPU_SHADER_INTERFACE_INFO(smooth_icon_interp_iface, "")
+ .smooth(Type::VEC2, "texCoord_interp")
+ .smooth(Type::VEC2, "mask_coord_interp");
diff --git a/source/blender/gpu/shaders/infos/gpu_shader_icon_info.hh b/source/blender/gpu/shaders/infos/gpu_shader_icon_info.hh
new file mode 100644
index 00000000000..3d4077bdb09
--- /dev/null
+++ b/source/blender/gpu/shaders/infos/gpu_shader_icon_info.hh
@@ -0,0 +1,22 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later
+ * Copyright 2022 Blender Foundation. All rights reserved. */
+
+/** \file
+ * \ingroup gpu
+ */
+
+#include "gpu_interface_info.hh"
+#include "gpu_shader_create_info.hh"
+
+GPU_SHADER_CREATE_INFO(gpu_shader_icon)
+ .vertex_out(smooth_icon_interp_iface)
+ .fragment_out(0, Type::VEC4, "fragColor")
+ .push_constant(Type::MAT4, "ModelViewProjectionMatrix")
+ .push_constant(Type::VEC4, "color")
+ .push_constant(Type::VEC4, "rect_icon")
+ .push_constant(Type::VEC4, "rect_geom")
+ .push_constant(Type::FLOAT, "text_width")
+ .sampler(0, ImageType::FLOAT_2D, "image")
+ .vertex_source("gpu_shader_icon_vert.glsl")
+ .fragment_source("gpu_shader_icon_frag.glsl")
+ .do_static_compilation(true);