mirror of
https://git.ryujinx.app/kenji-nx/ryujinx.git
synced 2025-12-13 04:37:02 +00:00
changed comments to english
This commit is contained in:
parent
d28cfb1c94
commit
77dea2787f
10 changed files with 79 additions and 81 deletions
|
|
@ -17,7 +17,7 @@
|
|||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
||||
<!-- Notifications + special-use Foreground Service (Android 14+) -->
|
||||
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
|
||||
<!-- Erforderlich für startForeground() -->
|
||||
<!-- Required for startForeground() -->
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK"/>
|
||||
<uses-permission android:name="android.permission.WAKE_LOCK"/>
|
||||
|
|
@ -88,8 +88,6 @@
|
|||
android:exported="false"
|
||||
android:stopWithTask="true"
|
||||
android:foregroundServiceType="mediaPlayback" />
|
||||
|
||||
|
||||
</application>
|
||||
|
||||
</manifest>
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ class GameHost(context: Context?, private val mainViewModel: MainViewModel) : Su
|
|||
|
||||
private val mainHandler = Handler(Looper.getMainLooper())
|
||||
|
||||
// ---- Foreground-Service Binding ----
|
||||
// ---- Foreground service binding ----
|
||||
private var emuBound = false
|
||||
private var emuBinder: EmulationService.LocalBinder? = null
|
||||
private var _startedViaService = false
|
||||
|
|
@ -52,7 +52,7 @@ class GameHost(context: Context?, private val mainViewModel: MainViewModel) : Su
|
|||
emuBound = true
|
||||
ghLog("EmulationService bound")
|
||||
|
||||
// Falls Start bereits vorbereitet wurde und noch kein Loop läuft → jetzt im Service starten
|
||||
// If startup is already prepared and no loop is running yet → start it in the service now
|
||||
if (_isStarted && !_startedViaService && _guestThread == null) {
|
||||
startRunLoopInService()
|
||||
}
|
||||
|
|
@ -66,7 +66,7 @@ class GameHost(context: Context?, private val mainViewModel: MainViewModel) : Su
|
|||
}
|
||||
}
|
||||
|
||||
// Resize-Stabilizer
|
||||
// Resize stabilizer
|
||||
private var stabilizerActive = false
|
||||
|
||||
// last known Android rotation (0,1,2,3)
|
||||
|
|
@ -143,7 +143,7 @@ class GameHost(context: Context?, private val mainViewModel: MainViewModel) : Su
|
|||
|
||||
override fun surfaceCreated(holder: SurfaceHolder) {
|
||||
ghLog("surfaceCreated")
|
||||
// Früh binden, damit der Service schon steht, bevor wir starten
|
||||
// Bind early so the service is ready before we start
|
||||
ensureServiceStartedAndBound()
|
||||
rebindNativeWindow(force = true)
|
||||
}
|
||||
|
|
@ -152,26 +152,26 @@ class GameHost(context: Context?, private val mainViewModel: MainViewModel) : Su
|
|||
ghLog("surfaceChanged ${width}x$height")
|
||||
if (_isClosed) return
|
||||
|
||||
// IMMER neu binden – auch wenn die Größe gleich bleibt
|
||||
// ALWAYS rebind—even if the size stays the same
|
||||
rebindNativeWindow(force = true)
|
||||
|
||||
val sizeChanged = (_width != width || _height != height)
|
||||
_width = width
|
||||
_height = height
|
||||
|
||||
// Service sicherstellen & Renderstart
|
||||
// Ensure service is running & start rendering
|
||||
ensureServiceStartedAndBound()
|
||||
start(holder)
|
||||
|
||||
// Resize stabilisieren (übernimmt plausibles final size set)
|
||||
// Stabilize resize (applies plausible final size)
|
||||
startStabilizedResize(expectedRotation = lastRotation)
|
||||
}
|
||||
|
||||
override fun surfaceDestroyed(holder: SurfaceHolder) {
|
||||
ghLog("surfaceDestroyed → shutdownBinding()")
|
||||
// Immer binden lösen (verhindert Leaks beim Task-Swipe)
|
||||
// Always unbind (prevents leaks when swiping away the task)
|
||||
shutdownBinding()
|
||||
// Eigentliche Emu-Beendigung passiert via close() / Exit Game
|
||||
// Actual emulation shutdown happens via close() / Exit Game
|
||||
}
|
||||
|
||||
override fun onWindowVisibilityChanged(visibility: Int) {
|
||||
|
|
@ -214,12 +214,12 @@ class GameHost(context: Context?, private val mainViewModel: MainViewModel) : Su
|
|||
private fun start(surfaceHolder: SurfaceHolder) {
|
||||
if (_isStarted) return
|
||||
|
||||
// NICHT gleich _isStarted = true → erst alles vorbereiten
|
||||
// Do NOT set _isStarted = true immediately → prepare everything first
|
||||
rebindNativeWindow(force = true)
|
||||
|
||||
game = if (mainViewModel.isMiiEditorLaunched) null else mainViewModel.gameModel
|
||||
|
||||
// Input initialisieren
|
||||
// Initialize input
|
||||
KenjinxNative.inputInitialize(width, height)
|
||||
_inputInitialized = true
|
||||
|
||||
|
|
@ -235,7 +235,7 @@ class GameHost(context: Context?, private val mainViewModel: MainViewModel) : Su
|
|||
KenjinxNative.setSurfaceRotationByAndroidRotation(currentRot ?: 0)
|
||||
try { KenjinxNative.deviceSetWindowHandle(currentWindowHandle) } catch (_: Throwable) {}
|
||||
|
||||
// Sanfter Kick nur wenn Renderer READY **und** Input init
|
||||
// Gentle kick only when renderer is READY **and** input is initialized
|
||||
if (width > 0 && height > 0 &&
|
||||
MainActivity.mainViewModel?.rendererReady == true &&
|
||||
_inputInitialized
|
||||
|
|
@ -247,10 +247,10 @@ class GameHost(context: Context?, private val mainViewModel: MainViewModel) : Su
|
|||
val qs = org.kenjinx.android.viewmodels.QuickSettings(mainViewModel.activity)
|
||||
try { KenjinxNative.graphicsSetFullscreenStretch(qs.stretchToFullscreen) } catch (_: Throwable) {}
|
||||
|
||||
// Host gilt nun als „gestartet“
|
||||
// Host is now considered 'started'
|
||||
_isStarted = true
|
||||
|
||||
// Immer bevorzugt im Service starten; wenn Bind noch nicht fertig → kurz warten, dann fallback
|
||||
// Prefer starting in the service; if binding isn't ready yet → wait briefly, then fall back
|
||||
if (emuBound) {
|
||||
startRunLoopInService()
|
||||
} else {
|
||||
|
|
@ -260,7 +260,7 @@ class GameHost(context: Context?, private val mainViewModel: MainViewModel) : Su
|
|||
if (emuBound) {
|
||||
startRunLoopInService()
|
||||
} else {
|
||||
// Fallback: lokaler Thread (sollte selten passieren)
|
||||
// Fallback: local thread (should be rare)
|
||||
ghLog("Fallback: starting RunLoop in local thread")
|
||||
_guestThread = thread(start = true, name = "KenjinxGuest") { runGame() }
|
||||
}
|
||||
|
|
@ -303,7 +303,7 @@ class GameHost(context: Context?, private val mainViewModel: MainViewModel) : Su
|
|||
|
||||
KenjinxNative.uiHandlerSetResponse(false, "")
|
||||
|
||||
// Emulation im Service stoppen (falls dort gestartet)
|
||||
// Stop emulation in the service (if started there)
|
||||
try {
|
||||
if (emuBound && _startedViaService) {
|
||||
emuBinder?.stopEmulation {
|
||||
|
|
@ -312,14 +312,14 @@ class GameHost(context: Context?, private val mainViewModel: MainViewModel) : Su
|
|||
}
|
||||
} catch (_: Throwable) { }
|
||||
|
||||
// Fallback: lokaler Thread beenden
|
||||
// Fallback: stop local thread
|
||||
try { _updateThread?.join(200) } catch (_: Throwable) {}
|
||||
try { _renderingThreadWatcher?.join(200) } catch (_: Throwable) {}
|
||||
|
||||
// Bindung lösen
|
||||
// Unbind
|
||||
shutdownBinding()
|
||||
|
||||
// Service explizit beenden (falls noch läuft)
|
||||
// Explicitly stop the service (if still running)
|
||||
try {
|
||||
mainViewModel.activity.stopService(Intent(mainViewModel.activity, EmulationService::class.java))
|
||||
} catch (_: Throwable) { }
|
||||
|
|
@ -429,7 +429,7 @@ class GameHost(context: Context?, private val mainViewModel: MainViewModel) : Su
|
|||
|
||||
attempts++
|
||||
|
||||
// 1 stabiler Tick oder max. 12 Versuche
|
||||
// One stable tick or max. 12 attempts
|
||||
if ((stableCount >= 1 || attempts >= 12) && w > 0 && h > 0) {
|
||||
ghLog("resize stabilized after $attempts ticks → ${w}x$h")
|
||||
safeSetSize(w, h)
|
||||
|
|
|
|||
|
|
@ -99,7 +99,7 @@ interface KenjinxNativeJna : Library {
|
|||
fun amiiboLoadBin(bytes: ByteArray, length: Int): Boolean
|
||||
fun amiiboClear()
|
||||
|
||||
// AUDIO (neu): direkte JNA-Brücke zu C#-Exports
|
||||
// AUDIO (new): direct JNA bridge to C# exports
|
||||
fun audioSetPaused(paused: Boolean)
|
||||
fun audioSetMuted(muted: Boolean)
|
||||
}
|
||||
|
|
@ -115,7 +115,7 @@ object KenjinxNative : KenjinxNativeJna by jnaInstance {
|
|||
fun loggingSetEnabled(logLevel: LogLevel, enabled: Boolean) =
|
||||
loggingSetEnabled(logLevel.ordinal, enabled)
|
||||
|
||||
// --- Rendering: Single-Thread-Option & sichere Wrapper --------------------
|
||||
// --- Rendering: single-thread option & safe wrappers --------------------
|
||||
|
||||
// 0 = Auto, 1 = SingleThread (Disable Threaded), 2 = Threaded
|
||||
private const val THREADING_AUTO = 0
|
||||
|
|
@ -142,7 +142,7 @@ object KenjinxNative : KenjinxNativeJna by jnaInstance {
|
|||
try { jnaInstance.graphicsSetPresentEnabled(enabled) } catch (_: Throwable) { /* ignore */ }
|
||||
}
|
||||
|
||||
// Sichere deviceResize-Implementierung (reines Rendering)
|
||||
// Safe deviceResize implementation (rendering-only)
|
||||
override fun deviceResize(width: Int, height: Int) {
|
||||
try {
|
||||
graphicsRendererSetSize(width, height)
|
||||
|
|
@ -150,7 +150,7 @@ object KenjinxNative : KenjinxNativeJna by jnaInstance {
|
|||
} catch (_: Throwable) { /* ignore */ }
|
||||
}
|
||||
|
||||
// Robustes graphicsInitialize mit QCOM-Heuristik + Fallback → SingleThread
|
||||
// Robust graphicsInitialize with QCOM heuristic + fallback → SingleThread
|
||||
override fun graphicsInitialize(
|
||||
rescale: Float,
|
||||
maxAnisotropy: Float,
|
||||
|
|
@ -165,8 +165,8 @@ object KenjinxNative : KenjinxNativeJna by jnaInstance {
|
|||
val requested = backendThreading
|
||||
val isQcom = "qcom".equals(android.os.Build.HARDWARE, true)
|
||||
|
||||
// Heuristik: Auf QCOM bei „Auto“ zunächst SingleThread probieren,
|
||||
// explizit gesetzte Werte bleiben unberührt.
|
||||
// Heuristic: On QCOM with “Auto”, first try SingleThread;
|
||||
// explicitly set values remain untouched.
|
||||
val firstChoice =
|
||||
if (isQcom && requested == THREADING_AUTO) THREADING_SINGLE else requested
|
||||
|
||||
|
|
@ -212,7 +212,7 @@ object KenjinxNative : KenjinxNativeJna by jnaInstance {
|
|||
}
|
||||
}
|
||||
|
||||
// --- optionale Wrapper für Audio (safer logging) ---
|
||||
// --- optional wrappers for audio (safer logging) ---
|
||||
override fun audioSetPaused(paused: Boolean) {
|
||||
try { jnaInstance.audioSetPaused(paused) }
|
||||
catch (t: Throwable) { Log.w("KenjinxNative", "audioSetPaused unavailable", t) }
|
||||
|
|
|
|||
|
|
@ -60,7 +60,7 @@ class MainActivity : BaseActivity() {
|
|||
var storageHelper: SimpleStorageHelper? = null
|
||||
lateinit var uiHandler: UiHandler
|
||||
|
||||
// Persistenz für Zombie-Erkennung
|
||||
// Persistence for zombie detection
|
||||
private val PREFS = "emu_core"
|
||||
private val KEY_EMU_RUNNING = "emu_running"
|
||||
|
||||
|
|
@ -297,7 +297,7 @@ class MainActivity : BaseActivity() {
|
|||
WindowCompat.setDecorFitsSystemWindows(window, false)
|
||||
window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
|
||||
|
||||
// Apply alignment
|
||||
// Apply orientation preference
|
||||
applyOrientationPreference()
|
||||
|
||||
WindowInsetsControllerCompat(window, window.decorView).let { controller ->
|
||||
|
|
@ -361,9 +361,9 @@ class MainActivity : BaseActivity() {
|
|||
|
||||
// --- Audio foreground/background gating ---
|
||||
private fun setAudioForegroundState(inForeground: Boolean) {
|
||||
// bevorzugt: pausieren statt nur muten
|
||||
// Prefer: pause instead of just mute
|
||||
try { KenjinxNative.audioSetPaused(!inForeground) } catch (_: Throwable) {}
|
||||
// fallback: Master-Mute
|
||||
// Fallback: master mute
|
||||
try { KenjinxNative.audioSetMuted(!inForeground) } catch (_: Throwable) {}
|
||||
}
|
||||
|
||||
|
|
@ -392,7 +392,7 @@ class MainActivity : BaseActivity() {
|
|||
setPresentEnabled(false, "onStop")
|
||||
try { KenjinxNative.detachWindow() } catch (_: Throwable) {}
|
||||
}
|
||||
// WICHTIG: Bindung sicher lösen (verhindert Leak)
|
||||
// IMPORTANT: unbind safely (prevents leak)
|
||||
try { mainViewModel?.gameHost?.shutdownBinding() } catch (_: Throwable) {}
|
||||
}
|
||||
|
||||
|
|
@ -472,7 +472,7 @@ class MainActivity : BaseActivity() {
|
|||
|
||||
if (hasFocus && isActive) {
|
||||
setAudioForegroundState(true)
|
||||
// NEU: zuerst sicherstellen, dass die Bindung existiert
|
||||
// NEW: first ensure that the binding exists
|
||||
try { mainViewModel?.gameHost?.ensureServiceStartedAndBound() } catch (_: Throwable) {}
|
||||
|
||||
setPresentEnabled(false, "focus gained → pre-rebind")
|
||||
|
|
@ -506,7 +506,7 @@ class MainActivity : BaseActivity() {
|
|||
try { displayManager.unregisterDisplayListener(displayListener) } catch (_: Throwable) {}
|
||||
try { unregisterReceiver(serviceStopReceiver) } catch (_: Throwable) {}
|
||||
|
||||
// NEU: Bindung aufräumen (verhindert Leak beim Task-Swipe)
|
||||
// NEW: clean up binding (prevents leak when swiping away the task)
|
||||
try { mainViewModel?.gameHost?.shutdownBinding() } catch (_: Throwable) {}
|
||||
}
|
||||
|
||||
|
|
@ -617,7 +617,7 @@ class MainActivity : BaseActivity() {
|
|||
val prefs = PreferenceManager.getDefaultSharedPreferences(this)
|
||||
val legacyPath = prefs.getString("gameFolder", null)
|
||||
if (!legacyPath.isNullOrEmpty()) {
|
||||
// Ohne SAF-URI kein Tree-Listing möglich
|
||||
// Without a SAF URI, tree listing is not possible
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
|
@ -649,7 +649,7 @@ class MainActivity : BaseActivity() {
|
|||
override fun onDestroy() {
|
||||
handler.removeCallbacks(enablePresentWhenReady)
|
||||
handler.removeCallbacks(reattachWindowWhenReady)
|
||||
// NEU: falls die Activity stirbt → Bindung garantiert lösen
|
||||
// NEW: if the activity dies → ensure unbinding
|
||||
try { mainViewModel?.gameHost?.shutdownBinding() } catch (_: Throwable) {}
|
||||
super.onDestroy()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ import java.util.concurrent.Future
|
|||
import java.util.concurrent.atomic.AtomicBoolean
|
||||
|
||||
/**
|
||||
* Foreground-Service für stabile Emulation im Hintergrund.
|
||||
* Foreground service for stable emulation in the background.
|
||||
* Manifest: android:foregroundServiceType="mediaPlayback"
|
||||
*/
|
||||
class EmulationService : Service() {
|
||||
|
|
@ -41,7 +41,7 @@ class EmulationService : Service() {
|
|||
private lateinit var executor: ExecutorService
|
||||
private var future: Future<*>? = null
|
||||
private val running = AtomicBoolean(false)
|
||||
// Nur wenn eine Emulation wirklich lief, dürfen wir nativ „hard close“ machen
|
||||
// Only if an emulation actually ran, we are allowed to perform a native “hard close”
|
||||
private val startedOnce = AtomicBoolean(false)
|
||||
|
||||
override fun onCreate() {
|
||||
|
|
@ -70,7 +70,7 @@ class EmulationService : Service() {
|
|||
override fun onTaskRemoved(rootIntent: Intent?) {
|
||||
super.onTaskRemoved(rootIntent)
|
||||
try { future?.cancel(true) } catch (_: Throwable) {}
|
||||
// Nur schließen, wenn zuvor gestartet
|
||||
// Only close if it was previously started
|
||||
hardCloseNativeIfStarted("onTaskRemoved")
|
||||
running.set(false)
|
||||
stopForeground(STOP_FOREGROUND_REMOVE)
|
||||
|
|
@ -87,17 +87,17 @@ class EmulationService : Service() {
|
|||
try { sendBroadcast(Intent(ACTION_STOPPED).setPackage(packageName)) } catch (_: Throwable) {}
|
||||
}
|
||||
|
||||
// ---- Steuerung via Binder ----
|
||||
// ---- Control via Binder ----
|
||||
|
||||
private fun startEmulation(runLoopBlock: () -> Unit) {
|
||||
// Nur einen RunLoop zulassen
|
||||
// Allow only a single run loop
|
||||
if (!running.compareAndSet(false, true)) return
|
||||
|
||||
future = executor.submit {
|
||||
try {
|
||||
// *** Kein Preflight-HardClose mehr! *** (crasht beim allerersten Start)
|
||||
// *** No preflight hard-close anymore! *** (crashes on the very first start)
|
||||
startedOnce.set(true)
|
||||
runLoopBlock() // blockiert bis Emulation endet
|
||||
runLoopBlock() // blocks until emulation ends
|
||||
} finally {
|
||||
startedOnce.set(false)
|
||||
running.set(false)
|
||||
|
|
@ -125,12 +125,12 @@ class EmulationService : Service() {
|
|||
stopSelf()
|
||||
}
|
||||
|
||||
// ---- Native Cleanup nur wenn jemals gestartet ----
|
||||
// ---- Native cleanup only if it was ever started ----
|
||||
private fun hardCloseNativeIfStarted(reason: String) {
|
||||
if (!startedOnce.get()) return
|
||||
try { KenjinxNative.detachWindow() } catch (_: Throwable) {}
|
||||
try { KenjinxNative.deviceCloseEmulation() } catch (_: Throwable) {}
|
||||
// KEIN graphicsSetPresentEnabled(false) hier – führt bei kaltem Start zu NRE in VulkanRenderer.ReleaseSurface()
|
||||
// NO graphicsSetPresentEnabled(false) here — causes NRE in VulkanRenderer.ReleaseSurface() on a cold start
|
||||
// android.util.Log.d("EmuService", "hardCloseNativeIfStarted: $reason")
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -188,7 +188,7 @@ class MainViewModel(val activity: MainActivity) {
|
|||
runBlocking {
|
||||
semaphore.acquire()
|
||||
launchOnUiThread {
|
||||
// We are only able to initialize the emulation context on the main thread
|
||||
// We can only initialize the emulation context on the main thread
|
||||
val tzId = TimeZone.getDefault().id
|
||||
success = KenjinxNative.deviceInitialize(
|
||||
settings.memoryManagerMode.ordinal,
|
||||
|
|
@ -300,7 +300,7 @@ class MainViewModel(val activity: MainActivity) {
|
|||
runBlocking {
|
||||
semaphore.acquire()
|
||||
launchOnUiThread {
|
||||
// We are only able to initialize the emulation context on the main thread
|
||||
// We can only initialize the emulation context on the main thread
|
||||
val tzId = TimeZone.getDefault().id
|
||||
success = KenjinxNative.deviceInitialize(
|
||||
settings.memoryManagerMode.ordinal,
|
||||
|
|
|
|||
|
|
@ -957,7 +957,7 @@ namespace LibKenjinx
|
|||
|
||||
// ===== PresentAllowed / Surface Control (JNI) =====
|
||||
|
||||
// alias für ältere Aufrufe, falls vorhanden
|
||||
// alias for older calls, if present
|
||||
[UnmanagedCallersOnly(EntryPoint = "graphicsRendererSetPresent")]
|
||||
public static void JniGraphicsRendererSetPresent(bool enabled)
|
||||
{
|
||||
|
|
@ -975,7 +975,7 @@ namespace LibKenjinx
|
|||
}
|
||||
}
|
||||
|
||||
// neuer Name: passt zu KenjinxNative.graphicsSetPresentEnabled(...)
|
||||
// new name: matches KenjinxNative.graphicsSetPresentEnabled(...)
|
||||
[UnmanagedCallersOnly(EntryPoint = "graphicsSetPresentEnabled")]
|
||||
public static void JniGraphicsSetPresentEnabled(bool enabled)
|
||||
{
|
||||
|
|
@ -1003,7 +1003,7 @@ namespace LibKenjinx
|
|||
}
|
||||
}
|
||||
|
||||
// von MainActivity/GameHost benutzt
|
||||
// used by MainActivity/GameHost
|
||||
[UnmanagedCallersOnly(EntryPoint = "reattachWindowIfReady")]
|
||||
public static bool JniReattachWindowIfReady()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -62,14 +62,14 @@ namespace LibKenjinx
|
|||
else if (graphicsBackend == GraphicsBackend.Vulkan)
|
||||
{
|
||||
// Prefer the platform-provided Vulkan loader (if present), fall back to default.
|
||||
var api = VulkanLoader?.GetApi() ?? Vk.GetApi();
|
||||
var api = VulkanLoader?.GetApi() ?? Silk.NET.Vulkan.Vk.GetApi();
|
||||
|
||||
Renderer = new VulkanRenderer(
|
||||
api,
|
||||
(instance, _) =>
|
||||
{
|
||||
// use provided CreateSurface delegate (Android path will create ANativeWindow surface)
|
||||
return new SurfaceKHR(createSurfaceFunc == null ? null : (ulong?)createSurfaceFunc(instance.Handle));
|
||||
// Use provided CreateSurface delegate (Android path will create ANativeWindow surface)
|
||||
return new Silk.NET.Vulkan.SurfaceKHR(createSurfaceFunc == null ? null : (ulong?)createSurfaceFunc(instance.Handle));
|
||||
},
|
||||
() => requiredExtensions,
|
||||
null);
|
||||
|
|
@ -226,7 +226,7 @@ namespace LibKenjinx
|
|||
_swapBuffersCallback = swapBuffersCallback;
|
||||
}
|
||||
|
||||
// ===== Convenience-Wrapper für Vulkan re-attach (von JNI nutzbar) =====
|
||||
// ===== Convenience wrapper for Vulkan re-attach (usable from JNI) =====
|
||||
public static bool TryReattachSurface()
|
||||
{
|
||||
if (Renderer is VulkanRenderer vr)
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||
|
||||
private bool _initialized;
|
||||
|
||||
// JNI/Lifecycle-Flag
|
||||
// JNI / lifecycle flag
|
||||
internal volatile bool PresentAllowed = true;
|
||||
|
||||
public uint ProgramCount { get; set; } = 0;
|
||||
|
|
@ -52,7 +52,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||
internal Lock BackgroundQueueLock { get; private set; }
|
||||
internal Lock QueueLock { get; private set; }
|
||||
|
||||
// NEU: SurfaceLock, um Create/Destroy/Queries zu serialisieren
|
||||
// NEW: SurfaceLock to serialize create/destroy/queries
|
||||
internal Lock SurfaceLock { get; private set; }
|
||||
|
||||
internal MemoryAllocator MemoryAllocator { get; private set; }
|
||||
|
|
@ -506,7 +506,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||
Queue = queue;
|
||||
QueueLock = new();
|
||||
|
||||
// Init Locks
|
||||
// Init locks
|
||||
SurfaceLock = new();
|
||||
if (maxQueueCount >= 2)
|
||||
{
|
||||
|
|
@ -1021,7 +1021,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||
return !(IsMoltenVk || IsQualcommProprietary);
|
||||
}
|
||||
|
||||
// ===== Surface/Present Lifecycle helpers =====
|
||||
// ===== Surface/Present lifecycle helpers =====
|
||||
|
||||
public unsafe bool RecreateSurface()
|
||||
{
|
||||
|
|
@ -1052,7 +1052,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||
}
|
||||
catch
|
||||
{
|
||||
// retry später
|
||||
// retry later
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||
private ScalingFilter _currentScalingFilter;
|
||||
private bool _colorSpacePassthroughEnabled;
|
||||
|
||||
// Gate für alle vk*Surface*-Queries
|
||||
// Gate for all vk*Surface* queries
|
||||
private volatile bool _allowSurfaceQueries = true;
|
||||
|
||||
public unsafe Window(VulkanRenderer gd, SurfaceKHR surface, PhysicalDevice physicalDevice, Device device)
|
||||
|
|
@ -221,7 +221,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||
var surfaceFormat = ChooseSwapSurfaceFormat(surfaceFormats, _colorSpacePassthroughEnabled);
|
||||
var extent = ChooseSwapExtent(capabilities);
|
||||
|
||||
// Guard gegen 0x0-Extent direkt nach Resume
|
||||
// Guard against 0x0 extent right after resume
|
||||
if (extent.Width == 0 || extent.Height == 0)
|
||||
{
|
||||
_swapchainIsDirty = true;
|
||||
|
|
@ -239,10 +239,10 @@ namespace Ryujinx.Graphics.Vulkan
|
|||
var usage = ImageUsageFlags.ColorAttachmentBit | ImageUsageFlags.TransferDstBit;
|
||||
if (!PlatformInfo.IsBionic)
|
||||
{
|
||||
usage |= ImageUsageFlags.StorageBit; // nur Desktop erlaubt Storage für swapchain
|
||||
usage |= ImageUsageFlags.StorageBit; // desktop only: allow storage on swapchain
|
||||
}
|
||||
|
||||
// Auf Android: Identity; sonst der vom Treiber empfohlene CurrentTransform
|
||||
// On Android: use identity; otherwise use the driver-recommended CurrentTransform
|
||||
var preTransform = PlatformInfo.IsBionic
|
||||
? SurfaceTransformFlagsKHR.IdentityBitKhr
|
||||
: capabilities.CurrentTransform;
|
||||
|
|
@ -411,7 +411,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||
|
||||
public unsafe override void Present(ITexture texture, ImageCrop crop, Action swapBuffersCallback)
|
||||
{
|
||||
// Falls Surface bereits neu ist, Queries aber noch gesperrt → freigeben.
|
||||
// If the surface is already new but queries are still disabled → re-enable them.
|
||||
if (!_allowSurfaceQueries && _surface.Handle != 0)
|
||||
{
|
||||
_allowSurfaceQueries = true;
|
||||
|
|
@ -423,7 +423,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||
return;
|
||||
}
|
||||
|
||||
// Wenn Größe noch nicht da ist, Swapchain später neu aufbauen
|
||||
// If size is not yet available, rebuild swapchain later
|
||||
if (_width <= 0 || _height <= 0)
|
||||
{
|
||||
RecreateSwapchain();
|
||||
|
|
@ -431,7 +431,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||
return;
|
||||
}
|
||||
|
||||
// Lazy-Init/Recovery
|
||||
// Lazy init / recovery
|
||||
if (_swapchain.Handle == 0 || _imageAvailableSemaphores == null || _renderFinishedSemaphores == null)
|
||||
{
|
||||
try { CreateSwapchain(); } catch { /* try again next frame */ }
|
||||
|
|
@ -473,7 +473,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||
}
|
||||
else if (acquireResult == Result.ErrorSurfaceLostKhr)
|
||||
{
|
||||
// Im Hintergrund nicht sofort neu erstellen – freigeben und zurück
|
||||
// In background do not recreate immediately—release and return
|
||||
_gd.ReleaseSurface();
|
||||
swapBuffersCallback?.Invoke();
|
||||
return;
|
||||
|
|
@ -491,13 +491,13 @@ namespace Ryujinx.Graphics.Vulkan
|
|||
|
||||
var cbs = _gd.CommandBufferPool.Rent();
|
||||
|
||||
// --- Layout/Stages je nach Pfad korrekt setzen ---
|
||||
bool allowStorageDst = !PlatformInfo.IsBionic; // Android: kein Storage auf Swapchain
|
||||
// --- Set layout/stages correctly depending on path ---
|
||||
bool allowStorageDst = !PlatformInfo.IsBionic; // Android: no storage on swapchain
|
||||
bool useComputeDst = allowStorageDst && _scalingFilter != null;
|
||||
|
||||
if (useComputeDst)
|
||||
{
|
||||
// Compute schreibt in das Swapchain-Image → General + ShaderWrite
|
||||
// Compute writes to the swapchain image → General + ShaderWrite
|
||||
Transition(
|
||||
cbs.CommandBuffer,
|
||||
swapchainImage,
|
||||
|
|
@ -510,7 +510,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||
}
|
||||
else
|
||||
{
|
||||
// Renderpass schreibt in das Swapchain-Image → ColorAttachmentOptimal
|
||||
// Render pass writes to the swapchain image → ColorAttachmentOptimal
|
||||
Transition(
|
||||
cbs.CommandBuffer,
|
||||
swapchainImage,
|
||||
|
|
@ -616,7 +616,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||
true);
|
||||
}
|
||||
|
||||
// Transition zu Present – Stages/Access je nach vorherigem Pfad
|
||||
// Transition to Present — stages/access depending on previous path
|
||||
if (useComputeDst)
|
||||
{
|
||||
Transition(
|
||||
|
|
@ -643,7 +643,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||
}
|
||||
|
||||
var waitSems = new Silk.NET.Vulkan.Semaphore[] { _imageAvailableSemaphores[semaphoreIndex] };
|
||||
var waitStages = new PipelineStageFlags[] { PipelineStageFlags.ColorAttachmentOutputBit }; // wichtig auf Android
|
||||
var waitStages = new PipelineStageFlags[] { PipelineStageFlags.ColorAttachmentOutputBit }; // important on Android
|
||||
var signalSems = new Silk.NET.Vulkan.Semaphore[] { _renderFinishedSemaphores[semaphoreIndex] };
|
||||
_gd.CommandBufferPool.Return(cbs, waitSems, waitStages, signalSems);
|
||||
|
||||
|
|
@ -835,8 +835,8 @@ namespace Ryujinx.Graphics.Vulkan
|
|||
// We don't need to use width and height as we can get the size from the surface.
|
||||
_swapchainIsDirty = true;
|
||||
|
||||
// Nach Resume sicherstellen, dass Surface-Queries wieder erlaubt sind,
|
||||
// falls vorher OnSurfaceLost() das Gate geschlossen hat.
|
||||
// After resume, ensure surface queries are enabled again
|
||||
// if OnSurfaceLost() previously closed the gate.
|
||||
if (_surface.Handle != 0)
|
||||
{
|
||||
SetSurfaceQueryAllowed(true);
|
||||
|
|
@ -846,7 +846,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||
public override void ChangeVSyncMode(VSyncMode vSyncMode)
|
||||
{
|
||||
_vSyncMode = vSyncMode;
|
||||
//present mode may change, so mark the swapchain for recreation
|
||||
// Present mode may change, so mark the swapchain for recreation
|
||||
_swapchainIsDirty = true;
|
||||
}
|
||||
|
||||
|
|
@ -904,7 +904,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||
{
|
||||
lock (_gd.SurfaceLock)
|
||||
{
|
||||
// harte Aufräumaktion, damit nach Resume nichts „altes“ übrig ist
|
||||
// Hard cleanup so nothing stale remains after resume
|
||||
_swapchainIsDirty = true;
|
||||
SetSurfaceQueryAllowed(false);
|
||||
|
||||
|
|
@ -953,7 +953,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||
}
|
||||
|
||||
_surface = new SurfaceKHR(0);
|
||||
_width = _height = 0; // erzwingt späteren sauberen Recreate-Pfad
|
||||
_width = _height = 0; // forces a clean recreate path later
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue