using skip frames properly (no audio)

This commit is contained in:
Sergio Martin 2024-01-14 06:12:32 +01:00
parent 040d7f3939
commit 57c43b6d98
7 changed files with 61 additions and 63 deletions

View File

@ -8,7 +8,13 @@ quickerNES is an attempt to modernizing and improving the performance of quickNE
- Add support for more mappers, controllers, and features supported by other emulators
- Improve accuracy, if possible
The main aim is to improve the exploration performance of my TASing bot, [JaffarPlus](https://github.com/SergioMartin86/jaffarPlus). However, if this work might help with homebrew emulation and other people having more fun, then much better!
The main aim is to improve the performance of skip (non-rendering, no-audio) frame advances for brute force botting. (See: [JaffarPlus](https://github.com/SergioMartin86/jaffarPlus)). However, if this work might help with homebrew emulation and other people having more fun, then much better!
Changes
=========
- Optimizations made in the CPU emulation core
- Sound is no longer emulated during skip frames
Credits
=========

View File

@ -38,7 +38,6 @@ Nes_Core::Nes_Core() : ppu( this )
mapper = NULL;
memset( &nes, 0, sizeof nes );
memset( &joypad, 0, sizeof joypad );
_doRendering = true;
}
const char * Nes_Core::init()
@ -498,8 +497,11 @@ nes_time_t Nes_Core::emulate_frame_()
}
}
nes_time_t Nes_Core::emulate_frame()
nes_time_t Nes_Core::emulate_frame(int joypad1, int joypad2)
{
current_joypad [0] = (joypad1 |= ~0xFF);
current_joypad [1] = (joypad2 |= ~0xFF);
joypad_read_count = 0;
cpu_time_offset = ppu.begin_frame( nes.timestamp ) - 1;
@ -508,10 +510,10 @@ nes_time_t Nes_Core::emulate_frame()
// TODO: clean this fucking mess up
auto t0 = emulate_frame_();
if (_doRendering == true) impl->apu.run_until_( t0 );
impl->apu.run_until_( t0 );
clock_ = cpu_time_offset;
auto t1 = cpu_time();
if (_doRendering == true) impl->apu.run_until_( t1 );
impl->apu.run_until_( t1 );
nes_time_t ppu_frame_length = ppu.frame_length();
nes_time_t length = cpu_time();

View File

@ -20,13 +20,10 @@ public:
Nes_Core();
~Nes_Core();
// Flag to enable/disable rendering
bool _doRendering;
const char * init();
const char * open( Nes_Cart const* );
void reset( bool full_reset = true, bool erase_battery_ram = false );
blip_time_t emulate_frame();
blip_time_t emulate_frame(int joypad1, int joypad2);
void close();
void save_state( Nes_State* ) const;

View File

@ -128,70 +128,59 @@ const char * Nes_Emu::emulate_skip_frame( int joypad1, int joypad2 )
{
char *old_host_pixels = host_pixels;
host_pixels = NULL;
const char *result = emulate_frame(joypad1, joypad2);
emu.emulate_frame(joypad1, joypad2);
host_pixels = old_host_pixels;
return result;
return 0;
}
const char * Nes_Emu::emulate_frame( int joypad1, int joypad2 )
{
emu.current_joypad [0] = (joypad1 |= ~0xFF);
emu.current_joypad [1] = (joypad2 |= ~0xFF);
emu.ppu.host_pixels = NULL;
if (emu._doRendering == true)
unsigned changed_count = sound_buf->channels_changed_count();
bool new_enabled = (frame_ != NULL);
if ( sound_buf_changed_count != changed_count || sound_enabled != new_enabled )
{
unsigned changed_count = sound_buf->channels_changed_count();
bool new_enabled = (frame_ != NULL);
if ( sound_buf_changed_count != changed_count || sound_enabled != new_enabled )
{
sound_buf_changed_count = changed_count;
sound_enabled = new_enabled;
enable_sound( sound_enabled );
}
sound_buf_changed_count = changed_count;
sound_enabled = new_enabled;
enable_sound( sound_enabled );
}
frame_t* f = frame_;
if ( f )
{
emu.ppu.max_palette_size = host_palette_size;
emu.ppu.host_palette = f->palette + emu.ppu.palette_begin;
// add black and white for emulator to use (unless emulator uses entire
// palette for frame)
f->palette [252] = 0x0F;
f->palette [254] = 0x30;
f->palette [255] = 0x0F;
if ( host_pixels )
emu.ppu.host_pixels = (uint8_t*) host_pixels +
emu.ppu.host_row_bytes * f->top;
frame_t* f = frame_;
if ( f )
{
emu.ppu.max_palette_size = host_palette_size;
emu.ppu.host_palette = f->palette + emu.ppu.palette_begin;
// add black and white for emulator to use (unless emulator uses entire
// palette for frame)
f->palette [252] = 0x0F;
f->palette [254] = 0x30;
f->palette [255] = 0x0F;
if ( host_pixels )
emu.ppu.host_pixels = (uint8_t*) host_pixels +
emu.ppu.host_row_bytes * f->top;
if ( sound_buf->samples_avail() )
clear_sound_buf();
if ( sound_buf->samples_avail() )
clear_sound_buf();
nes_time_t frame_len = emu.emulate_frame();
sound_buf->end_frame( frame_len, false );
nes_time_t frame_len = emu.emulate_frame(joypad1, joypad2);
sound_buf->end_frame( frame_len, false );
f = frame_;
f->sample_count = sound_buf->samples_avail();
f->chan_count = sound_buf->samples_per_frame();
f->palette_begin = emu.ppu.palette_begin;
f->palette_size = emu.ppu.palette_size;
f->joypad_read_count = emu.joypad_read_count;
f->burst_phase = emu.ppu.burst_phase;
f->pitch = emu.ppu.host_row_bytes;
f->pixels = emu.ppu.host_pixels + f->left;
}
else
{
emu.ppu.max_palette_size = 0;
emu.emulate_frame();
}
f = frame_;
f->sample_count = sound_buf->samples_avail();
f->chan_count = sound_buf->samples_per_frame();
f->palette_begin = emu.ppu.palette_begin;
f->palette_size = emu.ppu.palette_size;
f->joypad_read_count = emu.joypad_read_count;
f->burst_phase = emu.ppu.burst_phase;
f->pitch = emu.ppu.host_row_bytes;
f->pixels = emu.ppu.host_pixels + f->left;
}
else
{
emu.ppu.max_palette_size = 0;
emu.emulate_frame();
emu.emulate_frame(joypad1, joypad2);
}
return 0;

View File

@ -40,11 +40,8 @@ public:
static const uint16_t image_width = 256;
static const uint16_t image_height = 240;
const uint8_t* getHostPixels () const { return emu.ppu.host_pixels; }
const uint8_t* getHostPixels () const { return emu.ppu.host_pixels; }
void enableRendering() { emu._doRendering = true; }
void disableRendering() { emu._doRendering = false; }
// Basic emulation
// Emulate one video frame using joypad1 and joypad2 as input. Afterwards, image

View File

@ -171,6 +171,9 @@ class EmuInstance
return moveString;
}
void enableRendering() { _doRendering = true; }
void disableRendering() { _doRendering = false; }
void advanceState(const std::string& move)
{
if (move.find("r") != std::string::npos) _nes->reset(false);
@ -180,13 +183,17 @@ class EmuInstance
void advanceState(const inputType controller1, const inputType controller2)
{
_nes->emulate_frame(controller1, controller2);
if (_doRendering == true) _nes->emulate_frame(controller1, controller2);
if (_doRendering == false) _nes->emulate_skip_frame(controller1, controller2);
}
Nes_Emu* getInternalEmulator() const { return _nes; }
private:
// Flag to determine whether to enable/disable rendering
bool _doRendering = true;
inline size_t getStateSizeImpl() const
{
// Using dry writer to just obtain the state size

View File

@ -58,7 +58,7 @@ int main(int argc, char *argv[])
auto e = EmuInstance(romFilePath, initialStateFilePath);
// Disable rendering
e.getInternalEmulator()->disableRendering();
e.disableRendering();
// Getting initial hash
auto initialHash = e.getStateHash();