feat(agc): accept graphics programs without an AGC shader header
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
e28fbc277d
commit
767b8f83a2
|
|
@ -84,7 +84,8 @@ struct ShaderSnapshot {
|
|||
};
|
||||
|
||||
|
||||
std::shared_ptr<const ShaderSnapshot> ReadRawComputeShader(std::uint64_t address);
|
||||
std::shared_ptr<const ShaderSnapshot> ReadRawShader(std::uint64_t address);
|
||||
std::shared_ptr<const ShaderSnapshot> ProgramSnapshot(const ShaderRegistry& shaders, std::uint64_t address, std::initializer_list<std::uint8_t> types);
|
||||
|
||||
std::shared_ptr<const ShaderRecompiler::SourceHandle> PrepareShaderWithDiagnostics(const ShaderRecompiler::RecompileRequest& request, ShaderRecompiler::ShaderPreparationContext* preparation = nullptr);
|
||||
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ void Driver::dispatch(QueueState& queue, std::span<const std::uint32_t> packet,
|
|||
PerformanceTimer timing("Driver.Dispatch");
|
||||
const auto address = (static_cast<std::uint64_t>(readRegister(queue.shader, 0x20c)) << 8u) | (static_cast<std::uint64_t>(readRegister(queue.shader, 0x20d) & 0xffu) << 40u);
|
||||
auto registeredShader = RegisteredProgram(*submission.shaders, address, {0});
|
||||
if (!registeredShader) registeredShader = ReadRawComputeShader(address);
|
||||
if (!registeredShader) registeredShader = ReadRawShader(address);
|
||||
const auto& snapshot = *registeredShader;
|
||||
require(snapshot.type == 0, "compute program refers to a non-compute shader");
|
||||
const auto userCount = (readRegister(queue.shader, 0x213) >> 1u) & 0x1fu;
|
||||
|
|
|
|||
|
|
@ -123,7 +123,9 @@ DrawVerdict Driver::draw(QueueState& queue, std::span<const std::uint32_t> packe
|
|||
std::vector<ShaderRecompiler::LinkedProgram> linked;
|
||||
for (std::size_t i = 0; i < programs.size(); ++i) {
|
||||
const auto& program = programs[i];
|
||||
memory.insert(memory.end(), program.memory.begin(), program.memory.end());
|
||||
for (const auto& region : program.memory) {
|
||||
if (!region.bytes.empty()) memory.push_back(region);
|
||||
}
|
||||
linked.push_back({roles[i], program.binary, program.userDataBase, program.firstUserSgpr, program.userData});
|
||||
}
|
||||
timing.Mark("prepare");
|
||||
|
|
|
|||
|
|
@ -28,10 +28,10 @@ void DecodeGraphicsPrograms(DrawDecode& decoded, const QueueState& queue, const
|
|||
const auto prepare = [&](std::uint64_t address, std::uint8_t type, Stage stage, std::uint32_t rsrc2, std::uint32_t userDataBase) {
|
||||
const bool nullPixel = stage == Stage::Fragment && (address == 0 || pixelSkipped);
|
||||
if (nullPixel) address = NullPixelProgramAddress();
|
||||
const auto registered = RegisteredProgram(registry, address, {type});
|
||||
require(registered != nullptr, "graphics program does not belong to a compatible registered shader");
|
||||
const auto& snapshot = *registered;
|
||||
const auto program = ProgramSnapshot(registry, address, {type});
|
||||
const auto& snapshot = *program;
|
||||
require((address - snapshot.codeAddress) % sizeof(std::uint32_t) == 0, "graphics entry point is not dword aligned");
|
||||
require(snapshot.header.empty() || snapshot.type == type, "graphics program refers to an incompatible shader binary type");
|
||||
if (!nullPixel) Graphics::NoteRegisterRead(Graphics::RegisterBank::Shader, rsrc2);
|
||||
const auto resources = nullPixel ? 0u : ReadGraphicsRegister(queue.shader, rsrc2);
|
||||
const auto userCount = ((resources >> 1u) & 0x1fu) | (((resources >> 27u) & 1u) << 5u);
|
||||
|
|
@ -43,7 +43,7 @@ void DecodeGraphicsPrograms(DrawDecode& decoded, const QueueState& queue, const
|
|||
8,
|
||||
{},
|
||||
{{{snapshot.codeAddress, std::as_bytes(std::span(snapshot.code))}, {snapshot.headerAddress, snapshot.header}}},
|
||||
registered,
|
||||
program,
|
||||
codeOffset
|
||||
};
|
||||
for (std::uint32_t i = 0; i < userCount; ++i) {
|
||||
|
|
@ -86,9 +86,8 @@ void DecodeGraphicsPrograms(DrawDecode& decoded, const QueueState& queue, const
|
|||
append(0x0c8, 2, Stage::TessellationEvaluation, 0x08b, 0x08c, Role::Domain);
|
||||
} else if (graphics.stages.path == Graphics::ShaderPath::Geometry) {
|
||||
const auto frontAddress = programAddress(0xc8);
|
||||
const auto snapshot = RegisteredProgram(registry, frontAddress, {2, 4});
|
||||
require(snapshot != nullptr, "geometry front program is not registered");
|
||||
const auto type = snapshot->type;
|
||||
const auto front = ProgramSnapshot(registry, frontAddress, {2, 4});
|
||||
const auto type = front->header.empty() ? std::uint8_t{2} : front->type;
|
||||
require(type == 2 || type == 4, "invalid geometry front binary type");
|
||||
append(0xc8, type, Stage::Mesh, 0x8b, 0x8c, Role::Main);
|
||||
initializeMerged(programs.back(), 0x82, type == 4);
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ std::shared_ptr<const ShaderRecompiler::SourceHandle> PrepareShaderWithDiagnosti
|
|||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<const ShaderSnapshot> ReadRawComputeShader(std::uint64_t address) {
|
||||
std::shared_ptr<const ShaderSnapshot> ReadRawShader(std::uint64_t address) {
|
||||
GuestMemory::CheckRange(reinterpret_cast<const void*>(address), sizeof(std::uint32_t), 256);
|
||||
static std::mutex cacheMutex;
|
||||
static std::list<std::shared_ptr<const ShaderSnapshot>> cache;
|
||||
|
|
@ -100,7 +100,12 @@ std::shared_ptr<const ShaderSnapshot> ReadRawComputeShader(std::uint64_t address
|
|||
if (snapshot.code.size() == available) break;
|
||||
}
|
||||
}
|
||||
throw std::runtime_error("AGC driver: raw compute program has no reachable end within mapped code or the size limit");
|
||||
throw std::runtime_error("AGC driver: raw shader program has no reachable end within mapped code or the size limit");
|
||||
}
|
||||
|
||||
std::shared_ptr<const ShaderSnapshot> ProgramSnapshot(const ShaderRegistry& shaders, std::uint64_t address, std::initializer_list<std::uint8_t> types) {
|
||||
if (auto registered = RegisteredProgram(shaders, address, types)) return registered;
|
||||
return ReadRawShader(address);
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
|
|
|||
|
|
@ -163,6 +163,7 @@ ShaderRecompiler::ShaderPixelStageInfo DecodePixelStageInfo(const Registers& con
|
|||
}
|
||||
|
||||
ShaderRecompiler::ShaderVertexStageInfo DecodeVertexStageInfo(std::span<const std::byte> header, std::uint64_t headerAddress, std::span<const std::uint32_t> userData, std::vector<DecodeRead>* reads, bool staticAbi) {
|
||||
if (header.empty()) return {};
|
||||
if (header.size() < sizeof(Shader)) throw std::runtime_error("AGC graphics: shader header is smaller than the fixed AGC header");
|
||||
Shader shader;
|
||||
std::memcpy(&shader, header.data(), sizeof(Shader));
|
||||
|
|
|
|||
|
|
@ -593,20 +593,20 @@ int main() {
|
|||
rawCode[1] = 0xbf810000;
|
||||
rawCode[2] = 0xbf810000;
|
||||
const auto rawAddress = reinterpret_cast<std::uintptr_t>(rawCode.data());
|
||||
const auto literal = AgcDriver::DriverDetail::ReadRawComputeShader(rawAddress);
|
||||
const auto literal = AgcDriver::DriverDetail::ReadRawShader(rawAddress);
|
||||
check(literal->code.size() == 3 && literal->header.empty(), "raw compute stopped at an instruction literal");
|
||||
check(AgcDriver::DriverDetail::ReadRawComputeShader(rawAddress) == literal, "unchanged raw compute code lost its snapshot identity");
|
||||
check(AgcDriver::DriverDetail::ReadRawShader(rawAddress) == literal, "unchanged raw compute code lost its snapshot identity");
|
||||
rawCode[0] = 0xbf820002;
|
||||
rawCode[1] = 0xbf810000;
|
||||
rawCode[2] = 0xbf800000;
|
||||
rawCode[3] = 0xbf810000;
|
||||
const auto branched = AgcDriver::DriverDetail::ReadRawComputeShader(rawAddress);
|
||||
const auto branched = AgcDriver::DriverDetail::ReadRawShader(rawAddress);
|
||||
check(branched->code.size() == 4 && literal->code[0] == 0xbe8003ff, "raw compute lost branch targets or modified an earlier snapshot");
|
||||
check(branched != literal, "changed raw compute code reused a stale snapshot");
|
||||
std::array<std::shared_ptr<const AgcDriver::DriverDetail::ShaderSnapshot>, 8> concurrent;
|
||||
std::vector<std::thread> readers;
|
||||
for (auto& snapshot : concurrent) readers.emplace_back([&snapshot, rawAddress] {
|
||||
snapshot = AgcDriver::DriverDetail::ReadRawComputeShader(rawAddress);
|
||||
snapshot = AgcDriver::DriverDetail::ReadRawShader(rawAddress);
|
||||
});
|
||||
for (auto& reader : readers) reader.join();
|
||||
for (const auto& snapshot : concurrent) check(snapshot == branched, "concurrent raw compute readers lost snapshot reuse");
|
||||
|
|
@ -615,23 +615,23 @@ int main() {
|
|||
const auto firstAddress = reinterpret_cast<std::uintptr_t>(programs.front().data());
|
||||
readers.clear();
|
||||
for (auto& snapshot : concurrent) readers.emplace_back([&snapshot, firstAddress] {
|
||||
snapshot = AgcDriver::DriverDetail::ReadRawComputeShader(firstAddress);
|
||||
snapshot = AgcDriver::DriverDetail::ReadRawShader(firstAddress);
|
||||
});
|
||||
for (auto& reader : readers) reader.join();
|
||||
for (const auto& snapshot : concurrent) check(snapshot == concurrent.front(), "concurrent raw compute misses duplicated snapshots");
|
||||
const auto evicted = AgcDriver::DriverDetail::ReadRawComputeShader(firstAddress);
|
||||
const auto evicted = AgcDriver::DriverDetail::ReadRawShader(firstAddress);
|
||||
for (std::size_t i = 1; i < programs.size(); ++i)
|
||||
AgcDriver::DriverDetail::ReadRawComputeShader(reinterpret_cast<std::uintptr_t>(programs[i].data()));
|
||||
check(AgcDriver::DriverDetail::ReadRawComputeShader(firstAddress) != evicted && evicted->code[0] == 0xbf810000,
|
||||
AgcDriver::DriverDetail::ReadRawShader(reinterpret_cast<std::uintptr_t>(programs[i].data()));
|
||||
check(AgcDriver::DriverDetail::ReadRawShader(firstAddress) != evicted && evicted->code[0] == 0xbf810000,
|
||||
"raw compute cache eviction lost snapshot lifetime or exceeded its entry limit");
|
||||
check(!expectFailure([&] { AgcDriver::DriverDetail::ReadRawComputeShader(rawAddress + 4); }).empty(), "raw compute accepted a misaligned entry");
|
||||
check(!expectFailure([] { AgcDriver::DriverDetail::ReadRawComputeShader(0); }).empty(), "raw compute accepted an unmapped entry");
|
||||
check(!expectFailure([&] { AgcDriver::DriverDetail::ReadRawShader(rawAddress + 4); }).empty(), "raw compute accepted a misaligned entry");
|
||||
check(!expectFailure([] { AgcDriver::DriverDetail::ReadRawShader(0); }).empty(), "raw compute accepted an unmapped entry");
|
||||
alignas(256) std::array<std::uint32_t, 128> straddling{};
|
||||
straddling.fill(0xbf800000);
|
||||
straddling[63] = 0xf4000000;
|
||||
straddling[64] = 0xfa000000;
|
||||
straddling[65] = 0xbf810000;
|
||||
const auto straddled = AgcDriver::DriverDetail::ReadRawComputeShader(reinterpret_cast<std::uintptr_t>(straddling.data()));
|
||||
const auto straddled = AgcDriver::DriverDetail::ReadRawShader(reinterpret_cast<std::uintptr_t>(straddling.data()));
|
||||
check(straddled->code.size() == 66 && straddled->code[64] == 0xfa000000, "raw compute stopped at a memory instruction across its first read window");
|
||||
#ifdef _WIN32
|
||||
auto* mapping = static_cast<std::uint32_t*>(VirtualAlloc(nullptr, 8192, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE));
|
||||
|
|
@ -641,18 +641,18 @@ int main() {
|
|||
auto* boundedCode = mapping + 1024 - 64;
|
||||
std::fill_n(boundedCode, 64, 0xbf800000u);
|
||||
const auto boundedAddress = reinterpret_cast<std::uintptr_t>(boundedCode);
|
||||
const auto unterminated = expectFailure([&] { AgcDriver::DriverDetail::ReadRawComputeShader(boundedAddress); });
|
||||
const auto unterminated = expectFailure([&] { AgcDriver::DriverDetail::ReadRawShader(boundedAddress); });
|
||||
boundedCode[63] = 0xbf810000;
|
||||
const auto bounded = AgcDriver::DriverDetail::ReadRawComputeShader(boundedAddress);
|
||||
const auto bounded = AgcDriver::DriverDetail::ReadRawShader(boundedAddress);
|
||||
check(VirtualProtect(mapping + 1024, 4096, PAGE_READWRITE, &protection) != 0, "cannot extend raw compute mapping");
|
||||
boundedCode[63] = 0xbf800000;
|
||||
boundedCode[64] = 0xbf810000;
|
||||
const auto extended = AgcDriver::DriverDetail::ReadRawComputeShader(boundedAddress);
|
||||
const auto extended = AgcDriver::DriverDetail::ReadRawShader(boundedAddress);
|
||||
check(extended != bounded && extended->code.size() == 65, "raw compute reused code before its end changed");
|
||||
check(VirtualProtect(mapping + 1024, 4096, PAGE_NOACCESS, &protection) != 0, "cannot revoke cached raw compute tail");
|
||||
check(!expectFailure([&] { AgcDriver::DriverDetail::ReadRawComputeShader(boundedAddress); }).empty(), "raw compute reused an inaccessible cached tail");
|
||||
check(!expectFailure([&] { AgcDriver::DriverDetail::ReadRawShader(boundedAddress); }).empty(), "raw compute reused an inaccessible cached tail");
|
||||
check(VirtualProtect(mapping, 4096, PAGE_NOACCESS, &protection) != 0, "cannot revoke cached raw compute code");
|
||||
check(!expectFailure([&] { AgcDriver::DriverDetail::ReadRawComputeShader(boundedAddress); }).empty(), "raw compute reused inaccessible cached code");
|
||||
check(!expectFailure([&] { AgcDriver::DriverDetail::ReadRawShader(boundedAddress); }).empty(), "raw compute reused inaccessible cached code");
|
||||
check(VirtualFree(mapping, 0, MEM_RELEASE) != 0, "cannot release raw compute boundary test");
|
||||
check(!unterminated.empty() && bounded->code.size() == 64, "raw compute crossed inaccessible memory or missed its last instruction");
|
||||
#endif
|
||||
|
|
|
|||
Loading…
Reference in New Issue