mirror of
https://github.com/bsnes-emu/bsnes.git
synced 2025-08-21 20:35:48 +02:00
v111.8
Added fully working deterministic save state support (non-portable.) Rewind is now 100% deterministic, disk save states are still portable. Added run-ahead support.
This commit is contained in:
@@ -73,6 +73,8 @@ struct Stream {
|
|||||||
write(samples);
|
write(samples);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto serialize(serializer&) -> void;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct Channel {
|
struct Channel {
|
||||||
vector<Filter> filters;
|
vector<Filter> filters;
|
||||||
|
@@ -115,3 +115,11 @@ auto Stream::write(const double samples[]) -> void {
|
|||||||
|
|
||||||
audio.process();
|
audio.process();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto Stream::serialize(serializer& s) -> void {
|
||||||
|
for(auto& channel : channels) {
|
||||||
|
channel.resampler.serialize(s);
|
||||||
|
}
|
||||||
|
s.real(inputFrequency);
|
||||||
|
s.real(outputFrequency);
|
||||||
|
}
|
||||||
|
@@ -29,7 +29,7 @@ using namespace nall;
|
|||||||
|
|
||||||
namespace Emulator {
|
namespace Emulator {
|
||||||
static const string Name = "bsnes";
|
static const string Name = "bsnes";
|
||||||
static const string Version = "111.7";
|
static const string Version = "111.8";
|
||||||
static const string Author = "byuu";
|
static const string Author = "byuu";
|
||||||
static const string License = "GPLv3";
|
static const string License = "GPLv3";
|
||||||
static const string Website = "https://byuu.org";
|
static const string Website = "https://byuu.org";
|
||||||
|
@@ -101,6 +101,9 @@ struct Interface {
|
|||||||
|
|
||||||
virtual auto frameSkip() -> uint { return 0; }
|
virtual auto frameSkip() -> uint { return 0; }
|
||||||
virtual auto setFrameSkip(uint frameSkip) -> void {}
|
virtual auto setFrameSkip(uint frameSkip) -> void {}
|
||||||
|
|
||||||
|
virtual auto runAhead() -> bool { return false; }
|
||||||
|
virtual auto setRunAhead(bool runAhead) -> void {}
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -67,7 +67,7 @@ auto CPU::load() -> bool {
|
|||||||
|
|
||||||
auto CPU::power(bool reset) -> void {
|
auto CPU::power(bool reset) -> void {
|
||||||
WDC65816::power();
|
WDC65816::power();
|
||||||
create(Enter, system.cpuFrequency());
|
Thread::create(Enter, system.cpuFrequency());
|
||||||
coprocessors.reset();
|
coprocessors.reset();
|
||||||
PPUcounter::reset();
|
PPUcounter::reset();
|
||||||
PPUcounter::scanline = {&CPU::scanline, this};
|
PPUcounter::scanline = {&CPU::scanline, this};
|
||||||
|
@@ -18,6 +18,7 @@ auto DSP::main() -> void {
|
|||||||
|
|
||||||
int count = spc_dsp.sample_count();
|
int count = spc_dsp.sample_count();
|
||||||
if(count > 0) {
|
if(count > 0) {
|
||||||
|
if(!system.runAhead)
|
||||||
for(uint n = 0; n < count; n += 2) {
|
for(uint n = 0; n < count; n += 2) {
|
||||||
float left = samplebuffer[n + 0] / 32768.0f;
|
float left = samplebuffer[n + 0] / 32768.0f;
|
||||||
float right = samplebuffer[n + 1] / 32768.0f;
|
float right = samplebuffer[n + 1] / 32768.0f;
|
||||||
|
@@ -330,7 +330,15 @@ auto Interface::frameSkip() -> uint {
|
|||||||
|
|
||||||
auto Interface::setFrameSkip(uint frameSkip) -> void {
|
auto Interface::setFrameSkip(uint frameSkip) -> void {
|
||||||
system.frameSkip = frameSkip;
|
system.frameSkip = frameSkip;
|
||||||
system.frameCounter = 0;
|
system.frameCounter = frameSkip;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Interface::runAhead() -> bool {
|
||||||
|
return system.runAhead;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Interface::setRunAhead(bool runAhead) -> void {
|
||||||
|
system.runAhead = runAhead;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -71,6 +71,9 @@ struct Interface : Emulator::Interface {
|
|||||||
|
|
||||||
auto frameSkip() -> uint override;
|
auto frameSkip() -> uint override;
|
||||||
auto setFrameSkip(uint frameSkip) -> void override;
|
auto setFrameSkip(uint frameSkip) -> void override;
|
||||||
|
|
||||||
|
auto runAhead() -> bool override;
|
||||||
|
auto setRunAhead(bool runAhead) -> void override;
|
||||||
};
|
};
|
||||||
|
|
||||||
#include "configuration.hpp"
|
#include "configuration.hpp"
|
||||||
|
@@ -88,7 +88,7 @@ auto PPU::step(uint clocks) -> void {
|
|||||||
auto PPU::main() -> void {
|
auto PPU::main() -> void {
|
||||||
scanline();
|
scanline();
|
||||||
|
|
||||||
if(system.frameCounter == 0) {
|
if(system.frameCounter == 0 && !system.runAhead) {
|
||||||
uint y = vcounter();
|
uint y = vcounter();
|
||||||
if(y >= 1 && y <= 239) {
|
if(y >= 1 && y <= 239) {
|
||||||
step(renderCycle());
|
step(renderCycle());
|
||||||
@@ -127,7 +127,7 @@ auto PPU::scanline() -> void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
auto PPU::refresh() -> void {
|
auto PPU::refresh() -> void {
|
||||||
if(system.frameCounter == 0) {
|
if(system.frameCounter == 0 && !system.runAhead) {
|
||||||
auto output = this->output;
|
auto output = this->output;
|
||||||
uint pitch, width, height;
|
uint pitch, width, height;
|
||||||
if(!hd()) {
|
if(!hd()) {
|
||||||
|
@@ -189,6 +189,8 @@ auto PPU::refresh() -> void {
|
|||||||
return ppufast.refresh();
|
return ppufast.refresh();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(system.runAhead) return;
|
||||||
|
|
||||||
auto output = this->output;
|
auto output = this->output;
|
||||||
auto pitch = 512;
|
auto pitch = 512;
|
||||||
auto width = 512;
|
auto width = 512;
|
||||||
|
@@ -73,13 +73,13 @@ namespace SuperFamicom {
|
|||||||
extern Scheduler scheduler;
|
extern Scheduler scheduler;
|
||||||
|
|
||||||
struct Thread {
|
struct Thread {
|
||||||
enum : uint { Size = 16_KiB * sizeof(void*) };
|
enum : uint { Size = 4_KiB * sizeof(void*) };
|
||||||
|
|
||||||
auto create(auto (*entrypoint)() -> void, uint frequency_) -> void {
|
auto create(auto (*entrypoint)() -> void, uint frequency_) -> void {
|
||||||
if(!thread) {
|
if(!thread) {
|
||||||
thread = co_create(Thread::Size, entrypoint);
|
thread = co_create(Thread::Size, entrypoint);
|
||||||
} else {
|
} else {
|
||||||
co_derive(thread, Thread::Size, entrypoint);
|
thread = co_derive(thread, Thread::Size, entrypoint);
|
||||||
}
|
}
|
||||||
frequency = frequency_;
|
frequency = frequency_;
|
||||||
clock = 0;
|
clock = 0;
|
||||||
@@ -95,23 +95,26 @@ namespace SuperFamicom {
|
|||||||
}
|
}
|
||||||
|
|
||||||
auto serializeStack(serializer& s) -> void {
|
auto serializeStack(serializer& s) -> void {
|
||||||
auto stack = new uint8_t[Thread::Size];
|
static uint8_t stack[Thread::Size];
|
||||||
|
bool active = co_active() == thread;
|
||||||
|
|
||||||
if(s.mode() == serializer::Size) {
|
if(s.mode() == serializer::Size) {
|
||||||
s.array(stack, Thread::Size);
|
s.array(stack, Thread::Size);
|
||||||
|
s.boolean(active);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(s.mode() == serializer::Load) {
|
if(s.mode() == serializer::Load) {
|
||||||
s.array(stack, Thread::Size);
|
s.array(stack, Thread::Size);
|
||||||
|
s.boolean(active);
|
||||||
memory::copy(thread, stack, Thread::Size);
|
memory::copy(thread, stack, Thread::Size);
|
||||||
|
if(active) scheduler.active = thread;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(s.mode() == serializer::Save) {
|
if(s.mode() == serializer::Save) {
|
||||||
memory::copy(stack, thread, Thread::Size);
|
memory::copy(stack, thread, Thread::Size);
|
||||||
s.array(stack, Thread::Size);
|
s.array(stack, Thread::Size);
|
||||||
|
s.boolean(active);
|
||||||
}
|
}
|
||||||
|
|
||||||
delete[] stack;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
cothread_t thread = nullptr;
|
cothread_t thread = nullptr;
|
||||||
|
@@ -1,4 +1,5 @@
|
|||||||
auto System::serialize(bool synchronize) -> serializer {
|
auto System::serialize(bool synchronize) -> serializer {
|
||||||
|
if(!information.serializeSize[synchronize]) return {}; //should never occur
|
||||||
if(synchronize) runToSave();
|
if(synchronize) runToSave();
|
||||||
|
|
||||||
uint signature = 0x31545342;
|
uint signature = 0x31545342;
|
||||||
@@ -38,7 +39,7 @@ auto System::unserialize(serializer& s) -> bool {
|
|||||||
if(string{version} != Emulator::SerializerVersion) return false;
|
if(string{version} != Emulator::SerializerVersion) return false;
|
||||||
if(fastPPU != hacks.fastPPU) return false;
|
if(fastPPU != hacks.fastPPU) return false;
|
||||||
|
|
||||||
power(/* reset = */ false);
|
if(synchronize) power(/* reset = */ false);
|
||||||
serializeAll(s, synchronize);
|
serializeAll(s, synchronize);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@@ -24,17 +24,54 @@ auto System::runToSave() -> void {
|
|||||||
//fallback in case of unrecognized method specified
|
//fallback in case of unrecognized method specified
|
||||||
if(method != "Fast" && method != "Strict") method = "Fast";
|
if(method != "Fast" && method != "Strict") method = "Fast";
|
||||||
|
|
||||||
auto synchronize = [&](cothread_t thread) -> bool {
|
scheduler.mode = Scheduler::Mode::Synchronize;
|
||||||
scheduler.mode = Scheduler::Mode::Synchronize;
|
if(method == "Fast") runToSaveFast();
|
||||||
|
if(method == "Strict") runToSaveStrict();
|
||||||
|
|
||||||
|
scheduler.mode = Scheduler::Mode::Run;
|
||||||
|
scheduler.active = cpu.thread;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto System::runToSaveFast() -> void {
|
||||||
|
//run the emulator normally until the CPU thread naturally hits a synchronization point
|
||||||
|
while(true) {
|
||||||
|
scheduler.enter();
|
||||||
|
if(scheduler.event == Scheduler::Event::Frame) frameEvent();
|
||||||
|
if(scheduler.event == Scheduler::Event::Synchronized) {
|
||||||
|
if(scheduler.active != cpu.thread) continue;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if(scheduler.event == Scheduler::Event::Desynchronized) continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
//ignore any desynchronization events to force all other threads to their synchronization points
|
||||||
|
auto synchronize = [&](cothread_t thread) -> void {
|
||||||
scheduler.active = thread;
|
scheduler.active = thread;
|
||||||
while(true) {
|
while(true) {
|
||||||
scheduler.enter();
|
scheduler.enter();
|
||||||
if(scheduler.event == Scheduler::Event::Frame) frameEvent();
|
if(scheduler.event == Scheduler::Event::Frame) frameEvent();
|
||||||
if(scheduler.event == Scheduler::Event::Synchronized) break;
|
if(scheduler.event == Scheduler::Event::Synchronized) break;
|
||||||
if(scheduler.event == Scheduler::Event::Desynchronized) {
|
if(scheduler.event == Scheduler::Event::Desynchronized) continue;
|
||||||
if(method == "Fast") continue;
|
}
|
||||||
if(method == "Strict") return false;
|
};
|
||||||
}
|
|
||||||
|
synchronize(smp.thread);
|
||||||
|
synchronize(ppu.thread);
|
||||||
|
for(auto coprocessor : cpu.coprocessors) {
|
||||||
|
synchronize(coprocessor->thread);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto System::runToSaveStrict() -> void {
|
||||||
|
//run every thread until it cleanly hits a synchronization point
|
||||||
|
//if it fails, start resynchronizing every thread again
|
||||||
|
auto synchronize = [&](cothread_t thread) -> bool {
|
||||||
|
scheduler.active = thread;
|
||||||
|
while(true) {
|
||||||
|
scheduler.enter();
|
||||||
|
if(scheduler.event == Scheduler::Event::Frame) frameEvent();
|
||||||
|
if(scheduler.event == Scheduler::Event::Synchronized) break;
|
||||||
|
if(scheduler.event == Scheduler::Event::Desynchronized) return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
@@ -55,9 +92,6 @@ auto System::runToSave() -> void {
|
|||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
scheduler.mode = Scheduler::Mode::Run;
|
|
||||||
scheduler.active = cpu.thread;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
auto System::frameEvent() -> void {
|
auto System::frameEvent() -> void {
|
||||||
@@ -97,8 +131,6 @@ auto System::load(Emulator::Interface* interface) -> bool {
|
|||||||
}
|
}
|
||||||
if(cartridge.has.BSMemorySlot) bsmemory.load();
|
if(cartridge.has.BSMemorySlot) bsmemory.load();
|
||||||
|
|
||||||
information.serializeSize[0] = serializeInit(0);
|
|
||||||
information.serializeSize[1] = serializeInit(1);
|
|
||||||
this->interface = interface;
|
this->interface = interface;
|
||||||
return information.loaded = true;
|
return information.loaded = true;
|
||||||
}
|
}
|
||||||
@@ -195,6 +227,9 @@ auto System::power(bool reset) -> void {
|
|||||||
controllerPort1.connect(settings.controllerPort1);
|
controllerPort1.connect(settings.controllerPort1);
|
||||||
controllerPort2.connect(settings.controllerPort2);
|
controllerPort2.connect(settings.controllerPort2);
|
||||||
expansionPort.connect(settings.expansionPort);
|
expansionPort.connect(settings.expansionPort);
|
||||||
|
|
||||||
|
information.serializeSize[0] = serializeInit(0);
|
||||||
|
information.serializeSize[1] = serializeInit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -10,6 +10,8 @@ struct System {
|
|||||||
|
|
||||||
auto run() -> void;
|
auto run() -> void;
|
||||||
auto runToSave() -> void;
|
auto runToSave() -> void;
|
||||||
|
auto runToSaveFast() -> void;
|
||||||
|
auto runToSaveStrict() -> void;
|
||||||
auto frameEvent() -> void;
|
auto frameEvent() -> void;
|
||||||
|
|
||||||
auto load(Emulator::Interface*) -> bool;
|
auto load(Emulator::Interface*) -> bool;
|
||||||
@@ -23,6 +25,7 @@ struct System {
|
|||||||
|
|
||||||
uint frameSkip = 0;
|
uint frameSkip = 0;
|
||||||
uint frameCounter = 0;
|
uint frameCounter = 0;
|
||||||
|
bool runAhead = 0;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Emulator::Interface* interface = nullptr;
|
Emulator::Interface* interface = nullptr;
|
||||||
|
@@ -8,8 +8,6 @@ auto InputManager::bindHotkeys() -> void {
|
|||||||
static int stateSlot = 1;
|
static int stateSlot = 1;
|
||||||
static double frequency = 48000.0;
|
static double frequency = 48000.0;
|
||||||
static double volume = 0.0;
|
static double volume = 0.0;
|
||||||
static bool fastForwarding = false;
|
|
||||||
static bool rewinding = false;
|
|
||||||
|
|
||||||
hotkeys.append(InputHotkey("Toggle Fullscreen").onPress([] {
|
hotkeys.append(InputHotkey("Toggle Fullscreen").onPress([] {
|
||||||
program.toggleVideoFullScreen();
|
program.toggleVideoFullScreen();
|
||||||
@@ -32,8 +30,8 @@ auto InputManager::bindHotkeys() -> void {
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
hotkeys.append(InputHotkey("Rewind").onPress([&] {
|
hotkeys.append(InputHotkey("Rewind").onPress([&] {
|
||||||
if(!emulator->loaded() || fastForwarding) return;
|
if(!emulator->loaded() || program.fastForwarding) return;
|
||||||
rewinding = true;
|
program.rewinding = true;
|
||||||
if(program.rewind.frequency == 0) {
|
if(program.rewind.frequency == 0) {
|
||||||
program.showMessage("Please enable rewind support in Settings->Emulator first");
|
program.showMessage("Please enable rewind support in Settings->Emulator first");
|
||||||
} else {
|
} else {
|
||||||
@@ -46,7 +44,7 @@ auto InputManager::bindHotkeys() -> void {
|
|||||||
Emulator::audio.setVolume(volume * 0.65);
|
Emulator::audio.setVolume(volume * 0.65);
|
||||||
}
|
}
|
||||||
}).onRelease([&] {
|
}).onRelease([&] {
|
||||||
rewinding = false;
|
program.rewinding = false;
|
||||||
if(!emulator->loaded()) return;
|
if(!emulator->loaded()) return;
|
||||||
program.rewindMode(Program::Rewind::Mode::Playing);
|
program.rewindMode(Program::Rewind::Mode::Playing);
|
||||||
program.mute &= ~Program::Mute::Rewind;
|
program.mute &= ~Program::Mute::Rewind;
|
||||||
@@ -84,8 +82,8 @@ auto InputManager::bindHotkeys() -> void {
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
hotkeys.append(InputHotkey("Fast Forward").onPress([] {
|
hotkeys.append(InputHotkey("Fast Forward").onPress([] {
|
||||||
if(!emulator->loaded() || rewinding) return;
|
if(!emulator->loaded() || program.rewinding) return;
|
||||||
fastForwarding = true;
|
program.fastForwarding = true;
|
||||||
emulator->setFrameSkip(emulator->configuration("Hacks/PPU/Fast") == "true" ? settings.fastForward.frameSkip : 0);
|
emulator->setFrameSkip(emulator->configuration("Hacks/PPU/Fast") == "true" ? settings.fastForward.frameSkip : 0);
|
||||||
video.setBlocking(false);
|
video.setBlocking(false);
|
||||||
audio.setBlocking(settings.fastForward.limiter != 0);
|
audio.setBlocking(settings.fastForward.limiter != 0);
|
||||||
@@ -101,7 +99,7 @@ auto InputManager::bindHotkeys() -> void {
|
|||||||
Emulator::audio.setVolume(volume * 0.65);
|
Emulator::audio.setVolume(volume * 0.65);
|
||||||
}
|
}
|
||||||
}).onRelease([] {
|
}).onRelease([] {
|
||||||
fastForwarding = false;
|
program.fastForwarding = false;
|
||||||
if(!emulator->loaded()) return;
|
if(!emulator->loaded()) return;
|
||||||
emulator->setFrameSkip(0);
|
emulator->setFrameSkip(0);
|
||||||
video.setBlocking(settings.video.blocking);
|
video.setBlocking(settings.video.blocking);
|
||||||
|
@@ -93,7 +93,22 @@ auto Program::main() -> void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
rewindRun();
|
rewindRun();
|
||||||
emulator->run();
|
|
||||||
|
if(!settings.emulator.runAhead.frames || fastForwarding || rewinding) {
|
||||||
|
emulator->run();
|
||||||
|
} else {
|
||||||
|
emulator->setRunAhead(true);
|
||||||
|
emulator->run();
|
||||||
|
auto state = emulator->serialize(0);
|
||||||
|
if(settings.emulator.runAhead.frames >= 2) emulator->run();
|
||||||
|
if(settings.emulator.runAhead.frames >= 3) emulator->run();
|
||||||
|
if(settings.emulator.runAhead.frames >= 4) emulator->run();
|
||||||
|
emulator->setRunAhead(false);
|
||||||
|
emulator->run();
|
||||||
|
state.setMode(serializer::Mode::Load);
|
||||||
|
emulator->unserialize(state);
|
||||||
|
}
|
||||||
|
|
||||||
if(emulatorSettings.autoSaveMemory.checked()) {
|
if(emulatorSettings.autoSaveMemory.checked()) {
|
||||||
auto currentTime = chrono::timestamp();
|
auto currentTime = chrono::timestamp();
|
||||||
if(currentTime - autoSaveTime >= settings.emulator.autoSaveMemory.interval) {
|
if(currentTime - autoSaveTime >= settings.emulator.autoSaveMemory.interval) {
|
||||||
|
@@ -200,6 +200,9 @@ public:
|
|||||||
Rewind = 1 << 4,
|
Rewind = 1 << 4,
|
||||||
};};
|
};};
|
||||||
uint mute = 0;
|
uint mute = 0;
|
||||||
|
|
||||||
|
bool fastForwarding = false;
|
||||||
|
bool rewinding = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
extern Program program;
|
extern Program program;
|
||||||
|
@@ -35,7 +35,7 @@ auto Program::rewindRun() -> void {
|
|||||||
if(rewind.history.size() >= rewind.length) {
|
if(rewind.history.size() >= rewind.length) {
|
||||||
rewind.history.takeFirst();
|
rewind.history.takeFirst();
|
||||||
}
|
}
|
||||||
auto s = emulator->serialize(settings.emulator.serialization.synchronize);
|
auto s = emulator->serialize(0);
|
||||||
rewind.history.append(s);
|
rewind.history.append(s);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@@ -2,8 +2,30 @@ auto EnhancementSettings::create() -> void {
|
|||||||
setCollapsible();
|
setCollapsible();
|
||||||
setVisible(false);
|
setVisible(false);
|
||||||
|
|
||||||
overclockingLabel.setText("Overclocking").setFont(Font().setBold());
|
runAheadLabel.setText("Run-Ahead").setFont(Font().setBold());
|
||||||
|
runAhead0.setText("Disabled").onActivate([&] {
|
||||||
|
settings.emulator.runAhead.frames = 0;
|
||||||
|
});
|
||||||
|
runAhead1.setText("One Frame").onActivate([&] {
|
||||||
|
settings.emulator.runAhead.frames = 1;
|
||||||
|
});
|
||||||
|
runAhead2.setText("Two Frames").onActivate([&] {
|
||||||
|
settings.emulator.runAhead.frames = 2;
|
||||||
|
});
|
||||||
|
runAhead3.setText("Three Frames").onActivate([&] {
|
||||||
|
settings.emulator.runAhead.frames = 3;
|
||||||
|
});
|
||||||
|
runAhead4.setText("Four Frames").onActivate([&] {
|
||||||
|
settings.emulator.runAhead.frames = 4;
|
||||||
|
});
|
||||||
|
if(settings.emulator.runAhead.frames == 0) runAhead0.setChecked();
|
||||||
|
if(settings.emulator.runAhead.frames == 1) runAhead1.setChecked();
|
||||||
|
if(settings.emulator.runAhead.frames == 2) runAhead2.setChecked();
|
||||||
|
if(settings.emulator.runAhead.frames == 3) runAhead3.setChecked();
|
||||||
|
if(settings.emulator.runAhead.frames == 4) runAhead4.setChecked();
|
||||||
|
runAheadSpacer.setColor({192, 192, 192});
|
||||||
|
|
||||||
|
overclockingLabel.setText("Overclocking").setFont(Font().setBold());
|
||||||
overclockingLayout.setSize({3, 3});
|
overclockingLayout.setSize({3, 3});
|
||||||
overclockingLayout.column(0).setAlignment(1.0);
|
overclockingLayout.column(0).setAlignment(1.0);
|
||||||
overclockingLayout.column(1).setAlignment(0.5);
|
overclockingLayout.column(1).setAlignment(0.5);
|
||||||
|
@@ -115,7 +115,7 @@ auto Settings::process(bool load) -> void {
|
|||||||
bind(boolean, "Emulator/AutoSaveStateOnUnload", emulator.autoSaveStateOnUnload);
|
bind(boolean, "Emulator/AutoSaveStateOnUnload", emulator.autoSaveStateOnUnload);
|
||||||
bind(boolean, "Emulator/AutoLoadStateOnLoad", emulator.autoLoadStateOnLoad);
|
bind(boolean, "Emulator/AutoLoadStateOnLoad", emulator.autoLoadStateOnLoad);
|
||||||
bind(text, "Emulator/Serialization/Method", emulator.serialization.method);
|
bind(text, "Emulator/Serialization/Method", emulator.serialization.method);
|
||||||
bind(boolean, "Emulator/Serialization/Synchronize", emulator.serialization.synchronize);
|
bind(natural, "Emulator/RunAhead/Frames", emulator.runAhead.frames);
|
||||||
bind(boolean, "Emulator/Hack/Hotfixes", emulator.hack.hotfixes);
|
bind(boolean, "Emulator/Hack/Hotfixes", emulator.hack.hotfixes);
|
||||||
bind(text, "Emulator/Hack/Entropy", emulator.hack.entropy);
|
bind(text, "Emulator/Hack/Entropy", emulator.hack.entropy);
|
||||||
bind(natural, "Emulator/Hack/CPU/Overclock", emulator.hack.cpu.overclock);
|
bind(natural, "Emulator/Hack/CPU/Overclock", emulator.hack.cpu.overclock);
|
||||||
|
@@ -96,8 +96,10 @@ struct Settings : Markup::Node {
|
|||||||
bool autoLoadStateOnLoad = false;
|
bool autoLoadStateOnLoad = false;
|
||||||
struct Serialization {
|
struct Serialization {
|
||||||
string method = "Fast";
|
string method = "Fast";
|
||||||
bool synchronize = true;
|
|
||||||
} serialization;
|
} serialization;
|
||||||
|
struct RunAhead {
|
||||||
|
uint frames = 0;
|
||||||
|
} runAhead;
|
||||||
struct Hack {
|
struct Hack {
|
||||||
bool hotfixes = true;
|
bool hotfixes = true;
|
||||||
string entropy = "Low";
|
string entropy = "Low";
|
||||||
@@ -330,6 +332,16 @@ struct EnhancementSettings : VerticalLayout {
|
|||||||
auto create() -> void;
|
auto create() -> void;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
Label runAheadLabel{this, Size{~0, 0}, 2};
|
||||||
|
HorizontalLayout runAheadLayout{this, Size{~0, 0}};
|
||||||
|
RadioLabel runAhead0{&runAheadLayout, Size{0, 0}};
|
||||||
|
RadioLabel runAhead1{&runAheadLayout, Size{0, 0}};
|
||||||
|
RadioLabel runAhead2{&runAheadLayout, Size{0, 0}};
|
||||||
|
RadioLabel runAhead3{&runAheadLayout, Size{0, 0}};
|
||||||
|
RadioLabel runAhead4{&runAheadLayout, Size{0, 0}};
|
||||||
|
Group runAheadGroup{&runAhead0, &runAhead1, &runAhead2, &runAhead3, &runAhead4};
|
||||||
|
Canvas runAheadSpacer{this, Size{~0, 1}};
|
||||||
|
//
|
||||||
Label overclockingLabel{this, Size{~0, 0}, 2};
|
Label overclockingLabel{this, Size{~0, 0}, 2};
|
||||||
TableLayout overclockingLayout{this, Size{~0, 0}};
|
TableLayout overclockingLayout{this, Size{~0, 0}};
|
||||||
Label cpuLabel{&overclockingLayout, Size{0, 0}};
|
Label cpuLabel{&overclockingLayout, Size{0, 0}};
|
||||||
|
@@ -1,6 +1,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <nall/queue.hpp>
|
#include <nall/queue.hpp>
|
||||||
|
#include <nall/serializer.hpp>
|
||||||
#include <nall/dsp/dsp.hpp>
|
#include <nall/dsp/dsp.hpp>
|
||||||
|
|
||||||
namespace nall::DSP::Resampler {
|
namespace nall::DSP::Resampler {
|
||||||
@@ -11,6 +12,7 @@ struct Cubic {
|
|||||||
inline auto pending() const -> uint;
|
inline auto pending() const -> uint;
|
||||||
inline auto read() -> double;
|
inline auto read() -> double;
|
||||||
inline auto write(double sample) -> void;
|
inline auto write(double sample) -> void;
|
||||||
|
inline auto serialize(serializer&) -> void;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
double inputFrequency;
|
double inputFrequency;
|
||||||
@@ -67,4 +69,13 @@ auto Cubic::write(double sample) -> void {
|
|||||||
mu -= 1.0;
|
mu -= 1.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto Cubic::serialize(serializer& s) -> void {
|
||||||
|
s.real(inputFrequency);
|
||||||
|
s.real(outputFrequency);
|
||||||
|
s.real(ratio);
|
||||||
|
s.real(fraction);
|
||||||
|
s.array(history);
|
||||||
|
samples.serialize(s);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -51,6 +51,15 @@ struct serializer {
|
|||||||
return _capacity;
|
return _capacity;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto setMode(Mode mode) -> bool {
|
||||||
|
if(_mode == Mode::Save && mode == Mode::Load) {
|
||||||
|
_mode = mode;
|
||||||
|
_size = 0;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
template<typename T> auto real(T& value) -> serializer& {
|
template<typename T> auto real(T& value) -> serializer& {
|
||||||
enum : uint { size = sizeof(T) };
|
enum : uint { size = sizeof(T) };
|
||||||
//this is rather dangerous, and not cross-platform safe;
|
//this is rather dangerous, and not cross-platform safe;
|
||||||
|
Reference in New Issue
Block a user