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_native_library_class;
|
||||||
static jclass s_core_error_class;
|
static jclass s_core_error_class;
|
||||||
static jclass s_savestate_info_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_on_core_error;
|
||||||
static jmethodID s_display_alert_msg;
|
static jmethodID s_display_alert_msg;
|
||||||
static jmethodID s_display_alert_prompt;
|
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_exit_emulation_activity;
|
||||||
static jmethodID s_request_camera_permission;
|
static jmethodID s_request_camera_permission;
|
||||||
static jmethodID s_request_mic_permission;
|
static jmethodID s_request_mic_permission;
|
||||||
|
static jmethodID s_disk_cache_load_progress;
|
||||||
|
|
||||||
namespace IDCache {
|
namespace IDCache {
|
||||||
|
|
||||||
@ -64,6 +67,14 @@ jclass GetSavestateInfoClass() {
|
|||||||
return s_savestate_info_class;
|
return s_savestate_info_class;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
jclass GetDiskCacheProgressClass() {
|
||||||
|
return s_disk_cache_progress_class;
|
||||||
|
}
|
||||||
|
|
||||||
|
jclass GetDiskCacheLoadCallbackStageClass() {
|
||||||
|
return s_load_callback_stage_class;
|
||||||
|
}
|
||||||
|
|
||||||
jmethodID GetOnCoreError() {
|
jmethodID GetOnCoreError() {
|
||||||
return s_on_core_error;
|
return s_on_core_error;
|
||||||
}
|
}
|
||||||
@ -100,6 +111,10 @@ jmethodID GetRequestMicPermission() {
|
|||||||
return s_request_mic_permission;
|
return s_request_mic_permission;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
jmethodID GetDiskCacheLoadProgress() {
|
||||||
|
return s_disk_cache_load_progress;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace IDCache
|
} // namespace IDCache
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
@ -130,6 +145,11 @@ jint JNI_OnLoad(JavaVM* vm, void* reserved) {
|
|||||||
env->NewGlobalRef(env->FindClass("org/citra/citra_emu/NativeLibrary$SavestateInfo")));
|
env->NewGlobalRef(env->FindClass("org/citra/citra_emu/NativeLibrary$SavestateInfo")));
|
||||||
s_core_error_class = reinterpret_cast<jclass>(
|
s_core_error_class = reinterpret_cast<jclass>(
|
||||||
env->NewGlobalRef(env->FindClass("org/citra/citra_emu/NativeLibrary$CoreError")));
|
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_on_core_error = env->GetStaticMethodID(
|
||||||
s_native_library_class, "OnCoreError",
|
s_native_library_class, "OnCoreError",
|
||||||
"(Lorg/citra/citra_emu/NativeLibrary$CoreError;Ljava/lang/String;)Z");
|
"(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");
|
env->GetStaticMethodID(s_native_library_class, "RequestCameraPermission", "()Z");
|
||||||
s_request_mic_permission =
|
s_request_mic_permission =
|
||||||
env->GetStaticMethodID(s_native_library_class, "RequestMicPermission", "()Z");
|
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);
|
MiiSelector::InitJNI(env);
|
||||||
SoftwareKeyboard::InitJNI(env);
|
SoftwareKeyboard::InitJNI(env);
|
||||||
|
@ -14,6 +14,8 @@ JNIEnv* GetEnvForThread();
|
|||||||
jclass GetNativeLibraryClass();
|
jclass GetNativeLibraryClass();
|
||||||
jclass GetCoreErrorClass();
|
jclass GetCoreErrorClass();
|
||||||
jclass GetSavestateInfoClass();
|
jclass GetSavestateInfoClass();
|
||||||
|
jclass GetDiskCacheProgressClass();
|
||||||
|
jclass GetDiskCacheLoadCallbackStageClass();
|
||||||
jmethodID GetOnCoreError();
|
jmethodID GetOnCoreError();
|
||||||
jmethodID GetDisplayAlertMsg();
|
jmethodID GetDisplayAlertMsg();
|
||||||
jmethodID GetDisplayAlertPrompt();
|
jmethodID GetDisplayAlertPrompt();
|
||||||
@ -23,6 +25,7 @@ jmethodID GetLandscapeScreenLayout();
|
|||||||
jmethodID GetExitEmulationActivity();
|
jmethodID GetExitEmulationActivity();
|
||||||
jmethodID GetRequestCameraPermission();
|
jmethodID GetRequestCameraPermission();
|
||||||
jmethodID GetRequestMicPermission();
|
jmethodID GetRequestMicPermission();
|
||||||
|
jmethodID GetDiskCacheLoadProgress();
|
||||||
|
|
||||||
} // namespace IDCache
|
} // namespace IDCache
|
||||||
|
|
||||||
|
@ -119,6 +119,29 @@ static bool HandleCoreError(Core::System::ResultStatus result, const std::string
|
|||||||
env->NewStringUTF(details.c_str())) != JNI_FALSE;
|
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 Camera::NDK::Factory* g_ndk_factory{};
|
||||||
|
|
||||||
static void TryShutdown() {
|
static void TryShutdown() {
|
||||||
@ -190,22 +213,23 @@ static Core::System::ResultStatus RunCitra(const std::string& filepath) {
|
|||||||
is_running = true;
|
is_running = true;
|
||||||
pause_emulation = false;
|
pause_emulation = false;
|
||||||
|
|
||||||
|
LoadDiskCacheProgress(VideoCore::LoadCallbackStage::Prepare, 0, 0);
|
||||||
|
|
||||||
std::unique_ptr<Frontend::GraphicsContext> cpu_context{window->CreateSharedContext()};
|
std::unique_ptr<Frontend::GraphicsContext> cpu_context{window->CreateSharedContext()};
|
||||||
if (Settings::values.use_asynchronous_gpu_emulation) {
|
if (Settings::values.use_asynchronous_gpu_emulation) {
|
||||||
cpu_context->MakeCurrent();
|
cpu_context->MakeCurrent();
|
||||||
}
|
}
|
||||||
|
|
||||||
system.Renderer().Rasterizer()->LoadDiskResources(
|
system.Renderer().Rasterizer()->LoadDiskResources(
|
||||||
!is_running, [](VideoCore::LoadCallbackStage stage, std::size_t value, std::size_t total) {
|
!is_running, &LoadDiskCacheProgress
|
||||||
if(value%10 == 0 || value + 1 == total){
|
);
|
||||||
LOG_INFO(Frontend, "Shader cache Stage {}: {}/{}", stage, value + 1, total);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (Settings::values.use_asynchronous_gpu_emulation) {
|
if (Settings::values.use_asynchronous_gpu_emulation) {
|
||||||
cpu_context->DoneCurrent();
|
cpu_context->DoneCurrent();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
LoadDiskCacheProgress(VideoCore::LoadCallbackStage::Complete, 0, 0);
|
||||||
|
|
||||||
SCOPE_EXIT({ TryShutdown(); });
|
SCOPE_EXIT({ TryShutdown(); });
|
||||||
|
|
||||||
// Audio stretching on Android is only useful with lower framerates, disable it when fullspeed
|
// 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