Well, it's broken, but I'm making progress

This commit is contained in:
Bradlee Speice 2025-02-02 19:00:21 -05:00
parent 6d02b3dcc1
commit 221d544a01
6 changed files with 440 additions and 120 deletions

59
Cargo.lock generated
View File

@ -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",
]

View File

@ -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 }

View File

@ -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]

View File

@ -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<f32> for BiUnit {
fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> f32 {
rng.sample::<f32, _>(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<R: Rng + ?Sized>(
rng: &mut R,
total_weight: f32,
transforms: &[Transform],
) -> Transform {
let mut weight = rng.sample::<f32, _>(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 `<i32>.max(<i32>)` 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];
}
}

View File

@ -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

View File

@ -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<wgpu::Buffer>,
transforms: Option<wgpu::Buffer>,
variations: Option<wgpu::Buffer>,
accum_image: Option<wgpu::Buffer>,
bind_group: Option<wgpu::BindGroup>,
}
@ -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::<IfsConstants>() as u32,
range: 0..size_of::<ImageConstants>() 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::<IfsConstants>() as u32,
range: 0..size_of::<ImageConstants>() 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::<IfsConstants>() as u32,
max_push_constant_size: size_of::<ImageConstants>() 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);