mirror of
https://github.com/exoticorn/microw8.git
synced 2026-11-14 07:17:19 +01:00
Compare commits
8
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d478d3ad49 | ||
|
|
502852e59a | ||
|
|
5efa8b3465 | ||
|
|
daf2a02cd8 | ||
|
|
8d5374a867 | ||
|
|
142b6a4c15 | ||
|
|
877fceb089 | ||
|
|
f0ba0f2b99 |
No files matched your search
@@ -2,7 +2,7 @@ name: Rust
|
|||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches: [ master, testing ]
|
branches: [ master ]
|
||||||
|
|
||||||
env:
|
env:
|
||||||
CARGO_TERM_COLOR: always
|
CARGO_TERM_COLOR: always
|
||||||
|
|||||||
Generated
+1442
-1086
File diff suppressed because it is too large.
Load diff
+10
-10
@@ -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 = "0.37.0", optional = true }
|
wasmtime = { version = "5.0.0", optional = true }
|
||||||
anyhow = "1"
|
anyhow = "1"
|
||||||
env_logger = "0.9"
|
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 = "4"
|
notify = "5"
|
||||||
pico-args = "0.4"
|
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.2", optional = true }
|
warp = { version = "0.3.3", optional = true }
|
||||||
tokio = { version = "1.17.0", features = ["sync", "rt"], optional = true }
|
tokio = { version = "1.24.0", features = ["sync", "rt"], optional = true }
|
||||||
tokio-stream = { version = "0.1.8", features = ["sync"], optional = true }
|
tokio-stream = { version = "0.1.11", features = ["sync"], optional = true }
|
||||||
webbrowser = { version = "0.6.0", optional = true }
|
webbrowser = { version = "0.8.6", optional = true }
|
||||||
ansi_term = "0.12.1"
|
ansi_term = "0.12.1"
|
||||||
cpal = { version = "0.13.5", optional = true }
|
cpal = { version = "0.14.2", optional = true }
|
||||||
rubato = { version = "0.11.0", optional = true }
|
rubato = { version = "0.12.0", optional = true }
|
||||||
@@ -34,6 +34,7 @@ import "env.rectangle_outline" fn rectangle_outline(f32, f32, f32, f32, i32);
|
|||||||
import "env.circle_outline" fn circle_outline(f32, f32, f32, i32);
|
import "env.circle_outline" fn circle_outline(f32, f32, f32, i32);
|
||||||
import "env.exp" fn exp(f32) -> f32;
|
import "env.exp" fn exp(f32) -> f32;
|
||||||
import "env.playNote" fn playNote(i32, i32);
|
import "env.playNote" fn playNote(i32, i32);
|
||||||
|
import "env.sndGes" fn sndGes(i32) -> f32;
|
||||||
|
|
||||||
const TIME_MS = 0x40;
|
const TIME_MS = 0x40;
|
||||||
const GAMEPAD = 0x44;
|
const GAMEPAD = 0x44;
|
||||||
|
|||||||
@@ -34,6 +34,7 @@
|
|||||||
(import "env" "circle_outline" (func $circle_outline (param f32) (param f32) (param f32) (param i32)))
|
(import "env" "circle_outline" (func $circle_outline (param f32) (param f32) (param f32) (param i32)))
|
||||||
(import "env" "exp" (func $exp (param f32) (result f32)))
|
(import "env" "exp" (func $exp (param f32) (result f32)))
|
||||||
(import "env" "playNote" (func $playNote (param i32) (param i32)))
|
(import "env" "playNote" (func $playNote (param i32) (param i32)))
|
||||||
|
(import "env" "sndGes" (func $sndGes (param i32) (result f32)))
|
||||||
|
|
||||||
;; to use defines, include this file with a preprocessor
|
;; to use defines, include this file with a preprocessor
|
||||||
;; like gpp (https://logological.org/gpp).
|
;; like gpp (https://logological.org/gpp).
|
||||||
|
|||||||
Generated
+1
-1
@@ -391,7 +391,7 @@ checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "upkr"
|
name = "upkr"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/exoticorn/upkr.git?rev=2e7983fc#2e7983fc650788d98da2eecef2d16f63e849e4a0"
|
source = "git+https://github.com/exoticorn/upkr.git?rev=d93aec186c9fb91d962c488682a2db125c61306c#d93aec186c9fb91d962c488682a2db125c61306c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"cdivsufsort",
|
"cdivsufsort",
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
@@ -10,7 +10,7 @@ const GesState.Size = GesState.Filter + 8*4;
|
|||||||
const GesStateOffset = 32;
|
const GesStateOffset = 32;
|
||||||
const GesBufferOffset = 32 + GesState.Size;
|
const GesBufferOffset = 32 + GesState.Size;
|
||||||
|
|
||||||
export fn gesSnd(t: i32) -> f32 {
|
export fn sndGes(t: i32) -> f32 {
|
||||||
let baseAddr = 0!0x12c78;
|
let baseAddr = 0!0x12c78;
|
||||||
if !(t & 127) {
|
if !(t & 127) {
|
||||||
let i: i32;
|
let i: i32;
|
||||||
|
|||||||
@@ -29,6 +29,19 @@ Examplers for older versions:
|
|||||||
|
|
||||||
## Versions
|
## Versions
|
||||||
|
|
||||||
|
### v0.2.1
|
||||||
|
|
||||||
|
* [Web runtime](v0.2.1)
|
||||||
|
* [Linux](https://github.com/exoticorn/microw8/releases/download/v0.2.1/microw8-0.2.1-linux.tgz)
|
||||||
|
* [MacOS](https://github.com/exoticorn/microw8/releases/download/v0.2.1/microw8-0.2.1-macos.tgz)
|
||||||
|
* [Windows](https://github.com/exoticorn/microw8/releases/download/v0.2.1/microw8-0.2.1-windows.zip)
|
||||||
|
|
||||||
|
Changes:
|
||||||
|
|
||||||
|
* new gpu accelerated renderer with (optional) crt filter
|
||||||
|
* optimized `hline` function, a big speed-up when drawing large filled circles or rectangles
|
||||||
|
* print fractional size of packed `uw8` cart
|
||||||
|
|
||||||
### v0.2.0
|
### v0.2.0
|
||||||
|
|
||||||
* [Web runtime](v0.2.0)
|
* [Web runtime](v0.2.0)
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
<!doctype html><html><head><meta charset="utf8"><title>MicroW8</title><link href="https://fonts.googleapis.com/css2?family=Josefin+Sans:wght@500&display=swap" rel="stylesheet"><style>html,body,canvas{margin:0;padding:0}html{color:#808070;height:100%;background-color:#202024;font-family:Josefin Sans,Verdana,Arial Black,sans-serif}body{height:100%;grid-template-rows:1fr 0fr;display:grid}#uw8{position:absolute}#uw8 a{font-size:130%}#centered{place-self:center}a{color:#303630}a:hover{color:#405040}.screen{width:320px;height:240px;image-rendering:pixelated;background-color:#202428;border:4px solid #303040;margin-bottom:8px;box-shadow:5px 5px 20px #000}#screen{cursor:none}#start{font-size:150%}#timer:before{content:"";width:12px;height:12px;background-color:red;border-radius:6px;margin-right:3px;display:inline-block}#message{margin-bottom:8px}.error{color:#e04030}button{color:#808070;background-color:#303440;border:1px solid #404040;border-radius:4px;padding:4px;font-family:inherit}button:hover{background-color:#202010}button:active{background-color:#504450}@media (min-width:680px) and (min-height:620px){.screen{width:640px;height:480px}}@media (min-width:1000px) and (min-height:800px){.screen{width:960px;height:720px}}@media (width:640px) and (height:480px){.screen{width:640px;height:480px;border:0;margin:0}body{overflow:hidden}}</style></head><body> <div id="uw8"> <a href="https://exoticorn.github.io/microw8">MicroW8</a> 0.2.1 </div> <div id="centered"> <canvas class="screen" id="screen" width="320" height="240"> </canvas> <button class="screen" id="start" style="display:none">Click to start</button> <div id="timer" hidden></div> <div id="message"></div> <button id="cartButton" style="visibility:hidden">Load cart...</button> </div> <div id="footer"> <a href="http://unlicense.org/" ref="license">Unlicense</a> </div> </body><script type="module">function e(e){return e&&e.__esModule?e.default:e}class t extends AudioWorkletNode{constructor(e){super(e,"apu",{outputChannelCount:[2]})}}let a=(...e)=>new Uint8Array(...e),n=(...e)=>new Uint32Array(...e);let s=function(s,A={}){A.setMessage||(A.setMessage=(e,t)=>{t&&console.log("error: "+t)});let r,B,o,i,l,d,F,u,g=s.getContext("2d"),c=g.createImageData(320,240),D=A.devkitMode,C=0,f=null==A.keyboardElement?s:A.keyboardElement;if(f){let e=e=>{let t,a="keydown"==e.type;switch(e.code){case"ArrowUp":t=1;break;case"ArrowDown":t=2;break;case"ArrowLeft":t=4;break;case"ArrowRight":t=8;break;case"KeyZ":t=16;break;case"KeyX":t=32;break;case"KeyA":t=64;break;case"KeyS":t=128;break;case"KeyR":a&&w(B,!0);break;case"F9":a&&s.toBlob((e=>{h(e,".png")})),e.preventDefault();break;case"F10":a&&function(){if(l)return l.stop(),l=null,F.disconnect(u),F=null,void(u=null);let e=s.captureStream();u=o.createMediaStreamDestination(),F=i,i.connect(u),e.addTrack(u.stream.getAudioTracks()[0]),l=new MediaRecorder(e,{mimeType:"video/webm",videoBitsPerSecond:25e6});let t=[];l.ondataavailable=e=>{t.push(e.data)};let a=A.timerElement;a&&(a.hidden=!1,a.innerText="00:00");function n(){if(d){if(a){let e=Math.floor((Date.now()-d)/1e3);a.innerText=Math.floor(e/60).toString().padStart(2,"0")+":"+(e%60).toString().padStart(2,"0")}setTimeout(n,1e3)}}l.onstop=()=>{a&&(a.hidden=!0),h(new Blob(t,{type:"video/webm"}),".webm")},l.start(),d=Date.now(),setTimeout(n,1e3)}(),e.preventDefault()}a?C|=t:C&=~t};f.onkeydown=e,f.onkeyup=e}async function w(l,d){r&&(r(),r=null),o=new AudioContext({sampleRate:44100});let F=!0,u=new AbortController;r=()=>{o.close(),F=!1,u.abort()};let f=l.byteLength;if(A.setMessage(f),0==f)return;await o.audioWorklet.addModule(e("data:application/javascript,let%20e%3D%28...e%29%3D%3Enew%20Uint8Array%28...e%29%3Bclass%20s%20extends%20AudioWorkletProcessor%7Bconstructor%28%29%7Bsuper%28%29%2Cthis.sampleIndex%3D0%2Cthis.currentTime%3D0%2Cthis.isFirstMessage%3D%210%2Cthis.pendingUpdates%3D%5B%5D%2Cthis.port.onmessage%3De%3D%3E%7Bthis.memory%3F%28this.isFirstMessage%26%26%28this.currentTime%2B%3D%28e.data.t-this.currentTime%29%2F8%2Cthis.isFirstMessage%3D%211%29%2Cthis.pendingUpdates.push%28e.data%29%29%3Athis.load%28e.data%5B0%5D%2Ce.data%5B1%5D%29%7D%7Dasync%20load%28e%2Cs%29%7Blet%20t%3Dnew%20WebAssembly.Memory%28%7Binitial%3A4%2Cmaximum%3A4%7D%29%2Ci%3D%7Benv%3A%7Bmemory%3At%7D%7D%3Bfor%28let%20e%20of%5B%22acos%22%2C%22asin%22%2C%22atan%22%2C%22atan2%22%2C%22cos%22%2C%22exp%22%2C%22log%22%2C%22sin%22%2C%22tan%22%2C%22pow%22%5D%29i.env%5Be%5D%3DMath%5Be%5D%3Bfor%28let%20e%3D9%3Be%3C64%3B%2B%2Be%29i.env%5B%22reserved%22%2Be%5D%3D%28%29%3D%3E%7B%7D%3Blet%20r%3D%22%22%3Bi.env.logChar%3De%3D%3E%7B10%3D%3De%3F%28console.log%28r%29%2Cr%3D%22%22%29%3Ar%2B%3DString.fromCharCode%28e%29%7D%3Bfor%28let%20e%3D0%3Be%3C16%3B%2B%2Be%29i.env%5B%22g_reserved%22%2Be%5D%3D0%3Blet%20n%3Dasync%20e%3D%3E%28await%20WebAssembly.instantiate%28e%2Ci%29%29.instance%2Ca%3Dawait%20n%28e%29%3Bfor%28let%20e%20in%20a.exports%29i.env%5Be%5D%3Da.exports%5Be%5D%3Blet%20o%3Dawait%20n%28s%29%3Bthis.memory%3Dt%2Cthis.snd%3Do.exports.snd%7Line truncated
|
||||||
@@ -4,7 +4,7 @@
|
|||||||
<section>
|
<section>
|
||||||
<h1 class="text-center heading-text">A WebAssembly based fantasy console</h1>
|
<h1 class="text-center heading-text">A WebAssembly based fantasy console</h1>
|
||||||
</section>
|
</section>
|
||||||
<a href="v0.2.0">
|
<a href="v0.2.1">
|
||||||
<img class="demonstration-gif" style="width:640px;height:480px;image-rendering:pixelated" src="img/technotunnel.png"></img>
|
<img class="demonstration-gif" style="width:640px;height:480px;image-rendering:pixelated" src="img/technotunnel.png"></img>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
+14
-7
@@ -1,18 +1,21 @@
|
|||||||
use anyhow::{anyhow, bail, Result};
|
use anyhow::{anyhow, bail, Result};
|
||||||
use notify::{DebouncedEvent, RecommendedWatcher, Watcher};
|
use notify::{Event, EventKind, RecommendedWatcher, Watcher};
|
||||||
use std::{collections::BTreeSet, path::PathBuf, sync::mpsc, time::Duration};
|
use std::{collections::BTreeSet, path::PathBuf, sync::mpsc};
|
||||||
|
|
||||||
pub struct FileWatcher {
|
pub struct FileWatcher {
|
||||||
watcher: RecommendedWatcher,
|
watcher: RecommendedWatcher,
|
||||||
watched_files: BTreeSet<PathBuf>,
|
watched_files: BTreeSet<PathBuf>,
|
||||||
directories: BTreeSet<PathBuf>,
|
directories: BTreeSet<PathBuf>,
|
||||||
rx: mpsc::Receiver<DebouncedEvent>,
|
rx: mpsc::Receiver<Event>,
|
||||||
}
|
}
|
||||||
|
|
||||||
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::watcher(tx, Duration::from_millis(100))?;
|
let watcher = notify::recommended_watcher(move |res| match res {
|
||||||
|
Ok(event) => _ = tx.send(event),
|
||||||
|
Err(err) => eprintln!("Error watching for file changes: {err}"),
|
||||||
|
})?;
|
||||||
Ok(FileWatcher {
|
Ok(FileWatcher {
|
||||||
watcher,
|
watcher,
|
||||||
watched_files: BTreeSet::new(),
|
watched_files: BTreeSet::new(),
|
||||||
@@ -36,9 +39,10 @@ impl FileWatcher {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn poll_changed_file(&self) -> Result<Option<PathBuf>> {
|
pub fn poll_changed_file(&self) -> Result<Option<PathBuf>> {
|
||||||
let event = self.rx.try_recv();
|
match self.rx.try_recv() {
|
||||||
match event {
|
Ok(event) => match event.kind {
|
||||||
Ok(DebouncedEvent::Create(path) | DebouncedEvent::Write(path)) => {
|
EventKind::Create(_) | EventKind::Modify(_) => {
|
||||||
|
for path in event.paths {
|
||||||
let handle = same_file::Handle::from_path(&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)? {
|
||||||
@@ -46,6 +50,9 @@ impl FileWatcher {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
},
|
||||||
Err(mpsc::TryRecvError::Disconnected) => bail!("File watcher disconnected"),
|
Err(mpsc::TryRecvError::Disconnected) => bail!("File watcher disconnected"),
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-3
@@ -81,11 +81,12 @@ fn run(mut args: Arguments) -> Result<()> {
|
|||||||
#[cfg(not(feature = "native"))]
|
#[cfg(not(feature = "native"))]
|
||||||
let run_browser = args.contains(["-b", "--browser"]) || true;
|
let run_browser = args.contains(["-b", "--browser"]) || true;
|
||||||
|
|
||||||
|
#[allow(unused)]
|
||||||
let disable_audio = args.contains(["-m", "--no-audio"]);
|
let disable_audio = args.contains(["-m", "--no-audio"]);
|
||||||
|
|
||||||
#[cfg(feature = "native")]
|
#[cfg(feature = "native")]
|
||||||
let window_config = {
|
let window_config = {
|
||||||
let mut config = WindowConfig::default();
|
let mut config = uw8_window::WindowConfig::default();
|
||||||
if !run_browser {
|
if !run_browser {
|
||||||
config.parse_arguments(&mut args);
|
config.parse_arguments(&mut args);
|
||||||
}
|
}
|
||||||
@@ -98,8 +99,6 @@ fn run(mut args: Arguments) -> Result<()> {
|
|||||||
|
|
||||||
use std::process::exit;
|
use std::process::exit;
|
||||||
|
|
||||||
use uw8_window::WindowConfig;
|
|
||||||
|
|
||||||
let mut runtime: Box<dyn Runtime> = if !run_browser {
|
let mut runtime: Box<dyn Runtime> = if !run_browser {
|
||||||
#[cfg(not(feature = "native"))]
|
#[cfg(not(feature = "native"))]
|
||||||
unimplemented!();
|
unimplemented!();
|
||||||
|
|||||||
+1
-1
@@ -1 +1 @@
|
|||||||
<!doctype html><html><head><meta charset="utf8"><title>uw8-run</title><style>html,body,canvas{background-color:#202024;margin:0;padding:0}html{height:100%}body{height:100%;grid-template-rows:1fr;display:grid}#screen{image-rendering:pixelated;border:4px solid #303040;place-self:center}#message{width:calc(100% - 16px);color:#c64;z-index:2;background-color:#0006;padding:8px;font:700 12pt sans-serif;position:absolute}@media (min-width:648px) and (min-height:488px){#screen{width:640px;height:480px}}@media (min-width:968px) and (min-height:728px){#screen{width:960px;height:720px}}</style></head><body> <canvas id="screen" width="320" height="240" tabindex="1"> </canvas> <div id="message"></div> </body><script type="module">function e(e){return e&&e.__esModule?e.default:e}class t extends AudioWorkletNode{constructor(e){super(e,"apu",{outputChannelCount:[2]})}}let A=(...e)=>new Uint8Array(...e),a=(...e)=>new Uint32Array(...e);let s=function(s,B={}){B.setMessage||(B.setMessage=(e,t)=>{t&&console.log("error: "+t)});let n,r,o,i,l,F,d,g,C=s.getContext("2d"),u=C.createImageData(320,240),D=B.devkitMode,c=0,f=null==B.keyboardElement?s:B.keyboardElement;if(f){let e=e=>{let t,A="keydown"==e.type;switch(e.code){case"ArrowUp":t=1;break;case"ArrowDown":t=2;break;case"ArrowLeft":t=4;break;case"ArrowRight":t=8;break;case"KeyZ":t=16;break;case"KeyX":t=32;break;case"KeyA":t=64;break;case"KeyS":t=128;break;case"KeyR":A&&w(r,!0);break;case"F9":A&&s.toBlob((e=>{I(e,".png")})),e.preventDefault();break;case"F10":A&&function(){if(l)return l.stop(),l=null,d.disconnect(g),d=null,void(g=null);let e=s.captureStream();g=o.createMediaStreamDestination(),d=i,i.connect(g),e.addTrack(g.stream.getAudioTracks()[0]),l=new MediaRecorder(e,{mimeType:"video/webm",videoBitsPerSecond:25e6});let t=[];l.ondataavailable=e=>{t.push(e.data)};let A=B.timerElement;A&&(A.hidden=!1,A.innerText="00:00");function a(){if(F){if(A){let e=Math.floor((Date.now()-F)/1e3);A.innerText=Math.floor(e/60).toString().padStart(2,"0")+":"+(e%60).toString().padStart(2,"0")}setTimeout(a,1e3)}}l.onstop=()=>{A&&(A.hidden=!0),I(new Blob(t,{type:"video/webm"}),".webm")},l.start(),F=Date.now(),setTimeout(a,1e3)}(),e.preventDefault()}A?c|=t:c&=~t};f.onkeydown=e,f.onkeyup=e}async function w(l,F){n&&(n(),n=null),o=new AudioContext({sampleRate:44100});let d=!0,g=new AbortController;n=()=>{o.close(),d=!1,g.abort()};let f=l.byteLength;if(B.setMessage(f),0==f)return;await o.audioWorklet.addModule(e("data:application/javascript,let%20e%3D%28...e%29%3D%3Enew%20Uint8Array%28...e%29%3Bclass%20s%20extends%20AudioWorkletProcessor%7Bconstructor%28%29%7Bsuper%28%29%2Cthis.sampleIndex%3D0%2Cthis.currentTime%3D0%2Cthis.isFirstMessage%3D%210%2Cthis.pendingUpdates%3D%5B%5D%2Cthis.port.onmessage%3De%3D%3E%7Bthis.memory%3F%28this.isFirstMessage%26%26%28this.currentTime%2B%3D%28e.data.t-this.currentTime%29%2F8%2Cthis.isFirstMessage%3D%211%29%2Cthis.pendingUpdates.push%28e.data%29%29%3Athis.load%28e.data%5B0%5D%2Ce.data%5B1%5D%29%7D%7Dasync%20load%28e%2Cs%29%7Blet%20t%3Dnew%20WebAssembly.Memory%28%7Binitial%3A4%2Cmaximum%3A4%7D%29%2Ci%3D%7Benv%3A%7Bmemory%3At%7D%7D%3Bfor%28let%20e%20of%5B%22acos%22%2C%22asin%22%2C%22atan%22%2C%22atan2%22%2C%22cos%22%2C%22exp%22%2C%22log%22%2C%22sin%22%2C%22tan%22%2C%22pow%22%5D%29i.env%5Be%5D%3DMath%5Be%5D%3Bfor%28let%20e%3D9%3Be%3C64%3B%2B%2Be%29i.env%5B%22reserved%22%2Be%5D%3D%28%29%3D%3E%7B%7D%3Blet%20r%3D%22%22%3Bi.env.logChar%3De%3D%3E%7B10%3D%3De%3F%28console.log%28r%29%2Cr%3D%22%22%29%3Ar%2B%3DString.fromCharCode%28e%29%7D%3Bfor%28let%20e%3D0%3Be%3C16%3B%2B%2Be%29i.env%5B%22g_reserved%22%2Be%5D%3D0%3Blet%20n%3Dasync%20e%3D%3E%28await%20WebAssembly.instantiate%28e%2Ci%29%29.instance%2Ca%3Dawait%20n%28e%29%3Bfor%28let%20e%20in%20a.exports%29i.env%5Be%5D%3Da.exports%5Be%5D%3Blet%20o%3Dawait%20n%28s%29%3Bthis.memory%3Dt%2Cthis.snd%3Do.exports.snd%7C%7Ca.exports.gesSnd%2Cthis.port.postMessage%282%29%7Dprocess%28s%2Ct%2Ci%29%7Bif%28this.isFirstMessage%3D%210%2Cthis.snd%29%7Bfor%28%3Bthis.pendingUpdates.length%3E0%26%26this.pendingUpdates%5B0%5D.t%3C%3Dthis.currentTime%3B%29e%28this.memory.buffer%2C80%2C32%29.set%28e%28this.pendingUpdates.shift%28%29.r%29%29%3Bnew%20Uint32Array%28this.memory.buffer%29%5B16%5D%3Dthis.currentTime%3Blet%20s%3Dt%5B0%5D%2Ci%3Dthis.sampleIndex%2Cr%3Ds%5B0%5D.length%3Bfor%28let%20e%3D0%3Be%3Cr%3B%2B%2Be%29s%5B0%5D%5Be%5D%3Dthis.snd%28i%2B%2B%29%2Cs%5B1%5D%5Be%5D%3Dthis.snd%28i%2B%2B%29%3Bthis.sampleIndex%3D4294967295%26i%2Cthis.currentTime%2B%3Dr%2F44.1%7Dreturn%210%7D%7DregisterProcessor%28%22apu%22%2Cs%29%3B")),i=new t(o);let I,h=0,Q=new Promise((e=>I=e)),m=e=>{h|=e,3==h&&I&&(I(!0),I=null)},M=()=>{"suspended"==o.state?(B.startButton&&(B.startButton.style="",s.style="display:none"),(B.startButton||s).onclick=()=>{o.resume()}):(B.startButton&&(B.startButton.style="display:none",s.style=""),m(1))};o.onstatechange=M,M(),r=l;let E=window.location.pathname;if(f<=1024&&!F){let e="";for(let t of A(l))e+=String.fromCharCode(t);E+="#"+btoa(e),E!=window.locLine truncated
|
<!doctype html><html><head><meta charset="utf8"><title>uw8-run</title><style>html,body,canvas{background-color:#202024;margin:0;padding:0}html{height:100%}body{height:100%;grid-template-rows:1fr;display:grid}#screen{image-rendering:pixelated;border:4px solid #303040;place-self:center}#message{width:calc(100% - 16px);color:#c64;z-index:2;background-color:#0006;padding:8px;font:700 12pt sans-serif;position:absolute}@media (min-width:648px) and (min-height:488px){#screen{width:640px;height:480px}}@media (min-width:968px) and (min-height:728px){#screen{width:960px;height:720px}}</style></head><body> <canvas id="screen" width="320" height="240" tabindex="1"> </canvas> <div id="message"></div> </body><script type="module">function e(e){return e&&e.__esModule?e.default:e}class t extends AudioWorkletNode{constructor(e){super(e,"apu",{outputChannelCount:[2]})}}let s=(...e)=>new Uint8Array(...e),A=(...e)=>new Uint32Array(...e);let a=function(a,n={}){n.setMessage||(n.setMessage=(e,t)=>{t&&console.log("error: "+t)});let B,o,r,i,l,F,d,C,D=a.getContext("2d"),g=D.createImageData(320,240),u=n.devkitMode,c=0,w=null==n.keyboardElement?a:n.keyboardElement;if(w){let e=e=>{let t,s="keydown"==e.type;switch(e.code){case"ArrowUp":t=1;break;case"ArrowDown":t=2;break;case"ArrowLeft":t=4;break;case"ArrowRight":t=8;break;case"KeyZ":t=16;break;case"KeyX":t=32;break;case"KeyA":t=64;break;case"KeyS":t=128;break;case"KeyR":s&&h(o,!0);break;case"F9":s&&a.toBlob((e=>{f(e,".png")})),e.preventDefault();break;case"F10":s&&function(){if(l)return l.stop(),l=null,d.disconnect(C),d=null,void(C=null);let e=a.captureStream();C=r.createMediaStreamDestination(),d=i,i.connect(C),e.addTrack(C.stream.getAudioTracks()[0]),l=new MediaRecorder(e,{mimeType:"video/webm",videoBitsPerSecond:25e6});let t=[];l.ondataavailable=e=>{t.push(e.data)};let s=n.timerElement;s&&(s.hidden=!1,s.innerText="00:00");function A(){if(F){if(s){let e=Math.floor((Date.now()-F)/1e3);s.innerText=Math.floor(e/60).toString().padStart(2,"0")+":"+(e%60).toString().padStart(2,"0")}setTimeout(A,1e3)}}l.onstop=()=>{s&&(s.hidden=!0),f(new Blob(t,{type:"video/webm"}),".webm")},l.start(),F=Date.now(),setTimeout(A,1e3)}(),e.preventDefault()}s?c|=t:c&=~t};w.onkeydown=e,w.onkeyup=e}async function h(l,F){B&&(B(),B=null),r=new AudioContext({sampleRate:44100});let d=!0,C=new AbortController;B=()=>{r.close(),d=!1,C.abort()};let w=l.byteLength;if(n.setMessage(w),0==w)return;await r.audioWorklet.addModule(e("data:application/javascript,let%20e%3D%28...e%29%3D%3Enew%20Uint8Array%28...e%29%3Bclass%20s%20extends%20AudioWorkletProcessor%7Bconstructor%28%29%7Bsuper%28%29%2Cthis.sampleIndex%3D0%2Cthis.currentTime%3D0%2Cthis.isFirstMessage%3D%210%2Cthis.pendingUpdates%3D%5B%5D%2Cthis.port.onmessage%3De%3D%3E%7Bthis.memory%3F%28this.isFirstMessage%26%26%28this.currentTime%2B%3D%28e.data.t-this.currentTime%29%2F8%2Cthis.isFirstMessage%3D%211%29%2Cthis.pendingUpdates.push%28e.data%29%29%3Athis.load%28e.data%5B0%5D%2Ce.data%5B1%5D%29%7D%7Dasync%20load%28e%2Cs%29%7Blet%20t%3Dnew%20WebAssembly.Memory%28%7Binitial%3A4%2Cmaximum%3A4%7D%29%2Ci%3D%7Benv%3A%7Bmemory%3At%7D%7D%3Bfor%28let%20e%20of%5B%22acos%22%2C%22asin%22%2C%22atan%22%2C%22atan2%22%2C%22cos%22%2C%22exp%22%2C%22log%22%2C%22sin%22%2C%22tan%22%2C%22pow%22%5D%29i.env%5Be%5D%3DMath%5Be%5D%3Bfor%28let%20e%3D9%3Be%3C64%3B%2B%2Be%29i.env%5B%22reserved%22%2Be%5D%3D%28%29%3D%3E%7B%7D%3Blet%20r%3D%22%22%3Bi.env.logChar%3De%3D%3E%7B10%3D%3De%3F%28console.log%28r%29%2Cr%3D%22%22%29%3Ar%2B%3DString.fromCharCode%28e%29%7D%3Bfor%28let%20e%3D0%3Be%3C16%3B%2B%2Be%29i.env%5B%22g_reserved%22%2Be%5D%3D0%3Blet%20n%3Dasync%20e%3D%3E%28await%20WebAssembly.instantiate%28e%2Ci%29%29.instance%2Ca%3Dawait%20n%28e%29%3Bfor%28let%20e%20in%20a.exports%29i.env%5Be%5D%3Da.exports%5Be%5D%3Blet%20o%3Dawait%20n%28s%29%3Bthis.memory%3Dt%2Cthis.snd%3Do.exports.snd%7C%7Ca.exports.sndGes%2Cthis.port.postMessage%282%29%7Dprocess%28s%2Ct%2Ci%29%7Bif%28this.isFirstMessage%3D%210%2Cthis.snd%29%7Bfor%28%3Bthis.pendingUpdates.length%3E0%26%26this.pendingUpdates%5B0%5D.t%3C%3Dthis.currentTime%3B%29e%28this.memory.buffer%2C80%2C32%29.set%28e%28this.pendingUpdates.shift%28%29.r%29%29%3Bnew%20Uint32Array%28this.memory.buffer%29%5B16%5D%3Dthis.currentTime%3Blet%20s%3Dt%5B0%5D%2Ci%3Dthis.sampleIndex%2Cr%3Ds%5B0%5D.length%3Bfor%28let%20e%3D0%3Be%3Cr%3B%2B%2Be%29s%5B0%5D%5Be%5D%3Dthis.snd%28i%2B%2B%29%2Cs%5B1%5D%5Be%5D%3Dthis.snd%28i%2B%2B%29%3Bthis.sampleIndex%3D4294967295%26i%2Cthis.currentTime%2B%3Dr%2F44.1%7Dreturn%210%7D%7DregisterProcessor%28%22apu%22%2Cs%29%3B")),i=new t(r);let f,y=0,Q=new Promise((e=>f=e)),m=e=>{y|=e,3==y&&f&&(f(!0),f=null)},p=()=>{"suspended"==r.state?(n.startButton&&(n.startButton.style="",a.style="display:none"),(n.startButton||a).onclick=()=>{r.resume()}):(n.startButton&&(n.startButton.style="display:none",a.style=""),m(1))};r.onstatechange=p,p(),o=l;let E=window.location.pathname;if(w<=1024&&!F){let e="";for(let t of s(l))e+=String.fromCharCode(t);E+="#"+btoa(e),E!=window.locLine truncated
|
||||||
+6
-6
@@ -93,7 +93,7 @@ impl super::Runtime for MicroW8 {
|
|||||||
linker.define("env", "memory", memory)?;
|
linker.define("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")?;
|
||||||
|
|
||||||
let platform_data = include_bytes!("../platform/bin/platform.uw8");
|
let platform_data = include_bytes!("../platform/bin/platform.uw8");
|
||||||
memory.data_mut(&mut store)[..platform_data.len()].copy_from_slice(platform_data);
|
memory.data_mut(&mut store)[..platform_data.len()].copy_from_slice(platform_data);
|
||||||
@@ -131,8 +131,8 @@ impl super::Runtime for MicroW8 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let instance = linker.instantiate(&mut store, &module)?;
|
let instance = linker.instantiate(&mut store, &module)?;
|
||||||
let end_frame = platform_instance.get_typed_func::<(), (), _>(&mut store, "endFrame")?;
|
let end_frame = platform_instance.get_typed_func::<(), ()>(&mut store, "endFrame")?;
|
||||||
let update = instance.get_typed_func::<(), (), _>(&mut store, "upd").ok();
|
let update = instance.get_typed_func::<(), ()>(&mut store, "upd").ok();
|
||||||
|
|
||||||
let (sound_tx, stream) = if self.disable_audio {
|
let (sound_tx, stream) = if self.disable_audio {
|
||||||
(None, None)
|
(None, None)
|
||||||
@@ -313,8 +313,8 @@ fn init_sound(
|
|||||||
let instance = linker.instantiate(&mut store, module)?;
|
let instance = linker.instantiate(&mut store, module)?;
|
||||||
|
|
||||||
let snd = instance
|
let snd = instance
|
||||||
.get_typed_func::<(i32,), f32, _>(&mut store, "snd")
|
.get_typed_func::<(i32,), f32>(&mut store, "snd")
|
||||||
.or_else(|_| platform_instance.get_typed_func::<(i32,), f32, _>(&mut store, "gesSnd"))?;
|
.or_else(|_| platform_instance.get_typed_func::<(i32,), f32>(&mut store, "sndGes"))?;
|
||||||
|
|
||||||
let host = cpal::default_host();
|
let host = cpal::default_host();
|
||||||
let device = host
|
let device = host
|
||||||
@@ -342,7 +342,7 @@ fn init_sound(
|
|||||||
.ok_or_else(|| anyhow!("Could not find float output config"))?;
|
.ok_or_else(|| anyhow!("Could not find float output config"))?;
|
||||||
let sample_rate = cpal::SampleRate(44100)
|
let sample_rate = cpal::SampleRate(44100)
|
||||||
.max(config.min_sample_rate())
|
.max(config.min_sample_rate())
|
||||||
.max(config.max_sample_rate());
|
.min(config.max_sample_rate());
|
||||||
let config = config.with_sample_rate(sample_rate);
|
let config = config.with_sample_rate(sample_rate);
|
||||||
let buffer_size = match *config.buffer_size() {
|
let buffer_size = match *config.buffer_size() {
|
||||||
cpal::SupportedBufferSize::Unknown => cpal::BufferSize::Default,
|
cpal::SupportedBufferSize::Unknown => cpal::BufferSize::Default,
|
||||||
|
|||||||
@@ -167,6 +167,7 @@ impl BaseModule {
|
|||||||
add_function(&mut functions, &type_map, "exp", &[F32], Some(F32));
|
add_function(&mut functions, &type_map, "exp", &[F32], Some(F32));
|
||||||
|
|
||||||
add_function(&mut functions, &type_map, "playNote", &[I32, I32], None);
|
add_function(&mut functions, &type_map, "playNote", &[I32, I32], None);
|
||||||
|
add_function(&mut functions, &type_map, "sndGes", &[I32], Some(F32));
|
||||||
|
|
||||||
for i in functions.len()..64 {
|
for i in functions.len()..64 {
|
||||||
add_function(
|
add_function(
|
||||||
|
|||||||
@@ -220,6 +220,11 @@ impl<'a> ParsedModule<'a> {
|
|||||||
validate_table_section(reader)?;
|
validate_table_section(reader)?;
|
||||||
table_section = Some(Section::new(range, ()));
|
table_section = Some(Section::new(range, ()));
|
||||||
}
|
}
|
||||||
|
Payload::MemorySection(reader) => {
|
||||||
|
if reader.get_count() != 0 {
|
||||||
|
bail!("Found non-empty MemorySection. Memory has to be imported!");
|
||||||
|
}
|
||||||
|
}
|
||||||
Payload::ElementSection(mut reader) => {
|
Payload::ElementSection(mut reader) => {
|
||||||
let mut elements = Vec::with_capacity(reader.get_count() as usize);
|
let mut elements = Vec::with_capacity(reader.get_count() as usize);
|
||||||
for _ in 0..reader.get_count() {
|
for _ in 0..reader.get_count() {
|
||||||
|
|||||||
Generated
+656
-188
File diff suppressed because it is too large.
Load diff
@@ -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.26.1"
|
winit = "0.27.5"
|
||||||
env_logger = "0.9"
|
env_logger = "0.10"
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
pico-args = "0.4"
|
pico-args = "0.5"
|
||||||
wgpu = "0.13.1"
|
wgpu = "0.15"
|
||||||
pollster = "0.2"
|
pollster = "0.2.5"
|
||||||
bytemuck = { version = "1.4", 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.23.0", default-features = false, features = ["x11"] }
|
||||||
winapi = "0.3.9"
|
winapi = { version = "0.3.9", features = [ "timeapi" ] }
|
||||||
+14
-14
@@ -36,36 +36,36 @@ fn sample_pixel(coords: vec2<i32>, offset: vec4<f32>) -> vec3<f32> {
|
|||||||
|
|
||||||
@fragment
|
@fragment
|
||||||
fn fs_main(in: VertexOutput) -> @location(0) vec4<f32> {
|
fn fs_main(in: VertexOutput) -> @location(0) vec4<f32> {
|
||||||
let pixel = floor(in.tex_coords);
|
let pixelf = floor(in.tex_coords);
|
||||||
let o = vec2<f32>(0.5) - (in.tex_coords - pixel);
|
let o = vec2<f32>(0.5) - (in.tex_coords - pixelf);
|
||||||
let pixel = vec2<i32>(pixel);
|
let pixel = vec2<i32>(pixelf);
|
||||||
|
|
||||||
let offset_x = o.xxxx + vec4<f32>(-0.125, 0.375, 0.125, -0.375) * uniforms.texture_scale.z;
|
let offset_x = o.xxxx + vec4<f32>(-0.125, 0.375, 0.125, -0.375) * uniforms.texture_scale.z;
|
||||||
let offset_y = o.yyyy + vec4<f32>(-0.375, -0.125, 0.375, 0.125) * uniforms.texture_scale.z;
|
let offset_y = o.yyyy + vec4<f32>(-0.375, -0.125, 0.375, 0.125) * uniforms.texture_scale.z;
|
||||||
|
|
||||||
let offset_x0 = max(abs(offset_x + vec4<f32>(-1.0)) - vec4<f32>(0.5), vec4<f32>(0.0));
|
var offset_x0 = max(abs(offset_x + vec4<f32>(-1.0)) - vec4<f32>(0.5), vec4<f32>(0.0));
|
||||||
let offset_x1 = max(abs(offset_x) - vec4<f32>(0.5), vec4<f32>(0.0));
|
var offset_x1 = max(abs(offset_x) - vec4<f32>(0.5), vec4<f32>(0.0));
|
||||||
let offset_x2 = max(abs(offset_x + vec4<f32>(1.0)) - vec4<f32>(0.5), vec4<f32>(0.0));
|
var offset_x2 = max(abs(offset_x + vec4<f32>(1.0)) - vec4<f32>(0.5), vec4<f32>(0.0));
|
||||||
|
|
||||||
let offset_x0 = offset_x0 * offset_x0;
|
offset_x0 = offset_x0 * offset_x0;
|
||||||
let offset_x1 = offset_x1 * offset_x1;
|
offset_x1 = offset_x1 * offset_x1;
|
||||||
let offset_x2 = offset_x2 * offset_x2;
|
offset_x2 = offset_x2 * offset_x2;
|
||||||
|
|
||||||
let offset_yr = offset_y + vec4<f32>(-1.0);
|
var offset_yr = offset_y + vec4<f32>(-1.0);
|
||||||
let offset_yr = vec4<f32>(0.02) + offset_yr * offset_yr;
|
offset_yr = vec4<f32>(0.02) + offset_yr * offset_yr;
|
||||||
|
|
||||||
var acc = sample_pixel(pixel + vec2<i32>(-1, -1), offset_x0 + offset_yr);
|
var acc = sample_pixel(pixel + vec2<i32>(-1, -1), offset_x0 + offset_yr);
|
||||||
acc = acc + sample_pixel(pixel + vec2<i32>(0, -1), offset_x1 + offset_yr);
|
acc = acc + sample_pixel(pixel + vec2<i32>(0, -1), offset_x1 + offset_yr);
|
||||||
acc = acc + sample_pixel(pixel + vec2<i32>(1, -1), offset_x2 + offset_yr);
|
acc = acc + sample_pixel(pixel + vec2<i32>(1, -1), offset_x2 + offset_yr);
|
||||||
|
|
||||||
let offset_yr = vec4<f32>(0.02) + offset_y * offset_y;
|
offset_yr = vec4<f32>(0.02) + offset_y * offset_y;
|
||||||
|
|
||||||
acc = acc + sample_pixel(pixel + vec2<i32>(-1, 0), offset_x0 + offset_yr);
|
acc = acc + sample_pixel(pixel + vec2<i32>(-1, 0), offset_x0 + offset_yr);
|
||||||
acc = acc + sample_pixel(pixel, offset_x1 + offset_yr);
|
acc = acc + sample_pixel(pixel, offset_x1 + offset_yr);
|
||||||
acc = acc + sample_pixel(pixel + vec2<i32>(1, 0), offset_x2 + offset_yr);
|
acc = acc + sample_pixel(pixel + vec2<i32>(1, 0), offset_x2 + offset_yr);
|
||||||
|
|
||||||
let offset_yr = offset_y + vec4<f32>(1.0);
|
offset_yr = offset_y + vec4<f32>(1.0);
|
||||||
let offset_yr = vec4<f32>(0.02) + offset_yr * offset_yr;
|
offset_yr = vec4<f32>(0.02) + offset_yr * offset_yr;
|
||||||
|
|
||||||
acc = acc + sample_pixel(pixel + vec2<i32>(-1, 1), offset_x0 + offset_yr);
|
acc = acc + sample_pixel(pixel + vec2<i32>(-1, 1), offset_x0 + offset_yr);
|
||||||
acc = acc + sample_pixel(pixel + vec2<i32>(0, 1), offset_x1 + offset_yr);
|
acc = acc + sample_pixel(pixel + vec2<i32>(0, 1), offset_x1 + offset_yr);
|
||||||
|
|||||||
@@ -53,8 +53,8 @@ impl Window {
|
|||||||
|
|
||||||
window.set_cursor_visible(false);
|
window.set_cursor_visible(false);
|
||||||
|
|
||||||
let instance = wgpu::Instance::new(wgpu::Backends::all());
|
let instance = wgpu::Instance::new(Default::default());
|
||||||
let surface = unsafe { instance.create_surface(&window) };
|
let surface = unsafe { instance.create_surface(&window) }?;
|
||||||
let adapter = instance
|
let adapter = instance
|
||||||
.request_adapter(&wgpu::RequestAdapterOptions {
|
.request_adapter(&wgpu::RequestAdapterOptions {
|
||||||
power_preference: wgpu::PowerPreference::LowPower,
|
power_preference: wgpu::PowerPreference::LowPower,
|
||||||
@@ -71,11 +71,8 @@ impl Window {
|
|||||||
let palette_screen_mode = PaletteScreenMode::new(&device);
|
let palette_screen_mode = PaletteScreenMode::new(&device);
|
||||||
|
|
||||||
let surface_config = wgpu::SurfaceConfiguration {
|
let surface_config = wgpu::SurfaceConfiguration {
|
||||||
usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
|
|
||||||
format: surface.get_supported_formats(&adapter)[0],
|
|
||||||
width: window.inner_size().width,
|
|
||||||
height: window.inner_size().height,
|
|
||||||
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")
|
||||||
};
|
};
|
||||||
|
|
||||||
let filter: Box<dyn Filter> = create_filter(
|
let filter: Box<dyn Filter> = create_filter(
|
||||||
@@ -357,6 +354,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: &[]
|
||||||
});
|
});
|
||||||
|
|
||||||
let palette_texture = device.create_texture(&wgpu::TextureDescriptor {
|
let palette_texture = device.create_texture(&wgpu::TextureDescriptor {
|
||||||
@@ -371,6 +369,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: &[]
|
||||||
});
|
});
|
||||||
|
|
||||||
let screen_texture = device.create_texture(&wgpu::TextureDescriptor {
|
let screen_texture = device.create_texture(&wgpu::TextureDescriptor {
|
||||||
@@ -385,6 +384,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: &[]
|
||||||
});
|
});
|
||||||
|
|
||||||
let framebuffer_texture_view =
|
let framebuffer_texture_view =
|
||||||
|
|||||||
+42
-6
@@ -4,31 +4,64 @@ use std::time::Instant;
|
|||||||
mod cpu;
|
mod cpu;
|
||||||
mod gpu;
|
mod gpu;
|
||||||
|
|
||||||
pub struct Window(Box<dyn WindowImpl>);
|
pub struct Window {
|
||||||
|
inner: Box<dyn WindowImpl>,
|
||||||
|
fps_counter: Option<FpsCounter>,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct FpsCounter {
|
||||||
|
start: Instant,
|
||||||
|
num_frames: u32,
|
||||||
|
}
|
||||||
|
|
||||||
impl Window {
|
impl Window {
|
||||||
pub fn new(config: WindowConfig) -> Result<Window> {
|
pub fn new(config: WindowConfig) -> Result<Window> {
|
||||||
|
let fps_counter = if config.fps_counter {
|
||||||
|
Some(FpsCounter {
|
||||||
|
start: Instant::now(),
|
||||||
|
num_frames: 0,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
if config.enable_gpu {
|
if config.enable_gpu {
|
||||||
match gpu::Window::new(config) {
|
match gpu::Window::new(config) {
|
||||||
Ok(window) => return Ok(Window(Box::new(window))),
|
Ok(window) => {
|
||||||
|
return Ok(Window {
|
||||||
|
inner: Box::new(window),
|
||||||
|
fps_counter,
|
||||||
|
})
|
||||||
|
}
|
||||||
Err(err) => eprintln!(
|
Err(err) => eprintln!(
|
||||||
"Failed to create gpu window: {}\nFalling back tp cpu window",
|
"Failed to create gpu window: {}\nFalling back tp cpu window",
|
||||||
err
|
err
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
cpu::Window::new().map(|window| Window(Box::new(window)))
|
cpu::Window::new().map(|window| Window {
|
||||||
|
inner: Box::new(window),
|
||||||
|
fps_counter,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn begin_frame(&mut self) -> Input {
|
pub fn begin_frame(&mut self) -> Input {
|
||||||
self.0.begin_frame()
|
self.inner.begin_frame()
|
||||||
}
|
}
|
||||||
pub fn end_frame(&mut self, framebuffer: &[u8], palette: &[u8], next_frame: Instant) {
|
pub fn end_frame(&mut self, framebuffer: &[u8], palette: &[u8], next_frame: Instant) {
|
||||||
self.0.end_frame(framebuffer, palette, next_frame)
|
self.inner.end_frame(framebuffer, palette, next_frame);
|
||||||
|
if let Some(ref mut fps_counter) = self.fps_counter {
|
||||||
|
fps_counter.num_frames += 1;
|
||||||
|
let elapsed = fps_counter.start.elapsed().as_secs_f32();
|
||||||
|
if elapsed >= 1.0 {
|
||||||
|
println!("fps: {:.1}", fps_counter.num_frames as f32 / elapsed);
|
||||||
|
fps_counter.num_frames = 0;
|
||||||
|
fps_counter.start = Instant::now();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_open(&self) -> bool {
|
pub fn is_open(&self) -> bool {
|
||||||
self.0.is_open()
|
self.inner.is_open()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -37,6 +70,7 @@ pub struct WindowConfig {
|
|||||||
enable_gpu: bool,
|
enable_gpu: bool,
|
||||||
filter: u32,
|
filter: u32,
|
||||||
fullscreen: bool,
|
fullscreen: bool,
|
||||||
|
fps_counter: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for WindowConfig {
|
impl Default for WindowConfig {
|
||||||
@@ -45,6 +79,7 @@ impl Default for WindowConfig {
|
|||||||
enable_gpu: true,
|
enable_gpu: true,
|
||||||
filter: 5,
|
filter: 5,
|
||||||
fullscreen: false,
|
fullscreen: false,
|
||||||
|
fps_counter: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -66,6 +101,7 @@ impl WindowConfig {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.fullscreen = args.contains("--fullscreen");
|
self.fullscreen = args.contains("--fullscreen");
|
||||||
|
self.fps_counter = args.contains("--fps");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -63,7 +63,7 @@ class APU extends AudioWorkletProcessor {
|
|||||||
|
|
||||||
this.memory = memory;
|
this.memory = memory;
|
||||||
|
|
||||||
this.snd = instance.exports.snd || platform_instance.exports.gesSnd;
|
this.snd = instance.exports.snd || platform_instance.exports.sndGes;
|
||||||
|
|
||||||
this.port.postMessage(2);
|
this.port.postMessage(2);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in new issue
Block a user