Compare commits

..

39 Commits

Author SHA1 Message Date
lizzie b02db21e45 evil ping 2026-08-15 06:03:49 +00:00
lizzie 7dde5a4b3e don't murder me for this change 2026-08-15 06:01:36 +00:00
lizzie 5de66b2a53 need the extra threads... 2026-08-15 04:45:44 +00:00
lizzie 86b31fcad9 more gymnastics to give fake results? 2026-08-15 01:25:58 +00:00
lizzie ecc28bd9c3 first fake icmp impl 2026-08-15 01:06:19 +00:00
lizzie a4e1c4b20e fix missing type 2026-08-14 23:07:21 +00:00
lizzie 71aedd5f74 fix ldn sockets being regarded as normal socks 2026-08-14 16:42:44 +00:00
lizzie 943b42209e fx2 2026-08-14 16:42:44 +00:00
lizzie e786bc8a51 fix??? 2026-08-14 16:42:44 +00:00
lizzie 8513d3da7c fix sockets 2026-08-14 16:42:44 +00:00
lizzie 0330b90f1a struct pollfd 2026-08-14 16:42:44 +00:00
lizzie f2dfdf6970 fixup tests 2026-08-14 16:42:44 +00:00
lizzie d73bc5ba9e fix windows screaming 2026-08-14 16:42:44 +00:00
lizzie 7fd81bb9f4 musl scared of defines ooo 2026-08-14 16:42:44 +00:00
lizzie 3f24787575 airplane mode takes priority 2026-08-14 16:42:44 +00:00
lizzie a8911d61ce uh 2026-08-14 16:42:44 +00:00
lizzie 2373027b79 add tcp options 2026-08-14 16:42:44 +00:00
lizzie e7d8c3da71 nuke extra threads, extra error support; use span instead of vector for poll fds 2026-08-14 16:42:44 +00:00
lizzie 36fcd1e350 we love when cURL has bugs? 2026-08-14 16:42:44 +00:00
lizzie 38bcfe6781 add extra polling types 2026-08-14 16:42:44 +00:00
lizzie edefb4367f various fixes to sockopt 2026-08-14 16:42:44 +00:00
lizzie 4a0ca0d022 allow configure socket level 2026-08-14 16:42:44 +00:00
lizzie 7bd6f4cd77 coalesce the various sockopt 2026-08-14 16:42:44 +00:00
lizzie 6ce07fb0da honour the provided MSG flags 2026-08-14 16:42:44 +00:00
lizzie 7266d683aa extra fixups 2026-08-14 16:42:44 +00:00
lizzie 2576cbc6d7 fixup stuffs for windows 2026-08-14 16:42:44 +00:00
lizzie 1e315dc34b fix messages native flags 2026-08-14 16:42:44 +00:00
lizzie 118e2f441d properly handle writebuffer and dont write OOB 2026-08-14 16:42:44 +00:00
lizzie 4e012d9d56 fixes for non BSD 2026-08-14 16:42:44 +00:00
lizzie 018039eb3b [net] refactor to remove uneeded abstraction layer
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2026-08-14 16:42:44 +00:00
lizzie e7a931d06b [eden-cli] rework loop to use new SDL3 event dispatch system (#4108)
modernized into the new SDL3 API for event dispatching.

also stopped using the periodic comparison and just added a SDL timer like it wants us to do
is it better than before? well it's certainly more portable now. docs say this improves battery usage on busywaits for event polling.

Signed-off-by: lizzie <lizzie@eden-emu.dev>

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4108
Reviewed-by: Maufeat <sahyno1996@gmail.com>
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
2026-08-12 18:10:24 +02:00
crueter 8648c27cbb [cmake] Simplify DetectPlatform and normalize platform names (#4183)
Since 3.25 introduced `LINUX` and `BSD`, use them, and standardize other platforms to that style

Signed-off-by: crueter <crueter@eden-emu.dev>
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4183
Reviewed-by: Samuel <lizzie@eden-emu.dev>
Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
2026-08-10 04:57:38 +02:00
codeman4033 def76946f0 [forgejo] Replace mentions of yuzu in issue templates with eden (#3842)
Also fix the broken link for log files in the bug report

Signed-off-by: codeman4033 <codeman4033@eden-emu.dev>

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3842
Reviewed-by: crueter <crueter@eden-emu.dev>
Reviewed-by: Samuel <lizzie@eden-emu.dev>
2026-08-10 03:55:03 +02:00
BoiledElectricity 13d39f39aa [qt] fix relative ROM paths passed on the CLI on macOS (#4105)
On macOS, convert relative paths to absolute paths before changing the
working directory. Otherwise, valid Switch formats may incorrectly fail
with a "The ROM format is not supported" error.

Fixes: https://github.com/eden-emulator/Issue-Reports/issues/404
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4105
Reviewed-by: crueter <crueter@eden-emu.dev>
Reviewed-by: Samuel <lizzie@eden-emu.dev>
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
2026-08-09 17:18:58 +02:00
lizzie 7731b5bc86 [core/hle/kernel/svc] demote ResetSignal log Debug->Trace (#4161)
I don't see why it's LOG_DEBUG if WaitSynchronization is LOG_TRACE, leaves incomplete data
may as well bundle it to trace with the others? :)

Signed-off-by: lizzie <lizzie@eden-emu.dev>

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4161
Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
Reviewed-by: Maufeat <sahyno1996@gmail.com>
2026-08-09 14:13:27 +02:00
lizzie c4ab2f6b9e [cmake] Add ENABLE_WERROR option (#4080)
Should warnings be errors? that's a philosophical debate which I can only answer with "no, but if you think otherwise here is an option for you"

Signed-off-by: lizzie <lizzie@eden-emu.dev>

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4080
Reviewed-by: crueter <crueter@eden-emu.dev>
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
2026-08-09 02:56:28 +02:00
xbzk dd12266c26 [input] added option to disable wgi/xinput to prevent SDL GUIDE hack (#4237)
- [x] I have read and followed the [Contribution Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/CONTRIBUTING.md#code-contributions).
- [x] I have read and followed the [AI Policy](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/AI.md)
- [x] I have read and followed the [Coding Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/Coding.md) to the best of my ability.

-------------------
So, many users hate the fact when they press START+SELECT, an additional HOME event is sent.

That is annoying coz for most users' default hotkey settings, it closes eden when not in game, and when in game it calls a NOT controller responsive prompt about leaving Eden.
There are several games with reasons to press start+select, also there are homebrews which relies on that combo to open internal menus.
Again. That is annoying.

Digging deep i've found that SDL implements something called guide hack: When Start+Select is pressed it sends a synthetic GUIDE(which code equals to HOME).
This was intended for controllers missing the HOME button, but SDL fails to track them all, and misses API to disabling it.

I've figured out a patch to SDL to disable it, but since it would be better to invest on a PR straight into SDL repo, on our side i thought it better to simply add an option to disable WGI (Windows.Gaming.Input) and Xinput, both paths which could lead into the hack enabling.

Setting is available in a remote place no one will bother.

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4237
Reviewed-by: Samuel <lizzie@eden-emu.dev>
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
2026-08-09 02:53:37 +02:00
crueter a3d32a18b8 [meta] Discourage profanity usage (#4239)
I'm not into millennial humor, personally.

Proactively removed as much profanity as I could grep.

Signed-off-by: crueter <crueter@eden-emu.dev>
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4239
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
Reviewed-by: Samuel <lizzie@eden-emu.dev>
2026-08-09 02:53:16 +02:00
crueter c0ffc900cd [ci] Only update translations once a week (#4256)
They've slowed down enough at this point.

Signed-off-by: crueter <crueter@eden-emu.dev>
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4256
2026-08-08 05:46:52 +02:00
112 changed files with 1985 additions and 3602 deletions
+2 -2
View File
@@ -1,9 +1,9 @@
name: tx-pull
on:
# tuesday, saturday at 2pm
# monday at 4pm
schedule:
- cron: '0 14 * * 2,6'
- cron: '0 16 * * 1'
workflow_dispatch:
jobs:
+2 -2
View File
@@ -3,7 +3,7 @@ description: File a bug report
body:
- type: markdown
attributes:
value: Tech support does not belong here. You should only file an issue here if you think you have experienced an actual bug with yuzu.
value: Tech support does not belong here. You should only file an issue here if you think you have experienced an actual bug with Eden.
- type: checkboxes
attributes:
label: Is there an existing issue for this?
@@ -43,7 +43,7 @@ body:
id: log
attributes:
label: Log File
description: A log file will help our developers to better diagnose and fix the issue. Instructions can be found [here](https://yuzu-emu.org/help/reference/log-files).
description: A log file will help our developers to better diagnose and fix the issue. Instructions can be found [here](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/user/HowToAccessLogs.md).
validations:
required: true
- type: textarea
+2 -2
View File
@@ -4,7 +4,7 @@ labels: "request"
body:
- type: markdown
attributes:
value: Tech support does not belong here. You should only file an issue here if you are requesting a feature you believe would make yuzu better.
value: Tech support does not belong here. You should only file an issue here if you are requesting a feature you believe would make Eden better.
- type: checkboxes
attributes:
label: Is there an existing issue for this?
@@ -23,6 +23,6 @@ body:
id: why-feature
attributes:
label: Why would this feature be useful?
description: A brief description of why this feature would make yuzu better.
description: A brief description of why this feature would make Eden better.
validations:
required: true
+29 -15
View File
@@ -28,23 +28,23 @@ if (NOT DEFINED ARCHITECTURE)
endif()
# Needed for FFmpeg w/ VAAPI and DRM
if (PLATFORM_OPENBSD)
if (OPENBSD)
# OpenBSD 7.8 broke libcxx when upgrading, so we must define the PSTL backend manually
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -I${CMAKE_SYSROOT}/usr/X11R6/include -D_LIBCPP_PSTL_BACKEND_SERIAL=1")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -I${CMAKE_SYSROOT}/usr/X11R6/include -D_LIBCPP_PSTL_BACKEND_SERIAL=1")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -L${CMAKE_SYSROOT}/usr/X11R6/lib")
elseif (PLATFORM_NETBSD)
elseif (NETBSD)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -I${CMAKE_SYSROOT}/usr/X11R7/include -I${CMAKE_SYSROOT}/usr/pkg/include/c++/v1")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -I${CMAKE_SYSROOT}/usr/X11R7/include -I${CMAKE_SYSROOT}/usr/pkg/include/c++/v1")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -L${CMAKE_SYSROOT}/usr/X11R7/lib")
endif()
# NetBSD: Fun for the whole family!
if (PLATFORM_NETBSD)
if (NETBSD)
set(ENV{PKG_CONFIG_PATH} "${PKG_CONFIG_PATH}:${CMAKE_SYSROOT}/usr/pkg/lib/ffmpeg7/pkgconfig")
endif()
cmake_dependent_option(YUZU_STATIC_ROOM "Build a static room executable only (CI only)" OFF "PLATFORM_LINUX" OFF)
cmake_dependent_option(YUZU_STATIC_ROOM "Build a static room executable only (CI only)" OFF "LINUX" OFF)
if (YUZU_STATIC_ROOM)
set(YUZU_ROOM ON)
set(YUZU_ROOM_STANDALONE ON)
@@ -79,6 +79,7 @@ set(YUZU_QT_MIRROR "" CACHE STRING "What mirror to use for downloading the bundl
cmake_dependent_option(YUZU_USE_BUNDLED_QT "Download bundled Qt binaries" "${MSVC}" "ENABLE_QT" OFF)
option(ENABLE_DEBUG_TOOLS "Enable debugging tools (maxwell disassembler, SPIRV translator, etc)" OFF)
option(ENABLE_WERROR "Enable -Werror diagnostics" ON)
# non-linux bundled qt are static
if (YUZU_USE_BUNDLED_QT AND (APPLE OR NOT UNIX))
@@ -157,6 +158,19 @@ if (CXX_CLANG_CL)
$<$<COMPILE_LANGUAGE:C,CXX>:-Wno-reserved-identifier>
$<$<COMPILE_LANGUAGE:C,CXX>:-Wno-deprecated-declarations>
$<$<COMPILE_LANGUAGE:C,CXX>:-Wno-cast-function-type-mismatch>
$<$<COMPILE_LANGUAGE:C,CXX>:-Wno-c99-extensions>
$<$<COMPILE_LANGUAGE:C,CXX>:-Wno-c++17-compat>
$<$<COMPILE_LANGUAGE:C,CXX>:-Wno-c++11-compat-reserved-user-defined-literal>
$<$<COMPILE_LANGUAGE:C,CXX>:-Wno-c++11-compat-deprecated-writable-strings>
$<$<COMPILE_LANGUAGE:C,CXX>:-Wno-c++11-compat-pedantic>
$<$<COMPILE_LANGUAGE:C,CXX>:-Wno-c++11-compat>
$<$<COMPILE_LANGUAGE:C,CXX>:-Wno-c++0x-compat>
$<$<COMPILE_LANGUAGE:C,CXX>:-Wno-c++98-c++11-compat-binary-literal>
$<$<COMPILE_LANGUAGE:C,CXX>:-Wno-c++98-compat-pedantic>
$<$<COMPILE_LANGUAGE:C,CXX>:-Wno-c++98-compat>
$<$<COMPILE_LANGUAGE:C,CXX>:-Wno-c99-compat>
$<$<COMPILE_LANGUAGE:C,CXX>:-Wno-c98-compat>
$<$<COMPILE_LANGUAGE:C,CXX>:-Wno-c99-extensions>
$<$<COMPILE_LANGUAGE:C,CXX>:/EHsc>)
# REQUIRED CPU features IN Windows-amd64
if (ARCHITECTURE_x86_64)
@@ -210,7 +224,7 @@ endif()
# ffmpeg
option(YUZU_USE_BUNDLED_FFMPEG "Download bundled FFmpeg" ${EXT_DEFAULT})
cmake_dependent_option(YUZU_USE_EXTERNAL_FFMPEG "Build FFmpeg from external source" "${PLATFORM_SUN}" "NOT WIN32 AND NOT ANDROID" OFF)
cmake_dependent_option(YUZU_USE_EXTERNAL_FFMPEG "Build FFmpeg from external source" "${SOLARIS}" "NOT WIN32 AND NOT ANDROID" OFF)
# sirit
set(BUNDLED_SIRIT_DEFAULT OFF)
@@ -221,7 +235,7 @@ endif()
option(YUZU_USE_BUNDLED_SIRIT "Download bundled sirit" ${BUNDLED_SIRIT_DEFAULT})
# FreeBSD 15+ has libusb, versions below should disable it
cmake_dependent_option(ENABLE_LIBUSB "Enable the use of LibUSB" ON "WIN32 OR PLATFORM_LINUX OR PLATFORM_FREEBSD OR APPLE" OFF)
cmake_dependent_option(ENABLE_LIBUSB "Enable the use of LibUSB" ON "WIN32 OR LINUX OR FREEBSD OR APPLE" OFF)
cmake_dependent_option(ENABLE_OPENGL "Enable OpenGL" ON "NOT (WIN32 AND ARCHITECTURE_arm64) AND NOT APPLE" OFF)
mark_as_advanced(FORCE ENABLE_OPENGL)
@@ -235,7 +249,7 @@ option(YUZU_TESTS "Compile tests" "${BUILD_TESTING}")
# Install udev rules on Linux (mainly for gyros)
# Only acts on joysticks and nothing else.
cmake_dependent_option(YUZU_INSTALL_UDEV_RULES "Install udev rules for gyro access" OFF "PLATFORM_LINUX" OFF)
cmake_dependent_option(YUZU_INSTALL_UDEV_RULES "Install udev rules for gyro access" OFF "LINUX" OFF)
option(YUZU_DOWNLOAD_ANDROID_VVL "Download validation layer binary for android" ON)
@@ -248,7 +262,7 @@ cmake_dependent_option(YUZU_ROOM_STANDALONE "Enable standalone room executable"
cmake_dependent_option(YUZU_CMD "Compile the eden-cli executable" ON "NOT ANDROID" OFF)
cmake_dependent_option(YUZU_CRASH_DUMPS "Compile crash dump (Minidump) support" OFF "WIN32 OR PLATFORM_LINUX" OFF)
cmake_dependent_option(YUZU_CRASH_DUMPS "Compile crash dump (Minidump) support" OFF "WIN32 OR LINUX" OFF)
option(YUZU_DOWNLOAD_TIME_ZONE_DATA "Always download time zone binaries" ON)
set(YUZU_TZDB_PATH "" CACHE STRING "Path to a pre-downloaded timezone database")
@@ -258,7 +272,7 @@ cmake_dependent_option(YUZU_USE_BUNDLED_MOLTENVK "Download bundled MoltenVK lib"
option(YUZU_DISABLE_LLVM "Disable LLVM (useful for CI)" OFF)
set(DEFAULT_YUZU_USE_BUNDLED_OPENSSL OFF)
if (EXT_DEFAULT OR PLATFORM_SUN OR PLATFORM_OPENBSD)
if (EXT_DEFAULT OR SOLARIS OR OPENBSD)
set(DEFAULT_YUZU_USE_BUNDLED_OPENSSL ON)
endif()
@@ -281,7 +295,7 @@ if(EXISTS ${PROJECT_SOURCE_DIR}/hooks/pre-commit AND NOT EXISTS ${PROJECT_SOURCE
endif()
endif()
if (ARCHITECTURE_arm64 AND (ANDROID OR PLATFORM_LINUX))
if (ARCHITECTURE_arm64 AND (ANDROID OR LINUX))
set(HAS_NCE 1)
add_compile_definitions(HAS_NCE=1)
endif()
@@ -290,7 +304,7 @@ if (YUZU_ROOM)
add_compile_definitions(YUZU_ROOM)
endif()
if (UNIX AND NOT (PLATFORM_LINUX OR WIN32))
if (UNIX AND NOT (LINUX OR WIN32))
if(CXX_APPLE OR CXX_CLANG)
# libc++ has stop_token and jthread as experimental
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fexperimental-library")
@@ -404,7 +418,7 @@ if (Boost_ADDED)
if (NOT MSVC OR CXX_CLANG)
# boost sucks
if (PLATFORM_SUN)
if (SOLARIS)
add_compile_options($<$<COMPILE_LANGUAGE:C,CXX>:-pthreads>)
endif()
@@ -508,9 +522,9 @@ elseif (WIN32)
# PSAPI is the Process Status API
set(PLATFORM_LIBRARIES ${PLATFORM_LIBRARIES} psapi imm32 version crypt32 rpcrt4 gdi32 wldap32 mswsock)
endif()
elseif (PLATFORM_MANAGARM)
elseif (MANAGARM)
set(PLATFORM_LIBRARIES iconv intl)
elseif (PLATFORM_HAIKU)
elseif (HAIKUOS)
# Haiku is so special :)
set(PLATFORM_LIBRARIES bsd /boot/system/lib/libnetwork.so)
elseif (CMAKE_SYSTEM_NAME MATCHES "^(Linux|kFreeBSD|GNU|SunOS)$")
@@ -604,7 +618,7 @@ if (ENABLE_QT)
# Best practice is to ask for all components at once, so they are from the same version
set(YUZU_QT_COMPONENTS Core Widgets Charts Concurrent Gui)
if (PLATFORM_LINUX OR PLATFORM_FREEBSD)
if (LINUX OR FREEBSD)
list(APPEND YUZU_QT_COMPONENTS DBus)
# yes Qt, we get it
set(QT_NO_PRIVATE_MODULE_WARNING ON)
+1 -1
View File
@@ -13,7 +13,7 @@ find_package_handle_standard_args(Opus
VERSION_VAR OPUS_VERSION
)
if (PLATFORM_MSYS)
if (MSYS2)
FixMsysPath(PkgConfig::OPUS)
endif()
+1 -1
View File
@@ -14,7 +14,7 @@ find_package_handle_standard_args(enet
VERSION_VAR ENET_VERSION
)
if (PLATFORM_MSYS)
if (MSYS2)
FixMsysPath(PkgConfig::ENET)
endif()
+1 -1
View File
@@ -14,7 +14,7 @@ find_package_handle_standard_args(libusb
VERSION_VAR LIBUSB_VERSION
)
if (PLATFORM_MSYS)
if (MSYS2)
FixMsysPath(PkgConfig::LIBUSB)
endif()
+1 -1
View File
@@ -13,7 +13,7 @@ else()
find_package(PkgConfig QUIET)
pkg_search_module(LZ4 QUIET IMPORTED_TARGET liblz4)
if (PLATFORM_MSYS)
if (MSYS2)
FixMsysPath(PkgConfig::LZ4)
endif()
+5 -1
View File
@@ -22,9 +22,13 @@ Eden is free, open-source, copyleft software, licensed under the terms of the [G
- No LLM or AI usage, *period*, for patches, pull requests, issues, comments, debugging, brainstorming, etc.
- For details on why, see the [detailed AI policy](docs/policies/AI.md).
- Usage of any form of profanity or otherwise unsavory language is generally discouraged.
- This is primarily because it rarely helps to actually understand what's going on.
- Remember that your comments should be focused and actually address what's happening. With very few exceptions, expletives are actively detrimental at best.
- New code must follow the same general style as the surrounding codebase. Exceptions may be granted in certain cases.
- Maintainers reserve the right to change your patches and pull requests at will. We will try to avoid this.
- You should respect all decisions made by the [code owners](docs/CODEOWNERS) in your particular subsystem. If you feel they are overstepping or are incorrect, don't be afraid to stand your ground!
- You should generally respect all decisions made by the [code owners](docs/CODEOWNERS) in your particular subsystem.
- However, if you feel they are overstepping or are incorrect, don't be afraid to stand your ground! Maintainers are not always correct.
- While we do *not* adhere to the terms of a formal code of conduct, you will generally be expected to respect other developers, contributors, and community members.
- You **must** have basic knowledge of [Git](https://git-scm.com/learn). Knowing how to manage your branches and follow proper fork policies is a necessity.
+2
View File
@@ -46,6 +46,8 @@ These options control dependencies.
- `YUZU_INSTALL_UDEV_RULES` (OFF) Install udev rules to enable hidraw access
- Needed for gyroscopes
- Only available on Linux
- `ENABLE_DEBUG_TOOLS` (OFF) Enables debugging and development tools, see [tools](../tools/README.md).
- `ENABLE_WERROR` (ON) Enables warnings as errors (-Werror).
### Flavors
+1 -1
View File
@@ -34,7 +34,7 @@ Everyone has their own way of viewing good/bad C++ practices, my general outline
- The reason is because the project has `-fno-rtti` disabled by default, due to the costs of dynamic polymorphism.
- Always copy-on-value for objects with `sizeof(void *) >= sizeof(T) * 2`, i.e objects sized as 2 pointers or less, for bigger objects you can use ref/pointer as usual.
- Try using move semantics instead of references, whenever possible.
- Remember function parameters are extremelly cheap as fuck, don't be afraid to place upto 8 parameters on a given function.
- Function parameters are cheap. Don't be afraid to use as many as needed (API usability permitting).
- Don't save a reference in structures of a parent object, i.e:
```c++
+1 -1
View File
@@ -164,7 +164,7 @@ if (NOT ANDROID)
AddJsonPackage(sdl3)
else()
message(STATUS "Using bundled SDL3")
if (PLATFORM_FREEBSD)
if (FREEBSD)
set(BUILD_SHARED_LIBS ON)
endif()
AddJsonPackage(sdl3-ci)
+23 -80
View File
@@ -1,45 +1,46 @@
# SPDX-FileCopyrightText: Copyright 2025 crueter
# SPDX-FileCopyrightText: Copyright 2026 crueter
# SPDX-License-Identifier: LGPL-3.0-or-later
## DetectPlatform ##
# This is a small helper that sets PLATFORM_<platform> variables for various
# This is a small helper that sets platform variables for various
# operating systems and distributions. Note that Apple, Windows, Android, etc.
# are not covered, as CMake already does that for us.
# It also sets CXX_<compiler> for the C++ compiler.
# Furthermore, some platforms have really silly requirements/quirks, so this
# also does a few of those.
# This module contains contributions from the Eden Emulator Project,
# notably from crueter and Lizzie.
if (${CMAKE_SYSTEM_NAME} STREQUAL "SunOS")
set(PLATFORM_SUN ON)
set(SOLARIS ON)
elseif (${CMAKE_SYSTEM_NAME} STREQUAL "OpenOrbis")
set(PLATFORM_PS4 ON)
set(OPENORBIS ON)
elseif (${CMAKE_SYSTEM_NAME} STREQUAL "managarm")
set(PLATFORM_MANAGARM ON)
elseif (${CMAKE_SYSTEM_NAME} STREQUAL "FreeBSD")
set(PLATFORM_FREEBSD ON)
elseif (${CMAKE_SYSTEM_NAME} STREQUAL "OpenBSD")
set(PLATFORM_OPENBSD ON)
elseif (${CMAKE_SYSTEM_NAME} STREQUAL "NetBSD")
set(PLATFORM_NETBSD ON)
elseif (${CMAKE_SYSTEM_NAME} STREQUAL "DragonFly")
set(PLATFORM_DRAGONFLYBSD ON)
set(MANAGARM ON)
elseif (${CMAKE_SYSTEM_NAME} STREQUAL "Haiku")
set(PLATFORM_HAIKU ON)
elseif (${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
set(PLATFORM_LINUX ON)
set(HAIKUOS ON)
endif()
# BSD
if (DEFINED BSD)
if ("${BSD}" STREQUAL "DragonFlyBSD")
set(DRAGONFLYBSD ON)
elseif ("${BSD}" STREQUAL "FreeBSD")
set(FREEBSD ON)
elseif ("${BSD}" STREQUAL "OpenBSD")
set(OPENBSD ON)
elseif ("${BSD}" STREQUAL "NetBSD")
set(NETBSD ON)
endif()
endif()
# dumb heuristic to detect msys2
if (CMAKE_COMMAND MATCHES "msys64")
set(PLATFORM_MSYS ON)
set(MSYS2 ON)
endif()
# compiler checks
if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
set(CXX_CLANG ON)
if (MSVC)
@@ -47,8 +48,6 @@ if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
endif()
elseif (CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
set(CXX_GCC ON)
elseif (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
set(CXX_CL ON)
elseif (CMAKE_CXX_COMPILER_ID STREQUAL "IntelLLVM")
set(CXX_ICC ON)
elseif (CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang")
@@ -61,37 +60,13 @@ if(MINGW AND CXX_CLANG)
set(CMAKE_SYSTEM_VERSION 10.0.0)
endif()
# NB: this does not account for SPARC
if (PLATFORM_SUN)
# Terrific OpenIndiana pkg shenanigans
list(APPEND CMAKE_PREFIX_PATH
"${CMAKE_SYSROOT}/usr/lib/qt/6.6/lib/amd64/cmake")
list(APPEND CMAKE_MODULE_PATH
"${CMAKE_SYSROOT}/usr/lib/qt/6.6/lib/amd64/cmake")
# Amazing - absolutely incredible
list(APPEND CMAKE_PREFIX_PATH "${CMAKE_SYSROOT}/usr/lib/amd64/cmake")
list(APPEND CMAKE_MODULE_PATH "${CMAKE_SYSROOT}/usr/lib/amd64/cmake")
# For some mighty reason, doing a normal release build sometimes
# may not trigger the proper -O3 switch to materialize
if (CMAKE_BUILD_TYPE MATCHES "Release")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -O3")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O3")
endif()
if (CMAKE_BUILD_TYPE MATCHES "RelWithDebInfo")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -O2")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O2")
endif()
endif()
# MSYS2 utilities
# Sometimes, PkgConfig modules will incorrectly reference / when CMake
# wants you to reference it as C:/msys64/. This function corrects that.
# Example in a Find module:
#[[
if (PLATFORM_MSYS)
if (MSYS2)
FixMsysPath(PkgConfig::OPUS)
endif()
]]
@@ -116,41 +91,9 @@ function(FixMsysPath target)
INTERFACE_INCLUDE_DIRECTORIES ${include_dir})
endfunction()
# MSYSTEM handling + program_path
if (PLATFORM_MSYS)
# really, really dumb heuristic to detect what environment we are in
macro(system var)
if (CMAKE_COMMAND MATCHES ${var})
set(MSYSTEM ${var})
endif()
endmacro()
system(mingw64)
system(clang64)
system(clangarm64)
system(ucrt64)
if (NOT DEFINED MSYSTEM)
set(MSYSTEM msys2)
endif()
# We generally want to prioritize environment-specific binaries if possible
# some, like autoconf, are not present on environments besides msys2 though
set(CMAKE_PROGRAM_PATH C:/msys64/${MSYSTEM}/bin C:/msys64/usr/bin)
set(ENV{PKG_CONFIG_PATH} C:/msys64/${MSYSTEM}/lib/pkgconfig)
endif()
# This saves a truly ridiculous amount of time during linking
# In my tests, without this, Eden takes 2 mins, with this, it takes 3-5 seconds
# or on GitHub Actions, 10 minutes -> 3 seconds
# Saves linking time
if (MINGW)
set(MINGW_FLAGS "-Wl,--strip-all -Wl,--gc-sections")
set(CMAKE_EXE_LINKER_FLAGS_RELEASE
"${CMAKE_EXE_LINKER_FLAGS_RELEASE} ${MINGW_FLAGS}")
endif()
# awesome
if (PLATFORM_FREEBSD OR PLATFORM_DRAGONFLYBSD)
set(CMAKE_EXE_LINKER_FLAGS
"${CMAKE_EXE_LINKER_FLAGS} -L${CMAKE_SYSROOT}/usr/local/lib")
endif()
+5 -5
View File
@@ -56,7 +56,7 @@ if (NOT YUZU_USE_BUNDLED_FFMPEG)
endif()
endif()
if (PLATFORM_PS4 OR PLATFORM_MANAGARM)
if (OPENORBIS OR MANAGARM)
# Doesn't support VA-API, don't go thru the embarrassment of trying to enable it
list(APPEND FFmpeg_HWACCEL_FLAGS --disable-vaapi)
elseif (ANDROID)
@@ -75,7 +75,7 @@ elseif (UNIX AND NOT DEFINED FFmpeg_IS_CROSS_COMPILING AND NOT ANDROID)
if(X11_FOUND)
if (NOT APPLE)
# In Solaris needs explicit linking for ffmpeg which links to /lib/amd64/libX11.so
if(PLATFORM_SUN)
if(SOLARIS)
list(APPEND FFmpeg_HWACCEL_LIBRARIES
X11
"${CMAKE_SYSROOT}/usr/lib/xorg/amd64/libdrm.so")
@@ -158,7 +158,7 @@ elseif (UNIX AND NOT DEFINED FFmpeg_IS_CROSS_COMPILING AND NOT ANDROID)
endif()
endif()
if (PLATFORM_PS4)
if (OPENORBIS)
list(APPEND FFmpeg_CROSS_COMPILE_LIBS
-lkernel
-lSceUserService
@@ -172,7 +172,7 @@ if (PLATFORM_PS4)
--extra-cxxflags=${CMAKE_SYSROOT}/usr/include
--extra-libs="${FFmpeg_CROSS_COMPILE_LIBS}"
)
elseif (PLATFORM_MANAGARM)
elseif (MANAGARM)
# Required for proper stuff
list(APPEND FFmpeg_CROSS_COMPILE_FLAGS
--disable-pthreads
@@ -299,7 +299,7 @@ else()
set(FFmpeg_BUILD_LIBRARIES ${FFmpeg_LIBRARIES})
# BSD make or Solaris make don't support ffmpeg make-j8
if (PLATFORM_LINUX OR ANDROID OR APPLE OR WIN32 OR PLATFORM_FREEBSD)
if (LINUX OR ANDROID OR APPLE OR WIN32 OR FREEBSD)
set(FFmpeg_MAKE_ARGS -j${SYSTEM_THREADS})
else()
set(FFmpeg_MAKE_ARGS "")
+1 -1
View File
@@ -11,7 +11,7 @@ if (NOT libusb_ADDED)
endif()
# TODO: *BSD fails to compile--may need different configs/symbols
if (MINGW OR PLATFORM_LINUX OR APPLE)
if (MINGW OR LINUX OR APPLE)
set(LIBUSB_FOUND ON CACHE BOOL "libusb is present" FORCE)
set(LIBUSB_VERSION "1.0.24" CACHE STRING "libusb version string" FORCE)
+2 -2
View File
@@ -1177,7 +1177,7 @@ static void *stbi__load_main(stbi__context *s, int *x, int *y, int *comp, int re
#endif
#ifndef STBI_NO_TGA
// test tga last because it's a crappy test!
// test tga last because it's a bad test!
if (stbi__tga_test(s))
return stbi__tga_load(s,x,y,comp,req_comp, ri);
#endif
@@ -7662,7 +7662,7 @@ static int stbi__info_main(stbi__context *s, int *x, int *y, int *comp)
if (stbi__hdr_info(s, x, y, comp)) return 1;
#endif
// test tga last because it's a crappy test!
// test tga last because it's a bad test!
#ifndef STBI_NO_TGA
if (stbi__tga_info(s, x, y, comp))
return 1;
+21 -9
View File
@@ -155,12 +155,24 @@ else()
$<$<COMPILE_LANGUAGE:CXX>:-fno-rtti>)
endif()
if (ENABLE_WERROR)
add_compile_options(
$<$<COMPILE_LANGUAGE:C,CXX>:-Werror=all>
$<$<COMPILE_LANGUAGE:C,CXX>:-Werror=extra>
$<$<COMPILE_LANGUAGE:C,CXX>:-Werror=missing-declarations>
$<$<COMPILE_LANGUAGE:C,CXX>:-Werror=shadow>
$<$<COMPILE_LANGUAGE:C,CXX>:-Werror=unused>)
else()
add_compile_options(
$<$<COMPILE_LANGUAGE:C,CXX>:-Wall>
$<$<COMPILE_LANGUAGE:C,CXX>:-Wextra>
$<$<COMPILE_LANGUAGE:C,CXX>:-Wmissing-declarations>
$<$<COMPILE_LANGUAGE:C,CXX>:-Wshadow>
$<$<COMPILE_LANGUAGE:C,CXX>:-Wunused>
# Some compilers could particularly misbehave :)
$<$<COMPILE_LANGUAGE:C,CXX>:-Wno-error-all>)
endif()
add_compile_options(
$<$<COMPILE_LANGUAGE:C,CXX>:-Werror=all>
$<$<COMPILE_LANGUAGE:C,CXX>:-Werror=extra>
$<$<COMPILE_LANGUAGE:C,CXX>:-Werror=missing-declarations>
$<$<COMPILE_LANGUAGE:C,CXX>:-Werror=shadow>
$<$<COMPILE_LANGUAGE:C,CXX>:-Werror=unused>
$<$<COMPILE_LANGUAGE:C,CXX>:-Wno-attributes>
$<$<COMPILE_LANGUAGE:C,CXX>:-Wno-invalid-offsetof>
$<$<COMPILE_LANGUAGE:C,CXX>:-Wno-unused-parameter>
@@ -169,9 +181,9 @@ else()
if (CXX_CLANG OR CXX_ICC OR CXX_APPLE) # Clang, AppleClang, or Intel C++
if (NOT MSVC)
add_compile_options(
$<$<COMPILE_LANGUAGE:C,CXX>:-Werror=shadow-uncaptured-local>
$<$<COMPILE_LANGUAGE:C,CXX>:-Werror=implicit-fallthrough>
$<$<COMPILE_LANGUAGE:C,CXX>:-Werror=type-limits>)
$<$<COMPILE_LANGUAGE:C,CXX>:-Wshadow-uncaptured-local>
$<$<COMPILE_LANGUAGE:C,CXX>:-Wimplicit-fallthrough>
$<$<COMPILE_LANGUAGE:C,CXX>:-Wtype-limits>)
endif()
add_compile_options(
$<$<COMPILE_LANGUAGE:C,CXX>:-Wno-braced-scalar-init>
@@ -181,7 +193,7 @@ else()
if (ARCHITECTURE_x86_64)
add_compile_options($<$<COMPILE_LANGUAGE:C,CXX>:-mcx16>)
if (PLATFORM_LINUX OR PLATFORM_FREEBSD)
if (LINUX OR FREEBSD)
add_compile_options($<$<COMPILE_LANGUAGE:C,CXX>:-mtls-dialect=gnu2>)
endif()
endif()
@@ -27,7 +27,6 @@ enum class BooleanSetting(override val key: String) : AbstractBooleanSetting {
RENDERER_ASYNCHRONOUS_GPU_EMULATION("use_asynchronous_gpu_emulation"),
RENDERER_ASYNC_PRESENTATION("async_presentation"),
RENDERER_ASYNCHRONOUS_SHADERS("use_asynchronous_shaders"),
RENDERER_UNIFIED_MEMORY("use_unified_memory"),
RENDERER_REACTIVE_FLUSHING("use_reactive_flushing"),
ENABLE_BUFFER_HISTORY("enable_buffer_history"),
USE_OPTIMIZED_VERTEX_BUFFERS("use_optimized_vertex_buffers"),
@@ -685,13 +685,6 @@ abstract class SettingsItem(
descriptionId = R.string.renderer_asynchronous_shaders_description
)
)
put(
SwitchSetting(
BooleanSetting.RENDERER_UNIFIED_MEMORY,
titleId = R.string.renderer_unified_memory,
descriptionId = R.string.renderer_unified_memory_description
)
)
put(
SingleChoiceSetting(
IntSetting.FAST_GPU_TIME,
@@ -304,7 +304,6 @@ class SettingsFragmentPresenter(
add(BooleanSetting.EMULATE_BGR565.key)
add(BooleanSetting.RESCALE_HACK.key)
add(BooleanSetting.RENDERER_ASYNCHRONOUS_SHADERS.key)
add(BooleanSetting.RENDERER_UNIFIED_MEMORY.key)
add(IntSetting.ANDROID_PIPELINE_WORKERS.key)
add(BooleanSetting.RENDERER_ASYNCHRONOUS_GPU_EMULATION.key)
add(BooleanSetting.RENDERER_ASYNC_PRESENTATION.key)
@@ -524,8 +524,6 @@
<string name="rescale_hack_description">Enables a legacy handling for the rescale configuration pass for games by using a quick rescale path</string>
<string name="renderer_asynchronous_shaders">Use asynchronous shaders</string>
<string name="renderer_asynchronous_shaders_description">Compiles shaders asynchronously. This may reduce stutters but may also introduce glitches.</string>
<string name="renderer_unified_memory">Unified memory access</string>
<string name="renderer_unified_memory_description">Allows GPU write buffer readbacks directly into guest memory, skipping the CPU staging copy.</string>
<string name="gpu_unswizzle_settings">GPU Unswizzle Settings</string>
<string name="gpu_unswizzle_settings_description">Configure GPU-based texture unswizzling parameters or disable it entirely. Adjust these settings to balance performance and texture loading quality.</string>
<string name="gpu_unswizzle_enable">Enable GPU Unswizzle</string>
+1 -1
View File
@@ -222,7 +222,7 @@ if (MSVC)
)
else()
target_compile_options(audio_core PRIVATE
$<$<COMPILE_LANGUAGE:C,CXX>:-Werror=conversion>
$<$<COMPILE_LANGUAGE:C,CXX>:-Wconversion>
$<$<COMPILE_LANGUAGE:C,CXX>:-Wno-sign-conversion>)
endif()
+1 -2
View File
@@ -108,7 +108,6 @@ add_library(
settings_input.h
settings_setting.h
slot_vector.h
socket_types.h
spin_lock.h
stb.cpp
stb.h
@@ -220,7 +219,7 @@ endif()
if(CXX_CLANG)
target_compile_options(common PRIVATE
$<$<COMPILE_LANGUAGE:C,CXX>:-fsized-deallocation>
$<$<COMPILE_LANGUAGE:C,CXX>:-Werror=unreachable-code-aggressive>)
$<$<COMPILE_LANGUAGE:C,CXX>:-Wunreachable-code-aggressive>)
target_compile_definitions(
common
PRIVATE
+2 -2
View File
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2017 Citra Emulator Project
@@ -11,7 +11,7 @@
#include <string>
#include <vector>
#include "common/common_types.h"
#include "common/socket_types.h"
#include "core/internal_network/socket_types.h"
#include "web_service/web_result.h"
namespace AnnounceMultiplayerRoom {
+8 -21
View File
@@ -31,9 +31,7 @@ s64 GetMaxPermissibleResidentMapCount() {
} // namespace
HeapTracker::HeapTracker(Common::HostMemory& buffer)
: m_buffer(buffer),
m_has_hardware_buffer_backing(!buffer.BackingHardwareBuffers().empty()),
m_max_resident_map_count(GetMaxPermissibleResidentMapCount()) {}
: m_buffer(buffer), m_max_resident_map_count(GetMaxPermissibleResidentMapCount()) {}
HeapTracker::~HeapTracker() = default;
void HeapTracker::Map(size_t virtual_offset, size_t host_offset, size_t length,
@@ -87,8 +85,7 @@ void HeapTracker::Unmap(size_t virtual_offset, size_t size, bool is_separate_hea
// If resident, erase from resident map.
if (item->is_resident) {
m_resident_map_count -= this->HostMapCount(item->paddr, item->size);
ASSERT(m_resident_map_count >= 0);
ASSERT(--m_resident_map_count >= 0);
m_resident_mappings.erase(m_resident_mappings.iterator_to(*item));
}
@@ -194,7 +191,7 @@ bool HeapTracker::DeferredMapSeparateHeap(size_t virtual_offset) {
// This map is now resident.
it->is_resident = true;
m_resident_map_count += this->HostMapCount(it->paddr, it->size);
m_resident_map_count++;
m_resident_mappings.insert(*it);
}
@@ -216,17 +213,17 @@ void HeapTracker::RebuildSeparateHeapAddressSpace() {
// Despite being worse in theory, this has proven to be better in practice than more
// regularly dumping a smaller amount, because it significantly reduces average case
// lock contention.
s64 const desired_count = (std::min)(m_resident_map_count, m_max_resident_map_count) / 2;
std::size_t const desired_count = (std::min)(m_resident_map_count, m_max_resident_map_count) / 2;
std::size_t const evict_count = m_resident_map_count - desired_count;
auto it = m_resident_mappings.begin();
while (m_resident_map_count > desired_count && it != m_resident_mappings.end()) {
for (size_t i = 0; i < evict_count && it != m_resident_mappings.end(); i++) {
// Unmark and unmap.
it->is_resident = false;
m_buffer.Unmap(it->vaddr, it->size, false);
// Advance.
m_resident_map_count -= this->HostMapCount(it->paddr, it->size);
ASSERT(m_resident_map_count >= 0);
ASSERT(--m_resident_map_count >= 0);
it = m_resident_mappings.erase(it);
}
}
@@ -248,7 +245,6 @@ void HeapTracker::SplitHeapMapLocked(VAddr offset) {
// Cache the original values.
auto* const left = std::addressof(*it);
const size_t orig_size = left->size;
const s64 orig_host_map_count = this->HostMapCount(left->paddr, orig_size);
// Adjust the left map.
const size_t left_size = offset - left->vaddr;
@@ -270,20 +266,11 @@ void HeapTracker::SplitHeapMapLocked(VAddr offset) {
// If resident, also insert into resident map.
if (right->is_resident) {
m_resident_map_count += this->HostMapCount(left->paddr, left->size) +
this->HostMapCount(right->paddr, right->size) -
orig_host_map_count;
m_resident_map_count++;
m_resident_mappings.insert(*right);
}
}
s64 HeapTracker::HostMapCount(PAddr paddr, size_t size) const {
if (!m_has_hardware_buffer_backing) {
return size != 0 ? 1 : 0;
}
return static_cast<s64>(m_buffer.BackingMapCount(paddr, size));
}
HeapTracker::AddrTree::iterator HeapTracker::GetNearestHeapMapLocked(VAddr offset) {
const SeparateHeapMap key{
.vaddr = offset,
-6
View File
@@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -85,13 +82,10 @@ private:
AddrTree::iterator GetNearestHeapMapLocked(VAddr offset);
s64 HostMapCount(PAddr paddr, size_t size) const;
void RebuildSeparateHeapAddressSpace();
private:
Common::HostMemory& m_buffer;
const bool m_has_hardware_buffer_backing;
const s64 m_max_resident_map_count;
std::shared_mutex m_rebuild_lock{};
+6 -368
View File
@@ -51,45 +51,14 @@
#endif // ^^^ POSIX ^^^
#include <atomic>
#include <mutex>
#include <random>
#include <vector>
#include "common/alignment.h"
#include "common/assert.h"
#include "common/free_region_manager.h"
#include "common/host_memory.h"
#include "common/logging.h"
#include "common/memory_detect.h"
#include "common/settings.h"
#ifdef __ANDROID__
#include <dlfcn.h>
#include <android/hardware_buffer.h>
namespace {
struct NativeHandle {
int version;
int numFds;
int numInts;
int data[1];
};
using PFN_AHardwareBuffer_getNativeHandle = const NativeHandle* (*)(const AHardwareBuffer*);
PFN_AHardwareBuffer_getNativeHandle ResolveGetNativeHandle() {
void* const lib = dlopen("libnativewindow.so", RTLD_NOW);
if (lib == nullptr) {
return nullptr;
}
return reinterpret_cast<PFN_AHardwareBuffer_getNativeHandle>(
dlsym(lib, "AHardwareBuffer_getNativeHandle"));
}
} // namespace
#endif
#if defined(__ANDROID__) && __ANDROID_API__ < 30
#include <sys/syscall.h>
@@ -106,12 +75,6 @@ namespace Common {
[[maybe_unused]] constexpr size_t PageAlignment = 0x1000;
[[maybe_unused]] constexpr size_t HugePageSize = 0x200000;
static std::atomic<u64> committed_backing_size{};
u64 GetCommittedBackingSize() noexcept {
return committed_backing_size.load(std::memory_order_relaxed);
}
#ifdef _WIN32
// Manually imported for MinGW compatibility
@@ -160,7 +123,7 @@ static void GetFuncAddress(Common::DynamicLibrary& dll, const char* name, T& pfn
class HostMemory::Impl {
public:
explicit Impl(size_t backing_size_, size_t virtual_size_, size_t)
explicit Impl(size_t backing_size_, size_t virtual_size_)
: backing_size{backing_size_}
, virtual_size{virtual_size_}
, process{GetCurrentProcess()}
@@ -266,10 +229,6 @@ public:
UNREACHABLE();
}
bool IsBackingShared() const noexcept {
return true;
}
const size_t backing_size; ///< Size of the backing memory in bytes
const size_t virtual_size; ///< Size of the virtual address placeholder in bytes
@@ -542,10 +501,9 @@ static int shm_open_anon(int flags, mode_t mode) {
class HostMemory::Impl {
public:
explicit Impl(size_t backing_size_, size_t virtual_size_, size_t preferred_offset_)
explicit Impl(size_t backing_size_, size_t virtual_size_)
: backing_size{backing_size_}
, virtual_size{virtual_size_}
, preferred_offset{preferred_offset_}
{}
bool Init() {
@@ -585,15 +543,10 @@ public:
LOG_WARNING(Common_Memory, "Using private mappings instead of shared ones");
backing_base = static_cast<u8*>(mmap(nullptr, backing_size, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0));
if (fd > 0) {
fd = -1;
close(fd);
}
fd = -1;
} else {
#ifdef __ANDROID__
if (InitAhbBacking()) {
return InitVirtual();
}
#endif
backing_base = static_cast<u8*>(mmap(nullptr, backing_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0));
}
if (backing_base == MAP_FAILED) {
@@ -601,10 +554,7 @@ public:
return false;
}
return InitVirtual();
}
bool InitVirtual() {
// Virtual memory initialization
virtual_base = virtual_map_base = static_cast<u8*>(ChooseVirtualBase(virtual_size));
if (virtual_base == MAP_FAILED) {
LOG_CRITICAL(HW_Memory, "mmap failed: {}", strerror(errno));
@@ -617,242 +567,6 @@ public:
return true;
}
#ifdef __ANDROID__
static AHardwareBuffer_Desc MakeBlobDesc(size_t len) {
return AHardwareBuffer_Desc{
.width = static_cast<u32>(len),
.height = 1,
.layers = 1,
.format = AHARDWAREBUFFER_FORMAT_BLOB,
.usage = AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN |
AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN |
AHARDWAREBUFFER_USAGE_GPU_DATA_BUFFER,
.stride = 0,
.rfu0 = 0,
.rfu1 = 0,
};
}
static bool ProbeAhbBacking(PFN_AHardwareBuffer_getNativeHandle get_native_handle) {
const AHardwareBuffer_Desc desc = MakeBlobDesc(PageAlignment * 2);
AHardwareBuffer* buffer{};
if (AHardwareBuffer_allocate(&desc, &buffer) != 0 || buffer == nullptr) {
return false;
}
const NativeHandle* const handle = get_native_handle(buffer);
if (handle == nullptr || handle->numFds < 1) {
AHardwareBuffer_release(buffer);
return false;
}
const int probe_fd = handle->data[0];
bool ok = true;
const auto try_map = [&](int prot, off_t offset, const char* what) {
if (!ok) {
return;
}
void* const ptr = mmap(nullptr, PageAlignment, prot, MAP_SHARED, probe_fd, offset);
if (ptr == MAP_FAILED) {
ok = false;
return;
}
munmap(ptr, PageAlignment);
};
try_map(PROT_READ | PROT_WRITE, 0, "shared mappings");
try_map(PROT_READ | PROT_WRITE, static_cast<off_t>(PageAlignment), "mappings at an offset");
#ifdef ARCHITECTURE_arm64
try_map(PROT_READ | PROT_EXEC, 0, "executable mappings");
#endif
AHardwareBuffer_release(buffer);
return ok;
}
size_t ComputeAhbBudget(size_t window_size) const {
const u64 total_physical = Common::GetMemInfo().TotalPhysicalMemory;
constexpr u64 BaselineFootprint = 6ULL << 30;
if (total_physical <= BaselineFootprint) {
return 0;
}
const u64 max_map_count = Common::GetMaxMapCount();
constexpr u64 ReservedMaps = 24576;
if (max_map_count == 0 || max_map_count <= ReservedMaps) {
return 0;
}
u64 budget = (total_physical - BaselineFootprint) / 2;
constexpr u64 MapSlotsPerWindow = 4096;
const u64 affordable_windows = (max_map_count - ReservedMaps) / MapSlotsPerWindow;
budget = (std::min)(budget, affordable_windows * window_size);
const u64 available = Common::GetAvailablePhysicalMemory();
if (available != 0) {
budget = (std::min)(budget, available / 2);
}
budget = (std::min)(budget, static_cast<u64>(backing_size));
budget = Common::AlignDown(budget, window_size);
constexpr u64 MinimumBudget = 256ULL << 20;
if (budget < MinimumBudget) {
return 0;
}
return static_cast<size_t>(budget);
}
bool InitAhbBacking() {
if (!Settings::values.use_unified_memory.GetValue()) {
return false;
}
static const PFN_AHardwareBuffer_getNativeHandle get_native_handle =
ResolveGetNativeHandle();
if (get_native_handle == nullptr) {
return false;
}
constexpr size_t window_size = 256ULL << 20;
const size_t budget = ComputeAhbBudget(window_size);
if (budget == 0) {
return false;
}
if (!ProbeAhbBacking(get_native_handle)) {
return false;
}
const size_t aligned_backing = Common::AlignDown(backing_size, window_size);
const size_t max_windows = (std::min)(budget, aligned_backing) / window_size;
std::vector<AHardwareBuffer*> buffers;
std::vector<int> buffer_fds;
const auto cleanup = [&] {
for (AHardwareBuffer* buffer : buffers) {
AHardwareBuffer_release(buffer);
}
buffers.clear();
buffer_fds.clear();
};
for (size_t i = 0; i < max_windows; ++i) {
const AHardwareBuffer_Desc desc = MakeBlobDesc(window_size);
AHardwareBuffer* buffer{};
if (AHardwareBuffer_allocate(&desc, &buffer) != 0 || buffer == nullptr) {
break;
}
const NativeHandle* const handle = get_native_handle(buffer);
if (handle == nullptr || handle->numFds < 1) {
AHardwareBuffer_release(buffer);
break;
}
const int buffer_fd = handle->data[0];
const off_t buffer_len = lseek(buffer_fd, 0, SEEK_END);
if (buffer_len < static_cast<off_t>(window_size)) {
AHardwareBuffer_release(buffer);
break;
}
buffers.push_back(buffer);
buffer_fds.push_back(buffer_fd);
}
const size_t num_windows = buffers.size();
if (num_windows == 0) {
return false;
}
const size_t region_size = num_windows * window_size;
const size_t region_base = Common::AlignDown(
(std::min)(preferred_offset, aligned_backing - region_size), window_size);
u8* const base = static_cast<u8*>(mmap(nullptr, backing_size, PROT_NONE,
MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE, -1, 0));
if (base == MAP_FAILED) {
cleanup();
return false;
}
const auto map_over_reservation = [&](size_t offset, size_t len, int map_fd,
off_t map_offset) {
if (len == 0) {
return true;
}
if (mmap(base + offset, len, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_FIXED, map_fd,
map_offset) == MAP_FAILED) {
munmap(base, backing_size);
cleanup();
return false;
}
return true;
};
if (!map_over_reservation(0, region_base, fd, 0)) {
return false;
}
for (size_t i = 0; i < num_windows; ++i) {
if (!map_over_reservation(region_base + i * window_size, window_size, buffer_fds[i],
0)) {
return false;
}
}
const size_t tail_offset = region_base + region_size;
if (!map_over_reservation(tail_offset, backing_size - tail_offset, fd,
static_cast<off_t>(tail_offset))) {
return false;
}
backing_base = base;
ahb_windows = std::move(buffers);
ahb_fds = std::move(buffer_fds);
ahb_window_size = window_size;
ahb_base = region_base;
ahb_bytes = region_size;
committed_backing_size.store(region_size, std::memory_order_relaxed);
return true;
}
void MapBackingRange(size_t virtual_offset, size_t host_offset, size_t length, int prot_flags) {
while (length > 0) {
int map_fd = fd;
off_t map_offset = static_cast<off_t>(host_offset);
size_t chunk = length;
if (host_offset < ahb_base) {
chunk = (std::min)(chunk, ahb_base - host_offset);
} else if (host_offset < ahb_base + ahb_bytes) {
const size_t relative = host_offset - ahb_base;
const size_t window = relative / ahb_window_size;
const size_t local = relative % ahb_window_size;
map_fd = ahb_fds[window];
map_offset = static_cast<off_t>(local);
chunk = (std::min)(chunk, ahb_window_size - local);
}
void* const ret = mmap(virtual_base + virtual_offset, chunk, prot_flags,
MAP_SHARED | MAP_FIXED, map_fd, map_offset);
ASSERT_MSG(ret != MAP_FAILED, "mmap: {}", strerror(errno));
virtual_offset += chunk;
host_offset += chunk;
length -= chunk;
}
}
size_t BackingMapCount(size_t host_offset, size_t length) const noexcept {
if (length == 0) {
return 0;
}
if (ahb_bytes == 0) {
return 1;
}
size_t count = 0;
while (length > 0) {
size_t chunk = length;
if (host_offset < ahb_base) {
chunk = (std::min)(chunk, ahb_base - host_offset);
} else if (host_offset < ahb_base + ahb_bytes) {
const size_t local = (host_offset - ahb_base) % ahb_window_size;
chunk = (std::min)(chunk, ahb_window_size - local);
}
host_offset += chunk;
length -= chunk;
++count;
}
return count;
}
std::span<AHardwareBuffer* const> AhbWindows() const noexcept {
return ahb_windows;
}
size_t AhbWindowSize() const noexcept {
return ahb_bytes != 0 ? ahb_window_size : 0;
}
size_t AhbBase() const noexcept {
return ahb_base;
}
#endif
~Impl() {
Release();
}
@@ -873,12 +587,6 @@ public:
#ifdef ARCHITECTURE_arm64
if (True(perms & MemoryPermission::Execute))
prot_flags |= PROT_EXEC;
#endif
#ifdef __ANDROID__
if (ahb_bytes != 0) {
MapBackingRange(virtual_offset, host_offset, length, prot_flags);
return;
}
#endif
int flags = (fd >= 0 ? MAP_SHARED : MAP_PRIVATE) | MAP_FIXED;
void* ret = mmap(virtual_base + virtual_offset, length, prot_flags, flags, fd, host_offset);
@@ -924,18 +632,8 @@ public:
virtual_base = nullptr;
}
bool IsBackingShared() const noexcept {
#ifdef __ANDROID__
if (ahb_bytes != 0) {
return true;
}
#endif
return fd >= 0;
}
const size_t backing_size; ///< Size of the backing memory in bytes
const size_t virtual_size; ///< Size of the virtual address placeholder in bytes
const size_t preferred_offset;
u8* backing_base{reinterpret_cast<u8*>(MAP_FAILED)};
u8* virtual_base{reinterpret_cast<u8*>(MAP_FAILED)};
@@ -958,18 +656,6 @@ private:
int ret = close(fd);
ASSERT_MSG(ret == 0, "close failed: {}", strerror(errno));
}
#ifdef __ANDROID__
for (AHardwareBuffer* buffer : ahb_windows) {
AHardwareBuffer_release(buffer);
}
ahb_windows.clear();
ahb_fds.clear();
if (ahb_bytes != 0) {
committed_backing_size.store(0, std::memory_order_relaxed);
ahb_bytes = 0;
}
#endif
}
void AdjustMap(size_t* virtual_offset, size_t* length) {
@@ -995,19 +681,11 @@ private:
int fd{-1}; // memfd file descriptor, -1 is the error value of memfd_create
FreeRegionManager free_manager{};
#ifdef __ANDROID__
std::vector<AHardwareBuffer*> ahb_windows;
std::vector<int> ahb_fds;
size_t ahb_window_size{};
size_t ahb_base{};
size_t ahb_bytes{};
#endif
};
#endif // ^^^ POSIX ^^^
HostMemory::HostMemory(size_t backing_size_, size_t virtual_size_, size_t preferred_offset_)
HostMemory::HostMemory(size_t backing_size_, size_t virtual_size_)
: backing_size(backing_size_)
, virtual_size(virtual_size_)
{
@@ -1019,7 +697,7 @@ HostMemory::HostMemory(size_t backing_size_, size_t virtual_size_, size_t prefer
#else
// Try to allocate a fastmem arena.
// The implementation will fail with std::bad_alloc on errors.
impl = std::make_unique<HostMemory::Impl>(AlignUp(backing_size, PageAlignment), AlignUp(virtual_size, PageAlignment) + HugePageSize, preferred_offset_);
impl = std::make_unique<HostMemory::Impl>(AlignUp(backing_size, PageAlignment), AlignUp(virtual_size, PageAlignment) + HugePageSize);
if (impl->Init()) {
backing_base = impl->backing_base;
virtual_base = impl->virtual_base;
@@ -1089,46 +767,6 @@ void HostMemory::ClearBackingRegion(size_t physical_offset, size_t length, u32 f
std::memset(backing_base + physical_offset, fill_value, length);
}
std::span<AHardwareBuffer* const> HostMemory::BackingHardwareBuffers() const noexcept {
#ifdef __ANDROID__
return impl ? impl->AhbWindows() : std::span<AHardwareBuffer* const>{};
#else
return {};
#endif
}
size_t HostMemory::BackingMapCount(size_t host_offset, size_t length) const noexcept {
#ifdef __ANDROID__
return impl ? impl->BackingMapCount(host_offset, length) : (length != 0 ? 1 : 0);
#else
return length != 0 ? 1 : 0;
#endif
}
size_t HostMemory::BackingHardwareBufferWindowSize() const noexcept {
#ifdef __ANDROID__
return impl ? impl->AhbWindowSize() : 0;
#else
return 0;
#endif
}
bool HostMemory::IsBackingShared() const noexcept {
#if defined(__OPENORBIS__) || defined(__managarm__)
return false;
#else
return impl && impl->IsBackingShared();
#endif
}
size_t HostMemory::BackingHardwareBufferBase() const noexcept {
#ifdef __ANDROID__
return impl ? impl->AhbBase() : 0;
#else
return 0;
#endif
}
void HostMemory::EnableDirectMappedAddress() {
#if !(defined(__OPENORBIS__) || defined(__managarm__))
if (impl) {
+1 -20
View File
@@ -8,17 +8,12 @@
#include <memory>
#include <optional>
#include <span>
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "common/virtual_buffer.h"
struct AHardwareBuffer;
namespace Common {
[[nodiscard]] u64 GetCommittedBackingSize() noexcept;
enum class MemoryPermission : u32 {
Read = 1 << 0,
Write = 1 << 1,
@@ -33,7 +28,7 @@ DECLARE_ENUM_FLAG_OPERATORS(MemoryPermission)
*/
class HostMemory {
public:
explicit HostMemory(size_t backing_size_, size_t virtual_size_, size_t preferred_offset_ = 0);
explicit HostMemory(size_t backing_size_, size_t virtual_size_);
~HostMemory();
/**
@@ -67,20 +62,6 @@ public:
return backing_base;
}
[[nodiscard]] size_t BackingSize() const noexcept {
return backing_size;
}
[[nodiscard]] size_t BackingMapCount(size_t host_offset, size_t length) const noexcept;
[[nodiscard]] std::span<AHardwareBuffer* const> BackingHardwareBuffers() const noexcept;
[[nodiscard]] size_t BackingHardwareBufferWindowSize() const noexcept;
[[nodiscard]] size_t BackingHardwareBufferBase() const noexcept;
[[nodiscard]] bool IsBackingShared() const noexcept;
[[nodiscard]] u8* VirtualBasePointer() noexcept {
return virtual_base;
}
-58
View File
@@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -20,10 +17,6 @@
#endif
#endif
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include "common/memory_detect.h"
namespace Common {
@@ -76,55 +69,4 @@ const MemoryInfo& GetMemInfo() {
return mem_info;
}
u64 GetAvailablePhysicalMemory() {
#ifdef _WIN32
MEMORYSTATUSEX memorystatus;
memorystatus.dwLength = sizeof(memorystatus);
if (GlobalMemoryStatusEx(&memorystatus)) {
return memorystatus.ullAvailPhys;
}
return 0;
#elif defined(__linux__)
if (std::FILE* const file = std::fopen("/proc/meminfo", "re")) {
char line[256];
u64 available = 0;
while (std::fgets(line, sizeof(line), file) != nullptr) {
if (std::strncmp(line, "MemAvailable:", 13) == 0) {
available = std::strtoull(line + 13, nullptr, 10) * 1024ULL;
break;
}
}
std::fclose(file);
if (available != 0) {
return available;
}
}
struct sysinfo info;
if (sysinfo(&info) == 0) {
const u64 unit = info.mem_unit != 0 ? info.mem_unit : 1ULL;
return (static_cast<u64>(info.freeram) + static_cast<u64>(info.bufferram)) * unit;
}
return 0;
#else
return 0;
#endif
}
u64 GetMaxMapCount() {
#ifdef __linux__
if (std::FILE* const file = std::fopen("/proc/sys/vm/max_map_count", "re")) {
char line[32];
u64 count = 0;
if (std::fgets(line, sizeof(line), file) != nullptr) {
count = std::strtoull(line, nullptr, 10);
}
std::fclose(file);
return count;
}
return 0;
#else
return 0;
#endif
}
} // namespace Common
-7
View File
@@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -21,8 +18,4 @@ struct MemoryInfo {
*/
[[nodiscard]] const MemoryInfo& GetMemInfo();
[[nodiscard]] u64 GetAvailablePhysicalMemory();
[[nodiscard]] u64 GetMaxMapCount();
} // namespace Common
+9 -4
View File
@@ -587,9 +587,6 @@ struct Values {
SwitchableSetting<bool> use_asynchronous_shaders{linkage, false, "use_asynchronous_shaders",
Category::RendererHacks};
SwitchableSetting<bool> use_unified_memory{linkage, false, "use_unified_memory",
Category::RendererHacks};
SwitchableSetting<GpuUnswizzleSize> gpu_unswizzle_texture_size{linkage,
GpuUnswizzleSize::Large,
"gpu_unswizzle_texture_size",
@@ -705,7 +702,15 @@ struct Values {
// Controls
InputSetting<std::array<PlayerInput, 10>> players;
Setting<bool> disable_wgi_xinput{
linkage, false, "disable_wgi_xinput", Category::Controls, Specialization::Default,
// Only read/write disable_wgi_xinput on Windows platforms
#ifdef _WIN32
true
#else
false
#endif
};
Setting<bool> enable_raw_input{
linkage, false, "enable_raw_input", Category::Controls, Specialization::Default,
// Only read/write enable_raw_input on Windows platforms
-178
View File
@@ -1,178 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <optional>
#include <string>
#include "common/common_types.h"
namespace Network {
/// Address families
enum class Domain : u8 {
Unspecified, ///< Represents 0, used in getaddrinfo hints
INET, ///< Address family for IPv4
};
/// Socket types
enum class Type {
Unspecified, ///< Represents 0, used in getaddrinfo hints
STREAM,
DGRAM,
RAW,
SEQPACKET,
};
/// Protocol values for sockets
enum class Protocol : u8 {
Unspecified, ///< Represents 0, usable in various places
IP,
ICMP,
TCP,
UDP,
IPV6,
RAW,
IGMP,
GGP,
IPV4,
ST,
EGP,
PIGP,
RCCMON,
NVPII,
PUP,
ARGUS,
EMCON,
XNET,
CHAOS,
MUX,
MEAS,
HMP,
PRM,
IDP,
TRUNK1,
TRUNK2,
LEAF1,
LEAF2,
RDP,
IRTP,
TP,
BLT,
NSP,
INP,
DCCP,
//TODO: 3PC,
IDPR,
XTP,
DDP,
CMTP,
TPXX,
IL,
SDRP,
ROUTING,
FRAGMENT,
IDRP,
RSVP,
GRE,
MHRP,
BHA,
ESP,
AH,
INLSP,
SWIPE,
NHRP,
MOBILE,
TLSP,
SKIP,
ICMPV6,
NONE,
DSTOPTS,
AHIP,
CFTP,
HELLO,
SATEXPAK,
KRYPTOLAN,
RVD,
IPPC,
ADFS,
SATMON,
VISA,
IPCV,
CPNX,
CPHB,
WSN,
PVP,
BRSATMON,
ND,
WBMON,
WBEXPAK,
EON,
VMTP,
SVMTP,
VINES,
TTP,
IGP,
DGP,
TCF,
IGRP,
OSPFIGP,
SRPC,
LARP,
MTP,
AX25,
IPEIP,
MICP,
SCCSP,
ETHERIP,
ENCAP,
APES,
GMTP,
IPCOMP,
SCTP,
MH,
UDPLITE,
HIP,
SHIM6,
PIM,
CARP,
PGM,
MPLS,
PFSYNC
};
/// Shutdown mode
enum class ShutdownHow {
RD,
WR,
RDWR,
};
/// Array of IPv4 address
using IPv4Address = std::array<u8, 4>;
/// Cross-platform sockaddr structure
struct SockAddrIn {
Domain family;
IPv4Address ip;
u16 portno;
};
constexpr u32 FLAG_MSG_PEEK = 0x2;
constexpr u32 FLAG_MSG_DONTWAIT = 0x80;
constexpr u32 FLAG_O_NONBLOCK = 0x800;
/// Cross-platform addrinfo structure
struct AddrInfo {
Domain family;
Type socket_type;
Protocol protocol;
SockAddrIn addr;
std::optional<std::string> canon_name;
};
} // namespace Network
+5 -2
View File
@@ -1133,6 +1133,9 @@ add_library(core STATIC
internal_network/network_interface.h
internal_network/socket_proxy.cpp
internal_network/socket_proxy.h
internal_network/socket_icmp.cpp
internal_network/socket_icmp.h
internal_network/socket_types.h
internal_network/sockets.h
internal_network/wifi_scanner.h
launch_timestamp_cache.cpp
@@ -1173,7 +1176,7 @@ add_library(core STATIC
if (ENABLE_WIFI_SCAN)
target_sources(core PRIVATE internal_network/wifi_scanner.cpp)
if (PLATFORM_LINUX)
if (LINUX)
target_link_libraries(core PRIVATE iw)
endif()
else()
@@ -1199,7 +1202,7 @@ if (MSVC)
)
else()
target_compile_options(core PRIVATE
$<$<COMPILE_LANGUAGE:C,CXX>:-Werror=conversion>
$<$<COMPILE_LANGUAGE:C,CXX>:-Wconversion>
$<$<COMPILE_LANGUAGE:C,CXX>:-Wno-sign-conversion>
$<$<COMPILE_LANGUAGE:C,CXX>:-Wno-cast-function-type>
$<$<CXX_COMPILER_ID:Clang>:-fsized-deallocation>)
+1 -5
View File
@@ -119,7 +119,6 @@ struct System::Impl {
is_multicore = Settings::values.use_multi_core.GetValue();
extended_memory_layout = Settings::values.memory_layout_mode.GetValue() != Settings::MemoryLayout::Memory_4Gb;
unified_memory = Settings::values.use_unified_memory.GetValue();
core_timing.SetMulticore(is_multicore);
core_timing.Initialize([&system]() { system.RegisterHostThread(); });
@@ -147,8 +146,7 @@ struct System::Impl {
!device_memory.has_value() ||
is_multicore != Settings::values.use_multi_core.GetValue() ||
extended_memory_layout != (Settings::values.memory_layout_mode.GetValue() !=
Settings::MemoryLayout::Memory_4Gb) ||
unified_memory != Settings::values.use_unified_memory.GetValue();
Settings::MemoryLayout::Memory_4Gb);
if (!must_reinitialize) {
return;
@@ -159,7 +157,6 @@ struct System::Impl {
is_multicore = Settings::values.use_multi_core.GetValue();
extended_memory_layout =
Settings::values.memory_layout_mode.GetValue() != Settings::MemoryLayout::Memory_4Gb;
unified_memory = Settings::values.use_unified_memory.GetValue();
Initialize(system);
}
@@ -506,7 +503,6 @@ struct System::Impl {
std::atomic_bool is_powered_on{};
bool is_multicore : 1 = false;
bool extended_memory_layout : 1 = false;
bool unified_memory : 1 = false;
bool exit_locked : 1 = false;
bool exit_requested : 1 = false;
bool nvdec_active : 1 = false;
+1 -13
View File
@@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -15,18 +12,9 @@ constexpr size_t VirtualReserveSize = 1ULL << 38;
constexpr size_t VirtualReserveSize = 1ULL << 39;
#endif
namespace {
size_t ApplicationPoolOffset() {
using Init = Kernel::Board::Nintendo::Nx::KSystemControl::Init;
const size_t dram_size = Init::GetIntendedMemorySize();
const size_t application_pool_size = Init::GetApplicationPoolSize();
return dram_size > application_pool_size ? dram_size - application_pool_size : 0;
}
}
DeviceMemory::DeviceMemory()
: buffer{Kernel::Board::Nintendo::Nx::KSystemControl::Init::GetIntendedMemorySize(),
VirtualReserveSize, ApplicationPoolOffset()} {}
VirtualReserveSize} {}
DeviceMemory::~DeviceMemory() = default;
-35
View File
@@ -20,8 +20,6 @@
#include "common/scratch_buffer.h"
#include "common/virtual_buffer.h"
struct AHardwareBuffer;
namespace Core {
constexpr size_t DEVICE_PAGEBITS = 12ULL;
@@ -97,34 +95,6 @@ public:
ApplyOpOnPAddr(address, buffer, operation);
}
u8* GetPhysicalBase() noexcept {
return reinterpret_cast<u8*>(physical_base);
}
const u8* GetPhysicalBase() const noexcept {
return reinterpret_cast<const u8*>(physical_base);
}
size_t GetPhysicalSize() const noexcept {
return physical_size;
}
std::span<AHardwareBuffer* const> GetBackingHardwareBuffers() const noexcept {
return ahb_windows;
}
size_t GetBackingHardwareBufferWindowSize() const noexcept {
return ahb_window_size;
}
size_t GetBackingHardwareBufferBase() const noexcept {
return ahb_base;
}
bool IsBackingShared() const noexcept {
return backing_is_shared;
}
PAddr GetPhysicalRawAddressFromDAddr(DAddr address) const {
PAddr subbits = PAddr(address & page_mask);
auto paddr = tracked_entries[(address >> page_bits)].compressed_physical_ptr;
@@ -201,11 +171,6 @@ private:
std::unique_ptr<DeviceMemoryManagerAllocator<Traits>> impl;
const uintptr_t physical_base;
const size_t physical_size;
const std::span<AHardwareBuffer* const> ahb_windows;
const size_t ahb_window_size;
const size_t ahb_base;
const bool backing_is_shared;
DeviceInterface* device_inter;
struct TrackedEntry {
-5
View File
@@ -171,11 +171,6 @@ struct DeviceMemoryManagerAllocator {
template <typename Traits>
DeviceMemoryManager<Traits>::DeviceMemoryManager(const DeviceMemory& device_memory_)
: physical_base{uintptr_t(device_memory_.buffer.BackingBasePointer())}
, physical_size{device_memory_.buffer.BackingSize()}
, ahb_windows{device_memory_.buffer.BackingHardwareBuffers()}
, ahb_window_size{device_memory_.buffer.BackingHardwareBufferWindowSize()}
, ahb_base{device_memory_.buffer.BackingHardwareBufferBase()}
, backing_is_shared{device_memory_.buffer.IsBackingShared()}
, device_inter{nullptr}
, compressed_device_addr(1ULL << ((Settings::values.memory_layout_mode.GetValue() == Settings::MemoryLayout::Memory_4Gb ? physical_min_bits : physical_max_bits) - Memory::YUZU_PAGEBITS))
, tracked_entries(device_as_size >> Memory::YUZU_PAGEBITS)
@@ -28,7 +28,7 @@ Result CloseHandle(Core::System& system, Handle handle) {
/// Clears the signaled state of an event or process.
Result ResetSignal(Core::System& system, Handle handle) {
LOG_DEBUG(Kernel_SVC, "called handle {:#08x}", handle);
LOG_TRACE(Kernel_SVC, "called handle {:#08x}", handle);
// Get the current handle table.
const auto& handle_table = GetCurrentProcess(system.Kernel()).GetHandleTable();
+1 -1
View File
@@ -18,7 +18,7 @@
#include <ankerl/unordered_dense.h>
#include "common/logging.h"
#include "common/socket_types.h"
#include "core/internal_network/socket_types.h"
#include "core/hle/result.h"
#include "core/hle/service/ldn/ldn_results.h"
#include "core/hle/service/ldn/ldn_types.h"
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator
@@ -84,7 +84,7 @@ private:
RestrictionSettings restriction_settings{};
std::array<char, 8> pin_code{};
Capability capability{};
// TODO: this is RAW as fuck
// TODO: this is raw
PlayTimerSettings raw_play_timer_settings{};
KernelHelpers::ServiceContext service_context;
+7 -8
View File
@@ -129,6 +129,13 @@ ServerManager::~ServerManager() {
}
}
void ServerManager::StartAdditionalHostThreads(const char* name, size_t num_threads) {
for (size_t i = 0; i < num_threads; i++) {
auto thread_name = fmt::format("{}:{}", name, i + 1);
m_threads.emplace_back(m_system.Kernel().RunOnHostCoreThread(std::move(thread_name), [&] { this->LoopProcessImpl(); }));
}
}
void ServerManager::RunServer(std::unique_ptr<ServerManager>&& server_manager) {
server_manager->m_system.RunServer(std::move(server_manager));
}
@@ -245,14 +252,6 @@ Result ServerManager::ManageDeferral(Kernel::KEvent** out_event) {
R_SUCCEED();
}
void ServerManager::StartAdditionalHostThreads(const char* name, size_t num_threads) {
for (size_t i = 0; i < num_threads; i++) {
auto thread_name = fmt::format("{}:{}", name, i + 1);
m_threads.emplace_back(m_system.Kernel().RunOnHostCoreThread(
std::move(thread_name), [&] { this->LoopProcessImpl(); }));
}
}
Result ServerManager::LoopProcess() {
SCOPE_EXIT {
m_stopped.Set();
+4 -1
View File
@@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -48,8 +51,8 @@ public:
Result ManageDeferral(Kernel::KEvent** out_event);
Result LoopProcess();
void StartAdditionalHostThreads(const char* name, size_t num_threads);
void StartAdditionalHostThreads(const char* name, size_t num_threads);
static void RunServer(std::unique_ptr<ServerManager>&& server);
private:
@@ -1020,7 +1020,7 @@ Result ISystemSettingsServer::GetBatteryLot(Out<BatteryLot> out_battery_lot) {
c.lot_number[1] = 'H';
c.lot_number[2] = 'A';
c.lot_number[3] = 'C';
// TODO: I have no fucking idea what the letters mean
// TODO: what do the letters mean?
c.lot_number[4] = 'H';
c.lot_number[5] = 'Z';
c.lot_number[6] = 'Z';
+217 -273
View File
@@ -12,7 +12,7 @@
#include <fmt/ranges.h>
#include "common/logging.h"
#include "common/socket_types.h"
#include "core/internal_network/socket_types.h"
#include "core/core.h"
#include "core/hle/kernel/k_thread.h"
#include "core/hle/service/ipc_helpers.h"
@@ -20,6 +20,7 @@
#include "core/hle/service/sockets/sockets_translate.h"
#include "core/internal_network/network.h"
#include "core/internal_network/socket_proxy.h"
#include "core/internal_network/socket_icmp.h"
#include "core/internal_network/sockets.h"
#include "network/network.h"
#include <common/settings.h>
@@ -28,11 +29,11 @@ namespace Service::Sockets {
namespace {
bool IsConnectionBased(Type type) {
bool IsConnectionBased(Network::Type type) {
switch (type) {
case Type::STREAM:
case Network::Type::STREAM:
return true;
case Type::DGRAM:
case Network::Type::DGRAM:
return false;
default:
UNIMPLEMENTED_MSG("Unimplemented type={}", type);
@@ -92,7 +93,7 @@ void BSD::ConnectWork::Execute(BSD* bsd) {
void BSD::ConnectWork::Response(HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(ResultSuccess);
rb.Push<s32>(bsd_errno == Errno::SUCCESS ? 0 : -1);
rb.Push<s32>(bsd_errno == Network::Errno::SUCCESS ? 0 : -1);
rb.PushEnum(bsd_errno);
}
@@ -170,12 +171,8 @@ void BSD::Socket(HLERequestContext& ctx) {
const u32 domain = rp.Pop<u32>();
const u32 type = rp.Pop<u32>();
const u32 protocol = rp.Pop<u32>();
LOG_DEBUG(Service, "called. domain={} type={} protocol={}", domain, type, protocol);
const auto [fd, bsd_errno] = SocketImpl(static_cast<Domain>(domain), static_cast<Type>(type),
static_cast<Protocol>(protocol));
const auto [fd, bsd_errno] = SocketImpl(Network::Domain(domain), Network::Type(type), Network::Protocol(protocol));
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(ResultSuccess);
rb.Push<s32>(fd);
@@ -246,13 +243,13 @@ void BSD::GetPeerName(HLERequestContext& ctx) {
LOG_DEBUG(Service, "called. fd={}", fd);
std::vector<u8> write_buffer(ctx.GetWriteBufferSize());
const Errno bsd_errno = GetPeerNameImpl(fd, write_buffer);
const Network::Errno bsd_errno = GetPeerNameImpl(fd, write_buffer);
ctx.WriteBuffer(write_buffer);
IPC::ResponseBuilder rb{ctx, 5};
rb.Push(ResultSuccess);
rb.Push<s32>(bsd_errno != Errno::SUCCESS ? -1 : 0);
rb.Push<s32>(bsd_errno != Network::Errno::SUCCESS ? -1 : 0);
rb.PushEnum(bsd_errno);
rb.Push<u32>(static_cast<u32>(write_buffer.size()));
}
@@ -264,13 +261,13 @@ void BSD::GetSockName(HLERequestContext& ctx) {
LOG_DEBUG(Service, "called. fd={}", fd);
std::vector<u8> write_buffer(ctx.GetWriteBufferSize());
const Errno bsd_errno = GetSockNameImpl(fd, write_buffer);
const Network::Errno bsd_errno = GetSockNameImpl(fd, write_buffer);
ctx.WriteBuffer(write_buffer);
IPC::ResponseBuilder rb{ctx, 5};
rb.Push(ResultSuccess);
rb.Push<s32>(bsd_errno != Errno::SUCCESS ? -1 : 0);
rb.Push<s32>(bsd_errno != Network::Errno::SUCCESS ? -1 : 0);
rb.PushEnum(bsd_errno);
rb.Push<u32>(static_cast<u32>(write_buffer.size()));
}
@@ -278,21 +275,19 @@ void BSD::GetSockName(HLERequestContext& ctx) {
void BSD::GetSockOpt(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const s32 fd = rp.Pop<s32>();
const u32 level = rp.Pop<u32>();
const auto optname = static_cast<OptName>(rp.Pop<u32>());
const auto level = Network::SocketLevel(rp.Pop<u32>());
const auto optname = Network::OptName(rp.Pop<u32>());
std::vector<u8> optval(ctx.GetWriteBufferSize());
LOG_DEBUG(Service, "called. fd={} level={} optname={:#x} len={:#x}", fd, level, optname,
optval.size());
const Errno err = GetSockOptImpl(fd, level, optname, optval);
LOG_DEBUG(Service, "called. fd={} level={} optname={:#x} len={:#x}", fd, level, optname, optval.size());
const Network::Errno err = GetSockOptImpl(fd, level, optname, optval);
ctx.WriteBuffer(optval);
IPC::ResponseBuilder rb{ctx, 5};
rb.Push(ResultSuccess);
rb.Push<s32>(err == Errno::SUCCESS ? 0 : -1);
rb.Push<s32>(err == Network::Errno::SUCCESS ? 0 : -1);
rb.PushEnum(err);
rb.Push<u32>(static_cast<u32>(optval.size()));
}
@@ -315,7 +310,7 @@ void BSD::Fcntl(HLERequestContext& ctx) {
LOG_DEBUG(Service, "called. fd={} cmd={} arg={}", fd, cmd, arg);
const auto [ret, bsd_errno] = FcntlImpl(fd, static_cast<FcntlCmd>(cmd), arg);
const auto [ret, bsd_errno] = FcntlImpl(fd, Network::FcntlCmd(cmd), arg);
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(ResultSuccess);
@@ -327,13 +322,11 @@ void BSD::SetSockOpt(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const s32 fd = rp.Pop<s32>();
const u32 level = rp.Pop<u32>();
const OptName optname = static_cast<OptName>(rp.Pop<u32>());
const Network::SocketLevel level = Network::SocketLevel(rp.Pop<u32>());
const Network::OptName optname = Network::OptName(rp.Pop<u32>());
const auto optval = ctx.ReadBuffer();
LOG_DEBUG(Service, "called. fd={} level={} optname={:#x} optlen={}", fd, level,
static_cast<u32>(optname), optval.size());
LOG_DEBUG(Service, "called. fd={} level={} optname={:#x} optlen={}", fd, level, u32(optname), optval.size());
BuildErrnoResponse(ctx, SetSockOptImpl(fd, level, optname, optval));
}
@@ -454,7 +447,7 @@ void BSD::DuplicateSocket(HLERequestContext& ctx) {
struct OutputParameters {
s32 ret;
Errno bsd_errno;
Network::Errno bsd_errno;
};
static_assert(sizeof(OutputParameters) == 0x8);
@@ -468,10 +461,10 @@ void BSD::DuplicateSocket(HLERequestContext& ctx) {
if (auto* res = std::get_if<s32>(&res_v)) {
rb.PushRaw(OutputParameters{
.ret = *res,
.bsd_errno = Errno::SUCCESS,
.bsd_errno = Network::Errno::SUCCESS,
});
} else {
auto* err = std::get_if<Errno>(&res_v);
auto* err = std::get_if<Network::Errno>(&res_v);
rb.PushRaw(OutputParameters{
.ret = 0,
.bsd_errno = *err,
@@ -486,7 +479,7 @@ void BSD::EventFd(HLERequestContext& ctx) {
LOG_WARNING(Service, "(STUBBED) called. initval={}, flags={}", initval, flags);
BuildErrnoResponse(ctx, Errno::SUCCESS);
BuildErrnoResponse(ctx, Network::Errno::SUCCESS);
}
template <typename Work>
@@ -495,130 +488,130 @@ void BSD::ExecuteWork(HLERequestContext& ctx, Work work) {
work.Response(ctx);
}
std::pair<s32, Errno> BSD::SocketImpl(Domain domain, Type type, Protocol protocol) {
if (type == Type::SEQPACKET) {
std::pair<s32, Network::Errno> BSD::SocketImpl(Network::Domain domain, Network::Type type, Network::Protocol protocol) {
LOG_DEBUG(Network, "domain={},type={},protocol={}", u32(domain), u32(type), u32(protocol));
if (type == Network::Type::SEQPACKET) {
UNIMPLEMENTED_MSG("SOCK_SEQPACKET errno management");
} else if (type == Type::RAW && (domain != Domain::INET || protocol != Protocol::ICMP)) {
} else if (type == Network::Type::RAW && (domain != Network::Domain::INET || protocol != Network::Protocol::ICMP)) {
UNIMPLEMENTED_MSG("SOCK_RAW errno management");
}
[[maybe_unused]] const bool unk_flag = (static_cast<u32>(type) & 0x20000000) != 0;
[[maybe_unused]] const bool unk_flag = (u32(type) & 0x20000000) != 0;
UNIMPLEMENTED_IF_MSG(unk_flag, "Unknown flag in type");
type = static_cast<Type>(static_cast<u32>(type) & ~0x20000000);
type = Network::Type(u32(type) & ~0x20000000);
const s32 fd = FindFreeFileDescriptorHandle();
if (fd < 0) {
LOG_ERROR(Service, "No more file descriptors available");
return {-1, Errno::MFILE};
return {-1, Network::Errno::MFILE};
}
if (Settings::values.airplane_mode.GetValue() && IsConnectionBased(type)) {
LOG_ERROR(Service, "Airplane mode is enabled, cannot create socket");
file_descriptors[fd].reset();
return {-1, Network::Errno::NOTCONN};
}
file_descriptors[fd] = FileDescriptor{};
FileDescriptor& descriptor = *file_descriptors[fd];
// ENONMEM might be thrown here
LOG_INFO(Service, "New socket fd={},domain={},type={},prot={}", fd, domain, type, protocol);
LOG_INFO(Service, "New socket fd={}", fd);
// While room is important -- we need to remember ICMP takes priority over **everything else**
// TODO: rework this so proxy sockets can be done transparently? -- like what if i need
// to browse the internet while playing LDN or something stupid like that?
auto room_member = Network::GetRoomMember().lock();
if (room_member && room_member->IsConnected()) {
if (protocol == Network::Protocol::ICMP || protocol == Network::Protocol::ICMPV6) {
descriptor.socket = std::make_shared<Network::IcmpSocket>();
} else if (room_member && room_member->IsConnected()) {
descriptor.socket = std::make_shared<Network::ProxySocket>();
} else {
descriptor.socket = std::make_shared<Network::Socket>();
}
descriptor.socket->Initialize(Translate(domain), Translate(type), Translate(protocol));
auto const bsd_errno = descriptor.socket->Initialize(domain, type, protocol);
descriptor.is_connection_based = IsConnectionBased(type);
if (Settings::values.airplane_mode.GetValue() && descriptor.is_connection_based) {
LOG_ERROR(Service, "Airplane mode is enabled, cannot create socket");
return {-1, Errno::NOTCONN};
#ifdef _WIN32
if (descriptor.is_connection_based && descriptor.socket->fd == INVALID_SOCKET) {
#else
if (descriptor.is_connection_based && descriptor.socket->fd == Network::Socket::INVALID_SOCKET) {
#endif
file_descriptors[fd].reset();
return {-1, bsd_errno};
}
return {fd, Errno::SUCCESS};
return {fd, Network::Errno::SUCCESS};
}
std::pair<s32, Errno> BSD::PollImpl(std::vector<u8>& write_buffer, std::span<const u8> read_buffer,
s32 nfds, s32 timeout) {
std::pair<s32, Network::Errno> BSD::PollImpl(std::vector<u8>& write_buffer, std::span<const u8> read_buffer, s32 nfds, s32 timeout) {
LOG_DEBUG(Network, "nfds={},timeout={}", nfds, timeout);
if (nfds <= 0) {
// When no entries are provided, -1 is returned with errno zero
return {-1, Errno::SUCCESS};
return {-1, Network::Errno::SUCCESS};
}
if (read_buffer.size() < nfds * sizeof(PollFD)) {
return {-1, Errno::INVAL};
if (read_buffer.size() < nfds * sizeof(Network::PollFD)) {
return {-1, Network::Errno::INVAL};
}
if (write_buffer.size() < nfds * sizeof(PollFD)) {
return {-1, Errno::INVAL};
if (write_buffer.size() < nfds * sizeof(Network::PollFD)) {
return {-1, Network::Errno::INVAL};
}
std::vector<PollFD> fds(nfds);
std::memcpy(fds.data(), read_buffer.data(), nfds * sizeof(PollFD));
std::span<const Network::PollFD> in_fds(reinterpret_cast<const Network::PollFD*>(read_buffer.data()), nfds);
std::span<Network::PollFD> out_fds(reinterpret_cast<Network::PollFD*>(write_buffer.data()), nfds);
std::copy(in_fds.begin(), in_fds.end(), out_fds.begin());
if (timeout >= 0) {
const s64 seconds = timeout / 1000;
const u64 nanoseconds = 1'000'000 * (static_cast<u64>(timeout) % 1000);
const u64 nanoseconds = 1'000'000 * (u64(timeout) % 1000);
if (seconds < 0) {
return {-1, Errno::INVAL};
return {-1, Network::Errno::INVAL};
}
if (nanoseconds > 999'999'999) {
return {-1, Errno::INVAL};
return {-1, Network::Errno::INVAL};
}
} else if (timeout != -1) {
return {-1, Errno::INVAL};
return {-1, Network::Errno::INVAL};
}
for (PollFD& pollfd : fds) {
ASSERT(False(pollfd.revents));
if (pollfd.fd > static_cast<s32>(MAX_FD) || pollfd.fd < 0) {
LOG_ERROR(Service, "File descriptor handle={} is invalid", pollfd.fd);
pollfd.revents = PollEvents{};
return {0, Errno::SUCCESS};
}
const std::optional<FileDescriptor>& descriptor = file_descriptors[pollfd.fd];
if (!descriptor) {
LOG_TRACE(Service, "File descriptor handle={} is not allocated", pollfd.fd);
pollfd.revents = PollEvents::Nval;
return {0, Errno::SUCCESS};
for (size_t i = 0; i < in_fds.size(); ++i) {
ASSERT(out_fds[i].fd == in_fds[i].fd && False(in_fds[i].revents));
if (!IsFileDescriptorValid(in_fds[i].fd)) {
out_fds[i].revents = {};
if (!file_descriptors[in_fds[i].fd])
out_fds[i].revents = Network::PollEvents::NVAL;
return {0, Network::Errno::SUCCESS};
}
}
std::vector<Network::PollFD> host_pollfds(fds.size());
std::transform(fds.begin(), fds.end(), host_pollfds.begin(), [](PollFD pollfd) {
Network::PollFD result;
result.socket = file_descriptors[pollfd.fd]->socket.get();
result.events = Translate(pollfd.events);
result.revents = Network::PollEvents{};
std::vector<Network::HostPollFD> host_pollfds(in_fds.size());
std::transform(in_fds.begin(), in_fds.end(), host_pollfds.begin(), [](auto const e) {
Network::HostPollFD result{};
result.socket = file_descriptors[e.fd]->socket.get();
result.events = e.events;
result.revents = {};
return result;
});
const auto result = Network::Poll(host_pollfds, timeout);
const size_t num = host_pollfds.size();
for (size_t i = 0; i < num; ++i) {
fds[i].revents = Translate(host_pollfds[i].revents);
}
std::memcpy(write_buffer.data(), fds.data(), nfds * sizeof(PollFD));
return Translate(result);
auto const res = Network::Poll(host_pollfds, timeout);
for (size_t i = 0; i < in_fds.size(); ++i)
out_fds[i].revents = host_pollfds[i].revents;
return res;
}
std::pair<s32, Errno> BSD::AcceptImpl(s32 fd, std::vector<u8>& write_buffer) {
std::pair<s32, Network::Errno> BSD::AcceptImpl(s32 fd, std::vector<u8>& write_buffer) {
LOG_DEBUG(Network, "fd={}", fd);
if (!IsFileDescriptorValid(fd)) {
return {-1, Errno::BADF};
return {-1, Network::Errno::BADF};
}
const s32 new_fd = FindFreeFileDescriptorHandle();
if (new_fd < 0) {
LOG_ERROR(Service, "No more file descriptors available");
return {-1, Errno::MFILE};
return {-1, Network::Errno::MFILE};
}
FileDescriptor& descriptor = *file_descriptors[fd];
auto [result, bsd_errno] = descriptor.socket->Accept();
if (bsd_errno != Network::Errno::SUCCESS) {
return {-1, Translate(bsd_errno)};
return {-1, bsd_errno};
}
file_descriptors[new_fd] = FileDescriptor{};
@@ -626,267 +619,218 @@ std::pair<s32, Errno> BSD::AcceptImpl(s32 fd, std::vector<u8>& write_buffer) {
new_descriptor.socket = std::move(result.socket);
new_descriptor.is_connection_based = descriptor.is_connection_based;
const SockAddrIn guest_addr_in = Translate(result.sockaddr_in);
PutValue(write_buffer, guest_addr_in);
return {new_fd, Errno::SUCCESS};
PutValue(write_buffer, result.sockaddr_in);
return {new_fd, Network::Errno::SUCCESS};
}
Errno BSD::BindImpl(s32 fd, std::span<const u8> addr) {
Network::Errno BSD::BindImpl(s32 fd, std::span<const u8> addr) {
LOG_DEBUG(Network, "fd={}", fd);
if (!IsFileDescriptorValid(fd)) {
return Errno::BADF;
return Network::Errno::BADF;
}
ASSERT(addr.size() >= 16);
if (!file_descriptors[fd]->socket) {
LOG_WARNING(Service, "Uninitialized socket");
return Errno::BADF;
return Network::Errno::BADF;
}
auto addr_in = GetValue<SockAddrIn>(addr);
return Translate(file_descriptors[fd]->socket->Bind(Translate(addr_in)));
auto addr_in = GetValue<Network::SockAddrIn>(addr);
return file_descriptors[fd]->socket->Bind(addr_in);
}
Errno BSD::ConnectImpl(s32 fd, std::span<const u8> addr) {
Network::Errno BSD::ConnectImpl(s32 fd, std::span<const u8> addr) {
LOG_DEBUG(Network, "fd={}", fd);
if (!IsFileDescriptorValid(fd)) {
return Errno::BADF;
return Network::Errno::BADF;
}
ASSERT(addr.size() >= 16);
if (!file_descriptors[fd]->socket) {
LOG_WARNING(Service, "Uninitialized socket");
return Errno::BADF;
return Network::Errno::BADF;
}
auto addr_in = GetValue<SockAddrIn>(addr);
const Errno result = Translate(file_descriptors[fd]->socket->Connect(Translate(addr_in)));
if (result == Errno::ISCONN) {
auto addr_in = GetValue<Network::SockAddrIn>(addr);
const Network::Errno result = file_descriptors[fd]->socket->Connect(addr_in);
if (result == Network::Errno::ISCONN) {
LOG_DEBUG(Service, "returned ISCONN - socket already connected");
return Errno::SUCCESS;
return Network::Errno::SUCCESS;
}
return result;
}
Errno BSD::GetPeerNameImpl(s32 fd, std::vector<u8>& write_buffer) {
Network::Errno BSD::GetPeerNameImpl(s32 fd, std::vector<u8>& write_buffer) {
LOG_DEBUG(Network, "fd={}", fd);
if (!IsFileDescriptorValid(fd)) {
return Errno::BADF;
return Network::Errno::BADF;
}
if (!file_descriptors[fd]->socket) {
LOG_WARNING(Service, "Uninitialized socket");
return Errno::BADF;
return Network::Errno::BADF;
}
const auto [addr_in, bsd_errno] = file_descriptors[fd]->socket->GetPeerName();
if (bsd_errno != Network::Errno::SUCCESS) {
return Translate(bsd_errno);
return bsd_errno;
}
const SockAddrIn guest_addrin = Translate(addr_in);
ASSERT(write_buffer.size() >= sizeof(guest_addrin));
write_buffer.resize(sizeof(guest_addrin));
PutValue(write_buffer, guest_addrin);
return Translate(bsd_errno);
ASSERT(write_buffer.size() >= addr_in.len);
write_buffer.resize(addr_in.len);
PutValue(write_buffer, addr_in);
return bsd_errno;
}
Errno BSD::GetSockNameImpl(s32 fd, std::vector<u8>& write_buffer) {
Network::Errno BSD::GetSockNameImpl(s32 fd, std::vector<u8>& write_buffer) {
LOG_DEBUG(Network, "fd={}", fd);
if (!IsFileDescriptorValid(fd)) {
return Errno::BADF;
return Network::Errno::BADF;
}
if (!file_descriptors[fd]->socket) {
LOG_WARNING(Service, "Uninitialized socket");
return Errno::BADF;
return Network::Errno::BADF;
}
const auto [addr_in, bsd_errno] = file_descriptors[fd]->socket->GetSockName();
if (bsd_errno != Network::Errno::SUCCESS) {
return Translate(bsd_errno);
return bsd_errno;
}
const SockAddrIn guest_addrin = Translate(addr_in);
ASSERT(write_buffer.size() >= sizeof(guest_addrin));
write_buffer.resize(sizeof(guest_addrin));
PutValue(write_buffer, guest_addrin);
return Translate(bsd_errno);
ASSERT(write_buffer.size() >= addr_in.len);
write_buffer.resize(addr_in.len);
PutValue(write_buffer, addr_in);
return bsd_errno;
}
Errno BSD::ListenImpl(s32 fd, s32 backlog) {
Network::Errno BSD::ListenImpl(s32 fd, s32 backlog) {
LOG_DEBUG(Network, "fd={},backlog={}", fd, backlog);
if (!IsFileDescriptorValid(fd)) {
return Errno::BADF;
return Network::Errno::BADF;
}
if (!file_descriptors[fd]->socket) {
LOG_WARNING(Service, "Uninitialized socket");
return Errno::BADF;
return Network::Errno::BADF;
}
return Translate(file_descriptors[fd]->socket->Listen(backlog));
return file_descriptors[fd]->socket->Listen(backlog);
}
std::pair<s32, Errno> BSD::FcntlImpl(s32 fd, FcntlCmd cmd, s32 arg) {
std::pair<s32, Network::Errno> BSD::FcntlImpl(s32 fd, Network::FcntlCmd cmd, s32 arg) {
LOG_DEBUG(Network, "fd={},cmd={},arg={}", fd, u32(cmd), arg);
if (!IsFileDescriptorValid(fd)) {
return {-1, Errno::BADF};
return {-1, Network::Errno::BADF};
}
if (!file_descriptors[fd]->socket) {
LOG_WARNING(Service, "Uninitialized socket");
return {-1, Errno::BADF};
return {-1, Network::Errno::BADF};
}
FileDescriptor& descriptor = *file_descriptors[fd];
switch (cmd) {
case FcntlCmd::GETFL:
case Network::FcntlCmd::GETFL:
ASSERT(arg == 0);
return {descriptor.flags, Errno::SUCCESS};
case FcntlCmd::SETFL: {
const bool enable = (arg & Network::FLAG_O_NONBLOCK) != 0;
const Errno bsd_errno = Translate(descriptor.socket->SetNonBlock(enable));
if (bsd_errno != Errno::SUCCESS) {
return {descriptor.flags, Network::Errno::SUCCESS};
case Network::FcntlCmd::SETFL: {
const bool enable = (arg & u32(Network::FcntlFlags::NONBLOCK_NX)) != 0;
const Network::Errno bsd_errno = descriptor.socket->SetNonBlock(enable);
if (bsd_errno != Network::Errno::SUCCESS) {
return {-1, bsd_errno};
}
descriptor.flags = arg;
return {0, Errno::SUCCESS};
return {0, Network::Errno::SUCCESS};
}
default:
UNIMPLEMENTED_MSG("Unimplemented cmd={}", cmd);
return {-1, Errno::SUCCESS};
return {-1, Network::Errno::SUCCESS};
}
}
Errno BSD::GetSockOptImpl(s32 fd, u32 level, OptName optname, std::vector<u8>& optval) {
Network::Errno BSD::GetSockOptImpl(s32 fd, Network::SocketLevel level, Network::OptName optname, std::vector<u8>& optval) {
LOG_DEBUG(Network, "fd={},level={},optname={}", fd, u32(level), u32(optname));
if (!IsFileDescriptorValid(fd)) {
return Errno::BADF;
return Network::Errno::BADF;
}
if (!file_descriptors[fd]->socket) {
LOG_WARNING(Service, "Uninitialized socket");
return Errno::BADF;
return Network::Errno::BADF;
}
if (level != static_cast<u32>(SocketLevel::SOCKET)) {
UNIMPLEMENTED_MSG("Unknown getsockopt level");
return Errno::SUCCESS;
if (level != Network::SocketLevel::SOCKET) {
LOG_WARNING(Service, "(stubbed) level fd={}, level={}, optname={}", fd, level, optname);
}
Network::SocketBase* const socket = file_descriptors[fd]->socket.get();
switch (optname) {
case OptName::ERROR_: {
case Network::OptName::ERROR_: {
auto [pending_err, getsockopt_err] = socket->GetPendingError();
if (getsockopt_err == Network::Errno::SUCCESS) {
Errno translated_pending_err = Translate(pending_err);
ASSERT_OR_EXECUTE_MSG(
optval.size() == sizeof(Errno), { return Errno::INVAL; },
optval.size() == sizeof(Network::Errno), { return Network::Errno::INVAL; },
"Incorrect getsockopt option size");
optval.resize(sizeof(Errno));
PutValue(optval, translated_pending_err);
optval.resize(sizeof(Network::Errno));
PutValue(optval, pending_err);
}
return Translate(getsockopt_err);
return getsockopt_err;
}
default:
UNIMPLEMENTED_MSG("Unimplemented optname={}", optname);
return Errno::SUCCESS;
return Network::Errno::SUCCESS;
}
}
Errno BSD::SetSockOptImpl(s32 fd, u32 level, OptName optname, std::span<const u8> optval) {
Network::Errno BSD::SetSockOptImpl(s32 fd, Network::SocketLevel level, Network::OptName optname, std::span<const u8> optval) {
LOG_DEBUG(Service, "fd={},level={},optname={}", fd, level, optname);
if (!IsFileDescriptorValid(fd)) {
return Errno::BADF;
return Network::Errno::BADF;
}
if (!file_descriptors[fd]->socket) {
LOG_WARNING(Service, "Uninitialized socket");
return Errno::BADF;
}
if (level != static_cast<u32>(SocketLevel::SOCKET)) {
LOG_WARNING(Service, "(STUBBED) setsockopt with level={}, optname={}", level, optname);
return Errno::SUCCESS;
return Network::Errno::BADF;
}
Network::SocketBase* const socket = file_descriptors[fd]->socket.get();
if (optname == OptName::LINGER) {
ASSERT(optval.size() == sizeof(Linger));
auto linger = GetValue<Linger>(optval);
ASSERT(linger.onoff == 0 || linger.onoff == 1);
return Translate(socket->SetLinger(linger.onoff != 0, linger.linger));
}
ASSERT(optval.size() == sizeof(u32));
auto value = GetValue<u32>(optval);
switch (optname) {
case OptName::REUSEADDR:
ASSERT(value == 0 || value == 1);
return Translate(socket->SetReuseAddr(value != 0));
case OptName::KEEPALIVE:
ASSERT(value == 0 || value == 1);
return Translate(socket->SetKeepAlive(value != 0));
case OptName::BROADCAST:
ASSERT(value == 0 || value == 1);
return Translate(socket->SetBroadcast(value != 0));
case OptName::SNDBUF:
return Translate(socket->SetSndBuf(value));
case OptName::RCVBUF:
return Translate(socket->SetRcvBuf(value));
case OptName::SNDTIMEO:
return Translate(socket->SetSndTimeo(value));
case OptName::RCVTIMEO:
return Translate(socket->SetRcvTimeo(value));
case OptName::NOSIGPIPE:
LOG_WARNING(Service, "(STUBBED) setting NOSIGPIPE to {}", value);
return Errno::SUCCESS;
default:
UNIMPLEMENTED_MSG("Unimplemented optname={}", optname);
return Errno::SUCCESS;
}
return socket->SetSockOpt(level, optname, optval);
}
Errno BSD::ShutdownImpl(s32 fd, s32 how) {
Network::Errno BSD::ShutdownImpl(s32 fd, s32 how) {
LOG_DEBUG(Network, "fd={},how={}", fd, how);
if (!IsFileDescriptorValid(fd)) {
return Errno::BADF;
return Network::Errno::BADF;
}
if (!file_descriptors[fd]->socket) {
LOG_WARNING(Service, "Uninitialized socket");
return Errno::BADF;
return Network::Errno::BADF;
}
const Network::ShutdownHow host_how = Translate(static_cast<ShutdownHow>(how));
return Translate(file_descriptors[fd]->socket->Shutdown(host_how));
return file_descriptors[fd]->socket->Shutdown(Network::ShutdownHow(how));
}
std::pair<s32, Errno> BSD::RecvImpl(s32 fd, u32 flags, std::vector<u8>& message) {
std::pair<s32, Network::Errno> BSD::RecvImpl(s32 fd, u32 flags, std::vector<u8>& message) {
LOG_DEBUG(Network, "fd={},flags={}", fd, flags);
if (!IsFileDescriptorValid(fd)) {
return {-1, Errno::BADF};
return {-1, Network::Errno::BADF};
}
FileDescriptor& descriptor = *file_descriptors[fd];
// Apply flags
using Network::FLAG_MSG_DONTWAIT;
using Network::FLAG_O_NONBLOCK;
if ((flags & FLAG_MSG_DONTWAIT) != 0) {
flags &= ~FLAG_MSG_DONTWAIT;
if ((descriptor.flags & FLAG_O_NONBLOCK) == 0) {
if ((flags & u32(Network::MsgOpt::DONTWAIT)) != 0) {
flags &= ~u32(Network::MsgOpt::DONTWAIT);
if ((descriptor.flags & u32(Network::FcntlFlags::NONBLOCK_NX)) == 0) {
descriptor.socket->SetNonBlock(true);
}
}
const auto [ret, bsd_errno] = Translate(descriptor.socket->Recv(flags, message));
const auto [ret, bsd_errno] = descriptor.socket->Recv(flags, message);
// Restore original state
if ((descriptor.flags & FLAG_O_NONBLOCK) == 0) {
if ((descriptor.flags & u32(Network::FcntlFlags::NONBLOCK_NX)) == 0)
descriptor.socket->SetNonBlock(false);
}
return {ret, bsd_errno};
}
std::pair<s32, Errno> BSD::RecvFromImpl(s32 fd, u32 flags, std::vector<u8>& message,
std::vector<u8>& addr) {
std::pair<s32, Network::Errno> BSD::RecvFromImpl(s32 fd, u32 flags, std::vector<u8>& message, std::vector<u8>& addr) {
LOG_DEBUG(Network, "fd={},flags={}", fd, flags);
if (!IsFileDescriptorValid(fd)) {
return {-1, Errno::BADF};
return {-1, Network::Errno::BADF};
}
FileDescriptor& descriptor = *file_descriptors[fd];
@@ -901,19 +845,17 @@ std::pair<s32, Errno> BSD::RecvFromImpl(s32 fd, u32 flags, std::vector<u8>& mess
}
// Apply flags
using Network::FLAG_MSG_DONTWAIT;
using Network::FLAG_O_NONBLOCK;
if ((flags & FLAG_MSG_DONTWAIT) != 0) {
flags &= ~FLAG_MSG_DONTWAIT;
if ((descriptor.flags & FLAG_O_NONBLOCK) == 0) {
if ((flags & u32(Network::MsgOpt::DONTWAIT)) != 0) {
flags &= ~u32(Network::MsgOpt::DONTWAIT);
if ((descriptor.flags & u32(Network::FcntlFlags::NONBLOCK_NX)) == 0) {
descriptor.socket->SetNonBlock(true);
}
}
const auto [ret, bsd_errno] = Translate(descriptor.socket->RecvFrom(flags, message, p_addr_in));
const auto [ret, bsd_errno] = descriptor.socket->RecvFrom(flags, message, p_addr_in);
// Restore original state
if ((descriptor.flags & FLAG_O_NONBLOCK) == 0) {
if ((descriptor.flags & u32(Network::FcntlFlags::NONBLOCK_NX)) == 0) {
descriptor.socket->SetNonBlock(false);
}
@@ -922,58 +864,59 @@ std::pair<s32, Errno> BSD::RecvFromImpl(s32 fd, u32 flags, std::vector<u8>& mess
addr.clear();
} else {
ASSERT(addr.size() >= 16);
const SockAddrIn result = Translate(addr_in);
PutValue(addr, result);
PutValue(addr, addr_in);
}
}
return {ret, bsd_errno};
}
std::pair<s32, Errno> BSD::SendImpl(s32 fd, u32 flags, std::span<const u8> message) {
std::pair<s32, Network::Errno> BSD::SendImpl(s32 fd, u32 flags, std::span<const u8> message) {
LOG_DEBUG(Network, "fd={},flags={}", fd, flags);
if (!IsFileDescriptorValid(fd)) {
return {-1, Errno::BADF};
return {-1, Network::Errno::BADF};
}
if (!file_descriptors[fd]->socket) {
LOG_WARNING(Service, "Uninitialized socket");
return {-1, Errno::BADF};
return {-1, Network::Errno::BADF};
}
return Translate(file_descriptors[fd]->socket->Send(message, flags));
return file_descriptors[fd]->socket->Send(message, flags);
}
std::pair<s32, Errno> BSD::SendToImpl(s32 fd, u32 flags, std::span<const u8> message,
std::span<const u8> addr) {
std::pair<s32, Network::Errno> BSD::SendToImpl(s32 fd, u32 flags, std::span<const u8> message, std::span<const u8> addr) {
LOG_DEBUG(Network, "fd={},flags={}", fd, flags);
if (!IsFileDescriptorValid(fd)) {
return {-1, Errno::BADF};
return {-1, Network::Errno::BADF};
}
if (!file_descriptors[fd]->socket) {
LOG_WARNING(Service, "Uninitialized socket");
return {-1, Errno::BADF};
return {-1, Network::Errno::BADF};
}
Network::SockAddrIn addr_in;
Network::SockAddrIn addr_in{};
Network::SockAddrIn* p_addr_in = nullptr;
if (!addr.empty()) {
ASSERT(addr.size() >= 16);
auto guest_addr_in = GetValue<SockAddrIn>(addr);
addr_in = Translate(guest_addr_in);
auto guest_addr_in = GetValue<Network::SockAddrIn>(addr);
addr_in = guest_addr_in;
p_addr_in = &addr_in;
}
return Translate(file_descriptors[fd]->socket->SendTo(flags, message, p_addr_in));
return file_descriptors[fd]->socket->SendTo(flags, message, p_addr_in);
}
Errno BSD::CloseImpl(s32 fd) {
Network::Errno BSD::CloseImpl(s32 fd) {
LOG_DEBUG(Network, "fd={}", fd);
if (!IsFileDescriptorValid(fd)) {
return Errno::BADF;
return Network::Errno::BADF;
}
if (!file_descriptors[fd]->socket) {
LOG_WARNING(Service, "Uninitialized socket");
return Errno::BADF;
return Network::Errno::BADF;
}
const Errno bsd_errno = Translate(file_descriptors[fd]->socket->Close());
if (bsd_errno != Errno::SUCCESS) {
auto const bsd_errno = file_descriptors[fd]->socket->Close();
if (bsd_errno != Network::Errno::SUCCESS) {
return bsd_errno;
}
@@ -983,15 +926,16 @@ Errno BSD::CloseImpl(s32 fd) {
return bsd_errno;
}
std::variant<s32, Errno> BSD::DuplicateSocketImpl(s32 fd) {
std::variant<s32, Network::Errno> BSD::DuplicateSocketImpl(s32 fd) {
LOG_DEBUG(Network, "fd={}", fd);
if (!IsFileDescriptorValid(fd)) {
return Errno::BADF;
return Network::Errno::BADF;
}
const s32 new_fd = FindFreeFileDescriptorHandle();
if (new_fd < 0) {
if (!IsFileDescriptorValid(new_fd)) {
LOG_ERROR(Service, "No more file descriptors available");
return Errno::MFILE;
return Network::Errno::MFILE;
}
file_descriptors[new_fd] = FileDescriptor{
@@ -1003,6 +947,7 @@ std::variant<s32, Errno> BSD::DuplicateSocketImpl(s32 fd) {
}
std::optional<std::shared_ptr<Network::SocketBase>> BSD::GetSocket(s32 fd) {
LOG_DEBUG(Network, "fd={}", fd);
if (!IsFileDescriptorValid(fd)) {
return std::nullopt;
}
@@ -1014,41 +959,40 @@ std::optional<std::shared_ptr<Network::SocketBase>> BSD::GetSocket(s32 fd) {
}
s32 BSD::FindFreeFileDescriptorHandle() noexcept {
for (s32 fd = 0; fd < static_cast<s32>(file_descriptors.size()); ++fd) {
if (!file_descriptors[fd]) {
// first three file descriptors are reserved for:
// STDOUT_FILENO, STDIN_FILENO and STDERR_FILENO
for (s32 fd = 0; fd < s32(file_descriptors.size()); ++fd)
if (!file_descriptors[fd])
return fd;
}
}
return -1;
}
bool BSD::IsFileDescriptorValid(s32 fd) const noexcept {
if (fd > static_cast<s32>(MAX_FD) || fd < 0) {
LOG_ERROR(Service, "Invalid file descriptor handle={}", fd);
if (fd < 0 || fd >= s32(file_descriptors.size())) {
LOG_ERROR(Service, "Invalid handle={}", fd);
return false;
}
if (!file_descriptors[fd]) {
LOG_ERROR(Service, "File descriptor handle={} is not allocated", fd);
LOG_ERROR(Service, "handle={} is not allocated", fd);
return false;
}
return true;
}
void BSD::BuildErrnoResponse(HLERequestContext& ctx, Errno bsd_errno) const noexcept {
void BSD::BuildErrnoResponse(HLERequestContext& ctx, Network::Errno bsd_errno) const noexcept {
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(ResultSuccess);
rb.Push<s32>(bsd_errno == Errno::SUCCESS ? 0 : -1);
rb.Push<s32>(bsd_errno == Network::Errno::SUCCESS ? 0 : -1);
rb.PushEnum(bsd_errno);
}
void BSD::OnProxyPacketReceived(const Network::ProxyPacket& packet) {
for (auto& optional_descriptor : file_descriptors) {
if (!optional_descriptor.has_value()) {
continue;
if (optional_descriptor.has_value()) {
FileDescriptor& descriptor = *optional_descriptor;
descriptor.socket.get()->HandleProxyPacket(packet);
}
FileDescriptor& descriptor = *optional_descriptor;
descriptor.socket.get()->HandleProxyPacket(packet);
}
}
+27 -27
View File
@@ -11,7 +11,7 @@
#include <variant>
#include "common/common_types.h"
#include "common/socket_types.h"
#include "core/internal_network/socket_types.h"
#include "core/hle/service/service.h"
#include "core/hle/service/sockets/sockets.h"
#include "network/network.h"
@@ -35,8 +35,8 @@ public:
// These methods are called from SSL; the first two are also called from
// this class for the corresponding IPC methods.
// On the real device, the SSL service makes IPC calls to this service.
std::variant<s32, Errno> DuplicateSocketImpl(s32 fd);
Errno CloseImpl(s32 fd);
std::variant<s32, Network::Errno> DuplicateSocketImpl(s32 fd);
Network::Errno CloseImpl(s32 fd);
std::optional<std::shared_ptr<Network::SocketBase>> GetSocket(s32 fd);
private:
@@ -58,7 +58,7 @@ private:
std::span<const u8> read_buffer;
std::vector<u8> write_buffer;
s32 ret{};
Errno bsd_errno{};
Network::Errno bsd_errno{};
};
struct AcceptWork {
@@ -68,7 +68,7 @@ private:
s32 fd;
std::vector<u8> write_buffer;
s32 ret{};
Errno bsd_errno{};
Network::Errno bsd_errno{};
};
struct ConnectWork {
@@ -77,7 +77,7 @@ private:
s32 fd;
std::span<const u8> addr;
Errno bsd_errno{};
Network::Errno bsd_errno{};
};
struct RecvWork {
@@ -88,7 +88,7 @@ private:
u32 flags;
std::vector<u8> message;
s32 ret{};
Errno bsd_errno{};
Network::Errno bsd_errno{};
};
struct RecvFromWork {
@@ -100,7 +100,7 @@ private:
std::vector<u8> message;
std::vector<u8> addr;
s32 ret{};
Errno bsd_errno{};
Network::Errno bsd_errno{};
};
struct SendWork {
@@ -111,7 +111,7 @@ private:
u32 flags;
std::span<const u8> message;
s32 ret{};
Errno bsd_errno{};
Network::Errno bsd_errno{};
};
struct SendToWork {
@@ -123,7 +123,7 @@ private:
std::span<const u8> message;
std::span<const u8> addr;
s32 ret{};
Errno bsd_errno{};
Network::Errno bsd_errno{};
};
void RegisterClient(HLERequestContext& ctx);
@@ -154,30 +154,30 @@ private:
template <typename Work>
void ExecuteWork(HLERequestContext& ctx, Work work);
std::pair<s32, Errno> SocketImpl(Domain domain, Type type, Protocol protocol);
std::pair<s32, Errno> PollImpl(std::vector<u8>& write_buffer, std::span<const u8> read_buffer,
std::pair<s32, Network::Errno> SocketImpl(Network::Domain domain, Network::Type type, Network::Protocol protocol);
std::pair<s32, Network::Errno> PollImpl(std::vector<u8>& write_buffer, std::span<const u8> read_buffer,
s32 nfds, s32 timeout);
std::pair<s32, Errno> AcceptImpl(s32 fd, std::vector<u8>& write_buffer);
Errno BindImpl(s32 fd, std::span<const u8> addr);
Errno ConnectImpl(s32 fd, std::span<const u8> addr);
Errno GetPeerNameImpl(s32 fd, std::vector<u8>& write_buffer);
Errno GetSockNameImpl(s32 fd, std::vector<u8>& write_buffer);
Errno ListenImpl(s32 fd, s32 backlog);
std::pair<s32, Errno> FcntlImpl(s32 fd, FcntlCmd cmd, s32 arg);
Errno GetSockOptImpl(s32 fd, u32 level, OptName optname, std::vector<u8>& optval);
Errno SetSockOptImpl(s32 fd, u32 level, OptName optname, std::span<const u8> optval);
Errno ShutdownImpl(s32 fd, s32 how);
std::pair<s32, Errno> RecvImpl(s32 fd, u32 flags, std::vector<u8>& message);
std::pair<s32, Errno> RecvFromImpl(s32 fd, u32 flags, std::vector<u8>& message,
std::pair<s32, Network::Errno> AcceptImpl(s32 fd, std::vector<u8>& write_buffer);
Network::Errno BindImpl(s32 fd, std::span<const u8> addr);
Network::Errno ConnectImpl(s32 fd, std::span<const u8> addr);
Network::Errno GetPeerNameImpl(s32 fd, std::vector<u8>& write_buffer);
Network::Errno GetSockNameImpl(s32 fd, std::vector<u8>& write_buffer);
Network::Errno ListenImpl(s32 fd, s32 backlog);
std::pair<s32, Network::Errno> FcntlImpl(s32 fd, Network::FcntlCmd cmd, s32 arg);
Network::Errno GetSockOptImpl(s32 fd, Network::SocketLevel level, Network::OptName optname, std::vector<u8>& optval);
Network::Errno SetSockOptImpl(s32 fd, Network::SocketLevel level, Network::OptName optname, std::span<const u8> optval);
Network::Errno ShutdownImpl(s32 fd, s32 how);
std::pair<s32, Network::Errno> RecvImpl(s32 fd, u32 flags, std::vector<u8>& message);
std::pair<s32, Network::Errno> RecvFromImpl(s32 fd, u32 flags, std::vector<u8>& message,
std::vector<u8>& addr);
std::pair<s32, Errno> SendImpl(s32 fd, u32 flags, std::span<const u8> message);
std::pair<s32, Errno> SendToImpl(s32 fd, u32 flags, std::span<const u8> message,
std::pair<s32, Network::Errno> SendImpl(s32 fd, u32 flags, std::span<const u8> message);
std::pair<s32, Network::Errno> SendToImpl(s32 fd, u32 flags, std::span<const u8> message,
std::span<const u8> addr);
s32 FindFreeFileDescriptorHandle() noexcept;
bool IsFileDescriptorValid(s32 fd) const noexcept;
void BuildErrnoResponse(HLERequestContext& ctx, Errno bsd_errno) const noexcept;
void BuildErrnoResponse(HLERequestContext& ctx, Network::Errno bsd_errno) const noexcept;
static inline std::array<std::optional<FileDescriptor>, MAX_FD> file_descriptors{};
+40 -44
View File
@@ -102,39 +102,39 @@ static bool IsBlockedHost(const std::string& host) {
[&host](const std::string& domain) { return host.find(domain) != std::string::npos; });
}
static NetDbError GetAddrInfoErrorToNetDbError(GetAddrInfoError result) {
static NetDbError GetAddrInfoErrorToNetDbError(Network::GetAddrInfoError result) {
// These combinations have been verified on console (but are not
// exhaustive).
switch (result) {
case GetAddrInfoError::SUCCESS:
case Network::GetAddrInfoError::SUCCESS:
return NetDbError::Success;
case GetAddrInfoError::AGAIN:
case Network::GetAddrInfoError::AGAIN:
return NetDbError::TryAgain;
case GetAddrInfoError::NODATA:
case Network::GetAddrInfoError::NODATA:
return NetDbError::HostNotFound;
case GetAddrInfoError::SERVICE:
case Network::GetAddrInfoError::SERVICE:
return NetDbError::Success;
default:
return NetDbError::HostNotFound;
}
}
static Errno GetAddrInfoErrorToErrno(GetAddrInfoError result) {
static Network::Errno GetAddrInfoErrorToErrno(Network::GetAddrInfoError result) {
// These combinations have been verified on console (but are not
// exhaustive).
switch (result) {
case GetAddrInfoError::SUCCESS:
case Network::GetAddrInfoError::SUCCESS:
// Note: Sometimes a successful lookup sets errno to EADDRNOTAVAIL for
// some reason, but that doesn't seem useful to implement.
return Errno::SUCCESS;
case GetAddrInfoError::AGAIN:
return Errno::SUCCESS;
case GetAddrInfoError::NODATA:
return Errno::SUCCESS;
case GetAddrInfoError::SERVICE:
return Errno::INVAL;
return Network::Errno::SUCCESS;
case Network::GetAddrInfoError::AGAIN:
return Network::Errno::SUCCESS;
case Network::GetAddrInfoError::NODATA:
return Network::Errno::SUCCESS;
case Network::GetAddrInfoError::SERVICE:
return Network::Errno::INVAL;
default:
return Errno::SUCCESS;
return Network::Errno::SUCCESS;
}
}
@@ -155,9 +155,7 @@ static void AppendNulTerminated(std::vector<u8>& vec, std::string_view str) {
// host's gethostbyname, because it simplifies portability: e.g., getaddrinfo
// behaves the same on Unix and Windows, unlike gethostbyname where Windows
// doesn't implement h_errno.
static std::vector<u8> SerializeAddrInfoAsHostEnt(const std::vector<Network::AddrInfo>& vec,
std::string_view host) {
static std::vector<u8> SerializeAddrInfoAsHostEnt(std::span<const Network::AddrInfo> vec, std::string_view host) {
std::vector<u8> data;
// h_name: use the input hostname (append nul-terminated)
AppendNulTerminated(data, host);
@@ -165,12 +163,12 @@ static std::vector<u8> SerializeAddrInfoAsHostEnt(const std::vector<Network::Add
Append<u32_be>(data, 0); // count of h_aliases
// (If the count were nonzero, the aliases would be appended as nul-terminated here.)
Append<u16_be>(data, static_cast<u16>(Domain::INET)); // h_addrtype
Append<u16_be>(data, u16(Network::Domain::INET)); // h_addrtype
Append<u16_be>(data, sizeof(Network::IPv4Address)); // h_length
// h_addr_list:
size_t count = vec.size();
ASSERT(count <= UINT32_MAX);
Append<u32_be>(data, static_cast<uint32_t>(count));
Append<u32_be>(data, u32(count));
for (const Network::AddrInfo& addrinfo : vec) {
// On the Switch, this is passed through htonl despite already being
// big-endian, so it ends up as little-endian.
@@ -182,7 +180,7 @@ static std::vector<u8> SerializeAddrInfoAsHostEnt(const std::vector<Network::Add
return data;
}
static std::pair<u32, GetAddrInfoError> GetHostByNameRequestImpl(HLERequestContext& ctx) {
static std::pair<u32, Network::GetAddrInfoError> GetHostByNameRequestImpl(HLERequestContext& ctx) {
struct InputParameters {
u8 use_nsd_resolve;
u32 cancel_handle;
@@ -205,7 +203,7 @@ static std::pair<u32, GetAddrInfoError> GetHostByNameRequestImpl(HLERequestConte
// Prevent resolution of Nintendo servers
if (IsBlockedHost(host)) {
LOG_WARNING(Network, "Resolution of hostname {} requested, returning EAI_AGAIN", host);
return {0, GetAddrInfoError::AGAIN};
return {0, Network::GetAddrInfoError::AGAIN};
}
auto res_v = Network::GetAddressInfo(host, /*service*/ std::nullopt);
@@ -213,10 +211,10 @@ static std::pair<u32, GetAddrInfoError> GetHostByNameRequestImpl(HLERequestConte
const std::vector<u8> data = SerializeAddrInfoAsHostEnt(*res, host);
const u32 data_size = u32(data.size());
ctx.WriteBuffer(data, 0);
return {data_size, GetAddrInfoError::SUCCESS};
return {data_size, Network::GetAddrInfoError::SUCCESS};
}
auto* err = std::get_if<Network::GetAddrInfoError>(&res_v);
return {0, Translate(*err)};
return {0, *err};
}
void SFDNSRES::GetHostByNameRequest(HLERequestContext& ctx) {
@@ -224,7 +222,7 @@ void SFDNSRES::GetHostByNameRequest(HLERequestContext& ctx) {
struct OutputParameters {
NetDbError netdb_error;
Errno bsd_errno;
Network::Errno bsd_errno;
u32 data_size;
};
static_assert(sizeof(OutputParameters) == 0xc);
@@ -244,7 +242,7 @@ void SFDNSRES::GetHostByNameRequestWithOptions(HLERequestContext& ctx) {
struct OutputParameters {
u32 data_size;
NetDbError netdb_error;
Errno bsd_errno;
Network::Errno bsd_errno;
};
static_assert(sizeof(OutputParameters) == 0xc);
@@ -257,24 +255,23 @@ void SFDNSRES::GetHostByNameRequestWithOptions(HLERequestContext& ctx) {
});
}
static std::vector<u8> SerializeAddrInfo(const std::vector<Network::AddrInfo>& vec,
std::string_view host) {
static std::vector<u8> SerializeAddrInfo(std::span<const Network::AddrInfo> vec, std::string_view host) {
// Adapted from
// https://github.com/switchbrew/libnx/blob/c5a9a909a91657a9818a3b7e18c9b91ff0cbb6e3/nx/source/runtime/resolver.c#L190
std::vector<u8> data;
for (const Network::AddrInfo& addrinfo : vec) {
// serialized addrinfo:
Append<u32_be>(data, 0xBEEFCAFE); // magic
Append<u32_be>(data, 0); // ai_flags
Append<u32_be>(data, static_cast<u32>(Translate(addrinfo.family))); // ai_family
Append<u32_be>(data, static_cast<u32>(Translate(addrinfo.socket_type))); // ai_socktype
Append<u32_be>(data, static_cast<u32>(Translate(addrinfo.protocol))); // ai_protocol
Append<u32_be>(data, 0xBEEFCAFE); // magic
Append<u32_be>(data, 0); // ai_flags
Append<u32_be>(data, u32(addrinfo.family)); // ai_family
Append<u32_be>(data, u32(addrinfo.socket_type)); // ai_socktype
Append<u32_be>(data, u32(addrinfo.protocol)); // ai_protocol
Append<u32_be>(data, 16); // ai_addrlen
// ^ *not* sizeof(SerializedSockAddrIn), not that it matters since they're the same size
// ai_addr:
Append<u16_be>(data, static_cast<u16>(Translate(addrinfo.addr.family))); // sin_family
Append<u16_be>(data, u16(addrinfo.addr.family)); // sin_family
// On the Switch, the following fields are passed through htonl despite
// already being big-endian, so they end up as little-endian.
Append<u16_le>(data, addrinfo.addr.portno); // sin_port
@@ -296,7 +293,7 @@ static std::vector<u8> SerializeAddrInfo(const std::vector<Network::AddrInfo>& v
return data;
}
static std::pair<u32, GetAddrInfoError> GetAddrInfoRequestImpl(HLERequestContext& ctx) {
static std::pair<u32, Network::GetAddrInfoError> GetAddrInfoRequestImpl(HLERequestContext& ctx) {
struct InputParameters {
u8 use_nsd_resolve;
u32 cancel_handle;
@@ -321,7 +318,7 @@ static std::pair<u32, GetAddrInfoError> GetAddrInfoRequestImpl(HLERequestContext
// Prevent resolution of Nintendo servers
if (IsBlockedHost(host)) {
LOG_WARNING(Network, "Resolution of hostname {} requested, returning EAI_AGAIN", host);
return {0, GetAddrInfoError::AGAIN};
return {0, Network::GetAddrInfoError::AGAIN};
}
std::optional<std::string> service = std::nullopt;
@@ -331,24 +328,23 @@ static std::pair<u32, GetAddrInfoError> GetAddrInfoRequestImpl(HLERequestContext
}
// Serialized hints are also passed in a buffer, but are ignored for now.
auto res_v = Network::GetAddressInfo(host, service);
if (auto* res = std::get_if<std::vector<Network::AddrInfo>>(&res_v)) {
const std::vector<u8> data = SerializeAddrInfo(*res, host);
const u32 data_size = u32(data.size());
ctx.WriteBuffer(data, 0);
return {data_size, GetAddrInfoError::SUCCESS};
return {data_size, Network::GetAddrInfoError::SUCCESS};
}
auto* err = std::get_if<Network::GetAddrInfoError>(&res_v);
return {0, Translate(*err)};
return {0, *err};
}
void SFDNSRES::GetAddrInfoRequest(HLERequestContext& ctx) {
auto [data_size, emu_gai_err] = GetAddrInfoRequestImpl(ctx);
struct OutputParameters {
Errno bsd_errno;
GetAddrInfoError gai_error;
Network::Errno bsd_errno;
Network::GetAddrInfoError gai_error;
u32 data_size;
};
static_assert(sizeof(OutputParameters) == 0xc);
@@ -364,7 +360,7 @@ void SFDNSRES::GetAddrInfoRequest(HLERequestContext& ctx) {
void SFDNSRES::GetGaiStringErrorRequest(HLERequestContext& ctx) {
struct InputParameters {
GetAddrInfoError gai_errno;
Network::GetAddrInfoError gai_errno;
};
IPC::RequestParser rp{ctx};
auto input = rp.PopRaw<InputParameters>();
@@ -382,9 +378,9 @@ void SFDNSRES::GetAddrInfoRequestWithOptions(HLERequestContext& ctx) {
struct OutputParameters {
u32 data_size;
GetAddrInfoError gai_error;
Network::GetAddrInfoError gai_error;
NetDbError netdb_error;
Errno bsd_errno;
Network::Errno bsd_errno;
};
static_assert(sizeof(OutputParameters) == 0x10);
+3 -1
View File
@@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -11,7 +14,6 @@ namespace Service::Sockets {
void LoopProcess(Core::System& system) {
auto server_manager = std::make_unique<ServerManager>(system);
server_manager->RegisterNamedService("bsd:s", std::make_shared<BSD>(system, "bsd:s"));
server_manager->RegisterNamedService("bsd:u", std::make_shared<BSD>(system, "bsd:u"));
server_manager->RegisterNamedService("bsdcfg", std::make_shared<BSDCFG>(system));
+1 -236
View File
@@ -8,6 +8,7 @@
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "core/internal_network/socket_types.h"
namespace Core {
class System;
@@ -15,242 +16,6 @@ class System;
namespace Service::Sockets {
enum class Errno : u32 {
SUCCESS = 0,
BADF = 9,
AGAIN = 11,
INVAL = 22,
MFILE = 24,
PIPE = 32,
MSGSIZE = 90,
CONNABORTED = 103,
CONNRESET = 104,
NOTCONN = 107,
TIMEDOUT = 110,
CONNREFUSED = 111,
INPROGRESS = 115,
ISCONN = 106,
};
enum class GetAddrInfoError : s32 {
SUCCESS = 0,
ADDRFAMILY = 1,
AGAIN = 2,
BADFLAGS = 3,
FAIL = 4,
FAMILY = 5,
MEMORY = 6,
NODATA = 7,
NONAME = 8,
SERVICE = 9,
SOCKTYPE = 10,
SYSTEM = 11,
BADHINTS = 12,
PROTOCOL = 13,
OVERFLOW_ = 14, // avoid name collision with Windows macro
OTHER = 15,
};
enum class Domain : u32 {
Unspecified = 0,
INET = 2,
};
enum class Type : u32 {
Unspecified = 0,
STREAM = 1,
DGRAM = 2,
RAW = 3,
SEQPACKET = 5,
};
enum class Protocol : u32 {
IP = 0,
ICMP = 1,
TCP = 6,
UDP = 17,
//
IPV6 = 41,
RAW = 255,
//
HOPOPTS = 0,
IGMP = 2,
GGP = 3,
IPV4 = 4,
ST = 7,
EGP = 8,
PIGP = 9,
RCCMON = 10,
NVPII = 11,
PUP = 12,
ARGUS = 13,
EMCON = 14,
XNET = 15,
CHAOS = 16,
MUX = 18,
MEAS = 19,
HMP = 20,
PRM = 21,
IDP = 22,
TRUNK1 = 23,
TRUNK2 = 24,
LEAF1 = 25,
LEAF2 = 26,
RDP = 27,
IRTP = 28,
TP = 29,
BLT = 30,
NSP = 31,
INP = 32,
DCCP = 33,
//3PC = 34,
IDPR = 35,
XTP = 36,
DDP = 37,
CMTP = 38,
TPXX = 39,
IL = 40,
SDRP = 42,
ROUTING = 43,
FRAGMENT = 44,
IDRP = 45,
RSVP = 46,
GRE = 47,
MHRP = 48,
BHA = 49,
ESP = 50,
AH = 51,
INLSP = 52,
SWIPE = 53,
NHRP = 54,
MOBILE = 55,
TLSP = 56,
SKIP = 57,
ICMPV6 = 58,
NONE = 59,
DSTOPTS = 60,
AHIP = 61,
CFTP = 62,
HELLO = 63,
SATEXPAK = 64,
KRYPTOLAN = 65,
RVD = 66,
IPPC = 67,
ADFS = 68,
SATMON = 69,
VISA = 70,
IPCV = 71,
CPNX = 72,
CPHB = 73,
WSN = 74,
PVP = 75,
BRSATMON = 76,
ND = 77,
WBMON = 78,
WBEXPAK = 79,
EON = 80,
VMTP = 81,
SVMTP = 82,
VINES = 83,
TTP = 84,
IGP = 85,
DGP = 86,
TCF = 87,
IGRP = 88,
OSPFIGP = 89,
SRPC = 90,
LARP = 91,
MTP = 92,
AX25 = 93,
IPEIP = 94,
MICP = 95,
SCCSP = 96,
ETHERIP = 97,
ENCAP = 98,
APES = 99,
GMTP = 100,
IPCOMP = 108,
SCTP = 132,
MH = 135,
UDPLITE = 136,
HIP = 139,
SHIM6 = 140,
PIM = 103,
CARP = 112,
PGM = 113,
MPLS = 137,
PFSYNC = 240,
};
enum class SocketLevel : u32 {
IP = 0,
TCP = 6,
SOCKET = 0xffff, // i.e. SOL_SOCKET
};
enum class OptName : u32 {
REUSEADDR = 0x4,
KEEPALIVE = 0x8,
BROADCAST = 0x20,
LINGER = 0x80,
SNDBUF = 0x1001,
RCVBUF = 0x1002,
SNDTIMEO = 0x1005,
RCVTIMEO = 0x1006,
ERROR_ = 0x1007, // avoid name collision with Windows macro
NOSIGPIPE = 0x800, // at least according to libnx
ACCEPTFILTER = 0x1000,
BINTIME = 0x2000,
NO_OFFLOAD = 0x4000,
NO_DDP = 0x8000,
};
enum class ShutdownHow : s32 {
RD = 0,
WR = 1,
RDWR = 2,
};
enum class FcntlCmd : s32 {
GETFL = 3,
SETFL = 4,
};
struct SockAddrIn {
u8 len;
u8 family;
u16 portno;
std::array<u8, 4> ip;
std::array<u8, 248> zeroes;
};
static_assert(sizeof(SockAddrIn) == 0x100);
enum class PollEvents : u16 {
// Using Pascal case because IN is a macro on Windows.
In = 1 << 0,
Pri = 1 << 1,
Out = 1 << 2,
Err = 1 << 3,
Hup = 1 << 4,
Nval = 1 << 5,
RdNorm = 1 << 6,
RdBand = 1 << 7,
WrBand = 1 << 8,
};
DECLARE_ENUM_FLAG_OPERATORS(PollEvents);
struct PollFD {
s32 fd;
PollEvents events;
PollEvents revents;
};
struct Linger {
u32 onoff;
u32 linger;
};
void LoopProcess(Core::System& system);
} // namespace Service::Sockets
@@ -15,388 +15,42 @@
namespace Service::Sockets {
Errno Translate(Network::Errno value) {
switch (value) {
case Network::Errno::SUCCESS:
return Errno::SUCCESS;
case Network::Errno::BADF:
return Errno::BADF;
case Network::Errno::AGAIN:
return Errno::AGAIN;
case Network::Errno::INVAL:
return Errno::INVAL;
case Network::Errno::MFILE:
return Errno::MFILE;
case Network::Errno::PIPE:
return Errno::PIPE;
case Network::Errno::CONNREFUSED:
return Errno::CONNREFUSED;
case Network::Errno::NOTCONN:
return Errno::NOTCONN;
case Network::Errno::TIMEDOUT:
return Errno::TIMEDOUT;
case Network::Errno::CONNABORTED:
return Errno::CONNABORTED;
case Network::Errno::CONNRESET:
return Errno::CONNRESET;
case Network::Errno::INPROGRESS:
return Errno::INPROGRESS;
case Network::Errno::ISCONN:
return Errno::ISCONN;
default:
UNIMPLEMENTED_MSG("Unimplemented errno={}", value);
return Errno::SUCCESS;
}
}
std::pair<s32, Errno> Translate(std::pair<s32, Network::Errno> value) {
return {value.first, Translate(value.second)};
}
GetAddrInfoError Translate(Network::GetAddrInfoError error) {
switch (error) {
case Network::GetAddrInfoError::SUCCESS:
return GetAddrInfoError::SUCCESS;
case Network::GetAddrInfoError::ADDRFAMILY:
return GetAddrInfoError::ADDRFAMILY;
case Network::GetAddrInfoError::AGAIN:
return GetAddrInfoError::AGAIN;
case Network::GetAddrInfoError::BADFLAGS:
return GetAddrInfoError::BADFLAGS;
case Network::GetAddrInfoError::FAIL:
return GetAddrInfoError::FAIL;
case Network::GetAddrInfoError::FAMILY:
return GetAddrInfoError::FAMILY;
case Network::GetAddrInfoError::MEMORY:
return GetAddrInfoError::MEMORY;
case Network::GetAddrInfoError::NODATA:
return GetAddrInfoError::NODATA;
case Network::GetAddrInfoError::NONAME:
return GetAddrInfoError::NONAME;
case Network::GetAddrInfoError::SERVICE:
return GetAddrInfoError::SERVICE;
case Network::GetAddrInfoError::SOCKTYPE:
return GetAddrInfoError::SOCKTYPE;
case Network::GetAddrInfoError::SYSTEM:
return GetAddrInfoError::SYSTEM;
case Network::GetAddrInfoError::BADHINTS:
return GetAddrInfoError::BADHINTS;
case Network::GetAddrInfoError::PROTOCOL:
return GetAddrInfoError::PROTOCOL;
case Network::GetAddrInfoError::OVERFLOW_:
return GetAddrInfoError::OVERFLOW_;
case Network::GetAddrInfoError::OTHER:
return GetAddrInfoError::OTHER;
default:
UNIMPLEMENTED_MSG("Unimplemented GetAddrInfoError={}", error);
return GetAddrInfoError::OTHER;
}
}
const char* Translate(GetAddrInfoError error) {
const char* Translate(Network::GetAddrInfoError error) {
// https://android.googlesource.com/platform/bionic/+/085543106/libc/dns/net/getaddrinfo.c#254
switch (error) {
case GetAddrInfoError::SUCCESS:
case Network::GetAddrInfoError::SUCCESS:
return "Success";
case GetAddrInfoError::ADDRFAMILY:
case Network::GetAddrInfoError::ADDRFAMILY:
return "Address family for hostname not supported";
case GetAddrInfoError::AGAIN:
case Network::GetAddrInfoError::AGAIN:
return "Temporary failure in name resolution";
case GetAddrInfoError::BADFLAGS:
case Network::GetAddrInfoError::BADFLAGS:
return "Invalid value for ai_flags";
case GetAddrInfoError::FAIL:
case Network::GetAddrInfoError::FAIL:
return "Non-recoverable failure in name resolution";
case GetAddrInfoError::FAMILY:
case Network::GetAddrInfoError::FAMILY:
return "ai_family not supported";
case GetAddrInfoError::MEMORY:
case Network::GetAddrInfoError::MEMORY:
return "Memory allocation failure";
case GetAddrInfoError::NODATA:
case Network::GetAddrInfoError::NODATA:
return "No address associated with hostname";
case GetAddrInfoError::NONAME:
case Network::GetAddrInfoError::NONAME:
return "hostname nor servname provided, or not known";
case GetAddrInfoError::SERVICE:
case Network::GetAddrInfoError::SERVICE:
return "servname not supported for ai_socktype";
case GetAddrInfoError::SOCKTYPE:
case Network::GetAddrInfoError::SOCKTYPE:
return "ai_socktype not supported";
case GetAddrInfoError::SYSTEM:
case Network::GetAddrInfoError::SYSTEM:
return "System error returned in errno";
case GetAddrInfoError::BADHINTS:
case Network::GetAddrInfoError::BADHINTS:
return "Invalid value for hints";
case GetAddrInfoError::PROTOCOL:
case Network::GetAddrInfoError::PROTOCOL:
return "Resolved protocol is unknown";
case GetAddrInfoError::OVERFLOW_:
case Network::GetAddrInfoError::OVERFLOW_:
return "Argument buffer overflow";
default:
return "Unknown error";
}
}
Network::Domain Translate(Domain domain) {
switch (domain) {
case Domain::Unspecified:
return Network::Domain::Unspecified;
case Domain::INET:
return Network::Domain::INET;
default:
UNIMPLEMENTED_MSG("Unimplemented domain={}", domain);
return {};
}
}
Domain Translate(Network::Domain domain) {
switch (domain) {
case Network::Domain::Unspecified:
return Domain::Unspecified;
case Network::Domain::INET:
return Domain::INET;
default:
UNIMPLEMENTED_MSG("Unimplemented domain={}", domain);
return {};
}
}
Network::Type Translate(Type type) {
switch (type) {
case Type::Unspecified:
return Network::Type::Unspecified;
case Type::STREAM:
return Network::Type::STREAM;
case Type::DGRAM:
return Network::Type::DGRAM;
case Type::RAW:
return Network::Type::RAW;
case Type::SEQPACKET:
return Network::Type::SEQPACKET;
default:
UNIMPLEMENTED_MSG("Unimplemented type={}", type);
return Network::Type{};
}
}
Type Translate(Network::Type type) {
switch (type) {
case Network::Type::Unspecified: return Type::Unspecified;
case Network::Type::STREAM: return Type::STREAM;
case Network::Type::DGRAM: return Type::DGRAM;
case Network::Type::RAW: return Type::RAW;
case Network::Type::SEQPACKET: return Type::SEQPACKET;
default:
UNIMPLEMENTED_MSG("Unimplemented type={}", type);
return Type{};
}
}
#define NETWORK_PROTOCOL_TRANSLATE_LIST \
NETWORK_PROTOCOL_TRANSLATE_ELEM(IP) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(ICMP) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(TCP) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(UDP) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(IPV6) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(RAW) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(IGMP) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(GGP) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(IPV4) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(ST) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(EGP) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(PIGP) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(RCCMON) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(NVPII) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(PUP) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(ARGUS) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(EMCON) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(XNET) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(CHAOS) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(MUX) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(MEAS) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(HMP) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(PRM) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(IDP) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(TRUNK1) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(TRUNK2) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(LEAF1) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(LEAF2) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(RDP) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(IRTP) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(TP) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(BLT) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(NSP) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(INP) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(DCCP) \
/*NETWORK_PROTOCOL_TRANSLATE_ELEM(3PC)*/ \
NETWORK_PROTOCOL_TRANSLATE_ELEM(IDPR) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(XTP) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(DDP) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(CMTP) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(TPXX) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(IL) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(SDRP) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(ROUTING) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(FRAGMENT) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(IDRP) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(RSVP) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(GRE) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(MHRP) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(BHA) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(ESP) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(AH) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(INLSP) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(SWIPE) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(NHRP) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(MOBILE) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(TLSP) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(SKIP) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(ICMPV6) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(NONE) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(DSTOPTS) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(AHIP) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(CFTP) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(HELLO) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(SATEXPAK) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(KRYPTOLAN) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(RVD) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(IPPC) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(ADFS) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(SATMON) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(VISA) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(IPCV) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(CPNX) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(CPHB) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(WSN) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(PVP) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(BRSATMON) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(ND) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(WBMON) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(WBEXPAK) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(EON) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(VMTP) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(SVMTP) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(VINES) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(TTP) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(IGP) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(DGP) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(TCF) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(IGRP) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(OSPFIGP) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(SRPC) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(LARP) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(MTP) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(AX25) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(IPEIP) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(MICP) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(SCCSP) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(ETHERIP) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(ENCAP) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(APES) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(GMTP) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(IPCOMP) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(SCTP) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(MH) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(UDPLITE) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(HIP) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(SHIM6) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(PIM) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(CARP) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(PGM) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(MPLS) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(PFSYNC)
[[nodiscard]] Network::Protocol Translate(Protocol protocol) {
switch (protocol) {
#define NETWORK_PROTOCOL_TRANSLATE_ELEM(name) case Protocol::name: return Network::Protocol::name;
NETWORK_PROTOCOL_TRANSLATE_LIST
#undef NETWORK_PROTOCOL_TRANSLATE_ELEM
default:
UNIMPLEMENTED_MSG("Unimplemented protocol={}", protocol);
return {};
}
}
[[nodiscard]] Protocol Translate(Network::Protocol protocol) {
switch (protocol) {
#define NETWORK_PROTOCOL_TRANSLATE_ELEM(name) case Network::Protocol::name: return Protocol::name;
NETWORK_PROTOCOL_TRANSLATE_LIST
#undef NETWORK_PROTOCOL_TRANSLATE_ELEM
default:
UNIMPLEMENTED_MSG("Unimplemented protocol={}", protocol);
return {};
}
}
#undef NETWORK_PROTOCOL_TRANSLATE_LIST
Network::PollEvents Translate(PollEvents flags) {
Network::PollEvents result{};
const auto translate = [&result, &flags](PollEvents from, Network::PollEvents to) {
if (True(flags & from)) {
flags &= ~from;
result |= to;
}
};
translate(PollEvents::In, Network::PollEvents::In);
translate(PollEvents::Pri, Network::PollEvents::Pri);
translate(PollEvents::Out, Network::PollEvents::Out);
translate(PollEvents::Err, Network::PollEvents::Err);
translate(PollEvents::Hup, Network::PollEvents::Hup);
translate(PollEvents::Nval, Network::PollEvents::Nval);
translate(PollEvents::RdNorm, Network::PollEvents::RdNorm);
translate(PollEvents::RdBand, Network::PollEvents::RdBand);
translate(PollEvents::WrBand, Network::PollEvents::WrBand);
UNIMPLEMENTED_IF_MSG((u16)flags != 0, "Unimplemented flags={}", (u16)flags);
return result;
}
PollEvents Translate(Network::PollEvents flags) {
PollEvents result{};
const auto translate = [&result, &flags](Network::PollEvents from, PollEvents to) {
if (True(flags & from)) {
flags &= ~from;
result |= to;
}
};
translate(Network::PollEvents::In, PollEvents::In);
translate(Network::PollEvents::Pri, PollEvents::Pri);
translate(Network::PollEvents::Out, PollEvents::Out);
translate(Network::PollEvents::Err, PollEvents::Err);
translate(Network::PollEvents::Hup, PollEvents::Hup);
translate(Network::PollEvents::Nval, PollEvents::Nval);
translate(Network::PollEvents::RdNorm, PollEvents::RdNorm);
translate(Network::PollEvents::RdBand, PollEvents::RdBand);
translate(Network::PollEvents::WrBand, PollEvents::WrBand);
UNIMPLEMENTED_IF_MSG((u16)flags != 0, "Unimplemented flags={}", (u16)flags);
return result;
}
Network::SockAddrIn Translate(SockAddrIn value) {
// All lengths are valid, from [0 upto 256]
return {
.family = Translate(Domain(value.family)),
.ip = value.ip,
.portno = static_cast<u16>(value.portno >> 8 | value.portno << 8),
};
}
SockAddrIn Translate(Network::SockAddrIn value) {
return {
.len = 16,
.family = static_cast<u8>(Translate(value.family)),
.portno = static_cast<u16>(value.portno >> 8 | value.portno << 8),
.ip = value.ip,
.zeroes = {},
};
}
Network::ShutdownHow Translate(ShutdownHow how) {
switch (how) {
case ShutdownHow::RD:
return Network::ShutdownHow::RD;
case ShutdownHow::WR:
return Network::ShutdownHow::WR;
case ShutdownHow::RDWR:
return Network::ShutdownHow::RDWR;
default:
UNIMPLEMENTED_MSG("Unimplemented how={}", how);
return {};
}
}
} // namespace Service::Sockets
@@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -11,49 +14,7 @@
namespace Service::Sockets {
/// Translate abstract errno to guest errno
Errno Translate(Network::Errno value);
/// Translate abstract return value errno pair to guest return value errno pair
std::pair<s32, Errno> Translate(std::pair<s32, Network::Errno> value);
/// Translate abstract getaddrinfo error to guest getaddrinfo error
GetAddrInfoError Translate(Network::GetAddrInfoError value);
/// Translate guest error to string
const char* Translate(GetAddrInfoError value);
/// Translate guest domain to abstract domain
Network::Domain Translate(Domain domain);
/// Translate abstract domain to guest domain
Domain Translate(Network::Domain domain);
/// Translate guest type to abstract type
Network::Type Translate(Type type);
/// Translate abstract type to guest type
Type Translate(Network::Type type);
/// Translate guest protocol to abstract protocol
Network::Protocol Translate(Protocol protocol);
/// Translate abstract protocol to guest protocol
Protocol Translate(Network::Protocol protocol);
/// Translate guest poll event flags to abstract poll event flags
Network::PollEvents Translate(PollEvents flags);
/// Translate abstract poll event flags to guest poll event flags
PollEvents Translate(Network::PollEvents flags);
/// Translate guest socket address structure to abstract socket address structure
Network::SockAddrIn Translate(SockAddrIn value);
/// Translate abstract socket address structure to guest socket address structure
SockAddrIn Translate(Network::SockAddrIn value);
/// Translate guest shutdown mode to abstract shutdown mode
Network::ShutdownHow Translate(ShutdownHow how);
const char* Translate(Network::GetAddrInfoError value);
} // namespace Service::Sockets
+1 -1
View File
@@ -132,7 +132,7 @@ public:
auto bsd = system.ServiceManager().GetService<Service::Sockets::BSD>("bsd:u");
if (bsd) {
auto err = bsd->CloseImpl(fd);
if (err != Service::Sockets::Errno::SUCCESS) {
if (err != Network::Errno::SUCCESS) {
LOG_ERROR(Service_SSL, "Failed to close duplicated socket: {}", err);
}
}
+3 -2
View File
@@ -1,10 +1,11 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include <atomic>
#include <common/socket_types.h>
#include <mutex>
#include "core/internal_network/socket_types.h"
namespace Network {
File diff suppressed because it is too large Load Diff
+9 -63
View File
@@ -13,7 +13,7 @@
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "common/socket_types.h"
#include "core/internal_network/socket_types.h"
#ifdef _WIN32
#include <winsock2.h>
@@ -31,68 +31,10 @@ namespace Network {
class SocketBase;
class Socket;
/// Error code for network functions
enum class Errno {
SUCCESS,
BADF,
INVAL,
MFILE,
PIPE,
NOTCONN,
AGAIN,
CONNREFUSED,
CONNRESET,
CONNABORTED,
HOSTUNREACH,
NETDOWN,
NETUNREACH,
TIMEDOUT,
MSGSIZE,
INPROGRESS,
ISCONN,
OTHER,
};
enum class GetAddrInfoError {
SUCCESS,
ADDRFAMILY,
AGAIN,
BADFLAGS,
FAIL,
FAMILY,
MEMORY,
NODATA,
NONAME,
SERVICE,
SOCKTYPE,
SYSTEM,
BADHINTS,
PROTOCOL,
OVERFLOW_,
OTHER,
};
/// Cross-platform poll fd structure
enum class PollEvents : u16 {
// Using Pascal case because IN is a macro on Windows.
In = 1 << 0,
Pri = 1 << 1,
Out = 1 << 2,
Err = 1 << 3,
Hup = 1 << 4,
Nval = 1 << 5,
RdNorm = 1 << 6,
RdBand = 1 << 7,
WrBand = 1 << 8,
};
DECLARE_ENUM_FLAG_OPERATORS(PollEvents);
struct PollFD {
SocketBase* socket;
PollEvents events;
PollEvents revents;
struct HostPollFD {
SocketBase* socket = nullptr;
Network::PollEvents events = {};
Network::PollEvents revents = {};
};
class NetworkInstance {
@@ -101,6 +43,10 @@ public:
~NetworkInstance();
};
sockaddr_in TranslateFromSockAddrIn(Network::SockAddrIn input);
Network::SockAddrIn TranslateToSockAddrIn(sockaddr_in input);
s32 TranslateMsgOptToNative(s32 flags);
void CancelPendingSocketOperations();
void RestartSocketOperations();
@@ -46,7 +46,7 @@ namespace Network {
#ifdef _WIN32
std::vector<Network::NetworkInterface> GetAvailableNetworkInterfaces() {
std::vector<NetworkInterface> GetAvailableNetworkInterfaces() {
ULONG buf_size = 0;
if (GetAdaptersAddresses(
@@ -66,7 +66,7 @@ std::vector<Network::NetworkInterface> GetAvailableNetworkInterfaces() {
return {};
}
std::vector<Network::NetworkInterface> result;
std::vector<NetworkInterface> result;
for (auto* a = addrs; a; a = a->Next) {
@@ -89,7 +89,7 @@ std::vector<Network::NetworkInterface> GetAvailableNetworkInterfaces() {
gw = reinterpret_cast<sockaddr_in*>(a->FirstGatewayAddress->Address.lpSockaddr)
->sin_addr;
result.emplace_back(Network::NetworkInterface{
result.emplace_back(NetworkInterface{
.name = Common::UTF16ToUTF8(std::wstring{a->FriendlyName}),
.ip_address = ip,
.subnet_mask = mask,
@@ -103,7 +103,7 @@ std::vector<Network::NetworkInterface> GetAvailableNetworkInterfaces() {
#else
std::vector<Network::NetworkInterface> GetAvailableNetworkInterfaces() {
std::vector<NetworkInterface> GetAvailableNetworkInterfaces() {
#if defined(__ANDROID__) || defined(__linux__)
struct ifaddrs* ifaddr = nullptr;
if (getifaddrs(&ifaddr) != 0) {
@@ -135,7 +135,7 @@ std::vector<Network::NetworkInterface> GetAvailableNetworkInterfaces() {
LOG_WARNING(Network, "\"/proc/net/route\" not found - using gateway 0");
}
#endif
std::vector<Network::NetworkInterface> ifaces;
std::vector<NetworkInterface> ifaces;
for (auto ifa = ifaddr; ifa != nullptr; ifa = ifa->ifa_next) {
if (ifa->ifa_addr == nullptr || ifa->ifa_netmask == nullptr /* Have a netmask and address */
|| ifa->ifa_addr->sa_family != AF_INET /* Must be of kind AF_INET */
@@ -149,7 +149,7 @@ std::vector<Network::NetworkInterface> GetAvailableNetworkInterfaces() {
});
in_addr gw; // Solaris defines s_addr as a macro, can't use special C++ shenanigans here
gw.s_addr = it != routes.end() ? it->gateway : 0;
ifaces.emplace_back(Network::NetworkInterface{
ifaces.emplace_back(NetworkInterface{
.name = ifa->ifa_name,
.ip_address = std::bit_cast<struct sockaddr_in>(*ifa->ifa_addr).sin_addr,
.subnet_mask = std::bit_cast<struct sockaddr_in>(*ifa->ifa_netmask).sin_addr,
@@ -159,7 +159,7 @@ std::vector<Network::NetworkInterface> GetAvailableNetworkInterfaces() {
freeifaddrs(ifaddr);
return ifaces;
#elif defined(__FreeBSD__)
std::vector<Network::NetworkInterface> ifaces;
std::vector<NetworkInterface> ifaces;
int fd = ::socket(PF_ROUTE, SOCK_RAW, AF_UNSPEC);
if (fd < 0) {
LOG_ERROR(Network, "socket: {}", std::strerror(errno));
@@ -191,7 +191,7 @@ std::vector<Network::NetworkInterface> GetAvailableNetworkInterfaces() {
size_t msglen = rtm->rtm_msglen - sizeof(*ifm);
char const* p = (char const*)(ifm + 1);
Network::NetworkInterface iface{};
NetworkInterface iface{};
for (size_t i = 0; i < RTAX_MAX; i++)
if ((ifm->ifm_addrs & (1 << i)) != 0) {
struct sockaddr const* sa = reinterpret_cast<struct sockaddr const*>(p);
@@ -220,7 +220,7 @@ std::vector<Network::NetworkInterface> GetAvailableNetworkInterfaces() {
#endif // _WIN32
std::optional<Network::NetworkInterface> GetSelectedNetworkInterface() {
std::optional<NetworkInterface> GetSelectedNetworkInterface() {
auto const& sel_if = Settings::values.network_interface.GetValue();
if (auto const ifaces = Network::GetAvailableNetworkInterfaces(); ifaces.size() > 0) {
if (sel_if.empty())
+174
View File
@@ -0,0 +1,174 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#include <cstdio>
#include "common/assert.h"
#include "common/logging.h"
#include "core/internal_network/socket_icmp.h"
#ifdef __unix__
#include <unistd.h>
#include <sys/socket.h>
#endif
namespace Network {
namespace {
u16 ComputeChecksum(std::span<const u8> data) {
u32 sum = 0;
for (size_t i = 0; i < data.size(); i += 2) {
u32 value = (u32(data[i + 0]) << 8ull) | u32(data[i + 1]); //big endian
sum += value;
}
if (data.size() % 2 != 0){
sum += u16(data[data.size() - 1]) << 8;
}
while ((sum >> 16) != 0)
sum = (sum & 0xffff) + (sum >> 16);
return (~sum) & 0xffff;
}
}
IcmpSocket::IcmpSocket() noexcept {}
IcmpSocket::~IcmpSocket() {
if (fd == INVALID_SOCKET) {
return;
}
fd = INVALID_SOCKET;
}
Errno IcmpSocket::SetSockOpt(Network::SocketLevel level, Network::OptName optname, std::span<const u8> optval) {
LOG_WARNING(Network, "(stubbed) level={},optname={},optval={}", level, optname, optval.size());
return Errno::SUCCESS;
}
Errno IcmpSocket::Initialize(Domain domain, Type type, Protocol socket_protocol) {
return Errno::SUCCESS;
}
std::pair<IcmpSocket::AcceptResult, Errno> IcmpSocket::Accept() {
LOG_WARNING(Network, "(stubbed) called");
return {AcceptResult{}, Errno::SUCCESS};
}
Errno IcmpSocket::Connect(Network::SockAddrIn addr_in) {
LOG_WARNING(Network, "(stubbed) called");
return Errno::SUCCESS;
}
std::pair<Network::SockAddrIn, Errno> IcmpSocket::GetPeerName() {
LOG_WARNING(Network, "(stubbed) called");
return {Network::SockAddrIn{}, Errno::SUCCESS};
}
std::pair<Network::SockAddrIn, Errno> IcmpSocket::GetSockName() {
LOG_WARNING(Network, "(stubbed) called");
return {Network::SockAddrIn{}, Errno::SUCCESS};
}
Errno IcmpSocket::Bind(Network::SockAddrIn addr) {
LOG_WARNING(Network, "(stubbed) called");
return Errno::SUCCESS;
}
Errno IcmpSocket::Listen(s32 backlog) {
LOG_WARNING(Network, "(stubbed) called");
return Errno::SUCCESS;
}
Errno IcmpSocket::Shutdown(ShutdownHow how) {
LOG_WARNING(Network, "(stubbed) called");
return Errno::SUCCESS;
}
std::pair<s32, Errno> IcmpSocket::Recv(int flags, std::span<u8> message) {
LOG_DEBUG(Network, "(stubbed) called");
return {s32(0), Errno::NOTCONN};
}
std::pair<s32, Errno> IcmpSocket::RecvFrom(int flags, std::span<u8> message, Network::SockAddrIn* addr) {
LOG_DEBUG(Network, "(stubbed) called");
ASSERT(flags == 0);
ASSERT(message.size() < std::size_t((std::numeric_limits<int>::max)()));
#ifdef __unix__
if (addr) {
if (seq_ident.empty())
return {0, Errno::SUCCESS};
// PLEASE DON'T KILL ME, I SWEAR THIS IS LEGITIMATELY THE BEST WAY TO DO IT
// IF YOU OPEN socket() GOOGLE WILL STRAIGHT UP IP BAN YOU AFTER 2 HOURS
auto const cmd = fmt::format("ping -t 3 -o {}.{}.{}.{}", addr->ip[0], addr->ip[1], addr->ip[2], addr->ip[3]);
if (::system(cmd.c_str()) == 0) {
std::vector<u8> data{
8,
0,
0, //checksum
0,
u8(seq_ident.front() >> 24), //ident
u8(seq_ident.front() >> 16),
u8(seq_ident.front() >> 8), // seq
u8(seq_ident.front() >> 0),
};
seq_ident.pop_back();
auto const csum = ComputeChecksum(std::span<const u8>{data.begin(), data.end()});
data[2] = u8(csum >> 8); //hi
data[3] = u8(csum); //lo
auto const n = (std::max)(data.size(), message.size());
std::copy(data.begin(), data.begin() + n, message.begin());
return {n, Errno::SUCCESS};
}
return {-1, Errno::TIMEDOUT};
}
#endif
return {-1, Errno::INVAL};
}
std::pair<s32, Errno> IcmpSocket::Send(std::span<const u8> message, int flags) {
LOG_DEBUG(Network, "(stubbed) called");
seq_ident.push_back(
(u32(message[4]) << 24)
| (u32(message[5]) << 16)
| (u32(message[6]) << 8)
| (u32(message[7]) << 0)
);
return {s32(0), Errno::NOTCONN};
}
std::pair<s32, Errno> IcmpSocket::SendTo(u32 flags, std::span<const u8> message, const Network::SockAddrIn* addr) {
LOG_DEBUG(Network, "(stubbed) called");
ASSERT(message.size() < size_t((std::numeric_limits<int>::max)()));
// 0 -> 8 (IPv4), 128 (IPv6)
// 1 -> 0
// 2..4 -> checksum
// 4..6 -> ident
// 6..8 -> seq
if (!message.empty())
return {s32(message.size()), Errno::SUCCESS};
return {-1, Errno::INVAL};
}
Errno IcmpSocket::Close() {
LOG_DEBUG(Network, "called");
fd = INVALID_SOCKET;
return Errno::SUCCESS;
}
std::pair<Errno, Errno> IcmpSocket::GetPendingError() {
LOG_DEBUG(Network, "called");
return {Errno::SUCCESS, Errno::SUCCESS};
}
bool IcmpSocket::IsOpened() const {
return fd != INVALID_SOCKET;
}
void IcmpSocket::HandleProxyPacket(const ProxyPacket& packet) {
LOG_WARNING(Network, "(stubbed) called");
}
Errno IcmpSocket::SetNonBlock(bool enable) {
return Errno::SUCCESS;
}
} // namespace Network
+37
View File
@@ -0,0 +1,37 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include <span>
#include "core/internal_network/sockets.h"
namespace Network {
class IcmpSocket : public Network::SocketBase {
public:
explicit IcmpSocket() noexcept;
~IcmpSocket() override;
Errno Initialize(Domain domain, Type type, Protocol socket_protocol) override;
Errno Close() override;
std::pair<AcceptResult, Errno> Accept() override;
Errno Connect(Network::SockAddrIn addr_in) override;
std::pair<Network::SockAddrIn, Errno> GetPeerName() override;
std::pair<Network::SockAddrIn, Errno> GetSockName() override;
Errno Bind(Network::SockAddrIn addr) override;
Errno Listen(s32 backlog) override;
Errno Shutdown(ShutdownHow how) override;
std::pair<s32, Errno> Recv(int flags, std::span<u8> message) override;
std::pair<s32, Errno> RecvFrom(int flags, std::span<u8> message, Network::SockAddrIn* addr) override;
std::pair<s32, Errno> Send(std::span<const u8> message, int flags) override;
std::pair<s32, Errno> SendTo(u32 flags, std::span<const u8> message, const Network::SockAddrIn* addr) override;
Errno SetSockOpt(Network::SocketLevel level, Network::OptName option, std::span<const u8> value) override;
std::pair<Errno, Errno> GetPendingError() override;
bool IsOpened() const override;
void HandleProxyPacket(const ProxyPacket& packet) override;
Errno SetNonBlock(bool enable) override;
std::vector<u32> seq_ident;
};
} // namespace Network
+57 -95
View File
@@ -47,71 +47,82 @@ void ProxySocket::HandleProxyPacket(const ProxyPacket& packet) {
received_packets.push(decompressed);
}
template <typename T>
Errno ProxySocket::SetSockOpt(SOCKET fd_, int option, T value) {
LOG_DEBUG(Network, "(STUBBED) called");
Errno ProxySocket::SetNonBlock(bool enable) {
blocking = !enable;
return Errno::SUCCESS;
}
Errno ProxySocket::SetSockOpt(Network::SocketLevel level, Network::OptName optname, std::span<const u8> optval) {
LOG_DEBUG(Network, "level={},optname={},optval={}", level, optname, optval.size());
// numeric values?
if (optval.size() >= sizeof(u32)) {
u32 value;
std::memcpy(&value, optval.data(), sizeof(value));
if (optname == Network::OptName::BROADCAST)
broadcast = bool(value);
if (optname == Network::OptName::SNDTIMEO)
send_timeout = value;
if (optname == Network::OptName::RCVTIMEO)
receive_timeout = value;
}
return Errno::SUCCESS;
}
Errno ProxySocket::Initialize(Domain domain, Type type, Protocol socket_protocol) {
protocol = socket_protocol;
SetSockOpt(fd, SO_TYPE, type);
return Errno::SUCCESS;
}
std::pair<ProxySocket::AcceptResult, Errno> ProxySocket::Accept() {
LOG_WARNING(Network, "(STUBBED) called");
LOG_WARNING(Network, "(stubbed) called");
return {AcceptResult{}, Errno::SUCCESS};
}
Errno ProxySocket::Connect(SockAddrIn addr_in) {
LOG_WARNING(Network, "(STUBBED) called");
Errno ProxySocket::Connect(Network::SockAddrIn addr_in) {
LOG_WARNING(Network, "(stubbed) called");
return Errno::SUCCESS;
}
std::pair<SockAddrIn, Errno> ProxySocket::GetPeerName() {
LOG_WARNING(Network, "(STUBBED) called");
return {SockAddrIn{}, Errno::SUCCESS};
std::pair<Network::SockAddrIn, Errno> ProxySocket::GetPeerName() {
LOG_WARNING(Network, "(stubbed) called");
return {Network::SockAddrIn{}, Errno::SUCCESS};
}
std::pair<SockAddrIn, Errno> ProxySocket::GetSockName() {
LOG_WARNING(Network, "(STUBBED) called");
return {SockAddrIn{}, Errno::SUCCESS};
std::pair<Network::SockAddrIn, Errno> ProxySocket::GetSockName() {
LOG_WARNING(Network, "(stubbed) called");
return {Network::SockAddrIn{}, Errno::SUCCESS};
}
Errno ProxySocket::Bind(SockAddrIn addr) {
Errno ProxySocket::Bind(Network::SockAddrIn addr) {
if (is_bound) {
LOG_WARNING(Network, "Rebinding Socket is unimplemented!");
return Errno::SUCCESS;
}
local_endpoint = addr;
is_bound = true;
return Errno::SUCCESS;
}
Errno ProxySocket::Listen(s32 backlog) {
LOG_WARNING(Network, "(STUBBED) called");
LOG_WARNING(Network, "(stubbed) called");
return Errno::SUCCESS;
}
Errno ProxySocket::Shutdown(ShutdownHow how) {
LOG_WARNING(Network, "(STUBBED) called");
LOG_WARNING(Network, "(stubbed) called");
return Errno::SUCCESS;
}
std::pair<s32, Errno> ProxySocket::Recv(int flags, std::span<u8> message) {
LOG_WARNING(Network, "(STUBBED) called");
LOG_WARNING(Network, "(stubbed) called");
ASSERT(flags == 0);
ASSERT(message.size() < static_cast<size_t>((std::numeric_limits<int>::max)()));
return {static_cast<s32>(0), Errno::SUCCESS};
ASSERT(message.size() < std::size_t((std::numeric_limits<int>::max)()));
return {s32(0), Errno::SUCCESS};
}
std::pair<s32, Errno> ProxySocket::RecvFrom(int flags, std::span<u8> message, SockAddrIn* addr) {
std::pair<s32, Errno> ProxySocket::RecvFrom(int flags, std::span<u8> message, Network::SockAddrIn* addr) {
ASSERT(flags == 0);
ASSERT(message.size() < static_cast<size_t>((std::numeric_limits<int>::max)()));
ASSERT(message.size() < std::size_t((std::numeric_limits<int>::max)()));
// TODO (flTobi): Verify the timeout behavior and break when connection is lost
const auto timestamp = std::chrono::steady_clock::now();
@@ -134,29 +145,29 @@ std::pair<s32, Errno> ProxySocket::RecvFrom(int flags, std::span<u8> message, So
std::this_thread::yield();
const auto time_diff = std::chrono::steady_clock::now() - timestamp;
const auto time_diff_ms =
std::chrono::duration_cast<std::chrono::milliseconds>(time_diff).count();
const auto time_diff_ms = std::chrono::duration_cast<std::chrono::milliseconds>(time_diff).count();
if (time_diff_ms > timeout) {
return {-1, Errno::TIMEDOUT};
}
}
}
std::pair<s32, Errno> ProxySocket::ReceivePacket(int flags, std::span<u8> message, SockAddrIn* addr,
std::size_t max_length) {
std::pair<s32, Errno> ProxySocket::ReceivePacket(int flags, std::span<u8> message, Network::SockAddrIn* addr, std::size_t max_length) {
LOG_DEBUG(Network, "called");
ProxyPacket& packet = received_packets.front();
if (addr) {
addr->family = Domain::INET;
addr->len = 16;
addr->family = u8(Network::Domain::INET);
addr->ip = packet.local_endpoint.ip; // The senders ip address
addr->portno = packet.local_endpoint.portno; // The senders port number
addr->zeroes = {};
}
bool peek = (flags & FLAG_MSG_PEEK) != 0;
bool peek = (flags & u32(Network::MsgOpt::PEEK)) != 0;
std::size_t read_bytes;
if (packet.data.size() > max_length) {
read_bytes = max_length;
memcpy(message.data(), packet.data.data(), max_length);
std::memcpy(message.data(), packet.data.data(), max_length);
if (protocol == Protocol::UDP) {
if (!peek) {
@@ -165,51 +176,48 @@ std::pair<s32, Errno> ProxySocket::ReceivePacket(int flags, std::span<u8> messag
return {-1, Errno::MSGSIZE};
} else if (protocol == Protocol::TCP) {
std::vector<u8> numArray(packet.data.size() - max_length);
std::copy(packet.data.begin() + max_length, packet.data.end(),
std::back_inserter(numArray));
std::copy(packet.data.begin() + max_length, packet.data.end(), std::back_inserter(numArray));
packet.data = numArray;
}
} else {
read_bytes = packet.data.size();
memcpy(message.data(), packet.data.data(), read_bytes);
std::memcpy(message.data(), packet.data.data(), read_bytes);
if (!peek) {
received_packets.pop();
}
}
return {static_cast<u32>(read_bytes), Errno::SUCCESS};
return {u32(read_bytes), Errno::SUCCESS};
}
std::pair<s32, Errno> ProxySocket::Send(std::span<const u8> message, int flags) {
LOG_WARNING(Network, "(STUBBED) called");
ASSERT(message.size() < static_cast<size_t>((std::numeric_limits<int>::max)()));
LOG_WARNING(Network, "(stubbed) called");
ASSERT(message.size() < size_t((std::numeric_limits<int>::max)()));
ASSERT(flags == 0);
return {static_cast<s32>(0), Errno::SUCCESS};
return {s32(0), Errno::SUCCESS};
}
void ProxySocket::SendPacket(ProxyPacket& packet) {
if (auto room_member = Network::GetRoomMember().lock()) {
if (room_member->IsConnected()) {
packet.data = Common::Compression::CompressDataZSTDDefault(packet.data.data(),
packet.data.size());
packet.data = Common::Compression::CompressDataZSTDDefault(packet.data.data(), packet.data.size());
room_member->SendProxyPacket(packet);
}
}
}
std::pair<s32, Errno> ProxySocket::SendTo(u32 flags, std::span<const u8> message,
const SockAddrIn* addr) {
std::pair<s32, Errno> ProxySocket::SendTo(u32 flags, std::span<const u8> message, const Network::SockAddrIn* addr) {
LOG_DEBUG(Network, "called");
ASSERT(flags == 0);
if (!is_bound) {
LOG_ERROR(Network, "ProxySocket is not bound!");
return {static_cast<s32>(message.size()), Errno::SUCCESS};
return {s32(message.size()), Errno::SUCCESS};
}
if (auto room_member = Network::GetRoomMember().lock()) {
if (!room_member->IsConnected()) {
return {static_cast<s32>(message.size()), Errno::SUCCESS};
return {s32(message.size()), Errno::SUCCESS};
}
}
@@ -234,65 +242,19 @@ std::pair<s32, Errno> ProxySocket::SendTo(u32 flags, std::span<const u8> message
SendPacket(packet);
return {static_cast<s32>(message.size()), Errno::SUCCESS};
return {s32(message.size()), Errno::SUCCESS};
}
Errno ProxySocket::Close() {
LOG_DEBUG(Network, "called");
fd = INVALID_SOCKET;
closed = true;
return Errno::SUCCESS;
}
Errno ProxySocket::SetLinger(bool enable, u32 linger) {
struct Linger {
u16 linger_enable;
u16 linger_time;
} values;
values.linger_enable = enable ? 1 : 0;
values.linger_time = static_cast<u16>(linger);
return SetSockOpt(fd, SO_LINGER, values);
}
Errno ProxySocket::SetReuseAddr(bool enable) {
return SetSockOpt<u32>(fd, SO_REUSEADDR, enable ? 1 : 0);
}
Errno ProxySocket::SetBroadcast(bool enable) {
broadcast = enable;
return SetSockOpt<u32>(fd, SO_BROADCAST, enable ? 1 : 0);
}
Errno ProxySocket::SetSndBuf(u32 value) {
return SetSockOpt(fd, SO_SNDBUF, value);
}
Errno ProxySocket::SetKeepAlive(bool enable) {
return Errno::SUCCESS;
}
Errno ProxySocket::SetRcvBuf(u32 value) {
return SetSockOpt(fd, SO_RCVBUF, value);
}
Errno ProxySocket::SetSndTimeo(u32 value) {
send_timeout = value;
return SetSockOpt(fd, SO_SNDTIMEO, static_cast<int>(value));
}
Errno ProxySocket::SetRcvTimeo(u32 value) {
receive_timeout = value;
return SetSockOpt(fd, SO_RCVTIMEO, static_cast<int>(value));
}
Errno ProxySocket::SetNonBlock(bool enable) {
blocking = !enable;
return Errno::SUCCESS;
}
std::pair<Errno, Errno> ProxySocket::GetPendingError() {
LOG_DEBUG(Network, "(STUBBED) called");
LOG_DEBUG(Network, "called");
return {Errno::SUCCESS, Errno::SUCCESS};
}
+10 -27
View File
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -29,13 +29,13 @@ public:
std::pair<AcceptResult, Errno> Accept() override;
Errno Connect(SockAddrIn addr_in) override;
Errno Connect(Network::SockAddrIn addr_in) override;
std::pair<SockAddrIn, Errno> GetPeerName() override;
std::pair<Network::SockAddrIn, Errno> GetPeerName() override;
std::pair<SockAddrIn, Errno> GetSockName() override;
std::pair<Network::SockAddrIn, Errno> GetSockName() override;
Errno Bind(SockAddrIn addr) override;
Errno Bind(Network::SockAddrIn addr) override;
Errno Listen(s32 backlog) override;
@@ -43,9 +43,9 @@ public:
std::pair<s32, Errno> Recv(int flags, std::span<u8> message) override;
std::pair<s32, Errno> RecvFrom(int flags, std::span<u8> message, SockAddrIn* addr) override;
std::pair<s32, Errno> RecvFrom(int flags, std::span<u8> message, Network::SockAddrIn* addr) override;
std::pair<s32, Errno> ReceivePacket(int flags, std::span<u8> message, SockAddrIn* addr,
std::pair<s32, Errno> ReceivePacket(int flags, std::span<u8> message, Network::SockAddrIn* addr,
std::size_t max_length);
std::pair<s32, Errno> Send(std::span<const u8> message, int flags) override;
@@ -53,28 +53,11 @@ public:
void SendPacket(ProxyPacket& packet);
std::pair<s32, Errno> SendTo(u32 flags, std::span<const u8> message,
const SockAddrIn* addr) override;
Errno SetLinger(bool enable, u32 linger) override;
Errno SetReuseAddr(bool enable) override;
Errno SetBroadcast(bool enable) override;
Errno SetKeepAlive(bool enable) override;
Errno SetSndBuf(u32 value) override;
Errno SetRcvBuf(u32 value) override;
Errno SetSndTimeo(u32 value) override;
Errno SetRcvTimeo(u32 value) override;
const Network::SockAddrIn* addr) override;
Errno SetNonBlock(bool enable) override;
template <typename T>
Errno SetSockOpt(SOCKET fd, int option, T value);
Errno SetSockOpt(Network::SocketLevel level, Network::OptName option, std::span<const u8> value) override;
std::pair<Errno, Errno> GetPendingError() override;
@@ -86,7 +69,7 @@ private:
u32 send_timeout = 0;
u32 receive_timeout = 0;
bool is_bound = false;
SockAddrIn local_endpoint{};
Network::SockAddrIn local_endpoint{};
bool blocking = true;
std::queue<ProxyPacket> received_packets;
Protocol protocol;
+360
View File
@@ -0,0 +1,360 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <optional>
#include <string>
#include "common/common_types.h"
#include "common/common_funcs.h"
// Most of these structures are direct mappings of guest's
// expectations for these values, in other words, they're the
// values that HOS is expected to use AND handle.
namespace Network {
enum class Errno : u32 {
SUCCESS = 0,
BADF = 9,
AGAIN = 11,
INVAL = 22,
MFILE = 24,
PIPE = 32,
NOTSOCK = 88,
MSGSIZE = 90,
PROTOTYPE = 91,
NOPROTOOPT = 92,
PROTONOSUPPORT = 93,
SOCKTNOSUPPORT = 94,
NOTSUP = 95,
ADDRINUSE = 98,
ADDRNOTAVAIL = 99,
NETDOWN = 100,
NETUNREACH = 101,
CONNABORTED = 103,
CONNRESET = 104,
ISCONN = 106,
NOTCONN = 107,
TIMEDOUT = 110,
CONNREFUSED = 111,
HOSTUNREACH = 113,
ALREADY = 114,
INPROGRESS = 115,
STALE = 116,
/* made up error? */
OTHER = 196,
};
enum class GetAddrInfoError : s32 {
SUCCESS = 0,
ADDRFAMILY = 1,
AGAIN = 2,
BADFLAGS = 3,
FAIL = 4,
FAMILY = 5,
MEMORY = 6,
NODATA = 7,
NONAME = 8,
SERVICE = 9,
SOCKTYPE = 10,
SYSTEM = 11,
BADHINTS = 12,
PROTOCOL = 13,
OVERFLOW_ = 14, // avoid name collision with Windows macro
OTHER = 15,
};
enum class Domain : u32 {
Unspecified = 0,
UNIX = 1,
INET = 2,
IMPLINK = 3,
PUP = 4,
CHAOS = 5,
NETBIOS = 6,
ISO = 7,
ECMA = 8,
DATAKIT = 9,
CCITT = 10,
SNA = 11,
DECnet = 12,
DLI = 13,
LAT = 14,
HYLINK = 15,
APPLETALK = 16,
ROUTE = 17,
LINK = 18,
COIP = 20,
CNT = 21,
IPX = 23,
SIP = 24,
ISDN = 26,
INET6 = 28,
NATM = 29,
ATM = 30,
NETGRAPH = 32,
SLOW = 33,
SCLUSTER = 34,
ARP = 35,
BLUETOOTH = 36,
IEEE80211 = 37,
NETLINK = 38,
INET_SDP = 40,
INET6_SDP = 42,
};
enum class Type : u32 {
Unspecified = 0,
STREAM = 1,
DGRAM = 2,
RAW = 3,
RDM = 4,
SEQPACKET = 5,
};
enum class Protocol : u32 {
IP = 0,
ICMP = 1,
TCP = 6,
UDP = 17,
//
IPV6 = 41,
RAW = 255,
//
HOPOPTS = 0,
IGMP = 2,
GGP = 3,
IPV4 = 4,
ST = 7,
EGP = 8,
PIGP = 9,
RCCMON = 10,
NVPII = 11,
PUP = 12,
ARGUS = 13,
EMCON = 14,
XNET = 15,
CHAOS = 16,
MUX = 18,
MEAS = 19,
HMP = 20,
PRM = 21,
IDP = 22,
TRUNK1 = 23,
TRUNK2 = 24,
LEAF1 = 25,
LEAF2 = 26,
RDP = 27,
IRTP = 28,
TP = 29,
BLT = 30,
NSP = 31,
INP = 32,
DCCP = 33,
//3PC = 34,
IDPR = 35,
XTP = 36,
DDP = 37,
CMTP = 38,
TPXX = 39,
IL = 40,
SDRP = 42,
ROUTING = 43,
FRAGMENT = 44,
IDRP = 45,
RSVP = 46,
GRE = 47,
MHRP = 48,
BHA = 49,
ESP = 50,
AH = 51,
INLSP = 52,
SWIPE = 53,
NHRP = 54,
MOBILE = 55,
TLSP = 56,
SKIP = 57,
ICMPV6 = 58,
NONE = 59,
DSTOPTS = 60,
AHIP = 61,
CFTP = 62,
HELLO = 63,
SATEXPAK = 64,
KRYPTOLAN = 65,
RVD = 66,
IPPC = 67,
ADFS = 68,
SATMON = 69,
VISA = 70,
IPCV = 71,
CPNX = 72,
CPHB = 73,
WSN = 74,
PVP = 75,
BRSATMON = 76,
ND = 77,
WBMON = 78,
WBEXPAK = 79,
EON = 80,
VMTP = 81,
SVMTP = 82,
VINES = 83,
TTP = 84,
IGP = 85,
DGP = 86,
TCF = 87,
IGRP = 88,
OSPFIGP = 89,
SRPC = 90,
LARP = 91,
MTP = 92,
AX25 = 93,
IPEIP = 94,
MICP = 95,
SCCSP = 96,
ETHERIP = 97,
ENCAP = 98,
APES = 99,
GMTP = 100,
IPCOMP = 108,
SCTP = 132,
MH = 135,
UDPLITE = 136,
HIP = 139,
SHIM6 = 140,
PIM = 103,
CARP = 112,
PGM = 113,
MPLS = 137,
PFSYNC = 240,
};
enum class SocketLevel : u32 {
IP = 0,
ICMP = 1,
TCP = 6,
UDP = 17,
CONFIG = 0xfffe,
SOCKET = 0xffff, // i.e. SOL_SOCKET
};
enum class MsgOpt : u32 {
OOB = 0x00001,
PEEK = 0x00002,
DONTROUTE = 0x00004,
EOR_ = 0x00008,
TRUNC = 0x00010,
CTRUNC = 0x00020,
WAITALL = 0x00040,
DONTWAIT = 0x00080,
EOF_ = 0x00100,
NOSIGNAL = 0x20000,
};
enum class OptName : u32 {
DEBUG = 0x0001,
ACCEPTCONN = 0x0002,
REUSEADDR = 0x0004,
KEEPALIVE = 0x0008,
DONTROUTE = 0x0010,
BROADCAST = 0x0020,
USELOOPBACK = 0x0040,
LINGER = 0x0080,
OOBINLINE = 0x0100,
REUSEPORT = 0x0200,
TIMESTAMP = 0x0400,
NOSIGPIPE = 0x0800, // at least according to libnx
ACCEPTFILER = 0x1000,
SNDBUF = 0x1001,
RCVBUF = 0x1002,
SNDTIMEO = 0x1005,
RCVTIMEO = 0x1006,
ERROR_ = 0x1007, // avoid name collision with Windows macro
ACCEPTFILTER = 0x1000,
BINTIME = 0x2000,
NO_OFFLOAD = 0x4000,
NO_DDP = 0x8000,
};
enum class TcpOptName : u32 {
NODELAY = 0x0001,
MAXSEG = 0x0002,
NOPUSH = 0x0004,
NOOPT = 0x0008,
MS5SIG = 0x0010,
INFO = 0x0020
};
enum class ShutdownHow : s32 {
RD = 0,
WR = 1,
RDWR = 2,
};
enum class FcntlCmd : s32 {
GETFL = 3,
SETFL = 4,
};
enum class FcntlFlags : u32 {
NONBLOCK = 0x004,
NONBLOCK_NX = 0x800,
// Provided for convenience
NONBLOCK_ANY = u32(NONBLOCK) | u32(NONBLOCK_NX),
};
/// Array of IPv4 address
using IPv4Address = std::array<u8, 4>;
struct SockAddrIn {
u8 len;
u8 family;
u16 portno;
IPv4Address ip;
std::array<u8, 248> zeroes;
};
static_assert(sizeof(SockAddrIn) == 0x100);
enum class PollEvents : u16 {
// Using Pascal case because IN is a macro on Windows.
IN_ = 0x0001,
PRI_ = 0x0002,
OUT_ = 0x0004,
ERR_ = 0x0008,
HUP_ = 0x0010,
NVAL = 0x0020,
RDNORM = 0x0040,
RDBAND = 0x0080,
WRBAND = 0x0100,
IGNEOF = 0x2000,
};
DECLARE_ENUM_FLAG_OPERATORS(PollEvents);
struct PollFD {
s32 fd;
Network::PollEvents events;
Network::PollEvents revents;
};
static_assert(sizeof(PollFD) == 8);
struct Linger {
s32 onoff;
s32 linger;
};
static_assert(sizeof(Linger) == 8);
/// @brief Cross-platform addrinfo structure (not guest)
struct AddrInfo {
Domain family;
Type socket_type;
Protocol protocol;
SockAddrIn addr;
std::optional<std::string> canon_name;
};
} // namespace Network
+19 -54
View File
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
@@ -30,7 +30,7 @@ public:
struct AcceptResult {
std::unique_ptr<SocketBase> socket;
SockAddrIn sockaddr_in;
Network::SockAddrIn sockaddr_in;
};
SocketBase() = default;
@@ -46,13 +46,13 @@ public:
virtual std::pair<AcceptResult, Errno> Accept() = 0;
virtual Errno Connect(SockAddrIn addr_in) = 0;
virtual Errno Connect(Network::SockAddrIn addr_in) = 0;
virtual std::pair<SockAddrIn, Errno> GetPeerName() = 0;
virtual std::pair<Network::SockAddrIn, Errno> GetPeerName() = 0;
virtual std::pair<SockAddrIn, Errno> GetSockName() = 0;
virtual std::pair<Network::SockAddrIn, Errno> GetSockName() = 0;
virtual Errno Bind(SockAddrIn addr) = 0;
virtual Errno Bind(Network::SockAddrIn addr) = 0;
virtual Errno Listen(s32 backlog) = 0;
@@ -60,31 +60,16 @@ public:
virtual std::pair<s32, Errno> Recv(int flags, std::span<u8> message) = 0;
virtual std::pair<s32, Errno> RecvFrom(int flags, std::span<u8> message, SockAddrIn* addr) = 0;
virtual std::pair<s32, Errno> RecvFrom(int flags, std::span<u8> message, Network::SockAddrIn* addr) = 0;
virtual std::pair<s32, Errno> Send(std::span<const u8> message, int flags) = 0;
virtual std::pair<s32, Errno> SendTo(u32 flags, std::span<const u8> message,
const SockAddrIn* addr) = 0;
virtual Errno SetLinger(bool enable, u32 linger) = 0;
virtual Errno SetReuseAddr(bool enable) = 0;
virtual Errno SetKeepAlive(bool enable) = 0;
virtual Errno SetBroadcast(bool enable) = 0;
virtual Errno SetSndBuf(u32 value) = 0;
virtual Errno SetRcvBuf(u32 value) = 0;
virtual Errno SetSndTimeo(u32 value) = 0;
virtual Errno SetRcvTimeo(u32 value) = 0;
virtual std::pair<s32, Errno> SendTo(u32 flags, std::span<const u8> message, const Network::SockAddrIn* addr) = 0;
virtual Errno SetNonBlock(bool enable) = 0;
virtual Errno SetSockOpt(Network::SocketLevel level, Network::OptName option, std::span<const u8> value) = 0;
virtual std::pair<Errno, Errno> GetPendingError() = 0;
virtual bool IsOpened() const = 0;
@@ -95,7 +80,6 @@ public:
return fd;
}
protected:
SOCKET fd = INVALID_SOCKET;
};
@@ -114,13 +98,13 @@ public:
std::pair<AcceptResult, Errno> Accept() override;
Errno Connect(SockAddrIn addr_in) override;
Errno Connect(Network::SockAddrIn addr_in) override;
std::pair<SockAddrIn, Errno> GetPeerName() override;
std::pair<Network::SockAddrIn, Errno> GetPeerName() override;
std::pair<SockAddrIn, Errno> GetSockName() override;
std::pair<Network::SockAddrIn, Errno> GetSockName() override;
Errno Bind(SockAddrIn addr) override;
Errno Bind(Network::SockAddrIn addr) override;
Errno Listen(s32 backlog) override;
@@ -128,38 +112,19 @@ public:
std::pair<s32, Errno> Recv(int flags, std::span<u8> message) override;
std::pair<s32, Errno> RecvFrom(int flags, std::span<u8> message, SockAddrIn* addr) override;
std::pair<s32, Errno> RecvFrom(int flags, std::span<u8> message, Network::SockAddrIn* addr) override;
std::pair<s32, Errno> Send(std::span<const u8> message, int flags) override;
std::pair<s32, Errno> SendTo(u32 flags, std::span<const u8> message,
const SockAddrIn* addr) override;
Errno SetLinger(bool enable, u32 linger) override;
Errno SetReuseAddr(bool enable) override;
Errno SetKeepAlive(bool enable) override;
Errno SetBroadcast(bool enable) override;
Errno SetSndBuf(u32 value) override;
Errno SetRcvBuf(u32 value) override;
Errno SetSndTimeo(u32 value) override;
Errno SetRcvTimeo(u32 value) override;
std::pair<s32, Errno> SendTo(u32 flags, std::span<const u8> message, const Network::SockAddrIn* addr) override;
Errno SetNonBlock(bool enable) override;
template <typename T>
Errno SetSockOpt(SOCKET fd, int option, T value);
Errno SetSockOpt(Network::SocketLevel level, Network::OptName option, std::span<const u8> value) override;
std::pair<Errno, Errno> GetPendingError() override;
template <typename T>
std::pair<T, Errno> GetSockOpt(SOCKET fd, int option);
Errno GetSockOpt(Network::SocketLevel level, Network::OptName optname, std::span<u8> value);
bool IsOpened() const override;
@@ -169,6 +134,6 @@ private:
bool is_non_blocking = false;
};
std::pair<s32, Errno> Poll(std::vector<PollFD>& poll_fds, s32 timeout);
std::pair<s32, Errno> Poll(std::span<Network::HostPollFD> poll_fds, s32 timeout);
} // namespace Network
+1 -1
View File
@@ -25,7 +25,7 @@ endif()
# Dynarmic project options
option(DYNARMIC_ENABLE_CPU_FEATURE_DETECTION "Turning this off causes dynarmic to assume the host CPU doesn't support anything later than SSE3" ON)
if (PLATFORM_OPENBSD OR PLATFORM_DRAGONFLY OR PLATFORM_NETBSD)
if (OPENBSD OR DRAGONFLY OR NETBSD)
set(REQUIRE_WX ON)
else()
set(REQUIRE_WX OFF)
+1 -1
View File
@@ -363,7 +363,7 @@ elseif (APPLE)
backend/exception_handler_macos_mig.c
)
endif()
elseif (UNIX AND NOT PLATFORM_HAIKU)
elseif (UNIX AND NOT HAIKUOS)
# Haiku lacks <ucontext.h>
if (CMAKE_SYSTEM_NAME STREQUAL "Linux")
target_link_libraries(dynarmic PRIVATE rt)
@@ -234,7 +234,7 @@ private:
BlockOfCode block_of_code;
A32EmitX64 emitter;
Optimization::PolyfillOptions polyfill_options;
// Keep it here, you don't wanna mess with the fuckery that's initializer lists
// Keep it here in order to not mess with initializer lists
const A32::UserConfig conf;
Jit* jit_interface;
@@ -2160,8 +2160,6 @@ void EmitFPVectorToFixed(BlockOfCode& code, EmitContext& ctx, IR::Inst* inst) {
ROUNDING_MODE_CASE(CASE, 0x3e) \
ROUNDING_MODE_CASE(CASE, 0x3f)
// FUCK YOU MSVC, FUCKING DEPTH CANT EVEN HANDLE 8+16+32+64 DEPTH OF A ELSE STATMENT YOU FUCKING STUPID
// BURN MSVC BURN IT STUPID COMPILER CAN'T EVEN COMPILE THE MOST BASIC C++
ROUNDING_MODE_SWITCH(ToNearest_TieEven)
ROUNDING_MODE_SWITCH(TowardsPlusInfinity)
ROUNDING_MODE_SWITCH(TowardsMinusInfinity)
+1 -1
View File
@@ -787,7 +787,7 @@ static void FoldCountLeadingZeros(IR::Inst& inst, bool is_32_bit) {
/// Folds division operations based on the following:
///
/// 1. x / 0 -> 0 (NOTE: This is an ARM-specific behavior defined in the architecture reference manual)
/// 2a. 0x8000_0000 / 0xFFFF_FFFF -> 0x8000_0000 (NOTE: More ARM bullshit)
/// 2a. 0x8000_0000 / 0xFFFF_FFFF -> 0x8000_0000 (NOTE: More ARM errata)
/// 2b. 0x8000_0000_0000_0000 / 0xFFFF_FFFF_FFFF_FFFF -> 0x8000_0000_0000_0000
/// 3. imm_x / imm_y -> result
/// 4. x / 1 -> x
+1 -1
View File
@@ -151,7 +151,7 @@ if (MSVC)
)
else()
target_compile_options(hid_core PRIVATE
$<$<COMPILE_LANGUAGE:C,CXX>:-Werror=conversion>
$<$<COMPILE_LANGUAGE:C,CXX>:-Wconversion>
$<$<COMPILE_LANGUAGE:C,CXX>:-Wno-sign-conversion>
$<$<COMPILE_LANGUAGE:C,CXX>:-Wno-cast-function-type>
$<$<CXX_COMPILER_ID:Clang>:-fsized-deallocation>)
+1 -1
View File
@@ -44,7 +44,7 @@ if (MSVC)
/we4800 # Implicit conversion from 'type' to bool. Possible information loss
)
else()
target_compile_options(input_common PRIVATE $<$<COMPILE_LANGUAGE:C,CXX>:-Werror=conversion>)
target_compile_options(input_common PRIVATE $<$<COMPILE_LANGUAGE:C,CXX>:-Wconversion>)
endif()
if (ANDROID)
+5 -2
View File
@@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
@@ -157,8 +160,8 @@ Common::ParamPackage Android::BuildButtonParamPackageForButton(PadIdentifier ide
bool Android::MatchVID(Common::UUID device, const std::vector<std::string>& vids) const {
for (size_t i = 0; i < vids.size(); ++i) {
auto fucker = device.RawString();
if (fucker.find(vids[i]) != std::string::npos) {
auto dev_str = device.RawString();
if (dev_str.find(vids[i]) != std::string::npos) {
return true;
}
}
+7
View File
@@ -648,6 +648,13 @@ SDLDriver::SDLDriver(std::string input_engine_) : InputEngine(std::move(input_en
// Disable raw input. When enabled this setting causes SDL to die when a web applet opens
SDL_SetHint(SDL_HINT_JOYSTICK_RAWINPUT, Settings::values.enable_raw_input ? "1" : "0");
#ifdef _WIN32
if (Settings::values.disable_wgi_xinput) {
SDL_SetHintWithPriority(SDL_HINT_JOYSTICK_RAWINPUT_CORRELATE_XINPUT, "0", SDL_HINT_OVERRIDE);
SDL_SetHintWithPriority(SDL_HINT_JOYSTICK_WGI, "0", SDL_HINT_OVERRIDE);
}
#endif
// SDL3 defaults Steam Controller Bluetooth HIDAPI support to off, which can disable gyro.
SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_STEAM, "1");
SDL_SetHint(SDL_HINT_GAMECONTROLLER_SENSOR_FUSION, "1");
+2 -2
View File
@@ -1,4 +1,4 @@
# SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
# SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
# SPDX-License-Identifier: GPL-3.0-or-later
# SPDX-FileCopyrightText: 2022 yuzu Emulator Project
@@ -28,6 +28,6 @@ if (ENABLE_WEB_SERVICE)
endif()
# Solaris uses /lib/amd64/libsocket.so and /lib/amd64/libnsl.so
if (PLATFORM_SUN)
if (SOLARIS)
target_link_libraries(network PRIVATE socket nsl)
endif()
+2 -2
View File
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2017 Citra Emulator Project
@@ -12,7 +12,7 @@
#include <vector>
#include "common/announce_multiplayer_room.h"
#include "common/common_types.h"
#include "common/socket_types.h"
#include "core/internal_network/socket_types.h"
#include "network/verify_user.h"
namespace Network {
+8 -4
View File
@@ -10,7 +10,7 @@
#include <thread>
#include "common/assert.h"
#include "common/polyfill_thread.h"
#include "common/socket_types.h"
#include "core/internal_network/socket_types.h"
#include "enet/enet.h"
#include "network/packet.h"
#include "network/room_member.h"
@@ -358,19 +358,23 @@ void RoomMember::RoomMemberImpl::HandleProxyPackets(const ENetEvent* event) {
// Parse the ProxyPacket from the packet
u8 local_family;
packet.Read(local_family);
proxy_packet.local_endpoint.family = static_cast<Domain>(local_family);
proxy_packet.local_endpoint.len = 16;
proxy_packet.local_endpoint.family = u8(Network::Domain(local_family));
packet.Read(proxy_packet.local_endpoint.ip);
packet.Read(proxy_packet.local_endpoint.portno);
proxy_packet.local_endpoint.zeroes = {};
u8 remote_family;
packet.Read(remote_family);
proxy_packet.remote_endpoint.family = static_cast<Domain>(remote_family);
proxy_packet.remote_endpoint.len = 16;
proxy_packet.remote_endpoint.family = u8(Network::Domain(remote_family));
packet.Read(proxy_packet.remote_endpoint.ip);
packet.Read(proxy_packet.remote_endpoint.portno);
proxy_packet.local_endpoint.zeroes = {};
u8 protocol_type;
packet.Read(protocol_type);
proxy_packet.protocol = static_cast<Protocol>(protocol_type);
proxy_packet.protocol = Network::Protocol(protocol_type);
packet.Read(proxy_packet.broadcast);
packet.Read(proxy_packet.data);
+4 -4
View File
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2017 Citra Emulator Project
@@ -12,7 +12,7 @@
#include <vector>
#include "common/announce_multiplayer_room.h"
#include "common/common_types.h"
#include "common/socket_types.h"
#include "core/internal_network/socket_types.h"
#include "network/room.h"
namespace Network {
@@ -39,8 +39,8 @@ struct LDNPacket {
/// Information about the received proxy packets.
struct ProxyPacket {
SockAddrIn local_endpoint;
SockAddrIn remote_endpoint;
Network::SockAddrIn local_endpoint;
Network::SockAddrIn remote_endpoint;
Protocol protocol;
bool broadcast;
std::vector<u8> data;
@@ -227,8 +227,6 @@ std::unique_ptr<TranslationMap> InitializeTranslations(QObject* parent) {
tr("Preserves GPU-modified data by reading it back before uploading.\nSome games require this to render certain effects properly."));
INSERT(Settings, use_asynchronous_shaders, tr("Enable asynchronous shader compilation"),
tr("May reduce shader stutter."));
INSERT(Settings, use_unified_memory, tr("Enable unified memory access"),
tr("Lets the GPU write buffer readbacks directly into guest memory."));
INSERT(Settings, gpu_clock, tr("GPU Clocks"),
tr("Makes the game believe GPU work finishes faster than it does, so it stops lowering "
"resolution and render distance to fit the Switch's clocks."));
+1 -1
View File
@@ -253,7 +253,7 @@ if (MSVC)
)
else()
target_compile_options(shader_recompiler PRIVATE
$<$<COMPILE_LANGUAGE:C,CXX>:-Werror=conversion>
$<$<COMPILE_LANGUAGE:C,CXX>:-Wconversion>
# Bracket depth determines maximum size of a fold expression in Clang since 9c9974c3ccb6.
# And this in turns limits the size of a std::array.
$<$<CXX_COMPILER_ID:Clang>:-fbracket-depth=1024>
@@ -13,7 +13,6 @@
namespace Shader::Backend::SPIRV {
namespace {
Id SharedPointer(EmitContext& ctx, Id offset, u32 index_offset = 0) {
offset = ctx.BoundSharedOffset(offset, 4 + index_offset * 4);
const Id shift_id{ctx.Const(2U)};
Id index{ctx.OpShiftRightArithmetic(ctx.U32[1], offset, shift_id)};
if (index_offset > 0) {
@@ -161,8 +160,7 @@ Id EmitSharedAtomicExchange32(EmitContext& ctx, Id offset, Id value) {
Id EmitSharedAtomicExchange64(EmitContext& ctx, Id offset, Id value) {
if (ctx.profile.support_shared_int64_atomics && ctx.uses_explicit_workgroup_layout) {
const Id shift_id{ctx.Const(3U)};
const Id index{
ctx.OpShiftRightArithmetic(ctx.U32[1], ctx.BoundSharedOffset(offset, 8), shift_id)};
const Id index{ctx.OpShiftRightArithmetic(ctx.U32[1], offset, shift_id)};
const Id pointer{
ctx.OpAccessChain(ctx.shared_u64, ctx.shared_memory_u64, ctx.u32_zero_value, index)};
const auto [scope, semantics]{AtomicArgs(ctx)};
@@ -31,7 +31,6 @@ std::pair<Id, Id> ExtractArgs(EmitContext& ctx, Id offset, u32 mask, u32 count)
} // Anonymous namespace
Id EmitLoadSharedU8(EmitContext& ctx, Id offset) {
offset = ctx.BoundSharedOffset(offset, 1);
if (ctx.uses_explicit_workgroup_layout) {
const Id pointer{
ctx.OpAccessChain(ctx.shared_u8, ctx.shared_memory_u8, ctx.u32_zero_value, offset)};
@@ -43,7 +42,6 @@ Id EmitLoadSharedU8(EmitContext& ctx, Id offset) {
}
Id EmitLoadSharedS8(EmitContext& ctx, Id offset) {
offset = ctx.BoundSharedOffset(offset, 1);
if (ctx.uses_explicit_workgroup_layout) {
const Id pointer{
ctx.OpAccessChain(ctx.shared_u8, ctx.shared_memory_u8, ctx.u32_zero_value, offset)};
@@ -55,7 +53,6 @@ Id EmitLoadSharedS8(EmitContext& ctx, Id offset) {
}
Id EmitLoadSharedU16(EmitContext& ctx, Id offset) {
offset = ctx.BoundSharedOffset(offset, 2);
if (ctx.uses_explicit_workgroup_layout) {
const Id pointer{Pointer(ctx, ctx.shared_u16, ctx.shared_memory_u16, offset, 1)};
return ctx.OpUConvert(ctx.U32[1], ctx.OpLoad(ctx.U16, pointer));
@@ -66,7 +63,6 @@ Id EmitLoadSharedU16(EmitContext& ctx, Id offset) {
}
Id EmitLoadSharedS16(EmitContext& ctx, Id offset) {
offset = ctx.BoundSharedOffset(offset, 2);
if (ctx.uses_explicit_workgroup_layout) {
const Id pointer{Pointer(ctx, ctx.shared_u16, ctx.shared_memory_u16, offset, 1)};
return ctx.OpSConvert(ctx.U32[1], ctx.OpLoad(ctx.U16, pointer));
@@ -77,7 +73,6 @@ Id EmitLoadSharedS16(EmitContext& ctx, Id offset) {
}
Id EmitLoadSharedU32(EmitContext& ctx, Id offset) {
offset = ctx.BoundSharedOffset(offset, 4);
if (ctx.uses_explicit_workgroup_layout) {
const Id pointer{Pointer(ctx, ctx.shared_u32, ctx.shared_memory_u32, offset, 2)};
return ctx.OpLoad(ctx.U32[1], pointer);
@@ -87,7 +82,6 @@ Id EmitLoadSharedU32(EmitContext& ctx, Id offset) {
}
Id EmitLoadSharedU64(EmitContext& ctx, Id offset) {
offset = ctx.BoundSharedOffset(offset, 8);
if (ctx.uses_explicit_workgroup_layout) {
const Id pointer{Pointer(ctx, ctx.shared_u32x2, ctx.shared_memory_u32x2, offset, 3)};
return ctx.OpLoad(ctx.U32[2], pointer);
@@ -103,7 +97,6 @@ Id EmitLoadSharedU64(EmitContext& ctx, Id offset) {
}
Id EmitLoadSharedU128(EmitContext& ctx, Id offset) {
offset = ctx.BoundSharedOffset(offset, 16);
if (ctx.uses_explicit_workgroup_layout) {
const Id pointer{Pointer(ctx, ctx.shared_u32x4, ctx.shared_memory_u32x4, offset, 4)};
return ctx.OpLoad(ctx.U32[4], pointer);
@@ -120,7 +113,6 @@ Id EmitLoadSharedU128(EmitContext& ctx, Id offset) {
}
void EmitWriteSharedU8(EmitContext& ctx, Id offset, Id value) {
offset = ctx.BoundSharedOffset(offset, 1);
if (ctx.uses_explicit_workgroup_layout) {
const Id pointer{
ctx.OpAccessChain(ctx.shared_u8, ctx.shared_memory_u8, ctx.u32_zero_value, offset)};
@@ -131,7 +123,6 @@ void EmitWriteSharedU8(EmitContext& ctx, Id offset, Id value) {
}
void EmitWriteSharedU16(EmitContext& ctx, Id offset, Id value) {
offset = ctx.BoundSharedOffset(offset, 2);
if (ctx.uses_explicit_workgroup_layout) {
const Id pointer{Pointer(ctx, ctx.shared_u16, ctx.shared_memory_u16, offset, 1)};
ctx.OpStore(pointer, ctx.OpUConvert(ctx.U16, value));
@@ -141,7 +132,6 @@ void EmitWriteSharedU16(EmitContext& ctx, Id offset, Id value) {
}
void EmitWriteSharedU32(EmitContext& ctx, Id offset, Id value) {
offset = ctx.BoundSharedOffset(offset, 4);
Id pointer{};
if (ctx.uses_explicit_workgroup_layout) {
pointer = Pointer(ctx, ctx.shared_u32, ctx.shared_memory_u32, offset, 2);
@@ -154,7 +144,6 @@ void EmitWriteSharedU32(EmitContext& ctx, Id offset, Id value) {
}
void EmitWriteSharedU64(EmitContext& ctx, Id offset, Id value) {
offset = ctx.BoundSharedOffset(offset, 8);
if (ctx.uses_explicit_workgroup_layout) {
const Id pointer{Pointer(ctx, ctx.shared_u32x2, ctx.shared_memory_u32x2, offset, 3)};
ctx.OpStore(pointer, value);
@@ -170,7 +159,6 @@ void EmitWriteSharedU64(EmitContext& ctx, Id offset, Id value) {
}
void EmitWriteSharedU128(EmitContext& ctx, Id offset, Id value) {
offset = ctx.BoundSharedOffset(offset, 16);
if (ctx.uses_explicit_workgroup_layout) {
const Id pointer{Pointer(ctx, ctx.shared_u32x4, ctx.shared_memory_u32x4, offset, 4)};
ctx.OpStore(pointer, value);
@@ -600,16 +600,6 @@ void EmitContext::DefineLocalMemory(const IR::Program& program) {
}
}
Id EmitContext::BoundSharedOffset(Id offset, u32 access_bytes) {
if (shared_memory_declared_bytes == 0) {
return offset;
}
const u32 last_valid{shared_memory_declared_bytes > access_bytes
? shared_memory_declared_bytes - access_bytes
: 0U};
return OpUMin(U32[1], offset, Const(last_valid));
}
void EmitContext::DefineSharedMemory(const IR::Program& program) {
uses_explicit_workgroup_layout =
profile.support_explicit_workgroup_layout &&
@@ -618,15 +608,8 @@ void EmitContext::DefineSharedMemory(const IR::Program& program) {
if (program.shared_memory_size == 0) {
return;
}
const u32 device_limit{profile.max_shared_memory_size};
const u32 shared_memory_size{device_limit != 0 && program.shared_memory_size > device_limit
? device_limit
: program.shared_memory_size};
if (shared_memory_size != program.shared_memory_size) {
shared_memory_declared_bytes = shared_memory_size;
}
const auto make{[&](Id element_type, u32 element_size) {
const u32 num_elements{Common::DivCeil(shared_memory_size, element_size)};
const u32 num_elements{Common::DivCeil(program.shared_memory_size, element_size)};
const Id array_type{TypeArray(element_type, Const(num_elements))};
Decorate(array_type, spv::Decoration::ArrayStride, element_size);
@@ -661,7 +644,7 @@ void EmitContext::DefineSharedMemory(const IR::Program& program) {
std::tie(shared_memory_u32x4, shared_u32x4, std::ignore) = make(U32[4], 16);
return;
}
const u32 num_elements{Common::DivCeil(shared_memory_size, 4U)};
const u32 num_elements{Common::DivCeil(program.shared_memory_size, 4U)};
const Id type{TypeArray(U32[1], Const(num_elements))};
shared_memory_u32_type = TypePointer(spv::StorageClass::Workgroup, type);
@@ -312,8 +312,6 @@ public:
Id local_memory{};
bool uses_explicit_workgroup_layout{};
u32 shared_memory_declared_bytes{};
[[nodiscard]] Id BoundSharedOffset(Id offset, u32 access_bytes);
Id shared_memory_u8{};
Id shared_memory_u16{};
Id shared_memory_u32{};
-3
View File
@@ -101,9 +101,6 @@ struct Profile {
u32 gl_max_compute_smem_size{};
/// Largest workgroup shared memory allocation the device accepts, 0 when unconstrained
u32 max_shared_memory_size{};
/// Maxwell and earlier nVidia architectures have broken robust support
bool has_broken_robust{};
+7 -2
View File
@@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -16,9 +19,11 @@ TEST_CASE("Network::Errors", "[core]") {
}
Network::SockAddrIn addr{
Network::Domain::INET,
{127, 0, 0, 1},
16,
u8(Network::Domain::INET),
1, // hopefully nobody running this test has something listening on port 1
{127, 0, 0, 1},
{},
};
REQUIRE(socks[0].Connect(addr) == Network::Errno::CONNREFUSED);
+1 -1
View File
@@ -373,7 +373,7 @@ else()
$<$<COMPILE_LANGUAGE:C,CXX>:-Wno-shadow>
$<$<COMPILE_LANGUAGE:C,CXX>:-Wno-unused-local-typedef>)
else()
target_compile_options(video_core PRIVATE $<$<COMPILE_LANGUAGE:C,CXX>:-Werror=conversion>)
target_compile_options(video_core PRIVATE $<$<COMPILE_LANGUAGE:C,CXX>:-Wconversion>)
endif()
target_compile_options(video_core PRIVATE $<$<COMPILE_LANGUAGE:C,CXX>:-Wno-sign-conversion>)
+35 -213
View File
@@ -571,11 +571,7 @@ void BufferCache<P>::AccumulateFlushes() {
template <class P>
bool BufferCache<P>::ShouldWaitAsyncFlushes() const noexcept {
if (async_buffers.empty()) {
return false;
}
return async_buffers.front().has_value() ||
!pending_downloads.front().unified_copies.empty();
return (!async_buffers.empty() && async_buffers.front().has_value());
}
template <class P>
@@ -583,7 +579,6 @@ void BufferCache<P>::CommitAsyncFlushesHigh() {
AccumulateFlushes();
if (committed_gpu_modified_ranges.empty()) {
pending_downloads.emplace_back();
async_buffers.emplace_back(std::optional<Async_Buffer>{});
return;
}
@@ -643,84 +638,27 @@ void BufferCache<P>::CommitAsyncFlushesHigh() {
}
committed_gpu_modified_ranges.clear();
if (downloads.empty()) {
pending_downloads.emplace_back();
async_buffers.emplace_back(std::optional<Async_Buffer>{});
return;
}
struct QueuedUnifiedCopy {
u64 window;
BufferId buffer_id;
boost::container::small_vector<BufferCopy, 16> copies;
};
AsyncDownloadBatch batch;
boost::container::small_vector<std::pair<BufferCopy, BufferId>, 16> staging_downloads;
boost::container::small_vector<QueuedUnifiedCopy, 4> unified_copy_queue;
boost::container::small_vector<u64, 4> window_ids;
UnifiedWindowGroups groups;
u64 staging_size_bytes = 0;
for (auto& [copy, buffer_id] : downloads) {
Buffer& buffer = slot_buffers[buffer_id];
const DAddr orig_device_addr = buffer.CpuAddr() + copy.src_offset;
bool unified = false;
if constexpr (USE_UNIFIED_MEMORY) {
if (runtime.HasUnifiedMemory()) {
window_ids.clear();
groups.clear();
unified = ResolveUnifiedWindows(orig_device_addr, copy.src_offset, copy.size,
window_ids, groups);
}
}
BufferCopy record{copy};
record.src_offset = static_cast<size_t>(orig_device_addr);
if (unified) {
async_downloads.Add(orig_device_addr, copy.size);
buffer.MarkUsage(copy.src_offset, copy.size);
for (size_t i = 0; i < window_ids.size(); ++i) {
unified_copy_queue.push_back(
QueuedUnifiedCopy{window_ids[i], buffer_id, std::move(groups[i])});
}
batch.unified_copies.push_back(record);
continue;
}
copy.dst_offset = staging_size_bytes;
constexpr u64 align = 64ULL;
staging_size_bytes += (copy.size + align - 1) & ~(align - 1ULL);
staging_downloads.push_back({copy, buffer_id});
}
std::optional<Async_Buffer> download_staging;
if (!staging_downloads.empty()) {
download_staging = runtime.DownloadStagingBuffer(staging_size_bytes, true);
}
auto download_staging = runtime.DownloadStagingBuffer(total_size_bytes, true);
boost::container::small_vector<BufferCopy, 4> normalized_copies;
runtime.PreCopyBarrier();
for (auto& [copy, buffer_id] : staging_downloads) {
copy.dst_offset += download_staging->offset;
for (auto& [copy, buffer_id] : downloads) {
copy.dst_offset += download_staging.offset;
const std::array copies{copy};
BufferCopy second_copy{copy};
Buffer& buffer = slot_buffers[buffer_id];
BufferCopy record{copy};
record.src_offset = static_cast<size_t>(buffer.CpuAddr()) + copy.src_offset;
const DAddr orig_device_addr = static_cast<DAddr>(record.src_offset);
second_copy.src_offset = static_cast<size_t>(buffer.CpuAddr()) + copy.src_offset;
const DAddr orig_device_addr = static_cast<DAddr>(second_copy.src_offset);
async_downloads.Add(orig_device_addr, copy.size);
buffer.MarkUsage(copy.src_offset, copy.size);
runtime.CopyBuffer(download_staging->buffer, buffer, copies, false);
batch.staging_copies.push_back(record);
}
if constexpr (USE_UNIFIED_MEMORY) {
for (const auto& queued : unified_copy_queue) {
const std::span<const BufferCopy> group_span(queued.copies.data(),
queued.copies.size());
runtime.CopyToUnifiedMemory(queued.window, slot_buffers[queued.buffer_id], group_span);
}
if (!unified_copy_queue.empty()) {
runtime.FlushUnifiedMemoryCopies();
runtime.UnifiedMemoryHostBarrier();
}
runtime.CopyBuffer(download_staging.buffer, buffer, copies, false);
normalized_copies.push_back(second_copy);
}
runtime.PostCopyBarrier();
pending_downloads.emplace_back(std::move(batch));
async_buffers.emplace_back(std::move(download_staging));
pending_downloads.emplace_back(std::move(normalized_copies));
async_buffers.emplace_back(download_staging);
}
template <class P>
@@ -735,49 +673,32 @@ void BufferCache<P>::PopAsyncFlushes() {
template <class P>
void BufferCache<P>::PopAsyncBuffers() {
struct Writeback {
DAddr addr;
const u8* src;
u64 size;
};
boost::container::small_vector<Writeback, 8> writebacks;
{
std::scoped_lock lock{mutex};
if (async_buffers.empty()) {
return;
}
auto& batch = pending_downloads.front();
auto& async_buffer = async_buffers.front();
if (async_buffer.has_value()) {
const u8* base = async_buffer->mapped_span.data();
const size_t base_offset = async_buffer->offset;
for (const auto& copy : batch.staging_copies) {
const DAddr device_addr = static_cast<DAddr>(copy.src_offset);
const u64 dst_offset = copy.dst_offset - base_offset;
const u8* read_mapped_memory = base + dst_offset;
async_downloads.ForEachInRange(
device_addr, copy.size, [&](DAddr start, DAddr end, s32) {
writebacks.push_back(
{start, &read_mapped_memory[start - device_addr], end - start});
});
async_downloads.Subtract(device_addr, copy.size, [&](DAddr start, DAddr end) {
gpu_modified_ranges.Subtract(start, end - start);
});
}
async_buffers_death_ring.emplace_back(*async_buffer);
}
for (const auto& copy : batch.unified_copies) {
const DAddr device_addr = static_cast<DAddr>(copy.src_offset);
async_downloads.Subtract(device_addr, copy.size, [&](DAddr start, DAddr end) {
gpu_modified_ranges.Subtract(start, end - start);
});
}
if (async_buffers.empty()) {
return;
}
if (!async_buffers.front().has_value()) {
async_buffers.pop_front();
pending_downloads.pop_front();
return;
}
for (const auto& wb : writebacks) {
device_memory.WriteBlockUnsafe(wb.addr, wb.src, wb.size);
auto& downloads = pending_downloads.front();
auto& async_buffer = async_buffers.front();
u8* base = async_buffer->mapped_span.data();
const size_t base_offset = async_buffer->offset;
for (const auto& copy : downloads) {
const DAddr device_addr = static_cast<DAddr>(copy.src_offset);
const u64 dst_offset = copy.dst_offset - base_offset;
const u8* read_mapped_memory = base + dst_offset;
async_downloads.ForEachInRange(device_addr, copy.size, [&](DAddr start, DAddr end, s32) {
device_memory.WriteBlockUnsafe(start, &read_mapped_memory[start - device_addr],
end - start);
});
async_downloads.Subtract(device_addr, copy.size, [&](DAddr start, DAddr end) {
gpu_modified_ranges.Subtract(start, end - start);
});
}
async_buffers_death_ring.emplace_back(*async_buffer);
async_buffers.pop_front();
pending_downloads.pop_front();
}
template <class P>
@@ -1778,99 +1699,6 @@ void BufferCache<P>::ImmediateUploadMemory([[maybe_unused]] Buffer& buffer,
}
}
template <class P>
bool BufferCache<P>::ResolveUnifiedWindows(
[[maybe_unused]] DAddr device_addr, [[maybe_unused]] u64 buffer_offset,
[[maybe_unused]] u64 size, [[maybe_unused]] boost::container::small_vector<u64, 4>& window_ids,
[[maybe_unused]] UnifiedWindowGroups& groups) {
if constexpr (USE_UNIFIED_MEMORY) {
const u8* const physical_base = device_memory.GetPhysicalBase();
const u64 unified_base = runtime.UnifiedMemoryBase();
const u64 unified_size = runtime.UnifiedMemorySize();
const u64 window_size = runtime.UnifiedMemoryWindowSize();
if (window_size == 0) {
return false;
}
const auto group_for = [&](u64 window) -> boost::container::small_vector<BufferCopy, 16>& {
for (size_t i = 0; i < window_ids.size(); ++i) {
if (window_ids[i] == window) {
return groups[i];
}
}
window_ids.push_back(window);
groups.emplace_back();
return groups.back();
};
u64 downloaded = 0;
while (downloaded < size) {
const DAddr page_addr = device_addr + downloaded;
const u8* const ptr = device_memory.GetPointer<u8>(page_addr);
if (ptr == nullptr) {
return false;
}
const u64 page_offset = page_addr & Core::DEVICE_PAGEMASK;
u64 chunk = (std::min)(size - downloaded,
static_cast<u64>(Core::DEVICE_PAGESIZE) - page_offset);
const u64 phys_offset = static_cast<u64>(ptr - physical_base);
if (phys_offset < unified_base || phys_offset - unified_base + chunk > unified_size) {
return false;
}
const u64 relative = phys_offset - unified_base;
const u64 window = relative / window_size;
const u64 local_offset = relative % window_size;
chunk = (std::min)(chunk, window_size - local_offset);
auto& group = group_for(window);
if (!group.empty()) {
BufferCopy& last = group.back();
if (last.src_offset + last.size == buffer_offset + downloaded &&
last.dst_offset + last.size == local_offset) {
last.size += chunk;
downloaded += chunk;
continue;
}
}
group.push_back(BufferCopy{
.src_offset = buffer_offset + downloaded,
.dst_offset = local_offset,
.size = chunk,
});
downloaded += chunk;
}
return true;
} else {
return false;
}
}
template <class P>
bool BufferCache<P>::TryUnifiedDownloadMemory([[maybe_unused]] Buffer& buffer,
[[maybe_unused]] std::span<BufferCopy> copies) {
if constexpr (USE_UNIFIED_MEMORY) {
boost::container::small_vector<u64, 4> window_ids;
UnifiedWindowGroups groups;
for (const BufferCopy& copy : copies) {
if (!ResolveUnifiedWindows(buffer.CpuAddr() + copy.src_offset, copy.src_offset,
copy.size, window_ids, groups)) {
return false;
}
}
for (const BufferCopy& copy : copies) {
buffer.MarkUsage(copy.src_offset, copy.size);
}
runtime.PreCopyBarrier();
for (size_t i = 0; i < window_ids.size(); ++i) {
const std::span<const BufferCopy> group_span(groups[i].data(), groups[i].size());
runtime.CopyToUnifiedMemory(window_ids[i], buffer, group_span);
}
runtime.FlushUnifiedMemoryCopies();
runtime.UnifiedMemoryHostBarrier();
runtime.Finish();
return true;
} else {
return false;
}
}
template <class P>
void BufferCache<P>::MappedUploadMemory([[maybe_unused]] Buffer& buffer,
[[maybe_unused]] u64 total_size_bytes,
@@ -1974,12 +1802,6 @@ void BufferCache<P>::DownloadBufferMemory(Buffer& buffer, DAddr device_addr, u64
}
if constexpr (USE_MEMORY_MAPS) {
if constexpr (USE_UNIFIED_MEMORY) {
if (runtime.HasUnifiedMemory() &&
TryUnifiedDownloadMemory(buffer, std::span(copies.data(), copies.size()))) {
return;
}
}
auto download_staging = runtime.DownloadStagingBuffer(total_size_bytes);
const u8* const mapped_memory = download_staging.mapped_span.data();
const std::span<BufferCopy> copies_span(copies.data(), copies.data() + copies.size());
@@ -180,7 +180,6 @@ class BufferCache : public VideoCommon::ChannelSetupCaches<BufferCacheChannelInf
static constexpr bool USE_MEMORY_MAPS = P::USE_MEMORY_MAPS;
static constexpr bool SEPARATE_IMAGE_BUFFERS_BINDINGS = P::SEPARATE_IMAGE_BUFFER_BINDINGS;
static constexpr bool USE_MEMORY_MAPS_FOR_UPLOADS = P::USE_MEMORY_MAPS_FOR_UPLOADS;
static constexpr bool USE_UNIFIED_MEMORY = P::USE_UNIFIED_MEMORY;
#ifdef YUZU_LEGACY
static constexpr s64 TARGET_THRESHOLD = 3_GiB;
@@ -444,15 +443,6 @@ private:
void MappedUploadMemory(Buffer& buffer, u64 total_size_bytes, std::span<BufferCopy> copies);
bool TryUnifiedDownloadMemory(Buffer& buffer, std::span<BufferCopy> copies);
using UnifiedWindowGroups =
boost::container::small_vector<boost::container::small_vector<BufferCopy, 16>, 4>;
bool ResolveUnifiedWindows(DAddr device_addr, u64 buffer_offset, u64 size,
boost::container::small_vector<u64, 4>& window_ids,
UnifiedWindowGroups& groups);
void DownloadBufferMemory(Buffer& buffer_id);
void DownloadBufferMemory(Buffer& buffer_id, DAddr device_addr, u64 size);
@@ -508,14 +498,9 @@ private:
std::deque<Common::RangeSet<DAddr>> committed_gpu_modified_ranges;
// Async Buffers
struct AsyncDownloadBatch {
boost::container::small_vector<BufferCopy, 4> staging_copies;
boost::container::small_vector<BufferCopy, 4> unified_copies;
};
Common::OverlapRangeSet<DAddr> async_downloads;
std::deque<std::optional<Async_Buffer>> async_buffers;
std::deque<AsyncDownloadBatch> pending_downloads;
std::deque<boost::container::small_vector<BufferCopy, 4>> pending_downloads;
std::optional<Async_Buffer> current_buffer;
std::deque<Async_Buffer> async_buffers_death_ring;
+1 -1
View File
@@ -2257,7 +2257,7 @@ public:
/// Returns whether the vertex array specified by index is supposed to be
/// accessed per instance or not.
bool IsInstancingEnabled(std::size_t index) const {
return bool(is_instanced[index]); //FUCK YOU MSVC
return bool(is_instanced[index]);
}
};
-1
View File
@@ -40,7 +40,6 @@ public:
}
struct Regs {
// No fucking idea
INSERT_PADDING_BYTES_NOINIT(0x48);
} regs{};
private:
+1 -1
View File
@@ -963,7 +963,7 @@ void Vic::WriteABGR(const OutputSurfaceConfig& output_surface_config, VideoPixel
auto pixel1213 = _mm_load_si128((__m128i*)&inp[src + x + 12]);
auto pixel1415 = _mm_load_si128((__m128i*)&inp[src + x + 14]);
// Right-shift the channels by 16 to un-do the left shit on read and bring the range
// Right-shift the channels by 16 to un-do the left shift on read and bring the range
// back to 8-bit.
pixel01 = _mm_srli_epi16(pixel01, 2);
pixel23 = _mm_srli_epi16(pixel23, 2);
+1 -1
View File
@@ -88,7 +88,7 @@ set(SHADER_FILES
${CMAKE_CURRENT_SOURCE_DIR}/sgsr1_shader_mobile_edge_direction.frag
)
if (PLATFORM_HAIKU)
if (HAIKUOS)
# glslangValidator WILL crash, glslang will not
set(GLSLANGVALIDATOR "glslang")
else()
+1 -1
View File
@@ -441,7 +441,7 @@ void MacroInterpreterImpl::Reset() {
pc = 0;
delayed_pc = {};
method_address.raw = 0;
// Vector must hold its last indices otherwise wonky shit will happen
// Vector must hold its last indices otherwise chaos will ensue
// The next parameter index starts at 1, because $r1 already has the value of the first
// parameter.
next_parameter_index = 1;
@@ -261,7 +261,6 @@ struct BufferCacheParams {
// TODO: Investigate why OpenGL seems to perform worse with persistently mapped buffer uploads
static constexpr bool USE_MEMORY_MAPS_FOR_UPLOADS = false;
static constexpr bool USE_UNIFIED_MEMORY = false;
};
using BufferCache = VideoCommon::BufferCache<BufferCacheParams>;
+1 -1
View File
@@ -59,7 +59,7 @@ std::vector<std::string> GetExtensions() {
std::vector<std::string> extensions;
for (GLint index = 0; index < num_extensions; ++index) {
auto const* p = reinterpret_cast<const char*>(glGetStringi(GL_EXTENSIONS, GLuint(index)));
if (p != nullptr) // Fuck you? - sincerely, buggy mesa drivers
if (p != nullptr)
extensions.push_back(std::string{p});
}
return extensions;
@@ -237,7 +237,6 @@ ShaderCache::ShaderCache(Tegra::MaxwellDeviceMemoryManager& device_memory_,
.has_gl_bool_ref_bug = device.HasBoolRefBug(),
.ignore_nan_fp_comparisons = true,
.gl_max_compute_smem_size = device.GetMaxComputeSharedMemorySize(),
.max_shared_memory_size = device.GetMaxComputeSharedMemorySize(),
.min_ssbo_alignment = device.GetShaderStorageBufferAlignment(),
// Use the host limit, but never more than the guest can produce. Maxwell exposes 8 clip
// distances and the SPIR-V output array is sized for at most 8, so clamping here keeps a
@@ -8,7 +8,6 @@
#include <array>
#include <cstring>
#include <span>
#include <utility>
#include <vector>
#include "video_core/buffer_cache/buffer_cache_base.h"
@@ -33,32 +32,6 @@ VkBufferCopy MakeBufferCopy(const VideoCommon::BufferCopy& copy) {
};
}
constexpr size_t MAX_WINDOW_BARRIER_RANGES = 8;
using WindowRange = std::pair<VkDeviceSize, VkDeviceSize>;
using WindowRanges = boost::container::small_vector<WindowRange, MAX_WINDOW_BARRIER_RANGES>;
void CoalesceWindowRanges(WindowRanges& ranges) {
if (ranges.size() < 2) {
return;
}
std::sort(ranges.begin(), ranges.end());
size_t merged = 0;
for (size_t index = 1; index < ranges.size(); ++index) {
if (ranges[index].first <= ranges[merged].second) {
ranges[merged].second = (std::max)(ranges[merged].second, ranges[index].second);
} else {
ranges[++merged] = ranges[index];
}
}
ranges.resize(merged + 1);
if (ranges.size() > MAX_WINDOW_BARRIER_RANGES) {
const WindowRange bounding{ranges.front().first, ranges.back().second};
ranges.clear();
ranges.push_back(bounding);
}
}
VkIndexType IndexTypeFromNumElements(const Device& device, u32 num_elements) {
if (num_elements <= 0xff && device.IsExtIndexTypeUint8Supported()) {
return VK_INDEX_TYPE_UINT8_EXT;
@@ -392,141 +365,6 @@ BufferCacheRuntime::BufferCacheRuntime(const Device& device_, MemoryAllocator& m
scheduler_, staging_pool_);
}
void BufferCacheRuntime::TryEnableUnifiedMemory(void* base, size_t size,
std::span<AHardwareBuffer* const> hardware_buffers,
size_t hardware_buffer_window,
size_t hardware_buffer_base) {
unified_memory = std::make_unique<HostMemoryImport>(
device, base, size, hardware_buffers, hardware_buffer_window, hardware_buffer_base);
if (!unified_memory->IsValid()) {
unified_memory.reset();
}
}
void BufferCacheRuntime::CopyToUnifiedMemory(
size_t window_index, VkBuffer src_buffer,
std::span<const VideoCommon::BufferCopy> copies) {
if (!unified_memory || src_buffer == VK_NULL_HANDLE || copies.empty() ||
window_index >= unified_memory->GetWindowCount() ||
unified_memory->GetWindowBuffer(window_index) == VK_NULL_HANDLE) {
return;
}
PendingUnifiedCopy& pending = pending_unified_copies.emplace_back();
pending.window = window_index;
pending.buffer = src_buffer;
pending.copies.resize(copies.size());
std::ranges::transform(copies, pending.copies.begin(), MakeBufferCopy);
}
void BufferCacheRuntime::FlushUnifiedMemoryCopies() {
if (pending_unified_copies.empty()) {
return;
}
struct UnifiedCopyCommand {
VkBuffer buffer;
boost::container::small_vector<VkBufferCopy, 8> copies;
};
std::stable_sort(pending_unified_copies.begin(), pending_unified_copies.end(),
[](const PendingUnifiedCopy& lhs, const PendingUnifiedCopy& rhs) {
return lhs.window < rhs.window;
});
const bool foreign = unified_memory->NeedsForeignOwnershipTransfer();
const u32 queue_family = device.GetGraphicsFamily();
size_t group_begin = 0;
while (group_begin < pending_unified_copies.size()) {
const size_t window = pending_unified_copies[group_begin].window;
size_t group_end = group_begin;
while (group_end < pending_unified_copies.size() &&
pending_unified_copies[group_end].window == window) {
++group_end;
}
const VkBuffer window_buffer = unified_memory->GetWindowBuffer(window);
WindowRanges ranges;
for (size_t index = group_begin; index < group_end; ++index) {
for (const VkBufferCopy& copy : pending_unified_copies[index].copies) {
ranges.emplace_back(copy.dstOffset, copy.dstOffset + copy.size);
}
}
CoalesceWindowRanges(ranges);
boost::container::small_vector<VkBufferMemoryBarrier, MAX_WINDOW_BARRIER_RANGES> acquire;
boost::container::small_vector<VkBufferMemoryBarrier, MAX_WINDOW_BARRIER_RANGES> release;
if (foreign) {
for (const WindowRange& range : ranges) {
acquire.push_back(VkBufferMemoryBarrier{
.sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER,
.pNext = nullptr,
.srcAccessMask = 0,
.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT,
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_FOREIGN_EXT,
.dstQueueFamilyIndex = queue_family,
.buffer = window_buffer,
.offset = range.first,
.size = range.second - range.first,
});
release.push_back(VkBufferMemoryBarrier{
.sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER,
.pNext = nullptr,
.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT,
.dstAccessMask = 0,
.srcQueueFamilyIndex = queue_family,
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_FOREIGN_EXT,
.buffer = window_buffer,
.offset = range.first,
.size = range.second - range.first,
});
}
}
boost::container::small_vector<UnifiedCopyCommand, 4> commands;
commands.reserve(group_end - group_begin);
for (size_t index = group_begin; index < group_end; ++index) {
PendingUnifiedCopy& pending = pending_unified_copies[index];
commands.push_back(UnifiedCopyCommand{pending.buffer, std::move(pending.copies)});
}
scheduler.RequestOutsideRenderPassOperationContext();
scheduler.Record([window_buffer, acquire = std::move(acquire), release = std::move(release),
commands = std::move(commands)](vk::CommandBuffer cmdbuf) {
if (!acquire.empty()) {
cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
VK_PIPELINE_STAGE_TRANSFER_BIT, 0, {},
VideoCommon::FixSmallVectorADL(acquire), {});
}
for (const UnifiedCopyCommand& command : commands) {
cmdbuf.CopyBuffer(command.buffer, window_buffer,
VideoCommon::FixSmallVectorADL(command.copies));
}
if (!release.empty()) {
cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT,
VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, 0, {},
VideoCommon::FixSmallVectorADL(release), {});
}
});
group_begin = group_end;
}
pending_unified_copies.clear();
}
void BufferCacheRuntime::UnifiedMemoryHostBarrier() {
static constexpr VkMemoryBarrier HOST_BARRIER{
.sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER,
.pNext = nullptr,
.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT,
.dstAccessMask = VK_ACCESS_HOST_READ_BIT,
};
scheduler.RequestOutsideRenderPassOperationContext();
scheduler.Record([](vk::CommandBuffer cmdbuf) {
cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_HOST_BIT, 0,
HOST_BARRIER);
});
}
StagingBufferRef BufferCacheRuntime::UploadStagingBuffer(size_t size) {
return staging_pool.Request(size, MemoryUsage::Upload);
}
@@ -564,7 +402,6 @@ u32 BufferCacheRuntime::GetStorageBufferAlignment() const {
}
void BufferCacheRuntime::TickFrame(Common::SlotVector<Buffer>& slot_buffers) noexcept {
FlushUnifiedMemoryCopies();
for (auto it = slot_buffers.begin(); it != slot_buffers.end(); it++) {
if (scheduler.IsFree(it->LastUsageTick())) {
it->ResetUsageTracking();
@@ -7,10 +7,6 @@
#pragma once
#include <limits>
#include <memory>
#include <span>
#include <boost/container/small_vector.hpp>
#include "video_core/buffer_cache/buffer_cache_base.h"
#include "video_core/buffer_cache/memory_tracker_base.h"
@@ -101,33 +97,6 @@ public:
void TickFrame(Common::SlotVector<Buffer>& slot_buffers) noexcept;
void TryEnableUnifiedMemory(void* base, size_t size,
std::span<AHardwareBuffer* const> hardware_buffers,
size_t hardware_buffer_window, size_t hardware_buffer_base);
[[nodiscard]] bool HasUnifiedMemory() const noexcept {
return unified_memory != nullptr && unified_memory->IsValid();
}
[[nodiscard]] u64 UnifiedMemorySize() const noexcept {
return unified_memory ? unified_memory->GetSize() : 0;
}
[[nodiscard]] u64 UnifiedMemoryBase() const noexcept {
return unified_memory ? unified_memory->GetBaseOffset() : 0;
}
[[nodiscard]] u64 UnifiedMemoryWindowSize() const noexcept {
return unified_memory ? unified_memory->GetWindowSize() : 0;
}
void CopyToUnifiedMemory(size_t window_index, VkBuffer src_buffer,
std::span<const VideoCommon::BufferCopy> copies);
void FlushUnifiedMemoryCopies();
void UnifiedMemoryHostBarrier();
u64 CurrentTick();
u64 KnownGpuTick();
@@ -211,12 +180,6 @@ public:
}
private:
struct PendingUnifiedCopy {
size_t window;
VkBuffer buffer;
boost::container::small_vector<VkBufferCopy, 8> copies;
};
void BindBuffer(const Buffer& buffer, u32 offset, u32 size) {
const VkBuffer handle = buffer.Handle();
if (handle == VK_NULL_HANDLE) {
@@ -241,8 +204,6 @@ private:
std::shared_ptr<QuadStripIndexBuffer> quad_strip_index_buffer;
vk::Buffer null_buffer;
std::unique_ptr<HostMemoryImport> unified_memory;
boost::container::small_vector<PendingUnifiedCopy, 8> pending_unified_copies;
std::unique_ptr<Uint8Pass> uint8_pass;
QuadIndexedPass quad_index_pass;
@@ -265,7 +226,6 @@ struct BufferCacheParams {
static constexpr bool USE_MEMORY_MAPS = true;
static constexpr bool SEPARATE_IMAGE_BUFFER_BINDINGS = false;
static constexpr bool USE_MEMORY_MAPS_FOR_UPLOADS = true;
static constexpr bool USE_UNIFIED_MEMORY = true;
};
using BufferCache = VideoCommon::BufferCache<BufferCacheParams>;
@@ -446,7 +446,6 @@ PipelineCache::PipelineCache(Tegra::MaxwellDeviceMemoryManager& device_memory_,
.has_broken_fp16_float_controls = driver_id == VK_DRIVER_ID_NVIDIA_PROPRIETARY,
.ignore_nan_fp_comparisons = false,
.has_broken_spirv_subgroup_mask_vector_extract_dynamic = false,
.max_shared_memory_size = device.GetMaxComputeSharedMemorySize(),
.has_broken_robust =
device.IsNvidia() && device.GetNvidiaArch() <= NvidiaArchitecture::Arch_Pascal,
.min_ssbo_alignment = device.GetStorageBufferAlignment(),
@@ -922,6 +921,19 @@ std::unique_ptr<ComputePipeline> PipelineCache::CreateComputePipeline(
}
auto program{TranslateProgram(pools.inst, pools.block, env, cfg, host_info)};
const VkDriverIdKHR driver_id = device.GetDriverID();
const bool needs_shared_mem_clamp =
driver_id == VK_DRIVER_ID_QUALCOMM_PROPRIETARY ||
driver_id == VK_DRIVER_ID_ARM_PROPRIETARY;
const u32 max_shared_memory = device.GetMaxComputeSharedMemorySize();
if (needs_shared_mem_clamp && program.shared_memory_size > max_shared_memory) {
LOG_WARNING(Render_Vulkan,
"Compute shader {:#016x} requests {}KB shared memory but device max is {}KB - clamping",
key.unique_hash,
program.shared_memory_size / 1024,
max_shared_memory / 1024);
program.shared_memory_size = max_shared_memory;
}
const std::vector<u32> code{EmitSPIRV(profile, program)};
device.SaveShader(code);
vk::ShaderModule spv_module{BuildShader(device, code)};
@@ -225,13 +225,6 @@ RasterizerVulkan::RasterizerVulkan(Core::Frontend::EmuWindow& emu_window_, Tegra
fence_manager(*this, gpu, texture_cache, buffer_cache, query_cache, device, scheduler),
wfi_event(device.GetLogical().CreateEvent()) {
scheduler.SetQueryCache(query_cache);
if (Settings::values.use_unified_memory.GetValue() && device_memory.IsBackingShared()) {
buffer_cache_runtime.TryEnableUnifiedMemory(
device_memory.GetPhysicalBase(), device_memory.GetPhysicalSize(),
device_memory.GetBackingHardwareBuffers(),
device_memory.GetBackingHardwareBufferWindowSize(),
device_memory.GetBackingHardwareBufferBase());
}
}
RasterizerVulkan::~RasterizerVulkan() {
@@ -16,7 +16,6 @@
#include <fmt/format.h>
#include "common/assert.h"
#include "common/host_memory.h"
#include "common/literals.h"
#include <ranges>
#include "common/settings.h"
@@ -975,7 +974,6 @@ bool Device::GetSuitability(bool requires_swapchain) {
FOR_EACH_VK_FEATURE_EXT(FEATURE_EXTENSION);
FOR_EACH_VK_EXTENSION(EXTENSION);
FOR_EACH_VK_PLATFORM_EXTENSION(EXTENSION);
if (supported_extensions.contains(VK_KHR_ROBUSTNESS_2_EXTENSION_NAME)) {
loaded_extensions.erase(VK_EXT_ROBUSTNESS_2_EXTENSION_NAME);
@@ -988,13 +986,6 @@ bool Device::GetSuitability(bool requires_swapchain) {
extensions.robustness_2 = false;
}
#ifdef __ANDROID__
if (extensions.external_memory_ahb && !extensions.queue_family_foreign) {
loaded_extensions.erase(VK_ANDROID_EXTERNAL_MEMORY_ANDROID_HARDWARE_BUFFER_EXTENSION_NAME);
extensions.external_memory_ahb = false;
}
#endif
#undef FEATURE_EXTENSION
#undef EXTENSION
@@ -1151,21 +1142,6 @@ bool Device::GetSuitability(bool requires_swapchain) {
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MAINTENANCE_5_PROPERTIES_KHR;
SetNext(next, properties.maintenance5);
}
if (extensions.external_memory_host) {
properties.external_memory_host.sType =
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_MEMORY_HOST_PROPERTIES_EXT;
SetNext(next, properties.external_memory_host);
}
if (extensions.maintenance3 || instance_version >= VK_API_VERSION_1_1) {
properties.maintenance3.sType =
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MAINTENANCE_3_PROPERTIES;
SetNext(next, properties.maintenance3);
}
if (extensions.maintenance4 || features.maintenance4.maintenance4) {
properties.maintenance4.sType =
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MAINTENANCE_4_PROPERTIES;
SetNext(next, properties.maintenance4);
}
// Perform the property fetch.
physical.GetProperties2(properties2);
@@ -1540,27 +1516,12 @@ void Device::CollectPhysicalMemoryInfo() {
device_access_memory = 0;
u64 device_initial_usage = 0;
u64 local_memory = 0;
const auto heap_has_usable_type = [&mem_properties](size_t heap) {
for (u32 index = 0; index < mem_properties.memoryTypeCount; ++index) {
if (mem_properties.memoryTypes[index].heapIndex != heap) {
continue;
}
if ((mem_properties.memoryTypes[index].propertyFlags &
VK_MEMORY_PROPERTY_PROTECTED_BIT) == 0) {
return true;
}
}
return false;
};
for (size_t element = 0; element < num_properties; ++element) {
const bool is_heap_local =
(mem_properties.memoryHeaps[element].flags & VK_MEMORY_HEAP_DEVICE_LOCAL_BIT) != 0;
if (!is_integrated && !is_heap_local) {
continue;
}
if (!heap_has_usable_type(element)) {
continue;
}
valid_heap_memory.push_back(element);
if (is_heap_local) {
local_memory += mem_properties.memoryHeaps[element].size;
@@ -1572,13 +1533,6 @@ void Device::CollectPhysicalMemoryInfo() {
}
device_access_memory += mem_properties.memoryHeaps[element].size;
}
const u64 committed_backing = Common::GetCommittedBackingSize();
if (committed_backing != 0) {
LOG_INFO(Render_Vulkan, "Discounting {} MiB of guest memory committed by the host",
committed_backing >> 20);
local_memory -= std::min(local_memory, committed_backing);
device_access_memory -= std::min(device_access_memory, committed_backing);
}
if (is_integrated) {
const s64 available_memory = static_cast<s64>(device_access_memory - device_initial_usage);
const u64 memory_size = Settings::values.vram_usage_mode.GetValue() == Settings::VramUsageMode::Aggressive ? 6_GiB : 4_GiB;
@@ -83,7 +83,6 @@ VK_DEFINE_HANDLE(VmaAllocator)
EXTENSION(EXT, CONDITIONAL_RENDERING, conditional_rendering) \
EXTENSION(EXT, CONSERVATIVE_RASTERIZATION, conservative_rasterization) \
EXTENSION(EXT, DEPTH_RANGE_UNRESTRICTED, depth_range_unrestricted) \
EXTENSION(EXT, EXTERNAL_MEMORY_HOST, external_memory_host) \
EXTENSION(EXT, MEMORY_BUDGET, memory_budget) \
EXTENSION(EXT, ROBUSTNESS_2, robustness_2) \
EXTENSION(EXT, SAMPLER_FILTER_MINMAX, sampler_filter_minmax) \
@@ -113,14 +112,6 @@ VK_DEFINE_HANDLE(VmaAllocator)
EXTENSION(IMG, FILTER_CUBIC, filter_cubic_img) \
EXTENSION(QCOM, FILTER_CUBIC_WEIGHTS, filter_cubic_weights)
#ifdef __ANDROID__
#define FOR_EACH_VK_PLATFORM_EXTENSION(EXTENSION) \
EXTENSION(EXT, QUEUE_FAMILY_FOREIGN, queue_family_foreign) \
EXTENSION(ANDROID, EXTERNAL_MEMORY_ANDROID_HARDWARE_BUFFER, external_memory_ahb)
#else
#define FOR_EACH_VK_PLATFORM_EXTENSION(EXTENSION)
#endif
// Define extensions which must be supported.
#define FOR_EACH_VK_MANDATORY_EXTENSION(EXTENSION_NAME) \
EXTENSION_NAME(VK_EXT_VERTEX_ATTRIBUTE_DIVISOR_EXTENSION_NAME) \
@@ -847,30 +838,6 @@ FN_MAX_LIMIT_LIST
return extensions.conditional_rendering;
}
bool IsExtExternalMemoryHostSupported() const {
return extensions.external_memory_host;
}
bool IsExtExternalMemoryAhbSupported() const {
#ifdef __ANDROID__
return extensions.external_memory_ahb && extensions.queue_family_foreign;
#else
return false;
#endif
}
u64 GetMinImportedHostPointerAlignment() const {
return properties.external_memory_host.minImportedHostPointerAlignment;
}
u64 GetMaxBufferSize() const {
return properties.maintenance4.maxBufferSize;
}
u64 GetMaxMemoryAllocationSize() const {
return properties.maintenance3.maxMemoryAllocationSize;
}
bool IsExtAstcDecodeModeSupported() const {
return extensions.astc_decode_mode;
}
@@ -1143,7 +1110,6 @@ private:
FOR_EACH_VK_FEATURE_1_4(FEATURE);
FOR_EACH_VK_FEATURE_EXT(FEATURE);
FOR_EACH_VK_EXTENSION(EXTENSION);
FOR_EACH_VK_PLATFORM_EXTENSION(EXTENSION);
#undef EXTENSION
#undef FEATURE
@@ -1175,10 +1141,7 @@ private:
VkPhysicalDeviceDescriptorBufferPropertiesEXT descriptor_buffer{};
VkPhysicalDeviceSubgroupSizeControlProperties subgroup_size_control{};
VkPhysicalDeviceTransformFeedbackPropertiesEXT transform_feedback{};
VkPhysicalDeviceMaintenance3Properties maintenance3{};
VkPhysicalDeviceMaintenance4Properties maintenance4{};
VkPhysicalDeviceMaintenance5PropertiesKHR maintenance5{};
VkPhysicalDeviceExternalMemoryHostPropertiesEXT external_memory_host{};
VkPhysicalDeviceProperties properties{};
};
@@ -25,34 +25,9 @@
#include "video_core/gpu_logging/gpu_logging.h"
#include "common/settings.h"
#ifdef __ANDROID__
#include <android/hardware_buffer.h>
#endif
namespace Vulkan {
namespace {
[[nodiscard]] std::optional<u32> FindImportMemoryType(
const VkPhysicalDeviceMemoryProperties &props, u32 type_mask) {
const auto find = [&](VkMemoryPropertyFlags wanted) -> std::optional<u32> {
for (u32 i = 0; i < props.memoryTypeCount; ++i) {
if (((type_mask >> i) & 1u) != 0 &&
(props.memoryTypes[i].propertyFlags & wanted) == wanted) {
return i;
}
}
return std::nullopt;
};
auto type_index = find(VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT |
VK_MEMORY_PROPERTY_HOST_COHERENT_BIT |
VK_MEMORY_PROPERTY_HOST_CACHED_BIT);
if (!type_index) {
type_index = find(VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT |
VK_MEMORY_PROPERTY_HOST_COHERENT_BIT);
}
return type_index;
}
// Helpers translating MemoryUsage to flags/usage
[[maybe_unused]] VkMemoryPropertyFlags MemoryUsagePropertyFlags(MemoryUsage usage) {
@@ -225,263 +200,6 @@ namespace Vulkan {
size = 0;
}
HostMemoryImport::HostMemoryImport(const Device &device_, void *base, size_t size,
std::span<AHardwareBuffer *const> hardware_buffers,
size_t hardware_buffer_window, size_t hardware_buffer_base)
: device{device_} {
if (ImportHardwareBuffers(hardware_buffers, hardware_buffer_window, hardware_buffer_base,
size)) {
return;
}
if (device.IsTiler()) {
return;
}
if (!hardware_buffers.empty()) {
return;
}
if (ImportHostPointer(base, size)) {
return;
}
LOG_INFO(Render_Vulkan, "Unified memory disabled, no host memory import path");
}
bool HostMemoryImport::ImportHostPointer(void *base, size_t size) {
if (!device.IsExtExternalMemoryHostSupported()) {
return false;
}
const u64 alignment = device.GetMinImportedHostPointerAlignment();
if (alignment == 0 || !Common::IsAligned(reinterpret_cast<uintptr_t>(base), alignment) ||
!Common::IsAligned(size, alignment)) {
return false;
}
using namespace Common::Literals;
constexpr VkDeviceSize DesktopWindowSize = 4_GiB;
VkDeviceSize candidate_window = DesktopWindowSize;
const u64 max_buffer_size = device.GetMaxBufferSize();
if (max_buffer_size != 0 && max_buffer_size < candidate_window) {
candidate_window = max_buffer_size;
}
const u64 max_allocation_size = device.GetMaxMemoryAllocationSize();
if (max_allocation_size != 0 && max_allocation_size < candidate_window) {
candidate_window = max_allocation_size;
}
candidate_window = Common::AlignDown(candidate_window, alignment);
if (candidate_window == 0) {
return false;
}
window_size = candidate_window;
const auto &logical = device.GetLogical();
const auto memory_props = device.GetPhysical().GetMemoryProperties().memoryProperties;
for (size_t offset = 0; offset < size; offset += window_size) {
u8 *const window_base = static_cast<u8 *>(base) + offset;
const VkDeviceSize window_len =
(std::min)(static_cast<VkDeviceSize>(size - offset), window_size);
VkMemoryHostPointerPropertiesEXT host_props{
.sType = VK_STRUCTURE_TYPE_MEMORY_HOST_POINTER_PROPERTIES_EXT,
.pNext = nullptr,
.memoryTypeBits = 0,
};
if (logical.GetMemoryHostPointerPropertiesEXT(
VK_EXTERNAL_MEMORY_HANDLE_TYPE_HOST_ALLOCATION_BIT_EXT, window_base,
&host_props) != VK_SUCCESS ||
host_props.memoryTypeBits == 0) {
break;
}
const VkExternalMemoryBufferCreateInfo external_info{
.sType = VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_BUFFER_CREATE_INFO,
.pNext = nullptr,
.handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_HOST_ALLOCATION_BIT_EXT,
};
const VkBufferCreateInfo buffer_ci{
.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
.pNext = &external_info,
.flags = 0,
.size = window_len,
.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT,
.sharingMode = VK_SHARING_MODE_EXCLUSIVE,
.queueFamilyIndexCount = 0,
.pQueueFamilyIndices = nullptr,
};
VkBuffer new_buffer{};
if (logical.CreateBufferRaw(buffer_ci, &new_buffer) != VK_SUCCESS) {
break;
}
const VkMemoryRequirements requirements =
logical.GetBufferMemoryRequirements(new_buffer);
const u32 type_mask = requirements.memoryTypeBits & host_props.memoryTypeBits;
if (type_mask == 0 || requirements.size > window_len) {
logical.DestroyBufferRaw(new_buffer);
break;
}
const auto type_index = FindImportMemoryType(memory_props, type_mask);
if (!type_index) {
logical.DestroyBufferRaw(new_buffer);
break;
}
const u32 heap_index = memory_props.memoryTypes[*type_index].heapIndex;
const VkDeviceSize heap_size = memory_props.memoryHeaps[heap_index].size;
if (imported_size + window_len > heap_size / 2) {
logical.DestroyBufferRaw(new_buffer);
break;
}
const VkImportMemoryHostPointerInfoEXT import_info{
.sType = VK_STRUCTURE_TYPE_IMPORT_MEMORY_HOST_POINTER_INFO_EXT,
.pNext = nullptr,
.handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_HOST_ALLOCATION_BIT_EXT,
.pHostPointer = window_base,
};
const VkMemoryAllocateInfo alloc_info{
.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
.pNext = &import_info,
.allocationSize = window_len,
.memoryTypeIndex = *type_index,
};
vk::DeviceMemory memory = logical.TryAllocateMemory(alloc_info);
if (!memory) {
logical.DestroyBufferRaw(new_buffer);
break;
}
if (logical.BindBufferMemory(new_buffer, *memory, 0) != VK_SUCCESS) {
logical.DestroyBufferRaw(new_buffer);
break;
}
windows.push_back(Window{
.memory = std::move(memory),
.buffer = new_buffer,
});
imported_size += static_cast<size_t>(window_len);
}
if (windows.empty()) {
return false;
}
return true;
}
bool HostMemoryImport::ImportHardwareBuffers(
[[maybe_unused]] std::span<AHardwareBuffer *const> hardware_buffers,
[[maybe_unused]] size_t hardware_buffer_window,
[[maybe_unused]] size_t hardware_buffer_base, [[maybe_unused]] size_t size) {
#ifdef __ANDROID__
if (hardware_buffers.empty() || hardware_buffer_window == 0 ||
!device.IsExtExternalMemoryAhbSupported()) {
return false;
}
const u64 max_allocation_size = device.GetMaxMemoryAllocationSize();
if (max_allocation_size != 0 && hardware_buffer_window > max_allocation_size) {
return false;
}
if (hardware_buffer_base >= size) {
return false;
}
const auto &logical = device.GetLogical();
const auto memory_props = device.GetPhysical().GetMemoryProperties().memoryProperties;
window_size = hardware_buffer_window;
base_offset = hardware_buffer_base;
for (size_t i = 0; i < hardware_buffers.size(); ++i) {
const size_t offset = hardware_buffer_base + i * hardware_buffer_window;
if (offset >= size) {
break;
}
const VkDeviceSize window_len = (std::min)(
static_cast<VkDeviceSize>(size - offset),
static_cast<VkDeviceSize>(hardware_buffer_window));
VkAndroidHardwareBufferPropertiesANDROID ahb_props{
.sType = VK_STRUCTURE_TYPE_ANDROID_HARDWARE_BUFFER_PROPERTIES_ANDROID,
.pNext = nullptr,
.allocationSize = 0,
.memoryTypeBits = 0,
};
if (logical.GetAndroidHardwareBufferPropertiesANDROID(hardware_buffers[i],
&ahb_props) != VK_SUCCESS ||
ahb_props.memoryTypeBits == 0 || ahb_props.allocationSize < window_len) {
break;
}
const VkExternalMemoryBufferCreateInfo external_info{
.sType = VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_BUFFER_CREATE_INFO,
.pNext = nullptr,
.handleTypes =
VK_EXTERNAL_MEMORY_HANDLE_TYPE_ANDROID_HARDWARE_BUFFER_BIT_ANDROID,
};
const VkBufferCreateInfo buffer_ci{
.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
.pNext = &external_info,
.flags = 0,
.size = window_len,
.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT,
.sharingMode = VK_SHARING_MODE_EXCLUSIVE,
.queueFamilyIndexCount = 0,
.pQueueFamilyIndices = nullptr,
};
VkBuffer new_buffer{};
if (logical.CreateBufferRaw(buffer_ci, &new_buffer) != VK_SUCCESS) {
break;
}
const VkMemoryRequirements requirements =
logical.GetBufferMemoryRequirements(new_buffer);
const u32 type_mask = requirements.memoryTypeBits & ahb_props.memoryTypeBits;
if (type_mask == 0 || requirements.size > ahb_props.allocationSize) {
logical.DestroyBufferRaw(new_buffer);
break;
}
const auto type_index = FindImportMemoryType(memory_props, type_mask);
if (!type_index) {
logical.DestroyBufferRaw(new_buffer);
break;
}
const VkImportAndroidHardwareBufferInfoANDROID import_info{
.sType = VK_STRUCTURE_TYPE_IMPORT_ANDROID_HARDWARE_BUFFER_INFO_ANDROID,
.pNext = nullptr,
.buffer = hardware_buffers[i],
};
const VkMemoryDedicatedAllocateInfo dedicated_info{
.sType = VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO,
.pNext = &import_info,
.image = VK_NULL_HANDLE,
.buffer = new_buffer,
};
const VkMemoryAllocateInfo alloc_info{
.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
.pNext = &dedicated_info,
.allocationSize = ahb_props.allocationSize,
.memoryTypeIndex = *type_index,
};
vk::DeviceMemory memory = logical.TryAllocateMemory(alloc_info);
if (!memory) {
logical.DestroyBufferRaw(new_buffer);
break;
}
if (logical.BindBufferMemory(new_buffer, *memory, 0) != VK_SUCCESS) {
logical.DestroyBufferRaw(new_buffer);
break;
}
windows.push_back(Window{
.memory = std::move(memory),
.buffer = new_buffer,
});
imported_size += static_cast<size_t>(window_len);
}
if (windows.empty()) {
window_size = 0;
base_offset = 0;
return false;
}
foreign_ownership = true;
return true;
#else
return false;
#endif
}
HostMemoryImport::~HostMemoryImport() {
for (Window &window : windows) {
if (window.buffer != VK_NULL_HANDLE) {
device.GetLogical().DestroyBufferRaw(window.buffer);
}
}
}
MemoryAllocator::MemoryAllocator(const Device &device_)
: device{device_}, allocator{device.GetAllocator()},
properties{device_.GetPhysical().GetMemoryProperties().memoryProperties},

Some files were not shown because too many files have changed in this diff Show More