mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2025-07-08 06:58:07 +00:00
209 lines
6.3 KiB
C++
209 lines
6.3 KiB
C++
// Copyright 2011 Dolphin Emulator Project
|
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
|
|
#pragma once
|
|
|
|
#include <functional>
|
|
#include <memory>
|
|
#include <set>
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
#include "Common/Assert.h"
|
|
#include "Common/Config/Config.h"
|
|
#include "Core/FifoPlayer/FifoDataFile.h"
|
|
#include "Core/PowerPC/CPUCoreBase.h"
|
|
#include "VideoCommon/CPMemory.h"
|
|
#include "VideoCommon/OpcodeDecoding.h"
|
|
|
|
class FifoDataFile;
|
|
struct MemoryUpdate;
|
|
|
|
namespace Core
|
|
{
|
|
class System;
|
|
}
|
|
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;
|
|
|
|
enum class FramePartType
|
|
{
|
|
Commands,
|
|
PrimitiveData,
|
|
EFBCopy,
|
|
};
|
|
|
|
struct FramePart
|
|
{
|
|
constexpr FramePart(FramePartType type, u32 start, u32 end, const CPState& cpmem)
|
|
: m_type(type), m_start(start), m_end(end), m_cpmem(cpmem)
|
|
{
|
|
}
|
|
|
|
const FramePartType m_type;
|
|
const u32 m_start;
|
|
const u32 m_end;
|
|
const CPState m_cpmem;
|
|
};
|
|
|
|
struct AnalyzedFrameInfo
|
|
{
|
|
std::vector<FramePart> parts;
|
|
Common::EnumMap<u32, FramePartType::EFBCopy> part_type_counts;
|
|
|
|
void AddPart(FramePartType type, u32 start, u32 end, const CPState& cpmem)
|
|
{
|
|
parts.emplace_back(type, start, end, cpmem);
|
|
part_type_counts[type]++;
|
|
}
|
|
};
|
|
|
|
class FifoPlayer
|
|
{
|
|
public:
|
|
using CallbackFunc = std::function<void()>;
|
|
|
|
explicit FifoPlayer(Core::System& system);
|
|
FifoPlayer(const FifoPlayer&) = delete;
|
|
FifoPlayer(FifoPlayer&&) = delete;
|
|
FifoPlayer& operator=(const FifoPlayer&) = delete;
|
|
FifoPlayer& operator=(FifoPlayer&&) = delete;
|
|
~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; }
|
|
|
|
// Callbacks
|
|
void SetFileLoadedCallback(CallbackFunc callback);
|
|
void SetFrameWrittenCallback(CallbackFunc callback) { m_FrameWrittenCb = std::move(callback); }
|
|
|
|
bool IsRunningWithFakeVideoInterfaceUpdates() const;
|
|
|
|
private:
|
|
class CPUCore;
|
|
friend class CPUCore;
|
|
|
|
CPU::State AdvanceFrame();
|
|
|
|
void WriteFrame(const FifoFrameInfo& frame, const AnalyzedFrameInfo& info);
|
|
void WriteFramePart(const FramePart& part, u32* next_mem_update, const FifoFrameInfo& frame);
|
|
|
|
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 ClearEfb();
|
|
|
|
void WriteCP(u32 address, u16 value);
|
|
void WritePI(u32 address, u32 value);
|
|
|
|
void FlushWGP();
|
|
void WaitForGPUInactive();
|
|
|
|
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);
|
|
|
|
bool IsIdleSet() const;
|
|
bool IsHighWatermarkSet() const;
|
|
|
|
void RefreshConfig();
|
|
|
|
Core::System& m_system;
|
|
|
|
bool m_Loop = true;
|
|
// If enabled then all memory updates happen at once before the first frame
|
|
bool m_EarlyMemoryUpdates = false;
|
|
|
|
u32 m_CurrentFrame = 0;
|
|
u32 m_FrameRangeStart = 0;
|
|
u32 m_FrameRangeEnd = 0;
|
|
|
|
u32 m_ObjectRangeStart = 0;
|
|
u32 m_ObjectRangeEnd = 10000;
|
|
|
|
u64 m_CyclesPerFrame = 0;
|
|
u32 m_ElapsedCycles = 0;
|
|
u32 m_FrameFifoSize = 0;
|
|
|
|
CallbackFunc m_FileLoadedCb = nullptr;
|
|
CallbackFunc m_FrameWrittenCb = nullptr;
|
|
Config::ConfigChangedCallbackID m_config_changed_callback_id;
|
|
|
|
std::unique_ptr<FifoDataFile> m_File;
|
|
|
|
std::vector<AnalyzedFrameInfo> m_FrameInfo;
|
|
};
|