Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
21027fe
feat: add Bionic-FG frame generation as alternative to LSFG-VK
xXJSONDeruloXx May 13, 2026
655476a
fix: disable stale Bionic FG layer
xXJSONDeruloXx May 14, 2026
d01849d
fix: stabilize Bionic FG launch
xXJSONDeruloXx May 14, 2026
24845e6
Merge branch 'utkarshdalal:master' into feat/bionic-fg-integration
xXJSONDeruloXx May 15, 2026
ccbaf33
feat: update bionic-fg to model-1 real graph (v8)
xXJSONDeruloXx May 14, 2026
0fd2774
chore: update bionic-fg (skip model-0 alloc on model=1)
xXJSONDeruloXx May 14, 2026
f5bdcc5
chore: bionic-fg model-1 scratch fixes
xXJSONDeruloXx May 14, 2026
8e11ac0
chore: bionic-fg shader_44 position fix + updated synth bindings
xXJSONDeruloXx May 14, 2026
dce2af1
chore: bionic-fg shader_49 synthesis fix
xXJSONDeruloXx May 14, 2026
740584e
chore: bionic-fg shader_54/55 + complete 26-entry model-1 pass graph
xXJSONDeruloXx May 14, 2026
08f8684
chore: update bionic fg layer
xXJSONDeruloXx May 15, 2026
a4b05f2
fix: refresh bionic fg runtime installs
xXJSONDeruloXx May 15, 2026
f2a1a83
feat: add bionic fg quick menu hot reload
xXJSONDeruloXx May 15, 2026
bca8612
feat: add off option for bionic fg
xXJSONDeruloXx May 15, 2026
7e0b8f1
feat: enable bionic fg paced present
xXJSONDeruloXx May 15, 2026
242b1d9
chore: update bionic fg layer
xXJSONDeruloXx May 15, 2026
a86ce02
fix: remove bionic fg flow subtitle
xXJSONDeruloXx May 15, 2026
2fafae6
merge: bionic fg hot reload
xXJSONDeruloXx May 15, 2026
9a004a6
fix: show source fps in hud
xXJSONDeruloXx May 15, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"file_format_version": "1.0.0",
"layer": {
"name": "VK_LAYER_BIONIC_framegen",
"type": "GLOBAL",
"api_version": "1.3.0",
"library_path": "../../../lib/libbionic-fg-layer.so",
"implementation_version": "2",
"description": "Bionic FG standalone frame generation",
"functions": {
"vkGetInstanceProcAddr": "BionicFG_GetInstanceProcAddr",
"vkGetDeviceProcAddr": "BionicFG_GetDeviceProcAddr"
},
"enable_environment": {
"BIONIC_FG_ENABLE": "1"
},
"disable_environment": {
"DISABLE_BIONIC_FG": "1"
}
}
}
Binary file not shown.
165 changes: 154 additions & 11 deletions app/src/main/java/app/gamenative/ui/component/QuickMenu.kt
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@ private object QuickMenuTab {
const val EFFECTS = 2
const val CONTROLLER = 3
const val TOOLS = 4
const val BIONIC_FG = 5
}

