[oden] Garbage assets, need to rewrite to IO

This commit is contained in:
John Doty 2023-06-27 17:16:37 -07:00
parent 75fcc427ac
commit 17805fa4a6
17 changed files with 240 additions and 94 deletions

View file

@ -69,7 +69,7 @@ where
*ret
}
}
Err(Error::Exception(e, _)) => unsafe {
Err(Error::Exception(e, _, _)) => unsafe {
// If we returned `Error::Exception` then we're propagating an
// exception through the JS stack, just flip it.
let exc = &e.val;

View file

@ -308,7 +308,12 @@ impl ContextRef {
pub(crate) fn exception_error(&self) -> Error {
let exc = self.exception();
let desc = exc.to_string(&self).unwrap_or_else(|_| String::new());
Error::Exception(exc, desc)
let stack = exc
.get_property(&self, "stack")
.and_then(|stack| stack.to_string(&self))
.unwrap_or_else(|_| String::new());
Error::Exception(exc, desc, stack)
}
}

View file

@ -1,4 +1,4 @@
use crate::{ContextRef, Error, Result, Value, ValueRef};
use crate::{ContextRef, Error, Result, Value, ValueRef, ValueType};
use std::num::TryFromIntError;
pub trait TryFromValue: Sized {
@ -105,10 +105,24 @@ impl TryFromValue for Value {
}
}
impl TryFromValue for () {
#[inline]
fn try_from_value(value: &ValueRef, _ctx: &ContextRef) -> Result<Self> {
if value.is_undefined() {
Ok(())
} else {
Err(Error::InvalidType {
expected: ValueType::Undefined,
found: value.value_type(),
})
}
}
}
impl<T: TryFromValue> TryFromValue for Option<T> {
#[inline]
fn try_from_value(value: &ValueRef, ctx: &ContextRef) -> Result<Self> {
if value.is_undefined() {
if value.is_null() || value.is_undefined() {
Ok(None)
} else {
Ok(Some(T::try_from_value(value, ctx)?))

View file

@ -7,21 +7,21 @@ pub trait TryIntoValue {
impl TryIntoValue for u8 {
#[inline]
fn try_into_value(self, ctx: &ContextRef) -> ValueResult {
ctx.new_u64(self)
ctx.new_u32(self)
}
}
impl TryIntoValue for u16 {
#[inline]
fn try_into_value(self, ctx: &ContextRef) -> ValueResult {
ctx.new_u64(self)
ctx.new_u32(self)
}
}
impl TryIntoValue for u32 {
#[inline]
fn try_into_value(self, ctx: &ContextRef) -> ValueResult {
ctx.new_u64(self)
ctx.new_u32(self)
}
}
@ -116,7 +116,7 @@ impl TryIntoValue for Error {
}
Error::ConversionError(e) => Err(Error::ConversionError(e)),
Error::RustFunctionError(e) => Err(Error::RustFunctionError(e)),
Error::Exception(v, d) => Err(Error::Exception(v.dup(ctx), d)),
Error::Exception(v, d, s) => Err(Error::Exception(v.dup(ctx), d, s)),
Error::OutOfMemory => Err(Error::OutOfMemory),
Error::IOError(e) => Err(Error::IOError(e)),
Error::ParseError(name, err) => Err(Error::ParseError(name, err)),
@ -135,8 +135,15 @@ impl<T: TryIntoValue> TryIntoValue for Option<T> {
#[inline]
fn try_into_value(self, ctx: &ContextRef) -> ValueResult {
match self {
None => Ok(ctx.undefined()),
None => Ok(ctx.null()),
Some(v) => v.try_into_value(ctx),
}
}
}
impl TryIntoValue for () {
#[inline]
fn try_into_value(self, ctx: &ContextRef) -> ValueResult {
Ok(ctx.undefined())
}
}

View file

@ -18,7 +18,7 @@ pub use conversion::*;
pub use runtime::Runtime;
pub use value::{Value, ValueRef, ValueType};
#[derive(Error, Debug)]
#[derive(Debug, Error)]
pub enum Error {
#[error("too many classes have been registered")]
TooManyClasses,
@ -39,8 +39,8 @@ pub enum Error {
ConversionError(String),
#[error("an error occurred calling a rust function: {0}")]
RustFunctionError(String),
#[error("an exception was thrown during evaluation: {1}")]
Exception(Value, String),
#[error("an exception was thrown during evaluation: {1}\nStack: {2}")]
Exception(Value, String, String),
#[error("out of memory")]
OutOfMemory,
#[error("an io error occurred: {0}")]
@ -66,7 +66,7 @@ pub type ValueResult = core::result::Result<Value, Error>;
pub(crate) fn throw_error(context: &ContextRef, error: Error) -> sys::JSValue {
match error {
Error::Exception(v, _) => unsafe {
Error::Exception(v, _, _) => unsafe {
sys::JS_DupValue(context.ctx, v.val);
sys::JS_Throw(context.ctx, v.val)
},

View file

@ -164,7 +164,7 @@ unsafe extern "C" fn init_func<T: NativeModule>(
let context = ContextRef::from_raw(ctx);
match NativeModuleState::<T>::define(&context, m) {
Ok(_) => 0,
Err(Error::Exception(e, _)) => unsafe {
Err(Error::Exception(e, _, _)) => unsafe {
// If we returned `Error::Exception` then we're propagating an
// exception through the JS stack, just flip it.
let exc = &e.val;

View file

@ -137,9 +137,7 @@ impl ValueRef {
let mut res: u32 = 0;
let ret = sys::JS_ToUint32(ctx.ctx, &mut res, self.val);
if ret < 0 {
let exc = ctx.exception();
let desc = exc.to_string(&ctx).unwrap_or_else(|_| String::new());
Err(Error::Exception(exc, desc))
Err(ctx.exception_error())
} else {
Ok(res)
}

5
src/assets.ts Normal file
View file

@ -0,0 +1,5 @@
import * as core from "asset-core";
export function load_texture(path: string): number {
return core.load_texture(path);
}

View file

@ -51,3 +51,12 @@ export function spr(
sh = sh || h;
core.spr(x, y, w, h, sx, sy, sw, sh);
}
/**
* Set the specified texture as the current texture for calls to e.g. spr().
*
* @param id - The identifier of the texture to use.
*/
export function use_texture(id: number) {
core.use_texture(id);
}

View file

@ -1,4 +1,5 @@
use bytemuck;
use std::collections::HashMap;
use wgpu::util::DeviceExt;
use winit::{
event::*,
@ -65,7 +66,8 @@ struct State {
vertex_buffer: wgpu::Buffer,
max_vertices: usize,
diffuse_bind_group: wgpu::BindGroup,
sprite_bind_group_layout: wgpu::BindGroupLayout,
sprite_textures: HashMap<u32, wgpu::BindGroup>,
screen_uniform: ScreenUniforms,
screen_uniform_buffer: wgpu::Buffer,
@ -146,11 +148,12 @@ impl State {
};
surface.configure(&device, &config);
let diffuse_bytes = include_bytes!("happy-tree.png");
let diffuse_texture =
texture::Texture::from_bytes(&device, &queue, diffuse_bytes, "happy-tree.png").unwrap();
// TODO: DELETE THIS
// let diffuse_bytes = include_bytes!("happy-tree.png");
// let diffuse_texture =
// texture::Texture::from_bytes(&device, &queue, diffuse_bytes, "happy-tree.png").unwrap();
let texture_bind_group_layout =
let sprite_bind_group_layout =
device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
entries: &[
wgpu::BindGroupLayoutEntry {
@ -172,23 +175,24 @@ impl State {
count: None,
},
],
label: Some("texture_bind_group_layout"),
label: Some("sprite_bind_group_layout"),
});
let diffuse_bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
layout: &texture_bind_group_layout,
entries: &[
wgpu::BindGroupEntry {
binding: 0,
resource: wgpu::BindingResource::TextureView(&diffuse_texture.view),
},
wgpu::BindGroupEntry {
binding: 1,
resource: wgpu::BindingResource::Sampler(&diffuse_texture.sampler),
},
],
label: Some("diffuse_bind_group"),
});
// TODO: DELETE THIS
// let sprite_bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
// layout: &sprite_bind_group_layout,
// entries: &[
// wgpu::BindGroupEntry {
// binding: 0,
// resource: wgpu::BindingResource::TextureView(&diffuse_texture.view),
// },
// wgpu::BindGroupEntry {
// binding: 1,
// resource: wgpu::BindingResource::Sampler(&diffuse_texture.sampler),
// },
// ],
// label: Some("diffuse_bind_group"),
// });
let screen_uniform = ScreenUniforms::new(size.width, size.height);
let screen_uniform_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
@ -229,10 +233,7 @@ impl State {
let render_pipeline_layout =
device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
label: Some("Render Pipeline Layout"),
bind_group_layouts: &[
&texture_bind_group_layout,
&screen_uniform_bind_group_layout,
],
bind_group_layouts: &[&sprite_bind_group_layout, &screen_uniform_bind_group_layout],
push_constant_ranges: &[],
});
@ -294,7 +295,8 @@ impl State {
render_pipeline,
vertex_buffer,
max_vertices,
diffuse_bind_group,
sprite_bind_group_layout,
sprite_textures: HashMap::new(),
screen_uniform,
screen_uniform_buffer,
screen_uniform_bind_group,
@ -348,6 +350,31 @@ impl State {
color: Some(cc.color),
commands: Vec::new(),
}),
GraphicsCommand::CreateTexture(ct) => {
let texture = texture::Texture::from_image(
&self.device,
&self.queue,
&ct.image,
Some(&ct.label),
);
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: Some(&ct.label),
});
self.sprite_textures.insert(ct.id, sprite_bind_group);
}
GraphicsCommand::EndFrame => (),
other => match passes.last_mut() {
Some(pass) => pass.commands.push(other),
@ -395,6 +422,7 @@ impl State {
depth_stencil_attachment: None,
});
let mut texture_id = None;
vertices.clear();
for command in pass.commands {
match command {
@ -428,19 +456,29 @@ impl State {
});
}
GraphicsCommand::Clear(_) => (), // Already handled
GraphicsCommand::EndFrame => (), // Should never appear
GraphicsCommand::UseTexture(id) => texture_id = Some(id),
GraphicsCommand::CreateTexture(_) => (), // Already handled
GraphicsCommand::Clear(_) => (), // Already handled
GraphicsCommand::EndFrame => (), // Should never appear
}
}
assert!(vertices.len() < self.max_vertices); // !
self.queue
.write_buffer(&self.vertex_buffer, 0, bytemuck::cast_slice(&vertices));
render_pass.set_pipeline(&self.render_pipeline);
render_pass.set_bind_group(0, &self.diffuse_bind_group, &[]);
render_pass.set_bind_group(1, &self.screen_uniform_bind_group, &[]);
render_pass.set_vertex_buffer(0, self.vertex_buffer.slice(..));
render_pass.draw(0..(vertices.len() as u32), 0..1);
if let Some(id) = texture_id {
assert!(vertices.len() < self.max_vertices); // !
self.queue.write_buffer(
&self.vertex_buffer,
0,
bytemuck::cast_slice(&vertices),
);
render_pass.set_pipeline(&self.render_pipeline);
let bind_group = self.sprite_textures.get(&id).unwrap();
render_pass.set_bind_group(0, bind_group, &[]);
render_pass.set_bind_group(1, &self.screen_uniform_bind_group, &[]);
render_pass.set_vertex_buffer(0, self.vertex_buffer.slice(..));
render_pass.draw(0..(vertices.len() as u32), 0..1);
}
}
// Submit will accept anything that implements IntoIter

View file

@ -1,12 +1,17 @@
import { cls, print, spr } from "./graphics.ts";
import { cls, print, spr, use_texture } from "./graphics.ts";
import { load_texture } from "./assets.ts";
let the_texture = 0;
export function init() {
print("Hello world!");
the_texture = load_texture("./src/happy-tree.png");
}
export function update() {}
export function draw() {
cls(0.1, 0.2, 0.3);
use_texture(the_texture);
spr((320 - 256) / 2, 0, 256, 240, 0, 0);
}

View file

@ -12,6 +12,8 @@ use graphics::GraphicsCommand;
mod typescript;
use typescript::transpile_to_javascript;
pub mod assets;
struct Loader {}
impl Loader {
@ -42,6 +44,7 @@ pub struct ScriptContext {
draw: Value,
gfx: graphics::GraphicsAPI,
_assets: assets::AssetsAPI,
gfx_receive: Receiver<graphics::GraphicsCommand>,
}
@ -56,8 +59,10 @@ impl ScriptContext {
let (gfx_send, gfx_receive) = channel();
let gfx = graphics::GraphicsAPI::define(&context, gfx_send)
let gfx = graphics::GraphicsAPI::define(&context, gfx_send.clone())
.expect("Graphics module should load without error");
let assets = assets::AssetsAPI::define(&context, gfx_send.clone())
.expect("Assets module should load without error");
let module = context
.import_module("./src/main.ts", "")
@ -82,6 +87,8 @@ impl ScriptContext {
gfx,
gfx_receive,
_assets: assets,
}
}

56
src/script/assets.rs Normal file
View file

@ -0,0 +1,56 @@
use crate::script::graphics::{CreateTextureCommand, GraphicsCommand};
use oden_js::{module::native::NativeModuleBuilder, ContextRef, Error, Result};
use std::sync::atomic::{AtomicU32, Ordering};
use std::sync::mpsc::Sender;
use std::sync::Arc;
struct AssetsImpl {
next_texture_id: AtomicU32,
gfx_sender: Sender<GraphicsCommand>,
}
impl AssetsImpl {
fn new(sender: Sender<GraphicsCommand>) -> Self {
AssetsImpl {
gfx_sender: sender,
next_texture_id: AtomicU32::new(0),
}
}
fn load_texture(&self, path: &str) -> Result<u32> {
let bytes = std::fs::read(path)?;
let image = match image::load_from_memory(&bytes) {
Ok(i) => i,
Err(e) => return Err(Error::RustFunctionError(format!("{e}"))),
};
let id = self.next_texture_id.fetch_add(1, Ordering::SeqCst);
let _ = self
.gfx_sender
.send(GraphicsCommand::CreateTexture(CreateTextureCommand {
id,
image,
label: path.into(),
}));
Ok(id)
}
}
pub struct AssetsAPI {}
impl AssetsAPI {
pub fn define(ctx: &ContextRef, sender: Sender<GraphicsCommand>) -> oden_js::Result<Self> {
let assets = Arc::new(AssetsImpl::new(sender));
let mut builder = NativeModuleBuilder::new(ctx);
{
let assets = assets.clone();
builder.export(
"load_texture",
ctx.new_fn(move |_ctx: &ContextRef, p: String| assets.load_texture(&p))?,
)?;
}
builder.build("asset-core")?;
Ok(AssetsAPI {})
}
}

View file

@ -1,4 +1,4 @@
use oden_js::{module, ContextRef, Value, ValueResult};
use oden_js::{module, ContextRef};
use std::sync::mpsc::Sender;
use std::sync::Arc;
@ -24,11 +24,20 @@ pub struct SpriteCommand {
pub sh: f32,
}
#[derive(Debug)]
pub struct CreateTextureCommand {
pub id: u32,
pub image: image::DynamicImage,
pub label: String,
}
#[derive(Debug)]
pub enum GraphicsCommand {
Clear(ClearCommand),
Print(PrintCommand),
Sprite(SpriteCommand),
CreateTexture(CreateTextureCommand),
UseTexture(u32),
EndFrame,
}
@ -41,33 +50,19 @@ impl GraphicsImpl {
GraphicsImpl { sender }
}
fn print_fn(&self, ctx: &ContextRef, text: String) -> ValueResult {
fn print(&self, text: String) -> () {
let _ = self
.sender
.send(GraphicsCommand::Print(PrintCommand { text }));
Ok(Value::undefined(ctx))
}
fn cls_fn(&self, ctx: &ContextRef, r: f64, g: f64, b: f64) -> ValueResult {
fn cls(&self, r: f64, g: f64, b: f64) -> () {
let _ = self.sender.send(GraphicsCommand::Clear(ClearCommand {
color: [r, g, b, 1.0],
}));
Ok(Value::undefined(ctx))
}
fn spr_fn(
&self,
ctx: &ContextRef,
x: f32,
y: f32,
w: f32,
h: f32,
u: f32,
v: f32,
sw: f32,
sh: f32,
) -> ValueResult {
fn spr(&self, x: f32, y: f32, w: f32, h: f32, u: f32, v: f32, sw: f32, sh: f32) {
let _ = self.sender.send(GraphicsCommand::Sprite(SpriteCommand {
x,
y,
@ -78,7 +73,10 @@ impl GraphicsImpl {
sw,
sh,
}));
Ok(Value::undefined(ctx))
}
fn use_texture(&self, id: u32) {
let _ = self.sender.send(GraphicsCommand::UseTexture(id));
}
}
@ -94,16 +92,14 @@ impl GraphicsAPI {
let gfx = gfx.clone();
builder.export(
"print",
ctx.new_fn(move |ctx: &ContextRef, t: String| gfx.print_fn(ctx, t))?,
ctx.new_fn(move |_: &ContextRef, t: String| gfx.print(t))?,
)?;
}
{
let gfx = gfx.clone();
builder.export(
"cls",
ctx.new_fn(move |ctx: &ContextRef, r: f64, g: f64, b: f64| {
gfx.cls_fn(ctx, r, g, b)
})?,
ctx.new_fn(move |_: &ContextRef, r: f64, g: f64, b: f64| gfx.cls(r, g, b))?,
)?;
}
{
@ -111,7 +107,7 @@ impl GraphicsAPI {
builder.export(
"spr",
ctx.new_fn(
move |ctx: &ContextRef,
move |_: &ContextRef,
x: f32,
y: f32,
w: f32,
@ -119,12 +115,17 @@ impl GraphicsAPI {
u: f32,
v: f32,
sw: f32,
sh: f32| {
gfx.spr_fn(ctx, x, y, w, h, u, v, sw, sh)
},
sh: f32| gfx.spr(x, y, w, h, u, v, sw, sh),
)?,
)?;
}
{
let gfx = gfx.clone();
builder.export(
"use_texture",
ctx.new_fn(move |_: &ContextRef, id: u32| gfx.use_texture(id))?,
)?;
}
builder.build("graphics-core")?;
Ok(GraphicsAPI { gfx })
}

View file

@ -1,4 +1,3 @@
use anyhow::*;
use image::GenericImageView;
pub struct Texture {
@ -8,22 +7,22 @@ pub struct Texture {
}
impl Texture {
pub fn from_bytes(
device: &wgpu::Device,
queue: &wgpu::Queue,
bytes: &[u8],
label: &str,
) -> Result<Self> {
let img = image::load_from_memory(bytes)?;
Self::from_image(device, queue, &img, Some(label))
}
// pub fn from_bytes(
// device: &wgpu::Device,
// queue: &wgpu::Queue,
// bytes: &[u8],
// label: &str,
// ) -> Result<Self> {
// let img = image::load_from_memory(bytes)?;
// Ok(Self::from_image(device, queue, &img, Some(label)))
// }
pub fn from_image(
device: &wgpu::Device,
queue: &wgpu::Queue,
img: &image::DynamicImage,
label: Option<&str>,
) -> Result<Self> {
) -> Self {
let rgba = img.to_rgba8();
let dimensions = img.dimensions();
@ -70,10 +69,10 @@ impl Texture {
..Default::default()
});
Ok(Self {
Self {
texture,
view,
sampler,
})
}
}
}

1
types/asset-core.d.ts vendored Normal file
View file

@ -0,0 +1 @@
export function load_texture(path: string): number;

View file

@ -12,3 +12,4 @@ export function spr(
sw: number,
sh: number
);
export function use_texture(id: number);