From d238766756736c4b41350595ced44bddac4c6c16 Mon Sep 17 00:00:00 2001 From: Ryan Kuba Date: Wed, 9 Jul 2025 19:37:12 +0000 Subject: [PATCH 1/8] ci: Build on Ubuntu 22.04 for broader glibc compat --- .github/workflows/build.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index be106e2831..e59ebbdef9 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -174,25 +174,25 @@ jobs: build_param: --debug artifact_name: xemu-ubuntu-x86_64-debug artifact_filename: xemu-ubuntu-x86_64-debug.tgz - runs-on: ubuntu-24.04 + runs-on: ubuntu-22.04 - arch: x86_64 configuration: Release build_param: artifact_name: xemu-ubuntu-x86_64-release artifact_filename: xemu-ubuntu-x86_64-release.tgz - runs-on: ubuntu-24.04 + runs-on: ubuntu-22.04 - arch: aarch64 configuration: Debug build_param: --debug artifact_name: xemu-ubuntu-aarch64-debug artifact_filename: xemu-ubuntu-aarch64-debug.tgz - runs-on: ubuntu-24.04-arm + runs-on: ubuntu-22.04-arm - arch: aarch64 configuration: Release build_param: artifact_name: xemu-ubuntu-aarch64-release artifact_filename: xemu-ubuntu-aarch64-release.tgz - runs-on: ubuntu-24.04-arm + runs-on: ubuntu-22.04-arm steps: - name: Initialize compiler cache id: cache From c832e0d0dd1587565ad1a16beab471b95eaf9d5b Mon Sep 17 00:00:00 2001 From: Matt Borgerson Date: Thu, 10 Jul 2025 13:38:16 -0700 Subject: [PATCH 2/8] scripts/gen-license.py: Add license path parameter --- scripts/gen-license.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/scripts/gen-license.py b/scripts/gen-license.py index c9a248f21a..621dd52ac9 100755 --- a/scripts/gen-license.py +++ b/scripts/gen-license.py @@ -44,7 +44,7 @@ def banner(s): class Lib: def __init__(self, name, url, - license, license_url, license_lines=None, + license, license_url=None, license_path=None, license_lines=None, submodule=None, version=None, ships_static=set(), ships_dynamic=set(), @@ -54,6 +54,7 @@ class Lib: self.url = url self.license = license self.license_url = license_url + self.license_path = license_path or os.path.join('licenses', self.name + '.license.txt') self.license_lines = license_lines self.submodule = submodule self._version = version @@ -95,10 +96,9 @@ class Lib: assert False, 'Failed to get version info for ' + self.name @property - def license_text(self): - fname = os.path.join('licenses', self.name + '.license.txt') - if os.path.exists(fname): - with open(fname, 'r', encoding='utf-8') as f: + def license_text(self): + if os.path.exists(self.license_path): + with open(self.license_path, 'r', encoding='utf-8') as f: return f.read() import requests d = requests.get(self.license_url).content.decode('utf-8') From 967f35444cee1d7344cdd1e01387a9a15f6404f0 Mon Sep 17 00:00:00 2001 From: Matt Borgerson Date: Thu, 10 Jul 2025 13:38:16 -0700 Subject: [PATCH 3/8] scripts/gen-license.py: Run black formatter --- scripts/gen-license.py | 856 ++++++++++++++++++++++++----------------- 1 file changed, 502 insertions(+), 354 deletions(-) diff --git a/scripts/gen-license.py b/scripts/gen-license.py index 621dd52ac9..af56025d06 100755 --- a/scripts/gen-license.py +++ b/scripts/gen-license.py @@ -13,367 +13,509 @@ import os.path import re import sys -gplv2 = 'gplv2' -mit = 'mit' -bsd = 'bsd' -bsd_2clause = 'bsd-2clause' -bsd_3clause = 'bsd-3clause' -zlib = 'zlib' -lgplv2_1 = 'lgplv2_1' -apache2 = 'apache2' -unlicense = 'unlicense' -multi = 'multi' +gplv2 = "gplv2" +mit = "mit" +bsd = "bsd" +bsd_2clause = "bsd-2clause" +bsd_3clause = "bsd-3clause" +zlib = "zlib" +lgplv2_1 = "lgplv2_1" +apache2 = "apache2" +unlicense = "unlicense" +multi = "multi" -windows = 'windows' -macos = 'darwin' -linux = 'linux' -all_platforms = { windows, macos, linux } +windows = "windows" +macos = "darwin" +linux = "linux" +all_platforms = {windows, macos, linux} current_platform = linux versions = {} + def banner(s): - space = 1 - width = 80 - mid_len = 2*space+len(s) - left_dashes = (width-mid_len)//2 - right_dashes = width-mid_len-left_dashes - return '-'*left_dashes + ' ' + s + ' ' + '-'*right_dashes + space = 1 + width = 80 + mid_len = 2 * space + len(s) + left_dashes = (width - mid_len) // 2 + right_dashes = width - mid_len - left_dashes + return "-" * left_dashes + " " + s + " " + "-" * right_dashes class Lib: - def __init__(self, name, url, - license, license_url=None, license_path=None, license_lines=None, - submodule=None, - version=None, - ships_static=set(), ships_dynamic=set(), - platform=all_platforms, pkgconfig=None, - pkg_win=None, pkg_mac=None, pkg_ubuntu=None): - self.name = name - self.url = url - self.license = license - self.license_url = license_url - self.license_path = license_path or os.path.join('licenses', self.name + '.license.txt') - self.license_lines = license_lines - self.submodule = submodule - self._version = version - self.ships_static = ships_static - self.ships_dynamic = ships_dynamic - self.platform = platform - self.pkgconfig = pkgconfig - self.pkg_win = pkg_win - self.pkg_mac = pkg_mac - self.pkg_ubuntu = pkg_ubuntu + def __init__( + self, + name, + url, + license, + license_url=None, + license_path=None, + license_lines=None, + submodule=None, + version=None, + ships_static=set(), + ships_dynamic=set(), + platform=all_platforms, + pkgconfig=None, + pkg_win=None, + pkg_mac=None, + pkg_ubuntu=None, + ): + self.name = name + self.url = url + self.license = license + self.license_url = license_url + self.license_path = license_path or os.path.join( + "licenses", self.name + ".license.txt" + ) + self.license_lines = license_lines + self.submodule = submodule + self._version = version + self.ships_static = ships_static + self.ships_dynamic = ships_dynamic + self.platform = platform + self.pkgconfig = pkgconfig + self.pkg_win = pkg_win + self.pkg_mac = pkg_mac + self.pkg_ubuntu = pkg_ubuntu - @property - def version(self): - if self._version: - return self._version + @property + def version(self): + if self._version: + return self._version - if self.submodule: - self._version = self.submodule.head - return self._version + if self.submodule: + self._version = self.submodule.head + return self._version - if self.pkgconfig: - self._version = self.pkgconfig.modversion - return self._version + if self.pkgconfig: + self._version = self.pkgconfig.modversion + return self._version - if current_platform == windows and self.pkg_win: - self._version = subprocess.run(r"grep -e '_VERSION\s*:=' /opt/mxe/src/" + self.pkg_win + ".mk | cut -d'=' -f2", - capture_output=True, shell=True, - check=True).stdout.decode('utf-8').strip() - return self._version - elif current_platform == macos and self.pkg_mac: - self._version = versions[self.pkg_mac] - return self._version - elif current_platform == linux and self.pkg_ubuntu: - self._version = subprocess.run(r"dpkg -s " + self.pkg_ubuntu + " | grep Version | cut -d: -f2", - capture_output=True, shell=True, - check=True).stdout.decode('utf-8').strip() - return self._version + if current_platform == windows and self.pkg_win: + self._version = ( + subprocess.run( + r"grep -e '_VERSION\s*:=' /opt/mxe/src/" + + self.pkg_win + + ".mk | cut -d'=' -f2", + capture_output=True, + shell=True, + check=True, + ) + .stdout.decode("utf-8") + .strip() + ) + return self._version + elif current_platform == macos and self.pkg_mac: + self._version = versions[self.pkg_mac] + return self._version + elif current_platform == linux and self.pkg_ubuntu: + self._version = ( + subprocess.run( + r"dpkg -s " + self.pkg_ubuntu + " | grep Version | cut -d: -f2", + capture_output=True, + shell=True, + check=True, + ) + .stdout.decode("utf-8") + .strip() + ) + return self._version - assert False, 'Failed to get version info for ' + self.name + assert False, "Failed to get version info for " + self.name - @property - def license_text(self): - if os.path.exists(self.license_path): - with open(self.license_path, 'r', encoding='utf-8') as f: - return f.read() - import requests - d = requests.get(self.license_url).content.decode('utf-8') - if self.license_lines: - start, end = self.license_lines - d = '\n'.join(d.splitlines()[start-1:end+1]) - with open(fname, 'w') as f: - f.write(d) - return d + @property + def license_text(self): + if os.path.exists(self.license_path): + with open(self.license_path, "r", encoding="utf-8") as f: + return f.read() + import requests - @property - def is_active(self): - return current_platform in self.platform + d = requests.get(self.license_url).content.decode("utf-8") + if self.license_lines: + start, end = self.license_lines + d = "\n".join(d.splitlines()[start - 1 : end + 1]) + with open(fname, "w") as f: + f.write(d) + return d - @property - def does_ship_static(self): - return current_platform in self.ships_static + @property + def is_active(self): + return current_platform in self.platform - @property - def does_ship_dynamic(self): - return current_platform in self.ships_dynamic + @property + def does_ship_static(self): + return current_platform in self.ships_static - @property - def does_ship(self): - return self.is_active and (self.does_ship_static or self.does_ship_dynamic) + @property + def does_ship_dynamic(self): + return current_platform in self.ships_dynamic + + @property + def does_ship(self): + return self.is_active and (self.does_ship_static or self.does_ship_dynamic) class PkgConfig: - def __init__(self, name): - self.name = name + def __init__(self, name): + self.name = name + + @property + def modversion(self): + pkg_config = { + linux: "pkg-config", + windows: "x86_64-w64-mingw32.static-pkg-config", + macos: "pkg-config", + }[current_platform] + ver = subprocess.run( + [pkg_config, "--modversion", self.name], capture_output=True, check=True + ) + return ver.stdout.decode("utf-8").strip() - @property - def modversion(self): - pkg_config = { - linux: 'pkg-config', - windows: 'x86_64-w64-mingw32.static-pkg-config', - macos: 'pkg-config', - }[current_platform] - ver = subprocess.run([pkg_config, '--modversion', self.name], - capture_output=True, check=True) - return ver.stdout.decode('utf-8').strip() class Submodule: - def __init__(self, path): - self.path = path + def __init__(self, path): + self.path = path - @property - def head(self): - if self.path.endswith(".wrap"): - with open(self.path, "r", encoding="utf-8") as file: - contents = file.read() - revision = re.search(r"^revision\s*=\s*(.*)", contents, re.MULTILINE) - if revision: - return revision.group(1) - wrapdb_version = re.search(r"^wrapdb_version\s*=\s*([^-]*)", contents, re.MULTILINE) - if wrapdb_version: - return wrapdb_version.group(1) - assert False, "revision not found for subproject" + @property + def head(self): + if self.path.endswith(".wrap"): + with open(self.path, "r", encoding="utf-8") as file: + contents = file.read() + revision = re.search(r"^revision\s*=\s*(.*)", contents, re.MULTILINE) + if revision: + return revision.group(1) + wrapdb_version = re.search( + r"^wrapdb_version\s*=\s*([^-]*)", contents, re.MULTILINE + ) + if wrapdb_version: + return wrapdb_version.group(1) + assert False, "revision not found for subproject" - try: - return subprocess.run(['git', 'rev-parse', 'HEAD'], - cwd=self.path, capture_output=True, - check=True).stdout.decode('utf-8').strip() - except subprocess.CalledProcessError: - pass + try: + return ( + subprocess.run( + ["git", "rev-parse", "HEAD"], + cwd=self.path, + capture_output=True, + check=True, + ) + .stdout.decode("utf-8") + .strip() + ) + except subprocess.CalledProcessError: + pass - commit_file_path = os.path.join(self.path, 'HEAD') - if os.path.exists(commit_file_path): - return open(commit_file_path).read().strip() + commit_file_path = os.path.join(self.path, "HEAD") + if os.path.exists(commit_file_path): + return open(commit_file_path).read().strip() + + raise Exception("Failed to determine submodule revision") + return "" - raise Exception('Failed to determine submodule revision') - return '' LIBS = [ - -Lib('qemu', 'https://www.qemu.org/', - gplv2, 'https://raw.githubusercontent.com/xemu-project/xemu/master/LICENSE', - version='6.0.0' - ), - -# -# Built from source with xemu -# - -Lib('slirp', 'https://gitlab.freedesktop.org/slirp', - bsd_3clause, 'https://gitlab.freedesktop.org/slirp/libslirp/-/raw/master/COPYRIGHT', license_lines=(16,39), - ships_static=all_platforms, - pkgconfig=PkgConfig('slirp'), pkg_win='libslirp', pkg_mac='libslirp', pkg_ubuntu='libslirp-dev' - ), - -Lib('imgui', 'https://github.com/ocornut/imgui', - mit, 'https://raw.githubusercontent.com/ocornut/imgui/master/LICENSE.txt', - ships_static=all_platforms, - submodule=Submodule('subprojects/imgui.wrap') - ), - -Lib('implot', 'https://github.com/epezent/implot', - mit, 'https://raw.githubusercontent.com/epezent/implot/master/LICENSE', - ships_static=all_platforms, - submodule=Submodule('subprojects/implot.wrap') - ), - -Lib('noc', 'https://github.com/guillaumechereau/noc/blob/master/noc_file_dialog.h', - mit, 'https://raw.githubusercontent.com/xemu-project/xemu/master/ui/noc_file_dialog.h', license_lines=(1,22), - ships_static=all_platforms, - version='78b2e7b22506429dd1755ffff197c7da11507fd9' - ), - -Lib('stb_image', 'https://github.com/nothings/stb', - mit, 'https://raw.githubusercontent.com/nothings/stb/master/LICENSE', license_lines=(4,19), - ships_static=all_platforms, - version='2.25' - ), - -Lib('tomlplusplus', 'https://github.com/marzer/tomlplusplus', - mit, 'https://raw.githubusercontent.com/marzer/tomlplusplus/master/LICENSE', - ships_static=all_platforms, - submodule=Submodule('subprojects/tomlplusplus.wrap') - ), - -Lib('xxHash', 'https://github.com/Cyan4973/xxHash.git', - bsd, 'https://raw.githubusercontent.com/Cyan4973/xxHash/dev/LICENSE', license_lines=(1,26), - ships_static=all_platforms, - submodule=Submodule('subprojects/xxhash.wrap') - ), - -Lib('fpng', 'https://github.com/richgel999/fpng', - unlicense, 'https://github.com/richgel999/fpng/blob/main/README.md', - ships_static=all_platforms, - version='6926f5a0a78f22d42b074a0ab8032e07736babd4' - ), - -Lib('nv2a_vsh_cpu', 'https://github.com/xemu-project/nv2a_vsh_cpu', - unlicense, 'https://raw.githubusercontent.com/xemu-project/nv2a_vsh_cpu/main/LICENSE', - ships_static=all_platforms, - submodule=Submodule('subprojects/nv2a_vsh_cpu.wrap') - ), - -Lib('volk', 'https://github.com/zeux/volk', - mit, 'https://raw.githubusercontent.com/zeux/volk/master/LICENSE.md', - ships_static=all_platforms, - submodule=Submodule('subprojects/volk.wrap') - ), - -Lib('VulkanMemoryAllocator', 'https://github.com/GPUOpen-LibrariesAndSDKs/VulkanMemoryAllocator', - mit, 'https://raw.githubusercontent.com/GPUOpen-LibrariesAndSDKs/VulkanMemoryAllocator/master/LICENSE.txt', - ships_static=all_platforms, - submodule=Submodule('subprojects/VulkanMemoryAllocator.wrap') - ), - -Lib('SPIRV-Reflect', 'https://github.com/KhronosGroup/SPIRV-Reflect', - apache2, 'https://raw.githubusercontent.com/KhronosGroup/SPIRV-Reflect/main/LICENSE', - ships_static=all_platforms, - submodule=Submodule('subprojects/SPIRV-Reflect.wrap') - ), - -Lib('glslang', 'https://github.com/KhronosGroup/glslang', - bsd_3clause, 'https://raw.githubusercontent.com/KhronosGroup/glslang/main/LICENSE.txt', - ships_static=all_platforms, - submodule=Submodule('subprojects/glslang.wrap') - ), - -# -# Data files included with xemu -# - -Lib('roboto', 'https://github.com/googlefonts/roboto', - apache2, 'https://raw.githubusercontent.com/googlefonts/roboto/main/LICENSE', - ships_static=all_platforms, - version='2.138' - ), - -Lib('fontawesome', 'https://fontawesome.com', - multi, '', - ships_static=all_platforms, - version='6.1.1' - ), - -# -# Libraries either linked statically, dynamically linked & shipped, or dynamically linked with system-installed libraries only -# - -Lib('sdl2', 'https://www.libsdl.org/', - zlib, 'https://raw.githubusercontent.com/libsdl-org/SDL/main/LICENSE.txt', - ships_static={windows}, ships_dynamic={macos}, - pkgconfig=PkgConfig('sdl2'), pkg_win='sdl2', pkg_mac='sdl2', pkg_ubuntu='libsdl2-dev' - ), - -Lib('glib-2.0', 'https://gitlab.gnome.org/GNOME/glib', - lgplv2_1, 'https://gitlab.gnome.org/GNOME/glib/-/raw/master/COPYING', - ships_static={windows}, ships_dynamic={macos}, - pkgconfig=PkgConfig('glib-2.0'), pkg_win='glib', pkg_mac='glib', pkg_ubuntu='libglib2.0-dev' - ), - -# glib dep -Lib('pcre', 'http://pcre.org/', - bsd, 'http://www.pcre.org/original/license.txt', - pkgconfig=PkgConfig('libpcre'), pkg_ubuntu='libpcre3-dev' - ), -Lib('pcre2', 'http://pcre.org/', - bsd, 'https://www.pcre.org/licence.txt', - ships_static={windows}, ships_dynamic={macos}, - pkgconfig=PkgConfig('libpcre2-8'), pkg_win='pcre2', pkg_mac='pcre2', pkg_ubuntu='libpcre2-dev' - ), - -# glib dep -Lib('gettext', 'https://www.gnu.org/software/gettext/', - lgplv2_1, 'https://git.savannah.gnu.org/gitweb/?p=gettext.git;a=blob_plain;f=gettext-runtime/intl/COPYING.LIB;hb=HEAD', - ships_static={windows}, ships_dynamic={macos}, - pkg_win='gettext', pkg_mac='gettext-runtime', - ), - -# glib dep -Lib('iconv', 'https://www.gnu.org/software/libiconv/', - lgplv2_1, 'https://git.savannah.gnu.org/gitweb/?p=libiconv.git;a=blob_plain;f=COPYING.LIB;hb=HEAD', - ships_static={windows}, ships_dynamic={macos}, - pkg_win='libiconv', pkg_mac='libiconv' - ), - -Lib('libepoxy', 'https://github.com/anholt/libepoxy', - mit, 'https://raw.githubusercontent.com/anholt/libepoxy/master/COPYING', - ships_static={windows}, ships_dynamic={macos}, - pkgconfig=PkgConfig('epoxy'), pkg_win='libepoxy', pkg_mac='libepoxy', pkg_ubuntu='libepoxy-dev' - ), - -Lib('pixman', 'http://www.pixman.org/', - mit, 'https://cgit.freedesktop.org/pixman/plain/COPYING', - ships_static={windows}, ships_dynamic={macos}, - pkgconfig=PkgConfig('pixman-1'), - pkg_win='pixman', - pkg_mac='pixman', - pkg_ubuntu='libpixman-1-dev' - ), - -Lib('libsamplerate', 'https://github.com/libsndfile/libsamplerate', - bsd_2clause, 'https://raw.githubusercontent.com/libsndfile/libsamplerate/master/COPYING', - ships_static={windows}, ships_dynamic={macos}, - pkgconfig=PkgConfig('samplerate'), - pkg_win='libsamplerate', - pkg_mac='libsamplerate', - ), - -Lib('zlib', 'https://zlib.net/', - zlib, 'https://raw.githubusercontent.com/madler/zlib/master/README', license_lines=(87,106), - ships_static={windows}, ships_dynamic={macos}, - pkgconfig=PkgConfig('zlib'), pkg_win='zlib', pkg_mac='zlib', pkg_ubuntu='zlib1g-dev' - ), - -Lib('libmingw32', 'http://mingw-w64.org/', - multi, 'https://sourceforge.net/p/mingw-w64/mingw-w64/ci/master/tree/COPYING.MinGW-w64-runtime/COPYING.MinGW-w64-runtime.txt?format=raw', - ships_static={windows}, platform={windows}, - pkg_win='mingw-w64', - ), - -Lib('gtk', 'https://www.gtk.org/', - lgplv2_1, 'https://gitlab.gnome.org/GNOME/gtk/-/raw/master/COPYING', - platform={linux}, - pkgconfig=PkgConfig('gtk+-3.0'), pkg_ubuntu='libgtk-3-dev' - ), - -Lib('miniz', 'https://github.com/richgel999/miniz', - lgplv2_1, 'https://raw.githubusercontent.com/richgel999/miniz/master/LICENSE', - ships_static={windows}, platform={windows}, - version='2.1.0' - ), - -Lib('libcurl', 'https://curl.se/', - mit, 'https://raw.githubusercontent.com/curl/curl/refs/heads/master/COPYING', - ships_static={windows}, platform={windows}, - submodule=Submodule('subprojects/curl.wrap') - ), + Lib( + "qemu", + "https://www.qemu.org/", + gplv2, + "https://raw.githubusercontent.com/xemu-project/xemu/master/LICENSE", + version="6.0.0", + ), + # + # Built from source with xemu + # + Lib( + "slirp", + "https://gitlab.freedesktop.org/slirp", + bsd_3clause, + "https://gitlab.freedesktop.org/slirp/libslirp/-/raw/master/COPYRIGHT", + license_lines=(16, 39), + ships_static=all_platforms, + pkgconfig=PkgConfig("slirp"), + pkg_win="libslirp", + pkg_mac="libslirp", + pkg_ubuntu="libslirp-dev", + ), + Lib( + "imgui", + "https://github.com/ocornut/imgui", + mit, + "https://raw.githubusercontent.com/ocornut/imgui/master/LICENSE.txt", + ships_static=all_platforms, + submodule=Submodule("subprojects/imgui.wrap"), + ), + Lib( + "implot", + "https://github.com/epezent/implot", + mit, + "https://raw.githubusercontent.com/epezent/implot/master/LICENSE", + ships_static=all_platforms, + submodule=Submodule("subprojects/implot.wrap"), + ), + Lib( + "noc", + "https://github.com/guillaumechereau/noc/blob/master/noc_file_dialog.h", + mit, + "https://raw.githubusercontent.com/xemu-project/xemu/master/ui/noc_file_dialog.h", + license_lines=(1, 22), + ships_static=all_platforms, + version="78b2e7b22506429dd1755ffff197c7da11507fd9", + ), + Lib( + "stb_image", + "https://github.com/nothings/stb", + mit, + "https://raw.githubusercontent.com/nothings/stb/master/LICENSE", + license_lines=(4, 19), + ships_static=all_platforms, + version="2.25", + ), + Lib( + "tomlplusplus", + "https://github.com/marzer/tomlplusplus", + mit, + "https://raw.githubusercontent.com/marzer/tomlplusplus/master/LICENSE", + ships_static=all_platforms, + submodule=Submodule("subprojects/tomlplusplus.wrap"), + ), + Lib( + "xxHash", + "https://github.com/Cyan4973/xxHash.git", + bsd, + "https://raw.githubusercontent.com/Cyan4973/xxHash/dev/LICENSE", + license_lines=(1, 26), + ships_static=all_platforms, + submodule=Submodule("subprojects/xxhash.wrap"), + ), + Lib( + "fpng", + "https://github.com/richgel999/fpng", + unlicense, + "https://github.com/richgel999/fpng/blob/main/README.md", + ships_static=all_platforms, + version="6926f5a0a78f22d42b074a0ab8032e07736babd4", + ), + Lib( + "nv2a_vsh_cpu", + "https://github.com/xemu-project/nv2a_vsh_cpu", + unlicense, + "https://raw.githubusercontent.com/xemu-project/nv2a_vsh_cpu/main/LICENSE", + ships_static=all_platforms, + submodule=Submodule("subprojects/nv2a_vsh_cpu.wrap"), + ), + Lib( + "volk", + "https://github.com/zeux/volk", + mit, + "https://raw.githubusercontent.com/zeux/volk/master/LICENSE.md", + ships_static=all_platforms, + submodule=Submodule("subprojects/volk.wrap"), + ), + Lib( + "VulkanMemoryAllocator", + "https://github.com/GPUOpen-LibrariesAndSDKs/VulkanMemoryAllocator", + mit, + "https://raw.githubusercontent.com/GPUOpen-LibrariesAndSDKs/VulkanMemoryAllocator/master/LICENSE.txt", + ships_static=all_platforms, + submodule=Submodule("subprojects/VulkanMemoryAllocator.wrap"), + ), + Lib( + "SPIRV-Reflect", + "https://github.com/KhronosGroup/SPIRV-Reflect", + apache2, + "https://raw.githubusercontent.com/KhronosGroup/SPIRV-Reflect/main/LICENSE", + ships_static=all_platforms, + submodule=Submodule("subprojects/SPIRV-Reflect.wrap"), + ), + Lib( + "glslang", + "https://github.com/KhronosGroup/glslang", + bsd_3clause, + "https://raw.githubusercontent.com/KhronosGroup/glslang/main/LICENSE.txt", + ships_static=all_platforms, + submodule=Submodule("subprojects/glslang.wrap"), + ), + # + # Data files included with xemu + # + Lib( + "roboto", + "https://github.com/googlefonts/roboto", + apache2, + "https://raw.githubusercontent.com/googlefonts/roboto/main/LICENSE", + ships_static=all_platforms, + version="2.138", + ), + Lib( + "fontawesome", + "https://fontawesome.com", + multi, + "", + ships_static=all_platforms, + version="6.1.1", + ), + # + # Libraries either linked statically, dynamically linked & shipped, or dynamically linked with system-installed libraries only + # + Lib( + "sdl2", + "https://www.libsdl.org/", + zlib, + "https://raw.githubusercontent.com/libsdl-org/SDL/main/LICENSE.txt", + ships_static={windows}, + ships_dynamic={macos}, + pkgconfig=PkgConfig("sdl2"), + pkg_win="sdl2", + pkg_mac="sdl2", + pkg_ubuntu="libsdl2-dev", + ), + Lib( + "glib-2.0", + "https://gitlab.gnome.org/GNOME/glib", + lgplv2_1, + "https://gitlab.gnome.org/GNOME/glib/-/raw/master/COPYING", + ships_static={windows}, + ships_dynamic={macos}, + pkgconfig=PkgConfig("glib-2.0"), + pkg_win="glib", + pkg_mac="glib", + pkg_ubuntu="libglib2.0-dev", + ), + # glib dep + Lib( + "pcre", + "http://pcre.org/", + bsd, + "http://www.pcre.org/original/license.txt", + pkgconfig=PkgConfig("libpcre"), + pkg_ubuntu="libpcre3-dev", + ), + Lib( + "pcre2", + "http://pcre.org/", + bsd, + "https://www.pcre.org/licence.txt", + ships_static={windows}, + ships_dynamic={macos}, + pkgconfig=PkgConfig("libpcre2-8"), + pkg_win="pcre2", + pkg_mac="pcre2", + pkg_ubuntu="libpcre2-dev", + ), + # glib dep + Lib( + "gettext", + "https://www.gnu.org/software/gettext/", + lgplv2_1, + "https://git.savannah.gnu.org/gitweb/?p=gettext.git;a=blob_plain;f=gettext-runtime/intl/COPYING.LIB;hb=HEAD", + ships_static={windows}, + ships_dynamic={macos}, + pkg_win="gettext", + pkg_mac="gettext-runtime", + ), + # glib dep + Lib( + "iconv", + "https://www.gnu.org/software/libiconv/", + lgplv2_1, + "https://git.savannah.gnu.org/gitweb/?p=libiconv.git;a=blob_plain;f=COPYING.LIB;hb=HEAD", + ships_static={windows}, + ships_dynamic={macos}, + pkg_win="libiconv", + pkg_mac="libiconv", + ), + Lib( + "libepoxy", + "https://github.com/anholt/libepoxy", + mit, + "https://raw.githubusercontent.com/anholt/libepoxy/master/COPYING", + ships_static={windows}, + ships_dynamic={macos}, + pkgconfig=PkgConfig("epoxy"), + pkg_win="libepoxy", + pkg_mac="libepoxy", + pkg_ubuntu="libepoxy-dev", + ), + Lib( + "pixman", + "http://www.pixman.org/", + mit, + "https://cgit.freedesktop.org/pixman/plain/COPYING", + ships_static={windows}, + ships_dynamic={macos}, + pkgconfig=PkgConfig("pixman-1"), + pkg_win="pixman", + pkg_mac="pixman", + pkg_ubuntu="libpixman-1-dev", + ), + Lib( + "libsamplerate", + "https://github.com/libsndfile/libsamplerate", + bsd_2clause, + "https://raw.githubusercontent.com/libsndfile/libsamplerate/master/COPYING", + ships_static={windows}, + ships_dynamic={macos}, + pkgconfig=PkgConfig("samplerate"), + pkg_win="libsamplerate", + pkg_mac="libsamplerate", + ), + Lib( + "zlib", + "https://zlib.net/", + zlib, + "https://raw.githubusercontent.com/madler/zlib/master/README", + license_lines=(87, 106), + ships_static={windows}, + ships_dynamic={macos}, + pkgconfig=PkgConfig("zlib"), + pkg_win="zlib", + pkg_mac="zlib", + pkg_ubuntu="zlib1g-dev", + ), + Lib( + "libmingw32", + "http://mingw-w64.org/", + multi, + "https://sourceforge.net/p/mingw-w64/mingw-w64/ci/master/tree/COPYING.MinGW-w64-runtime/COPYING.MinGW-w64-runtime.txt?format=raw", + ships_static={windows}, + platform={windows}, + pkg_win="mingw-w64", + ), + Lib( + "gtk", + "https://www.gtk.org/", + lgplv2_1, + "https://gitlab.gnome.org/GNOME/gtk/-/raw/master/COPYING", + platform={linux}, + pkgconfig=PkgConfig("gtk+-3.0"), + pkg_ubuntu="libgtk-3-dev", + ), + Lib( + "miniz", + "https://github.com/richgel999/miniz", + lgplv2_1, + "https://raw.githubusercontent.com/richgel999/miniz/master/LICENSE", + ships_static={windows}, + platform={windows}, + version="2.1.0", + ), + Lib( + "libcurl", + "https://curl.se/", + mit, + "https://raw.githubusercontent.com/curl/curl/refs/heads/master/COPYING", + ships_static={windows}, + platform={windows}, + submodule=Submodule("subprojects/curl.wrap"), + ), ] + def gen_license(): - print(f'''\ + print( + f"""\ xemu is free and open source software. This binary of xemu has been made available to you under the terms of the GNU General Public License, version 2. @@ -390,36 +532,42 @@ The source code used to build this version of xemu is available at: xemu depends on several great packages/libraries which are also free and open source. The respective licenses of these packages are provided below. -''') - for lib in LIBS: - if lib.does_ship: - print(banner(lib.name)) - print('') - print('Project URL: ' + lib.url) - print('Version Included: ' + lib.version) - print('Project License:') +""" + ) + for lib in LIBS: + if lib.does_ship: + print(banner(lib.name)) + print("") + print("Project URL: " + lib.url) + print("Version Included: " + lib.version) + print("Project License:") + + print("") + print("\n".join([(" | " + l) for l in lib.license_text.splitlines()])) + print("") - print('') - print('\n'.join([(' | ' + l) for l in lib.license_text.splitlines()])) - print('') def main(): - import argparse - ap = argparse.ArgumentParser() - ap.add_argument('--platform', default='') - ap.add_argument('--version-file', default='') - args = ap.parse_args() + import argparse - if args.platform == '': - args.platform = sys.platform.lower() - global current_platform - current_platform = args.platform - if args.version_file != '': - with open(args.version_file, 'r') as f: - global versions - versions = {pkg: ver - for pkg, ver in map(lambda l: l.strip().split('='), f.readlines())} - gen_license() + ap = argparse.ArgumentParser() + ap.add_argument("--platform", default="") + ap.add_argument("--version-file", default="") + args = ap.parse_args() -if __name__ == '__main__': - main() + if args.platform == "": + args.platform = sys.platform.lower() + global current_platform + current_platform = args.platform + if args.version_file != "": + with open(args.version_file, "r") as f: + global versions + versions = { + pkg: ver + for pkg, ver in map(lambda l: l.strip().split("="), f.readlines()) + } + gen_license() + + +if __name__ == "__main__": + main() From 3b495859ca763779ffb8c1eeba88915ea6298681 Mon Sep 17 00:00:00 2001 From: Matt Borgerson Date: Thu, 10 Jul 2025 13:38:16 -0700 Subject: [PATCH 4/8] nvapi: Add library to create NVIDIA driver application profiles --- scripts/gen-license.py | 8 + thirdparty/meson.build | 2 + thirdparty/nvapi/meson.build | 4 + thirdparty/nvapi/nvapi.c | 165 ++++++++++++++++ thirdparty/nvapi/nvapi.h | 40 ++++ thirdparty/nvapi/nvapi_defs.LICENSE.txt | 22 +++ thirdparty/nvapi/nvapi_defs.h | 242 ++++++++++++++++++++++++ ui/meson.build | 4 + 8 files changed, 487 insertions(+) create mode 100644 thirdparty/nvapi/meson.build create mode 100644 thirdparty/nvapi/nvapi.c create mode 100644 thirdparty/nvapi/nvapi.h create mode 100644 thirdparty/nvapi/nvapi_defs.LICENSE.txt create mode 100644 thirdparty/nvapi/nvapi_defs.h diff --git a/scripts/gen-license.py b/scripts/gen-license.py index af56025d06..bf943336fb 100755 --- a/scripts/gen-license.py +++ b/scripts/gen-license.py @@ -337,6 +337,14 @@ LIBS = [ ships_static=all_platforms, submodule=Submodule("subprojects/glslang.wrap"), ), + Lib( + "NVIDIA NVAPI", + "https://github.com/NVIDIA/nvapi", + mit, + license_path="thirdparty/nvapi/nvapi_defs.LICENSE.txt", + version="R575", + ships_static={windows}, + ), # # Data files included with xemu # diff --git a/thirdparty/meson.build b/thirdparty/meson.build index b04ef34fbb..8f2858605f 100644 --- a/thirdparty/meson.build +++ b/thirdparty/meson.build @@ -26,3 +26,5 @@ vma = declare_dependency(include_directories: vma_subproj.include_directories('V dependencies: vulkan) endif + +subdir('nvapi') diff --git a/thirdparty/nvapi/meson.build b/thirdparty/nvapi/meson.build new file mode 100644 index 0000000000..fce74e7368 --- /dev/null +++ b/thirdparty/nvapi/meson.build @@ -0,0 +1,4 @@ +if host_os == 'windows' + libnvapi = static_library('nvapi', files(['nvapi.c'])) + nvapi = declare_dependency(link_with: libnvapi, include_directories: ['.']) +endif diff --git a/thirdparty/nvapi/nvapi.c b/thirdparty/nvapi/nvapi.c new file mode 100644 index 0000000000..734cb37dac --- /dev/null +++ b/thirdparty/nvapi/nvapi.c @@ -0,0 +1,165 @@ +/* + * NVIDIA Driver Settings Management + * + * Copyright (c) 2025 Matt Borgerson + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +#include +#include +#include +#include "nvapi.h" +#include "nvapi_defs.h" + +#define LOG(fmt, ...) fprintf(stderr, "nvapi: " fmt "\n", ##__VA_ARGS__) + +static HMODULE g_hnvapi; +static NvAPI_QueryInterface_t NvAPI_QueryInterface; +#define DECL_NVAPI_FUNC(name, id) static name##_t name; +NVAPI_FUNCS_X(DECL_NVAPI_FUNC) + +static bool init_nvapi_func(const char *name, unsigned int interface_id, + void **ptr) +{ + *ptr = NvAPI_QueryInterface(interface_id); + if (*ptr == NULL) { + LOG("Failed to resolve %s", name); + return false; + } + return true; +} + +bool nvapi_init(void) +{ +#ifdef _WIN64 + g_hnvapi = LoadLibraryA("nvapi64.dll"); +#else + g_hnvapi = LoadLibraryA("nvapi.dll"); +#endif + if (g_hnvapi == NULL) { + return false; + } + + NvAPI_QueryInterface = (NvAPI_QueryInterface_t)GetProcAddress( + g_hnvapi, "nvapi_QueryInterface"); + if (NvAPI_QueryInterface == NULL) { + LOG("GetProcAddress failed for NvAPI_QueryInterface"); + goto error; + } + +#define INIT_NVAPI_FUNC(name, id) \ + if (!init_nvapi_func(#name, id, (void *)&name)) \ + goto error; + NVAPI_FUNCS_X(INIT_NVAPI_FUNC) + + if (NvAPI_Initialize()) { + LOG("NvAPI_Initialize failed"); + goto error; + } + + return true; + +error: + FreeLibrary(g_hnvapi); + g_hnvapi = NULL; + return false; +} + +void nvapi_finalize(void) +{ + if (g_hnvapi) { + NvAPI_Unload(); + g_hnvapi = NULL; + } +} + +bool nvapi_setup_profile(NvApiProfileOpts opts) +{ + if (g_hnvapi == NULL) { + return false; + } + + bool result = false; + NvDRSSessionHandle session; + if (NvAPI_DRS_CreateSession(&session)) { + LOG("NvAPI_DRS_CreateSession failed"); + return false; + } + + if (NvAPI_DRS_LoadSettings(session)) { + LOG("NvAPI_DRS_LoadSettings failed"); + goto cleanup; + } + + NvDRSProfileHandle profile = NULL; + if (NvAPI_DRS_FindProfileByName(session, (NvU16 *)opts.profile_name, + &profile)) { + NVDRS_PROFILE profile_info = { + .version = NVDRS_PROFILE_VER, + .isPredefined = 0, + }; + wcsncpy(profile_info.profileName, opts.profile_name, + sizeof(profile_info.profileName) / sizeof(wchar_t)); + if (NvAPI_DRS_CreateProfile(session, &profile_info, &profile)) { + LOG("NvAPI_DRS_CreateProfile failed"); + goto cleanup; + } + LOG("Created new profile"); + } + + NVDRS_APPLICATION_V4 app = { .version = NVDRS_APPLICATION_VER_V4 }; + if (NvAPI_DRS_GetApplicationInfo(session, profile, + (NvU16 *)opts.executable_name, &app)) { + app.isPredefined = 0; + app.launcher[0] = 0; + app.fileInFolder[0] = 0; + wcsncpy(app.appName, opts.executable_name, + sizeof(app.appName) / sizeof(wchar_t)); + if (NvAPI_DRS_CreateApplication(session, profile, &app)) { + LOG("NvAPI_DRS_CreateApplication failed"); + goto cleanup; + } + LOG("Added application to profile"); + } + + NVDRS_SETTING setting = { + .version = NVDRS_SETTING_VER, + .settingId = OGL_THREAD_CONTROL_ID, + .settingType = NVDRS_DWORD_TYPE, + .u32CurrentValue = opts.threaded_optimization ? + OGL_THREAD_CONTROL_ENABLE : + OGL_THREAD_CONTROL_DISABLE, + }; + if (NvAPI_DRS_SetSetting(session, profile, &setting)) { + LOG("NvAPI_DRS_SetSetting for settingId %x failed", + setting.settingId); + goto cleanup; + } + + if (NvAPI_DRS_SaveSettings(session)) { + LOG("NvAPI_DRS_SaveSettings failed"); + goto cleanup; + } + LOG("Saved settings"); + result = true; + +cleanup: + NvAPI_DRS_DestroySession(session); + return result; +} diff --git a/thirdparty/nvapi/nvapi.h b/thirdparty/nvapi/nvapi.h new file mode 100644 index 0000000000..e353e8b228 --- /dev/null +++ b/thirdparty/nvapi/nvapi.h @@ -0,0 +1,40 @@ +/* + * NVIDIA Driver Settings Management + * + * Copyright (c) 2025 Matt Borgerson + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +#ifndef NVAPI_H +#define NVAPI_H + +#include +#include + +typedef struct NvApiProfileOpts { + const wchar_t *profile_name; + const wchar_t *executable_name; + bool threaded_optimization; +} NvApiProfileOpts; + +bool nvapi_init(void); +bool nvapi_setup_profile(NvApiProfileOpts opts); +void nvapi_finalize(void); + +#endif diff --git a/thirdparty/nvapi/nvapi_defs.LICENSE.txt b/thirdparty/nvapi/nvapi_defs.LICENSE.txt new file mode 100644 index 0000000000..fd32c88ed5 --- /dev/null +++ b/thirdparty/nvapi/nvapi_defs.LICENSE.txt @@ -0,0 +1,22 @@ +Headers from the NVIDIA NVAPI SDK: + +SPDX-FileCopyrightText: Copyright (c) 2019-2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +SPDX-License-Identifier: MIT + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the "Software"), +to deal in the Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/thirdparty/nvapi/nvapi_defs.h b/thirdparty/nvapi/nvapi_defs.h new file mode 100644 index 0000000000..2672a7cc89 --- /dev/null +++ b/thirdparty/nvapi/nvapi_defs.h @@ -0,0 +1,242 @@ +/* + * Definitions for interacting with the NVAPI library from the NVIDIA NVAPI + * SDK: + * + * SPDX-FileCopyrightText: Copyright (c) 2019-2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +#ifndef NVAPI_DEFS_H +#define NVAPI_DEFS_H + +// clang-format off + +#include + +enum ESetting { + OGL_THREAD_CONTROL_ID = 0x20C1221E, +}; + +enum EValues_OGL_THREAD_CONTROL { + OGL_THREAD_CONTROL_ENABLE = 0x00000001, + OGL_THREAD_CONTROL_DISABLE = 0x00000002, + OGL_THREAD_CONTROL_NUM_VALUES = 2, + OGL_THREAD_CONTROL_DEFAULT = 0U +}; + +typedef uint32_t NvU32; +typedef uint16_t NvU16; +typedef uint8_t NvU8; + +#define MAKE_NVAPI_VERSION(typeName,ver) (NvU32)(sizeof(typeName) | ((ver)<<16)) + +#define NV_DECLARE_HANDLE(name) struct name##__ { int unused; }; typedef struct name##__ *name + +NV_DECLARE_HANDLE(NvDRSSessionHandle); +NV_DECLARE_HANDLE(NvDRSProfileHandle); + +#define NVAPI_UNICODE_STRING_MAX 2048 +#define NVAPI_BINARY_DATA_MAX 4096 +typedef NvU16 NvAPI_UnicodeString[NVAPI_UNICODE_STRING_MAX]; +typedef char NvAPI_ShortString[64]; + +#define NVAPI_SETTING_MAX_VALUES 100 + +typedef enum _NVDRS_SETTING_TYPE +{ + NVDRS_DWORD_TYPE, + NVDRS_BINARY_TYPE, + NVDRS_STRING_TYPE, + NVDRS_WSTRING_TYPE +} NVDRS_SETTING_TYPE; + +typedef enum _NVDRS_SETTING_LOCATION +{ + NVDRS_CURRENT_PROFILE_LOCATION, + NVDRS_GLOBAL_PROFILE_LOCATION, + NVDRS_BASE_PROFILE_LOCATION, + NVDRS_DEFAULT_PROFILE_LOCATION +} NVDRS_SETTING_LOCATION; + +typedef struct _NVDRS_GPU_SUPPORT +{ + NvU32 geforce : 1; + NvU32 quadro : 1; + NvU32 nvs : 1; + NvU32 reserved4 : 1; + NvU32 reserved5 : 1; + NvU32 reserved6 : 1; + NvU32 reserved7 : 1; + NvU32 reserved8 : 1; + NvU32 reserved9 : 1; + NvU32 reserved10 : 1; + NvU32 reserved11 : 1; + NvU32 reserved12 : 1; + NvU32 reserved13 : 1; + NvU32 reserved14 : 1; + NvU32 reserved15 : 1; + NvU32 reserved16 : 1; + NvU32 reserved17 : 1; + NvU32 reserved18 : 1; + NvU32 reserved19 : 1; + NvU32 reserved20 : 1; + NvU32 reserved21 : 1; + NvU32 reserved22 : 1; + NvU32 reserved23 : 1; + NvU32 reserved24 : 1; + NvU32 reserved25 : 1; + NvU32 reserved26 : 1; + NvU32 reserved27 : 1; + NvU32 reserved28 : 1; + NvU32 reserved29 : 1; + NvU32 reserved30 : 1; + NvU32 reserved31 : 1; + NvU32 reserved32 : 1; +} NVDRS_GPU_SUPPORT; + +//! Enum to decide on the datatype of setting value. +typedef struct _NVDRS_BINARY_SETTING +{ + NvU32 valueLength; //!< valueLength should always be in number of bytes. + NvU8 valueData[NVAPI_BINARY_DATA_MAX]; +} NVDRS_BINARY_SETTING; + +typedef struct _NVDRS_SETTING_VALUES +{ + NvU32 version; //!< Structure Version + NvU32 numSettingValues; //!< Total number of values available in a setting. + NVDRS_SETTING_TYPE settingType; //!< Type of setting value. + union //!< Setting can hold either DWORD or Binary value or string. Not mixed types. + { + NvU32 u32DefaultValue; //!< Accessing default DWORD value of this setting. + NVDRS_BINARY_SETTING binaryDefaultValue; //!< Accessing default Binary value of this setting. + //!< Must be allocated by caller with valueLength specifying buffer size, or only valueLength will be filled in. + NvAPI_UnicodeString wszDefaultValue; //!< Accessing default unicode string value of this setting. + }; + union //!< Setting values can be of either DWORD, Binary values or String type, + { //!< NOT mixed types. + NvU32 u32Value; //!< All possible DWORD values for a setting + NVDRS_BINARY_SETTING binaryValue; //!< All possible Binary values for a setting + NvAPI_UnicodeString wszValue; //!< Accessing current unicode string value of this setting. + }settingValues[NVAPI_SETTING_MAX_VALUES]; +} NVDRS_SETTING_VALUES; + +//! Macro for constructing the version field of ::_NVDRS_SETTING_VALUES +#define NVDRS_SETTING_VALUES_VER MAKE_NVAPI_VERSION(NVDRS_SETTING_VALUES,1) + +typedef struct _NVDRS_SETTING_V1 +{ + NvU32 version; //!< Structure Version + NvAPI_UnicodeString settingName; //!< String name of setting + NvU32 settingId; //!< 32 bit setting Id + NVDRS_SETTING_TYPE settingType; //!< Type of setting value. + NVDRS_SETTING_LOCATION settingLocation; //!< Describes where the value in CurrentValue comes from. + NvU32 isCurrentPredefined; //!< It is different than 0 if the currentValue is a predefined Value, + //!< 0 if the currentValue is a user value. + NvU32 isPredefinedValid; //!< It is different than 0 if the PredefinedValue union contains a valid value. + union //!< Setting can hold either DWORD or Binary value or string. Not mixed types. + { + NvU32 u32PredefinedValue; //!< Accessing default DWORD value of this setting. + NVDRS_BINARY_SETTING binaryPredefinedValue; //!< Accessing default Binary value of this setting. + //!< Must be allocated by caller with valueLength specifying buffer size, + //!< or only valueLength will be filled in. + NvAPI_UnicodeString wszPredefinedValue; //!< Accessing default unicode string value of this setting. + }; + union //!< Setting can hold either DWORD or Binary value or string. Not mixed types. + { + NvU32 u32CurrentValue; //!< Accessing current DWORD value of this setting. + NVDRS_BINARY_SETTING binaryCurrentValue; //!< Accessing current Binary value of this setting. + //!< Must be allocated by caller with valueLength specifying buffer size, + //!< or only valueLength will be filled in. + NvAPI_UnicodeString wszCurrentValue; //!< Accessing current unicode string value of this setting. + }; +} NVDRS_SETTING_V1; + +//! Macro for constructing the version field of ::_NVDRS_SETTING +#define NVDRS_SETTING_VER1 MAKE_NVAPI_VERSION(NVDRS_SETTING_V1, 1) + +typedef NVDRS_SETTING_V1 NVDRS_SETTING; +#define NVDRS_SETTING_VER NVDRS_SETTING_VER1 + +typedef struct _NVDRS_APPLICATION_V4 +{ + NvU32 version; //!< Structure Version + NvU32 isPredefined; //!< Is the application userdefined/predefined + NvAPI_UnicodeString appName; //!< String name of the Application + NvAPI_UnicodeString userFriendlyName; //!< UserFriendly name of the Application + NvAPI_UnicodeString launcher; //!< Indicates the name (if any) of the launcher that starts the Application + NvAPI_UnicodeString fileInFolder; //!< Select this application only if this file is found. + //!< When specifying multiple files, separate them using the ':' character. + NvU32 isMetro:1; //!< Windows 8 style app + NvU32 isCommandLine:1; //!< Command line parsing for the application name + NvU32 reserved:30; //!< Reserved. Should be 0. + NvAPI_UnicodeString commandLine; //!< If isCommandLine is set to 0 this must be an empty. If isCommandLine is set to 1 + //!< this contains application's command line as if it was returned by GetCommandLineW. +} NVDRS_APPLICATION_V4; + +#define NVDRS_APPLICATION_VER_V4 MAKE_NVAPI_VERSION(NVDRS_APPLICATION_V4,4) + +typedef NVDRS_APPLICATION_V4 NVDRS_APPLICATION; +#define NVDRS_APPLICATION_VER NVDRS_APPLICATION_VER_V4 + +typedef struct _NVDRS_PROFILE_V1 +{ + NvU32 version; //!< Structure Version + NvAPI_UnicodeString profileName; //!< String name of the Profile + NVDRS_GPU_SUPPORT gpuSupport; //!< This read-only flag indicates the profile support on either + //!< Quadro, or Geforce, or both. + NvU32 isPredefined; //!< Is the Profile user-defined, or predefined + NvU32 numOfApps; //!< Total number of applications that belong to this profile. Read-only + NvU32 numOfSettings; //!< Total number of settings applied for this Profile. Read-only +} NVDRS_PROFILE_V1; + +typedef NVDRS_PROFILE_V1 NVDRS_PROFILE; + +//! Macro for constructing the version field of ::NVDRS_PROFILE +#define NVDRS_PROFILE_VER1 MAKE_NVAPI_VERSION(NVDRS_PROFILE_V1,1) +#define NVDRS_PROFILE_VER NVDRS_PROFILE_VER1 + +#define NVAPI_FUNCS_X(FUNC) \ + FUNC(NvAPI_DRS_CreateApplication, 0x4347A9DE) \ + FUNC(NvAPI_DRS_CreateProfile, 0xCC176068) \ + FUNC(NvAPI_DRS_CreateSession, 0x0694D52E) \ + FUNC(NvAPI_DRS_DestroySession, 0xDAD9CFF8) \ + FUNC(NvAPI_DRS_FindProfileByName, 0x7E4A9A0B) \ + FUNC(NvAPI_DRS_GetApplicationInfo, 0xED1F8C69) \ + FUNC(NvAPI_DRS_LoadSettings, 0x375DBD6B) \ + FUNC(NvAPI_DRS_SaveSettings, 0xFCBC7E14) \ + FUNC(NvAPI_DRS_SetSetting, 0x577DD202) \ + FUNC(NvAPI_Initialize, 0x0150E828) \ + FUNC(NvAPI_Unload, 0xD22BDD7E) + +typedef int(__cdecl *NvAPI_DRS_CreateApplication_t)(NvDRSSessionHandle, NvDRSProfileHandle, NVDRS_APPLICATION *); +typedef int(__cdecl *NvAPI_DRS_CreateProfile_t)(NvDRSSessionHandle, NVDRS_PROFILE *, NvDRSProfileHandle *); +typedef int(__cdecl *NvAPI_DRS_CreateSession_t)(NvDRSSessionHandle *); +typedef int(__cdecl *NvAPI_DRS_DestroySession_t)(NvDRSSessionHandle); +typedef int(__cdecl *NvAPI_DRS_FindProfileByName_t)(NvDRSSessionHandle, NvAPI_UnicodeString, NvDRSProfileHandle *); +typedef int(__cdecl *NvAPI_DRS_GetApplicationInfo_t)(NvDRSSessionHandle, NvDRSProfileHandle, NvAPI_UnicodeString, NVDRS_APPLICATION *); +typedef int(__cdecl *NvAPI_DRS_LoadSettings_t)(NvDRSSessionHandle); +typedef int(__cdecl *NvAPI_DRS_SaveSettings_t)(NvDRSSessionHandle); +typedef int(__cdecl *NvAPI_DRS_SetSetting_t)(NvDRSSessionHandle, NvDRSProfileHandle, NVDRS_SETTING *); +typedef int(__cdecl *NvAPI_Initialize_t)(void); +typedef int(__cdecl *NvAPI_Unload_t)(void); +typedef void *(__cdecl *NvAPI_QueryInterface_t)(unsigned int interface_id); + +#endif diff --git a/ui/meson.build b/ui/meson.build index 83ff4cfd1b..b0b19d3f79 100644 --- a/ui/meson.build +++ b/ui/meson.build @@ -36,6 +36,10 @@ xemu_ss.add(files( 'xemu-widescreen.c', )) +if host_os == 'windows' + xemu_ss.add(nvapi) +endif + subdir('xui') if host_os == 'darwin' From 8f29452ca928ba52156bfff9f8102ee3dd14ad8a Mon Sep 17 00:00:00 2001 From: Matt Borgerson Date: Thu, 10 Jul 2025 13:38:16 -0700 Subject: [PATCH 5/8] ui: Initialize preferred xemu NVIDIA application profile --- config_spec.yml | 3 +++ ui/xemu.c | 52 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 55 insertions(+) diff --git a/config_spec.yml b/config_spec.yml index 5b2991390c..68db468d9c 100644 --- a/config_spec.yml +++ b/config_spec.yml @@ -212,6 +212,9 @@ display: advanced_tree_state: type: bool default: false + setup_nvidia_profile: + type: bool + default: true audio: vp: diff --git a/ui/xemu.c b/ui/xemu.c index 3d3c2a9587..81a951d35c 100644 --- a/ui/xemu.c +++ b/ui/xemu.c @@ -61,6 +61,7 @@ #include #ifdef _WIN32 +#include "nvapi.h" // Provide hint to prefer high-performance graphics for hybrid systems // https://gpuopen.com/learn/amdpowerxpressrequesthighperformance/ __declspec(dllexport) DWORD AmdPowerXpressRequestHighPerformance = 1; @@ -1265,6 +1266,51 @@ static void sleep_ns(int64_t ns) #endif } +#ifdef _WIN32 +static const wchar_t *get_executable_name(void) +{ + static wchar_t exe_name[MAX_PATH] = { 0 }; + static bool initialized = false; + + if (!initialized) { + wchar_t full_path[MAX_PATH]; + DWORD length = GetModuleFileNameW(NULL, full_path, MAX_PATH); + if (length == 0 || length == MAX_PATH) { + return NULL; + } + + wchar_t *last_slash = wcsrchr(full_path, L'\\'); + if (last_slash) { + wcsncpy_s(exe_name, MAX_PATH, last_slash + 1, _TRUNCATE); + } else { + wcsncpy_s(exe_name, MAX_PATH, full_path, _TRUNCATE); + } + + initialized = true; + } + + return exe_name; +} + +static void setup_nvidia_profile(void) +{ + const wchar_t *exe_name = get_executable_name(); + if (exe_name == NULL) { + fprintf(stderr, "Failed to get current executable name\n"); + return; + } + + if (nvapi_init()) { + nvapi_setup_profile((NvApiProfileOpts){ + .profile_name = L"xemu", + .executable_name = exe_name, + .threaded_optimization = false, + }); + nvapi_finalize(); + } +} +#endif + int main(int argc, char **argv) { QemuThread thread; @@ -1324,6 +1370,12 @@ int main(int argc, char **argv) } atexit(xemu_settings_save); +#ifdef _WIN32 + if (g_config.display.setup_nvidia_profile) { + setup_nvidia_profile(); + } +#endif + sdl2_display_very_early_init(NULL); qemu_sem_init(&display_init_sem, 0); From 196726b898802b2a7ce0e5d859fdcf2a40bc6261 Mon Sep 17 00:00:00 2001 From: Erik Abair Date: Sun, 20 Jul 2025 16:59:31 -0700 Subject: [PATCH 6/8] nv2a/gl: Allocate only needed size for inline arrays Some HW/driver combinations appear to slow down dramatically when using very large GL buffers. Since the GL buffer supporting inline arrays is allocated after the guest `END`, the total size needed is known and the buffer may be sized appropriately. It would be good to test performance in games that use relatively large inline arrays (e.g., "King of Fighters 2003"). Fixes #2301 --- hw/xbox/nv2a/pgraph/gl/vertex.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/hw/xbox/nv2a/pgraph/gl/vertex.c b/hw/xbox/nv2a/pgraph/gl/vertex.c index 632dde2546..13b5639b9b 100644 --- a/hw/xbox/nv2a/pgraph/gl/vertex.c +++ b/hw/xbox/nv2a/pgraph/gl/vertex.c @@ -223,9 +223,9 @@ unsigned int pgraph_gl_bind_inline_array(NV2AState *d) nv2a_profile_inc_counter(NV2A_PROF_GEOM_BUFFER_UPDATE_2); glBindBuffer(GL_ARRAY_BUFFER, r->gl_inline_array_buffer); - glBufferData(GL_ARRAY_BUFFER, NV2A_MAX_BATCH_LENGTH * sizeof(uint32_t), - NULL, GL_STREAM_DRAW); - glBufferSubData(GL_ARRAY_BUFFER, 0, index_count * vertex_size, pg->inline_array); + GLsizeiptr buffer_size = index_count * vertex_size; + glBufferData(GL_ARRAY_BUFFER, buffer_size, NULL, GL_STREAM_DRAW); + glBufferSubData(GL_ARRAY_BUFFER, 0, buffer_size, pg->inline_array); pgraph_gl_bind_vertex_attributes(d, 0, index_count-1, true, vertex_size, index_count-1); @@ -308,4 +308,4 @@ void pgraph_gl_finalize_buffers(PGRAPHState *pg) glDeleteVertexArrays(1, &r->gl_vertex_array); r->gl_vertex_array = 0; -} \ No newline at end of file +} From 6da2d812435cb78783d9f7a5940d438f7dcdce7a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 21 Jul 2025 03:53:39 +0000 Subject: [PATCH 7/8] ci: bump astral-sh/setup-uv from 6.3.1 to 6.4.1 Bumps [astral-sh/setup-uv](https://github.com/astral-sh/setup-uv) from 6.3.1 to 6.4.1. - [Release notes](https://github.com/astral-sh/setup-uv/releases) - [Commits](https://github.com/astral-sh/setup-uv/compare/bd01e18f51369d5a26f1651c3cb451d3417e3bba...7edac99f961f18b581bbd960d59d049f04c0002f) --- updated-dependencies: - dependency-name: astral-sh/setup-uv dependency-version: 6.4.1 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- .github/workflows/bump-subproject-wraps.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/bump-subproject-wraps.yml b/.github/workflows/bump-subproject-wraps.yml index c027a03f0f..e7c0662a90 100644 --- a/.github/workflows/bump-subproject-wraps.yml +++ b/.github/workflows/bump-subproject-wraps.yml @@ -17,7 +17,7 @@ jobs: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 - name: Install the latest version of uv - uses: astral-sh/setup-uv@bd01e18f51369d5a26f1651c3cb451d3417e3bba # v6 + uses: astral-sh/setup-uv@7edac99f961f18b581bbd960d59d049f04c0002f # v6 with: enable-cache: false From e817cd59203e17f26a49cd861c5c0495abecf61f Mon Sep 17 00:00:00 2001 From: xemu-robot Date: Mon, 21 Jul 2025 06:04:07 +0000 Subject: [PATCH 8/8] meson: Bump SPIRV-Reflect to vulkan-sdk-1.4.321.0 --- subprojects/SPIRV-Reflect.wrap | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/subprojects/SPIRV-Reflect.wrap b/subprojects/SPIRV-Reflect.wrap index 43ec680c93..ff20642bd2 100644 --- a/subprojects/SPIRV-Reflect.wrap +++ b/subprojects/SPIRV-Reflect.wrap @@ -1,6 +1,6 @@ [wrap-git] url = https://github.com/KhronosGroup/SPIRV-Reflect -revision = c6c0f5c9796bdef40c55065d82e0df67c38a29a4 +revision = e55086b044225f9b511ae44dbf9b079d3625943f depth = 1 [update]