Compare commits

...

4 Commits

5 changed files with 194 additions and 30 deletions

View File

@ -245,6 +245,10 @@ uint8 botw_crashFuncMask[] = { 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF
uint8 ffl_floatArrayEndianSwap[] = { 0x7C,0x08,0x02,0xA6,0x94,0x21,0xFF,0xE8,0x93,0xC1,0x00,0x10,0x7C,0x7E,0x1B,0x78,0x93,0xE1,0x00,0x14,0x93,0x81,0x00,0x08,0x7C,0x9F,0x23,0x78,0x93,0xA1,0x00,0x0C,0x90,0x01,0x00,0x1C,0x3B,0xA0,0x00,0x00,0x7C,0x1D,0xF8,0x40,0x40,0x80,0x00,0x20,0x57,0xBC,0x10,0x3A,0x7C,0x3E,0xE4,0x2E };
// Alternative FFL endian swap signature (Mario Kart 8 and possibly other titles)
// Same algorithm as above but with a different compiler-scheduled prologue ordering
uint8 ffl_floatArrayEndianSwap_v2[] = { 0x7C,0x08,0x02,0xA6,0x94,0x21,0xFF,0xE8,0x93,0xE1,0x00,0x14,0x93,0xA1,0x00,0x0C,0x7C,0x9F,0x23,0x78,0x93,0x81,0x00,0x08,0x93,0xC1,0x00,0x10,0x90,0x01,0x00,0x1C,0x3B,0xA0,0x00,0x00,0x7C,0x1D,0xF8,0x40,0x7C,0x7E,0x1B,0x78,0x40,0x80,0x00,0x20,0x57,0xBC,0x10,0x3A,0x7C,0x3E,0xE4,0x2E };
uint8 xcx_enterCriticalSectionSignature[] = { 0x94,0x21,0xFF,0xE0,0xBF,0x41,0x00,0x08,0x7C,0x08,0x02,0xA6,0x90,0x01,0x00,0x24,0x7C,0x7E,0x1B,0x78,0x80,0x1E,0x00,0x08,0x2C,0x00,0x00,0x00,0x41,0x82,0x00,0xC0,0x48,0x01,0xD7,0xA1,0x7C,0x7A,0x1B,0x79,0x41,0x82,0x00,0xB4,0x81,0x3E,0x00,0x04,0x7C,0x09,0xD0,0x40,0x40,0x82,0x00,0x2C,0x7D,0x20,0xF0,0x28,0x7C,0x00,0xF0,0x6C };
uint8 xcx_enterCriticalSectionMask[] = { 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF };
@ -361,11 +365,23 @@ void GamePatch_scan()
hleAddr = hle_locate(ffl_floatArrayEndianSwap, NULL, sizeof(ffl_floatArrayEndianSwap));
if (hleAddr)
{
cemuLog_logDebug(LogType::Force, "HLE: Hook FFL float array endian swap function at 0x{:08x}", hleAddr);
cemuLog_logDebug(LogType::Force, "HLE: Hook FFL float array endian swap function (v1) at 0x{:08x}", hleAddr);
sint32 functionIndex = hleIndex_h000000003;
uint32 opcode = (1 << 26) | (functionIndex); // opcode for HLE: 0x1000 + FunctionIndex
memory_write<uint32>(hleAddr, opcode);
}
else
{
// Try v2 signature (Mario Kart 8 and possibly other titles with reordered prologue)
hleAddr = hle_locate(ffl_floatArrayEndianSwap_v2, NULL, sizeof(ffl_floatArrayEndianSwap_v2));
if (hleAddr)
{
cemuLog_logDebug(LogType::Force, "HLE: Hook FFL float array endian swap function (v2) at 0x{:08x}", hleAddr);
sint32 functionIndex = hleIndex_h000000003;
uint32 opcode = (1 << 26) | (functionIndex); // opcode for HLE: 0x1000 + FunctionIndex
memory_write<uint32>(hleAddr, opcode);
}
}
// XCX freeze workaround
//hleAddr = hle_locate(xcx_enterCriticalSectionSignature, xcx_enterCriticalSectionMask, sizeof(xcx_enterCriticalSectionSignature));

View File

@ -336,6 +336,134 @@ void VulkanRenderer::GetDeviceFeatures()
cemuLog_log(LogType::Force, fmt::format("VulkanLimits: UBAlignment {0} nonCoherentAtomSize {1}", prop2.properties.limits.minUniformBufferOffsetAlignment, prop2.properties.limits.nonCoherentAtomSize));
}
#if BOOST_OS_LINUX
#include <sys/wait.h>
#include "resource/IconsFontAwesome5.h"
int BreathOfTheWildChildProcessMain()
{
InitializeGlobalVulkan();
struct sigaction sa{.sa_handler = [](int unused){_exit(1);}};
int ret = sigaction(SIGABRT, &sa, nullptr);
freopen("/dev/null", "w", stderr);
setenv("RADV_DEBUG", "llvm", 1);
VkInstanceCreateInfo create_info{};
create_info.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
VkInstance instance = VK_NULL_HANDLE;
if (vkCreateInstance(&create_info, nullptr, &instance) != VK_SUCCESS)
return 1;
InitializeInstanceVulkan(instance);
// this function will abort() when LLVM is absent
uint32_t count = 0;
vkEnumeratePhysicalDevices(instance, &count, nullptr);
vkDestroyInstance(instance, nullptr);
return 0;
}
static void LinuxBreathOfTheWildWorkaround(VkInstance& instance, const VkInstanceCreateInfo* create_info)
{
// 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
const char* debugEnvC = getenv("RADV_DEBUG");
std::string_view debugEnv = debugEnvC != nullptr ? debugEnvC : "";
if (debugEnv.find("aco") != std::string_view::npos || debugEnv.find("llvm") != std::string_view::npos)
return;
uint32_t count = 0;
vkEnumeratePhysicalDevices(instance, &count, nullptr);
std::vector<VkPhysicalDevice> physicalDevices{count};
vkEnumeratePhysicalDevices(instance, &count, physicalDevices.data());
// Find the first AMD device using a RADV driver and store its version
int version = 0;
for (auto& i : physicalDevices)
{
VkPhysicalDeviceDriverProperties driverProps{};
driverProps.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DRIVER_PROPERTIES;
VkPhysicalDeviceProperties2 prop{};
prop.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2;
prop.pNext = &driverProps;
vkGetPhysicalDeviceProperties2(i, &prop);
if (prop.properties.vendorID != 0x1002 || driverProps.driverID != VK_DRIVER_ID_MESA_RADV)
continue;
version = prop.properties.driverVersion;
break;
}
if (version == 0)
return;
int major = VK_API_VERSION_MAJOR(version);
int minor = VK_API_VERSION_MINOR(version);
int patch = VK_API_VERSION_PATCH(version);
// If the driver is unaffected skip the workaround.
// affected drivers:
// 25.3.0 - 26.0.4
if ((major <= 25 && minor < 3) || (major == 26 && (minor > 0 || patch >= 5)) || major > 26)
return;
// check if running with LLVM would crash because mesa is LLVM-less.
int childID = fork();
if (childID == 0) // inside this if statement runs in child
{
setenv("CEMU_DETECT_RADV","1", 1);
execl("/proc/self/exe", "/proc/self/exe", nullptr);
_exit(2); // exec failed so err on the safe side and signal failure
}
int childStatus = 0;
waitpid(childID, &childStatus, 0);
// if the process didn't exit cleanly or failed to determine LLVM status
if (!WIFEXITED(childStatus) || WEXITSTATUS(childStatus) == 2)
{
cemuLog_log(LogType::Force, "BOTW/RADV workaround not applied because detecting LLVM presence failed unexpectedly");
return;
}
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);
}
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);
VkResult err = vkCreateInstance(create_info, nullptr, &instance);
// re-check for errors just in case.
if (err != VK_SUCCESS)
throw std::runtime_error(fmt::format("Unable to re-create a Vulkan instance after RADV/LLVM workaround: {}", err));
InitializeInstanceVulkan(instance);
LatteOverlay_pushNotification(std::string{(const char*)ICON_FA_EXCLAMATION_TRIANGLE} + "RADV_DEBUG=llvm set automatically to avoid crashing due to a driver bug. If possible update mesa to 26.0.5 or newer", 10'000);
}
#endif
VulkanRenderer::VulkanRenderer()
{
glslang::InitializeProcess();
@ -395,6 +523,15 @@ VulkanRenderer::VulkanRenderer()
if (!InitializeInstanceVulkan(m_instance))
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;
vkEnumeratePhysicalDevices(m_instance, &device_count, nullptr);
if (device_count == 0)

View File

@ -5,6 +5,9 @@
#if BOOST_OS_WINDOWS
#include <iphlpapi.h>
#elif BOOST_OS_LINUX
#include <ifaddrs.h>
#include <net/if.h>
#endif
// AC lib (manages internet connection)
@ -81,6 +84,37 @@ void _GetLocalIPAndSubnetMask(uint32& localIp, uint32& subnetMask)
cemuLog_logDebug(LogType::Force, "_GetLocalIPAndSubnetMask(): Failed to find network IP and subnet mask");
_GetLocalIPAndSubnetMaskFallback(localIp, subnetMask);
}
#elif BOOST_OS_LINUX
void _GetLocalIPAndSubnetMask(uint32& localIp, uint32& subnetMask)
{
struct ifaddrs *ifaddr;
if (getifaddrs(&ifaddr) == -1)
{
cemuLog_log(LogType::Force, "Failed to acquire local IP and subnet mask");
_GetLocalIPAndSubnetMaskFallback(localIp, subnetMask);
}
stdx::scope_exit _ifa([&]{ freeifaddrs(ifaddr); });
for (const struct ifaddrs* ifa = ifaddr; ifa != nullptr; ifa = ifa->ifa_next)
{
if (ifa->ifa_addr == nullptr || ifa->ifa_addr->sa_family != AF_INET)
continue;
if (!(ifa->ifa_flags & IFF_UP) || !(ifa->ifa_flags & IFF_RUNNING))
continue;
if (ifa->ifa_flags & IFF_LOOPBACK)
continue;
const auto* addr_in = reinterpret_cast<struct sockaddr_in*>(ifa->ifa_addr);
localIp = ntohl(addr_in->sin_addr.s_addr);
const auto* mask_in = reinterpret_cast<struct sockaddr_in*>(ifa->ifa_netmask);
subnetMask = ntohl(mask_in->sin_addr.s_addr);
return;
}
cemuLog_logDebug(LogType::Force, "_GetLocalIPAndSubnetMask(): Failed to find network IP and subnet mask");
_GetLocalIPAndSubnetMaskFallback(localIp, subnetMask);
}
#else
void _GetLocalIPAndSubnetMask(uint32& localIp, uint32& subnetMask)
{

View File

@ -621,35 +621,6 @@ bool MainWindow::FileLoad(const fs::path launchPath, wxLaunchGameEvent::INITIATE
cemuLog_log(LogType::Force, "GameMode has been started.");
}
}
#endif
// Workaround for BOTW + Mesa. Runes like Magnesis and the camera cause GPU crashes.
// Using the LLVM shader compiler prevents crashes which points to an issue with the default ACO compiler.
// Either that or Cemu is violating a specification (GLSL?) causing the shaders to be broken after compilation.
#if BOOST_OS_LINUX
uint64 currentTitleId = CafeSystem::GetForegroundTitleId();
if (currentTitleId == 0x00050000101c9500 || currentTitleId == 0x00050000101c9400 || currentTitleId == 0x00050000101c9300)
{
// if the variable is empty set it to llvm, otherwise check if it contains llvm/aco and if not append it
if (const char* value; (value = getenv("RADV_DEBUG")) != NULL && strlen(value) != 0)
{
std::string valueStr{value};
// only append ,llvm when llvm or aco are not already passed as flags.
// "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
// 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)
{
valueStr.append(",llvm");
setenv("RADV_DEBUG", valueStr.c_str(), 1);
}
}
else
{
setenv("RADV_DEBUG", "llvm", 1);
}
}
#endif
CreateCanvas();

View File

@ -255,8 +255,14 @@ int main(int argc, char* argv[])
#else
int BreathOfTheWildChildProcessMain();
int main(int argc, char *argv[])
{
#if BOOST_OS_LINUX
if (getenv("CEMU_DETECT_RADV") != nullptr)
return BreathOfTheWildChildProcessMain();
#endif
#if BOOST_OS_LINUX || BOOST_OS_BSD
XInitThreads();
#endif