diff --git a/src/KenjinxAndroid/app/src/main/AndroidManifest.xml b/src/KenjinxAndroid/app/src/main/AndroidManifest.xml index fe31d0b7f..25830e639 100644 --- a/src/KenjinxAndroid/app/src/main/AndroidManifest.xml +++ b/src/KenjinxAndroid/app/src/main/AndroidManifest.xml @@ -17,7 +17,7 @@ - + @@ -88,8 +88,6 @@ android:exported="false" android:stopWithTask="true" android:foregroundServiceType="mediaPlayback" /> - - diff --git a/src/KenjinxAndroid/app/src/main/java/org/kenjinx/android/GameHost.kt b/src/KenjinxAndroid/app/src/main/java/org/kenjinx/android/GameHost.kt index b7926c7a4..74fa6b908 100644 --- a/src/KenjinxAndroid/app/src/main/java/org/kenjinx/android/GameHost.kt +++ b/src/KenjinxAndroid/app/src/main/java/org/kenjinx/android/GameHost.kt @@ -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) diff --git a/src/KenjinxAndroid/app/src/main/java/org/kenjinx/android/KenjinxNative.kt b/src/KenjinxAndroid/app/src/main/java/org/kenjinx/android/KenjinxNative.kt index a77b2330d..621673292 100644 --- a/src/KenjinxAndroid/app/src/main/java/org/kenjinx/android/KenjinxNative.kt +++ b/src/KenjinxAndroid/app/src/main/java/org/kenjinx/android/KenjinxNative.kt @@ -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) } diff --git a/src/KenjinxAndroid/app/src/main/java/org/kenjinx/android/MainActivity.kt b/src/KenjinxAndroid/app/src/main/java/org/kenjinx/android/MainActivity.kt index cb20ae720..c2ba03b08 100644 --- a/src/KenjinxAndroid/app/src/main/java/org/kenjinx/android/MainActivity.kt +++ b/src/KenjinxAndroid/app/src/main/java/org/kenjinx/android/MainActivity.kt @@ -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() } diff --git a/src/KenjinxAndroid/app/src/main/java/org/kenjinx/android/service/EmulationService.kt b/src/KenjinxAndroid/app/src/main/java/org/kenjinx/android/service/EmulationService.kt index 2c2b12cc8..3127e8687 100644 --- a/src/KenjinxAndroid/app/src/main/java/org/kenjinx/android/service/EmulationService.kt +++ b/src/KenjinxAndroid/app/src/main/java/org/kenjinx/android/service/EmulationService.kt @@ -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") } diff --git a/src/KenjinxAndroid/app/src/main/java/org/kenjinx/android/viewmodels/MainViewModel.kt b/src/KenjinxAndroid/app/src/main/java/org/kenjinx/android/viewmodels/MainViewModel.kt index ddfd96a5f..69b153144 100644 --- a/src/KenjinxAndroid/app/src/main/java/org/kenjinx/android/viewmodels/MainViewModel.kt +++ b/src/KenjinxAndroid/app/src/main/java/org/kenjinx/android/viewmodels/MainViewModel.kt @@ -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, diff --git a/src/LibKenjinx/Android/JniExportedMethods.cs b/src/LibKenjinx/Android/JniExportedMethods.cs index 16885b894..527415ec7 100644 --- a/src/LibKenjinx/Android/JniExportedMethods.cs +++ b/src/LibKenjinx/Android/JniExportedMethods.cs @@ -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() { diff --git a/src/LibKenjinx/LibKenjinx.Graphics.cs b/src/LibKenjinx/LibKenjinx.Graphics.cs index 3430ead1e..36a3de12a 100644 --- a/src/LibKenjinx/LibKenjinx.Graphics.cs +++ b/src/LibKenjinx/LibKenjinx.Graphics.cs @@ -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) diff --git a/src/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs b/src/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs index eeb561a09..f7d1e8df9 100644 --- a/src/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs +++ b/src/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs @@ -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; } } diff --git a/src/Ryujinx.Graphics.Vulkan/Window.cs b/src/Ryujinx.Graphics.Vulkan/Window.cs index 271fffbfa..4af33c8a8 100644 --- a/src/Ryujinx.Graphics.Vulkan/Window.cs +++ b/src/Ryujinx.Graphics.Vulkan/Window.cs @@ -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 } }