diff --git a/src/LibRyujinx/Android/JniExportedMethods.cs b/src/LibRyujinx/Android/JniExportedMethods.cs
index fc2a36328..e3a9ab860 100644
--- a/src/LibRyujinx/Android/JniExportedMethods.cs
+++ b/src/LibRyujinx/Android/JniExportedMethods.cs
@@ -49,6 +49,9 @@ namespace LibRyujinx
         [DllImport("libryujinxjni")]
         internal extern static void onFrameEnd(double time);
 
+        [DllImport("libryujinxjni")]
+        internal extern static void setProgressInfo(IntPtr info, float progress);
+
         [DllImport("libryujinxjni")]
         internal extern static void setCurrentTransform(long native_window, int transform);
 
@@ -287,7 +290,7 @@ namespace LibRyujinx
                 extensions.Add(GetString(jEnv, ext));
             }
 
-            if((long)driverHandle != 0)
+            if ((long)driverHandle != 0)
             {
                 VulkanLoader = new VulkanLoader((IntPtr)(long)driverHandle);
             }
diff --git a/src/LibRyujinx/LibRyujinx.Graphics.cs b/src/LibRyujinx/LibRyujinx.Graphics.cs
index 6308a94c3..b247a5b98 100644
--- a/src/LibRyujinx/LibRyujinx.Graphics.cs
+++ b/src/LibRyujinx/LibRyujinx.Graphics.cs
@@ -1,10 +1,13 @@
 using ARMeilleure.Translation;
+using LibHac.Bcat;
 using LibRyujinx.Shared;
 using OpenTK.Graphics.OpenGL;
 using Ryujinx.Common.Configuration;
+using Ryujinx.Cpu;
 using Ryujinx.Graphics.GAL;
 using Ryujinx.Graphics.GAL.Multithreading;
 using Ryujinx.Graphics.Gpu;
+using Ryujinx.Graphics.Gpu.Shader;
 using Ryujinx.Graphics.OpenGL;
 using Ryujinx.Graphics.Vulkan;
 using Silk.NET.Vulkan;
@@ -153,64 +156,125 @@ namespace LibRyujinx
             device.Gpu.Renderer.Initialize(GraphicsDebugLevel.None);
             _gpuCancellationTokenSource = new CancellationTokenSource();
 
