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
3 changes: 3 additions & 0 deletions core/libs/prx/libSceAgcDriver/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3170,12 +3170,15 @@ if(BUILD_TESTING)
set_tests_properties(agc_driver_covered_centroid_state PROPERTIES TIMEOUT 30)
add_test(NAME agc_driver_sample_location_grid_fallback COMMAND agc_driver_graphics_tests --sample-location-grid-fallback)
set_tests_properties(agc_driver_sample_location_grid_fallback PROPERTIES TIMEOUT 30)
add_test(NAME agc_driver_unobserved_provoking_vertex COMMAND agc_driver_graphics_tests --unobserved-provoking-vertex)
set_tests_properties(agc_driver_unobserved_provoking_vertex PROPERTIES TIMEOUT 30)
add_test(NAME agc_driver_sample_order_planning COMMAND agc_driver_graphics_tests --sample-order)
set_tests_properties(agc_driver_sample_order_planning PROPERTIES TIMEOUT 30)
if(WIN32)
set_tests_properties(agc_compute_descriptor_layout_flags PROPERTIES ENVIRONMENT_MODIFICATION "PATH=path_list_prepend:$<TARGET_FILE_DIR:libc>")
set_tests_properties(agc_driver_covered_centroid_state PROPERTIES ENVIRONMENT_MODIFICATION "PATH=path_list_prepend:$<TARGET_FILE_DIR:libc>")
set_tests_properties(agc_driver_sample_location_grid_fallback PROPERTIES ENVIRONMENT_MODIFICATION "PATH=path_list_prepend:$<TARGET_FILE_DIR:libc>")
set_tests_properties(agc_driver_unobserved_provoking_vertex PROPERTIES ENVIRONMENT_MODIFICATION "PATH=path_list_prepend:$<TARGET_FILE_DIR:libc>")
set_tests_properties(agc_driver_sample_order_planning PROPERTIES ENVIRONMENT_MODIFICATION "PATH=path_list_prepend:$<TARGET_FILE_DIR:libc>")
endif()
add_test(NAME agc_compute_pipeline_capture COMMAND agc_compute_pipeline_capture_tests)
Expand Down
64 changes: 64 additions & 0 deletions core/libs/prx/libSceAgcDriver/Graphics/src/Pipeline.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,70 @@ void ValidateProvokingVertex(const Context& context, const State& state, std::sp
}
return;
}
struct Decorations {
std::unordered_map<std::uint64_t, std::uint32_t> builtIns;
std::vector<std::uint64_t> perVertexValues;
std::vector<std::uint64_t> perPrimitiveValues;
};
const auto readDecorations = [](const ShaderRecompiler::RecompileResult& program, Decorations& decorations) {
const auto words = program.spirv.Words();
if (words.size() < 5) return false;
constexpr std::uint32_t NoMember = 0xffffffffu;
const auto key = [](std::uint32_t target, std::uint32_t member) { return (static_cast<std::uint64_t>(target) << 32u) | member; };
for (std::size_t cursor = 5; cursor < words.size();) {
const auto count = words[cursor] >> 16u;
if (count == 0 || count > words.size() - cursor) return false;
const auto op = static_cast<spv::Op>(words[cursor] & 0xffffu);
const std::size_t decoration = op == spv::OpDecorate ? 2 : op == spv::OpMemberDecorate ? 3 : 0;
if (decoration != 0 && count > decoration) {
const auto target = key(words[cursor + 1], op == spv::OpMemberDecorate ? words[cursor + 2] : NoMember);
if (words[cursor + decoration] == spv::DecorationFlat || words[cursor + decoration] == spv::DecorationPerVertexKHR) decorations.perVertexValues.push_back(target);
if (words[cursor + decoration] == spv::DecorationPerPrimitiveEXT) decorations.perPrimitiveValues.push_back(target);
if (words[cursor + decoration] == spv::DecorationBuiltIn && count > decoration + 1) decorations.builtIns[target] = words[cursor + decoration + 1];
}
cursor += count;
}
return true;
};
const auto readsProvokingVertex = [&](const ShaderRecompiler::RecompileResult& program) {
if (std::any_of(program.fragmentParameters.begin(), program.fragmentParameters.end(), [](const ShaderRecompiler::FragmentParameter& parameter) {
return parameter.flat || parameter.perVertex || parameter.custom || parameter.flatP10;
})) return true;
Decorations decorations;
if (!readDecorations(program, decorations)) return true;
const auto& builtIns = decorations.builtIns;
return std::any_of(decorations.perVertexValues.begin(), decorations.perVertexValues.end(), [&](std::uint64_t target) {
const auto builtIn = builtIns.find(target);
if (builtIn == builtIns.end()) return true;
switch (builtIn->second) {
case spv::BuiltInFragCoord:
case spv::BuiltInPointCoord:
case spv::BuiltInFrontFacing:
case spv::BuiltInSampleId:
case spv::BuiltInSamplePosition:
case spv::BuiltInSampleMask:
case spv::BuiltInHelperInvocation:
case spv::BuiltInSubgroupSize:
case spv::BuiltInSubgroupLocalInvocationId:
return false;
default:
return true;
}
});
};
const auto writesProvokingVertexBuiltIn = [&](const ShaderRecompiler::RecompileResult& program) {
Decorations decorations;
if (!readDecorations(program, decorations)) return true;
return std::any_of(decorations.builtIns.begin(), decorations.builtIns.end(), [&](const auto& builtIn) {
return (builtIn.second == spv::BuiltInLayer || builtIn.second == spv::BuiltInViewportIndex) &&
std::find(decorations.perPrimitiveValues.begin(), decorations.perPrimitiveValues.end(), builtIn.first) == decorations.perPrimitiveValues.end();
});
};
const bool observed = std::any_of(shaders.begin(), shaders.end(), [&](const CompiledShader& shader) {
if (shader.stage == ShaderRecompiler::ShaderStage::Fragment) return shader.program == nullptr || readsProvokingVertex(*shader.program);
return shader.program != nullptr && writesProvokingVertexBuiltIn(*shader.program);
});
if (!context.provokingVertexLast && !observed) return;
Require(context.provokingVertexLast, "last provoking vertex requires VK_EXT_provoking_vertex with provokingVertexLast enabled");
const bool meshPrimitives = state.stages.path == ShaderPath::Geometry && state.stages.mesh;
Require(!state.stages.tessellation && (meshPrimitives || (state.stages.path == ShaderPath::Vertex && !state.stages.mesh)), "last provoking vertex is unsupported for generated primitive pipelines");
Expand Down
107 changes: 106 additions & 1 deletion core/libs/prx/libSceAgcDriver/tests/Graphics.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9354,7 +9354,7 @@ void ProvokingVertexTests() {
ValidateProvokingVertex(unsupported, first);
auto last = first;
last.provokingVertexMode = VK_PROVOKING_VERTEX_MODE_LAST_VERTEX_EXT;
expectFailure([&] { ValidateProvokingVertex(unsupported, last); }, "last provoking vertex requires VK_EXT_provoking_vertex with provokingVertexLast enabled");
ValidateProvokingVertex(unsupported, last);
auto supported = unsupported;
supported.provokingVertexLast = true;
ValidateProvokingVertex(supported, last);
Expand Down Expand Up @@ -9477,6 +9477,105 @@ void ProvokingVertexTests() {
expectFailure([&] { ValidateProvokingVertex(supported, last); }, "invalid provoking vertex mode");
}

void UnobservedProvokingVertexTests() {
using namespace AgcDriver::Graphics;
auto last = DecodeState(makeState());
last.provokingVertexMode = VK_PROVOKING_VERTEX_MODE_LAST_VERTEX_EXT;
const Context unsupported{};
const auto module = [](std::initializer_list<std::uint32_t> instructions) {
std::vector<std::uint32_t> words{spv::MagicNumber, 0x10300u, 0u, 4u, 0u};
words.insert(words.end(), instructions);
return words;
};
ShaderRecompiler::RecompileResult fragment{};
const std::array<CompiledShader, 1> shaders{{{ShaderRecompiler::ShaderStage::Fragment, &fragment, 0}}};
const ShaderRecompiler::FragmentParameter smooth{0, 0, false, false};
const auto refused = [&](const State& state) {
expectFailure([&] { ValidateProvokingVertex(unsupported, state, shaders); }, "last provoking vertex requires VK_EXT_provoking_vertex with provokingVertexLast enabled");
};
for (const auto topology : {VK_PRIMITIVE_TOPOLOGY_POINT_LIST, VK_PRIMITIVE_TOPOLOGY_LINE_LIST, VK_PRIMITIVE_TOPOLOGY_LINE_STRIP, VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP, VK_PRIMITIVE_TOPOLOGY_TRIANGLE_FAN}) {
auto state = last;
state.topology = topology;
ValidateProvokingVertex(unsupported, state);
fragment.fragmentParameters.clear();
fragment.spirv = module({});
ValidateProvokingVertex(unsupported, state, shaders);
fragment.fragmentParameters = {smooth};
fragment.spirv = module({(4u << 16u) | spv::OpDecorate, 1u, spv::DecorationLocation, 0u});
ValidateProvokingVertex(unsupported, state, shaders);
for (std::uint32_t read = 0; read < 4; ++read) {
const ShaderRecompiler::FragmentParameter observed{1, 1, read == 0, read == 1, read == 2, read == 3, false};
fragment.fragmentParameters = {smooth, observed};
refused(state);
}
fragment.fragmentParameters = {smooth};
for (const std::uint32_t decoration : {static_cast<std::uint32_t>(spv::DecorationFlat), static_cast<std::uint32_t>(spv::DecorationPerVertexKHR)}) {
fragment.spirv = module({(3u << 16u) | spv::OpDecorate, 1u, decoration});
refused(state);
fragment.spirv = module({(4u << 16u) | spv::OpMemberDecorate, 1u, 0u, decoration});
refused(state);
}
fragment.spirv = module({(5u << 16u) | spv::OpDecorate, 1u, spv::DecorationLocation});
refused(state);
fragment.spirv = std::vector<std::uint32_t>{};
refused(state);
}
fragment.fragmentParameters = {smooth};
for (const std::uint32_t generated : {static_cast<std::uint32_t>(spv::BuiltInSampleId), static_cast<std::uint32_t>(spv::BuiltInFrontFacing), static_cast<std::uint32_t>(spv::BuiltInSubgroupLocalInvocationId)}) {
fragment.spirv = module({(4u << 16u) | spv::OpDecorate, 1u, spv::DecorationBuiltIn, generated, (3u << 16u) | spv::OpDecorate, 1u, spv::DecorationFlat});
ValidateProvokingVertex(unsupported, last, shaders);
fragment.spirv = module({(3u << 16u) | spv::OpDecorate, 1u, spv::DecorationFlat, (4u << 16u) | spv::OpDecorate, 1u, spv::DecorationBuiltIn, generated});
ValidateProvokingVertex(unsupported, last, shaders);
fragment.spirv = module({(5u << 16u) | spv::OpMemberDecorate, 1u, 0u, spv::DecorationBuiltIn, generated, (4u << 16u) | spv::OpMemberDecorate, 1u, 0u, spv::DecorationFlat});
ValidateProvokingVertex(unsupported, last, shaders);
fragment.spirv = module({(4u << 16u) | spv::OpDecorate, 1u, spv::DecorationBuiltIn, generated, (3u << 16u) | spv::OpDecorate, 2u, spv::DecorationFlat});
refused(last);
fragment.spirv = module({(5u << 16u) | spv::OpMemberDecorate, 1u, 0u, spv::DecorationBuiltIn, generated, (4u << 16u) | spv::OpMemberDecorate, 1u, 1u, spv::DecorationFlat});
refused(last);
}
for (const std::uint32_t passed : {static_cast<std::uint32_t>(spv::BuiltInLayer), static_cast<std::uint32_t>(spv::BuiltInViewportIndex), static_cast<std::uint32_t>(spv::BuiltInPrimitiveId)}) {
fragment.spirv = module({(4u << 16u) | spv::OpDecorate, 1u, spv::DecorationBuiltIn, passed, (3u << 16u) | spv::OpDecorate, 1u, spv::DecorationFlat});
refused(last);
fragment.spirv = module({(5u << 16u) | spv::OpMemberDecorate, 1u, 0u, spv::DecorationBuiltIn, passed, (4u << 16u) | spv::OpMemberDecorate, 1u, 0u, spv::DecorationFlat});
refused(last);
}
ShaderRecompiler::RecompileResult vertex{};
vertex.fragmentParameters = {{0, 0, true, true, true, true, true}};
vertex.spirv = module({(3u << 16u) | spv::OpDecorate, 1u, spv::DecorationFlat});
fragment.fragmentParameters = {smooth};
fragment.spirv = module({});
const std::array<CompiledShader, 2> stages{{{ShaderRecompiler::ShaderStage::Vertex, &vertex, 0}, {ShaderRecompiler::ShaderStage::Fragment, &fragment, 0}}};
ValidateProvokingVertex(unsupported, last, stages);
Context supported{};
supported.provokingVertexLast = true;
for (const auto stage : {ShaderRecompiler::ShaderStage::Vertex, ShaderRecompiler::ShaderStage::TessellationEvaluation, ShaderRecompiler::ShaderStage::Geometry, ShaderRecompiler::ShaderStage::Mesh}) {
ShaderRecompiler::RecompileResult writer{};
const std::array<CompiledShader, 2> written{{{stage, &writer, 0}, {ShaderRecompiler::ShaderStage::Fragment, &fragment, 0}}};
const auto refusedWrite = [&] {
expectFailure([&] { ValidateProvokingVertex(unsupported, last, written); }, "last provoking vertex requires VK_EXT_provoking_vertex with provokingVertexLast enabled");
ValidateProvokingVertex(supported, last, written);
};
for (const std::uint32_t selected : {static_cast<std::uint32_t>(spv::BuiltInLayer), static_cast<std::uint32_t>(spv::BuiltInViewportIndex)}) {
writer.spirv = module({(4u << 16u) | spv::OpDecorate, 1u, spv::DecorationBuiltIn, selected});
refusedWrite();
writer.spirv = module({(5u << 16u) | spv::OpMemberDecorate, 1u, 0u, spv::DecorationBuiltIn, selected});
refusedWrite();
writer.spirv = module({(5u << 16u) | spv::OpMemberDecorate, 1u, 0u, spv::DecorationBuiltIn, selected, (4u << 16u) | spv::OpMemberDecorate, 1u, 1u, spv::DecorationPerPrimitiveEXT});
refusedWrite();
writer.spirv = module({(4u << 16u) | spv::OpMemberDecorate, 1u, 0u, spv::DecorationPerPrimitiveEXT, (5u << 16u) | spv::OpMemberDecorate, 1u, 0u, spv::DecorationBuiltIn, selected});
ValidateProvokingVertex(unsupported, last, written);
writer.spirv = module({(4u << 16u) | spv::OpDecorate, 1u, spv::DecorationBuiltIn, selected, (3u << 16u) | spv::OpDecorate, 1u, spv::DecorationPerPrimitiveEXT});
ValidateProvokingVertex(unsupported, last, written);
}
writer.spirv = module({(4u << 16u) | spv::OpDecorate, 1u, spv::DecorationBuiltIn, spv::BuiltInPosition, (5u << 16u) | spv::OpMemberDecorate, 2u, 0u, spv::DecorationBuiltIn, spv::BuiltInPointSize});
ValidateProvokingVertex(unsupported, last, written);
writer.spirv = std::vector<std::uint32_t>{};
refusedWrite();
}
const std::array<CompiledShader, 1> missing{{{ShaderRecompiler::ShaderStage::Fragment, nullptr, 0}}};
expectFailure([&] { ValidateProvokingVertex(unsupported, last, missing); }, "last provoking vertex requires VK_EXT_provoking_vertex with provokingVertexLast enabled");
}

void ProvokingVertexRenderPassTests() {
using namespace AgcDriver::Graphics;
const auto first = DecodeState(makeState());
Expand Down Expand Up @@ -11397,6 +11496,12 @@ int main(int argc, char** argv) {
std::cerr << "descriptor pool reset failure did not terminate\n";
return 1;
}
if (argc == 2 && std::string_view(argv[1]) == "--unobserved-provoking-vertex") {
UnobservedProvokingVertexTests();
ProvokingVertexTests();
std::cout << "Unobserved provoking vertex checks passed\n";
return 0;
}
if (argc == 2 && std::string_view(argv[1]) == "--covered-centroid-state") {
multisampleTests();
std::cout << "Covered-centroid and multisample state checks passed\n";
Expand Down
Loading
Loading