Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add patch-vk for vulkan limit bypass support #872

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
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
419 changes: 220 additions & 199 deletions README.md

Large diffs are not rendered by default.

275 changes: 236 additions & 39 deletions drivers.json

Large diffs are not rendered by default.

254 changes: 254 additions & 0 deletions patch-vk.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,254 @@
#!/bin/bash
# halt on any error for safety and proper pipe handling
set -euo pipefail ; # <- this semicolon and comment make options apply
# even when script is corrupt by CRLF line terminators (issue #75)
# empty line must follow this comment for immediate fail with CRLF newlines

# root check
if [ "$(id -u)" -ne 0 ]; then
echo
echo -e "Please run as root!"
echo
exit 1
fi


backup_path="/opt/nvidia/libnvidia-eglcore-backup"
silent_flag=''
manual_driver_version=''
flatpak_flag=''
backup_suffix=''

print_usage() { printf '
SYNOPSIS
patch-vk.sh [-s] [-r|-h|-c VERSION|-l|-f]

DESCRIPTION
The patch for Nvidia vulkan drivers to remove NVENC session limit

-s Silent mode (No output)
-r Rollback to original (Restore lib from backup)
-h Print this help message
-c VERSION Check if version VERSION supported by this patch.
Returns true exit code (0) if version is supported.
-l List supported driver versions
-d VERSION Use VERSION driver version when looking for libraries
instead of using nvidia-smi to detect it.
-j Output the patch list to stdout as JSON
'
}

# shellcheck disable=SC2209
opmode="patch"

while getopts 'rshjc:ld:' flag; do
case "${flag}" in
r) opmode="${opmode}rollback" ;;
s) silent_flag='true' ;;
h) opmode="${opmode}help" ;;
c) opmode="${opmode}checkversion" ; checked_version="$OPTARG" ;;
l) opmode="${opmode}listversions" ;;
d) manual_driver_version="$OPTARG" ;;
j) opmode="dump" ;;
*) echo "Incorrect option specified in command line" ; exit 2 ;;
esac
done

if [[ $silent_flag ]]; then
exec 1> /dev/null
fi

if [[ $flatpak_flag ]]; then
backup_suffix='.flatpak'
echo "WARNING: Flatpak flag enabled (-f), modifying ONLY the Flatpak driver."
fi

declare -A patch_list=(
["555.42.02"]='s/\x53\x89\xf3\x41\xb9\x04\x00\x00\x00\x48\x83\xec\x10/\x48\xc7\xc0\x01\x00\x00\x00\xc3\xc3\xc3\xc3\xc3\xc3/g'
["555.58"]='s/\x53\x89\xf3\x41\xb9\x04\x00\x00\x00\x48\x83\xec\x10/\x48\xc7\xc0\x01\x00\x00\x00\xc3\xc3\xc3\xc3\xc3\xc3/g'
["555.58.02"]='s/\x53\x89\xf3\x41\xb9\x04\x00\x00\x00\x48\x83\xec\x10/\x48\xc7\xc0\x01\x00\x00\x00\xc3\xc3\xc3\xc3\xc3\xc3/g'
["560.35.03"]='s/\x53\x89\xf3\x41\xb9\x04\x00\x00\x00\x48\x83\xec\x10/\x48\xc7\xc0\x01\x00\x00\x00\xc3\xc3\xc3\xc3\xc3\xc3/g'
["560.28.03"]='s/\x53\x89\xf3\x41\xb9\x04\x00\x00\x00\x48\x83\xec\x10/\x48\xc7\xc0\x01\x00\x00\x00\xc3\xc3\xc3\xc3\xc3\xc3/g'
["565.57.01"]='s/\x53\x89\xf3\x41\xb9\x04\x00\x00\x00\x48\x83\xec\x10/\x48\xc7\xc0\x01\x00\x00\x00\xc3\xc3\xc3\xc3\xc3\xc3/g'
)

check_version_supported () {
local ver="$1"
[[ "${patch_list[$ver]+isset}" ]]
}

# get_flatpak_driver_path () {
# # Flatpak's package versioning replaces '.' by '-'
# version="$(echo "$1" | tr '.' '-')"
# # Attempts to patch system flatpak
# if path=$(flatpak info --show-location "org.freedesktop.Platform.GL.nvidia-${version}" 2>/dev/null); then
# echo "$path/files/lib"
# # If it isn't found will login as the user that envoked sudo & patch this version
# elif path=$(su -c - ${SUDO_USER} 'flatpak info --show-location "org.freedesktop.Platform.GL.nvidia-'${version}'"'); then
# echo "$path/files/lib"
# fi
# }

get_supported_versions () {
for drv in "${!patch_list[@]}"; do
echo "$drv"
done | sort -t. -n
return 0
}

patch_common () {
NVIDIA_SMI="$(command -v nvidia-smi || true)"
if [[ ! "$NVIDIA_SMI" ]] ; then
echo 'nvidia-smi utility not found. Probably driver is not installed.'
exit 1
fi

if [[ "$manual_driver_version" ]]; then
driver_version="$manual_driver_version"

echo "Using manually entered nvidia driver version: $driver_version"
else
cmd="$NVIDIA_SMI --query-gpu=driver_version --format=csv,noheader,nounits"
driver_versions_list=$($cmd) || (
ret_code=$?
echo "Can not detect nvidia driver version."
echo "CMD: \"$cmd\""
echo "Result: \"$driver_versions_list\""
echo "nvidia-smi retcode: $ret_code"
exit 1
)
driver_version=$(echo "$driver_versions_list" | head -n 1)

echo "Detected nvidia driver version: $driver_version"
fi

if ! check_version_supported "$driver_version" ; then
echo "Patch for this ($driver_version) nvidia driver not found."
echo "Patch is available for versions: "
get_supported_versions
exit 1
fi

patch="${patch_list[$driver_version]}"
driver_maj_version=${driver_version%%.*}
object='libnvidia-eglcore.so'
echo $object

# if [[ $flatpak_flag ]]; then
# driver_dir=$(get_flatpak_driver_path "$driver_version")
# if [ -z "$driver_dir" ]; then
# echo "ERROR: Flatpak package for driver $driver_version does not appear to be installed."
# echo "Try rebooting your computer and/or running 'flatpak update'."
# exit 1
# fi
# # return early because the code below is out of scope for the Flatpak driver
# return 0
# fi

declare -a driver_locations=(
'/usr/lib/x86_64-linux-gnu'
'/usr/lib/x86_64-linux-gnu/nvidia/current/'
'/usr/lib/x86_64-linux-gnu/nvidia/tesla/'
"/usr/lib/x86_64-linux-gnu/nvidia/tesla-${driver_version%%.*}/"
'/usr/lib64'
'/usr/lib'
"/usr/lib/nvidia-${driver_version%%.*}"
)

dir_found=''
for driver_dir in "${driver_locations[@]}" ; do
if [[ -e "$driver_dir/$object.$driver_version" ]]; then
dir_found='true'
break
fi
done

[[ "$dir_found" ]] || { echo "ERROR: cannot detect driver directory"; exit 1; }

}

ensure_bytes_are_valid () {
driver_file="$driver_dir/$object.$driver_version"
original_bytes=$(awk -F / '$2 { print $2 }' <<< "$patch")
patched_bytes=$(awk -F / '$3 { print $3 }' <<< "$patch")
if LC_ALL=C grep -qaP "$original_bytes" "$driver_file"; then
printf "Bytes to patch: %s\n" "$original_bytes"
return 0 # file is ready to be patched
fi
if LC_ALL=C grep -qaP "$patched_bytes" "$driver_file"; then
echo "Warn: Bytes '$patched_bytes' already present in '$driver_file'."
return 0 # file is likely patched already
fi
echo "Error: Could not find bytes '$original_bytes' to patch in '$driver_file'."
exit 1
}

rollback () {
patch_common
if [[ -f "$backup_path/$object.$driver_version$backup_suffix" ]]; then
cp -p "$backup_path/$object.$driver_version$backup_suffix" \
"$driver_dir/$object.$driver_version"
echo "Restore from backup $object.$driver_version$backup_suffix"
else
echo "Backup not found. Try to patch first."
exit 1
fi
}

patch () {
patch_common
ensure_bytes_are_valid
if [[ -f "$backup_path/$object.$driver_version$backup_suffix" ]]; then
bkp_hash="$(sha1sum "$backup_path/$object.$driver_version$backup_suffix" | cut -f1 -d\ )"
drv_hash="$(sha1sum "$driver_dir/$object.$driver_version" | cut -f1 -d\ )"
if [[ "$bkp_hash" != "$drv_hash" ]] ; then
echo "Backup exists and driver file differ from backup. Skipping patch."
return 0
fi
else
echo "Attention! Backup not found. Copying current $object to backup."
mkdir -p "$backup_path"
cp -p "$driver_dir/$object.$driver_version" \
"$backup_path/$object.$driver_version$backup_suffix"
fi
sha1sum "$backup_path/$object.$driver_version$backup_suffix"
sed "$patch" "$backup_path/$object.$driver_version$backup_suffix" > \
"${PATCH_OUTPUT_DIR-$driver_dir}/$object.$driver_version"
sha1sum "${PATCH_OUTPUT_DIR-$driver_dir}/$object.$driver_version"
ldconfig
echo "Patched!"
}

query_version_support () {
if check_version_supported "$checked_version" ; then
echo "SUPPORTED"
exit 0
else
echo "NOT SUPPORTED"
exit 1
fi
}

list_supported_versions () {
get_supported_versions
}

dump_patches () {
for i in "${!patch_list[@]}"
do
echo "$i"
echo "${patch_list[$i]}"
done |
jq --sort-keys -n -R 'reduce inputs as $i ({}; . + { ($i): (input|(tonumber? // .)) })'
}

case "${opmode}" in
patch) patch ;;
patchrollback) rollback ;;
patchhelp) print_usage ; exit 2 ;;
patchcheckversion) query_version_support ;;
patchlistversions) list_supported_versions ;;
dump) dump_patches ;;
*) echo "Incorrect combination of flags. Use option -h to get help."
exit 2 ;;
esac
2 changes: 1 addition & 1 deletion tools/autopatch/update_patch.sh
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ SYNOPSIS
DESCRIPTION
Update the patch for Nvidia NVENC or NVFBC drivers for a new version

-f PATCH_FILE The file (patch.sh/patch-fbc.sh) that should be updated
-f PATCH_FILE The file (patch.sh/patch-fbc.sh/patch-vk.sh) that should be updated
-v VERSION Driver version (by default copies latest existing patch)
-o OLD_VERSION Copy patch string from this older driver version
-b PATCHSTR Append PATCHSTR to the patch_list
Expand Down
2 changes: 2 additions & 0 deletions tools/readme-autogen/readme_autogen.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,11 @@ def row_gen():
driver_link = ''
nvenc_patch = md_true if drv['nvenc_patch'] else md_false
nvfbc_patch = md_true if drv['nvfbc_patch'] else md_false
nvvk_patch = md_true if drv['nvvk_patch'] else md_false
yield row_tmpl.substitute(version=drv['version'],
nvenc_patch=nvenc_patch,
nvfbc_patch=nvfbc_patch,
nvvk_patch=nvvk_patch,
driver_link=driver_link)
version_list = "\n".join(row_gen())
latest_version = drivers[-1]['version']
Expand Down
2 changes: 1 addition & 1 deletion tools/readme-autogen/templates/linux_driver_row.tmpl
Original file line number Diff line number Diff line change
@@ -1 +1 @@
| $version | $nvenc_patch | $nvfbc_patch | $driver_link |
| $version | $nvenc_patch | $nvfbc_patch | $nvvk_patch | $driver_link |
25 changes: 23 additions & 2 deletions tools/readme-autogen/templates/linux_readme_master.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ NVENC and NvFBC patches for Nvidia drivers

[NvFBC patch](patch-fbc.sh) allows to use NvFBC on consumer-grade GPUs. It should be applied same way as NVENC `patch.sh`, except you have to use `patch-fbc.sh` instead.

[Vulkan Patch](patch-vk.sh) removes restriction on maximum number of simultaneous Vulkan video encoding sessions imposed by Nvidia to consumer-grade GPUs, this is similar to [NVENC patch](patch.sh) but for Vulkan API.

Main target operating system is **GNU/Linux**, but for **Windows** support see [**win** (clickable)](win).

---
Expand All @@ -25,8 +27,8 @@ If you want to donate, please send it to your favorite open source organizations

## Version Table

| Version | NVENC patch | NVFBC patch | Driver link |
| :--- | :---: | :---: | ---: |
| Version | NVENC patch | NVFBC patch | NV_VK patch | Driver link |
| :--- | :---: | :---: | :---: | :---: |
$version_list

## Synopsis
Expand Down Expand Up @@ -70,7 +72,26 @@ DESCRIPTION
-d VERSION Use VERSION driver version when looking for libraries
instead of using nvidia-smi to detect it.
-f Enable support for Flatpak NVIDIA drivers.
```

```
# bash ./patch-vk.sh -h

SYNOPSIS
patch-vk.sh [-s] [-r|-h|-c VERSION|-l|-f]

DESCRIPTION
The patch for Nvidia vulkan drivers to remove NVENC session limit

-s Silent mode (No output)
-r Rollback to original (Restore lib from backup)
-h Print this help message
-c VERSION Check if version VERSION supported by this patch.
Returns true exit code (0) if version is supported.
-l List supported driver versions
-d VERSION Use VERSION driver version when looking for libraries
instead of using nvidia-smi to detect it.
-j Output the patch list to stdout as JSON
```

## Step-by-Step guide
Expand Down