Implement graphics pipelines loading screen core logic

This commit is contained in:
lynxnb 2023-04-24 17:44:39 +02:00
parent 8c2b39858d
commit 3bb24a52ab
8 changed files with 180 additions and 21 deletions

View File

@ -189,6 +189,7 @@ dependencies {
/* Other Java */
implementation 'info.debatty:java-string-similarity:2.0.0'
implementation 'com.github.KikiManjaro:colorpicker:v1.1.12'
implementation 'com.github.android:renderscript-intrinsics-replacement-toolkit:344be3f'
}
kapt {

View File

@ -63,8 +63,11 @@ namespace skyline {
waitForSubmitOrCancelId{environ->GetMethodID(instanceClass, "waitForSubmitOrCancel", "(Lemu/skyline/applet/swkbd/SoftwareKeyboardDialog;)[Ljava/lang/Object;")},
closeKeyboardId{environ->GetMethodID(instanceClass, "closeKeyboard", "(Lemu/skyline/applet/swkbd/SoftwareKeyboardDialog;)V")},
showValidationResultId{environ->GetMethodID(instanceClass, "showValidationResult", "(Lemu/skyline/applet/swkbd/SoftwareKeyboardDialog;ILjava/lang/String;)I")},
getVersionCodeId{environ->GetMethodID(instanceClass, "getVersionCode", "()I")},
getIntegerValueId{environ->GetMethodID(environ->FindClass("java/lang/Integer"), "intValue", "()I")} {
getIntegerValueId{environ->GetMethodID(environ->FindClass("java/lang/Integer"), "intValue", "()I")},
showPipelineLoadingScreenId{environ->GetMethodID(instanceClass, "showPipelineLoadingScreen", "(I)V")},
updatePipelineLoadingProgressId{environ->GetMethodID(instanceClass, "updatePipelineLoadingProgress", "(I)V")},
hidePipelineLoadingScreenId{environ->GetMethodID(instanceClass, "hidePipelineLoadingScreen", "()V")},
getVersionCodeId{environ->GetMethodID(instanceClass, "getVersionCode", "()I")} {
env.Initialize(environ);
}
@ -137,14 +140,26 @@ namespace skyline {
env->DeleteGlobalRef(dialog);
}
i32 JvmManager::GetVersionCode() {
return env->CallIntMethod(instance, getVersionCodeId);
}
JvmManager::KeyboardCloseResult JvmManager::ShowValidationResult(jobject dialog, KeyboardTextCheckResult checkResult, std::u16string message) {
auto str{env->NewString(reinterpret_cast<const jchar *>(message.data()), static_cast<int>(message.length()))};
auto result{static_cast<KeyboardCloseResult>(env->CallIntMethod(instance, showValidationResultId, dialog, checkResult, str))};
env->DeleteLocalRef(str);
return result;
}
void JvmManager::ShowPipelineLoadingScreen(u32 totalPipelineCount) {
env->CallVoidMethod(instance, showPipelineLoadingScreenId, static_cast<jint>(totalPipelineCount));
}
void JvmManager::UpdatePipelineLoadingProgress(u32 progress) {
env->CallVoidMethod(instance, updatePipelineLoadingProgressId, static_cast<jint>(progress));
}
void JvmManager::HidePipelineLoadingScreen() {
env->CallVoidMethod(instance, hidePipelineLoadingScreenId);
}
i32 JvmManager::GetVersionCode() {
return env->CallIntMethod(instance, getVersionCodeId);
}
}

View File

@ -176,6 +176,21 @@ namespace skyline {
*/
KeyboardCloseResult ShowValidationResult(KeyboardHandle dialog, KeyboardTextCheckResult checkResult, std::u16string message);
/**
* @brief A call to EmulationActivity.showPipelineLoadingScreen in Kotlin
*/
void ShowPipelineLoadingScreen(u32 totalPipelineCount);
/**
* @brief A call to EmulationActivity.updatePipelineLoadingProgress in Kotlin
*/
void UpdatePipelineLoadingProgress(u32 progress);
/**
* @brief A call to EmulationActivity.hidePipelineLoadingScreen in Kotlin
*/
void HidePipelineLoadingScreen();
/**
* @brief A call to EmulationActivity.getVersionCode in Kotlin
* @return A version code in Vulkan's format with 14-bit patch + 10-bit major and minor components
@ -186,12 +201,17 @@ namespace skyline {
jmethodID initializeControllersId;
jmethodID vibrateDeviceId;
jmethodID clearVibrationDeviceId;
jmethodID showKeyboardId;
jmethodID waitForSubmitOrCancelId;
jmethodID closeKeyboardId;
jmethodID showValidationResultId;
jmethodID getVersionCodeId;
jmethodID getIntegerValueId;
jmethodID showPipelineLoadingScreenId;
jmethodID updatePipelineLoadingProgressId;
jmethodID hidePipelineLoadingScreenId;
jmethodID getVersionCodeId;
};
}

View File

@ -32,12 +32,12 @@ import androidx.core.view.updateMargins
import androidx.fragment.app.FragmentTransaction
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import dagger.hilt.android.AndroidEntryPoint
import emu.skyline.BuildConfig
import emu.skyline.applet.swkbd.SoftwareKeyboardConfig
import emu.skyline.applet.swkbd.SoftwareKeyboardDialog
import emu.skyline.data.AppItem
import emu.skyline.data.AppItemTag
import emu.skyline.databinding.EmuActivityBinding
import emu.skyline.emulation.PipelineLoadingFragment
import emu.skyline.input.*
import emu.skyline.loader.RomFile
import emu.skyline.loader.getRomFormat
@ -446,7 +446,7 @@ class EmulationActivity : AppCompatActivity(), SurfaceHolder.Callback, View.OnTo
}
}
override fun onPictureInPictureModeChanged(isInPictureInPictureMode: Boolean, newConfig: Configuration) {
override fun onPictureInPictureModeChanged(isInPictureInPictureMode : Boolean, newConfig : Configuration) {
super.onPictureInPictureModeChanged(isInPictureInPictureMode, newConfig)
if (isInPictureInPictureMode) {
@ -463,10 +463,11 @@ class EmulationActivity : AppCompatActivity(), SurfaceHolder.Callback, View.OnTo
} else {
try {
unregisterReceiver(pictureInPictureReceiver)
} catch (ignored : Exception) { }
} catch (ignored : Exception) {
}
resumeEmulator()
binding.onScreenControllerView.apply {
controllerType = inputHandler.getFirstControllerType()
isGone = controllerType == ControllerType.None || !appSettings.onScreenControl
@ -511,6 +512,44 @@ class EmulationActivity : AppCompatActivity(), SurfaceHolder.Callback, View.OnTo
vibrators.clear()
}
private lateinit var pipelineLoadingFragment : PipelineLoadingFragment
/**
* Called by native code to show the pipeline loading progress screen
*/
@Suppress("unused")
fun showPipelineLoadingScreen(totalPipelineCount : Int) {
pipelineLoadingFragment = PipelineLoadingFragment.newInstance(item, totalPipelineCount)
supportFragmentManager
.beginTransaction()
.setCustomAnimations(android.R.anim.fade_in, android.R.anim.fade_out)
.replace(R.id.emulation_fragment, pipelineLoadingFragment)
.commit()
}
/**
* Called by native code to update the pipeline loading progress
*/
@Suppress("unused")
fun updatePipelineLoadingProgress(progress : Int) {
runOnUiThread {
pipelineLoadingFragment.updateProgress(progress)
}
}
/**
* Called by native code to hide the pipeline loading progress screen
*/
@Suppress("unused")
fun hidePipelineLoadingScreen() {
supportFragmentManager
.beginTransaction()
.setCustomAnimations(android.R.anim.fade_in, android.R.anim.fade_out)
.remove(pipelineLoadingFragment)
.commit()
}
override fun surfaceCreated(holder : SurfaceHolder) {
Log.d(Tag, "surfaceCreated Holder: $holder")

View File

@ -0,0 +1,79 @@
/*
* SPDX-License-Identifier: MPL-2.0
* Copyright © 2023 Skyline Team and Contributors (https://github.com/skyline-emu/)
*/
package emu.skyline.emulation
import android.annotation.SuppressLint
import android.graphics.RenderEffect
import android.graphics.Shader
import android.os.Build
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import com.google.android.renderscript.Toolkit
import emu.skyline.data.AppItem
import emu.skyline.data.AppItemTag
import emu.skyline.databinding.PipelineLoadingBinding
import emu.skyline.utils.serializable
private const val TotalPipelineCountTag = "PipelineLoadingFragment::TotalCount"
private const val PipelineProgressTag = "PipelineLoadingFragment::Progress"
class PipelineLoadingFragment : Fragment() {
private val item by lazy { requireArguments().serializable<AppItem>(AppItemTag)!! }
private val totalPipelineCount by lazy { requireArguments().getInt(TotalPipelineCountTag) }
private lateinit var binding : PipelineLoadingBinding
override fun onCreateView(inflater : LayoutInflater, container : ViewGroup?, savedInstanceState : Bundle?) = PipelineLoadingBinding.inflate(inflater).also { binding = it }.root
@SuppressLint("SetTextI18n")
override fun onViewCreated(view : View, savedInstanceState : Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding.gameTitle.apply {
text = item.title
isSelected = true
}
binding.gameVersion.text = item.version
binding.gameIcon.setImageBitmap(item.bitmapIcon)
val progress = savedInstanceState?.getInt(PipelineProgressTag) ?: 0
binding.progressBar.max = totalPipelineCount
updateProgress(progress)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
binding.backgroundImage.setImageBitmap(item.bitmapIcon)
binding.backgroundImage.setRenderEffect(RenderEffect.createBlurEffect(75f, 75f, Shader.TileMode.MIRROR))
} else {
binding.backgroundImage.setImageBitmap(Toolkit.blur(item.bitmapIcon, 15))
}
}
override fun onSaveInstanceState(outState : Bundle) {
super.onSaveInstanceState(outState)
outState.putInt(PipelineProgressTag, binding.progressBar.progress)
}
@SuppressLint("SetTextI18n")
fun updateProgress(progress : Int) {
if (!this::binding.isInitialized)
return
binding.progressBar.progress = progress
binding.progressLabel.text = "$progress/$totalPipelineCount (${(progress.toFloat() / totalPipelineCount * 100).toInt()}%)"
}
companion object {
fun newInstance(item : AppItem, totalPipelineCount : Int) = PipelineLoadingFragment().apply {
arguments = Bundle().apply {
putSerializable(AppItemTag, item)
putInt(TotalPipelineCountTag, totalPipelineCount)
}
}
}
}

View File

@ -27,8 +27,8 @@
android:layout_gravity="top|left"
android:layout_marginLeft="@dimen/onScreenItemHorizontalMargin"
android:layout_marginTop="5dp"
tools:text="60 FPS\n16.6±0.10ms"
android:textColor="@color/colorPerfStatsPrimary" />
android:textColor="@color/colorPerfStatsPrimary"
tools:text="60 FPS\n16.6±0.10ms" />
<ImageButton
android:id="@+id/on_screen_pause_toggle"
@ -53,4 +53,9 @@
android:src="@drawable/ic_show"
app:tint="#40FFFFFF"
tools:ignore="ContentDescription" />
<FrameLayout
android:id="@+id/emulation_fragment"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</FrameLayout>

View File

@ -6,7 +6,7 @@
android:layout_height="match_parent">
<com.google.android.material.imageview.ShapeableImageView
android:id="@+id/game_icon_bg"
android:id="@+id/background_image"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:contentDescription="@string/icon"
@ -52,7 +52,7 @@
android:layout_marginStart="16dp"
android:maxWidth="360dp"
android:orientation="vertical"
app:layout_constraintBottom_toTopOf="@+id/shader_info"
app:layout_constraintBottom_toTopOf="@+id/pipeline_info"
app:layout_constraintEnd_toEndOf="@id/guidelineTextEdge"
app:layout_constraintStart_toEndOf="@id/guidelineIconText"
app:layout_constraintTop_toTopOf="parent">
@ -80,7 +80,7 @@
</LinearLayout>
<LinearLayout
android:id="@+id/shader_info"
android:id="@+id/pipeline_info"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
@ -90,11 +90,11 @@
app:layout_constraintTop_toBottomOf="@+id/game_info">
<TextView
android:id="@+id/shaders_compiling"
android:id="@+id/pipelines_compiling"
style="?attr/textAppearanceBodyMedium"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/compiling_cached_shaders"
android:text="@string/compiling_cached_pipelines"
android:textColor="@android:color/white"
android:textSize="17sp"
app:layout_constraintStart_toStartOf="parent"
@ -109,7 +109,7 @@
app:trackThickness="10dp" />
<TextView
android:id="@+id/progress"
android:id="@+id/progress_label"
style="?attr/textAppearanceBodyMedium"
android:layout_width="wrap_content"
android:layout_height="wrap_content"

View File

@ -256,7 +256,7 @@
<!-- Software Keyboard -->
<string name="input_hint">Input Text</string>
<!-- Misc -->
<string name="compiling_cached_shaders">Compiling cached shaders...</string>
<string name="compiling_cached_pipelines">Compiling cached pipelines...</string>
<!--suppress AndroidLintUnusedResources -->
<string name="expand_button_title" tools:override="true">Expand</string>
<string name="undo">Undo</string>