Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion core/libs/prx/libSceAgcDriver/tests/ShaderDiskCache.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1355,7 +1355,7 @@ IrResourcePlan handBuiltPlan() {
DescriptorSource table;
table.dwordCount = 8u;
table.dwords[0] = &readConst(3);
table.indirectImage = DescriptorSource::IndirectImage{3u, 2u, 16u, 4u, 2u, 32u, true};
table.indirectImage = DescriptorSource::IndirectImage{3u, 2u, 16u, 4u, 2u, 32u, 48u, true};
DescriptorSource heap;
heap.dwordCount = 4u;
heap.dwords[1] = &readConst(6);
Expand Down
52 changes: 52 additions & 0 deletions core/libs/prx/libSceAgcDriver/tests/ShaderMemory.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1076,6 +1076,58 @@ void verifyBindlessTable() {
#if ANYPS5_ENABLE_SPIRV_TOOLS
static_cast<void>(ValidateAndOptimizeSpirv(queried->spirv, query.target.vulkanVersion, query.target.spirvVersion));
#endif
static std::array<std::uint32_t, 48> strided{};
for (std::uint32_t entry = 0; entry < 4u; entry++) {
std::fill_n(strided.begin() + entry * 12u, 4u, 0xdead0000u + entry);
std::copy(heap[entry].begin(), heap[entry].end(), strided.begin() + entry * 12u + 4u);
}
const std::vector<std::uint32_t> stridedCode{0xf4080100u, 0xfa000000u, 0xf4080200u, 0xfa000010u, 0xf4080300u, 0xfa000020u, 0xf4080700u, 0xfa000030u, 0x7e200500u, 0x9310ff10u, 0x00000030u, 0xf42c0502u, 0x20000010u, 0xf09c0f08u, 0x00450000u, 0xe0700000u, 0x80070000u, 0xbf810000u};
const auto stridedHeapV = bufferDescriptor(strided.data(), 48u, 4u);
std::copy(stridedHeapV.begin(), stridedHeapV.end(), srt.begin());
auto stridedRequest = makeRequest(stridedCode);
const auto stridedPlan = GetResourcePlan(stridedRequest);
std::uint32_t stridedTables = 0;
for (const auto& source : stridedPlan->descriptorSources) {
if (!source.indirectImage.has_value()) continue;
stridedTables++;
require(!source.indirectImage->hasMaterial && source.indirectImage->entryStride == 48u && source.indirectImage->entryOffset == 16u, "bindless: the 48-byte table entries were not recorded");
}
require(stridedTables == 1, "bindless: a 48-byte table was not planned");
AgcDriver::ShaderMemory stridedMemory({});
const auto stridedCapture = stridedMemory.Capture(stridedRequest);
const auto stridedDirect = static_cast<std::uint32_t>(stridedPlan->info.images.size());
const auto stridedRoot = tableRoot(*stridedCapture, stridedDirect);
const auto stridedMapping = mappingOf(stridedCapture->snapshot);
require(std::vector<std::uint32_t>(stridedMapping.begin(), stridedMapping.begin() + 7) == std::vector<std::uint32_t>{3u, 0u, 0u, 1u, 1u, 3u, 3u}, "bindless: the 48-byte table is not the identity mapping");
require(stridedCapture->snapshot.images[stridedRoot].dwords == heap[0] && stridedCapture->snapshot.images[stridedDirect].dwords == heap[1] && stridedCapture->snapshot.images[stridedDirect + 1u].dwords == heap[2] && stridedCapture->snapshot.images[stridedDirect + 2u].dwords == heap[3], "bindless: the 48-byte table slots are wrong");
stridedRequest.context.memory = stridedMemory.Regions();
require(!Recompile(stridedRequest, *stridedCapture)->spirv.empty(), "bindless: the 48-byte table did not compile");

static std::array<std::uint32_t, 64> leading{};
for (std::uint32_t entry = 0; entry < 4u; entry++) {
std::copy(heap[entry].begin(), heap[entry].end(), leading.begin() + entry * 16u);
std::fill_n(leading.begin() + entry * 16u + 8u, 8u, 0xdead0000u + entry);
}
const std::vector<std::uint32_t> leadingCode{0xf4080100u, 0xfa000000u, 0xf4080200u, 0xfa000010u, 0xf4080300u, 0xfa000020u, 0xf4080700u, 0xfa000030u, 0x7e200500u, 0x9310ff10u, 0x00000040u, 0xf42c0502u, 0x20000000u, 0xf09c0f08u, 0x00450000u, 0xe0700000u, 0x80070000u, 0xbf810000u};
const auto leadingHeapV = bufferDescriptor(leading.data(), 64u, 4u);
std::copy(leadingHeapV.begin(), leadingHeapV.end(), srt.begin());
auto leadingRequest = makeRequest(leadingCode);
const auto leadingPlan = GetResourcePlan(leadingRequest);
std::uint32_t leadingTables = 0;
for (const auto& source : leadingPlan->descriptorSources) {
if (!source.indirectImage.has_value()) continue;
leadingTables++;
require(!source.indirectImage->hasMaterial && source.indirectImage->entryStride == 64u && source.indirectImage->entryOffset == 0u, "bindless: the 64-byte table entries were not recorded");
}
require(leadingTables == 1, "bindless: a 64-byte table was not planned");
AgcDriver::ShaderMemory leadingMemory({});
const auto leadingCapture = leadingMemory.Capture(leadingRequest);
const auto leadingDirect = static_cast<std::uint32_t>(leadingPlan->info.images.size());
const auto leadingRoot = tableRoot(*leadingCapture, leadingDirect);
require(leadingCapture->snapshot.images[leadingRoot].dwords == heap[0] && leadingCapture->snapshot.images[leadingDirect].dwords == heap[1] && leadingCapture->snapshot.images[leadingDirect + 1u].dwords == heap[2] && leadingCapture->snapshot.images[leadingDirect + 2u].dwords == heap[3], "bindless: the 64-byte table with the T# first and data behind it has wrong slots");
leadingRequest.context.memory = leadingMemory.Regions();
require(!Recompile(leadingRequest, *leadingCapture)->spirv.empty(), "bindless: the 64-byte table did not compile");
fillSrt(4u);

// A table wider than the slots without a material pattern is rejected.
fillSrt(100u);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ struct DescriptorSource {
std::uint32_t selectorOffset = 0;
std::uint32_t keyArg = 0;
std::uint32_t entryOffset = 0;
std::uint32_t entryStride = 32;
bool hasMaterial = false;

bool operator==(const IndirectImage& other) const = default;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -333,8 +333,8 @@ void resolveTableImage(const IrResourcePlan& plan, std::uint32_t imageIndex, con
const ShaderBufferResource heap = decodeBufferDescriptor(heapValue);
const std::uint64_t heapSize = heap.GetSize();
if (heap.Type() != 0u || heap.Base48() > std::numeric_limits<std::uint64_t>::max() - heapSize) throw std::runtime_error("bindless heap buffer is invalid");
if (heapSize != 0u && (heapSize < table.entryOffset || (heapSize - table.entryOffset) % TableEntryBytes != 0u)) throw std::runtime_error("bindless heap contains a partial descriptor");
const auto entries = heapSize > table.entryOffset ? static_cast<std::uint32_t>(std::min<std::uint64_t>((heapSize - table.entryOffset) / TableEntryBytes, std::numeric_limits<std::uint32_t>::max())) : 0u;
if (heapSize != 0u && (heapSize < table.entryOffset + TableEntryBytes || ((heapSize - table.entryOffset) % table.entryStride != 0u && (heapSize - table.entryOffset) % table.entryStride < TableEntryBytes))) throw std::runtime_error("bindless heap contains a partial descriptor");
const auto entries = heapSize >= table.entryOffset + TableEntryBytes ? static_cast<std::uint32_t>(std::min<std::uint64_t>((heapSize - table.entryOffset - TableEntryBytes) / table.entryStride + 1u, std::numeric_limits<std::uint32_t>::max())) : 0u;
const auto readWord = [&](std::uint64_t address, std::uint32_t& word) {
if (!runtime.readMemory(runtime.userContext, address, &word)) {
throw std::runtime_error("failed to read a bindless image table from memory");
Expand Down Expand Up @@ -392,7 +392,7 @@ void resolveTableImage(const IrResourcePlan& plan, std::uint32_t imageIndex, con
for (std::size_t i = 0u; i < keys.size(); ++i) {
auto& candidate = candidates[i];
candidate.dwordCount = 8u;
const std::uint64_t address = heapBase + table.entryOffset + static_cast<std::uint64_t>(keys[i]) * TableEntryBytes;
const std::uint64_t address = heapBase + table.entryOffset + static_cast<std::uint64_t>(keys[i]) * table.entryStride;
for (std::uint32_t dword = 0u; dword < 8u; ++dword) readWord(address + dword * sizeof(std::uint32_t), candidate.dwords[dword]);
if (nullImageDescriptor(candidate)) {
++paddedNull;
Expand Down
21 changes: 18 additions & 3 deletions core/shader/recompiler/Optimization/src/ResourceTracker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -460,7 +460,7 @@ class Tracker {
}

// The table entry offset `key * 32` (a shift or a multiply), optionally plus an immediate.
static bool MatchTableOffset(IrValue* value, IrValue*& key, std::uint32_t& entryOffset) {
static bool MatchTableOffset(IrValue* value, IrValue*& key, std::uint32_t& entryOffset, std::uint32_t& entryStride) {
value = value->Resolve();
entryOffset = 0;
if (value->Opcode() == IrOpcode::IAdd32 && value->ArgumentCount() == 2u) {
Expand All @@ -483,6 +483,7 @@ class Tracker {
return false;
}
key = value->Argument(0)->Resolve();
entryStride = 32u;
return true;
}
if (value->Opcode() != IrOpcode::IMul32) {
Expand All @@ -495,7 +496,8 @@ class Tracker {
} else {
return false;
}
return scale == 32u;
entryStride = scale;
return scale >= 32u && scale % sizeof(std::uint32_t) == 0u;
}

// A T# whose eight dwords are scalar reads of one V# (the table) at `entryOffset + key * 32`
Expand Down Expand Up @@ -538,10 +540,22 @@ class Tracker {

IrValue* key = nullptr;
std::uint32_t entryOffset = 0;
if (!MatchTableOffset(heapOffset, key, entryOffset) || key->Type() != IrType::U32) {
std::uint32_t entryStride = 0;
if (!MatchTableOffset(heapOffset, key, entryOffset, entryStride) || key->Type() != IrType::U32) {
return false;
}
entryOffset += immediateOffset;
if (entryOffset + 8u * sizeof(std::uint32_t) > entryStride) {
return false;
}
if (entryStride != 32u) {
DescriptorSource direct;
MakeSource(handle, 8u, false, false, direct);
std::uint32_t badDword = 0;
if (ValidateSource(direct, badDword)) {
return false;
}
}

for (const auto* read : heapReads) {
if (!usedOnlyAsImage(*read, heapReads)) {
Expand All @@ -559,6 +573,7 @@ class Tracker {
table.heapSource = heapSourceIndex;
table.materialSource = heapSourceIndex;
table.entryOffset = entryOffset;
table.entryStride = entryStride;
DescriptorSource materialSource = heapSource;
std::uint32_t materialMemoryIndex = 0;
const MemoryInfo* materialMemory = ScalarReadMemory(*key, materialMemoryIndex);
Expand Down
8 changes: 5 additions & 3 deletions core/shader/recompiler/ShaderDiskCache.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ static_assert(sizeof(IrValue) == 152, "IrValue changed: update the plan value en
static_assert(sizeof(GuestRegister) == 8, "GuestRegister changed: update the plan value encoder");
static_assert(sizeof(MemoryInfo) == 80, "MemoryInfo changed: update the plan encoder");
static_assert(sizeof(DescriptorSource) == 104, "DescriptorSource changed: update the plan encoder");
static_assert(sizeof(DescriptorSource::IndirectImage) == 28, "IndirectImage changed: update the plan encoder");
static_assert(sizeof(DescriptorSource::IndirectImage) == 32, "IndirectImage changed: update the plan encoder");
static_assert(sizeof(ResourceBlock) == 80, "ResourceBlock changed: update the plan encoder");
static_assert(sizeof(SrtRead) == 16, "SrtRead changed: update the plan encoder");
static_assert(sizeof(UniformFill) == 28, "UniformFill changed: update the plan encoder");
Expand Down Expand Up @@ -147,7 +147,7 @@ static_assert(fieldCount<IrResourcePlan>() == 21, "IrResourcePlan changed: updat
static_assert(fieldCount<GuestRegister>() == 2, "GuestRegister changed: update the plan value encoder");
static_assert(fieldCount<MemoryInfo>() == 30, "MemoryInfo changed: update the plan encoder");
static_assert(fieldCount<DescriptorSource>() == 3, "DescriptorSource changed: update the plan encoder");
static_assert(fieldCount<DescriptorSource::IndirectImage>() == 7, "IndirectImage changed: update the plan encoder");
static_assert(fieldCount<DescriptorSource::IndirectImage>() == 8, "IndirectImage changed: update the plan encoder");
static_assert(fieldCount<ResourceBlock>() == 4, "ResourceBlock changed: update the plan encoder");
static_assert(fieldCount<SrtRead>() == 2, "SrtRead changed: update the plan encoder");
static_assert(fieldCount<UniformFill>() == 5, "UniformFill changed: update the plan encoder");
Expand Down Expand Up @@ -972,6 +972,7 @@ bool encodePlan(Writer& writer, const IrResourcePlan& plan) {
out.Value(table.selectorOffset);
out.Value(table.keyArg);
out.Value(table.entryOffset);
out.Value(table.entryStride);
out.Value(table.hasMaterial);
});
writer.List(plan.controlFlow, [&](Writer& out, const ResourceBlock& block) {
Expand Down Expand Up @@ -1103,7 +1104,7 @@ bool decodePlan(Reader& reader, IrResourcePlan& plan) {
}
}
reader.List(plan.memoryInfo, 78, decodeMemoryInfo);
reader.List(plan.descriptorSources, 62, [&](Reader& in, DescriptorSource& source) {
reader.List(plan.descriptorSources, 66, [&](Reader& in, DescriptorSource& source) {
for (auto& dword : source.dwords) dword = valueAt(in.Get<std::uint32_t>());
in.Value(source.dwordCount);
const bool indirect = in.Get<bool>();
Expand All @@ -1114,6 +1115,7 @@ bool decodePlan(Reader& reader, IrResourcePlan& plan) {
in.Value(table.selectorOffset);
in.Value(table.keyArg);
in.Value(table.entryOffset);
in.Value(table.entryStride);
in.Value(table.hasMaterial);
source.indirectImage = indirect ? std::optional(table) : std::nullopt;
});
Expand Down
Loading