mirror of
https://github.com/boykopovar/AnyPS5.git
synced 2026-10-22 08:33:12 -05:00
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>
136 lines
7.4 KiB
C++
136 lines
7.4 KiB
C++
#include "BdaShader.hpp"
|
|
#include "SpirvBackend/SpirvBda.hpp"
|
|
#include "SpirvBackend/SpirvMemory/SpirvTypes.hpp"
|
|
#include "SpirvBackend/SpirvMemory/SpirvConstants.hpp"
|
|
#include "IntermediateRepresentation/IrBuilder.hpp"
|
|
#if ANYPS5_ENABLE_SPIRV_TOOLS
|
|
#include "SpirvBackend/SpirvOptimizer.hpp"
|
|
#endif
|
|
|
|
namespace {
|
|
|
|
template<typename TEmit>
|
|
std::vector<std::uint32_t> MakeShader(std::uint64_t address, bool coherent, bool stops, const TEmit& emit, std::uint32_t extracted = 0, bool writes = false) {
|
|
using namespace ShaderRecompiler;
|
|
IrProgram program;
|
|
program.Resources().stage = IrShaderStage::Compute;
|
|
program.Info().usesDma = true;
|
|
program.Info().bdaWrites = writes;
|
|
if (coherent) {
|
|
MemoryInfo memory;
|
|
memory.coherent = true;
|
|
program.Resources().memoryInfo = {memory};
|
|
}
|
|
SpirvEmitterState state(program, {});
|
|
EmitBaseHeader(state.module, program);
|
|
const auto define = [&](std::uint32_t binding) {
|
|
const auto variable = state.module.DefineGlobalVariable(TypeStorageBufferPointer(state), spv::StorageClassStorageBuffer);
|
|
state.module.AddAnnotation(spv::OpDecorate, variable, spv::DecorationDescriptorSet, 0u);
|
|
state.module.AddAnnotation(spv::OpDecorate, variable, spv::DecorationBinding, binding);
|
|
return variable;
|
|
};
|
|
state.bdaPagetableVariable = define(0);
|
|
state.faultBufferVariable = define(1);
|
|
const auto output = define(2);
|
|
std::vector<std::uint32_t> interfaceVariables;
|
|
if (!stops) {
|
|
interfaceVariables.push_back(state.module.DefineGlobalVariable(TypePointer(state, spv::StorageClassInput, TypeU32(state)), spv::StorageClassInput));
|
|
state.module.AddAnnotation(spv::OpDecorate, interfaceVariables[0], spv::DecorationBuiltIn, spv::BuiltInLocalInvocationIndex);
|
|
}
|
|
state.bdaStopsInvocations = stops;
|
|
DefineGetBdaPointer(state);
|
|
const auto main = state.module.AllocateId();
|
|
state.module.AddFunction(spv::OpFunction, TypeVoid(state), main, spv::FunctionControlMaskNone, TypeFunction(state));
|
|
EmitLabel(state, state.module.AllocateId());
|
|
SpirvValueEmitContext ctx(state);
|
|
auto& instruction = writes ? program.CreateValue(IrOpcode::StoreAddressU32, IrType::Void) : extracted != 0u ? program.CreateValue(IrOpcode::LoadAddressU32x4, IrType::U32x4) : program.CreateValue(IrOpcode::LoadAddressU32, IrType::U32);
|
|
IrBuilder ir(program);
|
|
for (std::uint32_t dword = 0; dword < 4u; ++dword) {
|
|
if ((extracted & (1u << dword)) == 0u) continue;
|
|
auto& extract = program.CreateValue(IrOpcode::CompositeExtractU32x4, IrType::U32);
|
|
extract.AddArgument(&instruction);
|
|
extract.AddArgument(&ir.Constant(dword));
|
|
}
|
|
MemoryFlags flags{};
|
|
flags.pc = 0x1234;
|
|
instruction.SetFlags(flags);
|
|
auto base = BdaConstant(state, address);
|
|
if (!stops) {
|
|
const auto index = state.module.AllocateId();
|
|
state.module.AddFunction(spv::OpLoad, TypeU32(state), index, interfaceVariables[0]);
|
|
base = Binary(state, spv::OpIAdd, TypeScalarU64(state), base, Unary(state, spv::OpUConvert, TypeScalarU64(state), index));
|
|
}
|
|
const auto values = emit(ctx, instruction, base);
|
|
for (std::uint32_t index = 0; index < values.size(); ++index) state.module.AddFunction(spv::OpStore, BdaWord(state, output, ConstantU32(state, index)), values[index]);
|
|
state.module.AddFunction(spv::OpReturn);
|
|
state.module.AddFunction(spv::OpFunctionEnd);
|
|
state.module.AddExecutionMode(main, spv::ExecutionModeLocalSize, 1u, 1u, 1u);
|
|
state.module.EmitEntryPoint(spv::ExecutionModelGLCompute, main, "main", interfaceVariables);
|
|
return state.module.Finalize();
|
|
}
|
|
|
|
}
|
|
|
|
std::vector<std::uint32_t> MakeBdaTestShader(std::uint64_t address, std::uint32_t bits, std::int64_t offset) {
|
|
using namespace ShaderRecompiler;
|
|
return MakeShader(address, false, true, [&](SpirvValueEmitContext& ctx, const IrValue& instruction, std::uint32_t base) {
|
|
if (offset != 0) base = AddBdaAddress(ctx, instruction, base, BdaConstant(ctx.state, offset < 0 ? std::uint64_t{0} - static_cast<std::uint64_t>(offset) : static_cast<std::uint64_t>(offset)), offset < 0);
|
|
return std::vector<std::uint32_t>{EmitBdaRead(ctx, instruction, base, bits)};
|
|
});
|
|
}
|
|
|
|
std::vector<std::uint32_t> MakeBdaSpanReadTestShader(std::uint64_t address, std::uint32_t offset, std::uint32_t extracted, bool coherent, bool stops) {
|
|
using namespace ShaderRecompiler;
|
|
return MakeShader(address, coherent, stops, [&](SpirvValueEmitContext& ctx, const IrValue& instruction, std::uint32_t base) {
|
|
const auto values = EmitBdaDwordReads(ctx, instruction, base, offset, 4u);
|
|
return std::vector<std::uint32_t>(values.begin(), values.end());
|
|
}, extracted);
|
|
}
|
|
|
|
std::vector<std::uint32_t> MakeBdaDwordWriteTestShader(std::uint64_t address, std::uint32_t dwords, const std::uint32_t* values) {
|
|
using namespace ShaderRecompiler;
|
|
return MakeShader(address, false, true, [&](SpirvValueEmitContext& ctx, const IrValue& instruction, std::uint32_t base) {
|
|
auto& state = ctx.state;
|
|
std::vector<std::uint32_t> parts;
|
|
for (std::uint32_t dword = 0; dword < dwords; ++dword) parts.push_back(ConstantU32(state, values[dword]));
|
|
const auto composite = state.module.AllocateId();
|
|
if (dwords == 2u) state.module.AddFunction(spv::OpCompositeConstruct, TypeU32Vector(state, 2u), composite, parts[0], parts[1]);
|
|
else if (dwords == 3u) state.module.AddFunction(spv::OpCompositeConstruct, TypeU32Vector(state, 3u), composite, parts[0], parts[1], parts[2]);
|
|
else state.module.AddFunction(spv::OpCompositeConstruct, TypeU32Vector(state, 4u), composite, parts[0], parts[1], parts[2], parts[3]);
|
|
EmitBdaDwordWrites(ctx, instruction, base, 0u, dwords, composite);
|
|
return std::vector<std::uint32_t>{ConstantU32(state, 1u)};
|
|
}, 0u, true);
|
|
}
|
|
|
|
std::vector<std::uint32_t> MakeBdaDwordReadTestShader(std::uint64_t address, std::uint32_t dwords, bool coherent, bool stops) {
|
|
using namespace ShaderRecompiler;
|
|
return MakeShader(address, coherent, stops, [&](SpirvValueEmitContext& ctx, const IrValue& instruction, std::uint32_t base) {
|
|
const auto values = EmitBdaDwordReads(ctx, instruction, base, 0u, dwords, true);
|
|
return std::vector<std::uint32_t>(values.begin(), values.begin() + dwords);
|
|
});
|
|
}
|
|
|
|
std::vector<std::uint32_t> MakeBdaStoreTestShader(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) {
|
|
EmitBdaStore(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;
|
|
}
|
|
|
|
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;
|
|
}
|