From 221d544a01ad1cac561022c17a1561dc58752049 Mon Sep 17 00:00:00 2001 From: Bradlee Speice Date: Sun, 2 Feb 2025 19:00:21 -0500 Subject: [PATCH] Well, it's broken, but I'm making progress --- Cargo.lock | 59 ++++++++ Cargo.toml | 2 + crates/flare-shader/Cargo.toml | 2 + crates/flare-shader/src/lib.rs | 268 ++++++++++++++++++++++++++++----- crates/flare/Cargo.toml | 2 + crates/flare/src/main.rs | 227 +++++++++++++++++----------- 6 files changed, 440 insertions(+), 120 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 01e74c9..51fa1ba 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -260,6 +260,12 @@ dependencies = [ "syn", ] +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + [[package]] name = "bytes" version = "1.9.0" @@ -555,6 +561,8 @@ dependencies = [ "flare-shader", "futures-executor", "glam", + "rand", + "rand_xoshiro", "spirv-builder", "wgpu", "winit", @@ -566,6 +574,8 @@ version = "0.1.0" dependencies = [ "bytemuck", "glam", + "rand", + "rand_xoshiro", "spirv-std", ] @@ -1483,6 +1493,15 @@ dependencies = [ "windows-sys 0.59.0", ] +[[package]] +name = "ppv-lite86" +version = "0.2.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" +dependencies = [ + "zerocopy", +] + [[package]] name = "presser" version = "0.3.1" @@ -1531,6 +1550,45 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "rand_xoshiro" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f97cdb2a36ed4183de61b2f824cc45c9f1037f28afe0a322e9fff4c108b5aaa" +dependencies = [ + "rand_core", +] + [[package]] name = "range-alloc" version = "0.1.4" @@ -2940,6 +2998,7 @@ version = "0.7.35" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" dependencies = [ + "byteorder", "zerocopy-derive", ] diff --git a/Cargo.toml b/Cargo.toml index dbfd32a..4396c62 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,6 +20,8 @@ bytemuck = { version = "1.21", features = ["derive"] } env_logger = "0.11" futures-executor = "0.3" glam = { version = "0.29", features = ["bytemuck", "libm"], default-features = false } +rand = { version = "0.8", default-features = false } +rand_xoshiro = "0.6" spirv-std = { git = "https://github.com/rust-gpu/rust-gpu", rev = "854e9ba7da26d52ca0038ab2c7b252652e4d6b1e" } #spirv-builder = { git = "https://github.com/rust-gpu/rust-gpu", rev = "854e9ba7da26d52ca0038ab2c7b252652e4d6b1e"} spirv-builder = { git = "https://github.com/rust-gpu/rust-gpu", rev = "854e9ba7da26d52ca0038ab2c7b252652e4d6b1e", features = ["use-installed-tools"], default-features = false } diff --git a/crates/flare-shader/Cargo.toml b/crates/flare-shader/Cargo.toml index 92fd300..4302d0c 100644 --- a/crates/flare-shader/Cargo.toml +++ b/crates/flare-shader/Cargo.toml @@ -11,6 +11,8 @@ crate-type = ["dylib", "lib"] [dependencies] bytemuck.workspace = true glam.workspace = true +rand.workspace = true +rand_xoshiro.workspace = true spirv-std.workspace = true [lints] diff --git a/crates/flare-shader/src/lib.rs b/crates/flare-shader/src/lib.rs index 4750f7b..d2480fb 100644 --- a/crates/flare-shader/src/lib.rs +++ b/crates/flare-shader/src/lib.rs @@ -1,24 +1,29 @@ #![cfg_attr(target_arch = "spirv", no_std)] -use glam::{UVec2, Vec4, Vec4Swizzles, uvec2, vec2, vec4}; +use glam::{Vec4, Vec4Swizzles, vec2, vec4}; +use rand::Rng; +use rand::distributions::Standard; +use rand::prelude::Distribution; +use rand_xoshiro::Xoshiro128Plus; use spirv_std::spirv; +use spirv_std::num_traits::Float; #[derive(Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)] #[repr(C)] -pub struct IfsConstants { - accum_width: u32, - accum_height: u32, - viewport_width: u32, - viewport_height: u32, +pub struct ImageConstants { + accum_width: i32, + accum_height: i32, + viewport_width: i32, + viewport_height: i32, background_color: Vec4, } -impl IfsConstants { +impl ImageConstants { pub fn new( - accum_width: u32, - accum_height: u32, - viewport_width: u32, - viewport_height: u32, + accum_width: i32, + accum_height: i32, + viewport_width: i32, + viewport_height: i32, background_color: Vec4, ) -> Self { Self { @@ -30,21 +35,48 @@ impl IfsConstants { } } - pub fn viewport_dimensions(&self) -> UVec2 { - uvec2(self.viewport_width, self.viewport_height) + pub fn viewport_dimensions(&self) -> glam::IVec2 { + glam::ivec2(self.viewport_width, self.viewport_height) } - pub fn with_accumulate(&mut self, width: u32, height: u32) { + pub fn with_accumulate(&mut self, width: i32, height: i32) { self.accum_width = width; self.accum_height = height; } - pub fn with_viewport(&mut self, width: u32, height: u32) { + pub fn with_viewport(&mut self, width: i32, height: i32) { self.viewport_width = width; self.viewport_height = height; } } +#[derive(Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)] +#[repr(C)] +pub struct CameraConstants { + scale: f32, + zoom: f32, + rotate: f32, + offset_x: f32, + offset_y: f32, +} + +impl CameraConstants { + pub fn camera(&self, image_width: i32, image_height: i32) -> glam::Affine2 { + let zoom_rotate_offset = glam::Affine2::from_scale_angle_translation( + glam::Vec2::splat(2f32.powf(self.zoom)), + self.rotate.to_radians(), + -vec2(self.offset_x, self.offset_y), + ); + let ifs_to_pixel = glam::Affine2::from_scale_angle_translation( + glam::Vec2::splat(self.scale), + 0.0, + vec2(image_width as f32 / 2.0, image_height as f32 / 2.0), + ); + + ifs_to_pixel * zoom_rotate_offset + } +} + pub trait Color { type Element; const BLACK: Self; @@ -59,37 +91,203 @@ impl Color for Vec4 { const WHITE: Self = vec4(1., 1., 1., 1.); } -pub(crate) fn image_index(pixel_x: usize, pixel_y: usize, image_width: u32) -> usize { - pixel_x + pixel_y * image_width as usize +pub(crate) fn image_index(pixel_x: i32, pixel_y: i32, image_width: i32) -> i32 { + pixel_x + pixel_y * image_width +} + +pub struct BiUnit; + +impl Distribution for BiUnit { + fn sample(&self, rng: &mut R) -> f32 { + rng.sample::(Standard) * 2.0 - 1.0 + } +} + +#[derive(Copy, Clone, Debug, bytemuck::Pod, bytemuck::Zeroable)] +#[repr(C)] +pub struct ThreadState { + rng_state: [u32; 4], + point: glam::Vec2, +} + +impl ThreadState { + fn xoshiro_to_state(rng: &Xoshiro128Plus) -> [u32; 4] { + unsafe { core::mem::transmute::<_, [u32; 4]>(rng.clone()) } + } + + fn state_to_xoshiro(rng_state: &[u32; 4]) -> Xoshiro128Plus { + unsafe { core::mem::transmute::<_, Xoshiro128Plus>(*rng_state) } + } + + pub fn new(rng: &mut Xoshiro128Plus) -> Self { + let point = vec2(rng.sample(BiUnit), rng.sample(BiUnit)); + let rng_state = Self::xoshiro_to_state(rng); + rng.jump(); + Self { rng_state, point } + } + + pub fn get_rng(&self) -> Xoshiro128Plus { + Self::state_to_xoshiro(&self.rng_state) + } + + pub fn set_rng(&mut self, rng: &Xoshiro128Plus) { + self.rng_state = Self::xoshiro_to_state(rng) + } +} + +#[derive(Copy, Clone, Debug, bytemuck::Pod, bytemuck::Zeroable)] +#[repr(C)] +pub struct Coefs { + a: f32, + b: f32, + c: f32, + d: f32, + e: f32, + f: f32, +} + +impl Coefs { + pub fn new(a: f32, b: f32, c: f32, d: f32, e: f32, f: f32) -> Self { + Self { a, b, c, d, e, f } + } + + pub fn apply(&self, point: glam::Vec2) -> glam::Vec2 { + vec2( + self.a * point.x + self.b * point.y + self.c, + self.d * point.x + self.e * point.y + self.f, + ) + } } #[derive(Copy, Clone, Debug, bytemuck::Pod, bytemuck::Zeroable)] #[repr(C)] pub struct Transform { + coefs: Coefs, variation_offset: u32, variation_count: u32, weight: f32, - color: f32, - color_speed: f32, +} + +impl Transform { + pub fn new(coefs: Coefs, variation_offset: u32, variation_count: u32, weight: f32) -> Self { + Self { + coefs, + variation_offset, + variation_count, + weight, + } + } + + pub fn apply(&self, variations: &[Variation], point: glam::Vec2) -> glam::Vec2 { + let point = self.coefs.apply(point); + let mut variation_point = glam::Vec2::ZERO; + let variation_begin = self.variation_offset as usize; + let variation_end = (self.variation_offset + self.variation_count) as usize; + for i in variation_begin..variation_end { + variation_point += variations[i].apply(point); + } + variation_point + } +} + +#[derive(Copy, Clone, Debug)] +#[repr(u32)] +pub enum VariationKind { + Linear = 0, +} + +// UNSAFE: Sound because enum has guaranteed layout (u32) and defined zero-value +unsafe impl bytemuck::Zeroable for VariationKind {} +// UNSAFE: Sound because enum has guaranteed layout (u32) and defined zero-value +unsafe impl bytemuck::Pod for VariationKind {} + +#[derive(Copy, Clone, Debug, bytemuck::Pod, bytemuck::Zeroable)] +#[repr(C)] +pub struct Variation { + kind: VariationKind, + weight: f32, +} + +impl Variation { + pub fn new(kind: VariationKind, weight: f32) -> Self { + Self { kind, weight } + } + + pub fn apply(&self, point: glam::Vec2) -> glam::Vec2 { + (match self.kind { + VariationKind::Linear => point, + }) * self.weight + } +} + +fn next_transform( + rng: &mut R, + total_weight: f32, + transforms: &[Transform], +) -> Transform { + let mut weight = rng.sample::(Standard) * total_weight; + for i in 0..transforms.len() { + weight -= transforms[i].weight; + if weight <= 0.0 { + return transforms[i]; + } + } + unreachable!() } #[spirv(compute(threads(1)))] pub fn main_cs( - #[spirv(push_constant)] constants: &IfsConstants, - #[spirv(storage_buffer, descriptor_set = 0, binding = 0)] transforms: &[Transform], - #[spirv(storage_buffer, descriptor_set = 0, binding = 1)] accum_image: &mut [Vec4], + #[spirv(push_constant)] image_constants: &ImageConstants, + #[spirv(storage_buffer, descriptor_set = 0, binding = 0)] thread_state: &mut [ThreadState], + #[spirv(storage_buffer, descriptor_set = 0, binding = 1)] transforms: &[Transform], + #[spirv(storage_buffer, descriptor_set = 0, binding = 2)] variations: &[Variation], + #[spirv(storage_buffer, descriptor_set = 0, binding = 3)] accum_image: &mut [Vec4], ) { - let block_size = 64; - for i_width in 0..constants.accum_width as usize { - for i_height in 0..constants.accum_height as usize { - let color = if (i_width / block_size % 2 == 1) != (i_height / block_size % 2 == 1) { - Vec4::BLACK - } else { - Vec4::WHITE - }; + let mut rng = thread_state[0].get_rng(); + let mut point = thread_state[0].point; - accum_image[image_index(i_width, i_height, constants.accum_width)] = color; + let mut transform_weight = 0f32; + for i in 0..transforms.len() { + transform_weight += transforms[i].weight; + } + + // Fuse 20, should be provided by a uniform in the future + for _i in 0..20 { + point = next_transform(&mut rng, transform_weight, transforms).apply(variations, point); + } + + // ...because `.max()` has compilation issues. + let max_dimension = if image_constants.accum_width > image_constants.accum_height { image_constants.accum_width } else { image_constants.accum_height }; + + // Fixed camera, should be provided by a uniform in the future + let camera = CameraConstants { + scale: max_dimension as f32 / 4.0, + zoom: 0.0, + rotate: 0.0, + offset_x: 0.0, + offset_y: 0.0, + } + .camera(image_constants.accum_width, image_constants.accum_height); + + // Iterate 100,000, should be provided by a uniform in the future + for _i in 0..100_000 { + point = next_transform(&mut rng, transform_weight, transforms).apply(variations, point); + + let pixel_coordinates = camera.transform_point2(point).as_ivec2(); + if pixel_coordinates.x < 0 + || pixel_coordinates.x >= image_constants.accum_width + || pixel_coordinates.y < 0 + || pixel_coordinates.y >= image_constants.accum_height + { + continue; } + + let ii = image_index( + pixel_coordinates.x, + pixel_coordinates.y, + image_constants.accum_height, + ); + accum_image[ii as usize] = Color::WHITE; } } @@ -112,7 +310,7 @@ pub fn main_vs( #[spirv(fragment)] pub fn main_fs( #[spirv(frag_coord)] frag_coord: Vec4, - #[spirv(push_constant)] ifs_constants: &IfsConstants, + #[spirv(push_constant)] ifs_constants: &ImageConstants, #[spirv(storage_buffer, descriptor_set = 0, binding = 0)] accum_image: &[Vec4], output: &mut Vec4, ) { @@ -144,9 +342,9 @@ pub fn main_fs( *output = ifs_constants.background_color; } else { *output = accum_image[image_index( - accum_coordinate.x as usize, - accum_coordinate.y as usize, + accum_coordinate.x as i32, + accum_coordinate.y as i32, ifs_constants.accum_width, - )]; + ) as usize]; } } diff --git a/crates/flare/Cargo.toml b/crates/flare/Cargo.toml index 7e6edf7..0c48e2b 100644 --- a/crates/flare/Cargo.toml +++ b/crates/flare/Cargo.toml @@ -12,6 +12,8 @@ env_logger.workspace = true flare-shader = { path = "../flare-shader" } futures-executor.workspace = true glam.workspace = true +rand = { workspace = true, default-features = true } +rand_xoshiro.workspace = true wgpu.workspace = true winit.workspace = true diff --git a/crates/flare/src/main.rs b/crates/flare/src/main.rs index 89ecfa9..bd5395a 100644 --- a/crates/flare/src/main.rs +++ b/crates/flare/src/main.rs @@ -1,12 +1,12 @@ -use flare_shader::{Color, IfsConstants, Transform}; +use flare_shader::{Coefs, Color, ImageConstants, ThreadState, Transform, Variation, VariationKind}; use futures_executor::block_on; use glam::Vec4; +use rand::SeedableRng; +use rand_xoshiro::Xoshiro128Plus; use std::sync::Arc; -use bytemuck::Zeroable; use wgpu::util::DeviceExt; use wgpu::{ - Adapter, BindGroupLayout, Device, Features, Instance, Queue, ShaderModule, Surface, - SurfaceConfiguration, + Adapter, Device, Features, Instance, Queue, ShaderModule, Surface, SurfaceConfiguration, }; use winit::event::MouseButton; use winit::{ @@ -19,11 +19,12 @@ use winit::{ struct AccumulatePipeline { device: Device, - bind_group_layout: BindGroupLayout, - pipeline_layout: wgpu::PipelineLayout, + bind_group_layout: wgpu::BindGroupLayout, pipeline: wgpu::ComputePipeline, + thread_state: Option, transforms: Option, + variations: Option, accum_image: Option, bind_group: Option, } @@ -33,10 +34,32 @@ impl AccumulatePipeline { wgpu::BindGroupLayoutDescriptor { label: Some("accumulate"), entries: &[ - // transforms + // thread_state wgpu::BindGroupLayoutEntry { binding: 0, visibility: wgpu::ShaderStages::COMPUTE, + ty: wgpu::BindingType::Buffer { + ty: wgpu::BufferBindingType::Storage { read_only: false }, + has_dynamic_offset: false, + min_binding_size: None, + }, + count: None, + }, + // transforms + wgpu::BindGroupLayoutEntry { + binding: 1, + visibility: wgpu::ShaderStages::COMPUTE, + ty: wgpu::BindingType::Buffer { + ty: wgpu::BufferBindingType::Storage { read_only: true }, + has_dynamic_offset: false, + min_binding_size: None, + }, + count: None, + }, + // variations + wgpu::BindGroupLayoutEntry { + binding: 2, + visibility: wgpu::ShaderStages::COMPUTE, ty: wgpu::BindingType::Buffer { ty: wgpu::BufferBindingType::Storage { read_only: true }, has_dynamic_offset: false, @@ -46,7 +69,7 @@ impl AccumulatePipeline { }, // accum_image wgpu::BindGroupLayoutEntry { - binding: 1, + binding: 3, visibility: wgpu::ShaderStages::COMPUTE, ty: wgpu::BindingType::Buffer { ty: wgpu::BufferBindingType::Storage { read_only: false }, @@ -58,27 +81,7 @@ impl AccumulatePipeline { ], }; - fn create_bind_group( - device: &Device, - layout: &wgpu::BindGroupLayout, - transforms: &wgpu::Buffer, - accum_image: &wgpu::Buffer, - ) -> wgpu::BindGroup { - device.create_bind_group(&wgpu::BindGroupDescriptor { - label: Some("accumulate"), - layout, - entries: &[ - wgpu::BindGroupEntry { - binding: 0, - resource: transforms.as_entire_binding(), - }, - wgpu::BindGroupEntry { - binding: 1, - resource: accum_image.as_entire_binding(), - }, - ], - }) - } + const N_THREADS: usize = 1; pub fn new(device: &Device, module: &ShaderModule) -> Self { let bind_group_layout = @@ -88,7 +91,7 @@ impl AccumulatePipeline { bind_group_layouts: &[&bind_group_layout], push_constant_ranges: &[wgpu::PushConstantRange { stages: wgpu::ShaderStages::COMPUTE, - range: 0..size_of::() as u32, + range: 0..size_of::() as u32, }], }); let pipeline = device.create_compute_pipeline(&wgpu::ComputePipelineDescriptor { @@ -100,12 +103,27 @@ impl AccumulatePipeline { cache: None, }); + let rng = rand::thread_rng(); + let mut rng_xoshiro = Xoshiro128Plus::from_rng(rng).expect("Unable to seed thread_state"); + let mut thread_state_elements = vec![]; + for _i in 0..Self::N_THREADS { + thread_state_elements.push(ThreadState::new(&mut rng_xoshiro)); + } + let thread_state = Some( + device.create_buffer_init(&wgpu::util::BufferInitDescriptor { + label: Some("accumulate/thread_state"), + contents: bytemuck::cast_slice(&thread_state_elements), + usage: wgpu::BufferUsages::STORAGE, + }), + ); + Self { device: device.clone(), bind_group_layout, - pipeline_layout, pipeline, + thread_state, transforms: None, + variations: None, accum_image: None, bind_group: None, } @@ -126,6 +144,19 @@ impl AccumulatePipeline { self.bind_group.take(); } + pub fn set_variations(&mut self, variations: &[Variation]) { + self.variations = Some( + self.device + .create_buffer_init(&wgpu::util::BufferInitDescriptor { + label: Some("accumulate/variations"), + contents: bytemuck::cast_slice(variations), + usage: wgpu::BufferUsages::STORAGE, + }), + ); + + self.bind_group.take(); + } + pub fn set_accum_image(&mut self, accum_image: &wgpu::Buffer) { self.accum_image = Some(accum_image.clone()); @@ -134,34 +165,49 @@ impl AccumulatePipeline { } fn fetch_bind_group(&mut self) -> &wgpu::BindGroup { - self.bind_group - .get_or_insert_with(|| { - self.device.create_bind_group(&wgpu::BindGroupDescriptor { - label: Some("accumulate"), - layout: &self.bind_group_layout, - entries: &[ - wgpu::BindGroupEntry { - binding: 0, - resource: self - .transforms - .as_ref() - .expect("transforms missing") - .as_entire_binding(), - }, - wgpu::BindGroupEntry { - binding: 1, - resource: self - .accum_image - .as_ref() - .expect("accum_image missing") - .as_entire_binding(), - }, - ], - }) + self.bind_group.get_or_insert_with(|| { + self.device.create_bind_group(&wgpu::BindGroupDescriptor { + label: Some("accumulate"), + layout: &self.bind_group_layout, + entries: &[ + wgpu::BindGroupEntry { + binding: 0, + resource: self + .thread_state + .as_ref() + .expect("thread_state missing") + .as_entire_binding(), + }, + wgpu::BindGroupEntry { + binding: 1, + resource: self + .transforms + .as_ref() + .expect("transforms missing") + .as_entire_binding(), + }, + wgpu::BindGroupEntry { + binding: 2, + resource: self + .variations + .as_ref() + .expect("variations missing") + .as_entire_binding(), + }, + wgpu::BindGroupEntry { + binding: 3, + resource: self + .accum_image + .as_ref() + .expect("accum_image missing") + .as_entire_binding(), + }, + ], }) + }) } - pub fn run(&mut self, encoder: &mut wgpu::CommandEncoder, constants: &IfsConstants) { + pub fn run(&mut self, encoder: &mut wgpu::CommandEncoder, constants: &ImageConstants) { let mut pass = encoder.begin_compute_pass(&wgpu::ComputePassDescriptor { label: Some("accumulate"), timestamp_writes: None, @@ -206,7 +252,7 @@ impl RenderPipeline { bind_group_layouts: &[&bind_group_layout], push_constant_ranges: &[wgpu::PushConstantRange { stages: wgpu::ShaderStages::FRAGMENT, - range: 0..size_of::() as u32, + range: 0..size_of::() as u32, }], }); let pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { @@ -249,28 +295,27 @@ impl RenderPipeline { } fn fetch_bind_group(&mut self) -> &wgpu::BindGroup { - self.bind_group - .get_or_insert_with(|| { - self.device.create_bind_group(&wgpu::BindGroupDescriptor { - label: Some("render"), - layout: &self.bind_group_layout, - entries: &[wgpu::BindGroupEntry { - binding: 0, - resource: self - .accum_image - .as_ref() - .expect("accum_image missing") - .as_entire_binding(), - }], - }) + self.bind_group.get_or_insert_with(|| { + self.device.create_bind_group(&wgpu::BindGroupDescriptor { + label: Some("render"), + layout: &self.bind_group_layout, + entries: &[wgpu::BindGroupEntry { + binding: 0, + resource: self + .accum_image + .as_ref() + .expect("accum_image missing") + .as_entire_binding(), + }], }) + }) } pub fn run( &mut self, encoder: &mut wgpu::CommandEncoder, output_view: &wgpu::TextureView, - constants: &IfsConstants, + constants: &ImageConstants, ) { let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { label: Some("render"), @@ -318,7 +363,7 @@ impl Flare { let adapter = block_on(adapter).expect("Failed to find GPU adapter"); let required_limits = wgpu::Limits { - max_push_constant_size: size_of::() as u32, + max_push_constant_size: size_of::() as u32, ..Default::default() }; @@ -351,7 +396,7 @@ struct FlareRender<'window> { surface_configuration: SurfaceConfiguration, accumulate_pipeline: AccumulatePipeline, render_pipeline: RenderPipeline, - ifs_constants: IfsConstants, + ifs_constants: ImageConstants, accum_image: wgpu::Buffer, } @@ -385,17 +430,17 @@ impl FlareRender<'_> { let mut render_pipeline = RenderPipeline::new(&flare.device, &flare.module, surface_configuration.format); - let ifs_constants = IfsConstants::new( - window_size.width, - window_size.height, - window_size.width, - window_size.height, + let ifs_constants = ImageConstants::new( + window_size.width as i32, + window_size.height as i32, + window_size.width as i32, + window_size.height as i32, Vec4::BLACK, ); - let accum_image = Self::create_accum_image(&flare.device, window_size.width, window_size.height); + let accum_image = + Self::create_accum_image(&flare.device, window_size.width, window_size.height); - accumulate_pipeline.set_transforms(&[Transform::zeroed()]); accumulate_pipeline.set_accum_image(&accum_image); render_pipeline.set_accum_image(&accum_image); @@ -422,9 +467,11 @@ impl FlareRender<'_> { .create_command_encoder(&Default::default()); if run_accumulate { - self.accumulate_pipeline.run(&mut encoder, &self.ifs_constants); + self.accumulate_pipeline + .run(&mut encoder, &self.ifs_constants); } - self.render_pipeline.run(&mut encoder, &output_view, &self.ifs_constants); + self.render_pipeline + .run(&mut encoder, &output_view, &self.ifs_constants); self.flare.queue.submit(Some(encoder.finish())); output.present(); @@ -435,7 +482,8 @@ impl FlareRender<'_> { self.ifs_constants .with_accumulate(vp_dimensions.x, vp_dimensions.y); - let accum_image = Self::create_accum_image(&self.flare.device, vp_dimensions.x, vp_dimensions.y); + let accum_image = + Self::create_accum_image(&self.flare.device, vp_dimensions.x as u32, vp_dimensions.y as u32); self.accumulate_pipeline.set_accum_image(&accum_image); self.render_pipeline.set_accum_image(&accum_image); self.accum_image = accum_image; @@ -446,7 +494,7 @@ impl FlareRender<'_> { self.surface_configuration.height = height; self.surface .configure(&self.flare.device, &self.surface_configuration); - self.ifs_constants.with_viewport(width, height); + self.ifs_constants.with_viewport(width as i32, height as i32); } } @@ -481,6 +529,15 @@ impl ApplicationHandler for Application<'_> { self.window = Some(window.clone()); let mut flare_render = FlareRender::new(self.flare.clone(), window); + + let f0 = Transform::new(Coefs::new(0.5, 0., 0., 0., 0.5, 0.), 0, 1, 1.0); + let f1 = Transform::new(Coefs::new(0.5, 0., 0.5, 0., 0.5, 0.), 0, 1, 1.0); + let f2 = Transform::new(Coefs::new(0.5, 0., 0., 0., 0.5, 0.5), 0, 1, 1.0); + let variation = Variation::new(VariationKind::Linear, 1.0); + + flare_render.accumulate_pipeline.set_transforms(&[f0, f1, f2]); + flare_render.accumulate_pipeline.set_variations(&[variation]); + flare_render.render(true); self.flare_render = Some(flare_render);