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

View file

@ -308,7 +308,12 @@ impl ContextRef {
pub(crate) fn exception_error(&self) -> Error { pub(crate) fn exception_error(&self) -> Error {
let exc = self.exception(); let exc = self.exception();
let desc = exc.to_string(&self).unwrap_or_else(|_| String::new()); 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; use std::num::TryFromIntError;
pub trait TryFromValue: Sized { 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> { impl<T: TryFromValue> TryFromValue for Option<T> {
#[inline] #[inline]
fn try_from_value(value: &ValueRef, ctx: &ContextRef) -> Result<Self> { fn try_from_value(value: &ValueRef, ctx: &ContextRef) -> Result<Self> {
if value.is_undefined() { if value.is_null() || value.is_undefined() {
Ok(None) Ok(None)
} else { } else {
Ok(Some(T::try_from_value(value, ctx)?)) Ok(Some(T::try_from_value(value, ctx)?))

View file

@ -7,21 +7,21 @@ pub trait TryIntoValue {
impl TryIntoValue for u8 { impl TryIntoValue for u8 {
#[inline] #[inline]
fn try_into_value(self, ctx: &ContextRef) -> ValueResult { fn try_into_value(self, ctx: &ContextRef) -> ValueResult {
ctx.new_u64(self) ctx.new_u32(self)
} }
} }
impl TryIntoValue for u16 { impl TryIntoValue for u16 {
#[inline] #[inline]
fn try_into_value(self, ctx: &ContextRef) -> ValueResult { fn try_into_value(self, ctx: &ContextRef) -> ValueResult {
ctx.new_u64(self) ctx.new_u32(self)
} }
} }
impl TryIntoValue for u32 { impl TryIntoValue for u32 {
#[inline] #[inline]
fn try_into_value(self, ctx: &ContextRef) -> ValueResult { 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::ConversionError(e) => Err(Error::ConversionError(e)),
Error::RustFunctionError(e) => Err(Error::RustFunctionError(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::OutOfMemory => Err(Error::OutOfMemory),
Error::IOError(e) => Err(Error::IOError(e)), Error::IOError(e) => Err(Error::IOError(e)),
Error::ParseError(name, err) => Err(Error::ParseError(name, err)), Error::ParseError(name, err) => Err(Error::ParseError(name, err)),
@ -135,8 +135,15 @@ impl<T: TryIntoValue> TryIntoValue for Option<T> {
#[inline] #[inline]
fn try_into_value(self, ctx: &ContextRef) -> ValueResult { fn try_into_value(self, ctx: &ContextRef) -> ValueResult {
match self { match self {
None => Ok(ctx.undefined()), None => Ok(ctx.null()),
Some(v) => v.try_into_value(ctx), 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 runtime::Runtime;
pub use value::{Value, ValueRef, ValueType}; pub use value::{Value, ValueRef, ValueType};
#[derive(Error, Debug)] #[derive(Debug, Error)]
pub enum Error { pub enum Error {
#[error("too many classes have been registered")] #[error("too many classes have been registered")]
TooManyClasses, TooManyClasses,
@ -39,8 +39,8 @@ pub enum Error {
ConversionError(String), ConversionError(String),
#[error("an error occurred calling a rust function: {0}")] #[error("an error occurred calling a rust function: {0}")]
RustFunctionError(String), RustFunctionError(String),
#[error("an exception was thrown during evaluation: {1}")] #[error("an exception was thrown during evaluation: {1}\nStack: {2}")]
Exception(Value, String), Exception(Value, String, String),
#[error("out of memory")] #[error("out of memory")]
OutOfMemory, OutOfMemory,
#[error("an io error occurred: {0}")] #[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 { pub(crate) fn throw_error(context: &ContextRef, error: Error) -> sys::JSValue {
match error { match error {
Error::Exception(v, _) => unsafe { Error::Exception(v, _, _) => unsafe {
sys::JS_DupValue(context.ctx, v.val); sys::JS_DupValue(context.ctx, v.val);
sys::JS_Throw(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); let context = ContextRef::from_raw(ctx);
match NativeModuleState::<T>::define(&context, m) { match NativeModuleState::<T>::define(&context, m) {
Ok(_) => 0, Ok(_) => 0,
Err(Error::Exception(e, _)) => unsafe { Err(Error::Exception(e, _, _)) => unsafe {
// If we returned `Error::Exception` then we're propagating an // If we returned `Error::Exception` then we're propagating an
// exception through the JS stack, just flip it. // exception through the JS stack, just flip it.
let exc = &e.val; let exc = &e.val;

View file

@ -137,9 +137,7 @@ impl ValueRef {
let mut res: u32 = 0; let mut res: u32 = 0;
let ret = sys::JS_ToUint32(ctx.ctx, &mut res, self.val); let ret = sys::JS_ToUint32(ctx.ctx, &mut res, self.val);
if ret < 0 { if ret < 0 {
let exc = ctx.exception(); Err(ctx.exception_error())
let desc = exc.to_string(&ctx).unwrap_or_else(|_| String::new());
Err(Error::Exception(exc, desc))
} else { } else {
Ok(res) 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; sh = sh || h;
core.spr(x, y, w, h, sx, sy, sw, sh); 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 bytemuck;
use std::collections::HashMap;
use wgpu::util::DeviceExt; use wgpu::util::DeviceExt;
use winit::{ use winit::{
event::*, event::*,
@ -65,7 +66,8 @@ struct State {
vertex_buffer: wgpu::Buffer, vertex_buffer: wgpu::Buffer,
max_vertices: usize, 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: ScreenUniforms,
screen_uniform_buffer: wgpu::Buffer, screen_uniform_buffer: wgpu::Buffer,
@ -146,11 +148,12 @@ impl State {
}; };
surface.configure(&device, &config); surface.configure(&device, &config);
let diffuse_bytes = include_bytes!("happy-tree.png"); // TODO: DELETE THIS
let diffuse_texture = // let diffuse_bytes = include_bytes!("happy-tree.png");
texture::Texture::from_bytes(&device, &queue, diffuse_bytes, "happy-tree.png").unwrap(); // 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 { device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
entries: &[ entries: &[
wgpu::BindGroupLayoutEntry { wgpu::BindGroupLayoutEntry {
@ -172,23 +175,24 @@ impl State {
count: None, count: None,
}, },
], ],
label: Some("texture_bind_group_layout"), label: Some("sprite_bind_group_layout"),
}); });
let diffuse_bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor { // TODO: DELETE THIS
layout: &texture_bind_group_layout, // let sprite_bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
entries: &[ // layout: &sprite_bind_group_layout,
wgpu::BindGroupEntry { // entries: &[
binding: 0, // wgpu::BindGroupEntry {
resource: wgpu::BindingResource::TextureView(&diffuse_texture.view), // binding: 0,
}, // resource: wgpu::BindingResource::TextureView(&diffuse_texture.view),
wgpu::BindGroupEntry { // },
binding: 1, // wgpu::BindGroupEntry {
resource: wgpu::BindingResource::Sampler(&diffuse_texture.sampler), // binding: 1,
}, // resource: wgpu::BindingResource::Sampler(&diffuse_texture.sampler),
], // },
label: Some("diffuse_bind_group"), // ],
}); // 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 {
@ -229,10 +233,7 @@ impl State {
let render_pipeline_layout = let render_pipeline_layout =
device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
label: Some("Render Pipeline Layout"), label: Some("Render Pipeline Layout"),
bind_group_layouts: &[ bind_group_layouts: &[&sprite_bind_group_layout, &screen_uniform_bind_group_layout],
&texture_bind_group_layout,
&screen_uniform_bind_group_layout,
],
push_constant_ranges: &[], push_constant_ranges: &[],
}); });
@ -294,7 +295,8 @@ impl State {
render_pipeline, render_pipeline,
vertex_buffer, vertex_buffer,
max_vertices, max_vertices,
diffuse_bind_group, sprite_bind_group_layout,
sprite_textures: HashMap::new(),
screen_uniform, screen_uniform,
screen_uniform_buffer, screen_uniform_buffer,
screen_uniform_bind_group, screen_uniform_bind_group,
@ -348,6 +350,31 @@ impl State {
color: Some(cc.color), color: Some(cc.color),
commands: Vec::new(), 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 => (), GraphicsCommand::EndFrame => (),
other => match passes.last_mut() { other => match passes.last_mut() {
Some(pass) => pass.commands.push(other), Some(pass) => pass.commands.push(other),
@ -395,6 +422,7 @@ impl State {
depth_stencil_attachment: None, depth_stencil_attachment: None,
}); });
let mut texture_id = None;
vertices.clear(); vertices.clear();
for command in pass.commands { for command in pass.commands {
match command { match command {
@ -428,20 +456,30 @@ impl State {
}); });
} }
GraphicsCommand::UseTexture(id) => texture_id = Some(id),
GraphicsCommand::CreateTexture(_) => (), // Already handled
GraphicsCommand::Clear(_) => (), // Already handled GraphicsCommand::Clear(_) => (), // Already handled
GraphicsCommand::EndFrame => (), // Should never appear GraphicsCommand::EndFrame => (), // Should never appear
} }
} }
if let Some(id) = texture_id {
assert!(vertices.len() < self.max_vertices); // ! assert!(vertices.len() < self.max_vertices); // !
self.queue self.queue.write_buffer(
.write_buffer(&self.vertex_buffer, 0, bytemuck::cast_slice(&vertices)); &self.vertex_buffer,
0,
bytemuck::cast_slice(&vertices),
);
render_pass.set_pipeline(&self.render_pipeline); render_pass.set_pipeline(&self.render_pipeline);
render_pass.set_bind_group(0, &self.diffuse_bind_group, &[]);
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_bind_group(1, &self.screen_uniform_bind_group, &[]);
render_pass.set_vertex_buffer(0, self.vertex_buffer.slice(..)); render_pass.set_vertex_buffer(0, self.vertex_buffer.slice(..));
render_pass.draw(0..(vertices.len() as u32), 0..1); render_pass.draw(0..(vertices.len() as u32), 0..1);
} }
}
// Submit will accept anything that implements IntoIter // Submit will accept anything that implements IntoIter
self.queue.submit(std::iter::once(encoder.finish())); self.queue.submit(std::iter::once(encoder.finish()));

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() { export function init() {
print("Hello world!"); print("Hello world!");
the_texture = load_texture("./src/happy-tree.png");
} }
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);
use_texture(the_texture);
spr((320 - 256) / 2, 0, 256, 240, 0, 0); spr((320 - 256) / 2, 0, 256, 240, 0, 0);
} }

View file

@ -12,6 +12,8 @@ use graphics::GraphicsCommand;
mod typescript; mod typescript;
use typescript::transpile_to_javascript; use typescript::transpile_to_javascript;
pub mod assets;
struct Loader {} struct Loader {}
impl Loader { impl Loader {
@ -42,6 +44,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>,
} }
@ -56,8 +59,10 @@ impl ScriptContext {
let (gfx_send, gfx_receive) = channel(); 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"); .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 let module = context
.import_module("./src/main.ts", "") .import_module("./src/main.ts", "")
@ -82,6 +87,8 @@ impl ScriptContext {
gfx, gfx,
gfx_receive, 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::mpsc::Sender;
use std::sync::Arc; use std::sync::Arc;
@ -24,11 +24,20 @@ pub struct SpriteCommand {
pub sh: f32, pub sh: f32,
} }
#[derive(Debug)]
pub struct CreateTextureCommand {
pub id: u32,
pub image: image::DynamicImage,
pub label: String,
}
#[derive(Debug)] #[derive(Debug)]
pub enum GraphicsCommand { pub enum GraphicsCommand {
Clear(ClearCommand), Clear(ClearCommand),
Print(PrintCommand), Print(PrintCommand),
Sprite(SpriteCommand), Sprite(SpriteCommand),
CreateTexture(CreateTextureCommand),
UseTexture(u32),
EndFrame, EndFrame,
} }
@ -41,33 +50,19 @@ impl GraphicsImpl {
GraphicsImpl { sender } GraphicsImpl { sender }
} }
fn print_fn(&self, ctx: &ContextRef, text: String) -> ValueResult { fn print(&self, text: String) -> () {
let _ = self let _ = self
.sender .sender
.send(GraphicsCommand::Print(PrintCommand { text })); .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 { let _ = self.sender.send(GraphicsCommand::Clear(ClearCommand {
color: [r, g, b, 1.0], color: [r, g, b, 1.0],
})); }));
Ok(Value::undefined(ctx))
} }
fn spr_fn( fn spr(&self, x: f32, y: f32, w: f32, h: f32, u: f32, v: f32, sw: f32, sh: f32) {
&self,
ctx: &ContextRef,
x: f32,
y: f32,
w: f32,
h: f32,
u: f32,
v: f32,
sw: f32,
sh: f32,
) -> ValueResult {
let _ = self.sender.send(GraphicsCommand::Sprite(SpriteCommand { let _ = self.sender.send(GraphicsCommand::Sprite(SpriteCommand {
x, x,
y, y,
@ -78,7 +73,10 @@ impl GraphicsImpl {
sw, sw,
sh, 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(); let gfx = gfx.clone();
builder.export( builder.export(
"print", "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(); let gfx = gfx.clone();
builder.export( builder.export(
"cls", "cls",
ctx.new_fn(move |ctx: &ContextRef, r: f64, g: f64, b: f64| { ctx.new_fn(move |_: &ContextRef, r: f64, g: f64, b: f64| gfx.cls(r, g, b))?,
gfx.cls_fn(ctx, r, g, b)
})?,
)?; )?;
} }
{ {
@ -111,7 +107,7 @@ impl GraphicsAPI {
builder.export( builder.export(
"spr", "spr",
ctx.new_fn( ctx.new_fn(
move |ctx: &ContextRef, move |_: &ContextRef,
x: f32, x: f32,
y: f32, y: f32,
w: f32, w: f32,
@ -119,12 +115,17 @@ impl GraphicsAPI {
u: f32, u: f32,
v: f32, v: f32,
sw: f32, sw: f32,
sh: f32| { sh: f32| gfx.spr(x, y, w, h, u, v, sw, sh),
gfx.spr_fn(ctx, 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")?; builder.build("graphics-core")?;
Ok(GraphicsAPI { gfx }) Ok(GraphicsAPI { gfx })
} }

View file

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