MemoryCard: Batch sector writes together 5 seconds at a time

Reduces disk writes for SSDs (assuming the OS didn't just cache all the
writes), and limits OSD spam.

Fixes #146.
This commit is contained in:
Connor McLaughlin 2020-04-09 00:14:26 +10:00
parent 0a004361fc
commit 3c45603cb4
2 changed files with 50 additions and 13 deletions

View file

@ -11,17 +11,28 @@ Log_SetChannel(MemoryCard);
MemoryCard::MemoryCard(System* system) : m_system(system) MemoryCard::MemoryCard(System* system) : m_system(system)
{ {
m_FLAG.no_write_yet = true; m_FLAG.no_write_yet = true;
m_save_event =
system->CreateTimingEvent("Memory Card Host Flush", SAVE_DELAY_IN_SYSCLK_TICKS, SAVE_DELAY_IN_SYSCLK_TICKS,
std::bind(&MemoryCard::SaveIfChanged, this, true), false);
} }
MemoryCard::~MemoryCard() = default; MemoryCard::~MemoryCard()
{
SaveIfChanged(false);
}
void MemoryCard::Reset() void MemoryCard::Reset()
{ {
ResetTransferState(); ResetTransferState();
SaveIfChanged(true);
} }
bool MemoryCard::DoState(StateWrapper& sw) bool MemoryCard::DoState(StateWrapper& sw)
{ {
if (sw.IsReading())
SaveIfChanged(true);
sw.Do(&m_state); sw.Do(&m_state);
sw.Do(&m_address); sw.Do(&m_address);
sw.Do(&m_sector_offset); sw.Do(&m_sector_offset);
@ -40,7 +51,6 @@ void MemoryCard::ResetTransferState()
m_sector_offset = 0; m_sector_offset = 0;
m_checksum = 0; m_checksum = 0;
m_last_byte = 0; m_last_byte = 0;
m_changed = false;
} }
bool MemoryCard::Transfer(const u8 data_in, u8* data_out) bool MemoryCard::Transfer(const u8 data_in, u8* data_out)
@ -131,7 +141,7 @@ bool MemoryCard::Transfer(const u8 data_in, u8* data_out)
{ {
if (m_sector_offset == 0) if (m_sector_offset == 0)
{ {
Log_DevPrintf("Writing memory card sector %u", ZeroExtend32(m_address)); Log_InfoPrintf("Writing memory card sector %u", ZeroExtend32(m_address));
m_checksum = Truncate8(m_address >> 8) ^ Truncate8(m_address) ^ data_in; m_checksum = Truncate8(m_address >> 8) ^ Truncate8(m_address) ^ data_in;
m_FLAG.no_write_yet = false; m_FLAG.no_write_yet = false;
} }
@ -141,9 +151,7 @@ bool MemoryCard::Transfer(const u8 data_in, u8* data_out)
} }
const u32 offset = ZeroExtend32(m_address) * SECTOR_SIZE + m_sector_offset; const u32 offset = ZeroExtend32(m_address) * SECTOR_SIZE + m_sector_offset;
if (m_data[offset] != data_in) m_changed |= (m_data[offset] != data_in);
m_changed = true;
m_data[offset] = data_in; m_data[offset] = data_in;
*data_out = m_last_byte; *data_out = m_last_byte;
@ -155,10 +163,7 @@ bool MemoryCard::Transfer(const u8 data_in, u8* data_out)
m_state = State::WriteChecksum; m_state = State::WriteChecksum;
m_sector_offset = 0; m_sector_offset = 0;
if (m_changed) if (m_changed)
{ QueueFileSave();
m_changed = false;
SaveToFile();
}
} }
} }
break; break;
@ -317,6 +322,8 @@ void MemoryCard::Format()
// write test frame // write test frame
std::memcpy(GetSectorPtr(63), GetSectorPtr(0), SECTOR_SIZE); std::memcpy(GetSectorPtr(63), GetSectorPtr(0), SECTOR_SIZE);
m_changed = true;
} }
u8* MemoryCard::GetSectorPtr(u32 sector) u8* MemoryCard::GetSectorPtr(u32 sector)
@ -342,8 +349,15 @@ bool MemoryCard::LoadFromFile()
return true; return true;
} }
bool MemoryCard::SaveToFile() bool MemoryCard::SaveIfChanged(bool display_osd_message)
{ {
m_save_event->Deactivate();
if (!m_changed)
return true;
m_changed = false;
if (m_filename.empty()) if (m_filename.empty())
return false; return false;
@ -364,6 +378,19 @@ bool MemoryCard::SaveToFile()
} }
Log_InfoPrintf("Saved memory card to '%s'", m_filename.c_str()); Log_InfoPrintf("Saved memory card to '%s'", m_filename.c_str());
m_system->GetHostInterface()->AddOSDMessage(SmallString::FromFormat("Saved memory card to '%s'", m_filename.c_str())); if (display_osd_message)
m_system->GetHostInterface()->AddOSDMessage(
SmallString::FromFormat("Saved memory card to '%s'", m_filename.c_str()));
return true; return true;
} }
void MemoryCard::QueueFileSave()
{
// skip if the event is already pending, or we don't have a backing file
if (m_save_event->IsActive() || m_filename.empty())
return;
// save in one second, that should be long enough for everything to finish writing
m_save_event->Schedule(SAVE_DELAY_IN_SYSCLK_TICKS);
}

View file

@ -7,6 +7,7 @@
#include <string_view> #include <string_view>
class System; class System;
class TimingEvent;
class MemoryCard final class MemoryCard final
{ {
@ -33,6 +34,13 @@ public:
void Format(); void Format();
private: private:
enum : u32
{
// save in three seconds, that should be long enough for everything to finish writing
SAVE_DELAY_IN_SECONDS = 5,
SAVE_DELAY_IN_SYSCLK_TICKS = MASTER_CLOCK * SAVE_DELAY_IN_SECONDS,
};
union FLAG union FLAG
{ {
u8 bits; u8 bits;
@ -76,9 +84,11 @@ private:
u8* GetSectorPtr(u32 sector); u8* GetSectorPtr(u32 sector);
bool LoadFromFile(); bool LoadFromFile();
bool SaveToFile(); bool SaveIfChanged(bool display_osd_message);
void QueueFileSave();
System* m_system; System* m_system;
std::unique_ptr<TimingEvent> m_save_event;
State m_state = State::Idle; State m_state = State::Idle;
u16 m_address = 0; u16 m_address = 0;