mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2025-03-24 17:34:54 +01:00
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
This commit is contained in:
parent
f8037e3ccf
commit
07c4da6084
@ -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
|
||||
|
@ -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" />
|
||||
|
@ -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>
|
@ -364,6 +364,10 @@ bool CBoot::BootUp()
|
||||
break;
|
||||
}
|
||||
|
||||
case SCoreStartupParameter::BOOT_DFF:
|
||||
// do nothing
|
||||
break;
|
||||
|
||||
default:
|
||||
{
|
||||
PanicAlertT("Tried to load an unknown file type.");
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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());
|
||||
|
@ -153,7 +153,8 @@ struct SCoreStartupParameter
|
||||
BOOT_ELF,
|
||||
BOOT_DOL,
|
||||
BOOT_WII_NAND,
|
||||
BOOT_BS2
|
||||
BOOT_BS2,
|
||||
BOOT_DFF
|
||||
};
|
||||
EBootType m_BootType;
|
||||
|
||||
|
235
Source/Core/Core/Src/FifoPlayer/FifoAnalyzer.cpp
Normal file
235
Source/Core/Core/Src/FifoPlayer/FifoAnalyzer.cpp
Normal file
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
54
Source/Core/Core/Src/FifoPlayer/FifoAnalyzer.h
Normal file
54
Source/Core/Core/Src/FifoPlayer/FifoAnalyzer.h
Normal file
@ -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
|
285
Source/Core/Core/Src/FifoPlayer/FifoDataFile.cpp
Normal file
285
Source/Core/Core/Src/FifoPlayer/FifoDataFile.cpp
Normal file
@ -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);
|
||||
}
|
||||
}
|
112
Source/Core/Core/Src/FifoPlayer/FifoDataFile.h
Normal file
112
Source/Core/Core/Src/FifoPlayer/FifoDataFile.h
Normal file
@ -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
|
85
Source/Core/Core/Src/FifoPlayer/FifoFileStruct.h
Normal file
85
Source/Core/Core/Src/FifoPlayer/FifoFileStruct.h
Normal file
@ -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
|
346
Source/Core/Core/Src/FifoPlayer/FifoPlaybackAnalyzer.cpp
Normal file
346
Source/Core/Core/Src/FifoPlayer/FifoPlaybackAnalyzer.cpp
Normal file
@ -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);
|
||||
}
|
||||
}
|
64
Source/Core/Core/Src/FifoPlayer/FifoPlaybackAnalyzer.h
Normal file
64
Source/Core/Core/Src/FifoPlayer/FifoPlaybackAnalyzer.h
Normal file
@ -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
|
456
Source/Core/Core/Src/FifoPlayer/FifoPlayer.cpp
Normal file
456
Source/Core/Core/Src/FifoPlayer/FifoPlayer.cpp
Normal file
@ -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;
|
||||
}
|
121
Source/Core/Core/Src/FifoPlayer/FifoPlayer.h
Normal file
121
Source/Core/Core/Src/FifoPlayer/FifoPlayer.h
Normal file
@ -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
|
305
Source/Core/Core/Src/FifoPlayer/FifoRecordAnalyzer.cpp
Normal file
305
Source/Core/Core/Src/FifoPlayer/FifoRecordAnalyzer.cpp
Normal file
@ -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);
|
||||
}
|
56
Source/Core/Core/Src/FifoPlayer/FifoRecordAnalyzer.h
Normal file
56
Source/Core/Core/Src/FifoPlayer/FifoRecordAnalyzer.h
Normal file
@ -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
|
220
Source/Core/Core/Src/FifoPlayer/FifoRecorder.cpp
Normal file
220
Source/Core/Core/Src/FifoPlayer/FifoRecorder.cpp
Normal file
@ -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;
|
||||
}
|
80
Source/Core/Core/Src/FifoPlayer/FifoRecorder.h
Normal file
80
Source/Core/Core/Src/FifoPlayer/FifoRecorder.h
Normal file
@ -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
|
@ -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
|
||||
|
@ -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" />
|
||||
|
@ -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>
|
511
Source/Core/DolphinWX/Src/FifoPlayerDlg.cpp
Normal file
511
Source/Core/DolphinWX/Src/FifoPlayerDlg.cpp
Normal file
@ -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();
|
||||
}
|
96
Source/Core/DolphinWX/Src/FifoPlayerDlg.h
Normal file
96
Source/Core/DolphinWX/Src/FifoPlayerDlg.h
Normal file
@ -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
|
@ -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)
|
||||
{
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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)
|
||||
|
@ -94,6 +94,7 @@ enum
|
||||
IDM_PROPERTIES,
|
||||
IDM_GAMEWIKI,
|
||||
IDM_LOAD_WII_MENU,
|
||||
IDM_FIFOPLAYER,
|
||||
IDM_CONNECT_WIIMOTE1,
|
||||
IDM_CONNECT_WIIMOTE2,
|
||||
IDM_CONNECT_WIIMOTE3,
|
||||
|
@ -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
|
||||
|
@ -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()
|
||||
|
@ -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);
|
||||
|
@ -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();
|
||||
|
@ -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;
|
||||
};
|
||||
|
@ -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++)
|
||||
|
Loading…
x
Reference in New Issue
Block a user