From 008761005c912e9491fb228eca9cb7f4ae1aa4c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Isa=C3=AFe?= Date: Mon, 6 May 2024 09:47:13 +0200 Subject: [PATCH] refactor!: update render dependencies & cleanup the code (#70) * chore: bump render deps versions * refactor: replace EventLoopWindowTarget by ActiveEventLoop * refactor: remove wasm32 target code * refactor: update window init using deprecated method I will further update it later, this is just enough for the crate to compile * refactor: add default compilation option to the state creation * feat: add new basic structures based on https://github.com/rust-windowing/winit/issues/3626#issuecomment-2081794856 * fix: revert an IDE-induced rename * fix: revert idem * feat: complete GfxState constructor * refactor: make GfxState::new synchronous using pollster * feat: impl ApplicationHandler for App * feat: add necessary update methods to gfx state * fix: add handle vertex building code * chore: cleanup imports & re-exports * refactor: move shader_data & camera modules * fix: update render examples * chore: add cusotm lints to honeycomb-render * fix: process some lints * fix: add back the possibility to run default examples --- Cargo.toml | 6 +- .../examples/render/default_no_aa.rs | 4 +- .../examples/render/default_smaa1x.rs | 2 +- .../examples/render/splitsquaremap.rs | 2 +- .../examples/render/squaremap.rs | 2 +- .../render/squaremap_parameterized.rs | 2 +- .../examples/render/squaremap_shift.rs | 2 +- .../examples/render/squaremap_split_diff.rs | 2 +- .../examples/render/squaremap_split_some.rs | 2 +- honeycomb-render/Cargo.toml | 11 - honeycomb-render/src/handle.rs | 17 +- honeycomb-render/src/lib.rs | 24 +- honeycomb-render/src/representations/mod.rs | 1 + .../src/{ => representations}/shader_data.rs | 3 +- honeycomb-render/src/runner.rs | 167 ++---- honeycomb-render/src/state/app.rs | 106 ++++ honeycomb-render/src/{ => state}/camera.rs | 1 + .../src/{state.rs => state/gfx.rs} | 477 +++++++----------- honeycomb-render/src/state/mod.rs | 40 ++ 19 files changed, 414 insertions(+), 457 deletions(-) rename honeycomb-render/src/{ => representations}/shader_data.rs (99%) create mode 100644 honeycomb-render/src/state/app.rs rename honeycomb-render/src/{ => state}/camera.rs (99%) rename honeycomb-render/src/{state.rs => state/gfx.rs} (62%) create mode 100644 honeycomb-render/src/state/mod.rs diff --git a/Cargo.toml b/Cargo.toml index a4f61af7..9470d8f9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -44,9 +44,9 @@ bytemuck = "1.15.0" cgmath = "0.18.0" env_logger = "0.11.3" pollster = "0.3.0" -smaa = "0.13.0" -wgpu = "0.19.4" -winit = "0.29.15" +smaa = "0.14.0" +wgpu = "0.20.0" +winit = "0.30.0" [profile.bench] debug = true diff --git a/honeycomb-examples/examples/render/default_no_aa.rs b/honeycomb-examples/examples/render/default_no_aa.rs index dd3c5ce5..40adb7a9 100644 --- a/honeycomb-examples/examples/render/default_no_aa.rs +++ b/honeycomb-examples/examples/render/default_no_aa.rs @@ -1,9 +1,9 @@ -use honeycomb_render::{RenderParameters, Runner, SmaaMode}; +use honeycomb_render::{launch, RenderParameters, SmaaMode}; fn main() { let render_params = RenderParameters { smaa_mode: SmaaMode::Disabled, ..Default::default() }; - Runner::default().run::(render_params, None); + launch::(render_params, None); } diff --git a/honeycomb-examples/examples/render/default_smaa1x.rs b/honeycomb-examples/examples/render/default_smaa1x.rs index 81fa4935..d77dab21 100644 --- a/honeycomb-examples/examples/render/default_smaa1x.rs +++ b/honeycomb-examples/examples/render/default_smaa1x.rs @@ -5,5 +5,5 @@ fn main() { smaa_mode: SmaaMode::Smaa1X, ..Default::default() }; - Runner::default().run::(render_params, None); + launch::(render_params, None); } diff --git a/honeycomb-examples/examples/render/splitsquaremap.rs b/honeycomb-examples/examples/render/splitsquaremap.rs index 06d99ff3..0b3778a5 100644 --- a/honeycomb-examples/examples/render/splitsquaremap.rs +++ b/honeycomb-examples/examples/render/splitsquaremap.rs @@ -7,5 +7,5 @@ fn main() { ..Default::default() }; let map: CMap2 = GridBuilder::split_unit_squares(4).build2().unwrap(); - Runner::default().run(render_params, Some(&map)); + launch(render_params, Some(&map)); } diff --git a/honeycomb-examples/examples/render/squaremap.rs b/honeycomb-examples/examples/render/squaremap.rs index 4ad4d095..b5f0e012 100644 --- a/honeycomb-examples/examples/render/squaremap.rs +++ b/honeycomb-examples/examples/render/squaremap.rs @@ -7,5 +7,5 @@ fn main() { ..Default::default() }; let map: CMap2 = GridBuilder::unit_squares(4).build2().unwrap(); - Runner::default().run(render_params, Some(&map)); + launch(render_params, Some(&map)); } diff --git a/honeycomb-examples/examples/render/squaremap_parameterized.rs b/honeycomb-examples/examples/render/squaremap_parameterized.rs index 1106542f..26cb0aac 100644 --- a/honeycomb-examples/examples/render/squaremap_parameterized.rs +++ b/honeycomb-examples/examples/render/squaremap_parameterized.rs @@ -12,5 +12,5 @@ fn main() { .len_per_cell_y(3.0_f64) .build2() .unwrap(); - Runner::default().run(render_params, Some(&map)); + launch(render_params, Some(&map)); } diff --git a/honeycomb-examples/examples/render/squaremap_shift.rs b/honeycomb-examples/examples/render/squaremap_shift.rs index 9b31e7ef..7763f41f 100644 --- a/honeycomb-examples/examples/render/squaremap_shift.rs +++ b/honeycomb-examples/examples/render/squaremap_shift.rs @@ -60,5 +60,5 @@ fn main() { let elapsed = now.elapsed(); println!("I: Finished shifting in {}μs", elapsed.as_micros()); - Runner::default().run(render_params, Some(&map)); + launch(render_params, Some(&map)); } diff --git a/honeycomb-examples/examples/render/squaremap_split_diff.rs b/honeycomb-examples/examples/render/squaremap_split_diff.rs index e9259d2b..f69e3fc4 100644 --- a/honeycomb-examples/examples/render/squaremap_split_diff.rs +++ b/honeycomb-examples/examples/render/squaremap_split_diff.rs @@ -57,5 +57,5 @@ fn main() { let elapsed = now.elapsed(); println!("I: Finished splitting in {}μs", elapsed.as_micros()); - Runner::default().run(render_params, Some(&map)); + launch(render_params, Some(&map)); } diff --git a/honeycomb-examples/examples/render/squaremap_split_some.rs b/honeycomb-examples/examples/render/squaremap_split_some.rs index 5238e49f..6b701e85 100644 --- a/honeycomb-examples/examples/render/squaremap_split_some.rs +++ b/honeycomb-examples/examples/render/squaremap_split_some.rs @@ -55,5 +55,5 @@ fn main() { let elapsed = now.elapsed(); println!("I: Finished splitting in {}μs", elapsed.as_micros()); - Runner::default().run(render_params, Some(&map)); + launch(render_params, Some(&map)); } diff --git a/honeycomb-render/Cargo.toml b/honeycomb-render/Cargo.toml index fd38a829..d18d7da3 100644 --- a/honeycomb-render/Cargo.toml +++ b/honeycomb-render/Cargo.toml @@ -12,9 +12,6 @@ keywords.workspace = true authors.workspace = true publish = true -[lib] -crate-type = ["cdylib", "rlib"] - [dependencies] bytemuck = { workspace = true, features = ["derive"] } cfg-if.workspace = true @@ -25,11 +22,3 @@ pollster.workspace = true smaa.workspace = true wgpu.workspace = true winit.workspace = true - -[target.'cfg(target_arch = "wasm32")'.dependencies] -console_error_panic_hook = "0.1.6" -console_log = "1.0" -wasm-bindgen = "0.2" -wasm-bindgen-futures = "0.4" -web-sys = { version = "0.3", features = ["Document", "Window", "Element"] } -wgpu = { workspace = true, features = ["webgl"] } diff --git a/honeycomb-render/src/handle.rs b/honeycomb-render/src/handle.rs index 9f06ee6d..1b179c5b 100644 --- a/honeycomb-render/src/handle.rs +++ b/honeycomb-render/src/handle.rs @@ -5,11 +5,15 @@ // ------ IMPORTS -use crate::representations::intermediates::{Entity, IntermediateFace}; -use crate::shader_data::Coords2Shader; -use crate::SmaaMode; +use crate::{ + representations::{ + intermediates::{Entity, IntermediateFace}, + shader_data::Coords2Shader, + }, + MapRef, SmaaMode, +}; use honeycomb_core::{ - CMap2, CoordsFloat, DartIdentifier, EdgeIdentifier, Orbit2, OrbitPolicy, Vertex2, NULL_DART_ID, + CoordsFloat, DartIdentifier, EdgeIdentifier, Orbit2, OrbitPolicy, Vertex2, NULL_DART_ID, }; // ------ CONTENT @@ -18,6 +22,7 @@ use honeycomb_core::{ /// /// The user can easily adjust parameters in this structure to obtain /// the preferred output style. +#[derive(Clone, Copy)] pub struct RenderParameters { /// Anti-aliasing configuration. pub smaa_mode: SmaaMode, @@ -44,7 +49,7 @@ impl Default for RenderParameters { } pub struct CMap2RenderHandle<'a, T: CoordsFloat> { - handle: &'a CMap2, + handle: MapRef<'a, T>, params: RenderParameters, intermediate_buffer: Vec>, dart_construction_buffer: Vec, @@ -54,7 +59,7 @@ pub struct CMap2RenderHandle<'a, T: CoordsFloat> { } impl<'a, T: CoordsFloat> CMap2RenderHandle<'a, T> { - pub fn new(map: &'a CMap2, params: Option) -> Self { + pub fn new(map: MapRef<'a, T>, params: Option) -> Self { Self { handle: map, params: params.unwrap_or_default(), diff --git a/honeycomb-render/src/lib.rs b/honeycomb-render/src/lib.rs index 15e1ee72..3914f397 100644 --- a/honeycomb-render/src/lib.rs +++ b/honeycomb-render/src/lib.rs @@ -2,7 +2,7 @@ //! //! This crate implements a runner that can be used to display combinatorial maps. //! -//! It currently only supports 2D maps as the core library only implements these (as [CMap2]) +//! It currently only supports 2D maps as the core library only implements these (as [`honeycomb_core::CMap2`]) //! //! ## Key bindings //! @@ -14,15 +14,22 @@ //! //! Examples are available in the **honeycomb-examples** crate. -#[cfg(doc)] -use honeycomb_core::CMap2; +// ------ CUSTOM LINTS + +// more lints +#![warn(clippy::pedantic)] +#![warn(missing_docs)] +// some exceptions +#![allow(clippy::module_name_repetitions)] +#![allow(clippy::too_many_lines)] +#![allow(clippy::cast_possible_truncation)] +#![allow(clippy::cast_sign_loss)] +#![allow(clippy::cast_precision_loss)] // ------ MODULE DECLARATIONS -mod camera; mod handle; mod runner; -mod shader_data; mod state; mod representations; @@ -30,5 +37,10 @@ mod representations; // ------ RE-EXPORTS pub use handle::RenderParameters; -pub use runner::Runner; +pub use runner::{launch, launch_async}; pub use state::SmaaMode; + +// ------ CONTENT + +/// Convenience type alias +type MapRef<'a, T> = &'a honeycomb_core::CMap2; diff --git a/honeycomb-render/src/representations/mod.rs b/honeycomb-render/src/representations/mod.rs index 8e244555..c0e9e5bc 100644 --- a/honeycomb-render/src/representations/mod.rs +++ b/honeycomb-render/src/representations/mod.rs @@ -1 +1,2 @@ pub mod intermediates; +pub mod shader_data; diff --git a/honeycomb-render/src/shader_data.rs b/honeycomb-render/src/representations/shader_data.rs similarity index 99% rename from honeycomb-render/src/shader_data.rs rename to honeycomb-render/src/representations/shader_data.rs index 5d2abba0..ae1981c9 100644 --- a/honeycomb-render/src/shader_data.rs +++ b/honeycomb-render/src/representations/shader_data.rs @@ -5,8 +5,9 @@ // ------ IMPORTS -use crate::representations::intermediates::Entity; use bytemuck::{Pod, Zeroable}; + +use crate::representations::intermediates::Entity; use honeycomb_core::{CoordsFloat, Vertex2}; // ------ CONTENT diff --git a/honeycomb-render/src/runner.rs b/honeycomb-render/src/runner.rs index 9e05ed63..4059131b 100644 --- a/honeycomb-render/src/runner.rs +++ b/honeycomb-render/src/runner.rs @@ -4,145 +4,46 @@ // ------ IMPORTS -use winit::event::{Event, WindowEvent}; use winit::event_loop::EventLoop; -use winit::window::Window; -use crate::state::State; -use crate::RenderParameters; -use honeycomb_core::{CMap2, CoordsFloat}; +use crate::{state::App, MapRef, RenderParameters}; +use honeycomb_core::CoordsFloat; // ------ CONTENT -cfg_if::cfg_if! { - if #[cfg(target_arch = "wasm32")] { - pub type MapRef<'a, T> = &'static CMap2; - } else { - pub type MapRef<'a, T> = &'a CMap2; - } +#[allow(clippy::missing_panics_doc)] +/// Main rendering function. +/// +/// # Arguments +/// +/// - `render_params: RenderParameters` -- Render parameterization. +/// - `map: Option<&CMap2>` -- Optional reference to the map that should be rendered +/// +/// If no reference is passed to the method, a hardcoded example will be rendered instead. +/// +/// # Example +/// +/// Because the renderer uses the core and utils crates, examples are provided as standalone +/// files rather than in the doc. You can find them in the project's repository, under the +/// `honeycomb-examples` crate and run them using `cargo`: +/// +/// ```shell +/// cargo run --example +/// ``` +/// +pub fn launch(render_params: RenderParameters, map: Option>) { + // enable custom env logging + env_logger::init(); + // build app & event loop + let mut app = App::new(render_params, map); + let event_loop = EventLoop::new().unwrap(); + let _ = event_loop.run_app(&mut app); } -const TARGET_FPS: f32 = 240.; - -/// This yields an approximate 240 FPS -const MS_PER_FRAME: u128 = (1000. / TARGET_FPS) as u128; - -async fn inner( - event_loop: EventLoop<()>, - window: Window, - render_params: RenderParameters, - map: Option>, +/// UNIMPLEMENTED +pub async fn launch_async( + _render_params: RenderParameters, + _map: Option>, ) { - let mut state = if let Some(val) = map { - State::new(&window, render_params, val).await - } else { - State::new_test(&window, render_params).await - }; - println!("I: Press F1 to quit"); - event_loop - .run(move |event, target| { - // process events - match event { - Event::WindowEvent { - window_id, - event: wevent, - } => { - if window_id == state.window().id() && !state.input(&wevent, target) { - match wevent { - WindowEvent::Resized(new_size) => state.resize(Some(new_size)), - WindowEvent::RedrawRequested => { - let start = std::time::Instant::now(); - state.update(); - match state.render() { - Ok(_) => {} - Err(wgpu::SurfaceError::Lost) => state.resize(None), - Err(wgpu::SurfaceError::OutOfMemory) => target.exit(), // kill if OOM - Err(e) => eprintln!("{:?}", e), - }; - // put a hard cap on the rendering speed - std::thread::sleep(std::time::Duration::from_millis( - (MS_PER_FRAME - start.elapsed().as_millis()) as u64, - )); - } - WindowEvent::CloseRequested => target.exit(), - _ => {} - }; - } - } - Event::AboutToWait => { - state.window().request_redraw(); - } - _ => {} - } - }) - .unwrap(); -} - -/// Main rendering structure -pub struct Runner { - event_loop: EventLoop<()>, - window: Window, -} - -impl Runner { - /// Main rendering method. - /// - /// # Arguments - /// - /// - `render_params: RenderParameters` -- Render parameterization. - /// - `map: Option<&CMap2>` -- Optionnal reference to the map that should be rendered - /// - /// If no reference is passed to the method, a hardcoded example will be rendered instead. - /// - /// # Example - /// - /// Because the renderer uses the core and utils crates, examples are provided as standalone - /// files rather than in the doc. You can run them using the following command: - /// - /// ```shell - /// cargo run --example - /// ``` - /// - pub fn run(self, render_params: RenderParameters, map: Option>) { - cfg_if::cfg_if! { - if #[cfg(target_arch = "wasm32")] { - std::panic::set_hook(Box::new(console_error_panic_hook::hook)); - console_log::init().expect("could not initialize logger"); - wasm_bindgen_futures::spawn_local(inner(self.event_loop, self.window, render_params, map)); - } else { - env_logger::init(); - pollster::block_on(inner(self.event_loop, self.window, render_params, map)); - } - } - } - - /// UNIMPLEMENTED - pub async fn run_async(&self) { - unimplemented!() - } -} - -impl Default for Runner { - fn default() -> Self { - let event_loop = EventLoop::new().unwrap(); - #[allow(unused_mut)] - let mut builder = winit::window::WindowBuilder::new(); - #[cfg(target_arch = "wasm32")] - { - use wasm_bindgen::JsCast; - use winit::platform::web::WindowBuilderExtWebSys; - let canvas = web_sys::window() - .unwrap() - .document() - .unwrap() - .get_element_by_id("wasm-example") - .unwrap() - .dyn_into::() - .unwrap(); - builder = builder.with_canvas(Some(canvas)); - } - let window = builder.build(&event_loop).unwrap(); - - Self { event_loop, window } - } + unimplemented!() } diff --git a/honeycomb-render/src/state/app.rs b/honeycomb-render/src/state/app.rs new file mode 100644 index 00000000..d1488bd2 --- /dev/null +++ b/honeycomb-render/src/state/app.rs @@ -0,0 +1,106 @@ +//! mod doc + +use crate::handle::CMap2RenderHandle; +use crate::representations::shader_data::TEST_VERTICES; +use crate::state::gfx::GfxState; +use crate::MapRef; +use crate::RenderParameters; +use honeycomb_core::CoordsFloat; +use std::sync::Arc; +use winit::application::ApplicationHandler; +use winit::event::WindowEvent; +use winit::event_loop::ActiveEventLoop; +use winit::window::{Window, WindowId}; + +const TARGET_FPS: f32 = 240.; + +/// This yields an approximate 240 FPS +const MS_PER_FRAME: u128 = (1000. / TARGET_FPS) as u128; + +pub struct App<'a, T: CoordsFloat> { + window: Option>, + gfx: Option, + render_params: RenderParameters, + map_handle: Option>, +} + +impl<'a, T: CoordsFloat> App<'a, T> { + pub fn new(params: RenderParameters, map: Option>) -> Self { + Self { + window: None, + gfx: None, + render_params: params, + map_handle: map.map(|map_ref| CMap2RenderHandle::new(map_ref, Some(params))), + } + } +} + +impl<'a, T: CoordsFloat> ApplicationHandler for App<'a, T> { + fn resumed(&mut self, event_loop: &ActiveEventLoop) { + let win_attrs = Window::default_attributes().with_title("honeycomb-render"); + let window = Arc::new(event_loop.create_window(win_attrs).unwrap()); + + if self.map_handle.is_some() { + self.map_handle.as_mut().unwrap().build_intermediate(); + self.map_handle.as_mut().unwrap().build_faces(); + self.map_handle.as_mut().unwrap().build_darts(); + self.map_handle.as_mut().unwrap().build_betas(); + self.map_handle.as_mut().unwrap().save_buffered(); + } + + let gfx_state = GfxState::new( + Arc::clone(&window), + self.render_params.smaa_mode, + if self.map_handle.is_some() { + self.map_handle.as_ref().unwrap().vertices() + } else { + TEST_VERTICES + }, + ); + window.request_redraw(); + + self.window = Some(window); + self.gfx = Some(gfx_state); + } + + fn window_event( + &mut self, + event_loop: &ActiveEventLoop, + window_id: WindowId, + event: WindowEvent, + ) { + if self.window.as_ref().unwrap().id() == window_id + && !self.gfx.as_mut().unwrap().input(&event, event_loop) + { + match event { + WindowEvent::Resized(new_size) => { + self.gfx.as_mut().unwrap().resize(Some(new_size)); + self.window.as_ref().unwrap().request_redraw(); + } + WindowEvent::RedrawRequested => { + let start = std::time::Instant::now(); + self.gfx.as_mut().unwrap().update(); + match self.gfx.as_mut().unwrap().render(None) { + Ok(()) => {} + Err(wgpu::SurfaceError::Lost) => { + self.gfx.as_mut().unwrap().resize(None); + self.window.as_ref().unwrap().request_redraw(); + } + Err(wgpu::SurfaceError::OutOfMemory) => event_loop.exit(), // kill if OOM + Err(e) => eprintln!("{e:?}"), + }; + // put a hard cap on the rendering speed + std::thread::sleep(std::time::Duration::from_millis( + MS_PER_FRAME.saturating_sub(start.elapsed().as_millis()) as u64, + )); + } + WindowEvent::CloseRequested => event_loop.exit(), + _ => {} + }; + } + } + + fn about_to_wait(&mut self, _: &ActiveEventLoop) { + self.window.as_ref().unwrap().request_redraw(); + } +} diff --git a/honeycomb-render/src/camera.rs b/honeycomb-render/src/state/camera.rs similarity index 99% rename from honeycomb-render/src/camera.rs rename to honeycomb-render/src/state/camera.rs index 142df75a..837a1852 100644 --- a/honeycomb-render/src/camera.rs +++ b/honeycomb-render/src/state/camera.rs @@ -60,6 +60,7 @@ impl CameraUniform { } } +#[allow(clippy::struct_excessive_bools)] pub struct CameraController { speed: f32, is_forward_pressed: bool, diff --git a/honeycomb-render/src/state.rs b/honeycomb-render/src/state/gfx.rs similarity index 62% rename from honeycomb-render/src/state.rs rename to honeycomb-render/src/state/gfx.rs index 45d44fcf..0584489f 100644 --- a/honeycomb-render/src/state.rs +++ b/honeycomb-render/src/state/gfx.rs @@ -1,76 +1,201 @@ -//! Rendering system code -//! -//! This module contains all code used to setup and continuously render available data. +//! mod doc -// ------ IMPORTS - -// intern -use crate::camera::{Camera, CameraController, CameraUniform, SPEED_FACTOR}; -use crate::handle::CMap2RenderHandle; -use crate::shader_data::{Coords2Shader, TEST_VERTICES}; -use crate::RenderParameters; -use honeycomb_core::{CMap2, CoordsFloat}; - -// extern use std::borrow::Cow; +use std::sync::Arc; use wgpu::util::DeviceExt; -use wgpu::PrimitiveTopology; +use wgpu::{PipelineCompilationOptions, PrimitiveTopology}; use winit::dpi::PhysicalSize; -use winit::event::{ElementState, KeyEvent}; -use winit::event_loop::EventLoopWindowTarget; +use winit::event::{ElementState, KeyEvent, WindowEvent}; +use winit::event_loop::ActiveEventLoop; use winit::keyboard::{Key, NamedKey}; -use winit::{event::WindowEvent, window::Window}; - -// ------ CONTENT - -/// Anti-aliasing configuration enum -/// -/// This enum is a bridge to the eponymous enum of the smaa [crate][SMAA]. This prevents -/// the user from adding another external crate to its project. -/// -/// [SMAA]: https://github.com/fintelia/smaa-rs -#[derive(Debug, Default, Clone, Copy)] -pub enum SmaaMode { - /// SMAA1x anti-aliasing. - Smaa1X, - #[default] - /// Disabled anti-aliasing. This is the default value. - Disabled, +use winit::window::Window; + +use crate::{ + representations::shader_data::Coords2Shader, + state::camera::{Camera, CameraController, CameraUniform, SPEED_FACTOR}, +}; + +pub struct GfxState { + pub(crate) surface: wgpu::Surface<'static>, + pub(crate) device: wgpu::Device, + pub(crate) queue: wgpu::Queue, + pub(crate) config: wgpu::SurfaceConfiguration, + pub(crate) size: PhysicalSize, + pub(crate) render_pipeline: wgpu::RenderPipeline, + pub(crate) vertex_buffer: wgpu::Buffer, + pub(crate) num_vertices: u32, + pub(crate) camera: Camera, + pub(crate) camera_uniform: CameraUniform, + pub(crate) camera_buffer: wgpu::Buffer, + pub(crate) camera_bind_group: wgpu::BindGroup, + pub(crate) camera_controller: CameraController, + pub(crate) smaa_target: smaa::SmaaTarget, } -impl From for smaa::SmaaMode { - fn from(value: SmaaMode) -> Self { - match value { - SmaaMode::Smaa1X => smaa::SmaaMode::Smaa1X, - SmaaMode::Disabled => smaa::SmaaMode::Disabled, +impl GfxState { + pub fn new( + window: Arc, + antialiasing: crate::SmaaMode, + render_slice: &[Coords2Shader], + ) -> Self { + let instance = wgpu::Instance::default(); + + eprintln!("I: Available adapters:"); + for a in instance.enumerate_adapters(wgpu::Backends::all()) { + eprintln!(" {:#?}", a.get_info()); + } + + // fetch window size + let mut size = window.inner_size(); + size.width = size.width.max(1); + size.height = size.height.max(1); + + let surface = instance.create_surface(window).unwrap(); + + let ( + device, + queue, + config, + camera, + camera_uniform, + camera_buffer, + camera_bind_group, + camera_controller, + swapchain_format, + render_pipeline, + ) = pollster::block_on(inner(&instance, &surface, size)); + + let smaa_target = smaa::SmaaTarget::new( + &device, + &queue, + size.width, + size.height, + swapchain_format, + smaa::SmaaMode::from(antialiasing), + ); + + let vertex_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { + label: Some("Vertex Buffer"), + contents: bytemuck::cast_slice(render_slice), + usage: wgpu::BufferUsages::VERTEX, + }); + + let num_vertices = render_slice.len() as u32; + + Self { + surface, + device, + queue, + config, + size, + render_pipeline, + vertex_buffer, + num_vertices, + camera, + camera_uniform, + camera_buffer, + camera_bind_group, + camera_controller, + smaa_target, } } -} -pub struct State<'a, T: CoordsFloat> { - surface: wgpu::Surface<'a>, - device: wgpu::Device, - queue: wgpu::Queue, - config: wgpu::SurfaceConfiguration, - size: PhysicalSize, - render_pipeline: wgpu::RenderPipeline, - vertex_buffer: wgpu::Buffer, - num_vertices: u32, - camera: Camera, - camera_uniform: CameraUniform, - camera_buffer: wgpu::Buffer, - camera_bind_group: wgpu::BindGroup, - camera_controller: CameraController, - smaa_target: smaa::SmaaTarget, - map_handle: Option>, - window: &'a Window, + pub fn resize(&mut self, new_size_opt: Option>) { + let new_size = new_size_opt.unwrap_or(self.size); + self.config.width = new_size.width.max(1); + self.config.height = new_size.height.max(1); + self.surface.configure(&self.device, &self.config); + } + + pub fn input(&mut self, event: &WindowEvent, target: &ActiveEventLoop) -> bool { + // early check for exit + if let WindowEvent::KeyboardInput { + event: KeyEvent { + state, logical_key, .. + }, + .. + } = event + { + let is_pressed = *state == ElementState::Pressed; + if let Key::Named(key) = logical_key { + if is_pressed & (key == &NamedKey::F1) { + target.exit(); + } + } + }; + self.camera_controller.process_events(event) + } + + pub fn update(&mut self) { + self.camera_controller.update_camera(&mut self.camera); + self.camera_uniform.update_view_proj(&self.camera); + self.queue.write_buffer( + &self.camera_buffer, + 0, + bytemuck::cast_slice(&[self.camera_uniform]), + ); + } + + pub fn render( + &mut self, + render_slice: Option<&[Coords2Shader]>, + ) -> Result<(), wgpu::SurfaceError> { + // update the vertex buffer & num vertices + if let Some(slice) = render_slice { + self.vertex_buffer = + self.device + .create_buffer_init(&wgpu::util::BufferInitDescriptor { + label: Some("Vertex Buffer"), + contents: bytemuck::cast_slice(slice), + usage: wgpu::BufferUsages::VERTEX, + }); + self.num_vertices = slice.len() as u32; + } + + // render + let frame = self.surface.get_current_texture()?; + let view = frame + .texture + .create_view(&wgpu::TextureViewDescriptor::default()); + let smaa_frame = self + .smaa_target + .start_frame(&self.device, &self.queue, &view); + let mut encoder = self + .device + .create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None }); + { + let mut rpass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { + label: None, + color_attachments: &[Some(wgpu::RenderPassColorAttachment { + view: &smaa_frame, + resolve_target: None, + ops: wgpu::Operations { + load: wgpu::LoadOp::Clear(wgpu::Color::WHITE), + store: wgpu::StoreOp::Store, + }, + })], + depth_stencil_attachment: None, + timestamp_writes: None, + occlusion_query_set: None, + }); + rpass.set_pipeline(&self.render_pipeline); + rpass.set_bind_group(0, &self.camera_bind_group, &[]); + rpass.set_vertex_buffer(0, self.vertex_buffer.slice(..)); + rpass.draw(0..self.num_vertices, 0..1); + } + + self.queue.submit(Some(encoder.finish())); + smaa_frame.resolve(); + frame.present(); + Ok(()) + } } async fn inner( - window: &Window, + instance: &wgpu::Instance, + surface: &wgpu::Surface<'_>, size: PhysicalSize, ) -> ( - wgpu::Surface<'_>, wgpu::Device, wgpu::Queue, wgpu::SurfaceConfiguration, @@ -82,22 +207,11 @@ async fn inner( wgpu::TextureFormat, wgpu::RenderPipeline, ) { - let instance = wgpu::Instance::default(); - - #[cfg(not(target_arch = "wasm32"))] - { - eprintln!("I: Available adapters:"); - for a in instance.enumerate_adapters(wgpu::Backends::all()) { - eprintln!(" {:#?}", a.get_info()) - } - } - - let surface = instance.create_surface(window).unwrap(); let adapter = instance .request_adapter(&wgpu::RequestAdapterOptions { power_preference: wgpu::PowerPreference::default(), force_fallback_adapter: false, - compatible_surface: Some(&surface), + compatible_surface: Some(surface), }) .await .expect("E: Failed to fetch appropriate adaptater"); @@ -119,7 +233,7 @@ async fn inner( let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor { label: None, - source: wgpu::ShaderSource::Wgsl(Cow::Borrowed(include_str!("shaders/shader2.wgsl"))), + source: wgpu::ShaderSource::Wgsl(Cow::Borrowed(include_str!("../shaders/shader2.wgsl"))), }); let config = surface @@ -194,19 +308,21 @@ async fn inner( module: &shader, entry_point: "vs_main", buffers: &[Coords2Shader::desc()], + compilation_options: PipelineCompilationOptions::default(), }, fragment: Some(wgpu::FragmentState { module: &shader, entry_point: "fs_main", targets: &[Some(swapchain_format.into())], + compilation_options: PipelineCompilationOptions::default(), }), primitive: wgpu::PrimitiveState { topology: PrimitiveTopology::TriangleList, strip_index_format: None, - front_face: Default::default(), + front_face: wgpu::FrontFace::default(), cull_mode: None, unclipped_depth: false, - polygon_mode: Default::default(), + polygon_mode: wgpu::PolygonMode::default(), conservative: false, }, depth_stencil: None, @@ -215,7 +331,6 @@ async fn inner( }); ( - surface, device, queue, config, @@ -228,217 +343,3 @@ async fn inner( render_pipeline, ) } - -impl<'a, T: CoordsFloat> State<'a, T> { - pub async fn new( - window: &'a Window, - render_params: RenderParameters, - map: &'a CMap2, - ) -> Self { - let mut size = window.inner_size(); - size.width = size.width.max(1); - size.height = size.height.max(1); - - let ( - surface, - device, - queue, - config, - camera, - camera_uniform, - camera_buffer, - camera_bind_group, - camera_controller, - swapchain_format, - render_pipeline, - ) = inner(window, size).await; - - let smaa_target = smaa::SmaaTarget::new( - &device, - &queue, - window.inner_size().width, - window.inner_size().height, - swapchain_format, - smaa::SmaaMode::from(render_params.smaa_mode), - ); - - let mut map_handle = CMap2RenderHandle::new(map, Some(render_params)); - map_handle.build_intermediate(); - map_handle.build_darts(); - map_handle.build_faces(); - map_handle.build_betas(); - map_handle.save_buffered(); - - let render_slice = map_handle.vertices(); - - let vertex_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { - label: Some("Vertex Buffer"), - contents: bytemuck::cast_slice(render_slice), - usage: wgpu::BufferUsages::VERTEX, - }); - - let num_vertices = render_slice.len() as u32; - - Self { - surface, - device, - queue, - config, - size, - vertex_buffer, - render_pipeline, - num_vertices, - camera, - camera_uniform, - camera_buffer, - camera_bind_group, - camera_controller, - smaa_target, - map_handle: Some(map_handle), - window, - } - } - - pub async fn new_test(window: &'a Window, render_params: RenderParameters) -> Self { - let mut size = window.inner_size(); - size.width = size.width.max(1); - size.height = size.height.max(1); - - let ( - surface, - device, - queue, - config, - camera, - camera_uniform, - camera_buffer, - camera_bind_group, - camera_controller, - swapchain_format, - render_pipeline, - ) = inner(window, size).await; - - let smaa_target = smaa::SmaaTarget::new( - &device, - &queue, - window.inner_size().width, - window.inner_size().height, - swapchain_format, - smaa::SmaaMode::from(render_params.smaa_mode), - ); - - let vertex_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { - label: Some("Vertex Buffer"), - contents: bytemuck::cast_slice(TEST_VERTICES), - usage: wgpu::BufferUsages::VERTEX, - }); - - let num_vertices = TEST_VERTICES.len() as u32; - - Self { - surface, - device, - queue, - config, - size, - vertex_buffer, - render_pipeline, - num_vertices, - camera, - camera_uniform, - camera_buffer, - camera_bind_group, - camera_controller, - smaa_target, - map_handle: None, - window, - } - } - - pub fn window(&self) -> &Window { - self.window - } - - pub fn resize(&mut self, new_size_opt: Option>) { - let new_size = new_size_opt.unwrap_or(self.size); - self.config.width = new_size.width.max(1); - self.config.height = new_size.height.max(1); - self.surface.configure(&self.device, &self.config); - self.window.request_redraw(); - } - - pub fn input(&mut self, event: &WindowEvent, target: &EventLoopWindowTarget<()>) -> bool { - // early check for exit - if let WindowEvent::KeyboardInput { - event: KeyEvent { - state, logical_key, .. - }, - .. - } = event - { - let is_pressed = *state == ElementState::Pressed; - if let Key::Named(key) = logical_key { - if is_pressed & (key == &NamedKey::F1) { - target.exit(); - } - } - }; - self.camera_controller.process_events(event) - } - - pub fn update(&mut self) { - self.camera_controller.update_camera(&mut self.camera); - self.camera_uniform.update_view_proj(&self.camera); - self.queue.write_buffer( - &self.camera_buffer, - 0, - bytemuck::cast_slice(&[self.camera_uniform]), - ); - } - - pub fn render(&mut self) -> Result<(), wgpu::SurfaceError> { - // update the current number of vertices - if let Some(handle) = &self.map_handle { - self.num_vertices = handle.vertices().len() as u32; - }; - // render - let frame = self - .surface - .get_current_texture() - .expect("Failed to acquire next swap chain texture"); - let view = frame - .texture - .create_view(&wgpu::TextureViewDescriptor::default()); - let smaa_frame = self - .smaa_target - .start_frame(&self.device, &self.queue, &view); - let mut encoder = self - .device - .create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None }); - { - let mut rpass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { - label: None, - color_attachments: &[Some(wgpu::RenderPassColorAttachment { - view: &smaa_frame, - resolve_target: None, - ops: wgpu::Operations { - load: wgpu::LoadOp::Clear(wgpu::Color::WHITE), - store: wgpu::StoreOp::Store, - }, - })], - depth_stencil_attachment: None, - timestamp_writes: None, - occlusion_query_set: None, - }); - rpass.set_pipeline(&self.render_pipeline); - rpass.set_bind_group(0, &self.camera_bind_group, &[]); - rpass.set_vertex_buffer(0, self.vertex_buffer.slice(..)); - rpass.draw(0..self.num_vertices, 0..1); - } - - self.queue.submit(Some(encoder.finish())); - smaa_frame.resolve(); - frame.present(); - Ok(()) - } -} diff --git a/honeycomb-render/src/state/mod.rs b/honeycomb-render/src/state/mod.rs new file mode 100644 index 00000000..4eb19fa2 --- /dev/null +++ b/honeycomb-render/src/state/mod.rs @@ -0,0 +1,40 @@ +//! Rendering system code +//! +//! This module contains all code used to setup and continuously render available data. + +// ------ MODULE DECLARATIONS + +mod app; +mod camera; +mod gfx; + +// ------ RE-EXPORTS + +pub use app::App; + +// ------ CONTENT + +/// Anti-aliasing configuration enum +/// +/// This enum is a bridge to the eponymous enum of the smaa [crate][SMAA]. This prevents +/// the user from adding another external crate to its project. +/// +/// [SMAA]: https://github.com/fintelia/smaa-rs +#[derive(Debug, Default, Clone, Copy)] +pub enum SmaaMode { + #[allow(clippy::doc_markdown)] + /// SMAA1x anti-aliasing. + Smaa1X, + #[default] + /// Disabled anti-aliasing. This is the default value. + Disabled, +} + +impl From for smaa::SmaaMode { + fn from(value: SmaaMode) -> Self { + match value { + SmaaMode::Smaa1X => smaa::SmaaMode::Smaa1X, + SmaaMode::Disabled => smaa::SmaaMode::Disabled, + } + } +}