mirror of
https://git.743378673.xyz/MeloNX/MeloNX.git
synced 2025-06-27 19:06:23 +02:00
Merge branch 'XC-ios-ht' into rumbleDev
This commit is contained in:
commit
6ece04918d
46 changed files with 6610 additions and 463 deletions
|
@ -48,7 +48,7 @@ MeloNX works on iPhone XS/XR and later and iPad 8th Gen and later. Check out the
|
|||
|
||||
4. **Enable JIT**
|
||||
- Use your preferred method to enable Just-In-Time (JIT) compilation.
|
||||
- We reccomend using [JitStreamer](https://jkcoxson.com/jitstreamer)
|
||||
- We reccomend using [StikDebug](https://apps.apple.com/us/app/stikdebug/id6744045754)
|
||||
|
||||
5. **Add Necessary Files**
|
||||
|
||||
|
@ -90,7 +90,7 @@ If having Issues installing firmware (Make sure your keys are installed first)
|
|||
|
||||
9. **Enable JIT**
|
||||
- Use your preferred method to enable Just-In-Time (JIT) compilation.
|
||||
- We recommend using [JitStreamer](https://jkcoxson.com/jitstreamer)
|
||||
- We recommend using [StikDebug](https://apps.apple.com/us/app/stikdebug/id6744045754)
|
||||
|
||||
|
||||
### TrollStore
|
||||
|
|
|
@ -3,11 +3,13 @@
|
|||
# Define the destination directory (hardcoded)
|
||||
DESTINATION_DIR="src/MeloNX/Dependencies/Dynamic\ Libraries/Ryujinx.Headless.SDL2.dylib"
|
||||
|
||||
dotnet clean
|
||||
|
||||
# Restore the project
|
||||
dotnet restore
|
||||
|
||||
# Build the project with the specified version
|
||||
dotnet build -c Release
|
||||
# dotnet build -c Release
|
||||
|
||||
# Publish the project with the specified runtime and settings
|
||||
dotnet publish -c Release -r ios-arm64 -p:ExtraDefineConstants=DISABLE_UPDATER src/Ryujinx.Headless.SDL2 --self-contained true
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
#!/bin/bash
|
||||
|
||||
XCCONFIG_FILE="${SRCROOT}/MeloNX.xcconfig"
|
||||
# XCCONFIG_FILE="${SRCROOT}/MeloNX.xcconfig"
|
||||
|
||||
# Define the common paths to search for dotnet, including user-specific directories
|
||||
SEARCH_PATHS=(
|
||||
"/usr/local/share/dotnet"
|
||||
"/usr/local/bin"
|
||||
|
@ -14,10 +13,10 @@ SEARCH_PATHS=(
|
|||
"$HOME/Developer"
|
||||
)
|
||||
|
||||
# Initialize DOTNET_PATH as empty
|
||||
|
||||
|
||||
DOTNET_PATH=""
|
||||
|
||||
# Search in the defined paths
|
||||
for path in "${SEARCH_PATHS[@]}"; do
|
||||
if [ -d "$path" ]; then
|
||||
DOTNET_PATH=$(find "$path" -name dotnet -type f -print -quit 2>/dev/null)
|
||||
|
@ -27,20 +26,8 @@ for path in "${SEARCH_PATHS[@]}"; do
|
|||
fi
|
||||
done
|
||||
|
||||
# Check if the path was found
|
||||
if [ -z "$DOTNET_PATH" ]; then
|
||||
echo "Error: dotnet path not found."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "dotnet path: $DOTNET_PATH"
|
||||
|
||||
# Escape the path for sed
|
||||
ESCAPED_PATH=$(echo "$DOTNET_PATH" | sed 's/\//\\\//g')
|
||||
|
||||
# Update the xcconfig file
|
||||
sed -i '' "s/^DOTNET = .*/DOTNET = $ESCAPED_PATH/g" "$XCCONFIG_FILE"
|
||||
|
||||
$DOTNET_PATH clean
|
||||
|
||||
echo "Updated MeloNX.xcconfig with DOTNET path: $DOTNET_PATH"
|
||||
echo "$DOTNET_PATH"
|
||||
|
|
21
distribution/ios/xc-compile.sh
Executable file
21
distribution/ios/xc-compile.sh
Executable file
|
@ -0,0 +1,21 @@
|
|||
dotnet_output=$(./distribution/ios/get_dotnet.sh)
|
||||
exit_code=$?
|
||||
|
||||
if [ $exit_code -eq 0 ]; then
|
||||
dotnet="$dotnet_output"
|
||||
else
|
||||
echo "error: .NET not found, Please follow the compilation instructions on the gitea." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
$dotnet publish -c Release -r ios-arm64 -p:ExtraDefineConstants=DISABLE_UPDATER src/Ryujinx.Headless.SDL2 --self-contained true
|
||||
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "warning: Compiling MeloNX failed! Running dotnet clean + restore then Retrying..."
|
||||
|
||||
$dotnet clean
|
||||
|
||||
$dotnet restore
|
||||
|
||||
$dotnet publish -c Release -r ios-arm64 -p:ExtraDefineConstants=DISABLE_UPDATER src/Ryujinx.Headless.SDL2 --self-contained true
|
||||
fi
|
|
@ -75,7 +75,7 @@ namespace ARMeilleure.Translation
|
|||
FunctionTable = new AddressTable<ulong>(for64Bits ? _levels64Bit : _levels32Bit);
|
||||
Stubs = new TranslatorStubs(FunctionTable);
|
||||
|
||||
FunctionTable.Fill = (ulong)Stubs.SlowDispatchStub;
|
||||
FunctionTable.Fill = (ulong)Stubs.DispatchStub;
|
||||
}
|
||||
|
||||
public IPtcLoadState LoadDiskCache(string titleIdText, string displayVersion, bool enabled)
|
||||
|
|
|
@ -9,5 +9,3 @@
|
|||
// https://help.apple.com/xcode/#/dev745c5c974
|
||||
|
||||
VERSION = 1.7.0
|
||||
|
||||
DOTNET = /usr/local/share/dotnet/dotnet
|
||||
|
|
|
@ -31,7 +31,7 @@
|
|||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXContainerItemProxy section */
|
||||
4E2953AB2D803BC9000497CD /* PBXContainerItemProxy */ = {
|
||||
4E59B0A32DEA5CA9004BFF2A /* PBXContainerItemProxy */ = {
|
||||
isa = PBXContainerItemProxy;
|
||||
containerPortal = 4E80A9852CD6F54500029585 /* Project object */;
|
||||
proxyType = 1;
|
||||
|
@ -119,6 +119,10 @@
|
|||
CodeSignOnCopy,
|
||||
RemoveHeadersOnCopy,
|
||||
);
|
||||
"Dependencies/Dynamic Libraries/StosJIT.framework" = (
|
||||
CodeSignOnCopy,
|
||||
RemoveHeadersOnCopy,
|
||||
);
|
||||
"Dependencies/Dynamic Libraries/libMoltenVK.dylib" = (
|
||||
CodeSignOnCopy,
|
||||
);
|
||||
|
@ -173,6 +177,7 @@
|
|||
"Dependencies/Dynamic Libraries/libMoltenVK.dylib",
|
||||
"Dependencies/Dynamic Libraries/Ryujinx.Headless.SDL2.dylib",
|
||||
"Dependencies/Dynamic Libraries/RyujinxHelper.framework",
|
||||
"Dependencies/Dynamic Libraries/StosJIT.framework",
|
||||
Dependencies/XCFrameworks/libavcodec.xcframework,
|
||||
Dependencies/XCFrameworks/libavfilter.xcframework,
|
||||
Dependencies/XCFrameworks/libavformat.xcframework,
|
||||
|
@ -257,12 +262,12 @@
|
|||
/* Begin PBXLegacyTarget section */
|
||||
BD43C61D2D1B23AB003BBC42 /* Ryujinx */ = {
|
||||
isa = PBXLegacyTarget;
|
||||
buildArgumentsString = "publish -c Release -r ios-arm64 -p:ExtraDefineConstants=DISABLE_UPDATER src/Ryujinx.Headless.SDL2 --self-contained true";
|
||||
buildArgumentsString = "./distribution/ios/xc-compile.sh";
|
||||
buildConfigurationList = BD43C61E2D1B23AB003BBC42 /* Build configuration list for PBXLegacyTarget "Ryujinx" */;
|
||||
buildPhases = (
|
||||
);
|
||||
buildToolPath = "$(DOTNET)";
|
||||
buildWorkingDirectory = "$(SRCROOT)/../..";
|
||||
buildToolPath = /bin/sh;
|
||||
buildWorkingDirectory = "$(SRCROOT)/../../";
|
||||
dependencies = (
|
||||
);
|
||||
name = Ryujinx;
|
||||
|
@ -287,7 +292,7 @@
|
|||
buildRules = (
|
||||
);
|
||||
dependencies = (
|
||||
4E2953AC2D803BC9000497CD /* PBXTargetDependency */,
|
||||
4E59B0A42DEA5CA9004BFF2A /* PBXTargetDependency */,
|
||||
);
|
||||
fileSystemSynchronizedGroups = (
|
||||
4E80A98F2CD6F54500029585 /* MeloNX */,
|
||||
|
@ -473,11 +478,10 @@
|
|||
/* End PBXSourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXTargetDependency section */
|
||||
4E2953AC2D803BC9000497CD /* PBXTargetDependency */ = {
|
||||
4E59B0A42DEA5CA9004BFF2A /* PBXTargetDependency */ = {
|
||||
isa = PBXTargetDependency;
|
||||
platformFilter = ios;
|
||||
target = BD43C6212D1B248D003BBC42 /* com.Stossy11.MeloNX.RyujinxAg */;
|
||||
targetProxy = 4E2953AB2D803BC9000497CD /* PBXContainerItemProxy */;
|
||||
targetProxy = 4E59B0A32DEA5CA9004BFF2A /* PBXContainerItemProxy */;
|
||||
};
|
||||
4E80A99F2CD6F54700029585 /* PBXTargetDependency */ = {
|
||||
isa = PBXTargetDependency;
|
||||
|
@ -562,6 +566,7 @@
|
|||
ONLY_ACTIVE_ARCH = NO;
|
||||
SDKROOT = iphoneos;
|
||||
SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)";
|
||||
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||
SWIFT_ENFORCE_EXCLUSIVE_ACCESS = "debug-only";
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||
};
|
||||
|
@ -628,6 +633,7 @@
|
|||
SDKROOT = iphoneos;
|
||||
SWIFT_COMPILATION_MODE = wholemodule;
|
||||
SWIFT_DISABLE_SAFETY_CHECKS = YES;
|
||||
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||
SWIFT_ENFORCE_EXCLUSIVE_ACCESS = "debug-only";
|
||||
VALIDATE_PRODUCT = YES;
|
||||
};
|
||||
|
@ -643,7 +649,7 @@
|
|||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
DEVELOPMENT_ASSET_PATHS = "";
|
||||
DEVELOPMENT_TEAM = "";
|
||||
DEVELOPMENT_TEAM = 95J8WZ4TN8;
|
||||
ENABLE_PREVIEWS = YES;
|
||||
ENABLE_TESTABILITY = NO;
|
||||
FRAMEWORK_SEARCH_PATHS = (
|
||||
|
@ -730,6 +736,36 @@
|
|||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||
);
|
||||
GCC_OPTIMIZATION_LEVEL = z;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
|
@ -742,7 +778,7 @@
|
|||
INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
|
||||
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
|
||||
INFOPLIST_KEY_UILaunchScreen_Generation = YES;
|
||||
INFOPLIST_KEY_UIRequiresFullScreen = YES;
|
||||
INFOPLIST_KEY_UIRequiresFullScreen = NO;
|
||||
INFOPLIST_KEY_UISupportedInterfaceOrientations = "UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown";
|
||||
INFOPLIST_KEY_UISupportsDocumentBrowser = YES;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 15.6;
|
||||
|
@ -916,6 +952,51 @@
|
|||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||
);
|
||||
MARKETING_VERSION = "$(VERSION)";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.stossy11.MeloNX;
|
||||
|
@ -1026,6 +1107,36 @@
|
|||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||
);
|
||||
GCC_OPTIMIZATION_LEVEL = z;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
|
@ -1038,7 +1149,7 @@
|
|||
INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
|
||||
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
|
||||
INFOPLIST_KEY_UILaunchScreen_Generation = YES;
|
||||
INFOPLIST_KEY_UIRequiresFullScreen = YES;
|
||||
INFOPLIST_KEY_UIRequiresFullScreen = NO;
|
||||
INFOPLIST_KEY_UISupportedInterfaceOrientations = "UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown";
|
||||
INFOPLIST_KEY_UISupportsDocumentBrowser = YES;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 15.6;
|
||||
|
@ -1212,6 +1323,51 @@
|
|||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||
);
|
||||
MARKETING_VERSION = "$(VERSION)";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.stossy11.MeloNX;
|
||||
|
|
Binary file not shown.
|
@ -17,11 +17,20 @@ func SecTaskCopyValueForEntitlement(
|
|||
_ error: NSErrorPointer
|
||||
) -> CFTypeRef?
|
||||
|
||||
@_silgen_name("SecTaskCopyTeamIdentifier")
|
||||
func SecTaskCopyTeamIdentifier(
|
||||
_ task: SecTaskRef,
|
||||
_ error: NSErrorPointer
|
||||
) -> NSString?
|
||||
|
||||
@_silgen_name("SecTaskCreateFromSelf")
|
||||
func SecTaskCreateFromSelf(
|
||||
_ allocator: CFAllocator?
|
||||
) -> SecTaskRef?
|
||||
|
||||
@_silgen_name("CFRelease")
|
||||
func CFRelease(_ cf: CFTypeRef)
|
||||
|
||||
@_silgen_name("SecTaskCopyValuesForEntitlements")
|
||||
func SecTaskCopyValuesForEntitlements(
|
||||
_ task: SecTaskRef,
|
||||
|
@ -29,30 +38,43 @@ func SecTaskCopyValuesForEntitlements(
|
|||
_ error: UnsafeMutablePointer<Unmanaged<CFError>?>?
|
||||
) -> CFDictionary?
|
||||
|
||||
func releaseSecTask(_ task: SecTaskRef) {
|
||||
let cf = unsafeBitCast(task, to: CFTypeRef.self)
|
||||
CFRelease(cf)
|
||||
}
|
||||
|
||||
func checkAppEntitlements(_ ents: [String]) -> [String: Any] {
|
||||
guard let task = SecTaskCreateFromSelf(nil) else {
|
||||
// print("Failed to create SecTask")
|
||||
return [:]
|
||||
}
|
||||
defer {
|
||||
releaseSecTask(task)
|
||||
}
|
||||
|
||||
guard let entitlements = SecTaskCopyValuesForEntitlements(task, ents as CFArray, nil) else {
|
||||
// print("Failed to get entitlements")
|
||||
return [:]
|
||||
}
|
||||
|
||||
return (entitlements as? [String: Any]) ?? [:]
|
||||
return (entitlements as NSDictionary) as? [String: Any] ?? [:]
|
||||
}
|
||||
|
||||
func checkAppEntitlement(_ ent: String) -> Bool {
|
||||
guard let task = SecTaskCreateFromSelf(nil) else {
|
||||
// print("Failed to create SecTask")
|
||||
return false
|
||||
}
|
||||
defer {
|
||||
releaseSecTask(task)
|
||||
}
|
||||
|
||||
guard let entitlement = SecTaskCopyValueForEntitlement(task, ent as NSString, nil) else {
|
||||
return false
|
||||
}
|
||||
|
||||
guard let entitlements = SecTaskCopyValueForEntitlement(task, ent as NSString, nil) else {
|
||||
// print("Failed to get entitlements")
|
||||
return false
|
||||
if let number = entitlement as? NSNumber {
|
||||
return number.boolValue
|
||||
} else if let bool = entitlement as? Bool {
|
||||
return bool
|
||||
}
|
||||
|
||||
return entitlements.boolValue != nil && entitlements.boolValue
|
||||
return false
|
||||
}
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
|
||||
#include <SDL2/SDL.h>
|
||||
#include <SDL2/SDL_syswm.h>
|
||||
#include <StosJIT/StosJIT-Swift.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
|
|
|
@ -20,6 +20,14 @@ func isJITEnabled() -> Bool {
|
|||
return csops(pid: getpid(), ops: 0, useraddr: &flags, usersize: Int32(MemoryLayout.size(ofValue: flags))) == 0 && (flags & Int(CS_DEBUGGED)) != 0 ? allocateTest() : false
|
||||
}
|
||||
|
||||
func checkDebugged() -> Bool {
|
||||
var flags: Int = 0
|
||||
if checkAppEntitlement("dynamic-codesigning") {
|
||||
return true
|
||||
}
|
||||
return csops(pid: getpid(), ops: 0, useraddr: &flags, usersize: Int32(MemoryLayout.size(ofValue: flags))) == 0 && (flags & Int(CS_DEBUGGED)) != 0
|
||||
}
|
||||
|
||||
func checkMemoryPermissions(at address: UnsafeRawPointer) -> Bool {
|
||||
var region: vm_address_t = vm_address_t(UInt(bitPattern: address))
|
||||
var regionSize: vm_size_t = 0
|
||||
|
@ -54,6 +62,7 @@ func allocateTest() -> Bool {
|
|||
|
||||
memcpy(jitMemory, code, code.count)
|
||||
|
||||
|
||||
if mprotect(jitMemory, pageSize, PROT_READ | PROT_EXEC) != 0 {
|
||||
return false
|
||||
}
|
||||
|
|
|
@ -118,6 +118,7 @@ func presentAlert(title: String, message: String, completion: (() -> Void)? = ni
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
struct LaunchApp: Codable {
|
||||
let success: Bool
|
||||
let message: String
|
||||
|
|
|
@ -9,11 +9,55 @@ import Foundation
|
|||
import Network
|
||||
import UIKit
|
||||
|
||||
func enableJITStik() {
|
||||
let bundleid = Bundle.main.bundleIdentifier ?? "Unknown"
|
||||
|
||||
let address = URL(string: "stikjit://enable-jit?bundle-id=\(bundleid)")!
|
||||
if UIApplication.shared.canOpenURL(address) {
|
||||
UIApplication.shared.open(address)
|
||||
|
||||
func stikJITorStikDebug() -> Int {
|
||||
let teamid = SecTaskCopyTeamIdentifier(SecTaskCreateFromSelf(nil)!, nil)
|
||||
|
||||
if checkifappinstalled("com.stik.sj") {
|
||||
return 1 // StikDebug
|
||||
}
|
||||
|
||||
if checkifappinstalled("com.stik.sj.\(String(teamid ?? ""))") {
|
||||
return 2 // StikJIT
|
||||
}
|
||||
|
||||
return 0 // Not Found
|
||||
}
|
||||
|
||||
|
||||
func checkifappinstalled(_ id: String) -> Bool {
|
||||
|
||||
guard let handle = dlopen("/System/Library/PrivateFrameworks/SpringBoardServices.framework/SpringBoardServices", RTLD_LAZY) else {
|
||||
if let error = dlerror() {
|
||||
print(String(cString: error))
|
||||
}
|
||||
return false
|
||||
// fatalError("Failed to open dylib")
|
||||
}
|
||||
|
||||
typealias SBSLaunchApplicationWithIdentifierFunc = @convention(c) (CFString, Bool) -> Int32
|
||||
guard let sym = dlsym(handle, "SBSLaunchApplicationWithIdentifier") else {
|
||||
if let error = dlerror() {
|
||||
print(String(cString: error))
|
||||
}
|
||||
dlclose(handle)
|
||||
return false
|
||||
}
|
||||
|
||||
let bundleID: CFString = id as CFString
|
||||
let suspended: Bool = false
|
||||
|
||||
|
||||
let SBSLaunchApplicationWithIdentifier = unsafeBitCast(sym, to: SBSLaunchApplicationWithIdentifierFunc.self)
|
||||
let result = SBSLaunchApplicationWithIdentifier(bundleID, suspended)
|
||||
|
||||
return result == 9
|
||||
}
|
||||
|
||||
func enableJITStik() {
|
||||
let urlScheme = "stikjit://enable-jit?bundle-id=\(Bundle.main.bundleIdentifier ?? "wow")"
|
||||
if let launchURL = URL(string: urlScheme), !isJITEnabled() {
|
||||
UIApplication.shared.open(launchURL, options: [:], completionHandler: nil)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,7 +13,7 @@ class MemoryUsageMonitor: ObservableObject {
|
|||
private var timer: Timer?
|
||||
|
||||
init() {
|
||||
timer = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { [weak self] _ in
|
||||
timer = Timer.scheduledTimer(withTimeInterval: 0.2, repeats: true) { [weak self] _ in
|
||||
self?.updateMemoryUsage()
|
||||
}
|
||||
}
|
||||
|
@ -32,6 +32,7 @@ class MemoryUsageMonitor: ObservableObject {
|
|||
}
|
||||
|
||||
if result == KERN_SUCCESS {
|
||||
memoryUsage = 0
|
||||
memoryUsage = taskInfo.phys_footprint
|
||||
}
|
||||
else {
|
||||
|
@ -46,7 +47,6 @@ class MemoryUsageMonitor: ObservableObject {
|
|||
formatter.countStyle = .memory
|
||||
return formatter.string(fromByteCount: Int64(bytes))
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -1,22 +0,0 @@
|
|||
//
|
||||
// Untitled.swift
|
||||
// MeloNX
|
||||
//
|
||||
// Created by Stossy11 on 21/12/2024.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
struct PerformanceOverlayView: View {
|
||||
@StateObject private var memorymonitor = MemoryUsageMonitor()
|
||||
|
||||
@StateObject private var fpsmonitor = FPSMonitor()
|
||||
|
||||
var body: some View {
|
||||
VStack {
|
||||
Text("\(fpsmonitor.formatFPS())")
|
||||
Text(memorymonitor.formatMemorySize(memorymonitor.memoryUsage))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -6,34 +6,28 @@
|
|||
//
|
||||
|
||||
import Foundation
|
||||
import SwiftUI
|
||||
|
||||
|
||||
class MTLHud {
|
||||
|
||||
class MTLHud: ObservableObject {
|
||||
@Published var canMetalHud: Bool = false
|
||||
|
||||
var isEnabled: Bool {
|
||||
if let getenv = getenv("MTL_HUD_ENABLED") {
|
||||
return String(cString: getenv).contains("1")
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
static let shared = MTLHud()
|
||||
|
||||
private init() {
|
||||
let _ = openMetalDylib() // i'm fixing the warnings just because you said i suck at coding Autumn (propenchiefer,
|
||||
https://youtu.be/tc65SNOTMz4 7:23)
|
||||
if UserDefaults.standard.bool(forKey: "MTL_HUD_ENABLED") {
|
||||
@AppStorage("MTL_HUD_ENABLED") var metalHudEnabled: Bool = false {
|
||||
didSet {
|
||||
if metalHudEnabled {
|
||||
enable()
|
||||
} else {
|
||||
disable()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func toggle() {
|
||||
// print(UserDefaults.standard.bool(forKey: "MTL_HUD_ENABLED"))
|
||||
if UserDefaults.standard.bool(forKey: "MTL_HUD_ENABLED") {
|
||||
|
||||
static let shared = MTLHud()
|
||||
|
||||
private init() {
|
||||
canMetalHud = openMetalDylib() // i'm fixing the warnings just because you said i suck at coding Autumn (propenchiefer, https://youtu.be/tc65SNOTMz4 7:23)
|
||||
|
||||
if metalHudEnabled {
|
||||
enable()
|
||||
} else {
|
||||
disable()
|
||||
|
@ -44,14 +38,8 @@ class MTLHud {
|
|||
let path = "/usr/lib/libMTLHud.dylib"
|
||||
|
||||
if dlopen(path, RTLD_NOW) != nil {
|
||||
// print("Library loaded from \(path)")
|
||||
canMetalHud = true
|
||||
return true
|
||||
} else {
|
||||
if let error = String(validatingUTF8: dlerror()) {
|
||||
// print("Error loading library: \(error)")
|
||||
}
|
||||
canMetalHud = false
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,6 +10,7 @@ import SwiftUI
|
|||
import GameController
|
||||
import MetalKit
|
||||
import Metal
|
||||
import Darwin
|
||||
|
||||
class LogCapture {
|
||||
static let shared = LogCapture()
|
||||
|
@ -117,6 +118,11 @@ struct iOSNav<Content: View>: View {
|
|||
}
|
||||
}
|
||||
|
||||
func threadEntry(_ arg: () -> Void) -> UnsafeMutableRawPointer? {
|
||||
arg()
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
class Ryujinx : ObservableObject {
|
||||
@Published var isRunning = false
|
||||
|
@ -128,12 +134,12 @@ class Ryujinx : ObservableObject {
|
|||
@Published var isPortrait = false
|
||||
@Published var firmwareversion = "0"
|
||||
@Published var emulationUIView: MeloMTKView? = nil
|
||||
@Published var config: Ryujinx.Configuration? = nil
|
||||
@Published var config: Ryujinx.Arguments? = nil
|
||||
@Published var games: [Game] = []
|
||||
|
||||
@Published var defMLContentSize: CGFloat?
|
||||
|
||||
var thread: Thread = Thread { }
|
||||
var thread: pthread_t? = nil
|
||||
|
||||
@Published var jitenabled = false
|
||||
|
||||
|
@ -143,7 +149,7 @@ class Ryujinx : ObservableObject {
|
|||
|
||||
static let shared = Ryujinx()
|
||||
|
||||
private init() {
|
||||
func addGames() {
|
||||
self.games = loadGames()
|
||||
}
|
||||
|
||||
|
@ -153,45 +159,63 @@ class Ryujinx : ObservableObject {
|
|||
cool()
|
||||
}
|
||||
} else {
|
||||
thread = Thread {
|
||||
cool()
|
||||
}
|
||||
// Box the closure
|
||||
let boxed = Unmanaged.passRetained(ClosureBox(cool)).toOpaque()
|
||||
|
||||
thread.qualityOfService = .userInteractive
|
||||
thread.name = "MeloNX"
|
||||
thread.start()
|
||||
var thread: pthread_t?
|
||||
let result = pthread_create(&thread, nil, { arg in
|
||||
let unmanaged = Unmanaged<ClosureBox>.fromOpaque(arg)
|
||||
let box = unmanaged.takeRetainedValue()
|
||||
box.closure()
|
||||
return nil
|
||||
}, boxed)
|
||||
|
||||
if result == 0 {
|
||||
pthread_detach(thread!)
|
||||
} else {
|
||||
print("Failed to create thread: \(result)")
|
||||
Unmanaged<ClosureBox>.fromOpaque(boxed).release()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public struct Configuration : Codable, Equatable {
|
||||
private class ClosureBox {
|
||||
let closure: () -> Void
|
||||
init(_ closure: @escaping () -> Void) {
|
||||
self.closure = closure
|
||||
}
|
||||
}
|
||||
|
||||
public class Arguments : Observable, Codable, Equatable {
|
||||
var gamepath: String
|
||||
var inputids: [String]
|
||||
var resscale: Float
|
||||
var debuglogs: Bool
|
||||
var tracelogs: Bool
|
||||
var nintendoinput: Bool
|
||||
var enableInternet: Bool
|
||||
var listinputids: Bool
|
||||
var aspectRatio: AspectRatio
|
||||
var memoryManagerMode: String
|
||||
var disableShaderCache: Bool
|
||||
var hypervisor: Bool
|
||||
var disableDockedMode: Bool
|
||||
var enableTextureRecompression: Bool
|
||||
var additionalArgs: [String]
|
||||
var maxAnisotropy: Float
|
||||
var macroHLE: Bool
|
||||
var ignoreMissingServices: Bool
|
||||
var expandRam: Bool
|
||||
var dfsIntegrityChecks: Bool
|
||||
var disablePTC: Bool
|
||||
var disablevsync: Bool
|
||||
var language: SystemLanguage
|
||||
var regioncode: SystemRegionCode
|
||||
var handHeldController: Bool
|
||||
var resscale: Float = 1.0
|
||||
var debuglogs: Bool = false
|
||||
var tracelogs: Bool = false
|
||||
var nintendoinput: Bool = true
|
||||
var enableInternet: Bool = false
|
||||
var listinputids: Bool = false
|
||||
var aspectRatio: AspectRatio = .fixed16x9
|
||||
var memoryManagerMode: String = "HostMappedUnsafe"
|
||||
var disableShaderCache: Bool = false
|
||||
var hypervisor: Bool = false
|
||||
var disableDockedMode: Bool = false
|
||||
var enableTextureRecompression: Bool = true
|
||||
var additionalArgs: [String] = []
|
||||
var maxAnisotropy: Float = 1.0
|
||||
var macroHLE: Bool = true
|
||||
var ignoreMissingServices: Bool = false
|
||||
var expandRam: Bool = false
|
||||
var dfsIntegrityChecks: Bool = false
|
||||
var disablePTC: Bool = false
|
||||
var disablevsync: Bool = false
|
||||
var language: SystemLanguage = .americanEnglish
|
||||
var regioncode: SystemRegionCode = .usa
|
||||
var handHeldController: Bool = true
|
||||
var backendMultithreading: Bool = true
|
||||
|
||||
|
||||
init(gamepath: String,
|
||||
init(gamepath: String = "",
|
||||
inputids: [String] = [],
|
||||
debuglogs: Bool = false,
|
||||
tracelogs: Bool = false,
|
||||
|
@ -215,7 +239,8 @@ class Ryujinx : ObservableObject {
|
|||
disablevsync: Bool = false,
|
||||
language: SystemLanguage = .americanEnglish,
|
||||
regioncode: SystemRegionCode = .usa,
|
||||
handHeldController: Bool = false
|
||||
handHeldController: Bool = false,
|
||||
backendMultithreading: Bool = true
|
||||
) {
|
||||
self.gamepath = gamepath
|
||||
self.inputids = inputids
|
||||
|
@ -242,11 +267,39 @@ class Ryujinx : ObservableObject {
|
|||
self.language = language
|
||||
self.regioncode = regioncode
|
||||
self.handHeldController = handHeldController
|
||||
self.backendMultithreading = backendMultithreading
|
||||
}
|
||||
|
||||
|
||||
static func == (lhs: Arguments, rhs: Arguments) -> Bool {
|
||||
return lhs.resscale == rhs.resscale &&
|
||||
lhs.debuglogs == rhs.debuglogs &&
|
||||
lhs.tracelogs == rhs.tracelogs &&
|
||||
lhs.nintendoinput == rhs.nintendoinput &&
|
||||
lhs.enableInternet == rhs.enableInternet &&
|
||||
lhs.listinputids == rhs.listinputids &&
|
||||
lhs.aspectRatio == rhs.aspectRatio &&
|
||||
lhs.memoryManagerMode == rhs.memoryManagerMode &&
|
||||
lhs.disableShaderCache == rhs.disableShaderCache &&
|
||||
lhs.hypervisor == rhs.hypervisor &&
|
||||
lhs.disableDockedMode == rhs.disableDockedMode &&
|
||||
lhs.enableTextureRecompression == rhs.enableTextureRecompression &&
|
||||
lhs.additionalArgs == rhs.additionalArgs &&
|
||||
lhs.maxAnisotropy == rhs.maxAnisotropy &&
|
||||
lhs.macroHLE == rhs.macroHLE &&
|
||||
lhs.ignoreMissingServices == rhs.ignoreMissingServices &&
|
||||
lhs.expandRam == rhs.expandRam &&
|
||||
lhs.dfsIntegrityChecks == rhs.dfsIntegrityChecks &&
|
||||
lhs.disablePTC == rhs.disablePTC &&
|
||||
lhs.disablevsync == rhs.disablevsync &&
|
||||
lhs.language == rhs.language &&
|
||||
lhs.regioncode == rhs.regioncode &&
|
||||
lhs.handHeldController == rhs.handHeldController
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
func start(with config: Configuration) throws {
|
||||
func start(with config: Arguments) throws {
|
||||
guard !isRunning else {
|
||||
throw RyujinxError.alreadyRunning
|
||||
}
|
||||
|
@ -254,6 +307,32 @@ class Ryujinx : ObservableObject {
|
|||
self.config = config
|
||||
|
||||
|
||||
if UserDefaults.standard.bool(forKey: "lockInApp") {
|
||||
let cool = Thread {
|
||||
while true {
|
||||
if UserDefaults.standard.bool(forKey: "lockInApp") {
|
||||
if let workspaceClass = NSClassFromString("LSApplicationWorkspace") as? NSObject.Type,
|
||||
let workspace = workspaceClass.perform(NSSelectorFromString("defaultWorkspace"))?.takeUnretainedValue() {
|
||||
|
||||
let selector = NSSelectorFromString("openApplicationWithBundleID:")
|
||||
|
||||
if workspace.responds(to: selector) {
|
||||
workspace.perform(selector, with: Bundle.main.bundleIdentifier ?? "")
|
||||
} else {
|
||||
print("Selector not found or not responding.")
|
||||
}
|
||||
} else {
|
||||
print("Could not get LSApplicationWorkspace class.")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cool.qualityOfService = .userInteractive
|
||||
cool.start()
|
||||
}
|
||||
|
||||
|
||||
runloop { [self] in
|
||||
|
||||
isRunning = true
|
||||
|
@ -380,10 +459,12 @@ class Ryujinx : ObservableObject {
|
|||
|
||||
isRunning = false
|
||||
|
||||
UserDefaults.standard.set(false, forKey: "lockInApp")
|
||||
|
||||
self.emulationUIView = nil
|
||||
self.metalLayer = nil
|
||||
|
||||
thread.cancel()
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
@ -431,10 +512,9 @@ class Ryujinx : ObservableObject {
|
|||
// print("Error loading games from roms folder: \(error)")
|
||||
return games
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private func buildCommandLineArgs(from config: Configuration) -> [String] {
|
||||
private func buildCommandLineArgs(from config: Arguments) -> [String] {
|
||||
var args: [String] = []
|
||||
|
||||
// Add the game path
|
||||
|
@ -476,6 +556,14 @@ class Ryujinx : ObservableObject {
|
|||
|
||||
args.append(contentsOf: ["--aspect-ratio", config.aspectRatio.rawValue])
|
||||
|
||||
// args.append(contentsOf: ["--system-timezone", TimeZone.current.identifier])
|
||||
|
||||
args.append(contentsOf: ["--system-time-offset", String(TimeZone.current.secondsFromGMT())])
|
||||
|
||||
if !config.backendMultithreading {
|
||||
args.append(contentsOf: ["--backend-multithreading", "Off"])
|
||||
}
|
||||
|
||||
|
||||
if config.nintendoinput {
|
||||
args.append("--correct-controller")
|
||||
|
|
|
@ -45,26 +45,28 @@ struct ControllerView: View {
|
|||
HStack(spacing: 30) {
|
||||
VStack(spacing: 15) {
|
||||
ShoulderButtonsViewLeft()
|
||||
.padding(.vertical)
|
||||
ZStack {
|
||||
JoystickController(showBackground: $hideDpad)
|
||||
if !hideDpad {
|
||||
DPadView()
|
||||
.opacity(hideDpad ? 0 : 1)
|
||||
.allowsHitTesting(!hideDpad)
|
||||
.animation(.easeInOut(duration: 0.2), value: hideDpad)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
VStack(spacing: 15) {
|
||||
ShoulderButtonsViewRight()
|
||||
.padding(.vertical)
|
||||
ZStack {
|
||||
JoystickController(iscool: true, showBackground: $hideABXY)
|
||||
if !hideABXY {
|
||||
ABXYView()
|
||||
.opacity(hideABXY ? 0 : 1)
|
||||
.allowsHitTesting(!hideABXY)
|
||||
.animation(.easeInOut(duration: 0.2), value: hideABXY)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
HStack(spacing: 60) {
|
||||
HStack {
|
||||
|
@ -90,14 +92,15 @@ struct ControllerView: View {
|
|||
HStack {
|
||||
VStack(spacing: 20) {
|
||||
ShoulderButtonsViewLeft()
|
||||
.padding(.vertical)
|
||||
ZStack {
|
||||
JoystickController(showBackground: $hideDpad)
|
||||
if !hideDpad {
|
||||
DPadView()
|
||||
.opacity(hideDpad ? 0 : 1)
|
||||
.allowsHitTesting(!hideDpad)
|
||||
.animation(.easeInOut(duration: 0.2), value: hideDpad)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Spacer()
|
||||
|
||||
|
@ -107,17 +110,18 @@ struct ControllerView: View {
|
|||
|
||||
VStack(spacing: 20) {
|
||||
ShoulderButtonsViewRight()
|
||||
.padding(.vertical)
|
||||
ZStack {
|
||||
JoystickController(iscool: true, showBackground: $hideABXY)
|
||||
if !hideABXY {
|
||||
ABXYView()
|
||||
.opacity(hideABXY ? 0 : 1)
|
||||
.allowsHitTesting(!hideABXY)
|
||||
.animation(.easeInOut(duration: 0.2), value: hideABXY)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private var centerButtons: some View {
|
||||
Group {
|
||||
|
@ -282,7 +286,7 @@ struct ButtonView: View {
|
|||
.scaledToFit()
|
||||
.frame(width: size.width, height: size.height)
|
||||
.foregroundStyle(.white)
|
||||
.opacity(isPressed ? 0.6 : 1.0)
|
||||
.opacity(isPressed ? 0.6 : 0.8)
|
||||
.allowsHitTesting(false)
|
||||
}
|
||||
.frame(width: size.width, height: size.height)
|
||||
|
|
|
@ -9,6 +9,8 @@
|
|||
import SwiftUI
|
||||
|
||||
struct Joystick: View {
|
||||
@AppStorage("On-ScreenControllerScale") var controllerScale: Double = 1.0
|
||||
|
||||
@Binding var position: CGPoint
|
||||
@State var joystickSize: CGFloat
|
||||
var boundarySize: CGFloat
|
||||
|
@ -18,6 +20,7 @@ struct Joystick: View {
|
|||
|
||||
let sensitivity: CGFloat = 1.5
|
||||
|
||||
|
||||
var dragGesture: some Gesture {
|
||||
DragGesture()
|
||||
.onChanged { value in
|
||||
|
@ -56,12 +59,14 @@ struct Joystick: View {
|
|||
Circle()
|
||||
.fill(Color.clear.opacity(0))
|
||||
.frame(width: boundarySize, height: boundarySize)
|
||||
.scaleEffect(controllerScale)
|
||||
|
||||
if showBackground {
|
||||
Circle()
|
||||
.fill(Color.gray.opacity(0.4))
|
||||
.frame(width: boundarySize, height: boundarySize)
|
||||
.animation(.easeInOut(duration: 0.1), value: showBackground)
|
||||
.scaleEffect(controllerScale)
|
||||
}
|
||||
|
||||
Circle()
|
||||
|
@ -74,6 +79,7 @@ struct Joystick: View {
|
|||
)
|
||||
.offset(offset)
|
||||
.gesture(dragGesture)
|
||||
.scaleEffect(controllerScale)
|
||||
}
|
||||
.frame(width: boundarySize, height: boundarySize)
|
||||
.onChange(of: showBackground) { newValue in
|
||||
|
|
|
@ -16,7 +16,7 @@ struct JoystickController: View {
|
|||
@State var position: CGPoint = CGPoint(x: 0, y: 0)
|
||||
var dragDiameter: CGFloat {
|
||||
var selfs = CGFloat(160)
|
||||
selfs *= controllerScale
|
||||
// selfs *= controllerScale
|
||||
if UIDevice.current.systemName.contains("iPadOS") {
|
||||
return selfs * 1.2
|
||||
}
|
||||
|
@ -28,9 +28,6 @@ struct JoystickController: View {
|
|||
VStack {
|
||||
Joystick(position: $position, joystickSize: dragDiameter * 0.2, boundarySize: dragDiameter, showBackground: $showBackground)
|
||||
.onChange(of: position) { newValue in
|
||||
let scaledX = Float(newValue.x)
|
||||
let scaledY = Float(newValue.y) // my dumbass broke this by having -y instead of y :/
|
||||
// print("Joystick Position: (\(scaledX), \(scaledY))")
|
||||
|
||||
if iscool != nil {
|
||||
Ryujinx.shared.virtualController.thumbstickMoved(.right, x: newValue.x, y: newValue.y)
|
||||
|
|
|
@ -14,6 +14,10 @@ struct EmulationView: View {
|
|||
@AppStorage("showScreenShotButton") var ssb: Bool = false
|
||||
@AppStorage("showlogsgame") var showlogsgame: Bool = false
|
||||
|
||||
@AppStorage("On-ScreenControllerOpacity") var controllerOpacity: Double = 1.0
|
||||
|
||||
@AppStorage("disableTouch") var blackScreen = false
|
||||
|
||||
@State var isPresentedThree: Bool = false
|
||||
@State var isAirplaying = Air.shared.connected
|
||||
@Binding var startgame: Game?
|
||||
|
@ -31,6 +35,11 @@ struct EmulationView: View {
|
|||
.onAppear {
|
||||
Air.play(AnyView(MetalView().ignoresSafeArea().edgesIgnoringSafeArea(.all)))
|
||||
}
|
||||
|
||||
Color.black
|
||||
.ignoresSafeArea()
|
||||
.edgesIgnoringSafeArea(.all)
|
||||
.allowsHitTesting(false)
|
||||
} else {
|
||||
MetalView() // The Emulation View
|
||||
.ignoresSafeArea()
|
||||
|
@ -41,6 +50,8 @@ struct EmulationView: View {
|
|||
|
||||
if isVCA {
|
||||
ControllerView() // Virtual Controller
|
||||
.opacity(controllerOpacity)
|
||||
.allowsHitTesting(true)
|
||||
}
|
||||
|
||||
Group {
|
||||
|
|
|
@ -0,0 +1,70 @@
|
|||
//
|
||||
// Untitled.swift
|
||||
// MeloNX
|
||||
//
|
||||
// Created by Stossy11 on 21/12/2024.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
struct PerformanceOverlayView: View {
|
||||
@StateObject private var memorymonitor = MemoryUsageMonitor()
|
||||
|
||||
@StateObject private var fpsmonitor = FPSMonitor()
|
||||
|
||||
var body: some View {
|
||||
VStack {
|
||||
Text("\(fpsmonitor.formatFPS())")
|
||||
.foregroundStyle(.white)
|
||||
.stroke(color: .black, width: 2)
|
||||
Text(memorymonitor.formatMemorySize(memorymonitor.memoryUsage))
|
||||
.foregroundStyle(.white)
|
||||
.stroke(color: .black, width: 2)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension View {
|
||||
func stroke(color: Color, width: CGFloat = 1) -> some View {
|
||||
modifier(StrokeModifier(strokeSize: width, strokeColor: color))
|
||||
}
|
||||
}
|
||||
|
||||
struct StrokeModifier: ViewModifier {
|
||||
private let id = UUID()
|
||||
var strokeSize: CGFloat = 1
|
||||
var strokeColor: Color = .blue
|
||||
|
||||
func body(content: Content) -> some View {
|
||||
if strokeSize > 0 {
|
||||
appliedStrokeBackground(content: content)
|
||||
} else {
|
||||
content
|
||||
}
|
||||
}
|
||||
|
||||
private func appliedStrokeBackground(content: Content) -> some View {
|
||||
content
|
||||
.padding(strokeSize*2)
|
||||
.background(
|
||||
Rectangle()
|
||||
.foregroundColor(strokeColor)
|
||||
.mask(alignment: .center) {
|
||||
mask(content: content)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
func mask(content: Content) -> some View {
|
||||
Canvas { context, size in
|
||||
context.addFilter(.alphaThreshold(min: 0.01))
|
||||
if let resolvedView = context.resolveSymbol(id: id) {
|
||||
context.draw(resolvedView, at: .init(x: size.width/2, y: size.height/2))
|
||||
}
|
||||
} symbols: {
|
||||
content
|
||||
.tag(id)
|
||||
.blur(radius: strokeSize)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -32,11 +32,16 @@ struct ContentView: View {
|
|||
@AppStorage("isVirtualController") var isVCA: Bool = true
|
||||
|
||||
// Settings and Configuration
|
||||
@State private var config: Ryujinx.Configuration
|
||||
private var config: Ryujinx.Arguments {
|
||||
settingsManager.config
|
||||
}
|
||||
|
||||
@StateObject private var settingsManager = SettingsManager.shared
|
||||
|
||||
@State var settings: [MoltenVKSettings]
|
||||
@AppStorage("useTrollStore") var useTrollStore: Bool = false
|
||||
|
||||
// JIT
|
||||
@AppStorage("useTrollStore") var useTrollStore: Bool = false
|
||||
@AppStorage("jitStreamerEB") var jitStreamerEB: Bool = false
|
||||
@AppStorage("stikJIT") var stikJIT: Bool = false
|
||||
|
||||
|
@ -55,6 +60,9 @@ struct ContentView: View {
|
|||
private let animationDuration: Double = 1.0
|
||||
@State private var isAnimating = false
|
||||
@State var isLoading = true
|
||||
|
||||
|
||||
// MARK: - CORE
|
||||
@StateObject var ryujinx = Ryujinx.shared
|
||||
|
||||
// MARK: - SDL
|
||||
|
@ -62,14 +70,6 @@ struct ContentView: View {
|
|||
|
||||
// MARK: - Initialization
|
||||
init() {
|
||||
var defaultConfig = loadSettings()
|
||||
if defaultConfig == nil {
|
||||
saveSettings(config: .init(gamepath: ""))
|
||||
defaultConfig = loadSettings()
|
||||
}
|
||||
|
||||
_config = State(initialValue: defaultConfig!)
|
||||
|
||||
let defaultSettings: [MoltenVKSettings] = [
|
||||
MoltenVKSettings(string: "MVK_USE_METAL_PRIVATE_API", value: "1"),
|
||||
MoltenVKSettings(string: "MVK_CONFIG_USE_METAL_PRIVATE_API", value: "1"),
|
||||
|
@ -139,7 +139,6 @@ struct ContentView: View {
|
|||
private var mainMenuView: some View {
|
||||
MainTabView(
|
||||
startemu: $game,
|
||||
config: $config,
|
||||
MVKconfig: $settings,
|
||||
controllersList: $controllersList,
|
||||
currentControllers: $currentControllers,
|
||||
|
@ -155,6 +154,8 @@ struct ContentView: View {
|
|||
}
|
||||
|
||||
|
||||
UserDefaults.standard.set(false, forKey: "lockInApp")
|
||||
|
||||
// print(MTLHud.shared.isEnabled)
|
||||
|
||||
initControllerObservers()
|
||||
|
@ -163,6 +164,11 @@ struct ContentView: View {
|
|||
ControllerListView(game: $game)
|
||||
))
|
||||
|
||||
refreshControllersList()
|
||||
|
||||
|
||||
ryujinx.addGames()
|
||||
|
||||
checkJitStatus()
|
||||
}
|
||||
.onOpenURL { url in
|
||||
|
@ -373,6 +379,8 @@ struct ContentView: View {
|
|||
if jitStreamerEB {
|
||||
jitStreamerEB = false // byee jitstreamer eb
|
||||
}
|
||||
|
||||
|
||||
if !ryujinx.jitenabled {
|
||||
if useTrollStore {
|
||||
askForJIT()
|
||||
|
@ -381,7 +389,12 @@ struct ContentView: View {
|
|||
} else if jitStreamerEB {
|
||||
enableJITEB()
|
||||
} else {
|
||||
// print("no JIT")
|
||||
if !allocateTest(), checkDebugged() {
|
||||
loop_heartbeat()
|
||||
sleep(5)
|
||||
let cool = String(cString: attach(getpid())!)
|
||||
print(cool)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -389,8 +402,9 @@ struct ContentView: View {
|
|||
private func handleDeepLink(_ url: URL) {
|
||||
if let components = URLComponents(url: url, resolvingAgainstBaseURL: true),
|
||||
components.host == "game" {
|
||||
DispatchQueue.main.async {
|
||||
|
||||
refreshControllersList()
|
||||
|
||||
if let text = components.queryItems?.first(where: { $0.name == "id" })?.value {
|
||||
game = ryujinx.games.first(where: { $0.titleId == text })
|
||||
} else if let text = components.queryItems?.first(where: { $0.name == "name" })?.value {
|
||||
|
@ -399,7 +413,6 @@ struct ContentView: View {
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension Array {
|
||||
@inlinable public mutating func mutableForEach(_ body: (inout Element) throws -> Void) rethrows {
|
||||
|
@ -454,7 +467,6 @@ class LocationManager: NSObject, CLLocationManagerDelegate {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
struct ControllerListView: View {
|
||||
@State private var selectedIndex = 0
|
||||
@Binding var game: Game?
|
||||
|
|
|
@ -273,7 +273,7 @@ struct GameLibraryView: View {
|
|||
gameRequirements: $gameRequirements,
|
||||
gameInfo: $gameInfo
|
||||
)
|
||||
.padding(.horizontal, 3)
|
||||
.padding(.horizontal)
|
||||
.padding(.vertical, 8)
|
||||
}
|
||||
}
|
||||
|
@ -290,7 +290,7 @@ struct GameLibraryView: View {
|
|||
gameRequirements: $gameRequirements,
|
||||
gameInfo: $gameInfo
|
||||
)
|
||||
.padding(.horizontal, 3)
|
||||
.padding(.horizontal)
|
||||
.padding(.vertical, 8)
|
||||
}
|
||||
}
|
||||
|
@ -314,12 +314,19 @@ struct GameLibraryView: View {
|
|||
} else {
|
||||
Menu("Applets") {
|
||||
Button {
|
||||
let game = Game(containerFolder: URL(string: "none")!, fileType: .item, fileURL: URL(string: "MiiMaker")!, titleName: "Mii Maker", titleId: "0", developer: "Nintendo", version: firmwareversion)
|
||||
let game = Game(containerFolder: URL(string: "none")!, fileType: .item, fileURL: URL(string: "0x0100000000001009")!, titleName: "Mii Maker", titleId: "0", developer: "Nintendo", version: firmwareversion)
|
||||
self.startemu = game
|
||||
} label: {
|
||||
Label("Launch Mii Maker", systemImage: "person.crop.circle")
|
||||
}
|
||||
|
||||
Button {
|
||||
let game = Game(containerFolder: URL(string: "none")!, fileType: .item, fileURL: URL(string: "0x0100000000001000")!, titleName: "Home Menu (Broken)", titleId: "0", developer: "Nintendo", version: firmwareversion)
|
||||
self.startemu = game
|
||||
} label: {
|
||||
Label("Home Menu (Broken)", systemImage: "house.circle")
|
||||
}
|
||||
.foregroundStyle(.red)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -658,6 +665,7 @@ struct GameCardView: View {
|
|||
@Binding var gameRequirements: [GameRequirements]
|
||||
@Binding var gameInfo: Game?
|
||||
@Environment(\.colorScheme) var colorScheme
|
||||
let totalMemory = ProcessInfo.processInfo.physicalMemory
|
||||
|
||||
var gameRequirement: GameRequirements? {
|
||||
gameRequirements.first(where: { $0.game_id == game.titleId })
|
||||
|
@ -729,7 +737,17 @@ struct GameCardView: View {
|
|||
.foregroundColor(.white)
|
||||
.padding(.horizontal, 6)
|
||||
.padding(.vertical, 2)
|
||||
.background(Color.blue)
|
||||
.background(req.memoryInt <= Int(String(format: "%.0f", Double(totalMemory) / 1_000_000_000)) ?? 0 ? Color.blue : Color.red)
|
||||
.cornerRadius(4)
|
||||
}
|
||||
} else {
|
||||
HStack(spacing: 4) {
|
||||
Text("0GB")
|
||||
.font(.system(size: 10, weight: .medium))
|
||||
.foregroundColor(.clear)
|
||||
.padding(.horizontal, 6)
|
||||
.padding(.vertical, 2)
|
||||
.background(Color.clear)
|
||||
.cornerRadius(4)
|
||||
}
|
||||
}
|
||||
|
@ -796,16 +814,15 @@ struct GameListRow: View {
|
|||
|
||||
HStack {
|
||||
Text(game.developer)
|
||||
.font(.system(size: 14))
|
||||
.font(.system(size: 12))
|
||||
.foregroundColor(.secondary)
|
||||
.multilineTextAlignment(.leading)
|
||||
|
||||
if !game.version.isEmpty && game.version != "0" {
|
||||
Text("•")
|
||||
.foregroundColor(.secondary)
|
||||
Divider().frame(width: 1, height: 15)
|
||||
|
||||
Text("v\(game.version)")
|
||||
.font(.system(size: 14))
|
||||
.font(.system(size: 10))
|
||||
.foregroundColor(.secondary)
|
||||
}
|
||||
}
|
||||
|
@ -820,7 +837,6 @@ struct GameListRow: View {
|
|||
let totalMemory = ProcessInfo.processInfo.physicalMemory
|
||||
|
||||
HStack(spacing: 4) {
|
||||
// Memory requirement badge
|
||||
Text(gameReq.device_memory)
|
||||
.font(.system(size: 11, weight: .medium))
|
||||
.foregroundColor(.white)
|
||||
|
@ -830,8 +846,11 @@ struct GameListRow: View {
|
|||
Capsule()
|
||||
.fill(gameReq.memoryInt <= Int(String(format: "%.0f", Double(totalMemory) / 1_000_000_000)) ?? 0 ? Color.blue : Color.red)
|
||||
)
|
||||
.lineLimit(1)
|
||||
.truncationMode(.tail)
|
||||
.fixedSize(horizontal: true, vertical: false)
|
||||
.layoutPriority(1)
|
||||
|
||||
// Compatibility badge
|
||||
Text(gameReq.compatibility)
|
||||
.font(.system(size: 11, weight: .medium))
|
||||
.foregroundColor(.white)
|
||||
|
@ -841,6 +860,10 @@ struct GameListRow: View {
|
|||
Capsule()
|
||||
.fill(gameReq.color)
|
||||
)
|
||||
.lineLimit(1)
|
||||
.truncationMode(.tail)
|
||||
.fixedSize(horizontal: true, vertical: false)
|
||||
.layoutPriority(1)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -7,9 +7,206 @@
|
|||
|
||||
import SwiftUI
|
||||
import SwiftSVG
|
||||
import UIKit
|
||||
|
||||
|
||||
class SplitViewController: UISplitViewController {
|
||||
private let sidebarViewController: UIViewController
|
||||
private let contentViewController: UIViewController
|
||||
|
||||
init(sidebarViewController: UIViewController, contentViewController: UIViewController) {
|
||||
self.sidebarViewController = sidebarViewController
|
||||
self.contentViewController = contentViewController
|
||||
super.init(style: .doubleColumn)
|
||||
|
||||
self.preferredDisplayMode = .oneBesideSecondary
|
||||
self.preferredSplitBehavior = .tile
|
||||
self.presentsWithGesture = true
|
||||
|
||||
self.setViewController(sidebarViewController, for: .primary)
|
||||
self.setViewController(contentViewController, for: .secondary)
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
|
||||
self.primaryBackgroundStyle = .sidebar
|
||||
|
||||
let displayModeButtonItem = self.displayModeButtonItem
|
||||
contentViewController.navigationItem.leftBarButtonItem = displayModeButtonItem
|
||||
}
|
||||
|
||||
func showSidebar() {
|
||||
self.preferredDisplayMode = .oneBesideSecondary
|
||||
}
|
||||
|
||||
func hideSidebar() {
|
||||
self.preferredDisplayMode = .secondaryOnly
|
||||
}
|
||||
|
||||
func toggleSidebar() {
|
||||
if self.displayMode == .oneBesideSecondary {
|
||||
self.preferredDisplayMode = .secondaryOnly
|
||||
} else {
|
||||
self.preferredDisplayMode = .oneBesideSecondary
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct SidebarView<Content: View>: View {
|
||||
var sidebar: () -> AnyView
|
||||
var content: () -> Content
|
||||
@Binding var showSidebar: Bool
|
||||
|
||||
init(sidebar: @escaping () -> AnyView, content: @escaping () -> Content, showSidebar: Binding<Bool>) {
|
||||
self.sidebar = sidebar
|
||||
self.content = content
|
||||
self._showSidebar = showSidebar
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
SidebarViewRepresentable(
|
||||
sidebar: sidebar(),
|
||||
content: content(),
|
||||
showSidebar: $showSidebar
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
struct SidebarViewRepresentable<Sidebar: View, Content: View>: UIViewControllerRepresentable {
|
||||
var sidebar: Sidebar
|
||||
var content: Content
|
||||
@Binding var showSidebar: Bool
|
||||
|
||||
func makeUIViewController(context: Context) -> SplitViewController {
|
||||
let sidebarVC = UIHostingController(rootView: sidebar)
|
||||
let contentVC = UINavigationController(rootViewController: UIHostingController(rootView: content))
|
||||
|
||||
let splitVC = SplitViewController(sidebarViewController: sidebarVC, contentViewController: contentVC)
|
||||
splitVC.setOverrideTraitCollection(
|
||||
UITraitCollection(horizontalSizeClass: .regular),
|
||||
forChild: splitVC
|
||||
)
|
||||
return splitVC
|
||||
}
|
||||
|
||||
func updateUIViewController(_ uiViewController: SplitViewController, context: Context) {
|
||||
if let sidebarVC = uiViewController.viewController(for: .primary) as? UIHostingController<Sidebar> {
|
||||
sidebarVC.rootView = sidebar
|
||||
}
|
||||
if let navController = uiViewController.viewController(for: .secondary) as? UINavigationController,
|
||||
let contentVC = navController.topViewController as? UIHostingController<Content> {
|
||||
contentVC.rootView = content
|
||||
}
|
||||
|
||||
if showSidebar {
|
||||
uiViewController.showSidebar()
|
||||
} else {
|
||||
uiViewController.hideSidebar()
|
||||
}
|
||||
}
|
||||
|
||||
static func dismantleUIViewController(_ uiViewController: SplitViewController, coordinator: Coordinator) {
|
||||
}
|
||||
}
|
||||
|
||||
class SettingsManager: ObservableObject {
|
||||
@Published var config: Ryujinx.Arguments {
|
||||
didSet {
|
||||
debouncedSave()
|
||||
}
|
||||
}
|
||||
|
||||
private var saveWorkItem: DispatchWorkItem?;
|
||||
|
||||
public static var shared = SettingsManager()
|
||||
|
||||
private init() {
|
||||
self.config = SettingsManager.loadSettings() ?? Ryujinx.Arguments()
|
||||
}
|
||||
|
||||
func debouncedSave() {
|
||||
saveWorkItem?.cancel()
|
||||
|
||||
let workItem = DispatchWorkItem { [weak self] in
|
||||
guard let self = self else { return }
|
||||
self.saveSettings()
|
||||
}
|
||||
|
||||
saveWorkItem = workItem
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 0.3, execute: workItem)
|
||||
}
|
||||
|
||||
func saveSettings() {
|
||||
do {
|
||||
let encoder = JSONEncoder()
|
||||
encoder.outputFormatting = .prettyPrinted
|
||||
let data = try encoder.encode(config)
|
||||
|
||||
let fileURL = URL.documentsDirectory.appendingPathComponent("config.json")
|
||||
|
||||
try data.write(to: fileURL)
|
||||
print("Settings saved successfully")
|
||||
} catch {
|
||||
print("Failed to save settings: \(error)")
|
||||
}
|
||||
}
|
||||
|
||||
static func loadSettings() -> Ryujinx.Arguments? {
|
||||
do {
|
||||
let fileURL = URL.documentsDirectory.appendingPathComponent("config.json")
|
||||
|
||||
guard FileManager.default.fileExists(atPath: fileURL.path) else {
|
||||
print("Config file does not exist, creating new config")
|
||||
return nil
|
||||
}
|
||||
|
||||
let data = try Data(contentsOf: fileURL)
|
||||
|
||||
let decoder = JSONDecoder()
|
||||
let configs = try decoder.decode(Ryujinx.Arguments.self, from: data)
|
||||
return configs
|
||||
} catch {
|
||||
print("Failed to load settings: \(error)")
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func loadSettings() {
|
||||
do {
|
||||
let fileURL = URL.documentsDirectory.appendingPathComponent("config.json")
|
||||
|
||||
guard FileManager.default.fileExists(atPath: fileURL.path) else {
|
||||
print("Config file does not exist, creating new config")
|
||||
saveSettings()
|
||||
return
|
||||
}
|
||||
|
||||
let data = try Data(contentsOf: fileURL)
|
||||
|
||||
let decoder = JSONDecoder()
|
||||
let configs = try decoder.decode(Ryujinx.Arguments.self, from: data)
|
||||
|
||||
self.config = configs
|
||||
} catch {
|
||||
print("Failed to load settings: \(error)")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
struct SettingsViewNew: View {
|
||||
@StateObject private var settingsManager = SettingsManager.shared
|
||||
|
||||
private var config: Binding<Ryujinx.Arguments> {
|
||||
$settingsManager.config
|
||||
}
|
||||
|
||||
struct SettingsView: View {
|
||||
@Binding var config: Ryujinx.Configuration
|
||||
@Binding var MoltenVKSettings: [MoltenVKSettings]
|
||||
|
||||
@Binding var controllersList: [Controller]
|
||||
|
@ -30,7 +227,6 @@ struct SettingsView: View {
|
|||
]
|
||||
|
||||
@AppStorage("RyuDemoControls") var ryuDemo: Bool = false
|
||||
@AppStorage("MTL_HUD_ENABLED") var metalHUDEnabled: Bool = false
|
||||
|
||||
@AppStorage("showScreenShotButton") var ssb: Bool = false
|
||||
|
||||
|
@ -45,12 +241,16 @@ struct SettingsView: View {
|
|||
|
||||
@AppStorage("On-ScreenControllerScale") var controllerScale: Double = 1.0
|
||||
|
||||
@AppStorage("On-ScreenControllerOpacity") var controllerOpacity: Double = 1.0
|
||||
|
||||
@AppStorage("hasbeenfinished") var finishedStorage: Bool = false
|
||||
|
||||
@AppStorage("showlogsloading") var showlogsloading: Bool = true
|
||||
|
||||
@AppStorage("showlogsgame") var showlogsgame: Bool = false
|
||||
|
||||
@AppStorage("toggleGreen") var toggleGreen: Bool = false
|
||||
|
||||
@AppStorage("stick-button") var stickButton = false
|
||||
@AppStorage("waitForVPN") var waitForVPN = false
|
||||
|
||||
|
@ -60,10 +260,17 @@ struct SettingsView: View {
|
|||
|
||||
@AppStorage("disableTouch") var disableTouch = false
|
||||
|
||||
@AppStorage("disableTouch") var blackScreen = false
|
||||
|
||||
@AppStorage("location-enabled") var locationenabled: Bool = false
|
||||
|
||||
@AppStorage("runOnMainThread") var runOnMainThread = false
|
||||
|
||||
@AppCodableStorage("toggleButtons") var toggleButtons = ToggleButtonsState()
|
||||
|
||||
let totalMemory = ProcessInfo.processInfo.physicalMemory
|
||||
|
||||
@AppStorage("lockInApp") var restartApp = false
|
||||
|
||||
@State private var showResolutionInfo = false
|
||||
@State private var showAnisotropicInfo = false
|
||||
|
@ -77,11 +284,26 @@ struct SettingsView: View {
|
|||
|
||||
@State private var selectedCategory: SettingsCategory = .graphics
|
||||
|
||||
@StateObject var metalHudEnabler = MTLHud.shared
|
||||
|
||||
var filteredMemoryModes: [(String, String)] {
|
||||
guard !searchText.isEmpty else { return memoryManagerModes }
|
||||
return memoryManagerModes.filter { $0.1.localizedCaseInsensitiveContains(searchText) }
|
||||
}
|
||||
|
||||
var appVersion: String {
|
||||
guard let version = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String else {
|
||||
return "Unknown"
|
||||
}
|
||||
return version
|
||||
}
|
||||
|
||||
@FocusState private var isArgumentsKeyboardVisible: Bool
|
||||
|
||||
|
||||
@State private var selectedView = "Data Management"
|
||||
@State private var sidebar = true
|
||||
|
||||
enum SettingsCategory: String, CaseIterable, Identifiable {
|
||||
case graphics = "Graphics"
|
||||
case input = "Input"
|
||||
|
@ -102,16 +324,132 @@ struct SettingsView: View {
|
|||
}
|
||||
}
|
||||
|
||||
var appVersion: String {
|
||||
guard let version = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String else {
|
||||
return "Unknown"
|
||||
}
|
||||
return version
|
||||
}
|
||||
|
||||
@FocusState private var isArgumentsKeyboardVisible: Bool
|
||||
|
||||
var body: some View {
|
||||
if UIDevice.current.userInterfaceIdiom == .phone {
|
||||
iOSSettings
|
||||
} else {
|
||||
iPadOSSettings
|
||||
.ignoresSafeArea()
|
||||
.edgesIgnoringSafeArea(.all)
|
||||
}
|
||||
}
|
||||
|
||||
var iPadOSSettings: some View {
|
||||
VStack {
|
||||
SidebarView(
|
||||
sidebar: {
|
||||
AnyView(
|
||||
ScrollView(.vertical) {
|
||||
VStack {
|
||||
VStack(spacing: 16) {
|
||||
HStack {
|
||||
Circle()
|
||||
.fill(ryujinx.jitenabled ? Color.green : Color.red)
|
||||
.frame(width: 12, height: 12)
|
||||
|
||||
Text(ryujinx.jitenabled ? "JIT Enabled" : "JIT Not Acquired")
|
||||
.font(.subheadline.weight(.medium))
|
||||
.foregroundColor(ryujinx.jitenabled ? .green : .red)
|
||||
|
||||
Spacer()
|
||||
|
||||
let memoryText = ProcessInfo.processInfo.isiOSAppOnMac
|
||||
? String(format: "%.0f GB", Double(totalMemory) / (1024 * 1024 * 1024))
|
||||
: String(format: "%.0f GB", Double(totalMemory) / 1_000_000_000)
|
||||
|
||||
Text("\(memoryText) RAM")
|
||||
.font(.subheadline.weight(.medium))
|
||||
.foregroundColor(.secondary)
|
||||
|
||||
}
|
||||
|
||||
InfoCard(
|
||||
title: "Device",
|
||||
value: UIDevice.modelName,
|
||||
icon: deviceIcon,
|
||||
color: .blue
|
||||
)
|
||||
|
||||
InfoCard(
|
||||
title: "System",
|
||||
value: "\(UIDevice.current.systemName) \(UIDevice.current.systemVersion)",
|
||||
icon: "applelogo",
|
||||
color: .gray
|
||||
)
|
||||
|
||||
InfoCard(
|
||||
title: "Increased Memory Limit",
|
||||
value: checkAppEntitlement("com.apple.developer.kernel.increased-memory-limit") ? "Enabled" : "Disabled",
|
||||
icon: "memorychip.fill",
|
||||
color: .orange
|
||||
)
|
||||
}
|
||||
.padding()
|
||||
|
||||
Divider()
|
||||
|
||||
ForEach(SettingsCategory.allCases, id: \.id) { key in
|
||||
HStack {
|
||||
Rectangle()
|
||||
.frame(width: 2.5, height: 35)
|
||||
.foregroundStyle(selectedCategory == key ? Color.accentColor : Color.clear)
|
||||
Text(key.rawValue) // Fix here
|
||||
Spacer()
|
||||
}
|
||||
.foregroundStyle(selectedCategory == key ? Color.accentColor : Color.primary)
|
||||
.padding(5)
|
||||
.background(
|
||||
Color(uiColor: .secondarySystemBackground).opacity(selectedCategory == key ? 1 : 0)
|
||||
)
|
||||
.background(
|
||||
Rectangle()
|
||||
.stroke(selectedCategory == key ? .teal : .clear, lineWidth: 2.5)
|
||||
)
|
||||
.contentShape(Rectangle())
|
||||
.onTapGesture {
|
||||
withAnimation(.smooth) {
|
||||
selectedCategory = key // Uncommented and fixed
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.padding()
|
||||
}
|
||||
)
|
||||
},
|
||||
content: {
|
||||
ScrollView {
|
||||
switch selectedCategory {
|
||||
case .graphics:
|
||||
graphicsSettings
|
||||
case .input:
|
||||
inputSettings
|
||||
case .system:
|
||||
systemSettings
|
||||
case .advanced:
|
||||
advancedSettings
|
||||
case .misc:
|
||||
miscSettings
|
||||
}
|
||||
}
|
||||
},
|
||||
showSidebar: $sidebar
|
||||
)
|
||||
.onAppear {
|
||||
mVKPreFillBuffer = false
|
||||
|
||||
|
||||
if let configs = SettingsManager.loadSettings() {
|
||||
settingsManager.loadSettings()
|
||||
} else {
|
||||
settingsManager.saveSettings()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
var iOSSettings: some View {
|
||||
iOSNav {
|
||||
ZStack {
|
||||
// Background color
|
||||
|
@ -141,7 +479,6 @@ struct SettingsView: View {
|
|||
// Settings content
|
||||
ScrollView {
|
||||
VStack(spacing: 24) {
|
||||
// Device Info Card
|
||||
deviceInfoCard
|
||||
.padding(.horizontal)
|
||||
.padding(.top)
|
||||
|
@ -172,15 +509,12 @@ struct SettingsView: View {
|
|||
.onAppear {
|
||||
mVKPreFillBuffer = false
|
||||
|
||||
if let configs = loadSettings() {
|
||||
self.config = configs
|
||||
if let configs = SettingsManager.loadSettings() {
|
||||
settingsManager.loadSettings()
|
||||
} else {
|
||||
saveSettings()
|
||||
settingsManager.saveSettings()
|
||||
}
|
||||
}
|
||||
.onChange(of: config) { _ in
|
||||
saveSettings()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -200,7 +534,6 @@ struct SettingsView: View {
|
|||
|
||||
Spacer()
|
||||
|
||||
let totalMemory = ProcessInfo.processInfo.physicalMemory
|
||||
let memoryText = ProcessInfo.processInfo.isiOSAppOnMac
|
||||
? String(format: "%.0f GB", Double(totalMemory) / (1024 * 1024 * 1024))
|
||||
: String(format: "%.0f GB", Double(totalMemory) / 1_000_000_000)
|
||||
|
@ -317,7 +650,7 @@ struct SettingsView: View {
|
|||
}
|
||||
|
||||
VStack(spacing: 8) {
|
||||
Slider(value: $config.resscale, in: 0.1...3.0, step: 0.05)
|
||||
Slider(value: config.resscale, in: 0.1...3.0, step: 0.05)
|
||||
|
||||
HStack {
|
||||
Text("0.1x")
|
||||
|
@ -326,7 +659,7 @@ struct SettingsView: View {
|
|||
|
||||
Spacer()
|
||||
|
||||
Text("\(config.resscale, specifier: "%.2f")x")
|
||||
Text("\(settingsManager.config.resscale, specifier: "%.2f")x")
|
||||
.font(.headline)
|
||||
.foregroundColor(.blue)
|
||||
|
||||
|
@ -364,7 +697,7 @@ struct SettingsView: View {
|
|||
}
|
||||
|
||||
VStack(spacing: 8) {
|
||||
Slider(value: $config.maxAnisotropy, in: 0...16.0, step: 0.1)
|
||||
Slider(value: config.maxAnisotropy, in: 0...16.0, step: 0.1)
|
||||
|
||||
HStack {
|
||||
Text("Off")
|
||||
|
@ -373,7 +706,7 @@ struct SettingsView: View {
|
|||
|
||||
Spacer()
|
||||
|
||||
Text("\(config.maxAnisotropy, specifier: "%.1f")x")
|
||||
Text("\(settingsManager.config.maxAnisotropy, specifier: "%.1f")x")
|
||||
.font(.headline)
|
||||
.foregroundColor(.blue)
|
||||
|
||||
|
@ -390,23 +723,23 @@ struct SettingsView: View {
|
|||
// Toggle options card
|
||||
SettingsCard {
|
||||
VStack(spacing: 4) {
|
||||
SettingsToggle(isOn: $config.disableShaderCache, icon: "memorychip", label: "Shader Cache")
|
||||
SettingsToggle(isOn: config.disableShaderCache, icon: "memorychip", label: "Shader Cache")
|
||||
|
||||
Divider()
|
||||
|
||||
SettingsToggle(isOn: $config.disablevsync, icon: "arrow.triangle.2.circlepath", label: "Disable VSync")
|
||||
SettingsToggle(isOn: config.disablevsync, icon: "arrow.triangle.2.circlepath", label: "Disable VSync")
|
||||
|
||||
Divider()
|
||||
|
||||
SettingsToggle(isOn: $config.enableTextureRecompression, icon: "rectangle.compress.vertical", label: "Texture Recompression")
|
||||
SettingsToggle(isOn: config.enableTextureRecompression, icon: "rectangle.compress.vertical", label: "Texture Recompression")
|
||||
|
||||
Divider()
|
||||
|
||||
SettingsToggle(isOn: $config.disableDockedMode, icon: "dock.rectangle", label: "Docked Mode")
|
||||
SettingsToggle(isOn: config.disableDockedMode, icon: "dock.rectangle", label: "Docked Mode")
|
||||
|
||||
Divider()
|
||||
|
||||
SettingsToggle(isOn: $config.macroHLE, icon: "gearshape", label: "Macro HLE")
|
||||
SettingsToggle(isOn: config.macroHLE, icon: "gearshape", label: "Macro HLE")
|
||||
|
||||
Divider()
|
||||
|
||||
|
@ -421,7 +754,7 @@ struct SettingsView: View {
|
|||
.font(.headline)
|
||||
|
||||
if (horizontalSizeClass == .regular && verticalSizeClass == .regular) || (horizontalSizeClass == .regular && verticalSizeClass == .compact) {
|
||||
Picker(selection: $config.aspectRatio) {
|
||||
Picker(selection: config.aspectRatio) {
|
||||
ForEach(AspectRatio.allCases, id: \.self) { ratio in
|
||||
Text(ratio.displayName).tag(ratio)
|
||||
}
|
||||
|
@ -430,7 +763,7 @@ struct SettingsView: View {
|
|||
}
|
||||
.pickerStyle(.segmented)
|
||||
} else {
|
||||
Picker(selection: $config.aspectRatio) {
|
||||
Picker(selection: config.aspectRatio) {
|
||||
ForEach(AspectRatio.allCases, id: \.self) { ratio in
|
||||
Text(ratio.displayName).tag(ratio)
|
||||
}
|
||||
|
@ -473,7 +806,7 @@ struct SettingsView: View {
|
|||
// On-screen controls card
|
||||
SettingsCard {
|
||||
VStack(spacing: 4) {
|
||||
SettingsToggle(isOn: $config.handHeldController, icon: "formfitting.gamecontroller", label: "Player 1 to Handheld")
|
||||
SettingsToggle(isOn: config.handHeldController, icon: "formfitting.gamecontroller", label: "Player 1 to Handheld")
|
||||
|
||||
Divider()
|
||||
|
||||
|
@ -503,8 +836,13 @@ struct SettingsView: View {
|
|||
// Controller scale card
|
||||
SettingsCard {
|
||||
VStack(alignment: .leading, spacing: 12) {
|
||||
Text("On-Screen Controller")
|
||||
.font(.headline)
|
||||
.foregroundColor(.primary)
|
||||
|
||||
Group {
|
||||
HStack {
|
||||
labelWithIcon("On-Screen Controller Scale", iconName: "magnifyingglass")
|
||||
labelWithIcon("Scale", iconName: "magnifyingglass")
|
||||
.font(.headline)
|
||||
Spacer()
|
||||
Button {
|
||||
|
@ -545,6 +883,53 @@ struct SettingsView: View {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
Divider()
|
||||
|
||||
Group {
|
||||
HStack {
|
||||
labelWithIcon("Opacity", iconName: "magnifyingglass")
|
||||
.font(.headline)
|
||||
Spacer()
|
||||
Button {
|
||||
showControllerInfo.toggle()
|
||||
} label: {
|
||||
Image(systemName: "info.circle")
|
||||
.foregroundColor(.secondary)
|
||||
}
|
||||
.buttonStyle(.plain)
|
||||
.alert(isPresented: $showControllerInfo) {
|
||||
Alert(
|
||||
title: Text("On-Screen Controller Opacity"),
|
||||
message: Text("Adjust the On-Screen Controller transparency."),
|
||||
dismissButton: .default(Text("OK"))
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
VStack(spacing: 8) {
|
||||
Slider(value: $controllerOpacity, in: 0.1...1.0, step: 0.05)
|
||||
|
||||
HStack {
|
||||
Text("More Transparent")
|
||||
.font(.caption2)
|
||||
.foregroundColor(.secondary)
|
||||
|
||||
Spacer()
|
||||
|
||||
Text("\(controllerOpacity, specifier: "%.2f")x")
|
||||
.font(.headline)
|
||||
.foregroundColor(.blue)
|
||||
|
||||
Spacer()
|
||||
|
||||
Text("Less Transparent")
|
||||
.font(.caption2)
|
||||
.foregroundColor(.secondary)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -632,7 +1017,7 @@ struct SettingsView: View {
|
|||
labelWithIcon("System Language", iconName: "character.bubble")
|
||||
.font(.headline)
|
||||
|
||||
Picker(selection: $config.language) {
|
||||
Picker(selection: config.language) {
|
||||
ForEach(SystemLanguage.allCases, id: \.self) { language in
|
||||
Text(language.displayName).tag(language)
|
||||
}
|
||||
|
@ -650,7 +1035,7 @@ struct SettingsView: View {
|
|||
labelWithIcon("Region", iconName: "globe")
|
||||
.font(.headline)
|
||||
|
||||
Picker(selection: $config.regioncode) {
|
||||
Picker(selection: config.regioncode) {
|
||||
ForEach(SystemRegionCode.allCases, id: \.self) { region in
|
||||
Text(region.displayName).tag(region)
|
||||
}
|
||||
|
@ -676,7 +1061,7 @@ struct SettingsView: View {
|
|||
.font(.subheadline)
|
||||
.foregroundColor(.secondary)
|
||||
|
||||
Picker(selection: $config.memoryManagerMode) {
|
||||
Picker(selection: config.memoryManagerMode) {
|
||||
ForEach(filteredMemoryModes, id: \.0) { key, displayName in
|
||||
Text(displayName).tag(key)
|
||||
}
|
||||
|
@ -688,7 +1073,7 @@ struct SettingsView: View {
|
|||
|
||||
Divider()
|
||||
|
||||
SettingsToggle(isOn: $config.disablePTC, icon: "cpu", label: "Disable PTC")
|
||||
SettingsToggle(isOn: config.disablePTC, icon: "cpu", label: "Disable PTC")
|
||||
|
||||
if let gpuInfo = getGPUInfo(), gpuInfo.hasPrefix("Apple M") {
|
||||
Divider()
|
||||
|
@ -697,24 +1082,11 @@ struct SettingsView: View {
|
|||
SettingsToggle(isOn: .constant(false), icon: "bolt", label: "Hypervisor")
|
||||
.disabled(true)
|
||||
} else if checkAppEntitlement("com.apple.private.hypervisor") {
|
||||
SettingsToggle(isOn: $config.hypervisor, icon: "bolt", label: "Hypervisor")
|
||||
SettingsToggle(isOn: config.hypervisor, icon: "bolt", label: "Hypervisor")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Memory hacks card
|
||||
SettingsCard {
|
||||
VStack(spacing: 4) {
|
||||
SettingsToggle(isOn: $config.expandRam, icon: "exclamationmark.bubble", label: "Expand Guest RAM (6GB)")
|
||||
.accentColor(.red)
|
||||
|
||||
Divider()
|
||||
|
||||
SettingsToggle(isOn: $config.ignoreMissingServices, icon: "waveform.path", label: "Ignore Missing Services")
|
||||
.accentColor(.red)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -733,11 +1105,11 @@ struct SettingsView: View {
|
|||
|
||||
Divider()
|
||||
|
||||
SettingsToggle(isOn: $config.debuglogs, icon: "exclamationmark.bubble", label: "Debug Logs")
|
||||
SettingsToggle(isOn: config.debuglogs, icon: "exclamationmark.bubble", label: "Debug Logs")
|
||||
|
||||
Divider()
|
||||
|
||||
SettingsToggle(isOn: $config.tracelogs, icon: "waveform.path", label: "Trace Logs")
|
||||
SettingsToggle(isOn: config.tracelogs, icon: "waveform.path", label: "Trace Logs")
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -748,15 +1120,16 @@ struct SettingsView: View {
|
|||
|
||||
Divider()
|
||||
|
||||
SettingsToggle(isOn: $config.dfsIntegrityChecks, icon: "checkmark.shield", label: "Disable FS Integrity Checks")
|
||||
SettingsToggle(isOn: config.dfsIntegrityChecks, icon: "checkmark.shield", label: "Disable FS Integrity Checks")
|
||||
|
||||
Divider()
|
||||
|
||||
SettingsToggle(isOn: config.backendMultithreading, icon: "inset.filled.rectangle.and.person.filled", label: "Backend Multithreading")
|
||||
|
||||
Divider()
|
||||
|
||||
if MTLHud.shared.canMetalHud {
|
||||
SettingsToggle(isOn: $metalHUDEnabled, icon: "speedometer", label: "Metal Performance HUD")
|
||||
.onChange(of: metalHUDEnabled) { newValue in
|
||||
MTLHud.shared.toggle()
|
||||
}
|
||||
SettingsToggle(isOn: $metalHudEnabler.metalHudEnabled, icon: "speedometer", label: "Metal Performance HUD")
|
||||
|
||||
Divider()
|
||||
}
|
||||
|
@ -775,8 +1148,23 @@ struct SettingsView: View {
|
|||
.foregroundColor(.blue)
|
||||
Spacer()
|
||||
}
|
||||
.padding(.vertical, 8)
|
||||
.padding(8)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// Memory hacks card
|
||||
SettingsCard {
|
||||
VStack(spacing: 4) {
|
||||
SettingsToggle(isOn: config.expandRam, icon: "exclamationmark.bubble", label: "Expand Guest RAM")
|
||||
.accentColor(.red)
|
||||
.disabled(totalMemory < 5723)
|
||||
|
||||
Divider()
|
||||
|
||||
SettingsToggle(isOn: config.ignoreMissingServices, icon: "waveform.path", label: "Ignore Missing Services")
|
||||
.accentColor(.red)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -787,17 +1175,21 @@ struct SettingsView: View {
|
|||
.font(.headline)
|
||||
.foregroundColor(.primary)
|
||||
|
||||
if #available(iOS 15.0, *) {
|
||||
TextField("Separate arguments with commas" ,text: Binding(
|
||||
let binding = Binding(
|
||||
get: {
|
||||
config.additionalArgs.joined(separator: ", ")
|
||||
config.additionalArgs.wrappedValue.joined(separator: ", ")
|
||||
},
|
||||
set: { newValue in
|
||||
config.additionalArgs = newValue
|
||||
settingsManager.config.additionalArgs = newValue
|
||||
.split(separator: ",")
|
||||
.map { $0.trimmingCharacters(in: .whitespaces) }
|
||||
}
|
||||
))
|
||||
)
|
||||
|
||||
|
||||
if #available(iOS 15.0, *) {
|
||||
|
||||
TextField("Separate arguments with commas", text: binding)
|
||||
.font(.system(.body, design: .monospaced))
|
||||
.textFieldStyle(.roundedBorder)
|
||||
.textInputAutocapitalization(.none)
|
||||
|
@ -812,16 +1204,7 @@ struct SettingsView: View {
|
|||
}
|
||||
.focused($isArgumentsKeyboardVisible)
|
||||
} else {
|
||||
TextField("Separate arguments with commas", text: Binding(
|
||||
get: {
|
||||
config.additionalArgs.joined(separator: ", ")
|
||||
},
|
||||
set: { newValue in
|
||||
config.additionalArgs = newValue
|
||||
.split(separator: ",")
|
||||
.map { $0.trimmingCharacters(in: .whitespaces) }
|
||||
}
|
||||
))
|
||||
TextField("Separate arguments with commas", text: binding)
|
||||
.font(.system(.body, design: .monospaced))
|
||||
.textFieldStyle(.roundedBorder)
|
||||
.disableAutocorrection(true)
|
||||
|
@ -858,27 +1241,55 @@ struct SettingsView: View {
|
|||
SettingsSection(title: "Miscellaneous Options") {
|
||||
SettingsCard {
|
||||
VStack(spacing: 4) {
|
||||
if UIDevice.current.userInterfaceIdiom == .pad {
|
||||
SettingsToggle(isOn: $toggleGreen, icon: "arrow.clockwise", label: "Toggle Color Green when \"ON\"")
|
||||
|
||||
Divider()
|
||||
}
|
||||
|
||||
|
||||
// Disable Touch card
|
||||
SettingsToggle(isOn: $disableTouch, icon: "rectangle.and.hand.point.up.left.filled", label: "Disable Touch")
|
||||
|
||||
Divider()
|
||||
|
||||
// Screenshot button card
|
||||
if colorScheme == .light {
|
||||
SettingsToggle(isOn: $disableTouch, icon: "iphone.slash", label: "Black Screen when using AirPlay")
|
||||
|
||||
Divider()
|
||||
}
|
||||
|
||||
// Exit button card
|
||||
SettingsToggle(isOn: $ssb, icon: "arrow.left.circle", label: "Exit Button")
|
||||
|
||||
Divider()
|
||||
|
||||
// Restarts app when it crashes card
|
||||
SettingsToggle(isOn: $restartApp, icon: "arrow.clockwise", label: "Lock in App")
|
||||
|
||||
Divider()
|
||||
|
||||
|
||||
// Location to keep app in Background
|
||||
SettingsToggle(isOn: $locationenabled, icon: "location.viewfinder", label: "Keep app in background")
|
||||
|
||||
Divider()
|
||||
|
||||
|
||||
// JIT options
|
||||
if #available(iOS 17.0.1, *) {
|
||||
SettingsToggle(isOn: $stikJIT, icon: "bolt.heart", label: "StikJIT")
|
||||
let checked = stikJITorStikDebug()
|
||||
let stikJIT = checked == 1 ? "StikDebug" : checked == 2 ? "StikJIT" : "StikDebug"
|
||||
|
||||
SettingsToggle(isOn: $stikJIT, icon: "bolt.heart", label: stikJIT)
|
||||
.contextMenu {
|
||||
Button {
|
||||
if let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene,
|
||||
let mainWindow = windowScene.windows.last {
|
||||
let alertController = UIAlertController(title: "About StikJIT", message: "StikJIT is a really amazing iOS Application to Enable JIT on the go on-device, made by the best, most kind, helpful and nice developers of all time jkcoxson and Blu <3", preferredStyle: .alert)
|
||||
let alertController = UIAlertController(title: "About \(stikJIT)", message: "\(stikJIT) is a really amazing iOS Application to Enable JIT on the go on-device, made by the best, most kind, helpful and nice developers of all time jkcoxson and Blu <3", preferredStyle: .alert)
|
||||
|
||||
let learnMoreButton = UIAlertAction(title: "Learn More", style: .default) {_ in
|
||||
UIApplication.shared.open(URL(string: "https://github.com/0-Blu/StikJIT")!)
|
||||
UIApplication.shared.open(URL(string: "https://github.com/StephenDev0/StikJIT")!)
|
||||
}
|
||||
alertController.addAction(learnMoreButton)
|
||||
|
||||
|
@ -947,9 +1358,6 @@ struct SettingsView: View {
|
|||
}
|
||||
}
|
||||
|
||||
func saveSettings() {
|
||||
MeloNX.saveSettings(config: config)
|
||||
}
|
||||
|
||||
func getGPUInfo() -> String? {
|
||||
let device = MTLCreateSystemDefaultDevice()
|
||||
|
@ -1012,7 +1420,7 @@ struct SVGView: UIViewRepresentable {
|
|||
}
|
||||
}
|
||||
|
||||
func saveSettings(config: Ryujinx.Configuration) {
|
||||
func saveSettings(config: Ryujinx.Arguments) {
|
||||
do {
|
||||
let encoder = JSONEncoder()
|
||||
encoder.outputFormatting = .prettyPrinted
|
||||
|
@ -1027,7 +1435,7 @@ func saveSettings(config: Ryujinx.Configuration) {
|
|||
}
|
||||
}
|
||||
|
||||
func loadSettings() -> Ryujinx.Configuration? {
|
||||
func loadSettings() -> Ryujinx.Arguments? {
|
||||
do {
|
||||
let fileURL = URL.documentsDirectory.appendingPathComponent("config.json")
|
||||
|
||||
|
@ -1039,7 +1447,7 @@ func loadSettings() -> Ryujinx.Configuration? {
|
|||
let data = try Data(contentsOf: fileURL)
|
||||
|
||||
let decoder = JSONDecoder()
|
||||
let configs = try decoder.decode(Ryujinx.Configuration.self, from: data)
|
||||
let configs = try decoder.decode(Ryujinx.Arguments.self, from: data)
|
||||
return configs
|
||||
} catch {
|
||||
// print("Failed to load settings: \(error)")
|
||||
|
@ -1104,6 +1512,7 @@ struct SettingsCard<Content: View>: View {
|
|||
}
|
||||
|
||||
var body: some View {
|
||||
if UIDevice.current.userInterfaceIdiom == .phone {
|
||||
content
|
||||
.padding()
|
||||
.background(
|
||||
|
@ -1112,17 +1521,27 @@ struct SettingsCard<Content: View>: View {
|
|||
.shadow(color: Color.black.opacity(0.05), radius: 5, x: 0, y: 2)
|
||||
)
|
||||
.padding(.horizontal)
|
||||
} else {
|
||||
VStack {
|
||||
Divider()
|
||||
content
|
||||
Divider()
|
||||
}
|
||||
.padding()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct SettingsToggle: View {
|
||||
let isOn: Binding<Bool>
|
||||
@Binding var isOn: Bool
|
||||
let icon: String
|
||||
let label: String
|
||||
var disabled: Bool = false
|
||||
@AppStorage("toggleGreen") var toggleGreen: Bool = false
|
||||
|
||||
var body: some View {
|
||||
Toggle(isOn: isOn) {
|
||||
if UIDevice.current.userInterfaceIdiom == .phone {
|
||||
Toggle(isOn: $isOn) {
|
||||
HStack(spacing: 8) {
|
||||
if icon.hasSuffix(".svg") {
|
||||
SVGView(svgName: icon, color: .blue)
|
||||
|
@ -1140,6 +1559,35 @@ struct SettingsToggle: View {
|
|||
.toggleStyle(SwitchToggleStyle(tint: .blue))
|
||||
.disabled(disabled)
|
||||
.padding(.vertical, 6)
|
||||
} else {
|
||||
Group {
|
||||
HStack(spacing: 8) {
|
||||
HStack {
|
||||
if icon.hasSuffix(".svg") {
|
||||
SVGView(svgName: icon, color: .blue)
|
||||
.frame(width: 20, height: 20)
|
||||
} else {
|
||||
Image(systemName: icon)
|
||||
// .symbolRenderingMode(.hierarchical)
|
||||
.foregroundStyle(.blue)
|
||||
}
|
||||
|
||||
Text(label)
|
||||
.font(.body)
|
||||
}
|
||||
|
||||
Spacer()
|
||||
|
||||
|
||||
Text(isOn ? "ON" : "Off")
|
||||
.foregroundStyle(isOn ? (toggleGreen ? .green : .blue) : .blue)
|
||||
}
|
||||
.padding()
|
||||
.onTapGesture {
|
||||
isOn.toggle()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func disabled(_ disabled: Bool) -> SettingsToggle {
|
||||
|
|
|
@ -11,7 +11,6 @@ import UniformTypeIdentifiers
|
|||
|
||||
struct MainTabView: View {
|
||||
@Binding var startemu: Game?
|
||||
@Binding var config: Ryujinx.Configuration
|
||||
@Binding var MVKconfig: [MoltenVKSettings]
|
||||
@Binding var controllersList: [Controller]
|
||||
@Binding var currentControllers: [Controller]
|
||||
|
@ -25,7 +24,8 @@ struct MainTabView: View {
|
|||
Label("Games", systemImage: "gamecontroller.fill")
|
||||
}
|
||||
|
||||
SettingsView(config: $config, MoltenVKSettings: $MVKconfig, controllersList: $controllersList, currentControllers: $currentControllers, onscreencontroller: $onscreencontroller)
|
||||
// SettingsView(config: $config, MoltenVKSettings: $MVKconfig, controllersList: $controllersList, currentControllers: $currentControllers, onscreencontroller: $onscreencontroller)
|
||||
SettingsViewNew(MoltenVKSettings: $MVKconfig, controllersList: $controllersList, currentControllers: $currentControllers, onscreencontroller: $onscreencontroller)
|
||||
.tabItem {
|
||||
Label("Settings", systemImage: "gear")
|
||||
}
|
||||
|
|
|
@ -280,11 +280,10 @@ struct UpdateManagerSheet: View {
|
|||
print("toggle selection \(update.path)")
|
||||
|
||||
updates = updates.map { item in
|
||||
var mutableItem = item
|
||||
mutableItem.isSelected = item.path == update.path && !update.isSelected
|
||||
item.isSelected = item.path == update.path && !update.isSelected
|
||||
// print(mutableItem.isSelected)
|
||||
// print(update.isSelected)
|
||||
return mutableItem
|
||||
return item
|
||||
}
|
||||
|
||||
// print(updates)
|
||||
|
|
|
@ -28,6 +28,8 @@ struct MeloNXApp: App {
|
|||
@State var showOutOfDateSheet = false
|
||||
@State var updateInfo: LatestVersionResponse? = nil
|
||||
|
||||
@StateObject var metalHudEnabler = MTLHud.shared
|
||||
|
||||
@State var finished = false
|
||||
@AppStorage("hasbeenfinished") var finishedStorage: Bool = false
|
||||
|
||||
|
@ -47,6 +49,10 @@ struct MeloNXApp: App {
|
|||
if checkForUpdate {
|
||||
checkLatestVersion()
|
||||
}
|
||||
|
||||
print(metalHudEnabler.canMetalHud)
|
||||
|
||||
UserDefaults.standard.set(false, forKey: "lockInApp")
|
||||
}
|
||||
.sheet(isPresented: Binding(
|
||||
get: { showOutOfDateSheet && updateInfo != nil },
|
||||
|
|
|
@ -0,0 +1,330 @@
|
|||
#if 0
|
||||
#elif defined(__arm64__) && __arm64__
|
||||
// Generated by Apple Swift version 6.0.3 effective-5.10 (swiftlang-6.0.3.1.10 clang-1600.0.30.1)
|
||||
#ifndef STOSJIT_SWIFT_H
|
||||
#define STOSJIT_SWIFT_H
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wgcc-compat"
|
||||
|
||||
#if !defined(__has_include)
|
||||
# define __has_include(x) 0
|
||||
#endif
|
||||
#if !defined(__has_attribute)
|
||||
# define __has_attribute(x) 0
|
||||
#endif
|
||||
#if !defined(__has_feature)
|
||||
# define __has_feature(x) 0
|
||||
#endif
|
||||
#if !defined(__has_warning)
|
||||
# define __has_warning(x) 0
|
||||
#endif
|
||||
|
||||
#if __has_include(<swift/objc-prologue.h>)
|
||||
# include <swift/objc-prologue.h>
|
||||
#endif
|
||||
|
||||
#pragma clang diagnostic ignored "-Wauto-import"
|
||||
#if defined(__OBJC__)
|
||||
#include <Foundation/Foundation.h>
|
||||
#endif
|
||||
#if defined(__cplusplus)
|
||||
#include <cstdint>
|
||||
#include <cstddef>
|
||||
#include <cstdbool>
|
||||
#include <cstring>
|
||||
#include <stdlib.h>
|
||||
#include <new>
|
||||
#include <type_traits>
|
||||
#else
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#endif
|
||||
#if defined(__cplusplus)
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wnon-modular-include-in-framework-module"
|
||||
#if defined(__arm64e__) && __has_include(<ptrauth.h>)
|
||||
# include <ptrauth.h>
|
||||
#else
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wreserved-macro-identifier"
|
||||
# ifndef __ptrauth_swift_value_witness_function_pointer
|
||||
# define __ptrauth_swift_value_witness_function_pointer(x)
|
||||
# endif
|
||||
# ifndef __ptrauth_swift_class_method_pointer
|
||||
# define __ptrauth_swift_class_method_pointer(x)
|
||||
# endif
|
||||
#pragma clang diagnostic pop
|
||||
#endif
|
||||
#pragma clang diagnostic pop
|
||||
#endif
|
||||
|
||||
#if !defined(SWIFT_TYPEDEFS)
|
||||
# define SWIFT_TYPEDEFS 1
|
||||
# if __has_include(<uchar.h>)
|
||||
# include <uchar.h>
|
||||
# elif !defined(__cplusplus)
|
||||
typedef uint_least16_t char16_t;
|
||||
typedef uint_least32_t char32_t;
|
||||
# endif
|
||||
typedef float swift_float2 __attribute__((__ext_vector_type__(2)));
|
||||
typedef float swift_float3 __attribute__((__ext_vector_type__(3)));
|
||||
typedef float swift_float4 __attribute__((__ext_vector_type__(4)));
|
||||
typedef double swift_double2 __attribute__((__ext_vector_type__(2)));
|
||||
typedef double swift_double3 __attribute__((__ext_vector_type__(3)));
|
||||
typedef double swift_double4 __attribute__((__ext_vector_type__(4)));
|
||||
typedef int swift_int2 __attribute__((__ext_vector_type__(2)));
|
||||
typedef int swift_int3 __attribute__((__ext_vector_type__(3)));
|
||||
typedef int swift_int4 __attribute__((__ext_vector_type__(4)));
|
||||
typedef unsigned int swift_uint2 __attribute__((__ext_vector_type__(2)));
|
||||
typedef unsigned int swift_uint3 __attribute__((__ext_vector_type__(3)));
|
||||
typedef unsigned int swift_uint4 __attribute__((__ext_vector_type__(4)));
|
||||
#endif
|
||||
|
||||
#if !defined(SWIFT_PASTE)
|
||||
# define SWIFT_PASTE_HELPER(x, y) x##y
|
||||
# define SWIFT_PASTE(x, y) SWIFT_PASTE_HELPER(x, y)
|
||||
#endif
|
||||
#if !defined(SWIFT_METATYPE)
|
||||
# define SWIFT_METATYPE(X) Class
|
||||
#endif
|
||||
#if !defined(SWIFT_CLASS_PROPERTY)
|
||||
# if __has_feature(objc_class_property)
|
||||
# define SWIFT_CLASS_PROPERTY(...) __VA_ARGS__
|
||||
# else
|
||||
# define SWIFT_CLASS_PROPERTY(...)
|
||||
# endif
|
||||
#endif
|
||||
#if !defined(SWIFT_RUNTIME_NAME)
|
||||
# if __has_attribute(objc_runtime_name)
|
||||
# define SWIFT_RUNTIME_NAME(X) __attribute__((objc_runtime_name(X)))
|
||||
# else
|
||||
# define SWIFT_RUNTIME_NAME(X)
|
||||
# endif
|
||||
#endif
|
||||
#if !defined(SWIFT_COMPILE_NAME)
|
||||
# if __has_attribute(swift_name)
|
||||
# define SWIFT_COMPILE_NAME(X) __attribute__((swift_name(X)))
|
||||
# else
|
||||
# define SWIFT_COMPILE_NAME(X)
|
||||
# endif
|
||||
#endif
|
||||
#if !defined(SWIFT_METHOD_FAMILY)
|
||||
# if __has_attribute(objc_method_family)
|
||||
# define SWIFT_METHOD_FAMILY(X) __attribute__((objc_method_family(X)))
|
||||
# else
|
||||
# define SWIFT_METHOD_FAMILY(X)
|
||||
# endif
|
||||
#endif
|
||||
#if !defined(SWIFT_NOESCAPE)
|
||||
# if __has_attribute(noescape)
|
||||
# define SWIFT_NOESCAPE __attribute__((noescape))
|
||||
# else
|
||||
# define SWIFT_NOESCAPE
|
||||
# endif
|
||||
#endif
|
||||
#if !defined(SWIFT_RELEASES_ARGUMENT)
|
||||
# if __has_attribute(ns_consumed)
|
||||
# define SWIFT_RELEASES_ARGUMENT __attribute__((ns_consumed))
|
||||
# else
|
||||
# define SWIFT_RELEASES_ARGUMENT
|
||||
# endif
|
||||
#endif
|
||||
#if !defined(SWIFT_WARN_UNUSED_RESULT)
|
||||
# if __has_attribute(warn_unused_result)
|
||||
# define SWIFT_WARN_UNUSED_RESULT __attribute__((warn_unused_result))
|
||||
# else
|
||||
# define SWIFT_WARN_UNUSED_RESULT
|
||||
# endif
|
||||
#endif
|
||||
#if !defined(SWIFT_NORETURN)
|
||||
# if __has_attribute(noreturn)
|
||||
# define SWIFT_NORETURN __attribute__((noreturn))
|
||||
# else
|
||||
# define SWIFT_NORETURN
|
||||
# endif
|
||||
#endif
|
||||
#if !defined(SWIFT_CLASS_EXTRA)
|
||||
# define SWIFT_CLASS_EXTRA
|
||||
#endif
|
||||
#if !defined(SWIFT_PROTOCOL_EXTRA)
|
||||
# define SWIFT_PROTOCOL_EXTRA
|
||||
#endif
|
||||
#if !defined(SWIFT_ENUM_EXTRA)
|
||||
# define SWIFT_ENUM_EXTRA
|
||||
#endif
|
||||
#if !defined(SWIFT_CLASS)
|
||||
# if __has_attribute(objc_subclassing_restricted)
|
||||
# define SWIFT_CLASS(SWIFT_NAME) SWIFT_RUNTIME_NAME(SWIFT_NAME) __attribute__((objc_subclassing_restricted)) SWIFT_CLASS_EXTRA
|
||||
# define SWIFT_CLASS_NAMED(SWIFT_NAME) __attribute__((objc_subclassing_restricted)) SWIFT_COMPILE_NAME(SWIFT_NAME) SWIFT_CLASS_EXTRA
|
||||
# else
|
||||
# define SWIFT_CLASS(SWIFT_NAME) SWIFT_RUNTIME_NAME(SWIFT_NAME) SWIFT_CLASS_EXTRA
|
||||
# define SWIFT_CLASS_NAMED(SWIFT_NAME) SWIFT_COMPILE_NAME(SWIFT_NAME) SWIFT_CLASS_EXTRA
|
||||
# endif
|
||||
#endif
|
||||
#if !defined(SWIFT_RESILIENT_CLASS)
|
||||
# if __has_attribute(objc_class_stub)
|
||||
# define SWIFT_RESILIENT_CLASS(SWIFT_NAME) SWIFT_CLASS(SWIFT_NAME) __attribute__((objc_class_stub))
|
||||
# define SWIFT_RESILIENT_CLASS_NAMED(SWIFT_NAME) __attribute__((objc_class_stub)) SWIFT_CLASS_NAMED(SWIFT_NAME)
|
||||
# else
|
||||
# define SWIFT_RESILIENT_CLASS(SWIFT_NAME) SWIFT_CLASS(SWIFT_NAME)
|
||||
# define SWIFT_RESILIENT_CLASS_NAMED(SWIFT_NAME) SWIFT_CLASS_NAMED(SWIFT_NAME)
|
||||
# endif
|
||||
#endif
|
||||
#if !defined(SWIFT_PROTOCOL)
|
||||
# define SWIFT_PROTOCOL(SWIFT_NAME) SWIFT_RUNTIME_NAME(SWIFT_NAME) SWIFT_PROTOCOL_EXTRA
|
||||
# define SWIFT_PROTOCOL_NAMED(SWIFT_NAME) SWIFT_COMPILE_NAME(SWIFT_NAME) SWIFT_PROTOCOL_EXTRA
|
||||
#endif
|
||||
#if !defined(SWIFT_EXTENSION)
|
||||
# define SWIFT_EXTENSION(M) SWIFT_PASTE(M##_Swift_, __LINE__)
|
||||
#endif
|
||||
#if !defined(OBJC_DESIGNATED_INITIALIZER)
|
||||
# if __has_attribute(objc_designated_initializer)
|
||||
# define OBJC_DESIGNATED_INITIALIZER __attribute__((objc_designated_initializer))
|
||||
# else
|
||||
# define OBJC_DESIGNATED_INITIALIZER
|
||||
# endif
|
||||
#endif
|
||||
#if !defined(SWIFT_ENUM_ATTR)
|
||||
# if __has_attribute(enum_extensibility)
|
||||
# define SWIFT_ENUM_ATTR(_extensibility) __attribute__((enum_extensibility(_extensibility)))
|
||||
# else
|
||||
# define SWIFT_ENUM_ATTR(_extensibility)
|
||||
# endif
|
||||
#endif
|
||||
#if !defined(SWIFT_ENUM)
|
||||
# define SWIFT_ENUM(_type, _name, _extensibility) enum _name : _type _name; enum SWIFT_ENUM_ATTR(_extensibility) SWIFT_ENUM_EXTRA _name : _type
|
||||
# if __has_feature(generalized_swift_name)
|
||||
# define SWIFT_ENUM_NAMED(_type, _name, SWIFT_NAME, _extensibility) enum _name : _type _name SWIFT_COMPILE_NAME(SWIFT_NAME); enum SWIFT_COMPILE_NAME(SWIFT_NAME) SWIFT_ENUM_ATTR(_extensibility) SWIFT_ENUM_EXTRA _name : _type
|
||||
# else
|
||||
# define SWIFT_ENUM_NAMED(_type, _name, SWIFT_NAME, _extensibility) SWIFT_ENUM(_type, _name, _extensibility)
|
||||
# endif
|
||||
#endif
|
||||
#if !defined(SWIFT_UNAVAILABLE)
|
||||
# define SWIFT_UNAVAILABLE __attribute__((unavailable))
|
||||
#endif
|
||||
#if !defined(SWIFT_UNAVAILABLE_MSG)
|
||||
# define SWIFT_UNAVAILABLE_MSG(msg) __attribute__((unavailable(msg)))
|
||||
#endif
|
||||
#if !defined(SWIFT_AVAILABILITY)
|
||||
# define SWIFT_AVAILABILITY(plat, ...) __attribute__((availability(plat, __VA_ARGS__)))
|
||||
#endif
|
||||
#if !defined(SWIFT_WEAK_IMPORT)
|
||||
# define SWIFT_WEAK_IMPORT __attribute__((weak_import))
|
||||
#endif
|
||||
#if !defined(SWIFT_DEPRECATED)
|
||||
# define SWIFT_DEPRECATED __attribute__((deprecated))
|
||||
#endif
|
||||
#if !defined(SWIFT_DEPRECATED_MSG)
|
||||
# define SWIFT_DEPRECATED_MSG(...) __attribute__((deprecated(__VA_ARGS__)))
|
||||
#endif
|
||||
#if !defined(SWIFT_DEPRECATED_OBJC)
|
||||
# if __has_feature(attribute_diagnose_if_objc)
|
||||
# define SWIFT_DEPRECATED_OBJC(Msg) __attribute__((diagnose_if(1, Msg, "warning")))
|
||||
# else
|
||||
# define SWIFT_DEPRECATED_OBJC(Msg) SWIFT_DEPRECATED_MSG(Msg)
|
||||
# endif
|
||||
#endif
|
||||
#if defined(__OBJC__)
|
||||
#if !defined(IBSegueAction)
|
||||
# define IBSegueAction
|
||||
#endif
|
||||
#endif
|
||||
#if !defined(SWIFT_EXTERN)
|
||||
# if defined(__cplusplus)
|
||||
# define SWIFT_EXTERN extern "C"
|
||||
# else
|
||||
# define SWIFT_EXTERN extern
|
||||
# endif
|
||||
#endif
|
||||
#if !defined(SWIFT_CALL)
|
||||
# define SWIFT_CALL __attribute__((swiftcall))
|
||||
#endif
|
||||
#if !defined(SWIFT_INDIRECT_RESULT)
|
||||
# define SWIFT_INDIRECT_RESULT __attribute__((swift_indirect_result))
|
||||
#endif
|
||||
#if !defined(SWIFT_CONTEXT)
|
||||
# define SWIFT_CONTEXT __attribute__((swift_context))
|
||||
#endif
|
||||
#if !defined(SWIFT_ERROR_RESULT)
|
||||
# define SWIFT_ERROR_RESULT __attribute__((swift_error_result))
|
||||
#endif
|
||||
#if defined(__cplusplus)
|
||||
# define SWIFT_NOEXCEPT noexcept
|
||||
#else
|
||||
# define SWIFT_NOEXCEPT
|
||||
#endif
|
||||
#if !defined(SWIFT_C_INLINE_THUNK)
|
||||
# if __has_attribute(always_inline)
|
||||
# if __has_attribute(nodebug)
|
||||
# define SWIFT_C_INLINE_THUNK inline __attribute__((always_inline)) __attribute__((nodebug))
|
||||
# else
|
||||
# define SWIFT_C_INLINE_THUNK inline __attribute__((always_inline))
|
||||
# endif
|
||||
# else
|
||||
# define SWIFT_C_INLINE_THUNK inline
|
||||
# endif
|
||||
#endif
|
||||
#if defined(_WIN32)
|
||||
#if !defined(SWIFT_IMPORT_STDLIB_SYMBOL)
|
||||
# define SWIFT_IMPORT_STDLIB_SYMBOL __declspec(dllimport)
|
||||
#endif
|
||||
#else
|
||||
#if !defined(SWIFT_IMPORT_STDLIB_SYMBOL)
|
||||
# define SWIFT_IMPORT_STDLIB_SYMBOL
|
||||
#endif
|
||||
#endif
|
||||
#if defined(__OBJC__)
|
||||
#if __has_feature(objc_modules)
|
||||
#if __has_warning("-Watimport-in-framework-header")
|
||||
#pragma clang diagnostic ignored "-Watimport-in-framework-header"
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#endif
|
||||
#pragma clang diagnostic ignored "-Wproperty-attribute-mismatch"
|
||||
#pragma clang diagnostic ignored "-Wduplicate-method-arg"
|
||||
#if __has_warning("-Wpragma-clang-attribute")
|
||||
# pragma clang diagnostic ignored "-Wpragma-clang-attribute"
|
||||
#endif
|
||||
#pragma clang diagnostic ignored "-Wunknown-pragmas"
|
||||
#pragma clang diagnostic ignored "-Wnullability"
|
||||
#pragma clang diagnostic ignored "-Wdollar-in-identifier-extension"
|
||||
#pragma clang diagnostic ignored "-Wunsafe-buffer-usage"
|
||||
|
||||
#if __has_attribute(external_source_symbol)
|
||||
# pragma push_macro("any")
|
||||
# undef any
|
||||
# pragma clang attribute push(__attribute__((external_source_symbol(language="Swift", defined_in="StosJIT",generated_declaration))), apply_to=any(function,enum,objc_interface,objc_category,objc_protocol))
|
||||
# pragma pop_macro("any")
|
||||
#endif
|
||||
|
||||
#if defined(__OBJC__)
|
||||
|
||||
SWIFT_EXTERN char * _Nullable attach(int32_t pid) SWIFT_NOEXCEPT SWIFT_WARN_UNUSED_RESULT;
|
||||
|
||||
|
||||
SWIFT_EXTERN char * _Nullable debugattachanddetachApp(char * _Nonnull bundleId) SWIFT_NOEXCEPT SWIFT_WARN_UNUSED_RESULT;
|
||||
|
||||
|
||||
SWIFT_EXTERN void detach(void) SWIFT_NOEXCEPT;
|
||||
|
||||
|
||||
SWIFT_EXTERN void loop_heartbeat(void) SWIFT_NOEXCEPT;
|
||||
|
||||
|
||||
SWIFT_EXTERN BOOL writeZeroToMemory(uint64_t addr, int32_t length) SWIFT_NOEXCEPT SWIFT_WARN_UNUSED_RESULT;
|
||||
|
||||
#endif
|
||||
#if __has_attribute(external_source_symbol)
|
||||
# pragma clang attribute pop
|
||||
#endif
|
||||
#if defined(__cplusplus)
|
||||
#endif
|
||||
#pragma clang diagnostic pop
|
||||
#endif
|
||||
|
||||
#else
|
||||
#error unsupported Swift architecture
|
||||
#endif
|
|
@ -0,0 +1,19 @@
|
|||
//
|
||||
// StosJIT.h
|
||||
// StosJIT
|
||||
//
|
||||
// Created by Stossy11 on 10/05/2025.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <StosJIT/idevice.h>
|
||||
|
||||
//! Project version number for StosJIT.
|
||||
FOUNDATION_EXPORT double StosJITVersionNumber;
|
||||
|
||||
//! Project version string for StosJIT.
|
||||
FOUNDATION_EXPORT const unsigned char StosJITVersionString[];
|
||||
|
||||
// In this header, you should import all the public headers of your framework using statements like #import <StosJIT/PublicHeader.h>
|
||||
|
||||
|
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
Binary file not shown.
BIN
src/MeloNX/MeloNX/Dependencies/Dynamic Libraries/StosJIT.framework/StosJIT
Executable file
BIN
src/MeloNX/MeloNX/Dependencies/Dynamic Libraries/StosJIT.framework/StosJIT
Executable file
Binary file not shown.
|
@ -0,0 +1,201 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>files</key>
|
||||
<dict>
|
||||
<key>Headers/StosJIT-Swift.h</key>
|
||||
<data>
|
||||
h9vaTwhC6FlnyKmIkaxLQGlFd1g=
|
||||
</data>
|
||||
<key>Headers/StosJIT.h</key>
|
||||
<data>
|
||||
ggHr5wlLNIIPydwUL9Vxm6abxjo=
|
||||
</data>
|
||||
<key>Headers/idevice.h</key>
|
||||
<data>
|
||||
mHDz7368FsBID56/epJ2NgIkha4=
|
||||
</data>
|
||||
<key>Headers/plist.h</key>
|
||||
<data>
|
||||
bL/f0MQDpLfvIcI1zxPwMuJ/PfI=
|
||||
</data>
|
||||
<key>Info.plist</key>
|
||||
<data>
|
||||
ZTTwPKlta/gjXAr1HIHmyAxeU4E=
|
||||
</data>
|
||||
<key>Modules/StosJIT.swiftmodule/Project/arm64-apple-ios.swiftsourceinfo</key>
|
||||
<data>
|
||||
2mJoWBgg56N+3OxKfIDMLZFNHVk=
|
||||
</data>
|
||||
<key>Modules/StosJIT.swiftmodule/arm64-apple-ios.abi.json</key>
|
||||
<data>
|
||||
gcwBsH4BgyFY4sVtNt+/xOKS3vY=
|
||||
</data>
|
||||
<key>Modules/StosJIT.swiftmodule/arm64-apple-ios.swiftdoc</key>
|
||||
<data>
|
||||
YPtkDrAuBiPPEp4ZdRdBVlFXnRM=
|
||||
</data>
|
||||
<key>Modules/StosJIT.swiftmodule/arm64-apple-ios.swiftmodule</key>
|
||||
<data>
|
||||
9cIInnjJzJFtY+CZm2iNo5qL3MQ=
|
||||
</data>
|
||||
<key>Modules/module.modulemap</key>
|
||||
<data>
|
||||
cnpvYzvLIwWcxkQodj5uLbHkyRk=
|
||||
</data>
|
||||
</dict>
|
||||
<key>files2</key>
|
||||
<dict>
|
||||
<key>Headers/StosJIT-Swift.h</key>
|
||||
<dict>
|
||||
<key>hash2</key>
|
||||
<data>
|
||||
1obIr4IjMvtcyNyYIV/Nh/5wahcA1cFjc4n4XVlNt2I=
|
||||
</data>
|
||||
</dict>
|
||||
<key>Headers/StosJIT.h</key>
|
||||
<dict>
|
||||
<key>hash2</key>
|
||||
<data>
|
||||
yY9KyrRdOYRdlb7G6wVMU2hogasXMjwV5r8jUIk44ok=
|
||||
</data>
|
||||
</dict>
|
||||
<key>Headers/idevice.h</key>
|
||||
<dict>
|
||||
<key>hash2</key>
|
||||
<data>
|
||||
zR9/TB9Dnv3uRC8qqGvaQ6c2yyOFUURmrHKLdEiUh/g=
|
||||
</data>
|
||||
</dict>
|
||||
<key>Headers/plist.h</key>
|
||||
<dict>
|
||||
<key>hash2</key>
|
||||
<data>
|
||||
yFbGsiXBBp91tfsSFtS0Utt2Gpc3MEDFiMVXKG9q1rs=
|
||||
</data>
|
||||
</dict>
|
||||
<key>Modules/StosJIT.swiftmodule/Project/arm64-apple-ios.swiftsourceinfo</key>
|
||||
<dict>
|
||||
<key>hash2</key>
|
||||
<data>
|
||||
sZBe57nozztJzv83RPLjKIRYGSQmeE7XYCqr63xZONM=
|
||||
</data>
|
||||
</dict>
|
||||
<key>Modules/StosJIT.swiftmodule/arm64-apple-ios.abi.json</key>
|
||||
<dict>
|
||||
<key>hash2</key>
|
||||
<data>
|
||||
Qnesa0n4URGWAopawg9bGx36dUwkYV00BoCJ8LFzlyg=
|
||||
</data>
|
||||
</dict>
|
||||
<key>Modules/StosJIT.swiftmodule/arm64-apple-ios.swiftdoc</key>
|
||||
<dict>
|
||||
<key>hash2</key>
|
||||
<data>
|
||||
k7F2Xs2hh9iMbK8IE8TMtN6gjQ9kWs30NUKHeupq6VE=
|
||||
</data>
|
||||
</dict>
|
||||
<key>Modules/StosJIT.swiftmodule/arm64-apple-ios.swiftmodule</key>
|
||||
<dict>
|
||||
<key>hash2</key>
|
||||
<data>
|
||||
gMDYNHcBPCNwZw2A5mEUiCyYAS9VhtQG0z+/WqAUrOQ=
|
||||
</data>
|
||||
</dict>
|
||||
<key>Modules/module.modulemap</key>
|
||||
<dict>
|
||||
<key>hash2</key>
|
||||
<data>
|
||||
FGwGKs5SNvpCyiIWiOP4eml9m2e3KISmtCJVtNnUnUc=
|
||||
</data>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>rules</key>
|
||||
<dict>
|
||||
<key>^.*</key>
|
||||
<true/>
|
||||
<key>^.*\.lproj/</key>
|
||||
<dict>
|
||||
<key>optional</key>
|
||||
<true/>
|
||||
<key>weight</key>
|
||||
<real>1000</real>
|
||||
</dict>
|
||||
<key>^.*\.lproj/locversion.plist$</key>
|
||||
<dict>
|
||||
<key>omit</key>
|
||||
<true/>
|
||||
<key>weight</key>
|
||||
<real>1100</real>
|
||||
</dict>
|
||||
<key>^Base\.lproj/</key>
|
||||
<dict>
|
||||
<key>weight</key>
|
||||
<real>1010</real>
|
||||
</dict>
|
||||
<key>^version.plist$</key>
|
||||
<true/>
|
||||
</dict>
|
||||
<key>rules2</key>
|
||||
<dict>
|
||||
<key>.*\.dSYM($|/)</key>
|
||||
<dict>
|
||||
<key>weight</key>
|
||||
<real>11</real>
|
||||
</dict>
|
||||
<key>^(.*/)?\.DS_Store$</key>
|
||||
<dict>
|
||||
<key>omit</key>
|
||||
<true/>
|
||||
<key>weight</key>
|
||||
<real>2000</real>
|
||||
</dict>
|
||||
<key>^.*</key>
|
||||
<true/>
|
||||
<key>^.*\.lproj/</key>
|
||||
<dict>
|
||||
<key>optional</key>
|
||||
<true/>
|
||||
<key>weight</key>
|
||||
<real>1000</real>
|
||||
</dict>
|
||||
<key>^.*\.lproj/locversion.plist$</key>
|
||||
<dict>
|
||||
<key>omit</key>
|
||||
<true/>
|
||||
<key>weight</key>
|
||||
<real>1100</real>
|
||||
</dict>
|
||||
<key>^Base\.lproj/</key>
|
||||
<dict>
|
||||
<key>weight</key>
|
||||
<real>1010</real>
|
||||
</dict>
|
||||
<key>^Info\.plist$</key>
|
||||
<dict>
|
||||
<key>omit</key>
|
||||
<true/>
|
||||
<key>weight</key>
|
||||
<real>20</real>
|
||||
</dict>
|
||||
<key>^PkgInfo$</key>
|
||||
<dict>
|
||||
<key>omit</key>
|
||||
<true/>
|
||||
<key>weight</key>
|
||||
<real>20</real>
|
||||
</dict>
|
||||
<key>^embedded\.provisionprofile$</key>
|
||||
<dict>
|
||||
<key>weight</key>
|
||||
<real>20</real>
|
||||
</dict>
|
||||
<key>^version\.plist$</key>
|
||||
<dict>
|
||||
<key>weight</key>
|
||||
<real>20</real>
|
||||
</dict>
|
||||
</dict>
|
||||
</dict>
|
||||
</plist>
|
622
src/Ryujinx.Cpu/LightningJit/Cache/WriteZeroCache.cs
Normal file
622
src/Ryujinx.Cpu/LightningJit/Cache/WriteZeroCache.cs
Normal file
|
@ -0,0 +1,622 @@
|
|||
using ARMeilleure.Memory;
|
||||
using Ryujinx.Common;
|
||||
using Ryujinx.Memory;
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
|
||||
namespace Ryujinx.Cpu.LightningJit.Cache
|
||||
{
|
||||
class WriteZeroCache : IDisposable
|
||||
{
|
||||
private const int CodeAlignment = 4;
|
||||
private const int InitialCacheSize = 2 * 1024 * 1024;
|
||||
private const int GrowthCacheSize = 2 * 1024 * 1024;
|
||||
private const int MaxSharedCacheSize = 512 * 1024 * 1024;
|
||||
private const int MaxLocalCacheSize = 128 * 1024 * 1024;
|
||||
|
||||
[DllImport("StosJIT.framework/StosJIT", EntryPoint = "writeZeroToMemory")]
|
||||
public static extern bool WriteZeroToMemory(ulong addr, int length);
|
||||
|
||||
// How many calls to the same function we allow until we pad the shared cache to force the function to become available there
|
||||
// and allow the guest to take the fast path.
|
||||
private const int MinCallsForPad = 8;
|
||||
|
||||
private class MemoryCache : IDisposable
|
||||
{
|
||||
private readonly ReservedRegion _region;
|
||||
private readonly CacheMemoryAllocator _cacheAllocator;
|
||||
public readonly IJitMemoryAllocator Allocator;
|
||||
private readonly ulong _maxSize;
|
||||
private ulong _currentSize;
|
||||
|
||||
private readonly Dictionary<int, HashSet<int>> _reusePages;
|
||||
private readonly object _reuselock = new object();
|
||||
|
||||
public CacheMemoryAllocator CacheAllocator => _cacheAllocator;
|
||||
public IntPtr Pointer => _region.Block.Pointer;
|
||||
public ulong CurrentSize => _currentSize;
|
||||
public ulong MaxSize => _maxSize;
|
||||
|
||||
public MemoryCache(IJitMemoryAllocator allocator, ulong maxSize)
|
||||
{
|
||||
Allocator = allocator;
|
||||
_maxSize = maxSize;
|
||||
_currentSize = InitialCacheSize;
|
||||
|
||||
|
||||
_region = new(allocator, maxSize);
|
||||
_cacheAllocator = new((int)maxSize);
|
||||
|
||||
_reusePages = new Dictionary<int, HashSet<int>>();
|
||||
|
||||
_region.Block.MapAsRw(0, _currentSize);
|
||||
_region.ExpandIfNeeded(_currentSize);
|
||||
|
||||
WriteZeroToMemory((ulong)_region.Block.Pointer.ToInt64(), (int)_currentSize);
|
||||
}
|
||||
|
||||
public bool TryGetReusablePage(int size, out int offset)
|
||||
{
|
||||
lock (_reuselock)
|
||||
{
|
||||
if (_reusePages.TryGetValue(size, out var exactOffsets) && exactOffsets.Count > 0)
|
||||
{
|
||||
offset = exactOffsets.First();
|
||||
exactOffsets.Remove(offset);
|
||||
return true;
|
||||
}
|
||||
|
||||
var largerSizes = _reusePages.Where(kvp => kvp.Key > size && kvp.Value.Count > 0)
|
||||
.OrderBy(kvp => kvp.Key)
|
||||
.FirstOrDefault();
|
||||
|
||||
if (largerSizes.Value != null && largerSizes.Value.Count > 0)
|
||||
{
|
||||
int largerSize = largerSizes.Key;
|
||||
var largerOffsets = largerSizes.Value;
|
||||
|
||||
offset = largerOffsets.First();
|
||||
largerOffsets.Remove(offset);
|
||||
|
||||
int remainingSize = largerSize - size;
|
||||
if (remainingSize > 0)
|
||||
{
|
||||
AddReusablePage(offset + size, remainingSize);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
offset = -1;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public void AddReusablePage(int offset, int size)
|
||||
{
|
||||
if (size < (int)MemoryBlock.GetPageSize())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
lock (_reuselock)
|
||||
{
|
||||
if (!_reusePages.TryGetValue(size, out var offsets))
|
||||
{
|
||||
offsets = new HashSet<int>();
|
||||
_reusePages[size] = offsets;
|
||||
}
|
||||
offsets.Add(offset);
|
||||
}
|
||||
}
|
||||
|
||||
public int Allocate(int codeSize)
|
||||
{
|
||||
codeSize = AlignCodeSize(codeSize);
|
||||
|
||||
if (codeSize >= (int)MemoryBlock.GetPageSize() &&
|
||||
(codeSize % (int)MemoryBlock.GetPageSize() == 0) &&
|
||||
TryGetReusablePage(codeSize, out int reuseOffset))
|
||||
{
|
||||
ReprotectAsRw(reuseOffset, codeSize);
|
||||
return reuseOffset;
|
||||
}
|
||||
|
||||
int allocOffset = _cacheAllocator.Allocate(codeSize);
|
||||
|
||||
if (allocOffset < 0)
|
||||
{
|
||||
throw new OutOfMemoryException("JIT Cache exhausted.");
|
||||
}
|
||||
|
||||
|
||||
ulong requiredSize = (ulong)allocOffset + (ulong)codeSize;
|
||||
if (requiredSize > _currentSize)
|
||||
{
|
||||
ulong neededGrowth = requiredSize - _currentSize;
|
||||
ulong growthIncrements = (neededGrowth + GrowthCacheSize - 1) / GrowthCacheSize;
|
||||
ulong newSize = _currentSize + (growthIncrements * GrowthCacheSize);
|
||||
|
||||
newSize = Math.Min(newSize, _maxSize);
|
||||
|
||||
if (newSize <= _currentSize || requiredSize > newSize)
|
||||
{
|
||||
throw new OutOfMemoryException("JIT Cache exhausted, cannot grow further.");
|
||||
}
|
||||
|
||||
_region.Block.MapAsRw(_currentSize, newSize - _currentSize);
|
||||
_region.ExpandIfNeeded(newSize);
|
||||
|
||||
WriteZeroToMemory((ulong)(_region.Block.Pointer.ToInt64() + (long)_currentSize), (int)(newSize - _currentSize));
|
||||
|
||||
_currentSize = newSize;
|
||||
}
|
||||
|
||||
return allocOffset;
|
||||
}
|
||||
|
||||
public void Free(int offset, int size)
|
||||
{
|
||||
if (size >= (int)MemoryBlock.GetPageSize() && (size % (int)MemoryBlock.GetPageSize() == 0) &&
|
||||
(offset % (int)MemoryBlock.GetPageSize() == 0))
|
||||
{
|
||||
AddReusablePage(offset, size);
|
||||
}
|
||||
else
|
||||
{
|
||||
_cacheAllocator.Free(offset, size);
|
||||
}
|
||||
}
|
||||
|
||||
public void ReprotectAsRw(int offset, int size)
|
||||
{
|
||||
Debug.Assert(offset >= 0 && (offset & (int)(MemoryBlock.GetPageSize() - 1)) == 0);
|
||||
Debug.Assert(size > 0 && (size & (int)(MemoryBlock.GetPageSize() - 1)) == 0);
|
||||
|
||||
_region.Block.MapAsRw((ulong)offset, (ulong)size);
|
||||
}
|
||||
|
||||
public void ReprotectAsRx(int offset, int size)
|
||||
{
|
||||
Debug.Assert(offset >= 0 && (offset & (int)(MemoryBlock.GetPageSize() - 1)) == 0);
|
||||
Debug.Assert(size > 0 && (size & (int)(MemoryBlock.GetPageSize() - 1)) == 0);
|
||||
|
||||
_region.Block.MapAsRx((ulong)offset, (ulong)size);
|
||||
|
||||
if (OperatingSystem.IsMacOS() || OperatingSystem.IsIOS())
|
||||
{
|
||||
JitSupportDarwin.SysIcacheInvalidate(_region.Block.Pointer + offset, size);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new PlatformNotSupportedException();
|
||||
}
|
||||
}
|
||||
|
||||
public void ClearReusePool()
|
||||
{
|
||||
lock (_reuselock)
|
||||
{
|
||||
_reusePages.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
private static int AlignCodeSize(int codeSize)
|
||||
{
|
||||
return checked(codeSize + (CodeAlignment - 1)) & ~(CodeAlignment - 1);
|
||||
}
|
||||
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
ClearReusePool();
|
||||
_region.Dispose();
|
||||
_cacheAllocator.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(disposing: true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
}
|
||||
|
||||
private readonly IStackWalker _stackWalker;
|
||||
private readonly Translator _translator;
|
||||
private readonly List<MemoryCache> _sharedCaches;
|
||||
private readonly List<MemoryCache> _localCaches;
|
||||
private readonly Dictionary<ulong, PageAlignedRangeList> _pendingMaps;
|
||||
private readonly object _lock;
|
||||
|
||||
class ThreadLocalCacheEntry
|
||||
{
|
||||
public readonly int Offset;
|
||||
public readonly int Size;
|
||||
public readonly IntPtr FuncPtr;
|
||||
public readonly int CacheIndex;
|
||||
private int _useCount;
|
||||
|
||||
public ThreadLocalCacheEntry(int offset, int size, IntPtr funcPtr, int cacheIndex)
|
||||
{
|
||||
Offset = offset;
|
||||
Size = size;
|
||||
FuncPtr = funcPtr;
|
||||
CacheIndex = cacheIndex;
|
||||
_useCount = 0;
|
||||
}
|
||||
|
||||
public int IncrementUseCount()
|
||||
{
|
||||
return ++_useCount;
|
||||
}
|
||||
}
|
||||
|
||||
[ThreadStatic]
|
||||
private static Dictionary<ulong, ThreadLocalCacheEntry> _threadLocalCache;
|
||||
|
||||
public WriteZeroCache(IJitMemoryAllocator allocator, IStackWalker stackWalker, Translator translator)
|
||||
{
|
||||
_stackWalker = stackWalker;
|
||||
_translator = translator;
|
||||
_sharedCaches = new List<MemoryCache> { new(allocator, MaxSharedCacheSize) };
|
||||
_localCaches = new List<MemoryCache> { new(allocator, MaxLocalCacheSize) };
|
||||
_pendingMaps = new Dictionary<ulong, PageAlignedRangeList>();
|
||||
_lock = new();
|
||||
}
|
||||
|
||||
private PageAlignedRangeList GetPendingMapForCache(int cacheIndex)
|
||||
{
|
||||
ulong cacheKey = (ulong)cacheIndex;
|
||||
if (!_pendingMaps.TryGetValue(cacheKey, out var pendingMap))
|
||||
{
|
||||
pendingMap = new PageAlignedRangeList(
|
||||
(offset, size) => _sharedCaches[cacheIndex].ReprotectAsRx(offset, size),
|
||||
(address, func) => RegisterFunction(address, func));
|
||||
_pendingMaps[cacheKey] = pendingMap;
|
||||
}
|
||||
return pendingMap;
|
||||
}
|
||||
|
||||
private bool HasInAnyPendingMap(ulong guestAddress)
|
||||
{
|
||||
foreach (var pendingMap in _pendingMaps.Values)
|
||||
{
|
||||
if (pendingMap.Has(guestAddress))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private int AllocateInSharedCache(int codeLength)
|
||||
{
|
||||
for (int i = 0; i < _sharedCaches.Count; i++)
|
||||
{
|
||||
try
|
||||
{
|
||||
return (i << 28) | _sharedCaches[i].Allocate(codeLength);
|
||||
}
|
||||
catch (OutOfMemoryException)
|
||||
{
|
||||
// Try next cache
|
||||
}
|
||||
}
|
||||
|
||||
// All existing caches are full, create a new one
|
||||
lock (_lock)
|
||||
{
|
||||
var allocator = _sharedCaches[0].Allocator;
|
||||
_sharedCaches.Add(new(allocator, MaxSharedCacheSize));
|
||||
return (_sharedCaches.Count - 1) << 28 | _sharedCaches[_sharedCaches.Count - 1].Allocate(codeLength);
|
||||
}
|
||||
}
|
||||
|
||||
private int AllocateInLocalCache(int codeLength)
|
||||
{
|
||||
for (int i = 0; i < _localCaches.Count; i++)
|
||||
{
|
||||
try
|
||||
{
|
||||
return (i << 28) | _localCaches[i].Allocate(codeLength);
|
||||
}
|
||||
catch (OutOfMemoryException)
|
||||
{
|
||||
// Try next cache
|
||||
}
|
||||
}
|
||||
|
||||
lock (_lock)
|
||||
{
|
||||
var allocator = _localCaches[0].Allocator;
|
||||
_localCaches.Add(new(allocator, MaxLocalCacheSize));
|
||||
return (_localCaches.Count - 1) << 28 | _localCaches[_localCaches.Count - 1].Allocate(codeLength);
|
||||
}
|
||||
}
|
||||
|
||||
private static (int cacheIndex, int offset) SplitCacheOffset(int combinedOffset)
|
||||
{
|
||||
return (combinedOffset >> 28, combinedOffset & 0xFFFFFFF);
|
||||
}
|
||||
|
||||
public unsafe IntPtr Map(IntPtr framePointer, ReadOnlySpan<byte> code, ulong guestAddress, ulong guestSize)
|
||||
{
|
||||
if (TryGetThreadLocalFunction(guestAddress, out IntPtr funcPtr))
|
||||
{
|
||||
return funcPtr;
|
||||
}
|
||||
|
||||
lock (_lock)
|
||||
{
|
||||
if (!HasInAnyPendingMap(guestAddress) && !_translator.Functions.ContainsKey(guestAddress))
|
||||
{
|
||||
int combinedOffset = AllocateInSharedCache(code.Length);
|
||||
var (cacheIndex, funcOffset) = SplitCacheOffset(combinedOffset);
|
||||
|
||||
MemoryCache cache = _sharedCaches[cacheIndex];
|
||||
funcPtr = cache.Pointer + funcOffset;
|
||||
|
||||
code.CopyTo(new Span<byte>((void*)funcPtr, code.Length));
|
||||
|
||||
TranslatedFunction function = new(funcPtr, guestSize);
|
||||
|
||||
GetPendingMapForCache(cacheIndex).Add(funcOffset, code.Length, guestAddress, function);
|
||||
}
|
||||
|
||||
ClearThreadLocalCache(framePointer);
|
||||
|
||||
return AddThreadLocalFunction(code, guestAddress);
|
||||
}
|
||||
}
|
||||
|
||||
public unsafe IntPtr MapPageAligned(ReadOnlySpan<byte> code)
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
int cacheIndex;
|
||||
int funcOffset;
|
||||
IntPtr mappedFuncPtr = IntPtr.Zero;
|
||||
|
||||
for (cacheIndex = 0; cacheIndex < _sharedCaches.Count; cacheIndex++)
|
||||
{
|
||||
try
|
||||
{
|
||||
var pendingMap = GetPendingMapForCache(cacheIndex);
|
||||
|
||||
pendingMap.Pad(_sharedCaches[cacheIndex].CacheAllocator);
|
||||
|
||||
int sizeAligned = BitUtils.AlignUp(code.Length, (int)MemoryBlock.GetPageSize());
|
||||
funcOffset = _sharedCaches[cacheIndex].Allocate(sizeAligned);
|
||||
|
||||
Debug.Assert((funcOffset & ((int)MemoryBlock.GetPageSize() - 1)) == 0);
|
||||
|
||||
IntPtr funcPtr1 = _sharedCaches[cacheIndex].Pointer + funcOffset;
|
||||
|
||||
code.CopyTo(new Span<byte>((void*)funcPtr1, code.Length));
|
||||
|
||||
_sharedCaches[cacheIndex].ReprotectAsRx(funcOffset, sizeAligned);
|
||||
|
||||
return funcPtr1;
|
||||
}
|
||||
catch (OutOfMemoryException)
|
||||
{
|
||||
// Try next cache
|
||||
}
|
||||
}
|
||||
|
||||
var allocator = _sharedCaches[0].Allocator;
|
||||
var newCache = new MemoryCache(allocator, MaxSharedCacheSize);
|
||||
_sharedCaches.Add(newCache);
|
||||
cacheIndex = _sharedCaches.Count - 1;
|
||||
|
||||
var newPendingMap = GetPendingMapForCache(cacheIndex);
|
||||
|
||||
newPendingMap.Pad(newCache.CacheAllocator);
|
||||
|
||||
int newSizeAligned = BitUtils.AlignUp(code.Length, (int)MemoryBlock.GetPageSize());
|
||||
funcOffset = newCache.Allocate(newSizeAligned);
|
||||
|
||||
Debug.Assert((funcOffset & ((int)MemoryBlock.GetPageSize() - 1)) == 0);
|
||||
|
||||
IntPtr funcPtr = newCache.Pointer + funcOffset;
|
||||
code.CopyTo(new Span<byte>((void*)funcPtr, code.Length));
|
||||
|
||||
newCache.ReprotectAsRx(funcOffset, newSizeAligned);
|
||||
|
||||
return funcPtr;
|
||||
}
|
||||
}
|
||||
|
||||
private bool TryGetThreadLocalFunction(ulong guestAddress, out IntPtr funcPtr)
|
||||
{
|
||||
if ((_threadLocalCache ??= new()).TryGetValue(guestAddress, out var entry))
|
||||
{
|
||||
if (entry.IncrementUseCount() >= MinCallsForPad)
|
||||
{
|
||||
// Function is being called often, let's make it available in the shared cache so that the guest code
|
||||
// can take the fast path and stop calling the emulator to get the function from the thread local cache.
|
||||
// To do that we pad all "pending" function until they complete a page of memory, allowing us to reprotect them as RX.
|
||||
|
||||
lock (_lock)
|
||||
{
|
||||
foreach (var pendingMap in _pendingMaps.Values)
|
||||
{
|
||||
// Get the cache index from the pendingMap key
|
||||
if (_pendingMaps.FirstOrDefault(x => x.Value == pendingMap).Key is ulong cacheIndex)
|
||||
{
|
||||
// Use the correct shared cache for padding based on the cache index
|
||||
pendingMap.Pad(_sharedCaches[(int)cacheIndex].CacheAllocator);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
funcPtr = entry.FuncPtr;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
funcPtr = IntPtr.Zero;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private void ClearThreadLocalCache(IntPtr framePointer)
|
||||
{
|
||||
// Try to delete functions that are already on the shared cache
|
||||
// and no longer being executed.
|
||||
|
||||
if (_threadLocalCache == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
IntPtr[] cachePointers = new IntPtr[_localCaches.Count];
|
||||
int[] cacheSizes = new int[_localCaches.Count];
|
||||
|
||||
for (int i = 0; i < _localCaches.Count; i++)
|
||||
{
|
||||
cachePointers[i] = _localCaches[i].Pointer;
|
||||
cacheSizes[i] = (int)_localCaches[i].CurrentSize;
|
||||
}
|
||||
|
||||
IntPtr[] sharedPointers = new IntPtr[_sharedCaches.Count];
|
||||
int[] sharedSizes = new int[_sharedCaches.Count];
|
||||
|
||||
for (int i = 0; i < _sharedCaches.Count; i++)
|
||||
{
|
||||
sharedPointers[i] = _sharedCaches[i].Pointer;
|
||||
sharedSizes[i] = (int)_sharedCaches[i].CurrentSize;
|
||||
}
|
||||
|
||||
IEnumerable<ulong> callStack = null;
|
||||
for (int i = 0; i < _localCaches.Count; i++)
|
||||
{
|
||||
callStack = _stackWalker.GetCallStack(
|
||||
framePointer,
|
||||
cachePointers[i],
|
||||
cacheSizes[i],
|
||||
sharedPointers[i],
|
||||
sharedSizes[i]
|
||||
);
|
||||
}
|
||||
|
||||
List<(ulong, ThreadLocalCacheEntry)> toDelete = new();
|
||||
|
||||
foreach ((ulong address, ThreadLocalCacheEntry entry) in _threadLocalCache)
|
||||
{
|
||||
bool canDelete = !HasInAnyPendingMap(address);
|
||||
if (!canDelete)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach (ulong funcAddress in callStack)
|
||||
{
|
||||
if (funcAddress >= (ulong)entry.FuncPtr && funcAddress < (ulong)entry.FuncPtr + (ulong)entry.Size)
|
||||
{
|
||||
canDelete = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (canDelete)
|
||||
{
|
||||
toDelete.Add((address, entry));
|
||||
}
|
||||
}
|
||||
|
||||
int pageSize = (int)MemoryBlock.GetPageSize();
|
||||
|
||||
foreach ((ulong address, ThreadLocalCacheEntry entry) in toDelete)
|
||||
{
|
||||
_threadLocalCache.Remove(address);
|
||||
|
||||
int sizeAligned = BitUtils.AlignUp(entry.Size, pageSize);
|
||||
var (cacheIndex, offset) = SplitCacheOffset(entry.Offset);
|
||||
|
||||
_localCaches[cacheIndex].Free(offset, sizeAligned);
|
||||
_localCaches[cacheIndex].ReprotectAsRw(offset, sizeAligned);
|
||||
}
|
||||
}
|
||||
|
||||
public void ClearEntireThreadLocalCache()
|
||||
{
|
||||
if (_threadLocalCache == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int pageSize = (int)MemoryBlock.GetPageSize();
|
||||
|
||||
foreach ((_, ThreadLocalCacheEntry entry) in _threadLocalCache)
|
||||
{
|
||||
int sizeAligned = BitUtils.AlignUp(entry.Size, pageSize);
|
||||
var (cacheIndex, offset) = SplitCacheOffset(entry.Offset);
|
||||
|
||||
_localCaches[cacheIndex].Free(offset, sizeAligned);
|
||||
_localCaches[cacheIndex].ReprotectAsRw(offset, sizeAligned);
|
||||
}
|
||||
|
||||
_threadLocalCache.Clear();
|
||||
_threadLocalCache = null;
|
||||
}
|
||||
|
||||
private unsafe IntPtr AddThreadLocalFunction(ReadOnlySpan<byte> code, ulong guestAddress)
|
||||
{
|
||||
int alignedSize = BitUtils.AlignUp(code.Length, (int)MemoryBlock.GetPageSize());
|
||||
int combinedOffset = AllocateInLocalCache(alignedSize);
|
||||
var (cacheIndex, funcOffset) = SplitCacheOffset(combinedOffset);
|
||||
|
||||
Debug.Assert((funcOffset & (int)(MemoryBlock.GetPageSize() - 1)) == 0);
|
||||
|
||||
IntPtr funcPtr = _localCaches[cacheIndex].Pointer + funcOffset;
|
||||
code.CopyTo(new Span<byte>((void*)funcPtr, code.Length));
|
||||
|
||||
(_threadLocalCache ??= new()).Add(guestAddress, new(funcOffset, code.Length, funcPtr, cacheIndex));
|
||||
|
||||
_localCaches[cacheIndex].ReprotectAsRx(funcOffset, alignedSize);
|
||||
|
||||
return funcPtr;
|
||||
}
|
||||
|
||||
private void RegisterFunction(ulong address, TranslatedFunction func)
|
||||
{
|
||||
TranslatedFunction oldFunc = _translator.Functions.GetOrAdd(address, func.GuestSize, func);
|
||||
|
||||
Debug.Assert(oldFunc == func);
|
||||
|
||||
_translator.RegisterFunction(address, func);
|
||||
}
|
||||
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
foreach (var cache in _localCaches)
|
||||
{
|
||||
cache.Dispose();
|
||||
}
|
||||
|
||||
foreach (var cache in _sharedCaches)
|
||||
{
|
||||
cache.Dispose();
|
||||
}
|
||||
|
||||
_localCaches.Clear();
|
||||
_sharedCaches.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(disposing: true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -11,6 +11,7 @@ using System.Collections.Concurrent;
|
|||
using System.Collections.Generic;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Threading;
|
||||
using System.IO;
|
||||
|
||||
namespace Ryujinx.Cpu.LightningJit
|
||||
{
|
||||
|
@ -40,6 +41,7 @@ namespace Ryujinx.Cpu.LightningJit
|
|||
|
||||
private readonly ConcurrentQueue<KeyValuePair<ulong, TranslatedFunction>> _oldFuncs;
|
||||
private readonly NoWxCache _noWxCache;
|
||||
private readonly WriteZeroCache _writeZeroCache;
|
||||
private bool _disposed;
|
||||
|
||||
internal TranslatorCache<TranslatedFunction> Functions { get; }
|
||||
|
@ -54,17 +56,44 @@ namespace Ryujinx.Cpu.LightningJit
|
|||
_oldFuncs = new ConcurrentQueue<KeyValuePair<ulong, TranslatedFunction>>();
|
||||
|
||||
if (IsNoWxPlatform)
|
||||
{
|
||||
if (File.Exists("/System/Library/CoreServices/SystemVersion.plist"))
|
||||
{
|
||||
string content = File.ReadAllText("/System/Library/CoreServices/SystemVersion.plist");
|
||||
if (content.Contains("22E5200s") && content.Contains("18.4") && content.Contains("Beta"))
|
||||
{
|
||||
// iOS 18.4db1 (22E5200s) disables traditional JIT (R/X) and needs a debugger to fill to the page to make the executable region a debug map.
|
||||
// Apple has confirmed that this change will be coming to later iOS releases.
|
||||
// Credit to JJTech for figuring out a workaround: https://gist.github.com/JJTech0130/142aee0f7bda9c61a421140d17afbdeb
|
||||
Console.WriteLine($"User is using iOS 18.4db1 (22E5200s), enabling Debugger Memory Writing");
|
||||
_writeZeroCache = new(new JitMemoryAllocator(), CreateStackWalker(), this);
|
||||
Functions = new TranslatorCache<TranslatedFunction>();
|
||||
FunctionTable = new AddressTable<ulong>(for64Bits ? _levels64Bit : _levels32Bit);
|
||||
Stubs = new TranslatorStubs(FunctionTable, _writeZeroCache);
|
||||
}
|
||||
else
|
||||
{
|
||||
_noWxCache = new(new JitMemoryAllocator(), CreateStackWalker(), this);
|
||||
Functions = new TranslatorCache<TranslatedFunction>();
|
||||
FunctionTable = new AddressTable<ulong>(for64Bits ? _levels64Bit : _levels32Bit);
|
||||
Stubs = new TranslatorStubs(FunctionTable, _noWxCache);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_noWxCache = new(new JitMemoryAllocator(), CreateStackWalker(), this);
|
||||
Functions = new TranslatorCache<TranslatedFunction>();
|
||||
FunctionTable = new AddressTable<ulong>(for64Bits ? _levels64Bit : _levels32Bit);
|
||||
Stubs = new TranslatorStubs(FunctionTable, _noWxCache);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
JitCache.Initialize(new JitMemoryAllocator(forJit: true));
|
||||
}
|
||||
|
||||
Functions = new TranslatorCache<TranslatedFunction>();
|
||||
FunctionTable = new AddressTable<ulong>(for64Bits ? _levels64Bit : _levels32Bit);
|
||||
Stubs = new TranslatorStubs(FunctionTable, _noWxCache);
|
||||
Stubs = new TranslatorStubs(FunctionTable, (NoWxCache)null);
|
||||
}
|
||||
|
||||
FunctionTable.Fill = (ulong)Stubs.SlowDispatchStub;
|
||||
|
||||
|
@ -96,6 +125,7 @@ namespace Ryujinx.Cpu.LightningJit
|
|||
|
||||
NativeInterface.UnregisterThread();
|
||||
_noWxCache?.ClearEntireThreadLocalCache();
|
||||
_writeZeroCache?.ClearEntireThreadLocalCache();
|
||||
}
|
||||
|
||||
internal IntPtr GetOrTranslatePointer(IntPtr framePointer, ulong address, ExecutionMode mode)
|
||||
|
@ -103,9 +133,13 @@ namespace Ryujinx.Cpu.LightningJit
|
|||
if (_noWxCache != null)
|
||||
{
|
||||
CompiledFunction func = Compile(address, mode);
|
||||
|
||||
return _noWxCache.Map(framePointer, func.Code, address, (ulong)func.GuestCodeLength);
|
||||
}
|
||||
else if (_writeZeroCache != null)
|
||||
{
|
||||
CompiledFunction func = Compile(address, mode);
|
||||
return _writeZeroCache.Map(framePointer, func.Code, address, (ulong)func.GuestCodeLength);
|
||||
}
|
||||
|
||||
return GetOrTranslate(address, mode).FuncPointer;
|
||||
}
|
||||
|
@ -205,6 +239,10 @@ namespace Ryujinx.Cpu.LightningJit
|
|||
{
|
||||
_noWxCache.Dispose();
|
||||
}
|
||||
else if (_writeZeroCache != null)
|
||||
{
|
||||
_writeZeroCache.Dispose();
|
||||
}
|
||||
else
|
||||
{
|
||||
ClearJitCache();
|
||||
|
|
|
@ -25,6 +25,8 @@ namespace Ryujinx.Cpu.LightningJit
|
|||
|
||||
private readonly AddressTable<ulong> _functionTable;
|
||||
private readonly NoWxCache _noWxCache;
|
||||
private readonly WriteZeroCache _writeZeroCache;
|
||||
|
||||
private readonly GetFunctionAddressDelegate _getFunctionAddressRef;
|
||||
private readonly IntPtr _getFunctionAddress;
|
||||
private readonly Lazy<IntPtr> _dispatchStub;
|
||||
|
@ -92,6 +94,26 @@ namespace Ryujinx.Cpu.LightningJit
|
|||
_dispatchLoop = new(GenerateDispatchLoop, isThreadSafe: true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="TranslatorStubs"/> class with the specified
|
||||
/// <see cref="Translator"/> instance.
|
||||
/// </summary>
|
||||
/// <param name="functionTable">Function table used to store pointers to the functions that the guest code will call</param>
|
||||
/// <param name="writeZeroCache">Cache used on iOS versions that need a debugger to make a debug map</param>
|
||||
/// <exception cref="ArgumentNullException"><paramref name="translator"/> is null</exception>
|
||||
public TranslatorStubs(AddressTable<ulong> functionTable, WriteZeroCache writeZeroCache)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(functionTable);
|
||||
|
||||
_functionTable = functionTable;
|
||||
_writeZeroCache = writeZeroCache;
|
||||
_getFunctionAddressRef = NativeInterface.GetFunctionAddress;
|
||||
_getFunctionAddress = Marshal.GetFunctionPointerForDelegate(_getFunctionAddressRef);
|
||||
_slowDispatchStub = new(GenerateSlowDispatchStub, isThreadSafe: true);
|
||||
_dispatchStub = new(GenerateDispatchStub, isThreadSafe: true);
|
||||
_dispatchLoop = new(GenerateDispatchLoop, isThreadSafe: true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Releases all resources used by the <see cref="TranslatorStubs"/> instance.
|
||||
/// </summary>
|
||||
|
@ -361,6 +383,10 @@ namespace Ryujinx.Cpu.LightningJit
|
|||
{
|
||||
return _noWxCache.MapPageAligned(code);
|
||||
}
|
||||
else if (_writeZeroCache != null)
|
||||
{
|
||||
return _writeZeroCache.MapPageAligned(code);
|
||||
}
|
||||
else
|
||||
{
|
||||
return JitCache.Map(code);
|
||||
|
|
|
@ -106,6 +106,8 @@ namespace Ryujinx.Graphics.Vulkan
|
|||
var externalMemoryBuffer = new ExternalMemoryBufferCreateInfo
|
||||
{
|
||||
SType = StructureType.ExternalMemoryBufferCreateInfo,
|
||||
// For MoltenVK 1.3
|
||||
// ExternalMemoryHandleTypeFlagsExt.MtlBufferBitExt
|
||||
HandleTypes = ExternalMemoryHandleTypeFlags.HostAllocationBitExt,
|
||||
};
|
||||
|
||||
|
@ -676,4 +678,10 @@ namespace Ryujinx.Graphics.Vulkan
|
|||
Dispose(true);
|
||||
}
|
||||
}
|
||||
|
||||
public static class ExternalMemoryHandleTypeFlagsExt
|
||||
{
|
||||
public const ExternalMemoryHandleTypeFlags MtlBufferBitExt = (ExternalMemoryHandleTypeFlags)0x00010000;
|
||||
public const ExternalMemoryHandleTypeFlags MtlHeapBitExt = (ExternalMemoryHandleTypeFlags)0x00040000;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,7 +10,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||
public const int MaxShaderStages = 5;
|
||||
public const int MaxUniformBuffersPerStage = 18;
|
||||
public const int MaxStorageBuffersPerStage = 16;
|
||||
public const int MaxTexturesPerStage = 64;
|
||||
public const int MaxTexturesPerStage = 31;
|
||||
public const int MaxImagesPerStage = 16;
|
||||
public const int MaxUniformBufferBindings = MaxUniformBuffersPerStage * MaxShaderStages;
|
||||
public const int MaxStorageBufferBindings = MaxStorageBuffersPerStage * MaxShaderStages;
|
||||
|
|
|
@ -47,27 +47,27 @@ namespace Ryujinx.Graphics.Vulkan
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
public unsafe void UpdateBuffers(int setIndex, int baseBinding, ReadOnlySpan<DescriptorBufferInfo> bufferInfo, DescriptorType type)
|
||||
{
|
||||
|
||||
|
||||
// Proceed if all checks pass
|
||||
fixed (DescriptorBufferInfo* pBufferInfo = bufferInfo)
|
||||
for (int i = 0; i < bufferInfo.Length; i++)
|
||||
{
|
||||
fixed (DescriptorBufferInfo* pBufferInfo = &bufferInfo[i])
|
||||
{
|
||||
var writeDescriptorSet = new WriteDescriptorSet
|
||||
{
|
||||
SType = StructureType.WriteDescriptorSet,
|
||||
DstSet = _descriptorSets[setIndex],
|
||||
DstBinding = (uint)baseBinding,
|
||||
DstBinding = (uint)(baseBinding + i),
|
||||
DescriptorType = type,
|
||||
DescriptorCount = (uint)bufferInfo.Length,
|
||||
DescriptorCount = 1,
|
||||
PBufferInfo = pBufferInfo
|
||||
};
|
||||
|
||||
// Update descriptor sets
|
||||
_holder.Api.UpdateDescriptorSets(_holder.Device, 1, writeDescriptorSet, 0, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public unsafe void UpdateImage(int setIndex, int bindingIndex, DescriptorImageInfo imageInfo, DescriptorType type)
|
||||
{
|
||||
|
@ -90,21 +90,24 @@ namespace Ryujinx.Graphics.Vulkan
|
|||
|
||||
public unsafe void UpdateImages(int setIndex, int baseBinding, ReadOnlySpan<DescriptorImageInfo> imageInfo, DescriptorType type)
|
||||
{
|
||||
fixed (DescriptorImageInfo* pImageInfo = imageInfo)
|
||||
for (int i = 0; i < imageInfo.Length; i++)
|
||||
{
|
||||
fixed (DescriptorImageInfo* pImageInfo = &imageInfo[i])
|
||||
{
|
||||
var writeDescriptorSet = new WriteDescriptorSet
|
||||
{
|
||||
SType = StructureType.WriteDescriptorSet,
|
||||
DstSet = _descriptorSets[setIndex],
|
||||
DstBinding = (uint)baseBinding,
|
||||
DstBinding = (uint)(baseBinding + i),
|
||||
DescriptorType = type,
|
||||
DescriptorCount = (uint)imageInfo.Length,
|
||||
DescriptorCount = 1,
|
||||
PImageInfo = pImageInfo,
|
||||
};
|
||||
|
||||
_holder.Api.UpdateDescriptorSets(_holder.Device, 1, in writeDescriptorSet, 0, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public unsafe void UpdateImagesCombined(int setIndex, int baseBinding, ReadOnlySpan<DescriptorImageInfo> imageInfo, DescriptorType type)
|
||||
{
|
||||
|
@ -120,33 +123,24 @@ namespace Ryujinx.Graphics.Vulkan
|
|||
bool nonNull = imageInfo[i].ImageView.Handle != 0 && imageInfo[i].Sampler.Handle != 0;
|
||||
if (nonNull)
|
||||
{
|
||||
int count = 1;
|
||||
|
||||
while (i + count < imageInfo.Length &&
|
||||
imageInfo[i + count].ImageView.Handle != 0 &&
|
||||
imageInfo[i + count].Sampler.Handle != 0)
|
||||
{
|
||||
count++;
|
||||
}
|
||||
|
||||
var writeDescriptorSet = new WriteDescriptorSet
|
||||
{
|
||||
SType = StructureType.WriteDescriptorSet,
|
||||
DstSet = _descriptorSets[setIndex],
|
||||
DstBinding = (uint)(baseBinding + i),
|
||||
DescriptorType = DescriptorType.CombinedImageSampler,
|
||||
DescriptorType = type,
|
||||
DescriptorCount = 1,
|
||||
PImageInfo = pImageInfo,
|
||||
PImageInfo = pImageInfo + i,
|
||||
};
|
||||
|
||||
_holder.Api.UpdateDescriptorSets(_holder.Device, 1, in writeDescriptorSet, 0, null);
|
||||
|
||||
i += count - 1;
|
||||
_holder.Api.UpdateDescriptorSets(_holder.Device, 1, in writeDescriptorSet, 0, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public unsafe void UpdateBufferImage(int setIndex, int bindingIndex, BufferView texelBufferView, DescriptorType type)
|
||||
{
|
||||
if (texelBufferView.Handle != 0UL)
|
||||
|
@ -174,22 +168,15 @@ namespace Ryujinx.Graphics.Vulkan
|
|||
|
||||
fixed (BufferView* pTexelBufferView = texelBufferView)
|
||||
{
|
||||
for (uint i = 0; i < texelBufferView.Length;)
|
||||
for (int i = 0; i < texelBufferView.Length; i++)
|
||||
{
|
||||
uint count = 1;
|
||||
|
||||
if (texelBufferView[(int)i].Handle != 0UL)
|
||||
if (texelBufferView[i].Handle != 0UL)
|
||||
{
|
||||
while (i + count < texelBufferView.Length && texelBufferView[(int)(i + count)].Handle != 0UL)
|
||||
{
|
||||
count++;
|
||||
}
|
||||
|
||||
var writeDescriptorSet = new WriteDescriptorSet
|
||||
{
|
||||
SType = StructureType.WriteDescriptorSet,
|
||||
DstSet = _descriptorSets[setIndex],
|
||||
DstBinding = (uint)baseBinding + i,
|
||||
DstBinding = (uint)baseBinding + (uint)i,
|
||||
DescriptorType = type,
|
||||
DescriptorCount = 1,
|
||||
PTexelBufferView = pTexelBufferView + i,
|
||||
|
@ -197,8 +184,6 @@ namespace Ryujinx.Graphics.Vulkan
|
|||
|
||||
_holder.Api.UpdateDescriptorSets(_holder.Device, 1, in writeDescriptorSet, 0, null);
|
||||
}
|
||||
|
||||
i += count;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -757,6 +757,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||
return mirrored;
|
||||
}
|
||||
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private void UpdateAndBind(CommandBufferScoped cbs, ShaderCollection program, int setIndex, PipelineBindPoint pbp)
|
||||
{
|
||||
|
@ -810,9 +811,12 @@ namespace Ryujinx.Graphics.Vulkan
|
|||
}
|
||||
}
|
||||
|
||||
// Split buffer updates into individual slices for MoltenVK compatibility
|
||||
ReadOnlySpan<DescriptorBufferInfo> uniformBuffers = _uniformBuffers;
|
||||
|
||||
tu.Push(uniformBuffers.Slice(binding, count));
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
tu.Push(uniformBuffers.Slice(binding + i, 1));
|
||||
}
|
||||
}
|
||||
else if (setIndex == PipelineBase.StorageSetIndex)
|
||||
{
|
||||
|
@ -836,9 +840,12 @@ namespace Ryujinx.Graphics.Vulkan
|
|||
}
|
||||
}
|
||||
|
||||
// Split buffer updates into individual slices for MoltenVK compatibility
|
||||
ReadOnlySpan<DescriptorBufferInfo> storageBuffers = _storageBuffers;
|
||||
|
||||
tu.Push(storageBuffers.Slice(binding, count));
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
tu.Push(storageBuffers.Slice(binding + i, 1));
|
||||
}
|
||||
}
|
||||
else if (setIndex == PipelineBase.TextureSetIndex)
|
||||
{
|
||||
|
@ -867,7 +874,10 @@ namespace Ryujinx.Graphics.Vulkan
|
|||
}
|
||||
}
|
||||
|
||||
tu.Push<DescriptorImageInfo>(textures[..count]);
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
tu.Push<DescriptorImageInfo>(textures.Slice(i, 1));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -878,7 +888,10 @@ namespace Ryujinx.Graphics.Vulkan
|
|||
bufferTextures[i] = _bufferTextureRefs[binding + i]?.GetBufferView(cbs, false) ?? default;
|
||||
}
|
||||
|
||||
tu.Push<BufferView>(bufferTextures[..count]);
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
tu.Push<BufferView>(bufferTextures.Slice(i, 1));
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
|
@ -906,7 +919,10 @@ namespace Ryujinx.Graphics.Vulkan
|
|||
images[i].ImageView = _imageRefs[binding + i].ImageView?.Get(cbs).Value ?? default;
|
||||
}
|
||||
|
||||
tu.Push<DescriptorImageInfo>(images[..count]);
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
tu.Push<DescriptorImageInfo>(images.Slice(i, 1));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -917,7 +933,10 @@ namespace Ryujinx.Graphics.Vulkan
|
|||
bufferImages[i] = _bufferImageRefs[binding + i]?.GetBufferView(cbs, true) ?? default;
|
||||
}
|
||||
|
||||
tu.Push<BufferView>(bufferImages[..count]);
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
tu.Push<BufferView>(bufferImages.Slice(i, 1));
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
|
@ -940,6 +959,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||
_gd.Api.CmdBindDescriptorSets(cbs.CommandBuffer, pbp, _program.PipelineLayout, (uint)setIndex, 1, sets, 0, ReadOnlySpan<uint>.Empty);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private void UpdateAndBindTexturesWithoutTemplate(CommandBufferScoped cbs, ShaderCollection program, PipelineBindPoint pbp)
|
||||
{
|
||||
int setIndex = PipelineBase.TextureSetIndex;
|
||||
|
@ -950,11 +970,12 @@ namespace Ryujinx.Graphics.Vulkan
|
|||
return;
|
||||
}
|
||||
|
||||
if (_updateDescriptorCacheCbIndex)
|
||||
var dummyImageInfo = new DescriptorImageInfo
|
||||
{
|
||||
_updateDescriptorCacheCbIndex = false;
|
||||
program.UpdateDescriptorCacheCommandBufferIndex(cbs.CommandBufferIndex);
|
||||
}
|
||||
ImageView = _dummyTexture.GetImageView().Get(cbs).Value,
|
||||
Sampler = _dummySampler.GetSampler().Get(cbs).Value,
|
||||
ImageLayout = ImageLayout.General
|
||||
};
|
||||
|
||||
var dsc = program.GetNewDescriptorSetCollection(setIndex, out _).Get(cbs);
|
||||
|
||||
|
@ -967,71 +988,78 @@ namespace Ryujinx.Graphics.Vulkan
|
|||
{
|
||||
if (segment.Type != ResourceType.BufferTexture)
|
||||
{
|
||||
Span<DescriptorImageInfo> textures = _textures;
|
||||
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
ref var texture = ref textures[i];
|
||||
ref var refs = ref _textureRefs[binding + i];
|
||||
int index = binding + i;
|
||||
ref var textureRef = ref _textureRefs[index];
|
||||
|
||||
texture.ImageView = refs.ImageView?.Get(cbs).Value ?? default;
|
||||
texture.Sampler = refs.Sampler?.Get(cbs).Value ?? default;
|
||||
var imageView = textureRef.ImageView?.Get(cbs).Value ?? dummyImageInfo.ImageView;
|
||||
var sampler = textureRef.Sampler?.Get(cbs).Value ?? dummyImageInfo.Sampler;
|
||||
|
||||
if (texture.ImageView.Handle == 0)
|
||||
var imageInfo = new DescriptorImageInfo
|
||||
{
|
||||
texture.ImageView = _dummyTexture.GetImageView().Get(cbs).Value;
|
||||
}
|
||||
ImageView = imageView.Handle != 0 ? imageView : dummyImageInfo.ImageView,
|
||||
Sampler = sampler.Handle != 0 ? sampler : dummyImageInfo.Sampler,
|
||||
ImageLayout = ImageLayout.General
|
||||
};
|
||||
|
||||
if (texture.Sampler.Handle == 0)
|
||||
{
|
||||
texture.Sampler = _dummySampler.GetSampler().Get(cbs).Value;
|
||||
}
|
||||
|
||||
if (OperatingSystem.IsIOS()) {
|
||||
Span<DescriptorImageInfo> singleTexture = textures.Slice(i, 1);
|
||||
dsc.UpdateImages(0, binding + i, singleTexture, DescriptorType.CombinedImageSampler);
|
||||
}
|
||||
}
|
||||
if (!OperatingSystem.IsIOS()) {
|
||||
dsc.UpdateImages(0, binding, textures[..count], DescriptorType.CombinedImageSampler);
|
||||
dsc.UpdateImages(0, index, new[] { imageInfo }, DescriptorType.CombinedImageSampler);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Span<BufferView> bufferTextures = _bufferTextures;
|
||||
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
bufferTextures[i] = _bufferTextureRefs[binding + i]?.GetBufferView(cbs, false) ?? default;
|
||||
int index = binding + i;
|
||||
var bufferView = _bufferTextureRefs[index]?.GetBufferView(cbs, false) ?? default;
|
||||
dsc.UpdateBufferImages(0, index, new[] { bufferView }, DescriptorType.UniformTexelBuffer);
|
||||
}
|
||||
|
||||
dsc.UpdateBufferImages(0, binding, bufferTextures[..count], DescriptorType.UniformTexelBuffer);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var arrayRef = _textureArrayRefs[binding];
|
||||
|
||||
if (segment.Type != ResourceType.BufferTexture)
|
||||
{
|
||||
// Span<DescriptorImageInfo> images = _imageRefs.GetImageInfos(_gd, cbs, _dummyTexture, _dummySampler);
|
||||
|
||||
if (OperatingSystem.IsIOS())
|
||||
var imageInfos = arrayRef.Array.GetImageInfos(_gd, cbs, _dummyTexture, _dummySampler);
|
||||
if (imageInfos != null)
|
||||
{
|
||||
dsc.UpdateImages(0, binding, _textureArrayRefs[binding].Array.GetImageInfos(_gd, cbs, _dummyTexture, _dummySampler), DescriptorType.CombinedImageSampler);
|
||||
}
|
||||
else
|
||||
for (int i = 0; i < imageInfos.Length && i < count; i++)
|
||||
{
|
||||
dsc.UpdateImages(0, binding, _textureArrayRefs[binding].Array.GetImageInfos(_gd, cbs, _dummyTexture, _dummySampler), DescriptorType.CombinedImageSampler);
|
||||
dsc.UpdateImages(0, binding + i, new[] { imageInfos[i] }, DescriptorType.CombinedImageSampler);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
dsc.UpdateBufferImages(0, binding, _textureArrayRefs[binding].Array.GetBufferViews(cbs), DescriptorType.UniformTexelBuffer);
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
dsc.UpdateImages(0, binding + i, new[] { dummyImageInfo }, DescriptorType.CombinedImageSampler);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var bufferViews = arrayRef.Array.GetBufferViews(cbs);
|
||||
if (bufferViews != null)
|
||||
{
|
||||
for (int i = 0; i < bufferViews.Length && i < count; i++)
|
||||
{
|
||||
dsc.UpdateBufferImages(0, binding + i, new[] { bufferViews[i] }, DescriptorType.UniformTexelBuffer);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
dsc.UpdateBufferImages(0, binding + i, new[] { default(BufferView) }, DescriptorType.UniformTexelBuffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var sets = dsc.GetSets();
|
||||
|
||||
_gd.Api.CmdBindDescriptorSets(cbs.CommandBuffer, pbp, _program.PipelineLayout, (uint)setIndex, 1, sets, 0, ReadOnlySpan<uint>.Empty);
|
||||
}
|
||||
|
||||
|
|
|
@ -1241,7 +1241,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||
|
||||
int vbSize = vertexBuffer.Buffer.Size;
|
||||
|
||||
if ((Gd.Vendor == Vendor.Amd || !OperatingSystem.IsIOSVersionAtLeast(17)) && !Gd.IsMoltenVk && vertexBuffer.Stride > 0)
|
||||
if (Gd.Vendor == Vendor.Amd && !Gd.IsMoltenVk && vertexBuffer.Stride > 0)
|
||||
{
|
||||
// AMD has a bug where if offset + stride * count is greater than
|
||||
// the size, then the last attribute will have the wrong value.
|
||||
|
|
|
@ -409,7 +409,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||
{
|
||||
SType = StructureType.PipelineVertexInputStateCreateInfo,
|
||||
VertexAttributeDescriptionCount = VertexAttributeDescriptionsCount,
|
||||
PVertexAttributeDescriptions = isMoltenVk ? pVertexAttributeDescriptions2 : pVertexAttributeDescriptions,
|
||||
PVertexAttributeDescriptions = pVertexAttributeDescriptions2, // isMoltenVk ? pVertexAttributeDescriptions2 : pVertexAttributeDescriptions,
|
||||
VertexBindingDescriptionCount = VertexBindingDescriptionsCount,
|
||||
PVertexBindingDescriptions = pVertexBindingDescriptions,
|
||||
};
|
||||
|
@ -521,6 +521,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||
|
||||
uint blendEnables = 0;
|
||||
|
||||
|
||||
if (gd.IsMoltenVk && Internal.AttachmentIntegerFormatMask != 0)
|
||||
{
|
||||
// Blend can't be enabled for integer formats, so let's make sure it is disabled.
|
||||
|
|
|
@ -422,7 +422,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||
features2.Features.ShaderStorageImageMultisample,
|
||||
_physicalDevice.IsDeviceExtensionPresent(ExtConditionalRendering.ExtensionName),
|
||||
isDynamicStateSupported,
|
||||
features2.Features.MultiViewport && !(IsMoltenVk && Vendor == Vendor.Amd), // Workaround for AMD on MoltenVK issue
|
||||
features2.Features.MultiViewport && !IsMoltenVk, // Workaround for AMD on MoltenVK issue
|
||||
!IsMoltenVk ? featuresRobustness2.NullDescriptor : false,
|
||||
supportsPushDescriptors && !IsMoltenVk,
|
||||
propertiesPushDescriptor.MaxPushDescriptors,
|
||||
|
@ -785,7 +785,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||
shaderSubgroupSize: (int)Capabilities.SubgroupSize,
|
||||
storageBufferOffsetAlignment: (int)limits.MinStorageBufferOffsetAlignment,
|
||||
textureBufferOffsetAlignment: (int)limits.MinTexelBufferOffsetAlignment,
|
||||
gatherBiasPrecision: IsIntelWindows || IsAmdWindows ? (int)Capabilities.SubTexelPrecisionBits : 0,
|
||||
gatherBiasPrecision: (int)Capabilities.SubTexelPrecisionBits, //IsIntelWindows || IsAmdWindows ? (int)Capabilities.SubTexelPrecisionBits : 0,
|
||||
maximumGpuMemory: GetTotalGPUMemory());
|
||||
}
|
||||
|
||||
|
|
|
@ -93,6 +93,7 @@ using Ryujinx.Input.HLE;
|
|||
using Silk.NET.Vulkan;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Runtime.InteropServices;
|
||||
using SDL2;
|
||||
|
||||
|
@ -1139,7 +1140,7 @@ namespace Ryujinx.Headless.SDL2
|
|||
_libHacHorizonManager.InitializeBcatServer();
|
||||
_libHacHorizonManager.InitializeSystemClients();
|
||||
|
||||
_contentManager = new ContentManager(_virtualFileSystem);
|
||||
// _contentManager = new ContentManager(_virtualFileSystem);
|
||||
|
||||
_accountManager = new AccountManager(_libHacHorizonManager.RyujinxClient, option.UserProfile);
|
||||
|
||||
|
@ -1202,8 +1203,13 @@ namespace Ryujinx.Headless.SDL2
|
|||
return;
|
||||
}
|
||||
|
||||
if (option.InputPath == "MiiMaker") {
|
||||
string contentPath = _contentManager.GetInstalledContentPath(0x0100000000001009, StorageId.BuiltInSystem, NcaContentType.Program);
|
||||
|
||||
Match match = Regex.Match(option.InputPath, @"0x[0-9A-Fa-f]+");
|
||||
if (match.Success)
|
||||
{
|
||||
string hexStr = match.Value.Substring(2);
|
||||
ulong id = Convert.ToUInt64(hexStr, 16);
|
||||
string contentPath = _contentManager.GetInstalledContentPath(id, StorageId.BuiltInSystem, NcaContentType.Program);
|
||||
|
||||
option.InputPath = contentPath;
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue