Compare commits

..

2 Commits

Author SHA1 Message Date
lizzie ade32609df license ffs 2026-04-26 09:20:11 +00:00
lizzie 8f64430215 [video_core] simplify InvalidationAccumulator
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2026-04-26 09:19:30 +00:00
151 changed files with 15551 additions and 17887 deletions
+9 -9
View File
@@ -268,6 +268,11 @@ if (NOT EXISTS ${PROJECT_BINARY_DIR}/${compat_json})
file(WRITE ${PROJECT_BINARY_DIR}/${compat_json} "") file(WRITE ${PROJECT_BINARY_DIR}/${compat_json} "")
endif() endif()
if (YUZU_LEGACY)
message(WARNING "Making legacy build. Performance may suffer.")
add_compile_definitions(YUZU_LEGACY)
endif()
if (ARCHITECTURE_arm64 AND (ANDROID OR PLATFORM_LINUX)) if (ARCHITECTURE_arm64 AND (ANDROID OR PLATFORM_LINUX))
set(HAS_NCE 1) set(HAS_NCE 1)
add_compile_definitions(HAS_NCE=1) add_compile_definitions(HAS_NCE=1)
@@ -419,10 +424,10 @@ if (zstd_ADDED)
add_library(zstd::libzstd ALIAS libzstd_static) add_library(zstd::libzstd ALIAS libzstd_static)
endif() endif()
# nlohmann
AddJsonPackage(nlohmann)
if (NOT YUZU_STATIC_ROOM) if (NOT YUZU_STATIC_ROOM)
# nlohmann
AddJsonPackage(nlohmann)
# zlib # zlib
AddJsonPackage(zlib) AddJsonPackage(zlib)
@@ -444,11 +449,6 @@ if (NOT YUZU_STATIC_ROOM)
if (NOT TARGET Opus::opus) if (NOT TARGET Opus::opus)
add_library(Opus::opus ALIAS opus) add_library(Opus::opus ALIAS opus)
endif() endif()
if (APPLE)
# KosmicKrisp
AddJsonPackage(kosmickrisp)
endif()
endif() endif()
if(NOT TARGET Boost::headers) if(NOT TARGET Boost::headers)
@@ -485,7 +485,7 @@ endfunction()
# ============================================= # =============================================
if (APPLE) if (APPLE)
foreach(fw Carbon Metal Cocoa IOKit CoreVideo CoreMedia Security) foreach(fw Carbon Metal Cocoa IOKit CoreVideo CoreMedia)
find_library(${fw}_LIBRARY ${fw} REQUIRED) find_library(${fw}_LIBRARY ${fw} REQUIRED)
list(APPEND PLATFORM_LIBRARIES ${${fw}_LIBRARY}) list(APPEND PLATFORM_LIBRARIES ${${fw}_LIBRARY})
endforeach() endforeach()
+2 -4
View File
@@ -33,11 +33,9 @@ endif()
set(GIT_DESC ${BUILD_VERSION}) set(GIT_DESC ${BUILD_VERSION})
# Generate cpp with Git revision from template # Generate cpp with Git revision from template
# Also if this is a CI build, add the build name (ie: Nightly, Canary) to the scm_rev file as well
# TODO(crueter): Stable releases feed. # Auto-updater metadata! Must somewhat mirror GitHub/Forgejo API endpoint
set(BUILD_AUTO_UPDATE_STABLE_REPO "eden-emu/eden")
set(BUILD_AUTO_UPDATE_STABLE_API "git.eden-emu.dev")
set(BUILD_AUTO_UPDATE_STABLE_API_PATH "/api/v1/repos/")
set(BUILD_AUTO_UPDATE_API_PATH "/latest/release.json") set(BUILD_AUTO_UPDATE_API_PATH "/latest/release.json")
if (NIGHTLY_BUILD) if (NIGHTLY_BUILD)
-9
View File
@@ -114,14 +114,5 @@
"QUAZIP_INSTALL OFF", "QUAZIP_INSTALL OFF",
"QUAZIP_ENABLE_QTEXTCODEC OFF" "QUAZIP_ENABLE_QTEXTCODEC OFF"
] ]
},
"kosmickrisp": {
"package": "KosmicKrisp",
"repo": "crueter-ci/KosmicKrisp",
"hash": "4709fb3735ed40048cab9a64abed9a661517dada92b6e8928d578e947fdd6af21b3da4887822f4c3ad3055c6641f9a63243e10e2dd2bba00379618dcc0cda4be",
"git_version": "26.0.6",
"tag": "v%VERSION%",
"artifact": "KosmicKrisp-%VERSION%.tar.zst",
"bundled": true
} }
} }
+498 -566
View File
File diff suppressed because it is too large Load Diff
+501 -571
View File
File diff suppressed because it is too large Load Diff
+501 -571
View File
File diff suppressed because it is too large Load Diff
+501 -571
View File
File diff suppressed because it is too large Load Diff
+501 -571
View File
File diff suppressed because it is too large Load Diff
+501 -571
View File
File diff suppressed because it is too large Load Diff
+500 -568
View File
File diff suppressed because it is too large Load Diff
+572 -644
View File
File diff suppressed because it is too large Load Diff
+504 -571
View File
File diff suppressed because it is too large Load Diff
+501 -571
View File
File diff suppressed because it is too large Load Diff
+504 -571
View File
File diff suppressed because it is too large Load Diff
+504 -572
View File
File diff suppressed because it is too large Load Diff
+501 -571
View File
File diff suppressed because it is too large Load Diff
+501 -571
View File
File diff suppressed because it is too large Load Diff
+496 -566
View File
File diff suppressed because it is too large Load Diff
+501 -571
View File
File diff suppressed because it is too large Load Diff
+497 -564
View File
File diff suppressed because it is too large Load Diff
+504 -571
View File
File diff suppressed because it is too large Load Diff
+501 -571
View File
File diff suppressed because it is too large Load Diff
+504 -573
View File
File diff suppressed because it is too large Load Diff
+498 -565
View File
File diff suppressed because it is too large Load Diff
+502 -572
View File
File diff suppressed because it is too large Load Diff
+498 -566
View File
File diff suppressed because it is too large Load Diff
+501 -571
View File
File diff suppressed because it is too large Load Diff
+501 -571
View File
File diff suppressed because it is too large Load Diff
+505 -576
View File
File diff suppressed because it is too large Load Diff
+503 -571
View File
File diff suppressed because it is too large Load Diff
+4 -30
View File
@@ -4,8 +4,7 @@
- [Arch Linux](#arch-linux) - [Arch Linux](#arch-linux)
- [Gentoo Linux](#gentoo-linux) - [Gentoo Linux](#gentoo-linux)
- [macOS](#macos) - [macOS](#macos)
- [OpenIndiana](#openindiana) - [Solaris](#solaris)
- [OmniOS](#omnios)
- [HaikuOS](#haikuos) - [HaikuOS](#haikuos)
- [OpenBSD](#openbsd) - [OpenBSD](#openbsd)
- [FreeBSD](#freebsd) - [FreeBSD](#freebsd)
@@ -32,14 +31,14 @@ If you're having issues with building, always consult that ebuild.
macOS is largely untested. Expect crashes, significant Vulkan issues, and other fun stuff. macOS is largely untested. Expect crashes, significant Vulkan issues, and other fun stuff.
## OpenIndiana ## Solaris
Always consult [the OpenIndiana package list](https://pkg.openindiana.org/hipster/en/index.shtml) to cross-verify availability. Always consult [the OpenIndiana package list](https://pkg.openindiana.org/hipster/en/index.shtml) to cross-verify availability.
Run the usual update + install of essential toolings: `sudo pkg update && sudo pkg install git cmake`. Run the usual update + install of essential toolings: `sudo pkg update && sudo pkg install git cmake`.
- **gcc**: Install either `developer/gcc-14`. - **gcc**: `sudo pkg install developer/gcc-14`.
- **clang**: Version 20 is broken, install `developer/clang-19`. - **clang**: Version 20 is broken, use `sudo pkg install developer/clang-19`.
Qt Widgets appears to be broken. For now, add `-DENABLE_QT=OFF` to your configure command. In the meantime, a Qt Quick frontend is in the works--check back later! Qt Widgets appears to be broken. For now, add `-DENABLE_QT=OFF` to your configure command. In the meantime, a Qt Quick frontend is in the works--check back later!
@@ -68,31 +67,6 @@ export LIBGL_ALWAYS_SOFTWARE=1
- If using OpenIndiana, due to a bug in SDL2's CMake configuration, audio driver defaults to SunOS `<sys/audioio.h>`, which does not exist on OpenIndiana. Using external or bundled SDL2 may solve this. - If using OpenIndiana, due to a bug in SDL2's CMake configuration, audio driver defaults to SunOS `<sys/audioio.h>`, which does not exist on OpenIndiana. Using external or bundled SDL2 may solve this.
- System OpenSSL generally does not work. Instead, use `-DYUZU_USE_BUNDLED_OPENSSL=ON` to use a bundled static OpenSSL, or build a system dependency from source. - System OpenSSL generally does not work. Instead, use `-DYUZU_USE_BUNDLED_OPENSSL=ON` to use a bundled static OpenSSL, or build a system dependency from source.
## OmniOS
Install `developer/gcc14` on OmniOS using pkgsrc.
Since so many dependencies are missing on `OmniOS`, you may wish to use `-DCPMUTIL_FORCE_BUNDLED=ON -DYUZU_USE_EXTERNAL_SDL2=ON`
For OmniOS you are required to build glslang yourself:
```sh
sudo pkg install python-313
git clone --depth=1 https://github.com/KhronosGroup/glslang.git
cd glslang
python3.13 ./update_glslang_sources.py
cmake -B build -DCMAKE_BUILD_TYPE=Release
cmake --build build -- -j `nproc`
cmake --install build
```
It may be tempting to specify `-t glslang`, but this will cause installation to fail. So don't.
Using `--parallel` on CMake incorrectly passes `dmake ... -jn` instead of `dmake ... -j n`, this is a bug with OmniOS's CMake, and as such it's recommended to not use this option until it's fixed.
You may also need to install `gmake` in order to properly build FFmpeg, this is provided by the `build-essential` package.
If it wasn't obvious already, you require a X11 server to properly run the emulator within OmniOS, [this guide](https://web.archive.org/web/20260424200928/https://geekblood.wordpress.com/2017/10/26/installing-x11-and-a-desktop-environment-on-omnios/) is a great starting point for that, the links to pkgsrc are outdated so follow [this exemplar](https://pkgsrc.smartos.org/install-on-illumos/) as well:
## HaikuOS ## HaikuOS
It's recommended to do a `pkgman full-sync` before installing. See [HaikuOS: Installing applications](https://www.haiku-os.org/guides/daily-tasks/install-applications/). Sometimes the process may be interrupted by an error like "Interrupted syscall". Simply firing the command again fixes the issue. By default `g++` is included on the default installation. It's recommended to do a `pkgman full-sync` before installing. See [HaikuOS: Installing applications](https://www.haiku-os.org/guides/daily-tasks/install-applications/). Sometimes the process may be interrupted by an error like "Interrupted syscall". Simply firing the command again fixes the issue. By default `g++` is included on the default installation.
+3 -13
View File
@@ -291,23 +291,13 @@ pkg install gcc14 git cmake unzip nasm autoconf bash pkgconf ffmpeg glslang gmak
</details> </details>
<details> <details>
<summary>OpenIndiana</summary> <summary>Solaris / OpenIndiana</summary>
```sh ```sh
sudo pkg install git cmake qt6 boost glslang libzip library/lz4 libusb-1 nlohmann-json openssl opus sdl2 zlib compress/zstd unzip pkg-config nasm autoconf mesa library/libdrm header-drm developer/fmt sudo pkg install qt6 boost glslang libzip library/lz4 libusb-1 nlohmann-json openssl opus sdl2 zlib compress/zstd unzip pkg-config nasm autoconf mesa library/libdrm header-drm developer/fmt
``` ```
[Caveats](./Caveats.md#openindiana). [Caveats](./Caveats.md#solaris).
</details>
<details>
<summary>OmniOS</summary>
```sh
sudo pkgin install git cmake autoconf build-essential libusb-1 nasm gcc13
```
[Caveats](./Caveats.md#omnios).
</details> </details>
<details> <details>
+14
View File
@@ -389,3 +389,17 @@ if (ANDROID)
add_library(oboe::oboe ALIAS oboe) add_library(oboe::oboe ALIAS oboe)
endif() endif()
if (APPLE)
# moltenvk
if (NOT YUZU_USE_BUNDLED_MOLTENVK)
find_library(MOLTENVK_LIBRARY MoltenVK)
endif()
# TODO: kosmickrisp?
if (NOT MOLTENVK_LIBRARY OR YUZU_USE_BUNDLED_MOLTENVK)
AddJsonPackage(moltenvk)
set(MOLTENVK_LIBRARY "${moltenvk_SOURCE_DIR}/MoltenVK/dylib/macOS/libMoltenVK.dylib" CACHE STRING "" FORCE)
endif()
endif()
+8
View File
@@ -174,6 +174,14 @@
"bundled": true, "bundled": true,
"skip_updates": "true" "skip_updates": "true"
}, },
"moltenvk": {
"repo": "V380-Ori/Ryujinx.MoltenVK",
"tag": "v%VERSION%-ryujinx",
"git_version": "1.4.1",
"artifact": "MoltenVK-macOS.tar",
"hash": "5695b36ca5775819a71791557fcb40a4a5ee4495be6b8442e0b666d0c436bec02aae68cc6210183f7a5c986bdbec0e117aecfad5396e496e9c2fd5c89133a347",
"bundled": true
},
"gamemode": { "gamemode": {
"repo": "FeralInteractive/gamemode", "repo": "FeralInteractive/gamemode",
"sha": "ce6fe122f3", "sha": "ce6fe122f3",
-10
View File
@@ -25,16 +25,6 @@ if (NIGHTLY_BUILD)
add_compile_definitions(NIGHTLY_BUILD) add_compile_definitions(NIGHTLY_BUILD)
endif() endif()
if (YUZU_LEGACY)
message(WARNING "Making legacy build. Performance may suffer.")
add_compile_definitions(YUZU_LEGACY)
endif()
if (GENSHIN_SPOOF)
message(WARNING "Making Genshin spoof build")
add_compile_definitions(GENSHIN_SPOOF)
endif()
# Set compilation flags # Set compilation flags
if (MSVC AND NOT CXX_CLANG) if (MSVC AND NOT CXX_CLANG)
set(CMAKE_CONFIGURATION_TYPES Debug Release CACHE STRING "" FORCE) set(CMAKE_CONFIGURATION_TYPES Debug Release CACHE STRING "" FORCE)
-6
View File
@@ -203,12 +203,6 @@ android {
resValue("string", "app_name_suffixed", "Eden Optimized") resValue("string", "app_name_suffixed", "Eden Optimized")
applicationId = "com.miHoYo.Yuanshen" applicationId = "com.miHoYo.Yuanshen"
externalNativeBuild {
cmake {
arguments.add("-DGENSHIN_SPOOF=ON")
}
}
ndk { ndk {
abiFilters += listOf("arm64-v8a") abiFilters += listOf("arm64-v8a")
} }
@@ -33,19 +33,6 @@ import org.yuzu.yuzu_emu.applets.web.WebBrowser
* with the native side of the Yuzu code. * with the native side of the Yuzu code.
*/ */
object NativeLibrary { object NativeLibrary {
@Keep
data class UpdateResult(
var tag: String = "",
var title: String = "",
var body: String = "",
var url: String = "",
var assets: MutableList<String> = mutableListOf()
) {
fun addAsset(asset: String) {
assets.add(asset)
}
}
@JvmField @JvmField
var sEmulationActivity = WeakReference<EmulationActivity?>(null) var sEmulationActivity = WeakReference<EmulationActivity?>(null)
@@ -253,7 +240,17 @@ object NativeLibrary {
/** /**
* Checks for available updates. * Checks for available updates.
*/ */
external fun checkForUpdate(): UpdateResult? external fun checkForUpdate(): Array<String>?
/**
* Return the URL to the release page
*/
external fun getUpdateUrl(version: String): String
/**
* Return the URL to download the APK for the given version
*/
external fun getUpdateApkUrl(tag: String, artifact: String, packageId: String): String
/** /**
* Returns whether the update checker is enabled through CMAKE options. * Returns whether the update checker is enabled through CMAKE options.
@@ -175,25 +175,25 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
val latestVersion = NativeLibrary.checkForUpdate() val latestVersion = NativeLibrary.checkForUpdate()
if (latestVersion != null) { if (latestVersion != null) {
runOnUiThread { runOnUiThread {
showUpdateDialog(latestVersion) val tag: String = latestVersion[0]
val name: String = latestVersion[1]
showUpdateDialog(tag, name)
} }
} }
}.start() }.start()
} }
// TODO(crueter): body, "View on Forgejo" button private fun showUpdateDialog(tag: String, name: String) {
private fun showUpdateDialog(release: NativeLibrary.UpdateResult) {
MaterialAlertDialogBuilder(this) MaterialAlertDialogBuilder(this)
.setTitle(R.string.update_available) .setTitle(R.string.update_available)
.setMessage(getString(R.string.update_available_description, release.title)) .setMessage(getString(R.string.update_available_description, name))
.setPositiveButton(android.R.string.ok) { _, _ -> .setPositiveButton(android.R.string.ok) { _, _ ->
val assets = release.assets var artifact = tag
// Nightly builds have a slightly different format
if (assets.isEmpty()) { if (NativeLibrary.isNightlyBuild()) {
openLink(release.url) artifact = tag.substringAfter('.', tag)
} else {
downloadAndInstallUpdate(release)
} }
downloadAndInstallUpdate(tag, artifact)
} }
.setNeutralButton(R.string.cancel) { dialog, _ -> .setNeutralButton(R.string.cancel) { dialog, _ ->
dialog.dismiss() dialog.dismiss()
@@ -206,23 +206,17 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
.show() .show()
} }
private fun openLink(link: String) { private fun downloadAndInstallUpdate(version: String, artifact: String) {
val intent = Intent(Intent.ACTION_VIEW, link.toUri())
startActivity(intent)
}
private fun downloadAndInstallUpdate(release: NativeLibrary.UpdateResult) {
CoroutineScope(Dispatchers.IO).launch { CoroutineScope(Dispatchers.IO).launch {
val packageId = applicationContext.packageName val packageId = applicationContext.packageName
val asset = release.assets[0] val apkUrl = NativeLibrary.getUpdateApkUrl(version, artifact, packageId)
val artifact = asset.split("/").last()
val apkFile = File(cacheDir, "update-$artifact.apk") val apkFile = File(cacheDir, "update-$artifact.apk")
withContext(Dispatchers.Main) { withContext(Dispatchers.Main) {
showDownloadProgressDialog() showDownloadProgressDialog()
} }
val downloader = APKDownloader(asset, apkFile) val downloader = APKDownloader(apkUrl, apkFile)
downloader.download( downloader.download(
onProgress = { progress -> onProgress = { progress ->
runOnUiThread { runOnUiThread {
@@ -254,7 +248,7 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
} else { } else {
Toast.makeText( Toast.makeText(
this@MainActivity, this@MainActivity,
getString(R.string.update_download_failed) + "\n\nURL: $asset", getString(R.string.update_download_failed) + "\n\nURL: $apkUrl",
Toast.LENGTH_LONG Toast.LENGTH_LONG
).show() ).show()
} }
@@ -283,7 +277,7 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
private fun updateDownloadProgress(progress: Int) { private fun updateDownloadProgress(progress: Int) {
progressBar?.progress = progress progressBar?.progress = progress
progressMessage?.text = getString(R.string.percent, progress) progressMessage?.text = "$progress%"
} }
private fun dismissDownloadProgressDialog() { private fun dismissDownloadProgressDialog() {
+58 -58
View File
@@ -1699,76 +1699,76 @@ JNIEXPORT jboolean JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_isNightlyBuild(
#ifdef ENABLE_UPDATE_CHECKER #ifdef ENABLE_UPDATE_CHECKER
JNIEXPORT jobject JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_checkForUpdate( JNIEXPORT jobjectArray JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_checkForUpdate(
JNIEnv* env, JNIEnv* env,
jobject obj) { jobject obj) {
std::optional<Common::Net::Release> release = UpdateChecker::GetUpdate(); std::optional<UpdateChecker::Update> release = UpdateChecker::GetUpdate();
if (!release) return nullptr; if (!release) return nullptr;
const std::string tag = release->tag; const std::string tag = release->tag;
const std::string title = release->title; const std::string name = release->name;
const std::string body = release->body;
const std::string url = release->html_url;
// Android *should* only ever define a single asset. jobjectArray result = env->NewObjectArray(2, env->FindClass("java/lang/String"), nullptr);
// If not, something has gone wrong, but the Kotlin side can handle it.
const auto assets = release->GetPlatformAssets();
jclass updateResultClass = env->FindClass("org/yuzu/yuzu_emu/NativeLibrary$UpdateResult"); const jstring jtag = env->NewStringUTF(tag.c_str());
if (!updateResultClass) { const jstring jname = env->NewStringUTF(name.c_str());
LOG_ERROR(Frontend, "Could not find UpdateResult class");
return nullptr;
}
jmethodID updateResultCtor = env->GetMethodID(updateResultClass, "<init>", "()V");
if (!updateResultCtor) {
LOG_ERROR(Frontend, "Could not find UpdateResult ctor");
env->DeleteLocalRef(updateResultClass);
return nullptr;
}
jmethodID setTag = env->GetMethodID(updateResultClass, "setTag", "(Ljava/lang/String;)V");
jmethodID setTitle = env->GetMethodID(updateResultClass, "setTitle", "(Ljava/lang/String;)V");
jmethodID setBody = env->GetMethodID(updateResultClass, "setBody", "(Ljava/lang/String;)V");
jmethodID setUrl = env->GetMethodID(updateResultClass, "setUrl", "(Ljava/lang/String;)V");
jmethodID addAsset = env->GetMethodID(updateResultClass, "addAsset", "(Ljava/lang/String;)V");
jobject updateResult = env->NewObject(updateResultClass, updateResultCtor);
LOG_DEBUG(Frontend, "Tag: {}", tag);
LOG_DEBUG(Frontend, "Title: {}", title);
LOG_DEBUG(Frontend, "Body: {}", body);
LOG_DEBUG(Frontend, "Url: {}", url);
const auto jtag = env->NewStringUTF(tag.c_str());
const auto jtitle = env->NewStringUTF(title.c_str());
const auto jbody = env->NewStringUTF(body.c_str());
const auto jurl = env->NewStringUTF(url.c_str());
env->CallVoidMethod(updateResult, setTag, jtag);
env->CallVoidMethod(updateResult, setTitle, jtitle);
env->CallVoidMethod(updateResult, setBody, jbody);
env->CallVoidMethod(updateResult, setUrl, jurl);
// TODO(crueter): Handling for multiple assets?
// Maybe another data class x(
for (const Common::Net::Asset &a : assets) {
const auto jaurl = env->NewStringUTF(a.path.c_str());
env->CallVoidMethod(updateResult, addAsset, jaurl);
env->DeleteLocalRef(jaurl);
}
env->SetObjectArrayElement(result, 0, jtag);
env->SetObjectArrayElement(result, 1, jname);
env->DeleteLocalRef(jtag); env->DeleteLocalRef(jtag);
env->DeleteLocalRef(jtitle); env->DeleteLocalRef(jname);
env->DeleteLocalRef(jbody);
env->DeleteLocalRef(jurl);
env->DeleteLocalRef(updateResultClass); return result;
return updateResult;
} }
JNIEXPORT jstring JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_getUpdateUrl(
JNIEnv* env,
jobject obj,
jstring version) {
const char* version_str = env->GetStringUTFChars(version, nullptr);
const std::string url = fmt::format("{}/{}",
std::string{Common::g_build_auto_update_api},
version_str);
env->ReleaseStringUTFChars(version, version_str);
return env->NewStringUTF(url.c_str());
}
JNIEXPORT jstring JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_getUpdateApkUrl(
JNIEnv* env,
jobject obj,
jstring tag,
jstring artifact,
jstring packageId) {
const char* version_str = env->GetStringUTFChars(tag, nullptr);
const char* artifact_str = env->GetStringUTFChars(artifact, nullptr);
const char* package_id_str = env->GetStringUTFChars(packageId, nullptr);
std::string variant;
std::string package_id(package_id_str);
if (package_id.find("dev.legacy.eden_emulator") != std::string::npos) {
variant = "legacy";
} else if (package_id.find("com.miHoYo.Yuanshen") != std::string::npos) {
variant = "optimized";
} else {
#ifdef ARCHITECTURE_arm64
variant = "standard";
#else
variant = "chromeos";
#endif
}
const std::string apk_filename = fmt::format("Eden-Android-{}-{}.apk", artifact_str, variant);
const std::string url = fmt::format("{}/{}/{}",
std::string{Common::g_build_auto_update_api},
version_str, apk_filename);
env->ReleaseStringUTFChars(tag, version_str);
env->ReleaseStringUTFChars(artifact, artifact_str);
env->ReleaseStringUTFChars(packageId, package_id_str);
return env->NewStringUTF(url.c_str());
}
#endif #endif
JNIEXPORT jstring JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_getBuildVersion( JNIEXPORT jstring JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_getBuildVersion(
@@ -1159,6 +1159,5 @@
<string name="license_fidelityfx_fsr_description">تحسين الجودة بدرجة عالية من AMD</string> <string name="license_fidelityfx_fsr_description">تحسين الجودة بدرجة عالية من AMD</string>
<string name="external_content">محتوى خارجي</string> <string name="external_content">محتوى خارجي</string>
<string name="add_folders">إضافة مجلد</string> <string name="add_folders">إضافة مجلد</string>
<string name="percent">%1$d%%</string>
</resources> </resources>
@@ -767,7 +767,7 @@
<string name="version">Versión</string> <string name="version">Versión</string>
<string name="copy_details">Copiar detalles</string> <string name="copy_details">Copiar detalles</string>
<string name="add_ons">Complementos</string> <string name="add_ons">Complementos</string>
<string name="add_ons_description">Alternar mods, actualizaciones y contenido descargable</string> <string name="add_ons_description">Activa/desactiva mods, actualizaciones y contenidos descargables</string>
<string name="playtime">Tiempo jugado:</string> <string name="playtime">Tiempo jugado:</string>
<string name="reset_playtime">Borrar tiempo de juego</string> <string name="reset_playtime">Borrar tiempo de juego</string>
<string name="reset_playtime_description">Restablecer el tiempo de juego actual a 0 segundos</string> <string name="reset_playtime_description">Restablecer el tiempo de juego actual a 0 segundos</string>
@@ -1153,6 +1153,5 @@
<string name="license_fidelityfx_fsr_description">Upscaling de alta calidad de AMD</string> <string name="license_fidelityfx_fsr_description">Upscaling de alta calidad de AMD</string>
<string name="external_content">Contenido externo</string> <string name="external_content">Contenido externo</string>
<string name="add_folders">Añadir carpeta</string> <string name="add_folders">Añadir carpeta</string>
<string name="percent">%1$d%%</string>
</resources> </resources>
@@ -413,9 +413,7 @@
<string name="frame_limit_slider">Limiter le pourcentage de vitesse</string> <string name="frame_limit_slider">Limiter le pourcentage de vitesse</string>
<string name="frame_limit_slider_description">Spécifier le pourcentage pour limiter la vitesse d\'émulation. 100% correspond à la vitesse normale. Des valeurs plus élevées ou plus basses augmenteront ou diminueront la limite de vitesse.</string> <string name="frame_limit_slider_description">Spécifier le pourcentage pour limiter la vitesse d\'émulation. 100% correspond à la vitesse normale. Des valeurs plus élevées ou plus basses augmenteront ou diminueront la limite de vitesse.</string>
<string name="turbo_speed_limit">Vitesse turbo</string> <string name="turbo_speed_limit">Vitesse turbo</string>
<string name="turbo_speed_limit_description">Lorsque le mode Turbo est activé, l\'émulation s\'exécute à cette vitesse.</string>
<string name="slow_speed_limit">Vitesse lente</string> <string name="slow_speed_limit">Vitesse lente</string>
<string name="slow_speed_limit_description">Lorsque le mode Lent est activé, l\'émulation s\'exécute à cette vitesse.</string>
<string name="cpu_backend">Backend du CPU</string> <string name="cpu_backend">Backend du CPU</string>
<string name="cpu_accuracy">Précision du CPU</string> <string name="cpu_accuracy">Précision du CPU</string>
<string name="value_with_units">%1$s%2$s</string> <string name="value_with_units">%1$s%2$s</string>
@@ -489,8 +489,6 @@
<string name="enable_buffer_history">Включить историю буфера</string> <string name="enable_buffer_history">Включить историю буфера</string>
<string name="enable_buffer_history_description">Позволяет обращаться к предыдущим состояниям буфера. Эта опция может повысить качество рендеринга и стабильность производительности в некоторых играх.</string> <string name="enable_buffer_history_description">Позволяет обращаться к предыдущим состояниям буфера. Эта опция может повысить качество рендеринга и стабильность производительности в некоторых играх.</string>
<string name="use_optimized_vertex_buffers">Оптимизированные вершинные буферы</string> <string name="use_optimized_vertex_buffers">Оптимизированные вершинные буферы</string>
<string name="use_optimized_vertex_buffers_description">Включает оптимизированную привязку вершинного буфера для повышения производительности. Требует Mesa Turnip 26.0+ / QCOM драйверы. Приводит к вылету на старых версиях Turnip.</string>
<string name="hacks">Хаки</string> <string name="hacks">Хаки</string>
<string name="fast_gpu_time">Быстрое время ГПУ</string> <string name="fast_gpu_time">Быстрое время ГПУ</string>
@@ -498,9 +496,6 @@
<string name="skip_cpu_inner_invalidation">Пропустить внутреннюю инвалидацию ЦП</string> <string name="skip_cpu_inner_invalidation">Пропустить внутреннюю инвалидацию ЦП</string>
<string name="skip_cpu_inner_invalidation_description">Пропускает некоторые инвалидации кэша на стороне ЦП при обновлениях памяти, уменьшая нагрузку на процессор и повышая производительность. Может вызывать сбои в некоторых играх.</string> <string name="skip_cpu_inner_invalidation_description">Пропускает некоторые инвалидации кэша на стороне ЦП при обновлениях памяти, уменьшая нагрузку на процессор и повышая производительность. Может вызывать сбои в некоторых играх.</string>
<string name="fix_bloom_effects">Исправить эффекты размытия</string> <string name="fix_bloom_effects">Исправить эффекты размытия</string>
<string name="fix_bloom_effects_description">Частично убирает размытие в LA/EOW (Adreno A6XX - A7XX/ Turnip), полностью отключает его в Burnout. Внимание: может вызывать графические артефакты в других играх.</string>
<string name="emulate_bgr565">Эмулировать BGR565</string>
<string name="emulate_bgr565_description">Исправляет проблемы с инвертированными цветами в играх, а также со странными артефактами или некорректными тенями.</string>
<string name="renderer_asynchronous_shaders">Использовать асинхронные шейдеры</string> <string name="renderer_asynchronous_shaders">Использовать асинхронные шейдеры</string>
<string name="renderer_asynchronous_shaders_description">Компилирует шейдеры асинхронно. Это может уменьшить подтормаживания, но также может вызвать графические артефакты.</string> <string name="renderer_asynchronous_shaders_description">Компилирует шейдеры асинхронно. Это может уменьшить подтормаживания, но также может вызвать графические артефакты.</string>
<string name="gpu_unswizzle_settings">Настройки распаковки текстур (Unswizzle)</string> <string name="gpu_unswizzle_settings">Настройки распаковки текстур (Unswizzle)</string>
@@ -1155,4 +1150,5 @@
<string name="license_fidelityfx_fsr_description">Высококачественное масштабирование от AMD</string> <string name="license_fidelityfx_fsr_description">Высококачественное масштабирование от AMD</string>
<string name="external_content">Дополнительный контент</string> <string name="external_content">Дополнительный контент</string>
<string name="add_folders">Добавить папку</string> <string name="add_folders">Добавить папку</string>
</resources>
</resources>
@@ -1155,6 +1155,5 @@
<string name="license_fidelityfx_fsr_description">Високоякісне масштабування від AMD</string> <string name="license_fidelityfx_fsr_description">Високоякісне масштабування від AMD</string>
<string name="external_content">Зовнішній вміст</string> <string name="external_content">Зовнішній вміст</string>
<string name="add_folders">Додати теку</string> <string name="add_folders">Додати теку</string>
<string name="percent">%1$d%%</string>
</resources> </resources>
@@ -483,8 +483,6 @@
<string name="enable_buffer_history">启用缓冲区历史</string> <string name="enable_buffer_history">启用缓冲区历史</string>
<string name="enable_buffer_history_description">启用对先前缓冲区状态的访问。此选项可在某些游戏中提升渲染质量并保持性能的一致性。</string> <string name="enable_buffer_history_description">启用对先前缓冲区状态的访问。此选项可在某些游戏中提升渲染质量并保持性能的一致性。</string>
<string name="use_optimized_vertex_buffers">优化顶点缓冲区</string> <string name="use_optimized_vertex_buffers">优化顶点缓冲区</string>
<string name="use_optimized_vertex_buffers_description">启用经过优化的顶点缓冲区绑定以提升性能。需要 Mesa 26.0 及以上版本的 Turnip 或 QCOM 驱动程序。若使用较旧版本的 Turnip 驱动则会导致崩溃。</string>
<string name="hacks">Hacks</string> <string name="hacks">Hacks</string>
<string name="fast_gpu_time">GPU 超频频率</string> <string name="fast_gpu_time">GPU 超频频率</string>
@@ -492,9 +490,6 @@
<string name="skip_cpu_inner_invalidation">跳过CPU内部无效化</string> <string name="skip_cpu_inner_invalidation">跳过CPU内部无效化</string>
<string name="skip_cpu_inner_invalidation_description">在内存更新期间跳过某些CPU端缓存无效化,减少CPU使用率并提高其性能。可能会导致某些游戏出现故障或崩溃。</string> <string name="skip_cpu_inner_invalidation_description">在内存更新期间跳过某些CPU端缓存无效化,减少CPU使用率并提高其性能。可能会导致某些游戏出现故障或崩溃。</string>
<string name="fix_bloom_effects">修复 Bloom 效果</string> <string name="fix_bloom_effects">修复 Bloom 效果</string>
<string name="fix_bloom_effects_description">减少《塞尔达传说:智慧的再现》(Adreno A6XX - A7XX/ Turnip)中的 bloom 模糊,并移除《Burnout》中的 bloom 效果。警告:可能会导致在其他游戏中出现图形异常。</string>
<string name="emulate_bgr565">模拟 BGR565</string>
<string name="emulate_bgr565_description">修复了游戏中的颜色反转以及出现的异常画面瑕疵或奇怪阴影问题</string>
<string name="renderer_asynchronous_shaders">使用异步着色器</string> <string name="renderer_asynchronous_shaders">使用异步着色器</string>
<string name="renderer_asynchronous_shaders_description">异步编译着色器。这可能会减少卡顿,但也可能会导致图形错误。</string> <string name="renderer_asynchronous_shaders_description">异步编译着色器。这可能会减少卡顿,但也可能会导致图形错误。</string>
<string name="gpu_unswizzle_settings">GPU 还原设置</string> <string name="gpu_unswizzle_settings">GPU 还原设置</string>
@@ -1149,6 +1144,5 @@
<string name="license_fidelityfx_fsr_description">AMD 的高品质画面增强技术</string> <string name="license_fidelityfx_fsr_description">AMD 的高品质画面增强技术</string>
<string name="external_content">外部内容</string> <string name="external_content">外部内容</string>
<string name="add_folders">添加文件夹</string> <string name="add_folders">添加文件夹</string>
<string name="percent">%1$d%%</string>
</resources> </resources>
@@ -1783,6 +1783,5 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
<string name="external_content">External Content</string> <string name="external_content">External Content</string>
<string name="add_folders">Add Folder</string> <string name="add_folders">Add Folder</string>
<string name="percent">%1$d%%</string>
</resources> </resources>
+2 -3
View File
@@ -147,8 +147,7 @@ add_library(
zstd_compression.h zstd_compression.h
fs/ryujinx_compat.h fs/ryujinx_compat.cpp fs/ryujinx_compat.h fs/ryujinx_compat.cpp
fs/symlink.h fs/symlink.cpp fs/symlink.h fs/symlink.cpp
httplib.h httplib.h)
net/net.h net/net.cpp)
if(WIN32) if(WIN32)
target_sources(common PRIVATE windows/timer_resolution.cpp target_sources(common PRIVATE windows/timer_resolution.cpp
@@ -246,7 +245,7 @@ else()
target_link_libraries(common PUBLIC Boost::headers) target_link_libraries(common PUBLIC Boost::headers)
endif() endif()
target_link_libraries(common PUBLIC Boost::filesystem Boost::context httplib::httplib nlohmann_json::nlohmann_json) target_link_libraries(common PUBLIC Boost::filesystem Boost::context httplib::httplib)
if (lz4_ADDED) if (lz4_ADDED)
target_include_directories(common PRIVATE ${lz4_SOURCE_DIR}/lib) target_include_directories(common PRIVATE ${lz4_SOURCE_DIR}/lib)
+26 -15
View File
@@ -13,13 +13,13 @@
namespace Common { namespace Common {
template <typename BaseAddr> template <typename BaseAddr>
MultiLevelPageTable<BaseAddr>::MultiLevelPageTable(std::size_t address_space_bits_, std::size_t first_level_bits_, std::size_t page_bits_) MultiLevelPageTable<BaseAddr>::MultiLevelPageTable(std::size_t address_space_bits_,
: address_space_bits{address_space_bits_} std::size_t first_level_bits_,
, first_level_bits{first_level_bits_} std::size_t page_bits_)
, page_bits{page_bits_} : address_space_bits{address_space_bits_},
{ first_level_bits{first_level_bits_}, page_bits{page_bits_} {
if (page_bits == 0) { if (page_bits == 0) {
return; return;
} }
first_level_shift = address_space_bits - first_level_bits; first_level_shift = address_space_bits - first_level_bits;
first_level_chunk_size = (1ULL << (first_level_shift - page_bits)) * sizeof(BaseAddr); first_level_chunk_size = (1ULL << (first_level_shift - page_bits)) * sizeof(BaseAddr);
@@ -30,9 +30,12 @@ MultiLevelPageTable<BaseAddr>::MultiLevelPageTable(std::size_t address_space_bit
void* base{VirtualAlloc(nullptr, alloc_size, MEM_RESERVE, PAGE_READWRITE)}; void* base{VirtualAlloc(nullptr, alloc_size, MEM_RESERVE, PAGE_READWRITE)};
#else #else
void* base{mmap(nullptr, alloc_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0)}; void* base{mmap(nullptr, alloc_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0)};
if (base == MAP_FAILED)
if (base == MAP_FAILED) {
base = nullptr; base = nullptr;
}
#endif #endif
ASSERT(base); ASSERT(base);
base_ptr = reinterpret_cast<BaseAddr*>(base); base_ptr = reinterpret_cast<BaseAddr*>(base);
} }
@@ -53,21 +56,29 @@ template <typename BaseAddr>
void MultiLevelPageTable<BaseAddr>::ReserveRange(u64 start, std::size_t size) { void MultiLevelPageTable<BaseAddr>::ReserveRange(u64 start, std::size_t size) {
const u64 new_start = start >> first_level_shift; const u64 new_start = start >> first_level_shift;
const u64 new_end = (start + size) >> first_level_shift; const u64 new_end = (start + size) >> first_level_shift;
for (u64 i = new_start; i <= new_end; i++) for (u64 i = new_start; i <= new_end; i++) {
if (!first_level_map[i]) if (!first_level_map[i]) {
AllocateLevel(i); AllocateLevel(i);
}
}
} }
template <typename BaseAddr> template <typename BaseAddr>
void MultiLevelPageTable<BaseAddr>::AllocateLevel(u64 index) { void MultiLevelPageTable<BaseAddr>::AllocateLevel(u64 level) {
void* ptr = reinterpret_cast<char *>(base_ptr) + index * first_level_chunk_size; void* ptr = reinterpret_cast<char *>(base_ptr) + level * first_level_chunk_size;
#ifdef _WIN32 #ifdef _WIN32
void* base = VirtualAlloc(ptr, first_level_chunk_size, MEM_COMMIT, PAGE_READWRITE); void* base{VirtualAlloc(ptr, first_level_chunk_size, MEM_COMMIT, PAGE_READWRITE)};
ASSERT(base);
#else #else
void* base = ptr; void* base{mmap(ptr, first_level_chunk_size, PROT_READ | PROT_WRITE,
MAP_ANONYMOUS | MAP_PRIVATE, -1, 0)};
if (base == MAP_FAILED) {
base = nullptr;
}
#endif #endif
first_level_map[index] = base; ASSERT(base);
first_level_map[level] = base;
} }
} // namespace Common } // namespace Common
-287
View File
@@ -1,287 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#include <optional>
#include <boost/algorithm/string/classification.hpp>
#include <boost/algorithm/string/replace.hpp>
#include <boost/algorithm/string/split.hpp>
#include <fmt/format.h>
#include "common/scm_rev.h"
#include "net.h"
#include "common/logging.h"
#include "common/httplib.h"
#ifdef YUZU_BUNDLED_OPENSSL
#include <openssl/cert.h>
#endif
#define QT_TR_NOOP(x) x
namespace Common::Net {
std::vector<Asset> Release::GetPlatformAssets() const {
// TODO(crueter): Need better handling for this as a whole.
#ifdef NIGHTLY_BUILD
std::vector<std::string> result;
boost::algorithm::split(result, tag, boost::is_any_of("."));
if (result.size() != 2)
return {};
const auto ref = result.at(1);
#else
const auto ref = tag;
#endif
std::vector<Asset> found_assets;
// FIXME: This is mildly inefficient.
// Finds assets based on a hierarchy of regex search strings.
const auto find_asset = [&found_assets, ref, this](const std::string& name,
const std::vector<std::string>& suffixes) {
for (const std::string& asset : assets) {
for (const auto& suffix : suffixes) {
if (asset.ends_with(suffix)) {
const std::string_view asset_sv = asset;
const size_t pos = asset_sv.find_last_of('/');
const std::string_view filename =
(pos != std::string_view::npos) ? asset_sv.substr(pos + 1) : asset_sv;
found_assets.emplace_back(Asset{
.name = name,
.url = host,
.path = asset,
.filename = std::string{filename},
});
return;
}
}
}
};
#ifdef _WIN32
#ifdef ARCHITECTURE_x86_64
find_asset("Standard", {"amd64-msvc-standard.exe", "amd64-msvc-standard.zip", "mingw-amd64-gcc-standard.exe", "mingw-amd64-gcc-standard.zip"});
find_asset("PGO", {"mingw-amd64-clang-pgo.exe", "mingw-amd64-clang-pgo.zip"});
#elif defined(ARCHITECTURE_arm64)
find_asset("Standard", {"mingw-arm64-clang-standard.exe", "mingw-arm64-clang-standard.zip"});
find_asset("PGO", {"mingw-arm64-clang-pgo.exe", "mingw-arm64-clang-pgo.zip"});
#endif
#elif defined(__APPLE__)
#ifdef ARCHITECTURE_arm64
find_asset("Standard", {".dmg", ".tar.gz"});
#endif
#elif defined(__ANDROID__)
#ifdef ARCHITECTURE_x86_64
find_asset("Standard", {"chromeos.apk"});
#elif defined(ARCHITECTURE_arm64)
#ifdef YUZU_LEGACY
find_asset("Standard", {"legacy.apk"});
#elif defined(GENSHIN_SPOOF)
find_asset("Standard", {"optimized.apk"});
#else
find_asset("Standard", {"standard.apk"});
#endif
#endif
#endif
return found_assets;
}
static inline u64 ParseIsoTimestamp(const std::string& iso) {
if (iso.empty())
return 0;
std::string buf = iso;
if (buf.back() == 'Z')
buf.pop_back();
std::tm tm{};
std::istringstream ss(buf);
ss >> std::get_time(&tm, "%Y-%m-%dT%H:%M:%S");
if (ss.fail())
return 0;
#ifdef _WIN32
return static_cast<u64>(_mkgmtime(&tm));
#else
return static_cast<u64>(timegm(&tm));
#endif
}
std::optional<Release> Release::FromJson(const nlohmann::json& json, const std::string& host,
const std::string& repo) {
Release rel;
if (!json.is_object())
return std::nullopt;
rel.tag = json.value("tag_name", std::string{});
if (rel.tag.empty())
return std::nullopt;
rel.title = json.value("name", rel.tag);
rel.id = json.value("id", std::hash<std::string>{}(rel.title));
rel.published = ParseIsoTimestamp(json.value("published_at", std::string{}));
rel.prerelease = json.value("prerelease", false);
auto body = json.value("body", rel.title);
boost::replace_all(body, "\\r", "");
boost::replace_all(body, "\\n", "\n");
rel.body = body;
rel.host = host;
const auto release_base =
fmt::format("{}/{}/releases", Common::g_build_auto_update_website, repo);
const auto fallback_html = fmt::format("{}/tag/{}", release_base, rel.tag);
rel.html_url = json.value("html_url", fallback_html);
// This is our own "fake" API.
if (json.contains("base")) {
const auto base = json.value("base", fmt::format("https://{}", Common::g_build_auto_update_api));
rel.base_download_url = fmt::format("{}/{}", base, rel.tag);
// Assets are easy :)
rel.assets = json.value("assets", std::vector<std::string>{});
} else {
const auto base_download_url = fmt::format("/{}/releases/download/{}", repo, rel.tag);
rel.base_download_url = base_download_url;
// assets are a bit more complex here. :(
std::vector<std::string> assets;
const nlohmann::json& arr = json["assets"];
for (const auto &obj : arr) {
const auto url = obj.value("browser_download_url", std::string{});
assets.emplace_back(url);
}
rel.assets = assets;
}
return rel;
}
std::optional<Release> Release::FromJson(const std::string_view& json, const std::string& host,
const std::string& repo) {
try {
return FromJson(nlohmann::json::parse(json), host, repo);
} catch (std::exception& e) {
LOG_WARNING(Common, "Failed to parse JSON: {}", e.what());
}
return {};
}
std::vector<Release> Release::ListFromJson(const nlohmann::json& json, const std::string& host,
const std::string& repo) {
if (!json.is_array())
return {};
std::vector<Release> releases;
for (const auto& obj : json) {
auto rel = Release::FromJson(obj, host, repo);
if (rel)
releases.emplace_back(rel.value());
}
return releases;
}
std::vector<Release> Release::ListFromJson(const std::string_view& json, const std::string& host,
const std::string& repo) {
try {
return ListFromJson(nlohmann::json::parse(json), host, repo);
} catch (std::exception& e) {
LOG_WARNING(Common, "Failed to parse JSON: {}", e.what());
}
return {};
}
std::optional<std::string> MakeRequest(const std::string& url, const std::string& path) {
try {
constexpr std::size_t timeout_seconds = 15;
std::unique_ptr<httplib::Client> client = std::make_unique<httplib::Client>(url);
client->set_connection_timeout(timeout_seconds);
client->set_read_timeout(timeout_seconds);
client->set_write_timeout(timeout_seconds);
#ifdef YUZU_BUNDLED_OPENSSL
client->load_ca_cert_store(kCert, sizeof(kCert));
#endif
if (client == nullptr) {
LOG_ERROR(Common, "Invalid URL {}{}", url, path);
return {};
}
httplib::Request request{
.method = "GET",
.path = path,
};
client->set_follow_location(true);
httplib::Result result = client->send(request);
if (!result) {
LOG_ERROR(Common, "GET to {}{} returned null", url, path);
return {};
}
const auto& response = result.value();
if (response.status >= 400) {
LOG_ERROR(Common, "GET to {}{} returned error status code: {}", url, path,
response.status);
return {};
}
if (!response.headers.contains("content-type")) {
LOG_ERROR(Common, "GET to {}{} returned no content", url, path);
return {};
}
return response.body;
} catch (std::exception& e) {
LOG_ERROR(Common, "GET to {}{} failed during update check: {}", url, path, e.what());
return std::nullopt;
}
}
std::vector<Release> GetReleases() {
const auto body = GetReleasesBody();
if (!body) {
LOG_WARNING(Common, "Failed to get stable releases");
return {};
}
const std::string_view body_str = body.value();
const auto url = fmt::format("https://{}", Common::g_build_auto_update_stable_api);
return Release::ListFromJson(body_str, url, Common::g_build_auto_update_stable_repo);
}
std::optional<Release> GetLatestRelease() {
const auto releases_path = Common::g_build_auto_update_api_path;
const auto url = fmt::format("https://{}", Common::g_build_auto_update_api);
const auto body = MakeRequest(url, releases_path);
if (!body) {
LOG_WARNING(Common, "Failed to get latest release");
return std::nullopt;
}
const std::string_view body_str = body.value();
return Release::FromJson(body_str, url, Common::g_build_auto_update_repo);
}
std::optional<std::string> GetReleasesBody() {
const auto releases_path =
fmt::format("/{}/{}/releases", Common::g_build_auto_update_stable_api_path,
Common::g_build_auto_update_stable_repo);
const auto url = fmt::format("https://{}", Common::g_build_auto_update_stable_api);
return MakeRequest(url, releases_path);
}
} // namespace Common::Net
-56
View File
@@ -1,56 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include <optional>
#include <string>
#include <vector>
#include <nlohmann/json.hpp>
#include "common/common_types.h"
namespace Common::Net {
typedef struct {
std::string name;
std::string url;
std::string path;
std::string filename;
} Asset;
typedef struct Release {
std::string title;
std::string body;
std::string tag;
std::string base_download_url;
std::string html_url;
std::string host;
std::vector<std::string> assets;
u64 id;
u64 published;
bool prerelease;
// Get the relevant list of assets for the current platform.
std::vector<Asset> GetPlatformAssets() const;
static std::optional<Release> FromJson(const nlohmann::json& json, const std::string &host, const std::string& repo);
static std::optional<Release> FromJson(const std::string_view& json, const std::string &host, const std::string& repo);
static std::vector<Release> ListFromJson(const nlohmann::json &json, const std::string &host, const std::string &repo);
static std::vector<Release> ListFromJson(const std::string_view &json, const std::string &host, const std::string &repo);
} Release;
// Make a request via httplib, and return the response body if applicable.
std::optional<std::string> MakeRequest(const std::string &url, const std::string &path);
// Get all of the latest stable releases.
std::vector<Release> GetReleases();
// Get all of the latest stable releases as text.
std::optional<std::string> GetReleasesBody();
// Get the latest release of the current channel.
std::optional<Release> GetLatestRelease();
}
+4 -2
View File
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
@@ -142,8 +142,10 @@ struct PageTable {
VirtualBuffer<PageEntryData> entries; VirtualBuffer<PageEntryData> entries;
static_assert(sizeof(PageEntryData) == 32); static_assert(sizeof(PageEntryData) == 32);
u8* fastmem_arena{};
std::size_t current_address_space_width_in_bits{}; std::size_t current_address_space_width_in_bits{};
u8* fastmem_arena{};
std::size_t page_size{}; std::size_t page_size{};
}; };
-6
View File
@@ -22,9 +22,6 @@
#define BUILD_AUTO_UPDATE_API "@BUILD_AUTO_UPDATE_API@" #define BUILD_AUTO_UPDATE_API "@BUILD_AUTO_UPDATE_API@"
#define BUILD_AUTO_UPDATE_API_PATH "@BUILD_AUTO_UPDATE_API_PATH@" #define BUILD_AUTO_UPDATE_API_PATH "@BUILD_AUTO_UPDATE_API_PATH@"
#define BUILD_AUTO_UPDATE_REPO "@BUILD_AUTO_UPDATE_REPO@" #define BUILD_AUTO_UPDATE_REPO "@BUILD_AUTO_UPDATE_REPO@"
#define BUILD_AUTO_UPDATE_STABLE_API "@BUILD_AUTO_UPDATE_STABLE_API@"
#define BUILD_AUTO_UPDATE_STABLE_API_PATH "@BUILD_AUTO_UPDATE_STABLE_API_PATH@"
#define BUILD_AUTO_UPDATE_STABLE_REPO "@BUILD_AUTO_UPDATE_STABLE_REPO@"
#define IS_NIGHTLY_BUILD @IS_NIGHTLY_BUILD@ #define IS_NIGHTLY_BUILD @IS_NIGHTLY_BUILD@
namespace Common { namespace Common {
@@ -48,8 +45,5 @@ constexpr const char g_build_auto_update_website[] = BUILD_AUTO_UPDATE_WEBSITE;
constexpr const char g_build_auto_update_api[] = BUILD_AUTO_UPDATE_API; constexpr const char g_build_auto_update_api[] = BUILD_AUTO_UPDATE_API;
constexpr const char g_build_auto_update_api_path[] = BUILD_AUTO_UPDATE_API_PATH; constexpr const char g_build_auto_update_api_path[] = BUILD_AUTO_UPDATE_API_PATH;
constexpr const char g_build_auto_update_repo[] = BUILD_AUTO_UPDATE_REPO; constexpr const char g_build_auto_update_repo[] = BUILD_AUTO_UPDATE_REPO;
constexpr const char g_build_auto_update_stable_api[] = BUILD_AUTO_UPDATE_STABLE_API;
constexpr const char g_build_auto_update_stable_api_path[] = BUILD_AUTO_UPDATE_STABLE_API_PATH;
constexpr const char g_build_auto_update_stable_repo[] = BUILD_AUTO_UPDATE_STABLE_REPO;
} // namespace Common } // namespace Common
-3
View File
@@ -28,8 +28,5 @@ extern const char g_build_auto_update_website[];
extern const char g_build_auto_update_api[]; extern const char g_build_auto_update_api[];
extern const char g_build_auto_update_api_path[]; extern const char g_build_auto_update_api_path[];
extern const char g_build_auto_update_repo[]; extern const char g_build_auto_update_repo[];
extern const char g_build_auto_update_stable_api[];
extern const char g_build_auto_update_stable_api_path[];
extern const char g_build_auto_update_stable_repo[];
} // namespace Common } // namespace Common
+17 -18
View File
@@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
@@ -24,9 +21,7 @@ public:
// "with the current allocator"); // "with the current allocator");
constexpr VirtualBuffer() = default; constexpr VirtualBuffer() = default;
explicit VirtualBuffer(std::size_t count) noexcept explicit VirtualBuffer(std::size_t count) : alloc_size{count * sizeof(T)} {
: alloc_size{count * sizeof(T)}
{
base_ptr = reinterpret_cast<T*>(AllocateMemoryPages(alloc_size)); base_ptr = reinterpret_cast<T*>(AllocateMemoryPages(alloc_size));
} }
@@ -38,8 +33,8 @@ public:
VirtualBuffer& operator=(const VirtualBuffer&) = delete; VirtualBuffer& operator=(const VirtualBuffer&) = delete;
VirtualBuffer(VirtualBuffer&& other) noexcept VirtualBuffer(VirtualBuffer&& other) noexcept
: alloc_size{std::exchange(other.alloc_size, 0)}, base_ptr{std::exchange(other.base_ptr), nullptr} : alloc_size{std::exchange(other.alloc_size, 0)}, base_ptr{std::exchange(other.base_ptr),
{} nullptr} {}
VirtualBuffer& operator=(VirtualBuffer&& other) noexcept { VirtualBuffer& operator=(VirtualBuffer&& other) noexcept {
alloc_size = std::exchange(other.alloc_size, 0); alloc_size = std::exchange(other.alloc_size, 0);
@@ -47,31 +42,35 @@ public:
return *this; return *this;
} }
void resize(std::size_t count) noexcept { void resize(std::size_t count) {
if (auto const new_size = count * sizeof(T); new_size != alloc_size) { const auto new_size = count * sizeof(T);
FreeMemoryPages(base_ptr, alloc_size); if (new_size == alloc_size) {
alloc_size = new_size; return;
base_ptr = reinterpret_cast<T*>(AllocateMemoryPages(alloc_size));
} }
FreeMemoryPages(base_ptr, alloc_size);
alloc_size = new_size;
base_ptr = reinterpret_cast<T*>(AllocateMemoryPages(alloc_size));
} }
[[nodiscard]] constexpr const T& operator[](std::size_t index) const noexcept { [[nodiscard]] constexpr const T& operator[](std::size_t index) const {
return base_ptr[index]; return base_ptr[index];
} }
[[nodiscard]] constexpr T& operator[](std::size_t index) noexcept { [[nodiscard]] constexpr T& operator[](std::size_t index) {
return base_ptr[index]; return base_ptr[index];
} }
[[nodiscard]] constexpr T* data() noexcept { [[nodiscard]] constexpr T* data() {
return base_ptr; return base_ptr;
} }
[[nodiscard]] constexpr const T* data() const noexcept { [[nodiscard]] constexpr const T* data() const {
return base_ptr; return base_ptr;
} }
[[nodiscard]] constexpr std::size_t size() const noexcept { [[nodiscard]] constexpr std::size_t size() const {
return alloc_size / sizeof(T); return alloc_size / sizeof(T);
} }
+2 -2
View File
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
@@ -283,7 +283,7 @@ std::string_view GDBStubA32::GetTargetXML() const {
<reg name="r11" bitsize="32" type="uint32"/> <reg name="r11" bitsize="32" type="uint32"/>
<reg name="r12" bitsize="32" type="uint32"/> <reg name="r12" bitsize="32" type="uint32"/>
<reg name="sp" bitsize="32" type="data_ptr"/> <reg name="sp" bitsize="32" type="data_ptr"/>
<reg name="lr" bitsize="32"/> <reg name="lr" bitsize="32" type="code_ptr"/>
<reg name="pc" bitsize="32" type="code_ptr"/> <reg name="pc" bitsize="32" type="code_ptr"/>
<!-- The CPSR is register 25, rather than register 16, because <!-- The CPSR is register 25, rather than register 16, because
the FPA registers historically were placed between the PC the FPA registers historically were placed between the PC
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
@@ -109,7 +109,7 @@ Result AesCtrCounterExtendedStorage::GetEntryList(Entry* out_entries, s32* out_e
R_UNLESS(out_entries != nullptr || entry_count == 0, ResultNullptrArgument); R_UNLESS(out_entries != nullptr || entry_count == 0, ResultNullptrArgument);
// Check that our range is valid. // Check that our range is valid.
BucketTree::Offsets table_offsets{}; BucketTree::Offsets table_offsets;
R_TRY(m_table.GetOffsets(std::addressof(table_offsets))); R_TRY(m_table.GetOffsets(std::addressof(table_offsets)));
R_UNLESS(table_offsets.IsInclude(offset, size), ResultOutOfRange); R_UNLESS(table_offsets.IsInclude(offset, size), ResultOutOfRange);
@@ -167,7 +167,7 @@ size_t AesCtrCounterExtendedStorage::Read(u8* buffer, size_t size, size_t offset
ASSERT(Common::IsAligned(offset, BlockSize)); ASSERT(Common::IsAligned(offset, BlockSize));
ASSERT(Common::IsAligned(size, BlockSize)); ASSERT(Common::IsAligned(size, BlockSize));
BucketTree::Offsets table_offsets{}; BucketTree::Offsets table_offsets;
ASSERT(R_SUCCEEDED(m_table.GetOffsets(std::addressof(table_offsets)))); ASSERT(R_SUCCEEDED(m_table.GetOffsets(std::addressof(table_offsets))));
ASSERT(table_offsets.IsInclude(offset, size)); ASSERT(table_offsets.IsInclude(offset, size));
@@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
@@ -92,7 +89,7 @@ public:
virtual size_t Read(u8* buffer, size_t size, size_t offset) const override; virtual size_t Read(u8* buffer, size_t size, size_t offset) const override;
virtual size_t GetSize() const override { virtual size_t GetSize() const override {
BucketTree::Offsets offsets{}; BucketTree::Offsets offsets;
ASSERT(R_SUCCEEDED(m_table.GetOffsets(std::addressof(offsets)))); ASSERT(R_SUCCEEDED(m_table.GetOffsets(std::addressof(offsets))));
return offsets.end_offset; return offsets.end_offset;
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
@@ -270,7 +270,7 @@ Result BucketTree::Find(Visitor* visitor, s64 virtual_address) {
R_UNLESS(virtual_address >= 0, ResultInvalidOffset); R_UNLESS(virtual_address >= 0, ResultInvalidOffset);
R_UNLESS(!this->IsEmpty(), ResultOutOfRange); R_UNLESS(!this->IsEmpty(), ResultOutOfRange);
BucketTree::Offsets offsets{}; BucketTree::Offsets offsets;
R_TRY(this->GetOffsets(std::addressof(offsets))); R_TRY(this->GetOffsets(std::addressof(offsets)));
R_TRY(visitor->Initialize(this, offsets)); R_TRY(visitor->Initialize(this, offsets));
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
@@ -125,7 +125,7 @@ private:
} }
// Get the table offsets. // Get the table offsets.
BucketTree::Offsets table_offsets{}; BucketTree::Offsets table_offsets;
R_TRY(m_table.GetOffsets(std::addressof(table_offsets))); R_TRY(m_table.GetOffsets(std::addressof(table_offsets)));
// Validate arguments. // Validate arguments.
@@ -177,7 +177,7 @@ private:
ASSERT(out != nullptr); ASSERT(out != nullptr);
// Get our table offsets. // Get our table offsets.
BucketTree::Offsets offsets{}; BucketTree::Offsets offsets;
R_TRY(m_table.GetOffsets(std::addressof(offsets))); R_TRY(m_table.GetOffsets(std::addressof(offsets)));
// Set the output. // Set the output.
@@ -195,7 +195,7 @@ private:
R_SUCCEED_IF(size == 0); R_SUCCEED_IF(size == 0);
// Get the table offsets. // Get the table offsets.
BucketTree::Offsets table_offsets{}; BucketTree::Offsets table_offsets;
R_TRY(m_table.GetOffsets(std::addressof(table_offsets))); R_TRY(m_table.GetOffsets(std::addressof(table_offsets)));
// Validate arguments. // Validate arguments.
@@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
@@ -55,7 +52,7 @@ Result IndirectStorage::GetEntryList(Entry* out_entries, s32* out_entry_count, s
R_UNLESS(out_entries != nullptr || entry_count == 0, ResultNullptrArgument); R_UNLESS(out_entries != nullptr || entry_count == 0, ResultNullptrArgument);
// Check that our range is valid. // Check that our range is valid.
BucketTree::Offsets table_offsets{}; BucketTree::Offsets table_offsets;
R_TRY(m_table.GetOffsets(std::addressof(table_offsets))); R_TRY(m_table.GetOffsets(std::addressof(table_offsets)));
R_UNLESS(table_offsets.IsInclude(offset, size), ResultOutOfRange); R_UNLESS(table_offsets.IsInclude(offset, size), ResultOutOfRange);
@@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
@@ -167,7 +164,7 @@ Result IndirectStorage::OperatePerEntry(s64 offset, s64 size, F func) {
R_SUCCEED_IF(size == 0); R_SUCCEED_IF(size == 0);
// Get the table offsets. // Get the table offsets.
BucketTree::Offsets table_offsets{}; BucketTree::Offsets table_offsets;
R_TRY(m_table.GetOffsets(std::addressof(table_offsets))); R_TRY(m_table.GetOffsets(std::addressof(table_offsets)));
// Validate arguments. // Validate arguments.
@@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
@@ -21,7 +18,7 @@ size_t SparseStorage::Read(u8* buffer, size_t size, size_t offset) const {
SparseStorage* self = const_cast<SparseStorage*>(this); SparseStorage* self = const_cast<SparseStorage*>(this);
if (self->GetEntryTable().IsEmpty()) { if (self->GetEntryTable().IsEmpty()) {
BucketTree::Offsets table_offsets{}; BucketTree::Offsets table_offsets;
ASSERT(R_SUCCEEDED(self->GetEntryTable().GetOffsets(std::addressof(table_offsets)))); ASSERT(R_SUCCEEDED(self->GetEntryTable().GetOffsets(std::addressof(table_offsets))));
ASSERT(table_offsets.IsInclude(offset, size)); ASSERT(table_offsets.IsInclude(offset, size));
+32 -17
View File
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
@@ -191,7 +191,8 @@ Result KPageTableBase::InitializeForKernel(bool is_64_bit, KVirtualAddress start
m_cached_physical_heap_region = nullptr; m_cached_physical_heap_region = nullptr;
// Initialize our implementation. // Initialize our implementation.
m_impl.Resize(m_address_space_width, PageBits); m_impl = std::make_unique<Common::PageTable>();
m_impl->Resize(m_address_space_width, PageBits);
// Set the tracking memory. // Set the tracking memory.
m_memory = std::addressof(memory); m_memory = std::addressof(memory);
@@ -201,7 +202,13 @@ Result KPageTableBase::InitializeForKernel(bool is_64_bit, KVirtualAddress start
m_memory_block_slab_manager)); m_memory_block_slab_manager));
} }
Result KPageTableBase::InitializeForProcess(Svc::CreateProcessFlag as_type, bool enable_aslr, bool enable_das_merge, bool from_back, KMemoryManager::Pool pool, KProcessAddress code_address, size_t code_size, KSystemResource* system_resource, KResourceLimit* resource_limit, Core::Memory::Memory& memory, KProcessAddress aslr_space_start) { Result KPageTableBase::InitializeForProcess(Svc::CreateProcessFlag as_type, bool enable_aslr,
bool enable_das_merge, bool from_back,
KMemoryManager::Pool pool, KProcessAddress code_address,
size_t code_size, KSystemResource* system_resource,
KResourceLimit* resource_limit,
Core::Memory::Memory& memory,
KProcessAddress aslr_space_start) {
// Calculate region extents. // Calculate region extents.
const size_t as_width = GetAddressSpaceWidth(as_type); const size_t as_width = GetAddressSpaceWidth(as_type);
const KProcessAddress start = 0; const KProcessAddress start = 0;
@@ -268,7 +275,6 @@ Result KPageTableBase::InitializeForProcess(Svc::CreateProcessFlag as_type, bool
// Set other basic fields. // Set other basic fields.
m_enable_aslr = enable_aslr; m_enable_aslr = enable_aslr;
m_enable_device_address_space_merge = enable_das_merge; m_enable_device_address_space_merge = enable_das_merge;
m_allowed_exec_device_mapping = false;
m_address_space_start = start; m_address_space_start = start;
m_address_space_end = end; m_address_space_end = end;
m_is_kernel = false; m_is_kernel = false;
@@ -313,10 +319,14 @@ Result KPageTableBase::InitializeForProcess(Svc::CreateProcessFlag as_type, bool
// Determine random placements for each region. // Determine random placements for each region.
size_t alias_rnd = 0, heap_rnd = 0, stack_rnd = 0, kmap_rnd = 0; size_t alias_rnd = 0, heap_rnd = 0, stack_rnd = 0, kmap_rnd = 0;
if (enable_aslr) { if (enable_aslr) {
alias_rnd = KSystemControl::GenerateRandomRange(0, remaining_size / RegionAlignment) * RegionAlignment; alias_rnd = KSystemControl::GenerateRandomRange(0, remaining_size / RegionAlignment) *
heap_rnd = KSystemControl::GenerateRandomRange(0, remaining_size / RegionAlignment) * RegionAlignment; RegionAlignment;
stack_rnd = KSystemControl::GenerateRandomRange(0, remaining_size / RegionAlignment) * RegionAlignment; heap_rnd = KSystemControl::GenerateRandomRange(0, remaining_size / RegionAlignment) *
kmap_rnd = KSystemControl::GenerateRandomRange(0, remaining_size / RegionAlignment) * RegionAlignment; RegionAlignment;
stack_rnd = KSystemControl::GenerateRandomRange(0, remaining_size / RegionAlignment) *
RegionAlignment;
kmap_rnd = KSystemControl::GenerateRandomRange(0, remaining_size / RegionAlignment) *
RegionAlignment;
} }
// Setup heap and alias regions. // Setup heap and alias regions.
@@ -435,13 +445,15 @@ Result KPageTableBase::InitializeForProcess(Svc::CreateProcessFlag as_type, bool
ASSERT(heap_last < kmap_start || kmap_last < heap_start); ASSERT(heap_last < kmap_start || kmap_last < heap_start);
// Initialize our implementation. // Initialize our implementation.
m_impl.Resize(m_address_space_width, PageBits); m_impl = std::make_unique<Common::PageTable>();
m_impl->Resize(m_address_space_width, PageBits);
// Set the tracking memory. // Set the tracking memory.
m_memory = std::addressof(memory); m_memory = std::addressof(memory);
// Initialize our memory block manager. // Initialize our memory block manager.
R_RETURN(m_memory_block_manager.Initialize(m_address_space_start, m_address_space_end, m_memory_block_slab_manager)); R_RETURN(m_memory_block_manager.Initialize(m_address_space_start, m_address_space_end,
m_memory_block_slab_manager));
} }
Result KPageTableBase::FinalizeProcess() { Result KPageTableBase::FinalizeProcess() {
@@ -464,7 +476,7 @@ void KPageTableBase::Finalize() {
this->FinalizeProcess(); this->FinalizeProcess();
auto BlockCallback = [&](KProcessAddress addr, u64 size) { auto BlockCallback = [&](KProcessAddress addr, u64 size) {
if (m_impl.fastmem_arena) { if (m_impl->fastmem_arena) {
m_system.DeviceMemory().buffer.Unmap(GetInteger(addr), size, false); m_system.DeviceMemory().buffer.Unmap(GetInteger(addr), size, false);
} }
@@ -502,6 +514,9 @@ void KPageTableBase::Finalize() {
m_resource_limit->Release(Svc::LimitableResource::PhysicalMemoryMax, m_resource_limit->Release(Svc::LimitableResource::PhysicalMemoryMax,
m_mapped_ipc_server_memory); m_mapped_ipc_server_memory);
} }
// Close the backing page table, as the destructor is not called for guest objects.
m_impl.reset();
} }
KProcessAddress KPageTableBase::GetRegionAddress(Svc::MemoryState state) const { KProcessAddress KPageTableBase::GetRegionAddress(Svc::MemoryState state) const {
@@ -2334,7 +2349,7 @@ Result KPageTableBase::QueryPhysicalAddress(Svc::lp64::PhysicalMemoryInfo* out,
TraversalContext context; TraversalContext context;
TraversalEntry next_entry; TraversalEntry next_entry;
bool traverse_valid = bool traverse_valid =
m_impl.BeginTraversal(std::addressof(next_entry), std::addressof(context), virt_addr); m_impl->BeginTraversal(std::addressof(next_entry), std::addressof(context), virt_addr);
R_UNLESS(traverse_valid, ResultInvalidCurrentMemory); R_UNLESS(traverse_valid, ResultInvalidCurrentMemory);
// Set tracking variables. // Set tracking variables.
@@ -2345,7 +2360,7 @@ Result KPageTableBase::QueryPhysicalAddress(Svc::lp64::PhysicalMemoryInfo* out,
while (true) { while (true) {
// Continue the traversal. // Continue the traversal.
traverse_valid = traverse_valid =
m_impl.ContinueTraversal(std::addressof(next_entry), std::addressof(context)); m_impl->ContinueTraversal(std::addressof(next_entry), std::addressof(context));
if (!traverse_valid) { if (!traverse_valid) {
break; break;
} }
@@ -5715,14 +5730,14 @@ Result KPageTableBase::Operate(PageLinkedList* page_list, KProcessAddress virt_a
this->MakePageGroup(pages_to_close, virt_addr, num_pages); this->MakePageGroup(pages_to_close, virt_addr, num_pages);
// Unmap. // Unmap.
m_memory->UnmapRegion(m_impl, virt_addr, num_pages * PageSize, separate_heap); m_memory->UnmapRegion(*m_impl, virt_addr, num_pages * PageSize, separate_heap);
R_SUCCEED(); R_SUCCEED();
} }
case OperationType::Map: { case OperationType::Map: {
ASSERT(virt_addr != 0); ASSERT(virt_addr != 0);
ASSERT(Common::IsAligned(GetInteger(virt_addr), PageSize)); ASSERT(Common::IsAligned(GetInteger(virt_addr), PageSize));
m_memory->MapMemoryRegion(m_impl, virt_addr, num_pages * PageSize, phys_addr, m_memory->MapMemoryRegion(*m_impl, virt_addr, num_pages * PageSize, phys_addr,
ConvertToMemoryPermission(properties.perm), false); ConvertToMemoryPermission(properties.perm), false);
// Open references to pages, if we should. // Open references to pages, if we should.
@@ -5739,7 +5754,7 @@ Result KPageTableBase::Operate(PageLinkedList* page_list, KProcessAddress virt_a
case OperationType::ChangePermissions: case OperationType::ChangePermissions:
case OperationType::ChangePermissionsAndRefresh: case OperationType::ChangePermissionsAndRefresh:
case OperationType::ChangePermissionsAndRefreshAndFlush: { case OperationType::ChangePermissionsAndRefreshAndFlush: {
m_memory->ProtectRegion(m_impl, virt_addr, num_pages * PageSize, m_memory->ProtectRegion(*m_impl, virt_addr, num_pages * PageSize,
ConvertToMemoryPermission(properties.perm)); ConvertToMemoryPermission(properties.perm));
R_SUCCEED(); R_SUCCEED();
} }
@@ -5773,7 +5788,7 @@ Result KPageTableBase::Operate(PageLinkedList* page_list, KProcessAddress virt_a
const size_t size{node.GetNumPages() * PageSize}; const size_t size{node.GetNumPages() * PageSize};
// Map the pages. // Map the pages.
m_memory->MapMemoryRegion(m_impl, virt_addr, size, node.GetAddress(), m_memory->MapMemoryRegion(*m_impl, virt_addr, size, node.GetAddress(),
ConvertToMemoryPermission(properties.perm), separate_heap); ConvertToMemoryPermission(properties.perm), separate_heap);
virt_addr += size; virt_addr += size;
+22 -12
View File
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
@@ -215,7 +215,7 @@ private:
mutable KLightLock m_general_lock; mutable KLightLock m_general_lock;
mutable KLightLock m_map_physical_memory_lock; mutable KLightLock m_map_physical_memory_lock;
KLightLock m_device_map_lock; KLightLock m_device_map_lock;
Common::PageTable m_impl{}; std::unique_ptr<Common::PageTable> m_impl{};
Core::Memory::Memory* m_memory{}; Core::Memory::Memory* m_memory{};
KMemoryBlockManager m_memory_block_manager{}; KMemoryBlockManager m_memory_block_manager{};
u32 m_allocate_option{}; u32 m_allocate_option{};
@@ -223,7 +223,6 @@ private:
bool m_is_kernel{}; bool m_is_kernel{};
bool m_enable_aslr{}; bool m_enable_aslr{};
bool m_enable_device_address_space_merge{}; bool m_enable_device_address_space_merge{};
bool m_allowed_exec_device_mapping{};
KMemoryBlockSlabManager* m_memory_block_slab_manager{}; KMemoryBlockSlabManager* m_memory_block_slab_manager{};
KBlockInfoManager* m_block_info_manager{}; KBlockInfoManager* m_block_info_manager{};
KResourceLimit* m_resource_limit{}; KResourceLimit* m_resource_limit{};
@@ -256,10 +255,6 @@ public:
return m_enable_aslr; return m_enable_aslr;
} }
void AllowDeviceMappingOfExecPages() {
m_allowed_exec_device_mapping = true;
}
bool Contains(KProcessAddress addr) const { bool Contains(KProcessAddress addr) const {
return m_address_space_start <= addr && addr <= m_address_space_end - 1; return m_address_space_start <= addr && addr <= m_address_space_end - 1;
} }
@@ -305,11 +300,26 @@ public:
} }
public: public:
[[nodiscard]] Core::Memory::Memory& GetMemory() noexcept { return *m_memory; } Core::Memory::Memory& GetMemory() {
[[nodiscard]] Core::Memory::Memory const& GetMemory() const noexcept { return *m_memory; } return *m_memory;
[[nodiscard]] Common::PageTable& GetImpl() noexcept { return m_impl; } }
[[nodiscard]] Common::PageTable const& GetImpl() const noexcept { return m_impl; }
[[nodiscard]] size_t GetNumGuardPages() const noexcept { return this->IsKernel() ? 1 : 4; } Core::Memory::Memory& GetMemory() const {
return *m_memory;
}
Common::PageTable& GetImpl() {
return *m_impl;
}
Common::PageTable& GetImpl() const {
return *m_impl;
}
size_t GetNumGuardPages() const {
return this->IsKernel() ? 1 : 4;
}
protected: protected:
// NOTE: These three functions (Operate, Operate, FinalizeUpdate) are virtual functions // NOTE: These three functions (Operate, Operate, FinalizeUpdate) are virtual functions
// in Nintendo's kernel. We devirtualize them, since KPageTable is the only derived // in Nintendo's kernel. We devirtualize them, since KPageTable is the only derived
-13
View File
@@ -341,11 +341,6 @@ Result KProcess::Initialize(const Svc::CreateProcessParameter& params, const KPa
// Initialize capabilities. // Initialize capabilities.
R_TRY(m_capabilities.InitializeForKip(caps, std::addressof(m_page_table))); R_TRY(m_capabilities.InitializeForKip(caps, std::addressof(m_page_table)));
// Enable mapping device pages as executable on legacy processes.
if (m_capabilities.GetIntendedKernelMajorVersion() < 26) {
m_page_table.AllowDeviceMappingOfExecPages();
}
// Initialize the process id. // Initialize the process id.
m_process_id = m_kernel.CreateNewUserProcessID(); m_process_id = m_kernel.CreateNewUserProcessID();
ASSERT(InitialProcessIdMin <= m_process_id); ASSERT(InitialProcessIdMin <= m_process_id);
@@ -442,11 +437,6 @@ Result KProcess::Initialize(const Svc::CreateProcessParameter& params,
// Initialize capabilities. // Initialize capabilities.
R_TRY(m_capabilities.InitializeForUser(user_caps, std::addressof(m_page_table))); R_TRY(m_capabilities.InitializeForUser(user_caps, std::addressof(m_page_table)));
// Enable mapping device pages as executable on legacy processes.
if (m_capabilities.GetIntendedKernelMajorVersion() < 26) {
m_page_table.AllowDeviceMappingOfExecPages();
}
// Initialize the process id. // Initialize the process id.
m_process_id = m_kernel.CreateNewUserProcessID(); m_process_id = m_kernel.CreateNewUserProcessID();
ASSERT(ProcessIdMin <= m_process_id); ASSERT(ProcessIdMin <= m_process_id);
@@ -999,9 +989,6 @@ Result KProcess::Run(s32 priority, size_t stack_size) {
main_thread->GetContext().r[0] = 0; main_thread->GetContext().r[0] = 0;
main_thread->GetContext().r[1] = thread_handle; main_thread->GetContext().r[1] = thread_handle;
// Pass the thread handle to the thread local region.
this->GetMemory().Write32(GetInteger(main_thread->GetTlsAddress()) + 0x110, thread_handle);
// Update our state. // Update our state.
this->ChangeState((state == State::Created) ? State::Running : State::RunningAttached); this->ChangeState((state == State::Created) ? State::Running : State::RunningAttached);
ON_RESULT_FAILURE_2 { ON_RESULT_FAILURE_2 {
+14 -7
View File
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
@@ -33,14 +33,21 @@ public:
m_page_table.Finalize(); m_page_table.Finalize();
} }
void AllowDeviceMappingOfExecPages() { Core::Memory::Memory& GetMemory() {
m_page_table.AllowDeviceMappingOfExecPages(); return m_page_table.GetMemory();
} }
[[nodiscard]] Core::Memory::Memory& GetMemory() noexcept { return m_page_table.GetMemory(); } Core::Memory::Memory& GetMemory() const {
[[nodiscard]] Core::Memory::Memory const& GetMemory() const noexcept { return m_page_table.GetMemory(); } return m_page_table.GetMemory();
[[nodiscard]] Common::PageTable& GetImpl() noexcept { return m_page_table.GetImpl(); } }
[[nodiscard]] Common::PageTable const& GetImpl() const noexcept { return m_page_table.GetImpl(); }
Common::PageTable& GetImpl() {
return m_page_table.GetImpl();
}
Common::PageTable& GetImpl() const {
return m_page_table.GetImpl();
}
size_t GetNumGuardPages() const { size_t GetNumGuardPages() const {
return m_page_table.GetNumGuardPages(); return m_page_table.GetNumGuardPages();
+18 -20
View File
@@ -76,13 +76,11 @@ namespace Kernel {
std::atomic_uint16_t interrupt_flag; std::atomic_uint16_t interrupt_flag;
std::atomic_uint8_t cache_maintenance_flag; std::atomic_uint8_t cache_maintenance_flag;
std::atomic_int64_t thread_cpu_time; std::atomic_int64_t thread_cpu_time;
std::atomic_uint32_t current_thread_handle;
}; };
static_assert(offsetof(ThreadLocalRegion, disable_count) == 0x100); static_assert(offsetof(ThreadLocalRegion, disable_count) == 0x100);
static_assert(offsetof(ThreadLocalRegion, interrupt_flag) == 0x102); static_assert(offsetof(ThreadLocalRegion, interrupt_flag) == 0x102);
static_assert(offsetof(ThreadLocalRegion, cache_maintenance_flag) == 0x104); static_assert(offsetof(ThreadLocalRegion, cache_maintenance_flag) == 0x104);
static_assert(offsetof(ThreadLocalRegion, thread_cpu_time) == 0x108); static_assert(offsetof(ThreadLocalRegion, thread_cpu_time) == 0x108);
static_assert(offsetof(ThreadLocalRegion, current_thread_handle) == 0x110);
class ThreadQueueImplForKThreadSleep final : public KThreadQueueWithoutEndWait { class ThreadQueueImplForKThreadSleep final : public KThreadQueueWithoutEndWait {
public: public:
@@ -130,24 +128,24 @@ namespace Kernel {
// Next, assert things based on the type. // Next, assert things based on the type.
switch (type) { switch (type) {
case ThreadType::Main: case ThreadType::Main:
ASSERT(arg == 0); ASSERT(arg == 0);
[[fallthrough]]; [[fallthrough]];
case ThreadType::User: case ThreadType::User:
ASSERT(((owner == nullptr) || ASSERT(((owner == nullptr) ||
(owner->GetCoreMask() | (1ULL << virt_core)) == owner->GetCoreMask())); (owner->GetCoreMask() | (1ULL << virt_core)) == owner->GetCoreMask()));
ASSERT(((owner == nullptr) || (prio > Svc::LowestThreadPriority) || ASSERT(((owner == nullptr) || (prio > Svc::LowestThreadPriority) ||
(owner->GetPriorityMask() | (1ULL << prio)) == owner->GetPriorityMask())); (owner->GetPriorityMask() | (1ULL << prio)) == owner->GetPriorityMask()));
break; break;
case ThreadType::HighPriority: case ThreadType::HighPriority:
case ThreadType::Dummy: case ThreadType::Dummy:
break; break;
case ThreadType::Kernel: case ThreadType::Kernel:
UNIMPLEMENTED(); UNIMPLEMENTED();
break; break;
default: default:
ASSERT_MSG(false, "KThread::Initialize: Unknown ThreadType {}", static_cast<u32>(type)); ASSERT_MSG(false, "KThread::Initialize: Unknown ThreadType {}", static_cast<u32>(type));
break; break;
} }
m_thread_type = type; m_thread_type = type;
+5 -23
View File
@@ -50,14 +50,6 @@ void PhysicalCore::RunThread(Kernel::KThread* thread) {
}; };
const auto ExitContext = [&]() { const auto ExitContext = [&]() {
// Save the JIT context back to the thread so the debugger can
// read the current register state. Debug halt paths (step,
// breakpoint, watchpoint) return from RunThread without going
// through the scheduler's Unload/SaveContext.
if (system.DebuggerEnabled()) {
interface->GetContext(thread->GetContext());
}
// Unlock the thread. // Unlock the thread.
interface->UnlockThread(thread); interface->UnlockThread(thread);
@@ -105,16 +97,11 @@ void PhysicalCore::RunThread(Kernel::KThread* thread) {
} }
// Determine why we stopped. // Determine why we stopped.
// If a step completed successfully, skip other halt reason handlers — const bool supervisor_call = True(hr & Core::HaltReason::SupervisorCall);
// the step takes priority (e.g. step may also set InstructionBreakpoint const bool prefetch_abort = True(hr & Core::HaltReason::PrefetchAbort);
// if the next instruction happens to be a breakpoint). const bool breakpoint = True(hr & Core::HaltReason::InstructionBreakpoint);
const bool step_completed = True(hr & Core::HaltReason::StepThread) const bool data_abort = True(hr & Core::HaltReason::DataAbort);
&& thread->GetStepState() == StepState::StepPerformed; const bool interrupt = True(hr & Core::HaltReason::BreakLoop);
const bool supervisor_call = !step_completed && True(hr & Core::HaltReason::SupervisorCall);
const bool prefetch_abort = !step_completed && True(hr & Core::HaltReason::PrefetchAbort);
const bool breakpoint = !step_completed && True(hr & Core::HaltReason::InstructionBreakpoint);
const bool data_abort = !step_completed && True(hr & Core::HaltReason::DataAbort);
const bool interrupt = !step_completed && True(hr & Core::HaltReason::BreakLoop);
// Since scheduling may occur here, we cannot use any cached // Since scheduling may occur here, we cannot use any cached
// state after returning from calls we make. // state after returning from calls we make.
@@ -124,11 +111,6 @@ void PhysicalCore::RunThread(Kernel::KThread* thread) {
if (breakpoint || prefetch_abort) { if (breakpoint || prefetch_abort) {
if (breakpoint) { if (breakpoint) {
interface->RewindBreakpointInstruction(); interface->RewindBreakpointInstruction();
// RewindBreakpointInstruction sets the JIT state to the
// saved breakpoint context. Update the thread context to
// match, since ExitContext already saved the post-execution
// state.
interface->GetContext(thread->GetContext());
} }
if (system.DebuggerEnabled()) { if (system.DebuggerEnabled()) {
system.GetDebugger().NotifyThreadStopped(thread); system.GetDebugger().NotifyThreadStopped(thread);
+2 -7
View File
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
@@ -75,12 +75,7 @@ Result CreateThread(Core::System& system, Handle* out_handle, u64 entry_point, u
KThread::Register(kernel, thread); KThread::Register(kernel, thread);
// Add the thread to the handle table. // Add the thread to the handle table.
R_TRY(process.GetHandleTable().Add(out_handle, thread)); R_RETURN(process.GetHandleTable().Add(out_handle, thread));
// Pass the thread handle to the thread local region.
process.GetMemory().Write32(GetInteger(thread->GetTlsAddress()) + 0x110, *out_handle);
R_SUCCEED();
} }
/// Starts the thread for the provided handle /// Starts the thread for the provided handle
+3 -3
View File
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
@@ -53,8 +53,8 @@ constexpr inline u32 RequiredKernelVersion =
// This is the highest SVC version supported, to be updated on new kernel releases. // This is the highest SVC version supported, to be updated on new kernel releases.
// NOTE: Official kernel versions have SVC major = SDK major + 4, SVC minor = SDK minor. // NOTE: Official kernel versions have SVC major = SDK major + 4, SVC minor = SDK minor.
constexpr inline u32 SupportedKernelMajorVersion = ConvertToSvcMajorVersion(22); constexpr inline u32 SupportedKernelMajorVersion = ConvertToSvcMajorVersion(20);
constexpr inline u32 SupportedKernelMinorVersion = ConvertToSvcMinorVersion(2); constexpr inline u32 SupportedKernelMinorVersion = ConvertToSvcMinorVersion(5);
constexpr inline u32 SupportedKernelVersion = constexpr inline u32 SupportedKernelVersion =
EncodeKernelVersion(SupportedKernelMajorVersion, SupportedKernelMinorVersion); EncodeKernelVersion(SupportedKernelMajorVersion, SupportedKernelMinorVersion);
-8
View File
@@ -1155,14 +1155,6 @@ void Module::Interface::StoreSaveDataThumbnailSystem(HLERequestContext& ctx) {
StoreSaveDataThumbnail(ctx, uuid, tid); StoreSaveDataThumbnail(ctx, uuid, tid);
} }
void Module::Interface::GetPinCodeLength(HLERequestContext& ctx) {
LOG_WARNING(Service_ACC, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.Push<u32>(0);
}
void Module::Interface::StoreSaveDataThumbnail(HLERequestContext& ctx, const Common::UUID& uuid, void Module::Interface::StoreSaveDataThumbnail(HLERequestContext& ctx, const Common::UUID& uuid,
const u64 tid) { const u64 tid) {
IPC::ResponseBuilder rb{ctx, 2}; IPC::ResponseBuilder rb{ctx, 2};
+1 -2
View File
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
@@ -48,7 +48,6 @@ public:
void StoreSaveDataThumbnailApplication(HLERequestContext& ctx); void StoreSaveDataThumbnailApplication(HLERequestContext& ctx);
void GetBaasAccountManagerForSystemService(HLERequestContext& ctx); void GetBaasAccountManagerForSystemService(HLERequestContext& ctx);
void StoreSaveDataThumbnailSystem(HLERequestContext& ctx); void StoreSaveDataThumbnailSystem(HLERequestContext& ctx);
void GetPinCodeLength(HLERequestContext& ctx);
private: private:
Result InitializeApplicationInfoBase(); Result InitializeApplicationInfoBase();
+2 -2
View File
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
@@ -60,7 +60,7 @@ ACC_SU::ACC_SU(std::shared_ptr<Module> module_, std::shared_ptr<ProfileManager>
{291, nullptr, "ProxyProcedureForFloatingRegistrationWithNintendoAccount"}, {291, nullptr, "ProxyProcedureForFloatingRegistrationWithNintendoAccount"},
{299, nullptr, "SuspendBackgroundDaemon"}, {299, nullptr, "SuspendBackgroundDaemon"},
{400, nullptr, "SetPinCode"}, // 18.0.0+ {400, nullptr, "SetPinCode"}, // 18.0.0+
{401, &ACC_SU::GetPinCodeLength, "GetPinCodeLength"}, // 18.0.0+ {401, nullptr, "GetPinCodeLength"}, // 18.0.0+
{402, nullptr, "GetPinCode"}, // 18.0.0+ {402, nullptr, "GetPinCode"}, // 18.0.0+
{410, nullptr, "GetPinCodeErrorCount"}, // 18.0.0+ {410, nullptr, "GetPinCodeErrorCount"}, // 18.0.0+
{411, nullptr, "ResetPinCodeErrorCount"}, // 18.0.0+ {411, nullptr, "ResetPinCodeErrorCount"}, // 18.0.0+
+1 -4
View File
@@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
@@ -43,7 +40,7 @@ ACC_U1::ACC_U1(std::shared_ptr<Module> module_, std::shared_ptr<ProfileManager>
{152, nullptr, "LoadSignedDeviceIdentifierCacheForNintendoAccount"}, {152, nullptr, "LoadSignedDeviceIdentifierCacheForNintendoAccount"},
{190, nullptr, "GetUserLastOpenedApplication"}, {190, nullptr, "GetUserLastOpenedApplication"},
{191, nullptr, "ActivateOpenContextHolder"}, {191, nullptr, "ActivateOpenContextHolder"},
{401, &ACC_U1::GetPinCodeLength, "GetPinCodeLength"}, // 18.0.0+ {401, nullptr, "GetPinCodeLength"}, // 18.0.0+
{402, nullptr, "GetPinCode"}, // 18.0.0+ {402, nullptr, "GetPinCode"}, // 18.0.0+
{997, nullptr, "DebugInvalidateTokenCacheForUser"}, {997, nullptr, "DebugInvalidateTokenCacheForUser"},
{998, nullptr, "DebugSetUserStateClose"}, {998, nullptr, "DebugSetUserStateClose"},
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
@@ -92,7 +92,7 @@ Result IApplicationCreator::CreateSystemApplication(
std::unique_ptr<Loader::AppLoader> loader; std::unique_ptr<Loader::AppLoader> loader;
auto process = auto process =
CreateProcess(system, application_id, 1, 22); CreateProcess(system, application_id, 1, 21);
R_UNLESS(process != nullptr, ResultUnknown); R_UNLESS(process != nullptr, ResultUnknown);
const auto applet = std::make_shared<Applet>(system, std::move(process), true); const auto applet = std::make_shared<Applet>(system, std::move(process), true);
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
@@ -75,8 +75,7 @@ ICommonStateGetter::ICommonStateGetter(Core::System& system_, std::shared_ptr<Ap
{502, nullptr, "IsSleepEnabled"}, {502, nullptr, "IsSleepEnabled"},
{503, nullptr, "IsDisablingSleepSuppressed"}, {503, nullptr, "IsDisablingSleepSuppressed"},
{600, nullptr, "Unknown600"}, //20.0.0+ {600, nullptr, "Unknown600"}, //20.0.0+
{610, D<&ICommonStateGetter::Unknown610>, "Unknown610"}, //21.0.0+ {610, nullptr, "Unknown610"}, //21.0.0+
{611, D<&ICommonStateGetter::Unknown611>, "Unknown611"}, //22.0.0+
{900, D<&ICommonStateGetter::SetRequestExitToLibraryAppletAtExecuteNextProgramEnabled>, "SetRequestExitToLibraryAppletAtExecuteNextProgramEnabled"}, //11.0.0+ {900, D<&ICommonStateGetter::SetRequestExitToLibraryAppletAtExecuteNextProgramEnabled>, "SetRequestExitToLibraryAppletAtExecuteNextProgramEnabled"}, //11.0.0+
{910, nullptr, "GetLaunchRequiredTick"}, //17.0.0+ {910, nullptr, "GetLaunchRequiredTick"}, //17.0.0+
{1000, nullptr, "BeginVrMode3d"}, //19.0.0+ {1000, nullptr, "BeginVrMode3d"}, //19.0.0+
@@ -363,14 +362,4 @@ Result ICommonStateGetter::SetHandlingHomeButtonShortPressedEnabled(bool enabled
R_SUCCEED(); R_SUCCEED();
} }
Result ICommonStateGetter::Unknown610() {
LOG_WARNING(Service_AM, "(STUBBED) called");
R_SUCCEED();
}
Result ICommonStateGetter::Unknown611() {
LOG_WARNING(Service_AM, "(STUBBED) called");
R_SUCCEED();
}
} // namespace Service::AM } // namespace Service::AM
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
@@ -63,8 +63,6 @@ private:
Result SetRequestExitToLibraryAppletAtExecuteNextProgramEnabled(); Result SetRequestExitToLibraryAppletAtExecuteNextProgramEnabled();
Result PushToGeneralChannel(SharedPointer<IStorage> storage); // cmd 20 Result PushToGeneralChannel(SharedPointer<IStorage> storage); // cmd 20
Result SetHandlingHomeButtonShortPressedEnabled(bool enabled); Result SetHandlingHomeButtonShortPressedEnabled(bool enabled);
Result Unknown610();
Result Unknown611();
void SetCpuBoostMode(HLERequestContext& ctx); void SetCpuBoostMode(HLERequestContext& ctx);
@@ -78,7 +78,6 @@ ILibraryAppletAccessor::ILibraryAppletAccessor(Core::System& system_,
{120, nullptr, "GetLibraryAppletInfo"}, {120, nullptr, "GetLibraryAppletInfo"},
{150, nullptr, "RequestForAppletToGetForeground"}, {150, nullptr, "RequestForAppletToGetForeground"},
{160, D<&ILibraryAppletAccessor::GetIndirectLayerConsumerHandle>, "GetIndirectLayerConsumerHandle"}, //2.0.0+ {160, D<&ILibraryAppletAccessor::GetIndirectLayerConsumerHandle>, "GetIndirectLayerConsumerHandle"}, //2.0.0+
{170, D<&ILibraryAppletAccessor::Unknown170>, "Unknown170"}, //22.0.0+
}; };
// clang-format on // clang-format on
@@ -218,12 +217,6 @@ Result ILibraryAppletAccessor::GetIndirectLayerConsumerHandle(Out<u64> out_handl
R_SUCCEED(); R_SUCCEED();
} }
Result ILibraryAppletAccessor::Unknown170(OutCopyHandle<Kernel::KReadableEvent> out_event) {
LOG_WARNING(Service_AM, "(STUBBED) called");
*out_event = m_applet->unknown_event.GetHandle();
R_SUCCEED();
}
void ILibraryAppletAccessor::FrontendExecute() { void ILibraryAppletAccessor::FrontendExecute() {
if (m_applet->frontend) { if (m_applet->frontend) {
m_applet->frontend->Initialize(); m_applet->frontend->Initialize();
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
@@ -37,7 +37,6 @@ private:
Result GetPopOutDataEvent(OutCopyHandle<Kernel::KReadableEvent> out_event); Result GetPopOutDataEvent(OutCopyHandle<Kernel::KReadableEvent> out_event);
Result GetPopInteractiveOutDataEvent(OutCopyHandle<Kernel::KReadableEvent> out_event); Result GetPopInteractiveOutDataEvent(OutCopyHandle<Kernel::KReadableEvent> out_event);
Result GetIndirectLayerConsumerHandle(Out<u64> out_handle); Result GetIndirectLayerConsumerHandle(Out<u64> out_handle);
Result Unknown170(OutCopyHandle<Kernel::KReadableEvent> out_event);
void FrontendExecute(); void FrontendExecute();
void FrontendExecuteInteractive(); void FrontendExecuteInteractive();
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
@@ -118,10 +118,9 @@ std::shared_ptr<ILibraryAppletAccessor> CreateGuestApplet(Core::System& system,
Firmware1900 = 19, Firmware1900 = 19,
Firmware2000 = 20, Firmware2000 = 20,
Firmware2100 = 21, Firmware2100 = 21,
Firmware2200 = 22,
}; };
auto process = CreateProcess(system, program_id, Firmware1400, Firmware2200); auto process = CreateProcess(system, program_id, Firmware1400, Firmware2100);
if (!process) { if (!process) {
// Couldn't initialize the guest process // Couldn't initialize the guest process
return {}; return {};
+128 -31
View File
@@ -1,8 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
#include "common/net/net.h"
#include "common/scm_rev.h"
#include "core/hle/service/bcat/news/builtin_news.h" #include "core/hle/service/bcat/news/builtin_news.h"
#include "core/hle/service/bcat/news/msgpack.h" #include "core/hle/service/bcat/news/msgpack.h"
#include "core/hle/service/bcat/news/news_storage.h" #include "core/hle/service/bcat/news/news_storage.h"
@@ -24,8 +22,10 @@
#include <filesystem> #include <filesystem>
#include <fstream> #include <fstream>
#include <future> #include <future>
#include <iomanip>
#include <mutex> #include <mutex>
#include <optional> #include <optional>
#include <sstream>
#include <thread> #include <thread>
#ifdef YUZU_BUNDLED_OPENSSL #ifdef YUZU_BUNDLED_OPENSSL
@@ -35,6 +35,9 @@
namespace Service::News { namespace Service::News {
namespace { namespace {
// TODO(crueter): COMPILE DEFINITION
constexpr const char* GitHubAPI_EdenReleases = "/api/v1/repos/eden-emu/eden/releases";
// Cached logo data // Cached logo data
std::vector<u8> default_logo_small; std::vector<u8> default_logo_small;
std::vector<u8> default_logo_large; std::vector<u8> default_logo_large;
@@ -63,6 +66,24 @@ u32 HashToNewsId(std::string_view key) {
return static_cast<u32>(std::hash<std::string_view>{}(key) & 0x7FFFFFFF); return static_cast<u32>(std::hash<std::string_view>{}(key) & 0x7FFFFFFF);
} }
u64 ParseIsoTimestamp(const std::string& iso) {
if (iso.empty()) return 0;
std::string buf = iso;
if (buf.back() == 'Z') buf.pop_back();
std::tm tm{};
std::istringstream ss(buf);
ss >> std::get_time(&tm, "%Y-%m-%dT%H:%M:%S");
if (ss.fail()) return 0;
#ifdef _WIN32
return static_cast<u64>(_mkgmtime(&tm));
#else
return static_cast<u64>(timegm(&tm));
#endif
}
std::vector<u8> TryLoadFromDisk(const std::filesystem::path& path) { std::vector<u8> TryLoadFromDisk(const std::filesystem::path& path) {
if (!std::filesystem::exists(path)) return {}; if (!std::filesystem::exists(path)) return {};
@@ -79,9 +100,8 @@ std::vector<u8> TryLoadFromDisk(const std::filesystem::path& path) {
return data; return data;
} }
// TODO(crueter): Migrate to use Common::Net
std::vector<u8> DownloadImage(const std::string& url_path, const std::filesystem::path& cache_path) { std::vector<u8> DownloadImage(const std::string& url_path, const std::filesystem::path& cache_path) {
LOG_DEBUG(Service_BCAT, "Downloading image: https://eden-emu.dev{}", url_path); LOG_INFO(Service_BCAT, "Downloading image: https://eden-emu.dev{}", url_path);
try { try {
httplib::Client cli("https://eden-emu.dev"); httplib::Client cli("https://eden-emu.dev");
cli.set_follow_location(true); cli.set_follow_location(true);
@@ -206,6 +226,67 @@ void WriteCachedJson(std::string_view json) {
(void)Common::FS::WriteStringToFile(path, Common::FS::FileType::TextFile, json); (void)Common::FS::WriteStringToFile(path, Common::FS::FileType::TextFile, json);
} }
std::optional<std::string> DownloadReleasesJson() {
try {
#ifdef YUZU_BUNDLED_OPENSSL
const auto url = "https://git.eden-emu.dev";
#else
const auto url = "git.eden-emu.dev";
#endif
// TODO(crueter): This is duplicated between frontend and here.
constexpr auto path = GitHubAPI_EdenReleases;
constexpr std::size_t timeout_seconds = 15;
std::unique_ptr<httplib::Client> client = std::make_unique<httplib::Client>(url);
client->set_connection_timeout(timeout_seconds);
client->set_read_timeout(timeout_seconds);
client->set_write_timeout(timeout_seconds);
#ifdef YUZU_BUNDLED_OPENSSL
client->load_ca_cert_store(kCert, sizeof(kCert));
#endif
if (client == nullptr) {
LOG_ERROR(Service_BCAT, "Invalid URL {}{}", url, path);
return {};
}
httplib::Request request{
.method = "GET",
.path = path,
};
client->set_follow_location(true);
httplib::Result result = client->send(request);
if (!result) {
LOG_ERROR(Service_BCAT, "GET to {}{} returned null", url, path);
return {};
} else if (result->status < 400) {
return result->body;
}
if (result->status >= 400) {
LOG_ERROR(Service_BCAT,
"GET to {}{} returned error status code: {}",
url,
path,
result->status);
return {};
}
if (!result->headers.contains("content-type")) {
LOG_ERROR(Service_BCAT, "GET to {}{} returned no content", url, path);
return {};
}
} catch (...) {
LOG_WARNING(Service_BCAT, " failed to download releases");
}
return std::nullopt;
}
// idk but News App does not render Markdown or HTML, so remove some formatting. // idk but News App does not render Markdown or HTML, so remove some formatting.
std::string SanitizeMarkdown(std::string_view markdown) { std::string SanitizeMarkdown(std::string_view markdown) {
std::string result; std::string result;
@@ -261,7 +342,9 @@ std::string SanitizeMarkdown(std::string_view markdown) {
return text; return text;
} }
std::string FormatBody(std::string body, const std::string_view &title) { std::string FormatBody(const nlohmann::json& release, std::string_view title) {
std::string body = release.value("body", std::string{});
if (body.empty()) { if (body.empty()) {
return std::string(title); return std::string(title);
} }
@@ -292,32 +375,52 @@ std::string FormatBody(std::string body, const std::string_view &title) {
return body; return body;
} }
void ImportReleases(const std::vector<Common::Net::Release> &releases) { void ImportReleases(std::string_view json_text) {
nlohmann::json root;
try {
root = nlohmann::json::parse(json_text);
} catch (...) {
LOG_WARNING(Service_BCAT, "failed to parse JSON");
return;
}
if (!root.is_array()) return;
std::vector<u32> news_ids; std::vector<u32> news_ids;
for (const auto& rel : releases) { for (const auto& rel : root) {
const u32 news_id = u32(rel.id & 0x7FFFFFFF); if (!rel.is_object()) continue;
std::string title = rel.value("name", rel.value("tag_name", std::string{}));
if (title.empty()) continue;
const u64 release_id = rel.value("id", 0);
const u32 news_id = release_id ? static_cast<u32>(release_id & 0x7FFFFFFF) : HashToNewsId(title);
news_ids.push_back(news_id); news_ids.push_back(news_id);
} }
PreloadNewsImages(news_ids); PreloadNewsImages(news_ids);
for (const auto& rel : releases) { for (const auto& rel : root) {
const std::string title = rel.title; if (!rel.is_object()) continue;
const std::string body = rel.body;
const std::string html_url = rel.html_url;
const u32 news_id = u32(rel.id & 0x7FFFFFFF); std::string title = rel.value("name", rel.value("tag_name", std::string{}));
const u64 published = rel.published; if (title.empty()) continue;
const u64 release_id = rel.value("id", 0);
const u32 news_id = release_id ? static_cast<u32>(release_id & 0x7FFFFFFF) : HashToNewsId(title);
const u64 published = ParseIsoTimestamp(rel.value("published_at", std::string{}));
const u64 pickup_limit = published + 600000000; const u64 pickup_limit = published + 600000000;
const u32 priority = rel.prerelease ? 1500 : 2500; const u32 priority = rel.value("prerelease", false) ? 1500 : 2500;
std::string author = "Eden"; std::string author = "eden";
if (rel.contains("author") && rel["author"].is_object()) {
author = rel["author"].value("login", "eden");
}
auto payload = BuildMsgpack(title, FormatBody(body, title), title, published, auto payload = BuildMsgpack(title, FormatBody(rel, title), title, published,
pickup_limit, priority, {"en"}, author, {}, pickup_limit, priority, {"en"}, author, {},
html_url, news_id); rel.value("html_url", std::string{}), news_id);
const std::string news_id_str = fmt::format("LA{:020}", rel.id); const std::string news_id_str = fmt::format("LA{:020}", news_id);
GithubNewsMeta meta{ GithubNewsMeta meta{
.news_id = news_id_str, .news_id = news_id_str,
@@ -462,21 +565,15 @@ void EnsureBuiltinNewsLoaded() {
LoadDefaultLogos(); LoadDefaultLogos();
if (const auto cached = ReadCachedJson()) { if (const auto cached = ReadCachedJson()) {
const std::string_view body = cached.value(); ImportReleases(*cached);
const auto releases = Common::Net::Release::ListFromJson(body, Common::g_build_auto_update_stable_api, Common::g_build_auto_update_stable_repo); LOG_DEBUG(Service_BCAT, "news: {} entries loaded from cache", NewsStorage::Instance().ListAll().size());
ImportReleases(releases);
LOG_INFO(Service_BCAT, "news: {} entries loaded from cache", NewsStorage::Instance().ListAll().size());
} }
std::thread([] { std::thread([] {
if (const auto fresh = Common::Net::GetReleasesBody()) { if (const auto fresh = DownloadReleasesJson()) {
const std::string_view body = fresh.value(); WriteCachedJson(*fresh);
WriteCachedJson(body); ImportReleases(*fresh);
const auto releases = Common::Net::Release::ListFromJson(body, Common::g_build_auto_update_stable_api, Common::g_build_auto_update_stable_repo); LOG_DEBUG(Service_BCAT, "news: {} entries updated from Forgejo", NewsStorage::Instance().ListAll().size());
ImportReleases(releases);
LOG_INFO(Service_BCAT, "news: {} entries updated from Forgejo", NewsStorage::Instance().ListAll().size());
} }
}).detach(); }).detach();
}); });
+7 -3
View File
@@ -89,7 +89,7 @@ void NvMap::UnmapHandle(Handle& handle_description) {
// Free and unmap the handle from Host1x GMMU // Free and unmap the handle from Host1x GMMU
if (handle_description.pin_virt_address) { if (handle_description.pin_virt_address) {
host1x.gmmu_manager.Unmap(static_cast<GPUVAddr>(handle_description.pin_virt_address), host1x.GMMU().Unmap(static_cast<GPUVAddr>(handle_description.pin_virt_address),
handle_description.aligned_size); handle_description.aligned_size);
host1x.Allocator().Free(handle_description.pin_virt_address, host1x.Allocator().Free(handle_description.pin_virt_address,
static_cast<u32>(handle_description.aligned_size)); static_cast<u32>(handle_description.aligned_size));
@@ -169,8 +169,12 @@ DAddr NvMap::PinHandle(NvMap::Handle::Id handle, bool low_area_pin) {
std::scoped_lock lock(handle_description->mutex); std::scoped_lock lock(handle_description->mutex);
const auto map_low_area = [&] { const auto map_low_area = [&] {
if (handle_description->pin_virt_address == 0) { if (handle_description->pin_virt_address == 0) {
u32 address = host1x.Allocator().Allocate(u32(handle_description->aligned_size)); auto& gmmu_allocator = host1x.Allocator();
host1x.gmmu_manager.Map(GPUVAddr(address), handle_description->d_address, handle_description->aligned_size); auto& gmmu = host1x.GMMU();
u32 address =
gmmu_allocator.Allocate(static_cast<u32>(handle_description->aligned_size));
gmmu.Map(static_cast<GPUVAddr>(address), handle_description->d_address,
handle_description->aligned_size);
handle_description->pin_virt_address = address; handle_description->pin_virt_address = address;
} }
}; };
@@ -10,7 +10,6 @@
#include "common/alignment.h" #include "common/alignment.h"
#include "common/assert.h" #include "common/assert.h"
#include "common/common_types.h"
#include "common/logging.h" #include "common/logging.h"
#include "core/core.h" #include "core/core.h"
#include "core/hle/service/nvdrv/core/container.h" #include "core/hle/service/nvdrv/core/container.h"
@@ -131,12 +130,12 @@ NvResult nvhost_as_gpu::AllocAsEx(IoctlAllocAsEx& params) {
const auto start_pages{static_cast<u32>(vm.va_range_start >> VM::PAGE_SIZE_BITS)}; const auto start_pages{static_cast<u32>(vm.va_range_start >> VM::PAGE_SIZE_BITS)};
const auto end_pages{static_cast<u32>(vm.va_range_split >> VM::PAGE_SIZE_BITS)}; const auto end_pages{static_cast<u32>(vm.va_range_split >> VM::PAGE_SIZE_BITS)};
vm.small_page_allocator.emplace(start_pages, end_pages); vm.small_page_allocator = std::make_shared<VM::Allocator>(start_pages, end_pages);
const auto start_big_pages{static_cast<u32>(vm.va_range_split >> vm.big_page_size_bits)}; const auto start_big_pages{static_cast<u32>(vm.va_range_split >> vm.big_page_size_bits)};
const auto end_big_pages{ const auto end_big_pages{
static_cast<u32>((vm.va_range_end - vm.va_range_split) >> vm.big_page_size_bits)}; static_cast<u32>((vm.va_range_end - vm.va_range_split) >> vm.big_page_size_bits)};
vm.big_page_allocator.emplace(start_big_pages, end_big_pages); vm.big_page_allocator = std::make_unique<VM::Allocator>(start_big_pages, end_big_pages);
gmmu = std::make_shared<Tegra::MemoryManager>(system, max_big_page_bits, vm.va_range_split, gmmu = std::make_shared<Tegra::MemoryManager>(system, max_big_page_bits, vm.va_range_split,
vm.big_page_size_bits, VM::PAGE_SIZE_BITS); vm.big_page_size_bits, VM::PAGE_SIZE_BITS);
@@ -189,8 +188,8 @@ NvResult nvhost_as_gpu::AllocateSpace(IoctlAllocSpace& params) {
} }
allocation_map[params.offset] = { allocation_map[params.offset] = {
.mappings{},
.size = size, .size = size,
.mappings{},
.page_size = params.page_size, .page_size = params.page_size,
.sparse = (params.flags & MappingFlags::Sparse) != MappingFlags::None, .sparse = (params.flags & MappingFlags::Sparse) != MappingFlags::None,
.big_pages = params.page_size != VM::YUZU_PAGESIZE, .big_pages = params.page_size != VM::YUZU_PAGESIZE,
@@ -199,57 +198,72 @@ NvResult nvhost_as_gpu::AllocateSpace(IoctlAllocSpace& params) {
return NvResult::Success; return NvResult::Success;
} }
bool nvhost_as_gpu::FreeMappingLocked(u64 offset) noexcept { void nvhost_as_gpu::FreeMappingLocked(u64 offset) {
if (auto const it = mapping_map.find(offset); it != mapping_map.end()) { auto mapping{mapping_map.at(offset)};
auto const mapping = it->second;
if (!mapping.fixed) { if (!mapping->fixed) {
auto& allocator{mapping.big_page ? *vm.big_page_allocator : *vm.small_page_allocator}; auto& allocator{mapping->big_page ? *vm.big_page_allocator : *vm.small_page_allocator};
u32 page_size_bits{mapping.big_page ? vm.big_page_size_bits : VM::PAGE_SIZE_BITS}; u32 page_size_bits{mapping->big_page ? vm.big_page_size_bits : VM::PAGE_SIZE_BITS};
u32 page_size{mapping.big_page ? vm.big_page_size : VM::YUZU_PAGESIZE}; u32 page_size{mapping->big_page ? vm.big_page_size : VM::YUZU_PAGESIZE};
u64 aligned_size{Common::AlignUp(mapping.size, page_size)}; u64 aligned_size{Common::AlignUp(mapping->size, page_size)};
allocator.Free(u32(mapping.offset >> page_size_bits), u32(aligned_size >> page_size_bits));
} allocator.Free(static_cast<u32>(mapping->offset >> page_size_bits),
nvmap.UnpinHandle(mapping.handle); static_cast<u32>(aligned_size >> page_size_bits));
// Sparse mappings shouldn't be fully unmapped, just returned to their sparse state
// Only FreeSpace can unmap them fully
if (mapping.sparse_alloc) {
gmmu->MapSparse(offset, mapping.size, mapping.big_page);
} else {
gmmu->Unmap(offset, mapping.size);
}
mapping_map.erase(offset);
return true;
} }
return false;
nvmap.UnpinHandle(mapping->handle);
// Sparse mappings shouldn't be fully unmapped, just returned to their sparse state
// Only FreeSpace can unmap them fully
if (mapping->sparse_alloc) {
gmmu->MapSparse(offset, mapping->size, mapping->big_page);
} else {
gmmu->Unmap(offset, mapping->size);
}
mapping_map.erase(offset);
} }
NvResult nvhost_as_gpu::FreeSpace(IoctlFreeSpace& params) { NvResult nvhost_as_gpu::FreeSpace(IoctlFreeSpace& params) {
LOG_DEBUG(Service_NVDRV, "called, offset={:X}, pages={:X}, page_size={:X}", params.offset, params.pages, params.page_size); LOG_DEBUG(Service_NVDRV, "called, offset={:X}, pages={:X}, page_size={:X}", params.offset,
params.pages, params.page_size);
std::scoped_lock lock(mutex); std::scoped_lock lock(mutex);
if (!vm.initialised) { if (!vm.initialised) {
return NvResult::BadValue; return NvResult::BadValue;
} }
if (auto const it = allocation_map.find(params.offset); it != allocation_map.end()) {
auto const allocation = it->second;
if (allocation.page_size != params.page_size || allocation.size != (u64(params.pages) * params.page_size))
return NvResult::BadValue;
for (const auto mapping_offset : allocation.mappings) try {
if (!FreeMappingLocked(mapping_offset)) auto allocation{allocation_map[params.offset]};
return NvResult::BadValue;
if (allocation.page_size != params.page_size ||
allocation.size != (static_cast<u64>(params.pages) * params.page_size)) {
return NvResult::BadValue;
}
for (const auto& mapping : allocation.mappings) {
FreeMappingLocked(mapping->offset);
}
// Unset sparse flag if required // Unset sparse flag if required
if (allocation.sparse) if (allocation.sparse) {
gmmu->Unmap(params.offset, allocation.size); gmmu->Unmap(params.offset, allocation.size);
}
auto& allocator{params.page_size == VM::YUZU_PAGESIZE ? *vm.small_page_allocator : *vm.big_page_allocator}; auto& allocator{params.page_size == VM::YUZU_PAGESIZE ? *vm.small_page_allocator
u32 page_size_bits{params.page_size == VM::YUZU_PAGESIZE ? VM::PAGE_SIZE_BITS : vm.big_page_size_bits}; : *vm.big_page_allocator};
u32 page_size_bits{params.page_size == VM::YUZU_PAGESIZE ? VM::PAGE_SIZE_BITS
: vm.big_page_size_bits};
allocator.Free(u32(params.offset >> page_size_bits), u32(allocation.size >> page_size_bits)); allocator.Free(static_cast<u32>(params.offset >> page_size_bits),
static_cast<u32>(allocation.size >> page_size_bits));
allocation_map.erase(params.offset); allocation_map.erase(params.offset);
return NvResult::Success; } catch (const std::out_of_range&) {
return NvResult::BadValue;
} }
return NvResult::BadValue;
return NvResult::Success;
} }
NvResult nvhost_as_gpu::Remap(std::span<IoctlRemapEntry> entries) { NvResult nvhost_as_gpu::Remap(std::span<IoctlRemapEntry> entries) {
@@ -313,18 +327,26 @@ NvResult nvhost_as_gpu::MapBufferEx(IoctlMapBufferEx& params) {
// Remaps a subregion of an existing mapping to a different PA // Remaps a subregion of an existing mapping to a different PA
if ((params.flags & MappingFlags::Remap) != MappingFlags::None) { if ((params.flags & MappingFlags::Remap) != MappingFlags::None) {
if (auto const it = mapping_map.find(params.offset); it != mapping_map.end()) { try {
auto const mapping = it->second; auto mapping{mapping_map.at(params.offset)};
if (mapping.size < params.mapping_size) {
LOG_WARNING(Service_NVDRV, "Cannot remap a partially mapped GPU address space region: {:#X}", params.offset); if (mapping->size < params.mapping_size) {
LOG_WARNING(Service_NVDRV,
"Cannot remap a partially mapped GPU address space region: {:#X}",
params.offset);
return NvResult::BadValue; return NvResult::BadValue;
} }
u64 gpu_address = u64(params.offset + params.buffer_offset);
VAddr device_address{mapping.ptr + params.buffer_offset}; u64 gpu_address{static_cast<u64>(params.offset + params.buffer_offset)};
gmmu->Map(gpu_address, device_address, params.mapping_size, Tegra::PTEKind(params.kind), mapping.big_page); VAddr device_address{mapping->ptr + params.buffer_offset};
gmmu->Map(gpu_address, device_address, params.mapping_size,
static_cast<Tegra::PTEKind>(params.kind), mapping->big_page);
return NvResult::Success; return NvResult::Success;
} else { } catch (const std::out_of_range&) {
LOG_WARNING(Service_NVDRV, "Cannot remap an unmapped GPU address space region: {:#X}", params.offset); LOG_WARNING(Service_NVDRV, "Cannot remap an unmapped GPU address space region: {:#X}",
params.offset);
return NvResult::BadValue; return NvResult::BadValue;
} }
} }
@@ -334,7 +356,8 @@ NvResult nvhost_as_gpu::MapBufferEx(IoctlMapBufferEx& params) {
return NvResult::BadValue; return NvResult::BadValue;
} }
DAddr device_address = DAddr(nvmap.PinHandle(params.handle, false) + params.buffer_offset); DAddr device_address{
static_cast<DAddr>(nvmap.PinHandle(params.handle, false) + params.buffer_offset)};
u64 size{params.mapping_size ? params.mapping_size : handle->orig_size}; u64 size{params.mapping_size ? params.mapping_size : handle->orig_size};
bool big_page{[&]() { bool big_page{[&]() {
@@ -358,22 +381,32 @@ NvResult nvhost_as_gpu::MapBufferEx(IoctlMapBufferEx& params) {
} }
const bool use_big_pages = alloc->second.big_pages && big_page; const bool use_big_pages = alloc->second.big_pages && big_page;
gmmu->Map(params.offset, device_address, size, static_cast<Tegra::PTEKind>(params.kind), use_big_pages); gmmu->Map(params.offset, device_address, size, static_cast<Tegra::PTEKind>(params.kind),
use_big_pages);
alloc->second.mappings.push_back(params.offset); auto mapping{std::make_shared<Mapping>(params.handle, device_address, params.offset, size,
mapping_map.insert_or_assign(params.offset, Mapping(params.handle, device_address, params.offset, size, true, use_big_pages, alloc->second.sparse)); true, use_big_pages, alloc->second.sparse)};
alloc->second.mappings.push_back(mapping);
mapping_map[params.offset] = mapping;
} else { } else {
auto& allocator{big_page ? *vm.big_page_allocator : *vm.small_page_allocator}; auto& allocator{big_page ? *vm.big_page_allocator : *vm.small_page_allocator};
u32 page_size{big_page ? vm.big_page_size : VM::YUZU_PAGESIZE}; u32 page_size{big_page ? vm.big_page_size : VM::YUZU_PAGESIZE};
u32 page_size_bits{big_page ? vm.big_page_size_bits : VM::PAGE_SIZE_BITS}; u32 page_size_bits{big_page ? vm.big_page_size_bits : VM::PAGE_SIZE_BITS};
params.offset = u64(allocator.Allocate(u32(Common::AlignUp(size, page_size) >> page_size_bits))) << page_size_bits; params.offset = static_cast<u64>(allocator.Allocate(
static_cast<u32>(Common::AlignUp(size, page_size) >> page_size_bits)))
<< page_size_bits;
if (!params.offset) { if (!params.offset) {
ASSERT_MSG(false, "Failed to allocate free space in the GPU AS!"); ASSERT_MSG(false, "Failed to allocate free space in the GPU AS!");
return NvResult::InsufficientMemory; return NvResult::InsufficientMemory;
} }
gmmu->Map(params.offset, device_address, Common::AlignUp(size, page_size), Tegra::PTEKind(params.kind), big_page);
mapping_map.insert_or_assign(params.offset, Mapping(params.handle, device_address, params.offset, size, false, big_page, false)); gmmu->Map(params.offset, device_address, Common::AlignUp(size, page_size),
static_cast<Tegra::PTEKind>(params.kind), big_page);
auto mapping{std::make_shared<Mapping>(params.handle, device_address, params.offset, size,
false, big_page, false)};
mapping_map[params.offset] = mapping;
} }
map_buffer_offsets.insert(params.offset); map_buffer_offsets.insert(params.offset);
@@ -382,30 +415,35 @@ NvResult nvhost_as_gpu::MapBufferEx(IoctlMapBufferEx& params) {
} }
NvResult nvhost_as_gpu::UnmapBuffer(IoctlUnmapBuffer& params) { NvResult nvhost_as_gpu::UnmapBuffer(IoctlUnmapBuffer& params) {
std::scoped_lock lock(mutex); if (map_buffer_offsets.find(params.offset) != map_buffer_offsets.end()) {
if (auto const offset_it = map_buffer_offsets.find(params.offset); offset_it != map_buffer_offsets.end()) {
LOG_DEBUG(Service_NVDRV, "called, offset={:#X}", params.offset); LOG_DEBUG(Service_NVDRV, "called, offset={:#X}", params.offset);
std::scoped_lock lock(mutex);
if (!vm.initialised) { if (!vm.initialised) {
return NvResult::BadValue; return NvResult::BadValue;
} }
auto const it = mapping_map.find(params.offset); auto mapping{mapping_map.at(params.offset)};
auto const mapping = it->second;
if (!mapping.fixed) { if (!mapping->fixed) {
auto& allocator{mapping.big_page ? *vm.big_page_allocator : *vm.small_page_allocator}; auto& allocator{mapping->big_page ? *vm.big_page_allocator : *vm.small_page_allocator};
u32 page_size_bits{mapping.big_page ? vm.big_page_size_bits : VM::PAGE_SIZE_BITS}; u32 page_size_bits{mapping->big_page ? vm.big_page_size_bits : VM::PAGE_SIZE_BITS};
allocator.Free(u32(mapping.offset >> page_size_bits), u32(mapping.size >> page_size_bits));
allocator.Free(static_cast<u32>(mapping->offset >> page_size_bits),
static_cast<u32>(mapping->size >> page_size_bits));
} }
// Sparse mappings shouldn't be fully unmapped, just returned to their sparse state // Sparse mappings shouldn't be fully unmapped, just returned to their sparse state
// Only FreeSpace can unmap them fully // Only FreeSpace can unmap them fully
if (mapping.sparse_alloc) { if (mapping->sparse_alloc) {
gmmu->MapSparse(params.offset, mapping.size, mapping.big_page); gmmu->MapSparse(params.offset, mapping->size, mapping->big_page);
} else { } else {
gmmu->Unmap(params.offset, mapping.size); gmmu->Unmap(params.offset, mapping->size);
} }
nvmap.UnpinHandle(mapping.handle); nvmap.UnpinHandle(mapping->handle);
mapping_map.erase(params.offset); mapping_map.erase(params.offset);
map_buffer_offsets.erase(params.offset); map_buffer_offsets.erase(params.offset);
} }
@@ -440,7 +478,8 @@ void nvhost_as_gpu::GetVARegionsImpl(IoctlGetVaRegions& params) {
} }
NvResult nvhost_as_gpu::GetVARegions1(IoctlGetVaRegions& params) { NvResult nvhost_as_gpu::GetVARegions1(IoctlGetVaRegions& params) {
LOG_DEBUG(Service_NVDRV, "called, buf_addr={:X}, buf_size={:X}", params.buf_addr, params.buf_size); LOG_DEBUG(Service_NVDRV, "called, buf_addr={:X}, buf_size={:X}", params.buf_addr,
params.buf_size);
std::scoped_lock lock(mutex); std::scoped_lock lock(mutex);
@@ -157,7 +157,7 @@ private:
NvResult GetVARegions1(IoctlGetVaRegions& params); NvResult GetVARegions1(IoctlGetVaRegions& params);
NvResult GetVARegions3(IoctlGetVaRegions& params, std::span<VaRegion> regions); NvResult GetVARegions3(IoctlGetVaRegions& params, std::span<VaRegion> regions);
[[nodiscard]] bool FreeMappingLocked(u64 offset) noexcept; void FreeMappingLocked(u64 offset);
Module& module; Module& module;
@@ -165,36 +165,35 @@ private:
NvCore::NvMap& nvmap; NvCore::NvMap& nvmap;
struct Mapping { struct Mapping {
NvCore::NvMap::Handle::Id handle;
DAddr ptr; DAddr ptr;
u64 offset; u64 offset;
u64 size; u64 size;
NvCore::NvMap::Handle::Id handle; bool fixed;
bool fixed : 1; bool big_page; // Only valid if fixed == false
bool big_page : 1; // Only valid if fixed == false bool sparse_alloc;
bool sparse_alloc : 1;
Mapping(NvCore::NvMap::Handle::Id handle_, DAddr ptr_, u64 offset_, u64 size_, bool fixed_, bool big_page_, bool sparse_alloc_) Mapping(NvCore::NvMap::Handle::Id handle_, DAddr ptr_, u64 offset_, u64 size_, bool fixed_,
: ptr(ptr_), offset(offset_), size(size_), handle(handle_) bool big_page_, bool sparse_alloc_)
, fixed(fixed_), big_page(big_page_), sparse_alloc(sparse_alloc_) : handle(handle_), ptr(ptr_), offset(offset_), size(size_), fixed(fixed_),
{} big_page(big_page_), sparse_alloc(sparse_alloc_) {}
}; };
struct Allocation { struct Allocation {
std::vector<u64> mappings;
u64 size; u64 size;
std::list<std::shared_ptr<Mapping>> mappings;
u32 page_size; u32 page_size;
bool sparse; bool sparse;
bool big_pages; bool big_pages;
}; };
//!< This maps the base addresses of mapped buffers to their total sizes and std::map<u64, std::shared_ptr<Mapping>>
//!< mapping type, this is needed as what was originally a single buffer may mapping_map; //!< This maps the base addresses of mapped buffers to their total sizes and
//!< have been split into multiple GPU side buffers with the remap flag. //!< mapping type, this is needed as what was originally a single buffer may
std::map<u64, Mapping> mapping_map; //!< have been split into multiple GPU side buffers with the remap flag.
//!< Holds allocations created by AllocSpace from std::map<u64, Allocation> allocation_map; //!< Holds allocations created by AllocSpace from
//!< which fixed buffers can be mapped into //!< which fixed buffers can be mapped into
std::map<u64, Allocation> allocation_map; std::mutex mutex; //!< Locks all AS operations
std::mutex mutex; //!< Locks all AS operations
struct VM { struct VM {
static constexpr u32 YUZU_PAGESIZE{0x1000}; static constexpr u32 YUZU_PAGESIZE{0x1000};
@@ -214,8 +213,9 @@ private:
using Allocator = Common::FlatAllocator<u32, 0, 32>; using Allocator = Common::FlatAllocator<u32, 0, 32>;
std::optional<Allocator> big_page_allocator; std::unique_ptr<Allocator> big_page_allocator;
std::optional<Allocator> small_page_allocator; //! Shared as this is also used by nvhost::GpuChannel std::shared_ptr<Allocator>
small_page_allocator; //! Shared as this is also used by nvhost::GpuChannel
bool initialised{}; bool initialised{};
} vm; } vm;
@@ -409,13 +409,6 @@ struct AccountNotificationSettings {
static_assert(sizeof(AccountNotificationSettings) == 0x18, static_assert(sizeof(AccountNotificationSettings) == 0x18,
"AccountNotificationSettings is an invalid size"); "AccountNotificationSettings is an invalid size");
/// This is nn::settings::system::AccountUserSettings (stubbed)
struct AccountUserSettings {
std::array<u8, 0x40> data;
};
static_assert(sizeof(AccountUserSettings) == 0x40,
"AccountUserSettings is an invalid size");
/// This is nn::settings::factory::BatteryLot /// This is nn::settings::factory::BatteryLot
struct BatteryLot { struct BatteryLot {
std::array<char, 0x18> lot_number; std::array<char, 0x18> lot_number;
@@ -340,9 +340,9 @@ ISystemSettingsServer::ISystemSettingsServer(Core::System& system_)
{306, nullptr, "GetPinCodeReregistrationGuideAccounts"}, //20.0.0+ {306, nullptr, "GetPinCodeReregistrationGuideAccounts"}, //20.0.0+
{307, nullptr, "SetPinCodeReregistrationGuideAccounts"}, //20.0.0+ {307, nullptr, "SetPinCodeReregistrationGuideAccounts"}, //20.0.0+
{315, C<&ISystemSettingsServer::GetHttpAuthConfigs>, "GetHttpAuthConfigs"}, //21.0.0+ {315, C<&ISystemSettingsServer::GetHttpAuthConfigs>, "GetHttpAuthConfigs"}, //21.0.0+
{319, C<&ISystemSettingsServer::GetAccountUserSettings>, "GetAccountUserSettings"}, //21.0.0+ {319, nullptr, "GetAccountUserSettings"}, //21.0.0+
{320, nullptr, "SetAccountUserSettings"}, //21.0.0+ {320, nullptr, "SetAccountUserSettings"}, //21.0.0+
{321, C<&ISystemSettingsServer::GetDefaultAccountUserSettings>, "GetDefaultAccountUserSettings"}, //21.0.0+ {321, nullptr, "GetDefaultAccountUserSettings"}, //21.0.0+
}; };
// clang-format on // clang-format on
@@ -1428,23 +1428,6 @@ Result ISystemSettingsServer::GetHttpAuthConfigs(Out<s32> out_count, OutBuffer<B
R_SUCCEED(); R_SUCCEED();
} }
Result ISystemSettingsServer::GetAccountUserSettings(
Out<u32> out_count,
OutLargeData<AccountUserSettings, BufferAttr_HipcMapAlias> out_settings) {
LOG_WARNING(Service_SET, "(STUBBED) called");
*out_count = 0;
*out_settings = {};
R_SUCCEED();
}
Result ISystemSettingsServer::GetDefaultAccountUserSettings(Out<AccountUserSettings> out_settings) {
LOG_WARNING(Service_SET, "(STUBBED) called");
*out_settings = {};
R_SUCCEED();
}
void ISystemSettingsServer::SetupSettings() { void ISystemSettingsServer::SetupSettings() {
auto system_dir = Common::FS::GetEdenPath(Common::FS::EdenPath::NANDDir) / "system/save/8000000000000050"; auto system_dir = Common::FS::GetEdenPath(Common::FS::EdenPath::NANDDir) / "system/save/8000000000000050";
if (!LoadSettingsFile(system_dir, []() { return DefaultSystemSettings(); })) { if (!LoadSettingsFile(system_dir, []() { return DefaultSystemSettings(); })) {
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
@@ -161,10 +161,6 @@ public:
Result GetPanelCrcMode(Out<s32> out_panel_crc_mode); Result GetPanelCrcMode(Out<s32> out_panel_crc_mode);
Result SetPanelCrcMode(s32 panel_crc_mode); Result SetPanelCrcMode(s32 panel_crc_mode);
Result GetHttpAuthConfigs(Out<s32> out_count, OutBuffer<BufferAttr_HipcMapAlias> out_configs); Result GetHttpAuthConfigs(Out<s32> out_count, OutBuffer<BufferAttr_HipcMapAlias> out_configs);
Result GetAccountUserSettings(
Out<u32> out_count,
OutLargeData<AccountUserSettings, BufferAttr_HipcMapAlias> out_settings);
Result GetDefaultAccountUserSettings(Out<AccountUserSettings> out_settings);
private: private:
bool LoadSettingsFile(std::filesystem::path& path, auto&& default_func); bool LoadSettingsFile(std::filesystem::path& path, auto&& default_func);
+9 -11
View File
@@ -67,24 +67,22 @@ public:
Result GetServicePort(Kernel::KClientPort** out_client_port, const std::string& name); Result GetServicePort(Kernel::KClientPort** out_client_port, const std::string& name);
template <std::derived_from<SessionRequestHandler> T> template <std::derived_from<SessionRequestHandler> T>
std::shared_ptr<T> GetService(const std::string& name, bool block = false) const { std::shared_ptr<T> GetService(const std::string& service_name, bool block = false) const {
std::unique_lock l{lock}; auto service = registered_services.find(service_name);
auto it = registered_services.find(name); if (service == registered_services.end() && !block) {
if (it == registered_services.end() && !block) { LOG_DEBUG(Service, "Can't find service: {}", service_name);
LOG_DEBUG(Service, "Can't find service: {}", name);
return nullptr; return nullptr;
} else if (block) { } else if (block) {
using namespace std::literals::chrono_literals; using namespace std::literals::chrono_literals;
while (it == registered_services.end()) { while (service == registered_services.end()) {
l.unlock();
Kernel::Svc::SleepThread( Kernel::Svc::SleepThread(
kernel.System(), kernel.System(),
std::chrono::duration_cast<std::chrono::nanoseconds>(100ms).count()); std::chrono::duration_cast<std::chrono::nanoseconds>(100ms).count());
l.lock(); service = registered_services.find(service_name);
it = registered_services.find(name);
} }
} }
return std::static_pointer_cast<T>(it->second());
return std::static_pointer_cast<T>(service->second());
} }
void InvokeControlRequest(HLERequestContext& context); void InvokeControlRequest(HLERequestContext& context);
@@ -98,7 +96,7 @@ private:
std::unique_ptr<Controller> controller_interface; std::unique_ptr<Controller> controller_interface;
/// Map of registered services, retrieved using GetServicePort. /// Map of registered services, retrieved using GetServicePort.
mutable std::mutex lock; std::mutex lock;
ankerl::unordered_dense::map<std::string, SessionRequestHandlerFactory> registered_services; ankerl::unordered_dense::map<std::string, SessionRequestHandlerFactory> registered_services;
ankerl::unordered_dense::map<std::string, Kernel::KClientPort*> service_ports; ankerl::unordered_dense::map<std::string, Kernel::KClientPort*> service_ports;
@@ -228,8 +228,9 @@ AppLoader_DeconstructedRomDirectory::LoadResult AppLoader_DeconstructedRomDirect
code_size += patch_ctx.GetTotalPatchSize(); code_size += patch_ctx.GetTotalPatchSize();
// TODO: this is bad form of ASLR, it sucks // TODO: this is bad form of ASLR, it sucks
std::uintptr_t aslr_offset = ((::Settings::values.rng_seed_enabled.GetValue() size_t aslr_offset = ((::Settings::values.rng_seed_enabled.GetValue()
? ::Settings::values.rng_seed.GetValue() : Common::Random::Random64(0)) << 12) & 0xfff000; ? ::Settings::values.rng_seed.GetValue()
: Common::Random::Random64(0)) * 0x734287f27) & 0xfff000;
// Setup the process code layout // Setup the process code layout
if (process.LoadFromMetadata(metadata, code_size, fastmem_base, aslr_offset, is_hbl).IsError()) { if (process.LoadFromMetadata(metadata, code_size, fastmem_base, aslr_offset, is_hbl).IsError()) {
+3 -2
View File
@@ -89,8 +89,9 @@ AppLoader::LoadResult AppLoader_KIP::Load(Kernel::KProcess& process,
codeset.DataSegment().size += kip->GetBSSSize(); codeset.DataSegment().size += kip->GetBSSSize();
// TODO: this is bad form of ASLR, it sucks // TODO: this is bad form of ASLR, it sucks
std::uintptr_t aslr_offset = ((::Settings::values.rng_seed_enabled.GetValue() size_t aslr_offset = ((::Settings::values.rng_seed_enabled.GetValue()
? ::Settings::values.rng_seed.GetValue() : Common::Random::Random64(0)) << 12) & 0xfff000; ? ::Settings::values.rng_seed.GetValue()
: Common::Random::Random64(0)) * 0x734287f27) & 0xfff000;
// Setup the process code layout // Setup the process code layout
if (process.LoadFromMetadata(FileSys::ProgramMetadata::GetDefault(), codeset.memory.size(), 0, aslr_offset, false).IsError()) { if (process.LoadFromMetadata(FileSys::ProgramMetadata::GetDefault(), codeset.memory.size(), 0, aslr_offset, false).IsError()) {
+3 -2
View File
@@ -242,8 +242,9 @@ static bool LoadNroImpl(Core::System& system, Kernel::KProcess& process,
}(); }();
// TODO: this is bad form of ASLR, it sucks // TODO: this is bad form of ASLR, it sucks
std::uintptr_t aslr_offset = ((::Settings::values.rng_seed_enabled.GetValue() size_t aslr_offset = ((::Settings::values.rng_seed_enabled.GetValue()
? ::Settings::values.rng_seed.GetValue() : Common::Random::Random64(0)) << 12) & 0xfff000; ? ::Settings::values.rng_seed.GetValue()
: Common::Random::Random64(0)) * 0x734287f27) & 0xfff000;
// Setup the process code layout // Setup the process code layout
if (process if (process
@@ -866,17 +866,10 @@ void EmitIR<IR::Opcode::VectorMaxS32>(oaknut::CodeGenerator& code, EmitContext&
template<> template<>
void EmitIR<IR::Opcode::VectorMaxS64>(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) { void EmitIR<IR::Opcode::VectorMaxS64>(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) {
#ifdef __APPLE__
EmitThreeOp(code, ctx, inst, [&](auto& Qresult, auto& Qa, auto& Qb) {
code.CMGT(Qresult->D2(), Qa->D2(), Qb->D2());
code.BSL(Qresult->B16(), Qa->B16(), Qb->B16());
});
#else
(void)code; (void)code;
(void)ctx; (void)ctx;
(void)inst; (void)inst;
UNREACHABLE(); UNREACHABLE();
#endif
} }
template<> template<>
@@ -896,17 +889,10 @@ void EmitIR<IR::Opcode::VectorMaxU32>(oaknut::CodeGenerator& code, EmitContext&
template<> template<>
void EmitIR<IR::Opcode::VectorMaxU64>(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) { void EmitIR<IR::Opcode::VectorMaxU64>(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) {
#ifdef __APPLE__
EmitThreeOp(code, ctx, inst, [&](auto& Qresult, auto& Qa, auto& Qb) {
code.CMHI(Qresult->D2(), Qa->D2(), Qb->D2());
code.BSL(Qresult->B16(), Qa->B16(), Qb->B16());
});
#else
(void)code; (void)code;
(void)ctx; (void)ctx;
(void)inst; (void)inst;
UNREACHABLE(); UNREACHABLE();
#endif
} }
template<> template<>
@@ -926,17 +912,10 @@ void EmitIR<IR::Opcode::VectorMinS32>(oaknut::CodeGenerator& code, EmitContext&
template<> template<>
void EmitIR<IR::Opcode::VectorMinS64>(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) { void EmitIR<IR::Opcode::VectorMinS64>(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) {
#ifdef __APPLE__
EmitThreeOp(code, ctx, inst, [&](auto& Qresult, auto& Qa, auto& Qb) {
code.CMGT(Qresult->D2(), Qb->D2(), Qa->D2());
code.BSL(Qresult->B16(), Qa->B16(), Qb->B16());
});
#else
(void)code; (void)code;
(void)ctx; (void)ctx;
(void)inst; (void)inst;
UNREACHABLE(); UNREACHABLE();
#endif
} }
template<> template<>
@@ -956,17 +935,10 @@ void EmitIR<IR::Opcode::VectorMinU32>(oaknut::CodeGenerator& code, EmitContext&
template<> template<>
void EmitIR<IR::Opcode::VectorMinU64>(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) { void EmitIR<IR::Opcode::VectorMinU64>(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) {
#ifdef __APPLE__
EmitThreeOp(code, ctx, inst, [&](auto& Qresult, auto& Qa, auto& Qb) {
code.CMHI(Qresult->D2(), Qb->D2(), Qa->D2());
code.BSL(Qresult->B16(), Qa->B16(), Qb->B16());
});
#else
(void)code; (void)code;
(void)ctx; (void)ctx;
(void)inst; (void)inst;
UNREACHABLE(); UNREACHABLE();
#endif
} }
template<> template<>
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
/* This file is part of the dynarmic project. /* This file is part of the dynarmic project.
@@ -39,7 +39,7 @@ bool TranslatorVisitor::BL(Imm<26> imm26) {
ir.PushRSB(ir.current_location->AdvancePC(4)); ir.PushRSB(ir.current_location->AdvancePC(4));
const u64 target = ir.PC() + offset; const u64 target = ir.PC() + offset;
ir.SetTerm(IR::Term::LinkBlockFast{ir.current_location->SetPC(target)}); ir.SetTerm(IR::Term::LinkBlock{ir.current_location->SetPC(target)});
return false; return false;
} }
File diff suppressed because one or more lines are too long
+130 -29
View File
@@ -5,45 +5,146 @@
// Licensed under GPLv2 or any later version // Licensed under GPLv2 or any later version
// Refer to the license.txt file included. // Refer to the license.txt file included.
#ifdef NIGHTLY_BUILD
#include <boost/algorithm/string/classification.hpp> #include <boost/algorithm/string/classification.hpp>
#include <boost/algorithm/string/split.hpp> #include <boost/algorithm/string/split.hpp>
#endif
#include <fmt/format.h> #include <fmt/format.h>
#include "common/net/net.h" #include "common/logging.h"
#include "common/scm_rev.h" #include "common/scm_rev.h"
#include "update_checker.h" #include "update_checker.h"
#include "common/logging.h" #include "common/httplib.h"
std::optional<Common::Net::Release> UpdateChecker::GetUpdate() { #ifdef YUZU_BUNDLED_OPENSSL
const auto latest = Common::Net::GetLatestRelease(); #include <openssl/cert.h>
if (!latest) return std::nullopt;
LOG_INFO(Frontend, "Received update {}", latest->title);
#ifdef NIGHTLY_BUILD
std::vector<std::string> result;
boost::split(result, latest->tag, boost::is_any_of("."));
if (result.size() != 2)
return std::nullopt;
const std::string tag = result[1];
boost::split(result, std::string{Common::g_build_version}, boost::is_any_of("-"));
if (result.empty())
return std::nullopt;
const std::string build = result[0];
#else
const std::string tag = latest->tag;
const std::string build = Common::g_build_version;
#endif #endif
if (tag != build) #include <nlohmann/json.hpp>
return latest; #include <optional>
#include <string>
std::optional<std::string> UpdateChecker::GetResponse(std::string url, std::string path)
{
try {
constexpr std::size_t timeout_seconds = 15;
std::unique_ptr<httplib::Client> client = std::make_unique<httplib::Client>(url);
client->set_connection_timeout(timeout_seconds);
client->set_read_timeout(timeout_seconds);
client->set_write_timeout(timeout_seconds);
#ifdef YUZU_BUNDLED_OPENSSL
client->load_ca_cert_store(kCert, sizeof(kCert));
#endif
if (client == nullptr) {
LOG_ERROR(Frontend, "Invalid URL {}{}", url, path);
return {};
}
httplib::Request request{
.method = "GET",
.path = path,
};
client->set_follow_location(true);
httplib::Result result = client->send(request);
if (!result) {
LOG_ERROR(Frontend, "GET to {}{} returned null", url, path);
return {};
}
const auto &response = result.value();
if (response.status >= 400) {
LOG_ERROR(Frontend,
"GET to {}{} returned error status code: {}",
url,
path,
response.status);
return {};
}
if (!response.headers.contains("content-type")) {
LOG_ERROR(Frontend, "GET to {}{} returned no content", url, path);
return {};
}
return response.body;
} catch (std::exception &e) {
LOG_ERROR(Frontend,
"GET to {}{} failed during update check: {}",
url,
path,
e.what());
return std::nullopt;
}
}
std::optional<UpdateChecker::Update> UpdateChecker::GetLatestRelease() {
#ifdef YUZU_BUNDLED_OPENSSL
const auto update_check_url = fmt::format("https://{}", Common::g_build_auto_update_api);
#else
const auto update_check_url = std::string{Common::g_build_auto_update_api};
#endif
auto update_check_path = std::string{Common::g_build_auto_update_api_path};
try {
const auto response = UpdateChecker::GetResponse(update_check_url, update_check_path);
if (!response)
return {};
const std::string latest_tag = nlohmann::json::parse(response.value()).at("tag_name");
const std::string latest_name = nlohmann::json::parse(response.value()).at("name");
return Update{latest_tag, latest_name};
} catch (nlohmann::detail::out_of_range&) {
LOG_ERROR(Frontend,
"Parsing JSON response from {}{} failed during update check: "
"nlohmann::detail::out_of_range",
update_check_url,
update_check_path);
return {};
} catch (nlohmann::detail::type_error&) {
LOG_ERROR(Frontend,
"Parsing JSON response from {}{} failed during update check: "
"nlohmann::detail::type_error",
update_check_url,
update_check_path);
return {};
}
}
std::optional<UpdateChecker::Update> UpdateChecker::GetUpdate() {
const std::optional<UpdateChecker::Update> latest_release_tag =
UpdateChecker::GetLatestRelease();
if (!latest_release_tag)
goto empty;
{
std::string tag, build;
if (Common::g_is_nightly_build) {
std::vector<std::string> result;
boost::split(result, latest_release_tag->tag, boost::is_any_of("."));
if (result.size() != 2)
goto empty;
tag = result[1];
boost::split(result, std::string{Common::g_build_version}, boost::is_any_of("-"));
if (result.empty())
goto empty;
build = result[0];
} else {
tag = latest_release_tag->tag;
build = Common::g_build_version;
}
if (tag != build)
return latest_release_tag.value();
}
empty:
return std::nullopt; return std::nullopt;
} }
+8 -2
View File
@@ -8,10 +8,16 @@
#pragma once #pragma once
#include <optional> #include <optional>
#include "common/net/net.h" #include <string>
namespace UpdateChecker { namespace UpdateChecker {
std::optional<Common::Net::Release> GetUpdate(); typedef struct {
std::string tag;
std::string name;
} Update;
std::optional<std::string> GetResponse(std::string url, std::string path);
std::optional<Update> GetLatestRelease();
std::optional<Update> GetUpdate();
} // namespace UpdateChecker } // namespace UpdateChecker
+9 -15
View File
@@ -157,14 +157,11 @@ void NPad::ControllerUpdate(Core::HID::ControllerTriggerType type, std::size_t c
if (!controller.device->IsConnected()) { if (!controller.device->IsConnected()) {
return; return;
} }
if (auto* shared_memory = controller.shared_memory; shared_memory) { auto* shared_memory = controller.shared_memory;
const auto& battery_level = controller.device->GetBattery(); const auto& battery_level = controller.device->GetBattery();
shared_memory->battery_level_dual = battery_level.dual.battery_level; shared_memory->battery_level_dual = battery_level.dual.battery_level;
shared_memory->battery_level_left = battery_level.left.battery_level; shared_memory->battery_level_left = battery_level.left.battery_level;
shared_memory->battery_level_right = battery_level.right.battery_level; shared_memory->battery_level_right = battery_level.right.battery_level;
} else {
LOG_WARNING(Service_HID, "shared_memory is null {}", controller_idx);
}
break; break;
} }
default: default:
@@ -183,10 +180,6 @@ void NPad::InitNewlyAddedController(u64 aruid, Core::HID::NpadIdType npad_id) {
const auto& body_colors = controller.device->GetColors(); const auto& body_colors = controller.device->GetColors();
const auto& battery_level = controller.device->GetBattery(); const auto& battery_level = controller.device->GetBattery();
auto* shared_memory = controller.shared_memory; auto* shared_memory = controller.shared_memory;
if (!shared_memory) {
LOG_WARNING(Service_HID, "shared_memory is null for npad_id={}", npad_id);
return;
}
if (controller_type == Core::HID::NpadStyleIndex::None) { if (controller_type == Core::HID::NpadStyleIndex::None) {
npad_resource.SignalStyleSetUpdateEvent(aruid, npad_id); npad_resource.SignalStyleSetUpdateEvent(aruid, npad_id);
return; return;
@@ -808,7 +801,7 @@ Result NPad::DisconnectNpad(u64 aruid, Core::HID::NpadIdType npad_id) {
auto* shared_memory = controller.shared_memory; auto* shared_memory = controller.shared_memory;
if (!shared_memory) { if (!shared_memory) {
LOG_WARNING(Service_HID, "shared_memory is null for npad_id={}", npad_id); LOG_WARNING(Service_HID, "DisconnectNpad: shared_memory is null for npad_id={}", npad_id);
return ResultSuccess; return ResultSuccess;
} }
// Don't reset shared_memory->assignment_mode this value is persistent // Don't reset shared_memory->assignment_mode this value is persistent
@@ -1202,10 +1195,11 @@ const Core::HID::SixAxisSensorProperties& NPad::GetSixaxisProperties(
AppletDetailedUiType NPad::GetAppletDetailedUiType(Core::HID::NpadIdType npad_id) { AppletDetailedUiType NPad::GetAppletDetailedUiType(Core::HID::NpadIdType npad_id) {
const auto aruid = applet_resource_holder.applet_resource->GetActiveAruid(); const auto aruid = applet_resource_holder.applet_resource->GetActiveAruid();
const auto* shared_memory = GetControllerFromNpadIdType(aruid, npad_id).shared_memory; const auto& shared_memory = GetControllerFromNpadIdType(aruid, npad_id).shared_memory;
return { return {
.ui_variant = 0, .ui_variant = 0,
.footer = shared_memory ? shared_memory->applet_footer_type : AppletFooterUiType::None, .footer = shared_memory->applet_footer_type,
}; };
} }
@@ -14,38 +14,6 @@
namespace Shader::Backend::SPIRV { namespace Shader::Backend::SPIRV {
namespace { namespace {
class DescriptorIndex {
public:
explicit DescriptorIndex(EmitContext& ctx, const IR::Value& index)
: id{index.IsImmediate() ? ctx.Const(index.U32()) : ctx.Def(index)},
is_non_uniform{ctx.profile.support_sampled_image_array_nonuniform_indexing &&
!index.IsImmediate()} {
if (!is_non_uniform) {
return;
}
if (ctx.profile.supported_spirv < 0x00010400) {
ctx.AddExtension("SPV_EXT_descriptor_indexing");
}
ctx.AddCapability(spv::Capability::ShaderNonUniform);
ctx.AddCapability(spv::Capability::SampledImageArrayNonUniformIndexing);
Decorate(ctx, id);
}
Id Value() const {
return id;
}
void Decorate(EmitContext& ctx, Id object) const {
if (is_non_uniform) {
ctx.Decorate(object, spv::Decoration::NonUniform);
}
}
private:
Id id;
bool is_non_uniform;
};
class ImageOperands { class ImageOperands {
public: public:
[[maybe_unused]] static constexpr bool ImageSampleOffsetAllowed = false; [[maybe_unused]] static constexpr bool ImageSampleOffsetAllowed = false;
@@ -221,12 +189,8 @@ private:
Id Texture(EmitContext& ctx, IR::TextureInstInfo info, [[maybe_unused]] const IR::Value& index) { Id Texture(EmitContext& ctx, IR::TextureInstInfo info, [[maybe_unused]] const IR::Value& index) {
const TextureDefinition& def{ctx.textures.at(info.descriptor_index)}; const TextureDefinition& def{ctx.textures.at(info.descriptor_index)};
if (def.count > 1) { if (def.count > 1) {
const DescriptorIndex idx{ctx, index}; const Id pointer{ctx.OpAccessChain(def.pointer_type, def.id, ctx.Def(index))};
const Id pointer{ctx.OpAccessChain(def.pointer_type, def.id, idx.Value())}; return ctx.OpLoad(def.sampled_type, pointer);
idx.Decorate(ctx, pointer);
const Id object{ctx.OpLoad(def.sampled_type, pointer)};
idx.Decorate(ctx, object);
return object;
} else { } else {
return ctx.OpLoad(def.sampled_type, def.id); return ctx.OpLoad(def.sampled_type, def.id);
} }
@@ -244,14 +208,9 @@ Id TextureImage(EmitContext& ctx, IR::TextureInstInfo info, const IR::Value& ind
} else { } else {
const TextureDefinition& def{ctx.textures.at(info.descriptor_index)}; const TextureDefinition& def{ctx.textures.at(info.descriptor_index)};
if (def.count > 1) { if (def.count > 1) {
const DescriptorIndex idx{ctx, index}; const Id idx{index.IsImmediate() ? ctx.Const(index.U32()) : ctx.Def(index)};
const Id ptr{ctx.OpAccessChain(def.pointer_type, def.id, idx.Value())}; const Id ptr{ctx.OpAccessChain(def.pointer_type, def.id, idx)};
idx.Decorate(ctx, ptr); return ctx.OpImage(def.image_type, ctx.OpLoad(def.sampled_type, ptr));
const Id object{ctx.OpLoad(def.sampled_type, ptr)};
idx.Decorate(ctx, object);
const Id image{ctx.OpImage(def.image_type, object)};
idx.Decorate(ctx, image);
return image;
} }
return ctx.OpImage(def.image_type, ctx.OpLoad(def.sampled_type, def.id)); return ctx.OpImage(def.image_type, ctx.OpLoad(def.sampled_type, def.id));
} }
@@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
@@ -20,9 +17,6 @@ struct HostTranslateInfo {
bool support_snorm_render_buffer{}; ///< True when the device supports SNORM render buffers bool support_snorm_render_buffer{}; ///< True when the device supports SNORM render buffers
bool support_viewport_index_layer{}; ///< True when the device supports gl_Layer in VS bool support_viewport_index_layer{}; ///< True when the device supports gl_Layer in VS
u32 min_ssbo_alignment{}; ///< Minimum alignment supported by the device for SSBOs u32 min_ssbo_alignment{}; ///< Minimum alignment supported by the device for SSBOs
u32 max_per_stage_descriptor_sampled_images{1024}; ///< maximum sampled descriptors per stage
u32 max_per_stage_resources{4096}; ///< maximum resources per stage
u32 max_descriptor_set_sampled_images{1024}; ///< maximum sampled descriptors per set
bool support_geometry_shader_passthrough{}; ///< True when the device supports geometry bool support_geometry_shader_passthrough{}; ///< True when the device supports geometry
///< passthrough shaders ///< passthrough shaders
bool support_conditional_barrier{}; ///< True when the device supports barriers in conditional bool support_conditional_barrier{}; ///< True when the device supports barriers in conditional
+12 -117
View File
@@ -32,71 +32,6 @@ using TextureInstVector = boost::container::small_vector<TextureInst, 24>;
constexpr u32 DESCRIPTOR_SIZE = 8; constexpr u32 DESCRIPTOR_SIZE = 8;
constexpr u32 DESCRIPTOR_SIZE_SHIFT = static_cast<u32>(std::countr_zero(DESCRIPTOR_SIZE)); constexpr u32 DESCRIPTOR_SIZE_SHIFT = static_cast<u32>(std::countr_zero(DESCRIPTOR_SIZE));
constexpr u32 DYNAMIC_DESCRIPTOR_CBUF_BYTES = 16 * 1024;
constexpr u32 MAX_DYNAMIC_DESCRIPTOR_COUNT = 1024;
u32 DynamicDescriptorSizeShift(const IR::U32& dynamic_offset) {
const IR::Inst* const inst{dynamic_offset.InstRecursive()};
if (!inst || inst->GetOpcode() != IR::Opcode::ShiftLeftLogical32) {
return DESCRIPTOR_SIZE_SHIFT;
}
const IR::Value shift{inst->Arg(1)};
if (!shift.IsImmediate()) {
return DESCRIPTOR_SIZE_SHIFT;
}
const u32 size_shift{shift.U32()};
return size_shift >= DESCRIPTOR_SIZE_SHIFT && size_shift < 31 ? size_shift
: DESCRIPTOR_SIZE_SHIFT;
}
u32 DynamicDescriptorCount(u32 base_offset, u32 size_shift) {
if (size_shift >= 31 || base_offset >= DYNAMIC_DESCRIPTOR_CBUF_BYTES) {
return 1;
}
const u32 stride{1U << size_shift};
const u32 available{DYNAMIC_DESCRIPTOR_CBUF_BYTES - base_offset};
if (available < DESCRIPTOR_SIZE) {
return 1;
}
const u32 available_count{1U + (available - DESCRIPTOR_SIZE) / stride};
return std::min(MAX_DYNAMIC_DESCRIPTOR_COUNT, available_count);
}
u32 SaturatingSub(u32 lhs, u32 rhs) {
return lhs > rhs ? lhs - rhs : 0;
}
template <typename Descriptors>
u32 StaticDescriptorCount(const Descriptors& descriptors) {
u32 count{};
for (const auto& desc : descriptors) {
if (desc.count <= 1) {
count += desc.count;
}
}
return count;
}
u32 DynamicSampledTextureCap(const Info& info, const HostTranslateInfo& host_info,
u32 dynamic_arrays) {
if (dynamic_arrays == 0) {
return MAX_DYNAMIC_DESCRIPTOR_COUNT;
}
const u32 sampled_static_count{StaticDescriptorCount(info.texture_buffer_descriptors) +
StaticDescriptorCount(info.texture_descriptors)};
const u32 resource_static_count{
NumDescriptors(info.constant_buffer_descriptors) +
NumDescriptors(info.storage_buffers_descriptors) + sampled_static_count +
NumDescriptors(info.image_buffer_descriptors) + NumDescriptors(info.image_descriptors)};
const u32 sampled_limit{std::min(host_info.max_per_stage_descriptor_sampled_images,
host_info.max_descriptor_set_sampled_images)};
const u32 sampled_budget{SaturatingSub(sampled_limit, sampled_static_count)};
const u32 resource_budget{SaturatingSub(host_info.max_per_stage_resources,
resource_static_count)};
const u32 sampled_cap{sampled_budget / dynamic_arrays};
const u32 resource_cap{resource_budget / dynamic_arrays};
return std::max(1U, std::min({MAX_DYNAMIC_DESCRIPTOR_COUNT, sampled_cap, resource_cap}));
}
IR::Opcode IndexedInstruction(const IR::Inst& inst) { IR::Opcode IndexedInstruction(const IR::Inst& inst) {
switch (inst.GetOpcode()) { switch (inst.GetOpcode()) {
@@ -174,39 +109,6 @@ IR::Opcode IndexedInstruction(const IR::Inst& inst) {
} }
} }
bool IsStorageImageOpcode(IR::Opcode opcode) {
switch (opcode) {
case IR::Opcode::ImageRead:
case IR::Opcode::ImageAtomicIAdd32:
case IR::Opcode::ImageAtomicSMin32:
case IR::Opcode::ImageAtomicUMin32:
case IR::Opcode::ImageAtomicSMax32:
case IR::Opcode::ImageAtomicUMax32:
case IR::Opcode::ImageAtomicInc32:
case IR::Opcode::ImageAtomicDec32:
case IR::Opcode::ImageAtomicAnd32:
case IR::Opcode::ImageAtomicOr32:
case IR::Opcode::ImageAtomicXor32:
case IR::Opcode::ImageAtomicExchange32:
case IR::Opcode::ImageWrite:
return true;
default:
return false;
}
}
u32 DynamicSampledTextureArrayCount(const TextureInstVector& to_replace) {
u32 count{};
for (const TextureInst& inst : to_replace) {
const auto flags{inst.inst->Flags<IR::TextureInstInfo>()};
if (inst.cbuf.count > 1 && !IsStorageImageOpcode(IndexedInstruction(*inst.inst)) &&
flags.type != TextureType::Buffer) {
++count;
}
}
return count;
}
bool IsBindless(const IR::Inst& inst) { bool IsBindless(const IR::Inst& inst) {
switch (inst.GetOpcode()) { switch (inst.GetOpcode()) {
case IR::Opcode::BindlessImageSampleImplicitLod: case IR::Opcode::BindlessImageSampleImplicitLod:
@@ -452,7 +354,6 @@ std::optional<ConstBufferAddr> TryGetConstBuffer(const IR::Inst* inst, Environme
} else { } else {
return std::nullopt; return std::nullopt;
} }
const u32 size_shift{DynamicDescriptorSizeShift(dynamic_offset)};
return ConstBufferAddr{ return ConstBufferAddr{
.index = index.U32(), .index = index.U32(),
.offset = base_offset, .offset = base_offset,
@@ -461,7 +362,7 @@ std::optional<ConstBufferAddr> TryGetConstBuffer(const IR::Inst* inst, Environme
.secondary_offset = 0, .secondary_offset = 0,
.secondary_shift_left = 0, .secondary_shift_left = 0,
.dynamic_offset = dynamic_offset, .dynamic_offset = dynamic_offset,
.count = DynamicDescriptorCount(base_offset, size_shift), .count = 8,
.has_secondary = false, .has_secondary = false,
}; };
} }
@@ -688,8 +589,6 @@ void TexturePass(Environment& env, IR::Program& program, const HostTranslateInfo
program.info.texture_descriptors, program.info.texture_descriptors,
program.info.image_descriptors, program.info.image_descriptors,
}; };
const u32 sampled_dynamic_cap{
DynamicSampledTextureCap(program.info, host_info, DynamicSampledTextureArrayCount(to_replace))};
for (TextureInst& texture_inst : to_replace) { for (TextureInst& texture_inst : to_replace) {
// TODO: Handle arrays // TODO: Handle arrays
IR::Inst* const inst{texture_inst.inst}; IR::Inst* const inst{texture_inst.inst};
@@ -733,9 +632,6 @@ void TexturePass(Environment& env, IR::Program& program, const HostTranslateInfo
break; break;
} }
u32 index; u32 index;
const u32 size_shift{cbuf.count > 1 ? DynamicDescriptorSizeShift(cbuf.dynamic_offset)
: DESCRIPTOR_SIZE_SHIFT};
u32 count{cbuf.count};
switch (inst->GetOpcode()) { switch (inst->GetOpcode()) {
case IR::Opcode::ImageRead: case IR::Opcode::ImageRead:
case IR::Opcode::ImageAtomicIAdd32: case IR::Opcode::ImageAtomicIAdd32:
@@ -764,8 +660,8 @@ void TexturePass(Environment& env, IR::Program& program, const HostTranslateInfo
.is_integer = is_integer, .is_integer = is_integer,
.cbuf_index = cbuf.index, .cbuf_index = cbuf.index,
.cbuf_offset = cbuf.offset, .cbuf_offset = cbuf.offset,
.count = count, .count = cbuf.count,
.size_shift = size_shift, .size_shift = DESCRIPTOR_SIZE_SHIFT,
}); });
} else { } else {
index = descriptors.Add(ImageDescriptor{ index = descriptors.Add(ImageDescriptor{
@@ -776,8 +672,8 @@ void TexturePass(Environment& env, IR::Program& program, const HostTranslateInfo
.is_integer = is_integer, .is_integer = is_integer,
.cbuf_index = cbuf.index, .cbuf_index = cbuf.index,
.cbuf_offset = cbuf.offset, .cbuf_offset = cbuf.offset,
.count = count, .count = cbuf.count,
.size_shift = size_shift, .size_shift = DESCRIPTOR_SIZE_SHIFT,
}); });
} }
break; break;
@@ -792,11 +688,10 @@ void TexturePass(Environment& env, IR::Program& program, const HostTranslateInfo
.secondary_cbuf_index = cbuf.secondary_index, .secondary_cbuf_index = cbuf.secondary_index,
.secondary_cbuf_offset = cbuf.secondary_offset, .secondary_cbuf_offset = cbuf.secondary_offset,
.secondary_shift_left = cbuf.secondary_shift_left, .secondary_shift_left = cbuf.secondary_shift_left,
.count = count, .count = cbuf.count,
.size_shift = size_shift, .size_shift = DESCRIPTOR_SIZE_SHIFT,
}); });
} else { } else {
count = std::min(count, sampled_dynamic_cap);
index = descriptors.Add(TextureDescriptor{ index = descriptors.Add(TextureDescriptor{
.type = flags.type, .type = flags.type,
.is_depth = flags.is_depth != 0, .is_depth = flags.is_depth != 0,
@@ -808,8 +703,8 @@ void TexturePass(Environment& env, IR::Program& program, const HostTranslateInfo
.secondary_cbuf_index = cbuf.secondary_index, .secondary_cbuf_index = cbuf.secondary_index,
.secondary_cbuf_offset = cbuf.secondary_offset, .secondary_cbuf_offset = cbuf.secondary_offset,
.secondary_shift_left = cbuf.secondary_shift_left, .secondary_shift_left = cbuf.secondary_shift_left,
.count = count, .count = cbuf.count,
.size_shift = size_shift, .size_shift = DESCRIPTOR_SIZE_SHIFT,
}); });
} }
break; break;
@@ -817,12 +712,12 @@ void TexturePass(Environment& env, IR::Program& program, const HostTranslateInfo
flags.descriptor_index.Assign(index); flags.descriptor_index.Assign(index);
inst->SetFlags(flags); inst->SetFlags(flags);
if (count > 1) { if (cbuf.count > 1) {
const auto insert_point{IR::Block::InstructionList::s_iterator_to(*inst)}; const auto insert_point{IR::Block::InstructionList::s_iterator_to(*inst)};
IR::IREmitter ir{*texture_inst.block, insert_point}; IR::IREmitter ir{*texture_inst.block, insert_point};
const IR::U32 shift{ir.Imm32(size_shift)}; const IR::U32 shift{ir.Imm32(DESCRIPTOR_SIZE_SHIFT)};
inst->SetArg(0, ir.UMin(ir.ShiftRightLogical(cbuf.dynamic_offset, shift), inst->SetArg(0, ir.UMin(ir.ShiftRightLogical(cbuf.dynamic_offset, shift),
ir.Imm32(count - 1))); ir.Imm32(DESCRIPTOR_SIZE - 1)));
} else { } else {
inst->SetArg(0, IR::Value{}); inst->SetArg(0, IR::Value{});
} }

Some files were not shown because too many files have changed in this diff Show More