Move more logic from frontend to base HostInterface

This commit is contained in:
Connor McLaughlin
2020-02-16 00:14:28 +09:00
parent f0578bb932
commit fd8ed08307
17 changed files with 539 additions and 619 deletions

View File

@ -54,44 +54,87 @@ HostInterface::HostInterface()
m_game_list->SetDatabaseFilename(GetGameListDatabaseFileName());
}
HostInterface::~HostInterface() = default;
bool HostInterface::CreateSystem()
HostInterface::~HostInterface()
{
m_system = System::Create(this);
// system should be shut down prior to the destructor
Assert(!m_system && !m_audio_stream && !m_display);
}
// Pull in any invalid settings which have been reset.
m_settings = m_system->GetSettings();
m_paused = true;
bool HostInterface::BootSystemFromFile(const char* filename)
{
if (!AcquireHostDisplay())
{
ReportFormattedError("Failed to acquire host display");
return false;
}
// set host display settings
m_display->SetDisplayLinearFiltering(m_settings.display_linear_filtering);
// create the audio stream. this will never fail, since we'll just fall back to null
m_audio_stream = CreateAudioStream(m_settings.audio_backend);
if (!m_audio_stream || !m_audio_stream->Reconfigure(AUDIO_SAMPLE_RATE, AUDIO_CHANNELS, AUDIO_BUFFER_SIZE, 4))
{
ReportFormattedError("Failed to create or configure audio stream, falling back to null output.");
m_audio_stream.reset();
m_audio_stream = AudioStream::CreateNullAudioStream();
m_audio_stream->Reconfigure(AUDIO_SAMPLE_RATE, AUDIO_CHANNELS, AUDIO_BUFFER_SIZE, 4);
}
m_system = System::Create(this);
if (!m_system->Boot(filename))
{
ReportFormattedError("System failed to boot. The log may contain more information.");
DestroySystem();
return false;
}
OnSystemCreated();
m_paused = m_settings.start_paused;
m_audio_stream->PauseOutput(m_paused);
UpdateSpeedLimiterState();
if (m_paused)
OnSystemPaused(true);
return true;
}
bool HostInterface::BootSystem(const char* filename, const char* state_filename)
bool HostInterface::BootSystemFromBIOS()
{
if (!m_system->Boot(filename))
return false;
return BootSystemFromFile(nullptr);
}
m_paused = m_settings.start_paused;
void HostInterface::PauseSystem(bool paused)
{
if (paused == m_paused)
return;
m_paused = paused;
m_audio_stream->PauseOutput(m_paused);
OnSystemPaused(paused);
UpdateSpeedLimiterState();
if (state_filename && !LoadState(state_filename))
return false;
return true;
}
void HostInterface::ResetSystem()
{
m_system->Reset();
m_system->ResetPerformanceCounters();
AddOSDMessage("System reset.");
}
void HostInterface::DestroySystem()
{
if (!m_system)
return;
m_system.reset();
m_paused = false;
UpdateSpeedLimiterState();
m_audio_stream.reset();
ReleaseHostDisplay();
OnSystemDestroyed();
OnRunningGameChanged();
}
void HostInterface::ReportError(const char* message)
@ -283,11 +326,6 @@ void HostInterface::DrawDebugWindows()
m_system->GetMDEC()->DrawDebugStateWindow();
}
void HostInterface::ClearImGuiFocus()
{
ImGui::SetWindowFocus(nullptr);
}
std::optional<std::vector<u8>> HostInterface::GetBIOSImage(ConsoleRegion region)
{
// Try the other default filenames in the directory of the configured BIOS.
@ -349,28 +387,48 @@ bool HostInterface::LoadState(const char* filename)
if (!stream)
return false;
AddFormattedOSDMessage(2.0f, "Loading state from %s...", filename);
AddFormattedOSDMessage(2.0f, "Loading state from '%s'...", filename);
const bool result = m_system->LoadState(stream.get());
if (!result)
if (m_system)
{
ReportFormattedError("Loading state from %s failed. Resetting.", filename);
m_system->Reset();
if (!m_system->LoadState(stream.get()))
{
ReportFormattedError("Loading state from '%s' failed. Resetting.", filename);
m_system->Reset();
return false;
}
m_system->ResetPerformanceCounters();
}
else
{
if (!BootSystemFromFile(nullptr))
{
ReportFormattedError("Failed to boot system to load state from '%s'.", filename);
return false;
}
if (!m_system->LoadState(stream.get()))
{
ReportFormattedError("Failed to load state. The log may contain more information. Shutting down system.");
DestroySystem();
return false;
}
}
return result;
return true;
}
bool HostInterface::LoadState(bool global, u32 slot)
{
const std::string& code = m_system->GetRunningCode();
if (!global && code.empty())
if (!global && (!m_system || m_system->GetRunningCode().empty()))
{
ReportFormattedError("Can't save per-game state without a running game code.");
return false;
}
std::string save_path = global ? GetGlobalSaveStateFileName(slot) : GetGameSaveStateFileName(code.c_str(), slot);
std::string save_path =
global ? GetGlobalSaveStateFileName(slot) : GetGameSaveStateFileName(m_system->GetRunningCode().c_str(), slot);
return LoadState(save_path.c_str());
}
@ -385,12 +443,12 @@ bool HostInterface::SaveState(const char* filename)
const bool result = m_system->SaveState(stream.get());
if (!result)
{
ReportFormattedError("Saving state to %s failed.", filename);
ReportFormattedError("Saving state to '%s' failed.", filename);
stream->Discard();
}
else
{
AddFormattedOSDMessage(2.0f, "State saved to %s.", filename);
AddFormattedOSDMessage(2.0f, "State saved to '%s'.", filename);
stream->Commit();
}
@ -431,7 +489,17 @@ void HostInterface::UpdateSpeedLimiterState()
m_system->ResetPerformanceCounters();
}
void HostInterface::SwitchGPURenderer() {}
void HostInterface::OnSystemCreated() {}
void HostInterface::OnSystemPaused(bool paused)
{
ReportFormattedMessage("System %s.", paused ? "paused" : "resumed");
}
void HostInterface::OnSystemDestroyed()
{
ReportFormattedMessage("System shut down.");
}
void HostInterface::OnSystemPerformanceCountersUpdated() {}
@ -644,16 +712,16 @@ void HostInterface::UpdateSettings(const std::function<void()>& apply_callback)
apply_callback();
if (m_settings.gpu_renderer != old_gpu_renderer)
SwitchGPURenderer();
if (m_settings.video_sync_enabled != old_vsync_enabled || m_settings.audio_sync_enabled != old_audio_sync_enabled ||
m_settings.speed_limiter_enabled != old_speed_limiter_enabled)
{
UpdateSpeedLimiterState();
}
RecreateSystem();
if (m_system)
{
if (m_settings.video_sync_enabled != old_vsync_enabled || m_settings.audio_sync_enabled != old_audio_sync_enabled ||
m_settings.speed_limiter_enabled != old_speed_limiter_enabled)
{
UpdateSpeedLimiterState();
}
if (m_settings.emulation_speed != old_emulation_speed)
{
m_system->UpdateThrottlePeriod();
@ -672,7 +740,7 @@ void HostInterface::UpdateSettings(const std::function<void()>& apply_callback)
}
}
if (m_settings.display_linear_filtering != old_display_linear_filtering)
if (m_display && m_settings.display_linear_filtering != old_display_linear_filtering)
m_display->SetDisplayLinearFiltering(m_settings.display_linear_filtering);
}
@ -704,3 +772,30 @@ void HostInterface::ModifyResolutionScale(s32 increment)
GPU::VRAM_WIDTH * m_settings.gpu_resolution_scale,
GPU::VRAM_HEIGHT * m_settings.gpu_resolution_scale);
}
void HostInterface::RecreateSystem()
{
std::unique_ptr<ByteStream> stream = ByteStream_CreateGrowableMemoryStream(nullptr, 8 * 1024);
if (!m_system->SaveState(stream.get()) || !stream->SeekAbsolute(0))
{
ReportError("Failed to save state before system recreation. Shutting down.");
DestroySystem();
return;
}
DestroySystem();
if (!BootSystemFromFile(nullptr))
{
ReportError("Failed to boot system after recreation.");
return;
}
if (!m_system->LoadState(stream.get()))
{
ReportError("Failed to load state after system recreation. Shutting down.");
DestroySystem();
return;
}
m_system->ResetPerformanceCounters();
}

