fix(shader): store unaligned 32-bit BDA writes byte by byte
EmitBdaWrite, used for buffer stores through GPU-selected descriptors, recorded an Unaligned fault and dropped every 32-bit store whose address is not 4-byte aligned. EmitBdaStore and the BDA reads already take the byte path for unaligned addresses, and raw buffer accesses use the exact bytes, as RDNA does in unaligned mode. The unaligned case now stores the four bytes one by one like the 8- and 16-bit stores of the same function. Sword of the Sea hit this about once per frame from one compute store, so four bytes were lost and a fault was reported each time. The BDA store execution table now also runs through EmitBdaWrite; its unaligned dword cases failed before this change. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
33ae282e4e
commit
803a131275
|
|
@ -9,6 +9,7 @@
|
|||
#include <limits>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
namespace {
|
||||
|
|
@ -345,15 +346,16 @@ void RunBdaStoreExecutionTests(const Context& context) {
|
|||
constexpr std::uint32_t writable = Abi::Read | Abi::Write;
|
||||
const Abi::Header header{Abi::Version, 2, sizeof(Abi::Range), 0};
|
||||
std::array<Abi::Range, 2> ranges{};
|
||||
bool runtime = false;
|
||||
const auto run = [&](const char* name, std::uint64_t address, std::uint32_t bits, bool coherent, const std::array<std::uint8_t, 5>& expected, std::initializer_list<std::uint64_t> writtenAddresses, Abi::FaultReason reason = {}, std::uint64_t faultAddress = 0, std::uint32_t faultBytes = 0) {
|
||||
const std::string label = std::string("BDA store ") + name + (coherent ? " coherent: " : ": ");
|
||||
const std::string label = std::string(runtime ? "BDA runtime write " : "BDA store ") + name + (coherent ? " coherent: " : ": ");
|
||||
std::memset(first.Bytes().data(), 0xcd, first.Bytes().size());
|
||||
std::memset(second.Bytes().data(), 0xcd, second.Bytes().size());
|
||||
std::memcpy(table.Bytes().data(), &header, sizeof(header));
|
||||
std::memcpy(table.Bytes().data() + sizeof(header), ranges.data(), sizeof(ranges));
|
||||
std::memset(fault.Bytes().data(), 0, fault.Bytes().size());
|
||||
std::memset(output.Bytes().data(), 0, output.Bytes().size());
|
||||
Pipeline pipeline(context, MakeBdaStoreTestShader(address, 0xa1b2c3d4u, bits, coherent), {&table, &fault, &output});
|
||||
Pipeline pipeline(context, runtime ? MakeBdaWriteTestShader(address, 0xa1b2c3d4u, bits, coherent) : MakeBdaStoreTestShader(address, 0xa1b2c3d4u, bits, coherent), {&table, &fault, &output});
|
||||
pipeline.Run(1);
|
||||
std::array<std::uint8_t, 5> actual{};
|
||||
std::memcpy(actual.data(), first.Bytes().data(), 2);
|
||||
|
|
@ -387,7 +389,8 @@ void RunBdaStoreExecutionTests(const Context& context) {
|
|||
std::memcpy(&completed, output.Bytes().data(), sizeof(completed));
|
||||
Require(completed == 1, label + "store did not finish its per-byte accesses");
|
||||
};
|
||||
for (const bool coherent : {false, true}) {
|
||||
for (const auto& [coherent, writes] : std::initializer_list<std::pair<bool, bool>>{{false, false}, {false, true}, {true, false}, {true, true}}) {
|
||||
runtime = writes;
|
||||
ranges = {{{guest, guest + 2, first.DeviceAddress(), writable, 0}, {guest + 2, guest + 5, second.DeviceAddress(), writable, 0}}};
|
||||
run("byte", guest + 1, 8, coherent, {0xcd, 0xd4, 0xcd, 0xcd, 0xcd}, {guest + 1});
|
||||
run("split short", guest + 1, 16, coherent, {0xcd, 0xd4, 0xc3, 0xcd, 0xcd}, {guest + 1, guest + 2});
|
||||
|
|
|
|||
|
|
@ -121,3 +121,15 @@ std::vector<std::uint32_t> MakeBdaStoreTestShader(std::uint64_t address, std::ui
|
|||
#endif
|
||||
return shader;
|
||||
}
|
||||
|
||||
std::vector<std::uint32_t> MakeBdaWriteTestShader(std::uint64_t address, std::uint32_t value, std::uint32_t bits, bool coherent) {
|
||||
using namespace ShaderRecompiler;
|
||||
auto shader = MakeShader(address, coherent, true, [&](SpirvValueEmitContext& ctx, const IrValue& instruction, std::uint32_t base) {
|
||||
EmitBdaWrite(ctx, instruction, base, ConstantU32(ctx.state, value), bits);
|
||||
return std::vector<std::uint32_t>{ConstantU32(ctx.state, 1u)};
|
||||
}, 0u, true);
|
||||
#if ANYPS5_ENABLE_SPIRV_TOOLS
|
||||
shader = ValidateAndOptimizeSpirv(shader, VK_API_VERSION_1_1, shader[1], false, false);
|
||||
#endif
|
||||
return shader;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ std::vector<std::uint32_t> MakeBdaDwordReadTestShader(std::uint64_t address, std
|
|||
std::vector<std::uint32_t> MakeBdaSpanReadTestShader(std::uint64_t address, std::uint32_t offset, std::uint32_t extracted, bool coherent, bool stops);
|
||||
std::vector<std::uint32_t> MakeBdaDwordWriteTestShader(std::uint64_t address, std::uint32_t dwords, const std::uint32_t* values);
|
||||
std::vector<std::uint32_t> MakeBdaStoreTestShader(std::uint64_t address, std::uint32_t value, std::uint32_t bits, bool coherent);
|
||||
std::vector<std::uint32_t> MakeBdaWriteTestShader(std::uint64_t address, std::uint32_t value, std::uint32_t bits, bool coherent);
|
||||
void RunBdaStoreExecutionTests(const AgcDriver::Graphics::Context& context);
|
||||
void RunBdaExecutionTests(const AgcDriver::Graphics::Context& context);
|
||||
void RunBdaContractTests();
|
||||
|
|
|
|||
|
|
@ -358,7 +358,7 @@ void EmitBdaWrite(SpirvValueEmitContext& ctx, const IrValue& inst, std::uint32_t
|
|||
if (state.bdaWritePointerFunction == 0 || state.bdaNoteWriteFunction == 0) ctx.Fail(inst, "BDA write functions are missing");
|
||||
const auto instruction = BdaInstructionPc(state, inst);
|
||||
if (bits != 8u && bits != 16u && bits != 32u) ctx.Fail(inst, "unsupported BDA write width");
|
||||
if (bits != 32u) {
|
||||
const auto storeBytes = [&] {
|
||||
EmitBdaOverflowCheck(state, address, bits / 8u, instruction);
|
||||
for (std::uint32_t byte = 0u; byte < bits / 8u; ++byte) {
|
||||
const auto guest = Binary(state, spv::OpIAdd, TypeScalarU64(state), address, BdaConstant(state, byte));
|
||||
|
|
@ -373,10 +373,13 @@ void EmitBdaWrite(SpirvValueEmitContext& ctx, const IrValue& inst, std::uint32_t
|
|||
state.module.AddFunction(spv::OpFunctionCall, state.module.Type(spv::OpTypeVoid), state.module.AllocateId(), state.bdaNoteWriteFunction, guest);
|
||||
});
|
||||
}
|
||||
};
|
||||
if (bits != 32u) {
|
||||
storeBytes();
|
||||
return;
|
||||
}
|
||||
const auto unaligned = BdaAddressUnaligned(state, address);
|
||||
EmitIfCondition(state, unaligned, [&] { RecordBdaFault(state, address, ConstantU32(state, 4u), instruction, BdaAbi::FaultReason::Unaligned); });
|
||||
EmitIfCondition(state, unaligned, storeBytes);
|
||||
EmitIfCondition(state, Unary(state, spv::OpLogicalNot, TypeBool(state), unaligned), [&] {
|
||||
EmitBdaStoreAt(state, address, 4u, TypePhysicalU32Pointer(state), value, instruction, BdaAccessMask(state, inst));
|
||||
});
|
||||
|
|
|
|||
Loading…
Reference in New Issue