Compare commits
No commits in common. "17fdee51e6faf93214fd763933ec84b163f4940c" and "c1d86676c3a296faf9ec11913fde136162c3959f" have entirely different histories.
17fdee51e6
...
c1d86676c3
8 changed files with 74 additions and 328 deletions
|
|
@ -1,6 +1,6 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
callback::new_fn, conversion::RustFunction, module::Module, Atom, ClassID, Error, Promise,
|
callback::new_fn, conversion::RustFunction, module::Module, Atom, ClassID, Error, Result,
|
||||||
Result, Runtime, Value, ValueRef, ValueResult,
|
Runtime, Value, ValueRef, ValueResult,
|
||||||
};
|
};
|
||||||
use bitflags::bitflags;
|
use bitflags::bitflags;
|
||||||
use oden_js_sys as sys;
|
use oden_js_sys as sys;
|
||||||
|
|
@ -34,7 +34,6 @@ pub struct ContextRef {
|
||||||
pub(crate) ctx: *mut sys::JSContext,
|
pub(crate) ctx: *mut sys::JSContext,
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Should all these require mutability to enforce single-threadedness?
|
|
||||||
impl ContextRef {
|
impl ContextRef {
|
||||||
pub(crate) fn from_raw(ctx: *mut sys::JSContext) -> Self {
|
pub(crate) fn from_raw(ctx: *mut sys::JSContext) -> Self {
|
||||||
ContextRef { ctx }
|
ContextRef { ctx }
|
||||||
|
|
@ -278,64 +277,6 @@ impl ContextRef {
|
||||||
Value::from_raw(v, self)
|
Value::from_raw(v, self)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Construct a new promise.
|
|
||||||
pub fn new_promise(&self) -> Result<Promise> {
|
|
||||||
unsafe {
|
|
||||||
let mut resolving_funcs: [sys::JSValue; 2] =
|
|
||||||
[sys::JS_MakeUndefined(), sys::JS_MakeUndefined()];
|
|
||||||
let val =
|
|
||||||
sys::JS_NewPromiseCapability(self.ctx, &mut resolving_funcs as *mut sys::JSValue);
|
|
||||||
|
|
||||||
if sys::JS_ValueGetTag(val) == sys::JS_TAG_EXCEPTION {
|
|
||||||
Err(self.exception_error())
|
|
||||||
} else {
|
|
||||||
Ok(Promise::new(
|
|
||||||
Value::from_raw(val, self),
|
|
||||||
Value::from_raw(resolving_funcs[0], self),
|
|
||||||
Value::from_raw(resolving_funcs[1], self),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Construct a new exception object, suitable for throwing.
|
|
||||||
pub fn new_error(&self, message: &str) -> Value {
|
|
||||||
let e = match self.new_string(message) {
|
|
||||||
Ok(e) => e,
|
|
||||||
Err(_) => match self.new_string("INTERNAL ERROR: Embedded NUL in message") {
|
|
||||||
Ok(e) => e,
|
|
||||||
|
|
||||||
// Faulting this hard is inexcusable.
|
|
||||||
Err(_) => return self.exception(),
|
|
||||||
},
|
|
||||||
};
|
|
||||||
unsafe {
|
|
||||||
let err = Value::from_raw(sys::JS_NewError(self.ctx), self);
|
|
||||||
|
|
||||||
// NOTE: Throughout this function we work at the lower-level
|
|
||||||
// error handling stuff because the errors are easier to
|
|
||||||
// manage. (We know how it can fail!)
|
|
||||||
if sys::JS_ValueGetTag(err.val) == sys::JS_TAG_EXCEPTION {
|
|
||||||
// GIVE UP; This is out of memory anyway things probably
|
|
||||||
// went wrong because of that. We'll return *that*
|
|
||||||
// exception.
|
|
||||||
return self.exception();
|
|
||||||
}
|
|
||||||
|
|
||||||
sys::JS_DupValue(self.ctx, e.val); // SetProperty takes ownership.
|
|
||||||
let prop = CString::new("message").unwrap();
|
|
||||||
if sys::JS_SetPropertyStr(self.ctx, err.val, prop.as_ptr(), e.val) == -1 {
|
|
||||||
// As before, we're just going to take the exception from
|
|
||||||
// the context, and drop the one we were trying to create
|
|
||||||
// on the floor.
|
|
||||||
return self.exception();
|
|
||||||
}
|
|
||||||
|
|
||||||
// We put the message in, we can return the value.
|
|
||||||
err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 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) })
|
||||||
|
|
@ -374,23 +315,6 @@ impl ContextRef {
|
||||||
|
|
||||||
Error::Exception(exc, desc, stack)
|
Error::Exception(exc, desc, stack)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Process all pending async jobs. This includes all promise resolutions.
|
|
||||||
pub fn process_all_jobs(&self) -> Result<()> {
|
|
||||||
// TODO: SAFETY
|
|
||||||
// This is unsafe because multiple contexts can be sharing the same runtime and cause
|
|
||||||
// a race condition on the underlying runtime.
|
|
||||||
loop {
|
|
||||||
let mut ctx1: *mut sys::JSContext = std::ptr::null_mut();
|
|
||||||
let err = unsafe { sys::JS_ExecutePendingJob(sys::JS_GetRuntime(self.ctx), &mut ctx1) };
|
|
||||||
if err == 0 {
|
|
||||||
break;
|
|
||||||
} else if err < 0 {
|
|
||||||
return Err(ContextRef::from_raw(ctx1).exception_error());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,6 @@ mod class;
|
||||||
mod context;
|
mod context;
|
||||||
mod conversion;
|
mod conversion;
|
||||||
pub mod module;
|
pub mod module;
|
||||||
mod promise;
|
|
||||||
mod runtime;
|
mod runtime;
|
||||||
mod value;
|
mod value;
|
||||||
|
|
||||||
|
|
@ -16,7 +15,6 @@ 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::Promise;
|
|
||||||
pub use runtime::Runtime;
|
pub use runtime::Runtime;
|
||||||
pub use value::{Value, ValueRef, ValueType};
|
pub use value::{Value, ValueRef, ValueType};
|
||||||
|
|
||||||
|
|
@ -51,20 +49,6 @@ pub enum Error {
|
||||||
ParseError(String, String),
|
ParseError(String, String),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Error {
|
|
||||||
// Convert the error into an exception-type object which can be
|
|
||||||
// thrown. This is *different* from try_into_value which just propagates
|
|
||||||
// the error.
|
|
||||||
pub fn to_js_error(&self, context: &ContextRef) -> Value {
|
|
||||||
if let Error::Exception(e, _, _) = self {
|
|
||||||
e.clone()
|
|
||||||
} else {
|
|
||||||
let message = self.to_string();
|
|
||||||
context.new_error(&message)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<NulError> for Error {
|
impl From<NulError> for Error {
|
||||||
fn from(_: NulError) -> Self {
|
fn from(_: NulError) -> Self {
|
||||||
Error::UnexpectedNul
|
Error::UnexpectedNul
|
||||||
|
|
@ -91,9 +75,35 @@ pub(crate) fn throw_error(context: &ContextRef, error: Error) -> sys::JSValue {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn throw_string(context: &ContextRef, message: String) -> sys::JSValue {
|
pub(crate) fn throw_string(context: &ContextRef, message: String) -> sys::JSValue {
|
||||||
let err = context.new_error(&message);
|
let ctx = context.ctx;
|
||||||
unsafe {
|
match context.new_string(&message) {
|
||||||
sys::JS_DupValue(context.ctx, err.val);
|
Ok(e) => unsafe {
|
||||||
sys::JS_Throw(context.ctx, err.val)
|
// Because context.new_string yields an owned Value, and will
|
||||||
|
// clean it up on the way out, we need to explicitly DupValue a
|
||||||
|
// reference for the `Throw` to own.
|
||||||
|
let err = sys::JS_NewError(ctx);
|
||||||
|
if sys::JS_ValueGetTag(err) == sys::JS_TAG_EXCEPTION {
|
||||||
|
// GIVE UP; this is out of memory anyway things probably went
|
||||||
|
// wrong because of that.
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
sys::JS_DupValue(ctx, e.val); // SetProperty takes ownership.
|
||||||
|
let prop = CString::new("message").unwrap();
|
||||||
|
if sys::JS_SetPropertyStr(ctx, err, prop.as_ptr(), e.val) == -1 {
|
||||||
|
// Also an out of memory but we need to free the error object
|
||||||
|
// on our way out.
|
||||||
|
sys::JS_FreeValue(ctx, err);
|
||||||
|
return sys::JS_MakeException(); // JS_EXCEPTION
|
||||||
|
}
|
||||||
|
|
||||||
|
sys::JS_Throw(ctx, err)
|
||||||
|
},
|
||||||
|
Err(_) => unsafe {
|
||||||
|
sys::JS_Throw(
|
||||||
|
ctx,
|
||||||
|
sys::JS_NewString(ctx, "Errors within errors: embedded nulls in the description of the error that occurred".as_bytes().as_ptr() as *const i8),
|
||||||
|
)
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,34 +0,0 @@
|
||||||
use crate::{ContextRef, Value, ValueRef};
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct Promise {
|
|
||||||
pub object: Value,
|
|
||||||
pub resolve_fn: Value,
|
|
||||||
pub reject_fn: Value,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Promise {
|
|
||||||
pub(crate) fn new(object: Value, resolve_fn: Value, reject_fn: Value) -> Self {
|
|
||||||
Promise {
|
|
||||||
object,
|
|
||||||
resolve_fn,
|
|
||||||
reject_fn,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn dup(&self, ctx: &ContextRef) -> Self {
|
|
||||||
Promise {
|
|
||||||
object: self.object.dup(ctx),
|
|
||||||
resolve_fn: self.resolve_fn.dup(ctx),
|
|
||||||
reject_fn: self.reject_fn.dup(ctx),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn resolve(self, context: &ContextRef, value: &ValueRef) {
|
|
||||||
let _ = self.resolve_fn.call(context, &[value]);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn reject(self, context: &ContextRef, value: &ValueRef) {
|
|
||||||
let _ = self.reject_fn.call(context, &[value]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -310,16 +310,14 @@ impl ValueRef {
|
||||||
Ok(result)
|
Ok(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn call(&self, ctx: &ContextRef, args: &[&ValueRef]) -> Result<Value> {
|
pub fn call(&self, ctx: &ContextRef) -> Result<Value> {
|
||||||
// TODO: There *must* be a way to avoid this allocation.
|
|
||||||
let mut args: Vec<sys::JSValue> = args.iter().map(|v| v.val).collect();
|
|
||||||
unsafe {
|
unsafe {
|
||||||
ctx.check_exception(sys::JS_Call(
|
ctx.check_exception(sys::JS_Call(
|
||||||
ctx.ctx,
|
ctx.ctx,
|
||||||
self.val,
|
self.val,
|
||||||
sys::JS_MakeUndefined(),
|
sys::JS_MakeUndefined(),
|
||||||
args.len() as i32,
|
0,
|
||||||
args.as_mut_ptr(),
|
std::ptr::null_mut(),
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -347,16 +345,9 @@ impl Value {
|
||||||
/// the runtime of the specified context, if not the context itself. This
|
/// the runtime of the specified context, if not the context itself. This
|
||||||
/// function makes no attempt to validate this.
|
/// function makes no attempt to validate this.
|
||||||
pub(crate) fn from_raw(val: sys::JSValue, ctx: &ContextRef) -> Self {
|
pub(crate) fn from_raw(val: sys::JSValue, ctx: &ContextRef) -> Self {
|
||||||
Value::from_raw_rt(
|
|
||||||
val,
|
|
||||||
Runtime::from_raw(unsafe { sys::JS_GetRuntime(ctx.ctx) }),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn from_raw_rt(val: sys::JSValue, rt: Runtime) -> Self {
|
|
||||||
Value {
|
Value {
|
||||||
value: ValueRef::from_raw(val),
|
value: ValueRef::from_raw(val),
|
||||||
rt,
|
rt: Runtime::from_raw(unsafe { sys::JS_GetRuntime(ctx.ctx) }),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -403,15 +394,6 @@ impl fmt::Debug for Value {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Clone for Value {
|
|
||||||
fn clone(&self) -> Self {
|
|
||||||
unsafe {
|
|
||||||
sys::JS_DupValueRT(self.rt.rt, self.val);
|
|
||||||
}
|
|
||||||
Value::from_raw_rt(self.val, self.rt.clone())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
|
||||||
42
src/lib.rs
42
src/lib.rs
|
|
@ -178,6 +178,22 @@ impl State {
|
||||||
label: Some("sprite_bind_group_layout"),
|
label: Some("sprite_bind_group_layout"),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// 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 = ScreenUniforms::new(size.width, size.height);
|
||||||
let screen_uniform_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
|
let screen_uniform_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
|
||||||
label: Some("Screen Uniform Buffer"),
|
label: Some("Screen Uniform Buffer"),
|
||||||
|
|
@ -189,7 +205,7 @@ impl State {
|
||||||
device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
|
device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
|
||||||
entries: &[wgpu::BindGroupLayoutEntry {
|
entries: &[wgpu::BindGroupLayoutEntry {
|
||||||
binding: 0,
|
binding: 0,
|
||||||
visibility: wgpu::ShaderStages::VERTEX | wgpu::ShaderStages::FRAGMENT,
|
visibility: wgpu::ShaderStages::VERTEX,
|
||||||
ty: wgpu::BindingType::Buffer {
|
ty: wgpu::BindingType::Buffer {
|
||||||
ty: wgpu::BufferBindingType::Uniform,
|
ty: wgpu::BufferBindingType::Uniform,
|
||||||
has_dynamic_offset: false,
|
has_dynamic_offset: false,
|
||||||
|
|
@ -209,28 +225,28 @@ impl State {
|
||||||
label: Some("camera_bind_group"),
|
label: Some("camera_bind_group"),
|
||||||
});
|
});
|
||||||
|
|
||||||
let sprite_shader = device.create_shader_module(wgpu::ShaderModuleDescriptor {
|
let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor {
|
||||||
label: Some("Sprite Shader"),
|
label: Some("Shader"),
|
||||||
source: wgpu::ShaderSource::Wgsl(include_str!("sprite_shader.wgsl").into()),
|
source: wgpu::ShaderSource::Wgsl(include_str!("shader.wgsl").into()),
|
||||||
});
|
});
|
||||||
|
|
||||||
let sprite_pipeline_layout =
|
let render_pipeline_layout =
|
||||||
device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
|
device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
|
||||||
label: Some("Sprite Pipeline Layout"),
|
label: Some("Render Pipeline Layout"),
|
||||||
bind_group_layouts: &[&sprite_bind_group_layout, &screen_uniform_bind_group_layout],
|
bind_group_layouts: &[&sprite_bind_group_layout, &screen_uniform_bind_group_layout],
|
||||||
push_constant_ranges: &[],
|
push_constant_ranges: &[],
|
||||||
});
|
});
|
||||||
|
|
||||||
let sprite_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
|
let render_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
|
||||||
label: Some("Sprite Pipeline"),
|
label: Some("Render Pipeline"),
|
||||||
layout: Some(&sprite_pipeline_layout),
|
layout: Some(&render_pipeline_layout),
|
||||||
vertex: wgpu::VertexState {
|
vertex: wgpu::VertexState {
|
||||||
module: &sprite_shader,
|
module: &shader,
|
||||||
entry_point: "vs_main",
|
entry_point: "vs_main",
|
||||||
buffers: &[Vertex::desc()],
|
buffers: &[Vertex::desc()],
|
||||||
},
|
},
|
||||||
fragment: Some(wgpu::FragmentState {
|
fragment: Some(wgpu::FragmentState {
|
||||||
module: &sprite_shader,
|
module: &shader,
|
||||||
entry_point: "fs_main",
|
entry_point: "fs_main",
|
||||||
targets: &[Some(wgpu::ColorTargetState {
|
targets: &[Some(wgpu::ColorTargetState {
|
||||||
format: config.format,
|
format: config.format,
|
||||||
|
|
@ -276,7 +292,7 @@ impl State {
|
||||||
queue,
|
queue,
|
||||||
config,
|
config,
|
||||||
size,
|
size,
|
||||||
render_pipeline: sprite_pipeline,
|
render_pipeline,
|
||||||
vertex_buffer,
|
vertex_buffer,
|
||||||
max_vertices,
|
max_vertices,
|
||||||
sprite_bind_group_layout,
|
sprite_bind_group_layout,
|
||||||
|
|
@ -513,7 +529,7 @@ pub async fn run() {
|
||||||
|
|
||||||
let mut state = State::new(window).await;
|
let mut state = State::new(window).await;
|
||||||
|
|
||||||
let mut context = script::ScriptContext::new();
|
let context = script::ScriptContext::new();
|
||||||
context.init();
|
context.init();
|
||||||
|
|
||||||
event_loop.run(move |event, _, control_flow| {
|
event_loop.run(move |event, _, control_flow| {
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,9 @@
|
||||||
use oden_js::{
|
use oden_js::{
|
||||||
module::loader::{ModuleLoader, ModuleSource},
|
module::loader::{ModuleLoader, ModuleSource},
|
||||||
Context, ContextRef, Promise, Result, Runtime, Value, ValueResult,
|
Context, ContextRef, Result, Runtime, Value,
|
||||||
};
|
};
|
||||||
use std::collections::HashMap;
|
|
||||||
use std::ffi::OsStr;
|
use std::ffi::OsStr;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use std::sync::atomic::{AtomicU64, Ordering};
|
|
||||||
use std::sync::mpsc::{channel, Receiver};
|
use std::sync::mpsc::{channel, Receiver};
|
||||||
|
|
||||||
pub mod graphics;
|
pub mod graphics;
|
||||||
|
|
@ -39,21 +37,6 @@ impl ModuleLoader for Loader {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Eq, PartialEq, Hash, Debug, Clone, Copy)]
|
|
||||||
pub struct PromiseHandle(u64);
|
|
||||||
|
|
||||||
impl PromiseHandle {
|
|
||||||
pub fn new() -> Self {
|
|
||||||
static NEXT_ID: AtomicU64 = AtomicU64::new(0);
|
|
||||||
PromiseHandle(NEXT_ID.fetch_add(1, Ordering::SeqCst))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub enum ScriptEvent {
|
|
||||||
AddPromise(PromiseHandle, Promise),
|
|
||||||
CompletePromise(PromiseHandle, Box<dyn FnOnce(&ContextRef) -> ValueResult>),
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct ScriptContext {
|
pub struct ScriptContext {
|
||||||
context: Context,
|
context: Context,
|
||||||
init: Value,
|
init: Value,
|
||||||
|
|
@ -63,9 +46,6 @@ pub struct ScriptContext {
|
||||||
gfx: graphics::GraphicsAPI,
|
gfx: graphics::GraphicsAPI,
|
||||||
_assets: assets::AssetsAPI,
|
_assets: assets::AssetsAPI,
|
||||||
gfx_receive: Receiver<graphics::GraphicsCommand>,
|
gfx_receive: Receiver<graphics::GraphicsCommand>,
|
||||||
|
|
||||||
script_receive: Receiver<ScriptEvent>,
|
|
||||||
promises: HashMap<PromiseHandle, Promise>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ScriptContext {
|
impl ScriptContext {
|
||||||
|
|
@ -78,7 +58,6 @@ impl ScriptContext {
|
||||||
context.add_intrinsic_operators();
|
context.add_intrinsic_operators();
|
||||||
|
|
||||||
let (gfx_send, gfx_receive) = channel();
|
let (gfx_send, gfx_receive) = channel();
|
||||||
let (script_send, script_receive) = channel();
|
|
||||||
|
|
||||||
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");
|
||||||
|
|
@ -110,9 +89,6 @@ impl ScriptContext {
|
||||||
gfx_receive,
|
gfx_receive,
|
||||||
|
|
||||||
_assets: assets,
|
_assets: assets,
|
||||||
|
|
||||||
script_receive,
|
|
||||||
promises: HashMap::new(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -120,52 +96,18 @@ impl ScriptContext {
|
||||||
// We would want a bi-directional gate for frames to not let the
|
// We would want a bi-directional gate for frames to not let the
|
||||||
// game thread go to fast probably? And to discard whole frames &c.
|
// game thread go to fast probably? And to discard whole frames &c.
|
||||||
|
|
||||||
pub fn init(&mut self) {
|
pub fn init(&self) {
|
||||||
self.init
|
self.init.call(&self.context).expect("Exception in init");
|
||||||
.call(&self.context, &[])
|
|
||||||
.expect("Exception in init");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update(&mut self) {
|
pub fn update(&self) {
|
||||||
// Handle any promises that have completed before calling update.
|
|
||||||
while let Ok(event) = self.script_receive.try_recv() {
|
|
||||||
match event {
|
|
||||||
// TODO: Capture debugging information.
|
|
||||||
ScriptEvent::AddPromise(handle, promise) => {
|
|
||||||
self.promises.insert(handle, promise);
|
|
||||||
}
|
|
||||||
ScriptEvent::CompletePromise(handle, value_producer) => {
|
|
||||||
if let Some(promise) = self.promises.remove(&handle) {
|
|
||||||
let result = value_producer(&self.context);
|
|
||||||
match result {
|
|
||||||
Ok(v) => {
|
|
||||||
promise.resolve(&self.context, &v);
|
|
||||||
}
|
|
||||||
Err(e) => {
|
|
||||||
let error = e.to_js_error(&self.context);
|
|
||||||
promise.reject(&self.context, &error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Tell the runtime to process all pending "jobs".
|
|
||||||
self.context
|
|
||||||
.process_all_jobs()
|
|
||||||
.expect("Error processing async jobs");
|
|
||||||
|
|
||||||
// Now run the update function.
|
|
||||||
self.update
|
self.update
|
||||||
.call(&self.context, &[])
|
.call(&self.context)
|
||||||
.expect("Exception in update");
|
.expect("Exception in update");
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn render(&mut self) -> Vec<graphics::GraphicsCommand> {
|
pub fn render(&self) -> Vec<graphics::GraphicsCommand> {
|
||||||
self.draw
|
self.draw.call(&self.context).expect("Exception in draw");
|
||||||
.call(&self.context, &[])
|
|
||||||
.expect("Exception in draw");
|
|
||||||
self.gfx.end_frame();
|
self.gfx.end_frame();
|
||||||
|
|
||||||
let mut commands = Vec::new();
|
let mut commands = Vec::new();
|
||||||
|
|
|
||||||
|
|
@ -48,30 +48,13 @@ const RES = vec2f(320.0, 240.0); // The logical resolution of the screen.
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fragment shader....
|
// Fragment shader
|
||||||
|
|
||||||
@group(0) @binding(0) var t_diffuse : texture_2d<f32>;
|
@group(0) @binding(0) var t_diffuse : texture_2d<f32>;
|
||||||
@group(0) @binding(1) var s_diffuse : sampler;
|
@group(0) @binding(1) var s_diffuse : sampler;
|
||||||
|
|
||||||
@fragment fn fs_main(in : VertexOutput)->@location(0) vec4<f32> {
|
@fragment fn fs_main(in : VertexOutput)->@location(0) vec4<f32> {
|
||||||
// The "screen" is centered in the window, so anything outside of the
|
|
||||||
// screen borders should be black. But *where are they*?
|
|
||||||
let RES_AR = RES.x / RES.y; // The aspect ratio of the logical screen.
|
|
||||||
let screen_ar = screen.resolution.x / screen.resolution.y;
|
|
||||||
var black_mod = 1.0;
|
|
||||||
if (screen_ar > RES_AR) {
|
|
||||||
// Wider than tall, bars are on the left and right.
|
|
||||||
let active_width = screen.resolution.y * RES_AR;
|
|
||||||
let half_delta = (screen.resolution.x - active_width) / 2.0;
|
|
||||||
if (in.clip_position.x < half_delta ||
|
|
||||||
in.clip_position.x > half_delta + active_width) {
|
|
||||||
black_mod = 0.0;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Taller than wide, bars are on top and bottom.
|
|
||||||
}
|
|
||||||
|
|
||||||
let dims = vec2f(textureDimensions(t_diffuse));
|
let dims = vec2f(textureDimensions(t_diffuse));
|
||||||
|
|
||||||
return black_mod * textureSample(t_diffuse, s_diffuse, in.tex_coords / dims);
|
return textureSample(t_diffuse, s_diffuse, in.tex_coords / dims);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,77 +0,0 @@
|
||||||
// Vertex shader
|
|
||||||
|
|
||||||
struct ScreenUniform {
|
|
||||||
resolution : vec2f,
|
|
||||||
};
|
|
||||||
@group(1) @binding(0) // 1.
|
|
||||||
var<uniform> screen : ScreenUniform;
|
|
||||||
|
|
||||||
struct VertexInput {
|
|
||||||
@location(0) position : vec3<f32>, @location(1) tex_coords : vec2<f32>,
|
|
||||||
};
|
|
||||||
|
|
||||||
struct VertexOutput {
|
|
||||||
@builtin(position) clip_position : vec4<f32>,
|
|
||||||
@location(0) tex_coords : vec2<f32>,
|
|
||||||
};
|
|
||||||
|
|
||||||
const RES = vec2f(320.0, 240.0); // The logical resolution of the screen.
|
|
||||||
|
|
||||||
@vertex fn vs_main(model : VertexInput)->VertexOutput {
|
|
||||||
var out : VertexOutput;
|
|
||||||
out.tex_coords = model.tex_coords;
|
|
||||||
|
|
||||||
let RES_AR = RES.x / RES.y; // The aspect ratio of the logical screen.
|
|
||||||
|
|
||||||
// the actual resolution of the screen.
|
|
||||||
let screen_ar = screen.resolution.x / screen.resolution.y;
|
|
||||||
|
|
||||||
// Compute the difference in resolution ... correctly?
|
|
||||||
//
|
|
||||||
// nudge is the amount to add to the logical resolution so that the pixels
|
|
||||||
// stay the same size but we respect the aspect ratio of the screen. (So
|
|
||||||
// there's more of them in either the x or y direction.)
|
|
||||||
var nudge = vec2f(0.0);
|
|
||||||
if (screen_ar > RES_AR) {
|
|
||||||
nudge.x = (RES.y * screen_ar) - RES.x;
|
|
||||||
} else {
|
|
||||||
nudge.y = (RES.x / screen_ar) - RES.y;
|
|
||||||
}
|
|
||||||
var new_logical_resolution = RES + nudge;
|
|
||||||
|
|
||||||
// Now we can convert the incoming position to clip space, in the new screen.
|
|
||||||
let in_pos = vec2f(model.position.x, model.position.y);
|
|
||||||
let centered = in_pos + (nudge / 2.0);
|
|
||||||
let position = (2.0 * centered / new_logical_resolution) - 1.0;
|
|
||||||
|
|
||||||
out.clip_position = vec4f(position, model.position.z, 1.0);
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fragment shader....
|
|
||||||
|
|
||||||
@group(0) @binding(0) var t_diffuse : texture_2d<f32>;
|
|
||||||
@group(0) @binding(1) var s_diffuse : sampler;
|
|
||||||
|
|
||||||
@fragment fn fs_main(in : VertexOutput)->@location(0) vec4<f32> {
|
|
||||||
// The "screen" is centered in the window, so anything outside of the
|
|
||||||
// screen borders should be black. But *where are they*?
|
|
||||||
let RES_AR = RES.x / RES.y; // The aspect ratio of the logical screen.
|
|
||||||
let screen_ar = screen.resolution.x / screen.resolution.y;
|
|
||||||
var black_mod = 1.0;
|
|
||||||
if (screen_ar > RES_AR) {
|
|
||||||
// Wider than tall, bars are on the left and right.
|
|
||||||
let active_width = screen.resolution.y * RES_AR;
|
|
||||||
let half_delta = (screen.resolution.x - active_width) / 2.0;
|
|
||||||
if (in.clip_position.x < half_delta ||
|
|
||||||
in.clip_position.x > half_delta + active_width) {
|
|
||||||
black_mod = 0.0;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Taller than wide, bars are on top and bottom.
|
|
||||||
}
|
|
||||||
|
|
||||||
let dims = vec2f(textureDimensions(t_diffuse));
|
|
||||||
|
|
||||||
return black_mod * textureSample(t_diffuse, s_diffuse, in.tex_coords / dims);
|
|
||||||
}
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue