fix(diagnostics): capture failed shader preparation requests
This commit is contained in:
parent
23ed501f4a
commit
097aaa698b
|
|
@ -907,6 +907,10 @@ if(BUILD_TESTING)
|
|||
set_tests_properties(${preparedTest} PROPERTIES ENVIRONMENT_MODIFICATION "PATH=path_list_prepend:$<TARGET_FILE_DIR:libc>")
|
||||
endif()
|
||||
endforeach()
|
||||
set_tests_properties(agc_driver_prepared_shaders
|
||||
agc_driver_prepared_shaders_indirect agc_driver_prepared_shaders_uncached
|
||||
agc_driver_prepared_shaders_debug agc_driver_prepared_shaders_debug_failure
|
||||
PROPERTIES RESOURCE_LOCK shader_failure_capture)
|
||||
endif()
|
||||
|
||||
option(AGC_BUILD_VISUAL_TEST "Enable the standalone AGC SPIR-V visual test" OFF)
|
||||
|
|
|
|||
|
|
@ -62,6 +62,7 @@ public:
|
|||
void ResolveGraphicsAbi(const Shader* vertex, const Shader* pixel, std::uint32_t primitiveType);
|
||||
static void FoldDrawOffsets(const ShaderRecompiler::RecompileResult& result, const DrawProgram& program, Pm4::DrawParameters& parameters);
|
||||
static std::optional<Graphics::IndirectDrawPath> ClassifyIndirectDraw(const ShaderRecompiler::RecompileResult& result, const Graphics::State& graphics, const DrawProgram& frontProgram, const std::shared_ptr<VulkanDevice>& localDevice, Pm4::DrawParameters& drawParameters, bool traceIndirect);
|
||||
static std::string dumpRequest(std::uint64_t address, const ShaderRecompiler::RecompileRequest& request);
|
||||
|
||||
private:
|
||||
friend class SampledReadScope;
|
||||
|
|
@ -136,7 +137,6 @@ private:
|
|||
static void dumpPackets(std::span<const std::uint32_t> commands, const std::uint32_t* guest = nullptr);
|
||||
static void validate(const Submission& submission, const std::uint32_t* guest = nullptr);
|
||||
static void reportSkip(const char* kind, const std::string& what);
|
||||
static std::string dumpRequest(std::uint64_t address, const ShaderRecompiler::RecompileRequest& request);
|
||||
static bool matchesFillKernel(std::span<const std::uint32_t> code, const std::vector<std::uint32_t>& userData, const ShaderRecompiler::ShaderComputeStageInfo& compute);
|
||||
static bool fillClearEnabled();
|
||||
static bool fillClearExactOnly();
|
||||
|
|
|
|||
|
|
@ -14,6 +14,10 @@
|
|||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace ShaderRecompiler {
|
||||
class ShaderPreparationContext;
|
||||
}
|
||||
|
||||
namespace AgcDriver::DriverDetail {
|
||||
|
||||
struct ShaderSnapshot;
|
||||
|
|
@ -71,6 +75,8 @@ struct ShaderSnapshot {
|
|||
|
||||
std::shared_ptr<const ShaderSnapshot> ReadRawComputeShader(std::uint64_t address);
|
||||
|
||||
std::shared_ptr<const ShaderRecompiler::SourceHandle> PrepareShaderWithDiagnostics(const ShaderRecompiler::RecompileRequest& request, ShaderRecompiler::ShaderPreparationContext* preparation = nullptr);
|
||||
|
||||
std::uint64_t NullPixelProgramAddress();
|
||||
std::optional<ShaderRecompiler::ShaderFloatMode> RegisteredFloatMode(const ShaderSnapshot& snapshot);
|
||||
void PublishRegisteredShader(std::shared_ptr<ShaderRegistry>& registry, const std::shared_ptr<const ShaderSnapshot>& snapshot);
|
||||
|
|
|
|||
|
|
@ -20,6 +20,17 @@
|
|||
|
||||
namespace AgcDriver::DriverDetail {
|
||||
|
||||
std::shared_ptr<const ShaderRecompiler::SourceHandle> PrepareShaderWithDiagnostics(const ShaderRecompiler::RecompileRequest& request, ShaderRecompiler::ShaderPreparationContext* preparation) {
|
||||
try {
|
||||
return ShaderRecompiler::PrepareShader(request, preparation);
|
||||
} catch (...) {
|
||||
try {
|
||||
if (std::getenv("APS5_DUMP_SHADERS") != nullptr) static_cast<void>(Driver::dumpRequest(request.shader.codeAddress, request));
|
||||
} catch (...) {}
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<const ShaderSnapshot> ReadRawComputeShader(std::uint64_t address) {
|
||||
GuestMemory::CheckRange(reinterpret_cast<const void*>(address), sizeof(std::uint32_t), 256);
|
||||
static std::mutex cacheMutex;
|
||||
|
|
@ -181,7 +192,7 @@ std::shared_ptr<const ShaderRecompiler::SourceHandle> SourceHandleFor(const Shad
|
|||
if (snapshot.type != 0 || request.shader.stage != ShaderRecompiler::ShaderStage::Compute) throw std::runtime_error("AGC driver: unregistered program is not a compute shader");
|
||||
APS5_LOG_ERR("Compute shader 0x%llx was not registered; preparing its artifact at dispatch", static_cast<unsigned long long>(snapshot.codeAddress));
|
||||
}
|
||||
auto handle = ShaderRecompiler::PrepareShader(request);
|
||||
auto handle = PrepareShaderWithDiagnostics(request);
|
||||
snapshot.prepared->entries.push_back({codeOffset, handle});
|
||||
return handle;
|
||||
}
|
||||
|
|
@ -235,7 +246,7 @@ ShaderRecompiler::PreparedShaderInvocation InvocationFor(const ShaderSnapshot& s
|
|||
if (snapshot.type != 0 || request.shader.stage != ShaderRecompiler::ShaderStage::Compute) throw std::runtime_error("AGC driver: unregistered program is not a compute shader");
|
||||
APS5_LOG_ERR("Compute shader 0x%llx was not registered; preparing its artifact at dispatch", static_cast<unsigned long long>(snapshot.codeAddress));
|
||||
}
|
||||
auto handle = ShaderRecompiler::PrepareShader(request);
|
||||
auto handle = PrepareShaderWithDiagnostics(request);
|
||||
invocationRequest = request;
|
||||
invocationRequest.shader.code = ShaderRecompiler::GetPreparedCode(*handle);
|
||||
auto invocation = ShaderRecompiler::PreparedShaderInvocation::TryCreate(invocationRequest, handle, key);
|
||||
|
|
@ -409,7 +420,7 @@ std::vector<PreparedShaders::Entry> PrepareRegistered(const ShaderSnapshot& snap
|
|||
ShaderRecompiler::ShaderPreparationContext preparation;
|
||||
const auto append = [&] {
|
||||
PerformanceTimer timing("Shader.PrepareArtifact");
|
||||
entries.push_back({codeOffset, ShaderRecompiler::PrepareShader(request, &preparation)});
|
||||
entries.push_back({codeOffset, PrepareShaderWithDiagnostics(request, &preparation)});
|
||||
};
|
||||
append();
|
||||
if (compute) {
|
||||
|
|
@ -468,7 +479,7 @@ std::vector<PreparedGraphicsStage> PrepareGraphicsStages(const DrawDecode& decod
|
|||
}
|
||||
if (handle == nullptr) {
|
||||
PerformanceTimer timing("Shader.PrepareArtifact");
|
||||
handle = ShaderRecompiler::PrepareShader(request);
|
||||
handle = PrepareShaderWithDiagnostics(request);
|
||||
}
|
||||
const auto bytes = handle->artifact->bindings.pushConstantSizeBytes;
|
||||
require(bytes <= capacity - pushOffset, "prepared graphics stages exceed the push constant block");
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
#include "VulkanTestDevice.hpp"
|
||||
#include "Optimization/ResourceProgram.hpp"
|
||||
#include "ControlFlow/RequestSerializer.hpp"
|
||||
#include "prx/libSceAgcDriver/Execution/include/Driver/Shaders/ShaderRegistry.hpp"
|
||||
#include "prx/libSceAgcDriver/Execution/include/Driver.hpp"
|
||||
#include "prx/libSceAgcDriver/Submit/include/Acb.hpp"
|
||||
|
|
@ -13,6 +14,10 @@
|
|||
#include <vector>
|
||||
#include <future>
|
||||
#include <barrier>
|
||||
#include <cstdlib>
|
||||
#include <cstdio>
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
|
||||
namespace {
|
||||
|
||||
|
|
@ -138,6 +143,52 @@ void Run(AgcDriver::VulkanDevice& device) {
|
|||
ExpectFailure([&] { static_cast<void>(AgcDriver::DriverDetail::SourceHandleFor(snapshot, 0, request)); }, "artifact is missing");
|
||||
}
|
||||
|
||||
void FailureCapture(AgcDriver::VulkanDevice& device) {
|
||||
using namespace ShaderRecompiler;
|
||||
const std::array<std::uint32_t, 1> code{0xffffffffu};
|
||||
const std::vector<std::byte> header(2048, std::byte{0x5a});
|
||||
RecompileRequest request{{ShaderStage::Compute, 0x12345cafeull, code, 0x20000, header},
|
||||
{32, 0, {}, ShaderComputeStageInfo{{1, 1, 1}, 0, {}, false, 1, {}}, {}, {}, {}}, device.ComputeTarget(32), {0, 0, 0, 128}};
|
||||
const auto failure = [&](auto action) {
|
||||
try { action(); }
|
||||
catch (const std::exception& error) { return std::string(error.what()); }
|
||||
throw std::runtime_error("invalid shader preparation succeeded");
|
||||
};
|
||||
const auto expected = failure([&] { static_cast<void>(PrepareShader(request)); });
|
||||
const char* previous = std::getenv("APS5_DUMP_SHADERS");
|
||||
const std::string saved = previous != nullptr ? previous : "";
|
||||
const std::string path = "shader_12345cafe.req";
|
||||
std::remove(path.c_str());
|
||||
#ifdef _WIN32
|
||||
Require(_putenv_s("APS5_DUMP_SHADERS", "") == 0, "cannot disable shader capture");
|
||||
#else
|
||||
Require(unsetenv("APS5_DUMP_SHADERS") == 0, "cannot disable shader capture");
|
||||
#endif
|
||||
Require(failure([&] { static_cast<void>(AgcDriver::DriverDetail::PrepareShaderWithDiagnostics(request)); }) == expected, "disabled shader capture changed the preparation failure");
|
||||
Require(!std::ifstream(path, std::ios::binary).is_open(), "disabled shader capture created a request");
|
||||
#ifdef _WIN32
|
||||
Require(_putenv_s("APS5_DUMP_SHADERS", "1") == 0, "cannot enable shader capture");
|
||||
#else
|
||||
Require(setenv("APS5_DUMP_SHADERS", "1", 1) == 0, "cannot enable shader capture");
|
||||
#endif
|
||||
const auto actual = failure([&] { static_cast<void>(AgcDriver::DriverDetail::PrepareShaderWithDiagnostics(request)); });
|
||||
#ifdef _WIN32
|
||||
Require(_putenv_s("APS5_DUMP_SHADERS", saved.c_str()) == 0, "cannot restore shader capture");
|
||||
#else
|
||||
Require((previous != nullptr ? setenv("APS5_DUMP_SHADERS", saved.c_str(), 1) : unsetenv("APS5_DUMP_SHADERS")) == 0, "cannot restore shader capture");
|
||||
#endif
|
||||
Require(actual == expected, "shader capture changed the preparation failure");
|
||||
std::ifstream file(path, std::ios::binary);
|
||||
std::ostringstream text;
|
||||
text << file.rdbuf();
|
||||
file.close();
|
||||
std::remove(path.c_str());
|
||||
const RequestSerializer serializer;
|
||||
Require(text.str().size() > 1024 && text.str() == serializer.Serialize(request), "shader failure capture was incomplete");
|
||||
const auto replay = serializer.Deserialize(text.str());
|
||||
Require(serializer.Serialize(replay.request) == text.str(), "shader failure capture changed on replay");
|
||||
}
|
||||
|
||||
void PrepareMultisampledStorage(AgcDriver::VulkanDevice& device) {
|
||||
using namespace ShaderRecompiler;
|
||||
std::array<std::uint32_t, 13> code{0xd7460000u, 0x0401060cu, 0xd7460001u, 0x0405060du, 0x7e04020eu, 0x7e060280u, 0x7e080208u, 0x7e0a0209u, 0x7e0c020au, 0x7e0e020bu, 0xf0200f38u, 0x00000400u, 0xbf810000u};
|
||||
|
|
@ -325,6 +376,7 @@ int main(int argc, char** argv) {
|
|||
auto device = OpenVulkanTestDevice();
|
||||
if (!device) return VulkanTestSkipped;
|
||||
Run(*device);
|
||||
FailureCapture(*device);
|
||||
Require(argc != 2 || std::string_view(argv[1]) != "--fail-before-registration", "injected failure before registration");
|
||||
PrepareMultisampledStorage(*device);
|
||||
device.reset();
|
||||
|
|
|
|||
Loading…
Reference in New Issue