Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -53,3 +53,6 @@ signing.properties
References/
*.hprof
android_sysvshm/build64/

# local build artifacts (FEX/Proton wcp)
dist/
96 changes: 96 additions & 0 deletions UNIXLIBS_README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
# FEX UnixLibs for WinNative — What It Is & How It Works

A short guide to the new **UnixLibs** feature: what changed, what gets installed,
and how the new `.so` files get loaded.

---

## The one-paragraph version

FEX (the x86→ARM translator used by ARM64EC containers) always ran as **Windows
DLLs living inside the container's C: drive**. Those DLLs are "boxed" inside
Wine's Windows world, so they can't talk to the Android kernel directly.
**UnixLibs** adds a small **native `.so`** that runs on the *Android side* and
gives FEX a direct line to the kernel — unlocking hardware-assisted x86 memory
ordering (TSO) and other speedups. Nothing about how you use containers changes;
it just runs faster where the hardware supports it.

---

## How it works (simple)

1. The FEX DLLs (`libwow64fex.dll` / `libarm64ecfex.dll`) stay in `system32` —
they're still the entry point Wine loads.
2. On launch, each FEX DLL asks Wine: *"load my native helper by name."*
3. Wine finds the matching **`.so`** (`libwow64fex.so` / `libarm64ecfex.so`) in
the running Wine/Proton's Unix library folder and loads it.
4. FEX now has a bridge from the boxed Windows side out to the Android kernel and
uses it for the fast paths (see performance note at the bottom).

The DLL and the `.so` are a **pair** — the DLL loads the `.so`; they work
*together*, the `.so` does not replace the DLL.

---

## What's different from the old setup

| Piece | Old | New (UnixLibs) |
|---|---|---|
| **Proton** | Wine couldn't load a `.so` helper by name, so FEX silently fell back to the slower in-DLL path. | Proton's `ntdll` now supports **load-unixlib-by-name**, so it can find and load the `.so`. |
| **FEX version** | The FEX `.wcp` shipped **only** the two PE `.dll` files. | The FEX `.wcp` ships the `.dll` files **plus** the matching native **`.so`** files (built for Android/Bionic). |
| **WinNative APK** | Only copied the FEX `.dll` into `system32`. | Adds a **"Use UnixLibs" toggle** and, when on, copies the `.so` into the active Proton's Unix lib folder so Wine can find it. |

---

## What gets installed now

Three matching pieces are needed (all ARM64EC only):

1. **New Proton** — e.g. `Proton-11-B5-Arm64EC-Steam-UnixLibs` (its `ntdll`
knows how to load the `.so`).
2. **New FEX component** — e.g. `FEX-2607-UnixLibs`, which contains:
- `libwow64fex.dll`, `libarm64ecfex.dll` → copied into `system32` (as before)
- `libwow64fex.so`, `libarm64ecfex.so` → the new native helpers
3. **New WinNative APK** — with the **Use UnixLibs** toggle and the wiring that
places the `.so` where Wine looks for it.

Install the Proton and FEX components from **Components** like normal, pick them
in the container/shortcut, and make sure **Use UnixLibs** is on (it turns on
automatically when the selected FEX version includes `.so` files).

---

## How the `.so` files get loaded

- When you launch a container, WinNative copies the FEX `.so` files into the
**active Proton's** `lib/wine/aarch64-unix/` folder (the exact place Wine
searches). This happens per-launch, so it follows whichever Proton you pick.
- Wine's `ntdll` then loads the `.so` by name when FEX asks for it.
- Turning the toggle **off** removes the `.so` again, so FEX goes back to the
plain DLL path.

**How to confirm it worked:** enable FEX/emulator logs, launch a game, and look
in the FEX log for:

```
FEX: Loaded FEXUnixLib
```

If you see that (once for each backend), the native helper is active. If it's
missing, FEX is running the old DLL-only path.

---

## What you might feel (performance)

FEX already translated x86 to native ARM either way — UnixLibs doesn't make
translation "native," it gives FEX **kernel access**. The main win is
**hardware TSO memory ordering**: FEX can drop the expensive software memory
barriers it normally needs for correct x86 behavior and let the CPU enforce it
instead — *fast and correct at the same time* (the same trick Apple's Rosetta
uses). Expect the biggest gains in memory- and multithread-heavy games, plus
smoother frame pacing.

