diff --git a/compositor_render/src/transformations/layout.rs b/compositor_render/src/transformations/layout.rs index 3a5ff2828..d33c87d7f 100644 --- a/compositor_render/src/transformations/layout.rs +++ b/compositor_render/src/transformations/layout.rs @@ -121,6 +121,7 @@ impl LayoutNode { let params: Vec = layouts .iter() .map(|layout| { + let layout_resolution = [layout.width, layout.height]; let (is_texture, background_color, input_resolution) = match layout.content { RenderLayoutContent::ChildNode { index, .. } => ( 1, @@ -137,6 +138,7 @@ impl LayoutNode { .vertices_transformation_matrix(&output_resolution), transform_texture_coords_matrix: layout .texture_coords_transformation_matrix(&input_resolution), + layout_resolution, } }) .collect(); diff --git a/compositor_render/src/transformations/layout/apply_layouts.wgsl b/compositor_render/src/transformations/layout/apply_layouts.wgsl index 8e02b66f2..2b8648977 100644 --- a/compositor_render/src/transformations/layout/apply_layouts.wgsl +++ b/compositor_render/src/transformations/layout/apply_layouts.wgsl @@ -14,6 +14,7 @@ struct Layout { texture_coord_transformation: mat4x4, color: vec4, // used only when is_texture == 0 is_texture: u32, // 0 -> color, 1 -> texture + layout_resolution: vec2, } @group(0) @binding(0) var texture: texture_2d; diff --git a/compositor_render/src/transformations/layout/apply_layouts_lanczos.wgsl b/compositor_render/src/transformations/layout/apply_layouts_lanczos.wgsl new file mode 100644 index 000000000..64082f59e --- /dev/null +++ b/compositor_render/src/transformations/layout/apply_layouts_lanczos.wgsl @@ -0,0 +1,111 @@ +struct VertexInput { + @location(0) position: vec3, + @location(1) tex_coords: vec2, +} + +struct VertexOutput { + @builtin(position) position: vec4, + @location(0) tex_coords: vec2, +} + + +struct Layout { + vertices_transformation: mat4x4, + texture_coord_transformation: mat4x4, + color: vec4, // used only when is_texture == 0 + is_texture: u32, // 0 -> color, 1 -> texture + layout_resolution: vec2, +} + +@group(0) @binding(0) var texture: texture_2d; +@group(1) @binding(0) var layouts: array; +@group(2) @binding(0) var sampler_: sampler; + +var layout_id: u32; + +@vertex +fn vs_main(input: VertexInput) -> VertexOutput { + var output: VertexOutput; + + let vertices_transformation_matrix: mat4x4 = layouts[layout_id].vertices_transformation; + let texture_coord_transformation_matrix: mat4x4 = layouts[layout_id].texture_coord_transformation; + + output.position = vec4(input.position, 1.0) * vertices_transformation_matrix; + output.tex_coords = (vec4(input.tex_coords, 0.0, 1.0) * texture_coord_transformation_matrix).xy; + + return output; +} + +const PI: f32 = 3.1415926535897932384626433832795; +// Lanczos parameter a +const A: f32 = 3.0; + + +// Lanczos Sinc Function +fn sinc(x: f32) -> f32 { + if x == 0.0 { + return 1.0; + } + let x_pi = PI * x; + return (sin(x_pi) / x_pi); +} + +// Lanczos Weight Function +fn lanczos(x: f32) -> f32 { + if abs(x) < A { + return sinc(x) * sinc(x / A); + } else { + return 0.0; + } +} + + +@fragment +fn fs_main(input: VertexOutput) -> @location(0) vec4 { + let current_layout = layouts[layout_id]; + let dim = textureDimensions(texture); + + let input_resolution: vec2 = vec2(f32(dim.x), f32(dim.y)); + let scaling_factor: vec2 = current_layout.layout_resolution / input_resolution; + + let half_pixel_size: vec2 = vec2(0.5) / input_resolution; + + var color: vec4 = vec4(0.0, 0.0, 0.0, 0.0); + var normalization: f32 = 0.0; + + + for (var i: f32 = -A; i <= A; i += 1.0) { + for (var j: f32 = -A; j <= A; j += 1.0) { + // Position on input texture in pixels + let x = input.tex_coords * input_resolution; + // Sample position in pixels + let sample_pixel_x = floor(input.tex_coords.x * input_resolution.x) + i; + let sample_pixel_y = floor(input.tex_coords.y * input_resolution.y) + j; + + // Sample position in texture coordinates + let sample_coord_x = clamp((sample_pixel_x / input_resolution.x) + half_pixel_size.x, 0.0, 1.0); + let sample_coord_y = clamp((sample_pixel_y / input_resolution.y) + half_pixel_size.y, 0.0, 1.0); + let sample_coords = vec2(sample_coord_x, sample_coord_y); + + // Distance of sampled output pixel from sampled position + let dx = sample_pixel_x - x.x; + let dy = sample_pixel_y - x.y; + + let sample_color: vec4 = textureSample(texture, sampler_, sample_coords); + // Lanczos weight normalizing sampled values + let weight: f32 = lanczos(dx) * lanczos(dy); + color += sample_color * weight; + normalization += weight; + } + } + + // sampling can't be conditional, so in case of plane_id == -1 + // sample textures[0], but ignore the result. + if (current_layout.is_texture == 0u) { + return current_layout.color; + } + // clamp transparent, when crop > input texture + let is_inside: f32 = round(f32(input.tex_coords.x < 1.0 && input.tex_coords.x > 0.0 && input.tex_coords.y > 0.0 && input.tex_coords.y < 1.0)); + + return is_inside * color / normalization; +} diff --git a/compositor_render/src/transformations/layout/params.rs b/compositor_render/src/transformations/layout/params.rs index 168e610c0..3cee0a8ec 100644 --- a/compositor_render/src/transformations/layout/params.rs +++ b/compositor_render/src/transformations/layout/params.rs @@ -7,8 +7,9 @@ use crate::{scene::RGBAColor, wgpu::WgpuCtx}; pub(super) struct LayoutNodeParams { pub(super) transform_vertices_matrix: Mat4, pub(super) transform_texture_coords_matrix: Mat4, - pub(super) is_texture: u32, pub(super) background_color: RGBAColor, + pub(super) is_texture: u32, + pub(super) layout_resolution: [f32; 2], } pub(super) struct ParamsBuffer { @@ -70,21 +71,24 @@ impl ParamsBuffer { params .iter() .map(LayoutNodeParams::shader_buffer_content) - .collect::>() + .collect::>() .concat() .into() } } +const LAYOUT_STRUCT_SIZE: usize = 160; + impl LayoutNodeParams { - fn shader_buffer_content(&self) -> [u8; 160] { + fn shader_buffer_content(&self) -> [u8; LAYOUT_STRUCT_SIZE] { let Self { transform_vertices_matrix, transform_texture_coords_matrix, - is_texture, background_color, + is_texture, + layout_resolution, } = self; - let mut result = [0; 160]; + let mut result = [0; LAYOUT_STRUCT_SIZE]; fn from_u8_color(value: u8) -> [u8; 4] { (value as f32 / 255.0).to_ne_bytes() } @@ -93,7 +97,6 @@ impl LayoutNodeParams { result[64..128].copy_from_slice(bytemuck::bytes_of( &transform_texture_coords_matrix.transpose(), )); - // 12 bytes padding result[128..132].copy_from_slice(&from_u8_color(background_color.0)); result[132..136].copy_from_slice(&from_u8_color(background_color.1)); result[136..140].copy_from_slice(&from_u8_color(background_color.2)); @@ -101,6 +104,8 @@ impl LayoutNodeParams { result[144..148].copy_from_slice(&is_texture.to_ne_bytes()); // 12 bytes padding + result[152..156].copy_from_slice(&layout_resolution[0].to_ne_bytes()); + result[156..160].copy_from_slice(&layout_resolution[1].to_ne_bytes()); result } diff --git a/compositor_render/src/transformations/layout/shader.rs b/compositor_render/src/transformations/layout/shader.rs index fbd8cab42..21adbbcba 100644 --- a/compositor_render/src/transformations/layout/shader.rs +++ b/compositor_render/src/transformations/layout/shader.rs @@ -21,7 +21,7 @@ impl LayoutShader { let shader_module = wgpu_ctx .device - .create_shader_module(wgpu::include_wgsl!("./apply_layouts.wgsl")); + .create_shader_module(wgpu::include_wgsl!("./apply_layouts_lanczos.wgsl")); let result = Self::new_pipeline(wgpu_ctx, shader_module)?; scope.pop(&wgpu_ctx.device)?; @@ -93,9 +93,11 @@ impl LayoutShader { occlusion_query_set: None, }); - for (layout_id, texture_bg) in input_texture_bgs.iter().enumerate() { - render_pass.set_pipeline(&self.pipeline); + render_pass.set_pipeline(&self.pipeline); + render_pass.set_bind_group(1, params, &[]); + render_pass.set_bind_group(2, &self.sampler.bind_group, &[]); + for (layout_id, texture_bg) in input_texture_bgs.iter().enumerate() { render_pass.set_push_constants( ShaderStages::VERTEX_FRAGMENT, 0, @@ -103,8 +105,6 @@ impl LayoutShader { ); render_pass.set_bind_group(0, texture_bg, &[]); - render_pass.set_bind_group(1, params, &[]); - render_pass.set_bind_group(2, &self.sampler.bind_group, &[]); wgpu_ctx.plane.draw(&mut render_pass); } diff --git a/compositor_render/src/wgpu/format/interleaved_yuv_to_rgba.wgsl b/compositor_render/src/wgpu/format/interleaved_yuv_to_rgba.wgsl index 232e0ccc3..91ad5407f 100644 --- a/compositor_render/src/wgpu/format/interleaved_yuv_to_rgba.wgsl +++ b/compositor_render/src/wgpu/format/interleaved_yuv_to_rgba.wgsl @@ -33,14 +33,14 @@ fn fs_main(input: VertexOutput) -> @location(0) vec4 { // - adding eps to avoid numerical errors when converting f32 -> u32 var x_pos = u32((input.tex_coords.x * f32(dimensions.x) - half_pixel_width + eps) * 2.0); // x_pos/2 is calculated before conversion to float to make sure that reminder is lost for odd column. - var tex_coords = vec2((f32(x_pos/2) / f32(dimensions.x)) + half_pixel_width, input.tex_coords.y); + var tex_coords = vec2((f32( x_pos / 2u) / f32(dimensions.x)) + half_pixel_width, input.tex_coords.y); var uyvy = textureSample(texture, sampler_, tex_coords); var u = uyvy.x; var v = uyvy.z; var y = uyvy.y; - if (x_pos % 2 != 0) { + if (x_pos % 2u != 0u) { y = uyvy.w; } diff --git a/integration_tests/examples/test_pattern.rs b/integration_tests/examples/test_pattern.rs index 1de4bef12..cead3aeba 100644 --- a/integration_tests/examples/test_pattern.rs +++ b/integration_tests/examples/test_pattern.rs @@ -71,8 +71,13 @@ fn start_example_client_code() -> Result<()> { "shader_id": "example_shader", "children": [ { - "type": "input_stream", - "input_id": "input_1", + "type": "rescaler", + "width": 1760, + "height": 990, + "child": { + "type": "input_stream", + "input_id": "input_1" + }, } ], "resolution": { "width": VIDEO_RESOLUTION.width, "height": VIDEO_RESOLUTION.height },