Compare commits

...

2 Commits

Author SHA1 Message Date
TJnotJT
a1173c53d3 GS/HW: Reduce barriers in triangle strips/fans.
Account for triangles forming non-overlapping quads in triangles strips/fans.
2026-01-26 11:57:20 +01:00
JordanTheToaster
ac0deff9b2 Memcard: Purge automatic save management 2026-01-26 03:35:44 +01:00
8 changed files with 90 additions and 44 deletions

View File

@@ -40,7 +40,6 @@ MemoryCardSettingsWidget::MemoryCardSettingsWidget(SettingsWindow* settings_dial
SettingWidgetBinder::BindWidgetToFolderSetting(sif, m_ui.directory, m_ui.browse, m_ui.open, m_ui.reset, "Folders",
"MemoryCards", Path::Combine(EmuFolders::DataRoot, "memcards"));
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.automaticManagement, "EmuCore", "McdFolderAutoManage", true);
setupAdditionalUi();
@@ -58,11 +57,6 @@ MemoryCardSettingsWidget::MemoryCardSettingsWidget(SettingsWindow* settings_dial
connect(m_ui.deleteCard, &QPushButton::clicked, this, &MemoryCardSettingsWidget::deleteCard);
refresh();
dialog()->registerWidgetHelp(m_ui.automaticManagement, tr("Automatically manage saves based on running game"),
tr("Checked"),
tr("(Folder type only / Card size: Auto) Loads only the relevant booted game saves, ignoring others. Avoids "
"running out of space for saves."));
}
MemoryCardSettingsWidget::~MemoryCardSettingsWidget() = default;

View File

@@ -9,6 +9,7 @@
#include <QtWidgets/QTreeWidget>
#include <QtWidgets/QToolButton>
#include <QtWidgets/QListWidget>
#include <QtWidgets/QCheckBox>
#include <array>
#include <optional>

View File

@@ -175,22 +175,6 @@
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="settingsGroupBox">
<property name="title">
<string>Settings</string>
</property>
<layout class="QGridLayout" name="gridLayout_3">
<item row="0" column="0">
<widget class="QCheckBox" name="automaticManagement">
<property name="text">
<string>Automatically manage saves based on running game</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
<customwidgets>
@@ -211,7 +195,6 @@
<tabstop>renameCard</tabstop>
<tabstop>convertCard</tabstop>
<tabstop>deleteCard</tabstop>
<tabstop>automaticManagement</tabstop>
</tabstops>
<resources/>
<connections/>

View File

@@ -1326,7 +1326,6 @@ struct Pcsx2Config
UseSavestateSelector : 1,
InhibitScreensaver : 1,
BackupSavestate : 1,
McdFolderAutoManage : 1,
ManuallySetRealTimeClock : 1, // passes user-set real-time clock information to cdvd at startup
UseSystemLocaleFormat : 1, // presents OS time format instead of yyyy-MM-dd HH:mm:ss for manual RTC

View File

