Compare commits
11 commits
9353cf51e6
...
5b3b127f83
Author | SHA1 | Date | |
---|---|---|---|
5b3b127f83 | |||
0b4e8df6bd | |||
68c09d006e | |||
ba20e5c2f5 | |||
020ad29a8c | |||
4982dcfaa5 | |||
677148bdca | |||
f088f028f3 | |||
19eb8272b1 | |||
86ddb51a87 | |||
10aca2f90c |
17 changed files with 169 additions and 121 deletions
|
@ -310,6 +310,7 @@ 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)
|
||||||
|
find_package(oaknut 2.0.1)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if (YUZU_ROOM)
|
if (YUZU_ROOM)
|
||||||
|
|
|
@ -1,11 +0,0 @@
|
||||||
# SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
|
||||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
|
|
||||||
include(FindPackageHandleStandardArgs)
|
|
||||||
|
|
||||||
find_package(PkgConfig QUIET)
|
|
||||||
pkg_search_module(sirit QUIET IMPORTED_TARGET sirit)
|
|
||||||
find_package_handle_standard_args(sirit
|
|
||||||
REQUIRED_VARS sirit_LINK_LIBRARIES
|
|
||||||
VERSION_VAR sirit_VERSION
|
|
||||||
)
|
|
2
externals/cpmfile.json
vendored
2
externals/cpmfile.json
vendored
|
@ -10,7 +10,7 @@
|
||||||
"repo": "eden-emulator/sirit",
|
"repo": "eden-emulator/sirit",
|
||||||
"sha": "db1f1e8ab5",
|
"sha": "db1f1e8ab5",
|
||||||
"hash": "73eb3a042848c63a10656545797e85f40d142009dfb7827384548a385e1e28e1ac72f42b25924ce530d58275f8638554281e884d72f9c7aaf4ed08690a414b05",
|
"hash": "73eb3a042848c63a10656545797e85f40d142009dfb7827384548a385e1e28e1ac72f42b25924ce530d58275f8638554281e884d72f9c7aaf4ed08690a414b05",
|
||||||
"find_args": "MODULE",
|
"find_args": "CONFIG",
|
||||||
"options": [
|
"options": [
|
||||||
"SIRIT_USE_SYSTEM_SPIRV_HEADERS ON"
|
"SIRIT_USE_SYSTEM_SPIRV_HEADERS ON"
|
||||||
]
|
]
|
||||||
|
|
|
@ -27,6 +27,8 @@ SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
<uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" android:required="false" />
|
<uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" android:required="false" />
|
||||||
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
|
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
|
||||||
<uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
|
<uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
|
||||||
|
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||||
|
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_SPECIAL_USE" />
|
||||||
<uses-permission android:name="android.permission.BLUETOOTH" android:maxSdkVersion="30" />
|
<uses-permission android:name="android.permission.BLUETOOTH" android:maxSdkVersion="30" />
|
||||||
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" android:maxSdkVersion="30" />
|
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" android:maxSdkVersion="30" />
|
||||||
|
|
||||||
|
@ -93,6 +95,10 @@ SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
<meta-data android:name="android.nfc.action.TECH_DISCOVERED" android:resource="@xml/nfc_tech_filter" />
|
<meta-data android:name="android.nfc.action.TECH_DISCOVERED" android:resource="@xml/nfc_tech_filter" />
|
||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
|
<service android:name="org.yuzu.yuzu_emu.utils.ForegroundService" android:foregroundServiceType="specialUse">
|
||||||
|
<property android:name="android.app.PROPERTY_SPECIAL_USE_FGS_SUBTYPE" android:value="Keep emulation running in background"/>
|
||||||
|
</service>
|
||||||
|
|
||||||
<provider
|
<provider
|
||||||
android:name=".features.DocumentProvider"
|
android:name=".features.DocumentProvider"
|
||||||
android:authorities="${applicationId}.user"
|
android:authorities="${applicationId}.user"
|
||||||
|
|
|
@ -22,6 +22,17 @@ fun Context.getPublicFilesDir(): File = getExternalFilesDir(null) ?: filesDir
|
||||||
|
|
||||||
class YuzuApplication : Application() {
|
class YuzuApplication : Application() {
|
||||||
private fun createNotificationChannels() {
|
private fun createNotificationChannels() {
|
||||||
|
val name: CharSequence = getString(R.string.app_notification_channel_name)
|
||||||
|
val description = getString(R.string.app_notification_channel_description)
|
||||||
|
val foregroundService = NotificationChannel(
|
||||||
|
getString(R.string.app_notification_channel_id),
|
||||||
|
name,
|
||||||
|
NotificationManager.IMPORTANCE_DEFAULT
|
||||||
|
)
|
||||||
|
foregroundService.description = description
|
||||||
|
foregroundService.setSound(null, null)
|
||||||
|
foregroundService.vibrationPattern = null
|
||||||
|
|
||||||
val noticeChannel = NotificationChannel(
|
val noticeChannel = NotificationChannel(
|
||||||
getString(R.string.notice_notification_channel_id),
|
getString(R.string.notice_notification_channel_id),
|
||||||
getString(R.string.notice_notification_channel_name),
|
getString(R.string.notice_notification_channel_name),
|
||||||
|
@ -34,6 +45,7 @@ class YuzuApplication : Application() {
|
||||||
// or other notification behaviors after this
|
// or other notification behaviors after this
|
||||||
val notificationManager = getSystemService(NotificationManager::class.java)
|
val notificationManager = getSystemService(NotificationManager::class.java)
|
||||||
notificationManager.createNotificationChannel(noticeChannel)
|
notificationManager.createNotificationChannel(noticeChannel)
|
||||||
|
notificationManager.createNotificationChannel(foregroundService)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCreate() {
|
override fun onCreate() {
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
package org.yuzu.yuzu_emu.activities
|
package org.yuzu.yuzu_emu.activities
|
||||||
|
|
||||||
import android.annotation.SuppressLint
|
import android.annotation.SuppressLint
|
||||||
|
import android.app.Activity
|
||||||
import android.app.PendingIntent
|
import android.app.PendingIntent
|
||||||
import android.app.PictureInPictureParams
|
import android.app.PictureInPictureParams
|
||||||
import android.app.RemoteAction
|
import android.app.RemoteAction
|
||||||
|
@ -59,6 +60,7 @@ import org.yuzu.yuzu_emu.utils.ParamPackage
|
||||||
import org.yuzu.yuzu_emu.utils.ThemeHelper
|
import org.yuzu.yuzu_emu.utils.ThemeHelper
|
||||||
import java.text.NumberFormat
|
import java.text.NumberFormat
|
||||||
import kotlin.math.roundToInt
|
import kotlin.math.roundToInt
|
||||||
|
import org.yuzu.yuzu_emu.utils.ForegroundService
|
||||||
|
|
||||||
class EmulationActivity : AppCompatActivity(), SensorEventListener {
|
class EmulationActivity : AppCompatActivity(), SensorEventListener {
|
||||||
private lateinit var binding: ActivityEmulationBinding
|
private lateinit var binding: ActivityEmulationBinding
|
||||||
|
@ -78,6 +80,8 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener {
|
||||||
|
|
||||||
private val emulationViewModel: EmulationViewModel by viewModels()
|
private val emulationViewModel: EmulationViewModel by viewModels()
|
||||||
|
|
||||||
|
private var foregroundService: Intent? = null
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
Log.gameLaunched = true
|
Log.gameLaunched = true
|
||||||
ThemeHelper.setTheme(this)
|
ThemeHelper.setTheme(this)
|
||||||
|
@ -128,6 +132,9 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener {
|
||||||
nfcReader = NfcReader(this)
|
nfcReader = NfcReader(this)
|
||||||
nfcReader.initialize()
|
nfcReader.initialize()
|
||||||
|
|
||||||
|
foregroundService = Intent(this, ForegroundService::class.java)
|
||||||
|
startForegroundService(foregroundService)
|
||||||
|
|
||||||
val preferences = PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext)
|
val preferences = PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext)
|
||||||
if (!preferences.getBoolean(Settings.PREF_MEMORY_WARNING_SHOWN, false)) {
|
if (!preferences.getBoolean(Settings.PREF_MEMORY_WARNING_SHOWN, false)) {
|
||||||
if (MemoryUtil.isLessThan(MemoryUtil.REQUIRED_MEMORY, MemoryUtil.totalMemory)) {
|
if (MemoryUtil.isLessThan(MemoryUtil.REQUIRED_MEMORY, MemoryUtil.totalMemory)) {
|
||||||
|
@ -189,6 +196,12 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener {
|
||||||
stopMotionSensorListener()
|
stopMotionSensorListener()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onDestroy() {
|
||||||
|
super.onDestroy()
|
||||||
|
stopForegroundService(this)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
override fun onUserLeaveHint() {
|
override fun onUserLeaveHint() {
|
||||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S) {
|
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S) {
|
||||||
if (BooleanSetting.PICTURE_IN_PICTURE.getBoolean() && !isInPictureInPictureMode) {
|
if (BooleanSetting.PICTURE_IN_PICTURE.getBoolean() && !isInPictureInPictureMode) {
|
||||||
|
@ -511,6 +524,12 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener {
|
||||||
companion object {
|
companion object {
|
||||||
const val EXTRA_SELECTED_GAME = "SelectedGame"
|
const val EXTRA_SELECTED_GAME = "SelectedGame"
|
||||||
|
|
||||||
|
fun stopForegroundService(activity: Activity) {
|
||||||
|
val startIntent = Intent(activity, ForegroundService::class.java)
|
||||||
|
startIntent.action = ForegroundService.ACTION_STOP
|
||||||
|
activity.startForegroundService(startIntent)
|
||||||
|
}
|
||||||
|
|
||||||
fun launch(activity: AppCompatActivity, game: Game) {
|
fun launch(activity: AppCompatActivity, game: Game) {
|
||||||
val launcher = Intent(activity, EmulationActivity::class.java)
|
val launcher = Intent(activity, EmulationActivity::class.java)
|
||||||
launcher.putExtra(EXTRA_SELECTED_GAME, game)
|
launcher.putExtra(EXTRA_SELECTED_GAME, game)
|
||||||
|
|
|
@ -48,6 +48,7 @@ import java.io.BufferedOutputStream
|
||||||
import java.util.zip.ZipEntry
|
import java.util.zip.ZipEntry
|
||||||
import java.util.zip.ZipInputStream
|
import java.util.zip.ZipInputStream
|
||||||
import androidx.core.content.edit
|
import androidx.core.content.edit
|
||||||
|
import org.yuzu.yuzu_emu.activities.EmulationActivity
|
||||||
import kotlin.text.compareTo
|
import kotlin.text.compareTo
|
||||||
|
|
||||||
class MainActivity : AppCompatActivity(), ThemeProvider {
|
class MainActivity : AppCompatActivity(), ThemeProvider {
|
||||||
|
@ -188,6 +189,9 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
|
||||||
if (it) checkKeys()
|
if (it) checkKeys()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Dismiss previous notifications (should not happen unless a crash occurred)
|
||||||
|
EmulationActivity.stopForegroundService(this)
|
||||||
|
|
||||||
setInsets()
|
setInsets()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -293,6 +297,11 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
|
||||||
themeId = resId
|
themeId = resId
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onDestroy() {
|
||||||
|
EmulationActivity.stopForegroundService(this)
|
||||||
|
super.onDestroy()
|
||||||
|
}
|
||||||
|
|
||||||
val getGamesDirectory =
|
val getGamesDirectory =
|
||||||
registerForActivityResult(ActivityResultContracts.OpenDocumentTree()) { result ->
|
registerForActivityResult(ActivityResultContracts.OpenDocumentTree()) { result ->
|
||||||
if (result != null) {
|
if (result != null) {
|
||||||
|
|
|
@ -0,0 +1,79 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
// Copyright 2023 Citra Emulator Project
|
||||||
|
// Licensed under GPLv2 or any later version
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
package org.yuzu.yuzu_emu.utils
|
||||||
|
|
||||||
|
import android.app.PendingIntent
|
||||||
|
import android.app.Service
|
||||||
|
import android.content.Intent
|
||||||
|
import android.os.IBinder
|
||||||
|
import androidx.core.app.NotificationCompat
|
||||||
|
import androidx.core.app.NotificationManagerCompat
|
||||||
|
import org.yuzu.yuzu_emu.R
|
||||||
|
import org.yuzu.yuzu_emu.activities.EmulationActivity
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A service that shows a permanent notification in the background to avoid the app getting
|
||||||
|
* cleared from memory by the system.
|
||||||
|
*/
|
||||||
|
class ForegroundService : Service() {
|
||||||
|
companion object {
|
||||||
|
const val EMULATION_RUNNING_NOTIFICATION = 0x1000
|
||||||
|
|
||||||
|
const val ACTION_STOP = "stop"
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun showRunningNotification() {
|
||||||
|
// Intent is used to resume emulation if the notification is clicked
|
||||||
|
val contentIntent = PendingIntent.getActivity(
|
||||||
|
this,
|
||||||
|
0,
|
||||||
|
Intent(this, EmulationActivity::class.java),
|
||||||
|
PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT
|
||||||
|
)
|
||||||
|
val builder =
|
||||||
|
NotificationCompat.Builder(this, getString(R.string.app_notification_channel_id))
|
||||||
|
.setSmallIcon(R.drawable.ic_stat_notification_logo)
|
||||||
|
.setContentTitle(getString(R.string.app_name))
|
||||||
|
.setContentText(getString(R.string.app_notification_running))
|
||||||
|
.setPriority(NotificationCompat.PRIORITY_DEFAULT)
|
||||||
|
.setOngoing(true)
|
||||||
|
.setVibrate(null)
|
||||||
|
.setSound(null)
|
||||||
|
.setContentIntent(contentIntent)
|
||||||
|
startForeground(EMULATION_RUNNING_NOTIFICATION, builder.build())
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onBind(intent: Intent): IBinder? {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreate() {
|
||||||
|
showRunningNotification()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
|
||||||
|
if (intent?.action == ACTION_STOP) {
|
||||||
|
try {
|
||||||
|
NotificationManagerCompat.from(this).cancel(EMULATION_RUNNING_NOTIFICATION)
|
||||||
|
stopForeground(STOP_FOREGROUND_REMOVE)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.error("Failed to stop foreground service")
|
||||||
|
}
|
||||||
|
stopSelfResult(startId)
|
||||||
|
return START_NOT_STICKY
|
||||||
|
}
|
||||||
|
|
||||||
|
if (intent != null) {
|
||||||
|
showRunningNotification()
|
||||||
|
}
|
||||||
|
return START_STICKY
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDestroy() =
|
||||||
|
NotificationManagerCompat.from(this).cancel(EMULATION_RUNNING_NOTIFICATION)
|
||||||
|
}
|
|
@ -8,6 +8,11 @@
|
||||||
<string name="notice_notification_channel_id" translatable="false">noticesAndErrors</string>
|
<string name="notice_notification_channel_id" translatable="false">noticesAndErrors</string>
|
||||||
<string name="notice_notification_channel_description">Shows notifications when something goes wrong.</string>
|
<string name="notice_notification_channel_description">Shows notifications when something goes wrong.</string>
|
||||||
<string name="notification_permission_not_granted">Notification permission not granted!</string>
|
<string name="notification_permission_not_granted">Notification permission not granted!</string>
|
||||||
|
<string name="app_notification_channel_name" translatable="false">Eden</string>
|
||||||
|
<string name="app_notification_channel_id" translatable="false">Eden</string>
|
||||||
|
<string name="app_notification_channel_description">Eden Switch emulator notifications</string>
|
||||||
|
<string name="app_notification_running">Eden is Running</string>
|
||||||
|
|
||||||
|
|
||||||
<!-- Stats Overlay settings -->
|
<!-- Stats Overlay settings -->
|
||||||
<string name="enhanced_fps_suffix">(Enhanced)</string>
|
<string name="enhanced_fps_suffix">(Enhanced)</string>
|
||||||
|
|
|
@ -32,7 +32,6 @@ add_library(
|
||||||
atomic_ops.h
|
atomic_ops.h
|
||||||
bit_cast.h
|
bit_cast.h
|
||||||
bit_field.h
|
bit_field.h
|
||||||
bit_set.h
|
|
||||||
bit_util.h
|
bit_util.h
|
||||||
bounded_threadsafe_queue.h
|
bounded_threadsafe_queue.h
|
||||||
cityhash.cpp
|
cityhash.cpp
|
||||||
|
|
|
@ -1,86 +0,0 @@
|
||||||
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
|
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <array>
|
|
||||||
#include <bit>
|
|
||||||
|
|
||||||
#include "common/alignment.h"
|
|
||||||
#include "common/bit_util.h"
|
|
||||||
#include "common/common_types.h"
|
|
||||||
|
|
||||||
namespace Common {
|
|
||||||
|
|
||||||
namespace impl {
|
|
||||||
|
|
||||||
template <typename Storage, size_t N>
|
|
||||||
class BitSet {
|
|
||||||
|
|
||||||
public:
|
|
||||||
constexpr BitSet() = default;
|
|
||||||
|
|
||||||
constexpr void SetBit(size_t i) {
|
|
||||||
this->words[i / FlagsPerWord] |= GetBitMask(i % FlagsPerWord);
|
|
||||||
}
|
|
||||||
|
|
||||||
constexpr void ClearBit(size_t i) {
|
|
||||||
this->words[i / FlagsPerWord] &= ~GetBitMask(i % FlagsPerWord);
|
|
||||||
}
|
|
||||||
|
|
||||||
constexpr size_t CountLeadingZero() const {
|
|
||||||
for (size_t i = 0; i < NumWords; i++) {
|
|
||||||
if (this->words[i]) {
|
|
||||||
return FlagsPerWord * i + CountLeadingZeroImpl(this->words[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return FlagsPerWord * NumWords;
|
|
||||||
}
|
|
||||||
|
|
||||||
constexpr size_t GetNextSet(size_t n) const {
|
|
||||||
for (size_t i = (n + 1) / FlagsPerWord; i < NumWords; i++) {
|
|
||||||
Storage word = this->words[i];
|
|
||||||
if (!IsAligned(n + 1, FlagsPerWord)) {
|
|
||||||
word &= GetBitMask(n % FlagsPerWord) - 1;
|
|
||||||
}
|
|
||||||
if (word) {
|
|
||||||
return FlagsPerWord * i + CountLeadingZeroImpl(word);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return FlagsPerWord * NumWords;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
static_assert(std::is_unsigned_v<Storage>);
|
|
||||||
static_assert(sizeof(Storage) <= sizeof(u64));
|
|
||||||
|
|
||||||
static constexpr size_t FlagsPerWord = BitSize<Storage>();
|
|
||||||
static constexpr size_t NumWords = AlignUp(N, FlagsPerWord) / FlagsPerWord;
|
|
||||||
|
|
||||||
static constexpr auto CountLeadingZeroImpl(Storage word) {
|
|
||||||
return std::countl_zero(static_cast<unsigned long long>(word)) -
|
|
||||||
(BitSize<unsigned long long>() - FlagsPerWord);
|
|
||||||
}
|
|
||||||
|
|
||||||
static constexpr Storage GetBitMask(size_t bit) {
|
|
||||||
return Storage(1) << (FlagsPerWord - 1 - bit);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::array<Storage, NumWords> words{};
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace impl
|
|
||||||
|
|
||||||
template <size_t N>
|
|
||||||
using BitSet8 = impl::BitSet<u8, N>;
|
|
||||||
|
|
||||||
template <size_t N>
|
|
||||||
using BitSet16 = impl::BitSet<u16, N>;
|
|
||||||
|
|
||||||
template <size_t N>
|
|
||||||
using BitSet32 = impl::BitSet<u32, N>;
|
|
||||||
|
|
||||||
template <size_t N>
|
|
||||||
using BitSet64 = impl::BitSet<u64, N>;
|
|
||||||
|
|
||||||
} // namespace Common
|
|
|
@ -1224,7 +1224,7 @@ if (HAS_NCE)
|
||||||
arm/nce/patcher.h
|
arm/nce/patcher.h
|
||||||
arm/nce/visitor_base.h
|
arm/nce/visitor_base.h
|
||||||
)
|
)
|
||||||
target_link_libraries(core PRIVATE merry::mcl merry::oaknut)
|
target_link_libraries(core PRIVATE merry::oaknut)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if (ARCHITECTURE_x86_64 OR ARCHITECTURE_arm64)
|
if (ARCHITECTURE_x86_64 OR ARCHITECTURE_arm64)
|
||||||
|
|
|
@ -27,11 +27,11 @@ template <>
|
||||||
struct std::hash<PatchCacheKey> {
|
struct std::hash<PatchCacheKey> {
|
||||||
size_t operator()(const PatchCacheKey& key) const {
|
size_t operator()(const PatchCacheKey& key) const {
|
||||||
// Simple XOR hash of first few bytes
|
// Simple XOR hash of first few bytes
|
||||||
size_t hash = 0;
|
size_t hash_ = 0;
|
||||||
for (size_t i = 0; i < key.module_id.size(); ++i) {
|
for (size_t i = 0; i < key.module_id.size(); ++i) {
|
||||||
hash ^= static_cast<size_t>(key.module_id[i]) << ((i % sizeof(size_t)) * 8);
|
hash_ ^= static_cast<size_t>(key.module_id[i]) << ((i % sizeof(size_t)) * 8);
|
||||||
}
|
}
|
||||||
return hash ^ std::hash<uintptr_t>{}(key.offset);
|
return hash_ ^ std::hash<uintptr_t>{}(key.offset);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2025 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
|
||||||
|
|
||||||
|
@ -5,10 +8,12 @@
|
||||||
|
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <bit>
|
#include <bit>
|
||||||
|
#include <bitset>
|
||||||
#include <concepts>
|
#include <concepts>
|
||||||
|
#include <cstddef>
|
||||||
|
|
||||||
|
#include "common/alignment.h"
|
||||||
#include "common/assert.h"
|
#include "common/assert.h"
|
||||||
#include "common/bit_set.h"
|
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
#include "common/concepts.h"
|
#include "common/concepts.h"
|
||||||
|
|
||||||
|
@ -159,7 +164,7 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_queues[priority].PushBack(core, member)) {
|
if (m_queues[priority].PushBack(core, member)) {
|
||||||
m_available_priorities[core].SetBit(priority);
|
m_available_priorities[core].set(std::size_t(priority));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -172,7 +177,7 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_queues[priority].PushFront(core, member)) {
|
if (m_queues[priority].PushFront(core, member)) {
|
||||||
m_available_priorities[core].SetBit(priority);
|
m_available_priorities[core].set(std::size_t(priority));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -185,14 +190,19 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_queues[priority].Remove(core, member)) {
|
if (m_queues[priority].Remove(core, member)) {
|
||||||
m_available_priorities[core].ClearBit(priority);
|
m_available_priorities[core].reset(std::size_t(priority));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr Member* GetFront(s32 core) const {
|
constexpr Member* GetFront(s32 core) const {
|
||||||
ASSERT(IsValidCore(core));
|
ASSERT(IsValidCore(core));
|
||||||
|
|
||||||
const s32 priority = static_cast<s32>(m_available_priorities[core].CountLeadingZero());
|
const s32 priority = s32([](auto const& e) {
|
||||||
|
for (size_t i = 0; i < e.size(); ++i)
|
||||||
|
if (e[i])
|
||||||
|
return i;
|
||||||
|
return e.size();
|
||||||
|
}(m_available_priorities[core]));
|
||||||
if (priority <= LowestPriority) {
|
if (priority <= LowestPriority) {
|
||||||
return m_queues[priority].GetFront(core);
|
return m_queues[priority].GetFront(core);
|
||||||
} else {
|
} else {
|
||||||
|
@ -211,16 +221,22 @@ public:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<size_t N>
|
||||||
|
constexpr size_t GetNextSet(std::bitset<N> const& bit, size_t n) const {
|
||||||
|
for (size_t i = n + 1; i < bit.size(); i++)
|
||||||
|
if (bit[i])
|
||||||
|
return i;
|
||||||
|
return bit.size();
|
||||||
|
}
|
||||||
|
|
||||||
constexpr Member* GetNext(s32 core, const Member* member) const {
|
constexpr Member* GetNext(s32 core, const Member* member) const {
|
||||||
ASSERT(IsValidCore(core));
|
ASSERT(IsValidCore(core));
|
||||||
|
|
||||||
Member* next = member->GetPriorityQueueEntry(core).GetNext();
|
Member* next = member->GetPriorityQueueEntry(core).GetNext();
|
||||||
if (next == nullptr) {
|
if (next == nullptr) {
|
||||||
const s32 priority = static_cast<s32>(
|
s32 priority = s32(GetNextSet(m_available_priorities[core], member->GetPriority()));
|
||||||
m_available_priorities[core].GetNextSet(member->GetPriority()));
|
if (priority <= LowestPriority)
|
||||||
if (priority <= LowestPriority) {
|
|
||||||
next = m_queues[priority].GetFront(core);
|
next = m_queues[priority].GetFront(core);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return next;
|
return next;
|
||||||
}
|
}
|
||||||
|
@ -250,7 +266,7 @@ public:
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::array<KPerCoreQueue, NumPriority> m_queues{};
|
std::array<KPerCoreQueue, NumPriority> m_queues{};
|
||||||
std::array<Common::BitSet64<NumPriority>, NumCores> m_available_priorities{};
|
std::array<std::bitset<NumPriority>, NumCores> m_available_priorities{};
|
||||||
};
|
};
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
|
@ -374,7 +374,7 @@ endif()
|
||||||
|
|
||||||
target_compile_options(dynarmic PRIVATE ${DYNARMIC_CXX_FLAGS})
|
target_compile_options(dynarmic PRIVATE ${DYNARMIC_CXX_FLAGS})
|
||||||
target_link_libraries(dynarmic
|
target_link_libraries(dynarmic
|
||||||
PRIVATE
|
PUBLIC
|
||||||
fmt::fmt
|
fmt::fmt
|
||||||
merry::mcl
|
merry::mcl
|
||||||
)
|
)
|
||||||
|
|
|
@ -792,6 +792,11 @@ void BufferCache<P>::BindHostGraphicsUniformBuffer(size_t stage, u32 index, u32
|
||||||
const u32 size = (std::min)(binding.size, (*channel_state->uniform_buffer_sizes)[stage][index]);
|
const u32 size = (std::min)(binding.size, (*channel_state->uniform_buffer_sizes)[stage][index]);
|
||||||
Buffer& buffer = slot_buffers[binding.buffer_id];
|
Buffer& buffer = slot_buffers[binding.buffer_id];
|
||||||
TouchBuffer(buffer, binding.buffer_id);
|
TouchBuffer(buffer, binding.buffer_id);
|
||||||
|
const bool sync_buffer = SynchronizeBuffer(buffer, device_addr, size);
|
||||||
|
if (sync_buffer) {
|
||||||
|
++channel_state->uniform_cache_hits[0];
|
||||||
|
}
|
||||||
|
++channel_state->uniform_cache_shots[0];
|
||||||
const bool use_fast_buffer = binding.buffer_id != NULL_BUFFER_ID &&
|
const bool use_fast_buffer = binding.buffer_id != NULL_BUFFER_ID &&
|
||||||
size <= channel_state->uniform_buffer_skip_cache_size &&
|
size <= channel_state->uniform_buffer_skip_cache_size &&
|
||||||
!memory_tracker.IsRegionGpuModified(device_addr, size);
|
!memory_tracker.IsRegionGpuModified(device_addr, size);
|
||||||
|
@ -822,12 +827,6 @@ void BufferCache<P>::BindHostGraphicsUniformBuffer(size_t stage, u32 index, u32
|
||||||
device_memory.ReadBlockUnsafe(device_addr, span.data(), size);
|
device_memory.ReadBlockUnsafe(device_addr, span.data(), size);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// Classic cached path
|
|
||||||
const bool sync_cached = SynchronizeBuffer(buffer, device_addr, size);
|
|
||||||
if (sync_cached) {
|
|
||||||
++channel_state->uniform_cache_hits[0];
|
|
||||||
}
|
|
||||||
++channel_state->uniform_cache_shots[0];
|
|
||||||
|
|
||||||
// Skip binding if it's not needed and if the bound buffer is not the fast version
|
// Skip binding if it's not needed and if the bound buffer is not the fast version
|
||||||
// This exists to avoid instances where the fast buffer is bound and a GPU write happens
|
// This exists to avoid instances where the fast buffer is bound and a GPU write happens
|
||||||
|
|
|
@ -31,7 +31,7 @@ struct DescriptorBank {
|
||||||
bool DescriptorBankInfo::IsSuperset(const DescriptorBankInfo& subset) const noexcept {
|
bool DescriptorBankInfo::IsSuperset(const DescriptorBankInfo& subset) const noexcept {
|
||||||
return uniform_buffers >= subset.uniform_buffers && storage_buffers >= subset.storage_buffers &&
|
return uniform_buffers >= subset.uniform_buffers && storage_buffers >= subset.storage_buffers &&
|
||||||
texture_buffers >= subset.texture_buffers && image_buffers >= subset.image_buffers &&
|
texture_buffers >= subset.texture_buffers && image_buffers >= subset.image_buffers &&
|
||||||
textures >= subset.textures && images >= subset.image_buffers;
|
textures >= subset.textures && images >= subset.images;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Descriptors>
|
template <typename Descriptors>
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue