Merge pull request #4844 from johnmaia/debug/skip-shader

feat(agc): skip the draws and dispatches of one shader with APS5_SKIP_SHADER
This commit is contained in:
Dean 2026-10-14 15:32:04 +13:00 committed by GitHub
commit 2ba3147ac8
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 82 additions and 0 deletions

View File

@ -8,6 +8,8 @@ namespace AgcDriver::DriverDetail {
double TraceMs();
std::uint64_t SkippedShader();
void require(bool condition, const char* reason);
std::uint32_t readRegister(const Registers& registers, std::uint32_t offset);

View File

@ -1,5 +1,6 @@
#include "prx/libSceAgcDriver/Execution/include/Driver/Driver.hpp"
#include "prx/libSceAgcDriver/Execution/include/Driver/Diagnostics.hpp"
#include <cstdlib>
#include <stdexcept>
namespace AgcDriver::DriverDetail {
@ -9,6 +10,11 @@ double TraceMs() {
return std::chrono::duration<double, std::milli>(std::chrono::steady_clock::now() - origin).count();
}
std::uint64_t SkippedShader() {
static const std::uint64_t skipped = [] { const char* text = std::getenv("APS5_SKIP_SHADER"); return text ? std::strtoull(text, nullptr, 16) : 0ull; }();
return skipped;
}
void require(bool condition, const char* reason) {
if (!condition) {
throw std::runtime_error(std::string("AGC driver: ") + reason);

View File

@ -16,6 +16,7 @@ namespace AgcDriver::DriverDetail {
void Driver::dispatch(QueueState& queue, std::span<const std::uint32_t> packet, const Submission& submission, std::uint64_t indirectArguments) {
PerformanceTimer timing("Driver.Dispatch");
const auto address = (static_cast<std::uint64_t>(readRegister(queue.shader, 0x20c)) << 8u) | (static_cast<std::uint64_t>(readRegister(queue.shader, 0x20d) & 0xffu) << 40u);
if (address == SkippedShader() && address != 0) return;
auto registeredShader = RegisteredProgram(*submission.shaders, address, {0});
if (!registeredShader) registeredShader = ReadRawShader(address);
const auto& snapshot = *registeredShader;

View File

@ -4,6 +4,7 @@
#include "prx/libSceAgcDriver/Execution/include/PerformanceTimer.hpp"
#include "prx/libSceAgcDriver/Execution/include/Pm4.hpp"
#include "Optimization/ResourceProgram.hpp"
#include <algorithm>
#include <cstdlib>
namespace AgcDriver::DriverDetail {
@ -94,6 +95,7 @@ DrawVerdict Driver::draw(QueueState& queue, std::span<const std::uint32_t> packe
const auto& graphics = decode->state;
auto pixel = decode->pixel;
std::vector<DrawProgram> programs = decode->programs;
if (const auto skipped = SkippedShader(); skipped != 0 && std::any_of(programs.begin(), programs.end(), [skipped](const DrawProgram& program) { return program.binary.codeAddress == skipped; })) return drawn();
const auto setMeshIndexBuffer = [&](const Pm4::DrawParameters& parameters) {
if (!graphics.stages.mesh) return;
auto& words = programs.front().userData;

View File

@ -0,0 +1,71 @@
#include "prx/libSceAgcDriver/Execution/include/Driver.hpp"
#include "prx/libSceAgcDriver/Execution/include/GuestMemory.hpp"
#include "prx/libSceAgcDriver/Graphics/include/Draw.hpp"
#include "VulkanTestDevice.hpp"
#include <array>
#include <cstdio>
#include <cstdlib>
#include <iostream>
#include <string>
#include <vector>
namespace {
using AgcDriver::Graphics::Require;
alignas(256) constexpr std::array<std::uint32_t, 7> Skipped{0xd7460000u, 0x04010c04u, 0x7e0202ffu, 0x12345678u, 0xe0702000u, 0x80000100u, 0xbf810000u};
alignas(256) constexpr std::array<std::uint32_t, 7> Kept{0xd7460000u, 0x04010c04u, 0x7e0202ffu, 0x12345678u, 0xe0702000u, 0x80000100u, 0xbf810000u};
constexpr std::uint32_t Untouched = 0xcafebabeu;
constexpr std::uint32_t Stored = 0x12345678u;
alignas(256) std::array<std::uint32_t, 64> SkippedOutput{};
alignas(256) std::array<std::uint32_t, 64> KeptOutput{};
void Dispatch(const std::array<std::uint32_t, 7>& code, std::array<std::uint32_t, 64>& output) {
const auto address = reinterpret_cast<std::uintptr_t>(output.data());
const std::array<std::uint32_t, 4> data{static_cast<std::uint32_t>(address), static_cast<std::uint32_t>((address >> 32u) & 0xffffu) | (4u << 16u), static_cast<std::uint32_t>(output.size()), 0x00014204u};
const auto program = reinterpret_cast<std::uintptr_t>(code.data());
std::vector<std::uint32_t> words{
0xc0037600u, 0x207, 64, 1, 1,
0xc0027600u, 0x20c, static_cast<std::uint32_t>(program >> 8u), static_cast<std::uint32_t>(program >> 40u),
0xc0017600u, 0x213, (4u << 1u) | (1u << 7u),
0xc0047600u, 0x240, data[0], data[1], data[2], data[3],
0xc0031500u, 1, 1, 1, 0x41u,
};
Packet packet{words.data(), static_cast<std::uint32_t>(words.size()), 0, {}};
AgcDriver::Submit(&packet, 0);
AgcDriverWaitIdle_nid_postfix();
AgcDriver::GuestMemory::FlushGpuWrites(address, sizeof(output));
}
}
int main() {
try {
char value[32];
std::snprintf(value, sizeof(value), "%llx", static_cast<unsigned long long>(reinterpret_cast<std::uintptr_t>(Skipped.data())));
#ifdef _WIN32
Require(_putenv_s("APS5_SKIP_SHADER", value) == 0, "cannot set APS5_SKIP_SHADER");
#else
Require(setenv("APS5_SKIP_SHADER", value, 1) == 0, "cannot set APS5_SKIP_SHADER");
#endif
{
const auto device = OpenVulkanTestDevice();
if (!device) return VulkanTestSkipped;
}
SkippedOutput.fill(Untouched);
KeptOutput.fill(Untouched);
Dispatch(Skipped, SkippedOutput);
Dispatch(Kept, KeptOutput);
for (std::size_t i = 0; i < SkippedOutput.size(); ++i) {
Require(SkippedOutput[i] == Untouched, "the dispatch of the skipped shader stored dword " + std::to_string(i));
Require(KeptOutput[i] == Stored, "the dispatch of another shader did not store dword " + std::to_string(i));
}
AgcDriverShutdown_nid_postfix();
std::puts("skip shader tests passed");
return 0;
} catch (const std::exception& error) {
std::cerr << error.what() << '\n';
AgcDriverShutdown_nid_postfix();
return 1;
}
}