mirror of
https://github.com/ARMSX2/ARMSX3.git
synced 2026-08-24 16:58:52 -07:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
39ca5cdab6 | ||
|
|
b82432c793 | ||
|
|
7f54855b7d | ||
|
|
0819f1ef15 | ||
|
|
8ee20d91d5 |
@@ -29,8 +29,8 @@ android {
|
||||
applicationId = "com.armsx3"
|
||||
minSdk = 26
|
||||
targetSdk = 37
|
||||
versionCode = 12
|
||||
versionName = "0.7.1"
|
||||
versionCode = 13
|
||||
versionName = "0.7.2"
|
||||
|
||||
// ARMSX2's UI reads these. STORAGE_ALL_FILES gates the all-files storage path in
|
||||
// onboarding; IN_APP_UPDATER gates the in-app GitHub-release updater.
|
||||
|
||||
@@ -63,6 +63,8 @@ object ConfigStore {
|
||||
private const val KEY_SPU_DECODER_RESTORE = "config.migrated.spuDecoderRestoreLlvm"
|
||||
private const val KEY_XFLOAT_BACK_TO_APPROX = "config.migrated.xfloatBackToApprox"
|
||||
private const val KEY_PRECISE_SPU_OFF = "config.migrated.preciseSpuVerifyOff"
|
||||
// Oboe became the Android default in 0.7.2; move anyone still on the old Cubeb default.
|
||||
private const val KEY_AUDIO_OBOE = "config.migrated.audioOboeDefault"
|
||||
private const val KEY_ATOMIC_DMA_OFF = "config.migrated.atomicDmaStoresOff"
|
||||
// Bumped: the first pass recorded only Vblank Rate, which did not hold on its own.
|
||||
private const val KEY_VBLANK_60 = "config.migrated.frameCap60"
|
||||
@@ -316,6 +318,16 @@ object ConfigStore {
|
||||
}
|
||||
|
||||
|
||||
// Oboe is the Android default now. Only move people sitting on the previous default
|
||||
// (Cubeb, index 2) -- anyone who deliberately picked Null or another backend keeps it.
|
||||
if (!MainActivityRuntime.prefs.getBoolean(KEY_AUDIO_OBOE, false)) {
|
||||
if (raw != null && parsed.ps3.audioRenderer == 2) {
|
||||
parsed = parsed.copy(ps3 = parsed.ps3.copy(audioRenderer = 4))
|
||||
dirty = true
|
||||
}
|
||||
MainActivityRuntime.prefs.edit { putBoolean(KEY_AUDIO_OBOE, true) }
|
||||
}
|
||||
|
||||
// The ARM64 block checksum is fixed, so the full-compare workaround can go.
|
||||
if (!MainActivityRuntime.prefs.getBoolean(KEY_PRECISE_SPU_OFF, false)) {
|
||||
if (raw != null && parsed.ps3.preciseSpuVerification) {
|
||||
|
||||
@@ -198,6 +198,12 @@ data class Ps3Settings(
|
||||
* preciseSpuVerification). Accurate is a rarely-exercised path and costs
|
||||
* speed, so there is no reason to sit on it.
|
||||
*/
|
||||
/**
|
||||
* Which face button confirms in PS3 system dialogs. 0 = circle, 1 = cross, matching
|
||||
* enter_button_assign. Japanese-region games and hardware confirm with circle; the rest of
|
||||
* the world uses cross, which is why RPCS3 exposes it rather than deriving it from region.
|
||||
*/
|
||||
val enterButtonAssign: Int = 1,
|
||||
val spuXFloat: Int = 1,
|
||||
val accurateSpuRsv: Boolean = true,
|
||||
/**
|
||||
@@ -249,7 +255,8 @@ data class Ps3Settings(
|
||||
val debugConsoleMode: Boolean = false,
|
||||
val resolution: Int = 2,
|
||||
val anisoFilter: Int = 0,
|
||||
val audioRenderer: Int = 2,
|
||||
/** Index into Rpcs3Settings.AUDIO_RENDERERS. 4 = Oboe, the Android default (see node_audio). */
|
||||
val audioRenderer: Int = 4,
|
||||
/**
|
||||
* Output aspect override in permille (1778 = 16:9, 1333 = 4:3), 0 = follow the game.
|
||||
*
|
||||
@@ -1064,6 +1071,7 @@ data class Settings(
|
||||
put("PS3/Net", "Internet enabled", "enum", ps3.netEnabled.toString())
|
||||
put("PS3/Net", "PSN status", "enum", ps3.psnStatus.toString())
|
||||
put("PS3/Net", "UPNP Enabled", "bool", ps3.upnpEnabled.toString())
|
||||
put("PS3/System", "Enter button assignment", "enum", ps3.enterButtonAssign.toString())
|
||||
put("PS3/Core", "SPU XFloat Accuracy", "enum", ps3.spuXFloat.toString())
|
||||
put("PS3/Core", "Accurate SPU Reservations", "bool", ps3.accurateSpuRsv.toString())
|
||||
put("PS3/Core", "Accurate Cache Line Stores", "bool", ps3.accurateCacheLine.toString())
|
||||
@@ -2032,6 +2040,7 @@ data class Settings(
|
||||
put("ps3NetEnabled", ps3.netEnabled)
|
||||
put("ps3PsnStatus", ps3.psnStatus)
|
||||
put("ps3UpnpEnabled", ps3.upnpEnabled)
|
||||
put("ps3EnterButtonAssign", ps3.enterButtonAssign)
|
||||
put("ps3SpuXFloat", ps3.spuXFloat)
|
||||
put("ps3AccurateSpuRsv", ps3.accurateSpuRsv)
|
||||
put("ps3AccurateCacheLine", ps3.accurateCacheLine)
|
||||
@@ -2370,6 +2379,7 @@ data class Settings(
|
||||
netEnabled = json.optBoolean("ps3NetEnabled", def.ps3.netEnabled),
|
||||
psnStatus = json.optBoolean("ps3PsnStatus", def.ps3.psnStatus),
|
||||
upnpEnabled = json.optBoolean("ps3UpnpEnabled", def.ps3.upnpEnabled),
|
||||
enterButtonAssign = json.optInt("ps3EnterButtonAssign", def.ps3.enterButtonAssign),
|
||||
spuXFloat = json.optInt("ps3SpuXFloat", def.ps3.spuXFloat),
|
||||
accurateSpuRsv = json.optBoolean("ps3AccurateSpuRsv", def.ps3.accurateSpuRsv),
|
||||
accurateCacheLine = json.optBoolean("ps3AccurateCacheLine", def.ps3.accurateCacheLine),
|
||||
@@ -2688,6 +2698,7 @@ data class Settings(
|
||||
if (current.ps3.netEnabled != base.ps3.netEnabled) j.put("ps3NetEnabled", current.ps3.netEnabled)
|
||||
if (current.ps3.psnStatus != base.ps3.psnStatus) j.put("ps3PsnStatus", current.ps3.psnStatus)
|
||||
if (current.ps3.upnpEnabled != base.ps3.upnpEnabled) j.put("ps3UpnpEnabled", current.ps3.upnpEnabled)
|
||||
if (current.ps3.enterButtonAssign != base.ps3.enterButtonAssign) j.put("ps3EnterButtonAssign", current.ps3.enterButtonAssign)
|
||||
if (current.ps3.spuXFloat != base.ps3.spuXFloat) j.put("ps3SpuXFloat", current.ps3.spuXFloat)
|
||||
if (current.ps3.accurateSpuRsv != base.ps3.accurateSpuRsv) j.put("ps3AccurateSpuRsv", current.ps3.accurateSpuRsv)
|
||||
if (current.ps3.accurateCacheLine != base.ps3.accurateCacheLine) j.put("ps3AccurateCacheLine", current.ps3.accurateCacheLine)
|
||||
@@ -2987,6 +2998,7 @@ data class Settings(
|
||||
netEnabled = if (overrides.has("ps3NetEnabled")) overrides.getBoolean("ps3NetEnabled") else base.ps3.netEnabled,
|
||||
psnStatus = if (overrides.has("ps3PsnStatus")) overrides.getBoolean("ps3PsnStatus") else base.ps3.psnStatus,
|
||||
upnpEnabled = if (overrides.has("ps3UpnpEnabled")) overrides.getBoolean("ps3UpnpEnabled") else base.ps3.upnpEnabled,
|
||||
enterButtonAssign = if (overrides.has("ps3EnterButtonAssign")) overrides.getInt("ps3EnterButtonAssign") else base.ps3.enterButtonAssign,
|
||||
spuXFloat = if (overrides.has("ps3SpuXFloat")) overrides.getInt("ps3SpuXFloat") else base.ps3.spuXFloat,
|
||||
accurateSpuRsv = if (overrides.has("ps3AccurateSpuRsv")) overrides.getBoolean("ps3AccurateSpuRsv") else base.ps3.accurateSpuRsv,
|
||||
accurateCacheLine = if (overrides.has("ps3AccurateCacheLine")) overrides.getBoolean("ps3AccurateCacheLine") else base.ps3.accurateCacheLine,
|
||||
|
||||
@@ -525,6 +525,14 @@ val EN: Map<String, String> = mapOf(
|
||||
"adv.accurateRsxRsv.description" to "Synchronises GPU access to reserved memory strictly. Fixes rare graphical corruption at a performance cost.",
|
||||
"adv.ppuRsvPriority.label" to "PPU Reservation Priority",
|
||||
"adv.ppuRsvPriority.description" to "Gives the main CPU priority over the SPUs when competing for the same memory. Can help games that stall waiting on the PPU.",
|
||||
"pad.section.enterButton" to "Enter Button Assignment",
|
||||
"pad.enterButton.label" to "Confirm button",
|
||||
"pad.enterButton.circle" to "Enter with circle",
|
||||
"pad.enterButton.cross" to "Enter with cross",
|
||||
"pad.enterButton.description" to "Which button confirms in PS3 system dialogs. Japanese games usually expect circle; most others use cross.",
|
||||
"app.resetAll" to "Reset all settings",
|
||||
"app.resetAll.desc" to "Put every setting back to its default. Per-game settings and controller binds are kept.",
|
||||
"app.resetAll.confirm" to "Every global setting goes back to its default. Per-game overrides and controller binds are not touched.",
|
||||
"adv.spuVerification.label" to "SPU Verification",
|
||||
"adv.spuVerification.description" to "Verifies compiled SPU code against the original. Catches miscompiles; turning it off is faster but makes bad codegen silent.",
|
||||
"adv.preciseSpuVerification.label" to "Precise SPU Verification",
|
||||
@@ -821,6 +829,8 @@ val EN: Map<String, String> = mapOf(
|
||||
"pad.stickFeel.acceleration.description" to "Non-linear response curve: small tilts stay precise for aiming, full tilt ramps up to full speed. 0 = linear (off); higher = more curve.",
|
||||
"pad.stickFeel.acceleration.label" to "Acceleration",
|
||||
"pad.stickFeel.antiDeadzone.description" to "Smallest output sent to the game, to cancel a game's OWN built-in stick deadzone (e.g. Cold Fear / Area 51 ignore the stick until ~45%, then aim jumps). Set near the game's deadzone so any stick movement responds immediately and the full travel maps smoothly above it. 0 = off.",
|
||||
"pad.stickFeel.squareGate.label" to "Full Diagonal Range",
|
||||
"pad.stickFeel.squareGate.description" to "Sends the full range on diagonals instead of the reduced value a real DualShock gives (~70%), so diagonal movement is as fast as straight up/down/left/right. On by default. Turn off to match original hardware exactly.",
|
||||
"pad.stickFeel.antiDeadzone.label" to "Anti-Deadzone",
|
||||
"pad.stickFeel.deadzone.description" to "Fraction of physical analog travel ignored near center (applied to the stick's radial distance, so diagonals behave like cardinals). Output re-normalizes past it, so movement still ramps smoothly from 0 — which also means the on-screen effect can be masked by a game's OWN built-in deadzone (Area 51 ignores input below ~45% no matter what you set here; use Anti-Deadzone for that). 0 = off — raw hardware values pass through, including any stick drift.",
|
||||
"pad.stickFeel.deadzone.label" to "Deadzone",
|
||||
|
||||
@@ -364,6 +364,22 @@ object ControllerMappings {
|
||||
private const val KEY_STICK_ANTIDZ = "pad.stick.antiDeadzone"
|
||||
const val STICK_ANTIDZ_MAX = 0.60f
|
||||
private val prefStickAntiDz = PerStickPref(KEY_STICK_ANTIDZ, 0.0f, 0f, STICK_ANTIDZ_MAX)
|
||||
|
||||
// Square gate: send the full per-axis range on diagonals instead of capping them to the
|
||||
// unit circle. A DualShock 3 is circular-gated, so a full diagonal is ~0.707 per axis, and
|
||||
// emitting that is technically faithful -- but it lands inside the internal deadzone of games
|
||||
// that test each axis separately, and their camera then crawls diagonally while the cardinals
|
||||
// are fine. Oblivion is the case that found this.
|
||||
//
|
||||
// DEFAULT ON. Faithfulness to a circular gate is not worth a control scheme that feels broken,
|
||||
// and a modern pad's own gate is closer to square anyway. Off restores the hardware curve for
|
||||
// anyone who wants it. Per stick.
|
||||
private const val KEY_STICK_SQUARE = "pad.stick.square"
|
||||
fun stickSquareGate(left: Boolean): Boolean =
|
||||
MainActivityRuntime.prefs.getBoolean(KEY_STICK_SQUARE + if (left) ".l" else ".r", true)
|
||||
fun setStickSquareGate(left: Boolean, v: Boolean) =
|
||||
MainActivityRuntime.prefs.edit { putBoolean(KEY_STICK_SQUARE + if (left) ".l" else ".r", v) }
|
||||
|
||||
fun stickAntiDeadzone(left: Boolean): Float = prefStickAntiDz.get(left)
|
||||
fun setStickAntiDeadzone(left: Boolean, v: Float) = prefStickAntiDz.set(left, v)
|
||||
|
||||
|
||||
@@ -4361,7 +4361,12 @@ open class MainActivityRuntime : ComponentActivity() {
|
||||
val mag = kotlin.math.hypot(gx, gy)
|
||||
if (mag <= 0f) return
|
||||
val shaped = shapeStickMag(mag.coerceAtMost(1f), left)
|
||||
val scale = shaped / mag // preserves direction; caps square-gate diagonals at unit circle
|
||||
// Dividing by the magnitude caps a full diagonal at the unit circle: 0.707 per axis, which
|
||||
// is what a circular-gated DualShock 3 really sends. Games that deadzone each axis on its
|
||||
// own then ignore diagonals almost entirely. Dividing by the LARGER axis instead expands
|
||||
// to the square, so a full diagonal reaches 1.0 on both. Same result on the cardinals.
|
||||
val denom = if (ControllerMappings.stickSquareGate(left)) kotlin.math.max(abs(gx), abs(gy)) else mag
|
||||
val scale = if (denom > 0f) shaped / denom else 0f
|
||||
val ox = gx * scale
|
||||
val oy = gy * scale
|
||||
if (ox > 0f) accumAnalog(aXPos, ox) else if (ox < 0f) accumAnalog(aXNeg, -ox)
|
||||
|
||||
@@ -792,6 +792,7 @@ fun AppTab() {
|
||||
)
|
||||
|
||||
ClearCacheRow()
|
||||
ResetAllSettingsRow()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -836,6 +837,77 @@ private fun ClearCacheRow() {
|
||||
}
|
||||
}
|
||||
|
||||
/** Put every global setting back to its default in one go.
|
||||
*
|
||||
* The per-tab Reset in the top bar only covers the page you are looking at, which is right for
|
||||
* undoing one experiment but tedious when a config has drifted across half a dozen tabs. This is
|
||||
* the "start clean" button. Per-game overrides are deliberately left alone: they belong to
|
||||
* individual games, are invisible from here, and wiping them from a global page would be a
|
||||
* surprise. Controller binds live in ControllerMappings and keep their own reset. */
|
||||
@Composable
|
||||
private fun ResetAllSettingsRow() {
|
||||
var confirming by remember { mutableStateOf(false) }
|
||||
|
||||
Surface(
|
||||
onClick = { confirming = true },
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
.controllerFocusable("app.resetAll", RoundedCornerShape(20.dp), onConfirm = { confirming = true }),
|
||||
shape = RoundedCornerShape(20.dp),
|
||||
color = MaterialTheme.colorScheme.surfaceVariant.copy(alpha = 0.72f),
|
||||
border = BorderStroke(1.dp, MaterialTheme.colorScheme.error.copy(alpha = 0.46f)),
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth().padding(horizontal = 14.dp, vertical = 12.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
Surface(
|
||||
modifier = Modifier.size(46.dp),
|
||||
shape = RoundedCornerShape(14.dp),
|
||||
color = MaterialTheme.colorScheme.errorContainer,
|
||||
) {
|
||||
Row(verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.Center) {
|
||||
Text("↺", fontSize = 21.sp)
|
||||
}
|
||||
}
|
||||
Spacer(Modifier.width(12.dp))
|
||||
Column(Modifier.weight(1f)) {
|
||||
Text(str("app.resetAll"), style = MaterialTheme.typography.titleMedium)
|
||||
Text(
|
||||
str("app.resetAll.desc"),
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (confirming) {
|
||||
com.armsx2.ui.common.ConfirmOverlay(
|
||||
title = str("app.resetAll"),
|
||||
message = str("app.resetAll.confirm"),
|
||||
confirmLabel = str("action.reset"),
|
||||
destructive = true,
|
||||
idPrefix = "settings-reset-all",
|
||||
onConfirm = {
|
||||
val defaults = com.armsx2.config.Settings()
|
||||
com.armsx2.ui.InGameOverlay.settingsState.value = defaults
|
||||
com.armsx2.config.ConfigStore.saveGlobal(defaults)
|
||||
|
||||
// Push straight to the core when a game is live, the same way the per-tab reset
|
||||
// does. Without this the UI shows defaults while the running VM keeps the old
|
||||
// values until the next boot.
|
||||
if (MainActivityRuntime.nativeReady.value &&
|
||||
MainActivityRuntime.eState.value != com.armsx2.EmuState.STOPPED) {
|
||||
runCatching { defaults.applyTo() }
|
||||
}
|
||||
|
||||
confirming = false
|
||||
},
|
||||
onDismiss = { confirming = false },
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/** Export / import everything a reinstall would destroy: save states, memory cards, artwork,
|
||||
* per-game settings, controller profiles, patches and every preference. ROMs and BIOS are left
|
||||
* out — those live outside the app and survive on their own. See [com.armsx2.BackupManager]. */
|
||||
|
||||
@@ -56,7 +56,7 @@ import kotlinx.coroutines.withContext
|
||||
import com.armsx3.NativeApp
|
||||
|
||||
@Composable
|
||||
fun PadTab(@Suppress("UNUSED_PARAMETER") state: MutableState<Settings>) {
|
||||
fun PadTab(state: MutableState<Settings>) {
|
||||
val scroll = settingsScrollState()
|
||||
ControllerAutoScroll(scroll)
|
||||
val capture = remember { mutableStateOf<ControllerMappings.Action?>(null) }
|
||||
@@ -448,6 +448,24 @@ fun PadTab(@Suppress("UNUSED_PARAMETER") state: MutableState<Settings>) {
|
||||
// (com.armsx2.ui.settings.GyroSection). Here it follows the Pad tab's Global/Game
|
||||
// scope (editSerial) and shares the tab's refreshToken so it re-reads live.
|
||||
GyroSection(editSerial = editSerial, externalRefresh = refreshToken)
|
||||
// Which face button the PS3 itself treats as "confirm" in system dialogs. This is a
|
||||
// console setting (cellSysutil ID_ENTER_BUTTON_ASSIGN), not a pad remap: it changes what
|
||||
// the GAME asks for, so it has to live in the config rather than in the bind table.
|
||||
// Japanese titles generally expect circle and can read as inverted without it.
|
||||
CollapsibleSection(str("pad.section.enterButton"), initiallyExpanded = false) {
|
||||
SegmentedRow(
|
||||
label = str("pad.enterButton.label"),
|
||||
options = listOf(str("pad.enterButton.circle"), str("pad.enterButton.cross")),
|
||||
selectedIndex = state.value.ps3.enterButtonAssign.coerceIn(0, 1),
|
||||
description = str("pad.enterButton.description"),
|
||||
onChange = { idx ->
|
||||
com.armsx2.ui.InGameOverlay.saveSettings(
|
||||
state.value.copy(ps3 = state.value.ps3.copy(enterButtonAssign = idx)),
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
CollapsibleSection(str("pad.section.buttonMapping"), initiallyExpanded = false) {
|
||||
ControllerMappings.actions.forEach { action ->
|
||||
val physical = ControllerMappings.physicalForScope(action, editPlayer.intValue, editSerial)
|
||||
@@ -899,6 +917,12 @@ private fun StickFeelSliders(left: Boolean, title: String, refreshToken: Mutable
|
||||
valueFormatter = { "${it * 5}%" },
|
||||
onChange = { ControllerMappings.setStickSensitivity(left, it / 20f); refreshToken.value++ },
|
||||
)
|
||||
|
||||
ToggleRow(
|
||||
str("pad.stickFeel.squareGate.label"),
|
||||
ControllerMappings.stickSquareGate(left),
|
||||
description = str("pad.stickFeel.squareGate.description"),
|
||||
) { ControllerMappings.setStickSquareGate(left, it); refreshToken.value++ }
|
||||
SettingsDivider()
|
||||
IntSliderRow(
|
||||
label = str("pad.stickFeel.acceleration.label"),
|
||||
|
||||
+55
-41
@@ -25,73 +25,87 @@ import org.json.JSONObject
|
||||
internal val SETTINGS_CATEGORY_FIELDS: Map<SettingsCategory, List<String>> = mapOf(
|
||||
// PerformanceTab.kt
|
||||
SettingsCategory.Performance to listOf(
|
||||
"eeClampMode", "eeCycleRate", "eeCycleSkip", "eeFpuRoundMode",
|
||||
"fastCDVD", "fpsLimit", "frameSkip", "framerateNtsc", "frameratePal",
|
||||
"intcStat", "mtvu", "nominalSpeedPercent", "skipDuplicateFrames", "vu0RoundMode",
|
||||
"vu1Instant", "vu1RoundMode", "vuClampMode", "vuDeferredWrites", "vuFlagHack",
|
||||
"vuNeonFusions", "vuSkipStallSim", "waitLoop",
|
||||
"accurateBlendingUnit", "affinityMode", "eeClampMode", "eeCycleRate", "eeCycleSkip",
|
||||
"eeFpuRoundMode", "fastCDVD", "fpsLimit", "frameSkip", "framerateNtsc", "frameratePal",
|
||||
"hwMipmap", "hwRov", "hwScaler", "intcStat", "mtvu", "nominalSpeedPercent",
|
||||
"ps3AccurateCacheLine", "ps3AccurateSpuDma", "ps3AccurateSpuRsv", "ps3ClocksScale",
|
||||
"ps3GpuTurbo", "ps3LlvmPrecompile", "ps3LlvmThreads", "ps3MaxSpursThreads",
|
||||
"ps3PpuDecoder", "ps3PreferredSpuThreads", "ps3SavestateCompatibleMode",
|
||||
"ps3SilenceAllLogs", "ps3SpuBlockSize", "ps3SpuCache", "ps3SpuDecoder",
|
||||
"ps3SpuLoopDetection", "ps3SpuXFloat", "screenResOverride", "skipDuplicateFrames",
|
||||
"texturePreloading", "upscaleFloat", "vu0RoundMode", "vu1Instant", "vu1RoundMode",
|
||||
"vuClampMode", "vuDeferredWrites", "vuFlagHack", "vuNeonFusions", "vuSkipStallSim",
|
||||
"waitLoop",
|
||||
),
|
||||
// RendererTab.kt
|
||||
SettingsCategory.Graphics to listOf(
|
||||
"accurateBlendingUnit", "adrenoFbFetch", "aspectRatio", "casMode", "casSharpness",
|
||||
"customAspectRatio", "deinterlaceMode", "displayBilinear", "dumpReplaceableTextures", "fmvAspectRatio",
|
||||
"forceMaliFbFetch", "fxaa", "gpuProfile", "gsBackThreadMode", "hardwareDownloadMode",
|
||||
"hwAa1", "hwAccurateAlphaTest", "hwMipmap", "hwRov", "loadTextureReplacements",
|
||||
"adrenoFbFetch", "aspectRatio", "autoProgressiveScan", "casMode", "casSharpness",
|
||||
"customAspectRatio", "customDriverId", "deinterlaceMode", "displayBilinear",
|
||||
"displayFitMode", "dumpReplaceableTextures", "fmvAspectRatio", "forceMaliFbFetch",
|
||||
"fxaa", "gpuProfile", "gsBackThreadMode", "hardwareDownloadMode", "hwAa1",
|
||||
"hwAccurateAlphaTest", "landscapeRenderTop", "loadTextureReplacements",
|
||||
"loadTextureReplacementsAsync", "maxAnisotropy", "orientation",
|
||||
"osdShowTextureReplacements", "portraitRenderTop", "landscapeRenderTop", "autoProgressiveScan",
|
||||
"affinityMode", "precacheTextureReplacements",
|
||||
"osdShowTextureReplacements", "portraitRenderTop", "precacheTextureReplacements",
|
||||
"ps3AnisoFilter", "ps3AsyncTexStream", "ps3DisableZcull", "ps3DisplayAspect",
|
||||
"ps3MsaaMode", "ps3MultithreadedRsx", "ps3ReadColorBuffers", "ps3ReadDepthBuffer",
|
||||
"ps3RelaxedZcull", "ps3Resolution", "ps3ShaderMode", "ps3StrictRendering",
|
||||
"ps3VramLimitMb", "ps3WriteColorBuffers", "ps3WriteDepthBuffer", "renderer",
|
||||
"shadeBoost", "shadeBoostBrightness", "shadeBoostContrast", "shadeBoostGamma",
|
||||
"shadeBoostSaturation", "shaderChainEnabled", "shaderChainParams", "shaderChainPreset",
|
||||
"textureFiltering", "texturePreloading", "triFilter", "tvShader", "upscaleFloat",
|
||||
"vsyncEnable", "displayFitMode", "ps3DisplayAspect",
|
||||
"textureFiltering", "triFilter", "tvShader", "upscaleFloat", "useAngleOpenGL",
|
||||
"vsyncEnable",
|
||||
),
|
||||
// AudioTab.kt
|
||||
SettingsCategory.Audio to listOf(
|
||||
"audioBufferMs", "audioFastForwardVolume", "audioMuted", "audioOpenSLES",
|
||||
"audioOutputLatencyMs", "audioSwapChannels", "audioTimeStretch", "audioVolume",
|
||||
"spu2LightweightMix", "spu2NeonReverb",
|
||||
"ps3AudioBufferMs", "ps3AudioChannels", "ps3AudioCubebBackend", "ps3AudioFormat",
|
||||
"ps3AudioRenderer", "ps3AudioTimeStretch", "spu2LightweightMix", "spu2NeonReverb",
|
||||
),
|
||||
// NetworkTab.kt
|
||||
SettingsCategory.Network to listOf(
|
||||
"dev9AutoGateway", "dev9AutoMask", "dev9Dns1", "dev9Dns2", "dev9EthApi", "dev9EthDevice",
|
||||
"dev9EthEnable", "dev9EthHosts", "dev9EthLogDhcp", "dev9EthLogDns", "dev9Gateway",
|
||||
"dev9HddEnable", "dev9HddFile", "dev9InterceptDhcp", "dev9Mask", "dev9ModeDns1",
|
||||
"dev9ModeDns2", "dev9Ps2Ip", "ip", "url", "usbKeyboard",
|
||||
"dev9AutoGateway", "dev9AutoMask", "dev9Dns1", "dev9Dns2", "dev9EthApi",
|
||||
"dev9EthDevice", "dev9EthEnable", "dev9EthHosts", "dev9EthLogDhcp", "dev9EthLogDns",
|
||||
"dev9Gateway", "dev9HddEnable", "dev9HddFile", "dev9InterceptDhcp", "dev9Mask",
|
||||
"dev9ModeDns1", "dev9ModeDns2", "dev9Ps2Ip", "ip", "ps3NetEnabled", "ps3PsnStatus",
|
||||
"ps3UpnpEnabled", "url", "usbKeyboard",
|
||||
),
|
||||
// OverlayTab.kt
|
||||
SettingsCategory.OnScreen to listOf(
|
||||
"osdColor", "osdScale", "osdShowCpu", "osdShowFps", "osdShowFrameTimes", "osdShowGpu",
|
||||
"osdShowGpuStats", "osdShowGsStats", "osdShowHardwareInfo", "osdShowInputs",
|
||||
"osdShowMessages", "osdShowResolution", "osdShowSettings", "osdShowSpeed",
|
||||
"osdShowVersion", "osdShowVps",
|
||||
// RPCS3's own performance overlay (the lower half of the tab).
|
||||
"ps3OverlayEnabled", "ps3OverlayDetail", "ps3OverlayPosition", "ps3OverlayFontSize",
|
||||
"ps3OverlayOpacity", "ps3OverlayFramerateGraph", "ps3OverlayFrametimeGraph",
|
||||
"ps3OverlayBodyColor", "ps3OverlayBodyBg", "ps3OverlayTitleColor", "ps3OverlayTitleBg",
|
||||
"osdShowVersion", "osdShowVps", "ps3OverlayBodyBg", "ps3OverlayBodyColor",
|
||||
"ps3OverlayDetail", "ps3OverlayEnabled", "ps3OverlayFontSize",
|
||||
"ps3OverlayFramerateGraph", "ps3OverlayFrametimeGraph", "ps3OverlayOpacity",
|
||||
"ps3OverlayPosition", "ps3OverlayTitleBg", "ps3OverlayTitleColor",
|
||||
),
|
||||
// FixesTab.kt — also owns the GameDB fixes and the recompiler toggles, which moved here
|
||||
// from Performance and from the retired Recompiler tab.
|
||||
SettingsCategory.Advanced to listOf(
|
||||
"enableFastBoot", "enableGameFixes",
|
||||
"gamefixBlitInternalFps", "gamefixDmaBusy", "gamefixEETiming", "gamefixFpuMul",
|
||||
"gamefixFullVu0Sync", "gamefixGifFifo", "gamefixGoemonTlb", "gamefixIbit",
|
||||
"gamefixInstantDma", "gamefixOphFlag", "gamefixSkipMpeg",
|
||||
"gamefixSoftwareRendererFmv", "gamefixVif1Stall", "gamefixVuAddSub",
|
||||
"gamefixVuOverflow", "gamefixVuSync", "gamefixXgkick",
|
||||
"enableFastmem", "recEE", "recIOP", "recVU0", "recVU1",
|
||||
"alignSprite", "antiBlur", "autoFlush", "autoFlushSw", "bilinearUpscale", "cpuClutRender",
|
||||
"cpuFramebufferConversion", "cpuSpriteRenderBw", "cpuSpriteRenderLevel", "cropBottom",
|
||||
"cropLeft", "cropRight", "cropTop", "displayZoom", "disableDepthEmulation", "disableFramebufferFetch",
|
||||
"disableInterlaceOffset", "disablePartialInvalidation", "disableRenderFixes",
|
||||
"disableSafeFeatures", "disableShaderCache", "disableVertexShaderExpand", "dithering",
|
||||
"drawBuffering", "estimateTextureRegion", "forceEvenSpritePosition",
|
||||
"alignSprite", "antiBlur", "autoFlush", "autoFlushSw", "bilinearUpscale",
|
||||
"cpuClutRender", "cpuFramebufferConversion", "cpuSpriteRenderBw",
|
||||
"cpuSpriteRenderLevel", "cropBottom", "cropLeft", "cropRight", "cropTop",
|
||||
"disableDepthEmulation", "disableFramebufferFetch", "disableInterlaceOffset",
|
||||
"disablePartialInvalidation", "disableRenderFixes", "disableSafeFeatures",
|
||||
"disableShaderCache", "disableVertexShaderExpand", "displayZoom", "dithering",
|
||||
"drawBuffering", "enableFastBoot", "enableFastmem", "enableGameFixes",
|
||||
"estimateTextureRegion", "forceEvenSpritePosition", "gamefixBlitInternalFps",
|
||||
"gamefixDmaBusy", "gamefixEETiming", "gamefixFpuMul", "gamefixFullVu0Sync",
|
||||
"gamefixGifFifo", "gamefixGoemonTlb", "gamefixIbit", "gamefixInstantDma",
|
||||
"gamefixOphFlag", "gamefixSkipMpeg", "gamefixSoftwareRendererFmv", "gamefixVif1Stall",
|
||||
"gamefixVuAddSub", "gamefixVuOverflow", "gamefixVuSync", "gamefixXgkick",
|
||||
"gpuPaletteConversion", "gpuTargetClut", "halfPixelOffset", "hwAccurateAlphaTest",
|
||||
"integerScaling", "limit24BitDepth", "manualUserHacks", "mergeSprite", "mipmapSw",
|
||||
"nativeScaling", "overrideTextureBarriers", "preloadFrameData", "readTargetsWhenClosing",
|
||||
"roundSprite", "screenOffsets", "showOverscan", "skipDrawEnd", "skipDrawStart",
|
||||
"spinCpuReadbacks", "spinGpuReadbacks", "swThreads", "swThreadsHeight",
|
||||
"syncToHostRefresh", "textureInsideRt", "textureOffsetX", "textureOffsetY",
|
||||
"unscaledPaletteDraw", "useBlitSwapChain", "vsyncQueueSize",
|
||||
"nativeScaling", "overrideTextureBarriers", "preloadFrameData", "ps3AccurateCacheLine",
|
||||
"ps3AccurateDfma", "ps3AccurateRsxRsv", "ps3AccurateSpuRsv", "ps3DebugConsoleMode",
|
||||
"ps3HleLwmutex", "ps3PpuNanHandling", "ps3PpuRsvPriority", "ps3PreciseSpuVerification",
|
||||
"ps3SetDazFtz", "ps3SleepTimers", "ps3SpuVerification", "ps3SpuXFloat",
|
||||
"readTargetsWhenClosing", "recEE", "recIOP", "recVU0", "recVU1", "roundSprite",
|
||||
"screenOffsets", "showOverscan", "skipDrawEnd", "skipDrawStart", "spinCpuReadbacks",
|
||||
"spinGpuReadbacks", "swThreads", "swThreadsHeight", "syncToHostRefresh",
|
||||
"textureInsideRt", "textureOffsetX", "textureOffsetY", "unscaledPaletteDraw",
|
||||
"useBlitSwapChain", "vsyncQueueSize",
|
||||
),
|
||||
// Controls / Hotkeys / Skins / General / Info / Patches / About own no Settings fields —
|
||||
// Controls keeps its binds and tunables in ControllerMappings and has its own reset row.
|
||||
|
||||
@@ -2174,6 +2174,19 @@ error_code sys_fs_unlink(ppu_thread& ppu, vm::cptr<char> path)
|
||||
{
|
||||
return {mp == &g_mp_sys_dev_hdd1 ? sys_fs.warning : sys_fs.error, CELL_ENOENT, path};
|
||||
}
|
||||
case fs::error::readonly:
|
||||
{
|
||||
// A virtual device that cannot be modified. On Android that is the mounted ISO behind
|
||||
// /app_home, whose device_base mutators report readonly by design -- so any game that
|
||||
// writes, renames or deletes inside its own USRDIR lands here. Oblivion deletes
|
||||
// /app_home/warnings.txt at startup, which a real console would simply allow.
|
||||
//
|
||||
// This used to fall through to the throw below, which killed the PPU thread inside the
|
||||
// syscall: the game froze with the CPU idle and nothing in the log but a stalled RSX.
|
||||
// Report it and let the guest decide what to do, which is what hardware would do for a
|
||||
// write to read-only media.
|
||||
return { CELL_EROFS, path };
|
||||
}
|
||||
default:
|
||||
{
|
||||
if (has_non_directory_components(local_path, ends_with_delim_dot_or_dotdot(vpath)))
|
||||
@@ -2425,7 +2438,9 @@ error_code sys_fs_fcntl(ppu_thread& ppu, u32 fd, u32 op, vm::ptr<void> _arg, u32
|
||||
// Load mountpoint (doesn't support multiple // at the start)
|
||||
std::string_view vpath{arg->name.get_ptr(), arg->name_size};
|
||||
|
||||
sys_fs.notice("sys_fs_fcntl(0xc0000006): %s", vpath);
|
||||
// Trace for the same reason as sys_fs_utime: this rides along with the utime polling
|
||||
// loop and made up the last third of that flood.
|
||||
sys_fs.trace("sys_fs_fcntl(0xc0000006): %s", vpath);
|
||||
|
||||
// Check only mountpoint
|
||||
vpath = vpath.substr(0, vpath.find_first_of('\0'));
|
||||
@@ -3087,6 +3102,19 @@ error_code sys_fs_truncate(ppu_thread& ppu, vm::cptr<char> path, u64 size)
|
||||
{
|
||||
return {mp == &g_mp_sys_dev_hdd1 ? sys_fs.warning : sys_fs.error, CELL_ENOENT, path};
|
||||
}
|
||||
case fs::error::readonly:
|
||||
{
|
||||
// A virtual device that cannot be modified. On Android that is the mounted ISO behind
|
||||
// /app_home, whose device_base mutators report readonly by design -- so any game that
|
||||
// writes, renames or deletes inside its own USRDIR lands here. Oblivion deletes
|
||||
// /app_home/warnings.txt at startup, which a real console would simply allow.
|
||||
//
|
||||
// This used to fall through to the throw below, which killed the PPU thread inside the
|
||||
// syscall: the game froze with the CPU idle and nothing in the log but a stalled RSX.
|
||||
// Report it and let the guest decide what to do, which is what hardware would do for a
|
||||
// write to read-only media.
|
||||
return { CELL_EROFS, path };
|
||||
}
|
||||
default:
|
||||
{
|
||||
if (has_non_directory_components(local_path, ends_with_delim_dot_or_dotdot(vpath)))
|
||||
@@ -3311,8 +3339,13 @@ error_code sys_fs_utime(ppu_thread& ppu, vm::cptr<char> path, vm::cptr<CellFsUti
|
||||
{
|
||||
lv2_obj::sleep(ppu);
|
||||
|
||||
sys_fs.warning("sys_fs_utime(path=%s, timep=*0x%x)", path, timep);
|
||||
sys_fs.warning("** actime=%u, modtime=%u", timep->actime, timep->modtime);
|
||||
// Trace, not warning. Setting a file's timestamps is routine and uninteresting, but games
|
||||
// that poll it do so in tight loops: Oblivion's FileCaching thread hit this 7274 times in ten
|
||||
// seconds on one .BSA, and at two warning lines a call that alone stalled the emulator for
|
||||
// over twenty seconds. Logging is not free on Android. Raise the sys_fs channel to Trace to
|
||||
// get these back.
|
||||
sys_fs.trace("sys_fs_utime(path=%s, timep=*0x%x)", path, timep);
|
||||
sys_fs.trace("** actime=%u, modtime=%u", timep->actime, timep->modtime);
|
||||
|
||||
const auto [path_error, vpath] = translate_to_str(path);
|
||||
|
||||
@@ -3354,6 +3387,19 @@ error_code sys_fs_utime(ppu_thread& ppu, vm::cptr<char> path, vm::cptr<CellFsUti
|
||||
{
|
||||
return {mp == &g_mp_sys_dev_hdd1 ? sys_fs.warning : sys_fs.error, CELL_ENOENT, path};
|
||||
}
|
||||
case fs::error::readonly:
|
||||
{
|
||||
// A virtual device that cannot be modified. On Android that is the mounted ISO behind
|
||||
// /app_home, whose device_base mutators report readonly by design -- so any game that
|
||||
// writes, renames or deletes inside its own USRDIR lands here. Oblivion deletes
|
||||
// /app_home/warnings.txt at startup, which a real console would simply allow.
|
||||
//
|
||||
// This used to fall through to the throw below, which killed the PPU thread inside the
|
||||
// syscall: the game froze with the CPU idle and nothing in the log but a stalled RSX.
|
||||
// Report it and let the guest decide what to do, which is what hardware would do for a
|
||||
// write to read-only media.
|
||||
return { CELL_EROFS, path };
|
||||
}
|
||||
default:
|
||||
{
|
||||
if (has_non_directory_components(local_path, ends_with_delim_dot_or_dotdot(vpath)))
|
||||
|
||||
@@ -614,7 +614,10 @@ error_code sys_mmapper_map_shared_memory(ppu_thread& ppu, u32 addr, u32 mem_id,
|
||||
{
|
||||
ppu.state += cpu_flag::wait;
|
||||
|
||||
sys_mmapper.warning("sys_mmapper_map_shared_memory(addr=0x%x, mem_id=0x%x, flags=0x%x)", addr, mem_id, flags);
|
||||
// Trace, not warning: a successful map is routine, and games that cycle shared memory do it
|
||||
// thousands of times a second. Oblivion logged 6476 of these in ten seconds, which on Android
|
||||
// costs more than the mapping itself. Raise the sys_mmapper channel to Trace to get them back.
|
||||
sys_mmapper.trace("sys_mmapper_map_shared_memory(addr=0x%x, mem_id=0x%x, flags=0x%x)", addr, mem_id, flags);
|
||||
|
||||
const auto area = vm::get(vm::any, addr);
|
||||
|
||||
@@ -765,7 +768,8 @@ error_code sys_mmapper_unmap_shared_memory(ppu_thread& ppu, u32 addr, vm::ptr<u3
|
||||
{
|
||||
ppu.state += cpu_flag::wait;
|
||||
|
||||
sys_mmapper.warning("sys_mmapper_unmap_shared_memory(addr=0x%x, mem_id=*0x%x)", addr, mem_id);
|
||||
// Pairs with the map above; same reasoning.
|
||||
sys_mmapper.trace("sys_mmapper_unmap_shared_memory(addr=0x%x, mem_id=*0x%x)", addr, mem_id);
|
||||
|
||||
const auto area = vm::get(vm::any, addr);
|
||||
|
||||
|
||||
+16
-1
@@ -1194,7 +1194,22 @@ namespace vm
|
||||
|
||||
if (!utils::memory_lock(g_sudo_addr + addr, size))
|
||||
{
|
||||
vm_log.error("Failed to lock sudo memory (addr=0x%x, size=0x%x). Consider increasing your system limits.", addr, size);
|
||||
// Report this once per session, not once per call.
|
||||
//
|
||||
// Android does not grant RLIMIT_MEMLOCK to ordinary apps, so this fails for every
|
||||
// mapping and cannot be "fixed" by the user the message tells to raise their limits.
|
||||
// Games that map and unmap shared memory in a loop then drown the log in it --
|
||||
// Oblivion's BSTaskManagerThread produced 6470 of these in ten seconds, and writing
|
||||
// them is expensive enough on Android to stall the emulator outright.
|
||||
//
|
||||
// The first one still says what happened; the rest are the same fact repeated.
|
||||
static atomic_t<bool> s_reported{false};
|
||||
|
||||
if (!s_reported.exchange(true))
|
||||
{
|
||||
vm_log.error("Failed to lock sudo memory (addr=0x%x, size=0x%x). Consider increasing your system limits."
|
||||
" Further failures will not be reported.", addr, size);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -744,10 +744,15 @@ namespace rsx
|
||||
// why it is game- and timing-dependent rather than reliable.
|
||||
fifo_ctrl->sync_get_force();
|
||||
|
||||
// Spin budget for the idle wait below. Reset whenever a fresh idle period starts, so
|
||||
// each drain gets its own short hot window before parking.
|
||||
static thread_local u32 s_fifo_idle_spins = 0;
|
||||
|
||||
if (performance_counters.state == FIFO::state::running)
|
||||
{
|
||||
performance_counters.FIFO_idle_timestamp = get_system_time();
|
||||
performance_counters.state = FIFO::state::empty;
|
||||
s_fifo_idle_spins = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -776,8 +781,36 @@ namespace rsx
|
||||
// It has now caused two wrong conclusions in one session: once reading a
|
||||
// starving RSX as CPU-bound decode, and once reading a thread stuck in an
|
||||
// occlusion query wait as the same thing.
|
||||
// UPDATE: the yield is now itself the problem, and it is measured. A native
|
||||
// profile of Arkham City gameplay put ~11% of TOTAL process CPU in sched_yield
|
||||
// reached from here -- 93% of the RSX thread's kernel time, and its single largest
|
||||
// cost. sched_yield is close to the worst available wait on this device: it is a
|
||||
// syscall, it forces a scheduler pass, and with ~14 hot threads over 8 cores it is
|
||||
// usually rescheduled immediately -- so it burns a core one of the five SPU threads
|
||||
// actually wants, while doing nothing to notice the guest sooner.
|
||||
//
|
||||
// A short hot spin first, so a PUT that lands within microseconds is still caught
|
||||
// without paying any wake latency; only sustained idle parks. WFE costs no syscall
|
||||
// and the architected event stream bounds the park to tens of microseconds, so the
|
||||
// RSX still sits on the frame's dependency chain rather than sleeping through work.
|
||||
//
|
||||
// The pre-spin is not optional: ouroboros420/rpcsx parked bare here (e31ef44ef) and
|
||||
// had to walk it back (832c23078) when the wake latency cost frametime smoothness.
|
||||
RSX_PROF_SCOPE(idle);
|
||||
|
||||
#if defined(ARCH_ARM64)
|
||||
if (s_fifo_idle_spins < 8)
|
||||
{
|
||||
s_fifo_idle_spins++;
|
||||
utils::pause();
|
||||
}
|
||||
else
|
||||
{
|
||||
utils::wait_for_event();
|
||||
}
|
||||
#else
|
||||
std::this_thread::yield();
|
||||
#endif
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
@@ -15,7 +15,12 @@
|
||||
#include "gcm_printing.h"
|
||||
#include "RSXDisAsm.h"
|
||||
|
||||
#ifdef __ANDROID__
|
||||
#include <unistd.h> // ::gettid() for the ADPF feed
|
||||
#endif
|
||||
|
||||
#include "Emu/System.h"
|
||||
#include "Emu/system_utils.hpp"
|
||||
#include "Emu/Cell/PPUThread.h"
|
||||
#include "Emu/Cell/SPUThread.h"
|
||||
#include "Emu/Cell/timers.hpp"
|
||||
@@ -2778,6 +2783,55 @@ namespace rsx
|
||||
{
|
||||
m_eng_interrupt_mask.clear(rsx::display_interrupt);
|
||||
|
||||
#ifdef __ANDROID__
|
||||
// ADPF feed: publish this frame's real CPU cost and the presenting thread's OS tid so
|
||||
// the app can drive PerformanceHintManager. Measured at a fixed point each iteration, so
|
||||
// the previous iteration's frame-limiter sleep lands in the idle delta and is excluded.
|
||||
// Advisory only: these go to atomics nothing in the core reads back.
|
||||
// Ported from ouroboros420/rpcsx (3d4ba6060).
|
||||
{
|
||||
static thread_local u64 s_last_now = 0;
|
||||
static thread_local u64 s_last_idle = 0;
|
||||
static thread_local s32 s_tid = 0;
|
||||
|
||||
if (s_tid == 0)
|
||||
{
|
||||
s_tid = static_cast<s32>(::gettid());
|
||||
}
|
||||
|
||||
// Republished every flip so a recreated RSX thread overwrites a stale tid, rather
|
||||
// than leaving the app's hint session pointed at a dead thread after a restart.
|
||||
rpcs3::utils::set_rsx_thread_tid(s_tid);
|
||||
|
||||
const u64 now_us = get_system_time();
|
||||
const u64 idle_us = performance_counters.idle_time.load();
|
||||
|
||||
if (s_last_now != 0 && now_us > s_last_now)
|
||||
{
|
||||
const u64 wall = now_us - s_last_now;
|
||||
|
||||
// The flip-to-flip deadline. Without it the hint judges a 30fps game against a
|
||||
// 60fps target and over-boosts, which is pure heat.
|
||||
rpcs3::utils::report_frame_period_ns(wall * 1000);
|
||||
|
||||
// idle_time is reset periodically by get_load(), so a delta that went backwards is
|
||||
// a reset, not a real frame. Idle can also exceed the wall window (it accrues from
|
||||
// FIFO/semaphore paths). Reporting work == wall in either case would feed a bogus
|
||||
// fully-busy sample and over-boost; skipping leaves the last good one in place.
|
||||
if (idle_us >= s_last_idle)
|
||||
{
|
||||
if (const u64 idle_delta = idle_us - s_last_idle; idle_delta < wall)
|
||||
{
|
||||
rpcs3::utils::report_frame_work_ns((wall - idle_delta) * 1000);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
s_last_now = now_us;
|
||||
s_last_idle = idle_us;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (async_flip_requested & flip_request::any)
|
||||
{
|
||||
// Deferred flip
|
||||
|
||||
@@ -314,7 +314,22 @@ namespace vk
|
||||
shader_types_support.allow_float16 = (driver_properties.driverID == VK_DRIVER_ID_AMD_PROPRIETARY_KHR);
|
||||
}
|
||||
|
||||
if (is_MOBILE(get_driver_vendor()) && shader_types_support.allow_float16)
|
||||
// Qualcomm's compiler stopped rejecting native float16 at some point, and the blanket
|
||||
// disable below now costs more than it saves: emulating fp16 with fp32 does NOT "render
|
||||
// correctly" as claimed -- Oblivion's water simply does not draw on Vulkan, while the GL
|
||||
// backend (which has no such workaround) draws it. Verified fixed by allowing fp16 on
|
||||
// driver 512.676.53.
|
||||
//
|
||||
// Gated on the version rather than removed. The original failure is a bad one to
|
||||
// reintroduce -- every pipeline rejected, so the game is black while audio and the
|
||||
// compile overlay keep working, which reads as a renderer bug rather than a shader one --
|
||||
// and older Adreno drivers may still be affected. Only Adreno is opened up: no other
|
||||
// mobile vendor has been tested either way, so they keep the safe path.
|
||||
constexpr u32 s_adreno_fp16_min_driver = (512u << 22) | (676u << 12) | 53u; // 512.676.53
|
||||
const bool adreno_fp16_ok = is_ADRENO(get_driver_vendor()) &&
|
||||
props.driverVersion >= s_adreno_fp16_min_driver;
|
||||
|
||||
if (!adreno_fp16_ok && is_MOBILE(get_driver_vendor()) && shader_types_support.allow_float16)
|
||||
{
|
||||
// Adreno advertises shaderFloat16, but its shader compiler rejects the
|
||||
// SPIR-V RPCS3 generates with native float16_t in it -- every game
|
||||
|
||||
@@ -264,7 +264,15 @@ struct cfg_root : cfg::node
|
||||
{
|
||||
node_audio(cfg::node* _this) : cfg::node(_this, "Audio") {}
|
||||
|
||||
#ifdef __ANDROID__
|
||||
// Oboe by default here. Cubeb reaches AAudio too, but Oboe is what carries the per-device
|
||||
// quirks database, the automatic AAudio -> OpenSL ES fallback on parts where AAudio
|
||||
// misbehaves, and error-callback stream recovery when the route changes or the device
|
||||
// disconnects -- all of which are the normal case on a handheld, not the exception.
|
||||
cfg::_enum<audio_renderer> renderer{ this, "Renderer", audio_renderer::oboe, true };
|
||||
#else
|
||||
cfg::_enum<audio_renderer> renderer{ this, "Renderer", audio_renderer::cubeb, true };
|
||||
#endif
|
||||
cfg::_enum<audio_provider> provider{ this, "Audio Provider", audio_provider::cell_audio, false };
|
||||
cfg::_enum<audio_avport> rsxaudio_port{ this, "RSXAudio Avport", audio_avport::hdmi_0, true };
|
||||
cfg::_bool dump_to_file{ this, "Dump to file", false, true };
|
||||
|
||||
Reference in New Issue
Block a user