Well, it's broken, but I'm making progress
This commit is contained in:
parent
6d02b3dcc1
commit
221d544a01
59
Cargo.lock
generated
59
Cargo.lock
generated
@ -260,6 +260,12 @@ dependencies = [
|
|||||||
"syn",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "byteorder"
|
||||||
|
version = "1.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bytes"
|
name = "bytes"
|
||||||
version = "1.9.0"
|
version = "1.9.0"
|
||||||
@ -555,6 +561,8 @@ dependencies = [
|
|||||||
"flare-shader",
|
"flare-shader",
|
||||||
"futures-executor",
|
"futures-executor",
|
||||||
"glam",
|
"glam",
|
||||||
|
"rand",
|
||||||
|
"rand_xoshiro",
|
||||||
"spirv-builder",
|
"spirv-builder",
|
||||||
"wgpu",
|
"wgpu",
|
||||||
"winit",
|
"winit",
|
||||||
@ -566,6 +574,8 @@ version = "0.1.0"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"bytemuck",
|
"bytemuck",
|
||||||
"glam",
|
"glam",
|
||||||
|
"rand",
|
||||||
|
"rand_xoshiro",
|
||||||
"spirv-std",
|
"spirv-std",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -1483,6 +1493,15 @@ dependencies = [
|
|||||||
"windows-sys 0.59.0",
|
"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]]
|
[[package]]
|
||||||
name = "presser"
|
name = "presser"
|
||||||
version = "0.3.1"
|
version = "0.3.1"
|
||||||
@ -1531,6 +1550,45 @@ dependencies = [
|
|||||||
"proc-macro2",
|
"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]]
|
[[package]]
|
||||||
name = "range-alloc"
|
name = "range-alloc"
|
||||||
version = "0.1.4"
|
version = "0.1.4"
|
||||||
@ -2940,6 +2998,7 @@ version = "0.7.35"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0"
|
checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"byteorder",
|
||||||
"zerocopy-derive",
|
"zerocopy-derive",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -20,6 +20,8 @@ bytemuck = { version = "1.21", features = ["derive"] }
|
|||||||
env_logger = "0.11"
|
env_logger = "0.11"
|
||||||
futures-executor = "0.3"
|
futures-executor = "0.3"
|
||||||
glam = { version = "0.29", features = ["bytemuck", "libm"], default-features = false }
|
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-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"}
|
||||||
spirv-builder = { git = "https://github.com/rust-gpu/rust-gpu", rev = "854e9ba7da26d52ca0038ab2c7b252652e4d6b1e", features = ["use-installed-tools"], default-features = false }
|
spirv-builder = { git = "https://github.com/rust-gpu/rust-gpu", rev = "854e9ba7da26d52ca0038ab2c7b252652e4d6b1e", features = ["use-installed-tools"], default-features = false }
|
||||||
|
@ -11,6 +11,8 @@ crate-type = ["dylib", "lib"]
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
bytemuck.workspace = true
|
bytemuck.workspace = true
|
||||||
glam.workspace = true
|
glam.workspace = true
|
||||||
|
rand.workspace = true
|
||||||
|
rand_xoshiro.workspace = true
|
||||||
spirv-std.workspace = true
|
spirv-std.workspace = true
|
||||||
|
|
||||||
[lints]
|
[lints]
|
||||||
|
@ -1,24 +1,29 @@
|
|||||||
#![cfg_attr(target_arch = "spirv", no_std)]
|
#![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::spirv;
|
||||||
|
use spirv_std::num_traits::Float;
|
||||||
|
|
||||||
#[derive(Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)]
|
#[derive(Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)]
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
pub struct IfsConstants {
|
pub struct ImageConstants {
|
||||||
accum_width: u32,
|
accum_width: i32,
|
||||||
accum_height: u32,
|
accum_height: i32,
|
||||||
viewport_width: u32,
|
viewport_width: i32,
|
||||||
viewport_height: u32,
|
viewport_height: i32,
|
||||||
background_color: Vec4,
|
background_color: Vec4,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl IfsConstants {
|
impl ImageConstants {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
accum_width: u32,
|
accum_width: i32,
|
||||||
accum_height: u32,
|
accum_height: i32,
|
||||||
viewport_width: u32,
|
viewport_width: i32,
|
||||||
viewport_height: u32,
|
viewport_height: i32,
|
||||||
background_color: Vec4,
|
background_color: Vec4,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
@ -30,21 +35,48 @@ impl IfsConstants {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn viewport_dimensions(&self) -> UVec2 {
|
pub fn viewport_dimensions(&self) -> glam::IVec2 {
|
||||||
uvec2(self.viewport_width, self.viewport_height)
|
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_width = width;
|
||||||
self.accum_height = height;
|
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_width = width;
|
||||||
self.viewport_height = height;
|
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 {
|
pub trait Color {
|
||||||
type Element;
|
type Element;
|
||||||
const BLACK: Self;
|
const BLACK: Self;
|
||||||
@ -59,37 +91,203 @@ impl Color for Vec4 {
|
|||||||
const WHITE: Self = vec4(1., 1., 1., 1.);
|
const WHITE: Self = vec4(1., 1., 1., 1.);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn image_index(pixel_x: usize, pixel_y: usize, image_width: u32) -> usize {
|
pub(crate) fn image_index(pixel_x: i32, pixel_y: i32, image_width: i32) -> i32 {
|
||||||
pixel_x + pixel_y * image_width as usize
|
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)]
|
#[derive(Copy, Clone, Debug, bytemuck::Pod, bytemuck::Zeroable)]
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
pub struct Transform {
|
pub struct Transform {
|
||||||
|
coefs: Coefs,
|
||||||
variation_offset: u32,
|
variation_offset: u32,
|
||||||
variation_count: u32,
|
variation_count: u32,
|
||||||
weight: f32,
|
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)))]
|
#[spirv(compute(threads(1)))]
|
||||||
pub fn main_cs(
|
pub fn main_cs(
|
||||||
#[spirv(push_constant)] constants: &IfsConstants,
|
#[spirv(push_constant)] image_constants: &ImageConstants,
|
||||||
#[spirv(storage_buffer, descriptor_set = 0, binding = 0)] transforms: &[Transform],
|
#[spirv(storage_buffer, descriptor_set = 0, binding = 0)] thread_state: &mut [ThreadState],
|
||||||
#[spirv(storage_buffer, descriptor_set = 0, binding = 1)] accum_image: &mut [Vec4],
|
#[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;
|
let mut rng = thread_state[0].get_rng();
|
||||||
for i_width in 0..constants.accum_width as usize {
|
let mut point = thread_state[0].point;
|
||||||
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
|
|
||||||
};
|
|
||||||
|
|
||||||
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)]
|
#[spirv(fragment)]
|
||||||
pub fn main_fs(
|
pub fn main_fs(
|
||||||
#[spirv(frag_coord)] frag_coord: Vec4,
|
#[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],
|
#[spirv(storage_buffer, descriptor_set = 0, binding = 0)] accum_image: &[Vec4],
|
||||||
output: &mut Vec4,
|
output: &mut Vec4,
|
||||||
) {
|
) {
|
||||||
@ -144,9 +342,9 @@ pub fn main_fs(
|
|||||||
*output = ifs_constants.background_color;
|
*output = ifs_constants.background_color;
|
||||||
} else {
|
} else {
|
||||||
*output = accum_image[image_index(
|
*output = accum_image[image_index(
|
||||||
accum_coordinate.x as usize,
|
accum_coordinate.x as i32,
|
||||||
accum_coordinate.y as usize,
|
accum_coordinate.y as i32,
|
||||||
ifs_constants.accum_width,
|
ifs_constants.accum_width,
|
||||||
)];
|
) as usize];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -12,6 +12,8 @@ env_logger.workspace = true
|
|||||||
flare-shader = { path = "../flare-shader" }
|
flare-shader = { path = "../flare-shader" }
|
||||||
futures-executor.workspace = true
|
futures-executor.workspace = true
|
||||||
glam.workspace = true
|
glam.workspace = true
|
||||||
|
rand = { workspace = true, default-features = true }
|
||||||
|
rand_xoshiro.workspace = true
|
||||||
wgpu.workspace = true
|
wgpu.workspace = true
|
||||||
winit.workspace = true
|
winit.workspace = true
|
||||||
|
|
||||||
|
@ -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 futures_executor::block_on;
|
||||||
use glam::Vec4;
|
use glam::Vec4;
|
||||||
|
use rand::SeedableRng;
|
||||||
|
use rand_xoshiro::Xoshiro128Plus;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use bytemuck::Zeroable;
|
|
||||||
use wgpu::util::DeviceExt;
|
use wgpu::util::DeviceExt;
|
||||||
use wgpu::{
|
use wgpu::{
|
||||||
Adapter, BindGroupLayout, Device, Features, Instance, Queue, ShaderModule, Surface,
|
Adapter, Device, Features, Instance, Queue, ShaderModule, Surface, SurfaceConfiguration,
|
||||||
SurfaceConfiguration,
|
|
||||||
};
|
};
|
||||||
use winit::event::MouseButton;
|
use winit::event::MouseButton;
|
||||||
use winit::{
|
use winit::{
|
||||||
@ -19,11 +19,12 @@ use winit::{
|
|||||||
|
|
||||||
struct AccumulatePipeline {
|
struct AccumulatePipeline {
|
||||||
device: Device,
|
device: Device,
|
||||||
bind_group_layout: BindGroupLayout,
|
bind_group_layout: wgpu::BindGroupLayout,
|
||||||
pipeline_layout: wgpu::PipelineLayout,
|
|
||||||
pipeline: wgpu::ComputePipeline,
|
pipeline: wgpu::ComputePipeline,
|
||||||
|
|
||||||
|
thread_state: Option<wgpu::Buffer>,
|
||||||
transforms: Option<wgpu::Buffer>,
|
transforms: Option<wgpu::Buffer>,
|
||||||
|
variations: Option<wgpu::Buffer>,
|
||||||
accum_image: Option<wgpu::Buffer>,
|
accum_image: Option<wgpu::Buffer>,
|
||||||
bind_group: Option<wgpu::BindGroup>,
|
bind_group: Option<wgpu::BindGroup>,
|
||||||
}
|
}
|
||||||
@ -33,10 +34,32 @@ impl AccumulatePipeline {
|
|||||||
wgpu::BindGroupLayoutDescriptor {
|
wgpu::BindGroupLayoutDescriptor {
|
||||||
label: Some("accumulate"),
|
label: Some("accumulate"),
|
||||||
entries: &[
|
entries: &[
|
||||||
// transforms
|
// thread_state
|
||||||
wgpu::BindGroupLayoutEntry {
|
wgpu::BindGroupLayoutEntry {
|
||||||
binding: 0,
|
binding: 0,
|
||||||
visibility: wgpu::ShaderStages::COMPUTE,
|
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::BindingType::Buffer {
|
||||||
ty: wgpu::BufferBindingType::Storage { read_only: true },
|
ty: wgpu::BufferBindingType::Storage { read_only: true },
|
||||||
has_dynamic_offset: false,
|
has_dynamic_offset: false,
|
||||||
@ -46,7 +69,7 @@ impl AccumulatePipeline {
|
|||||||
},
|
},
|
||||||
// accum_image
|
// accum_image
|
||||||
wgpu::BindGroupLayoutEntry {
|
wgpu::BindGroupLayoutEntry {
|
||||||
binding: 1,
|
binding: 3,
|
||||||
visibility: wgpu::ShaderStages::COMPUTE,
|
visibility: wgpu::ShaderStages::COMPUTE,
|
||||||
ty: wgpu::BindingType::Buffer {
|
ty: wgpu::BindingType::Buffer {
|
||||||
ty: wgpu::BufferBindingType::Storage { read_only: false },
|
ty: wgpu::BufferBindingType::Storage { read_only: false },
|
||||||
@ -58,27 +81,7 @@ impl AccumulatePipeline {
|
|||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
fn create_bind_group(
|
const N_THREADS: usize = 1;
|
||||||
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(),
|
|
||||||
},
|
|
||||||
],
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn new(device: &Device, module: &ShaderModule) -> Self {
|
pub fn new(device: &Device, module: &ShaderModule) -> Self {
|
||||||
let bind_group_layout =
|
let bind_group_layout =
|
||||||
@ -88,7 +91,7 @@ impl AccumulatePipeline {
|
|||||||
bind_group_layouts: &[&bind_group_layout],
|
bind_group_layouts: &[&bind_group_layout],
|
||||||
push_constant_ranges: &[wgpu::PushConstantRange {
|
push_constant_ranges: &[wgpu::PushConstantRange {
|
||||||
stages: wgpu::ShaderStages::COMPUTE,
|
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 {
|
let pipeline = device.create_compute_pipeline(&wgpu::ComputePipelineDescriptor {
|
||||||
@ -100,12 +103,27 @@ impl AccumulatePipeline {
|
|||||||
cache: None,
|
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 {
|
Self {
|
||||||
device: device.clone(),
|
device: device.clone(),
|
||||||
bind_group_layout,
|
bind_group_layout,
|
||||||
pipeline_layout,
|
|
||||||
pipeline,
|
pipeline,
|
||||||
|
thread_state,
|
||||||
transforms: None,
|
transforms: None,
|
||||||
|
variations: None,
|
||||||
accum_image: None,
|
accum_image: None,
|
||||||
bind_group: None,
|
bind_group: None,
|
||||||
}
|
}
|
||||||
@ -126,6 +144,19 @@ impl AccumulatePipeline {
|
|||||||
self.bind_group.take();
|
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) {
|
pub fn set_accum_image(&mut self, accum_image: &wgpu::Buffer) {
|
||||||
self.accum_image = Some(accum_image.clone());
|
self.accum_image = Some(accum_image.clone());
|
||||||
|
|
||||||
@ -134,14 +165,21 @@ impl AccumulatePipeline {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn fetch_bind_group(&mut self) -> &wgpu::BindGroup {
|
fn fetch_bind_group(&mut self) -> &wgpu::BindGroup {
|
||||||
self.bind_group
|
self.bind_group.get_or_insert_with(|| {
|
||||||
.get_or_insert_with(|| {
|
|
||||||
self.device.create_bind_group(&wgpu::BindGroupDescriptor {
|
self.device.create_bind_group(&wgpu::BindGroupDescriptor {
|
||||||
label: Some("accumulate"),
|
label: Some("accumulate"),
|
||||||
layout: &self.bind_group_layout,
|
layout: &self.bind_group_layout,
|
||||||
entries: &[
|
entries: &[
|
||||||
wgpu::BindGroupEntry {
|
wgpu::BindGroupEntry {
|
||||||
binding: 0,
|
binding: 0,
|
||||||
|
resource: self
|
||||||
|
.thread_state
|
||||||
|
.as_ref()
|
||||||
|
.expect("thread_state missing")
|
||||||
|
.as_entire_binding(),
|
||||||
|
},
|
||||||
|
wgpu::BindGroupEntry {
|
||||||
|
binding: 1,
|
||||||
resource: self
|
resource: self
|
||||||
.transforms
|
.transforms
|
||||||
.as_ref()
|
.as_ref()
|
||||||
@ -149,7 +187,15 @@ impl AccumulatePipeline {
|
|||||||
.as_entire_binding(),
|
.as_entire_binding(),
|
||||||
},
|
},
|
||||||
wgpu::BindGroupEntry {
|
wgpu::BindGroupEntry {
|
||||||
binding: 1,
|
binding: 2,
|
||||||
|
resource: self
|
||||||
|
.variations
|
||||||
|
.as_ref()
|
||||||
|
.expect("variations missing")
|
||||||
|
.as_entire_binding(),
|
||||||
|
},
|
||||||
|
wgpu::BindGroupEntry {
|
||||||
|
binding: 3,
|
||||||
resource: self
|
resource: self
|
||||||
.accum_image
|
.accum_image
|
||||||
.as_ref()
|
.as_ref()
|
||||||
@ -161,7 +207,7 @@ impl AccumulatePipeline {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
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 {
|
let mut pass = encoder.begin_compute_pass(&wgpu::ComputePassDescriptor {
|
||||||
label: Some("accumulate"),
|
label: Some("accumulate"),
|
||||||
timestamp_writes: None,
|
timestamp_writes: None,
|
||||||
@ -206,7 +252,7 @@ impl RenderPipeline {
|
|||||||
bind_group_layouts: &[&bind_group_layout],
|
bind_group_layouts: &[&bind_group_layout],
|
||||||
push_constant_ranges: &[wgpu::PushConstantRange {
|
push_constant_ranges: &[wgpu::PushConstantRange {
|
||||||
stages: wgpu::ShaderStages::FRAGMENT,
|
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 {
|
let pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
|
||||||
@ -249,8 +295,7 @@ impl RenderPipeline {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn fetch_bind_group(&mut self) -> &wgpu::BindGroup {
|
fn fetch_bind_group(&mut self) -> &wgpu::BindGroup {
|
||||||
self.bind_group
|
self.bind_group.get_or_insert_with(|| {
|
||||||
.get_or_insert_with(|| {
|
|
||||||
self.device.create_bind_group(&wgpu::BindGroupDescriptor {
|
self.device.create_bind_group(&wgpu::BindGroupDescriptor {
|
||||||
label: Some("render"),
|
label: Some("render"),
|
||||||
layout: &self.bind_group_layout,
|
layout: &self.bind_group_layout,
|
||||||
@ -270,7 +315,7 @@ impl RenderPipeline {
|
|||||||
&mut self,
|
&mut self,
|
||||||
encoder: &mut wgpu::CommandEncoder,
|
encoder: &mut wgpu::CommandEncoder,
|
||||||
output_view: &wgpu::TextureView,
|
output_view: &wgpu::TextureView,
|
||||||
constants: &IfsConstants,
|
constants: &ImageConstants,
|
||||||
) {
|
) {
|
||||||
let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
|
let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
|
||||||
label: Some("render"),
|
label: Some("render"),
|
||||||
@ -318,7 +363,7 @@ impl Flare {
|
|||||||
let adapter = block_on(adapter).expect("Failed to find GPU adapter");
|
let adapter = block_on(adapter).expect("Failed to find GPU adapter");
|
||||||
|
|
||||||
let required_limits = wgpu::Limits {
|
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()
|
..Default::default()
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -351,7 +396,7 @@ struct FlareRender<'window> {
|
|||||||
surface_configuration: SurfaceConfiguration,
|
surface_configuration: SurfaceConfiguration,
|
||||||
accumulate_pipeline: AccumulatePipeline,
|
accumulate_pipeline: AccumulatePipeline,
|
||||||
render_pipeline: RenderPipeline,
|
render_pipeline: RenderPipeline,
|
||||||
ifs_constants: IfsConstants,
|
ifs_constants: ImageConstants,
|
||||||
accum_image: wgpu::Buffer,
|
accum_image: wgpu::Buffer,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -385,17 +430,17 @@ impl FlareRender<'_> {
|
|||||||
let mut render_pipeline =
|
let mut render_pipeline =
|
||||||
RenderPipeline::new(&flare.device, &flare.module, surface_configuration.format);
|
RenderPipeline::new(&flare.device, &flare.module, surface_configuration.format);
|
||||||
|
|
||||||
let ifs_constants = IfsConstants::new(
|
let ifs_constants = ImageConstants::new(
|
||||||
window_size.width,
|
window_size.width as i32,
|
||||||
window_size.height,
|
window_size.height as i32,
|
||||||
window_size.width,
|
window_size.width as i32,
|
||||||
window_size.height,
|
window_size.height as i32,
|
||||||
Vec4::BLACK,
|
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);
|
accumulate_pipeline.set_accum_image(&accum_image);
|
||||||
render_pipeline.set_accum_image(&accum_image);
|
render_pipeline.set_accum_image(&accum_image);
|
||||||
|
|
||||||
@ -422,9 +467,11 @@ impl FlareRender<'_> {
|
|||||||
.create_command_encoder(&Default::default());
|
.create_command_encoder(&Default::default());
|
||||||
|
|
||||||
if run_accumulate {
|
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()));
|
self.flare.queue.submit(Some(encoder.finish()));
|
||||||
output.present();
|
output.present();
|
||||||
@ -435,7 +482,8 @@ impl FlareRender<'_> {
|
|||||||
self.ifs_constants
|
self.ifs_constants
|
||||||
.with_accumulate(vp_dimensions.x, vp_dimensions.y);
|
.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.accumulate_pipeline.set_accum_image(&accum_image);
|
||||||
self.render_pipeline.set_accum_image(&accum_image);
|
self.render_pipeline.set_accum_image(&accum_image);
|
||||||
self.accum_image = accum_image;
|
self.accum_image = accum_image;
|
||||||
@ -446,7 +494,7 @@ impl FlareRender<'_> {
|
|||||||
self.surface_configuration.height = height;
|
self.surface_configuration.height = height;
|
||||||
self.surface
|
self.surface
|
||||||
.configure(&self.flare.device, &self.surface_configuration);
|
.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());
|
self.window = Some(window.clone());
|
||||||
|
|
||||||
let mut flare_render = FlareRender::new(self.flare.clone(), window);
|
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);
|
flare_render.render(true);
|
||||||
|
|
||||||
self.flare_render = Some(flare_render);
|
self.flare_render = Some(flare_render);
|
||||||
|
Loading…
Reference in New Issue
Block a user