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 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 diff --git a/config_spec.yml b/config_spec.yml index e384c08cfe..becd2ab50e 100644 --- a/config_spec.yml +++ b/config_spec.yml @@ -360,6 +360,9 @@ display: advanced_tree_state: type: bool default: false + setup_nvidia_profile: + type: bool + default: true audio: vp: 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 +} diff --git a/scripts/gen-license.py b/scripts/gen-license.py index c9a248f21a..bf943336fb 100755 --- a/scripts/gen-license.py +++ b/scripts/gen-license.py @@ -13,367 +13,517 @@ 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, 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_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): - fname = os.path.join('licenses', self.name + '.license.txt') - if os.path.exists(fname): - with open(fname, '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"), + ), + 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 + # + 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 +540,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() 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] 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' diff --git a/ui/xemu.c b/ui/xemu.c index 4ee5ac9bf3..4152e5ff0c 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; @@ -1266,6 +1267,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; @@ -1325,6 +1371,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);