android: progress bar for disk shaders

This commit is contained in:
SachinVin 2021-03-30 02:00:02 +05:30 committed by bunnei
parent 1acc0884db
commit c5dd439e53
5 changed files with 216 additions and 5 deletions

View File

@ -0,0 +1,136 @@
package org.citra.citra_emu.disk_shader_cache;
import android.app.Activity;
import android.app.Dialog;
import android.os.Bundle;
import android.os.Handler;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.EditText;
import android.widget.FrameLayout;
import android.widget.ProgressBar;
import android.widget.SeekBar;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AlertDialog;
import androidx.fragment.app.DialogFragment;
import androidx.fragment.app.FragmentManager;
import org.citra.citra_emu.CitraApplication;
import org.citra.citra_emu.NativeLibrary;
import org.citra.citra_emu.R;
import org.citra.citra_emu.activities.EmulationActivity;
import java.util.Objects;
public class DiskShaderCacheProgress {
// Equivalent to VideoCore::LoadCallbackStage
public enum LoadCallbackStage {
Prepare,
Decompile,
Build,
Complete,
}
private static final Object finishLock = new Object();
private static ProgressDialogFragment fragment;
public static class ProgressDialogFragment extends DialogFragment {
private final Handler updateHandler = new Handler();
ProgressBar progressBar;
TextView progressText;
AlertDialog dialog;
static ProgressDialogFragment newInstance(String title, String message) {
ProgressDialogFragment frag = new ProgressDialogFragment();
Bundle args = new Bundle();
args.putString("title", title);
args.putString("message", message);
frag.setArguments(args);
return frag;
}
@NonNull
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
final Activity emulationActivity = Objects.requireNonNull(getActivity());
final String title = Objects.requireNonNull(Objects.requireNonNull(getArguments()).getString("title"));
final String message = Objects.requireNonNull(Objects.requireNonNull(getArguments()).getString("message"));
LayoutInflater inflater = LayoutInflater.from(emulationActivity);
View view = inflater.inflate(R.layout.dialog_progress_bar, null);
progressBar = view.findViewById(R.id.progress_bar);
progressText = view.findViewById(R.id.progress_text);
progressText.setText("");
setCancelable(false);
setRetainInstance(true);
AlertDialog.Builder builder = new AlertDialog.Builder(emulationActivity);
builder.setTitle(title);
builder.setMessage(message);
builder.setView(view);
builder.setNegativeButton(R.string.abort_button, (dialog, whichButton) -> {
});
dialog = builder.create();
dialog.create();
synchronized (finishLock) {
finishLock.notifyAll();
}
return dialog;
}
private void onUpdateProgress(LoadCallbackStage stage, int progress, int max) {
updateHandler.post(()->{
progressBar.setProgress(progress);
progressBar.setMax(max);
progressText.setText(String.format("%d/%d", progress, max));
if (stage == LoadCallbackStage.Build){
dialog.setMessage("Building shaders");
}
});
}
}
private static void prepareDialog() {
NativeLibrary.sEmulationActivity.get().runOnUiThread(() -> {
final EmulationActivity emulationActivity = NativeLibrary.sEmulationActivity.get();
fragment = ProgressDialogFragment.newInstance("Loading...", "Preparing shaders");
fragment.show(emulationActivity.getSupportFragmentManager(), "diskShaders");
});
synchronized (finishLock) {
try {
finishLock.wait();
} catch (Exception ignored) {
}
}
}
public static void loadProgress(LoadCallbackStage stage, int progress, int max) {
switch (stage) {
case Prepare:
prepareDialog();
break;
case Decompile:
case Build:
fragment.onUpdateProgress(stage, progress, max);
break;
case Complete:
// Workaround for when dialog is dismissed when the app is in the background
fragment.dismissAllowingStateLoss();
break;
}
}
}

View File

