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
13 changes: 10 additions & 3 deletions core/libs/prx/libSceAgcDriver/Graphics/src/ShaderValidation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ struct Decoration {
std::optional<std::uint32_t> stride;
std::optional<std::uint32_t> index;
bool block = false;
bool aliased = false;
bool patch = false;
bool perPrimitive = false;
bool perVertex = false;
Expand Down Expand Up @@ -363,6 +364,7 @@ Module Inspect(const CompiledShader& compiled, const State& state, const VkPhysi
decoration.perVertex = true;
}
if (kind == spv::DecorationBlock) decoration.block = true;
if (kind == spv::DecorationAliased) decoration.aliased = true;
break;
}
case spv::OpMemberDecorate:
Expand Down Expand Up @@ -432,6 +434,7 @@ Module Inspect(const CompiledShader& compiled, const State& state, const VkPhysi
for (const auto& [name, operands] : module.modes) Require(operands.empty() && (name == spv::ExecutionModeOriginUpperLeft || name == spv::ExecutionModeEarlyFragmentTests || name == spv::ExecutionModeDepthReplacing || name == spv::ExecutionModeDepthLess || name == spv::ExecutionModeDepthGreater || name == spv::ExecutionModePixelInterlockOrderedEXT || name == spv::ExecutionModeStencilRefReplacingEXT), "unsupported fragment execution mode");
} else Require(module.modes.empty(), "unsupported vertex execution mode");
std::set<std::pair<std::uint32_t, std::uint32_t>> descriptors;
std::map<std::pair<std::uint32_t, std::uint32_t>, bool> bufferViews;
bool push = false;
for (const auto& [id, variable] : module.variables) {
if (variable.storage == spv::StorageClassFunction || variable.storage == spv::StorageClassPrivate) continue;
Expand Down Expand Up @@ -518,12 +521,13 @@ Module Inspect(const CompiledShader& compiled, const State& state, const VkPhysi
} else {
Require(variable.storage == spv::StorageClassStorageBuffer && decoration.set && decoration.binding, "unsupported or unbound shader resource");
const auto key = std::make_pair(*decoration.set, *decoration.binding);
Require(descriptors.insert(key).second, "duplicate SPIR-V resource binding");
const auto binding = std::find_if(shader.bindings.begin(), shader.bindings.end(), [&](const auto& item) { return item.descriptorSet == key.first && item.binding == key.second; });
const bool array = binding != shader.bindings.end() && binding->role == ShaderRecompiler::DescriptorRole::GuestBuffers;
const auto [view, first] = bufferViews.emplace(key, decoration.aliased);
Require(descriptors.insert(key).second || (!first && view->second && decoration.aliased && array), "duplicate SPIR-V resource binding");
Require(binding != shader.bindings.end(), "SPIR-V resource is absent from recompiler binding metadata");
Require(binding->kind == ShaderRecompiler::DescriptorKind::StorageBuffer, "SPIR-V descriptor type disagrees with recompiler binding metadata");
Require(!binding->readOnly, "read-only descriptor metadata is unsupported because the recompiler emits no NonWritable decoration");
const bool array = binding->role == ShaderRecompiler::DescriptorRole::GuestBuffers;
Require(array || (shader.bdaAbiVersion == ShaderRecompiler::BdaAbi::Version && (binding->role == ShaderRecompiler::DescriptorRole::BdaPagetable || binding->role == ShaderRecompiler::DescriptorRole::FaultBuffer)) || binding->role == ShaderRecompiler::DescriptorRole::ShaderData || binding->role == ShaderRecompiler::DescriptorRole::FlattenedSrt || binding->role == ShaderRecompiler::DescriptorRole::Gds, "SPIR-V descriptor role is unsupported");
auto blockId = typeId;
if (array) {
Expand All @@ -537,7 +541,10 @@ Module Inspect(const CompiledShader& compiled, const State& state, const VkPhysi
const auto& block = module.Type(blockId);
Require(block.size() == 3 && (block[0] & 0xffffu) == spv::OpTypeStruct && module.decorations[blockId].block, "descriptor must be a Block struct with exactly one member");
const auto& words = module.Type(block[2]);
Require(words.size() == 3 && (words[0] & 0xffffu) == spv::OpTypeRuntimeArray && module.Signature(words[2]) == "u32", "descriptor block member must be a runtime array of u32");
Require(words.size() == 3 && (words[0] & 0xffffu) == spv::OpTypeRuntimeArray, "descriptor block member must be a runtime array");
const auto& element = module.Type(words[2]);
const bool unsignedInteger = element.size() == 4 && (element[0] & 0xffffu) == spv::OpTypeInt && element[3] == 0u;
Require(unsignedInteger && (element[2] == 32u || (array && decoration.aliased && element[2] == 64u)), "descriptor block member must be a runtime array of u32, or of u64 in an aliased guest buffer view");
const auto member = module.offsets.find({blockId, 0});
Require(member != module.offsets.end() && member->second == 0, "descriptor block member must have offset zero");
}
Expand Down
49 changes: 49 additions & 0 deletions core/libs/prx/libSceAgcDriver/tests/Graphics.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4076,6 +4076,54 @@ void stencilRefExportTests() {
static_cast<void>(AgcDriver::Graphics::DecodeState(queue));
}

std::set<std::string> bufferViews(std::span<const std::uint32_t> words) {
std::set<std::string> views;
for (std::size_t at = 5; at < words.size() && (words[at] >> 16u) != 0; at += words[at] >> 16u) {
if (static_cast<spv::Op>(words[at] & 0xffffu) != spv::OpName) continue;
const std::string name(reinterpret_cast<const char*>(&words[at + 2]));
if (name.starts_with("buffers")) views.insert(name);
}
return views;
}

void aliasedBufferViewTests() {
constexpr std::array<std::uint32_t, 5> capabilities{spv::CapabilityShader, spv::CapabilityInt64, spv::CapabilityPhysicalStorageBufferAddresses, spv::CapabilityStorageBuffer8BitAccess, spv::CapabilityInt64Atomics};
constexpr std::array<std::string_view, 2> extensions{"SPV_KHR_physical_storage_buffer", "SPV_KHR_8bit_storage"};
constexpr std::array<std::uint32_t, 12> userData{0u, 0u, 0u, 0u, 0x10000000u, 0u, 0x1000u, 0x31016facu, 0x20000000u, 0u, 0x1000u, 0x31038facu};
const auto code = [](std::uint32_t store, std::uint32_t resource) {
return std::array<std::uint32_t, 9>{0xe0000000u, 0x80010a00u, 0xbf8c3f70u, store, resource, 0x7e0e02f2u, 0xf800180fu, 0x07070707u, 0xbf810000u};
};
const std::array<std::pair<std::array<std::uint32_t, 9>, std::set<std::string>>, 2> cases{{
{code(0xe0104000u, 0x80010a00u), {"buffers", "buffers_coherent"}},
{code(0xe1480000u, 0x80010a00u), {"buffers", "buffers_u64"}},
}};
AgcDriver::Graphics::State state{};
state.stages.path = AgcDriver::Graphics::ShaderPath::Vertex;
ShaderRecompiler::RecompileResult vertex;
vertex.spirv = makeModule({});
const VkPhysicalDeviceSubgroupProperties subgroup{VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SUBGROUP_PROPERTIES};
for (const auto& [words, views] : cases) {
auto request = orderedPixelRequest(0x20600u, capabilities, words);
request.target.supportedExtensions = extensions;
request.target.bdaAbiVersion = ShaderRecompiler::BdaAbi::Version;
request.context.userDataBaseRegister = 0;
request.context.userData = userData;
auto pixel = ShaderRecompiler::Recompile(request);
const auto declared = bufferViews(pixel.spirv.Words());
std::string names;
for (const auto& name : declared) names += " " + name;
Require(declared == views, "the pixel shader declared the guest buffers as" + names + ", not as the expected " + std::to_string(views.size()) + " aliased variables");
const std::array<AgcDriver::Graphics::CompiledShader, 2> shaders{{{ShaderRecompiler::ShaderStage::Vertex, &vertex, 0}, {ShaderRecompiler::ShaderStage::Fragment, &pixel, 0}}};
AgcDriver::Graphics::ValidateShaders(shaders, state, subgroup, false, false, false, false, false, true);
auto unaliased = pixel.spirv.Words();
for (std::size_t at = 5; at < unaliased.size() && (unaliased[at] >> 16u) != 0; at += unaliased[at] >> 16u) {
if (static_cast<spv::Op>(unaliased[at] & 0xffffu) == spv::OpDecorate && unaliased[at + 2] == spv::DecorationAliased) unaliased[at + 2] = spv::DecorationRestrict;
}
pixel.spirv = std::move(unaliased);
expectFailure([&] { AgcDriver::Graphics::ValidateShaders(shaders, state, subgroup, false, false, false, false, false, true); }, "duplicate SPIR-V resource binding");
}
}

void ProvokingVertexTests() {
using namespace AgcDriver::Graphics;
constexpr std::uint32_t lastVertex = 1u << 19u;
Expand Down Expand Up @@ -5124,6 +5172,7 @@ int main(int argc, char** argv) {
conservativeZExportTests();
orderedPixelShaderTests();
stencilRefExportTests();
aliasedBufferViewTests();
ProvokingVertexTests();
ProvokingVertexRenderPassTests();
ConservativeRasterizationTests();
Expand Down
Loading