Skip to content

Commit

Permalink
Put some large arrays on the heap to avoid overflowing the WASM stack
Browse files Browse the repository at this point in the history
  • Loading branch information
nathsou committed Jul 22, 2023
1 parent 8a989f1 commit 98e0461
Show file tree
Hide file tree
Showing 11 changed files with 141 additions and 466 deletions.
72 changes: 0 additions & 72 deletions crate/src/apu/common.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
use crate::savestate::{self, SaveStateError};

#[derive(Default)]
pub struct Timer {
pub counter: u16,
Expand All @@ -19,26 +17,6 @@ impl Timer {
}
}

const TIMER_SECTION_NAME: &str = "timer";

impl savestate::Save for Timer {
fn save(&self, parent: &mut savestate::Section) {
let s = parent.create_child(TIMER_SECTION_NAME);

s.data.write_u16(self.counter);
s.data.write_u16(self.period);
}

fn load(&mut self, parent: &mut savestate::Section) -> Result<(), SaveStateError> {
let s = parent.get(TIMER_SECTION_NAME)?;

self.counter = s.data.read_u16()?;
self.period = s.data.read_u16()?;

Ok(())
}
}

#[rustfmt::skip]
const LENGTH_LOOKUP: [u8; 32] = [
10, 254, 20, 2, 40, 4, 80, 6,
Expand Down Expand Up @@ -82,26 +60,6 @@ impl LengthCounter {
}
}

const LENGTH_COUNTER_SECTION_NAME: &str = "length_counter";

impl savestate::Save for LengthCounter {
fn save(&self, parent: &mut savestate::Section) {
let s = parent.create_child(LENGTH_COUNTER_SECTION_NAME);

s.data.write_bool(self.enabled);
s.data.write_u8(self.counter);
}

fn load(&mut self, parent: &mut savestate::Section) -> Result<(), SaveStateError> {
let s = parent.get(LENGTH_COUNTER_SECTION_NAME)?;

self.enabled = s.data.read_bool()?;
self.counter = s.data.read_u8()?;

Ok(())
}
}

#[derive(Default)]
pub struct Envelope {
pub constant_mode: bool,
Expand Down Expand Up @@ -141,33 +99,3 @@ impl Envelope {
}
}
}

const ENVELOPE_SECTION_NAME: &str = "envelope";

impl savestate::Save for Envelope {
fn save(&self, parent: &mut savestate::Section) {
let s = parent.create_child(ENVELOPE_SECTION_NAME);

s.data.write_bool(self.constant_mode);
s.data.write_bool(self.looping);
s.data.write_bool(self.start);
s.data.write_u8(self.constant_volume);
s.data.write_u8(self.period);
s.data.write_u8(self.divider);
s.data.write_u8(self.decay);
}

fn load(&mut self, parent: &mut savestate::Section) -> Result<(), SaveStateError> {
let s = parent.get(ENVELOPE_SECTION_NAME)?;

self.constant_mode = s.data.read_bool()?;
self.looping = s.data.read_bool()?;
self.start = s.data.read_bool()?;
self.constant_volume = s.data.read_u8()?;
self.period = s.data.read_u8()?;
self.divider = s.data.read_u8()?;
self.decay = s.data.read_u8()?;

Ok(())
}
}
50 changes: 0 additions & 50 deletions crate/src/apu/dmc.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
use crate::savestate::{self, SaveStateError};

use super::common::Timer;

const DELTA_MODULATION_RATES: [u16; 16] = [
Expand Down Expand Up @@ -148,51 +146,3 @@ impl DeltaModulationChannel {
self.output_level
}
}

const DMC_SECTION_NAME: &str = "dmc";

