From 55e73bb4b9659797f9598b9dfb93fc87b52e8dea Mon Sep 17 00:00:00 2001 From: Stenzek Date: Sat, 24 Jun 2023 17:46:36 +1000 Subject: [PATCH] MTGS: Convert to namespace --- common/CMakeLists.txt | 1 + common/WrappedMemCopy.h | 50 +++ common/common.vcxproj | 1 + common/common.vcxproj.filters | 3 + pcsx2-gsrunner/Main.cpp | 16 +- pcsx2-qt/MainWindow.cpp | 5 +- pcsx2-qt/QtHost.cpp | 56 ++- pcsx2/Achievements.cpp | 16 +- pcsx2/CMakeLists.txt | 1 + pcsx2/Counters.cpp | 8 +- pcsx2/FiFo.cpp | 5 +- pcsx2/GS.cpp | 18 +- pcsx2/GS.h | 230 +--------- pcsx2/GS/GS.cpp | 29 +- pcsx2/GSDumpReplayer.cpp | 8 +- pcsx2/Gif_Unit.cpp | 38 +- pcsx2/Gif_Unit.h | 3 +- pcsx2/Hw.cpp | 2 + pcsx2/ImGui/FullscreenUI.cpp | 32 +- pcsx2/ImGui/ImGuiManager.cpp | 12 +- pcsx2/MTGS.cpp | 492 ++++++++++++--------- pcsx2/MTGS.h | 107 +++++ pcsx2/PerformanceMetrics.cpp | 7 +- pcsx2/R5900.cpp | 7 +- pcsx2/Recording/InputRecording.cpp | 7 +- pcsx2/Recording/InputRecordingControls.cpp | 8 +- pcsx2/SPU2/spu2.cpp | 8 +- pcsx2/SaveState.cpp | 9 +- pcsx2/VMManager.cpp | 45 +- pcsx2/Vif1_Dma.cpp | 4 +- tests/ctest/core/StubHost.cpp | 19 +- 31 files changed, 605 insertions(+), 642 deletions(-) create mode 100644 common/WrappedMemCopy.h create mode 100644 pcsx2/MTGS.h diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt index 4629c8e8db..75e4bc3be8 100644 --- a/common/CMakeLists.txt +++ b/common/CMakeLists.txt @@ -100,6 +100,7 @@ target_sources(common PRIVATE TraceLog.h WAVWriter.h WindowInfo.h + WrappedMemCopy.h emitter/implement/dwshift.h emitter/implement/group1.h emitter/implement/group2.h diff --git a/common/WrappedMemCopy.h b/common/WrappedMemCopy.h new file mode 100644 index 0000000000..bb9fb4b557 --- /dev/null +++ b/common/WrappedMemCopy.h @@ -0,0 +1,50 @@ +/* PCSX2 - PS2 Emulator for PCs + * Copyright (C) 2002-2023 PCSX2 Dev Team + * + * PCSX2 is free software: you can redistribute it and/or modify it under the terms + * of the GNU Lesser General Public License as published by the Free Software Found- + * ation, either version 3 of the License, or (at your option) any later version. + * + * PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with PCSX2. + * If not, see . + */ + +#include "common/Pcsx2Defs.h" + +__ri static void MemCopy_WrappedDest(const u128* src, u128* destBase, uint& destStart, uint destSize, uint len) +{ + uint endpos = destStart + len; + if (endpos < destSize) + { + memcpy(&destBase[destStart], src, len * 16); + destStart += len; + } + else + { + uint firstcopylen = destSize - destStart; + memcpy(&destBase[destStart], src, firstcopylen * 16); + destStart = endpos % destSize; + memcpy(destBase, src + firstcopylen, destStart * 16); + } +} + +__ri static void MemCopy_WrappedSrc(const u128* srcBase, uint& srcStart, uint srcSize, u128* dest, uint len) +{ + uint endpos = srcStart + len; + if (endpos < srcSize) + { + memcpy(dest, &srcBase[srcStart], len * 16); + srcStart += len; + } + else + { + uint firstcopylen = srcSize - srcStart; + memcpy(dest, &srcBase[srcStart], firstcopylen * 16); + srcStart = endpos % srcSize; + memcpy(dest + firstcopylen, srcBase, srcStart * 16); + } +} diff --git a/common/common.vcxproj b/common/common.vcxproj index ca7263cc56..9e10e31f5f 100644 --- a/common/common.vcxproj +++ b/common/common.vcxproj @@ -169,6 +169,7 @@ + diff --git a/common/common.vcxproj.filters b/common/common.vcxproj.filters index 09ab4632a4..6389b4ccea 100644 --- a/common/common.vcxproj.filters +++ b/common/common.vcxproj.filters @@ -348,6 +348,9 @@ Header Files + + Header Files + diff --git a/pcsx2-gsrunner/Main.cpp b/pcsx2-gsrunner/Main.cpp index 4ec6945915..e622bf3859 100644 --- a/pcsx2-gsrunner/Main.cpp +++ b/pcsx2-gsrunner/Main.cpp @@ -40,13 +40,13 @@ #include "pcsx2/Achievements.h" #include "pcsx2/CDVD/CDVD.h" #include "pcsx2/GS.h" -#include "pcsx2/GS/GS.h" #include "pcsx2/GSDumpReplayer.h" #include "pcsx2/Host.h" #include "pcsx2/INISettingsInterface.h" #include "pcsx2/ImGui/ImGuiManager.h" #include "pcsx2/Input/InputManager.h" #include "pcsx2/LogSink.h" +#include "pcsx2/MTGS.h" #include "pcsx2/PAD/Host/PAD.h" #include "pcsx2/PerformanceMetrics.h" #include "pcsx2/VMManager.h" @@ -69,7 +69,6 @@ static constexpr u32 WINDOW_WIDTH = 640; static constexpr u32 WINDOW_HEIGHT = 480; static MemorySettingsInterface s_settings_interface; -alignas(16) static SysMtgsThread s_mtgs_thread; static std::string s_output_prefix; static s32 s_loop_count = 1; @@ -362,15 +361,6 @@ std::optional InputManager::ConvertHostKeyboardCodeToString(u32 cod return std::nullopt; } -SysMtgsThread& GetMTGS() -{ - return s_mtgs_thread; -} - -////////////////////////////////////////////////////////////////////////// -// Interface Stuff -////////////////////////////////////////////////////////////////////////// - BEGIN_HOTKEY_LIST(g_host_hotkeys) END_HOTKEY_LIST() @@ -651,8 +641,8 @@ int main(int argc, char* argv[]) void Host::VSyncOnCPUThread() { // update GS thread copy of frame number - GetMTGS().RunOnGSThread([frame_number = GSDumpReplayer::GetFrameNumber()]() { s_dump_frame_number = frame_number; }); - GetMTGS().RunOnGSThread([loop_number = GSDumpReplayer::GetLoopCount()]() { s_loop_number = loop_number; }); + MTGS::RunOnGSThread([frame_number = GSDumpReplayer::GetFrameNumber()]() { s_dump_frame_number = frame_number; }); + MTGS::RunOnGSThread([loop_number = GSDumpReplayer::GetLoopCount()]() { s_loop_number = loop_number; }); // process any window messages (but we shouldn't really have any) GSRunner::PumpPlatformMessages(); diff --git a/pcsx2-qt/MainWindow.cpp b/pcsx2-qt/MainWindow.cpp index 91a529d25a..9f46afa729 100644 --- a/pcsx2-qt/MainWindow.cpp +++ b/pcsx2-qt/MainWindow.cpp @@ -41,6 +41,7 @@ #include "pcsx2/GameList.h" #include "pcsx2/Host.h" #include "pcsx2/LogSink.h" +#include "pcsx2/MTGS.h" #include "pcsx2/PerformanceMetrics.h" #include "pcsx2/Recording/InputRecording.h" #include "pcsx2/Recording/InputRecordingControls.h" @@ -709,7 +710,7 @@ void MainWindow::updateEmulationActions(bool starting, bool running, bool stoppi void MainWindow::updateDisplayRelatedActions(bool has_surface, bool render_to_main, bool fullscreen) { // rendering to main, or switched to gamelist/grid - m_ui.actionViewSystemDisplay->setEnabled((has_surface && render_to_main) || (!has_surface && GetMTGS().IsOpen())); + m_ui.actionViewSystemDisplay->setEnabled((has_surface && render_to_main) || (!has_surface && MTGS::IsOpen())); m_ui.menuWindowSize->setEnabled(has_surface && !fullscreen); m_ui.actionFullscreen->setEnabled(has_surface); @@ -821,7 +822,7 @@ bool MainWindow::isShowingGameList() const bool MainWindow::isRenderingFullscreen() const { - if (!GetMTGS().IsOpen() || !m_display_widget) + if (!MTGS::IsOpen() || !m_display_widget) return false; return getDisplayContainer()->isFullScreen(); diff --git a/pcsx2-qt/QtHost.cpp b/pcsx2-qt/QtHost.cpp index 017639de28..564694c21e 100644 --- a/pcsx2-qt/QtHost.cpp +++ b/pcsx2-qt/QtHost.cpp @@ -39,6 +39,7 @@ #include "pcsx2/ImGui/ImGuiManager.h" #include "pcsx2/Input/InputManager.h" #include "pcsx2/LogSink.h" +#include "pcsx2/MTGS.h" #include "pcsx2/PAD/Host/PAD.h" #include "pcsx2/PerformanceMetrics.h" #include "pcsx2/VMManager.h" @@ -190,7 +191,7 @@ void EmuThread::startFullscreenUI(bool fullscreen) return; } - if (VMManager::HasValidVM() || GetMTGS().IsOpen()) + if (VMManager::HasValidVM() || MTGS::IsOpen()) return; // this should just set the flag so it gets automatically started @@ -199,7 +200,7 @@ void EmuThread::startFullscreenUI(bool fullscreen) m_is_rendering_to_main = shouldRenderToMain(); m_is_fullscreen = fullscreen; - if (!GetMTGS().WaitForOpen()) + if (!MTGS::WaitForOpen()) { m_run_fullscreen_ui = false; return; @@ -217,18 +218,18 @@ void EmuThread::stopFullscreenUI() QMetaObject::invokeMethod(this, &EmuThread::stopFullscreenUI, Qt::QueuedConnection); // wait until the host display is gone - while (GetMTGS().IsOpen()) + while (MTGS::IsOpen()) QApplication::processEvents(QEventLoop::ExcludeUserInputEvents, 1); return; } - if (!GetMTGS().IsOpen()) + if (!MTGS::IsOpen()) return; pxAssertRel(!VMManager::HasValidVM(), "VM is not valid at FSUI shutdown time"); m_run_fullscreen_ui = false; - GetMTGS().WaitForClose(); + MTGS::WaitForClose(); } void EmuThread::startVM(std::shared_ptr boot_params) @@ -509,14 +510,14 @@ void EmuThread::setFullscreen(bool fullscreen, bool allow_render_to_main) return; } - if (!GetMTGS().IsOpen() || m_is_fullscreen == fullscreen) + if (!MTGS::IsOpen() || m_is_fullscreen == fullscreen) return; // This will call back to us on the MTGS thread. m_is_fullscreen = fullscreen; m_is_rendering_to_main = allow_render_to_main && shouldRenderToMain(); - GetMTGS().UpdateDisplayWindow(); - GetMTGS().WaitGS(); + MTGS::UpdateDisplayWindow(); + MTGS::WaitGS(); // If we're using exclusive fullscreen, the refresh rate may have changed. UpdateVSyncRate(true); @@ -530,17 +531,17 @@ void EmuThread::setSurfaceless(bool surfaceless) return; } - if (!GetMTGS().IsOpen() || m_is_surfaceless == surfaceless) + if (!MTGS::IsOpen() || m_is_surfaceless == surfaceless) return; // If we went surfaceless and were running the fullscreen UI, stop MTGS running idle. // Otherwise, we'll keep trying to present to nothing. - GetMTGS().SetRunIdle(!surfaceless && m_run_fullscreen_ui); + MTGS::SetRunIdle(!surfaceless && m_run_fullscreen_ui); // This will call back to us on the MTGS thread. m_is_surfaceless = surfaceless; - GetMTGS().UpdateDisplayWindow(); - GetMTGS().WaitGS(); + MTGS::UpdateDisplayWindow(); + MTGS::WaitGS(); } void EmuThread::applySettings() @@ -601,14 +602,14 @@ void EmuThread::checkForSettingChanges(const Pcsx2Config& old_config) updatePerformanceMetrics(true); } - if (GetMTGS().IsOpen()) + if (MTGS::IsOpen()) { const bool render_to_main = shouldRenderToMain(); if (!m_is_fullscreen && m_is_rendering_to_main != render_to_main) { m_is_rendering_to_main = render_to_main; - GetMTGS().UpdateDisplayWindow(); - GetMTGS().WaitGS(); + MTGS::UpdateDisplayWindow(); + MTGS::WaitGS(); } } } @@ -634,7 +635,7 @@ void EmuThread::toggleSoftwareRendering() if (!VMManager::HasValidVM()) return; - GetMTGS().ToggleSoftwareRendering(); + MTGS::ToggleSoftwareRendering(); } void EmuThread::switchRenderer(GSRendererType renderer) @@ -648,7 +649,7 @@ void EmuThread::switchRenderer(GSRendererType renderer) if (!VMManager::HasValidVM()) return; - GetMTGS().SwitchRenderer(renderer); + MTGS::SwitchRenderer(renderer); } void EmuThread::changeDisc(CDVD_SourceType source, const QString& path) @@ -792,10 +793,10 @@ void EmuThread::connectDisplaySignals(DisplayWidget* widget) void EmuThread::onDisplayWindowResized(int width, int height, float scale) { - if (!GetMTGS().IsOpen()) + if (!MTGS::IsOpen()) return; - GetMTGS().ResizeDisplayWindow(width, height, scale); + MTGS::ResizeDisplayWindow(width, height, scale); } void EmuThread::onApplicationStateChanged(Qt::ApplicationState state) @@ -841,7 +842,7 @@ void EmuThread::redrawDisplayWindow() if (!VMManager::HasValidVM() || VMManager::GetState() == VMState::Running) return; - GetMTGS().PresentCurrentFrame(); + MTGS::PresentCurrentFrame(); } void EmuThread::runOnCPUThread(const std::function& func) @@ -860,7 +861,7 @@ void EmuThread::queueSnapshot(quint32 gsdump_frames) if (!VMManager::HasValidVM()) return; - GetMTGS().RunOnGSThread([gsdump_frames]() { GSQueueSnapshot(std::string(), gsdump_frames); }); + MTGS::RunOnGSThread([gsdump_frames]() { GSQueueSnapshot(std::string(), gsdump_frames); }); } void EmuThread::beginCapture(const QString& path) @@ -874,13 +875,13 @@ void EmuThread::beginCapture(const QString& path) if (!VMManager::HasValidVM()) return; - GetMTGS().RunOnGSThread([path = path.toStdString()]() { + MTGS::RunOnGSThread([path = path.toStdString()]() { GSBeginCapture(std::move(path)); }); // Sync GS thread. We want to start adding audio at the same time as video. // TODO: This could be up to 64 frames behind... use the pts to adjust it. - GetMTGS().WaitGS(false, false, false); + MTGS::WaitGS(false, false, false); } void EmuThread::endCapture() @@ -894,7 +895,7 @@ void EmuThread::endCapture() if (!VMManager::HasValidVM()) return; - GetMTGS().RunOnGSThread(&GSEndCapture); + MTGS::RunOnGSThread(&GSEndCapture); } std::optional EmuThread::acquireRenderWindow(bool recreate_window) @@ -1185,13 +1186,6 @@ void Host::SetFullscreen(bool enabled) g_emu_thread->setFullscreen(enabled, true); } -alignas(16) static SysMtgsThread s_mtgs_thread; - -SysMtgsThread& GetMTGS() -{ - return s_mtgs_thread; -} - bool QtHost::InitializeConfig() { if (!EmuFolders::InitializeCriticalFolders()) diff --git a/pcsx2/Achievements.cpp b/pcsx2/Achievements.cpp index 443f7c28e3..e6b779a623 100644 --- a/pcsx2/Achievements.cpp +++ b/pcsx2/Achievements.cpp @@ -21,13 +21,13 @@ #include "CDVD/IsoFS/IsoFS.h" #include "CDVD/IsoFS/IsoFSCDVD.h" #include "Elfheader.h" -#include "GS.h" #include "Host.h" #include "ImGui/FullscreenUI.h" #include "ImGui/ImGuiFullscreen.h" #include "ImGui/ImGuiManager.h" #include "IopMem.h" #include "Memory.h" +#include "MTGS.h" #include "VMManager.h" #include "svnrev.h" #include "vtlb.h" @@ -426,7 +426,7 @@ std::string Achievements::GetUserAgent() void Achievements::BeginLoadingScreen(const char* text, bool* was_running_idle) { - GetMTGS().RunOnGSThread(&ImGuiManager::InitializeFullscreenUI); + MTGS::RunOnGSThread(&ImGuiManager::InitializeFullscreenUI); ImGuiFullscreen::OpenBackgroundProgressDialog("achievements_loading", text, 0, 0, 0); } @@ -1041,7 +1041,7 @@ void Achievements::DownloadImage(std::string url, std::string cache_filename) return; } - GetMTGS().RunOnGSThread([cache_filename]() { ImGuiFullscreen::InvalidateCachedTexture(cache_filename); }); + MTGS::RunOnGSThread([cache_filename]() { ImGuiFullscreen::InvalidateCachedTexture(cache_filename); }); }; s_http_downloader->CreateRequest(std::move(url), std::move(callback)); @@ -1072,7 +1072,7 @@ void Achievements::DisplayAchievementSummary() summary.append(TRANSLATE_SV("Achievements", "Leaderboard submission is enabled.")); } - GetMTGS().RunOnGSThread([title = std::move(title), summary = std::move(summary), icon = s_game_icon]() { + MTGS::RunOnGSThread([title = std::move(title), summary = std::move(summary), icon = s_game_icon]() { if (FullscreenUI::IsInitialized()) ImGuiFullscreen::AddNotification(10.0f, std::move(title), std::move(summary), std::move(icon)); }); @@ -1092,7 +1092,7 @@ void Achievements::DisplayMasteredNotification() std::string message(fmt::format( "{} achievements, {} points{}", GetAchievementCount(), GetCurrentPointsForGame(), s_challenge_mode ? " (Hardcore Mode)" : "")); - GetMTGS().RunOnGSThread([title = std::move(title), message = std::move(message), icon = s_game_icon]() { + MTGS::RunOnGSThread([title = std::move(title), message = std::move(message), icon = s_game_icon]() { if (FullscreenUI::IsInitialized()) ImGuiFullscreen::AddNotification(20.0f, std::move(title), std::move(message), std::move(icon)); }); @@ -1157,7 +1157,7 @@ void Achievements::GetPatchesCallback(s32 status_code, const std::string& conten return; // ensure fullscreen UI is ready - GetMTGS().RunOnGSThread(&ImGuiManager::InitializeFullscreenUI); + MTGS::RunOnGSThread(&ImGuiManager::InitializeFullscreenUI); s_game_id = response.id; s_game_title = response.title; @@ -1847,7 +1847,7 @@ void Achievements::SubmitLeaderboardCallback(s32 status_code, const std::string& std::string summary = fmt::format( "Your Score: {} (Best: {})\nLeaderboard Position: {} of {}", submitted_score, best_score, response.new_rank, response.num_entries); - GetMTGS().RunOnGSThread([title = lb->title, summary = std::move(summary), icon = s_game_icon]() { + MTGS::RunOnGSThread([title = lb->title, summary = std::move(summary), icon = s_game_icon]() { if (FullscreenUI::IsInitialized()) ImGuiFullscreen::AddNotification(10.0f, std::move(title), std::move(summary), std::move(icon)); }); @@ -1890,7 +1890,7 @@ void Achievements::UnlockAchievement(u32 achievement_id, bool add_notification / break; } - GetMTGS().RunOnGSThread( + MTGS::RunOnGSThread( [title = std::move(title), description = achievement->description, icon = GetAchievementBadgePath(*achievement)]() { ImGuiFullscreen::AddNotification(15.0f, std::move(title), std::move(description), std::move(icon)); }); diff --git a/pcsx2/CMakeLists.txt b/pcsx2/CMakeLists.txt index efd2bdf20b..a716b89260 100644 --- a/pcsx2/CMakeLists.txt +++ b/pcsx2/CMakeLists.txt @@ -202,6 +202,7 @@ set(pcsx2Headers LogSink.h PINE.h Mdec.h + MTGS.h MTVU.h Memory.h MemoryCardFile.h diff --git a/pcsx2/Counters.cpp b/pcsx2/Counters.cpp index 51bc1b6a44..fee3d2f36f 100644 --- a/pcsx2/Counters.cpp +++ b/pcsx2/Counters.cpp @@ -1,5 +1,5 @@ /* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2021 PCSX2 Dev Team + * Copyright (C) 2002-2023 PCSX2 Dev Team * * PCSX2 is free software: you can redistribute it and/or modify it under the terms * of the GNU Lesser General Public License as published by the Free Software Found- @@ -26,16 +26,16 @@ #include "GS.h" #include "GS/GS.h" -#include "VUmicro.h" +#include "MTGS.h" #include "PerformanceMetrics.h" #include "Patch.h" - #include "ps2/HwInternal.h" #include "Sio.h" #include "SPU2/spu2.h" #include "PAD/Host/PAD.h" #include "Recording/InputRecording.h" #include "VMManager.h" +#include "VUmicro.h" using namespace Threading; @@ -538,7 +538,7 @@ static __fi void DoFMVSwitch() RendererSwitched = GSConfig.UseHardwareRenderer(); // we don't use the sw toggle here, because it'll change back to auto if set to sw - GetMTGS().SwitchRenderer(new_fmv_state ? GSRendererType::SW : EmuConfig.GS.Renderer, false); + MTGS::SwitchRenderer(new_fmv_state ? GSRendererType::SW : EmuConfig.GS.Renderer, false); } else RendererSwitched = false; diff --git a/pcsx2/FiFo.cpp b/pcsx2/FiFo.cpp index 4a5070dd0e..5b058b04a6 100644 --- a/pcsx2/FiFo.cpp +++ b/pcsx2/FiFo.cpp @@ -1,5 +1,5 @@ /* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2010 PCSX2 Dev Team + * Copyright (C) 2002-2023 PCSX2 Dev Team * * PCSX2 is free software: you can redistribute it and/or modify it under the terms * of the GNU Lesser General Public License as published by the Free Software Found- @@ -19,6 +19,7 @@ #include "Gif.h" #include "Gif_Unit.h" +#include "MTGS.h" #include "Vif.h" #include "Vif_Dma.h" @@ -51,7 +52,7 @@ void ReadFIFO_VIF1(mem128_t* out) } if (vif1Regs.stat.FQC > 0) { - GetMTGS().InitAndReadFIFO(reinterpret_cast(out), 1); + MTGS::InitAndReadFIFO(reinterpret_cast(out), 1); vif1.GSLastDownloadSize--; GUNIT_LOG("ReadFIFO_VIF1"); if (vif1.GSLastDownloadSize <= 16) diff --git a/pcsx2/GS.cpp b/pcsx2/GS.cpp index 6d350551d6..0f9c02b7d3 100644 --- a/pcsx2/GS.cpp +++ b/pcsx2/GS.cpp @@ -14,17 +14,15 @@ */ #include "PrecompiledHeader.h" -#include "Common.h" -#include - -#include "Gif_Unit.h" #include "Counters.h" +#include "Common.h" #include "Config.h" +#include "Gif_Unit.h" +#include "MTGS.h" #include "VMManager.h" -using namespace Threading; -using namespace R5900; +#include alignas(16) u8 g_RealGSMem[Ps2MemSize::GSregs]; static bool s_GSRegistersWritten = false; @@ -38,7 +36,7 @@ void gsSetVideoMode(GS_VideoMode mode) // Make sure framelimiter options are in sync with GS capabilities. void gsReset() { - GetMTGS().ResetGS(true); + MTGS::ResetGS(true); gsVideoMode = GS_VideoMode::Uninitialized; std::memset(g_RealGSMem, 0, sizeof(g_RealGSMem)); UpdateVSyncRate(true); @@ -72,7 +70,7 @@ void gsUpdateFrequency(Pcsx2Config& config) config.GS.LimitScalar = 0.0f; } - GetMTGS().UpdateVSyncMode(); + MTGS::UpdateVSyncMode(); UpdateVSyncRate(true); } @@ -88,7 +86,7 @@ static __fi void gsCSRwrite( const tGS_CSR& csr ) std::memset(g_RealGSMem, 0, sizeof(g_RealGSMem)); GSIMR.reset(); CSRreg.Reset(); - GetMTGS().SendSimplePacket(GS_RINGTYPE_RESET, 0, 0, 0); + MTGS::SendSimplePacket(MTGS::Command::Reset, 0, 0, 0); } if(csr.FLUSH) @@ -371,7 +369,7 @@ void gsPostVsyncStart() const bool registers_written = s_GSRegistersWritten; s_GSRegistersWritten = false; - GetMTGS().PostVsyncStart(registers_written); + MTGS::PostVsyncStart(registers_written); } void SaveStateBase::gsFreeze() diff --git a/pcsx2/GS.h b/pcsx2/GS.h index 1636c22c2f..49a6db2f11 100644 --- a/pcsx2/GS.h +++ b/pcsx2/GS.h @@ -1,5 +1,5 @@ /* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2010 PCSX2 Dev Team + * Copyright (C) 2002-2023 PCSX2 Dev Team * * PCSX2 is free software: you can redistribute it and/or modify it under the terms * of the GNU Lesser General Public License as published by the Free Software Found- @@ -19,10 +19,6 @@ #include "Gif.h" #include "GS/GS.h" #include "SingleRegisterTypes.h" -#include -#include -#include -#include extern double GetVerticalFrequency(); alignas(16) extern u8 g_RealGSMem[Ps2MemSize::GSregs]; @@ -281,165 +277,6 @@ enum class GS_VideoMode : int extern GS_VideoMode gsVideoMode; extern bool gsIsInterlaced; -///////////////////////////////////////////////////////////////////////////// -// MTGS Threaded Class Declaration - -// Uncomment this to enable the MTGS debug stack, which tracks to ensure reads -// and writes stay synchronized. Warning: the debug stack is VERY slow. -//#define RINGBUF_DEBUG_STACK - -enum MTGS_RingCommand -{ - GS_RINGTYPE_P1, - GS_RINGTYPE_P2, - GS_RINGTYPE_P3, - GS_RINGTYPE_VSYNC, - GS_RINGTYPE_FREEZE, - GS_RINGTYPE_RESET, // issues a GSreset() command. - GS_RINGTYPE_SOFTRESET, // issues a soft reset for the GIF - GS_RINGTYPE_CRC, - GS_RINGTYPE_GSPACKET, - GS_RINGTYPE_MTVU_GSPACKET, - GS_RINGTYPE_INIT_AND_READ_FIFO, - GS_RINGTYPE_ASYNC_CALL, -}; - - -struct MTGS_FreezeData -{ - freezeData* fdata; - s32 retval; // value returned from the call, valid only after an mtgsWaitGS() -}; - -struct MTGS_MemoryScreenshotData -{ - u32 width = 0; - u32 height = 0; - std::vector pixels; // width * height - bool success = false; -}; - -// -------------------------------------------------------------------------------------- -// SysMtgsThread -// -------------------------------------------------------------------------------------- -class SysMtgsThread -{ -public: - using AsyncCallType = std::function; - - // note: when m_ReadPos == m_WritePos, the fifo is empty - // Threading info: m_ReadPos is updated by the MTGS thread. m_WritePos is updated by the EE thread - std::atomic m_ReadPos; // cur pos gs is reading from - std::atomic m_WritePos; // cur pos ee thread is writing to - - std::atomic m_SignalRingEnable; - std::atomic m_SignalRingPosition; - - std::atomic m_QueuedFrameCount; - std::atomic m_VsyncSignalListener; - - std::mutex m_mtx_RingBufferBusy2; // Gets released on semaXGkick waiting... - Threading::WorkSema m_sem_event; - Threading::UserspaceSemaphore m_sem_OnRingReset; - Threading::UserspaceSemaphore m_sem_Vsync; - - // used to keep multiple threads from sending packets to the ringbuffer concurrently. - // (currently not used or implemented -- is a planned feature for a future threaded VU1) - //MutexLockRecursive m_PacketLocker; - - // Used to delay the sending of events. Performance is better if the ringbuffer - // has more than one command in it when the thread is kicked. - int m_CopyDataTally; - - // These vars maintain instance data for sending Data Packets. - // Only one data packet can be constructed and uploaded at a time. - - uint m_packet_startpos; // size of the packet (data only, ie. not including the 16 byte command!) - uint m_packet_size; // size of the packet (data only, ie. not including the 16 byte command!) - uint m_packet_writepos; // index of the data location in the ringbuffer. - -#ifdef RINGBUF_DEBUG_STACK - std::mutex m_lock_Stack; -#endif - - std::thread m_thread; - Threading::ThreadHandle m_thread_handle; - std::atomic_bool m_open_flag{false}; - std::atomic_bool m_shutdown_flag{false}; - std::atomic_bool m_run_idle_flag{false}; - Threading::UserspaceSemaphore m_open_or_close_done; - -public: - SysMtgsThread(); - virtual ~SysMtgsThread(); - - __fi const Threading::ThreadHandle& GetThreadHandle() const { return m_thread_handle; } - __fi bool IsOpen() const { return m_open_flag.load(std::memory_order_acquire); } - - /// Starts the thread, if it hasn't already been started. - void StartThread(); - - /// Fully stops the thread, closing in the process if needed. - void ShutdownThread(); - - /// Re-presents the current frame. Call when things like window resizes happen to re-display - /// the current frame with the correct proportions. Should only be called from the CPU thread. - void PresentCurrentFrame(); - - // Waits for the GS to empty out the entire ring buffer contents. - void WaitGS(bool syncRegs=true, bool weakWait=false, bool isMTVU=false); - void ResetGS(bool hardware_reset); - - void PrepDataPacket(MTGS_RingCommand cmd, u32 size); - void PrepDataPacket(GIF_PATH pathidx, u32 size); - void SendDataPacket(); - void SendGameCRC(u32 crc); - bool WaitForOpen(); - void WaitForClose(); - void Freeze(FreezeAction mode, MTGS_FreezeData& data); - - void SendSimpleGSPacket(MTGS_RingCommand type, u32 offset, u32 size, GIF_PATH path); - void SendSimplePacket(MTGS_RingCommand type, int data0, int data1, int data2); - void SendPointerPacket(MTGS_RingCommand type, u32 data0, void* data1); - - u8* GetDataPacketPtr() const; - void SetEvent(); - void PostVsyncStart(bool registers_written); - void InitAndReadFIFO(u8* mem, u32 qwc); - - void RunOnGSThread(AsyncCallType func); - void ApplySettings(); - void ResizeDisplayWindow(int width, int height, float scale); - void UpdateDisplayWindow(); - void SetVSyncMode(VsyncMode mode); - void UpdateVSyncMode(); - void SwitchRenderer(GSRendererType renderer, bool display_message = true); - void SetSoftwareRendering(bool software, bool display_message = true); - void ToggleSoftwareRendering(); - bool SaveMemorySnapshot(u32 window_width, u32 window_height, bool apply_aspect, bool crop_borders, - u32* width, u32* height, std::vector* pixels); - void SetRunIdle(bool enabled); - -protected: - bool TryOpenGS(); - void CloseGS(); - - void ThreadEntryPoint(); - void MainLoop(); - - void GenericStall(uint size); - - // Used internally by SendSimplePacket type functions - void _FinishSimplePacket(); -}; - -// GetMTGS() is a required external implementation. This function is *NOT* provided -// by the PCSX2 core library. It provides an interface for the linking User Interface -// apps or DLLs to reference their own instance of SysMtgsThread (also allowing them -// to extend the class and override virtual methods). -// -extern SysMtgsThread& GetMTGS(); - ///////////////////////////////////////////////////////////////////////////// // Generalized GS Functions and Stuff @@ -469,68 +306,3 @@ extern u128 gsNonMirroredRead(u32 mem); void gsIrq(); extern tGS_CSR CSRr; - -// Size of the ringbuffer as a power of 2 -- size is a multiple of simd128s. -// (actual size is 1<EndCapture(); }); - GetMTGS().WaitGS(false, false, false); + MTGS::RunOnGSThread([]() { g_gs_renderer->EndCapture(); }); + MTGS::WaitGS(false, false, false); return; } - GetMTGS().RunOnGSThread([]() { + MTGS::RunOnGSThread([]() { std::string filename(fmt::format("{}.{}", GSGetBaseVideoFilename(), GSConfig.CaptureContainer)); g_gs_renderer->BeginCapture(std::move(filename)); }); // Sync GS thread. We want to start adding audio at the same time as video. - GetMTGS().WaitGS(false, false, false); + MTGS::WaitGS(false, false, false); } }}, {"GSDumpSingleFrame", TRANSLATE_NOOP("Hotkeys", "Graphics"), TRANSLATE_NOOP("Hotkeys", "Save Single Frame GS Dump"), [](s32 pressed) { if (!pressed) { - GetMTGS().RunOnGSThread([]() { GSQueueSnapshot(std::string(), 1); }); + MTGS::RunOnGSThread([]() { GSQueueSnapshot(std::string(), 1); }); } }}, {"GSDumpMultiFrame", TRANSLATE_NOOP("Hotkeys", "Graphics"), TRANSLATE_NOOP("Hotkeys", "Save Multi Frame GS Dump"), [](s32 pressed) { - GetMTGS().RunOnGSThread([pressed]() { + MTGS::RunOnGSThread([pressed]() { if (pressed > 0) GSQueueSnapshot(std::string(), std::numeric_limits::max()); else @@ -1023,7 +1024,7 @@ BEGIN_HOTKEY_LIST(g_gs_hotkeys){"Screenshot", TRANSLATE_NOOP("Hotkeys", "Graphic TRANSLATE_NOOP("Hotkeys", "Toggle Software Rendering"), [](s32 pressed) { if (!pressed) - GetMTGS().ToggleSoftwareRendering(); + MTGS::ToggleSoftwareRendering(); }}, {"IncreaseUpscaleMultiplier", TRANSLATE_NOOP("Hotkeys", "Graphics"), TRANSLATE_NOOP("Hotkeys", "Increase Upscale Multiplier"), @@ -1067,7 +1068,7 @@ BEGIN_HOTKEY_LIST(g_gs_hotkeys){"Screenshot", TRANSLATE_NOOP("Hotkeys", "Graphic Host::OSD_QUICK_DURATION); EmuConfig.GS.HWMipmap = new_level; - GetMTGS().RunOnGSThread([new_level]() { + MTGS::RunOnGSThread([new_level]() { GSConfig.HWMipmap = new_level; g_gs_renderer->PurgeTextureCache(); g_gs_renderer->PurgePool(); @@ -1099,7 +1100,7 @@ BEGIN_HOTKEY_LIST(g_gs_hotkeys){"Screenshot", TRANSLATE_NOOP("Hotkeys", "Graphic Host::OSD_QUICK_DURATION); EmuConfig.GS.InterlaceMode = new_mode; - GetMTGS().RunOnGSThread([new_mode]() { GSConfig.InterlaceMode = new_mode; }); + MTGS::RunOnGSThread([new_mode]() { GSConfig.InterlaceMode = new_mode; }); }}, {"ToggleTextureDumping", TRANSLATE_NOOP("Hotkeys", "Graphics"), TRANSLATE_NOOP("Hotkeys", "Toggle Texture Dumping"), [](s32 pressed) { @@ -1110,7 +1111,7 @@ BEGIN_HOTKEY_LIST(g_gs_hotkeys){"Screenshot", TRANSLATE_NOOP("Hotkeys", "Graphic EmuConfig.GS.DumpReplaceableTextures ? TRANSLATE_STR("Hotkeys", "Texture dumping is now enabled.") : TRANSLATE_STR("Hotkeys", "Texture dumping is now disabled."), Host::OSD_INFO_DURATION); - GetMTGS().ApplySettings(); + MTGS::ApplySettings(); } }}, {"ToggleTextureReplacements", TRANSLATE_NOOP("Hotkeys", "Graphics"), @@ -1124,7 +1125,7 @@ BEGIN_HOTKEY_LIST(g_gs_hotkeys){"Screenshot", TRANSLATE_NOOP("Hotkeys", "Graphic TRANSLATE_STR("Hotkeys", "Texture replacements are now enabled.") : TRANSLATE_STR("Hotkeys", "Texture replacements are now disabled."), Host::OSD_INFO_DURATION); - GetMTGS().ApplySettings(); + MTGS::ApplySettings(); } }}, {"ReloadTextureReplacements", TRANSLATE_NOOP("Hotkeys", "Graphics"), @@ -1141,7 +1142,7 @@ BEGIN_HOTKEY_LIST(g_gs_hotkeys){"Screenshot", TRANSLATE_NOOP("Hotkeys", "Graphic { Host::AddKeyedOSDMessage("ReloadTextureReplacements", TRANSLATE_STR("Hotkeys", "Reloading texture replacements..."), Host::OSD_INFO_DURATION); - GetMTGS().RunOnGSThread([]() { GSTextureReplacements::ReloadReplacementMap(); }); + MTGS::RunOnGSThread([]() { GSTextureReplacements::ReloadReplacementMap(); }); } } }}, diff --git a/pcsx2/GSDumpReplayer.cpp b/pcsx2/GSDumpReplayer.cpp index 215f45c557..1670c57033 100644 --- a/pcsx2/GSDumpReplayer.cpp +++ b/pcsx2/GSDumpReplayer.cpp @@ -213,8 +213,8 @@ static void GSDumpReplayerLoadInitialState() // load GS state freezeData fd = {static_cast(s_dump_file->GetStateData().size()), const_cast(s_dump_file->GetStateData().data())}; - MTGS_FreezeData mfd = {&fd, 0}; - GetMTGS().Freeze(FreezeAction::Load, mfd); + MTGS::FreezeData mfd = {&fd, 0}; + MTGS::Freeze(FreezeAction::Load, mfd); if (mfd.retval != 0) Host::ReportFormattedErrorAsync("GSDumpReplayer", "Failed to load GS state."); } @@ -317,7 +317,7 @@ void GSDumpReplayerCpuStep() s_dump_frame_number++; GSDumpReplayerUpdateFrameLimit(); GSDumpReplayerFrameLimit(); - GetMTGS().PostVsyncStart(false); + MTGS::PostVsyncStart(false); VMManager::Internal::VSyncOnCPUThread(); if (VMManager::Internal::IsExecutionInterrupted()) GSDumpReplayerExitExecution(); @@ -331,7 +331,7 @@ void GSDumpReplayerCpuStep() // Allocate an extra quadword, some transfers write too much (e.g. Lego Racers 2 with Z24 downloads). std::unique_ptr arr(new u8[(size + 1) * 16]); - GetMTGS().InitAndReadFIFO(arr.get(), size); + MTGS::InitAndReadFIFO(arr.get(), size); } break; diff --git a/pcsx2/Gif_Unit.cpp b/pcsx2/Gif_Unit.cpp index 88c9e22525..46b057d848 100644 --- a/pcsx2/Gif_Unit.cpp +++ b/pcsx2/Gif_Unit.cpp @@ -195,42 +195,6 @@ void Gif_FinishIRQ() } } -// Used in MTVU mode... MTVU will later complete a real packet -void Gif_AddGSPacketMTVU(GS_Packet& gsPack, GIF_PATH path) -{ - GetMTGS().SendSimpleGSPacket(GS_RINGTYPE_MTVU_GSPACKET, 0, 0, path); -} - -void Gif_AddCompletedGSPacket(GS_Packet& gsPack, GIF_PATH path) -{ - //DevCon.WriteLn("Adding Completed Gif Packet [size=%x]", gsPack.size); - if (COPY_GS_PACKET_TO_MTGS) - { - GetMTGS().PrepDataPacket(path, gsPack.size / 16); - MemCopy_WrappedDest((u128*)&gifUnit.gifPath[path].buffer[gsPack.offset], RingBuffer.m_Ring, - GetMTGS().m_packet_writepos, RingBufferSize, gsPack.size / 16); - GetMTGS().SendDataPacket(); - } - else - { - pxAssertDev(!gsPack.readAmount, "Gif Unit - gsPack.readAmount only valid for MTVU path 1!"); - gifUnit.gifPath[path].readAmount.fetch_add(gsPack.size); - GetMTGS().SendSimpleGSPacket(GS_RINGTYPE_GSPACKET, gsPack.offset, gsPack.size, path); - } -} - -void Gif_AddBlankGSPacket(u32 size, GIF_PATH path) -{ - //DevCon.WriteLn("Adding Blank Gif Packet [size=%x]", size); - gifUnit.gifPath[path].readAmount.fetch_add(size); - GetMTGS().SendSimpleGSPacket(GS_RINGTYPE_GSPACKET, ~0u, size, path); -} - -void Gif_MTGS_Wait(bool isMTVU) -{ - GetMTGS().WaitGS(false, true, isMTVU); -} - void SaveStateBase::gifPathFreeze(u32 path) { @@ -262,7 +226,7 @@ void SaveStateBase::gifFreeze() { bool mtvuMode = THREAD_VU1; pxAssert(vu1Thread.IsDone()); - GetMTGS().WaitGS(); + MTGS::WaitGS(); FreezeTag("Gif Unit"); Freeze(mtvuMode); Freeze(gifUnit.stat); diff --git a/pcsx2/Gif_Unit.h b/pcsx2/Gif_Unit.h index 1f80618d6e..9d89503dc6 100644 --- a/pcsx2/Gif_Unit.h +++ b/pcsx2/Gif_Unit.h @@ -19,6 +19,7 @@ #include "Vif.h" #include "GS.h" #include "GS/GSRegs.h" +#include "MTGS.h" // FIXME common path ? #include "common/boost_spsc_queue.hpp" @@ -193,7 +194,7 @@ struct Gif_Path_MTVU // Set a size based on MTGS but keep a factor 2 to avoid too waste to much // memory overhead. Note the struct is instantied 3 times (for each gif // path) - ringbuffer_base gsPackQueue; + ringbuffer_base gsPackQueue; Gif_Path_MTVU() { Reset(); } void Reset() { diff --git a/pcsx2/Hw.cpp b/pcsx2/Hw.cpp index 928877369c..86514cd628 100644 --- a/pcsx2/Hw.cpp +++ b/pcsx2/Hw.cpp @@ -22,6 +22,8 @@ #include "USB/USB.h" #include "x86/newVif.h" +#include "common/WrappedMemCopy.h" + #include "fmt/core.h" using namespace R5900; diff --git a/pcsx2/ImGui/FullscreenUI.cpp b/pcsx2/ImGui/FullscreenUI.cpp index 5eaf3be022..28f4070963 100644 --- a/pcsx2/ImGui/FullscreenUI.cpp +++ b/pcsx2/ImGui/FullscreenUI.cpp @@ -1,5 +1,5 @@ /* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2023 PCSX2 Dev Team + * Copyright (C) 2002-2023 PCSX2 Dev Team * * PCSX2 is free software: you can redistribute it and/or modify it under the terms * of the GNU Lesser General Public License as published by the Free Software Found- @@ -22,7 +22,6 @@ #include "GS/Renderers/Common/GSTexture.h" #include "Achievements.h" #include "CDVD/CDVDdiscReader.h" -#include "GS.h" #include "GameList.h" #include "Host.h" #include "INISettingsInterface.h" @@ -31,6 +30,7 @@ #include "ImGui/ImGuiManager.h" #include "Input/InputManager.h" #include "MemoryCardFile.h" +#include "MTGS.h" #include "PAD/Host/PAD.h" #include "Sio.h" #include "USB/USB.h" @@ -584,7 +584,7 @@ bool FullscreenUI::Initialize() s_initialized = true; s_hotkey_list_cache = InputManager::GetHotkeyList(); - GetMTGS().SetRunIdle(true); + MTGS::SetRunIdle(true); if (VMManager::HasValidVM()) { @@ -623,13 +623,13 @@ void FullscreenUI::CheckForConfigChanges(const Pcsx2Config& old_config) if (old_config.Achievements.Enabled && !EmuConfig.Achievements.Enabled) { // So, wait just in case. - GetMTGS().RunOnGSThread([]() { + MTGS::RunOnGSThread([]() { if (s_current_main_window == MainWindowType::Achievements || s_current_main_window == MainWindowType::Leaderboards) { ReturnToMainWindow(); } }); - GetMTGS().WaitGS(false, false, false); + MTGS::WaitGS(false, false, false); } #endif } @@ -639,7 +639,7 @@ void FullscreenUI::OnVMStarted() if (!IsInitialized()) return; - GetMTGS().RunOnGSThread([]() { + MTGS::RunOnGSThread([]() { if (!IsInitialized()) return; @@ -653,7 +653,7 @@ void FullscreenUI::OnVMDestroyed() if (!IsInitialized()) return; - GetMTGS().RunOnGSThread([]() { + MTGS::RunOnGSThread([]() { if (!IsInitialized()) return; @@ -667,7 +667,7 @@ void FullscreenUI::GameChanged(std::string path, std::string serial, std::string if (!IsInitialized()) return; - GetMTGS().RunOnGSThread( + MTGS::RunOnGSThread( [path = std::move(path), serial = std::move(serial), title = std::move(title), disc_crc, crc]() { if (!IsInitialized()) return; @@ -711,7 +711,7 @@ void FullscreenUI::OpenPauseMenu() if (!VMManager::HasValidVM()) return; - GetMTGS().RunOnGSThread([]() { + MTGS::RunOnGSThread([]() { if (!ImGuiManager::InitializeFullscreenUI() || s_current_main_window != MainWindowType::None) return; @@ -866,7 +866,7 @@ void FullscreenUI::InvalidateCoverCache() if (!IsInitialized()) return; - GetMTGS().RunOnGSThread([]() { s_cover_image_map.clear(); }); + MTGS::RunOnGSThread([]() { s_cover_image_map.clear(); }); } void FullscreenUI::ReturnToMainWindow() @@ -1030,7 +1030,7 @@ void FullscreenUI::DoToggleSoftwareRenderer() if (!VMManager::HasValidVM()) return; - GetMTGS().ToggleSoftwareRendering(); + MTGS::ToggleSoftwareRendering(); }); } @@ -2244,7 +2244,7 @@ void FullscreenUI::StartAutomaticBinding(u32 port) // messy because the enumeration has to happen on the input thread Host::RunOnCPUThread([port]() { std::vector> devices(InputManager::EnumerateDevices()); - GetMTGS().RunOnGSThread([port, devices = std::move(devices)]() { + MTGS::RunOnGSThread([port, devices = std::move(devices)]() { if (devices.empty()) { ShowToast({}, "Automatic binding failed, no devices are available."); @@ -2274,7 +2274,7 @@ void FullscreenUI::StartAutomaticBinding(u32 port) // and the toast needs to happen on the UI thread. - GetMTGS().RunOnGSThread([result, name = std::move(name)]() { + MTGS::RunOnGSThread([result, name = std::move(name)]() { ShowToast({}, result ? fmt::format("Automatic mapping completed for {}.", name) : fmt::format("Automatic mapping failed for {}.", name)); }); @@ -5890,7 +5890,7 @@ void FullscreenUI::DrawCoverDownloaderWindow() GameList::DownloadCovers(urls, use_serial_names, progress, [](const GameList::Entry* entry, std::string save_path) { // cache the cover path on our side once it's saved Host::RunOnCPUThread([path = entry->path, save_path = std::move(save_path)]() { - GetMTGS().RunOnGSThread([path = std::move(path), save_path = std::move(save_path)]() { + MTGS::RunOnGSThread([path = std::move(path), save_path = std::move(save_path)]() { s_cover_image_map[std::move(path)] = std::move(save_path); }); }); @@ -6167,7 +6167,7 @@ void FullscreenUI::OpenAchievementsWindow() if (!VMManager::HasValidVM() || !Achievements::IsActive()) return; - GetMTGS().RunOnGSThread([]() { + MTGS::RunOnGSThread([]() { if (!ImGuiManager::InitializeFullscreenUI()) return; @@ -6557,7 +6557,7 @@ void FullscreenUI::OpenLeaderboardsWindow() if (!VMManager::HasValidVM() || !Achievements::IsActive()) return; - GetMTGS().RunOnGSThread([]() { + MTGS::RunOnGSThread([]() { if (!ImGuiManager::InitializeFullscreenUI()) return; diff --git a/pcsx2/ImGui/ImGuiManager.cpp b/pcsx2/ImGui/ImGuiManager.cpp index 6381996e64..0aa7a05684 100644 --- a/pcsx2/ImGui/ImGuiManager.cpp +++ b/pcsx2/ImGui/ImGuiManager.cpp @@ -18,7 +18,6 @@ #include "GS/Renderers/Common/GSDevice.h" #include "Config.h" #include "Counters.h" -#include "GS.h" #include "GS/GS.h" #include "Host.h" #include "IconsFontAwesome5.h" @@ -27,6 +26,7 @@ #include "ImGui/ImGuiManager.h" #include "ImGui/ImGuiOverlays.h" #include "Input/InputManager.h" +#include "MTGS.h" #include "PerformanceMetrics.h" #include "Recording/InputRecording.h" #include "VMManager.h" @@ -713,7 +713,7 @@ void ImGuiManager::AddTextInput(std::string str) // Has to go through the CPU -> GS thread :( Host::RunOnCPUThread([str = std::move(str)]() { - GetMTGS().RunOnGSThread([str = std::move(str)]() { + MTGS::RunOnGSThread([str = std::move(str)]() { if (!ImGui::GetCurrentContext()) return; @@ -737,7 +737,7 @@ bool ImGuiManager::ProcessPointerButtonEvent(InputBindingKey key, float value) return false; // still update state anyway - GetMTGS().RunOnGSThread([button = key.data, down = (value != 0.0f)]() { ImGui::GetIO().AddMouseButtonEvent(button, down); }); + MTGS::RunOnGSThread([button = key.data, down = (value != 0.0f)]() { ImGui::GetIO().AddMouseButtonEvent(button, down); }); return s_imgui_wants_mouse.load(std::memory_order_acquire); } @@ -749,7 +749,7 @@ bool ImGuiManager::ProcessPointerAxisEvent(InputBindingKey key, float value) // still update state anyway const bool horizontal = (key.data == static_cast(InputPointerAxis::WheelX)); - GetMTGS().RunOnGSThread([wheel_x = horizontal ? value : 0.0f, wheel_y = horizontal ? 0.0f : value]() { + MTGS::RunOnGSThread([wheel_x = horizontal ? value : 0.0f, wheel_y = horizontal ? 0.0f : value]() { ImGui::GetIO().AddMouseWheelEvent(wheel_x, wheel_y); }); @@ -763,7 +763,7 @@ bool ImGuiManager::ProcessHostKeyEvent(InputBindingKey key, float value) return false; // still update state anyway - GetMTGS().RunOnGSThread([imkey = iter->second, down = (value != 0.0f)]() { ImGui::GetIO().AddKeyEvent(imkey, down); }); + MTGS::RunOnGSThread([imkey = iter->second, down = (value != 0.0f)]() { ImGui::GetIO().AddKeyEvent(imkey, down); }); return s_imgui_wants_keyboard.load(std::memory_order_acquire); } @@ -805,7 +805,7 @@ bool ImGuiManager::ProcessGenericInputEvent(GenericInputBinding key, float value if (static_cast(key) >= std::size(key_map) || key_map[static_cast(key)] == ImGuiKey_None) return false; - GetMTGS().RunOnGSThread( + MTGS::RunOnGSThread( [key = key_map[static_cast(key)], value]() { ImGui::GetIO().AddKeyAnalogEvent(key, (value > 0.0f), value); }); return true; diff --git a/pcsx2/MTGS.cpp b/pcsx2/MTGS.cpp index 2b8d5e4a92..2a05c2fd9b 100644 --- a/pcsx2/MTGS.cpp +++ b/pcsx2/MTGS.cpp @@ -1,5 +1,5 @@ /* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2010 PCSX2 Dev Team + * Copyright (C) 2002-2023 PCSX2 Dev Team * * PCSX2 is free software: you can redistribute it and/or modify it under the terms * of the GNU Lesser General Public License as published by the Free Software Found- @@ -14,27 +14,26 @@ */ #include "PrecompiledHeader.h" -#include "Common.h" - -#include - -#include "common/ScopedGuard.h" -#include "common/StringUtil.h" #include "GS.h" #include "Gif_Unit.h" +#include "MTGS.h" #include "MTVU.h" -#include "Elfheader.h" - #include "Host.h" #include "IconsFontAwesome5.h" #include "VMManager.h" +#include "common/ScopedGuard.h" +#include "common/StringUtil.h" +#include "common/WrappedMemCopy.h" + +#include +#include +#include + // Uncomment this to enable profiling of the GS RingBufferCopy function. //#define PCSX2_GSRING_SAMPLING_STATS -using namespace Threading; - #if 0 //PCSX2_DEBUG #define MTGS_LOG Console.WriteLn #else @@ -44,94 +43,143 @@ using namespace Threading; } while (0) #endif +namespace MTGS +{ + struct BufferedData + { + u128 m_Ring[RingBufferSize]; + u8 Regs[Ps2MemSize::GSregs]; + + u128& operator[](uint idx) + { + pxAssert(idx < RingBufferSize); + return m_Ring[idx]; + } + }; + + static bool TryOpenGS(); + static void CloseGS(); + + static void ThreadEntryPoint(); + static void MainLoop(); + + static void GenericStall(uint size); + + static void PrepDataPacket(Command cmd, u32 size); + static void PrepDataPacket(GIF_PATH pathidx, u32 size); + static void SendDataPacket(); + + static void SendSimpleGSPacket(Command type, u32 offset, u32 size, GIF_PATH path); + static void SendPointerPacket(Command type, u32 data0, void* data1); + static void _FinishSimplePacket(); + static u8* GetDataPacketPtr(); + + static void SetEvent(); + + alignas(32) BufferedData RingBuffer; + + // note: when m_ReadPos == m_WritePos, the fifo is empty + // Threading info: m_ReadPos is updated by the MTGS thread. m_WritePos is updated by the EE thread + alignas(64) static std::atomic s_ReadPos; // cur pos gs is reading from + alignas(64) static std::atomic s_WritePos; // cur pos ee thread is writing to + + // These vars maintain instance data for sending Data Packets. + // Only one data packet can be constructed and uploaded at a time. + static u32 s_packet_startpos; // size of the packet (data only, ie. not including the 16 byte command!) + static u32 s_packet_size; // size of the packet (data only, ie. not including the 16 byte command!) + static u32 s_packet_writepos; // index of the data location in the ringbuffer. + + static std::atomic s_SignalRingEnable; + static std::atomic s_SignalRingPosition; + + static std::atomic s_QueuedFrameCount; + static std::atomic s_VsyncSignalListener; + + static std::mutex s_mtx_RingBufferBusy2; // Gets released on semaXGkick waiting... + static Threading::WorkSema s_sem_event; + static Threading::UserspaceSemaphore s_sem_OnRingReset; + static Threading::UserspaceSemaphore s_sem_Vsync; + + // Used to delay the sending of events. Performance is better if the ringbuffer + // has more than one command in it when the thread is kicked. + static int s_CopyDataTally; + +#ifdef RINGBUF_DEBUG_STACK + static std::mutex s_lock_Stack; + static std::list ringposStack; +#endif + + static Threading::Thread s_thread; + static std::atomic_bool s_open_flag{false}; + static std::atomic_bool s_shutdown_flag{false}; + static std::atomic_bool s_run_idle_flag{false}; + static Threading::UserspaceSemaphore s_open_or_close_done; +} // namespace MTGS + // ===================================================================================================== // MTGS Threaded Class Implementation // ===================================================================================================== -alignas(32) MTGS_BufferedData RingBuffer; - - -#ifdef RINGBUF_DEBUG_STACK -#include -std::list ringposStack; -#endif - -SysMtgsThread::SysMtgsThread() -#ifdef RINGBUF_DEBUG_STACK - : m_lock_Stack() -#endif +const Threading::ThreadHandle& MTGS::GetThreadHandle() { - m_ReadPos = 0; - m_WritePos = 0; - m_packet_size = 0; - m_packet_writepos = 0; - - m_QueuedFrameCount = 0; - m_VsyncSignalListener = false; - m_SignalRingEnable = false; - m_SignalRingPosition = 0; - - m_CopyDataTally = 0; + return s_thread; } -SysMtgsThread::~SysMtgsThread() +bool MTGS::IsOpen() { - ShutdownThread(); + return s_open_flag.load(std::memory_order_acquire); } -void SysMtgsThread::StartThread() +void MTGS::StartThread() { - if (m_thread.joinable()) + if (s_thread.Joinable()) return; - pxAssertRel(!m_open_flag.load(), "GS thread should not be opened when starting"); - m_sem_event.Reset(); - m_shutdown_flag.store(false, std::memory_order_release); - m_thread = std::thread(&SysMtgsThread::ThreadEntryPoint, this); + pxAssertRel(!s_open_flag.load(), "GS thread should not be opened when starting"); + s_sem_event.Reset(); + s_shutdown_flag.store(false, std::memory_order_release); + s_thread.Start(&MTGS::ThreadEntryPoint); } -void SysMtgsThread::ShutdownThread() +void MTGS::ShutdownThread() { - if (!m_thread.joinable()) + if (!s_thread.Joinable()) return; // just go straight to shutdown, don't wait-for-open again - m_shutdown_flag.store(true, std::memory_order_release); + s_shutdown_flag.store(true, std::memory_order_release); if (IsOpen()) WaitForClose(); // make sure the thread actually exits - m_sem_event.NotifyOfWork(); - m_thread.join(); + s_sem_event.NotifyOfWork(); + s_thread.Join(); } -void SysMtgsThread::ThreadEntryPoint() +void MTGS::ThreadEntryPoint() { Threading::SetNameOfCurrentThread("GS"); - m_thread_handle = Threading::ThreadHandle::GetForCallingThread(); - for (;;) { // wait until we're actually asked to initialize (and config has been loaded, etc) - while (!m_open_flag.load(std::memory_order_acquire)) + while (!s_open_flag.load(std::memory_order_acquire)) { - if (m_shutdown_flag.load(std::memory_order_acquire)) + if (s_shutdown_flag.load(std::memory_order_acquire)) { - m_sem_event.Kill(); - m_thread_handle = {}; + s_sem_event.Kill(); return; } - m_sem_event.WaitForWork(); + s_sem_event.WaitForWork(); } // try initializing.. this could fail const bool opened = TryOpenGS(); - m_open_flag.store(opened, std::memory_order_release); + s_open_flag.store(opened, std::memory_order_release); // notify emu thread that we finished opening (or failed) - m_open_or_close_done.Post(); + s_open_or_close_done.Post(); // are we open? if (!opened) @@ -145,32 +193,32 @@ void SysMtgsThread::ThreadEntryPoint() // when we come back here, it's because we closed (or shutdown) // that means the emu thread should be blocked, waiting for us to be done - pxAssertRel(!m_open_flag.load(std::memory_order_relaxed), "Open flag is clear on close"); + pxAssertRel(!s_open_flag.load(std::memory_order_relaxed), "Open flag is clear on close"); CloseGS(); - m_open_or_close_done.Post(); + s_open_or_close_done.Post(); // we need to reset sem_event here, because MainLoop() kills it. - m_sem_event.Reset(); + s_sem_event.Reset(); } GSshutdown(); } -void SysMtgsThread::ResetGS(bool hardware_reset) +void MTGS::ResetGS(bool hardware_reset) { - pxAssertDev(!IsOpen() || (m_ReadPos == m_WritePos), "Must close or terminate the GS thread prior to gsReset."); + pxAssertDev(!IsOpen() || (s_ReadPos == s_WritePos), "Must close or terminate the GS thread prior to gsReset."); // MTGS Reset process: // * clear the ringbuffer. // * Signal a reset. // * clear the path and byRegs structs (used by GIFtagDummy) - m_ReadPos = m_WritePos.load(); - m_QueuedFrameCount = 0; - m_VsyncSignalListener = 0; + s_ReadPos = s_WritePos.load(); + s_QueuedFrameCount = 0; + s_VsyncSignalListener = 0; MTGS_LOG("MTGS: Sending Reset..."); - SendSimplePacket(GS_RINGTYPE_RESET, static_cast(hardware_reset), 0, 0); + SendSimplePacket(Command::Reset, static_cast(hardware_reset), 0, 0); SetEvent(); } @@ -186,7 +234,7 @@ struct RingCmdPacket_Vsync u32 pad[3]; }; -void SysMtgsThread::PostVsyncStart(bool registers_written) +void MTGS::PostVsyncStart(bool registers_written) { // Optimization note: Typically regset1 isn't needed. The regs in that area are typically // changed infrequently, usually during video mode changes. However, on modern systems the @@ -194,20 +242,20 @@ void SysMtgsThread::PostVsyncStart(bool registers_written) // not worth the effort or overhead of trying to selectively avoid it. uint packsize = sizeof(RingCmdPacket_Vsync) / 16; - PrepDataPacket(GS_RINGTYPE_VSYNC, packsize); - MemCopy_WrappedDest((u128*)PS2MEM_GS, RingBuffer.m_Ring, m_packet_writepos, RingBufferSize, 0xf); + PrepDataPacket(Command::VSync, packsize); + MemCopy_WrappedDest((u128*)PS2MEM_GS, RingBuffer.m_Ring, s_packet_writepos, RingBufferSize, 0xf); u32* remainder = (u32*)GetDataPacketPtr(); remainder[0] = GSCSRr; remainder[1] = GSIMR._u32; (GSRegSIGBLID&)remainder[2] = GSSIGLBLID; remainder[4] = static_cast(registers_written); - m_packet_writepos = (m_packet_writepos + 2) & RingBufferMask; + s_packet_writepos = (s_packet_writepos + 2) & RingBufferMask; SendDataPacket(); // Vsyncs should always start the GS thread, regardless of how little has actually be queued. - if (m_CopyDataTally != 0) + if (s_CopyDataTally != 0) SetEvent(); // If the MTGS is allowed to queue a lot of frames in advance, it creates input lag. @@ -221,16 +269,16 @@ void SysMtgsThread::PostVsyncStart(bool registers_written) // If those are needed back, it's better to increase the VsyncQueueSize via PCSX_vm.ini. // (The Xenosaga engine is known to run into this, due to it throwing bulks of data in one frame followed by 2 empty frames.) - if ((m_QueuedFrameCount.fetch_add(1) < EmuConfig.GS.VsyncQueueSize) /*|| (!EmuConfig.GS.VsyncEnable && !EmuConfig.GS.FrameLimitEnable)*/) + if ((s_QueuedFrameCount.fetch_add(1) < EmuConfig.GS.VsyncQueueSize) /*|| (!EmuConfig.GS.VsyncEnable && !EmuConfig.GS.FrameLimitEnable)*/) return; - m_VsyncSignalListener.store(true, std::memory_order_release); + s_VsyncSignalListener.store(true, std::memory_order_release); //Console.WriteLn( Color_Blue, "(EEcore Sleep) Vsync\t\tringpos=0x%06x, writepos=0x%06x", m_ReadPos.load(), m_WritePos.load() ); - m_sem_Vsync.Wait(); + s_sem_Vsync.Wait(); } -void SysMtgsThread::InitAndReadFIFO(u8* mem, u32 qwc) +void MTGS::InitAndReadFIFO(u8* mem, u32 qwc) { if (EmuConfig.GS.HWDownloadMode >= GSHardwareDownloadMode::Unsynchronized && GSConfig.UseHardwareRenderer()) { @@ -242,7 +290,7 @@ void SysMtgsThread::InitAndReadFIFO(u8* mem, u32 qwc) return; } - SendPointerPacket(GS_RINGTYPE_INIT_AND_READ_FIFO, qwc, mem); + SendPointerPacket(Command::InitAndReadFIFO, qwc, mem); WaitGS(false, false, false); } @@ -261,7 +309,7 @@ union PacketTagType }; }; -bool SysMtgsThread::TryOpenGS() +bool MTGS::TryOpenGS() { std::memcpy(RingBuffer.Regs, PS2MEM_GS, sizeof(PS2MEM_GS)); @@ -272,7 +320,7 @@ bool SysMtgsThread::TryOpenGS() return true; } -void SysMtgsThread::MainLoop() +void MTGS::MainLoop() { // Threading info: run in MTGS thread // m_ReadPos is only update by the MTGS thread so it is safe to load it with a relaxed atomic @@ -281,13 +329,13 @@ void SysMtgsThread::MainLoop() PacketTagType prevCmd; #endif - std::unique_lock mtvu_lock(m_mtx_RingBufferBusy2); + std::unique_lock mtvu_lock(s_mtx_RingBufferBusy2); while (true) { - if (m_run_idle_flag.load(std::memory_order_acquire) && VMManager::GetState() != VMState::Running) + if (s_run_idle_flag.load(std::memory_order_acquire) && VMManager::GetState() != VMState::Running) { - if (!m_sem_event.CheckForWork()) + if (!s_sem_event.CheckForWork()) { GSPresentCurrentFrame(); GSThrottlePresentation(); @@ -296,18 +344,18 @@ void SysMtgsThread::MainLoop() else { mtvu_lock.unlock(); - m_sem_event.WaitForWork(); + s_sem_event.WaitForWork(); mtvu_lock.lock(); } - if (!m_open_flag.load(std::memory_order_acquire)) + if (!s_open_flag.load(std::memory_order_acquire)) break; // note: m_ReadPos is intentionally not volatile, because it should only // ever be modified by this thread. - while (m_ReadPos.load(std::memory_order_relaxed) != m_WritePos.load(std::memory_order_acquire)) + while (s_ReadPos.load(std::memory_order_relaxed) != s_WritePos.load(std::memory_order_acquire)) { - const unsigned int local_ReadPos = m_ReadPos.load(std::memory_order_relaxed); + const unsigned int local_ReadPos = s_ReadPos.load(std::memory_order_relaxed); pxAssert(local_ReadPos < RingBufferSize); @@ -317,7 +365,7 @@ void SysMtgsThread::MainLoop() #ifdef RINGBUF_DEBUG_STACK // pop a ringpos off the stack. It should match this one! - m_lock_Stack.Lock(); + s_lock_Stack.Lock(); uptr stackpos = ringposStack.back(); if (stackpos != local_ReadPos) { @@ -326,13 +374,13 @@ void SysMtgsThread::MainLoop() pxAssert(stackpos == local_ReadPos); prevCmd = tag; ringposStack.pop_back(); - m_lock_Stack.Release(); + s_lock_Stack.Release(); #endif - switch (tag.command) + switch (static_cast(tag.command)) { #if COPY_GS_PACKET_TO_MTGS == 1 - case GS_RINGTYPE_P1: + case Command::GIFPath1: { uint datapos = (local_ReadPos + 1) & RingBufferMask; const int qsize = tag.data[0]; @@ -357,7 +405,7 @@ void SysMtgsThread::MainLoop() } break; - case GS_RINGTYPE_P2: + case Command::GIFPath2: { uint datapos = (local_ReadPos + 1) & RingBufferMask; const int qsize = tag.data[0]; @@ -382,7 +430,7 @@ void SysMtgsThread::MainLoop() } break; - case GS_RINGTYPE_P3: + case Command::GIFPath3: { uint datapos = (local_ReadPos + 1) & RingBufferMask; const int qsize = tag.data[0]; @@ -407,7 +455,7 @@ void SysMtgsThread::MainLoop() } break; #endif - case GS_RINGTYPE_GSPACKET: + case Command::GSPacket: { Gif_Path& path = gifUnit.gifPath[tag.data[2]]; u32 offset = tag.data[0]; @@ -418,7 +466,7 @@ void SysMtgsThread::MainLoop() break; } - case GS_RINGTYPE_MTVU_GSPACKET: + case Command::MTVUGSPacket: { MTVU_LOG("MTGS - Waiting on semaXGkick!"); if (!vu1Thread.semaXGkick.TryWait()) @@ -439,9 +487,9 @@ void SysMtgsThread::MainLoop() default: { - switch (tag.command) + switch (static_cast(tag.command)) { - case GS_RINGTYPE_VSYNC: + case Command::VSync: { const int qsize = tag.data[0]; ringposinc += qsize; @@ -463,9 +511,9 @@ void SysMtgsThread::MainLoop() // CSR & 0x2000; is the pageflip id. GSvsync((((u32&)RingBuffer.Regs[0x1000]) & 0x2000) ? 0 : 1, remainder[4] != 0); - m_QueuedFrameCount.fetch_sub(1); - if (m_VsyncSignalListener.exchange(false)) - m_sem_Vsync.Post(); + s_QueuedFrameCount.fetch_sub(1); + if (s_VsyncSignalListener.exchange(false)) + s_sem_Vsync.Post(); // Do not StateCheckInThread() here // Otherwise we could pause while there's still data in the queue @@ -473,7 +521,7 @@ void SysMtgsThread::MainLoop() } break; - case GS_RINGTYPE_ASYNC_CALL: + case Command::AsyncCall: { AsyncCallType* const func = (AsyncCallType*)tag.pointer; (*func)(); @@ -481,20 +529,20 @@ void SysMtgsThread::MainLoop() } break; - case GS_RINGTYPE_FREEZE: + case Command::Freeze: { - MTGS_FreezeData* data = (MTGS_FreezeData*)tag.pointer; + MTGS::FreezeData* data = (MTGS::FreezeData*)tag.pointer; int mode = tag.data[0]; data->retval = GSfreeze((FreezeAction)mode, (freezeData*)data->fdata); } break; - case GS_RINGTYPE_RESET: + case Command::Reset: MTGS_LOG("(MTGS Packet Read) ringtype=Reset"); GSreset(tag.data[0] != 0); break; - case GS_RINGTYPE_SOFTRESET: + case Command::SoftReset: { int mask = tag.data[0]; MTGS_LOG("(MTGS Packet Read) ringtype=SoftReset"); @@ -502,20 +550,20 @@ void SysMtgsThread::MainLoop() } break; - case GS_RINGTYPE_CRC: + case Command::CRC: GSSetGameCRC(tag.data[0]); break; - case GS_RINGTYPE_INIT_AND_READ_FIFO: + case Command::InitAndReadFIFO: MTGS_LOG("(MTGS Packet Read) ringtype=Fifo2, size=%d", tag.data[0]); GSInitAndReadFIFO((u8*)tag.pointer, tag.data[0]); break; #ifdef PCSX2_DEVBUILD default: - Console.Error("GSThreadProc, bad packet (%x) at m_ReadPos: %x, m_WritePos: %x", tag.command, local_ReadPos, m_WritePos.load()); + Console.Error("GSThreadProc, bad packet (%x) at m_ReadPos: %x, m_WritePos: %x", tag.command, local_ReadPos, s_WritePos.load()); pxFail("Bad packet encountered in the MTGS Ringbuffer."); - m_ReadPos.store(m_WritePos.load(std::memory_order_acquire), std::memory_order_release); + s_ReadPos.store(s_WritePos.load(std::memory_order_acquire), std::memory_order_release); continue; #else // Optimized performance in non-Dev builds. @@ -525,23 +573,23 @@ void SysMtgsThread::MainLoop() } } - uint newringpos = (m_ReadPos.load(std::memory_order_relaxed) + ringposinc) & RingBufferMask; + uint newringpos = (s_ReadPos.load(std::memory_order_relaxed) + ringposinc) & RingBufferMask; if (EmuConfig.GS.SynchronousMTGS) { - pxAssert(m_WritePos == newringpos); + pxAssert(s_WritePos == newringpos); } - m_ReadPos.store(newringpos, std::memory_order_release); + s_ReadPos.store(newringpos, std::memory_order_release); - if (m_SignalRingEnable.load(std::memory_order_acquire)) + if (s_SignalRingEnable.load(std::memory_order_acquire)) { // The EEcore has requested a signal after some amount of processed data. - if (m_SignalRingPosition.fetch_sub(ringposinc) <= 0) + if (s_SignalRingPosition.fetch_sub(ringposinc) <= 0) { // Make sure to post the signal after the m_ReadPos has been updated... - m_SignalRingEnable.store(false, std::memory_order_release); - m_sem_OnRingReset.Post(); + s_SignalRingEnable.store(false, std::memory_order_release); + s_sem_OnRingReset.Post(); continue; } } @@ -553,25 +601,25 @@ void SysMtgsThread::MainLoop() // won't sleep the eternity, even if SignalRingPosition didn't reach 0 for some reason. // Important: Need to unlock the MTGS busy signal PRIOR, so that EEcore SetEvent() calls // parallel to this handler aren't accidentally blocked. - if (m_SignalRingEnable.exchange(false)) + if (s_SignalRingEnable.exchange(false)) { //Console.Warning( "(MTGS Thread) Dangling RingSignal on empty buffer! signalpos=0x%06x", m_SignalRingPosition.exchange(0) ) ); - m_SignalRingPosition.store(0, std::memory_order_release); - m_sem_OnRingReset.Post(); + s_SignalRingPosition.store(0, std::memory_order_release); + s_sem_OnRingReset.Post(); } - if (m_VsyncSignalListener.exchange(false)) - m_sem_Vsync.Post(); + if (s_VsyncSignalListener.exchange(false)) + s_sem_Vsync.Post(); //Console.Warning( "(MTGS Thread) Nothing to do! ringpos=0x%06x", m_ReadPos ); } // Unblock any threads in WaitGS in case MTGS gets cancelled while still processing work - m_ReadPos.store(m_WritePos.load(std::memory_order_acquire), std::memory_order_relaxed); - m_sem_event.Kill(); + s_ReadPos.store(s_WritePos.load(std::memory_order_acquire), std::memory_order_relaxed); + s_sem_event.Kill(); } -void SysMtgsThread::CloseGS() +void MTGS::CloseGS() { GSclose(); } @@ -580,9 +628,8 @@ void SysMtgsThread::CloseGS() // If syncRegs, then writes pcsx2's gs regs to MTGS's internal copy // If weakWait, then this function is allowed to exit after MTGS finished a path1 packet // If isMTVU, then this implies this function is being called from the MTVU thread... -void SysMtgsThread::WaitGS(bool syncRegs, bool weakWait, bool isMTVU) +void MTGS::WaitGS(bool syncRegs, bool weakWait, bool isMTVU) { - pxAssertDev(std::this_thread::get_id() != m_thread.get_id(), "This method is only allowed from threads *not* named MTGS."); if (!pxAssertDev(IsOpen(), "MTGS Warning! WaitGS issued on a closed thread.")) return; @@ -606,8 +653,8 @@ void SysMtgsThread::WaitGS(bool syncRegs, bool weakWait, bool isMTVU) while (true) { // m_mtx_RingBufferBusy2.Wait(); - m_mtx_RingBufferBusy2.lock(); - m_mtx_RingBufferBusy2.unlock(); + s_mtx_RingBufferBusy2.lock(); + s_mtx_RingBufferBusy2.unlock(); if (path.GetPendingGSPackets() != startP1Packs) break; } @@ -615,7 +662,7 @@ void SysMtgsThread::WaitGS(bool syncRegs, bool weakWait, bool isMTVU) } else { - if (!m_sem_event.WaitForEmpty()) + if (!s_sem_event.WaitForEmpty()) pxFailRel("MTGS Thread Died"); } @@ -630,31 +677,31 @@ void SysMtgsThread::WaitGS(bool syncRegs, bool weakWait, bool isMTVU) // Sets the gsEvent flag and releases a timeslice. // For use in loops that wait on the GS thread to do certain things. -void SysMtgsThread::SetEvent() +void MTGS::SetEvent() { - m_sem_event.NotifyOfWork(); - m_CopyDataTally = 0; + s_sem_event.NotifyOfWork(); + s_CopyDataTally = 0; } -u8* SysMtgsThread::GetDataPacketPtr() const +u8* MTGS::GetDataPacketPtr() { - return (u8*)&RingBuffer[m_packet_writepos & RingBufferMask]; + return (u8*)&RingBuffer[s_packet_writepos & RingBufferMask]; } // Closes the data packet send command, and initiates the gs thread (if needed). -void SysMtgsThread::SendDataPacket() +void MTGS::SendDataPacket() { // make sure a previous copy block has been started somewhere. - pxAssert(m_packet_size != 0); + pxAssert(s_packet_size != 0); - uint actualSize = ((m_packet_writepos - m_packet_startpos) & RingBufferMask) - 1; - pxAssert(actualSize <= m_packet_size); - pxAssert(m_packet_writepos < RingBufferSize); + uint actualSize = ((s_packet_writepos - s_packet_startpos) & RingBufferMask) - 1; + pxAssert(actualSize <= s_packet_size); + pxAssert(s_packet_writepos < RingBufferSize); - PacketTagType& tag = (PacketTagType&)RingBuffer[m_packet_startpos]; + PacketTagType& tag = (PacketTagType&)RingBuffer[s_packet_startpos]; tag.data[0] = actualSize; - m_WritePos.store(m_packet_writepos, std::memory_order_release); + s_WritePos.store(s_packet_writepos, std::memory_order_release); if (EmuConfig.GS.SynchronousMTGS) { @@ -662,22 +709,22 @@ void SysMtgsThread::SendDataPacket() } else { - m_CopyDataTally += m_packet_size; - if (m_CopyDataTally > 0x2000) + s_CopyDataTally += s_packet_size; + if (s_CopyDataTally > 0x2000) SetEvent(); } - m_packet_size = 0; + s_packet_size = 0; //m_PacketLocker.Release(); } -void SysMtgsThread::GenericStall(uint size) +void MTGS::GenericStall(uint size) { // Note on volatiles: m_WritePos is not modified by the GS thread, so there's no need // to use volatile reads here. We do cache it though, since we know it never changes, // except for calls to RingbufferRestert() -- handled below. - const uint writepos = m_WritePos.load(std::memory_order_relaxed); + const uint writepos = s_WritePos.load(std::memory_order_relaxed); // Sanity checks! (within the confines of our ringbuffer please!) pxAssert(size < RingBufferSize); @@ -688,7 +735,7 @@ void SysMtgsThread::GenericStall(uint size) // But if not then we need to make sure the readpos is outside the scope of // the block about to be written (writepos + size) - uint readpos = m_ReadPos.load(std::memory_order_acquire); + uint readpos = s_ReadPos.load(std::memory_order_acquire); uint freeroom; if (writepos < readpos) @@ -716,17 +763,17 @@ void SysMtgsThread::GenericStall(uint size) if (somedone > 0x80) { - pxAssertDev(m_SignalRingEnable == 0, "MTGS Thread Synchronization Error"); - m_SignalRingPosition.store(somedone, std::memory_order_release); + pxAssertDev(s_SignalRingEnable == 0, "MTGS Thread Synchronization Error"); + s_SignalRingPosition.store(somedone, std::memory_order_release); //Console.WriteLn( Color_Blue, "(EEcore Sleep) PrepDataPacker \tringpos=0x%06x, writepos=0x%06x, signalpos=0x%06x", readpos, writepos, m_SignalRingPosition ); while (true) { - m_SignalRingEnable.store(true, std::memory_order_release); + s_SignalRingEnable.store(true, std::memory_order_release); SetEvent(); - m_sem_OnRingReset.Wait(); - readpos = m_ReadPos.load(std::memory_order_acquire); + s_sem_OnRingReset.Wait(); + readpos = s_ReadPos.load(std::memory_order_acquire); //Console.WriteLn( Color_Blue, "(EEcore Awake) Report!\tringpos=0x%06x", readpos ); if (writepos < readpos) @@ -738,7 +785,7 @@ void SysMtgsThread::GenericStall(uint size) break; } - pxAssertDev(m_SignalRingPosition <= 0, "MTGS Thread Synchronization Error"); + pxAssertDev(s_SignalRingPosition <= 0, "MTGS Thread Synchronization Error"); } else { @@ -746,8 +793,8 @@ void SysMtgsThread::GenericStall(uint size) SetEvent(); while (true) { - SpinWait(); - readpos = m_ReadPos.load(std::memory_order_acquire); + Threading::SpinWait(); + readpos = s_ReadPos.load(std::memory_order_acquire); if (writepos < readpos) freeroom = readpos - writepos; @@ -761,21 +808,21 @@ void SysMtgsThread::GenericStall(uint size) } } -void SysMtgsThread::PrepDataPacket(MTGS_RingCommand cmd, u32 size) +void MTGS::PrepDataPacket(Command cmd, u32 size) { - m_packet_size = size; + s_packet_size = size; ++size; // takes into account our RingCommand QWC. GenericStall(size); // Command qword: Low word is the command, and the high word is the packet // length in SIMDs (128 bits). - const unsigned int local_WritePos = m_WritePos.load(std::memory_order_relaxed); + const unsigned int local_WritePos = s_WritePos.load(std::memory_order_relaxed); PacketTagType& tag = (PacketTagType&)RingBuffer[local_WritePos]; - tag.command = cmd; - tag.data[0] = m_packet_size; - m_packet_startpos = local_WritePos; - m_packet_writepos = (local_WritePos + 1) & RingBufferMask; + tag.command = static_cast(cmd); + tag.data[0] = s_packet_size; + s_packet_startpos = local_WritePos; + s_packet_writepos = (local_WritePos + 1) & RingBufferMask; } // Returns the amount of giftag data processed (in simd128 values). @@ -783,33 +830,33 @@ void SysMtgsThread::PrepDataPacket(MTGS_RingCommand cmd, u32 size) // around VU memory instead of having buffer overflow... // Parameters: // size - size of the packet data, in smd128's -void SysMtgsThread::PrepDataPacket(GIF_PATH pathidx, u32 size) +void MTGS::PrepDataPacket(GIF_PATH pathidx, u32 size) { //m_PacketLocker.Acquire(); - PrepDataPacket((MTGS_RingCommand)pathidx, size); + PrepDataPacket(static_cast(pathidx), size); } -__fi void SysMtgsThread::_FinishSimplePacket() +__fi void MTGS::_FinishSimplePacket() { - uint future_writepos = (m_WritePos.load(std::memory_order_relaxed) + 1) & RingBufferMask; - pxAssert(future_writepos != m_ReadPos.load(std::memory_order_acquire)); - m_WritePos.store(future_writepos, std::memory_order_release); + uint future_writepos = (s_WritePos.load(std::memory_order_relaxed) + 1) & RingBufferMask; + pxAssert(future_writepos != s_ReadPos.load(std::memory_order_acquire)); + s_WritePos.store(future_writepos, std::memory_order_release); if (EmuConfig.GS.SynchronousMTGS) WaitGS(); else - ++m_CopyDataTally; + ++s_CopyDataTally; } -void SysMtgsThread::SendSimplePacket(MTGS_RingCommand type, int data0, int data1, int data2) +void MTGS::SendSimplePacket(Command type, int data0, int data1, int data2) { //ScopedLock locker( m_PacketLocker ); GenericStall(1); - PacketTagType& tag = (PacketTagType&)RingBuffer[m_WritePos.load(std::memory_order_relaxed)]; + PacketTagType& tag = (PacketTagType&)RingBuffer[s_WritePos.load(std::memory_order_relaxed)]; - tag.command = type; + tag.command = static_cast(type); tag.data[0] = data0; tag.data[1] = data1; tag.data[2] = data2; @@ -817,38 +864,38 @@ void SysMtgsThread::SendSimplePacket(MTGS_RingCommand type, int data0, int data1 _FinishSimplePacket(); } -void SysMtgsThread::SendSimpleGSPacket(MTGS_RingCommand type, u32 offset, u32 size, GIF_PATH path) +void MTGS::SendSimpleGSPacket(Command type, u32 offset, u32 size, GIF_PATH path) { SendSimplePacket(type, (int)offset, (int)size, (int)path); if (!EmuConfig.GS.SynchronousMTGS) { - m_CopyDataTally += size / 16; - if (m_CopyDataTally > 0x2000) + s_CopyDataTally += size / 16; + if (s_CopyDataTally > 0x2000) SetEvent(); } } -void SysMtgsThread::SendPointerPacket(MTGS_RingCommand type, u32 data0, void* data1) +void MTGS::SendPointerPacket(Command type, u32 data0, void* data1) { //ScopedLock locker( m_PacketLocker ); GenericStall(1); - PacketTagType& tag = (PacketTagType&)RingBuffer[m_WritePos.load(std::memory_order_relaxed)]; + PacketTagType& tag = (PacketTagType&)RingBuffer[s_WritePos.load(std::memory_order_relaxed)]; - tag.command = type; + tag.command = static_cast(type); tag.data[0] = data0; tag.pointer = (uptr)data1; _FinishSimplePacket(); } -void SysMtgsThread::SendGameCRC(u32 crc) +void MTGS::SendGameCRC(u32 crc) { - SendSimplePacket(GS_RINGTYPE_CRC, crc, 0, 0); + SendSimplePacket(Command::CRC, crc, 0, 0); } -bool SysMtgsThread::WaitForOpen() +bool MTGS::WaitForOpen() { if (IsOpen()) return true; @@ -856,57 +903,56 @@ bool SysMtgsThread::WaitForOpen() StartThread(); // request open, and kick the thread. - m_open_flag.store(true, std::memory_order_release); - m_sem_event.NotifyOfWork(); + s_open_flag.store(true, std::memory_order_release); + s_sem_event.NotifyOfWork(); // wait for it to finish its stuff - m_open_or_close_done.Wait(); + s_open_or_close_done.Wait(); // did we succeed? - const bool result = m_open_flag.load(std::memory_order_acquire); + const bool result = s_open_flag.load(std::memory_order_acquire); if (!result) Console.Error("GS failed to open."); return result; } -void SysMtgsThread::WaitForClose() +void MTGS::WaitForClose() { if (!IsOpen()) return; // ask the thread to stop processing work, by clearing the open flag - m_open_flag.store(false, std::memory_order_release); + s_open_flag.store(false, std::memory_order_release); // and kick the thread if it's sleeping - m_sem_event.NotifyOfWork(); + s_sem_event.NotifyOfWork(); // and wait for it to finish up.. - m_open_or_close_done.Wait(); + s_open_or_close_done.Wait(); } -void SysMtgsThread::Freeze(FreezeAction mode, MTGS_FreezeData& data) +void MTGS::Freeze(FreezeAction mode, MTGS::FreezeData& data) { pxAssertRel(IsOpen(), "GS thread is open"); - pxAssertDev(std::this_thread::get_id() != m_thread.get_id(), "This method is only allowed from threads *not* named MTGS."); // synchronize regs before loading if (mode == FreezeAction::Load) WaitGS(true); - SendPointerPacket(GS_RINGTYPE_FREEZE, (int)mode, &data); + SendPointerPacket(Command::Freeze, (int)mode, &data); WaitGS(false); } -void SysMtgsThread::RunOnGSThread(AsyncCallType func) +void MTGS::RunOnGSThread(AsyncCallType func) { - SendPointerPacket(GS_RINGTYPE_ASYNC_CALL, 0, new AsyncCallType(std::move(func))); + SendPointerPacket(Command::AsyncCall, 0, new AsyncCallType(std::move(func))); // wake the gs thread in case it's sleeping SetEvent(); } -void SysMtgsThread::ApplySettings() +void MTGS::ApplySettings() { pxAssertRel(IsOpen(), "MTGS is running"); @@ -922,7 +968,7 @@ void SysMtgsThread::ApplySettings() WaitGS(false, false, false); } -void SysMtgsThread::ResizeDisplayWindow(int width, int height, float scale) +void MTGS::ResizeDisplayWindow(int width, int height, float scale) { pxAssertRel(IsOpen(), "MTGS is running"); RunOnGSThread([width, height, scale]() { @@ -934,7 +980,7 @@ void SysMtgsThread::ResizeDisplayWindow(int width, int height, float scale) }); } -void SysMtgsThread::UpdateDisplayWindow() +void MTGS::UpdateDisplayWindow() { pxAssertRel(IsOpen(), "MTGS is running"); RunOnGSThread([]() { @@ -946,7 +992,7 @@ void SysMtgsThread::UpdateDisplayWindow() }); } -void SysMtgsThread::SetVSyncMode(VsyncMode mode) +void MTGS::SetVSyncMode(VsyncMode mode) { pxAssertRel(IsOpen(), "MTGS is running"); @@ -956,12 +1002,12 @@ void SysMtgsThread::SetVSyncMode(VsyncMode mode) }); } -void SysMtgsThread::UpdateVSyncMode() +void MTGS::UpdateVSyncMode() { SetVSyncMode(Host::GetEffectiveVSyncMode()); } -void SysMtgsThread::SwitchRenderer(GSRendererType renderer, bool display_message /* = true */) +void MTGS::SwitchRenderer(GSRendererType renderer, bool display_message /* = true */) { pxAssertRel(IsOpen(), "MTGS is running"); @@ -980,7 +1026,7 @@ void SysMtgsThread::SwitchRenderer(GSRendererType renderer, bool display_message WaitGS(false, false, false); } -void SysMtgsThread::SetSoftwareRendering(bool software, bool display_message /* = true */) +void MTGS::SetSoftwareRendering(bool software, bool display_message /* = true */) { // for hardware, use the chosen api in the base config, or auto if base is set to sw GSRendererType new_renderer; @@ -992,13 +1038,13 @@ void SysMtgsThread::SetSoftwareRendering(bool software, bool display_message /* SwitchRenderer(new_renderer, display_message); } -void SysMtgsThread::ToggleSoftwareRendering() +void MTGS::ToggleSoftwareRendering() { // reading from the GS thread.. but should be okay here SetSoftwareRendering(GSConfig.Renderer != GSRendererType::SW); } -bool SysMtgsThread::SaveMemorySnapshot(u32 window_width, u32 window_height, bool apply_aspect, bool crop_borders, +bool MTGS::SaveMemorySnapshot(u32 window_width, u32 window_height, bool apply_aspect, bool crop_borders, u32* width, u32* height, std::vector* pixels) { bool result = false; @@ -1009,9 +1055,9 @@ bool SysMtgsThread::SaveMemorySnapshot(u32 window_width, u32 window_height, bool return result; } -void SysMtgsThread::PresentCurrentFrame() +void MTGS::PresentCurrentFrame() { - if (m_run_idle_flag.load(std::memory_order_relaxed)) + if (s_run_idle_flag.load(std::memory_order_relaxed)) { // If we're running idle, we're going to re-present anyway. return; @@ -1022,8 +1068,44 @@ void SysMtgsThread::PresentCurrentFrame() }); } -void SysMtgsThread::SetRunIdle(bool enabled) +void MTGS::SetRunIdle(bool enabled) { // NOTE: Should only be called on the GS thread. - m_run_idle_flag.store(enabled, std::memory_order_release); + s_run_idle_flag.store(enabled, std::memory_order_release); } + +// Used in MTVU mode... MTVU will later complete a real packet +void Gif_AddGSPacketMTVU(GS_Packet& gsPack, GIF_PATH path) +{ + MTGS::SendSimpleGSPacket(MTGS::Command::MTVUGSPacket, 0, 0, path); +} + +void Gif_AddCompletedGSPacket(GS_Packet& gsPack, GIF_PATH path) +{ + //DevCon.WriteLn("Adding Completed Gif Packet [size=%x]", gsPack.size); + if (COPY_GS_PACKET_TO_MTGS) + { + MTGS::PrepDataPacket(path, gsPack.size / 16); + MemCopy_WrappedDest((u128*)&gifUnit.gifPath[path].buffer[gsPack.offset], MTGS::RingBuffer.m_Ring, + MTGS::s_packet_writepos, MTGS::RingBufferSize, gsPack.size / 16); + MTGS::SendDataPacket(); + } + else + { + pxAssertDev(!gsPack.readAmount, "Gif Unit - gsPack.readAmount only valid for MTVU path 1!"); + gifUnit.gifPath[path].readAmount.fetch_add(gsPack.size); + MTGS::SendSimpleGSPacket(MTGS::Command::GSPacket, gsPack.offset, gsPack.size, path); + } +} + +void Gif_AddBlankGSPacket(u32 size, GIF_PATH path) +{ + //DevCon.WriteLn("Adding Blank Gif Packet [size=%x]", size); + gifUnit.gifPath[path].readAmount.fetch_add(size); + MTGS::SendSimpleGSPacket(MTGS::Command::GSPacket, ~0u, size, path); +} + +void Gif_MTGS_Wait(bool isMTVU) +{ + MTGS::WaitGS(false, true, isMTVU); +} \ No newline at end of file diff --git a/pcsx2/MTGS.h b/pcsx2/MTGS.h new file mode 100644 index 0000000000..43f967b209 --- /dev/null +++ b/pcsx2/MTGS.h @@ -0,0 +1,107 @@ +/* PCSX2 - PS2 Emulator for PCs + * Copyright (C) 2002-2023 PCSX2 Dev Team + * + * PCSX2 is free software: you can redistribute it and/or modify it under the terms + * of the GNU Lesser General Public License as published by the Free Software Found- + * ation, either version 3 of the License, or (at your option) any later version. + * + * PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with PCSX2. + * If not, see . + */ + +#pragma once + +#include "GS.h" + +#include + +///////////////////////////////////////////////////////////////////////////// +// MTGS Threaded Class Declaration + +// Uncomment this to enable the MTGS debug stack, which tracks to ensure reads +// and writes stay synchronized. Warning: the debug stack is VERY slow. +//#define RINGBUF_DEBUG_STACK + +namespace MTGS +{ + using AsyncCallType = std::function; + + enum class Command : u32 + { + GIFPath1, + GIFPath2, + GIFPath3, + VSync, + Freeze, + Reset, // issues a GSreset() command. + SoftReset, // issues a soft reset for the GIF + CRC, + GSPacket, + MTVUGSPacket, + InitAndReadFIFO, + AsyncCall, + }; + + struct FreezeData + { + freezeData* fdata; + s32 retval; // value returned from the call, valid only after an mtgsWaitGS() + }; + + const Threading::ThreadHandle& GetThreadHandle(); + bool IsOpen(); + + /// Starts the thread, if it hasn't already been started. + void StartThread(); + + /// Fully stops the thread, closing in the process if needed. + void ShutdownThread(); + + /// Re-presents the current frame. Call when things like window resizes happen to re-display + /// the current frame with the correct proportions. Should only be called from the CPU thread. + void PresentCurrentFrame(); + + // Waits for the GS to empty out the entire ring buffer contents. + void WaitGS(bool syncRegs = true, bool weakWait = false, bool isMTVU = false); + void ResetGS(bool hardware_reset); + + void SendGameCRC(u32 crc); + bool WaitForOpen(); + void WaitForClose(); + void Freeze(FreezeAction mode, FreezeData& data); + + void SendSimplePacket(Command type, int data0, int data1, int data2); + + void PostVsyncStart(bool registers_written); + void InitAndReadFIFO(u8* mem, u32 qwc); + + void RunOnGSThread(AsyncCallType func); + void ApplySettings(); + void ResizeDisplayWindow(int width, int height, float scale); + void UpdateDisplayWindow(); + void SetVSyncMode(VsyncMode mode); + void UpdateVSyncMode(); + void SwitchRenderer(GSRendererType renderer, bool display_message = true); + void SetSoftwareRendering(bool software, bool display_message = true); + void ToggleSoftwareRendering(); + bool SaveMemorySnapshot(u32 window_width, u32 window_height, bool apply_aspect, bool crop_borders, + u32* width, u32* height, std::vector* pixels); + void SetRunIdle(bool enabled); + + // Size of the ringbuffer as a power of 2 -- size is a multiple of simd128s. + // (actual size is 1<(s_frames_since_last_update)); const u64 cpu_time = s_cpu_thread_handle.GetCPUTime(); - const u64 gs_time = GetMTGS().GetThreadHandle().GetCPUTime(); + const u64 gs_time = MTGS::GetThreadHandle().GetCPUTime(); const u64 vu_time = THREAD_VU1 ? vu1Thread.GetThreadHandle().GetCPUTime() : 0; const u64 capture_time = GSCapture::IsCapturing() ? GSCapture::GetEncoderThreadHandle().GetCPUTime() : 0; diff --git a/pcsx2/R5900.cpp b/pcsx2/R5900.cpp index d13da7d076..46c515c4c9 100644 --- a/pcsx2/R5900.cpp +++ b/pcsx2/R5900.cpp @@ -1,5 +1,5 @@ /* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2010 PCSX2 Dev Team + * Copyright (C) 2002-2023 PCSX2 Dev Team * * PCSX2 is free software: you can redistribute it and/or modify it under the terms * of the GNU Lesser General Public License as published by the Free Software Found- @@ -70,11 +70,6 @@ extern SysMainMemory& GetVmMemory(); void cpuReset() { - vu1Thread.WaitVU(); - vu1Thread.Reset(); - if (GetMTGS().IsOpen()) - GetMTGS().WaitGS(); // GS better be done processing before we reset the EE, just in case. - GetVmMemory().Reset(); std::memset(&cpuRegs, 0, sizeof(cpuRegs)); diff --git a/pcsx2/Recording/InputRecording.cpp b/pcsx2/Recording/InputRecording.cpp index 427b6abeed..255abac645 100644 --- a/pcsx2/Recording/InputRecording.cpp +++ b/pcsx2/Recording/InputRecording.cpp @@ -1,5 +1,5 @@ /* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2022 PCSX2 Dev Team + * Copyright (C) 2002-2023 PCSX2 Dev Team * * PCSX2 is free software: you can redistribute it and/or modify it under the terms * of the GNU Lesser General Public License as published by the Free Software Found- @@ -15,8 +15,9 @@ #include "PrecompiledHeader.h" -#include "SaveState.h" #include "Counters.h" +#include "MTGS.h" +#include "SaveState.h" void SaveStateBase::InputRecordingFreeze() { @@ -146,7 +147,7 @@ void InputRecording::closeActiveFile() { m_is_active = false; InputRec::log("Input recording stopped"); - GetMTGS().PresentCurrentFrame(); + MTGS::PresentCurrentFrame(); } else { diff --git a/pcsx2/Recording/InputRecordingControls.cpp b/pcsx2/Recording/InputRecordingControls.cpp index b7a4adfae5..f19b92bba6 100644 --- a/pcsx2/Recording/InputRecordingControls.cpp +++ b/pcsx2/Recording/InputRecordingControls.cpp @@ -22,7 +22,7 @@ #include "InputRecordingControls.h" #include "Utilities/InputRecordingLogger.h" -#include "GS.h" +#include "MTGS.h" #include "VMManager.h" void InputRecordingControls::toggleRecordMode() @@ -43,7 +43,7 @@ void InputRecordingControls::setRecordMode(bool waitForFrameToEnd) { m_state = Mode::Recording; InputRec::log("Record mode ON"); - GetMTGS().PresentCurrentFrame(); + MTGS::PresentCurrentFrame(); } else { @@ -60,7 +60,7 @@ void InputRecordingControls::setReplayMode(bool waitForFrameToEnd) { m_state = Mode::Replaying; InputRec::log("Replay mode ON"); - GetMTGS().PresentCurrentFrame(); + MTGS::PresentCurrentFrame(); } else { @@ -86,7 +86,7 @@ void InputRecordingControls::processControlQueue() m_controlQueue.front()(); m_controlQueue.pop(); } - GetMTGS().PresentCurrentFrame(); + MTGS::PresentCurrentFrame(); } } diff --git a/pcsx2/SPU2/spu2.cpp b/pcsx2/SPU2/spu2.cpp index 88700e4951..661904aa3f 100644 --- a/pcsx2/SPU2/spu2.cpp +++ b/pcsx2/SPU2/spu2.cpp @@ -1,5 +1,5 @@ /* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2023 PCSX2 Dev Team + * Copyright (C) 2002-2023 PCSX2 Dev Team * * PCSX2 is free software: you can redistribute it and/or modify it under the terms * of the GNU Lesser General Public License as published by the Free Software Found- @@ -18,8 +18,8 @@ #include "SPU2/Debug.h" #include "SPU2/spu2.h" #include "SPU2/Dma.h" -#include "GS.h" #include "GS/GSCapture.h" +#include "MTGS.h" #include "R3000A.h" namespace SPU2 @@ -134,8 +134,8 @@ void SPU2::UpdateSampleRate() // Can't be capturing when the sample rate changes. if (IsAudioCaptureActive()) { - GetMTGS().RunOnGSThread(&GSEndCapture); - GetMTGS().WaitGS(false, false, false); + MTGS::RunOnGSThread(&GSEndCapture); + MTGS::WaitGS(false, false, false); } } diff --git a/pcsx2/SaveState.cpp b/pcsx2/SaveState.cpp index bab4a2be6e..82fdb78e27 100644 --- a/pcsx2/SaveState.cpp +++ b/pcsx2/SaveState.cpp @@ -26,6 +26,7 @@ #include "GS.h" #include "GS/GS.h" #include "Host.h" +#include "MTGS.h" #include "MTVU.h" #include "PAD/Host/PAD.h" #include "Patch.h" @@ -59,7 +60,7 @@ static void PreLoadPrep() // ensure everything is in sync before we start overwriting stuff. if (THREAD_VU1) vu1Thread.WaitVU(); - GetMTGS().WaitGS(false); + MTGS::WaitGS(false); // backup current TLBs, since we're going to overwrite them all std::memcpy(s_tlb_backup, tlb, sizeof(s_tlb_backup)); @@ -322,8 +323,8 @@ struct SysState_Component static int SysState_MTGSFreeze(FreezeAction mode, freezeData* fP) { - MTGS_FreezeData sstate = { fP, 0 }; - GetMTGS().Freeze(mode, sstate); + MTGS::FreezeData sstate = { fP, 0 }; + MTGS::Freeze(mode, sstate); return sstate.retval; } @@ -709,7 +710,7 @@ std::unique_ptr SaveState_SaveScreenshot() u32 width, height; std::vector pixels; - if (!GetMTGS().SaveMemorySnapshot(SCREENSHOT_WIDTH, SCREENSHOT_HEIGHT, true, false, &width, &height, &pixels)) + if (!MTGS::SaveMemorySnapshot(SCREENSHOT_WIDTH, SCREENSHOT_HEIGHT, true, false, &width, &height, &pixels)) { // saving failed for some reason, device lost? return nullptr; diff --git a/pcsx2/VMManager.cpp b/pcsx2/VMManager.cpp index 2eea9ada1b..685af1f6b9 100644 --- a/pcsx2/VMManager.cpp +++ b/pcsx2/VMManager.cpp @@ -34,6 +34,7 @@ #include "Input/InputManager.h" #include "IopBios.h" #include "LogSink.h" +#include "MTGS.h" #include "MTVU.h" #include "MemoryCardFile.h" #include "PAD/Host/PAD.h" @@ -236,7 +237,7 @@ void VMManager::SetState(VMState state) { if (THREAD_VU1) vu1Thread.WaitVU(); - GetMTGS().WaitGS(false); + MTGS::WaitGS(false); InputManager::PauseVibration(); } else @@ -384,6 +385,8 @@ void VMManager::Internal::CPUThreadShutdown() USBshutdown(); GSshutdown(); + MTGS::ShutdownThread(); + #ifdef _WIN32 CoUninitialize(); #endif @@ -521,7 +524,7 @@ void VMManager::ApplySettings() { if (THREAD_VU1) vu1Thread.WaitVU(); - GetMTGS().WaitGS(false); + MTGS::WaitGS(false); } // Reset to a clean Pcsx2Config. Otherwise things which are optional (e.g. gamefixes) @@ -544,7 +547,7 @@ void VMManager::ApplyCoreSettings() { if (THREAD_VU1) vu1Thread.WaitVU(); - GetMTGS().WaitGS(false); + MTGS::WaitGS(false); } // Reset to a clean Pcsx2Config. Otherwise things which are optional (e.g. gamefixes) @@ -631,7 +634,7 @@ void VMManager::Internal::UpdateEmuFolders() if (EmuFolders::Textures != old_textures_directory) { - GetMTGS().RunOnGSThread([]() { + MTGS::RunOnGSThread([]() { if (VMManager::HasValidVM()) GSTextureReplacements::ReloadReplacementMap(); }); @@ -640,7 +643,7 @@ void VMManager::Internal::UpdateEmuFolders() if (EmuFolders::Videos != old_videos_directory) { if (VMManager::HasValidVM()) - GetMTGS().RunOnGSThread(&GSEndCapture); + MTGS::RunOnGSThread(&GSEndCapture); } } } @@ -1189,8 +1192,8 @@ bool VMManager::Initialize(VMBootParameters boot_params) EmuConfig.LimiterMode = GetInitialLimiterMode(); Console.WriteLn("Opening GS..."); - s_gs_open_on_initialize = GetMTGS().IsOpen(); - if (!s_gs_open_on_initialize && !GetMTGS().WaitForOpen()) + s_gs_open_on_initialize = MTGS::IsOpen(); + if (!s_gs_open_on_initialize && !MTGS::WaitForOpen()) { // we assume GS is going to report its own error Console.WriteLn("Failed to open GS."); @@ -1199,7 +1202,7 @@ bool VMManager::Initialize(VMBootParameters boot_params) ScopedGuard close_gs = []() { if (!s_gs_open_on_initialize) - GetMTGS().WaitForClose(); + MTGS::WaitForClose(); }; Console.WriteLn("Opening SPU2..."); @@ -1310,7 +1313,7 @@ void VMManager::Shutdown(bool save_resume_state) // sync everything if (THREAD_VU1) vu1Thread.WaitVU(); - GetMTGS().WaitGS(); + MTGS::WaitGS(); if (!GSDumpReplayer::IsReplayingDump() && save_resume_state) { @@ -1359,12 +1362,12 @@ void VMManager::Shutdown(bool save_resume_state) // so that the texture cache and targets are all cleared. if (s_gs_open_on_initialize) { - GetMTGS().WaitGS(false, false, false); - GetMTGS().ResetGS(true); + MTGS::WaitGS(false, false, false); + MTGS::ResetGS(true); } else { - GetMTGS().WaitForClose(); + MTGS::WaitForClose(); } PADshutdown(); @@ -1402,6 +1405,10 @@ void VMManager::Reset() return; #endif + vu1Thread.WaitVU(); + vu1Thread.Reset(); + MTGS::WaitGS(); + const bool elf_was_changed = (s_current_crc != 0); ClearELFInfo(); if (elf_was_changed) @@ -1416,7 +1423,7 @@ void VMManager::Reset() if (g_InputRecording.isActive()) { g_InputRecording.handleReset(); - GetMTGS().PresentCurrentFrame(); + MTGS::PresentCurrentFrame(); } // If we were paused, state won't be resetting, so don't flip back to running. @@ -1511,7 +1518,7 @@ bool VMManager::DoLoadState(const char* filename) if (g_InputRecording.isActive()) { g_InputRecording.handleLoadingSavestate(); - GetMTGS().PresentCurrentFrame(); + MTGS::PresentCurrentFrame(); } return true; } @@ -2044,7 +2051,7 @@ void VMManager::CheckForGSConfigChanges(const Pcsx2Config& old_config) EmuConfig.LimiterMode = GetInitialLimiterMode(); ResetFrameLimiterState(); - GetMTGS().ApplySettings(); + MTGS::ApplySettings(); } void VMManager::CheckForFramerateConfigChanges(const Pcsx2Config& old_config) @@ -2073,7 +2080,7 @@ void VMManager::CheckForPatchConfigChanges(const Pcsx2Config& old_config) // This is a bit messy, because the patch config update happens after the settings are loaded, // if we disable widescreen patches, we have to reload the original settings again. if (Patch::ReloadPatchAffectingOptions()) - GetMTGS().ApplySettings(); + MTGS::ApplySettings(); } void VMManager::CheckForDEV9ConfigChanges(const Pcsx2Config& old_config) @@ -2171,7 +2178,7 @@ void VMManager::CheckForConfigChanges(const Pcsx2Config& old_config) // For the big picture UI, we still need to update GS settings, since it's running, // and we don't update its config when we start the VM. - if (HasValidVM() || GetMTGS().IsOpen()) + if (HasValidVM() || MTGS::IsOpen()) CheckForGSConfigChanges(old_config); if (EmuConfig.Achievements != old_config.Achievements) @@ -2705,7 +2712,7 @@ void VMManager::SetEmuThreadAffinities() if (EmuConfig.Cpu.AffinityControlMode != 0) Console.Error("Insufficient processors for affinity control."); - GetMTGS().GetThreadHandle().SetAffinity(0); + MTGS::GetThreadHandle().SetAffinity(0); vu1Thread.GetThreadHandle().SetAffinity(0); s_vm_thread_handle.SetAffinity(0); return; @@ -2748,7 +2755,7 @@ void VMManager::SetEmuThreadAffinities() const u64 gs_affinity = static_cast(1) << gs_index; Console.WriteLn(Color_StrongGreen, "GS thread is on processor %u (0x%llx)", gs_index, gs_affinity); - GetMTGS().GetThreadHandle().SetAffinity(gs_affinity); + MTGS::GetThreadHandle().SetAffinity(gs_affinity); } void VMManager::SetHardwareDependentDefaultSettings(SettingsInterface& si) diff --git a/pcsx2/Vif1_Dma.cpp b/pcsx2/Vif1_Dma.cpp index f0364eaeb2..5a5bf595e6 100644 --- a/pcsx2/Vif1_Dma.cpp +++ b/pcsx2/Vif1_Dma.cpp @@ -1,5 +1,5 @@ /* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2010 PCSX2 Dev Team + * Copyright (C) 2002-2023 PCSX2 Dev Team * * PCSX2 is free software: you can redistribute it and/or modify it under the terms * of the GNU Lesser General Public License as published by the Free Software Found- @@ -72,7 +72,7 @@ void vif1TransferToMemory() pxAssert(p3.isDone() || !p3.gifTag.isValid); } - GetMTGS().InitAndReadFIFO(reinterpret_cast(pMem), size); + MTGS::InitAndReadFIFO(reinterpret_cast(pMem), size); // pMem += size; //Some games such as Alex Ferguson's Player Manager 2001 reads less than GSLastDownloadSize by VIF then reads the remainder by FIFO diff --git a/tests/ctest/core/StubHost.cpp b/tests/ctest/core/StubHost.cpp index 8a510e111a..55a8239472 100644 --- a/tests/ctest/core/StubHost.cpp +++ b/tests/ctest/core/StubHost.cpp @@ -1,5 +1,5 @@ /* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2021 PCSX2 Dev Team + * Copyright (C) 2002-2023 PCSX2 Dev Team * * PCSX2 is free software: you can redistribute it and/or modify it under the terms * of the GNU Lesser General Public License as published by the Free Software Found- @@ -13,13 +13,12 @@ * If not, see . */ +#include "pcsx2/Achievements.h" +#include "pcsx2/GS.h" +#include "pcsx2/Host.h" #include "pcsx2/ImGui/ImGuiManager.h" #include "pcsx2/Input/InputManager.h" -#include "pcsx2/GS.h" -#include "pcsx2/GS/GS.h" -#include "pcsx2/Host.h" #include "pcsx2/VMManager.h" -#include "pcsx2/Achievements.h" void Host::CommitBaseSettingChanges() { @@ -219,15 +218,5 @@ std::optional InputManager::ConvertHostKeyboardCodeToString(u32 cod return std::nullopt; } -SysMtgsThread& GetMTGS() -{ - throw std::exception(); -} - -////////////////////////////////////////////////////////////////////////// -// Interface Stuff -////////////////////////////////////////////////////////////////////////// - -const IConsoleWriter* PatchesCon = &Console; BEGIN_HOTKEY_LIST(g_host_hotkeys) END_HOTKEY_LIST() \ No newline at end of file