Merge pull request #9540 from Pokechu22/better-fifo-analyzer-part-2

Fifo analyzer quality of life improvements
This commit is contained in:
Mat M 2021-05-14 15:51:53 -04:00 committed by GitHub
commit d74a1068b8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
28 changed files with 918 additions and 816 deletions

View File

@ -258,37 +258,37 @@ u32 AnalyzeCommand(const u8* data, DecodeMode mode)
void LoadCPReg(u32 subCmd, u32 value, CPMemory& cpMem)
{
switch (subCmd & 0xF0)
switch (subCmd & CP_COMMAND_MASK)
{
case 0x50:
case VCD_LO:
cpMem.vtxDesc.low.Hex = value;
break;
case 0x60:
case VCD_HI:
cpMem.vtxDesc.high.Hex = value;
break;
case 0x70:
ASSERT((subCmd & 0x0F) < 8);
cpMem.vtxAttr[subCmd & 7].g0.Hex = value;
case CP_VAT_REG_A:
ASSERT(subCmd - CP_VAT_REG_A < CP_NUM_VAT_REG);
cpMem.vtxAttr[subCmd & CP_VAT_MASK].g0.Hex = value;
break;
case 0x80:
ASSERT((subCmd & 0x0F) < 8);
cpMem.vtxAttr[subCmd & 7].g1.Hex = value;
case CP_VAT_REG_B:
ASSERT(subCmd - CP_VAT_REG_B < CP_NUM_VAT_REG);
cpMem.vtxAttr[subCmd & CP_VAT_MASK].g1.Hex = value;
break;
case 0x90:
ASSERT((subCmd & 0x0F) < 8);
cpMem.vtxAttr[subCmd & 7].g2.Hex = value;
case CP_VAT_REG_C:
ASSERT(subCmd - CP_VAT_REG_C < CP_NUM_VAT_REG);
cpMem.vtxAttr[subCmd & CP_VAT_MASK].g2.Hex = value;
break;
case 0xA0:
cpMem.arrayBases[subCmd & 0xF] = value;
case ARRAY_BASE:
cpMem.arrayBases[subCmd & CP_ARRAY_MASK] = value;
break;
case 0xB0:
cpMem.arrayStrides[subCmd & 0xF] = value & 0xFF;
case ARRAY_STRIDE:
cpMem.arrayStrides[subCmd & CP_ARRAY_MASK] = value & 0xFF;
break;
}
}

View File

@ -22,9 +22,9 @@ u32 AnalyzeCommand(const u8* data, DecodeMode mode);
struct CPMemory
{
TVtxDesc vtxDesc;
std::array<VAT, 8> vtxAttr;
std::array<u32, 16> arrayBases;
std::array<u32, 16> arrayStrides;
std::array<VAT, CP_NUM_VAT_REG> vtxAttr;
std::array<u32, CP_NUM_ARRAYS> arrayBases;
std::array<u32, CP_NUM_ARRAYS> arrayStrides;
};
void LoadCPReg(u32 subCmd, u32 value, CPMemory& cpMem);

View File