impl savestate::Save for DeltaModulationChannel {
fn save(&self, parent: &mut savestate::Section) {
let s = parent.create_child(DMC_SECTION_NAME);

s.data.write_bool(self.enabled);
s.data.write_bool(self.interrupt_flag);
s.data.write_bool(self.loop_flag);
s.data.write_u8(self.output_level);
s.data.write_u16(self.sample_addr);
s.data.write_u16(self.sample_len);
s.data.write_u16(self.current_addr);
s.data.write_u16(self.bytes_remaining);
s.data.write_u8(self.shift_register);
s.data.write_bool(self.silence_flag);
s.data.write_u8(self.output_bits_remaining);
s.data.write_bool(self.irq_enabled);
s.data.write_u32(self.cpu_stall);
// ignore memory_read_request

self.timer.save(s);
}

fn load(&mut self, parent: &mut savestate::Section) -> Result<(), SaveStateError> {
let s = parent.get(DMC_SECTION_NAME)?;

self.enabled = s.data.read_bool()?;
self.interrupt_flag = s.data.read_bool()?;
self.loop_flag = s.data.read_bool()?;
self.output_level = s.data.read_u8()?;
self.sample_addr = s.data.read_u16()?;
self.sample_len = s.data.read_u16()?;
self.current_addr = s.data.read_u16()?;
self.bytes_remaining = s.data.read_u16()?;
self.shift_register = s.data.read_u8()?;
self.silence_flag = s.data.read_bool()?;
self.output_bits_remaining = s.data.read_u8()?;
self.irq_enabled = s.data.read_bool()?;
self.cpu_stall = s.data.read_u32()?;
// ignore memory_read_request

self.timer.load(s)?;

Ok(())
}
}
109 changes: 23 additions & 86 deletions crate/src/apu/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
use crate::savestate::{self, SaveStateError};