data class QuickMenuItem(
Expand Down Expand Up @@ -254,6 +255,15 @@ fun QuickMenu(
isTouchscreenModeActive: Boolean = false,
onTouchGestureSettingsClick: () -> Unit = {},
activeToggleIds: Set<Int> = emptySet(),
frameGenerationMultiplier: Int = 0,
// Bionic-FG hot-reload state (tab only visible when isBionicFgAvailable)
isBionicFgAvailable: Boolean = false,
bionicFgMultiplier: Int = 2,
bionicFgFlowScale: Float = 0.80f,
bionicFgModel: String = "0",
onBionicFgMultiplierChanged: (Int) -> Unit = {},
onBionicFgFlowScaleChanged: (Float) -> Unit = {},
onBionicFgModelChanged: (String) -> Unit = {},
// LSFG hot-reload state (tab only visible when isLsfgAvailable)
isLsfgAvailable: Boolean = false,
lsfgMultiplier: Int = 2,
Expand Down Expand Up @@ -326,29 +336,35 @@ fun QuickMenu(

var selectedTab by rememberSaveable {
mutableIntStateOf(
if (PrefManager.quickMenuLastTab == QuickMenuTab.LSFG && !isLsfgAvailable)
QuickMenuTab.HUD
else PrefManager.quickMenuLastTab
when {
PrefManager.quickMenuLastTab == QuickMenuTab.BIONIC_FG && !isBionicFgAvailable -> QuickMenuTab.HUD
PrefManager.quickMenuLastTab == QuickMenuTab.LSFG && !isLsfgAvailable -> QuickMenuTab.HUD
else -> PrefManager.quickMenuLastTab
}
)
}
val selectedTabLabelResId = when (selectedTab) {
QuickMenuTab.HUD -> R.string.performance_hud
QuickMenuTab.BIONIC_FG -> R.string.bionic_fg_tab_title
QuickMenuTab.LSFG -> R.string.lsfg_tab_title
QuickMenuTab.EFFECTS -> R.string.screen_effects
QuickMenuTab.TOOLS -> R.string.task_manager
else -> R.string.quick_menu_tab_controller
}

val hudScrollState = rememberScrollState()
val bionicFgScrollState = rememberScrollState()
val effectsScrollState = rememberScrollState()
val lsfgScrollState = rememberScrollState()
val effectsTabFocusRequester = remember { FocusRequester() }
val controllerScrollState = rememberScrollState()
val bionicFgTabFocusRequester = remember { FocusRequester() }
val lsfgTabFocusRequester = remember { FocusRequester() }
val hudTabFocusRequester = remember { FocusRequester() }
val controllerTabFocusRequester = remember { FocusRequester() }
val toolsTabFocusRequester = remember { FocusRequester() }
val hudItemFocusRequester = remember { FocusRequester() }
val bionicFgItemFocusRequester = remember { FocusRequester() }
val effectsItemFocusRequester = remember { FocusRequester() }
val controllerItemFocusRequester = remember { FocusRequester() }
val toolsItemFocusRequester = remember { FocusRequester() }
Expand Down Expand Up @@ -462,6 +478,20 @@ fun QuickMenu(
modifier = Modifier.width(56.dp),
focusRequester = hudTabFocusRequester,
)
if (isBionicFgAvailable) {
QuickMenuTabButton(
icon = Icons.Default.Speed,
contentDescriptionResId = R.string.bionic_fg_tab_title,
selected = selectedTab == QuickMenuTab.BIONIC_FG,
accentColor = PluviaTheme.colors.accentPurple,
onSelected = {
selectedTab = QuickMenuTab.BIONIC_FG
PrefManager.quickMenuLastTab = selectedTab
},
modifier = Modifier.width(56.dp),
focusRequester = bionicFgTabFocusRequester,
)
}
if (isLsfgAvailable) {
QuickMenuTabButton(
icon = Icons.Default.Speed,
Expand Down Expand Up @@ -565,7 +595,7 @@ fun QuickMenu(
fpsLimiterEnabled = fpsLimiterEnabled,
fpsLimiterTarget = fpsLimiterTarget,
fpsLimiterMax = fpsLimiterMax,
lsfgMultiplier = if (isLsfgAvailable) lsfgMultiplier else 0,
frameGenerationMultiplier = frameGenerationMultiplier,
onTogglePerformanceHud = {
onItemSelected(QuickMenuAction.PERFORMANCE_HUD)
},
Expand All @@ -578,6 +608,20 @@ fun QuickMenu(
)
}

QuickMenuTab.BIONIC_FG -> {
BionicFgQuickMenuTab(
multiplier = bionicFgMultiplier,
flowScale = bionicFgFlowScale,
model = bionicFgModel,
onMultiplierChanged = onBionicFgMultiplierChanged,
onFlowScaleChanged = onBionicFgFlowScaleChanged,
onModelChanged = onBionicFgModelChanged,
scrollState = bionicFgScrollState,
focusRequester = bionicFgItemFocusRequester,
modifier = Modifier.fillMaxSize(),
)
}

QuickMenuTab.LSFG -> {
LsfgQuickMenuTab(
multiplier = lsfgMultiplier,
Expand Down Expand Up @@ -672,6 +716,7 @@ fun QuickMenu(
try {
when (selectedTab) {
QuickMenuTab.HUD -> hudItemFocusRequester.requestFocus()
QuickMenuTab.BIONIC_FG -> bionicFgItemFocusRequester.requestFocus()
QuickMenuTab.LSFG -> lsfgItemFocusRequester.requestFocus()
QuickMenuTab.EFFECTS -> effectsItemFocusRequester.requestFocus()
QuickMenuTab.TOOLS -> toolsItemFocusRequester.requestFocus()
Expand Down Expand Up @@ -741,7 +786,7 @@ private fun PerformanceHudQuickMenuTab(
fpsLimiterEnabled: Boolean,
fpsLimiterTarget: Int,
fpsLimiterMax: Int,
lsfgMultiplier: Int,
frameGenerationMultiplier: Int,
onTogglePerformanceHud: () -> Unit,
onPerformanceHudConfigChanged: (PerformanceHudConfig) -> Unit,
onFpsLimiterEnabledChanged: (Boolean) -> Unit,
Expand All @@ -759,22 +804,22 @@ private fun PerformanceHudQuickMenuTab(
verticalArrangement = Arrangement.spacedBy(4.dp),
) {
// ── FPS Limiter (topmost) ────────────────────────────────────────
val limiterControlledByLsfg = lsfgMultiplier >= 2
val limiterControlledByFrameGeneration = frameGenerationMultiplier >= 2
QuickMenuToggleRow(
title = stringResource(R.string.performance_hud_fps_limiter),
subtitle = if (limiterControlledByLsfg) {
stringResource(R.string.performance_hud_fps_limiter_lsfg_override)
subtitle = if (limiterControlledByFrameGeneration) {
stringResource(R.string.performance_hud_fps_limiter_frame_generation_override)
} else null,
enabled = fpsLimiterEnabled && !limiterControlledByLsfg,
enabled = fpsLimiterEnabled && !limiterControlledByFrameGeneration,
onToggle = {
if (!limiterControlledByLsfg) onFpsLimiterEnabledChanged(!fpsLimiterEnabled)
if (!limiterControlledByFrameGeneration) onFpsLimiterEnabledChanged(!fpsLimiterEnabled)
},
accentColor = accentColor,
focusRequester = focusRequester,
)

AnimatedVisibility(
visible = fpsLimiterEnabled && !limiterControlledByLsfg,
visible = fpsLimiterEnabled && !limiterControlledByFrameGeneration,
enter = expandVertically() + fadeIn(),
exit = shrinkVertically() + fadeOut(),
) {
Expand Down Expand Up @@ -1080,6 +1125,104 @@ private fun PerformanceHudQuickMenuTab(
}
}

@Composable
private fun BionicFgQuickMenuTab(
multiplier: Int,
flowScale: Float,
model: String,
onMultiplierChanged: (Int) -> Unit,
onFlowScaleChanged: (Float) -> Unit,
onModelChanged: (String) -> Unit,
scrollState: ScrollState,
focusRequester: FocusRequester? = null,
modifier: Modifier = Modifier,
) {
val accentColor = PluviaTheme.colors.accentPurple
val selectedModel = if (model == "1") "1" else "0"
val isEnabled = multiplier >= 2

Column(
modifier = modifier
.verticalScroll(scrollState)
.focusGroup(),
verticalArrangement = Arrangement.spacedBy(4.dp),
) {
QuickMenuSectionHeader(
title = stringResource(R.string.bionic_fg_multiplier),
)
Row(
modifier = Modifier.padding(horizontal = 8.dp),
horizontalArrangement = Arrangement.spacedBy(8.dp),
) {
listOf(0, 2, 3, 4).forEach { value ->
QuickMenuChoiceChip(
text = if (value == 0) "Off" else "${value}x",
selected = multiplier == value || (value == 0 && multiplier < 2),
accentColor = accentColor,
onClick = { onMultiplierChanged(value) },
modifier = Modifier.width(56.dp),
focusRequester = if (value == 0) focusRequester else null,
)
}
}

AnimatedVisibility(
visible = isEnabled,
enter = expandVertically() + fadeIn(),
exit = shrinkVertically() + fadeOut(),
) {
Column(verticalArrangement = Arrangement.spacedBy(4.dp)) {
Spacer(modifier = Modifier.height(4.dp))

QuickMenuAdjustmentRow(
title = stringResource(R.string.bionic_fg_flow_scale),
valueText = String.format(java.util.Locale.US, "%.2f", flowScale),
progress = (flowScale - 0.25f) / 0.75f,
onDecrease = {
val next = (flowScale - 0.05f).coerceIn(0.25f, 1.0f)
onFlowScaleChanged(String.format(java.util.Locale.US, "%.2f", next).toFloat())
},
onIncrease = {
val next = (flowScale + 0.05f).coerceIn(0.25f, 1.0f)
onFlowScaleChanged(String.format(java.util.Locale.US, "%.2f", next).toFloat())
},
accentColor = accentColor,
)

Spacer(modifier = Modifier.height(4.dp))

QuickMenuSectionHeader(
title = stringResource(R.string.bionic_fg_model),
subtitle = if (selectedModel == "1") {
stringResource(R.string.bionic_fg_model_1)
} else {
stringResource(R.string.bionic_fg_model_0)
},
)
Row(
modifier = Modifier.padding(horizontal = 8.dp),
horizontalArrangement = Arrangement.spacedBy(8.dp),
) {
QuickMenuChoiceChip(
text = "M0",
selected = selectedModel == "0",
accentColor = accentColor,
onClick = { onModelChanged("0") },
)
QuickMenuChoiceChip(
text = "M1",
selected = selectedModel == "1",
accentColor = accentColor,
onClick = { onModelChanged("1") },
)
}
}
}

Spacer(modifier = Modifier.height(12.dp))
}
}

@Composable
private fun LsfgQuickMenuTab(
multiplier: Int,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package app.gamenative.ui.component.dialog

import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Slider
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
Expand All @@ -18,6 +17,7 @@ import app.gamenative.ui.component.settings.SettingsListDropdown
import app.gamenative.ui.component.settings.SettingsMultiListDropdown
import app.gamenative.ui.theme.settingsTileColors
import app.gamenative.ui.theme.settingsTileColorsAlt
import app.gamenative.utils.BionicFgManager
import app.gamenative.utils.LsfgVkManager
import com.alorma.compose.settings.ui.SettingsGroup
import com.alorma.compose.settings.ui.SettingsSwitch
Expand Down Expand Up @@ -325,6 +325,8 @@ fun GraphicsTabContent(state: ContainerConfigState, default: Boolean = false) {
// with a Vortek/Adreno graphics driver.
if (!default) LsfgSection(state)

if (!default) BionicFgSection(state)

SettingsSwitch(
colors = settingsTileColorsAlt(),
title = { Text(text = stringResource(R.string.use_dri3)) },
Expand All @@ -337,6 +339,68 @@ fun GraphicsTabContent(state: ContainerConfigState, default: Boolean = false) {
}
}

@Composable
private fun BionicFgSection(state: ContainerConfigState) {
val config = state.config.value
val bionicFgSupported = config.containerVariant.equals(Container.BIONIC, ignoreCase = true)
if (!bionicFgSupported) return

SettingsGroup {
SettingsSwitch(
colors = settingsTileColorsAlt(),
title = { Text(text = stringResource(R.string.bionic_fg_enable)) },
subtitle = { Text(text = stringResource(R.string.bionic_fg_description)) },
state = config.bionicFgEnabled,
onCheckedChange = { enabled ->
state.config.value = config.copy(bionicFgEnabled = enabled)
},
)

if (config.bionicFgEnabled) {
Column(modifier = Modifier.padding(horizontal = 16.dp, vertical = 8.dp)) {
Text(text = stringResource(R.string.bionic_fg_multiplier))
Slider(
value = config.bionicFgMultiplier.coerceIn(2, 4).toFloat(),
onValueChange = { newValue ->
val clamped = newValue.roundToInt().coerceIn(2, 4)
state.config.value = config.copy(bionicFgMultiplier = clamped)
},
valueRange = 2f..4f,
steps = 1,
)
Text(text = if (config.bionicFgMultiplier < 2) "Off" else "${config.bionicFgMultiplier}x")
}

Column(modifier = Modifier.padding(horizontal = 16.dp, vertical = 8.dp)) {
Text(text = stringResource(R.string.bionic_fg_flow_scale))
Slider(
value = config.bionicFgFlowScale,
onValueChange = { newValue ->
val clamped = newValue.coerceIn(0.25f, 1.0f)
state.config.value = config.copy(bionicFgFlowScale = clamped)
},
valueRange = 0.25f..1.0f,
)
Text(text = String.format(java.util.Locale.US, "%.2f", config.bionicFgFlowScale))
}

val modelOptions = listOf(
stringResource(R.string.bionic_fg_model_0),
stringResource(R.string.bionic_fg_model_1)
)
SettingsListDropdown(
colors = settingsTileColors(),
title = { Text(text = stringResource(R.string.bionic_fg_model)) },
value = if (config.bionicFgModel == "1") 1 else 0,
items = modelOptions,
onItemSelected = { idx ->
state.config.value = config.copy(bionicFgModel = if (idx == 1) "1" else "0")
},
)
}
}
}

@Composable
private fun DxWrapperSection(state: ContainerConfigState) {
val config = state.config.value
Expand Down
Loading
Loading