Files
AnyPS5/core/libs/prx/libSceAgcDriver/tests/BdaContracts.cpp
T
Celegans12 c1e4a0ba8b fix(shader): record BDA faults in hull programs instead of refusing them
Every hull program with a BDA access was refused with "BDA fault
termination requires a barrier-safe tessellation-control execution
protocol". The refusal predates BdaInvocationsMayStop: a program with
s_barrier no longer stops a faulting invocation; the fault is recorded,
the read returns zero and every invocation reaches the barrier. In a
hull, s_barrier is the only control barrier the backend emits after the
guest code starts, so that rule already covers it, and a hull without
one can stop an invocation without stranding a barrier.

The tessellation test draws hulls that scale their levels by a BDA read,
with and without s_barrier, and requires an unmapped read to report a
tessellation-control fault (not on llvmpipe, which runs hull loads that
the unmapped-address check skips). The contract test now accepts a hull
with s_barrier.
2026-10-14 04:59:27 +02:00

60 lines
3.0 KiB
C++

#include "BdaShader.hpp"
#include "ControlFlow/RequestSerializer.hpp"
#include "SpirvBackend/SpirvBda.hpp"
#include <array>
namespace {
template<typename TAction>
void reject(TAction action, const char* expected) {
try { action(); }
catch (const std::runtime_error& error) {
AgcDriver::Graphics::Require(std::string(error.what()).find(expected) != std::string::npos, std::string("unexpected contract error: ") + error.what());
return;
}
throw std::runtime_error(std::string("missing contract rejection: ") + expected);
}
}
void RunBdaContractTests() {
using namespace ShaderRecompiler;
const std::array<std::uint32_t, 3> capabilities{spv::CapabilityInt64, spv::CapabilityPhysicalStorageBufferAddresses, spv::CapabilityStorageBuffer8BitAccess};
const std::array<std::string_view, 2> extensions{"SPV_KHR_physical_storage_buffer", "SPV_KHR_8bit_storage"};
SpirvTargetOptions target{0x00401000u, 0x00010300u, 32, BdaAbi::Version, capabilities, extensions};
IrProgram program;
program.Resources().stage = IrShaderStage::Compute;
program.Info().usesDma = true;
ValidateBdaTarget(program, target);
target.bdaAbiVersion = 0;
reject([&] { ValidateBdaTarget(program, target); }, "ABI version");
target.bdaAbiVersion = BdaAbi::Version;
target.supportedCapabilities = {};
reject([&] { ValidateBdaTarget(program, target); }, "capability");
target.supportedCapabilities = capabilities;
target.supportedExtensions = {};
reject([&] { ValidateBdaTarget(program, target); }, "extension");
target.supportedExtensions = extensions;
auto& block = program.CreateBlock();
block.AppendInstruction(&program.CreateValue(IrOpcode::Barrier, IrType::Void));
program.BlockOrder().push_back(&block);
ValidateBdaTarget(program, target);
AgcDriver::Graphics::Require(!BdaInvocationsMayStop(program), "a BDA program with barriers must keep its invocations running");
program.Resources().stage = IrShaderStage::TessellationControl;
ValidateBdaTarget(program, target);
program.Resources().stage = IrShaderStage::Compute;
RecompileRequest request{};
request.target.bdaAbiVersion = BdaAbi::Version;
request.target.supportedCapabilities = capabilities;
request.target.supportedExtensions = extensions;
request.target.fragmentShaderBarycentricEnabled = false;
const RequestSerializer serializer;
const auto encoded = serializer.Serialize(request);
const auto decoded = serializer.Deserialize(encoded);
AgcDriver::Graphics::Require(decoded.request.target.bdaAbiVersion == BdaAbi::Version && decoded.request.target.supportedCapabilities.size() == capabilities.size() && decoded.request.target.supportedExtensions[1] == extensions[1], "BDA request serialization changed target contract");
auto invalid = encoded;
invalid[0] = invalid[0] == 'A' ? 'B' : 'A';
reject([&] { static_cast<void>(serializer.Deserialize(invalid)); }, "request signature");
reject([&] { static_cast<void>(serializer.Deserialize("NVNQQWMAAAA=")); }, "serialization version");
}