mirror of
https://github.com/cemu-project/Cemu.git
synced 2026-04-25 04:05:49 -06:00
Merge branch 'cemu-project:main' into feat/gfxpack-url
This commit is contained in:
commit
201287a49f
1
.github/workflows/build.yml
vendored
1
.github/workflows/build.yml
vendored
@ -283,6 +283,7 @@ jobs:
|
||||
mv bin/Cemu_app/Cemu.app/Contents/MacOS/Cemu_release bin/Cemu_app/Cemu.app/Contents/MacOS/Cemu
|
||||
sed -i '' 's/Cemu_release/Cemu/g' bin/Cemu_app/Cemu.app/Contents/Info.plist
|
||||
chmod a+x bin/Cemu_app/Cemu.app/Contents/MacOS/{Cemu,update.sh}
|
||||
codesign --entitlements ${{github.workspace}}/src/resource/cemu.macos.entitlements --force --deep --preserve-metadata=entitlements,requirements,flags,runtime --sign - --timestamp --options runtime bin/Cemu_app/Cemu.app/Contents/MacOS/Cemu
|
||||
ln -s /Applications bin/Cemu_app/Applications
|
||||
hdiutil create ./bin/tmp.dmg -ov -volname "Cemu" -fs HFS+ -srcfolder "./bin/Cemu_app"
|
||||
hdiutil convert ./bin/tmp.dmg -format UDZO -o bin/Cemu.dmg
|
||||
|
||||
31
BUILD.md
31
BUILD.md
@ -149,44 +149,31 @@ If you are getting a different error than any of the errors listed above, you ma
|
||||
|
||||
To compile Cemu, a recent enough compiler and STL with C++20 support is required! LLVM 13 and below
|
||||
don't support the C++20 feature set required, so either install LLVM from Homebrew or make sure that
|
||||
you have a recent enough version of Xcode. Xcode 15 is known to work. The OpenGL graphics API isn't
|
||||
supported on macOS, so Vulkan must be used through the Molten-VK compatibility layer.
|
||||
you have a recent enough version of Xcode. Xcode 15 is known to work.
|
||||
|
||||
### Installing brew
|
||||
|
||||
1. `/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"`
|
||||
2. Set up the Homebrew shell environment:
|
||||
1. **On an Intel Mac:** `eval "$(/usr/local/Homebrew/bin/brew shellenv)"`
|
||||
2. **On an Apple Silicon Mac:** eval `"$(/opt/homebrew/bin/brew shellenv)"`
|
||||
- **On an Apple Silicon Mac:** eval `"$(/opt/homebrew/bin/brew shellenv)"`
|
||||
- **On an Intel Mac:** `eval "$(/usr/local/Homebrew/bin/brew shellenv)"`
|
||||
|
||||
### Installing Tool Dependencies
|
||||
### Dependencies
|
||||
|
||||
The native versions of these can be used regardless of what type of Mac you have.
|
||||
|
||||
`brew install git cmake ninja nasm automake libtool`
|
||||
|
||||
### Installing Library Dependencies
|
||||
|
||||
**On Apple Silicon Macs, Rosetta 2 and the x86_64 version of Homebrew must be used to install these dependencies:**
|
||||
1. `softwareupdate --install-rosetta` # Install Rosetta 2 if you don't have it. This only has to be done once
|
||||
2. `arch -x86_64 zsh` # run an x64 shell
|
||||
3. `/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"`
|
||||
4. `eval "$(/usr/local/Homebrew/bin/brew shellenv)"`
|
||||
|
||||
Then install the dependencies:
|
||||
|
||||
`brew install boost molten-vk`
|
||||
`brew install git cmake ninja nasm automake libtool boost molten-vk`
|
||||
|
||||
### Build Cemu using CMake
|
||||
|
||||
1. `git clone --recursive https://github.com/cemu-project/Cemu`
|
||||
2. `cd Cemu`
|
||||
3. `cmake -S . -B build -DCMAKE_BUILD_TYPE=release -DCMAKE_OSX_ARCHITECTURES=x86_64 -G Ninja`
|
||||
3. `cmake -S . -B build -DCMAKE_BUILD_TYPE=release -G Ninja`
|
||||
4. `cmake --build build`
|
||||
5. You should now have a Cemu executable file in the /bin folder, which you can run using `./bin/Cemu_release`.
|
||||
|
||||
#### Troubleshooting steps
|
||||
- If step 3 gives you an error about not being able to find ninja, try appending `-DCMAKE_MAKE_PROGRAM=/usr/local/bin/ninja` to the command and running it again.
|
||||
- If step 3 gives you an error about not being able to find ninja, try appending the following to the command and try again:
|
||||
- **On an Apple Silicon Mac:** `-DCMAKE_MAKE_PROGRAM=/opt/homebrew/bin/ninja`
|
||||
- **On an Intel Mac:** `-DCMAKE_MAKE_PROGRAM=/usr/local/bin/ninja`
|
||||
|
||||
## FreeBSD
|
||||
|
||||
|
||||
@ -130,10 +130,10 @@ if (MACOS_BUNDLE)
|
||||
COMMAND ${CMAKE_COMMAND} ARGS -E copy "${LIBUSB_PATH}" "${CMAKE_SOURCE_DIR}/bin/${OUTPUT_NAME}.app/Contents/Frameworks/libusb-1.0.0.dylib"
|
||||
COMMAND ${CMAKE_COMMAND} ARGS -E copy "${CMAKE_SOURCE_DIR}/src/resource/update.sh" "${CMAKE_SOURCE_DIR}/bin/${OUTPUT_NAME}.app/Contents/MacOS/update.sh"
|
||||
COMMAND bash -c "install_name_tool -add_rpath @executable_path/../Frameworks ${CMAKE_SOURCE_DIR}/bin/${OUTPUT_NAME}.app/Contents/MacOS/${OUTPUT_NAME}"
|
||||
COMMAND bash -c "install_name_tool -change ${LIBUSB_PATH} @executable_path/../Frameworks/libusb-1.0.0.dylib ${CMAKE_SOURCE_DIR}/bin/${OUTPUT_NAME}.app/Contents/MacOS/${OUTPUT_NAME}")
|
||||
COMMAND install_name_tool -change @rpath/libusb-1.0.0.dylib @executable_path/../Frameworks/libusb-1.0.0.dylib ${CMAKE_SOURCE_DIR}/bin/${OUTPUT_NAME}.app/Contents/MacOS/${OUTPUT_NAME})
|
||||
else()
|
||||
if(APPLE)
|
||||
find_library(MOLTENVK_LIBRARY
|
||||
find_library(MOLTENVK_LIBRARY
|
||||
NAMES MoltenVK moltenvk libMoltenVK.dylib
|
||||
PATHS /usr/local/lib /opt/homebrew/lib
|
||||
)
|
||||
@ -143,7 +143,7 @@ else()
|
||||
else()
|
||||
message(WARNING "libMoltenVK.dylib not found")
|
||||
endif()
|
||||
set_target_properties(CemuBin PROPERTIES
|
||||
set_target_properties(CemuBin PROPERTIES
|
||||
BUILD_WITH_INSTALL_RPATH TRUE
|
||||
INSTALL_RPATH "/usr/local/lib;/opt/homebrew/lib"
|
||||
)
|
||||
|
||||
@ -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));
|
||||
|
||||
@ -336,6 +336,136 @@ 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.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 +525,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)
|
||||
|
||||
@ -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();
|
||||
|
||||
@ -155,7 +155,6 @@ void BreakpointWindow::OnBreakpointToggled(wxListEvent& event)
|
||||
DebuggerBreakpoint* bp = (DebuggerBreakpoint*)m_breakpoints->GetItemData(index);
|
||||
const uint32 address = std::stoul(line.ToStdString(), nullptr, 16);
|
||||
debugger_toggleBreakpoint(address, state, bp);
|
||||
m_breakpoints->CheckItem(index, state);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
13
src/resource/cemu.macos.entitlements
Normal file
13
src/resource/cemu.macos.entitlements
Normal file
@ -0,0 +1,13 @@
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>com.apple.security.cs.disable-library-validation</key>
|
||||
<true/>
|
||||
<key>com.apple.security.get-task-allow</key>
|
||||
<true/>
|
||||
<key>com.apple.security.cs.allow-jit</key>
|
||||
<true/>
|
||||
<key>com.apple.security.cs.allow-unsigned-executable-memory</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
||||
Loading…
Reference in New Issue
Block a user