mirror of
https://github.com/izzy2lost/dolphin.git
synced 2026-06-19 01:16:48 -07:00
Merge pull request #9540 from Pokechu22/better-fifo-analyzer-part-2
Fifo analyzer quality of life improvements
This commit is contained in:
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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,9 +90,14 @@ 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;
|
||||
@@ -98,5 +105,8 @@ void FifoPlaybackAnalyzer::AnalyzeFrames(FifoDataFile* file,
|
||||
|
||||
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());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -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); });
|
||||
}
|
||||
|
||||
@@ -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
|
||||
{
|
||||
TexComponentCount Elements;
|
||||
ComponentFormat Format;
|
||||
u8 Frac;
|
||||
constexpr ColorComponentCount GetColorElements(size_t idx) const
|
||||
{
|
||||
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
|
||||
|
||||
@@ -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;
|
||||
WriteCall(TexMtx_ReadDirect_UByte);
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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];
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
memcpy(&m_native_vtx_decl, &a->m_native_vtx_decl, sizeof(PortableVertexDeclaration));
|
||||
}
|
||||
else
|
||||
{
|
||||
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
|
||||
loader = std::make_unique<VertexLoader>(vtx_desc, vtx_attr);
|
||||
if (loader->IsInitialized())
|
||||
return loader;
|
||||
// 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);
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user