Compare commits
No commits in common. "96e95e22cefb4726f60d37294f12461f7a9394fb" and "3b2af4bb1409beacffa58ee68590b2eeba1e66d6" have entirely different histories.
96e95e22ce
...
3b2af4bb14
16 changed files with 107 additions and 181 deletions
|
|
@ -324,34 +324,6 @@ impl ContextRef {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe extern "C" fn free_array_buffer_bytes(
|
|
||||||
_rt: *mut sys::JSRuntime,
|
|
||||||
opaque: *mut std::ffi::c_void,
|
|
||||||
_ptr: *mut std::ffi::c_void,
|
|
||||||
) {
|
|
||||||
drop(Box::from_raw(opaque as *mut Vec<u8>));
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn new_array_buffer<T>(&self, buffer: T) -> ValueResult
|
|
||||||
where
|
|
||||||
T: Into<Vec<u8>>,
|
|
||||||
{
|
|
||||||
let mut vec_box = Box::new(buffer.into());
|
|
||||||
unsafe {
|
|
||||||
let is_shared = 0;
|
|
||||||
let byte_ptr = vec_box.as_mut_ptr();
|
|
||||||
let byte_len = vec_box.len();
|
|
||||||
self.check_exception(sys::JS_NewArrayBuffer(
|
|
||||||
self.ctx,
|
|
||||||
byte_ptr,
|
|
||||||
byte_len,
|
|
||||||
Some(Self::free_array_buffer_bytes),
|
|
||||||
Box::into_raw(vec_box) as *mut _,
|
|
||||||
is_shared,
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Fetch the global object for the context.
|
/// Fetch the global object for the context.
|
||||||
pub fn global_object(&self) -> ValueResult {
|
pub fn global_object(&self) -> ValueResult {
|
||||||
self.check_exception(unsafe { sys::JS_GetGlobalObject(self.ctx) })
|
self.check_exception(unsafe { sys::JS_GetGlobalObject(self.ctx) })
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ impl PromiseHandle {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type PromiseResult = Box<dyn FnOnce(&ContextRef) -> ValueResult + Send + 'static>;
|
pub type PromiseResult = Box<dyn Fn(&ContextRef) -> ValueResult + Send + 'static>;
|
||||||
|
|
||||||
pub(crate) enum PromiseEvent {
|
pub(crate) enum PromiseEvent {
|
||||||
Resolved(PromiseResult),
|
Resolved(PromiseResult),
|
||||||
|
|
@ -48,7 +48,7 @@ impl Promise {
|
||||||
|
|
||||||
pub fn resolve<T>(mut self, value: T)
|
pub fn resolve<T>(mut self, value: T)
|
||||||
where
|
where
|
||||||
T: FnOnce(&ContextRef) -> ValueResult + Send + 'static,
|
T: Fn(&ContextRef) -> ValueResult + Send + 'static,
|
||||||
{
|
{
|
||||||
let _ = self
|
let _ = self
|
||||||
.channel
|
.channel
|
||||||
|
|
@ -58,7 +58,7 @@ impl Promise {
|
||||||
|
|
||||||
pub fn reject<T>(mut self, value: T)
|
pub fn reject<T>(mut self, value: T)
|
||||||
where
|
where
|
||||||
T: FnOnce(&ContextRef) -> ValueResult + Send + 'static,
|
T: Fn(&ContextRef) -> ValueResult + Send + 'static,
|
||||||
{
|
{
|
||||||
let _ = self
|
let _ = self
|
||||||
.channel
|
.channel
|
||||||
|
|
|
||||||
|
|
@ -175,21 +175,13 @@ impl Runtime {
|
||||||
|
|
||||||
/// Process all pending async jobs. This includes all promise resolutions.
|
/// Process all pending async jobs. This includes all promise resolutions.
|
||||||
fn process_promise_completions(&self) {
|
fn process_promise_completions(&self) {
|
||||||
let mut promises = vec![];
|
// TODO: This could be more robust if we buffered all the completed
|
||||||
|
// promise entries and then dropped the borrow of the state, so
|
||||||
// First, gather all the completed promises into a temporary vector
|
// that we never invoked user code while borrowing our private
|
||||||
// so that we only need to borrow our mutable state for a short
|
// state mutably.
|
||||||
// period of time.
|
|
||||||
let mut state = unsafe { PrivateState::from_rt_mut(self.rt) };
|
let mut state = unsafe { PrivateState::from_rt_mut(self.rt) };
|
||||||
while let Ok((handle, evt)) = state.promise_recv.try_recv() {
|
while let Ok((handle, evt)) = state.promise_recv.try_recv() {
|
||||||
if let Some(entry) = state.promise_table.remove(&handle) {
|
if let Some(entry) = state.promise_table.remove(&handle) {
|
||||||
promises.push((entry, evt));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
drop(state); // Don't need our internal state anymore.
|
|
||||||
|
|
||||||
// Nowe we can complete all the promises.
|
|
||||||
for (entry, evt) in promises {
|
|
||||||
let ctx = ContextRef::from_raw(entry.context);
|
let ctx = ContextRef::from_raw(entry.context);
|
||||||
let (callback, value) = match evt {
|
let (callback, value) = match evt {
|
||||||
PromiseEvent::Resolved(v) => (entry.resolve, v),
|
PromiseEvent::Resolved(v) => (entry.resolve, v),
|
||||||
|
|
@ -214,6 +206,7 @@ impl Runtime {
|
||||||
.expect("Exception thrown by promise callback");
|
.expect("Exception thrown by promise callback");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Process all pending async jobs. This includes all promise resolutions.
|
/// Process all pending async jobs. This includes all promise resolutions.
|
||||||
pub fn process_all_jobs(&self) -> Result<()> {
|
pub fn process_all_jobs(&self) -> Result<()> {
|
||||||
|
|
|
||||||
|
|
@ -323,20 +323,6 @@ impl ValueRef {
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the underlying bytes for a an ArrayBuffer object. Obviously it
|
|
||||||
/// only works if this is an un-detached ArrayBuffer value.
|
|
||||||
pub fn get_array_buffer<'a>(&'a self, ctx: &ContextRef) -> Result<&'a [u8]> {
|
|
||||||
unsafe {
|
|
||||||
let mut size: usize = 0;
|
|
||||||
let buffer = sys::JS_GetArrayBuffer(ctx.ctx, &mut size as *mut usize, self.val);
|
|
||||||
if buffer.is_null() {
|
|
||||||
Err(ctx.exception_error())
|
|
||||||
} else {
|
|
||||||
Ok(std::slice::from_raw_parts(buffer, size))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Debug for ValueRef {
|
impl fmt::Debug for ValueRef {
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,5 @@
|
||||||
import * as io from "./io.ts";
|
import * as core from "asset-core";
|
||||||
import * as gfx from "./graphics.ts";
|
|
||||||
|
|
||||||
export async function load_texture(path: string): Promise<number> {
|
export function load_texture(path: string): number {
|
||||||
const buffer = await io.load(path);
|
return core.load_texture(path);
|
||||||
return gfx.create_texture(buffer, path);
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -52,19 +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);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a texture based on the loaded buffer.
|
|
||||||
*
|
|
||||||
* @param buffer The underlying bytes that make up the texture image.
|
|
||||||
* @param label The label to put onto the texture (for debugging).
|
|
||||||
*/
|
|
||||||
export function create_texture(
|
|
||||||
buffer: ArrayBuffer,
|
|
||||||
label: string | undefined = undefined
|
|
||||||
): number {
|
|
||||||
return core.create_texture(buffer, label);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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().
|
||||||
*
|
*
|
||||||
|
|
|
||||||
11
src/io.ts
11
src/io.ts
|
|
@ -1,11 +0,0 @@
|
||||||
import * as core from "io-core";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Load the specified file into memory.
|
|
||||||
*
|
|
||||||
* @param path The path of the file to load.
|
|
||||||
* @returns The contents of the file.
|
|
||||||
*/
|
|
||||||
export function load(path: string): Promise<ArrayBuffer> {
|
|
||||||
return core.load(path);
|
|
||||||
}
|
|
||||||
10
src/lib.rs
10
src/lib.rs
|
|
@ -339,10 +339,7 @@ impl State {
|
||||||
&self.device,
|
&self.device,
|
||||||
&self.queue,
|
&self.queue,
|
||||||
&ct.image,
|
&ct.image,
|
||||||
match &ct.label {
|
Some(&ct.label),
|
||||||
Some(l) => Some(&l),
|
|
||||||
None => None,
|
|
||||||
},
|
|
||||||
);
|
);
|
||||||
let sprite_bind_group =
|
let sprite_bind_group =
|
||||||
self.device.create_bind_group(&wgpu::BindGroupDescriptor {
|
self.device.create_bind_group(&wgpu::BindGroupDescriptor {
|
||||||
|
|
@ -357,10 +354,7 @@ impl State {
|
||||||
resource: wgpu::BindingResource::Sampler(&texture.sampler),
|
resource: wgpu::BindingResource::Sampler(&texture.sampler),
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
label: match &ct.label {
|
label: Some(&ct.label),
|
||||||
Some(l) => Some(&l),
|
|
||||||
None => None,
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|
||||||
self.sprite_textures.insert(ct.id, sprite_bind_group);
|
self.sprite_textures.insert(ct.id, sprite_bind_group);
|
||||||
|
|
|
||||||
10
src/main.ts
10
src/main.ts
|
|
@ -1,22 +1,18 @@
|
||||||
import { cls, print, spr, use_texture } from "./graphics.ts";
|
import { cls, print, spr, use_texture } from "./graphics.ts";
|
||||||
import { load_texture } from "./assets.ts";
|
import { load_texture } from "./assets.ts";
|
||||||
|
|
||||||
let the_texture: number | undefined = undefined;
|
let the_texture = 0;
|
||||||
|
|
||||||
export function init() {
|
export function init() {
|
||||||
print("Hello world!");
|
print("Hello world!");
|
||||||
|
// TODO: Async IO
|
||||||
// Start this load, but then...
|
the_texture = load_texture("./src/happy-tree.png");
|
||||||
load_texture("./src/happy-tree.png").then((n) => (the_texture = n));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function update() {}
|
export function update() {}
|
||||||
|
|
||||||
export function draw() {
|
export function draw() {
|
||||||
cls(0.1, 0.2, 0.3);
|
cls(0.1, 0.2, 0.3);
|
||||||
if (the_texture != undefined) {
|
|
||||||
// ...it gets resolved here?
|
|
||||||
use_texture(the_texture);
|
use_texture(the_texture);
|
||||||
spr((320 - 256) / 2, 0, 256, 240, 0, 0);
|
spr((320 - 256) / 2, 0, 256, 240, 0, 0);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,8 @@ use graphics::GraphicsCommand;
|
||||||
mod typescript;
|
mod typescript;
|
||||||
use typescript::transpile_to_javascript;
|
use typescript::transpile_to_javascript;
|
||||||
|
|
||||||
mod io;
|
pub mod assets;
|
||||||
|
pub mod io;
|
||||||
|
|
||||||
struct Loader {}
|
struct Loader {}
|
||||||
|
|
||||||
|
|
@ -44,6 +45,7 @@ pub struct ScriptContext {
|
||||||
draw: Value,
|
draw: Value,
|
||||||
|
|
||||||
gfx: graphics::GraphicsAPI,
|
gfx: graphics::GraphicsAPI,
|
||||||
|
_assets: assets::AssetsAPI,
|
||||||
gfx_receive: Receiver<graphics::GraphicsCommand>,
|
gfx_receive: Receiver<graphics::GraphicsCommand>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -60,6 +62,8 @@ impl ScriptContext {
|
||||||
|
|
||||||
let gfx = graphics::GraphicsAPI::define(&context, gfx_send.clone())
|
let gfx = graphics::GraphicsAPI::define(&context, gfx_send.clone())
|
||||||
.expect("Graphics module should load without error");
|
.expect("Graphics module should load without error");
|
||||||
|
let assets = assets::AssetsAPI::define(&context, gfx_send.clone())
|
||||||
|
.expect("Assets module should load without error");
|
||||||
let _io = io::IoAPI::define(&context).expect("IO module should load without error");
|
let _io = io::IoAPI::define(&context).expect("IO module should load without error");
|
||||||
|
|
||||||
let module = context
|
let module = context
|
||||||
|
|
@ -85,6 +89,8 @@ impl ScriptContext {
|
||||||
|
|
||||||
gfx,
|
gfx,
|
||||||
gfx_receive,
|
gfx_receive,
|
||||||
|
|
||||||
|
_assets: assets,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
56
src/script/assets.rs
Normal file
56
src/script/assets.rs
Normal 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 {})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,5 +1,4 @@
|
||||||
use oden_js::{module, ContextRef, Error, Result, Value};
|
use oden_js::{module, ContextRef};
|
||||||
use std::sync::atomic::{AtomicU32, Ordering};
|
|
||||||
use std::sync::mpsc::Sender;
|
use std::sync::mpsc::Sender;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
|
@ -29,7 +28,7 @@ pub struct SpriteCommand {
|
||||||
pub struct CreateTextureCommand {
|
pub struct CreateTextureCommand {
|
||||||
pub id: u32,
|
pub id: u32,
|
||||||
pub image: image::DynamicImage,
|
pub image: image::DynamicImage,
|
||||||
pub label: Option<String>,
|
pub label: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
|
@ -43,16 +42,12 @@ pub enum GraphicsCommand {
|
||||||
}
|
}
|
||||||
|
|
||||||
struct GraphicsImpl {
|
struct GraphicsImpl {
|
||||||
next_texture_id: AtomicU32,
|
|
||||||
sender: Sender<GraphicsCommand>,
|
sender: Sender<GraphicsCommand>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl GraphicsImpl {
|
impl GraphicsImpl {
|
||||||
pub fn new(sender: Sender<GraphicsCommand>) -> Self {
|
pub fn new(sender: Sender<GraphicsCommand>) -> Self {
|
||||||
GraphicsImpl {
|
GraphicsImpl { sender }
|
||||||
sender,
|
|
||||||
next_texture_id: AtomicU32::new(0),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn print(&self, text: String) -> () {
|
fn print(&self, text: String) -> () {
|
||||||
|
|
@ -80,30 +75,6 @@ impl GraphicsImpl {
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_texture(
|
|
||||||
&self,
|
|
||||||
context: &ContextRef,
|
|
||||||
buffer: Value,
|
|
||||||
label: Option<String>,
|
|
||||||
) -> Result<u32> {
|
|
||||||
let bytes = buffer.get_array_buffer(context)?;
|
|
||||||
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
|
|
||||||
.sender
|
|
||||||
.send(GraphicsCommand::CreateTexture(CreateTextureCommand {
|
|
||||||
id,
|
|
||||||
image,
|
|
||||||
label,
|
|
||||||
}));
|
|
||||||
|
|
||||||
Ok(id)
|
|
||||||
}
|
|
||||||
|
|
||||||
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));
|
||||||
}
|
}
|
||||||
|
|
@ -155,17 +126,6 @@ impl GraphicsAPI {
|
||||||
ctx.new_fn(move |_: &ContextRef, id: u32| gfx.use_texture(id))?,
|
ctx.new_fn(move |_: &ContextRef, id: u32| gfx.use_texture(id))?,
|
||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
{
|
|
||||||
let gfx = gfx.clone();
|
|
||||||
builder.export(
|
|
||||||
"create_texture",
|
|
||||||
ctx.new_fn(
|
|
||||||
move |c: &ContextRef, buffer: Value, label: Option<String>| {
|
|
||||||
gfx.create_texture(c, buffer, label)
|
|
||||||
},
|
|
||||||
)?,
|
|
||||||
)?;
|
|
||||||
}
|
|
||||||
builder.build("graphics-core")?;
|
builder.build("graphics-core")?;
|
||||||
Ok(GraphicsAPI { gfx })
|
Ok(GraphicsAPI { gfx })
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -71,12 +71,7 @@ impl IoImpl {
|
||||||
self.thread_pool.execute(Box::new(move || {
|
self.thread_pool.execute(Box::new(move || {
|
||||||
// TODO: Actually read the path.
|
// TODO: Actually read the path.
|
||||||
let path = path;
|
let path = path;
|
||||||
|
promise.resolve(move |ctx: &ContextRef| ctx.new_string(&path));
|
||||||
let result = std::fs::read(path);
|
|
||||||
promise.resolve(move |ctx: &ContextRef| match result {
|
|
||||||
Ok(v) => ctx.new_array_buffer(v),
|
|
||||||
Err(err) => Err(err.into()),
|
|
||||||
});
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
Ok(value)
|
Ok(value)
|
||||||
|
|
|
||||||
1
types/asset-core.d.ts
vendored
Normal file
1
types/asset-core.d.ts
vendored
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
export function load_texture(path: string): number;
|
||||||
4
types/graphics-core.d.ts
vendored
4
types/graphics-core.d.ts
vendored
|
|
@ -12,8 +12,4 @@ export function spr(
|
||||||
sw: number,
|
sw: number,
|
||||||
sh: number
|
sh: number
|
||||||
);
|
);
|
||||||
export function create_texture(
|
|
||||||
buffer: ArrayBuffer,
|
|
||||||
label: string | undefined
|
|
||||||
): number;
|
|
||||||
export function use_texture(id: number);
|
export function use_texture(id: number);
|
||||||
|
|
|
||||||
3
types/io-core.d.ts
vendored
3
types/io-core.d.ts
vendored
|
|
@ -1,3 +0,0 @@
|
||||||
// These are the functions exposed by the native IO module.
|
|
||||||
//
|
|
||||||
export function load(path: string): Promise<ArrayBuffer>;
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue