Compare commits

...
16 Commits
7 changed files with 88 additions and 66 deletions
Generated
+1 -1
View File
@@ -1709,7 +1709,7 @@ checksum = "e2db585e1d738fc771bf08a151420d3ed193d9d895a36df7f6f8a9456b911ddc"
[[package]] [[package]]
name = "lemur" name = "lemur"
version = "0.2.2" version = "0.2.9"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"bitflags 2.6.0", "bitflags 2.6.0",
+1 -1
View File
@@ -4,7 +4,7 @@ description = "An emulator for the Virtual Boy."
repository = "https://git.virtual-boy.com/PVB/lemur" repository = "https://git.virtual-boy.com/PVB/lemur"
publish = false publish = false
license = "MIT" license = "MIT"
version = "0.2.2" version = "0.2.9"
edition = "2021" edition = "2021"
[dependencies] [dependencies]
+9 -2
View File
@@ -1,3 +1,4 @@
set -e
version=$(cat Cargo.toml | sed -n -e '/version/ {s/.* = *//p;q}' | tr -d '"') version=$(cat Cargo.toml | sed -n -e '/version/ {s/.* = *//p;q}' | tr -d '"')
read -p "You wanted to release $version, right? [Y/n] " -n 1 -r read -p "You wanted to release $version, right? [Y/n] " -n 1 -r
@@ -18,11 +19,15 @@ if ! command -v jq 2>&1 >/dev/null; then
echo "Please install jq." echo "Please install jq."
exit 1 exit 1
fi fi
if ! command -v docker 2>&1 >/dev/null; then
echo "Please install docker."
exit 1
fi
docker build -f build.Dockerfile -t lemur-build . docker build -f build.Dockerfile -t lemur-build .
MSYS_NO_PATHCONV=1 docker run -it --rm -v .:/app -w /app --entrypoint bash lemur-build /app/scripts/do-bundle.sh MSYS_NO_PATHCONV=1 docker run -it --rm -v .:/app -w /app --entrypoint bash lemur-build /app/scripts/do-bundle.sh
read -r -d EOF 'body' <<EOF body=$(cat <<EOF
## How to install ## How to install
The emulator can be found in the "Downloads" section of this release. The emulator can be found in the "Downloads" section of this release.
@@ -43,8 +48,9 @@ If you're not sure which to choose, use [this guide](https://support.apple.com/e
You can either download and run \`lemur-linux\`, or download and install the attached .deb file. You can either download and run \`lemur-linux\`, or download and install the attached .deb file.
EOF EOF
)
read -r -d EOF 'payload' <<EOF payload=$(cat <<EOF
{ {
"body": $(echo "$body" | jq -Rsa .), "body": $(echo "$body" | jq -Rsa .),
"draft": false, "draft": false,
@@ -53,6 +59,7 @@ read -r -d EOF 'payload' <<EOF
"tag_name": "v${version}" "tag_name": "v${version}"
} }
EOF EOF
)
echo "Creating release..." echo "Creating release..."
response=$(curl -s --json "$payload" "https://git.virtual-boy.com/api/v1/repos/PVB/lemur/releases?token=$RELEASE_TOKEN") response=$(curl -s --json "$payload" "https://git.virtual-boy.com/api/v1/repos/PVB/lemur/releases?token=$RELEASE_TOKEN")
+17 -11
View File
@@ -251,17 +251,23 @@ impl Viewport {
}); });
egui_extras::install_image_loaders(&ctx); egui_extras::install_image_loaders(&ctx);
let mut painter = egui_wgpu::winit::Painter::new( #[allow(unused_mut)]
ctx.clone(), let mut wgpu_config = egui_wgpu::WgpuConfiguration {
egui_wgpu::WgpuConfiguration { present_mode: wgpu::PresentMode::AutoNoVsync,
present_mode: wgpu::PresentMode::AutoNoVsync, ..egui_wgpu::WgpuConfiguration::default()
..egui_wgpu::WgpuConfiguration::default() };
}, #[cfg(windows)]
1, {
None, if let egui_wgpu::WgpuSetup::CreateNew {
false, supported_backends, ..
true, } = &mut wgpu_config.wgpu_setup
); {
*supported_backends -= wgpu::Backends::VULKAN;
}
}
let mut painter =
egui_wgpu::winit::Painter::new(ctx.clone(), wgpu_config, 1, None, false, true);
let mut info = ViewportInfo::default(); let mut info = ViewportInfo::default();
let mut builder = app.initial_viewport(); let mut builder = app.initial_viewport();
+57 -49
View File
@@ -141,6 +141,8 @@ pub struct Emulator {
linked: Arc<AtomicBool>, linked: Arc<AtomicBool>,
renderers: HashMap<SimId, TextureSink>, renderers: HashMap<SimId, TextureSink>,
messages: HashMap<SimId, mpsc::Sender<Toast>>, messages: HashMap<SimId, mpsc::Sender<Toast>>,
eye_contents: Vec<u8>,
audio_samples: Vec<f32>,
} }
impl Emulator { impl Emulator {
@@ -164,6 +166,8 @@ impl Emulator {
linked, linked,
renderers: HashMap::new(), renderers: HashMap::new(),
messages: HashMap::new(), messages: HashMap::new(),
eye_contents: vec![0u8; 384 * 224 * 2],
audio_samples: vec![0.0; EXPECTED_FRAME_SIZE],
}) })
} }
@@ -257,56 +261,8 @@ impl Emulator {
} }
pub fn run(&mut self) { pub fn run(&mut self) {
let mut eye_contents = vec![0u8; 384 * 224 * 2];
let mut audio_samples = vec![];
loop { loop {
let p1_running = self.running[SimId::Player1.to_index()].load(Ordering::Acquire); let idle = self.tick();
let p2_running = self.running[SimId::Player2.to_index()].load(Ordering::Acquire);
let mut idle = p1_running || p2_running;
if p1_running && p2_running {
Sim::emulate_many(&mut self.sims);
} else if p1_running {
self.sims[SimId::Player1.to_index()].emulate();
} else if p2_running {
self.sims[SimId::Player2.to_index()].emulate();
}
for sim_id in SimId::values() {
let Some(renderer) = self.renderers.get_mut(&sim_id) else {
continue;
};
let Some(sim) = self.sims.get_mut(sim_id.to_index()) else {
continue;
};
if sim.read_pixels(&mut eye_contents) {
idle = false;
if renderer.queue_render(&eye_contents).is_err() {
self.renderers.remove(&sim_id);
}
}
}
let p1_audio =
p1_running && self.audio_on[SimId::Player1.to_index()].load(Ordering::Acquire);
let p2_audio =
p2_running && self.audio_on[SimId::Player2.to_index()].load(Ordering::Acquire);
let weight = if p1_audio && p2_audio { 0.5 } else { 1.0 };
if p1_audio {
if let Some(sim) = self.sims.get_mut(SimId::Player1.to_index()) {
sim.read_samples(&mut audio_samples, weight);
}
}
if p2_audio {
if let Some(sim) = self.sims.get_mut(SimId::Player2.to_index()) {
sim.read_samples(&mut audio_samples, weight);
}
}
if audio_samples.is_empty() {
audio_samples.resize(EXPECTED_FRAME_SIZE, 0.0);
} else {
idle = false;
}
self.audio.update(&audio_samples);
audio_samples.clear();
if idle { if idle {
// The game is paused, and we have output all the video/audio we have. // The game is paused, and we have output all the video/audio we have.
// Block the thread until a new command comes in. // Block the thread until a new command comes in.
@@ -331,6 +287,58 @@ impl Emulator {
} }
} }
// returns true if the emulator is "idle" (i.e. this didn't output anything)
pub fn tick(&mut self) -> bool {
let p1_running = self.running[SimId::Player1.to_index()].load(Ordering::Acquire);
let p2_running = self.running[SimId::Player2.to_index()].load(Ordering::Acquire);
let mut idle = p1_running || p2_running;
if p1_running && p2_running {
Sim::emulate_many(&mut self.sims);
} else if p1_running {
self.sims[SimId::Player1.to_index()].emulate();
} else if p2_running {
self.sims[SimId::Player2.to_index()].emulate();
}
for sim_id in SimId::values() {
let Some(renderer) = self.renderers.get_mut(&sim_id) else {
continue;
};
let Some(sim) = self.sims.get_mut(sim_id.to_index()) else {
continue;
};
if sim.read_pixels(&mut self.eye_contents) {
idle = false;
if renderer.queue_render(&self.eye_contents).is_err() {
self.renderers.remove(&sim_id);
}
}
}
let p1_audio =
p1_running && self.audio_on[SimId::Player1.to_index()].load(Ordering::Acquire);
let p2_audio =
p2_running && self.audio_on[SimId::Player2.to_index()].load(Ordering::Acquire);
let weight = if p1_audio && p2_audio { 0.5 } else { 1.0 };
if p1_audio {
if let Some(sim) = self.sims.get_mut(SimId::Player1.to_index()) {
sim.read_samples(&mut self.audio_samples, weight);
}
}
if p2_audio {
if let Some(sim) = self.sims.get_mut(SimId::Player2.to_index()) {
sim.read_samples(&mut self.audio_samples, weight);
}
}
if self.audio_samples.is_empty() {
self.audio_samples.resize(EXPECTED_FRAME_SIZE, 0.0);
} else {
idle = false;
}
self.audio.update(&self.audio_samples);
self.audio_samples.clear();
idle
}
fn handle_command(&mut self, command: EmulatorCommand) { fn handle_command(&mut self, command: EmulatorCommand) {
match command { match command {
EmulatorCommand::ConnectToSim(sim_id, renderer, messages) => { EmulatorCommand::ConnectToSim(sim_id, renderer, messages) => {
+2 -1
View File
@@ -147,7 +147,8 @@ impl Sim {
let memory = vec![0u64; size.div_ceil(4)]; let memory = vec![0u64; size.div_ceil(4)];
let sim: *mut VB = Box::into_raw(memory.into_boxed_slice()).cast(); let sim: *mut VB = Box::into_raw(memory.into_boxed_slice()).cast();
unsafe { vb_init(sim) }; unsafe { vb_init(sim) };
unsafe { vb_set_option(sim, VBOption::PseudoHalt, 1) }; // pseudohalt is disabled due to breaking red alarm
unsafe { vb_set_option(sim, VBOption::PseudoHalt, 0) };
unsafe { vb_reset(sim) }; unsafe { vb_reset(sim) };
// set up userdata // set up userdata