Merge branch 'opengl_renderer'

This commit is contained in:
Rafael Caetano 2024-01-02 23:00:07 +00:00
commit 52f9022fad
22 changed files with 278 additions and 40 deletions

View File

@ -32,6 +32,7 @@ if (ENABLE_JIT)
add_definitions(-DJIT_ENABLED)
endif()
add_definitions(-DENABLE_OGLRENDERER)
set(CORE-LIB ../melonDS-android-lib)
add_subdirectory(${CORE-LIB} ./melonDS-android-lib)
include_directories(${CORE-LIB}/src/android)

View File

@ -10,6 +10,8 @@
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_DATA_SYNC" />
<uses-feature android:glEsVersion="0x00030002" android:required="false" />
<application
android:name=".MelonDSApplication"
android:allowBackup="true"

View File

@ -10,9 +10,13 @@ MelonDSAndroid::EmulatorConfiguration MelonDSAndroidConfiguration::buildEmulator
jclass audioInterpolationEnumClass = env->FindClass("me/magnum/melonds/domain/model/AudioInterpolation");
jclass audioLatencyEnumClass = env->FindClass("me/magnum/melonds/domain/model/AudioLatency");
jclass micSourceEnumClass = env->FindClass("me/magnum/melonds/domain/model/MicSource");
jclass videoRendererEnumClass = env->FindClass("me/magnum/melonds/domain/model/VideoRenderer");
jclass renderConfigurationClass = env->FindClass("me/magnum/melonds/domain/model/RendererConfiguration");
jmethodID uriToStringMethod = env->GetMethodID(uriClass, "toString", "()Ljava/lang/String;");
jobject firmwareConfigurationObject = env->GetObjectField(emulatorConfiguration, env->GetFieldID(emulatorConfigurationClass, "firmwareConfiguration", "Lme/magnum/melonds/domain/model/FirmwareConfiguration;"));
jobject rendererConfigurationObject = env->GetObjectField(emulatorConfiguration, env->GetFieldID(emulatorConfigurationClass, "rendererConfiguration", "Lme/magnum/melonds/domain/model/RendererConfiguration;"));
jboolean useCustomBios = env->GetBooleanField(emulatorConfiguration, env->GetFieldID(emulatorConfigurationClass, "useCustomBios", "Z"));
jboolean showBootScreen = env->GetBooleanField(emulatorConfiguration, env->GetFieldID(emulatorConfigurationClass, "showBootScreen", "Z"));
jobject dsBios7Uri = env->GetObjectField(emulatorConfiguration, env->GetFieldID(emulatorConfigurationClass, "dsBios7Uri", "Landroid/net/Uri;"));
@ -40,6 +44,8 @@ MelonDSAndroid::EmulatorConfiguration MelonDSAndroidConfiguration::buildEmulator
jint audioLatency = env->GetIntField(audioLatencyEnum, env->GetFieldID(audioLatencyEnumClass, "latencyValue", "I"));
jobject micSourceEnum = env->GetObjectField(emulatorConfiguration, env->GetFieldID(emulatorConfigurationClass, "micSource", "Lme/magnum/melonds/domain/model/MicSource;"));
jint micSource = env->GetIntField(micSourceEnum, env->GetFieldID(micSourceEnumClass, "sourceValue", "I"));
jobject videoRendererEnum = env->GetObjectField(rendererConfigurationObject, env->GetFieldID(renderConfigurationClass, "renderer", "Lme/magnum/melonds/domain/model/VideoRenderer;"));
jint videoRenderer = env->GetIntField(videoRendererEnum, env->GetFieldID(videoRendererEnumClass, "renderer", "I"));
jboolean isCopy = JNI_FALSE;
jstring dsBios7String = dsBios7Uri ? (jstring) env->CallObjectMethod(dsBios7Uri, uriToStringMethod) : nullptr;
jstring dsBios9String = dsBios9Uri ? (jstring) env->CallObjectMethod(dsBios9Uri, uriToStringMethod) : nullptr;
@ -56,8 +62,6 @@ MelonDSAndroid::EmulatorConfiguration MelonDSAndroidConfiguration::buildEmulator
const char* dsiFirmwarePath = dsiFirmwareUri ? env->GetStringUTFChars(dsiFirmwareString, &isCopy) : nullptr;
const char* dsiNandPath = dsiNandUri ? env->GetStringUTFChars(dsiNandString, &isCopy) : nullptr;
const char* internalDir = env->GetStringUTFChars(internalFilesDir, nullptr);
jobject firmwareConfigurationObject = env->GetObjectField(emulatorConfiguration, env->GetFieldID(emulatorConfigurationClass, "firmwareConfiguration", "Lme/magnum/melonds/domain/model/FirmwareConfiguration;"));
jobject rendererConfigurationObject = env->GetObjectField(emulatorConfiguration, env->GetFieldID(emulatorConfigurationClass, "rendererConfiguration", "Lme/magnum/melonds/domain/model/RendererConfiguration;"));
MelonDSAndroid::EmulatorConfiguration finalEmulatorConfiguration;
finalEmulatorConfiguration.userInternalFirmwareAndBios = !useCustomBios;
@ -84,6 +88,7 @@ MelonDSAndroid::EmulatorConfiguration MelonDSAndroidConfiguration::buildEmulator
finalEmulatorConfiguration.rewindEnabled = enableRewind ? 1 : 0;
finalEmulatorConfiguration.rewindCaptureSpacingSeconds = rewindPeriodSeconds;
finalEmulatorConfiguration.rewindLengthSeconds = rewindWindowSeconds;
finalEmulatorConfiguration.renderer = videoRenderer;
return finalEmulatorConfiguration;
}
@ -134,10 +139,12 @@ MelonDSAndroid::FirmwareConfiguration MelonDSAndroidConfiguration::buildFirmware
GPU::RenderSettings MelonDSAndroidConfiguration::buildRenderSettings(JNIEnv* env, jobject renderSettings) {
jclass renderSettingsClass = env->GetObjectClass(renderSettings);
jmethodID getResolutionScalingMethod = env->GetMethodID(renderSettingsClass, "getResolutionScaling", "()I");
jboolean threadedRendering = env->GetBooleanField(renderSettings, env->GetFieldID(renderSettingsClass, "threadedRendering", "Z"));
jint internalResolutionScaling = env->CallIntMethod(renderSettings, getResolutionScalingMethod);
return {
threadedRendering == JNI_TRUE,
1,
false
threadedRendering == JNI_TRUE,
internalResolutionScaling,
false
};
}

