mirror of
https://github.com/boykopovar/AnyPS5.git
synced 2026-10-27 00:28:14 +00:00
fix(shader): align prepared graphics with draw stage decoding
This commit is contained in:
1 parent
3e47c30ed7
commit
cb57d2502a
18 files changed
+404
-55
No files matched your search
@@ -1,6 +1,7 @@
|
||||
#include "prx/libSceAgc/Shader/include/PrimState.hpp"
|
||||
|
||||
#include <cstdio>
|
||||
#include <array>
|
||||
#include <stdexcept>
|
||||
#include <prx/libc/include/General.hpp>
|
||||
|
||||
@@ -62,8 +63,8 @@ int APS5_VABI sceAgcCreatePrimState(ShaderRegister* cx_regs, ShaderRegister* uc_
|
||||
}
|
||||
|
||||
if (cx_regs != nullptr && uc_regs != nullptr) {
|
||||
AgcDriverResolveShaderAbi_nid_postfix(gs, {cx_regs, 2}, {uc_regs, 3});
|
||||
if (hs != nullptr) AgcDriverResolveShaderAbi_nid_postfix(hs, {cx_regs, 2}, {uc_regs, 3});
|
||||
const std::array<const Shader*, 2> stages{hs, gs};
|
||||
AgcDriverResolveGraphicsStagesAbi_nid_postfix(std::span(stages).subspan(hs == nullptr ? 1u : 0u), {cx_regs, 2}, {uc_regs, 3});
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -606,6 +606,8 @@ foreach(agcExecutionTestSource IN LISTS agcExecutionTestSources)
|
||||
list(APPEND agcExecutionTests ${agcExecutionTest})
|
||||
endforeach()
|
||||
|
||||
target_include_directories(agc_driver_prepared_graphics_tests PRIVATE $<TARGET_PROPERTY:shader_recompiler_agc_driver,INTERFACE_INCLUDE_DIRECTORIES>)
|
||||
|
||||
target_include_directories(agc_driver_prepared_shaders_tests PRIVATE
|
||||
$<TARGET_PROPERTY:shader_recompiler_agc_driver,INTERFACE_INCLUDE_DIRECTORIES>
|
||||
)
|
||||
|
||||
@@ -18,6 +18,7 @@ extern "C" void AgcDriverShutdown_nid_postfix();
|
||||
extern "C" void AgcDriverSuspendPoint_nid_postfix();
|
||||
extern "C" void AgcDriverRegisterShader_nid_postfix(const Shader* shader);
|
||||
extern "C" void AgcDriverResolveShaderAbi_nid_postfix(const Shader* shader, std::span<const ShaderRegister> context, std::span<const ShaderRegister> primitive);
|
||||
extern "C" void AgcDriverResolveGraphicsStagesAbi_nid_postfix(std::span<const Shader* const> stages, std::span<const ShaderRegister> context, std::span<const ShaderRegister> primitive);
|
||||
extern "C" void AgcDriverResolveGraphicsAbi_nid_postfix(const Shader* vertex, const Shader* pixel, std::uint32_t primitiveType);
|
||||
|
||||
#endif
|
||||
@@ -32,6 +32,15 @@ struct DrawDecode {
|
||||
std::vector<ShaderRecompiler::ProgramRole> roles;
|
||||
};
|
||||
|
||||
void DecodeGraphicsPrograms(DrawDecode& decoded, const QueueState& queue, const ShaderRegistry& registry, bool staticAbi, bool includeFragment);
|
||||
|
||||
struct PreparedGraphicsStage {
|
||||
std::shared_ptr<const ShaderSnapshot> snapshot;
|
||||
PreparedShaders::Entry entry;
|
||||
};
|
||||
|
||||
std::vector<PreparedGraphicsStage> PrepareGraphicsStages(const DrawDecode& decoded, const ShaderRecompiler::SpirvTarget& target);
|
||||
|
||||
struct DrawRecipeRecord {
|
||||
std::vector<std::weak_ptr<const DispatchVariant>> stages;
|
||||
std::shared_ptr<const DrawRecipe> recipe;
|
||||
|
||||
@@ -57,6 +57,7 @@ public:
|
||||
void ReleaseWindow(void* window);
|
||||
void RegisterShader(const Shader* shader);
|
||||
void ResolveShaderAbi(const Shader* shader, std::span<const ShaderRegister> context, std::span<const ShaderRegister> primitive);
|
||||
void ResolveGraphicsStagesAbi(std::span<const Shader* const> stages, std::span<const ShaderRegister> context, std::span<const ShaderRegister> primitive);
|
||||
void ResolveGraphicsAbi(const Shader* vertex, const Shader* pixel, std::uint32_t primitiveType);
|
||||
|
||||
private:
|
||||
@@ -96,7 +97,7 @@ private:
|
||||
void lookupDispatch(std::uint64_t address, const Submission& submission, std::uint64_t key, bool noDispatchCache, bool traceCache, bool profile, std::span<const ShaderRecompiler::MemoryRegion> memory, DispatchPhaseTiming& phaseTiming, std::array<double, DriverPhaseCount>& phaseMs, std::shared_ptr<const ShaderRecompiler::RecompileResult>& compiledResult, std::shared_ptr<DispatchVariant>& keepVariant, std::vector<ShaderRecompiler::MemoryRegion>& captured, std::vector<std::uint32_t>& liveWords, bool& dataHit, bool& cached, bool& validated, std::shared_ptr<DispatchEntry>& missedEntry, bool& missedDiffering);
|
||||
void insertDispatch(std::uint64_t address, std::uint64_t key, bool noDispatchCache, bool profile, const std::shared_ptr<const ShaderSnapshot>& registeredShader, std::uint64_t forgetAtCapture, std::span<const ShaderRecompiler::MemoryRegion> memory, const std::shared_ptr<ShaderMemory>& shaderMemory, const std::vector<ShaderRecompiler::MemoryRegion>& captured, const std::shared_ptr<const ShaderRecompiler::ResourceCapture>& capture, const std::shared_ptr<const ShaderRecompiler::RecompileResult>& compiledResult, const std::shared_ptr<DispatchEntry>& missedEntry, bool missedDiffering, std::shared_ptr<DispatchVariant>& attachVariant, DispatchPhaseTiming& phaseTiming);
|
||||
void dispatch(QueueState& queue, std::span<const std::uint32_t> packet, const Submission& submission, std::uint64_t indirectArguments = 0);
|
||||
void verifyDataHit(const ShaderSnapshot& snapshot, std::size_t codeOffset, std::uint64_t deviceSerial, ShaderRecompiler::RecompileRequest request, std::span<const ShaderRecompiler::MemoryRegion> memory, std::uint64_t address, const DispatchVariant& variant, std::span<const std::uint32_t> liveWords, const ShaderRecompiler::RecompileResult& patched);
|
||||
void verifyDataHit(const ShaderSnapshot& snapshot, std::size_t codeOffset, ShaderRecompiler::RecompileRequest request, std::span<const ShaderRecompiler::MemoryRegion> memory, std::uint64_t address, const DispatchVariant& variant, std::span<const std::uint32_t> liveWords, const ShaderRecompiler::RecompileResult& patched);
|
||||
static bool drawEntries();
|
||||
static bool verifyDrawEntries();
|
||||
static bool registerKeyEnabled();
|
||||
|
||||
@@ -17,7 +17,6 @@ namespace AgcDriver::DriverDetail {
|
||||
struct PreparedShaders {
|
||||
struct Entry {
|
||||
std::size_t codeOffset;
|
||||
std::uint64_t deviceSerial;
|
||||
std::shared_ptr<const ShaderRecompiler::SourceHandle> handle;
|
||||
};
|
||||
struct Rectangle {
|
||||
@@ -45,8 +44,8 @@ std::shared_ptr<const ShaderSnapshot> ReadRawComputeShader(std::uint64_t address
|
||||
std::uint64_t NullPixelProgramAddress();
|
||||
ShaderRecompiler::RectListShaders PreparedRectangle(const ShaderSnapshot& snapshot, std::uint64_t vertexId, std::uint64_t fragmentId);
|
||||
|
||||
std::shared_ptr<const ShaderRecompiler::SourceHandle> SourceHandleFor(const ShaderSnapshot& snapshot, std::size_t codeOffset, std::uint64_t deviceSerial, const ShaderRecompiler::RecompileRequest& request);
|
||||
ShaderRecompiler::PreparedShaderInvocation InvocationFor(const ShaderSnapshot& snapshot, std::size_t codeOffset, std::uint64_t deviceSerial, const ShaderRecompiler::RecompileRequest& request);
|
||||
std::shared_ptr<const ShaderRecompiler::SourceHandle> SourceHandleFor(const ShaderSnapshot& snapshot, std::size_t codeOffset, const ShaderRecompiler::RecompileRequest& request);
|
||||
ShaderRecompiler::PreparedShaderInvocation InvocationFor(const ShaderSnapshot& snapshot, std::size_t codeOffset, const ShaderRecompiler::RecompileRequest& request);
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -6,14 +6,14 @@
|
||||
|
||||
namespace AgcDriver::DriverDetail {
|
||||
|
||||
void Driver::verifyDataHit(const ShaderSnapshot& snapshot, std::size_t codeOffset, std::uint64_t deviceSerial, ShaderRecompiler::RecompileRequest request, std::span<const ShaderRecompiler::MemoryRegion> memory, std::uint64_t address, const DispatchVariant& variant, std::span<const std::uint32_t> liveWords, const ShaderRecompiler::RecompileResult& patched) {
|
||||
void Driver::verifyDataHit(const ShaderSnapshot& snapshot, std::size_t codeOffset, ShaderRecompiler::RecompileRequest request, std::span<const ShaderRecompiler::MemoryRegion> memory, std::uint64_t address, const DispatchVariant& variant, std::span<const std::uint32_t> liveWords, const ShaderRecompiler::RecompileResult& patched) {
|
||||
const auto fail = [&](const char* what, std::size_t position, std::size_t slot) {
|
||||
std::fprintf(stderr, "[dispatch-cache] APS5_VERIFY_DATA_HITS: %s disagrees (program 0x%llx, position %zu, slot %zu)\n", what, static_cast<unsigned long long>(address), position, slot);
|
||||
std::fflush(stderr);
|
||||
std::abort();
|
||||
};
|
||||
auto verifyMemory = std::make_shared<ShaderMemory>(memory, &queryPendingWrite, &observePendingWrite, hookWaitCounter());
|
||||
const auto invocation = InvocationFor(snapshot, codeOffset, deviceSerial, request);
|
||||
const auto invocation = InvocationFor(snapshot, codeOffset, request);
|
||||
const auto capture = verifyMemory->Capture(invocation);
|
||||
const auto regions = verifyMemory->Regions();
|
||||
request.context.memory = regions;
|
||||
|
||||
@@ -95,7 +95,7 @@ void Driver::dispatch(QueueState& queue, std::span<const std::uint32_t> packet,
|
||||
explicit ProbeScope(bool active) : active(active) { if (active) ShaderRecompiler::SetDebugProbeActive(true); }
|
||||
~ProbeScope() { if (active) ShaderRecompiler::SetDebugProbeActive(false); }
|
||||
} probeScope{probeThis};
|
||||
const auto invocation = InvocationFor(snapshot, codeOffset, localDevice->Serial(), request);
|
||||
const auto invocation = InvocationFor(snapshot, codeOffset, request);
|
||||
if (fillBuffer(queue, submission.queue, packet, std::span(snapshot.code).subspan(codeOffset), userData, compute, localDevice)) {
|
||||
pendingDispatchPhases().outcome = DispatchOutcome::FillHle;
|
||||
return;
|
||||
@@ -225,7 +225,7 @@ void Driver::dispatch(QueueState& queue, std::span<const std::uint32_t> packet,
|
||||
phaseTiming.Phase(PhaseRecompile);
|
||||
insertDispatch(address, key, noDispatchCache, profile, registeredShader, forgetAtCapture, memory, shaderMemory, captured, capture, compiledResult, missedEntry, missedDiffering, attachVariant, phaseTiming);
|
||||
}
|
||||
if (verifyDataHits() && dataHit) verifyDataHit(snapshot, codeOffset, localDevice->Serial(), request, memory, address, *keepVariant, liveWords, *compiledResult);
|
||||
if (verifyDataHits() && dataHit) verifyDataHit(snapshot, codeOffset, request, memory, address, *keepVariant, liveWords, *compiledResult);
|
||||
|
||||
if (recordQueuedLabelsAfterCapture(submission.queue, captured)) {
|
||||
dispatch(queue, packet, submission, indirectArguments);
|
||||
|
||||
@@ -22,7 +22,7 @@ ShaderRecompiler::RecompileResult Driver::materializeDrawStage(std::size_t i, st
|
||||
ShaderRecompiler::GraphicsCompileContext{program.firstUserSgpr, linked, graphics.stages.mesh, graphics.stages.tessellation, {drawParameters.indexAddress, drawParameters.indexCount, drawParameters.indexSize, drawParameters.instanceCount}}
|
||||
};
|
||||
const auto waitedBefore = traceCapSync() || profile ? Graphics::Recorder::ThreadWaitedMs() : 0.0;
|
||||
const auto invocation = InvocationFor(*program.snapshot, program.codeOffset, localDevice->Serial(), request);
|
||||
const auto invocation = InvocationFor(*program.snapshot, program.codeOffset, request);
|
||||
auto& stageCapture = stageCaptures[i];
|
||||
stageCapture.forgetSerial = GuestMemory::ForgetSerial();
|
||||
stageCapture.pushOffset = pushOffset;
|
||||
|
||||
@@ -5,28 +5,38 @@
|
||||
|
||||
namespace AgcDriver::DriverDetail {
|
||||
|
||||
std::shared_ptr<DrawDecode> Driver::decodeDraw(const QueueState& queue, const Submission& submission) {
|
||||
namespace {
|
||||
|
||||
std::uint32_t ReadGraphicsRegister(const Registers& registers, std::uint32_t offset) {
|
||||
const auto found = registers.find(offset);
|
||||
return found == registers.end() ? 0u : found->second;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void DecodeGraphicsPrograms(DrawDecode& decoded, const QueueState& queue, const ShaderRegistry& registry, bool staticAbi, bool includeFragment) {
|
||||
using Stage = ShaderRecompiler::ShaderStage;
|
||||
using Role = ShaderRecompiler::ProgramRole;
|
||||
const auto programAddress = [&](std::uint32_t base) {
|
||||
Graphics::NoteRegisterRead(Graphics::RegisterBank::Shader, base);
|
||||
Graphics::NoteRegisterRead(Graphics::RegisterBank::Shader, base + 1);
|
||||
const auto high = readRegister(queue.shader, base + 1);
|
||||
const auto high = ReadGraphicsRegister(queue.shader, base + 1);
|
||||
require((high & ~0xffu) == 0, "reserved graphics program address bits are set");
|
||||
return (static_cast<std::uint64_t>(readRegister(queue.shader, base)) << 8u) | (static_cast<std::uint64_t>(high) << 40u);
|
||||
return (static_cast<std::uint64_t>(ReadGraphicsRegister(queue.shader, base)) << 8u) | (static_cast<std::uint64_t>(high) << 40u);
|
||||
};
|
||||
const bool pixelSkipped = Graphics::PixelProgramSkipped(queue);
|
||||
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();
|
||||
auto it = submission.shaders->upper_bound(address);
|
||||
require(it != submission.shaders->begin(), "graphics program does not belong to a registered shader");
|
||||
auto it = registry.upper_bound(address);
|
||||
require(it != registry.begin(), "graphics program does not belong to a registered shader");
|
||||
--it;
|
||||
const auto& snapshot = *it->second;
|
||||
require(address - snapshot.codeAddress < snapshot.code.size() * sizeof(std::uint32_t), "graphics program is outside registered shader code");
|
||||
require((address - snapshot.codeAddress) % sizeof(std::uint32_t) == 0, "graphics entry point is not dword aligned");
|
||||
require(snapshot.type == type, "graphics program refers to an incompatible shader binary type");
|
||||
Graphics::NoteRegisterRead(Graphics::RegisterBank::Shader, rsrc2);
|
||||
const auto resources = nullPixel && !queue.shader.contains(rsrc2) ? 0u : readRegister(queue.shader, rsrc2);
|
||||
const auto resources = nullPixel && !queue.shader.contains(rsrc2) ? 0u : ReadGraphicsRegister(queue.shader, rsrc2);
|
||||
const auto userCount = ((resources >> 1u) & 0x1fu) | (((resources >> 27u) & 1u) << 5u);
|
||||
require(userCount <= 32, "graphics user SGPR count exceeds the register bank");
|
||||
const auto codeOffset = static_cast<std::size_t>((address - snapshot.codeAddress) / sizeof(std::uint32_t));
|
||||
@@ -41,13 +51,12 @@ std::shared_ptr<DrawDecode> Driver::decodeDraw(const QueueState& queue, const Su
|
||||
};
|
||||
for (std::uint32_t i = 0; i < userCount; ++i) {
|
||||
Graphics::NoteRegisterRead(Graphics::RegisterBank::Shader, userDataBase + i);
|
||||
result.userData.push_back(readUserData(queue.shader, userDataBase + i));
|
||||
result.userData.push_back((staticInterface ? ReadGraphicsRegister(queue.shader, userDataBase + i) : readUserData(queue.shader, userDataBase + i)));
|
||||
}
|
||||
return result;
|
||||
};
|
||||
{
|
||||
auto product = std::make_shared<DrawDecode>();
|
||||
product->state = Graphics::DecodeState(queue);
|
||||
auto* product = &decoded;
|
||||
auto& programs = product->programs;
|
||||
auto& roles = product->roles;
|
||||
programs.reserve(5);
|
||||
@@ -61,9 +70,10 @@ std::shared_ptr<DrawDecode> Driver::decodeDraw(const QueueState& queue, const Su
|
||||
program.userData.insert(program.userData.begin(), 8, 0);
|
||||
Graphics::NoteRegisterRead(Graphics::RegisterBank::Shader, pointerBase);
|
||||
Graphics::NoteRegisterRead(Graphics::RegisterBank::Shader, pointerBase + 1);
|
||||
if (staticAbi) return;
|
||||
if (!pointerRequired && !queue.shader.contains(pointerBase) && !queue.shader.contains(pointerBase + 1)) return;
|
||||
const auto low = readRegister(queue.shader, pointerBase);
|
||||
const auto high = readRegister(queue.shader, pointerBase + 1);
|
||||
const auto low = ReadGraphicsRegister(queue.shader, pointerBase);
|
||||
const auto high = ReadGraphicsRegister(queue.shader, pointerBase + 1);
|
||||
const auto address = static_cast<std::uint64_t>(low) | (static_cast<std::uint64_t>(high) << 32u);
|
||||
require(address != 0 || !pointerRequired, "merged shader user-data address is null");
|
||||
if (address == 0) return;
|
||||
@@ -79,8 +89,8 @@ std::shared_ptr<DrawDecode> Driver::decodeDraw(const QueueState& queue, const Su
|
||||
append(0x0c8, 2, Stage::TessellationEvaluation, 0x08b, 0x08c, Role::Domain);
|
||||
} else if (graphics.stages.path == Graphics::ShaderPath::Geometry) {
|
||||
const auto frontAddress = programAddress(0xc8);
|
||||
auto snapshot = submission.shaders->upper_bound(frontAddress);
|
||||
require(snapshot != submission.shaders->begin(), "geometry front program is not registered");
|
||||
auto snapshot = registry.upper_bound(frontAddress);
|
||||
require(snapshot != registry.begin(), "geometry front program is not registered");
|
||||
--snapshot;
|
||||
const auto type = snapshot->second->type;
|
||||
require(type == 2 || type == 4, "invalid geometry front binary type");
|
||||
@@ -90,18 +100,25 @@ std::shared_ptr<DrawDecode> Driver::decodeDraw(const QueueState& queue, const Su
|
||||
} else {
|
||||
append(0xc8, 2, Stage::Vertex, 0x8b, 0x8c, Role::Main);
|
||||
}
|
||||
const bool nullPixel = pixelSkipped;
|
||||
if (nullPixel) {
|
||||
const auto rejection = Graphics::NullPixelProgramRejection(queue);
|
||||
require(rejection.empty(), rejection.c_str());
|
||||
if (includeFragment) {
|
||||
if (pixelSkipped) {
|
||||
const auto rejection = Graphics::NullPixelProgramRejection(queue);
|
||||
require(rejection.empty(), rejection.c_str());
|
||||
}
|
||||
append(0x008, 1, Stage::Fragment, 0x00b, 0x00c, Role::Fragment);
|
||||
programs.back().firstUserSgpr = 0;
|
||||
}
|
||||
append(0x008, 1, Stage::Fragment, 0x00b, 0x00c, Role::Fragment);
|
||||
programs.back().firstUserSgpr = 0;
|
||||
product->pixel = Graphics::DecodePixelStageInfo(queue.context, Graphics::ExportMappings(graphics), nullPixel);
|
||||
return product;
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<DrawDecode> Driver::decodeDraw(const QueueState& queue, const Submission& submission) {
|
||||
auto product = std::make_shared<DrawDecode>();
|
||||
product->state = Graphics::DecodeState(queue);
|
||||
DecodeGraphicsPrograms(*product, queue, *submission.shaders, false, true);
|
||||
product->pixel = Graphics::DecodePixelStageInfo(queue.context, Graphics::ExportMappings(product->state), Graphics::PixelProgramUnset(queue));
|
||||
return product;
|
||||
}
|
||||
|
||||
void Driver::resolveDrawDecode(const QueueState& queue, const Submission& submission, std::shared_ptr<const DrawDecode>& decode, bool registerKey, std::uint64_t drawKey, bool profile) {
|
||||
if (decode == nullptr || verifyDrawRecipe()) {
|
||||
std::vector<Graphics::RegisterRead> readLog;
|
||||
|
||||
@@ -116,4 +116,7 @@ extern "C" void AgcDriverResolveShaderAbi_nid_postfix(const Shader* shader, std:
|
||||
|
||||
extern "C" void AgcDriverResolveGraphicsAbi_nid_postfix(const Shader* vertex, const Shader* pixel, std::uint32_t primitiveType) {
|
||||
AgcDriver::DriverDetail::Driver::Get().ResolveGraphicsAbi(vertex, pixel, primitiveType);
|
||||
}
|
||||
}
|
||||
extern "C" void AgcDriverResolveGraphicsStagesAbi_nid_postfix(std::span<const Shader* const> stages, std::span<const ShaderRegister> context, std::span<const ShaderRegister> primitive) {
|
||||
AgcDriver::DriverDetail::Driver::Get().ResolveGraphicsStagesAbi(stages, context, primitive);
|
||||
}
|
||||
@@ -6,6 +6,7 @@
|
||||
#include "prx/libSceAgcDriver/Execution/include/Driver/Diagnostics.hpp"
|
||||
#include "prx/libSceAgcDriver/Execution/include/GuestMemory.hpp"
|
||||
#include "CacheKey.hpp"
|
||||
#include "CompiledVariant.hpp"
|
||||
#include "Optimization/ResourceProgram.hpp"
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
@@ -83,16 +84,16 @@ std::shared_ptr<const ShaderSnapshot> ReadRawComputeShader(std::uint64_t address
|
||||
|
||||
|
||||
|
||||
std::shared_ptr<const ShaderRecompiler::SourceHandle> SourceHandleFor(const ShaderSnapshot& snapshot, std::size_t codeOffset, std::uint64_t deviceSerial, const ShaderRecompiler::RecompileRequest& request) {
|
||||
std::shared_ptr<const ShaderRecompiler::SourceHandle> SourceHandleFor(const ShaderSnapshot& snapshot, std::size_t codeOffset, const ShaderRecompiler::RecompileRequest& request) {
|
||||
std::lock_guard lock(snapshot.prepared->mutex);
|
||||
for (const auto& entry : snapshot.prepared->entries) {
|
||||
if (entry.codeOffset == codeOffset && entry.deviceSerial == deviceSerial && ShaderRecompiler::MatchesPreparedShader(request, *entry.handle)) return entry.handle;
|
||||
if (entry.codeOffset == codeOffset && ShaderRecompiler::MatchesPreparedShader(request, *entry.handle)) return entry.handle;
|
||||
}
|
||||
if (snapshot.header.empty()) {
|
||||
if (snapshot.type != 0 || request.shader.stage != ShaderRecompiler::ShaderStage::Compute) throw std::runtime_error("AGC driver: unregistered program is not a compute shader");
|
||||
APS5_LOG_ERR("Compute shader 0x%llx was not registered; preparing its artifact at dispatch", static_cast<unsigned long long>(snapshot.codeAddress));
|
||||
auto handle = ShaderRecompiler::PrepareShader(request);
|
||||
snapshot.prepared->entries.push_back({codeOffset, deviceSerial, handle});
|
||||
snapshot.prepared->entries.push_back({codeOffset, handle});
|
||||
return handle;
|
||||
}
|
||||
throw std::runtime_error("AGC driver: prepared shader artifact is missing for the requested static ABI");
|
||||
@@ -106,14 +107,14 @@ ShaderRecompiler::RectListShaders PreparedRectangle(const ShaderSnapshot& snapsh
|
||||
throw std::runtime_error("AGC driver: prepared rectangle artifacts are missing");
|
||||
}
|
||||
|
||||
ShaderRecompiler::PreparedShaderInvocation InvocationFor(const ShaderSnapshot& snapshot, std::size_t codeOffset, std::uint64_t deviceSerial, const ShaderRecompiler::RecompileRequest& request) {
|
||||
ShaderRecompiler::PreparedShaderInvocation InvocationFor(const ShaderSnapshot& snapshot, std::size_t codeOffset, const ShaderRecompiler::RecompileRequest& request) {
|
||||
require(codeOffset < snapshot.code.size(), "prepared shader code offset is outside the snapshot");
|
||||
const auto code = std::span(snapshot.code).subspan(codeOffset);
|
||||
require(request.shader.code.data() == code.data() && request.shader.code.size() == code.size(), "prepared invocation does not refer to registered code");
|
||||
auto invocationRequest = request;
|
||||
std::lock_guard lock(snapshot.prepared->mutex);
|
||||
for (const auto& entry : snapshot.prepared->entries) {
|
||||
if (entry.codeOffset != codeOffset || entry.deviceSerial != deviceSerial) continue;
|
||||
if (entry.codeOffset != codeOffset) continue;
|
||||
invocationRequest.shader.code = ShaderRecompiler::GetPreparedCode(*entry.handle);
|
||||
if (auto invocation = ShaderRecompiler::PreparedShaderInvocation::TryCreate(invocationRequest, entry.handle)) return std::move(*invocation);
|
||||
}
|
||||
@@ -230,7 +231,7 @@ std::vector<PreparedShaders::Entry> PrepareRegistered(const ShaderSnapshot& snap
|
||||
ShaderRecompiler::RecompileRequest request{{stage, address, code, snapshot.headerAddress, snapshot.header}, {wave, firstUser, userData, compute, pixel, vertex, memory}, stage == Stage::Compute ? device.ComputeTarget(wave) : device.Target(), {0, 0, 0, 128}, graphics};
|
||||
if (graphics && graphics->mesh) request.layout.pushConstantSizeBytes = ShaderRecompiler::MeshDrawPushOffsetBytes;
|
||||
std::vector<PreparedShaders::Entry> entries;
|
||||
const auto append = [&] { entries.push_back({codeOffset, device.Serial(), ShaderRecompiler::PrepareShader(request)}); };
|
||||
const auto append = [&] { entries.push_back({codeOffset, ShaderRecompiler::PrepareShader(request)}); };
|
||||
append();
|
||||
if (compute) {
|
||||
request.context.compute->partialThreads = {1, 1, 1};
|
||||
@@ -244,9 +245,91 @@ std::vector<PreparedShaders::Entry> PrepareRegistered(const ShaderSnapshot& snap
|
||||
|
||||
}
|
||||
|
||||
std::vector<PreparedGraphicsStage> PrepareGraphicsStages(const DrawDecode& decoded, const ShaderRecompiler::SpirvTarget& target) {
|
||||
require(decoded.programs.size() == decoded.roles.size(), "graphics ABI program roles are incomplete");
|
||||
for (const auto& program : decoded.programs) require(program.snapshot != nullptr, "graphics ABI has no registered shader snapshot");
|
||||
std::vector<ShaderRecompiler::ProgramRole> expected;
|
||||
if (decoded.state.stages.path == Graphics::ShaderPath::Tessellation) expected = {ShaderRecompiler::ProgramRole::Local, ShaderRecompiler::ProgramRole::Hull, ShaderRecompiler::ProgramRole::Domain};
|
||||
else {
|
||||
expected.push_back(ShaderRecompiler::ProgramRole::Main);
|
||||
if (decoded.state.stages.path == Graphics::ShaderPath::Geometry && !decoded.programs.empty() && decoded.programs.front().snapshot->type == 4) expected.push_back(ShaderRecompiler::ProgramRole::GeometryBack);
|
||||
}
|
||||
if (!decoded.roles.empty() && decoded.roles.back() == ShaderRecompiler::ProgramRole::Fragment) expected.push_back(ShaderRecompiler::ProgramRole::Fragment);
|
||||
require(expected == decoded.roles, "graphics ABI linked programs do not match the stage routing");
|
||||
std::vector<ShaderRecompiler::LinkedProgram> linked;
|
||||
std::vector<ShaderRecompiler::MemoryRegion> memory;
|
||||
for (std::size_t index = 0; index < decoded.programs.size(); ++index) {
|
||||
const auto& program = decoded.programs[index];
|
||||
require(program.snapshot != nullptr && program.codeOffset < program.snapshot->code.size(), "graphics ABI has an invalid registered entry point");
|
||||
const auto code = std::span(program.snapshot->code).subspan(program.codeOffset);
|
||||
require(program.binary.code.data() == code.data() && program.binary.code.size() == code.size() && program.binary.codeAddress == program.snapshot->codeAddress + program.codeOffset * sizeof(std::uint32_t), "graphics ABI entry point differs from its snapshot");
|
||||
linked.push_back({decoded.roles[index], program.binary, program.userDataBase, program.firstUserSgpr, program.userData});
|
||||
memory.insert(memory.end(), program.memory.begin(), program.memory.end());
|
||||
}
|
||||
std::vector<PreparedGraphicsStage> prepared;
|
||||
std::uint32_t pushOffset = 0;
|
||||
const auto capacity = decoded.state.stages.mesh ? ShaderRecompiler::MeshDrawPushOffsetBytes : Graphics::PipelinePushConstantBytes;
|
||||
for (std::size_t index = 0; index < decoded.programs.size(); ++index) {
|
||||
if (decoded.roles[index] == ShaderRecompiler::ProgramRole::GeometryBack) continue;
|
||||
const auto& program = decoded.programs[index];
|
||||
const bool fragment = program.binary.stage == ShaderRecompiler::ShaderStage::Fragment;
|
||||
const auto wave = fragment ? decoded.state.stages.fragmentWaveSize : decoded.state.stages.vertexWaveSize;
|
||||
std::optional<ShaderRecompiler::ShaderVertexStageInfo> vertex;
|
||||
if (!fragment) vertex = Graphics::DecodeVertexStageInfo(program.binary.header, program.binary.headerAddress, program.userData, nullptr, true);
|
||||
ShaderRecompiler::RecompileRequest request{program.binary, {wave, program.firstUserSgpr, program.userData, {}, fragment ? std::optional(decoded.pixel) : std::nullopt, vertex, memory}, target, {0, 0, pushOffset, capacity - pushOffset}, ShaderRecompiler::GraphicsCompileContext{program.firstUserSgpr, linked, decoded.state.stages.mesh, decoded.state.stages.tessellation, {}}};
|
||||
const auto handle = ShaderRecompiler::PrepareShader(request);
|
||||
const auto bytes = handle->artifact->bindings.pushConstantSizeBytes;
|
||||
require(bytes <= capacity - pushOffset, "prepared graphics stages exceed the push constant block");
|
||||
prepared.push_back({program.snapshot, {program.codeOffset, handle}});
|
||||
pushOffset += bytes;
|
||||
}
|
||||
return prepared;
|
||||
}
|
||||
|
||||
void Driver::ResolveGraphicsStagesAbi(std::span<const Shader* const> stages, std::span<const ShaderRegister> context, std::span<const ShaderRegister> primitive) {
|
||||
CheckFailure();
|
||||
require(!stages.empty(), "graphics ABI has no shader headers");
|
||||
QueueState state{};
|
||||
std::shared_ptr<const ShaderRegistry> registry;
|
||||
{
|
||||
std::lock_guard lock(mutex);
|
||||
registry = shaders;
|
||||
for (const auto* shader : stages) {
|
||||
GuestMemory::CheckRange(shader, sizeof(Shader), alignof(Shader));
|
||||
const auto address = reinterpret_cast<std::uintptr_t>(const_cast<const void*>(shader->code));
|
||||
require(registry != nullptr && registry->contains(address), "graphics ABI refers to an unregistered shader");
|
||||
const auto& snapshot = *registry->at(address);
|
||||
require(snapshot.headerAddress == reinterpret_cast<std::uintptr_t>(shader), "graphics ABI refers to a replaced shader header");
|
||||
const auto registered = RegisteredState(snapshot);
|
||||
for (const auto& [offset, value] : registered.shader) state.shader.insert_or_assign(offset, value);
|
||||
for (const auto& [offset, value] : registered.context) state.context.insert_or_assign(offset, value);
|
||||
for (const auto& [offset, value] : registered.userConfig) state.userConfig.insert_or_assign(offset, value);
|
||||
}
|
||||
}
|
||||
for (const auto reg : context) state.context.insert_or_assign(reg.offset, reg.value);
|
||||
for (const auto reg : primitive) state.userConfig.insert_or_assign(reg.offset, reg.value);
|
||||
DrawDecode decoded{};
|
||||
decoded.state.stages = Graphics::DecodeShaderStages(state);
|
||||
DecodeGraphicsPrograms(decoded, state, *registry, true, false);
|
||||
const auto localDevice = device.Load();
|
||||
require(localDevice != nullptr, "graphics ABI device is missing");
|
||||
auto prepared = PrepareGraphicsStages(decoded, localDevice->Target());
|
||||
for (auto& stage : prepared) {
|
||||
std::lock_guard lock(stage.snapshot->prepared->mutex);
|
||||
auto& entries = stage.snapshot->prepared->entries;
|
||||
const auto duplicate = std::ranges::any_of(entries, [&](const auto& existing) { return existing.codeOffset == stage.entry.codeOffset && existing.handle->artifact == stage.entry.handle->artifact; });
|
||||
if (!duplicate) entries.push_back(std::move(stage.entry));
|
||||
}
|
||||
}
|
||||
|
||||
void Driver::ResolveShaderAbi(const Shader* shader, std::span<const ShaderRegister> context, std::span<const ShaderRegister> primitive) {
|
||||
CheckFailure();
|
||||
GuestMemory::CheckRange(shader, sizeof(Shader), alignof(Shader));
|
||||
if (shader->type != 0 && shader->type != 1) {
|
||||
const std::array<const Shader*, 1> stages{shader};
|
||||
ResolveGraphicsStagesAbi(stages, context, primitive);
|
||||
return;
|
||||
}
|
||||
std::shared_ptr<const ShaderSnapshot> snapshot;
|
||||
{
|
||||
std::lock_guard lock(mutex);
|
||||
|
||||
@@ -0,0 +1,197 @@
|
||||
#include "VulkanTestDevice.hpp"
|
||||
#include "SceShaders.hpp"
|
||||
#include "prx/libSceAgcDriver/Execution/include/Driver/Draw/DrawCache.hpp"
|
||||
#include "prx/libSceAgcDriver/Graphics/include/ShaderInputState.hpp"
|
||||
#include "Optimization/ResourceProgram.hpp"
|
||||
#include <spirv/unified1/spirv.hpp>
|
||||
#include <array>
|
||||
#include <vector>
|
||||
#include <cstring>
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
|
||||
namespace {
|
||||
|
||||
void Require(bool condition, const char* message) {
|
||||
if (!condition) throw std::runtime_error(message);
|
||||
}
|
||||
|
||||
template<typename TAction>
|
||||
void Reject(TAction action, const char* expected) {
|
||||
try { action(); }
|
||||
catch (const std::exception& error) {
|
||||
Require(std::string(error.what()).find(expected) != std::string::npos, error.what());
|
||||
return;
|
||||
}
|
||||
throw std::runtime_error("invalid graphics ABI was accepted");
|
||||
}
|
||||
|
||||
struct Fixture {
|
||||
struct Header {
|
||||
Shader shader{};
|
||||
ShaderUserData users{};
|
||||
} header;
|
||||
alignas(256) std::array<std::uint32_t, 65> code{};
|
||||
std::shared_ptr<AgcDriver::DriverDetail::ShaderSnapshot> snapshot;
|
||||
|
||||
void Initialize(std::uint8_t type) {
|
||||
header.shader.type = type;
|
||||
header.shader.user_data = &header.users;
|
||||
code.fill(0xffffffffu);
|
||||
code.back() = 0xbf810000u;
|
||||
const auto address = reinterpret_cast<std::uintptr_t>(code.data());
|
||||
const auto headerAddress = reinterpret_cast<std::uintptr_t>(&header);
|
||||
snapshot = std::make_shared<AgcDriver::DriverDetail::ShaderSnapshot>();
|
||||
snapshot->type = type;
|
||||
snapshot->codeAddress = address;
|
||||
snapshot->headerAddress = headerAddress;
|
||||
snapshot->code.assign(code.begin(), code.end());
|
||||
snapshot->header.resize(sizeof(header));
|
||||
std::memcpy(snapshot->header.data(), &header, sizeof(header));
|
||||
}
|
||||
|
||||
void Bind(AgcDriver::QueueState& queue, std::uint32_t program, std::uint32_t resources) const {
|
||||
const auto address = snapshot->codeAddress + 256u;
|
||||
queue.shader[program] = static_cast<std::uint32_t>(address >> 8u);
|
||||
queue.shader[program + 1u] = static_cast<std::uint32_t>(address >> 40u);
|
||||
queue.shader[resources] = 16u << 1u;
|
||||
}
|
||||
};
|
||||
|
||||
void Check(AgcDriver::VulkanDevice& device, AgcDriver::Graphics::ShaderPath path, const std::filesystem::path& dump) {
|
||||
using namespace AgcDriver::DriverDetail;
|
||||
using namespace ShaderRecompiler;
|
||||
Fixture front;
|
||||
Fixture back;
|
||||
Fixture domain;
|
||||
Fixture fragment;
|
||||
const bool tessellation = path == AgcDriver::Graphics::ShaderPath::Tessellation;
|
||||
const bool mesh = path == AgcDriver::Graphics::ShaderPath::Geometry;
|
||||
front.Initialize(tessellation ? 5u : mesh ? 4u : 2u);
|
||||
back.Initialize(tessellation ? 7u : 6u);
|
||||
domain.Initialize(2u);
|
||||
fragment.Initialize(1u);
|
||||
const std::array<std::uint32_t, 7> pixelCode{0xc8020002u, 0xc8060102u, 0xc80a0202u, 0xc80e0302u, 0xf800180fu, 0x03020100u, 0xbf810000u};
|
||||
fragment.snapshot->code.resize(64);
|
||||
fragment.snapshot->code.insert(fragment.snapshot->code.end(), pixelCode.begin(), pixelCode.end());
|
||||
ShaderRegistry registry;
|
||||
for (const auto* fixture : {&front, &back, &domain, &fragment}) registry.emplace(fixture->snapshot->codeAddress, fixture->snapshot);
|
||||
AgcDriver::QueueState queue{};
|
||||
front.Bind(queue, tessellation ? 0x148u : 0xc8u, tessellation ? 0x10bu : 0x8bu);
|
||||
if (tessellation || mesh) back.Bind(queue, tessellation ? 0x108u : 0x88u, tessellation ? 0x10bu : 0x8bu);
|
||||
if (tessellation) domain.Bind(queue, 0xc8u, 0x8bu);
|
||||
fragment.Bind(queue, 0x8u, 0xbu);
|
||||
alignas(8) const std::array<std::uint32_t, 2> merged{};
|
||||
const auto mergedAddress = reinterpret_cast<std::uintptr_t>(merged.data());
|
||||
const auto pointerBase = tessellation ? 0x102u : 0x82u;
|
||||
queue.shader[pointerBase] = static_cast<std::uint32_t>(mergedAddress);
|
||||
queue.shader[pointerBase + 1u] = static_cast<std::uint32_t>(mergedAddress >> 32u);
|
||||
DrawDecode prepared{};
|
||||
prepared.state.stages.path = path;
|
||||
prepared.state.stages.vertexWaveSize = tessellation || mesh ? 64u : 32u;
|
||||
prepared.state.stages.fragmentWaveSize = 32u;
|
||||
if (mesh) prepared.state.stages.mesh = MeshConfiguration{4, 1, 3, 3, 1, 64, 128, 0, 4};
|
||||
if (tessellation) prepared.state.stages.tessellation = TessellationConfiguration{3, 4, 1, 2, 2};
|
||||
prepared.pixel.wave32 = true;
|
||||
prepared.pixel.interpolatorCount = 2;
|
||||
prepared.pixel.interpolatorSettings[0] = 0x403u;
|
||||
prepared.pixel.interpolatorSettings[1] = 0x220u;
|
||||
prepared.pixel.targetOutputMode[0] = 9;
|
||||
prepared.pixel.targetExportMapping.fill(0xe4u);
|
||||
DecodeGraphicsPrograms(prepared, queue, registry, true, true);
|
||||
DrawDecode draw{};
|
||||
draw.state = prepared.state;
|
||||
draw.pixel = prepared.pixel;
|
||||
DecodeGraphicsPrograms(draw, queue, registry, false, true);
|
||||
Require(prepared.programs.size() == draw.programs.size() && prepared.roles == draw.roles, "prepared graphics programs differ from draw programs");
|
||||
for (std::size_t index = 0; index < prepared.programs.size(); ++index) {
|
||||
const auto& expected = prepared.programs[index];
|
||||
const auto& actual = draw.programs[index];
|
||||
Require(expected.codeOffset == 64u && expected.binary.code.size() == (prepared.roles[index] == ProgramRole::Fragment ? pixelCode.size() : 1u) && expected.binary.code[0] == (prepared.roles[index] == ProgramRole::Fragment ? pixelCode[0] : 0xbf810000u), "graphics entry point did not trim the code prefix");
|
||||
Require(expected.binary.stage == actual.binary.stage && expected.firstUserSgpr == actual.firstUserSgpr && expected.userData.size() == actual.userData.size(), "graphics preparation changed the user SGPR ABI");
|
||||
}
|
||||
auto target = device.Target();
|
||||
std::vector<std::uint32_t> capabilities(target.supportedCapabilities.begin(), target.supportedCapabilities.end());
|
||||
std::vector<std::string_view> extensions(target.supportedExtensions.begin(), target.supportedExtensions.end());
|
||||
if (mesh) {
|
||||
capabilities.push_back(spv::CapabilityMeshShadingEXT);
|
||||
extensions.push_back("SPV_EXT_mesh_shader");
|
||||
target.supportedCapabilities = capabilities;
|
||||
target.supportedExtensions = extensions;
|
||||
target.mesh = MeshTargetLimits{{128, 1, 1}, 128, 32768, 256, 256, 128, 32768, 1, 1};
|
||||
}
|
||||
const auto stages = PrepareGraphicsStages(prepared, target);
|
||||
Require(stages.size() == (tessellation ? 4u : 2u), "graphics preparation compiled the wrong stages");
|
||||
for (const auto& stage : stages) stage.snapshot->prepared->entries.push_back(stage.entry);
|
||||
const auto& pixelArtifact = GetPreparedArtifact(*stages.back().entry.handle);
|
||||
Require(!pixelArtifact.fragmentParameters.empty() && pixelArtifact.fragmentParameters.front().sourceLocation == 3u, "prepared fragment lost interpolant mapping");
|
||||
if (!dump.empty()) {
|
||||
for (std::size_t index = 0; index < stages.size(); ++index) {
|
||||
const auto& words = GetPreparedArtifact(*stages[index].entry.handle).spirv;
|
||||
const auto name = dump / (std::to_string(static_cast<unsigned>(path)) + "-" + std::to_string(index) + ".spv");
|
||||
std::ofstream output(name, std::ios::binary);
|
||||
output.write(reinterpret_cast<const char*>(words.data()), static_cast<std::streamsize>(words.size() * sizeof(std::uint32_t)));
|
||||
Require(static_cast<bool>(output), "cannot write prepared graphics SPIR-V");
|
||||
}
|
||||
}
|
||||
auto incomplete = prepared;
|
||||
incomplete.roles.pop_back();
|
||||
Reject([&] { static_cast<void>(PrepareGraphicsStages(incomplete, target)); }, "program roles are incomplete");
|
||||
auto wrongEntry = prepared;
|
||||
wrongEntry.programs.front().binary.codeAddress += 4;
|
||||
Reject([&] { static_cast<void>(PrepareGraphicsStages(wrongEntry, target)); }, "entry point differs");
|
||||
std::vector<LinkedProgram> linked;
|
||||
std::vector<MemoryRegion> memory;
|
||||
for (std::size_t index = 0; index < draw.programs.size(); ++index) {
|
||||
const auto& program = draw.programs[index];
|
||||
linked.push_back({draw.roles[index], program.binary, program.userDataBase, program.firstUserSgpr, program.userData});
|
||||
memory.insert(memory.end(), program.memory.begin(), program.memory.end());
|
||||
}
|
||||
for (std::size_t index = 0; index < draw.programs.size(); ++index) {
|
||||
if (draw.roles[index] == ProgramRole::GeometryBack) continue;
|
||||
const auto& program = draw.programs[index];
|
||||
const bool pixel = program.binary.stage == ShaderStage::Fragment;
|
||||
std::optional<ShaderVertexStageInfo> vertex;
|
||||
if (!pixel) vertex = AgcDriver::Graphics::DecodeVertexStageInfo(program.binary.header, program.binary.headerAddress, program.userData, nullptr, true);
|
||||
RecompileRequest request{program.binary, {pixel ? 32u : prepared.state.stages.vertexWaveSize, program.firstUserSgpr, program.userData, {}, pixel ? std::optional(draw.pixel) : std::nullopt, vertex, memory}, target, {0, 0, 0, mesh ? MeshDrawPushOffsetBytes : 128u}, GraphicsCompileContext{program.firstUserSgpr, linked, prepared.state.stages.mesh, prepared.state.stages.tessellation, {0, 3, 4, 1}}};
|
||||
static_cast<void>(InvocationFor(*program.snapshot, program.codeOffset, request));
|
||||
request.layout.pushConstantOffsetBytes = 4;
|
||||
request.layout.pushConstantSizeBytes -= 4;
|
||||
Reject([&] { static_cast<void>(InvocationFor(*program.snapshot, program.codeOffset, request)); }, "artifact is missing");
|
||||
request.layout = {0, 0, 0, mesh ? MeshDrawPushOffsetBytes : 128u};
|
||||
if (pixel) request.context.pixel->interpolatorSettings[0] ^= 1u;
|
||||
else if (tessellation) ++request.graphics->tessellation->outputControlPoints;
|
||||
else if (mesh) ++request.graphics->mesh->maxVertices;
|
||||
else request.context.userDataBaseRegister = 0;
|
||||
Reject([&] { static_cast<void>(InvocationFor(*program.snapshot, program.codeOffset, request)); }, "artifact is missing");
|
||||
}
|
||||
ShaderRegistry invalidRegistry;
|
||||
DrawDecode invalid{};
|
||||
invalid.state = prepared.state;
|
||||
Reject([&] { DecodeGraphicsPrograms(invalid, queue, invalidRegistry, true, true); }, "registered");
|
||||
const auto frontBase = tessellation ? 0x148u : 0xc8u;
|
||||
queue.shader[frontBase + 1u] |= 0x100u;
|
||||
Reject([&] { DecodeGraphicsPrograms(invalid, queue, registry, true, true); }, "reserved graphics program address");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
try {
|
||||
auto device = OpenVulkanTestDevice();
|
||||
if (!device) return VulkanTestSkipped;
|
||||
Require(argc <= 2, "invalid test arguments");
|
||||
const auto dump = argc == 2 ? std::filesystem::path(argv[1]) : std::filesystem::path{};
|
||||
Check(*device, AgcDriver::Graphics::ShaderPath::Vertex, dump);
|
||||
Check(*device, AgcDriver::Graphics::ShaderPath::Geometry, dump);
|
||||
Check(*device, AgcDriver::Graphics::ShaderPath::Tessellation, dump);
|
||||
std::cout << "prepared graphics ABI tests passed\n";
|
||||
return 0;
|
||||
} catch (const std::exception& error) {
|
||||
std::cerr << error.what() << '\n';
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
@@ -34,16 +34,15 @@ void Run(AgcDriver::VulkanDevice& device) {
|
||||
const ShaderRecompiler::ShaderComputeStageInfo compute{{1, 1, 1}, 0, {false, false, false}, false, 1, {}};
|
||||
ShaderRecompiler::RecompileRequest request{{ShaderRecompiler::ShaderStage::Compute, reinterpret_cast<std::uintptr_t>(code.data()), code, 0, {}}, {32, 0, users, compute, {}, {}, {}}, device.ComputeTarget(32), {0, 0, 0, 128}};
|
||||
AgcDriver::DriverDetail::ShaderSnapshot snapshot{request.shader.codeAddress, 0, 0, {code.begin(), code.end()}, {}};
|
||||
ExpectFailure([&] { static_cast<void>(AgcDriver::DriverDetail::SourceHandleFor(snapshot, 0, device.Serial(), request)); }, "artifact is missing");
|
||||
ExpectFailure([&] { static_cast<void>(AgcDriver::DriverDetail::SourceHandleFor(snapshot, 0, request)); }, "artifact is missing");
|
||||
const auto handle = ShaderRecompiler::PrepareShader(request);
|
||||
snapshot.prepared->entries.push_back({0, device.Serial(), handle});
|
||||
ExpectFailure([&] { static_cast<void>(AgcDriver::DriverDetail::SourceHandleFor(snapshot, 1, device.Serial(), request)); }, "artifact is missing");
|
||||
ExpectFailure([&] { static_cast<void>(AgcDriver::DriverDetail::SourceHandleFor(snapshot, 0, device.Serial() + 1, request)); }, "artifact is missing");
|
||||
snapshot.prepared->entries.push_back({0, handle});
|
||||
ExpectFailure([&] { static_cast<void>(AgcDriver::DriverDetail::SourceHandleFor(snapshot, 1, request)); }, "artifact is missing");
|
||||
const auto& artifact = ShaderRecompiler::GetPreparedArtifact(*handle);
|
||||
for (std::uint32_t value = 0; value < 8; ++value) {
|
||||
users.fill(value);
|
||||
request.useCache = value % 2 == 0;
|
||||
const auto ready = AgcDriver::DriverDetail::SourceHandleFor(snapshot, 0, device.Serial(), request);
|
||||
const auto ready = AgcDriver::DriverDetail::SourceHandleFor(snapshot, 0, request);
|
||||
ShaderRecompiler::SrtRuntime runtime{};
|
||||
runtime.userData = users;
|
||||
const auto capture = ShaderRecompiler::CaptureResources(request, runtime, *ready);
|
||||
@@ -55,12 +54,11 @@ void Run(AgcDriver::VulkanDevice& device) {
|
||||
auto registeredRequest = request;
|
||||
registeredRequest.shader.code = snapshot.code;
|
||||
registeredRequest.useCache = true;
|
||||
const auto invocation = AgcDriver::DriverDetail::InvocationFor(snapshot, 0, device.Serial(), registeredRequest);
|
||||
ExpectFailure([&] { static_cast<void>(AgcDriver::DriverDetail::InvocationFor(snapshot, 0, device.Serial(), request)); }, "does not refer to registered code");
|
||||
ExpectFailure([&] { static_cast<void>(AgcDriver::DriverDetail::InvocationFor(snapshot, 1, device.Serial(), registeredRequest)); }, "outside the snapshot");
|
||||
ExpectFailure([&] { static_cast<void>(AgcDriver::DriverDetail::InvocationFor(snapshot, 0, device.Serial() + 1, registeredRequest)); }, "artifact is missing");
|
||||
const auto invocation = AgcDriver::DriverDetail::InvocationFor(snapshot, 0, registeredRequest);
|
||||
ExpectFailure([&] { static_cast<void>(AgcDriver::DriverDetail::InvocationFor(snapshot, 0, request)); }, "does not refer to registered code");
|
||||
ExpectFailure([&] { static_cast<void>(AgcDriver::DriverDetail::InvocationFor(snapshot, 1, registeredRequest)); }, "outside the snapshot");
|
||||
registeredRequest.context.compute->numThreads[0] = 2;
|
||||
ExpectFailure([&] { static_cast<void>(AgcDriver::DriverDetail::InvocationFor(snapshot, 0, device.Serial(), registeredRequest)); }, "artifact is missing");
|
||||
ExpectFailure([&] { static_cast<void>(AgcDriver::DriverDetail::InvocationFor(snapshot, 0, registeredRequest)); }, "artifact is missing");
|
||||
ShaderRecompiler::SrtRuntime preparedRuntime{};
|
||||
preparedRuntime.userData = users;
|
||||
const auto preparedCapture = invocation.Capture(preparedRuntime);
|
||||
@@ -73,11 +71,25 @@ void Run(AgcDriver::VulkanDevice& device) {
|
||||
Require(changedResult != firstResult && changedResult->spirv.data() == firstResult->spirv.data(), "prepared invocation did not distinguish changed runtime data");
|
||||
users[0] -= 1;
|
||||
Require(invocation.Materialize(*preparedCapture) == firstResult, "prepared invocation evicted the previous runtime data");
|
||||
auto replacement = OpenVulkanTestDevice();
|
||||
Require(replacement != nullptr && replacement->Serial() != device.Serial(), "replacement device was not created");
|
||||
auto replacementRequest = request;
|
||||
replacementRequest.target = replacement->ComputeTarget(32);
|
||||
Require(AgcDriver::DriverDetail::SourceHandleFor(snapshot, 0, replacementRequest) == handle, "device replacement discarded a compatible prepared artifact");
|
||||
replacementRequest.shader.code = snapshot.code;
|
||||
const auto replacementInvocation = AgcDriver::DriverDetail::InvocationFor(snapshot, 0, replacementRequest);
|
||||
const auto replacementCapture = replacementInvocation.Capture(preparedRuntime);
|
||||
const auto replacementResult = replacementInvocation.Materialize(*replacementCapture);
|
||||
Require(replacementResult->spirv.data() == artifact.spirv.data(), "device replacement recompiled the prepared shader");
|
||||
replacement->Dispatch(*replacementResult, 1, 1, 1);
|
||||
replacement->WaitIdle();
|
||||
replacementRequest.target.nonConstantImageOffsets = !replacementRequest.target.nonConstantImageOffsets;
|
||||
ExpectFailure([&] { static_cast<void>(AgcDriver::DriverDetail::InvocationFor(snapshot, 0, replacementRequest)); }, "artifact is missing");
|
||||
ShaderRecompiler::SrtRuntime runtime{};
|
||||
runtime.userData = users;
|
||||
const auto capture = ShaderRecompiler::CaptureResources(request, runtime, *handle);
|
||||
request.context.compute->numThreads[0] = 2;
|
||||
ExpectFailure([&] { static_cast<void>(AgcDriver::DriverDetail::SourceHandleFor(snapshot, 0, device.Serial(), request)); }, "artifact is missing");
|
||||
ExpectFailure([&] { static_cast<void>(AgcDriver::DriverDetail::SourceHandleFor(snapshot, 0, request)); }, "artifact is missing");
|
||||
ExpectFailure([&] { static_cast<void>(ShaderRecompiler::CaptureResources(request, runtime, *handle)); }, "does not match the static ABI");
|
||||
ExpectFailure([&] { static_cast<void>(ShaderRecompiler::MaterializeShader(request, *capture, *handle)); }, "does not match the static ABI");
|
||||
const auto otherHandle = ShaderRecompiler::PrepareShader(request);
|
||||
@@ -86,10 +98,10 @@ void Run(AgcDriver::VulkanDevice& device) {
|
||||
ExpectFailure([&] { static_cast<void>(ShaderRecompiler::MaterializeShader(request, *capture, *otherHandle)); }, "another prepared shader");
|
||||
request.context.compute->numThreads[0] = 1;
|
||||
request.layout.pushConstantSizeBytes = 124;
|
||||
ExpectFailure([&] { static_cast<void>(AgcDriver::DriverDetail::SourceHandleFor(snapshot, 0, device.Serial(), request)); }, "artifact is missing");
|
||||
ExpectFailure([&] { static_cast<void>(AgcDriver::DriverDetail::SourceHandleFor(snapshot, 0, request)); }, "artifact is missing");
|
||||
request.layout.pushConstantSizeBytes = 128;
|
||||
code[0] = 0xffffffffu;
|
||||
ExpectFailure([&] { static_cast<void>(AgcDriver::DriverDetail::SourceHandleFor(snapshot, 0, device.Serial(), request)); }, "artifact is missing");
|
||||
ExpectFailure([&] { static_cast<void>(AgcDriver::DriverDetail::SourceHandleFor(snapshot, 0, request)); }, "artifact is missing");
|
||||
}
|
||||
|
||||
void PrepareMultisampledStorage(AgcDriver::VulkanDevice& device) {
|
||||
@@ -97,6 +109,8 @@ void PrepareMultisampledStorage(AgcDriver::VulkanDevice& device) {
|
||||
std::array<std::uint32_t, 13> code{0xd7460000u, 0x0401060cu, 0xd7460001u, 0x0405060du, 0x7e04020eu, 0x7e060280u, 0x7e080208u, 0x7e0a0209u, 0x7e0c020au, 0x7e0e020bu, 0xf0200f38u, 0x00000400u, 0xbf810000u};
|
||||
RecompileRequest request{};
|
||||
request.shader = {ShaderStage::Compute, 0x30000u, code, 0, {}};
|
||||
const std::array<std::uint32_t, 16> users{};
|
||||
request.context.userData = users;
|
||||
request.context.waveSize = 32;
|
||||
request.context.compute = ShaderComputeStageInfo{{8, 8, 1}, 0, {true, true, false}, false, 2, {}};
|
||||
request.target = device.ComputeTarget(32);
|
||||
|
||||
@@ -52,6 +52,7 @@ public:
|
||||
append(key, request.context.pixel);
|
||||
append(key, request.context.vertex);
|
||||
appendMesh(key, request);
|
||||
appendTessellation(key, request);
|
||||
std::uint64_t hash = 0xcbf29ce484222325ull;
|
||||
for (const auto value : key) {
|
||||
hash ^= value;
|
||||
@@ -82,6 +83,7 @@ private:
|
||||
append(key, request.context.pixel);
|
||||
append(key, request.context.vertex);
|
||||
appendMesh(key, request);
|
||||
appendTessellation(key, request);
|
||||
append(key, request.target);
|
||||
append(key, DebugProbeActive());
|
||||
append(key, RayTracingStrict());
|
||||
@@ -96,6 +98,14 @@ private:
|
||||
for (const auto value : {mesh->inputPrimitive, mesh->primitivesPerGroup, mesh->verticesPerGroup, mesh->maxVertices, mesh->maxPrimitives, mesh->threadsPerGroup, mesh->ldsSizeDwords, mesh->provokingVertex, mesh->esgsItemSize}) append(key, value);
|
||||
}
|
||||
|
||||
static void appendTessellation(std::vector<std::uint64_t>& key, const RecompileRequest& request) {
|
||||
if (request.shader.stage != ShaderStage::Local && request.shader.stage != ShaderStage::TessellationControl && request.shader.stage != ShaderStage::TessellationEvaluation) return;
|
||||
const auto* tessellation = request.graphics && request.graphics->tessellation ? &*request.graphics->tessellation : nullptr;
|
||||
append(key, tessellation != nullptr);
|
||||
if (tessellation == nullptr) return;
|
||||
for (const auto value : {tessellation->inputControlPoints, tessellation->outputControlPoints, tessellation->domain, tessellation->partitioning, tessellation->outputTopology}) append(key, value);
|
||||
}
|
||||
|
||||
template<typename TValue>
|
||||
static void append(std::vector<std::uint64_t>& key, TValue value) requires (std::is_integral_v<TValue> || std::is_enum_v<TValue>) {
|
||||
key.push_back(static_cast<std::uint64_t>(value));
|
||||
|
||||
@@ -86,7 +86,8 @@ std::uint32_t HostSubgroupSize(const RecompileRequest& request) {
|
||||
|
||||
ShaderStageInputInfo RequestInputInfo(const RecompileRequest& request) {
|
||||
const auto* mesh = request.graphics && request.graphics->mesh ? &*request.graphics->mesh : nullptr;
|
||||
return BuildShaderStageInputInfo(toShaderStageKind(request.shader.stage), request.context, HostSubgroupSize(request), mesh);
|
||||
const auto* tessellation = request.graphics && request.graphics->tessellation ? &*request.graphics->tessellation : nullptr;
|
||||
return BuildShaderStageInputInfo(toShaderStageKind(request.shader.stage), request.context, HostSubgroupSize(request), mesh, tessellation);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ namespace ShaderRecompiler {
|
||||
|
||||
// hostSubgroupSize is the device subgroup width; wave64 workgroups on 32-wide subgroups run each
|
||||
// invocation as two guest lanes so a guest wave stays one host subgroup.
|
||||
ShaderStageInputInfo BuildShaderStageInputInfo(ShaderStageKind stage, const GuestContext& context, std::uint32_t hostSubgroupSize, const MeshConfiguration* mesh = nullptr);
|
||||
ShaderStageInputInfo BuildShaderStageInputInfo(ShaderStageKind stage, const GuestContext& context, std::uint32_t hostSubgroupSize, const MeshConfiguration* mesh = nullptr, const TessellationConfiguration* tessellation = nullptr);
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -63,7 +63,7 @@ void _detectVertexBuffers(ShaderVertexInputInfo& info) {
|
||||
|
||||
}
|
||||
|
||||
ShaderStageInputInfo BuildShaderStageInputInfo(ShaderStageKind stage, const GuestContext& context, std::uint32_t hostSubgroupSize, const MeshConfiguration* mesh) {
|
||||
ShaderStageInputInfo BuildShaderStageInputInfo(ShaderStageKind stage, const GuestContext& context, std::uint32_t hostSubgroupSize, const MeshConfiguration* mesh, const TessellationConfiguration* tessellation) {
|
||||
switch (stage) {
|
||||
case ShaderStageKind::Compute: {
|
||||
if (!context.compute.has_value()) {
|
||||
@@ -171,6 +171,17 @@ ShaderStageInputInfo BuildShaderStageInputInfo(ShaderStageKind stage, const Gues
|
||||
vertexStorage.resourcesDst[i].fetchIndex = vertex.resourcesDst[i].fetchIndex;
|
||||
}
|
||||
_detectVertexBuffers(vertexStorage);
|
||||
if (stage == ShaderStageKind::Local || stage == ShaderStageKind::TessellationControl || stage == ShaderStageKind::TessellationEvaluation) {
|
||||
if (tessellation == nullptr) throw std::runtime_error("ShaderInputInfoBuilder: tessellation configuration is missing");
|
||||
if (tessellation->inputControlPoints == 0u || tessellation->inputControlPoints > 32u || tessellation->outputControlPoints == 0u || tessellation->outputControlPoints > 32u) throw std::runtime_error("ShaderInputInfoBuilder: invalid tessellation control-point counts");
|
||||
if (tessellation->domain != 1u || tessellation->partitioning != 2u || tessellation->outputTopology != 2u) throw std::runtime_error("ShaderInputInfoBuilder: unsupported tessellation configuration");
|
||||
auto& target = vertexStorage.tess;
|
||||
target.inputControlPoints = tessellation->inputControlPoints;
|
||||
target.outputControlPoints = tessellation->outputControlPoints;
|
||||
target.domain = tessellation->domain;
|
||||
target.partitioning = tessellation->partitioning;
|
||||
target.outputTopology = tessellation->outputTopology;
|
||||
}
|
||||
if (stage == ShaderStageKind::Mesh) {
|
||||
if (mesh == nullptr) throw std::runtime_error("ShaderInputInfoBuilder: a mesh-stage program has no mesh configuration");
|
||||
auto& target = vertexStorage.mesh;
|
||||
|
||||
Reference in new issue
Block a user