-            device.Gpu.Renderer.RunLoop(() =>
+            device.Gpu.ShaderCacheStateChanged += LoadProgressStateChangedHandler;
+            device.Processes.ActiveApplication.DiskCacheLoadState.StateChanged += LoadProgressStateChangedHandler;
+
+            try
             {
-                _gpuDoneEvent.Reset();
-                device.Gpu.SetGpuThread();
-                device.Gpu.InitializeShaderCache(_gpuCancellationTokenSource.Token);
-                Translator.IsReadyForTranslation.Set();
-
-                _isActive = true;
-
-                while (_isActive)
+                device.Gpu.Renderer.RunLoop(() =>
                 {
-                    if (_isStopped)
-                    {
-                        break;
-                    }
+                    _gpuDoneEvent.Reset();
+                    device.Gpu.SetGpuThread();
+                    device.Gpu.InitializeShaderCache(_gpuCancellationTokenSource.Token);
+                    Translator.IsReadyForTranslation.Set();
 
-                    debug_break(1);
+                    _isActive = true;
 
-                    if (Ryujinx.Common.SystemInfo.SystemInfo.IsBionic)
+                    while (_isActive)
                     {
-                        setRenderingThread();
-                    }
-
-                    if (device.WaitFifo())
-                    {
-                        device.Statistics.RecordFifoStart();
-                        device.ProcessFrame();
-                        device.Statistics.RecordFifoEnd();
-                    }
-
-                    while (device.ConsumeFrameAvailable())
-                    {
-                        device.PresentFrame(() =>
+                        if (_isStopped)
                         {
-                            VulkanRenderer? vk = device.Gpu.Renderer as VulkanRenderer;
-                            if(vk == null)
-                            {
-                                vk = (device.Gpu.Renderer as ThreadedRenderer)?.BaseRenderer as VulkanRenderer;
-                            }
+                            break;
+                        }
 
-                            if(vk != null)
-                            {
-                                var transform = vk.CurrentTransform;
+                        debug_break(1);
 
-                                setCurrentTransform(_window, (int)transform);
-                            }
-                            _swapBuffersCallback?.Invoke();
-                        });
+                        if (Ryujinx.Common.SystemInfo.SystemInfo.IsBionic)
+                        {
+                            setRenderingThread();
+                        }
+
+                        if (device.WaitFifo())
+                        {
+                            device.Statistics.RecordFifoStart();
+                            device.ProcessFrame();
+                            device.Statistics.RecordFifoEnd();
+                        }
+
+                        while (device.ConsumeFrameAvailable())
+                        {
+                            device.PresentFrame(() =>
+                            {
+                                VulkanRenderer? vk = device.Gpu.Renderer as VulkanRenderer;
+                                if (vk == null)
+                                {
+                                    vk = (device.Gpu.Renderer as ThreadedRenderer)?.BaseRenderer as VulkanRenderer;
+                                }
+
+                                if (vk != null)
+                                {
+                                    var transform = vk.CurrentTransform;
+
+                                    setCurrentTransform(_window, (int)transform);
+                                }
+                                _swapBuffersCallback?.Invoke();
+                            });
+                        }
                     }
-                }
 
-                if (device.Gpu.Renderer is ThreadedRenderer threaded)
-                {
-                    threaded.FlushThreadedCommands();
-                }
+                    if (device.Gpu.Renderer is ThreadedRenderer threaded)
+                    {
+                        threaded.FlushThreadedCommands();
+                    }
 
-                _gpuDoneEvent.Set();
-            });
+                    _gpuDoneEvent.Set();
+                });
+            }
+            finally
+            {
+                device.Gpu.ShaderCacheStateChanged -= LoadProgressStateChangedHandler;
+                device.Processes.ActiveApplication.DiskCacheLoadState.StateChanged -= LoadProgressStateChangedHandler;
+            }
+        }
+
+        private static void LoadProgressStateChangedHandler<T>(T state, int current, int total) where T : Enum
+        {
+            void SetInfo(string status, float value)
+            {
+                var ptr = Marshal.StringToHGlobalAnsi(status);
+
+                setProgressInfo(ptr, value);
+
+                Marshal.FreeHGlobal(ptr);
+            }
+                    var status = $"{current} / {total}";
+                    var progress = current / (float)total;
+
+            switch (state)
+            {
+                case LoadState ptcState:
+                    if (float.IsNaN((progress)))
+                        progress = 0;
+
+                    switch (ptcState)
+                    {
+                        case LoadState.Unloaded:
+                        case LoadState.Loading:
+                            SetInfo($"Loading PTC {status}", progress);
+                            break;
+                        case LoadState.Loaded:
+                            SetInfo($"PTC Loaded", -1);
+                            break;
+                    }
+                    break;
+                case ShaderCacheState shaderCacheState:
+                    switch (shaderCacheState)
+                    {
+                        case ShaderCacheState.Start:
+                        case ShaderCacheState.Loading:
+                            SetInfo($"Compiling Shaders {status}", progress);
+                            break;
+                        case ShaderCacheState.Packaging:
+                            SetInfo($"Packaging Shaders {status}", progress);
+                            break;
+                        case ShaderCacheState.Loaded:
+                            SetInfo($"Shaders Loaded", -1);
+                            break;
+                    }
+                    break;
+                default:
+                    throw new ArgumentException($"Unknown Progress Handler type {typeof(T)}");
+            }
         }
 
         [UnmanagedCallersOnly(EntryPoint = "graphics_renderer_set_swap_buffer_callback")]