Caveat: this is **hardware/kernel dependent**. On chips/kernels that expose the
memory-model toggle you'll feel it; on ones that don't, the `.so` still loads but
that speedup isn't available — no gain, but no regression either.
96 changes: 86 additions & 10 deletions app/src/main/feature/library/GameSettings.kt
Original file line number Diff line number Diff line change
Expand Up @@ -507,6 +507,7 @@ class GameSettingsStateHolder {
val selectedFexcoreVersion = mutableIntStateOf(0)
val fexcorePresetEntries = mutableStateOf<List<String>>(emptyList())
val selectedFexcorePreset = mutableIntStateOf(0)
val useUnixLibs = mutableStateOf(true)

// Advanced - System
val startupSelectionEntries = mutableStateOf<List<String>>(emptyList())
Expand Down Expand Up @@ -3554,6 +3555,14 @@ private fun AdvancedSection(

// FEXCore — hidden when FEXCore isn't explicitly in either slot.
if (state.showFexcoreFrame.value) {
val fexVersionUnix = state.fexcoreVersionEntries.value
.getOrNull(state.selectedFexcoreVersion.intValue)
?.contains("unix", ignoreCase = true) == true
val protonUnix = state.wineVersionDisplay.value.contains("unix", ignoreCase = true) ||
state.wineVersionEntries.value.getOrNull(state.selectedWineVersion.intValue)
?.contains("unix", ignoreCase = true) == true
val unixCompatible = fexVersionUnix && protonUnix

val fexcoreUsage = emulatorUsageLabel(state, setOf("fexcore"))
EmulatorSectionHeader(stringResource(R.string.container_fexcore_config), fexcoreUsage)
Spacer(Modifier.height(8.dp))
Expand All @@ -3564,7 +3573,14 @@ private fun AdvancedSection(
label = stringResource(R.string.container_fexcore_version),
entries = state.fexcoreVersionEntries.value,
selectedIndex = state.selectedFexcoreVersion.intValue,
onSelected = { state.selectedFexcoreVersion.intValue = it }
onSelected = { state.selectedFexcoreVersion.intValue = it },
labelTrailing = {
UnixLibsChip(
compatible = unixCompatible,
on = state.useUnixLibs.value,
onClick = { state.useUnixLibs.value = !state.useUnixLibs.value }
)
}
)
}
Box(Modifier.weight(1f)) {
Expand Down Expand Up @@ -3825,6 +3841,48 @@ private fun CpuChip(
}
}

@Composable
private fun UnixLibsChip(
compatible: Boolean,
on: Boolean,
onClick: () -> Unit
) {
val active = compatible && on
val bgColor = if (active) AccentBlue.copy(alpha = 0.15f) else ChipSurface
val borderColor = if (active) AccentBlue.copy(alpha = 0.4f) else ChipBorder
val textColor = if (active) AccentBlue else TextDim

Box(
modifier = Modifier
.clip(RoundedCornerShape(6.dp))
.background(bgColor)
.border(1.dp, borderColor, RoundedCornerShape(6.dp))
.then(
if (compatible) {
Modifier
.clickable(onClick = onClick)
.paneNavItem(
cornerRadius = 6.dp,
onActivate = onClick,
highlightColor = NavHighlight,
tapToSelect = true,
)
} else {
Modifier
}
)
.padding(horizontal = 10.dp, vertical = 2.dp),
contentAlignment = Alignment.Center
) {
Text(
"UnixLibs",
color = textColor,
fontSize = SettingLabelSize,
fontWeight = if (active) FontWeight.SemiBold else FontWeight.Normal
)
}
}


@Composable
private fun HtmlText(
Expand Down Expand Up @@ -3957,7 +4015,8 @@ private fun SettingDropdown(
selectedIndex: Int,
onSelected: (Int) -> Unit,
enabled: Boolean = true,
disabledAlpha: Float = 0.4f
disabledAlpha: Float = 0.4f,
labelTrailing: (@Composable () -> Unit)? = null
) {
var expanded by remember { mutableStateOf(false) }
val menuOffset = rememberSmartDropdownOffset()
Expand All @@ -3978,14 +4037,31 @@ private fun SettingDropdown(
}

Column(modifier = Modifier.fillMaxWidth().alpha(alpha)) {
Text(
label,
color = TextSecondary,
fontSize = SettingLabelSize,
fontWeight = FontWeight.Medium,
letterSpacing = 0.3.sp,
modifier = Modifier.padding(bottom = SettingTightGap)
)
if (labelTrailing != null) {
Row(
modifier = Modifier.fillMaxWidth().padding(bottom = SettingTightGap),
verticalAlignment = Alignment.CenterVertically
) {
Text(
label,
color = TextSecondary,
fontSize = SettingLabelSize,
fontWeight = FontWeight.Medium,
letterSpacing = 0.3.sp
)
Spacer(Modifier.weight(1f))
labelTrailing()
}
} else {
Text(
label,
color = TextSecondary,
fontSize = SettingLabelSize,
fontWeight = FontWeight.Medium,
letterSpacing = 0.3.sp,
modifier = Modifier.padding(bottom = SettingTightGap)
)
}
Box {
Row(
modifier = Modifier
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -422,6 +422,7 @@ class ContainerSettingsComposeDialog @JvmOverloads constructor(
}

state.fullscreenStretched.value = c?.isFullscreenStretched() ?: false
state.useUnixLibs.value = c?.isUseUnixLibs() ?: true

// Steam fields are shortcut-only in the UI; leave any existing steam
// state on the container untouched — saveSettings() skips them.
Expand Down Expand Up @@ -799,6 +800,7 @@ class ContainerSettingsComposeDialog @JvmOverloads constructor(
c.setWinComponents(wincomponents)
c.setDrives(drivesString)
c.setFullscreenStretched(state.fullscreenStretched.value)
c.setUseUnixLibs(state.useUnixLibs.value)
c.setInputType(finalInputType)
c.setExclusiveXInput(state.containerExclusiveInput.value)
c.setStartupSelection(startupSelection)
Expand Down Expand Up @@ -839,6 +841,7 @@ class ContainerSettingsComposeDialog @JvmOverloads constructor(
data.put("wincomponents", wincomponents)
data.put("drives", drivesString)
data.put("fullscreenStretched", state.fullscreenStretched.value)
data.put("useUnixLibs", state.useUnixLibs.value)
data.put("inputType", finalInputType)
data.put("exclusiveXInput", state.containerExclusiveInput.value)
data.put("startupSelection", startupSelection.toInt())
Expand Down
12 changes: 12 additions & 0 deletions app/src/main/feature/shortcuts/ShortcutSettingsComposeDialog.kt
Original file line number Diff line number Diff line change
Expand Up @@ -414,6 +414,10 @@ class ShortcutSettingsComposeDialog private constructor(
if (container.isFullscreenStretched) "1" else "0"
)
state.fullscreenStretched.value = fullscreenStretched == "1"
state.useUnixLibs.value = getShortcutSetting(
"useUnixLibs",
if (container.isUseUnixLibs) "1" else "0"
) == "1"

// LC_ALL
state.lcAll.value = getShortcutSetting("lc_all", container.getLC_ALL())
Expand Down Expand Up @@ -1071,6 +1075,13 @@ class ShortcutSettingsComposeDialog private constructor(
if (container.isFullscreenStretched) "1" else "0"
)

// Use UnixLibs
hasContainerOverride = hasContainerOverride or saveOverride(
"useUnixLibs",
if (state.useUnixLibs.value) "1" else "0",
if (container.isUseUnixLibs) "1" else "0"
)

// Win components
val wincomponents = buildWinComponentsString()
hasContainerOverride =
Expand Down Expand Up @@ -2160,6 +2171,7 @@ class ShortcutSettingsComposeDialog private constructor(

state.lcAll.value = container.getLC_ALL()
state.fullscreenStretched.value = container.isFullscreenStretched
state.useUnixLibs.value = container.isUseUnixLibs

val startupEntries = state.startupSelectionEntries.value
state.selectedStartupSelection.intValue = container.getStartupSelection().toInt()
Expand Down
1 change: 1 addition & 0 deletions app/src/main/res/values-da/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -505,6 +505,7 @@
<string name="container_fexcore_config">FEXCore</string>
<string name="container_fexcore_preset">FEXCore-forudindstilling</string>
<string name="container_fexcore_version">FEXCore-version</string>
<string name="use_unix_libs">Brug UnixLibs</string>

<!-- Container > Graphics Driver -->
<string name="container_graphics_driver">Grafikdriver</string>
Expand Down
1 change: 1 addition & 0 deletions app/src/main/res/values-de/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -505,6 +505,7 @@
<string name="container_fexcore_config">FEXCore</string>
<string name="container_fexcore_preset">FEXCore-Voreinstellung</string>
<string name="container_fexcore_version">FEXCore-Version</string>
<string name="use_unix_libs">UnixLibs verwenden</string>

<!-- Container > Graphics Driver -->
<string name="container_graphics_driver">Grafiktreiber</string>
Expand Down
1 change: 1 addition & 0 deletions app/src/main/res/values-es/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -505,6 +505,7 @@
<string name="container_fexcore_config">FEXCore</string>
<string name="container_fexcore_preset">Preset de FEXCore</string>
<string name="container_fexcore_version">Versión de FEXCore</string>
<string name="use_unix_libs">Usar UnixLibs</string>

<!-- Container > Graphics Driver -->
<string name="container_graphics_driver">Controlador gráfico</string>
Expand Down
1 change: 1 addition & 0 deletions app/src/main/res/values-fr/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -505,6 +505,7 @@
<string name="container_fexcore_config">FEXCore</string>
<string name="container_fexcore_preset">Préréglage FEXCore</string>
<string name="container_fexcore_version">Version de FEXCore</string>
<string name="use_unix_libs">Utiliser les UnixLibs</string>

<!-- Container > Graphics Driver -->
<string name="container_graphics_driver">Pilote graphique</string>
Expand Down
1 change: 1 addition & 0 deletions app/src/main/res/values-hi/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -559,6 +559,7 @@
<string name="container_fexcore_config">FEXCore</string>
<string name="container_fexcore_preset">FEXCore प्रीसेट</string>
<string name="container_fexcore_version">FEXCore संस्करण</string>
<string name="use_unix_libs">UnixLibs का उपयोग करें</string>
<string name="container_graphics_driver">ग्राफ़िक्स ड्राइवर</string>
<string name="container_graphics_configuration">ग्राफ़िक्स ड्राइवर कॉन्फ़िगरेशन</string>
<string name="container_graphics_version">ग्राफ़िक्स ड्राइवर संस्करण</string>
Expand Down
1 change: 1 addition & 0 deletions app/src/main/res/values-it/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -505,6 +505,7 @@
<string name="container_fexcore_config">FEXCore</string>
<string name="container_fexcore_preset">Preset FEXCore</string>
<string name="container_fexcore_version">Versione FEXCore</string>
<string name="use_unix_libs">Usa UnixLibs</string>

<!-- Container > Graphics Driver -->
<string name="container_graphics_driver">Driver grafico</string>
Expand Down
1 change: 1 addition & 0 deletions app/src/main/res/values-ko/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -505,6 +505,7 @@
<string name="container_fexcore_config">FEXCore</string>
<string name="container_fexcore_preset">FEXCore 프리셋</string>
<string name="container_fexcore_version">FEXCore 버전</string>
<string name="use_unix_libs">UnixLibs 사용</string>

<!-- Container > Graphics Driver -->
<string name="container_graphics_driver">그래픽 드라이버</string>
Expand Down
1 change: 1 addition & 0 deletions app/src/main/res/values-pl/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -509,6 +509,7 @@
<string name="container_fexcore_config">FEXCore</string>
<string name="container_fexcore_preset">Preset FEXCore</string>
<string name="container_fexcore_version">Wersja FEXCore</string>
<string name="use_unix_libs">Użyj UnixLibs</string>

<!-- Container > Graphics Driver -->
<string name="container_graphics_driver">Sterownik grafiki</string>
Expand Down
1 change: 1 addition & 0 deletions app/src/main/res/values-pt-rBR/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -505,6 +505,7 @@
<string name="container_fexcore_config">FEXCore</string>
<string name="container_fexcore_preset">Predefinicao FEXCore</string>
<string name="container_fexcore_version">Versao do FEXCore</string>
<string name="use_unix_libs">Usar UnixLibs</string>

<!-- Container > Graphics Driver -->
<string name="container_graphics_driver">Driver Grafico</string>
Expand Down
1 change: 1 addition & 0 deletions app/src/main/res/values-ro/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -505,6 +505,7 @@
<string name="container_fexcore_config">FEXCore</string>
<string name="container_fexcore_preset">Preset FEXCore</string>
<string name="container_fexcore_version">Versiune FEXCore</string>
<string name="use_unix_libs">Folosește UnixLibs</string>

<!-- Container > Graphics Driver -->
<string name="container_graphics_driver">Driver grafic</string>
Expand Down
1 change: 1 addition & 0 deletions app/src/main/res/values-ru/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -513,6 +513,7 @@
<string name="container_fexcore_config">FEXCore</string>
<string name="container_fexcore_preset">Пресет FEXCore</string>
<string name="container_fexcore_version">Версия FEXCore</string>
<string name="use_unix_libs">Использовать UnixLibs</string>
<!-- Container > Graphics Driver -->
<string name="container_graphics_driver">Графический драйвер</string>
<string name="container_graphics_configuration">Конфигурация графического драйвера</string>
Expand Down
1 change: 1 addition & 0 deletions app/src/main/res/values-uk/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -509,6 +509,7 @@
<string name="container_fexcore_config">FEXCore</string>
<string name="container_fexcore_preset">Пресет FEXCore</string>
<string name="container_fexcore_version">Версія FEXCore</string>
<string name="use_unix_libs">Використовувати UnixLibs</string>

<!-- Container > Graphics Driver -->
<string name="container_graphics_driver">Графічний драйвер</string>
Expand Down
Loading