Compare commits

..

12 Commits

Author SHA1 Message Date
CamilleLaVey 53060c9a91 Small experiment 2026-04-08 23:47:15 +02:00
CamilleLaVey 8200a7564c [texture_cache] Removal of lowmemorydevice dead code 2026-04-08 23:47:15 +02:00
CamilleLaVey 1637a2cb30 fix license headers+ 2026-04-08 23:47:15 +02:00
CamilleLaVey cb851cf091 [texture_cache] Adjusted GC logic for the iterations with older or obsolete textures 2026-04-08 23:47:15 +02:00
CamilleLaVey 90fd089e58 [texture_cache] Reduce garbage collection logic by simplifying conditions and thresholds 2026-04-08 23:47:15 +02:00
CamilleLaVey 29e77b5e2d small fix for the softlock after lru cache removal 2026-04-08 23:47:15 +02:00
CamilleLaVey c515a6bf83 Gido MEOW 2026-04-08 23:47:15 +02:00
CamilleLaVey 2e90822686 [texture_cache] Replace LRU index with frame tick in ImageBase + update garbage collection logic 2026-04-08 23:47:15 +02:00
CamilleLaVey 9bef0b35ba I got meowed by Gidoly 2026-04-08 23:47:15 +02:00
CamilleLaVey ba1e137bb6 [buffer_cache] Removal of LRU inside buffer cache and replaced with tick operations inside frames. 2026-04-08 23:47:15 +02:00
CamilleLaVey febddbd00c [maxwell] Removed prefetching for ProcessCommands 2026-04-08 23:47:15 +02:00
CamilleLaVey ad2225b5d0 [maxwell] Refactor execution mask initialization to use fill() instead of reset() 2026-04-08 23:47:15 +02:00
126 changed files with 9786 additions and 10067 deletions
+5 -5
View File
@@ -35,17 +35,17 @@ set(GIT_DESC ${BUILD_VERSION})
# Generate cpp with Git revision from template # Generate cpp with Git revision from template
# Also if this is a CI build, add the build name (ie: Nightly, Canary) to the scm_rev file as well # Also if this is a CI build, add the build name (ie: Nightly, Canary) to the scm_rev file as well
# Auto-updater metadata! Must somewhat mirror GitHub/Forgejo API endpoint # Auto-updater metadata! Must somewhat mirror GitHub API endpoint
set(BUILD_AUTO_UPDATE_API_PATH "/latest/release.json")
if (NIGHTLY_BUILD) if (NIGHTLY_BUILD)
set(BUILD_AUTO_UPDATE_WEBSITE "https://git.eden-emu.dev") set(BUILD_AUTO_UPDATE_WEBSITE "https://git.eden-emu.dev")
set(BUILD_AUTO_UPDATE_API "nightly.eden-emu.dev") set(BUILD_AUTO_UPDATE_API "git.eden-emu.dev")
set(BUILD_AUTO_UPDATE_API_PATH "/api/v1/repos/")
set(BUILD_AUTO_UPDATE_REPO "eden-ci/nightly") set(BUILD_AUTO_UPDATE_REPO "eden-ci/nightly")
set(REPO_NAME "Eden Nightly") set(REPO_NAME "Eden Nightly")
else() else()
set(BUILD_AUTO_UPDATE_WEBSITE "https://git.eden-emu.dev") set(BUILD_AUTO_UPDATE_WEBSITE "https://git.eden-emu.dev")
set(BUILD_AUTO_UPDATE_API "stable.eden-emu.dev") set(BUILD_AUTO_UPDATE_API "git.eden-emu.dev")
set(BUILD_AUTO_UPDATE_API_PATH "/api/v1/repos/")
set(BUILD_AUTO_UPDATE_REPO "eden-emu/eden") set(BUILD_AUTO_UPDATE_REPO "eden-emu/eden")
set(REPO_NAME "Eden") set(REPO_NAME "Eden")
endif() endif()
+337 -334
View File
File diff suppressed because it is too large Load Diff
+306 -302
View File
File diff suppressed because it is too large Load Diff
+306 -302
View File
File diff suppressed because it is too large Load Diff
+306 -302
View File
File diff suppressed because it is too large Load Diff
+306 -302
View File
File diff suppressed because it is too large Load Diff
+306 -302
View File
File diff suppressed because it is too large Load Diff
+334 -331
View File
File diff suppressed because it is too large Load Diff
+306 -302
View File
File diff suppressed because it is too large Load Diff
+308 -304
View File
File diff suppressed because it is too large Load Diff
+306 -302
View File
File diff suppressed because it is too large Load Diff
+396 -393
View File
File diff suppressed because it is too large Load Diff
+308 -304
View File
File diff suppressed because it is too large Load Diff
+306 -302
View File
File diff suppressed because it is too large Load Diff
+306 -302
View File
File diff suppressed because it is too large Load Diff
+307 -303
View File
File diff suppressed because it is too large Load Diff
+306 -302
View File
File diff suppressed because it is too large Load Diff
+308 -304
View File
File diff suppressed because it is too large Load Diff
+471 -495
View File
File diff suppressed because it is too large Load Diff
+306 -302
View File
File diff suppressed because it is too large Load Diff
+308 -304
View File
File diff suppressed because it is too large Load Diff
+308 -305
View File
File diff suppressed because it is too large Load Diff
+308 -304
View File
File diff suppressed because it is too large Load Diff
+308 -305
View File
File diff suppressed because it is too large Load Diff
+306 -302
View File
File diff suppressed because it is too large Load Diff
+306 -302
View File
File diff suppressed because it is too large Load Diff
+308 -304
View File
File diff suppressed because it is too large Load Diff
+306 -302
View File
File diff suppressed because it is too large Load Diff
@@ -15,7 +15,6 @@ 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
@@ -101,7 +100,6 @@ 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) {
@@ -268,18 +266,12 @@ 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) {
!isPictureInPictureSupported() || if (BooleanSetting.PICTURE_IN_PICTURE.getBoolean() && !isInPictureInPictureMode) {
!BooleanSetting.PICTURE_IN_PICTURE.getBoolean() || val pictureInPictureParamsBuilder = PictureInPictureParams.Builder()
isInPictureInPictureMode .getPictureInPictureActionsBuilder().getPictureInPictureAspectBuilder()
) { enterPictureInPictureMode(pictureInPictureParamsBuilder.build())
return }
}
val pictureInPictureParamsBuilder = PictureInPictureParams.Builder()
.getPictureInPictureActionsBuilder().getPictureInPictureAspectBuilder()
runPictureInPictureAction("enter picture-in-picture mode") {
enterPictureInPictureMode(pictureInPictureParamsBuilder.build())
} }
} }
@@ -659,29 +651,7 @@ 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) {
@@ -691,9 +661,7 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener, InputManager
BooleanSetting.PICTURE_IN_PICTURE.getBoolean() && isEmulationActive BooleanSetting.PICTURE_IN_PICTURE.getBoolean() && isEmulationActive
) )
} }
runPictureInPictureAction("set picture-in-picture params") { setPictureInPictureParams(pictureInPictureParamsBuilder.build())
setPictureInPictureParams(pictureInPictureParamsBuilder.build())
}
} }
fun displayMultiplayerDialog() { fun displayMultiplayerDialog() {
@@ -1,7 +1,3 @@
// 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
@@ -37,10 +33,8 @@ 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,7 +17,6 @@ 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,13 +756,6 @@ 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,6 +284,8 @@ 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)
@@ -293,10 +295,7 @@ 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,23 +291,13 @@ 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"
) )
@@ -881,7 +871,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
if (drawerView == binding.quickSettingsSheet) { if (drawerView == binding.quickSettingsSheet) {
isQuickSettingsMenuOpen = true isQuickSettingsMenuOpen = true
if (shouldUseCustom) { if (shouldUseCustom) {
SettingsFile.loadCustomConfig(game!!) SettingsFile.loadCustomConfig(args.game!!)
} }
} }
} }
@@ -2509,6 +2499,7 @@ 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) {
@@ -2546,8 +2537,7 @@ 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,23 +17,16 @@ 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
@@ -56,7 +49,6 @@ 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
@@ -83,63 +75,6 @@ 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)
@@ -184,25 +119,6 @@ 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, true, Settings::Setting<bool> hide_overlay_on_controller_input{linkage, false,
"hide_overlay_on_controller_input", "hide_overlay_on_controller_input",
Settings::Category::Overlay, Settings::Category::Overlay,
Settings::Specialization::Default, true, Settings::Specialization::Default, true,
+8 -6
View File
@@ -1726,8 +1726,9 @@ JNIEXPORT jstring JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_getUpdateUrl(
jobject obj, jobject obj,
jstring version) { jstring version) {
const char* version_str = env->GetStringUTFChars(version, nullptr); const char* version_str = env->GetStringUTFChars(version, nullptr);
const std::string url = fmt::format("{}/{}", const std::string url = fmt::format("{}/{}/releases/tag/{}",
std::string{Common::g_build_auto_update_api}, std::string{Common::g_build_auto_update_website},
std::string{Common::g_build_auto_update_repo},
version_str); version_str);
env->ReleaseStringUTFChars(version, version_str); env->ReleaseStringUTFChars(version, version_str);
return env->NewStringUTF(url.c_str()); return env->NewStringUTF(url.c_str());
@@ -1759,10 +1760,11 @@ JNIEXPORT jstring JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_getUpdateApkUrl(
} }
const std::string apk_filename = fmt::format("Eden-Android-{}-{}.apk", artifact_str, variant); const std::string apk_filename = fmt::format("Eden-Android-{}-{}.apk", artifact_str, variant);
const std::string url = fmt::format("{}/{}/releases/download/{}/{}",
const std::string url = fmt::format("{}/{}/{}", std::string{Common::g_build_auto_update_website},
std::string{Common::g_build_auto_update_api}, std::string{Common::g_build_auto_update_repo},
version_str, apk_filename); version_str,
apk_filename);
env->ReleaseStringUTFChars(tag, version_str); env->ReleaseStringUTFChars(tag, version_str);
env->ReleaseStringUTFChars(artifact, artifact_str); env->ReleaseStringUTFChars(artifact, artifact_str);
@@ -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,10 +486,6 @@
<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>
@@ -631,10 +627,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>
@@ -660,7 +656,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>
@@ -686,8 +682,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>
@@ -766,7 +762,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>
@@ -878,13 +874,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>
@@ -1126,7 +1122,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 del procesador</string> <string name="cpu_info">Información de la CPU</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 los contenidos instalados</string> <string name="verify_installed_content">Verificar contenido instalado</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 del procesador</string> <string name="cpu_backend">Motor de la CPU</string>
<string name="cpu_accuracy">Precisión del procesador</string> <string name="cpu_accuracy">Precisión de la CPU</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 del procesador</string> <string name="fast_cpu_time">Overclock de la CPU</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 77–21000.</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 77–21000.</string>
@@ -480,10 +480,6 @@
<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>
@@ -536,7 +532,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">Procesador</string> <string name="cpu">CPU</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,10 +482,6 @@
<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>
@@ -1084,7 +1080,6 @@
<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,10 +482,6 @@
<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">显示 Frametime</string> <string name="show_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,25 +476,21 @@
<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">启用对先前缓冲区状态的访问。此选项可在某些游戏中提升渲染质量并保持性能的一致性。</string> <string name="enable_buffer_history_description">允许访问之前的缓冲状态。\n这个选项可能会提升某些游戏的渲染质量和性能一致性。</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">修复 Bloom 效果</string> <string name="fix_bloom_effects">修复泛光效果</string>
<string name="fix_bloom_effects_description">减少《塞尔达传说:智慧的再现》(Adreno 700)中的 bloom 模糊,并移除《Burnout》中的 bloom 效果。警告:可能会导致在其他游戏中出现画面显示问题。</string> <string name="fix_bloom_effects_description">减少《塞尔达传说:智慧的再现》(LA/EOW) 在 Adreno 700 系列 GPU 上的模糊感,并移除《横冲直撞》(Burnout) 中的模糊特效。警告:可能会导致其他游戏出现画面花屏或异常。</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/ QCOM drivers. Will crash on older Turnip drivers.</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="hacks">Hacks</string> <string name="hacks">Hacks</string>
@@ -510,9 +510,7 @@
<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 A6XX - A7XX/ Turnip), 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 700), 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,9 +243,6 @@ 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()));
-139
View File
@@ -1,139 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <deque>
#include <memory>
#include <type_traits>
#include "common/common_types.h"
namespace Common {
template <class Traits>
class LeastRecentlyUsedCache {
using ObjectType = typename Traits::ObjectType;
using TickType = typename Traits::TickType;
struct Item {
ObjectType obj;
TickType tick;
Item* next{};
Item* prev{};
};
public:
LeastRecentlyUsedCache() : first_item{}, last_item{} {}
~LeastRecentlyUsedCache() = default;
size_t Insert(ObjectType obj, TickType tick) {
const auto new_id = Build();
auto& item = item_pool[new_id];
item.obj = obj;
item.tick = tick;
Attach(item);
return new_id;
}
void Touch(size_t id, TickType tick) {
auto& item = item_pool[id];
if (item.tick >= tick) {
return;
}
item.tick = tick;
if (&item == last_item) {
return;
}
Detach(item);
Attach(item);
}
void Free(size_t id) {
auto& item = item_pool[id];
Detach(item);
item.prev = nullptr;
item.next = nullptr;
free_items.push_back(id);
}
template <typename Func>
void ForEachItemBelow(TickType tick, Func&& func) {
static constexpr bool RETURNS_BOOL =
std::is_same_v<std::invoke_result<Func, ObjectType>, bool>;
Item* iterator = first_item;
while (iterator) {
if (static_cast<s64>(tick) - static_cast<s64>(iterator->tick) < 0) {
return;
}
Item* next = iterator->next;
if constexpr (RETURNS_BOOL) {
if (func(iterator->obj)) {
return;
}
} else {
func(iterator->obj);
}
iterator = next;
}
}
private:
size_t Build() {
if (free_items.empty()) {
const size_t item_id = item_pool.size();
auto& item = item_pool.emplace_back();
item.next = nullptr;
item.prev = nullptr;
return item_id;
}
const size_t item_id = free_items.front();
free_items.pop_front();
auto& item = item_pool[item_id];
item.next = nullptr;
item.prev = nullptr;
return item_id;
}
void Attach(Item& item) {
if (!first_item) {
first_item = &item;
}
if (!last_item) {
last_item = &item;
} else {
item.prev = last_item;
last_item->next = &item;
item.next = nullptr;
last_item = &item;
}
}
void Detach(Item& item) {
if (item.prev) {
item.prev->next = item.next;
}
if (item.next) {
item.next->prev = item.prev;
}
if (&item == first_item) {
first_item = item.next;
if (first_item) {
first_item->prev = nullptr;
}
}
if (&item == last_item) {
last_item = item.prev;
if (last_item) {
last_item->next = nullptr;
}
}
}
std::deque<Item> item_pool;
std::deque<size_t> free_items;
Item* first_item{};
Item* last_item{};
};
} // namespace Common
+4 -7
View File
@@ -555,9 +555,6 @@ 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};
@@ -587,7 +584,7 @@ struct Values {
SwitchableSetting<ExtendedDynamicState> dyna_state{linkage, SwitchableSetting<ExtendedDynamicState> dyna_state{linkage,
#if defined(ANDROID) #if defined(ANDROID)
ExtendedDynamicState::Disabled, ExtendedDynamicState::EDS1,
#elif defined(__APPLE__) #elif defined(__APPLE__)
ExtendedDynamicState::Disabled, ExtendedDynamicState::Disabled,
#else #else
@@ -639,8 +636,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::Debugging}; Setting<u32> serial_battery{linkage, 0, "serial_battery", Category::System};
Setting<u32> serial_unit{linkage, 0, "serial_unit", Category::Debugging}; Setting<u32> serial_unit{linkage, 0, "serial_unit", Category::System};
// 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{
@@ -810,7 +807,7 @@ struct Values {
0, 0,
65535, 65535,
"debug_knobs", "debug_knobs",
Category::Debugging, Category::Core,
Specialization::Countable, Specialization::Countable,
true, true,
true}; true};
@@ -289,6 +289,7 @@ 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,6 +340,7 @@ 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:
+59 -43
View File
@@ -6,8 +6,6 @@
#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"
@@ -20,53 +18,59 @@ 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
if (!flusher_data.thread) {
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); // Only start the thread the very first time this function is called
flusher_data.msg_buffer.clear(); std::call_once(start_flag, [] {
} flush_thread = std::make_unique<std::thread>(FlushDbgLoop);
if (stop_token.stop_requested()) break; });
}
}
flusher_data.msg_cv.notify_all();
});
}
{ {
std::lock_guard lock(flusher_data.msg_mutex); std::lock_guard lock(msg_mutex);
const auto old_size = flusher_data.msg_buffer.size(); const auto old_size = msg_buffer.size();
flusher_data.msg_buffer.resize(old_size + len); msg_buffer.resize(old_size + len);
GetCurrentMemory(system.Kernel()).ReadBlock(address, flusher_data.msg_buffer.data() + old_size, len); GetCurrentMemory(system.Kernel()).ReadBlock(address, msg_buffer.data() + old_size, len);
flusher_data.last_msg_time = std::chrono::steady_clock::now();
last_msg_time = std::chrono::steady_clock::now();
} }
flusher_data.msg_cv.notify_one();
msg_cv.notify_one();
R_SUCCEED(); R_SUCCEED();
} }
@@ -78,4 +82,16 @@ 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
+7 -3
View File
@@ -89,7 +89,7 @@ void NvMap::UnmapHandle(Handle& handle_description) {
// Free and unmap the handle from Host1x GMMU // Free and unmap the handle from Host1x GMMU
if (handle_description.pin_virt_address) { if (handle_description.pin_virt_address) {
host1x.gmmu_manager.Unmap(static_cast<GPUVAddr>(handle_description.pin_virt_address), host1x.GMMU().Unmap(static_cast<GPUVAddr>(handle_description.pin_virt_address),
handle_description.aligned_size); handle_description.aligned_size);
host1x.Allocator().Free(handle_description.pin_virt_address, host1x.Allocator().Free(handle_description.pin_virt_address,
static_cast<u32>(handle_description.aligned_size)); static_cast<u32>(handle_description.aligned_size));
@@ -169,8 +169,12 @@ DAddr NvMap::PinHandle(NvMap::Handle::Id handle, bool low_area_pin) {
std::scoped_lock lock(handle_description->mutex); std::scoped_lock lock(handle_description->mutex);
const auto map_low_area = [&] { const auto map_low_area = [&] {
if (handle_description->pin_virt_address == 0) { if (handle_description->pin_virt_address == 0) {
u32 address = host1x.Allocator().Allocate(u32(handle_description->aligned_size)); auto& gmmu_allocator = host1x.Allocator();
host1x.gmmu_manager.Map(GPUVAddr(address), handle_description->d_address, handle_description->aligned_size); auto& gmmu = host1x.GMMU();
u32 address =
gmmu_allocator.Allocate(static_cast<u32>(handle_description->aligned_size));
gmmu.Map(static_cast<GPUVAddr>(address), handle_description->d_address,
handle_description->aligned_size);
handle_description->pin_virt_address = address; handle_description->pin_virt_address = address;
} }
}; };
@@ -23,14 +23,16 @@ 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);
@@ -47,9 +49,6 @@ 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();
} }
@@ -58,6 +57,11 @@ 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) {
+44 -9
View File
@@ -80,24 +80,55 @@ std::optional<std::string> UpdateChecker::GetResponse(std::string url, std::stri
} }
} }
std::optional<UpdateChecker::Update> UpdateChecker::GetLatestRelease() { std::optional<UpdateChecker::Update> UpdateChecker::GetLatestRelease(bool include_prereleases) {
#ifdef YUZU_BUNDLED_OPENSSL #ifdef YUZU_BUNDLED_OPENSSL
const auto update_check_url = fmt::format("https://{}", Common::g_build_auto_update_api); const auto update_check_url = fmt::format("https://{}", Common::g_build_auto_update_api);
#else #else
const auto update_check_url = std::string{Common::g_build_auto_update_api}; const auto update_check_url = std::string{Common::g_build_auto_update_api};
#endif #endif
auto update_check_path = std::string{Common::g_build_auto_update_api_path}; auto update_check_path = fmt::format("{}{}", std::string{Common::g_build_auto_update_api_path},
std::string{Common::g_build_auto_update_repo});
try { try {
const auto response = UpdateChecker::GetResponse(update_check_url, update_check_path); if (include_prereleases) { // This can return either a prerelease or a stable release,
// whichever is more recent.
const auto update_check_tags_path = update_check_path + "/tags";
const auto update_check_releases_path = update_check_path + "/releases";
if (!response) const auto tags_response = UpdateChecker::GetResponse(update_check_url, update_check_tags_path);
return {}; const auto releases_response = UpdateChecker::GetResponse(update_check_url, update_check_releases_path);
const std::string latest_tag = nlohmann::json::parse(response.value()).at("tag_name"); if (!tags_response || !releases_response)
const std::string latest_name = nlohmann::json::parse(response.value()).at("name"); return {};
const std::string latest_tag
= nlohmann::json::parse(tags_response.value()).at(0).at("name");
const std::string latest_name =
nlohmann::json::parse(releases_response.value()).at(0).at("name");
const bool latest_tag_has_release = releases_response.value().find(
fmt::format("\"{}\"", latest_tag))
!= std::string::npos;
// If there is a newer tag, but that tag has no associated release, don't prompt the
// user to update.
if (!latest_tag_has_release)
return {};
return Update{latest_tag, latest_name};
} else { // This is a stable release, only check for other stable releases.
update_check_path += "/releases/latest";
const auto response = UpdateChecker::GetResponse(update_check_url, update_check_path);
if (!response)
return {};
const std::string latest_tag = nlohmann::json::parse(response.value()).at("tag_name");
const std::string latest_name = nlohmann::json::parse(response.value()).at("name");
return Update{latest_tag, latest_name};
}
return Update{latest_tag, latest_name};
} catch (nlohmann::detail::out_of_range&) { } catch (nlohmann::detail::out_of_range&) {
LOG_ERROR(Frontend, LOG_ERROR(Frontend,
"Parsing JSON response from {}{} failed during update check: " "Parsing JSON response from {}{} failed during update check: "
@@ -116,8 +147,12 @@ std::optional<UpdateChecker::Update> UpdateChecker::GetLatestRelease() {
} }
std::optional<UpdateChecker::Update> UpdateChecker::GetUpdate() { std::optional<UpdateChecker::Update> UpdateChecker::GetUpdate() {
const bool is_prerelease = ((strstr(Common::g_build_version, "pre-alpha") != NULL) ||
(strstr(Common::g_build_version, "alpha") != NULL) ||
(strstr(Common::g_build_version, "beta") != NULL) ||
(strstr(Common::g_build_version, "rc") != NULL));
const std::optional<UpdateChecker::Update> latest_release_tag = const std::optional<UpdateChecker::Update> latest_release_tag =
UpdateChecker::GetLatestRelease(); UpdateChecker::GetLatestRelease(is_prerelease);
if (!latest_release_tag) if (!latest_release_tag)
goto empty; goto empty;
+1 -1
View File
@@ -18,6 +18,6 @@ typedef struct {
} Update; } Update;
std::optional<std::string> GetResponse(std::string url, std::string path); std::optional<std::string> GetResponse(std::string url, std::string path);
std::optional<Update> GetLatestRelease(); std::optional<Update> GetLatestRelease(bool include_prereleases);
std::optional<Update> GetUpdate(); std::optional<Update> GetUpdate();
} // namespace UpdateChecker } // namespace UpdateChecker
+3 -2
View File
@@ -169,8 +169,6 @@ 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"
@@ -317,6 +315,9 @@ 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,9 +178,6 @@ 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,7 +76,6 @@ 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{};
+4 -4
View File
@@ -8,7 +8,7 @@ add_subdirectory(host_shaders)
add_subdirectory(gpu_logging) add_subdirectory(gpu_logging)
if(LIBVA_FOUND) if(LIBVA_FOUND)
set_source_files_properties(host1x/ffmpeg.cpp set_source_files_properties(host1x/ffmpeg/ffmpeg.cpp
PROPERTIES COMPILE_DEFINITIONS LIBVA_FOUND=1) PROPERTIES COMPILE_DEFINITIONS LIBVA_FOUND=1)
list(APPEND FFmpeg_LIBRARIES ${LIBVA_LIBRARIES}) list(APPEND FFmpeg_LIBRARIES ${LIBVA_LIBRARIES})
endif() endif()
@@ -72,9 +72,9 @@ add_library(video_core STATIC
host1x/codecs/vp8.h host1x/codecs/vp8.h
host1x/codecs/vp9.cpp host1x/codecs/vp9.cpp
host1x/codecs/vp9.h host1x/codecs/vp9.h
host1x/codec_types.h host1x/codecs/vp9_types.h
host1x/ffmpeg.cpp host1x/ffmpeg/ffmpeg.cpp
host1x/ffmpeg.h host1x/ffmpeg/ffmpeg.h
host1x/control.cpp host1x/control.cpp
host1x/control.h host1x/control.h
host1x/gpu_device_memory_manager.cpp host1x/gpu_device_memory_manager.cpp
+5 -5
View File
@@ -109,12 +109,12 @@ public:
return static_cast<u32>(other_cpu_addr - cpu_addr); return static_cast<u32>(other_cpu_addr - cpu_addr);
} }
size_t getLRUID() const noexcept { u64 GetFrameTick() const noexcept {
return lru_id; return frame_tick;
} }
void setLRUID(size_t lru_id_) { void SetFrameTick(u64 tick) noexcept {
lru_id = lru_id_; frame_tick = tick;
} }
size_t SizeBytes() const { size_t SizeBytes() const {
@@ -125,7 +125,7 @@ private:
VAddr cpu_addr = 0; VAddr cpu_addr = 0;
BufferFlagBits flags{}; BufferFlagBits flags{};
int stream_score = 0; int stream_score = 0;
size_t lru_id = SIZE_MAX; u64 frame_tick = 0;
size_t size_bytes = 0; size_t size_bytes = 0;
}; };
+27 -26
View File
@@ -58,17 +58,22 @@ void BufferCache<P>::RunGarbageCollector() {
const bool aggressive_gc = total_used_memory >= critical_memory; const bool aggressive_gc = total_used_memory >= critical_memory;
const u64 ticks_to_destroy = aggressive_gc ? 60 : 120; const u64 ticks_to_destroy = aggressive_gc ? 60 : 120;
int num_iterations = aggressive_gc ? 64 : 32; int num_iterations = aggressive_gc ? 64 : 32;
const auto clean_up = [this, &num_iterations](BufferId buffer_id) { const u64 threshold = frame_tick - ticks_to_destroy;
boost::container::small_vector<BufferId, 64> expired;
for (auto [id, buffer] : slot_buffers) {
if (buffer->GetFrameTick() < threshold) {
expired.push_back(id);
}
}
for (const auto buffer_id : expired) {
if (num_iterations == 0) { if (num_iterations == 0) {
return true; break;
} }
--num_iterations; --num_iterations;
auto& buffer = slot_buffers[buffer_id]; auto& buffer = slot_buffers[buffer_id];
DownloadBufferMemory(buffer); DownloadBufferMemory(buffer);
DeleteBuffer(buffer_id); DeleteBuffer(buffer_id);
return false; }
};
lru_cache.ForEachItemBelow(frame_tick - ticks_to_destroy, clean_up);
} }
template <class P> template <class P>
@@ -1067,29 +1072,26 @@ 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];
const auto& control = maxwell3d->regs.transform_feedback.controls[index]; if (maxwell3d->regs.transform_feedback.controls[index].varying_count == 0 &&
const bool has_layout = control.varying_count != 0 || control.stride != 0; maxwell3d->regs.transform_feedback.controls[index].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);
host_bindings.buffers.push_back(host_buffer); MarkWrittenBuffer(binding.buffer_id, binding.device_addr, size);
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);
} }
runtime.BindTransformFeedbackBuffers(host_bindings); if (host_bindings.buffers.size() > 0) {
runtime.BindTransformFeedbackBuffers(host_bindings);
}
} }
template <class P> template <class P>
@@ -1598,10 +1600,9 @@ void BufferCache<P>::ChangeRegister(BufferId buffer_id) {
const auto size = buffer.SizeBytes(); const auto size = buffer.SizeBytes();
if (insert) { if (insert) {
total_used_memory += Common::AlignUp(size, 1024); total_used_memory += Common::AlignUp(size, 1024);
buffer.setLRUID(lru_cache.Insert(buffer_id, frame_tick)); buffer.SetFrameTick(frame_tick);
} else { } else {
total_used_memory -= Common::AlignUp(size, 1024); total_used_memory -= Common::AlignUp(size, 1024);
lru_cache.Free(buffer.getLRUID());
} }
const DAddr device_addr_begin = buffer.CpuAddr(); const DAddr device_addr_begin = buffer.CpuAddr();
const DAddr device_addr_end = device_addr_begin + size; const DAddr device_addr_end = device_addr_begin + size;
@@ -1619,7 +1620,7 @@ void BufferCache<P>::ChangeRegister(BufferId buffer_id) {
template <class P> template <class P>
void BufferCache<P>::TouchBuffer(Buffer& buffer, BufferId buffer_id) noexcept { void BufferCache<P>::TouchBuffer(Buffer& buffer, BufferId buffer_id) noexcept {
if (buffer_id != NULL_BUFFER_ID) { if (buffer_id != NULL_BUFFER_ID) {
lru_cache.Touch(buffer.getLRUID(), frame_tick); buffer.SetFrameTick(frame_tick);
} }
} }
@@ -23,7 +23,6 @@
#include "common/common_types.h" #include "common/common_types.h"
#include "common/div_ceil.h" #include "common/div_ceil.h"
#include "common/literals.h" #include "common/literals.h"
#include "common/lru_cache.h"
#include "common/range_sets.h" #include "common/range_sets.h"
#include "common/scope_exit.h" #include "common/scope_exit.h"
#include "common/settings.h" #include "common/settings.h"
@@ -506,11 +505,6 @@ private:
size_t immediate_buffer_capacity = 0; size_t immediate_buffer_capacity = 0;
Common::ScratchBuffer<u8> immediate_buffer_alloc; Common::ScratchBuffer<u8> immediate_buffer_alloc;
struct LRUItemParams {
using ObjectType = BufferId;
using TickType = u64;
};
Common::LeastRecentlyUsedCache<LRUItemParams> lru_cache;
u64 frame_tick = 0; u64 frame_tick = 0;
u64 total_used_memory = 0; u64 total_used_memory = 0;
u64 minimum_memory = 0; u64 minimum_memory = 0;
+4 -5
View File
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Ryujinx Team and Contributors // SPDX-FileCopyrightText: Ryujinx Team and Contributors
@@ -20,10 +20,9 @@
namespace Tegra { namespace Tegra {
CDmaPusher::CDmaPusher(Host1x::Host1x& host1x_, s32 id) CDmaPusher::CDmaPusher(Host1x::Host1x& host1x_, s32 id)
: host_processor{std::make_unique<Host1x::Control>(host1x_)} : host1x{host1x_}, memory_manager{host1x.GMMU()},
, host1x{host1x_} host_processor{std::make_unique<Host1x::Control>(host1x_)}, current_class{
, current_class{ChClassId(id)} static_cast<ChClassId>(id)} {
{
thread = std::jthread([this](std::stop_token stop_token) { ProcessEntries(stop_token); }); thread = std::jthread([this](std::stop_token stop_token) { ProcessEntries(stop_token); });
} }
+9 -9
View File
@@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
@@ -120,6 +117,9 @@ protected:
virtual void ProcessMethod(u32 method, u32 arg) = 0; virtual void ProcessMethod(u32 method, u32 arg) = 0;
Host1x::Host1x& host1x;
Tegra::MemoryManager& memory_manager;
private: private:
/// Process the command entry /// Process the command entry
void ProcessEntries(std::stop_token stop_token); void ProcessEntries(std::stop_token stop_token);
@@ -127,14 +127,14 @@ private:
/// Invoke command class devices to execute the command based on the current state /// Invoke command class devices to execute the command based on the current state
void ExecuteCommand(u32 state_offset, u32 data); void ExecuteCommand(u32 state_offset, u32 data);
protected:
ThiRegisters thi_regs{};
std::deque<ChCommandHeaderList> command_lists;
std::condition_variable_any command_cv;
std::jthread thread;
std::unique_ptr<Host1x::Control> host_processor; std::unique_ptr<Host1x::Control> host_processor;
std::mutex command_mutex; std::mutex command_mutex;
Host1x::Host1x& host1x; std::condition_variable_any command_cv;
std::deque<ChCommandHeaderList> command_lists;
std::jthread thread;
ThiRegisters thi_regs{};
ChClassId current_class; ChClassId current_class;
}; };
+35 -3
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
@@ -122,7 +122,35 @@ void DmaPusher::ProcessCommands(std::span<const CommandHeader> commands) {
dma_state.is_last_call = true; dma_state.is_last_call = true;
index += max_write; index += max_write;
} else if (dma_state.method_count) { } else if (dma_state.method_count) {
auto const command_header = commands[index]; //can copy if (!dma_state.non_incrementing && !dma_increment_once &&
dma_state.method >= non_puller_methods) {
auto subchannel = subchannels[dma_state.subchannel];
const u32 available = u32(std::min<size_t>(
index + dma_state.method_count, commands.size()) - index);
u32 batch = 0;
u32 method = dma_state.method;
while (batch < available) {
const bool needs_exec =
(method < Engines::EngineInterface::EXECUTION_MASK_TABLE_SIZE)
? subchannel->execution_mask[method]
: subchannel->execution_mask_default;
if (needs_exec) break;
batch++;
method++;
}
if (batch > 0) {
auto& sink = subchannel->method_sink;
sink.reserve(sink.size() + batch);
for (u32 j = 0; j < batch; j++) {
sink.emplace_back(dma_state.method + j, commands[index + j].argument);
}
dma_state.method += batch;
dma_state.method_count -= batch;
index += batch;
continue;
}
}
auto const command_header = commands[index];
dma_state.dma_word_offset = u32(index * sizeof(u32)); dma_state.dma_word_offset = u32(index * sizeof(u32));
dma_state.is_last_call = dma_state.method_count <= 1; dma_state.is_last_call = dma_state.method_count <= 1;
CallMethod(command_header.argument); CallMethod(command_header.argument);
@@ -181,7 +209,11 @@ void DmaPusher::CallMethod(u32 argument) const {
}); });
} else { } else {
auto subchannel = subchannels[dma_state.subchannel]; auto subchannel = subchannels[dma_state.subchannel];
if (!subchannel->execution_mask[dma_state.method]) { const bool needs_execution =
(dma_state.method < Engines::EngineInterface::EXECUTION_MASK_TABLE_SIZE)
? subchannel->execution_mask[dma_state.method]
: subchannel->execution_mask_default;
if (!needs_execution) {
subchannel->method_sink.emplace_back(dma_state.method, argument); subchannel->method_sink.emplace_back(dma_state.method, argument);
} else { } else {
subchannel->ConsumeSink(); subchannel->ConsumeSink();
+8 -5
View File
@@ -6,9 +6,9 @@
#pragma once #pragma once
#include <bitset> #include <array>
#include <limits>
#include <vector> #include <boost/container/small_vector.hpp>
#include "common/common_types.h" #include "common/common_types.h"
@@ -41,8 +41,11 @@ public:
ConsumeSinkImpl(); ConsumeSinkImpl();
} }
std::bitset<(std::numeric_limits<u16>::max)()> execution_mask{}; static constexpr size_t EXECUTION_MASK_TABLE_SIZE = 0xE00;
std::vector<std::pair<u32, u32>> method_sink{};
std::array<u8, EXECUTION_MASK_TABLE_SIZE> execution_mask{};
bool execution_mask_default{};
boost::container::small_vector<std::pair<u32, u32>, 64> method_sink{};
bool current_dirty{}; bool current_dirty{};
GPUVAddr current_dma_segment; GPUVAddr current_dma_segment;
+1 -1
View File
@@ -26,7 +26,7 @@ Fermi2D::Fermi2D(MemoryManager& memory_manager_) : memory_manager{memory_manager
regs.src.depth = 1; regs.src.depth = 1;
regs.dst.depth = 1; regs.dst.depth = 1;
execution_mask.reset(); execution_mask.fill(0);
execution_mask[FERMI2D_REG_INDEX(pixels_from_memory.src_y0) + 1] = true; execution_mask[FERMI2D_REG_INDEX(pixels_from_memory.src_y0) + 1] = true;
} }
+1 -1
View File
@@ -18,7 +18,7 @@ namespace Tegra::Engines {
KeplerCompute::KeplerCompute(Core::System& system_, MemoryManager& memory_manager_) KeplerCompute::KeplerCompute(Core::System& system_, MemoryManager& memory_manager_)
: system{system_}, memory_manager{memory_manager_}, upload_state{memory_manager, regs.upload} { : system{system_}, memory_manager{memory_manager_}, upload_state{memory_manager, regs.upload} {
execution_mask.reset(); execution_mask.fill(0);
execution_mask[KEPLER_COMPUTE_REG_INDEX(exec_upload)] = true; execution_mask[KEPLER_COMPUTE_REG_INDEX(exec_upload)] = true;
execution_mask[KEPLER_COMPUTE_REG_INDEX(data_upload)] = true; execution_mask[KEPLER_COMPUTE_REG_INDEX(data_upload)] = true;
execution_mask[KEPLER_COMPUTE_REG_INDEX(launch)] = true; execution_mask[KEPLER_COMPUTE_REG_INDEX(launch)] = true;
+1 -1
View File
@@ -22,7 +22,7 @@ KeplerMemory::~KeplerMemory() = default;
void KeplerMemory::BindRasterizer(VideoCore::RasterizerInterface* rasterizer_) { void KeplerMemory::BindRasterizer(VideoCore::RasterizerInterface* rasterizer_) {
upload_state.BindRasterizer(rasterizer_); upload_state.BindRasterizer(rasterizer_);
execution_mask.reset(); execution_mask.fill(0);
execution_mask[KEPLERMEMORY_REG_INDEX(exec)] = true; execution_mask[KEPLERMEMORY_REG_INDEX(exec)] = true;
execution_mask[KEPLERMEMORY_REG_INDEX(data)] = true; execution_mask[KEPLERMEMORY_REG_INDEX(data)] = true;
} }
+17 -5
View File
@@ -4,8 +4,10 @@
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#include <algorithm>
#include <cstring> #include <cstring>
#include <optional> #include <optional>
#include "common/assert.h" #include "common/assert.h"
#include "common/bit_util.h" #include "common/bit_util.h"
#include "common/scope_exit.h" #include "common/scope_exit.h"
@@ -37,9 +39,10 @@ Maxwell3D::Maxwell3D(Core::System& system_, MemoryManager& memory_manager_)
{ {
dirty.flags.flip(); dirty.flags.flip();
InitializeRegisterDefaults(); InitializeRegisterDefaults();
execution_mask.reset(); execution_mask.fill(0);
for (size_t i = 0; i < execution_mask.size(); i++) for (size_t i = 0; i < EXECUTION_MASK_TABLE_SIZE; i++)
execution_mask[i] = IsMethodExecutable(u32(i)); execution_mask[i] = IsMethodExecutable(u32(i));
execution_mask_default = true;
} }
Maxwell3D::~Maxwell3D() = default; Maxwell3D::~Maxwell3D() = default;
@@ -298,18 +301,27 @@ u32 Maxwell3D::ProcessShadowRam(u32 method, u32 argument) {
} }
void Maxwell3D::ConsumeSinkImpl() { void Maxwell3D::ConsumeSinkImpl() {
std::stable_sort(method_sink.begin(), method_sink.end(),
[](const auto& a, const auto& b) { return a.first < b.first; });
const auto sink_size = method_sink.size();
const auto control = shadow_state.shadow_ram_control; const auto control = shadow_state.shadow_ram_control;
if (control == Regs::ShadowRamControl::Track || control == Regs::ShadowRamControl::TrackWithFilter) { if (control == Regs::ShadowRamControl::Track || control == Regs::ShadowRamControl::TrackWithFilter) {
for (auto [method, value] : method_sink) { for (size_t i = 0; i < sink_size; ++i) {
const auto [method, value] = method_sink[i];
shadow_state.reg_array[method] = value; shadow_state.reg_array[method] = value;
ProcessDirtyRegisters(method, value); ProcessDirtyRegisters(method, value);
} }
} else if (control == Regs::ShadowRamControl::Replay) { } else if (control == Regs::ShadowRamControl::Replay) {
for (auto [method, value] : method_sink) for (size_t i = 0; i < sink_size; ++i) {
const auto [method, value] = method_sink[i];
ProcessDirtyRegisters(method, shadow_state.reg_array[method]); ProcessDirtyRegisters(method, shadow_state.reg_array[method]);
}
} else { } else {
for (auto [method, value] : method_sink) for (size_t i = 0; i < sink_size; ++i) {
const auto [method, value] = method_sink[i];
ProcessDirtyRegisters(method, value); ProcessDirtyRegisters(method, value);
}
} }
method_sink.clear(); method_sink.clear();
} }
+1 -1
View File
@@ -23,7 +23,7 @@ using namespace Texture;
MaxwellDMA::MaxwellDMA(Core::System& system_, MemoryManager& memory_manager_) MaxwellDMA::MaxwellDMA(Core::System& system_, MemoryManager& memory_manager_)
: system{system_}, memory_manager{memory_manager_} { : system{system_}, memory_manager{memory_manager_} {
execution_mask.reset(); execution_mask.fill(0);
execution_mask[offsetof(Regs, launch_dma) / sizeof(u32)] = true; execution_mask[offsetof(Regs, launch_dma) / sizeof(u32)] = true;
} }
+1 -1
View File
@@ -89,7 +89,7 @@ struct GPU::Impl {
renderer = std::move(renderer_); renderer = std::move(renderer_);
rasterizer = renderer->ReadRasterizer(); rasterizer = renderer->ReadRasterizer();
host1x.MemoryManager().BindInterface(rasterizer); host1x.MemoryManager().BindInterface(rasterizer);
host1x.gmmu_manager.BindRasterizer(rasterizer); host1x.GMMU().BindRasterizer(rasterizer);
} }
/// Flush all current written commands into the host GPU for execution. /// Flush all current written commands into the host GPU for execution.
+2 -11
View File
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-FileCopyrightText: Copyright 2025 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,16 +92,7 @@ void ThreadManager::InvalidateRegion(DAddr addr, u64 size) {
} }
void ThreadManager::FlushAndInvalidateRegion(DAddr addr, u64 size) { void ThreadManager::FlushAndInvalidateRegion(DAddr addr, u64 size) {
if (Settings::IsGPULevelHigh()) { // Skip flush on asynch mode, as FlushAndInvalidateRegion is not used for anything too important
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);
} }
+19 -17
View File
@@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
@@ -16,12 +13,9 @@
namespace Tegra { namespace Tegra {
Codec::Codec(Host1x::Host1x& host1x_, const Host1x::NvdecCommon::NvdecRegisters& regs) Codec::Codec(Host1x::Host1x& host1x_, const Host1x::NvdecCommon::NvdecRegisters& regs)
: host1x(host1x_) : host1x(host1x_), state{regs}, h264_decoder(std::make_unique<Decoder::H264>(host1x)),
, state{regs} vp8_decoder(std::make_unique<Decoder::VP8>(host1x)),
, h264_decoder(host1x_) vp9_decoder(std::make_unique<Decoder::VP9>(host1x)) {}
, vp8_decoder(host1x_)
, vp9_decoder(host1x_)
{}
Codec::~Codec() = default; Codec::~Codec() = default;
@@ -38,11 +32,13 @@ void Codec::SetTargetCodec(Host1x::NvdecCommon::VideoCodec codec) {
void Codec::Decode() { void Codec::Decode() {
const bool is_first_frame = !initialized; const bool is_first_frame = !initialized;
if (is_first_frame) if (is_first_frame) {
Initialize(); Initialize();
}
if (!initialized) if (!initialized) {
return; return;
}
// Assemble bitstream. // Assemble bitstream.
bool vp9_hidden_frame = false; bool vp9_hidden_frame = false;
@@ -50,13 +46,13 @@ void Codec::Decode() {
const auto packet_data = [&]() { const auto packet_data = [&]() {
switch (current_codec) { switch (current_codec) {
case Tegra::Host1x::NvdecCommon::VideoCodec::H264: case Tegra::Host1x::NvdecCommon::VideoCodec::H264:
return h264_decoder.ComposeFrame(state, &configuration_size, is_first_frame); return h264_decoder->ComposeFrame(state, &configuration_size, is_first_frame);
case Tegra::Host1x::NvdecCommon::VideoCodec::VP8: case Tegra::Host1x::NvdecCommon::VideoCodec::VP8:
return vp8_decoder.ComposeFrame(state); return vp8_decoder->ComposeFrame(state);
case Tegra::Host1x::NvdecCommon::VideoCodec::VP9: case Tegra::Host1x::NvdecCommon::VideoCodec::VP9:
vp9_decoder.ComposeFrame(state); vp9_decoder->ComposeFrame(state);
vp9_hidden_frame = vp9_decoder.WasFrameHidden(); vp9_hidden_frame = vp9_decoder->WasFrameHidden();
return vp9_decoder.GetFrameBytes(); return vp9_decoder->GetFrameBytes();
default: default:
ASSERT(false); ASSERT(false);
return std::span<const u8>{}; return std::span<const u8>{};
@@ -85,13 +81,19 @@ void Codec::Decode() {
std::unique_ptr<FFmpeg::Frame> Codec::GetCurrentFrame() { std::unique_ptr<FFmpeg::Frame> Codec::GetCurrentFrame() {
// Sometimes VIC will request more frames than have been decoded. // Sometimes VIC will request more frames than have been decoded.
// in this case, return a blank frame and don't overwrite previous data. // in this case, return a blank frame and don't overwrite previous data.
if (frames.empty()) if (frames.empty()) {
return {}; return {};
}
auto frame = std::move(frames.front()); auto frame = std::move(frames.front());
frames.pop(); frames.pop();
return frame; return frame;
} }
Host1x::NvdecCommon::VideoCodec Codec::GetCurrentCodec() const {
return current_codec;
}
std::string_view Codec::GetCurrentCodecName() const { std::string_view Codec::GetCurrentCodecName() const {
switch (current_codec) { switch (current_codec) {
case Host1x::NvdecCommon::VideoCodec::None: case Host1x::NvdecCommon::VideoCodec::None:
+14 -10
View File
@@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
@@ -11,14 +8,17 @@
#include <string_view> #include <string_view>
#include <queue> #include <queue>
#include "common/common_types.h" #include "common/common_types.h"
#include "video_core/host1x/codecs/h264.h" #include "video_core/host1x/ffmpeg/ffmpeg.h"
#include "video_core/host1x/codecs/vp8.h"
#include "video_core/host1x/codecs/vp9.h"
#include "video_core/host1x/ffmpeg.h"
#include "video_core/host1x/nvdec_common.h" #include "video_core/host1x/nvdec_common.h"
namespace Tegra { namespace Tegra {
namespace Decoder {
class H264;
class VP8;
class VP9;
} // namespace Decoder
namespace Host1x { namespace Host1x {
class Host1x; class Host1x;
} // namespace Host1x } // namespace Host1x
@@ -40,6 +40,9 @@ public:
/// Returns next decoded frame /// Returns next decoded frame
[[nodiscard]] std::unique_ptr<FFmpeg::Frame> GetCurrentFrame(); [[nodiscard]] std::unique_ptr<FFmpeg::Frame> GetCurrentFrame();
/// Returns the value of current_codec
[[nodiscard]] Host1x::NvdecCommon::VideoCodec GetCurrentCodec() const;
/// Return name of the current codec /// Return name of the current codec
[[nodiscard]] std::string_view GetCurrentCodecName() const; [[nodiscard]] std::string_view GetCurrentCodecName() const;
@@ -50,9 +53,10 @@ private:
Host1x::Host1x& host1x; Host1x::Host1x& host1x;
const Host1x::NvdecCommon::NvdecRegisters& state; const Host1x::NvdecCommon::NvdecRegisters& state;
Decoders::H264 h264_decoder; std::unique_ptr<Decoder::H264> h264_decoder;
Decoders::VP8 vp8_decoder; std::unique_ptr<Decoder::VP8> vp8_decoder;
Decoders::VP9 vp9_decoder; std::unique_ptr<Decoder::VP9> vp9_decoder;
std::queue<std::unique_ptr<FFmpeg::Frame>> frames{}; std::queue<std::unique_ptr<FFmpeg::Frame>> frames{};
}; };
+11 -12
View File
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
@@ -12,11 +12,10 @@
namespace Tegra { namespace Tegra {
Decoder::Decoder(Host1x::Host1x& host1x_, s32 id_, const Host1x::NvdecCommon::NvdecRegisters& regs_) Decoder::Decoder(Host1x::Host1x& host1x_, s32 id_, const Host1x::NvdecCommon::NvdecRegisters& regs_,
: host1x(host1x_) Host1x::FrameQueue& frame_queue_)
, regs{regs_} : host1x(host1x_), memory_manager{host1x.GMMU()}, regs{regs_}, id{id_}, frame_queue{
, id{id_} frame_queue_} {}
{}
Decoder::~Decoder() = default; Decoder::~Decoder() = default;
@@ -54,11 +53,11 @@ void Decoder::Decode() {
} }
if (UsingDecodeOrder()) { if (UsingDecodeOrder()) {
host1x.frame_queue.PushDecodeOrder(id, luma_top, std::move(frame)); frame_queue.PushDecodeOrder(id, luma_top, std::move(frame));
host1x.frame_queue.PushDecodeOrder(id, luma_bottom, std::move(frame_copy)); frame_queue.PushDecodeOrder(id, luma_bottom, std::move(frame_copy));
} else { } else {
host1x.frame_queue.PushPresentOrder(id, luma_top, std::move(frame)); frame_queue.PushPresentOrder(id, luma_top, std::move(frame));
host1x.frame_queue.PushPresentOrder(id, luma_bottom, std::move(frame_copy)); frame_queue.PushPresentOrder(id, luma_bottom, std::move(frame_copy));
} }
} else { } else {
auto [luma_offset, chroma_offset] = GetProgressiveOffsets(); auto [luma_offset, chroma_offset] = GetProgressiveOffsets();
@@ -69,9 +68,9 @@ void Decoder::Decode() {
} }
if (UsingDecodeOrder()) { if (UsingDecodeOrder()) {
host1x.frame_queue.PushDecodeOrder(id, luma_offset, std::move(frame)); frame_queue.PushDecodeOrder(id, luma_offset, std::move(frame));
} else { } else {
host1x.frame_queue.PushPresentOrder(id, luma_offset, std::move(frame)); frame_queue.PushPresentOrder(id, luma_offset, std::move(frame));
} }
} }
} }
+15 -5
View File
@@ -14,7 +14,7 @@
#include <queue> #include <queue>
#include "common/common_types.h" #include "common/common_types.h"
#include "video_core/host1x/ffmpeg.h" #include "video_core/host1x/ffmpeg/ffmpeg.h"
#include "video_core/host1x/nvdec_common.h" #include "video_core/host1x/nvdec_common.h"
namespace Tegra { namespace Tegra {
@@ -35,23 +35,33 @@ public:
return decode_api.UsingDecodeOrder(); return decode_api.UsingDecodeOrder();
} }
/// Returns the value of current_codec
[[nodiscard]] Host1x::NvdecCommon::VideoCodec GetCurrentCodec() const {
return codec;
}
/// Return name of the current codec /// Return name of the current codec
[[nodiscard]] virtual std::string_view GetCurrentCodecName() const = 0; [[nodiscard]] virtual std::string_view GetCurrentCodecName() const = 0;
protected: protected:
explicit Decoder(Host1x::Host1x& host1x, s32 id, const Host1x::NvdecCommon::NvdecRegisters& regs); explicit Decoder(Host1x::Host1x& host1x, s32 id,
const Host1x::NvdecCommon::NvdecRegisters& regs,
Host1x::FrameQueue& frame_queue);
virtual std::span<const u8> ComposeFrame() = 0; virtual std::span<const u8> ComposeFrame() = 0;
virtual std::tuple<u64, u64> GetProgressiveOffsets() = 0; virtual std::tuple<u64, u64> GetProgressiveOffsets() = 0;
virtual std::tuple<u64, u64, u64, u64> GetInterlacedOffsets() = 0; virtual std::tuple<u64, u64, u64, u64> GetInterlacedOffsets() = 0;
virtual bool IsInterlaced() = 0; virtual bool IsInterlaced() = 0;
FFmpeg::DecodeApi decode_api;
Host1x::Host1x& host1x; Host1x::Host1x& host1x;
Tegra::MemoryManager& memory_manager;
const Host1x::NvdecCommon::NvdecRegisters& regs; const Host1x::NvdecCommon::NvdecRegisters& regs;
s32 id; s32 id;
bool initialized : 1 = false; Host1x::FrameQueue& frame_queue;
bool vp9_hidden_frame : 1 = false; Host1x::NvdecCommon::VideoCodec codec;
FFmpeg::DecodeApi decode_api;
bool initialized{};
bool vp9_hidden_frame{};
}; };
} // namespace Tegra } // namespace Tegra
+55 -42
View File
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
@@ -14,11 +14,25 @@
#include "video_core/memory_manager.h" #include "video_core/memory_manager.h"
namespace Tegra::Decoders { namespace Tegra::Decoders {
namespace {
// ZigZag LUTs from libavcodec.
constexpr std::array<u8, 64> zig_zag_direct{
0, 1, 8, 16, 9, 2, 3, 10, 17, 24, 32, 25, 18, 11, 4, 5, 12, 19, 26, 33, 40, 48,
41, 34, 27, 20, 13, 6, 7, 14, 21, 28, 35, 42, 49, 56, 57, 50, 43, 36, 29, 22, 15, 23,
30, 37, 44, 51, 58, 59, 52, 45, 38, 31, 39, 46, 53, 60, 61, 54, 47, 55, 62, 63,
};
H264::H264(Host1x::Host1x& host1x_, const Host1x::NvdecCommon::NvdecRegisters& regs_, s32 id_) constexpr std::array<u8, 16> zig_zag_scan{
: Decoder{host1x_, id_, regs_} 0 + 0 * 4, 1 + 0 * 4, 0 + 1 * 4, 0 + 2 * 4, 1 + 1 * 4, 2 + 0 * 4, 3 + 0 * 4, 2 + 1 * 4,
{ 1 + 2 * 4, 0 + 3 * 4, 1 + 3 * 4, 2 + 2 * 4, 3 + 1 * 4, 3 + 2 * 4, 2 + 3 * 4, 3 + 3 * 4,
initialized = decode_api.Initialize(Host1x::NvdecCommon::VideoCodec::H264); };
} // Anonymous namespace
H264::H264(Host1x::Host1x& host1x_, const Host1x::NvdecCommon::NvdecRegisters& regs_, s32 id_,
Host1x::FrameQueue& frame_queue_)
: Decoder{host1x_, id_, regs_, frame_queue_} {
codec = Host1x::NvdecCommon::VideoCodec::H264;
initialized = decode_api.Initialize(codec);
} }
H264::~H264() = default; H264::~H264() = default;
@@ -51,11 +65,14 @@ bool H264::IsInterlaced() {
} }
std::span<const u8> H264::ComposeFrame() { std::span<const u8> H264::ComposeFrame() {
host1x.memory_manager.ReadBlock(regs.picture_info_offset.Address(), &current_context, sizeof(H264DecoderContext)); memory_manager.ReadBlock(regs.picture_info_offset.Address(), &current_context,
sizeof(H264DecoderContext));
const s64 frame_number = current_context.h264_parameter_set.frame_number.Value(); const s64 frame_number = current_context.h264_parameter_set.frame_number.Value();
if (!is_first_frame && frame_number != 0) { if (!is_first_frame && frame_number != 0) {
frame_scratch.resize_destructive(current_context.stream_len); frame_scratch.resize_destructive(current_context.stream_len);
host1x.memory_manager.ReadBlock(regs.frame_bitstream_offset.Address(), frame_scratch.data(), frame_scratch.size()); memory_manager.ReadBlock(regs.frame_bitstream_offset.Address(), frame_scratch.data(),
frame_scratch.size());
return frame_scratch; return frame_scratch;
} }
@@ -157,13 +174,15 @@ std::span<const u8> H264::ComposeFrame() {
for (s32 index = 0; index < 6; index++) { for (s32 index = 0; index < 6; index++) {
writer.WriteBit(true); writer.WriteBit(true);
writer.WriteScalingList(current_context.weight_scale_4x4, index * 16, 16); std::span<const u8> matrix{current_context.weight_scale_4x4};
writer.WriteScalingList(scan_scratch, matrix, index * 16, 16);
} }
if (current_context.h264_parameter_set.transform_8x8_mode_flag) { if (current_context.h264_parameter_set.transform_8x8_mode_flag) {
for (s32 index = 0; index < 2; index++) { for (s32 index = 0; index < 2; index++) {
writer.WriteBit(true); writer.WriteBit(true);
writer.WriteScalingList(current_context.weight_scale_8x8, index * 64, 64); std::span<const u8> matrix{current_context.weight_scale_8x8};
writer.WriteScalingList(scan_scratch, matrix, index * 64, 64);
} }
} }
@@ -177,7 +196,11 @@ std::span<const u8> H264::ComposeFrame() {
const auto& encoded_header = writer.GetByteArray(); const auto& encoded_header = writer.GetByteArray();
frame_scratch.resize(encoded_header.size() + current_context.stream_len); frame_scratch.resize(encoded_header.size() + current_context.stream_len);
std::memcpy(frame_scratch.data(), encoded_header.data(), encoded_header.size()); std::memcpy(frame_scratch.data(), encoded_header.data(), encoded_header.size());
host1x.memory_manager.ReadBlock(regs.frame_bitstream_offset.Address(), frame_scratch.data() + encoded_header.size(), current_context.stream_len);
memory_manager.ReadBlock(regs.frame_bitstream_offset.Address(),
frame_scratch.data() + encoded_header.size(),
current_context.stream_len);
return frame_scratch; return frame_scratch;
} }
@@ -206,37 +229,23 @@ void H264BitWriter::WriteBit(bool state) {
WriteBits(state ? 1 : 0, 1); WriteBits(state ? 1 : 0, 1);
} }
void H264BitWriter::WriteScalingList(std::span<const u8> list, s32 start, s32 count) { void H264BitWriter::WriteScalingList(Common::ScratchBuffer<u8>& scan, std::span<const u8> list,
s32 start, s32 count) {
scan.resize_destructive(count);
if (count == 16) { if (count == 16) {
u8 last_scale = 8; std::memcpy(scan.data(), zig_zag_scan.data(), scan.size());
for (s32 index = 0; index < count; index++) {
// libavcodec has a zig zag LUT, but we dont need it, just use a magic
// constant which is a packing of 4 bits for each component of the table
const u8 value = list[start + ((0xfeb7adc963258410 >> (index * 4)) & 0xf)];
const s32 delta_scale = s32(value - last_scale);
WriteSe(delta_scale);
last_scale = value;
}
} else { } else {
// ZigZag LUTs from libavcodec: this is the famous zigzag pattern found in the ffmpeg logo itself! std::memcpy(scan.data(), zig_zag_direct.data(), scan.size());
static constexpr std::array<u8, 64> scan{ }
0, 1, 8, 16, 9, 2, 3, 10, u8 last_scale = 8;
17, 24, 32, 25, 18, 11, 4,
5, 12, 19, 26, 33, 40, 48, for (s32 index = 0; index < count; index++) {
41, 34, 27, 20, 13, 6, 7, const u8 value = list[start + scan[index]];
14, 21, 28, 35, 42, 49, 56, const s32 delta_scale = static_cast<s32>(value - last_scale);
57, 50, 43, 36, 29, 22, 15,
23, 30, 37, 44, 51, 58, 59, WriteSe(delta_scale);
52, 45, 38, 31, 39, 46, 53,
60, 61, 54, 47, 55, 62, 63, last_scale = value;
};
u8 last_scale = 8;
for (s32 index = 0; index < count; index++) {
const u8 value = list[start + scan[index]];
const s32 delta_scale = s32(value - last_scale);
WriteSe(delta_scale);
last_scale = value;
}
} }
} }
@@ -277,15 +286,19 @@ void H264BitWriter::WriteBits(s32 value, s32 bit_count) {
void H264BitWriter::WriteExpGolombCodedInt(s32 value) { void H264BitWriter::WriteExpGolombCodedInt(s32 value) {
const s32 sign = value <= 0 ? 0 : 1; const s32 sign = value <= 0 ? 0 : 1;
if (!sign) value = -value; if (value < 0) {
WriteExpGolombCodedUInt((value << 1) - sign); value = -value;
}
value = (value << 1) - sign;
WriteExpGolombCodedUInt(value);
} }
void H264BitWriter::WriteExpGolombCodedUInt(u32 value) { void H264BitWriter::WriteExpGolombCodedUInt(u32 value) {
const s32 size = 32 - std::countl_zero(value + 1); const s32 size = 32 - std::countl_zero(value + 1);
WriteBits(1, size); WriteBits(1, size);
value -= (1U << (size - 1)) - 1; value -= (1U << (size - 1)) - 1;
WriteBits(s32(value), size - 1); WriteBits(static_cast<s32>(value), size - 1);
} }
s32 H264BitWriter::GetFreeBufferBits() { s32 H264BitWriter::GetFreeBufferBits() {
+187 -9
View File
@@ -1,6 +1,3 @@
// 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
@@ -9,10 +6,11 @@
#include <span> #include <span>
#include <vector> #include <vector>
#include "common/bit_field.h"
#include "common/common_funcs.h"
#include "common/common_types.h" #include "common/common_types.h"
#include "common/scratch_buffer.h" #include "common/scratch_buffer.h"
#include "video_core/host1x/codecs/decoder.h" #include "video_core/host1x/codecs/decoder.h"
#include "video_core/host1x/codec_types.h"
#include "video_core/host1x/nvdec_common.h" #include "video_core/host1x/nvdec_common.h"
namespace Tegra { namespace Tegra {
@@ -42,7 +40,8 @@ public:
/// Based on section 7.3.2.1.1.1 and Table 7-4 in the H.264 specification /// Based on section 7.3.2.1.1.1 and Table 7-4 in the H.264 specification
/// Writes the scaling matrices of the sream /// Writes the scaling matrices of the sream
void WriteScalingList(std::span<const u8> list, s32 start, s32 count); void WriteScalingList(Common::ScratchBuffer<u8>& scan, std::span<const u8> list, s32 start,
s32 count);
/// Return the bitstream as a vector. /// Return the bitstream as a vector.
[[nodiscard]] std::vector<u8>& GetByteArray(); [[nodiscard]] std::vector<u8>& GetByteArray();
@@ -62,9 +61,188 @@ private:
std::vector<u8> byte_array; std::vector<u8> byte_array;
}; };
struct Offset {
constexpr u32 Address() const noexcept {
return offset << 8;
}
private:
u32 offset;
};
static_assert(std::is_trivial_v<Offset>, "Offset must be trivial");
static_assert(sizeof(Offset) == 0x4, "Offset has the wrong size!");
struct H264ParameterSet {
s32 log2_max_pic_order_cnt_lsb_minus4; ///< 0x00
s32 delta_pic_order_always_zero_flag; ///< 0x04
s32 frame_mbs_only_flag; ///< 0x08
u32 pic_width_in_mbs; ///< 0x0C
u32 frame_height_in_mbs; ///< 0x10
union { ///< 0x14
BitField<0, 2, u32> tile_format;
BitField<2, 3, u32> gob_height;
BitField<5, 27, u32> reserved_surface_format;
};
u32 entropy_coding_mode_flag; ///< 0x18
s32 pic_order_present_flag; ///< 0x1C
s32 num_refidx_l0_default_active; ///< 0x20
s32 num_refidx_l1_default_active; ///< 0x24
s32 deblocking_filter_control_present_flag; ///< 0x28
s32 redundant_pic_cnt_present_flag; ///< 0x2C
u32 transform_8x8_mode_flag; ///< 0x30
u32 pitch_luma; ///< 0x34
u32 pitch_chroma; ///< 0x38
Offset luma_top_offset; ///< 0x3C
Offset luma_bot_offset; ///< 0x40
Offset luma_frame_offset; ///< 0x44
Offset chroma_top_offset; ///< 0x48
Offset chroma_bot_offset; ///< 0x4C
Offset chroma_frame_offset; ///< 0x50
u32 hist_buffer_size; ///< 0x54
union { ///< 0x58
union {
BitField<0, 1, u64> mbaff_frame;
BitField<1, 1, u64> direct_8x8_inference;
BitField<2, 1, u64> weighted_pred;
BitField<3, 1, u64> constrained_intra_pred;
BitField<4, 1, u64> ref_pic;
BitField<5, 1, u64> field_pic;
BitField<6, 1, u64> bottom_field;
BitField<7, 1, u64> second_field;
} flags;
BitField<8, 4, u64> log2_max_frame_num_minus4;
BitField<12, 2, u64> chroma_format_idc;
BitField<14, 2, u64> pic_order_cnt_type;
BitField<16, 6, s64> pic_init_qp_minus26;
BitField<22, 5, s64> chroma_qp_index_offset;
BitField<27, 5, s64> second_chroma_qp_index_offset;
BitField<32, 2, u64> weighted_bipred_idc;
BitField<34, 7, u64> curr_pic_idx;
BitField<41, 5, u64> curr_col_idx;
BitField<46, 16, u64> frame_number;
BitField<62, 1, u64> frame_surfaces;
BitField<63, 1, u64> output_memory_layout;
};
};
static_assert(sizeof(H264ParameterSet) == 0x60, "H264ParameterSet is an invalid size");
#define ASSERT_POSITION(field_name, position) \
static_assert(offsetof(H264ParameterSet, field_name) == position, \
"Field " #field_name " has invalid position")
ASSERT_POSITION(log2_max_pic_order_cnt_lsb_minus4, 0x00);
ASSERT_POSITION(delta_pic_order_always_zero_flag, 0x04);
ASSERT_POSITION(frame_mbs_only_flag, 0x08);
ASSERT_POSITION(pic_width_in_mbs, 0x0C);
ASSERT_POSITION(frame_height_in_mbs, 0x10);
ASSERT_POSITION(tile_format, 0x14);
ASSERT_POSITION(entropy_coding_mode_flag, 0x18);
ASSERT_POSITION(pic_order_present_flag, 0x1C);
ASSERT_POSITION(num_refidx_l0_default_active, 0x20);
ASSERT_POSITION(num_refidx_l1_default_active, 0x24);
ASSERT_POSITION(deblocking_filter_control_present_flag, 0x28);
ASSERT_POSITION(redundant_pic_cnt_present_flag, 0x2C);
ASSERT_POSITION(transform_8x8_mode_flag, 0x30);
ASSERT_POSITION(pitch_luma, 0x34);
ASSERT_POSITION(pitch_chroma, 0x38);
ASSERT_POSITION(luma_top_offset, 0x3C);
ASSERT_POSITION(luma_bot_offset, 0x40);
ASSERT_POSITION(luma_frame_offset, 0x44);
ASSERT_POSITION(chroma_top_offset, 0x48);
ASSERT_POSITION(chroma_bot_offset, 0x4C);
ASSERT_POSITION(chroma_frame_offset, 0x50);
ASSERT_POSITION(hist_buffer_size, 0x54);
ASSERT_POSITION(flags, 0x58);
#undef ASSERT_POSITION
struct DpbEntry {
union {
BitField<0, 7, u32> index;
BitField<7, 5, u32> col_idx;
BitField<12, 2, u32> state;
BitField<14, 1, u32> is_long_term;
BitField<15, 1, u32> non_existing;
BitField<16, 1, u32> is_field;
BitField<17, 4, u32> top_field_marking;
BitField<21, 4, u32> bottom_field_marking;
BitField<25, 1, u32> output_memory_layout;
BitField<26, 6, u32> reserved;
} flags;
std::array<u32, 2> field_order_cnt;
u32 frame_idx;
};
static_assert(sizeof(DpbEntry) == 0x10, "DpbEntry has the wrong size!");
struct DisplayParam {
union {
BitField<0, 1, u32> enable_tf_output;
BitField<1, 1, u32> vc1_map_y_flag;
BitField<2, 3, u32> map_y_value;
BitField<5, 1, u32> vc1_map_uv_flag;
BitField<6, 3, u32> map_uv_value;
BitField<9, 8, u32> out_stride;
BitField<17, 3, u32> tiling_format;
BitField<20, 1, u32> output_structure; // 0=frame, 1=field
BitField<21, 11, u32> reserved0;
};
std::array<s32, 2> output_top;
std::array<s32, 2> output_bottom;
union {
BitField<0, 1, u32> enable_histogram;
BitField<1, 12, u32> histogram_start_x;
BitField<13, 12, u32> histogram_start_y;
BitField<25, 7, u32> reserved1;
};
union {
BitField<0, 12, u32> histogram_end_x;
BitField<12, 12, u32> histogram_end_y;
BitField<24, 8, u32> reserved2;
};
};
static_assert(sizeof(DisplayParam) == 0x1C, "DisplayParam has the wrong size!");
struct H264DecoderContext {
INSERT_PADDING_WORDS_NOINIT(13); ///< 0x0000
std::array<u8, 16> eos; ///< 0x0034
u8 explicit_eos_present_flag; ///< 0x0044
u8 hint_dump_en; ///< 0x0045
INSERT_PADDING_BYTES_NOINIT(2); ///< 0x0046
u32 stream_len; ///< 0x0048
u32 slice_count; ///< 0x004C
u32 mbhist_buffer_size; ///< 0x0050
u32 gptimer_timeout_value; ///< 0x0054
H264ParameterSet h264_parameter_set; ///< 0x0058
std::array<s32, 2> curr_field_order_cnt; ///< 0x00B8
std::array<DpbEntry, 16> dpb; ///< 0x00C0
std::array<u8, 0x60> weight_scale_4x4; ///< 0x01C0
std::array<u8, 0x80> weight_scale_8x8; ///< 0x0220
std::array<u8, 2> num_inter_view_refs_lX; ///< 0x02A0
std::array<u8, 14> reserved2; ///< 0x02A2
std::array<std::array<s8, 16>, 2> inter_view_refidx_lX; ///< 0x02B0
union { ///< 0x02D0
BitField<0, 1, u32> lossless_ipred8x8_filter_enable;
BitField<1, 1, u32> qpprime_y_zero_transform_bypass_flag;
BitField<2, 30, u32> reserved3;
};
DisplayParam display_param; ///< 0x02D4
std::array<u32, 3> reserved4; ///< 0x02F0
};
static_assert(sizeof(H264DecoderContext) == 0x2FC, "H264DecoderContext is an invalid size");
#define ASSERT_POSITION(field_name, position) \
static_assert(offsetof(H264DecoderContext, field_name) == position, \
"Field " #field_name " has invalid position")
ASSERT_POSITION(stream_len, 0x48);
ASSERT_POSITION(h264_parameter_set, 0x58);
ASSERT_POSITION(dpb, 0xC0);
ASSERT_POSITION(weight_scale_4x4, 0x1C0);
#undef ASSERT_POSITION
class H264 final : public Decoder { class H264 final : public Decoder {
public: public:
explicit H264(Host1x::Host1x& host1x, const Host1x::NvdecCommon::NvdecRegisters& regs, s32 id); explicit H264(Host1x::Host1x& host1x, const Host1x::NvdecCommon::NvdecRegisters& regs, s32 id,
Host1x::FrameQueue& frame_queue);
~H264() override; ~H264() override;
H264(const H264&) = delete; H264(const H264&) = delete;
@@ -85,10 +263,10 @@ public:
} }
private: private:
H264DecoderContext current_context{};
std::array<u8, 64> scan_scratch;
Common::ScratchBuffer<u8> frame_scratch;
bool is_first_frame{true}; bool is_first_frame{true};
Common::ScratchBuffer<u8> frame_scratch;
Common::ScratchBuffer<u8> scan_scratch;
H264DecoderContext current_context{};
}; };
} // namespace Decoders } // namespace Decoders
+26 -23
View File
@@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
@@ -11,10 +8,11 @@
#include "video_core/memory_manager.h" #include "video_core/memory_manager.h"
namespace Tegra::Decoders { namespace Tegra::Decoders {
VP8::VP8(Host1x::Host1x& host1x_, const Host1x::NvdecCommon::NvdecRegisters& regs_, s32 id_) VP8::VP8(Host1x::Host1x& host1x_, const Host1x::NvdecCommon::NvdecRegisters& regs_, s32 id_,
: Decoder{host1x_, id_, regs_} Host1x::FrameQueue& frame_queue_)
{ : Decoder{host1x_, id_, regs_, frame_queue_} {
initialized = decode_api.Initialize(Host1x::NvdecCommon::VideoCodec::VP8); codec = Host1x::NvdecCommon::VideoCodec::VP8;
initialized = decode_api.Initialize(codec);
} }
VP8::~VP8() = default; VP8::~VP8() = default;
@@ -27,30 +25,35 @@ std::tuple<u64, u64> VP8::GetProgressiveOffsets() {
std::tuple<u64, u64, u64, u64> VP8::GetInterlacedOffsets() { std::tuple<u64, u64, u64, u64> VP8::GetInterlacedOffsets() {
auto luma_top{regs.surface_luma_offsets[static_cast<u32>(Vp8SurfaceIndex::Current)].Address()}; auto luma_top{regs.surface_luma_offsets[static_cast<u32>(Vp8SurfaceIndex::Current)].Address()};
auto luma_bottom = regs.surface_luma_offsets[u32(Vp8SurfaceIndex::Current)].Address(); auto luma_bottom{
auto chroma_top = regs.surface_chroma_offsets[u32(Vp8SurfaceIndex::Current)].Address(); regs.surface_luma_offsets[static_cast<u32>(Vp8SurfaceIndex::Current)].Address()};
auto chroma_bottom = regs.surface_chroma_offsets[u32(Vp8SurfaceIndex::Current)].Address(); auto chroma_top{
regs.surface_chroma_offsets[static_cast<u32>(Vp8SurfaceIndex::Current)].Address()};
auto chroma_bottom{
regs.surface_chroma_offsets[static_cast<u32>(Vp8SurfaceIndex::Current)].Address()};
return {luma_top, luma_bottom, chroma_top, chroma_bottom}; return {luma_top, luma_bottom, chroma_top, chroma_bottom};
} }
std::span<const u8> VP8::ComposeFrame() { std::span<const u8> VP8::ComposeFrame() {
host1x.gmmu_manager.ReadBlock(regs.picture_info_offset.Address(), &current_context, sizeof(VP8PictureInfo)); memory_manager.ReadBlock(regs.picture_info_offset.Address(), &current_context,
sizeof(VP8PictureInfo));
const bool is_key_frame = current_context.key_frame == 1u; const bool is_key_frame = current_context.key_frame == 1u;
const auto bitstream_size = size_t(current_context.vld_buffer_size); const auto bitstream_size = static_cast<size_t>(current_context.vld_buffer_size);
const size_t header_size = is_key_frame ? 10u : 3u; const size_t header_size = is_key_frame ? 10u : 3u;
frame_scratch.resize(header_size + bitstream_size); frame_scratch.resize(header_size + bitstream_size);
// Based on page 30 of the VP8 specification. // Based on page 30 of the VP8 specification.
// https://datatracker.ietf.org/doc/rfc6386/ // https://datatracker.ietf.org/doc/rfc6386/
frame_scratch[0] = is_key_frame ? 0u : 1u; // 1-bit frame type (0: keyframe, 1: interframes). frame_scratch[0] = is_key_frame ? 0u : 1u; // 1-bit frame type (0: keyframe, 1: interframes).
frame_scratch[0] |= u8((current_context.version & 7u) << 1u); // 3-bit version number frame_scratch[0] |=
frame_scratch[0] |= u8(1u << 4u); // 1-bit show_frame flag static_cast<u8>((current_context.version & 7u) << 1u); // 3-bit version number
frame_scratch[0] |= static_cast<u8>(1u << 4u); // 1-bit show_frame flag
// The next 19-bits are the first partition size // The next 19-bits are the first partition size
frame_scratch[0] |= u8((current_context.first_part_size & 7u) << 5u); frame_scratch[0] |= static_cast<u8>((current_context.first_part_size & 7u) << 5u);
frame_scratch[1] = u8((current_context.first_part_size & 0x7f8u) >> 3u); frame_scratch[1] = static_cast<u8>((current_context.first_part_size & 0x7f8u) >> 3u);
frame_scratch[2] = u8((current_context.first_part_size & 0x7f800u) >> 11u); frame_scratch[2] = static_cast<u8>((current_context.first_part_size & 0x7f800u) >> 11u);
if (is_key_frame) { if (is_key_frame) {
frame_scratch[3] = 0x9du; frame_scratch[3] = 0x9du;
@@ -58,15 +61,15 @@ std::span<const u8> VP8::ComposeFrame() {
frame_scratch[5] = 0x2au; frame_scratch[5] = 0x2au;
// TODO(ameerj): Horizontal/Vertical Scale // TODO(ameerj): Horizontal/Vertical Scale
// 16 bits: (2 bits Horizontal Scale << 14) | Width (14 bits) // 16 bits: (2 bits Horizontal Scale << 14) | Width (14 bits)
frame_scratch[6] = u8(current_context.frame_width & 0xff); frame_scratch[6] = static_cast<u8>(current_context.frame_width & 0xff);
frame_scratch[7] = u8(((current_context.frame_width >> 8) & 0x3f)); frame_scratch[7] = static_cast<u8>(((current_context.frame_width >> 8) & 0x3f));
// 16 bits:(2 bits Vertical Scale << 14) | Height (14 bits) // 16 bits:(2 bits Vertical Scale << 14) | Height (14 bits)
frame_scratch[8] = u8(current_context.frame_height & 0xff); frame_scratch[8] = static_cast<u8>(current_context.frame_height & 0xff);
frame_scratch[9] = u8(((current_context.frame_height >> 8) & 0x3f)); frame_scratch[9] = static_cast<u8>(((current_context.frame_height >> 8) & 0x3f));
} }
const u64 bitstream_offset = regs.frame_bitstream_offset.Address(); const u64 bitstream_offset = regs.frame_bitstream_offset.Address();
host1x.gmmu_manager.ReadBlock(bitstream_offset, frame_scratch.data() + header_size, bitstream_size); memory_manager.ReadBlock(bitstream_offset, frame_scratch.data() + header_size, bitstream_size);
return frame_scratch; return frame_scratch;
} }
+46 -6
View File
@@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
@@ -14,7 +11,6 @@
#include "common/scratch_buffer.h" #include "common/scratch_buffer.h"
#include "video_core/host1x/codecs/decoder.h" #include "video_core/host1x/codecs/decoder.h"
#include "video_core/host1x/nvdec_common.h" #include "video_core/host1x/nvdec_common.h"
#include "video_core/host1x/codec_types.h"
namespace Tegra { namespace Tegra {
@@ -32,7 +28,8 @@ enum class Vp8SurfaceIndex : u32 {
class VP8 final : public Decoder { class VP8 final : public Decoder {
public: public:
explicit VP8(Host1x::Host1x& host1x, const Host1x::NvdecCommon::NvdecRegisters& regs, s32 id); explicit VP8(Host1x::Host1x& host1x, const Host1x::NvdecCommon::NvdecRegisters& regs, s32 id,
Host1x::FrameQueue& frame_queue);
~VP8() override; ~VP8() override;
VP8(const VP8&) = delete; VP8(const VP8&) = delete;
@@ -55,8 +52,51 @@ public:
} }
private: private:
VP8PictureInfo current_context{};
Common::ScratchBuffer<u8> frame_scratch; Common::ScratchBuffer<u8> frame_scratch;
struct VP8PictureInfo {
INSERT_PADDING_WORDS_NOINIT(14);
u16 frame_width; // actual frame width
u16 frame_height; // actual frame height
u8 key_frame;
u8 version;
union {
u8 raw;
BitField<0, 2, u8> tile_format;
BitField<2, 3, u8> gob_height;
BitField<5, 3, u8> reserved_surface_format;
};
u8 error_conceal_on; // 1: error conceal on; 0: off
u32 first_part_size; // the size of first partition(frame header and mb header partition)
u32 hist_buffer_size; // in units of 256
u32 vld_buffer_size; // in units of 1
// Current frame buffers
std::array<u32, 2> frame_stride; // [y_c]
u32 luma_top_offset; // offset of luma top field in units of 256
u32 luma_bot_offset; // offset of luma bottom field in units of 256
u32 luma_frame_offset; // offset of luma frame in units of 256
u32 chroma_top_offset; // offset of chroma top field in units of 256
u32 chroma_bot_offset; // offset of chroma bottom field in units of 256
u32 chroma_frame_offset; // offset of chroma frame in units of 256
INSERT_PADDING_BYTES_NOINIT(0x1c); // NvdecDisplayParams
// Decode picture buffer related
s8 current_output_memory_layout;
// output NV12/NV24 setting. index 0: golden; 1: altref; 2: last
std::array<s8, 3> output_memory_layout;
u8 segmentation_feature_data_update;
INSERT_PADDING_BYTES_NOINIT(3);
// ucode return result
u32 result_value;
std::array<u32, 8> partition_offset;
INSERT_PADDING_WORDS_NOINIT(3);
};
static_assert(sizeof(VP8PictureInfo) == 0xc0, "PictureInfo is an invalid size");
VP8PictureInfo current_context{};
}; };
} // namespace Decoders } // namespace Decoders
+63 -20
View File
@@ -1,11 +1,10 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-FileCopyrightText: Copyright 2025 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
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#include <algorithm> // for std::copy #include <algorithm> // for std::copy
#include <bit>
#include <numeric> #include <numeric>
#include "common/alignment.h" #include "common/alignment.h"
@@ -16,11 +15,11 @@
namespace Tegra::Decoders { namespace Tegra::Decoders {
namespace { namespace {
static constexpr u32 diff_update_probability = 252; constexpr u32 diff_update_probability = 252;
static constexpr u32 frame_sync_code = 0x498342; constexpr u32 frame_sync_code = 0x498342;
// Default compressed header probabilities once frame context resets // Default compressed header probabilities once frame context resets
static constexpr Vp9EntropyProbs default_probs{ constexpr Vp9EntropyProbs default_probs{
.y_mode_prob{ .y_mode_prob{
65, 32, 18, 144, 162, 194, 41, 51, 98, 132, 68, 18, 165, 217, 196, 45, 40, 78, 65, 32, 18, 144, 162, 194, 41, 51, 98, 132, 68, 18, 165, 217, 196, 45, 40, 78,
173, 80, 19, 176, 240, 193, 64, 35, 46, 221, 135, 38, 194, 248, 121, 96, 85, 29, 173, 80, 19, 176, 240, 193, 64, 35, 46, 221, 135, 38, 194, 248, 121, 96, 85, 29,
@@ -159,6 +158,34 @@ static constexpr Vp9EntropyProbs default_probs{
.high_precision{128, 128}, .high_precision{128, 128},
}; };
constexpr std::array<u8, 256> norm_lut{
0, 7, 6, 6, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
};
constexpr std::array<u8, 254> map_lut{
20, 21, 22, 23, 24, 25, 0, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37,
1, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 2, 50, 51, 52, 53, 54,
55, 56, 57, 58, 59, 60, 61, 3, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72,
73, 4, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 5, 86, 87, 88, 89,
90, 91, 92, 93, 94, 95, 96, 97, 6, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107,
108, 109, 7, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 8, 122, 123, 124,
125, 126, 127, 128, 129, 130, 131, 132, 133, 9, 134, 135, 136, 137, 138, 139, 140, 141, 142,
143, 144, 145, 10, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 11, 158, 159,
160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 12, 170, 171, 172, 173, 174, 175, 176, 177,
178, 179, 180, 181, 13, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 14, 194,
195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 15, 206, 207, 208, 209, 210, 211, 212,
213, 214, 215, 216, 217, 16, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 17,
230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 18, 242, 243, 244, 245, 246, 247,
248, 249, 250, 251, 252, 253, 19,
};
// 6.2.14 Tile size calculation // 6.2.14 Tile size calculation
[[nodiscard]] s32 CalcMinLog2TileCols(s32 frame_width) { [[nodiscard]] s32 CalcMinLog2TileCols(s32 frame_width) {
@@ -200,17 +227,25 @@ static constexpr Vp9EntropyProbs default_probs{
[[nodiscard]] s32 RemapProbability(s32 new_prob, s32 old_prob) { [[nodiscard]] s32 RemapProbability(s32 new_prob, s32 old_prob) {
new_prob--; new_prob--;
old_prob--; old_prob--;
u8 i = old_prob * 2 <= 0xff
? u8((std::max)(0, RecenterNonNeg(new_prob, old_prob) - 1)) std::size_t index{};
: u8((std::max)(0, RecenterNonNeg(0xff - 1 - new_prob, 0xff - 1 - old_prob) - 1));
return s32((i + 7) % 13 == 0 ? (i + 7) / 13 - 1 : i + 20 - (i + 7) / 13); if (old_prob * 2 <= 0xff) {
index = static_cast<std::size_t>((std::max)(0, RecenterNonNeg(new_prob, old_prob) - 1));
} else {
index = static_cast<std::size_t>(
(std::max)(0, RecenterNonNeg(0xff - 1 - new_prob, 0xff - 1 - old_prob) - 1));
}
return static_cast<s32>(map_lut[index]);
} }
} // Anonymous namespace } // Anonymous namespace
VP9::VP9(Host1x::Host1x& host1x_, const Host1x::NvdecCommon::NvdecRegisters& regs_, s32 id_) VP9::VP9(Host1x::Host1x& host1x_, const Host1x::NvdecCommon::NvdecRegisters& regs_, s32 id_,
: Decoder{host1x_, id_, regs_} Host1x::FrameQueue& frame_queue_)
{ : Decoder{host1x_, id_, regs_, frame_queue_} {
initialized = decode_api.Initialize(Host1x::NvdecCommon::VideoCodec::VP9); codec = Host1x::NvdecCommon::VideoCodec::VP9;
initialized = decode_api.Initialize(codec);
} }
VP9::~VP9() = default; VP9::~VP9() = default;
@@ -342,7 +377,8 @@ void VP9::WriteSegmentation(VpxBitStreamWriter& writer) {
if (update_map) { if (update_map) {
EntropyProbs entropy_probs{}; EntropyProbs entropy_probs{};
host1x.gmmu_manager.ReadBlock(regs.vp9_prob_tab_buffer_offset.Address(), &entropy_probs, sizeof(entropy_probs)); memory_manager.ReadBlock(regs.vp9_prob_tab_buffer_offset.Address(), &entropy_probs,
sizeof(entropy_probs));
auto WriteProb = [&](u8 prob) { auto WriteProb = [&](u8 prob) {
bool coded = prob != 255; bool coded = prob != 255;
@@ -406,7 +442,8 @@ void VP9::WriteSegmentation(VpxBitStreamWriter& writer) {
} }
Vp9PictureInfo VP9::GetVp9PictureInfo() { Vp9PictureInfo VP9::GetVp9PictureInfo() {
host1x.gmmu_manager.ReadBlock(regs.picture_info_offset.Address(), &current_picture_info, sizeof(PictureInfo)); memory_manager.ReadBlock(regs.picture_info_offset.Address(), &current_picture_info,
sizeof(PictureInfo));
Vp9PictureInfo vp9_info = current_picture_info.Convert(); Vp9PictureInfo vp9_info = current_picture_info.Convert();
InsertEntropy(regs.vp9_prob_tab_buffer_offset.Address(), vp9_info.entropy); InsertEntropy(regs.vp9_prob_tab_buffer_offset.Address(), vp9_info.entropy);
@@ -422,7 +459,7 @@ Vp9PictureInfo VP9::GetVp9PictureInfo() {
void VP9::InsertEntropy(u64 offset, Vp9EntropyProbs& dst) { void VP9::InsertEntropy(u64 offset, Vp9EntropyProbs& dst) {
EntropyProbs entropy; EntropyProbs entropy;
host1x.gmmu_manager.ReadBlock(offset, &entropy, sizeof(EntropyProbs)); memory_manager.ReadBlock(offset, &entropy, sizeof(EntropyProbs));
entropy.Convert(dst); entropy.Convert(dst);
} }
@@ -432,7 +469,9 @@ Vp9FrameContainer VP9::GetCurrentFrame() {
// gpu.SyncGuestHost(); epic, why? // gpu.SyncGuestHost(); epic, why?
current_frame.info = GetVp9PictureInfo(); current_frame.info = GetVp9PictureInfo();
current_frame.bit_stream.resize(current_frame.info.bitstream_size); current_frame.bit_stream.resize(current_frame.info.bitstream_size);
host1x.gmmu_manager.ReadBlock(regs.frame_bitstream_offset.Address(), current_frame.bit_stream.data(), current_frame.info.bitstream_size); memory_manager.ReadBlock(regs.frame_bitstream_offset.Address(),
current_frame.bit_stream.data(),
current_frame.info.bitstream_size);
} }
if (!next_frame.bit_stream.empty()) { if (!next_frame.bit_stream.empty()) {
Vp9FrameContainer temp{ Vp9FrameContainer temp{
@@ -854,9 +893,13 @@ std::span<const u8> VP9::ComposeFrame() {
// Write headers and frame to buffer // Write headers and frame to buffer
frame_scratch.resize(uncompressed_header.size() + compressed_header.size() + bitstream.size()); frame_scratch.resize(uncompressed_header.size() + compressed_header.size() + bitstream.size());
std::copy(uncompressed_header.begin(), uncompressed_header.end(), frame_scratch.begin()); std::copy(uncompressed_header.begin(), uncompressed_header.end(), frame_scratch.begin());
std::copy(compressed_header.begin(), compressed_header.end(), frame_scratch.begin() + uncompressed_header.size()); std::copy(compressed_header.begin(), compressed_header.end(),
std::copy(bitstream.begin(), bitstream.end(), frame_scratch.begin() + uncompressed_header.size() + compressed_header.size()); frame_scratch.begin() + uncompressed_header.size());
std::copy(bitstream.begin(), bitstream.end(),
frame_scratch.begin() + uncompressed_header.size() + compressed_header.size());
vp9_hidden_frame = WasFrameHidden(); vp9_hidden_frame = WasFrameHidden();
return GetFrameBytes(); return GetFrameBytes();
} }
@@ -886,7 +929,7 @@ void VpxRangeEncoder::Write(bool bit, s32 probability) {
local_range = range - split; local_range = range - split;
} }
s32 shift = s32(local_range == 0 ? 0 : (std::countl_zero<uint32_t>(local_range) - 24)); s32 shift = static_cast<s32>(norm_lut[local_range]);
local_range <<= shift; local_range <<= shift;
count += shift; count += shift;
+5 -6
View File
@@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
@@ -14,7 +11,7 @@
#include "common/scratch_buffer.h" #include "common/scratch_buffer.h"
#include "common/stream.h" #include "common/stream.h"
#include "video_core/host1x/codecs/decoder.h" #include "video_core/host1x/codecs/decoder.h"
#include "video_core/host1x/codec_types.h" #include "video_core/host1x/codecs/vp9_types.h"
#include "video_core/host1x/nvdec_common.h" #include "video_core/host1x/nvdec_common.h"
namespace Tegra { namespace Tegra {
@@ -116,7 +113,8 @@ private:
class VP9 final : public Decoder { class VP9 final : public Decoder {
public: public:
explicit VP9(Host1x::Host1x& host1x, const Host1x::NvdecCommon::NvdecRegisters& regs, s32 id); explicit VP9(Host1x::Host1x& host1x, const Host1x::NvdecCommon::NvdecRegisters& regs, s32 id,
Host1x::FrameQueue& frame_queue);
~VP9() override; ~VP9() override;
VP9(const VP9&) = delete; VP9(const VP9&) = delete;
@@ -195,10 +193,11 @@ private:
[[nodiscard]] std::vector<u8> ComposeCompressedHeader(); [[nodiscard]] std::vector<u8> ComposeCompressedHeader();
[[nodiscard]] VpxBitStreamWriter ComposeUncompressedHeader(); [[nodiscard]] VpxBitStreamWriter ComposeUncompressedHeader();
Common::ScratchBuffer<u8> frame_scratch;
std::array<s8, 4> loop_filter_ref_deltas{}; std::array<s8, 4> loop_filter_ref_deltas{};
std::array<s8, 2> loop_filter_mode_deltas{}; std::array<s8, 2> loop_filter_mode_deltas{};
Common::ScratchBuffer<u8> frame_scratch;
Vp9FrameContainer next_frame{}; Vp9FrameContainer next_frame{};
std::array<Vp9EntropyProbs, 4> frame_ctxs{}; std::array<Vp9EntropyProbs, 4> frame_ctxs{};
bool swap_ref_indices{}; bool swap_ref_indices{};
@@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
@@ -9,192 +6,12 @@
#include <array> #include <array>
#include <vector> #include <vector>
#include "common/bit_field.h"
#include "common/common_funcs.h" #include "common/common_funcs.h"
#include "common/common_types.h" #include "common/common_types.h"
namespace Tegra { namespace Tegra {
namespace Decoders { namespace Decoders {
struct Offset {
constexpr u32 Address() const noexcept {
return offset << 8;
}
private:
u32 offset;
};
static_assert(std::is_trivial_v<Offset>, "Offset must be trivial");
static_assert(sizeof(Offset) == 0x4, "Offset has the wrong size!");
struct H264ParameterSet {
s32 log2_max_pic_order_cnt_lsb_minus4; ///< 0x00
s32 delta_pic_order_always_zero_flag; ///< 0x04
s32 frame_mbs_only_flag; ///< 0x08
u32 pic_width_in_mbs; ///< 0x0C
u32 frame_height_in_mbs; ///< 0x10
union { ///< 0x14
BitField<0, 2, u32> tile_format;
BitField<2, 3, u32> gob_height;
BitField<5, 27, u32> reserved_surface_format;
};
u32 entropy_coding_mode_flag; ///< 0x18
s32 pic_order_present_flag; ///< 0x1C
s32 num_refidx_l0_default_active; ///< 0x20
s32 num_refidx_l1_default_active; ///< 0x24
s32 deblocking_filter_control_present_flag; ///< 0x28
s32 redundant_pic_cnt_present_flag; ///< 0x2C
u32 transform_8x8_mode_flag; ///< 0x30
u32 pitch_luma; ///< 0x34
u32 pitch_chroma; ///< 0x38
Offset luma_top_offset; ///< 0x3C
Offset luma_bot_offset; ///< 0x40
Offset luma_frame_offset; ///< 0x44
Offset chroma_top_offset; ///< 0x48
Offset chroma_bot_offset; ///< 0x4C
Offset chroma_frame_offset; ///< 0x50
u32 hist_buffer_size; ///< 0x54
union { ///< 0x58
union {
BitField<0, 1, u64> mbaff_frame;
BitField<1, 1, u64> direct_8x8_inference;
BitField<2, 1, u64> weighted_pred;
BitField<3, 1, u64> constrained_intra_pred;
BitField<4, 1, u64> ref_pic;
BitField<5, 1, u64> field_pic;
BitField<6, 1, u64> bottom_field;
BitField<7, 1, u64> second_field;
} flags;
BitField<8, 4, u64> log2_max_frame_num_minus4;
BitField<12, 2, u64> chroma_format_idc;
BitField<14, 2, u64> pic_order_cnt_type;
BitField<16, 6, s64> pic_init_qp_minus26;
BitField<22, 5, s64> chroma_qp_index_offset;
BitField<27, 5, s64> second_chroma_qp_index_offset;
BitField<32, 2, u64> weighted_bipred_idc;
BitField<34, 7, u64> curr_pic_idx;
BitField<41, 5, u64> curr_col_idx;
BitField<46, 16, u64> frame_number;
BitField<62, 1, u64> frame_surfaces;
BitField<63, 1, u64> output_memory_layout;
};
};
static_assert(sizeof(H264ParameterSet) == 0x60, "H264ParameterSet is an invalid size");
#define ASSERT_POSITION(field_name, position) \
static_assert(offsetof(H264ParameterSet, field_name) == position, \
"Field " #field_name " has invalid position")
ASSERT_POSITION(log2_max_pic_order_cnt_lsb_minus4, 0x00);
ASSERT_POSITION(delta_pic_order_always_zero_flag, 0x04);
ASSERT_POSITION(frame_mbs_only_flag, 0x08);
ASSERT_POSITION(pic_width_in_mbs, 0x0C);
ASSERT_POSITION(frame_height_in_mbs, 0x10);
ASSERT_POSITION(tile_format, 0x14);
ASSERT_POSITION(entropy_coding_mode_flag, 0x18);
ASSERT_POSITION(pic_order_present_flag, 0x1C);
ASSERT_POSITION(num_refidx_l0_default_active, 0x20);
ASSERT_POSITION(num_refidx_l1_default_active, 0x24);
ASSERT_POSITION(deblocking_filter_control_present_flag, 0x28);
ASSERT_POSITION(redundant_pic_cnt_present_flag, 0x2C);
ASSERT_POSITION(transform_8x8_mode_flag, 0x30);
ASSERT_POSITION(pitch_luma, 0x34);
ASSERT_POSITION(pitch_chroma, 0x38);
ASSERT_POSITION(luma_top_offset, 0x3C);
ASSERT_POSITION(luma_bot_offset, 0x40);
ASSERT_POSITION(luma_frame_offset, 0x44);
ASSERT_POSITION(chroma_top_offset, 0x48);
ASSERT_POSITION(chroma_bot_offset, 0x4C);
ASSERT_POSITION(chroma_frame_offset, 0x50);
ASSERT_POSITION(hist_buffer_size, 0x54);
ASSERT_POSITION(flags, 0x58);
#undef ASSERT_POSITION
struct DpbEntry {
union {
BitField<0, 7, u32> index;
BitField<7, 5, u32> col_idx;
BitField<12, 2, u32> state;
BitField<14, 1, u32> is_long_term;
BitField<15, 1, u32> non_existing;
BitField<16, 1, u32> is_field;
BitField<17, 4, u32> top_field_marking;
BitField<21, 4, u32> bottom_field_marking;
BitField<25, 1, u32> output_memory_layout;
BitField<26, 6, u32> reserved;
} flags;
std::array<u32, 2> field_order_cnt;
u32 frame_idx;
};
static_assert(sizeof(DpbEntry) == 0x10, "DpbEntry has the wrong size!");
struct DisplayParam {
union {
BitField<0, 1, u32> enable_tf_output;
BitField<1, 1, u32> vc1_map_y_flag;
BitField<2, 3, u32> map_y_value;
BitField<5, 1, u32> vc1_map_uv_flag;
BitField<6, 3, u32> map_uv_value;
BitField<9, 8, u32> out_stride;
BitField<17, 3, u32> tiling_format;
BitField<20, 1, u32> output_structure; // 0=frame, 1=field
BitField<21, 11, u32> reserved0;
};
std::array<s32, 2> output_top;
std::array<s32, 2> output_bottom;
union {
BitField<0, 1, u32> enable_histogram;
BitField<1, 12, u32> histogram_start_x;
BitField<13, 12, u32> histogram_start_y;
BitField<25, 7, u32> reserved1;
};
union {
BitField<0, 12, u32> histogram_end_x;
BitField<12, 12, u32> histogram_end_y;
BitField<24, 8, u32> reserved2;
};
};
static_assert(sizeof(DisplayParam) == 0x1C, "DisplayParam has the wrong size!");
struct H264DecoderContext {
INSERT_PADDING_WORDS_NOINIT(13); ///< 0x0000
std::array<u8, 16> eos; ///< 0x0034
u8 explicit_eos_present_flag; ///< 0x0044
u8 hint_dump_en; ///< 0x0045
INSERT_PADDING_BYTES_NOINIT(2); ///< 0x0046
u32 stream_len; ///< 0x0048
u32 slice_count; ///< 0x004C
u32 mbhist_buffer_size; ///< 0x0050
u32 gptimer_timeout_value; ///< 0x0054
H264ParameterSet h264_parameter_set; ///< 0x0058
std::array<s32, 2> curr_field_order_cnt; ///< 0x00B8
std::array<DpbEntry, 16> dpb; ///< 0x00C0
std::array<u8, 0x60> weight_scale_4x4; ///< 0x01C0
std::array<u8, 0x80> weight_scale_8x8; ///< 0x0220
std::array<u8, 2> num_inter_view_refs_lX; ///< 0x02A0
std::array<u8, 14> reserved2; ///< 0x02A2
std::array<std::array<s8, 16>, 2> inter_view_refidx_lX; ///< 0x02B0
union { ///< 0x02D0
BitField<0, 1, u32> lossless_ipred8x8_filter_enable;
BitField<1, 1, u32> qpprime_y_zero_transform_bypass_flag;
BitField<2, 30, u32> reserved3;
};
DisplayParam display_param; ///< 0x02D4
std::array<u32, 3> reserved4; ///< 0x02F0
};
static_assert(sizeof(H264DecoderContext) == 0x2FC, "H264DecoderContext is an invalid size");
#define ASSERT_POSITION(field_name, position) \
static_assert(offsetof(H264DecoderContext, field_name) == position, \
"Field " #field_name " has invalid position")
ASSERT_POSITION(stream_len, 0x48);
ASSERT_POSITION(h264_parameter_set, 0x58);
ASSERT_POSITION(dpb, 0xC0);
ASSERT_POSITION(weight_scale_4x4, 0x1C0);
#undef ASSERT_POSITION
enum class Vp9SurfaceIndex : u32 { enum class Vp9SurfaceIndex : u32 {
Last = 0, Last = 0,
Golden = 1, Golden = 1,
@@ -470,7 +287,10 @@ struct RefPoolElement {
bool refresh{}; bool refresh{};
}; };
#define ASSERT_POSITION(field_name, position) static_assert(offsetof(Vp9EntropyProbs, field_name) == position) #define ASSERT_POSITION(field_name, position) \
static_assert(offsetof(Vp9EntropyProbs, field_name) == position, \
"Field " #field_name " has invalid position")
ASSERT_POSITION(partition_prob, 0x0024); ASSERT_POSITION(partition_prob, 0x0024);
ASSERT_POSITION(switchable_interp_prob, 0x0724); ASSERT_POSITION(switchable_interp_prob, 0x0724);
ASSERT_POSITION(sign, 0x0772); ASSERT_POSITION(sign, 0x0772);
@@ -478,7 +298,10 @@ ASSERT_POSITION(class_0_fr, 0x079E);
ASSERT_POSITION(high_precision, 0x07B2); ASSERT_POSITION(high_precision, 0x07B2);
#undef ASSERT_POSITION #undef ASSERT_POSITION
#define ASSERT_POSITION(field_name, position) static_assert(offsetof(PictureInfo, field_name) == position) #define ASSERT_POSITION(field_name, position) \
static_assert(offsetof(PictureInfo, field_name) == position, \
"Field " #field_name " has invalid position")
ASSERT_POSITION(bitstream_size, 0x30); ASSERT_POSITION(bitstream_size, 0x30);
ASSERT_POSITION(last_frame_size, 0x48); ASSERT_POSITION(last_frame_size, 0x48);
ASSERT_POSITION(first_level, 0x70); ASSERT_POSITION(first_level, 0x70);
@@ -486,7 +309,9 @@ ASSERT_POSITION(segmentation, 0x80);
ASSERT_POSITION(loop_filter, 0xE4); ASSERT_POSITION(loop_filter, 0xE4);
#undef ASSERT_POSITION #undef ASSERT_POSITION
#define ASSERT_POSITION(field_name, position) static_assert(offsetof(EntropyProbs, field_name) == position) #define ASSERT_POSITION(field_name, position) \
static_assert(offsetof(EntropyProbs, field_name) == position, \
"Field " #field_name " has invalid position")
ASSERT_POSITION(inter_mode_prob, 0x400); ASSERT_POSITION(inter_mode_prob, 0x400);
ASSERT_POSITION(tx_8x8_prob, 0x470); ASSERT_POSITION(tx_8x8_prob, 0x470);
@@ -496,47 +321,5 @@ ASSERT_POSITION(class_0_fr, 0x560);
ASSERT_POSITION(coef_probs, 0x5A0); ASSERT_POSITION(coef_probs, 0x5A0);
#undef ASSERT_POSITION #undef ASSERT_POSITION
struct VP8PictureInfo {
INSERT_PADDING_WORDS_NOINIT(14);
u16 frame_width; // actual frame width
u16 frame_height; // actual frame height
u8 key_frame;
u8 version;
union {
u8 raw;
BitField<0, 2, u8> tile_format;
BitField<2, 3, u8> gob_height;
BitField<5, 3, u8> reserved_surface_format;
};
u8 error_conceal_on; // 1: error conceal on; 0: off
u32 first_part_size; // the size of first partition(frame header and mb header partition)
u32 hist_buffer_size; // in units of 256
u32 vld_buffer_size; // in units of 1
// Current frame buffers
std::array<u32, 2> frame_stride; // [y_c]
u32 luma_top_offset; // offset of luma top field in units of 256
u32 luma_bot_offset; // offset of luma bottom field in units of 256
u32 luma_frame_offset; // offset of luma frame in units of 256
u32 chroma_top_offset; // offset of chroma top field in units of 256
u32 chroma_bot_offset; // offset of chroma bottom field in units of 256
u32 chroma_frame_offset; // offset of chroma frame in units of 256
INSERT_PADDING_BYTES_NOINIT(0x1c); // NvdecDisplayParams
// Decode picture buffer related
s8 current_output_memory_layout;
// output NV12/NV24 setting. index 0: golden; 1: altref; 2: last
std::array<s8, 3> output_memory_layout;
u8 segmentation_feature_data_update;
INSERT_PADDING_BYTES_NOINIT(3);
// ucode return result
u32 result_value;
std::array<u32, 8> partition_offset;
INSERT_PADDING_WORDS_NOINIT(3);
};
static_assert(sizeof(VP8PictureInfo) == 0xc0, "PictureInfo is an invalid size");
}; // namespace Decoders }; // namespace Decoders
}; // namespace Tegra }; // namespace Tegra
@@ -9,7 +9,7 @@
#include "common/scope_exit.h" #include "common/scope_exit.h"
#include "common/settings.h" #include "common/settings.h"
#include "core/memory.h" #include "core/memory.h"
#include "video_core/host1x/ffmpeg.h" #include "video_core/host1x/ffmpeg/ffmpeg.h"
#include "video_core/memory_manager.h" #include "video_core/memory_manager.h"
extern "C" { extern "C" {
@@ -34,9 +34,7 @@ constexpr std::array PreferredGpuDecoders = {
AV_HWDEVICE_TYPE_DXVA2, AV_HWDEVICE_TYPE_DXVA2,
AV_HWDEVICE_TYPE_D3D12VA, AV_HWDEVICE_TYPE_D3D12VA,
#elif defined(__FreeBSD__) #elif defined(__FreeBSD__)
AV_HWDEVICE_TYPE_VAAPI,
AV_HWDEVICE_TYPE_VDPAU, AV_HWDEVICE_TYPE_VDPAU,
AV_HWDEVICE_TYPE_DRM,
#elif defined(__APPLE__) #elif defined(__APPLE__)
AV_HWDEVICE_TYPE_VIDEOTOOLBOX, AV_HWDEVICE_TYPE_VIDEOTOOLBOX,
#elif defined(ANDROID) #elif defined(ANDROID)
@@ -107,17 +105,18 @@ Frame::~Frame() {
Decoder::Decoder(Tegra::Host1x::NvdecCommon::VideoCodec codec) { Decoder::Decoder(Tegra::Host1x::NvdecCommon::VideoCodec codec) {
const AVCodecID av_codec = [&] { const AVCodecID av_codec = [&] {
switch (codec) { switch (codec) {
case Tegra::Host1x::NvdecCommon::VideoCodec::H264: case Tegra::Host1x::NvdecCommon::VideoCodec::H264:
return AV_CODEC_ID_H264; return AV_CODEC_ID_H264;
case Tegra::Host1x::NvdecCommon::VideoCodec::VP8: case Tegra::Host1x::NvdecCommon::VideoCodec::VP8:
return AV_CODEC_ID_VP8; return AV_CODEC_ID_VP8;
case Tegra::Host1x::NvdecCommon::VideoCodec::VP9: case Tegra::Host1x::NvdecCommon::VideoCodec::VP9:
return AV_CODEC_ID_VP9; return AV_CODEC_ID_VP9;
default: default:
UNIMPLEMENTED_MSG("Unknown codec {}", codec); UNIMPLEMENTED_MSG("Unknown codec {}", codec);
return AV_CODEC_ID_NONE; return AV_CODEC_ID_NONE;
} }
}(); }();
m_codec = avcodec_find_decoder(av_codec); m_codec = avcodec_find_decoder(av_codec);
} }
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
+6 -9
View File
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: 2021 yuzu Emulator Project // SPDX-FileCopyrightText: 2021 yuzu Emulator Project
@@ -12,12 +12,9 @@
namespace Tegra::Host1x { namespace Tegra::Host1x {
Host1x::Host1x(Core::System& system_) Host1x::Host1x(Core::System& system_)
: system{system_} : system{system_}, syncpoint_manager{},
, syncpoint_manager{} memory_manager(system.DeviceMemory()), gmmu_manager{system, memory_manager, 32, 0, 12},
, memory_manager(system.DeviceMemory()) allocator{std::make_unique<Common::FlatAllocator<u32, 0, 32>>(1 << 12)} {}
, gmmu_manager{system, memory_manager, 32, 0, 12}
, allocator{1 << 12}
{}
Host1x::~Host1x() = default; Host1x::~Host1x() = default;
@@ -27,13 +24,13 @@ void Host1x::StartDevice(s32 fd, ChannelType type, u32 syncpt) {
#ifdef YUZU_LEGACY #ifdef YUZU_LEGACY
std::call_once(nvdec_first_init, []() {std::this_thread::sleep_for(std::chrono::milliseconds{500});}); // HACK: For Astroneer std::call_once(nvdec_first_init, []() {std::this_thread::sleep_for(std::chrono::milliseconds{500});}); // HACK: For Astroneer
#endif #endif
devices[fd] = std::make_unique<Tegra::Host1x::Nvdec>(*this, fd, syncpt); devices[fd] = std::make_unique<Tegra::Host1x::Nvdec>(*this, fd, syncpt, frame_queue);
break; break;
case ChannelType::VIC: case ChannelType::VIC:
#ifdef YUZU_LEGACY #ifdef YUZU_LEGACY
std::call_once(vic_first_init, []() {std::this_thread::sleep_for(std::chrono::milliseconds{500});}); // HACK: For Astroneer std::call_once(vic_first_init, []() {std::this_thread::sleep_for(std::chrono::milliseconds{500});}); // HACK: For Astroneer
#endif #endif
devices[fd] = std::make_unique<Tegra::Host1x::Vic>(*this, fd, syncpt); devices[fd] = std::make_unique<Tegra::Host1x::Vic>(*this, fd, syncpt, frame_queue);
break; break;
default: default:
LOG_ERROR(HW_GPU, "Unimplemented host1x device {}", static_cast<u32>(type)); LOG_ERROR(HW_GPU, "Unimplemented host1x device {}", static_cast<u32>(type));
+12 -3
View File
@@ -184,12 +184,20 @@ public:
return memory_manager; return memory_manager;
} }
Tegra::MemoryManager& GMMU() {
return gmmu_manager;
}
const Tegra::MemoryManager& GMMU() const {
return gmmu_manager;
}
Common::FlatAllocator<u32, 0, 32>& Allocator() { Common::FlatAllocator<u32, 0, 32>& Allocator() {
return allocator; return *allocator;
} }
const Common::FlatAllocator<u32, 0, 32>& Allocator() const { const Common::FlatAllocator<u32, 0, 32>& Allocator() const {
return allocator; return *allocator;
} }
void StartDevice(s32 fd, ChannelType type, u32 syncpt); void StartDevice(s32 fd, ChannelType type, u32 syncpt);
@@ -203,11 +211,12 @@ public:
it->second->PushEntries(std::move(entries)); it->second->PushEntries(std::move(entries));
} }
private:
Core::System& system; Core::System& system;
SyncpointManager syncpoint_manager; SyncpointManager syncpoint_manager;
Tegra::MaxwellDeviceMemoryManager memory_manager; Tegra::MaxwellDeviceMemoryManager memory_manager;
Tegra::MemoryManager gmmu_manager; Tegra::MemoryManager gmmu_manager;
Common::FlatAllocator<u32, 0, 32> allocator; std::unique_ptr<Common::FlatAllocator<u32, 0, 32>> allocator;
FrameQueue frame_queue; FrameQueue frame_queue;
ankerl::unordered_dense::map<s32, std::unique_ptr<CDmaPusher>> devices; ankerl::unordered_dense::map<s32, std::unique_ptr<CDmaPusher>> devices;
#ifdef YUZU_LEGACY #ifdef YUZU_LEGACY
+30 -31
View File
@@ -1,10 +1,9 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-FileCopyrightText: Copyright 2025 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
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#include <variant>
#include "common/assert.h" #include "common/assert.h"
#include "common/polyfill_thread.h" #include "common/polyfill_thread.h"
@@ -20,13 +19,10 @@ namespace Tegra::Host1x {
#define NVDEC_REG_INDEX(field_name) \ #define NVDEC_REG_INDEX(field_name) \
(offsetof(NvdecCommon::NvdecRegisters, field_name) / sizeof(u64)) (offsetof(NvdecCommon::NvdecRegisters, field_name) / sizeof(u64))
Nvdec::Nvdec(Host1x& host1x_, s32 id_, u32 syncpt) Nvdec::Nvdec(Host1x& host1x_, s32 id_, u32 syncpt, FrameQueue& frame_queue_)
: CDmaPusher{host1x_, id_} : CDmaPusher{host1x_, id_}, id{id_}, syncpoint{syncpt}, frame_queue{frame_queue_} {
, id{id_}
, syncpoint{syncpt}
{
LOG_INFO(HW_GPU, "Created nvdec {}", id); LOG_INFO(HW_GPU, "Created nvdec {}", id);
host1x.frame_queue.Open(id); frame_queue.Open(id);
} }
Nvdec::~Nvdec() { Nvdec::~Nvdec() {
@@ -47,22 +43,24 @@ void Nvdec::ProcessMethod(u32 method, u32 argument) {
} }
void Nvdec::CreateDecoder(NvdecCommon::VideoCodec codec) { void Nvdec::CreateDecoder(NvdecCommon::VideoCodec codec) {
if (std::holds_alternative<std::monostate>(decoder)) { if (decoder.get()) {
switch (codec) { return;
case NvdecCommon::VideoCodec::H264:
decoder.emplace<Decoders::H264>(host1x, regs, id);
break;
case NvdecCommon::VideoCodec::VP8:
decoder.emplace<Decoders::VP8>(host1x, regs, id);
break;
case NvdecCommon::VideoCodec::VP9:
decoder.emplace<Decoders::VP9>(host1x, regs, id);
break;
default:
break;
}
LOG_INFO(HW_GPU, "Created decoder {} for id {}", codec, id);
} }
switch (codec) {
case NvdecCommon::VideoCodec::H264:
decoder = std::make_unique<Decoders::H264>(host1x, regs, id, frame_queue);
break;
case NvdecCommon::VideoCodec::VP8:
decoder = std::make_unique<Decoders::VP8>(host1x, regs, id, frame_queue);
break;
case NvdecCommon::VideoCodec::VP9:
decoder = std::make_unique<Decoders::VP9>(host1x, regs, id, frame_queue);
break;
default:
UNIMPLEMENTED_MSG("Codec {}", decoder->GetCurrentCodecName());
break;
}
LOG_INFO(HW_GPU, "Created decoder {} for id {}", decoder->GetCurrentCodecName(), id);
} }
void Nvdec::Execute() { void Nvdec::Execute() {
@@ -72,14 +70,15 @@ void Nvdec::Execute() {
std::this_thread::sleep_for(std::chrono::milliseconds(8)); std::this_thread::sleep_for(std::chrono::milliseconds(8));
return; return;
} }
if (auto* h264 = std::get_if<Decoders::H264>(&decoder)) { switch (decoder->GetCurrentCodec()) {
h264->Decode(); case NvdecCommon::VideoCodec::H264:
} else if (auto* vp8 = std::get_if<Decoders::VP8>(&decoder)) { case NvdecCommon::VideoCodec::VP8:
vp8->Decode(); case NvdecCommon::VideoCodec::VP9:
} else if (auto* vp9 = std::get_if<Decoders::VP9>(&decoder)) { decoder->Decode();
vp9->Decode(); break;
} else { default:
LOG_ERROR(HW_GPU, "Unrecognized codec executed?"); UNIMPLEMENTED_MSG("Codec {}", decoder->GetCurrentCodecName());
break;
} }
} }
+6 -13
View File
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-FileCopyrightText: Copyright 2025 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
@@ -8,14 +8,10 @@
#include <memory> #include <memory>
#include <vector> #include <vector>
#include <variant>
#include "common/common_types.h" #include "common/common_types.h"
#include "video_core/cdma_pusher.h" #include "video_core/cdma_pusher.h"
#include "video_core/host1x/codecs/decoder.h" #include "video_core/host1x/codecs/decoder.h"
#include "video_core/host1x/codecs/h264.h"
#include "video_core/host1x/codecs/vp8.h"
#include "video_core/host1x/codecs/vp9.h"
namespace Tegra { namespace Tegra {
@@ -25,7 +21,7 @@ class FrameQueue;
class Nvdec final : public CDmaPusher { class Nvdec final : public CDmaPusher {
public: public:
explicit Nvdec(Host1x& host1x, s32 id, u32 syncpt); explicit Nvdec(Host1x& host1x, s32 id, u32 syncpt, FrameQueue& frame_queue_);
~Nvdec(); ~Nvdec();
/// Writes the method into the state, Invoke Execute() if encountered /// Writes the method into the state, Invoke Execute() if encountered
@@ -42,15 +38,12 @@ private:
/// Invoke codec to decode a frame /// Invoke codec to decode a frame
void Execute(); void Execute();
NvdecCommon::NvdecRegisters regs{};
std::variant<
Decoders::H264,
Decoders::VP8,
Decoders::VP9,
std::monostate
> decoder = std::monostate{};
s32 id; s32 id;
u32 syncpoint; u32 syncpoint;
FrameQueue& frame_queue;
NvdecCommon::NvdecRegisters regs{};
std::unique_ptr<Decoder> decoder;
}; };
} // namespace Host1x } // namespace Host1x
+13 -12
View File
@@ -85,17 +85,18 @@ void SwizzleSurface(std::span<u8> output, u32 out_stride, std::span<const u8> in
} // namespace } // namespace
Vic::Vic(Host1x& host1x_, s32 id_, u32 syncpt) noexcept Vic::Vic(Host1x& host1x_, s32 id_, u32 syncpt, FrameQueue& frame_queue_) noexcept :
: CDmaPusher{host1x_, id_} CDmaPusher{host1x_, id_}
, id{id_} , id{id_}
, syncpoint{syncpt} , syncpoint{syncpt}
, frame_queue{frame_queue_}
{ {
LOG_INFO(HW_GPU, "Created vic {}", id); LOG_INFO(HW_GPU, "Created vic {}", id);
} }
Vic::~Vic() noexcept { Vic::~Vic() noexcept {
LOG_INFO(HW_GPU, "Destroying vic {}", id); LOG_INFO(HW_GPU, "Destroying vic {}", id);
host1x.frame_queue.Close(id); frame_queue.Close(id);
} }
void Vic::ProcessMethod(u32 method, u32 arg) noexcept { void Vic::ProcessMethod(u32 method, u32 arg) noexcept {
@@ -112,7 +113,7 @@ void Vic::ProcessMethod(u32 method, u32 arg) noexcept {
void Vic::Execute() noexcept { void Vic::Execute() noexcept {
ConfigStruct config{}; ConfigStruct config{};
host1x.gmmu_manager.ReadBlock(regs.config_struct_offset.Address(), &config, sizeof(ConfigStruct)); memory_manager.ReadBlock(regs.config_struct_offset.Address(), &config, sizeof(ConfigStruct));
auto output_width = config.output_surface_config.out_surface_width + 1; auto output_width = config.output_surface_config.out_surface_width + 1;
auto output_height = config.output_surface_config.out_surface_height + 1; auto output_height = config.output_surface_config.out_surface_height + 1;
@@ -123,8 +124,8 @@ void Vic::Execute() noexcept {
if (auto& slot_config = config.slot_structs[i]; slot_config.config.slot_enable) { if (auto& slot_config = config.slot_structs[i]; slot_config.config.slot_enable) {
auto const luma_offset = regs.surfaces[i][SurfaceIndex::Current].luma.Address(); auto const luma_offset = regs.surfaces[i][SurfaceIndex::Current].luma.Address();
if (nvdec_id == -1) if (nvdec_id == -1)
nvdec_id = host1x.frame_queue.VicFindNvdecFdFromOffset(luma_offset); nvdec_id = frame_queue.VicFindNvdecFdFromOffset(luma_offset);
if (auto frame = host1x.frame_queue.GetFrame(nvdec_id, luma_offset); frame.get()) { if (auto frame = frame_queue.GetFrame(nvdec_id, luma_offset); frame.get()) {
switch (frame->GetPixelFormat()) { switch (frame->GetPixelFormat()) {
case AV_PIX_FMT_YUV420P: case AV_PIX_FMT_YUV420P:
ReadY8__V8U8_N420(slot_config, regs.surfaces[i], std::move(frame), true); ReadY8__V8U8_N420(slot_config, regs.surfaces[i], std::move(frame), true);
@@ -880,8 +881,8 @@ void Vic::WriteY8__V8U8_N420(const OutputSurfaceConfig& output_surface_config) n
chroma_scratch.resize_destructive(out_chroma_size); chroma_scratch.resize_destructive(out_chroma_size);
Decode(luma_scratch.data(), chroma_scratch.data()); Decode(luma_scratch.data(), chroma_scratch.data());
Tegra::Memory::GpuGuestMemoryScoped<u8, Core::Memory::GuestMemoryFlags::SafeWrite> out_luma(host1x.gmmu_manager, regs.output_surface.luma.Address(), out_luma_swizzle_size, &swizzle_scratch); Tegra::Memory::GpuGuestMemoryScoped<u8, Core::Memory::GuestMemoryFlags::SafeWrite> out_luma(memory_manager, regs.output_surface.luma.Address(), out_luma_swizzle_size, &swizzle_scratch);
Tegra::Memory::GpuGuestMemoryScoped<u8, Core::Memory::GuestMemoryFlags::SafeWrite> out_chroma(host1x.gmmu_manager, regs.output_surface.chroma_u.Address(), out_chroma_swizzle_size, &swizzle_scratch); Tegra::Memory::GpuGuestMemoryScoped<u8, Core::Memory::GuestMemoryFlags::SafeWrite> out_chroma(memory_manager, regs.output_surface.chroma_u.Address(), out_chroma_swizzle_size, &swizzle_scratch);
if (block_height == 1) { if (block_height == 1) {
SwizzleSurface(out_luma, out_luma_stride, luma_scratch, out_luma_stride, out_luma_height); SwizzleSurface(out_luma, out_luma_stride, luma_scratch, out_luma_stride, out_luma_height);
SwizzleSurface(out_chroma, out_chroma_stride, chroma_scratch, out_chroma_stride, out_chroma_height); SwizzleSurface(out_chroma, out_chroma_stride, chroma_scratch, out_chroma_stride, out_chroma_height);
@@ -909,8 +910,8 @@ void Vic::WriteY8__V8U8_N420(const OutputSurfaceConfig& output_surface_config) n
luma_scratch.resize_destructive(out_luma_size); luma_scratch.resize_destructive(out_luma_size);
chroma_scratch.resize_destructive(out_chroma_size); chroma_scratch.resize_destructive(out_chroma_size);
Decode(luma_scratch.data(), chroma_scratch.data()); Decode(luma_scratch.data(), chroma_scratch.data());
host1x.gmmu_manager.WriteBlock(regs.output_surface.luma.Address(), luma_scratch.data(), out_luma_size); memory_manager.WriteBlock(regs.output_surface.luma.Address(), luma_scratch.data(), out_luma_size);
host1x.gmmu_manager.WriteBlock(regs.output_surface.chroma_u.Address(), chroma_scratch.data(), out_chroma_size); memory_manager.WriteBlock(regs.output_surface.chroma_u.Address(), chroma_scratch.data(), out_chroma_size);
} break; } break;
default: default:
UNREACHABLE(); UNREACHABLE();
@@ -1045,7 +1046,7 @@ void Vic::WriteABGR(const OutputSurfaceConfig& output_surface_config, VideoPixel
luma_scratch.resize_destructive(out_luma_size); luma_scratch.resize_destructive(out_luma_size);
Decode(luma_scratch.data(), output_surface.data()); Decode(luma_scratch.data(), output_surface.data());
Tegra::Memory::GpuGuestMemoryScoped<u8, Core::Memory::GuestMemoryFlags::SafeWrite> out_luma(host1x.gmmu_manager, regs.output_surface.luma.Address(), out_swizzle_size, &swizzle_scratch); Tegra::Memory::GpuGuestMemoryScoped<u8, Core::Memory::GuestMemoryFlags::SafeWrite> out_luma(memory_manager, regs.output_surface.luma.Address(), out_swizzle_size, &swizzle_scratch);
if (block_height == 1) { if (block_height == 1) {
SwizzleSurface(out_luma, out_luma_stride, luma_scratch, out_luma_stride, out_luma_height); SwizzleSurface(out_luma, out_luma_stride, luma_scratch, out_luma_stride, out_luma_height);
} else { } else {
@@ -1060,7 +1061,7 @@ void Vic::WriteABGR(const OutputSurfaceConfig& output_surface_config, VideoPixel
surface_stride * surface_height * BytesPerPixel, out_luma_width, out_luma_height, surface_stride * surface_height * BytesPerPixel, out_luma_width, out_luma_height,
out_luma_stride, out_luma_size); out_luma_stride, out_luma_size);
luma_scratch.resize_destructive(out_luma_size); luma_scratch.resize_destructive(out_luma_size);
Tegra::Memory::GpuGuestMemoryScoped<u8, Core::Memory::GuestMemoryFlags::SafeWrite> out_luma(host1x.gmmu_manager, regs.output_surface.luma.Address(), out_luma_size, &luma_scratch); Tegra::Memory::GpuGuestMemoryScoped<u8, Core::Memory::GuestMemoryFlags::SafeWrite> out_luma(memory_manager, regs.output_surface.luma.Address(), out_luma_size, &luma_scratch);
Decode(out_luma.data(), output_surface.data()); Decode(out_luma.data(), output_surface.data());
} break; } break;
default: default:
+7 -7
View File
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-FileCopyrightText: Copyright 2025 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
@@ -15,7 +15,6 @@
#include "common/common_types.h" #include "common/common_types.h"
#include "common/scratch_buffer.h" #include "common/scratch_buffer.h"
#include "video_core/cdma_pusher.h" #include "video_core/cdma_pusher.h"
#include "video_core/host1x/host1x.h"
namespace Tegra::Host1x { namespace Tegra::Host1x {
class Host1x; class Host1x;
@@ -606,7 +605,7 @@ public:
SetOutputSurfaceChromaUnusedOffset = offsetof(VicRegisters, output_surface.chroma_v) SetOutputSurfaceChromaUnusedOffset = offsetof(VicRegisters, output_surface.chroma_v)
}; };
explicit Vic(Host1x& host1x, s32 id, u32 syncpt) noexcept; explicit Vic(Host1x& host1x, s32 id, u32 syncpt, FrameQueue& frame_queue) noexcept;
~Vic() noexcept; ~Vic() noexcept;
/// Write to the device state. /// Write to the device state.
@@ -621,17 +620,18 @@ private:
void WriteY8__V8U8_N420(const OutputSurfaceConfig& output_surface_config) noexcept; void WriteY8__V8U8_N420(const OutputSurfaceConfig& output_surface_config) noexcept;
void WriteABGR(const OutputSurfaceConfig& output_surface_config, VideoPixelFormat format) noexcept; void WriteABGR(const OutputSurfaceConfig& output_surface_config, VideoPixelFormat format) noexcept;
s32 id;
s32 nvdec_id{-1};
u32 syncpoint;
VicRegisters regs{}; VicRegisters regs{};
FrameQueue& frame_queue;
Common::ScratchBuffer<u8> swizzle_scratch; Common::ScratchBuffer<u8> swizzle_scratch;
Common::ScratchBuffer<Pixel> output_surface; Common::ScratchBuffer<Pixel> output_surface;
Common::ScratchBuffer<Pixel> slot_surface; Common::ScratchBuffer<Pixel> slot_surface;
Common::ScratchBuffer<u8> luma_scratch; Common::ScratchBuffer<u8> luma_scratch;
Common::ScratchBuffer<u8> chroma_scratch; Common::ScratchBuffer<u8> chroma_scratch;
s32 id;
s32 nvdec_id{-1};
u32 syncpoint;
}; };
} // namespace Tegra::Host1x } // namespace Tegra::Host1x
@@ -1,6 +1,3 @@
// 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
@@ -8,22 +5,16 @@
layout(local_size_x = 1) in; layout(local_size_x = 1) in;
layout(std430, binding = 0) readonly buffer Query { layout(std430, binding = 0) buffer Query {
uint data[]; uvec2 initial;
uvec2 unknown;
uvec2 current;
}; };
layout(std430, binding = 1) writeonly buffer Result { layout(std430, binding = 1) buffer Result {
uint result; uint result;
}; };
layout(push_constant) uniform PushConstants {
uint compare_to_zero;
};
void main() { void main() {
if (compare_to_zero != 0u) { result = all(equal(initial, current)) ? 1 : 0;
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();
if (!force) { auto topology = Maxwell3D::Regs::PrimitiveTopology(parameters[0] & 0xFFFFU);
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,7 +412,6 @@ 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) &&
+1 -4
View File
@@ -1,6 +1,3 @@
// 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
@@ -78,7 +75,7 @@ public:
} }
u64 GetDependentMask() const { u64 GetDependentMask() const {
return dependent_mask; return dependence_mask;
} }
u64 GetAmendValue() const { u64 GetAmendValue() const {
@@ -629,9 +629,6 @@ 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,7 +190,9 @@ void FixedPipelineState::Refresh(Tegra::Engines::Maxwell3D& maxwell3d, DynamicFe
} }
} }
} }
dynamic_state.Refresh3(regs, features); if (!extended_dynamic_state_3_enables) {
dynamic_state.Refresh3(regs);
}
if (xfb_enabled) { if (xfb_enabled) {
RefreshXfbState(xfb_state, regs); RefreshXfbState(xfb_state, regs);
} }
@@ -293,22 +295,16 @@ 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) {
const DynamicFeatures& features) { logic_op_enable.Assign(regs.logic_op.enable != 0 ? 1 : 0);
if (!features.has_dynamic_state3_logic_op_enable) { depth_clamp_disabled.Assign(regs.viewport_clip_control.geometry_clip ==
logic_op_enable.Assign(regs.logic_op.enable != 0 ? 1 : 0); Maxwell::ViewportClipControl::GeometryClip::Passthrough ||
} regs.viewport_clip_control.geometry_clip ==
if (!features.has_dynamic_state3_depth_clamp_enable) { Maxwell::ViewportClipControl::GeometryClip::FrustumXYZ ||
depth_clamp_disabled.Assign(regs.viewport_clip_control.geometry_clip == regs.viewport_clip_control.geometry_clip ==
Maxwell::ViewportClipControl::GeometryClip::Passthrough || Maxwell::ViewportClipControl::GeometryClip::FrustumZ);
regs.viewport_clip_control.geometry_clip ==
Maxwell::ViewportClipControl::GeometryClip::FrustumXYZ || line_stipple_enable.Assign(regs.line_stipple_enable);
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,9 +27,6 @@ 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;
@@ -178,7 +175,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, const DynamicFeatures& features); void Refresh3(const Maxwell& regs);
Maxwell::ComparisonOp DepthTestFunc() const noexcept { Maxwell::ComparisonOp DepthTestFunc() const noexcept {
return UnpackComparisonOp(depth_test_func); return UnpackComparisonOp(depth_test_func);
@@ -268,7 +265,8 @@ 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) {
return offsetof(FixedPipelineState, attachments); // Exclude dynamic state and attributes
return offsetof(FixedPipelineState, dynamic_state);
} }
if (dynamic_vertex_input) { if (dynamic_vertex_input) {
// Exclude dynamic state // Exclude dynamic state
@@ -8,7 +8,6 @@
#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>
@@ -172,11 +171,7 @@ try
RendererVulkan::~RendererVulkan() { RendererVulkan::~RendererVulkan() {
scheduler.RegisterOnSubmit([] {}); scheduler.RegisterOnSubmit([] {});
scheduler.Finish(); void(device.GetLogical().WaitIdle());
{
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,7 +8,6 @@
// 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"
@@ -32,10 +31,7 @@ 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,10 +637,12 @@ 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] = 0; bindings.sizes[i] = VK_WHOLE_SIZE;
if (!device.HasNullDescriptor()) {
ReserveNullBuffer();
handle = *null_buffer;
}
} }
buffer_handles[i] = handle; buffer_handles[i] = handle;
} }

Some files were not shown because too many files have changed in this diff Show More