AnyPS5/core/libs/prx/libSceAgcDriver/tests/BdaShader.cpp

109 lines
6.1 KiB
C++

#include "BdaShader.hpp"
#include "SpirvBackend/SpirvBda.hpp"
#include "SpirvBackend/SpirvMemory/SpirvTypes.hpp"
#include "SpirvBackend/SpirvMemory/SpirvConstants.hpp"
#include "IntermediateRepresentation/IrBuilder.hpp"
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 = 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);
});
}