fix(agc): do not use fragment barycentric on MoltenVK

MoltenVK reports VK_KHR_fragment_shader_barycentric, but its SPIR-V to MSL step
rejects the PerVertexKHR decoration that the barycentric path uses, so every
pixel shader on that path fails to compile into a pipeline. Treat the feature
as unavailable when the driver is MoltenVK, so the no-barycentric path is used.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
Morten Holm 2026-10-08 17:31:20 +02:00
parent 4b005b229e
commit 5333a20537
4 changed files with 63 additions and 0 deletions

View File

@ -284,6 +284,15 @@ target_link_libraries(agc_structurizer_tests PRIVATE shader_recompiler_agc_drive
set_target_properties(agc_structurizer_tests PROPERTIES CXX_EXTENSIONS OFF)
configure_windows_unwind(agc_structurizer_tests)
add_test_executable(agc_fragment_barycentric_tests EXCLUDE_FROM_ALL tests/FragmentBarycentric.cpp)
target_include_directories(agc_fragment_barycentric_tests PRIVATE ${LIBS_INCLUDE_DIR} ${CMAKE_SOURCE_DIR}/3rdparty/Vulkan-Headers/include)
set_target_properties(agc_fragment_barycentric_tests PROPERTIES CXX_EXTENSIONS OFF)
configure_windows_unwind(agc_fragment_barycentric_tests)
if(BUILD_TESTING)
set_target_properties(agc_fragment_barycentric_tests PROPERTIES EXCLUDE_FROM_ALL OFF)
add_test(NAME agc_fragment_barycentric COMMAND agc_fragment_barycentric_tests)
endif()
add_test_executable(agc_subgroup_clock_tests EXCLUDE_FROM_ALL
tests/SubgroupClock.cpp
Execution/src/SubgroupClock.cpp

View File

@ -0,0 +1,14 @@
#ifndef CORE_LIBS_PRX_LIBSCEAGCDRIVER_EXECUTION_INCLUDE_FRAGMENTBARYCENTRIC_HPP
#define CORE_LIBS_PRX_LIBSCEAGCDRIVER_EXECUTION_INCLUDE_FRAGMENTBARYCENTRIC_HPP
#include <vulkan/vulkan.h>
namespace AgcDriver {
inline bool FragmentShaderBarycentricUsable(VkDriverId driver, bool reported) {
return reported && driver != VK_DRIVER_ID_MOLTENVK;
}
}
#endif

View File

@ -5,6 +5,7 @@
#include "prx/libSceAgcDriver/Execution/include/Driver.hpp"
#include "prx/libSceAgcDriver/Execution/include/PerformanceTimer.hpp"
#include "prx/libSceAgcDriver/Execution/include/BdaFeatures.hpp"
#include "prx/libSceAgcDriver/Execution/include/FragmentBarycentric.hpp"
#include "prx/libSceAgcDriver/Execution/include/SubgroupClock.hpp"
#include "prx/libSceAgcDriver/Execution/include/PresentationScaler.hpp"
#include "prx/libSceAgcDriver/Graphics/include/DepthSurface.hpp"
@ -756,6 +757,12 @@ VulkanDevice::VulkanDevice(const PresentationWindow* window) : state(std::make_u
VkPhysicalDeviceFeatures2 features{VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2, &barycentricFeatures};
state->InstanceFunction<PFN_vkGetPhysicalDeviceFeatures2>("vkGetPhysicalDeviceFeatures2")(selected, &features);
state->fragmentShaderBarycentric = barycentricFeatures.fragmentShaderBarycentric == VK_TRUE;
if (state->fragmentShaderBarycentric && hasExtension(VK_KHR_DRIVER_PROPERTIES_EXTENSION_NAME)) {
VkPhysicalDeviceDriverProperties driver{VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DRIVER_PROPERTIES};
VkPhysicalDeviceProperties2 driverProperties{VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2, &driver};
state->InstanceFunction<PFN_vkGetPhysicalDeviceProperties2>("vkGetPhysicalDeviceProperties2")(selected, &driverProperties);
state->fragmentShaderBarycentric = FragmentShaderBarycentricUsable(driver.driverID, state->fragmentShaderBarycentric);
}
}
VkPhysicalDeviceFragmentShaderInterlockFeaturesEXT interlockFeatures{VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FRAGMENT_SHADER_INTERLOCK_FEATURES_EXT};
if (hasExtension(VK_EXT_FRAGMENT_SHADER_INTERLOCK_EXTENSION_NAME)) {

View File

@ -0,0 +1,33 @@
#include "prx/libSceAgcDriver/Execution/include/FragmentBarycentric.hpp"
#include <cstdio>
namespace {
struct Case {
VkDriverId driver;
bool reported;
bool usable;
};
constexpr Case Cases[]{
{VK_DRIVER_ID_MOLTENVK, true, false},
{VK_DRIVER_ID_MOLTENVK, false, false},
{VK_DRIVER_ID_MESA_RADV, true, true},
{VK_DRIVER_ID_MESA_RADV, false, false},
{VK_DRIVER_ID_NVIDIA_PROPRIETARY, true, true},
{VK_DRIVER_ID_AMD_PROPRIETARY, true, true},
{VK_DRIVER_ID_INTEL_PROPRIETARY_WINDOWS, false, false},
};
}
int main() {
int failures = 0;
for (const auto& item : Cases) {
if (AgcDriver::FragmentShaderBarycentricUsable(item.driver, item.reported) != item.usable) {
std::fprintf(stderr, "driver %d reported %d: expected %s fragment barycentric\n", static_cast<int>(item.driver), item.reported ? 1 : 0, item.usable ? "usable" : "unusable");
++failures;
}
}
return failures == 0 ? 0 : 1;
}