Compare commits

..

No commits in common. "22327a71b3ad61f0a8d0b0087b9d27d88787c2bc" and "29b1a854c578a10f1ac56cb5001d58f37e2b7141" have entirely different histories.

14 changed files with 53 additions and 470 deletions

View file

@ -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 { load_texture } from "./assets";
import { since_start } from "./time"; import { since_start } from "./time";
import { btn, Button } from "./input"; import { btn, Button } from "./input";
// import { load_string } from "./io";
import { new_v2, vadd, vmul, vnorm } from "./vector"; 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. /// A nice looping frame counter.
let clock = 0; let clock = 0;
let bot_sprite: Texture | undefined = undefined; let bot_sprite: number | undefined = undefined;
// Note zelda overworld is 16x8 screens // Note zelda overworld is 16x8 screens
// zelda screen is 16x11 tiles // zelda screen is 16x11 tiles
// from a feeling point of view this is sufficient, apparently :D // 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... // 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()); print("Bot loaded at", since_start());
bot_sprite = n; 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; const friction = 0.6;
@ -102,10 +80,6 @@ const robo_info = {
export function draw() { export function draw() {
cls(0.1, 0.2, 0.3); cls(0.1, 0.2, 0.3);
if (!loaded) {
return;
}
if (bot_sprite != undefined) { if (bot_sprite != undefined) {
// ...it gets resolved here? // ...it gets resolved here?
use_texture(bot_sprite); use_texture(bot_sprite);

View file

@ -16,7 +16,7 @@ pub use atom::{Atom, AtomRef};
pub use class::{Class, ClassID}; pub use class::{Class, ClassID};
pub use context::{Context, ContextRef, EvalFlags}; pub use context::{Context, ContextRef, EvalFlags};
pub use conversion::*; pub use conversion::*;
pub use promise::{DefaultRejectedPromiseTracker, Promise, RejectedPromiseTracker}; pub use promise::Promise;
pub use runtime::Runtime; pub use runtime::Runtime;
pub use value::{Value, ValueRef, ValueType}; pub use value::{Value, ValueRef, ValueType};
@ -77,12 +77,6 @@ impl From<std::io::Error> for Error {
} }
} }
impl From<std::str::Utf8Error> for Error {
fn from(e: std::str::Utf8Error) -> Self {
Error::ConversionError(e.to_string())
}
}
pub type Result<T> = core::result::Result<T, Error>; pub type Result<T> = core::result::Result<T, Error>;
pub type ValueResult = core::result::Result<Value, Error>; pub type ValueResult = core::result::Result<Value, Error>;

View file

@ -1,4 +1,4 @@
use crate::{ContextRef, ValueRef, ValueResult}; use crate::{ContextRef, ValueResult};
use std::sync::atomic::{AtomicU64, Ordering}; use std::sync::atomic::{AtomicU64, Ordering};
use std::sync::mpsc::Sender; use std::sync::mpsc::Sender;
@ -72,53 +72,3 @@ impl Drop for Promise {
assert!(self.complete); assert!(self.complete);
} }
} }
pub trait RejectedPromiseTracker {
fn on_rejected_promise(
&self,
ctx: &ContextRef,
promise: &ValueRef,
reason: &ValueRef,
is_handled: bool,
);
}
impl<T> 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}");
}
}
}

View file

