From 1bffbaeb3cda267bda5db6f78e45f7a432bfb58a Mon Sep 17 00:00:00 2001 From: Xevion Date: Sun, 14 Apr 2024 17:31:27 -0500 Subject: [PATCH] Switch back to Asyncify without Emscripten Main Loop callback --- .cargo/config.toml | 6 +- .vscode/settings.json | 5 ++ assets/fruit.png | Bin 0 -> 1509 bytes scripts/build.sh | 4 +- src/emscripten.rs | 102 ++++++++++++---------------------- src/main.rs | 125 ++++++++++++++++++++++++------------------ 6 files changed, 118 insertions(+), 124 deletions(-) create mode 100644 .vscode/settings.json create mode 100644 assets/fruit.png diff --git a/.cargo/config.toml b/.cargo/config.toml index 23676a6..d321f8a 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -3,9 +3,9 @@ rustflags = [ # "-O", "-C", "link-args=-O2 --profiling", #"-C", "link-args=-O3 --closure 1", - # "-C", "link-args=-sASYNCIFY -sALLOW_MEMORY_GROWTH=1", - "-C", "link-args=-sALLOW_MEMORY_GROWTH=1", + "-C", "link-args=-sASYNCIFY -sALLOW_MEMORY_GROWTH=1", + # "-C", "link-args=-sALLOW_MEMORY_GROWTH=1", "-C", "link-args=-sUSE_SDL=2 -sUSE_SDL_IMAGE=2 -sSDL2_IMAGE_FORMATS=['png']", # USE_OGG, USE_VORBIS for OGG/VORBIS usage - # "-C", "link-args=--preload-file ./assets/", + "-C", "link-args=--preload-file assets/", ] diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..352a626 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,5 @@ +{ + "rust-analyzer.linkedProjects": [ + "./Cargo.toml" + ] +} \ No newline at end of file diff --git a/assets/fruit.png b/assets/fruit.png new file mode 100644 index 0000000000000000000000000000000000000000..6f00a708c3a4c472c21b05e487d9ead0d366a9ac GIT binary patch literal 1509 zcmVb*W3|`llE<6DR9j}m7qz}{?3Mi;xCLLWmy39aD0R@v5NFS=?73e7P1f*alS1%Lq zYVRytC&~8SuWnLsyI%i%mOp)xy@zY;V+a@mJBxtZ*^%484FN--F#<3nU=f&HU$+m1 zriX`SFDzP{Rlp{6v*v3hy)^=eVgJ)Mo)qEp#4XR6!hGOfm04_; z37~B{FYNr^xStkzeC%>MS2X*$ii!XeFXic_<$`|Jh7B)xTz{4Vz}~g5v~B+Sf4SyS zdKe>m;a4vn`zinW{htg%WnaL!aphp_c;R#9Wu4TOT6Jh(1Q5=b5kRzB#*^Vj08KL< z0f0YEhjx0hTU_5F+QXh-N0ATVX}LO)hF&?LSg~}XBtBfv^7w;?4-+k}-525Cy1P)0 zjUW*R0{C&;VMwSHZ7zT^ZanZ`mnZ(xv*Qa9CRJr}Kh{WrDOC>LaU+0vY>WVUG>iad zX0eL|ASK6`?CL21&?GqKr^sw;6#(bqx%&%cSD)PBTpJJq_V~kdXx?+YV0#v-kk`K- zC%g6K>2315JJU%(sgZNokOrBfh6r^tx%ugvnI1e(H=1xk z;M{#9fHVS9Bi{uAXxrXAv6Vu`9j@c=08W41xuJmC-XRB|_59F+B5vV-JiiL+2d`Sg^G6B*j|6*or9zM>0M7Gkz)h9ql)ySF02l`q^TTcc zsSHXLfDu4|fDr%=$b=_>jR2IV=1}r)*#s~+1jq#_*>~po;X$FHbkS#^D{^xHb1kd$ z57cle0)X|Gu0N01ucE0K#Z%LV%4XfSK=~pB^9r$gIz0=)E1!zT>2T-1E=1%w!h$ z#Wi1|#%=)Ax111wHXz>*j1Hv$1__k0|2)LQRIG0~0NM&G5J0ZvCzXR(0T=-+q8B3o zY#%~`w-7*>13=q<_@ECL8q_0zG!gg{_JPNT!&ZrR@^p^6dh8#C&J}h8O0xb`C_h|} zve5Bqx}*Z&aF#4STt}^pxTft@hXBQSkP^>7G~WHT0k_bhIC~;+kb$``XQdZ54gqEd zM}`K~iMQIxzwIXgp$yk7!s11MP}j6=1TcHCX?+MFE%|5y5K6*?*V;q^C{Y9?LoRsb zGE@K~5&%gA!kmC+;YaENy#v509PU21|38-cDapa$Hyo)Gf$RYxb?i@}8BV+rzz7F{ zIPZ&(U(aHP;ox<0_KFrG&N&(ZaC{Tp&%&QL2Y?s+@(3UtR!jhDSfU3?-~Z=&P-8b> z)N=sb0MuT&e;~^MQD>w%fhujSSOIWEES;w!EHd8$EPoe3Haa7KPyqmHMF)Xo(0iAx z+9xeC#J*F~HmkH{1VH}C377QS1_E%?d(l#8CzGI@n@*F@wUa%6eM|+QS@?6Ip=g|B zA#PFDSd3&uxXd4gR1$(V=I3Ss;|#d!e;$i#DRH0K7KA63weSaZ)F0Qav8bXtjR0hZ zVg!)J$q3-V-Q{lk$!u@bQup^Fg)3D6&EB4ROOhL3N%p^*0^mAn_qPxM>LsCV`k#Z0 z0JhL@ZAf(x*aZTpgVfsBQeBp;{fMoDhJYb3AYcSw#$X8SFarMp36Dfwu)a?}00000 LNkvXXu0mjfn;fbJ literal 0 HcmV?d00001 diff --git a/scripts/build.sh b/scripts/build.sh index b8bd790..7fd0c07 100755 --- a/scripts/build.sh +++ b/scripts/build.sh @@ -59,8 +59,8 @@ cp assets/index.html dist cp $output_folder/pacman.wasm dist cp $output_folder/pacman.js dist # only if .data file exists -if [ -f $output_folder/pacman.data ]; then - cp $output_folder/pacman.data dist +if [ -f $output_folder/deps/pacman.data ]; then + cp $output_folder/deps/pacman.data dist fi if [ "$serve" = 'true' ]; then diff --git a/src/emscripten.rs b/src/emscripten.rs index 949bd40..e137cf8 100644 --- a/src/emscripten.rs +++ b/src/emscripten.rs @@ -1,76 +1,44 @@ -// SOURCE: https://users.rust-lang.org/t/sdl2-emscripten-asmjs-and-invalid-renderer-panic/66567/2 -// Based on emscripten.rs from https://github.com/therocode/rust_emscripten_main_loop -// This file interacts with the Emscripten API to provide a scheduling mechanism for main looping. +pub mod emscripten { + use std::cell::RefCell; + use std::os::raw::{c_float, c_int, c_void}; + use std::ptr::null_mut; + + #[allow(non_camel_case_types)] + type em_callback_func = unsafe extern "C" fn(); -// Since Emscripten only schedules the looping to be executed later by the browser, we need to make sure that the -// data object looped upon lives as long as the looping is scheduled, as well as being properly destroyed afterwards. + extern "C" { + pub fn emscripten_set_main_loop( + func: em_callback_func, + fps: c_int, + simulate_infinite_loop: c_int, + ); + pub fn emscripten_cancel_main_loop(); + pub fn emscripten_get_now() -> c_float; + } -// The Emscripten function used for this is emscripten_set_main_loop which will do the scheduling as well as terminate the current code flow -// to prevent scopes from being exited which would cause objects to be destroyed prematurely. To be able to destroy the data object properly -// as looping is terminated, the object is stored in thread_local storage. + thread_local!(static MAIN_LOOP_CALLBACK: RefCell<*mut c_void> = RefCell::new(null_mut())); -use std::cell::RefCell; -use std::os::raw::c_int; - -// Declare our FFI to the Emscripten functions we need. These will be linked in when building for Emscripten targets. -#[allow(non_camel_case_types)] -type em_callback_func = unsafe extern "C" fn(); - -extern "C" { - pub fn emscripten_set_main_loop( - func: em_callback_func, - fps: c_int, - simulate_infinite_loop: c_int, - ); - pub fn emscripten_cancel_main_loop(); -} - -thread_local! { - // This is where the data object will be kept during the scheduled looping. The storage structure is justified as follows - - // thread_local - we need it outside of function scope. thread_local is enough since we only expect interactions from the same thread. - // RefCell<..> - allows for mutable access from anywhere which we need to store and then terminate. Still borrow-checked in runtime. - // Option<..> - we don't always have anything scheduled - // Box - make it work generically for any closure passed in - - static MAIN_LOOP_CLOSURE: RefCell>> = RefCell::new(None); -} - -// Schedules the given callback to be run over and over in a loop until it returns MainLoopEvent::Terminate. -// Retains ownership of the passed callback -pub fn set_main_loop_callback(callback: F) { - // Move the callback into the data storage for safe-keeping - MAIN_LOOP_CLOSURE.with(|d| { - *d.borrow_mut() = Some(Box::new(callback)); - }); - - // Define a wrapper function that is compatible with the emscripten_set_main_loop function. - // This function will take care of extracting and executing our closure. - unsafe extern "C" fn wrapper() { - // Access and run the stashed away closure - MAIN_LOOP_CLOSURE.with(|z| { - if let Some(closure) = &mut *z.borrow_mut() { - (*closure)(); - } + pub fn set_main_loop_callback(callback: F) + where + F: FnMut(), + { + MAIN_LOOP_CALLBACK.with(|log| { + *log.borrow_mut() = &callback as *const _ as *mut c_void; }); - } - // Schedule the above wrapper function to be called regularly with Emscripten - unsafe { - emscripten_set_main_loop(wrapper::, 0, 1); - } -} + unsafe { + emscripten_set_main_loop(wrapper::, 0, 1); + } -// This is used to de-schedule the main loop function and destroy the kept closure object -pub fn cancel_main_loop() { - // De-schedule - unsafe { - emscripten_cancel_main_loop(); + unsafe extern "C" fn wrapper() + where + F: FnMut(), + { + MAIN_LOOP_CALLBACK.with(|z| { + let closure = *z.borrow_mut() as *mut F; + (*closure)(); + }); + } } - - // Remove the stored closure object - MAIN_LOOP_CLOSURE.with(|d| { - *d.borrow_mut() = None; - }); } \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index 347ef7b..ea73692 100644 --- a/src/main.rs +++ b/src/main.rs @@ -3,45 +3,51 @@ use std::process; use std::rc::Rc; use sdl2::event::Event; +use sdl2::image::LoadTexture; use sdl2::keyboard::Keycode; use sdl2::pixels::Color; -use sdl2::rect::Rect; +use sdl2::rect::{Point, Rect}; static BLACK: Color = Color::RGB(0, 0, 0); static WHITE: Color = Color::RGB(255, 255, 255); -// export EMCC_CFLAGS="-s USE_SDL=2" -// cargo build --target asmjs-unknown-emscripten && open index.html - #[cfg(target_family = "wasm")] pub mod emscripten; fn main() { + // #[cfg(target_os = "emscripten")] + // sdl2::hint::set("SDL_EMSCRIPTEN_ASYNCIFY","1"); + let ctx = sdl2::init().unwrap(); let video_ctx = ctx.video().unwrap(); let window = match video_ctx .window("Hello, Rust / SDL2 / WASM!", 640, 480) .position_centered() - .opengl() + // .opengl() .build() { Ok(window) => window, Err(err) => panic!("failed to create window: {}", err), }; - let canvas = match window.into_canvas().present_vsync().build() { + let canvas = match window.into_canvas().accelerated().present_vsync().build() { Ok(canvas) => canvas, Err(err) => panic!("failed to create canvas: {}", err), }; - let rect = Rect::new(0, 0, 10, 10); + let texture_creator = canvas.texture_creator(); + let mut point = Point::new(0, 0); let ctx = Rc::new(RefCell::new(ctx)); - let rect = Rc::new(RefCell::new(rect)); let canvas = Rc::new(RefCell::new(canvas)); + let texture_creator = Rc::new(texture_creator); - let main_loop = move || { + let fruit_atlas = texture_creator + .load_texture("./assets/fruit.png") + .expect("could not load texture"); + + let mut main_loop = move || { let mut moved = false; for event in ctx.borrow_mut().event_pump().unwrap().poll_iter() { match event { @@ -53,67 +59,82 @@ fn main() { process::exit(1); } Event::KeyDown { - keycode: Some(key), - .. - } => { - match key { - Keycode::Left => { - rect.borrow_mut().x -= 10; - moved = true; - } - Keycode::Right => { - rect.borrow_mut().x += 10; - moved = true; - } - Keycode::Up => { - rect.borrow_mut().y -= 10; - moved = true; - } - Keycode::Down => { - rect.borrow_mut().y += 10; - moved = true; - } - _ => {} + keycode: Some(key), .. + } => match key { + Keycode::Left => { + point.x -= 32; + moved = true; } - } + Keycode::Right => { + point.x += 32; + moved = true; + } + Keycode::Up => { + point.y -= 32; + moved = true; + } + Keycode::Down => { + point.y += 32; + moved = true; + } + _ => {} + }, _ => {} } } // Handle wrapping at the edges if moved { - let mut rect = rect.borrow_mut(); let canvas_size = canvas.borrow().window().size(); - if rect.x < 0 { - rect.x = canvas_size.0 as i32 - rect.width() as i32; - } else if rect.x >= 640 { - rect.x = 0; + if point.x < 0 { + point.x = canvas_size.0 as i32 - 32; + } else if point.x >= 640 { + point.x = 0; } - if rect.y < 0 { - rect.y = canvas_size.1 as i32 - rect.height() as i32; - } else if rect.y >= canvas_size.1 as i32 { - rect.y = 0; + if point.y < 0 { + point.y = canvas_size.1 as i32 - 32; + } else if point.y >= canvas_size.1 as i32 { + point.y = 0; } } let mut canvas = canvas.borrow_mut(); + canvas.set_draw_color(BLACK); canvas.clear(); - canvas.set_draw_color(WHITE); - canvas.fill_rect(rect.borrow().clone()).expect("could not draw rectangle"); + + canvas + .copy_ex( + &fruit_atlas, + Rect::new(0, 0, 32, 32), + Rect::new(point.x, point.y, 32, 32), + 0.0, + Some(Point::new(0, 0)), + false, + false, + ) + .expect("could not draw texture"); + canvas.present(); }; - #[cfg(target_family = "wasm")] - emscripten::set_main_loop_callback(main_loop); + // #[cfg(target_family = "wasm")] + // { + // use crate::emscripten::emscripten::set_main_loop_callback; + // set_main_loop_callback(main_loop); + // } - #[cfg(not(target_family = "wasm"))] - { - use std::thread::sleep; - use std::time::Duration; - loop { - main_loop(); - sleep(Duration::from_millis(10)) - } + // #[cfg(not(target_family = "wasm"))] + // { + // use std::thread::sleep; + // use std::time::Duration; + // loop { + // main_loop(); + // sleep(Duration::from_millis(10)) + // } + // } + + loop { + main_loop(); } }