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
Original file line number Diff line number Diff line change
Expand Up @@ -2435,7 +2435,7 @@ void ShaderResources::buildComplete() {
for (auto element = first; element < last; ++element) {
const auto index = binding.imageAllocations[element];
if (binding.layout.descriptorType == VK_DESCRIPTOR_TYPE_SAMPLER) images.push_back({samplers[index]->Handle(), VK_NULL_HANDLE, VK_IMAGE_LAYOUT_UNDEFINED});
else if (binding.layout.descriptorType == VK_DESCRIPTOR_TYPE_STORAGE_IMAGE) images.push_back({VK_NULL_HANDLE, storageTextures[index]->StorageView(storageMips[index], storageFirstLayer[index]), VK_IMAGE_LAYOUT_GENERAL});
else if (binding.layout.descriptorType == VK_DESCRIPTOR_TYPE_STORAGE_IMAGE) images.push_back({VK_NULL_HANDLE, storageAtomic[index] ? storageTextures[index]->AtomicView(storageMips[index], storageFirstLayer[index]) : storageTextures[index]->StorageView(storageMips[index], storageFirstLayer[index]), VK_IMAGE_LAYOUT_GENERAL});
else images.push_back({VK_NULL_HANDLE, textureFirstLayer[index] ? textures[index]->FirstLayerView() : textures[index]->View(), textures[index]->Layout()});
}
if (recorded) runs.emplace_back(write.dstArrayElement, write.descriptorCount);
Expand Down Expand Up @@ -5211,7 +5211,7 @@ void ShaderResources::resolveImageTables() {
storageKeys.push_back(resource.dccAddress);
storageFirstLayer.push_back(firstLayer);
storageWritten.push_back(true);
storageAtomic.push_back(false);
storageAtomic.push_back(element < binding.imageAtomic.size() && binding.imageAtomic[element]);
storageAtomic64.push_back(false);
describedRanges.push_back({"storage", resource.baseAddress, guestBytes, resource.width, resource.height, resource.format, static_cast<int>(resource.tileMode), resource.dccAddress, true});
bindings[storageBinding].imageAllocations[absolute] = storageTextures.size() - 1u;
Expand Down
21 changes: 16 additions & 5 deletions core/libs/prx/libSceAgcDriver/tests/ShaderMemory.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1339,6 +1339,7 @@ constexpr std::uint32_t TableFormat8888UNorm = 56;
constexpr std::uint32_t TableFormat32UInt = 20;
constexpr std::uint32_t TableFormat16SInt = 12;
constexpr std::uint32_t TableFormat11_11_10UInt = 34;
constexpr std::uint32_t TableFormat32_32UInt = 62;
constexpr std::uint32_t TableType2D = 9;
constexpr std::uint32_t TableType3D = 10;

Expand Down Expand Up @@ -2964,7 +2965,17 @@ void verifyImageTableRegistration() {
const auto* storeTable = TableBinding(*store.result, DescriptorRole::ImageStorageTable);
require((storeFault & Abi::PoisonFlag) == 0u && storeTable != nullptr && storeTable->count != 0u && std::all_of(storeTable->imageWritten.begin(), storeTable->imageWritten.end(), [](bool written) { return written; }), "image table registration: the store's table was not bound as written storage elements");
require(TableBinding(*store.result, DescriptorRole::ImageTable) == nullptr || TableBinding(*store.result, DescriptorRole::ImageTable)->count == 0u, "image table registration: a store-only table bound sampled elements");
const std::array<std::pair<std::uint32_t, const char*>, 3> unsupportedStores{{{0xf0240f08u, "image_store_mip"}, {0xf0280f08u, "image_store_pck"}, {0xf0440108u, "image_atomic_add"}}};
auto atomicCode = storeCode;
atomicCode[8] = 0xf0440108u;
const auto atomic = CompileTable(TableRequest(atomicCode, userData));
const auto& atomicPlan = *atomic.capture->plan;
require(atomicPlan.info.images.size() == 1u && atomicPlan.info.images[0].table == 0u && atomicPlan.info.images[0].tableOperation == TableOperation::Supported, "image table registration: a table T# feeding image_atomic_add was not a supported table operation");
const auto& atomicMap = TableMap(*atomic.result);
const auto atomicFault = atomicMap[Abi::TableHeader(0) + Abi::TableFault];
const auto* atomicTable = TableBinding(*atomic.result, DescriptorRole::ImageStorageTable);
require((atomicFault & Abi::PoisonFlag) == 0u && atomicTable != nullptr && atomicTable->count != 0u && atomicTable->imageAtomic.size() == atomicTable->count && std::all_of(atomicTable->imageWritten.begin(), atomicTable->imageWritten.end(), [](bool written) { return written; }) && std::all_of(atomicTable->imageAtomic.begin(), atomicTable->imageAtomic.end(), [](bool atomic) { return atomic; }), "image table registration: the image_atomic_add table was not bound as atomic storage elements");
require(TableBinding(*atomic.result, DescriptorRole::ImageTable) == nullptr || TableBinding(*atomic.result, DescriptorRole::ImageTable)->count == 0u, "image table registration: an atomic-only table bound sampled elements");
const std::array<std::pair<std::uint32_t, const char*>, 2> unsupportedStores{{{0xf0240f08u, "image_store_mip"}, {0xf0280f08u, "image_store_pck"}}};
for (const auto& [instruction, name] : unsupportedStores) {
auto code = storeCode;
code[8] = instruction;
Expand Down Expand Up @@ -3370,19 +3381,19 @@ void verifySelectedRecordDirectOperations() {
FillTableSrt(guest, heap.data(), 32u, 4u);
guest.srt[16] = 2u;
const auto userData = TableUserData(guest);
const std::array<std::tuple<const char*, std::uint32_t, std::uint32_t>, 4> operations{{{"image_store", 0xf0200f08u, TableFormat32UInt}, {"image_atomic_add", 0xf0440108u, TableFormat32UInt}, {"image_load_pck", 0xf0080f08u, TableFormat32UInt}, {"image_load_by2", 0xf1080308u, TableFormat16UNorm}}};
const std::array<std::tuple<const char*, std::uint32_t, std::uint32_t>, 5> operations{{{"image_store", 0xf0200f08u, TableFormat32UInt}, {"image_atomic_add", 0xf0440108u, TableFormat32UInt}, {"image_atomic64_add", 0xf0440308u, TableFormat32_32UInt}, {"image_load_pck", 0xf0080f08u, TableFormat32UInt}, {"image_load_by2", 0xf1080308u, TableFormat16UNorm}}};
for (const auto& [name, word0, format] : operations) {
const auto what = std::string("selected record direct operations: ") + name;
heap[2] = TableT(textures[1].bytes.data(), format, TableType2D);
const auto code = WithImageOperation(TableSrtKeyCode, word0, false);
const auto request = TableRequest(code, userData);
const auto plan = GetResourcePlan(request);
require(plan->info.images.size() == 1u && plan->info.images[0].table == NoTable && !plan->descriptorSources.at(plan->info.images[0].source).tableColumn.has_value(), (what + " through a T# whose record the CPU evaluates was planned through an image table").c_str());
const auto compiled = CompileTable(request);
require(TableBinding(*compiled.result, DescriptorRole::ImageTable) == nullptr && compiled.result->imageTableFaults == 0u && TableCovered(compiled.regions, &heap[2], sizeof(heap[2])), (what + " through a T# whose record the CPU evaluates was not bound from its record").c_str());
const auto compiled = word0 == 0xf0440308u ? TableCompiled{} : CompileTable(request);
require(word0 == 0xf0440308u || (TableBinding(*compiled.result, DescriptorRole::ImageTable) == nullptr && compiled.result->imageTableFaults == 0u && TableCovered(compiled.regions, &heap[2], sizeof(heap[2]))), (what + " through a T# whose record the CPU evaluates was not bound from its record").c_str());
const auto wholeCode = WithImageOperation(TableWholeCode, word0, false);
const auto whole = GetResourcePlan(TableRequest(wholeCode, userData));
require(whole->info.images.size() == 1u && whole->info.images[0].table == 0u && whole->info.images[0].tableOperation == (word0 == 0xf0200f08u ? TableOperation::Supported : TableOperation::Unsupported), (what + " through a T# keyed per lane left the image table or has the wrong table operation").c_str());
require(whole->info.images.size() == 1u && whole->info.images[0].table == 0u && whole->info.images[0].tableOperation == (word0 == 0xf0200f08u || word0 == 0xf0440108u ? TableOperation::Supported : TableOperation::Unsupported), (what + " through a T# keyed per lane left the image table or has the wrong table operation").c_str());
}
const auto mixedCode = WithImageOperation(TableSrtKeyCode, 0xf0200f08u, true);
const auto mixed = GetResourcePlan(TableRequest(mixedCode, userData));
Expand Down
52 changes: 52 additions & 0 deletions core/libs/prx/libSceAgcDriver/tests/execution/ImageTable.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,22 @@ alignas(256) constexpr std::array<std::uint32_t, 16> StoreLaneTableCode{
0xf42c0802u, 0x24000000u, 0x360200bfu, 0x7e040280u, 0x7e080301u, 0xf0208f08u, 0x00090401u, 0xbf810000u,
};

alignas(256) constexpr std::array<std::uint32_t, 16> AtomicAddLaneTableCode{
0xf4080100u, 0xfa000000u, 0xf4080200u, 0xfa000010u, 0x8f108202u, 0xf4200444u, 0x20000000u, 0x9312b011u,
0xf42c0802u, 0x24000000u, 0x360200bfu, 0x7e040280u, 0x7e080301u, 0xf0448108u, 0x00090401u, 0xbf810000u,
};

alignas(256) constexpr std::array<std::uint32_t, 16> AtomicSwapLaneTableCode{
0xf4080100u, 0xfa000000u, 0xf4080200u, 0xfa000010u, 0x8f108202u, 0xf4200444u, 0x20000000u, 0x9312b011u,
0xf42c0802u, 0x24000000u, 0x360200bfu, 0x7e040280u, 0x7e080301u, 0xf03c8108u, 0x00090401u, 0xbf810000u,
};

alignas(256) constexpr std::array<std::uint32_t, 18> AtomicCmpSwapLaneTableCode{
0xf4080100u, 0xfa000000u, 0xf4080200u, 0xfa000010u, 0x8f108202u, 0xf4200444u, 0x20000000u, 0x9312b011u,
0xf42c0802u, 0x24000000u, 0x360200bfu, 0x7e040280u, 0x7e080301u, 0x7e0a0304u, 0x3a080881u, 0xf0408308u,
0x00090401u, 0xbf810000u,
};

alignas(256) constexpr std::array<std::uint32_t, 17> StoreTableCutCode{
0xf4080100u, 0xfa000000u, 0xf4080200u, 0xfa000010u, 0x8f108202u, 0xf4200444u, 0x20000000u, 0x9312b011u,
0xf42c0802u, 0x24000000u, 0x360200bfu, 0x7e040280u, 0x7da400ffu, 0x00001000u, 0xf0208f08u, 0x00090401u,
Expand Down Expand Up @@ -1117,6 +1133,41 @@ void RunStoreTests(AgcDriver::VulkanDevice& device) {
Require(untouched(), "image table store: a store through a null entry reached an image:\n" + null.log);
}

void RunAtomicTests(AgcDriver::VulkanDevice& device) {
struct Registration {
void* data;
Registration(void* data, std::size_t bytes) : data(data) { GuestAllocations::Mutation().Add(data, bytes, true, true, true); }
~Registration() { GuestAllocations::Mutation().Remove(data); }
} registeredUint(UintTexels.data(), sizeof(UintTexels)), registeredFloat(FloatTexels.data(), sizeof(FloatTexels)), registeredSint(SintTexels.data(), sizeof(SintTexels));
SetClassRecords();
for (std::uint32_t x = 0; x < Width; ++x) UintTexels[x] = 0x5000u + x;
for (std::uint32_t x = 0; x < Width; ++x) FloatTexels[x] = 1.0f;
Keys = {};
Keys[0] = 1;
const auto added = Run(device, AtomicAddLaneTableCode, 1, Lanes, 64);
AgcDriver::Graphics::StorageTexture::FlushAllPending("image table atomic test");
Require(!Faulted(added), "image table atomic: an add through a table faulted:\n" + added.log);
Require(added.imageTableCount != 0u, "image table atomic: the add bound no storage table elements");
for (std::uint32_t x = 0; x < Width; ++x) Require(UintTexels[x] == 0x5000u + x + x, "image table atomic: texel " + std::to_string(x) + " of the selected image is " + Hex(UintTexels[x]) + ", expected " + Hex(0x5000u + x + x));
const auto swapped = Run(device, AtomicSwapLaneTableCode, 1, Lanes, 64);
AgcDriver::Graphics::StorageTexture::FlushAllPending("image table atomic test");
Require(!Faulted(swapped), "image table atomic: an exchange through a table faulted:\n" + swapped.log);
for (std::uint32_t x = 0; x < Width; ++x) Require(UintTexels[x] == x, "image table atomic: texel " + std::to_string(x) + " of the selected image is " + Hex(UintTexels[x]) + ", expected " + Hex(x));
const auto compared = Run(device, AtomicCmpSwapLaneTableCode, 1, Lanes, 64);
AgcDriver::Graphics::StorageTexture::FlushAllPending("image table atomic test");
Require(!Faulted(compared), "image table atomic: a compare exchange through a table faulted:\n" + compared.log);
for (std::uint32_t x = 0; x < Width; ++x) Require(UintTexels[x] == (x ^ 1u), "image table atomic: texel " + std::to_string(x) + " of the selected image is " + Hex(UintTexels[x]) + ", expected " + Hex(x ^ 1u));
Keys[0] = 8;
const auto addedFloat = Run(device, AtomicAddLaneTableCode, 1, Lanes, 64);
AgcDriver::Graphics::StorageTexture::FlushAllPending("image table atomic test");
Require(!Faulted(addedFloat), "image table atomic: an add on a float record through a table faulted:\n" + addedFloat.log);
for (std::uint32_t x = 0; x < Width; ++x) Require(std::bit_cast<std::uint32_t>(FloatTexels[x]) == 0x3f800000u + x, "image table atomic: float texel " + std::to_string(x) + " is " + Hex(std::bit_cast<std::uint32_t>(FloatTexels[x])) + ", expected " + Hex(0x3f800000u + x));
Keys[0] = 6;
const auto null = Run(device, AtomicAddLaneTableCode, 1, Lanes, 64);
AgcDriver::Graphics::StorageTexture::FlushAllPending("image table atomic test");
for (std::uint32_t x = 0; x < Width; ++x) Require(UintTexels[x] == (x ^ 1u) && std::bit_cast<std::uint32_t>(FloatTexels[x]) == 0x3f800000u + x, "image table atomic: an add through a null entry reached an image:\n" + null.log);
}

void RunSelectedStoreTests(AgcDriver::VulkanDevice& device) {
alignas(256) static std::array<std::uint32_t, Width * Height> storeTexels{};
struct Registration {
Expand Down Expand Up @@ -1509,6 +1560,7 @@ int main() {
RunSelectedRecordTests(*device);
RunModeTests(*device);
RunStoreTests(*device);
RunAtomicTests(*device);
RunSelectedStoreTests(*device);
RunPointerTests(*device);
RunPointerRecordTests(*device);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ struct ImageTableElement {
bool depthCompare = false;
DescriptorBindingKind kind = DescriptorBindingKind::Buffers;
std::uint32_t samplers = 0;
bool atomic = false;
};

struct ImageTableSamplerElement {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -613,7 +613,7 @@ void DescriptorBindingBuilder::Populate(BindingAllocationResult& allocation, con
physical.imageShapes.push_back(ImageShapeForResource(shape));
physical.imageDepthCompare.push_back(element.depthCompare);
physical.imageWritten.push_back(ImageBindingResourceClass(element.kind) == ImageResourceClass::Storage);
physical.imageAtomic.push_back(false);
physical.imageAtomic.push_back(element.atomic);
std::uint32_t samplers = 0;
for (std::uint32_t sampler = 0; sampler < entry.tableSamplerMasks.size(); ++sampler) {
if ((element.samplers & (1u << sampler)) != 0u) samplers |= entry.tableSamplerMasks[sampler];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1713,17 +1713,17 @@ ResolvedImageTables ResourceMaterializer::ResolveImageTables(const ShaderInfo& i
auto element = words.dwords;
if (mode.conversionFormat != IrBufferFormat::Invalid || mode.depthBits) element[3] = (element[3] & ~0xfffu) | ShaderImageIdentitySwizzle;
const auto kind = DescriptorBindingForImage(mode);
const auto hash = hashWords(element, (static_cast<std::uint64_t>(mode.dimension) << 16u) ^ (static_cast<std::uint64_t>(mode.depthCompare) << 8u) ^ static_cast<std::uint64_t>(kind));
const auto hash = hashWords(element, (static_cast<std::uint64_t>(image.atomic) << 24u) ^ (static_cast<std::uint64_t>(mode.dimension) << 16u) ^ (static_cast<std::uint64_t>(mode.depthCompare) << 8u) ^ static_cast<std::uint64_t>(kind));
auto index = elementIndex.Find(hash, [&](std::uint32_t known) {
const auto& entry = out.elements[known];
return entry.words == element && entry.dimension == mode.dimension && entry.depthCompare == mode.depthCompare && entry.kind == kind;
return entry.words == element && entry.dimension == mode.dimension && entry.depthCompare == mode.depthCompare && entry.kind == kind && entry.atomic == image.atomic;
});
if (!index.has_value() && out.elements.size() >= Abi::ImageElementBudget) {
record[Abi::ImageRecordFlags] = poison(resource, PoisonReason::Budget, &words);
} else {
if (!index.has_value()) {
index = static_cast<std::uint32_t>(out.elements.size());
out.elements.push_back({element, mode.dimension, mode.depthCompare, kind, 0u});
out.elements.push_back({element, mode.dimension, mode.depthCompare, kind, 0u, image.atomic});
elementIndex.Insert(hash, *index);
}
out.elements[*index].samplers |= directSamplers[resource];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1707,7 +1707,7 @@ class Tracker {
continue;
}
image.table = next++;
if (image.atomic || image.packed || image.byElements != 0u) {
if (image.atomic64 || image.packed || image.byElements != 0u) {
image.tableOperation = TableOperation::Unsupported;
}
}
Expand Down
4 changes: 2 additions & 2 deletions core/shader/recompiler/SpirvBackend/src/SpirvImageEmitter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2041,7 +2041,7 @@ void EmitImage(SpirvValueEmitContext& ctx, const IrValue& inst) {
state.tableAccess = {};
if (tables) {
EmitLabel(state, invalid);
if (returnsValue) incoming.insert(incoming.end(), {ConstantU32CompositeZero(state, 4u), invalid});
if (returnsValue) incoming.insert(incoming.end(), {ZeroResult(ctx, inst), invalid});
state.module.AddFunction(spv::OpBranch, merge);
} else if (exits) {
EmitLabel(state, invalid);
Expand All @@ -2058,7 +2058,7 @@ void EmitImage(SpirvValueEmitContext& ctx, const IrValue& inst) {
EmitLabel(state, guardSkip);
state.module.AddFunction(spv::OpBranch, guardMerge);
EmitLabel(state, guardMerge);
if (returnsValue) state.module.AddFunction(spv::OpPhi, TypeId(state, inst.Type()), guardedId, resultId, merge, ConstantU32CompositeZero(state, 4u), guardSkip);
if (returnsValue) state.module.AddFunction(spv::OpPhi, TypeId(state, inst.Type()), guardedId, resultId, merge, ZeroResult(ctx, inst), guardSkip);
}
if (returnsValue) ctx.definitions.emplace(&inst, guardedId);
}
Expand Down
Loading
Loading