@ -21,6 +21,8 @@ static JavaVM* s_java_vm;
static jclass s_native_library_class;
static jclass s_core_error_class;
static jclass s_savestate_info_class;
static jclass s_disk_cache_progress_class;
static jclass s_load_callback_stage_class;
static jmethodID s_on_core_error;
static jmethodID s_display_alert_msg;
static jmethodID s_display_alert_prompt;
@ -30,6 +32,7 @@ static jmethodID s_landscape_screen_layout;
static jmethodID s_exit_emulation_activity;
static jmethodID s_request_camera_permission;
static jmethodID s_request_mic_permission;
static jmethodID s_disk_cache_load_progress;
namespace IDCache {
@ -64,6 +67,14 @@ jclass GetSavestateInfoClass() {
return s_savestate_info_class;
}
jclass GetDiskCacheProgressClass() {
return s_disk_cache_progress_class;
}
jclass GetDiskCacheLoadCallbackStageClass() {
return s_load_callback_stage_class;
}
jmethodID GetOnCoreError() {
return s_on_core_error;
}
@ -100,6 +111,10 @@ jmethodID GetRequestMicPermission() {
return s_request_mic_permission;
}
jmethodID GetDiskCacheLoadProgress() {
return s_disk_cache_load_progress;
}
} // namespace IDCache
#ifdef __cplusplus
@ -130,6 +145,11 @@ jint JNI_OnLoad(JavaVM* vm, void* reserved) {
env->NewGlobalRef(env->FindClass("org/citra/citra_emu/NativeLibrary$SavestateInfo")));
s_core_error_class = reinterpret_cast<jclass>(
env->NewGlobalRef(env->FindClass("org/citra/citra_emu/NativeLibrary$CoreError")));
s_disk_cache_progress_class = reinterpret_cast<jclass>(
env->NewGlobalRef(env->FindClass("org/citra/citra_emu/disk_shader_cache/DiskShaderCacheProgress")));
s_load_callback_stage_class = reinterpret_cast<jclass>(
env->NewGlobalRef(env->FindClass("org/citra/citra_emu/disk_shader_cache/DiskShaderCacheProgress$LoadCallbackStage")));
s_on_core_error = env->GetStaticMethodID(
s_native_library_class, "OnCoreError",
"(Lorg/citra/citra_emu/NativeLibrary$CoreError;Ljava/lang/String;)Z");
@ -149,6 +169,8 @@ jint JNI_OnLoad(JavaVM* vm, void* reserved) {
env->GetStaticMethodID(s_native_library_class, "RequestCameraPermission", "()Z");
s_request_mic_permission =
env->GetStaticMethodID(s_native_library_class, "RequestMicPermission", "()Z");
s_disk_cache_load_progress =
env->GetStaticMethodID(s_disk_cache_progress_class, "loadProgress", "(Lorg/citra/citra_emu/disk_shader_cache/DiskShaderCacheProgress$LoadCallbackStage;II)V");
MiiSelector::InitJNI(env);
SoftwareKeyboard::InitJNI(env);

View File

@ -14,6 +14,8 @@ JNIEnv* GetEnvForThread();
jclass GetNativeLibraryClass();
jclass GetCoreErrorClass();
jclass GetSavestateInfoClass();
jclass GetDiskCacheProgressClass();
jclass GetDiskCacheLoadCallbackStageClass();
jmethodID GetOnCoreError();
jmethodID GetDisplayAlertMsg();
jmethodID GetDisplayAlertPrompt();
@ -23,6 +25,7 @@ jmethodID GetLandscapeScreenLayout();
jmethodID GetExitEmulationActivity();
jmethodID GetRequestCameraPermission();
jmethodID GetRequestMicPermission();
jmethodID GetDiskCacheLoadProgress();
} // namespace IDCache

View File

@ -119,6 +119,29 @@ static bool HandleCoreError(Core::System::ResultStatus result, const std::string
env->NewStringUTF(details.c_str())) != JNI_FALSE;
}
static jobject ToJavaLoadCallbackStage(VideoCore::LoadCallbackStage stage) {
static const std::map<VideoCore::LoadCallbackStage, const char*> LoadCallbackStageMap{
{VideoCore::LoadCallbackStage::Prepare, "Prepare"},
{VideoCore::LoadCallbackStage::Decompile, "Decompile"},
{VideoCore::LoadCallbackStage::Build, "Build"},
{VideoCore::LoadCallbackStage::Complete, "Complete"},
};
const auto name = LoadCallbackStageMap.at(stage);
JNIEnv* env = IDCache::GetEnvForThread();
const jclass load_callback_stage_class = IDCache::GetDiskCacheLoadCallbackStageClass();
return env->GetStaticObjectField(
load_callback_stage_class, env->GetStaticFieldID(load_callback_stage_class, name,
"Lorg/citra/citra_emu/disk_shader_cache/DiskShaderCacheProgress$LoadCallbackStage;"));
}
static void LoadDiskCacheProgress(VideoCore::LoadCallbackStage stage, int progress, int max) {
JNIEnv* env = IDCache::GetEnvForThread();
env->CallStaticVoidMethod(IDCache::GetDiskCacheProgressClass(), IDCache::GetDiskCacheLoadProgress(), ToJavaLoadCallbackStage(stage), (jint) progress, (jint) max);
}
static Camera::NDK::Factory* g_ndk_factory{};
static void TryShutdown() {
@ -190,22 +213,23 @@ static Core::System::ResultStatus RunCitra(const std::string& filepath) {
is_running = true;
pause_emulation = false;
LoadDiskCacheProgress(VideoCore::LoadCallbackStage::Prepare, 0, 0);
std::unique_ptr<Frontend::GraphicsContext> cpu_context{window->CreateSharedContext()};
if (Settings::values.use_asynchronous_gpu_emulation) {
cpu_context->MakeCurrent();
}
system.Renderer().Rasterizer()->LoadDiskResources(
!is_running, [](VideoCore::LoadCallbackStage stage, std::size_t value, std::size_t total) {
if(value%10 == 0 || value + 1 == total){
LOG_INFO(Frontend, "Shader cache Stage {}: {}/{}", stage, value + 1, total);
}
});
!is_running, &LoadDiskCacheProgress
);
if (Settings::values.use_asynchronous_gpu_emulation) {
cpu_context->DoneCurrent();
}
LoadDiskCacheProgress(VideoCore::LoadCallbackStage::Complete, 0, 0);
SCOPE_EXIT({ TryShutdown(); });
// Audio stretching on Android is only useful with lower framerates, disable it when fullspeed

View File

@ -0,0 +1,26 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<ProgressBar
android:id="@+id/progress_bar"
style="?android:attr/progressBarStyleHorizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/spacing_large"
android:layout_marginRight="@dimen/spacing_large"
android:layout_alignParentEnd="true"
android:layout_below="@+id/progress_text"
android:layout_alignParentStart="true"/>
<TextView
android:id="@+id/progress_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/spacing_large"
android:layout_marginRight="@dimen/spacing_large"
android:gravity="right"
android:text="1/100" />
</LinearLayout>