Compare commits

...

45 Commits

Author SHA1 Message Date
CamilleLaVey 51dc28500b [vulkan, android] Add BGR565 emulated toggle 2026-04-23 15:12:41 +02:00
CamilleLaVey 2b1f163487 [vulkan] Small adjustments to dynamic_vertex_input setting 2026-04-23 15:12:41 +02:00
CamilleLaVey 52281d16e1 Revert "[vulkan] Updated VertexInput Stream offset" 2026-04-23 15:12:41 +02:00
CamilleLaVey d6105c5222 Revert "[vulkan] Rework DynamicState - ExtendedDynamicState" 2026-04-23 15:12:41 +02:00
CamilleLaVey 22bd482732 Revert "[vulkan] Adjustment ExtendedDynamicState" 2026-04-23 15:12:41 +02:00
CamilleLaVey 6770330d8e Revert "[vulkan] Adjusted pipeline keys inside patchControlPoints" 2026-04-23 15:12:41 +02:00
CamilleLaVey e776e337f2 Revert "fix comp err"
This reverts commit 470faa62551294e8073478552ccec437b0d4a9c9.
2026-04-23 15:12:41 +02:00
lizzie b0c04cfab4 fix comp err 2026-04-23 15:12:41 +02:00
CamilleLaVey 69e846f7ad [vulkan] Adjusted pipeline keys inside patchControlPoints 2026-04-23 15:12:41 +02:00
CamilleLaVey 9c53463b81 [vulkan] Adjustment ExtendedDynamicState 2026-04-23 15:12:41 +02:00
CamilleLaVey 0f749028b4 [vulkan] Rework DynamicState - ExtendedDynamicState 2026-04-23 15:12:41 +02:00
CamilleLaVey fee2c7edeb [vulkan] Updated VertexInput Stream offset 2026-04-23 15:12:41 +02:00
CamilleLaVey 657c432254 Fix license headers 2026-04-23 15:12:41 +02:00
CamilleLaVey e3725a160e [vulkan] Adjustment Viewport/Scissor in DynamicState 2026-04-23 15:12:41 +02:00
CamilleLaVey 0e2ff87fb2 [vulkan] Adjustment ImageView creation flags 2026-04-23 15:12:41 +02:00
CamilleLaVey e754ed9018 [vulkan] Adjustment ResetQueryPool 2026-04-23 15:12:41 +02:00
CamilleLaVey d7c1edbde2 [test] Force Synchronization of buffers 2026-04-23 15:12:41 +02:00
CamilleLaVey 5a0c7095c7 [vulkan] Adjustment VK_EXT_transform_feedback & VK_EXT_conditional_rendering 2026-04-23 15:12:41 +02:00
CamilleLaVey de273aefa5 [vulkan] Adjustment VK_EXT_transform_feedback Nº 2 2026-04-23 15:12:41 +02:00
CamilleLaVey da938123e1 [vulkan] Adjustment VK_EXT_transform_feedback 2026-04-23 15:12:41 +02:00
CamilleLaVey 9e2013d7f8 [test] Removed QCOM from presync workaround 2026-04-23 15:12:41 +02:00
CamilleLaVey 7872ae9985 [vulkan] Adjustment VK_EXT_conditional_rendering #1 2026-04-23 15:12:41 +02:00
CamilleLaVey 0a690baa3a [vulkan] Restore gatting flush operation on removed gpu accuracy to new gpu accuracy logic 2026-04-23 15:12:41 +02:00
CamilleLaVey a7cfd8b6a5 Revert "[vulkan] Adjusted VK_EXT_vertex_input_dynamic_state" 2026-04-23 15:12:41 +02:00
CamilleLaVey f1e4f69fc9 Revert "[vulkan] Adjustment ExtendedDynamicState System Nª1" 2026-04-23 15:12:41 +02:00
CamilleLaVey c68de909b8 [vulkan] Adjustment ExtendedDynamicState System Nª1 2026-04-23 15:12:41 +02:00
CamilleLaVey f085a85fbd [vulkan] Adjusted VK_EXT_vertex_input_dynamic_state 2026-04-23 15:12:41 +02:00
CamilleLaVey 4394321aaa [vulkan] Adjusted RobustImageAccess 2026-04-23 15:12:41 +02:00
CamilleLaVey baf51950e6 [vulkan] Adjusted VK_EXT_robustness2 2026-04-23 15:12:41 +02:00
CamilleLaVey 4753abd9f4 [vulkan] Adjusted VK_EXT_Custom_Border_Color implementation 2026-04-23 15:12:41 +02:00
CamilleLaVey 99d3f588f8 [vulkan] Removal of VK_EXT_unified_image_layouts 2026-04-23 15:12:41 +02:00
lizzie 417ed904f0 [dynarmic] fix MWAIT push/pop not being handled properly on xbyak (#3875)
push %edx is not what i wanted, nor what anybody wants, xbyak silently accepting this before was not intended
now that xbyak is updated this is an issue

the issue was simply:
a) ptr being overwritten (if it happened to be EDX/EBX/EAX) due to changed WAITPKG logic (oops)
b) xbyak not reporting wrong r32 pop/push on older versions thus missing it and now it throws an exception

