mirror of https://github.com/PCSX2/pcsx2.git
VMManager: Support playing back GS dumps
This commit is contained in:
parent
4d85d916b7
commit
4331ae1925
|
@ -28,6 +28,7 @@
|
||||||
#include "pcsx2/Frontend/InputManager.h"
|
#include "pcsx2/Frontend/InputManager.h"
|
||||||
#include "pcsx2/Frontend/ImGuiManager.h"
|
#include "pcsx2/Frontend/ImGuiManager.h"
|
||||||
#include "pcsx2/GS.h"
|
#include "pcsx2/GS.h"
|
||||||
|
#include "pcsx2/GSDumpReplayer.h"
|
||||||
#include "pcsx2/HostDisplay.h"
|
#include "pcsx2/HostDisplay.h"
|
||||||
#include "pcsx2/PAD/Host/PAD.h"
|
#include "pcsx2/PAD/Host/PAD.h"
|
||||||
#include "pcsx2/PerformanceMetrics.h"
|
#include "pcsx2/PerformanceMetrics.h"
|
||||||
|
@ -677,6 +678,9 @@ bool Host::BeginPresentFrame(bool frame_skip)
|
||||||
|
|
||||||
void Host::EndPresentFrame()
|
void Host::EndPresentFrame()
|
||||||
{
|
{
|
||||||
|
if (GSDumpReplayer::IsReplayingDump())
|
||||||
|
GSDumpReplayer::RenderUI();
|
||||||
|
|
||||||
ImGuiManager::RenderOSD();
|
ImGuiManager::RenderOSD();
|
||||||
s_host_display->EndPresent();
|
s_host_display->EndPresent();
|
||||||
ImGuiManager::NewFrame();
|
ImGuiManager::NewFrame();
|
||||||
|
|
|
@ -44,9 +44,15 @@
|
||||||
#include "svnrev.h"
|
#include "svnrev.h"
|
||||||
|
|
||||||
static constexpr char DISC_IMAGE_FILTER[] =
|
static constexpr char DISC_IMAGE_FILTER[] =
|
||||||
QT_TRANSLATE_NOOP("MainWindow", "All File Types (*.bin *.iso *.cue *.chd *.cso *.elf *.irx *.m3u);;Single-Track Raw Images (*.bin "
|
QT_TRANSLATE_NOOP("MainWindow", "All File Types (*.bin *.iso *.cue *.chd *.cso *.elf *.irx *.m3u *.gs *.gs.xz);;"
|
||||||
"*.iso);;Cue Sheets (*.cue);;MAME CHD Images (*.chd);;CSO Images (*.cso);;"
|
"Single-Track Raw Images (*.bin *.iso);;"
|
||||||
"ELF Executables (*.elf);;IRX Executables (*.irx);;Playlists (*.m3u)");
|
"Cue Sheets (*.cue);;"
|
||||||
|
"MAME CHD Images (*.chd);;"
|
||||||
|
"CSO Images (*.cso);;"
|
||||||
|
"ELF Executables (*.elf);;"
|
||||||
|
"IRX Executables (*.irx);;"
|
||||||
|
"Playlists (*.m3u);;"
|
||||||
|
"GS Dumps (*.gs *.gs.xz)");
|
||||||
|
|
||||||
const char* MainWindow::DEFAULT_THEME_NAME = "darkfusion";
|
const char* MainWindow::DEFAULT_THEME_NAME = "darkfusion";
|
||||||
|
|
||||||
|
|
|
@ -983,6 +983,7 @@ if(PCSX2_CORE)
|
||||||
Frontend/InputManager.cpp
|
Frontend/InputManager.cpp
|
||||||
Frontend/InputSource.cpp
|
Frontend/InputSource.cpp
|
||||||
Frontend/LayeredSettingsInterface.cpp
|
Frontend/LayeredSettingsInterface.cpp
|
||||||
|
GSDumpReplayer.cpp
|
||||||
HostSettings.cpp
|
HostSettings.cpp
|
||||||
VMManager.cpp
|
VMManager.cpp
|
||||||
)
|
)
|
||||||
|
@ -992,6 +993,7 @@ if(PCSX2_CORE)
|
||||||
Frontend/InputManager.h
|
Frontend/InputManager.h
|
||||||
Frontend/InputSource.h
|
Frontend/InputSource.h
|
||||||
Frontend/LayeredSettingsInterface.h
|
Frontend/LayeredSettingsInterface.h
|
||||||
|
GSDumpReplayer.h
|
||||||
HostSettings.h
|
HostSettings.h
|
||||||
VMManager.h)
|
VMManager.h)
|
||||||
endif()
|
endif()
|
||||||
|
|
|
@ -630,3 +630,17 @@ void ImGuiManager::RenderOSD()
|
||||||
DrawOSDMessages();
|
DrawOSDMessages();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
float ImGuiManager::GetGlobalScale()
|
||||||
|
{
|
||||||
|
return s_global_scale;
|
||||||
|
}
|
||||||
|
|
||||||
|
ImFont* ImGuiManager::GetStandardFont()
|
||||||
|
{
|
||||||
|
return s_standard_font;
|
||||||
|
}
|
||||||
|
|
||||||
|
ImFont* ImGuiManager::GetFixedFont()
|
||||||
|
{
|
||||||
|
return s_fixed_font;
|
||||||
|
}
|
||||||
|
|
|
@ -15,6 +15,8 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
struct ImFont;
|
||||||
|
|
||||||
namespace ImGuiManager
|
namespace ImGuiManager
|
||||||
{
|
{
|
||||||
/// Initializes ImGui, creates fonts, etc.
|
/// Initializes ImGui, creates fonts, etc.
|
||||||
|
@ -34,5 +36,14 @@ namespace ImGuiManager
|
||||||
|
|
||||||
/// Renders any on-screen display elements.
|
/// Renders any on-screen display elements.
|
||||||
void RenderOSD();
|
void RenderOSD();
|
||||||
|
|
||||||
|
/// Returns the scale of all on-screen elements.
|
||||||
|
float GetGlobalScale();
|
||||||
|
|
||||||
|
/// Returns the standard font for external drawing.
|
||||||
|
ImFont* GetStandardFont();
|
||||||
|
|
||||||
|
/// Returns the fixed-width font for external drawing.
|
||||||
|
ImFont* GetFixedFont();
|
||||||
} // namespace ImGuiManager
|
} // namespace ImGuiManager
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,342 @@
|
||||||
|
#include "PrecompiledHeader.h"
|
||||||
|
|
||||||
|
#include <atomic>
|
||||||
|
|
||||||
|
#include "common/FileSystem.h"
|
||||||
|
#include "common/StringUtil.h"
|
||||||
|
#include "common/Timer.h"
|
||||||
|
|
||||||
|
#include "imgui.h"
|
||||||
|
|
||||||
|
// Has to come before Gif.h
|
||||||
|
#include "MemoryTypes.h"
|
||||||
|
|
||||||
|
#include "Frontend/ImGuiManager.h"
|
||||||
|
#include "Frontend/GameList.h"
|
||||||
|
#include "Gif.h"
|
||||||
|
#include "Gif_Unit.h"
|
||||||
|
#include "GSDumpReplayer.h"
|
||||||
|
#include "GS/GSLzma.h"
|
||||||
|
#include "GS.h"
|
||||||
|
#include "Host.h"
|
||||||
|
#include "R3000A.h"
|
||||||
|
#include "R5900.h"
|
||||||
|
#include "VMManager.h"
|
||||||
|
#include "VUmicro.h"
|
||||||
|
|
||||||
|
static void GSDumpReplayerCpuReserve();
|
||||||
|
static void GSDumpReplayerCpuShutdown();
|
||||||
|
static void GSDumpReplayerCpuReset();
|
||||||
|
static void GSDumpReplayerCpuStep();
|
||||||
|
static void GSDumpReplayerCpuExecute();
|
||||||
|
static void GSDumpReplayerCpuCheckExecutionState();
|
||||||
|
static void GSDumpReplayerCpuThrowException(const BaseException& ex);
|
||||||
|
static void GSDumpReplayerCpuThrowCpuException(const BaseR5900Exception& ex);
|
||||||
|
static void GSDumpReplayerCpuClear(u32 addr, u32 size);
|
||||||
|
static uint GSDumpReplayerCpuGetCacheReserve();
|
||||||
|
static void GSDumpReplayerCpuSetCacheReserve(uint reserveInMegs);
|
||||||
|
|
||||||
|
static std::unique_ptr<GSDumpFile> s_dump_file;
|
||||||
|
static u32 s_current_packet = 0;
|
||||||
|
static u32 s_dump_frame_number = 0;
|
||||||
|
static bool s_dump_running = false;
|
||||||
|
static bool s_needs_state_loaded = false;
|
||||||
|
static u64 s_frame_ticks = 0;
|
||||||
|
static u64 s_next_frame_time = 0;
|
||||||
|
|
||||||
|
R5900cpu GSDumpReplayerCpu = {
|
||||||
|
GSDumpReplayerCpuReserve,
|
||||||
|
GSDumpReplayerCpuShutdown,
|
||||||
|
GSDumpReplayerCpuReset,
|
||||||
|
GSDumpReplayerCpuStep,
|
||||||
|
GSDumpReplayerCpuExecute,
|
||||||
|
GSDumpReplayerCpuCheckExecutionState,
|
||||||
|
GSDumpReplayerCpuThrowException,
|
||||||
|
GSDumpReplayerCpuThrowCpuException,
|
||||||
|
GSDumpReplayerCpuClear,
|
||||||
|
GSDumpReplayerCpuGetCacheReserve,
|
||||||
|
GSDumpReplayerCpuSetCacheReserve};
|
||||||
|
|
||||||
|
static InterpVU0 gsDumpVU0;
|
||||||
|
static InterpVU1 gsDumpVU1;
|
||||||
|
|
||||||
|
bool GSDumpReplayer::IsReplayingDump()
|
||||||
|
{
|
||||||
|
return static_cast<bool>(s_dump_file);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GSDumpReplayer::Initialize(const char* filename)
|
||||||
|
{
|
||||||
|
Common::Timer timer;
|
||||||
|
Console.WriteLn("(GSDumpReplayer) Reading file...");
|
||||||
|
|
||||||
|
s_dump_file = GSDumpFile::OpenGSDump(filename);
|
||||||
|
if (!s_dump_file || !s_dump_file->ReadFile())
|
||||||
|
{
|
||||||
|
Host::ReportFormattedErrorAsync("GSDumpReplayer", "Failed to open or read '%s'.", filename);
|
||||||
|
s_dump_file.reset();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Console.WriteLn("(GSDumpReplayer) Read file in %.2f ms.", timer.GetTimeMilliseconds());
|
||||||
|
|
||||||
|
// We replace all CPUs.
|
||||||
|
Cpu = &GSDumpReplayerCpu;
|
||||||
|
psxCpu = &psxInt;
|
||||||
|
CpuVU0 = &gsDumpVU0;
|
||||||
|
CpuVU1 = &gsDumpVU1;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GSDumpReplayer::Reset()
|
||||||
|
{
|
||||||
|
GSDumpReplayerCpuReset();
|
||||||
|
}
|
||||||
|
|
||||||
|
void GSDumpReplayer::Shutdown()
|
||||||
|
{
|
||||||
|
Console.WriteLn("(GSDumpReplayer) Shutting down.");
|
||||||
|
|
||||||
|
Cpu = nullptr;
|
||||||
|
psxCpu = nullptr;
|
||||||
|
CpuVU0 = nullptr;
|
||||||
|
CpuVU1 = nullptr;
|
||||||
|
s_dump_file.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string GSDumpReplayer::GetDumpSerial()
|
||||||
|
{
|
||||||
|
std::string ret;
|
||||||
|
|
||||||
|
if (!s_dump_file->GetSerial().empty())
|
||||||
|
{
|
||||||
|
ret = s_dump_file->GetSerial();
|
||||||
|
}
|
||||||
|
else if (s_dump_file->GetCRC() != 0)
|
||||||
|
{
|
||||||
|
// old dump files don't have serials, but we have the crc...
|
||||||
|
// so, let's try searching the game list for a crc match.
|
||||||
|
auto lock = GameList::GetLock();
|
||||||
|
const GameList::Entry* entry = GameList::GetEntryByCRC(s_dump_file->GetCRC());
|
||||||
|
if (entry)
|
||||||
|
ret = entry->serial;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 GSDumpReplayer::GetDumpCRC()
|
||||||
|
{
|
||||||
|
return s_dump_file->GetCRC();
|
||||||
|
}
|
||||||
|
|
||||||
|
void GSDumpReplayerCpuReserve()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void GSDumpReplayerCpuShutdown()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void GSDumpReplayerCpuReset()
|
||||||
|
{
|
||||||
|
s_needs_state_loaded = true;
|
||||||
|
s_current_packet = 0;
|
||||||
|
s_dump_frame_number = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void GSDumpReplayerLoadInitialState()
|
||||||
|
{
|
||||||
|
// reset GS registers to initial dump values
|
||||||
|
std::memcpy(PS2MEM_GS, s_dump_file->GetRegsData().data(),
|
||||||
|
std::min(Ps2MemSize::GSregs, static_cast<u32>(s_dump_file->GetRegsData().size())));
|
||||||
|
|
||||||
|
// load GS state
|
||||||
|
freezeData fd = {static_cast<int>(s_dump_file->GetStateData().size()),
|
||||||
|
const_cast<u8*>(s_dump_file->GetStateData().data())};
|
||||||
|
MTGS_FreezeData mfd = {&fd, 0};
|
||||||
|
GetMTGS().Freeze(FreezeAction::Load, mfd);
|
||||||
|
if (mfd.retval != 0)
|
||||||
|
Host::ReportFormattedErrorAsync("GSDumpReplayer", "Failed to load GS state.");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void GSDumpReplayerSendPacketToMTGS(GIF_PATH path, const u8* data, u32 length)
|
||||||
|
{
|
||||||
|
pxAssert((length % 16) == 0);
|
||||||
|
|
||||||
|
Gif_Path& gifPath = gifUnit.gifPath[path];
|
||||||
|
gifPath.CopyGSPacketData(const_cast<u8*>(data), length);
|
||||||
|
|
||||||
|
GS_Packet gsPack;
|
||||||
|
gsPack.offset = gifPath.curOffset;
|
||||||
|
gsPack.size = length;
|
||||||
|
gifPath.curOffset += length;
|
||||||
|
Gif_AddCompletedGSPacket(gsPack, path);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void GSDumpReplayerUpdateFrameLimit()
|
||||||
|
{
|
||||||
|
constexpr u32 default_frame_limit = 60;
|
||||||
|
const u32 frame_limit = static_cast<u32>(default_frame_limit * EmuConfig.GS.LimitScalar);
|
||||||
|
|
||||||
|
if (frame_limit > 0)
|
||||||
|
s_frame_ticks = (GetTickFrequency() + (frame_limit / 2)) / frame_limit;
|
||||||
|
else
|
||||||
|
s_frame_ticks = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void GSDumpReplayerFrameLimit()
|
||||||
|
{
|
||||||
|
if (s_frame_ticks == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Frame limiter
|
||||||
|
u64 now = GetCPUTicks();
|
||||||
|
const s64 ms = GetTickFrequency() / 1000;
|
||||||
|
const s64 sleep = s_next_frame_time - now - ms;
|
||||||
|
if (sleep > ms)
|
||||||
|
Threading::Sleep(sleep / ms);
|
||||||
|
while ((now = GetCPUTicks()) < s_next_frame_time)
|
||||||
|
ShortSpin();
|
||||||
|
s_next_frame_time = std::max(now, s_next_frame_time + s_frame_ticks);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GSDumpReplayerCpuStep()
|
||||||
|
{
|
||||||
|
if (s_needs_state_loaded)
|
||||||
|
{
|
||||||
|
GSDumpReplayerLoadInitialState();
|
||||||
|
s_needs_state_loaded = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const GSDumpFile::GSData& packet = s_dump_file->GetPackets()[s_current_packet];
|
||||||
|
s_current_packet = (s_current_packet + 1) % static_cast<u32>(s_dump_file->GetPackets().size());
|
||||||
|
if (s_current_packet == 0)
|
||||||
|
s_dump_frame_number = 0;
|
||||||
|
|
||||||
|
switch (packet.id)
|
||||||
|
{
|
||||||
|
case GSDumpTypes::GSType::Transfer:
|
||||||
|
{
|
||||||
|
switch (packet.path)
|
||||||
|
{
|
||||||
|
case GSDumpTypes::GSTransferPath::Path1Old:
|
||||||
|
{
|
||||||
|
std::unique_ptr<u8[]> data(new u8[16384]);
|
||||||
|
const s32 addr = 16384 - packet.length;
|
||||||
|
std::memcpy(data.get(), packet.data.get() + addr, packet.length);
|
||||||
|
GSDumpReplayerSendPacketToMTGS(GIF_PATH_1, data.get(), packet.length);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case GSDumpTypes::GSTransferPath::Path1New:
|
||||||
|
case GSDumpTypes::GSTransferPath::Path2:
|
||||||
|
case GSDumpTypes::GSTransferPath::Path3:
|
||||||
|
{
|
||||||
|
GSDumpReplayerSendPacketToMTGS(static_cast<GIF_PATH>(static_cast<u8>(packet.path) - 1),
|
||||||
|
reinterpret_cast<const u8*>(packet.data.get()), packet.length);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case GSDumpTypes::GSType::VSync:
|
||||||
|
{
|
||||||
|
s_dump_frame_number++;
|
||||||
|
GSDumpReplayerCpuCheckExecutionState();
|
||||||
|
GSDumpReplayerUpdateFrameLimit();
|
||||||
|
GSDumpReplayerFrameLimit();
|
||||||
|
GetMTGS().PostVsyncStart(false);
|
||||||
|
VMManager::Internal::VSyncOnCPUThread();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case GSDumpTypes::GSType::ReadFIFO2:
|
||||||
|
{
|
||||||
|
std::unique_ptr<char[]> arr(new char[*((int*)packet.data.get())]);
|
||||||
|
GSreadFIFO2((u8*)arr.get(), *((int*)packet.data.get()));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case GSDumpTypes::GSType::Registers:
|
||||||
|
{
|
||||||
|
std::memcpy(PS2MEM_GS, packet.data.get(), std::min<s32>(packet.length, Ps2MemSize::GSregs));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void GSDumpReplayerCpuExecute()
|
||||||
|
{
|
||||||
|
s_dump_running = true;
|
||||||
|
s_next_frame_time = GetCPUTicks();
|
||||||
|
|
||||||
|
while (s_dump_running)
|
||||||
|
{
|
||||||
|
GSDumpReplayerCpuStep();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void GSDumpReplayerCpuCheckExecutionState()
|
||||||
|
{
|
||||||
|
if (VMManager::Internal::IsExecutionInterrupted())
|
||||||
|
s_dump_running = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GSDumpReplayerCpuThrowException(const BaseException& ex)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void GSDumpReplayerCpuThrowCpuException(const BaseR5900Exception& ex)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void GSDumpReplayerCpuClear(u32 addr, u32 size)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
uint GSDumpReplayerCpuGetCacheReserve()
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GSDumpReplayerCpuSetCacheReserve(uint reserveInMegs)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void GSDumpReplayer::RenderUI()
|
||||||
|
{
|
||||||
|
const float scale = ImGuiManager::GetGlobalScale();
|
||||||
|
const float shadow_offset = std::ceil(1.0f * scale);
|
||||||
|
const float margin = std::ceil(10.0f * scale);
|
||||||
|
const float spacing = std::ceil(5.0f * scale);
|
||||||
|
float position_y = margin;
|
||||||
|
|
||||||
|
ImDrawList* dl = ImGui::GetBackgroundDrawList();
|
||||||
|
ImFont* font = ImGuiManager::GetFixedFont();
|
||||||
|
FastFormatAscii text;
|
||||||
|
ImVec2 text_size;
|
||||||
|
|
||||||
|
#define DRAW_LINE(font, text, color) \
|
||||||
|
do \
|
||||||
|
{ \
|
||||||
|
text_size = font->CalcTextSizeA(font->FontSize, std::numeric_limits<float>::max(), -1.0f, (text), nullptr, nullptr); \
|
||||||
|
dl->AddText(font, font->FontSize, ImVec2(margin + shadow_offset, position_y + shadow_offset), IM_COL32(0, 0, 0, 100), (text)); \
|
||||||
|
dl->AddText(font, font->FontSize, ImVec2(margin, position_y), color, (text)); \
|
||||||
|
position_y += text_size.y + spacing; \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
text.Write("Dump Frame: %u", s_dump_frame_number);
|
||||||
|
DRAW_LINE(font, text.c_str(), IM_COL32(255, 255, 255, 255));
|
||||||
|
|
||||||
|
text.Clear();
|
||||||
|
text.Write("Packet Number: %u/%u", s_current_packet, static_cast<u32>(s_dump_file->GetPackets().size()));
|
||||||
|
DRAW_LINE(font, text.c_str(), IM_COL32(255, 255, 255, 255));
|
||||||
|
|
||||||
|
#undef DRAW_LINE
|
||||||
|
}
|
|
@ -0,0 +1,15 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace GSDumpReplayer
|
||||||
|
{
|
||||||
|
bool IsReplayingDump();
|
||||||
|
|
||||||
|
bool Initialize(const char* filename);
|
||||||
|
void Reset();
|
||||||
|
void Shutdown();
|
||||||
|
|
||||||
|
std::string GetDumpSerial();
|
||||||
|
u32 GetDumpCRC();
|
||||||
|
|
||||||
|
void RenderUI();
|
||||||
|
}
|
|
@ -49,6 +49,10 @@ BIOS
|
||||||
|
|
||||||
#include "common/PageFaultSource.h"
|
#include "common/PageFaultSource.h"
|
||||||
|
|
||||||
|
#ifdef PCSX2_CORE
|
||||||
|
#include "GSDumpReplayer.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef ENABLECACHE
|
#ifdef ENABLECACHE
|
||||||
#include "Cache.h"
|
#include "Cache.h"
|
||||||
#endif
|
#endif
|
||||||
|
@ -850,7 +854,13 @@ void eeMemoryReserve::Reset()
|
||||||
vtlb_VMap(0x00000000,0x00000000,0x20000000);
|
vtlb_VMap(0x00000000,0x00000000,0x20000000);
|
||||||
vtlb_VMapUnmap(0x20000000,0x60000000);
|
vtlb_VMapUnmap(0x20000000,0x60000000);
|
||||||
|
|
||||||
if (!LoadBIOS())
|
#ifdef PCSX2_CORE
|
||||||
|
const bool needs_bios = !GSDumpReplayer::IsReplayingDump();
|
||||||
|
#else
|
||||||
|
constexpr bool needs_bios = true;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (needs_bios && !LoadBIOS())
|
||||||
pxFailRel("Failed to load BIOS");
|
pxFailRel("Failed to load BIOS");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -27,6 +27,11 @@
|
||||||
#include "common/MemsetFast.inl"
|
#include "common/MemsetFast.inl"
|
||||||
#include "common/Perf.h"
|
#include "common/Perf.h"
|
||||||
|
|
||||||
|
#ifdef PCSX2_CORE
|
||||||
|
#include "GSDumpReplayer.h"
|
||||||
|
|
||||||
|
extern R5900cpu GSDumpReplayerCpu;
|
||||||
|
#endif
|
||||||
|
|
||||||
// --------------------------------------------------------------------------------------
|
// --------------------------------------------------------------------------------------
|
||||||
// RecompiledCodeReserve (implementations)
|
// RecompiledCodeReserve (implementations)
|
||||||
|
@ -576,6 +581,11 @@ void SysCpuProviderPack::ApplyConfig() const
|
||||||
|
|
||||||
if( EmuConfig.Cpu.Recompiler.EnableVU1 )
|
if( EmuConfig.Cpu.Recompiler.EnableVU1 )
|
||||||
CpuVU1 = (BaseVUmicroCPU*)CpuProviders->microVU1;
|
CpuVU1 = (BaseVUmicroCPU*)CpuProviders->microVU1;
|
||||||
|
|
||||||
|
#ifdef PCSX2_CORE
|
||||||
|
if (GSDumpReplayer::IsReplayingDump())
|
||||||
|
Cpu = &GSDumpReplayerCpu;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
// Resets all PS2 cpu execution caches, which does not affect that actual PS2 state/condition.
|
// Resets all PS2 cpu execution caches, which does not affect that actual PS2 state/condition.
|
||||||
|
|
|
@ -35,6 +35,7 @@
|
||||||
#include "Elfheader.h"
|
#include "Elfheader.h"
|
||||||
#include "FW.h"
|
#include "FW.h"
|
||||||
#include "GS.h"
|
#include "GS.h"
|
||||||
|
#include "GSDumpReplayer.h"
|
||||||
#include "HostDisplay.h"
|
#include "HostDisplay.h"
|
||||||
#include "HostSettings.h"
|
#include "HostSettings.h"
|
||||||
#include "IopBios.h"
|
#include "IopBios.h"
|
||||||
|
@ -222,6 +223,10 @@ void VMManager::LoadSettings()
|
||||||
EmuConfig.GS.MaskUserHacks();
|
EmuConfig.GS.MaskUserHacks();
|
||||||
EmuConfig.GS.MaskUpscalingHacks();
|
EmuConfig.GS.MaskUpscalingHacks();
|
||||||
|
|
||||||
|
// Force MTVU off when playing back GS dumps, it doesn't get used.
|
||||||
|
if (GSDumpReplayer::IsReplayingDump())
|
||||||
|
EmuConfig.Speedhacks.vuThread = false;
|
||||||
|
|
||||||
if (HasValidVM())
|
if (HasValidVM())
|
||||||
ApplyGameFixes();
|
ApplyGameFixes();
|
||||||
}
|
}
|
||||||
|
@ -454,11 +459,20 @@ void VMManager::UpdateRunningGame(bool force)
|
||||||
// we have the CRC but we're still at the bios and the settings are changed
|
// we have the CRC but we're still at the bios and the settings are changed
|
||||||
// (e.g. the user presses TAB to speed up emulation), we don't want to apply the
|
// (e.g. the user presses TAB to speed up emulation), we don't want to apply the
|
||||||
// settings as if the game is already running (title, loadeding patches, etc).
|
// settings as if the game is already running (title, loadeding patches, etc).
|
||||||
bool ingame = (ElfCRC && (g_GameLoading || g_GameStarted));
|
u32 new_crc;
|
||||||
const u32 new_crc = ingame ? ElfCRC : 0;
|
std::string new_serial;
|
||||||
const std::string crc_string(StringUtil::StdStringFromFormat("%08X", new_crc));
|
if (!GSDumpReplayer::IsReplayingDump())
|
||||||
|
{
|
||||||
|
const bool ingame = (ElfCRC && (g_GameLoading || g_GameStarted));
|
||||||
|
new_crc = ingame ? ElfCRC : 0;
|
||||||
|
new_serial = ingame ? SysGetDiscID().ToStdString() : SysGetBiosDiscID().ToStdString();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
new_crc = GSDumpReplayer::GetDumpCRC();
|
||||||
|
new_serial = GSDumpReplayer::GetDumpSerial();
|
||||||
|
}
|
||||||
|
|
||||||
std::string new_serial(ingame ? SysGetDiscID().ToStdString() : SysGetBiosDiscID().ToStdString());
|
|
||||||
if (!force && s_game_crc == new_crc && s_game_serial == new_serial)
|
if (!force && s_game_crc == new_crc && s_game_serial == new_serial)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
@ -488,7 +502,7 @@ void VMManager::UpdateRunningGame(bool force)
|
||||||
ApplySettings();
|
ApplySettings();
|
||||||
|
|
||||||
ForgetLoadedPatches();
|
ForgetLoadedPatches();
|
||||||
LoadPatches(crc_string, true, false);
|
LoadPatches(StringUtil::StdStringFromFormat("%08X", new_crc), true, false);
|
||||||
GetMTGS().SendGameCRC(new_crc);
|
GetMTGS().SendGameCRC(new_crc);
|
||||||
|
|
||||||
Host::OnGameChanged(s_disc_path, s_game_serial, s_game_name, s_game_crc);
|
Host::OnGameChanged(s_disc_path, s_game_serial, s_game_name, s_game_crc);
|
||||||
|
@ -544,7 +558,18 @@ bool VMManager::Initialize(const VMBootParameters& boot_params)
|
||||||
};
|
};
|
||||||
|
|
||||||
LoadSettings();
|
LoadSettings();
|
||||||
ApplyBootParameters(boot_params);
|
|
||||||
|
if (IsGSDumpFileName(boot_params.source))
|
||||||
|
{
|
||||||
|
CDVDsys_ChangeSource(CDVD_SourceType::NoDisc);
|
||||||
|
if (!GSDumpReplayer::Initialize(boot_params.source.c_str()))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ApplyBootParameters(boot_params);
|
||||||
|
}
|
||||||
|
|
||||||
EmuConfig.LimiterMode = GetInitialLimiterMode();
|
EmuConfig.LimiterMode = GetInitialLimiterMode();
|
||||||
|
|
||||||
Console.WriteLn("Allocating memory map...");
|
Console.WriteLn("Allocating memory map...");
|
||||||
|
@ -661,17 +686,18 @@ bool VMManager::Initialize(const VMBootParameters& boot_params)
|
||||||
frameLimitReset();
|
frameLimitReset();
|
||||||
cpuReset();
|
cpuReset();
|
||||||
|
|
||||||
|
Console.WriteLn("VM subsystems initialized in %.2f ms", init_timer.GetTimeMilliseconds());
|
||||||
|
s_state.store(VMState::Paused);
|
||||||
|
Host::OnVMStarted();
|
||||||
|
|
||||||
UpdateRunningGame(true);
|
UpdateRunningGame(true);
|
||||||
|
|
||||||
SetEmuThreadAffinities(true);
|
SetEmuThreadAffinities(true);
|
||||||
|
|
||||||
PerformanceMetrics::Clear();
|
PerformanceMetrics::Clear();
|
||||||
Console.WriteLn("VM subsystems initialized in %.2f ms", init_timer.GetTimeMilliseconds());
|
|
||||||
s_state.store(VMState::Paused);
|
|
||||||
Host::OnVMStarted();
|
|
||||||
|
|
||||||
// do we want to load state?
|
// do we want to load state?
|
||||||
if (!boot_params.save_state.empty())
|
if (!GSDumpReplayer::IsReplayingDump() && !boot_params.save_state.empty())
|
||||||
{
|
{
|
||||||
if (!DoLoadState(boot_params.save_state.c_str()))
|
if (!DoLoadState(boot_params.save_state.c_str()))
|
||||||
{
|
{
|
||||||
|
@ -692,12 +718,16 @@ void VMManager::Shutdown(bool allow_save_resume_state /* = true */)
|
||||||
vu1Thread.WaitVU();
|
vu1Thread.WaitVU();
|
||||||
GetMTGS().WaitGS();
|
GetMTGS().WaitGS();
|
||||||
|
|
||||||
if (allow_save_resume_state && ShouldSaveResumeState())
|
if (!GSDumpReplayer::IsReplayingDump() && allow_save_resume_state && ShouldSaveResumeState())
|
||||||
{
|
{
|
||||||
std::string resume_file_name(GetCurrentSaveStateFileName(-1));
|
std::string resume_file_name(GetCurrentSaveStateFileName(-1));
|
||||||
if (!resume_file_name.empty() && !DoSaveState(resume_file_name.c_str(), -1))
|
if (!resume_file_name.empty() && !DoSaveState(resume_file_name.c_str(), -1))
|
||||||
Console.Error("Failed to save resume state");
|
Console.Error("Failed to save resume state");
|
||||||
}
|
}
|
||||||
|
else if (GSDumpReplayer::IsReplayingDump())
|
||||||
|
{
|
||||||
|
GSDumpReplayer::Shutdown();
|
||||||
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
std::unique_lock lock(s_info_mutex);
|
std::unique_lock lock(s_info_mutex);
|
||||||
|
@ -790,6 +820,9 @@ std::string VMManager::GetCurrentSaveStateFileName(s32 slot)
|
||||||
|
|
||||||
bool VMManager::DoLoadState(const char* filename)
|
bool VMManager::DoLoadState(const char* filename)
|
||||||
{
|
{
|
||||||
|
if (GSDumpReplayer::IsReplayingDump())
|
||||||
|
return false;
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
Host::OnSaveStateLoading(filename);
|
Host::OnSaveStateLoading(filename);
|
||||||
|
@ -808,6 +841,9 @@ bool VMManager::DoLoadState(const char* filename)
|
||||||
|
|
||||||
bool VMManager::DoSaveState(const char* filename, s32 slot_for_message)
|
bool VMManager::DoSaveState(const char* filename, s32 slot_for_message)
|
||||||
{
|
{
|
||||||
|
if (GSDumpReplayer::IsReplayingDump())
|
||||||
|
return false;
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
std::unique_ptr<ArchiveEntryList> elist = std::make_unique<ArchiveEntryList>(new VmStateBuffer(L"Zippable Savestate"));
|
std::unique_ptr<ArchiveEntryList> elist = std::make_unique<ArchiveEntryList>(new VmStateBuffer(L"Zippable Savestate"));
|
||||||
|
@ -920,6 +956,11 @@ bool VMManager::IsElfFileName(const std::string& path)
|
||||||
return (StringUtil::Strcasecmp(&path[pos], ".elf") == 0);
|
return (StringUtil::Strcasecmp(&path[pos], ".elf") == 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool VMManager::IsGSDumpFileName(const std::string& path)
|
||||||
|
{
|
||||||
|
return (StringUtil::EndsWithNoCase(path, ".gs") || StringUtil::EndsWithNoCase(path, ".gs.xz"));
|
||||||
|
}
|
||||||
|
|
||||||
void VMManager::SetBootParametersForPath(const std::string& path, VMBootParameters* params)
|
void VMManager::SetBootParametersForPath(const std::string& path, VMBootParameters* params)
|
||||||
{
|
{
|
||||||
if (IsElfFileName(path))
|
if (IsElfFileName(path))
|
||||||
|
@ -927,6 +968,11 @@ void VMManager::SetBootParametersForPath(const std::string& path, VMBootParamete
|
||||||
params->elf_override = path;
|
params->elf_override = path;
|
||||||
params->source_type = CDVD_SourceType::NoDisc;
|
params->source_type = CDVD_SourceType::NoDisc;
|
||||||
}
|
}
|
||||||
|
else if (IsGSDumpFileName(path))
|
||||||
|
{
|
||||||
|
params->source_type = CDVD_SourceType::NoDisc;
|
||||||
|
params->source = path;
|
||||||
|
}
|
||||||
else if (!path.empty())
|
else if (!path.empty())
|
||||||
{
|
{
|
||||||
params->source_type = CDVD_SourceType::Iso;
|
params->source_type = CDVD_SourceType::Iso;
|
||||||
|
|
|
@ -135,6 +135,9 @@ namespace VMManager
|
||||||
/// Returns true if the specified path is an ELF.
|
/// Returns true if the specified path is an ELF.
|
||||||
bool IsElfFileName(const std::string& path);
|
bool IsElfFileName(const std::string& path);
|
||||||
|
|
||||||
|
/// Returns true if the specified path is a GS Dump.
|
||||||
|
bool IsGSDumpFileName(const std::string& path);
|
||||||
|
|
||||||
/// Updates boot parameters for a given start filename. If it's an elf, it'll set elf_override, otherwise source.
|
/// Updates boot parameters for a given start filename. If it's an elf, it'll set elf_override, otherwise source.
|
||||||
void SetBootParametersForPath(const std::string& path, VMBootParameters* params);
|
void SetBootParametersForPath(const std::string& path, VMBootParameters* params);
|
||||||
|
|
||||||
|
|
|
@ -186,6 +186,7 @@
|
||||||
<ClCompile Include="GameDatabase.cpp" />
|
<ClCompile Include="GameDatabase.cpp" />
|
||||||
<ClCompile Include="Gif_Logger.cpp" />
|
<ClCompile Include="Gif_Logger.cpp" />
|
||||||
<ClCompile Include="Gif_Unit.cpp" />
|
<ClCompile Include="Gif_Unit.cpp" />
|
||||||
|
<ClCompile Include="GSDumpReplayer.cpp" />
|
||||||
<ClCompile Include="GS\Renderers\DX11\D3D.cpp" />
|
<ClCompile Include="GS\Renderers\DX11\D3D.cpp" />
|
||||||
<ClCompile Include="GS\Renderers\HW\GSTextureReplacementLoaders.cpp" />
|
<ClCompile Include="GS\Renderers\HW\GSTextureReplacementLoaders.cpp" />
|
||||||
<ClCompile Include="GS\Renderers\HW\GSTextureReplacements.cpp" />
|
<ClCompile Include="GS\Renderers\HW\GSTextureReplacements.cpp" />
|
||||||
|
@ -488,6 +489,7 @@
|
||||||
<ClInclude Include="Frontend\XInputSource.h" />
|
<ClInclude Include="Frontend\XInputSource.h" />
|
||||||
<ClInclude Include="GameDatabase.h" />
|
<ClInclude Include="GameDatabase.h" />
|
||||||
<ClInclude Include="Gif_Unit.h" />
|
<ClInclude Include="Gif_Unit.h" />
|
||||||
|
<ClInclude Include="GSDumpReplayer.h" />
|
||||||
<ClInclude Include="GS\Renderers\DX11\D3D.h" />
|
<ClInclude Include="GS\Renderers\DX11\D3D.h" />
|
||||||
<ClInclude Include="GS\Renderers\HW\GSTextureReplacements.h" />
|
<ClInclude Include="GS\Renderers\HW\GSTextureReplacements.h" />
|
||||||
<ClInclude Include="GS\Window\GSwxDialog.h" />
|
<ClInclude Include="GS\Window\GSwxDialog.h" />
|
||||||
|
|
|
@ -1202,6 +1202,7 @@
|
||||||
<ClCompile Include="x86\iR5900Analysis.cpp">
|
<ClCompile Include="x86\iR5900Analysis.cpp">
|
||||||
<Filter>System\Ps2\EmotionEngine\EE\Dynarec</Filter>
|
<Filter>System\Ps2\EmotionEngine\EE\Dynarec</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
<ClCompile Include="GSDumpReplayer.cpp" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClInclude Include="Patch.h">
|
<ClInclude Include="Patch.h">
|
||||||
|
@ -1985,6 +1986,7 @@
|
||||||
<ClInclude Include="x86\iR5900Analysis.h">
|
<ClInclude Include="x86\iR5900Analysis.h">
|
||||||
<Filter>System\Ps2\EmotionEngine\EE\Dynarec</Filter>
|
<Filter>System\Ps2\EmotionEngine\EE\Dynarec</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
|
<ClInclude Include="GSDumpReplayer.h" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ResourceCompile Include="GS\GS.rc">
|
<ResourceCompile Include="GS\GS.rc">
|
||||||
|
|
Loading…
Reference in New Issue