From 06aae1272d7aa4c1bc5584167592ceacb09e4a7c Mon Sep 17 00:00:00 2001 From: SDK Chan Date: Wed, 17 Sep 2025 15:12:29 +0000 Subject: [PATCH 1/6] [gpu/nvdrv] Unstub SetErrorNotifier, add PostErrorNotification function --- .../hle/service/nvdrv/devices/nvhost_gpu.cpp | 120 +++++++++++++++++- .../hle/service/nvdrv/devices/nvhost_gpu.h | 12 ++ 2 files changed, 127 insertions(+), 5 deletions(-) diff --git a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp index 5f754650d9..87012246fa 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp @@ -8,6 +8,7 @@ #include "common/assert.h" #include "common/logging/log.h" #include "core/core.h" +#include "core/hle/kernel/k_event.h" #include "core/hle/kernel/k_process.h" #include "core/hle/service/nvdrv/core/container.h" #include "core/hle/service/nvdrv/core/nvmap.h" @@ -161,11 +162,115 @@ NvResult nvhost_gpu::ZCullBind(IoctlZCullBind& params) { } NvResult nvhost_gpu::SetErrorNotifier(IoctlSetErrorNotifier& params) { - LOG_WARNING(Service_NVDRV, "(STUBBED) called, offset={:X}, size={:X}, mem={:X}", params.offset, - params.size, params.mem); + LOG_DEBUG(Service_NVDRV, "called, offset={:#X}, size={:#X}, mem={:#X}", + params.offset, params.size, params.mem); + + if (!params.mem || !params.size) { + std::scoped_lock lk(channel_mutex); + if (!channel_state->initialized) { + LOG_CRITICAL(Service_NVDRV, "No address space bound for setting up error notifier!"); + return NvResult::NotInitialized; + } + + error_notifier_params = {}; + LOG_DEBUG(Service_NVDRV, "Error notifier disabled!"); + return NvResult::Success; + } + + constexpr u64 error_notification_size = sizeof(IoctlGetErrorNotification); + if (params.size < error_notification_size) { + LOG_ERROR(Service_NVDRV, "Error notification size: {:#X} is too small. Need at least {:#X}", params.size, + error_notification_size); + return NvResult::InvalidSize; + } + + auto handle = nvmap.GetHandle(static_cast(params.mem)); + if (!handle) { + LOG_ERROR(Service_NVDRV, "Unknown nvmap handle id {:#X}", params.mem); + return NvResult::BadParameter; + } + + + if (params.offset > handle->size || params.size > (handle->size - params.offset)) { + LOG_ERROR(Service_NVDRV, "Error notifier out of bounds: offset={:#X} size={:#X} handle size={:#X}", params.offset, + params.size, handle->size); + return NvResult::InvalidSize; + } + + u64 write_address, write_offset, handle_id; + { + std::scoped_lock lk(channel_mutex); + if (!channel_state->initialized) { + LOG_CRITICAL(Service_NVDRV, "No address space bound for setting up error notifier!"); + return NvResult::NotInitialized; + } + + error_notifier_params = params; + write_address = handle->address; + write_offset = params.offset; + handle_id = handle->id; + } + + if (write_address) { + IoctlGetErrorNotification error_notification{}; + error_notification.status = static_cast(NotifierStatus::NoError); + system.ApplicationMemory().WriteBlock(write_address + write_offset, &error_notification, sizeof(error_notification)); + } else { + LOG_WARNING(Service_NVDRV, + "nvmap handle id {:#X} has no virtual address assigned; " + "skipping initialization write for error notification!", + handle_id); + } + return NvResult::Success; } +void nvhost_gpu::PostErrorNotification(u32 info32, u16 info16, NotifierStatus status) { + IoctlSetErrorNotifier error_notifier_params_snapshot{}; + Kernel::KEvent *error_notifier_event_snapshot{}; + { + std::scoped_lock lk(channel_mutex); + error_notifier_params_snapshot = error_notifier_params; + error_notifier_event_snapshot = error_notifier_event; + } + + if (!error_notifier_params_snapshot.mem || error_notifier_params_snapshot.size < sizeof(IoctlGetErrorNotification)) { + LOG_DEBUG(Service_NVDRV, "PostErrorNotification: notifier not configured or too small!"); + return; + } + + auto handle = nvmap.GetHandle(static_cast(error_notifier_params_snapshot.mem)); + if (!handle || !handle->address) { + LOG_ERROR(Service_NVDRV, "PostErrorNotification: invalid handle or virtual address!"); + return; + } + + IoctlGetErrorNotification error_init{}; + error_init.info32 = info32; + error_init.info16 = info16; + error_init.status = static_cast(status); + const u64 write_size = std::min(sizeof(IoctlGetErrorNotification), + error_notifier_params_snapshot.size); + if (error_notifier_params_snapshot.offset >= handle->size || + write_size > (handle->size - error_notifier_params_snapshot.offset)) { + LOG_ERROR(Service_NVDRV, "PostErrorNotification: bounds check failed!"); + return; + } + + const u64 virtual_address = handle->address + error_notifier_params_snapshot.offset; + if (virtual_address < handle->address) { + LOG_ERROR(Service_NVDRV, "PostErrorNotification: virtual address overflow!"); + return; + } + + auto &application_memory = system.ApplicationMemory(); + application_memory.WriteBlock(virtual_address, &error_init, write_size); + + if (error_notifier_event_snapshot) { + error_notifier_event_snapshot->Signal(); + } +} + NvResult nvhost_gpu::SetChannelPriority(IoctlChannelSetPriority& params) { channel_priority = params.priority; LOG_DEBUG(Service_NVDRV, "(STUBBED) called, priority={:X}", channel_priority); @@ -251,7 +356,7 @@ NvResult nvhost_gpu::AllocateObjectContext(IoctlAllocObjCtx& params) { params.flags = allowed_mask; } - s32_le ctx_class_number_index = + s32_le ctx_class_number_index = GetObjectContextClassNumberIndex(static_cast(params.class_num)); if (ctx_class_number_index < 0) { LOG_ERROR(Service_NVDRV, "Invalid class number for object context: {:#X}", @@ -324,6 +429,7 @@ NvResult nvhost_gpu::SubmitGPFIFOImpl(IoctlSubmitGpfifo& params, Tegra::CommandL if (flags.fence_wait.Value()) { if (flags.increment_value.Value()) { + PostErrorNotification(flags.raw, 0, NotifierStatus::GenericError); return NvResult::BadParameter; } @@ -357,7 +463,11 @@ NvResult nvhost_gpu::SubmitGPFIFOImpl(IoctlSubmitGpfifo& params, Tegra::CommandL NvResult nvhost_gpu::SubmitGPFIFOBase1(IoctlSubmitGpfifo& params, std::span commands, bool kickoff) { if (params.num_entries > commands.size()) { - UNIMPLEMENTED(); + LOG_ERROR(Service_NVDRV, + "SubmitGPFIFO: num_entries={:#X} > provided commands={:#X}", + params.num_entries, commands.size()); + + PostErrorNotification(params.num_entries, 0, NotifierStatus::BadGpfifo); return NvResult::InvalidSize; } @@ -376,7 +486,7 @@ NvResult nvhost_gpu::SubmitGPFIFOBase1(IoctlSubmitGpfifo& params, NvResult nvhost_gpu::SubmitGPFIFOBase2(IoctlSubmitGpfifo& params, std::span commands) { if (params.num_entries > commands.size()) { - UNIMPLEMENTED(); + PostErrorNotification(params.num_entries, 0, NotifierStatus::BadGpfifo); return NvResult::InvalidSize; } diff --git a/src/core/hle/service/nvdrv/devices/nvhost_gpu.h b/src/core/hle/service/nvdrv/devices/nvhost_gpu.h index fb0a5be959..16c4a95474 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_gpu.h +++ b/src/core/hle/service/nvdrv/devices/nvhost_gpu.h @@ -66,6 +66,16 @@ private: CtxChannelGPFIFO = 0xB06F, }; + enum class NotifierStatus : u16_le { + NoError = 0xFFFF, + GenericError = 0x0001, + MmuFault = 0x0002, + IllegalMethod= 0x0003, + InvalidObject= 0x0004, + BadGpfifo = 0x0005, + TimeoutHang = 0x0006, + }; + struct IoctlSetNvmapFD { s32_le nvmap_fd{}; }; @@ -172,6 +182,8 @@ private: s32_le nvmap_fd{}; u64_le user_data{}; IoctlZCullBind zcull_params{}; + IoctlSetErrorNotifier error_notifier_params{}; + void PostErrorNotification(u32 info32, u16 info16, NotifierStatus status); std::array, 6> ctxObjs{}; u32_le channel_priority{}; u32_le channel_timeslice{}; From b0e8e27212922f82cf34d3751c61c5b1e46dfaf7 Mon Sep 17 00:00:00 2001 From: SDK Chan Date: Wed, 17 Sep 2025 15:16:04 +0000 Subject: [PATCH 2/6] [gpu/nvdrv] Remove redundant whitespace --- src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp index 87012246fa..eb209bf599 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp @@ -190,7 +190,6 @@ NvResult nvhost_gpu::SetErrorNotifier(IoctlSetErrorNotifier& params) { return NvResult::BadParameter; } - if (params.offset > handle->size || params.size > (handle->size - params.offset)) { LOG_ERROR(Service_NVDRV, "Error notifier out of bounds: offset={:#X} size={:#X} handle size={:#X}", params.offset, params.size, handle->size); From e1ffeec21211f2fc3a255172aced9c51d659b896 Mon Sep 17 00:00:00 2001 From: crueter Date: Thu, 18 Sep 2025 02:37:02 +0200 Subject: [PATCH 3/6] [docs] refactor: full rewrite, generalization + dedup (#488) "docs but awesome" Combines most of the stuff that was repeated thrice over verbatim into a single common Build Instructions page, with additional caveats marked elsewhere. Prettifies some stuff too because why not. cc: @Lizzie @DraVee @MaranBr @SDK-Chan Co-authored-by: Caio Oliveira Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/488 Reviewed-by: Lizzie Reviewed-by: MaranBr --- README.md | 8 +- docs/Build.md | 160 +++++++++++++++++++++++++ docs/CPM.md | 2 +- docs/Caveats.md | 52 ++++++++ docs/Deps.md | 214 +++++++++++++++++++++++++++++++++ docs/Development.md | 24 +--- docs/Options.md | 69 +++++++++++ docs/README.md | 10 ++ docs/build/Android.md | 7 +- docs/build/FreeBSD.md | 81 ------------- docs/build/Linux.md | 138 --------------------- docs/build/OpenBSD.md | 10 -- docs/build/Solaris.md | 51 -------- docs/build/Windows.md | 259 ---------------------------------------- docs/build/macOS.md | 78 ------------ docs/img/creator-1.png | Bin 0 -> 64671 bytes docs/scripts/Linux.md | 31 +++++ docs/scripts/Windows.md | 29 +++++ 18 files changed, 573 insertions(+), 650 deletions(-) create mode 100644 docs/Build.md create mode 100644 docs/Caveats.md create mode 100644 docs/Deps.md create mode 100644 docs/Options.md create mode 100644 docs/README.md delete mode 100644 docs/build/FreeBSD.md delete mode 100644 docs/build/Linux.md delete mode 100644 docs/build/OpenBSD.md delete mode 100644 docs/build/Solaris.md delete mode 100644 docs/build/Windows.md delete mode 100644 docs/build/macOS.md create mode 100644 docs/img/creator-1.png create mode 100644 docs/scripts/Linux.md create mode 100644 docs/scripts/Windows.md diff --git a/README.md b/README.md index e1f0b50b37..959b903385 100644 --- a/README.md +++ b/README.md @@ -57,13 +57,7 @@ If you would like to contribute, we are open to new developers and pull requests ## Building -* **Windows**: [Windows Building Guide](./docs/build/Windows.md) -* **Linux**: [Linux Building Guide](./docs/build/Linux.md) -* **Android**: [Android Building Guide](./docs/build/Android.md) -* **Solaris**: [Solaris Building Guide](./docs/build/Solaris.md) -* **FreeBSD**: [FreeBSD Building Guide](./docs/build/FreeBSD.md) -* **macOS**: [macOS Building Guide](./docs/build/macOS.md) -* **OpenBSD**: [OpenBSD Building Guide](./docs/build/OpenBSD.md) +See the [General Build Guide](docs/Build.md) ## Download diff --git a/docs/Build.md b/docs/Build.md new file mode 100644 index 0000000000..52a671ab1e --- /dev/null +++ b/docs/Build.md @@ -0,0 +1,160 @@ +# Building Eden + +> [!WARNING] +> This guide is intended for developers ONLY. If you are not a developer or packager, you are unlikely to receive support. + +This is a full-fledged guide to build Eden on all supported platforms. + +## Dependencies +First, you must [install some dependencies](Deps.md). + +## Clone +Next, you will want to clone Eden via the terminal: + +```sh +git clone https://git.eden-emu.dev/eden-emu/eden.git +cd eden +``` + +Or use Qt Creator (Create Project -> Import Project -> Git Clone). + +## Android + +Android has a completely different build process than other platforms. See its [dedicated page](build/Android.md). + +## Initial Configuration + +If the configure phase fails, see the `Troubleshooting` section below. Usually, as long as you followed the dependencies guide, the defaults *should* successfully configure and build. + +### Qt Creator + +This is the recommended GUI method for Linux, macOS, and Windows. + +
+Click to Open + +> [!WARNING] +> On MSYS2, to use Qt Creator you are recommended to *also* install Qt from the online installer, ensuring to select the "MinGW" version. + +Open the CMakeLists.txt file in your cloned directory via File -> Open File or Project (Ctrl+O), if you didn't clone Eden via the project import tool. + +Select your desired "kit" (usually, the default is okay). RelWithDebInfo or Release is recommended: + +![Qt Creator kits](img/creator-1.png) + +Hit "Configure Project", then wait for CMake to finish configuring (may take a while on Windows). + +
+ +### Command Line + +This is recommended for *BSD, Solaris, Linux, and MSYS2. MSVC is possible, but not recommended. + +
+Click to Open + +Note that CMake must be in your PATH, and you must be in the cloned Eden directory. On Windows, you must also set up a Visual C++ development environment. This can be done by running `C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Auxiliary\Build\vcvars64.bat` in the same terminal. + +Recommended generators: + +- MSYS2: `MSYS Makefiles` +- MSVC: Install **[ninja](https://ninja-build.org/)** and use `Ninja`, OR use `Visual Studio 17 2022` +- macOS: `Ninja` (preferred) or `Xcode` +- Others: `Ninja` (preferred) or `UNIX Makefiles` + +BUILD_TYPE should usually be `Release` or `RelWithDebInfo` (debug symbols--compiled executable will be large). If you are using a debugger and annoyed with stuff getting optimized out, try `Debug`. + +Also see the [Options](Options.md) page for additional CMake options. + +```sh +cmake -S . -B build -G "GENERATOR" -DCMAKE_BUILD_TYPE= -DYUZU_TESTS=OFF +``` + +If you are on Windows and prefer to use Clang: + +```sh +cmake -S . -B build -G "GENERATOR" -DCMAKE_C_COMPILER=clang-cl -DCMAKE_CXX_COMPILER=clang-cl +``` + +
+ +### [CLion](https://www.jetbrains.com/clion/) + +
+Click to Open + +* Clone the Repository: + + + + + +--- + +### Building & Setup + +* Once Cloned, You will be taken to a prompt like the image below: + + + +* Set the settings to the image below: +* Change `Build type: Release` +* Change `Name: Release` +* Change `Toolchain Visual Studio` +* Change `Generator: Let CMake decide` +* Change `Build directory: build` + + + +* Click OK; now Clion will build a directory and index your code to allow for IntelliSense. Please be patient. +* Once this process has been completed (No loading bar bottom right), you can now build eden +* In the top right, click on the drop-down menu, select all configurations, then select eden + + + +* Now run by clicking the play button or pressing Shift+F10, and eden will auto-launch once built. + + +
+ +## Troubleshooting + +If your initial configure failed: +- *Carefully* re-read the [dependencies guide](Deps.md) +- Clear the CPM cache (`.cache/cpm`) and CMake cache (`/CMakeCache.txt`) +- Evaluate the error and find any related settings +- See the [CPM docs](CPM.md) to see if you may need to forcefully bundle any packages + +Otherwise, feel free to ask for help in Revolt or Discord. + +## Caveats + +Many platforms have quirks, bugs, and other fun stuff that may cause issues when building OR running. See the [Caveats page](Caveats.md) before continuing. + +## Building & Running + +### Qt Creator + +Simply hit Ctrl+B, or the "hammer" icon in the bottom left. To run, hit the "play" icon, or Ctrl+R. + +### Command Line + +If you are not on Windows and are using the `UNIX Makefiles` generator, you must also add `-j$(nproc)` to this command. + +``` +cmake --build build +``` + +Your compiled executable will be in: +- `build/bin/eden.exe` for Windows, +- `build/bin/eden.app/Contents/MacOS/eden` for macOS, +- and `build/bin/eden` for others. + +## Scripts + +Some platforms have convenience scripts provided for building. + +- **[Linux](scripts/Linux.md)** +- **[Windows](scripts/Windows.md)** + +macOS scripts will come soon. \ No newline at end of file diff --git a/docs/CPM.md b/docs/CPM.md index bce224da40..03d8a039f9 100644 --- a/docs/CPM.md +++ b/docs/CPM.md @@ -177,7 +177,7 @@ If `ci` is `true`: ### Examples -In order: OpenSSL CI, Boost (tag + artifact), discord-rpc (sha + options + patches), Opus (options + find_args) +In order: OpenSSL CI, Boost (tag + artifact), Opus (options + find_args), discord-rpc (sha + options + patches) ```json { diff --git a/docs/Caveats.md b/docs/Caveats.md new file mode 100644 index 0000000000..7bc2428bab --- /dev/null +++ b/docs/Caveats.md @@ -0,0 +1,52 @@ +# Caveats + +## Arch Linux + +- httplib AUR package is broken. Set `httplib_FORCE_BUNDLED=ON` if you have it installed. +- Eden is also available as an [AUR package](https://aur.archlinux.org/packages/eden-git). If you are unable to build, either use that or compare your process to the PKGBUILD. + +## Gentoo Linux + +Do not use the system sirit or xbyak packages. + +## macOS + +macOS is largely untested. Expect crashes, significant Vulkan issues, and other fun stuff. + +## Solaris + +Qt Widgets appears to be broken. For now, add `-DENABLE_QT=OFF` to your configure command. In the meantime, a Qt Quick frontend is in the works--check back later! + +This is needed for some dependencies that call cc directly (tz): + +```sh +echo '#!/bin/sh' >cc +echo 'gcc $@' >>cc +chmod +x cc +export PATH="$PATH:$PWD" +``` + +Default MESA is a bit outdated, the following environment variables should be set for a smoother experience: +```sh +export MESA_GL_VERSION_OVERRIDE=4.6 +export MESA_GLSL_VERSION_OVERRIDE=460 +export MESA_EXTENSION_MAX_YEAR=2025 +export MESA_DEBUG=1 +export MESA_VK_VERSION_OVERRIDE=1.3 +# Only if nvidia/intel drm drivers cause crashes, will severely hinder performance +export LIBGL_ALWAYS_SOFTWARE=1 +``` + +- Modify the generated ffmpeg.make (in build dir) if using multiple threads (base system `make` doesn't use `-j4`, so change for `gmake`). +- If using OpenIndiana, due to a bug in SDL2's CMake configuration, audio driver defaults to SunOS ``, which does not exist on OpenIndiana. Using external or bundled SDL2 may solve this. +- System OpenSSL generally does not work. Instead, use `-DYUZU_USE_BUNDLED_OPENSSL=ON` to use a bundled static OpenSSL, or build a system dependency from source. + +## OpenBSD + +After configuration, you may need to modify `externals/ffmpeg/CMakeFiles/ffmpeg-build/build.make` to use `-j$(nproc)` instead of just `-j`. + +## FreeBSD + +Eden is not currently available as a port on FreeBSD, though it is in the works. For now, the recommended method of usage is to compile it yourself. + +The available OpenSSL port (3.0.17) is out-of-date, and using a bundled static library instead is recommended; to do so, add `-DYUZU_USE_BUNDLED_OPENSSL=ON` to your CMake configure command. \ No newline at end of file diff --git a/docs/Deps.md b/docs/Deps.md new file mode 100644 index 0000000000..cfc6f0365b --- /dev/null +++ b/docs/Deps.md @@ -0,0 +1,214 @@ +# Dependencies + +To build Eden, you MUST have a C++ compiler. +* On Linux, this is usually [GCC](https://gcc.gnu.org/) 11+ or [Clang](https://clang.llvm.org/) v14+ + - GCC 12 also requires Clang 14+ +* On Windows, this is either: + - **[MSVC](https://visualstudio.microsoft.com/downloads/)**, + * *A convenience script to install the **minimal** version (Visual Build Tools) is provided in `.ci/windows/install-msvc.ps1`* + - clang-cl - can be downloaded from the MSVC installer, + - or **[MSYS2](https://www.msys2.org)** +* On macOS, this is Apple Clang + - This can be installed with `xcode-select --install` + +The following additional tools are also required: + +* **[CMake](https://www.cmake.org/)** 3.22+ - already included with the Android SDK +* **[Git](https://git-scm.com/)** for version control + - **[Windows installer](https://gitforwindows.org)** +* On Windows, you must install the **[Vulkan SDK](https://vulkan.lunarg.com/sdk/home#windows)** as well + - *A convenience script to install the latest SDK is provided in `.ci/windows/install-vulkan-sdk.ps1`* + +If you are on desktop and plan to use the Qt frontend, you *must* install Qt 6, and optionally Qt Creator (the recommended IDE for building) +* On Linux, *BSD and macOS, this can be done by the package manager + - If you wish to use Qt Creator, append `qtcreator` or `qt-creator` to the commands seen below. +* MSVC/clang-cl users on Windows must install through the [official installer](https://www.qt.io/download-qt-installer-oss) +* Linux and macOS users may choose to use the installer as well. +* MSYS2 can also install Qt 6 via the package manager + +If you are on Windows and NOT building with MSYS2, you may go [back home](Build.md) and continue. + +## Externals +The following are handled by Eden's externals: + +* [FFmpeg](https://ffmpeg.org/) (should use `-DYUZU_USE_EXTERNAL_FFMPEG=ON`) +* [SDL2](https://www.libsdl.org/download-2.0.php) 2.0.18+ (should use `-DYUZU_USE_EXTERNAL_SDL2=ON` OR `-DYUZU_USE_BUNDLED_SDL2=ON` to reduce compile time) + +All other dependencies will be downloaded and built by [CPM](https://github.com/cpm-cmake/CPM.cmake/) if `YUZU_USE_CPM` is on, but will always use system dependencies if available (UNIX-like only): + +* [Boost](https://www.boost.org/users/download/) 1.57.0+ +* [Catch2](https://github.com/catchorg/Catch2) 3.0.1 if `YUZU_TESTS` or `DYNARMIC_TESTS` are on +* [fmt](https://fmt.dev/) 8.0.1+ +* [lz4](http://www.lz4.org) +* [nlohmann\_json](https://github.com/nlohmann/json) 3.8+ +* [OpenSSL](https://www.openssl.org/source/) 1.1.1+ +* [ZLIB](https://www.zlib.net/) 1.2+ +* [zstd](https://facebook.github.io/zstd/) 1.5+ +* [enet](http://enet.bespin.org/) 1.3+ +* [Opus](https://opus-codec.org/) 1.3+ +* [MbedTLS](https://github.com/Mbed-TLS/mbedtls) 3+ + +Vulkan 1.3.274+ is also needed: +* [VulkanUtilityLibraries](https://github.com/KhronosGroup/Vulkan-Utility-Libraries) +* [VulkanHeaders](https://github.com/KhronosGroup/Vulkan-Headers) +* [SPIRV-Tools](https://github.com/KhronosGroup/SPIRV-Tools) +* [SPIRV-Headers](https://github.com/KhronosGroup/SPIRV-Headers) + +Certain other dependencies will be fetched by CPM regardless. System packages *can* be used for these libraries, but many are either not packaged by most distributions OR have issues when used by the system: + +* [SimpleIni](https://github.com/brofield/simpleini) +* [DiscordRPC](https://github.com/eden-emulator/discord-rpc) +* [cubeb](https://github.com/mozilla/cubeb) +* [libusb](https://github.com/libusb/libusb) +* [VulkanMemoryAllocator](https://github.com/GPUOpen-LibrariesAndSDKs/VulkanMemoryAllocator) +* [sirit](https://github.com/eden-emulator/sirit) +* [httplib](https://github.com/yhirose/cpp-httplib) - if `ENABLE_QT_UPDATE_CHECKER` or `ENABLE_WEB_SERVICE` are on +* [cpp-jwt](https://github.com/arun11299/cpp-jwt) 1.4+ - if `ENABLE_WEB_SERVICE` is on +* [unordered-dense](https://github.com/martinus/unordered_dense) +* [mcl](https://github.com/azahar-emu/mcl) - subject to removal + +On amd64: +* [xbyak](https://github.com/herumi/xbyak) - 7.22 or earlier is recommended +* [zycore](https://github.com/zyantific/zycore-c) +* [zydis](https://github.com/zyantific/zydis) 4+ +* Note: zydis and zycore-c MUST match. Using one as a system dependency and the other as a bundled dependency WILL break things + +On aarch64 OR if `DYNARMIC_TESTS` is on: +* [oaknut](https://github.com/merryhime/oaknut) 2.0.1+ + +On riscv64: +* [biscuit](https://github.com/lioncash/biscuit) 0.9.1+ + +## Commands + +These are commands to install all necessary dependencies on various Linux and BSD distributions, as well as macOS. Always review what you're running before you hit Enter! + +Click on the arrows to expand. + +
+Arch Linux + +```sh +sudo pacman -Syu --needed base-devel boost catch2 cmake enet ffmpeg fmt git glslang libzip lz4 mbedtls ninja nlohmann-json openssl opus qt6-base qt6-multimedia sdl2 zlib zstd zip unzip zydis zycore vulkan-headers vulkan-utility-libraries libusb spirv-tools spirv-headers +``` + +* Building with QT Web Engine requires `qt6-webengine` as well. +* Proper Wayland support requires `qt6-wayland` +* GCC 11 or later is required. +
+ +
+Ubuntu, Debian, Mint Linux + +```sh +sudo apt-get install autoconf cmake g++ gcc git glslang-tools libasound2 libboost-context-dev libglu1-mesa-dev libhidapi-dev libpulse-dev libtool libudev-dev libxcb-icccm4 libxcb-image0 libxcb-keysyms1 libxcb-render-util0 libxcb-xinerama0 libxcb-xkb1 libxext-dev libxkbcommon-x11-0 mesa-common-dev nasm ninja-build qt6-base-private-dev libmbedtls-dev catch2 libfmt-dev liblz4-dev nlohmann-json3-dev libzstd-dev libssl-dev libavfilter-dev libavcodec-dev libswscale-dev pkg-config zlib1g-dev libva-dev libvdpau-dev qt6-tools-dev libzydis-dev zydis-tools libzycore-dev +``` + +* Ubuntu 22.04, Linux Mint 20, or Debian 12 or later is required. +* To enable QT Web Engine, add `-DYUZU_USE_QT_WEB_ENGINE=ON` when running CMake. +
+ +
+Fedora Linux + +```sh +sudo dnf install autoconf ccache cmake fmt-devel gcc{,-c++} glslang hidapi-devel json-devel libtool libusb1-devel libzstd-devel lz4-devel nasm ninja-build openssl-devel pulseaudio-libs-devel qt6-linguist qt6-qtbase{-private,}-devel qt6-qtwebengine-devel qt6-qtmultimedia-devel speexdsp-devel wayland-devel zlib-devel ffmpeg-devel libXext-devel +``` + +* Force system libraries via CMake arguments: + * SDL2: `-DYUZU_USE_BUNDLED_SDL2=OFF -DYUZU_USE_EXTERNAL_SDL2=OFF` + * FFmpeg: `-DYUZU_USE_EXTERNAL_FFMPEG=OFF` +* [RPM Fusion](https://rpmfusion.org/) is required for `ffmpeg-devel` +* Fedora 32 or later is required. +* Fedora 36+ users with GCC 12 need Clang and should configure CMake with: +
+ +
+macOS + +Install dependencies from **[Homebrew](https://brew.sh/)** + +```sh +brew install autoconf automake boost ffmpeg fmt glslang hidapi libtool libusb lz4 ninja nlohmann-json openssl pkg-config qt@6 sdl2 speexdsp zlib zstd cmake Catch2 molten-vk vulkan-loader spirv-tools +``` + +If you are compiling on Intel Mac, or are using a Rosetta Homebrew installation, you must replace all references of `/opt/homebrew` with `/usr/local`. + +To run with MoltenVK, install additional dependencies: +```sh +brew install molten-vk vulkan-loader +``` + +
+ +
+FreeBSD + +``` +devel/cmake +devel/sdl20 +devel/boost-libs +devel/catch2 +devel/libfmt +devel/nlohmann-json +devel/ninja +devel/nasm +devel/autoconf +devel/pkgconf +devel/qt6-base + +net/enet + +multimedia/ffnvcodec-headers +multimedia/ffmpeg + +audio/opus + +archivers/liblz4 + +lang/gcc12 + +graphics/glslang +graphics/vulkan-utility-libraries +``` + +If using FreeBSD 12 or prior, use `devel/pkg-config` instead. +
+ +
+OpenBSD + +```sh +pkg_add -u +pkg_add cmake nasm git boost unzip--iconv autoconf-2.72p0 bash ffmpeg glslang gmake llvm-19.1.7p3 qt6 jq fmt nlohmann-json enet boost vulkan-utility-libraries vulkan-headers spirv-headers spirv-tools catch2 sdl2 libusb1.1.0.27 +``` +
+ +
+Solaris / OpenIndiana + +Always consult [the OpenIndiana package list](https://pkg.openindiana.org/hipster/en/index.shtml) to cross-verify availability. + +Run the usual update + install of essential toolings: `sudo pkg update && sudo pkg install git cmake`. + +- **gcc**: `sudo pkg install developer/gcc-14`. +- **clang**: Version 20 is broken, use `sudo pkg install developer/clang-19`. + +Then install the libraries: `sudo pkg install qt6 boost glslang libzip library/lz4 nlohmann-json openssl opus sdl2 zlib compress/zstd unzip pkg-config nasm autoconf mesa library/libdrm header-drm developer/fmt`. +
+ +
+MSYS2 + +* Open the `MSYS2 MinGW 64-bit` shell (`mingw64.exe`) +* Download and install all dependencies using: + * `pacman -Syu git make mingw-w64-x86_64-SDL2 mingw-w64-x86_64-cmake mingw-w64-x86_64-python-pip mingw-w64-x86_64-qt6 mingw-w64-x86_64-toolchain autoconf libtool automake-wrapper` +* Add MinGW binaries to the PATH: + * `echo 'PATH=/mingw64/bin:$PATH' >> ~/.bashrc` +* Add VulkanSDK to the PATH: + * `echo 'PATH=$(readlink -e /c/VulkanSDK/*/Bin/):$PATH' >> ~/.bashrc` +
+ +## All Done + +You may now return to the **[root build guide](Build.md)**. \ No newline at end of file diff --git a/docs/Development.md b/docs/Development.md index e4816cd1ec..d170818d10 100644 --- a/docs/Development.md +++ b/docs/Development.md @@ -1,23 +1,3 @@ -# Development - -* **Windows**: [Windows Building Guide](./build/Windows.md) -* **Linux**: [Linux Building Guide](./build/Linux.md) -* **Android**: [Android Building Guide](./build/Android.md) -* **Solaris**: [Solaris Building Guide](./build/Solaris.md) -* **FreeBSD**: [FreeBSD Building Guide](./build/FreeBSD.md) -* **macOS**: [macOS Building Guide](./build/macOS.md) -* **OpenBSD**: [OpenBSD Building Guide](./build/OpenBSD.md) - -# CPM - -CPM (CMake Package Manager) is the preferred method of managing dependencies within Eden. Documentation on adding dependencies/using CPMUtil is in the works. - -Notes: -- `YUZU_USE_CPM` is set by default on MSVC and Android. Other platforms should use this if certain "required" system dependencies (e.g. OpenSSL) are broken or missing -- `CPMUTIL_DEFAULT_SYSTEM` can be set to `OFF` to force the usage of bundled dependencies. This can marginally decrease the final package size. -- When adding new prebuilt dependencies a la OpenSSL, SDL2, or FFmpeg, there *must* be a CMake option made available to forcefully download this bundle. See the OpenSSL implementation in the root CMakeLists for an example. - * This is necessary to allow for creation of fully-qualified source packs that allow for offline builds after download (some package managers and distros enforce this) - # Guidelines ## License Headers @@ -37,6 +17,8 @@ FIX=true .ci/license-header.sh git commit --amend -a --no-edit ``` +If the work is licensed/vendored from other people or projects, you may omit the license headers. Additionally, if you wish to retain authorship over a piece of code, you may attribute it to yourself; however, the code may be changed at any given point and brought under the attribution of Eden. + ## Pull Requests Pull requests are only to be merged by core developers when properly tested and discussions conclude on Discord or other communication channels. Labels are recommended but not required. However, all PRs MUST be namespaced and optionally typed: ``` @@ -49,7 +31,7 @@ Pull requests are only to be merged by core developers when properly tested and - The level of namespacing is generally left to the committer's choice. - However, we never recommend going more than two levels *except* in `hle`, in which case you may go as many as four levels depending on the specificity of your changes. -- Ocassionally, up to two namespaces may be provided for more clarity. +- Ocassionally, up to two additional namespaces may be provided for more clarity. * Changes that affect the entire project (sans CMake changes) should be namespaced as `meta`. - Maintainers are permitted to change namespaces at will. - Commits within PRs are not required to be namespaced, but it is highly recommended. diff --git a/docs/Options.md b/docs/Options.md new file mode 100644 index 0000000000..d19aab63f6 --- /dev/null +++ b/docs/Options.md @@ -0,0 +1,69 @@ +# CMake Options + +To change these options, add `-DOPTION_NAME=NEWVALUE` to the command line. + +- On Qt Creator, go to Project -> Current Configuration + +Notes: +- Defaults are marked per-platform. +- "Non-UNIX" just means Windows/MSVC and Android (yes, macOS is UNIX +- Android generally doesn't need to change anything; if you do, go to `src/android/app/build.gradle.kts` +- To set a boolean variable to on, use `ON` for the value; to turn it off, use `OFF` +- If a variable is mentioned as being e.g. "ON" for a specific platform(s), that means it is defaulted to OFF on others +- TYPE is always boolean unless otherwise specified +- Format: + * `OPTION_NAME` (TYPE DEFAULT) DESCRIPTION + +## Options + +- `YUZU_USE_CPM` (ON for non-UNIX) Use CPM to fetch system dependencies (fmt, boost, etc) if needed. Externals will still be fetched. See the [CPM](CPM.md) and [Deps](Deps.md) docs for more info. +- `ENABLE_WEB_SERVICE` (ON) Enable multiplayer service +- `ENABLE_WIFI_SCAN` (OFF) Enable WiFi scanning (requires iw on Linux) - experimental +- `YUZU_USE_BUNDLED_FFMPEG` (ON for non-UNIX) Download (Windows, Android) or build (UNIX) bundled FFmpeg +- `ENABLE_CUBEB` (ON) Enables the cubeb audio backend +- `YUZU_TESTS` (ON) Compile tests - requires Catch2 +- `YUZU_USE_PRECOMPILED_HEADERS` (ON for non-UNIX) Use precompiled headers +- `YUZU_DOWNLOAD_ANDROID_VVL` (ON) Download validation layer binary for Android +- `YUZU_ENABLE_LTO` (OFF) Enable link-time optimization + * Not recommended on Windows + * UNIX may be better off appending `-flto=thin` to compiler args +- `YUZU_DOWNLOAD_TIME_ZONE_DATA` (ON) Always download time zone binaries + * Currently, build fails without this +- `YUZU_USE_FASTER_LD` (ON) Check if a faster linker is available + * Only available on UNIX +- `USE_SYSTEM_MOLTENVK` (OFF, macOS only) Use the system MoltenVK lib (instead of the bundled one) +- `YUZU_TZDB_PATH` (string) Path to a pre-downloaded timezone database (useful for nixOS) +- `ENABLE_OPENSSL` (ON for Linux and *BSD) Enable OpenSSL backend for the ssl service + * Always enabled if the web service is enabled +- `YUZU_USE_BUNDLED_OPENSSL` (ON for MSVC) Download bundled OpenSSL build + * Always on for Android + * Unavailable on OpenBSD + +The following options are desktop only: +- `ENABLE_SDL2` (ON) Enable the SDL2 desktop, audio, and input frontend (HIGHLY RECOMMENDED!) + * Unavailable on Android +- `YUZU_USE_EXTERNAL_SDL2` (ON for non-UNIX) Compiles SDL2 from source +- `YUZU_USE_BUNDLED_SDL2` (ON for MSVC) Download a prebuilt SDL2 + * Unavailable on OpenBSD + * Only enabled if YUZU_USE_CPM and ENABLE_SDL2 are both ON +- `ENABLE_LIBUSB` (ON) Enable the use of the libusb input frontend (HIGHLY RECOMMENDED) +- `ENABLE_OPENGL` (ON) Enable the OpenGL graphics frontend + * Unavailable on Windows/ARM64 and Android +- `ENABLE_QT` (ON) Enable the Qt frontend (recommended) +- `ENABLE_QT_TRANSLATION` (OFF) Enable translations for the Qt frontend +- `ENABLE_QT_UPDATE_CHECKER` (OFF) Enable update checker for the Qt frontend +- `YUZU_USE_BUNDLED_QT` (ON for MSVC) Download bundled Qt binaries + * Note that using **system Qt** requires you to include the Qt CMake directory in `CMAKE_PREFIX_PATH`, e.g: + * `-DCMAKE_PREFIX_PATH=C:/Qt/6.9.0/msvc2022_64/lib/cmake/Qt6` +- `YUZU_QT_MIRROR` (string) What mirror to use for downloading the bundled Qt libraries +- `YUZU_USE_QT_MULTIMEDIA` (OFF) Use QtMultimedia for camera support +- `YUZU_USE_QT_WEB_ENGINE` (OFF) Use QtWebEngine for web applet implementation (requires the huge QtWebEngine dependency; not recommended) +- `USE_DISCORD_PRESENCE` (OFF) Enables Discord Rich Presence (Qt frontend only) +- `YUZU_ROOM` (ON) Enable dedicated room functionality +- `YUZU_ROOM_STANDALONE` (ON) Enable standalone room executable (eden-room) + * Requires `YUZU_ROOM` +- `YUZU_CMD` (ON) Compile the SDL2 frontend (eden-cli) - requires SDL2 +- `YUZU_CRASH_DUMPS` Compile crash dump (Minidump) support" + * Currently only available on Windows and Linux + +See `src/dynarmic/CMakeLists.txt` for additional options--usually, these don't need changed \ No newline at end of file diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 0000000000..71e79e15ea --- /dev/null +++ b/docs/README.md @@ -0,0 +1,10 @@ +# Eden Build Documentation + +This contains documentation created by developers. This contains build instructions, guidelines, instructions/layouts for [cool stuff we made](CPM.md), and more. + +- **[General Build Instructions](Build.md)** +- **[Development Guidelines](Development.md)** +- **[Dependencies](Deps.md)** +- **[CPM - CMake Package Manager](CPM.md)** +- **[Platform-Specific Caveats](Caveats.md)** +- **[User Directory Handling](User.md)** \ No newline at end of file diff --git a/docs/build/Android.md b/docs/build/Android.md index 0538d351ea..c8ff3a3b1e 100644 --- a/docs/build/Android.md +++ b/docs/build/Android.md @@ -2,7 +2,7 @@ ## Dependencies * [Android Studio](https://developer.android.com/studio) -* [NDK 25.2.9519653 and CMake 3.22.1](https://developer.android.com/studio/projects/install-ndk#default-version) +* [NDK 27+ and CMake 3.22.1](https://developer.android.com/studio/projects/install-ndk#default-version) * [Git](https://git-scm.com/download) ### WINDOWS ONLY - Additional Dependencies @@ -16,8 +16,7 @@ git clone --recursive https://git.eden-emu.dev/eden-emu/eden.git ``` Eden by default will be cloned into - * `C:\Users\\eden` on Windows -* `~/eden` on Linux -* And wherever on macOS +* `~/eden` on Linux and macOS ## Building 1. Start Android Studio, on the startup dialog select `Open`. @@ -32,7 +31,7 @@ Eden by default will be cloned into - `export ANDROID_SDK_ROOT=path/to/sdk` `export ANDROID_NDK_ROOT=path/to/ndk`. 4. Navigate to `eden/src/android`. -5. Then Build with `./gradlew assemblerelWithDebInfo`. +5. Then Build with `./gradlew assembleRelWithDebInfo`. 6. To build the optimised build use `./gradlew assembleGenshinSpoofRelWithDebInfo`. ### Script diff --git a/docs/build/FreeBSD.md b/docs/build/FreeBSD.md deleted file mode 100644 index 97eef8f9d8..0000000000 --- a/docs/build/FreeBSD.md +++ /dev/null @@ -1,81 +0,0 @@ -Eden is not currently available as a port on FreeBSD, though it is in the works. For now, the recommended method of usage is to compile it yourself. Check back often, as the build process frequently changes. - -## Dependencies. -Eden needs the following dependencies: - -``` -devel/cmake -devel/sdl20 -devel/boost-libs -devel/catch2 -devel/libfmt -devel/nlohmann-json -devel/ninja -devel/nasm -devel/autoconf -devel/pkgconf -devel/qt6-base - -net/enet - -multimedia/ffnvcodec-headers -multimedia/ffmpeg - -audio/opus - -archivers/liblz4 - -lang/gcc12 - -graphics/glslang -graphics/vulkan-utility-libraries -``` - -If using FreeBSD 12 or prior, use `devel/pkg-config` instead. - ---- - -### Build preparations: -Run the following command to clone eden with git: -```sh -git clone --recursive https://git.eden-emu.dev/eden-emu/eden -``` -You usually want to add the `--recursive` parameter as it also takes care of the external dependencies for you. - -Now change into the eden directory and create a build directory there: -```sh -cd eden -mkdir build -``` - -Change into that build directory: -```sh -cd build -``` - -#### 1. Building in Release Mode (usually preferred and the most performant choice): -```sh -cmake .. -GNinja -DYUZU_TESTS=OFF -``` - -#### 2. Building in Release Mode with debugging symbols (useful if you want to debug errors for a eventual fix): -```sh -cmake .. -GNinja -DCMAKE_BUILD_TYPE=RelWithDebInfo -DYUZU_TESTS=ON -``` - -Build the emulator locally: -```sh -ninja -``` - -Optional: If you wish to install eden globally onto your system issue the following command: -```sh -sudo ninja install -``` -OR -```sh -doas -- ninja install -``` - -## OpenSSL -The available OpenSSL port (3.0.17) is out-of-date, and using a bundled static library instead is recommended; to do so, add `-DYUZU_USE_CPM=ON` to your CMake configure command. \ No newline at end of file diff --git a/docs/build/Linux.md b/docs/build/Linux.md deleted file mode 100644 index a6b7b2dda7..0000000000 --- a/docs/build/Linux.md +++ /dev/null @@ -1,138 +0,0 @@ -### Dependencies - -You'll need to download and install the following to build Eden: - - * [GCC](https://gcc.gnu.org/) v11+ (for C++20 support) & misc - * If GCC 12 is installed, [Clang](https://clang.llvm.org/) v14+ is required for compiling - * [CMake](https://www.cmake.org/) 3.22+ - -The following are handled by Eden's externals: - - * [FFmpeg](https://ffmpeg.org/) - * [SDL2](https://www.libsdl.org/download-2.0.php) 2.0.18+ - * [opus](https://opus-codec.org/downloads/) 1.3+ - -All other dependencies will be downloaded and built by [CPM](https://github.com/cpm-cmake/CPM.cmake/) if `YUZU_USE_CPM` is on, but will always use system dependencies if available: - - * [Boost](https://www.boost.org/users/download/) 1.79.0+ - * [Catch2](https://github.com/catchorg/Catch2) 2.13.7 - 2.13.9 - * [fmt](https://fmt.dev/) 8.0.1+ - * [lz4](http://www.lz4.org) 1.8+ - * [nlohmann_json](https://github.com/nlohmann/json) 3.8+ - * [OpenSSL](https://www.openssl.org/source/) 1.1.1+ - * [ZLIB](https://www.zlib.net/) 1.2+ - * [zstd](https://facebook.github.io/zstd/) 1.5+ - * [enet](http://enet.bespin.org/) 1.3+ - * [cubeb](https://github.com/mozilla/cubeb) - * [SimpleIni](https://github.com/brofield/simpleini) - -Certain other dependencies (httplib, jwt, sirit, etc.) will be fetched by CPM regardless. System packages *can* be used for these libraries but this is generally not recommended. - -Dependencies are listed here as commands that can be copied/pasted. Of course, they should be inspected before being run. - -- Arch / Manjaro: - - `sudo pacman -Syu --needed base-devel boost catch2 cmake enet ffmpeg fmt git glslang libzip lz4 mbedtls ninja nlohmann-json openssl opus qt6-base qt6-multimedia sdl2 zlib zstd zip unzip` - - Building with QT Web Engine requires `qt6-webengine` as well. - - Proper wayland support requires `qt6-wayland` - - GCC 11 or later is required. - -- Ubuntu / Linux Mint / Debian: - - `sudo apt-get install autoconf cmake g++ gcc git glslang-tools libasound2 libboost-context-dev libglu1-mesa-dev libhidapi-dev libpulse-dev libtool libudev-dev libxcb-icccm4 libxcb-image0 libxcb-keysyms1 libxcb-render-util0 libxcb-xinerama0 libxcb-xkb1 libxext-dev libxkbcommon-x11-0 mesa-common-dev nasm ninja-build qt6-base-private-dev libmbedtls-dev catch2 libfmt-dev liblz4-dev nlohmann-json3-dev libzstd-dev libssl-dev libavfilter-dev libavcodec-dev libswscale-dev pkg-config zlib1g-dev libva-dev libvdpau-dev` - - Ubuntu 22.04, Linux Mint 20, or Debian 12 or later is required. - - Users need to manually specify building with QT Web Engine enabled. This is done using the parameter `-DYUZU_USE_QT_WEB_ENGINE=ON` when running CMake. - - Users need to manually disable building SDL2 from externals if they intend to use the version provided by their system by adding the parameters `-DYUZU_USE_EXTERNAL_SDL2=OFF` - -```sh -git submodule update --init --recursive -cmake .. -GNinja -DCMAKE_C_COMPILER=gcc-11 -DCMAKE_CXX_COMPILER=g++-11 -``` - -- Fedora: - - `sudo dnf install autoconf ccache cmake fmt-devel gcc{,-c++} glslang hidapi-devel json-devel libtool libusb1-devel libzstd-devel lz4-devel nasm ninja-build openssl-devel pulseaudio-libs-devel qt6-linguist qt6-qtbase{-private,}-devel qt6-qtwebengine-devel qt6-qtmultimedia-devel speexdsp-devel wayland-devel zlib-devel ffmpeg-devel libXext-devel` - - Fedora 32 or later is required. - - Due to GCC 12, Fedora 36 or later users need to install `clang`, and configure CMake to use it via `-DCMAKE_CXX_COMPILER=clang++ -DCMAKE_C_COMPILER=clang` - - CMake arguments to force system libraries: - - SDL2: `-DYUZU_USE_BUNDLED_SDL2=OFF -DYUZU_USE_EXTERNAL_SDL2=OFF` - - FFmpeg: `-DYUZU_USE_EXTERNAL_FFMPEG=OFF` - - [RPM Fusion](https://rpmfusion.org/) (free) is required to install `ffmpeg-devel` - -### Cloning Eden with Git - -**Master:** - -```bash -git clone --recursive https://git.eden-emu.dev/eden-emu/eden -cd eden -``` - -The `--recursive` option automatically clones the required Git submodules. - -### Building Eden in Release Mode (Optimised) - -If you need to run ctests, you can disable `-DYUZU_TESTS=OFF` and install Catch2. - -```bash -mkdir build && cd build -cmake .. -GNinja -DYUZU_TESTS=OFF -ninja -sudo ninja install -``` -You may also want to include support for Discord Rich Presence by adding `-DUSE_DISCORD_PRESENCE=ON` after `cmake ..` - -`-DYUZU_USE_EXTERNAL_VULKAN_SPIRV_TOOLS=OFF` might be needed if ninja command failed with `undefined reference to symbol 'spvOptimizerOptionsCreate`, reason currently unknown - -Optionally, you can use `cmake-gui ..` to adjust various options (e.g. disable the Qt GUI). - -### Building Eden in Debug Mode (Slow) - -```bash -mkdir build && cd build -cmake .. -GNinja -DCMAKE_BUILD_TYPE=Debug -DYUZU_TESTS=OFF -ninja -``` - -### Building with debug symbols - -```bash -mkdir build && cd build -cmake .. -GNinja -DCMAKE_BUILD_TYPE=RelWithDebInfo -DYUZU -DYUZU_TESTS=OFF -ninja -``` - -### Building with Scripts -A convenience script for building is provided in `.ci/linux/build.sh`. You must provide an arch target for optimization, e.g. `.ci/linux/build.sh amd64`. Valid targets: -- `legacy`: x86_64 generic, only needed for CPUs older than 2013 or so -- `amd64`: x86_64-v3, for CPUs newer than 2013 or so -- `steamdeck` / `zen2`: For Steam Deck or Zen >= 2 AMD CPUs (untested on Intel) -- `rog-ally` / `allyx` / `zen4`: For ROG Ally X or Zen >= 4 AMD CPUs (untested on Intel) -- `aarch64`: For armv8-a CPUs, older than mid-2021 or so -- `armv9`: For armv9-a CPUs, newer than mid-2021 or so -- `native`: Optimize to your native host architecture - -Extra flags to pass to CMake should be passed after the arch target. - -Additional environment variables can be used to control building: -- `NPROC`: Number of threads to use for compilation (defaults to all) -- `TARGET`: Set to `appimage` to disable standalone `eden-cli` and `eden-room` executables -- `BUILD_TYPE`: Sets the build type to use. Defaults to `Release` - -The following environment variables are boolean flags. Set to `true` to enable or `false` to disable: -- `DEVEL` (default FALSE): Disable Qt update checker -- `USE_WEBENGINE` (default FALSE): Enable Qt WebEngine -- `USE_MULTIMEDIA` (default TRUE): Enable Qt Multimedia - -After building, an AppImage can be packaged via `.ci/linux/package.sh`. This script takes the same arch targets as the build script. If the build was created in a different directory, you can specify its path relative to the source directory, e.g. `.ci/linux/package.sh amd64 build-appimage`. Additionally, set the `DEVEL` environment variable to `true` to change the app name to `Eden Nightly`. - -### Running without installing - -After building, the binaries `eden` and `eden-cmd` (depending on your build options) will end up in `build/bin/`. - -```bash -# SDL -cd build/bin/ -./eden-cmd - -# Qt -cd build/bin/ -./eden -``` diff --git a/docs/build/OpenBSD.md b/docs/build/OpenBSD.md deleted file mode 100644 index 6a55fd269d..0000000000 --- a/docs/build/OpenBSD.md +++ /dev/null @@ -1,10 +0,0 @@ -# Building for OpenBSD - -```sh -pkg_add -u -pkg_add cmake nasm git boost unzip--iconv autoconf-2.72p0 bash ffmpeg glslang gmake llvm-19.1.7p3 qt6 jq -git --recursive https://git.eden-emu.dev/eden-emu/eden -cmake -DCMAKE_C_COMPILER=clang-19 -DCMAKE_CXX_COMPILER=clang++-19 -DDYNARMIC_USE_PRECOMPILED_HEADERS=OFF -DCMAKE_BUILD_TYPE=Debug -DENABLE_QT=OFF -DENABLE_OPENSSL=OFF -DENABLE_WEB_SERVICE=OFF -B /usr/obj/eden -``` - -- Modify `externals/ffmpeg/CMakeFiles/ffmpeg-build/build.make` to use `-j$(nproc)` instead of just `-j`. diff --git a/docs/build/Solaris.md b/docs/build/Solaris.md deleted file mode 100644 index f7174c2869..0000000000 --- a/docs/build/Solaris.md +++ /dev/null @@ -1,51 +0,0 @@ -# Building for Solaris - -## Dependencies. -Always consult [the OpenIndiana package list](https://pkg.openindiana.org/hipster/en/index.shtml) to cross-verify availability. - -Run the usual update + install of essential toolings: `sudo pkg update && sudo pkg install git cmake`. - -- **gcc**: `sudo pkg install developer/gcc-14`. -- **clang**: Version 20 is broken, use `sudo pkg install developer/clang-19`. - -Then install the libraies: `sudo pkg install qt6 boost glslang libzip library/lz4 nlohmann-json openssl opus sdl2 zlib compress/zstd unzip pkg-config nasm autoconf mesa library/libdrm header-drm developer/fmt`. - -### Building - -Clone eden with git `git clone --recursive https://git.eden-emu.dev/eden-emu/eden` - -```sh -# Needed for some dependencies that call cc directly (tz) -echo '#!/bin/sh' >cc -echo 'gcc $@' >>cc -chmod +x cc -export PATH="$PATH:$PWD" -``` - -Patch for FFmpeg: -```sh -sed -i 's/ make / gmake /' externals/ffmpeg/CMakeFiles/ffmpeg-build.dir/build.make -``` - -- **Configure**: `cmake -B build -DYUZU_USE_CPM=ON -DCMAKE_CXX_FLAGS="-I/usr/include/SDL2" -DCMAKE_C_FLAGS="-I/usr/include/SDL2"`. -- **Build**: `cmake --build build`. -- **Installing**: `sudo cmake --install build`. - -### Running - -Default Mesa is a bit outdated, the following environment variables should be set for a smoother experience: -```sh -export MESA_GL_VERSION_OVERRIDE=4.6 -export MESA_GLSL_VERSION_OVERRIDE=460 -export MESA_EXTENSION_MAX_YEAR=2025 -export MESA_DEBUG=1 -export MESA_VK_VERSION_OVERRIDE=1.3 -# Only if nvidia/intel drm drivers cause crashes, will severely hinder performance -export LIBGL_ALWAYS_SOFTWARE=1 -``` - -### Notes - -- Modify the generated ffmpeg.make (in build dir) if using multiple threads (base system `make` doesn't use `-j4`, so change for `gmake`). -- If using OpenIndiana, due to a bug in SDL2 cmake configuration; Audio driver defaults to SunOS ``, which does not exist on OpenIndiana. -- System OpenSSL generally does not work. Instead, use `-DYUZU_USE_CPM=ON` to use a bundled static OpenSSL, or build a system dependency from source. \ No newline at end of file diff --git a/docs/build/Windows.md b/docs/build/Windows.md deleted file mode 100644 index 76602e6d69..0000000000 --- a/docs/build/Windows.md +++ /dev/null @@ -1,259 +0,0 @@ -# ⚠️ This guide is for developers ONLY! Support will be provided to developers ONLY. - -## 📋 Current building methods: - -* [ Minimal Dependencies](#minimal-dependencies) -* [⚡ Method I: MSVC Build for Windows](#method-i-msvc-build-for-windows) -* [🐧 Method II: MinGW-w64 Build with MSYS2](#method-ii-mingw-w64-build-with-msys2) -* [🖥️ Method III: CLion Environment Setup](#method-iii-clion-environment-setup) -* [💻 Building from the command line with MSVC](#building-from-the-command-line-with-msvc) -* [📜 Building with Scripts](#building-with-scripts) - ---- - - -## Minimal Dependencies - -On Windows, **all** library dependencies are **automatically included** within the `externals` folder. - -You still need to install: - -* **[CMake](https://cmake.org/download/)** - Used to generate Visual Studio project files. -* **[Vulkan SDK](https://vulkan.lunarg.com/sdk/home#windows)** - Make sure to select **Latest SDK**. - - * *A convenience script to install the latest SDK is provided in `.ci/windows/install-vulkan-sdk.ps1`* -* **[Git for Windows](https://gitforwindows.org)** - We recommend installing Git for command line use and version control integration. - - - - * *While installing Git Bash, select "Git from the command line and also from 3rd-party software". If missed, manually set `git.exe` path in CMake.* - ---- - -## ⚡ Method I: MSVC Build for Windows - -### a. Prerequisites to MSVC Build - -* **[Visual Studio 2022 Community](https://visualstudio.microsoft.com/downloads/)** - Make sure to **select C++ support** in the installer, or **update to the latest version** if already installed. - - * *A convenience script to install the **minimal** version (Visual Build Tools) is provided in `.ci/windows/install-msvc.ps1`* - ---- - -### b. Clone the eden repository with Git - -Open Terminal on - -```cmd -git clone https://git.eden-emu.dev/eden-emu/eden -cd eden -``` - -* *By default `eden` downloads to `C:\Users\\eden`* - ---- - -### c. Building - -* Open the CMake GUI application and point it to the `eden` - - - -* For the build directory, use a `build/` subdirectory inside the source directory or some other directory of your choice. (Tell CMake to create it.) - -* Click the "Configure" button and choose `Visual Studio 17 2022`, with `x64` for the optional platform. - - - - * *(You may also want to disable `YUZU_TESTS` in this case since Catch2 is not yet supported with this.)* - - - -* Click "Generate" to create the project files. - - - -* Open the solution file `yuzu.sln` in Visual Studio 2022, which is located in the build folder. - - - -* * Depending on whether you want a graphical user interface or not, select in the Solution Explorer: - * `eden` (GUI) - * `eden-cmd` (command-line only) - * Then **right-click** and choose `Set as StartUp Project`. - - - - -* Select the appropriate build type, `Debug` for debug purposes or `Release` for performance (in case of doubt choose `Release`). - - - -* **Right-click** the project you want to build and press **Build** in the submenu or press `F5`. - - - ---- - -## 🐧 Method II: MinGW-w64 Build with MSYS2 - -### a. Prerequisites to MinGW-w64 - -* **[MSYS2](https://www.msys2.org)** - A versatile and up-to-date development environment for Windows, providing a Unix-like shell, package manager, and toolchain. - ---- - -### b. Install eden dependencies for MinGW-w64 - -* Open the `MSYS2 MinGW 64-bit` shell (`mingw64.exe`) -* Download and install all dependencies using: - * `pacman -Syu git make mingw-w64-x86_64-SDL2 mingw-w64-x86_64-cmake mingw-w64-x86_64-python-pip mingw-w64-x86_64-qt6 mingw-w64-x86_64-toolchain autoconf libtool automake-wrapper` -* Add MinGW binaries to the PATH: - * `echo 'PATH=/mingw64/bin:$PATH' >> ~/.bashrc` -* Add VulkanSDK to the PATH: - * `echo 'PATH=$(readlink -e /c/VulkanSDK/*/Bin/):$PATH' >> ~/.bashrc` - ---- - -### c. Clone the eden repository with Git - -```cmd -git clone https://git.eden-emu.dev/eden-emu/eden -cd eden -``` - ---- - -### d. Building dynamically-linked eden - -* This process will generate a *dynamically* linked build - -```bash -# Make build dir and enter -mkdir build && cd build - -# Generate CMake Makefiles -cmake .. -G "MSYS Makefiles" -DYUZU_TESTS=OFF - -# Build -make -j$(nproc) - -# Run eden! -./bin/eden.exe -``` - -* *Warning: This build is not a **static** build meaning that you **need** to include all of the DLLs with the .exe in order to use it or other systems!* - ---- - -### Additional notes - - -* Eden doesn't require the rather large Qt dependency, but you will lack a GUI frontend - -```bash -# ... - -# Generate CMake Makefiles (withou QT) -cmake .. -G "MSYS Makefiles" -DYUZU_TESTS=OFF -DENABLE_QT=no - -$ ... -``` - -* Running programs from inside `MSYS2 MinGW x64` shell has a different `%PATH%` than directly from explorer. - * This different `%PATH%` has the locations of the other DLLs required. - - - ---- - -## 🖥️ Method III: CLion Environment Setup - -### a. Prerequisites to CLion - -* [CLion](https://www.jetbrains.com/clion/) - This IDE is not free; for a free alternative, check Method I - ---- - -### b. Cloning eden with CLion - -* Clone the Repository: - - - - - ---- - -### c. Building & Setup - -* Once Cloned, You will be taken to a prompt like the image below: - - - -* Set the settings to the image below: -* Change `Build type: Release` -* Change `Name: Release` -* Change `Toolchain Visual Studio` -* Change `Generator: Let CMake decide` -* Change `Build directory: build` - - - -* Click OK; now Clion will build a directory and index your code to allow for IntelliSense. Please be patient. -* Once this process has been completed (No loading bar bottom right), you can now build eden -* In the top right, click on the drop-down menu, select all configurations, then select eden - - - -* Now run by clicking the play button or pressing Shift+F10, and eden will auto-launch once built. - - - ---- - -## 💻 Building from the command line with MSVC - -```cmd -# Clone eden and enter -git clone https://git.eden-emu.dev/eden-emu/eden -cd eden - -# Make build dir and enter -mkdir build && cd build - -# Generate CMake Makefiles -cmake .. -G "Visual Studio 17 2022" -A x64 -DYUZU_TESTS=OFF - -# Build -cmake --build . --config Release -``` - -## 📜 Building with Scripts - -* A convenience script for building is provided in `.ci/windows/build.sh`. -* You must run this with Bash, e.g. Git Bash or MinGW TTY. -* To use this script, you must have `windeployqt` installed (usually bundled with Qt) and set the `WINDEPLOYQT` environment variable to its canonical Bash location: - * `WINDEPLOYQT="/c/Qt/6.9.1/msvc2022_64/bin/windeployqt6.exe" .ci/windows/build.sh`. -* You can use `aqtinstall`, more info on and - - -* Extra CMake flags should be placed in the arguments of the script. - -#### Additional environment variables can be used to control building: - -* `BUILD_TYPE` (default `Release`): Sets the build type to use. - -* The following environment variables are boolean flags. Set to `true` to enable or `false` to disable: - - * `DEVEL` (default FALSE): Disable Qt update checker - * `USE_WEBENGINE` (default FALSE): Enable Qt WebEngine - * `USE_MULTIMEDIA` (default TRUE): Enable Qt Multimedia - * `BUNDLE_QT` (default FALSE): Use bundled Qt - - * Note that using **system Qt** requires you to include the Qt CMake directory in `CMAKE_PREFIX_PATH` - * `.ci/windows/build.sh -DCMAKE_PREFIX_PATH=C:/Qt/6.9.0/msvc2022_64/lib/cmake/Qt6` - -* After building, a zip can be packaged via `.ci/windows/package.sh`. You must have 7-zip installed and in your PATH. - * The resulting zip will be placed into `artifacts` in the source directory. - diff --git a/docs/build/macOS.md b/docs/build/macOS.md deleted file mode 100644 index fd1873b849..0000000000 --- a/docs/build/macOS.md +++ /dev/null @@ -1,78 +0,0 @@ -Please note this article is intended for development, and Eden on macOS is not currently ready for regular use. - -This article was written for developers. Eden support for macOS is not ready for casual use. - -## Dependencies -Install dependencies from Homebrew: -```sh -brew install autoconf automake boost ffmpeg fmt glslang hidapi libtool libusb lz4 ninja nlohmann-json openssl pkg-config qt@6 sdl2 speexdsp zlib zstd cmake Catch2 molten-vk vulkan-loader spirv-tools -``` - -If you are compiling on Intel Mac, or are using a Rosetta Homebrew installation, you must replace all references of `/opt/homebrew` with `/usr/local`. - -Now, clone the repo: -```sh -git clone --recursive https://git.eden-emu.dev/eden-emu/eden -cd eden -``` - -## Method I: ninja - ---- -Build for release -```sh -export Qt6_DIR="/opt/homebrew/opt/qt@6/lib/cmake" -export LIBVULKAN_PATH=/opt/homebrew/lib/libvulkan.dylib -cmake -B build -GNinja -DCMAKE_BUILD_TYPE=RelWithDebInfo -DYUZU_TESTS=OFF -DENABLE_WEB_SERVICE=ON -DENABLE_LIBUSB=OFF -DCLANG_FORMAT=ON -DSDL2_DISABLE_INSTALL=ON -DSDL_ALTIVEC=ON -ninja -``` - -You may also want to include support for Discord Rich Presence by adding `-DUSE_DISCORD_PRESENCE=ON` -```sh -export Qt6_DIR="/opt/homebrew/opt/qt@6/lib/cmake" -cmake -B build -GNinja -DCMAKE_BUILD_TYPE=RelWithDebInfo -DYUZU_TESTS=OFF -DENABLE_WEB_SERVICE=OFF -DENABLE_LIBUSB=OFF -ninja -``` - -Run the output: -``` -bin/eden.app/Contents/MacOS/eden -``` - -## Method II: Xcode - ---- -Build for release -```sh -export Qt6_DIR="/opt/homebrew/opt/qt@6/lib/cmake" -export LIBVULKAN_PATH=/opt/homebrew/lib/libvulkan.dylib -# Only if having errors about Xcode 15.0 -sudo /usr/bin/xcode-select --switch /Users/admin/Downloads/Xcode.ap -cmake -B build -GXcode -DCMAKE_BUILD_TYPE=RelWithDebInfo -DYUZU_TESTS=OFF -DENABLE_WEB_SERVICE=ON -DENABLE_LIBUSB=OFF -DCLANG_FORMAT=ON -DSDL2_DISABLE_INSTALL=ON -DSDL_ALTIVEC=ON -xcodebuild build -project yuzu.xcodeproj -scheme "yuzu" -configuration "RelWithDebInfo" -``` - -Build with debug symbols: -```sh -export Qt6_DIR="/opt/homebrew/opt/qt@6/lib/cmake" -cmake -B build -GNinja -DCMAKE_BUILD_TYPE=RelWithDebInfo -DYUZU_TESTS=OFF -DENABLE_WEB_SERVICE=OFF -DENABLE_LIBUSB=OFF -ninja -``` - -Run the output: -``` -bin/eden.app/Contents/MacOS/eden -``` - ---- - -To run with MoltenVK, install additional dependencies: -```sh -brew install molten-vk vulkan-loader -``` - -Run with Vulkan loader path: -```sh -export LIBVULKAN_PATH=/opt/homebrew/lib/libvulkan.dylib -bin/eden.app/Contents/MacOS/eden -``` diff --git a/docs/img/creator-1.png b/docs/img/creator-1.png new file mode 100644 index 0000000000000000000000000000000000000000..14d679cfbb6dabd5bcf7300395aa4d22ae2b1d62 GIT binary patch literal 64671 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μ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%0Ay*v1tt;;D^9?ybazI(tsNvoIkg)qOU2gHM0m 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 literal 0 HcmV?d00001 diff --git a/docs/scripts/Linux.md b/docs/scripts/Linux.md new file mode 100644 index 0000000000..8b18e41ced --- /dev/null +++ b/docs/scripts/Linux.md @@ -0,0 +1,31 @@ +# Linux Build Scripts + +* Provided script: `.ci/linux/build.sh` +* Must specify arch target, e.g.: `.ci/linux/build.sh amd64` +* Valid targets: + * `native`: Optimize to your native host architecture + * `legacy`: x86\_64 generic, only needed for CPUs older than 2013 or so + * `amd64`: x86\_64-v3, for CPUs newer than 2013 or so + * `steamdeck` / `zen2`: For Steam Deck or Zen >= 2 AMD CPUs (untested on Intel) + * `rog-ally` / `allyx` / `zen4`: For ROG Ally X or Zen >= 4 AMD CPUs (untested on Intel) + * `aarch64`: For armv8-a CPUs, older than mid-2021 or so + * `armv9`: For armv9-a CPUs, newer than mid-2021 or so +* Extra CMake flags go after the arch target. + +### Environment Variables + +* `NPROC`: Number of compilation threads (default: all cores) +* `TARGET`: Set `appimage` to disable standalone `eden-cli` and `eden-room` +* `BUILD_TYPE`: Build type (default: `Release`) + +Boolean flags (set `true` to enable, `false` to disable): + +* `DEVEL` (default `FALSE`): Disable Qt update checker +* `USE_WEBENGINE` (default `FALSE`): Enable Qt WebEngine +* `USE_MULTIMEDIA` (default `FALSE`): Enable Qt Multimedia + +* AppImage packaging script: `.ci/linux/package.sh` + + * Accepts same arch targets as build script + * Use `DEVEL=true` to rename app to `Eden Nightly` + * This should generally not be used unless in a tailor-made packaging environment (e.g. Actions/CI) \ No newline at end of file diff --git a/docs/scripts/Windows.md b/docs/scripts/Windows.md new file mode 100644 index 0000000000..e60c2119a2 --- /dev/null +++ b/docs/scripts/Windows.md @@ -0,0 +1,29 @@ +# Windows Build Scripts + +* A convenience script for building is provided in `.ci/windows/build.sh`. +* You must run this with Bash, e.g. Git Bash or the MinGW TTY. +* To use this script, you must have `windeployqt` installed (usually bundled with Qt) and set the `WINDEPLOYQT` environment variable to its canonical Bash location: + * `WINDEPLOYQT="/c/Qt/6.9.1/msvc2022_64/bin/windeployqt6.exe" .ci/windows/build.sh`. +* You can use `aqtinstall`, more info on and + + +* Extra CMake flags should be placed in the arguments of the script. + +#### Additional environment variables can be used to control building: + +* `BUILD_TYPE` (default `Release`): Sets the build type to use. + +* The following environment variables are boolean flags. Set to `true` to enable or `false` to disable: + + * `DEVEL` (default FALSE): Disable Qt update checker + * `USE_WEBENGINE` (default FALSE): Enable Qt WebEngine + * `USE_MULTIMEDIA` (default FALSE): Enable Qt Multimedia + * `BUNDLE_QT` (default FALSE): Use bundled Qt + + * Note that using **system Qt** requires you to include the Qt CMake directory in `CMAKE_PREFIX_PATH` + * `.ci/windows/build.sh -DCMAKE_PREFIX_PATH=C:/Qt/6.9.0/msvc2022_64/lib/cmake/Qt6` + +* After building, a zip can be packaged via `.ci/windows/package.sh`. You must have 7-zip installed and in your PATH. + * The resulting zip will be placed into `artifacts` in the source directory. + + From b1ce3c8dc1259afe8e0a90d8393591f2d33738b7 Mon Sep 17 00:00:00 2001 From: crueter Date: Thu, 18 Sep 2025 02:38:15 +0200 Subject: [PATCH 4/6] [docs] CODEOWNERS (#491) cc: @CamilleLaVey @Lizzie initial guess for everyone, may need more specificity etc. Signed-off-by: crueter Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/491 Reviewed-by: Shinmegumi --- docs/CODEOWNERS | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 docs/CODEOWNERS diff --git a/docs/CODEOWNERS b/docs/CODEOWNERS new file mode 100644 index 0000000000..503f9ec1fe --- /dev/null +++ b/docs/CODEOWNERS @@ -0,0 +1,26 @@ +# ui stuff +/src/android @AleksandrPopovich @nyxynx @Producdevity +/src/yuzu @crueter +/src/eden @crueter +/src/frontend_common @crueter +/src/qt_common @crueter + +# docs, meta +/docs @Lizzie @crueter +/.ci @crueter + +# cmake +*.cmake @crueter +*/CMakeLists.txt @crueter +*.in @crueter + +# individual stuff +/src/web_service @AleksandrPopovich +/src/dynarmic @Lizzie +/src/core @Lizzie @Maufeat @PavelBARABANOV @MrPurple666 +/src/core/hle @Maufeat @PavelBARABANOV @SDK-Chan +/src/*_room @AleksandrPopovich +/src/video_core @CamilleLaVey @MaranBr @Wildcard @weakboson + +# Global owners/triage +* @CamilleLaVey @Maufeat @crueter @MrPurple666 @MaranBr @Lizzie \ No newline at end of file From eda0a76ceccf952535de9995d4c71244f875ac32 Mon Sep 17 00:00:00 2001 From: SDK Chan Date: Wed, 17 Sep 2025 15:12:29 +0000 Subject: [PATCH 5/6] [gpu/nvdrv] Unstub SetErrorNotifier, add PostErrorNotification function --- .../hle/service/nvdrv/devices/nvhost_gpu.cpp | 120 +++++++++++++++++- .../hle/service/nvdrv/devices/nvhost_gpu.h | 12 ++ 2 files changed, 127 insertions(+), 5 deletions(-) diff --git a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp index 5f754650d9..87012246fa 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp @@ -8,6 +8,7 @@ #include "common/assert.h" #include "common/logging/log.h" #include "core/core.h" +#include "core/hle/kernel/k_event.h" #include "core/hle/kernel/k_process.h" #include "core/hle/service/nvdrv/core/container.h" #include "core/hle/service/nvdrv/core/nvmap.h" @@ -161,11 +162,115 @@ NvResult nvhost_gpu::ZCullBind(IoctlZCullBind& params) { } NvResult nvhost_gpu::SetErrorNotifier(IoctlSetErrorNotifier& params) { - LOG_WARNING(Service_NVDRV, "(STUBBED) called, offset={:X}, size={:X}, mem={:X}", params.offset, - params.size, params.mem); + LOG_DEBUG(Service_NVDRV, "called, offset={:#X}, size={:#X}, mem={:#X}", + params.offset, params.size, params.mem); + + if (!params.mem || !params.size) { + std::scoped_lock lk(channel_mutex); + if (!channel_state->initialized) { + LOG_CRITICAL(Service_NVDRV, "No address space bound for setting up error notifier!"); + return NvResult::NotInitialized; + } + + error_notifier_params = {}; + LOG_DEBUG(Service_NVDRV, "Error notifier disabled!"); + return NvResult::Success; + } + + constexpr u64 error_notification_size = sizeof(IoctlGetErrorNotification); + if (params.size < error_notification_size) { + LOG_ERROR(Service_NVDRV, "Error notification size: {:#X} is too small. Need at least {:#X}", params.size, + error_notification_size); + return NvResult::InvalidSize; + } + + auto handle = nvmap.GetHandle(static_cast(params.mem)); + if (!handle) { + LOG_ERROR(Service_NVDRV, "Unknown nvmap handle id {:#X}", params.mem); + return NvResult::BadParameter; + } + + + if (params.offset > handle->size || params.size > (handle->size - params.offset)) { + LOG_ERROR(Service_NVDRV, "Error notifier out of bounds: offset={:#X} size={:#X} handle size={:#X}", params.offset, + params.size, handle->size); + return NvResult::InvalidSize; + } + + u64 write_address, write_offset, handle_id; + { + std::scoped_lock lk(channel_mutex); + if (!channel_state->initialized) { + LOG_CRITICAL(Service_NVDRV, "No address space bound for setting up error notifier!"); + return NvResult::NotInitialized; + } + + error_notifier_params = params; + write_address = handle->address; + write_offset = params.offset; + handle_id = handle->id; + } + + if (write_address) { + IoctlGetErrorNotification error_notification{}; + error_notification.status = static_cast(NotifierStatus::NoError); + system.ApplicationMemory().WriteBlock(write_address + write_offset, &error_notification, sizeof(error_notification)); + } else { + LOG_WARNING(Service_NVDRV, + "nvmap handle id {:#X} has no virtual address assigned; " + "skipping initialization write for error notification!", + handle_id); + } + return NvResult::Success; } +void nvhost_gpu::PostErrorNotification(u32 info32, u16 info16, NotifierStatus status) { + IoctlSetErrorNotifier error_notifier_params_snapshot{}; + Kernel::KEvent *error_notifier_event_snapshot{}; + { + std::scoped_lock lk(channel_mutex); + error_notifier_params_snapshot = error_notifier_params; + error_notifier_event_snapshot = error_notifier_event; + } + + if (!error_notifier_params_snapshot.mem || error_notifier_params_snapshot.size < sizeof(IoctlGetErrorNotification)) { + LOG_DEBUG(Service_NVDRV, "PostErrorNotification: notifier not configured or too small!"); + return; + } + + auto handle = nvmap.GetHandle(static_cast(error_notifier_params_snapshot.mem)); + if (!handle || !handle->address) { + LOG_ERROR(Service_NVDRV, "PostErrorNotification: invalid handle or virtual address!"); + return; + } + + IoctlGetErrorNotification error_init{}; + error_init.info32 = info32; + error_init.info16 = info16; + error_init.status = static_cast(status); + const u64 write_size = std::min(sizeof(IoctlGetErrorNotification), + error_notifier_params_snapshot.size); + if (error_notifier_params_snapshot.offset >= handle->size || + write_size > (handle->size - error_notifier_params_snapshot.offset)) { + LOG_ERROR(Service_NVDRV, "PostErrorNotification: bounds check failed!"); + return; + } + + const u64 virtual_address = handle->address + error_notifier_params_snapshot.offset; + if (virtual_address < handle->address) { + LOG_ERROR(Service_NVDRV, "PostErrorNotification: virtual address overflow!"); + return; + } + + auto &application_memory = system.ApplicationMemory(); + application_memory.WriteBlock(virtual_address, &error_init, write_size); + + if (error_notifier_event_snapshot) { + error_notifier_event_snapshot->Signal(); + } +} + NvResult nvhost_gpu::SetChannelPriority(IoctlChannelSetPriority& params) { channel_priority = params.priority; LOG_DEBUG(Service_NVDRV, "(STUBBED) called, priority={:X}", channel_priority); @@ -251,7 +356,7 @@ NvResult nvhost_gpu::AllocateObjectContext(IoctlAllocObjCtx& params) { params.flags = allowed_mask; } - s32_le ctx_class_number_index = + s32_le ctx_class_number_index = GetObjectContextClassNumberIndex(static_cast(params.class_num)); if (ctx_class_number_index < 0) { LOG_ERROR(Service_NVDRV, "Invalid class number for object context: {:#X}", @@ -324,6 +429,7 @@ NvResult nvhost_gpu::SubmitGPFIFOImpl(IoctlSubmitGpfifo& params, Tegra::CommandL if (flags.fence_wait.Value()) { if (flags.increment_value.Value()) { + PostErrorNotification(flags.raw, 0, NotifierStatus::GenericError); return NvResult::BadParameter; } @@ -357,7 +463,11 @@ NvResult nvhost_gpu::SubmitGPFIFOImpl(IoctlSubmitGpfifo& params, Tegra::CommandL NvResult nvhost_gpu::SubmitGPFIFOBase1(IoctlSubmitGpfifo& params, std::span commands, bool kickoff) { if (params.num_entries > commands.size()) { - UNIMPLEMENTED(); + LOG_ERROR(Service_NVDRV, + "SubmitGPFIFO: num_entries={:#X} > provided commands={:#X}", + params.num_entries, commands.size()); + + PostErrorNotification(params.num_entries, 0, NotifierStatus::BadGpfifo); return NvResult::InvalidSize; } @@ -376,7 +486,7 @@ NvResult nvhost_gpu::SubmitGPFIFOBase1(IoctlSubmitGpfifo& params, NvResult nvhost_gpu::SubmitGPFIFOBase2(IoctlSubmitGpfifo& params, std::span commands) { if (params.num_entries > commands.size()) { - UNIMPLEMENTED(); + PostErrorNotification(params.num_entries, 0, NotifierStatus::BadGpfifo); return NvResult::InvalidSize; } diff --git a/src/core/hle/service/nvdrv/devices/nvhost_gpu.h b/src/core/hle/service/nvdrv/devices/nvhost_gpu.h index fb0a5be959..16c4a95474 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_gpu.h +++ b/src/core/hle/service/nvdrv/devices/nvhost_gpu.h @@ -66,6 +66,16 @@ private: CtxChannelGPFIFO = 0xB06F, }; + enum class NotifierStatus : u16_le { + NoError = 0xFFFF, + GenericError = 0x0001, + MmuFault = 0x0002, + IllegalMethod= 0x0003, + InvalidObject= 0x0004, + BadGpfifo = 0x0005, + TimeoutHang = 0x0006, + }; + struct IoctlSetNvmapFD { s32_le nvmap_fd{}; }; @@ -172,6 +182,8 @@ private: s32_le nvmap_fd{}; u64_le user_data{}; IoctlZCullBind zcull_params{}; + IoctlSetErrorNotifier error_notifier_params{}; + void PostErrorNotification(u32 info32, u16 info16, NotifierStatus status); std::array, 6> ctxObjs{}; u32_le channel_priority{}; u32_le channel_timeslice{}; From 8546613823104c19318c24cf2e5bde0419039a19 Mon Sep 17 00:00:00 2001 From: SDK Chan Date: Wed, 17 Sep 2025 15:16:04 +0000 Subject: [PATCH 6/6] [gpu/nvdrv] Remove redundant whitespace --- src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp index 87012246fa..eb209bf599 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp @@ -190,7 +190,6 @@ NvResult nvhost_gpu::SetErrorNotifier(IoctlSetErrorNotifier& params) { return NvResult::BadParameter; } - if (params.offset > handle->size || params.size > (handle->size - params.offset)) { LOG_ERROR(Service_NVDRV, "Error notifier out of bounds: offset={:#X} size={:#X} handle size={:#X}", params.offset, params.size, handle->size);