v1.1:
- better mimalloc support so that we're using 2M pages instead of 1G, which typically aren't configured on systems. ignore libhugetlbfs unless mimalloc is absent, it only degrades performance for mimalloc.
- fixes for KDocker icon detection (Xorg only, not Wayland compatible)
- use hugeadm to manage hugepage pool dynamically. this is a new dependency, make sure you've got it installed so you don't have to statically allocate your pages at boot (no bootloader config changes required)
- use hugeadm to restore the hugepages
- $HOME/factorio.env allows persistently overriding a few variables, like DRI_PRIME and __GL_YIELD="USELEEP" if you need those for your specific system
- one of those is the HUGEADM_PAGESZ variable, which can be used to specify how much RAM you would like Factorio to have access to. default is 8192, which would reserve 16GiB of 2MiB pages.
- wmctrl invocation fixes so that the window can be renamed correctly (Xorg only, not Wayland compatible)
v1.0:
- mimalloc, libhugetlbfs support for better mem allocators
- set CPU to performance mode and then restore to previous mode after Factorio exits. this doesn't happen on battery, if you're in a laptop. (requires upower to detect laptops)
- if not using Wayland, KDocker may be used to plop Factorio into your system tray (custom icon paths supported)
- if running Wayland, SDL2 override
- allows running multiple copies of Factorio from the one script, folder name provided as the first argument
- run Factorio reniced to -19 so it gets more CPU time
- disable Optimus for laptops with NVIDIA support, unless you've explicitly enabled Optimus. it will leave it alone.
- renames the Factorio window to exclude the version string, adding the folder name for the installation as a suffix instead. this makes it easier to use in OBS Studio.
[Linux] Yet another Factorio Launcher (Wayland, mimalloc)
[Linux] Yet another Factorio Launcher (Wayland, mimalloc)
This is a script I've been using for some time, and completely overhauled it for public consumption today.
Last edited by ptx0 on Sat Jun 18, 2022 9:54 pm, edited 3 times in total.
Re: Yet another Factorio Launcher (Wayland, mimalloc)
Code: Select all
#!/bin/bash
#### CONFIGURATION
if [ -f "${HOME}/.factorio.env" ]; then
# Place environment variables here and we'll grab them on each startup.
# This is so you can override the config values without modifying this script.
source "${HOME}/.factorio.env"
else
### Change this to wherever your factorio installations are stored.
### Multiple copies of Factorio may be installed alongside each other.
### Value: /home/$USER/factorio
### Example module path: /home/$USER/factorio/vanilla
export FACTORIO_PATH="${HOME}/factorio"
### 16GiB of Hugepages might be too much for your system.
export HUGEADM_PAGESZ
HUGEADM_PAGESZ=8192
export HUGEADM_POOLSIZE
HUGEADM_POOLSIZE=2097152
fi
#### FUNCTIONS
disable_optimus() {
if [ -z "$__GLX_VENDOR_LIBRARY_NAME" ] && [ -z "$__NV_PRIME_RENDER_OFFLOAD" ]; then
echo "Overriding NVIDIA Optimus flags with:"
export DRI_PRIME=0
export __NV_PRIME_RENDER_OFFLOAD=0
export __GLX_VENDOR_LIBRARY_NAME=NOT_nvidia
else
echo "Not disabling pre-existing NVIDIA Optimus flags:"
fi
# Use AMD or Intel iGPU.
echo " DRI_PRIME: ${DRI_PRIME}"
echo " __NV_PRIME_RENDER_OFFLOAD: ${__NV_PRIME_RENDER_OFFLOAD}"
echo " __GLX_VENDOR_LIBRARY_NAME: ${__GLX_VENDOR_LIBRARY_NAME}"
}
enable_mimalloc() {
! [ -z "${MIMALLOC_DISABLE}" ] && echo "mimalloc disabled." && return
LIBMIMALLOC_PATH='/usr/lib64/libmimalloc.so'
if ! [ -f "${LIBMIMALLOC_PATH}" ]; then
echo "mimalloc doesn't exist. You might really want to install this."
else
echo "Enabled mimalloc."
export MIMALLOC_LARGE_OS_PAGES=1 # Use 2MiB pages
export MIMALLOC_RESERVE_HUGE_OS_PAGES=0 # Use n 1GiB pages
export MALLOC_ARENA_MAX=1 # Tell Glibc to only allocate memory in a single "arena".
export MIMALLOC_PAGE_RESET=0 # Signal when pages are empty
export MIMALLOC_EAGER_COMMIT_DELAY=4 # The first 4MiB of allocated memory won't be hugepages
export MIMALLOC_SHOW_STATS=0 # Display mimalloc stats
export LD_PRELOAD="${LD_PRELOAD} ${LIBMIMALLOC_PATH}"
return
fi
LIBHUGETLBFS_PATH="/usr/lib64/libhugetlbfs.so"
if [ -f "${LIBHUGETLBFS_PATH}" ]; then
export LD_PRELOAD="${LD_PRELOAD} ${LIBHUGETLBFS_PATH}"
export HUGETLB_MORECORE=thp
export HUGETLB_RESTRICT_EXE=factorio
echo "Enabled libhugetlbfs parameters for easy huge page support."
else
echo "You do not even have libhugetlbfs installed. There is very little we can do for your performance here."
fi
}
configure_mempool() {
export HUGEADM_PATH
export HUGEADM_CURRENTSIZE
# Current pool size (allocated hugepages)
HUGEADM_CURRENTSIZE=$(hugeadm --pool-list | grep "${HUGEADM_POOLSIZE}" | awk '{ print $3; }')
# Maximum pool size (how many hugepages)
HUGEADM_MAXIMUMSIZE=$(hugeadm --pool-list | grep "${HUGEADM_POOLSIZE}" | awk '{ print $4; }')
HUGEADM_PATH=$(which hugeadm)
if [ -z "${HUGEADM_PATH}" ]; then
echo 'hugeadm is not installed. Was unable to configure the system hugepages pool size.'
fi
export HUGEADM_FREE
export TARGET_HUGEPAGESZ=0 # By default, we'll assume we need to allocate zero pages.
HUGEADM_FREE=$(expr "${HUGEADM_MAXIMUMSIZE}" - "${HUGEADM_CURRENTSIZE}")
if [ "${HUGEADM_FREE}" -lt "${HUGEADM_PAGESZ}" ]; then
# We don't have enough free hugepages. Let's go for gold and increase it by the current desired amount.
TARGET_HUGEPAGESZ=$(expr "${HUGEADM_PAGESZ}" - "${HUGEADM_FREE}")
sudo "${HUGEADM_PATH}" --hard --pool-pages-max "2MB:${TARGET_HUGEPAGESZ}" || echo "Could not configure hugepages pool size via hugeadm."
echo "Added ${TARGET_HUGEPAGESZ} to system hugepages memory pool."
else
echo "We have enough free pages (${HUGEADM_FREE} / ${HUGEADM_MAXIMUMSIZE}). Continuing."
fi
}
restore_mempool() {
if [ "${TARGET_HUGEPAGESZ}" -gt 0 ]; then
echo "Being a good citizen and restoring memory pool size back to ${HUGEADM_MAXIMUMSIZE}."
sudo "${HUGEADM_PATH}" --hard --pool-pages-max "2MB:${HUGEADM_MAXIMUMSIZE}" || echo "Could not configure hugepages pool size via hugeadm."
else
TOTAL_MEM_WASTED=$(expr "${HUGEADM_MAXIMUMSIZE}" \* 2)
echo "There were no extra hugepages allocated at startup, so there is nothing to clean up now. You could free ${TOTAL_MEM_WASTED}M for other applications by reducing the maximum pool size to zero by default."
fi
}
cpu_performance_mode() {
# Default is to assume we're on a desktop.
export CURRENT_GOVERNOR
CURRENT_GOVERNOR=$(cat /sys/devices/system/cpu/cpufreq/policy*/scaling_governor | head -n1)
export IS_LAPTOP=0
# Detect if we're on a laptop, and if so, if we're charged/charging.
upower -i /org/freedesktop/UPower/devices/battery_BAT0 | grep -o 'power supply.*yes' > /dev/null 2>&1 && export IS_LAPTOP=1 && export POWER_STATE=$(upower -i /org/freedesktop/UPower/devices/battery_BAT0 | grep -E -o '(fully-charged|charging)')
# If we're not on a laptop, or we are fully charged/charging, go into "performance" CPU governor
if [ "$IS_LAPTOP" -eq 0 ] || ! [ -z "$POWER_STATE" ]; then
echo "Enabling CPU governor: performance"
# This requires sudo access. It's not important to have this work, but it might be nice on low power systems or huge save files.
echo performance | sudo tee /sys/devices/system/cpu/cpufreq/policy*/scaling_governor > /dev/null
else
echo "Not changing CPU governor. We are not plugged in to a power source."
fi
}
cpu_governor_reset() {
echo "Enabling CPU governor: ${CURRENT_GOVERNOR}"
echo "${CURRENT_GOVERNOR}" | sudo tee /sys/devices/system/cpu/cpufreq/policy*/scaling_governor > /dev/null
}
custom_icon() {
export KDOCKER_PREFIX='' # Do not use kdocker unless it exists.
export KDOCKER_PATH=$(which kdocker)
if [ -z "${KDOCKER_PATH}" ]; then
echo "Not running KDocker, it is not installed."
return
fi
if ! [ -z "${WAYLAND_DISPLAY}" ]; then
echo "Not running KDocker on Wayland."
return
fi
echo "Detect custom tray icon..."
export ICON_PATH="${FACTORIO_PATH}/'${MODULE}'/data/core/graphics/factorio-icon.png" # Use the default Factorio icon as provided by Wube
ls "${HOME}/.icons/factorio.png" > /dev/null 2>&1 && export ICON_PATH="${HOME}/.icons/factorio.png" # Use an override icon, if user wants a special one.
ls "${MODULE_DIR}/icon.png" > /dev/null 2>&1 && export ICON_PATH="${MODULE_DIR}/icon.png" # Use a module-specific override icon, so that each install can have their own icon.
export KDOCKER_SECONDS=60 # Wait 60 seconds until Factorio is most likely ready.
export KDOCKER_PREFIX="${KDOCKER_PATH} -i ${ICON_PATH} -x "${FACTORIO_PID}" -d ${KDOCKER_SECONDS}"
echo "Using KDocker command: ${KDOCKER_PREFIX}"
${KDOCKER_PREFIX} && export KDOCKER_PID=$!
}
kill_existing_factorio() {
export FACTORIO_PID
FACTORIO_PID=$(fuser "${MODULE_DIR}/.lock" 2>/dev/null | grep -E -o '[0-9]+' | head -n1)
if ! [ -z "$FACTORIO_PID" ]; then
kill -9 "${FACTORIO_PID}"
fi
}
wait_for_factorio() {
COUNT=0
while [ "$COUNT" -lt 10 ]
do
COUNT=$(expr $COUNT + 1)
export FACTORIO_PID
FACTORIO_PID=$(fuser "${MODULE_DIR}/.lock" 2>/dev/null | grep -E -o '[0-9]+' | head -n1)
[ -n "${FACTORIO_PID}" ] && break
sleep .25
done
if [ "$COUNT" -eq 10 ]; then
echo "Giving up. Couldn't start Factorio client."
exit 1
fi
}
renice_factorio() {
echo "Looking for pid and renicing to realtime status..."
sudo renice -n -19 "${FACTORIO_PID}" > /dev/null 2>&1
}
rename_window() {
WMCTRL_PATH=$(which wmctrl)
if [ -z "${WMCTRL_PATH}" ]; then
echo "Changing Factorio window title for consistency, so that OBS can find it easily..."
WINDOW_ID=$(wmctrl -l -p | awk -v vpid="$FACTORIO_PID" '$3 == vpid{ print $1 }')
if ! [ -z "${WINDOW_ID}" ]; then
echo "No window ID found."
return
fi
FACTORIO_TITLE="Factorio ${MODULE}"
wmctrl -i -r "${WINDOW_ID}" -I "Factorio" -N "${FACTORIO_TITLE}"
echo "Window renamed to ${FACTORIO_TITLE}."
fi
}
factorio_ping() {
echo "Watching for Factorio process to end."
while true; do
sleep 30
if ! [ -d "/proc/${FACTORIO_PID}" ]; then
echo "It looks like Factorio has exited. Ending script execution..."
break
fi
done
}
kill_kdocker() {
if ! [ -z "${KDOCKER_PREFIX}" ] && ! [ -z "${KDOCKER_PID}" ]; then
echo "Killing KDocker..."
kill -9 ${KDOCKER_PID}
fi
}
wayland_sdl_override(){
if ! [ -z "${WAYLAND_DISPLAY}" ]; then
export QT_QPA_PLATFORM=wayland
export SDL_DYNAMIC_API=/usr/lib64/libSDL2.so
if ! [ -f "${SDL_DYNAMIC_API}" ]; then
echo "SDL 2.0 library not found?"
else
echo "SDL2 override for Wayland enabled."
fi
export SDL_VIDEODRIVER=wayland
else
echo "Not running Wayland, no SDL2 override needed."
fi
}
#### SCRIPT BELOW
### Using Wayland display server? We'll overload SDL2 with the OS-provided copy and force-enable Wayland output.
wayland_sdl_override
### This is laptop-specific optimization for Factorio.
### If it's not provided by the OS, we assume you do not want to use the discrete GPU for rendering Factorio.
### This wakes the GPU up (obviously) and ensures the laptop uses more power than it needs to. If you don't
### want this behaviour, export these variables from your shell profile, or change these values to 1 & nvidia
disable_optimus
### Are we plugged in, or on a desktop?
### Switch CPU governor to performance mode.
cpu_performance_mode
### Support multiple copies of Factorio.
### To have spaces in the name, just 'Quote It' when running the script.
### Example: ./factorio.sh "Space Exploration"
MODULE="vanilla"
if ! [ -z "$1" ]; then
MODULE="$1"
fi
MODULE_DIR="${FACTORIO_PATH}/${MODULE}"
pushd "${MODULE_DIR}" > /dev/null || (echo "Can not enter installation directory. Ensure it exists: ${MODULE_DIR}" && exit 1)
### Detect an existing Factorio instance, and kill it.
kill_existing_factorio
### Microsoft mimalloc is useful to boost FPS in megabases as a more efficient memory allocator.
configure_mempool
enable_mimalloc
### Try and execute Factorio.
./bin/x64/factorio > "${HOME}/.factorio.log" 2>&1 &
#### Wait for Factorio and grab the PID. Exit if we don't find it in 10 attempts.
wait_for_factorio
### We do not want to use KDocker on Wayland. It is not supported yet.
custom_icon
### Now that it's running, renice it so we can grab more CPU.
renice_factorio
### Wait until Factorio exits.
factorio_ping
# Unconfigure hugepages if we've altered the system environment.
restore_mempool
### Go back to the previous CPU governor.
cpu_governor_reset
### End the KDocker process.
kill_kdocker
exit 0
Re: [Linux] Yet another Factorio Launcher (Wayland, mimalloc)
updated the post with v1.1 of the launcher, it properly detects icon paths and more.v1.1:
- better mimalloc support so that we're using 2M pages instead of 1G, which typically aren't configured on systems. ignore libhugetlbfs unless mimalloc is absent, it only degrades performance for mimalloc.
- fixes for KDocker icon detection (Xorg only, not Wayland compatible)
- use hugeadm to manage hugepage pool dynamically. this is a new dependency, make sure you've got it installed so you don't have to statically allocate your pages at boot (no bootloader config changes required)
- use hugeadm to restore the hugepages
- $HOME/factorio.env allows persistently overriding a few variables, like DRI_PRIME and __GL_YIELD="USELEEP" if you need those for your specific system
- one of those is the HUGEADM_PAGESZ variable, which can be used to specify how much RAM you would like Factorio to have access to. default is 8192, which would reserve 16GiB of 2MiB pages.
- wmctrl invocation fixes so that the window can be renamed correctly (Xorg only, not Wayland compatible)