diff --git a/core/libs/prx/libSceAgcDriver/CMakeLists.txt b/core/libs/prx/libSceAgcDriver/CMakeLists.txt index 86d62006f..4b02d99f1 100644 --- a/core/libs/prx/libSceAgcDriver/CMakeLists.txt +++ b/core/libs/prx/libSceAgcDriver/CMakeLists.txt @@ -633,6 +633,19 @@ target_link_libraries(agc_formatted_buffer_once_tests PRIVATE shader_recompiler_ set_target_properties(agc_formatted_buffer_once_tests PROPERTIES CXX_EXTENSIONS OFF) configure_windows_unwind(agc_formatted_buffer_once_tests) +add_test_executable(agc_rect_list_position_invariant_tests EXCLUDE_FROM_ALL tests/RectListPositionInvariant.cpp) +target_link_libraries(agc_rect_list_position_invariant_tests PRIVATE shader_recompiler_agc_driver libc) +set_target_properties(agc_rect_list_position_invariant_tests PROPERTIES CXX_EXTENSIONS OFF) +configure_windows_unwind(agc_rect_list_position_invariant_tests) +if(BUILD_TESTING) + set_target_properties(agc_rect_list_position_invariant_tests PROPERTIES EXCLUDE_FROM_ALL OFF) + add_test(NAME agc_rect_list_position_invariant COMMAND agc_rect_list_position_invariant_tests) + if(WIN32) + set_tests_properties(agc_rect_list_position_invariant PROPERTIES ENVIRONMENT_MODIFICATION + "PATH=path_list_prepend:$") + endif() +endif() + add_test_executable(agc_shader_disk_cache_tests EXCLUDE_FROM_ALL tests/ShaderDiskCache.cpp) target_include_directories(agc_shader_disk_cache_tests PRIVATE ${LIBS_INCLUDE_DIR}) target_link_libraries(agc_shader_disk_cache_tests PRIVATE shader_recompiler_agc_driver libc) diff --git a/core/libs/prx/libSceAgcDriver/tests/RectListPositionInvariant.cpp b/core/libs/prx/libSceAgcDriver/tests/RectListPositionInvariant.cpp new file mode 100644 index 000000000..4c5be55a9 --- /dev/null +++ b/core/libs/prx/libSceAgcDriver/tests/RectListPositionInvariant.cpp @@ -0,0 +1,66 @@ +#include "Recompiler.hpp" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace { + +using namespace ShaderRecompiler; + +void Check(bool condition, const std::string& message) { + if (!condition) throw std::runtime_error(message); +} + +bool PositionIsInvariant(std::span words) { + Check(words.size() >= 5 && words[0] == spv::MagicNumber, "rect-list invariant: missing SPIR-V module"); + std::vector> positionMembers; + std::vector> invariantMembers; + for (std::size_t offset = 5; offset < words.size();) { + const auto count = words[offset] >> spv::WordCountShift; + Check(count != 0 && count <= words.size() - offset, "rect-list invariant: malformed SPIR-V instruction"); + const auto opcode = words[offset] & spv::OpCodeMask; + if (opcode == spv::OpMemberDecorate && count == 5 && words[offset + 3] == spv::DecorationBuiltIn && words[offset + 4] == spv::BuiltInPosition) { + positionMembers.emplace_back(words[offset + 1], words[offset + 2]); + } + if (opcode == spv::OpMemberDecorate && count == 4 && words[offset + 3] == spv::DecorationInvariant) { + invariantMembers.emplace_back(words[offset + 1], words[offset + 2]); + } + offset += count; + } + Check(!positionMembers.empty(), "rect-list invariant: shader has no gl_Position"); + for (const auto& member : positionMembers) { + if (std::find(invariantMembers.begin(), invariantMembers.end(), member) == invariantMembers.end()) return false; + } + return true; +} + +} + +int main() { + try { + static constexpr std::array capabilities{spv::CapabilityShader, spv::CapabilityTessellation}; + SpirvTarget target{}; + target.vulkanVersion = 0x00401000u; + target.spirvVersion = 0x00010400u; + target.supportedCapabilities = capabilities; + target.tessellation = TessellationTargetLimits{32, 128, 128, 120, 4096, 128, 128}; + RecompileResult vertex; + RecompileResult fragment; + const auto shaders = BuildRectListShaders(vertex, fragment, target); + Check(PositionIsInvariant(shaders.control.spirv.Words()), "rect-list control gl_Position is not Invariant"); + Check(PositionIsInvariant(shaders.evaluation.spirv.Words()), "rect-list evaluation gl_Position is not Invariant"); + std::printf("rect-list position invariant tests passed\n"); + return 0; + } catch (const std::exception& error) { + std::fprintf(stderr, "%s\n", error.what()); + return 1; + } +} diff --git a/core/shader/recompiler/SpirvBackend/src/RectListShaders.cpp b/core/shader/recompiler/SpirvBackend/src/RectListShaders.cpp index f8af830fa..59757fa18 100644 --- a/core/shader/recompiler/SpirvBackend/src/RectListShaders.cpp +++ b/core/shader/recompiler/SpirvBackend/src/RectListShaders.cpp @@ -41,7 +41,7 @@ public: vec4FloatType = type(spv::OpTypeVector, floatType, 4u); functionType = type(spv::OpTypeFunction, voidType); - perVertexType = builder.DecoratedType(spv::OpTypeStruct, {{spv::OpMemberDecorate, {0u, spv::DecorationBuiltIn, spv::BuiltInPosition}}, {spv::OpDecorate, {spv::DecorationBlock}}}, vec4FloatType); + perVertexType = builder.DecoratedType(spv::OpTypeStruct, {{spv::OpMemberDecorate, {0u, spv::DecorationBuiltIn, spv::BuiltInPosition}}, {spv::OpMemberDecorate, {0u, spv::DecorationInvariant}}, {spv::OpDecorate, {spv::DecorationBlock}}}, vec4FloatType); ptrInputVec4Float = pointer(spv::StorageClassInput, vec4FloatType); ptrOutputVec4Float = pointer(spv::StorageClassOutput, vec4FloatType);