18 Commits

Author SHA1 Message Date
BjoernSchilberg
9d437457f7 Merge 937ccf60c9 into a651107104 2023-09-03 11:33:19 -07:00
a651107104 add start and snd functions to filter exports exclude list 2023-09-03 10:19:30 +02:00
22c35e37f4 remove debug trace 2023-09-02 15:37:27 +02:00
805c939097 add support for sound devices that only accept 16bit audio 2023-08-23 23:03:23 +02:00
440e150896 update dependencies 2023-08-23 00:52:51 +02:00
77b2e27346 clamp snd samples into valid -1.0..1.0 range 2023-08-23 00:20:35 +02:00
09e4fcbf14 add --scale option 2023-08-11 23:52:19 +02:00
dbeb242fb2 add support for br_table instruction when packing cart 2023-03-20 23:08:42 +01:00
BjoernSchilberg
937ccf60c9 Added a tinygo example. 2023-02-12 22:24:16 +01:00
4fe4bce0ad token env var has changed 2023-01-31 00:14:25 +01:00
fe86153562 use fixed version of zola-deploy-action 2023-01-31 00:01:07 +01:00
c9dadaca2e try add safe.directory 2023-01-30 09:48:57 +01:00
5dc3e281ce and again 2023-01-30 09:45:28 +01:00
ce3afb821f another attempt changing the owner to fix permission issue 2023-01-30 09:43:54 +01:00
2652a351ad remove chown again 2023-01-30 09:38:57 +01:00
9109722409 fix main.yml 2023-01-30 09:37:12 +01:00
f861c262a1 update ci actions, hopefully fix permission error 2023-01-30 09:15:09 +01:00
bbfb5eba49 add back event debouncing in file watcher 2023-01-30 00:09:25 +01:00
19 changed files with 1783 additions and 1810 deletions

View File

@@ -30,9 +30,9 @@ jobs:
run: sudo apt-get install -y libxkbcommon-dev libasound2-dev run: sudo apt-get install -y libxkbcommon-dev libasound2-dev
if: matrix.os == 'ubuntu-latest' if: matrix.os == 'ubuntu-latest'
- name: Checkout - name: Checkout
uses: actions/checkout@v2 uses: actions/checkout@v3
- name: Cache build dirs - name: Cache build dirs
uses: actions/cache@v2 uses: actions/cache@v3
with: with:
path: | path: |
~/.cargo/bin/ ~/.cargo/bin/
@@ -44,7 +44,7 @@ jobs:
- name: Build - name: Build
run: cargo build --release --verbose run: cargo build --release --verbose
- name: Upload artifact - name: Upload artifact
uses: actions/upload-artifact@v2 uses: actions/upload-artifact@v3
with: with:
name: uw8-${{ matrix.build }} name: uw8-${{ matrix.build }}
path: target/release/${{ matrix.exe }} path: target/release/${{ matrix.exe }}

View File