@ -6,6 +6,7 @@
#include <vector>
#include "Common/Assert.h"
#include "Common/CommonTypes.h"
#include "Core/FifoPlayer/FifoAnalyzer.h"
#include "Core/FifoPlayer/FifoDataFile.h"
@ -25,14 +26,14 @@ void FifoPlaybackAnalyzer::AnalyzeFrames(FifoDataFile* file,
std::vector<AnalyzedFrameInfo>& frameInfo)
{
u32* cpMem = file->GetCPMem();
FifoAnalyzer::LoadCPReg(0x50, cpMem[0x50], s_CpMem);
FifoAnalyzer::LoadCPReg(0x60, cpMem[0x60], s_CpMem);
FifoAnalyzer::LoadCPReg(VCD_LO, cpMem[VCD_LO], s_CpMem);
FifoAnalyzer::LoadCPReg(VCD_HI, cpMem[VCD_HI], s_CpMem);
for (int i = 0; i < 8; ++i)
for (u32 i = 0; i < CP_NUM_VAT_REG; ++i)
{
FifoAnalyzer::LoadCPReg(0x70 + i, cpMem[0x70 + i], s_CpMem);
FifoAnalyzer::LoadCPReg(0x80 + i, cpMem[0x80 + i], s_CpMem);
FifoAnalyzer::LoadCPReg(0x90 + i, cpMem[0x90 + i], s_CpMem);
FifoAnalyzer::LoadCPReg(CP_VAT_REG_A + i, cpMem[CP_VAT_REG_A + i], s_CpMem);
FifoAnalyzer::LoadCPReg(CP_VAT_REG_B + i, cpMem[CP_VAT_REG_B + i], s_CpMem);
FifoAnalyzer::LoadCPReg(CP_VAT_REG_C + i, cpMem[CP_VAT_REG_C + i], s_CpMem);
}
frameInfo.clear();
@ -80,6 +81,7 @@ void FifoPlaybackAnalyzer::AnalyzeFrames(FifoDataFile* file,
{
// Clean up frame analysis
analyzed.objectStarts.clear();
analyzed.objectCPStates.clear();
analyzed.objectEnds.clear();
return;
@ -88,15 +90,23 @@ void FifoPlaybackAnalyzer::AnalyzeFrames(FifoDataFile* file,
if (wasDrawing != s_DrawingObject)
{
if (s_DrawingObject)
{
analyzed.objectStarts.push_back(cmdStart);
analyzed.objectCPStates.push_back(s_CpMem);
}
else
{
analyzed.objectEnds.push_back(cmdStart);
}
}
cmdStart += cmdSize;
}
if (analyzed.objectEnds.size() < analyzed.objectStarts.size())
analyzed.objectEnds.push_back(cmdStart);
ASSERT(analyzed.objectStarts.size() == analyzed.objectCPStates.size());
ASSERT(analyzed.objectStarts.size() == analyzed.objectEnds.size());
}
}

View File

@ -7,11 +7,15 @@
#include <string>
#include <vector>
#include "Core/FifoPlayer/FifoAnalyzer.h"
#include "Core/FifoPlayer/FifoDataFile.h"
struct AnalyzedFrameInfo
{
// Start of the primitives for the object (after previous update commands)
std::vector<u32> objectStarts;
std::vector<FifoAnalyzer::CPMemory> objectCPStates;
// End of the primitives for the object
std::vector<u32> objectEnds;
std::vector<MemoryUpdate> memoryUpdates;
};

View File

@ -51,7 +51,7 @@ bool FifoPlayer::Open(const std::string& filename)
{
FifoPlaybackAnalyzer::AnalyzeFrames(m_File.get(), m_FrameInfo);
m_FrameRangeEnd = m_File->GetFrameCount();
m_FrameRangeEnd = m_File->GetFrameCount() - 1;
}
if (m_FileLoadedCb)
@ -131,13 +131,10 @@ private:
CPU::State FifoPlayer::AdvanceFrame()
{
if (m_CurrentFrame >= m_FrameRangeEnd)
if (m_CurrentFrame > m_FrameRangeEnd)
{
if (!m_Loop)
return CPU::State::PowerDown;
// If there are zero frames in the range then sleep instead of busy spinning
if (m_FrameRangeStart >= m_FrameRangeEnd)
return CPU::State::Stepping;
// When looping, reload the contents of all the BP/CP/CF registers.
// This ensures that each time the first frame is played back, the state of the
@ -189,23 +186,40 @@ bool FifoPlayer::IsRunningWithFakeVideoInterfaceUpdates() const
return m_File->ShouldGenerateFakeVIUpdates();
}
u32 FifoPlayer::GetFrameObjectCount() const
u32 FifoPlayer::GetMaxObjectCount() const
{
if (m_CurrentFrame < m_FrameInfo.size())
u32 result = 0;
for (auto& frame : m_FrameInfo)
{
return (u32)(m_FrameInfo[m_CurrentFrame].objectStarts.size());
const u32 count = static_cast<u32>(frame.objectStarts.size());
if (count > result)
result = count;
}
return result;
}
u32 FifoPlayer::GetFrameObjectCount(u32 frame) const
{
if (frame < m_FrameInfo.size())
{
return static_cast<u32>(m_FrameInfo[frame].objectStarts.size());
}
return 0;
}
u32 FifoPlayer::GetCurrentFrameObjectCount() const
{
return GetFrameObjectCount(m_CurrentFrame);
}
void FifoPlayer::SetFrameRangeStart(u32 start)
{
if (m_File)
{
u32 frameCount = m_File->GetFrameCount();
if (start > frameCount)
start = frameCount;
const u32 lastFrame = m_File->GetFrameCount() - 1;
if (start > lastFrame)
start = lastFrame;
m_FrameRangeStart = start;
if (m_FrameRangeEnd < start)
@ -220,9 +234,9 @@ void FifoPlayer::SetFrameRangeEnd(u32 end)
{
if (m_File)
{
u32 frameCount = m_File->GetFrameCount();
if (end > frameCount)
end = frameCount;
const u32 lastFrame = m_File->GetFrameCount() - 1;
if (end > lastFrame)
end = lastFrame;
m_FrameRangeEnd = end;
if (m_FrameRangeStart > end)
@ -375,6 +389,8 @@ void FifoPlayer::WriteFifo(const u8* data, u32 start, u32 end)
{
while (IsHighWatermarkSet())
{
if (CPU::GetState() != CPU::State::Running)
break;
CoreTiming::Idle();
CoreTiming::Advance();
}

View File

@ -74,7 +74,9 @@ public:
bool IsPlaying() const;
FifoDataFile* GetFile() const { return m_File.get(); }
u32 GetFrameObjectCount() const;
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

View File

@ -17,16 +17,16 @@ void FifoRecordAnalyzer::Initialize(const u32* cpMem)
{
s_DrawingObject = false;
FifoAnalyzer::LoadCPReg(0x50, *(cpMem + 0x50), s_CpMem);
FifoAnalyzer::LoadCPReg(0x60, *(cpMem + 0x60), s_CpMem);
for (int i = 0; i < 8; ++i)
FifoAnalyzer::LoadCPReg(0x70 + i, *(cpMem + 0x70 + i), s_CpMem);
FifoAnalyzer::LoadCPReg(VCD_LO, cpMem[VCD_LO], s_CpMem);
FifoAnalyzer::LoadCPReg(VCD_HI, cpMem[VCD_HI], s_CpMem);
for (u32 i = 0; i < CP_NUM_VAT_REG; ++i)
FifoAnalyzer::LoadCPReg(CP_VAT_REG_A + i, cpMem[CP_VAT_REG_A + i], s_CpMem);
const u32* const bases_start = cpMem + 0xA0;
const u32* const bases_start = cpMem + ARRAY_BASE;
const u32* const bases_end = bases_start + s_CpMem.arrayBases.size();
std::copy(bases_start, bases_end, s_CpMem.arrayBases.begin());
const u32* const strides_start = cpMem + 0xB0;
const u32* const strides_start = cpMem + ARRAY_STRIDE;
const u32* const strides_end = strides_start + s_CpMem.arrayStrides.size();
std::copy(strides_start, strides_end, s_CpMem.arrayStrides.begin());
}

View File

@ -25,6 +25,7 @@
#include "VideoCommon/BPMemory.h"
#include "VideoCommon/CPMemory.h"
#include "VideoCommon/OpcodeDecoding.h"
#include "VideoCommon/VertexLoaderBase.h"
#include "VideoCommon/XFStructs.h"
constexpr int FRAME_ROLE = Qt::UserRole;
@ -81,6 +82,9 @@ void FIFOAnalyzer::CreateWidgets()
m_search_previous = new QPushButton(tr("Previous Match"));
m_search_label = new QLabel;
m_search_next->setEnabled(false);
m_search_previous->setEnabled(false);
auto* box_layout = new QHBoxLayout;
box_layout->addWidget(m_search_edit);
@ -111,6 +115,7 @@ void FIFOAnalyzer::ConnectWidgets()
connect(m_detail_list, &QListWidget::itemSelectionChanged, this,
&FIFOAnalyzer::UpdateDescription);
connect(m_search_edit, &QLineEdit::returnPressed, this, &FIFOAnalyzer::BeginSearch);
connect(m_search_new, &QPushButton::clicked, this, &FIFOAnalyzer::BeginSearch);
connect(m_search_next, &QPushButton::clicked, this, &FIFOAnalyzer::FindNext);
connect(m_search_previous, &QPushButton::clicked, this, &FIFOAnalyzer::FindPrevious);
@ -139,78 +144,102 @@ void FIFOAnalyzer::UpdateTree()
auto* file = FifoPlayer::GetInstance().GetFile();
int object_count = FifoPlayer::GetInstance().GetFrameObjectCount();
int frame_count = file->GetFrameCount();
for (int i = 0; i < frame_count; i++)
const u32 frame_count = file->GetFrameCount();
for (u32 frame = 0; frame < frame_count; frame++)
{
auto* frame_item = new QTreeWidgetItem({tr("Frame %1").arg(i)});
auto* frame_item = new QTreeWidgetItem({tr("Frame %1").arg(frame)});
recording_item->addChild(frame_item);
for (int j = 0; j < object_count; j++)
const u32 object_count = FifoPlayer::GetInstance().GetFrameObjectCount(frame);
for (u32 object = 0; object < object_count; object++)
{
auto* object_item = new QTreeWidgetItem({tr("Object %1").arg(j)});
auto* object_item = new QTreeWidgetItem({tr("Object %1").arg(object)});
frame_item->addChild(object_item);
object_item->setData(0, FRAME_ROLE, i);
object_item->setData(0, OBJECT_ROLE, j);
object_item->setData(0, FRAME_ROLE, frame);
object_item->setData(0, OBJECT_ROLE, object);
}
}
}
static std::string GetPrimitiveName(u8 cmd)
{
if ((cmd & 0xC0) != 0x80)
{
PanicAlertFmt("Not a primitive command: {:#04x}", cmd);
return "";
}
const u8 vat = cmd & OpcodeDecoder::GX_VAT_MASK; // Vertex loader index (0 - 7)
const u8 primitive =
(cmd & OpcodeDecoder::GX_PRIMITIVE_MASK) >> OpcodeDecoder::GX_PRIMITIVE_SHIFT;
static constexpr std::array<const char*, 8> names = {
"GX_DRAW_QUADS", "GX_DRAW_QUADS_2 (nonstandard)",
"GX_DRAW_TRIANGLES", "GX_DRAW_TRIANGLE_STRIP",
"GX_DRAW_TRIANGLE_FAN", "GX_DRAW_LINES",
"GX_DRAW_LINE_STRIP", "GX_DRAW_POINTS",
};
return fmt::format("{} VAT {}", names[primitive], vat);
}
void FIFOAnalyzer::UpdateDetails()
{
m_detail_list->clear();
// Clearing the detail list can update the selection, which causes UpdateDescription to be called
// immediately. However, the object data offsets have not been recalculated yet, which can cause
// the wrong data to be used, potentially leading to out of bounds data or other bad things.
// Clear m_object_data_offsets first, so that UpdateDescription exits immediately.
m_object_data_offsets.clear();
m_detail_list->clear();
m_search_results.clear();
m_search_next->setEnabled(false);
m_search_previous->setEnabled(false);
m_search_label->clear();
auto items = m_tree_widget->selectedItems();
if (!FifoPlayer::GetInstance().IsPlaying())
return;
const auto items = m_tree_widget->selectedItems();
if (items.isEmpty() || items[0]->data(0, OBJECT_ROLE).isNull())
return;
int frame_nr = items[0]->data(0, FRAME_ROLE).toInt();
int object_nr = items[0]->data(0, OBJECT_ROLE).toInt();
const u32 frame_nr = items[0]->data(0, FRAME_ROLE).toUInt();
const u32 object_nr = items[0]->data(0, OBJECT_ROLE).toUInt();
const auto& frame_info = FifoPlayer::GetInstance().GetAnalyzedFrameInfo(frame_nr);
const auto& fifo_frame = FifoPlayer::GetInstance().GetFile()->GetFrame(frame_nr);
const u8* objectdata_start = &fifo_frame.fifoData[frame_info.objectStarts[object_nr]];
const u8* objectdata_end = &fifo_frame.fifoData[frame_info.objectEnds[object_nr]];
const u8* objectdata = objectdata_start;
const std::ptrdiff_t obj_offset =
objectdata_start - &fifo_frame.fifoData[frame_info.objectStarts[0]];
// Note that frame_info.objectStarts[object_nr] is the start of the primitive data,
// but we want to start with the register updates which happen before that.
const u32 object_start = (object_nr == 0 ? 0 : frame_info.objectEnds[object_nr - 1]);
const u32 object_size = frame_info.objectEnds[object_nr] - object_start;
int cmd = *objectdata++;
int stream_size = Common::swap16(objectdata);
objectdata += 2;
QString new_label = QStringLiteral("%1: %2 %3 ")
.arg(obj_offset, 8, 16, QLatin1Char('0'))
.arg(cmd, 2, 16, QLatin1Char('0'))
.arg(stream_size, 4, 16, QLatin1Char('0'));
if (stream_size && ((objectdata_end - objectdata) % stream_size))
new_label += tr("NOTE: Stream size doesn't match actual data length\n");
const u8* const object = &fifo_frame.fifoData[object_start];
while (objectdata < objectdata_end)
new_label += QStringLiteral("%1").arg(*objectdata++, 2, 16, QLatin1Char('0'));
m_detail_list->addItem(new_label);
m_object_data_offsets.push_back(0);
// Between objectdata_end and next_objdata_start, there are register setting commands
if (object_nr + 1 < static_cast<int>(frame_info.objectStarts.size()))
u32 object_offset = 0;
while (object_offset < object_size)
{
const u8* next_objdata_start = &fifo_frame.fifoData[frame_info.objectStarts[object_nr + 1]];
while (objectdata < next_objdata_start)
{
m_object_data_offsets.push_back(objectdata - objectdata_start);
int new_offset = objectdata - &fifo_frame.fifoData[frame_info.objectStarts[0]];
int command = *objectdata++;
QString new_label;
const u32 start_offset = object_offset;
m_object_data_offsets.push_back(start_offset);
const u8 command = object[object_offset++];
switch (command)
{
case OpcodeDecoder::GX_NOP:
if (object[object_offset] == OpcodeDecoder::GX_NOP)
{
u32 nop_count = 2;
while (object[++object_offset] == OpcodeDecoder::GX_NOP)
nop_count++;
new_label = QStringLiteral("NOP (%1x)").arg(nop_count);
}
else
{
new_label = QStringLiteral("NOP");
}
break;
case OpcodeDecoder::GX_CMD_UNKNOWN_METRICS:
@ -223,9 +252,10 @@ void FIFOAnalyzer::UpdateDetails()
case OpcodeDecoder::GX_LOAD_CP_REG:
{
u32 cmd2 = *objectdata++;
u32 value = Common::swap32(objectdata);
objectdata += 4;
const u8 cmd2 = object[object_offset++];
const u32 value = Common::swap32(&object[object_offset]);
object_offset += 4;
const auto [name, desc] = GetCPRegInfo(cmd2, value);
ASSERT(!name.empty());
@ -238,23 +268,21 @@ void FIFOAnalyzer::UpdateDetails()
case OpcodeDecoder::GX_LOAD_XF_REG:
{
const auto [name, desc] = GetXFTransferInfo(objectdata);
u32 cmd2 = Common::swap32(objectdata);
objectdata += 4;
const auto [name, desc] = GetXFTransferInfo(&object[object_offset]);
const u32 cmd2 = Common::swap32(&object[object_offset]);
object_offset += 4;
ASSERT(!name.empty());
u8 streamSize = ((cmd2 >> 16) & 15) + 1;
const u8* stream_start = objectdata;
const u8* stream_end = stream_start + streamSize * 4;
const u8 stream_size = ((cmd2 >> 16) & 15) + 1;
new_label = QStringLiteral("XF %1 ").arg(cmd2, 8, 16, QLatin1Char('0'));
while (objectdata < stream_end)
{
new_label += QStringLiteral("%1").arg(*objectdata++, 2, 16, QLatin1Char('0'));
if (((objectdata - stream_start) % 4) == 0)
new_label += QLatin1Char(' ');
for (u8 i = 0; i < stream_size; i++)
{
const u32 value = Common::swap32(&object[object_offset]);
object_offset += 4;
new_label += QStringLiteral("%1 ").arg(value, 8, 16, QLatin1Char('0'));
}
new_label += QStringLiteral(" ") + QString::fromStdString(name);
@ -262,17 +290,35 @@ void FIFOAnalyzer::UpdateDetails()
break;
case OpcodeDecoder::GX_LOAD_INDX_A:
{
const auto [desc, written] =
GetXFIndexedLoadInfo(ARRAY_XF_A, Common::swap32(&object[object_offset]));
object_offset += 4;
new_label = QStringLiteral("LOAD INDX A %1").arg(QString::fromStdString(desc));
}
break;
case OpcodeDecoder::GX_LOAD_INDX_B:
{
const auto [desc, written] =
GetXFIndexedLoadInfo(ARRAY_XF_B, Common::swap32(&object[object_offset]));
object_offset += 4;
new_label = QStringLiteral("LOAD INDX B %1").arg(QString::fromStdString(desc));
}
break;
case OpcodeDecoder::GX_LOAD_INDX_C:
{
const auto [desc, written] =
GetXFIndexedLoadInfo(ARRAY_XF_C, Common::swap32(&object[object_offset]));
object_offset += 4;
new_label = QStringLiteral("LOAD INDX C %1").arg(QString::fromStdString(desc));
}
break;
case OpcodeDecoder::GX_LOAD_INDX_D:
{
objectdata += 4;
new_label = (command == OpcodeDecoder::GX_LOAD_INDX_A) ?
QStringLiteral("LOAD INDX A") :
(command == OpcodeDecoder::GX_LOAD_INDX_B) ?
QStringLiteral("LOAD INDX B") :
(command == OpcodeDecoder::GX_LOAD_INDX_C) ? QStringLiteral("LOAD INDX C") :
QStringLiteral("LOAD INDX D");
const auto [desc, written] =
GetXFIndexedLoadInfo(ARRAY_XF_D, Common::swap32(&object[object_offset]));
object_offset += 4;
new_label = QStringLiteral("LOAD INDX D %1").arg(QString::fromStdString(desc));
}
break;
@ -281,15 +327,15 @@ void FIFOAnalyzer::UpdateDetails()
// call to start them
// That is done to make it easier to track where memory is updated
ASSERT(false);
objectdata += 8;
object_offset += 8;
new_label = QStringLiteral("CALL DL");
break;
case OpcodeDecoder::GX_LOAD_BP_REG:
{
const u8 cmd2 = *objectdata++;
const u32 cmddata = Common::swap24(objectdata);
objectdata += 3;
const u8 cmd2 = object[object_offset++];
const u32 cmddata = Common::swap24(&object[object_offset]);
object_offset += 3;
const auto [name, desc] = GetBPRegInfo(cmd2, cmddata);
ASSERT(!name.empty());
@ -302,26 +348,73 @@ void FIFOAnalyzer::UpdateDetails()
break;
default:
new_label = tr("Unexpected 0x80 call? Aborting...");
objectdata = static_cast<const u8*>(next_objdata_start);
if ((command & 0xC0) == 0x80)
{
// Object primitive data
const u8 vat = command & OpcodeDecoder::GX_VAT_MASK;
const auto& vtx_desc = frame_info.objectCPStates[object_nr].vtxDesc;
const auto& vtx_attr = frame_info.objectCPStates[object_nr].vtxAttr[vat];
const auto name = GetPrimitiveName(command);
const u16 vertex_count = Common::swap16(&object[object_offset]);
object_offset += 2;
const u32 vertex_size = VertexLoaderBase::GetVertexSize(vtx_desc, vtx_attr);
// Note that vertex_count is allowed to be 0, with no special treatment
// (another command just comes right after the current command, with no vertices in between)
const u32 object_prim_size = vertex_count * vertex_size;
new_label = QStringLiteral("PRIMITIVE %1 (%2) %3 vertices %4 bytes/vertex %5 total bytes")
.arg(QString::fromStdString(name))
.arg(command, 2, 16, QLatin1Char('0'))
.arg(vertex_count)
.arg(vertex_size)
.arg(object_prim_size);
// It's not really useful to have a massive unreadable hex string for the object primitives.
// Put it in the description instead.
// #define INCLUDE_HEX_IN_PRIMITIVES
#ifdef INCLUDE_HEX_IN_PRIMITIVES
new_label += QStringLiteral(" ");
for (u32 i = 0; i < object_prim_size; i++)
{
new_label += QStringLiteral("%1").arg(object[object_offset++], 2, 16, QLatin1Char('0'));
}
#else
object_offset += object_prim_size;
#endif
}
else
{
new_label = QStringLiteral("Unknown opcode %1").arg(command, 2, 16);
}
break;
}
new_label = QStringLiteral("%1: ").arg(new_offset, 8, 16, QLatin1Char('0')) + new_label;
new_label = QStringLiteral("%1: ").arg(object_start + start_offset, 8, 16, QLatin1Char('0')) +
new_label;
m_detail_list->addItem(new_label);
}
}
ASSERT(object_offset == object_size);
// Needed to ensure the description updates when changing objects
m_detail_list->setCurrentRow(0);
}
void FIFOAnalyzer::BeginSearch()
{
QString search_str = m_search_edit->text();
const QString search_str = m_search_edit->text();
auto items = m_tree_widget->selectedItems();
if (items.isEmpty() || items[0]->data(0, FRAME_ROLE).isNull())
if (!FifoPlayer::GetInstance().IsPlaying())
return;
if (items[0]->data(0, OBJECT_ROLE).isNull())
const auto items = m_tree_widget->selectedItems();
if (items.isEmpty() || items[0]->data(0, FRAME_ROLE).isNull() ||
items[0]->data(0, OBJECT_ROLE).isNull())
{
m_search_label->setText(tr("Invalid search parameters (no object selected)"));
return;
@ -356,37 +449,36 @@ void FIFOAnalyzer::BeginSearch()
m_search_results.clear();
int frame_nr = items[0]->data(0, FRAME_ROLE).toInt();
int object_nr = items[0]->data(0, OBJECT_ROLE).toInt();
const u32 frame_nr = items[0]->data(0, FRAME_ROLE).toUInt();
const u32 object_nr = items[0]->data(0, OBJECT_ROLE).toUInt();
const AnalyzedFrameInfo& frame_info = FifoPlayer::GetInstance().GetAnalyzedFrameInfo(frame_nr);
const FifoFrameInfo& fifo_frame = FifoPlayer::GetInstance().GetFile()->GetFrame(frame_nr);
// TODO: Support searching through the last object...how do we know where the cmd data ends?
const u32 object_start = (object_nr == 0 ? 0 : frame_info.objectEnds[object_nr - 1]);
const u32 object_size = frame_info.objectEnds[object_nr] - object_start;
const u8* const object = &fifo_frame.fifoData[object_start];
// TODO: Support searching for bit patterns
for (u32 cmd_nr = 0; cmd_nr < m_object_data_offsets.size(); cmd_nr++)
{
const u32 cmd_start = m_object_data_offsets[cmd_nr];
const u32 cmd_end = (cmd_nr + 1 == m_object_data_offsets.size()) ?
object_size :
m_object_data_offsets[cmd_nr + 1];
const auto* start_ptr = &fifo_frame.fifoData[frame_info.objectStarts[object_nr]];
const auto* end_ptr = &fifo_frame.fifoData[frame_info.objectStarts[object_nr + 1]];
const u8* const cmd_start_ptr = &object[cmd_start];
const u8* const cmd_end_ptr = &object[cmd_end];
for (const u8* ptr = start_ptr; ptr < end_ptr - length + 1; ++ptr)
for (const u8* ptr = cmd_start_ptr; ptr < cmd_end_ptr - length + 1; ptr++)
{
if (std::equal(search_val.begin(), search_val.end(), ptr))
{
SearchResult result;
result.frame = frame_nr;
result.object = object_nr;
result.cmd = 0;
for (unsigned int cmd_nr = 1; cmd_nr < m_object_data_offsets.size(); ++cmd_nr)
{
if (ptr < start_ptr + m_object_data_offsets[cmd_nr])
{
result.cmd = cmd_nr - 1;
m_search_results.emplace_back(frame_nr, object_nr, cmd_nr);
break;
}
}
m_search_results.push_back(result);
}
}
ShowSearchResult(0);
@ -397,41 +489,29 @@ void FIFOAnalyzer::BeginSearch()
void FIFOAnalyzer::FindNext()
{
int index = m_detail_list->currentRow();
const int index = m_detail_list->currentRow();
ASSERT(index >= 0);
if (index == -1)
auto next_result =
std::find_if(m_search_results.begin(), m_search_results.end(),
[index](auto& result) { return result.m_cmd > static_cast<u32>(index); });
if (next_result != m_search_results.end())
{
ShowSearchResult(0);
return;
}
for (auto it = m_search_results.begin(); it != m_search_results.end(); ++it)
{
if (it->cmd > index)
{
ShowSearchResult(it - m_search_results.begin());
return;
}
ShowSearchResult(next_result - m_search_results.begin());
}
}
void FIFOAnalyzer::FindPrevious()
{
int index = m_detail_list->currentRow();
const int index = m_detail_list->currentRow();
ASSERT(index >= 0);
if (index == -1)
auto prev_result =
std::find_if(m_search_results.rbegin(), m_search_results.rend(),
[index](auto& result) { return result.m_cmd < static_cast<u32>(index); });
if (prev_result != m_search_results.rend())
{
ShowSearchResult(m_search_results.size() - 1);
return;
}
for (auto it = m_search_results.rbegin(); it != m_search_results.rend(); ++it)
{
if (it->cmd < index)
{
ShowSearchResult(m_search_results.size() - 1 - (it - m_search_results.rbegin()));
return;
}
ShowSearchResult((m_search_results.rend() - prev_result) - 1);
}
}
@ -440,7 +520,7 @@ void FIFOAnalyzer::ShowSearchResult(size_t index)
if (m_search_results.empty())
return;
if (index > m_search_results.size())
if (index >= m_search_results.size())
{
ShowSearchResult(m_search_results.size() - 1);
return;
@ -449,10 +529,10 @@ void FIFOAnalyzer::ShowSearchResult(size_t index)
const auto& result = m_search_results[index];
QTreeWidgetItem* object_item =
m_tree_widget->topLevelItem(0)->child(result.frame)->child(result.object);
m_tree_widget->topLevelItem(0)->child(result.m_frame)->child(result.m_object);
m_tree_widget->setCurrentItem(object_item);
m_detail_list->setCurrentRow(result.cmd);
m_detail_list->setCurrentRow(result.m_cmd);
m_search_next->setEnabled(index + 1 < m_search_results.size());
m_search_previous->setEnabled(index > 0);
@ -462,20 +542,28 @@ void FIFOAnalyzer::UpdateDescription()
{
m_entry_detail_browser->clear();
auto items = m_tree_widget->selectedItems();
if (items.isEmpty())
if (!FifoPlayer::GetInstance().IsPlaying())
return;
int frame_nr = items[0]->data(0, FRAME_ROLE).toInt();
int object_nr = items[0]->data(0, OBJECT_ROLE).toInt();
int entry_nr = m_detail_list->currentRow();
const auto items = m_tree_widget->selectedItems();
const AnalyzedFrameInfo& frame = FifoPlayer::GetInstance().GetAnalyzedFrameInfo(frame_nr);
if (items.isEmpty() || m_object_data_offsets.empty())
return;
if (items[0]->data(0, FRAME_ROLE).isNull() || items[0]->data(0, OBJECT_ROLE).isNull())
return;
const u32 frame_nr = items[0]->data(0, FRAME_ROLE).toUInt();
const u32 object_nr = items[0]->data(0, OBJECT_ROLE).toUInt();
const u32 entry_nr = m_detail_list->currentRow();
const AnalyzedFrameInfo& frame_info = FifoPlayer::GetInstance().GetAnalyzedFrameInfo(frame_nr);
const FifoFrameInfo& fifo_frame = FifoPlayer::GetInstance().GetFile()->GetFrame(frame_nr);
const u8* cmddata =
&fifo_frame.fifoData[frame.objectStarts[object_nr]] + m_object_data_offsets[entry_nr];
const u32 object_start = (object_nr == 0 ? 0 : frame_info.objectEnds[object_nr - 1]);
const u32 entry_start = m_object_data_offsets[entry_nr];
const u8* cmddata = &fifo_frame.fifoData[object_start + entry_start];
// TODO: Not sure whether we should bother translating the descriptions
@ -528,6 +616,74 @@ void FIFOAnalyzer::UpdateDescription()
else
text += QString::fromStdString(desc);
}
else if (*cmddata == OpcodeDecoder::GX_LOAD_INDX_A)
{
const auto [desc, written] = GetXFIndexedLoadInfo(ARRAY_XF_A, Common::swap32(cmddata + 1));
text = QString::fromStdString(desc);
text += QLatin1Char{'\n'};
text += tr("Usually used for position matrices");
text += QLatin1Char{'\n'};
text += QString::fromStdString(written);
}
else if (*cmddata == OpcodeDecoder::GX_LOAD_INDX_B)
{
const auto [desc, written] = GetXFIndexedLoadInfo(ARRAY_XF_B, Common::swap32(cmddata + 1));
text = QString::fromStdString(desc);
text += QLatin1Char{'\n'};
text += tr("Usually used for normal matrices");
text += QLatin1Char{'\n'};
text += QString::fromStdString(written);
}
else if (*cmddata == OpcodeDecoder::GX_LOAD_INDX_C)
{
const auto [desc, written] = GetXFIndexedLoadInfo(ARRAY_XF_C, Common::swap32(cmddata + 1));
text = QString::fromStdString(desc);
text += QLatin1Char{'\n'};
text += tr("Usually used for tex coord matrices");
text += QLatin1Char{'\n'};
text += QString::fromStdString(written);
}
else if (*cmddata == OpcodeDecoder::GX_LOAD_INDX_D)
{
const auto [desc, written] = GetXFIndexedLoadInfo(ARRAY_XF_D, Common::swap32(cmddata + 1));
text = QString::fromStdString(desc);
text += QLatin1Char{'\n'};
text += tr("Usually used for light objects");
text += QLatin1Char{'\n'};
text += QString::fromStdString(written);
}
else if ((*cmddata & 0xC0) == 0x80)
{
const u8 vat = *cmddata & OpcodeDecoder::GX_VAT_MASK;
const auto name = GetPrimitiveName(*cmddata);
const u16 vertex_count = Common::swap16(cmddata + 1);
text = tr("Primitive ");
text += QString::fromStdString(name);
text += QLatin1Char{'\n'};
const auto& vtx_desc = frame_info.objectCPStates[object_nr].vtxDesc;
const auto& vtx_attr = frame_info.objectCPStates[object_nr].vtxAttr[vat];
const auto component_sizes = VertexLoaderBase::GetVertexComponentSizes(vtx_desc, vtx_attr);
u32 i = 3;
for (u32 vertex_num = 0; vertex_num < vertex_count; vertex_num++)
{
text += QLatin1Char{'\n'};
for (u32 comp_size : component_sizes)
{
for (u32 comp_off = 0; comp_off < comp_size; comp_off++)
{
text += QStringLiteral("%1").arg(cmddata[i++], 2, 16, QLatin1Char('0'));
}
text += QLatin1Char{' '};
}
}
}
else
{
text = tr("No description available");

View File

@ -8,6 +8,8 @@
#include <QWidget>
#include "Common/CommonTypes.h"
class QGroupBox;
class QLabel;
class QLineEdit;
@ -57,9 +59,13 @@ private:
struct SearchResult
{
int frame;
int object;
int cmd;
constexpr SearchResult(u32 frame, u32 object, u32 cmd)
: m_frame(frame), m_object(object), m_cmd(cmd)
{
}
const u32 m_frame;
const u32 m_object;
const u32 m_cmd;
};
std::vector<int> m_object_data_offsets;

View File

@ -6,9 +6,13 @@
#include <QCheckBox>
#include <QDialogButtonBox>
#include <QEvent>
#include <QFileDialog>
#include <QGroupBox>
#include <QHBoxLayout>
#include <QIcon>
#include <QKeyEvent>
#include <QKeySequence>
#include <QLabel>
#include <QPushButton>
#include <QSpinBox>
@ -26,12 +30,13 @@
#include "DolphinQt/FIFO/FIFOAnalyzer.h"
#include "DolphinQt/QtUtils/ModalMessageBox.h"
#include "DolphinQt/QtUtils/QueueOnObject.h"
#include "DolphinQt/Resources.h"
#include "DolphinQt/Settings.h"
FIFOPlayerWindow::FIFOPlayerWindow(QWidget* parent) : QDialog(parent)
FIFOPlayerWindow::FIFOPlayerWindow(QWidget* parent) : QWidget(parent)
{
setWindowTitle(tr("FIFO Player"));
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
setWindowIcon(Resources::GetAppIcon());
CreateWidgets();
ConnectWidgets();
@ -50,11 +55,14 @@ FIFOPlayerWindow::FIFOPlayerWindow(QWidget* parent) : QDialog(parent)
});
connect(&Settings::Instance(), &Settings::EmulationStateChanged, this, [this](Core::State state) {
if (state == Core::State::Running)
if (state == Core::State::Running && m_emu_state != Core::State::Paused)
OnEmulationStarted();
else if (state == Core::State::Uninitialized)
OnEmulationStopped();
m_emu_state = state;
});
installEventFilter(this);
}
FIFOPlayerWindow::~FIFOPlayerWindow()
@ -166,7 +174,7 @@ void FIFOPlayerWindow::ConnectWidgets()
connect(m_save, &QPushButton::clicked, this, &FIFOPlayerWindow::SaveRecording);
connect(m_record, &QPushButton::clicked, this, &FIFOPlayerWindow::StartRecording);
connect(m_stop, &QPushButton::clicked, this, &FIFOPlayerWindow::StopRecording);
connect(m_button_box, &QDialogButtonBox::rejected, this, &FIFOPlayerWindow::reject);
connect(m_button_box, &QDialogButtonBox::rejected, this, &FIFOPlayerWindow::hide);
connect(m_early_memory_updates, &QCheckBox::toggled, this,
&FIFOPlayerWindow::OnEarlyMemoryUpdatesChanged);
connect(m_frame_range_from, qOverload<int>(&QSpinBox::valueChanged), this,
@ -244,6 +252,7 @@ void FIFOPlayerWindow::OnEmulationStopped()
StopRecording();
UpdateControls();
m_analyzer->Update();
}
void FIFOPlayerWindow::OnRecordingDone()
@ -260,7 +269,7 @@ void FIFOPlayerWindow::UpdateInfo()
m_info_label->setText(
tr("%1 frame(s)\n%2 object(s)\nCurrent Frame: %3")
.arg(QString::number(file->GetFrameCount()),
QString::number(FifoPlayer::GetInstance().GetFrameObjectCount()),
QString::number(FifoPlayer::GetInstance().GetCurrentFrameObjectCount()),
QString::number(FifoPlayer::GetInstance().GetCurrentFrameNum())));
return;
}
@ -297,14 +306,16 @@ void FIFOPlayerWindow::OnFIFOLoaded()
{
FifoDataFile* file = FifoPlayer::GetInstance().GetFile();
auto object_count = FifoPlayer::GetInstance().GetFrameObjectCount();
auto object_count = FifoPlayer::GetInstance().GetMaxObjectCount();
auto frame_count = file->GetFrameCount();
m_frame_range_to->setMaximum(frame_count);
m_object_range_to->setMaximum(object_count);
m_frame_range_to->setMaximum(frame_count - 1);
m_object_range_to->setMaximum(object_count - 1);
m_frame_range_to->setValue(frame_count);
m_object_range_to->setValue(object_count);
m_frame_range_from->setValue(0);
m_object_range_from->setValue(0);
m_frame_range_to->setValue(frame_count - 1);
m_object_range_to->setValue(object_count - 1);
UpdateInfo();
UpdateLimits();
@ -331,10 +342,10 @@ void FIFOPlayerWindow::OnLimitsChanged()
void FIFOPlayerWindow::UpdateLimits()
{
m_frame_range_from->setMaximum(std::max(m_frame_range_to->value() - 1, 0));
m_frame_range_to->setMinimum(m_frame_range_from->value() + 1);
m_object_range_from->setMaximum(std::max(m_object_range_to->value() - 1, 0));
m_object_range_to->setMinimum(m_object_range_from->value() + 1);
m_frame_range_from->setMaximum(m_frame_range_to->value());
m_frame_range_to->setMinimum(m_frame_range_from->value());
m_object_range_from->setMaximum(m_object_range_to->value());
m_object_range_to->setMinimum(m_object_range_from->value());
}
void FIFOPlayerWindow::UpdateControls()
@ -367,3 +378,15 @@ void FIFOPlayerWindow::UpdateControls()
m_save->setEnabled(FifoRecorder::GetInstance().IsRecordingDone());
}
bool FIFOPlayerWindow::eventFilter(QObject* object, QEvent* event)
{
// Close when escape is pressed
if (event->type() == QEvent::KeyPress)
{
if (static_cast<QKeyEvent*>(event)->matches(QKeySequence::Cancel))
hide();
}
return false;
}

View File

@ -4,7 +4,9 @@
#pragma once
#include <QDialog>
#include <QWidget>
#include "Core/Core.h"
class QCheckBox;
class QDialogButtonBox;
@ -13,7 +15,7 @@ class QPushButton;
class QSpinBox;
class FIFOAnalyzer;
class FIFOPlayerWindow : public QDialog
class FIFOPlayerWindow : public QWidget
{
Q_OBJECT
public:
@ -43,6 +45,8 @@ private:
void UpdateInfo();
void UpdateLimits();
bool eventFilter(QObject* object, QEvent* event) final override;
QLabel* m_info_label;
QPushButton* m_load;
QPushButton* m_save;
@ -62,4 +66,5 @@ private:
QDialogButtonBox* m_button_box;
FIFOAnalyzer* m_analyzer;
Core::State m_emu_state = Core::State::Uninitialized;
};

View File

@ -1214,7 +1214,7 @@ void MainWindow::ShowFIFOPlayer()
{
if (!m_fifo_window)
{
m_fifo_window = new FIFOPlayerWindow(this);
m_fifo_window = new FIFOPlayerWindow;
connect(m_fifo_window, &FIFOPlayerWindow::LoadFIFORequested, this,
[this](const QString& path) { StartGame(path, ScanForSecondDisc::No); });
}

View File

@ -63,6 +63,11 @@ enum
ARRAY_TEXCOORD0 = 4,
NUM_TEXCOORD_ARRAYS = 8,
ARRAY_XF_A = 12, // Usually used for position matrices
ARRAY_XF_B = 13, // Usually used for normal matrices
ARRAY_XF_C = 14, // Usually used for tex coord matrices
ARRAY_XF_D = 15, // Usually used for light objects
// Number of arrays related to vertex components (position, normal, color, tex coord)
// Excludes the 4 arrays used for indexed XF loads
NUM_VERTEX_COMPONENT_ARRAYS = 12,
@ -317,10 +322,10 @@ union UVAT_group0
// 21:29
BitField<21, 1, TexComponentCount> Tex0CoordElements;
BitField<22, 3, ComponentFormat> Tex0CoordFormat;
BitField<25, 5, u32> Tex0Frac;
BitField<25, 5, u8, u32> Tex0Frac;
// 30:31
BitField<30, 1, u32> ByteDequant;
BitField<31, 1, u32> NormalIndex3;
BitField<30, 1, bool, u32> ByteDequant;
BitField<31, 1, bool, u32> NormalIndex3;
};
template <>
struct fmt::formatter<UVAT_group0>
@ -363,20 +368,20 @@ union UVAT_group1
// 0:8
BitField<0, 1, TexComponentCount> Tex1CoordElements;
BitField<1, 3, ComponentFormat> Tex1CoordFormat;
BitField<4, 5, u32> Tex1Frac;
BitField<4, 5, u8, u32> Tex1Frac;
// 9:17
BitField<9, 1, TexComponentCount> Tex2CoordElements;
BitField<10, 3, ComponentFormat> Tex2CoordFormat;
BitField<13, 5, u32> Tex2Frac;
BitField<13, 5, u8, u32> Tex2Frac;
// 18:26
BitField<18, 1, TexComponentCount> Tex3CoordElements;
BitField<19, 3, ComponentFormat> Tex3CoordFormat;
BitField<22, 5, u32> Tex3Frac;
BitField<22, 5, u8, u32> Tex3Frac;
// 27:30
BitField<27, 1, TexComponentCount> Tex4CoordElements;
BitField<28, 3, ComponentFormat> Tex4CoordFormat;
// 31
BitField<31, 1, u32> VCacheEnhance;
BitField<31, 1, bool, u32> VCacheEnhance;
};
template <>
struct fmt::formatter<UVAT_group1>
@ -410,19 +415,19 @@ union UVAT_group2
{
u32 Hex;
// 0:4
BitField<0, 5, u32> Tex4Frac;
BitField<0, 5, u8, u32> Tex4Frac;
// 5:13
BitField<5, 1, TexComponentCount> Tex5CoordElements;
BitField<6, 3, ComponentFormat> Tex5CoordFormat;
BitField<9, 5, u32> Tex5Frac;
BitField<9, 5, u8, u32> Tex5Frac;
// 14:22
BitField<14, 1, TexComponentCount> Tex6CoordElements;
BitField<15, 3, ComponentFormat> Tex6CoordFormat;
BitField<18, 5, u32> Tex6Frac;
BitField<18, 5, u8, u32> Tex6Frac;
// 23:31
BitField<23, 1, TexComponentCount> Tex7CoordElements;
BitField<24, 3, ComponentFormat> Tex7CoordFormat;
BitField<27, 5, u32> Tex7Frac;
BitField<27, 5, u8, u32> Tex7Frac;
};
template <>
struct fmt::formatter<UVAT_group2>
@ -450,30 +455,123 @@ struct fmt::formatter<UVAT_group2>
}
};
struct ColorAttr
struct VAT
{
ColorComponentCount Elements;
ColorFormat Comp;
};
UVAT_group0 g0;
UVAT_group1 g1;
UVAT_group2 g2;
struct TexAttr
constexpr ColorComponentCount GetColorElements(size_t idx) const
{
TexComponentCount Elements;
ComponentFormat Format;
u8 Frac;
switch (idx)
{
case 0:
return g0.Color0Elements;
case 1:
return g0.Color1Elements;
default:
PanicAlertFmt("Invalid color index {}", idx);
return ColorComponentCount::RGB;
}
}
constexpr ColorFormat GetColorFormat(size_t idx) const
{
switch (idx)
{
case 0:
return g0.Color0Comp;
case 1:
return g0.Color1Comp;
default:
PanicAlertFmt("Invalid color index {}", idx);
return ColorFormat::RGB565;
}
}
constexpr TexComponentCount GetTexElements(size_t idx) const
{
switch (idx)
{
case 0:
return g0.Tex0CoordElements;
case 1:
return g1.Tex1CoordElements;
case 2:
return g1.Tex2CoordElements;
case 3:
return g1.Tex3CoordElements;
case 4:
return g1.Tex4CoordElements;
case 5:
return g2.Tex5CoordElements;
case 6:
return g2.Tex6CoordElements;
case 7:
return g2.Tex7CoordElements;
default:
PanicAlertFmt("Invalid tex coord index {}", idx);
return TexComponentCount::S;
}
}
constexpr ComponentFormat GetTexFormat(size_t idx) const
{
switch (idx)
{
case 0:
return g0.Tex0CoordFormat;
case 1:
return g1.Tex1CoordFormat;
case 2:
return g1.Tex2CoordFormat;
case 3:
return g1.Tex3CoordFormat;
case 4:
return g1.Tex4CoordFormat;
case 5:
return g2.Tex5CoordFormat;
case 6:
return g2.Tex6CoordFormat;
case 7:
return g2.Tex7CoordFormat;
default:
PanicAlertFmt("Invalid tex coord index {}", idx);
return ComponentFormat::UByte;
}
}
constexpr u8 GetTexFrac(size_t idx) const
{
switch (idx)
{
case 0:
return g0.Tex0Frac;
case 1:
return g1.Tex1Frac;
case 2:
return g1.Tex2Frac;
case 3:
return g1.Tex3Frac;
case 4:
return g2.Tex4Frac;
case 5:
return g2.Tex5Frac;
case 6:
return g2.Tex6Frac;
case 7:
return g2.Tex7Frac;
default:
PanicAlertFmt("Invalid tex coord index {}", idx);
return 0;
}
}
};
struct TVtxAttr
template <>
struct fmt::formatter<VAT>
{
CoordComponentCount PosElements;
ComponentFormat PosFormat;
u8 PosFrac;
NormalComponentCount NormalElements;
ComponentFormat NormalFormat;
ColorAttr color[2];
TexAttr texCoord[8];
bool ByteDequant;
u8 NormalIndex3;
constexpr auto parse(format_parse_context& ctx) { return ctx.begin(); }
template <typename FormatContext>
auto format(const VAT& vat, FormatContext& ctx)
{
return format_to(ctx.out(), "{}\n{}\n{}", vat.g0, vat.g1, vat.g2);
}
};
// Matrix indices
@ -518,13 +616,6 @@ struct fmt::formatter<TMatrixIndexB>
}
};
struct VAT
{
UVAT_group0 g0;
UVAT_group1 g1;
UVAT_group2 g2;
};
class VertexLoaderBase;
// STATE_TO_SAVE

View File

@ -72,21 +72,16 @@ VertexLoader::VertexLoader(const TVtxDesc& vtx_desc, const VAT& vtx_attr)
CompileVertexTranslator();
// generate frac factors
m_posScale = 1.0f / (1U << m_VtxAttr.PosFrac);
for (int i = 0; i < 8; i++)
m_tcScale[i] = 1.0f / (1U << m_VtxAttr.texCoord[i].Frac);
m_posScale = 1.0f / (1U << m_VtxAttr.g0.PosFrac);
for (u32 i = 0; i < 8; i++)
m_tcScale[i] = 1.0f / (1U << m_VtxAttr.GetTexFrac(i));
}
void VertexLoader::CompileVertexTranslator()
{
m_VertexSize = 0;
const TVtxAttr& vtx_attr = m_VtxAttr;
// Reset pipeline
m_numPipelineStages = 0;
u32 components = 0;
// Position in pc vertex format.
int nat_offset = 0;
@ -94,72 +89,25 @@ void VertexLoader::CompileVertexTranslator()
if (m_VtxDesc.low.PosMatIdx)
{
WriteCall(PosMtx_ReadDirect_UByte);
components |= VB_HAS_POSMTXIDX;
m_native_vtx_decl.posmtx.components = 4;
m_native_vtx_decl.posmtx.enable = true;
m_native_vtx_decl.posmtx.offset = nat_offset;
m_native_vtx_decl.posmtx.type = VAR_UNSIGNED_BYTE;
m_native_vtx_decl.posmtx.integer = true;
nat_offset += 4;
m_VertexSize += 1;
}
if (m_VtxDesc.low.Tex0MatIdx)
for (auto texmtxidx : m_VtxDesc.low.TexMatIdx)
{
m_VertexSize += 1;
components |= VB_HAS_TEXMTXIDX0;
WriteCall(TexMtx_ReadDirect_UByte);
}
if (m_VtxDesc.low.Tex1MatIdx)
{
m_VertexSize += 1;
components |= VB_HAS_TEXMTXIDX1;
WriteCall(TexMtx_ReadDirect_UByte);
}
if (m_VtxDesc.low.Tex2MatIdx)
{
m_VertexSize += 1;
components |= VB_HAS_TEXMTXIDX2;
WriteCall(TexMtx_ReadDirect_UByte);
}
if (m_VtxDesc.low.Tex3MatIdx)
{
m_VertexSize += 1;
components |= VB_HAS_TEXMTXIDX3;
WriteCall(TexMtx_ReadDirect_UByte);
}
if (m_VtxDesc.low.Tex4MatIdx)
{
m_VertexSize += 1;
components |= VB_HAS_TEXMTXIDX4;
WriteCall(TexMtx_ReadDirect_UByte);
}
if (m_VtxDesc.low.Tex5MatIdx)
{
m_VertexSize += 1;
components |= VB_HAS_TEXMTXIDX5;
WriteCall(TexMtx_ReadDirect_UByte);
}
if (m_VtxDesc.low.Tex6MatIdx)
{
m_VertexSize += 1;
components |= VB_HAS_TEXMTXIDX6;
WriteCall(TexMtx_ReadDirect_UByte);
}
if (m_VtxDesc.low.Tex7MatIdx)
{
m_VertexSize += 1;
components |= VB_HAS_TEXMTXIDX7;
if (texmtxidx)
WriteCall(TexMtx_ReadDirect_UByte);
}
// Write vertex position loader
WriteCall(VertexLoader_Position::GetFunction(m_VtxDesc.low.Position, m_VtxAttr.PosFormat,
m_VtxAttr.PosElements));
WriteCall(VertexLoader_Position::GetFunction(m_VtxDesc.low.Position, m_VtxAttr.g0.PosFormat,
m_VtxAttr.g0.PosElements));
m_VertexSize += VertexLoader_Position::GetSize(m_VtxDesc.low.Position, m_VtxAttr.PosFormat,
m_VtxAttr.PosElements);
int pos_elements = m_VtxAttr.PosElements == CoordComponentCount::XY ? 2 : 3;
int pos_elements = m_VtxAttr.g0.PosElements == CoordComponentCount::XY ? 2 : 3;
m_native_vtx_decl.position.components = pos_elements;
m_native_vtx_decl.position.enable = true;
m_native_vtx_decl.position.offset = nat_offset;
@ -170,22 +118,19 @@ void VertexLoader::CompileVertexTranslator()
// Normals
if (m_VtxDesc.low.Normal != VertexComponentFormat::NotPresent)
{
m_VertexSize += VertexLoader_Normal::GetSize(m_VtxDesc.low.Normal, m_VtxAttr.NormalFormat,
m_VtxAttr.NormalElements, m_VtxAttr.NormalIndex3);
TPipelineFunction pFunc =
VertexLoader_Normal::GetFunction(m_VtxDesc.low.Normal, m_VtxAttr.NormalFormat,
m_VtxAttr.NormalElements, m_VtxAttr.NormalIndex3);
VertexLoader_Normal::GetFunction(m_VtxDesc.low.Normal, m_VtxAttr.g0.NormalFormat,
m_VtxAttr.g0.NormalElements, m_VtxAttr.g0.NormalIndex3);
if (pFunc == nullptr)
{
PanicAlertFmt("VertexLoader_Normal::GetFunction({} {} {} {}) returned zero!",
m_VtxDesc.low.Normal, m_VtxAttr.NormalFormat, m_VtxAttr.NormalElements,
m_VtxAttr.NormalIndex3);
m_VtxDesc.low.Normal, m_VtxAttr.g0.NormalFormat, m_VtxAttr.g0.NormalElements,
m_VtxAttr.g0.NormalIndex3);
}
WriteCall(pFunc);
for (int i = 0; i < (vtx_attr.NormalElements == NormalComponentCount::NBT ? 3 : 1); i++)
for (int i = 0; i < (m_VtxAttr.g0.NormalElements == NormalComponentCount::NBT ? 3 : 1); i++)
{
m_native_vtx_decl.normals[i].components = 3;
m_native_vtx_decl.normals[i].enable = true;
@ -194,10 +139,6 @@ void VertexLoader::CompileVertexTranslator()
m_native_vtx_decl.normals[i].integer = false;
nat_offset += 12;
}
components |= VB_HAS_NRM0;
if (m_VtxAttr.NormalElements == NormalComponentCount::NBT)
components |= VB_HAS_NRM1 | VB_HAS_NRM2;
}
for (size_t i = 0; i < m_VtxDesc.low.Color.Size(); i++)
@ -205,101 +146,17 @@ void VertexLoader::CompileVertexTranslator()
m_native_vtx_decl.colors[i].components = 4;
m_native_vtx_decl.colors[i].type = VAR_UNSIGNED_BYTE;
m_native_vtx_decl.colors[i].integer = false;
switch (m_VtxDesc.low.Color[i])
{
case VertexComponentFormat::NotPresent:
break;
case VertexComponentFormat::Direct:
switch (m_VtxAttr.color[i].Comp)
{
case ColorFormat::RGB565:
m_VertexSize += 2;
WriteCall(Color_ReadDirect_16b_565);
break;
case ColorFormat::RGB888:
m_VertexSize += 3;
WriteCall(Color_ReadDirect_24b_888);
break;
case ColorFormat::RGB888x:
m_VertexSize += 4;
WriteCall(Color_ReadDirect_32b_888x);
break;
case ColorFormat::RGBA4444:
m_VertexSize += 2;
WriteCall(Color_ReadDirect_16b_4444);
break;
case ColorFormat::RGBA6666:
m_VertexSize += 3;
WriteCall(Color_ReadDirect_24b_6666);
break;
case ColorFormat::RGBA8888:
m_VertexSize += 4;
WriteCall(Color_ReadDirect_32b_8888);
break;
default:
ASSERT(0);
break;
}
break;
case VertexComponentFormat::Index8:
m_VertexSize += 1;
switch (m_VtxAttr.color[i].Comp)
{
case ColorFormat::RGB565:
WriteCall(Color_ReadIndex8_16b_565);
break;
case ColorFormat::RGB888:
WriteCall(Color_ReadIndex8_24b_888);
break;
case ColorFormat::RGB888x:
WriteCall(Color_ReadIndex8_32b_888x);
break;
case ColorFormat::RGBA4444:
WriteCall(Color_ReadIndex8_16b_4444);
break;
case ColorFormat::RGBA6666:
WriteCall(Color_ReadIndex8_24b_6666);
break;
case ColorFormat::RGBA8888:
WriteCall(Color_ReadIndex8_32b_8888);
break;
default:
ASSERT(0);
break;
}
break;
case VertexComponentFormat::Index16:
m_VertexSize += 2;
switch (m_VtxAttr.color[i].Comp)
{
case ColorFormat::RGB565:
WriteCall(Color_ReadIndex16_16b_565);
break;
case ColorFormat::RGB888:
WriteCall(Color_ReadIndex16_24b_888);
break;
case ColorFormat::RGB888x:
WriteCall(Color_ReadIndex16_32b_888x);
break;
case ColorFormat::RGBA4444:
WriteCall(Color_ReadIndex16_16b_4444);
break;
case ColorFormat::RGBA6666:
WriteCall(Color_ReadIndex16_24b_6666);
break;
case ColorFormat::RGBA8888:
WriteCall(Color_ReadIndex16_32b_8888);
break;
default:
ASSERT(0);
break;
}
break;
}
// Common for the three bottom cases
TPipelineFunction pFunc =
VertexLoader_Color::GetFunction(m_VtxDesc.low.Color[i], m_VtxAttr.GetColorFormat(i));
if (pFunc != nullptr)
WriteCall(pFunc);
else
ASSERT(m_VtxDesc.low.Color[i] == VertexComponentFormat::NotPresent);
if (m_VtxDesc.low.Color[i] != VertexComponentFormat::NotPresent)
{
components |= VB_HAS_COL0 << i;
m_native_vtx_decl.colors[i].offset = nat_offset;
m_native_vtx_decl.colors[i].enable = true;
nat_offset += 4;
@ -314,8 +171,8 @@ void VertexLoader::CompileVertexTranslator()
m_native_vtx_decl.texcoords[i].integer = false;
const auto tc = m_VtxDesc.high.TexCoord[i].Value();
const auto format = m_VtxAttr.texCoord[i].Format;
const auto elements = m_VtxAttr.texCoord[i].Elements;
const auto format = m_VtxAttr.GetTexFormat(i);
const auto elements = m_VtxAttr.GetTexElements(i);
if (tc != VertexComponentFormat::NotPresent)
{
@ -326,26 +183,21 @@ void VertexLoader::CompileVertexTranslator()
ASSERT_MSG(VIDEO, elements == TexComponentCount::S || elements == TexComponentCount::ST,
"Invalid number of texture coordinates elements!\n(elements = %d)", (u32)elements);
components |= VB_HAS_UV0 << i;
WriteCall(VertexLoader_TextCoord::GetFunction(tc, format, elements));
m_VertexSize += VertexLoader_TextCoord::GetSize(tc, format, elements);
}
if (components & (VB_HAS_TEXMTXIDX0 << i))
if (m_VtxDesc.low.TexMatIdx[i])
{
m_native_vtx_decl.texcoords[i].enable = true;
m_native_vtx_decl.texcoords[i].components = 3;
nat_offset += 12;
if (tc != VertexComponentFormat::NotPresent)
{
// if texmtx is included, texcoord will always be 3 floats, z will be the texmtx index
m_native_vtx_decl.texcoords[i].components = 3;
nat_offset += 12;
WriteCall(m_VtxAttr.texCoord[i].Elements == TexComponentCount::ST ? TexMtx_Write_Float :
TexMtx_Write_Float2);
WriteCall(elements == TexComponentCount::ST ? TexMtx_Write_Float : TexMtx_Write_Float2);
}
else
{
m_native_vtx_decl.texcoords[i].components = 3;
nat_offset += 12;
WriteCall(TexMtx_Write_Float3);
}
}
@ -354,26 +206,29 @@ void VertexLoader::CompileVertexTranslator()
if (tc != VertexComponentFormat::NotPresent)
{
m_native_vtx_decl.texcoords[i].enable = true;
m_native_vtx_decl.texcoords[i].components =
vtx_attr.texCoord[i].Elements == TexComponentCount::ST ? 2 : 1;
nat_offset += 4 * (vtx_attr.texCoord[i].Elements == TexComponentCount::ST ? 2 : 1);
m_native_vtx_decl.texcoords[i].components = elements == TexComponentCount::ST ? 2 : 1;
nat_offset += 4 * (elements == TexComponentCount::ST ? 2 : 1);
}
}
if (tc == VertexComponentFormat::NotPresent)
{
// if there's more tex coords later, have to write a dummy call
size_t j = i + 1;
for (; j < m_VtxDesc.high.TexCoord.Size(); ++j)
bool has_more = false;
for (size_t j = 0; j < m_VtxDesc.high.TexCoord.Size(); ++j)
{
if (m_VtxDesc.high.TexCoord[j] != VertexComponentFormat::NotPresent)
{
has_more = true;
WriteCall(VertexLoader_TextCoord::GetDummyFunction()); // important to get indices right!
break;
}
else if (m_VtxDesc.low.TexMatIdx[i])
{
has_more = true;
}
// tricky!
if (j == 8 && !((components & VB_HAS_TEXMTXIDXALL) & (VB_HAS_TEXMTXIDXALL << (i + 1))))
}
if (!has_more)
{
// no more tex coords and tex matrices, so exit loop
break;
@ -387,7 +242,6 @@ void VertexLoader::CompileVertexTranslator()
WriteCall(SkipVertex);
}
m_native_components = components;
m_native_vtx_decl.stride = nat_offset;
}

View File

@ -22,8 +22,6 @@ public:
VertexLoader(const TVtxDesc& vtx_desc, const VAT& vtx_attr);
int RunVertices(DataReader src, DataReader dst, int count) override;
std::string GetName() const override { return "OldLoader"; }
bool IsInitialized() override { return true; } // This vertex loader supports all formats
// They are used for the communication with the loader functions
float m_posScale;
float m_tcScale[8];

View File

@ -25,6 +25,20 @@ constexpr ARM64Reg stride_reg = ARM64Reg::X11;
constexpr ARM64Reg arraybase_reg = ARM64Reg::X10;
constexpr ARM64Reg scale_reg = ARM64Reg::X9;
static constexpr int GetLoadSize(int load_bytes)
{
if (load_bytes == 1)
return 1;
else if (load_bytes <= 2)
return 2;
else if (load_bytes <= 4)
return 4;
else if (load_bytes <= 8)
return 8;
else
return 16;
}
alignas(16) static const float scale_factors[] = {
1.0 / (1ULL << 0), 1.0 / (1ULL << 1), 1.0 / (1ULL << 2), 1.0 / (1ULL << 3),
1.0 / (1ULL << 4), 1.0 / (1ULL << 5), 1.0 / (1ULL << 6), 1.0 / (1ULL << 7),
@ -39,9 +53,6 @@ alignas(16) static const float scale_factors[] = {
VertexLoaderARM64::VertexLoaderARM64(const TVtxDesc& vtx_desc, const VAT& vtx_att)
: VertexLoaderBase(vtx_desc, vtx_att), m_float_emit(this)
{
if (!IsInitialized())
return;
AllocCodeSpace(4096);
ClearCodeSpace();
GenerateVertexLoader();
@ -120,8 +131,7 @@ int VertexLoaderARM64::ReadVertex(VertexComponentFormat attribute, ComponentForm
int elem_size = GetElementSize(format);
int load_bytes = elem_size * count_in;
int load_size =
load_bytes == 1 ? 1 : load_bytes <= 2 ? 2 : load_bytes <= 4 ? 4 : load_bytes <= 8 ? 8 : 16;
int load_size = GetLoadSize(load_bytes);
load_size <<= 3;
elem_size <<= 3;
@ -380,10 +390,11 @@ void VertexLoaderARM64::GenerateVertexLoader()
for (size_t i = 0; i < m_VtxDesc.high.TexCoord.Size(); i++)
{
has_tc |= m_VtxDesc.high.TexCoord[i] != VertexComponentFormat::NotPresent;
has_tc_scale |= !!m_VtxAttr.texCoord[i].Frac;
has_tc_scale |= (m_VtxAttr.GetTexFrac(i) != 0);
}
bool need_scale = (m_VtxAttr.ByteDequant && m_VtxAttr.PosFrac) || (has_tc && has_tc_scale) ||
bool need_scale = (m_VtxAttr.g0.ByteDequant && m_VtxAttr.g0.PosFrac) ||
(has_tc && has_tc_scale) ||
(m_VtxDesc.low.Normal != VertexComponentFormat::NotPresent);
AlignCode16();
@ -412,7 +423,6 @@ void VertexLoaderARM64::GenerateVertexLoader()
STR(IndexType::Unsigned, scratch1_reg, EncodeRegTo64(scratch2_reg), 0);
SetJumpTarget(dont_store);
m_native_components |= VB_HAS_POSMTXIDX;
m_native_vtx_decl.posmtx.components = 4;
m_native_vtx_decl.posmtx.enable = true;
m_native_vtx_decl.posmtx.offset = m_dst_ofs;
@ -431,36 +441,33 @@ void VertexLoaderARM64::GenerateVertexLoader()
// Position
{
int elem_size = GetElementSize(m_VtxAttr.PosFormat);
int pos_elements = m_VtxAttr.PosElements == CoordComponentCount::XY ? 2 : 3;
int elem_size = GetElementSize(m_VtxAttr.g0.PosFormat);
int pos_elements = m_VtxAttr.g0.PosElements == CoordComponentCount::XY ? 2 : 3;
int load_bytes = elem_size * pos_elements;
int load_size =
load_bytes == 1 ? 1 : load_bytes <= 2 ? 2 : load_bytes <= 4 ? 4 : load_bytes <= 8 ? 8 : 16;
int load_size = GetLoadSize(load_bytes);
load_size <<= 3;
s32 offset = GetAddressImm(ARRAY_POSITION, m_VtxDesc.low.Position, EncodeRegTo64(scratch1_reg),
load_size);
ReadVertex(m_VtxDesc.low.Position, m_VtxAttr.PosFormat, pos_elements, pos_elements,
m_VtxAttr.ByteDequant, m_VtxAttr.PosFrac, &m_native_vtx_decl.position, offset);
ReadVertex(m_VtxDesc.low.Position, m_VtxAttr.g0.PosFormat, pos_elements, pos_elements,
m_VtxAttr.g0.ByteDequant, m_VtxAttr.g0.PosFrac, &m_native_vtx_decl.position, offset);
}
if (m_VtxDesc.low.Normal != VertexComponentFormat::NotPresent)
{
static const u8 map[8] = {7, 6, 15, 14};
const u8 scaling_exponent = map[u32(m_VtxAttr.NormalFormat)];
const int limit = m_VtxAttr.NormalElements == NormalComponentCount::NBT ? 3 : 1;
const u8 scaling_exponent = map[u32(m_VtxAttr.g0.NormalFormat.Value())];
const int limit = m_VtxAttr.g0.NormalElements == NormalComponentCount::NBT ? 3 : 1;
s32 offset = -1;
for (int i = 0; i < (m_VtxAttr.NormalElements == NormalComponentCount::NBT ? 3 : 1); i++)
for (int i = 0; i < (m_VtxAttr.g0.NormalElements == NormalComponentCount::NBT ? 3 : 1); i++)
{
if (!i || m_VtxAttr.NormalIndex3)
if (!i || m_VtxAttr.g0.NormalIndex3)
{
int elem_size = GetElementSize(m_VtxAttr.NormalFormat);
int elem_size = GetElementSize(m_VtxAttr.g0.NormalFormat);
int load_bytes = elem_size * 3;
int load_size = load_bytes == 1 ?
1 :
load_bytes <= 2 ? 2 : load_bytes <= 4 ? 4 : load_bytes <= 8 ? 8 : 16;
int load_size = GetLoadSize(load_bytes);
offset = GetAddressImm(ARRAY_NORMAL, m_VtxDesc.low.Normal, EncodeRegTo64(scratch1_reg),
load_size << 3);
@ -470,7 +477,7 @@ void VertexLoaderARM64::GenerateVertexLoader()
else
offset += i * elem_size * 3;
}
int bytes_read = ReadVertex(m_VtxDesc.low.Normal, m_VtxAttr.NormalFormat, 3, 3, true,
int bytes_read = ReadVertex(m_VtxDesc.low.Normal, m_VtxAttr.g0.NormalFormat, 3, 3, true,
scaling_exponent, &m_native_vtx_decl.normals[i], offset);
if (offset == -1)
@ -478,10 +485,6 @@ void VertexLoaderARM64::GenerateVertexLoader()
else
offset += bytes_read;
}
m_native_components |= VB_HAS_NRM0;
if (m_VtxAttr.NormalElements == NormalComponentCount::NBT)
m_native_components |= VB_HAS_NRM1 | VB_HAS_NRM2;
}
for (size_t i = 0; i < m_VtxDesc.low.Color.Size(); i++)
@ -493,14 +496,13 @@ void VertexLoaderARM64::GenerateVertexLoader()
if (m_VtxDesc.low.Color[i] != VertexComponentFormat::NotPresent)
{
u32 align = 4;
if (m_VtxAttr.color[i].Comp == ColorFormat::RGB565 ||
m_VtxAttr.color[i].Comp == ColorFormat::RGBA4444)
if (m_VtxAttr.GetColorFormat(i) == ColorFormat::RGB565 ||
m_VtxAttr.GetColorFormat(i) == ColorFormat::RGBA4444)
align = 2;
s32 offset = GetAddressImm(ARRAY_COLOR0 + int(i), m_VtxDesc.low.Color[i],
EncodeRegTo64(scratch1_reg), align);
ReadColor(m_VtxDesc.low.Color[i], m_VtxAttr.color[i].Comp, offset);
m_native_components |= VB_HAS_COL0 << i;
ReadColor(m_VtxDesc.low.Color[i], m_VtxAttr.GetColorFormat(i), offset);
m_native_vtx_decl.colors[i].components = 4;
m_native_vtx_decl.colors[i].enable = true;
m_native_vtx_decl.colors[i].offset = m_dst_ofs;
@ -516,28 +518,23 @@ void VertexLoaderARM64::GenerateVertexLoader()
m_native_vtx_decl.texcoords[i].type = VAR_FLOAT;
m_native_vtx_decl.texcoords[i].integer = false;
int elements = m_VtxAttr.texCoord[i].Elements == TexComponentCount::S ? 1 : 2;
int elements = m_VtxAttr.GetTexElements(i) == TexComponentCount::S ? 1 : 2;
if (m_VtxDesc.high.TexCoord[i] != VertexComponentFormat::NotPresent)
{
m_native_components |= VB_HAS_UV0 << i;
int elem_size = GetElementSize(m_VtxAttr.texCoord[i].Format);
int elem_size = GetElementSize(m_VtxAttr.GetTexFormat(i));
int load_bytes = elem_size * (elements + 2);
int load_size = load_bytes == 1 ?
1 :
load_bytes <= 2 ? 2 : load_bytes <= 4 ? 4 : load_bytes <= 8 ? 8 : 16;
int load_size = GetLoadSize(load_bytes);
load_size <<= 3;
s32 offset = GetAddressImm(ARRAY_TEXCOORD0 + int(i), m_VtxDesc.high.TexCoord[i],
EncodeRegTo64(scratch1_reg), load_size);
u8 scaling_exponent = m_VtxAttr.texCoord[i].Frac;
ReadVertex(m_VtxDesc.high.TexCoord[i], m_VtxAttr.texCoord[i].Format, elements,
m_VtxDesc.low.TexMatIdx[i] ? 2 : elements, m_VtxAttr.ByteDequant, scaling_exponent,
&m_native_vtx_decl.texcoords[i], offset);
u8 scaling_exponent = m_VtxAttr.GetTexFrac(i);
ReadVertex(m_VtxDesc.high.TexCoord[i], m_VtxAttr.GetTexFormat(i), elements,
m_VtxDesc.low.TexMatIdx[i] ? 2 : elements, m_VtxAttr.g0.ByteDequant,
scaling_exponent, &m_native_vtx_decl.texcoords[i], offset);
}
if (m_VtxDesc.low.TexMatIdx[i])
{
m_native_components |= VB_HAS_TEXMTXIDX0 << i;
m_native_vtx_decl.texcoords[i].components = 3;
m_native_vtx_decl.texcoords[i].enable = true;
m_native_vtx_decl.texcoords[i].type = VAR_FLOAT;
@ -603,7 +600,7 @@ void VertexLoaderARM64::GenerateVertexLoader()
FlushIcache();
m_VertexSize = m_src_ofs;
ASSERT(m_vertex_size == m_src_ofs);
m_native_vtx_decl.stride = m_dst_ofs;
}

View File

@ -19,8 +19,6 @@ public:
VertexLoaderARM64(const TVtxDesc& vtx_desc, const VAT& vtx_att);
protected:
std::string GetName() const override { return "VertexLoaderARM64"; }
bool IsInitialized() override { return true; }
int RunVertices(DataReader src, DataReader dst, int count) override;
private:

View File

@ -19,6 +19,10 @@
#include "VideoCommon/DataReader.h"
#include "VideoCommon/VertexLoader.h"
#include "VideoCommon/VertexLoader_Color.h"
#include "VideoCommon/VertexLoader_Normal.h"
#include "VideoCommon/VertexLoader_Position.h"
#include "VideoCommon/VertexLoader_TextCoord.h"
#ifdef _M_X86_64
#include "VideoCommon/VertexLoaderX64.h"
@ -26,93 +30,6 @@
#include "VideoCommon/VertexLoaderARM64.h"
#endif
VertexLoaderBase::VertexLoaderBase(const TVtxDesc& vtx_desc, const VAT& vtx_attr)
: m_VtxDesc{vtx_desc}, m_vat{vtx_attr}
{
SetVAT(vtx_attr);
}
void VertexLoaderBase::SetVAT(const VAT& vat)
{
m_VtxAttr.PosElements = vat.g0.PosElements;
m_VtxAttr.PosFormat = vat.g0.PosFormat;
m_VtxAttr.PosFrac = vat.g0.PosFrac;
m_VtxAttr.NormalElements = vat.g0.NormalElements;
m_VtxAttr.NormalFormat = vat.g0.NormalFormat;
m_VtxAttr.color[0].Elements = vat.g0.Color0Elements;
m_VtxAttr.color[0].Comp = vat.g0.Color0Comp;
m_VtxAttr.color[1].Elements = vat.g0.Color1Elements;
m_VtxAttr.color[1].Comp = vat.g0.Color1Comp;
m_VtxAttr.texCoord[0].Elements = vat.g0.Tex0CoordElements;
m_VtxAttr.texCoord[0].Format = vat.g0.Tex0CoordFormat;
m_VtxAttr.texCoord[0].Frac = vat.g0.Tex0Frac;
m_VtxAttr.ByteDequant = vat.g0.ByteDequant;
m_VtxAttr.NormalIndex3 = vat.g0.NormalIndex3;
m_VtxAttr.texCoord[1].Elements = vat.g1.Tex1CoordElements;
m_VtxAttr.texCoord[1].Format = vat.g1.Tex1CoordFormat;
m_VtxAttr.texCoord[1].Frac = vat.g1.Tex1Frac;
m_VtxAttr.texCoord[2].Elements = vat.g1.Tex2CoordElements;
m_VtxAttr.texCoord[2].Format = vat.g1.Tex2CoordFormat;
m_VtxAttr.texCoord[2].Frac = vat.g1.Tex2Frac;
m_VtxAttr.texCoord[3].Elements = vat.g1.Tex3CoordElements;
m_VtxAttr.texCoord[3].Format = vat.g1.Tex3CoordFormat;
m_VtxAttr.texCoord[3].Frac = vat.g1.Tex3Frac;
m_VtxAttr.texCoord[4].Elements = vat.g1.Tex4CoordElements;
m_VtxAttr.texCoord[4].Format = vat.g1.Tex4CoordFormat;
m_VtxAttr.texCoord[4].Frac = vat.g2.Tex4Frac;
m_VtxAttr.texCoord[5].Elements = vat.g2.Tex5CoordElements;
m_VtxAttr.texCoord[5].Format = vat.g2.Tex5CoordFormat;
m_VtxAttr.texCoord[5].Frac = vat.g2.Tex5Frac;
m_VtxAttr.texCoord[6].Elements = vat.g2.Tex6CoordElements;
m_VtxAttr.texCoord[6].Format = vat.g2.Tex6CoordFormat;
m_VtxAttr.texCoord[6].Frac = vat.g2.Tex6Frac;
m_VtxAttr.texCoord[7].Elements = vat.g2.Tex7CoordElements;
m_VtxAttr.texCoord[7].Format = vat.g2.Tex7CoordFormat;
m_VtxAttr.texCoord[7].Frac = vat.g2.Tex7Frac;
};
std::string VertexLoaderBase::ToString() const
{
std::string dest;
dest.reserve(250);
dest += GetName();
dest += ": ";
dest += fmt::format("{}b skin: {} P: {} {}-{} ", m_VertexSize, m_VtxDesc.low.PosMatIdx,
m_VtxAttr.PosElements, m_VtxDesc.low.Position, m_VtxAttr.PosFormat);
if (m_VtxDesc.low.Normal != VertexComponentFormat::NotPresent)
{
dest += fmt::format("Nrm: {} {}-{} ", m_VtxAttr.NormalElements, m_VtxDesc.low.Normal,
m_VtxAttr.NormalFormat);
}
for (size_t i = 0; i < g_main_cp_state.vtx_desc.low.Color.Size(); i++)
{
if (g_main_cp_state.vtx_desc.low.Color[i] == VertexComponentFormat::NotPresent)
continue;
const auto& color = m_VtxAttr.color[i];
dest += fmt::format("C{}: {} {}-{} ", i, color.Elements, g_main_cp_state.vtx_desc.low.Color[i],
color.Comp);
}
for (size_t i = 0; i < g_main_cp_state.vtx_desc.high.TexCoord.Size(); i++)
{
if (g_main_cp_state.vtx_desc.high.TexCoord[i] == VertexComponentFormat::NotPresent)
continue;
const auto& tex_coord = m_VtxAttr.texCoord[i];
dest += fmt::format("T{}: {} {}-{} ", i, tex_coord.Elements,
g_main_cp_state.vtx_desc.high.TexCoord[i], tex_coord.Format);
}
dest += fmt::format(" - {} v", m_numLoadedVertices);
return dest;
}
// a hacky implementation to compare two vertex loaders
class VertexLoaderTester : public VertexLoaderBase
{
@ -121,31 +38,25 @@ public:
const TVtxDesc& vtx_desc, const VAT& vtx_attr)
: VertexLoaderBase(vtx_desc, vtx_attr), a(std::move(a_)), b(std::move(b_))
{
m_initialized = a && b && a->IsInitialized() && b->IsInitialized();
if (m_initialized)
ASSERT(a && b);
if (a->m_vertex_size == b->m_vertex_size && a->m_native_components == b->m_native_components &&
a->m_native_vtx_decl.stride == b->m_native_vtx_decl.stride)
{
m_initialized = a->m_VertexSize == b->m_VertexSize &&
a->m_native_components == b->m_native_components &&
a->m_native_vtx_decl.stride == b->m_native_vtx_decl.stride;
// These are generated from the VAT and vertex desc, so they should match.
// m_native_vtx_decl.stride isn't set yet, though.
ASSERT(m_vertex_size == a->m_vertex_size && m_native_components == a->m_native_components);
if (m_initialized)
{
m_VertexSize = a->m_VertexSize;
m_native_components = a->m_native_components;
memcpy(&m_native_vtx_decl, &a->m_native_vtx_decl, sizeof(PortableVertexDeclaration));
}
else
{
ERROR_LOG_FMT(VIDEO, "Can't compare vertex loaders that expect different vertex formats!");
ERROR_LOG_FMT(VIDEO, "a: m_VertexSize {}, m_native_components {:#010x}, stride {}",
a->m_VertexSize, a->m_native_components, a->m_native_vtx_decl.stride);
ERROR_LOG_FMT(VIDEO, "b: m_VertexSize {}, m_native_components {:#010x}, stride {}",
b->m_VertexSize, b->m_native_components, b->m_native_vtx_decl.stride);
PanicAlertFmt("Can't compare vertex loaders that expect different vertex formats!\n"
"a: m_vertex_size {}, m_native_components {:#010x}, stride {}\n"
"b: m_vertex_size {}, m_native_components {:#010x}, stride {}",
a->m_vertex_size, a->m_native_components, a->m_native_vtx_decl.stride,
b->m_vertex_size, b->m_native_components, b->m_native_vtx_decl.stride);
}
}
}
~VertexLoaderTester() override {}
int RunVertices(DataReader src, DataReader dst, int count) override
{
buffer_a.resize(count * a->m_native_vtx_decl.stride + 4);
@ -168,22 +79,17 @@ public:
std::min(count_a, count_b) * m_native_vtx_decl.stride))
{
ERROR_LOG_FMT(VIDEO,
"The two vertex loaders have loaded different data "
"(guru meditation {:#010x}, {:#010x}, {:#010x}, {:#010x}, {:#010x}).",
m_VtxDesc.low.Hex, m_VtxDesc.high.Hex, m_vat.g0.Hex, m_vat.g1.Hex,
m_vat.g2.Hex);
"The two vertex loaders have loaded different data. Configuration:"
"\nVertex desc:\n{}\n\nVertex attr:\n{}",
m_VtxDesc, m_VtxAttr);
}
memcpy(dst.GetPointer(), buffer_a.data(), count_a * m_native_vtx_decl.stride);
m_numLoadedVertices += count;
return count_a;
}
std::string GetName() const override { return "CompareLoader"; }
bool IsInitialized() override { return m_initialized; }
private:
bool m_initialized;
std::unique_ptr<VertexLoaderBase> a;
std::unique_ptr<VertexLoaderBase> b;
@ -191,36 +97,112 @@ private:
std::vector<u8> buffer_b;
};
template <class Function>
static void GetComponentSizes(const TVtxDesc& vtx_desc, const VAT& vtx_attr, Function f)
{
if (vtx_desc.low.PosMatIdx)
f(1);
for (auto texmtxidx : vtx_desc.low.TexMatIdx)
{
if (texmtxidx)
f(1);
}
const u32 pos_size = VertexLoader_Position::GetSize(vtx_desc.low.Position, vtx_attr.g0.PosFormat,
vtx_attr.g0.PosElements);
if (pos_size != 0)
f(pos_size);
const u32 norm_size =
VertexLoader_Normal::GetSize(vtx_desc.low.Normal, vtx_attr.g0.NormalFormat,
vtx_attr.g0.NormalElements, vtx_attr.g0.NormalIndex3);
if (norm_size != 0)
f(norm_size);
for (u32 i = 0; i < vtx_desc.low.Color.Size(); i++)
{
const u32 color_size =
VertexLoader_Color::GetSize(vtx_desc.low.Color[i], vtx_attr.GetColorFormat(i));
if (color_size != 0)
f(color_size);
}
for (u32 i = 0; i < vtx_desc.high.TexCoord.Size(); i++)
{
const u32 tc_size = VertexLoader_TextCoord::GetSize(
vtx_desc.high.TexCoord[i], vtx_attr.GetTexFormat(i), vtx_attr.GetTexElements(i));
if (tc_size != 0)
f(tc_size);
}
}
u32 VertexLoaderBase::GetVertexSize(const TVtxDesc& vtx_desc, const VAT& vtx_attr)
{
u32 size = 0;
GetComponentSizes(vtx_desc, vtx_attr, [&size](u32 s) { size += s; });
return size;
}
u32 VertexLoaderBase::GetVertexComponents(const TVtxDesc& vtx_desc, const VAT& vtx_attr)
{
u32 components = 0;
if (vtx_desc.low.PosMatIdx)
components |= VB_HAS_POSMTXIDX;
for (u32 i = 0; i < vtx_desc.low.TexMatIdx.Size(); i++)
{
if (vtx_desc.low.TexMatIdx[i])
components |= VB_HAS_TEXMTXIDX0 << i;
}
// Vertices always have positions; thus there is no VB_HAS_POS as it would always be set
if (vtx_desc.low.Normal != VertexComponentFormat::NotPresent)
{
components |= VB_HAS_NRM0;
if (vtx_attr.g0.NormalElements == NormalComponentCount::NBT)
components |= VB_HAS_NRM1 | VB_HAS_NRM2;
}
for (u32 i = 0; i < vtx_desc.low.Color.Size(); i++)
{
if (vtx_desc.low.Color[i] != VertexComponentFormat::NotPresent)
components |= VB_HAS_COL0 << i;
}
for (u32 i = 0; i < vtx_desc.high.TexCoord.Size(); i++)
{
if (vtx_desc.high.TexCoord[i] != VertexComponentFormat::NotPresent)
components |= VB_HAS_UV0 << i;
}
return components;
}
std::vector<u32> VertexLoaderBase::GetVertexComponentSizes(const TVtxDesc& vtx_desc,
const VAT& vtx_attr)
{
std::vector<u32> sizes;
GetComponentSizes(vtx_desc, vtx_attr, [&sizes](u32 s) { sizes.push_back(s); });
return sizes;
}
std::unique_ptr<VertexLoaderBase> VertexLoaderBase::CreateVertexLoader(const TVtxDesc& vtx_desc,
const VAT& vtx_attr)
{
std::unique_ptr<VertexLoaderBase> loader;
std::unique_ptr<VertexLoaderBase> loader = nullptr;
//#define COMPARE_VERTEXLOADERS
#if defined(COMPARE_VERTEXLOADERS) && defined(_M_X86_64)
// first try: Any new VertexLoader vs the old one
loader = std::make_unique<VertexLoaderTester>(
std::make_unique<VertexLoader>(vtx_desc, vtx_attr), // the software one
std::make_unique<VertexLoaderX64>(vtx_desc, vtx_attr), // the new one to compare
vtx_desc, vtx_attr);
if (loader->IsInitialized())
return loader;
#elif defined(_M_X86_64)
#if defined(_M_X86_64)
loader = std::make_unique<VertexLoaderX64>(vtx_desc, vtx_attr);
if (loader->IsInitialized())
return loader;
#elif defined(_M_ARM_64)
loader = std::make_unique<VertexLoaderARM64>(vtx_desc, vtx_attr);
if (loader->IsInitialized())
return loader;
#endif
// last try: The old VertexLoader
// Use the software loader as a fallback
// (not currently applicable, as both VertexLoaderX64 and VertexLoaderARM64
// are always usable, but if a loader that only works on some CPUs is created
// then this fallback would be used)
if (!loader)
loader = std::make_unique<VertexLoader>(vtx_desc, vtx_attr);
if (loader->IsInitialized())
return loader;
PanicAlertFmt("No Vertex Loader found.");
return nullptr;
#if defined(COMPARE_VERTEXLOADERS)
return std::make_unique<VertexLoaderTester>(
std::make_unique<VertexLoader>(vtx_desc, vtx_attr), // the software one
std::move(loader), // the new one to compare
vtx_desc, vtx_attr);
#else
return loader;
#endif
}

View File

@ -7,6 +7,7 @@
#include <array>
#include <memory>
#include <string>
#include <vector>
#include "Common/CommonTypes.h"
#include "VideoCommon/CPMemory.h"
@ -60,33 +61,31 @@ struct hash<VertexLoaderUID>
class VertexLoaderBase
{
public:
static u32 GetVertexSize(const TVtxDesc& vtx_desc, const VAT& vtx_attr);
static u32 GetVertexComponents(const TVtxDesc& vtx_desc, const VAT& vtx_attr);
static std::vector<u32> GetVertexComponentSizes(const TVtxDesc& vtx_desc, const VAT& vtx_attr);
static std::unique_ptr<VertexLoaderBase> CreateVertexLoader(const TVtxDesc& vtx_desc,
const VAT& vtx_attr);
virtual ~VertexLoaderBase() {}
virtual int RunVertices(DataReader src, DataReader dst, int count) = 0;
virtual bool IsInitialized() = 0;
// For debugging / profiling
std::string ToString() const;
virtual std::string GetName() const = 0;
// per loader public state
int m_VertexSize = 0; // number of bytes of a raw GC vertex
PortableVertexDeclaration m_native_vtx_decl{};
u32 m_native_components = 0;
const u32 m_vertex_size; // number of bytes of a raw GC vertex
const u32 m_native_components;
// used by VertexLoaderManager
NativeVertexFormat* m_native_vertex_format = nullptr;
int m_numLoadedVertices = 0;
protected:
VertexLoaderBase(const TVtxDesc& vtx_desc, const VAT& vtx_attr);
void SetVAT(const VAT& vat);
VertexLoaderBase(const TVtxDesc& vtx_desc, const VAT& vtx_attr)
: m_VtxDesc{vtx_desc}, m_VtxAttr{vtx_attr}, m_vertex_size{GetVertexSize(vtx_desc, vtx_attr)},
m_native_components{GetVertexComponents(vtx_desc, vtx_attr)}
{
}
// GC vertex format
TVtxAttr m_VtxAttr; // VAT decoded into easy format
TVtxDesc m_VtxDesc; // Not really used currently - or well it is, but could be easily avoided.
VAT m_vat;
const VAT m_VtxAttr;
const TVtxDesc m_VtxDesc;
};

View File

@ -114,34 +114,6 @@ struct entry
};
} // namespace
std::string VertexLoadersToString()
{
std::lock_guard<std::mutex> lk(s_vertex_loader_map_lock);
std::vector<entry> entries;
size_t total_size = 0;
for (const auto& map_entry : s_vertex_loader_map)
{
entry e = {map_entry.second->ToString(),
static_cast<u64>(map_entry.second->m_numLoadedVertices)};
total_size += e.text.size() + 1;
entries.push_back(std::move(e));
}
sort(entries.begin(), entries.end());
std::string dest;
dest.reserve(total_size);
for (const entry& entry : entries)
{
dest += entry.text;
dest += '\n';
}
return dest;
}
void MarkAllDirty()
{
g_main_cp_state.attr_dirty = BitSet32::AllTrue(8);
@ -275,7 +247,7 @@ int RunVertices(int vtx_attr_group, int primitive, int count, DataReader src, bo
VertexLoaderBase* loader = RefreshLoader(vtx_attr_group, is_preprocess);
int size = count * loader->m_VertexSize;
int size = count * loader->m_vertex_size;
if ((int)src.size() < size)
return -1;

View File

@ -38,9 +38,6 @@ NativeVertexFormat* GetUberVertexFormat(const PortableVertexDeclaration& decl);
// Returns -1 if buf_size is insufficient, else the amount of bytes consumed
int RunVertices(int vtx_attr_group, int primitive, int count, DataReader src, bool is_preprocess);
// For debugging
std::string VertexLoadersToString();
NativeVertexFormat* GetCurrentVertexFormat();
// Resolved pointers to array bases. Used by vertex loaders.

View File

@ -45,15 +45,13 @@ static OpArg MPIC(const void* ptr)
VertexLoaderX64::VertexLoaderX64(const TVtxDesc& vtx_desc, const VAT& vtx_att)
: VertexLoaderBase(vtx_desc, vtx_att)
{
if (!IsInitialized())
return;
AllocCodeSpace(4096);
ClearCodeSpace();
GenerateVertexLoader();
WriteProtect();
const std::string name = ToString();
const std::string name =
fmt::format("VertexLoaderX64\nVtx desc: \n{}\nVAT:\n{}", vtx_desc, vtx_att);
JitRegister::Register(region, GetCodePtr(), name.c_str());
}
@ -420,7 +418,6 @@ void VertexLoaderX64::GenerateVertexLoader()
MOV(32, MPIC(VertexLoaderManager::position_matrix_index, count_reg, SCALE_4), R(scratch1));
SetJumpTarget(dont_store);
m_native_components |= VB_HAS_POSMTXIDX;
m_native_vtx_decl.posmtx.components = 4;
m_native_vtx_decl.posmtx.enable = true;
m_native_vtx_decl.posmtx.offset = m_dst_ofs;
@ -438,31 +435,27 @@ void VertexLoaderX64::GenerateVertexLoader()
}
OpArg data = GetVertexAddr(ARRAY_POSITION, m_VtxDesc.low.Position);
int pos_elements = m_VtxAttr.PosElements == CoordComponentCount::XY ? 2 : 3;
ReadVertex(data, m_VtxDesc.low.Position, m_VtxAttr.PosFormat, pos_elements, pos_elements,
m_VtxAttr.ByteDequant, m_VtxAttr.PosFrac, &m_native_vtx_decl.position);
int pos_elements = m_VtxAttr.g0.PosElements == CoordComponentCount::XY ? 2 : 3;
ReadVertex(data, m_VtxDesc.low.Position, m_VtxAttr.g0.PosFormat, pos_elements, pos_elements,
m_VtxAttr.g0.ByteDequant, m_VtxAttr.g0.PosFrac, &m_native_vtx_decl.position);
if (m_VtxDesc.low.Normal != VertexComponentFormat::NotPresent)
{
static const u8 map[8] = {7, 6, 15, 14};
const u8 scaling_exponent = map[u32(m_VtxAttr.NormalFormat)];
const int limit = m_VtxAttr.NormalElements == NormalComponentCount::NBT ? 3 : 1;
const u8 scaling_exponent = map[u32(m_VtxAttr.g0.NormalFormat.Value())];
const int limit = m_VtxAttr.g0.NormalElements == NormalComponentCount::NBT ? 3 : 1;
for (int i = 0; i < limit; i++)
{
if (!i || m_VtxAttr.NormalIndex3)
if (!i || m_VtxAttr.g0.NormalIndex3)
{
data = GetVertexAddr(ARRAY_NORMAL, m_VtxDesc.low.Normal);
int elem_size = GetElementSize(m_VtxAttr.NormalFormat);
int elem_size = GetElementSize(m_VtxAttr.g0.NormalFormat);
data.AddMemOffset(i * elem_size * 3);
}
data.AddMemOffset(ReadVertex(data, m_VtxDesc.low.Normal, m_VtxAttr.NormalFormat, 3, 3, true,
scaling_exponent, &m_native_vtx_decl.normals[i]));
data.AddMemOffset(ReadVertex(data, m_VtxDesc.low.Normal, m_VtxAttr.g0.NormalFormat, 3, 3,
true, scaling_exponent, &m_native_vtx_decl.normals[i]));
}
m_native_components |= VB_HAS_NRM0;
if (m_VtxAttr.NormalElements == NormalComponentCount::NBT)
m_native_components |= VB_HAS_NRM1 | VB_HAS_NRM2;
}
for (size_t i = 0; i < m_VtxDesc.low.Color.Size(); i++)
@ -470,8 +463,7 @@ void VertexLoaderX64::GenerateVertexLoader()
if (m_VtxDesc.low.Color[i] != VertexComponentFormat::NotPresent)
{
data = GetVertexAddr(ARRAY_COLOR0 + int(i), m_VtxDesc.low.Color[i]);
ReadColor(data, m_VtxDesc.low.Color[i], m_VtxAttr.color[i].Comp);
m_native_components |= VB_HAS_COL0 << i;
ReadColor(data, m_VtxDesc.low.Color[i], m_VtxAttr.GetColorFormat(i));
m_native_vtx_decl.colors[i].components = 4;
m_native_vtx_decl.colors[i].enable = true;
m_native_vtx_decl.colors[i].offset = m_dst_ofs;
@ -483,19 +475,17 @@ void VertexLoaderX64::GenerateVertexLoader()
for (size_t i = 0; i < m_VtxDesc.high.TexCoord.Size(); i++)
{
int elements = m_VtxAttr.texCoord[i].Elements == TexComponentCount::ST ? 2 : 1;
int elements = m_VtxAttr.GetTexElements(i) == TexComponentCount::ST ? 2 : 1;
if (m_VtxDesc.high.TexCoord[i] != VertexComponentFormat::NotPresent)
{
data = GetVertexAddr(ARRAY_TEXCOORD0 + int(i), m_VtxDesc.high.TexCoord[i]);
u8 scaling_exponent = m_VtxAttr.texCoord[i].Frac;
ReadVertex(data, m_VtxDesc.high.TexCoord[i], m_VtxAttr.texCoord[i].Format, elements,
m_VtxDesc.low.TexMatIdx[i] ? 2 : elements, m_VtxAttr.ByteDequant, scaling_exponent,
&m_native_vtx_decl.texcoords[i]);
m_native_components |= VB_HAS_UV0 << i;
u8 scaling_exponent = m_VtxAttr.GetTexFrac(i);
ReadVertex(data, m_VtxDesc.high.TexCoord[i], m_VtxAttr.GetTexFormat(i), elements,
m_VtxDesc.low.TexMatIdx[i] ? 2 : elements, m_VtxAttr.g0.ByteDequant,
scaling_exponent, &m_native_vtx_decl.texcoords[i]);
}
if (m_VtxDesc.low.TexMatIdx[i])
{
m_native_components |= VB_HAS_TEXMTXIDX0 << i;
m_native_vtx_decl.texcoords[i].components = 3;
m_native_vtx_decl.texcoords[i].enable = true;
m_native_vtx_decl.texcoords[i].type = VAR_FLOAT;
@ -546,7 +536,7 @@ void VertexLoaderX64::GenerateVertexLoader()
RET();
}
m_VertexSize = m_src_ofs;
ASSERT(m_vertex_size == m_src_ofs);
m_native_vtx_decl.stride = m_dst_ofs;
}

View File

@ -18,8 +18,6 @@ public:
VertexLoaderX64(const TVtxDesc& vtx_desc, const VAT& vtx_att);
protected:
std::string GetName() const override { return "VertexLoaderX64"; }
bool IsInitialized() override { return true; }
int RunVertices(DataReader src, DataReader dst, int count) override;
private:

View File

@ -5,6 +5,7 @@
#include <cstring>
#include "Common/CommonTypes.h"
#include "Common/MsgHandler.h"
#include "Common/Swap.h"
#include "VideoCommon/VertexLoader.h"
@ -136,7 +137,6 @@ void Color_ReadIndex_32b_8888(VertexLoader* loader)
(index * g_main_cp_state.array_strides[ARRAY_COLOR0 + loader->m_colIndex]);
SetCol(loader, Read32(address));
}
} // Anonymous namespace
void Color_ReadDirect_24b_888(VertexLoader* loader)
{
@ -149,10 +149,12 @@ void Color_ReadDirect_32b_888x(VertexLoader* loader)
SetCol(loader, Read24(DataGetPosition()));
DataSkip(4);
}
void Color_ReadDirect_16b_565(VertexLoader* loader)
{
SetCol565(loader, DataRead<u16>());
}
void Color_ReadDirect_16b_4444(VertexLoader* loader)
{
u16 value;
@ -161,62 +163,52 @@ void Color_ReadDirect_16b_4444(VertexLoader* loader)
SetCol4444(loader, value);
DataSkip(2);
}
void Color_ReadDirect_24b_6666(VertexLoader* loader)
{
SetCol6666(loader, Common::swap32(DataGetPosition() - 1));
DataSkip(3);
}
void Color_ReadDirect_32b_8888(VertexLoader* loader)
{
SetCol(loader, DataReadU32Unswapped());
}
void Color_ReadIndex8_16b_565(VertexLoader* loader)
constexpr TPipelineFunction s_table_read_color[4][6] = {
{nullptr, nullptr, nullptr, nullptr, nullptr, nullptr},
{Color_ReadDirect_16b_565, Color_ReadDirect_24b_888, Color_ReadDirect_32b_888x,
Color_ReadDirect_16b_4444, Color_ReadDirect_24b_6666, Color_ReadDirect_32b_8888},
{Color_ReadIndex_16b_565<u8>, Color_ReadIndex_24b_888<u8>, Color_ReadIndex_32b_888x<u8>,
Color_ReadIndex_16b_4444<u8>, Color_ReadIndex_24b_6666<u8>, Color_ReadIndex_32b_8888<u8>},
{Color_ReadIndex_16b_565<u16>, Color_ReadIndex_24b_888<u16>, Color_ReadIndex_32b_888x<u16>,
Color_ReadIndex_16b_4444<u16>, Color_ReadIndex_24b_6666<u16>, Color_ReadIndex_32b_8888<u16>},
};
constexpr u32 s_table_read_color_vertex_size[4][6] = {
{0, 0, 0, 0, 0, 0},
{2, 3, 4, 2, 3, 4},
{1, 1, 1, 1, 1, 1},
{2, 2, 2, 2, 2, 2},
};
} // Anonymous namespace
u32 VertexLoader_Color::GetSize(VertexComponentFormat type, ColorFormat format)
{
Color_ReadIndex_16b_565<u8>(loader);
if (format > ColorFormat::RGBA8888)
{
PanicAlertFmt("Invalid color format {}", format);
return 0;
}
void Color_ReadIndex8_24b_888(VertexLoader* loader)
{
Color_ReadIndex_24b_888<u8>(loader);
}
void Color_ReadIndex8_32b_888x(VertexLoader* loader)
{
Color_ReadIndex_32b_888x<u8>(loader);
}
void Color_ReadIndex8_16b_4444(VertexLoader* loader)
{
Color_ReadIndex_16b_4444<u8>(loader);
}
void Color_ReadIndex8_24b_6666(VertexLoader* loader)
{
Color_ReadIndex_24b_6666<u8>(loader);
}
void Color_ReadIndex8_32b_8888(VertexLoader* loader)
{
Color_ReadIndex_32b_8888<u8>(loader);
return s_table_read_color_vertex_size[u32(type)][u32(format)];
}
void Color_ReadIndex16_16b_565(VertexLoader* loader)
TPipelineFunction VertexLoader_Color::GetFunction(VertexComponentFormat type, ColorFormat format)
{
Color_ReadIndex_16b_565<u16>(loader);
}
void Color_ReadIndex16_24b_888(VertexLoader* loader)
if (format > ColorFormat::RGBA8888)
{
Color_ReadIndex_24b_888<u16>(loader);
PanicAlertFmt("Invalid color format {}", format);
return nullptr;
}
void Color_ReadIndex16_32b_888x(VertexLoader* loader)
{
Color_ReadIndex_32b_888x<u16>(loader);
}
void Color_ReadIndex16_16b_4444(VertexLoader* loader)
{
Color_ReadIndex_16b_4444<u16>(loader);
}
void Color_ReadIndex16_24b_6666(VertexLoader* loader)
{
Color_ReadIndex_24b_6666<u16>(loader);
}
void Color_ReadIndex16_32b_8888(VertexLoader* loader)
{
Color_ReadIndex_32b_8888<u16>(loader);
return s_table_read_color[u32(type)][u32(format)];
}

View File

@ -4,25 +4,15 @@
#pragma once
class VertexLoader;
#include "Common/CommonTypes.h"
#include "VideoCommon/VertexLoader.h"
void Color_ReadDirect_24b_888(VertexLoader* loader);
void Color_ReadDirect_32b_888x(VertexLoader* loader);
void Color_ReadDirect_16b_565(VertexLoader* loader);
void Color_ReadDirect_16b_4444(VertexLoader* loader);
void Color_ReadDirect_24b_6666(VertexLoader* loader);
void Color_ReadDirect_32b_8888(VertexLoader* loader);
enum class VertexComponentFormat;
enum class ColorFormat;
void Color_ReadIndex8_16b_565(VertexLoader* loader);
void Color_ReadIndex8_24b_888(VertexLoader* loader);
void Color_ReadIndex8_32b_888x(VertexLoader* loader);
void Color_ReadIndex8_16b_4444(VertexLoader* loader);
void Color_ReadIndex8_24b_6666(VertexLoader* loader);
void Color_ReadIndex8_32b_8888(VertexLoader* loader);
void Color_ReadIndex16_16b_565(VertexLoader* loader);
void Color_ReadIndex16_24b_888(VertexLoader* loader);
void Color_ReadIndex16_32b_888x(VertexLoader* loader);
void Color_ReadIndex16_16b_4444(VertexLoader* loader);
void Color_ReadIndex16_24b_6666(VertexLoader* loader);
void Color_ReadIndex16_32b_8888(VertexLoader* loader);
class VertexLoader_Color
{
public:
static u32 GetSize(VertexComponentFormat type, ColorFormat format);
static TPipelineFunction GetFunction(VertexComponentFormat type, ColorFormat format);
};

View File

@ -259,12 +259,19 @@ void LoadXFReg(u32 transferSize, u32 baseAddress, DataReader src)
}
}
constexpr std::tuple<u32, u32, u32> ExtractIndexedXF(u32 val)
{
const u32 index = val >> 16;
const u32 address = val & 0xFFF; // check mask
const u32 size = ((val >> 12) & 0xF) + 1;
return {index, address, size};
}
// TODO - verify that it is correct. Seems to work, though.
void LoadIndexedXF(u32 val, int refarray)
{
int index = val >> 16;
int address = val & 0xFFF; // check mask
int size = ((val >> 12) & 0xF) + 1;
const auto [index, address, size] = ExtractIndexedXF(val);
// load stuff from array to address in xf mem
u32* currData = (u32*)(&xfmem) + address;
@ -279,7 +286,7 @@ void LoadIndexedXF(u32 val, int refarray)
g_main_cp_state.array_strides[refarray] * index);
}
bool changed = false;
for (int i = 0; i < size; ++i)
for (u32 i = 0; i < size; ++i)
{
if (currData[i] != Common::swap32(newData[i]))
{
@ -290,15 +297,14 @@ void LoadIndexedXF(u32 val, int refarray)
}
if (changed)
{
for (int i = 0; i < size; ++i)
for (u32 i = 0; i < size; ++i)
currData[i] = Common::swap32(newData[i]);
}
}
void PreprocessIndexedXF(u32 val, int refarray)
{
const u32 index = val >> 16;
const u32 size = ((val >> 12) & 0xF) + 1;
const auto [index, address, size] = ExtractIndexedXF(val);
const u8* new_data = Memory::GetPointer(g_preprocess_cp_state.array_bases[refarray] +
g_preprocess_cp_state.array_strides[refarray] * index);
@ -643,3 +649,18 @@ std::pair<std::string, std::string> GetXFTransferInfo(const u8* data)
return std::make_pair(fmt::to_string(name), fmt::to_string(desc));
}
std::pair<std::string, std::string> GetXFIndexedLoadInfo(u8 array, u32 value)
{
const auto [index, address, size] = ExtractIndexedXF(value);
const auto desc = fmt::format("Load {} bytes to XF address {:03x} from CP array {} row {}", size,
address, array, index);
fmt::memory_buffer written;
for (u32 i = 0; i < size; i++)
{
fmt::format_to(written, "{}\n", GetXFMemName(address + i));
}
return std::make_pair(desc, fmt::to_string(written));
}

View File

@ -13,3 +13,4 @@ std::pair<std::string, std::string> GetXFRegInfo(u32 address, u32 value);
std::string GetXFMemName(u32 address);
std::string GetXFMemDescription(u32 address, u32 value);
std::pair<std::string, std::string> GetXFTransferInfo(const u8* data);
std::pair<std::string, std::string> GetXFIndexedLoadInfo(u8 array, u32 value);

View File

@ -62,7 +62,7 @@ protected:
void CreateAndCheckSizes(size_t input_size, size_t output_size)
{
m_loader = VertexLoaderBase::CreateVertexLoader(m_vtx_desc, m_vtx_attr);
ASSERT_EQ((int)input_size, m_loader->m_VertexSize);
ASSERT_EQ(input_size, m_loader->m_vertex_size);
ASSERT_EQ((int)output_size, m_loader->m_native_vtx_decl.stride);
}