Compare commits

...

34 Commits

Author SHA1 Message Date
Gamer64
d1e0df68ed Revert some unused stuff 2025-06-27 09:05:45 +02:00
crueter
accf4cda38 fix translation and display
Signed-off-by: crueter <swurl@swurl.xyz>
2025-06-26 18:38:06 -04:00
Gamer64
3dd6eced05 settings.h: Attemp to fix android slider 2025-06-26 22:07:00 +02:00
Gamer64
8c173242cf settings.h: Pair use_custom_cpu_ticks with cpu_ticks 2025-06-26 19:10:26 +02:00
Gamer64
334d998e63 Update configure_cpu.h 2025-06-26 18:36:07 +02:00
Gamer64
cbf5f4b8c9 Fix include 2025-06-26 18:20:16 +02:00
Gamer64
617d4c5b1a PC: Add Custom CPU Ticks to UI, simplify core timing 2025-06-26 17:54:06 +02:00
Gamer64
05fb5743da vscode, its GetValue(), not get() lol 2025-06-26 17:03:29 +02:00
Gamer64
d921d668ec Fix parameter again 2025-06-26 16:46:21 +02:00
Gamer64
d69f3abd52 Fix incorrect assigment type 2025-06-26 16:08:57 +02:00
Gamer64
69026faa88 Android: Add min & max 2025-06-26 15:54:29 +02:00
Gamer64
f4cb591578 Add Custom CPU Ticks to Android UI 2025-06-26 15:45:50 +02:00
Gamer64
7e4a804e6c more testing 2025-06-26 13:39:20 +02:00
Gamer64
6bc212e4cb small test to see how it works 2025-06-26 12:12:09 +02:00
Pavel Barabanov
846ec85f24 [hid] stub for ActivateDebugMouse 2025-06-24 07:13:07 +03:00
Maufeat
59e69d91f4 MacOS fix compilation errors and correct app icon and title (#202)
- Adds ICONV_LIBRARY to CMakeList to fix ffmpeg compilation error
- Replaces the app icon from yuzu.icns to eden.icns
- Changes app title and name from yuzu to eden
- Updates MoltenVK to 1.3.0

Note: The wiki also needs an update, after merge to master I'll edit the wiki. The wiki says to installs Qt5 but eden uses Qt6.
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/202
Co-authored-by: Maufeat <sahyno1996@gmail.com>
Co-committed-by: Maufeat <sahyno1996@gmail.com>
2025-06-21 21:01:05 +00:00
Bix
02603abbdc Enables building of APK with Optimised package name. en-gb. (#206)
Build with: ./gradlew assembleGenshinSpoofRelease
Using com.miHoYo.Yuanshen to enable device dependent features such as AI frame generation.

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/206
Co-authored-by: Bix <xq9zp7f2@proton.me>
Co-committed-by: Bix <xq9zp7f2@proton.me>
2025-06-21 19:35:18 +00:00
MrPurple666
2f01c69710 LRU Cache Refactor with Thread-Safety (#199)
The cache is now thread-safe using std::shared_mutex, allowing concurrent reads without blocking and some other minor things to better maintenance

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/199
Co-authored-by: MrPurple666 <mrpurple666@noreply.localhost>
Co-committed-by: MrPurple666 <mrpurple666@noreply.localhost>
2025-06-21 19:32:32 +00:00
JPikachu
2946cdbd2d renderer: add area sampling scaling method (#201)
Adds Area Sampling to the list of scaling options. Works well to achieve a high-quality, smooth super-sampling effect. Dolphin has had this for a while and so has Ryujinx, so lui decided to port it.

Adapted from these two PRs:
https://github.com/Ryujinx/Ryujinx/pull/7304
https://github.com/dolphin-emu/dolphin/pull/11999

Credit: Torzu, lui

Reviewed-on: http://vub63vv26q6v27xzv2dtcd25xumubshogm67yrpaz2rculqxs7jlfqad.onion/torzu-emu/torzu/pulls/57
Co-authored-by: lui <lui@vub63vv26q6v27xzv2dtcd25xumubshogm67yrpaz2rculqxs7jlfqad.onion>
Co-committed-by: lui <lui@vub63vv26q6v27xzv2dtcd25xumubshogm67yrpaz2rculqxs7jlfqad.onion>

Co-authored-by: lui <lui@vub63vv26q6v27xzv2dtcd25xumubshogm67yrpaz2rculqxs7jlfqad.onion>
Co-authored-by: crueter <swurl@swurl.xyz>
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/201
Co-authored-by: JPikachu <jpikachu.eden@gmail.com>
Co-committed-by: JPikachu <jpikachu.eden@gmail.com>
2025-06-21 13:35:04 +00:00
Maufeat
8c33b0bb5d Add Device Power State (Windows, Linux, Mac and Android) (#197)
Uses native power state methods to display battery percentage and charging state correctly. Mainly for qlaunch.
Tested on Windows, Linux. Mac and Android

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/197
Co-authored-by: Maufeat <sahyno1996@gmail.com>
Co-committed-by: Maufeat <sahyno1996@gmail.com>
2025-06-18 08:34:54 +00:00
crueter
6bf5ae700a Fix macOS build (#198)
Signed-off-by: Aleksandr Popovich <alekpopo@pm.me>
Co-authored-by: Aleksandr Popovich <alekpopo@pm.me>
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/198
2025-06-18 08:00:09 +00:00
crueter
704d4e4428 [android, desktop] Firebomb pre-alpha banner (#195)
Signed-off-by: crueter <swurl@swurl.xyz>
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/195
Co-authored-by: crueter <swurl@swurl.xyz>
Co-committed-by: crueter <swurl@swurl.xyz>
2025-06-17 01:59:05 +00:00
Aleksandr Popovich
6aeba9de66 [vk] Add some dynamic handling in the pipeline (#142)
Co-authored-by: crueter <swurl@swurl.xyz>
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/142
Co-authored-by: Aleksandr Popovich <alekpopo@pm.me>
Co-committed-by: Aleksandr Popovich <alekpopo@pm.me>
2025-06-16 22:36:30 +00:00
crueter
eae819f0d6 Migrate BuildConfig
Signed-off-by: crueter <swurl@swurl.xyz>
2025-06-16 14:39:12 -04:00
crueter
a4123200c0 fix packaging (#192)
Signed-off-by: crueter <swurl@swurl.xyz>
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/192
Co-authored-by: crueter <swurl@swurl.xyz>
Co-committed-by: crueter <swurl@swurl.xyz>
2025-06-16 03:27:06 +00:00
crueter
5591ce30c9 Revert "[gradle] fix warnings & update deps (#189)"
This reverts commit af923c92eb.
2025-06-15 21:46:24 -04:00
crueter
af923c92eb [gradle] fix warnings & update deps (#189)
Signed-off-by: crueter <swurl@swurl.xyz>
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/189
Co-authored-by: crueter <swurl@swurl.xyz>
Co-committed-by: crueter <swurl@swurl.xyz>
2025-06-15 22:30:12 +00:00
crueter
cf00554d23 windows JIT fix (#191)
Signed-off-by: crueter <swurl@swurl.xyz>
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/191
Co-authored-by: crueter <swurl@swurl.xyz>
Co-committed-by: crueter <swurl@swurl.xyz>
2025-06-15 19:25:40 +00:00
Pavel Barabanov
cee222f0e6 fixing crashes when installing updates 2025-06-15 15:29:52 +03:00
crueter
b2d16cb3dd [ci] Fix license-header workflow
Signed-off-by: crueter <swurl@swurl.xyz>
2025-06-15 05:02:32 -04:00
crueter
ef2d0a9076 [ci] tmp: debug license-header
Signed-off-by: crueter <swurl@swurl.xyz>
2025-06-15 04:58:00 -04:00
xbzk
f3e00b633e multiplayer lobby search dialog send button clipping fix (#188)
fix for send button.

Co-authored-by: Allison Cunha <allisonbzk@gmail.com>
Co-authored-by: crueter <swurl@swurl.xyz>
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/188
Reviewed-by: crueter <crueter@noreply.localhost>
Co-authored-by: xbzk <xbzk@noreply.localhost>
Co-committed-by: xbzk <xbzk@noreply.localhost>
2025-06-15 08:53:41 +00:00
MaranBr
cc7f2808ed Update FFmpeg to 7.1.1, libvpx to 1.13.1 and libx264 to c24e06c on Android (#187)
This updates FFmpeg to 7.1.1, libvpx to 1.13.1 and libx264 to c24e06c on Android.

Co-authored-by: MaranBr <maranbr@outlook.com>
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/187
Reviewed-by: crueter <crueter@noreply.localhost>
Co-authored-by: MaranBr <maranbr@noreply.localhost>
Co-committed-by: MaranBr <maranbr@noreply.localhost>
2025-06-14 17:42:22 +00:00
xbzk
6b46aca0b7 top margin fixed for non zero cutout screens (#185)
intended to fix top margin for screens with non zero cutout insets

Co-authored-by: Allison Cunha <allisonbzk@gmail.com>
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/185
Co-authored-by: xbzk <xbzk@noreply.localhost>
Co-committed-by: xbzk <xbzk@noreply.localhost>
2025-06-12 20:41:13 +00:00
67 changed files with 1011 additions and 406 deletions

View File

@@ -7,9 +7,16 @@ license_header = <<~EOF
EOF
print 'Getting branch changes...'
puts "\n"
branch_name = `git rev-parse --abbrev-ref HEAD`.chomp
print branch_name
puts "\n"
branch_commits = `git log #{branch_name} --not master --pretty=format:"%h"`.split("\n")
print branch_commits
puts "\n"
branch_commit_range = "#{branch_commits[-1]}^..#{branch_commits[0]}"
print branch_commit_range
puts "\n"
branch_changed_files = `git diff-tree --no-commit-id --name-only #{branch_commit_range} -r`.split("\n")
puts 'done'

View File

@@ -34,9 +34,6 @@ fi
if [ "$TARGET" = "appimage" ]; then
export EXTRA_CMAKE_FLAGS=(-DCMAKE_INSTALL_PREFIX=/usr -DYUZU_ROOM=ON -DYUZU_ROOM_STANDALONE=OFF -DYUZU_CMD=OFF)
# Bundle required QT wayland libraries
export EXTRA_QT_PLUGINS="waylandcompositor"
export EXTRA_PLATFORM_PLUGINS="libqwayland-egl.so;libqwayland-generic.so"
else
# For the linux-fresh verification target, verify compilation without PCH as well.
export EXTRA_CMAKE_FLAGS=(-DYUZU_USE_PRECOMPILED_HEADERS=OFF)

View File

@@ -11,8 +11,8 @@ export ARCH="$BASE_ARCH"
export BUILDDIR="$2"
LIB4BN="https://raw.githubusercontent.com/VHSgunzo/sharun/refs/heads/main/lib4bin"
URUNTIME="https://github.com/VHSgunzo/uruntime/releases/latest/download/uruntime-appimage-dwarfs-$ARCH"
SHARUN="https://github.com/VHSgunzo/sharun/releases/latest/download/sharun-${BASE_ARCH}-aio"
URUNTIME="https://github.com/VHSgunzo/uruntime/releases/latest/download/uruntime-appimage-dwarfs-${BASE_ARCH}"
if [ "$ARCH" = 'x86_64' ]; then
if [ "$1" = 'v3' ]; then
@@ -48,22 +48,24 @@ fi
UPINFO='gh-releases-zsync|eden-emulator|Releases|latest|*.AppImage.zsync'
LIBDIR="/usr/lib"
# some distros are weird and use a subdir
if [ ! -f "/usr/lib/libGL.so" ]
# Workaround for Gentoo
if [ ! -d "$LIBDIR/qt6" ]
then
LIBDIR="/usr/lib64"
fi
# Workaround for Debian
if [ ! -d "$LIBDIR/qt6" ]
then
LIBDIR="/usr/lib/${BASE_ARCH}-linux-gnu"
fi
# Bundle all libs
# temp workaround for arch being silly
mkdir -p share/X11
cp -r /usr/share/X11/xkb share/X11
wget --retry-connrefused --tries=30 "$LIB4BN" -O ./lib4bin
chmod +x ./lib4bin
xvfb-run -a -- ./lib4bin -p -v -e -s -k \
wget --retry-connrefused --tries=30 "$SHARUN" -O ./sharun-aio
chmod +x ./sharun-aio
xvfb-run -a ./sharun-aio l -p -v -e -s -k \
../$BUILDDIR/bin/eden* \
$LIBDIR/lib*GL*.so* \
$LIBDIR/libSDL2*.so* \
@@ -88,14 +90,18 @@ xvfb-run -a -- ./lib4bin -p -v -e -s -k \
$LIBDIR/spa-0.2/*/* \
$LIBDIR/alsa-lib/*
rm -f ./sharun-aio
# Prepare sharun
if [ "$ARCH" = 'aarch64' ]; then
# allow the host vulkan to be used for aarch64 given the sed situation
# allow the host vulkan to be used for aarch64 given the sad situation
echo 'SHARUN_ALLOW_SYS_VKICD=1' > ./.env
fi
wget https://github.com/VHSgunzo/sharun/releases/download/v0.6.3/sharun-x86_64 -O sharun
chmod a+x sharun
# Workaround for Gentoo
if [ -d "shared/libproxy" ]; then
cp shared/libproxy/* lib/
fi
ln -f ./sharun ./AppRun
./sharun -g
@@ -121,9 +127,4 @@ echo "Generating AppImage..."
echo "Generating zsync file..."
zsyncmake *.AppImage -u *.AppImage
echo "All Done!"
# Cleanup
rm -rf AppDir
rm uruntime
echo "All Done!"

View File

@@ -1,7 +1,7 @@
name: eden-license
on:
pull_request_target:
pull_request:
branches: [ master ]
jobs:
@@ -13,9 +13,8 @@ jobs:
fetch-depth: 0
- name: Fetch
run: git fetch origin
run: git fetch origin master:master
# TODO: fix the script
- name: Make script executable
run: chmod +x ./.ci/license-header.rb

View File

@@ -512,6 +512,8 @@ if (APPLE)
# Umbrella framework for everything GUI-related
find_library(COCOA_LIBRARY Cocoa)
set(PLATFORM_LIBRARIES ${COCOA_LIBRARY} ${IOKIT_LIBRARY} ${COREVIDEO_LIBRARY})
find_library(ICONV_LIBRARY iconv REQUIRED)
list(APPEND PLATFORM_LIBRARIES ${ICONV_LIBRARY})
elseif (WIN32)
# Target Windows 10
add_definitions(-D_WIN32_WINNT=0x0A00 -DWINVER=0x0A00)

BIN
dist/eden.icns vendored Normal file

Binary file not shown.

View File

@@ -163,13 +163,24 @@ alignas(64) static constinit std::array<HostLoc, ABI_AllCallerSaveSize() - 1> AB
};
void ABI_PushCallerSaveRegistersAndAdjustStackExcept(BlockOfCode& code, const HostLoc exception) {
#ifdef _MSC_VER
std::vector<HostLoc> regs;
std::remove_copy(ABI_ALL_CALLER_SAVE.begin(), ABI_ALL_CALLER_SAVE.end(), std::back_inserter(regs), exception);
ABI_PushRegistersAndAdjustStack(code, 0, regs);
#else
ASSUME(size_t(exception) < 32);
ABI_PushRegistersAndAdjustStack(code, 0, ABI_CALLER_SAVED_EXCEPT_TABLE[size_t(exception)]);
#endif
}
void ABI_PopCallerSaveRegistersAndAdjustStackExcept(BlockOfCode& code, const HostLoc exception) {
#ifdef _MSC_VER
std::vector<HostLoc> regs;
std::remove_copy(ABI_ALL_CALLER_SAVE.begin(), ABI_ALL_CALLER_SAVE.end(), std::back_inserter(regs), exception);
ABI_PopRegistersAndAdjustStack(code, 0, regs);
#else
ASSUME(size_t(exception) < 32);
ABI_PopRegistersAndAdjustStack(code, 0, ABI_CALLER_SAVED_EXCEPT_TABLE[size_t(exception)]);
#endif
}
} // namespace Dynarmic::Backend::X64

View File

@@ -225,7 +225,7 @@ if (NOT WIN32 AND NOT ANDROID)
elseif(ANDROID)
# Use yuzu FFmpeg binaries
if (ARCHITECTURE_arm64)
set(FFmpeg_EXT_NAME "ffmpeg-android-v5.1.LTS-aarch64")
set(FFmpeg_EXT_NAME "ffmpeg-android-7.1.1-aarch64")
elseif (ARCHITECTURE_x86_64)
set(FFmpeg_EXT_NAME "ffmpeg-android-v5.1.LTS-x86_64")
else()

View File

@@ -1,9 +1,9 @@
// SPDX-FileCopyrightText: Copyright yuzu/Citra Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright yuzu/Citra Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
import android.annotation.SuppressLint
import kotlin.collections.setOf
import org.jlleitschuh.gradle.ktlint.reporter.ReporterType
@@ -35,6 +35,7 @@ android {
buildFeatures {
viewBinding = true
buildConfig = true
}
compileOptions {
@@ -62,11 +63,7 @@ android {
targetSdk = 35
versionName = getGitVersion()
versionCode = if (System.getenv("AUTO_VERSIONED") == "true") {
autoVersion
} else {
1
}
versionCode = autoVersion
ndk {
@SuppressLint("ChromeOsAbiSupport")
@@ -121,7 +118,6 @@ android {
isDefault = true
resValue("string", "app_name_suffixed", "eden Debug Release")
signingConfig = signingConfigs.getByName("default")
isMinifyEnabled = true
isDebuggable = true
proguardFiles(
getDefaultProguardFile("proguard-android.txt"),
@@ -131,7 +127,7 @@ android {
applicationIdSuffix = ".relWithDebInfo"
isJniDebuggable = true
}
// Signed by debug key disallowing distribution on Play Store.
// Attaches 'debug' suffix to version and package name, allowing installation alongside the release build.
debug {
@@ -144,14 +140,22 @@ android {
}
}
flavorDimensions.add("version")
productFlavors {
create("mainline") {
isDefault = true
dimension = "version"
android {
flavorDimensions.add("version")
productFlavors {
create("mainline") {
dimension = "version"
// No need to set applicationId here
}
create("genshinSpoof") {
dimension = "version"
applicationId = "com.miHoYo.Yuanshen" // Correct use of applicationId inside the flavor block
}
}
}
externalNativeBuild {
cmake {
version = "3.22.1"

View File

@@ -1,9 +1,9 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
// SPDX-FileCopyrightText: 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
package org.yuzu.yuzu_emu
@@ -489,4 +489,9 @@ object NativeLibrary {
* Checks if all necessary keys are present for decryption
*/
external fun areKeysPresent(): Boolean
/**
* Updates the device power state to global variables
*/
external fun updatePowerState(percentage: Int, isCharging: Boolean, hasBattery: Boolean)
}

View File

@@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -13,6 +16,7 @@ import org.yuzu.yuzu_emu.utils.DirectoryInitialization
import org.yuzu.yuzu_emu.utils.DocumentsTree
import org.yuzu.yuzu_emu.utils.GpuDriverHelper
import org.yuzu.yuzu_emu.utils.Log
import org.yuzu.yuzu_emu.utils.PowerStateUpdater
fun Context.getPublicFilesDir(): File = getExternalFilesDir(null) ?: filesDir
@@ -40,6 +44,7 @@ class YuzuApplication : Application() {
GpuDriverHelper.initializeDriverParameters()
NativeInput.reloadInputDevices()
NativeLibrary.logDeviceInfo()
PowerStateUpdater.start()
Log.logDeviceInfo()
createNotificationChannels()

View File

@@ -1,9 +1,9 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
// SPDX-FileCopyrightText: 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
package org.yuzu.yuzu_emu.activities
@@ -58,6 +58,7 @@ import org.yuzu.yuzu_emu.utils.NativeConfig
import org.yuzu.yuzu_emu.utils.NfcReader
import org.yuzu.yuzu_emu.utils.ParamPackage
import org.yuzu.yuzu_emu.utils.ThemeHelper
import org.yuzu.yuzu_emu.utils.PowerStateUtils
import java.text.NumberFormat
import kotlin.math.roundToInt

View File

@@ -13,6 +13,7 @@ enum class BooleanSetting(override val key: String) : AbstractBooleanSetting {
CORE_SYNC_CORE_SPEED("sync_core_speed"),
RENDERER_USE_SPEED_LIMIT("use_speed_limit"),
USE_FAST_CPU_TIME("use_fast_cpu_time"),
USE_CUSTOM_CPU_TICKS("use_custom_cpu_ticks"),
USE_DOCKED_MODE("use_docked_mode"),
USE_AUTO_STUB("use_auto_stub"),
RENDERER_USE_DISK_SHADER_CACHE("use_disk_shader_cache"),

View File

@@ -37,6 +37,7 @@ enum class IntSetting(override val key: String) : AbstractIntSetting {
MEMORY_LAYOUT("memory_layout_mode"),
FSR_SHARPENING_SLIDER("fsr_sharpening_slider"),
FAST_CPU_TIME("fast_cpu_time"),
CPU_TICKS("cpu_ticks"),
FAST_GPU_TIME("fast_gpu_time"),
CABINET_APPLET("cabinet_applet_mode"),

View File

@@ -1,7 +1,6 @@
// SPDX-FileCopyrightText: 2025 Eden Emulator Project
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
package org.yuzu.yuzu_emu.features.settings.model
import org.yuzu.yuzu_emu.R
@@ -36,7 +35,6 @@ object Settings {
const val PREF_FIRST_APP_LAUNCH = "FirstApplicationLaunch"
const val PREF_SHOULD_SHOW_DRIVER_WARNING = "ShouldShowDriverWarning"
const val PREF_SHOULD_SHOW_PRE_ALPHA_WARNING = "ShouldShowPreAlphaWarning"
const val PREF_SHOULD_SHOW_PRE_ALPHA_BANNER = "ShouldShowPreAlphaBanner"
const val PREF_SHOULD_SHOW_EDENS_VEIL_DIALOG = "ShouldShowEdensVeilDialog"
const val PREF_MEMORY_WARNING_SHOWN = "MemoryWarningShown"
const val SECTION_STATS_OVERLAY = "Stats Overlay"

View File

@@ -577,6 +577,22 @@ abstract class SettingsItem(
valuesId = R.array.clockValues
)
)
put(
SwitchSetting(
BooleanSetting.USE_CUSTOM_CPU_TICKS,
titleId = R.string.custom_cpu_ticks,
descriptionId = R.string.custom_cpu_ticks_description
)
)
put(
SliderSetting(
IntSetting.CPU_TICKS,
titleId = R.string.cpu_ticks,
descriptionId = 0,
min = 77,
max = 65535
)
)
put(
SwitchSetting(
BooleanSetting.RENDERER_REACTIVE_FLUSHING,

View File

@@ -220,6 +220,7 @@ class SettingsFragmentPresenter(
private fun addGraphicsSettings(sl: ArrayList<SettingsItem>) {
sl.apply {
// TODO(crueter): reorganize this, this is awful
add(IntSetting.RENDERER_ACCURACY.key)
add(IntSetting.RENDERER_RESOLUTION.key)
add(IntSetting.RENDERER_VSYNC.key)
@@ -447,6 +448,8 @@ class SettingsFragmentPresenter(
add(HeaderSetting(R.string.veil_misc))
add(BooleanSetting.USE_FAST_CPU_TIME.key)
add(IntSetting.FAST_CPU_TIME.key)
add(BooleanSetting.USE_CUSTOM_CPU_TICKS.key)
add(IntSetting.CPU_TICKS.key)
add(BooleanSetting.USE_LRU_CACHE.key)
add(BooleanSetting.CORE_SYNC_CORE_SPEED.key)
add(IntSetting.MEMORY_LAYOUT.key)

View File

@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: 2025 Eden Emulator Project
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
package org.yuzu.yuzu_emu.ui
@@ -162,12 +162,6 @@ class GamesFragment : Fragment() {
}
setInsets()
val shouldDisplayPreAlphaBanner =
PreferenceManager.getDefaultSharedPreferences(requireContext())
.getBoolean(Settings.PREF_SHOULD_SHOW_PRE_ALPHA_BANNER, true)
if (shouldDisplayPreAlphaBanner) {
addPreAlphaBanner()
}
}
val applyGridGamesBinding = {
@@ -238,86 +232,6 @@ class GamesFragment : Fragment() {
navController.navigate(R.id.action_gamesFragment_to_homeSettingsFragment)
}
private fun addPreAlphaBanner() {
val preAlphaBanner = TextView(requireContext()).apply {
id = "pre_alpha_banner".hashCode()
layoutParams = ConstraintLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT
).apply {
marginStart = resources.getDimensionPixelSize(R.dimen.spacing_med)
marginEnd = resources.getDimensionPixelSize(R.dimen.spacing_med)
topMargin = resources.getDimensionPixelSize(R.dimen.spacing_large)
topToBottom = R.id.frame_search
}
setPadding(
resources.getDimensionPixelSize(R.dimen.spacing_med),
resources.getDimensionPixelSize(R.dimen.spacing_large),
resources.getDimensionPixelSize(R.dimen.spacing_med),
resources.getDimensionPixelSize(R.dimen.spacing_med)
)
setBackgroundColor(
MaterialColors.getColor(
this,
com.google.android.material.R.attr.colorPrimary
)
)
text = getString(R.string.pre_alpha_warning)
setTextAppearance(
com.google.android.material.R.style.TextAppearance_Material3_HeadlineSmall
)
setTextColor(
MaterialColors.getColor(
this,
com.google.android.material.R.attr.colorOnError
)
)
gravity = Gravity.CENTER
}
val closeButton = ImageButton(requireContext()).apply {
id = "pre_alpha_close_button".hashCode()
layoutParams = ConstraintLayout.LayoutParams(
resources.getDimensionPixelSize(R.dimen.spacing_large),
resources.getDimensionPixelSize(R.dimen.spacing_large)
).apply {
startToStart = "pre_alpha_banner".hashCode()
topToTop = "pre_alpha_banner".hashCode()
bottomToBottom = "pre_alpha_banner".hashCode()
marginStart = resources.getDimensionPixelSize(R.dimen.spacing_large) * 2
topMargin = resources.getDimensionPixelSize(R.dimen.spacing_small)
}
setImageResource(android.R.drawable.ic_menu_close_clear_cancel)
setColorFilter(
MaterialColors.getColor(
this,
com.google.android.material.R.attr.colorOnError
)
)
setBackgroundColor(Color.Transparent.toArgb())
setOnClickListener {
PreferenceManager.getDefaultSharedPreferences(requireContext())
.edit() {
putBoolean(Settings.PREF_SHOULD_SHOW_PRE_ALPHA_BANNER, false)
}
binding.root.removeView(preAlphaBanner)
binding.root.removeView(this)
binding.swipeRefresh.updateLayoutParams<ConstraintLayout.LayoutParams> {
topToBottom = R.id.frame_search
}
}
}
binding.root.addView(preAlphaBanner)
binding.root.addView(closeButton)
binding.swipeRefresh.updateLayoutParams<ConstraintLayout.LayoutParams> {
topToBottom = preAlphaBanner.id
}
}
private fun showViewMenu(anchor: View) {
val popup = PopupMenu(requireContext(), anchor)
popup.menuInflater.inflate(R.menu.menu_game_views, popup.menu)
@@ -444,32 +358,24 @@ class GamesFragment : Fragment() {
private fun setInsets() =
ViewCompat.setOnApplyWindowInsetsListener(
binding.root
) { view: View, windowInsets: WindowInsetsCompat ->
) { _: View, windowInsets: WindowInsetsCompat ->
val barInsets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars())
val cutoutInsets = windowInsets.getInsets(WindowInsetsCompat.Type.displayCutout())
val spacingNavigation = resources.getDimensionPixelSize(R.dimen.spacing_navigation)
resources.getDimensionPixelSize(R.dimen.spacing_navigation_rail)
val isLandscape =
resources.configuration.orientation == Configuration.ORIENTATION_LANDSCAPE
binding.swipeRefresh.setProgressViewEndTarget(
false,
barInsets.top + resources.getDimensionPixelSize(R.dimen.spacing_refresh_end)
)
val leftInsets = barInsets.left + cutoutInsets.left
val rightInsets = barInsets.right + cutoutInsets.right
val topInsets = barInsets.top + cutoutInsets.top
val bottomInsets = barInsets.bottom + cutoutInsets.bottom
val leftInset = barInsets.left + cutoutInsets.left
val rightInset = barInsets.right + cutoutInsets.right
val topInset = maxOf(barInsets.top, cutoutInsets.top)
val mlpSwipe = binding.swipeRefresh.layoutParams as ViewGroup.MarginLayoutParams
if (view.getLayoutDirection() == View.LAYOUT_DIRECTION_LTR) {
mlpSwipe.leftMargin = leftInsets
mlpSwipe.rightMargin = rightInsets
} else {
mlpSwipe.leftMargin = leftInsets
mlpSwipe.rightMargin = rightInsets
}
mlpSwipe.leftMargin = leftInset
mlpSwipe.rightMargin = rightInset
binding.swipeRefresh.layoutParams = mlpSwipe
val mlpHeader = binding.header.layoutParams as ViewGroup.MarginLayoutParams
@@ -477,29 +383,27 @@ class GamesFragment : Fragment() {
// Store original margins only once
if (originalHeaderTopMargin == null) {
originalHeaderTopMargin = mlpHeader.topMargin
originalHeaderBottomMargin = mlpHeader.bottomMargin
originalHeaderRightMargin = mlpHeader.rightMargin
originalHeaderLeftMargin = mlpHeader.leftMargin
}
// Always set margin as original + insets
mlpHeader.leftMargin = (originalHeaderLeftMargin ?: 0) + leftInsets
mlpHeader.rightMargin = (originalHeaderRightMargin ?: 0) + rightInsets
mlpHeader.topMargin = (originalHeaderTopMargin ?: 0) + if (!isLandscape) topInsets else 0
mlpHeader.bottomMargin = (originalHeaderBottomMargin ?: 0) + if (!isLandscape) bottomInsets else 0
mlpHeader.leftMargin = (originalHeaderLeftMargin ?: 0) + leftInset
mlpHeader.rightMargin = (originalHeaderRightMargin ?: 0) + rightInset
mlpHeader.topMargin = (originalHeaderTopMargin ?: 0) + topInset + resources.getDimensionPixelSize(R.dimen.spacing_med)
binding.header.layoutParams = mlpHeader
binding.noticeText.updatePadding(bottom = spacingNavigation)
binding.header.updatePadding(top = cutoutInsets.top + resources.getDimensionPixelSize(R.dimen.spacing_large) + if (isLandscape) barInsets.top else 0)
binding.gridGames.updatePadding(
top = resources.getDimensionPixelSize(R.dimen.spacing_med)
)
val mlpFab = binding.addDirectory.layoutParams as ViewGroup.MarginLayoutParams
val fabPadding = resources.getDimensionPixelSize(R.dimen.spacing_large)
mlpFab.leftMargin = leftInsets + fabPadding
mlpFab.leftMargin = leftInset + fabPadding
mlpFab.bottomMargin = barInsets.bottom + fabPadding
mlpFab.rightMargin = rightInsets + fabPadding
mlpFab.rightMargin = rightInset + fabPadding
binding.addDirectory.layoutParams = mlpFab
windowInsets

View File

@@ -0,0 +1,46 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
package org.yuzu.yuzu_emu.utils
import android.os.Handler
import android.os.Looper
import org.yuzu.yuzu_emu.NativeLibrary
import org.yuzu.yuzu_emu.YuzuApplication
object PowerStateUpdater {
private lateinit var handler: Handler
private lateinit var runnable: Runnable
private const val UPDATE_INTERVAL_MS = 1000L
private var isStarted = false
fun start() {
if (isStarted) {
return
}
val context = YuzuApplication.appContext
handler = Handler(Looper.getMainLooper())
runnable = Runnable {
val info = PowerStateUtils.getBatteryInfo(context)
NativeLibrary.updatePowerState(info[0], info[1] == 1, info[2] == 1)
handler.postDelayed(runnable, UPDATE_INTERVAL_MS)
}
handler.post(runnable)
isStarted = true
}
fun stop() {
if (!isStarted) {
return
}
if (::handler.isInitialized) {
handler.removeCallbacks(runnable)
}
isStarted = false
}
}

View File

@@ -0,0 +1,45 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
package org.yuzu.yuzu_emu.utils
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.os.BatteryManager
import android.os.Build
object PowerStateUtils {
@JvmStatic
fun getBatteryInfo(context: Context?): IntArray {
if (context == null) {
return intArrayOf(0, 0, 0) // Percentage, IsCharging, HasBattery
}
val results = intArrayOf(100, 0, 1)
val iFilter = IntentFilter(Intent.ACTION_BATTERY_CHANGED)
val batteryStatusIntent: Intent? = context.registerReceiver(null, iFilter)
if (batteryStatusIntent != null) {
val present = batteryStatusIntent.getBooleanExtra(BatteryManager.EXTRA_PRESENT, true)
if (!present) {
results[2] = 0; results[0] = 0; results[1] = 0; return results
}
results[2] = 1
val level = batteryStatusIntent.getIntExtra(BatteryManager.EXTRA_LEVEL, -1)
val scale = batteryStatusIntent.getIntExtra(BatteryManager.EXTRA_SCALE, -1)
if (level != -1 && scale != -1 && scale != 0) {
results[0] = (level.toFloat() / scale.toFloat() * 100.0f).toInt()
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
val bm = context.getSystemService(Context.BATTERY_SERVICE) as BatteryManager?
results[0] = bm?.getIntProperty(BatteryManager.BATTERY_PROPERTY_CAPACITY) ?: 100
}
val status = batteryStatusIntent.getIntExtra(BatteryManager.EXTRA_STATUS, -1)
results[1] = if (status == BatteryManager.BATTERY_STATUS_CHARGING || status == BatteryManager.BATTERY_STATUS_FULL) 1 else 0
}
return results
}
}

View File

@@ -1,9 +1,9 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
// SPDX-FileCopyrightText: 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#include <codecvt>
#include <locale>
@@ -79,6 +79,11 @@ static EmulationSession s_instance;
std::unique_ptr<AndroidMultiplayer> multiplayer{nullptr};
std::shared_ptr<Core::AnnounceMultiplayerSession> announce_multiplayer_session;
//Power Status default values
std::atomic<int> g_battery_percentage = {100};
std::atomic<bool> g_is_charging = {false};
std::atomic<bool> g_has_battery = {true};
EmulationSession::EmulationSession() {
m_vfs = std::make_shared<FileSys::RealVfsFilesystem>();
}
@@ -1014,4 +1019,16 @@ JNIEXPORT void JNICALL Java_org_yuzu_yuzu_1emu_network_NetPlayManager_netPlayUnb
JNIEnv* env, [[maybe_unused]] jobject obj, jstring username) {
multiplayer->NetPlayUnbanUser(Common::Android::GetJString(env, username));
}
JNIEXPORT void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_updatePowerState(
JNIEnv* env,
jobject,
jint percentage,
jboolean isCharging,
jboolean hasBattery) {
g_battery_percentage.store(percentage, std::memory_order_relaxed);
g_is_charging.store(isCharging, std::memory_order_relaxed);
g_has_battery.store(hasBattery, std::memory_order_relaxed);
}
} // extern "C"

View File

@@ -70,9 +70,10 @@
<com.google.android.material.card.MaterialCardView
android:id="@+id/search_background"
style="?attr/materialCardViewFilledStyle"
android:layout_width="wrap_content"
android:layout_width="match_parent"
android:layout_height="48dp"
android:layout_marginStart="16dp"
android:layout_marginEnd="16dp"
android:layout_marginTop="12dp"
app:cardCornerRadius="24dp">
@@ -81,7 +82,7 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginStart="16dp"
android:layout_marginEnd="48dp"
android:layout_marginEnd="16dp"
android:orientation="horizontal">
<ImageView
@@ -109,22 +110,27 @@
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_gravity="center_vertical|end"
android:layout_marginEnd="16dp"
android:layout_marginEnd="48dp"
android:background="?attr/selectableItemBackground"
android:src="@drawable/ic_clear"
android:visibility="invisible"
app:tint="?attr/colorOnSurfaceVariant"
tools:visibility="visible" />
<ImageView
android:id="@+id/btn_submit"
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_gravity="center_vertical|end"
android:layout_marginEnd="16dp"
android:background="?attr/selectableItemBackground"
android:src="@drawable/ic_send"
android:contentDescription="@string/submit"
app:tint="?attr/colorOnSurfaceVariant"
tools:visibility="visible" />
</com.google.android.material.card.MaterialCardView>
<com.google.android.material.button.MaterialButton
android:id="@+id/btn_submit"
style="@style/Widget.Material3.Button.ElevatedButton"
android:layout_width="110dp"
android:layout_height="48dp"
android:layout_marginTop="12dp"
android:text="@string/submit" />
</LinearLayout>

View File

@@ -250,6 +250,7 @@
<item>@string/scaling_filter_gaussian</item>
<item>@string/scaling_filter_scale_force</item>
<item>@string/scaling_filter_fsr</item>
<item>@string/scaling_filter_area</item>
</string-array>
<integer-array name="rendererScalingFilterValues">
@@ -259,6 +260,7 @@
<item>3</item>
<item>4</item>
<item>5</item>
<item>6</item>
</integer-array>
<string-array name="rendererAntiAliasingNames">

View File

@@ -90,6 +90,9 @@
<string name="use_lru_cache_description">Enable or disable the Least Recently Used (LRU) cache, increasing performance by saving CPU process usage. Some games have issue with it, notably TotK 1.2.1, so disable if the game doesn\'t boot or crashes randomly.</string>
<string name="use_fast_cpu_time">Fast CPU Time</string>
<string name="use_fast_cpu_time_description">Forces the emulated CPU to run at a higher clock, reducing certain FPS limiters. This option is hacky and may cause issues, and weaker CPUs may see reduced performance.</string>
<string name="custom_cpu_ticks">Custom CPU Ticks</string>
<string name="custom_cpu_ticks_description">Set a custom value of CPU ticks. Higher values can increase performance, but may also cause the game to freeze. A range of 7721000 is recommended.</string>
<string name="cpu_ticks">Ticks</string>
<string name="fast_cpu_time">CPU Clock</string>
<string name="fast_cpu_time_description">Use Boost (1700MHz) to run at the Switch\'s highest native clock, or Fast (2000MHz) to run at 2x clock.</string>
<string name="memory_layout">Memory Layout</string>
@@ -863,6 +866,7 @@
<string name="scaling_filter_gaussian">Gaussian</string>
<string name="scaling_filter_scale_force">ScaleForce</string>
<string name="scaling_filter_fsr">AMD FidelityFX™ Super Resolution</string>
<string name="scaling_filter_area">Area</string>
<!-- Anti-Aliasing -->
<string name="anti_aliasing_none">None</string>

View File

@@ -1,3 +1,6 @@
# SPDX-FileCopyrightText: Copyright 2025 yuzu Emulator Project
# SPDX-License-Identifier: GPL-3.0-or-later
# SPDX-FileCopyrightText: 2023 yuzu Emulator Project
# SPDX-License-Identifier: GPL-3.0-or-later
@@ -14,7 +17,5 @@ android.useAndroidX=true
# Kotlin code style for this project: "official" or "obsolete":
kotlin.code.style=official
kotlin.parallel.tasks.in.project=true
android.defaults.buildfeatures.buildconfig=true
# Android Gradle plugin 8.0.2
android.suppressUnsupportedCompileSdk=34

View File

@@ -42,6 +42,8 @@ add_library(common STATIC
demangle.h
detached_tasks.cpp
detached_tasks.h
device_power_state.cpp
device_power_state.h
div_ceil.h
dynamic_library.cpp
dynamic_library.h

View File

@@ -0,0 +1,102 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#include "device_power_state.h"
#if defined(_WIN32)
#include <windows.h>
#elif defined(__ANDROID__)
#include <atomic>
extern std::atomic<int> g_battery_percentage;
extern std::atomic<bool> g_is_charging;
extern std::atomic<bool> g_has_battery;
#elif defined(__APPLE__)
#include <TargetConditionals.h>
#if TARGET_OS_MAC
#include <IOKit/ps/IOPSKeys.h>
#include <IOKit/ps/IOPowerSources.h>
#endif
#elif defined(__linux__)
#include <fstream>
#include <string>
#include <dirent.h>
#endif
namespace Common {
PowerStatus GetPowerStatus()
{
PowerStatus info;
#if defined(_WIN32)
SYSTEM_POWER_STATUS status;
if (GetSystemPowerStatus(&status)) {
if (status.BatteryFlag == 128) {
info.has_battery = false;
} else {
info.percentage = status.BatteryLifePercent;
info.charging = (status.BatteryFlag & 8) != 0;
}
} else {
info.has_battery = false;
}
#elif defined(__ANDROID__)
info.percentage = g_battery_percentage.load(std::memory_order_relaxed);
info.charging = g_is_charging.load(std::memory_order_relaxed);
info.has_battery = g_has_battery.load(std::memory_order_relaxed);
#elif defined(__APPLE__) && TARGET_OS_MAC
CFTypeRef info_ref = IOPSCopyPowerSourcesInfo();
CFArrayRef sources = IOPSCopyPowerSourcesList(info_ref);
if (CFArrayGetCount(sources) > 0) {
CFDictionaryRef battery =
IOPSGetPowerSourceDescription(info_ref, CFArrayGetValueAtIndex(sources, 0));
CFNumberRef curNum =
(CFNumberRef)CFDictionaryGetValue(battery, CFSTR(kIOPSCurrentCapacityKey));
CFNumberRef maxNum =
(CFNumberRef)CFDictionaryGetValue(battery, CFSTR(kIOPSMaxCapacityKey));
int cur = 0, max = 0;
CFNumberGetValue(curNum, kCFNumberIntType, &cur);
CFNumberGetValue(maxNum, kCFNumberIntType, &max);
if (max > 0)
info.percentage = (cur * 100) / max;
CFBooleanRef isCharging =
(CFBooleanRef)CFDictionaryGetValue(battery, CFSTR(kIOPSIsChargingKey));
info.charging = CFBooleanGetValue(isCharging);
} else {
info.has_battery = false;
}
CFRelease(sources);
CFRelease(info_ref);
#elif defined(__linux__)
const char* battery_path = "/sys/class/power_supply/BAT0/";
std::ifstream capFile(std::string(battery_path) + "capacity");
if (capFile) {
capFile >> info.percentage;
}
else {
info.has_battery = false;
}
std::ifstream statFile(std::string(battery_path) + "status");
if (statFile) {
std::string status;
std::getline(statFile, status);
info.charging = (status == "Charging");
}
#else
info.has_battery = false;
#endif
return info;
}
}

View File

@@ -0,0 +1,14 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
namespace Common {
struct PowerStatus {
int percentage = -1;
bool charging = false;
bool has_battery = true;
};
PowerStatus GetPowerStatus();
} // namespace Common

View File

@@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -256,6 +259,25 @@ struct Values {
true,
&use_fast_cpu_time};
SwitchableSetting<bool> use_custom_cpu_ticks{linkage,
false,
"use_custom_cpu_ticks",
Category::Cpu,
Specialization::Paired,
true,
true};
SwitchableSetting<u32, true> cpu_ticks{linkage,
16000,
77,
65535,
"cpu_ticks",
Category::Cpu,
Specialization::Countable,
true,
true,
&use_custom_cpu_ticks};
SwitchableSetting<bool> cpu_debug_mode{linkage, false, "cpu_debug_mode", Category::CpuDebug};
Setting<bool> cpuopt_page_tables{linkage, true, "cpuopt_page_tables", Category::CpuDebug};
@@ -668,10 +690,6 @@ struct Values {
Setting<bool> censor_username{linkage, true, "censor_username", Category::Miscellaneous};
Setting<bool> use_dev_keys{linkage, false, "use_dev_keys", Category::Miscellaneous};
Setting<bool> first_launch{linkage, true, "first_launch", Category::Miscellaneous};
Setting<bool> hide_pre_alpha_warning{linkage,
false,
"hide_pre_alpha_warning",
Category::Miscellaneous};
// Network
Setting<std::string> network_interface{linkage, std::string(), "network_interface",

View File

@@ -1,3 +1,9 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2024 Torzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -158,7 +164,7 @@ ENUM(ResolutionSetup,
Res7X,
Res8X);
ENUM(ScalingFilter, NearestNeighbor, Bilinear, Bicubic, Gaussian, ScaleForce, Fsr, MaxEnum);
ENUM(ScalingFilter, NearestNeighbor, Bilinear, Bicubic, Gaussian, ScaleForce, Fsr, Area, MaxEnum);
ENUM(AntiAliasing, None, Fxaa, Smaa, MaxEnum);

View File

@@ -1,136 +1,187 @@
#pragma once
// SPDX-FileCopyrightText: 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include <list>
#include <unordered_map>
#include <optional>
#include <shared_mutex>
#include <unordered_map>
#include <utility>
#include "common/logging/log.h"
template<typename KeyType, typename ValueType>
template <typename KeyType, typename ValueType>
class LRUCache {
private:
bool enabled = true;
size_t capacity;
std::list<KeyType> cache_list;
std::unordered_map<KeyType, std::pair<typename std::list<KeyType>::iterator, ValueType>> cache_map;
public:
explicit LRUCache(size_t capacity, bool enabled = true) : enabled(enabled), capacity(capacity) {
cache_map.reserve(capacity);
LOG_WARNING(Core, "LRU Cache initialized with state: {}", enabled ? "enabled" : "disabled");
using key_type = KeyType;
using value_type = ValueType;
using size_type = std::size_t;
struct Statistics {
size_type hits = 0;
size_type misses = 0;
void reset() noexcept { hits = misses = 0; }
};
explicit LRUCache(size_type capacity, bool enabled = true)
: enabled_{enabled}, capacity_{capacity} {
cache_map_.reserve(capacity_);
LOG_WARNING(Core, "LRU Cache initialised (state: {} | capacity: {})", enabled_ ? "enabled" : "disabled", capacity_);
}
// Returns pointer to value if found, nullptr otherwise
ValueType* get(const KeyType& key) {
if (!enabled) return nullptr;
// Non-movable copy semantics
LRUCache(const LRUCache&) = delete;
LRUCache& operator=(const LRUCache&) = delete;
LRUCache(LRUCache&& other) noexcept { *this = std::move(other); }
LRUCache& operator=(LRUCache&& other) noexcept {
if (this == &other) return *this;
std::unique_lock this_lock(mutex_, std::defer_lock);
std::unique_lock other_lock(other.mutex_, std::defer_lock);
std::lock(this_lock, other_lock);
enabled_ = other.enabled_;
capacity_ = other.capacity_;
cache_list_ = std::move(other.cache_list_);
cache_map_ = std::move(other.cache_map_);
stats_ = other.stats_;
return *this;
}
~LRUCache() = default;
auto it = cache_map.find(key);
if (it == cache_map.end()) {
[[nodiscard]] value_type* get(const key_type& key) {
if (!enabled_) [[unlikely]] return nullptr;
std::unique_lock lock(mutex_);
auto it = cache_map_.find(key);
if (it == cache_map_.end()) {
++stats_.misses;
return nullptr;
}
// Move the accessed item to the front of the list (most recently used)
cache_list.splice(cache_list.begin(), cache_list, it->second.first);
return &(it->second.second);
move_to_front(it);
++stats_.hits;
return &it->second.second;
}
// Returns pointer to value if found (without promoting it), nullptr otherwise
ValueType* peek(const KeyType& key) const {
if (!enabled) return nullptr;
auto it = cache_map.find(key);
return it != cache_map.end() ? &(it->second.second) : nullptr;
[[nodiscard]] value_type* peek(const key_type& key) const {
if (!enabled_) [[unlikely]] return nullptr;
std::shared_lock lock(mutex_);
auto it = cache_map_.find(key);
return it == cache_map_.end() ? nullptr : &it->second.second;
}
// Inserts or updates a key-value pair
void put(const KeyType& key, const ValueType& value) {
if (!enabled) return;
template <typename V>
void put(const key_type& key, V&& value) {
if (!enabled_) [[unlikely]] return;
std::unique_lock lock(mutex_);
insert_or_update(key, std::forward<V>(value));
}
auto it = cache_map.find(key);
if (it != cache_map.end()) {
// Key exists, update value and move to front
it->second.second = value;
cache_list.splice(cache_list.begin(), cache_list, it->second.first);
return;
template <typename ValueFactory>
value_type& get_or_emplace(const key_type& key, ValueFactory&& factory) {
std::unique_lock lock(mutex_);
auto it = cache_map_.find(key);
if (it != cache_map_.end()) {
move_to_front(it);
return it->second.second;
}
// Remove the least recently used item if cache is full
if (cache_map.size() >= capacity) {
auto last = cache_list.back();
cache_map.erase(last);
cache_list.pop_back();
}
// Insert new item at the front
cache_list.push_front(key);
cache_map[key] = {cache_list.begin(), value};
value_type new_value = factory();
insert_or_update(key, std::move(new_value));
return cache_map_.find(key)->second.second;
}
// Enable or disable the LRU cache
void setEnabled(bool state) {
enabled = state;
LOG_WARNING(Core, "LRU Cache state changed to: {}", state ? "enabled" : "disabled");
if (!enabled) {
clear();
}
[[nodiscard]] bool contains(const key_type& key) const {
if (!enabled_) return false;
std::shared_lock lock(mutex_);
return cache_map_.find(key) != cache_map_.end();
}
// Check if the cache is enabled
bool isEnabled() const {
return enabled;
}
// Attempts to get value, returns std::nullopt if not found
std::optional<ValueType> try_get(const KeyType& key) {
auto* val = get(key);
return val ? std::optional<ValueType>(*val) : std::nullopt;
}
// Checks if key exists in cache
bool contains(const KeyType& key) const {
if (!enabled) return false;
return cache_map.find(key) != cache_map.end();
}
// Removes a key from the cache if it exists
bool erase(const KeyType& key) {
if (!enabled) return false;
auto it = cache_map.find(key);
if (it == cache_map.end()) {
return false;
}
cache_list.erase(it->second.first);
cache_map.erase(it);
bool erase(const key_type& key) {
if (!enabled_) return false;
std::unique_lock lock(mutex_);
auto it = cache_map_.find(key);
if (it == cache_map_.end()) return false;
cache_list_.erase(it->second.first);
cache_map_.erase(it);
return true;
}
// Removes all elements from the cache
void clear() {
cache_map.clear();
cache_list.clear();
std::unique_lock lock(mutex_);
cache_list_.clear();
cache_map_.clear();
stats_.reset();
}
// Returns current number of elements in cache
size_t size() const {
return enabled ? cache_map.size() : 0;
[[nodiscard]] size_type size() const {
if (!enabled_) return 0;
std::shared_lock lock(mutex_);
return cache_map_.size();
}
// Returns maximum capacity of cache
size_t get_capacity() const {
return capacity;
[[nodiscard]] size_type get_capacity() const { return capacity_; }
void resize(size_type new_capacity) {
if (!enabled_) return;
std::unique_lock lock(mutex_);
capacity_ = new_capacity;
shrink_if_needed();
cache_map_.reserve(capacity_);
}
// Resizes the cache, evicting LRU items if new capacity is smaller
void resize(size_t new_capacity) {
if (!enabled) return;
void setEnabled(bool state) {
std::unique_lock lock(mutex_);
enabled_ = state;
LOG_WARNING(Core, "LRU Cache state changed to: {}", state ? "enabled" : "disabled");
if (!enabled_) clear();
}
capacity = new_capacity;
while (cache_map.size() > capacity) {
auto last = cache_list.back();
cache_map.erase(last);
cache_list.pop_back();
[[nodiscard]] bool isEnabled() const { return enabled_; }
[[nodiscard]] Statistics stats() const {
std::shared_lock lock(mutex_);
return stats_;
}
private:
using list_type = std::list<key_type>;
using list_iterator = typename list_type::iterator;
using map_value_type = std::pair<list_iterator, value_type>;
using map_type = std::unordered_map<key_type, map_value_type>;
template <typename V>
void insert_or_update(const key_type& key, V&& value) {
auto it = cache_map_.find(key);
if (it != cache_map_.end()) {
it->second.second = std::forward<V>(value);
move_to_front(it);
return;
}
cache_map.reserve(capacity);
// evict LRU if full
if (cache_map_.size() >= capacity_) {
const auto& lru_key = cache_list_.back();
cache_map_.erase(lru_key);
cache_list_.pop_back();
}
cache_list_.push_front(key);
cache_map_[key] = {cache_list_.begin(), std::forward<V>(value)};
}
void move_to_front(typename map_type::iterator it) {
cache_list_.splice(cache_list_.begin(), cache_list_, it->second.first);
it->second.first = cache_list_.begin();
}
void shrink_if_needed() {
while (cache_map_.size() > capacity_) {
const auto& lru_key = cache_list_.back();
cache_map_.erase(lru_key);
cache_list_.pop_back();
}
}
private:
mutable std::shared_mutex mutex_;
bool enabled_{true};
size_type capacity_;
list_type cache_list_;
map_type cache_map_;
mutable Statistics stats_;
};

View File

@@ -1,5 +1,5 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
// SPDX-FileCopyrightText: 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#include "common/arm64/native_clock.h"
#include "common/bit_cast.h"
@@ -14,6 +14,23 @@
namespace Core::NCE {
Patcher::Patcher(Patcher&& other) noexcept
: patch_cache(std::move(other.patch_cache)),
m_patch_instructions(std::move(other.m_patch_instructions)),
c(m_patch_instructions),
m_save_context(other.m_save_context),
m_load_context(other.m_load_context),
mode(other.mode),
total_program_size(other.total_program_size),
m_relocate_module_index(other.m_relocate_module_index),
modules(std::move(other.modules)),
curr_patch(nullptr) {
if (!modules.empty()) {
curr_patch = &modules.back();
}
}
using namespace Common::Literals;
using namespace oaknut::util;

View File

@@ -1,5 +1,5 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
// SPDX-FileCopyrightText: 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
@@ -15,6 +15,7 @@
#include "core/hle/kernel/k_typed_address.h"
#include "core/hle/kernel/physical_memory.h"
#include "lru_cache.h"
#include <utility>
namespace Core::NCE {
@@ -30,6 +31,10 @@ using EntryTrampolines = std::unordered_map<ModuleTextAddress, PatchTextAddress>
class Patcher {
public:
Patcher(const Patcher&) = delete;
Patcher& operator=(const Patcher&) = delete;
Patcher(Patcher&& other) noexcept;
Patcher& operator=(Patcher&&) noexcept = delete;
explicit Patcher();
~Patcher();
@@ -62,7 +67,7 @@ private:
void WriteCntpctHandler(ModuleDestLabel module_dest, oaknut::XReg dest_reg);
private:
static constexpr size_t CACHE_SIZE = 4096; // Cache size for patch entries
static constexpr size_t CACHE_SIZE = 16384; // Cache size for patch entries
LRUCache<uintptr_t, PatchTextAddress> patch_cache{CACHE_SIZE, Settings::values.lru_cache_enabled.GetValue()};
void BranchToPatch(uintptr_t module_dest) {
@@ -70,7 +75,7 @@ private:
LOG_DEBUG(Core_ARM, "LRU cache lookup for address {:#x}", module_dest);
// Try to get existing patch entry from cache
if (auto* cached_patch = patch_cache.get(module_dest)) {
LOG_DEBUG(Core_ARM, "LRU cache hit for address {:#x}", module_dest);
LOG_WARNING(Core_ARM, "LRU cache hit for address {:#x}", module_dest);
curr_patch->m_branch_to_patch_relocations.push_back({c.offset(), *cached_patch});
return;
}

View File

@@ -172,7 +172,9 @@ void CoreTiming::UnscheduleEvent(const std::shared_ptr<EventType>& event_type,
}
void CoreTiming::AddTicks(u64 ticks_to_add) {
cpu_ticks += ticks_to_add;
cpu_ticks = Settings::values.use_custom_cpu_ticks.GetValue()
? Settings::values.cpu_ticks.GetValue()
: cpu_ticks + ticks_to_add;
downcount -= static_cast<s64>(cpu_ticks);
}

View File

@@ -26,6 +26,7 @@ enum class TitleType : u8 {
Update = 0x81,
AOC = 0x82,
DeltaTitle = 0x83,
DataPatch = 0x84,
};
enum class ContentRecordType : u8 {

View File

@@ -48,7 +48,7 @@ IHidServer::IHidServer(Core::System& system_, std::shared_ptr<ResourceManager> r
{1, C<&IHidServer::ActivateDebugPad>, "ActivateDebugPad"},
{11, C<&IHidServer::ActivateTouchScreen>, "ActivateTouchScreen"},
{21, C<&IHidServer::ActivateMouse>, "ActivateMouse"},
{26, nullptr, "ActivateDebugMouse"},
{26, C<&IHidServer::ActivateDebugMouse>, "ActivateDebugMouse"},
{31, C<&IHidServer::ActivateKeyboard>, "ActivateKeyboard"},
{32, C<&IHidServer::SendKeyboardLockKeyEvent>, "SendKeyboardLockKeyEvent"},
{40, C<&IHidServer::AcquireXpadIdEventHandle>, "AcquireXpadIdEventHandle"},
@@ -234,6 +234,11 @@ Result IHidServer::ActivateMouse(ClientAppletResourceUserId aruid) {
R_RETURN(GetResourceManager()->GetMouse()->Activate(aruid.pid));
}
Result IHidServer::ActivateDebugMouse(ClientAppletResourceUserId aruid) {
LOG_WARNING(Service_HID, "(STUBBED) called, applet_resource_user_id={}", aruid.pid);
R_SUCCEED();
}
Result IHidServer::ActivateKeyboard(ClientAppletResourceUserId aruid) {
LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", aruid.pid);

View File

@@ -37,6 +37,7 @@ private:
Result ActivateDebugPad(ClientAppletResourceUserId aruid);
Result ActivateTouchScreen(ClientAppletResourceUserId aruid);
Result ActivateMouse(ClientAppletResourceUserId aruid);
Result ActivateDebugMouse(ClientAppletResourceUserId aruid);
Result ActivateKeyboard(ClientAppletResourceUserId aruid);
Result SendKeyboardLockKeyEvent(u32 flags);
Result AcquireXpadIdEventHandle(OutCopyHandle<Kernel::KReadableEvent> out_event,

View File

@@ -1,8 +1,12 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <memory>
#include "common/device_power_state.h"
#include "common/logging/log.h"
#include "core/core.h"
#include "core/hle/kernel/k_event.h"
@@ -148,17 +152,31 @@ PSM::~PSM() = default;
void PSM::GetBatteryChargePercentage(HLERequestContext& ctx) {
LOG_DEBUG(Service_PTM, "called");
u32 percentage = 100;
Common::PowerStatus power_status = Common::GetPowerStatus();
if (power_status.has_battery && power_status.percentage >= 0) {
percentage = static_cast<u32>(power_status.percentage);
}
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.Push<u32>(battery_charge_percentage);
rb.Push<u32>(percentage);
}
void PSM::GetChargerType(HLERequestContext& ctx) {
LOG_DEBUG(Service_PTM, "called");
ChargerType charger = ChargerType::Unplugged;
Common::PowerStatus power_status = Common::GetPowerStatus();
if (power_status.has_battery && power_status.charging) {
charger = ChargerType::RegularCharger;
}
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.PushEnum(charger_type);
rb.PushEnum(charger);
}
void PSM::OpenSession(HLERequestContext& ctx) {

View File

@@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -23,9 +26,6 @@ private:
void GetBatteryChargePercentage(HLERequestContext& ctx);
void GetChargerType(HLERequestContext& ctx);
void OpenSession(HLERequestContext& ctx);
u32 battery_charge_percentage{100};
ChargerType charger_type{ChargerType::RegularCharger};
};
} // namespace Service::PTM

View File

@@ -7,7 +7,6 @@
#include <catch2/catch_test_macros.hpp>
#include "common/alignment.h"
#include "common/common_types.h"
#include "video_core/buffer_cache/memory_tracker_base.h"

View File

@@ -34,7 +34,7 @@ constexpr std::array PreferredGpuDecoders = {
AV_HWDEVICE_TYPE_VAAPI,
AV_HWDEVICE_TYPE_VDPAU,
#endif
AV_HWDEVICE_TYPE_VULKAN,
AV_HWDEVICE_TYPE_VULKAN
};
AVPixelFormat GetGpuFormat(AVCodecContext* codec_context, const AVPixelFormat* pix_fmts) {
@@ -99,8 +99,7 @@ bool Decoder::SupportsDecodingOnDevice(AVPixelFormat* out_pix_fmt, AVHWDeviceTyp
for (int i = 0;; i++) {
const AVCodecHWConfig* config = avcodec_get_hw_config(m_codec, i);
if (!config) {
LOG_DEBUG(HW_GPU, "{} decoder does not support device type {}", m_codec->name,
av_hwdevice_get_type_name(type));
LOG_DEBUG(HW_GPU, "{} decoder does not support device type {}", m_codec->name, av_hwdevice_get_type_name(type));
break;
}
if (config->methods & AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX && config->device_type == type) {
@@ -131,8 +130,7 @@ HardwareContext::~HardwareContext() {
av_buffer_unref(&m_gpu_decoder);
}
bool HardwareContext::InitializeForDecoder(DecoderContext& decoder_context,
const Decoder& decoder) {
bool HardwareContext::InitializeForDecoder(DecoderContext& decoder_context, const Decoder& decoder) {
const auto supported_types = GetSupportedDeviceTypes();
for (const auto type : PreferredGpuDecoders) {
AVPixelFormat hw_pix_fmt;
@@ -152,17 +150,14 @@ bool HardwareContext::InitializeForDecoder(DecoderContext& decoder_context,
}
}
LOG_INFO(HW_GPU, "Hardware decoding is disabled due to implementation issues, using CPU.");
return false;
}
bool HardwareContext::InitializeWithType(AVHWDeviceType type) {
av_buffer_unref(&m_gpu_decoder);
if (const int ret = av_hwdevice_ctx_create(&m_gpu_decoder, type, nullptr, nullptr, 0);
ret < 0) {
LOG_DEBUG(HW_GPU, "av_hwdevice_ctx_create({}) failed: {}", av_hwdevice_get_type_name(type),
AVError(ret));
if (const int ret = av_hwdevice_ctx_create(&m_gpu_decoder, type, nullptr, nullptr, 0); ret < 0) {
LOG_DEBUG(HW_GPU, "av_hwdevice_ctx_create({}) failed: {}", av_hwdevice_get_type_name(type), AVError(ret));
return false;
}
@@ -189,6 +184,7 @@ bool HardwareContext::InitializeWithType(AVHWDeviceType type) {
DecoderContext::DecoderContext(const Decoder& decoder) : m_decoder{decoder} {
m_codec_context = avcodec_alloc_context3(m_decoder.GetCodec());
av_opt_set(m_codec_context->priv_data, "preset", "veryfast", 0);
av_opt_set(m_codec_context->priv_data, "tune", "zerolatency", 0);
m_codec_context->thread_count = 0;
m_codec_context->thread_type &= ~FF_THREAD_FRAME;
@@ -199,8 +195,7 @@ DecoderContext::~DecoderContext() {
avcodec_free_context(&m_codec_context);
}
void DecoderContext::InitializeHardwareDecoder(const HardwareContext& context,
AVPixelFormat hw_pix_fmt) {
void DecoderContext::InitializeHardwareDecoder(const HardwareContext& context, AVPixelFormat hw_pix_fmt) {
m_codec_context->hw_device_ctx = av_buffer_ref(context.GetBufferRef());
m_codec_context->get_format = GetGpuFormat;
m_codec_context->pix_fmt = hw_pix_fmt;
@@ -223,21 +218,15 @@ bool DecoderContext::SendPacket(const Packet& packet) {
m_temp_frame = std::make_shared<Frame>();
m_got_frame = 0;
// Android can randomly crash when calling decode directly, so skip.
// TODO update ffmpeg and hope that fixes it.
#ifndef ANDROID
if (!m_codec_context->hw_device_ctx && m_codec_context->codec_id == AV_CODEC_ID_H264) {
m_decode_order = true;
auto* codec{ffcodec(m_decoder.GetCodec())};
if (const int ret = codec->cb.decode(m_codec_context, m_temp_frame->GetFrame(),
&m_got_frame, packet.GetPacket());
ret < 0) {
if (const int ret = codec->cb.decode(m_codec_context, m_temp_frame->GetFrame(), &m_got_frame, packet.GetPacket()); ret < 0) {
LOG_DEBUG(Service_NVDRV, "avcodec_send_packet error {}", AVError(ret));
return false;
}
return true;
}
#endif
if (const int ret = avcodec_send_packet(m_codec_context, packet.GetPacket()); ret < 0) {
LOG_ERROR(HW_GPU, "avcodec_send_packet error: {}", AVError(ret));
@@ -248,9 +237,6 @@ bool DecoderContext::SendPacket(const Packet& packet) {
}
std::shared_ptr<Frame> DecoderContext::ReceiveFrame() {
// Android can randomly crash when calling decode directly, so skip.
// TODO update ffmpeg and hope that fixes it.
#ifndef ANDROID
if (!m_codec_context->hw_device_ctx && m_codec_context->codec_id == AV_CODEC_ID_H264) {
m_decode_order = true;
auto* codec{ffcodec(m_decoder.GetCodec())};
@@ -269,10 +255,7 @@ std::shared_ptr<Frame> DecoderContext::ReceiveFrame() {
LOG_ERROR(Service_NVDRV, "Failed to receive a frame! error {}", ret);
return {};
}
} else
#endif
{
} else {
const auto ReceiveImpl = [&](AVFrame* frame) {
if (const int ret = avcodec_receive_frame(m_codec_context, frame); ret < 0) {
LOG_ERROR(HW_GPU, "avcodec_receive_frame error: {}", AVError(ret));
@@ -292,9 +275,7 @@ std::shared_ptr<Frame> DecoderContext::ReceiveFrame() {
}
m_temp_frame->SetFormat(PreferredGpuFormat);
if (const int ret = av_hwframe_transfer_data(m_temp_frame->GetFrame(),
intermediate_frame.GetFrame(), 0);
ret < 0) {
if (const int ret = av_hwframe_transfer_data(m_temp_frame->GetFrame(), intermediate_frame.GetFrame(), 0); ret < 0) {
LOG_ERROR(HW_GPU, "av_hwframe_transfer_data error: {}", AVError(ret));
return {};
}

View File

@@ -21,9 +21,7 @@ extern "C" {
#include <libavcodec/avcodec.h>
#include <libavutil/opt.h>
#ifndef ANDROID
#include <libavcodec/codec_internal.h>
#endif
#if defined(__GNUC__) || defined(__clang__)
#pragma GCC diagnostic pop

View File

@@ -42,6 +42,7 @@ set(SHADER_FILES
opengl_present_scaleforce.frag
opengl_smaa.glsl
pitch_unswizzle.comp
present_area.frag
present_bicubic.frag
present_gaussian.frag
queries_prefix_scan_sum.comp

View File

@@ -0,0 +1,107 @@
#version 460 core
layout(location = 0) in vec2 frag_tex_coord;
layout(location = 0) out vec4 color;
layout(binding = 0) uniform sampler2D color_texture;
#ifdef VULKAN
struct ScreenRectVertex {
vec2 position;
vec2 tex_coord;
};
layout (push_constant) uniform PushConstants {
mat4 modelview_matrix;
ScreenRectVertex vertices[4];
};
#else // OpenGL
layout(location = 1) uniform uvec2 screen_size;
#endif
/***** Area Sampling *****/
// By Sam Belliveau and Filippo Tarpini. Public Domain license.
// Effectively a more accurate sharp bilinear filter when upscaling,
// that also works as a mathematically perfect downscale filter.
// https://entropymine.com/imageworsener/pixelmixing/
// https://github.com/obsproject/obs-studio/pull/1715
// https://legacy.imagemagick.org/Usage/filter/
vec4 AreaSampling(sampler2D textureSampler, vec2 texCoords, vec2 source_size, vec2 target_size) {
// Determine the sizes of the source and target images.
vec2 inverted_target_size = vec2(1.0) / target_size;
// Determine the range of the source image that the target pixel will cover.
vec2 range = source_size * inverted_target_size;
vec2 beg = (texCoords.xy * source_size) - (range * 0.5);
vec2 end = beg + range;
// Compute the top-left and bottom-right corners of the pixel box.
ivec2 f_beg = ivec2(floor(beg));
ivec2 f_end = ivec2(floor(end));
// Compute how much of the start and end pixels are covered horizontally & vertically.
float area_w = 1.0 - fract(beg.x);
float area_n = 1.0 - fract(beg.y);
float area_e = fract(end.x);
float area_s = fract(end.y);
// Compute the areas of the corner pixels in the pixel box.
float area_nw = area_n * area_w;
float area_ne = area_n * area_e;
float area_sw = area_s * area_w;
float area_se = area_s * area_e;
// Initialize the color accumulator.
vec4 avg_color = vec4(0.0, 0.0, 0.0, 0.0);
// Accumulate corner pixels.
avg_color += area_nw * texelFetch(textureSampler, ivec2(f_beg.x, f_beg.y), 0);
avg_color += area_ne * texelFetch(textureSampler, ivec2(f_end.x, f_beg.y), 0);
avg_color += area_sw * texelFetch(textureSampler, ivec2(f_beg.x, f_end.y), 0);
avg_color += area_se * texelFetch(textureSampler, ivec2(f_end.x, f_end.y), 0);
// Determine the size of the pixel box.
int x_range = int(f_end.x - f_beg.x - 0.5);
int y_range = int(f_end.y - f_beg.y - 0.5);
// Accumulate top and bottom edge pixels.
for (int x = f_beg.x + 1; x <= f_beg.x + x_range; ++x) {
avg_color += area_n * texelFetch(textureSampler, ivec2(x, f_beg.y), 0);
avg_color += area_s * texelFetch(textureSampler, ivec2(x, f_end.y), 0);
}
// Accumulate left and right edge pixels and all the pixels in between.
for (int y = f_beg.y + 1; y <= f_beg.y + y_range; ++y) {
avg_color += area_w * texelFetch(textureSampler, ivec2(f_beg.x, y), 0);
avg_color += area_e * texelFetch(textureSampler, ivec2(f_end.x, y), 0);
for (int x = f_beg.x + 1; x <= f_beg.x + x_range; ++x) {
avg_color += texelFetch(textureSampler, ivec2(x, y), 0);
}
}
// Compute the area of the pixel box that was sampled.
float area_corners = area_nw + area_ne + area_sw + area_se;
float area_edges = float(x_range) * (area_n + area_s) + float(y_range) * (area_w + area_e);
float area_center = float(x_range) * float(y_range);
// Return the normalized average color.
return avg_color / (area_corners + area_edges + area_center);
}
void main() {
vec2 source_image_size = textureSize(color_texture, 0);
vec2 window_size;
#ifdef VULKAN
window_size.x = vertices[1].position.x - vertices[0].position.x;
window_size.y = vertices[2].position.y - vertices[0].position.y;
#else // OpenGL
window_size = screen_size;
#endif
color = AreaSampling(color_texture, frag_tex_coord, source_image_size, window_size);
}

View File

@@ -1,3 +1,9 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2024 Torzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -86,6 +92,9 @@ void BlitScreen::CreateWindowAdapt() {
case Settings::ScalingFilter::ScaleForce:
window_adapt = MakeScaleForce(device);
break;
case Settings::ScalingFilter::Area:
window_adapt = MakeArea(device);
break;
case Settings::ScalingFilter::Fsr:
case Settings::ScalingFilter::Bilinear:
default:

View File

@@ -1,8 +1,15 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2024 Torzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "video_core/host_shaders/opengl_present_frag.h"
#include "video_core/host_shaders/opengl_present_scaleforce_frag.h"
#include "video_core/host_shaders/present_area_frag.h"
#include "video_core/host_shaders/present_bicubic_frag.h"
#include "video_core/host_shaders/present_gaussian_frag.h"
#include "video_core/renderer_opengl/present/filters.h"
@@ -36,4 +43,9 @@ std::unique_ptr<WindowAdaptPass> MakeScaleForce(const Device& device) {
fmt::format("#version 460\n{}", HostShaders::OPENGL_PRESENT_SCALEFORCE_FRAG));
}
std::unique_ptr<WindowAdaptPass> MakeArea(const Device& device) {
return std::make_unique<WindowAdaptPass>(device, CreateBilinearSampler(),
HostShaders::PRESENT_AREA_FRAG);
}
} // namespace OpenGL

View File

@@ -1,3 +1,9 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2024 Torzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -13,5 +19,6 @@ std::unique_ptr<WindowAdaptPass> MakeBilinear(const Device& device);
std::unique_ptr<WindowAdaptPass> MakeBicubic(const Device& device);
std::unique_ptr<WindowAdaptPass> MakeGaussian(const Device& device);
std::unique_ptr<WindowAdaptPass> MakeScaleForce(const Device& device);
std::unique_ptr<WindowAdaptPass> MakeArea(const Device& device);
} // namespace OpenGL

View File

@@ -1,3 +1,9 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2024 Torzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -10,6 +16,7 @@ namespace OpenGL {
constexpr GLint PositionLocation = 0;
constexpr GLint TexCoordLocation = 1;
constexpr GLint ModelViewMatrixLocation = 0;
constexpr GLint ScreenSizeLocation = 1;
struct ScreenRectVertex {
constexpr ScreenRectVertex() = default;

View File

@@ -1,3 +1,9 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2024 Torzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -110,6 +116,9 @@ void WindowAdaptPass::DrawToFramebuffer(ProgramManager& program_manager, std::li
glBindTextureUnit(0, textures[i]);
glProgramUniformMatrix3x2fv(vert.handle, ModelViewMatrixLocation, 1, GL_FALSE,
matrices[i].data());
glProgramUniform2ui(frag.handle, ScreenSizeLocation,
static_cast<GLuint>(layout.screen.GetWidth()),
static_cast<GLuint>(layout.screen.GetHeight()));
glNamedBufferSubData(vertex_buffer.handle, 0, sizeof(vertices[i]), std::data(vertices[i]));
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
}

View File

@@ -1,8 +1,15 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2024 Torzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "common/common_types.h"
#include "video_core/host_shaders/present_area_frag_spv.h"
#include "video_core/host_shaders/present_bicubic_frag_spv.h"
#include "video_core/host_shaders/present_gaussian_frag_spv.h"
#include "video_core/host_shaders/vulkan_present_frag_spv.h"
@@ -53,4 +60,9 @@ std::unique_ptr<WindowAdaptPass> MakeScaleForce(const Device& device, VkFormat f
SelectScaleForceShader(device));
}
std::unique_ptr<WindowAdaptPass> MakeArea(const Device& device, VkFormat frame_format) {
return std::make_unique<WindowAdaptPass>(device, frame_format, CreateBilinearSampler(device),
BuildShader(device, PRESENT_AREA_FRAG_SPV));
}
} // namespace Vulkan

View File

@@ -1,3 +1,9 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2024 Torzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -14,5 +20,6 @@ std::unique_ptr<WindowAdaptPass> MakeBilinear(const Device& device, VkFormat fra
std::unique_ptr<WindowAdaptPass> MakeBicubic(const Device& device, VkFormat frame_format);
std::unique_ptr<WindowAdaptPass> MakeGaussian(const Device& device, VkFormat frame_format);
std::unique_ptr<WindowAdaptPass> MakeScaleForce(const Device& device, VkFormat frame_format);
std::unique_ptr<WindowAdaptPass> MakeArea(const Device& device, VkFormat frame_format);
} // namespace Vulkan

View File

@@ -1,3 +1,9 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2024 Torzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -43,6 +49,9 @@ void BlitScreen::SetWindowAdaptPass() {
case Settings::ScalingFilter::ScaleForce:
window_adapt = MakeScaleForce(device, swapchain_view_format);
break;
case Settings::ScalingFilter::Area:
window_adapt = MakeArea(device, swapchain_view_format);
break;
case Settings::ScalingFilter::Fsr:
case Settings::ScalingFilter::Bilinear:
default:

View File

@@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -928,6 +931,7 @@ bool AccelerateDMA::BufferToImage(const Tegra::DMA::ImageCopy& copy_info,
void RasterizerVulkan::UpdateDynamicStates() {
auto& regs = maxwell3d->regs;
UpdateViewportsState(regs);
UpdateScissorsState(regs);
UpdateDepthBias(regs);
@@ -935,7 +939,24 @@ void RasterizerVulkan::UpdateDynamicStates() {
UpdateDepthBounds(regs);
UpdateStencilFaces(regs);
UpdateLineWidth(regs);
if (device.IsExtExtendedDynamicStateSupported()) {
const u8 dynamic_state = Settings::values.dyna_state.GetValue();
auto features = DynamicFeatures{
.has_extended_dynamic_state = device.IsExtExtendedDynamicStateSupported()
&& dynamic_state > 0,
.has_extended_dynamic_state_2 = device.IsExtExtendedDynamicState2Supported()
&& dynamic_state > 1,
.has_extended_dynamic_state_2_extra = device.IsExtExtendedDynamicState2ExtrasSupported()
&& dynamic_state > 1,
.has_extended_dynamic_state_3_blend = device.IsExtExtendedDynamicState3BlendingSupported()
&& dynamic_state > 2,
.has_extended_dynamic_state_3_enables = device.IsExtExtendedDynamicState3EnablesSupported()
&& dynamic_state > 2,
.has_dynamic_vertex_input = device.IsExtVertexInputDynamicStateSupported(),
};
if (features.has_extended_dynamic_state) {
UpdateCullMode(regs);
UpdateDepthCompareOp(regs);
UpdateFrontFace(regs);
@@ -946,45 +967,54 @@ void RasterizerVulkan::UpdateDynamicStates() {
UpdateDepthTestEnable(regs);
UpdateDepthWriteEnable(regs);
UpdateStencilTestEnable(regs);
if (device.IsExtExtendedDynamicState2Supported()) {
if (features.has_extended_dynamic_state_2) {
UpdatePrimitiveRestartEnable(regs);
UpdateRasterizerDiscardEnable(regs);
UpdateDepthBiasEnable(regs);
}
if (device.IsExtExtendedDynamicState3EnablesSupported()) {
if (features.has_extended_dynamic_state_3_enables) {
using namespace Tegra::Engines;
if (device.GetDriverID() == VkDriverIdKHR::VK_DRIVER_ID_AMD_OPEN_SOURCE
|| device.GetDriverID() == VkDriverIdKHR::VK_DRIVER_ID_AMD_PROPRIETARY) {
struct In {
struct In
{
const Maxwell3D::Regs::VertexAttribute::Type d;
In(Maxwell3D::Regs::VertexAttribute::Type n) : d(n) {}
bool operator()(Maxwell3D::Regs::VertexAttribute n) const {
In(Maxwell3D::Regs::VertexAttribute::Type n)
: d(n)
{}
bool operator()(Maxwell3D::Regs::VertexAttribute n) const
{
return n.type == d;
}
};
auto has_float = std::any_of(
regs.vertex_attrib_format.begin(), regs.vertex_attrib_format.end(),
In(Maxwell3D::Regs::VertexAttribute::Type::Float));
auto has_float = std::any_of(regs.vertex_attrib_format.begin(),
regs.vertex_attrib_format.end(),
In(Maxwell3D::Regs::VertexAttribute::Type::Float));
if (regs.logic_op.enable)
regs.logic_op.enable = static_cast<u32>(!has_float);
UpdateLogicOpEnable(regs);
} else
} else {
UpdateLogicOpEnable(regs);
}
UpdateDepthClampEnable(regs);
}
}
if (device.IsExtExtendedDynamicState2ExtrasSupported()) {
if (features.has_extended_dynamic_state_2_extra) {
UpdateLogicOp(regs);
}
if (device.IsExtExtendedDynamicState3Supported()) {
if (features.has_extended_dynamic_state_3_enables) {
UpdateBlending(regs);
UpdateLineStippleEnable(regs);
UpdateConservativeRasterizationMode(regs);
}
}
if (device.IsExtVertexInputDynamicStateSupported()) {
if (features.has_dynamic_vertex_input) {
UpdateVertexInput(regs);
}
}
@@ -1104,25 +1134,39 @@ void RasterizerVulkan::UpdateDepthBias(Tegra::Engines::Maxwell3D::Regs& regs) {
regs.zeta.format == Tegra::DepthFormat::S8Z24_UNORM ||
regs.zeta.format == Tegra::DepthFormat::V8Z24_UNORM;
size_t length = sizeof(NEEDS_D24) / sizeof(u64);
bool needs_convert = false;
for (size_t i = 0; i < length; ++i) {
if (NEEDS_D24[i] == program_id) {
needs_convert = true;
break;
if (is_d24 && !device.SupportsD24DepthBuffer()) {
static constexpr const size_t length = sizeof(NEEDS_D24) / sizeof(NEEDS_D24[0]);
static constexpr const u64 *start = NEEDS_D24;
static constexpr const u64 *end = NEEDS_D24 + length;
const u64 *it = std::find(start, end, program_id);
if (it != end) {
// the base formulas can be obtained from here:
// https://docs.microsoft.com/en-us/windows/win32/direct3d11/d3d10-graphics-programming-guide-output-merger-stage-depth-bias
const double rescale_factor = static_cast<double>(1ULL << (32 - 24))
/ (static_cast<double>(0x1.ep+127));
units = static_cast<float>(static_cast<double>(units) * rescale_factor);
}
}
if (is_d24 && !device.SupportsD24DepthBuffer() && needs_convert) {
// the base formulas can be obtained from here:
// https://docs.microsoft.com/en-us/windows/win32/direct3d11/d3d10-graphics-programming-guide-output-merger-stage-depth-bias
const double rescale_factor = static_cast<double>(1ULL << (32 - 24))
/ (static_cast<double>(0x1.ep+127));
units = static_cast<float>(static_cast<double>(units) * rescale_factor);
} scheduler.Record([constant = units, clamp = regs.depth_bias_clamp,
factor = regs.slope_scale_depth_bias](vk::CommandBuffer cmdbuf) {
cmdbuf.SetDepthBias(constant, clamp, factor);
});
scheduler.Record(
[constant = units, clamp = regs.depth_bias_clamp, factor = regs.slope_scale_depth_bias, this](
vk::CommandBuffer cmdbuf) {
if (device.IsExtDepthBiasControlSupported()) {
static VkDepthBiasRepresentationInfoEXT bias_info{
.sType = VK_STRUCTURE_TYPE_DEPTH_BIAS_REPRESENTATION_INFO_EXT,
.pNext = nullptr,
.depthBiasRepresentation = VK_DEPTH_BIAS_REPRESENTATION_LEAST_REPRESENTABLE_VALUE_FORCE_UNORM_EXT,
.depthBiasExact = VK_FALSE,
};
cmdbuf.SetDepthBias(constant, clamp, factor, &bias_info);
} else {
cmdbuf.SetDepthBias(constant, clamp, factor);
}
});
}
void RasterizerVulkan::UpdateBlendConstants(Tegra::Engines::Maxwell3D::Regs& regs) {
@@ -1304,6 +1348,45 @@ void RasterizerVulkan::UpdateRasterizerDiscardEnable(Tegra::Engines::Maxwell3D::
});
}
void RasterizerVulkan::UpdateConservativeRasterizationMode(Tegra::Engines::Maxwell3D::Regs& regs)
{
if (!state_tracker.TouchConservativeRasterizationMode()) {
return;
}
scheduler.Record([enable = regs.conservative_raster_enable](vk::CommandBuffer cmdbuf) {
cmdbuf.SetConservativeRasterizationModeEXT(
enable ? VK_CONSERVATIVE_RASTERIZATION_MODE_UNDERESTIMATE_EXT
: VK_CONSERVATIVE_RASTERIZATION_MODE_DISABLED_EXT);
});
}
void RasterizerVulkan::UpdateLineStippleEnable(Tegra::Engines::Maxwell3D::Regs& regs)
{
if (!state_tracker.TouchLineStippleEnable()) {
return;
}
scheduler.Record([enable = regs.line_stipple_enable](vk::CommandBuffer cmdbuf) {
cmdbuf.SetLineStippleEnableEXT(enable);
});
}
void RasterizerVulkan::UpdateLineRasterizationMode(Tegra::Engines::Maxwell3D::Regs& regs)
{
// if (!state_tracker.TouchLi()) {
// return;
// }
// TODO: The maxwell emulator does not capture line rasters
// scheduler.Record([enable = regs.line](vk::CommandBuffer cmdbuf) {
// cmdbuf.SetConservativeRasterizationModeEXT(
// enable ? VK_CONSERVATIVE_RASTERIZATION_MODE_UNDERESTIMATE_EXT
// : VK_CONSERVATIVE_RASTERIZATION_MODE_DISABLED_EXT);
// });
}
void RasterizerVulkan::UpdateDepthBiasEnable(Tegra::Engines::Maxwell3D::Regs& regs) {
if (!state_tracker.TouchDepthBiasEnable()) {
return;

View File

@@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -139,7 +142,7 @@ public:
u32 pixel_stride);
private:
static constexpr u64 NEEDS_D24[] = {
static constexpr const u64 NEEDS_D24[] = {
0x01006A800016E000ULL, // SSBU
0x0100E95004038000ULL, // XC2
0x0100A6301214E000ULL, // FE:Engage
@@ -175,6 +178,9 @@ private:
void UpdateDepthCompareOp(Tegra::Engines::Maxwell3D::Regs& regs);
void UpdatePrimitiveRestartEnable(Tegra::Engines::Maxwell3D::Regs& regs);
void UpdateRasterizerDiscardEnable(Tegra::Engines::Maxwell3D::Regs& regs);
void UpdateConservativeRasterizationMode(Tegra::Engines::Maxwell3D::Regs& regs);
void UpdateLineStippleEnable(Tegra::Engines::Maxwell3D::Regs& regs);
void UpdateLineRasterizationMode(Tegra::Engines::Maxwell3D::Regs& regs);
void UpdateDepthBiasEnable(Tegra::Engines::Maxwell3D::Regs& regs);
void UpdateLogicOpEnable(Tegra::Engines::Maxwell3D::Regs& regs);
void UpdateDepthClampEnable(Tegra::Engines::Maxwell3D::Regs& regs);

View File

@@ -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-License-Identifier: GPL-2.0-or-later
@@ -50,6 +53,8 @@ Flags MakeInvalidationFlags() {
StateEnable,
PrimitiveRestartEnable,
RasterizerDiscardEnable,
ConservativeRasterizationMode,
LineStippleEnable,
DepthBiasEnable,
LogicOpEnable,
DepthClampEnable,
@@ -137,6 +142,8 @@ void SetupDirtyStateEnable(Tables& tables) {
setup(OFF(stencil_enable), StencilTestEnable);
setup(OFF(primitive_restart.enabled), PrimitiveRestartEnable);
setup(OFF(rasterize_enable), RasterizerDiscardEnable);
setup(OFF(conservative_raster_enable), ConservativeRasterizationMode);
setup(OFF(line_stipple_enable), LineStippleEnable);
setup(OFF(polygon_offset_point_enable), DepthBiasEnable);
setup(OFF(polygon_offset_line_enable), DepthBiasEnable);
setup(OFF(polygon_offset_fill_enable), DepthBiasEnable);

View File

@@ -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-License-Identifier: GPL-2.0-or-later
@@ -50,6 +53,8 @@ enum : u8 {
StencilTestEnable,
PrimitiveRestartEnable,
RasterizerDiscardEnable,
ConservativeRasterizationMode,
LineStippleEnable,
DepthBiasEnable,
StateEnable,
LogicOp,
@@ -200,10 +205,15 @@ public:
return Exchange(Dirty::RasterizerDiscardEnable, false);
}
bool TouchDepthBiasEnable() {
return Exchange(Dirty::DepthBiasEnable, false);
bool TouchConservativeRasterizationMode()
{
return Exchange(Dirty::ConservativeRasterizationMode, false);
}
bool TouchLineStippleEnable() { return Exchange(Dirty::ConservativeRasterizationMode, false); }
bool TouchDepthBiasEnable() { return Exchange(Dirty::DepthBiasEnable, false); }
bool TouchLogicOpEnable() {
return Exchange(Dirty::LogicOpEnable, false);
}

View File

@@ -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-License-Identifier: GPL-2.0-or-later
@@ -141,6 +144,9 @@ void Load(VkDevice device, DeviceDispatch& dld) noexcept {
X(vkCmdSetDepthWriteEnableEXT);
X(vkCmdSetPrimitiveRestartEnableEXT);
X(vkCmdSetRasterizerDiscardEnableEXT);
X(vkCmdSetConservativeRasterizationModeEXT);
X(vkCmdSetLineRasterizationModeEXT);
X(vkCmdSetLineStippleEnableEXT);
X(vkCmdSetDepthBiasEnableEXT);
X(vkCmdSetLogicOpEnableEXT);
X(vkCmdSetDepthClampEnableEXT);

View File

@@ -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-License-Identifier: GPL-2.0-or-later
@@ -234,6 +237,9 @@ struct DeviceDispatch : InstanceDispatch {
PFN_vkCmdSetDepthWriteEnableEXT vkCmdSetDepthWriteEnableEXT{};
PFN_vkCmdSetPrimitiveRestartEnableEXT vkCmdSetPrimitiveRestartEnableEXT{};
PFN_vkCmdSetRasterizerDiscardEnableEXT vkCmdSetRasterizerDiscardEnableEXT{};
PFN_vkCmdSetConservativeRasterizationModeEXT vkCmdSetConservativeRasterizationModeEXT{};
PFN_vkCmdSetLineRasterizationModeEXT vkCmdSetLineRasterizationModeEXT{};
PFN_vkCmdSetLineStippleEnableEXT vkCmdSetLineStippleEnableEXT{};
PFN_vkCmdSetDepthBiasEnableEXT vkCmdSetDepthBiasEnableEXT{};
PFN_vkCmdSetLogicOpEnableEXT vkCmdSetLogicOpEnableEXT{};
PFN_vkCmdSetDepthClampEnableEXT vkCmdSetDepthClampEnableEXT{};
@@ -1431,6 +1437,21 @@ public:
dld->vkCmdSetRasterizerDiscardEnableEXT(handle, enable ? VK_TRUE : VK_FALSE);
}
void SetConservativeRasterizationModeEXT(VkConservativeRasterizationModeEXT mode) const noexcept
{
dld->vkCmdSetConservativeRasterizationModeEXT(handle, mode);
}
void SetLineRasterizationModeEXT(VkLineRasterizationModeEXT mode) const noexcept
{
dld->vkCmdSetLineRasterizationModeEXT(handle, mode);
}
void SetLineStippleEnableEXT(bool enable) const noexcept
{
dld->vkCmdSetLineStippleEnableEXT(handle, enable ? VK_TRUE : VK_FALSE);
}
void SetDepthBiasEnableEXT(bool enable) const noexcept {
dld->vkCmdSetDepthBiasEnableEXT(handle, enable ? VK_TRUE : VK_FALSE);
}

View File

@@ -366,7 +366,7 @@ target_sources(yuzu
)
if (APPLE)
set(MACOSX_ICON "../../dist/yuzu.icns")
set(MACOSX_ICON "../../dist/eden.icns")
set_source_files_properties(${MACOSX_ICON} PROPERTIES MACOSX_PACKAGE_LOCATION Resources)
target_sources(yuzu PRIVATE ${MACOSX_ICON})
set_target_properties(yuzu PROPERTIES MACOSX_BUNDLE TRUE)
@@ -374,7 +374,7 @@ if (APPLE)
if (NOT USE_SYSTEM_MOLTENVK)
set(MOLTENVK_PLATFORM "macOS")
set(MOLTENVK_VERSION "v1.2.7")
set(MOLTENVK_VERSION "v1.3.0")
download_moltenvk_external(${MOLTENVK_PLATFORM} ${MOLTENVK_VERSION})
endif()
find_library(MOLTENVK_LIBRARY MoltenVK REQUIRED)

View File

@@ -1,3 +1,8 @@
<!--
SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
SPDX-License-Identifier: GPL-3.0-or-later
-->
<?xml version="1.0" encoding="UTF-8"?>
<!--
@@ -15,7 +20,7 @@ SPDX-License-Identifier: GPL-2.0-or-later
<key>CFBundleGetInfoString</key>
<string></string>
<key>CFBundleIconFile</key>
<string>yuzu.icns</string>
<string>eden.icns</string>
<key>CFBundleIdentifier</key>
<string>com.yuzu-emu.yuzu</string>
<key>CFBundleInfoDictionaryVersion</key>
@@ -23,7 +28,7 @@ SPDX-License-Identifier: GPL-2.0-or-later
<key>CFBundleLongVersionString</key>
<string></string>
<key>CFBundleName</key>
<string>yuzu</string>
<string>eden</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>

View File

@@ -73,8 +73,9 @@ void ConfigureCpu::Setup(const ConfigurationShared::Builder& builder) {
} else if (setting->Id() == Settings::values.cpu_backend.Id()) {
backend_layout->addWidget(widget);
backend_combobox = widget->combobox;
} else if (setting->Id() == Settings::values.fast_cpu_time.Id()
|| setting->Id() == Settings::values.use_fast_cpu_time.Id()) {
} else if (setting->Id() == Settings::values.fast_cpu_time.Id()) {
ui->general_layout->addWidget(widget);
} else if (setting->Id() == Settings::values.cpu_ticks.Id()) {
ui->general_layout->addWidget(widget);
} else {
// Presently, all other settings here are unsafe checkboxes

View File

@@ -1,8 +1,10 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: 2016 Citra Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <algorithm>
#include <functional>
#include "yuzu/configuration/configure_profile_manager.h"
#include <QDialog>
#include <QDialogButtonBox>
#include <QFileDialog>
@@ -18,8 +20,10 @@
#include "core/core.h"
#include "core/hle/service/acc/profile_manager.h"
#include "ui_configure_profile_manager.h"
#include "yuzu/configuration/configure_profile_manager.h"
#include "yuzu/util/limitable_input_dialog.h"
#include <algorithm>
#include <functional>
#include <iostream>
namespace {
// Same backup JPEG used by acc IProfile::GetImage if no jpeg found

View File

@@ -1,3 +1,9 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2024 Torzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -100,6 +106,13 @@ std::unique_ptr<TranslationMap> InitializeTranslations(QWidget* parent)
tr("Overclocks the emulated CPU to remove some FPS limiters. Weaker CPUs may see reduced performance, "
"and certain games may behave improperly.\nUse Boost (1700MHz) to run at the Switch's highest native "
"clock, or Fast (2000MHz) to run at 2x clock."));
INSERT(Settings, use_custom_cpu_ticks, QString(), QString());
INSERT(Settings,
cpu_ticks,
tr("Custom CPU Ticks"),
tr("Set a custom value of CPU ticks. Higher values can increase performance, but may "
"also cause the game to freeze. A range of 7721000 is recommended."));
INSERT(Settings, cpu_backend, tr("Backend:"), QString());
// Cpu Debug
@@ -536,6 +549,7 @@ std::unique_ptr<ComboboxTranslationMap> ComboboxEnumeration(QWidget* parent)
PAIR(ScalingFilter, Gaussian, tr("Gaussian")),
PAIR(ScalingFilter, ScaleForce, tr("ScaleForce")),
PAIR(ScalingFilter, Fsr, tr("AMD FidelityFX™ Super Resolution")),
PAIR(ScalingFilter, Area, tr("Area")),
}});
translations->insert({Settings::EnumMetadata<Settings::AntiAliasing>::Index(),
{

View File

@@ -1,3 +1,9 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2024 Torzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -40,6 +46,7 @@ static const std::map<Settings::ScalingFilter, QString> scaling_filter_texts_map
{Settings::ScalingFilter::ScaleForce,
QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "ScaleForce"))},
{Settings::ScalingFilter::Fsr, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "FSR"))},
{Settings::ScalingFilter::Area, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Area"))},
};
static const std::map<Settings::ConsoleMode, QString> use_docked_mode_texts_map = {

View File

@@ -371,40 +371,6 @@ GameList::GameList(FileSys::VirtualFilesystem vfs_, FileSys::ManualContentProvid
layout->setContentsMargins(0, 0, 0, 0);
layout->setSpacing(0);
warning_layout = new QHBoxLayout;
pre_alpha_warning = new QLabel;
pre_alpha_warning->setText(
tr("IMPORTANT: Eden is PRE-ALPHA SOFTWARE. "
"Bugs and unfinished features are expected to be present at this stage."));
pre_alpha_warning->setWordWrap(true);
pre_alpha_warning->setOpenExternalLinks(true);
pre_alpha_warning->setStyleSheet(
QString::fromStdString("color: black; font-weight: bold;"));
warning_dont_show_again = new QPushButton(this);
warning_dont_show_again->setStyleSheet(
QString::fromStdString("color: #DFDFDF; background-color: #383838;"));
warning_dont_show_again->setText(tr("Don't Show Again"));
connect(warning_dont_show_again, &QPushButton::clicked, this, [=, this] {
Settings::values.hide_pre_alpha_warning.SetValue(true);
layout->removeWidget(warning_widget);
warning_widget->hide();
});
warning_layout->addWidget(pre_alpha_warning, 1);
warning_layout->addWidget(warning_dont_show_again);
warning_layout->setContentsMargins(3, 3, 3, 3);
warning_widget = new QWidget;
warning_widget->setStyleSheet(QString::fromStdString("background-color: khaki;"));
warning_widget->setLayout(warning_layout);
if (!Settings::values.hide_pre_alpha_warning.GetValue()) {
layout->addWidget(warning_widget);
} else {
warning_widget->hide();
}
layout->addWidget(tree_view);
layout->addWidget(search_field);
setLayout(layout);

View File

@@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: 2015 Citra Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -176,11 +179,6 @@ private:
ControllerNavigation* controller_navigation = nullptr;
CompatibilityList compatibility_list;
QHBoxLayout* warning_layout = nullptr;
QWidget* warning_widget = nullptr;
QPushButton* warning_dont_show_again = nullptr;
QLabel* pre_alpha_warning = nullptr;
friend class GameListSearchField;
const PlayTime::PlayTimeManager& play_time_manager;

View File

@@ -2407,6 +2407,7 @@ void GMainWindow::OnGameListOpenFolder(u64 program_id, GameListOpenTarget target
ASSERT_MSG(has_user_save != has_device_save, "Game uses both user and device savedata?");
// TODO(alekpop): It returns the wrong user
switch (target) {
case GameListOpenTarget::SaveData: {
open_target = tr("Save Data");