use self::{
dmc::DeltaModulationChannel,
filters::Filter,
Expand All @@ -15,7 +13,8 @@ mod noise;
mod pulse;
mod triangle;

const APU_BUFFER_SIZE: usize = 16 * 1024;
const BUFFER_SIZE: usize = 8 * 1024; // 2^14
const BUFFER_MASK: u16 = (BUFFER_SIZE as u16) - 1;
const CPU_FREQ: f64 = 1789772.5;

#[derive(Clone, Copy)]
Expand Down Expand Up @@ -46,8 +45,9 @@ impl From<FrameMode> for u8 {
#[allow(clippy::upper_case_acronyms)]
pub struct APU {
cycles_per_sample: f64,
buffer: [f32; APU_BUFFER_SIZE],
buffer_write_index: u16,
buffer: Box<[f32; BUFFER_SIZE]>, // avoid stack overflow in WASM
front_ptr: u16,
back_ptr: u16,
cycle: u32,
frame_counter: u32,
frame_interrupt: bool,
Expand All @@ -61,7 +61,7 @@ pub struct APU {
triangle: TriangleChannel,
noise: NoiseChannel,
dmc: DeltaModulationChannel,
filters: Vec<Filter>,
filters: [Filter; 3],
}

#[rustfmt::skip]
Expand Down Expand Up @@ -110,8 +110,9 @@ impl APU {
pub fn new(sound_card_sample_rate: f64) -> APU {
APU {
cycles_per_sample: CPU_FREQ / sound_card_sample_rate,
buffer: [0.0; APU_BUFFER_SIZE],
buffer_write_index: 0,
buffer: Box::new([0.0; BUFFER_SIZE]),
front_ptr: 0,
back_ptr: 0,
cycle: 0,
frame_counter: 0,
frame_interrupt: false,
Expand All @@ -125,7 +126,7 @@ impl APU {
samples_pushed: 0,
irq_inhibit: false,
prev_irq: false,
filters: vec![
filters: [
Filter::new_high_pass(sound_card_sample_rate as f32, 90.0),
Filter::new_high_pass(sound_card_sample_rate as f32, 440.0),
Filter::new_low_pass(sound_card_sample_rate as f32, 14_000.0),
Expand Down Expand Up @@ -154,14 +155,9 @@ impl APU {
fn push_sample(&mut self) {
let sample = self.get_sample();
self.current_sample = Some(sample);

if self.buffer_write_index < APU_BUFFER_SIZE as u16 {
self.buffer[self.buffer_write_index as usize] = sample;
self.samples_pushed += 1;
self.buffer_write_index += 1;
} else {
self.clear_buffer();
}
self.buffer[self.front_ptr as usize] = sample;
self.samples_pushed += 1;
self.front_ptr = (self.front_ptr + 1) & BUFFER_MASK;
}

#[inline]
Expand Down Expand Up @@ -317,34 +313,24 @@ impl APU {
}

pub fn remaining_buffered_samples(&self) -> u16 {
self.buffer_write_index
if self.front_ptr >= self.back_ptr {
self.front_ptr - self.back_ptr
} else {
BUFFER_SIZE as u16 - self.back_ptr + self.front_ptr
}
}

pub fn fill(&mut self, buffer: &mut [f32]) {
let client_buffer_size = buffer.len();

#[allow(clippy::needless_range_loop)]
for i in 0..client_buffer_size {
buffer[i] = if i < self.buffer_write_index as usize {
self.buffer[i]
} else {
0.0 // Buffer underflow
};
for i in 0..buffer.len().min(self.remaining_buffered_samples() as usize) {
buffer[i] = self.buffer[self.back_ptr as usize];
self.back_ptr = (self.back_ptr + 1) & BUFFER_MASK;
}

for i in client_buffer_size..APU_BUFFER_SIZE {
self.buffer[i - client_buffer_size] = self.buffer[i];
}

self.buffer_write_index = if self.buffer_write_index > client_buffer_size as u16 {
self.buffer_write_index - client_buffer_size as u16
} else {
0
};
}

pub fn clear_buffer(&mut self) {
self.buffer_write_index = 0;
self.front_ptr = 0;
self.back_ptr = 0;
self.buffer.fill(0.0);
}

Expand Down Expand Up @@ -379,52 +365,3 @@ impl APU {
self.dmc.set_memory_read_response(val);
}
}

impl savestate::Save for APU {
fn save(&self, parent: &mut savestate::Section) {
let s = parent.create_child("apu");

// ignore cycles_per_sample
// as it depends on the sample rate

s.data.write_f32_slice(&self.buffer);
s.data.write_u16(self.buffer_write_index);
s.data.write_u32(self.cycle);
s.data.write_u32(self.frame_counter);
s.data.write_bool(self.frame_interrupt);
s.data.write_u8(self.frame_mode.into());
// ignore current_sample
s.data.write_u32(self.samples_pushed);
s.data.write_bool(self.irq_inhibit);
s.data.write_bool(self.prev_irq);

self.pulse1.save(s);
self.pulse2.save(s);
self.triangle.save(s);
self.noise.save(s);
self.dmc.save(s);
}

fn load(&mut self, parent: &mut savestate::Section) -> Result<(), SaveStateError> {
let s = parent.get("apu")?;

s.data.read_f32_slice(&mut self.buffer)?;
self.buffer_write_index = s.data.read_u16()?;
self.cycle = s.data.read_u32()?;
self.frame_counter = s.data.read_u32()?;
self.frame_interrupt = s.data.read_bool()?;
self.frame_mode = s.data.read_u8()?.into();
// ignore current_sample
self.samples_pushed = s.data.read_u32()?;
self.irq_inhibit = s.data.read_bool()?;
self.prev_irq = s.data.read_bool()?;

self.pulse1.load(s)?;
self.pulse2.load(s)?;
self.triangle.load(s)?;
self.noise.load(s)?;
self.dmc.load(s)?;

Ok(())
}
}
32 changes: 0 additions & 32 deletions crate/src/apu/noise.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
use crate::savestate::{self, SaveStateError};

use super::common::{Envelope, LengthCounter, Timer};

const NOISE_PERIOD_TABLE: [u16; 16] = [
Expand Down Expand Up @@ -91,33 +89,3 @@ impl NoiseChannel {
}
}
}

const NOISE_SECTION_NAME: &str = "noise";

impl savestate::Save for NoiseChannel {
fn save(&self, parent: &mut savestate::Section) {
let s = parent.create_child(NOISE_SECTION_NAME);

s.data.write_bool(self.enabled);
s.data.write_u16(self.shift_register);
s.data.write_bool(self.mode);

self.length_counter.save(s);
self.envelope.save(s);
self.timer.save(s);
}

fn load(&mut self, parent: &mut savestate::Section) -> Result<(), SaveStateError> {
let s = parent.get(NOISE_SECTION_NAME)?;

self.enabled = s.data.read_bool()?;
self.shift_register = s.data.read_u16()?;
self.mode = s.data.read_bool()?;

self.length_counter.load(s)?;
self.envelope.load(s)?;
self.timer.load(s)?;

Ok(())
}
}
Loading

0 comments on commit 98e0461

Please sign in to comment.