diff --git a/core/libs/prx/libSceAgcDriver/Execution/include/Driver/Diagnostics.hpp b/core/libs/prx/libSceAgcDriver/Execution/include/Driver/Diagnostics.hpp index 3c0093fbe..3667d7bfd 100644 --- a/core/libs/prx/libSceAgcDriver/Execution/include/Driver/Diagnostics.hpp +++ b/core/libs/prx/libSceAgcDriver/Execution/include/Driver/Diagnostics.hpp @@ -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); diff --git a/core/libs/prx/libSceAgcDriver/Execution/src/Driver/Diagnostics.cpp b/core/libs/prx/libSceAgcDriver/Execution/src/Driver/Diagnostics.cpp index 59d038112..3e80f40e9 100644 --- a/core/libs/prx/libSceAgcDriver/Execution/src/Driver/Diagnostics.cpp +++ b/core/libs/prx/libSceAgcDriver/Execution/src/Driver/Diagnostics.cpp @@ -1,5 +1,6 @@ #include "prx/libSceAgcDriver/Execution/include/Driver/Driver.hpp" #include "prx/libSceAgcDriver/Execution/include/Driver/Diagnostics.hpp" +#include #include namespace AgcDriver::DriverDetail { @@ -9,6 +10,11 @@ double TraceMs() { return std::chrono::duration(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); diff --git a/core/libs/prx/libSceAgcDriver/Execution/src/Driver/Dispatch/Dispatch.cpp b/core/libs/prx/libSceAgcDriver/Execution/src/Driver/Dispatch/Dispatch.cpp index a6da3ceb5..7719b40f2 100644 --- a/core/libs/prx/libSceAgcDriver/Execution/src/Driver/Dispatch/Dispatch.cpp +++ b/core/libs/prx/libSceAgcDriver/Execution/src/Driver/Dispatch/Dispatch.cpp @@ -16,6 +16,7 @@ namespace AgcDriver::DriverDetail { void Driver::dispatch(QueueState& queue, std::span packet, const Submission& submission, std::uint64_t indirectArguments) { PerformanceTimer timing("Driver.Dispatch"); const auto address = (static_cast(readRegister(queue.shader, 0x20c)) << 8u) | (static_cast(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; diff --git a/core/libs/prx/libSceAgcDriver/Execution/src/Driver/Draw/Draw.cpp b/core/libs/prx/libSceAgcDriver/Execution/src/Driver/Draw/Draw.cpp index e51868b6a..1ac53eebb 100644 --- a/core/libs/prx/libSceAgcDriver/Execution/src/Driver/Draw/Draw.cpp +++ b/core/libs/prx/libSceAgcDriver/Execution/src/Driver/Draw/Draw.cpp @@ -4,6 +4,7 @@ #include "prx/libSceAgcDriver/Execution/include/PerformanceTimer.hpp" #include "prx/libSceAgcDriver/Execution/include/Pm4.hpp" #include "Optimization/ResourceProgram.hpp" +#include #include namespace AgcDriver::DriverDetail { @@ -94,6 +95,7 @@ DrawVerdict Driver::draw(QueueState& queue, std::span packe const auto& graphics = decode->state; auto pixel = decode->pixel; std::vector 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; diff --git a/core/libs/prx/libSceAgcDriver/tests/execution/SkipShader.cpp b/core/libs/prx/libSceAgcDriver/tests/execution/SkipShader.cpp new file mode 100644 index 000000000..377e819b4 --- /dev/null +++ b/core/libs/prx/libSceAgcDriver/tests/execution/SkipShader.cpp @@ -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 +#include +#include +#include +#include +#include + +namespace { + +using AgcDriver::Graphics::Require; + +alignas(256) constexpr std::array Skipped{0xd7460000u, 0x04010c04u, 0x7e0202ffu, 0x12345678u, 0xe0702000u, 0x80000100u, 0xbf810000u}; +alignas(256) constexpr std::array Kept{0xd7460000u, 0x04010c04u, 0x7e0202ffu, 0x12345678u, 0xe0702000u, 0x80000100u, 0xbf810000u}; +constexpr std::uint32_t Untouched = 0xcafebabeu; +constexpr std::uint32_t Stored = 0x12345678u; +alignas(256) std::array SkippedOutput{}; +alignas(256) std::array KeptOutput{}; + +void Dispatch(const std::array& code, std::array& output) { + const auto address = reinterpret_cast(output.data()); + const std::array data{static_cast(address), static_cast((address >> 32u) & 0xffffu) | (4u << 16u), static_cast(output.size()), 0x00014204u}; + const auto program = reinterpret_cast(code.data()); + std::vector words{ + 0xc0037600u, 0x207, 64, 1, 1, + 0xc0027600u, 0x20c, static_cast(program >> 8u), static_cast(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(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(reinterpret_cast(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; + } +}