Skip to content

Commit

Permalink
refact: integrating with bevy_ecs crate
Browse files Browse the repository at this point in the history
  • Loading branch information
DarcJC committed May 25, 2023
1 parent ec099da commit 49ea839
Show file tree
Hide file tree
Showing 5 changed files with 210 additions and 112 deletions.
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
1 change: 1 addition & 0 deletions renderer/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
158 changes: 158 additions & 0 deletions renderer/src/app/context.rs
Original file line number Diff line number Diff line change
@@ -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<winit::event_loop::EventLoop<()>>,
window: Option<winit::window::Window>,
display_component: Option<darc_renderer::component::DisplayComponent>,
}

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::<js_sys::Error>().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);
},
_ => {},
}
}
);
}
}
137 changes: 28 additions & 109 deletions renderer/src/app/main.rs
Original file line number Diff line number Diff line change
@@ -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<ApplicationContext> = 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));
Expand All @@ -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::<js_sys::Error>().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) {
}
25 changes: 22 additions & 3 deletions renderer/src/component/mod.rs
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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<World> = RwLock::new(create_world());
pub static ref GSCHEDULES: RwLock<Schedule> = 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<u32>,
pub render_pipeline: wgpu::RenderPipeline,
Expand All @@ -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 {
Expand Down

0 comments on commit 49ea839

Please sign in to comment.