diff --git a/core/libs/prx/libSceAgcDriver/tests/BdaExecution.cpp b/core/libs/prx/libSceAgcDriver/tests/BdaExecution.cpp index 5fc40da4e..2804e7e81 100644 --- a/core/libs/prx/libSceAgcDriver/tests/BdaExecution.cpp +++ b/core/libs/prx/libSceAgcDriver/tests/BdaExecution.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include 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 ranges{}; + bool runtime = false; const auto run = [&](const char* name, std::uint64_t address, std::uint32_t bits, bool coherent, const std::array& expected, std::initializer_list 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 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>{{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}); diff --git a/core/libs/prx/libSceAgcDriver/tests/BdaShader.cpp b/core/libs/prx/libSceAgcDriver/tests/BdaShader.cpp index 529457a60..60a2e8785 100644 --- a/core/libs/prx/libSceAgcDriver/tests/BdaShader.cpp +++ b/core/libs/prx/libSceAgcDriver/tests/BdaShader.cpp @@ -121,3 +121,15 @@ std::vector MakeBdaStoreTestShader(std::uint64_t address, std::ui #endif return shader; } + +std::vector 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{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; +} diff --git a/core/libs/prx/libSceAgcDriver/tests/BdaShader.hpp b/core/libs/prx/libSceAgcDriver/tests/BdaShader.hpp index abfefe977..62a3adcb1 100644 --- a/core/libs/prx/libSceAgcDriver/tests/BdaShader.hpp +++ b/core/libs/prx/libSceAgcDriver/tests/BdaShader.hpp @@ -10,6 +10,7 @@ std::vector MakeBdaDwordReadTestShader(std::uint64_t address, std std::vector MakeBdaSpanReadTestShader(std::uint64_t address, std::uint32_t offset, std::uint32_t extracted, bool coherent, bool stops); std::vector MakeBdaDwordWriteTestShader(std::uint64_t address, std::uint32_t dwords, const std::uint32_t* values); std::vector MakeBdaStoreTestShader(std::uint64_t address, std::uint32_t value, std::uint32_t bits, bool coherent); +std::vector 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(); diff --git a/core/shader/recompiler/SpirvBackend/src/SpirvBdaRead.cpp b/core/shader/recompiler/SpirvBackend/src/SpirvBdaRead.cpp index f07e159ee..5f9f55301 100644 --- a/core/shader/recompiler/SpirvBackend/src/SpirvBdaRead.cpp +++ b/core/shader/recompiler/SpirvBackend/src/SpirvBdaRead.cpp @@ -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)); });