diff --git a/src/RyujinxAndroid/app/src/main/cpp/ryujinx.cpp b/src/RyujinxAndroid/app/src/main/cpp/ryujinx.cpp
index 858fb9be0..278c176b5 100644
--- a/src/RyujinxAndroid/app/src/main/cpp/ryujinx.cpp
+++ b/src/RyujinxAndroid/app/src/main/cpp/ryujinx.cpp
@@ -26,6 +26,9 @@ JNIEnv* _rendererEnv = nullptr;
 
 std::chrono::time_point<std::chrono::steady_clock, std::chrono::nanoseconds> _currentTimePoint;
 
+std::string progressInfo = "";
+float progress = -1;
+
 JNIEnv* getEnv(bool isRenderer){
     JNIEnv* env;
     if(isRenderer){
@@ -130,6 +133,14 @@ jstring createString(
     return str;
 }
 
+jstring createStringFromStdString(
+        JNIEnv *env,
+        std::string s) {
+    auto str = env->NewStringUTF(s.c_str());
+
+    return str;
+}
+
 
 }
 extern "C"
@@ -168,6 +179,11 @@ void onFrameEnd(double time) {
     env->CallStaticVoidMethod(cl, _updateFrameTime,
                               nano);
 }
+extern "C"
+void setProgressInfo(char* info, float progressValue) {
+    progressInfo = std::string (info);
+    progress = progressValue;
+}
 
 extern "C"
 void setCurrentTransform(long native_window, int transform){
@@ -283,3 +299,15 @@ Java_org_ryujinx_android_NativeHelpers_setSwapInterval(JNIEnv *env, jobject thiz
 
     return nativeWindow->setSwapInterval(nativeWindow, swap_interval);
 }
+
+extern "C"
+JNIEXPORT jfloat JNICALL
+Java_org_ryujinx_android_NativeHelpers_getProgressValue(JNIEnv *env, jobject thiz) {
+    return progress;
+}
+
+extern "C"
+JNIEXPORT jstring JNICALL
+Java_org_ryujinx_android_NativeHelpers_getProgressInfo(JNIEnv *env, jobject thiz) {
+    return createStringFromStdString(env, progressInfo);
+}
diff --git a/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/GameActivity.kt b/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/GameActivity.kt
index 1a1bb96b4..37113912f 100644
--- a/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/GameActivity.kt
+++ b/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/GameActivity.kt
@@ -21,9 +21,11 @@ import androidx.compose.foundation.layout.wrapContentWidth
 import androidx.compose.material3.AlertDialog
 import androidx.compose.material3.AlertDialogDefaults
 import androidx.compose.material3.Button
+import androidx.compose.material3.Card
 import androidx.compose.material3.ExperimentalMaterial3Api
 import androidx.compose.material3.Icon
 import androidx.compose.material3.IconButton
+import androidx.compose.material3.LinearProgressIndicator
 import androidx.compose.material3.MaterialTheme
 import androidx.compose.material3.Surface
 import androidx.compose.material3.Text
@@ -50,7 +52,9 @@ import kotlin.math.abs
 import kotlin.math.roundToInt
 
 class GameActivity : ComponentActivity() {
-    private var physicalControllerManager: PhysicalControllerManager = PhysicalControllerManager(this)
+    private var physicalControllerManager: PhysicalControllerManager =
+        PhysicalControllerManager(this)
+
     override fun onCreate(savedInstanceState: Bundle?) {
         super.onCreate(savedInstanceState)
 
@@ -70,7 +74,7 @@ class GameActivity : ComponentActivity() {
     @SuppressLint("RestrictedApi")
     override fun dispatchKeyEvent(event: KeyEvent?): Boolean {
         event?.apply {
-            if(physicalControllerManager.onKeyEvent(this))
+            if (physicalControllerManager.onKeyEvent(this))
                 return true
         }
         return super.dispatchKeyEvent(event)
@@ -105,20 +109,22 @@ class GameActivity : ComponentActivity() {
         force60HzRefreshRate(false)
     }
 
-    private fun force60HzRefreshRate(enable : Boolean) {
+    private fun force60HzRefreshRate(enable: Boolean) {
         // Hack for MIUI devices since they don't support the standard Android APIs
         try {
             val setFpsIntent = Intent("com.miui.powerkeeper.SET_ACTIVITY_FPS")
             setFpsIntent.putExtra("package_name", "org.ryujinx.android")
             setFpsIntent.putExtra("isEnter", enable)
             sendBroadcast(setFpsIntent)
-        } catch (_ : Exception) {
+        } catch (_: Exception) {
         }
 
         if (enable)
-            display?.supportedModes?.minByOrNull { abs(it.refreshRate - 60f) }?.let { window.attributes.preferredDisplayModeId = it.modeId }
+            display?.supportedModes?.minByOrNull { abs(it.refreshRate - 60f) }
+                ?.let { window.attributes.preferredDisplayModeId = it.modeId }
         else
-            display?.supportedModes?.maxByOrNull { it.refreshRate }?.let { window.attributes.preferredDisplayModeId = it.modeId }
+            display?.supportedModes?.maxByOrNull { it.refreshRate }
+                ?.let { window.attributes.preferredDisplayModeId = it.modeId }
     }
 
     private fun setFullScreen(fullscreen: Boolean) {
@@ -139,6 +145,7 @@ class GameActivity : ComponentActivity() {
             }
         }
     }
+
     @Composable
     fun GameView(mainViewModel: MainViewModel) {
         Box(modifier = Modifier.fillMaxSize()) {
@@ -170,6 +177,20 @@ class GameActivity : ComponentActivity() {
                 mutableStateOf(false)
             }
 
+            val showLoading = remember {
+                mutableStateOf(true)
+            }
+
+            val progressValue = remember {
+                mutableStateOf(0.0f)
+            }
+
+            val progress = remember {
+                mutableStateOf("Loading")
+            }
+
+            mainViewModel.setProgressStates(showLoading, progressValue, progress)
+
             // touch surface
             Surface(color = Color.Transparent, modifier = Modifier
                 .fillMaxSize()
@@ -213,47 +234,54 @@ class GameActivity : ComponentActivity() {
                     }
                 }) {
             }
-            GameController.Compose(mainViewModel)
-            Row(
-                modifier = Modifier
-                    .align(Alignment.BottomCenter)
-                    .padding(8.dp)
-            ) {
-                IconButton(modifier = Modifier.padding(4.dp), onClick = {
-                    showMore.value = true
-                }) {
-                    Icon(
-                        imageVector = CssGgIcons.ToolbarBottom,
-                        contentDescription = "Open Panel"
-                    )
-                }
-            }
+            if (!showLoading.value) {
+                GameController.Compose(mainViewModel)
 
-            if(showMore.value){
-                Popup(alignment = Alignment.BottomCenter, onDismissRequest = {showMore.value = false}) {
-                    Surface(modifier = Modifier.padding(16.dp),
-                        shape = MaterialTheme.shapes.medium) {
-                        Row(modifier = Modifier.padding(8.dp)) {
-                            IconButton(modifier = Modifier.padding(4.dp), onClick = {
-                                showMore.value = false
-                                showController.value = !showController.value
-                                mainViewModel.controller?.setVisible(showController.value)
-                            }) {
-                                Icon(
-                                    imageVector = Icons.videoGame(),
-                                    contentDescription = "Toggle Virtual Pad"
-                                )
-                            }
-                            IconButton(modifier = Modifier.padding(4.dp), onClick = {
-                                showMore.value = false
-                                enableVsync.value = !enableVsync.value
-                                RyujinxNative().graphicsRendererSetVsync(enableVsync.value)
-                            }) {
-                                Icon(
-                                    imageVector = Icons.vSync(),
-                                    tint = if(enableVsync.value) Color.Green else Color.Red,
-                                    contentDescription = "Toggle VSync"
-                                )
+                Row(
+                    modifier = Modifier
+                        .align(Alignment.BottomCenter)
+                        .padding(8.dp)
+                ) {
+                    IconButton(modifier = Modifier.padding(4.dp), onClick = {
+                        showMore.value = true
+                    }) {
+                        Icon(
+                            imageVector = CssGgIcons.ToolbarBottom,
+                            contentDescription = "Open Panel"
+                        )
+                    }
+                }
+
+                if (showMore.value) {
+                    Popup(
+                        alignment = Alignment.BottomCenter,
+                        onDismissRequest = { showMore.value = false }) {
+                        Surface(
+                            modifier = Modifier.padding(16.dp),
+                            shape = MaterialTheme.shapes.medium
+                        ) {
+                            Row(modifier = Modifier.padding(8.dp)) {
+                                IconButton(modifier = Modifier.padding(4.dp), onClick = {
+                                    showMore.value = false
+                                    showController.value = !showController.value
+                                    mainViewModel.controller?.setVisible(showController.value)
+                                }) {
+                                    Icon(
+                                        imageVector = Icons.videoGame(),
+                                        contentDescription = "Toggle Virtual Pad"
+                                    )
+                                }
+                                IconButton(modifier = Modifier.padding(4.dp), onClick = {
+                                    showMore.value = false
+                                    enableVsync.value = !enableVsync.value
+                                    RyujinxNative().graphicsRendererSetVsync(enableVsync.value)
+                                }) {
+                                    Icon(
+                                        imageVector = Icons.vSync(),
+                                        tint = if (enableVsync.value) Color.Green else Color.Red,
+                                        contentDescription = "Toggle VSync"
+                                    )
+                                }
                             }
                         }
                     }
@@ -268,6 +296,39 @@ class GameActivity : ComponentActivity() {
                 showBackNotice.value = true
             }
 
+            if (showLoading.value) {
+                Card(
+                    modifier = Modifier
+                        .padding(16.dp)
+                        .fillMaxWidth(0.5f)
+                        .align(Alignment.Center),
+                    shape = MaterialTheme.shapes.medium
+                ) {
+                    Column(
+                        modifier = Modifier
+                            .padding(16.dp)
+                            .fillMaxWidth()
+                    ) {
+                        Text(text = progress.value)
+
+                        if (progressValue.value > -1)
+                            LinearProgressIndicator(
+                                modifier = Modifier
+                                    .fillMaxWidth()
+                                    .padding(top = 16.dp),
+                                progress = progressValue.value
+                            )
+                        else
+                            LinearProgressIndicator(
+                                modifier = Modifier
+                                    .fillMaxWidth()
+                                    .padding(top = 16.dp)
+                            )
+                    }
+
+                }
+            }
+
             if (showBackNotice.value) {
                 AlertDialog(onDismissRequest = { showBackNotice.value = false }) {
                     Column {
diff --git a/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/GameHost.kt b/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/GameHost.kt
index 8a15e3a05..f19fc93da 100644
--- a/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/GameHost.kt
+++ b/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/GameHost.kt
@@ -5,6 +5,7 @@ import android.content.Context
 import android.os.Build
 import android.view.SurfaceHolder
 import android.view.SurfaceView
+import androidx.compose.runtime.MutableState
 import org.ryujinx.android.viewmodels.GameModel
 import org.ryujinx.android.viewmodels.MainViewModel
 import org.ryujinx.android.viewmodels.QuickSettings
@@ -12,6 +13,10 @@ import kotlin.concurrent.thread
 
 @SuppressLint("ViewConstructor")
 class GameHost(context: Context?, private val mainViewModel: MainViewModel) : SurfaceView(context), SurfaceHolder.Callback {
+    private var isProgressHidden: Boolean = false
+    private var progress: MutableState<String>? = null
+    private var progressValue: MutableState<Float>? = null
+    private var showLoading: MutableState<Boolean>? = null
     private var game: GameModel? = null
     private var _isClosed: Boolean = false
     private var _renderingThreadWatcher: Thread? = null
@@ -29,6 +34,8 @@ class GameHost(context: Context?, private val mainViewModel: MainViewModel) : Su
         holder.addCallback(this)
 
         nativeWindow = NativeWindow(this)
+
+        mainViewModel.gameHost = this
     }
 
     override fun surfaceCreated(holder: SurfaceHolder) {
@@ -75,7 +82,6 @@ class GameHost(context: Context?, private val mainViewModel: MainViewModel) : Su
     }
 
     private fun start(surfaceHolder: SurfaceHolder) {
-        mainViewModel.gameHost = this
         if(_isStarted)
             return
 
@@ -106,11 +112,31 @@ class GameHost(context: Context?, private val mainViewModel: MainViewModel) : Su
 
         _updateThread = thread(start = true) {
             var c = 0
+            val helper = NativeHelpers()
             while (_isStarted) {
                 _nativeRyujinx.inputUpdate()
                 Thread.sleep(1)
+
+                showLoading?.apply {
+                    if(value){
+                        var value = helper.getProgressValue()
+
+                        if(value != -1f)
+                        progress?.apply {
+                            this.value = helper.getProgressInfo()
+                        }
+
+                        progressValue?.apply {
+                            this.value = value
+                        }
+                    }
+                }
                 c++
                 if (c >= 1000) {
+                    if(helper.getProgressValue() == -1f)
+                        progress?.apply {
+                            this.value = "Loading ${game!!.titleName}"
+                        }
                     c = 0
                     mainViewModel.updateStats(_nativeRyujinx.deviceGetGameFifo(), _nativeRyujinx.deviceGetGameFrameRate(), _nativeRyujinx.deviceGetGameFrameTime())
                 }
@@ -143,4 +169,26 @@ class GameHost(context: Context?, private val mainViewModel: MainViewModel) : Su
 
         game?.close()
     }
+
+    fun setProgressStates(
+        showLoading: MutableState<Boolean>?,
+        progressValue: MutableState<Float>?,
+        progress: MutableState<String>?
+    ) {
+        this.showLoading = showLoading
+        this.progressValue = progressValue
+        this.progress = progress
+
+        showLoading?.apply {
+            showLoading.value = !isProgressHidden
+        }
+    }
+
+    fun hideProgressIndicator() {
+        isProgressHidden = true
+        showLoading?.apply {
+            if (value == isProgressHidden)
+                value = !isProgressHidden
+        }
+    }
 }
diff --git a/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/MainActivity.kt b/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/MainActivity.kt
index d9817f69a..5f3b95481 100644
--- a/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/MainActivity.kt
+++ b/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/MainActivity.kt
@@ -34,6 +34,8 @@ class MainActivity : ComponentActivity() {
             if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
                 mainViewModel?.performanceManager?.updateRenderingSessionTime(gameTime)
             }
+
+            mainViewModel?.gameHost?.hideProgressIndicator()
         }
     }
 
diff --git a/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/NativeHelpers.kt b/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/NativeHelpers.kt
index af46708a2..eead88176 100644
--- a/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/NativeHelpers.kt
+++ b/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/NativeHelpers.kt
@@ -26,4 +26,6 @@ class NativeHelpers {
     external fun getMaxSwapInterval(nativeWindow: Long): Int
     external fun getMinSwapInterval(nativeWindow: Long): Int
     external fun setSwapInterval(nativeWindow: Long, swapInterval: Int): Int
+    external fun getProgressInfo() : String
+    external fun getProgressValue() : Float
 }
diff --git a/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/viewmodels/GameModel.kt b/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/viewmodels/GameModel.kt
index 1a0490827..bf166b1c9 100644
--- a/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/viewmodels/GameModel.kt
+++ b/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/viewmodels/GameModel.kt
@@ -8,7 +8,7 @@ import org.ryujinx.android.RyujinxNative
 
 
 class GameModel(var file: DocumentFile, val context: Context) {
-    private var descriptor: ParcelFileDescriptor? = null
+    var descriptor: ParcelFileDescriptor? = null
     var fileName: String?
     var fileSize = 0.0
     var titleName: String? = null
diff --git a/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/viewmodels/HomeViewModel.kt b/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/viewmodels/HomeViewModel.kt
index b17e45d1e..e8666757f 100644
--- a/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/viewmodels/HomeViewModel.kt
+++ b/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/viewmodels/HomeViewModel.kt
@@ -11,11 +11,13 @@ import com.anggrayudi.storage.file.extension
 import com.anggrayudi.storage.file.getAbsolutePath
 import com.anggrayudi.storage.file.search
 import org.ryujinx.android.MainActivity
+import kotlin.concurrent.thread
 
 class HomeViewModel(
     val activity: MainActivity? = null,
     val mainViewModel: MainViewModel? = null
 ) {
+    private var isLoading: Boolean = false
     private var gameList: SnapshotStateList<GameModel>? = null
     private var loadedCache: List<GameModel> = listOf()
     private var gameFolderPath: DocumentFile? = null
@@ -68,23 +70,39 @@ class HomeViewModel(
 
     fun reloadGameList() {
         var storage = activity?.storageHelper ?: return
+        
+        if(isLoading)
+            return
         val folder = gameFolderPath ?: return
+        
+        isLoading = true
 
         val files = mutableListOf<GameModel>()
 
-        for (file in folder.search(false, DocumentFileType.FILE)) {
-            if (file.extension == "xci" || file.extension == "nsp")
-                activity.let {
-                    files.add(GameModel(file, it))
+        thread {
+            try {
+                for (file in folder.search(false, DocumentFileType.FILE)) {
+                    if (file.extension == "xci" || file.extension == "nsp")
+                        activity.let {
+                            files.add(GameModel(file, it))
+                        }
                 }
+
+                loadedCache = files.toList()
+
+                isLoading = false
+
+                applyFilter()
+            }
+            finally {
+                isLoading = false
+            }
         }
-
-        loadedCache = files.toList()
-
-        applyFilter()
     }
 
     private fun applyFilter() {
+        if(isLoading)
+            return
         gameList?.clear()
         gameList?.addAll(loadedCache)
     }
@@ -93,4 +111,4 @@ class HomeViewModel(
         gameList = list
         applyFilter()
     }
-}
\ No newline at end of file
+}
diff --git a/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/viewmodels/MainViewModel.kt b/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/viewmodels/MainViewModel.kt
index c6c157f8d..c7d64ecc0 100644
--- a/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/viewmodels/MainViewModel.kt
+++ b/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/viewmodels/MainViewModel.kt
@@ -7,6 +7,9 @@ import android.os.Build
 import android.os.PerformanceHintManager
 import androidx.compose.runtime.MutableState
 import androidx.navigation.NavHostController
+import com.anggrayudi.storage.extension.launchOnUiThread
+import kotlinx.coroutines.runBlocking
+import kotlinx.coroutines.sync.Semaphore
 import org.ryujinx.android.GameActivity
 import org.ryujinx.android.GameController
 import org.ryujinx.android.GameHost
@@ -25,13 +28,20 @@ import java.io.File
 class MainViewModel(val activity: MainActivity) {
     var physicalControllerManager: PhysicalControllerManager? = null
     var gameModel: GameModel? = null
-    var gameHost: GameHost? = null
     var controller: GameController? = null
     var performanceManager: PerformanceManager? = null
     var selected: GameModel? = null
     private var gameTimeState: MutableState<Double>? = null
     private var gameFpsState: MutableState<Double>? = null
     private var fifoState: MutableState<Double>? = null
+    private var progress: MutableState<String>? = null
+    private var progressValue: MutableState<Float>? = null
+    private var showLoading: MutableState<Boolean>? = null
+    var gameHost: GameHost? = null
+        set(value) {
+            field = value
+            field?.setProgressStates(showLoading, progressValue, progress)
+        }
     var navController : NavHostController? = null
 
     var homeViewModel: HomeViewModel = HomeViewModel(activity, this)
@@ -55,7 +65,7 @@ class MainViewModel(val activity: MainActivity) {
 
         val descriptor = game.open()
 
-        if(descriptor == 0)
+        if (descriptor == 0)
             return false
 
         gameModel = game
@@ -69,7 +79,7 @@ class MainViewModel(val activity: MainActivity) {
             BackendThreading = org.ryujinx.android.BackendThreading.Auto.ordinal
         })
 
-        if(!success)
+        if (!success)
             return false
 
         val nativeHelpers = NativeHelpers()
@@ -120,27 +130,39 @@ class MainViewModel(val activity: MainActivity) {
             nativeInterop.VkRequiredExtensions!!,
             driverHandle
         )
-        if(!success)
+        if (!success)
             return false
 
-        success = nativeRyujinx.deviceInitialize(
-            settings.isHostMapped,
-            settings.useNce,
-            SystemLanguage.AmericanEnglish.ordinal,
-            RegionCode.USA.ordinal,
-            settings.enableVsync,
-            settings.enableDocked,
-            settings.enablePtc,
-            false,
-            "UTC",
-            settings.ignoreMissingServices
-        )
-        if(!success)
+        val semaphore = Semaphore(1, 0)
+        runBlocking {
+            semaphore.acquire()
+            launchOnUiThread {
+                // We are only able to initialize the emulation context on the main thread
+                success = nativeRyujinx.deviceInitialize(
+                    settings.isHostMapped,
+                    settings.useNce,
+                    SystemLanguage.AmericanEnglish.ordinal,
+                    RegionCode.USA.ordinal,
+                    settings.enableVsync,
+                    settings.enableDocked,
+                    settings.enablePtc,
+                    false,
+                    "UTC",
+                    settings.ignoreMissingServices
+                )
+
+                semaphore.release()
+            }
+            semaphore.acquire()
+            semaphore.release()
+        }
+
+        if (!success)
             return false
 
         success = nativeRyujinx.deviceLoadDescriptor(descriptor, game.isXci())
 
-        if(!success)
+        if (!success)
             return false
 
         return true
@@ -180,4 +202,15 @@ class MainViewModel(val activity: MainActivity) {
         val intent = Intent(activity, GameActivity::class.java)
         activity.startActivity(intent)
     }
+
+    fun setProgressStates(
+        showLoading: MutableState<Boolean>,
+        progressValue: MutableState<Float>,
+        progress: MutableState<String>
+    ) {
+        this.showLoading = showLoading
+        this.progressValue = progressValue
+        this.progress = progress
+        gameHost?.setProgressStates(showLoading, progressValue, progress)
+    }
 }
diff --git a/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/views/HomeViews.kt b/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/views/HomeViews.kt
index 5a8e44167..8f22d9e24 100644
--- a/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/views/HomeViews.kt
+++ b/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/views/HomeViews.kt
@@ -61,14 +61,13 @@ import androidx.compose.ui.zIndex
 import androidx.navigation.NavHostController
 import coil.compose.AsyncImage
 import com.anggrayudi.storage.extension.launchOnUiThread
-import kotlinx.coroutines.launch
-import kotlinx.coroutines.runBlocking
 import org.ryujinx.android.MainActivity
 import org.ryujinx.android.R
 import org.ryujinx.android.viewmodels.GameModel
 import org.ryujinx.android.viewmodels.HomeViewModel
 import java.io.File
 import java.util.Locale
+import kotlin.concurrent.thread
 import kotlin.math.roundToInt
 
 class HomeViews {
@@ -380,20 +379,18 @@ class HomeViews {
                     .combinedClickable(
                         onClick = {
                             if (gameModel.titleId.isNullOrEmpty() || gameModel.titleId != "0000000000000000") {
-                                runBlocking {
-                                    launch {
-                                        showLoading.value = true
-                                        val success =
-                                            viewModel.mainViewModel?.loadGame(gameModel) ?: false
-                                        if (success) {
-                                            launchOnUiThread {
-                                                viewModel.mainViewModel?.navigateToGame()
-                                            }
-                                        } else {
-                                            gameModel.close()
+                                thread {
+                                    showLoading.value = true
+                                    val success =
+                                        viewModel.mainViewModel?.loadGame(gameModel) ?: false
+                                    if (success) {
+                                        launchOnUiThread {
+                                            viewModel.mainViewModel?.navigateToGame()
                                         }
-                                        showLoading.value = false
+                                    } else {
+                                        gameModel.close()
                                     }
+                                    showLoading.value = false
                                 }
                             }
                         },