Signed-off-by: lizzie <lizzie@eden-emu.dev>
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3875
Reviewed-by: crueter <crueter@eden-emu.dev>
Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
2026-04-23 05:06:33 +02:00
wildcard 838cc926f6 keyboard fixes (#3865)
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3865
Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
2026-04-23 03:43:54 +02:00
coolone 860acb4faf [android] Fix crash on start any games for many handhelds (Ayaneo, Retroid etc) (#3647)
## Summary
This change hardens Android Picture-in-Picture handling to avoid runtime crashes on devices/ROMs where PiP APIs are unavailable or behave inconsistently.

## What changed
- Added a feature gate for PiP support using `FEATURE_PICTURE_IN_PICTURE`.
- Early-returned from PiP flows when unsupported.
- Wrapped PiP API calls (`enterPictureInPictureMode`, `setPictureInPictureParams`) in guarded handlers that catch `IllegalStateException` and `UnsupportedOperationException`.
- Logged one warning per failed PiP action to avoid log spam.

## Why
On some Android devices, calling PiP APIs can throw at runtime even when the app is otherwise functioning normally. This patch prevents those calls from crashing/interrupting emulation and keeps behavior unchanged on supported devices.

## Notes
- No behavior changes for fully PiP-capable devices.
- Safe no-op on unsupported devices.

Co-authored-by: Nikolai Trukhin <coolone.official@gmail.com>
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3647
Reviewed-by: DraVee <chimera@dravee.dev>
Reviewed-by: Lizzie <lizzie@eden-emu.dev>
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
2026-04-22 05:32:52 +02:00
Eden CI a0bb6324c0 [dist, android] Update translations from Transifex for Apr 21 (#3871)
Automatic translation update for Apr 21

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3871
2026-04-21 16:02:40 +02:00
MaranBr 3a823de605 [dynarmic] Remove Ignore Global Monitor from CPU Accuracy Auto (#3846)
This fixes a flickering issue in some games, such as The Legend of Zelda: Echoes of Wisdom.

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3846
Reviewed-by: crueter <crueter@eden-emu.dev>
Reviewed-by: Lizzie <lizzie@eden-emu.dev>
2026-04-21 05:03:43 +02:00
crueter 0257a8d491 [desktop] Change data manager to use tabs instead of a list (#3856)
Signed-off-by: crueter <crueter@eden-emu.dev>
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3856
Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
Reviewed-by: Lizzie <lizzie@eden-emu.dev>
2026-04-21 04:01:39 +02:00
PavelBARABANOV 6c76908ddb [android] Allow manual overlay toggle even with controller connected + move async GPU options to hacks (#3862)
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3862
Co-authored-by: PavelBARABANOV <pavelbarabanov94@gmail.com>
Co-committed-by: PavelBARABANOV <pavelbarabanov94@gmail.com>
2026-04-17 20:29:45 +02:00
omardotdev e6ad51e3d5 [settings] remove duplicate settings (#3847)
this removes the duplicate serial knob, battery/unit serial shown on the settings tab and uses the debugging tab instead
(this used to be a pr to fix breeze compatibility issues where the text/input fields would look wrong when the theme was set to breeze but since the settings was moved its not really needed to change the window size)

Co-authored-by: Omar <180954465+omardotdev@users.noreply.github.com>
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3847
Reviewed-by: crueter <crueter@eden-emu.dev>
Reviewed-by: Lizzie <lizzie@eden-emu.dev>
Co-authored-by: omardotdev <omardotdev@eden-emu.dev>
Co-committed-by: omardotdev <omardotdev@eden-emu.dev>
2026-04-17 06:58:40 +02:00
Eden CI f11b69d6b7 [dist, android] Update translations from Transifex for Apr 14 (#3859)
Automatic translation update for Apr 14

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3859
Co-authored-by: Eden CI <ci@eden-emu.dev>
Co-committed-by: Eden CI <ci@eden-emu.dev>
2026-04-14 18:14:01 +02:00
xbzk 27cadb24f3 [android, intent] Run restoreContentForGame for shortcut launching + Improve custom settings context for shortcut launching (based on normal launch) (#3854)
intended to fix two bugs identified by Pavel upon android shortcut launching (and prolly all FEs since the rely on intent launching too):
1. external content not loading: fixed by running restoreContentForGame  for per game mount in content path.
2. custom settings no loading: that was proven to be user side, but adjustments were made to match normal launch.
should be harmless for normal usage. once validated can be merged.

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3854
Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
Co-authored-by: xbzk <xbzk@eden-emu.dev>
Co-committed-by: xbzk <xbzk@eden-emu.dev>
2026-04-14 00:21:51 +02:00
John a7ef19e028 [settings] Exposes the Hidden GPU Async Setting on other Platforms (#3833)
Disabling it may:
- Alleviates and Fixes flickering in Pokemon LZA, Kirby, Jamboree, XCDE, etc.
- Audio improvements in Metroid Prime 4

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3833
Reviewed-by: crueter <crueter@eden-emu.dev>
Co-authored-by: John <john@eden-emu.dev>
Co-committed-by: John <john@eden-emu.dev>
2026-04-13 00:28:12 +02:00
Eden CI 2c1eff2e60 [dist, android] Update translations from Transifex for Apr 11 (#3850)
Automatic translation update for Apr 11

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3850
Co-authored-by: Eden CI <ci@eden-emu.dev>
Co-committed-by: Eden CI <ci@eden-emu.dev>
2026-04-11 18:37:13 +02:00
xbzk abb616c357 [vk,swapchain] vsync: Changed Immediate fallback from (FIFO) to (Mailbox, then FIFO) (#3746)
Just discovered that stock adreno drivers on a730 had no Immediate VSync mode available, and it was falling back to FIFO, which causes a lot of response delay in Unity games.

But if someone chose Immediate, it prefers low latency above buffering, so Mailbox is his friend, and FIFO is his nemesis.

So i just changed Immediate to fall back to Mailbox first, and only if Mailbox is not available either it falls back to FIFO:

Immediate -> Mailbox -> FIFO;
Mailbox -> FIFO;
FIFO Relaxed -> FIFO.

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3746
Reviewed-by: crueter <crueter@eden-emu.dev>
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
Co-authored-by: xbzk <xbzk@eden-emu.dev>
Co-committed-by: xbzk <xbzk@eden-emu.dev>
2026-04-11 02:49:55 +02:00
lizzie c95cb8f8ec [hle] improved flusher that uses jthread (#3837)
not happy with this impl, but I made this a bit quickly to demostrate it can be done better :)

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

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3837
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-04-09 23:46:38 +02:00
85 changed files with 9237 additions and 8938 deletions
+334 -337
View File
File diff suppressed because it is too large Load Diff
+302 -306
View File
File diff suppressed because it is too large Load Diff
+302 -306
View File
File diff suppressed because it is too large Load Diff
+302 -306
View File
File diff suppressed because it is too large Load Diff
+302 -306
View File
File diff suppressed because it is too large Load Diff
+302 -306
View File
File diff suppressed because it is too large Load Diff
+331 -334
View File
File diff suppressed because it is too large Load Diff
+302 -306
View File
File diff suppressed because it is too large Load Diff
+304 -308
View File
File diff suppressed because it is too large Load Diff
+302 -306
View File
File diff suppressed because it is too large Load Diff
+364 -367
View File
File diff suppressed because it is too large Load Diff
+304 -308
View File
File diff suppressed because it is too large Load Diff
+302 -306
View File
File diff suppressed because it is too large Load Diff
+302 -306
View File
File diff suppressed because it is too large Load Diff
+303 -307
View File
File diff suppressed because it is too large Load Diff
+302 -306
View File
File diff suppressed because it is too large Load Diff
+304 -308
View File
File diff suppressed because it is too large Load Diff
+496 -472
View File
File diff suppressed because it is too large Load Diff
+302 -306
View File
File diff suppressed because it is too large Load Diff
+304 -308
View File
File diff suppressed because it is too large Load Diff
+305 -308
View File
File diff suppressed because it is too large Load Diff
+304 -308
View File
File diff suppressed because it is too large Load Diff
+305 -308
View File
File diff suppressed because it is too large Load Diff
+302 -306
View File
File diff suppressed because it is too large Load Diff
+302 -306
View File
File diff suppressed because it is too large Load Diff
+304 -308
View File
File diff suppressed because it is too large Load Diff
+302 -306
View File
File diff suppressed because it is too large Load Diff
@@ -15,6 +15,7 @@ import android.content.BroadcastReceiver
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.content.IntentFilter import android.content.IntentFilter
import android.content.pm.PackageManager
import android.content.res.Configuration import android.content.res.Configuration
import android.graphics.Rect import android.graphics.Rect
import android.graphics.drawable.Icon import android.graphics.drawable.Icon
@@ -100,6 +101,7 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener, InputManager
private var romSwapGeneration = 0 private var romSwapGeneration = 0
private var hasEmulationSession = processHasEmulationSession private var hasEmulationSession = processHasEmulationSession
private val romSwapStopTimeoutRunnable = Runnable { onRomSwapStopTimeout() } private val romSwapStopTimeoutRunnable = Runnable { onRomSwapStopTimeout() }
private val pictureInPictureFailureActions: MutableSet<String> = mutableSetOf()
private fun onRomSwapStopTimeout() { private fun onRomSwapStopTimeout() {
if (!isWaitingForRomSwapStop) { if (!isWaitingForRomSwapStop) {
@@ -266,12 +268,18 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener, InputManager
} }
override fun onUserLeaveHint() { override fun onUserLeaveHint() {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S ||
if (BooleanSetting.PICTURE_IN_PICTURE.getBoolean() && !isInPictureInPictureMode) { !isPictureInPictureSupported() ||
val pictureInPictureParamsBuilder = PictureInPictureParams.Builder() !BooleanSetting.PICTURE_IN_PICTURE.getBoolean() ||
.getPictureInPictureActionsBuilder().getPictureInPictureAspectBuilder() isInPictureInPictureMode
enterPictureInPictureMode(pictureInPictureParamsBuilder.build()) ) {
} return
}
val pictureInPictureParamsBuilder = PictureInPictureParams.Builder()
.getPictureInPictureActionsBuilder().getPictureInPictureAspectBuilder()
runPictureInPictureAction("enter picture-in-picture mode") {
enterPictureInPictureMode(pictureInPictureParamsBuilder.build())
} }
} }
@@ -651,7 +659,29 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener, InputManager
return this.apply { setActions(pictureInPictureActions) } return this.apply { setActions(pictureInPictureActions) }
} }
private fun isPictureInPictureSupported() =
Build.VERSION.SDK_INT >= Build.VERSION_CODES.O &&
packageManager.hasSystemFeature(PackageManager.FEATURE_PICTURE_IN_PICTURE)
private fun runPictureInPictureAction(actionName: String, action: () -> Unit) {
try {
action()
} catch (e: IllegalStateException) {
if (pictureInPictureFailureActions.add(actionName)) {
Log.warning("[PiP] Failed to $actionName: ${e.message}")
}
} catch (e: UnsupportedOperationException) {
if (pictureInPictureFailureActions.add(actionName)) {
Log.warning("[PiP] Failed to $actionName: ${e.message}")
}
}
}
fun buildPictureInPictureParams() { fun buildPictureInPictureParams() {
if (!isPictureInPictureSupported()) {
return
}
val pictureInPictureParamsBuilder = PictureInPictureParams.Builder() val pictureInPictureParamsBuilder = PictureInPictureParams.Builder()
.getPictureInPictureActionsBuilder().getPictureInPictureAspectBuilder() .getPictureInPictureActionsBuilder().getPictureInPictureAspectBuilder()
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
@@ -661,7 +691,9 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener, InputManager
BooleanSetting.PICTURE_IN_PICTURE.getBoolean() && isEmulationActive BooleanSetting.PICTURE_IN_PICTURE.getBoolean() && isEmulationActive
) )
} }
setPictureInPictureParams(pictureInPictureParamsBuilder.build()) runPictureInPictureAction("set picture-in-picture params") {
setPictureInPictureParams(pictureInPictureParamsBuilder.build())
}
} }
fun displayMultiplayerDialog() { fun displayMultiplayerDialog() {
@@ -1,3 +1,7 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
@@ -33,8 +37,10 @@ object SoftwareKeyboard {
val emulationActivity = NativeLibrary.sEmulationActivity.get() val emulationActivity = NativeLibrary.sEmulationActivity.get()
val overlayView = emulationActivity!!.findViewById<View>(R.id.surface_input_overlay) val overlayView = emulationActivity!!.findViewById<View>(R.id.surface_input_overlay)
overlayView.requestFocus()
val im = val im =
overlayView.context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager overlayView.context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
im.restartInput(overlayView)
im.showSoftInput(overlayView, InputMethodManager.SHOW_FORCED) im.showSoftInput(overlayView, InputMethodManager.SHOW_FORCED)
// There isn't a good way to know that the IMM is dismissed, so poll every 500ms to submit inline keyboard result. // There isn't a good way to know that the IMM is dismissed, so poll every 500ms to submit inline keyboard result.
@@ -17,6 +17,7 @@ enum class BooleanSetting(override val key: String) : AbstractBooleanSetting {
USE_CUSTOM_CPU_TICKS("use_custom_cpu_ticks"), USE_CUSTOM_CPU_TICKS("use_custom_cpu_ticks"),
SKIP_CPU_INNER_INVALIDATION("skip_cpu_inner_invalidation"), SKIP_CPU_INNER_INVALIDATION("skip_cpu_inner_invalidation"),
FIX_BLOOM_EFFECTS("fix_bloom_effects"), FIX_BLOOM_EFFECTS("fix_bloom_effects"),
EMULATE_BGR565("emulate_bgr565"),
CPUOPT_UNSAFE_HOST_MMU("cpuopt_unsafe_host_mmu"), CPUOPT_UNSAFE_HOST_MMU("cpuopt_unsafe_host_mmu"),
USE_DOCKED_MODE("use_docked_mode"), USE_DOCKED_MODE("use_docked_mode"),
USE_AUTO_STUB("use_auto_stub"), USE_AUTO_STUB("use_auto_stub"),
@@ -756,6 +756,13 @@ abstract class SettingsItem(
descriptionId = R.string.fix_bloom_effects_description descriptionId = R.string.fix_bloom_effects_description
) )
) )
put(
SwitchSetting(
BooleanSetting.EMULATE_BGR565,
titleId = R.string.emulate_bgr565,
descriptionId = R.string.emulate_bgr565_description
)
)
put( put(
SwitchSetting( SwitchSetting(
BooleanSetting.CPUOPT_UNSAFE_HOST_MMU, BooleanSetting.CPUOPT_UNSAFE_HOST_MMU,
@@ -284,8 +284,6 @@ class SettingsFragmentPresenter(
add(BooleanSetting.SYNC_MEMORY_OPERATIONS.key) add(BooleanSetting.SYNC_MEMORY_OPERATIONS.key)
add(BooleanSetting.RENDERER_USE_DISK_SHADER_CACHE.key) add(BooleanSetting.RENDERER_USE_DISK_SHADER_CACHE.key)
add(BooleanSetting.RENDERER_FORCE_MAX_CLOCK.key) add(BooleanSetting.RENDERER_FORCE_MAX_CLOCK.key)
add(BooleanSetting.RENDERER_ASYNCHRONOUS_GPU_EMULATION.key)
add(BooleanSetting.RENDERER_ASYNC_PRESENTATION.key)
add(BooleanSetting.RENDERER_REACTIVE_FLUSHING.key) add(BooleanSetting.RENDERER_REACTIVE_FLUSHING.key)
add(BooleanSetting.ENABLE_BUFFER_HISTORY.key) add(BooleanSetting.ENABLE_BUFFER_HISTORY.key)
add(BooleanSetting.USE_OPTIMIZED_VERTEX_BUFFERS.key) add(BooleanSetting.USE_OPTIMIZED_VERTEX_BUFFERS.key)
@@ -295,7 +293,10 @@ class SettingsFragmentPresenter(
add(IntSetting.FAST_GPU_TIME.key) add(IntSetting.FAST_GPU_TIME.key)
add(BooleanSetting.SKIP_CPU_INNER_INVALIDATION.key) add(BooleanSetting.SKIP_CPU_INNER_INVALIDATION.key)
add(BooleanSetting.FIX_BLOOM_EFFECTS.key) add(BooleanSetting.FIX_BLOOM_EFFECTS.key)
add(BooleanSetting.EMULATE_BGR565.key)
add(BooleanSetting.RENDERER_ASYNCHRONOUS_SHADERS.key) add(BooleanSetting.RENDERER_ASYNCHRONOUS_SHADERS.key)
add(BooleanSetting.RENDERER_ASYNCHRONOUS_GPU_EMULATION.key)
add(BooleanSetting.RENDERER_ASYNC_PRESENTATION.key)
add(SettingsItem.GPU_UNSWIZZLE_COMBINED) add(SettingsItem.GPU_UNSWIZZLE_COMBINED)
add(HeaderSetting(R.string.extensions)) add(HeaderSetting(R.string.extensions))
@@ -291,13 +291,23 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
// Game launched via intent (check for existing custom config) // Game launched via intent (check for existing custom config)
intentGame != null -> { intentGame != null -> {
game?.let { gameInstance -> game?.let { gameInstance ->
runCatching { GameHelper.restoreContentForGame(gameInstance) }
.onFailure {
Log.warning(
"[EmulationFragment] Failed to restore content for intent launch: ${it.message}"
)
}
val customConfigFile = SettingsFile.getCustomSettingsFile(gameInstance) val customConfigFile = SettingsFile.getCustomSettingsFile(gameInstance)
if (customConfigFile.exists()) { if (customConfigFile.exists()) {
shouldUseCustom = true
Log.info( Log.info(
"[EmulationFragment] Found existing custom settings for ${gameInstance.title}, loading them" "[EmulationFragment] Found existing custom settings for ${gameInstance.title}, loading them"
) )
SettingsFile.loadCustomConfig(gameInstance) SettingsFile.loadCustomConfig(gameInstance)
NativeConfig.unloadPerGameConfig()
} else { } else {
shouldUseCustom = false
Log.info( Log.info(
"[EmulationFragment] No custom settings found for ${gameInstance.title}, using global settings" "[EmulationFragment] No custom settings found for ${gameInstance.title}, using global settings"
) )
@@ -871,7 +881,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
if (drawerView == binding.quickSettingsSheet) { if (drawerView == binding.quickSettingsSheet) {
isQuickSettingsMenuOpen = true isQuickSettingsMenuOpen = true
if (shouldUseCustom) { if (shouldUseCustom) {
SettingsFile.loadCustomConfig(args.game!!) SettingsFile.loadCustomConfig(game!!)
} }
} }
} }
@@ -2499,7 +2509,6 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
fun toggleOverlay(enable: Boolean) { fun toggleOverlay(enable: Boolean) {
if (!isAdded || _binding == null) return if (!isAdded || _binding == null) return
if (enable && hasPhysicalControllerConnected && !args.overlayGamelessEditMode) return
if (enable == !BooleanSetting.SHOW_INPUT_OVERLAY.getBoolean()) { if (enable == !BooleanSetting.SHOW_INPUT_OVERLAY.getBoolean()) {
// Reset controller input flag so controller can hide overlay again // Reset controller input flag so controller can hide overlay again
if (!enable) { if (!enable) {
@@ -2537,7 +2546,8 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
if (binding.surfaceInputOverlay.isGamelessMode()) return if (binding.surfaceInputOverlay.isGamelessMode()) return
if (hasConnectedControllers) { if (hasConnectedControllers) {
if (BooleanSetting.SHOW_INPUT_OVERLAY.getBoolean()) { if (BooleanSetting.SHOW_INPUT_OVERLAY.getBoolean() &&
BooleanSetting.HIDE_OVERLAY_ON_CONTROLLER_INPUT.getBoolean()) {
overlayHiddenByPhysicalController = true overlayHiddenByPhysicalController = true
toggleOverlay(false) toggleOverlay(false)
} }
@@ -17,16 +17,23 @@ import android.graphics.drawable.VectorDrawable
import android.os.Build import android.os.Build
import android.os.Handler import android.os.Handler
import android.os.Looper import android.os.Looper
import android.text.Editable
import android.text.InputType
import android.util.AttributeSet import android.util.AttributeSet
import android.view.HapticFeedbackConstants import android.view.HapticFeedbackConstants
import android.view.KeyEvent
import android.view.MotionEvent import android.view.MotionEvent
import android.view.View import android.view.View
import android.view.View.OnTouchListener import android.view.View.OnTouchListener
import android.view.WindowInsets import android.view.WindowInsets
import android.view.inputmethod.BaseInputConnection
import android.view.inputmethod.EditorInfo
import android.view.inputmethod.InputConnection
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.window.layout.WindowMetricsCalculator import androidx.window.layout.WindowMetricsCalculator
import kotlin.math.max import kotlin.math.max
import kotlin.math.min import kotlin.math.min
import org.yuzu.yuzu_emu.NativeLibrary
import org.yuzu.yuzu_emu.features.input.NativeInput import org.yuzu.yuzu_emu.features.input.NativeInput
import org.yuzu.yuzu_emu.R import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.features.input.model.NativeAnalog import org.yuzu.yuzu_emu.features.input.model.NativeAnalog
@@ -49,6 +56,7 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
private val overlayButtons: MutableSet<InputOverlayDrawableButton> = HashSet() private val overlayButtons: MutableSet<InputOverlayDrawableButton> = HashSet()
private val overlayDpads: MutableSet<InputOverlayDrawableDpad> = HashSet() private val overlayDpads: MutableSet<InputOverlayDrawableDpad> = HashSet()
private val overlayJoysticks: MutableSet<InputOverlayDrawableJoystick> = HashSet() private val overlayJoysticks: MutableSet<InputOverlayDrawableJoystick> = HashSet()
private val imeEditable = Editable.Factory.getInstance().newEditable("")
private var inEditMode = false private var inEditMode = false
private var gamelessMode = false private var gamelessMode = false
@@ -75,6 +83,63 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
// External listener for EmulationFragment joypad overlay auto-hide // External listener for EmulationFragment joypad overlay auto-hide
var touchEventListener: ((MotionEvent) -> Unit)? = null var touchEventListener: ((MotionEvent) -> Unit)? = null
override fun onCheckIsTextEditor(): Boolean = true
override fun onCreateInputConnection(outAttrs: EditorInfo): InputConnection {
imeEditable.clear()
outAttrs.inputType =
InputType.TYPE_CLASS_TEXT or
InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS or
InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD
outAttrs.imeOptions = EditorInfo.IME_FLAG_NO_EXTRACT_UI or EditorInfo.IME_ACTION_DONE
outAttrs.initialSelStart = 0
outAttrs.initialSelEnd = 0
return object : BaseInputConnection(this, true) {
override fun getEditable(): Editable = imeEditable
override fun commitText(text: CharSequence?, newCursorPosition: Int): Boolean {
if (!text.isNullOrEmpty()) {
forwardCommittedText(text)
}
return super.commitText(text, newCursorPosition)
}
override fun deleteSurroundingText(beforeLength: Int, afterLength: Int): Boolean {
repeat(beforeLength.coerceAtLeast(0)) {
NativeLibrary.submitInlineKeyboardInput(KeyEvent.KEYCODE_DEL)
}
return super.deleteSurroundingText(beforeLength, afterLength)
}
override fun sendKeyEvent(event: KeyEvent): Boolean {
if (event.action != KeyEvent.ACTION_DOWN) {
return true
}
when (event.keyCode) {
KeyEvent.KEYCODE_BACK,
KeyEvent.KEYCODE_DEL,
KeyEvent.KEYCODE_ENTER -> {
NativeLibrary.submitInlineKeyboardInput(event.keyCode)
}
else -> {
val textChar = event.unicodeChar
if (textChar != 0) {
NativeLibrary.submitInlineKeyboardText(textChar.toChar().toString())
}
}
}
return true
}
override fun performEditorAction(actionCode: Int): Boolean {
NativeLibrary.submitInlineKeyboardInput(KeyEvent.KEYCODE_ENTER)
return true
}
}
}
override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) { override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
super.onLayout(changed, left, top, right, bottom) super.onLayout(changed, left, top, right, bottom)
@@ -119,6 +184,25 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
} }
} }
private fun forwardCommittedText(text: CharSequence) {
val builder = StringBuilder()
text.forEach { character ->
when (character) {
'\n' -> {
if (builder.isNotEmpty()) {
NativeLibrary.submitInlineKeyboardText(builder.toString())
builder.clear()
}
NativeLibrary.submitInlineKeyboardInput(KeyEvent.KEYCODE_ENTER)
}
else -> builder.append(character)
}
}
if (builder.isNotEmpty()) {
NativeLibrary.submitInlineKeyboardText(builder.toString())
}
}
private fun drawGrid(canvas: Canvas) { private fun drawGrid(canvas: Canvas) {
val gridSize = IntSetting.OVERLAY_GRID_SIZE.getInt() val gridSize = IntSetting.OVERLAY_GRID_SIZE.getInt()
val width = canvas.width val width = canvas.width
@@ -95,7 +95,7 @@ namespace AndroidSettings {
Settings::Setting<u32> input_overlay_auto_hide{linkage, 5, "input_overlay_auto_hide", Settings::Setting<u32> input_overlay_auto_hide{linkage, 5, "input_overlay_auto_hide",
Settings::Category::Overlay, Settings::Category::Overlay,
Settings::Specialization::Default, true, true, &enable_input_overlay_auto_hide}; Settings::Specialization::Default, true, true, &enable_input_overlay_auto_hide};
Settings::Setting<bool> hide_overlay_on_controller_input{linkage, false, Settings::Setting<bool> hide_overlay_on_controller_input{linkage, true,
"hide_overlay_on_controller_input", "hide_overlay_on_controller_input",
Settings::Category::Overlay, Settings::Category::Overlay,
Settings::Specialization::Default, true, Settings::Specialization::Default, true,
@@ -32,7 +32,7 @@
<string name="hide_overlay_on_controller_input">إخفاء الطبقة عند إدخال ذراع التحكم</string> <string name="hide_overlay_on_controller_input">إخفاء الطبقة عند إدخال ذراع التحكم</string>
<string name="hide_overlay_on_controller_input_description">إخفاء تلقائي لطبقة عناصر التحكم باللمس عند استخدام ذراع تحكم فعلية. تظهر الطبقة مرة أخرى عند فصل ذراع التحكم.</string> <string name="hide_overlay_on_controller_input_description">إخفاء تلقائي لطبقة عناصر التحكم باللمس عند استخدام ذراع تحكم فعلية. تظهر الطبقة مرة أخرى عند فصل ذراع التحكم.</string>
<string name="invert_confirm_back_controller_buttons">عكس أزرار التأكيد/الرجوع في وحدة التحكم</string> <string name="invert_confirm_back_controller_buttons">عكس أزرار التأكيد/الرجوع في وحدة التحكم</string>
<string name="invert_confirm_back_controller_buttons_description">قم بتبديل طريقة عمل زري التأكيد والرجوع في نظام Android لتتوافق مع أنماط Switch و Xbox أثناء استخدام واجهة المستخدم الخاصة بالتطبيق.</string> <string name="invert_confirm_back_controller_buttons_description">تبديل طريقة التعامل مع زري «تأكيد» و«رجوع» في نظام Android لتتوافق مع أنماط أجهزة Switch وXbox أثناء استخدام واجهة المستخدم الخاصة بالتطبيق.</string>
<string name="input_overlay_options">طبقة الإدخال</string> <string name="input_overlay_options">طبقة الإدخال</string>
<string name="input_overlay_options_description">ضبط ذراع التحكم على الشاشة</string> <string name="input_overlay_options_description">ضبط ذراع التحكم على الشاشة</string>
@@ -268,7 +268,7 @@
<string name="product">المنتج</string> <string name="product">المنتج</string>
<string name="android_version">إصدار Android</string> <string name="android_version">إصدار Android</string>
<string name="android_security_patch">تصحيح الأمان</string> <string name="android_security_patch">تصحيح الأمان</string>
<string name="build_id">معرف البناء</string> <string name="build_id">معرف الإصدار</string>
<string name="general_information">معلومات عامة</string> <string name="general_information">معلومات عامة</string>
<string name="hardware">الأجهزة</string> <string name="hardware">الأجهزة</string>
<string name="supported_abis">واجهات برمجة التطبيقات المدعومة</string> <string name="supported_abis">واجهات برمجة التطبيقات المدعومة</string>
@@ -287,7 +287,7 @@
<string name="install_amiibo_keys">تثبيت مفاتيح أميبو</string> <string name="install_amiibo_keys">تثبيت مفاتيح أميبو</string>
<string name="install_amiibo_keys_description">مطلوب لاستخدام أميبو في اللعبة</string> <string name="install_amiibo_keys_description">مطلوب لاستخدام أميبو في اللعبة</string>
<string name="gpu_driver_fetcher">أداة جلب برامج تشغيل وحدة المعالجة الرسومية</string> <string name="gpu_driver_fetcher">أداة جلب برامج تشغيل وحدة المعالجة الرسومية</string>
<string name="gpu_driver_manager">مدير برامج تشغيل وحدة معالجة الرسومات</string> <string name="gpu_driver_manager">إدارة برامج تشغيل وحدة معالجة الرسومات</string>
<string name="install_gpu_driver_description">تثبيت برامج تشغيل بديلة لأداء أو دقة أفضل</string> <string name="install_gpu_driver_description">تثبيت برامج تشغيل بديلة لأداء أو دقة أفضل</string>
<string name="advanced_settings">الإعدادات المتقدمة</string> <string name="advanced_settings">الإعدادات المتقدمة</string>
<string name="settings_description">ضبط إعدادات المحاكي</string> <string name="settings_description">ضبط إعدادات المحاكي</string>
@@ -403,7 +403,7 @@
<string name="contributors">المساهمين</string> <string name="contributors">المساهمين</string>
<string name="contributors_description">الأشخاص الذين جعلوا تطبيق Eden لنظام Android ممكنًا</string> <string name="contributors_description">الأشخاص الذين جعلوا تطبيق Eden لنظام Android ممكنًا</string>
<string name="licenses_description">المشاريع التي تجعل Eden لأجهزة Android ممكنة</string> <string name="licenses_description">المشاريع التي تجعل Eden لأجهزة Android ممكنة</string>
<string name="build">البناء</string> <string name="build">الإصدار</string>
<string name="user_data">بيانات المستخدم</string> <string name="user_data">بيانات المستخدم</string>
<string name="user_data_description">استيراد/تصدير جميع بيانات التطبيق.\nعند استيراد بيانات المستخدم، سيتم حذف جميع بيانات المستخدم الموجودة!\nقد يُسبب استيراد البيانات من Citron بعض المشاكل. يُنصح باستيراد جميع البيانات المطلوبة يدويًا.</string> <string name="user_data_description">استيراد/تصدير جميع بيانات التطبيق.\nعند استيراد بيانات المستخدم، سيتم حذف جميع بيانات المستخدم الموجودة!\nقد يُسبب استيراد البيانات من Citron بعض المشاكل. يُنصح باستيراد جميع البيانات المطلوبة يدويًا.</string>
<string name="exporting_user_data">جارٍ تصدير بيانات المستخدم...</string> <string name="exporting_user_data">جارٍ تصدير بيانات المستخدم...</string>
@@ -486,6 +486,10 @@
<string name="use_disk_shader_cache_description">يقلل من التأتأة عن طريق تخزين وتحميل التظليلات التي تم إنشاؤها محليًا.</string> <string name="use_disk_shader_cache_description">يقلل من التأتأة عن طريق تخزين وتحميل التظليلات التي تم إنشاؤها محليًا.</string>
<string name="renderer_force_max_clock">إجبار السرعة القصوى (لأجهزة Adreno فقط)</string> <string name="renderer_force_max_clock">إجبار السرعة القصوى (لأجهزة Adreno فقط)</string>
<string name="renderer_force_max_clock_description">يجبر وحدة معالجة الرسومات على العمل بأقصى سرعة ممكنة (سيظل يتم تطبيق القيود الحرارية).</string> <string name="renderer_force_max_clock_description">يجبر وحدة معالجة الرسومات على العمل بأقصى سرعة ممكنة (سيظل يتم تطبيق القيود الحرارية).</string>
<string name="renderer_asynchronous_gpu_emulation">محاكاة غير متزامنة لوحدة معالجة الرسومات</string>
<string name="renderer_asynchronous_gpu_emulation_description">يُشغّل هذا الخيار محاكاة وحدة معالجة الرسومات بشكل غير متزامن لتقليل توقف وحدة المعالجة المركزية وتحسين الإنتاجية. عطّل هذا الخيار فقط في حال واجهت مشاكل متعلقة بالتوقيت.</string>
<string name="renderer_async_presentation">عرض غير متزامن</string>
<string name="renderer_async_presentation_description">يحسّن الأداء بشكل طفيف عن طريق نقل عملية العرض إلى خيط معالجة منفصل لوحدة المعالجة المركزية.</string>
<string name="renderer_reactive_flushing">استخدم التنظيف التفاعلي</string> <string name="renderer_reactive_flushing">استخدم التنظيف التفاعلي</string>
<string name="renderer_reactive_flushing_description">يحسن دقة العرض في بعض الألعاب على حساب الأداء.</string> <string name="renderer_reactive_flushing_description">يحسن دقة العرض في بعض الألعاب على حساب الأداء.</string>
<string name="enable_buffer_history">تمكين سجل التخزين المؤقت</string> <string name="enable_buffer_history">تمكين سجل التخزين المؤقت</string>
@@ -627,10 +631,10 @@
<string name="gamecube_controller">ذراع تحكم GameCube</string> <string name="gamecube_controller">ذراع تحكم GameCube</string>
<string name="invert_axis">عكس المحور</string> <string name="invert_axis">عكس المحور</string>
<string name="invert_button">عكس الزر</string> <string name="invert_button">عكس الزر</string>
<string name="toggle_button">زر التبديل</string> <string name="toggle_button">زر تشغيل/إيقاف</string>
<string name="turbo_button">زر التوربو</string> <string name="turbo_button">زر التوربو</string>
<string name="set_threshold">تعيين الحد الفاصل</string> <string name="set_threshold">تعيين الحد الفاصل</string>
<string name="toggle_axis">تبديل المحور</string> <string name="toggle_axis">تشغيل/إيقاف المحور</string>
<string name="connected">متصل</string> <string name="connected">متصل</string>
<string name="use_system_vibrator">استخدم هزاز النظام</string> <string name="use_system_vibrator">استخدم هزاز النظام</string>
<string name="input_overlay">طبقة الإدخال</string> <string name="input_overlay">طبقة الإدخال</string>
@@ -656,7 +660,7 @@
<!-- Miscellaneous --> <!-- Miscellaneous -->
<string name="slider_default">افتراضي</string> <string name="slider_default">افتراضي</string>
<string name="default_string">الافتراضي</string> <string name="default_string">افتراضي</string>
<string name="loading">جارٍ التحميل…</string> <string name="loading">جارٍ التحميل…</string>
<string name="shutting_down">جارٍ إيقاف التشغيل...</string> <string name="shutting_down">جارٍ إيقاف التشغيل...</string>
<string name="reset_setting_confirmation">هل تريد إعادة تعيين هذا الإعداد إلى قيمته الافتراضية؟</string> <string name="reset_setting_confirmation">هل تريد إعادة تعيين هذا الإعداد إلى قيمته الافتراضية؟</string>
@@ -682,8 +686,8 @@
<string name="import_success">تم الاستيراد بنجاح</string> <string name="import_success">تم الاستيراد بنجاح</string>
<string name="export_success">تم التصدير بنجاح</string> <string name="export_success">تم التصدير بنجاح</string>
<string name="start">بدء</string> <string name="start">بدء</string>
<string name="global">المتغيرات العامة</string> <string name="global">الإعدادات العامة</string>
<string name="custom">مخصص</string> <string name="custom">الإعدادات المخصصة للعبة</string>
<string name="import_complete">اكتمل الاستيراد</string> <string name="import_complete">اكتمل الاستيراد</string>
<string name="use_global_setting">استخدام الإعداد العام</string> <string name="use_global_setting">استخدام الإعداد العام</string>
<string name="operation_completed_successfully">اكتملت العملية بنجاح</string> <string name="operation_completed_successfully">اكتملت العملية بنجاح</string>
@@ -762,7 +766,7 @@
<string name="info_description">معرف العنوان، المطور، الإصدار</string> <string name="info_description">معرف العنوان، المطور، الإصدار</string>
<string name="per_game_settings">الإعدادات الخاصة للعبة</string> <string name="per_game_settings">الإعدادات الخاصة للعبة</string>
<string name="per_game_settings_description">تعديل الإعدادات الخاصة بهذه اللعبة</string> <string name="per_game_settings_description">تعديل الإعدادات الخاصة بهذه اللعبة</string>
<string name="launch_options">تشغيل الإعدادات</string> <string name="launch_options">إعدادات التشغيل</string>
<string name="path">المسار</string> <string name="path">المسار</string>
<string name="program_id">معرف العنوان</string> <string name="program_id">معرف العنوان</string>
<string name="developer">المطور</string> <string name="developer">المطور</string>
@@ -874,13 +878,13 @@
<!-- Emulation Menu --> <!-- Emulation Menu -->
<string name="emulation_exit">خروج من المحاكاة</string> <string name="emulation_exit">خروج من المحاكاة</string>
<string name="emulation_done">إنهاء</string> <string name="emulation_done">إنهاء</string>
<string name="emulation_toggle_controls">تبديل أزرار التحكم</string> <string name="emulation_toggle_controls">تشغيل/إيقاف أزرار التحكم</string>
<string name="emulation_rel_stick_center">مركز العصا النسبي</string> <string name="emulation_rel_stick_center">مركز العصا النسبي</string>
<string name="emulation_dpad_slide">انزلاق الأسهم</string> <string name="emulation_dpad_slide">انزلاق الأسهم</string>
<string name="emulation_haptics">الاهتزازات الديناميكية</string> <string name="emulation_haptics">الاهتزازات الديناميكية</string>
<string name="emulation_show_overlay">عرض ذراع التحكم</string> <string name="emulation_show_overlay">عرض ذراع التحكم</string>
<string name="emulation_hide_overlay">إخفاء ذراع التحكم</string> <string name="emulation_hide_overlay">إخفاء ذراع التحكم</string>
<string name="emulation_toggle_all">تبديل الكل</string> <string name="emulation_toggle_all">تشغيل/إيقاف الكل</string>
<string name="emulation_control_adjust">ضبط الطبقة</string> <string name="emulation_control_adjust">ضبط الطبقة</string>
<string name="emulation_control_scale">الحجم</string> <string name="emulation_control_scale">الحجم</string>
<string name="emulation_control_opacity">الشفافية</string> <string name="emulation_control_opacity">الشفافية</string>
@@ -1122,7 +1126,7 @@
<string name="enable_overlay_description">تمكين التطبيق الصغير المدمج في Horizon. اضغط مع الاستمرار على زر الشاشة الرئيسية لمدة 1 ثانية لإظهاره.</string> <string name="enable_overlay_description">تمكين التطبيق الصغير المدمج في Horizon. اضغط مع الاستمرار على زر الشاشة الرئيسية لمدة 1 ثانية لإظهاره.</string>
<!-- Profile Management --> <!-- Profile Management -->
<string name="profile_manager">مدير الملف الشخصي</string> <string name="profile_manager">إدارة ملف التعريف</string>
<string name="profile_manager_description">إدارة ملفات تعريف المستخدمين</string> <string name="profile_manager_description">إدارة ملفات تعريف المستخدمين</string>
<string name="profile_add_user">إضافة مستخدم</string> <string name="profile_add_user">إضافة مستخدم</string>
<string name="profile_new_user">مستخدم جديد</string> <string name="profile_new_user">مستخدم جديد</string>
@@ -272,7 +272,7 @@
<string name="general_information">Información general</string> <string name="general_information">Información general</string>
<string name="hardware">Hardware</string> <string name="hardware">Hardware</string>
<string name="supported_abis">ABIs soportadas</string> <string name="supported_abis">ABIs soportadas</string>
<string name="cpu_info">Información de la CPU</string> <string name="cpu_info">Información del procesador</string>
<string name="gpu_information">Información de la GPU</string> <string name="gpu_information">Información de la GPU</string>
<string name="vulkan_driver_version">Versión del controlador de Vulkan</string> <string name="vulkan_driver_version">Versión del controlador de Vulkan</string>
<string name="error_getting_emulator_info">Error al obtener la información del emulador</string> <string name="error_getting_emulator_info">Error al obtener la información del emulador</string>
@@ -351,7 +351,7 @@
<item quantity="other">%d archivos de guardado importados con éxito.</item> <item quantity="other">%d archivos de guardado importados con éxito.</item>
</plurals> </plurals>
<string name="no_save_data_found">No se encontraron datos de guardado</string> <string name="no_save_data_found">No se encontraron datos de guardado</string>
<string name="verify_installed_content">Verificar contenido instalado</string> <string name="verify_installed_content">Verificar los contenidos instalados</string>
<string name="verify_installed_content_description">Comprueba todo el contenido instalado por si hubiese alguno corrupto</string> <string name="verify_installed_content_description">Comprueba todo el contenido instalado por si hubiese alguno corrupto</string>
<string name="keys_missing">Faltan las claves de encriptación</string> <string name="keys_missing">Faltan las claves de encriptación</string>
@@ -416,8 +416,8 @@
<string name="turbo_speed_limit_description">Cuando el modo turbo esté activado, la emulación se ejecutará a esta velocidad.</string> <string name="turbo_speed_limit_description">Cuando el modo turbo esté activado, la emulación se ejecutará a esta velocidad.</string>
<string name="slow_speed_limit">Velocidad lenta</string> <string name="slow_speed_limit">Velocidad lenta</string>
<string name="slow_speed_limit_description">Cuando el modo lento esté activado, la emulación se ejecutará a esta velocidad.</string> <string name="slow_speed_limit_description">Cuando el modo lento esté activado, la emulación se ejecutará a esta velocidad.</string>
<string name="cpu_backend">Motor de la CPU</string> <string name="cpu_backend">Motor del procesador</string>
<string name="cpu_accuracy">Precisión de la CPU</string> <string name="cpu_accuracy">Precisión del procesador</string>
<string name="value_with_units">%1$s%2$s</string> <string name="value_with_units">%1$s%2$s</string>
<!-- System settings strings --> <!-- System settings strings -->
@@ -433,7 +433,7 @@
<string name="set_custom_rtc">Configurar RTC personalizado</string> <string name="set_custom_rtc">Configurar RTC personalizado</string>
<!-- CPU --> <!-- CPU -->
<string name="fast_cpu_time">Overclock de la CPU</string> <string name="fast_cpu_time">Overclock del procesador</string>
<string name="fast_cpu_time_description">Fuerza a la CPU emulada a ejecutarser a una velocidad de reloj más alta, lo cual reduce ciertos limitadores de fotogramas por segundo. Usa Boost (1700 MHz) para ejecutar a la velocidad de reloj nativa más alta de la Switch, o Fast (2000 MHz) para ejecutar a una velocidad doble de reloj.</string> <string name="fast_cpu_time_description">Fuerza a la CPU emulada a ejecutarser a una velocidad de reloj más alta, lo cual reduce ciertos limitadores de fotogramas por segundo. Usa Boost (1700 MHz) para ejecutar a la velocidad de reloj nativa más alta de la Switch, o Fast (2000 MHz) para ejecutar a una velocidad doble de reloj.</string>
<string name="custom_cpu_ticks">Ticks de CPU personalizados</string> <string name="custom_cpu_ticks">Ticks de CPU personalizados</string>
<string name="custom_cpu_ticks_description">Establezca un valor personalizado de los ciclos de la CPU. Los valores más altos pueden aumentar el rendimiento, pero también pueden hacer que el juego se congele. Se recomienda un rango de 7721000.</string> <string name="custom_cpu_ticks_description">Establezca un valor personalizado de los ciclos de la CPU. Los valores más altos pueden aumentar el rendimiento, pero también pueden hacer que el juego se congele. Se recomienda un rango de 7721000.</string>
@@ -480,6 +480,10 @@
<string name="use_disk_shader_cache_description">Reduce los tirones almacenando y cargando los sombreadores generados.</string> <string name="use_disk_shader_cache_description">Reduce los tirones almacenando y cargando los sombreadores generados.</string>
<string name="renderer_force_max_clock">Forzar velocidad al máximo (solo Adreno)</string> <string name="renderer_force_max_clock">Forzar velocidad al máximo (solo Adreno)</string>
<string name="renderer_force_max_clock_description">Fuerza a la GPU a ejecutarse a la velocidad máxima de reloj posible (se seguirán aplicando restricciones térmicas).</string> <string name="renderer_force_max_clock_description">Fuerza a la GPU a ejecutarse a la velocidad máxima de reloj posible (se seguirán aplicando restricciones térmicas).</string>
<string name="renderer_asynchronous_gpu_emulation">Emulación de GPU asíncrona</string>
<string name="renderer_asynchronous_gpu_emulation_description">Ejecuta la emulación de la GPU de forma asíncrona para reducir los bloqueos de la CPU y mejorar el rendimiento. Desactiva esta opción solo si experimentas problemas de sincronización.</string>
<string name="renderer_async_presentation">Presentación asíncrona</string>
<string name="renderer_async_presentation_description">Mejora ligeramente el rendimiento al mover la presentación a un hilo independiente de la CPU.</string>
<string name="renderer_reactive_flushing">Usar limpieza reactiva</string> <string name="renderer_reactive_flushing">Usar limpieza reactiva</string>
<string name="renderer_reactive_flushing_description">Mejora la precisión de renderizado en algunos juegos, pero reduce el rendimiento.</string> <string name="renderer_reactive_flushing_description">Mejora la precisión de renderizado en algunos juegos, pero reduce el rendimiento.</string>
<string name="enable_buffer_history">Activar el historial del búfer</string> <string name="enable_buffer_history">Activar el historial del búfer</string>
@@ -532,7 +536,7 @@
<string name="warning_resolution">Escalar la resolución a 2x o más puede causar problemas y ralentizar significativamente su dispositivo.</string> <string name="warning_resolution">Escalar la resolución a 2x o más puede causar problemas y ralentizar significativamente su dispositivo.</string>
<!-- Debug settings strings --> <!-- Debug settings strings -->
<string name="cpu">CPU</string> <string name="cpu">Procesador</string>
<string name="use_auto_stub">Usar Auto Stub</string> <string name="use_auto_stub">Usar Auto Stub</string>
<string name="use_auto_stub_description">Rellena automáticamente servicios y funciones ausentes. Puede mejorar la compatibilidad pero puede causar cierres inesperados.</string> <string name="use_auto_stub_description">Rellena automáticamente servicios y funciones ausentes. Puede mejorar la compatibilidad pero puede causar cierres inesperados.</string>
@@ -482,6 +482,10 @@
<string name="use_disk_shader_cache_description">Уменьшение зависаний за счет хранения и загрузки сгенерированных шейдеров.</string> <string name="use_disk_shader_cache_description">Уменьшение зависаний за счет хранения и загрузки сгенерированных шейдеров.</string>
<string name="renderer_force_max_clock">Принудительная максимальная тактовая частота (только для Adreno)</string> <string name="renderer_force_max_clock">Принудительная максимальная тактовая частота (только для Adreno)</string>
<string name="renderer_force_max_clock_description">Заставляет ГПУ работать на максимально возможных тактовых частотах (тепловые ограничения все равно будут применяться).</string> <string name="renderer_force_max_clock_description">Заставляет ГПУ работать на максимально возможных тактовых частотах (тепловые ограничения все равно будут применяться).</string>
<string name="renderer_asynchronous_gpu_emulation">Асинхронная эмуляция ГПУ</string>
<string name="renderer_asynchronous_gpu_emulation_description">Выполняет эмуляцию ГПУ асинхронно для снижения задержек ЦП и увеличения производительности. Отключайте только при возникновении проблем с таймингами.</string>
<string name="renderer_async_presentation">Асинхронная презентация</string>
<string name="renderer_async_presentation_description">Немного улучшает производительность, перемещая презентацию в отдельный поток ЦП.</string>
<string name="renderer_reactive_flushing">Реактивная очистка</string> <string name="renderer_reactive_flushing">Реактивная очистка</string>
<string name="renderer_reactive_flushing_description">Повышение точности рендеринга в некоторых играх за счет снижения производительности.</string> <string name="renderer_reactive_flushing_description">Повышение точности рендеринга в некоторых играх за счет снижения производительности.</string>
<string name="enable_buffer_history">Включить историю буфера</string> <string name="enable_buffer_history">Включить историю буфера</string>
@@ -1080,6 +1084,7 @@
<string name="app_language_system">Следовать системе</string> <string name="app_language_system">Следовать системе</string>
<!-- Static Themes --> <!-- Static Themes -->
<string name="static_theme_color">Цвет темы</string> <string name="static_theme_color">Цвет темы</string>
<string name="eden_theme">Eden</string>
<string name="violet">Фиолетовый </string> <string name="violet">Фиолетовый </string>
<string name="blue">Синий</string> <string name="blue">Синий</string>
<string name="cyan">Циановый</string> <string name="cyan">Циановый</string>
@@ -482,6 +482,10 @@
<string name="use_disk_shader_cache_description">Зменшує затримки шляхом збереження шейдерів.</string> <string name="use_disk_shader_cache_description">Зменшує затримки шляхом збереження шейдерів.</string>
<string name="renderer_force_max_clock">Максимальна тактова частота (тільки Adreno)</string> <string name="renderer_force_max_clock">Максимальна тактова частота (тільки Adreno)</string>
<string name="renderer_force_max_clock_description">Змушує GPU працювати на максимальній тактовій частоті.</string> <string name="renderer_force_max_clock_description">Змушує GPU працювати на максимальній тактовій частоті.</string>
<string name="renderer_asynchronous_gpu_emulation">Асинхронна емуляція ГП</string>
<string name="renderer_asynchronous_gpu_emulation_description">Емуляція ГП виконується асинхронно для зменшення затримок ЦП й покращення пропускної здатності. Вимкніть лише у випадку виникнення проблем із таймінгами.</string>
<string name="renderer_async_presentation">Асинхронне подання</string>
<string name="renderer_async_presentation_description">Трохи покращує продуктивність завдяки переміщенню подання на окремий потік ЦП.</string>
<string name="renderer_reactive_flushing">Реактивне очищення</string> <string name="renderer_reactive_flushing">Реактивне очищення</string>
<string name="renderer_reactive_flushing_description">Покращує точність рендерингу в деяких іграх.</string> <string name="renderer_reactive_flushing_description">Покращує точність рендерингу в деяких іграх.</string>
<string name="enable_buffer_history">Увімкнути історію буфера</string> <string name="enable_buffer_history">Увімкнути історію буфера</string>
@@ -55,7 +55,7 @@
<string name="stats_overlay_options_description">配置性能统计叠加层中显示的信息</string> <string name="stats_overlay_options_description">配置性能统计叠加层中显示的信息</string>
<string name="show_fps">显示帧率</string> <string name="show_fps">显示帧率</string>
<string name="show_fps_description">显示当前帧率</string> <string name="show_fps_description">显示当前帧率</string>
<string name="show_frametime">显示帧时间</string> <string name="show_frametime">显示 Frametime</string>
<string name="show_app_ram_usage">显示应用内存使用情况</string> <string name="show_app_ram_usage">显示应用内存使用情况</string>
<string name="show_app_ram_usage_description">显示模拟器内存用量</string> <string name="show_app_ram_usage_description">显示模拟器内存用量</string>
<string name="show_system_ram_usage">显示系统内存使用情况</string> <string name="show_system_ram_usage">显示系统内存使用情况</string>
@@ -476,21 +476,25 @@
<string name="use_disk_shader_cache_description">将生成的着色器缓存于磁盘中并进行读取,以减少卡顿。</string> <string name="use_disk_shader_cache_description">将生成的着色器缓存于磁盘中并进行读取,以减少卡顿。</string>
<string name="renderer_force_max_clock">强制最大时钟 (仅限 Adreno)</string> <string name="renderer_force_max_clock">强制最大时钟 (仅限 Adreno)</string>
<string name="renderer_force_max_clock_description">强制 GPU 以最大时钟运行 (仍被温控限制)。</string> <string name="renderer_force_max_clock_description">强制 GPU 以最大时钟运行 (仍被温控限制)。</string>
<string name="renderer_asynchronous_gpu_emulation">GPU 异步模拟</string>
<string name="renderer_asynchronous_gpu_emulation_description">异步运行 GPU 模拟,以减少 CPU 停顿并提高吞吐量。仅当遇到与时序相关的问题时才应禁用此功能。</string>
<string name="renderer_async_presentation">异步呈现</string>
<string name="renderer_async_presentation_description">通过将呈现操作移至单独的 CPU 线程来略微提升性能。</string>
<string name="renderer_reactive_flushing">启用反应性刷新</string> <string name="renderer_reactive_flushing">启用反应性刷新</string>
<string name="renderer_reactive_flushing_description">牺牲性能提高某些游戏的渲染精度。</string> <string name="renderer_reactive_flushing_description">通过牺牲性能提高某些游戏的渲染精度。</string>
<string name="enable_buffer_history">启用缓冲区历史</string> <string name="enable_buffer_history">启用缓冲区历史</string>
<string name="enable_buffer_history_description">允许访问之前的缓冲状态。\n这个选项可能会提升某些游戏渲染质量性能一致性。</string> <string name="enable_buffer_history_description">启用对先前缓冲状态的访问。此选项可在某些游戏中提升渲染质量并保持性能一致性。</string>
<string name="use_optimized_vertex_buffers">优化顶点缓冲区</string> <string name="use_optimized_vertex_buffers">优化顶点缓冲区</string>
<string name="use_optimized_vertex_buffers_description">实现优化顶点缓冲区绑定以提升性能。需要 Mesa 26.0+ Turnip 驱动。老驱动会崩溃。</string> <string name="use_optimized_vertex_buffers_description">启用经过优化顶点缓冲区绑定以提升性能。需要 Mesa 26.0 及以上版本的 Turnip 驱动程序。在旧版驱动程序上会导致程序崩溃。</string>
<string name="hacks">Hacks</string> <string name="hacks">Hacks</string>
<string name="fast_gpu_time">GPU 超频频率</string> <string name="fast_gpu_time">GPU 超频频率</string>
<string name="fast_gpu_time_description">强制大多数游戏以其最高原生分辨率运行。使用 256 获得最性能,使用 512 获得最大图形保真度。</string> <string name="fast_gpu_time_description">强制大多数游戏以其最高原生分辨率运行。设置为 256 获得最性能,设置为 512 获得最佳画面保真度。</string>
<string name="skip_cpu_inner_invalidation">跳过CPU内部无效化</string> <string name="skip_cpu_inner_invalidation">跳过CPU内部无效化</string>
<string name="skip_cpu_inner_invalidation_description">在内存更新期间跳过某些CPU端缓存无效化,减少CPU使用率并提高其性能。可能会导致某些游戏出现故障或崩溃。</string> <string name="skip_cpu_inner_invalidation_description">在内存更新期间跳过某些CPU端缓存无效化,减少CPU使用率并提高其性能。可能会导致某些游戏出现故障或崩溃。</string>
<string name="fix_bloom_effects">修复泛光效果</string> <string name="fix_bloom_effects">修复 Bloom 效果</string>
<string name="fix_bloom_effects_description">减少《塞尔达传说:智慧的再现》(LA/EOW) 在 Adreno 700 系列 GPU 上的模糊,并移除《横冲直撞》(Burnout) 中的模糊特效。警告:可能会导致其他游戏出现画面花屏或异常</string> <string name="fix_bloom_effects_description">减少《塞尔达传说:智慧的再现》Adreno 700)中的 bloom 模糊,并移除《Burnout》中的 bloom 效果。警告:可能会导致其他游戏出现画面显示问题</string>
<string name="renderer_asynchronous_shaders">使用异步着色器</string> <string name="renderer_asynchronous_shaders">使用异步着色器</string>
<string name="renderer_asynchronous_shaders_description">异步编译着色器。这可能会减少卡顿,但也可能会导致图形错误。</string> <string name="renderer_asynchronous_shaders_description">异步编译着色器。这可能会减少卡顿,但也可能会导致图形错误。</string>
<string name="gpu_unswizzle_settings">GPU 还原设置</string> <string name="gpu_unswizzle_settings">GPU 还原设置</string>
@@ -501,7 +501,7 @@
<string name="enable_buffer_history">Enable buffer history</string> <string name="enable_buffer_history">Enable buffer history</string>
<string name="enable_buffer_history_description">Enables access to previous buffer states. This option may improve rendering quality and performance consistency in some games.</string> <string name="enable_buffer_history_description">Enables access to previous buffer states. This option may improve rendering quality and performance consistency in some games.</string>
<string name="use_optimized_vertex_buffers">Optimized Vertex Buffers</string> <string name="use_optimized_vertex_buffers">Optimized Vertex Buffers</string>
<string name="use_optimized_vertex_buffers_description">Enables optimized vertex buffer binding for improved performance. Requires Mesa 26.0+ Turnip drivers. Will crash on older drivers.</string> <string name="use_optimized_vertex_buffers_description">Enables optimized vertex buffer binding for improved performance. Requires Mesa 26.0+ Turnip drivers/ QCOM drivers. Will crash on older Turnip drivers.</string>
<string name="hacks">Hacks</string> <string name="hacks">Hacks</string>
@@ -510,7 +510,9 @@
<string name="skip_cpu_inner_invalidation">Skip CPU Inner Invalidation</string> <string name="skip_cpu_inner_invalidation">Skip CPU Inner Invalidation</string>
<string name="skip_cpu_inner_invalidation_description">Skips certain CPU-side cache invalidations during memory updates, reducing CPU usage and improving it\'s performance. This may cause glitches or crashes on some games.</string> <string name="skip_cpu_inner_invalidation_description">Skips certain CPU-side cache invalidations during memory updates, reducing CPU usage and improving it\'s performance. This may cause glitches or crashes on some games.</string>
<string name="fix_bloom_effects">Fix Bloom Effects</string> <string name="fix_bloom_effects">Fix Bloom Effects</string>
<string name="fix_bloom_effects_description">Reduces bloom blur in LA/EOW (Adreno 700), removes bloom in Burnout. Warning: may cause graphical artifacts in other games.</string> <string name="fix_bloom_effects_description">Reduces bloom blur in LA/EOW (Adreno A6XX - A7XX/ Turnip), removes bloom in Burnout. Warning: may cause graphical artifacts in other games.</string>
<string name="emulate_bgr565">Emulate BGR565</string>
<string name="emulate_bgr565_description">Fixes problems with inverted colors in games or strange artifacts or strange shadows.</string>
<string name="renderer_asynchronous_shaders">Use asynchronous shaders</string> <string name="renderer_asynchronous_shaders">Use asynchronous shaders</string>
<string name="renderer_asynchronous_shaders_description">Compiles shaders asynchronously. This may reduce stutters but may also introduce glitches.</string> <string name="renderer_asynchronous_shaders_description">Compiles shaders asynchronously. This may reduce stutters but may also introduce glitches.</string>
<string name="gpu_unswizzle_settings">GPU Unswizzle Settings</string> <string name="gpu_unswizzle_settings">GPU Unswizzle Settings</string>
@@ -243,6 +243,9 @@ void AndroidKeyboard::SubmitInlineKeyboardInput(int key_code) {
static_cast<s32>(m_current_text.size())); static_cast<s32>(m_current_text.size()));
break; break;
case KEYCODE_DEL: case KEYCODE_DEL:
if (m_current_text.empty()) {
return;
}
m_current_text.pop_back(); m_current_text.pop_back();
submit_inline_callback(Service::AM::Frontend::SwkbdReplyType::ChangedString, m_current_text, submit_inline_callback(Service::AM::Frontend::SwkbdReplyType::ChangedString, m_current_text,
static_cast<int>(m_current_text.size())); static_cast<int>(m_current_text.size()));
+7 -4
View File
@@ -555,6 +555,9 @@ struct Values {
SwitchableSetting<bool> fix_bloom_effects{linkage, false, "fix_bloom_effects", SwitchableSetting<bool> fix_bloom_effects{linkage, false, "fix_bloom_effects",
Category::RendererHacks}; Category::RendererHacks};
SwitchableSetting<bool> emulate_bgr565{linkage, false, "emulate_bgr565",
Category::RendererHacks};
SwitchableSetting<bool> rescale_hack{linkage, false, "rescale_hack", SwitchableSetting<bool> rescale_hack{linkage, false, "rescale_hack",
Category::RendererHacks}; Category::RendererHacks};
@@ -584,7 +587,7 @@ struct Values {
SwitchableSetting<ExtendedDynamicState> dyna_state{linkage, SwitchableSetting<ExtendedDynamicState> dyna_state{linkage,
#if defined(ANDROID) #if defined(ANDROID)
ExtendedDynamicState::EDS1, ExtendedDynamicState::Disabled,
#elif defined(__APPLE__) #elif defined(__APPLE__)
ExtendedDynamicState::Disabled, ExtendedDynamicState::Disabled,
#else #else
@@ -636,8 +639,8 @@ struct Values {
Category::System}; Category::System};
SwitchableSetting<Region, true> region_index{linkage, Region::Usa, "region_index", Category::System}; SwitchableSetting<Region, true> region_index{linkage, Region::Usa, "region_index", Category::System};
SwitchableSetting<TimeZone, true> time_zone_index{linkage, TimeZone::Auto, "time_zone_index", Category::System}; SwitchableSetting<TimeZone, true> time_zone_index{linkage, TimeZone::Auto, "time_zone_index", Category::System};
Setting<u32> serial_battery{linkage, 0, "serial_battery", Category::System}; Setting<u32> serial_battery{linkage, 0, "serial_battery", Category::Debugging};
Setting<u32> serial_unit{linkage, 0, "serial_unit", Category::System}; Setting<u32> serial_unit{linkage, 0, "serial_unit", Category::Debugging};
// Measured in seconds since epoch // Measured in seconds since epoch
SwitchableSetting<bool> custom_rtc_enabled{linkage, false, "custom_rtc_enabled", Category::System, Specialization::Paired, true, true}; SwitchableSetting<bool> custom_rtc_enabled{linkage, false, "custom_rtc_enabled", Category::System, Specialization::Paired, true, true};
SwitchableSetting<s64> custom_rtc{ SwitchableSetting<s64> custom_rtc{
@@ -807,7 +810,7 @@ struct Values {
0, 0,
65535, 65535,
"debug_knobs", "debug_knobs",
Category::Core, Category::Debugging,
Specialization::Countable, Specialization::Countable,
true, true,
true}; true};
@@ -289,7 +289,6 @@ void ArmDynarmic32::MakeJit(Common::PageTable* page_table) {
config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_UnfuseFMA; config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_UnfuseFMA;
config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_IgnoreStandardFPCRValue; config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_IgnoreStandardFPCRValue;
config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_InaccurateNaN; config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_InaccurateNaN;
config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_IgnoreGlobalMonitor;
break; break;
// Paranoia mode for debugging optimizations // Paranoia mode for debugging optimizations
case Settings::CpuAccuracy::Paranoid: case Settings::CpuAccuracy::Paranoid:
@@ -340,7 +340,6 @@ void ArmDynarmic64::MakeJit(Common::PageTable* page_table, std::size_t address_s
config.unsafe_optimizations = true; config.unsafe_optimizations = true;
config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_UnfuseFMA; config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_UnfuseFMA;
config.fastmem_address_space_bits = 64; config.fastmem_address_space_bits = 64;
config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_IgnoreGlobalMonitor;
break; break;
// Paranoia mode for debugging optimizations // Paranoia mode for debugging optimizations
case Settings::CpuAccuracy::Paranoid: case Settings::CpuAccuracy::Paranoid:
+43 -59
View File
@@ -6,6 +6,8 @@
#include <condition_variable> #include <condition_variable>
#include <mutex> #include <mutex>
#include <optional>
#include <stop_token>
#include <thread> #include <thread>
#include "core/core.h" #include "core/core.h"
@@ -18,59 +20,53 @@ namespace Kernel::Svc {
constexpr auto MAX_MSG_TIME = std::chrono::milliseconds(250); constexpr auto MAX_MSG_TIME = std::chrono::milliseconds(250);
const auto MAX_MSG_SIZE = 0x1000; const auto MAX_MSG_SIZE = 0x1000;
static std::string msg_buffer;
static std::mutex msg_mutex;
static std::condition_variable msg_cv;
static std::chrono::steady_clock::time_point last_msg_time;
static bool worker_running = true;
static std::unique_ptr<std::thread> flush_thread;
static std::once_flag start_flag;
static void FlushDbgLoop() {
while (true) {
std::unique_lock lock(msg_mutex);
msg_cv.wait(lock, [] { return !msg_buffer.empty() || !worker_running; });
if (!worker_running && msg_buffer.empty()) break;
auto timeout = last_msg_time + MAX_MSG_TIME;
bool woke_early = msg_cv.wait_until(lock, timeout, [] {
return msg_buffer.size() >= MAX_MSG_SIZE || !worker_running;
});
if (!woke_early || msg_buffer.size() >= MAX_MSG_SIZE || !worker_running) {
if (!msg_buffer.empty()) {
// Remove trailing newline as LOG_INFO adds that anyways
if (msg_buffer.back() == '\n')
msg_buffer.pop_back();
LOG_INFO(Debug_Emulated, "\n{}", msg_buffer);
msg_buffer.clear();
}
if (!worker_running) break;
}
}
}
/// Used to output a message on a debug hardware unit - does nothing on a retail unit /// Used to output a message on a debug hardware unit - does nothing on a retail unit
Result OutputDebugString(Core::System& system, u64 address, u64 len) { Result OutputDebugString(Core::System& system, u64 address, u64 len) {
static struct DebugFlusher {
std::string msg_buffer;
std::mutex msg_mutex;
std::condition_variable msg_cv;
std::chrono::steady_clock::time_point last_msg_time;
std::optional<std::jthread> thread;
} flusher_data;
R_SUCCEED_IF(len == 0); R_SUCCEED_IF(len == 0);
// Only start the thread the very first time this function is called // Only start the thread the very first time this function is called
std::call_once(start_flag, [] { if (!flusher_data.thread) {
flush_thread = std::make_unique<std::thread>(FlushDbgLoop); flusher_data.thread.emplace([](std::stop_token stop_token) {
}); while (!stop_token.stop_requested()) {
std::unique_lock lock(flusher_data.msg_mutex);
flusher_data.msg_cv.wait(lock, [&stop_token] {
return !flusher_data.msg_buffer.empty() || stop_token.stop_requested();
});
if (stop_token.stop_requested() && flusher_data.msg_buffer.empty())
break;
auto timeout = flusher_data.last_msg_time + MAX_MSG_TIME;
bool woke_early = flusher_data.msg_cv.wait_until(lock, timeout, [&stop_token] {
return flusher_data.msg_buffer.size() >= MAX_MSG_SIZE || stop_token.stop_requested();
});
if (!woke_early || flusher_data.msg_buffer.size() >= MAX_MSG_SIZE || stop_token.stop_requested()) {
if (!flusher_data.msg_buffer.empty()) {
// Remove trailing newline as LOG_INFO adds that anyways
if (flusher_data.msg_buffer.back() == '\n')
flusher_data.msg_buffer.pop_back();
{ LOG_INFO(Debug_Emulated, "\n{}", flusher_data.msg_buffer);
std::lock_guard lock(msg_mutex); flusher_data.msg_buffer.clear();
const auto old_size = msg_buffer.size(); }
msg_buffer.resize(old_size + len); if (stop_token.stop_requested()) break;
GetCurrentMemory(system.Kernel()).ReadBlock(address, msg_buffer.data() + old_size, len); }
}
last_msg_time = std::chrono::steady_clock::now(); flusher_data.msg_cv.notify_all();
});
} }
{
msg_cv.notify_one(); std::lock_guard lock(flusher_data.msg_mutex);
const auto old_size = flusher_data.msg_buffer.size();
flusher_data.msg_buffer.resize(old_size + len);
GetCurrentMemory(system.Kernel()).ReadBlock(address, flusher_data.msg_buffer.data() + old_size, len);
flusher_data.last_msg_time = std::chrono::steady_clock::now();
}
flusher_data.msg_cv.notify_one();
R_SUCCEED(); R_SUCCEED();
} }
@@ -82,16 +78,4 @@ Result OutputDebugString64From32(Core::System& system, uint32_t debug_str, uint3
R_RETURN(OutputDebugString(system, debug_str, len)); R_RETURN(OutputDebugString(system, debug_str, len));
} }
struct BufferAutoFlush {
~BufferAutoFlush() {
{
std::lock_guard lock(msg_mutex);
worker_running = false;
}
msg_cv.notify_all();
if (flush_thread && flush_thread->joinable()) flush_thread->join();
}
};
static BufferAutoFlush auto_flusher;
} // namespace Kernel::Svc } // namespace Kernel::Svc
@@ -23,16 +23,14 @@ static const auto default_cg_mode = nullptr; //Allow RWE
namespace Dynarmic { namespace Dynarmic {
void EmitSpinLockLock(Xbyak::CodeGenerator& code, Xbyak::Reg64 ptr, Xbyak::Reg32 tmp, bool waitpkg) { void EmitSpinLockLock(Xbyak::CodeGenerator& code, Xbyak::Reg64 ptr, Xbyak::Reg32 tmp, bool waitpkg) {
// TODO: this is because we lack regalloc - so better to be safe :(
if (waitpkg) {
code.push(Xbyak::util::eax);
code.push(Xbyak::util::ebx);
code.push(Xbyak::util::edx);
}
Xbyak::Label start, loop; Xbyak::Label start, loop;
code.jmp(start, code.T_NEAR); code.jmp(start, code.T_NEAR);
code.L(loop); code.L(loop);
if (waitpkg) { if (waitpkg) {
// TODO: this is because we lack regalloc - so better to be safe :(
code.push(Xbyak::util::rax);
code.push(Xbyak::util::rbx);
code.push(Xbyak::util::rdx);
// TODO: This clobbers EAX and EDX did we tell the regalloc? // TODO: This clobbers EAX and EDX did we tell the regalloc?
// ARM ptr for address-monitoring // ARM ptr for address-monitoring
code.umonitor(ptr); code.umonitor(ptr);
@@ -49,6 +47,9 @@ void EmitSpinLockLock(Xbyak::CodeGenerator& code, Xbyak::Reg64 ptr, Xbyak::Reg32
code.umwait(Xbyak::util::ebx); code.umwait(Xbyak::util::ebx);
// CF == 1 if we hit the OS-timeout in IA32_UMWAIT_CONTROL without a write // CF == 1 if we hit the OS-timeout in IA32_UMWAIT_CONTROL without a write
// CF == 0 if we exited the wait for any other reason // CF == 0 if we exited the wait for any other reason
code.pop(Xbyak::util::rdx);
code.pop(Xbyak::util::rbx);
code.pop(Xbyak::util::rax);
} else { } else {
code.pause(); code.pause();
} }
@@ -57,11 +58,6 @@ void EmitSpinLockLock(Xbyak::CodeGenerator& code, Xbyak::Reg64 ptr, Xbyak::Reg32
/*code.lock();*/ code.xchg(code.dword[ptr], tmp); /*code.lock();*/ code.xchg(code.dword[ptr], tmp);
code.test(tmp, tmp); code.test(tmp, tmp);
code.jnz(loop, code.T_NEAR); code.jnz(loop, code.T_NEAR);
if (waitpkg) {
code.pop(Xbyak::util::edx);
code.pop(Xbyak::util::ebx);
code.pop(Xbyak::util::eax);
}
} }
void EmitSpinLockUnlock(Xbyak::CodeGenerator& code, Xbyak::Reg64 ptr, Xbyak::Reg32 tmp) { void EmitSpinLockUnlock(Xbyak::CodeGenerator& code, Xbyak::Reg64 ptr, Xbyak::Reg32 tmp) {
+2 -3
View File
@@ -169,6 +169,8 @@ std::unique_ptr<TranslationMap> InitializeTranslations(QObject* parent) {
tr("Runs an additional optimization pass over generated SPIRV shaders.\n" tr("Runs an additional optimization pass over generated SPIRV shaders.\n"
"Will increase time required for shader compilation.\nMay slightly improve " "Will increase time required for shader compilation.\nMay slightly improve "
"performance.\nThis feature is experimental.")); "performance.\nThis feature is experimental."));
INSERT(Settings, use_asynchronous_gpu_emulation, tr("Use asynchronous GPU emulation"),
tr("Uses an extra CPU thread for rendering.\nThis option should always remain enabled."));
INSERT(Settings, nvdec_emulation, tr("NVDEC emulation:"), INSERT(Settings, nvdec_emulation, tr("NVDEC emulation:"),
tr("Specifies how videos should be decoded.\nIt can either use the CPU or the GPU for " tr("Specifies how videos should be decoded.\nIt can either use the CPU or the GPU for "
"decoding, or perform no decoding at all (black screen on videos).\n" "decoding, or perform no decoding at all (black screen on videos).\n"
@@ -315,9 +317,6 @@ std::unique_ptr<TranslationMap> InitializeTranslations(QObject* parent) {
"their resolution, details and supported controllers and depending on this setting.\n" "their resolution, details and supported controllers and depending on this setting.\n"
"Setting to Handheld can help improve performance for low end systems.")); "Setting to Handheld can help improve performance for low end systems."));
INSERT(Settings, current_user, QString(), QString()); INSERT(Settings, current_user, QString(), QString());
INSERT(Settings, serial_unit, tr("Unit Serial"), QString());
INSERT(Settings, serial_battery, tr("Battery Serial"), QString());
INSERT(Settings, debug_knobs, tr("Debug knobs"), QString());
// Controls // Controls
@@ -178,6 +178,9 @@ void DefineGenericOutput(EmitContext& ctx, size_t index, std::optional<u32> invo
ctx.Decorate(id, spv::Decoration::XfbBuffer, xfb_varying->buffer); ctx.Decorate(id, spv::Decoration::XfbBuffer, xfb_varying->buffer);
ctx.Decorate(id, spv::Decoration::XfbStride, xfb_varying->stride); ctx.Decorate(id, spv::Decoration::XfbStride, xfb_varying->stride);
ctx.Decorate(id, spv::Decoration::Offset, xfb_varying->offset); ctx.Decorate(id, spv::Decoration::Offset, xfb_varying->offset);
if (ctx.stage == Stage::Geometry && xfb_varying->stream != 0) {
ctx.Decorate(id, spv::Decoration::Stream, xfb_varying->stream);
}
} }
if (num_components < 4 || element > 0) { if (num_components < 4 || element > 0) {
const std::string_view subswizzle{swizzle.substr(element, num_components)}; const std::string_view subswizzle{swizzle.substr(element, num_components)};
+1
View File
@@ -76,6 +76,7 @@ enum class TessSpacing {
struct TransformFeedbackVarying { struct TransformFeedbackVarying {
u32 buffer{}; u32 buffer{};
u32 stream{};
u32 stride{}; u32 stride{};
u32 offset{}; u32 offset{};
u32 components{}; u32 components{};
+18 -15
View File
@@ -1067,26 +1067,29 @@ void BufferCache<P>::BindHostTransformFeedbackBuffers() {
HostBindings<typename P::Buffer> host_bindings; HostBindings<typename P::Buffer> host_bindings;
for (u32 index = 0; index < NUM_TRANSFORM_FEEDBACK_BUFFERS; ++index) { for (u32 index = 0; index < NUM_TRANSFORM_FEEDBACK_BUFFERS; ++index) {
const Binding& binding = channel_state->transform_feedback_buffers[index]; const Binding& binding = channel_state->transform_feedback_buffers[index];
if (maxwell3d->regs.transform_feedback.controls[index].varying_count == 0 && const auto& control = maxwell3d->regs.transform_feedback.controls[index];
maxwell3d->regs.transform_feedback.controls[index].stride == 0) { const bool has_layout = control.varying_count != 0 || control.stride != 0;
break;
Buffer* host_buffer = &slot_buffers[NULL_BUFFER_ID];
u32 offset = 0;
u32 size = 0;
if (has_layout && binding.buffer_id != NULL_BUFFER_ID && binding.size != 0) {
Buffer& buffer = slot_buffers[binding.buffer_id];
TouchBuffer(buffer, binding.buffer_id);
size = binding.size;
SynchronizeBuffer(buffer, binding.device_addr, size);
MarkWrittenBuffer(binding.buffer_id, binding.device_addr, size);
offset = buffer.Offset(binding.device_addr);
buffer.MarkUsage(offset, size);
host_buffer = &buffer;
} }
Buffer& buffer = slot_buffers[binding.buffer_id];
TouchBuffer(buffer, binding.buffer_id);
const u32 size = binding.size;
SynchronizeBuffer(buffer, binding.device_addr, size);
MarkWrittenBuffer(binding.buffer_id, binding.device_addr, size); host_bindings.buffers.push_back(host_buffer);
const u32 offset = buffer.Offset(binding.device_addr);
buffer.MarkUsage(offset, size);
host_bindings.buffers.push_back(&buffer);
host_bindings.offsets.push_back(offset); host_bindings.offsets.push_back(offset);
host_bindings.sizes.push_back(size); host_bindings.sizes.push_back(size);
} }
if (host_bindings.buffers.size() > 0) { runtime.BindTransformFeedbackBuffers(host_bindings);
runtime.BindTransformFeedbackBuffers(host_bindings);
}
} }
template <class P> template <class P>
+11 -2
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-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
@@ -92,7 +92,16 @@ void ThreadManager::InvalidateRegion(DAddr addr, u64 size) {
} }
void ThreadManager::FlushAndInvalidateRegion(DAddr addr, u64 size) { void ThreadManager::FlushAndInvalidateRegion(DAddr addr, u64 size) {
// Skip flush on asynch mode, as FlushAndInvalidateRegion is not used for anything too important if (Settings::IsGPULevelHigh()) {
if (!is_async) {
PushCommand(FlushRegionCommand(addr, size));
} else {
auto& gpu = system.GPU();
const u64 fence = gpu.RequestFlush(addr, size);
TickGPU();
gpu.WaitForSyncOperation(fence);
}
}
rasterizer->OnCacheInvalidation(addr, size); rasterizer->OnCacheInvalidation(addr, size);
} }
@@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
@@ -5,16 +8,22 @@
layout(local_size_x = 1) in; layout(local_size_x = 1) in;
layout(std430, binding = 0) buffer Query { layout(std430, binding = 0) readonly buffer Query {
uvec2 initial; uint data[];
uvec2 unknown;
uvec2 current;
}; };
layout(std430, binding = 1) buffer Result { layout(std430, binding = 1) writeonly buffer Result {
uint result; uint result;
}; };
layout(push_constant) uniform PushConstants {
uint compare_to_zero;
};
void main() { void main() {
result = all(equal(initial, current)) ? 1 : 0; if (compare_to_zero != 0u) {
result = (data[0] != 0u && data[1] != 0u) ? 1u : 0u;
} else {
result = (data[0] == data[4] && data[1] == data[5]) ? 1u : 0u;
}
} }
+2 -2
View File
@@ -285,11 +285,11 @@ void HLE_MultiDrawIndexedIndirectCount::Fallback(Engines::Maxwell3D& maxwell3d,
} }
void HLE_DrawIndirectByteCount::Execute(Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters, [[maybe_unused]] u32 method) { void HLE_DrawIndirectByteCount::Execute(Engines::Maxwell3D& maxwell3d, std::span<const u32> parameters, [[maybe_unused]] u32 method) {
const bool force = maxwell3d.Rasterizer().HasDrawTransformFeedback(); const bool force = maxwell3d.Rasterizer().HasDrawTransformFeedback();
auto topology = Maxwell3D::Regs::PrimitiveTopology(parameters[0] & 0xFFFFU); if (!force) {
if (!force && (!maxwell3d.AnyParametersDirty() || !IsTopologySafe(topology))) {
Fallback(maxwell3d, parameters); Fallback(maxwell3d, parameters);
return; return;
} }
auto topology = Maxwell3D::Regs::PrimitiveTopology(parameters[0] & 0xFFFFU);
auto& params = maxwell3d.draw_manager->GetIndirectParams(); auto& params = maxwell3d.draw_manager->GetIndirectParams();
params.is_byte_count = true; params.is_byte_count = true;
params.is_indexed = false; params.is_indexed = false;
+1
View File
@@ -412,6 +412,7 @@ bool QueryCacheBase<Traits>::AccelerateHostConditionalRendering() {
.found_query = nullptr, .found_query = nullptr,
}; };
} }
it_current = it_current_2;
} }
auto* query = impl->ObtainQuery(it_current->second); auto* query = impl->ObtainQuery(it_current->second);
qc_dirty |= True(query->flags & QueryFlagBits::IsHostManaged) && qc_dirty |= True(query->flags & QueryFlagBits::IsHostManaged) &&
+4 -1
View File
@@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
@@ -75,7 +78,7 @@ public:
} }
u64 GetDependentMask() const { u64 GetDependentMask() const {
return dependence_mask; return dependent_mask;
} }
u64 GetAmendValue() const { u64 GetAmendValue() const {
@@ -629,6 +629,9 @@ void RasterizerOpenGL::ReleaseFences(bool force) {
void RasterizerOpenGL::FlushAndInvalidateRegion(DAddr addr, u64 size, void RasterizerOpenGL::FlushAndInvalidateRegion(DAddr addr, u64 size,
VideoCommon::CacheType which) { VideoCommon::CacheType which) {
if (Settings::IsGPULevelHigh()) {
FlushRegion(addr, size, which);
}
InvalidateRegion(addr, size, which); InvalidateRegion(addr, size, which);
} }
@@ -190,9 +190,7 @@ void FixedPipelineState::Refresh(Tegra::Engines::Maxwell3D& maxwell3d, DynamicFe
} }
} }
} }
if (!extended_dynamic_state_3_enables) { dynamic_state.Refresh3(regs, features);
dynamic_state.Refresh3(regs);
}
if (xfb_enabled) { if (xfb_enabled) {
RefreshXfbState(xfb_state, regs); RefreshXfbState(xfb_state, regs);
} }
@@ -295,16 +293,22 @@ void FixedPipelineState::DynamicState::Refresh2(const Maxwell& regs,
depth_bias_enable.Assign(enabled_lut[POLYGON_OFFSET_ENABLE_LUT[topology_index]] != 0 ? 1 : 0); depth_bias_enable.Assign(enabled_lut[POLYGON_OFFSET_ENABLE_LUT[topology_index]] != 0 ? 1 : 0);
} }
void FixedPipelineState::DynamicState::Refresh3(const Maxwell& regs) { void FixedPipelineState::DynamicState::Refresh3(const Maxwell& regs,
logic_op_enable.Assign(regs.logic_op.enable != 0 ? 1 : 0); const DynamicFeatures& features) {
depth_clamp_disabled.Assign(regs.viewport_clip_control.geometry_clip == if (!features.has_dynamic_state3_logic_op_enable) {
Maxwell::ViewportClipControl::GeometryClip::Passthrough || logic_op_enable.Assign(regs.logic_op.enable != 0 ? 1 : 0);
regs.viewport_clip_control.geometry_clip == }
Maxwell::ViewportClipControl::GeometryClip::FrustumXYZ || if (!features.has_dynamic_state3_depth_clamp_enable) {
regs.viewport_clip_control.geometry_clip == depth_clamp_disabled.Assign(regs.viewport_clip_control.geometry_clip ==
Maxwell::ViewportClipControl::GeometryClip::FrustumZ); Maxwell::ViewportClipControl::GeometryClip::Passthrough ||
regs.viewport_clip_control.geometry_clip ==
line_stipple_enable.Assign(regs.line_stipple_enable); Maxwell::ViewportClipControl::GeometryClip::FrustumXYZ ||
regs.viewport_clip_control.geometry_clip ==
Maxwell::ViewportClipControl::GeometryClip::FrustumZ);
}
if (!features.has_dynamic_state3_line_stipple_enable) {
line_stipple_enable.Assign(regs.line_stipple_enable);
}
} }
size_t FixedPipelineState::Hash() const noexcept { size_t FixedPipelineState::Hash() const noexcept {
@@ -27,6 +27,9 @@ struct DynamicFeatures {
bool has_extended_dynamic_state_2_patch_control_points; bool has_extended_dynamic_state_2_patch_control_points;
bool has_extended_dynamic_state_3_blend; bool has_extended_dynamic_state_3_blend;
bool has_extended_dynamic_state_3_enables; bool has_extended_dynamic_state_3_enables;
bool has_dynamic_state3_depth_clamp_enable;
bool has_dynamic_state3_logic_op_enable;
bool has_dynamic_state3_line_stipple_enable;
bool has_dynamic_vertex_input; bool has_dynamic_vertex_input;
bool has_provoking_vertex; bool has_provoking_vertex;
bool has_provoking_vertex_first_mode; bool has_provoking_vertex_first_mode;
@@ -175,7 +178,7 @@ struct FixedPipelineState {
void Refresh(const Maxwell& regs); void Refresh(const Maxwell& regs);
void Refresh2(const Maxwell& regs, Maxwell::PrimitiveTopology topology, void Refresh2(const Maxwell& regs, Maxwell::PrimitiveTopology topology,
bool base_features_supported); bool base_features_supported);
void Refresh3(const Maxwell& regs); void Refresh3(const Maxwell& regs, const DynamicFeatures& features);
Maxwell::ComparisonOp DepthTestFunc() const noexcept { Maxwell::ComparisonOp DepthTestFunc() const noexcept {
return UnpackComparisonOp(depth_test_func); return UnpackComparisonOp(depth_test_func);
@@ -265,8 +268,7 @@ struct FixedPipelineState {
return sizeof(*this); return sizeof(*this);
} }
if (dynamic_vertex_input && extended_dynamic_state_3_blend) { if (dynamic_vertex_input && extended_dynamic_state_3_blend) {
// Exclude dynamic state and attributes return offsetof(FixedPipelineState, attachments);
return offsetof(FixedPipelineState, dynamic_state);
} }
if (dynamic_vertex_input) { if (dynamic_vertex_input) {
// Exclude dynamic state // Exclude dynamic state
@@ -8,6 +8,7 @@
#include <array> #include <array>
#include <cstring> #include <cstring>
#include <memory> #include <memory>
#include <mutex>
#include <optional> #include <optional>
#include <string> #include <string>
#include <vector> #include <vector>
@@ -171,7 +172,11 @@ try
RendererVulkan::~RendererVulkan() { RendererVulkan::~RendererVulkan() {
scheduler.RegisterOnSubmit([] {}); scheduler.RegisterOnSubmit([] {});
void(device.GetLogical().WaitIdle()); scheduler.Finish();
{
std::scoped_lock lock{scheduler.submit_mutex};
void(device.GetLogical().WaitIdle());
}
} }
void RendererVulkan::Composite(std::span<const Tegra::FramebufferConfig> framebuffers) { void RendererVulkan::Composite(std::span<const Tegra::FramebufferConfig> framebuffers) {
@@ -8,6 +8,7 @@
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#include <vulkan/vulkan_core.h> #include <vulkan/vulkan_core.h>
#include <mutex>
#include "video_core/framebuffer_config.h" #include "video_core/framebuffer_config.h"
#include "video_core/present.h" #include "video_core/present.h"
#include "video_core/renderer_vulkan/present/filters.h" #include "video_core/renderer_vulkan/present/filters.h"
@@ -31,7 +32,10 @@ BlitScreen::~BlitScreen() = default;
void BlitScreen::WaitIdle() { void BlitScreen::WaitIdle() {
present_manager.WaitPresent(); present_manager.WaitPresent();
scheduler.Finish(); scheduler.Finish();
device.GetLogical().WaitIdle(); {
std::scoped_lock lock{scheduler.submit_mutex};
device.GetLogical().WaitIdle();
}
} }
void BlitScreen::SetWindowAdaptPass() { void BlitScreen::SetWindowAdaptPass() {
@@ -637,12 +637,10 @@ void BufferCacheRuntime::BindTransformFeedbackBuffers(VideoCommon::HostBindings<
for (u32 i = 0; i < bindings.buffers.size(); ++i) { for (u32 i = 0; i < bindings.buffers.size(); ++i) {
auto handle = bindings.buffers[i]->Handle(); auto handle = bindings.buffers[i]->Handle();
if (handle == VK_NULL_HANDLE) { if (handle == VK_NULL_HANDLE) {
ReserveNullBuffer();
handle = *null_buffer;
bindings.offsets[i] = 0; bindings.offsets[i] = 0;
bindings.sizes[i] = VK_WHOLE_SIZE; bindings.sizes[i] = 0;
if (!device.HasNullDescriptor()) {
ReserveNullBuffer();
handle = *null_buffer;
}
} }
buffer_handles[i] = handle; buffer_handles[i] = handle;
} }
@@ -228,6 +228,10 @@ struct QueriesPrefixScanPushConstants {
u32 accumulation_limit; u32 accumulation_limit;
u32 buffer_offset; u32 buffer_offset;
}; };
struct ConditionalRenderingResolvePushConstants {
u32 compare_to_zero;
};
} // Anonymous namespace } // Anonymous namespace
ComputePass::ComputePass(const Device& device_, DescriptorPool& descriptor_pool, ComputePass::ComputePass(const Device& device_, DescriptorPool& descriptor_pool,
@@ -413,7 +417,8 @@ ConditionalRenderingResolvePass::ConditionalRenderingResolvePass(
const Device& device_, Scheduler& scheduler_, DescriptorPool& descriptor_pool_, const Device& device_, Scheduler& scheduler_, DescriptorPool& descriptor_pool_,
ComputePassDescriptorQueue& compute_pass_descriptor_queue_) ComputePassDescriptorQueue& compute_pass_descriptor_queue_)
: ComputePass(device_, descriptor_pool_, INPUT_OUTPUT_DESCRIPTOR_SET_BINDINGS, : ComputePass(device_, descriptor_pool_, INPUT_OUTPUT_DESCRIPTOR_SET_BINDINGS,
INPUT_OUTPUT_DESCRIPTOR_UPDATE_TEMPLATE, INPUT_OUTPUT_BANK_INFO, nullptr, INPUT_OUTPUT_DESCRIPTOR_UPDATE_TEMPLATE, INPUT_OUTPUT_BANK_INFO,
COMPUTE_PUSH_CONSTANT_RANGE<sizeof(ConditionalRenderingResolvePushConstants)>,
RESOLVE_CONDITIONAL_RENDER_COMP_SPV), RESOLVE_CONDITIONAL_RENDER_COMP_SPV),
scheduler{scheduler_}, compute_pass_descriptor_queue{compute_pass_descriptor_queue_} {} scheduler{scheduler_}, compute_pass_descriptor_queue{compute_pass_descriptor_queue_} {}
@@ -430,7 +435,7 @@ void ConditionalRenderingResolvePass::Resolve(VkBuffer dst_buffer, VkBuffer src_
const void* const descriptor_data{compute_pass_descriptor_queue.UpdateData()}; const void* const descriptor_data{compute_pass_descriptor_queue.UpdateData()};
scheduler.RequestOutsideRenderPassOperationContext(); scheduler.RequestOutsideRenderPassOperationContext();
scheduler.Record([this, descriptor_data](vk::CommandBuffer cmdbuf) { scheduler.Record([this, descriptor_data, compare_to_zero](vk::CommandBuffer cmdbuf) {
static constexpr VkMemoryBarrier read_barrier{ static constexpr VkMemoryBarrier read_barrier{
.sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER, .sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER,
.pNext = nullptr, .pNext = nullptr,
@@ -443,6 +448,9 @@ void ConditionalRenderingResolvePass::Resolve(VkBuffer dst_buffer, VkBuffer src_
.srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT, .srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT,
.dstAccessMask = VK_ACCESS_CONDITIONAL_RENDERING_READ_BIT_EXT, .dstAccessMask = VK_ACCESS_CONDITIONAL_RENDERING_READ_BIT_EXT,
}; };
const ConditionalRenderingResolvePushConstants uniforms{
.compare_to_zero = compare_to_zero ? 1U : 0U,
};
const VkDescriptorSet set = descriptor_allocator.Commit(); const VkDescriptorSet set = descriptor_allocator.Commit();
device.GetLogical().UpdateDescriptorSet(set, *descriptor_template, descriptor_data); device.GetLogical().UpdateDescriptorSet(set, *descriptor_template, descriptor_data);
@@ -450,9 +458,11 @@ void ConditionalRenderingResolvePass::Resolve(VkBuffer dst_buffer, VkBuffer src_
VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0, read_barrier); VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0, read_barrier);
cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_COMPUTE, *pipeline); cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_COMPUTE, *pipeline);
cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_COMPUTE, *layout, 0, set, {}); cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_COMPUTE, *layout, 0, set, {});
cmdbuf.PushConstants(*layout, VK_SHADER_STAGE_COMPUTE_BIT, uniforms);
cmdbuf.Dispatch(1, 1, 1); cmdbuf.Dispatch(1, 1, 1);
cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT,
VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, 0, write_barrier); VK_PIPELINE_STAGE_CONDITIONAL_RENDERING_BIT_EXT, 0,
write_barrier);
}); });
} }
@@ -520,7 +530,7 @@ void QueriesPrefixScanPass::Run(VkBuffer accumulation_buffer, VkBuffer dst_buffe
const VkDescriptorSet set = descriptor_allocator.Commit(); const VkDescriptorSet set = descriptor_allocator.Commit();
device.GetLogical().UpdateDescriptorSet(set, *descriptor_template, descriptor_data); device.GetLogical().UpdateDescriptorSet(set, *descriptor_template, descriptor_data);
cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT,
VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0, read_barrier); VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0, read_barrier);
cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_COMPUTE, *pipeline); cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_COMPUTE, *pipeline);
cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_COMPUTE, *layout, 0, set, {}); cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_COMPUTE, *layout, 0, set, {});
@@ -467,6 +467,10 @@ bool GraphicsPipeline::ConfigureImpl(bool is_indexed) {
bind_stage_info(4); bind_stage_info(4);
} }
if (regs.transform_feedback_enabled != 0) {
scheduler.RequestOutsideRenderPassOperationContext();
}
buffer_cache.UpdateGraphicsBuffers(is_indexed); buffer_cache.UpdateGraphicsBuffers(is_indexed);
buffer_cache.BindHostGeometryBuffers(is_indexed); buffer_cache.BindHostGeometryBuffers(is_indexed);
@@ -485,6 +485,12 @@ PipelineCache::PipelineCache(Tegra::MaxwellDeviceMemoryManager& device_memory_,
device.IsExtExtendedDynamicState3BlendingSupported(); device.IsExtExtendedDynamicState3BlendingSupported();
dynamic_features.has_extended_dynamic_state_3_enables = dynamic_features.has_extended_dynamic_state_3_enables =
device.IsExtExtendedDynamicState3EnablesSupported(); device.IsExtExtendedDynamicState3EnablesSupported();
dynamic_features.has_dynamic_state3_depth_clamp_enable =
device.SupportsDynamicState3DepthClampEnable();
dynamic_features.has_dynamic_state3_logic_op_enable =
device.SupportsDynamicState3LogicOpEnable();
dynamic_features.has_dynamic_state3_line_stipple_enable =
device.SupportsDynamicState3LineStippleEnable();
// VIDS: Independent toggle (not affected by dyna_state levels) // VIDS: Independent toggle (not affected by dyna_state levels)
dynamic_features.has_dynamic_vertex_input = dynamic_features.has_dynamic_vertex_input =
@@ -189,7 +189,7 @@ void PresentManager::RecreateFrame(Frame* frame, u32 width, u32 height, VkFormat
frame->image = memory_allocator.CreateImage({ frame->image = memory_allocator.CreateImage({
.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
.pNext = nullptr, .pNext = nullptr,
.flags = VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT, .flags = VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT | VK_IMAGE_CREATE_EXTENDED_USAGE_BIT,
.imageType = VK_IMAGE_TYPE_2D, .imageType = VK_IMAGE_TYPE_2D,
.format = swapchain.GetImageFormat(), .format = swapchain.GetImageFormat(),
.extent = .extent =
+129 -26
View File
@@ -157,8 +157,9 @@ public:
ReserveHostQuery(); ReserveHostQuery();
scheduler.Record([query_pool = current_query_pool, scheduler.Record([query_pool = current_query_pool,
query_index = current_bank_slot](vk::CommandBuffer cmdbuf) { query_index = current_bank_slot](vk::CommandBuffer cmdbuf) {
const bool use_precise = Settings::IsGPULevelHigh(); const bool use_precise = Settings::IsGPULevelHigh();
cmdbuf.ResetQueryPool(query_pool, static_cast<u32>(query_index), 1);
cmdbuf.BeginQuery(query_pool, static_cast<u32>(query_index), cmdbuf.BeginQuery(query_pool, static_cast<u32>(query_index),
use_precise ? VK_QUERY_CONTROL_PRECISE_BIT : 0); use_precise ? VK_QUERY_CONTROL_PRECISE_BIT : 0);
}); });
@@ -220,8 +221,7 @@ public:
} }
PauseCounter(); PauseCounter();
const auto driver_id = device.GetDriverID(); const auto driver_id = device.GetDriverID();
if (driver_id == VK_DRIVER_ID_QUALCOMM_PROPRIETARY || if (driver_id == VK_DRIVER_ID_ARM_PROPRIETARY || driver_id == VK_DRIVER_ID_MESA_TURNIP) {
driver_id == VK_DRIVER_ID_ARM_PROPRIETARY || driver_id == VK_DRIVER_ID_MESA_TURNIP) {
pending_sync.clear(); pending_sync.clear();
sync_values_stash.clear(); sync_values_stash.clear();
return; return;
@@ -666,13 +666,18 @@ public:
offsets.fill(0); offsets.fill(0);
last_queries.fill(0); last_queries.fill(0);
last_queries_stride.fill(1); last_queries_stride.fill(1);
stream_to_slot.fill(INVALID_SLOT);
VkBufferUsageFlags counter_buffer_usage =
VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
if (device.IsExtTransformFeedbackSupported()) {
counter_buffer_usage |= VK_BUFFER_USAGE_TRANSFORM_FEEDBACK_COUNTER_BUFFER_BIT_EXT;
}
const VkBufferCreateInfo buffer_ci = { const VkBufferCreateInfo buffer_ci = {
.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
.pNext = nullptr, .pNext = nullptr,
.flags = 0, .flags = 0,
.size = TFBQueryBank::QUERY_SIZE * NUM_STREAMS, .size = TFBQueryBank::QUERY_SIZE * NUM_STREAMS,
.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT | .usage = counter_buffer_usage,
VK_BUFFER_USAGE_TRANSFORM_FEEDBACK_COUNTER_BUFFER_BIT_EXT,
.sharingMode = VK_SHARING_MODE_EXCLUSIVE, .sharingMode = VK_SHARING_MODE_EXCLUSIVE,
.queueFamilyIndexCount = 0, .queueFamilyIndexCount = 0,
.pQueueFamilyIndices = nullptr, .pQueueFamilyIndices = nullptr,
@@ -692,6 +697,9 @@ public:
~TFBCounterStreamer() = default; ~TFBCounterStreamer() = default;
void StartCounter() override { void StartCounter() override {
if (!device.IsExtTransformFeedbackSupported()) {
return;
}
FlushBeginTFB(); FlushBeginTFB();
has_started = true; has_started = true;
} }
@@ -706,7 +714,9 @@ public:
void CloseCounter() override { void CloseCounter() override {
if (has_flushed_end_pending) { if (has_flushed_end_pending) {
FlushEndTFB(); if (scheduler.IsRenderPassActive()) {
FlushEndTFB();
}
} }
runtime.View3DRegs([this](Maxwell3D& maxwell3d) { runtime.View3DRegs([this](Maxwell3D& maxwell3d) {
if (maxwell3d.regs.transform_feedback_enabled == 0) { if (maxwell3d.regs.transform_feedback_enabled == 0) {
@@ -756,18 +766,33 @@ public:
if (has_timestamp) { if (has_timestamp) {
new_query->flags |= VideoCommon::QueryFlagBits::HasTimestamp; new_query->flags |= VideoCommon::QueryFlagBits::HasTimestamp;
} }
if (!device.IsExtTransformFeedbackSupported()) {
new_query->flags |= VideoCommon::QueryFlagBits::IsFinalValueSynced;
return index;
}
if (!subreport_) { if (!subreport_) {
new_query->flags |= VideoCommon::QueryFlagBits::IsFinalValueSynced; new_query->flags |= VideoCommon::QueryFlagBits::IsFinalValueSynced;
return index; return index;
} }
const size_t subreport = static_cast<size_t>(*subreport_); const size_t subreport = static_cast<size_t>(*subreport_);
if (subreport >= NUM_STREAMS) {
new_query->flags |= VideoCommon::QueryFlagBits::IsFinalValueSynced;
return index;
}
last_queries[subreport] = address; last_queries[subreport] = address;
if ((streams_mask & (1ULL << subreport)) == 0) { if ((streams_mask & (1ULL << subreport)) == 0) {
new_query->flags |= VideoCommon::QueryFlagBits::IsFinalValueSynced; new_query->flags |= VideoCommon::QueryFlagBits::IsFinalValueSynced;
return index; return index;
} }
const size_t slot = stream_to_slot[subreport];
if (slot >= NUM_STREAMS) {
new_query->flags |= VideoCommon::QueryFlagBits::IsFinalValueSynced;
return index;
}
scheduler.RequestOutsideRenderPassOperationContext();
CloseCounter(); CloseCounter();
auto [bank_slot, data_slot] = ProduceCounterBuffer(subreport); auto [bank_slot, data_slot] = ProduceCounterBuffer(slot);
new_query->start_bank_id = static_cast<u32>(bank_slot); new_query->start_bank_id = static_cast<u32>(bank_slot);
new_query->size_banks = 1; new_query->size_banks = 1;
new_query->start_slot = static_cast<u32>(data_slot); new_query->start_slot = static_cast<u32>(data_slot);
@@ -778,6 +803,9 @@ public:
} }
std::optional<std::pair<DAddr, size_t>> GetLastQueryStream(size_t stream) { std::optional<std::pair<DAddr, size_t>> GetLastQueryStream(size_t stream) {
if (stream >= NUM_STREAMS) {
return std::nullopt;
}
if (last_queries[stream] != 0) { if (last_queries[stream] != 0) {
std::pair<DAddr, size_t> result(last_queries[stream], last_queries_stride[stream]); std::pair<DAddr, size_t> result(last_queries[stream], last_queries_stride[stream]);
return result; return result;
@@ -789,6 +817,10 @@ public:
return out_topology; return out_topology;
} }
u32 GetPatchVertices() const {
return patch_vertices;
}
bool HasUnsyncedQueries() const override { bool HasUnsyncedQueries() const override {
return !pending_flush_queries.empty(); return !pending_flush_queries.empty();
} }
@@ -855,6 +887,9 @@ public:
private: private:
void FlushBeginTFB() { void FlushBeginTFB() {
if (!device.IsExtTransformFeedbackSupported()) [[unlikely]] {
return;
}
if (has_flushed_end_pending) [[unlikely]] { if (has_flushed_end_pending) [[unlikely]] {
return; return;
} }
@@ -868,12 +903,24 @@ private:
}); });
return; return;
} }
static constexpr VkMemoryBarrier COUNTER_RESUME_BARRIER{
.sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER,
.pNext = nullptr,
.srcAccessMask = VK_ACCESS_TRANSFORM_FEEDBACK_COUNTER_WRITE_BIT_EXT,
.dstAccessMask = VK_ACCESS_TRANSFORM_FEEDBACK_COUNTER_READ_BIT_EXT,
};
scheduler.Record([this, total = static_cast<u32>(buffers_count)](vk::CommandBuffer cmdbuf) { scheduler.Record([this, total = static_cast<u32>(buffers_count)](vk::CommandBuffer cmdbuf) {
cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFORM_FEEDBACK_BIT_EXT,
VK_PIPELINE_STAGE_TRANSFORM_FEEDBACK_BIT_EXT, 0,
COUNTER_RESUME_BARRIER);
cmdbuf.BeginTransformFeedbackEXT(0, total, counter_buffers.data(), offsets.data()); cmdbuf.BeginTransformFeedbackEXT(0, total, counter_buffers.data(), offsets.data());
}); });
} }
void FlushEndTFB() { void FlushEndTFB() {
if (!device.IsExtTransformFeedbackSupported()) [[unlikely]] {
return;
}
if (!has_flushed_end_pending) [[unlikely]] { if (!has_flushed_end_pending) [[unlikely]] {
UNREACHABLE(); UNREACHABLE();
return; return;
@@ -899,28 +946,48 @@ private:
void UpdateBuffers() { void UpdateBuffers() {
last_queries.fill(0); last_queries.fill(0);
last_queries_stride.fill(1); last_queries_stride.fill(1);
stream_to_slot.fill(INVALID_SLOT);
streams_mask = 0; // reset previously recorded streams streams_mask = 0; // reset previously recorded streams
runtime.View3DRegs([this](Maxwell3D& maxwell3d) { runtime.View3DRegs([this](Maxwell3D& maxwell3d) {
buffers_count = 0; buffers_count = 0;
out_topology = maxwell3d.draw_manager->GetDrawState().topology; out_topology = maxwell3d.draw_manager->GetDrawState().topology;
patch_vertices = std::max(maxwell3d.regs.patch_vertices, 1U);
if (out_topology == Maxwell3D::Regs::PrimitiveTopology::Patches) {
switch (maxwell3d.regs.tessellation.params.output_primitives.Value()) {
case Maxwell3D::Regs::Tessellation::OutputPrimitives::Points:
out_topology = Maxwell3D::Regs::PrimitiveTopology::Points;
break;
case Maxwell3D::Regs::Tessellation::OutputPrimitives::Lines:
out_topology = Maxwell3D::Regs::PrimitiveTopology::LineStrip;
break;
case Maxwell3D::Regs::Tessellation::OutputPrimitives::Triangles_CW:
case Maxwell3D::Regs::Tessellation::OutputPrimitives::Triangles_CCW:
out_topology = Maxwell3D::Regs::PrimitiveTopology::TriangleStrip;
break;
}
}
for (size_t i = 0; i < Maxwell3D::Regs::NumTransformFeedbackBuffers; i++) { for (size_t i = 0; i < Maxwell3D::Regs::NumTransformFeedbackBuffers; i++) {
const auto& tf = maxwell3d.regs.transform_feedback; const auto& tf = maxwell3d.regs.transform_feedback;
if (tf.buffers[i].enable == 0) { if (tf.buffers[i].enable == 0) {
continue; continue;
} }
buffers_count = std::max<size_t>(buffers_count, i + 1);
const size_t stream = tf.controls[i].stream; const size_t stream = tf.controls[i].stream;
if (stream >= last_queries_stride.size()) { if (stream >= last_queries_stride.size()) {
LOG_WARNING(Render_Vulkan, "TransformFeedback stream {} out of range", stream); LOG_WARNING(Render_Vulkan, "TransformFeedback stream {} out of range", stream);
continue; continue;
} }
if ((streams_mask & (1ULL << stream)) != 0) {
continue;
}
last_queries_stride[stream] = tf.controls[i].stride; last_queries_stride[stream] = tf.controls[i].stride;
stream_to_slot[stream] = i;
streams_mask |= 1ULL << stream; streams_mask |= 1ULL << stream;
buffers_count = std::max<size_t>(buffers_count, stream + 1);
} }
}); });
} }
std::pair<size_t, size_t> ProduceCounterBuffer(size_t stream) { std::pair<size_t, size_t> ProduceCounterBuffer(size_t slot_index) {
if (current_bank == nullptr || current_bank->IsClosed()) { if (current_bank == nullptr || current_bank->IsClosed()) {
current_bank_id = current_bank_id =
bank_pool.ReserveBank([this](std::deque<TFBQueryBank>& queue, size_t index) { bank_pool.ReserveBank([this](std::deque<TFBQueryBank>& queue, size_t index) {
@@ -946,7 +1013,8 @@ private:
}; };
scheduler.RequestOutsideRenderPassOperationContext(); scheduler.RequestOutsideRenderPassOperationContext();
scheduler.Record([dst_buffer = current_bank->GetBuffer(), scheduler.Record([dst_buffer = current_bank->GetBuffer(),
src_buffer = counter_buffers[stream], src_offset = offsets[stream], src_buffer = counter_buffers[slot_index],
src_offset = offsets[slot_index],
slot](vk::CommandBuffer cmdbuf) { slot](vk::CommandBuffer cmdbuf) {
cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFORM_FEEDBACK_BIT_EXT, cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFORM_FEEDBACK_BIT_EXT,
VK_PIPELINE_STAGE_TRANSFER_BIT, 0, READ_BARRIER); VK_PIPELINE_STAGE_TRANSFER_BIT, 0, READ_BARRIER);
@@ -965,6 +1033,7 @@ private:
friend class PrimitivesSucceededStreamer; friend class PrimitivesSucceededStreamer;
static constexpr size_t NUM_STREAMS = 4; static constexpr size_t NUM_STREAMS = 4;
static constexpr size_t INVALID_SLOT = NUM_STREAMS;
QueryCacheRuntime& runtime; QueryCacheRuntime& runtime;
const Device& device; const Device& device;
@@ -994,7 +1063,9 @@ private:
std::array<VkDeviceSize, NUM_STREAMS> offsets{}; std::array<VkDeviceSize, NUM_STREAMS> offsets{};
std::array<DAddr, NUM_STREAMS> last_queries; std::array<DAddr, NUM_STREAMS> last_queries;
std::array<size_t, NUM_STREAMS> last_queries_stride; std::array<size_t, NUM_STREAMS> last_queries_stride;
std::array<size_t, NUM_STREAMS> stream_to_slot;
Maxwell3D::Regs::PrimitiveTopology out_topology; Maxwell3D::Regs::PrimitiveTopology out_topology;
u32 patch_vertices{1};
u64 streams_mask; u64 streams_mask;
}; };
@@ -1015,6 +1086,7 @@ public:
u64 stride{}; u64 stride{};
DAddr dependant_address{}; DAddr dependant_address{};
Maxwell3D::Regs::PrimitiveTopology topology{Maxwell3D::Regs::PrimitiveTopology::Points}; Maxwell3D::Regs::PrimitiveTopology topology{Maxwell3D::Regs::PrimitiveTopology::Points};
u32 patch_vertices{1};
size_t dependant_index{}; size_t dependant_index{};
bool dependant_manage{}; bool dependant_manage{};
}; };
@@ -1031,6 +1103,10 @@ public:
~PrimitivesSucceededStreamer() = default; ~PrimitivesSucceededStreamer() = default;
void ResetCounter() override {
tfb_streamer.ResetCounter();
}
size_t WriteCounter(DAddr address, bool has_timestamp, u32 value, size_t WriteCounter(DAddr address, bool has_timestamp, u32 value,
std::optional<u32> subreport_) override { std::optional<u32> subreport_) override {
auto index = BuildQuery(); auto index = BuildQuery();
@@ -1048,6 +1124,7 @@ public:
auto dependant_address_opt = tfb_streamer.GetLastQueryStream(subreport); auto dependant_address_opt = tfb_streamer.GetLastQueryStream(subreport);
bool must_manage_dependance = false; bool must_manage_dependance = false;
new_query->topology = tfb_streamer.GetOutputTopology(); new_query->topology = tfb_streamer.GetOutputTopology();
new_query->patch_vertices = tfb_streamer.GetPatchVertices();
if (dependant_address_opt) { if (dependant_address_opt) {
auto [dep_address, stride] = *dependant_address_opt; auto [dep_address, stride] = *dependant_address_opt;
new_query->dependant_address = dep_address; new_query->dependant_address = dep_address;
@@ -1068,6 +1145,7 @@ public:
} }
new_query->stride = 1; new_query->stride = 1;
runtime.View3DRegs([new_query, subreport](Maxwell3D& maxwell3d) { runtime.View3DRegs([new_query, subreport](Maxwell3D& maxwell3d) {
new_query->patch_vertices = std::max(maxwell3d.regs.patch_vertices, 1U);
for (size_t i = 0; i < Maxwell3D::Regs::NumTransformFeedbackBuffers; i++) { for (size_t i = 0; i < Maxwell3D::Regs::NumTransformFeedbackBuffers; i++) {
const auto& tf = maxwell3d.regs.transform_feedback; const auto& tf = maxwell3d.regs.transform_feedback;
if (tf.buffers[i].enable == 0) { if (tf.buffers[i].enable == 0) {
@@ -1131,27 +1209,39 @@ public:
} }
} }
query->value = [&]() -> u64 { query->value = [&]() -> u64 {
const auto saturating_subtract = [](u64 value, u64 amount) {
return value > amount ? value - amount : 0;
};
switch (query->topology) { switch (query->topology) {
case Maxwell3D::Regs::PrimitiveTopology::Points: case Maxwell3D::Regs::PrimitiveTopology::Points:
return num_vertices; return num_vertices;
case Maxwell3D::Regs::PrimitiveTopology::Lines: case Maxwell3D::Regs::PrimitiveTopology::Lines:
return num_vertices / 2; return num_vertices / 2;
case Maxwell3D::Regs::PrimitiveTopology::LineLoop: case Maxwell3D::Regs::PrimitiveTopology::LineLoop:
return (num_vertices / 2) + 1; return num_vertices > 1 ? num_vertices : 0;
case Maxwell3D::Regs::PrimitiveTopology::LineStrip: case Maxwell3D::Regs::PrimitiveTopology::LineStrip:
return num_vertices - 1; return saturating_subtract(num_vertices, 1);
case Maxwell3D::Regs::PrimitiveTopology::Patches: case Maxwell3D::Regs::PrimitiveTopology::LinesAdjacency:
return num_vertices / 4;
case Maxwell3D::Regs::PrimitiveTopology::LineStripAdjacency:
return saturating_subtract(num_vertices, 3);
case Maxwell3D::Regs::PrimitiveTopology::Triangles: case Maxwell3D::Regs::PrimitiveTopology::Triangles:
case Maxwell3D::Regs::PrimitiveTopology::TrianglesAdjacency:
return num_vertices / 3; return num_vertices / 3;
case Maxwell3D::Regs::PrimitiveTopology::TrianglesAdjacency:
return num_vertices / 6;
case Maxwell3D::Regs::PrimitiveTopology::TriangleFan: case Maxwell3D::Regs::PrimitiveTopology::TriangleFan:
case Maxwell3D::Regs::PrimitiveTopology::TriangleStrip: case Maxwell3D::Regs::PrimitiveTopology::TriangleStrip:
return saturating_subtract(num_vertices, 2);
case Maxwell3D::Regs::PrimitiveTopology::TriangleStripAdjacency: case Maxwell3D::Regs::PrimitiveTopology::TriangleStripAdjacency:
return num_vertices - 2; return num_vertices > 4 ? (num_vertices - 4) / 2 : 0;
case Maxwell3D::Regs::PrimitiveTopology::Quads: case Maxwell3D::Regs::PrimitiveTopology::Quads:
return num_vertices / 4; return num_vertices / 4;
case Maxwell3D::Regs::PrimitiveTopology::QuadStrip:
return num_vertices > 2 ? (num_vertices - 2) / 2 : 0;
case Maxwell3D::Regs::PrimitiveTopology::Polygon: case Maxwell3D::Regs::PrimitiveTopology::Polygon:
return 1U; return num_vertices >= 3 ? 1U : 0U;
case Maxwell3D::Regs::PrimitiveTopology::Patches:
return num_vertices / std::max<u64>(query->patch_vertices, 1U);
default: default:
return num_vertices; return num_vertices;
} }
@@ -1202,16 +1292,24 @@ struct QueryCacheRuntimeImpl {
hcr_setup.pNext = nullptr; hcr_setup.pNext = nullptr;
hcr_setup.flags = 0; hcr_setup.flags = 0;
conditional_resolve_pass = std::make_unique<ConditionalRenderingResolvePass>( const bool has_conditional_rendering = device.IsExtConditionalRendering();
device, scheduler, descriptor_pool, compute_pass_descriptor_queue); if (has_conditional_rendering) {
conditional_resolve_pass = std::make_unique<ConditionalRenderingResolvePass>(
device, scheduler, descriptor_pool, compute_pass_descriptor_queue);
}
VkBufferUsageFlags hcr_buffer_usage =
VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT;
if (has_conditional_rendering) {
hcr_buffer_usage |= VK_BUFFER_USAGE_CONDITIONAL_RENDERING_BIT_EXT;
}
const VkBufferCreateInfo buffer_ci = { const VkBufferCreateInfo buffer_ci = {
.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
.pNext = nullptr, .pNext = nullptr,
.flags = 0, .flags = 0,
.size = sizeof(u32), .size = sizeof(u32),
.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | .usage = hcr_buffer_usage,
VK_BUFFER_USAGE_CONDITIONAL_RENDERING_BIT_EXT,
.sharingMode = VK_SHARING_MODE_EXCLUSIVE, .sharingMode = VK_SHARING_MODE_EXCLUSIVE,
.queueFamilyIndexCount = 0, .queueFamilyIndexCount = 0,
.pQueueFamilyIndices = nullptr, .pQueueFamilyIndices = nullptr,
@@ -1338,15 +1436,17 @@ void QueryCacheRuntime::HostConditionalRenderingCompareValueImpl(VideoCommon::Lo
} }
} }
void QueryCacheRuntime::HostConditionalRenderingCompareBCImpl(DAddr address, bool is_equal) { void QueryCacheRuntime::HostConditionalRenderingCompareBCImpl(DAddr address, bool is_equal,
bool compare_to_zero) {
VkBuffer to_resolve; VkBuffer to_resolve;
u32 to_resolve_offset; u32 to_resolve_offset;
const u32 resolve_size = compare_to_zero ? 8 : 24;
{ {
std::scoped_lock lk(impl->buffer_cache.mutex); std::scoped_lock lk(impl->buffer_cache.mutex);
static constexpr auto sync_info = VideoCommon::ObtainBufferSynchronize::NoSynchronize; const auto sync_info = VideoCommon::ObtainBufferSynchronize::FullSynchronize;
const auto post_op = VideoCommon::ObtainBufferOperation::DoNothing; const auto post_op = VideoCommon::ObtainBufferOperation::DoNothing;
const auto [buffer, offset] = const auto [buffer, offset] =
impl->buffer_cache.ObtainCPUBuffer(address, 24, sync_info, post_op); impl->buffer_cache.ObtainCPUBuffer(address, resolve_size, sync_info, post_op);
to_resolve = buffer->Handle(); to_resolve = buffer->Handle();
to_resolve_offset = static_cast<u32>(offset); to_resolve_offset = static_cast<u32>(offset);
} }
@@ -1355,7 +1455,7 @@ void QueryCacheRuntime::HostConditionalRenderingCompareBCImpl(DAddr address, boo
PauseHostConditionalRendering(); PauseHostConditionalRendering();
} }
impl->conditional_resolve_pass->Resolve(*impl->hcr_resolve_buffer, to_resolve, impl->conditional_resolve_pass->Resolve(*impl->hcr_resolve_buffer, to_resolve,
to_resolve_offset, false); to_resolve_offset, compare_to_zero);
impl->hcr_setup.buffer = *impl->hcr_resolve_buffer; impl->hcr_setup.buffer = *impl->hcr_resolve_buffer;
impl->hcr_setup.offset = 0; impl->hcr_setup.offset = 0;
impl->hcr_setup.flags = is_equal ? 0 : VK_CONDITIONAL_RENDERING_INVERTED_BIT_EXT; impl->hcr_setup.flags = is_equal ? 0 : VK_CONDITIONAL_RENDERING_INVERTED_BIT_EXT;
@@ -1371,7 +1471,7 @@ bool QueryCacheRuntime::HostConditionalRenderingCompareValue(VideoCommon::Lookup
if (!impl->device.IsExtConditionalRendering()) { if (!impl->device.IsExtConditionalRendering()) {
return false; return false;
} }
HostConditionalRenderingCompareValueImpl(object_1, false); HostConditionalRenderingCompareBCImpl(object_1.address, true, true);
return true; return true;
} }
@@ -1420,7 +1520,8 @@ bool QueryCacheRuntime::HostConditionalRenderingCompareValues(VideoCommon::Looku
auto driver_id = impl->device.GetDriverID(); auto driver_id = impl->device.GetDriverID();
const bool is_gpu_high = Settings::IsGPULevelHigh(); const bool is_gpu_high = Settings::IsGPULevelHigh();
if ((!is_gpu_high && driver_id == VK_DRIVER_ID_INTEL_PROPRIETARY_WINDOWS) || driver_id == VK_DRIVER_ID_QUALCOMM_PROPRIETARY || driver_id == VK_DRIVER_ID_ARM_PROPRIETARY || driver_id == VK_DRIVER_ID_MESA_TURNIP) { if ((!is_gpu_high && driver_id == VK_DRIVER_ID_INTEL_PROPRIETARY_WINDOWS) || driver_id == VK_DRIVER_ID_ARM_PROPRIETARY || driver_id == VK_DRIVER_ID_MESA_TURNIP) {
EndHostConditionalRendering();
return true; return true;
} }
@@ -1437,10 +1538,12 @@ bool QueryCacheRuntime::HostConditionalRenderingCompareValues(VideoCommon::Looku
} }
if (!is_gpu_high) { if (!is_gpu_high) {
EndHostConditionalRendering();
return true; return true;
} }
if (!is_in_bc[0] && !is_in_bc[1]) { if (!is_in_bc[0] && !is_in_bc[1]) {
EndHostConditionalRendering();
return true; return true;
} }
HostConditionalRenderingCompareBCImpl(object_1.address, equal_check); HostConditionalRenderingCompareBCImpl(object_1.address, equal_check);
@@ -63,7 +63,8 @@ public:
private: private:
void HostConditionalRenderingCompareValueImpl(VideoCommon::LookupData object, bool is_equal); void HostConditionalRenderingCompareValueImpl(VideoCommon::LookupData object, bool is_equal);
void HostConditionalRenderingCompareBCImpl(DAddr address, bool is_equal); void HostConditionalRenderingCompareBCImpl(DAddr address, bool is_equal,
bool compare_to_zero = false);
friend struct QueryCacheRuntimeImpl; friend struct QueryCacheRuntimeImpl;
std::unique_ptr<QueryCacheRuntimeImpl> impl; std::unique_ptr<QueryCacheRuntimeImpl> impl;
}; };
+102 -35
View File
@@ -173,6 +173,28 @@ DrawParams MakeDrawParams(const MaxwellDrawState& draw_state, u32 num_instances,
} }
return params; return params;
} }
bool SupportsPrimitiveRestart(VkPrimitiveTopology topology) {
switch (topology) {
case VK_PRIMITIVE_TOPOLOGY_POINT_LIST:
case VK_PRIMITIVE_TOPOLOGY_LINE_LIST:
case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST:
case VK_PRIMITIVE_TOPOLOGY_LINE_LIST_WITH_ADJACENCY:
case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST_WITH_ADJACENCY:
case VK_PRIMITIVE_TOPOLOGY_PATCH_LIST:
return false;
default:
return true;
}
}
bool IsPrimitiveRestartSupported(const Device& device, VkPrimitiveTopology topology) {
return ((topology != VK_PRIMITIVE_TOPOLOGY_PATCH_LIST &&
device.IsTopologyListPrimitiveRestartSupported()) ||
SupportsPrimitiveRestart(topology) ||
(topology == VK_PRIMITIVE_TOPOLOGY_PATCH_LIST &&
device.IsPatchListPrimitiveRestartSupported()));
}
} // Anonymous namespace } // Anonymous namespace
RasterizerVulkan::RasterizerVulkan(Core::Frontend::EmuWindow& emu_window_, Tegra::GPU& gpu_, RasterizerVulkan::RasterizerVulkan(Core::Frontend::EmuWindow& emu_window_, Tegra::GPU& gpu_,
@@ -225,6 +247,7 @@ void RasterizerVulkan::PrepareDraw(bool is_indexed, Func&& draw_func) {
UpdateDynamicStates(); UpdateDynamicStates();
query_cache.NotifySegment(true);
HandleTransformFeedback(); HandleTransformFeedback();
query_cache.CounterEnable(VideoCommon::QueryType::ZPassPixelCount64, query_cache.CounterEnable(VideoCommon::QueryType::ZPassPixelCount64,
maxwell3d->regs.zpass_pixel_count_enable); maxwell3d->regs.zpass_pixel_count_enable);
@@ -336,6 +359,7 @@ void RasterizerVulkan::DrawTexture() {
UpdateDynamicStates(); UpdateDynamicStates();
query_cache.NotifySegment(true);
query_cache.CounterEnable(VideoCommon::QueryType::ZPassPixelCount64, query_cache.CounterEnable(VideoCommon::QueryType::ZPassPixelCount64,
maxwell3d->regs.zpass_pixel_count_enable); maxwell3d->regs.zpass_pixel_count_enable);
const auto& draw_texture_state = maxwell3d->draw_manager->GetDrawTextureState(); const auto& draw_texture_state = maxwell3d->draw_manager->GetDrawTextureState();
@@ -575,11 +599,17 @@ void RasterizerVulkan::DispatchCompute() {
} }
void RasterizerVulkan::ResetCounter(VideoCommon::QueryType type) { void RasterizerVulkan::ResetCounter(VideoCommon::QueryType type) {
if (type != VideoCommon::QueryType::ZPassPixelCount64) { switch (type) {
case VideoCommon::QueryType::ZPassPixelCount64:
case VideoCommon::QueryType::StreamingByteCount:
case VideoCommon::QueryType::StreamingPrimitivesSucceeded:
case VideoCommon::QueryType::VtgPrimitivesOut:
query_cache.CounterReset(type);
return;
default:
LOG_DEBUG(Render_Vulkan, "Unimplemented counter reset={}", type); LOG_DEBUG(Render_Vulkan, "Unimplemented counter reset={}", type);
return; return;
} }
query_cache.CounterReset(type);
} }
void RasterizerVulkan::Query(GPUVAddr gpu_addr, VideoCommon::QueryType type, void RasterizerVulkan::Query(GPUVAddr gpu_addr, VideoCommon::QueryType type,
@@ -766,6 +796,9 @@ void RasterizerVulkan::ReleaseFences(bool force) {
void RasterizerVulkan::FlushAndInvalidateRegion(DAddr addr, u64 size, void RasterizerVulkan::FlushAndInvalidateRegion(DAddr addr, u64 size,
VideoCommon::CacheType which) { VideoCommon::CacheType which) {
if (Settings::IsGPULevelHigh()) {
FlushRegion(addr, size, which);
}
InvalidateRegion(addr, size, which); InvalidateRegion(addr, size, which);
} }
@@ -830,6 +863,10 @@ bool RasterizerVulkan::AccelerateConditionalRendering() {
return query_cache.AccelerateHostConditionalRendering(); return query_cache.AccelerateHostConditionalRendering();
} }
bool RasterizerVulkan::HasDrawTransformFeedback() {
return device.IsTransformFeedbackDrawSupported();
}
bool RasterizerVulkan::AccelerateSurfaceCopy(const Tegra::Engines::Fermi2D::Surface& src, bool RasterizerVulkan::AccelerateSurfaceCopy(const Tegra::Engines::Fermi2D::Surface& src,
const Tegra::Engines::Fermi2D::Surface& dst, const Tegra::Engines::Fermi2D::Surface& dst,
const Tegra::Engines::Fermi2D::Config& copy_config) { const Tegra::Engines::Fermi2D::Config& copy_config) {
@@ -976,6 +1013,12 @@ bool AccelerateDMA::BufferToImage(const Tegra::DMA::ImageCopy& copy_info,
void RasterizerVulkan::UpdateDynamicStates() { void RasterizerVulkan::UpdateDynamicStates() {
auto& regs = maxwell3d->regs; auto& regs = maxwell3d->regs;
auto& flags = maxwell3d->dirty.flags;
const auto topology = maxwell3d->draw_manager->GetDrawState().topology;
if (state_tracker.ChangePrimitiveTopology(topology)) {
flags[Dirty::DepthBiasEnable] = true;
flags[Dirty::PrimitiveRestartEnable] = true;
}
// Core Dynamic States (Vulkan 1.0) - Always active regardless of dyna_state setting // Core Dynamic States (Vulkan 1.0) - Always active regardless of dyna_state setting
UpdateViewportsState(regs); UpdateViewportsState(regs);
@@ -1084,6 +1127,9 @@ void RasterizerVulkan::UpdateViewportsState(Tegra::Engines::Maxwell3D::Regs& reg
if (!state_tracker.TouchViewports()) { if (!state_tracker.TouchViewports()) {
return; return;
} }
maxwell3d->dirty.flags[Dirty::Scissors] = true;
if (!regs.viewport_scale_offset_enabled) { if (!regs.viewport_scale_offset_enabled) {
float x = static_cast<float>(regs.surface_clip.x); float x = static_cast<float>(regs.surface_clip.x);
float y = static_cast<float>(regs.surface_clip.y); float y = static_cast<float>(regs.surface_clip.y);
@@ -1101,8 +1147,12 @@ void RasterizerVulkan::UpdateViewportsState(Tegra::Engines::Maxwell3D::Regs& reg
.minDepth = 0.0f, .minDepth = 0.0f,
.maxDepth = 1.0f, .maxDepth = 1.0f,
}; };
scheduler.Record([viewport](vk::CommandBuffer cmdbuf) { scheduler.Record([this, viewport](vk::CommandBuffer cmdbuf) {
cmdbuf.SetViewport(0, viewport); const u32 num_viewports = std::min<u32>(device.GetMaxViewports(), Maxwell::NumViewports);
std::array<VkViewport, Maxwell::NumViewports> viewport_list{};
viewport_list.fill(viewport);
const vk::Span<VkViewport> viewports(viewport_list.data(), num_viewports);
cmdbuf.SetViewport(0, viewports);
}); });
return; return;
} }
@@ -1142,8 +1192,12 @@ void RasterizerVulkan::UpdateScissorsState(Tegra::Engines::Maxwell3D::Regs& regs
scissor.offset.y = static_cast<int32_t>(y); scissor.offset.y = static_cast<int32_t>(y);
scissor.extent.width = width; scissor.extent.width = width;
scissor.extent.height = height; scissor.extent.height = height;
scheduler.Record([scissor](vk::CommandBuffer cmdbuf) { scheduler.Record([this, scissor](vk::CommandBuffer cmdbuf) {
cmdbuf.SetScissor(0, scissor); const u32 num_scissors = std::min<u32>(device.GetMaxViewports(), Maxwell::NumViewports);
std::array<VkRect2D, Maxwell::NumViewports> scissor_list{};
scissor_list.fill(scissor);
const vk::Span<VkRect2D> scissors(scissor_list.data(), num_scissors);
cmdbuf.SetScissor(0, scissors);
}); });
return; return;
} }
@@ -1388,7 +1442,17 @@ void RasterizerVulkan::UpdatePrimitiveRestartEnable(Tegra::Engines::Maxwell3D::R
if (!state_tracker.TouchPrimitiveRestartEnable()) { if (!state_tracker.TouchPrimitiveRestartEnable()) {
return; return;
} }
scheduler.Record([enable = regs.primitive_restart.enabled](vk::CommandBuffer cmdbuf) {
bool enable = regs.primitive_restart.enabled != 0;
if (device.IsMoltenVK()) {
enable = true;
} else if (enable) {
const auto topology =
MaxwellToVK::PrimitiveTopology(device, maxwell3d->draw_manager->GetDrawState().topology);
enable = IsPrimitiveRestartSupported(device, topology);
}
scheduler.Record([enable](vk::CommandBuffer cmdbuf) {
cmdbuf.SetPrimitiveRestartEnableEXT(enable); cmdbuf.SetPrimitiveRestartEnableEXT(enable);
}); });
} }
@@ -1727,7 +1791,9 @@ void RasterizerVulkan::UpdateStencilTestEnable(Tegra::Engines::Maxwell3D::Regs&
void RasterizerVulkan::UpdateVertexInput(Tegra::Engines::Maxwell3D::Regs& regs) { void RasterizerVulkan::UpdateVertexInput(Tegra::Engines::Maxwell3D::Regs& regs) {
auto& dirty{maxwell3d->dirty.flags}; auto& dirty{maxwell3d->dirty.flags};
if (!dirty[Dirty::VertexInput]) { const bool vertex_input_dirty = dirty[Dirty::VertexInput];
const bool vertex_buffers_dirty = dirty[VideoCommon::Dirty::VertexBuffers];
if (!vertex_input_dirty && !vertex_buffers_dirty) {
return; return;
} }
dirty[Dirty::VertexInput] = false; dirty[Dirty::VertexInput] = false;
@@ -1735,38 +1801,31 @@ void RasterizerVulkan::UpdateVertexInput(Tegra::Engines::Maxwell3D::Regs& regs)
boost::container::static_vector<VkVertexInputBindingDescription2EXT, 32> bindings; boost::container::static_vector<VkVertexInputBindingDescription2EXT, 32> bindings;
boost::container::static_vector<VkVertexInputAttributeDescription2EXT, 32> attributes; boost::container::static_vector<VkVertexInputAttributeDescription2EXT, 32> attributes;
// There seems to be a bug on Nvidia's driver where updating only higher attributes ends up const u32 max_attributes =
// generating dirty state. Track the highest dirty attribute and update all attributes until static_cast<u32>(std::min<size_t>(Maxwell::NumVertexAttributes,
// that one. device.GetMaxVertexInputAttributes()));
size_t highest_dirty_attr{}; const u32 max_bindings =
for (size_t index = 0; index < Maxwell::NumVertexAttributes; ++index) { static_cast<u32>(std::min<size_t>(Maxwell::NumVertexArrays,
if (dirty[Dirty::VertexAttribute0 + index]) { device.GetMaxVertexInputBindings()));
highest_dirty_attr = index;
}
} for (u32 index = 0; index < max_attributes; ++index) {
for (size_t index = 0; index < highest_dirty_attr; ++index) {
const Maxwell::VertexAttribute attribute{regs.vertex_attrib_format[index]}; const Maxwell::VertexAttribute attribute{regs.vertex_attrib_format[index]};
const u32 binding{attribute.buffer}; const u32 binding{attribute.buffer};
dirty[Dirty::VertexAttribute0 + index] = false; if (attribute.constant || binding >= max_bindings) {
dirty[Dirty::VertexBinding0 + static_cast<size_t>(binding)] = true;
if (!attribute.constant) {
attributes.push_back({
.sType = VK_STRUCTURE_TYPE_VERTEX_INPUT_ATTRIBUTE_DESCRIPTION_2_EXT,
.pNext = nullptr,
.location = static_cast<u32>(index),
.binding = binding,
.format = MaxwellToVK::VertexFormat(device, attribute.type, attribute.size),
.offset = attribute.offset,
});
}
}
for (size_t index = 0; index < Maxwell::NumVertexAttributes; ++index) {
if (!dirty[Dirty::VertexBinding0 + index]) {
continue; continue;
} }
dirty[Dirty::VertexBinding0 + index] = false; attributes.push_back({
.sType = VK_STRUCTURE_TYPE_VERTEX_INPUT_ATTRIBUTE_DESCRIPTION_2_EXT,
.pNext = nullptr,
.location = index,
.binding = binding,
.format = MaxwellToVK::VertexFormat(device, attribute.type, attribute.size),
.offset = attribute.offset,
});
}
const u32 binding{static_cast<u32>(index)}; for (u32 binding = 0; binding < max_bindings; ++binding) {
const auto& input_binding{regs.vertex_streams[binding]}; const auto& input_binding{regs.vertex_streams[binding]};
const bool is_instanced{regs.vertex_stream_instances.IsInstancingEnabled(binding)}; const bool is_instanced{regs.vertex_stream_instances.IsInstancingEnabled(binding)};
bindings.push_back({ bindings.push_back({
@@ -1778,6 +1837,14 @@ void RasterizerVulkan::UpdateVertexInput(Tegra::Engines::Maxwell3D::Regs& regs)
.divisor = is_instanced ? input_binding.frequency : 1, .divisor = is_instanced ? input_binding.frequency : 1,
}); });
} }
for (size_t index = 0; index < Maxwell::NumVertexAttributes; ++index) {
dirty[Dirty::VertexAttribute0 + index] = false;
}
for (size_t index = 0; index < Maxwell::NumVertexArrays; ++index) {
dirty[Dirty::VertexBinding0 + index] = false;
}
scheduler.Record([bindings, attributes](vk::CommandBuffer cmdbuf) { scheduler.Record([bindings, attributes](vk::CommandBuffer cmdbuf) {
cmdbuf.SetVertexInputEXT(bindings, attributes); cmdbuf.SetVertexInputEXT(bindings, attributes);
}); });
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project // SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
@@ -122,6 +122,7 @@ public:
void FlushCommands() override; void FlushCommands() override;
void TickFrame() override; void TickFrame() override;
bool AccelerateConditionalRendering() override; bool AccelerateConditionalRendering() override;
bool HasDrawTransformFeedback() override;
bool AccelerateSurfaceCopy(const Tegra::Engines::Fermi2D::Surface& src, bool AccelerateSurfaceCopy(const Tegra::Engines::Fermi2D::Surface& src,
const Tegra::Engines::Fermi2D::Surface& dst, const Tegra::Engines::Fermi2D::Surface& dst,
const Tegra::Engines::Fermi2D::Config& copy_config) override; const Tegra::Engines::Fermi2D::Config& copy_config) override;
@@ -324,6 +324,8 @@ void Scheduler::EndRenderPass()
return; return;
} }
query_cache->CounterClose(VideoCommon::QueryType::StreamingByteCount);
// Log render pass end // Log render pass end
if (Settings::values.gpu_logging_enabled.GetValue() && if (Settings::values.gpu_logging_enabled.GetValue() &&
Settings::values.gpu_log_vulkan_calls.GetValue()) { Settings::values.gpu_log_vulkan_calls.GetValue()) {
@@ -63,6 +63,11 @@ public:
/// of a renderpass. /// of a renderpass.
void RequestOutsideRenderPassOperationContext(); void RequestOutsideRenderPassOperationContext();
/// Returns true when a render pass is currently active in the scheduler state.
bool IsRenderPassActive() const {
return state.renderpass != VK_NULL_HANDLE;
}
/// Update the pipeline to the current execution context. /// Update the pipeline to the current execution context.
bool UpdateGraphicsPipeline(GraphicsPipeline* pipeline); bool UpdateGraphicsPipeline(GraphicsPipeline* pipeline);
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project // SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
@@ -87,6 +87,7 @@ Flags MakeInvalidationFlags() {
void SetupDirtyViewports(Tables& tables) { void SetupDirtyViewports(Tables& tables) {
FillBlock(tables[0], OFF(viewport_transform), NUM(viewport_transform), Viewports); FillBlock(tables[0], OFF(viewport_transform), NUM(viewport_transform), Viewports);
FillBlock(tables[0], OFF(viewports), NUM(viewports), Viewports); FillBlock(tables[0], OFF(viewports), NUM(viewports), Viewports);
FillBlock(tables[1], OFF(surface_clip), NUM(surface_clip), Viewports);
tables[0][OFF(viewport_scale_offset_enabled)] = Viewports; tables[0][OFF(viewport_scale_offset_enabled)] = Viewports;
tables[1][OFF(window_origin)] = Viewports; tables[1][OFF(window_origin)] = Viewports;
} }
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project // SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
@@ -65,8 +65,10 @@ static VkPresentModeKHR ChooseSwapPresentMode(bool has_imm, bool has_mailbox,
return mode; return mode;
} }
}(); }();
if (setting == Settings::VSyncMode::Immediate && !has_imm) {
setting = Settings::VSyncMode::Mailbox;
}
if ((setting == Settings::VSyncMode::Mailbox && !has_mailbox) || if ((setting == Settings::VSyncMode::Mailbox && !has_mailbox) ||
(setting == Settings::VSyncMode::Immediate && !has_imm) ||
(setting == Settings::VSyncMode::FifoRelaxed && !has_fifo_relaxed)) { (setting == Settings::VSyncMode::FifoRelaxed && !has_fifo_relaxed)) {
setting = Settings::VSyncMode::Fifo; setting = Settings::VSyncMode::Fifo;
} }
@@ -176,7 +176,18 @@ constexpr VkBorderColor ConvertBorderColor(const std::array<float, 4>& color) {
.pViewFormats = view_formats.data(), .pViewFormats = view_formats.data(),
}; };
if (view_formats.size() > 1) { if (view_formats.size() > 1) {
image_ci.flags |= VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT; image_ci.flags |=
VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT | VK_IMAGE_CREATE_EXTENDED_USAGE_BIT;
const bool has_storage_compatible_view =
std::any_of(view_formats.begin(), view_formats.end(), [&device](VkFormat view_format) {
return device.IsFormatSupported(view_format, VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT,
FormatType::Optimal);
});
if (has_storage_compatible_view) {
image_ci.usage |= VK_IMAGE_USAGE_STORAGE_BIT;
}
if (device.IsKhrImageFormatListSupported()) { if (device.IsKhrImageFormatListSupported()) {
image_ci.pNext = &image_format_list; image_ci.pNext = &image_format_list;
} }
@@ -668,11 +679,16 @@ void CopyBufferToImage(vk::CommandBuffer cmdbuf, VkBuffer src_buffer, VkImage im
} }
void TryTransformSwizzleIfNeeded(PixelFormat format, std::array<SwizzleSource, 4>& swizzle, void TryTransformSwizzleIfNeeded(PixelFormat format, std::array<SwizzleSource, 4>& swizzle,
bool emulate_a4b4g4r4) { bool emulate_bgr565, bool emulate_a4b4g4r4) {
switch (format) { switch (format) {
case PixelFormat::A1B5G5R5_UNORM: case PixelFormat::A1B5G5R5_UNORM:
std::ranges::transform(swizzle, swizzle.begin(), SwapBlueRed); std::ranges::transform(swizzle, swizzle.begin(), SwapBlueRed);
break; break;
case PixelFormat::B5G6R5_UNORM:
if (emulate_bgr565) {
std::ranges::transform(swizzle, swizzle.begin(), SwapBlueRed);
}
break;
case PixelFormat::A5B5G5R1_UNORM: case PixelFormat::A5B5G5R1_UNORM:
std::ranges::transform(swizzle, swizzle.begin(), SwapSpecial); std::ranges::transform(swizzle, swizzle.begin(), SwapSpecial);
break; break;
@@ -2119,22 +2135,21 @@ ImageView::ImageView(TextureCacheRuntime& runtime, const VideoCommon::ImageViewI
if (!info.IsRenderTarget()) { if (!info.IsRenderTarget()) {
swizzle = info.Swizzle(); swizzle = info.Swizzle();
TryTransformSwizzleIfNeeded(format, swizzle, TryTransformSwizzleIfNeeded(format, swizzle,
!device->IsExt4444FormatsSupported()); device->MustEmulateBGR565(),
!device->IsExt4444FormatsSupported());
if ((aspect_mask & (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT)) != 0) { if ((aspect_mask & (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT)) != 0) {
std::ranges::transform(swizzle, swizzle.begin(), ConvertGreenRed); std::ranges::transform(swizzle, swizzle.begin(), ConvertGreenRed);
SanitizeDepthStencilSwizzle(swizzle, device->SupportsDepthStencilSwizzleOne()); SanitizeDepthStencilSwizzle(swizzle, device->SupportsDepthStencilSwizzleOne());
} }
} }
const auto format_info = MaxwellToVK::SurfaceFormat(*device, FormatType::Optimal, true, format); const auto format_info = MaxwellToVK::SurfaceFormat(*device, FormatType::Optimal, true, format);
if (ImageUsageFlags(format_info, format) != image.UsageFlags()) { const VkImageUsageFlags requested_view_usage = ImageUsageFlags(format_info, format);
LOG_WARNING(Render_Vulkan, const VkImageUsageFlags image_usage = image.UsageFlags();
"Image view format {} has different usage flags than image format {}", format, const VkImageUsageFlags clamped_view_usage = requested_view_usage & image_usage;
image.info.format);
}
const VkImageViewUsageCreateInfo image_view_usage{ const VkImageViewUsageCreateInfo image_view_usage{
.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_USAGE_CREATE_INFO, .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_USAGE_CREATE_INFO,
.pNext = nullptr, .pNext = nullptr,
.usage = ImageUsageFlags(format_info, format), .usage = clamped_view_usage,
}; };
const VkImageViewCreateInfo create_info{ const VkImageViewCreateInfo create_info{
.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
@@ -2300,23 +2315,18 @@ vk::ImageView ImageView::MakeView(VkFormat vk_format, VkImageAspectFlags aspect_
Sampler::Sampler(TextureCacheRuntime& runtime, const Tegra::Texture::TSCEntry& tsc) { Sampler::Sampler(TextureCacheRuntime& runtime, const Tegra::Texture::TSCEntry& tsc) {
const auto& device = runtime.device; const auto& device = runtime.device;
// Check if custom border colors are supported const bool has_custom_border_extension = runtime.device.IsExtCustomBorderColorSupported();
const bool has_custom_border_colors = runtime.device.IsCustomBorderColorsSupported(); const bool has_format_undefined =
const bool has_format_undefined = runtime.device.IsCustomBorderColorWithoutFormatSupported(); has_custom_border_extension && runtime.device.IsCustomBorderColorWithoutFormatSupported();
const bool has_custom_border_colors =
has_format_undefined && runtime.device.IsCustomBorderColorsSupported();
const auto color = tsc.BorderColor(); const auto color = tsc.BorderColor();
// Determine border format based on available features:
// - If customBorderColorWithoutFormat is available: use VK_FORMAT_UNDEFINED (most flexible)
// - If only customBorderColors is available: use concrete format (R8G8B8A8_UNORM)
// - If neither is available: use standard border colors (handled by ConvertBorderColor)
const VkFormat border_format = has_format_undefined ? VK_FORMAT_UNDEFINED
: VK_FORMAT_R8G8B8A8_UNORM;
const VkSamplerCustomBorderColorCreateInfoEXT border_ci{ const VkSamplerCustomBorderColorCreateInfoEXT border_ci{
.sType = VK_STRUCTURE_TYPE_SAMPLER_CUSTOM_BORDER_COLOR_CREATE_INFO_EXT, .sType = VK_STRUCTURE_TYPE_SAMPLER_CUSTOM_BORDER_COLOR_CREATE_INFO_EXT,
.pNext = nullptr, .pNext = nullptr,
.customBorderColor = std::bit_cast<VkClearColorValue>(color), .customBorderColor = std::bit_cast<VkClearColorValue>(color),
.format = border_format, .format = VK_FORMAT_UNDEFINED,
}; };
const void* pnext = nullptr; const void* pnext = nullptr;
if (has_custom_border_colors) { if (has_custom_border_colors) {
+2 -2
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-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
@@ -88,13 +88,13 @@ std::pair<std::array<Shader::TransformFeedbackVarying, 256>, u32> MakeTransformF
return 0; return 0;
}; };
UNIMPLEMENTED_IF_MSG(layout.stream != 0, "Stream is not zero: {}", layout.stream);
Shader::TransformFeedbackVarying varying{ Shader::TransformFeedbackVarying varying{
.buffer = static_cast<u32>(buffer), .buffer = static_cast<u32>(buffer),
.stride = layout.stride, .stride = layout.stride,
.offset = offset * 4, .offset = offset * 4,
.components = 1, .components = 1,
}; };
varying.stream = layout.stream;
const u32 base_offset = offset; const u32 base_offset = offset;
const auto attribute{get_attribute(offset)}; const auto attribute{get_attribute(offset)};
if (std::ranges::find(VECTORS, Common::AlignDown(attribute, 4)) != VECTORS.end()) { if (std::ranges::find(VECTORS, Common::AlignDown(attribute, 4)) != VECTORS.end()) {
+32 -25
View File
@@ -869,6 +869,10 @@ bool Device::HasTimelineSemaphore() const {
return features.timeline_semaphore.timelineSemaphore; return features.timeline_semaphore.timelineSemaphore;
} }
bool Device::MustEmulateBGR565() const {
return Settings::values.emulate_bgr565.GetValue();
}
bool Device::GetSuitability(bool requires_swapchain) { bool Device::GetSuitability(bool requires_swapchain) {
// Assume we will be suitable. // Assume we will be suitable.
bool suitable = true; bool suitable = true;
@@ -919,6 +923,17 @@ bool Device::GetSuitability(bool requires_swapchain) {
FOR_EACH_VK_FEATURE_EXT(FEATURE_EXTENSION); FOR_EACH_VK_FEATURE_EXT(FEATURE_EXTENSION);
FOR_EACH_VK_EXTENSION(EXTENSION); FOR_EACH_VK_EXTENSION(EXTENSION);
if (supported_extensions.contains(VK_KHR_ROBUSTNESS_2_EXTENSION_NAME)) {
loaded_extensions.erase(VK_EXT_ROBUSTNESS_2_EXTENSION_NAME);
loaded_extensions.insert(VK_KHR_ROBUSTNESS_2_EXTENSION_NAME);
extensions.robustness_2 = true;
} else if (supported_extensions.contains(VK_EXT_ROBUSTNESS_2_EXTENSION_NAME)) {
loaded_extensions.insert(VK_EXT_ROBUSTNESS_2_EXTENSION_NAME);
extensions.robustness_2 = true;
} else {
extensions.robustness_2 = false;
}
#undef FEATURE_EXTENSION #undef FEATURE_EXTENSION
#undef EXTENSION #undef EXTENSION
@@ -1131,8 +1146,6 @@ bool Device::GetSuitability(bool requires_swapchain) {
if (u32(Settings::values.dyna_state.GetValue()) == 0) { if (u32(Settings::values.dyna_state.GetValue()) == 0) {
LOG_INFO(Render_Vulkan, "Extended Dynamic State disabled by user setting, clearing all EDS features"); LOG_INFO(Render_Vulkan, "Extended Dynamic State disabled by user setting, clearing all EDS features");
features.custom_border_color.customBorderColors = false;
features.custom_border_color.customBorderColorWithoutFormat = false;
features.extended_dynamic_state.extendedDynamicState = false; features.extended_dynamic_state.extendedDynamicState = false;
features.extended_dynamic_state2.extendedDynamicState2 = false; features.extended_dynamic_state2.extendedDynamicState2 = false;
features.extended_dynamic_state3.extendedDynamicState3ColorBlendEnable = false; features.extended_dynamic_state3.extendedDynamicState3ColorBlendEnable = false;
@@ -1148,24 +1161,13 @@ bool Device::GetSuitability(bool requires_swapchain) {
void Device::RemoveUnsuitableExtensions() { void Device::RemoveUnsuitableExtensions() {
// VK_EXT_custom_border_color // VK_EXT_custom_border_color
// Enable extension if driver supports it, then check individual features
// - customBorderColors: Required to use VK_BORDER_COLOR_FLOAT_CUSTOM_EXT
// - customBorderColorWithoutFormat: Optional, allows VK_FORMAT_UNDEFINED
// If only customBorderColors is available, we must provide a specific format
if (extensions.custom_border_color) { if (extensions.custom_border_color) {
// Verify that at least customBorderColors is available extensions.custom_border_color =
if (!features.custom_border_color.customBorderColors) { features.custom_border_color.customBorderColors &&
LOG_WARNING(Render_Vulkan, features.custom_border_color.customBorderColorWithoutFormat;
"VK_EXT_custom_border_color reported but customBorderColors feature not available, disabling");
extensions.custom_border_color = false;
}
} }
RemoveExtensionFeatureIfUnsuitable(extensions.custom_border_color, features.custom_border_color, RemoveExtensionFeatureIfUnsuitable(extensions.custom_border_color, features.custom_border_color,
VK_EXT_CUSTOM_BORDER_COLOR_EXTENSION_NAME); VK_EXT_CUSTOM_BORDER_COLOR_EXTENSION_NAME);
// VK_KHR_unified_image_layouts
extensions.unified_image_layouts = features.unified_image_layouts.unifiedImageLayouts;
RemoveExtensionFeatureIfUnsuitable(extensions.unified_image_layouts, features.unified_image_layouts,
VK_KHR_UNIFIED_IMAGE_LAYOUTS_EXTENSION_NAME);
// VK_EXT_depth_bias_control // VK_EXT_depth_bias_control
extensions.depth_bias_control = extensions.depth_bias_control =
@@ -1251,16 +1253,22 @@ void Device::RemoveUnsuitableExtensions() {
VK_EXT_EXTENDED_DYNAMIC_STATE_3_EXTENSION_NAME); VK_EXT_EXTENDED_DYNAMIC_STATE_3_EXTENSION_NAME);
// VK_EXT_robustness2 // VK_EXT_robustness2
extensions.robustness_2 = features.robustness2.robustBufferAccess2 || features.robustness2.robustBufferAccess2 = VK_FALSE;
features.robustness2.robustImageAccess2 || features.robustness2.robustImageAccess2 = VK_FALSE;
features.robustness2.nullDescriptor; extensions.robustness_2 = features.robustness2.nullDescriptor;
const char* robustness2_extension_name =
loaded_extensions.contains(VK_KHR_ROBUSTNESS_2_EXTENSION_NAME)
? VK_KHR_ROBUSTNESS_2_EXTENSION_NAME
: VK_EXT_ROBUSTNESS_2_EXTENSION_NAME;
RemoveExtensionFeatureIfUnsuitable(extensions.robustness_2, features.robustness2, RemoveExtensionFeatureIfUnsuitable(extensions.robustness_2, features.robustness2,
VK_EXT_ROBUSTNESS_2_EXTENSION_NAME); robustness2_extension_name);
// VK_EXT_image_robustness // Image robustness
extensions.image_robustness = features.image_robustness.robustImageAccess; extensions.robust_image_access = features.robust_image_access.robustImageAccess;
RemoveExtensionFeatureIfUnsuitable(extensions.image_robustness, features.image_robustness, RemoveExtensionFeatureIfUnsuitable(extensions.robust_image_access,
features.robust_image_access,
VK_EXT_IMAGE_ROBUSTNESS_EXTENSION_NAME); VK_EXT_IMAGE_ROBUSTNESS_EXTENSION_NAME);
// VK_KHR_shader_atomic_int64 // VK_KHR_shader_atomic_int64
@@ -1288,8 +1296,7 @@ void Device::RemoveUnsuitableExtensions() {
// VK_EXT_transform_feedback // VK_EXT_transform_feedback
extensions.transform_feedback = extensions.transform_feedback =
features.transform_feedback.transformFeedback && features.transform_feedback.transformFeedback &&
properties.transform_feedback.maxTransformFeedbackBuffers > 0 && properties.transform_feedback.maxTransformFeedbackBuffers > 0;
properties.transform_feedback.transformFeedbackQueries;
RemoveExtensionFeatureIfUnsuitable(extensions.transform_feedback, features.transform_feedback, RemoveExtensionFeatureIfUnsuitable(extensions.transform_feedback, features.transform_feedback,
VK_EXT_TRANSFORM_FEEDBACK_EXTENSION_NAME); VK_EXT_TRANSFORM_FEEDBACK_EXTENSION_NAME);
+16 -37
View File
@@ -38,7 +38,7 @@ VK_DEFINE_HANDLE(VmaAllocator)
FEATURE(KHR, TimelineSemaphore, TIMELINE_SEMAPHORE, timeline_semaphore) FEATURE(KHR, TimelineSemaphore, TIMELINE_SEMAPHORE, timeline_semaphore)
#define FOR_EACH_VK_FEATURE_1_3(FEATURE) \ #define FOR_EACH_VK_FEATURE_1_3(FEATURE) \
FEATURE(EXT, ImageRobustness, IMAGE_ROBUSTNESS, image_robustness) \ FEATURE(EXT, ImageRobustness, IMAGE_ROBUSTNESS, robust_image_access) \
FEATURE(EXT, ShaderDemoteToHelperInvocation, SHADER_DEMOTE_TO_HELPER_INVOCATION, \ FEATURE(EXT, ShaderDemoteToHelperInvocation, SHADER_DEMOTE_TO_HELPER_INVOCATION, \
shader_demote_to_helper_invocation) \ shader_demote_to_helper_invocation) \
FEATURE(EXT, SubgroupSizeControl, SUBGROUP_SIZE_CONTROL, subgroup_size_control) \ FEATURE(EXT, SubgroupSizeControl, SUBGROUP_SIZE_CONTROL, subgroup_size_control) \
@@ -68,8 +68,7 @@ VK_DEFINE_HANDLE(VmaAllocator)
FEATURE(KHR, PipelineExecutableProperties, PIPELINE_EXECUTABLE_PROPERTIES, \ FEATURE(KHR, PipelineExecutableProperties, PIPELINE_EXECUTABLE_PROPERTIES, \
pipeline_executable_properties) \ pipeline_executable_properties) \
FEATURE(KHR, WorkgroupMemoryExplicitLayout, WORKGROUP_MEMORY_EXPLICIT_LAYOUT, \ FEATURE(KHR, WorkgroupMemoryExplicitLayout, WORKGROUP_MEMORY_EXPLICIT_LAYOUT, \
workgroup_memory_explicit_layout) \ workgroup_memory_explicit_layout)
FEATURE(KHR, UnifiedImageLayouts, UNIFIED_IMAGE_LAYOUTS, unified_image_layouts)
// Define miscellaneous extensions which may be used by the implementation here. // Define miscellaneous extensions which may be used by the implementation here.
@@ -124,7 +123,6 @@ VK_DEFINE_HANDLE(VmaAllocator)
EXTENSION_NAME(VK_EXT_EXTENDED_DYNAMIC_STATE_3_EXTENSION_NAME) \ EXTENSION_NAME(VK_EXT_EXTENDED_DYNAMIC_STATE_3_EXTENSION_NAME) \
EXTENSION_NAME(VK_EXT_EXTERNAL_MEMORY_HOST_EXTENSION_NAME) \ EXTENSION_NAME(VK_EXT_EXTERNAL_MEMORY_HOST_EXTENSION_NAME) \
EXTENSION_NAME(VK_EXT_4444_FORMATS_EXTENSION_NAME) \ EXTENSION_NAME(VK_EXT_4444_FORMATS_EXTENSION_NAME) \
EXTENSION_NAME(VK_EXT_IMAGE_ROBUSTNESS_EXTENSION_NAME) \
EXTENSION_NAME(VK_EXT_LINE_RASTERIZATION_EXTENSION_NAME) \ EXTENSION_NAME(VK_EXT_LINE_RASTERIZATION_EXTENSION_NAME) \
EXTENSION_NAME(VK_EXT_ROBUSTNESS_2_EXTENSION_NAME) \ EXTENSION_NAME(VK_EXT_ROBUSTNESS_2_EXTENSION_NAME) \
EXTENSION_NAME(VK_EXT_VERTEX_INPUT_DYNAMIC_STATE_EXTENSION_NAME) \ EXTENSION_NAME(VK_EXT_VERTEX_INPUT_DYNAMIC_STATE_EXTENSION_NAME) \
@@ -174,13 +172,11 @@ VK_DEFINE_HANDLE(VmaAllocator)
FEATURE_NAME(depth_bias_control, depthBiasExact) \ FEATURE_NAME(depth_bias_control, depthBiasExact) \
FEATURE_NAME(extended_dynamic_state, extendedDynamicState) \ FEATURE_NAME(extended_dynamic_state, extendedDynamicState) \
FEATURE_NAME(format_a4b4g4r4, formatA4B4G4R4) \ FEATURE_NAME(format_a4b4g4r4, formatA4B4G4R4) \
FEATURE_NAME(image_robustness, robustImageAccess) \ FEATURE_NAME(robust_image_access, robustImageAccess) \
FEATURE_NAME(index_type_uint8, indexTypeUint8) \ FEATURE_NAME(index_type_uint8, indexTypeUint8) \
FEATURE_NAME(primitive_topology_list_restart, primitiveTopologyListRestart) \ FEATURE_NAME(primitive_topology_list_restart, primitiveTopologyListRestart) \
FEATURE_NAME(provoking_vertex, provokingVertexLast) \ FEATURE_NAME(provoking_vertex, provokingVertexLast) \
FEATURE_NAME(robustness2, nullDescriptor) \ FEATURE_NAME(robustness2, nullDescriptor) \
FEATURE_NAME(robustness2, robustBufferAccess2) \
FEATURE_NAME(robustness2, robustImageAccess2) \
FEATURE_NAME(shader_float16_int8, shaderFloat16) \ FEATURE_NAME(shader_float16_int8, shaderFloat16) \
FEATURE_NAME(shader_float16_int8, shaderInt8) \ FEATURE_NAME(shader_float16_int8, shaderInt8) \
FEATURE_NAME(timeline_semaphore, timelineSemaphore) \ FEATURE_NAME(timeline_semaphore, timelineSemaphore) \
@@ -542,6 +538,17 @@ public:
return extensions.transform_feedback; return extensions.transform_feedback;
} }
/// Returns true if transform feedback draw commands are supported.
bool IsTransformFeedbackDrawSupported() const {
return extensions.transform_feedback && properties.transform_feedback.transformFeedbackDraw;
}
/// Returns true if transform feedback query types are supported.
bool IsTransformFeedbackQueriesSupported() const {
return extensions.transform_feedback &&
properties.transform_feedback.transformFeedbackQueries;
}
/// Returns true if the device supports VK_EXT_transform_feedback properly. /// Returns true if the device supports VK_EXT_transform_feedback properly.
bool AreTransformFeedbackGeometryStreamsSupported() const { bool AreTransformFeedbackGeometryStreamsSupported() const {
return features.transform_feedback.geometryStreams; return features.transform_feedback.geometryStreams;
@@ -552,36 +559,6 @@ public:
return extensions.custom_border_color; return extensions.custom_border_color;
} }
/// Returns true if the device supports VK_EXT_image_robustness.
bool IsExtImageRobustnessSupported() const {
return extensions.image_robustness;
}
/// Returns true if robustImageAccess is supported.
bool IsRobustImageAccessSupported() const {
return features.image_robustness.robustImageAccess;
}
/// Returns true if the device supports VK_EXT_robustness2.
bool IsExtRobustness2Supported() const {
return extensions.robustness_2;
}
/// Returns true if robustBufferAccess2 is supported.
bool IsRobustBufferAccess2Supported() const {
return features.robustness2.robustBufferAccess2;
}
/// Returns true if robustImageAccess2 is supported.
bool IsRobustImageAccess2Supported() const {
return features.robustness2.robustImageAccess2;
}
/// Returns true if nullDescriptor is supported.
bool IsNullDescriptorSupported() const {
return features.robustness2.nullDescriptor;
}
/// Returns true if customBorderColors feature is available. /// Returns true if customBorderColors feature is available.
bool IsCustomBorderColorsSupported() const { bool IsCustomBorderColorsSupported() const {
return features.custom_border_color.customBorderColors; return features.custom_border_color.customBorderColors;
@@ -805,6 +782,8 @@ public:
return features.robustness2.nullDescriptor; return features.robustness2.nullDescriptor;
} }
bool MustEmulateBGR565() const;
bool HasExactDepthBiasControl() const { bool HasExactDepthBiasControl() const {
return features.depth_bias_control.depthBiasExact; return features.depth_bias_control.depthBiasExact;
} }
@@ -123,6 +123,7 @@ void Load(VkDevice device, DeviceDispatch& dld) noexcept {
X(vkCmdEndDebugUtilsLabelEXT); X(vkCmdEndDebugUtilsLabelEXT);
X(vkCmdFillBuffer); X(vkCmdFillBuffer);
X(vkCmdPipelineBarrier); X(vkCmdPipelineBarrier);
X(vkCmdResetQueryPool);
X(vkCmdPushConstants); X(vkCmdPushConstants);
X(vkCmdPushDescriptorSetWithTemplateKHR); X(vkCmdPushDescriptorSetWithTemplateKHR);
X(vkCmdSetBlendConstants); X(vkCmdSetBlendConstants);
@@ -225,6 +225,7 @@ struct DeviceDispatch : InstanceDispatch {
PFN_vkCmdEndTransformFeedbackEXT vkCmdEndTransformFeedbackEXT{}; PFN_vkCmdEndTransformFeedbackEXT vkCmdEndTransformFeedbackEXT{};
PFN_vkCmdFillBuffer vkCmdFillBuffer{}; PFN_vkCmdFillBuffer vkCmdFillBuffer{};
PFN_vkCmdPipelineBarrier vkCmdPipelineBarrier{}; PFN_vkCmdPipelineBarrier vkCmdPipelineBarrier{};
PFN_vkCmdResetQueryPool vkCmdResetQueryPool{};
PFN_vkCmdPushConstants vkCmdPushConstants{}; PFN_vkCmdPushConstants vkCmdPushConstants{};
PFN_vkCmdPushDescriptorSetWithTemplateKHR vkCmdPushDescriptorSetWithTemplateKHR{}; PFN_vkCmdPushDescriptorSetWithTemplateKHR vkCmdPushDescriptorSetWithTemplateKHR{};
PFN_vkCmdResolveImage vkCmdResolveImage{}; PFN_vkCmdResolveImage vkCmdResolveImage{};
@@ -1168,6 +1169,10 @@ public:
dld->vkCmdEndQuery(handle, query_pool, query); dld->vkCmdEndQuery(handle, query_pool, query);
} }
void ResetQueryPool(VkQueryPool query_pool, u32 first_query, u32 query_count) const noexcept {
dld->vkCmdResetQueryPool(handle, query_pool, first_query, query_count);
}
void BindDescriptorSets(VkPipelineBindPoint bind_point, VkPipelineLayout layout, u32 first, void BindDescriptorSets(VkPipelineBindPoint bind_point, VkPipelineLayout layout, u32 first,
Span<VkDescriptorSet> sets, Span<u32> dynamic_offsets) const noexcept { Span<VkDescriptorSet> sets, Span<u32> dynamic_offsets) const noexcept {
dld->vkCmdBindDescriptorSets(handle, bind_point, layout, first, sets.size(), sets.data(), dld->vkCmdBindDescriptorSets(handle, bind_point, layout, first, sets.size(), sets.data(),
@@ -51,6 +51,8 @@ void ConfigureDebug::SetConfiguration() {
ui->enable_all_controllers->setChecked(Settings::values.enable_all_controllers.GetValue()); ui->enable_all_controllers->setChecked(Settings::values.enable_all_controllers.GetValue());
ui->extended_logging->setChecked(Settings::values.extended_logging.GetValue()); ui->extended_logging->setChecked(Settings::values.extended_logging.GetValue());
ui->perform_vulkan_check->setChecked(Settings::values.perform_vulkan_check.GetValue()); ui->perform_vulkan_check->setChecked(Settings::values.perform_vulkan_check.GetValue());
ui->serial_battery_edit->setText(QString::fromStdString(std::to_string(Settings::values.serial_battery.GetValue())));
ui->serial_board_edit->setText(QString::fromStdString(std::to_string(Settings::values.serial_unit.GetValue())));
#ifdef YUZU_USE_QT_WEB_ENGINE #ifdef YUZU_USE_QT_WEB_ENGINE
ui->disable_web_applet->setChecked(Settings::values.disable_web_applet.GetValue()); ui->disable_web_applet->setChecked(Settings::values.disable_web_applet.GetValue());
#else #else
@@ -126,6 +128,8 @@ void ConfigureDebug::ApplyConfiguration() {
Settings::values.extended_logging = ui->extended_logging->isChecked(); Settings::values.extended_logging = ui->extended_logging->isChecked();
Settings::values.perform_vulkan_check = ui->perform_vulkan_check->isChecked(); Settings::values.perform_vulkan_check = ui->perform_vulkan_check->isChecked();
Settings::values.disable_web_applet = ui->disable_web_applet->isChecked(); Settings::values.disable_web_applet = ui->disable_web_applet->isChecked();
Settings::values.serial_battery = ui->serial_battery_edit->text().toUInt();
Settings::values.serial_unit = ui->serial_board_edit->text().toUInt();
Settings::values.debug_knobs = ui->debug_knobs_spinbox->value(); Settings::values.debug_knobs = ui->debug_knobs_spinbox->value();
Debugger::ToggleConsole(); Debugger::ToggleConsole();
Common::Log::Filter filter; Common::Log::Filter filter;
+20 -24
View File
@@ -23,27 +23,18 @@ DataDialog::DataDialog(QWidget* parent) : QDialog(parent), ui(std::make_unique<U
// TODO: Should we make this a single widget that pulls data from a model? // TODO: Should we make this a single widget that pulls data from a model?
#define WIDGET(label, name) \ #define WIDGET(label, name) \
ui->page->addWidget(new DataWidget(FrontendCommon::DataManager::DataDir::name, \ ui->pages->addTab(new DataWidget(FrontendCommon::DataManager::DataDir::name, \
QtCommon::StringLookup::DataManager##name##Tooltip, \ QtCommon::StringLookup::DataManager##name##Tooltip, \
QStringLiteral(#name), this)); \ QStringLiteral(#name), this), \
ui->labels->addItem(label); label);
WIDGET(tr("Shaders"), Shaders) WIDGET(tr("Shaders"), Shaders)
WIDGET(tr("UserNAND"), UserNand) WIDGET(tr("User NAND"), UserNand)
WIDGET(tr("SysNAND"), SysNand) WIDGET(tr("System NAND"), SysNand)
WIDGET(tr("Mods"), Mods) WIDGET(tr("Mods"), Mods)
WIDGET(tr("Saves"), Saves) WIDGET(tr("Saves"), Saves)
#undef WIDGET #undef WIDGET
connect(ui->labels, &QListWidget::itemSelectionChanged, this, [this]() {
const auto items = ui->labels->selectedItems();
if (items.isEmpty()) {
return;
}
ui->page->setCurrentIndex(ui->labels->row(items[0]));
});
} }
DataDialog::~DataDialog() = default; DataDialog::~DataDialog() = default;
@@ -71,25 +62,29 @@ DataWidget::DataWidget(FrontendCommon::DataManager::DataDir data_dir,
} }
void DataWidget::clear() { void DataWidget::clear() {
std::string user_id = selectProfile(); std::optional<std::string> user_id = selectProfile();
QtCommon::Content::ClearDataDir(m_dir, user_id); if (!user_id) return;
QtCommon::Content::ClearDataDir(m_dir, user_id.value());
scan(); scan();
} }
void DataWidget::open() { void DataWidget::open() {
std::string user_id = selectProfile(); std::optional<std::string> user_id = selectProfile();
if (!user_id) return;
QDesktopServices::openUrl(QUrl::fromLocalFile( QDesktopServices::openUrl(QUrl::fromLocalFile(
QString::fromStdString(FrontendCommon::DataManager::GetDataDirString(m_dir, user_id)))); QString::fromStdString(FrontendCommon::DataManager::GetDataDirString(m_dir, user_id.value()))));
} }
void DataWidget::upload() { void DataWidget::upload() {
std::string user_id = selectProfile(); std::optional<std::string> user_id = selectProfile();
QtCommon::Content::ExportDataDir(m_dir, user_id, m_exportName); if (!user_id) return;
QtCommon::Content::ExportDataDir(m_dir, user_id.value(), m_exportName);
} }
void DataWidget::download() { void DataWidget::download() {
std::string user_id = selectProfile(); std::optional<std::string> user_id = selectProfile();
QtCommon::Content::ImportDataDir(m_dir, user_id, std::bind(&DataWidget::scan, this)); if (!user_id) return;
QtCommon::Content::ImportDataDir(m_dir, user_id.value(), std::bind(&DataWidget::scan, this));
} }
void DataWidget::scan() { void DataWidget::scan() {
@@ -108,10 +103,11 @@ void DataWidget::scan() {
QtConcurrent::run([this]() { return FrontendCommon::DataManager::DataDirSize(m_dir); })); QtConcurrent::run([this]() { return FrontendCommon::DataManager::DataDirSize(m_dir); }));
} }
std::string DataWidget::selectProfile() { std::optional<std::string> DataWidget::selectProfile() {
std::string user_id{}; std::string user_id{};
if (m_dir == FrontendCommon::DataManager::DataDir::Saves) { if (m_dir == FrontendCommon::DataManager::DataDir::Saves) {
user_id = GetProfileIDString(); user_id = GetProfileIDString();
if (user_id.empty()) return std::nullopt;
} }
return user_id; return user_id;
+1 -1
View File
@@ -45,7 +45,7 @@ private:
FrontendCommon::DataManager::DataDir m_dir; FrontendCommon::DataManager::DataDir m_dir;
const QString m_exportName; const QString m_exportName;
std::string selectProfile(); std::optional<std::string> selectProfile();
}; };
#endif // DATA_DIALOG_H #endif // DATA_DIALOG_H
+2 -24
View File
@@ -27,31 +27,9 @@
</property> </property>
<layout class="QVBoxLayout" name="verticalLayout"> <layout class="QVBoxLayout" name="verticalLayout">
<item> <item>
<layout class="QHBoxLayout" name="horizontalLayout" stretch="1,1"> <layout class="QHBoxLayout" name="horizontalLayout" stretch="0">
<item> <item>
<widget class="QListWidget" name="labels"> <widget class="QTabWidget" name="pages">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item>
<widget class="QStackedWidget" name="page">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>275</width>
<height>200</height>
</size>
</property>
<property name="currentIndex"> <property name="currentIndex">
<number>-1</number> <number>-1</number>
</property> </property>