Compare commits

...

18 commits

Author SHA1 Message Date
MaranBr
8d8d1cf66a Minor adjustment 2026-03-26 02:47:22 +01:00
MaranBr
0c2934a6a0 Relax a little bit 2026-03-26 02:47:22 +01:00
MaranBr
71c1d04c96 ?ploo 2026-03-26 02:47:22 +01:00
MaranBr
319cf77d22 Again 2026-03-26 02:47:22 +01:00
MaranBr
87db29b831 ?ploo 2026-03-26 02:47:22 +01:00
MaranBr
1bd64e7a6b Relax GC if it's not in High Priority mode 2026-03-26 02:47:22 +01:00
MaranBr
13a4ab6ff3 Reapply "Simplify TextureCache GC and remove redundant code" 2026-03-26 02:47:22 +01:00
xbzk
f0d77e86e3
[android,ui] driver management: fixed driver add/removal unsync (#3757)
Some checks are pending
tx-src / sources (push) Waiting to run
Check Strings / check-strings (push) Waiting to run
Complementary for 3750.
User found a way to get same driver doubled and deleting one would lead to a crash.
Reason: manual driver install was still adding drivers directly to adapter, instead of thru drivermodel. fixed.
Also added guards against crash upon driver removal.
Thoroughly tested.

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3757
Reviewed-by: Maufeat <sahyno1996@gmail.com>
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
Co-authored-by: xbzk <xbzk@eden-emu.dev>
Co-committed-by: xbzk <xbzk@eden-emu.dev>
2026-03-25 12:13:04 +01:00
lizzie
24fe223692
[dynarmic] Remove last FPT LUT table, removing around 30kb worth of unused functions (#3718)
Some checks are pending
tx-src / sources (push) Waiting to run
Check Strings / check-strings (push) Waiting to run
Lets do the quick math
There was 1 LUT for every fsize() instancing

Now... the number of functions on each lut was (fsize + 1), multiplied by 5 (number of rounding modes)

8 = 9 * 5 = 45
16 = 17 * 5 = 85
32 = 33 * 5 = 165
64 = 65 * 5 = 325

this is just pure insanity - look at what fucking nm reported:

```
0000000003dc39b8 0000000000000008 V guard variable for void Dynarmic::Backend::X64::EmitFPVectorToFixed<16ul, false>(Dynarmic::Backend::X64::BlockOfCode&, Dynarmic::Backend::X64::EmitContext&, Dynarmic::IR::Inst*)::lut
0000000003dc3a18 0000000000000008 V guard variable for void Dynarmic::Backend::X64::EmitFPVectorToFixed<16ul, true>(Dynarmic::Backend::X64::BlockOfCode&, Dynarmic::Backend::X64::EmitContext&, Dynarmic::IR::Inst*)::lut
0000000003dc39d8 0000000000000008 V guard variable for void Dynarmic::Backend::X64::EmitFPVectorToFixed<32ul, false>(Dynarmic::Backend::X64::BlockOfCode&, Dynarmic::Backend::X64::EmitContext&, Dynarmic::IR::Inst*)::lut
0000000003dc3a38 0000000000000008 V guard variable for void Dynarmic::Backend::X64::EmitFPVectorToFixed<32ul, true>(Dynarmic::Backend::X64::BlockOfCode&, Dynarmic::Backend::X64::EmitContext&, Dynarmic::IR::Inst*)::lut
0000000003dc39f8 0000000000000008 V guard variable for void Dynarmic::Backend::X64::EmitFPVectorToFixed<64ul, false>(Dynarmic::Backend::X64::BlockOfCode&, Dynarmic::Backend::X64::EmitContext&, Dynarmic::IR::Inst*)::lut
0000000003dc3a58 0000000000000008 V guard variable for void Dynarmic::Backend::X64::EmitFPVectorToFixed<64ul, true>(Dynarmic::Backend::X64::BlockOfCode&, Dynarmic::Backend::X64::EmitContext&, Dynarmic::IR::Inst*)::lut
```

"ah its not bad" - OH MATE ITS JUST THE GUARD VARIABLES - i attached a file with just the functions generated for each case...

now with this PR only 6 * 6 functions are made (still not ideal, but way better), 36 is way better than 1156 FUCKING FUNCTIONS

Signed-off-by: lizzie <lizzie@eden-emu.dev>

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3718
Reviewed-by: DraVee <chimera@dravee.dev>
Reviewed-by: Maufeat <sahyno1996@gmail.com>
Co-authored-by: lizzie <lizzie@eden-emu.dev>
Co-committed-by: lizzie <lizzie@eden-emu.dev>
2026-03-25 10:48:53 +01:00
PavelBARABANOV
8f770618d2
[android] Rework of frame pacing mode + Surface mode detection per API level. (#3735)
Some checks failed
tx-src / sources (push) Has been cancelled
Check Strings / check-strings (push) Has been cancelled
This Pr is a reply to certain issues found on Android due to the new artificial waits inside Vulkan (Frame Pacing Mode); which caused GPU/CPU desync's even if TimelineSemaphore (Adreno's drivers) does a constant check to retain synchronization with each frame-data, removes the yield() for all platforms (remains the same on PC) and aligns a new way to handle the output of video by using native Android tools, such as AGP, which makes a bridge inside Vulkan to Android's Surface (screen) and reduces not only the latency, but also improves the smoothness of each frame processed; currently we quantize the amount of frame processed by hinting the surface on Android space and adjust the heuristics of the old handling (yuzu) and we link it to screen refresh rate; this way we ensure that even if the game moves below the screen's HZ, we can always pick up the cadence by clamping the duration of each frame and using a chrono function to work as internal fernce if performance goes below the game speed requirment or game's frame rate requirements.

Co-authored-by: CamilleLaVey <camillelavey99@gmail.com>
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3735
Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
Reviewed-by: Lizzie <lizzie@eden-emu.dev>
Co-authored-by: PavelBARABANOV <pavelbarabanov94@gmail.com>
Co-committed-by: PavelBARABANOV <pavelbarabanov94@gmail.com>
2026-03-24 01:25:44 +01:00
lizzie
b673dad40d
[hle/service/nifm] fix pack(pop) warning on clang (#3764)
Some checks are pending
tx-src / sources (push) Waiting to run
Check Strings / check-strings (push) Waiting to run
fucks up unity builds, also it's an innocuous trivial change for a warning that should've been fixed a while ago

Signed-off-by: lizzie <lizzie@eden-emu.dev>
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3764
Reviewed-by: Maufeat <sahyno1996@gmail.com>
Reviewed-by: crueter <crueter@eden-emu.dev>
Co-authored-by: lizzie <lizzie@eden-emu.dev>
Co-committed-by: lizzie <lizzie@eden-emu.dev>
2026-03-23 18:03:54 +01:00
lizzie
56d3f0e353
[dynarmic] fix dynarmic_tests build issues on xcode due to using relative paths (#3765)
Thanks to @chrelliott978 for the initial impl

Signed-off-by: lizzie <lizzie@eden-emu.dev>

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3765
Reviewed-by: Maufeat <sahyno1996@gmail.com>
Reviewed-by: crueter <crueter@eden-emu.dev>
Co-authored-by: lizzie <lizzie@eden-emu.dev>
Co-committed-by: lizzie <lizzie@eden-emu.dev>
2026-03-23 18:03:48 +01:00
crueter
ad58ab8976
[externals] Use Eden mirrors for llvm-mingw and tzdb (#3766)
My server is getting hammered, let's just move them here.

Signed-off-by: crueter <crueter@eden-emu.dev>
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3766
2026-03-23 17:05:40 +01:00
lizzie
772e38cb8d
[hle/service/sockets] fix hogwarts legacy crash due to non-blacklisted domain (#3762)
Some checks are pending
tx-src / sources (push) Waiting to run
Check Strings / check-strings (push) Waiting to run
Signed-off-by: lizzie <lizzie@eden-emu.dev>
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3762
Reviewed-by: Maufeat <sahyno1996@gmail.com>
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
Co-authored-by: lizzie <lizzie@eden-emu.dev>
Co-committed-by: lizzie <lizzie@eden-emu.dev>
2026-03-22 20:17:44 +01:00
lizzie
811cc18d74
[hle/acc] fix (false) return where it's just 2 (#3763)
Some checks are pending
tx-src / sources (push) Waiting to run
Check Strings / check-strings (push) Waiting to run
Signed-off-by: lizzie <lizzie@eden-emu.dev>
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3763
Reviewed-by: Maufeat <sahyno1996@gmail.com>
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
Co-authored-by: lizzie <lizzie@eden-emu.dev>
Co-committed-by: lizzie <lizzie@eden-emu.dev>
2026-03-22 17:37:21 +01:00
lizzie
c5b519380c
[externals] update renderdoc to 1.7.0 (#3751)
Some checks are pending
tx-src / sources (push) Waiting to run
Check Strings / check-strings (push) Waiting to run
Signed-off-by: lizzie <lizzie@eden-emu.dev>
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3751
Co-authored-by: lizzie <lizzie@eden-emu.dev>
Co-committed-by: lizzie <lizzie@eden-emu.dev>
2026-03-22 04:00:57 +01:00
xbzk
5ebdb29afd
[android,ui] feat fullscreen app setting (#3676)
why not? i like it a lot on both phone and TV.
toggle in app settings. disabled by default so no hassle.

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3676
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
Co-authored-by: xbzk <xbzk@eden-emu.dev>
Co-committed-by: xbzk <xbzk@eden-emu.dev>
2026-03-22 02:06:45 +01:00
lizzie
96e177702e
[qt_common] remove 109kb worth of duplicate shortcut data (#3756)
Some checks failed
tx-src / sources (push) Has been cancelled
Check Strings / check-strings (push) Has been cancelled
- repeated 32 times
- is like 3432
- 32 * 3432 = 109824 = 109kb
- now it's just 3.4kb

Signed-off-by: lizzie <lizzie@eden-emu.dev>

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3756
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
Co-authored-by: lizzie <lizzie@eden-emu.dev>
Co-committed-by: lizzie <lizzie@eden-emu.dev>
2026-03-20 22:09:14 +01:00
57 changed files with 851 additions and 433 deletions

View file

@ -87,8 +87,8 @@
"bundled": true
},
"llvm-mingw": {
"repo": "misc/llvm-mingw",
"git_host": "git.crueter.xyz",
"repo": "eden-emu/llvm-mingw",
"git_host": "git.eden-emu.dev",
"tag": "%VERSION%",
"version": "20250828",
"artifact": "clang-rt-builtins.tar.zst",

View file

@ -246,12 +246,13 @@
},
"tzdb": {
"package": "nx_tzdb",
"repo": "misc/tzdb_to_nx",
"git_host": "git.crueter.xyz",
"repo": "eden-emu/tzdb_to_nx",
"git_host": "git.eden-emu.dev",
"artifact": "%VERSION%.tar.gz",
"tag": "%VERSION%",
"hash": "dc37a189a44ce8b5c988ca550582431a6c7eadfd3c6e709bee6277116ee803e714333e85c9e6cbb5c69346a14d6f2cc7ed96e8aa09cc5fb8a89f945059651db6",
"version": "121125"
"hash": "cce65a12bf90f4ead43b24a0b95dfad77ac3d9bfbaaf66c55e6701346e7a1e44ca5d2f23f47ee35ee02271eb1082bf1762af207aad9fb236f1c8476812d008ed",
"version": "121125",
"git_version": "230326"
},
"vulkan-headers": {
"repo": "KhronosGroup/Vulkan-Headers",

View file

@ -7,7 +7,7 @@
/******************************************************************************
* The MIT License (MIT)
*
* Copyright (c) 2019-2025 Baldur Karlsson
* Copyright (c) 2015-2026 Baldur Karlsson
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@ -72,6 +72,10 @@ extern "C" {
// truncated version when only a uint64_t is available (e.g. Vulkan tags):
#define RENDERDOC_ShaderDebugMagicValue_truncated 0x48656670eab25520ULL
// this is a magic value for vulkan user tags to indicate which dispatchable API objects are which
// for object annotations
#define RENDERDOC_APIObjectAnnotationHelper 0xfbb3b337b664d0adULL
//////////////////////////////////////////////////////////////////////////////////////////////////
// RenderDoc capture options
//
@ -564,6 +568,128 @@ typedef uint32_t(RENDERDOC_CC *pRENDERDOC_DiscardFrameCapture)(RENDERDOC_DeviceP
// multiple times only the last title will be used.
typedef void(RENDERDOC_CC *pRENDERDOC_SetCaptureTitle)(const char *title);
// Annotations API:
//
// These functions allow you to specify annotations either on a per-command level, or a per-object
// level.
//
// Basic types of annotations are supported, as well as vector versions and references to API objects.
//
// The annotations are stored as keys, with the key being a dot-separated path allowing arbitrary
// nesting and user organisation. The keys are sorted in human order so `foo.2.bar` will be displayed
// before `foo.10.bar` to allow creation of arrays if desired.
//
// Deleting an annotation can be done by assigning an empty value to it.
// the type of an annotation value, or Empty to delete an annotation
typedef enum RENDERDOC_AnnotationType
{
eRENDERDOC_Empty,
eRENDERDOC_Bool,
eRENDERDOC_Int32,
eRENDERDOC_UInt32,
eRENDERDOC_Int64,
eRENDERDOC_UInt64,
eRENDERDOC_Float,
eRENDERDOC_Double,
eRENDERDOC_String,
eRENDERDOC_APIObject,
eRENDERDOC_AnnotationMax = 0x7FFFFFFF,
} RENDERDOC_AnnotationType;
// a union with vector annotation value data
typedef union RENDERDOC_AnnotationVectorValue
{
bool boolean[4];
int32_t int32[4];
int64_t int64[4];
uint32_t uint32[4];
uint64_t uint64[4];
float float32[4];
double float64[4];
} RENDERDOC_AnnotationVectorValue;
// a union with scalar annotation value data
typedef union RENDERDOC_AnnotationValue
{
bool boolean;
int32_t int32;
int64_t int64;
uint32_t uint32;
uint64_t uint64;
float float32;
double float64;
RENDERDOC_AnnotationVectorValue vector;
const char *string;
void *apiObject;
} RENDERDOC_AnnotationValue;
// a struct for specifying a GL object, as we don't have pointers we can use so instead we specify a
// pointer to this struct giving both the type and the name
typedef struct RENDERDOC_GLResourceReference
{
// this is the same GLenum identifier as passed to glObjectLabel
uint32_t identifier;
uint32_t name;
} GLResourceReference;
// simple C++ helpers to avoid the need for a temporary objects for value passing and GL object specification
#ifdef __cplusplus
struct RDGLObjectHelper
{
RENDERDOC_GLResourceReference gl;
RDGLObjectHelper(uint32_t identifier, uint32_t name)
{
gl.identifier = identifier;
gl.name = name;
}
operator RENDERDOC_GLResourceReference *() { return &gl; }
};
struct RDAnnotationHelper
{
RENDERDOC_AnnotationValue val;
RDAnnotationHelper(bool b) { val.boolean = b; }
RDAnnotationHelper(int32_t i) { val.int32 = i; }
RDAnnotationHelper(int64_t i) { val.int64 = i; }
RDAnnotationHelper(uint32_t i) { val.uint32 = i; }
RDAnnotationHelper(uint64_t i) { val.uint64 = i; }
RDAnnotationHelper(float f) { val.float32 = f; }
RDAnnotationHelper(double d) { val.float64 = d; }
RDAnnotationHelper(const char *s) { val.string = s; }
operator RENDERDOC_AnnotationValue *() { return &val; }
};
#endif
// The device is specified in the same way as other API calls that take a RENDERDOC_DevicePointer
// to specify the device.
//
// The object or queue/commandbuffer will depend on the graphics API in question.
//
// Return value:
// 0 - The annotation was applied successfully.
// 1 - The device is unknown/invalid
// 2 - The device is valid but the annotation is not supported for API-specific reasons, such as an
// unrecognised or invalid object or queue/commandbuffer
// 3 - The call is ill-formed or invalid e.g. empty is specified with a value pointer, or non-empty
// is specified with a NULL value pointer
typedef uint32_t(RENDERDOC_CC *pRENDERDOC_SetObjectAnnotation)(RENDERDOC_DevicePointer device,
void *object, const char *key,
RENDERDOC_AnnotationType valueType,
uint32_t valueVectorWidth,
const RENDERDOC_AnnotationValue *value);
typedef uint32_t(RENDERDOC_CC *pRENDERDOC_SetCommandAnnotation)(
RENDERDOC_DevicePointer device, void *queueOrCommandBuffer, const char *key,
RENDERDOC_AnnotationType valueType, uint32_t valueVectorWidth,
const RENDERDOC_AnnotationValue *value);
//////////////////////////////////////////////////////////////////////////////////////////////////
// RenderDoc API versions
//
@ -592,6 +718,7 @@ typedef enum RENDERDOC_Version
eRENDERDOC_API_Version_1_4_2 = 10402, // RENDERDOC_API_1_4_2 = 1 04 02
eRENDERDOC_API_Version_1_5_0 = 10500, // RENDERDOC_API_1_5_0 = 1 05 00
eRENDERDOC_API_Version_1_6_0 = 10600, // RENDERDOC_API_1_6_0 = 1 06 00
eRENDERDOC_API_Version_1_7_0 = 10700, // RENDERDOC_API_1_7_0 = 1 07 00
} RENDERDOC_Version;
// API version changelog:
@ -622,8 +749,10 @@ typedef enum RENDERDOC_Version
// 1.5.0 - Added feature: ShowReplayUI() to request that the replay UI show itself if connected
// 1.6.0 - Added feature: SetCaptureTitle() which can be used to set a title for a
// capture made with StartFrameCapture() or EndFrameCapture()
// 1.7.0 - Added feature: SetObjectAnnotation() / SetCommandAnnotation() for adding rich
// annotations to objects and command streams
typedef struct RENDERDOC_API_1_6_0
typedef struct RENDERDOC_API_1_7_0
{
pRENDERDOC_GetAPIVersion GetAPIVersion;
@ -701,20 +830,25 @@ typedef struct RENDERDOC_API_1_6_0
// new function in 1.6.0
pRENDERDOC_SetCaptureTitle SetCaptureTitle;
} RENDERDOC_API_1_6_0;
typedef RENDERDOC_API_1_6_0 RENDERDOC_API_1_0_0;
typedef RENDERDOC_API_1_6_0 RENDERDOC_API_1_0_1;
typedef RENDERDOC_API_1_6_0 RENDERDOC_API_1_0_2;
typedef RENDERDOC_API_1_6_0 RENDERDOC_API_1_1_0;
typedef RENDERDOC_API_1_6_0 RENDERDOC_API_1_1_1;
typedef RENDERDOC_API_1_6_0 RENDERDOC_API_1_1_2;
typedef RENDERDOC_API_1_6_0 RENDERDOC_API_1_2_0;
typedef RENDERDOC_API_1_6_0 RENDERDOC_API_1_3_0;
typedef RENDERDOC_API_1_6_0 RENDERDOC_API_1_4_0;
typedef RENDERDOC_API_1_6_0 RENDERDOC_API_1_4_1;
typedef RENDERDOC_API_1_6_0 RENDERDOC_API_1_4_2;
typedef RENDERDOC_API_1_6_0 RENDERDOC_API_1_5_0;
// new functions in 1.7.0
pRENDERDOC_SetObjectAnnotation SetObjectAnnotation;
pRENDERDOC_SetCommandAnnotation SetCommandAnnotation;
} RENDERDOC_API_1_7_0;
typedef RENDERDOC_API_1_7_0 RENDERDOC_API_1_0_0;
typedef RENDERDOC_API_1_7_0 RENDERDOC_API_1_0_1;
typedef RENDERDOC_API_1_7_0 RENDERDOC_API_1_0_2;
typedef RENDERDOC_API_1_7_0 RENDERDOC_API_1_1_0;
typedef RENDERDOC_API_1_7_0 RENDERDOC_API_1_1_1;
typedef RENDERDOC_API_1_7_0 RENDERDOC_API_1_1_2;
typedef RENDERDOC_API_1_7_0 RENDERDOC_API_1_2_0;
typedef RENDERDOC_API_1_7_0 RENDERDOC_API_1_3_0;
typedef RENDERDOC_API_1_7_0 RENDERDOC_API_1_4_0;
typedef RENDERDOC_API_1_7_0 RENDERDOC_API_1_4_1;
typedef RENDERDOC_API_1_7_0 RENDERDOC_API_1_4_2;
typedef RENDERDOC_API_1_7_0 RENDERDOC_API_1_5_0;
typedef RENDERDOC_API_1_7_0 RENDERDOC_API_1_6_0;
//////////////////////////////////////////////////////////////////////////////////////////////////
// RenderDoc API entry point

View file

@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
package org.yuzu.yuzu_emu.dialogs
@ -20,6 +20,8 @@ import org.yuzu.yuzu_emu.databinding.DialogChatBinding
import org.yuzu.yuzu_emu.databinding.ItemChatMessageBinding
import org.yuzu.yuzu_emu.features.settings.model.StringSetting
import org.yuzu.yuzu_emu.network.NetPlayManager
import org.yuzu.yuzu_emu.utils.CompatUtils
import org.yuzu.yuzu_emu.utils.FullscreenHelper
import java.text.SimpleDateFormat
import java.util.*
@ -34,6 +36,13 @@ class ChatDialog(context: Context) : BottomSheetDialog(context) {
private lateinit var binding: DialogChatBinding
private lateinit var chatAdapter: ChatAdapter
private val handler = Handler(Looper.getMainLooper())
private val hideSystemBars: Boolean by lazy {
runCatching {
FullscreenHelper.shouldHideSystemBars(CompatUtils.findActivity(context))
}.getOrElse {
FullscreenHelper.isFullscreenEnabled(context)
}
}
// TODO(alekpop, crueter): Top drawer for message notifications, perhaps use system notifs?
// TODO(alekpop, crueter): Context menu actions for chat users
@ -41,6 +50,7 @@ class ChatDialog(context: Context) : BottomSheetDialog(context) {
@SuppressLint("NotifyDataSetChanged")
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setOnShowListener { applyFullscreenMode() }
binding = DialogChatBinding.inflate(LayoutInflater.from(context))
setContentView(binding.root)
@ -75,6 +85,11 @@ class ChatDialog(context: Context) : BottomSheetDialog(context) {
}
}
override fun onStart() {
super.onStart()
applyFullscreenMode()
}
override fun dismiss() {
NetPlayManager.setChatOpen(false)
super.dismiss()
@ -108,6 +123,12 @@ class ChatDialog(context: Context) : BottomSheetDialog(context) {
private fun scrollToBottom() {
binding.chatRecyclerView.scrollToPosition(chatAdapter.itemCount - 1)
}
private fun applyFullscreenMode() {
window?.let { window ->
FullscreenHelper.applyToWindow(window, hideSystemBars)
}
}
}
class ChatAdapter(private val messages: List<ChatMessage>) :

View file

@ -31,15 +31,25 @@ import org.yuzu.yuzu_emu.databinding.DialogLobbyBrowserBinding
import org.yuzu.yuzu_emu.databinding.ItemLobbyRoomBinding
import org.yuzu.yuzu_emu.features.settings.model.StringSetting
import org.yuzu.yuzu_emu.network.NetPlayManager
import org.yuzu.yuzu_emu.utils.CompatUtils
import org.yuzu.yuzu_emu.utils.FullscreenHelper
import java.util.Locale
class LobbyBrowser(context: Context) : BottomSheetDialog(context) {
private lateinit var binding: DialogLobbyBrowserBinding
private lateinit var adapter: LobbyRoomAdapter
private val handler = Handler(Looper.getMainLooper())
private val hideSystemBars: Boolean by lazy {
runCatching {
FullscreenHelper.shouldHideSystemBars(CompatUtils.findActivity(context))
}.getOrElse {
FullscreenHelper.isFullscreenEnabled(context)
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setOnShowListener { applyFullscreenMode() }
behavior.state = BottomSheetBehavior.STATE_EXPANDED
behavior.skipCollapsed =
@ -81,6 +91,7 @@ class LobbyBrowser(context: Context) : BottomSheetDialog(context) {
behavior.expandedOffset = 0
behavior.skipCollapsed = true
behavior.state = BottomSheetBehavior.STATE_EXPANDED
applyFullscreenMode()
}
private fun setupRecyclerView() {
@ -274,4 +285,10 @@ class LobbyBrowser(context: Context) : BottomSheetDialog(context) {
}
private inner class ScoreItem(val score: Double, val item: NetPlayManager.RoomInfo)
private fun applyFullscreenMode() {
window?.let { window ->
FullscreenHelper.applyToWindow(window, hideSystemBars)
}
}
}

View file

@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
package org.yuzu.yuzu_emu.dialogs
@ -36,6 +36,7 @@ import org.yuzu.yuzu_emu.features.settings.model.StringSetting
import org.yuzu.yuzu_emu.network.NetDataValidators
import org.yuzu.yuzu_emu.network.NetPlayManager
import org.yuzu.yuzu_emu.utils.CompatUtils
import org.yuzu.yuzu_emu.utils.FullscreenHelper
import org.yuzu.yuzu_emu.utils.GameHelper
class NetPlayDialog(context: Context) : BottomSheetDialog(context) {
@ -43,9 +44,17 @@ class NetPlayDialog(context: Context) : BottomSheetDialog(context) {
private val gameNameList: MutableList<Array<String>> = mutableListOf()
private val gameIdList: MutableList<Array<Long>> = mutableListOf()
private val hideSystemBars: Boolean by lazy {
runCatching {
FullscreenHelper.shouldHideSystemBars(CompatUtils.findActivity(context))
}.getOrElse {
FullscreenHelper.isFullscreenEnabled(context)
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setOnShowListener { applyFullscreenMode() }
behavior.state = BottomSheetBehavior.STATE_EXPANDED
behavior.state = BottomSheetBehavior.STATE_EXPANDED
@ -118,6 +127,11 @@ class NetPlayDialog(context: Context) : BottomSheetDialog(context) {
}
}
override fun onStart() {
super.onStart()
applyFullscreenMode()
}
data class NetPlayItems(
val option: Int,
val name: String,
@ -352,6 +366,11 @@ class NetPlayDialog(context: Context) : BottomSheetDialog(context) {
TextValidatorWatcher.validStates.clear()
val activity = CompatUtils.findActivity(context)
val dialog = BottomSheetDialog(activity)
dialog.setOnShowListener {
dialog.window?.let { window ->
FullscreenHelper.applyToWindow(window, hideSystemBars)
}
}
dialog.behavior.state = BottomSheetBehavior.STATE_EXPANDED
dialog.behavior.state = BottomSheetBehavior.STATE_EXPANDED
@ -582,6 +601,12 @@ class NetPlayDialog(context: Context) : BottomSheetDialog(context) {
dialog.show()
}
private fun applyFullscreenMode() {
window?.let { window ->
FullscreenHelper.applyToWindow(window, hideSystemBars)
}
}
private fun showModerationDialog() {
val activity = CompatUtils.findActivity(context)
val dialog = MaterialAlertDialogBuilder(activity)

View file

@ -104,6 +104,8 @@ object Settings {
const val PREF_THEME_MODE = "ThemeMode"
const val PREF_BLACK_BACKGROUNDS = "BlackBackgrounds"
const val PREF_STATIC_THEME_COLOR = "StaticThemeColor"
const val PREF_APP_FULLSCREEN = "AppFullscreen"
const val APP_FULLSCREEN_DEFAULT = false
enum class EmulationOrientation(val int: Int) {
Unspecified(0),

View file

@ -670,15 +670,6 @@ abstract class SettingsItem(
valuesId = R.array.dmaAccuracyValues
)
)
put(
SingleChoiceSetting(
IntSetting.FRAME_PACING_MODE,
titleId = R.string.frame_pacing_mode,
descriptionId = R.string.frame_pacing_mode_description,
choicesId = R.array.framePacingModeNames,
valuesId = R.array.framePacingModeValues
)
)
put(
SwitchSetting(
BooleanSetting.RENDERER_ASYNCHRONOUS_SHADERS,

View file

@ -103,6 +103,7 @@ class SettingsActivity : AppCompatActivity() {
)
setInsets()
applyFullscreenPreference()
}
fun navigateBack() {
@ -122,6 +123,18 @@ class SettingsActivity : AppCompatActivity() {
}
}
override fun onResume() {
super.onResume()
applyFullscreenPreference()
}
override fun onWindowFocusChanged(hasFocus: Boolean) {
super.onWindowFocusChanged(hasFocus)
if (hasFocus) {
applyFullscreenPreference()
}
}
override fun onStop() {
super.onStop()
Log.info("[SettingsActivity] Settings activity stopping. Saving settings to INI...")
@ -188,4 +201,8 @@ class SettingsActivity : AppCompatActivity() {
windowInsets
}
}
private fun applyFullscreenPreference() {
FullscreenHelper.applyToActivity(this)
}
}

View file

@ -29,6 +29,8 @@ import org.yuzu.yuzu_emu.features.settings.model.view.*
import org.yuzu.yuzu_emu.utils.InputHandler
import org.yuzu.yuzu_emu.utils.NativeConfig
import org.yuzu.yuzu_emu.utils.DirectoryInitialization
import org.yuzu.yuzu_emu.utils.FullscreenHelper
import androidx.core.content.edit
import androidx.fragment.app.FragmentActivity
import org.yuzu.yuzu_emu.fragments.MessageDialogFragment
@ -254,7 +256,6 @@ class SettingsFragmentPresenter(
add(IntSetting.RENDERER_ACCURACY.key)
add(IntSetting.DMA_ACCURACY.key)
add(IntSetting.FRAME_PACING_MODE.key)
add(IntSetting.MAX_ANISOTROPY.key)
add(IntSetting.RENDERER_VRAM_USAGE_MODE.key)
add(IntSetting.RENDERER_ASTC_DECODE_METHOD.key)
@ -1187,6 +1188,39 @@ class SettingsFragmentPresenter(
)
)
val fullscreenSetting: AbstractBooleanSetting = object : AbstractBooleanSetting {
override fun getBoolean(needsGlobal: Boolean): Boolean =
FullscreenHelper.isFullscreenEnabled(context)
override fun setBoolean(value: Boolean) {
FullscreenHelper.setFullscreenEnabled(context, value)
settingsViewModel.setShouldRecreate(true)
}
override val key: String = Settings.PREF_APP_FULLSCREEN
override val isRuntimeModifiable: Boolean = true
override val pairedSettingKey: String = ""
override val isSwitchable: Boolean = false
override var global: Boolean = true
override val isSaveable: Boolean = true
override val defaultValue: Boolean = Settings.APP_FULLSCREEN_DEFAULT
override fun getValueAsString(needsGlobal: Boolean): String =
getBoolean(needsGlobal).toString()
override fun reset() {
setBoolean(defaultValue)
}
}
add(
SwitchSetting(
fullscreenSetting,
titleId = R.string.fullscreen_mode,
descriptionId = R.string.fullscreen_mode_description
)
)
add(HeaderSetting(R.string.buttons))
add(BooleanSetting.ENABLE_FOLDER_BUTTON.key)
add(BooleanSetting.ENABLE_QLAUNCH_BUTTON.key)

View file

@ -20,6 +20,7 @@ import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.YuzuApplication
import org.yuzu.yuzu_emu.databinding.ActivitySettingsBinding
import org.yuzu.yuzu_emu.utils.DirectoryInitialization
import org.yuzu.yuzu_emu.utils.FullscreenHelper
import org.yuzu.yuzu_emu.utils.InsetsHelper
import org.yuzu.yuzu_emu.utils.ThemeHelper
@ -89,6 +90,7 @@ class SettingsSubscreenActivity : AppCompatActivity() {
)
setInsets()
applyFullscreenPreference()
}
override fun onStart() {
@ -98,6 +100,18 @@ class SettingsSubscreenActivity : AppCompatActivity() {
}
}
override fun onResume() {
super.onResume()
applyFullscreenPreference()
}
override fun onWindowFocusChanged(hasFocus: Boolean) {
super.onWindowFocusChanged(hasFocus)
if (hasFocus) {
applyFullscreenPreference()
}
}
fun navigateBack() {
val navHostFragment =
supportFragmentManager.findFragmentById(R.id.fragment_container) as NavHostFragment
@ -149,4 +163,8 @@ class SettingsSubscreenActivity : AppCompatActivity() {
windowInsets
}
}
private fun applyFullscreenPreference() {
FullscreenHelper.applyToActivity(this)
}
}

View file

@ -29,7 +29,6 @@ import org.yuzu.yuzu_emu.databinding.FragmentDriverManagerBinding
import org.yuzu.yuzu_emu.features.settings.model.Settings
import org.yuzu.yuzu_emu.features.settings.model.StringSetting
import org.yuzu.yuzu_emu.features.settings.ui.SettingsSubscreen
import org.yuzu.yuzu_emu.model.Driver.Companion.toDriver
import org.yuzu.yuzu_emu.model.DriverViewModel
import org.yuzu.yuzu_emu.model.HomeViewModel
import org.yuzu.yuzu_emu.utils.FileUtil
@ -216,19 +215,23 @@ class DriverManagerFragment : Fragment() {
val driverData = GpuDriverHelper.getMetadataFromZip(driverFile)
val driverInList =
driverViewModel.driverData.firstOrNull { it.second == driverData }
driverViewModel.driverData.firstOrNull {
it.first == driverPath || it.second == driverData
}
if (driverInList != null) {
return@newInstance getString(R.string.driver_already_installed)
} else {
driverViewModel.onDriverAdded(Pair(driverPath, driverData))
withContext(Dispatchers.Main) {
if (_binding != null) {
refreshDriverList()
val adapter = binding.listDrivers.adapter as DriverAdapter
adapter.addItem(driverData.toDriver())
adapter.selectItem(adapter.currentList.indices.last)
val selectedPosition = adapter.currentList
.indexOfFirst { it.selected }
.let { if (it == -1) 0 else it }
driverViewModel.showClearButton(!StringSetting.DRIVER_PATH.global)
binding.listDrivers
.smoothScrollToPosition(adapter.currentList.indices.last)
.smoothScrollToPosition(selectedPosition)
}
}
}

View file

@ -9,6 +9,7 @@ import android.view.View
import android.view.ViewGroup
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import androidx.core.view.updatePadding
import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
import androidx.navigation.fragment.findNavController
@ -22,6 +23,7 @@ import org.yuzu.yuzu_emu.databinding.FragmentProfileManagerBinding
import org.yuzu.yuzu_emu.model.HomeViewModel
import org.yuzu.yuzu_emu.model.UserProfile
import org.yuzu.yuzu_emu.utils.NativeConfig
import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins
class ProfileManagerFragment : Fragment() {
private var _binding: FragmentProfileManagerBinding? = null
@ -172,11 +174,19 @@ class ProfileManagerFragment : Fragment() {
val leftInsets = barInsets.left + cutoutInsets.left
val rightInsets = barInsets.right + cutoutInsets.right
val fabLayoutParams = binding.buttonAddUser.layoutParams as ViewGroup.MarginLayoutParams
fabLayoutParams.leftMargin = leftInsets + 24
fabLayoutParams.rightMargin = rightInsets + 24
fabLayoutParams.bottomMargin = barInsets.bottom + 24
binding.buttonAddUser.layoutParams = fabLayoutParams
binding.toolbarProfiles.updateMargins(left = leftInsets, right = rightInsets)
binding.listProfiles.updateMargins(left = leftInsets, right = rightInsets)
binding.listProfiles.updatePadding(
bottom = barInsets.bottom +
resources.getDimensionPixelSize(R.dimen.spacing_bottom_list_fab)
)
val fabSpacing = resources.getDimensionPixelSize(R.dimen.spacing_fab)
binding.buttonAddUser.updateMargins(
left = leftInsets + fabSpacing,
right = rightInsets + fabSpacing,
bottom = barInsets.bottom + fabSpacing
)
windowInsets
}

View file

@ -154,15 +154,30 @@ class DriverViewModel : ViewModel() {
}
fun onDriverRemoved(removedPosition: Int, selectedPosition: Int) {
driversToDelete.add(driverData[removedPosition - 1].first)
driverData.removeAt(removedPosition - 1)
onDriverSelected(selectedPosition)
val driverIndex = removedPosition - 1
if (driverIndex !in driverData.indices) {
updateDriverList()
return
}
driversToDelete.add(driverData[driverIndex].first)
driverData.removeAt(driverIndex)
val safeSelectedPosition = selectedPosition.coerceIn(0, driverData.size)
onDriverSelected(safeSelectedPosition)
}
fun onDriverAdded(driver: Pair<String, GpuDriverMetadata>) {
if (driversToDelete.contains(driver.first)) {
driversToDelete.remove(driver.first)
}
val existingDriverIndex = driverData.indexOfFirst {
it.first == driver.first || it.second == driver.second
}
if (existingDriverIndex != -1) {
onDriverSelected(existingDriverIndex + 1)
return
}
driverData.add(driver)
onDriverSelected(driverData.size)
}

View file

@ -86,8 +86,6 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
binding = ActivityMainBinding.inflate(layoutInflater)
// Since Android 15, google automatically forces "games" to be 60 hrz
// This ensures the display's max refresh rate is actually used
display?.let {
val supportedModes = it.supportedModes
val maxRefreshRate = supportedModes.maxByOrNull { mode -> mode.refreshRate }
@ -169,6 +167,7 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
checkForUpdates()
}
setInsets()
applyFullscreenPreference()
}
private fun checkForUpdates() {
@ -345,6 +344,14 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
override fun onResume() {
ThemeHelper.setCorrectTheme(this)
super.onResume()
applyFullscreenPreference()
}
override fun onWindowFocusChanged(hasFocus: Boolean) {
super.onWindowFocusChanged(hasFocus)
if (hasFocus) {
applyFullscreenPreference()
}
}
private fun setInsets() = ViewCompat.setOnApplyWindowInsetsListener(
@ -364,6 +371,10 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
windowInsets
}
private fun applyFullscreenPreference() {
FullscreenHelper.applyToActivity(this)
}
override fun setTheme(resId: Int) {
super.setTheme(resId)
themeId = resId

View file

@ -0,0 +1,52 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
package org.yuzu.yuzu_emu.utils
import android.app.Activity
import android.content.Context
import android.view.Window
import androidx.core.content.edit
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import androidx.core.view.WindowInsetsControllerCompat
import androidx.preference.PreferenceManager
import org.yuzu.yuzu_emu.features.settings.model.Settings
object FullscreenHelper {
fun isFullscreenEnabled(context: Context): Boolean {
return PreferenceManager.getDefaultSharedPreferences(context).getBoolean(
Settings.PREF_APP_FULLSCREEN,
Settings.APP_FULLSCREEN_DEFAULT
)
}
fun setFullscreenEnabled(context: Context, enabled: Boolean) {
PreferenceManager.getDefaultSharedPreferences(context).edit {
putBoolean(Settings.PREF_APP_FULLSCREEN, enabled)
}
}
fun shouldHideSystemBars(activity: Activity): Boolean {
val rootInsets = ViewCompat.getRootWindowInsets(activity.window.decorView)
val barsCurrentlyHidden =
rootInsets?.isVisible(WindowInsetsCompat.Type.systemBars())?.not() ?: false
return isFullscreenEnabled(activity) || barsCurrentlyHidden
}
fun applyToWindow(window: Window, hideSystemBars: Boolean) {
val controller = WindowInsetsControllerCompat(window, window.decorView)
if (hideSystemBars) {
controller.hide(WindowInsetsCompat.Type.systemBars())
controller.systemBarsBehavior =
WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
} else {
controller.show(WindowInsetsCompat.Type.systemBars())
}
}
fun applyToActivity(activity: Activity) {
applyToWindow(activity.window, isFullscreenEnabled(activity))
}
}

View file

@ -6,8 +6,15 @@
#include <android/native_window_jni.h>
#include <algorithm>
#include <array>
#include <cmath>
#include <cstdint>
#include <dlfcn.h>
#include "common/android/id_cache.h"
#include "common/logging.h"
#include "common/settings.h"
#include "input_common/drivers/android.h"
#include "input_common/drivers/touch_screen.h"
#include "input_common/drivers/virtual_amiibo.h"
@ -22,6 +29,12 @@ void EmuWindow_Android::OnSurfaceChanged(ANativeWindow* surface) {
m_window_width = 0;
m_window_height = 0;
window_info.render_surface = nullptr;
m_last_frame_rate_hint = -1.0f;
m_pending_frame_rate_hint = -1.0f;
m_pending_frame_rate_hint_votes = 0;
m_smoothed_present_rate = 0.0f;
m_last_frame_display_time = {};
m_pending_frame_rate_since = {};
return;
}
@ -32,6 +45,7 @@ void EmuWindow_Android::OnSurfaceChanged(ANativeWindow* surface) {
UpdateCurrentFramebufferLayout(m_window_width, m_window_height);
window_info.render_surface = reinterpret_cast<void*>(surface);
UpdateFrameRateHint();
}
void EmuWindow_Android::OnTouchPressed(int id, float x, float y) {
@ -51,6 +65,9 @@ void EmuWindow_Android::OnTouchReleased(int id) {
}
void EmuWindow_Android::OnFrameDisplayed() {
UpdateObservedFrameRate();
UpdateFrameRateHint();
if (!m_first_frame) {
Common::Android::RunJNIOnFiber<void>(
[&](JNIEnv* env) { EmulationSession::GetInstance().OnEmulationStarted(); });
@ -58,6 +75,175 @@ void EmuWindow_Android::OnFrameDisplayed() {
}
}
void EmuWindow_Android::UpdateObservedFrameRate() {
const auto now = Clock::now();
if (m_last_frame_display_time.time_since_epoch().count() != 0) {
const auto frame_time = std::chrono::duration<float>(now - m_last_frame_display_time);
const float seconds = frame_time.count();
if (seconds > 0.0f) {
const float instantaneous_rate = 1.0f / seconds;
if (std::isfinite(instantaneous_rate) && instantaneous_rate >= 1.0f &&
instantaneous_rate <= 240.0f) {
constexpr float SmoothingFactor = 0.15f;
if (m_smoothed_present_rate <= 0.0f) {
m_smoothed_present_rate = instantaneous_rate;
} else {
m_smoothed_present_rate +=
(instantaneous_rate - m_smoothed_present_rate) * SmoothingFactor;
}
}
}
}
m_last_frame_display_time = now;
}
float EmuWindow_Android::QuantizeFrameRateHint(float frame_rate) {
if (!std::isfinite(frame_rate) || frame_rate <= 0.0f) {
return 0.0f;
}
frame_rate = std::clamp(frame_rate, 1.0f, 240.0f);
constexpr float Step = 0.5f;
return std::round(frame_rate / Step) * Step;
}
float EmuWindow_Android::GetFrameTimeVerifiedHint() const {
if (!EmulationSession::GetInstance().IsRunning()) {
return 0.0f;
}
const double frame_time_scale =
EmulationSession::GetInstance().System().GetPerfStats().GetLastFrameTimeScale();
if (!std::isfinite(frame_time_scale) || frame_time_scale <= 0.0) {
return 0.0f;
}
const float verified_rate =
std::clamp(60.0f / static_cast<float>(frame_time_scale), 0.0f, 240.0f);
return QuantizeFrameRateHint(verified_rate);
}
float EmuWindow_Android::GetFrameRateHint() const {
const float observed_rate = std::clamp(m_smoothed_present_rate, 0.0f, 240.0f);
const float frame_time_verified_hint = GetFrameTimeVerifiedHint();
if (m_last_frame_rate_hint > 0.0f && observed_rate > 0.0f) {
const float tolerance = std::max(m_last_frame_rate_hint * 0.12f, 4.0f);
if (std::fabs(observed_rate - m_last_frame_rate_hint) <= tolerance) {
return m_last_frame_rate_hint;
}
}
const float observed_hint = QuantizeFrameRateHint(observed_rate);
if (observed_hint > 0.0f) {
if (frame_time_verified_hint > 0.0f) {
const float tolerance = std::max(observed_hint * 0.20f, 3.0f);
if (std::fabs(observed_hint - frame_time_verified_hint) <= tolerance) {
return QuantizeFrameRateHint((observed_hint + frame_time_verified_hint) * 0.5f);
}
}
return observed_hint;
}
if (frame_time_verified_hint > 0.0f) {
return frame_time_verified_hint;
}
constexpr float NominalFrameRate = 60.0f;
if (!Settings::values.use_speed_limit.GetValue()) {
return NominalFrameRate;
}
const u16 speed_limit = Settings::SpeedLimit();
if (speed_limit == 0) {
return 0.0f;
}
const float speed_limited_rate =
NominalFrameRate * (static_cast<float>(std::min<u16>(speed_limit, 100)) / 100.0f);
return QuantizeFrameRateHint(speed_limited_rate);
}
void EmuWindow_Android::UpdateFrameRateHint() {
auto* const surface = reinterpret_cast<ANativeWindow*>(window_info.render_surface);
if (!surface) {
return;
}
const auto now = Clock::now();
const float frame_rate_hint = GetFrameRateHint();
if (std::fabs(frame_rate_hint - m_last_frame_rate_hint) < 0.01f) {
m_pending_frame_rate_hint = frame_rate_hint;
m_pending_frame_rate_hint_votes = 0;
m_pending_frame_rate_since = {};
return;
}
if (frame_rate_hint == 0.0f) {
m_pending_frame_rate_hint = frame_rate_hint;
m_pending_frame_rate_hint_votes = 0;
m_pending_frame_rate_since = now;
} else if (m_last_frame_rate_hint >= 0.0f) {
if (std::fabs(frame_rate_hint - m_pending_frame_rate_hint) >= 0.01f) {
m_pending_frame_rate_hint = frame_rate_hint;
m_pending_frame_rate_hint_votes = 1;
m_pending_frame_rate_since = now;
return;
}
++m_pending_frame_rate_hint_votes;
if (m_pending_frame_rate_since.time_since_epoch().count() == 0) {
m_pending_frame_rate_since = now;
}
const auto stable_for = now - m_pending_frame_rate_since;
const float reference_rate = std::max(frame_rate_hint, 1.0f);
const auto stable_duration = std::chrono::duration_cast<Clock::duration>(
std::chrono::duration<float>(std::clamp(3.0f / reference_rate, 0.15f, 0.40f)));
constexpr std::uint32_t MinStableVotes = 3;
if (m_pending_frame_rate_hint_votes < MinStableVotes || stable_for < stable_duration) {
return;
}
} else {
m_pending_frame_rate_since = now;
}
using SetFrameRateWithChangeStrategyFn =
int32_t (*)(ANativeWindow*, float, int8_t, int8_t);
using SetFrameRateFn = int32_t (*)(ANativeWindow*, float, int8_t);
static const auto set_frame_rate_with_change_strategy =
reinterpret_cast<SetFrameRateWithChangeStrategyFn>(
dlsym(RTLD_DEFAULT, "ANativeWindow_setFrameRateWithChangeStrategy"));
static const auto set_frame_rate = reinterpret_cast<SetFrameRateFn>(
dlsym(RTLD_DEFAULT, "ANativeWindow_setFrameRate"));
constexpr int8_t FrameRateCompatibilityDefault = 0;
constexpr int8_t ChangeFrameRateOnlyIfSeamless = 0;
int32_t result = -1;
if (set_frame_rate_with_change_strategy) {
result = set_frame_rate_with_change_strategy(surface, frame_rate_hint,
FrameRateCompatibilityDefault,
ChangeFrameRateOnlyIfSeamless);
} else if (set_frame_rate) {
result = set_frame_rate(surface, frame_rate_hint, FrameRateCompatibilityDefault);
} else {
return;
}
if (result != 0) {
LOG_DEBUG(Frontend, "Failed to update Android surface frame rate hint: {}", result);
return;
}
m_last_frame_rate_hint = frame_rate_hint;
m_pending_frame_rate_hint = frame_rate_hint;
m_pending_frame_rate_hint_votes = 0;
m_pending_frame_rate_since = {};
}
EmuWindow_Android::EmuWindow_Android(ANativeWindow* surface,
std::shared_ptr<Common::DynamicLibrary> driver_library)
: m_driver_library{driver_library} {

View file

@ -1,8 +1,13 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include <chrono>
#include <cstdint>
#include <memory>
#include <span>
@ -50,10 +55,24 @@ public:
};
private:
using Clock = std::chrono::steady_clock;
void UpdateFrameRateHint();
void UpdateObservedFrameRate();
[[nodiscard]] float GetFrameRateHint() const;
[[nodiscard]] float GetFrameTimeVerifiedHint() const;
[[nodiscard]] static float QuantizeFrameRateHint(float frame_rate);
float m_window_width{};
float m_window_height{};
std::shared_ptr<Common::DynamicLibrary> m_driver_library;
bool m_first_frame = false;
float m_last_frame_rate_hint = -1.0f;
float m_pending_frame_rate_hint = -1.0f;
float m_smoothed_present_rate = 0.0f;
Clock::time_point m_last_frame_display_time{};
Clock::time_point m_pending_frame_rate_since{};
std::uint32_t m_pending_frame_rate_hint_votes = 0;
};

View file

@ -5,21 +5,22 @@
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="?attr/colorSurface"
android:fitsSystemWindows="true">
android:background="?attr/colorSurface">
<com.google.android.material.appbar.AppBarLayout
android:id="@+id/appbar"
style="@style/Widget.Eden.TransparentTopAppBarLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:fitsSystemWindows="true">
android:fitsSystemWindows="true"
android:touchscreenBlocksFocus="false">
<com.google.android.material.appbar.MaterialToolbar
android:id="@+id/toolbar_profiles"
style="@style/Widget.Eden.TransparentTopToolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:touchscreenBlocksFocus="false"
app:title="@string/profile_manager"
app:navigationIcon="@drawable/ic_back"
app:titleCentered="false" />

View file

@ -1160,6 +1160,8 @@
<string name="theme_material_you">Material You</string>
<string name="app_settings">App Settings</string>
<string name="theme_and_color">Theme And Color</string>
<string name="fullscreen_mode">Fullscreen mode</string>
<string name="fullscreen_mode_description">Hide Android system bars across app screens. Swipe from an edge to reveal them temporarily.</string>
<!-- Theme Modes -->
<string name="change_theme_mode">Change theme mode</string>

View file

@ -712,9 +712,8 @@ public:
private:
void CheckAvailability(HLERequestContext& ctx) {
LOG_DEBUG(Service_ACC, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 3};
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
rb.Push(false); // TODO: Check when this is supposed to return true and when not
}
void GetAccountId(HLERequestContext& ctx) {

View file

@ -183,8 +183,8 @@ struct NifmWirelessSettingData {
static_assert(sizeof(NifmWirelessSettingData) == 0x70,
"NifmWirelessSettingData has incorrect size.");
#pragma pack(push, 1)
// This is nn::nifm::detail::sf::NetworkProfileData
#pragma pack(push, 1)
struct SfNetworkProfileData {
IpSettingData ip_setting_data{};
u128 uuid{};
@ -196,9 +196,11 @@ struct SfNetworkProfileData {
SfWirelessSettingData wireless_setting_data{};
INSERT_PADDING_BYTES(1);
};
#pragma pack(pop)
static_assert(sizeof(SfNetworkProfileData) == 0x17C, "SfNetworkProfileData has incorrect size.");
// This is nn::nifm::NetworkProfileData
#pragma pack(push, 1)
struct NifmNetworkProfileData {
u128 uuid{};
std::array<char, 0x40> network_name{};
@ -210,8 +212,8 @@ struct NifmNetworkProfileData {
NifmWirelessSettingData wireless_setting_data{};
IpSettingData ip_setting_data{};
};
static_assert(sizeof(NifmNetworkProfileData) == 0x18E,
"NifmNetworkProfileData has incorrect size.");
#pragma pack(pop)
static_assert(sizeof(NifmNetworkProfileData) == 0x18E, "NifmNetworkProfileData has incorrect size.");
struct PendingProfile {
std::array<char, 0x21> ssid{};

View file

@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
@ -53,12 +53,15 @@ enum class NetDbError : s32 {
NoData = 4,
};
static const constexpr std::array blockedDomains = {"srv.nintendo.net",
"battle.net",
"microsoft.com",
"mojang.com",
"xboxlive.com",
"minecraftservices.com"};
static const constexpr std::array blockedDomains = {
"srv.nintendo.net", //obvious
"phoenix-api.wbagora.com", //hogwarts legacy
"battle.net",
"microsoft.com", //minecraft dungeons + other games
"mojang.com",
"xboxlive.com",
"minecraftservices.com"
};
static bool IsBlockedHost(const std::string& host) {
return std::any_of(

View file

@ -1,9 +1,12 @@
// SPDX-FileCopyrightText: Copyright 2026 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
#pragma once
struct RENDERDOC_API_1_6_0;
struct RENDERDOC_API_1_7_0;
namespace Tools {
@ -15,7 +18,7 @@ public:
void ToggleCapture();
private:
RENDERDOC_API_1_6_0* rdoc_api{};
RENDERDOC_API_1_7_0* rdoc_api{};
bool is_capturing{false};
};

View file

@ -53,7 +53,6 @@ add_library(dynarmic STATIC
common/fp/util.h
common/llvm_disassemble.cpp
common/llvm_disassemble.h
common/lut_from_list.h
common/math_util.cpp
common/math_util.h
common/safe_ops.h

View file

@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
/* This file is part of the dynarmic project.
@ -31,7 +31,6 @@
#include "dynarmic/common/fp/info.h"
#include "dynarmic/common/fp/op.h"
#include "dynarmic/common/fp/rounding_mode.h"
#include "dynarmic/common/lut_from_list.h"
#include "dynarmic/ir/basic_block.h"
#include "dynarmic/ir/microinstruction.h"
#include "dynarmic/ir/opcodes.h"

View file

@ -31,7 +31,6 @@
#include "dynarmic/common/fp/info.h"
#include "dynarmic/common/fp/op.h"
#include "dynarmic/common/fp/rounding_mode.h"
#include "dynarmic/common/lut_from_list.h"
#include "dynarmic/interface/optimization_flags.h"
#include "dynarmic/ir/basic_block.h"
#include "dynarmic/ir/microinstruction.h"

View file

@ -31,7 +31,6 @@
#include "dynarmic/common/fp/info.h"
#include "dynarmic/common/fp/op.h"
#include "dynarmic/common/fp/util.h"
#include "dynarmic/common/lut_from_list.h"
#include "dynarmic/interface/optimization_flags.h"
#include "dynarmic/ir/basic_block.h"
#include "dynarmic/ir/microinstruction.h"
@ -2127,28 +2126,42 @@ void EmitFPVectorToFixed(BlockOfCode& code, EmitContext& ctx, IR::Inst* inst) {
}
}
using fbits_list = mp::lift_sequence<std::make_index_sequence<fsize + 1>>;
using rounding_list = mp::list<
mp::lift_value<FP::RoundingMode::ToNearest_TieEven>,
mp::lift_value<FP::RoundingMode::TowardsPlusInfinity>,
mp::lift_value<FP::RoundingMode::TowardsMinusInfinity>,
mp::lift_value<FP::RoundingMode::TowardsZero>,
mp::lift_value<FP::RoundingMode::ToNearest_TieAwayFromZero>>;
static const auto lut = Common::GenerateLookupTableFromList([]<typename I>(I) {
using FPT = mcl::unsigned_integer_of_size<fsize>; // WORKAROUND: For issue 678 on MSVC
return std::pair{
mp::lower_to_tuple_v<I>,
Common::FptrCast([](VectorArray<FPT>& output, const VectorArray<FPT>& input, FP::FPCR fpcr, FP::FPSR& fpsr) {
constexpr size_t fbits = mp::get<0, I>::value;
constexpr FP::RoundingMode rounding_mode = mp::get<1, I>::value;
using FPT = mcl::unsigned_integer_of_size<fsize>; // WORKAROUND: For issue 678 on MSVC
auto const func = [rounding]() -> void(*)(VectorArray<FPT>& output, const VectorArray<FPT>& input, FP::FPCR fpcr, FP::FPSR& fpsr) {
switch (rounding) {
case FP::RoundingMode::ToNearest_TieEven:
return [](VectorArray<FPT>& output, const VectorArray<FPT>& input, FP::FPCR fpcr, FP::FPSR& fpsr) {
for (size_t i = 0; i < output.size(); ++i)
output[i] = FPT(FP::FPToFixed<FPT>(fsize, input[i], fbits, unsigned_, fpcr, rounding_mode, fpsr));
})
};
}, mp::cartesian_product<fbits_list, rounding_list>{});
EmitTwoOpFallback<3>(code, ctx, inst, lut.at(std::make_tuple(fbits, rounding)));
output[i] = FPT(FP::FPToFixed<FPT>(fsize, input[i], fsize, unsigned_, fpcr, FP::RoundingMode::ToNearest_TieEven, fpsr));
};
case FP::RoundingMode::TowardsPlusInfinity:
return [](VectorArray<FPT>& output, const VectorArray<FPT>& input, FP::FPCR fpcr, FP::FPSR& fpsr) {
for (size_t i = 0; i < output.size(); ++i)
output[i] = FPT(FP::FPToFixed<FPT>(fsize, input[i], fsize, unsigned_, fpcr, FP::RoundingMode::TowardsPlusInfinity, fpsr));
};
case FP::RoundingMode::TowardsMinusInfinity:
return [](VectorArray<FPT>& output, const VectorArray<FPT>& input, FP::FPCR fpcr, FP::FPSR& fpsr) {
for (size_t i = 0; i < output.size(); ++i)
output[i] = FPT(FP::FPToFixed<FPT>(fsize, input[i], fsize, unsigned_, fpcr, FP::RoundingMode::TowardsMinusInfinity, fpsr));
};
case FP::RoundingMode::TowardsZero:
return [](VectorArray<FPT>& output, const VectorArray<FPT>& input, FP::FPCR fpcr, FP::FPSR& fpsr) {
for (size_t i = 0; i < output.size(); ++i)
output[i] = FPT(FP::FPToFixed<FPT>(fsize, input[i], fsize, unsigned_, fpcr, FP::RoundingMode::TowardsZero, fpsr));
};
case FP::RoundingMode::ToNearest_TieAwayFromZero:
return [](VectorArray<FPT>& output, const VectorArray<FPT>& input, FP::FPCR fpcr, FP::FPSR& fpsr) {
for (size_t i = 0; i < output.size(); ++i)
output[i] = FPT(FP::FPToFixed<FPT>(fsize, input[i], fsize, unsigned_, fpcr, FP::RoundingMode::ToNearest_TieAwayFromZero, fpsr));
};
case FP::RoundingMode::ToOdd:
return [](VectorArray<FPT>& output, const VectorArray<FPT>& input, FP::FPCR fpcr, FP::FPSR& fpsr) {
for (size_t i = 0; i < output.size(); ++i)
output[i] = FPT(FP::FPToFixed<FPT>(fsize, input[i], fsize, unsigned_, fpcr, FP::RoundingMode::ToOdd, fpsr));
};
}
}();
EmitTwoOpFallback<3>(code, ctx, inst, func);
}
void EmitX64::EmitFPVectorToSignedFixed16(EmitContext& ctx, IR::Inst* inst) {

View file

@ -1,55 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
/* This file is part of the dynarmic project.
* Copyright (c) 2018 MerryMage
* SPDX-License-Identifier: 0BSD
*/
#pragma once
#include <initializer_list>
#include <map>
#include <type_traits>
#include <mcl/mp/metafunction/apply.hpp>
#include <mcl/mp/typelist/list.hpp>
#include <mcl/type_traits/is_instance_of_template.hpp>
#ifdef _MSC_VER
# include <mcl/mp/typelist/head.hpp>
#endif
namespace Dynarmic::Common {
// prevents this function from printing 56,000 character warning messages
#ifdef __GNUC__
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wno-stack-usage"
#endif
#ifdef __clang__
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wno-stack-usage"
#endif
template<typename Function, typename... Values>
inline auto GenerateLookupTableFromList(Function f, mcl::mp::list<Values...>) {
#ifdef _MSC_VER
using PairT = std::invoke_result_t<Function, mcl::mp::head<mcl::mp::list<Values...>>>;
#else
using PairT = std::common_type_t<std::invoke_result_t<Function, Values>...>;
#endif
using MapT = mcl::mp::apply<std::map, PairT>;
static_assert(mcl::is_instance_of_template_v<std::pair, PairT>);
const std::initializer_list<PairT> pair_array{f(Values{})...};
return MapT(pair_array.begin(), pair_array.end());
}
#ifdef __GNUC__
#pragma GCC diagnostic pop
#endif
#ifdef __clang__
#pragma clang diagnostic pop
#endif
} // namespace Dynarmic::Common

View file

@ -20,15 +20,14 @@
#include <mcl/scope_exit.hpp>
#include "dynarmic/common/common_types.h"
#include "../fuzz_util.h"
#include "../rand_int.h"
#include "../unicorn_emu/a32_unicorn.h"
#include "./testenv.h"
#include "../native/testenv.h"
#include "dynarmic/tests/fuzz_util.h"
#include "dynarmic/tests/rand_int.h"
#include "dynarmic/tests/unicorn_emu/a32_unicorn.h"
#include "dynarmic/tests/A32/testenv.h"
#include "dynarmic/tests/native/testenv.h"
#include "dynarmic/common/fp/fpcr.h"
#include "dynarmic/common/fp/fpsr.h"
#include "dynarmic/common/llvm_disassemble.h"
#include "dynarmic/common/variant_util.h"
#include "dynarmic/frontend/A32/ITState.h"
#include "dynarmic/frontend/A32/a32_location_descriptor.h"
#include "dynarmic/frontend/A32/a32_types.h"

View file

@ -19,10 +19,10 @@
#include <mcl/bit/bit_field.hpp>
#include "dynarmic/common/common_types.h"
#include "../rand_int.h"
#include "../unicorn_emu/a32_unicorn.h"
#include "./testenv.h"
#include "../native/testenv.h"
#include "dynarmic/tests/rand_int.h"
#include "dynarmic/tests/unicorn_emu/a32_unicorn.h"
#include "dynarmic/tests/A32/testenv.h"
#include "dynarmic/tests/native/testenv.h"
#include "dynarmic/frontend/A32/FPSCR.h"
#include "dynarmic/frontend/A32/PSR.h"
#include "dynarmic/frontend/A32/a32_location_descriptor.h"
@ -107,7 +107,7 @@ static bool DoesBehaviorMatch(const A32Unicorn<ThumbTestEnv>& uni, const A32::Ji
return std::equal(interp_regs.begin(), interp_regs.end(), jit_regs.begin(), jit_regs.end()) && uni.GetCpsr() == jit.Cpsr() && interp_write_records == jit_write_records;
}
static void RunInstance(size_t run_number, ThumbTestEnv& test_env, A32Unicorn<ThumbTestEnv>& uni, A32::Jit& jit, const ThumbTestEnv::RegisterArray& initial_regs, size_t instruction_count, size_t instructions_to_execute_count) {
static void RunInstance(size_t run_number, ThumbTestEnv& test_env, A32::UserConfig const& config, A32Unicorn<ThumbTestEnv>& uni, A32::Jit& jit, const ThumbTestEnv::RegisterArray& initial_regs, size_t instruction_count, size_t instructions_to_execute_count) {
uni.ClearPageCache();
jit.ClearCache();
@ -145,9 +145,8 @@ static void RunInstance(size_t run_number, ThumbTestEnv& test_env, A32Unicorn<Th
printf("Failed at execution number %zu\n", run_number);
printf("\nInstruction Listing: \n");
for (size_t i = 0; i < instruction_count; i++) {
printf("%04x %s\n", test_env.code_mem[i], A32::DisassembleThumb16(test_env.code_mem[i]).c_str());
}
for (size_t i = 0; i < instruction_count; i++)
printf("%04x\n", test_env.code_mem[i]);
printf("\nInitial Register Listing: \n");
for (size_t i = 0; i < initial_regs.size(); i++) {
@ -175,11 +174,14 @@ static void RunInstance(size_t run_number, ThumbTestEnv& test_env, A32Unicorn<Th
A32::PSR cpsr;
cpsr.T(true);
IR::Block ir_block{A32::LocationDescriptor{0, {}, {}}};
size_t num_insts = 0;
while (num_insts < instructions_to_execute_count) {
A32::LocationDescriptor descriptor = {u32(num_insts * 4), cpsr, A32::FPSCR{}};
IR::Block ir_block = A32::Translate(descriptor, &test_env, {});
Optimization::Optimize(ir_block, &test_env, {});
ir_block.Reset(descriptor);
A32::Translate(ir_block, descriptor, &test_env, {});
Optimization::Optimize(ir_block, config, {});
printf("\n\nIR:\n%s", IR::DumpBlock(ir_block).c_str());
printf("\n\nx86_64:\n");
printf("%s", jit.Disassemble().c_str());
@ -201,8 +203,9 @@ void FuzzJitThumb16(const size_t instruction_count, const size_t instructions_to
test_env.code_mem.back() = 0xE7FE; // b +#0
// Prepare test subjects
A32::UserConfig config{GetUserConfig(&test_env)};
A32Unicorn uni{test_env};
A32::Jit jit{GetUserConfig(&test_env)};
A32::Jit jit{config};
for (size_t run_number = 0; run_number < run_count; run_number++) {
ThumbTestEnv::RegisterArray initial_regs;
@ -211,7 +214,7 @@ void FuzzJitThumb16(const size_t instruction_count, const size_t instructions_to
std::generate_n(test_env.code_mem.begin(), instruction_count, instruction_generator);
RunInstance(run_number, test_env, uni, jit, initial_regs, instruction_count, instructions_to_execute_count);
RunInstance(run_number, test_env, config, uni, jit, initial_regs, instruction_count, instructions_to_execute_count);
}
}
@ -225,7 +228,8 @@ void FuzzJitThumb32(const size_t instruction_count, const size_t instructions_to
// Prepare test subjects
A32Unicorn uni{test_env};
A32::Jit jit{GetUserConfig(&test_env)};
A32::UserConfig config{GetUserConfig(&test_env)};
A32::Jit jit{config};
for (size_t run_number = 0; run_number < run_count; run_number++) {
ThumbTestEnv::RegisterArray initial_regs;
@ -241,7 +245,7 @@ void FuzzJitThumb32(const size_t instruction_count, const size_t instructions_to
test_env.code_mem[i * 2 + 1] = first_halfword;
}
RunInstance(run_number, test_env, uni, jit, initial_regs, instruction_count, instructions_to_execute_count);
RunInstance(run_number, test_env, config, uni, jit, initial_regs, instruction_count, instructions_to_execute_count);
}
}
@ -504,7 +508,8 @@ TEST_CASE("Verify fix for off by one error in MemoryRead32 worked", "[Thumb][Thu
// Prepare test subjects
A32Unicorn<ThumbTestEnv> uni{test_env};
A32::Jit jit{GetUserConfig(&test_env)};
A32::UserConfig config{GetUserConfig(&test_env)};
A32::Jit jit{config};
constexpr ThumbTestEnv::RegisterArray initial_regs{
0xe90ecd70,
@ -534,5 +539,5 @@ TEST_CASE("Verify fix for off by one error in MemoryRead32 worked", "[Thumb][Thu
0xE7FE, // b +#0
};
RunInstance(1, test_env, uni, jit, initial_regs, 5, 5);
RunInstance(1, test_env, config, uni, jit, initial_regs, 5, 5);
}

View file

@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
/* This file is part of the dynarmic project.
@ -8,8 +8,8 @@
#include <catch2/catch_test_macros.hpp>
#include "./testenv.h"
#include "../native/testenv.h"
#include "dynarmic/tests/A32/testenv.h"
#include "dynarmic/tests/native/testenv.h"
#include "dynarmic/frontend/A32/a32_location_descriptor.h"
#include "dynarmic/interface/A32/a32.h"

View file

@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
/* This file is part of the dynarmic project.
@ -10,8 +10,8 @@
#include <catch2/catch_test_macros.hpp>
#include "./testenv.h"
#include "../native/testenv.h"
#include "dynarmic/tests/A32/testenv.h"
#include "dynarmic/tests/native/testenv.h"
#include "dynarmic/frontend/A32/a32_location_descriptor.h"
#include "dynarmic/interface/A32/a32.h"
#include "dynarmic/interface/A32/coprocessor.h"

View file

@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
/* This file is part of the dynarmic project.
@ -10,8 +10,8 @@
#include <catch2/catch_test_macros.hpp>
#include "./testenv.h"
#include "../native/testenv.h"
#include "dynarmic/tests/A32/testenv.h"
#include "dynarmic/tests/native/testenv.h"
using namespace Dynarmic;

View file

@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
/* This file is part of the dynarmic project.
@ -9,8 +9,8 @@
#include <catch2/catch_test_macros.hpp>
#include "dynarmic/common/common_types.h"
#include "./testenv.h"
#include "../native/testenv.h"
#include "dynarmic/tests/A32/testenv.h"
#include "dynarmic/tests/native/testenv.h"
#include "dynarmic/interface/A32/a32.h"
static Dynarmic::A32::UserConfig GetUserConfig(ThumbTestEnv* testenv) {

View file

@ -9,8 +9,8 @@
#include <catch2/catch_test_macros.hpp>
#include <oaknut/oaknut.hpp>
#include "./testenv.h"
#include "../native/testenv.h"
#include "dynarmic/tests/A64/testenv.h"
#include "dynarmic/tests/native/testenv.h"
#include "dynarmic/common/fp/fpsr.h"
#include "dynarmic/interface/exclusive_monitor.h"
#include "dynarmic/interface/optimization_flags.h"

View file

@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
/* This file is part of the dynarmic project.
@ -11,8 +11,8 @@
#include <catch2/catch_test_macros.hpp>
#include "dynarmic/common/common_types.h"
#include "./testenv.h"
#include "../native/testenv.h"
#include "dynarmic/tests/A64/testenv.h"
#include "dynarmic/tests/native/testenv.h"
using namespace Dynarmic;

View file

@ -15,11 +15,11 @@
#include <mcl/scope_exit.hpp>
#include "dynarmic/common/common_types.h"
#include "../fuzz_util.h"
#include "../rand_int.h"
#include "../unicorn_emu/a64_unicorn.h"
#include "./testenv.h"
#include "../native/testenv.h"
#include "dynarmic/tests/fuzz_util.h"
#include "dynarmic/tests/rand_int.h"
#include "dynarmic/tests/unicorn_emu/a64_unicorn.h"
#include "dynarmic/tests/A64/testenv.h"
#include "dynarmic/tests/native/testenv.h"
#include "dynarmic/common/fp/fpcr.h"
#include "dynarmic/common/fp/fpsr.h"
#include "dynarmic/common/llvm_disassemble.h"
@ -168,7 +168,7 @@ static Dynarmic::A64::UserConfig GetUserConfig(A64TestEnv& jit_env) {
return jit_user_config;
}
static void RunTestInstance(Dynarmic::A64::Jit& jit, A64Unicorn& uni, A64TestEnv& jit_env, A64TestEnv& uni_env, const A64Unicorn::RegisterArray& regs, const A64Unicorn::VectorArray& vecs, const size_t instructions_start, const std::vector<u32>& instructions, const u32 pstate, const u32 fpcr) {
static void RunTestInstance(Dynarmic::A64::Jit& jit, A64Unicorn& uni, A64TestEnv& jit_env, A64TestEnv& uni_env, Dynarmic::A64::UserConfig& conf, const A64Unicorn::RegisterArray& regs, const A64Unicorn::VectorArray& vecs, const size_t instructions_start, const std::vector<u32>& instructions, const u32 pstate, const u32 fpcr) {
jit_env.code_mem = instructions;
uni_env.code_mem = instructions;
jit_env.code_mem.emplace_back(0x14000000); // B .
@ -269,16 +269,13 @@ static void RunTestInstance(Dynarmic::A64::Jit& jit, A64Unicorn& uni, A64TestEnv
fmt::print("\n");
const auto get_code = [&jit_env](u64 vaddr) { return jit_env.MemoryReadCode(vaddr); };
IR::Block ir_block = A64::Translate({instructions_start, FP::FPCR{fpcr}}, get_code, {});
fmt::print("IR:\n");
fmt::print("{}\n", IR::DumpBlock(ir_block));
const A64::LocationDescriptor location{instructions_start, FP::FPCR{fpcr}};
IR::Block ir_block{location};
A64::Translate(ir_block, location, get_code, {});
fmt::print("IR:\n{}\n", IR::DumpBlock(ir_block));
Optimization::Optimize(ir_block, conf, {});
fmt::print("Optimized IR:\n");
fmt::print("{}\n", IR::DumpBlock(ir_block));
fmt::print("x86_64:\n");
fmt::print("{}", jit.Disassemble());
fmt::print("Optimized IR:\n{}\n", IR::DumpBlock(ir_block));
fmt::print("x86_64:\n{}", jit.Disassemble());
fmt::print("Interrupts:\n");
for (auto& i : uni_env.interrupts) {
puts(i.c_str());
@ -304,7 +301,8 @@ TEST_CASE("A64: Single random instruction", "[a64][unicorn]") {
A64TestEnv jit_env{};
A64TestEnv uni_env{};
Dynarmic::A64::Jit jit{GetUserConfig(jit_env)};
Dynarmic::A64::UserConfig conf{GetUserConfig(jit_env)};
Dynarmic::A64::Jit jit{conf};
A64Unicorn uni{uni_env};
A64Unicorn::RegisterArray regs;
@ -323,7 +321,7 @@ TEST_CASE("A64: Single random instruction", "[a64][unicorn]") {
INFO("Instruction: 0x" << std::hex << instructions[0]);
RunTestInstance(jit, uni, jit_env, uni_env, regs, vecs, start_address, instructions, pstate, fpcr);
RunTestInstance(jit, uni, jit_env, uni_env, conf, regs, vecs, start_address, instructions, pstate, fpcr);
}
}
@ -331,7 +329,8 @@ TEST_CASE("A64: Floating point instructions", "[a64][unicorn]") {
A64TestEnv jit_env{};
A64TestEnv uni_env{};
Dynarmic::A64::Jit jit{GetUserConfig(jit_env)};
Dynarmic::A64::UserConfig conf{GetUserConfig(jit_env)};
Dynarmic::A64::Jit jit{conf};
A64Unicorn uni{uni_env};
static constexpr std::array<u64, 80> float_numbers{
@ -448,7 +447,7 @@ TEST_CASE("A64: Floating point instructions", "[a64][unicorn]") {
INFO("Instruction: 0x" << std::hex << instructions[0]);
RunTestInstance(jit, uni, jit_env, uni_env, regs, vecs, start_address, instructions, pstate, fpcr);
RunTestInstance(jit, uni, jit_env, uni_env, conf, regs, vecs, start_address, instructions, pstate, fpcr);
}
}
@ -456,7 +455,8 @@ TEST_CASE("A64: Small random block", "[a64][unicorn]") {
A64TestEnv jit_env{};
A64TestEnv uni_env{};
Dynarmic::A64::Jit jit{GetUserConfig(jit_env)};
Dynarmic::A64::UserConfig conf{GetUserConfig(jit_env)};
Dynarmic::A64::Jit jit{conf};
A64Unicorn uni{uni_env};
A64Unicorn::RegisterArray regs;
@ -483,7 +483,7 @@ TEST_CASE("A64: Small random block", "[a64][unicorn]") {
INFO("Instruction 4: 0x" << std::hex << instructions[3]);
INFO("Instruction 5: 0x" << std::hex << instructions[4]);
RunTestInstance(jit, uni, jit_env, uni_env, regs, vecs, start_address, instructions, pstate, fpcr);
RunTestInstance(jit, uni, jit_env, uni_env, conf, regs, vecs, start_address, instructions, pstate, fpcr);
}
}
@ -491,7 +491,8 @@ TEST_CASE("A64: Large random block", "[a64][unicorn]") {
A64TestEnv jit_env{};
A64TestEnv uni_env{};
Dynarmic::A64::Jit jit{GetUserConfig(jit_env)};
Dynarmic::A64::UserConfig conf{GetUserConfig(jit_env)};
Dynarmic::A64::Jit jit{conf};
A64Unicorn uni{uni_env};
A64Unicorn::RegisterArray regs;
@ -512,6 +513,6 @@ TEST_CASE("A64: Large random block", "[a64][unicorn]") {
const u32 pstate = RandInt<u32>(0, 0xF) << 28;
const u32 fpcr = RandomFpcr();
RunTestInstance(jit, uni, jit_env, uni_env, regs, vecs, start_address, instructions, pstate, fpcr);
RunTestInstance(jit, uni, jit_env, uni_env, conf, regs, vecs, start_address, instructions, pstate, fpcr);
}
}

View file

@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
/* This file is part of the dynarmic project.
@ -8,8 +8,8 @@
#include <catch2/catch_test_macros.hpp>
#include "./testenv.h"
#include "../native/testenv.h"
#include "dynarmic/tests/A64/testenv.h"
#include "dynarmic/tests/native/testenv.h"
#include "dynarmic/interface/A64/a64.h"
TEST_CASE("misaligned load/store do not use page_table when detect_misaligned_access_via_page_table is set", "[a64]") {

View file

@ -1,11 +1,11 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#include <catch2/catch_test_macros.hpp>
#include <oaknut/oaknut.hpp>
#include "./testenv.h"
#include "../native/testenv.h"
#include "dynarmic/tests/A64/testenv.h"
#include "dynarmic/tests/native/testenv.h"
#include "dynarmic/interface/A64/a64.h"
using namespace Dynarmic;

View file

@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
/* This file is part of the dynarmic project.
@ -8,8 +8,8 @@
#include <catch2/catch_test_macros.hpp>
#include "./testenv.h"
#include "../native/testenv.h"
#include "dynarmic/tests/A64/testenv.h"
#include "dynarmic/tests/native/testenv.h"
#include "dynarmic/interface/A64/a64.h"
using namespace Dynarmic;

View file

@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
/* This file is part of the dynarmic project.
* Copyright (c) 2018 MerryMage
* SPDX-License-Identifier: 0BSD
@ -7,9 +10,9 @@
#include <catch2/catch_test_macros.hpp>
#include "../rand_int.h"
#include "../unicorn_emu/a64_unicorn.h"
#include "./testenv.h"
#include "dynarmic/tests/rand_int.h"
#include "dynarmic/tests/unicorn_emu/a64_unicorn.h"
#include "dynarmic/tests/A64/testenv.h"
using namespace Dynarmic;

View file

@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
/* This file is part of the dynarmic project.
@ -12,7 +12,7 @@
#include <catch2/catch_test_macros.hpp>
#include "dynarmic/common/common_types.h"
#include "../rand_int.h"
#include "dynarmic/tests/rand_int.h"
#include "dynarmic/common/fp/fpcr.h"
#include "dynarmic/common/fp/fpsr.h"
#include "dynarmic/common/fp/op.h"

View file

@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
/* This file is part of the dynarmic project.
@ -12,7 +12,7 @@
#include <catch2/catch_test_macros.hpp>
#include "dynarmic/common/common_types.h"
#include "../rand_int.h"
#include "dynarmic/tests/rand_int.h"
#include "dynarmic/common/fp/mantissa_util.h"
#include "dynarmic/common/safe_ops.h"

View file

@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
/* This file is part of the dynarmic project.
@ -12,7 +12,7 @@
#include <catch2/catch_test_macros.hpp>
#include "dynarmic/common/common_types.h"
#include "../rand_int.h"
#include "dynarmic/tests/rand_int.h"
#include "dynarmic/common/fp/fpcr.h"
#include "dynarmic/common/fp/fpsr.h"
#include "dynarmic/common/fp/unpacked.h"

View file

@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
/* This file is part of the dynarmic project.
@ -6,7 +6,7 @@
* SPDX-License-Identifier: 0BSD
*/
#include "./fuzz_util.h"
#include "dynarmic/tests/fuzz_util.h"
#include <cstring>
@ -14,7 +14,7 @@
#include <fmt/ostream.h>
#include "dynarmic/common/assert.h"
#include "./rand_int.h"
#include "dynarmic/tests/rand_int.h"
#include "dynarmic/common/fp/fpcr.h"
#include "dynarmic/common/fp/rounding_mode.h"

View file

@ -1,12 +1,12 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#include <catch2/catch_test_macros.hpp>
#include <oaknut/oaknut.hpp>
#include <immintrin.h>
#include "../A64/testenv.h"
#include "../native/testenv.h"
#include "dynarmic/tests/A64/testenv.h"
#include "dynarmic/tests/native/testenv.h"
#include "dynarmic/common/fp/fpsr.h"
#include "dynarmic/interface/exclusive_monitor.h"

View file

@ -22,8 +22,8 @@
#include "./A32/testenv.h"
#include "./A64/testenv.h"
#include "./fuzz_util.h"
#include "./rand_int.h"
#include "dynarmic/tests/fuzz_util.h"
#include "dynarmic/tests/rand_int.h"
#include "dynarmic/common/fp/fpcr.h"
#include "dynarmic/common/fp/fpsr.h"
#include "dynarmic/common/llvm_disassemble.h"

View file

@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
/* This file is part of the dynarmic project.
@ -6,22 +6,14 @@
* SPDX-License-Identifier: 0BSD
*/
#include "./a32_unicorn.h"
#include <type_traits>
#include "dynarmic/common/assert.h"
#include <fmt/format.h>
#include <mcl/bit/bit_field.hpp>
#include "dynarmic/tests/unicorn_emu/a32_unicorn.h"
#include "dynarmic/common/assert.h"
#include "dynarmic/tests/A32/testenv.h"
#include "../A32/testenv.h"
#define CHECKED(expr) \
do { \
if (auto cerr_ = (expr)) { \
ASSERT(false && "Call " #expr " failed with error: {} ({})\n", static_cast<size_t>(cerr_), \
uc_strerror(cerr_)); \
} \
} while (0)
#define CHECKED(expr) do if ((expr)) ASSERT(false && "Call " #expr " failed with error\n"); while (0)
constexpr u32 BEGIN_ADDRESS = 0;
constexpr u32 END_ADDRESS = ~u32(0);

View file

@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
/* This file is part of the dynarmic project.
@ -21,7 +21,7 @@
#include "dynarmic/common/common_types.h"
#include "../A32/testenv.h"
#include "dynarmic/tests/A32/testenv.h"
namespace Unicorn::A32 {
static constexpr size_t num_gprs = 16;

View file

@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
/* This file is part of the dynarmic project.
@ -6,17 +6,11 @@
* SPDX-License-Identifier: 0BSD
*/
#include "./a64_unicorn.h"
#include <fmt/format.h>
#include "dynarmic/tests/unicorn_emu/a64_unicorn.h"
#include "dynarmic/common/assert.h"
#define CHECKED(expr) \
do { \
if (auto cerr_ = (expr)) { \
ASSERT(false && "Call " #expr " failed with error: {} ({})\n", static_cast<size_t>(cerr_), \
uc_strerror(cerr_)); \
} \
} while (0)
#define CHECKED(expr) do if ((expr)) ASSERT(false && "Call " #expr " failed with error\n"); while (0)
constexpr u64 BEGIN_ADDRESS = 0;
constexpr u64 END_ADDRESS = ~u64(0);
@ -172,7 +166,7 @@ void A64Unicorn::DumpMemoryInformation() {
void A64Unicorn::InterruptHook(uc_engine* uc, u32 int_number, void* user_data) {
auto* this_ = static_cast<A64Unicorn*>(user_data);
u32 esr;
u32 esr = 0;
//CHECKED(uc_reg_read(uc, UC_ARM64_REG_ESR_EL0, &esr));
auto ec = esr >> 26;

View file

@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
/* This file is part of the dynarmic project.
@ -21,7 +21,7 @@
#include "dynarmic/common/common_types.h"
#include "../A64/testenv.h"
#include "dynarmic/tests/A64/testenv.h"
class A64Unicorn final {
public:

View file

@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: 2016 Citra Emulator Project
@ -25,6 +25,48 @@ namespace FS = Common::FS;
namespace UISettings {
// This shouldn't have anything except static initializers (no functions). So
// QKeySequence(...).toString() is NOT ALLOWED HERE.
// This must be in alphabetical order according to action name as it must have the same order as
// UISetting::values.shortcuts, which is alphabetically ordered.
// clang-format off
const std::array<Shortcut, 33> default_hotkeys{{
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Audio Mute/Unmute")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("Ctrl+M"), std::string("Home+Dpad_Right"), Qt::WindowShortcut, false}},
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Audio Volume Down")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("-"), std::string("Home+Dpad_Down"), Qt::ApplicationShortcut, true}},
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Audio Volume Up")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("="), std::string("Home+Dpad_Up"), Qt::ApplicationShortcut, true}},
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Capture Screenshot")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("Ctrl+P"), std::string("Screenshot"), Qt::WidgetWithChildrenShortcut, false}},
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Change Adapting Filter")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("F8"), std::string("Home+L"), Qt::ApplicationShortcut, false}},
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Change Docked Mode")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("F10"), std::string("Home+X"), Qt::ApplicationShortcut, false}},
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Change GPU Mode")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("F9"), std::string("Home+R"), Qt::ApplicationShortcut, false}},
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Configure")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("Ctrl+,"), std::string(""), Qt::WidgetWithChildrenShortcut, false}},
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Configure Current Game")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("Ctrl+."), std::string(""), Qt::WidgetWithChildrenShortcut, false}},
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Continue/Pause Emulation")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("F4"), std::string("Home+Plus"), Qt::WindowShortcut, false}},
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Exit Fullscreen")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("Esc"), std::string(""), Qt::WindowShortcut, false}},
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Exit Eden")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("Ctrl+Q"), std::string("Home+Minus"), Qt::WindowShortcut, false}},
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Fullscreen")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("F11"), std::string("Home+B"), Qt::WindowShortcut, false}},
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Load File")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("Ctrl+O"), std::string(""), Qt::WidgetWithChildrenShortcut, false}},
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Load/Remove Amiibo")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("F2"), std::string("Home+A"), Qt::WidgetWithChildrenShortcut, false}},
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Browse Public Game Lobby")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("Ctrl+B"), std::string(""), Qt::ApplicationShortcut, false}},
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Create Room")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("Ctrl+N"), std::string(""), Qt::ApplicationShortcut, false}},
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Direct Connect to Room")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("Ctrl+C"), std::string(""), Qt::ApplicationShortcut, false}},
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Leave Room")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("Ctrl+L"), std::string(""), Qt::ApplicationShortcut, false}},
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Show Current Room")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("Ctrl+R"), std::string(""), Qt::ApplicationShortcut, false}},
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Restart Emulation")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("F6"), std::string("R+Plus+Minus"), Qt::WindowShortcut, false}},
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Stop Emulation")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("F5"), std::string("L+Plus+Minus"), Qt::WindowShortcut, false}},
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "TAS Record")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("Ctrl+F7"), std::string(""), Qt::ApplicationShortcut, false}},
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "TAS Reset")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("Ctrl+F6"), std::string(""), Qt::ApplicationShortcut, false}},
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "TAS Start/Stop")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("Ctrl+F5"), std::string(""), Qt::ApplicationShortcut, false}},
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Toggle Filter Bar")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("Ctrl+F"), std::string(""), Qt::WindowShortcut, false}},
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Toggle Framerate Limit")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("Ctrl+U"), std::string("Home+Y"), Qt::ApplicationShortcut, false}},
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Toggle Turbo Speed")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("Ctrl+Z"), std::string(""), Qt::ApplicationShortcut, false}},
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Toggle Slow Speed")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("Ctrl+X"), std::string(""), Qt::ApplicationShortcut, false}},
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Toggle Mouse Panning")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("Ctrl+F9"), std::string(""), Qt::ApplicationShortcut, false}},
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Toggle Renderdoc Capture")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string(""), std::string(""), Qt::ApplicationShortcut, false}},
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Toggle Status Bar")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("Ctrl+S"), std::string(""), Qt::WindowShortcut, false}},
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Toggle Performance Overlay")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("Ctrl+V"), std::string(""), Qt::WindowShortcut, false}},
}};
// clang-format on
const Themes themes{{
{"Default", "default"},
{"Default Colorful", "colorful"},

View file

@ -249,47 +249,8 @@ u32 CalculateWidth(u32 height, Settings::AspectRatio ratio);
void SaveWindowState();
void RestoreWindowState(std::unique_ptr<QtConfig>& qtConfig);
// This shouldn't have anything except static initializers (no functions). So
// QKeySequence(...).toString() is NOT ALLOWED HERE.
// This must be in alphabetical order according to action name as it must have the same order as
// UISetting::values.shortcuts, which is alphabetically ordered.
// clang-format off
const std::array<Shortcut, 33> default_hotkeys{{
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Audio Mute/Unmute")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("Ctrl+M"), std::string("Home+Dpad_Right"), Qt::WindowShortcut, false}},
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Audio Volume Down")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("-"), std::string("Home+Dpad_Down"), Qt::ApplicationShortcut, true}},
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Audio Volume Up")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("="), std::string("Home+Dpad_Up"), Qt::ApplicationShortcut, true}},
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Capture Screenshot")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("Ctrl+P"), std::string("Screenshot"), Qt::WidgetWithChildrenShortcut, false}},
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Change Adapting Filter")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("F8"), std::string("Home+L"), Qt::ApplicationShortcut, false}},
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Change Docked Mode")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("F10"), std::string("Home+X"), Qt::ApplicationShortcut, false}},
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Change GPU Mode")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("F9"), std::string("Home+R"), Qt::ApplicationShortcut, false}},
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Configure")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("Ctrl+,"), std::string(""), Qt::WidgetWithChildrenShortcut, false}},
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Configure Current Game")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("Ctrl+."), std::string(""), Qt::WidgetWithChildrenShortcut, false}},
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Continue/Pause Emulation")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("F4"), std::string("Home+Plus"), Qt::WindowShortcut, false}},
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Exit Fullscreen")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("Esc"), std::string(""), Qt::WindowShortcut, false}},
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Exit Eden")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("Ctrl+Q"), std::string("Home+Minus"), Qt::WindowShortcut, false}},
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Fullscreen")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("F11"), std::string("Home+B"), Qt::WindowShortcut, false}},
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Load File")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("Ctrl+O"), std::string(""), Qt::WidgetWithChildrenShortcut, false}},
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Load/Remove Amiibo")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("F2"), std::string("Home+A"), Qt::WidgetWithChildrenShortcut, false}},
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Browse Public Game Lobby")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("Ctrl+B"), std::string(""), Qt::ApplicationShortcut, false}},
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Create Room")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("Ctrl+N"), std::string(""), Qt::ApplicationShortcut, false}},
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Direct Connect to Room")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("Ctrl+C"), std::string(""), Qt::ApplicationShortcut, false}},
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Leave Room")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("Ctrl+L"), std::string(""), Qt::ApplicationShortcut, false}},
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Show Current Room")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("Ctrl+R"), std::string(""), Qt::ApplicationShortcut, false}},
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Restart Emulation")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("F6"), std::string("R+Plus+Minus"), Qt::WindowShortcut, false}},
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Stop Emulation")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("F5"), std::string("L+Plus+Minus"), Qt::WindowShortcut, false}},
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "TAS Record")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("Ctrl+F7"), std::string(""), Qt::ApplicationShortcut, false}},
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "TAS Reset")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("Ctrl+F6"), std::string(""), Qt::ApplicationShortcut, false}},
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "TAS Start/Stop")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("Ctrl+F5"), std::string(""), Qt::ApplicationShortcut, false}},
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Toggle Filter Bar")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("Ctrl+F"), std::string(""), Qt::WindowShortcut, false}},
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Toggle Framerate Limit")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("Ctrl+U"), std::string("Home+Y"), Qt::ApplicationShortcut, false}},
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Toggle Turbo Speed")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("Ctrl+Z"), std::string(""), Qt::ApplicationShortcut, false}},
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Toggle Slow Speed")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("Ctrl+X"), std::string(""), Qt::ApplicationShortcut, false}},
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Toggle Mouse Panning")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("Ctrl+F9"), std::string(""), Qt::ApplicationShortcut, false}},
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Toggle Renderdoc Capture")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string(""), std::string(""), Qt::ApplicationShortcut, false}},
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Toggle Status Bar")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("Ctrl+S"), std::string(""), Qt::WindowShortcut, false}},
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Toggle Performance Overlay")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("Ctrl+V"), std::string(""), Qt::WindowShortcut, false}},
}};
// clang-format on
// sync with uisettings.cpp
extern const std::array<Shortcut, 33> default_hotkeys;
} // namespace UISettings

View file

@ -9,6 +9,10 @@
#include <limits>
#include <vector>
#ifdef __ANDROID__
#include <android/api-level.h>
#endif
#include "common/logging.h"
#include "common/settings.h"
#include "common/settings_enums.h"
@ -170,6 +174,7 @@ bool Swapchain::AcquireNextImage() {
break;
}
const auto wait_with_frame_pacing = [this] {
switch (Settings::values.frame_pacing_mode.GetValue()) {
case Settings::FramePacingMode::Target_Auto:
scheduler.Wait(resource_ticks[image_index]);
@ -187,6 +192,17 @@ bool Swapchain::AcquireNextImage() {
scheduler.Wait(resource_ticks[image_index], 120.0);
break;
}
};
#ifdef __ANDROID__
if (android_get_device_api_level() >= 30) {
scheduler.Wait(resource_ticks[image_index]);
} else {
wait_with_frame_pacing();
}
#else
wait_with_frame_pacing();
#endif
resource_ticks[image_index] = scheduler.CurrentTick();

View file

@ -70,14 +70,10 @@ TextureCache<P>::TextureCache(Runtime& runtime_, Tegra::MaxwellDeviceMemoryManag
(std::max)((std::min)(device_local_memory - min_vacancy_critical, min_spacing_critical),
DEFAULT_CRITICAL_MEMORY));
minimum_memory = static_cast<u64>((device_local_memory - mem_threshold) / 2);
lowmemorydevice = false;
} else {
expected_memory = DEFAULT_EXPECTED_MEMORY + 512_MiB;
critical_memory = DEFAULT_CRITICAL_MEMORY + 1_GiB;
minimum_memory = 0;
lowmemorydevice = true;
}
const bool gpu_unswizzle_enabled = Settings::values.gpu_unswizzle_enabled.GetValue();
@ -122,102 +118,48 @@ void TextureCache<P>::RunGarbageCollector() {
bool aggressive_mode = false;
u64 ticks_to_destroy = 0;
size_t num_iterations = 0;
const auto Configure = [&](bool allow_aggressive) {
high_priority_mode = total_used_memory >= expected_memory;
aggressive_mode = allow_aggressive && total_used_memory >= critical_memory;
ticks_to_destroy = aggressive_mode ? 10ULL : high_priority_mode ? 25ULL : 50ULL;
num_iterations = aggressive_mode ? 40 : (high_priority_mode ? 20 : 10);
};
const auto Cleanup = [this, &num_iterations, &high_priority_mode,
&aggressive_mode](ImageId image_id) {
const auto Cleanup = [this, &num_iterations, &high_priority_mode, &aggressive_mode](ImageId image_id) {
if (num_iterations == 0) {
return true;
}
--num_iterations;
auto& image = slot_images[image_id];
// Never delete recently allocated sparse textures (within 3 frames)
const bool is_recently_allocated = image.allocation_tick >= frame_tick - 3;
if (is_recently_allocated && image.info.is_sparse) {
return false;
}
if (True(image.flags & ImageFlagBits::IsDecoding)) {
// This image is still being decoded, deleting it will invalidate the slot
// used by the async decoder thread.
return false;
}
// Prioritize large sparse textures for cleanup
const bool is_large_sparse = lowmemorydevice &&
image.info.is_sparse &&
image.guest_size_bytes >= 256_MiB;
if (!aggressive_mode && !is_large_sparse &&
True(image.flags & ImageFlagBits::CostlyLoad)) {
const bool must_download = image.IsSafeDownload() && False(image.flags & ImageFlagBits::BadOverlap);
if (!aggressive_mode && !high_priority_mode && (True(image.flags & ImageFlagBits::CostlyLoad) || must_download)) {
return false;
}
const bool must_download =
image.IsSafeDownload() && False(image.flags & ImageFlagBits::BadOverlap);
if (!high_priority_mode && !is_large_sparse && must_download) {
return false;
}
if (must_download && !is_large_sparse) {
--num_iterations;
if (must_download) {
auto map = runtime.DownloadStagingBuffer(image.unswizzled_size_bytes);
const auto copies = FixSmallVectorADL(FullDownloadCopies(image.info));
image.DownloadMemory(map, copies);
runtime.Finish();
SwizzleImage(*gpu_memory, image.gpu_addr, image.info, copies, map.mapped_span,
swizzle_data_buffer);
SwizzleImage(*gpu_memory, image.gpu_addr, image.info, copies, map.mapped_span, swizzle_data_buffer);
}
if (True(image.flags & ImageFlagBits::Tracked)) {
UntrackImage(image, image_id);
}
UnregisterImage(image_id);
DeleteImage(image_id, image.scale_tick > frame_tick + 5);
if (total_used_memory < critical_memory) {
if (aggressive_mode) {
// Sink the aggresiveness.
num_iterations >>= 2;
aggressive_mode = false;
return false;
}
if (high_priority_mode && total_used_memory < expected_memory) {
num_iterations >>= 1;
high_priority_mode = false;
}
if (aggressive_mode && total_used_memory < critical_memory) {
num_iterations >>= 2;
aggressive_mode = false;
} else if (high_priority_mode && total_used_memory < expected_memory) {
num_iterations >>= 1;
high_priority_mode = false;
}
return false;
};
// Aggressively clear massive sparse textures
if (total_used_memory >= expected_memory) {
lru_cache.ForEachItemBelow(frame_tick, [&](ImageId image_id) {
auto& image = slot_images[image_id];
// Only target sparse textures that are old enough
if (lowmemorydevice &&
image.info.is_sparse &&
image.guest_size_bytes >= 256_MiB &&
image.allocation_tick < frame_tick - 3) {
LOG_DEBUG(HW_GPU, "GC targeting old sparse texture at 0x{:X} ({} MiB, age: {} frames)",
image.gpu_addr, image.guest_size_bytes / (1024 * 1024),
frame_tick - image.allocation_tick);
return Cleanup(image_id);
}
return false;
});
}
Configure(false);
lru_cache.ForEachItemBelow(frame_tick - ticks_to_destroy, Cleanup);
// If pressure is still too high, prune aggressively.
if (total_used_memory >= critical_memory) {
Configure(true);
lru_cache.ForEachItemBelow(frame_tick - ticks_to_destroy, Cleanup);
@ -1196,9 +1138,6 @@ void TextureCache<P>::RefreshContents(Image& image, ImageId image_id) {
}
image.flags &= ~ImageFlagBits::CpuModified;
if( lowmemorydevice && image.info.format == PixelFormat::BC1_RGBA_UNORM && MapSizeBytes(image) >= 256_MiB ) {
return;
}
TrackImage(image, image_id);
@ -1619,39 +1558,6 @@ ImageId TextureCache<P>::InsertImage(const ImageInfo& info, GPUVAddr gpu_addr,
}
}
ASSERT_MSG(cpu_addr, "Tried to insert an image to an invalid gpu_addr=0x{:x}", gpu_addr);
// For large sparse textures, aggressively clean up old allocations at same address
if (lowmemorydevice && info.is_sparse && CalculateGuestSizeInBytes(info) >= 256_MiB) {
const auto alloc_it = image_allocs_table.find(gpu_addr);
if (alloc_it != image_allocs_table.end()) {
const ImageAllocId alloc_id = alloc_it->second;
auto& alloc_images = slot_image_allocs[alloc_id].images;
// Collect old images at this address that were created more than 2 frames ago
boost::container::small_vector<ImageId, 4> to_delete;
for (ImageId old_image_id : alloc_images) {
Image& old_image = slot_images[old_image_id];
if (old_image.info.is_sparse &&
old_image.gpu_addr == gpu_addr &&
old_image.allocation_tick < frame_tick - 2) { // Try not to delete fresh textures
to_delete.push_back(old_image_id);
}
}
// Delete old images immediately
for (ImageId old_id : to_delete) {
Image& old_image = slot_images[old_id];
LOG_DEBUG(HW_GPU, "Immediately deleting old sparse texture at 0x{:X} ({} MiB)",
gpu_addr, old_image.guest_size_bytes / (1024 * 1024));
if (True(old_image.flags & ImageFlagBits::Tracked)) {
UntrackImage(old_image, old_id);
}
UnregisterImage(old_id);
DeleteImage(old_id, true);
}
}
}
const ImageId image_id = JoinImages(info, gpu_addr, *cpu_addr);
const Image& image = slot_images[image_id];
// Using "image.gpu_addr" instead of "gpu_addr" is important because it might be different
@ -1667,27 +1573,6 @@ template <class P>
ImageId TextureCache<P>::JoinImages(const ImageInfo& info, GPUVAddr gpu_addr, DAddr cpu_addr) {
ImageInfo new_info = info;
const size_t size_bytes = CalculateGuestSizeInBytes(new_info);
// Proactive cleanup for large sparse texture allocations
if (lowmemorydevice && new_info.is_sparse && size_bytes >= 256_MiB) {
const u64 estimated_alloc_size = size_bytes;
if (total_used_memory + estimated_alloc_size >= critical_memory) {
LOG_DEBUG(HW_GPU, "Large sparse texture allocation ({} MiB) - running aggressive GC. "
"Current memory: {} MiB, Critical: {} MiB",
size_bytes / (1024 * 1024),
total_used_memory / (1024 * 1024),
critical_memory / (1024 * 1024));
RunGarbageCollector();
// If still over threshold after GC, try one more aggressive pass
if (total_used_memory + estimated_alloc_size >= critical_memory) {
LOG_DEBUG(HW_GPU, "Still critically low on memory, running second GC pass");
RunGarbageCollector();
}
}
}
const bool broken_views = runtime.HasBrokenTextureViewFormats();
const bool native_bgr = runtime.HasNativeBgr();
join_overlap_ids.clear();

View file

@ -478,7 +478,6 @@ private:
u64 minimum_memory;
u64 expected_memory;
u64 critical_memory;
bool lowmemorydevice = false;
size_t gpu_unswizzle_maxsize = 0;
size_t swizzle_chunk_size = 0;
u32 swizzle_slices_per_batch = 0;