@ -1,8 +1,7 @@
use crate::{ use crate::{
module::loader::{load_module, DefaultModuleLoader, ModuleLoader}, module::loader::{load_module, DefaultModuleLoader, ModuleLoader},
promise::{PromiseEvent, PromiseHandle}, promise::{PromiseEvent, PromiseHandle},
ContextRef, DefaultRejectedPromiseTracker, Promise, RejectedPromiseTracker, Result, Value, ContextRef, Promise, Result, Value,
ValueRef,
}; };
use oden_js_sys as sys; use oden_js_sys as sys;
use std::cell::{RefCell, RefMut}; use std::cell::{RefCell, RefMut};
@ -46,19 +45,20 @@ struct PrivateState {
promise_send: Sender<(PromiseHandle, PromiseEvent)>, promise_send: Sender<(PromiseHandle, PromiseEvent)>,
promise_recv: Receiver<(PromiseHandle, PromiseEvent)>, promise_recv: Receiver<(PromiseHandle, PromiseEvent)>,
promise_table: HashMap<PromiseHandle, PromiseEntry>, // ! promise_table: HashMap<PromiseHandle, PromiseEntry>, // !
rejection_tracker: Arc<Box<dyn RejectedPromiseTracker>>,
} }
impl PrivateState { impl PrivateState {
pub fn new() -> Box<RefCell<Self>> { pub fn new<T>(loader: T) -> Box<RefCell<Self>>
where
T: ModuleLoader + 'static,
{
let (send, recv) = channel(); let (send, recv) = channel();
Box::new(RefCell::new(PrivateState { Box::new(RefCell::new(PrivateState {
refs: 1, refs: 1,
loader: Arc::new(Box::new(DefaultModuleLoader::new())), loader: Arc::new(Box::new(loader)),
promise_send: send, promise_send: send,
promise_recv: recv, promise_recv: recv,
promise_table: HashMap::new(), promise_table: HashMap::new(),
rejection_tracker: Arc::new(Box::new(DefaultRejectedPromiseTracker::new())),
})) }))
} }
@ -92,28 +92,6 @@ impl PrivateState {
let context = ContextRef::from_raw(ctx); let context = ContextRef::from_raw(ctx);
load_module(&context, path, &loader) 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<PrivateState>;
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)] #[derive(Debug)]
@ -123,17 +101,16 @@ pub struct Runtime {
impl Runtime { impl Runtime {
pub fn new() -> Runtime { pub fn new() -> Runtime {
let state = PrivateState::new(); Self::with_loader(DefaultModuleLoader::new())
}
pub fn with_loader<TLoader: ModuleLoader + 'static>(loader: TLoader) -> Runtime {
let state = PrivateState::new(loader);
let rt = unsafe { let rt = unsafe {
let rt = sys::JS_NewRuntime(); let rt = sys::JS_NewRuntime();
let state = Box::into_raw(state) as *mut _; let state = Box::into_raw(state) as *mut _;
sys::JS_SetRuntimeOpaque(rt, state); sys::JS_SetRuntimeOpaque(rt, state);
sys::JS_SetModuleLoaderFunc(rt, None, Some(PrivateState::module_loader), state); sys::JS_SetModuleLoaderFunc(rt, None, Some(PrivateState::module_loader), state);
sys::JS_SetHostPromiseRejectionTracker(
rt,
Some(PrivateState::promise_rejection_tracker),
state,
);
rt rt
}; };
Runtime { 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<T>(&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<T>(&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) { pub fn run_gc(&mut self) {
unsafe { unsafe {
sys::JS_RunGC(self.rt); sys::JS_RunGC(self.rt);

View file

@ -1,7 +1,7 @@
import * as io from "./io.ts"; import * as io from "./io.ts";
import * as gfx from "./graphics.ts"; import * as gfx from "./graphics.ts";
export async function load_texture(path: string): Promise<gfx.Texture> { export async function load_texture(path: string): Promise<number> {
const buffer = await io.load(path); const buffer = await io.load(path);
return gfx.create_texture(buffer, path); return gfx.create_texture(buffer, path);
} }

View file

@ -52,16 +52,6 @@ export function spr(
core.spr(x, y, w, h, sx, sy, sw, sh); 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. * Create a texture based on the loaded buffer.
* *
@ -71,41 +61,15 @@ export class Texture {
export function create_texture( export function create_texture(
buffer: ArrayBuffer, buffer: ArrayBuffer,
label: string | undefined = undefined label: string | undefined = undefined
): Texture { ): number {
const id = core.create_texture(buffer, label); return core.create_texture(buffer, label);
return new Texture(id);
} }
/** /**
* Set the specified texture as the current texture for calls to e.g. spr(). * 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) { export function use_texture(id: number) {
core.use_texture(id.id()); core.use_texture(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());
} }

View file

@ -6,12 +6,6 @@ import * as core from "io-core";
* @param path The path of the file to load. * @param path The path of the file to load.
* @returns The contents of the file. * @returns The contents of the file.
*/ */
export const load = core.load; export function load(path: string): Promise<ArrayBuffer> {
return core.load(path);
/** }
* 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;

View file

@ -1,16 +1,10 @@
use bytemuck; use bytemuck;
use std::collections::HashMap; use std::collections::HashMap;
use std::rc::Rc;
use std::sync::mpsc::Receiver; use std::sync::mpsc::Receiver;
use std::time::Instant; use std::time::Instant;
use tracy_client::{frame_mark, set_thread_name, span}; use tracy_client::{frame_mark, set_thread_name, span};
use wgpu::util::DeviceExt; use wgpu::util::DeviceExt;
use winit::{ use winit::{event::*, event_loop::EventLoop, window::Window, window::WindowBuilder};
event::*,
event_loop::{EventLoopBuilder, EventLoopProxy},
window::Window,
window::WindowBuilder,
};
mod script; mod script;
use script::graphics::GraphicsCommand; use script::graphics::GraphicsCommand;
@ -73,8 +67,6 @@ struct State {
sprite_bind_group_layout: wgpu::BindGroupLayout, sprite_bind_group_layout: wgpu::BindGroupLayout,
sprite_textures: HashMap<u32, wgpu::BindGroup>, sprite_textures: HashMap<u32, wgpu::BindGroup>,
write_textures: HashMap<u32, texture::Texture>,
screen_uniform: ScreenUniforms, screen_uniform: ScreenUniforms,
screen_uniform_buffer: wgpu::Buffer, screen_uniform_buffer: wgpu::Buffer,
screen_uniform_bind_group: wgpu::BindGroup, screen_uniform_bind_group: wgpu::BindGroup,
@ -282,7 +274,6 @@ impl State {
max_vertices, max_vertices,
sprite_bind_group_layout, sprite_bind_group_layout,
sprite_textures: HashMap::new(), sprite_textures: HashMap::new(),
write_textures: HashMap::new(),
screen_uniform, screen_uniform,
screen_uniform_buffer, screen_uniform_buffer,
screen_uniform_bind_group, screen_uniform_bind_group,
@ -317,19 +308,14 @@ impl State {
fn render(&mut self, commands: Vec<GraphicsCommand>) -> Result<(), wgpu::SurfaceError> { fn render(&mut self, commands: Vec<GraphicsCommand>) -> Result<(), wgpu::SurfaceError> {
let _span = span!("context render"); let _span = span!("context render");
let output = self.surface.get_current_texture()?; let output = self.surface.get_current_texture()?;
let view = output
.texture
.create_view(&wgpu::TextureViewDescriptor::default());
// Group the commands into passes. // 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 { struct Pass {
color: Option<[f64; 4]>, color: Option<[f64; 4]>,
commands: Vec<GraphicsCommand>, commands: Vec<GraphicsCommand>,
target: Rc<wgpu::TextureView>,
} }
let mut passes = Vec::new(); let mut passes = Vec::new();
for command in commands { for command in commands {
@ -337,9 +323,7 @@ impl State {
GraphicsCommand::Clear(cc) => passes.push(Pass { GraphicsCommand::Clear(cc) => passes.push(Pass {
color: Some(cc.color), color: Some(cc.color),
commands: Vec::new(), commands: Vec::new(),
target: last_view.clone(),
}), }),
GraphicsCommand::CreateTexture(ct) => { GraphicsCommand::CreateTexture(ct) => {
let texture = texture::Texture::from_image( let texture = texture::Texture::from_image(
&self.device, &self.device,
@ -371,93 +355,12 @@ impl State {
self.sprite_textures.insert(ct.id, sprite_bind_group); 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 => (), GraphicsCommand::EndFrame => (),
other => match passes.last_mut() { other => match passes.last_mut() {
Some(pass) => pass.commands.push(other), Some(pass) => pass.commands.push(other),
None => passes.push(Pass { None => passes.push(Pass {
color: None, color: None,
commands: vec![other], commands: vec![other],
target: last_view.clone(),
}), }),
}, },
} }
@ -480,7 +383,7 @@ impl State {
let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
label: Some("Render Pass"), label: Some("Render Pass"),
color_attachments: &[Some(wgpu::RenderPassColorAttachment { color_attachments: &[Some(wgpu::RenderPassColorAttachment {
view: &pass.target, view: &view,
resolve_target: None, resolve_target: None,
ops: wgpu::Operations { ops: wgpu::Operations {
load: if let Some([r, g, b, a]) = pass.color { load: if let Some([r, g, b, a]) = pass.color {
@ -535,11 +438,7 @@ impl State {
} }
GraphicsCommand::UseTexture(id) => texture_id = Some(id), GraphicsCommand::UseTexture(id) => texture_id = Some(id),
GraphicsCommand::CreateTexture(_) => (), // Already handled GraphicsCommand::CreateTexture(_) => (), // Already handled
GraphicsCommand::CreateWritableTexture { .. } => (), // Already handled
GraphicsCommand::WriteToTexture(_) => (), // Already handled
GraphicsCommand::WriteToScreen => (), // Already handled
GraphicsCommand::Clear(_) => (), // Already handled GraphicsCommand::Clear(_) => (), // Already handled
GraphicsCommand::EndFrame => (), // Should never appear GraphicsCommand::EndFrame => (), // Should never appear
} }
@ -573,19 +472,15 @@ impl State {
} }
} }
enum OdenEvent {
Close,
}
struct UIEvent { struct UIEvent {
winit: Event<'static, OdenEvent>, winit: Event<'static, ()>,
#[allow(unused)] #[allow(unused)]
time: Instant, time: Instant,
} }
// TODO: flume? (https://docs.rs/flume/latest/flume/) // TODO: flume? (https://docs.rs/flume/latest/flume/)
fn main_thread(event_loop: EventLoopProxy<OdenEvent>, state: State, reciever: Receiver<UIEvent>) { fn main_thread(state: State, reciever: Receiver<UIEvent>) {
let mut state = state; let mut state = state;
let mut script = script::ScriptContext::new(); let mut script = script::ScriptContext::new();
script.init(); script.init();
@ -605,10 +500,6 @@ fn main_thread(event_loop: EventLoopProxy<OdenEvent>, state: State, reciever: Re
} if window_id == state.window().id() => { } if window_id == state.window().id() => {
if !script.input(event) { if !script.input(event) {
match event { match event {
WindowEvent::CloseRequested => {
let _ = event_loop.send_event(OdenEvent::Close);
}
WindowEvent::CursorMoved { position, .. } => { WindowEvent::CursorMoved { position, .. } => {
state.mouse_x = position.x; state.mouse_x = position.x;
state.mouse_y = position.y; state.mouse_y = position.y;
@ -666,26 +557,20 @@ pub async fn run() {
set_thread_name!("ui thread"); set_thread_name!("ui thread");
env_logger::init(); env_logger::init();
let event_loop = EventLoopBuilder::<OdenEvent>::with_user_event().build(); let event_loop = EventLoop::new();
let window = WindowBuilder::new().build(&event_loop).unwrap(); let window = WindowBuilder::new().build(&event_loop).unwrap();
let event_loop_proxy = event_loop.create_proxy();
let state = State::new(window).await; let state = State::new(window).await;
let (sender, reciever) = std::sync::mpsc::channel(); let (sender, reciever) = std::sync::mpsc::channel();
std::thread::spawn(move || { std::thread::spawn(move || {
set_thread_name!("game thread"); set_thread_name!("game thread");
main_thread(event_loop_proxy, state, reciever); main_thread(state, reciever);
}); });
event_loop.run(move |event, _, control_flow| { event_loop.run(move |event, _, control_flow| {
control_flow.set_wait(); control_flow.set_wait();
match event {
Event::UserEvent(OdenEvent::Close) => {
control_flow.set_exit();
}
_ => {
if let Some(e) = event.to_static() { if let Some(e) = event.to_static() {
sender sender
.send(UIEvent { .send(UIEvent {
@ -694,7 +579,5 @@ pub async fn run() {
}) })
.unwrap(); .unwrap();
} }
}
}
}); });
} }

View file

@ -56,8 +56,7 @@ pub struct ScriptContext {
impl ScriptContext { impl ScriptContext {
pub fn new() -> Self { pub fn new() -> Self {
let mut runtime = Runtime::new(); let runtime = Runtime::with_loader(Loader::new());
runtime.set_module_loader(Loader::new());
let mut context = Context::new(runtime); let mut context = Context::new(runtime);
context.add_intrinsic_bigfloat(); context.add_intrinsic_bigfloat();

View file

@ -40,15 +40,7 @@ pub enum GraphicsCommand {
Print(PrintCommand), Print(PrintCommand),
Sprite(SpriteCommand), Sprite(SpriteCommand),
CreateTexture(CreateTextureCommand), CreateTexture(CreateTextureCommand),
CreateWritableTexture {
id: u32,
width: u32,
height: u32,
label: Option<String>,
},
UseTexture(u32), UseTexture(u32),
WriteToTexture(u32),
WriteToScreen,
EndFrame, EndFrame,
} }
@ -117,31 +109,6 @@ impl GraphicsImpl {
fn use_texture(&self, id: u32) { fn use_texture(&self, id: u32) {
let _ = self.sender.send(GraphicsCommand::UseTexture(id)); let _ = self.sender.send(GraphicsCommand::UseTexture(id));
} }
fn create_writable_texture(
&self,
width: u32,
height: u32,
label: Option<String>,
) -> Result<u32> {
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 { 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<String>| {
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")?; builder.build("graphics-core")?;
Ok(GraphicsAPI { gfx }) Ok(GraphicsAPI { gfx })
} }

View file

@ -82,24 +82,6 @@ impl IoImpl {
Ok(value) 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 {} pub struct IoAPI {}
@ -115,13 +97,6 @@ impl IoAPI {
ctx.new_fn(move |ctx: &ContextRef, p: String| io.load(ctx, &p))?, 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")?; builder.build("io-core")?;
Ok(IoAPI {}) Ok(IoAPI {})
} }

View file

@ -7,46 +7,15 @@ pub struct Texture {
} }
impl Texture { impl Texture {
pub fn new_writable( // pub fn from_bytes(
device: &wgpu::Device, // device: &wgpu::Device,
width: u32, // queue: &wgpu::Queue,
height: u32, // bytes: &[u8],
label: Option<&str>, // label: &str,
) -> Self { // ) -> Result<Self> {
let size = wgpu::Extent3d { // let img = image::load_from_memory(bytes)?;
width, // Ok(Self::from_image(device, queue, &img, Some(label)))
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_image( pub fn from_image(
device: &wgpu::Device, device: &wgpu::Device,
@ -62,7 +31,6 @@ impl Texture {
height: dimensions.1, height: dimensions.1,
depth_or_array_layers: 1, depth_or_array_layers: 1,
}; };
let texture = device.create_texture(&wgpu::TextureDescriptor { let texture = device.create_texture(&wgpu::TextureDescriptor {
label, label,
size, size,

View file

@ -1,9 +1,7 @@
// These are the functions exposed by the native graphics module. // These are the functions exposed by the native graphics module.
// //
export function cls(r: number, g: number, b: number); export function cls(r: number, g: number, b: number);
export function print(msg: string); export function print(msg: string);
export function spr( export function spr(
x: number, x: number,
y: number, y: number,
@ -14,20 +12,8 @@ export function spr(
sw: number, sw: number,
sh: number sh: number
); );
export function create_texture( export function create_texture(
buffer: ArrayBuffer, buffer: ArrayBuffer,
label: string | undefined label: string | undefined
): number; ): number;
export function use_texture(id: 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);

2
types/io-core.d.ts vendored
View file

@ -1,5 +1,3 @@
// These are the functions exposed by the native IO module. // These are the functions exposed by the native IO module.
// //
export function load(path: string): Promise<ArrayBuffer>; export function load(path: string): Promise<ArrayBuffer>;
export function load_string(path: string): Promise<string>;