summaryrefslogtreecommitdiff
path: root/rust/crates/compositor/src/shaders
diff options
context:
space:
mode:
Diffstat (limited to 'rust/crates/compositor/src/shaders')
-rw-r--r--rust/crates/compositor/src/shaders/blend.wgsl142
-rw-r--r--rust/crates/compositor/src/shaders/layer.wgsl49
-rw-r--r--rust/crates/compositor/src/shaders/mask.wgsl25
3 files changed, 216 insertions, 0 deletions
diff --git a/rust/crates/compositor/src/shaders/blend.wgsl b/rust/crates/compositor/src/shaders/blend.wgsl
new file mode 100644
index 00000000..9cc0e120
--- /dev/null
+++ b/rust/crates/compositor/src/shaders/blend.wgsl
@@ -0,0 +1,142 @@
+struct VertexOutput {
+ @builtin(position) position: vec4f,
+ @location(0) tex_coord: vec2f,
+}
+
+struct BlendUniforms {
+ blend_mode: u32,
+ _pad0: u32,
+ _pad1: u32,
+ _pad2: u32,
+}
+
+@group(0) @binding(0) var base_texture: texture_2d<f32>;
+@group(0) @binding(1) var base_sampler: sampler;
+@group(1) @binding(0) var layer_texture: texture_2d<f32>;
+@group(1) @binding(1) var layer_sampler: sampler;
+@group(2) @binding(0) var<uniform> uniforms: BlendUniforms;
+
+fn clamp01(color: vec3f) -> vec3f {
+ return clamp(color, vec3f(0.0), vec3f(1.0));
+}
+
+fn lum(c: vec3f) -> f32 {
+ return dot(c, vec3f(0.3, 0.59, 0.11));
+}
+
+fn sat(c: vec3f) -> f32 {
+ return max(max(c.r, c.g), c.b) - min(min(c.r, c.g), c.b);
+}
+
+fn clip_color(c: vec3f) -> vec3f {
+ var result = c;
+ let l = lum(result);
+ let n = min(min(result.r, result.g), result.b);
+ let x = max(max(result.r, result.g), result.b);
+ if (n < 0.0) {
+ result = l + ((result - l) * l) / (l - n);
+ }
+ if (x > 1.0) {
+ result = l + ((result - l) * (1.0 - l)) / (x - l);
+ }
+ return result;
+}
+
+fn set_lum(c: vec3f, l: f32) -> vec3f {
+ return clip_color(c + (l - lum(c)));
+}
+
+fn set_sat(color: vec3f, target_sat: f32) -> vec3f {
+ var result = color;
+ let max_value = max(max(result.r, result.g), result.b);
+ let min_value = min(min(result.r, result.g), result.b);
+ if (max_value <= min_value) {
+ return vec3f(0.0);
+ }
+ let scale = target_sat / (max_value - min_value);
+ result = (result - vec3f(min_value)) * scale;
+ return result;
+}
+
+fn hard_light(base: vec3f, layer: vec3f) -> vec3f {
+ let low = 2.0 * base * layer;
+ let high = 1.0 - 2.0 * (1.0 - base) * (1.0 - layer);
+ return select(low, high, layer >= vec3f(0.5));
+}
+
+fn soft_light_channel(base: f32, layer: f32) -> f32 {
+ if (layer <= 0.5) {
+ return base - (1.0 - 2.0 * layer) * base * (1.0 - base);
+ }
+
+ let d = select(
+ ((16.0 * base - 12.0) * base + 4.0) * base,
+ sqrt(base),
+ base > 0.25,
+ );
+ return base + (2.0 * layer - 1.0) * (d - base);
+}
+
+fn soft_light(base: vec3f, layer: vec3f) -> vec3f {
+ return vec3f(
+ soft_light_channel(base.r, layer.r),
+ soft_light_channel(base.g, layer.g),
+ soft_light_channel(base.b, layer.b),
+ );
+}
+
+fn color_dodge(base: vec3f, layer: vec3f) -> vec3f {
+ return select(
+ min(vec3f(1.0), base / max(vec3f(0.0001), vec3f(1.0) - layer)),
+ vec3f(1.0),
+ layer >= vec3f(1.0),
+ );
+}
+
+fn color_burn(base: vec3f, layer: vec3f) -> vec3f {
+ return select(
+ vec3f(1.0) - min(vec3f(1.0), (vec3f(1.0) - base) / max(vec3f(0.0001), layer)),
+ vec3f(0.0),
+ layer <= vec3f(0.0),
+ );
+}
+
+fn blend_rgb(base: vec3f, layer: vec3f, mode: u32) -> vec3f {
+ switch mode {
+ case 1u { return min(base, layer); }
+ case 2u { return base * layer; }
+ case 3u { return color_burn(base, layer); }
+ case 4u { return max(base, layer); }
+ case 5u { return 1.0 - (1.0 - base) * (1.0 - layer); }
+ case 6u { return min(vec3f(1.0), base + layer); }
+ case 7u { return color_dodge(base, layer); }
+ case 8u { return select(
+ 2.0 * base * layer,
+ 1.0 - 2.0 * (1.0 - base) * (1.0 - layer),
+ base >= vec3f(0.5),
+ ); }
+ case 9u { return soft_light(base, layer); }
+ case 10u { return hard_light(base, layer); }
+ case 11u { return abs(base - layer); }
+ case 12u { return base + layer - 2.0 * base * layer; }
+ case 13u { return set_lum(set_sat(layer, sat(base)), lum(base)); }
+ case 14u { return set_lum(set_sat(base, sat(layer)), lum(base)); }
+ case 15u { return set_lum(layer, lum(base)); }
+ case 16u { return set_lum(base, lum(layer)); }
+ default { return layer; }
+ }
+}
+
+@fragment
+fn fragment_main(input: VertexOutput) -> @location(0) vec4f {
+ let base = textureSample(base_texture, base_sampler, input.tex_coord);
+ let layer = textureSample(layer_texture, layer_sampler, input.tex_coord);
+
+ let blend_rgb_value = blend_rgb(base.rgb, layer.rgb, uniforms.blend_mode);
+ let out_alpha = layer.a + base.a * (1.0 - layer.a);
+ let out_rgb =
+ ((1.0 - layer.a) * base.rgb) +
+ (layer.a * ((1.0 - base.a) * layer.rgb + base.a * blend_rgb_value));
+
+ return vec4f(clamp01(out_rgb), out_alpha);
+}
diff --git a/rust/crates/compositor/src/shaders/layer.wgsl b/rust/crates/compositor/src/shaders/layer.wgsl
new file mode 100644
index 00000000..9e335687
--- /dev/null
+++ b/rust/crates/compositor/src/shaders/layer.wgsl
@@ -0,0 +1,49 @@
+struct VertexOutput {
+ @builtin(position) position: vec4f,
+ @location(0) tex_coord: vec2f,
+}
+
+struct LayerUniforms {
+ resolution: vec2f,
+ center: vec2f,
+ size: vec2f,
+ rotation_radians: f32,
+ opacity: f32,
+ flip_x: f32,
+ flip_y: f32,
+}
+
+@group(0) @binding(0) var source_texture: texture_2d<f32>;
+@group(0) @binding(1) var source_sampler: sampler;
+@group(1) @binding(0) var<uniform> uniforms: LayerUniforms;
+
+fn rotate_inverse(point: vec2f, angle: f32) -> vec2f {
+ let c = cos(angle);
+ let s = sin(angle);
+ return vec2f(
+ point.x * c + point.y * s,
+ -point.x * s + point.y * c,
+ );
+}
+
+@fragment
+fn fragment_main(input: VertexOutput) -> @location(0) vec4f {
+ let pixel = input.tex_coord * uniforms.resolution;
+ let local = rotate_inverse(pixel - uniforms.center, uniforms.rotation_radians);
+
+ let uv = vec2f(
+ local.x / uniforms.size.x + 0.5,
+ local.y / uniforms.size.y + 0.5,
+ );
+
+ if (uv.x < 0.0 || uv.x > 1.0 || uv.y < 0.0 || uv.y > 1.0) {
+ return vec4f(0.0, 0.0, 0.0, 0.0);
+ }
+
+ let sample_uv = vec2f(
+ select(uv.x, 1.0 - uv.x, uniforms.flip_x > 0.5),
+ select(uv.y, 1.0 - uv.y, uniforms.flip_y > 0.5),
+ );
+ let color = textureSampleLevel(source_texture, source_sampler, sample_uv, 0.0);
+ return vec4f(color.rgb, color.a * uniforms.opacity);
+}
diff --git a/rust/crates/compositor/src/shaders/mask.wgsl b/rust/crates/compositor/src/shaders/mask.wgsl
new file mode 100644
index 00000000..6bbfeccc
--- /dev/null
+++ b/rust/crates/compositor/src/shaders/mask.wgsl
@@ -0,0 +1,25 @@
+struct VertexOutput {
+ @builtin(position) position: vec4f,
+ @location(0) tex_coord: vec2f,
+}
+
+struct MaskUniforms {
+ inverted: f32,
+ _pad0: f32,
+ _pad1: f32,
+ _pad2: f32,
+}
+
+@group(0) @binding(0) var layer_texture: texture_2d<f32>;
+@group(0) @binding(1) var layer_sampler: sampler;
+@group(1) @binding(0) var mask_texture: texture_2d<f32>;
+@group(1) @binding(1) var mask_sampler: sampler;
+@group(2) @binding(0) var<uniform> uniforms: MaskUniforms;
+
+@fragment
+fn fragment_main(input: VertexOutput) -> @location(0) vec4f {
+ let layer = textureSample(layer_texture, layer_sampler, input.tex_coord);
+ let mask = textureSample(mask_texture, mask_sampler, input.tex_coord).a;
+ let alpha = select(mask, 1.0 - mask, uniforms.inverted > 0.5);
+ return vec4f(layer.rgb, layer.a * alpha);
+}