mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2025-07-26 23:59:54 -06:00

If the number of objects varied, this would result in either missing objects on some frames, or too many objects on some frames; the latter case could cause crashes. Since it used the current frame to get the count, if the FIFO is started before the FIFO analyzer is opened, then the current frame is effectively random, making it hard to reproduce consistently. This issue has existed since the FIFO analyzer was implemented for Qt.
166 lines
5.4 KiB
C++
166 lines
5.4 KiB
C++
// Copyright 2011 Dolphin Emulator Project
|
|
// Licensed under GPLv2+
|
|
// Refer to the license.txt file included.
|
|
|
|
#pragma once
|
|
|
|
#include <functional>
|
|
#include <memory>
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
#include "Core/FifoPlayer/FifoDataFile.h"
|
|
#include "Core/FifoPlayer/FifoPlaybackAnalyzer.h"
|
|
#include "Core/PowerPC/CPUCoreBase.h"
|
|
|
|
class FifoDataFile;
|
|
struct MemoryUpdate;
|
|
struct AnalyzedFrameInfo;
|
|
|
|
namespace CPU
|
|
{
|
|
enum class State;
|
|
}
|
|
|
|
// Story time:
|
|
// When FifoRecorder was created, efb copies weren't really used or they used efb2tex which ignored
|
|
// the underlying memory, so FifoRecorder didn't do anything special about the memory backing efb
|
|
// copies. This means the memory underlying efb copies go treated like regular textures and was
|
|
// baked into the fifo log. If you recorded with efb2ram on, the result of efb2ram would be baked
|
|
// into the fifo. If you recorded with efb2tex or efb off, random data would be included in the fifo
|
|
// log.
|
|
// Later the behaviour of efb2tex was changed to zero the underlying memory and check the hash of
|
|
// that.
|
|
// But this broke a whole lot of fifologs due to the following sequence of events:
|
|
// 1. fifoplayer would trigger the efb copy
|
|
// 2. Texture cache would zero the memory backing the texture and hash it.
|
|
// 3. Time passes.
|
|
// 4. fifoplayer would encounter the drawcall using the efb copy
|
|
// 5. fifoplayer would overwrite the memory backing the efb copy back to it's state when
|
|
// recording.
|
|
// 6. Texture cache would hash the memory and see that the hash no-longer matches
|
|
// 7. Texture cache would load whatever data was now in memory as a texture either a baked in
|
|
// efb2ram copy from recording time or just random data.
|
|
// 8. The output of fifoplayer would be wrong.
|
|
|
|
// To keep compatibility with old fifologs, we have this flag which signals texture cache to not
|
|
// bother
|
|
// hashing the memory and just assume the hash matched.
|
|
// At a later point proper efb copy support should be added to fiforecorder and this flag will
|
|
// change
|
|
// based on the version of the .dff file, but until then it will always be true when a fifolog is
|
|
// playing.
|
|
|
|
// Shitty global to fix a shitty problem
|
|
extern bool IsPlayingBackFifologWithBrokenEFBCopies;
|
|
|
|
class FifoPlayer
|
|
{
|
|
public:
|
|
using CallbackFunc = std::function<void()>;
|
|
|
|
~FifoPlayer();
|
|
|
|
bool Open(const std::string& filename);
|
|
void Close();
|
|
|
|
// Returns a CPUCoreBase instance that can be injected into PowerPC as a
|
|
// pseudo-CPU. The instance is only valid while the FifoPlayer is Open().
|
|
// Returns nullptr if the FifoPlayer is not initialized correctly.
|
|
// Play/Pause/Stop of the FifoLog can be controlled normally via the
|
|
// PowerPC state.
|
|
std::unique_ptr<CPUCoreBase> GetCPUCore();
|
|
|
|
bool IsPlaying() const;
|
|
|
|
FifoDataFile* GetFile() const { return m_File.get(); }
|
|
u32 GetMaxObjectCount() const;
|
|
u32 GetFrameObjectCount(u32 frame) const;
|
|
u32 GetCurrentFrameObjectCount() const;
|
|
u32 GetCurrentFrameNum() const { return m_CurrentFrame; }
|
|
const AnalyzedFrameInfo& GetAnalyzedFrameInfo(u32 frame) const { return m_FrameInfo[frame]; }
|
|
// Frame range
|
|
u32 GetFrameRangeStart() const { return m_FrameRangeStart; }
|
|
void SetFrameRangeStart(u32 start);
|
|
|
|
u32 GetFrameRangeEnd() const { return m_FrameRangeEnd; }
|
|
void SetFrameRangeEnd(u32 end);
|
|
|
|
// Object range
|
|
u32 GetObjectRangeStart() const { return m_ObjectRangeStart; }
|
|
void SetObjectRangeStart(u32 start) { m_ObjectRangeStart = start; }
|
|
u32 GetObjectRangeEnd() const { 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);
|
|
void SetFrameWrittenCallback(CallbackFunc callback) { m_FrameWrittenCb = std::move(callback); }
|
|
static FifoPlayer& GetInstance();
|
|
|
|
bool IsRunningWithFakeVideoInterfaceUpdates() const;
|
|
|
|
private:
|
|
class CPUCore;
|
|
|
|
FifoPlayer();
|
|
|
|
CPU::State AdvanceFrame();
|
|
|
|
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(const u8* data, u32 start, u32 end);
|
|
|
|
void SetupFifo();
|
|
|
|
void LoadMemory();
|
|
void LoadRegisters();
|
|
void LoadTextureMemory();
|
|
|
|
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, const u32* data);
|
|
|
|
bool ShouldLoadBP(u8 address);
|
|
bool ShouldLoadXF(u8 address);
|
|
|
|
static bool IsIdleSet();
|
|
static bool IsHighWatermarkSet();
|
|
|
|
bool m_Loop;
|
|
|
|
u32 m_CurrentFrame = 0;
|
|
u32 m_FrameRangeStart = 0;
|
|
u32 m_FrameRangeEnd = 0;
|
|
|
|
u32 m_ObjectRangeStart = 0;
|
|
u32 m_ObjectRangeEnd = 10000;
|
|
|
|
bool m_EarlyMemoryUpdates = false;
|
|
|
|
u64 m_CyclesPerFrame = 0;
|
|
u32 m_ElapsedCycles = 0;
|
|
u32 m_FrameFifoSize = 0;
|
|
|
|
CallbackFunc m_FileLoadedCb = nullptr;
|
|
CallbackFunc m_FrameWrittenCb = nullptr;
|
|
|
|
std::unique_ptr<FifoDataFile> m_File;
|
|
|
|
std::vector<AnalyzedFrameInfo> m_FrameInfo;
|
|
};
|