Compare commits

..

8 commits

Author SHA1 Message Date
e6face74c6 add MMPX filter
All checks were successful
eden-license / license-header (pull_request) Successful in 21s
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2025-09-26 17:31:00 +02:00
43ac4adbb7 better logic
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2025-09-26 17:31:00 +02:00
3348312d2a fix vk
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2025-09-26 17:31:00 +02:00
3ecd929a18 fix ogl
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2025-09-26 17:31:00 +02:00
87c2579b63 [vk, ogl] VK_QCOM ZTC, Bspline, Mitchell filter weights
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2025-09-26 17:31:00 +02:00
19eb8272b1
[video_core] Fix a bug in buffer cache that caused flickering in some games when using fast buffering (#2584)
This fixes a bug in the buffer cache that caused flickering in some games when using fast buffering. This fixes Kirby Star Allies, Yoshi's Crafted World, and possibly many others.

Reviewed-on: #2584
Reviewed-by: Lizzie <lizzie@eden-emu.dev>
Co-authored-by: MaranBr <maranbr@outlook.com>
Co-committed-by: MaranBr <maranbr@outlook.com>
2025-09-26 05:13:08 +02:00
nyx
86ddb51a87
[android] Implement foreground notification service (#480)
A notification is shown when emulation is active. This, in theory should help preventing Eden for getting destroyed in the background. It is also a nice Q.O.L feature to have.

Credits go to t895 for the initial implementation. This was back-ported from older official Citra builds, although with crashes which i fixed myself.

Reviewed-on: #480
Reviewed-by: crueter <crueter@eden-emu.dev>
Co-authored-by: nyx <contact@innix.space>
Co-committed-by: nyx <contact@innix.space>
2025-09-26 05:01:33 +02:00
10aca2f90c
[Vulkan] Descriptor Pool bug fix (#2564)
a bank could be (incorrectly) considered a superset if it had enough image buffer descriptors but not enough storage image descriptors, causing the allocator to pick a bank that can’t actually satisfy VK_DESCRIPTOR_TYPE_STORAGE_IMAGE demand resulting in sham allocations and creation of new pools.
Note to testers,
please look for any regressions in terms of visuals and most importantly please test the performance and ram usage.

Reviewed-on: #2564
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
Reviewed-by: Shinmegumi <shinmegumi@eden-emu.dev>
Reviewed-by: Lizzie <lizzie@eden-emu.dev>
Reviewed-by: crueter <crueter@eden-emu.dev>
Co-authored-by: wildcard <wildcard@eden-emu.dev>
Co-committed-by: wildcard <wildcard@eden-emu.dev>
2025-09-26 04:58:09 +02:00
8 changed files with 136 additions and 7 deletions

View file

@ -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.BLUETOOTH_CONNECT" />
<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_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" />
</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
android:name=".features.DocumentProvider"
android:authorities="${applicationId}.user"

View file

@ -22,6 +22,17 @@ fun Context.getPublicFilesDir(): File = getExternalFilesDir(null) ?: filesDir
class YuzuApplication : Application() {
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(
getString(R.string.notice_notification_channel_id),
getString(R.string.notice_notification_channel_name),
@ -34,6 +45,7 @@ class YuzuApplication : Application() {
// or other notification behaviors after this
val notificationManager = getSystemService(NotificationManager::class.java)
notificationManager.createNotificationChannel(noticeChannel)
notificationManager.createNotificationChannel(foregroundService)
}
override fun onCreate() {

View file

@ -7,6 +7,7 @@
package org.yuzu.yuzu_emu.activities
import android.annotation.SuppressLint
import android.app.Activity
import android.app.PendingIntent
import android.app.PictureInPictureParams
import android.app.RemoteAction
@ -59,6 +60,7 @@ import org.yuzu.yuzu_emu.utils.ParamPackage
import org.yuzu.yuzu_emu.utils.ThemeHelper
import java.text.NumberFormat
import kotlin.math.roundToInt
import org.yuzu.yuzu_emu.utils.ForegroundService
class EmulationActivity : AppCompatActivity(), SensorEventListener {
private lateinit var binding: ActivityEmulationBinding
@ -78,6 +80,8 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener {
private val emulationViewModel: EmulationViewModel by viewModels()
private var foregroundService: Intent? = null
override fun onCreate(savedInstanceState: Bundle?) {
Log.gameLaunched = true
ThemeHelper.setTheme(this)
@ -128,6 +132,9 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener {
nfcReader = NfcReader(this)
nfcReader.initialize()
foregroundService = Intent(this, ForegroundService::class.java)
startForegroundService(foregroundService)
val preferences = PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext)
if (!preferences.getBoolean(Settings.PREF_MEMORY_WARNING_SHOWN, false)) {
if (MemoryUtil.isLessThan(MemoryUtil.REQUIRED_MEMORY, MemoryUtil.totalMemory)) {
@ -189,6 +196,12 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener {
stopMotionSensorListener()
}
override fun onDestroy() {
super.onDestroy()
stopForegroundService(this)
}
override fun onUserLeaveHint() {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S) {
if (BooleanSetting.PICTURE_IN_PICTURE.getBoolean() && !isInPictureInPictureMode) {
@ -511,6 +524,12 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener {
companion object {
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) {
val launcher = Intent(activity, EmulationActivity::class.java)
launcher.putExtra(EXTRA_SELECTED_GAME, game)

View file

@ -48,6 +48,7 @@ import java.io.BufferedOutputStream
import java.util.zip.ZipEntry
import java.util.zip.ZipInputStream
import androidx.core.content.edit
import org.yuzu.yuzu_emu.activities.EmulationActivity
import kotlin.text.compareTo
class MainActivity : AppCompatActivity(), ThemeProvider {
@ -188,6 +189,9 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
if (it) checkKeys()
}
// Dismiss previous notifications (should not happen unless a crash occurred)
EmulationActivity.stopForegroundService(this)
setInsets()
}
@ -293,6 +297,11 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
themeId = resId
}
override fun onDestroy() {
EmulationActivity.stopForegroundService(this)
super.onDestroy()
}
val getGamesDirectory =
registerForActivityResult(ActivityResultContracts.OpenDocumentTree()) { result ->
if (result != null) {

View file

@ -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)
}

View file

@ -8,6 +8,11 @@
<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="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 -->
<string name="enhanced_fps_suffix">(Enhanced)</string>

View file

@ -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]);
Buffer& buffer = slot_buffers[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 &&
size <= channel_state->uniform_buffer_skip_cache_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);
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
// This exists to avoid instances where the fast buffer is bound and a GPU write happens

View file

@ -31,7 +31,7 @@ struct DescriptorBank {
bool DescriptorBankInfo::IsSuperset(const DescriptorBankInfo& subset) const noexcept {
return uniform_buffers >= subset.uniform_buffers && storage_buffers >= subset.storage_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>