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