diff --git a/game/main.ts b/game/main.ts index 802378b0..ce813849 100644 --- a/game/main.ts +++ b/game/main.ts @@ -1,48 +1,26 @@ -import { cls, print, spr, use_texture, Texture } from "./graphics"; +import { cls, print, spr, use_texture } from "./graphics"; import { load_texture } from "./assets"; import { since_start } from "./time"; import { btn, Button } from "./input"; -// import { load_string } from "./io"; import { new_v2, vadd, vmul, vnorm } from "./vector"; -/// TODO: Support reload by saving and restoring state from init/update/restore. - /// A nice looping frame counter. let clock = 0; -let bot_sprite: Texture | undefined = undefined; +let bot_sprite: number | undefined = undefined; // Note zelda overworld is 16x8 screens // zelda screen is 16x11 tiles // from a feeling point of view this is sufficient, apparently :D -let loaded = false; +export function init() { + print("Hello world!"); -async function load_map(path: string) { - // print("Loading map:", path); - // const blob = await load_string(path); - // const map = JSON.parse(blob); - // print("Loaded map:", map); -} - -function load_assets() { // Start this load, but then... - let texture_load = load_texture("./bot.png").then((n) => { + load_texture("./bot.png").then((n) => { print("Bot loaded at", since_start()); bot_sprite = n; }); - - let map_load = load_map("./overworld.ldtk"); - - Promise.all([texture_load, map_load]).then(() => { - loaded = true; - print("All are loaded."); - }); -} - -export function init() { - print("Hello world!"); - load_assets(); } const friction = 0.6; @@ -102,10 +80,6 @@ const robo_info = { export function draw() { cls(0.1, 0.2, 0.3); - if (!loaded) { - return; - } - if (bot_sprite != undefined) { // ...it gets resolved here? use_texture(bot_sprite); diff --git a/oden-js/src/lib.rs b/oden-js/src/lib.rs index cf11d155..6d6397c5 100644 --- a/oden-js/src/lib.rs +++ b/oden-js/src/lib.rs @@ -16,7 +16,7 @@ pub use atom::{Atom, AtomRef}; pub use class::{Class, ClassID}; pub use context::{Context, ContextRef, EvalFlags}; pub use conversion::*; -pub use promise::{DefaultRejectedPromiseTracker, Promise, RejectedPromiseTracker}; +pub use promise::Promise; pub use runtime::Runtime; pub use value::{Value, ValueRef, ValueType}; @@ -77,12 +77,6 @@ impl From for Error { } } -impl From for Error { - fn from(e: std::str::Utf8Error) -> Self { - Error::ConversionError(e.to_string()) - } -} - pub type Result = core::result::Result; pub type ValueResult = core::result::Result; diff --git a/oden-js/src/promise.rs b/oden-js/src/promise.rs index 03cabee8..8ee23b26 100644 --- a/oden-js/src/promise.rs +++ b/oden-js/src/promise.rs @@ -1,4 +1,4 @@ -use crate::{ContextRef, ValueRef, ValueResult}; +use crate::{ContextRef, ValueResult}; use std::sync::atomic::{AtomicU64, Ordering}; use std::sync::mpsc::Sender; @@ -72,53 +72,3 @@ impl Drop for Promise { assert!(self.complete); } } - -pub trait RejectedPromiseTracker { - fn on_rejected_promise( - &self, - ctx: &ContextRef, - promise: &ValueRef, - reason: &ValueRef, - is_handled: bool, - ); -} - -impl RejectedPromiseTracker for T -where - T: Fn(&ContextRef, &ValueRef, &ValueRef, bool) -> (), -{ - fn on_rejected_promise( - &self, - ctx: &ContextRef, - promise: &ValueRef, - reason: &ValueRef, - is_handled: bool, - ) { - self(ctx, promise, reason, is_handled); - } -} - -pub struct DefaultRejectedPromiseTracker {} - -impl DefaultRejectedPromiseTracker { - pub fn new() -> Self { - DefaultRejectedPromiseTracker {} - } -} - -impl RejectedPromiseTracker for DefaultRejectedPromiseTracker { - fn on_rejected_promise( - &self, - ctx: &ContextRef, - _promise: &ValueRef, - reason: &ValueRef, - is_handled: bool, - ) { - if !is_handled { - let reason_str = reason.to_string(ctx).expect( - "Unhandled rejected promise: reason unknown: unable to convert reason to string", - ); - panic!("Unhandled rejected promise: {reason_str}"); - } - } -} diff --git a/oden-js/src/runtime.rs b/oden-js/src/runtime.rs index 5c1fb4ab..e349d955 100644 --- a/oden-js/src/runtime.rs +++ b/oden-js/src/runtime.rs @@ -1,8 +1,7 @@ use crate::{ module::loader::{load_module, DefaultModuleLoader, ModuleLoader}, promise::{PromiseEvent, PromiseHandle}, - ContextRef, DefaultRejectedPromiseTracker, Promise, RejectedPromiseTracker, Result, Value, - ValueRef, + ContextRef, Promise, Result, Value, }; use oden_js_sys as sys; use std::cell::{RefCell, RefMut}; @@ -46,19 +45,20 @@ struct PrivateState { promise_send: Sender<(PromiseHandle, PromiseEvent)>, promise_recv: Receiver<(PromiseHandle, PromiseEvent)>, promise_table: HashMap, // ! - rejection_tracker: Arc>, } impl PrivateState { - pub fn new() -> Box> { + pub fn new(loader: T) -> Box> + where + T: ModuleLoader + 'static, + { let (send, recv) = channel(); Box::new(RefCell::new(PrivateState { refs: 1, - loader: Arc::new(Box::new(DefaultModuleLoader::new())), + loader: Arc::new(Box::new(loader)), promise_send: send, promise_recv: recv, promise_table: HashMap::new(), - rejection_tracker: Arc::new(Box::new(DefaultRejectedPromiseTracker::new())), })) } @@ -92,28 +92,6 @@ impl PrivateState { let context = ContextRef::from_raw(ctx); load_module(&context, path, &loader) } - - unsafe extern "C" fn promise_rejection_tracker( - ctx: *mut sys::JSContext, - promise: sys::JSValue, - reason: sys::JSValue, - is_handled: i32, - opaque: *mut std::os::raw::c_void, - ) { - let ctx = ContextRef::from_raw(ctx); - let promise = ValueRef::from_raw(promise); - let reason = ValueRef::from_raw(reason); - let is_handled = is_handled != 0; - let handler = unsafe { - let ptr = opaque as *const RefCell; - ptr.as_ref() - .expect("We already know this runtime is one of ours!") - .borrow() - .rejection_tracker - .clone() - }; - handler.on_rejected_promise(&ctx, &promise, &reason, is_handled); - } } #[derive(Debug)] @@ -123,17 +101,16 @@ pub struct Runtime { impl Runtime { pub fn new() -> Runtime { - let state = PrivateState::new(); + Self::with_loader(DefaultModuleLoader::new()) + } + + pub fn with_loader(loader: TLoader) -> Runtime { + let state = PrivateState::new(loader); let rt = unsafe { let rt = sys::JS_NewRuntime(); let state = Box::into_raw(state) as *mut _; sys::JS_SetRuntimeOpaque(rt, state); sys::JS_SetModuleLoaderFunc(rt, None, Some(PrivateState::module_loader), state); - sys::JS_SetHostPromiseRejectionTracker( - rt, - Some(PrivateState::promise_rejection_tracker), - state, - ); rt }; Runtime { rt } @@ -164,26 +141,6 @@ impl Runtime { } } - /// Set the handler for loading modules. By default this is an instance - /// of `DefaultModuleLoader`. - pub fn set_module_loader(&mut self, loader: T) - where - T: ModuleLoader + 'static, - { - let mut state = unsafe { PrivateState::from_rt_mut(self.rt) }; - state.loader = Arc::new(Box::new(loader)); - } - - /// Set a tracker to be notified whenever a promise is rejected. By - /// default, this is an instance of `DefaultRejectedPromiseHandler`. - pub fn set_rejected_promise_tracker(&mut self, tracker: T) - where - T: RejectedPromiseTracker + 'static, - { - let mut state = unsafe { PrivateState::from_rt_mut(self.rt) }; - state.rejection_tracker = Arc::new(Box::new(tracker)); - } - pub fn run_gc(&mut self) { unsafe { sys::JS_RunGC(self.rt); diff --git a/src/assets.ts b/src/assets.ts index b375d450..fe1dd156 100644 --- a/src/assets.ts +++ b/src/assets.ts @@ -1,7 +1,7 @@ import * as io from "./io.ts"; import * as gfx from "./graphics.ts"; -export async function load_texture(path: string): Promise { +export async function load_texture(path: string): Promise { const buffer = await io.load(path); return gfx.create_texture(buffer, path); } diff --git a/src/graphics.ts b/src/graphics.ts index 06ce66f3..185413b2 100644 --- a/src/graphics.ts +++ b/src/graphics.ts @@ -52,16 +52,6 @@ export function spr( core.spr(x, y, w, h, sx, sy, sw, sh); } -export class Texture { - #id: number; - constructor(id: number) { - this.#id = id; - } - id(): number { - return this.#id; - } -} - /** * Create a texture based on the loaded buffer. * @@ -71,41 +61,15 @@ export class Texture { export function create_texture( buffer: ArrayBuffer, label: string | undefined = undefined -): Texture { - const id = core.create_texture(buffer, label); - return new Texture(id); +): number { + return core.create_texture(buffer, label); } /** * Set the specified texture as the current texture for calls to e.g. spr(). * - * @param texture - The texture to use. + * @param id - The identifier of the texture to use. */ -export function use_texture(id: Texture) { - core.use_texture(id.id()); -} - -/** - * Create a texture that we can render to. - */ -export function create_writable_texture( - width: number, - height: number, - label: string | undefined = undefined -): Texture { - return new Texture(core.create_writable_texture(width, height, label)); -} - -/** - * Set the current render target to the screen. - */ -export function write_to_screen() { - core.write_to_screen(); -} - -/** - * Set the current render target to the specified texture. - */ -export function write_to_texture(texture: Texture) { - core.write_to_texture(texture.id()); +export function use_texture(id: number) { + core.use_texture(id); } diff --git a/src/io.ts b/src/io.ts index 7131bc94..e95fada6 100644 --- a/src/io.ts +++ b/src/io.ts @@ -6,12 +6,6 @@ import * as core from "io-core"; * @param path The path of the file to load. * @returns The contents of the file. */ -export const load = core.load; - -/** - * Load the specified file into memory as a string. - * - * @param path The path of the file to load. - * @returns The contents of the file decoded from utf-8. - */ -export const load_string = core.load_string; +export function load(path: string): Promise { + return core.load(path); +} diff --git a/src/lib.rs b/src/lib.rs index 5f0493bd..47b3c23b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,16 +1,10 @@ use bytemuck; use std::collections::HashMap; -use std::rc::Rc; use std::sync::mpsc::Receiver; use std::time::Instant; use tracy_client::{frame_mark, set_thread_name, span}; use wgpu::util::DeviceExt; -use winit::{ - event::*, - event_loop::{EventLoopBuilder, EventLoopProxy}, - window::Window, - window::WindowBuilder, -}; +use winit::{event::*, event_loop::EventLoop, window::Window, window::WindowBuilder}; mod script; use script::graphics::GraphicsCommand; @@ -73,8 +67,6 @@ struct State { sprite_bind_group_layout: wgpu::BindGroupLayout, sprite_textures: HashMap, - write_textures: HashMap, - screen_uniform: ScreenUniforms, screen_uniform_buffer: wgpu::Buffer, screen_uniform_bind_group: wgpu::BindGroup, @@ -282,7 +274,6 @@ impl State { max_vertices, sprite_bind_group_layout, sprite_textures: HashMap::new(), - write_textures: HashMap::new(), screen_uniform, screen_uniform_buffer, screen_uniform_bind_group, @@ -317,19 +308,14 @@ impl State { fn render(&mut self, commands: Vec) -> Result<(), wgpu::SurfaceError> { let _span = span!("context render"); let output = self.surface.get_current_texture()?; + let view = output + .texture + .create_view(&wgpu::TextureViewDescriptor::default()); // Group the commands into passes. - let screen_view = Rc::new( - output - .texture - .create_view(&wgpu::TextureViewDescriptor::default()), - ); - let mut last_view = screen_view.clone(); - struct Pass { color: Option<[f64; 4]>, commands: Vec, - target: Rc, } let mut passes = Vec::new(); for command in commands { @@ -337,9 +323,7 @@ impl State { GraphicsCommand::Clear(cc) => passes.push(Pass { color: Some(cc.color), commands: Vec::new(), - target: last_view.clone(), }), - GraphicsCommand::CreateTexture(ct) => { let texture = texture::Texture::from_image( &self.device, @@ -371,93 +355,12 @@ impl State { self.sprite_textures.insert(ct.id, sprite_bind_group); } - - GraphicsCommand::CreateWritableTexture { - id, - width, - height, - label, - } => { - let texture = texture::Texture::new_writable( - &self.device, - width, - height, - match &label { - Some(l) => Some(&l), - None => None, - }, - ); - let sprite_bind_group = - self.device.create_bind_group(&wgpu::BindGroupDescriptor { - layout: &self.sprite_bind_group_layout, - entries: &[ - wgpu::BindGroupEntry { - binding: 0, - resource: wgpu::BindingResource::TextureView(&texture.view), - }, - wgpu::BindGroupEntry { - binding: 1, - resource: wgpu::BindingResource::Sampler(&texture.sampler), - }, - ], - label: match &label { - Some(l) => Some(&l), - None => None, - }, - }); - - self.sprite_textures.insert(id, sprite_bind_group); - self.write_textures.insert(id, texture); - } - - GraphicsCommand::WriteToTexture(id) => { - let texture = self.write_textures.get(&id).unwrap(); - last_view = Rc::new( - texture - .texture - .create_view(&wgpu::TextureViewDescriptor::default()), - ); - let new_pass = match passes.last_mut() { - Some(pass) => { - if pass.commands.is_empty() { - pass.target = last_view.clone(); - false - } else { - true - } - } - None => true, - }; - if new_pass { - passes.push(Pass { - color: None, - commands: vec![], - target: last_view.clone(), - }) - } - } - - GraphicsCommand::WriteToScreen => { - if !Rc::ptr_eq(&last_view, &screen_view) { - // If I have a pass already I need a new one. - last_view = screen_view.clone(); - if !passes.is_empty() { - passes.push(Pass { - color: None, - commands: vec![], - target: last_view.clone(), - }) - } - } - } - GraphicsCommand::EndFrame => (), other => match passes.last_mut() { Some(pass) => pass.commands.push(other), None => passes.push(Pass { color: None, commands: vec![other], - target: last_view.clone(), }), }, } @@ -480,7 +383,7 @@ impl State { let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { label: Some("Render Pass"), color_attachments: &[Some(wgpu::RenderPassColorAttachment { - view: &pass.target, + view: &view, resolve_target: None, ops: wgpu::Operations { load: if let Some([r, g, b, a]) = pass.color { @@ -535,11 +438,7 @@ impl State { } GraphicsCommand::UseTexture(id) => texture_id = Some(id), - GraphicsCommand::CreateTexture(_) => (), // Already handled - GraphicsCommand::CreateWritableTexture { .. } => (), // Already handled - GraphicsCommand::WriteToTexture(_) => (), // Already handled - GraphicsCommand::WriteToScreen => (), // Already handled GraphicsCommand::Clear(_) => (), // Already handled GraphicsCommand::EndFrame => (), // Should never appear } @@ -573,19 +472,15 @@ impl State { } } -enum OdenEvent { - Close, -} - struct UIEvent { - winit: Event<'static, OdenEvent>, + winit: Event<'static, ()>, #[allow(unused)] time: Instant, } // TODO: flume? (https://docs.rs/flume/latest/flume/) -fn main_thread(event_loop: EventLoopProxy, state: State, reciever: Receiver) { +fn main_thread(state: State, reciever: Receiver) { let mut state = state; let mut script = script::ScriptContext::new(); script.init(); @@ -605,10 +500,6 @@ fn main_thread(event_loop: EventLoopProxy, state: State, reciever: Re } if window_id == state.window().id() => { if !script.input(event) { match event { - WindowEvent::CloseRequested => { - let _ = event_loop.send_event(OdenEvent::Close); - } - WindowEvent::CursorMoved { position, .. } => { state.mouse_x = position.x; state.mouse_y = position.y; @@ -666,35 +557,27 @@ pub async fn run() { set_thread_name!("ui thread"); env_logger::init(); - let event_loop = EventLoopBuilder::::with_user_event().build(); + let event_loop = EventLoop::new(); let window = WindowBuilder::new().build(&event_loop).unwrap(); - let event_loop_proxy = event_loop.create_proxy(); let state = State::new(window).await; let (sender, reciever) = std::sync::mpsc::channel(); std::thread::spawn(move || { set_thread_name!("game thread"); - main_thread(event_loop_proxy, state, reciever); + main_thread(state, reciever); }); event_loop.run(move |event, _, control_flow| { control_flow.set_wait(); - match event { - Event::UserEvent(OdenEvent::Close) => { - control_flow.set_exit(); - } - _ => { - if let Some(e) = event.to_static() { - sender - .send(UIEvent { - winit: e, - time: Instant::now(), - }) - .unwrap(); - } - } + if let Some(e) = event.to_static() { + sender + .send(UIEvent { + winit: e, + time: Instant::now(), + }) + .unwrap(); } }); } diff --git a/src/script.rs b/src/script.rs index 5d9b0c2e..4d38a705 100644 --- a/src/script.rs +++ b/src/script.rs @@ -56,8 +56,7 @@ pub struct ScriptContext { impl ScriptContext { pub fn new() -> Self { - let mut runtime = Runtime::new(); - runtime.set_module_loader(Loader::new()); + let runtime = Runtime::with_loader(Loader::new()); let mut context = Context::new(runtime); context.add_intrinsic_bigfloat(); diff --git a/src/script/graphics.rs b/src/script/graphics.rs index b55b5d93..b3efe8d0 100644 --- a/src/script/graphics.rs +++ b/src/script/graphics.rs @@ -40,15 +40,7 @@ pub enum GraphicsCommand { Print(PrintCommand), Sprite(SpriteCommand), CreateTexture(CreateTextureCommand), - CreateWritableTexture { - id: u32, - width: u32, - height: u32, - label: Option, - }, UseTexture(u32), - WriteToTexture(u32), - WriteToScreen, EndFrame, } @@ -117,31 +109,6 @@ impl GraphicsImpl { fn use_texture(&self, id: u32) { let _ = self.sender.send(GraphicsCommand::UseTexture(id)); } - - fn create_writable_texture( - &self, - width: u32, - height: u32, - label: Option, - ) -> Result { - let id = self.next_texture_id.fetch_add(1, Ordering::SeqCst); - let _ = self.sender.send(GraphicsCommand::CreateWritableTexture { - id, - width, - height, - label, - }); - - Ok(id) - } - - fn write_to_screen(&self) { - let _ = self.sender.send(GraphicsCommand::WriteToScreen); - } - - fn write_to_texture(&self, id: u32) { - let _ = self.sender.send(GraphicsCommand::WriteToTexture(id)); - } } pub struct GraphicsAPI { @@ -201,32 +168,6 @@ impl GraphicsAPI { )?, )?; } - { - let gfx = gfx.clone(); - builder.export( - "create_writable_texture", - ctx.new_fn( - move |_: &ContextRef, width: u32, height: u32, label: Option| { - gfx.create_writable_texture(width, height, label) - }, - )?, - )?; - } - { - let gfx = gfx.clone(); - builder.export( - "write_to_screen", - ctx.new_fn(move |_: &ContextRef| gfx.write_to_screen())?, - )?; - } - { - let gfx = gfx.clone(); - builder.export( - "write_to_texture", - ctx.new_fn(move |_: &ContextRef, id: u32| gfx.write_to_texture(id))?, - )?; - } - builder.build("graphics-core")?; Ok(GraphicsAPI { gfx }) } diff --git a/src/script/io.rs b/src/script/io.rs index ba3521fc..d508c040 100644 --- a/src/script/io.rs +++ b/src/script/io.rs @@ -82,24 +82,6 @@ impl IoImpl { Ok(value) } - - fn load_string(&self, context: &ContextRef, path: &str) -> ValueResult { - let (value, promise) = context.new_promise()?; - - let path = path.to_string(); - self.thread_pool.execute(Box::new(move || { - let path = path; - - let result = resolve_path(&path, &[]).and_then(|p| std::fs::read(p)); - promise.resolve(move |ctx: &ContextRef| { - let result = result?; - let string = std::str::from_utf8(&result)?; - ctx.new_string(string) - }); - })); - - Ok(value) - } } pub struct IoAPI {} @@ -115,13 +97,6 @@ impl IoAPI { ctx.new_fn(move |ctx: &ContextRef, p: String| io.load(ctx, &p))?, )?; } - { - let io = io.clone(); - builder.export( - "load_string", - ctx.new_fn(move |ctx: &ContextRef, p: String| io.load_string(ctx, &p))?, - )?; - } builder.build("io-core")?; Ok(IoAPI {}) } diff --git a/src/texture.rs b/src/texture.rs index ae0e20ef..eb1407e0 100644 --- a/src/texture.rs +++ b/src/texture.rs @@ -7,46 +7,15 @@ pub struct Texture { } impl Texture { - pub fn new_writable( - device: &wgpu::Device, - width: u32, - height: u32, - label: Option<&str>, - ) -> Self { - let size = wgpu::Extent3d { - width, - height, - depth_or_array_layers: 1, - }; - - let texture = device.create_texture(&wgpu::TextureDescriptor { - label, - size, - mip_level_count: 1, - sample_count: 1, - dimension: wgpu::TextureDimension::D2, - format: wgpu::TextureFormat::Rgba8UnormSrgb, - usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::RENDER_ATTACHMENT, - view_formats: &[], - }); - - let view = texture.create_view(&wgpu::TextureViewDescriptor::default()); - let sampler = device.create_sampler(&wgpu::SamplerDescriptor { - address_mode_u: wgpu::AddressMode::ClampToEdge, - address_mode_v: wgpu::AddressMode::ClampToEdge, - address_mode_w: wgpu::AddressMode::ClampToEdge, - mag_filter: wgpu::FilterMode::Linear, - min_filter: wgpu::FilterMode::Nearest, - mipmap_filter: wgpu::FilterMode::Nearest, - ..Default::default() - }); - - Self { - texture, - view, - sampler, - } - } + // pub fn from_bytes( + // device: &wgpu::Device, + // queue: &wgpu::Queue, + // bytes: &[u8], + // label: &str, + // ) -> Result { + // let img = image::load_from_memory(bytes)?; + // Ok(Self::from_image(device, queue, &img, Some(label))) + // } pub fn from_image( device: &wgpu::Device, @@ -62,7 +31,6 @@ impl Texture { height: dimensions.1, depth_or_array_layers: 1, }; - let texture = device.create_texture(&wgpu::TextureDescriptor { label, size, diff --git a/types/graphics-core.d.ts b/types/graphics-core.d.ts index 45cf9b2c..3fde39fd 100644 --- a/types/graphics-core.d.ts +++ b/types/graphics-core.d.ts @@ -1,9 +1,7 @@ // These are the functions exposed by the native graphics module. // export function cls(r: number, g: number, b: number); - export function print(msg: string); - export function spr( x: number, y: number, @@ -14,20 +12,8 @@ export function spr( sw: number, sh: number ); - export function create_texture( buffer: ArrayBuffer, label: string | undefined ): number; - export function use_texture(id: number); - -export function create_writable_texture( - width: number, - height: number, - label: string | undefined -): number; - -export function write_to_screen(); - -export function write_to_texture(id: number); diff --git a/types/io-core.d.ts b/types/io-core.d.ts index 283b70df..e5114f02 100644 --- a/types/io-core.d.ts +++ b/types/io-core.d.ts @@ -1,5 +1,3 @@ // These are the functions exposed by the native IO module. // export function load(path: string): Promise; - -export function load_string(path: string): Promise;