diff options
| author | donkopunchstania <donkopunchstania@gmail.com> | 2011-03-27 02:55:08 +0000 |
|---|---|---|
| committer | donkopunchstania <donkopunchstania@gmail.com> | 2011-03-27 02:55:08 +0000 |
| commit | 07c4da6084e6d87cf25f87868da10c068721c19f (patch) | |
| tree | 5fbbd3476f80cfdb891e8c1f2cbeac86ec85c8b5 /Source | |
| parent | f8037e3ccf7723c2c02f7cfae74128561e304727 (diff) | |
Add graphics FIFO recorder and player for debugging the graphics system.
git-svn-id: https://dolphin-emu.googlecode.com/svn/trunk@7414 8ced0084-cf51-0410-be5f-012b33b47a6e
Diffstat (limited to 'Source')
35 files changed, 3275 insertions, 6 deletions
diff --git a/Source/Core/Core/CMakeLists.txt b/Source/Core/Core/CMakeLists.txt index f9ac5bbb4d..b5d5954844 100644 --- a/Source/Core/Core/CMakeLists.txt +++ b/Source/Core/Core/CMakeLists.txt @@ -58,6 +58,12 @@ set(SRCS Src/ActionReplay.cpp Src/DSP/Jit/DSPJitMultiplier.cpp Src/DSP/Jit/DSPJitUtil.cpp Src/DSP/Jit/DSPJitMisc.cpp + Src/FifoPlayer/FifoAnalyzer.cpp + Src/FifoPlayer/FifoDataFile.cpp + Src/FifoPlayer/FifoPlaybackAnalyzer.cpp + Src/FifoPlayer/FifoPlayer.cpp + Src/FifoPlayer/FifoRecordAnalyzer.cpp + Src/FifoPlayer/FifoRecorder.cpp Src/HLE/HLE.cpp Src/HLE/HLE_Misc.cpp Src/HLE/HLE_OS.cpp diff --git a/Source/Core/Core/Core.vcxproj b/Source/Core/Core/Core.vcxproj index bfb0ec3184..f7d7c97bf3 100644 --- a/Source/Core/Core/Core.vcxproj +++ b/Source/Core/Core/Core.vcxproj @@ -243,6 +243,12 @@ <ClCompile Include="Src\DSP\Jit\DSPJitRegCache.cpp" /> <ClCompile Include="Src\DSP\Jit\DSPJitUtil.cpp" /> <ClCompile Include="Src\DSP\LabelMap.cpp" /> + <ClCompile Include="Src\FifoPlayer\FifoAnalyzer.cpp" /> + <ClCompile Include="Src\FifoPlayer\FifoPlaybackAnalyzer.cpp" /> + <ClCompile Include="Src\FifoPlayer\FifoDataFile.cpp" /> + <ClCompile Include="Src\FifoPlayer\FifoPlayer.cpp" /> + <ClCompile Include="Src\FifoPlayer\FifoRecordAnalyzer.cpp" /> + <ClCompile Include="Src\FifoPlayer\FifoRecorder.cpp" /> <ClCompile Include="Src\GeckoCode.cpp" /> <ClCompile Include="Src\GeckoCodeConfig.cpp" /> <ClCompile Include="Src\HLE\HLE.cpp" /> @@ -433,6 +439,13 @@ <ClInclude Include="Src\DSP\Jit\DSPJitRegCache.h" /> <ClInclude Include="Src\DSP\Jit\DSPJitUtil.h" /> <ClInclude Include="Src\DSP\LabelMap.h" /> + <ClInclude Include="Src\FifoPlayer\FifoAnalyzer.h" /> + <ClInclude Include="Src\FifoPlayer\FifoPlaybackAnalyzer.h" /> + <ClInclude Include="Src\FifoPlayer\FifoDataFile.h" /> + <ClInclude Include="Src\FifoPlayer\FifoFileStruct.h" /> + <ClInclude Include="Src\FifoPlayer\FifoPlayer.h" /> + <ClInclude Include="Src\FifoPlayer\FifoRecorder.h" /> + <ClInclude Include="Src\FifoPlayer\FifoRecordAnalyzer.h" /> <ClInclude Include="Src\GeckoCode.h" /> <ClInclude Include="Src\GeckoCodeConfig.h" /> <ClInclude Include="Src\HLE\HLE.h" /> diff --git a/Source/Core/Core/Core.vcxproj.filters b/Source/Core/Core/Core.vcxproj.filters index f8d14b8631..06bae14d9b 100644 --- a/Source/Core/Core/Core.vcxproj.filters +++ b/Source/Core/Core/Core.vcxproj.filters @@ -534,6 +534,25 @@ <ClCompile Include="Src\NetPlayClient.cpp"> <Filter>NetPlay</Filter> </ClCompile> + <ClCompile Include="Src\BootManager.cpp" /> + <ClCompile Include="Src\FifoPlayer\FifoDataFile.cpp"> + <Filter>FifoPlayer</Filter> + </ClCompile> + <ClCompile Include="Src\FifoPlayer\FifoPlayer.cpp"> + <Filter>FifoPlayer</Filter> + </ClCompile> + <ClCompile Include="Src\FifoPlayer\FifoRecorder.cpp"> + <Filter>FifoPlayer</Filter> + </ClCompile> + <ClCompile Include="Src\FifoPlayer\FifoPlaybackAnalyzer.cpp"> + <Filter>FifoPlayer</Filter> + </ClCompile> + <ClCompile Include="Src\FifoPlayer\FifoRecordAnalyzer.cpp"> + <Filter>FifoPlayer</Filter> + </ClCompile> + <ClCompile Include="Src\FifoPlayer\FifoAnalyzer.cpp"> + <Filter>FifoPlayer</Filter> + </ClCompile> </ItemGroup> <ItemGroup> <ClInclude Include="Src\ConfigManager.h" /> @@ -989,6 +1008,28 @@ <ClInclude Include="Src\NetPlay.h"> <Filter>NetPlay</Filter> </ClInclude> + <ClInclude Include="Src\BootManager.h" /> + <ClInclude Include="Src\FifoPlayer\FifoDataFile.h"> + <Filter>FifoPlayer</Filter> + </ClInclude> + <ClInclude Include="Src\FifoPlayer\FifoFileStruct.h"> + <Filter>FifoPlayer</Filter> + </ClInclude> + <ClInclude Include="Src\FifoPlayer\FifoPlayer.h"> + <Filter>FifoPlayer</Filter> + </ClInclude> + <ClInclude Include="Src\FifoPlayer\FifoRecorder.h"> + <Filter>FifoPlayer</Filter> + </ClInclude> + <ClInclude Include="Src\FifoPlayer\FifoPlaybackAnalyzer.h"> + <Filter>FifoPlayer</Filter> + </ClInclude> + <ClInclude Include="Src\FifoPlayer\FifoRecordAnalyzer.h"> + <Filter>FifoPlayer</Filter> + </ClInclude> + <ClInclude Include="Src\FifoPlayer\FifoAnalyzer.h"> + <Filter>FifoPlayer</Filter> + </ClInclude> </ItemGroup> <ItemGroup> <None Include="CMakeLists.txt" /> @@ -1124,5 +1165,8 @@ <Filter Include="NetPlay"> <UniqueIdentifier>{231ceb02-1122-402a-87a8-094a9ed768c2}</UniqueIdentifier> </Filter> + <Filter Include="FifoPlayer"> + <UniqueIdentifier>{ca7d56f7-4e84-4d15-9aea-7ae6fa7d6586}</UniqueIdentifier> + </Filter> </ItemGroup> </Project>
\ No newline at end of file diff --git a/Source/Core/Core/Src/Boot/Boot.cpp b/Source/Core/Core/Src/Boot/Boot.cpp index ff1a437255..45b33b8190 100644 --- a/Source/Core/Core/Src/Boot/Boot.cpp +++ b/Source/Core/Core/Src/Boot/Boot.cpp @@ -364,6 +364,10 @@ bool CBoot::BootUp() break; } + case SCoreStartupParameter::BOOT_DFF: + // do nothing + break; + default: { PanicAlertT("Tried to load an unknown file type."); diff --git a/Source/Core/Core/Src/Core.cpp b/Source/Core/Core/Src/Core.cpp index 404ea95f70..76164a13b0 100644 --- a/Source/Core/Core/Src/Core.cpp +++ b/Source/Core/Core/Src/Core.cpp @@ -35,6 +35,7 @@ #include "CPUDetect.h" #include "CoreTiming.h" #include "Boot/Boot.h" +#include "FifoPlayer/FifoPlayer.h" #include "HW/Memmap.h" #include "HW/ProcessorInterface.h" @@ -332,6 +333,33 @@ void CpuThread() return; } +void FifoPlayerThread() +{ + const SCoreStartupParameter& _CoreParameter = SConfig::GetInstance().m_LocalCoreStartupParameter; + + if (_CoreParameter.bCPUThread) + { + Common::SetCurrentThreadName("FIFO player thread"); + } + else + { + g_video_backend->Video_Prepare(); + Common::SetCurrentThreadName("FIFO-GPU thread"); + } + + if (_CoreParameter.bLockThreads) + Common::SetCurrentThreadAffinity(1); // Force to first core + + // Enter CPU run loop. When we leave it - we are done. + if (FifoPlayer::GetInstance().Open(_CoreParameter.m_strFilename)) + { + FifoPlayer::GetInstance().Play(); + FifoPlayer::GetInstance().Close(); + } + + return; +} + // Initalize and create emulation thread // Call browser: Init():g_EmuThread(). // See the BootManager.cpp file description for a complete call schedule. @@ -372,6 +400,13 @@ void EmuThread() Host_UpdateDisasmDialog(); Host_UpdateMainFrame(); + // Determine the cpu thread function + void (*cpuThreadFunc)(void); + if (_CoreParameter.m_BootType == SCoreStartupParameter::BOOT_DFF) + cpuThreadFunc = FifoPlayerThread; + else + cpuThreadFunc = CpuThread; + // ENTER THE VIDEO THREAD LOOP if (_CoreParameter.bCPUThread) { @@ -382,7 +417,7 @@ void EmuThread() g_video_backend->Video_Prepare(); // Spawn the CPU thread - g_cpu_thread = std::thread(CpuThread); + g_cpu_thread = std::thread(cpuThreadFunc); // become the GPU thread g_video_backend->Video_EnterLoop(); @@ -400,7 +435,7 @@ void EmuThread() Common::SetCurrentThreadName("Emuthread - Idle"); // Spawn the CPU+GPU thread - g_cpu_thread = std::thread(CpuThread); + g_cpu_thread = std::thread(cpuThreadFunc); while (PowerPC::GetState() != PowerPC::CPU_POWERDOWN) { diff --git a/Source/Core/Core/Src/CoreParameter.cpp b/Source/Core/Core/Src/CoreParameter.cpp index 22a12fec62..c4b2e7ee20 100644 --- a/Source/Core/Core/Src/CoreParameter.cpp +++ b/Source/Core/Core/Src/CoreParameter.cpp @@ -29,6 +29,7 @@ #include "CoreParameter.h" #include "ConfigManager.h" #include "Core.h" // for bWii +#include "FifoPlayer/FifoDataFile.h" SCoreStartupParameter::SCoreStartupParameter() : hInstance(0), @@ -197,6 +198,21 @@ bool SCoreStartupParameter::AutoSetup(EBootBS2 _BootBS2) m_BootType = BOOT_DOL; bNTSC = true; } + else if (!strcasecmp(Extension.c_str(), ".dff")) + { + bWii = true; + Region = USA_DIR; + bNTSC = true; + m_BootType = BOOT_DFF; + + FifoDataFile *ddfFile = FifoDataFile::Load(m_strFilename.c_str(), true); + + if (ddfFile) + { + bWii = ddfFile->GetIsWii(); + delete ddfFile; + } + } else if (DiscIO::CNANDContentManager::Access().GetNANDLoader(m_strFilename).IsValid()) { const DiscIO::IVolume* pVolume = DiscIO::CreateVolumeFromFilename(m_strFilename.c_str()); diff --git a/Source/Core/Core/Src/CoreParameter.h b/Source/Core/Core/Src/CoreParameter.h index 188ec63a10..4a1b43a4fa 100644 --- a/Source/Core/Core/Src/CoreParameter.h +++ b/Source/Core/Core/Src/CoreParameter.h @@ -153,7 +153,8 @@ struct SCoreStartupParameter BOOT_ELF, BOOT_DOL, BOOT_WII_NAND, - BOOT_BS2 + BOOT_BS2, + BOOT_DFF }; EBootType m_BootType; diff --git a/Source/Core/Core/Src/FifoPlayer/FifoAnalyzer.cpp b/Source/Core/Core/Src/FifoPlayer/FifoAnalyzer.cpp new file mode 100644 index 0000000000..dd975ef82a --- /dev/null +++ b/Source/Core/Core/Src/FifoPlayer/FifoAnalyzer.cpp @@ -0,0 +1,235 @@ +// Copyright (C) 2003 Dolphin Project.
+
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, version 2.0.
+
+// This program 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 2.0 for more details.
+
+// A copy of the GPL 2.0 should have been included with the program.
+// If not, see http://www.gnu.org/licenses/
+
+// Official SVN repository and contact information can be found at
+// http://code.google.com/p/dolphin-emu/
+
+#include "FifoAnalyzer.h"
+
+#include "Core.h"
+
+#include "VertexLoader.h"
+#include "VertexLoader_Position.h"
+#include "VertexLoader_Normal.h"
+#include "VertexLoader_TextCoord.h"
+
+namespace FifoAnalyzer
+{
+
+void Init()
+{
+ VertexLoader_Normal::Init();
+ VertexLoader_Position::Init();
+ VertexLoader_TextCoord::Init();
+}
+
+u8 ReadFifo8(u8 *&data)
+{
+ u8 value = data[0];
+ data += 1;
+ return value;
+}
+
+u16 ReadFifo16(u8 *&data)
+{
+ u16 value = Common::swap16(data);
+ data += 2;
+ return value;
+}
+
+u32 ReadFifo32(u8 *&data)
+{
+ u32 value = Common::swap32(data);
+ data += 4;
+ return value;
+}
+
+void InitBPMemory(BPMemory *bpMem)
+{
+ memset(bpMem, 0, sizeof(BPMemory));
+ bpMem->bpMask = 0x00FFFFFF;
+}
+
+BPCmd DecodeBPCmd(u32 value, const BPMemory &bpMem)
+{
+ //handle the mask register
+ int opcode = value >> 24;
+ int oldval = ((u32*)&bpMem)[opcode];
+ int newval = (oldval & ~bpMem.bpMask) | (value & bpMem.bpMask);
+ int changes = (oldval ^ newval) & 0xFFFFFF;
+
+ BPCmd bp = {opcode, changes, newval};
+
+ return bp;
+}
+
+void LoadBPReg(const BPCmd &bp, BPMemory &bpMem)
+{
+ ((u32*)&bpMem)[bp.address] = bp.newvalue;
+
+ //reset the mask register
+ if (bp.address != 0xFE)
+ bpMem.bpMask = 0xFFFFFF;
+}
+
+void GetTlutLoadData(u32 &tlutAddr, u32 &memAddr, u32 &tlutXferCount, BPMemory &bpMem)
+{
+ tlutAddr = (bpMem.tlutXferDest & 0x3FF) << 9;
+ tlutXferCount = (bpMem.tlutXferDest & 0x1FFC00) >> 5;
+
+ // TODO - figure out a cleaner way.
+ if (Core::g_CoreStartupParameter.bWii)
+ memAddr = bpmem.tlutXferSrc << 5;
+ else
+ memAddr = (bpmem.tlutXferSrc & 0xFFFFF) << 5;
+}
+
+void LoadCPReg(u32 subCmd, u32 value, CPMemory &cpMem)
+{
+ switch (subCmd & 0xF0)
+ {
+ case 0x50:
+ cpMem.vtxDesc.Hex &= ~0x1FFFF; // keep the Upper bits
+ cpMem.vtxDesc.Hex |= value;
+ break;
+
+ case 0x60:
+ cpMem.vtxDesc.Hex &= 0x1FFFF; // keep the lower 17Bits
+ cpMem.vtxDesc.Hex |= (u64)value << 17;
+ break;
+
+ case 0x70:
+ _assert_((subCmd & 0x0F) < 8);
+ cpMem.vtxAttr[subCmd & 7].g0.Hex = value;
+ break;
+
+ case 0x80:
+ _assert_((subCmd & 0x0F) < 8);
+ cpMem.vtxAttr[subCmd & 7].g1.Hex = value;
+ break;
+
+ case 0x90:
+ _assert_((subCmd & 0x0F) < 8);
+ cpMem.vtxAttr[subCmd & 7].g2.Hex = value;
+ break;
+
+ case 0xA0:
+ cpMem.arrayBases[subCmd & 0xF] = value;
+ break;
+
+ case 0xB0:
+ cpMem.arrayStrides[subCmd & 0xF] = value & 0xFF;
+ break;
+ }
+}
+
+u32 CalculateVertexSize(int vatIndex, const CPMemory &cpMem)
+{
+ u32 vertexSize = 0;
+
+ int sizes[21];
+ CalculateVertexElementSizes(sizes, vatIndex, cpMem);
+
+ for (int i = 0; i < 21; ++i)
+ vertexSize += sizes[i];
+
+ return vertexSize;
+}
+
+void CalculateVertexElementSizes(int sizes[], int vatIndex, const CPMemory &cpMem)
+{
+ const TVtxDesc &vtxDesc = cpMem.vtxDesc;
+ const VAT &vtxAttr = cpMem.vtxAttr[vatIndex];
+
+ // Colors
+ const int colDesc[2] = {vtxDesc.Color0, vtxDesc.Color1};
+ const int colComp[2] = {vtxAttr.g0.Color0Comp, vtxAttr.g0.Color1Comp};
+
+ const int tcElements[8] =
+ {
+ vtxAttr.g0.Tex0CoordElements, vtxAttr.g1.Tex1CoordElements, vtxAttr.g1.Tex2CoordElements,
+ vtxAttr.g1.Tex3CoordElements, vtxAttr.g1.Tex4CoordElements, vtxAttr.g2.Tex5CoordElements,
+ vtxAttr.g2.Tex6CoordElements, vtxAttr.g2.Tex7CoordElements
+ };
+
+ const int tcFormat[8] =
+ {
+ vtxAttr.g0.Tex0CoordFormat, vtxAttr.g1.Tex1CoordFormat, vtxAttr.g1.Tex2CoordFormat,
+ vtxAttr.g1.Tex3CoordFormat, vtxAttr.g1.Tex4CoordFormat, vtxAttr.g2.Tex5CoordFormat,
+ vtxAttr.g2.Tex6CoordFormat, vtxAttr.g2.Tex7CoordFormat
+ };
+
+ // Add position and texture matrix indices
+ u64 vtxDescHex = cpMem.vtxDesc.Hex;
+ for (int i = 0; i < 9; ++i)
+ {
+ sizes[i] = vtxDescHex & 1;
+ vtxDescHex >>= 1;
+ }
+
+ // Position
+ sizes[9] = VertexLoader_Position::GetSize(vtxDesc.Position, vtxAttr.g0.PosFormat, vtxAttr.g0.PosElements);
+
+ // Normals
+ if (vtxDesc.Normal != NOT_PRESENT)
+ {
+ sizes[10] = VertexLoader_Normal::GetSize(vtxDesc.Normal, vtxAttr.g0.NormalFormat, vtxAttr.g0.NormalElements, vtxAttr.g0.NormalIndex3);
+ }
+ else
+ {
+ sizes[10] = 0;
+ }
+
+ // Colors
+ for (int i = 0; i < 2; i++)
+ {
+ int size = 0;
+
+ switch (colDesc[i])
+ {
+ case NOT_PRESENT:
+ break;
+ case DIRECT:
+ switch (colComp[i])
+ {
+ case FORMAT_16B_565: size = 2; break;
+ case FORMAT_24B_888: size = 3; break;
+ case FORMAT_32B_888x: size = 4; break;
+ case FORMAT_16B_4444: size = 2; break;
+ case FORMAT_24B_6666: size = 3; break;
+ case FORMAT_32B_8888: size = 4; break;
+ default: _assert_(0); break;
+ }
+ break;
+ case INDEX8:
+ size = 1;
+ break;
+ case INDEX16:
+ size = 2;
+ break;
+ }
+
+ sizes[11 + i] = size;
+ }
+
+ // Texture coordinates
+ vtxDescHex = vtxDesc.Hex >> 17;
+ for (int i = 0; i < 8; i++)
+ {
+ sizes[13 + i] = VertexLoader_TextCoord::GetSize(vtxDescHex & 3, tcFormat[i], tcElements[i]);
+ vtxDescHex >>= 2;
+ }
+}
+
+}
\ No newline at end of file diff --git a/Source/Core/Core/Src/FifoPlayer/FifoAnalyzer.h b/Source/Core/Core/Src/FifoPlayer/FifoAnalyzer.h new file mode 100644 index 0000000000..55f1917e57 --- /dev/null +++ b/Source/Core/Core/Src/FifoPlayer/FifoAnalyzer.h @@ -0,0 +1,54 @@ +// Copyright (C) 2003 Dolphin Project.
+
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, version 2.0.
+
+// This program 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 2.0 for more details.
+
+// A copy of the GPL 2.0 should have been included with the program.
+// If not, see http://www.gnu.org/licenses/
+
+// Official SVN repository and contact information can be found at
+// http://code.google.com/p/dolphin-emu/
+
+#ifndef _FIFOANALYZER_H
+#define _FIFOANALYZER_H
+
+#include "Common.h"
+
+#include "BPMemory.h"
+#include "CPMemory.h"
+
+namespace FifoAnalyzer
+{
+ void Init();
+
+ u8 ReadFifo8(u8 *&data);
+ u16 ReadFifo16(u8 *&data);
+ u32 ReadFifo32(u8 *&data);
+
+ // TODO- move to video common
+ void InitBPMemory(BPMemory *bpMem);
+ BPCmd DecodeBPCmd(u32 value, const BPMemory &bpMem);
+ void LoadBPReg(const BPCmd &bp, BPMemory &bpMem);
+ void GetTlutLoadData(u32 &tlutAddr, u32 &memAddr, u32 &tlutXferCount, BPMemory &bpMem);
+
+ struct CPMemory
+ {
+ TVtxDesc vtxDesc;
+ VAT vtxAttr[8];
+ u32 arrayBases[16];
+ u32 arrayStrides[16];
+ };
+
+ void LoadCPReg(u32 subCmd, u32 value, CPMemory &cpMem);
+
+ u32 CalculateVertexSize(int vatIndex, const CPMemory &cpMem);
+ void CalculateVertexElementSizes(int sizes[], int vatIndex, const CPMemory &cpMem);
+}
+
+#endif
\ No newline at end of file diff --git a/Source/Core/Core/Src/FifoPlayer/FifoDataFile.cpp b/Source/Core/Core/Src/FifoPlayer/FifoDataFile.cpp new file mode 100644 index 0000000000..7933e67e64 --- /dev/null +++ b/Source/Core/Core/Src/FifoPlayer/FifoDataFile.cpp @@ -0,0 +1,285 @@ +// Copyright (C) 2003 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program 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 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#include "FifoDataFile.h" +#include "FifoFileStruct.h" + +#include "FileUtil.h" + +using namespace FifoFileStruct; +using namespace std; + +FifoDataFile::FifoDataFile() : + m_Flags(0) +{ +} + +FifoDataFile::~FifoDataFile() +{ + for (unsigned int frameIdx = 0; frameIdx < m_Frames.size(); ++frameIdx) + { + FifoFrameInfo &frame = m_Frames[frameIdx]; + + for (unsigned int i = 0; i < frame.memoryUpdates.size(); ++i) + delete []frame.memoryUpdates[i].data; + + delete []frame.fifoData; + } +} + +void FifoDataFile::SetIsWii(bool isWii) +{ + SetFlag(FLAG_IS_WII, isWii); +} + +bool FifoDataFile::GetIsWii() const +{ + return GetFlag(FLAG_IS_WII); +} + +void FifoDataFile::AddFrame(const FifoFrameInfo &frameInfo) +{ + m_Frames.push_back(frameInfo); +} + +bool FifoDataFile::Save(const char *filename) +{ + File::IOFile file; + if (!file.Open(filename, "wb")) + return false; + + // Add space for header + PadFile(sizeof(FileHeader), file); + + // Add space for frame list + u64 frameListOffset = file.Tell(); + for (unsigned int i = 0; i < m_Frames.size(); ++i) + PadFile(sizeof(FileFrameInfo), file); + + u64 bpMemOffset = file.Tell(); + file.WriteArray(m_BPMem, BP_MEM_SIZE); + + u64 cpMemOffset = file.Tell(); + file.WriteArray(m_CPMem, CP_MEM_SIZE); + + u64 xfMemOffset = file.Tell(); + file.WriteArray(m_XFMem, XF_MEM_SIZE); + + u64 xfRegsOffset = file.Tell(); + file.WriteArray(m_XFRegs, XF_REGS_SIZE); + + // Write header + FileHeader header; + header.fileId = FILE_ID; + header.file_version = VERSION_NUMBER; + header.min_loader_version = MIN_LOADER_VERSION; + + header.bpMemOffset = bpMemOffset; + header.bpMemSize = BP_MEM_SIZE; + + header.cpMemOffset = cpMemOffset; + header.cpMemSize = CP_MEM_SIZE; + + header.xfMemOffset = xfMemOffset; + header.xfMemSize = XF_MEM_SIZE; + + header.xfRegsOffset = xfRegsOffset; + header.xfRegsSize = XF_REGS_SIZE; + + header.frameListOffset = frameListOffset; + header.frameCount = m_Frames.size(); + + header.flags = m_Flags; + + file.Seek(0, SEEK_SET); + file.WriteBytes(&header, sizeof(FileHeader)); + + // Write frames list + for (unsigned int i = 0; i < m_Frames.size(); ++i) + { + const FifoFrameInfo &srcFrame = m_Frames[i]; + + // Write FIFO data + file.Seek(0, SEEK_END); + u64 dataOffset = file.Tell(); + file.WriteBytes(srcFrame.fifoData, srcFrame.fifoDataSize); + + u64 memoryUpdatesOffset = WriteMemoryUpdates(srcFrame.memoryUpdates, file); + + FileFrameInfo dstFrame; + dstFrame.fifoDataSize = srcFrame.fifoDataSize; + dstFrame.fifoDataOffset = dataOffset; + dstFrame.fifoStart = srcFrame.fifoStart; + dstFrame.fifoEnd = srcFrame.fifoEnd; + dstFrame.memoryUpdatesOffset = memoryUpdatesOffset; + dstFrame.numMemoryUpdates = srcFrame.memoryUpdates.size(); + + // Write frame info + u64 frameOffset = frameListOffset + (i * sizeof(FileFrameInfo)); + file.Seek(frameOffset, SEEK_SET); + file.WriteBytes(&dstFrame, sizeof(FileFrameInfo)); + } + + if (!file.Close()) + return false; + + return true; +} + +FifoDataFile *FifoDataFile::Load(const std::string &filename, bool flagsOnly) +{ + File::IOFile file; + file.Open(filename, "rb"); + if (!file) + return NULL; + + FileHeader header; + file.ReadBytes(&header, sizeof(header)); + + if (header.fileId != FILE_ID || header.min_loader_version > VERSION_NUMBER) + { + file.Close(); + return NULL; + } + + FifoDataFile* dataFile = new FifoDataFile; + + dataFile->m_Flags = header.flags; + + if (flagsOnly) + { + file.Close(); + return dataFile; + } + + u32 size = std::min((u32)BP_MEM_SIZE, header.bpMemSize); + file.Seek(header.bpMemOffset, SEEK_SET); + file.ReadArray(dataFile->m_BPMem, size); + + size = std::min((u32)CP_MEM_SIZE, header.cpMemSize); + file.Seek(header.cpMemOffset, SEEK_SET); + file.ReadArray(dataFile->m_CPMem, size); + + size = std::min((u32)XF_MEM_SIZE, header.xfMemSize); + file.Seek(header.xfMemOffset, SEEK_SET); + file.ReadArray(dataFile->m_XFMem, size); + + size = std::min((u32)XF_REGS_SIZE, header.xfRegsSize); + file.Seek(header.xfRegsOffset, SEEK_SET); + file.ReadArray(dataFile->m_XFRegs, size); + + // Read frames + for (u32 i = 0; i < header.frameCount; ++i) + { + u64 frameOffset = header.frameListOffset + (i * sizeof(FileFrameInfo)); + file.Seek(frameOffset, SEEK_SET); + FileFrameInfo srcFrame; + file.ReadBytes(&srcFrame, sizeof(FileFrameInfo)); + + FifoFrameInfo dstFrame; + dstFrame.fifoData = new u8[srcFrame.fifoDataSize]; + dstFrame.fifoDataSize = srcFrame.fifoDataSize; + dstFrame.fifoStart = srcFrame.fifoStart; + dstFrame.fifoEnd = srcFrame.fifoEnd; + + file.Seek(srcFrame.fifoDataOffset, SEEK_SET); + file.ReadBytes(dstFrame.fifoData, srcFrame.fifoDataSize); + + ReadMemoryUpdates(srcFrame.memoryUpdatesOffset, srcFrame.numMemoryUpdates, dstFrame.memoryUpdates, file); + + dataFile->AddFrame(dstFrame); + } + + file.Close(); + + return dataFile; +} + +void FifoDataFile::PadFile(u32 numBytes, File::IOFile &file) +{ + FILE *handle = file.GetHandle(); + + for (u32 i = 0; i < numBytes; ++i) + fputc(0, handle); +} + +void FifoDataFile::SetFlag(u32 flag, bool set) +{ + if (set) + m_Flags |= flag; + else + m_Flags &= ~flag; +} + +bool FifoDataFile::GetFlag(u32 flag) const +{ + return !!(m_Flags & flag); +} + +u64 FifoDataFile::WriteMemoryUpdates(const std::vector<MemoryUpdate> &memUpdates, File::IOFile &file) +{ + // Add space for memory update list + u64 updateListOffset = file.Tell(); + for (unsigned int i = 0; i < memUpdates.size(); ++i) + PadFile(sizeof(FileMemoryUpdate), file); + + for (unsigned int i = 0; i < memUpdates.size(); ++i) + { + const MemoryUpdate &srcUpdate = memUpdates[i]; + + // Write memory + file.Seek(0, SEEK_END); + u64 dataOffset = file.Tell(); + file.WriteBytes(srcUpdate.data, srcUpdate.size); + + FileMemoryUpdate dstUpdate; + dstUpdate.address = srcUpdate.address; + dstUpdate.dataOffset = dataOffset; + dstUpdate.dataSize = srcUpdate.size; + dstUpdate.fifoPosition = srcUpdate.fifoPosition; + dstUpdate.type = srcUpdate.type; + + u64 updateOffset = updateListOffset + (i * sizeof(FileMemoryUpdate)); + file.Seek(updateOffset, SEEK_SET); + file.WriteBytes(&dstUpdate, sizeof(FileMemoryUpdate)); + } + + return updateListOffset; +} + +void FifoDataFile::ReadMemoryUpdates(u64 fileOffset, u32 numUpdates, std::vector<MemoryUpdate> &memUpdates, File::IOFile &file) +{ + memUpdates.resize(numUpdates); + + for (u32 i = 0; i < numUpdates; ++i) + { + u64 updateOffset = fileOffset + (i * sizeof(FileMemoryUpdate)); + file.Seek(updateOffset, SEEK_SET); + FileMemoryUpdate srcUpdate; + file.ReadBytes(&srcUpdate, sizeof(FileMemoryUpdate)); + + MemoryUpdate &dstUpdate = memUpdates[i]; + dstUpdate.address = srcUpdate.address; + dstUpdate.fifoPosition = srcUpdate.fifoPosition; + dstUpdate.size = srcUpdate.dataSize; + dstUpdate.data = new u8[srcUpdate.dataSize]; + dstUpdate.type = (MemoryUpdate::Type)srcUpdate.type; + + file.Seek(srcUpdate.dataOffset, SEEK_SET); + file.ReadBytes(dstUpdate.data, srcUpdate.dataSize); + } +}
\ No newline at end of file diff --git a/Source/Core/Core/Src/FifoPlayer/FifoDataFile.h b/Source/Core/Core/Src/FifoPlayer/FifoDataFile.h new file mode 100644 index 0000000000..409b74ba8e --- /dev/null +++ b/Source/Core/Core/Src/FifoPlayer/FifoDataFile.h @@ -0,0 +1,112 @@ +// Copyright (C) 2003 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program 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 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#ifndef _FIFODATAFILE_H_ +#define _FIFODATAFILE_H_ + +#include "Common.h" +#include <vector> + +namespace File +{ + class IOFile; +} + +struct MemoryUpdate +{ + enum Type + { + TEXTURE_MAP = 0x01, + XF_DATA = 0x02, + VERTEX_STREAM = 0x04, + TLUT = 0x08 + }; + + u32 fifoPosition; + u32 address; + u32 size; + u8 *data; + Type type; +}; + +struct FifoFrameInfo +{ + u8 *fifoData; + u32 fifoDataSize; + + u32 fifoStart; + u32 fifoEnd; + + // Must be sorted by fifoPosition + std::vector<MemoryUpdate> memoryUpdates; +}; + +class FifoDataFile +{ +public: + enum + { + BP_MEM_SIZE = 256, + CP_MEM_SIZE = 256, + XF_MEM_SIZE = 4096, + XF_REGS_SIZE = 96, + }; + + FifoDataFile(); + ~FifoDataFile(); + + void SetIsWii(bool isWii); + bool GetIsWii() const; + + u32 *GetBPMem() { return m_BPMem; } + u32 *GetCPMem() { return m_CPMem; } + u32 *GetXFMem() { return m_XFMem; } + u32 *GetXFRegs() { return m_XFRegs; } + + void AddFrame(const FifoFrameInfo &frameInfo); + const FifoFrameInfo &GetFrame(int frame) const { return m_Frames[frame]; } + int GetFrameCount() { return m_Frames.size(); } + + bool Save(const char *filename); + + static FifoDataFile *Load(const std::string &filename, bool flagsOnly); + +private: + enum + { + FLAG_IS_WII = 1 + }; + + void PadFile(u32 numBytes, File::IOFile &file); + + void SetFlag(u32 flag, bool set); + bool GetFlag(u32 flag) const; + + u64 WriteMemoryUpdates(const std::vector<MemoryUpdate> &memUpdates, File::IOFile &file); + static void ReadMemoryUpdates(u64 fileOffset, u32 numUpdates, std::vector<MemoryUpdate> &memUpdates, File::IOFile &file); + + u32 m_BPMem[BP_MEM_SIZE]; + u32 m_CPMem[CP_MEM_SIZE]; + u32 m_XFMem[XF_MEM_SIZE]; + u32 m_XFRegs[XF_REGS_SIZE]; + + u32 m_Flags; + + std::vector<FifoFrameInfo> m_Frames; +}; + +#endif
\ No newline at end of file diff --git a/Source/Core/Core/Src/FifoPlayer/FifoFileStruct.h b/Source/Core/Core/Src/FifoPlayer/FifoFileStruct.h new file mode 100644 index 0000000000..e35ebd31bf --- /dev/null +++ b/Source/Core/Core/Src/FifoPlayer/FifoFileStruct.h @@ -0,0 +1,85 @@ +// Copyright (C) 2003 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program 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 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#ifndef _FIFOFILESTRUCT_H_ +#define _FIFOFILESTRUCT_H_ + +#include "Common.h" + +namespace FifoFileStruct +{ + +enum +{ + FILE_ID = 0x0d01f1f0, + VERSION_NUMBER = 1, + MIN_LOADER_VERSION = 1, +}; + +#pragma pack(push, 4) + +union FileHeader +{ + struct + { + u32 fileId; + u32 file_version; + u32 min_loader_version; + u64 bpMemOffset; + u32 bpMemSize; + u64 cpMemOffset; + u32 cpMemSize; + u64 xfMemOffset; + u32 xfMemSize; + u64 xfRegsOffset; + u32 xfRegsSize; + u64 frameListOffset; + u32 frameCount; + u32 flags; + }; + u32 rawData[32]; +}; + +union FileFrameInfo +{ + struct + { + u64 fifoDataOffset; + u32 fifoDataSize; + u32 fifoStart; + u32 fifoEnd; + u64 memoryUpdatesOffset; + u32 numMemoryUpdates; + }; + u32 rawData[16]; +}; + + +struct FileMemoryUpdate +{ + u32 fifoPosition; + u32 address; + u64 dataOffset; + u32 dataSize; + u8 type; +}; + +#pragma pack(pop) + +} + +#endif
\ No newline at end of file diff --git a/Source/Core/Core/Src/FifoPlayer/FifoPlaybackAnalyzer.cpp b/Source/Core/Core/Src/FifoPlayer/FifoPlaybackAnalyzer.cpp new file mode 100644 index 0000000000..ccec508cdb --- /dev/null +++ b/Source/Core/Core/Src/FifoPlayer/FifoPlaybackAnalyzer.cpp @@ -0,0 +1,346 @@ +// Copyright (C) 2003 Dolphin Project.
+
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, version 2.0.
+
+// This program 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 2.0 for more details.
+
+// A copy of the GPL 2.0 should have been included with the program.
+// If not, see http://www.gnu.org/licenses/
+
+// Official SVN repository and contact information can be found at
+// http://code.google.com/p/dolphin-emu/
+
+#include "FifoAnalyzer.h"
+#include "FifoDataFile.h"
+#include "FifoPlaybackAnalyzer.h"
+
+#include "Common.h"
+
+#include "OpcodeDecoding.h"
+#include "TextureDecoder.h"
+#include "VertexLoader.h"
+
+using namespace std;
+using namespace FifoAnalyzer;
+
+// For debugging
+#define LOG_FIFO_CMDS 0
+struct CmdData
+{
+ u32 size;
+ u32 offset;
+ u8 *ptr;
+};
+
+FifoPlaybackAnalyzer::FifoPlaybackAnalyzer()
+{
+ FifoAnalyzer::Init();
+}
+
+void FifoPlaybackAnalyzer::AnalyzeFrames(FifoDataFile *file, std::vector<AnalyzedFrameInfo> &frameInfo)
+{
+ // Load BP memory
+ u32 *bpMem = file->GetBPMem();
+ memcpy(&m_BpMem, bpMem, sizeof(BPMemory));
+
+ u32 *cpMem = file->GetCPMem();
+ FifoAnalyzer::LoadCPReg(0x50, cpMem[0x50], m_CpMem);
+ FifoAnalyzer::LoadCPReg(0x60, cpMem[0x60], m_CpMem);
+
+ for (int i = 0; i < 8; ++i)
+ {
+ FifoAnalyzer::LoadCPReg(0x70 + i, cpMem[0x70 + i], m_CpMem);
+ FifoAnalyzer::LoadCPReg(0x80 + i, cpMem[0x80 + i], m_CpMem);
+ FifoAnalyzer::LoadCPReg(0x90 + i, cpMem[0x90 + i], m_CpMem);
+ }
+
+ frameInfo.clear();
+ frameInfo.resize(file->GetFrameCount());
+
+ for (int frameIdx = 0; frameIdx < file->GetFrameCount(); ++frameIdx)
+ {
+ const FifoFrameInfo& frame = file->GetFrame(frameIdx);
+ AnalyzedFrameInfo& analyzed = frameInfo[frameIdx];
+
+ m_DrawingObject = false;
+
+ u32 cmdStart = 0;
+ u32 nextMemUpdate = 0;
+
+ // Debugging
+ vector<CmdData> prevCmds;
+
+ while (cmdStart < frame.fifoDataSize)
+ {
+ // Add memory updates that have occured before this point in the frame
+ while (nextMemUpdate < frame.memoryUpdates.size() && frame.memoryUpdates[nextMemUpdate].fifoPosition <= cmdStart)
+ {
+ AddMemoryUpdate(frame.memoryUpdates[nextMemUpdate], analyzed);
+ ++nextMemUpdate;
+ }
+
+ bool wasDrawing = m_DrawingObject;
+
+ u32 cmdSize = DecodeCommand(&frame.fifoData[cmdStart]);
+
+#if (LOG_FIFO_CMDS)
+ CmdData cmdData;
+ cmdData.offset = cmdStart;
+ cmdData.ptr = &frame.fifoData[cmdStart];
+ cmdData.size = cmdSize;
+ prevCmds.push_back(cmdData);
+#endif
+
+ // Check for error
+ if (cmdSize == 0)
+ {
+ // Clean up frame analysis
+ analyzed.objectStarts.clear();
+ analyzed.objectEnds.clear();
+
+ return;
+ }
+
+ if (wasDrawing != m_DrawingObject)
+ {
+ if (m_DrawingObject)
+ analyzed.objectStarts.push_back(cmdStart);
+ else
+ analyzed.objectEnds.push_back(cmdStart);
+ }
+
+ cmdStart += cmdSize;
+ }
+
+ if (analyzed.objectEnds.size() < analyzed.objectStarts.size())
+ analyzed.objectEnds.push_back(cmdStart);
+ }
+}
+
+void FifoPlaybackAnalyzer::AddMemoryUpdate(MemoryUpdate memUpdate, AnalyzedFrameInfo &frameInfo)
+{
+ u32 begin = memUpdate.address;
+ u32 end = memUpdate.address + memUpdate.size;
+
+ // Remove portions of memUpdate that overlap with memory ranges that have been written by the GP
+ for (unsigned int i = 0; i < m_WrittenMemory.size(); ++i)
+ {
+ const MemoryRange &range = m_WrittenMemory[i];
+
+ if (range.begin < end &&
+ range.end > begin)
+ {
+ s32 preSize = range.begin - begin;
+ s32 postSize = end - range.end;
+
+ if (postSize > 0)
+ {
+ if (preSize > 0)
+ {
+ memUpdate.size = preSize;
+ AddMemoryUpdate(memUpdate, frameInfo);
+ }
+
+ u32 bytesToRangeEnd = range.end - memUpdate.address;
+ memUpdate.data += bytesToRangeEnd;
+ memUpdate.size = postSize;
+ memUpdate.address = range.end;
+ }
+ else if (preSize > 0)
+ {
+ memUpdate.size = preSize;
+ }
+ else
+ {
+ // Ignore all of memUpdate
+ return;
+ }
+ }
+ }
+
+ frameInfo.memoryUpdates.push_back(memUpdate);
+}
+
+u32 FifoPlaybackAnalyzer::DecodeCommand(u8 *data)
+{
+ u8 *dataStart = data;
+
+ int cmd = ReadFifo8(data);
+
+ switch(cmd)
+ {
+ case GX_NOP:
+ case 0x44:
+ case GX_CMD_INVL_VC:
+ break;
+
+ case GX_LOAD_CP_REG:
+ {
+ m_DrawingObject = false;
+
+ u32 cmd2 = ReadFifo8(data);
+ u32 value = ReadFifo32(data);
+ FifoAnalyzer::LoadCPReg(cmd2, value, m_CpMem);
+ }
+ break;
+
+ case GX_LOAD_XF_REG:
+ {
+ m_DrawingObject = false;
+
+ u32 cmd2 = ReadFifo32(data);
+ u8 streamSize = ((cmd2 >> 16) & 15) + 1;
+
+ data += streamSize * 4;
+ }
+ break;
+
+ case GX_LOAD_INDX_A:
+ case GX_LOAD_INDX_B:
+ case GX_LOAD_INDX_C:
+ case GX_LOAD_INDX_D:
+ m_DrawingObject = false;
+ data += 4;
+ break;
+
+ case GX_CMD_CALL_DL:
+ // The recorder should have expanded display lists into the fifo stream and skipped the call to start them
+ // That is done to make it easier to track where memory is updated
+ _assert_(false);
+ data += 8;
+ break;
+
+ case GX_LOAD_BP_REG:
+ {
+ m_DrawingObject = false;
+
+ u32 cmd2 = ReadFifo32(data);
+ BPCmd bp = FifoAnalyzer::DecodeBPCmd(cmd2, m_BpMem);
+
+ FifoAnalyzer::LoadBPReg(bp, m_BpMem);
+
+ if (bp.address == BPMEM_TRIGGER_EFB_COPY)
+ StoreEfbCopyRegion();
+ }
+ break;
+
+ default:
+ if (cmd & 0x80)
+ {
+ m_DrawingObject = true;
+
+ u32 vtxAttrGroup = cmd & GX_VAT_MASK;
+ int vertexSize = FifoAnalyzer::CalculateVertexSize(vtxAttrGroup, m_CpMem);
+
+ u16 streamSize = ReadFifo16(data);
+
+ data += streamSize * vertexSize;
+ }
+ else
+ {
+ PanicAlert("FifoPlayer: Unknown Opcode (0x%x).\nAborting frame analysis.\n", cmd);
+ return 0;
+ }
+ break;
+ }
+
+ return data - dataStart;
+}
+
+void FifoPlaybackAnalyzer::StoreEfbCopyRegion()
+{
+ UPE_Copy peCopy = m_BpMem.triggerEFBCopy;
+
+ u32 copyfmt = peCopy.tp_realFormat();
+ bool bFromZBuffer = m_BpMem.zcontrol.pixel_format == PIXELFMT_Z24;
+ u32 address = bpmem.copyTexDest << 5;
+
+ u32 format = copyfmt;
+
+ if (peCopy.copy_to_xfb)
+ {
+ // Fake format to calculate size correctly
+ format = GX_TF_IA8;
+ }
+ else if (bFromZBuffer)
+ {
+ format |= _GX_TF_ZTF;
+ if (copyfmt == 11)
+ format = GX_TF_Z16;
+ else if (format < GX_TF_Z8 || format > GX_TF_Z24X8)
+ format |= _GX_TF_CTF;
+ }
+ else
+ {
+ if (copyfmt > GX_TF_RGBA8 || (copyfmt < GX_TF_RGB565 && !peCopy.intensity_fmt))
+ format |= _GX_TF_CTF;
+ }
+
+ int width = (m_BpMem.copyTexSrcWH.x + 1) >> peCopy.half_scale;
+ int height = (m_BpMem.copyTexSrcWH.y + 1) >> peCopy.half_scale;
+
+ u16 blkW = TexDecoder_GetBlockWidthInTexels(format) - 1;
+ u16 blkH = TexDecoder_GetBlockHeightInTexels(format) - 1;
+
+ s32 expandedWidth = (width + blkW) & (~blkW);
+ s32 expandedHeight = (height + blkH) & (~blkH);
+
+ int sizeInBytes = TexDecoder_GetTextureSizeInBytes(expandedWidth, expandedHeight, format);
+
+ StoreWrittenRegion(address, sizeInBytes);
+}
+
+void FifoPlaybackAnalyzer::StoreWrittenRegion(u32 address, u32 size)
+{
+ u32 end = address + size;
+ vector<MemoryRange>::iterator newRangeIter = m_WrittenMemory.end();
+
+ // Search for overlapping memory regions and expand them to include the new region
+ for (vector<MemoryRange>::iterator iter = m_WrittenMemory.begin(); iter != m_WrittenMemory.end();)
+ {
+ MemoryRange &range = *iter;
+
+ if (range.begin < end && range.end > address)
+ {
+ // range at iterator and new range overlap
+
+ if (newRangeIter == m_WrittenMemory.end())
+ {
+ // Expand range to include the written region
+ range.begin = std::min(address, range.begin);
+ range.end = std::max(end, range.end);
+ newRangeIter = iter;
+
+ ++iter;
+ }
+ else
+ {
+ // Expand region at rangeIter to include this range
+ MemoryRange &used = *newRangeIter;
+ used.begin = std::min(used.begin, range.begin);
+ used.end = std::max(used.end, range.end);
+
+ // Remove this entry
+ iter = m_WrittenMemory.erase(iter);
+ }
+ }
+ else
+ {
+ ++iter;
+ }
+ }
+
+ if (newRangeIter == m_WrittenMemory.end())
+ {
+ MemoryRange range;
+ range.begin = address;
+ range.end = end;
+
+ m_WrittenMemory.push_back(range);
+ }
+}
\ No newline at end of file diff --git a/Source/Core/Core/Src/FifoPlayer/FifoPlaybackAnalyzer.h b/Source/Core/Core/Src/FifoPlayer/FifoPlaybackAnalyzer.h new file mode 100644 index 0000000000..8c779d818d --- /dev/null +++ b/Source/Core/Core/Src/FifoPlayer/FifoPlaybackAnalyzer.h @@ -0,0 +1,64 @@ +// Copyright (C) 2003 Dolphin Project.
+
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, version 2.0.
+
+// This program 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 2.0 for more details.
+
+// A copy of the GPL 2.0 should have been included with the program.
+// If not, see http://www.gnu.org/licenses/
+
+// Official SVN repository and contact information can be found at
+// http://code.google.com/p/dolphin-emu/
+
+#ifndef _FIFOPLAYBACKANALYZER_H_
+#define _FIFOPLAYBACKANALYZER_H_
+
+#include "FifoAnalyzer.h"
+#include "FifoDataFile.h"
+
+#include <string>
+#include <vector>
+
+struct AnalyzedFrameInfo
+{
+ std::vector<u32> objectStarts;
+ std::vector<u32> objectEnds;
+ std::vector<MemoryUpdate> memoryUpdates;
+};
+
+class FifoPlaybackAnalyzer
+{
+public:
+ FifoPlaybackAnalyzer();
+
+ void AnalyzeFrames(FifoDataFile *file, std::vector<AnalyzedFrameInfo> &frameInfo);
+
+private:
+ struct MemoryRange
+ {
+ u32 begin;
+ u32 end;
+ };
+
+ void AddMemoryUpdate(MemoryUpdate memUpdate, AnalyzedFrameInfo &frameInfo);
+
+ u32 DecodeCommand(u8 *data);
+ void LoadBP(u32 value0);
+
+ void StoreEfbCopyRegion();
+ void StoreWrittenRegion(u32 address, u32 size);
+
+ bool m_DrawingObject;
+
+ std::vector<MemoryRange> m_WrittenMemory;
+
+ BPMemory m_BpMem;
+ FifoAnalyzer::CPMemory m_CpMem;
+};
+
+#endif
\ No newline at end of file diff --git a/Source/Core/Core/Src/FifoPlayer/FifoPlayer.cpp b/Source/Core/Core/Src/FifoPlayer/FifoPlayer.cpp new file mode 100644 index 0000000000..3acdfa41cb --- /dev/null +++ b/Source/Core/Core/Src/FifoPlayer/FifoPlayer.cpp @@ -0,0 +1,456 @@ +// Copyright (C) 2003 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program 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 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#include "FifoDataFile.h" +#include "FifoPlayer.h" + +#include "Common.h" +#include "CoreTiming.h" + +#include "HW/GPFifo.h" +#include "HW/Memmap.h" +#include "HW/SystemTimers.h" +#include "PowerPC/PowerPC.h" + +#include "BPMemory.h" + +FifoPlayer::~FifoPlayer() +{ + delete m_File; +} + +bool FifoPlayer::Open(const std::string& filename) +{ + Close(); + + m_File = FifoDataFile::Load(filename, false); + + if (m_File) + { + FifoPlaybackAnalyzer analyzer; + analyzer.AnalyzeFrames(m_File, m_FrameInfo); + + m_FrameRangeEnd = m_File->GetFrameCount(); + } + + if (m_FileLoadedCb) + m_FileLoadedCb(); + + return (m_File != NULL); +} + +void FifoPlayer::Close() +{ + delete m_File; + m_File = NULL; + + m_FrameRangeStart = 0; + m_FrameRangeEnd = 0; +} + +bool FifoPlayer::Play() +{ + if (!m_File) + return false; + + if (m_File->GetFrameCount() == 0) + return false; + + m_CurrentFrame = m_FrameRangeStart; + + LoadMemory(); + + // This loop replaces the CPU loop that occurs when a game is run + while (PowerPC::GetState() != PowerPC::CPU_POWERDOWN) + { + if (PowerPC::GetState() == PowerPC::CPU_RUNNING) + { + if (m_CurrentFrame >= m_FrameRangeEnd) + { + m_CurrentFrame = m_FrameRangeStart; + + CoreTiming::downcount = 0; + CoreTiming::Advance(); + } + else + { + if (m_FrameWrittenCb) + m_FrameWrittenCb(); + + if (m_EarlyMemoryUpdates && m_CurrentFrame == m_FrameRangeStart) + WriteAllMemoryUpdates(); + + WriteFrame(m_File->GetFrame(m_CurrentFrame), m_FrameInfo[m_CurrentFrame]); + ++m_CurrentFrame; + } + } + } + + return true; +} + +u32 FifoPlayer::GetFrameObjectCount() +{ + if (m_CurrentFrame < m_FrameInfo.size()) + return m_FrameInfo[m_CurrentFrame].objectStarts.size(); + + return 0; +} + +void FifoPlayer::SetFrameRangeStart(u32 start) +{ + if (m_File) + { + u32 frameCount = m_File->GetFrameCount(); + if (start > frameCount) + start = frameCount; + + m_FrameRangeStart = start; + if (m_FrameRangeEnd < start) + m_FrameRangeEnd = start; + + if (m_CurrentFrame < m_FrameRangeStart) + m_CurrentFrame = m_FrameRangeStart; + } +} + +void FifoPlayer::SetFrameRangeEnd(u32 end) +{ + if (m_File) + { + u32 frameCount = m_File->GetFrameCount(); + if (end > frameCount) + end = frameCount; + + m_FrameRangeEnd = end; + if (m_FrameRangeStart > end) + m_FrameRangeStart = end; + + if (m_CurrentFrame >= m_FrameRangeEnd) + m_CurrentFrame = m_FrameRangeStart; + } +} + +FifoPlayer &FifoPlayer::GetInstance() +{ + static FifoPlayer instance; + return instance; +} + +FifoPlayer::FifoPlayer() : + m_CurrentFrame(0), + m_FrameRangeStart(0), + m_FrameRangeEnd(0), + m_ObjectRangeStart(0), + m_ObjectRangeEnd(10000), + m_EarlyMemoryUpdates(false), + m_FileLoadedCb(NULL), + m_FrameWrittenCb(NULL), + m_File(NULL) +{ +} + +void FifoPlayer::WriteFrame(const FifoFrameInfo &frame, const AnalyzedFrameInfo &info) +{ + // Core timing information + m_CyclesPerFrame = SystemTimers::GetTicksPerSecond() / 60; + m_ElapsedCycles = 0; + m_FrameFifoSize = frame.fifoDataSize; + + // Determine start and end objects + u32 numObjects = info.objectStarts.size(); + u32 drawStart = std::min(numObjects, m_ObjectRangeStart); + u32 drawEnd = std::min(numObjects - 1, m_ObjectRangeEnd); + + u32 position = 0; + u32 memoryUpdate = 0; + + // Skip memory updates during frame if true + if (m_EarlyMemoryUpdates) + memoryUpdate = frame.memoryUpdates.size(); + + if (numObjects > 0) + { + u32 objectNum = 0; + + // Write fifo data skipping objects before the draw range + while (objectNum < drawStart) + { + WriteFramePart(position, info.objectStarts[objectNum], memoryUpdate, frame, info); + + position = info.objectEnds[objectNum]; + ++objectNum; + } + + // Write objects in draw range + if (objectNum < numObjects && drawStart <= drawEnd) + { + objectNum = drawEnd; + WriteFramePart(position, info.objectEnds[objectNum], memoryUpdate, frame, info); + position = info.objectEnds[objectNum]; + ++objectNum; + } + + // Write fifo data skipping objects after the draw range + while (objectNum < numObjects) + { + WriteFramePart(position, info.objectStarts[objectNum], memoryUpdate, frame, info); + + position = info.objectEnds[objectNum]; + ++objectNum; + } + } + + // Write data after the last object + WriteFramePart(position, frame.fifoDataSize, memoryUpdate, frame, info); + + FlushWGP(); +} + +void FifoPlayer::WriteFramePart(u32 dataStart, u32 dataEnd, u32 &nextMemUpdate, const FifoFrameInfo &frame, const AnalyzedFrameInfo &info) +{ + u8 *data = frame.fifoData; + + while (nextMemUpdate < frame.memoryUpdates.size() && dataStart < dataEnd) + { + const MemoryUpdate &memUpdate = info.memoryUpdates[nextMemUpdate]; + + if (memUpdate.fifoPosition < dataEnd) + { + if (dataStart < memUpdate.fifoPosition) + { + WriteFifo(data, dataStart, memUpdate.fifoPosition); + dataStart = memUpdate.fifoPosition; + } + + WriteMemory(memUpdate); + + ++nextMemUpdate; + } + else + { + WriteFifo(data, dataStart, dataEnd); + dataStart = dataEnd; + } + } + + if (dataStart < dataEnd) + WriteFifo(data, dataStart, dataEnd); +} + +void FifoPlayer::WriteAllMemoryUpdates() +{ + _assert_(m_File); + + for (int frameNum = 0; frameNum < m_File->GetFrameCount(); ++frameNum) + { + const FifoFrameInfo &frame = m_File->GetFrame(frameNum); + for (unsigned int i = 0; i < frame.memoryUpdates.size(); ++i) + { + WriteMemory(frame.memoryUpdates[i]); + } + } +} + +void FifoPlayer::WriteMemory(const MemoryUpdate& memUpdate) +{ + u8 *mem = NULL; + + if (memUpdate.address & 0x10000000) + mem = &Memory::m_pEXRAM[memUpdate.address & Memory::EXRAM_MASK]; + else + mem = &Memory::m_pRAM[memUpdate.address & Memory::RAM_MASK]; + + memcpy(mem, memUpdate.data, memUpdate.size); +} + +void FifoPlayer::WriteFifo(u8 *data, u32 start, u32 end) +{ + u32 written = start; + u32 lastBurstEnd = end - 1; + + // Write up to 256 bytes at a time + while (written < end) + { + u32 burstEnd = std::min(written + 255, lastBurstEnd); + + while (written < burstEnd) + GPFifo::FastWrite8(data[written++]); + + GPFifo::Write8(data[written++], 0); + + // Advance core timing + u32 elapsedCycles = u32(((u64)written * m_CyclesPerFrame) / m_FrameFifoSize); + u32 cyclesUsed = elapsedCycles - m_ElapsedCycles; + m_ElapsedCycles = elapsedCycles; + + CoreTiming::downcount -= cyclesUsed; + CoreTiming::Advance(); + } +} + +void FifoPlayer::SetupFifo() +{ + WriteCP(0x02, 0); // disable read, BP, interrupts + WriteCP(0x04, 7); // clear overflow, underflow, metrics + + const FifoFrameInfo& frame = m_File->GetFrame(m_CurrentFrame); + + // Set fifo bounds + WriteCP(0x20, frame.fifoStart); + WriteCP(0x22, frame.fifoStart >> 16); + WriteCP(0x24, frame.fifoEnd); + WriteCP(0x26, frame.fifoEnd >> 16); + + // Set watermarks + u32 fifoSize = frame.fifoEnd - frame.fifoStart; + WriteCP(0x28, fifoSize); + WriteCP(0x2a, fifoSize >> 16); + WriteCP(0x2c, 0); + WriteCP(0x2e, 0); + + // Set R/W pointers to fifo start + WriteCP(0x30, 0); + WriteCP(0x32, 0); + WriteCP(0x34, frame.fifoStart); + WriteCP(0x36, frame.fifoStart >> 16); + WriteCP(0x38, frame.fifoStart); + WriteCP(0x3a, frame.fifoStart >> 16); + + // Set fifo bounds + WritePI(12, frame.fifoStart); + WritePI(16, frame.fifoEnd); + + // Set write pointer + WritePI(20, frame.fifoStart); + FlushWGP(); + WritePI(20, frame.fifoStart); + + WriteCP(0x02, 17); // enable read & GP link +} + +void FifoPlayer::LoadMemory() +{ + Memory::Clear(); + + SetupFifo(); + + u32 *regs = m_File->GetBPMem(); + for (int i = 0; i < FifoDataFile::BP_MEM_SIZE; ++i) + { + if (ShouldLoadBP(i)) + LoadBPReg(i, regs[i]); + } + + regs = m_File->GetCPMem(); + LoadCPReg(0x30, regs[0x30]); + LoadCPReg(0x40, regs[0x40]); + LoadCPReg(0x50, regs[0x50]); + LoadCPReg(0x60, regs[0x60]); + + for (int i = 0; i < 8; ++i) + { + LoadCPReg(0x70 + i, regs[0x70 + i]); + LoadCPReg(0x80 + i, regs[0x80 + i]); + LoadCPReg(0x90 + i, regs[0x90 + i]); + } + + for (int i = 0; i < 16; ++i) + { + LoadCPReg(0xa0 + i, regs[0xa0 + i]); + LoadCPReg(0xb0 + i, regs[0xb0 + i]); + } + + regs = m_File->GetXFMem(); + for (int i = 0; i < FifoDataFile::XF_MEM_SIZE; i += 16) + LoadXFMem16(i, ®s[i]); + + regs = m_File->GetXFRegs(); + for (int i = 0; i < FifoDataFile::XF_REGS_SIZE; ++i) + LoadXFReg(i, regs[i]); + + FlushWGP(); +} + +void FifoPlayer::WriteCP(u32 address, u16 value) +{ + Memory::Write_U16(value, 0xCC000000 | address); +} + +void FifoPlayer::WritePI(u32 address, u32 value) +{ + Memory::Write_U32(value, 0xCC003000 | address); +} + +void FifoPlayer::FlushWGP() +{ + // Send 31 0s through the WGP + for (int i = 0; i < 7; ++i) + GPFifo::Write32(0, 0); + GPFifo::Write16(0, 0); + GPFifo::Write8(0, 0); + + GPFifo::ResetGatherPipe(); +} + +void FifoPlayer::LoadBPReg(u8 reg, u32 value) +{ + GPFifo::Write8(0x61, 0); // load BP reg + + u32 cmd = (reg << 24) & 0xff000000; + cmd |= (value & 0x00ffffff); + GPFifo::Write32(cmd, 0); +} + +void FifoPlayer::LoadCPReg(u8 reg, u32 value) +{ + GPFifo::Write8(0x08, 0); // load CP reg + GPFifo::Write8(reg, 0); + GPFifo::Write32(value, 0); +} + +void FifoPlayer::LoadXFReg(u16 reg, u32 value) +{ + GPFifo::Write8(0x10, 0); // load XF reg + GPFifo::Write32((reg & 0x0fff) | 0x1000, 0); // load 4 bytes into reg + GPFifo::Write32(value, 0); +} + +void FifoPlayer::LoadXFMem16(u16 address, u32 *data) +{ + // Loads 16 * 4 bytes in xf memory starting at address + GPFifo::Write8(0x10, 0); // load XF reg + GPFifo::Write32(0x000f0000 | (address & 0xffff), 0); // load 16 * 4 bytes into address + for (int i = 0; i < 16; ++i) + GPFifo::Write32(data[i], 0); +} + +bool FifoPlayer::ShouldLoadBP(u8 address) +{ + switch (address) + { + case BPMEM_SETDRAWDONE: + case BPMEM_PE_TOKEN_ID: + case BPMEM_PE_TOKEN_INT_ID: + case BPMEM_TRIGGER_EFB_COPY: + case BPMEM_LOADTLUT1: + case BPMEM_PERF1: + return false; + } + + return true; +} diff --git a/Source/Core/Core/Src/FifoPlayer/FifoPlayer.h b/Source/Core/Core/Src/FifoPlayer/FifoPlayer.h new file mode 100644 index 0000000000..2a02544a20 --- /dev/null +++ b/Source/Core/Core/Src/FifoPlayer/FifoPlayer.h @@ -0,0 +1,121 @@ +// Copyright (C) 2003 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program 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 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#ifndef _FIFOPLAYER_H_ +#define _FIFOPLAYER_H_ + +#include "FifoPlaybackAnalyzer.h" +#include <string> +#include <vector> + +class FifoDataFile; +struct MemoryUpdate; +struct AnalyzedFrameInfo; + +class FifoPlayer +{ +public: + typedef void(*CallbackFunc)(void); + + ~FifoPlayer(); + + bool Open(const std::string& filename); + void Close(); + + // Play is controlled by the state of PowerPC + bool Play(); + + FifoDataFile *GetFile() { return m_File; } + + u32 GetFrameObjectCount(); + u32 GetCurrentFrameNum() { return m_CurrentFrame; } + + // Frame range + u32 GetFrameRangeStart() { return m_FrameRangeStart; } + void SetFrameRangeStart(u32 start); + + u32 GetFrameRangeEnd() { return m_FrameRangeEnd; } + void SetFrameRangeEnd(u32 end); + + // Object range + u32 GetObjectRangeStart() { return m_ObjectRangeStart; } + void SetObjectRangeStart(u32 start) { m_ObjectRangeStart = start; } + + u32 GetObjectRangeEnd() { return m_ObjectRangeEnd; } + void SetObjectRangeEnd(u32 end) { m_ObjectRangeEnd = end; } + + // If enabled then all memory updates happen at once before the first frame + // Default is disabled + void SetEarlyMemoryUpdates(bool enabled) { m_EarlyMemoryUpdates = enabled; } + + // Callbacks + void SetFileLoadedCallback(CallbackFunc callback) { m_FileLoadedCb = callback; } + void SetFrameWrittenCallback(CallbackFunc callback) { m_FrameWrittenCb = callback; } + + static FifoPlayer &GetInstance(); + +private: + FifoPlayer(); + + void WriteFrame(const FifoFrameInfo &frame, const AnalyzedFrameInfo &info); + void WriteFramePart(u32 dataStart, u32 dataEnd, u32 &nextMemUpdate, const FifoFrameInfo &frame, const AnalyzedFrameInfo &info); + + void WriteAllMemoryUpdates(); + void WriteMemory(const MemoryUpdate &memUpdate); + + // writes a range of data to the fifo + // start and end must be relative to frame's fifo data so elapsed cycles are figured correctly + void WriteFifo(u8 *data, u32 start, u32 end); + + void SetupFifo(); + + void LoadMemory(); + + void WriteCP(u32 address, u16 value); + void WritePI(u32 address, u32 value); + + void FlushWGP(); + + void LoadBPReg(u8 reg, u32 value); + void LoadCPReg(u8 reg, u32 value); + void LoadXFReg(u16 reg, u32 value); + void LoadXFMem16(u16 address, u32 *data); + + bool ShouldLoadBP(u8 address); + + u32 m_CurrentFrame; + u32 m_FrameRangeStart; + u32 m_FrameRangeEnd; + + u32 m_ObjectRangeStart; + u32 m_ObjectRangeEnd; + + bool m_EarlyMemoryUpdates; + + u64 m_CyclesPerFrame; + u32 m_ElapsedCycles; + u32 m_FrameFifoSize; + + CallbackFunc m_FileLoadedCb; + CallbackFunc m_FrameWrittenCb; + + FifoDataFile *m_File; + + std::vector<AnalyzedFrameInfo> m_FrameInfo; +}; + +#endif
\ No newline at end of file diff --git a/Source/Core/Core/Src/FifoPlayer/FifoRecordAnalyzer.cpp b/Source/Core/Core/Src/FifoPlayer/FifoRecordAnalyzer.cpp new file mode 100644 index 0000000000..a8214a82c0 --- /dev/null +++ b/Source/Core/Core/Src/FifoPlayer/FifoRecordAnalyzer.cpp @@ -0,0 +1,305 @@ +// Copyright (C) 2003 Dolphin Project.
+
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, version 2.0.
+
+// This program 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 2.0 for more details.
+
+// A copy of the GPL 2.0 should have been included with the program.
+// If not, see http://www.gnu.org/licenses/
+
+// Official SVN repository and contact information can be found at
+// http://code.google.com/p/dolphin-emu/
+
+#include "FifoAnalyzer.h"
+#include "FifoRecordAnalyzer.h"
+#include "FifoRecorder.h"
+
+#include "Core.h"
+#include "HW/Memmap.h"
+
+#include "OpcodeDecoding.h"
+#include "TextureDecoder.h"
+
+using namespace FifoAnalyzer;
+
+
+FifoRecordAnalyzer::FifoRecordAnalyzer() :
+ m_DrawingObject(false),
+ m_BpMem(NULL)
+{
+}
+
+void FifoRecordAnalyzer::Initialize(u32 *bpMem, u32 *cpMem)
+{
+ m_DrawingObject = false;
+
+ m_BpMem = (BPMemory*)bpMem;
+
+ FifoAnalyzer::LoadCPReg(0x50, *(cpMem + 0x50), m_CpMem);
+ FifoAnalyzer::LoadCPReg(0x60, *(cpMem + 0x60), m_CpMem);
+ for (int i = 0; i < 8; ++i)
+ FifoAnalyzer::LoadCPReg(0x70 + i, *(cpMem + 0x70 + i), m_CpMem);
+
+ memcpy(m_CpMem.arrayBases, cpMem + 0xA0, 16 * 4);
+ memcpy(m_CpMem.arrayStrides, cpMem + 0xB0, 16 * 4);
+}
+
+void FifoRecordAnalyzer::AnalyzeGPCommand(u8 *data)
+{
+ DecodeOpcode(data);
+}
+
+void FifoRecordAnalyzer::DecodeOpcode(u8 *data)
+{
+ int cmd = ReadFifo8(data);
+
+ switch (cmd)
+ {
+ case GX_NOP:
+ case 0x44:
+ case GX_CMD_INVL_VC:
+ break;
+
+ case GX_LOAD_CP_REG:
+ {
+ m_DrawingObject = false;
+
+ u32 cmd2 = ReadFifo8(data);
+ u32 value = ReadFifo32(data);
+ FifoAnalyzer::LoadCPReg(cmd2, value, m_CpMem);
+ }
+
+ break;
+
+ case GX_LOAD_XF_REG:
+ m_DrawingObject = false;
+ break;
+
+ case GX_LOAD_INDX_A:
+ m_DrawingObject = false;
+ ProcessLoadIndexedXf(ReadFifo32(data), 0xc);
+ break;
+ case GX_LOAD_INDX_B:
+ m_DrawingObject = false;
+ ProcessLoadIndexedXf(ReadFifo32(data), 0xd);
+ break;
+ case GX_LOAD_INDX_C:
+ m_DrawingObject = false;
+ ProcessLoadIndexedXf(ReadFifo32(data), 0xe);
+ break;
+ case GX_LOAD_INDX_D:
+ m_DrawingObject = false;
+ ProcessLoadIndexedXf(ReadFifo32(data), 0xf);
+ break;
+
+ case GX_CMD_CALL_DL:
+ {
+ // The recorder should have expanded display lists into the fifo stream and skipped the call to start them
+ // That is done to make it easier to track where memory is updated
+ _assert_(false);
+ }
+ break;
+
+ case GX_LOAD_BP_REG:
+ {
+ m_DrawingObject = false;
+
+ u32 cmd2 = ReadFifo32(data);
+ BPCmd bp = FifoAnalyzer::DecodeBPCmd(cmd2, *m_BpMem);
+
+ if (bp.address == BPMEM_LOADTLUT1)
+ ProcessLoadTlut1();
+ }
+ break;
+
+ default:
+ if (cmd & 0x80)
+ {
+ if (!m_DrawingObject)
+ {
+ m_DrawingObject = true;
+ ProcessTexMaps();
+ }
+
+ ProcessVertexArrays(data, cmd & GX_VAT_MASK);
+ }
+ else
+ {
+ PanicAlert("FifoRecordAnalyzer: Unknown Opcode (0x%x).\n", cmd);
+ }
+ }
+}
+
+void FifoRecordAnalyzer::ProcessLoadTlut1()
+{
+ u32 tlutXferCount;
+ u32 tlutMemAddr;
+ u32 memAddr;
+
+ GetTlutLoadData(tlutMemAddr, memAddr, tlutXferCount, *m_BpMem);
+
+ FifoRecorder::GetInstance().WriteMemory(memAddr, tlutXferCount, MemoryUpdate::TLUT);
+}
+
+void FifoRecordAnalyzer::ProcessLoadIndexedXf(u32 val, int array)
+{
+ int index = val >> 16;
+ int size = ((val >> 12) & 0xF) + 1;
+
+ u32 address = m_CpMem.arrayBases[array] + m_CpMem.arrayStrides[array] * index;
+
+ FifoRecorder::GetInstance().WriteMemory(address, size * 4, MemoryUpdate::XF_DATA);
+}
+
+void FifoRecordAnalyzer::ProcessVertexArrays(u8 *data, u8 vtxAttrGroup)
+{
+ int sizes[21];
+ FifoAnalyzer::CalculateVertexElementSizes(sizes, vtxAttrGroup, m_CpMem);
+
+ // Determine offset of each element from start of vertex data
+ int offsets[12];
+ int offset = 0;
+ for (int i = 0; i < 12; ++i)
+ {
+ offsets[i] = offset;
+ offset += sizes[i + 9];
+ }
+
+ int vertexSize = offset;
+ int numVertices = ReadFifo16(data);
+
+ if (numVertices > 0)
+ {
+ for (int i = 0; i < 12; ++i)
+ {
+ WriteVertexArray(i, data + offsets[i], vertexSize, numVertices);
+ }
+ }
+}
+
+void FifoRecordAnalyzer::WriteVertexArray(int arrayIndex, u8 *vertexData, int vertexSize, int numVertices)
+{
+ // Skip if not indexed array
+ int arrayType = (m_CpMem.vtxDesc.Hex >> (9 + (arrayIndex * 2))) & 3;
+ if (arrayType < 2)
+ return;
+
+ int maxIndex = 0;
+
+ // Determine min and max indices
+ if (arrayType == INDEX8)
+ {
+ for (int i = 0; i < numVertices; ++i)
+ {
+ int index = *vertexData;
+ vertexData += vertexSize;
+
+ // 0xff skips the vertex
+ if (index != 0xff)
+ {
+ if (index > maxIndex)
+ maxIndex = index;
+ }
+ }
+ }
+ else
+ {
+ for (int i = 0; i < numVertices; ++i)
+ {
+ int index = Common::swap16(vertexData);
+ vertexData += vertexSize;
+
+ // 0xffff skips the vertex
+ if (index != 0xffff)
+ {
+ if (index > maxIndex)
+ maxIndex = index;
+ }
+ }
+ }
+
+ u32 arrayStart = m_CpMem.arrayBases[arrayIndex];
+ u32 arraySize = m_CpMem.arrayStrides[arrayIndex] * (maxIndex + 1);
+
+ FifoRecorder::GetInstance().WriteMemory(arrayStart, arraySize, MemoryUpdate::VERTEX_STREAM);
+}
+
+void FifoRecordAnalyzer::ProcessTexMaps()
+{
+ u32 writtenTexMaps = 0;
+
+ // Texture maps used in TEV indirect stages
+ for (u32 i = 0; i < m_BpMem->genMode.numindstages; ++i)
+ {
+ u32 texMap = m_BpMem->tevindref.getTexMap(i);
+
+ WriteTexMapMemory(texMap, writtenTexMaps);
+ }
+
+ // Texture maps used in TEV direct stages
+ for (u32 i = 0; i <= m_BpMem->genMode.numtevstages; ++i)
+ {
+ int stageNum2 = i >> 1;
+ int stageOdd = i & 1;
+ TwoTevStageOrders &order = m_BpMem->tevorders[stageNum2];
+ int texMap = order.getTexMap(stageOdd);
+
+ if (order.getEnable(stageOdd))
+ WriteTexMapMemory(texMap, writtenTexMaps);
+ }
+}
+
+void FifoRecordAnalyzer::WriteTexMapMemory(int texMap, u32 &writtenTexMaps)
+{
+ // Avoid rechecking the same texture map
+ u32 texMapMask = 1 << texMap;
+ if (writtenTexMaps & texMapMask)
+ return;
+
+ writtenTexMaps |= texMapMask;
+
+ FourTexUnits& texUnit = m_BpMem->tex[(texMap >> 2) & 1];
+ u8 subTexmap = texMap & 3;
+
+ TexImage0& ti0 = texUnit.texImage0[subTexmap];
+
+ u32 width = ti0.width + 1;
+ u32 height = ti0.height + 1;
+ u32 imageBase = texUnit.texImage3[subTexmap].image_base << 5;
+
+ u32 fmtWidth = TexDecoder_GetBlockWidthInTexels(ti0.format) - 1;
+ u32 fmtHeight = TexDecoder_GetBlockHeightInTexels(ti0.format) - 1;
+ int fmtDepth = TexDecoder_GetTexelSizeInNibbles(ti0.format);
+
+ // Round width and height up to the next block
+ width = (width + fmtWidth) & (~fmtWidth);
+ height = (height + fmtHeight) & (~fmtHeight);
+
+ u32 textureSize = (width * height * fmtDepth) / 2;
+
+ // TODO: mip maps
+ int mip = texUnit.texMode1[subTexmap].max_lod;
+ if ((texUnit.texMode0[subTexmap].min_filter & 3) == 0)
+ mip = 0;
+
+ while (mip)
+ {
+ width >>= 1;
+ height >>= 1;
+
+ width = max(width, fmtWidth);
+ height = max(height, fmtHeight);
+ u32 size = (width * height * fmtDepth) >> 1;
+
+ textureSize += size;
+
+ mip--;
+ }
+
+ FifoRecorder::GetInstance().WriteMemory(imageBase, textureSize, MemoryUpdate::TEXTURE_MAP);
+}
\ No newline at end of file diff --git a/Source/Core/Core/Src/FifoPlayer/FifoRecordAnalyzer.h b/Source/Core/Core/Src/FifoPlayer/FifoRecordAnalyzer.h new file mode 100644 index 0000000000..acfbbc38b5 --- /dev/null +++ b/Source/Core/Core/Src/FifoPlayer/FifoRecordAnalyzer.h @@ -0,0 +1,56 @@ +// Copyright (C) 2003 Dolphin Project.
+
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, version 2.0.
+
+// This program 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 2.0 for more details.
+
+// A copy of the GPL 2.0 should have been included with the program.
+// If not, see http://www.gnu.org/licenses/
+
+// Official SVN repository and contact information can be found at
+// http://code.google.com/p/dolphin-emu/
+
+#ifndef _FIFORECORDANALYZER_H_
+#define _FIFORECORDANALYZER_H_
+
+#include "FifoAnalyzer.h"
+
+#include "Common.h"
+
+#include "BPMemory.h"
+
+class FifoRecordAnalyzer
+{
+public:
+ FifoRecordAnalyzer();
+
+ // Must call this before analyzing GP commands
+ void Initialize(u32 *bpMem, u32 *cpMem);
+
+ // Assumes data contains all information for the command
+ // Calls FifoRecorder::WriteMemory
+ void AnalyzeGPCommand(u8 *data);
+
+private:
+ void DecodeOpcode(u8 *data);
+
+ void ProcessLoadTlut1();
+ void ProcessLoadIndexedXf(u32 val, int array);
+ void ProcessVertexArrays(u8 *data, u8 vtxAttrGroup);
+ void ProcessTexMaps();
+
+ void WriteVertexArray(int arrayIndex, u8 *vertexData, int vertexSize, int numVertices);
+ void WriteTexMapMemory(int texMap, u32 &writtenTexMaps);
+
+ bool m_DrawingObject;
+
+ BPMemory *m_BpMem;
+ FifoAnalyzer::CPMemory m_CpMem;
+};
+
+#endif
\ No newline at end of file diff --git a/Source/Core/Core/Src/FifoPlayer/FifoRecorder.cpp b/Source/Core/Core/Src/FifoPlayer/FifoRecorder.cpp new file mode 100644 index 0000000000..b369823c3d --- /dev/null +++ b/Source/Core/Core/Src/FifoPlayer/FifoRecorder.cpp @@ -0,0 +1,220 @@ +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program 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 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#include "ConfigManager.h" +#include "Core.h" +#include "FifoRecorder.h" +#include "Thread.h" +#include "HW/Memmap.h" + +static FifoRecorder instance; +static std::recursive_mutex sMutex; + +using namespace std; + +FifoRecorder::FifoRecorder() : + m_IsRecording(false), + m_WasRecording(false), + m_RequestedRecordingEnd(false), + m_RecordFramesRemaining(0), + m_FinishedCb(NULL), + m_File(NULL), + m_SkipNextData(true), + m_SkipFutureData(true), + m_FrameEnded(false), + m_Ram(NULL), + m_ExRam(NULL) +{ +} + +FifoRecorder::~FifoRecorder() +{ + m_IsRecording = false; +} + +void FifoRecorder::StartRecording(s32 numFrames, CallbackFunc finishedCb) +{ + sMutex.lock(); + + delete m_File; + delete []m_Ram; + delete []m_ExRam; + + m_File = new FifoDataFile; + + m_Ram = new u8[Memory::RAM_SIZE]; + m_ExRam = new u8[Memory::EXRAM_SIZE]; + memset(m_Ram, 0, Memory::RAM_SIZE); + memset(m_ExRam, 0, Memory::EXRAM_SIZE); + + m_File->SetIsWii(SConfig::GetInstance().m_LocalCoreStartupParameter.bWii); + + if (!m_IsRecording) + { + m_WasRecording = false; + m_IsRecording = true; + m_RecordFramesRemaining = numFrames; + } + + m_RequestedRecordingEnd = false; + m_FinishedCb = finishedCb; + + sMutex.unlock(); +} + +void FifoRecorder::StopRecording() +{ + m_RequestedRecordingEnd = true; +} + +void FifoRecorder::WriteGPCommand(u8 *data, u32 size) +{ + if (!m_SkipNextData) + { + m_RecordAnalyzer.AnalyzeGPCommand(data); + + // Copy data to buffer + u32 currentSize = m_FifoData.size(); + m_FifoData.resize(currentSize + size); + memcpy(&m_FifoData[currentSize], data, size); + } + + if (m_FrameEnded && m_FifoData.size() > 0) + { + u32 dataSize = m_FifoData.size(); + m_CurrentFrame.fifoDataSize = dataSize; + m_CurrentFrame.fifoData = new u8[dataSize]; + memcpy(m_CurrentFrame.fifoData, &m_FifoData[0], dataSize); + + sMutex.lock(); + + // Copy frame to file + // The file will be responsible for freeing the memory allocated for each frame's fifoData + m_File->AddFrame(m_CurrentFrame); + + if (m_FinishedCb && m_RequestedRecordingEnd) + m_FinishedCb(); + + sMutex.unlock(); + + m_CurrentFrame.memoryUpdates.clear(); + m_FifoData.clear(); + m_FrameEnded = false; + } + + m_SkipNextData = m_SkipFutureData; +} + +void FifoRecorder::WriteMemory(u32 address, u32 size, MemoryUpdate::Type type) +{ + u8 *curData; + u8 *newData; + if (address & 0x10000000) + { + curData = &m_ExRam[address & Memory::EXRAM_MASK]; + newData = &Memory::m_pEXRAM[address & Memory::EXRAM_MASK]; + } + else + { + curData = &m_Ram[address & Memory::RAM_MASK]; + newData = &Memory::m_pRAM[address & Memory::RAM_MASK]; + } + + if (memcmp(curData, newData, size) != 0) + { + // Update current memory + memcpy(curData, newData, size); + + // Record memory update + MemoryUpdate memUpdate; + memUpdate.address = address; + memUpdate.fifoPosition = m_FifoData.size(); + memUpdate.size = size; + memUpdate.type = type; + memUpdate.data = new u8[size]; + memcpy(memUpdate.data, newData, size); + + m_CurrentFrame.memoryUpdates.push_back(memUpdate); + } +} + +void FifoRecorder::EndFrame(u32 fifoStart, u32 fifoEnd) +{ + // m_IsRecording is assumed to be true at this point, otherwise this function would not be called + + sMutex.lock(); + + m_FrameEnded = true; + + m_CurrentFrame.fifoStart = fifoStart; + m_CurrentFrame.fifoEnd = fifoEnd; + + if (m_WasRecording) + { + // If recording a fixed number of frames then check if the end of the recording was reached + if (m_RecordFramesRemaining > 0) + { + --m_RecordFramesRemaining; + if (m_RecordFramesRemaining == 0) + m_RequestedRecordingEnd = true; + } + } + else + { + m_WasRecording = true; + + // Skip the first data which will be the frame copy command + m_SkipNextData = true; + m_SkipFutureData = false; + + m_FrameEnded = false; + + m_FifoData.reserve(1024 * 1024 * 4); + m_FifoData.clear(); + } + + if (m_RequestedRecordingEnd) + { + // Skip data after the next time WriteFifoData is called + m_SkipFutureData = true; + // Signal video backend that it should not call this function when the next frame ends + m_IsRecording = false; + } + + sMutex.unlock(); +} + +void FifoRecorder::SetVideoMemory(u32 *bpMem, u32 *cpMem, u32 *xfMem, u32 *xfRegs, u32 xfRegsSize) +{ + sMutex.lock(); + + if (m_File) + { + memcpy(m_File->GetBPMem(), bpMem, FifoDataFile::BP_MEM_SIZE * 4); + memcpy(m_File->GetCPMem(), cpMem, FifoDataFile::CP_MEM_SIZE * 4); + memcpy(m_File->GetXFMem(), xfMem, FifoDataFile::XF_MEM_SIZE * 4); + + u32 xfRegsCopySize = std::min((u32)FifoDataFile::XF_REGS_SIZE, xfRegsSize); + memcpy(m_File->GetXFRegs(), xfRegs, xfRegsCopySize * 4); + } + + m_RecordAnalyzer.Initialize(bpMem, cpMem); + + sMutex.unlock(); +} + +FifoRecorder &FifoRecorder::GetInstance() +{ + return instance; +} diff --git a/Source/Core/Core/Src/FifoPlayer/FifoRecorder.h b/Source/Core/Core/Src/FifoPlayer/FifoRecorder.h new file mode 100644 index 0000000000..44e7be5862 --- /dev/null +++ b/Source/Core/Core/Src/FifoPlayer/FifoRecorder.h @@ -0,0 +1,80 @@ +// Copyright (C) 2003 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program 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 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#ifndef _FIFORECORDER_H_ +#define _FIFORECORDER_H_ + +#include "FifoDataFile.h" +#include "FifoRecordAnalyzer.h" + +class FifoRecorder +{ +public: + typedef void(*CallbackFunc)(void); + + FifoRecorder(); + ~FifoRecorder(); + + void StartRecording(s32 numFrames, CallbackFunc finishedCb); + void StopRecording(); + + FifoDataFile *GetRecordedFile() { return m_File; } + + // Called from video thread + + // Must write one full GP command at a time + void WriteGPCommand(u8 *data, u32 size); + + void WriteMemory(u32 address, u32 size, MemoryUpdate::Type type); + + void EndFrame(u32 fifoStart, u32 fifoEnd); + + // This function must be called before writting GP commands + // bpMem must point to the actual bp mem array used by the plugin because it will be read as fifo data is recorded + void SetVideoMemory(u32 *bpMem, u32 *cpMem, u32 *xfMem, u32 *xfRegs, u32 xfRegsSize); + + // Checked once per frame prior to callng EndFrame() + bool IsRecording() { return m_IsRecording; } + + static FifoRecorder &GetInstance(); + +private: + // Accessed from both GUI and video threads + + // True if video thread should send data + volatile bool m_IsRecording; + // True if m_IsRecording was true during last frame + volatile bool m_WasRecording; + volatile bool m_RequestedRecordingEnd; + volatile s32 m_RecordFramesRemaining; + volatile CallbackFunc m_FinishedCb; + + FifoDataFile *volatile m_File; + + // Accessed only from video thread + + bool m_SkipNextData; + bool m_SkipFutureData; + bool m_FrameEnded; + FifoFrameInfo m_CurrentFrame; + std::vector<u8> m_FifoData; + u8 *m_Ram; + u8 *m_ExRam; + FifoRecordAnalyzer m_RecordAnalyzer; +}; + +#endif
\ No newline at end of file diff --git a/Source/Core/DolphinWX/CMakeLists.txt b/Source/Core/DolphinWX/CMakeLists.txt index b43b92ec50..bac3ba2c05 100644 --- a/Source/Core/DolphinWX/CMakeLists.txt +++ b/Source/Core/DolphinWX/CMakeLists.txt @@ -33,6 +33,7 @@ if(wxWidgets_FOUND) Src/Debugger/MemoryWindow.cpp Src/Debugger/RegisterView.cpp Src/Debugger/RegisterWindow.cpp + Src/FifoPlayerDlg.cpp Src/Frame.cpp Src/FrameAui.cpp Src/FrameTools.cpp diff --git a/Source/Core/DolphinWX/Dolphin.vcxproj b/Source/Core/DolphinWX/Dolphin.vcxproj index 82dfcd69ef..314a16faaf 100644 --- a/Source/Core/DolphinWX/Dolphin.vcxproj +++ b/Source/Core/DolphinWX/Dolphin.vcxproj @@ -232,6 +232,7 @@ xcopy "$(SolutionDir)..\Externals\SDL\$(PlatformName)\*.dll" "$(TargetDir)" /e / <ClCompile Include="Src\Debugger\DebuggerUIUtil.cpp" /> <ClCompile Include="Src\Debugger\DSPDebugWindow.cpp" /> <ClCompile Include="Src\Debugger\DSPRegisterView.cpp" /> + <ClCompile Include="Src\FifoPlayerDlg.cpp" /> <ClCompile Include="Src\Frame.cpp" /> <ClCompile Include="Src\FrameAui.cpp" /> <ClCompile Include="Src\FrameTools.cpp" /> @@ -294,6 +295,7 @@ xcopy "$(SolutionDir)..\Externals\SDL\$(PlatformName)\*.dll" "$(TargetDir)" /e / <ClInclude Include="Src\Debugger\DebuggerUIUtil.h" /> <ClInclude Include="Src\Debugger\DSPDebugWindow.h" /> <ClInclude Include="Src\Debugger\DSPRegisterView.h" /> + <ClInclude Include="Src\FifoPlayerDlg.h" /> <ClInclude Include="Src\Frame.h" /> <ClInclude Include="Src\GameListCtrl.h" /> <ClInclude Include="Src\GeckoCodeDiag.h" /> diff --git a/Source/Core/DolphinWX/Dolphin.vcxproj.filters b/Source/Core/DolphinWX/Dolphin.vcxproj.filters index bc3255787d..5fdf69b51b 100644 --- a/Source/Core/DolphinWX/Dolphin.vcxproj.filters +++ b/Source/Core/DolphinWX/Dolphin.vcxproj.filters @@ -129,6 +129,9 @@ <ClCompile Include="Src\LogConfigWindow.cpp"> <Filter>GUI</Filter> </ClCompile> + <ClCompile Include="Src\FifoPlayerDlg.cpp"> + <Filter>GUI</Filter> + </ClCompile> </ItemGroup> <ItemGroup> <ClInclude Include="Src\Main.h" /> @@ -252,6 +255,9 @@ <ClInclude Include="Src\LogConfigWindow.h"> <Filter>GUI</Filter> </ClInclude> + <ClInclude Include="Src\FifoPlayerDlg.h"> + <Filter>GUI</Filter> + </ClInclude> </ItemGroup> <ItemGroup> <None Include="Src\SConscript" /> @@ -291,4 +297,4 @@ <Filter>Resources</Filter> </ResourceCompile> </ItemGroup> -</Project> +</Project>
\ No newline at end of file diff --git a/Source/Core/DolphinWX/Src/FifoPlayerDlg.cpp b/Source/Core/DolphinWX/Src/FifoPlayerDlg.cpp new file mode 100644 index 0000000000..4f427e83d2 --- /dev/null +++ b/Source/Core/DolphinWX/Src/FifoPlayerDlg.cpp @@ -0,0 +1,511 @@ +// Copyright (C) 2003 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program 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 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#include "Common.h" +#include "FifoPlayerDlg.h" +#include "FileUtil.h" +#include "Thread.h" +#include "FifoPlayer/FifoPlayer.h" +#include "FifoPlayer/FifoRecorder.h" +#include <wx/spinctrl.h> + +DECLARE_EVENT_TYPE(RECORDING_FINISHED_EVENT, -1) +DEFINE_EVENT_TYPE(RECORDING_FINISHED_EVENT) + +DECLARE_EVENT_TYPE(FRAME_WRITTEN_EVENT, -1) +DEFINE_EVENT_TYPE(FRAME_WRITTEN_EVENT) + +using namespace std; + +std::recursive_mutex sMutex; +wxEvtHandler *volatile FifoPlayerDlg::m_EvtHandler = NULL; + +FifoPlayerDlg::FifoPlayerDlg(wxWindow * const parent) : + wxDialog(parent, wxID_ANY, _("FIFO Player"), wxDefaultPosition, wxDefaultSize), + m_FramesToRecord(1) +{ + CreateGUIControls(); + + sMutex.lock(); + m_EvtHandler = GetEventHandler(); + sMutex.unlock(); + + FifoPlayer::GetInstance().SetFileLoadedCallback(FileLoaded); + FifoPlayer::GetInstance().SetFrameWrittenCallback(FrameWritten); +} + +FifoPlayerDlg::~FifoPlayerDlg() +{ + Disconnect(RECORDING_FINISHED_EVENT, wxCommandEventHandler(FifoPlayerDlg::OnRecordingFinished), NULL, this); + Disconnect(FRAME_WRITTEN_EVENT, wxCommandEventHandler(FifoPlayerDlg::OnFrameWritten), NULL, this); + + // Disconnect Events + this->Disconnect( wxEVT_PAINT, wxPaintEventHandler( FifoPlayerDlg::OnPaint ), NULL, this ); + m_FrameFromCtrl->Disconnect( wxEVT_COMMAND_SPINCTRL_UPDATED, wxSpinEventHandler( FifoPlayerDlg::OnFrameFrom ), NULL, this ); + m_FrameToCtrl->Disconnect( wxEVT_COMMAND_SPINCTRL_UPDATED, wxSpinEventHandler( FifoPlayerDlg::OnFrameTo ), NULL, this ); + m_ObjectFromCtrl->Disconnect( wxEVT_COMMAND_SPINCTRL_UPDATED, wxSpinEventHandler( FifoPlayerDlg::OnObjectFrom ), NULL, this ); + m_ObjectToCtrl->Disconnect( wxEVT_COMMAND_SPINCTRL_UPDATED, wxSpinEventHandler( FifoPlayerDlg::OnObjectTo ), NULL, this ); + m_EarlyMemoryUpdates->Disconnect( wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler( FifoPlayerDlg::OnCheckEarlyMemoryUpdates ), NULL, this ); + m_RecordStop->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( FifoPlayerDlg::OnRecordStop ), NULL, this ); + m_Save->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( FifoPlayerDlg::OnSaveFile ), NULL, this ); + m_FramesToRecordCtrl->Disconnect( wxEVT_COMMAND_SPINCTRL_UPDATED, wxSpinEventHandler( FifoPlayerDlg::OnNumFramesToRecord ), NULL, this ); + m_Close->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( FifoPlayerDlg::OnCloseClick ), NULL, this ); + + FifoPlayer::GetInstance().SetFrameWrittenCallback(NULL); + + sMutex.lock(); + m_EvtHandler = NULL; + sMutex.unlock(); +} + +void FifoPlayerDlg::CreateGUIControls() +{ + SetSizeHints( wxDefaultSize, wxDefaultSize ); + + wxBoxSizer* sMain; + sMain = new wxBoxSizer( wxVERTICAL ); + + m_Notebook = new wxNotebook( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, 0 ); + m_PlayPage = new wxPanel( m_Notebook, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL ); + wxBoxSizer* sPlayPage; + sPlayPage = new wxBoxSizer( wxVERTICAL ); + + wxStaticBoxSizer* sPlayInfo; + sPlayInfo = new wxStaticBoxSizer( new wxStaticBox( m_PlayPage, wxID_ANY, _("File Info") ), wxVERTICAL ); + + m_NumFramesLabel = new wxStaticText( m_PlayPage, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0 ); + m_NumFramesLabel->Wrap( -1 ); + sPlayInfo->Add( m_NumFramesLabel, 0, wxALL, 5 ); + + m_CurrentFrameLabel = new wxStaticText( m_PlayPage, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0 ); + m_CurrentFrameLabel->Wrap( -1 ); + sPlayInfo->Add( m_CurrentFrameLabel, 0, wxALL, 5 ); + + m_NumObjectsLabel = new wxStaticText( m_PlayPage, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0 ); + m_NumObjectsLabel->Wrap( -1 ); + sPlayInfo->Add( m_NumObjectsLabel, 0, wxALL, 5 ); + + sPlayPage->Add( sPlayInfo, 1, wxEXPAND, 5 ); + + wxStaticBoxSizer* sFrameRange; + sFrameRange = new wxStaticBoxSizer( new wxStaticBox( m_PlayPage, wxID_ANY, _("Frame Range") ), wxHORIZONTAL ); + + m_FrameFromLabel = new wxStaticText( m_PlayPage, wxID_ANY, _("From"), wxDefaultPosition, wxDefaultSize, 0 ); + m_FrameFromLabel->Wrap( -1 ); + sFrameRange->Add( m_FrameFromLabel, 0, wxALL, 5 ); + + m_FrameFromCtrl = new wxSpinCtrl( m_PlayPage, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxSP_ARROW_KEYS, 0, 10, 0 ); + sFrameRange->Add( m_FrameFromCtrl, 0, wxALL, 5 ); + + m_FrameToLabel = new wxStaticText( m_PlayPage, wxID_ANY, _("To"), wxDefaultPosition, wxDefaultSize, 0 ); + m_FrameToLabel->Wrap( -1 ); + sFrameRange->Add( m_FrameToLabel, 0, wxALL, 5 ); + + m_FrameToCtrl = new wxSpinCtrl( m_PlayPage, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize( -1,-1 ), wxSP_ARROW_KEYS, 0, 10, 0 ); + sFrameRange->Add( m_FrameToCtrl, 0, wxALL, 5 ); + + sPlayPage->Add( sFrameRange, 0, wxEXPAND, 5 ); + + wxStaticBoxSizer* sObjectRange; + sObjectRange = new wxStaticBoxSizer( new wxStaticBox( m_PlayPage, wxID_ANY, _("Object Range") ), wxHORIZONTAL ); + + m_ObjectFromLabel = new wxStaticText( m_PlayPage, wxID_ANY, _("From"), wxDefaultPosition, wxDefaultSize, 0 ); + m_ObjectFromLabel->Wrap( -1 ); + sObjectRange->Add( m_ObjectFromLabel, 0, wxALL, 5 ); + + m_ObjectFromCtrl = new wxSpinCtrl( m_PlayPage, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxSP_ARROW_KEYS, 0, 10000, 0 ); + sObjectRange->Add( m_ObjectFromCtrl, 0, wxALL, 5 ); + + m_ObjectToLabel = new wxStaticText( m_PlayPage, wxID_ANY, wxT("To"), wxDefaultPosition, wxDefaultSize, 0 ); + m_ObjectToLabel->Wrap( -1 ); + sObjectRange->Add( m_ObjectToLabel, 0, wxALL, 5 ); + + m_ObjectToCtrl = new wxSpinCtrl( m_PlayPage, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxSP_ARROW_KEYS, 0, 10000, 0 ); + sObjectRange->Add( m_ObjectToCtrl, 0, wxALL, 5 ); + + sPlayPage->Add( sObjectRange, 0, wxEXPAND, 5 ); + + wxStaticBoxSizer* sPlayOptions; + sPlayOptions = new wxStaticBoxSizer( new wxStaticBox( m_PlayPage, wxID_ANY, _("Playback Options") ), wxVERTICAL ); + + m_EarlyMemoryUpdates = new wxCheckBox( m_PlayPage, wxID_ANY, _("Early Memory Updates"), wxDefaultPosition, wxDefaultSize, 0 ); + sPlayOptions->Add( m_EarlyMemoryUpdates, 0, wxALL, 5 ); + + sPlayPage->Add( sPlayOptions, 0, wxEXPAND, 5 ); + + m_PlayPage->SetSizer( sPlayPage ); + m_PlayPage->Layout(); + sPlayPage->Fit( m_PlayPage ); + m_Notebook->AddPage( m_PlayPage, wxT("Play"), true ); + m_RecordPage = new wxPanel( m_Notebook, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL ); + wxBoxSizer* sRecordPage; + sRecordPage = new wxBoxSizer( wxVERTICAL ); + + wxStaticBoxSizer* sRecordInfo; + sRecordInfo = new wxStaticBoxSizer( new wxStaticBox( m_RecordPage, wxID_ANY, _("Recording Info") ), wxVERTICAL ); + + m_RecordingFifoSizeLabel = new wxStaticText( m_RecordPage, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0 ); + m_RecordingFifoSizeLabel->Wrap( -1 ); + sRecordInfo->Add( m_RecordingFifoSizeLabel, 0, wxALL, 5 ); + + m_RecordingMemSizeLabel = new wxStaticText( m_RecordPage, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0 ); + m_RecordingMemSizeLabel->Wrap( -1 ); + sRecordInfo->Add( m_RecordingMemSizeLabel, 0, wxALL, 5 ); + + m_RecordingFramesLabel = new wxStaticText( m_RecordPage, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0 ); + m_RecordingFramesLabel->Wrap( -1 ); + sRecordInfo->Add( m_RecordingFramesLabel, 0, wxALL, 5 ); + + sRecordPage->Add( sRecordInfo, 0, wxEXPAND, 5 ); + + wxBoxSizer* sRecordButtons; + sRecordButtons = new wxBoxSizer( wxHORIZONTAL ); + + m_RecordStop = new wxButton( m_RecordPage, wxID_ANY, _("Record"), wxDefaultPosition, wxDefaultSize, 0 ); + sRecordButtons->Add( m_RecordStop, 0, wxALL, 5 ); + + m_Save = new wxButton( m_RecordPage, wxID_ANY, _("Save"), wxDefaultPosition, wxDefaultSize, 0 ); + sRecordButtons->Add( m_Save, 0, wxALL, 5 ); + + sRecordPage->Add( sRecordButtons, 0, wxEXPAND, 5 ); + + wxStaticBoxSizer* sRecordingOptions; + sRecordingOptions = new wxStaticBoxSizer( new wxStaticBox( m_RecordPage, wxID_ANY, _("Recording Options") ), wxHORIZONTAL ); + + m_FramesToRecordLabel = new wxStaticText( m_RecordPage, wxID_ANY, _("Frames To Record"), wxDefaultPosition, wxDefaultSize, 0 ); + m_FramesToRecordLabel->Wrap( -1 ); + sRecordingOptions->Add( m_FramesToRecordLabel, 0, wxALL, 5 ); + + m_FramesToRecordCtrl = new wxSpinCtrl( m_RecordPage, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxSP_ARROW_KEYS, 0, 10000, 1 ); + sRecordingOptions->Add( m_FramesToRecordCtrl, 0, wxALL, 5 ); + + sRecordPage->Add( sRecordingOptions, 0, wxEXPAND, 5 ); + + m_RecordPage->SetSizer( sRecordPage ); + m_RecordPage->Layout(); + sRecordPage->Fit( m_RecordPage ); + m_Notebook->AddPage( m_RecordPage, _("Record"), false ); + + sMain->Add( m_Notebook, 1, wxEXPAND | wxALL, 5 ); + + wxBoxSizer* sButtons; + sButtons = new wxBoxSizer( wxHORIZONTAL ); + + wxBoxSizer* sCloseButtonExpander; + sCloseButtonExpander = new wxBoxSizer( wxHORIZONTAL ); + + sButtons->Add( sCloseButtonExpander, 1, wxEXPAND, 5 ); + + m_Close = new wxButton( this, wxID_ANY, _("Close"), wxDefaultPosition, wxDefaultSize, 0 ); + sButtons->Add( m_Close, 0, wxALL, 5 ); + + sMain->Add( sButtons, 0, wxEXPAND, 5 ); + + SetSizer( sMain ); + Layout(); + sMain->Fit( this ); + + this->Center( wxBOTH ); + + // Connect Events + this->Connect( wxEVT_PAINT, wxPaintEventHandler( FifoPlayerDlg::OnPaint ) ); + m_FrameFromCtrl->Connect( wxEVT_COMMAND_SPINCTRL_UPDATED, wxSpinEventHandler( FifoPlayerDlg::OnFrameFrom ), NULL, this ); + m_FrameToCtrl->Connect( wxEVT_COMMAND_SPINCTRL_UPDATED, wxSpinEventHandler( FifoPlayerDlg::OnFrameTo ), NULL, this ); + m_ObjectFromCtrl->Connect( wxEVT_COMMAND_SPINCTRL_UPDATED, wxSpinEventHandler( FifoPlayerDlg::OnObjectFrom ), NULL, this ); + m_ObjectToCtrl->Connect( wxEVT_COMMAND_SPINCTRL_UPDATED, wxSpinEventHandler( FifoPlayerDlg::OnObjectTo ), NULL, this ); + m_EarlyMemoryUpdates->Connect( wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler( FifoPlayerDlg::OnCheckEarlyMemoryUpdates ), NULL, this ); + m_RecordStop->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( FifoPlayerDlg::OnRecordStop ), NULL, this ); + m_Save->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( FifoPlayerDlg::OnSaveFile ), NULL, this ); + m_FramesToRecordCtrl->Connect( wxEVT_COMMAND_SPINCTRL_UPDATED, wxSpinEventHandler( FifoPlayerDlg::OnNumFramesToRecord ), NULL, this ); + m_Close->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( FifoPlayerDlg::OnCloseClick ), NULL, this ); + + Connect(RECORDING_FINISHED_EVENT, wxCommandEventHandler(FifoPlayerDlg::OnRecordingFinished), NULL, this); + Connect(FRAME_WRITTEN_EVENT, wxCommandEventHandler(FifoPlayerDlg::OnFrameWritten), NULL, this); + + Show(); +} + +void FifoPlayerDlg::OnPaint( wxPaintEvent& event ) +{ + UpdatePlayGui(); + UpdateRecorderGui(); + + event.Skip(); +} + +void FifoPlayerDlg::OnFrameFrom( wxSpinEvent& event ) +{ + FifoPlayer &player = FifoPlayer::GetInstance(); + player.SetFrameRangeStart(event.GetPosition()); + + m_FrameFromCtrl->SetValue(player.GetFrameRangeStart()); + m_FrameToCtrl->SetValue(player.GetFrameRangeEnd()); +} + +void FifoPlayerDlg::OnFrameTo( wxSpinEvent& event ) +{ + FifoPlayer &player = FifoPlayer::GetInstance(); + player.SetFrameRangeEnd(event.GetPosition()); + + m_FrameFromCtrl->SetValue(player.GetFrameRangeStart()); + m_FrameToCtrl->SetValue(player.GetFrameRangeEnd()); +} + +void FifoPlayerDlg::OnObjectFrom( wxSpinEvent& event ) +{ + FifoPlayer::GetInstance().SetObjectRangeStart(event.GetPosition()); +} + +void FifoPlayerDlg::OnObjectTo( wxSpinEvent& event ) +{ + FifoPlayer::GetInstance().SetObjectRangeEnd(event.GetPosition()); +} + +void FifoPlayerDlg::OnCheckEarlyMemoryUpdates( wxCommandEvent& event ) +{ + FifoPlayer::GetInstance().SetEarlyMemoryUpdates(event.IsChecked()); +} + +void FifoPlayerDlg::OnSaveFile( wxCommandEvent& WXUNUSED (event) ) +{ + FifoDataFile *file = FifoRecorder::GetInstance().GetRecordedFile(); + + if (file) + { + wxString path = wxSaveFileSelector(_("Dolphin FIFO"), wxT("dff"), wxEmptyString, this); + + if (!path.empty()) + { + wxBeginBusyCursor(); + bool result = file->Save(path.mb_str()); + wxEndBusyCursor(); + + if (!result) + PanicAlert("Error saving file"); + } + } +} + +void FifoPlayerDlg::OnRecordStop( wxCommandEvent& WXUNUSED (event) ) +{ + FifoRecorder& recorder = FifoRecorder::GetInstance(); + + if (recorder.IsRecording()) + { + recorder.StopRecording(); + m_RecordStop->Disable(); + } + else + { + recorder.StartRecording(m_FramesToRecord, RecordingFinished); + m_RecordStop->SetLabel(_("Stop")); + } +} + +void FifoPlayerDlg::OnNumFramesToRecord( wxSpinEvent& event ) +{ + m_FramesToRecord = event.GetPosition(); + + // Entering 0 frames in the control indicates infinite frames to record + // The fifo recorder takes any value < 0 to be infinite frames + if (m_FramesToRecord < 1) + m_FramesToRecord = -1; +} + +void FifoPlayerDlg::OnCloseClick( wxCommandEvent& WXUNUSED (event) ) +{ + Hide(); +} + +void FifoPlayerDlg::OnRecordingFinished(wxCommandEvent& WXUNUSED (event) ) +{ + m_RecordStop->SetLabel(_("Record")); + m_RecordStop->Enable(); + + UpdateRecorderGui(); +} + +void FifoPlayerDlg::OnFrameWritten(wxCommandEvent& WXUNUSED (event)) +{ + m_CurrentFrameLabel->SetLabel(CreateCurrentFrameLabel()); + m_NumObjectsLabel->SetLabel(CreateFileObjectCountLabel()); +} + +void FifoPlayerDlg::UpdatePlayGui() +{ + m_NumFramesLabel->SetLabel(CreateFileFrameCountLabel()); + m_CurrentFrameLabel->SetLabel(CreateCurrentFrameLabel()); + m_NumObjectsLabel->SetLabel(CreateFileObjectCountLabel()); + + FifoPlayer &player = FifoPlayer::GetInstance(); + FifoDataFile *file = player.GetFile(); + u32 frameCount = 0; + if (file) + frameCount = file->GetFrameCount(); + + m_FrameFromCtrl->SetRange(0, frameCount); + m_FrameFromCtrl->SetValue(player.GetFrameRangeStart()); + + m_FrameToCtrl->SetRange(0, frameCount); + m_FrameToCtrl->SetValue(player.GetFrameRangeEnd()); + + m_ObjectFromCtrl->SetValue(player.GetObjectRangeStart()); + m_ObjectToCtrl->SetValue(player.GetObjectRangeEnd()); +} + +void FifoPlayerDlg::UpdateRecorderGui() +{ + m_RecordingFifoSizeLabel->SetLabel(CreateRecordingFifoSizeLabel()); + m_RecordingMemSizeLabel->SetLabel(CreateRecordingMemSizeLabel()); + m_RecordingFramesLabel->SetLabel(CreateRecordingFrameCountLabel()); + m_Save->Enable(GetSaveButtonEnabled()); +} + +wxString FifoPlayerDlg::CreateFileFrameCountLabel() const +{ + FifoDataFile *file = FifoPlayer::GetInstance().GetFile(); + + if (file) + return CreateIntegerLabel(file->GetFrameCount(), _("Frame")); + + return _("No file loaded"); +} + +wxString FifoPlayerDlg::CreateCurrentFrameLabel() const +{ + FifoDataFile *file = FifoPlayer::GetInstance().GetFile(); + + if (file) + return _("Frame ") + wxString::Format(wxT("%i"), FifoPlayer::GetInstance().GetCurrentFrameNum()); + + return wxEmptyString; +} + +wxString FifoPlayerDlg::CreateFileObjectCountLabel() const +{ + FifoDataFile *file = FifoPlayer::GetInstance().GetFile(); + + if (file) + return CreateIntegerLabel(FifoPlayer::GetInstance().GetFrameObjectCount(), _("Object")); + + return wxEmptyString; +} + +wxString FifoPlayerDlg::CreateRecordingFifoSizeLabel() const +{ + FifoDataFile *file = FifoRecorder::GetInstance().GetRecordedFile(); + + if (file) + { + int fifoBytes = 0; + for (int i = 0; i < file->GetFrameCount(); ++i) + fifoBytes += file->GetFrame(i).fifoDataSize; + + return CreateIntegerLabel(fifoBytes, _("FIFO Byte")); + } + + return _("No recorded file"); +} + +wxString FifoPlayerDlg::CreateRecordingMemSizeLabel() const +{ + FifoDataFile *file = FifoRecorder::GetInstance().GetRecordedFile(); + + if (file) + { + int memBytes = 0; + for (int frameNum = 0; frameNum < file->GetFrameCount(); ++frameNum) + { + const vector<MemoryUpdate>& memUpdates = file->GetFrame(frameNum).memoryUpdates; + for (unsigned int i = 0; i < memUpdates.size(); ++i) + memBytes += memUpdates[i].size; + } + + return CreateIntegerLabel(memBytes, _("Memory Byte")); + } + + return wxEmptyString; +} + +wxString FifoPlayerDlg::CreateRecordingFrameCountLabel() const +{ + FifoDataFile *file = FifoRecorder::GetInstance().GetRecordedFile(); + + if (file) + { + int numFrames = file->GetFrameCount(); + return CreateIntegerLabel(numFrames, _("Frame")); + } + + return wxEmptyString; +} + +wxString FifoPlayerDlg::CreateIntegerLabel(int size, const wxString& label) const +{ + wxString postfix; + if (size != 1) + postfix = _("s"); + + return wxString::Format(wxT("%i"), size) + wxT(" ") + label + postfix; +} + +bool FifoPlayerDlg::GetSaveButtonEnabled() const +{ + return (FifoRecorder::GetInstance().GetRecordedFile() != NULL); +} + +void FifoPlayerDlg::RecordingFinished() +{ + sMutex.lock(); + + wxEvtHandler* evtHandler = m_EvtHandler; + if (evtHandler) + { + wxCommandEvent event(RECORDING_FINISHED_EVENT); + evtHandler->AddPendingEvent( event ); + } + + sMutex.unlock(); +} + +void FifoPlayerDlg::FileLoaded() +{ + sMutex.lock(); + + wxEvtHandler* evtHandler = m_EvtHandler; + if (evtHandler) + { + wxPaintEvent event; + evtHandler->AddPendingEvent( event ); + } + + sMutex.unlock(); +} + +void FifoPlayerDlg::FrameWritten() +{ + sMutex.lock(); + + wxEvtHandler* evtHandler = m_EvtHandler; + if (evtHandler) + { + wxCommandEvent event(FRAME_WRITTEN_EVENT); + evtHandler->AddPendingEvent( event ); + } + + sMutex.unlock(); +}
\ No newline at end of file diff --git a/Source/Core/DolphinWX/Src/FifoPlayerDlg.h b/Source/Core/DolphinWX/Src/FifoPlayerDlg.h new file mode 100644 index 0000000000..7ecbef8de3 --- /dev/null +++ b/Source/Core/DolphinWX/Src/FifoPlayerDlg.h @@ -0,0 +1,96 @@ +// Copyright (C) 2003 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program 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 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#ifndef __FIFO_PLAYER_DLG_h__ +#define __FIFO_PLAYER_DLG_h__ + +#include <wx/wx.h> +#include <wx/notebook.h> + +class wxSpinCtrl; +class wxSpinEvent; + +class FifoPlayerDlg : public wxDialog +{ +public: + FifoPlayerDlg(wxWindow* parent); + ~FifoPlayerDlg(); + +private: + void CreateGUIControls(); + + void OnPaint( wxPaintEvent& event ); + void OnFrameFrom( wxSpinEvent& event ); + void OnFrameTo( wxSpinEvent& event ); + void OnObjectFrom( wxSpinEvent& event ); + void OnObjectTo( wxSpinEvent& event ); + void OnCheckEarlyMemoryUpdates( wxCommandEvent& event ); + void OnRecordStop( wxCommandEvent& event ); + void OnSaveFile( wxCommandEvent& event ); + void OnNumFramesToRecord( wxSpinEvent& event ); + void OnCloseClick( wxCommandEvent& event ); + + void OnRecordingFinished(wxCommandEvent& event); + void OnFrameWritten(wxCommandEvent& event); + + void UpdatePlayGui(); + void UpdateRecorderGui(); + + wxString CreateFileFrameCountLabel() const; + wxString CreateCurrentFrameLabel() const; + wxString CreateFileObjectCountLabel() const; + wxString CreateRecordingFifoSizeLabel() const; + wxString CreateRecordingMemSizeLabel() const; + wxString CreateRecordingFrameCountLabel() const; + wxString CreateIntegerLabel(int size, const wxString& label) const; + + bool GetSaveButtonEnabled() const; + + // Called from a non-GUI thread + static void RecordingFinished(); + static void FileLoaded(); + static void FrameWritten(); + + static wxEvtHandler *volatile m_EvtHandler; + + wxNotebook* m_Notebook; + wxPanel* m_PlayPage; + wxStaticText* m_NumFramesLabel; + wxStaticText* m_CurrentFrameLabel; + wxStaticText* m_NumObjectsLabel; + wxStaticText* m_FrameFromLabel; + wxSpinCtrl* m_FrameFromCtrl; + wxStaticText* m_FrameToLabel; + wxSpinCtrl* m_FrameToCtrl; + wxStaticText* m_ObjectFromLabel; + wxSpinCtrl* m_ObjectFromCtrl; + wxStaticText* m_ObjectToLabel; + wxSpinCtrl* m_ObjectToCtrl; + wxCheckBox* m_EarlyMemoryUpdates; + wxPanel* m_RecordPage; + wxStaticText* m_RecordingFifoSizeLabel; + wxStaticText* m_RecordingMemSizeLabel; + wxStaticText* m_RecordingFramesLabel; + wxButton* m_RecordStop; + wxButton* m_Save; + wxStaticText* m_FramesToRecordLabel; + wxSpinCtrl* m_FramesToRecordCtrl; + wxButton* m_Close; + + s32 m_FramesToRecord; +}; +#endif diff --git a/Source/Core/DolphinWX/Src/Frame.cpp b/Source/Core/DolphinWX/Src/Frame.cpp index 844743a37f..c8c1968797 100644 --- a/Source/Core/DolphinWX/Src/Frame.cpp +++ b/Source/Core/DolphinWX/Src/Frame.cpp @@ -272,6 +272,7 @@ EVT_MENU(IDM_CHEATS, CFrame::OnShow_CheatsWindow) EVT_MENU(IDM_CHANGEDISC, CFrame::OnChangeDisc) EVT_MENU(IDM_INSTALLWAD, CFrame::OnLoadWiiMenu) EVT_MENU(IDM_LOAD_WII_MENU, CFrame::OnLoadWiiMenu) +EVT_MENU(IDM_FIFOPLAYER, CFrame::OnFifoPlayer) EVT_MENU(IDM_TOGGLE_FULLSCREEN, CFrame::OnToggleFullscreen) EVT_MENU(IDM_TOGGLE_DUALCORE, CFrame::OnToggleDualCore) @@ -332,7 +333,8 @@ CFrame::CFrame(wxFrame* parent, , m_ToolBar(NULL), m_ToolBarDebug(NULL), m_ToolBarAui(NULL) , m_GameListCtrl(NULL), m_Panel(NULL) , m_RenderFrame(NULL), m_RenderParent(NULL) - , m_LogWindow(NULL), m_LogConfigWindow(NULL), UseDebugger(_UseDebugger) + , m_LogWindow(NULL), m_LogConfigWindow(NULL) + , m_FifoPlayerDlg(NULL), UseDebugger(_UseDebugger) , m_bBatchMode(_BatchMode), m_bEdit(false), m_bTabSplit(false), m_bNoDocking(false) , m_bGameLoading(false) { diff --git a/Source/Core/DolphinWX/Src/Frame.h b/Source/Core/DolphinWX/Src/Frame.h index 149264460e..ba7525ffe8 100644 --- a/Source/Core/DolphinWX/Src/Frame.h +++ b/Source/Core/DolphinWX/Src/Frame.h @@ -48,6 +48,7 @@ static inline wxBitmap _wxGetBitmapFromMemory(const unsigned char* data, int len class CGameListCtrl; class GameListItem; class CLogWindow; +class FifoPlayerDlg; class NetPlaySetupDiag; class wxCheatsWindow; @@ -175,6 +176,7 @@ private: wxPanel* m_RenderParent; CLogWindow* m_LogWindow; LogConfigWindow* m_LogConfigWindow; + FifoPlayerDlg* m_FifoPlayerDlg; bool UseDebugger; bool m_bBatchMode; bool m_bEdit; @@ -337,6 +339,7 @@ private: void OnShow_CheatsWindow(wxCommandEvent& event); void OnLoadWiiMenu(wxCommandEvent& event); + void OnFifoPlayer(wxCommandEvent& event); void OnConnectWiimote(wxCommandEvent& event); void GameListChanged(wxCommandEvent& event); diff --git a/Source/Core/DolphinWX/Src/FrameTools.cpp b/Source/Core/DolphinWX/Src/FrameTools.cpp index b6962af435..b489c7c4e6 100644 --- a/Source/Core/DolphinWX/Src/FrameTools.cpp +++ b/Source/Core/DolphinWX/Src/FrameTools.cpp @@ -51,6 +51,7 @@ Core::GetWindowHandle(). #include "BootManager.h" #include "LogWindow.h" #include "LogConfigWindow.h" +#include "FifoPlayerDlg.h" #include "WxUtils.h" #include "ConfigManager.h" // Core @@ -211,6 +212,9 @@ void CFrame::CreateMenu() toolsMenu->Append(IDM_LOAD_WII_MENU, wxString::Format(_("Load Wii System Menu %d%c"), sysmenuVersion, sysmenuRegion)); toolsMenu->Enable(IDM_LOAD_WII_MENU, DiscIO::CNANDContentManager::Access().GetNANDLoader(TITLEID_SYSMENU).IsValid()); + + toolsMenu->Append(IDM_FIFOPLAYER, _("Fifo Player")); + toolsMenu->AppendSeparator(); toolsMenu->AppendCheckItem(IDM_CONNECT_WIIMOTE1, GetMenuLabel(HK_WIIMOTE1_CONNECT)); toolsMenu->AppendCheckItem(IDM_CONNECT_WIIMOTE2, GetMenuLabel(HK_WIIMOTE2_CONNECT)); @@ -664,7 +668,7 @@ void CFrame::DoOpen(bool Boot) _("Select the file to load"), wxEmptyString, wxEmptyString, wxEmptyString, _("All GC/Wii files (elf, dol, gcm, iso, ciso, gcz, wad)") + - wxString::Format(wxT("|*.elf;*.dol;*.gcm;*.iso;*.ciso;*.gcz;*.wad|%s"), + wxString::Format(wxT("|*.elf;*.dol;*.gcm;*.iso;*.ciso;*.gcz;*.wad;*.dff|%s"), wxGetTranslation(wxALL_FILES)), wxFD_OPEN | wxFD_FILE_MUST_EXIST, this); @@ -1357,6 +1361,19 @@ void CFrame::OnLoadWiiMenu(wxCommandEvent& event) } +void CFrame::OnFifoPlayer(wxCommandEvent& WXUNUSED (event)) +{ + if (m_FifoPlayerDlg) + { + m_FifoPlayerDlg->Show(); + m_FifoPlayerDlg->SetFocus(); + } + else + { + m_FifoPlayerDlg = new FifoPlayerDlg(this); + } +} + void CFrame::ConnectWiimote(int wm_idx, bool connect) { if (Core::IsRunning() && SConfig::GetInstance().m_LocalCoreStartupParameter.bWii) diff --git a/Source/Core/DolphinWX/Src/Globals.h b/Source/Core/DolphinWX/Src/Globals.h index 72d8d81701..06191ffa5b 100644 --- a/Source/Core/DolphinWX/Src/Globals.h +++ b/Source/Core/DolphinWX/Src/Globals.h @@ -94,6 +94,7 @@ enum IDM_PROPERTIES, IDM_GAMEWIKI, IDM_LOAD_WII_MENU, + IDM_FIFOPLAYER, IDM_CONNECT_WIIMOTE1, IDM_CONNECT_WIIMOTE2, IDM_CONNECT_WIIMOTE3, diff --git a/Source/Core/VideoCommon/Src/CPMemory.h b/Source/Core/VideoCommon/Src/CPMemory.h index c7de2afc6d..c4a24ba7a4 100644 --- a/Source/Core/VideoCommon/Src/CPMemory.h +++ b/Source/Core/VideoCommon/Src/CPMemory.h @@ -262,4 +262,7 @@ extern VAT g_VtxAttr[8]; // Might move this into its own file later. void LoadCPReg(u32 SubCmd, u32 Value); +// Fills memory with data from CP regs +void FillCPMemoryArray(u32 *memory); + #endif // _CPMEMORY_H diff --git a/Source/Core/VideoCommon/Src/OpcodeDecoding.cpp b/Source/Core/VideoCommon/Src/OpcodeDecoding.cpp index 6d939882e7..18d6726ae3 100644 --- a/Source/Core/VideoCommon/Src/OpcodeDecoding.cpp +++ b/Source/Core/VideoCommon/Src/OpcodeDecoding.cpp @@ -33,6 +33,7 @@ #include "Core.h" #include "Host.h" #include "HW/Memmap.h" +#include "FifoPlayer/FifoRecorder.h" #include "VertexLoaderManager.h" @@ -50,6 +51,8 @@ #include "VideoConfig.h" u8* g_pVideoData = 0; +bool g_bRecordFifoData = false; + #if _M_SSE >= 0x301 DataReadU32xNfunc DataReadU32xFuncs_SSSE3[16] = { DataReadU32xN_SSSE3<1>, @@ -254,6 +257,8 @@ bool FifoCommandRunnable() static void Decode() { + u8 *opcodeStart = g_pVideoData; + int cmd_byte = DataReadU8(); switch (cmd_byte) { @@ -338,10 +343,16 @@ static void Decode() } break; } + + // Display lists get added directly into the FIFO stream + if (g_bRecordFifoData && cmd_byte != GX_CMD_CALL_DL) + FifoRecorder::GetInstance().WriteGPCommand(opcodeStart, g_pVideoData - opcodeStart); } static void DecodeSemiNop() { + u8 *opcodeStart = g_pVideoData; + int cmd_byte = DataReadU8(); switch (cmd_byte) { @@ -416,6 +427,9 @@ static void DecodeSemiNop() } break; } + + if (g_bRecordFifoData && cmd_byte != GX_CMD_CALL_DL) + FifoRecorder::GetInstance().WriteGPCommand(opcodeStart, g_pVideoData - opcodeStart); } void OpcodeDecoder_Init() diff --git a/Source/Core/VideoCommon/Src/OpcodeDecoding.h b/Source/Core/VideoCommon/Src/OpcodeDecoding.h index 9989f0fd24..f2e9cd1321 100644 --- a/Source/Core/VideoCommon/Src/OpcodeDecoding.h +++ b/Source/Core/VideoCommon/Src/OpcodeDecoding.h @@ -45,6 +45,9 @@ #define GX_DRAW_LINE_STRIP 0x6 // 0xB0 #define GX_DRAW_POINTS 0x7 // 0xB8 #define GX_DRAW_NONE 0x1; //Tis is a fake value to used in the backends + +extern bool g_bRecordFifoData; + void OpcodeDecoder_Init(); void OpcodeDecoder_Shutdown(); void OpcodeDecoder_Run(bool skipped_frame); diff --git a/Source/Core/VideoCommon/Src/RenderBase.cpp b/Source/Core/VideoCommon/Src/RenderBase.cpp index c7b18e509c..9029f5eff9 100644 --- a/Source/Core/VideoCommon/Src/RenderBase.cpp +++ b/Source/Core/VideoCommon/Src/RenderBase.cpp @@ -28,14 +28,20 @@ #include "RenderBase.h" #include "Atomic.h" +#include "BPMemory.h" +#include "CommandProcessor.h" +#include "CPMemory.h" #include "MainBase.h" #include "VideoConfig.h" #include "FramebufferManagerBase.h" #include "TextureCacheBase.h" #include "Fifo.h" +#include "OpcodeDecoding.h" #include "Timer.h" #include "StringUtil.h" #include "Host.h" +#include "XFMemory.h" +#include "FifoPlayer/FifoRecorder.h" #include <cmath> #include <string> @@ -75,6 +81,7 @@ int Renderer::s_LastEFBScale; bool Renderer::s_skipSwap; bool Renderer::XFBWrited; +bool Renderer::s_EnableDLCachingAfterRecording; unsigned int Renderer::prev_efb_format = (unsigned int)-1; @@ -91,6 +98,8 @@ Renderer::~Renderer() void Renderer::RenderToXFB(u32 xfbAddr, u32 fbWidth, u32 fbHeight, const EFBRectangle& sourceRc, float Gamma) { + CheckFifoRecording(); + if (!fbWidth || !fbHeight) return; @@ -332,6 +341,43 @@ void Renderer::SetWindowSize(int width, int height) Host_RequestRenderWindowSize(width, height); } +void Renderer::CheckFifoRecording() +{ + bool wasRecording = g_bRecordFifoData; + g_bRecordFifoData = FifoRecorder::GetInstance().IsRecording(); + + if (g_bRecordFifoData) + { + if (!wasRecording) + { + // Disable display list caching because the recorder does not handle it + s_EnableDLCachingAfterRecording = g_ActiveConfig.bDlistCachingEnable; + g_ActiveConfig.bDlistCachingEnable = false; + + RecordVideoMemory(); + } + + FifoRecorder::GetInstance().EndFrame(CommandProcessor::fifo.CPBase, CommandProcessor::fifo.CPEnd); + } + else if (wasRecording) + { + g_ActiveConfig.bDlistCachingEnable = s_EnableDLCachingAfterRecording; + } +} + +void Renderer::RecordVideoMemory() +{ + u32 *bpMem = (u32*)&bpmem; + u32 cpMem[256]; + u32 *xfMem = (u32*)xfmem; + u32 *xfRegs = (u32*)&xfregs; + + memset(cpMem, 0, 256 * 4); + FillCPMemoryArray(cpMem); + + FifoRecorder::GetInstance().SetVideoMemory(bpMem, cpMem, xfMem, xfRegs, sizeof(XFRegisters) / 4); +} + void UpdateViewport() { g_renderer->UpdateViewport(); diff --git a/Source/Core/VideoCommon/Src/RenderBase.h b/Source/Core/VideoCommon/Src/RenderBase.h index 9872200688..98f32c443c 100644 --- a/Source/Core/VideoCommon/Src/RenderBase.h +++ b/Source/Core/VideoCommon/Src/RenderBase.h @@ -151,6 +151,9 @@ protected: static bool CalculateTargetSize(int multiplier = 1); static void CalculateXYScale(const TargetRectangle& dst_rect); + static void CheckFifoRecording(); + static void RecordVideoMemory(); + static volatile bool s_bScreenshot; // The framebuffer size @@ -178,6 +181,8 @@ protected: static bool s_skipSwap; static bool XFBWrited; + static bool s_EnableDLCachingAfterRecording; + private: static unsigned int prev_efb_format; }; diff --git a/Source/Core/VideoCommon/Src/VertexLoaderManager.cpp b/Source/Core/VideoCommon/Src/VertexLoaderManager.cpp index 564892cb98..316941a639 100644 --- a/Source/Core/VideoCommon/Src/VertexLoaderManager.cpp +++ b/Source/Core/VideoCommon/Src/VertexLoaderManager.cpp @@ -214,6 +214,27 @@ void LoadCPReg(u32 sub_cmd, u32 value) } } +void FillCPMemoryArray(u32 *memory) +{ + memory[0x30] = MatrixIndexA.Hex; + memory[0x40] = MatrixIndexB.Hex; + memory[0x50] = (u32)g_VtxDesc.Hex; + memory[0x60] = (u32)(g_VtxDesc.Hex >> 17); + + for (int i = 0; i < 8; ++i) + { + memory[0x70 + i] = g_VtxAttr[i].g0.Hex; + memory[0x80 + i] = g_VtxAttr[i].g1.Hex; + memory[0x90 + i] = g_VtxAttr[i].g2.Hex; + } + + for (int i = 0; i < 16; ++i) + { + memory[0xA0 + i] = arraybases[i]; + memory[0xB0 + i] = arraystrides[i]; + } +} + void RecomputeCachedArraybases() { for (int i = 0; i < 16; i++) |
