From 3c45603cb4b8e6253bf7858e0901444fda974a9b Mon Sep 17 00:00:00 2001 From: Connor McLaughlin Date: Thu, 9 Apr 2020 00:14:26 +1000 Subject: [PATCH] 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. --- src/core/memory_card.cpp | 51 ++++++++++++++++++++++++++++++---------- src/core/memory_card.h | 12 +++++++++- 2 files changed, 50 insertions(+), 13 deletions(-) diff --git a/src/core/memory_card.cpp b/src/core/memory_card.cpp index a92915a9f..86939eec6 100644 --- a/src/core/memory_card.cpp +++ b/src/core/memory_card.cpp @@ -11,17 +11,28 @@ Log_SetChannel(MemoryCard); MemoryCard::MemoryCard(System* system) : m_system(system) { 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() { ResetTransferState(); + SaveIfChanged(true); } bool MemoryCard::DoState(StateWrapper& sw) { + if (sw.IsReading()) + SaveIfChanged(true); + sw.Do(&m_state); sw.Do(&m_address); sw.Do(&m_sector_offset); @@ -40,7 +51,6 @@ void MemoryCard::ResetTransferState() m_sector_offset = 0; m_checksum = 0; m_last_byte = 0; - m_changed = false; } 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) { - 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_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; - if (m_data[offset] != data_in) - m_changed = true; - + m_changed |= (m_data[offset] != data_in); m_data[offset] = data_in; *data_out = m_last_byte; @@ -155,10 +163,7 @@ bool MemoryCard::Transfer(const u8 data_in, u8* data_out) m_state = State::WriteChecksum; m_sector_offset = 0; if (m_changed) - { - m_changed = false; - SaveToFile(); - } + QueueFileSave(); } } break; @@ -317,6 +322,8 @@ void MemoryCard::Format() // write test frame std::memcpy(GetSectorPtr(63), GetSectorPtr(0), SECTOR_SIZE); + + m_changed = true; } u8* MemoryCard::GetSectorPtr(u32 sector) @@ -342,8 +349,15 @@ bool MemoryCard::LoadFromFile() 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()) return false; @@ -364,6 +378,19 @@ bool MemoryCard::SaveToFile() } 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; } + +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); +} diff --git a/src/core/memory_card.h b/src/core/memory_card.h index df8246bcb..3057a663b 100644 --- a/src/core/memory_card.h +++ b/src/core/memory_card.h @@ -7,6 +7,7 @@ #include class System; +class TimingEvent; class MemoryCard final { @@ -33,6 +34,13 @@ public: void Format(); 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 { u8 bits; @@ -76,9 +84,11 @@ private: u8* GetSectorPtr(u32 sector); bool LoadFromFile(); - bool SaveToFile(); + bool SaveIfChanged(bool display_osd_message); + void QueueFileSave(); System* m_system; + std::unique_ptr m_save_event; State m_state = State::Idle; u16 m_address = 0;