only check LLVM crash on affected driver versions and eliminate IPC.

This commit is contained in:
goeiecool9999 2026-04-07 12:02:25 +02:00
parent 21122d9907
commit a52fe81add

View File

@ -341,71 +341,44 @@ void VulkanRenderer::GetDeviceFeatures()
static void WorkaroundChildAbortHandler(int unused) static void WorkaroundChildAbortHandler(int unused)
{ {
_exit(2); _exit(1);
} }
static void PerformBOTWLinuxWorkaround(int subProcessPipes[2]) static void LinuxBreathOfTheWildWorkaround(VkInstance& instance, const VkInstanceCreateInfo* create_info)
{ {
int childID = fork();
if (childID == 0) // inside this if statement runs in child
{
struct sigaction sa{.sa_handler = WorkaroundChildAbortHandler};
sigaction(SIGABRT, &sa, nullptr);
freopen("/dev/null", "w", stderr); // if the user specified either shader backend, do nothing.
// should parse the flag list but there are currently no other flags containing llvm or aco as a substring
setenv("RADV_DEBUG", "llvm", 1); std::string_view debugEnv = getenv("RADV_DEBUG");
if (debugEnv.find("aco") != std::string_view::npos || debugEnv.find("llvm") != std::string_view::npos)
VkInstanceCreateInfo instanceCreateInfo = {}; return;
instanceCreateInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
VkInstance instance = VK_NULL_HANDLE; uint32_t count = 0;
if (vkCreateInstance(&instanceCreateInfo, nullptr, &instance) != VK_SUCCESS) vkEnumeratePhysicalDevices(instance, &count, nullptr);
_exit(1);
std::vector<VkPhysicalDevice> physicalDevices{count};
InitializeInstanceVulkan(instance); vkEnumeratePhysicalDevices(instance, &count, physicalDevices.data());
uint32_t count = 0; // Find the first AMD device using a RADV driver and store its version
vkEnumeratePhysicalDevices(instance, &count, nullptr); int version = 0;
for (auto& i : physicalDevices)
std::vector<VkPhysicalDevice> physicalDevices{count}; {
vkEnumeratePhysicalDevices(instance, &count, physicalDevices.data()); VkPhysicalDeviceDriverProperties driverProps{};
driverProps.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DRIVER_PROPERTIES;
for (auto& i : physicalDevices) VkPhysicalDeviceProperties2 prop{};
{ prop.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2;
VkPhysicalDeviceProperties prop{}; prop.pNext = &driverProps;
vkGetPhysicalDeviceProperties(i, &prop); vkGetPhysicalDeviceProperties2(i, &prop);
if (prop.vendorID != 0x1002) if (prop.properties.vendorID != 0x1002 || driverProps.driverID != VK_DRIVER_ID_MESA_RADV)
continue; continue;
std::string_view deviceName = prop.deviceName; version = prop.properties.driverVersion;
if (deviceName.find("RADV") != std::string_view::npos) break;
{ }
write(subProcessPipes[1], &prop.driverVersion, sizeof(uint32_t));
vkDestroyInstance(instance, nullptr); if (version == 0)
_exit(0);
}
}
// no appropriate device found to query version
vkDestroyInstance(instance, nullptr);
_exit(1);
}
int childStatus = 0;
waitpid(childID, &childStatus, 0);
if (WEXITSTATUS(childStatus) == 2)
{
cemuLog_log(LogType::Force, "BOTW/RADV workaround not applied because mesa was built without LLVM");
}
// only continue if the process exits with code zero, which means it determined the version and didn't crash.
if (WEXITSTATUS(childStatus) != 0)
return; return;
uint32_t version = 0;
if (read(subProcessPipes[0], &version, sizeof(version)) == -1)
return; // if we fail to read bail. Should never happen if the subprocess exited with 0
int major = VK_API_VERSION_MAJOR(version); int major = VK_API_VERSION_MAJOR(version);
int minor = VK_API_VERSION_MINOR(version); int minor = VK_API_VERSION_MINOR(version);
@ -417,38 +390,55 @@ static void PerformBOTWLinuxWorkaround(int subProcessPipes[2])
if ((major <= 25 && minor < 3) || (major == 26 && (minor > 0 || patch >= 5)) || major > 26) if ((major <= 25 && minor < 3) || (major == 26 && (minor > 0 || patch >= 5)) || major > 26)
return; return;
cemuLog_log(LogType::Force, "BOTW/RADV workaround active. Adding llvm to RADV_DEBUG environment variable"); // check if running with LLVM would crash because mesa is LLVM-less.
// if the variable is empty set it to llvm, otherwise check if it contains llvm/aco and if not append it int childID = fork();
if (const char* value; (value = getenv("RADV_DEBUG")) != NULL && strlen(value) != 0) if (childID == 0) // inside this if statement runs in child
{ {
std::string valueStr{value}; struct sigaction sa{.sa_handler = WorkaroundChildAbortHandler};
// only append ,llvm when llvm or aco are not already passed as flags. sigaction(SIGABRT, &sa, nullptr);
// "aco" is not a mesa flag (anymore, it used to be when llvm was default)
// but it will provide users with a way to override this workaround by setting RADV_DEBUG=aco freopen("/dev/null", "w", stderr);
// should parse the flag list but there are currently no other flags containing llvm or aco as a substring
if (valueStr.find("llvm") == std::string::npos && valueStr.find("aco") == std::string::npos) setenv("RADV_DEBUG", "llvm", 1);
{
valueStr.append(",llvm"); VkInstance instance = VK_NULL_HANDLE;
setenv("RADV_DEBUG", valueStr.c_str(), 1); vkCreateInstance(create_info, nullptr, &instance);
} // this function will abort() when LLVM is absent
uint32_t count = 0;
vkEnumeratePhysicalDevices(instance, &count, nullptr);
vkDestroyInstance(instance, nullptr);
_exit(0);
} }
else
int childStatus = 0;
waitpid(childID, &childStatus, 0);
if (WEXITSTATUS(childStatus) == 1)
cemuLog_log(LogType::Force, "BOTW/RADV workaround not applied because mesa was built without LLVM");
// only continue if the process exits with code zero, which means it didn't crash
if (WEXITSTATUS(childStatus) != 0)
return;
cemuLog_log(LogType::Force, "BOTW/RADV workaround active. Adding \"llvm\" to RADV_DEBUG environment variable");
if (debugEnv.empty())
{ {
setenv("RADV_DEBUG", "llvm", 1); setenv("RADV_DEBUG", "llvm", 1);
} }
else
{
std::string appendedDebugEnv{debugEnv};
appendedDebugEnv.append(",llvm");
setenv("RADV_DEBUG", appendedDebugEnv.c_str(), 1);
}
// recreate the vulkan instance to update debug setting
vkDestroyInstance(instance, nullptr);
vkCreateInstance(create_info, nullptr, &instance);
InitializeInstanceVulkan(instance);
} }
static void LinuxBreathOfTheWildWorkaround()
{
int subProcessPipes[2]{};
pipe(subProcessPipes);
PerformBOTWLinuxWorkaround(subProcessPipes);
close(subProcessPipes[0]);
close(subProcessPipes[1]);
}
#endif #endif
VulkanRenderer::VulkanRenderer() VulkanRenderer::VulkanRenderer()
@ -457,15 +447,6 @@ VulkanRenderer::VulkanRenderer()
cemuLog_log(LogType::Force, "------- Init Vulkan graphics backend -------"); cemuLog_log(LogType::Force, "------- Init Vulkan graphics backend -------");
// Workaround for BOTW + RADV. Runes like Magnesis and the camera cause GPU crashes.
#if BOOST_OS_LINUX
uint64 currentTitleId = CafeSystem::GetForegroundTitleId();
if (currentTitleId == 0x00050000101c9500 || currentTitleId == 0x00050000101c9400 || currentTitleId == 0x00050000101c9300)
{
LinuxBreathOfTheWildWorkaround();
}
#endif
const bool useValidationLayer = cemuLog_isLoggingEnabled(LogType::VulkanValidation); const bool useValidationLayer = cemuLog_isLoggingEnabled(LogType::VulkanValidation);
if (useValidationLayer) if (useValidationLayer)
cemuLog_log(LogType::Force, "Validation layer is enabled"); cemuLog_log(LogType::Force, "Validation layer is enabled");
@ -519,6 +500,15 @@ VulkanRenderer::VulkanRenderer()
if (!InitializeInstanceVulkan(m_instance)) if (!InitializeInstanceVulkan(m_instance))
throw std::runtime_error("Unable to load instanced Vulkan functions"); throw std::runtime_error("Unable to load instanced Vulkan functions");
// Workaround for BOTW + RADV. Runes like Magnesis and the camera cause GPU crashes.
#if BOOST_OS_LINUX
uint64 currentTitleId = CafeSystem::GetForegroundTitleId();
if (currentTitleId == 0x00050000101c9500 || currentTitleId == 0x00050000101c9400 || currentTitleId == 0x00050000101c9300)
{
LinuxBreathOfTheWildWorkaround(m_instance, &create_info);
}
#endif
uint32_t device_count = 0; uint32_t device_count = 0;
vkEnumeratePhysicalDevices(m_instance, &device_count, nullptr); vkEnumeratePhysicalDevices(m_instance, &device_count, nullptr);
if (device_count == 0) if (device_count == 0)