From 7ca267e7842b54603fbaa588dec5673edb6a7cad Mon Sep 17 00:00:00 2001 From: Finn Bear <finnbearlabs@gmail.com> Date: Sun, 8 May 2022 00:02:52 -0700 Subject: [PATCH 1/5] It begins. --- Cargo.lock | 384 +++++++++++++++++++++++++++++++++- Cargo.toml | 5 +- src/main.rs | 2 + src/slide/s6_fractals.rs | 18 +- src/slide/s6_fractals/wgpu.rs | 62 ++++++ 5 files changed, 466 insertions(+), 5 deletions(-) create mode 100644 src/slide/s6_fractals/wgpu.rs diff --git a/Cargo.lock b/Cargo.lock index 4602ed3..0251c52 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -80,6 +80,21 @@ dependencies = [ "num-traits", ] +[[package]] +name = "arrayvec" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8da52d66c7071e2e3fa2a1e5c6d088fec47b593032b254f5e980de8ea54454d6" + +[[package]] +name = "ash" +version = "0.34.0+1.2.203" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0f780da53d0063880d45554306489f09dd8d1bda47688b4a57bc579119356df" +dependencies = [ + "libloading", +] + [[package]] name = "async-broadcast" version = "0.3.4" @@ -195,6 +210,21 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +[[package]] +name = "bit-set" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e11e16035ea35e4e5997b393eacbf6f63983188f7a2ad25bfb13465f5ad59de" +dependencies = [ + "bit-vec", +] + +[[package]] +name = "bit-vec" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" + [[package]] name = "bitflags" version = "1.3.2" @@ -285,6 +315,12 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "cfg_aliases" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e" + [[package]] name = "cgl" version = "0.3.2" @@ -334,6 +370,15 @@ dependencies = [ "winapi", ] +[[package]] +name = "cmake" +version = "0.1.48" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8ad8cef104ac57b68b89df3208164d228503abbdce70f6880ffa3d970e7443a" +dependencies = [ + "cc", +] + [[package]] name = "cocoa" version = "0.24.0" @@ -365,6 +410,16 @@ dependencies = [ "objc", ] +[[package]] +name = "codespan-reporting" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e" +dependencies = [ + "termcolor", + "unicode-width", +] + [[package]] name = "color_quant" version = "1.1.0" @@ -399,6 +454,12 @@ dependencies = [ "custom_derive", ] +[[package]] +name = "copyless" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2df960f5d869b2dd8532793fde43eb5427cceb126c929747a26823ab0eeb536" + [[package]] name = "copypasta" version = "0.7.1" @@ -561,6 +622,17 @@ version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ef8ae57c4978a2acd8b869ce6b9ca1dfe817bff704c220209fdef2c0b75a01b9" +[[package]] +name = "d3d12" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2daefd788d1e96e0a9d66dee4b828b883509bc3ea9ce30665f04c3246372690c" +dependencies = [ + "bitflags", + "libloading", + "winapi", +] + [[package]] name = "dark-light" version = "0.2.2" @@ -970,6 +1042,15 @@ dependencies = [ "slab", ] +[[package]] +name = "fxhash" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c" +dependencies = [ + "byteorder", +] + [[package]] name = "generative_art_cartoon" version = "0.1.0" @@ -978,9 +1059,12 @@ dependencies = [ "egui_demo_lib", "image", "imageproc", + "pollster", "rand 0.8.5", "rand_chacha 0.3.1", + "shaderc", "structopt", + "wgpu", ] [[package]] @@ -1100,6 +1184,45 @@ dependencies = [ "gl_generator", ] +[[package]] +name = "gpu-alloc" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fc59e5f710e310e76e6707f86c561dd646f69a8876da9131703b2f717de818d" +dependencies = [ + "bitflags", + "gpu-alloc-types", +] + +[[package]] +name = "gpu-alloc-types" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54804d0d6bc9d7f26db4eaec1ad10def69b599315f487d32c334a80d1efe67a5" +dependencies = [ + "bitflags", +] + +[[package]] +name = "gpu-descriptor" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a538f217be4d405ff4719a283ca68323cc2384003eca5baaa87501e821c81dda" +dependencies = [ + "bitflags", + "gpu-descriptor-types", + "hashbrown 0.11.2", +] + +[[package]] +name = "gpu-descriptor-types" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "363e3677e55ad168fef68cf9de3a4a310b53124c5e784c53a1d70e92d23f2126" +dependencies = [ + "bitflags", +] + [[package]] name = "hashbrown" version = "0.9.1" @@ -1109,6 +1232,15 @@ dependencies = [ "ahash 0.4.7", ] +[[package]] +name = "hashbrown" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" +dependencies = [ + "ahash 0.7.6", +] + [[package]] name = "heck" version = "0.3.3" @@ -1133,6 +1265,12 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" +[[package]] +name = "hexf-parse" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfa686283ad6dd069f105e5ab091b04c62850d3e4cf5d67debad1933f55023df" + [[package]] name = "ident_case" version = "1.0.1" @@ -1182,6 +1320,22 @@ dependencies = [ "rusttype", ] +[[package]] +name = "indexmap" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f647032dfaa1f8b6dc29bd3edb7bbef4861b8b8007ebb118d6db284fd59f6ee" +dependencies = [ + "autocfg", + "hashbrown 0.11.2", +] + +[[package]] +name = "inplace_it" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90953f308a79fe6d62a4643e51f848fbfddcd05975a38e69fdf4ab86a7baf7ca" + [[package]] name = "instant" version = "0.1.12" @@ -1232,6 +1386,16 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "khronos-egl" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c2352bd1d0bceb871cb9d40f24360c8133c11d7486b68b5381c1dd1a32015e3" +dependencies = [ + "libc", + "libloading", +] + [[package]] name = "khronos_api" version = "3.1.0" @@ -1333,6 +1497,20 @@ dependencies = [ "autocfg", ] +[[package]] +name = "metal" +version = "0.23.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0514f491f4cc03632ab399ee01e2c1c1b12d3e1cf2d667c1ff5f87d6dcd2084" +dependencies = [ + "bitflags", + "block", + "core-graphics-types", + "foreign-types", + "log", + "objc", +] + [[package]] name = "minimal-lexical" version = "0.2.1" @@ -1371,6 +1549,24 @@ dependencies = [ "winapi", ] +[[package]] +name = "naga" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3012f2dbcc79e8e0b5825a4836a7106a75dd9b2fe42c528163be0f572538c705" +dependencies = [ + "bit-set", + "bitflags", + "codespan-reporting", + "hexf-parse", + "indexmap", + "log", + "num-traits", + "rustc-hash", + "spirv", + "thiserror", +] + [[package]] name = "nalgebra" version = "0.30.1" @@ -1641,6 +1837,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "915b1b472bc21c53464d6c8461c9d3af805ba1ef837e1cac254428f4a77177b1" dependencies = [ "malloc_buf", + "objc_exception", ] [[package]] @@ -1654,6 +1851,15 @@ dependencies = [ "objc_id", ] +[[package]] +name = "objc_exception" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad970fb455818ad6cba4c122ad012fae53ae8b4795f86378bce65e4f6bab2ca4" +dependencies = [ + "cc", +] + [[package]] name = "objc_id" version = "0.1.1" @@ -1676,7 +1882,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1c672c7ad9ec066e428c00eb917124a06f08db19e2584de982cc34b1f4c12485" dependencies = [ "dlv-list", - "hashbrown", + "hashbrown 0.9.1", ] [[package]] @@ -1802,6 +2008,12 @@ dependencies = [ "winapi", ] +[[package]] +name = "pollster" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5da3b0203fd7ee5720aa0b5e790b591aa5d3f41c3ed2c34a3a393382198af2f7" + [[package]] name = "ppv-lite86" version = "0.2.16" @@ -1851,6 +2063,12 @@ dependencies = [ "unicode-xid", ] +[[package]] +name = "profiling" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9145ac0af1d93c638c98c40cf7d25665f427b2a44ad0a99b1dccf3e2f25bb987" + [[package]] name = "quick-xml" version = "0.22.0" @@ -1949,6 +2167,12 @@ dependencies = [ "rand_core 0.5.1", ] +[[package]] +name = "range-alloc" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63e935c45e09cc6dcf00d2f0b2d630a58f4095320223d47fc68918722f0538b6" + [[package]] name = "raw-window-handle" version = "0.4.3" @@ -2025,6 +2249,21 @@ version = "0.6.25" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b" +[[package]] +name = "renderdoc-sys" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1382d1f0a252c4bf97dc20d979a2fdd05b024acd7c2ed0f7595d7817666a157" + +[[package]] +name = "roxmltree" +version = "0.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "921904a62e410e37e215c40381b7117f830d9d89ba60ab5236170541dd25646b" +dependencies = [ + "xmlparser", +] + [[package]] name = "rust-ini" version = "0.17.0" @@ -2035,6 +2274,12 @@ dependencies = [ "ordered-multimap", ] +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + [[package]] name = "rusttype" version = "0.9.2" @@ -2121,6 +2366,27 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ae1a47186c03a32177042e55dbc5fd5aee900b8e0069a8d70fba96a9375cd012" +[[package]] +name = "shaderc" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80e6fe602a861622769530a23bc40bfba31adbf186d0c8412e83f5519c5d6bee" +dependencies = [ + "libc", + "shaderc-sys", +] + +[[package]] +name = "shaderc-sys" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3794498651f8173d0afbc0bb8aca45ced111098227e755dde4c0ef2888c8d0bf" +dependencies = [ + "cmake", + "libc", + "roxmltree", +] + [[package]] name = "shared_library" version = "0.1.9" @@ -2204,6 +2470,16 @@ dependencies = [ "winapi", ] +[[package]] +name = "spirv" +version = "0.2.0+1.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "246bfa38fe3db3f1dfc8ca5a2cdeb7348c78be2112740cc0ec8ef18b6d94f830" +dependencies = [ + "bitflags", + "num-traits", +] + [[package]] name = "static_assertions" version = "1.1.0" @@ -2257,6 +2533,15 @@ dependencies = [ "unicode-xid", ] +[[package]] +name = "termcolor" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755" +dependencies = [ + "winapi-util", +] + [[package]] name = "textwrap" version = "0.11.0" @@ -2650,6 +2935,97 @@ dependencies = [ "cc", ] +[[package]] +name = "wgpu" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b97cd781ff044d6d697b632a2e212032c2e957d1afaa21dbf58069cbb8f78567" +dependencies = [ + "arrayvec", + "js-sys", + "log", + "naga", + "parking_lot", + "raw-window-handle", + "smallvec", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "wgpu-core", + "wgpu-hal", + "wgpu-types", +] + +[[package]] +name = "wgpu-core" +version = "0.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4688c000eb841ca55f7b35db659b78d6e1cd77d7caf8fb929f4e181f754047d" +dependencies = [ + "arrayvec", + "bitflags", + "cfg_aliases", + "codespan-reporting", + "copyless", + "fxhash", + "log", + "naga", + "parking_lot", + "profiling", + "raw-window-handle", + "smallvec", + "thiserror", + "wgpu-hal", + "wgpu-types", +] + +[[package]] +name = "wgpu-hal" +version = "0.12.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d684ea6a34974a2fc19f1dfd183d11a62e22d75c4f187a574bb1224df8e056c2" +dependencies = [ + "arrayvec", + "ash", + "bit-set", + "bitflags", + "block", + "core-graphics-types", + "d3d12", + "foreign-types", + "fxhash", + "glow", + "gpu-alloc", + "gpu-descriptor", + "inplace_it", + "js-sys", + "khronos-egl", + "libloading", + "log", + "metal", + "naga", + "objc", + "parking_lot", + "profiling", + "range-alloc", + "raw-window-handle", + "renderdoc-sys", + "thiserror", + "wasm-bindgen", + "web-sys", + "wgpu-types", + "winapi", +] + +[[package]] +name = "wgpu-types" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "549533d9e1cdd4b4cda7718d33ff500fc4c34b5467b71d76b547ae0324f3b2a2" +dependencies = [ + "bitflags", +] + [[package]] name = "wide" version = "0.7.4" @@ -2785,6 +3161,12 @@ version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2d7d3948613f75c98fd9328cfdcc45acc4d360655289d0a7d4ec931392200a3" +[[package]] +name = "xmlparser" +version = "0.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "114ba2b24d2167ef6d67d7d04c8cc86522b87f490025f39f0303b7db5bf5e3d8" + [[package]] name = "zbus" version = "2.1.1" diff --git a/Cargo.toml b/Cargo.toml index 6770c87..34448f7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,4 +10,7 @@ image = {version = "0.24", default-features = false, features=["png"]} imageproc = {git = "https://github.com/finnbear/imageproc", branch = "draw_filled_polygon"} rand = "0.8" rand_chacha = "0.3" -structopt = "0.3" \ No newline at end of file +structopt = "0.3" +wgpu = "0.12" +pollster = "0.2" +shaderc = "0.8" \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index d3ff066..f9e5dc0 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,6 +2,8 @@ #![feature(mixed_integer_ops)] #![feature(total_cmp)] #![feature(int_abs_diff)] +// For shaderc +#![feature(format_args_capture)] pub mod cartoon; pub mod color; diff --git a/src/slide/s6_fractals.rs b/src/slide/s6_fractals.rs index 1657fd2..d13e468 100644 --- a/src/slide/s6_fractals.rs +++ b/src/slide/s6_fractals.rs @@ -4,12 +4,13 @@ mod julia_gradient; mod mandelbrot; mod mandelbrot_gradient; mod rectangle; +mod wgpu; use crate::color::set_alpha; use crate::component::arrow::Arrow; use crate::component::code::{pseudocode, Code}; use crate::component::grid::Grid; -use crate::egui::{Color32, Context, Frame, Pos2, Shape, Stroke, Ui}; +use crate::egui::{Color32, Context, Frame, Pos2, Rect, Shape, Stroke, Ui}; use crate::fade_in::{fade_in, fade_in_manual, fade_out_manual}; use crate::governor::Governor; use crate::slide::s4_automata::randomize_grid; @@ -22,10 +23,12 @@ use crate::slide::s6_fractals::rectangle::rectangle; use crate::slide::Slide; use crate::window::WindowPosition; use eframe::egui::style::Margin; -use eframe::egui::{Align2, Vec2}; +use eframe::egui::{Align2, ColorImage, Vec2}; use eframe::epaint::CircleShape; use std::iter; use std::ops::{Bound, RangeBounds}; +use crate::component::image::Image; +use crate::egui; /// Mandelbrot, etc. pub struct Fractals { @@ -84,6 +87,10 @@ enum FractalsState { /// How many pixels of the grid we've filled in. pixels: usize, }, + MandelbrotZoom { + /// Fractal zoom background image. + image: ColorImage, + } } impl Default for Fractals { @@ -163,7 +170,8 @@ impl Slide for Fractals { FractalsState::MandelbrotGradient { .. } => { self.state = FractalsState::JuliaGradient { pixels: 0 } } - FractalsState::JuliaGradient { .. } => return true, + FractalsState::JuliaGradient { .. } => self.state = FractalsState::MandelbrotZoom {image: ColorImage::new([1280, 720], Color32::RED)}, + FractalsState::MandelbrotZoom {..} => return true, } false } @@ -280,6 +288,10 @@ impl Slide for Fractals { algo = Box::new(wrap_f32_algo(julia_gradient)); limit_pixels = Some(pixels); } + FractalsState::MandelbrotZoom {image} => { + let screen = ui.ctx().input().screen_rect(); + egui::Image::new(&ui.ctx().load_texture("zoom", image.clone()), Vec2::ZERO).paint_at(ui, screen); + } } // Which pixels the algorithm should render. diff --git a/src/slide/s6_fractals/wgpu.rs b/src/slide/s6_fractals/wgpu.rs new file mode 100644 index 0000000..729b0e0 --- /dev/null +++ b/src/slide/s6_fractals/wgpu.rs @@ -0,0 +1,62 @@ +use wgpu::{Backends, ComputePipeline, ComputePipelineDescriptor, Device, Instance, Queue}; + +struct Accelerator { + instance: Instance, + device: Device, + queue: Queue, + pipeline: ComputePipeline, +} + +impl Accelerator { + pub fn new() -> Self { + let instance = Instance::new(Backends::all()); + let adapter = pollster::block_on(instance + .request_adapter(&wgpu::RequestAdapterOptions { + power_preference: wgpu::PowerPreference::HighPerformance, + force_fallback_adapter: false, + compatible_surface: None, + })) + .unwrap(); + let (device, queue) = pollster::block_on(adapter + .request_device(&Default::default(), None)) + .unwrap(); + let compute_module = device.create_shader_module(&wgpu::ShaderModuleDescriptor { + label: Some("accelerator shader"), + source: vs_data, + }); + let compute_src = include_str!("compute.glsl"); + let mut compiler = shaderc::Compiler::new().unwrap(); + let compute_spirv = compiler + .compile_into_spirv( + vs_src, + shaderc::ShaderKind::, + "compute.glsl", + "main", + None, + ) + .unwrap(); + let fs_spirv = compiler + .compile_into_spirv( + fs_src, + shaderc::ShaderKind::Fragment, + "shader.frag", + "main", + None, + ) + .unwrap(); + let vs_data = wgpu::util::make_spirv(vs_spirv.as_binary_u8()); + let fs_data = wgpu::util::make_spirv(fs_spirv.as_binary_u8()); + let pipeline = device.create_compute_pipeline(&ComputePipelineDescriptor{ + label: Some("accelerator pipeline"), + layout: None, + module: &compute_module, + entry_point: "main" + }); + Self{ + instance, + device, + queue, + pipeline, + } + } +} \ No newline at end of file -- GitLab From 08d72460be06d8c441ce9a2ec94d251cc90318c3 Mon Sep 17 00:00:00 2001 From: Finn Bear <finnbearlabs@gmail.com> Date: Sun, 8 May 2022 11:28:08 -0700 Subject: [PATCH 2/5] Compute shader progress. --- Cargo.lock | 47 +---- Cargo.toml | 2 +- src/main.rs | 5 - src/slide/s6_fractals.rs | 45 ++++- src/slide/s6_fractals/compute.wgsl | 51 +++++ src/slide/s6_fractals/wgpu.rs | 289 ++++++++++++++++++++++++----- 6 files changed, 334 insertions(+), 105 deletions(-) create mode 100644 src/slide/s6_fractals/compute.wgsl diff --git a/Cargo.lock b/Cargo.lock index 0251c52..e017503 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -370,15 +370,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "cmake" -version = "0.1.48" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8ad8cef104ac57b68b89df3208164d228503abbdce70f6880ffa3d970e7443a" -dependencies = [ - "cc", -] - [[package]] name = "cocoa" version = "0.24.0" @@ -1055,6 +1046,7 @@ dependencies = [ name = "generative_art_cartoon" version = "0.1.0" dependencies = [ + "bytemuck", "eframe", "egui_demo_lib", "image", @@ -1062,7 +1054,6 @@ dependencies = [ "pollster", "rand 0.8.5", "rand_chacha 0.3.1", - "shaderc", "structopt", "wgpu", ] @@ -2255,15 +2246,6 @@ version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f1382d1f0a252c4bf97dc20d979a2fdd05b024acd7c2ed0f7595d7817666a157" -[[package]] -name = "roxmltree" -version = "0.14.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "921904a62e410e37e215c40381b7117f830d9d89ba60ab5236170541dd25646b" -dependencies = [ - "xmlparser", -] - [[package]] name = "rust-ini" version = "0.17.0" @@ -2366,27 +2348,6 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ae1a47186c03a32177042e55dbc5fd5aee900b8e0069a8d70fba96a9375cd012" -[[package]] -name = "shaderc" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80e6fe602a861622769530a23bc40bfba31adbf186d0c8412e83f5519c5d6bee" -dependencies = [ - "libc", - "shaderc-sys", -] - -[[package]] -name = "shaderc-sys" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3794498651f8173d0afbc0bb8aca45ced111098227e755dde4c0ef2888c8d0bf" -dependencies = [ - "cmake", - "libc", - "roxmltree", -] - [[package]] name = "shared_library" version = "0.1.9" @@ -3161,12 +3122,6 @@ version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2d7d3948613f75c98fd9328cfdcc45acc4d360655289d0a7d4ec931392200a3" -[[package]] -name = "xmlparser" -version = "0.13.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "114ba2b24d2167ef6d67d7d04c8cc86522b87f490025f39f0303b7db5bf5e3d8" - [[package]] name = "zbus" version = "2.1.1" diff --git a/Cargo.toml b/Cargo.toml index 34448f7..54fe911 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,4 +13,4 @@ rand_chacha = "0.3" structopt = "0.3" wgpu = "0.12" pollster = "0.2" -shaderc = "0.8" \ No newline at end of file +bytemuck = "1.9" \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index f9e5dc0..f90c788 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,9 +1,4 @@ -#![feature(derive_default_enum)] #![feature(mixed_integer_ops)] -#![feature(total_cmp)] -#![feature(int_abs_diff)] -// For shaderc -#![feature(format_args_capture)] pub mod cartoon; pub mod color; diff --git a/src/slide/s6_fractals.rs b/src/slide/s6_fractals.rs index d13e468..cdb425e 100644 --- a/src/slide/s6_fractals.rs +++ b/src/slide/s6_fractals.rs @@ -10,7 +10,8 @@ use crate::color::set_alpha; use crate::component::arrow::Arrow; use crate::component::code::{pseudocode, Code}; use crate::component::grid::Grid; -use crate::egui::{Color32, Context, Frame, Pos2, Rect, Shape, Stroke, Ui}; +use crate::egui; +use crate::egui::{Color32, Context, Frame, Pos2, Shape, Stroke, Ui}; use crate::fade_in::{fade_in, fade_in_manual, fade_out_manual}; use crate::governor::Governor; use crate::slide::s4_automata::randomize_grid; @@ -20,6 +21,7 @@ use crate::slide::s6_fractals::julia_gradient::julia as julia_gradient; use crate::slide::s6_fractals::mandelbrot::mandelbrot; use crate::slide::s6_fractals::mandelbrot_gradient::mandelbrot as mandelbrot_gradient; use crate::slide::s6_fractals::rectangle::rectangle; +use crate::slide::s6_fractals::wgpu::Accelerator; use crate::slide::Slide; use crate::window::WindowPosition; use eframe::egui::style::Margin; @@ -27,8 +29,6 @@ use eframe::egui::{Align2, ColorImage, Vec2}; use eframe::epaint::CircleShape; use std::iter; use std::ops::{Bound, RangeBounds}; -use crate::component::image::Image; -use crate::egui; /// Mandelbrot, etc. pub struct Fractals { @@ -90,7 +90,10 @@ enum FractalsState { MandelbrotZoom { /// Fractal zoom background image. image: ColorImage, - } + /// GPU accelerator. + accelerator: Accelerator, + zoom: f32, + }, } impl Default for Fractals { @@ -116,7 +119,11 @@ impl Default for Fractals { grid.stroke_width = 0.0; Self { - state: FractalsState::default(), + state: FractalsState::MandelbrotZoom { + image: ColorImage::new([1280, 720], Color32::RED), + accelerator: Accelerator::new(1280, 720), + zoom: 1.0, + }, //FractalsState::default(), governor: Governor::default(), code, grid, @@ -170,8 +177,14 @@ impl Slide for Fractals { FractalsState::MandelbrotGradient { .. } => { self.state = FractalsState::JuliaGradient { pixels: 0 } } - FractalsState::JuliaGradient { .. } => self.state = FractalsState::MandelbrotZoom {image: ColorImage::new([1280, 720], Color32::RED)}, - FractalsState::MandelbrotZoom {..} => return true, + FractalsState::JuliaGradient { .. } => { + self.state = FractalsState::MandelbrotZoom { + image: ColorImage::new([1280, 720], Color32::RED), + accelerator: Accelerator::new(1280, 720), + zoom: 1.0, + } + } + FractalsState::MandelbrotZoom { .. } => return true, } false } @@ -288,9 +301,23 @@ impl Slide for Fractals { algo = Box::new(wrap_f32_algo(julia_gradient)); limit_pixels = Some(pixels); } - FractalsState::MandelbrotZoom {image} => { + FractalsState::MandelbrotZoom { + image, + accelerator, + zoom, + } => { let screen = ui.ctx().input().screen_rect(); - egui::Image::new(&ui.ctx().load_texture("zoom", image.clone()), Vec2::ZERO).paint_at(ui, screen); + *zoom *= 0.9; + accelerator.run( + Pos2::new( + 0.3602404434376143632361252444495, + -0.6413130610648031748603750151, + ), + *zoom, + image, + ); + egui::Image::new(&ui.ctx().load_texture("zoom", image.clone()), Vec2::ZERO) + .paint_at(ui, screen); } } diff --git a/src/slide/s6_fractals/compute.wgsl b/src/slide/s6_fractals/compute.wgsl new file mode 100644 index 0000000..008bb5a --- /dev/null +++ b/src/slide/s6_fractals/compute.wgsl @@ -0,0 +1,51 @@ +struct ComputeParams { + width: u32; + height: u32; + center_x: f32; + center_y: f32; + zoom: f32; +}; + +[[group(0), binding(0)]] var<uniform> params: ComputeParams; +[[group(0), binding(1)]] var texture: [[stride(16)]] texture_storage_2d<rgba8unorm, write>; + +fn mandelbrot(x: f32, y: f32) -> f32 { + let max: f32 = 80.0; + var x1: f32 = 0.0; + var y1: f32 = 0.0; + var i: f32 = 0.0; + + loop { + if (x1 * x1 + y1 * y1 > 4.0 || i > max) { + break; + } + + let x_tmp: f32 = x1 * x1 - y1 * y1 + x; + y1 = 2.0 * x1 * y1 + y; + x1 = x_tmp; + i = i + 1.0; + } + + return i / max; +} + +[[stage(compute), workgroup_size(8, 8)]] +fn main([[builtin(global_invocation_id)]] global_id: vec3<u32>) { + let X: u32 = global_id.x; + let Y: u32 = global_id.y; + let W: u32 = params.width; + let H: u32 = params.height; + + if (X >= W || Y >= H) { + return; + } + + let aspect = f32(W) / f32(H); + let nx = (f32(X) / f32(W) - 0.5) * 2.0; + let ny = (f32(Y) / f32(H) - 0.5) * 2.0; + let fx = params.center_x + nx * params.zoom * aspect; + let fy = params.center_y + ny * params.zoom; + let iter = mandelbrot(fx, fy); + + textureStore(texture, vec2<i32>(i32(X), i32(Y)), vec4<f32>(iter, iter, iter, 1.0)); +} \ No newline at end of file diff --git a/src/slide/s6_fractals/wgpu.rs b/src/slide/s6_fractals/wgpu.rs index 729b0e0..45ffa58 100644 --- a/src/slide/s6_fractals/wgpu.rs +++ b/src/slide/s6_fractals/wgpu.rs @@ -1,62 +1,263 @@ -use wgpu::{Backends, ComputePipeline, ComputePipelineDescriptor, Device, Instance, Queue}; +use bytemuck::{Pod, Zeroable}; +use eframe::egui::{ColorImage, Pos2}; +use std::borrow::Cow; +use std::mem; +use std::num::NonZeroU32; +use wgpu::{ + Backends, BindGroup, Buffer, BufferDescriptor, ComputePipeline, ComputePipelineDescriptor, + Device, Extent3d, Instance, Queue, StorageTextureAccess, Texture, TextureDimension, + TextureFormat, TextureUsages, +}; -struct Accelerator { - instance: Instance, +/// Accelerates mandelbrot rendering. +pub struct Accelerator { + width: u32, + height: u32, device: Device, + params: ComputeParams, queue: Queue, + texture: Texture, + output_buffer: Buffer, pipeline: ComputePipeline, + bind_group: BindGroup, } impl Accelerator { - pub fn new() -> Self { - let instance = Instance::new(Backends::all()); - let adapter = pollster::block_on(instance - .request_adapter(&wgpu::RequestAdapterOptions { - power_preference: wgpu::PowerPreference::HighPerformance, - force_fallback_adapter: false, - compatible_surface: None, - })) - .unwrap(); - let (device, queue) = pollster::block_on(adapter - .request_device(&Default::default(), None)) - .unwrap(); + pub fn new(width: u32, height: u32) -> Self { + let instance = Instance::new(Backends::VULKAN); + let adapter = pollster::block_on(instance.request_adapter(&wgpu::RequestAdapterOptions { + power_preference: wgpu::PowerPreference::HighPerformance, + force_fallback_adapter: false, + compatible_surface: None, + })) + .unwrap(); + let (device, queue) = + pollster::block_on(adapter.request_device(&Default::default(), None)).unwrap(); let compute_module = device.create_shader_module(&wgpu::ShaderModuleDescriptor { label: Some("accelerator shader"), - source: vs_data, + source: wgpu::ShaderSource::Wgsl(Cow::Borrowed(include_str!("compute.wgsl"))), }); - let compute_src = include_str!("compute.glsl"); - let mut compiler = shaderc::Compiler::new().unwrap(); - let compute_spirv = compiler - .compile_into_spirv( - vs_src, - shaderc::ShaderKind::, - "compute.glsl", - "main", - None, - ) - .unwrap(); - let fs_spirv = compiler - .compile_into_spirv( - fs_src, - shaderc::ShaderKind::Fragment, - "shader.frag", - "main", - None, - ) - .unwrap(); - let vs_data = wgpu::util::make_spirv(vs_spirv.as_binary_u8()); - let fs_data = wgpu::util::make_spirv(fs_spirv.as_binary_u8()); - let pipeline = device.create_compute_pipeline(&ComputePipelineDescriptor{ + + let format = TextureFormat::Rgba8Unorm; + let texture = device.create_texture(&wgpu::TextureDescriptor { + label: None, + size: wgpu::Extent3d { + width, + height, + depth_or_array_layers: 1, + }, + mip_level_count: 1, + sample_count: 1, + dimension: TextureDimension::D2, + format, + usage: TextureUsages::COPY_SRC | TextureUsages::STORAGE_BINDING, + }); + let texture_view = texture.create_view(&wgpu::TextureViewDescriptor::default()); + + let params = ComputeParams::new(&device); + + // Create the bind group layout and compute pipeline for the life algorithm. + let bind_group_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { + entries: &[ + wgpu::BindGroupLayoutEntry { + binding: 0, + visibility: wgpu::ShaderStages::COMPUTE, + ty: params.binding_type(), + count: None, + }, + wgpu::BindGroupLayoutEntry { + binding: 1, + visibility: wgpu::ShaderStages::COMPUTE, + ty: wgpu::BindingType::StorageTexture { + access: StorageTextureAccess::WriteOnly, + format, + view_dimension: wgpu::TextureViewDimension::D2, + }, + count: None, + }, + ], + label: None, + }); + + let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { + label: Some("accelerator pipeline layout"), + bind_group_layouts: &[&bind_group_layout], + push_constant_ranges: &[], + }); + + let pipeline = device.create_compute_pipeline(&ComputePipelineDescriptor { label: Some("accelerator pipeline"), - layout: None, + layout: Some(&pipeline_layout), module: &compute_module, - entry_point: "main" + entry_point: "main", + }); + + let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor { + layout: &bind_group_layout, + entries: &[ + wgpu::BindGroupEntry { + binding: 0, + resource: params.binding_resource(), + }, + wgpu::BindGroupEntry { + binding: 1, + resource: wgpu::BindingResource::TextureView(&texture_view), + }, + ], + label: None, }); - Self{ - instance, + + let output_buffer_size = + (mem::size_of::<u32>() as u32 * width * height) as wgpu::BufferAddress; + let output_buffer_desc = wgpu::BufferDescriptor { + size: output_buffer_size, + usage: wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::MAP_READ, + label: None, + mapped_at_creation: false, + }; + let output_buffer = device.create_buffer(&output_buffer_desc); + + Self { + width, + height, device, queue, + params, + texture, + output_buffer, pipeline, + bind_group, + } + } + + pub fn run(&mut self, center: Pos2, zoom: f32, output: &mut ColorImage) { + self.params.set( + &self.device, + Params { + width: self.width, + height: self.height, + center_x: center.x, + center_y: center.y, + zoom, + }, + ); + + // Must match compute.wgsl. + const WORKGROUP_SIZE: (u32, u32) = (8, 8); + + let mut command_encoder = + self.device + .create_command_encoder(&wgpu::CommandEncoderDescriptor { + label: Some("accelerator command encoder"), + }); + + { + let mut compute = command_encoder.begin_compute_pass(&wgpu::ComputePassDescriptor { + label: Some("accelerator run"), + }); + + let x_dim = self.width + WORKGROUP_SIZE.0 - 1; + let x_groups = x_dim / WORKGROUP_SIZE.0; + let y_dim = self.height + WORKGROUP_SIZE.1 - 1; + let y_groups = y_dim / WORKGROUP_SIZE.1; + + compute.set_pipeline(&self.pipeline); + compute.set_bind_group(0, &self.bind_group, &[]); + compute.dispatch(x_groups, y_groups, 1); + } + + command_encoder.copy_texture_to_buffer( + wgpu::ImageCopyTexture { + aspect: wgpu::TextureAspect::All, + texture: &self.texture, + mip_level: 0, + origin: wgpu::Origin3d::ZERO, + }, + wgpu::ImageCopyBuffer { + buffer: &self.output_buffer, + layout: wgpu::ImageDataLayout { + offset: 0, + bytes_per_row: NonZeroU32::new(mem::size_of::<u32>() as u32 * self.width), + rows_per_image: NonZeroU32::new(self.height), + }, + }, + Extent3d { + width: self.width, + height: self.height, + depth_or_array_layers: 1, + }, + ); + + self.queue.submit(Some(command_encoder.finish())); + + { + let buffer_slice = self.output_buffer.slice(..); + + let mapping = buffer_slice.map_async(wgpu::MapMode::Read); + self.device.poll(wgpu::Maintain::Wait); + pollster::block_on(mapping).unwrap(); + + let data = buffer_slice.get_mapped_range(); + + *output = ColorImage::from_rgba_unmultiplied( + [self.width as usize, self.height as usize], + data.as_ref(), + ); } + self.output_buffer.unmap(); } -} \ No newline at end of file +} + +#[repr(C)] +#[derive(Clone, Copy, Pod, Zeroable)] +struct Params { + width: u32, + height: u32, + center_x: f32, + center_y: f32, + zoom: f32, +} + +struct ComputeParams { + buffer: wgpu::Buffer, +} + +impl ComputeParams { + pub fn new(device: &wgpu::Device) -> Self { + let buffer = device.create_buffer(&BufferDescriptor { + label: Some("params buffer"), + size: mem::size_of::<Params>() as _, + usage: wgpu::BufferUsages::UNIFORM + | wgpu::BufferUsages::COPY_DST + | wgpu::BufferUsages::MAP_WRITE, + mapped_at_creation: false, + }); + + Self { buffer } + } + + pub fn set(&mut self, device: &wgpu::Device, params: Params) { + let buffer_slice = self.buffer.slice(..); + + let mapping = buffer_slice.map_async(wgpu::MapMode::Write); + device.poll(wgpu::Maintain::Wait); + pollster::block_on(mapping).unwrap(); + + buffer_slice + .get_mapped_range_mut() + .copy_from_slice(bytemuck::bytes_of(¶ms)); + self.buffer.unmap(); + } + + pub fn binding_resource(&self) -> wgpu::BindingResource { + self.buffer.as_entire_binding() + } + + pub fn binding_type(&self) -> wgpu::BindingType { + wgpu::BindingType::Buffer { + ty: wgpu::BufferBindingType::Uniform, + has_dynamic_offset: false, + min_binding_size: wgpu::BufferSize::new(mem::size_of::<Params>() as _), + } + } +} -- GitLab From 5f52f42e3b595465ea5c3ee7560adedc5cc0fea0 Mon Sep 17 00:00:00 2001 From: Finn Bear <finnbearlabs@gmail.com> Date: Sun, 8 May 2022 12:17:38 -0700 Subject: [PATCH 3/5] Fractal zoom. --- src/fade_in.rs | 15 ++++++++ src/slide/s6_fractals.rs | 59 +++++++++++++++++++----------- src/slide/s6_fractals/compute.wgsl | 20 +++++++--- 3 files changed, 66 insertions(+), 28 deletions(-) diff --git a/src/fade_in.rs b/src/fade_in.rs index 9f8b68d..377c861 100644 --- a/src/fade_in.rs +++ b/src/fade_in.rs @@ -33,6 +33,21 @@ pub fn fade_in_manual<R>( add_contents(ui, alpha) } +/// Fade out some children, assuming the fading out started at [`fade_start`]. +pub fn fade_out<R>( + ui: &mut Ui, + fade_start: f64, + add_contents: impl FnOnce(&mut Ui) -> R, +) -> InnerResponse<R> { + let since_transition = ui.ctx().input().time - fade_start; + let alpha = (since_transition * (1.0 / FADE_DURATION)).clamp(0.0, 1.0) as f32; + if alpha < 1.0 { + // The fade isn't done yet, so schedule another render. + ui.ctx().request_repaint(); + } + with_alpha(ui, 1.0 - alpha, |ui| add_contents(ui)) +} + /// Like `fade_in_manual` but provides alpha *decreases* over time. pub fn fade_out_manual<R>( ui: &mut Ui, diff --git a/src/slide/s6_fractals.rs b/src/slide/s6_fractals.rs index cdb425e..77bba7b 100644 --- a/src/slide/s6_fractals.rs +++ b/src/slide/s6_fractals.rs @@ -12,7 +12,7 @@ use crate::component::code::{pseudocode, Code}; use crate::component::grid::Grid; use crate::egui; use crate::egui::{Color32, Context, Frame, Pos2, Shape, Stroke, Ui}; -use crate::fade_in::{fade_in, fade_in_manual, fade_out_manual}; +use crate::fade_in::{fade_in, fade_in_manual, fade_out, fade_out_manual}; use crate::governor::Governor; use crate::slide::s4_automata::randomize_grid; use crate::slide::s6_fractals::circle::circle; @@ -88,6 +88,8 @@ enum FractalsState { pixels: usize, }, MandelbrotZoom { + /// When we started fading in. + fade_start: f64, /// Fractal zoom background image. image: ColorImage, /// GPU accelerator. @@ -119,11 +121,7 @@ impl Default for Fractals { grid.stroke_width = 0.0; Self { - state: FractalsState::MandelbrotZoom { - image: ColorImage::new([1280, 720], Color32::RED), - accelerator: Accelerator::new(1280, 720), - zoom: 1.0, - }, //FractalsState::default(), + state: FractalsState::default(), governor: Governor::default(), code, grid, @@ -179,6 +177,7 @@ impl Slide for Fractals { } FractalsState::JuliaGradient { .. } => { self.state = FractalsState::MandelbrotZoom { + fade_start: ctx.input().time, image: ColorImage::new([1280, 720], Color32::RED), accelerator: Accelerator::new(1280, 720), zoom: 1.0, @@ -190,22 +189,29 @@ impl Slide for Fractals { } fn show(&mut self, ui: &mut Ui) { + let all_fade_out_start = match &self.state { + FractalsState::MandelbrotZoom { fade_start, .. } => *fade_start, + _ => f64::INFINITY, + }; + Frame::none().margin(Margin::same(20.0)).show(ui, |ui| { - ui.vertical_centered(|ui| { - ui.heading("Fractals"); - let quote_fade_start = match &self.state { - FractalsState::Before | FractalsState::Grid {..} | FractalsState::Erase {..} | FractalsState::Axes {..} | FractalsState::Rectangle {..} | FractalsState::Circle {..} => None, - FractalsState::Mandelbrot {quote, ..} => *quote, - _ => Some(0.0), - }; - if let Some(quote_fade_start) = quote_fade_start { - fade_in(ui, quote_fade_start, |ui| { - ui.add_space(ui.available_height() - 50.0); - ui.centered_and_justified(|ui| { - ui.label(r#""Beautiful, damn hard, increasingly useful. That's fractals." -Benoit Mandelbrot"#); + fade_out(ui, all_fade_out_start, |ui| { + ui.vertical_centered(|ui| { + ui.heading("Fractals"); + let quote_fade_start = match &self.state { + FractalsState::Before | FractalsState::Grid { .. } | FractalsState::Erase { .. } | FractalsState::Axes { .. } | FractalsState::Rectangle { .. } | FractalsState::Circle { .. } => None, + FractalsState::Mandelbrot { quote, .. } => *quote, + _ => Some(0.0), + }; + if let Some(quote_fade_start) = quote_fade_start { + fade_in(ui, quote_fade_start, |ui| { + ui.add_space(ui.available_height() - 50.0); + ui.centered_and_justified(|ui| { + ui.label(r#""Beautiful, damn hard, increasingly useful. That's fractals." -Benoit Mandelbrot"#); + }); }); - }); - } + } + }); }); }); @@ -302,20 +308,29 @@ impl Slide for Fractals { limit_pixels = Some(pixels); } FractalsState::MandelbrotZoom { + fade_start, image, accelerator, zoom, } => { + fade_out_manual(ui, *fade_start, |_, alpha| { + self.grid.alpha = alpha; + self.code.alpha = alpha; + }); + let screen = ui.ctx().input().screen_rect(); - *zoom *= 0.9; + if *zoom > 0.00006162543 { + *zoom *= 0.95; + } accelerator.run( Pos2::new( 0.3602404434376143632361252444495, - -0.6413130610648031748603750151, + -(-0.6413130610648031748603750151 - 0.7 * 0.00006162543), ), *zoom, image, ); + //println!("zoom: {}", *zoom); egui::Image::new(&ui.ctx().load_texture("zoom", image.clone()), Vec2::ZERO) .paint_at(ui, screen); } diff --git a/src/slide/s6_fractals/compute.wgsl b/src/slide/s6_fractals/compute.wgsl index 008bb5a..b888e3c 100644 --- a/src/slide/s6_fractals/compute.wgsl +++ b/src/slide/s6_fractals/compute.wgsl @@ -9,8 +9,7 @@ struct ComputeParams { [[group(0), binding(0)]] var<uniform> params: ComputeParams; [[group(0), binding(1)]] var texture: [[stride(16)]] texture_storage_2d<rgba8unorm, write>; -fn mandelbrot(x: f32, y: f32) -> f32 { - let max: f32 = 80.0; +fn mandelbrot(x: f32, y: f32, max: f32) -> f32 { var x1: f32 = 0.0; var y1: f32 = 0.0; var i: f32 = 0.0; @@ -41,11 +40,20 @@ fn main([[builtin(global_invocation_id)]] global_id: vec3<u32>) { } let aspect = f32(W) / f32(H); - let nx = (f32(X) / f32(W) - 0.5) * 2.0; - let ny = (f32(Y) / f32(H) - 0.5) * 2.0; + let nx = f32(X) / f32(W) - 0.5; + let ny = f32(Y) / f32(H) - 0.5; let fx = params.center_x + nx * params.zoom * aspect; let fy = params.center_y + ny * params.zoom; - let iter = mandelbrot(fx, fy); - textureStore(texture, vec2<i32>(i32(X), i32(Y)), vec4<f32>(iter, iter, iter, 1.0)); + let iter = mandelbrot(fx, fy, 50.0 + 0.1 / params.zoom); + + var stops = array<vec3<f32>, 3>(vec3<f32>(0.0, 0.0, 1.0), vec3<f32>(0.25, 1.0, 1.0), vec3<f32>(1.0, 1.0, 1.0)); + + var color = vec3<f32>(0.0, 0.0, 0.0); + + for (var i: i32 = 0; i < 3; i = i + 1) { + color = mix(color, stops[i], smoothStep(f32(i) / 4.0, f32(i + 1) / 4.0, iter)); + } + + textureStore(texture, vec2<i32>(i32(X), i32(Y)), vec4<f32>(color.x, color.y, color.z, 1.0)); } \ No newline at end of file -- GitLab From e9c9e9aeba5f4071b98d3e8c31a5c01805c23848 Mon Sep 17 00:00:00 2001 From: Finn Bear <finnbearlabs@gmail.com> Date: Sun, 8 May 2022 12:39:39 -0700 Subject: [PATCH 4/5] Improvements and optimizations. --- src/slide/s6_fractals.rs | 16 +++++++++------- src/slide/s6_fractals/compute.wgsl | 12 +++++++++--- 2 files changed, 18 insertions(+), 10 deletions(-) diff --git a/src/slide/s6_fractals.rs b/src/slide/s6_fractals.rs index 77bba7b..a1985bd 100644 --- a/src/slide/s6_fractals.rs +++ b/src/slide/s6_fractals.rs @@ -318,15 +318,17 @@ impl Slide for Fractals { self.code.alpha = alpha; }); + let ultimate = Pos2::new( + 0.3602404434376143632361252444495 + 0.5 * 0.00006162543, + -(-0.6413130610648031748603750151 - 0.7 * 0.00006162543), + ); + + let time = (ui.ctx().input().time - *fade_start) as f32; let screen = ui.ctx().input().screen_rect(); - if *zoom > 0.00006162543 { - *zoom *= 0.95; - } + *zoom = 3.0 * 0.95f32.powf(time * 10.0).max(0.00008); + let lerp = (time * 0.2).sqrt().min(1.0); accelerator.run( - Pos2::new( - 0.3602404434376143632361252444495, - -(-0.6413130610648031748603750151 - 0.7 * 0.00006162543), - ), + Pos2::new(ultimate.x * lerp, ultimate.y * lerp), *zoom, image, ); diff --git a/src/slide/s6_fractals/compute.wgsl b/src/slide/s6_fractals/compute.wgsl index b888e3c..aed29fa 100644 --- a/src/slide/s6_fractals/compute.wgsl +++ b/src/slide/s6_fractals/compute.wgsl @@ -45,14 +45,20 @@ fn main([[builtin(global_invocation_id)]] global_id: vec3<u32>) { let fx = params.center_x + nx * params.zoom * aspect; let fy = params.center_y + ny * params.zoom; - let iter = mandelbrot(fx, fy, 50.0 + 0.1 / params.zoom); + let iter = mandelbrot(fx, fy, 90.0 + 0.04 / params.zoom); - var stops = array<vec3<f32>, 3>(vec3<f32>(0.0, 0.0, 1.0), vec3<f32>(0.25, 1.0, 1.0), vec3<f32>(1.0, 1.0, 1.0)); + let count: i32 = 3; + var stops = array<vec3<f32>, 3>( + vec3<f32>(0.1, 0.05, 0.9), + vec3<f32>(0.25, 1.0, 1.0), + vec3<f32>(1.0, 1.0, 1.0) + ); var color = vec3<f32>(0.0, 0.0, 0.0); + let scale: f32 = f32(count + 1); for (var i: i32 = 0; i < 3; i = i + 1) { - color = mix(color, stops[i], smoothStep(f32(i) / 4.0, f32(i + 1) / 4.0, iter)); + color = mix(color, stops[i], smoothStep(f32(i) / scale, f32(i + 1) / 4.0, iter)); } textureStore(texture, vec2<i32>(i32(X), i32(Y)), vec4<f32>(color.x, color.y, color.z, 1.0)); -- GitLab From 603e82e0dd914d99a4bc8991ec96aa04eebf26ae Mon Sep 17 00:00:00 2001 From: Finn Bear <finnbearlabs@gmail.com> Date: Sun, 8 May 2022 15:25:25 -0700 Subject: [PATCH 5/5] Better fractal zoom. --- src/slide/s6_fractals.rs | 8 ++++-- src/slide/s6_fractals/compute.wgsl | 44 ++++++++++++++++++++---------- 2 files changed, 35 insertions(+), 17 deletions(-) diff --git a/src/slide/s6_fractals.rs b/src/slide/s6_fractals.rs index a1985bd..f7e5ae9 100644 --- a/src/slide/s6_fractals.rs +++ b/src/slide/s6_fractals.rs @@ -318,15 +318,19 @@ impl Slide for Fractals { self.code.alpha = alpha; }); + /* let ultimate = Pos2::new( 0.3602404434376143632361252444495 + 0.5 * 0.00006162543, -(-0.6413130610648031748603750151 - 0.7 * 0.00006162543), ); + */ + + let ultimate = Pos2::new(-0.613287065, -0.44526086); let time = (ui.ctx().input().time - *fade_start) as f32; let screen = ui.ctx().input().screen_rect(); - *zoom = 3.0 * 0.95f32.powf(time * 10.0).max(0.00008); - let lerp = (time * 0.2).sqrt().min(1.0); + *zoom = 3.0 * 0.95f32.powf(time * 10.0).max(0.0001); + let lerp = (time * 1.0).sqrt().min(1.0); accelerator.run( Pos2::new(ultimate.x * lerp, ultimate.y * lerp), *zoom, diff --git a/src/slide/s6_fractals/compute.wgsl b/src/slide/s6_fractals/compute.wgsl index aed29fa..eb85893 100644 --- a/src/slide/s6_fractals/compute.wgsl +++ b/src/slide/s6_fractals/compute.wgsl @@ -9,13 +9,13 @@ struct ComputeParams { [[group(0), binding(0)]] var<uniform> params: ComputeParams; [[group(0), binding(1)]] var texture: [[stride(16)]] texture_storage_2d<rgba8unorm, write>; -fn mandelbrot(x: f32, y: f32, max: f32) -> f32 { +fn mandelbrot(x: f32, y: f32, cutoff: f32) -> f32 { var x1: f32 = 0.0; var y1: f32 = 0.0; var i: f32 = 0.0; loop { - if (x1 * x1 + y1 * y1 > 4.0 || i > max) { + if (x1 * x1 + y1 * y1 > 16.0 || i >= cutoff) { break; } @@ -25,7 +25,8 @@ fn mandelbrot(x: f32, y: f32, max: f32) -> f32 { i = i + 1.0; } - return i / max; + var fract = 1.0 - log(log(sqrt(max(1.001, x1 * x1 + y1 * y1)))) / log(2.0); + return (i + fract) / cutoff; } [[stage(compute), workgroup_size(8, 8)]] @@ -45,21 +46,34 @@ fn main([[builtin(global_invocation_id)]] global_id: vec3<u32>) { let fx = params.center_x + nx * params.zoom * aspect; let fy = params.center_y + ny * params.zoom; - let iter = mandelbrot(fx, fy, 90.0 + 0.04 / params.zoom); + let max_iter = min(270.0, 32.0 + sqrt(16.0 / params.zoom)); + let o = params.zoom * 0.2 / f32(W); + let iter = pow((mandelbrot(fx + o, fy + o, max_iter) + mandelbrot(fx + o, fy - o, max_iter) + mandelbrot(fx - o, fy + o, max_iter) + mandelbrot(fx - o, fy - o, max_iter)) * 0.25, 1.0); - let count: i32 = 3; - var stops = array<vec3<f32>, 3>( - vec3<f32>(0.1, 0.05, 0.9), - vec3<f32>(0.25, 1.0, 1.0), - vec3<f32>(1.0, 1.0, 1.0) - ); + // https://flatuicolors.com/palette/defo + var amethyst = vec3<f32>(155.0/255.0, 89.0/255.0, 182.0/255.0); + var wet_asphalt = vec3<f32>(52.0/255.0, 73.0/255.0, 94.0/255.0); + var emerald = vec3<f32>(46.0/255.0, 204.0/255.0, 113.0/255.0); + var sunflower = vec3<f32>(241.0/255.0, 196.0/255.0, 15.0/255.0); + var alizarin = vec3<f32>(231.0/255.0, 76.0/255.0, 60.0/255.0); + var pomegranate = vec3<f32>(192.0/255.0, 57.0/255.0, 43.0/255.0); + var peter_river = vec3<f32>(52.0/255.0, 152.0/255.0, 219.0/255.0); + var midnight_blue = vec3<f32>(44.0/255.0, 62.0/255.0, 80.0/255.0); - var color = vec3<f32>(0.0, 0.0, 0.0); + let count: i32 = 8; + var stops = array<vec3<f32>, 8>( + midnight_blue, + alizarin, + wet_asphalt, + amethyst, + peter_river, + pomegranate, + sunflower, + vec3<f32>(0.1, 0.1, 0.1) + ); - let scale: f32 = f32(count + 1); - for (var i: i32 = 0; i < 3; i = i + 1) { - color = mix(color, stops[i], smoothStep(f32(i) / scale, f32(i + 1) / 4.0, iter)); - } + let i = i32(iter * f32(count - 1)); + let color = mix(stops[i], stops[i + 1], smoothStep(f32(i) / f32(count - 1), f32(i + 1) / f32(count - 1), iter)); textureStore(texture, vec2<i32>(i32(X), i32(Y)), vec4<f32>(color.x, color.y, color.z, 1.0)); } \ No newline at end of file -- GitLab