View File

@ -452,10 +452,12 @@ Java_me_magnum_melonds_MelonEmulator_setFastForwardEnabled(JNIEnv* env, jobject
}
JNIEXPORT void JNICALL
Java_me_magnum_melonds_MelonEmulator_updateEmulatorConfiguration(JNIEnv* env, jobject thiz, jobject emulatorConfiguration)
Java_me_magnum_melonds_MelonEmulator_updateEmulatorConfiguration(JNIEnv* env, jobject thiz, jobject emulatorConfiguration, jobject frameBuffer)
{
MelonDSAndroid::EmulatorConfiguration newConfiguration = MelonDSAndroidConfiguration::buildEmulatorConfiguration(env, emulatorConfiguration);
MelonDSAndroid::updateEmulatorConfiguration(newConfiguration);
u32* frameBufferPointer = (u32*) env->GetDirectBufferAddress(frameBuffer);
MelonDSAndroid::updateEmulatorConfiguration(newConfiguration, frameBufferPointer);
fastForwardSpeedMultiplier = newConfiguration.fastForwardSpeedMultiplier;
if (isFastForwardEnabled) {

View File

@ -113,5 +113,5 @@ object MelonEmulator {
external fun setFastForwardEnabled(enabled: Boolean)
external fun updateEmulatorConfiguration(emulatorConfiguration: EmulatorConfiguration)
external fun updateEmulatorConfiguration(emulatorConfiguration: EmulatorConfiguration, frameBuffer: ByteBuffer)
}

View File

@ -1,6 +1,7 @@
package me.magnum.melonds.common.runtime
import android.graphics.Bitmap
import me.magnum.melonds.domain.model.RendererConfiguration
import java.nio.ByteBuffer
import java.nio.ByteOrder
@ -11,23 +12,41 @@ class FrameBufferProvider {
private const val SCREEN_HEIGHT = 384
}
private val frameBuffer by lazy {
ByteBuffer.allocateDirect(SCREEN_WIDTH * SCREEN_HEIGHT * 4).order(ByteOrder.nativeOrder())
private var rendererConfiguration: RendererConfiguration? = null
private var frameBuffer: ByteBuffer? = null
fun setRendererConfiguration(configuration: RendererConfiguration) {
val mustResizeFrameBuffer = isFrameBufferReady() && rendererConfiguration?.resolutionScaling != configuration.resolutionScaling
rendererConfiguration = configuration
if (mustResizeFrameBuffer) {
frameBuffer = null
ensureFrameBufferIsReady()
}
}
fun isFrameBufferReady(): Boolean {
return frameBuffer != null
}
fun frameBuffer(): ByteBuffer {
return frameBuffer
return ensureFrameBufferIsReady()
}
fun getScreenshot(): Bitmap {
val frameBuffer = ensureFrameBufferIsReady()
val scale = rendererConfiguration?.resolutionScaling ?: 1
val scaledWidth = SCREEN_WIDTH * scale
return Bitmap.createBitmap(SCREEN_WIDTH, SCREEN_HEIGHT, Bitmap.Config.ARGB_8888).apply {
// Texture buffer is in BGR format. Convert to RGB
for (x in 0 until SCREEN_WIDTH) {
for (y in 0 until SCREEN_HEIGHT) {
val b = frameBuffer[(y * SCREEN_WIDTH + x) * 4 + 0].toInt() and 0xFF
val g = frameBuffer[(y * SCREEN_WIDTH + x) * 4 + 1].toInt() and 0xFF
val r = frameBuffer[(y * SCREEN_WIDTH + x) * 4 + 2].toInt() and 0xFF
val argbPixel = 0xFF000000.toInt() or r.shl(16) or g.shl(8) or b
val pixelPosition = (y * scale * scaledWidth + x * scale) * 4
// There's no need to do a manual pixel format conversion. Since getInt() uses the buffer's byte order, which is little endian, it will automatically
// convert the internal BGRA format into the ARGB format, which is what we need to build the bitmap
val argbPixel = frameBuffer.getInt(pixelPosition)
setPixel(x, y, argbPixel)
}
}
@ -35,9 +54,23 @@ class FrameBufferProvider {
}
fun clearFrameBuffer() {
frameBuffer.position(0)
repeat(frameBuffer.capacity() / 4) {
frameBuffer.putInt(0xFF000000.toInt())
frameBuffer?.let { buffer ->
buffer.position(0)
repeat(buffer.capacity() / 4) {
buffer.putInt(0xFF000000.toInt())
}
}
}
private fun ensureFrameBufferIsReady(): ByteBuffer {
if (frameBuffer != null) {
return frameBuffer!!
}
val rendererConfiguration = requireNotNull(rendererConfiguration)
val scaledWidth = SCREEN_WIDTH * rendererConfiguration.resolutionScaling
val scaledHeight= SCREEN_HEIGHT * rendererConfiguration.resolutionScaling
frameBuffer = ByteBuffer.allocateDirect(scaledWidth * scaledHeight * 4).order(ByteOrder.nativeOrder())
return frameBuffer!!
}
}

View File

@ -12,6 +12,8 @@ import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.android.qualifiers.ApplicationContext
import dagger.hilt.components.SingletonComponent
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import me.magnum.melonds.common.romprocessors.RomFileProcessorFactory
import me.magnum.melonds.common.uridelegates.UriHandler
import me.magnum.melonds.common.vibration.Api26VibratorDelegate
@ -37,7 +39,7 @@ object MelonModule {
@Provides
@Singleton
fun provideSettingsRepository(@ApplicationContext context: Context, sharedPreferences: SharedPreferences, gson: Gson, uriHandler: UriHandler): SettingsRepository {
return SharedPreferencesSettingsRepository(context, sharedPreferences, gson, uriHandler)
return SharedPreferencesSettingsRepository(context, sharedPreferences, gson, uriHandler, CoroutineScope(Dispatchers.IO))
}
@Provides

View File

@ -1,3 +1,19 @@
package me.magnum.melonds.domain.model
data class RendererConfiguration(val videoFiltering: VideoFiltering, val threadedRendering: Boolean)
data class RendererConfiguration(
val renderer: VideoRenderer,
private val internalVideoFiltering: VideoFiltering,
val threadedRendering: Boolean,
private val internalResolutionScaling: Int,
) {
val videoFiltering get() = when (renderer) {
VideoRenderer.SOFTWARE -> internalVideoFiltering
VideoRenderer.OPENGL -> VideoFiltering.NONE
}
val resolutionScaling get() = when (renderer) {
VideoRenderer.SOFTWARE -> 1
VideoRenderer.OPENGL -> internalResolutionScaling
}
}

View File

@ -0,0 +1,6 @@
package me.magnum.melonds.domain.model
enum class VideoRenderer(val renderer: Int) {
SOFTWARE(0),
OPENGL(1),
}

View File

@ -29,8 +29,10 @@ interface SettingsRepository {
fun showBootScreen(): Boolean
fun isJitEnabled(): Boolean
fun getVideoRenderer(): Flow<VideoRenderer>
fun getVideoInternalResolutionScaling(): Flow<Int>
fun getVideoFiltering(): Flow<VideoFiltering>
fun isThreadedRenderingEnabled(): Boolean
fun isThreadedRenderingEnabled(): Flow<Boolean>
fun getFpsCounterPosition(): FpsCounterPosition
fun getDSiCameraSource(): DSiCameraSourceType
fun getDSiCameraStaticImage(): Uri?
@ -73,4 +75,6 @@ interface SettingsRepository {
fun setRomSortingMode(sortingMode: SortingMode)
fun setRomSortingOrder(sortingOrder: SortingOrder)
fun setSelectedLayoutId(layoutId: UUID)
fun observeRenderConfiguration(): Flow<RendererConfiguration>
}

View File

@ -12,13 +12,38 @@ import androidx.documentfile.provider.DocumentFile
import com.google.gson.Gson
import io.reactivex.Observable
import io.reactivex.subjects.PublishSubject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.channels.BufferOverflow
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.SharedFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.conflate
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.shareIn
import me.magnum.melonds.common.uridelegates.UriHandler
import me.magnum.melonds.domain.model.*
import me.magnum.melonds.domain.model.AudioBitrate
import me.magnum.melonds.domain.model.AudioInterpolation
import me.magnum.melonds.domain.model.AudioLatency
import me.magnum.melonds.domain.model.ConsoleType
import me.magnum.melonds.domain.model.ControllerConfiguration
import me.magnum.melonds.domain.model.EmulatorConfiguration
import me.magnum.melonds.domain.model.FirmwareConfiguration
import me.magnum.melonds.domain.model.FpsCounterPosition
import me.magnum.melonds.domain.model.LayoutConfiguration
import me.magnum.melonds.domain.model.MacAddress
import me.magnum.melonds.domain.model.MicSource
import me.magnum.melonds.domain.model.RendererConfiguration
import me.magnum.melonds.domain.model.Rom
import me.magnum.melonds.domain.model.RomIconFiltering
import me.magnum.melonds.domain.model.SaveStateLocation
import me.magnum.melonds.domain.model.SizeUnit
import me.magnum.melonds.domain.model.SortingMode
import me.magnum.melonds.domain.model.SortingOrder
import me.magnum.melonds.domain.model.VideoFiltering
import me.magnum.melonds.domain.model.VideoRenderer
import me.magnum.melonds.domain.model.camera.DSiCameraSourceType
import me.magnum.melonds.domain.repositories.SettingsRepository
import me.magnum.melonds.extensions.isSustainedPerformanceModeAvailable
@ -28,14 +53,15 @@ import java.io.File
import java.io.FileReader
import java.io.IOException
import java.io.OutputStreamWriter
import java.util.*
import java.util.UUID
import kotlin.math.pow
class SharedPreferencesSettingsRepository(
private val context: Context,
private val preferences: SharedPreferences,
private val gson: Gson,
private val uriHandler: UriHandler
private val uriHandler: UriHandler,
preferencesCoroutineScope: CoroutineScope,
) : SettingsRepository, OnSharedPreferenceChangeListener {
companion object {
@ -46,11 +72,21 @@ class SharedPreferencesSettingsRepository(
private var controllerConfiguration: ControllerConfiguration? = null
private val preferenceObservers: HashMap<String, PublishSubject<Any>> = HashMap()
private val preferenceSharedFlows = mutableMapOf<String, MutableSharedFlow<Unit>>()
private val renderConfigurationFlow: SharedFlow<RendererConfiguration>
init {
preferences.registerOnSharedPreferenceChangeListener(this)
setDefaultThemeIfRequired()
setDefaultMacAddressIfRequired()
renderConfigurationFlow = combine(
getVideoRenderer(),
getVideoFiltering(),
isThreadedRenderingEnabled(),
getVideoInternalResolutionScaling(),
) { renderer, filtering, threadedRenderingEnabled, resolutionScaling ->
RendererConfiguration(renderer, filtering, threadedRenderingEnabled, resolutionScaling)
}.conflate().shareIn(preferencesCoroutineScope, SharingStarted.Lazily, replay = 1)
}
private fun setDefaultThemeIfRequired() {
@ -115,10 +151,7 @@ class SharedPreferencesSettingsRepository(
getAudioLatency(),
getMicSource(),
getFirmwareConfiguration(),
RendererConfiguration(
getVideoFiltering().first(),
isThreadedRenderingEnabled()
)
renderConfigurationFlow.first(),
)
}
@ -232,6 +265,20 @@ class SharedPreferencesSettingsRepository(
return preferences.getBoolean("enable_jit", defaultJitEnabled)
}
override fun getVideoRenderer(): Flow<VideoRenderer> {
return getOrCreatePreferenceSharedFlow("video_renderer") {
val videoRendererPreference = preferences.getString("video_renderer", "software")!!
VideoRenderer.valueOf(videoRendererPreference.uppercase())
}
}
override fun getVideoInternalResolutionScaling(): Flow<Int> {
return getOrCreatePreferenceSharedFlow("video_internal_resolution") {
val internalResolutionPreference = preferences.getString("video_internal_resolution", "1")!!
internalResolutionPreference.toIntOrNull() ?: 1
}
}
override fun getVideoFiltering(): Flow<VideoFiltering> {
return getOrCreatePreferenceSharedFlow("video_filtering") {
val filteringPreference = preferences.getString("video_filtering", "linear")!!
@ -239,8 +286,10 @@ class SharedPreferencesSettingsRepository(
}
}
override fun isThreadedRenderingEnabled(): Boolean {
return preferences.getBoolean("enable_threaded_rendering", true)
override fun isThreadedRenderingEnabled(): Flow<Boolean> {
return getOrCreatePreferenceSharedFlow("enable_threaded_rendering") {
preferences.getBoolean("enable_threaded_rendering", true)
}
}
override fun getFpsCounterPosition(): FpsCounterPosition {
@ -518,4 +567,8 @@ class SharedPreferencesSettingsRepository(
preferenceSharedFlows[key]?.tryEmit(Unit)
}
override fun observeRenderConfiguration(): Flow<RendererConfiguration> {
return renderConfigurationFlow
}
}

View File

@ -89,12 +89,14 @@ class AndroidEmulatorManager(
override suspend fun updateRomEmulatorConfiguration(rom: Rom) {
val configuration = getRomEmulatorConfiguration(rom)
MelonEmulator.updateEmulatorConfiguration(configuration)
frameBufferProvider.setRendererConfiguration(configuration.rendererConfiguration)
MelonEmulator.updateEmulatorConfiguration(configuration, frameBufferProvider.frameBuffer())
}
override suspend fun updateFirmwareEmulatorConfiguration(consoleType: ConsoleType) {
val configuration = getFirmwareEmulatorConfiguration(consoleType)
MelonEmulator.updateEmulatorConfiguration(configuration)
frameBufferProvider.setRendererConfiguration(configuration.rendererConfiguration)
MelonEmulator.updateEmulatorConfiguration(configuration, frameBufferProvider.frameBuffer())
}
override suspend fun getRewindWindow(): RewindWindow {
@ -166,6 +168,8 @@ class AndroidEmulatorManager(
}
private fun setupEmulator(emulatorConfiguration: EmulatorConfiguration) {
frameBufferProvider.setRendererConfiguration(emulatorConfiguration.rendererConfiguration)
MelonEmulator.setupEmulator(
emulatorConfiguration,
context.assets,

View File

@ -9,6 +9,7 @@ import android.opengl.Matrix
import me.magnum.melonds.common.opengl.Shader
import me.magnum.melonds.common.opengl.ShaderFactory
import me.magnum.melonds.common.opengl.ShaderProgramSource
import me.magnum.melonds.common.runtime.FrameBufferProvider
import me.magnum.melonds.domain.model.BackgroundMode
import me.magnum.melonds.domain.model.Rect
import me.magnum.melonds.domain.model.RuntimeBackground
@ -23,7 +24,7 @@ import javax.microedition.khronos.opengles.GL10
import kotlin.math.roundToInt
class DSRenderer(
private val frameBuffer: ByteBuffer,
private val frameBufferProvider: FrameBufferProvider,
private val context: Context,
) : GLSurfaceView.Renderer {
companion object {
@ -70,6 +71,9 @@ class DSRenderer(
private var width = 0f
private var height = 0f
private var internalWidth = 0
private var internalHeight = 0
private var backgroundWidth = 0
private var backgroundHeight = 0
@ -136,6 +140,9 @@ class DSRenderer(
}
private fun applyRendererConfiguration() {
internalWidth = SCREEN_WIDTH * (rendererConfiguration?.resolutionScaling ?: 1)
internalHeight = SCREEN_HEIGHT * (rendererConfiguration?.resolutionScaling ?: 1)
updateScreenCoordinates()
updateShader()
}
@ -275,6 +282,12 @@ class DSRenderer(
mustUpdateConfiguration = false
}
if (!frameBufferProvider.isFrameBufferReady()) {
return
}
val frameBuffer = frameBufferProvider.frameBuffer()
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT)
synchronized(backgroundLock) {
@ -290,7 +303,7 @@ class DSRenderer(
it.use()
GLES20.glActiveTexture(GLES20.GL_TEXTURE0)
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mainTexture)
GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, GLES20.GL_RGBA, SCREEN_WIDTH, SCREEN_HEIGHT, 0, GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, frameBuffer)
GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, GLES20.GL_RGBA, internalWidth, internalHeight, 0, GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, frameBuffer)
GLES20.glUniformMatrix4fv(it.uniformMvp, 1, false, mvpMatrix, 0)
GLES20.glVertexAttribPointer(it.attribPos, 2, GLES20.GL_FLOAT, false, 0, posBuffer)
GLES20.glVertexAttribPointer(it.attribUv, 2, GLES20.GL_FLOAT, false, 0, uvBuffer)

View File

@ -234,7 +234,7 @@ class EmulatorActivity : AppCompatActivity() {
onBackPressedDispatcher.addCallback(backPressedCallback)
melonTouchHandler = MelonTouchHandler()
dsRenderer = DSRenderer(frameBufferProvider.frameBuffer(), this)
dsRenderer = DSRenderer(frameBufferProvider, this)
binding.surfaceMain.apply {
setEGLContextClientVersion(2)
preserveEGLContextOnPause = true

View File

@ -562,8 +562,8 @@ class EmulatorViewModel @Inject constructor(
private fun startObservingRendererConfiguration() {
sessionCoroutineScope.launch {
settingsRepository.getVideoFiltering().collectLatest {
_runtimeRendererConfiguration.value = RuntimeRendererConfiguration(it)
settingsRepository.observeRenderConfiguration().collectLatest {
_runtimeRendererConfiguration.value = RuntimeRendererConfiguration(it.videoFiltering, it.resolutionScaling)
}
}
}

View File

@ -4,4 +4,5 @@ import me.magnum.melonds.domain.model.VideoFiltering
data class RuntimeRendererConfiguration(
val videoFiltering: VideoFiltering,
val resolutionScaling: Int,
)

View File

@ -2,11 +2,13 @@ package me.magnum.melonds.ui.settings.fragments
import android.os.Bundle
import androidx.preference.ListPreference
import androidx.preference.Preference
import androidx.preference.PreferenceFragmentCompat
import dagger.hilt.android.AndroidEntryPoint
import me.magnum.melonds.R
import me.magnum.melonds.common.DirectoryAccessValidator
import me.magnum.melonds.common.UriPermissionManager
import me.magnum.melonds.domain.model.VideoRenderer
import me.magnum.melonds.domain.model.camera.DSiCameraSourceType
import me.magnum.melonds.ui.settings.PreferenceFragmentHelper
import me.magnum.melonds.ui.settings.PreferenceFragmentTitleProvider
@ -21,12 +23,29 @@ class VideoPreferencesFragment : PreferenceFragmentCompat(), PreferenceFragmentT
@Inject lateinit var uriPermissionManager: UriPermissionManager
@Inject lateinit var directoryAccessValidator: DirectoryAccessValidator
private val softwareRendererPreferences = mutableListOf<Preference>()
private val openGlRendererPreferences = mutableListOf<Preference>()
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
setPreferencesFromResource(R.xml.pref_video, rootKey)
softwareRendererPreferences.apply {
add(findPreference("video_filtering")!!)
add(findPreference("enable_threaded_rendering")!!)
}
openGlRendererPreferences.apply {
add(findPreference("video_internal_resolution")!!)
}
val rendererPreference = findPreference<ListPreference>("video_renderer")!!
val dsiCameraSourcePreference = findPreference<ListPreference>("dsi_camera_source")!!
val dsiCameraImagePreference = findPreference<StoragePickerPreference>("dsi_camera_static_image")!!
rendererPreference.setOnPreferenceChangeListener { _, newValue ->
onRendererPreferenceChanged(newValue as String)
true
}
dsiCameraSourcePreference.setOnPreferenceChangeListener { _, newValue ->
updateDsiCameraImagePreference(dsiCameraImagePreference, newValue as String)
true
@ -34,9 +53,32 @@ class VideoPreferencesFragment : PreferenceFragmentCompat(), PreferenceFragmentT
helper.setupStoragePickerPreference(dsiCameraImagePreference)
onRendererPreferenceChanged(rendererPreference.value)
updateDsiCameraImagePreference(dsiCameraImagePreference, dsiCameraSourcePreference.value)
}
private fun onRendererPreferenceChanged(rendererValue: String) {
val newRenderer = enumValueOfIgnoreCase<VideoRenderer>(rendererValue)
when (newRenderer) {
VideoRenderer.SOFTWARE -> {
softwareRendererPreferences.forEach {
it.isVisible = true
}
openGlRendererPreferences.forEach {
it.isVisible = false
}
}
VideoRenderer.OPENGL -> {
softwareRendererPreferences.forEach {
it.isVisible = false
}
openGlRendererPreferences.forEach {
it.isVisible = true
}
}
}
}
private fun updateDsiCameraImagePreference(preference: StoragePickerPreference, dsiCameraSourceValue: String) {
val newSource = enumValueOfIgnoreCase<DSiCameraSourceType>(dsiCameraSourceValue)
preference.isEnabled = newSource == DSiCameraSourceType.STATIC_IMAGE

View File

@ -29,6 +29,22 @@
<item>bottom_right</item>
</string-array>
<string-array name="video_renderer_values">
<item>software</item>
<item>opengl</item>
</string-array>
<string-array name="video_internal_resolution_values">
<item>1</item>
<item>2</item>
<item>3</item>
<item>4</item>
<item>5</item>
<item>6</item>
<item>7</item>
<item>8</item>
</string-array>
<string-array name="video_filtering_values">
<item>none</item>
<item>linear</item>

View File

@ -183,6 +183,8 @@
<string name="category_video">Video</string>
<string name="category_video_summary">Filter, threaded rendering, FPS counter</string>
<string name="filter">Filter</string>
<string name="internal_resolution">Internal resolution</string>
<string name="renderer">Renderer</string>
<string name="dsi_camera_source">DSi camera source</string>
<string name="dsi_camera_image">DSi camera image</string>
<string name="category_audio">Audio</string>
@ -397,6 +399,22 @@
<item>Bottom right</item>
</string-array>
<string-array name="video_renderer_options">
<item>Software</item>
<item>OpenGL</item>
</string-array>
<string-array name="video_internal_resolution_options">
<item>1x native (256x192)</item>
<item>2x native (512x384)</item>
<item>3x native (768x576)</item>
<item>4x native (1024x768)</item>
<item>5x native (1280x960)</item>
<item>6x native (1536x1152)</item>
<item>7x native (1792x1344)</item>
<item>8x native (2048x1536)</item>
</string-array>
<string-array name="video_filtering_options">
<item>None</item>
<item>Linear</item>

View File

@ -3,6 +3,24 @@
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<ListPreference
android:key="video_renderer"
android:title="@string/renderer"
android:summary="%s"
app:iconSpaceReserved="false"
android:entries="@array/video_renderer_options"
android:entryValues="@array/video_renderer_values"
android:defaultValue="software"/>
<ListPreference
android:key="video_internal_resolution"
android:title="@string/internal_resolution"
android:summary="%s"
app:iconSpaceReserved="false"
android:entries="@array/video_internal_resolution_options"
android:entryValues="@array/video_internal_resolution_values"
android:defaultValue="1"/>
<ListPreference
android:key="video_filtering"
android:title="@string/filter"

View File

@ -1,7 +1,7 @@
object AppConfig {
const val compileSdkVersion = 34
const val targetSdkVersion = compileSdkVersion
const val minSdkVersion = 21
const val minSdkVersion = 24
const val ndkVersion = "25.1.8937393"
const val versionCode = 30

@ -1 +1 @@
Subproject commit a26a30f19a1d0317af82ebf8074740a61cf3584b
Subproject commit f1080b5160142f4b2a3720c02cc14cd808cbc225