@@ -3755,7 +3755,7 @@ GSState::PRIM_OVERLAP GSState::GetPrimitiveOverlapDrawlistImpl(bool save_drawlis
}
PRIM_OVERLAP overlap = PRIM_OVERLAP_NO;
bool check_quads = primclass == GS_TRIANGLE_CLASS;
bool check_quads = (primclass == GS_TRIANGLE_CLASS);
u32 i = 0;
u32 skip = 0; // Number of indices to skip if we have the bbox from the previous iteration.
@@ -3766,11 +3766,85 @@ GSState::PRIM_OVERLAP GSState::GetPrimitiveOverlapDrawlistImpl(bool save_drawlis
{
u32 j = i + skip;
skip = 0;
GSVector4i bbox(INT_MAX, INT_MAX, -INT_MAX, -INT_MAX);
while (j < count)
{
if (check_quads && j + 3 < count)
bool got_bbox = false;
// Assuming that indices 0-5 represent two triangles:
// Triangle strips: indices 1, 2 are identical to indices 3, 4. Indices 0, 5 are different.
// Triangles fans: indices 0, 2 are identical to indices 3, 4. Indices 1, 5 are different.
// Warning: this depends on how the vertices are arranged in the vertex kick.
// if that changes this detection will break.
constexpr std::array<std::array<std::array<int, 3>, 2>, 2> tri_order({
// Triangle strip expected indices.
std::array{ std::array<int, 3>{ 1, 2, 0 }, std::array<int, 3>{ 3, 4, 5 } },
// Triangle fan expected indices.
std::array{ std::array<int, 3>{ 0, 2, 1 }, std::array<int, 3>{ 3, 4, 5 } },
});
const auto CheckTriangleQuads = [&]<int type>() {
constexpr std::array<int, 3> tri0 = tri_order[type][0];
constexpr std::array<int, 3> tri1 = tri_order[type][1];
if (!got_bbox && primclass == GS_TRIANGLE_CLASS && j + 3 < count &&
index[j + tri0[0]] == index[j + tri1[0]] &&
index[j + tri0[1]] == index[j + tri1[1]])
{
// Get the initial triangle bbox.
bbox = GSVector4i(v[index[j + 0]].m[1]).upl16().xyxy();
bbox = bbox.runion(GSVector4i(v[index[j + 1]].m[1]).upl16().xyxy());
bbox = bbox.runion(GSVector4i(v[index[j + 2]].m[1]).upl16().xyxy());
while (true)
{
// There is a shared edge between this and next triangle.
// Check if the unshared point is on opposite sides of the shared edge.
const GSVector4i shared0 = GSVector4i(v[index[j + skip + tri0[0]]].m[1]).upl16();
const GSVector4i shared1 = GSVector4i(v[index[j + skip + tri0[1]]].m[1]).upl16() - shared0;
const GSVector4i unshared0 = GSVector4i(v[index[j + skip + tri0[2]]].m[1]).upl16() - shared0;
const GSVector4i unshared1 = GSVector4i(v[index[j + skip + tri1[2]]].m[1]).upl16() - shared0;
// Cross product signs comparison.
if ((unshared0.x * shared1.y - unshared0.y * shared1.x >= 0) ==
(unshared1.x * shared1.y - unshared1.y * shared1.x >= 0))
{
break; // Triangles are on same side of the shared edge so there's overlap.
}
// Corners are on opposite sides so we can assume a non-axis-aligned quad.
// Take union with the single unshared point.
bbox = bbox.runion(GSVector4i(v[index[j + skip + tri1[2]]].m[1]).upl16().xyxy());
skip += 3;
got_bbox = true;
if (!(j + skip + 3 < count &&
index[j + skip + tri0[0]] == index[j + skip + tri1[0]] &&
index[j + skip + tri0[1]] == index[j + skip + tri1[1]]))
{
// Cannot continue the strip/fan. Consume the last triangle and exit.
skip += 3;
break;
}
}
}
};
// First check: see if the triangles are part of triangle strip/fan.
if (primclass == GS_TRIANGLE_CLASS)
{
CheckTriangleQuads.template operator()<0>(); // Check triangle strips.
CheckTriangleQuads.template operator()<1>(); // Check triangle fans.
}
// Second check: see if triangles form an axis-aligned quad.
if (!got_bbox && primclass == GS_TRIANGLE_CLASS && check_quads && j + 3 < count)
{
const u16* RESTRICT idx0 = &index[j + 0];
const u16* RESTRICT idx1 = &index[j + 3];
@@ -3780,31 +3854,30 @@ GSState::PRIM_OVERLAP GSState::GetPrimitiveOverlapDrawlistImpl(bool save_drawlis
if (AreTrianglesQuad<0, 0>(v, idx0, idx1, &tri0, &tri1))
{
// tri.b is right angle corner
GSVector4i corner0 = GSVector4i(v[idx0[tri0.b]].m[1]).upl16().xyxy();
GSVector4i corner1 = GSVector4i(v[idx1[tri1.b]].m[1]).upl16().xyxy();
bbox = corner0.runion(corner1);
bbox = GSVector4i(v[idx0[tri0.b]].m[1]).upl16().xyxy();
bbox = bbox.runion(GSVector4i(v[idx1[tri1.b]].m[1]).upl16().xyxy());
skip = 6;
got_bbox = true;
}
else
{
bbox = GSVector4i(v[index[j + 0]].m[1]).upl16().xyxy();
bbox = bbox.runion(GSVector4i(v[index[j + 1]].m[1]).upl16().xyxy());
bbox = bbox.runion(GSVector4i(v[index[j + 2]].m[1]).upl16().xyxy());
skip = 3;
// If we fail a quad check assume the rest are not quads.
// If we fail a quad check assume the rest are not quads since the check is relatively expensive.
check_quads = false;
}
}
else
// Default case: just take the bbox of the prim vertices.
if (!got_bbox)
{
bbox = GSVector4i(v[GetIndex(j)].m[1]).upl16().xyxy();
for (int k = 1; k < n; k++) // Unroll
bbox = bbox.runion(GSVector4i(v[GetIndex(j + k)].m[1]).upl16().xyxy());
skip = n;
got_bbox = true;
}
// Avoid degenerate bbox.

View File

@@ -1923,7 +1923,6 @@ Pcsx2Config::Pcsx2Config()
{
bitset = 0;
// Set defaults for fresh installs / reset settings
McdFolderAutoManage = true;
EnablePatches = true;
EnableFastBoot = true;
EnableRecordingTools = true;
@@ -1979,7 +1978,6 @@ void Pcsx2Config::LoadSaveCore(SettingsWrapper& wrap)
SettingsWrapBitBool(HostFs);
SettingsWrapBitBool(BackupSavestate);
SettingsWrapBitBool(McdFolderAutoManage);
SettingsWrapBitBool(WarnAboutUnsafeSettings);

View File

@@ -611,7 +611,7 @@ void FileMcd_EmuOpen()
Mcd::impl.Open();
Mcd::implFolder.SetFiltering(EmuConfig.McdFolderAutoManage);
Mcd::implFolder.SetFiltering(true);
Mcd::implFolder.Open();
}
@@ -823,7 +823,7 @@ int FileMcd_ReIndex(uint port, uint slot, const std::string& filter)
// return Mcd::impl.ReIndex( combinedSlot, filter );
// break;
case MemoryCardType::Folder:
if (!Mcd::implFolder.ReIndex(combinedSlot, EmuConfig.McdFolderAutoManage, filter))
if (!Mcd::implFolder.ReIndex(combinedSlot, true, filter))
return -1;
break;
default:
@@ -930,7 +930,7 @@ std::vector<AvailableMcdInfo> FileMcd_GetAvailableCards(bool include_in_use_card
Pcsx2Config::McdOptions config;
config.Enabled = true;
config.Type = MemoryCardType::Folder;
sourceFolderMemoryCard.Open(fd.FileName, config, (8 * 1024 * 1024) / FolderMemoryCard::ClusterSize, EmuConfig.McdFolderAutoManage, "");
sourceFolderMemoryCard.Open(fd.FileName, config, (8 * 1024 * 1024) / FolderMemoryCard::ClusterSize, true, "");
mcds.push_back({std::move(basename), std::move(fd.FileName), fd.ModificationTime,
MemoryCardType::Folder, MemoryCardFileType::Unknown, 0u, sourceFolderMemoryCard.IsFormatted()});

View File

@@ -2993,8 +2993,6 @@ void VMManager::CheckForMemoryCardConfigChanges(const Pcsx2Config& old_config)
}
}
changed |= (EmuConfig.McdFolderAutoManage != old_config.McdFolderAutoManage);
if (!changed)
return;