From 49ea83930c9c53346479d992cbc01a659e332a12 Mon Sep 17 00:00:00 2001 From: DarcJC Date: Fri, 26 May 2023 01:47:13 +0800 Subject: [PATCH] refact: integrating with bevy_ecs crate --- Cargo.toml | 1 + renderer/Cargo.toml | 1 + renderer/src/app/context.rs | 158 ++++++++++++++++++++++++++++++++++ renderer/src/app/main.rs | 137 ++++++----------------------- renderer/src/component/mod.rs | 25 +++++- 5 files changed, 210 insertions(+), 112 deletions(-) create mode 100644 renderer/src/app/context.rs diff --git a/Cargo.toml b/Cargo.toml index 7703fab..506a3d3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,6 +16,7 @@ wgpu = "0.16.1" winit = "0.28.6" lazy_static = "1.4.0" anyhow = "1.0.71" +bevy_ecs = "0.10.1" # wasm32 dependencies console_error_panic_hook = "0.1" diff --git a/renderer/Cargo.toml b/renderer/Cargo.toml index 9164c8e..f0c9bd6 100644 --- a/renderer/Cargo.toml +++ b/renderer/Cargo.toml @@ -27,6 +27,7 @@ anyhow.workspace = true async-std.workspace = true js-sys.workspace = true bytemuck.workspace = true +bevy_ecs.workspace = true [target.'cfg(target_arch = "wasm32")'.dependencies] console_error_panic_hook.workspace = true diff --git a/renderer/src/app/context.rs b/renderer/src/app/context.rs new file mode 100644 index 0000000..d32a514 --- /dev/null +++ b/renderer/src/app/context.rs @@ -0,0 +1,158 @@ +use std::ops::DerefMut; + +use async_std::task::block_on; +use bevy_ecs::system::Resource; +use darc_renderer::component::{Action, GSCHEDULES, GWORLD}; +use winit::{event::{Event, WindowEvent, ElementState, VirtualKeyCode, KeyboardInput}, event_loop::ControlFlow}; + +#[cfg(target_arch = "wasm32")] +use wasm_bindgen::prelude::*; + +/// Context to be passed to the application +#[derive(Resource)] +pub(crate) struct ApplicationContext { + window_loop: Option>, + window: Option, + display_component: Option, +} + +unsafe impl Sync for ApplicationContext {} +unsafe impl Send for ApplicationContext {} + +impl ApplicationContext { + pub(crate) async fn new() -> ApplicationContext { + let window_loop = winit::event_loop::EventLoop::new(); + let window = winit::window::WindowBuilder::new() + .with_title("dArCEngine") + .build(&window_loop) + .unwrap(); + Self { + window_loop: Some(window_loop), + window: Some(window), + display_component: None, + } + } + + #[cfg(target_arch = "wasm32")] + pub async fn initialize(&mut self) { + let window = &mut self.window; + + wasm_bindgen_futures::spawn_local(async move { + // add canvas and then initialize gpu component with canvas handle + use winit::dpi::PhysicalSize; + use winit::platform::web::WindowExtWebSys; + web_sys::window() + .and_then(|win| win.document()) + .map(|doc| { + match doc.get_element_by_id("wasm-renderer") { + Some(dst) => { + window.set_inner_size(PhysicalSize::new(450, 400)); + let _ = dst.append_child(&web_sys::Element::from(window.canvas())); + } + None => { + window.set_inner_size(PhysicalSize::new(800, 800)); + let canvas = window.canvas(); + canvas.style().set_css_text( + "background-color: black; display: block; margin: 20px auto;", + ); + doc.body() + .map(|body| body.append_child(&web_sys::Element::from(canvas))); + } + }; + }) + .expect("Couldn't append canvas to document body."); + }); + } + + #[cfg(target_arch = "wasm32")] + pub fn run(&mut self) { + use async_std::task::block_on; + + let run_closure = + Closure::once_into_js(move || self.event_loop()); + + // Handle js exceptions. + // Otherwise the event loop will be stopped. + if let Err(error) = call_catch(&run_closure) { + let is_control_flow_exception = + error.dyn_ref::().map_or(false, |e| { + e.message().includes("Using exceptions for control flow", 0) + }); + + if !is_control_flow_exception { + web_sys::console::error_1(&error); + } + } + + // Bind js function to `call_catch` + #[wasm_bindgen] + extern "C" { + #[wasm_bindgen(catch, js_namespace = Function, js_name = "prototype.call.call")] + fn call_catch(this: &JsValue) -> Result<(), JsValue>; + } + } + + #[cfg(not(target_arch = "wasm32"))] + pub async fn initialize(&mut self) { + self.display_component = Some(darc_renderer::component::DisplayComponent::new(self.window.as_mut().unwrap()).await); + } + + #[cfg(not(target_arch = "wasm32"))] + pub fn run(&mut self) { + self.event_loop() + } + + pub fn event_loop(&mut self) { + if self.display_component.is_none() { + self.display_component = Some(block_on(darc_renderer::component::DisplayComponent::new(self.window.as_mut().unwrap()))); + } + let mut display_component = self.display_component.take().unwrap(); + let window_loop = self.window_loop.take().unwrap(); + let window = self.window.take().unwrap(); + window_loop.run( + move |event, _, control_flow| { + match event { + Event::WindowEvent { ref event, window_id } if window_id == window.id() && !display_component.input(event) => + match event { + WindowEvent::CloseRequested | WindowEvent::KeyboardInput { + input: KeyboardInput { + state: ElementState::Pressed, + virtual_keycode: Some(VirtualKeyCode::Escape), + .. + }, + .. + } => { + *control_flow = ControlFlow::Exit; + }, + WindowEvent::Resized(physical_size) => { + display_component.resize(*physical_size); + }, + WindowEvent::ScaleFactorChanged { new_inner_size, .. } => { + display_component.resize(**new_inner_size); + }, + _ => {}, + }, + Event::RedrawRequested(_) => { + display_component.update(); + match display_component.render() { + Ok(_) => {} + Err(wgpu::SurfaceError::Lost) => display_component.resize(display_component.size), + Err(wgpu::SurfaceError::OutOfMemory) => *control_flow = ControlFlow::ExitWithCode(1), + Err(e) => eprintln!("{:?}", e), + } + }, + Event::MainEventsCleared => { + window.request_redraw(); + // continue to process the game logic at the meantime + let mut world_lock = block_on(GWORLD.write()); + let world = world_lock.deref_mut(); + let mut schedules_lock = block_on(GSCHEDULES.write()); + let schedules = schedules_lock.deref_mut(); + schedules.run(world); + }, + _ => {}, + } + } + ); + } +} diff --git a/renderer/src/app/main.rs b/renderer/src/app/main.rs index 5f093d9..4074e82 100644 --- a/renderer/src/app/main.rs +++ b/renderer/src/app/main.rs @@ -1,18 +1,38 @@ -use winit::event::{ElementState, Event, KeyboardInput, VirtualKeyCode, WindowEvent}; -use winit::event_loop::{ControlFlow, EventLoop}; -use winit::window::WindowBuilder; -use darc_renderer::component::{Action, GPUComponent}; +pub mod context; + + +use std::sync::RwLock; + +use async_std::task::block_on; +use bevy_ecs::{world::World}; +use context::ApplicationContext; +use darc_renderer::component::{GWORLD, GSCHEDULES}; +use lazy_static::lazy_static; #[cfg(target_arch = "wasm32")] use wasm_bindgen::prelude::*; +lazy_static! { + static ref APPLICATION_CONTEXT: RwLock = RwLock::new(block_on(ApplicationContext::new())); +} + #[async_std::main] +#[cfg_attr(target_arch = "wasm32", wasm_bindgen(start))] async fn main() { - run().await; + initialize_world().await; + let mut schedule = GSCHEDULES.write().await; + schedule.add_system(main_loop_system); + drop(schedule); + + APPLICATION_CONTEXT.write().unwrap().run(); } -#[cfg_attr(target_arch = "wasm32", wasm_bindgen(start))] -pub async fn run() { +async fn initialize_world() { + initialize_logger(); + APPLICATION_CONTEXT.write().unwrap().initialize().await; +} + +fn initialize_logger() { cfg_if::cfg_if! { if #[cfg(target_arch = "wasm32")] { std::panic::set_hook(Box::new(console_error_panic_hook::hook)); @@ -21,108 +41,7 @@ pub async fn run() { env_logger::init(); } } - - let event_loop = EventLoop::new(); - let window = WindowBuilder::new().build(&event_loop).unwrap(); - - #[cfg(target_arch = "wasm32")] - { - wasm_bindgen_futures::spawn_local(async move { - // add canvas and then initialize gpu component with canvas handle - use winit::dpi::PhysicalSize; - use winit::platform::web::WindowExtWebSys; - web_sys::window() - .and_then(|win| win.document()) - .map(|doc| { - match doc.get_element_by_id("wasm-renderer") { - Some(dst) => { - window.set_inner_size(PhysicalSize::new(450, 400)); - let _ = dst.append_child(&web_sys::Element::from(window.canvas())); - } - None => { - window.set_inner_size(PhysicalSize::new(800, 800)); - let canvas = window.canvas(); - canvas.style().set_css_text( - "background-color: black; display: block; margin: 20px auto;", - ); - doc.body() - .map(|body| body.append_child(&web_sys::Element::from(canvas))); - } - }; - }) - .expect("Couldn't append canvas to document body."); - - let gpu = GPUComponent::new(&window).await; - - let run_closure = - Closure::once_into_js(move || start_event_loop(gpu, window, event_loop)); - - // Handle js exceptions. - // Otherwise the event loop will be stopped. - if let Err(error) = call_catch(&run_closure) { - let is_control_flow_exception = - error.dyn_ref::().map_or(false, |e| { - e.message().includes("Using exceptions for control flow", 0) - }); - - if !is_control_flow_exception { - web_sys::console::error_1(&error); - } - } - - // Bind js function to `call_catch` - #[wasm_bindgen] - extern "C" { - #[wasm_bindgen(catch, js_namespace = Function, js_name = "prototype.call.call")] - fn call_catch(this: &JsValue) -> Result<(), JsValue>; - } - }); - } - - #[cfg(not(target_arch = "wasm32"))] - { - let gpu = GPUComponent::new(&window).await; - start_event_loop(gpu, window, event_loop); - } } -fn start_event_loop(gpu: GPUComponent, window: winit::window::Window, event_loop: EventLoop<()>) { - let mut gpu = gpu; - event_loop.run( - move |event, _, control_flow| match event { - Event::WindowEvent { ref event, window_id } if window_id == window.id() && !gpu.input(event) => - match event { - WindowEvent::CloseRequested | WindowEvent::KeyboardInput { - input: KeyboardInput { - state: ElementState::Pressed, - virtual_keycode: Some(VirtualKeyCode::Escape), - .. - }, - .. - } => { - *control_flow = ControlFlow::Exit - }, - WindowEvent::Resized(physical_size) => { - gpu.resize(*physical_size); - }, - WindowEvent::ScaleFactorChanged { new_inner_size, .. } => { - gpu.resize(**new_inner_size); - }, - _ => {}, - }, - Event::RedrawRequested(_) => { - gpu.update(); - match gpu.render() { - Ok(_) => {} - Err(wgpu::SurfaceError::Lost) => gpu.resize(gpu.size), - Err(wgpu::SurfaceError::OutOfMemory) => *control_flow = ControlFlow::ExitWithCode(1), - Err(e) => eprintln!("{:?}", e), - } - }, - Event::MainEventsCleared => { - window.request_redraw(); - }, - _ => {}, - } - ); +fn main_loop_system(_world: &mut World) { } diff --git a/renderer/src/component/mod.rs b/renderer/src/component/mod.rs index bc27e3d..1c4cf4c 100644 --- a/renderer/src/component/mod.rs +++ b/renderer/src/component/mod.rs @@ -1,3 +1,7 @@ +use async_std::sync::RwLock; +use bevy_ecs::schedule::Schedule; +use bevy_ecs::world::World; +use lazy_static::lazy_static; use wgpu; use wgpu::AdapterInfo; use wgpu::util::DeviceExt; @@ -6,10 +10,25 @@ use winit::window::{Window, WindowId}; use crate::buffer::common::GPUBuffer; use crate::buffer::TRIANGLE_VERTICES; -pub struct GPUComponent { +lazy_static! { + pub static ref GWORLD: RwLock = RwLock::new(create_world()); + pub static ref GSCHEDULES: RwLock = RwLock::new(create_schedule()); +} + +fn create_world() -> World { + let world = World::default(); + world +} + +fn create_schedule() -> Schedule { + let schedule = Schedule::default(); + schedule +} + +pub struct DisplayComponent { pub surface: wgpu::Surface, pub device: wgpu::Device, - pub queue: wgpu::Queue, + pub queue: wgpu::Queue, pub config: wgpu::SurfaceConfiguration, pub size: winit::dpi::PhysicalSize, pub render_pipeline: wgpu::RenderPipeline, @@ -30,7 +49,7 @@ pub trait Action { fn render(&mut self) -> Result<(), wgpu::SurfaceError>; } -impl Action for GPUComponent { +impl Action for DisplayComponent { async fn new(window: &Window) -> Self { let size = window.inner_size(); let instance = wgpu::Instance::new(wgpu::InstanceDescriptor {