@@ -8,12 +8,12 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: checkout - name: checkout
uses: actions/checkout@v2 uses: actions/checkout@v3
- name: build_and_deploy - name: build_and_deploy
uses: shalzz/zola-deploy-action@v0.14.1 uses: shalzz/zola-deploy-action@70a101a14bbdeed13e7a42a9ed06b35c9e9e826e
env: env:
# Target branch # Target branch
PAGES_BRANCH: gh-pages PAGES_BRANCH: gh-pages
BUILD_DIR: site BUILD_DIR: site
# Provide personal access token # Provide personal access token
TOKEN: $GITHUB_ACTOR:${{ secrets.GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

2322
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -11,21 +11,21 @@ native = ["wasmtime", "uw8-window", "cpal", "rubato" ]
browser = ["warp", "tokio", "tokio-stream", "webbrowser"] browser = ["warp", "tokio", "tokio-stream", "webbrowser"]
[dependencies] [dependencies]
wasmtime = { version = "5.0.0", optional = true } wasmtime = { version = "12.0.0", optional = true }
anyhow = "1" anyhow = "1"
env_logger = "0.10" env_logger = "0.10"
log = "0.4" log = "0.4"
uw8-window = { path = "uw8-window", optional = true } uw8-window = { path = "uw8-window", optional = true }
notify = "5" notify-debouncer-mini = { version = "0.4.1", default-features = false }
pico-args = "0.5" pico-args = "0.5"
curlywas = { git = "https://github.com/exoticorn/curlywas.git", rev = "0e7ea50" } curlywas = { git = "https://github.com/exoticorn/curlywas.git", rev = "0e7ea50" }
wat = "1" wat = "1"
uw8-tool = { path = "uw8-tool" } uw8-tool = { path = "uw8-tool" }
same-file = "1" same-file = "1"
warp = { version = "0.3.3", optional = true } warp = { version = "0.3.5", optional = true }
tokio = { version = "1.24.0", features = ["sync", "rt"], optional = true } tokio = { version = "1.32.0", features = ["sync", "rt"], optional = true }
tokio-stream = { version = "0.1.11", features = ["sync"], optional = true } tokio-stream = { version = "0.1.14", features = ["sync"], optional = true }
webbrowser = { version = "0.8.6", optional = true } webbrowser = { version = "0.8.11", optional = true }
ansi_term = "0.12.1" ansi_term = "0.12.1"
cpal = { version = "0.14.2", optional = true } cpal = { version = "0.15.2", optional = true }
rubato = { version = "0.12.0", optional = true } rubato = { version = "0.14.0", optional = true }

6
examples/tinygo/build.sh Executable file
View File

@@ -0,0 +1,6 @@
#!/bin/bash
tinygo build -o cart.wasm -target target.json ./main.go
uw8 filter-exports cart.wasm cart.wasm && \
#wasm-opt -Oz --fast-math --strip-producers -o cart.wasm cart.wasm && \
uw8 pack -l 9 cart.wasm cart.uw8

38
examples/tinygo/main.go Normal file
View File

@@ -0,0 +1,38 @@
package main
import (
"math"
"unsafe"
)
//go:wasm-module env
//export atan2
func atan2(x, y float32) float32
//go:wasm-module env
//export time
func time() float32
func sqrt(v float32) float32 {
return float32(math.Sqrt(float64(v)))
}
var FRAMEBUFFER = (*[320 * 240]byte)(unsafe.Pointer(uintptr(120)))
//export upd
func upd() {
var i int
for i < 320*240 {
t := time() * 63.0
x := float32(i%320 - 160)
y := float32(i/320 - 120)
d := float32(40000.0) / sqrt(x*x+y*y)
u := atan2(x, y) * 512.0 / 3.141
c := uint8((int(d+t*2.0) ^ int(u+t)) >> 4)
FRAMEBUFFER[i] = c
i++
}
}
func main() {
}

View File

@@ -0,0 +1,23 @@
{
"llvm-target": "wasm32--wasi",
"build-tags": [ "tinygo.wasm" ],
"goos": "js",
"goarch": "wasm",
"linker": "wasm-ld",
"libc": "wasi-libc",
"cflags": [
"--target=wasm32--wasi",
"--sysroot={root}/lib/wasi-libc/sysroot",
"-Oz"
],
"ldflags": [
"--no-entry",
"--export-all",
"--import-memory",
"--initial-memory=262144",
"--global-base=81920",
"-zstack-size=4096",
"--strip-all"
],
"wasm-abi": "js"
}

View File

@@ -62,7 +62,6 @@ export fn sndGes(t: i32) -> f32 {
let phase = channelState!GesChannelState.Phase; let phase = channelState!GesChannelState.Phase;
let inline pulseWidth = channelReg?1; let inline pulseWidth = channelReg?1;
let phaseShift = (pulseWidth - 128) * 255;
let invPhaseInc = 1 as f32 / phaseInc as f32; let invPhaseInc = 1 as f32 / phaseInc as f32;
i = 0; i = 0;
@@ -131,7 +130,7 @@ export fn sndGes(t: i32) -> f32 {
let phaseInc = (freq * (65536.0 / 44100.0)) as i32; let phaseInc = (freq * (65536.0 / 44100.0)) as i32;
let phase = channelState!GesChannelState.Phase; let phase = channelState!GesChannelState.Phase;
if modSrc > ch { if modSrc < ch {
phase = phase - (phaseInc << 6); phase = phase - (phaseInc << 6);
} }

View File

@@ -1,23 +1,33 @@
use anyhow::{anyhow, bail, Result}; use anyhow::{anyhow, bail, Result};
use notify::{Event, EventKind, RecommendedWatcher, Watcher}; use notify_debouncer_mini::{
use std::{collections::BTreeSet, path::PathBuf, sync::mpsc}; new_debouncer,
notify::{self, RecommendedWatcher},
DebouncedEvent, DebouncedEventKind, Debouncer,
};
use std::{collections::BTreeSet, path::PathBuf, sync::mpsc, time::Duration};
pub struct FileWatcher { pub struct FileWatcher {
watcher: RecommendedWatcher, debouncer: Debouncer<RecommendedWatcher>,
watched_files: BTreeSet<PathBuf>, watched_files: BTreeSet<PathBuf>,
directories: BTreeSet<PathBuf>, directories: BTreeSet<PathBuf>,
rx: mpsc::Receiver<Event>, rx: mpsc::Receiver<DebouncedEvent>,
} }
impl FileWatcher { impl FileWatcher {
pub fn new() -> Result<FileWatcher> { pub fn new() -> Result<FileWatcher> {
let (tx, rx) = mpsc::channel(); let (tx, rx) = mpsc::channel();
let watcher = notify::recommended_watcher(move |res| match res { let debouncer = new_debouncer(Duration::from_millis(100), move |res| match res {
Ok(event) => _ = tx.send(event), Ok(events) => {
Err(err) => eprintln!("Error watching for file changes: {err}"), for event in events {
let _ = tx.send(event);
}
}
Err(err) => {
eprintln!("Error watching for file changes: {err}");
}
})?; })?;
Ok(FileWatcher { Ok(FileWatcher {
watcher, debouncer,
watched_files: BTreeSet::new(), watched_files: BTreeSet::new(),
directories: BTreeSet::new(), directories: BTreeSet::new(),
rx, rx,
@@ -29,7 +39,8 @@ impl FileWatcher {
let parent = path.parent().ok_or_else(|| anyhow!("File has no parent"))?; let parent = path.parent().ok_or_else(|| anyhow!("File has no parent"))?;
if !self.directories.contains(parent) { if !self.directories.contains(parent) {
self.watcher self.debouncer
.watcher()
.watch(parent, notify::RecursiveMode::NonRecursive)?; .watch(parent, notify::RecursiveMode::NonRecursive)?;
self.directories.insert(parent.to_path_buf()); self.directories.insert(parent.to_path_buf());
} }
@@ -41,13 +52,11 @@ impl FileWatcher {
pub fn poll_changed_file(&self) -> Result<Option<PathBuf>> { pub fn poll_changed_file(&self) -> Result<Option<PathBuf>> {
match self.rx.try_recv() { match self.rx.try_recv() {
Ok(event) => match event.kind { Ok(event) => match event.kind {
EventKind::Create(_) | EventKind::Modify(_) => { DebouncedEventKind::Any => {
for path in event.paths { let handle = same_file::Handle::from_path(&event.path)?;
let handle = same_file::Handle::from_path(&path)?; for file in &self.watched_files {
for file in &self.watched_files { if handle == same_file::Handle::from_path(file)? {
if handle == same_file::Handle::from_path(file)? { return Ok(Some(event.path));
return Ok(Some(path));
}
} }
} }
} }

View File

@@ -7,7 +7,7 @@ use cpal::traits::*;
use rubato::Resampler; use rubato::Resampler;
use uw8_window::{Window, WindowConfig}; use uw8_window::{Window, WindowConfig};
use wasmtime::{ use wasmtime::{
Engine, GlobalType, Memory, MemoryType, Module, Mutability, Store, TypedFunc, ValType, Engine, Func, GlobalType, Memory, MemoryType, Module, Mutability, Store, TypedFunc, ValType,
}; };
pub struct MicroW8 { pub struct MicroW8 {
@@ -90,7 +90,7 @@ impl super::Runtime for MicroW8 {
let memory = wasmtime::Memory::new(&mut store, MemoryType::new(4, Some(4)))?; let memory = wasmtime::Memory::new(&mut store, MemoryType::new(4, Some(4)))?;
let mut linker = wasmtime::Linker::new(&self.engine); let mut linker = wasmtime::Linker::new(&self.engine);
linker.define("env", "memory", memory)?; linker.define(&store, "env", "memory", memory)?;
let loader_instance = linker.instantiate(&mut store, &self.loader_module)?; let loader_instance = linker.instantiate(&mut store, &self.loader_module)?;
let load_uw8 = loader_instance.get_typed_func::<i32, i32>(&mut store, "load_uw8")?; let load_uw8 = loader_instance.get_typed_func::<i32, i32>(&mut store, "load_uw8")?;
@@ -255,15 +255,12 @@ fn add_native_functions(
} }
})?; })?;
for i in 0..16 { for i in 0..16 {
linker.define( let global = wasmtime::Global::new(
"env", &mut *store,
&format!("g_reserved{}", i), GlobalType::new(ValType::I32, Mutability::Const),
wasmtime::Global::new( 0.into(),
&mut *store,
GlobalType::new(ValType::I32, Mutability::Const),
0.into(),
)?,
)?; )?;
linker.define(&store, "env", &format!("g_reserved{}", i), global)?;
} }
Ok(()) Ok(())
@@ -276,14 +273,18 @@ fn instantiate_platform(
) -> Result<wasmtime::Instance> { ) -> Result<wasmtime::Instance> {
let platform_instance = linker.instantiate(&mut *store, &platform_module)?; let platform_instance = linker.instantiate(&mut *store, &platform_module)?;
for export in platform_instance.exports(&mut *store) { let exports: Vec<(String, Func)> = platform_instance
linker.define( .exports(&mut *store)
"env", .map(|e| {
export.name(), (
export e.name().to_owned(),
.into_func() e.into_func()
.expect("platform surely only exports functions"), .expect("platform surely only exports functions"),
)?; )
})
.collect();
for (name, func) in exports {
linker.define(&store, "env", &name, func)?;
} }
Ok(platform_instance) Ok(platform_instance)
@@ -310,7 +311,7 @@ fn init_sound(
let memory = wasmtime::Memory::new(&mut store, MemoryType::new(4, Some(4)))?; let memory = wasmtime::Memory::new(&mut store, MemoryType::new(4, Some(4)))?;
let mut linker = wasmtime::Linker::new(engine); let mut linker = wasmtime::Linker::new(engine);
linker.define("env", "memory", memory)?; linker.define(&store, "env", "memory", memory)?;
add_native_functions(&mut linker, &mut store)?; add_native_functions(&mut linker, &mut store)?;
let platform_instance = instantiate_platform(&mut linker, &mut store, platform_module)?; let platform_instance = instantiate_platform(&mut linker, &mut store, platform_module)?;
@@ -327,23 +328,26 @@ fn init_sound(
let mut configs: Vec<_> = device let mut configs: Vec<_> = device
.supported_output_configs()? .supported_output_configs()?
.filter(|config| { .filter(|config| {
config.channels() == 2 && config.sample_format() == cpal::SampleFormat::F32 config.channels() == 2
&& (config.sample_format() == cpal::SampleFormat::F32
|| config.sample_format() == cpal::SampleFormat::I16)
}) })
.collect(); .collect();
configs.sort_by_key(|config| { configs.sort_by_key(|config| {
let rate = 44100 let rate = 44100
.max(config.min_sample_rate().0) .max(config.min_sample_rate().0)
.min(config.max_sample_rate().0); .min(config.max_sample_rate().0);
if rate >= 44100 { let prio = if rate >= 44100 {
rate - 44100 rate - 44100
} else { } else {
(44100 - rate) * 1000 (44100 - rate) * 1000
} };
prio + (config.sample_format() == cpal::SampleFormat::I16) as u32
}); });
let config = configs let config = configs
.into_iter() .into_iter()
.next() .next()
.ok_or_else(|| anyhow!("Could not find float output config"))?; .ok_or_else(|| anyhow!("Could not find float or 16bit signed output config"))?;
let sample_rate = cpal::SampleRate(44100) let sample_rate = cpal::SampleRate(44100)
.max(config.min_sample_rate()) .max(config.min_sample_rate())
.min(config.max_sample_rate()); .min(config.max_sample_rate());
@@ -354,6 +358,7 @@ fn init_sound(
cpal::BufferSize::Fixed(256.max(min).min(max)) cpal::BufferSize::Fixed(256.max(min).min(max))
} }
}; };
let sample_format = config.sample_format();
let config = cpal::StreamConfig { let config = cpal::StreamConfig {
buffer_size, buffer_size,
..config.config() ..config.config()
@@ -373,8 +378,8 @@ fn init_sound(
None None
} else { } else {
let rs = rubato::FftFixedIn::new(44100, sample_rate, 128, 1, 2)?; let rs = rubato::FftFixedIn::new(44100, sample_rate, 128, 1, 2)?;
let input_buffers = rs.input_buffer_allocate(); let input_buffers = rs.input_buffer_allocate(true);
let output_buffers = rs.output_buffer_allocate(); let output_buffers = rs.output_buffer_allocate(true);
Some(Resampler { Some(Resampler {
resampler: rs, resampler: rs,
input_buffers, input_buffers,
@@ -386,96 +391,130 @@ fn init_sound(
let mut sample_index = 0; let mut sample_index = 0;
let mut pending_updates: Vec<RegisterUpdate> = Vec::with_capacity(30); let mut pending_updates: Vec<RegisterUpdate> = Vec::with_capacity(30);
let mut current_time = 0; let mut current_time = 0;
let stream = device.build_output_stream(
&config, let mut callback = move |mut outer_buffer: &mut [f32], _: &_| {
move |mut outer_buffer: &mut [f32], _| { let mut first_update = true;
let mut first_update = true; while let Ok(update) = rx.try_recv() {
while let Ok(update) = rx.try_recv() { if first_update {
if first_update { current_time += update.time.wrapping_sub(current_time) / 8;
current_time += update.time.wrapping_sub(current_time) / 8; first_update = false;
first_update = false; }
} pending_updates.push(update);
pending_updates.push(update); }
while !outer_buffer.is_empty() {
store.set_epoch_deadline(30);
while pending_updates
.first()
.into_iter()
.any(|u| u.time.wrapping_sub(current_time) <= 0)
{
let update = pending_updates.remove(0);
memory.write(&mut store, 80, &update.data).unwrap();
} }
while !outer_buffer.is_empty() { let duration = if let Some(update) = pending_updates.first() {
store.set_epoch_deadline(30); ((update.time.wrapping_sub(current_time) as usize) * sample_rate + 999) / 1000
while pending_updates } else {
.first() outer_buffer.len()
.into_iter() };
.any(|u| u.time.wrapping_sub(current_time) <= 0) let step_size = (duration.max(64) * 2).min(outer_buffer.len());
{
let update = pending_updates.remove(0);
memory.write(&mut store, 80, &update.data).unwrap();
}
let duration = if let Some(update) = pending_updates.first() { let mut buffer = &mut outer_buffer[..step_size];
((update.time.wrapping_sub(current_time) as usize) * sample_rate + 999) / 1000
{
let mem = memory.data_mut(&mut store);
mem[64..68].copy_from_slice(&current_time.to_le_bytes());
}
fn clamp_sample(s: f32) -> f32 {
if s.is_nan() {
0.0
} else { } else {
outer_buffer.len() s.max(-1.0).min(1.0)
};
let step_size = (duration.max(64) * 2).min(outer_buffer.len());
let mut buffer = &mut outer_buffer[..step_size];
{
let mem = memory.data_mut(&mut store);
mem[64..68].copy_from_slice(&current_time.to_le_bytes());
} }
}
if let Some(ref mut resampler) = resampler { if let Some(ref mut resampler) = resampler {
while !buffer.is_empty() { while !buffer.is_empty() {
let copy_size = resampler.output_buffers[0] let copy_size = resampler.output_buffers[0]
.len() .len()
.saturating_sub(resampler.output_index) .saturating_sub(resampler.output_index)
.min(buffer.len() / 2); .min(buffer.len() / 2);
if copy_size == 0 { if copy_size == 0 {
resampler.input_buffers[0].clear(); resampler.input_buffers[0].clear();
resampler.input_buffers[1].clear(); resampler.input_buffers[1].clear();
for _ in 0..resampler.resampler.input_frames_next() { for _ in 0..resampler.resampler.input_frames_next() {
resampler.input_buffers[0] resampler.input_buffers[0].push(clamp_sample(
.push(snd.call(&mut store, (sample_index,)).unwrap_or(0.0)); snd.call(&mut store, (sample_index,)).unwrap_or(0.0),
resampler.input_buffers[1] ));
.push(snd.call(&mut store, (sample_index + 1,)).unwrap_or(0.0)); resampler.input_buffers[1].push(clamp_sample(
sample_index = sample_index.wrapping_add(2); snd.call(&mut store, (sample_index + 1,)).unwrap_or(0.0),
} ));
sample_index = sample_index.wrapping_add(2);
resampler
.resampler
.process_into_buffer(
&resampler.input_buffers,
&mut resampler.output_buffers,
None,
)
.unwrap();
resampler.output_index = 0;
} else {
for i in 0..copy_size {
buffer[i * 2] =
resampler.output_buffers[0][resampler.output_index + i];
buffer[i * 2 + 1] =
resampler.output_buffers[1][resampler.output_index + i];
}
resampler.output_index += copy_size;
buffer = &mut buffer[copy_size * 2..];
} }
}
} else { resampler
for v in buffer { .resampler
*v = snd.call(&mut store, (sample_index,)).unwrap_or(0.0); .process_into_buffer(
sample_index = sample_index.wrapping_add(1); &resampler.input_buffers,
&mut resampler.output_buffers,
None,
)
.unwrap();
resampler.output_index = 0;
} else {
for i in 0..copy_size {
buffer[i * 2] = resampler.output_buffers[0][resampler.output_index + i];
buffer[i * 2 + 1] =
resampler.output_buffers[1][resampler.output_index + i];
}
resampler.output_index += copy_size;
buffer = &mut buffer[copy_size * 2..];
} }
} }
} else {
outer_buffer = &mut outer_buffer[step_size..]; for v in buffer {
current_time = *v = clamp_sample(snd.call(&mut store, (sample_index,)).unwrap_or(0.0));
current_time.wrapping_add((step_size * 500 / sample_rate).max(1) as i32); sample_index = sample_index.wrapping_add(1);
}
} }
},
move |err| { outer_buffer = &mut outer_buffer[step_size..];
dbg!(err); current_time = current_time.wrapping_add((step_size * 500 / sample_rate).max(1) as i32);
}, }
)?; };
let stream = if sample_format == cpal::SampleFormat::F32 {
device.build_output_stream(
&config,
callback,
move |err| {
dbg!(err);
},
None,
)?
} else {
device.build_output_stream(
&config,
move |mut buffer: &mut [i16], info| {
let mut float_buffer = [0f32; 256];
while !buffer.is_empty() {
let step_size = buffer.len().min(float_buffer.len());
let step_buffer = &mut float_buffer[..step_size];
callback(step_buffer, info);
for (dest, src) in buffer.iter_mut().take(step_size).zip(step_buffer.iter()) {
*dest = (src.max(-1.0).min(1.0) * 32767.0) as i16;
}
buffer = &mut buffer[step_size..];
}
},
move |err| {
dbg!(err);
},
None,
)?
};
Ok(Uw8Sound { stream, tx }) Ok(Uw8Sound { stream, tx })
} }

View File

@@ -2,17 +2,18 @@ import "env.memory" memory(4);
import "env.pow" fn pow(f32, f32) -> f32; import "env.pow" fn pow(f32, f32) -> f32;
import "env.sin" fn sin(f32) -> f32; import "env.sin" fn sin(f32) -> f32;
import "env.cls" fn cls(i32); import "env.cls" fn cls(i32);
import "env.exp" fn exp(f32) -> f32;
import "env.rectangle" fn rectangle(f32, f32, f32, f32, i32); import "env.rectangle" fn rectangle(f32, f32, f32, f32, i32);
include "../platform/src/ges.cwa" include "../platform/src/ges.cwa"
export fn snd(t: i32) -> f32 { export fn snd(t: i32) -> f32 {
gesSnd(t) sndGes(t)
} }
export fn upd() { export fn upd() {
80?0 = 32!32 / 200 & 2 | 0x41; 80?0 = 32!32 / 200 & 2 | 0x41;
80?3 = (32!32 / 400)%7*12/7 + 40; 80?3 = (32!32 / 400)%8*12/7 + 40;
let pulse = (32!32 * 256 / 2000) & 511; let pulse = (32!32 * 256 / 2000) & 511;
if pulse >= 256 { if pulse >= 256 {
pulse = 511 - pulse; pulse = 511 - pulse;

1
todo.txt Normal file
View File

@@ -0,0 +1 @@
* add support for 16bit sound (not just float)

View File

@@ -1,13 +1,17 @@
use std::path::Path;
use anyhow::Result; use anyhow::Result;
use std::path::Path;
pub fn filter_exports(in_path: &Path, out_path: &Path) -> Result<()> { pub fn filter_exports(in_path: &Path, out_path: &Path) -> Result<()> {
let mut module = walrus::Module::from_file(in_path)?; let mut module = walrus::Module::from_file(in_path)?;
let exports_to_delete: Vec<_> = module.exports.iter().filter_map(|export| match export.name.as_str() { let exports_to_delete: Vec<_> = module
"upd" => None, .exports
_ => Some(export.id()) .iter()
}).collect(); .filter_map(|export| match export.name.as_str() {
"start" | "upd" | "snd" => None,
_ => Some(export.id()),
})
.collect();
for id in exports_to_delete { for id in exports_to_delete {
module.exports.delete(id); module.exports.delete(id);

View File

@@ -767,7 +767,10 @@ fn remap_function(
De::End => En::End, De::End => En::End,
De::Br { relative_depth } => En::Br(relative_depth), De::Br { relative_depth } => En::Br(relative_depth),
De::BrIf { relative_depth } => En::BrIf(relative_depth), De::BrIf { relative_depth } => En::BrIf(relative_depth),
De::BrTable { .. } => todo!(), De::BrTable { targets } => En::BrTable(
targets.targets().collect::<Result<Vec<u32>, _>>()?.into(),
targets.default(),
),
De::Return => En::Return, De::Return => En::Return,
De::Call { function_index } => En::Call( De::Call { function_index } => En::Call(
*function_map *function_map

810
uw8-window/Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -6,13 +6,13 @@ edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies] [dependencies]
winit = "0.27.5" winit = "0.28.6"
env_logger = "0.10" env_logger = "0.10"
log = "0.4" log = "0.4"
pico-args = "0.5" pico-args = "0.5"
wgpu = "0.15" wgpu = "0.17"
pollster = "0.2.5" pollster = "0.3.0"
bytemuck = { version = "1.13", features = [ "derive" ] } bytemuck = { version = "1.13", features = [ "derive" ] }
anyhow = "1" anyhow = "1"
minifb = { version = "0.23.0", default-features = false, features = ["x11"] } minifb = { version = "0.25.0", default-features = false, features = ["x11"] }
winapi = { version = "0.3.9", features = [ "timeapi" ] } winapi = { version = "0.3.9", features = [ "timeapi" ] }

View File

@@ -30,8 +30,8 @@ fn row_factor(offset: f32) -> f32 {
} }
fn col_factor(offset: f32) -> f32 { fn col_factor(offset: f32) -> f32 {
let offset = max(0.0, abs(offset) - 0.4); let o = max(0.0, abs(offset) - 0.4);
return 1.0 / (1.0 + offset * offset * 16.0); return 1.0 / (1.0 + o * o * 16.0);
} }
fn sample_screen(tex_coords: vec2<f32>) -> vec4<f32> { fn sample_screen(tex_coords: vec2<f32>) -> vec4<f32> {

View File

@@ -1,6 +1,6 @@
use crate::{Input, WindowConfig, WindowImpl}; use crate::{Input, WindowConfig, WindowImpl};
use anyhow::{anyhow, Result}; use anyhow::{anyhow, Result};
use std::{num::NonZeroU32, time::Instant}; use std::time::Instant;
use winit::{ use winit::{
dpi::PhysicalSize, dpi::PhysicalSize,
@@ -41,7 +41,10 @@ impl Window {
async fn create(window_config: WindowConfig) -> Result<Window> { async fn create(window_config: WindowConfig) -> Result<Window> {
let event_loop = EventLoop::new(); let event_loop = EventLoop::new();
let window = WindowBuilder::new() let window = WindowBuilder::new()
.with_inner_size(PhysicalSize::new(640u32, 480)) .with_inner_size(PhysicalSize::new(
(320. * window_config.scale).round() as u32,
(240. * window_config.scale).round() as u32,
))
.with_min_inner_size(PhysicalSize::new(320u32, 240)) .with_min_inner_size(PhysicalSize::new(320u32, 240))
.with_title("MicroW8") .with_title("MicroW8")
.with_fullscreen(if window_config.fullscreen { .with_fullscreen(if window_config.fullscreen {
@@ -72,7 +75,13 @@ impl Window {
let surface_config = wgpu::SurfaceConfiguration { let surface_config = wgpu::SurfaceConfiguration {
present_mode: wgpu::PresentMode::AutoNoVsync, present_mode: wgpu::PresentMode::AutoNoVsync,
..surface.get_default_config(&adapter, window.inner_size().width, window.inner_size().height).expect("Surface incompatible with adapter") ..surface
.get_default_config(
&adapter,
window.inner_size().width,
window.inner_size().height,
)
.expect("Surface incompatible with adapter")
}; };
let filter: Box<dyn Filter> = create_filter( let filter: Box<dyn Filter> = create_filter(
@@ -354,7 +363,7 @@ impl PaletteScreenMode {
format: wgpu::TextureFormat::R8Uint, format: wgpu::TextureFormat::R8Uint,
usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST, usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST,
label: None, label: None,
view_formats: &[] view_formats: &[],
}); });
let palette_texture = device.create_texture(&wgpu::TextureDescriptor { let palette_texture = device.create_texture(&wgpu::TextureDescriptor {
@@ -369,7 +378,7 @@ impl PaletteScreenMode {
format: wgpu::TextureFormat::Rgba8UnormSrgb, format: wgpu::TextureFormat::Rgba8UnormSrgb,
usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST, usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST,
label: None, label: None,
view_formats: &[] view_formats: &[],
}); });
let screen_texture = device.create_texture(&wgpu::TextureDescriptor { let screen_texture = device.create_texture(&wgpu::TextureDescriptor {
@@ -384,7 +393,7 @@ impl PaletteScreenMode {
format: wgpu::TextureFormat::Rgba8UnormSrgb, format: wgpu::TextureFormat::Rgba8UnormSrgb,
usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::RENDER_ATTACHMENT, usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::RENDER_ATTACHMENT,
label: None, label: None,
view_formats: &[] view_formats: &[],
}); });
let framebuffer_texture_view = let framebuffer_texture_view =
@@ -491,7 +500,7 @@ impl PaletteScreenMode {
&bytemuck::cast_slice(pixels), &bytemuck::cast_slice(pixels),
wgpu::ImageDataLayout { wgpu::ImageDataLayout {
offset: 0, offset: 0,
bytes_per_row: NonZeroU32::new(320), bytes_per_row: Some(320),
rows_per_image: None, rows_per_image: None,
}, },
wgpu::Extent3d { wgpu::Extent3d {
@@ -513,7 +522,7 @@ impl PaletteScreenMode {
&bytemuck::cast_slice(palette), &bytemuck::cast_slice(palette),
wgpu::ImageDataLayout { wgpu::ImageDataLayout {
offset: 0, offset: 0,
bytes_per_row: NonZeroU32::new(256 * 4), bytes_per_row: Some(256 * 4),
rows_per_image: None, rows_per_image: None,
}, },
wgpu::Extent3d { wgpu::Extent3d {

View File

@@ -15,7 +15,7 @@ struct FpsCounter {
} }
impl Window { impl Window {
pub fn new(config: WindowConfig) -> Result<Window> { pub fn new(mut config: WindowConfig) -> Result<Window> {
let fps_counter = if config.fps_counter { let fps_counter = if config.fps_counter {
Some(FpsCounter { Some(FpsCounter {
start: Instant::now(), start: Instant::now(),
@@ -24,6 +24,7 @@ impl Window {
} else { } else {
None None
}; };
config.scale = config.scale.max(1.).min(20.);
if config.enable_gpu { if config.enable_gpu {
match gpu::Window::new(config) { match gpu::Window::new(config) {
Ok(window) => { Ok(window) => {
@@ -71,6 +72,7 @@ pub struct WindowConfig {
filter: u32, filter: u32,
fullscreen: bool, fullscreen: bool,
fps_counter: bool, fps_counter: bool,
scale: f32,
} }
impl Default for WindowConfig { impl Default for WindowConfig {
@@ -80,6 +82,7 @@ impl Default for WindowConfig {
filter: 5, filter: 5,
fullscreen: false, fullscreen: false,
fps_counter: false, fps_counter: false,
scale: 2.,
} }
} }
} }
@@ -102,6 +105,10 @@ impl WindowConfig {
} }
self.fullscreen = args.contains("--fullscreen"); self.fullscreen = args.contains("--fullscreen");
self.fps_counter = args.contains("--fps"); self.fps_counter = args.contains("--fps");
self.scale = args
.opt_value_from_str("--scale")
.unwrap()
.unwrap_or(self.scale);
} }
} }