mirror of
https://github.com/Liondarkz/issn_pc.git
synced 2026-11-07 12:47:50 -10:00
- Layout: src/ (loader, runtime), scripts/ (image preparation, patch compiler), tests/, tools/ (developer tools, fetch_fsr4_assets.sh, bridge helper, diagnostics), docs/history/ (dated reports); build.sh, run.sh, CMake, packaging, tools and tests follow the new paths. Raw run log removed. - Submodules: FSR-Vulkan points at upstream c64f093 and build.sh applies gpu/patches/fsr-vulkan to its working tree; LibAtrac9 registered in .gitmodules; build.sh initializes missing submodules. - No absolute home paths in scripts; the desktop entry is a template installed by launcher/install-desktop.sh. - Launcher: the save folder is chosen with a folder dialog (default shown, reset, open in the file manager), status icons that exist in Adwaita, upscaler asset status for FSR 4 / 4.1.1. - README.md / README.ru.md: what it is, status, highlights, differences from shadPS4, build/run, assets, layout, roadmap, credits. LICENSE: GPL-2.0. - tests/test_probe.py: expectations updated to the loader's current messages. Co-Authored-By: Claude Opus 5.5 <[email protected]>
114 lines
5.1 KiB
C++
114 lines
5.1 KiB
C++
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
// Standalone CPU regression checks: no game, Vulkan device or window required.
|
|
#include <array>
|
|
#include <cassert>
|
|
#include <cstdio>
|
|
#include "gpu/shadps4/video_core/renderer_vulkan/motion_history.h"
|
|
|
|
using namespace Vulkan::Motion;
|
|
|
|
int main() {
|
|
const std::array<uint16_t, 5> indices{1000, 1007, 1002, 1007, UINT16_MAX};
|
|
const auto range = IndexedRange(std::span(indices), -900, true);
|
|
assert(range.first == 100 && range.count == 8);
|
|
assert(IndexedRange(std::span(indices), -1001, true).count == 0);
|
|
assert(IndexedRange(std::span(indices), 0, false).count == 64536);
|
|
const std::array<uint32_t, 3> large{100000, 100003, UINT32_MAX};
|
|
assert(IndexedRange(std::span(large), 7, true).count == 4);
|
|
assert(IndexedRange(std::span(large), INT32_MAX, true).count == 0);
|
|
const std::array<uint16_t, 1> restart{UINT16_MAX};
|
|
assert(IndexedRange(std::span(restart), 0, true).count == 0);
|
|
assert(IndexedRange(std::span<const uint16_t>{}, 0, false).count == 0);
|
|
|
|
Draw draw{.shader = 1, .geometry = 2, .indices = 3, .topology = 4,
|
|
.index_count = 5, .instances = 2, .first_instance = 100,
|
|
.vertices = range};
|
|
History history(32);
|
|
history.NextFrame();
|
|
const auto first = history.Prepare(draw);
|
|
const auto duplicate = history.Prepare(draw);
|
|
assert(first.store && !first.load && first.first_vertex == 100);
|
|
assert(first.first_instance == 100 && first.instances == 2);
|
|
assert(duplicate.store == first.store + 16); // shared geometry, different draw
|
|
assert(history.Used() == 32); // firstInstance must NOT multiply the allocation
|
|
assert(!history.Prepare(draw).store && history.stats.exhausted == 1);
|
|
history.NextFrame();
|
|
const auto next = history.Prepare(draw);
|
|
assert(next.load == first.store && next.store != first.store);
|
|
assert(history.Prepare(draw).load == duplicate.store);
|
|
history.NextFrame(); // a missing frame invalidates the match
|
|
history.NextFrame();
|
|
assert(!history.Prepare(draw).load);
|
|
|
|
// A changed index buffer may reference formerly unused holes inside the same min/max.
|
|
// Do not accept that history merely because its addresses/counts stayed the same.
|
|
history.NextFrame();
|
|
auto changed = draw;
|
|
changed.topology++;
|
|
assert(!history.Prepare(changed).load);
|
|
changed = draw;
|
|
changed.first_instance++;
|
|
assert(!history.Prepare(changed).load);
|
|
history.NextFrame();
|
|
changed = draw;
|
|
changed.vertices.first++;
|
|
assert(!history.Prepare(changed).load);
|
|
|
|
History overflow(32);
|
|
draw.vertices.first = 0;
|
|
draw.vertices.count = INT32_MAX;
|
|
draw.instances = UINT32_MAX;
|
|
draw.first_instance = 0;
|
|
assert(!overflow.Prepare(draw).store);
|
|
assert(overflow.stats.exhausted == 1);
|
|
draw.first_instance = 10;
|
|
assert(!overflow.Prepare(draw).store && overflow.stats.invalid == 1);
|
|
draw.first_instance = 0;
|
|
draw.vertices.first = INT32_MAX;
|
|
draw.vertices.count = 2;
|
|
assert(!overflow.Prepare(draw).store && overflow.stats.invalid == 2);
|
|
|
|
// Small skeletons: history only while the bone palette changes (weapon spinning).
|
|
History gated(1024);
|
|
const History::GateKey weapon{.shader = 7, .index_count = 3, .instances = 1};
|
|
const auto frame = [&](uint64_t palette) {
|
|
gated.NextFrame();
|
|
return gated.Moving(weapon, palette);
|
|
};
|
|
assert(!gated.Moving(weapon, 1) && gated.stats.still == 1); // first sight
|
|
assert(!frame(1)); // same bones: camera motion
|
|
assert(frame(2) && frame(3)); // spinning
|
|
assert(!frame(3)); // stopped
|
|
gated.NextFrame();
|
|
gated.NextFrame();
|
|
assert(!gated.Moving(weapon, 4)); // after a skipped frame: no comparison
|
|
assert(!gated.Moving(weapon, 5)); // second occurrence of the same draw
|
|
assert(ClassifyBuffer(864) == BufferRole::Other && ClassifyBuffer(416) == BufferRole::Other);
|
|
assert(ClassifyBuffer(96) == BufferRole::SmallSkeleton &&
|
|
ClassifyBuffer(384) == BufferRole::SmallSkeleton);
|
|
assert(ClassifyBuffer(656) == BufferRole::Skeleton && ClassifyBuffer(64) == BufferRole::Other);
|
|
|
|
// Index ranges: reused between full scans, rescanned every Revalidate frames, trimmed.
|
|
IndexRangeCache ranges;
|
|
uint32_t scans = 0;
|
|
const IndexRangeCache::Key mesh{.address = 0x1000, .count = 3, .index_size = 2};
|
|
const auto scan = [&] {
|
|
++scans;
|
|
return IndexRangeCache::Result{{4, 3}, 99};
|
|
};
|
|
assert(ranges.Get(mesh, 0, scan).topology == 99 && scans == 1);
|
|
assert(ranges.Get(mesh, IndexRangeCache::Revalidate - 1, scan).range == (VertexRange{4, 3}));
|
|
assert(scans == 1);
|
|
ranges.Get(mesh, IndexRangeCache::Revalidate, scan);
|
|
assert(scans == 2);
|
|
ranges.Trim(IndexRangeCache::Revalidate + IndexRangeCache::Unused + 1);
|
|
assert(ranges.Size() == 0);
|
|
|
|
assert(JitterPhases(1920, 1920) == 8);
|
|
assert(JitterPhases(1280, 1920) == 18);
|
|
assert(JitterPhases(960, 1920) == 32);
|
|
assert(JitterPhases(640, 1920) == 72);
|
|
assert(JitterPhases(0, 1920) == 8);
|
|
std::puts("Motion history: PASS (ranges, restart, offsets, matching, capacity, gating, index cache, jitter)");
|
|
}
|