Skip to content
Merged
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
30 changes: 30 additions & 0 deletions core/libs/prx/libSceAgcDriver/tests/ShaderDiskCache.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
#include <memory>
#include <optional>
#include <random>
#include <set>
#include <span>
#include <stdexcept>
#include <string>
Expand Down Expand Up @@ -358,6 +359,9 @@ void verifyEntryRoundTrip() {
auto previousVersion = file;
previousVersion[4] = static_cast<std::byte>(ShaderDiskCache::FormatVersion - 1u);
require(ShaderDiskCache::DecodeEntry(previousVersion, key, decoded) == ShaderDiskCache::LoadStatus::Rejected, "an entry with the previous format version loads");
auto upstreamVersion = file;
upstreamVersion[4] = std::byte{30};
require(ShaderDiskCache::DecodeEntry(upstreamVersion, key, decoded) == ShaderDiskCache::LoadStatus::Rejected, "an entry with the upstream resource plan format loads");
auto incompatible = variant;
incompatible.artifact.runtimeAbiVersion = RuntimeAbi::Version + 1u;
const auto incompatibleFile = ShaderDiskCache::EncodeEntry(key, incompatible);
Expand Down Expand Up @@ -1554,6 +1558,9 @@ void verifyPlanEntryDamage(const std::vector<std::byte>& key, const IrResourcePl
auto previousFormat = file;
previousFormat[4] = static_cast<std::byte>(ShaderDiskCache::FormatVersion - 1u);
require(ShaderDiskCache::DecodePlanEntry(previousFormat, key, decoded) == ShaderDiskCache::LoadStatus::Rejected, what + ": a plan entry of the previous format loads");
auto upstreamFormat = file;
upstreamFormat[4] = std::byte{30};
require(ShaderDiskCache::DecodePlanEntry(upstreamFormat, key, decoded) == ShaderDiskCache::LoadStatus::Rejected, what + ": a plan entry with the upstream resource plan format loads");
auto otherSource = file;
otherSource[8] ^= std::byte{1};
require(ShaderDiskCache::DecodePlanEntry(otherSource, key, decoded) == ShaderDiskCache::LoadStatus::Rejected, what + ": a plan entry of another source version loads");
Expand Down Expand Up @@ -1621,7 +1628,23 @@ void verifyMalformedPlans() {
require(!ShaderDiskCache::EncodePlan(emptied, bytes), "a plan with an empty value slot was encoded");
}

void verifyIndirectImagePlanModes() {
std::set<std::vector<std::byte>> tableEncodings;
for (std::uint32_t flags = 0u; flags < 8u; ++flags) {
auto plan = handBuiltPlan();
auto& table = *plan.descriptorSources[1].indirectImage;
table.laneKey = (flags & 1u) != 0u;
table.indexed = (flags & 2u) != 0u;
table.implicitDerivatives = (flags & 4u) != 0u;
const auto bytes = encodedPlan(plan);
require(tableEncodings.insert(bytes).second, "indirect image table modes share a disk plan encoding");
IrResourcePlan decoded;
require(ShaderDiskCache::DecodePlan(bytes, decoded) && samePlan(plan, decoded), "indirect image table modes changed in a disk plan round trip");
}
}

void verifyPlanRoundTrip() {
verifyIndirectImagePlanModes();
const auto handBuilt = handBuiltPlan();
const auto handBytes = encodedPlan(handBuilt);
IrResourcePlan handDecoded;
Expand Down Expand Up @@ -1881,6 +1904,13 @@ void verifyDefaultDirectory(const char* self) {

int main(int argc, char** argv) {
try {
if (argc == 2 && std::string_view(argv[1]) == "--resource-plan-codec") {
verifyResultRoundTrip();
verifyEntryRoundTrip();
verifyIndirectImagePlanModes();
std::cout << "resource plan codec tests passed\n";
return 0;
}
if (argc == 2 && std::string_view(argv[1]) == "--load") return runLoadingProcess();
if (argc == 2 && std::string_view(argv[1]) == "--no-failure-memo") return runWithoutFailureMemo();
if (argc == 2 && std::string_view(argv[1]).starts_with("--load-plan")) return runPlanLoadingProcess(argv[1]);
Expand Down
310 changes: 310 additions & 0 deletions core/libs/prx/libSceAgcDriver/tests/execution/WaterfallImages.cpp

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,9 @@ struct BlockInfo {
};

struct DescriptorSource {
// A T# loaded from a table buffer (`heapSource`) at `entryOffset + key * 32`, the key a
// wave-uniform runtime value (handle argument `keyArg`). With `hasMaterial` the key is itself
// A T# loaded from a table buffer (`heapSource`) at `entryOffset + key * 32` (the table V#'s
// stride when `indexed`), the key a wave-uniform runtime value (handle argument `keyArg`), or
// each lane's own value when `laneKey`. With `hasMaterial` the key is itself
// `M[readfirstlane(i) * selectorStride + selectorOffset]` over `materialSource`, so the key
// set can be enumerated from the material records (see ResourceMaterializer).
struct IndirectImage {
Expand All @@ -32,6 +33,9 @@ struct DescriptorSource {
std::uint32_t keyArg = 0;
std::uint32_t entryOffset = 0;
bool hasMaterial = false;
bool laneKey = false;
bool indexed = false;
bool implicitDerivatives = false;

bool operator==(const IndirectImage& other) const = default;
};
Expand Down
28 changes: 24 additions & 4 deletions core/shader/recompiler/Optimization/src/ResourceMaterializer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -333,8 +333,19 @@ 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;
std::uint64_t entryBytes = TableEntryBytes;
std::uint32_t entries = 0u;
if (table.indexed) {
if (heap.NumRecords() != 0u) {
if (heap.SwizzleEnabled() || heap.AddTid() || heap.OutOfBounds() > 1u) throw std::runtime_error("bindless image table indexed through a V# with swizzling, ADD_TID or an unbounded range check is not implemented");
entryBytes = heap.Stride();
if (entryBytes < static_cast<std::uint64_t>(table.entryOffset) + TableEntryBytes) throw std::runtime_error("bindless heap record is smaller than its descriptor");
entries = heap.NumRecords();
}
} else {
if (heapSize != 0u && (heapSize < table.entryOffset || (heapSize - table.entryOffset) % TableEntryBytes != 0u)) throw std::runtime_error("bindless heap contains a partial descriptor");
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;
}
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 @@ -388,11 +399,12 @@ void resolveTableImage(const IrResourcePlan& plan, std::uint32_t imageIndex, con
const std::uint64_t heapBase = heap.Base48();
std::vector<DescriptorValue> candidates(keys.size());
std::vector<std::uint8_t> valid(keys.size(), 0u);
std::vector<std::uint32_t> modes(keys.size(), 0u);
std::uint32_t paddedNull = 0u;
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]) * entryBytes;
for (std::uint32_t dword = 0u; dword < 8u; ++dword) readWord(address + dword * sizeof(std::uint32_t), candidate.dwords[dword]);
if (nullImageDescriptor(candidate)) {
++paddedNull;
Expand All @@ -401,9 +413,17 @@ void resolveTableImage(const IrResourcePlan& plan, std::uint32_t imageIndex, con
if (!validImageDescriptor(candidate, image.r128)) throw std::runtime_error("bindless image table contains an invalid descriptor");
const auto decoded = decodeImageDescriptor(candidate, image, plan.srgbDecodeFormats);
if (decoded.fmask) throw std::runtime_error("bindless FMASK images are unsupported");
static_cast<void>(ResourceMaterializer::RuntimeImageMode(image, candidate, plan.info.runtimeImageModes.at(imageIndex)));
modes[i] = ResourceMaterializer::RuntimeImageMode(image, candidate, plan.info.runtimeImageModes.at(imageIndex));
valid[i] = 1u;
}
if (table.laneKey && table.implicitDerivatives) {
std::optional<std::uint32_t> boundMode;
for (std::size_t i = 0u; i < keys.size(); ++i) {
if (valid[i] == 0u) continue;
if (boundMode.has_value() && modes[i] != *boundMode) throw std::runtime_error("bindless image table keyed per lane holds images of different runtime modes under an implicit-derivative access, which is not implemented");
boundMode = modes[i];
}
}
resolution.mapping.clear();
for (std::size_t i = 0u; i < keys.size(); ++i) {
if (valid[i] != 0u) resolution.mapping.emplace_back(keys[i], static_cast<std::uint32_t>(i));
Expand Down
Loading
Loading