View File

@ -12,6 +12,7 @@
#include <vector>
class AudioStream;
class ByteStream;
class CDImage;
class HostDisplay;
class GameList;
@ -27,22 +28,31 @@ public:
virtual ~HostInterface();
/// Access to host display.
ALWAYS_INLINE HostDisplay* GetDisplay() const { return m_display.get(); }
ALWAYS_INLINE HostDisplay* GetDisplay() const { return m_display; }
/// Access to host audio stream.
AudioStream* GetAudioStream() const { return m_audio_stream.get(); }
ALWAYS_INLINE AudioStream* GetAudioStream() const { return m_audio_stream.get(); }
/// Returns a settings object which can be modified.
Settings& GetSettings() { return m_settings; }
ALWAYS_INLINE Settings& GetSettings() { return m_settings; }
/// Returns the game list.
const GameList* GetGameList() const { return m_game_list.get(); }
ALWAYS_INLINE const GameList* GetGameList() const { return m_game_list.get(); }
bool CreateSystem();
bool BootSystem(const char* filename, const char* state_filename);
bool BootSystemFromFile(const char* filename);
bool BootSystemFromBIOS();
void PauseSystem(bool paused);
void ResetSystem();
void DestroySystem();
/// Loads the current emulation state from file. Specifying a slot of -1 loads the "resume" game state.
bool LoadState(bool global, u32 slot);
bool LoadState(const char* filename);
/// Saves the current emulation state to a file. Specifying a slot of -1 saves the "resume" save state.
bool SaveState(bool global, u32 slot);
bool SaveState(const char* filename);
virtual void ReportError(const char* message);
virtual void ReportMessage(const char* message);
@ -53,12 +63,8 @@ public:
void AddOSDMessage(const char* message, float duration = 2.0f);
void AddFormattedOSDMessage(float duration, const char* format, ...);
/// Loads the BIOS image for the specified region.
virtual std::optional<std::vector<u8>> GetBIOSImage(ConsoleRegion region);
/// Returns the base user directory path.
const std::string& GetUserDirectory() const { return m_user_directory; }
ALWAYS_INLINE const std::string& GetUserDirectory() const { return m_user_directory; }
/// Returns a path relative to the user directory.
std::string GetUserDirectoryRelativePath(const char* format, ...) const;
@ -89,7 +95,13 @@ protected:
bool global;
};
virtual void SwitchGPURenderer();
virtual bool AcquireHostDisplay() = 0;
virtual void ReleaseHostDisplay() = 0;
virtual std::unique_ptr<AudioStream> CreateAudioStream(AudioBackend backend) = 0;
virtual void OnSystemCreated();
virtual void OnSystemPaused(bool paused);
virtual void OnSystemDestroyed();
virtual void OnSystemPerformanceCountersUpdated();
virtual void OnRunningGameChanged();
@ -122,13 +134,8 @@ protected:
/// Returns a list of save states for the specified game code.
std::vector<SaveStateInfo> GetAvailableSaveStates(const char* game_code) const;
/// Loads the current emulation state from file. Specifying a slot of -1 loads the "resume" game state.
bool LoadState(bool global, u32 slot);
bool LoadState(const char* filename);
/// Saves the current emulation state to a file. Specifying a slot of -1 saves the "resume" save state.
bool SaveState(bool global, u32 slot);
bool SaveState(const char* filename);
/// Loads the BIOS image for the specified region.
std::optional<std::vector<u8>> GetBIOSImage(ConsoleRegion region);
/// Restores all settings to defaults.
void SetDefaultSettings();
@ -143,14 +150,16 @@ protected:
/// Adjusts the internal (render) resolution of the hardware backends.
void ModifyResolutionScale(s32 increment);
/// Switches the GPU renderer by saving state, recreating the display window, and restoring state (if needed).
void RecreateSystem();
void UpdateSpeedLimiterState();
void DrawFPSWindow();
void DrawOSDMessages();
void DrawDebugWindows();
void ClearImGuiFocus();
std::unique_ptr<HostDisplay> m_display;
HostDisplay* m_display = nullptr;
std::unique_ptr<AudioStream> m_audio_stream;
std::unique_ptr<System> m_system;
std::unique_ptr<GameList> m_game_list;