android: progress bar for disk shaders
This commit is contained in:
parent
1acc0884db
commit
c5dd439e53
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
@ -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);
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
26
src/android/app/src/main/res/layout/dialog_progress_bar.xml
Normal file
26
src/android/app/src/main/res/layout/dialog_progress_bar.xml
Normal 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>
|
Loading…
x
Reference in New Issue
Block a user