Compare commits

..

5 Commits

Author SHA1 Message Date
lizzie d9bf7edb41 2026-09-25 11:37:08
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2026-09-25 11:37:08 +00:00
lizzie ac4a95ef63 2026-09-25 09:10:15
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2026-09-25 09:10:15 +00:00
lizzie 67ac72d35a Trigger build 2026-09-24 12:23:05 +00:00
lizzie 2495f7894d Fix license headers 2026-09-24 09:18:51 +00:00
lizzie 8f4e9d8c34 2026-09-24 09:00:03
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2026-09-24 09:00:03 +00:00
29 changed files with 244 additions and 533 deletions
@@ -16,16 +16,12 @@ import java.io.FileOutputStream
import java.security.KeyStore import java.security.KeyStore
import javax.net.ssl.TrustManagerFactory import javax.net.ssl.TrustManagerFactory
import javax.net.ssl.X509TrustManager import javax.net.ssl.X509TrustManager
import android.content.res.Configuration
import android.os.LocaleList
import org.yuzu.yuzu_emu.features.settings.model.IntSetting
import org.yuzu.yuzu_emu.utils.DirectoryInitialization import org.yuzu.yuzu_emu.utils.DirectoryInitialization
import org.yuzu.yuzu_emu.utils.DocumentsTree import org.yuzu.yuzu_emu.utils.DocumentsTree
import org.yuzu.yuzu_emu.utils.GpuDriverHelper import org.yuzu.yuzu_emu.utils.GpuDriverHelper
import org.yuzu.yuzu_emu.utils.Log import org.yuzu.yuzu_emu.utils.Log
import org.yuzu.yuzu_emu.utils.PowerStateUpdater import org.yuzu.yuzu_emu.utils.PowerStateUpdater
import org.yuzu.yuzu_emu.utils.ControllerNavigationGlobalHook import org.yuzu.yuzu_emu.utils.ControllerNavigationGlobalHook
import java.util.Locale
fun Context.getPublicFilesDir(): File = getExternalFilesDir(null) ?: filesDir fun Context.getPublicFilesDir(): File = getExternalFilesDir(null) ?: filesDir
@@ -84,38 +80,5 @@ class YuzuApplication : Application() {
val appContext: Context val appContext: Context
get() = application.applicationContext get() = application.applicationContext
private val LANGUAGE_CODES = arrayOf(
"system", "en", "es", "fr", "de", "it", "pt", "pt-BR", "ru", "ja", "ko",
"zh-CN", "zh-TW", "pl", "cs", "nb", "hu", "uk", "vi", "id", "ar", "ckb", "fa", "he", "sr"
)
fun applyLanguage(context: Context): Context {
val languageIndex = IntSetting.APP_LANGUAGE.getInt()
val langCode = if (languageIndex in LANGUAGE_CODES.indices) {
LANGUAGE_CODES[languageIndex]
} else {
"system"
}
if (langCode == "system") {
return context
}
val locale = when {
langCode.contains("-") -> {
val parts = langCode.split("-")
Locale.Builder().setLanguage(parts[0]).setRegion(parts[1]).build()
}
else -> Locale.Builder().setLanguage(langCode).build()
}
Locale.setDefault(locale)
val config = Configuration(context.resources.configuration)
config.setLocales(LocaleList(locale))
return context.createConfigurationContext(config)
}
} }
} }
@@ -115,10 +115,6 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener, InputManager
mainHandler.postDelayed(romSwapStopTimeoutRunnable, ROM_SWAP_STOP_TIMEOUT_MS) mainHandler.postDelayed(romSwapStopTimeoutRunnable, ROM_SWAP_STOP_TIMEOUT_MS)
} }
override fun attachBaseContext(base: Context) {
super.attachBaseContext(YuzuApplication.applyLanguage(base))
}
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
Log.gameLaunched = true Log.gameLaunched = true
ThemeHelper.setTheme(this) ThemeHelper.setTheme(this)
@@ -38,7 +38,6 @@ enum class IntSetting(override val key: String) : AbstractIntSetting {
THEME("theme"), THEME("theme"),
THEME_MODE("theme_mode"), THEME_MODE("theme_mode"),
STATIC_THEME_COLOR("static_theme_color"), STATIC_THEME_COLOR("static_theme_color"),
APP_LANGUAGE("app_language"),
OVERLAY_SCALE("control_scale"), OVERLAY_SCALE("control_scale"),
OVERLAY_OPACITY("control_opacity"), OVERLAY_OPACITY("control_opacity"),
LOCK_DRAWER("lock_drawer"), LOCK_DRAWER("lock_drawer"),
@@ -997,15 +997,6 @@ abstract class SettingsItem(
descriptionId = R.string.enable_qlaunch_button_description, descriptionId = R.string.enable_qlaunch_button_description,
) )
) )
put(
SingleChoiceSetting(
IntSetting.APP_LANGUAGE,
titleId = R.string.app_language,
descriptionId = R.string.app_language_description,
choicesId = R.array.appLanguageNames,
valuesId = R.array.appLanguageValues
)
)
put( put(
SwitchSetting( SwitchSetting(
BooleanSetting.RENDERER_DEBUG, BooleanSetting.RENDERER_DEBUG,
@@ -6,8 +6,6 @@
package org.yuzu.yuzu_emu.features.settings.ui package org.yuzu.yuzu_emu.features.settings.ui
import android.content.Context
import org.yuzu.yuzu_emu.YuzuApplication
import android.os.Bundle import android.os.Bundle
import android.view.View import android.view.View
import android.view.ViewGroup.MarginLayoutParams import android.view.ViewGroup.MarginLayoutParams
@@ -29,7 +27,6 @@ import org.yuzu.yuzu_emu.features.input.NativeInput
import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile
import org.yuzu.yuzu_emu.fragments.ResetSettingsDialogFragment import org.yuzu.yuzu_emu.fragments.ResetSettingsDialogFragment
import org.yuzu.yuzu_emu.utils.* import org.yuzu.yuzu_emu.utils.*
import org.yuzu.yuzu_emu.utils.collect
class SettingsActivity : AppCompatActivity() { class SettingsActivity : AppCompatActivity() {
private lateinit var binding: ActivitySettingsBinding private lateinit var binding: ActivitySettingsBinding
@@ -38,10 +35,6 @@ class SettingsActivity : AppCompatActivity() {
private val settingsViewModel: SettingsViewModel by viewModels() private val settingsViewModel: SettingsViewModel by viewModels()
override fun attachBaseContext(base: Context) {
super.attachBaseContext(YuzuApplication.applyLanguage(base))
}
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
ThemeHelper.setTheme(this) ThemeHelper.setTheme(this)
@@ -148,16 +141,6 @@ class SettingsActivity : AppCompatActivity() {
NativeConfig.savePerGameConfig() NativeConfig.savePerGameConfig()
NativeConfig.unloadPerGameConfig() NativeConfig.unloadPerGameConfig()
} }
if (settingsViewModel.shouldRecreateForLanguageChange.value) {
settingsViewModel.setShouldRecreateForLanguageChange(false)
val relaunchIntent = packageManager?.getLaunchIntentForPackage(packageName)
if (relaunchIntent != null) {
relaunchIntent.addFlags(android.content.Intent.FLAG_ACTIVITY_CLEAR_TASK or android.content.Intent.FLAG_ACTIVITY_NEW_TASK)
startActivity(relaunchIntent)
android.os.Process.killProcess(android.os.Process.myPid())
}
}
} }
} }
@@ -482,14 +482,6 @@ class SettingsAdapter(
position position
).show(fragment.childFragmentManager, SettingsDialogFragment.TAG) ).show(fragment.childFragmentManager, SettingsDialogFragment.TAG)
// reset language if detected
if (item.setting.key == "app_language") {
// recreate page apply language change instantly
fragment.requireActivity().recreate()
settingsViewModel.setShouldRecreateForLanguageChange(true)
}
return true return true
} }
@@ -387,12 +387,6 @@ class SettingsDialogFragment : DialogFragment(), DialogInterface.OnClickListener
) { ) {
settingsViewModel.setShouldReloadSettingsList(true) settingsViewModel.setShouldReloadSettingsList(true)
} }
if (scSetting.setting.key == "app_language") {
settingsViewModel.setShouldRecreateForLanguageChange(true)
// recreate page apply language change instantly
requireActivity().recreate()
}
} }
is StringSingleChoiceSetting -> { is StringSingleChoiceSetting -> {
@@ -1345,10 +1345,8 @@ class SettingsFragmentPresenter(
} }
} }
add(HeaderSetting(R.string.app_settings))
add(IntSetting.APP_LANGUAGE.key)
if (NativeLibrary.isUpdateCheckerEnabled()) { if (NativeLibrary.isUpdateCheckerEnabled()) {
add(HeaderSetting(R.string.app_settings))
add(BooleanSetting.ENABLE_UPDATE_CHECKS.key) add(BooleanSetting.ENABLE_UPDATE_CHECKS.key)
} }
@@ -44,10 +44,6 @@ class SettingsSubscreenActivity : AppCompatActivity() {
private val args by navArgs<SettingsSubscreenActivityArgs>() private val args by navArgs<SettingsSubscreenActivityArgs>()
override fun attachBaseContext(base: Context) {
super.attachBaseContext(YuzuApplication.applyLanguage(base))
}
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
ThemeHelper.setTheme(this) ThemeHelper.setTheme(this)
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2025 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
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project // SPDX-FileCopyrightText: 2023 yuzu Emulator Project
@@ -57,9 +57,6 @@ class SettingsViewModel : ViewModel() {
private val _shouldShowResetInputDialog = MutableStateFlow(false) private val _shouldShowResetInputDialog = MutableStateFlow(false)
val shouldShowResetInputDialog = _shouldShowResetInputDialog.asStateFlow() val shouldShowResetInputDialog = _shouldShowResetInputDialog.asStateFlow()
private val _shouldRecreateForLanguageChange = MutableStateFlow(false)
val shouldRecreateForLanguageChange = _shouldRecreateForLanguageChange.asStateFlow()
private val _shouldShowPathPicker = MutableStateFlow(false) private val _shouldShowPathPicker = MutableStateFlow(false)
val shouldShowPathPicker = _shouldShowPathPicker.asStateFlow() val shouldShowPathPicker = _shouldShowPathPicker.asStateFlow()
@@ -118,10 +115,6 @@ class SettingsViewModel : ViewModel() {
_shouldShowResetInputDialog.value = value _shouldShowResetInputDialog.value = value
} }
fun setShouldRecreateForLanguageChange(value: Boolean) {
_shouldRecreateForLanguageChange.value = value
}
fun setShouldShowPathPicker(value: Boolean) { fun setShouldShowPathPicker(value: Boolean) {
_shouldShowPathPicker.value = value _shouldShowPathPicker.value = value
} }
@@ -4,7 +4,6 @@
package org.yuzu.yuzu_emu.ui.main package org.yuzu.yuzu_emu.ui.main
import android.content.Intent import android.content.Intent
import android.content.Context
import android.net.Uri import android.net.Uri
import android.os.Bundle import android.os.Bundle
import android.view.View import android.view.View
@@ -71,10 +70,6 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
private val CHECKED_DECRYPTION = "CheckedDecryption" private val CHECKED_DECRYPTION = "CheckedDecryption"
private var checkedDecryption = false private var checkedDecryption = false
override fun attachBaseContext(base: Context) {
super.attachBaseContext(YuzuApplication.applyLanguage(base))
}
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
val splashScreen = installSplashScreen() val splashScreen = installSplashScreen()
splashScreen.setKeepOnScreenCondition { !DirectoryInitialization.areDirectoriesReady } splashScreen.setKeepOnScreenCondition { !DirectoryInitialization.areDirectoriesReady }
@@ -59,7 +59,6 @@ namespace AndroidSettings {
Settings::Setting<s32> static_theme_color{linkage, 0, "static_theme_color", Settings::Category::Android}; Settings::Setting<s32> static_theme_color{linkage, 0, "static_theme_color", Settings::Category::Android};
Settings::Setting<bool> black_backgrounds{linkage, false, "black_backgrounds", Settings::Setting<bool> black_backgrounds{linkage, false, "black_backgrounds",
Settings::Category::Android}; Settings::Category::Android};
Settings::Setting<s32> app_language{linkage, 0, "app_language", Settings::Category::Android};
Settings::Setting<bool> enable_update_checks{linkage, true, "enable_update_checks", Settings::Setting<bool> enable_update_checks{linkage, true, "enable_update_checks",
Settings::Category::Android}; Settings::Category::Android};
Settings::Setting<bool> enable_folder_button{linkage, true, "enable_folder_button", Settings::Setting<bool> enable_folder_button{linkage, true, "enable_folder_button",
@@ -409,62 +409,6 @@
<item>2</item> <item>2</item>
</integer-array> </integer-array>
<string-array name="appLanguageNames">
<item>@string/app_language_system</item>
<item>@string/app_language_english</item>
<item>@string/app_language_spanish</item>
<item>@string/app_language_french</item>
<item>@string/app_language_german</item>
<item>@string/app_language_italian</item>
<item>@string/app_language_portuguese</item>
<item>@string/app_language_brazilian_portuguese</item>
<item>@string/app_language_russian</item>
<item>@string/app_language_japanese</item>
<item>@string/app_language_korean</item>
<item>@string/app_language_simplified_chinese</item>
<item>@string/app_language_traditional_chinese</item>
<item>@string/app_language_polish</item>
<item>@string/app_language_czech</item>
<item>@string/app_language_norwegian</item>
<item>@string/app_language_hungarian</item>
<item>@string/app_language_ukrainian</item>
<item>@string/app_language_vietnamese</item>
<item>@string/app_language_indonesian</item>
<item>@string/app_language_arabic</item>
<item>@string/app_language_central_kurdish</item>
<item>@string/app_language_persian</item>
<item>@string/app_language_hebrew</item>
<item>@string/app_language_serbian</item>
<item>@string/app_language_thai</item>
</string-array>
<integer-array name="appLanguageValues">
<item>0</item>
<item>1</item>
<item>2</item>
<item>3</item>
<item>4</item>
<item>5</item>
<item>6</item>
<item>7</item>
<item>8</item>
<item>9</item>
<item>10</item>
<item>11</item>
<item>12</item>
<item>13</item>
<item>14</item>
<item>15</item>
<item>16</item>
<item>17</item>
<item>18</item>
<item>19</item>
<item>20</item>
<item>21</item>
<item>22</item>
<item>23</item>
<item>24</item>
</integer-array>
<string-array name="outputEngineEntries"> <string-array name="outputEngineEntries">
<item>@string/auto</item> <item>@string/auto</item>
<item>@string/sdl3</item> <item>@string/sdl3</item>
@@ -1281,36 +1281,6 @@
<string name="enable_qlaunch_button">QLaunch</string> <string name="enable_qlaunch_button">QLaunch</string>
<string name="enable_qlaunch_button_description">Show the button to launch QLaunch</string> <string name="enable_qlaunch_button_description">Show the button to launch QLaunch</string>
<!-- App Language -->
<string name="app_language">App Language</string>
<string name="app_language_description">Change the language of the app interface</string>
<string name="app_language_system">Follow System</string>
<string name="app_language_english" translatable="false">English</string>
<string name="app_language_spanish" translatable="false">Español</string>
<string name="app_language_french" translatable="false">Français</string>
<string name="app_language_german" translatable="false">Deutsch</string>
<string name="app_language_italian" translatable="false">Italiano</string>
<string name="app_language_portuguese" translatable="false">Português</string>
<string name="app_language_brazilian_portuguese" translatable="false">Português do Brasil</string>
<string name="app_language_russian" translatable="false">Русский</string>
<string name="app_language_japanese" translatable="false">日本語</string>
<string name="app_language_korean" translatable="false">한국어</string>
<string name="app_language_simplified_chinese" translatable="false">简体中文</string>
<string name="app_language_traditional_chinese" translatable="false">繁體中文</string>
<string name="app_language_polish" translatable="false">Polski</string>
<string name="app_language_czech" translatable="false">Čeština</string>
<string name="app_language_norwegian" translatable="false">Norsk bokmål</string>
<string name="app_language_hungarian" translatable="false">Magyar</string>
<string name="app_language_ukrainian" translatable="false">Українська</string>
<string name="app_language_vietnamese" translatable="false">Tiếng Việt</string>
<string name="app_language_indonesian" translatable="false">Bahasa Indonesia</string>
<string name="app_language_arabic" translatable="false">العربية</string>
<string name="app_language_central_kurdish" translatable="false">کوردیی ناوەندی</string>
<string name="app_language_persian" translatable="false">فارسی</string>
<string name="app_language_hebrew" translatable="false">עברית</string>
<string name="app_language_serbian" translatable="false">Српски</string>
<string name="app_language_thai" translatable="false">แบบไทย</string>
<!-- Static Themes --> <!-- Static Themes -->
<string name="static_theme_color">Theme Color</string> <string name="static_theme_color">Theme Color</string>
<string name="eden_theme">Eden</string> <string name="eden_theme">Eden</string>
+1
View File
@@ -76,6 +76,7 @@ add_library(
logging.h logging.h
lz4_compression.cpp lz4_compression.cpp
lz4_compression.h lz4_compression.h
make_unique_for_overwrite.h
math_util.h math_util.h
memory_detect.cpp memory_detect.cpp
memory_detect.h memory_detect.h
+27
View File
@@ -0,0 +1,27 @@
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <memory>
#include <type_traits>
namespace Common {
template <class T>
requires(!std::is_array_v<T>)
std::unique_ptr<T> make_unique_for_overwrite() {
return std::unique_ptr<T>(new T);
}
template <class T>
requires std::is_unbounded_array_v<T>
std::unique_ptr<T> make_unique_for_overwrite(std::size_t n) {
return std::unique_ptr<T>(new std::remove_extent_t<T>[n]);
}
template <class T, class... Args>
requires std::is_bounded_array_v<T>
void make_unique_for_overwrite(Args&&...) = delete;
} // namespace Common
+6 -6
View File
@@ -8,7 +8,8 @@
#include <iterator> #include <iterator>
#include <cstring> #include <cstring>
#include <memory>
#include "common/make_unique_for_overwrite.h"
namespace Common { namespace Common {
@@ -37,9 +38,8 @@ public:
ScratchBuffer() = default; ScratchBuffer() = default;
explicit ScratchBuffer(size_type initial_capacity) explicit ScratchBuffer(size_type initial_capacity)
: last_requested_size{initial_capacity} : last_requested_size{initial_capacity}, buffer_capacity{initial_capacity},
, buffer_capacity{initial_capacity} buffer{Common::make_unique_for_overwrite<T[]>(initial_capacity)} {}
, buffer{std::make_unique_for_overwrite<T[]>(initial_capacity)} {}
~ScratchBuffer() = default; ~ScratchBuffer() = default;
ScratchBuffer(const ScratchBuffer&) = delete; ScratchBuffer(const ScratchBuffer&) = delete;
@@ -64,7 +64,7 @@ public:
/// The previously held data will remain intact. /// The previously held data will remain intact.
void resize(size_type size) { void resize(size_type size) {
if (size > buffer_capacity) { if (size > buffer_capacity) {
auto new_buffer = std::make_unique_for_overwrite<T[]>(size); auto new_buffer = Common::make_unique_for_overwrite<T[]>(size);
std::memcpy(new_buffer.get(), buffer.get(), buffer_capacity * sizeof(T)); std::memcpy(new_buffer.get(), buffer.get(), buffer_capacity * sizeof(T));
buffer = std::move(new_buffer); buffer = std::move(new_buffer);
buffer_capacity = size; buffer_capacity = size;
@@ -77,7 +77,7 @@ public:
void resize_destructive(size_type size) { void resize_destructive(size_type size) {
if (size > buffer_capacity) { if (size > buffer_capacity) {
buffer_capacity = size; buffer_capacity = size;
buffer = std::make_unique_for_overwrite<T[]>(buffer_capacity); buffer = Common::make_unique_for_overwrite<T[]>(buffer_capacity);
} }
last_requested_size = size; last_requested_size = size;
} }
+54 -121
View File
@@ -8,14 +8,7 @@
#ifdef _WIN32 #ifdef _WIN32
#include <windows.h> #include <windows.h>
#include <mutex> #include <mutex>
#include <algorithm> #else
#include <vector>
#endif
#include <cerrno>
#include <cstring>
#ifndef _WIN32
#include <sys/mman.h> #include <sys/mman.h>
#endif #endif
@@ -26,60 +19,54 @@
namespace Common { namespace Common {
#ifdef _WIN32 #ifdef _WIN32
static std::vector<std::pair<u64, u64>> vector_regions {};
struct VectorRegion { // Workaround for handling non-commited memory accessed by Dynarmic; usually result of an error
u64 start_page;
u64 end_page;
};
static std::mutex& GetVectorRegionsMutex() {
static std::mutex* m = new std::mutex();
return *m;
}
static std::vector<VectorRegion>& GetVectorRegions() {
static std::vector<VectorRegion>* v = new std::vector<VectorRegion>();
return *v;
}
static LONG WINAPI FakePageFaultHandler(PEXCEPTION_POINTERS info) { static LONG WINAPI FakePageFaultHandler(PEXCEPTION_POINTERS info) {
if (info->ExceptionRecord->ExceptionCode != EXCEPTION_ACCESS_VIOLATION) { DWORD code = info->ExceptionRecord->ExceptionCode;
u64 exception_addr = reinterpret_cast<u64>(info->ExceptionRecord->ExceptionAddress);
if (code != EXCEPTION_ACCESS_VIOLATION) {
// Not our problem
return EXCEPTION_CONTINUE_SEARCH; return EXCEPTION_CONTINUE_SEARCH;
} }
const u64 fault_addr = info->ExceptionRecord->ExceptionInformation[1]; u64 addr = 0, addr2 = 0;
const u64 access_type = info->ExceptionRecord->ExceptionInformation[0];
const bool is_write = (access_type == 1);
const u64 fault_page = fault_addr >> HostPageBits;
u64 addr = 0; for (auto region: vector_regions) {
u64 addr2 = 0; auto addr_shifted = exception_addr >> HostPageBits;
if (region.first <= addr_shifted && addr_shifted <= region.second) {
addr = addr_shifted;
}
{ // Page-boundary accesses
std::lock_guard lock(GetVectorRegionsMutex()); if (auto addr_ = (exception_addr + 0x40) >> HostPageBits; addr_ != addr_shifted && region.first <= addr_ && addr_ <= region.second) {
for (const auto& region : GetVectorRegions()) { addr2 = addr_;
if (fault_page >= region.start_page && fault_page < region.end_page) { }
addr = fault_page;
} if (addr != 0 || addr2 != 0) {
const u64 page2 = (fault_addr + 0x3F) >> HostPageBits; break;
if (page2 != fault_page && page2 >= region.start_page && page2 < region.end_page) {
addr2 = page2;
}
if (addr != 0 || addr2 != 0) break;
} }
} }
if (addr == 0 && addr2 == 0) { if (addr == 0 && addr2 == 0) {
// Not our problem
return EXCEPTION_CONTINUE_SEARCH; return EXCEPTION_CONTINUE_SEARCH;
} }
LOG_ERROR(HW_Memory, "Accessing an unallocated region of a SparseLargeVector at {:#x}; this shouldn't happen and is likely a Dynarmic error!", fault_addr); LOG_ERROR(HW_Memory, "Accessing an unallocated region of a SparseLargeVector at {:#x}; this shouldn't happen and is likely a Dynarmic error!", exception_addr);
if (addr != 0 && !CommitVectorPage(addr << HostPageBits, is_write)) { // Commit this region
return EXCEPTION_CONTINUE_SEARCH; if (addr != 0) {
if (!CommitVectorPage(addr << HostPageBits, false)) {
return EXCEPTION_CONTINUE_SEARCH;
}
} }
if (addr2 != 0 && !CommitVectorPage(addr2 << HostPageBits, is_write)) { // Commit next region if needed
return EXCEPTION_CONTINUE_SEARCH; if (addr2 != 0) {
if (!CommitVectorPage(addr2 << HostPageBits, false)) {
return EXCEPTION_CONTINUE_SEARCH;
}
} }
return EXCEPTION_CONTINUE_EXECUTION; return EXCEPTION_CONTINUE_EXECUTION;
@@ -87,31 +74,23 @@ static LONG WINAPI FakePageFaultHandler(PEXCEPTION_POINTERS info) {
bool CommitVectorPage(uintptr_t addr, bool write) noexcept { bool CommitVectorPage(uintptr_t addr, bool write) noexcept {
MEMORY_BASIC_INFORMATION info {}; MEMORY_BASIC_INFORMATION info {};
const auto res = VirtualQuery(reinterpret_cast<void*>(addr), &info, sizeof(info)); auto res = VirtualQuery(reinterpret_cast<void*>(addr), &info, sizeof(info));
const DWORD perm = write ? PAGE_READWRITE : PAGE_READONLY;
if (res == 0) { if (res == 0) {
LOG_CRITICAL(HW_Memory, "Failed to query large buffer region at {:#x} with error {}, will try committing anyway", addr, GetLastError()); LOG_CRITICAL(HW_Memory, "Failed to query large buffer region at {:#x} with error {}, will try committing anyway", addr, GetLastError());
} else if (info.State == MEM_COMMIT) {
DWORD old_protect {};
if (!VirtualProtect(reinterpret_cast<void*>(addr), HostPageSize, perm, &old_protect)) {
LOG_ERROR(HW_Memory, "VirtualProtect failed at {:#x}, error {}", addr, GetLastError());
return false;
}
return true;
} else if (info.State != MEM_RESERVE) { } else if (info.State != MEM_RESERVE) {
LOG_ERROR(HW_Memory, "Tried to commit an unreserved large buffer region at {:#x} (state {:#x})", addr, info.State); LOG_ERROR(HW_Memory, "Tried to commit an unreserved large buffer region at {:#x} that is not mapped or is already committed (state {:#x})", addr, info.State);
return false; return false;
} }
if (VirtualAlloc(reinterpret_cast<LPVOID>(addr), HostPageSize, MEM_COMMIT, perm) == nullptr) { auto perm = write ? PAGE_READWRITE : PAGE_READONLY;
void* res2 = VirtualAlloc(reinterpret_cast<LPVOID>(addr), HostPageSize, MEM_COMMIT, perm);
if (res2 == nullptr) {
LOG_ERROR(HW_Memory, "Failed to commit large buffer region at {:#x}, error {}", addr, GetLastError()); LOG_ERROR(HW_Memory, "Failed to commit large buffer region at {:#x}, error {}", addr, GetLastError());
return false; return false;
} }
return true; return true;
} }
#endif #endif
#ifndef MAP_NOCORE #ifndef MAP_NOCORE
@@ -123,102 +102,56 @@ bool CommitVectorPage(uintptr_t addr, bool write) noexcept {
void DecommitVectorPage(uintptr_t base) noexcept { void DecommitVectorPage(uintptr_t base) noexcept {
#if defined(_WIN32) #if defined(_WIN32)
if (!VirtualFree(reinterpret_cast<LPVOID>(base), HostPageSize, MEM_DECOMMIT)) { VirtualFree(reinterpret_cast<LPVOID>(base), HostPageSize, MEM_DECOMMIT);
LOG_WARNING(HW_Memory, "VirtualFree(MEM_DECOMMIT) failed at {:#x}, error {}", base, GetLastError());
}
#elif defined(__linux__) #elif defined(__linux__)
if (madvise(reinterpret_cast<void*>(base), HostPageSize, MADV_DONTNEED) != 0) { // Linux's MADV_DONTNEED zeros out pages for us
LOG_WARNING(HW_Memory, "madvise(MADV_DONTNEED) failed at {:#x}: {}", base, std::strerror(errno)); madvise(reinterpret_cast<void*>(base), HostPageSize, MADV_DONTNEED);
}
#else #else
if (madvise(reinterpret_cast<void*>(base), HostPageSize, MADV_FREE) != 0) { madvise(reinterpret_cast<void*>(base), HostPageSize, MADV_FREE);
LOG_WARNING(HW_Memory, "madvise(MADV_FREE) failed at {:#x}: {}", base, std::strerror(errno));
}
std::memset(reinterpret_cast<void*>(base), 0, HostPageSize); std::memset(reinterpret_cast<void*>(base), 0, HostPageSize);
#endif #endif
} }
void* AllocateMemoryPages(std::size_t size) noexcept { void* AllocateMemoryPages(std::size_t size) noexcept {
if (size == 0) { if (auto page = HostPageSize; size % page != 0) {
return nullptr;
}
const auto page = HostPageSize;
if (size % page != 0) {
LOG_WARNING(HW_Memory, "Allocating unaligned large vector with size {:#x}; aligning to {} page size", size, page); LOG_WARNING(HW_Memory, "Allocating unaligned large vector with size {:#x}; aligning to {} page size", size, page);
if (size > SIZE_MAX - (page - 1)) {
LOG_CRITICAL(HW_Memory, "Size {:#x} would overflow page alignment", size);
return nullptr;
}
size = AlignUp(size, page); size = AlignUp(size, page);
} }
#ifdef _WIN32 #ifdef _WIN32
// We will never use this memory entirely so instead of committing it up front let's just reserve it and commit each page individually
void* base = VirtualAlloc(nullptr, size, MEM_RESERVE, PAGE_READWRITE); void* base = VirtualAlloc(nullptr, size, MEM_RESERVE, PAGE_READWRITE);
if (base != nullptr) { if (base != nullptr) {
{ vector_regions.emplace_back(reinterpret_cast<u64>(base), reinterpret_cast<u64>(base) + size);
std::lock_guard lock(GetVectorRegionsMutex());
GetVectorRegions().push_back({
reinterpret_cast<u64>(base) >> HostPageBits,
(reinterpret_cast<u64>(base) + size) >> HostPageBits,
});
}
static std::once_flag flag; static std::once_flag flag;
std::call_once(flag, []() { AddVectoredExceptionHandler(1, FakePageFaultHandler); }); std::call_once(flag, []() { AddVectoredExceptionHandler(1, FakePageFaultHandler); });
} else { } else {
// Try committing everything instead??
LOG_WARNING(HW_Memory, "Failed to reserve large vector region with error {}, trying to commit instead..", GetLastError()); LOG_WARNING(HW_Memory, "Failed to reserve large vector region with error {}, trying to commit instead..", GetLastError());
base = VirtualAlloc(nullptr, size, MEM_COMMIT, PAGE_READWRITE); base = VirtualAlloc(nullptr, size, MEM_COMMIT, PAGE_READWRITE);
} }
ASSERT_MSG(base, "Failed to reserve {:#x} sized region with error {}", size, GetLastError()); ASSERT_MSG(base, "Failed to reserve {:#x} sized region with error {}", size, GetLastError());
#else #else
int flags = MAP_ANON | MAP_PRIVATE; void* base = mmap(nullptr, size, PROT_READ, MAP_ANON | MAP_PRIVATE | MAP_NOCORE, -1, 0);
#ifdef MAP_NORESERVE if (base == MAP_FAILED)
flags |= MAP_NORESERVE;
#endif
#if defined(MAP_NOCORE)
flags |= MAP_NOCORE;
#endif
void* base = mmap(nullptr, size, PROT_READ, flags, -1, 0);
if (base == MAP_FAILED) {
base = nullptr; base = nullptr;
} ASSERT_MSG(base, "Failed to allocate {:#x} sized region with error {}", size, strerror(errno));
#ifdef MADV_HUGEPAGE
if (base != nullptr) {
madvise(base, size, MADV_HUGEPAGE);
}
#endif #endif
ASSERT_MSG(base, "Failed to allocate {:#x} sized region with error {}", size, std::strerror(errno));
#endif
return base; return base;
} }
void FreeMemoryPages(void* base, [[maybe_unused]] std::size_t size) noexcept { void FreeMemoryPages(void* base, [[maybe_unused]] std::size_t size) noexcept {
if (base == nullptr) { if (auto page = HostPageSize; size % page != 0) {
return;
}
if (const auto page = HostPageSize; size % page != 0) {
size = AlignUp(size, page); size = AlignUp(size, page);
} }
if (!base)
return;
#ifdef _WIN32 #ifdef _WIN32
{ ASSERT(VirtualFree(base, 0, MEM_RELEASE));
std::lock_guard lock(GetVectorRegionsMutex());
auto& regions = GetVectorRegions();
const u64 base_page = reinterpret_cast<u64>(base) >> HostPageBits;
regions.erase(std::remove_if(regions.begin(), regions.end(),
[base_page](const VectorRegion& r) { return r.start_page == base_page; }), regions.end());
}
if (!VirtualFree(base, 0, MEM_RELEASE)) {
LOG_ERROR(HW_Memory, "VirtualFree failed, error {}", GetLastError());
}
#else #else
if (munmap(base, size) != 0) { ASSERT(munmap(base, size) == 0);
LOG_ERROR(HW_Memory, "munmap failed: {}", std::strerror(errno));
}
#endif #endif
} }
+107 -145
View File
@@ -1,4 +1,4 @@
// 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
/* virtual_buffer.h */ /* virtual_buffer.h */
@@ -7,14 +7,10 @@
#pragma once #pragma once
#include <array>
#include <atomic> #include <atomic>
#include <bit> #include <bit>
#include <cerrno> #include <utility>
#include <cstdlib> #include <vector>
#include <cstring>
#include <memory>
#include <type_traits>
#ifndef _WIN32 #ifndef _WIN32
#include <unistd.h> #include <unistd.h>
@@ -32,9 +28,9 @@ constexpr u64 HostPageBits = 12;
constexpr u64 HostPageMask = ~(HostPageSize - 1); constexpr u64 HostPageMask = ~(HostPageSize - 1);
bool CommitVectorPage(uintptr_t addr, bool write) noexcept; bool CommitVectorPage(uintptr_t addr, bool write) noexcept;
#else #else
inline const u64 HostPageSize = static_cast<u64>(sysconf(_SC_PAGESIZE)); const u64 HostPageSize = sysconf(_SC_PAGESIZE);
inline const u64 HostPageBits = std::countr_zero(HostPageSize); const u64 HostPageBits = std::countr_zero(HostPageSize);
inline const u64 HostPageMask = ~(HostPageSize - 1); const u64 HostPageMask = ~(HostPageSize - 1);
#endif #endif
void* AllocateMemoryPages(std::size_t size) noexcept; void* AllocateMemoryPages(std::size_t size) noexcept;
@@ -47,18 +43,20 @@ template <typename T>
// requires std::is_trivially_copyable_v<T> // requires std::is_trivially_copyable_v<T>
class SparseLargeVector final { class SparseLargeVector final {
public: public:
SparseLargeVector() = default; constexpr SparseLargeVector() = default;
explicit SparseLargeVector(std::size_t count) noexcept { explicit SparseLargeVector(std::size_t count) noexcept
if (count > SIZE_MAX / sizeof(T)) { : alloc_size{count * sizeof(T)}
LOG_CRITICAL(Common_Memory, "SparseLargeVector size overflow: {} elements", count); {
return; base_ptr = static_cast<T*>(AllocateMemoryPages(alloc_size));
}
Allocate(count * sizeof(T)); // each item in vector holds information for 64 pages
auto denom = HostPageSize * 64;
committed_pages = std::vector<std::atomic<u64>>((alloc_size + denom - 1) / denom);
} }
~SparseLargeVector() noexcept { ~SparseLargeVector() noexcept {
Release(); FreeMemoryPages(base_ptr, alloc_size);
} }
SparseLargeVector(const SparseLargeVector&) = delete; SparseLargeVector(const SparseLargeVector&) = delete;
@@ -67,181 +65,145 @@ public:
SparseLargeVector& operator=(SparseLargeVector&& other) = delete; SparseLargeVector& operator=(SparseLargeVector&& other) = delete;
void ResizeAndClear(std::size_t count) noexcept { void ResizeAndClear(std::size_t count) noexcept {
if (count > SIZE_MAX / sizeof(T)) { if (auto const new_size = count * sizeof(T); new_size != alloc_size) {
LOG_CRITICAL(Common_Memory, "SparseLargeVector resize overflow: {} elements", count); FreeMemoryPages(base_ptr, alloc_size);
return; alloc_size = new_size;
base_ptr = static_cast<T*>(AllocateMemoryPages(alloc_size));
auto denom = HostPageSize * 64;
committed_pages = std::vector<std::atomic<u64>>((alloc_size + denom - 1) / denom);
} }
const std::size_t new_size = count * sizeof(T);
if (new_size == alloc_size) {
ZeroRegion(0, alloc_size / sizeof(T));
return;
}
Release();
Allocate(new_size);
} }
/// Returns a reference to the value of the requested index and allocates memory if needed.
T& GetAndFault(std::size_t index) noexcept { T& GetAndFault(std::size_t index) noexcept {
if (base_ptr == nullptr || index >= size()) [[unlikely]] { if (index > alloc_size / sizeof(T)) {
LOG_CRITICAL(Common_Memory, "SparseLargeVector RW access out of bounds @ {} (size {})", index, size()); UNREACHABLE_MSG("Out of bounds RW access on SparseLargeVector @ {}", index);
std::abort();
} }
const u64 byte_offset = static_cast<u64>(index) * sizeof(T);
if (!CommitPage(byte_offset)) [[unlikely]] { if (!IsCommittedPage(index)) {
LOG_CRITICAL(Common_Memory, "SparseLargeVector commit failed @ {} (offset {:#x})", index, byte_offset); CommitPage(index);
std::abort();
} }
return base_ptr[index]; return base_ptr[index];
} }
const T& GetOrDefault(std::size_t index) const noexcept { /// Returns a reference to the value of the requested index if initialized, or will otherwise return a zero-initialized object.
if (base_ptr == nullptr || index >= size()) [[unlikely]] { const T& GetOrDefault(std::size_t index) const {
LOG_CRITICAL(Common_Memory, "SparseLargeVector RO access out of bounds @ {}", index);
return DefaultValue();
}
#ifdef _WIN32 #ifdef _WIN32
if (!IsPageCommitted(static_cast<u64>(index) * sizeof(T))) { if (!IsCommittedPage(index)) {
return DefaultValue(); return *reinterpret_cast<const T*>(&default_val);
} }
#endif #endif
// On non-Windows, OS page table should optimize this by pointing to a zero page if unallocated.
return base_ptr[index]; return base_ptr[index];
} }
void Set(std::size_t index, const T& value) noexcept { void Set(std::size_t index, const T& value) noexcept {
if (base_ptr == nullptr || index >= size()) [[unlikely]] { if (index > alloc_size / sizeof(T)) {
LOG_CRITICAL(Common_Memory, "SparseLargeVector write out of bounds @ {}", index); LOG_CRITICAL(Common_Memory, "Out of bounds write on SparseLargeVector @ {}", index);
return;
}
const u64 byte_offset = static_cast<u64>(index) * sizeof(T);
if (!CommitPage(byte_offset)) [[unlikely]] {
LOG_CRITICAL(Common_Memory, "SparseLargeVector commit failed for write @ {}", index);
return; return;
} }
if (!IsCommittedPage(index))
CommitPage(index);
base_ptr[index] = value; base_ptr[index] = value;
} }
void ZeroRegion(std::size_t start, std::size_t end_) noexcept { void ZeroRegion(std::size_t start, std::size_t end_) noexcept {
if (base_ptr == nullptr || start >= end_) return; u64 base = reinterpret_cast<u64>(&base_ptr[start]);
const u64 end = reinterpret_cast<u64>(&base_ptr[end_]);
const u64 start_off = static_cast<u64>(start) * sizeof(T); const u64 end_page = AlignUp(base, HostPageSize);
const u64 end_off = static_cast<u64>(end_) * sizeof(T); const u64 first_size = (std::min)(end_page, end) - base;
const u64 first_page_end = (start_off + HostPageSize - 1) & HostPageMask;
if (start_off < first_page_end) { if (IsCommittedPage(start)) {
const u64 chunk_end = (std::min)(first_page_end, end_off); std::memset(reinterpret_cast<void*>(base), 0, first_size);
const u64 chunk_size = chunk_end - start_off;
if (chunk_size != 0 && IsPageCommitted(start_off)) {
std::memset(reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(base_ptr) + start_off), 0, chunk_size);
}
if (end_off <= first_page_end) return;
} }
for (u64 off = first_page_end; off < end_off; off += HostPageSize) { if (end <= end_page)
if (!IsPageCommitted(off)) continue; return;
const u64 remaining = end_off - off;
if (remaining >= HostPageSize) { base = end_page;
DecommitPage(off);
for (u64 page = base; page < end; page += HostPageSize) {
auto index = (page - reinterpret_cast<u64>(base_ptr)) / sizeof(T);
if (!IsCommittedPage(index)) {
continue;
}
if (end - page >= HostPageSize) {
DecommitPage(index);
} else { } else {
std::memset(reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(base_ptr) + off), 0, remaining); std::memset(reinterpret_cast<void*>(page), 0, end - page);
} }
} }
} }
void CommitRegion(std::size_t index, std::size_t end_) noexcept { constexpr void CommitRegion(size_t index, size_t end_) {
if (base_ptr == nullptr || index >= end_) return; const u64 base = static_cast<u64>(index) * sizeof(T);
const u64 start_off = static_cast<u64>(index) * sizeof(T); const u64 end = static_cast<u64>(end_) * sizeof(T);
const u64 end_off = static_cast<u64>(end_) * sizeof(T);
const u64 start_page = start_off & HostPageMask; for (u64 page = AlignDown(base, HostPageSize); page < end; page += HostPageSize) {
for (u64 off = start_page; off < end_off; off += HostPageSize) { if (!IsCommittedPage(page / sizeof(T))) {
if (!IsPageCommitted(off)) { CommitPage(page / sizeof(T));
(void)CommitPage(off);
} }
} }
} }
T& GetUnchecked(std::size_t index) noexcept { return base_ptr[index]; } constexpr T& GetUnchecked(size_t index) {
return base_ptr[index];
}
[[nodiscard]] const T& operator[](std::size_t index) const noexcept { return GetOrDefault(index); } [[nodiscard]] constexpr const T& operator[](std::size_t index) const noexcept {
[[nodiscard]] const T* data() const noexcept { return base_ptr; } return GetOrDefault(index);
[[nodiscard]] std::size_t size() const noexcept { return alloc_size / sizeof(T); } }
[[nodiscard]] constexpr const T* data() const noexcept {
return base_ptr;
}
[[nodiscard]] constexpr std::size_t size() const noexcept {
return alloc_size / sizeof(T);
}
private: private:
void Allocate(std::size_t new_size) noexcept { [[nodiscard]] constexpr bool IsCommittedPage(std::size_t index) const noexcept {
alloc_size = new_size; if (index > alloc_size / sizeof(T)) {
if (alloc_size == 0) { LOG_CRITICAL(Common_Memory, "Out of bounds access on large vector @ {}", index);
base_ptr = nullptr;
committed_pages.reset();
return;
}
base_ptr = static_cast<T*>(AllocateMemoryPages(alloc_size));
const std::size_t num_pages = NumPages();
const std::size_t num_words = (num_pages + 63) / 64;
committed_pages = std::make_unique<std::atomic<u64>[]>(num_words);
}
void Release() noexcept {
if (base_ptr != nullptr) {
FreeMemoryPages(base_ptr, alloc_size);
base_ptr = nullptr;
}
committed_pages.reset();
alloc_size = 0;
}
[[nodiscard]] u64 NumPages() const noexcept {
return (alloc_size + HostPageSize - 1) >> HostPageBits;
}
[[nodiscard]] bool IsPageCommitted(u64 byte_offset) const noexcept {
const u64 page_index = byte_offset >> HostPageBits;
if (committed_pages == nullptr || page_index >= NumPages()) return false;
const auto val = committed_pages[page_index >> 6].load(std::memory_order_acquire);
return (val >> (page_index & 63)) & 1;
}
void SetPageBit(u64 page_index, bool value) noexcept {
if (committed_pages == nullptr) return;
const u64 bit = 1ULL << (page_index & 63);
auto& atom = committed_pages[page_index >> 6];
if (value) {
atom.fetch_or(bit, std::memory_order_release);
} else {
atom.fetch_and(~bit, std::memory_order_release);
}
}
bool CommitPage(u64 byte_offset) noexcept {
const u64 page_index = byte_offset >> HostPageBits;
const uintptr_t page_addr = (reinterpret_cast<uintptr_t>(base_ptr) + byte_offset) & HostPageMask;
if (IsPageCommitted(byte_offset)) return true;
#if defined(_WIN32)
if (!CommitVectorPage(page_addr, true)) return false;
#else
if (mprotect(reinterpret_cast<void*>(page_addr), HostPageSize, PROT_READ | PROT_WRITE) != 0) {
LOG_ERROR(Common_Memory, "mprotect failed at {:#x}: {}", page_addr, std::strerror(errno));
return false; return false;
} }
auto page = (index * sizeof(T)) >> HostPageBits;
auto val = committed_pages[page >> 6].load(std::memory_order_acquire);
return (val >> (page & 63)) & 1;
}
constexpr void CommitPage(std::size_t index) noexcept {
auto page_index = (index * sizeof(T)) >> HostPageBits;
auto page = reinterpret_cast<uintptr_t>(base_ptr + index) & HostPageMask;
#if defined(_WIN32)
CommitVectorPage(page, true);
#else
mprotect(reinterpret_cast<void*>(page), HostPageSize, PROT_READ | PROT_WRITE);
#endif #endif
SetPageBit(page_index, true);
return true; committed_pages[page_index >> 6].fetch_or(1ULL << (page_index & 63), std::memory_order_release);
} }
void DecommitPage(u64 byte_offset) noexcept { constexpr void DecommitPage(std::size_t index) noexcept {
const u64 page_index = byte_offset >> HostPageBits; auto page_index = (index * sizeof(T)) >> HostPageBits;
const uintptr_t page_addr = (reinterpret_cast<uintptr_t>(base_ptr) + byte_offset) & HostPageMask; auto page = reinterpret_cast<uintptr_t>(base_ptr + index) & HostPageMask;
DecommitVectorPage(page_addr);
SetPageBit(page_index, false);
}
[[nodiscard]] const T& DefaultValue() const noexcept { committed_pages[page_index >> 6].fetch_and(~(1ULL << (page_index & 63)), std::memory_order_release);
return *reinterpret_cast<const T*>(&default_val); DecommitVectorPage(page);
} }
std::size_t alloc_size{}; std::size_t alloc_size{};
T* base_ptr{}; T* base_ptr{};
std::unique_ptr<std::atomic<u64>[]> committed_pages{};
alignas(T) const std::array<u8, sizeof(T)> default_val{}; std::vector<std::atomic<u64>> committed_pages{};
#ifdef _WIN32
const std::array<u8, sizeof(T)> default_val{};
#endif
}; };
} // namespace Common } // namespace Common
+4 -3
View File
@@ -109,7 +109,8 @@ VirtualFile RealVfsFilesystem::OpenFileFromEntry(std::string_view path_, std::op
auto reference = std::make_unique<FileReference>(); auto reference = std::make_unique<FileReference>();
this->InsertReferenceIntoListLocked(*reference); this->InsertReferenceIntoListLocked(*reference);
auto file = std::make_shared<RealVfsFile>(*this, std::move(reference), path, perms, size, std::move(parent_path)); auto file = std::shared_ptr<RealVfsFile>(
new RealVfsFile(*this, std::move(reference), path, perms, size, std::move(parent_path)));
cache[path] = file; cache[path] = file;
return file; return file;
@@ -176,7 +177,7 @@ bool RealVfsFilesystem::DeleteFile(std::string_view path_) {
VirtualDir RealVfsFilesystem::OpenDirectory(std::string_view path_, OpenMode perms) { VirtualDir RealVfsFilesystem::OpenDirectory(std::string_view path_, OpenMode perms) {
const auto path = FS::SanitizePath(path_, FS::DirectorySeparator::PlatformDefault); const auto path = FS::SanitizePath(path_, FS::DirectorySeparator::PlatformDefault);
return std::make_shared<RealVfsDirectory>(*this, path, perms); return std::shared_ptr<RealVfsDirectory>(new RealVfsDirectory(*this, path, perms));
} }
VirtualDir RealVfsFilesystem::CreateDirectory(std::string_view path_, OpenMode perms) { VirtualDir RealVfsFilesystem::CreateDirectory(std::string_view path_, OpenMode perms) {
@@ -184,7 +185,7 @@ VirtualDir RealVfsFilesystem::CreateDirectory(std::string_view path_, OpenMode p
if (!FS::CreateDirs(path)) { if (!FS::CreateDirs(path)) {
return nullptr; return nullptr;
} }
return std::make_shared<RealVfsDirectory>(*this, path, perms); return std::shared_ptr<RealVfsDirectory>(new RealVfsDirectory(*this, path, perms));
} }
VirtualDir RealVfsFilesystem::CopyDirectory(std::string_view old_path_, VirtualDir RealVfsFilesystem::CopyDirectory(std::string_view old_path_,
+6 -5
View File
@@ -82,9 +82,6 @@ class RealVfsFile : public VfsFile {
friend class RealVfsFilesystem; friend class RealVfsFilesystem;
public: public:
RealVfsFile(RealVfsFilesystem& base, std::unique_ptr<FileReference> reference,
const std::string& path, OpenMode perms = OpenMode::Read,
std::optional<u64> size = {}, std::optional<std::string> parent_path = {});
~RealVfsFile() override; ~RealVfsFile() override;
std::string GetName() const override; std::string GetName() const override;
@@ -98,6 +95,9 @@ public:
bool Rename(std::string_view name) override; bool Rename(std::string_view name) override;
private: private:
RealVfsFile(RealVfsFilesystem& base, std::unique_ptr<FileReference> reference,
const std::string& path, OpenMode perms = OpenMode::Read,
std::optional<u64> size = {}, std::optional<std::string> parent_path = {});
RealVfsFilesystem& base; RealVfsFilesystem& base;
std::unique_ptr<FileReference> reference; std::unique_ptr<FileReference> reference;
@@ -113,8 +113,6 @@ class RealVfsDirectory : public VfsDirectory {
friend class RealVfsFilesystem; friend class RealVfsFilesystem;
public: public:
RealVfsDirectory(RealVfsFilesystem& base, const std::string& path,
OpenMode perms = OpenMode::Read);
~RealVfsDirectory() override; ~RealVfsDirectory() override;
VirtualFile GetFileRelative(std::string_view relative_path) const override; VirtualFile GetFileRelative(std::string_view relative_path) const override;
@@ -140,6 +138,9 @@ public:
std::map<std::string, VfsEntryType, std::less<>> GetEntries() const override; std::map<std::string, VfsEntryType, std::less<>> GetEntries() const override;
private: private:
RealVfsDirectory(RealVfsFilesystem& base, const std::string& path,
OpenMode perms = OpenMode::Read);
template <typename T, typename R> template <typename T, typename R>
std::vector<std::shared_ptr<R>> IterateEntries() const; std::vector<std::shared_ptr<R>> IterateEntries() const;
+1 -1
View File
@@ -517,7 +517,7 @@ void WindowSystem::UpdateAppletStateLocked(Applet* applet, bool is_foreground, b
// Layer ordering. Composition sorts back-to-front. Now with enums for calrity. // Layer ordering. Composition sorts back-to-front. Now with enums for calrity.
s32 z_index = Background; s32 z_index = Background;
if (is_overlay) { if (is_overlay) {
z_index = this->IsOverlayOpenLocked(*applet) ? Overlay : Background; z_index = Overlay;
} else if (inherited_foreground) { } else if (inherited_foreground) {
z_index = is_obscured ? Foreground : ForegroundVisible; z_index = is_obscured ? Foreground : ForegroundVisible;
} }
@@ -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
@@ -24,7 +24,7 @@ IReceiverService::~IReceiverService() = default;
Result IReceiverService::OpenReceiver(Out<SharedPointer<IReceiver>> out_receiver) { Result IReceiverService::OpenReceiver(Out<SharedPointer<IReceiver>> out_receiver) {
LOG_DEBUG(Service_PSC, "called"); LOG_DEBUG(Service_PSC, "called");
*out_receiver = std::make_shared<IReceiver>(system); *out_receiver = std::shared_ptr<IReceiver>(new IReceiver(system));
R_SUCCEED(); R_SUCCEED();
} }
+10 -13
View File
@@ -45,19 +45,6 @@ NPad::NPad(Core::HID::HIDCore& hid_core_, KernelHelpers::ServiceContext& service
AbstractPad{hid_core_.kernel}, AbstractPad{hid_core_.kernel},
}} }}
{ {
for (std::size_t aruid_index = 0; aruid_index < AruidIndexMax; ++aruid_index) {
for (std::size_t i = 0; i < controller_data[aruid_index].size(); ++i) {
auto& controller = controller_data[aruid_index][i];
controller.device = hid_core.GetEmulatedControllerByIndex(i);
Core::HID::ControllerUpdateCallback engine_callback{
.on_change = [this, i, kernel = &hid_core.kernel](Core::HID::ControllerTriggerType type) {
ControllerUpdate(*kernel, type, i);
},
.is_npad_service = true,
};
controller.callback_key = controller.device->SetCallback(engine_callback);
}
}
for (std::size_t i = 0; i < abstracted_pads.size(); ++i) { for (std::size_t i = 0; i < abstracted_pads.size(); ++i) {
abstracted_pads[i].SetNpadId(IndexToNpadIdType(i)); abstracted_pads[i].SetNpadId(IndexToNpadIdType(i));
} }
@@ -106,6 +93,16 @@ Result NPad::Activate(u64 aruid) {
for (std::size_t i = 0; i < controller_data[aruid_index].size(); ++i) { for (std::size_t i = 0; i < controller_data[aruid_index].size(); ++i) {
auto& controller = controller_data[aruid_index][i]; auto& controller = controller_data[aruid_index][i];
controller.shared_memory = &data->shared_memory_format->npad.npad_entry[i].internal_state; controller.shared_memory = &data->shared_memory_format->npad.npad_entry[i].internal_state;
controller.device = hid_core.GetEmulatedControllerByIndex(i);
if (!controller.callback_key) {
Core::HID::ControllerUpdateCallback engine_callback{
.on_change = [this, i](Core::HID::ControllerTriggerType type) {
ControllerUpdate(hid_core.kernel, type, i);
},
.is_npad_service = true,
};
controller.callback_key = controller.device->SetCallback(engine_callback);
}
} }
// Prefill controller buffers // Prefill controller buffers
+8 -15
View File
@@ -121,7 +121,7 @@ void BufferCache<P>::UnmapGPUMemory(size_t as_id, GPUVAddr gpu_addr, size_t size
template <class P> template <class P>
void BufferCache<P>::WriteMemory(DAddr device_addr, u64 size) { void BufferCache<P>::WriteMemory(DAddr device_addr, u64 size) {
if (IsRegionGpuModified(device_addr, size)) { if (memory_tracker.IsRegionGpuModified(device_addr, size)) {
ClearDownload(device_addr, size); ClearDownload(device_addr, size);
gpu_modified_ranges.Subtract(device_addr, size); gpu_modified_ranges.Subtract(device_addr, size);
} }
@@ -311,8 +311,11 @@ std::pair<typename P::Buffer*, u32> BufferCache<P>::ObtainCPUBuffer(
MarkWrittenBuffer(buffer_id, device_addr, size); MarkWrittenBuffer(buffer_id, device_addr, size);
break; break;
case ObtainBufferOperation::DiscardWrite: { case ObtainBufferOperation::DiscardWrite: {
ClearDownload(device_addr, size); const DAddr device_addr_start = Common::AlignDown(device_addr, 64);
gpu_modified_ranges.Subtract(device_addr, size); const DAddr device_addr_end = Common::AlignUp(device_addr + size, 64);
const size_t new_size = device_addr_end - device_addr_start;
ClearDownload(device_addr_start, new_size);
gpu_modified_ranges.Subtract(device_addr_start, new_size);
break; break;
} }
default: default:
@@ -1739,24 +1742,14 @@ bool BufferCache<P>::SynchronizeBuffer(Buffer& buffer, DAddr device_addr, u32 si
u64 total_size_bytes = 0; u64 total_size_bytes = 0;
u64 largest_copy = 0; u64 largest_copy = 0;
const DAddr buffer_start = buffer.cpu_addr_cached; const DAddr buffer_start = buffer.cpu_addr_cached;
const auto add_upload = [&](DAddr start, DAddr end) { memory_tracker.ForEachUploadRange(device_addr, size, [&](u64 device_addr_out, u64 range_size) {
if (start == end) return;
const u64 range_size = end - start;
upload_copies.push_back(BufferCopy{ upload_copies.push_back(BufferCopy{
.src_offset = total_size_bytes, .src_offset = total_size_bytes,
.dst_offset = start - buffer_start, .dst_offset = device_addr_out - buffer_start,
.size = range_size, .size = range_size,
}); });
total_size_bytes += range_size; total_size_bytes += range_size;
largest_copy = (std::max)(largest_copy, range_size); largest_copy = (std::max)(largest_copy, range_size);
};
memory_tracker.ForEachUploadRange(device_addr, size, [&](u64 device_addr_out, u64 range_size) {
DAddr upload_start = device_addr_out;
gpu_modified_ranges.ForEachInRange(device_addr_out, range_size, [&](DAddr gpu_start, DAddr gpu_end) {
add_upload(upload_start, gpu_start);
upload_start = gpu_end;
});
add_upload(upload_start, device_addr_out + range_size);
}); });
if (total_size_bytes == 0) { if (total_size_bytes == 0) {
return true; return true;
+5 -10
View File
@@ -16,7 +16,7 @@
namespace Tegra { namespace Tegra {
constexpr u32 MacroRegistersStart = 0xE00; constexpr u32 MacroRegistersStart = 0xE00;
constexpr u32 ComputeInline = 0x6D; [[maybe_unused]] constexpr u32 ComputeInline = 0x6D;
DmaPusher::DmaPusher(Core::System& system_, MemoryManager& memory_manager_, Control::ChannelState& channel_state_) DmaPusher::DmaPusher(Core::System& system_, MemoryManager& memory_manager_, Control::ChannelState& channel_state_)
: system{system_} : system{system_}
@@ -73,16 +73,11 @@ bool DmaPusher::Step() {
synced = false; synced = false;
} }
if (header.size > 0 && dma_state.method >= MacroRegistersStart && subchannels[dma_state.subchannel]) {
subchannels[dma_state.subchannel]->current_dirty = memory_manager.IsMemoryDirty(dma_state.dma_get, header.size * sizeof(u32));
}
if (header.size > 0) { if (header.size > 0) {
if (subchannels[dma_state.subchannel] && dma_state.method_count) {
const auto engine = subchannel_type[dma_state.subchannel];
const bool kepler_payload = engine == Engines::EngineTypes::KeplerCompute && dma_state.method == ComputeInline && dma_state.non_incrementing;
const bool macro_payload = engine == Engines::EngineTypes::Maxwell3D && dma_state.method >= MacroRegistersStart;
if (kepler_payload || macro_payload) {
const size_t words = std::min<size_t>(dma_state.method_count, header.size);
subchannels[dma_state.subchannel]->current_dirty = memory_manager.IsMemoryDirty(dma_state.dma_get, words * sizeof(u32));
}
}
const bool use_safe = Settings::IsDMALevelDefault() ? Settings::IsGPULevelHigh() : Settings::IsDMALevelSafe(); const bool use_safe = Settings::IsDMALevelDefault() ? Settings::IsGPULevelHigh() : Settings::IsDMALevelSafe();
if (use_safe) { if (use_safe) {
Tegra::Memory::GpuGuestMemory<Tegra::CommandHeader, Tegra::Memory::GuestMemoryFlags::SafeRead>headers(memory_manager, dma_state.dma_get, header.size, &command_headers); Tegra::Memory::GpuGuestMemory<Tegra::CommandHeader, Tegra::Memory::GuestMemoryFlags::SafeRead>headers(memory_manager, dma_state.dma_get, header.size, &command_headers);
+4 -11
View File
@@ -49,16 +49,13 @@ void KeplerCompute::CallMethod(Core::System& system, u32 method, u32 method_argu
case KEPLER_COMPUTE_REG_INDEX(exec_upload): { case KEPLER_COMPUTE_REG_INDEX(exec_upload): {
UploadInfo info{.upload_address = upload_address, UploadInfo info{.upload_address = upload_address,
.exec_address = upload_state.ExecTargetAddress(), .exec_address = upload_state.ExecTargetAddress(),
.copy_size = upload_state.GetUploadSize(), .copy_size = upload_state.GetUploadSize()};
.was_dirty = upload_dirty};
uploads.push_back(info); uploads.push_back(info);
upload_state.ProcessExec(regs.exec_upload.linear != 0); upload_state.ProcessExec(regs.exec_upload.linear != 0);
break; break;
} }
case KEPLER_COMPUTE_REG_INDEX(data_upload): { case KEPLER_COMPUTE_REG_INDEX(data_upload): {
upload_address = current_dma_segment; upload_address = current_dma_segment;
upload_dirty = current_dirty;
current_dirty = false;
upload_state.ProcessData(method_argument, is_last_call); upload_state.ProcessData(method_argument, is_last_call);
break; break;
} }
@@ -67,11 +64,9 @@ void KeplerCompute::CallMethod(Core::System& system, u32 method, u32 method_argu
for (auto& data : uploads) { for (auto& data : uploads) {
const GPUVAddr offset = data.exec_address - launch_desc_loc; const GPUVAddr offset = data.exec_address - launch_desc_loc;
if (offset / sizeof(u32) == LAUNCH_REG_INDEX(grid_dim_x)) { if (offset / sizeof(u32) == LAUNCH_REG_INDEX(grid_dim_x) &&
const bool source_dirty = memory_manager.IsMemoryDirty(data.upload_address, data.copy_size); memory_manager.IsMemoryDirty(data.upload_address, data.copy_size)) {
if (data.was_dirty || source_dirty) { indirect_compute = {data.upload_address};
indirect_compute = {data.upload_address};
}
} }
} }
uploads.clear(); uploads.clear();
@@ -88,8 +83,6 @@ void KeplerCompute::CallMultiMethod(Core::System& system, u32 method, const u32*
switch (method) { switch (method) {
case KEPLER_COMPUTE_REG_INDEX(data_upload): case KEPLER_COMPUTE_REG_INDEX(data_upload):
upload_address = current_dma_segment; upload_address = current_dma_segment;
upload_dirty = current_dirty;
current_dirty = false;
upload_state.ProcessData(base_start, amount); upload_state.ProcessData(base_start, amount);
return; return;
default: default:
-2
View File
@@ -226,13 +226,11 @@ private:
VideoCore::RasterizerInterface* rasterizer = nullptr; VideoCore::RasterizerInterface* rasterizer = nullptr;
Upload::State upload_state; Upload::State upload_state;
GPUVAddr upload_address; GPUVAddr upload_address;
bool upload_dirty{};
struct UploadInfo { struct UploadInfo {
GPUVAddr upload_address; GPUVAddr upload_address;
GPUVAddr exec_address; GPUVAddr exec_address;
u32 copy_size; u32 copy_size;
bool was_dirty;
}; };
std::vector<UploadInfo> uploads; std::vector<UploadInfo> uploads;
std::optional<GPUVAddr> indirect_compute{}; std::optional<GPUVAddr> indirect_compute{};
@@ -94,13 +94,13 @@ void Layer::ConfigureDraw(const Device& device, PresentPushConstants* out_push_c
const u32 scaled_width = texture_info ? texture_info->scaled_width : texture_width; const u32 scaled_width = texture_info ? texture_info->scaled_width : texture_width;
const u32 scaled_height = texture_info ? texture_info->scaled_height : texture_height; const u32 scaled_height = texture_info ? texture_info->scaled_height : texture_height;
const bool use_accelerated = texture_info.has_value(); const bool use_accelerated = texture_info.has_value();
const bool is_applet =
(framebuffer.layer_stack_mask & Service::Nvnflinger::LayerStackBit(
Service::Nvnflinger::LayerStackId::Recording)) == 0;
RefreshResources(device, framebuffer); RefreshResources(device, framebuffer);
SetAntiAliasPass(device); SetAntiAliasPass(device);
#ifdef HAS_RESHADE #ifdef HAS_RESHADE
const bool is_applet =
(framebuffer.layer_stack_mask & Service::Nvnflinger::LayerStackBit(
Service::Nvnflinger::LayerStackId::Recording)) == 0;
SetPostProcessPass(device, is_applet); SetPostProcessPass(device, is_applet);
#endif #endif
@@ -141,11 +141,8 @@ void Layer::ConfigureDraw(const Device& device, PresentPushConstants* out_push_c
source_image_view = fsr->Draw(device, scheduler, image_index, source_image, source_image_view, render_extent, crop_rect); source_image_view = fsr->Draw(device, scheduler, image_index, source_image, source_image_view, render_extent, crop_rect);
crop_rect = {0, 0, 1, 1}; crop_rect = {0, 0, 1, 1};
} else if (auto* sgsr = std::get_if<SGSR>(&sr_filter)) { } else if (auto* sgsr = std::get_if<SGSR>(&sr_filter)) {
if (!is_applet) { source_image_view = sgsr->Draw(device, scheduler, image_index, source_image, source_image_view, render_extent, crop_rect);
source_image_view = sgsr->Draw(device, scheduler, image_index, source_image, crop_rect = {0, 0, 1, 1};
source_image_view, render_extent, crop_rect);
crop_rect = {0, 0, 1, 1};
}
} }
SetMatrixData(device, *out_push_constants, layout); SetMatrixData(device, *out_push_constants, layout);