1 // Copyright 2011 Dolphin Emulator Project
2 // Licensed under GPLv2+
3 // Refer to the license.txt file included.
4 
5 #pragma once
6 
7 #include <functional>
8 #include <memory>
9 #include <string>
10 #include <vector>
11 
12 #include "Core/FifoPlayer/FifoDataFile.h"
13 #include "Core/FifoPlayer/FifoPlaybackAnalyzer.h"
14 #include "Core/PowerPC/CPUCoreBase.h"
15 
16 class FifoDataFile;
17 struct MemoryUpdate;
18 struct AnalyzedFrameInfo;
19 
20 namespace CPU
21 {
22 enum class State;
23 }
24 
25 // Story time:
26 // When FifoRecorder was created, efb copies weren't really used or they used efb2tex which ignored
27 // the underlying memory, so FifoRecorder didn't do anything special about the memory backing efb
28 // copies. This means the memory underlying efb copies go treated like regular textures and was
29 // baked into the fifo log. If you recorded with efb2ram on, the result of efb2ram would be baked
30 // into the fifo. If you recorded with efb2tex or efb off, random data would be included in the fifo
31 // log.
32 // Later the behaviour of efb2tex was changed to zero the underlying memory and check the hash of
33 // that.
34 // But this broke a whole lot of fifologs due to the following sequence of events:
35 //    1. fifoplayer would trigger the efb copy
36 //    2. Texture cache would zero the memory backing the texture and hash it.
37 //    3. Time passes.
38 //    4. fifoplayer would encounter the drawcall using the efb copy
39 //    5. fifoplayer would overwrite the memory backing the efb copy back to it's state when
40 //    recording.
41 //    6. Texture cache would hash the memory and see that the hash no-longer matches
42 //    7. Texture cache would load whatever data was now in memory as a texture either a baked in
43 //       efb2ram copy from recording time or just random data.
44 //    8. The output of fifoplayer would be wrong.
45 
46 // To keep compatibility with old fifologs, we have this flag which signals texture cache to not
47 // bother
48 // hashing the memory and just assume the hash matched.
49 // At a later point proper efb copy support should be added to fiforecorder and this flag will
50 // change
51 // based on the version of the .dff file, but until then it will always be true when a fifolog is
52 // playing.
53 
54 // Shitty global to fix a shitty problem
55 extern bool IsPlayingBackFifologWithBrokenEFBCopies;
56 
57 class FifoPlayer
58 {
59 public:
60   using CallbackFunc = std::function<void()>;
61 
62   ~FifoPlayer();
63 
64   bool Open(const std::string& filename);
65   void Close();
66 
67   // Returns a CPUCoreBase instance that can be injected into PowerPC as a
68   // pseudo-CPU. The instance is only valid while the FifoPlayer is Open().
69   // Returns nullptr if the FifoPlayer is not initialized correctly.
70   // Play/Pause/Stop of the FifoLog can be controlled normally via the
71   // PowerPC state.
72   std::unique_ptr<CPUCoreBase> GetCPUCore();
73 
74   bool IsPlaying() const;
75 
GetFile()76   FifoDataFile* GetFile() const { return m_File.get(); }
77   u32 GetFrameObjectCount() const;
GetCurrentFrameNum()78   u32 GetCurrentFrameNum() const { return m_CurrentFrame; }
GetAnalyzedFrameInfo(u32 frame)79   const AnalyzedFrameInfo& GetAnalyzedFrameInfo(u32 frame) const { return m_FrameInfo[frame]; }
80   // Frame range
GetFrameRangeStart()81   u32 GetFrameRangeStart() const { return m_FrameRangeStart; }
82   void SetFrameRangeStart(u32 start);
83 
GetFrameRangeEnd()84   u32 GetFrameRangeEnd() const { return m_FrameRangeEnd; }
85   void SetFrameRangeEnd(u32 end);
86 
87   // Object range
GetObjectRangeStart()88   u32 GetObjectRangeStart() const { return m_ObjectRangeStart; }
SetObjectRangeStart(u32 start)89   void SetObjectRangeStart(u32 start) { m_ObjectRangeStart = start; }
GetObjectRangeEnd()90   u32 GetObjectRangeEnd() const { return m_ObjectRangeEnd; }
SetObjectRangeEnd(u32 end)91   void SetObjectRangeEnd(u32 end) { m_ObjectRangeEnd = end; }
92   // If enabled then all memory updates happen at once before the first frame
93   // Default is disabled
SetEarlyMemoryUpdates(bool enabled)94   void SetEarlyMemoryUpdates(bool enabled) { m_EarlyMemoryUpdates = enabled; }
95   // Callbacks
96   void SetFileLoadedCallback(CallbackFunc callback);
SetFrameWrittenCallback(CallbackFunc callback)97   void SetFrameWrittenCallback(CallbackFunc callback) { m_FrameWrittenCb = std::move(callback); }
98   static FifoPlayer& GetInstance();
99 
100   bool IsRunningWithFakeVideoInterfaceUpdates() const;
101 
102 private:
103   class CPUCore;
104 
105   FifoPlayer();
106 
107   CPU::State AdvanceFrame();
108 
109   void WriteFrame(const FifoFrameInfo& frame, const AnalyzedFrameInfo& info);
110   void WriteFramePart(u32 dataStart, u32 dataEnd, u32& nextMemUpdate, const FifoFrameInfo& frame,
111                       const AnalyzedFrameInfo& info);
112 
113   void WriteAllMemoryUpdates();
114   void WriteMemory(const MemoryUpdate& memUpdate);
115 
116   // writes a range of data to the fifo
117   // start and end must be relative to frame's fifo data so elapsed cycles are figured correctly
118   void WriteFifo(const u8* data, u32 start, u32 end);
119 
120   void SetupFifo();
121 
122   void LoadMemory();
123   void LoadRegisters();
124   void LoadTextureMemory();
125 
126   void WriteCP(u32 address, u16 value);
127   void WritePI(u32 address, u32 value);
128 
129   void FlushWGP();
130 
131   void LoadBPReg(u8 reg, u32 value);
132   void LoadCPReg(u8 reg, u32 value);
133   void LoadXFReg(u16 reg, u32 value);
134   void LoadXFMem16(u16 address, const u32* data);
135 
136   bool ShouldLoadBP(u8 address);
137 
138   static bool IsIdleSet();
139   static bool IsHighWatermarkSet();
140 
141   bool m_Loop;
142 
143   u32 m_CurrentFrame = 0;
144   u32 m_FrameRangeStart = 0;
145   u32 m_FrameRangeEnd = 0;
146 
147   u32 m_ObjectRangeStart = 0;
148   u32 m_ObjectRangeEnd = 10000;
149 
150   bool m_EarlyMemoryUpdates = false;
151 
152   u64 m_CyclesPerFrame = 0;
153   u32 m_ElapsedCycles = 0;
154   u32 m_FrameFifoSize = 0;
155 
156   CallbackFunc m_FileLoadedCb = nullptr;
157   CallbackFunc m_FrameWrittenCb = nullptr;
158 
159   std::unique_ptr<FifoDataFile> m_File;
160 
161   std::vector<AnalyzedFrameInfo> m_FrameInfo;
162 };
163