mirror of
https://git.eden-emu.dev/eden-emu/eden.git
synced 2026-09-09 13:36:35 +00:00
Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| dc04115e87 |
@@ -53,6 +53,7 @@ if (YUZU_STATIC_ROOM)
|
|||||||
# disable e v e r y t h i n g
|
# disable e v e r y t h i n g
|
||||||
set(ENABLE_QT OFF)
|
set(ENABLE_QT OFF)
|
||||||
set(YUZU_CMD OFF)
|
set(YUZU_CMD OFF)
|
||||||
|
set(ENABLE_CUBEB OFF)
|
||||||
set(ENABLE_UPDATE_CHECKER OFF)
|
set(ENABLE_UPDATE_CHECKER OFF)
|
||||||
set(USE_DISCORD_PRESENCE OFF)
|
set(USE_DISCORD_PRESENCE OFF)
|
||||||
set(BUILD_TESTING OFF)
|
set(BUILD_TESTING OFF)
|
||||||
@@ -214,6 +215,8 @@ if(MSVC)
|
|||||||
endif()
|
endif()
|
||||||
|
|
||||||
# TODO(crueter): Cleanup, each dep that has a bundled option should allow to choose between bundled, external, system
|
# TODO(crueter): Cleanup, each dep that has a bundled option should allow to choose between bundled, external, system
|
||||||
|
option(ENABLE_CUBEB "Enables the cubeb audio backend" ON)
|
||||||
|
|
||||||
set(EXT_DEFAULT OFF)
|
set(EXT_DEFAULT OFF)
|
||||||
if (MSVC OR ANDROID)
|
if (MSVC OR ANDROID)
|
||||||
set(EXT_DEFAULT ON)
|
set(EXT_DEFAULT ON)
|
||||||
@@ -572,6 +575,10 @@ if (NOT YUZU_STATIC_ROOM)
|
|||||||
find_package(DiscordRPC)
|
find_package(DiscordRPC)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
if (ENABLE_CUBEB)
|
||||||
|
find_package(cubeb)
|
||||||
|
endif()
|
||||||
|
|
||||||
if (YUZU_TESTS OR DYNARMIC_TESTS)
|
if (YUZU_TESTS OR DYNARMIC_TESTS)
|
||||||
find_package(Catch2)
|
find_package(Catch2)
|
||||||
endif()
|
endif()
|
||||||
|
|||||||
@@ -46,6 +46,18 @@
|
|||||||
"repo": "arun11299/cpp-jwt",
|
"repo": "arun11299/cpp-jwt",
|
||||||
"version": "7f24eb4c32"
|
"version": "7f24eb4c32"
|
||||||
},
|
},
|
||||||
|
"cubeb": {
|
||||||
|
"find_args": "CONFIG",
|
||||||
|
"hash": "8a4bcb2f83ba590f52c66626e895304a73eb61928dbc57777e1822e55378e3568366f17f9da4b80036cc2ef4ea9723c32abf6e7d9bbe00fb03654f0991596ab0",
|
||||||
|
"options": [
|
||||||
|
"USE_SANITIZERS OFF",
|
||||||
|
"BUILD_TESTS OFF",
|
||||||
|
"BUILD_TOOLS OFF",
|
||||||
|
"BUNDLE_SPEEX ON"
|
||||||
|
],
|
||||||
|
"repo": "mozilla/cubeb",
|
||||||
|
"version": "fa02160712"
|
||||||
|
},
|
||||||
"discord-rpc": {
|
"discord-rpc": {
|
||||||
"find_args": "MODULE",
|
"find_args": "MODULE",
|
||||||
"hash": "8d680b3a16d6f6bf292ad823cf8635595ff986f2a49f758e3009f505b540a9d75194a8cf44cddea75736a8c3e3b5160166e716a0939ce3f18cc463cf648753fe",
|
"hash": "8d680b3a16d6f6bf292ad823cf8635595ff986f2a49f758e3009f505b540a9d75194a8cf44cddea75736a8c3e3b5160166e716a0939ce3f18cc463cf648753fe",
|
||||||
|
|||||||
@@ -100,6 +100,8 @@ For reasons unberknownst to any human being, `glslangValidator` will crash upon
|
|||||||
|
|
||||||
For this reason this patch is NOT applied to default on all platforms (for obvious reasons) - instead this is a HaikuOS specific patch, apply with `git apply <absolute path to patch>` after cloning SPIRV-Tools then `make -C build` and add the resulting binary (in `build/StandAlone/glslang`) into PATH.
|
For this reason this patch is NOT applied to default on all platforms (for obvious reasons) - instead this is a HaikuOS specific patch, apply with `git apply <absolute path to patch>` after cloning SPIRV-Tools then `make -C build` and add the resulting binary (in `build/StandAlone/glslang`) into PATH.
|
||||||
|
|
||||||
|
`cubeb_devel` will also not work, either disable cubeb or uninstall it.
|
||||||
|
|
||||||
Still will not run flawlessly until `mesa-24` is available. Modify CMakeCache.txt with the `.so` of libGL and libGLESv2 by doing the incredibly difficult task of copy pasting them (`cp /boot/system/lib/libGL.so .`)
|
Still will not run flawlessly until `mesa-24` is available. Modify CMakeCache.txt with the `.so` of libGL and libGLESv2 by doing the incredibly difficult task of copy pasting them (`cp /boot/system/lib/libGL.so .`)
|
||||||
|
|
||||||
If you have `quazip1_qt6_devel`, uninstall it. It may call `Core5Compat` on CMake which is wrongly packaged.
|
If you have `quazip1_qt6_devel`, uninstall it. It may call `Core5Compat` on CMake which is wrongly packaged.
|
||||||
|
|||||||
+3
-2
@@ -73,6 +73,7 @@ Certain other dependencies will be fetched by CPM regardless. System packages *c
|
|||||||
|
|
||||||
* [SimpleIni](https://github.com/brofield/simpleini)
|
* [SimpleIni](https://github.com/brofield/simpleini)
|
||||||
* [DiscordRPC](https://github.com/eden-emulator/discord-rpc)
|
* [DiscordRPC](https://github.com/eden-emulator/discord-rpc)
|
||||||
|
* [cubeb](https://github.com/mozilla/cubeb)
|
||||||
* [libusb](https://github.com/libusb/libusb)
|
* [libusb](https://github.com/libusb/libusb)
|
||||||
* [VulkanMemoryAllocator](https://github.com/GPUOpen-LibrariesAndSDKs/VulkanMemoryAllocator)
|
* [VulkanMemoryAllocator](https://github.com/GPUOpen-LibrariesAndSDKs/VulkanMemoryAllocator)
|
||||||
* [sirit](https://github.com/eden-emulator/sirit)
|
* [sirit](https://github.com/eden-emulator/sirit)
|
||||||
@@ -121,7 +122,7 @@ sudo emerge -a \
|
|||||||
dev-util/spirv-tools dev-util/spirv-headers dev-util/vulkan-headers \
|
dev-util/spirv-tools dev-util/spirv-headers dev-util/vulkan-headers \
|
||||||
dev-util/vulkan-utility-libraries dev-util/glslang \
|
dev-util/vulkan-utility-libraries dev-util/glslang \
|
||||||
media-gfx/renderdoc media-libs/libva media-libs/opus media-video/ffmpeg \
|
media-gfx/renderdoc media-libs/libva media-libs/opus media-video/ffmpeg \
|
||||||
media-libs/VulkanMemoryAllocator media-libs/libsdl3 \
|
media-libs/VulkanMemoryAllocator media-libs/libsdl3 media-libs/cubeb \
|
||||||
net-libs/enet \
|
net-libs/enet \
|
||||||
sys-libs/zlib \
|
sys-libs/zlib \
|
||||||
dev-cpp/nlohmann_json dev-cpp/simpleini dev-cpp/cpp-httplib dev-cpp/cpp-jwt \
|
dev-cpp/nlohmann_json dev-cpp/simpleini dev-cpp/cpp-httplib dev-cpp/cpp-jwt \
|
||||||
@@ -355,7 +356,7 @@ pacman -Syuu --needed --noconfirm $packages
|
|||||||
<summary>HaikuOS</summary>
|
<summary>HaikuOS</summary>
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
pkgman install git cmake patch libfmt_devel nlohmann_json lz4_devel opus_devel boost1.90_devel vulkan_devel qt6_base_devel qt6_declarative_devel libsdl3_devel ffmpeg7_devel libx11_devel enet_devel catch2_devel quazip1_qt5_devel qt6_5compat_devel glslang qt6_devel qt6_charts_devel simpleini quazip_qt6_devel
|
pkgman install git cmake patch libfmt_devel nlohmann_json lz4_devel opus_devel boost1.90_devel vulkan_devel qt6_base_devel qt6_declarative_devel libsdl3_devel ffmpeg7_devel libx11_devel enet_devel catch2_devel quazip1_qt5_devel qt6_5compat_devel glslang qt6_devel qt6_charts_devel cubeb_devel simpleini quazip_qt6_devel
|
||||||
```
|
```
|
||||||
|
|
||||||
[Caveats](./Caveats.md#haikuos).
|
[Caveats](./Caveats.md#haikuos).
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ Modern game consoles require heavy power to be emulated appropriately. This is w
|
|||||||
|
|
||||||
For example, take a disk write, instead of emulating a proper SD card we instead use the C++ standard library for I/O. Additionally we use the abstractions provided by the `fs` service to "lie" to programs about certain SD card properties. Notably this includes making up sizes for the fake SD card, giving "realistic" values or expected outputs for a given card, and so on. And instead of writing to an actual SD card, the emulator simply writes to a file.
|
For example, take a disk write, instead of emulating a proper SD card we instead use the C++ standard library for I/O. Additionally we use the abstractions provided by the `fs` service to "lie" to programs about certain SD card properties. Notably this includes making up sizes for the fake SD card, giving "realistic" values or expected outputs for a given card, and so on. And instead of writing to an actual SD card, the emulator simply writes to a file.
|
||||||
|
|
||||||
This also means grand part of the emulator consists of just re-implementing firmware but using HLE primitives; for example audio doesn't go to an emulated audio device, but rather gets processed on the fly by a dedicated service and then passed to SDL3 or null backend.
|
This also means grand part of the emulator consists of just re-implementing firmware but using HLE primitives; for example audio doesn't go to an emulated audio device, but rather gets processed on the fly by a dedicated service and then passed to SDL3/cubeb/etc.
|
||||||
|
|
||||||
As such, many of the systems implemented are not 100% accurate to the original software, but they're "good enough" to pass as being so. While we do strive to maintain high compatibility (especially with homebrew), there are realistic limitations to these approaches.
|
As such, many of the systems implemented are not 100% accurate to the original software, but they're "good enough" to pass as being so. While we do strive to maintain high compatibility (especially with homebrew), there are realistic limitations to these approaches.
|
||||||
|
|
||||||
|
|||||||
@@ -35,6 +35,8 @@ These options control dependencies.
|
|||||||
|
|
||||||
- `ENABLE_WEB_SERVICE` (ON) Enable multiplayer service
|
- `ENABLE_WEB_SERVICE` (ON) Enable multiplayer service
|
||||||
- `ENABLE_WIFI_SCAN` (OFF) Enable WiFi scanning (requires iw on Linux) - experimental
|
- `ENABLE_WIFI_SCAN` (OFF) Enable WiFi scanning (requires iw on Linux) - experimental
|
||||||
|
- `ENABLE_CUBEB` (ON) Enables the cubeb audio backend
|
||||||
|
- This option is subject for removal.
|
||||||
- `YUZU_TESTS` (ON) Compile tests - requires Catch2
|
- `YUZU_TESTS` (ON) Compile tests - requires Catch2
|
||||||
- `ENABLE_LTO` (OFF) Enable link-time optimization
|
- `ENABLE_LTO` (OFF) Enable link-time optimization
|
||||||
- Not recommended on Windows
|
- Not recommended on Windows
|
||||||
|
|||||||
Vendored
+30
@@ -106,6 +106,36 @@ endif()
|
|||||||
# SimpleIni
|
# SimpleIni
|
||||||
AddJsonPackage(simpleini)
|
AddJsonPackage(simpleini)
|
||||||
|
|
||||||
|
# Most linux distros don't package cubeb, so enable regardless of cpm settings
|
||||||
|
if(ENABLE_CUBEB)
|
||||||
|
AddJsonPackage(cubeb)
|
||||||
|
|
||||||
|
if (cubeb_ADDED)
|
||||||
|
if (NOT MSVC)
|
||||||
|
if (TARGET speex)
|
||||||
|
target_compile_options(speex PRIVATE $<$<COMPILE_LANGUAGE:C,CXX>:-Wno-sign-compare>)
|
||||||
|
endif()
|
||||||
|
set_target_properties(cubeb PROPERTIES COMPILE_OPTIONS "")
|
||||||
|
target_compile_options(cubeb INTERFACE
|
||||||
|
$<$<COMPILE_LANGUAGE:C,CXX>:-Wno-implicit-const-int-float-conversion>
|
||||||
|
$<$<COMPILE_LANGUAGE:C,CXX>:-Wno-shadow>
|
||||||
|
$<$<COMPILE_LANGUAGE:C,CXX>:-Wno-missing-declarations>
|
||||||
|
$<$<COMPILE_LANGUAGE:C,CXX>:-Wno-return-type>
|
||||||
|
$<$<COMPILE_LANGUAGE:C,CXX>:-Wno-uninitialized>
|
||||||
|
)
|
||||||
|
else()
|
||||||
|
target_compile_options(cubeb PRIVATE
|
||||||
|
/wd4456
|
||||||
|
/wd4458
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if (NOT TARGET cubeb::cubeb)
|
||||||
|
add_library(cubeb::cubeb ALIAS cubeb)
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
if (NOT YUZU_USE_BUNDLED_SDL3)
|
if (NOT YUZU_USE_BUNDLED_SDL3)
|
||||||
if (NOT WIN32)
|
if (NOT WIN32)
|
||||||
# Yuzu itself needs: Atomic Audio Events Joystick Haptic Sensor Threads Timers
|
# Yuzu itself needs: Atomic Audio Events Joystick Haptic Sensor Threads Timers
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ pkgs.mkShellNoCC {
|
|||||||
enet libopus vulkan-headers vulkan-utility-libraries
|
enet libopus vulkan-headers vulkan-utility-libraries
|
||||||
spirv-tools spirv-headers vulkan-loader unzip
|
spirv-tools spirv-headers vulkan-loader unzip
|
||||||
glslang python3 httplib cpp-jwt ffmpeg-headless
|
glslang python3 httplib cpp-jwt ffmpeg-headless
|
||||||
libusb1
|
libusb1 cubeb
|
||||||
# eden
|
# eden
|
||||||
qt6.qtbase qt6.qtmultimedia qt6.qtwayland qt6.qttools
|
qt6.qtbase qt6.qtmultimedia qt6.qtwayland qt6.qttools
|
||||||
qt6.qtwebengine qt6.qt5compat
|
qt6.qtwebengine qt6.qt5compat
|
||||||
|
|||||||
@@ -114,7 +114,9 @@ class QuickSettings(val emulationFragment: EmulationFragment) {
|
|||||||
name: Int,
|
name: Int,
|
||||||
|
|
||||||
container: ViewGroup,
|
container: ViewGroup,
|
||||||
setting: BooleanSetting
|
setting: BooleanSetting,
|
||||||
|
isEnabled: Boolean = true,
|
||||||
|
onValueChanged: ((Boolean) -> Unit)? = null
|
||||||
) {
|
) {
|
||||||
val inflater = LayoutInflater.from(emulationFragment.requireContext())
|
val inflater = LayoutInflater.from(emulationFragment.requireContext())
|
||||||
val itemView = inflater.inflate(R.layout.item_quick_settings_menu, container, false)
|
val itemView = inflater.inflate(R.layout.item_quick_settings_menu, container, false)
|
||||||
@@ -125,15 +127,21 @@ class QuickSettings(val emulationFragment: EmulationFragment) {
|
|||||||
|
|
||||||
titleView.text = YuzuApplication.appContext.getString(name)
|
titleView.text = YuzuApplication.appContext.getString(name)
|
||||||
switchContainer.visibility = View.VISIBLE
|
switchContainer.visibility = View.VISIBLE
|
||||||
|
switchContainer.isEnabled = isEnabled
|
||||||
switchView.isChecked = setting.getBoolean()
|
switchView.isChecked = setting.getBoolean()
|
||||||
|
switchView.isEnabled = isEnabled
|
||||||
|
itemView.alpha = if (isEnabled) 1.0f else 0.5f
|
||||||
|
|
||||||
switchView.setOnCheckedChangeListener { _, isChecked ->
|
switchView.setOnCheckedChangeListener { _, isChecked ->
|
||||||
setting.setBoolean(isChecked)
|
setting.setBoolean(isChecked)
|
||||||
saveSettings()
|
saveSettings()
|
||||||
|
onValueChanged?.invoke(isChecked)
|
||||||
}
|
}
|
||||||
|
|
||||||
switchContainer.setOnClickListener {
|
switchContainer.setOnClickListener {
|
||||||
switchView.toggle()
|
if (switchView.isEnabled) {
|
||||||
|
switchView.toggle()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
container.addView(itemView)
|
container.addView(itemView)
|
||||||
}
|
}
|
||||||
@@ -177,7 +185,9 @@ class QuickSettings(val emulationFragment: EmulationFragment) {
|
|||||||
setting: AbstractSetting,
|
setting: AbstractSetting,
|
||||||
minValue: Int = 0,
|
minValue: Int = 0,
|
||||||
maxValue: Int = 100,
|
maxValue: Int = 100,
|
||||||
units: String = ""
|
units: String = "",
|
||||||
|
isEnabled: Boolean = true,
|
||||||
|
onValueChanged: ((Int) -> Unit)? = null
|
||||||
) {
|
) {
|
||||||
val inflater = LayoutInflater.from(emulationFragment.requireContext())
|
val inflater = LayoutInflater.from(emulationFragment.requireContext())
|
||||||
val itemView = inflater.inflate(R.layout.item_quick_settings_menu, container, false)
|
val itemView = inflater.inflate(R.layout.item_quick_settings_menu, container, false)
|
||||||
@@ -190,10 +200,13 @@ class QuickSettings(val emulationFragment: EmulationFragment) {
|
|||||||
|
|
||||||
titleView.text = YuzuApplication.appContext.getString(name)
|
titleView.text = YuzuApplication.appContext.getString(name)
|
||||||
sliderContainer.visibility = View.VISIBLE
|
sliderContainer.visibility = View.VISIBLE
|
||||||
|
sliderContainer.isEnabled = isEnabled
|
||||||
|
|
||||||
slider.valueFrom = minValue.toFloat()
|
slider.valueFrom = minValue.toFloat()
|
||||||
slider.valueTo = maxValue.toFloat()
|
slider.valueTo = maxValue.toFloat()
|
||||||
slider.stepSize = 1f
|
slider.stepSize = 1f
|
||||||
|
slider.isEnabled = isEnabled
|
||||||
|
itemView.alpha = if (isEnabled) 1.0f else 0.5f
|
||||||
val currentValue = when (setting) {
|
val currentValue = when (setting) {
|
||||||
is AbstractShortSetting -> setting.getShort(needsGlobal = false).toInt()
|
is AbstractShortSetting -> setting.getShort(needsGlobal = false).toInt()
|
||||||
is AbstractIntSetting -> setting.getInt(needsGlobal = false)
|
is AbstractIntSetting -> setting.getInt(needsGlobal = false)
|
||||||
@@ -204,8 +217,8 @@ class QuickSettings(val emulationFragment: EmulationFragment) {
|
|||||||
val displayValue = "${slider.value.toInt()}$units"
|
val displayValue = "${slider.value.toInt()}$units"
|
||||||
valueDisplay.text = displayValue
|
valueDisplay.text = displayValue
|
||||||
|
|
||||||
slider.addOnChangeListener { _, value, chanhed ->
|
slider.addOnChangeListener { _, value, changed ->
|
||||||
if (chanhed) {
|
if (changed) {
|
||||||
val intValue = value.toInt()
|
val intValue = value.toInt()
|
||||||
when (setting) {
|
when (setting) {
|
||||||
is AbstractShortSetting -> setting.setShort(intValue.toShort())
|
is AbstractShortSetting -> setting.setShort(intValue.toShort())
|
||||||
@@ -213,6 +226,7 @@ class QuickSettings(val emulationFragment: EmulationFragment) {
|
|||||||
}
|
}
|
||||||
saveSettings()
|
saveSettings()
|
||||||
valueDisplay.text = "$intValue$units"
|
valueDisplay.text = "$intValue$units"
|
||||||
|
onValueChanged?.invoke(intValue)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -92,6 +92,7 @@ import org.yuzu.yuzu_emu.utils.GameIconUtils
|
|||||||
import org.yuzu.yuzu_emu.utils.GpuDriverHelper
|
import org.yuzu.yuzu_emu.utils.GpuDriverHelper
|
||||||
import org.yuzu.yuzu_emu.utils.InputHandler
|
import org.yuzu.yuzu_emu.utils.InputHandler
|
||||||
import org.yuzu.yuzu_emu.utils.Log
|
import org.yuzu.yuzu_emu.utils.Log
|
||||||
|
import org.yuzu.yuzu_emu.utils.LosslessScalingHelper
|
||||||
import org.yuzu.yuzu_emu.utils.NativeConfig
|
import org.yuzu.yuzu_emu.utils.NativeConfig
|
||||||
import org.yuzu.yuzu_emu.utils.NativeFreedrenoConfig
|
import org.yuzu.yuzu_emu.utils.NativeFreedrenoConfig
|
||||||
import org.yuzu.yuzu_emu.utils.ViewUtils
|
import org.yuzu.yuzu_emu.utils.ViewUtils
|
||||||
@@ -1157,6 +1158,36 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
|
|||||||
|
|
||||||
quickSettings.addDivider(container)
|
quickSettings.addDivider(container)
|
||||||
|
|
||||||
|
val frameGenAvailable = LosslessScalingHelper.isInstalled() &&
|
||||||
|
LosslessScalingHelper.isSupportedByGpu()
|
||||||
|
val frameGenEnabled = BooleanSetting.RENDERER_FRAME_GEN.getBoolean()
|
||||||
|
val usesFixedMultiplier =
|
||||||
|
IntSetting.RENDERER_FRAME_GEN_TARGET_RATE.getInt() == 0
|
||||||
|
|
||||||
|
quickSettings.addBooleanSetting(
|
||||||
|
R.string.frame_gen,
|
||||||
|
container,
|
||||||
|
BooleanSetting.RENDERER_FRAME_GEN,
|
||||||
|
isEnabled = frameGenAvailable
|
||||||
|
) {
|
||||||
|
NativeLibrary.applySettings()
|
||||||
|
addQuickSettings()
|
||||||
|
}
|
||||||
|
|
||||||
|
quickSettings.addSliderSetting(
|
||||||
|
R.string.frame_gen_multiplier,
|
||||||
|
container,
|
||||||
|
IntSetting.RENDERER_FRAME_GEN_MULTIPLIER,
|
||||||
|
minValue = 2,
|
||||||
|
maxValue = 4,
|
||||||
|
units = "x",
|
||||||
|
isEnabled = frameGenAvailable && frameGenEnabled && usesFixedMultiplier
|
||||||
|
) {
|
||||||
|
NativeLibrary.applySettings()
|
||||||
|
}
|
||||||
|
|
||||||
|
quickSettings.addDivider(container)
|
||||||
|
|
||||||
quickSettings.addIntSetting(
|
quickSettings.addIntSetting(
|
||||||
R.string.renderer_accuracy,
|
R.string.renderer_accuracy,
|
||||||
container,
|
container,
|
||||||
|
|||||||
@@ -58,6 +58,13 @@ class LicensesFragment : Fragment() {
|
|||||||
R.string.license_fidelityfx_fsr_copyright,
|
R.string.license_fidelityfx_fsr_copyright,
|
||||||
R.string.license_fidelityfx_fsr_text
|
R.string.license_fidelityfx_fsr_text
|
||||||
),
|
),
|
||||||
|
License(
|
||||||
|
R.string.license_cubeb,
|
||||||
|
R.string.license_cubeb_description,
|
||||||
|
R.string.license_cubeb_link,
|
||||||
|
R.string.license_cubeb_copyright,
|
||||||
|
R.string.license_cubeb_text
|
||||||
|
),
|
||||||
License(
|
License(
|
||||||
R.string.license_dynarmic,
|
R.string.license_dynarmic,
|
||||||
R.string.license_dynarmic_description,
|
R.string.license_dynarmic_description,
|
||||||
|
|||||||
@@ -22,6 +22,7 @@
|
|||||||
#include "input_common/main.h"
|
#include "input_common/main.h"
|
||||||
#include "jni/emu_window/emu_window.h"
|
#include "jni/emu_window/emu_window.h"
|
||||||
#include "jni/native.h"
|
#include "jni/native.h"
|
||||||
|
#include "video_core/renderer_base.h"
|
||||||
|
|
||||||
void EmuWindow_Android::OnSurfaceChanged(ANativeWindow* surface) {
|
void EmuWindow_Android::OnSurfaceChanged(ANativeWindow* surface) {
|
||||||
if (!surface) {
|
if (!surface) {
|
||||||
@@ -125,10 +126,14 @@ float EmuWindow_Android::GetFrameTimeVerifiedHint() const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
float EmuWindow_Android::GetPresentedFrameMultiplier() {
|
float EmuWindow_Android::GetPresentedFrameMultiplier() {
|
||||||
if (!Settings::values.frame_gen.GetValue()) {
|
if (EmulationSession::GetInstance().IsRunning()) {
|
||||||
return 1.0f;
|
const VideoCore::FrameGenConfig config =
|
||||||
|
EmulationSession::GetInstance().System().Renderer().Settings().GetFrameGenConfig();
|
||||||
|
return config.enabled ? static_cast<float>(config.multiplier) : 1.0f;
|
||||||
}
|
}
|
||||||
return static_cast<float>(std::clamp<u32>(Settings::values.frame_gen_multiplier.GetValue(), 2, 4));
|
return Settings::values.frame_gen.GetValue()
|
||||||
|
? static_cast<float>(Settings::FrameGenMultiplier())
|
||||||
|
: 1.0f;
|
||||||
}
|
}
|
||||||
|
|
||||||
float EmuWindow_Android::GetFrameRateHint() const {
|
float EmuWindow_Android::GetFrameRateHint() const {
|
||||||
|
|||||||
@@ -799,6 +799,9 @@
|
|||||||
<string name="theme_mode_light">روشن</string>
|
<string name="theme_mode_light">روشن</string>
|
||||||
<string name="theme_mode_dark">تاریک</string>
|
<string name="theme_mode_dark">تاریک</string>
|
||||||
|
|
||||||
|
<!-- Audio output engines -->
|
||||||
|
<string name="cubeb">cubeb</string>
|
||||||
|
|
||||||
<!-- Anisotropic filtering options -->
|
<!-- Anisotropic filtering options -->
|
||||||
<string name="multiplier_x2">x2</string>
|
<string name="multiplier_x2">x2</string>
|
||||||
<string name="multiplier_x4">x4</string>
|
<string name="multiplier_x4">x4</string>
|
||||||
|
|||||||
@@ -466,11 +466,13 @@
|
|||||||
<string-array name="outputEngineEntries">
|
<string-array name="outputEngineEntries">
|
||||||
<item>@string/auto</item>
|
<item>@string/auto</item>
|
||||||
<item>@string/sdl3</item>
|
<item>@string/sdl3</item>
|
||||||
|
<item>@string/cubeb</item>
|
||||||
<item>@string/string_null</item>
|
<item>@string/string_null</item>
|
||||||
</string-array>
|
</string-array>
|
||||||
<integer-array name="outputEngineValues">
|
<integer-array name="outputEngineValues">
|
||||||
<item>0</item>
|
<item>0</item>
|
||||||
<item>2</item>
|
<item>2</item>
|
||||||
|
<item>1</item>
|
||||||
<item>3</item>
|
<item>3</item>
|
||||||
</integer-array>
|
</integer-array>
|
||||||
|
|
||||||
|
|||||||
@@ -1243,6 +1243,7 @@
|
|||||||
|
|
||||||
<!-- Audio output engines -->
|
<!-- Audio output engines -->
|
||||||
<string name="sdl3" translatable="false">SDL3</string>
|
<string name="sdl3" translatable="false">SDL3</string>
|
||||||
|
<string name="cubeb" translatable="false">cubeb</string>
|
||||||
|
|
||||||
<!-- Anisotropic filtering options -->
|
<!-- Anisotropic filtering options -->
|
||||||
<string name="multiplier_x2" translatable="false">x2</string>
|
<string name="multiplier_x2" translatable="false">x2</string>
|
||||||
@@ -1382,6 +1383,23 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
THE SOFTWARE.
|
THE SOFTWARE.
|
||||||
|
</string>
|
||||||
|
<string name="license_cubeb" translatable="false">cubeb</string>
|
||||||
|
<string name="license_cubeb_description" translatable="false">Cross platform audio library</string>
|
||||||
|
<string name="license_cubeb_link" translatable="false">https://github.com/mozilla/cubeb</string>
|
||||||
|
<string name="license_cubeb_copyright" translatable="false">Copyright © 2011 Mozilla Foundation</string>
|
||||||
|
<string name="license_cubeb_text" translatable="false">
|
||||||
|
Permission to use, copy, modify, and distribute this software for any
|
||||||
|
purpose with or without fee is hereby granted, provided that the above
|
||||||
|
copyright notice and this permission notice appear in all copies.\n\n
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
</string>
|
</string>
|
||||||
<string name="license_dynarmic" translatable="false">Dynarmic</string>
|
<string name="license_dynarmic" translatable="false">Dynarmic</string>
|
||||||
<string name="license_dynarmic_description" translatable="false">An ARM dynamic recompiler</string>
|
<string name="license_dynarmic_description" translatable="false">An ARM dynamic recompiler</string>
|
||||||
|
|||||||
@@ -229,6 +229,15 @@ endif()
|
|||||||
target_include_directories(audio_core PRIVATE ${OPUS_INCLUDE_DIRS})
|
target_include_directories(audio_core PRIVATE ${OPUS_INCLUDE_DIRS})
|
||||||
target_link_libraries(audio_core PUBLIC common core Opus::opus)
|
target_link_libraries(audio_core PUBLIC common core Opus::opus)
|
||||||
|
|
||||||
|
if (ENABLE_CUBEB)
|
||||||
|
target_sources(audio_core PRIVATE
|
||||||
|
sink/cubeb_sink.cpp
|
||||||
|
sink/cubeb_sink.h)
|
||||||
|
|
||||||
|
target_link_libraries(audio_core PRIVATE cubeb::cubeb)
|
||||||
|
target_compile_definitions(audio_core PRIVATE HAVE_CUBEB=1)
|
||||||
|
endif()
|
||||||
|
|
||||||
target_sources(audio_core PRIVATE
|
target_sources(audio_core PRIVATE
|
||||||
sink/sdl3_sink.cpp
|
sink/sdl3_sink.cpp
|
||||||
sink/sdl3_sink.h)
|
sink/sdl3_sink.h)
|
||||||
@@ -236,8 +245,4 @@ target_sources(audio_core PRIVATE
|
|||||||
target_link_libraries(audio_core PRIVATE SDL3::SDL3)
|
target_link_libraries(audio_core PRIVATE SDL3::SDL3)
|
||||||
target_compile_definitions(audio_core PRIVATE HAVE_SDL3)
|
target_compile_definitions(audio_core PRIVATE HAVE_SDL3)
|
||||||
|
|
||||||
if (ANDROID)
|
|
||||||
target_link_libraries(audio_core PRIVATE OpenSLES)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
create_target_directory_groups(audio_core)
|
create_target_directory_groups(audio_core)
|
||||||
|
|||||||
@@ -0,0 +1,450 @@
|
|||||||
|
// 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
|
||||||
|
|
||||||
|
#include <span>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "audio_core/common/common.h"
|
||||||
|
#include "audio_core/sink/cubeb_sink.h"
|
||||||
|
#include "audio_core/sink/sink_stream.h"
|
||||||
|
#include "common/logging.h"
|
||||||
|
#include "common/scope_exit.h"
|
||||||
|
#include "core/core.h"
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
#include <objbase.h>
|
||||||
|
#undef CreateEvent
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace AudioCore::Sink {
|
||||||
|
/**
|
||||||
|
* Cubeb sink stream, responsible for sinking samples to hardware.
|
||||||
|
*/
|
||||||
|
class CubebSinkStream final : public SinkStream {
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Create a new sink stream.
|
||||||
|
*
|
||||||
|
* @param ctx_ - Cubeb context to create this stream with.
|
||||||
|
* @param device_channels_ - Number of channels supported by the hardware.
|
||||||
|
* @param system_channels_ - Number of channels the audio systems expect.
|
||||||
|
* @param output_device - Cubeb output device id.
|
||||||
|
* @param input_device - Cubeb input device id.
|
||||||
|
* @param name_ - Name of this stream.
|
||||||
|
* @param type_ - Type of this stream.
|
||||||
|
* @param system_ - Core system.
|
||||||
|
* @param event - Event used only for audio renderer, signalled on buffer consume.
|
||||||
|
*/
|
||||||
|
CubebSinkStream(cubeb* ctx_, u32 device_channels_, u32 system_channels_,
|
||||||
|
cubeb_devid output_device, cubeb_devid input_device, const std::string& name_,
|
||||||
|
StreamType type_, Core::System& system_)
|
||||||
|
: SinkStream(system_, type_), ctx{ctx_} {
|
||||||
|
#ifdef _WIN32
|
||||||
|
CoInitializeEx(nullptr, COINIT_MULTITHREADED);
|
||||||
|
#endif
|
||||||
|
name = name_;
|
||||||
|
device_channels = device_channels_;
|
||||||
|
system_channels = system_channels_;
|
||||||
|
|
||||||
|
cubeb_stream_params params{};
|
||||||
|
params.rate = TargetSampleRate;
|
||||||
|
params.channels = device_channels;
|
||||||
|
params.format = CUBEB_SAMPLE_S16LE;
|
||||||
|
params.prefs = CUBEB_STREAM_PREF_NONE;
|
||||||
|
switch (params.channels) {
|
||||||
|
case 1:
|
||||||
|
params.layout = CUBEB_LAYOUT_MONO;
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
params.layout = CUBEB_LAYOUT_STEREO;
|
||||||
|
break;
|
||||||
|
case 6:
|
||||||
|
params.layout = CUBEB_LAYOUT_3F2_LFE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 minimum_latency{0};
|
||||||
|
const auto latency_error = cubeb_get_min_latency(ctx, ¶ms, &minimum_latency);
|
||||||
|
if (latency_error != CUBEB_OK) {
|
||||||
|
LOG_CRITICAL(Audio_Sink, "Error getting minimum latency, error: {}", latency_error);
|
||||||
|
minimum_latency = TargetSampleCount * 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
minimum_latency = (std::max)(minimum_latency, TargetSampleCount * 2);
|
||||||
|
|
||||||
|
LOG_INFO(Service_Audio,
|
||||||
|
"Opening cubeb stream {} type {} with: rate {} channels {} (system channels {}) "
|
||||||
|
"latency {}",
|
||||||
|
name, type, params.rate, params.channels, system_channels, minimum_latency);
|
||||||
|
|
||||||
|
auto init_error{0};
|
||||||
|
if (type == StreamType::In) {
|
||||||
|
init_error = cubeb_stream_init(ctx, &stream_backend, name.c_str(), input_device,
|
||||||
|
¶ms, output_device, nullptr, minimum_latency,
|
||||||
|
&CubebSinkStream::DataCallback,
|
||||||
|
&CubebSinkStream::StateCallback, this);
|
||||||
|
} else {
|
||||||
|
init_error = cubeb_stream_init(ctx, &stream_backend, name.c_str(), input_device,
|
||||||
|
nullptr, output_device, ¶ms, minimum_latency,
|
||||||
|
&CubebSinkStream::DataCallback,
|
||||||
|
&CubebSinkStream::StateCallback, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (init_error != CUBEB_OK) {
|
||||||
|
LOG_CRITICAL(Audio_Sink, "Error initializing cubeb stream, error: {}", init_error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Destroy the sink stream.
|
||||||
|
*/
|
||||||
|
~CubebSinkStream() override {
|
||||||
|
LOG_DEBUG(Service_Audio, "Destructing cubeb stream {}", name);
|
||||||
|
|
||||||
|
if (!ctx) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Finalize();
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
CoUninitialize();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finalize the sink stream.
|
||||||
|
*/
|
||||||
|
void Finalize() override {
|
||||||
|
Stop();
|
||||||
|
cubeb_stream_destroy(stream_backend);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Start the sink stream.
|
||||||
|
*
|
||||||
|
* @param resume - Set to true if this is resuming the stream a previously-active stream.
|
||||||
|
* Default false.
|
||||||
|
*/
|
||||||
|
void Start(bool resume = false) override {
|
||||||
|
if (!ctx || !paused) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
paused = false;
|
||||||
|
if (cubeb_stream_start(stream_backend) != CUBEB_OK) {
|
||||||
|
LOG_CRITICAL(Audio_Sink, "Error starting cubeb stream");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stop the sink stream.
|
||||||
|
*/
|
||||||
|
void Stop() override {
|
||||||
|
if (!ctx || paused) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
SignalPause();
|
||||||
|
if (cubeb_stream_stop(stream_backend) != CUBEB_OK) {
|
||||||
|
LOG_CRITICAL(Audio_Sink, "Error stopping cubeb stream");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
/**
|
||||||
|
* Main callback from Cubeb. Either expects samples from us (audio render/audio out), or will
|
||||||
|
* provide samples to be copied (audio in).
|
||||||
|
*
|
||||||
|
* @param stream - Cubeb-specific data about the stream.
|
||||||
|
* @param user_data - Custom data pointer passed along, points to a CubebSinkStream.
|
||||||
|
* @param in_buff - Input buffer to be used if the stream is an input type.
|
||||||
|
* @param out_buff - Output buffer to be used if the stream is an output type.
|
||||||
|
* @param num_frames_ - Number of frames of audio in the buffers. Note: Not number of samples.
|
||||||
|
*/
|
||||||
|
static long DataCallback([[maybe_unused]] cubeb_stream* stream, void* user_data,
|
||||||
|
[[maybe_unused]] const void* in_buff, void* out_buff,
|
||||||
|
long num_frames_) {
|
||||||
|
auto* impl = static_cast<CubebSinkStream*>(user_data);
|
||||||
|
if (!impl) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::size_t num_channels = impl->GetDeviceChannels();
|
||||||
|
const std::size_t frame_size = num_channels;
|
||||||
|
const std::size_t num_frames{static_cast<size_t>(num_frames_)};
|
||||||
|
|
||||||
|
if (impl->type == StreamType::In) {
|
||||||
|
std::span<const s16> input_buffer{reinterpret_cast<const s16*>(in_buff),
|
||||||
|
num_frames * frame_size};
|
||||||
|
impl->ProcessAudioIn(input_buffer, num_frames);
|
||||||
|
} else {
|
||||||
|
std::span<s16> output_buffer{reinterpret_cast<s16*>(out_buff), num_frames * frame_size};
|
||||||
|
impl->ProcessAudioOutAndRender(output_buffer, num_frames);
|
||||||
|
}
|
||||||
|
|
||||||
|
return num_frames_;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cubeb callback for if a device state changes. Unused currently.
|
||||||
|
*
|
||||||
|
* @param stream - Cubeb-specific data about the stream.
|
||||||
|
* @param user_data - Custom data pointer passed along, points to a CubebSinkStream.
|
||||||
|
* @param state - New state of the device.
|
||||||
|
*/
|
||||||
|
static void StateCallback(cubeb_stream*, void*, cubeb_state) {}
|
||||||
|
|
||||||
|
/// Main Cubeb context
|
||||||
|
cubeb* ctx{};
|
||||||
|
/// Cubeb stream backend
|
||||||
|
cubeb_stream* stream_backend{};
|
||||||
|
};
|
||||||
|
|
||||||
|
CubebSink::CubebSink(std::string_view target_device_name) {
|
||||||
|
// Cubeb requires COM to be initialized on the thread calling cubeb_init on Windows
|
||||||
|
#ifdef _WIN32
|
||||||
|
com_init_result = CoInitializeEx(nullptr, COINIT_MULTITHREADED);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (cubeb_init(&ctx, "Eden", nullptr) != CUBEB_OK) {
|
||||||
|
LOG_CRITICAL(Audio_Sink, "cubeb_init failed");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (target_device_name != auto_device_name && !target_device_name.empty()) {
|
||||||
|
cubeb_device_collection collection;
|
||||||
|
if (cubeb_enumerate_devices(ctx, CUBEB_DEVICE_TYPE_OUTPUT, &collection) != CUBEB_OK) {
|
||||||
|
LOG_WARNING(Audio_Sink, "Audio output device enumeration not supported");
|
||||||
|
} else {
|
||||||
|
const auto collection_end{collection.device + collection.count};
|
||||||
|
const auto device{
|
||||||
|
std::find_if(collection.device, collection_end, [&](const cubeb_device_info& info) {
|
||||||
|
return info.friendly_name != nullptr &&
|
||||||
|
target_device_name == std::string(info.friendly_name);
|
||||||
|
})};
|
||||||
|
if (device != collection_end) {
|
||||||
|
output_device = device->devid;
|
||||||
|
}
|
||||||
|
cubeb_device_collection_destroy(ctx, &collection);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cubeb_get_max_channel_count(ctx, &device_channels);
|
||||||
|
device_channels = device_channels >= 6U ? 6U : 2U;
|
||||||
|
}
|
||||||
|
|
||||||
|
CubebSink::~CubebSink() {
|
||||||
|
if (!ctx) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto& sink_stream : sink_streams) {
|
||||||
|
sink_stream.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
cubeb_destroy(ctx);
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
if (SUCCEEDED(com_init_result)) {
|
||||||
|
CoUninitialize();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
SinkStream* CubebSink::AcquireSinkStream(Core::System& system, u32 system_channels_,
|
||||||
|
const std::string& name, StreamType type) {
|
||||||
|
system_channels = system_channels_;
|
||||||
|
SinkStreamPtr& stream = sink_streams.emplace_back(std::make_unique<CubebSinkStream>(
|
||||||
|
ctx, device_channels, system_channels, output_device, input_device, name, type, system));
|
||||||
|
|
||||||
|
return stream.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CubebSink::CloseStream(SinkStream* stream) {
|
||||||
|
for (size_t i = 0; i < sink_streams.size(); i++) {
|
||||||
|
if (sink_streams[i].get() == stream) {
|
||||||
|
sink_streams[i].reset();
|
||||||
|
sink_streams.erase(sink_streams.begin() + i);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CubebSink::CloseStreams() {
|
||||||
|
sink_streams.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
f32 CubebSink::GetDeviceVolume() const {
|
||||||
|
if (sink_streams.empty()) {
|
||||||
|
return 1.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
return sink_streams[0]->GetDeviceVolume();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CubebSink::SetDeviceVolume(f32 volume) {
|
||||||
|
for (auto& stream : sink_streams) {
|
||||||
|
stream->SetDeviceVolume(volume);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CubebSink::SetSystemVolume(f32 volume) {
|
||||||
|
for (auto& stream : sink_streams) {
|
||||||
|
stream->SetSystemVolume(volume);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::string> ListCubebSinkDevices(bool capture) {
|
||||||
|
std::vector<std::string> device_list;
|
||||||
|
cubeb* ctx;
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
auto com_init_result = CoInitializeEx(nullptr, COINIT_MULTITHREADED);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (cubeb_init(&ctx, "Eden Device Enumerator", nullptr) != CUBEB_OK) {
|
||||||
|
LOG_CRITICAL(Audio_Sink, "cubeb_init failed");
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
if (SUCCEEDED(com_init_result)) {
|
||||||
|
CoUninitialize();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
auto type{capture ? CUBEB_DEVICE_TYPE_INPUT : CUBEB_DEVICE_TYPE_OUTPUT};
|
||||||
|
cubeb_device_collection collection;
|
||||||
|
if (cubeb_enumerate_devices(ctx, type, &collection) != CUBEB_OK) {
|
||||||
|
LOG_WARNING(Audio_Sink, "Audio output device enumeration not supported");
|
||||||
|
} else {
|
||||||
|
for (std::size_t i = 0; i < collection.count; i++) {
|
||||||
|
const cubeb_device_info& device = collection.device[i];
|
||||||
|
if (device.friendly_name && device.friendly_name[0] != '\0' &&
|
||||||
|
device.state == CUBEB_DEVICE_STATE_ENABLED) {
|
||||||
|
device_list.emplace_back(device.friendly_name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cubeb_device_collection_destroy(ctx, &collection);
|
||||||
|
}
|
||||||
|
|
||||||
|
cubeb_destroy(ctx);
|
||||||
|
return device_list;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* REVERSION TO 3833 - function GetCubebLatency REINTRODUCED FROM 3833 - DIABLO 3 FIX */
|
||||||
|
u32 GetCubebLatency() {
|
||||||
|
cubeb* ctx;
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
auto com_init_result = CoInitializeEx(nullptr, COINIT_MULTITHREADED);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Init cubeb
|
||||||
|
if (cubeb_init(&ctx, "yuzu Latency Getter", nullptr) != CUBEB_OK) {
|
||||||
|
LOG_CRITICAL(Audio_Sink, "cubeb_init failed");
|
||||||
|
// Return a large latency so we choose SDL instead.
|
||||||
|
return 10000u;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
if (SUCCEEDED(com_init_result)) {
|
||||||
|
CoUninitialize();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Get min latency
|
||||||
|
cubeb_stream_params params{};
|
||||||
|
params.rate = TargetSampleRate;
|
||||||
|
params.channels = 2;
|
||||||
|
params.format = CUBEB_SAMPLE_S16LE;
|
||||||
|
params.prefs = CUBEB_STREAM_PREF_NONE;
|
||||||
|
params.layout = CUBEB_LAYOUT_STEREO;
|
||||||
|
|
||||||
|
u32 latency{0};
|
||||||
|
const auto latency_error = cubeb_get_min_latency(ctx, ¶ms, &latency);
|
||||||
|
if (latency_error != CUBEB_OK) {
|
||||||
|
LOG_CRITICAL(Audio_Sink, "Error getting minimum latency, error: {}", latency_error);
|
||||||
|
latency = TargetSampleCount * 2;
|
||||||
|
}
|
||||||
|
latency = (std::max)(latency, TargetSampleCount * 2);
|
||||||
|
cubeb_destroy(ctx);
|
||||||
|
return latency;
|
||||||
|
}
|
||||||
|
|
||||||
|
// REVERTED back to 3833 - Below namespace section and function IsCubebSuitable() removed, reverting to GetCubebLatency() above. - DIABLO 3 FIX
|
||||||
|
/*
|
||||||
|
namespace {
|
||||||
|
static long TmpDataCallback(cubeb_stream*, void*, const void*, void*, long) {
|
||||||
|
return TargetSampleCount;
|
||||||
|
}
|
||||||
|
static void TmpStateCallback(cubeb_stream*, void*, cubeb_state) {}
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
bool IsCubebSuitable() {
|
||||||
|
#if !defined(HAVE_CUBEB)
|
||||||
|
return false;
|
||||||
|
#else
|
||||||
|
cubeb* ctx{nullptr};
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
auto com_init_result = CoInitializeEx(nullptr, COINIT_MULTITHREADED);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Init cubeb
|
||||||
|
if (cubeb_init(&ctx, "Eden Latency Getter", nullptr) != CUBEB_OK) {
|
||||||
|
LOG_ERROR(Audio_Sink, "Cubeb failed to init, it is not suitable.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
SCOPE_EXIT {
|
||||||
|
cubeb_destroy(ctx);
|
||||||
|
};
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
if (SUCCEEDED(com_init_result)) {
|
||||||
|
CoUninitialize();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Get min latency
|
||||||
|
cubeb_stream_params params{};
|
||||||
|
params.rate = TargetSampleRate;
|
||||||
|
params.channels = 2;
|
||||||
|
params.format = CUBEB_SAMPLE_S16LE;
|
||||||
|
params.prefs = CUBEB_STREAM_PREF_NONE;
|
||||||
|
params.layout = CUBEB_LAYOUT_STEREO;
|
||||||
|
|
||||||
|
u32 latency{0};
|
||||||
|
const auto latency_error = cubeb_get_min_latency(ctx, ¶ms, &latency);
|
||||||
|
if (latency_error != CUBEB_OK) {
|
||||||
|
LOG_ERROR(Audio_Sink, "Cubeb could not get min latency, it is not suitable.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
latency = (std::max)(latency, TargetSampleCount * 2);
|
||||||
|
|
||||||
|
// Test opening a device with standard parameters
|
||||||
|
cubeb_devid output_device{0};
|
||||||
|
cubeb_devid input_device{0};
|
||||||
|
std::string name{"Eden test"};
|
||||||
|
cubeb_stream* stream{nullptr};
|
||||||
|
|
||||||
|
if (cubeb_stream_init(ctx, &stream, name.c_str(), input_device, nullptr, output_device, ¶ms,
|
||||||
|
latency, &TmpDataCallback, &TmpStateCallback, nullptr) != CUBEB_OK) {
|
||||||
|
LOG_CRITICAL(Audio_Sink, "Cubeb could not open a device, it is not suitable.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
cubeb_stream_stop(stream);
|
||||||
|
cubeb_stream_destroy(stream);
|
||||||
|
return true;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
} // namespace AudioCore::Sink
|
||||||
@@ -0,0 +1,118 @@
|
|||||||
|
// SPDX-FileCopyrightText: Copyright 2025 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
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include <cubeb/cubeb.h>
|
||||||
|
|
||||||
|
#include "audio_core/sink/sink.h"
|
||||||
|
|
||||||
|
namespace Core {
|
||||||
|
class System;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace AudioCore::Sink {
|
||||||
|
class SinkStream;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cubeb backend sink, holds multiple output streams and is responsible for sinking samples to
|
||||||
|
* hardware. Used by Audio Render, Audio In and Audio Out.
|
||||||
|
*/
|
||||||
|
class CubebSink final : public Sink {
|
||||||
|
public:
|
||||||
|
explicit CubebSink(std::string_view device_id);
|
||||||
|
~CubebSink() override;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new sink stream.
|
||||||
|
*
|
||||||
|
* @param system - Core system.
|
||||||
|
* @param system_channels - Number of channels the audio system expects.
|
||||||
|
* May differ from the device's channel count.
|
||||||
|
* @param name - Name of this stream.
|
||||||
|
* @param type - Type of this stream, render/in/out.
|
||||||
|
*
|
||||||
|
* @return A pointer to the created SinkStream
|
||||||
|
*/
|
||||||
|
SinkStream* AcquireSinkStream(Core::System& system, u32 system_channels,
|
||||||
|
const std::string& name, StreamType type) override;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Close a given stream.
|
||||||
|
*
|
||||||
|
* @param stream - The stream to close.
|
||||||
|
*/
|
||||||
|
void CloseStream(SinkStream* stream) override;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Close all streams.
|
||||||
|
*/
|
||||||
|
void CloseStreams() override;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the device volume. Set from calls to the IAudioDevice service.
|
||||||
|
*
|
||||||
|
* @return Volume of the device.
|
||||||
|
*/
|
||||||
|
f32 GetDeviceVolume() const override;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the device volume. Set from calls to the IAudioDevice service.
|
||||||
|
*
|
||||||
|
* @param volume - New volume of the device.
|
||||||
|
*/
|
||||||
|
void SetDeviceVolume(f32 volume) override;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the system volume. Comes from the audio system using this stream.
|
||||||
|
*
|
||||||
|
* @param volume - New volume of the system.
|
||||||
|
*/
|
||||||
|
void SetSystemVolume(f32 volume) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
/// Backend Cubeb context
|
||||||
|
cubeb* ctx{};
|
||||||
|
/// Cubeb id of the actual hardware output device
|
||||||
|
cubeb_devid output_device{};
|
||||||
|
/// Cubeb id of the actual hardware input device
|
||||||
|
cubeb_devid input_device{};
|
||||||
|
/// Vector of streams managed by this sink
|
||||||
|
std::vector<SinkStreamPtr> sink_streams{};
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
/// Cubeb required COM to be initialized multi-threaded on Windows
|
||||||
|
u32 com_init_result = 0;
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a list of connected devices from Cubeb.
|
||||||
|
*
|
||||||
|
* @param capture - Return input (capture) devices if true, otherwise output devices.
|
||||||
|
*/
|
||||||
|
std::vector<std::string> ListCubebSinkDevices(bool capture);
|
||||||
|
|
||||||
|
// REVERSION - function GetCubebLatency() reintroduced from EA-3833 - DIABLO 3 FIX
|
||||||
|
/**
|
||||||
|
* Get the reported latency for this sink.
|
||||||
|
*
|
||||||
|
* @return Minimum latency for this sink.
|
||||||
|
*/
|
||||||
|
u32 GetCubebLatency();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if this backend is suitable for use.
|
||||||
|
* Checks if enabled, its latency, whether it opens successfully, etc.
|
||||||
|
*
|
||||||
|
* @return True is this backend is suitable, false otherwise.
|
||||||
|
*/
|
||||||
|
// bool IsCubebSuitable(); // REVERTED BACK TO GetCubebLatency() FROM 3833
|
||||||
|
|
||||||
|
} // namespace AudioCore::Sink
|
||||||
@@ -25,10 +25,9 @@ namespace {
|
|||||||
// See https://github.com/PCSX2/pcsx2/pull/12312
|
// See https://github.com/PCSX2/pcsx2/pull/12312
|
||||||
// "SDL and cubeb backends previously resulted in different names for the output which
|
// "SDL and cubeb backends previously resulted in different names for the output which
|
||||||
// caused them be identified as different applications by the OS."
|
// caused them be identified as different applications by the OS."
|
||||||
|
//
|
||||||
|
// Keep in sync with cubeb_sink.cpp name.
|
||||||
SDL_SetHint("SDL_AUDIO_DEVICE_APP_NAME", "yuzu Latency Getter");
|
SDL_SetHint("SDL_AUDIO_DEVICE_APP_NAME", "yuzu Latency Getter");
|
||||||
SDL_SetHint("SDL_HINT_AUDIO_DEVICE_STREAM_ROLE", "Game");
|
|
||||||
// We do our own processing, so just have SDL copy our audio
|
|
||||||
SDL_SetHint("SDL_HINT_AUDIO_DEVICE_RAW_STREAM", "1");
|
|
||||||
if (!SDL_InitSubSystem(SDL_INIT_AUDIO)) {
|
if (!SDL_InitSubSystem(SDL_INIT_AUDIO)) {
|
||||||
LOG_CRITICAL(Audio_Sink, "SDL_InitSubSystem audio failed: {}", SDL_GetError());
|
LOG_CRITICAL(Audio_Sink, "SDL_InitSubSystem audio failed: {}", SDL_GetError());
|
||||||
return false;
|
return false;
|
||||||
@@ -83,18 +82,23 @@ public:
|
|||||||
|
|
||||||
SDL_AudioSpec spec{};
|
SDL_AudioSpec spec{};
|
||||||
spec.freq = TargetSampleRate;
|
spec.freq = TargetSampleRate;
|
||||||
spec.channels = u8(device_channels);
|
spec.channels = static_cast<u8>(device_channels);
|
||||||
spec.format = SDL_AUDIO_S16LE;
|
spec.format = SDL_AUDIO_S16;
|
||||||
|
|
||||||
auto const is_capture = (type == StreamType::In);
|
std::string device_name{output_device};
|
||||||
std::string device_name = is_capture ? input_device : output_device;
|
bool capture{false};
|
||||||
const SDL_AudioDeviceID audio_device = device_name.empty()
|
if (type == StreamType::In) {
|
||||||
? (is_capture
|
device_name = input_device;
|
||||||
? SDL_AUDIO_DEVICE_DEFAULT_RECORDING
|
capture = true;
|
||||||
: SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK)
|
}
|
||||||
: FindAudioDeviceByName(device_name, is_capture);
|
|
||||||
|
|
||||||
stream = SDL_OpenAudioDeviceStream(audio_device, &spec, &SDLSinkStream::DataCallback, this);
|
const SDL_AudioDeviceID audio_device =
|
||||||
|
device_name.empty() ? (capture ? SDL_AUDIO_DEVICE_DEFAULT_RECORDING
|
||||||
|
: SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK)
|
||||||
|
: FindAudioDeviceByName(device_name, capture);
|
||||||
|
|
||||||
|
stream = SDL_OpenAudioDeviceStream(audio_device, &spec, &SDLSinkStream::DataCallback,
|
||||||
|
this);
|
||||||
|
|
||||||
if (stream == nullptr) {
|
if (stream == nullptr) {
|
||||||
LOG_CRITICAL(Audio_Sink, "Error opening SDL audio device: {}", SDL_GetError());
|
LOG_CRITICAL(Audio_Sink, "Error opening SDL audio device: {}", SDL_GetError());
|
||||||
@@ -103,12 +107,13 @@ public:
|
|||||||
|
|
||||||
SDL_AudioSpec stream_in{};
|
SDL_AudioSpec stream_in{};
|
||||||
SDL_AudioSpec stream_out{};
|
SDL_AudioSpec stream_out{};
|
||||||
void(SDL_GetAudioStreamFormat(stream, &stream_in, &stream_out));
|
static_cast<void>(SDL_GetAudioStreamFormat(stream, &stream_in, &stream_out));
|
||||||
|
|
||||||
LOG_INFO(Service_Audio,
|
LOG_INFO(Service_Audio,
|
||||||
"Opening SDL stream {} with: rate {} channels {} (system channels {}) "
|
"Opening SDL stream {} with: rate {} channels {} (system channels {}) "
|
||||||
" format {}",
|
" format {}",
|
||||||
static_cast<const void*>(stream), stream_out.freq, stream_out.channels,
|
static_cast<const void*>(stream), stream_out.freq, stream_out.channels,
|
||||||
system_channels, int(stream_out.format));
|
system_channels, static_cast<int>(stream_out.format));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -123,12 +128,14 @@ public:
|
|||||||
* Finalize the sink stream.
|
* Finalize the sink stream.
|
||||||
*/
|
*/
|
||||||
void Finalize() override {
|
void Finalize() override {
|
||||||
if (stream != nullptr) {
|
if (stream == nullptr) {
|
||||||
Stop();
|
return;
|
||||||
SDL_ClearAudioStream(stream);
|
|
||||||
SDL_DestroyAudioStream(stream);
|
|
||||||
stream = nullptr;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Stop();
|
||||||
|
SDL_ClearAudioStream(stream);
|
||||||
|
SDL_DestroyAudioStream(stream);
|
||||||
|
stream = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -141,8 +148,9 @@ public:
|
|||||||
if (stream == nullptr || !paused) {
|
if (stream == nullptr || !paused) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
paused = false;
|
paused = false;
|
||||||
void(SDL_ResumeAudioStreamDevice(stream));
|
static_cast<void>(SDL_ResumeAudioStreamDevice(stream));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -153,7 +161,7 @@ public:
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
SignalPause();
|
SignalPause();
|
||||||
void(SDL_PauseAudioStreamDevice(stream));
|
static_cast<void>(SDL_PauseAudioStreamDevice(stream));
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@@ -165,34 +173,51 @@ private:
|
|||||||
* @param stream - Buffer of samples to be filled or read.
|
* @param stream - Buffer of samples to be filled or read.
|
||||||
* @param len - Length of the stream in bytes.
|
* @param len - Length of the stream in bytes.
|
||||||
*/
|
*/
|
||||||
static void DataCallback(void* userdata, SDL_AudioStream* stream, int additional_amount, int total_amount) {
|
static void DataCallback(void* userdata, SDL_AudioStream* stream, int additional_amount,
|
||||||
if (auto* impl = static_cast<SDLSinkStream*>(userdata); impl) {
|
int total_amount) {
|
||||||
auto const num_channels = impl->GetDeviceChannels();
|
auto* impl = static_cast<SDLSinkStream*>(userdata);
|
||||||
auto const frame_size = num_channels;
|
|
||||||
if (impl->type == StreamType::In) {
|
if (!impl) {
|
||||||
auto const bytes_available = SDL_GetAudioStreamAvailable(stream);
|
return;
|
||||||
if (bytes_available > 0) {
|
}
|
||||||
std::vector<s16> input(bytes_available / int(sizeof(s16)));
|
|
||||||
auto const bytes_read = SDL_GetAudioStreamData(stream, input.data(), bytes_available);
|
const std::size_t num_channels = impl->GetDeviceChannels();
|
||||||
if (bytes_read > 0) {
|
const std::size_t frame_size = num_channels;
|
||||||
auto const num_frames = std::size_t(bytes_read) / sizeof(s16) / frame_size;
|
|
||||||
std::span<const s16> input_buffer{input.data(), std::size_t(bytes_read) / sizeof(s16)};
|
if (impl->type == StreamType::In) {
|
||||||
impl->ProcessAudioIn(input_buffer, num_frames);
|
const int bytes_available = SDL_GetAudioStreamAvailable(stream);
|
||||||
}
|
if (bytes_available <= 0) {
|
||||||
}
|
return;
|
||||||
} else if (additional_amount > 0 || total_amount > 0) {
|
|
||||||
auto const bytes_requested = additional_amount > 0 ? additional_amount : total_amount;
|
|
||||||
std::vector<s16> output(bytes_requested / int(sizeof(s16)));
|
|
||||||
auto const num_frames = std::size_t(bytes_requested) / sizeof(s16) / frame_size;
|
|
||||||
std::span<s16> output_buffer{output.data(), output.size()};
|
|
||||||
impl->ProcessAudioOutAndRender(output_buffer, num_frames);
|
|
||||||
void(SDL_PutAudioStreamData(stream, output.data(), bytes_requested));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::vector<s16> input(bytes_available / static_cast<int>(sizeof(s16)));
|
||||||
|
const int bytes_read = SDL_GetAudioStreamData(stream, input.data(), bytes_available);
|
||||||
|
if (bytes_read <= 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::size_t num_frames =
|
||||||
|
static_cast<std::size_t>(bytes_read) / sizeof(s16) / frame_size;
|
||||||
|
std::span<const s16> input_buffer{input.data(),
|
||||||
|
static_cast<std::size_t>(bytes_read) / sizeof(s16)};
|
||||||
|
impl->ProcessAudioIn(input_buffer, num_frames);
|
||||||
|
} else {
|
||||||
|
if (additional_amount <= 0 && total_amount <= 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const int bytes_requested = additional_amount > 0 ? additional_amount : total_amount;
|
||||||
|
std::vector<s16> output(bytes_requested / static_cast<int>(sizeof(s16)));
|
||||||
|
const std::size_t num_frames =
|
||||||
|
static_cast<std::size_t>(bytes_requested) / sizeof(s16) / frame_size;
|
||||||
|
std::span<s16> output_buffer{output.data(), output.size()};
|
||||||
|
impl->ProcessAudioOutAndRender(output_buffer, num_frames);
|
||||||
|
static_cast<void>(SDL_PutAudioStreamData(stream, output.data(), bytes_requested));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// SDL stream attached to an opened input/output device
|
/// SDL stream attached to an opened input/output device
|
||||||
SDL_AudioStream* stream = nullptr;
|
SDL_AudioStream* stream{};
|
||||||
};
|
};
|
||||||
|
|
||||||
SDLSink::SDLSink(std::string_view target_device_name) {
|
SDLSink::SDLSink(std::string_view target_device_name) {
|
||||||
@@ -208,9 +233,11 @@ SDLSink::SDLSink(std::string_view target_device_name) {
|
|||||||
|
|
||||||
SDLSink::~SDLSink() = default;
|
SDLSink::~SDLSink() = default;
|
||||||
|
|
||||||
SinkStream* SDLSink::AcquireSinkStream(Core::System& system, u32 system_channels_, const std::string&, StreamType type) {
|
SinkStream* SDLSink::AcquireSinkStream(Core::System& system, u32 system_channels_,
|
||||||
|
const std::string&, StreamType type) {
|
||||||
system_channels = system_channels_;
|
system_channels = system_channels_;
|
||||||
SinkStreamPtr& stream = sink_streams.emplace_back(std::make_unique<SDLSinkStream>(device_channels, system_channels, output_device, input_device, type, system));
|
SinkStreamPtr& stream = sink_streams.emplace_back(std::make_unique<SDLSinkStream>(
|
||||||
|
device_channels, system_channels, output_device, input_device, type, system));
|
||||||
return stream.get();
|
return stream.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -229,7 +256,11 @@ void SDLSink::CloseStreams() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
f32 SDLSink::GetDeviceVolume() const {
|
f32 SDLSink::GetDeviceVolume() const {
|
||||||
return sink_streams.empty() ? 1.0f : sink_streams[0]->GetDeviceVolume();
|
if (sink_streams.empty()) {
|
||||||
|
return 1.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
return sink_streams[0]->GetDeviceVolume();
|
||||||
}
|
}
|
||||||
|
|
||||||
void SDLSink::SetDeviceVolume(f32 volume) {
|
void SDLSink::SetDeviceVolume(f32 volume) {
|
||||||
@@ -244,21 +275,68 @@ void SDLSink::SetSystemVolume(f32 volume) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<std::string> ListSDLSinkDevices(bool is_capture) {
|
std::vector<std::string> ListSDLSinkDevices(bool capture) {
|
||||||
std::vector<std::string> device_list{};
|
if (!InitializeAudio())
|
||||||
if (InitializeAudio()) {
|
return {}; //no devices
|
||||||
int device_count = 0;
|
|
||||||
SDL_AudioDeviceID* devices = is_capture ? SDL_GetAudioRecordingDevices(&device_count) : SDL_GetAudioPlaybackDevices(&device_count);
|
std::vector<std::string> device_list;
|
||||||
if (devices != nullptr) {
|
int device_count = 0;
|
||||||
for (int i = 0; i < device_count; ++i) {
|
SDL_AudioDeviceID* devices =
|
||||||
if (const char* name = SDL_GetAudioDeviceName(devices[i]); name) {
|
capture ? SDL_GetAudioRecordingDevices(&device_count)
|
||||||
device_list.emplace_back(name);
|
: SDL_GetAudioPlaybackDevices(&device_count);
|
||||||
}
|
if (devices == nullptr) {
|
||||||
}
|
return device_list;
|
||||||
SDL_free(devices);
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < device_count; ++i) {
|
||||||
|
if (const char* name = SDL_GetAudioDeviceName(devices[i])) {
|
||||||
|
device_list.emplace_back(name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
SDL_free(devices);
|
||||||
|
|
||||||
return device_list;
|
return device_list;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* REVERSION to 3833 - function GetSDLLatency() REINTRODUCED FROM 3833 - DIABLO 3 FIX */
|
||||||
|
u32 GetSDLLatency() {
|
||||||
|
return TargetSampleCount * 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
// REVERTED back to 3833 - Below function IsSDLSuitable() removed, reverting to GetSDLLatency() above. - DIABLO 3 FIX
|
||||||
|
/*
|
||||||
|
bool IsSDLSuitable() {
|
||||||
|
#if !defined(HAVE_SDL3)
|
||||||
|
return false;
|
||||||
|
#else
|
||||||
|
// Check SDL can init
|
||||||
|
if (!InitializeAudio()!
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// We can set any latency frequency we want with SDL, so no need to check that.
|
||||||
|
|
||||||
|
// Check we can open a device with standard parameters
|
||||||
|
SDL_AudioSpec spec;
|
||||||
|
spec.freq = TargetSampleRate;
|
||||||
|
spec.channels = 2u;
|
||||||
|
spec.format = AUDIO_S16SYS;
|
||||||
|
spec.samples = TargetSampleCount * 2;
|
||||||
|
spec.callback = nullptr;
|
||||||
|
spec.userdata = nullptr;
|
||||||
|
|
||||||
|
SDL_AudioSpec obtained;
|
||||||
|
auto device = SDL_OpenAudioDevice(nullptr, false, &spec, &obtained, false);
|
||||||
|
|
||||||
|
if (device == 0) {
|
||||||
|
LOG_ERROR(Audio_Sink, "SDL failed to open a device, it is not suitable. Error: {}",
|
||||||
|
SDL_GetError());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
SDL_CloseAudioDevice(device);
|
||||||
|
return true;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
} // namespace AudioCore::Sink
|
} // namespace AudioCore::Sink
|
||||||
|
|||||||
@@ -90,4 +90,20 @@ private:
|
|||||||
*/
|
*/
|
||||||
std::vector<std::string> ListSDLSinkDevices(bool capture);
|
std::vector<std::string> ListSDLSinkDevices(bool capture);
|
||||||
|
|
||||||
|
// REVERSION - function GetSDLLatency() reintroduced from EA-3833 - DIABLO 3 FIX
|
||||||
|
/**
|
||||||
|
* Get the reported latency for this sink.
|
||||||
|
*
|
||||||
|
* @return Minimum latency for this sink.
|
||||||
|
*/
|
||||||
|
u32 GetSDLLatency();
|
||||||
|
|
||||||
|
/** REVERTED back to 3833 - Below function IsSDLSuitable() removed, reverting to GetSDLLatency() above. - DIABLO 3 FIX
|
||||||
|
* Check if this backend is suitable for use.
|
||||||
|
* Checks if enabled, its latency, whether it opens successfully, etc.
|
||||||
|
*
|
||||||
|
* @return True is this backend is suitable, false otherwise.
|
||||||
|
*/
|
||||||
|
//bool IsSDLSuitable(); // REVERTED for GetSDLLatency() from EA-3833
|
||||||
|
|
||||||
} // namespace AudioCore::Sink
|
} // namespace AudioCore::Sink
|
||||||
|
|||||||
@@ -10,6 +10,9 @@
|
|||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "audio_core/sink/sink_details.h"
|
#include "audio_core/sink/sink_details.h"
|
||||||
|
#ifdef HAVE_CUBEB
|
||||||
|
#include "audio_core/sink/cubeb_sink.h"
|
||||||
|
#endif
|
||||||
#ifdef HAVE_SDL3
|
#ifdef HAVE_SDL3
|
||||||
#include "audio_core/sink/sdl3_sink.h"
|
#include "audio_core/sink/sdl3_sink.h"
|
||||||
#endif
|
#endif
|
||||||
@@ -31,10 +34,24 @@ struct SinkDetails {
|
|||||||
FactoryFn factory;
|
FactoryFn factory;
|
||||||
/// A method to call to list available devices.
|
/// A method to call to list available devices.
|
||||||
ListDevicesFn list_devices;
|
ListDevicesFn list_devices;
|
||||||
|
/// Method to get the latency of this backend - REINTRODUCED FROM 3833 - DIABLO 3 FIX
|
||||||
|
LatencyFn latency;
|
||||||
|
/// Check whether this backend is suitable to be used.
|
||||||
|
/// SuitableFn is_suitable; // REVERTED FOR LatencyFn latency ABOVE - DIABLO 3 FIX
|
||||||
};
|
};
|
||||||
|
|
||||||
// sink_details is ordered in terms of desirability, with the best choice at the top.
|
// sink_details is ordered in terms of desirability, with the best choice at the top.
|
||||||
static constexpr SinkDetails sink_details[] = {
|
constexpr SinkDetails sink_details[] = {
|
||||||
|
#ifdef HAVE_CUBEB
|
||||||
|
SinkDetails{
|
||||||
|
Settings::AudioEngine::Cubeb,
|
||||||
|
[](std::string_view device_id) -> std::unique_ptr<Sink> {
|
||||||
|
return std::make_unique<CubebSink>(device_id);
|
||||||
|
},
|
||||||
|
&ListCubebSinkDevices,
|
||||||
|
&GetCubebLatency,
|
||||||
|
},
|
||||||
|
#endif
|
||||||
#ifdef HAVE_SDL3
|
#ifdef HAVE_SDL3
|
||||||
SinkDetails{
|
SinkDetails{
|
||||||
Settings::AudioEngine::Sdl3,
|
Settings::AudioEngine::Sdl3,
|
||||||
@@ -42,6 +59,7 @@ static constexpr SinkDetails sink_details[] = {
|
|||||||
return std::make_unique<SDLSink>(device_id);
|
return std::make_unique<SDLSink>(device_id);
|
||||||
},
|
},
|
||||||
&ListSDLSinkDevices,
|
&ListSDLSinkDevices,
|
||||||
|
&GetSDLLatency,
|
||||||
},
|
},
|
||||||
#endif
|
#endif
|
||||||
SinkDetails{
|
SinkDetails{
|
||||||
@@ -50,32 +68,69 @@ static constexpr SinkDetails sink_details[] = {
|
|||||||
return std::make_unique<NullSink>(device_id);
|
return std::make_unique<NullSink>(device_id);
|
||||||
},
|
},
|
||||||
[](bool capture) { return std::vector<std::string>{"null"}; },
|
[](bool capture) { return std::vector<std::string>{"null"}; },
|
||||||
|
[]() { return 0u; },
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const SinkDetails& GetOutputSinkDetails(Settings::AudioEngine sink_id) {
|
const SinkDetails& GetOutputSinkDetails(Settings::AudioEngine sink_id) {
|
||||||
const auto find_backend{[](Settings::AudioEngine id) {
|
const auto find_backend{[](Settings::AudioEngine id) {
|
||||||
return std::ranges::find_if(std::begin(sink_details), std::end(sink_details), [&id](const auto& e) {
|
return std::find_if(std::begin(sink_details), std::end(sink_details),
|
||||||
return e.id == id;
|
[&id](const auto& sink_detail) { return sink_detail.id == id; });
|
||||||
});
|
|
||||||
}};
|
}};
|
||||||
|
|
||||||
auto iter = find_backend(sink_id);
|
auto iter = find_backend(sink_id);
|
||||||
if (sink_id == Settings::AudioEngine::Cubeb || sink_id == Settings::AudioEngine::Auto) {
|
|
||||||
|
if (sink_id == Settings::AudioEngine::Auto) {
|
||||||
|
// REVERTED TO 3833 BELOW - DIABLO 3 FIX
|
||||||
|
/*
|
||||||
|
// Auto-select a backend. Use the sink details ordering, preferring cubeb first, checking
|
||||||
|
// that the backend is available and suitable to use.
|
||||||
|
for (auto& details : sink_details) {
|
||||||
|
if (details.is_suitable()) {
|
||||||
|
iter = &details;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/ // END REVERTED CODE - DIABLO 3 FIX
|
||||||
|
|
||||||
|
// BEGIN REINTRODUCED FROM 3833 - REPLACED CODE BLOCK ABOVE - DIABLO 3 FIX
|
||||||
|
// Auto-select a backend. Prefer CubeB, but it may report a large minimum latency which
|
||||||
|
// causes audio issues, in that case go with SDL.
|
||||||
|
#if defined(HAVE_CUBEB) && defined(HAVE_SDL3)
|
||||||
|
iter = find_backend(Settings::AudioEngine::Cubeb);
|
||||||
|
if (iter->latency() > TargetSampleCount * 3) {
|
||||||
iter = find_backend(Settings::AudioEngine::Sdl3);
|
iter = find_backend(Settings::AudioEngine::Sdl3);
|
||||||
LOG_INFO(Service_Audio, "Auto-selecting the {} backend", Settings::CanonicalizeEnum(iter->id));
|
}
|
||||||
|
#else
|
||||||
|
iter = std::begin(sink_details);
|
||||||
|
#endif
|
||||||
|
// END REINTRODUCED SECTION FROM 3833 - DIABLO 3 FIX
|
||||||
|
LOG_INFO(Service_Audio, "Auto-selecting the {} backend",
|
||||||
|
Settings::CanonicalizeEnum(iter->id));
|
||||||
|
/* BEGIN REMOVED - REVERTING BACK TO 3833, this didn't exist at all. - DIABLO 3 FIX
|
||||||
|
} else {
|
||||||
|
if (iter != std::end(sink_details) && !iter->is_suitable()) {
|
||||||
|
LOG_ERROR(Service_Audio, "Selected backend {} is not suitable, falling back to null",
|
||||||
|
Settings::CanonicalizeEnum(iter->id));
|
||||||
|
iter = find_backend(Settings::AudioEngine::Null);
|
||||||
|
} */ // END REMOVED REVERT - DIABLO 3 FIX
|
||||||
}
|
}
|
||||||
|
|
||||||
if (iter == std::end(sink_details)) {
|
if (iter == std::end(sink_details)) {
|
||||||
LOG_ERROR(Audio, "Invalid sink_id {}", Settings::CanonicalizeEnum(sink_id));
|
LOG_ERROR(Audio, "Invalid sink_id {}", Settings::CanonicalizeEnum(sink_id));
|
||||||
iter = find_backend(Settings::AudioEngine::Null);
|
iter = find_backend(Settings::AudioEngine::Null);
|
||||||
}
|
}
|
||||||
|
|
||||||
return *iter;
|
return *iter;
|
||||||
}
|
}
|
||||||
} // Anonymous namespace
|
} // Anonymous namespace
|
||||||
|
|
||||||
std::vector<Settings::AudioEngine> GetSinkIDs() {
|
std::vector<Settings::AudioEngine> GetSinkIDs() {
|
||||||
std::vector<Settings::AudioEngine> sink_ids(std::size(sink_details));
|
std::vector<Settings::AudioEngine> sink_ids(std::size(sink_details));
|
||||||
std::transform(std::begin(sink_details), std::end(sink_details), std::begin(sink_ids), [](const auto& sink) { return sink.id; });
|
|
||||||
|
std::transform(std::begin(sink_details), std::end(sink_details), std::begin(sink_ids),
|
||||||
|
[](const auto& sink) { return sink.id; });
|
||||||
|
|
||||||
return sink_ids;
|
return sink_ids;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -89,6 +89,7 @@ add_library(
|
|||||||
param_package.h
|
param_package.h
|
||||||
parent_of_member.h
|
parent_of_member.h
|
||||||
point.h
|
point.h
|
||||||
|
quaternion.h
|
||||||
range_map.h
|
range_map.h
|
||||||
range_mutex.h
|
range_mutex.h
|
||||||
range_sets.h
|
range_sets.h
|
||||||
|
|||||||
@@ -0,0 +1,79 @@
|
|||||||
|
// SPDX-FileCopyrightText: 2016 Citra Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "common/vector_math.h"
|
||||||
|
|
||||||
|
namespace Common {
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
class Quaternion {
|
||||||
|
public:
|
||||||
|
Vec3<T> xyz;
|
||||||
|
T w{};
|
||||||
|
|
||||||
|
[[nodiscard]] Quaternion<decltype(-T{})> Inverse() const {
|
||||||
|
return {-xyz, w};
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] Quaternion<decltype(T{} + T{})> operator+(const Quaternion& other) const {
|
||||||
|
return {xyz + other.xyz, w + other.w};
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] Quaternion<decltype(T{} - T{})> operator-(const Quaternion& other) const {
|
||||||
|
return {xyz - other.xyz, w - other.w};
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] Quaternion<decltype(T{} * T{} - T{} * T{})> operator*(
|
||||||
|
const Quaternion& other) const {
|
||||||
|
return {xyz * other.w + other.xyz * w + Cross(xyz, other.xyz),
|
||||||
|
w * other.w - Dot(xyz, other.xyz)};
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] Quaternion<T> Normalized() const {
|
||||||
|
T length = std::sqrt(xyz.Length2() + w * w);
|
||||||
|
return {xyz / length, w / length};
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] std::array<decltype(-T{}), 16> ToMatrix() const {
|
||||||
|
const T x2 = xyz[0] * xyz[0];
|
||||||
|
const T y2 = xyz[1] * xyz[1];
|
||||||
|
const T z2 = xyz[2] * xyz[2];
|
||||||
|
|
||||||
|
const T xy = xyz[0] * xyz[1];
|
||||||
|
const T wz = w * xyz[2];
|
||||||
|
const T xz = xyz[0] * xyz[2];
|
||||||
|
const T wy = w * xyz[1];
|
||||||
|
const T yz = xyz[1] * xyz[2];
|
||||||
|
const T wx = w * xyz[0];
|
||||||
|
|
||||||
|
return {1.0f - 2.0f * (y2 + z2),
|
||||||
|
2.0f * (xy + wz),
|
||||||
|
2.0f * (xz - wy),
|
||||||
|
0.0f,
|
||||||
|
2.0f * (xy - wz),
|
||||||
|
1.0f - 2.0f * (x2 + z2),
|
||||||
|
2.0f * (yz + wx),
|
||||||
|
0.0f,
|
||||||
|
2.0f * (xz + wy),
|
||||||
|
2.0f * (yz - wx),
|
||||||
|
1.0f - 2.0f * (x2 + y2),
|
||||||
|
0.0f,
|
||||||
|
0.0f,
|
||||||
|
0.0f,
|
||||||
|
0.0f,
|
||||||
|
1.0f};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
[[nodiscard]] auto QuaternionRotate(const Quaternion<T>& q, const Vec3<T>& v) {
|
||||||
|
return v + 2 * Cross(q.xyz, Cross(q.xyz, v) + v * q.w);
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] inline Quaternion<float> MakeQuaternion(const Vec3<float>& axis, float angle) {
|
||||||
|
return {axis * std::sin(angle / 2), std::cos(angle / 2)};
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Common
|
||||||
@@ -389,7 +389,7 @@ struct Values {
|
|||||||
true};
|
true};
|
||||||
|
|
||||||
SwitchableSetting<bool> frame_gen{linkage, false, "frame_gen", Category::Renderer,
|
SwitchableSetting<bool> frame_gen{linkage, false, "frame_gen", Category::Renderer,
|
||||||
Specialization::Default, true, false};
|
Specialization::Default, true, true};
|
||||||
|
|
||||||
SwitchableSetting<u32, true> frame_gen_multiplier{linkage,
|
SwitchableSetting<u32, true> frame_gen_multiplier{linkage,
|
||||||
2,
|
2,
|
||||||
@@ -399,7 +399,7 @@ struct Values {
|
|||||||
Category::Renderer,
|
Category::Renderer,
|
||||||
Specialization::Countable,
|
Specialization::Countable,
|
||||||
true,
|
true,
|
||||||
false,
|
true,
|
||||||
&frame_gen};
|
&frame_gen};
|
||||||
|
|
||||||
SwitchableSetting<u32, true> frame_gen_target_rate{linkage,
|
SwitchableSetting<u32, true> frame_gen_target_rate{linkage,
|
||||||
@@ -419,7 +419,7 @@ struct Values {
|
|||||||
Category::Renderer,
|
Category::Renderer,
|
||||||
Specialization::Default,
|
Specialization::Default,
|
||||||
true,
|
true,
|
||||||
false,
|
true,
|
||||||
&frame_gen};
|
&frame_gen};
|
||||||
|
|
||||||
SwitchableSetting<u32, true> frame_gen_flow_scale{linkage,
|
SwitchableSetting<u32, true> frame_gen_flow_scale{linkage,
|
||||||
@@ -442,11 +442,11 @@ struct Values {
|
|||||||
Category::Renderer,
|
Category::Renderer,
|
||||||
Specialization::Countable,
|
Specialization::Countable,
|
||||||
true,
|
true,
|
||||||
false,
|
true,
|
||||||
&frame_gen};
|
&frame_gen};
|
||||||
|
|
||||||
SwitchableSetting<bool> frame_gen_fp16{linkage, true, "frame_gen_fp16", Category::Renderer,
|
SwitchableSetting<bool> frame_gen_fp16{linkage, true, "frame_gen_fp16", Category::Renderer,
|
||||||
Specialization::Default, true, false, &frame_gen};
|
Specialization::Default, true, true, &frame_gen};
|
||||||
|
|
||||||
SwitchableSetting<bool> frame_gen_dump_flow{linkage, false, "frame_gen_dump_flow",
|
SwitchableSetting<bool> frame_gen_dump_flow{linkage, false, "frame_gen_dump_flow",
|
||||||
Category::Renderer};
|
Category::Renderer};
|
||||||
@@ -858,7 +858,7 @@ struct Values {
|
|||||||
SwitchableSetting<std::string> program_args{linkage,
|
SwitchableSetting<std::string> program_args{linkage,
|
||||||
std::string(),
|
std::string(),
|
||||||
"program_args",
|
"program_args",
|
||||||
Category::Debugging,
|
Category::System,
|
||||||
Specialization::Default,
|
Specialization::Default,
|
||||||
true, // save_ - persist in config file
|
true, // save_ - persist in config file
|
||||||
false}; // runtime_modifiable_ - startup-only
|
false}; // runtime_modifiable_ - startup-only
|
||||||
@@ -904,7 +904,7 @@ struct Values {
|
|||||||
0,
|
0,
|
||||||
65535,
|
65535,
|
||||||
"debug_knobs",
|
"debug_knobs",
|
||||||
Category::Debugging,
|
Category::System,
|
||||||
Specialization::Countable,
|
Specialization::Countable,
|
||||||
true,
|
true,
|
||||||
true};
|
true};
|
||||||
|
|||||||
+717
-93
@@ -1,4 +1,4 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: 2014 Tony Wasserka
|
// SPDX-FileCopyrightText: 2014 Tony Wasserka
|
||||||
@@ -7,128 +7,752 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#ifdef __ARM_NEON
|
||||||
|
#include <arm_neon.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
|
|
||||||
namespace Common {
|
namespace Common {
|
||||||
|
|
||||||
template <typename T, size_t N>
|
template <typename T>
|
||||||
class Vec {
|
class Vec2;
|
||||||
|
template <typename T>
|
||||||
|
class Vec3;
|
||||||
|
template <typename T>
|
||||||
|
class Vec4;
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
class Vec2 {
|
||||||
public:
|
public:
|
||||||
std::array<T, N> elems{};
|
T x{};
|
||||||
|
T y{};
|
||||||
|
|
||||||
constexpr Vec() = default;
|
constexpr Vec2() = default;
|
||||||
constexpr Vec(T e0) noexcept : elems{e0} {}
|
constexpr Vec2(const T& x_, const T& y_) : x(x_), y(y_) {}
|
||||||
constexpr Vec(T e0, T e1) noexcept : elems{e0, e1} {}
|
|
||||||
constexpr Vec(T e0, T e1, T e2) noexcept : elems{e0, e1, e2} {}
|
|
||||||
constexpr Vec(T e0, T e1, T e2, T e4) noexcept : elems{e0, e1, e2, e4} {}
|
|
||||||
//explicit constexpr Vec(const std::initializer_list<T> elems_) noexcept : elems{elems_} {}
|
|
||||||
|
|
||||||
[[nodiscard]] constexpr Vec<decltype(T{} + T{}), N> operator+(const Vec o) const noexcept {
|
template <typename T2>
|
||||||
Vec<decltype(T{} + T{}), N> r{};
|
[[nodiscard]] constexpr Vec2<T2> Cast() const {
|
||||||
for (size_t i = 0; i < N; ++i)
|
return Vec2<T2>(static_cast<T2>(x), static_cast<T2>(y));
|
||||||
r.elems[i] = elems[i] + o.elems[i];
|
|
||||||
return r;
|
|
||||||
}
|
}
|
||||||
constexpr Vec<T, N> operator+=(const Vec<T, N> o) noexcept { return *this = *this + o; }
|
|
||||||
|
|
||||||
[[nodiscard]] constexpr Vec<decltype(T{} - T{}), N> operator-(const Vec o) const noexcept {
|
[[nodiscard]] static constexpr Vec2 AssignToAll(const T& f) {
|
||||||
Vec<decltype(T{} - T{}), N> r{};
|
return Vec2{f, f};
|
||||||
for (size_t i = 0; i < N; ++i)
|
}
|
||||||
r.elems[i] = elems[i] - o.elems[i];
|
|
||||||
return r;
|
[[nodiscard]] constexpr Vec2<decltype(T{} + T{})> operator+(const Vec2& other) const {
|
||||||
|
return {x + other.x, y + other.y};
|
||||||
|
}
|
||||||
|
constexpr Vec2& operator+=(const Vec2& other) {
|
||||||
|
x += other.x;
|
||||||
|
y += other.y;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
[[nodiscard]] constexpr Vec2<decltype(T{} - T{})> operator-(const Vec2& other) const {
|
||||||
|
return {x - other.x, y - other.y};
|
||||||
|
}
|
||||||
|
constexpr Vec2& operator-=(const Vec2& other) {
|
||||||
|
x -= other.x;
|
||||||
|
y -= other.y;
|
||||||
|
return *this;
|
||||||
}
|
}
|
||||||
constexpr Vec<T, N> operator-=(const Vec<T, N> o) noexcept { return *this = *this - o; }
|
|
||||||
|
|
||||||
template <typename U = T>
|
template <typename U = T>
|
||||||
[[nodiscard]] constexpr Vec<std::enable_if_t<std::is_signed_v<U>, U>, N> operator-() const noexcept {
|
[[nodiscard]] constexpr Vec2<std::enable_if_t<std::is_signed_v<U>, U>> operator-() const {
|
||||||
Vec<U, N> r{};
|
return {-x, -y};
|
||||||
for (size_t i = 0; i < N; ++i)
|
}
|
||||||
r.elems[i] = -elems[i];
|
[[nodiscard]] constexpr Vec2<decltype(T{} * T{})> operator*(const Vec2& other) const {
|
||||||
return r;
|
return {x * other.x, y * other.y};
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] constexpr Vec<decltype(T{} * T{}), N> operator*(const Vec o) const noexcept {
|
|
||||||
Vec<decltype(T{} * T{}), N> r{};
|
|
||||||
for (size_t i = 0; i < N; ++i)
|
|
||||||
r.elems[i] = elems[i] * o.elems[i];
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
template <typename V>
|
template <typename V>
|
||||||
[[nodiscard]] constexpr Vec<decltype(T{} * V{}), N> operator*(const V f) const noexcept {
|
[[nodiscard]] constexpr Vec2<decltype(T{} * V{})> operator*(const V& f) const {
|
||||||
using TV = decltype(T{} * V{});
|
using TV = decltype(T{} * V{});
|
||||||
using C = std::common_type_t<T, V>;
|
using C = std::common_type_t<T, V>;
|
||||||
Vec<TV, N> r{};
|
|
||||||
for (size_t i = 0; i < N; ++i)
|
return {
|
||||||
r.elems[i] = TV(C(elems[i]) * C(f));
|
static_cast<TV>(static_cast<C>(x) * static_cast<C>(f)),
|
||||||
return r;
|
static_cast<TV>(static_cast<C>(y) * static_cast<C>(f)),
|
||||||
|
};
|
||||||
}
|
}
|
||||||
template <typename V>
|
|
||||||
constexpr Vec<T, N> operator*=(const V f) noexcept { return *this = *this * f; }
|
|
||||||
|
|
||||||
template <typename V>
|
template <typename V>
|
||||||
[[nodiscard]] constexpr Vec<decltype(T{} / V{}), N> operator/(const V f) const noexcept {
|
constexpr Vec2& operator*=(const V& f) {
|
||||||
|
*this = *this * f;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename V>
|
||||||
|
[[nodiscard]] constexpr Vec2<decltype(T{} / V{})> operator/(const V& f) const {
|
||||||
using TV = decltype(T{} / V{});
|
using TV = decltype(T{} / V{});
|
||||||
using C = std::common_type_t<T, V>;
|
using C = std::common_type_t<T, V>;
|
||||||
Vec<TV, N> r{};
|
|
||||||
for (size_t i = 0; i < N; ++i)
|
|
||||||
r.elems[i] = TV(C(elems[i]) / C(f));
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
template <typename V>
|
|
||||||
constexpr Vec<T, N> operator/=(const V f) noexcept { return *this = *this / f; }
|
|
||||||
|
|
||||||
[[nodiscard]] constexpr T Length2() const noexcept {
|
|
||||||
T r{};
|
|
||||||
for (size_t i = 0; i < N; ++i)
|
|
||||||
r += elems[i] * elems[i];
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
// Only implemented for T=float
|
|
||||||
[[nodiscard]] T Length() const { return T(std::sqrt(float(Length2()))); }
|
|
||||||
[[nodiscard]] Vec<T, N> Normalized() const { return *this / Length(); }
|
|
||||||
[[nodiscard]] constexpr T& operator[](std::size_t i) noexcept { return elems[i]; }
|
|
||||||
[[nodiscard]] constexpr const T& operator[](std::size_t i) const noexcept { return elems[i]; }
|
|
||||||
|
|
||||||
[[nodiscard]] std::array<decltype(-T{}), 16> ToMatrix() const {
|
|
||||||
const T x2 = elems[0] * elems[0];
|
|
||||||
const T y2 = elems[1] * elems[1];
|
|
||||||
const T z2 = elems[2] * elems[2];
|
|
||||||
|
|
||||||
const T xy = elems[0] * elems[1];
|
|
||||||
const T wz = elems[3] * elems[2];
|
|
||||||
const T xz = elems[0] * elems[2];
|
|
||||||
const T wy = elems[3] * elems[1];
|
|
||||||
const T yz = elems[1] * elems[2];
|
|
||||||
const T wx = elems[3] * elems[0];
|
|
||||||
return {
|
return {
|
||||||
1.0f - 2.0f * (y2 + z2),
|
static_cast<TV>(static_cast<C>(x) / static_cast<C>(f)),
|
||||||
2.0f * (xy + wz),
|
static_cast<TV>(static_cast<C>(y) / static_cast<C>(f)),
|
||||||
2.0f * (xz - wy),
|
|
||||||
0.0f,
|
|
||||||
2.0f * (xy - wz),
|
|
||||||
1.0f - 2.0f * (x2 + z2),
|
|
||||||
2.0f * (yz + wx),
|
|
||||||
0.0f,
|
|
||||||
2.0f * (xz + wy),
|
|
||||||
2.0f * (yz - wx),
|
|
||||||
1.0f - 2.0f * (x2 + y2),
|
|
||||||
0.0f,
|
|
||||||
0.0f,
|
|
||||||
0.0f,
|
|
||||||
0.0f,
|
|
||||||
1.0f
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename V>
|
||||||
|
constexpr Vec2& operator/=(const V& f) {
|
||||||
|
*this = *this / f;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] constexpr T Length2() const {
|
||||||
|
return x * x + y * y;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only implemented for T=float
|
||||||
|
[[nodiscard]] float Length() const;
|
||||||
|
[[nodiscard]] float Normalize(); // returns the previous length, which is often useful
|
||||||
|
|
||||||
|
[[nodiscard]] constexpr T& operator[](std::size_t i) {
|
||||||
|
return *((&x) + i);
|
||||||
|
}
|
||||||
|
[[nodiscard]] constexpr const T& operator[](std::size_t i) const {
|
||||||
|
return *((&x) + i);
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr void SetZero() {
|
||||||
|
x = 0;
|
||||||
|
y = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Common aliases: UV (texel coordinates), ST (texture coordinates)
|
||||||
|
[[nodiscard]] constexpr T& u() {
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
[[nodiscard]] constexpr T& v() {
|
||||||
|
return y;
|
||||||
|
}
|
||||||
|
[[nodiscard]] constexpr T& s() {
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
[[nodiscard]] constexpr T& t() {
|
||||||
|
return y;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] constexpr const T& u() const {
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
[[nodiscard]] constexpr const T& v() const {
|
||||||
|
return y;
|
||||||
|
}
|
||||||
|
[[nodiscard]] constexpr const T& s() const {
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
[[nodiscard]] constexpr const T& t() const {
|
||||||
|
return y;
|
||||||
|
}
|
||||||
|
|
||||||
|
// swizzlers - create a subvector of specific components
|
||||||
|
[[nodiscard]] constexpr Vec2 yx() const {
|
||||||
|
return Vec2(y, x);
|
||||||
|
}
|
||||||
|
[[nodiscard]] constexpr Vec2 vu() const {
|
||||||
|
return Vec2(y, x);
|
||||||
|
}
|
||||||
|
[[nodiscard]] constexpr Vec2 ts() const {
|
||||||
|
return Vec2(y, x);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename T, size_t N, typename V>
|
template <typename T, typename V>
|
||||||
[[nodiscard]] constexpr Vec<T, N> operator*(const V f, const Vec<T, N> v) noexcept {
|
[[nodiscard]] constexpr Vec2<T> operator*(const V& f, const Vec2<T>& vec) {
|
||||||
using C = std::common_type_t<T, V>;
|
using C = std::common_type_t<T, V>;
|
||||||
Vec<T, N> r{};
|
|
||||||
for (size_t i = 0; i < N; ++i)
|
return Vec2<T>(static_cast<T>(static_cast<C>(f) * static_cast<C>(vec.x)),
|
||||||
r.elems[i] = T(C(f) * C(v.elems[i]));
|
static_cast<T>(static_cast<C>(f) * static_cast<C>(vec.y)));
|
||||||
return r;
|
}
|
||||||
|
|
||||||
|
using Vec2f = Vec2<float>;
|
||||||
|
|
||||||
|
template <>
|
||||||
|
inline float Vec2<float>::Length() const {
|
||||||
|
return std::sqrt(x * x + y * y);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
inline float Vec2<float>::Normalize() {
|
||||||
|
float length = Length();
|
||||||
|
*this /= length;
|
||||||
|
return length;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
class Vec3 {
|
||||||
|
public:
|
||||||
|
T x{};
|
||||||
|
T y{};
|
||||||
|
T z{};
|
||||||
|
|
||||||
|
constexpr Vec3() = default;
|
||||||
|
constexpr Vec3(const T& x_, const T& y_, const T& z_) : x(x_), y(y_), z(z_) {}
|
||||||
|
|
||||||
|
template <typename T2>
|
||||||
|
[[nodiscard]] constexpr Vec3<T2> Cast() const {
|
||||||
|
return Vec3<T2>(static_cast<T2>(x), static_cast<T2>(y), static_cast<T2>(z));
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] static constexpr Vec3 AssignToAll(const T& f) {
|
||||||
|
return Vec3(f, f, f);
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] constexpr Vec3<decltype(T{} + T{})> operator+(const Vec3& other) const {
|
||||||
|
return {x + other.x, y + other.y, z + other.z};
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr Vec3& operator+=(const Vec3& other) {
|
||||||
|
x += other.x;
|
||||||
|
y += other.y;
|
||||||
|
z += other.z;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] constexpr Vec3<decltype(T{} - T{})> operator-(const Vec3& other) const {
|
||||||
|
return {x - other.x, y - other.y, z - other.z};
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr Vec3& operator-=(const Vec3& other) {
|
||||||
|
x -= other.x;
|
||||||
|
y -= other.y;
|
||||||
|
z -= other.z;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename U = T>
|
||||||
|
[[nodiscard]] constexpr Vec3<std::enable_if_t<std::is_signed_v<U>, U>> operator-() const {
|
||||||
|
return {-x, -y, -z};
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] constexpr Vec3<decltype(T{} * T{})> operator*(const Vec3& other) const {
|
||||||
|
return {x * other.x, y * other.y, z * other.z};
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename V>
|
||||||
|
[[nodiscard]] constexpr Vec3<decltype(T{} * V{})> operator*(const V& f) const {
|
||||||
|
using TV = decltype(T{} * V{});
|
||||||
|
using C = std::common_type_t<T, V>;
|
||||||
|
|
||||||
|
return {
|
||||||
|
static_cast<TV>(static_cast<C>(x) * static_cast<C>(f)),
|
||||||
|
static_cast<TV>(static_cast<C>(y) * static_cast<C>(f)),
|
||||||
|
static_cast<TV>(static_cast<C>(z) * static_cast<C>(f)),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename V>
|
||||||
|
constexpr Vec3& operator*=(const V& f) {
|
||||||
|
*this = *this * f;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
template <typename V>
|
||||||
|
[[nodiscard]] constexpr Vec3<decltype(T{} / V{})> operator/(const V& f) const {
|
||||||
|
using TV = decltype(T{} / V{});
|
||||||
|
using C = std::common_type_t<T, V>;
|
||||||
|
|
||||||
|
return {
|
||||||
|
static_cast<TV>(static_cast<C>(x) / static_cast<C>(f)),
|
||||||
|
static_cast<TV>(static_cast<C>(y) / static_cast<C>(f)),
|
||||||
|
static_cast<TV>(static_cast<C>(z) / static_cast<C>(f)),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename V>
|
||||||
|
constexpr Vec3& operator/=(const V& f) {
|
||||||
|
*this = *this / f;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
void RotateFromOrigin(float roll, float pitch, float yaw) {
|
||||||
|
float temp = y;
|
||||||
|
y = std::cos(roll) * y - std::sin(roll) * z;
|
||||||
|
z = std::sin(roll) * temp + std::cos(roll) * z;
|
||||||
|
|
||||||
|
temp = x;
|
||||||
|
x = std::cos(pitch) * x + std::sin(pitch) * z;
|
||||||
|
z = -std::sin(pitch) * temp + std::cos(pitch) * z;
|
||||||
|
|
||||||
|
temp = x;
|
||||||
|
x = std::cos(yaw) * x - std::sin(yaw) * y;
|
||||||
|
y = std::sin(yaw) * temp + std::cos(yaw) * y;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] constexpr T Length2() const {
|
||||||
|
return x * x + y * y + z * z;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only implemented for T=float
|
||||||
|
[[nodiscard]] float Length() const;
|
||||||
|
[[nodiscard]] Vec3 Normalized() const;
|
||||||
|
[[nodiscard]] float Normalize(); // returns the previous length, which is often useful
|
||||||
|
|
||||||
|
[[nodiscard]] constexpr T& operator[](std::size_t i) {
|
||||||
|
return *((&x) + i);
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] constexpr const T& operator[](std::size_t i) const {
|
||||||
|
return *((&x) + i);
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr void SetZero() {
|
||||||
|
x = 0;
|
||||||
|
y = 0;
|
||||||
|
z = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Common aliases: UVW (texel coordinates), RGB (colors), STQ (texture coordinates)
|
||||||
|
[[nodiscard]] constexpr T& u() {
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
[[nodiscard]] constexpr T& v() {
|
||||||
|
return y;
|
||||||
|
}
|
||||||
|
[[nodiscard]] constexpr T& w() {
|
||||||
|
return z;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] constexpr T& r() {
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
[[nodiscard]] constexpr T& g() {
|
||||||
|
return y;
|
||||||
|
}
|
||||||
|
[[nodiscard]] constexpr T& b() {
|
||||||
|
return z;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] constexpr T& s() {
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
[[nodiscard]] constexpr T& t() {
|
||||||
|
return y;
|
||||||
|
}
|
||||||
|
[[nodiscard]] constexpr T& q() {
|
||||||
|
return z;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] constexpr const T& u() const {
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
[[nodiscard]] constexpr const T& v() const {
|
||||||
|
return y;
|
||||||
|
}
|
||||||
|
[[nodiscard]] constexpr const T& w() const {
|
||||||
|
return z;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] constexpr const T& r() const {
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
[[nodiscard]] constexpr const T& g() const {
|
||||||
|
return y;
|
||||||
|
}
|
||||||
|
[[nodiscard]] constexpr const T& b() const {
|
||||||
|
return z;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] constexpr const T& s() const {
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
[[nodiscard]] constexpr const T& t() const {
|
||||||
|
return y;
|
||||||
|
}
|
||||||
|
[[nodiscard]] constexpr const T& q() const {
|
||||||
|
return z;
|
||||||
|
}
|
||||||
|
|
||||||
|
// swizzlers - create a subvector of specific components
|
||||||
|
// e.g. Vec2 uv() { return Vec2(x,y); }
|
||||||
|
// _DEFINE_SWIZZLER2 defines a single such function, DEFINE_SWIZZLER2 defines all of them for all
|
||||||
|
// component names (x<->r) and permutations (xy<->yx)
|
||||||
|
#define _DEFINE_SWIZZLER2(a, b, name) \
|
||||||
|
[[nodiscard]] constexpr Vec2<T> name() const { return Vec2<T>(a, b); }
|
||||||
|
#define DEFINE_SWIZZLER2(a, b, a2, b2, a3, b3, a4, b4) \
|
||||||
|
_DEFINE_SWIZZLER2(a, b, a##b); \
|
||||||
|
_DEFINE_SWIZZLER2(a, b, a2##b2); \
|
||||||
|
_DEFINE_SWIZZLER2(a, b, a3##b3); \
|
||||||
|
_DEFINE_SWIZZLER2(a, b, a4##b4); \
|
||||||
|
_DEFINE_SWIZZLER2(b, a, b##a); \
|
||||||
|
_DEFINE_SWIZZLER2(b, a, b2##a2); \
|
||||||
|
_DEFINE_SWIZZLER2(b, a, b3##a3); \
|
||||||
|
_DEFINE_SWIZZLER2(b, a, b4##a4)
|
||||||
|
|
||||||
|
DEFINE_SWIZZLER2(x, y, r, g, u, v, s, t);
|
||||||
|
DEFINE_SWIZZLER2(x, z, r, b, u, w, s, q);
|
||||||
|
DEFINE_SWIZZLER2(y, z, g, b, v, w, t, q);
|
||||||
|
#undef DEFINE_SWIZZLER2
|
||||||
|
#undef _DEFINE_SWIZZLER2
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T, typename V>
|
||||||
|
[[nodiscard]] constexpr Vec3<T> operator*(const V& f, const Vec3<T>& vec) {
|
||||||
|
using C = std::common_type_t<T, V>;
|
||||||
|
|
||||||
|
return Vec3<T>(static_cast<T>(static_cast<C>(f) * static_cast<C>(vec.x)),
|
||||||
|
static_cast<T>(static_cast<C>(f) * static_cast<C>(vec.y)),
|
||||||
|
static_cast<T>(static_cast<C>(f) * static_cast<C>(vec.z)));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
inline float Vec3<float>::Length() const {
|
||||||
|
return std::sqrt(x * x + y * y + z * z);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
inline Vec3<float> Vec3<float>::Normalized() const {
|
||||||
|
return *this / Length();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
inline float Vec3<float>::Normalize() {
|
||||||
|
float length = Length();
|
||||||
|
*this /= length;
|
||||||
|
return length;
|
||||||
|
}
|
||||||
|
|
||||||
|
using Vec3f = Vec3<float>;
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
class Vec4 {
|
||||||
|
public:
|
||||||
|
T x{};
|
||||||
|
T y{};
|
||||||
|
T z{};
|
||||||
|
T w{};
|
||||||
|
|
||||||
|
constexpr Vec4() = default;
|
||||||
|
constexpr Vec4(const T& x_, const T& y_, const T& z_, const T& w_)
|
||||||
|
: x(x_), y(y_), z(z_), w(w_) {}
|
||||||
|
|
||||||
|
template <typename T2>
|
||||||
|
[[nodiscard]] constexpr Vec4<T2> Cast() const {
|
||||||
|
return Vec4<T2>(static_cast<T2>(x), static_cast<T2>(y), static_cast<T2>(z),
|
||||||
|
static_cast<T2>(w));
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] static constexpr Vec4 AssignToAll(const T& f) {
|
||||||
|
return Vec4(f, f, f, f);
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] constexpr Vec4<decltype(T{} + T{})> operator+(const Vec4& other) const {
|
||||||
|
return {x + other.x, y + other.y, z + other.z, w + other.w};
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr Vec4& operator+=(const Vec4& other) {
|
||||||
|
x += other.x;
|
||||||
|
y += other.y;
|
||||||
|
z += other.z;
|
||||||
|
w += other.w;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] constexpr Vec4<decltype(T{} - T{})> operator-(const Vec4& other) const {
|
||||||
|
return {x - other.x, y - other.y, z - other.z, w - other.w};
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr Vec4& operator-=(const Vec4& other) {
|
||||||
|
x -= other.x;
|
||||||
|
y -= other.y;
|
||||||
|
z -= other.z;
|
||||||
|
w -= other.w;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename U = T>
|
||||||
|
[[nodiscard]] constexpr Vec4<std::enable_if_t<std::is_signed_v<U>, U>> operator-() const {
|
||||||
|
return {-x, -y, -z, -w};
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] constexpr Vec4<decltype(T{} * T{})> operator*(const Vec4& other) const {
|
||||||
|
return {x * other.x, y * other.y, z * other.z, w * other.w};
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename V>
|
||||||
|
[[nodiscard]] constexpr Vec4<decltype(T{} * V{})> operator*(const V& f) const {
|
||||||
|
using TV = decltype(T{} * V{});
|
||||||
|
using C = std::common_type_t<T, V>;
|
||||||
|
|
||||||
|
return {
|
||||||
|
static_cast<TV>(static_cast<C>(x) * static_cast<C>(f)),
|
||||||
|
static_cast<TV>(static_cast<C>(y) * static_cast<C>(f)),
|
||||||
|
static_cast<TV>(static_cast<C>(z) * static_cast<C>(f)),
|
||||||
|
static_cast<TV>(static_cast<C>(w) * static_cast<C>(f)),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename V>
|
||||||
|
constexpr Vec4& operator*=(const V& f) {
|
||||||
|
*this = *this * f;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename V>
|
||||||
|
[[nodiscard]] constexpr Vec4<decltype(T{} / V{})> operator/(const V& f) const {
|
||||||
|
using TV = decltype(T{} / V{});
|
||||||
|
using C = std::common_type_t<T, V>;
|
||||||
|
|
||||||
|
return {
|
||||||
|
static_cast<TV>(static_cast<C>(x) / static_cast<C>(f)),
|
||||||
|
static_cast<TV>(static_cast<C>(y) / static_cast<C>(f)),
|
||||||
|
static_cast<TV>(static_cast<C>(z) / static_cast<C>(f)),
|
||||||
|
static_cast<TV>(static_cast<C>(w) / static_cast<C>(f)),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename V>
|
||||||
|
constexpr Vec4& operator/=(const V& f) {
|
||||||
|
*this = *this / f;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] constexpr T Length2() const {
|
||||||
|
return x * x + y * y + z * z + w * w;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] constexpr T& operator[](std::size_t i) {
|
||||||
|
return *((&x) + i);
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] constexpr const T& operator[](std::size_t i) const {
|
||||||
|
return *((&x) + i);
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr void SetZero() {
|
||||||
|
x = 0;
|
||||||
|
y = 0;
|
||||||
|
z = 0;
|
||||||
|
w = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Common alias: RGBA (colors)
|
||||||
|
[[nodiscard]] constexpr T& r() {
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
[[nodiscard]] constexpr T& g() {
|
||||||
|
return y;
|
||||||
|
}
|
||||||
|
[[nodiscard]] constexpr T& b() {
|
||||||
|
return z;
|
||||||
|
}
|
||||||
|
[[nodiscard]] constexpr T& a() {
|
||||||
|
return w;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] constexpr const T& r() const {
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
[[nodiscard]] constexpr const T& g() const {
|
||||||
|
return y;
|
||||||
|
}
|
||||||
|
[[nodiscard]] constexpr const T& b() const {
|
||||||
|
return z;
|
||||||
|
}
|
||||||
|
[[nodiscard]] constexpr const T& a() const {
|
||||||
|
return w;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Swizzlers - Create a subvector of specific components
|
||||||
|
// e.g. Vec2 uv() { return Vec2(x,y); }
|
||||||
|
|
||||||
|
// _DEFINE_SWIZZLER2 defines a single such function
|
||||||
|
// DEFINE_SWIZZLER2_COMP1 defines one-component functions for all component names (x<->r)
|
||||||
|
// DEFINE_SWIZZLER2_COMP2 defines two component functions for all component names (x<->r) and
|
||||||
|
// permutations (xy<->yx)
|
||||||
|
#define _DEFINE_SWIZZLER2(a, b, name) \
|
||||||
|
[[nodiscard]] constexpr Vec2<T> name() const { return Vec2<T>(a, b); }
|
||||||
|
#define DEFINE_SWIZZLER2_COMP1(a, a2) \
|
||||||
|
_DEFINE_SWIZZLER2(a, a, a##a); \
|
||||||
|
_DEFINE_SWIZZLER2(a, a, a2##a2)
|
||||||
|
#define DEFINE_SWIZZLER2_COMP2(a, b, a2, b2) \
|
||||||
|
_DEFINE_SWIZZLER2(a, b, a##b); \
|
||||||
|
_DEFINE_SWIZZLER2(a, b, a2##b2); \
|
||||||
|
_DEFINE_SWIZZLER2(b, a, b##a); \
|
||||||
|
_DEFINE_SWIZZLER2(b, a, b2##a2)
|
||||||
|
|
||||||
|
DEFINE_SWIZZLER2_COMP2(x, y, r, g);
|
||||||
|
DEFINE_SWIZZLER2_COMP2(x, z, r, b);
|
||||||
|
DEFINE_SWIZZLER2_COMP2(x, w, r, a);
|
||||||
|
DEFINE_SWIZZLER2_COMP2(y, z, g, b);
|
||||||
|
DEFINE_SWIZZLER2_COMP2(y, w, g, a);
|
||||||
|
DEFINE_SWIZZLER2_COMP2(z, w, b, a);
|
||||||
|
DEFINE_SWIZZLER2_COMP1(x, r);
|
||||||
|
DEFINE_SWIZZLER2_COMP1(y, g);
|
||||||
|
DEFINE_SWIZZLER2_COMP1(z, b);
|
||||||
|
DEFINE_SWIZZLER2_COMP1(w, a);
|
||||||
|
#undef DEFINE_SWIZZLER2_COMP1
|
||||||
|
#undef DEFINE_SWIZZLER2_COMP2
|
||||||
|
#undef _DEFINE_SWIZZLER2
|
||||||
|
|
||||||
|
#define _DEFINE_SWIZZLER3(a, b, c, name) \
|
||||||
|
[[nodiscard]] constexpr Vec3<T> name() const { return Vec3<T>(a, b, c); }
|
||||||
|
#define DEFINE_SWIZZLER3_COMP1(a, a2) \
|
||||||
|
_DEFINE_SWIZZLER3(a, a, a, a##a##a); \
|
||||||
|
_DEFINE_SWIZZLER3(a, a, a, a2##a2##a2)
|
||||||
|
#define DEFINE_SWIZZLER3_COMP3(a, b, c, a2, b2, c2) \
|
||||||
|
_DEFINE_SWIZZLER3(a, b, c, a##b##c); \
|
||||||
|
_DEFINE_SWIZZLER3(a, c, b, a##c##b); \
|
||||||
|
_DEFINE_SWIZZLER3(b, a, c, b##a##c); \
|
||||||
|
_DEFINE_SWIZZLER3(b, c, a, b##c##a); \
|
||||||
|
_DEFINE_SWIZZLER3(c, a, b, c##a##b); \
|
||||||
|
_DEFINE_SWIZZLER3(c, b, a, c##b##a); \
|
||||||
|
_DEFINE_SWIZZLER3(a, b, c, a2##b2##c2); \
|
||||||
|
_DEFINE_SWIZZLER3(a, c, b, a2##c2##b2); \
|
||||||
|
_DEFINE_SWIZZLER3(b, a, c, b2##a2##c2); \
|
||||||
|
_DEFINE_SWIZZLER3(b, c, a, b2##c2##a2); \
|
||||||
|
_DEFINE_SWIZZLER3(c, a, b, c2##a2##b2); \
|
||||||
|
_DEFINE_SWIZZLER3(c, b, a, c2##b2##a2)
|
||||||
|
|
||||||
|
DEFINE_SWIZZLER3_COMP3(x, y, z, r, g, b);
|
||||||
|
DEFINE_SWIZZLER3_COMP3(x, y, w, r, g, a);
|
||||||
|
DEFINE_SWIZZLER3_COMP3(x, z, w, r, b, a);
|
||||||
|
DEFINE_SWIZZLER3_COMP3(y, z, w, g, b, a);
|
||||||
|
DEFINE_SWIZZLER3_COMP1(x, r);
|
||||||
|
DEFINE_SWIZZLER3_COMP1(y, g);
|
||||||
|
DEFINE_SWIZZLER3_COMP1(z, b);
|
||||||
|
DEFINE_SWIZZLER3_COMP1(w, a);
|
||||||
|
#undef DEFINE_SWIZZLER3_COMP1
|
||||||
|
#undef DEFINE_SWIZZLER3_COMP3
|
||||||
|
#undef _DEFINE_SWIZZLER3
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T, typename V>
|
||||||
|
[[nodiscard]] constexpr Vec4<decltype(V{} * T{})> operator*(const V& f, const Vec4<T>& vec) {
|
||||||
|
using TV = decltype(V{} * T{});
|
||||||
|
using C = std::common_type_t<T, V>;
|
||||||
|
|
||||||
|
return {
|
||||||
|
static_cast<TV>(static_cast<C>(f) * static_cast<C>(vec.x)),
|
||||||
|
static_cast<TV>(static_cast<C>(f) * static_cast<C>(vec.y)),
|
||||||
|
static_cast<TV>(static_cast<C>(f) * static_cast<C>(vec.z)),
|
||||||
|
static_cast<TV>(static_cast<C>(f) * static_cast<C>(vec.w)),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
using Vec4f = Vec4<float>;
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
constexpr decltype(T{} * T{} + T{} * T{}) Dot(const Vec2<T>& a, const Vec2<T>& b) {
|
||||||
|
return a.x * b.x + a.y * b.y;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
[[nodiscard]] constexpr decltype(T{} * T{} + T{} * T{}) Dot(const Vec3<T>& a, const Vec3<T>& b) {
|
||||||
|
return a.x * b.x + a.y * b.y + a.z * b.z;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
[[nodiscard]] constexpr decltype(T{} * T{} + T{} * T{}) Dot(const Vec4<T>& a, const Vec4<T>& b) {
|
||||||
|
return a.x * b.x + a.y * b.y + a.z * b.z + a.w * b.w;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
[[nodiscard]] inline float Dot(const Vec4<float>& a, const Vec4<float>& b) {
|
||||||
|
#ifdef __ARM_NEON
|
||||||
|
float32x4_t va = vld1q_f32(&a.x);
|
||||||
|
float32x4_t vb = vld1q_f32(&b.x);
|
||||||
|
float32x4_t result = vmulq_f32(va, vb);
|
||||||
|
#if defined(__aarch64__) // Use vaddvq_f32 in ARMv8 architectures
|
||||||
|
return vaddvq_f32(result);
|
||||||
|
#else // Use manual addition for older architectures
|
||||||
|
float32x2_t sum2 = vadd_f32(vget_high_f32(result), vget_low_f32(result));
|
||||||
|
return vget_lane_f32(vpadd_f32(sum2, sum2), 0);
|
||||||
|
#endif
|
||||||
|
#else
|
||||||
|
return a.x * b.x + a.y * b.y + a.z * b.z + a.w * b.w;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
[[nodiscard]] constexpr Vec3<decltype(T{} * T{} - T{} * T{})> Cross(const Vec3<T>& a,
|
||||||
|
const Vec3<T>& b) {
|
||||||
|
return {a.y * b.z - a.z * b.y, a.z * b.x - a.x * b.z, a.x * b.y - a.y * b.x};
|
||||||
|
}
|
||||||
|
|
||||||
|
// linear interpolation via float: 0.0=begin, 1.0=end
|
||||||
|
template <typename X>
|
||||||
|
[[nodiscard]] constexpr decltype(X{} * float{} + X{} * float{}) Lerp(const X& begin, const X& end,
|
||||||
|
const float t) {
|
||||||
|
return begin * (1.f - t) + end * t;
|
||||||
|
}
|
||||||
|
|
||||||
|
// linear interpolation via int: 0=begin, base=end
|
||||||
|
template <typename X, int base>
|
||||||
|
[[nodiscard]] constexpr decltype((X{} * int{} + X{} * int{}) / base) LerpInt(const X& begin,
|
||||||
|
const X& end,
|
||||||
|
const int t) {
|
||||||
|
return (begin * (base - t) + end * t) / base;
|
||||||
|
}
|
||||||
|
|
||||||
|
// bilinear interpolation. s is for interpolating x00-x01 and x10-x11, and t is for the second
|
||||||
|
// interpolation.
|
||||||
|
template <typename X>
|
||||||
|
[[nodiscard]] constexpr auto BilinearInterp(const X& x00, const X& x01, const X& x10, const X& x11,
|
||||||
|
const float s, const float t) {
|
||||||
|
auto y0 = Lerp(x00, x01, s);
|
||||||
|
auto y1 = Lerp(x10, x11, s);
|
||||||
|
return Lerp(y0, y1, t);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Utility vector factories
|
||||||
|
template <typename T>
|
||||||
|
[[nodiscard]] constexpr Vec2<T> MakeVec(const T& x, const T& y) {
|
||||||
|
return Vec2<T>{x, y};
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
[[nodiscard]] constexpr Vec3<T> MakeVec(const T& x, const T& y, const T& z) {
|
||||||
|
return Vec3<T>{x, y, z};
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
[[nodiscard]] constexpr Vec4<T> MakeVec(const T& x, const T& y, const Vec2<T>& zw) {
|
||||||
|
return MakeVec(x, y, zw[0], zw[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
[[nodiscard]] constexpr Vec3<T> MakeVec(const Vec2<T>& xy, const T& z) {
|
||||||
|
return MakeVec(xy[0], xy[1], z);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
[[nodiscard]] constexpr Vec3<T> MakeVec(const T& x, const Vec2<T>& yz) {
|
||||||
|
return MakeVec(x, yz[0], yz[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
[[nodiscard]] constexpr Vec4<T> MakeVec(const T& x, const T& y, const T& z, const T& w) {
|
||||||
|
return Vec4<T>{x, y, z, w};
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
[[nodiscard]] constexpr Vec4<T> MakeVec(const Vec2<T>& xy, const T& z, const T& w) {
|
||||||
|
return MakeVec(xy[0], xy[1], z, w);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
[[nodiscard]] constexpr Vec4<T> MakeVec(const T& x, const Vec2<T>& yz, const T& w) {
|
||||||
|
return MakeVec(x, yz[0], yz[1], w);
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE: This has priority over "Vec2<Vec2<T>> MakeVec(const Vec2<T>& x, const Vec2<T>& y)".
|
||||||
|
// Even if someone wanted to use an odd object like Vec2<Vec2<T>>, the compiler would error
|
||||||
|
// out soon enough due to misuse of the returned structure.
|
||||||
|
template <typename T>
|
||||||
|
[[nodiscard]] constexpr Vec4<T> MakeVec(const Vec2<T>& xy, const Vec2<T>& zw) {
|
||||||
|
return MakeVec(xy[0], xy[1], zw[0], zw[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
[[nodiscard]] constexpr Vec4<T> MakeVec(const Vec3<T>& xyz, const T& w) {
|
||||||
|
return MakeVec(xyz[0], xyz[1], xyz[2], w);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
[[nodiscard]] constexpr Vec4<T> MakeVec(const T& x, const Vec3<T>& yzw) {
|
||||||
|
return MakeVec(x, yzw[0], yzw[1], yzw[2]);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Common
|
} // namespace Common
|
||||||
|
|||||||
@@ -121,10 +121,7 @@ union Exclusive {
|
|||||||
constexpr explicit Exclusive(u32 raw_) : raw{raw_} {}
|
constexpr explicit Exclusive(u32 raw_) : raw{raw_} {}
|
||||||
|
|
||||||
constexpr bool Verify() {
|
constexpr bool Verify() {
|
||||||
if (this->GetSig() != 0x10) return false;
|
return this->GetSig() == 0x10;
|
||||||
const bool pair = decltype(l)::ExtractValue(raw) & 1;
|
|
||||||
const bool fixed_rt2 = decltype(rt2)::ExtractValue(raw) == 0b11111;
|
|
||||||
return pair || fixed_rt2;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr u32 GetSig() {
|
constexpr u32 GetSig() {
|
||||||
|
|||||||
@@ -10,15 +10,17 @@
|
|||||||
#include "core/hle/kernel/k_process.h"
|
#include "core/hle/kernel/k_process.h"
|
||||||
#include "core/hle/kernel/k_resource_limit.h"
|
#include "core/hle/kernel/k_resource_limit.h"
|
||||||
#include "core/hle/kernel/svc.h"
|
#include "core/hle/kernel/svc.h"
|
||||||
#include "core/hle/kernel/svc_results.h"
|
|
||||||
#include "core/hle/kernel/svc_version.h"
|
|
||||||
|
|
||||||
namespace Kernel::Svc {
|
namespace Kernel::Svc {
|
||||||
|
|
||||||
/// Gets system/memory information for the current process
|
/// Gets system/memory information for the current process
|
||||||
Result GetInfo(Core::System& system, u64* result, InfoType info_id_type, Handle handle, u64 info_sub_id) {
|
Result GetInfo(Core::System& system, u64* result, InfoType info_id_type, Handle handle,
|
||||||
LOG_TRACE(Kernel_SVC, "called info_id={:#x}, info_sub_id={:#x}, handle={:#08x}", info_id_type, info_sub_id, handle);
|
u64 info_sub_id) {
|
||||||
u32 info_id = u32(info_id_type);
|
LOG_TRACE(Kernel_SVC, "called info_id={:#x}, info_sub_id={:#x}, handle={:#08x}",
|
||||||
|
info_id_type, info_sub_id, handle);
|
||||||
|
|
||||||
|
u32 info_id = static_cast<u32>(info_id_type);
|
||||||
|
|
||||||
switch (info_id_type) {
|
switch (info_id_type) {
|
||||||
case InfoType::CoreMask:
|
case InfoType::CoreMask:
|
||||||
case InfoType::PriorityMask:
|
case InfoType::PriorityMask:
|
||||||
@@ -51,123 +53,152 @@ Result GetInfo(Core::System& system, u64* result, InfoType info_id_type, Handle
|
|||||||
case InfoType::CoreMask:
|
case InfoType::CoreMask:
|
||||||
*result = process->GetCoreMask();
|
*result = process->GetCoreMask();
|
||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
|
|
||||||
case InfoType::PriorityMask:
|
case InfoType::PriorityMask:
|
||||||
*result = process->GetPriorityMask();
|
*result = process->GetPriorityMask();
|
||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
|
|
||||||
case InfoType::AliasRegionAddress:
|
case InfoType::AliasRegionAddress:
|
||||||
*result = GetInteger(process->GetPageTable().GetAliasRegionStart());
|
*result = GetInteger(process->GetPageTable().GetAliasRegionStart());
|
||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
|
|
||||||
case InfoType::AliasRegionSize:
|
case InfoType::AliasRegionSize:
|
||||||
*result = process->GetPageTable().GetAliasRegionSize();
|
*result = process->GetPageTable().GetAliasRegionSize();
|
||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
case InfoType::HeapRegionAddress:
|
case InfoType::HeapRegionAddress:
|
||||||
*result = GetInteger(process->GetPageTable().GetHeapRegionStart());
|
*result = GetInteger(process->GetPageTable().GetHeapRegionStart());
|
||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
|
|
||||||
case InfoType::HeapRegionSize:
|
case InfoType::HeapRegionSize:
|
||||||
*result = process->GetPageTable().GetHeapRegionSize();
|
*result = process->GetPageTable().GetHeapRegionSize();
|
||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
|
|
||||||
case InfoType::AslrRegionAddress:
|
case InfoType::AslrRegionAddress:
|
||||||
*result = GetInteger(process->GetPageTable().GetAliasCodeRegionStart());
|
*result = GetInteger(process->GetPageTable().GetAliasCodeRegionStart());
|
||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
|
|
||||||
case InfoType::AslrRegionSize:
|
case InfoType::AslrRegionSize:
|
||||||
*result = process->GetPageTable().GetAliasCodeRegionSize();
|
*result = process->GetPageTable().GetAliasCodeRegionSize();
|
||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
|
|
||||||
case InfoType::StackRegionAddress:
|
case InfoType::StackRegionAddress:
|
||||||
*result = GetInteger(process->GetPageTable().GetStackRegionStart());
|
*result = GetInteger(process->GetPageTable().GetStackRegionStart());
|
||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
|
|
||||||
case InfoType::StackRegionSize:
|
case InfoType::StackRegionSize:
|
||||||
*result = process->GetPageTable().GetStackRegionSize();
|
*result = process->GetPageTable().GetStackRegionSize();
|
||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
|
|
||||||
case InfoType::TotalMemorySize:
|
case InfoType::TotalMemorySize:
|
||||||
*result = process->GetTotalUserPhysicalMemorySize(system.Kernel());
|
*result = process->GetTotalUserPhysicalMemorySize(system.Kernel());
|
||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
|
|
||||||
case InfoType::UsedMemorySize:
|
case InfoType::UsedMemorySize:
|
||||||
*result = process->GetUsedUserPhysicalMemorySize(system.Kernel());
|
*result = process->GetUsedUserPhysicalMemorySize(system.Kernel());
|
||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
|
|
||||||
case InfoType::SystemResourceSizeTotal:
|
case InfoType::SystemResourceSizeTotal:
|
||||||
*result = process->GetTotalSystemResourceSize();
|
*result = process->GetTotalSystemResourceSize();
|
||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
|
|
||||||
case InfoType::SystemResourceSizeUsed:
|
case InfoType::SystemResourceSizeUsed:
|
||||||
*result = process->GetUsedSystemResourceSize();
|
*result = process->GetUsedSystemResourceSize();
|
||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
|
|
||||||
case InfoType::ProgramId:
|
case InfoType::ProgramId:
|
||||||
*result = process->GetProgramId();
|
*result = process->GetProgramId();
|
||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
|
|
||||||
case InfoType::UserExceptionContextAddress:
|
case InfoType::UserExceptionContextAddress:
|
||||||
*result = GetInteger(process->GetProcessLocalRegionAddress());
|
*result = GetInteger(process->GetProcessLocalRegionAddress());
|
||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
|
|
||||||
case InfoType::TotalNonSystemMemorySize:
|
case InfoType::TotalNonSystemMemorySize:
|
||||||
*result = process->GetTotalNonSystemUserPhysicalMemorySize(system.Kernel());
|
*result = process->GetTotalNonSystemUserPhysicalMemorySize(system.Kernel());
|
||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
|
|
||||||
case InfoType::UsedNonSystemMemorySize:
|
case InfoType::UsedNonSystemMemorySize:
|
||||||
*result = process->GetUsedNonSystemUserPhysicalMemorySize(system.Kernel());
|
*result = process->GetUsedNonSystemUserPhysicalMemorySize(system.Kernel());
|
||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
|
|
||||||
case InfoType::IsApplication:
|
case InfoType::IsApplication:
|
||||||
*result = process->IsApplication();
|
*result = process->IsApplication();
|
||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
|
|
||||||
case InfoType::FreeThreadCount:
|
case InfoType::FreeThreadCount:
|
||||||
if (KResourceLimit* resource_limit = process->GetResourceLimit();
|
if (KResourceLimit* resource_limit = process->GetResourceLimit();
|
||||||
resource_limit != nullptr) {
|
resource_limit != nullptr) {
|
||||||
const auto current_value = resource_limit->GetCurrentValue(Svc::LimitableResource::ThreadCountMax);
|
const auto current_value =
|
||||||
const auto limit_value = resource_limit->GetLimitValue(Svc::LimitableResource::ThreadCountMax);
|
resource_limit->GetCurrentValue(Svc::LimitableResource::ThreadCountMax);
|
||||||
|
const auto limit_value =
|
||||||
|
resource_limit->GetLimitValue(Svc::LimitableResource::ThreadCountMax);
|
||||||
*result = limit_value - current_value;
|
*result = limit_value - current_value;
|
||||||
} else {
|
} else {
|
||||||
*result = 0;
|
*result = 0;
|
||||||
}
|
}
|
||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
|
|
||||||
case InfoType::AliasRegionExtraSize: {
|
case InfoType::AliasRegionExtraSize: {
|
||||||
R_UNLESS(info_sub_id == 0, ResultInvalidCombination);
|
if (info_sub_id != 0) {
|
||||||
|
return ResultInvalidCombination;
|
||||||
|
}
|
||||||
|
|
||||||
KProcess* current_process = GetCurrentProcessPointer(system.Kernel());
|
KProcess* current_process = GetCurrentProcessPointer(system.Kernel());
|
||||||
*result = current_process->GetPageTable().GetAliasRegionExtraSize();
|
*result = current_process->GetPageTable().GetAliasRegionExtraSize();
|
||||||
|
|
||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
}
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
LOG_ERROR(Kernel_SVC, "Unimplemented svcGetInfo id={:#016x}", info_id);
|
LOG_ERROR(Kernel_SVC, "Unimplemented svcGetInfo id={:#016x}", info_id);
|
||||||
R_THROW(ResultInvalidEnumValue);
|
R_THROW(ResultInvalidEnumValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
case InfoType::DebuggerAttached:
|
case InfoType::DebuggerAttached:
|
||||||
*result = 0;
|
*result = 0;
|
||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
|
|
||||||
case InfoType::ResourceLimit: {
|
case InfoType::ResourceLimit: {
|
||||||
R_UNLESS(handle == 0, ResultInvalidHandle);
|
R_UNLESS(handle == 0, ResultInvalidHandle);
|
||||||
R_UNLESS(info_sub_id == 0, ResultInvalidCombination);
|
R_UNLESS(info_sub_id == 0, ResultInvalidCombination);
|
||||||
|
|
||||||
KProcess* const current_process = GetCurrentProcessPointer(system.Kernel());
|
KProcess* const current_process = GetCurrentProcessPointer(system.Kernel());
|
||||||
KHandleTable& handle_table = current_process->GetHandleTable();
|
KHandleTable& handle_table = current_process->GetHandleTable();
|
||||||
if (auto const resource_limit = current_process->GetResourceLimit(); resource_limit) {
|
const auto resource_limit = current_process->GetResourceLimit();
|
||||||
Handle resource_handle{};
|
if (!resource_limit) {
|
||||||
R_TRY(handle_table.Add(system.Kernel(), std::addressof(resource_handle), resource_limit));
|
|
||||||
*result = resource_handle;
|
|
||||||
} else {
|
|
||||||
*result = Svc::InvalidHandle;
|
*result = Svc::InvalidHandle;
|
||||||
|
// Yes, the kernel considers this a successful operation.
|
||||||
|
R_SUCCEED();
|
||||||
}
|
}
|
||||||
// Yes, the kernel considers this a successful operation either way.
|
|
||||||
|
Handle resource_handle{};
|
||||||
|
R_TRY(handle_table.Add(system.Kernel(), std::addressof(resource_handle), resource_limit));
|
||||||
|
|
||||||
|
*result = resource_handle;
|
||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
}
|
}
|
||||||
|
|
||||||
case InfoType::RandomEntropy:
|
case InfoType::RandomEntropy:
|
||||||
R_UNLESS(handle == 0, ResultInvalidHandle);
|
R_UNLESS(handle == 0, ResultInvalidHandle);
|
||||||
R_UNLESS(info_sub_id < 4, ResultInvalidCombination);
|
R_UNLESS(info_sub_id < 4, ResultInvalidCombination);
|
||||||
|
|
||||||
*result = GetCurrentProcess(system.Kernel()).GetRandomEntropy(info_sub_id);
|
*result = GetCurrentProcess(system.Kernel()).GetRandomEntropy(info_sub_id);
|
||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
case InfoType::InitialProcessIdRange: {
|
|
||||||
LOG_WARNING(Kernel_SVC, "(STUBBED) Attempted to query privileged process id bounds, returned 0/64");
|
case InfoType::InitialProcessIdRange:
|
||||||
R_UNLESS(handle == InvalidHandle, ResultInvalidHandle);
|
LOG_WARNING(Kernel_SVC,
|
||||||
switch (InitialProcessIdRangeInfo(info_sub_id)) {
|
"(STUBBED) Attempted to query privileged process id bounds, returned 0");
|
||||||
case InitialProcessIdRangeInfo::Minimum:
|
*result = 0;
|
||||||
*result = 0; //todo
|
R_SUCCEED();
|
||||||
R_SUCCEED();
|
|
||||||
case InitialProcessIdRangeInfo::Maximum:
|
|
||||||
*result = 64; //todo
|
|
||||||
R_SUCCEED();
|
|
||||||
default:
|
|
||||||
R_THROW(ResultInvalidCombination);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case InfoType::ThreadTickCount: {
|
case InfoType::ThreadTickCount: {
|
||||||
constexpr u64 num_cpus = 4;
|
constexpr u64 num_cpus = 4;
|
||||||
if (info_sub_id != 0xFFFFFFFFFFFFFFFF && info_sub_id >= num_cpus) {
|
if (info_sub_id != 0xFFFFFFFFFFFFFFFF && info_sub_id >= num_cpus) {
|
||||||
LOG_ERROR(Kernel_SVC, "Core count is out of range, expected {} but got {}", num_cpus, info_sub_id);
|
LOG_ERROR(Kernel_SVC, "Core count is out of range, expected {} but got {}", num_cpus,
|
||||||
|
info_sub_id);
|
||||||
R_THROW(ResultInvalidCombination);
|
R_THROW(ResultInvalidCombination);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -175,7 +206,8 @@ Result GetInfo(Core::System& system, u64* result, InfoType info_id_type, Handle
|
|||||||
.GetHandleTable()
|
.GetHandleTable()
|
||||||
.GetObject<KThread>(system.Kernel(), Handle(handle));
|
.GetObject<KThread>(system.Kernel(), Handle(handle));
|
||||||
if (thread.IsNull()) {
|
if (thread.IsNull()) {
|
||||||
LOG_ERROR(Kernel_SVC, "Thread handle does not exist, handle={:#08x}", Handle(handle));
|
LOG_ERROR(Kernel_SVC, "Thread handle does not exist, handle={:#08x}",
|
||||||
|
static_cast<Handle>(handle));
|
||||||
R_THROW(ResultInvalidHandle);
|
R_THROW(ResultInvalidHandle);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -188,6 +220,7 @@ Result GetInfo(Core::System& system, u64* result, InfoType info_id_type, Handle
|
|||||||
u64 out_ticks = 0;
|
u64 out_ticks = 0;
|
||||||
if (same_thread && info_sub_id == 0xFFFFFFFFFFFFFFFF) {
|
if (same_thread && info_sub_id == 0xFFFFFFFFFFFFFFFF) {
|
||||||
const u64 thread_ticks = current_thread->GetCpuTime();
|
const u64 thread_ticks = current_thread->GetCpuTime();
|
||||||
|
|
||||||
out_ticks = thread_ticks + (core_timing.GetClockTicks() - prev_ctx_ticks);
|
out_ticks = thread_ticks + (core_timing.GetClockTicks() - prev_ctx_ticks);
|
||||||
} else if (same_thread && info_sub_id == system.Kernel().CurrentPhysicalCoreIndex()) {
|
} else if (same_thread && info_sub_id == system.Kernel().CurrentPhysicalCoreIndex()) {
|
||||||
out_ticks = core_timing.GetClockTicks() - prev_ctx_ticks;
|
out_ticks = core_timing.GetClockTicks() - prev_ctx_ticks;
|
||||||
@@ -197,40 +230,19 @@ Result GetInfo(Core::System& system, u64* result, InfoType info_id_type, Handle
|
|||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
}
|
}
|
||||||
case InfoType::IdleTickCount: {
|
case InfoType::IdleTickCount: {
|
||||||
// Verify the requested core is valid.
|
|
||||||
const bool core_valid =
|
|
||||||
(info_sub_id == 0xFFFFFFFFFFFFFFFF)
|
|
||||||
|| (info_sub_id == u64(system.Kernel().CurrentPhysicalCoreIndex()));
|
|
||||||
R_UNLESS(core_valid, ResultInvalidCombination);
|
|
||||||
|
|
||||||
// Verify the input handle is invalid.
|
// Verify the input handle is invalid.
|
||||||
R_UNLESS(handle == InvalidHandle, ResultInvalidHandle);
|
R_UNLESS(handle == InvalidHandle, ResultInvalidHandle);
|
||||||
|
|
||||||
|
// Verify the requested core is valid.
|
||||||
|
const bool core_valid =
|
||||||
|
(info_sub_id == 0xFFFFFFFFFFFFFFFF) ||
|
||||||
|
(info_sub_id == static_cast<u64>(system.Kernel().CurrentPhysicalCoreIndex()));
|
||||||
|
R_UNLESS(core_valid, ResultInvalidCombination);
|
||||||
|
|
||||||
// Get the idle tick count.
|
// Get the idle tick count.
|
||||||
*result = system.Kernel().CurrentScheduler()->GetIdleThread()->GetCpuTime();
|
*result = system.Kernel().CurrentScheduler()->GetIdleThread()->GetCpuTime();
|
||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
}
|
}
|
||||||
case InfoType::MesosphereMeta: {
|
|
||||||
enum MesosphereMetaInfo : u64 {
|
|
||||||
KernelVersion = 0,
|
|
||||||
IsKTraceEnabled = 1,
|
|
||||||
IsSingleStepEnabled = 2,
|
|
||||||
};
|
|
||||||
R_UNLESS(handle == InvalidHandle, ResultInvalidHandle);
|
|
||||||
switch (MesosphereMetaInfo(info_sub_id)) {
|
|
||||||
case MesosphereMetaInfo::KernelVersion:
|
|
||||||
*result = Kernel::Svc::SupportedKernelVersion;
|
|
||||||
R_SUCCEED();
|
|
||||||
case MesosphereMetaInfo::IsKTraceEnabled:
|
|
||||||
*result = 0;
|
|
||||||
R_SUCCEED();
|
|
||||||
case MesosphereMetaInfo::IsSingleStepEnabled:
|
|
||||||
*result = 0;
|
|
||||||
R_SUCCEED();
|
|
||||||
default:
|
|
||||||
R_THROW(ResultInvalidCombination);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case InfoType::MesosphereCurrentProcess: {
|
case InfoType::MesosphereCurrentProcess: {
|
||||||
// Verify the input handle is invalid.
|
// Verify the input handle is invalid.
|
||||||
R_UNLESS(handle == InvalidHandle, ResultInvalidHandle);
|
R_UNLESS(handle == InvalidHandle, ResultInvalidHandle);
|
||||||
@@ -243,11 +255,13 @@ Result GetInfo(Core::System& system, u64* result, InfoType info_id_type, Handle
|
|||||||
KHandleTable& handle_table = current_process->GetHandleTable();
|
KHandleTable& handle_table = current_process->GetHandleTable();
|
||||||
|
|
||||||
// Get a new handle for the current process.
|
// Get a new handle for the current process.
|
||||||
Handle tmp{};
|
Handle tmp;
|
||||||
R_TRY(handle_table.Add(system.Kernel(), std::addressof(tmp), current_process));
|
R_TRY(handle_table.Add(system.Kernel(), std::addressof(tmp), current_process));
|
||||||
|
|
||||||
// Set the output.
|
// Set the output.
|
||||||
*result = tmp;
|
*result = tmp;
|
||||||
|
|
||||||
|
// We succeeded.
|
||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
|
|||||||
@@ -1387,8 +1387,7 @@ public:
|
|||||||
{5, &ACC_U1::GetProfile, "GetProfile"},
|
{5, &ACC_U1::GetProfile, "GetProfile"},
|
||||||
{6, nullptr, "GetProfileDigest"},
|
{6, nullptr, "GetProfileDigest"},
|
||||||
{50, &ACC_U1::IsUserRegistrationRequestPermitted, "IsUserRegistrationRequestPermitted"},
|
{50, &ACC_U1::IsUserRegistrationRequestPermitted, "IsUserRegistrationRequestPermitted"},
|
||||||
{51, &ACC_U1::TrySelectUserWithoutInteractionDeprecated, "TrySelectUserWithoutInteractionDeprecated"},
|
{51, &ACC_U1::TrySelectUserWithoutInteraction, "TrySelectUserWithoutInteraction"},
|
||||||
{52, &ACC_U1::TrySelectUserWithoutInteraction, "TrySelectUserWithoutInteraction"},
|
|
||||||
{60, &ACC_U1::ListOpenContextStoredUsers, "ListOpenContextStoredUsers"},
|
{60, &ACC_U1::ListOpenContextStoredUsers, "ListOpenContextStoredUsers"},
|
||||||
{99, nullptr, "DebugActivateOpenContextRetention"},
|
{99, nullptr, "DebugActivateOpenContextRetention"},
|
||||||
{100, nullptr, "GetUserRegistrationNotifier"},
|
{100, nullptr, "GetUserRegistrationNotifier"},
|
||||||
|
|||||||
@@ -154,7 +154,6 @@ enum class AppletMessage : u32 {
|
|||||||
DetectLongPressingCaptureButton = 91,
|
DetectLongPressingCaptureButton = 91,
|
||||||
AlbumScreenShotTaken = 92,
|
AlbumScreenShotTaken = 92,
|
||||||
AlbumRecordingSaved = 93,
|
AlbumRecordingSaved = 93,
|
||||||
StartupLogoDisappeared = 95,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class LibraryAppletMode : u32 {
|
enum class LibraryAppletMode : u32 {
|
||||||
|
|||||||
@@ -91,7 +91,6 @@ struct Applet {
|
|||||||
// Common state
|
// Common state
|
||||||
bool sleep_lock_enabled{};
|
bool sleep_lock_enabled{};
|
||||||
bool vr_mode_enabled{};
|
bool vr_mode_enabled{};
|
||||||
bool vr_mode_enabled_3d{};
|
|
||||||
bool lcd_backlight_off_enabled{};
|
bool lcd_backlight_off_enabled{};
|
||||||
APM::CpuBoostMode boost_mode{};
|
APM::CpuBoostMode boost_mode{};
|
||||||
bool request_exit_to_library_applet_at_execute_next_program_enabled{};
|
bool request_exit_to_library_applet_at_execute_next_program_enabled{};
|
||||||
|
|||||||
@@ -73,16 +73,20 @@ Result DisplayLayerManager::CreateManagedDisplayLayer(u64* out_layer_id) {
|
|||||||
R_TRY(m_manager_display_service->CreateManagedLayer(
|
R_TRY(m_manager_display_service->CreateManagedLayer(
|
||||||
out_layer_id, 0, display_id, Service::AppletResourceUserId{m_process->GetProcessId()}));
|
out_layer_id, 0, display_id, Service::AppletResourceUserId{m_process->GetProcessId()}));
|
||||||
|
|
||||||
|
m_manager_display_service->SetLayerVisibility(m_visible, *out_layer_id);
|
||||||
|
(void)m_display_service->GetContainer()->SetLayerStackMask(*out_layer_id,
|
||||||
|
this->GetLayerStackMask());
|
||||||
|
|
||||||
if (m_applet_id != AppletId::Application) {
|
if (m_applet_id != AppletId::Application) {
|
||||||
(void)m_manager_display_service->SetLayerBlending(m_blending_enabled, *out_layer_id);
|
(void)m_manager_display_service->SetLayerBlending(m_blending_enabled, *out_layer_id);
|
||||||
if (m_applet_id == AppletId::OverlayDisplay) {
|
if (m_applet_id == AppletId::OverlayDisplay) {
|
||||||
(void)m_manager_display_service->SetLayerZIndex(-1, *out_layer_id);
|
(void)m_manager_display_service->SetLayerZIndex(Overlay, *out_layer_id);
|
||||||
(void)m_display_service->GetContainer()->SetLayerIsOverlay(*out_layer_id, true);
|
(void)m_display_service->GetContainer()->SetLayerIsOverlay(*out_layer_id, true);
|
||||||
} else {
|
} else {
|
||||||
(void)m_manager_display_service->SetLayerZIndex(1, *out_layer_id);
|
(void)m_manager_display_service->SetLayerZIndex(Foreground, *out_layer_id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
(void)m_display_service->GetContainer()->SetLayerZIndex(*out_layer_id, true);
|
|
||||||
m_managed_display_layers.emplace(*out_layer_id);
|
m_managed_display_layers.emplace(*out_layer_id);
|
||||||
|
|
||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
@@ -122,11 +126,12 @@ Result DisplayLayerManager::IsSystemBufferSharingEnabled() {
|
|||||||
|
|
||||||
// Ensure the overlay layer is visible
|
// Ensure the overlay layer is visible
|
||||||
m_manager_display_service->SetLayerVisibility(m_visible, m_system_shared_layer_id);
|
m_manager_display_service->SetLayerVisibility(m_visible, m_system_shared_layer_id);
|
||||||
|
(void)m_display_service->GetContainer()->SetLayerStackMask(m_system_shared_layer_id,
|
||||||
|
this->GetLayerStackMask());
|
||||||
m_manager_display_service->SetLayerBlending(m_blending_enabled, m_system_shared_layer_id);
|
m_manager_display_service->SetLayerBlending(m_blending_enabled, m_system_shared_layer_id);
|
||||||
s32 initial_z = 1;
|
s32 initial_z = Foreground;
|
||||||
(void)m_display_service->GetContainer()->SetLayerZIndex(m_system_shared_layer_id, true);
|
|
||||||
if (m_applet_id == AppletId::OverlayDisplay) {
|
if (m_applet_id == AppletId::OverlayDisplay) {
|
||||||
initial_z = -1;
|
initial_z = Overlay;
|
||||||
(void)m_display_service->GetContainer()->SetLayerIsOverlay(m_system_shared_layer_id, true);
|
(void)m_display_service->GetContainer()->SetLayerIsOverlay(m_system_shared_layer_id, true);
|
||||||
}
|
}
|
||||||
m_manager_display_service->SetLayerZIndex(initial_z, m_system_shared_layer_id);
|
m_manager_display_service->SetLayerZIndex(initial_z, m_system_shared_layer_id);
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||||
@@ -73,7 +73,8 @@ Result IAllSystemAppletProxiesService::OpenLibraryAppletProxy(
|
|||||||
|
|
||||||
Result IAllSystemAppletProxiesService::OpenOverlayAppletProxy(
|
Result IAllSystemAppletProxiesService::OpenOverlayAppletProxy(
|
||||||
Out<SharedPointer<IOverlayAppletProxy>> out_overlay_applet_proxy, ClientProcessId pid,
|
Out<SharedPointer<IOverlayAppletProxy>> out_overlay_applet_proxy, ClientProcessId pid,
|
||||||
InCopyHandle<Kernel::KProcess> process_handle) {
|
InCopyHandle<Kernel::KProcess> process_handle,
|
||||||
|
InLargeData<AppletAttribute, BufferAttr_HipcMapAlias> attribute) {
|
||||||
LOG_WARNING(Service_AM, "called");
|
LOG_WARNING(Service_AM, "called");
|
||||||
|
|
||||||
if (const auto applet = this->GetAppletFromProcessId(pid); applet) {
|
if (const auto applet = this->GetAppletFromProcessId(pid); applet) {
|
||||||
@@ -88,7 +89,8 @@ Result IAllSystemAppletProxiesService::OpenOverlayAppletProxy(
|
|||||||
|
|
||||||
Result IAllSystemAppletProxiesService::OpenSystemApplicationProxy(
|
Result IAllSystemAppletProxiesService::OpenSystemApplicationProxy(
|
||||||
Out<SharedPointer<IApplicationProxy>> out_system_application_proxy, ClientProcessId pid,
|
Out<SharedPointer<IApplicationProxy>> out_system_application_proxy, ClientProcessId pid,
|
||||||
InCopyHandle<Kernel::KProcess> process_handle) {
|
InCopyHandle<Kernel::KProcess> process_handle,
|
||||||
|
InLargeData<AppletAttribute, BufferAttr_HipcMapAlias> attribute) {
|
||||||
LOG_DEBUG(Service_AM, "called");
|
LOG_DEBUG(Service_AM, "called");
|
||||||
|
|
||||||
if (const auto applet = this->GetAppletFromProcessId(pid); applet) {
|
if (const auto applet = this->GetAppletFromProcessId(pid); applet) {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||||
@@ -36,13 +36,15 @@ private:
|
|||||||
InCopyHandle<Kernel::KProcess> process_handle,
|
InCopyHandle<Kernel::KProcess> process_handle,
|
||||||
InLargeData<AppletAttribute, BufferAttr_HipcMapAlias> attribute);
|
InLargeData<AppletAttribute, BufferAttr_HipcMapAlias> attribute);
|
||||||
Result OpenOverlayAppletProxy(Out<SharedPointer<IOverlayAppletProxy>> out_overlay_applet_proxy,
|
Result OpenOverlayAppletProxy(Out<SharedPointer<IOverlayAppletProxy>> out_overlay_applet_proxy,
|
||||||
ClientProcessId pid, InCopyHandle<Kernel::KProcess> process_handle);
|
ClientProcessId pid, InCopyHandle<Kernel::KProcess> process_handle,
|
||||||
|
InLargeData<AppletAttribute, BufferAttr_HipcMapAlias> attribute);
|
||||||
Result OpenLibraryAppletProxyOld(
|
Result OpenLibraryAppletProxyOld(
|
||||||
Out<SharedPointer<ILibraryAppletProxy>> out_library_applet_proxy, ClientProcessId pid,
|
Out<SharedPointer<ILibraryAppletProxy>> out_library_applet_proxy, ClientProcessId pid,
|
||||||
InCopyHandle<Kernel::KProcess> process_handle);
|
InCopyHandle<Kernel::KProcess> process_handle);
|
||||||
Result OpenSystemApplicationProxy(
|
Result OpenSystemApplicationProxy(
|
||||||
Out<SharedPointer<IApplicationProxy>> out_system_application_proxy, ClientProcessId pid,
|
Out<SharedPointer<IApplicationProxy>> out_system_application_proxy, ClientProcessId pid,
|
||||||
InCopyHandle<Kernel::KProcess> process_handle);
|
InCopyHandle<Kernel::KProcess> process_handle,
|
||||||
|
InLargeData<AppletAttribute, BufferAttr_HipcMapAlias> attribute);
|
||||||
Result GetSystemProcessCommonFunctions();
|
Result GetSystemProcessCommonFunctions();
|
||||||
Result GetAppletAlternativeFunctions();
|
Result GetAppletAlternativeFunctions();
|
||||||
|
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ ICommonStateGetter::ICommonStateGetter(Core::System& system_, std::shared_ptr<Ap
|
|||||||
{14, nullptr, "GetWakeupCount"}, //11.0.0+
|
{14, nullptr, "GetWakeupCount"}, //11.0.0+
|
||||||
{15, nullptr, "Unknown15"}, //19.0.0+
|
{15, nullptr, "Unknown15"}, //19.0.0+
|
||||||
{20, D<&ICommonStateGetter::PushToGeneralChannel>, "PushToGeneralChannel"},
|
{20, D<&ICommonStateGetter::PushToGeneralChannel>, "PushToGeneralChannel"},
|
||||||
{30, D<&ICommonStateGetter::GetHomeButtonReaderLockAccessor>, "GetHomeButtonReaderLockAccessor"},
|
{30, nullptr, "GetHomeButtonReaderLockAccessor"},
|
||||||
{31, D<&ICommonStateGetter::GetReaderLockAccessorEx>, "GetReaderLockAccessorEx"}, //2.0.0+
|
{31, D<&ICommonStateGetter::GetReaderLockAccessorEx>, "GetReaderLockAccessorEx"}, //2.0.0+
|
||||||
{32, D<&ICommonStateGetter::GetWriterLockAccessorEx>, "GetWriterLockAccessorEx"}, //7.0.0+
|
{32, D<&ICommonStateGetter::GetWriterLockAccessorEx>, "GetWriterLockAccessorEx"}, //7.0.0+
|
||||||
{40, nullptr, "GetCradleFwVersion"}, //2.0.0+
|
{40, nullptr, "GetCradleFwVersion"}, //2.0.0+
|
||||||
@@ -65,7 +65,7 @@ ICommonStateGetter::ICommonStateGetter(Core::System& system_, std::shared_ptr<Ap
|
|||||||
{100, D<&ICommonStateGetter::SetHandlingHomeButtonShortPressedEnabled>, "SetHandlingHomeButtonShortPressedEnabled"},
|
{100, D<&ICommonStateGetter::SetHandlingHomeButtonShortPressedEnabled>, "SetHandlingHomeButtonShortPressedEnabled"},
|
||||||
{110, nullptr, "OpenMyGpuErrorHandler"},
|
{110, nullptr, "OpenMyGpuErrorHandler"},
|
||||||
{120, D<&ICommonStateGetter::GetAppletLaunchedHistory>, "GetAppletLaunchedHistory"}, //13.0.0+
|
{120, D<&ICommonStateGetter::GetAppletLaunchedHistory>, "GetAppletLaunchedHistory"}, //13.0.0+
|
||||||
{130, D<&ICommonStateGetter::EnableStartupLogoDisappearedMessage>, "EnableStartupLogoDisappearedMessage"}, //21.0.0+
|
{130, nullptr, "Unknown130"}, //21.0.0+
|
||||||
{200, D<&ICommonStateGetter::GetOperationModeSystemInfo>, "GetOperationModeSystemInfo"},
|
{200, D<&ICommonStateGetter::GetOperationModeSystemInfo>, "GetOperationModeSystemInfo"},
|
||||||
{300, D<&ICommonStateGetter::GetSettingsPlatformRegion>, "GetSettingsPlatformRegion"},
|
{300, D<&ICommonStateGetter::GetSettingsPlatformRegion>, "GetSettingsPlatformRegion"},
|
||||||
{400, nullptr, "ActivateMigrationService"},
|
{400, nullptr, "ActivateMigrationService"},
|
||||||
@@ -74,17 +74,14 @@ ICommonStateGetter::ICommonStateGetter(Core::System& system_, std::shared_ptr<Ap
|
|||||||
{501, nullptr, "SuppressDisablingSleepTemporarily"},
|
{501, nullptr, "SuppressDisablingSleepTemporarily"},
|
||||||
{502, nullptr, "IsSleepEnabled"},
|
{502, nullptr, "IsSleepEnabled"},
|
||||||
{503, nullptr, "IsDisablingSleepSuppressed"},
|
{503, nullptr, "IsDisablingSleepSuppressed"},
|
||||||
{600, nullptr, "SetHidInputMagnificationForApplication"}, //20.0.0+
|
{600, nullptr, "Unknown600"}, //20.0.0+
|
||||||
{610, D<&ICommonStateGetter::Unknown610>, "Unknown610"}, //21.0.0+
|
{610, D<&ICommonStateGetter::Unknown610>, "Unknown610"}, //21.0.0+
|
||||||
{611, D<&ICommonStateGetter::Unknown611>, "Unknown611"}, //22.0.0+
|
{611, D<&ICommonStateGetter::Unknown611>, "Unknown611"}, //22.0.0+
|
||||||
{900, D<&ICommonStateGetter::SetRequestExitToLibraryAppletAtExecuteNextProgramEnabled>, "SetRequestExitToLibraryAppletAtExecuteNextProgramEnabled"}, //11.0.0+
|
{900, D<&ICommonStateGetter::SetRequestExitToLibraryAppletAtExecuteNextProgramEnabled>, "SetRequestExitToLibraryAppletAtExecuteNextProgramEnabled"}, //11.0.0+
|
||||||
{910, nullptr, "GetLaunchRequiredTick"}, //17.0.0+
|
{910, nullptr, "GetLaunchRequiredTick"}, //17.0.0+
|
||||||
{1000, D<&ICommonStateGetter::BeginVrMode3d>, "BeginVrMode3d"}, //19.0.0+
|
{1000, nullptr, "BeginVrMode3d"}, //19.0.0+
|
||||||
{1001, D<&ICommonStateGetter::EndVrMode3d>, "EndVrMode3d"}, //19.0.0+
|
{1001, nullptr, "EndVrMode3d"}, //19.0.0+
|
||||||
{1002, D<&ICommonStateGetter::IsVrModeEnabled3d>, "IsVrModeEnabled3d"}, //19.0.0+
|
{1002, nullptr, "IsVrModeEnabled3d"}, //19.0.0+
|
||||||
{1003, D<&ICommonStateGetter::GetVrLaboGoggleViewport>, "GetVrLaboGoggleViewport"}, //21.0.0+
|
|
||||||
{1004, D<&ICommonStateGetter::GetPanelPhysicalSizeForSpecificTitle>, "GetPanelPhysicalSizeForSpecificTitle"}, //21.0.0+
|
|
||||||
{1005, D<&ICommonStateGetter::GetPanelResolutionForSpecificTitle>, "GetPanelResolutionForSpecificTitle"}, //21.0.0+
|
|
||||||
};
|
};
|
||||||
// clang-format on
|
// clang-format on
|
||||||
|
|
||||||
@@ -314,12 +311,6 @@ Result ICommonStateGetter::PerformSystemButtonPressingIfInFocus(SystemButtonType
|
|||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
}
|
}
|
||||||
|
|
||||||
Result ICommonStateGetter::EnableStartupLogoDisappearedMessage() {
|
|
||||||
LOG_WARNING(Service_AM, "(STUBBED) called");
|
|
||||||
m_applet->lifecycle_manager.PushUnorderedMessage(system.Kernel(), AppletMessage::StartupLogoDisappeared);
|
|
||||||
R_SUCCEED();
|
|
||||||
}
|
|
||||||
|
|
||||||
Result ICommonStateGetter::GetOperationModeSystemInfo(Out<u32> out_operation_mode_system_info) {
|
Result ICommonStateGetter::GetOperationModeSystemInfo(Out<u32> out_operation_mode_system_info) {
|
||||||
LOG_WARNING(Service_AM, "(STUBBED) called");
|
LOG_WARNING(Service_AM, "(STUBBED) called");
|
||||||
*out_operation_mode_system_info = 0;
|
*out_operation_mode_system_info = 0;
|
||||||
@@ -364,12 +355,6 @@ Result ICommonStateGetter::PushToGeneralChannel(SharedPointer<IStorage> storage)
|
|||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
}
|
}
|
||||||
|
|
||||||
Result ICommonStateGetter::GetHomeButtonReaderLockAccessor(Out<SharedPointer<ILockAccessor>> out_lock_accessor) {
|
|
||||||
LOG_DEBUG(Service_AM, "called");
|
|
||||||
*out_lock_accessor = std::make_shared<ILockAccessor>(system);
|
|
||||||
R_SUCCEED();
|
|
||||||
}
|
|
||||||
|
|
||||||
Result ICommonStateGetter::SetHandlingHomeButtonShortPressedEnabled(bool enabled) {
|
Result ICommonStateGetter::SetHandlingHomeButtonShortPressedEnabled(bool enabled) {
|
||||||
LOG_DEBUG(Service_AM, "called, enabled={} applet_id={}", enabled, m_applet->applet_id);
|
LOG_DEBUG(Service_AM, "called, enabled={} applet_id={}", enabled, m_applet->applet_id);
|
||||||
|
|
||||||
@@ -378,58 +363,14 @@ Result ICommonStateGetter::SetHandlingHomeButtonShortPressedEnabled(bool enabled
|
|||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
}
|
}
|
||||||
|
|
||||||
Result ICommonStateGetter::Unknown610(u64 unk) {
|
Result ICommonStateGetter::Unknown610() {
|
||||||
LOG_WARNING(Service_AM, "(STUBBED) called");
|
LOG_WARNING(Service_AM, "(STUBBED) called");
|
||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
}
|
}
|
||||||
|
|
||||||
Result ICommonStateGetter::Unknown611(u8 unk) {
|
Result ICommonStateGetter::Unknown611() {
|
||||||
LOG_WARNING(Service_AM, "(STUBBED) called");
|
LOG_WARNING(Service_AM, "(STUBBED) called");
|
||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
}
|
}
|
||||||
|
|
||||||
Result ICommonStateGetter::BeginVrMode3d() {
|
|
||||||
std::scoped_lock lk{m_applet->lock};
|
|
||||||
m_applet->vr_mode_enabled_3d = true;
|
|
||||||
LOG_WARNING(Service_AM, "VR Mode is {}", m_applet->vr_mode_enabled_3d ? "on" : "off");
|
|
||||||
R_SUCCEED();
|
|
||||||
}
|
|
||||||
|
|
||||||
Result ICommonStateGetter::EndVrMode3d() {
|
|
||||||
std::scoped_lock lk{m_applet->lock};
|
|
||||||
m_applet->vr_mode_enabled_3d = false;
|
|
||||||
LOG_WARNING(Service_AM, "VR Mode is {}", m_applet->vr_mode_enabled_3d ? "on" : "off");
|
|
||||||
R_SUCCEED();
|
|
||||||
}
|
|
||||||
|
|
||||||
Result ICommonStateGetter::IsVrModeEnabled3d(Out<bool> out_is_vr_mode_enabled_3d) {
|
|
||||||
LOG_WARNING(Service_AM, "(STUBBED) called");
|
|
||||||
std::scoped_lock lk{m_applet->lock};
|
|
||||||
*out_is_vr_mode_enabled_3d = m_applet->vr_mode_enabled_3d;
|
|
||||||
R_SUCCEED();
|
|
||||||
}
|
|
||||||
|
|
||||||
Result ICommonStateGetter::GetVrLaboGoggleViewport(Out<s32> out_x, Out<s32> out_y, Out<s32> out_width, Out<s32> out_height) {
|
|
||||||
LOG_WARNING(Service_AM, "(STUBBED) called");
|
|
||||||
*out_x = 0;
|
|
||||||
*out_y = 0;
|
|
||||||
*out_width = 1280;
|
|
||||||
*out_height = 720;
|
|
||||||
R_SUCCEED();
|
|
||||||
}
|
|
||||||
|
|
||||||
Result ICommonStateGetter::GetPanelPhysicalSizeForSpecificTitle(Out<f32> out_width, Out<f32> out_height) {
|
|
||||||
LOG_WARNING(Service_AM, "(STUBBED) called");
|
|
||||||
*out_width = 137250.0f / 1000.0f;
|
|
||||||
*out_height = 77200.0f / 1000.0f;
|
|
||||||
R_SUCCEED();
|
|
||||||
}
|
|
||||||
|
|
||||||
Result ICommonStateGetter::GetPanelResolutionForSpecificTitle(Out<s32> out_width, Out<s32> out_height) {
|
|
||||||
LOG_WARNING(Service_AM, "(STUBBED) called");
|
|
||||||
*out_width = 1280;
|
|
||||||
*out_height = 720;
|
|
||||||
R_SUCCEED();
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace Service::AM
|
} // namespace Service::AM
|
||||||
|
|||||||
@@ -56,23 +56,15 @@ private:
|
|||||||
Result GetDefaultDisplayResolution(Out<s32> out_width, Out<s32> out_height);
|
Result GetDefaultDisplayResolution(Out<s32> out_width, Out<s32> out_height);
|
||||||
Result GetBuiltInDisplayType(Out<s32> out_display_type);
|
Result GetBuiltInDisplayType(Out<s32> out_display_type);
|
||||||
Result PerformSystemButtonPressingIfInFocus(SystemButtonType type);
|
Result PerformSystemButtonPressingIfInFocus(SystemButtonType type);
|
||||||
Result EnableStartupLogoDisappearedMessage();
|
|
||||||
Result GetOperationModeSystemInfo(Out<u32> out_operation_mode_system_info);
|
Result GetOperationModeSystemInfo(Out<u32> out_operation_mode_system_info);
|
||||||
Result GetAppletLaunchedHistory(Out<s32> out_count,
|
Result GetAppletLaunchedHistory(Out<s32> out_count,
|
||||||
OutArray<AppletId, BufferAttr_HipcMapAlias> out_applet_ids);
|
OutArray<AppletId, BufferAttr_HipcMapAlias> out_applet_ids);
|
||||||
Result GetSettingsPlatformRegion(Out<Set::PlatformRegion> out_settings_platform_region);
|
Result GetSettingsPlatformRegion(Out<Set::PlatformRegion> out_settings_platform_region);
|
||||||
Result SetRequestExitToLibraryAppletAtExecuteNextProgramEnabled();
|
Result SetRequestExitToLibraryAppletAtExecuteNextProgramEnabled();
|
||||||
Result PushToGeneralChannel(SharedPointer<IStorage> storage); // cmd 20
|
Result PushToGeneralChannel(SharedPointer<IStorage> storage); // cmd 20
|
||||||
Result GetHomeButtonReaderLockAccessor(Out<SharedPointer<ILockAccessor>> out_lock_accessor);
|
|
||||||
Result SetHandlingHomeButtonShortPressedEnabled(bool enabled);
|
Result SetHandlingHomeButtonShortPressedEnabled(bool enabled);
|
||||||
Result Unknown610(u64 unk);
|
Result Unknown610();
|
||||||
Result Unknown611(u8 unk);
|
Result Unknown611();
|
||||||
Result BeginVrMode3d();
|
|
||||||
Result EndVrMode3d();
|
|
||||||
Result IsVrModeEnabled3d(Out<bool> out_is_vr_mode_enabled_3d);
|
|
||||||
Result GetVrLaboGoggleViewport(Out<s32> out_x, Out<s32> out_y, Out<s32> out_width, Out<s32> out_height);
|
|
||||||
Result GetPanelPhysicalSizeForSpecificTitle(Out<f32> out_width, Out<f32> out_height);
|
|
||||||
Result GetPanelResolutionForSpecificTitle(Out<s32> out_width, Out<s32> out_height);
|
|
||||||
|
|
||||||
void SetCpuBoostMode(HLERequestContext& ctx);
|
void SetCpuBoostMode(HLERequestContext& ctx);
|
||||||
|
|
||||||
|
|||||||
@@ -798,7 +798,7 @@ void LoopProcess(Core::System& system) {
|
|||||||
const auto FileSystemProxyFactory = [&] { return std::make_shared<FSP_SRV>(system); };
|
const auto FileSystemProxyFactory = [&] { return std::make_shared<FSP_SRV>(system); };
|
||||||
|
|
||||||
server_manager->RegisterNamedService("fsp-ldr", std::make_shared<FSP_LDR>(system));
|
server_manager->RegisterNamedService("fsp-ldr", std::make_shared<FSP_LDR>(system));
|
||||||
server_manager->RegisterNamedService("fsp-pr", std::make_shared<FSP_PR>(system));
|
server_manager->RegisterNamedService("fsp:pr", std::make_shared<FSP_PR>(system));
|
||||||
server_manager->RegisterNamedService("fsp-srv", std::move(FileSystemProxyFactory));
|
server_manager->RegisterNamedService("fsp-srv", std::move(FileSystemProxyFactory));
|
||||||
ServerManager::RunServer(std::move(server_manager));
|
ServerManager::RunServer(std::move(server_manager));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ public:
|
|||||||
RegisterHandlers(functions);
|
RegisterHandlers(functions);
|
||||||
}
|
}
|
||||||
~I2CSession() override = default;
|
~I2CSession() override = default;
|
||||||
|
|
||||||
Result Send(InBuffer<BufferAttr_HipcMapAlias> in_data, u32 transaction_option) {
|
Result Send(InBuffer<BufferAttr_HipcMapAlias> in_data, u32 transaction_option) {
|
||||||
LOG_WARNING(Service, "(stubbed) topt={}", transaction_option);
|
LOG_WARNING(Service, "(stubbed) topt={}", transaction_option);
|
||||||
R_THROW(ResultUnknown);
|
R_THROW(ResultUnknown);
|
||||||
@@ -49,40 +50,21 @@ public:
|
|||||||
: ServiceFramework{system_, "i2c"}
|
: ServiceFramework{system_, "i2c"}
|
||||||
{
|
{
|
||||||
static const FunctionInfo functions[] = {
|
static const FunctionInfo functions[] = {
|
||||||
{0, C<&I2C::OpenSessionForDev>, "OpenSessionForDev"},
|
{0, nullptr, "OpenSessionForDev"},
|
||||||
{1, C<&I2C::OpenSession>, "OpenSession"},
|
{1, C<&I2C::OpenSession>, "OpenSession"},
|
||||||
{2, C<&I2C::HasDevice>, "HasDevice"},
|
{2, nullptr, "HasDevice"},
|
||||||
{3, C<&I2C::HasDeviceForDev>, "HasDeviceForDev"},
|
{3, nullptr, "HasDeviceForDev"},
|
||||||
{4, C<&I2C::OpenSession2>, "OpenSession2"},
|
{4, nullptr, "OpenSession2"},
|
||||||
};
|
};
|
||||||
RegisterHandlers(functions);
|
RegisterHandlers(functions);
|
||||||
}
|
}
|
||||||
~I2C() override = default;
|
~I2C() override = default;
|
||||||
Result OpenSessionForDev(OutInterface<I2CSession> out_session, s32 bus_idx, u32 slave_address, u32 addressing_mode, u32 speed_mode) {
|
|
||||||
|
Result OpenSession(I2CDevice device, OutInterface<I2CSession> out_session) {
|
||||||
LOG_DEBUG(Service, "(stubbed)");
|
LOG_DEBUG(Service, "(stubbed)");
|
||||||
*out_session = std::make_shared<I2CSession>(system);
|
*out_session = std::make_shared<I2CSession>(system);
|
||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
}
|
}
|
||||||
Result OpenSession(OutInterface<I2CSession> out_session, I2CDevice device) {
|
|
||||||
LOG_DEBUG(Service, "(stubbed)");
|
|
||||||
*out_session = std::make_shared<I2CSession>(system);
|
|
||||||
R_SUCCEED();
|
|
||||||
}
|
|
||||||
Result HasDevice(Out<bool> out_has_device, I2CDevice device) {
|
|
||||||
LOG_DEBUG(Service, "(stubbed)");
|
|
||||||
*out_has_device = false;
|
|
||||||
R_SUCCEED();
|
|
||||||
}
|
|
||||||
Result HasDeviceForDev(Out<bool> out_has_device, I2CDevice device) {
|
|
||||||
LOG_DEBUG(Service, "(stubbed)");
|
|
||||||
*out_has_device = false;
|
|
||||||
R_SUCCEED();
|
|
||||||
}
|
|
||||||
Result OpenSession2(OutInterface<I2CSession> out_session, u32 device_code) {
|
|
||||||
LOG_DEBUG(Service, "(stubbed) device_code={}", device_code);
|
|
||||||
*out_session = std::make_shared<I2CSession>(system);
|
|
||||||
R_SUCCEED();
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
void LoopProcess(Core::System& system) {
|
void LoopProcess(Core::System& system) {
|
||||||
|
|||||||
@@ -1,6 +1,3 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
@@ -33,15 +30,15 @@ struct DeviceSettings {
|
|||||||
INSERT_PADDING_BYTES(0x20); // Reserved
|
INSERT_PADDING_BYTES(0x20); // Reserved
|
||||||
|
|
||||||
// nn::settings::system::ConsoleSixAxisSensorAccelerationBias
|
// nn::settings::system::ConsoleSixAxisSensorAccelerationBias
|
||||||
Common::Vec<f32, 3> console_six_axis_sensor_acceleration_bias;
|
Common::Vec3<f32> console_six_axis_sensor_acceleration_bias;
|
||||||
// nn::settings::system::ConsoleSixAxisSensorAngularVelocityBias
|
// nn::settings::system::ConsoleSixAxisSensorAngularVelocityBias
|
||||||
Common::Vec<f32, 3> console_six_axis_sensor_angular_velocity_bias;
|
Common::Vec3<f32> console_six_axis_sensor_angular_velocity_bias;
|
||||||
// nn::settings::system::ConsoleSixAxisSensorAccelerationGain
|
// nn::settings::system::ConsoleSixAxisSensorAccelerationGain
|
||||||
std::array<u8, 0x24> console_six_axis_sensor_acceleration_gain;
|
std::array<u8, 0x24> console_six_axis_sensor_acceleration_gain;
|
||||||
// nn::settings::system::ConsoleSixAxisSensorAngularVelocityGain
|
// nn::settings::system::ConsoleSixAxisSensorAngularVelocityGain
|
||||||
std::array<u8, 0x24> console_six_axis_sensor_angular_velocity_gain;
|
std::array<u8, 0x24> console_six_axis_sensor_angular_velocity_gain;
|
||||||
// nn::settings::system::ConsoleSixAxisSensorAngularVelocityTimeBias
|
// nn::settings::system::ConsoleSixAxisSensorAngularVelocityTimeBias
|
||||||
Common::Vec<f32, 3> console_six_axis_sensor_angular_velocity_time_bias;
|
Common::Vec3<f32> console_six_axis_sensor_angular_velocity_time_bias;
|
||||||
// nn::settings::system::ConsoleSixAxisSensorAngularAcceleration
|
// nn::settings::system::ConsoleSixAxisSensorAngularAcceleration
|
||||||
std::array<u8, 0x24> console_six_axis_sensor_angular_acceleration;
|
std::array<u8, 0x24> console_six_axis_sensor_angular_acceleration;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||||
@@ -153,15 +153,15 @@ struct SystemSettings {
|
|||||||
INSERT_PADDING_BYTES(0x7FF8); // Reserved
|
INSERT_PADDING_BYTES(0x7FF8); // Reserved
|
||||||
|
|
||||||
// nn::settings::system::ConsoleSixAxisSensorAccelerationBias
|
// nn::settings::system::ConsoleSixAxisSensorAccelerationBias
|
||||||
Common::Vec<f32, 3> console_six_axis_sensor_acceleration_bias;
|
Common::Vec3<f32> console_six_axis_sensor_acceleration_bias;
|
||||||
// nn::settings::system::ConsoleSixAxisSensorAngularVelocityBias
|
// nn::settings::system::ConsoleSixAxisSensorAngularVelocityBias
|
||||||
Common::Vec<f32, 3> console_six_axis_sensor_angular_velocity_bias;
|
Common::Vec3<f32> console_six_axis_sensor_angular_velocity_bias;
|
||||||
// nn::settings::system::ConsoleSixAxisSensorAccelerationGain
|
// nn::settings::system::ConsoleSixAxisSensorAccelerationGain
|
||||||
std::array<u8, 0x24> console_six_axis_sensor_acceleration_gain;
|
std::array<u8, 0x24> console_six_axis_sensor_acceleration_gain;
|
||||||
// nn::settings::system::ConsoleSixAxisSensorAngularVelocityGain
|
// nn::settings::system::ConsoleSixAxisSensorAngularVelocityGain
|
||||||
std::array<u8, 0x24> console_six_axis_sensor_angular_velocity_gain;
|
std::array<u8, 0x24> console_six_axis_sensor_angular_velocity_gain;
|
||||||
// nn::settings::system::ConsoleSixAxisSensorAngularVelocityTimeBias
|
// nn::settings::system::ConsoleSixAxisSensorAngularVelocityTimeBias
|
||||||
Common::Vec<f32, 3> console_six_axis_sensor_angular_velocity_time_bias;
|
Common::Vec3<f32> console_six_axis_sensor_angular_velocity_time_bias;
|
||||||
// nn::settings::system::ConsoleSixAxisSensorAngularAcceleration
|
// nn::settings::system::ConsoleSixAxisSensorAngularAcceleration
|
||||||
std::array<u8, 0x24> console_six_axis_sensor_angular_velocity_acceleration;
|
std::array<u8, 0x24> console_six_axis_sensor_angular_velocity_acceleration;
|
||||||
INSERT_PADDING_BYTES(0x70); // Reserved
|
INSERT_PADDING_BYTES(0x70); // Reserved
|
||||||
|
|||||||
@@ -13,7 +13,6 @@
|
|||||||
#include "core/hle/service/vi/manager_display_service.h"
|
#include "core/hle/service/vi/manager_display_service.h"
|
||||||
#include "core/hle/service/vi/system_display_service.h"
|
#include "core/hle/service/vi/system_display_service.h"
|
||||||
#include "core/hle/service/vi/vi_results.h"
|
#include "core/hle/service/vi/vi_results.h"
|
||||||
#include "service_creator.h"
|
|
||||||
|
|
||||||
namespace Service::VI {
|
namespace Service::VI {
|
||||||
|
|
||||||
@@ -39,7 +38,6 @@ IApplicationDisplayService::IApplicationDisplayService(Core::System& system_,
|
|||||||
{2031, C<&IApplicationDisplayService::DestroyStrayLayer>, "DestroyStrayLayer"},
|
{2031, C<&IApplicationDisplayService::DestroyStrayLayer>, "DestroyStrayLayer"},
|
||||||
{2101, C<&IApplicationDisplayService::SetLayerScalingMode>, "SetLayerScalingMode"},
|
{2101, C<&IApplicationDisplayService::SetLayerScalingMode>, "SetLayerScalingMode"},
|
||||||
{2102, C<&IApplicationDisplayService::ConvertScalingMode>, "ConvertScalingMode"},
|
{2102, C<&IApplicationDisplayService::ConvertScalingMode>, "ConvertScalingMode"},
|
||||||
{2103, C<&IApplicationDisplayService::Cmd2103>, "Cmd2103"},
|
|
||||||
{2450, C<&IApplicationDisplayService::GetIndirectLayerImageMap>, "GetIndirectLayerImageMap"},
|
{2450, C<&IApplicationDisplayService::GetIndirectLayerImageMap>, "GetIndirectLayerImageMap"},
|
||||||
{2451, nullptr, "GetIndirectLayerImageCropMap"},
|
{2451, nullptr, "GetIndirectLayerImageCropMap"},
|
||||||
{2460, C<&IApplicationDisplayService::GetIndirectLayerImageRequiredMemoryInfo>, "GetIndirectLayerImageRequiredMemoryInfo"},
|
{2460, C<&IApplicationDisplayService::GetIndirectLayerImageRequiredMemoryInfo>, "GetIndirectLayerImageRequiredMemoryInfo"},
|
||||||
@@ -291,11 +289,6 @@ Result IApplicationDisplayService::ConvertScalingMode(Out<ConvertedScaleMode> ou
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Result IApplicationDisplayService::Cmd2103(Out<std::array<u8, 0x18>> out_unk18) {
|
|
||||||
LOG_WARNING(Service_VI, "(stubbed)");
|
|
||||||
R_SUCCEED();
|
|
||||||
}
|
|
||||||
|
|
||||||
Result IApplicationDisplayService::GetIndirectLayerImageMap(
|
Result IApplicationDisplayService::GetIndirectLayerImageMap(
|
||||||
Out<u64> out_size, Out<u64> out_stride,
|
Out<u64> out_size, Out<u64> out_stride,
|
||||||
OutBuffer<BufferAttr_HipcMapTransferAllowsNonSecure | BufferAttr_HipcMapAlias> out_buffer,
|
OutBuffer<BufferAttr_HipcMapTransferAllowsNonSecure | BufferAttr_HipcMapAlias> out_buffer,
|
||||||
|
|||||||
@@ -64,7 +64,6 @@ public:
|
|||||||
Result GetDisplayVsyncEvent(OutCopyHandle<Kernel::KReadableEvent> out_vsync_event,
|
Result GetDisplayVsyncEvent(OutCopyHandle<Kernel::KReadableEvent> out_vsync_event,
|
||||||
u64 display_id);
|
u64 display_id);
|
||||||
Result ConvertScalingMode(Out<ConvertedScaleMode> out_scaling_mode, NintendoScaleMode mode);
|
Result ConvertScalingMode(Out<ConvertedScaleMode> out_scaling_mode, NintendoScaleMode mode);
|
||||||
Result Cmd2103(Out<std::array<u8, 0x18>> out_unk18);
|
|
||||||
Result GetIndirectLayerImageMap(
|
Result GetIndirectLayerImageMap(
|
||||||
Out<u64> out_size, Out<u64> out_stride,
|
Out<u64> out_size, Out<u64> out_stride,
|
||||||
OutBuffer<BufferAttr_HipcMapTransferAllowsNonSecure | BufferAttr_HipcMapAlias> out_buffer,
|
OutBuffer<BufferAttr_HipcMapTransferAllowsNonSecure | BufferAttr_HipcMapAlias> out_buffer,
|
||||||
|
|||||||
@@ -300,6 +300,28 @@ Result SharedBufferManager::CreateSession(Kernel::KProcess* owner_process, u64*
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Claim a presentation slot range.
|
||||||
|
u32 slot_base = 0;
|
||||||
|
|
||||||
|
std::array<bool, SharedBufferMaxSessions> in_use{};
|
||||||
|
for (const auto& [existing_aruid, existing] : m_sessions) {
|
||||||
|
const u32 index = existing.presentation_slot_base / SharedBufferSlotsPerSession;
|
||||||
|
|
||||||
|
if (index < in_use.size())
|
||||||
|
in_use[index] = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 index = 0;
|
||||||
|
while (index < in_use.size() && in_use[index])
|
||||||
|
index++;
|
||||||
|
|
||||||
|
if (index >= in_use.size()) {
|
||||||
|
LOG_ERROR(Service_VI, "Out of shared buffer presentation slots ({} sessions)", SharedBufferMaxSessions);
|
||||||
|
R_THROW(VI::ResultOperationFailed);
|
||||||
|
}
|
||||||
|
|
||||||
|
slot_base = index * SharedBufferSlotsPerSession;
|
||||||
|
|
||||||
// Map into process.
|
// Map into process.
|
||||||
Common::ProcessAddress map_address{};
|
Common::ProcessAddress map_address{};
|
||||||
R_TRY(MapSharedBufferIntoProcessAddressSpace(std::addressof(map_address), m_buffer_page_group,
|
R_TRY(MapSharedBufferIntoProcessAddressSpace(std::addressof(map_address), m_buffer_page_group,
|
||||||
@@ -308,6 +330,7 @@ Result SharedBufferManager::CreateSession(Kernel::KProcess* owner_process, u64*
|
|||||||
// Create new session.
|
// Create new session.
|
||||||
auto [it, was_emplaced] = m_sessions.emplace(aruid, SharedBufferSession{});
|
auto [it, was_emplaced] = m_sessions.emplace(aruid, SharedBufferSession{});
|
||||||
auto& session = it->second;
|
auto& session = it->second;
|
||||||
|
session.presentation_slot_base = slot_base;
|
||||||
|
|
||||||
auto& container = m_nvdrv->GetContainer();
|
auto& container = m_nvdrv->GetContainer();
|
||||||
session.session_id = container.OpenSession(owner_process);
|
session.session_id = container.OpenSession(owner_process);
|
||||||
|
|||||||
@@ -299,17 +299,12 @@ void Config::ReadDataStorageValues() {
|
|||||||
void Config::ReadDebuggingValues() {
|
void Config::ReadDebuggingValues() {
|
||||||
BeginGroup(Settings::TranslateCategory(Settings::Category::Debugging));
|
BeginGroup(Settings::TranslateCategory(Settings::Category::Debugging));
|
||||||
|
|
||||||
if (global) {
|
// Intentionally not using the QT default setting as this is intended to be changed in the ini
|
||||||
// Intentionally not using the QT default setting as this is intended to be changed in the ini
|
Settings::values.record_frame_times =
|
||||||
Settings::values.record_frame_times =
|
ReadBooleanSetting(std::string("record_frame_times"), std::make_optional(false));
|
||||||
ReadBooleanSetting(std::string("record_frame_times"), std::make_optional(false));
|
|
||||||
|
|
||||||
ReadCategory(Settings::Category::Debugging);
|
ReadCategory(Settings::Category::Debugging);
|
||||||
ReadCategory(Settings::Category::DebuggingGraphics);
|
ReadCategory(Settings::Category::DebuggingGraphics);
|
||||||
} else {
|
|
||||||
ReadSettingGeneric(&Settings::values.program_args);
|
|
||||||
ReadSettingGeneric(&Settings::values.debug_knobs);
|
|
||||||
}
|
|
||||||
|
|
||||||
EndGroup();
|
EndGroup();
|
||||||
}
|
}
|
||||||
@@ -420,12 +415,12 @@ void Config::ReadLibraryAppletValues() {
|
|||||||
void Config::ReadValues() {
|
void Config::ReadValues() {
|
||||||
if (global) {
|
if (global) {
|
||||||
ReadDataStorageValues();
|
ReadDataStorageValues();
|
||||||
|
ReadDebuggingValues();
|
||||||
ReadDisabledAddOnValues();
|
ReadDisabledAddOnValues();
|
||||||
ReadServiceValues();
|
ReadServiceValues();
|
||||||
ReadWebServiceValues();
|
ReadWebServiceValues();
|
||||||
ReadMiscellaneousValues();
|
ReadMiscellaneousValues();
|
||||||
}
|
}
|
||||||
ReadDebuggingValues();
|
|
||||||
ReadLibraryAppletValues();
|
ReadLibraryAppletValues();
|
||||||
ReadNetworkValues();
|
ReadNetworkValues();
|
||||||
ReadControlValues();
|
ReadControlValues();
|
||||||
@@ -516,13 +511,13 @@ void Config::SaveValues() {
|
|||||||
if (global) {
|
if (global) {
|
||||||
LOG_DEBUG(Config, "Saving global generic configuration values");
|
LOG_DEBUG(Config, "Saving global generic configuration values");
|
||||||
SaveDataStorageValues();
|
SaveDataStorageValues();
|
||||||
|
SaveDebuggingValues();
|
||||||
SaveDisabledAddOnValues();
|
SaveDisabledAddOnValues();
|
||||||
SaveWebServiceValues();
|
SaveWebServiceValues();
|
||||||
SaveMiscellaneousValues();
|
SaveMiscellaneousValues();
|
||||||
} else {
|
} else {
|
||||||
LOG_DEBUG(Config, "Saving only generic configuration values");
|
LOG_DEBUG(Config, "Saving only generic configuration values");
|
||||||
}
|
}
|
||||||
SaveDebuggingValues();
|
|
||||||
SaveLibraryAppletValues();
|
SaveLibraryAppletValues();
|
||||||
SaveNetworkValues();
|
SaveNetworkValues();
|
||||||
SaveControlValues();
|
SaveControlValues();
|
||||||
@@ -605,16 +600,11 @@ void Config::SaveDataStorageValues() {
|
|||||||
void Config::SaveDebuggingValues() {
|
void Config::SaveDebuggingValues() {
|
||||||
BeginGroup(Settings::TranslateCategory(Settings::Category::Debugging));
|
BeginGroup(Settings::TranslateCategory(Settings::Category::Debugging));
|
||||||
|
|
||||||
if (global) {
|
// Intentionally not using the QT default setting as this is intended to be changed in the ini
|
||||||
// Intentionally not using the QT default setting as this is intended to be changed in the ini
|
WriteBooleanSetting(std::string("record_frame_times"), Settings::values.record_frame_times);
|
||||||
WriteBooleanSetting(std::string("record_frame_times"), Settings::values.record_frame_times);
|
|
||||||
|
|
||||||
WriteCategory(Settings::Category::Debugging);
|
WriteCategory(Settings::Category::Debugging);
|
||||||
WriteCategory(Settings::Category::DebuggingGraphics);
|
WriteCategory(Settings::Category::DebuggingGraphics);
|
||||||
} else {
|
|
||||||
WriteSettingGeneric(&Settings::values.program_args);
|
|
||||||
WriteSettingGeneric(&Settings::values.debug_knobs);
|
|
||||||
}
|
|
||||||
|
|
||||||
EndGroup();
|
EndGroup();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -170,12 +170,12 @@ void EmulatedConsole::SetMotion(const Common::Input::CallbackStatus& callback) {
|
|||||||
auto& emulated = console.motion_values.emulated;
|
auto& emulated = console.motion_values.emulated;
|
||||||
|
|
||||||
raw_status = TransformToMotion(callback);
|
raw_status = TransformToMotion(callback);
|
||||||
emulated.SetAcceleration(Common::Vec<f32, 3>{
|
emulated.SetAcceleration(Common::Vec3f{
|
||||||
raw_status.accel.x.value,
|
raw_status.accel.x.value,
|
||||||
raw_status.accel.y.value,
|
raw_status.accel.y.value,
|
||||||
raw_status.accel.z.value,
|
raw_status.accel.z.value,
|
||||||
});
|
});
|
||||||
emulated.SetGyroscope(Common::Vec<f32, 3>{
|
emulated.SetGyroscope(Common::Vec3f{
|
||||||
raw_status.gyro.x.value,
|
raw_status.gyro.x.value,
|
||||||
raw_status.gyro.y.value,
|
raw_status.gyro.y.value,
|
||||||
raw_status.gyro.z.value,
|
raw_status.gyro.z.value,
|
||||||
|
|||||||
@@ -18,6 +18,7 @@
|
|||||||
#include "common/input.h"
|
#include "common/input.h"
|
||||||
#include "common/param_package.h"
|
#include "common/param_package.h"
|
||||||
#include "common/point.h"
|
#include "common/point.h"
|
||||||
|
#include "common/quaternion.h"
|
||||||
#include "common/vector_math.h"
|
#include "common/vector_math.h"
|
||||||
#include "hid_core/frontend/motion_input.h"
|
#include "hid_core/frontend/motion_input.h"
|
||||||
#include "hid_core/hid_types.h"
|
#include "hid_core/hid_types.h"
|
||||||
@@ -42,12 +43,12 @@ using TouchValues = std::array<Common::Input::TouchStatus, MaxTouchDevices>;
|
|||||||
|
|
||||||
// Contains all motion related data that is used on the services
|
// Contains all motion related data that is used on the services
|
||||||
struct ConsoleMotion {
|
struct ConsoleMotion {
|
||||||
Common::Vec<f32, 3> accel{};
|
Common::Vec3f accel{};
|
||||||
Common::Vec<f32, 3> gyro{};
|
Common::Vec3f gyro{};
|
||||||
Common::Vec<f32, 3> rotation{};
|
Common::Vec3f rotation{};
|
||||||
std::array<Common::Vec<f32, 3>, 3> orientation{};
|
std::array<Common::Vec3f, 3> orientation{};
|
||||||
Common::Vec<f32, 4> quaternion{};
|
Common::Quaternion<f32> quaternion{};
|
||||||
Common::Vec<f32, 3> gyro_bias{};
|
Common::Vec3f gyro_bias{};
|
||||||
f32 verticalization_error{};
|
f32 verticalization_error{};
|
||||||
bool is_at_rest{};
|
bool is_at_rest{};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1051,12 +1051,12 @@ void EmulatedController::SetMotion(const Common::Input::CallbackStatus& callback
|
|||||||
auto& emulated = controller.motion_values[index].emulated;
|
auto& emulated = controller.motion_values[index].emulated;
|
||||||
|
|
||||||
raw_status = TransformToMotion(callback);
|
raw_status = TransformToMotion(callback);
|
||||||
emulated.SetAcceleration(Common::Vec<f32, 3>{
|
emulated.SetAcceleration(Common::Vec3f{
|
||||||
raw_status.accel.x.value,
|
raw_status.accel.x.value,
|
||||||
raw_status.accel.y.value,
|
raw_status.accel.y.value,
|
||||||
raw_status.accel.z.value,
|
raw_status.accel.z.value,
|
||||||
});
|
});
|
||||||
emulated.SetGyroscope(Common::Vec<f32, 3>{
|
emulated.SetGyroscope(Common::Vec3f{
|
||||||
raw_status.gyro.x.value,
|
raw_status.gyro.x.value,
|
||||||
raw_status.gyro.y.value,
|
raw_status.gyro.y.value,
|
||||||
raw_status.gyro.z.value,
|
raw_status.gyro.z.value,
|
||||||
|
|||||||
@@ -107,11 +107,11 @@ struct RingSensorForce {
|
|||||||
using NfcState = Common::Input::NfcStatus;
|
using NfcState = Common::Input::NfcStatus;
|
||||||
|
|
||||||
struct ControllerMotion {
|
struct ControllerMotion {
|
||||||
Common::Vec<f32, 3> accel{};
|
Common::Vec3f accel{};
|
||||||
Common::Vec<f32, 3> gyro{};
|
Common::Vec3f gyro{};
|
||||||
Common::Vec<f32, 3> rotation{};
|
Common::Vec3f rotation{};
|
||||||
Common::Vec<f32, 3> euler{};
|
Common::Vec3f euler{};
|
||||||
std::array<Common::Vec<f32, 3>, 3> orientation{};
|
std::array<Common::Vec3f, 3> orientation{};
|
||||||
bool is_at_rest{};
|
bool is_at_rest{};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -26,19 +26,20 @@ void MotionInput::SetPID(f32 new_kp, f32 new_ki, f32 new_kd) {
|
|||||||
kd = new_kd;
|
kd = new_kd;
|
||||||
}
|
}
|
||||||
|
|
||||||
void MotionInput::SetAcceleration(const Common::Vec<f32, 3>& acceleration) {
|
void MotionInput::SetAcceleration(const Common::Vec3f& acceleration) {
|
||||||
accel = acceleration;
|
accel = acceleration;
|
||||||
accel[0] = std::clamp(accel[0], -AccelMaxValue, AccelMaxValue);
|
|
||||||
accel[1] = std::clamp(accel[1], -AccelMaxValue, AccelMaxValue);
|
accel.x = std::clamp(accel.x, -AccelMaxValue, AccelMaxValue);
|
||||||
accel[2] = std::clamp(accel[2], -AccelMaxValue, AccelMaxValue);
|
accel.y = std::clamp(accel.y, -AccelMaxValue, AccelMaxValue);
|
||||||
|
accel.z = std::clamp(accel.z, -AccelMaxValue, AccelMaxValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MotionInput::SetGyroscope(const Common::Vec<f32, 3>& gyroscope) {
|
void MotionInput::SetGyroscope(const Common::Vec3f& gyroscope) {
|
||||||
gyro = gyroscope - gyro_bias;
|
gyro = gyroscope - gyro_bias;
|
||||||
|
|
||||||
gyro[0] = std::clamp(gyro[0], -GyroMaxValue, GyroMaxValue);
|
gyro.x = std::clamp(gyro.x, -GyroMaxValue, GyroMaxValue);
|
||||||
gyro[1] = std::clamp(gyro[1], -GyroMaxValue, GyroMaxValue);
|
gyro.y = std::clamp(gyro.y, -GyroMaxValue, GyroMaxValue);
|
||||||
gyro[2] = std::clamp(gyro[2], -GyroMaxValue, GyroMaxValue);
|
gyro.z = std::clamp(gyro.z, -GyroMaxValue, GyroMaxValue);
|
||||||
|
|
||||||
// Auto adjust gyro_bias to minimize drift
|
// Auto adjust gyro_bias to minimize drift
|
||||||
if (!IsMoving(IsAtRestRelaxed)) {
|
if (!IsMoving(IsAtRestRelaxed)) {
|
||||||
@@ -58,25 +59,25 @@ void MotionInput::SetGyroscope(const Common::Vec<f32, 3>& gyroscope) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void MotionInput::SetQuaternion(const Common::Vec<f32, 4>& quaternion) {
|
void MotionInput::SetQuaternion(const Common::Quaternion<f32>& quaternion) {
|
||||||
quat = quaternion;
|
quat = quaternion;
|
||||||
}
|
}
|
||||||
|
|
||||||
void MotionInput::SetEulerAngles(const Common::Vec<f32, 3>& euler_angles) {
|
void MotionInput::SetEulerAngles(const Common::Vec3f& euler_angles) {
|
||||||
const float cr = std::cos(euler_angles[0] * 0.5f);
|
const float cr = std::cos(euler_angles.x * 0.5f);
|
||||||
const float sr = std::sin(euler_angles[0] * 0.5f);
|
const float sr = std::sin(euler_angles.x * 0.5f);
|
||||||
const float cp = std::cos(euler_angles[1] * 0.5f);
|
const float cp = std::cos(euler_angles.y * 0.5f);
|
||||||
const float sp = std::sin(euler_angles[1] * 0.5f);
|
const float sp = std::sin(euler_angles.y * 0.5f);
|
||||||
const float cy = std::cos(euler_angles[2] * 0.5f);
|
const float cy = std::cos(euler_angles.z * 0.5f);
|
||||||
const float sy = std::sin(euler_angles[2] * 0.5f);
|
const float sy = std::sin(euler_angles.z * 0.5f);
|
||||||
|
|
||||||
quat[3] = cr * cp * cy + sr * sp * sy;
|
quat.w = cr * cp * cy + sr * sp * sy;
|
||||||
quat[0] = sr * cp * cy - cr * sp * sy;
|
quat.xyz.x = sr * cp * cy - cr * sp * sy;
|
||||||
quat[1] = cr * sp * cy + sr * cp * sy;
|
quat.xyz.y = cr * sp * cy + sr * cp * sy;
|
||||||
quat[2] = cr * cp * sy - sr * sp * cy;
|
quat.xyz.z = cr * cp * sy - sr * sp * cy;
|
||||||
}
|
}
|
||||||
|
|
||||||
void MotionInput::SetGyroBias(const Common::Vec<f32, 3>& bias) {
|
void MotionInput::SetGyroBias(const Common::Vec3f& bias) {
|
||||||
gyro_bias = bias;
|
gyro_bias = bias;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -97,7 +98,7 @@ void MotionInput::ResetRotations() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void MotionInput::ResetQuaternion() {
|
void MotionInput::ResetQuaternion() {
|
||||||
quat = Common::Vec<f32, 4>{0.0f, 0.0f, -1.0f, 0.0f};
|
quat = {{0.0f, 0.0f, -1.0f}, 0.0f};
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MotionInput::IsMoving(f32 sensitivity) const {
|
bool MotionInput::IsMoving(f32 sensitivity) const {
|
||||||
@@ -136,10 +137,10 @@ void MotionInput::UpdateOrientation(u64 elapsed_time) {
|
|||||||
ResetOrientation();
|
ResetOrientation();
|
||||||
}
|
}
|
||||||
// Short name local variable for readability
|
// Short name local variable for readability
|
||||||
f32 q1 = quat[3];
|
f32 q1 = quat.w;
|
||||||
f32 q2 = quat[0];
|
f32 q2 = quat.xyz[0];
|
||||||
f32 q3 = quat[1];
|
f32 q3 = quat.xyz[1];
|
||||||
f32 q4 = quat[2];
|
f32 q4 = quat.xyz[2];
|
||||||
const auto sample_period = static_cast<f32>(elapsed_time) / 1000000.0f;
|
const auto sample_period = static_cast<f32>(elapsed_time) / 1000000.0f;
|
||||||
|
|
||||||
// Ignore invalid elapsed time
|
// Ignore invalid elapsed time
|
||||||
@@ -149,23 +150,23 @@ void MotionInput::UpdateOrientation(u64 elapsed_time) {
|
|||||||
|
|
||||||
const auto normal_accel = accel.Normalized();
|
const auto normal_accel = accel.Normalized();
|
||||||
auto rad_gyro = gyro * std::numbers::pi_v<float> * 2.f;
|
auto rad_gyro = gyro * std::numbers::pi_v<float> * 2.f;
|
||||||
const f32 swap = rad_gyro[0];
|
const f32 swap = rad_gyro.x;
|
||||||
rad_gyro[0] = rad_gyro[1];
|
rad_gyro.x = rad_gyro.y;
|
||||||
rad_gyro[1] = -swap;
|
rad_gyro.y = -swap;
|
||||||
rad_gyro[2] = -rad_gyro[2];
|
rad_gyro.z = -rad_gyro.z;
|
||||||
|
|
||||||
// Clear gyro values if there is no gyro present
|
// Clear gyro values if there is no gyro present
|
||||||
if (only_accelerometer) {
|
if (only_accelerometer) {
|
||||||
rad_gyro[0] = 0;
|
rad_gyro.x = 0;
|
||||||
rad_gyro[1] = 0;
|
rad_gyro.y = 0;
|
||||||
rad_gyro[2] = 0;
|
rad_gyro.z = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ignore drift correction if acceleration is not reliable
|
// Ignore drift correction if acceleration is not reliable
|
||||||
if (accel.Length() >= 0.75f && accel.Length() <= 1.25f) {
|
if (accel.Length() >= 0.75f && accel.Length() <= 1.25f) {
|
||||||
const f32 ax = -normal_accel[0];
|
const f32 ax = -normal_accel.x;
|
||||||
const f32 ay = normal_accel[1];
|
const f32 ay = normal_accel.y;
|
||||||
const f32 az = -normal_accel[2];
|
const f32 az = -normal_accel.z;
|
||||||
|
|
||||||
// Estimated direction of gravity
|
// Estimated direction of gravity
|
||||||
const f32 vx = 2.0f * (q2 * q4 - q1 * q3);
|
const f32 vx = 2.0f * (q2 * q4 - q1 * q3);
|
||||||
@@ -173,7 +174,7 @@ void MotionInput::UpdateOrientation(u64 elapsed_time) {
|
|||||||
const f32 vz = q1 * q1 - q2 * q2 - q3 * q3 + q4 * q4;
|
const f32 vz = q1 * q1 - q2 * q2 - q3 * q3 + q4 * q4;
|
||||||
|
|
||||||
// Error is cross product between estimated direction and measured direction of gravity
|
// Error is cross product between estimated direction and measured direction of gravity
|
||||||
const Common::Vec<f32, 3> new_real_error{
|
const Common::Vec3f new_real_error = {
|
||||||
az * vx - ax * vz,
|
az * vx - ax * vz,
|
||||||
ay * vz - az * vy,
|
ay * vz - az * vy,
|
||||||
ax * vy - ay * vx,
|
ax * vy - ay * vx,
|
||||||
@@ -201,16 +202,16 @@ void MotionInput::UpdateOrientation(u64 elapsed_time) {
|
|||||||
rad_gyro += 10.0f * kd * derivative_error;
|
rad_gyro += 10.0f * kd * derivative_error;
|
||||||
|
|
||||||
// Emulate gyro values for games that need them
|
// Emulate gyro values for games that need them
|
||||||
gyro[0] = -rad_gyro[1];
|
gyro.x = -rad_gyro.y;
|
||||||
gyro[1] = rad_gyro[0];
|
gyro.y = rad_gyro.x;
|
||||||
gyro[2] = -rad_gyro[2];
|
gyro.z = -rad_gyro.z;
|
||||||
UpdateRotation(elapsed_time);
|
UpdateRotation(elapsed_time);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const f32 gx = rad_gyro[1];
|
const f32 gx = rad_gyro.y;
|
||||||
const f32 gy = rad_gyro[0];
|
const f32 gy = rad_gyro.x;
|
||||||
const f32 gz = rad_gyro[2];
|
const f32 gz = rad_gyro.z;
|
||||||
|
|
||||||
// Integrate rate of change of quaternion
|
// Integrate rate of change of quaternion
|
||||||
const f32 pa = q2;
|
const f32 pa = q2;
|
||||||
@@ -221,58 +222,57 @@ void MotionInput::UpdateOrientation(u64 elapsed_time) {
|
|||||||
q3 = pb + (q1 * gy - pa * gz + pc * gx) * (0.5f * sample_period);
|
q3 = pb + (q1 * gy - pa * gz + pc * gx) * (0.5f * sample_period);
|
||||||
q4 = pc + (q1 * gz + pa * gy - pb * gx) * (0.5f * sample_period);
|
q4 = pc + (q1 * gz + pa * gy - pb * gx) * (0.5f * sample_period);
|
||||||
|
|
||||||
quat[3] = q1;
|
quat.w = q1;
|
||||||
quat[0] = q2;
|
quat.xyz[0] = q2;
|
||||||
quat[1] = q3;
|
quat.xyz[1] = q3;
|
||||||
quat[2] = q4;
|
quat.xyz[2] = q4;
|
||||||
quat = quat.Normalized();
|
quat = quat.Normalized();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::array<Common::Vec<f32, 3>, 3> MotionInput::GetOrientation() const {
|
std::array<Common::Vec3f, 3> MotionInput::GetOrientation() const {
|
||||||
const Common::Vec<f32, 4> quad{
|
const Common::Quaternion<float> quad{
|
||||||
-quat[1],
|
.xyz = {-quat.xyz[1], -quat.xyz[0], -quat.w},
|
||||||
-quat[0],
|
.w = -quat.xyz[2],
|
||||||
-quat[3],
|
|
||||||
-quat[2],
|
|
||||||
};
|
};
|
||||||
const std::array<f32, 16> matrix4x4 = quad.ToMatrix();
|
const std::array<float, 16> matrix4x4 = quad.ToMatrix();
|
||||||
return {Common::Vec<f32, 3>(matrix4x4[0], matrix4x4[1], -matrix4x4[2]),
|
|
||||||
Common::Vec<f32, 3>(matrix4x4[4], matrix4x4[5], -matrix4x4[6]),
|
return {Common::Vec3f(matrix4x4[0], matrix4x4[1], -matrix4x4[2]),
|
||||||
Common::Vec<f32, 3>(-matrix4x4[8], -matrix4x4[9], matrix4x4[10])};
|
Common::Vec3f(matrix4x4[4], matrix4x4[5], -matrix4x4[6]),
|
||||||
|
Common::Vec3f(-matrix4x4[8], -matrix4x4[9], matrix4x4[10])};
|
||||||
}
|
}
|
||||||
|
|
||||||
Common::Vec<f32, 3> MotionInput::GetAcceleration() const {
|
Common::Vec3f MotionInput::GetAcceleration() const {
|
||||||
return accel;
|
return accel;
|
||||||
}
|
}
|
||||||
|
|
||||||
Common::Vec<f32, 3> MotionInput::GetGyroscope() const {
|
Common::Vec3f MotionInput::GetGyroscope() const {
|
||||||
return gyro;
|
return gyro;
|
||||||
}
|
}
|
||||||
|
|
||||||
Common::Vec<f32, 3> MotionInput::GetGyroBias() const {
|
Common::Vec3f MotionInput::GetGyroBias() const {
|
||||||
return gyro_bias;
|
return gyro_bias;
|
||||||
}
|
}
|
||||||
|
|
||||||
Common::Vec<f32, 4> MotionInput::GetQuaternion() const {
|
Common::Quaternion<f32> MotionInput::GetQuaternion() const {
|
||||||
return quat;
|
return quat;
|
||||||
}
|
}
|
||||||
|
|
||||||
Common::Vec<f32, 3> MotionInput::GetRotations() const {
|
Common::Vec3f MotionInput::GetRotations() const {
|
||||||
return rotations;
|
return rotations;
|
||||||
}
|
}
|
||||||
|
|
||||||
Common::Vec<f32, 3> MotionInput::GetEulerAngles() const {
|
Common::Vec3f MotionInput::GetEulerAngles() const {
|
||||||
// roll (x-axis rotation)
|
// roll (x-axis rotation)
|
||||||
const float sinr_cosp = 2 * (quat[3] * quat[0] + quat[1] * quat[2]);
|
const float sinr_cosp = 2 * (quat.w * quat.xyz.x + quat.xyz.y * quat.xyz.z);
|
||||||
const float cosr_cosp = 1 - 2 * (quat[0] * quat[0] + quat[1] * quat[1]);
|
const float cosr_cosp = 1 - 2 * (quat.xyz.x * quat.xyz.x + quat.xyz.y * quat.xyz.y);
|
||||||
|
|
||||||
// pitch (y-axis rotation)
|
// pitch (y-axis rotation)
|
||||||
const float sinp = std::sqrt(1 + 2 * (quat[3] * quat[1] - quat[0] * quat[2]));
|
const float sinp = std::sqrt(1 + 2 * (quat.w * quat.xyz.y - quat.xyz.x * quat.xyz.z));
|
||||||
const float cosp = std::sqrt(1 - 2 * (quat[3] * quat[1] - quat[0] * quat[2]));
|
const float cosp = std::sqrt(1 - 2 * (quat.w * quat.xyz.y - quat.xyz.x * quat.xyz.z));
|
||||||
|
|
||||||
// yaw (z-axis rotation)
|
// yaw (z-axis rotation)
|
||||||
const float siny_cosp = 2 * (quat[3] * quat[2] + quat[0] * quat[1]);
|
const float siny_cosp = 2 * (quat.w * quat.xyz.z + quat.xyz.x * quat.xyz.y);
|
||||||
const float cosy_cosp = 1 - 2 * (quat[1] * quat[1] + quat[2] * quat[2]);
|
const float cosy_cosp = 1 - 2 * (quat.xyz.y * quat.xyz.y + quat.xyz.z * quat.xyz.z);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
std::atan2(sinr_cosp, cosr_cosp),
|
std::atan2(sinr_cosp, cosr_cosp),
|
||||||
@@ -285,13 +285,13 @@ void MotionInput::ResetOrientation() {
|
|||||||
if (!reset_enabled || only_accelerometer) {
|
if (!reset_enabled || only_accelerometer) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!IsMoving(IsAtRestRelaxed) && accel[2] <= -0.9f) {
|
if (!IsMoving(IsAtRestRelaxed) && accel.z <= -0.9f) {
|
||||||
++reset_counter;
|
++reset_counter;
|
||||||
if (reset_counter > 900) {
|
if (reset_counter > 900) {
|
||||||
quat[3] = 0;
|
quat.w = 0;
|
||||||
quat[0] = 0;
|
quat.xyz[0] = 0;
|
||||||
quat[1] = 0;
|
quat.xyz[1] = 0;
|
||||||
quat[2] = -1;
|
quat.xyz[2] = -1;
|
||||||
SetOrientationFromAccelerometer();
|
SetOrientationFromAccelerometer();
|
||||||
integral_error = {};
|
integral_error = {};
|
||||||
reset_counter = 0;
|
reset_counter = 0;
|
||||||
@@ -309,15 +309,15 @@ void MotionInput::SetOrientationFromAccelerometer() {
|
|||||||
|
|
||||||
while (!IsCalibrated(0.01f) && ++iterations < 100) {
|
while (!IsCalibrated(0.01f) && ++iterations < 100) {
|
||||||
// Short name local variable for readability
|
// Short name local variable for readability
|
||||||
f32 q1 = quat[3];
|
f32 q1 = quat.w;
|
||||||
f32 q2 = quat[0];
|
f32 q2 = quat.xyz[0];
|
||||||
f32 q3 = quat[1];
|
f32 q3 = quat.xyz[1];
|
||||||
f32 q4 = quat[2];
|
f32 q4 = quat.xyz[2];
|
||||||
|
|
||||||
Common::Vec<f32, 3> rad_gyro;
|
Common::Vec3f rad_gyro;
|
||||||
const f32 ax = -normal_accel[0];
|
const f32 ax = -normal_accel.x;
|
||||||
const f32 ay = normal_accel[1];
|
const f32 ay = normal_accel.y;
|
||||||
const f32 az = -normal_accel[2];
|
const f32 az = -normal_accel.z;
|
||||||
|
|
||||||
// Estimated direction of gravity
|
// Estimated direction of gravity
|
||||||
const f32 vx = 2.0f * (q2 * q4 - q1 * q3);
|
const f32 vx = 2.0f * (q2 * q4 - q1 * q3);
|
||||||
@@ -325,7 +325,7 @@ void MotionInput::SetOrientationFromAccelerometer() {
|
|||||||
const f32 vz = q1 * q1 - q2 * q2 - q3 * q3 + q4 * q4;
|
const f32 vz = q1 * q1 - q2 * q2 - q3 * q3 + q4 * q4;
|
||||||
|
|
||||||
// Error is cross product between estimated direction and measured direction of gravity
|
// Error is cross product between estimated direction and measured direction of gravity
|
||||||
const Common::Vec<f32, 3> new_real_error = {
|
const Common::Vec3f new_real_error = {
|
||||||
az * vx - ax * vz,
|
az * vx - ax * vz,
|
||||||
ay * vz - az * vy,
|
ay * vz - az * vy,
|
||||||
ax * vy - ay * vx,
|
ax * vy - ay * vx,
|
||||||
@@ -338,9 +338,9 @@ void MotionInput::SetOrientationFromAccelerometer() {
|
|||||||
rad_gyro += 5.0f * ki * integral_error;
|
rad_gyro += 5.0f * ki * integral_error;
|
||||||
rad_gyro += 10.0f * kd * derivative_error;
|
rad_gyro += 10.0f * kd * derivative_error;
|
||||||
|
|
||||||
const f32 gx = rad_gyro[1];
|
const f32 gx = rad_gyro.y;
|
||||||
const f32 gy = rad_gyro[0];
|
const f32 gy = rad_gyro.x;
|
||||||
const f32 gz = rad_gyro[2];
|
const f32 gz = rad_gyro.z;
|
||||||
|
|
||||||
// Integrate rate of change of quaternion
|
// Integrate rate of change of quaternion
|
||||||
const f32 pa = q2;
|
const f32 pa = q2;
|
||||||
@@ -351,10 +351,10 @@ void MotionInput::SetOrientationFromAccelerometer() {
|
|||||||
q3 = pb + (q1 * gy - pa * gz + pc * gx) * (0.5f * sample_period);
|
q3 = pb + (q1 * gy - pa * gz + pc * gx) * (0.5f * sample_period);
|
||||||
q4 = pc + (q1 * gz + pa * gy - pb * gx) * (0.5f * sample_period);
|
q4 = pc + (q1 * gz + pa * gy - pb * gx) * (0.5f * sample_period);
|
||||||
|
|
||||||
quat[3] = q1;
|
quat.w = q1;
|
||||||
quat[0] = q2;
|
quat.xyz[0] = q2;
|
||||||
quat[1] = q3;
|
quat.xyz[1] = q3;
|
||||||
quat[2] = q4;
|
quat.xyz[2] = q4;
|
||||||
quat = quat.Normalized();
|
quat = quat.Normalized();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +1,10 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
|
#include "common/quaternion.h"
|
||||||
#include "common/vector_math.h"
|
#include "common/vector_math.h"
|
||||||
|
|
||||||
namespace Core::HID {
|
namespace Core::HID {
|
||||||
@@ -36,11 +34,11 @@ public:
|
|||||||
MotionInput& operator=(MotionInput&&) = default;
|
MotionInput& operator=(MotionInput&&) = default;
|
||||||
|
|
||||||
void SetPID(f32 new_kp, f32 new_ki, f32 new_kd);
|
void SetPID(f32 new_kp, f32 new_ki, f32 new_kd);
|
||||||
void SetAcceleration(const Common::Vec<f32, 3>& acceleration);
|
void SetAcceleration(const Common::Vec3f& acceleration);
|
||||||
void SetGyroscope(const Common::Vec<f32, 3>& gyroscope);
|
void SetGyroscope(const Common::Vec3f& gyroscope);
|
||||||
void SetQuaternion(const Common::Vec<f32, 4>& quaternion);
|
void SetQuaternion(const Common::Quaternion<f32>& quaternion);
|
||||||
void SetEulerAngles(const Common::Vec<f32, 3>& euler_angles);
|
void SetEulerAngles(const Common::Vec3f& euler_angles);
|
||||||
void SetGyroBias(const Common::Vec<f32, 3>& bias);
|
void SetGyroBias(const Common::Vec3f& bias);
|
||||||
void SetGyroThreshold(f32 threshold);
|
void SetGyroThreshold(f32 threshold);
|
||||||
|
|
||||||
/// Applies a modifier on top of the normal gyro threshold
|
/// Applies a modifier on top of the normal gyro threshold
|
||||||
@@ -55,13 +53,13 @@ public:
|
|||||||
|
|
||||||
void Calibrate();
|
void Calibrate();
|
||||||
|
|
||||||
[[nodiscard]] std::array<Common::Vec<f32, 3>, 3> GetOrientation() const;
|
[[nodiscard]] std::array<Common::Vec3f, 3> GetOrientation() const;
|
||||||
[[nodiscard]] Common::Vec<f32, 3> GetAcceleration() const;
|
[[nodiscard]] Common::Vec3f GetAcceleration() const;
|
||||||
[[nodiscard]] Common::Vec<f32, 3> GetGyroscope() const;
|
[[nodiscard]] Common::Vec3f GetGyroscope() const;
|
||||||
[[nodiscard]] Common::Vec<f32, 3> GetGyroBias() const;
|
[[nodiscard]] Common::Vec3f GetGyroBias() const;
|
||||||
[[nodiscard]] Common::Vec<f32, 3> GetRotations() const;
|
[[nodiscard]] Common::Vec3f GetRotations() const;
|
||||||
[[nodiscard]] Common::Vec<f32, 4> GetQuaternion() const;
|
[[nodiscard]] Common::Quaternion<f32> GetQuaternion() const;
|
||||||
[[nodiscard]] Common::Vec<f32, 3> GetEulerAngles() const;
|
[[nodiscard]] Common::Vec3f GetEulerAngles() const;
|
||||||
|
|
||||||
[[nodiscard]] bool IsMoving(f32 sensitivity) const;
|
[[nodiscard]] bool IsMoving(f32 sensitivity) const;
|
||||||
[[nodiscard]] bool IsCalibrated(f32 sensitivity) const;
|
[[nodiscard]] bool IsCalibrated(f32 sensitivity) const;
|
||||||
@@ -77,24 +75,24 @@ private:
|
|||||||
f32 kd;
|
f32 kd;
|
||||||
|
|
||||||
// PID errors
|
// PID errors
|
||||||
Common::Vec<f32, 3> real_error;
|
Common::Vec3f real_error;
|
||||||
Common::Vec<f32, 3> integral_error;
|
Common::Vec3f integral_error;
|
||||||
Common::Vec<f32, 3> derivative_error;
|
Common::Vec3f derivative_error;
|
||||||
|
|
||||||
// Quaternion containing the device orientation
|
// Quaternion containing the device orientation
|
||||||
Common::Vec<f32, 4> quat;
|
Common::Quaternion<f32> quat;
|
||||||
|
|
||||||
// Number of full rotations in each axis
|
// Number of full rotations in each axis
|
||||||
Common::Vec<f32, 3> rotations;
|
Common::Vec3f rotations;
|
||||||
|
|
||||||
// Acceleration vector measurement in G force
|
// Acceleration vector measurement in G force
|
||||||
Common::Vec<f32, 3> accel;
|
Common::Vec3f accel;
|
||||||
|
|
||||||
// Gyroscope vector measurement in radians/s.
|
// Gyroscope vector measurement in radians/s.
|
||||||
Common::Vec<f32, 3> gyro;
|
Common::Vec3f gyro;
|
||||||
|
|
||||||
// Vector to be subtracted from gyro measurements
|
// Vector to be subtracted from gyro measurements
|
||||||
Common::Vec<f32, 3> gyro_bias;
|
Common::Vec3f gyro_bias;
|
||||||
|
|
||||||
// Minimum gyro amplitude to detect if the device is moving
|
// Minimum gyro amplitude to detect if the device is moving
|
||||||
f32 gyro_threshold = 0.0f;
|
f32 gyro_threshold = 0.0f;
|
||||||
|
|||||||
@@ -609,10 +609,10 @@ static_assert(sizeof(SixAxisSensorAttribute) == 4, "SixAxisSensorAttribute is an
|
|||||||
struct SixAxisSensorState {
|
struct SixAxisSensorState {
|
||||||
s64 delta_time{};
|
s64 delta_time{};
|
||||||
s64 sampling_number{};
|
s64 sampling_number{};
|
||||||
Common::Vec<f32, 3> accel{};
|
Common::Vec3f accel{};
|
||||||
Common::Vec<f32, 3> gyro{};
|
Common::Vec3f gyro{};
|
||||||
Common::Vec<f32, 3> rotation{};
|
Common::Vec3f rotation{};
|
||||||
std::array<Common::Vec<f32, 3>, 3> orientation{};
|
std::array<Common::Vec3f, 3> orientation{};
|
||||||
SixAxisSensorAttribute attribute{};
|
SixAxisSensorAttribute attribute{};
|
||||||
INSERT_PADDING_BYTES(4); // Reserved
|
INSERT_PADDING_BYTES(4); // Reserved
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||||
@@ -196,7 +196,7 @@ struct ConsoleSixAxisSensorSharedMemoryFormat {
|
|||||||
bool is_seven_six_axis_sensor_at_rest{};
|
bool is_seven_six_axis_sensor_at_rest{};
|
||||||
INSERT_PADDING_BYTES(3); // padding
|
INSERT_PADDING_BYTES(3); // padding
|
||||||
f32 verticalization_error{};
|
f32 verticalization_error{};
|
||||||
Common::Vec<f32, 3> gyro_bias{};
|
Common::Vec3f gyro_bias{};
|
||||||
INSERT_PADDING_BYTES(4); // padding
|
INSERT_PADDING_BYTES(4); // padding
|
||||||
};
|
};
|
||||||
static_assert(sizeof(ConsoleSixAxisSensorSharedMemoryFormat) == 0x20,
|
static_assert(sizeof(ConsoleSixAxisSensorSharedMemoryFormat) == 0x20,
|
||||||
|
|||||||
@@ -46,11 +46,14 @@ void SevenSixAxis::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
|
|||||||
next_seven_sixaxis_state.accel = motion_status.accel;
|
next_seven_sixaxis_state.accel = motion_status.accel;
|
||||||
next_seven_sixaxis_state.gyro = motion_status.gyro;
|
next_seven_sixaxis_state.gyro = motion_status.gyro;
|
||||||
next_seven_sixaxis_state.quaternion = {
|
next_seven_sixaxis_state.quaternion = {
|
||||||
motion_status.quaternion[1],
|
{
|
||||||
motion_status.quaternion[0],
|
motion_status.quaternion.xyz.y,
|
||||||
-motion_status.quaternion[3],
|
motion_status.quaternion.xyz.x,
|
||||||
-motion_status.quaternion[2],
|
-motion_status.quaternion.w,
|
||||||
|
},
|
||||||
|
-motion_status.quaternion.xyz.z,
|
||||||
};
|
};
|
||||||
|
|
||||||
seven_sixaxis_lifo.WriteNextEntry(next_seven_sixaxis_state);
|
seven_sixaxis_lifo.WriteNextEntry(next_seven_sixaxis_state);
|
||||||
transfer_memory_owner->GetMemory().WriteBlock(transfer_memory, &seven_sixaxis_lifo,
|
transfer_memory_owner->GetMemory().WriteBlock(transfer_memory, &seven_sixaxis_lifo,
|
||||||
sizeof(seven_sixaxis_lifo));
|
sizeof(seven_sixaxis_lifo));
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
#include "common/vector_math.h"
|
#include "common/quaternion.h"
|
||||||
#include "common/typed_address.h"
|
#include "common/typed_address.h"
|
||||||
#include "hid_core/resources/controller_base.h"
|
#include "hid_core/resources/controller_base.h"
|
||||||
#include "hid_core/resources/ring_lifo.h"
|
#include "hid_core/resources/ring_lifo.h"
|
||||||
@@ -51,9 +51,9 @@ private:
|
|||||||
u64 timestamp{};
|
u64 timestamp{};
|
||||||
u64 sampling_number{};
|
u64 sampling_number{};
|
||||||
u64 unknown{};
|
u64 unknown{};
|
||||||
Common::Vec<f32, 3> accel{};
|
Common::Vec3f accel{};
|
||||||
Common::Vec<f32, 3> gyro{};
|
Common::Vec3f gyro{};
|
||||||
Common::Vec<f32, 4> quaternion{};
|
Common::Quaternion<f32> quaternion{};
|
||||||
};
|
};
|
||||||
static_assert(sizeof(SevenSixAxisState) == 0x48, "SevenSixAxisState is an invalid size");
|
static_assert(sizeof(SevenSixAxisState) == 0x48, "SevenSixAxisState is an invalid size");
|
||||||
|
|
||||||
|
|||||||
@@ -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-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
@@ -96,9 +93,9 @@ void SixAxis::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
|
|||||||
.accel = {0, 0, -1.0f},
|
.accel = {0, 0, -1.0f},
|
||||||
.orientation =
|
.orientation =
|
||||||
{
|
{
|
||||||
Common::Vec<f32, 3>{1.0f, 0, 0},
|
Common::Vec3f{1.0f, 0, 0},
|
||||||
Common::Vec<f32, 3>{0, 1.0f, 0},
|
Common::Vec3f{0, 1.0f, 0},
|
||||||
Common::Vec<f32, 3>{0, 0, 1.0f},
|
Common::Vec3f{0, 0, 1.0f},
|
||||||
},
|
},
|
||||||
.attribute = {1},
|
.attribute = {1},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -88,8 +88,8 @@ void Mouse::UpdateStickInput() {
|
|||||||
last_mouse_change *= maximum_stick_range;
|
last_mouse_change *= maximum_stick_range;
|
||||||
}
|
}
|
||||||
|
|
||||||
SetAxis(identifier, mouse_axis_x, last_mouse_change[0]);
|
SetAxis(identifier, mouse_axis_x, last_mouse_change.x);
|
||||||
SetAxis(identifier, mouse_axis_y, -last_mouse_change[1]);
|
SetAxis(identifier, mouse_axis_y, -last_mouse_change.y);
|
||||||
|
|
||||||
// Decay input over time
|
// Decay input over time
|
||||||
const float clamped_length = (std::min)(1.0f, length);
|
const float clamped_length = (std::min)(1.0f, length);
|
||||||
@@ -104,20 +104,20 @@ void Mouse::UpdateMotionInput() {
|
|||||||
const float sensitivity =
|
const float sensitivity =
|
||||||
IsMousePanningEnabled() ? default_motion_panning_sensitivity : default_motion_sensitivity;
|
IsMousePanningEnabled() ? default_motion_panning_sensitivity : default_motion_sensitivity;
|
||||||
|
|
||||||
const float rotation_velocity = std::sqrt(last_motion_change[0] * last_motion_change[0] +
|
const float rotation_velocity = std::sqrt(last_motion_change.x * last_motion_change.x +
|
||||||
last_motion_change[1] * last_motion_change[1]);
|
last_motion_change.y * last_motion_change.y);
|
||||||
|
|
||||||
// Clamp rotation speed
|
// Clamp rotation speed
|
||||||
if (rotation_velocity > maximum_rotation_speed / sensitivity) {
|
if (rotation_velocity > maximum_rotation_speed / sensitivity) {
|
||||||
const float multiplier = maximum_rotation_speed / rotation_velocity / sensitivity;
|
const float multiplier = maximum_rotation_speed / rotation_velocity / sensitivity;
|
||||||
last_motion_change[0] = last_motion_change[0] * multiplier;
|
last_motion_change.x = last_motion_change.x * multiplier;
|
||||||
last_motion_change[1] = last_motion_change[1] * multiplier;
|
last_motion_change.y = last_motion_change.y * multiplier;
|
||||||
}
|
}
|
||||||
|
|
||||||
const BasicMotion motion_data{
|
const BasicMotion motion_data{
|
||||||
.gyro_x = last_motion_change[0] * sensitivity,
|
.gyro_x = last_motion_change.x * sensitivity,
|
||||||
.gyro_y = last_motion_change[1] * sensitivity,
|
.gyro_y = last_motion_change.y * sensitivity,
|
||||||
.gyro_z = last_motion_change[2] * sensitivity,
|
.gyro_z = last_motion_change.z * sensitivity,
|
||||||
.accel_x = 0,
|
.accel_x = 0,
|
||||||
.accel_y = 0,
|
.accel_y = 0,
|
||||||
.accel_z = 0,
|
.accel_z = 0,
|
||||||
@@ -125,46 +125,53 @@ void Mouse::UpdateMotionInput() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
if (IsMousePanningEnabled()) {
|
if (IsMousePanningEnabled()) {
|
||||||
last_motion_change[0] = 0;
|
last_motion_change.x = 0;
|
||||||
last_motion_change[1] = 0;
|
last_motion_change.y = 0;
|
||||||
}
|
}
|
||||||
last_motion_change[2] = 0;
|
last_motion_change.z = 0;
|
||||||
|
|
||||||
SetMotion(motion_identifier, 0, motion_data);
|
SetMotion(motion_identifier, 0, motion_data);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Mouse::Move(int x, int y, int center_x, int center_y) {
|
void Mouse::Move(int x, int y, int center_x, int center_y) {
|
||||||
if (IsMousePanningEnabled()) {
|
if (IsMousePanningEnabled()) {
|
||||||
auto const mouse_change_int = Common::Vec<int, 2>(x, y) - Common::Vec<int, 2>(center_x, center_y);
|
const auto mouse_change =
|
||||||
auto const mouse_change = Common::Vec<float, 2>(float(mouse_change_int[0]), float(mouse_change_int[1]));
|
(Common::MakeVec(x, y) - Common::MakeVec(center_x, center_y)).Cast<float>();
|
||||||
auto const x_sensitivity = Settings::values.mouse_panning_x_sensitivity.GetValue() * default_panning_sensitivity;
|
const float x_sensitivity =
|
||||||
auto const y_sensitivity = Settings::values.mouse_panning_y_sensitivity.GetValue() * default_panning_sensitivity;
|
Settings::values.mouse_panning_x_sensitivity.GetValue() * default_panning_sensitivity;
|
||||||
auto const deadzone_cw = Settings::values.mouse_panning_deadzone_counterweight.GetValue() * default_deadzone_counterweight;
|
const float y_sensitivity =
|
||||||
last_motion_change += {-mouse_change[1] * x_sensitivity, -mouse_change[0] * y_sensitivity, 0};
|
Settings::values.mouse_panning_y_sensitivity.GetValue() * default_panning_sensitivity;
|
||||||
last_mouse_change[0] += mouse_change[0] * x_sensitivity;
|
const float deadzone_counterweight =
|
||||||
last_mouse_change[1] += mouse_change[1] * y_sensitivity;
|
Settings::values.mouse_panning_deadzone_counterweight.GetValue() *
|
||||||
// Bind the mouse change to [0 <= deadzone_cw <= 1.0]
|
default_deadzone_counterweight;
|
||||||
|
|
||||||
|
last_motion_change += {-mouse_change.y * x_sensitivity, -mouse_change.x * y_sensitivity, 0};
|
||||||
|
last_mouse_change.x += mouse_change.x * x_sensitivity;
|
||||||
|
last_mouse_change.y += mouse_change.y * y_sensitivity;
|
||||||
|
|
||||||
|
// Bind the mouse change to [0 <= deadzone_counterweight <= 1.0]
|
||||||
const float length = last_mouse_change.Length();
|
const float length = last_mouse_change.Length();
|
||||||
if (length < deadzone_cw && length != 0.0f) {
|
if (length < deadzone_counterweight && length != 0.0f) {
|
||||||
last_mouse_change /= length;
|
last_mouse_change /= length;
|
||||||
last_mouse_change *= deadzone_cw;
|
last_mouse_change *= deadzone_counterweight;
|
||||||
}
|
}
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (button_pressed) {
|
if (button_pressed) {
|
||||||
const auto mouse_move = Common::Vec<int, 2>(x, y) - mouse_origin;
|
const auto mouse_move = Common::MakeVec<int>(x, y) - mouse_origin;
|
||||||
const float x_sensitivity =
|
const float x_sensitivity =
|
||||||
Settings::values.mouse_panning_x_sensitivity.GetValue() * default_stick_sensitivity;
|
Settings::values.mouse_panning_x_sensitivity.GetValue() * default_stick_sensitivity;
|
||||||
const float y_sensitivity =
|
const float y_sensitivity =
|
||||||
Settings::values.mouse_panning_y_sensitivity.GetValue() * default_stick_sensitivity;
|
Settings::values.mouse_panning_y_sensitivity.GetValue() * default_stick_sensitivity;
|
||||||
SetAxis(identifier, mouse_axis_x, float(mouse_move[0]) * x_sensitivity);
|
SetAxis(identifier, mouse_axis_x, static_cast<float>(mouse_move.x) * x_sensitivity);
|
||||||
SetAxis(identifier, mouse_axis_y, float(-mouse_move[1]) * y_sensitivity);
|
SetAxis(identifier, mouse_axis_y, static_cast<float>(-mouse_move.y) * y_sensitivity);
|
||||||
|
|
||||||
last_motion_change = {
|
last_motion_change = {
|
||||||
float(-mouse_move[1]) * x_sensitivity,
|
static_cast<float>(-mouse_move.y) * x_sensitivity,
|
||||||
float(-mouse_move[0]) * y_sensitivity,
|
static_cast<float>(-mouse_move.x) * y_sensitivity,
|
||||||
last_motion_change[2],
|
last_motion_change.z,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -213,18 +220,18 @@ void Mouse::ReleaseButton(MouseButton button) {
|
|||||||
SetAxis(identifier, mouse_axis_y, 0);
|
SetAxis(identifier, mouse_axis_y, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
last_motion_change[0] = 0;
|
last_motion_change.x = 0;
|
||||||
last_motion_change[1] = 0;
|
last_motion_change.y = 0;
|
||||||
|
|
||||||
button_pressed = false;
|
button_pressed = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Mouse::MouseWheelChange(int x, int y) {
|
void Mouse::MouseWheelChange(int x, int y) {
|
||||||
wheel_position[0] += x;
|
wheel_position.x += x;
|
||||||
wheel_position[1] += y;
|
wheel_position.y += y;
|
||||||
last_motion_change[2] += static_cast<f32>(y);
|
last_motion_change.z += static_cast<f32>(y);
|
||||||
SetAxis(identifier, wheel_axis_x, static_cast<f32>(wheel_position[0]));
|
SetAxis(identifier, wheel_axis_x, static_cast<f32>(wheel_position.x));
|
||||||
SetAxis(identifier, wheel_axis_y, static_cast<f32>(wheel_position[1]));
|
SetAxis(identifier, wheel_axis_y, static_cast<f32>(wheel_position.y));
|
||||||
}
|
}
|
||||||
|
|
||||||
void Mouse::ReleaseAllButtons() {
|
void Mouse::ReleaseAllButtons() {
|
||||||
|
|||||||
@@ -107,11 +107,11 @@ private:
|
|||||||
|
|
||||||
Common::Input::ButtonNames GetUIButtonName(const Common::ParamPackage& params) const;
|
Common::Input::ButtonNames GetUIButtonName(const Common::ParamPackage& params) const;
|
||||||
|
|
||||||
Common::Vec<int, 2> mouse_origin;
|
Common::Vec2<int> mouse_origin;
|
||||||
Common::Vec<int, 2> last_mouse_position;
|
Common::Vec2<int> last_mouse_position;
|
||||||
Common::Vec<float, 2> last_mouse_change;
|
Common::Vec2<float> last_mouse_change;
|
||||||
Common::Vec<float, 3> last_motion_change;
|
Common::Vec3<float> last_motion_change;
|
||||||
Common::Vec<int, 2> wheel_position;
|
Common::Vec2<int> wheel_position;
|
||||||
bool button_pressed = false;
|
bool button_pressed = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -31,6 +31,3 @@ endif()
|
|||||||
if (SOLARIS)
|
if (SOLARIS)
|
||||||
target_link_libraries(network PRIVATE socket nsl)
|
target_link_libraries(network PRIVATE socket nsl)
|
||||||
endif()
|
endif()
|
||||||
if (MINGW)
|
|
||||||
target_link_libraries(network PRIVATE winmm)
|
|
||||||
endif()
|
|
||||||
|
|||||||
@@ -20,7 +20,6 @@ add_library(video_core STATIC
|
|||||||
buffer_cache/buffer_cache.h
|
buffer_cache/buffer_cache.h
|
||||||
buffer_cache/memory_tracker_base.h
|
buffer_cache/memory_tracker_base.h
|
||||||
buffer_cache/usage_tracker.h
|
buffer_cache/usage_tracker.h
|
||||||
buffer_cache/virtual_range_cache.h
|
|
||||||
buffer_cache/word_manager.h
|
buffer_cache/word_manager.h
|
||||||
cache_types.h
|
cache_types.h
|
||||||
capture.h
|
capture.h
|
||||||
@@ -62,6 +61,7 @@ add_library(video_core STATIC
|
|||||||
engines/puller.h
|
engines/puller.h
|
||||||
framebuffer_config.cpp
|
framebuffer_config.cpp
|
||||||
framebuffer_config.h
|
framebuffer_config.h
|
||||||
|
frame_gen/frame_gen_config.h
|
||||||
fsr.cpp
|
fsr.cpp
|
||||||
fsr.h
|
fsr.h
|
||||||
host1x/codecs/decoder.cpp
|
host1x/codecs/decoder.cpp
|
||||||
@@ -167,8 +167,6 @@ add_library(video_core STATIC
|
|||||||
renderer_vulkan/vk_fence_manager.h
|
renderer_vulkan/vk_fence_manager.h
|
||||||
renderer_vulkan/vk_graphics_pipeline.cpp
|
renderer_vulkan/vk_graphics_pipeline.cpp
|
||||||
renderer_vulkan/vk_graphics_pipeline.h
|
renderer_vulkan/vk_graphics_pipeline.h
|
||||||
renderer_vulkan/vk_multi_range_buffer.cpp
|
|
||||||
renderer_vulkan/vk_multi_range_buffer.h
|
|
||||||
renderer_vulkan/vk_master_semaphore.cpp
|
renderer_vulkan/vk_master_semaphore.cpp
|
||||||
renderer_vulkan/vk_master_semaphore.h
|
renderer_vulkan/vk_master_semaphore.h
|
||||||
renderer_vulkan/vk_pipeline_cache.cpp
|
renderer_vulkan/vk_pipeline_cache.cpp
|
||||||
|
|||||||
@@ -112,13 +112,6 @@ void BufferCache<P>::TickFrame() {
|
|||||||
async_buffers_death_ring.clear();
|
async_buffers_death_ring.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class P>
|
|
||||||
void BufferCache<P>::UnmapGPUMemory(size_t as_id, GPUVAddr gpu_addr, size_t size) {
|
|
||||||
if constexpr (requires { runtime.BindMultiRangeStorageBuffer(u64{}, bool{}); }) {
|
|
||||||
virtual_ranges.Unmap(as_id, gpu_addr, size);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class P>
|
template <class P>
|
||||||
void BufferCache<P>::WriteMemory(DAddr device_addr, u64 size) {
|
void BufferCache<P>::WriteMemory(DAddr device_addr, u64 size) {
|
||||||
if (memory_tracker.IsRegionGpuModified(device_addr, size)) {
|
if (memory_tracker.IsRegionGpuModified(device_addr, size)) {
|
||||||
@@ -215,8 +208,8 @@ bool BufferCache<P>::DMACopy(GPUVAddr src_address, GPUVAddr dest_address, u64 am
|
|||||||
BufferId buffer_b;
|
BufferId buffer_b;
|
||||||
do {
|
do {
|
||||||
channel_state->has_deleted_buffers = false;
|
channel_state->has_deleted_buffers = false;
|
||||||
buffer_a = FindBuffer(*cpu_src_address, static_cast<u32>(amount), false);
|
buffer_a = FindBuffer(*cpu_src_address, static_cast<u32>(amount));
|
||||||
buffer_b = FindBuffer(*cpu_dest_address, static_cast<u32>(amount), false);
|
buffer_b = FindBuffer(*cpu_dest_address, static_cast<u32>(amount));
|
||||||
} while (channel_state->has_deleted_buffers);
|
} while (channel_state->has_deleted_buffers);
|
||||||
auto& src_buffer = slot_buffers[buffer_a];
|
auto& src_buffer = slot_buffers[buffer_a];
|
||||||
auto& dest_buffer = slot_buffers[buffer_b];
|
auto& dest_buffer = slot_buffers[buffer_b];
|
||||||
@@ -272,7 +265,7 @@ bool BufferCache<P>::DMAClear(GPUVAddr dst_address, u64 amount, u32 value) {
|
|||||||
ClearDownload(*cpu_dst_address, size);
|
ClearDownload(*cpu_dst_address, size);
|
||||||
gpu_modified_ranges.Subtract(*cpu_dst_address, size);
|
gpu_modified_ranges.Subtract(*cpu_dst_address, size);
|
||||||
|
|
||||||
const BufferId buffer = FindBuffer(*cpu_dst_address, static_cast<u32>(size), false);
|
const BufferId buffer = FindBuffer(*cpu_dst_address, static_cast<u32>(size));
|
||||||
Buffer& dest_buffer = slot_buffers[buffer];
|
Buffer& dest_buffer = slot_buffers[buffer];
|
||||||
const u32 offset = dest_buffer.Offset(*cpu_dst_address);
|
const u32 offset = dest_buffer.Offset(*cpu_dst_address);
|
||||||
runtime.ClearBuffer(dest_buffer, offset, size, value);
|
runtime.ClearBuffer(dest_buffer, offset, size, value);
|
||||||
@@ -294,7 +287,7 @@ std::pair<typename P::Buffer*, u32> BufferCache<P>::ObtainBuffer(GPUVAddr gpu_ad
|
|||||||
template <class P>
|
template <class P>
|
||||||
std::pair<typename P::Buffer*, u32> BufferCache<P>::ObtainCPUBuffer(
|
std::pair<typename P::Buffer*, u32> BufferCache<P>::ObtainCPUBuffer(
|
||||||
DAddr device_addr, u32 size, ObtainBufferSynchronize sync_info, ObtainBufferOperation post_op) {
|
DAddr device_addr, u32 size, ObtainBufferSynchronize sync_info, ObtainBufferOperation post_op) {
|
||||||
const BufferId buffer_id = FindBuffer(device_addr, size, false);
|
const BufferId buffer_id = FindBuffer(device_addr, size);
|
||||||
Buffer& buffer = slot_buffers[buffer_id];
|
Buffer& buffer = slot_buffers[buffer_id];
|
||||||
|
|
||||||
// synchronize op
|
// synchronize op
|
||||||
@@ -1005,85 +998,11 @@ void BufferCache<P>::BindHostGraphicsUniformBuffer(size_t stage, u32 index, u32
|
|||||||
channel_state->fast_bound_uniform_buffers[stage] &= ~(1u << binding_index);
|
channel_state->fast_bound_uniform_buffers[stage] &= ~(1u << binding_index);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class P>
|
|
||||||
void BufferCache<P>::ResolveMultiRangeStorage(Binding& binding, bool is_written,
|
|
||||||
std::vector<MultiRangeSegment>& pool) {
|
|
||||||
binding.segment_first = 0;
|
|
||||||
binding.segment_count = 0;
|
|
||||||
if constexpr (requires { runtime.BindMultiRangeStorageBuffer(u64{}, bool{}); }) {
|
|
||||||
if (binding.gpu_addr == 0 || binding.size == 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (is_written && !runtime.PrefersSparseSources()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const VirtualSegments* found =
|
|
||||||
virtual_ranges.Query(*gpu_memory, binding.gpu_addr, binding.size);
|
|
||||||
if (!found || found->size() < 2) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const VirtualSegments segments = *found;
|
|
||||||
const u32 first = static_cast<u32>(pool.size());
|
|
||||||
const bool prefer_sparse = runtime.PrefersSparseSources();
|
|
||||||
for (const VirtualSegment& segment : segments) {
|
|
||||||
const BufferId buffer_id =
|
|
||||||
FindBuffer(segment.device_addr, segment.size, prefer_sparse);
|
|
||||||
if (!buffer_id) {
|
|
||||||
pool.resize(first);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
pool.push_back(MultiRangeSegment{
|
|
||||||
.buffer_id = buffer_id,
|
|
||||||
.device_addr = segment.device_addr,
|
|
||||||
.size = segment.size,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
binding.segment_first = first;
|
|
||||||
binding.segment_count = static_cast<u32>(segments.size());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class P>
|
|
||||||
bool BufferCache<P>::BindMultiRangeStorage(const Binding& binding, bool is_written,
|
|
||||||
std::span<const MultiRangeSegment> pool) {
|
|
||||||
if constexpr (requires { runtime.BindMultiRangeStorageBuffer(u64{}, bool{}); }) {
|
|
||||||
if (binding.segment_count < 2) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (binding.segment_first + binding.segment_count > pool.size()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
const u64 key = (static_cast<u64>(gpu_memory->GetID()) << 48) ^ binding.gpu_addr;
|
|
||||||
runtime.ResetMultiRange();
|
|
||||||
for (u32 index = 0; index < binding.segment_count; ++index) {
|
|
||||||
const MultiRangeSegment& segment = pool[binding.segment_first + index];
|
|
||||||
Buffer& buffer = slot_buffers[segment.buffer_id];
|
|
||||||
TouchBuffer(buffer, segment.buffer_id);
|
|
||||||
if (SynchronizeBuffer(buffer, segment.device_addr, segment.size)) {
|
|
||||||
runtime.InvalidateMultiRange(key);
|
|
||||||
}
|
|
||||||
const u32 offset = buffer.Offset(segment.device_addr);
|
|
||||||
buffer.MarkUsage(offset, segment.size);
|
|
||||||
if (is_written) {
|
|
||||||
MarkWrittenBuffer(segment.buffer_id, segment.device_addr, segment.size);
|
|
||||||
}
|
|
||||||
runtime.PushMultiRangeSource(buffer, offset, segment.size);
|
|
||||||
}
|
|
||||||
return runtime.BindMultiRangeStorageBuffer(key, is_written);
|
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class P>
|
template <class P>
|
||||||
void BufferCache<P>::BindHostGraphicsStorageBuffers(size_t stage) {
|
void BufferCache<P>::BindHostGraphicsStorageBuffers(size_t stage) {
|
||||||
u32 binding_index = 0;
|
u32 binding_index = 0;
|
||||||
ForEachEnabledBit(channel_state->enabled_storage_buffers[stage], [&](u32 index) {
|
ForEachEnabledBit(channel_state->enabled_storage_buffers[stage], [&](u32 index) {
|
||||||
const Binding& binding = channel_state->storage_buffers[stage][index];
|
const Binding& binding = channel_state->storage_buffers[stage][index];
|
||||||
const bool is_written = ((channel_state->written_storage_buffers[stage] >> index) & 1) != 0;
|
|
||||||
if (BindMultiRangeStorage(binding, is_written, graphics_segments)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
Buffer& buffer = slot_buffers[binding.buffer_id];
|
Buffer& buffer = slot_buffers[binding.buffer_id];
|
||||||
TouchBuffer(buffer, binding.buffer_id);
|
TouchBuffer(buffer, binding.buffer_id);
|
||||||
const u32 size = binding.size;
|
const u32 size = binding.size;
|
||||||
@@ -1091,6 +1010,7 @@ void BufferCache<P>::BindHostGraphicsStorageBuffers(size_t stage) {
|
|||||||
|
|
||||||
const u32 offset = buffer.Offset(binding.device_addr);
|
const u32 offset = buffer.Offset(binding.device_addr);
|
||||||
buffer.MarkUsage(offset, size);
|
buffer.MarkUsage(offset, size);
|
||||||
|
const bool is_written = ((channel_state->written_storage_buffers[stage] >> index) & 1) != 0;
|
||||||
|
|
||||||
if (is_written) {
|
if (is_written) {
|
||||||
MarkWrittenBuffer(binding.buffer_id, binding.device_addr, size);
|
MarkWrittenBuffer(binding.buffer_id, binding.device_addr, size);
|
||||||
@@ -1219,11 +1139,6 @@ void BufferCache<P>::BindHostComputeStorageBuffers() {
|
|||||||
u32 binding_index = 0;
|
u32 binding_index = 0;
|
||||||
ForEachEnabledBit(channel_state->enabled_compute_storage_buffers, [&](u32 index) {
|
ForEachEnabledBit(channel_state->enabled_compute_storage_buffers, [&](u32 index) {
|
||||||
const Binding& binding = channel_state->compute_storage_buffers[index];
|
const Binding& binding = channel_state->compute_storage_buffers[index];
|
||||||
const bool is_written =
|
|
||||||
((channel_state->written_compute_storage_buffers >> index) & 1) != 0;
|
|
||||||
if (BindMultiRangeStorage(binding, is_written, compute_segments)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
Buffer& buffer = slot_buffers[binding.buffer_id];
|
Buffer& buffer = slot_buffers[binding.buffer_id];
|
||||||
TouchBuffer(buffer, binding.buffer_id);
|
TouchBuffer(buffer, binding.buffer_id);
|
||||||
const u32 size = binding.size;
|
const u32 size = binding.size;
|
||||||
@@ -1231,6 +1146,8 @@ void BufferCache<P>::BindHostComputeStorageBuffers() {
|
|||||||
|
|
||||||
const u32 offset = buffer.Offset(binding.device_addr);
|
const u32 offset = buffer.Offset(binding.device_addr);
|
||||||
buffer.MarkUsage(offset, size);
|
buffer.MarkUsage(offset, size);
|
||||||
|
const bool is_written =
|
||||||
|
((channel_state->written_compute_storage_buffers >> index) & 1) != 0;
|
||||||
|
|
||||||
if (is_written) {
|
if (is_written) {
|
||||||
MarkWrittenBuffer(binding.buffer_id, binding.device_addr, size);
|
MarkWrittenBuffer(binding.buffer_id, binding.device_addr, size);
|
||||||
@@ -1276,7 +1193,6 @@ void BufferCache<P>::BindHostComputeTextureBuffers() {
|
|||||||
|
|
||||||
template <class P>
|
template <class P>
|
||||||
void BufferCache<P>::DoUpdateGraphicsBuffers(bool is_indexed) {
|
void BufferCache<P>::DoUpdateGraphicsBuffers(bool is_indexed) {
|
||||||
graphics_segments.clear();
|
|
||||||
BufferOperations([&]() {
|
BufferOperations([&]() {
|
||||||
if (is_indexed) {
|
if (is_indexed) {
|
||||||
UpdateIndexBuffer();
|
UpdateIndexBuffer();
|
||||||
@@ -1296,7 +1212,6 @@ void BufferCache<P>::DoUpdateGraphicsBuffers(bool is_indexed) {
|
|||||||
|
|
||||||
template <class P>
|
template <class P>
|
||||||
void BufferCache<P>::DoUpdateComputeBuffers() {
|
void BufferCache<P>::DoUpdateComputeBuffers() {
|
||||||
compute_segments.clear();
|
|
||||||
BufferOperations([&]() {
|
BufferOperations([&]() {
|
||||||
UpdateComputeUniformBuffers();
|
UpdateComputeUniformBuffers();
|
||||||
UpdateComputeStorageBuffers();
|
UpdateComputeStorageBuffers();
|
||||||
@@ -1319,11 +1234,11 @@ void BufferCache<P>::UpdateIndexBuffer() {
|
|||||||
auto inline_index_size = static_cast<u32>(draw_state.inline_index_draw_indexes.size());
|
auto inline_index_size = static_cast<u32>(draw_state.inline_index_draw_indexes.size());
|
||||||
u32 buffer_size = Common::AlignUp(inline_index_size, CACHING_PAGESIZE);
|
u32 buffer_size = Common::AlignUp(inline_index_size, CACHING_PAGESIZE);
|
||||||
if (inline_buffer_id == NULL_BUFFER_ID) [[unlikely]] {
|
if (inline_buffer_id == NULL_BUFFER_ID) [[unlikely]] {
|
||||||
inline_buffer_id = CreateBuffer(0, buffer_size, false);
|
inline_buffer_id = CreateBuffer(0, buffer_size);
|
||||||
}
|
}
|
||||||
if (slot_buffers[inline_buffer_id].SizeBytes() < buffer_size) [[unlikely]] {
|
if (slot_buffers[inline_buffer_id].SizeBytes() < buffer_size) [[unlikely]] {
|
||||||
slot_buffers.erase(inline_buffer_id);
|
slot_buffers.erase(inline_buffer_id);
|
||||||
inline_buffer_id = CreateBuffer(0, buffer_size, false);
|
inline_buffer_id = CreateBuffer(0, buffer_size);
|
||||||
}
|
}
|
||||||
channel_state->index_buffer = Binding{
|
channel_state->index_buffer = Binding{
|
||||||
.device_addr = 0,
|
.device_addr = 0,
|
||||||
@@ -1346,7 +1261,7 @@ void BufferCache<P>::UpdateIndexBuffer() {
|
|||||||
channel_state->index_buffer = Binding{
|
channel_state->index_buffer = Binding{
|
||||||
.device_addr = *device_addr,
|
.device_addr = *device_addr,
|
||||||
.size = size,
|
.size = size,
|
||||||
.buffer_id = FindBuffer(*device_addr, size, false),
|
.buffer_id = FindBuffer(*device_addr, size),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1383,7 +1298,7 @@ void BufferCache<P>::UpdateVertexBuffer(u32 index) {
|
|||||||
if (!gpu_memory->IsWithinGPUAddressRange(gpu_addr_end) || size >= 64_MiB) {
|
if (!gpu_memory->IsWithinGPUAddressRange(gpu_addr_end) || size >= 64_MiB) {
|
||||||
size = static_cast<u32>(gpu_memory->MaxContinuousRange(gpu_addr_begin, size));
|
size = static_cast<u32>(gpu_memory->MaxContinuousRange(gpu_addr_begin, size));
|
||||||
}
|
}
|
||||||
const BufferId buffer_id = FindBuffer(*device_addr, size, false);
|
const BufferId buffer_id = FindBuffer(*device_addr, size);
|
||||||
const Binding binding{
|
const Binding binding{
|
||||||
.device_addr = *device_addr,
|
.device_addr = *device_addr,
|
||||||
.size = size,
|
.size = size,
|
||||||
@@ -1404,7 +1319,7 @@ void BufferCache<P>::UpdateDrawIndirect() {
|
|||||||
binding = Binding{
|
binding = Binding{
|
||||||
.device_addr = *device_addr,
|
.device_addr = *device_addr,
|
||||||
.size = static_cast<u32>(size),
|
.size = static_cast<u32>(size),
|
||||||
.buffer_id = FindBuffer(*device_addr, static_cast<u32>(size), false),
|
.buffer_id = FindBuffer(*device_addr, static_cast<u32>(size)),
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
if (current_draw_indirect->include_count) {
|
if (current_draw_indirect->include_count) {
|
||||||
@@ -1428,7 +1343,7 @@ void BufferCache<P>::UpdateUniformBuffers(size_t stage) {
|
|||||||
channel_state->dirty_uniform_buffers[stage] |= 1U << index;
|
channel_state->dirty_uniform_buffers[stage] |= 1U << index;
|
||||||
}
|
}
|
||||||
// Resolve buffer
|
// Resolve buffer
|
||||||
binding.buffer_id = FindBuffer(binding.device_addr, binding.size, false);
|
binding.buffer_id = FindBuffer(binding.device_addr, binding.size);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1437,10 +1352,8 @@ void BufferCache<P>::UpdateStorageBuffers(size_t stage) {
|
|||||||
ForEachEnabledBit(channel_state->enabled_storage_buffers[stage], [&](u32 index) {
|
ForEachEnabledBit(channel_state->enabled_storage_buffers[stage], [&](u32 index) {
|
||||||
// Resolve buffer
|
// Resolve buffer
|
||||||
Binding& binding = channel_state->storage_buffers[stage][index];
|
Binding& binding = channel_state->storage_buffers[stage][index];
|
||||||
const BufferId buffer_id = FindBuffer(binding.device_addr, binding.size, false);
|
const BufferId buffer_id = FindBuffer(binding.device_addr, binding.size);
|
||||||
binding.buffer_id = buffer_id;
|
binding.buffer_id = buffer_id;
|
||||||
const bool is_written = ((channel_state->written_storage_buffers[stage] >> index) & 1) != 0;
|
|
||||||
ResolveMultiRangeStorage(binding, is_written, graphics_segments);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1448,7 +1361,7 @@ template <class P>
|
|||||||
void BufferCache<P>::UpdateTextureBuffers(size_t stage) {
|
void BufferCache<P>::UpdateTextureBuffers(size_t stage) {
|
||||||
ForEachEnabledBit(channel_state->enabled_texture_buffers[stage], [&](u32 index) {
|
ForEachEnabledBit(channel_state->enabled_texture_buffers[stage], [&](u32 index) {
|
||||||
Binding& binding = channel_state->texture_buffers[stage][index];
|
Binding& binding = channel_state->texture_buffers[stage][index];
|
||||||
binding.buffer_id = FindBuffer(binding.device_addr, binding.size, false);
|
binding.buffer_id = FindBuffer(binding.device_addr, binding.size);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1472,7 +1385,7 @@ void BufferCache<P>::UpdateTransformFeedbackBuffer(u32 index) {
|
|||||||
channel_state->transform_feedback_buffers[index] = NULL_BINDING;
|
channel_state->transform_feedback_buffers[index] = NULL_BINDING;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const BufferId buffer_id = FindBuffer(*device_addr, size, false);
|
const BufferId buffer_id = FindBuffer(*device_addr, size);
|
||||||
channel_state->transform_feedback_buffers[index] = Binding{
|
channel_state->transform_feedback_buffers[index] = Binding{
|
||||||
.device_addr = *device_addr,
|
.device_addr = *device_addr,
|
||||||
.size = size,
|
.size = size,
|
||||||
@@ -1494,7 +1407,7 @@ void BufferCache<P>::UpdateComputeUniformBuffers() {
|
|||||||
binding.size = cbuf.size;
|
binding.size = cbuf.size;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
binding.buffer_id = FindBuffer(binding.device_addr, binding.size, false);
|
binding.buffer_id = FindBuffer(binding.device_addr, binding.size);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1503,10 +1416,7 @@ void BufferCache<P>::UpdateComputeStorageBuffers() {
|
|||||||
ForEachEnabledBit(channel_state->enabled_compute_storage_buffers, [&](u32 index) {
|
ForEachEnabledBit(channel_state->enabled_compute_storage_buffers, [&](u32 index) {
|
||||||
// Resolve buffer
|
// Resolve buffer
|
||||||
Binding& binding = channel_state->compute_storage_buffers[index];
|
Binding& binding = channel_state->compute_storage_buffers[index];
|
||||||
binding.buffer_id = FindBuffer(binding.device_addr, binding.size, false);
|
binding.buffer_id = FindBuffer(binding.device_addr, binding.size);
|
||||||
const bool is_written =
|
|
||||||
((channel_state->written_compute_storage_buffers >> index) & 1) != 0;
|
|
||||||
ResolveMultiRangeStorage(binding, is_written, compute_segments);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1514,7 +1424,7 @@ template <class P>
|
|||||||
void BufferCache<P>::UpdateComputeTextureBuffers() {
|
void BufferCache<P>::UpdateComputeTextureBuffers() {
|
||||||
ForEachEnabledBit(channel_state->enabled_compute_texture_buffers, [&](u32 index) {
|
ForEachEnabledBit(channel_state->enabled_compute_texture_buffers, [&](u32 index) {
|
||||||
Binding& binding = channel_state->compute_texture_buffers[index];
|
Binding& binding = channel_state->compute_texture_buffers[index];
|
||||||
binding.buffer_id = FindBuffer(binding.device_addr, binding.size, false);
|
binding.buffer_id = FindBuffer(binding.device_addr, binding.size);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1530,7 +1440,7 @@ void BufferCache<P>::MarkWrittenBuffer(BufferId buffer_id, DAddr device_addr, u3
|
|||||||
}
|
}
|
||||||
|
|
||||||
template <class P>
|
template <class P>
|
||||||
BufferId BufferCache<P>::FindBuffer(DAddr device_addr, u32 size, bool sparse_compatible) {
|
BufferId BufferCache<P>::FindBuffer(DAddr device_addr, u32 size) {
|
||||||
if (device_addr == 0) {
|
if (device_addr == 0) {
|
||||||
return NULL_BUFFER_ID;
|
return NULL_BUFFER_ID;
|
||||||
}
|
}
|
||||||
@@ -1540,18 +1450,10 @@ BufferId BufferCache<P>::FindBuffer(DAddr device_addr, u32 size, bool sparse_com
|
|||||||
Buffer& buffer = slot_buffers[buffer_id];
|
Buffer& buffer = slot_buffers[buffer_id];
|
||||||
WaitForGpuFenceIfNeeded(buffer);
|
WaitForGpuFenceIfNeeded(buffer);
|
||||||
if (buffer.IsInBounds(device_addr, size)) {
|
if (buffer.IsInBounds(device_addr, size)) {
|
||||||
bool usable = true;
|
return buffer_id;
|
||||||
if constexpr (requires { buffer.IsSparseCompatible(); }) {
|
|
||||||
if (sparse_compatible && !buffer.IsSparseCompatible()) {
|
|
||||||
usable = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (usable) {
|
|
||||||
return buffer_id;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return CreateBuffer(device_addr, size, sparse_compatible);
|
return CreateBuffer(device_addr, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class P>
|
template <class P>
|
||||||
@@ -1673,15 +1575,13 @@ void BufferCache<P>::JoinOverlap(BufferId new_buffer_id, BufferId overlap_id,
|
|||||||
}
|
}
|
||||||
|
|
||||||
template <class P>
|
template <class P>
|
||||||
BufferId BufferCache<P>::CreateBuffer(DAddr device_addr, u32 wanted_size,
|
BufferId BufferCache<P>::CreateBuffer(DAddr device_addr, u32 wanted_size) {
|
||||||
bool sparse_compatible) {
|
|
||||||
DAddr device_addr_end = Common::AlignUp(device_addr + wanted_size, CACHING_PAGESIZE);
|
DAddr device_addr_end = Common::AlignUp(device_addr + wanted_size, CACHING_PAGESIZE);
|
||||||
device_addr = Common::AlignDown(device_addr, CACHING_PAGESIZE);
|
device_addr = Common::AlignDown(device_addr, CACHING_PAGESIZE);
|
||||||
wanted_size = static_cast<u32>(device_addr_end - device_addr);
|
wanted_size = static_cast<u32>(device_addr_end - device_addr);
|
||||||
const OverlapResult overlap = ResolveOverlaps(device_addr, wanted_size);
|
const OverlapResult overlap = ResolveOverlaps(device_addr, wanted_size);
|
||||||
const u32 size = static_cast<u32>(overlap.end - overlap.begin);
|
const u32 size = static_cast<u32>(overlap.end - overlap.begin);
|
||||||
const BufferId new_buffer_id =
|
const BufferId new_buffer_id = slot_buffers.insert(runtime, overlap.begin, size);
|
||||||
slot_buffers.insert(runtime, overlap.begin, size, sparse_compatible);
|
|
||||||
auto& new_buffer = slot_buffers[new_buffer_id];
|
auto& new_buffer = slot_buffers[new_buffer_id];
|
||||||
const size_t size_bytes = new_buffer.SizeBytes();
|
const size_t size_bytes = new_buffer.SizeBytes();
|
||||||
runtime.ClearBuffer(new_buffer, 0, size_bytes, 0);
|
runtime.ClearBuffer(new_buffer, 0, size_bytes, 0);
|
||||||
@@ -1845,7 +1745,7 @@ void BufferCache<P>::InlineMemoryImplementation(DAddr dest_address, size_t copy_
|
|||||||
ClearDownload(dest_address, copy_size);
|
ClearDownload(dest_address, copy_size);
|
||||||
gpu_modified_ranges.Subtract(dest_address, copy_size);
|
gpu_modified_ranges.Subtract(dest_address, copy_size);
|
||||||
|
|
||||||
BufferId buffer_id = FindBuffer(dest_address, static_cast<u32>(copy_size), false);
|
BufferId buffer_id = FindBuffer(dest_address, static_cast<u32>(copy_size));
|
||||||
auto& buffer = slot_buffers[buffer_id];
|
auto& buffer = slot_buffers[buffer_id];
|
||||||
SynchronizeBuffer(buffer, dest_address, static_cast<u32>(copy_size));
|
SynchronizeBuffer(buffer, dest_address, static_cast<u32>(copy_size));
|
||||||
|
|
||||||
@@ -1931,9 +1831,6 @@ void BufferCache<P>::DownloadBufferMemory(Buffer& buffer, DAddr device_addr, u64
|
|||||||
|
|
||||||
template <class P>
|
template <class P>
|
||||||
void BufferCache<P>::DeleteBuffer(BufferId buffer_id, bool do_not_mark) {
|
void BufferCache<P>::DeleteBuffer(BufferId buffer_id, bool do_not_mark) {
|
||||||
if constexpr (requires { runtime.OnBufferDeleted(slot_buffers[buffer_id]); }) {
|
|
||||||
runtime.OnBufferDeleted(slot_buffers[buffer_id]);
|
|
||||||
}
|
|
||||||
bool dirty_index{false};
|
bool dirty_index{false};
|
||||||
boost::container::small_vector<u64, NUM_VERTEX_BUFFERS> dirty_vertex_buffers;
|
boost::container::small_vector<u64, NUM_VERTEX_BUFFERS> dirty_vertex_buffers;
|
||||||
const auto scalar_replace = [buffer_id](Binding& binding) {
|
const auto scalar_replace = [buffer_id](Binding& binding) {
|
||||||
@@ -2037,14 +1934,9 @@ Binding BufferCache<P>::StorageBufferBinding(GPUVAddr ssbo_addr, u32 cbuf_index,
|
|||||||
// The end address used for size calculation does not need to be aligned
|
// The end address used for size calculation does not need to be aligned
|
||||||
const DAddr cpu_end = Common::AlignUp(*device_addr + size, Core::DEVICE_PAGESIZE);
|
const DAddr cpu_end = Common::AlignUp(*device_addr + size, Core::DEVICE_PAGESIZE);
|
||||||
|
|
||||||
u32 binding_size = static_cast<u32>(cpu_end - *aligned_device_addr);
|
|
||||||
if (is_written) {
|
|
||||||
binding_size = aligned_size;
|
|
||||||
}
|
|
||||||
const Binding binding{
|
const Binding binding{
|
||||||
.device_addr = *aligned_device_addr,
|
.device_addr = *aligned_device_addr,
|
||||||
.gpu_addr = aligned_gpu_addr,
|
.size = is_written ? aligned_size : static_cast<u32>(cpu_end - *aligned_device_addr),
|
||||||
.size = binding_size,
|
|
||||||
.buffer_id = BufferId{},
|
.buffer_id = BufferId{},
|
||||||
};
|
};
|
||||||
return binding;
|
return binding;
|
||||||
|
|||||||
@@ -29,7 +29,6 @@
|
|||||||
#include "common/settings.h"
|
#include "common/settings.h"
|
||||||
#include "common/slot_vector.h"
|
#include "common/slot_vector.h"
|
||||||
#include "video_core/buffer_cache/buffer_base.h"
|
#include "video_core/buffer_cache/buffer_base.h"
|
||||||
#include "video_core/buffer_cache/virtual_range_cache.h"
|
|
||||||
#include "video_core/control/channel_state_cache.h"
|
#include "video_core/control/channel_state_cache.h"
|
||||||
#include "video_core/delayed_destruction_ring.h"
|
#include "video_core/delayed_destruction_ring.h"
|
||||||
#include "video_core/dirty_flags.h"
|
#include "video_core/dirty_flags.h"
|
||||||
@@ -82,17 +81,8 @@ static constexpr u32 DEFAULT_SKIP_CACHE_SIZE = static_cast<u32>(4_KiB);
|
|||||||
|
|
||||||
struct Binding {
|
struct Binding {
|
||||||
DAddr device_addr{};
|
DAddr device_addr{};
|
||||||
GPUVAddr gpu_addr{};
|
|
||||||
u32 size{};
|
u32 size{};
|
||||||
BufferId buffer_id;
|
BufferId buffer_id;
|
||||||
u32 segment_first{};
|
|
||||||
u32 segment_count{};
|
|
||||||
};
|
|
||||||
|
|
||||||
struct MultiRangeSegment {
|
|
||||||
BufferId buffer_id;
|
|
||||||
DAddr device_addr{};
|
|
||||||
u32 size{};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct TextureBufferBinding : Binding {
|
struct TextureBufferBinding : Binding {
|
||||||
@@ -225,14 +215,6 @@ public:
|
|||||||
|
|
||||||
void TickFrame();
|
void TickFrame();
|
||||||
|
|
||||||
bool BindMultiRangeStorage(const Binding& binding, bool is_written,
|
|
||||||
std::span<const MultiRangeSegment> pool);
|
|
||||||
|
|
||||||
void ResolveMultiRangeStorage(Binding& binding, bool is_written,
|
|
||||||
std::vector<MultiRangeSegment>& pool);
|
|
||||||
|
|
||||||
void UnmapGPUMemory(size_t as_id, GPUVAddr gpu_addr, size_t size);
|
|
||||||
|
|
||||||
void WriteMemory(DAddr device_addr, u64 size);
|
void WriteMemory(DAddr device_addr, u64 size);
|
||||||
|
|
||||||
void CachedWriteMemory(DAddr device_addr, u64 size);
|
void CachedWriteMemory(DAddr device_addr, u64 size);
|
||||||
@@ -432,7 +414,7 @@ private:
|
|||||||
|
|
||||||
void MarkWrittenBuffer(BufferId buffer_id, DAddr device_addr, u32 size);
|
void MarkWrittenBuffer(BufferId buffer_id, DAddr device_addr, u32 size);
|
||||||
|
|
||||||
[[nodiscard]] BufferId FindBuffer(DAddr device_addr, u32 size, bool sparse_compatible);
|
[[nodiscard]] BufferId FindBuffer(DAddr device_addr, u32 size);
|
||||||
|
|
||||||
void WaitForGpuFenceIfNeeded(Buffer& buffer);
|
void WaitForGpuFenceIfNeeded(Buffer& buffer);
|
||||||
|
|
||||||
@@ -440,8 +422,7 @@ private:
|
|||||||
|
|
||||||
void JoinOverlap(BufferId new_buffer_id, BufferId overlap_id, bool accumulate_stream_score);
|
void JoinOverlap(BufferId new_buffer_id, BufferId overlap_id, bool accumulate_stream_score);
|
||||||
|
|
||||||
[[nodiscard]] BufferId CreateBuffer(DAddr device_addr, u32 wanted_size,
|
[[nodiscard]] BufferId CreateBuffer(DAddr device_addr, u32 wanted_size);
|
||||||
bool sparse_compatible);
|
|
||||||
|
|
||||||
void Register(BufferId buffer_id);
|
void Register(BufferId buffer_id);
|
||||||
|
|
||||||
@@ -532,9 +513,6 @@ private:
|
|||||||
using TickType = u64;
|
using TickType = u64;
|
||||||
};
|
};
|
||||||
Common::LeastRecentlyUsedCache<LRUItemParams> lru_cache;
|
Common::LeastRecentlyUsedCache<LRUItemParams> lru_cache;
|
||||||
VirtualRangeCache virtual_ranges;
|
|
||||||
std::vector<MultiRangeSegment> graphics_segments;
|
|
||||||
std::vector<MultiRangeSegment> compute_segments;
|
|
||||||
u64 frame_tick = 0;
|
u64 frame_tick = 0;
|
||||||
u64 total_used_memory = 0;
|
u64 total_used_memory = 0;
|
||||||
u64 minimum_memory = 0;
|
u64 minimum_memory = 0;
|
||||||
|
|||||||
@@ -1,173 +0,0 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <atomic>
|
|
||||||
#include <limits>
|
|
||||||
#include <mutex>
|
|
||||||
#include <optional>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include <boost/container/small_vector.hpp>
|
|
||||||
|
|
||||||
#include "common/common_types.h"
|
|
||||||
#include "common/container/unordered_map.h"
|
|
||||||
#include "video_core/memory_manager.h"
|
|
||||||
|
|
||||||
namespace VideoCommon {
|
|
||||||
|
|
||||||
struct VirtualSegment {
|
|
||||||
GPUVAddr gpu_addr;
|
|
||||||
DAddr device_addr;
|
|
||||||
u32 size;
|
|
||||||
};
|
|
||||||
|
|
||||||
using VirtualSegments = boost::container::small_vector<VirtualSegment, 8>;
|
|
||||||
|
|
||||||
class VirtualRangeCache {
|
|
||||||
public:
|
|
||||||
static constexpr size_t MAX_ENTRIES = 8192;
|
|
||||||
static constexpr size_t MAX_DEFERRED = 4096;
|
|
||||||
|
|
||||||
const VirtualSegments* Query(Tegra::MemoryManager& memory, GPUVAddr gpu_addr, u32 size) {
|
|
||||||
if (has_deferred.load(std::memory_order_acquire)) {
|
|
||||||
ApplyDeferred();
|
|
||||||
}
|
|
||||||
if (entries.size() > MAX_ENTRIES) {
|
|
||||||
entries.clear();
|
|
||||||
}
|
|
||||||
const size_t as_id = memory.GetID();
|
|
||||||
const u64 key = MakeKey(as_id, gpu_addr);
|
|
||||||
const auto it = entries.find(key);
|
|
||||||
if (it != entries.end() && it->second.as_id == as_id &&
|
|
||||||
it->second.gpu_addr == gpu_addr && it->second.size == size) {
|
|
||||||
return &it->second.segments;
|
|
||||||
}
|
|
||||||
Entry entry{};
|
|
||||||
entry.as_id = as_id;
|
|
||||||
entry.gpu_addr = gpu_addr;
|
|
||||||
entry.size = size;
|
|
||||||
const auto ranges = memory.GetSubmappedRange(gpu_addr, size);
|
|
||||||
GPUVAddr expected = gpu_addr;
|
|
||||||
bool contiguous = true;
|
|
||||||
for (const auto& [range_addr, range_size] : ranges) {
|
|
||||||
if (range_addr != expected || range_size == 0) {
|
|
||||||
contiguous = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
const std::optional<DAddr> device_addr = memory.GpuToCpuAddress(range_addr);
|
|
||||||
if (!device_addr || *device_addr == 0) {
|
|
||||||
contiguous = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (range_size > static_cast<size_t>((std::numeric_limits<u32>::max)())) {
|
|
||||||
contiguous = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
entry.segments.push_back(VirtualSegment{
|
|
||||||
.gpu_addr = range_addr,
|
|
||||||
.device_addr = *device_addr,
|
|
||||||
.size = static_cast<u32>(range_size),
|
|
||||||
});
|
|
||||||
expected += range_size;
|
|
||||||
}
|
|
||||||
if (!contiguous || expected != gpu_addr + size) {
|
|
||||||
entry.segments.clear();
|
|
||||||
}
|
|
||||||
const auto result = entries.insert_or_assign(key, std::move(entry));
|
|
||||||
return &result.first->second.segments;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Unmap(size_t as_id, GPUVAddr gpu_addr, u64 size) {
|
|
||||||
if (size == 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
{
|
|
||||||
std::scoped_lock lock{deferred_mutex};
|
|
||||||
if (!deferred.empty()) {
|
|
||||||
DeferredUnmap& last = deferred.back();
|
|
||||||
if (last.as_id == as_id && last.gpu_addr + last.size == gpu_addr) {
|
|
||||||
last.size += size;
|
|
||||||
has_deferred.store(true, std::memory_order_release);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (deferred.size() >= MAX_DEFERRED) {
|
|
||||||
deferred.clear();
|
|
||||||
deferred_overflow = true;
|
|
||||||
} else {
|
|
||||||
deferred.push_back(DeferredUnmap{
|
|
||||||
.as_id = as_id,
|
|
||||||
.gpu_addr = gpu_addr,
|
|
||||||
.size = size,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
has_deferred.store(true, std::memory_order_release);
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
struct Entry {
|
|
||||||
VirtualSegments segments;
|
|
||||||
size_t as_id{};
|
|
||||||
GPUVAddr gpu_addr{};
|
|
||||||
u32 size{};
|
|
||||||
};
|
|
||||||
|
|
||||||
struct DeferredUnmap {
|
|
||||||
size_t as_id;
|
|
||||||
GPUVAddr gpu_addr;
|
|
||||||
u64 size;
|
|
||||||
};
|
|
||||||
|
|
||||||
static u64 MakeKey(size_t as_id, GPUVAddr gpu_addr) {
|
|
||||||
return (static_cast<u64>(as_id) << 48) ^ gpu_addr;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ApplyDeferred() {
|
|
||||||
std::vector<DeferredUnmap> pending;
|
|
||||||
bool overflow = false;
|
|
||||||
{
|
|
||||||
std::scoped_lock lock{deferred_mutex};
|
|
||||||
has_deferred.store(false, std::memory_order_release);
|
|
||||||
pending.swap(deferred);
|
|
||||||
overflow = deferred_overflow;
|
|
||||||
deferred_overflow = false;
|
|
||||||
}
|
|
||||||
if (overflow) {
|
|
||||||
entries.clear();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (pending.empty() || entries.empty()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
for (auto it = entries.begin(); it != entries.end();) {
|
|
||||||
const Entry& entry = it->second;
|
|
||||||
const GPUVAddr entry_end = entry.gpu_addr + entry.size;
|
|
||||||
bool overlaps = false;
|
|
||||||
for (const DeferredUnmap& unmap : pending) {
|
|
||||||
if (unmap.as_id != entry.as_id) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (entry.gpu_addr < unmap.gpu_addr + unmap.size && unmap.gpu_addr < entry_end) {
|
|
||||||
overlaps = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (overlaps) {
|
|
||||||
it = entries.erase(it);
|
|
||||||
} else {
|
|
||||||
++it;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
::Common::unordered_map<u64, Entry> entries;
|
|
||||||
std::vector<DeferredUnmap> deferred;
|
|
||||||
std::mutex deferred_mutex;
|
|
||||||
std::atomic<bool> has_deferred{false};
|
|
||||||
bool deferred_overflow{};
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace VideoCommon
|
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <cstddef>
|
||||||
|
|
||||||
|
#include "common/common_types.h"
|
||||||
|
|
||||||
|
namespace VideoCore {
|
||||||
|
|
||||||
|
constexpr u32 MIN_FRAME_GEN_MULTIPLIER = 2;
|
||||||
|
constexpr u32 MAX_FRAME_GEN_MULTIPLIER = 4;
|
||||||
|
|
||||||
|
struct FrameGenConfig {
|
||||||
|
bool enabled{};
|
||||||
|
u32 multiplier{MIN_FRAME_GEN_MULTIPLIER};
|
||||||
|
u32 target_rate{};
|
||||||
|
bool flow_scale_auto{true};
|
||||||
|
u32 flow_scale{75};
|
||||||
|
u32 queue_target{1};
|
||||||
|
bool fp16{true};
|
||||||
|
bool dump_flow{};
|
||||||
|
|
||||||
|
[[nodiscard]] size_t Generations() const {
|
||||||
|
return enabled ? std::clamp(multiplier, MIN_FRAME_GEN_MULTIPLIER,
|
||||||
|
MAX_FRAME_GEN_MULTIPLIER) -
|
||||||
|
1
|
||||||
|
: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] size_t MaxGenerations() const {
|
||||||
|
if (!enabled) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return target_rate != 0 ? MAX_FRAME_GEN_MULTIPLIER - 1 : Generations();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator==(const FrameGenConfig&) const = default;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace VideoCore
|
||||||
@@ -55,11 +55,11 @@ constexpr u64 GpuClockMultiplier(Settings::GpuClock clock) {
|
|||||||
|
|
||||||
struct GPU::Impl {
|
struct GPU::Impl {
|
||||||
explicit Impl(Core::System& system_, bool is_async_, bool use_nvdec_)
|
explicit Impl(Core::System& system_, bool is_async_, bool use_nvdec_)
|
||||||
: gpu_thread{system_}
|
: system{system_}
|
||||||
, system{system_}
|
|
||||||
, use_nvdec{use_nvdec_}
|
, use_nvdec{use_nvdec_}
|
||||||
, shader_notify()
|
, shader_notify()
|
||||||
, is_async{is_async_}
|
, is_async{is_async_}
|
||||||
|
, gpu_thread{system_}
|
||||||
{}
|
{}
|
||||||
|
|
||||||
~Impl() = default;
|
~Impl() = default;
|
||||||
@@ -301,10 +301,6 @@ struct GPU::Impl {
|
|||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Destruction of thread must be done before all (non trivial)
|
|
||||||
// previous members has been destroyed
|
|
||||||
VideoCommon::GPUThread::ThreadManager gpu_thread;
|
|
||||||
|
|
||||||
Core::System& system;
|
Core::System& system;
|
||||||
|
|
||||||
std::unique_ptr<VideoCore::RendererBase> renderer;
|
std::unique_ptr<VideoCore::RendererBase> renderer;
|
||||||
@@ -333,6 +329,7 @@ struct GPU::Impl {
|
|||||||
|
|
||||||
const bool is_async;
|
const bool is_async;
|
||||||
|
|
||||||
|
VideoCommon::GPUThread::ThreadManager gpu_thread;
|
||||||
std::unique_ptr<Core::Frontend::GraphicsContext> cpu_context;
|
std::unique_ptr<Core::Frontend::GraphicsContext> cpu_context;
|
||||||
|
|
||||||
Tegra::Control::Scheduler scheduler;
|
Tegra::Control::Scheduler scheduler;
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
#include <thread>
|
#include <thread>
|
||||||
|
|
||||||
#include "common/logging.h"
|
#include "common/logging.h"
|
||||||
|
#include "common/settings.h"
|
||||||
#include "core/frontend/emu_window.h"
|
#include "core/frontend/emu_window.h"
|
||||||
#include "core/frontend/graphics_context.h"
|
#include "core/frontend/graphics_context.h"
|
||||||
#include "video_core/renderer_base.h"
|
#include "video_core/renderer_base.h"
|
||||||
@@ -22,6 +23,16 @@ RendererBase::RendererBase(Core::Frontend::EmuWindow& window_,
|
|||||||
RendererBase::~RendererBase() = default;
|
RendererBase::~RendererBase() = default;
|
||||||
|
|
||||||
void RendererBase::RefreshBaseSettings() {
|
void RendererBase::RefreshBaseSettings() {
|
||||||
|
renderer_settings.SetFrameGenConfig({
|
||||||
|
.enabled = Settings::values.frame_gen.GetValue(),
|
||||||
|
.multiplier = Settings::FrameGenMultiplier(),
|
||||||
|
.target_rate = Settings::values.frame_gen_target_rate.GetValue(),
|
||||||
|
.flow_scale_auto = Settings::values.frame_gen_flow_scale_auto.GetValue(),
|
||||||
|
.flow_scale = Settings::values.frame_gen_flow_scale.GetValue(),
|
||||||
|
.queue_target = Settings::values.frame_gen_queue_target.GetValue(),
|
||||||
|
.fp16 = Settings::values.frame_gen_fp16.GetValue(),
|
||||||
|
.dump_flow = Settings::values.frame_gen_dump_flow.GetValue(),
|
||||||
|
});
|
||||||
UpdateCurrentFramebufferLayout();
|
UpdateCurrentFramebufferLayout();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -9,10 +9,12 @@
|
|||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include <mutex>
|
||||||
|
|
||||||
#include "common/common_funcs.h"
|
#include "common/common_funcs.h"
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
#include "core/frontend/framebuffer_layout.h"
|
#include "core/frontend/framebuffer_layout.h"
|
||||||
|
#include "video_core/frame_gen/frame_gen_config.h"
|
||||||
#include "video_core/gpu.h"
|
#include "video_core/gpu.h"
|
||||||
#include "video_core/rasterizer_interface.h"
|
#include "video_core/rasterizer_interface.h"
|
||||||
|
|
||||||
@@ -30,6 +32,20 @@ struct RendererSettings {
|
|||||||
std::function<void(bool)> screenshot_complete_callback;
|
std::function<void(bool)> screenshot_complete_callback;
|
||||||
Layout::FramebufferLayout screenshot_framebuffer_layout;
|
Layout::FramebufferLayout screenshot_framebuffer_layout;
|
||||||
Service::Nvnflinger::LayerStackId screenshot_layer_stack{Service::Nvnflinger::LayerStackId::Default};
|
Service::Nvnflinger::LayerStackId screenshot_layer_stack{Service::Nvnflinger::LayerStackId::Default};
|
||||||
|
|
||||||
|
void SetFrameGenConfig(FrameGenConfig config) {
|
||||||
|
std::scoped_lock lock{frame_gen_mutex};
|
||||||
|
frame_gen_config = config;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] FrameGenConfig GetFrameGenConfig() const {
|
||||||
|
std::scoped_lock lock{frame_gen_mutex};
|
||||||
|
return frame_gen_config;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
mutable std::mutex frame_gen_mutex;
|
||||||
|
FrameGenConfig frame_gen_config;
|
||||||
};
|
};
|
||||||
|
|
||||||
class RendererBase {
|
class RendererBase {
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ constexpr std::array PROGRAM_LUT{
|
|||||||
Buffer::Buffer(BufferCacheRuntime&, VideoCommon::NullBufferParams null_params)
|
Buffer::Buffer(BufferCacheRuntime&, VideoCommon::NullBufferParams null_params)
|
||||||
: VideoCommon::BufferBase(null_params) {}
|
: VideoCommon::BufferBase(null_params) {}
|
||||||
|
|
||||||
Buffer::Buffer(BufferCacheRuntime& runtime, DAddr cpu_addr_, u64 size_bytes_, bool)
|
Buffer::Buffer(BufferCacheRuntime& runtime, DAddr cpu_addr_, u64 size_bytes_)
|
||||||
: VideoCommon::BufferBase(cpu_addr_, size_bytes_) {
|
: VideoCommon::BufferBase(cpu_addr_, size_bytes_) {
|
||||||
buffer.Create();
|
buffer.Create();
|
||||||
if (runtime.device.HasDebuggingToolAttached()) {
|
if (runtime.device.HasDebuggingToolAttached()) {
|
||||||
|
|||||||
@@ -23,8 +23,7 @@ class BufferCacheRuntime;
|
|||||||
|
|
||||||
class Buffer : public VideoCommon::BufferBase {
|
class Buffer : public VideoCommon::BufferBase {
|
||||||
public:
|
public:
|
||||||
explicit Buffer(BufferCacheRuntime&, DAddr cpu_addr, u64 size_bytes,
|
explicit Buffer(BufferCacheRuntime&, DAddr cpu_addr, u64 size_bytes);
|
||||||
bool sparse_compatible);
|
|
||||||
explicit Buffer(BufferCacheRuntime&, VideoCommon::NullBufferParams);
|
explicit Buffer(BufferCacheRuntime&, VideoCommon::NullBufferParams);
|
||||||
|
|
||||||
void ImmediateUpload(size_t offset, std::span<const u8> data) noexcept;
|
void ImmediateUpload(size_t offset, std::span<const u8> data) noexcept;
|
||||||
|
|||||||
@@ -23,13 +23,14 @@ constexpr size_t COLOR_CHANNELS = 4;
|
|||||||
constexpr u64 LSFG_REQUIRED_FRAMES = 2;
|
constexpr u64 LSFG_REQUIRED_FRAMES = 2;
|
||||||
constexpr u32 LSFG_RECURRENCE_FRAMES = 2;
|
constexpr u32 LSFG_RECURRENCE_FRAMES = 2;
|
||||||
|
|
||||||
[[nodiscard]] f32 ManualFlowScale() {
|
[[nodiscard]] f32 ManualFlowScale(const VideoCore::FrameGenConfig& config) {
|
||||||
return static_cast<f32>(Settings::values.frame_gen_flow_scale.GetValue()) / 100.0f;
|
return static_cast<f32>(config.flow_scale) / 100.0f;
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] f32 ConfiguredFlowScale(VkExtent2D guest_extent, VkExtent2D presented_extent) {
|
[[nodiscard]] f32 ConfiguredFlowScale(const VideoCore::FrameGenConfig& config,
|
||||||
if (!Settings::values.frame_gen_flow_scale_auto.GetValue()) {
|
VkExtent2D guest_extent, VkExtent2D presented_extent) {
|
||||||
return ManualFlowScale();
|
if (!config.flow_scale_auto) {
|
||||||
|
return ManualFlowScale(config);
|
||||||
}
|
}
|
||||||
if (guest_extent.width == 0 || presented_extent.width == 0) {
|
if (guest_extent.width == 0 || presented_extent.width == 0) {
|
||||||
return 1.0f;
|
return 1.0f;
|
||||||
@@ -194,15 +195,61 @@ FrameGen::FrameGen(MemoryAllocator& memory_allocator_, Scheduler& scheduler_)
|
|||||||
|
|
||||||
FrameGen::~FrameGen() = default;
|
FrameGen::~FrameGen() = default;
|
||||||
|
|
||||||
void FrameGen::Process(const Device& device, Frame* frame, VkFormat format,
|
void FrameGen::UpdateConfig(const VideoCore::FrameGenConfig& new_config) {
|
||||||
VkExtent2D guest_extent) {
|
if (!has_config) {
|
||||||
generated = false;
|
config = new_config;
|
||||||
|
has_config = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (config == new_config) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (unavailable || !Settings::values.frame_gen.GetValue()) {
|
const bool has_toggled_lsfg = config.enabled != new_config.enabled;
|
||||||
|
const bool precision_changed = config.fp16 != new_config.fp16;
|
||||||
|
const bool enabling = !config.enabled && new_config.enabled;
|
||||||
|
const bool must_reset_pipeline = has_toggled_lsfg || precision_changed;
|
||||||
|
const bool must_reload_shaders = precision_changed || enabling;
|
||||||
|
const bool must_reset_pacer =
|
||||||
|
has_toggled_lsfg || config.multiplier != new_config.multiplier ||
|
||||||
|
config.target_rate != new_config.target_rate;
|
||||||
|
|
||||||
|
if (must_reset_pipeline) {
|
||||||
if (chain) {
|
if (chain) {
|
||||||
scheduler.Finish();
|
scheduler.Finish();
|
||||||
chain.reset();
|
chain.reset();
|
||||||
}
|
}
|
||||||
|
if (must_reload_shaders) {
|
||||||
|
shaders.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
plan = {};
|
||||||
|
peak_guest_extent = {};
|
||||||
|
built_extent = {};
|
||||||
|
built_format = VK_FORMAT_UNDEFINED;
|
||||||
|
built_flow_scale = 0.0f;
|
||||||
|
frame_count = 0;
|
||||||
|
warm_streak = 0;
|
||||||
|
generated = false;
|
||||||
|
dumped = false;
|
||||||
|
|
||||||
|
// this will pickup the dll again once toggled
|
||||||
|
if (must_reload_shaders) {
|
||||||
|
unavailable = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (must_reset_pacer) {
|
||||||
|
pacer.Reset();
|
||||||
|
}
|
||||||
|
config = new_config;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FrameGen::Process(const Device& device, Frame* frame, VkFormat format,
|
||||||
|
VkExtent2D guest_extent) {
|
||||||
|
generated = false;
|
||||||
|
|
||||||
|
if (unavailable || !config.enabled) {
|
||||||
warm_streak = 0;
|
warm_streak = 0;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -213,7 +260,7 @@ void FrameGen::Process(const Device& device, Frame* frame, VkFormat format,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!shaders) {
|
if (!shaders) {
|
||||||
shaders.emplace(device);
|
shaders.emplace(device, config.fp16);
|
||||||
if (!shaders->IsValid()) {
|
if (!shaders->IsValid()) {
|
||||||
unavailable = true;
|
unavailable = true;
|
||||||
return;
|
return;
|
||||||
@@ -224,7 +271,7 @@ void FrameGen::Process(const Device& device, Frame* frame, VkFormat format,
|
|||||||
peak_guest_extent.height = std::max(peak_guest_extent.height, guest_extent.height);
|
peak_guest_extent.height = std::max(peak_guest_extent.height, guest_extent.height);
|
||||||
|
|
||||||
const VkExtent2D extent{.width = frame->width, .height = frame->height};
|
const VkExtent2D extent{.width = frame->width, .height = frame->height};
|
||||||
const f32 flow_scale = ConfiguredFlowScale(peak_guest_extent, extent);
|
const f32 flow_scale = ConfiguredFlowScale(config, peak_guest_extent, extent);
|
||||||
if (!chain || built_extent.width != extent.width || built_extent.height != extent.height ||
|
if (!chain || built_extent.width != extent.width || built_extent.height != extent.height ||
|
||||||
built_format != format || built_flow_scale != flow_scale) {
|
built_format != format || built_flow_scale != flow_scale) {
|
||||||
Rebuild(device, extent, format, flow_scale);
|
Rebuild(device, extent, format, flow_scale);
|
||||||
@@ -247,7 +294,7 @@ void FrameGen::Process(const Device& device, Frame* frame, VkFormat format,
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const bool dump_requested = generated && Settings::values.frame_gen_dump_flow.GetValue();
|
const bool dump_requested = generated && config.dump_flow;
|
||||||
if (!dump_requested) {
|
if (!dump_requested) {
|
||||||
dumped = false;
|
dumped = false;
|
||||||
} else if (!dumped) {
|
} else if (!dumped) {
|
||||||
@@ -261,7 +308,7 @@ size_t FrameGen::WantedGenerations(size_t capacity) {
|
|||||||
plan = {};
|
plan = {};
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
plan = pacer.Plan(capacity);
|
plan = pacer.Plan(capacity, config);
|
||||||
return plan.generations;
|
return plan.generations;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
#include <optional>
|
#include <optional>
|
||||||
|
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
|
#include "video_core/frame_gen/frame_gen_config.h"
|
||||||
#include "video_core/renderer_vulkan/present/frame_gen_pacer.h"
|
#include "video_core/renderer_vulkan/present/frame_gen_pacer.h"
|
||||||
#include "video_core/renderer_vulkan/present/lsfg_chain.h"
|
#include "video_core/renderer_vulkan/present/lsfg_chain.h"
|
||||||
#include "video_core/renderer_vulkan/present/lsfg_shaders.h"
|
#include "video_core/renderer_vulkan/present/lsfg_shaders.h"
|
||||||
@@ -22,6 +23,8 @@ public:
|
|||||||
explicit FrameGen(MemoryAllocator& memory_allocator, Scheduler& scheduler);
|
explicit FrameGen(MemoryAllocator& memory_allocator, Scheduler& scheduler);
|
||||||
~FrameGen();
|
~FrameGen();
|
||||||
|
|
||||||
|
void UpdateConfig(const VideoCore::FrameGenConfig& new_config);
|
||||||
|
|
||||||
void Process(const Device& device, Frame* frame, VkFormat format, VkExtent2D guest_extent);
|
void Process(const Device& device, Frame* frame, VkFormat format, VkExtent2D guest_extent);
|
||||||
|
|
||||||
[[nodiscard]] size_t WantedGenerations(size_t capacity);
|
[[nodiscard]] size_t WantedGenerations(size_t capacity);
|
||||||
@@ -39,6 +42,7 @@ private:
|
|||||||
|
|
||||||
std::optional<LsfgShaders> shaders;
|
std::optional<LsfgShaders> shaders;
|
||||||
std::optional<LsfgChain> chain;
|
std::optional<LsfgChain> chain;
|
||||||
|
VideoCore::FrameGenConfig config;
|
||||||
FrameGenPacer pacer;
|
FrameGenPacer pacer;
|
||||||
FrameGenPlan plan{};
|
FrameGenPlan plan{};
|
||||||
VkExtent2D peak_guest_extent{};
|
VkExtent2D peak_guest_extent{};
|
||||||
@@ -52,6 +56,7 @@ private:
|
|||||||
bool generated{};
|
bool generated{};
|
||||||
bool unavailable{};
|
bool unavailable{};
|
||||||
bool dumped{};
|
bool dumped{};
|
||||||
|
bool has_config{};
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Vulkan
|
} // namespace Vulkan
|
||||||
|
|||||||
@@ -5,7 +5,6 @@
|
|||||||
#include <cmath>
|
#include <cmath>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
#include "common/settings.h"
|
|
||||||
#include "video_core/renderer_vulkan/present/frame_gen_pacer.h"
|
#include "video_core/renderer_vulkan/present/frame_gen_pacer.h"
|
||||||
|
|
||||||
namespace Vulkan {
|
namespace Vulkan {
|
||||||
@@ -46,8 +45,8 @@ constexpr auto PROBE_STEP_DELAY = std::chrono::milliseconds(250);
|
|||||||
|
|
||||||
} // Anonymous namespace
|
} // Anonymous namespace
|
||||||
|
|
||||||
FrameGenPlan FrameGenPacer::Plan(size_t capacity) {
|
FrameGenPlan FrameGenPacer::Plan(size_t capacity, const VideoCore::FrameGenConfig& config) {
|
||||||
const size_t ceiling = std::min(capacity, Settings::FrameGenMaxGenerations());
|
const size_t ceiling = std::min(capacity, config.MaxGenerations());
|
||||||
if (ceiling == 0) {
|
if (ceiling == 0) {
|
||||||
Reset();
|
Reset();
|
||||||
return {};
|
return {};
|
||||||
@@ -69,7 +68,7 @@ FrameGenPlan FrameGenPacer::Plan(size_t capacity) {
|
|||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
const f32 target_rate = static_cast<f32>(Settings::values.frame_gen_target_rate.GetValue());
|
const f32 target_rate = static_cast<f32>(config.target_rate);
|
||||||
|
|
||||||
if (smoothed_interval > 0.0f) {
|
if (smoothed_interval > 0.0f) {
|
||||||
f32 burst_threshold = BURST_CADENCE_RATIO / smoothed_interval;
|
f32 burst_threshold = BURST_CADENCE_RATIO / smoothed_interval;
|
||||||
@@ -109,7 +108,7 @@ FrameGenPlan FrameGenPacer::Plan(size_t capacity) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (target_rate == 0.0f) {
|
if (target_rate == 0.0f) {
|
||||||
limit = std::min(Settings::FrameGenGenerations(), ceiling);
|
limit = std::min(config.Generations(), ceiling);
|
||||||
output_credit = 0.0f;
|
output_credit = 0.0f;
|
||||||
issued_generations = limit;
|
issued_generations = limit;
|
||||||
return {.generations = limit, .warm = limit > 0};
|
return {.generations = limit, .warm = limit > 0};
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
#include <optional>
|
#include <optional>
|
||||||
|
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
|
#include "video_core/frame_gen/frame_gen_config.h"
|
||||||
|
|
||||||
namespace Vulkan {
|
namespace Vulkan {
|
||||||
|
|
||||||
@@ -17,7 +18,7 @@ struct FrameGenPlan {
|
|||||||
|
|
||||||
class FrameGenPacer {
|
class FrameGenPacer {
|
||||||
public:
|
public:
|
||||||
[[nodiscard]] FrameGenPlan Plan(size_t capacity);
|
[[nodiscard]] FrameGenPlan Plan(size_t capacity, const VideoCore::FrameGenConfig& config);
|
||||||
|
|
||||||
void Reset();
|
void Reset();
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
#include "common/settings.h"
|
|
||||||
#include "video_core/frame_gen/lossless_dll.h"
|
#include "video_core/frame_gen/lossless_dll.h"
|
||||||
#include "video_core/renderer_vulkan/present/lsfg_shaders.h"
|
#include "video_core/renderer_vulkan/present/lsfg_shaders.h"
|
||||||
#include "video_core/renderer_vulkan/present/util.h"
|
#include "video_core/renderer_vulkan/present/util.h"
|
||||||
@@ -9,16 +8,15 @@
|
|||||||
|
|
||||||
namespace Vulkan {
|
namespace Vulkan {
|
||||||
|
|
||||||
LsfgShaders::LsfgShaders(const Device& device) {
|
LsfgShaders::LsfgShaders(const Device& device, bool prefer_fp16) {
|
||||||
if (!device.IsVulkanMemoryModelSupported() || !device.HasNullDescriptor()) {
|
if (!device.IsVulkanMemoryModelSupported() || !device.HasNullDescriptor()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const bool allow_fp16 = device.IsFloat16Supported();
|
const bool allow_fp16 = device.IsFloat16Supported();
|
||||||
const bool prefer_fp16 = allow_fp16 && Settings::values.frame_gen_fp16.GetValue();
|
|
||||||
|
|
||||||
VideoCore::FrameGen::ShaderModules code;
|
VideoCore::FrameGen::ShaderModules code;
|
||||||
if (VideoCore::FrameGen::LoadShaderModules(code, allow_fp16, prefer_fp16) !=
|
if (VideoCore::FrameGen::LoadShaderModules(code, allow_fp16, allow_fp16 && prefer_fp16) !=
|
||||||
VideoCore::FrameGen::LosslessStatus::Ok) {
|
VideoCore::FrameGen::LosslessStatus::Ok) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ class Device;
|
|||||||
|
|
||||||
class LsfgShaders {
|
class LsfgShaders {
|
||||||
public:
|
public:
|
||||||
explicit LsfgShaders(const Device& device);
|
explicit LsfgShaders(const Device& device, bool prefer_fp16);
|
||||||
|
|
||||||
[[nodiscard]] bool IsValid() const {
|
[[nodiscard]] bool IsValid() const {
|
||||||
return valid;
|
return valid;
|
||||||
|
|||||||
@@ -145,6 +145,7 @@ try
|
|||||||
, swapchain(*surface,
|
, swapchain(*surface,
|
||||||
device,
|
device,
|
||||||
scheduler,
|
scheduler,
|
||||||
|
Settings(),
|
||||||
render_window.GetFramebufferLayout().width,
|
render_window.GetFramebufferLayout().width,
|
||||||
render_window.GetFramebufferLayout().height)
|
render_window.GetFramebufferLayout().height)
|
||||||
, present_manager(instance,
|
, present_manager(instance,
|
||||||
@@ -152,6 +153,7 @@ try
|
|||||||
device,
|
device,
|
||||||
memory_allocator,
|
memory_allocator,
|
||||||
scheduler,
|
scheduler,
|
||||||
|
Settings(),
|
||||||
swapchain,
|
swapchain,
|
||||||
surface)
|
surface)
|
||||||
, blit_swapchain(device_memory,
|
, blit_swapchain(device_memory,
|
||||||
@@ -206,6 +208,9 @@ void RendererVulkan::Composite(std::span<const Tegra::FramebufferConfig> framebu
|
|||||||
}
|
}
|
||||||
|
|
||||||
RenderScreenshot(framebuffers);
|
RenderScreenshot(framebuffers);
|
||||||
|
#ifdef HAS_LSFG
|
||||||
|
frame_gen.UpdateConfig(Settings().GetFrameGenConfig());
|
||||||
|
#endif
|
||||||
Frame* frame = present_manager.GetRenderFrame();
|
Frame* frame = present_manager.GetRenderFrame();
|
||||||
|
|
||||||
scheduler.RequestOutsideRenderPassOperationContext();
|
scheduler.RequestOutsideRenderPassOperationContext();
|
||||||
|
|||||||
@@ -56,8 +56,7 @@ size_t BytesPerIndex(VkIndexType index_type) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
vk::Buffer CreateBuffer(const Device& device, const MemoryAllocator& memory_allocator, u64 size,
|
vk::Buffer CreateBuffer(const Device& device, const MemoryAllocator& memory_allocator, u64 size) {
|
||||||
VkDeviceSize sparse_alignment) {
|
|
||||||
VkBufferUsageFlags flags =
|
VkBufferUsageFlags flags =
|
||||||
VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT |
|
VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT |
|
||||||
VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT | VK_BUFFER_USAGE_STORAGE_TEXEL_BUFFER_BIT |
|
VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT | VK_BUFFER_USAGE_STORAGE_TEXEL_BUFFER_BIT |
|
||||||
@@ -83,9 +82,6 @@ vk::Buffer CreateBuffer(const Device& device, const MemoryAllocator& memory_allo
|
|||||||
.queueFamilyIndexCount = 0,
|
.queueFamilyIndexCount = 0,
|
||||||
.pQueueFamilyIndices = nullptr,
|
.pQueueFamilyIndices = nullptr,
|
||||||
};
|
};
|
||||||
if (sparse_alignment > 1) {
|
|
||||||
return memory_allocator.CreateBuffer(buffer_ci, MemoryUsage::DeviceLocal, sparse_alignment);
|
|
||||||
}
|
|
||||||
return memory_allocator.CreateBuffer(buffer_ci, MemoryUsage::DeviceLocal);
|
return memory_allocator.CreateBuffer(buffer_ci, MemoryUsage::DeviceLocal);
|
||||||
}
|
}
|
||||||
} // Anonymous namespace
|
} // Anonymous namespace
|
||||||
@@ -103,14 +99,10 @@ Buffer::Buffer(BufferCacheRuntime& runtime, VideoCommon::NullBufferParams null_p
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Buffer::Buffer(BufferCacheRuntime& runtime, DAddr cpu_addr_, u64 size_bytes_,
|
Buffer::Buffer(BufferCacheRuntime& runtime, DAddr cpu_addr_, u64 size_bytes_)
|
||||||
bool sparse_compatible_)
|
|
||||||
: VideoCommon::BufferBase(cpu_addr_, size_bytes_), device{&runtime.device},
|
: VideoCommon::BufferBase(cpu_addr_, size_bytes_), device{&runtime.device},
|
||||||
scheduler{&runtime.scheduler},
|
scheduler{&runtime.scheduler},
|
||||||
buffer{CreateBuffer(*device, runtime.memory_allocator, SizeBytes(),
|
buffer{CreateBuffer(*device, runtime.memory_allocator, SizeBytes())}, tracker{SizeBytes()} {
|
||||||
runtime.SparseAlignmentFor(sparse_compatible_))},
|
|
||||||
tracker{SizeBytes()} {
|
|
||||||
sparse_compatible = sparse_compatible_;
|
|
||||||
if (runtime.device.HasDebuggingToolAttached()) {
|
if (runtime.device.HasDebuggingToolAttached()) {
|
||||||
buffer.SetObjectNameEXT(fmt::format("Buffer {:#x}", CpuAddr()).c_str());
|
buffer.SetObjectNameEXT(fmt::format("Buffer {:#x}", CpuAddr()).c_str());
|
||||||
}
|
}
|
||||||
@@ -356,8 +348,7 @@ BufferCacheRuntime::BufferCacheRuntime(const Device& device_, MemoryAllocator& m
|
|||||||
: device{device_}, memory_allocator{memory_allocator_}, scheduler{scheduler_},
|
: device{device_}, memory_allocator{memory_allocator_}, scheduler{scheduler_},
|
||||||
staging_pool{staging_pool_}, guest_descriptor_queue{guest_descriptor_queue_},
|
staging_pool{staging_pool_}, guest_descriptor_queue{guest_descriptor_queue_},
|
||||||
quad_index_pass(device, scheduler, descriptor_pool, staging_pool,
|
quad_index_pass(device, scheduler, descriptor_pool, staging_pool,
|
||||||
compute_pass_descriptor_queue),
|
compute_pass_descriptor_queue) {
|
||||||
multi_range_buffers(device_) {
|
|
||||||
const VkDriverIdKHR driver_id = device.GetDriverID();
|
const VkDriverIdKHR driver_id = device.GetDriverID();
|
||||||
limit_dynamic_storage_buffers = driver_id == VK_DRIVER_ID_QUALCOMM_PROPRIETARY ||
|
limit_dynamic_storage_buffers = driver_id == VK_DRIVER_ID_QUALCOMM_PROPRIETARY ||
|
||||||
driver_id == VK_DRIVER_ID_ARM_PROPRIETARY;
|
driver_id == VK_DRIVER_ID_ARM_PROPRIETARY;
|
||||||
@@ -545,37 +536,6 @@ void BufferCacheRuntime::ClearBuffer(VkBuffer dest_buffer, u32 offset, size_t si
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
bool BufferCacheRuntime::BindMultiRangeStorageBuffer(u64 key, bool is_written) {
|
|
||||||
if (multi_range_sources.empty() || multi_range_total == 0) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
const MultiRangeRef ref = multi_range_buffers.Get(device, scheduler, memory_allocator, key,
|
|
||||||
multi_range_sources, multi_range_total);
|
|
||||||
if (ref.handle == VK_NULL_HANDLE) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (is_written && !ref.sparse) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (ref.needs_gather) {
|
|
||||||
PreCopyBarrier();
|
|
||||||
VkDeviceSize dst_offset = 0;
|
|
||||||
for (const MultiRangeSource& source : multi_range_sources) {
|
|
||||||
const std::array<VideoCommon::BufferCopy, 1> copy{VideoCommon::BufferCopy{
|
|
||||||
.src_offset = u64(source.offset),
|
|
||||||
.dst_offset = u64(dst_offset),
|
|
||||||
.size = size_t(source.size),
|
|
||||||
}};
|
|
||||||
CopyBuffer(ref.handle, source.handle, copy, false);
|
|
||||||
dst_offset += source.size;
|
|
||||||
}
|
|
||||||
PostCopyBarrier();
|
|
||||||
multi_range_buffers.MarkGathered(key);
|
|
||||||
}
|
|
||||||
guest_descriptor_queue.AddBuffer(ref.handle, ref.address, 0, ref.size);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void BufferCacheRuntime::BindIndexBuffer(PrimitiveTopology topology, IndexFormat index_format,
|
void BufferCacheRuntime::BindIndexBuffer(PrimitiveTopology topology, IndexFormat index_format,
|
||||||
u32 base_vertex, u32 num_indices, VkBuffer buffer,
|
u32 base_vertex, u32 num_indices, VkBuffer buffer,
|
||||||
u32 offset, [[maybe_unused]] u32 size) {
|
u32 offset, [[maybe_unused]] u32 size) {
|
||||||
|
|||||||
@@ -8,14 +8,11 @@
|
|||||||
|
|
||||||
#include <limits>
|
#include <limits>
|
||||||
|
|
||||||
#include <boost/container/small_vector.hpp>
|
|
||||||
|
|
||||||
#include "video_core/buffer_cache/buffer_cache_base.h"
|
#include "video_core/buffer_cache/buffer_cache_base.h"
|
||||||
#include "video_core/buffer_cache/memory_tracker_base.h"
|
#include "video_core/buffer_cache/memory_tracker_base.h"
|
||||||
#include "video_core/buffer_cache/usage_tracker.h"
|
#include "video_core/buffer_cache/usage_tracker.h"
|
||||||
#include "video_core/engines/maxwell_3d.h"
|
#include "video_core/engines/maxwell_3d.h"
|
||||||
#include "video_core/renderer_vulkan/vk_compute_pass.h"
|
#include "video_core/renderer_vulkan/vk_compute_pass.h"
|
||||||
#include "video_core/renderer_vulkan/vk_multi_range_buffer.h"
|
|
||||||
#include "video_core/renderer_vulkan/vk_staging_buffer_pool.h"
|
#include "video_core/renderer_vulkan/vk_staging_buffer_pool.h"
|
||||||
#include "video_core/renderer_vulkan/vk_update_descriptor.h"
|
#include "video_core/renderer_vulkan/vk_update_descriptor.h"
|
||||||
#include "video_core/surface.h"
|
#include "video_core/surface.h"
|
||||||
@@ -34,8 +31,7 @@ class BufferCacheRuntime;
|
|||||||
class Buffer : public VideoCommon::BufferBase {
|
class Buffer : public VideoCommon::BufferBase {
|
||||||
public:
|
public:
|
||||||
explicit Buffer(BufferCacheRuntime&, VideoCommon::NullBufferParams null_params);
|
explicit Buffer(BufferCacheRuntime&, VideoCommon::NullBufferParams null_params);
|
||||||
explicit Buffer(BufferCacheRuntime& runtime, VAddr cpu_addr_, u64 size_bytes_,
|
explicit Buffer(BufferCacheRuntime& runtime, VAddr cpu_addr_, u64 size_bytes_);
|
||||||
bool sparse_compatible_);
|
|
||||||
|
|
||||||
[[nodiscard]] VkBufferView View(u32 offset, u32 size, VideoCore::Surface::PixelFormat format);
|
[[nodiscard]] VkBufferView View(u32 offset, u32 size, VideoCore::Surface::PixelFormat format);
|
||||||
|
|
||||||
@@ -47,14 +43,6 @@ public:
|
|||||||
return device_address;
|
return device_address;
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] bool IsSparseCompatible() const noexcept {
|
|
||||||
return sparse_compatible;
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] vk::MemoryLocation Location() const noexcept {
|
|
||||||
return buffer.Location();
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] bool IsRegionUsed(u64 offset, u64 size) const noexcept {
|
[[nodiscard]] bool IsRegionUsed(u64 offset, u64 size) const noexcept {
|
||||||
return tracker.IsUsed(offset, size);
|
return tracker.IsUsed(offset, size);
|
||||||
}
|
}
|
||||||
@@ -89,7 +77,6 @@ private:
|
|||||||
VkDeviceAddress device_address{};
|
VkDeviceAddress device_address{};
|
||||||
u64 last_usage_tick{};
|
u64 last_usage_tick{};
|
||||||
bool is_null{};
|
bool is_null{};
|
||||||
bool sparse_compatible{};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class QuadArrayIndexBuffer;
|
class QuadArrayIndexBuffer;
|
||||||
@@ -138,7 +125,7 @@ public:
|
|||||||
|
|
||||||
void PreCopyBarrier();
|
void PreCopyBarrier();
|
||||||
|
|
||||||
void CopyBuffer(VkBuffer dst_buffer, VkBuffer src_buffer,
|
void CopyBuffer(VkBuffer src_buffer, VkBuffer dst_buffer,
|
||||||
std::span<const VideoCommon::BufferCopy> copies, bool barrier,
|
std::span<const VideoCommon::BufferCopy> copies, bool barrier,
|
||||||
bool can_reorder_upload = false);
|
bool can_reorder_upload = false);
|
||||||
|
|
||||||
@@ -168,46 +155,6 @@ public:
|
|||||||
return ref.mapped_span;
|
return ref.mapped_span;
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] VkDeviceSize SparseAlignmentFor(bool sparse_compatible) const noexcept {
|
|
||||||
if (!sparse_compatible || !multi_range_buffers.use_sparse) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
return multi_range_buffers.block_size;
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] bool PrefersSparseSources() const noexcept {
|
|
||||||
return multi_range_buffers.use_sparse;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ResetMultiRange() noexcept {
|
|
||||||
multi_range_sources.clear();
|
|
||||||
multi_range_total = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void PushMultiRangeSource(const Buffer& buffer, u32 offset, u32 size) {
|
|
||||||
const vk::MemoryLocation location = buffer.Location();
|
|
||||||
multi_range_sources.push_back(MultiRangeSource{
|
|
||||||
.handle = buffer.Handle(),
|
|
||||||
.memory = location.memory,
|
|
||||||
.memory_offset = location.offset,
|
|
||||||
.offset = offset,
|
|
||||||
.size = size,
|
|
||||||
.write_tick = buffer.getWriteTick(),
|
|
||||||
.memory_type = location.memory_type,
|
|
||||||
});
|
|
||||||
multi_range_total += size;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool BindMultiRangeStorageBuffer(u64 key, bool is_written);
|
|
||||||
|
|
||||||
void InvalidateMultiRange(u64 key) {
|
|
||||||
multi_range_buffers.Invalidate(key);
|
|
||||||
}
|
|
||||||
|
|
||||||
void OnBufferDeleted(const Buffer& buffer) {
|
|
||||||
multi_range_buffers.DropOwner(scheduler, buffer.Handle());
|
|
||||||
}
|
|
||||||
|
|
||||||
void BindUniformBuffer(const Buffer& buffer, u32 offset, u32 size) {
|
void BindUniformBuffer(const Buffer& buffer, u32 offset, u32 size) {
|
||||||
BindBuffer(buffer, offset, size);
|
BindBuffer(buffer, offset, size);
|
||||||
}
|
}
|
||||||
@@ -261,10 +208,6 @@ private:
|
|||||||
std::unique_ptr<Uint8Pass> uint8_pass;
|
std::unique_ptr<Uint8Pass> uint8_pass;
|
||||||
QuadIndexedPass quad_index_pass;
|
QuadIndexedPass quad_index_pass;
|
||||||
|
|
||||||
MultiRangeBufferCache multi_range_buffers;
|
|
||||||
boost::container::small_vector<MultiRangeSource, 16> multi_range_sources;
|
|
||||||
VkDeviceSize multi_range_total{};
|
|
||||||
|
|
||||||
bool limit_dynamic_storage_buffers = false;
|
bool limit_dynamic_storage_buffers = false;
|
||||||
u32 max_dynamic_storage_buffers = (std::numeric_limits<u32>::max)();
|
u32 max_dynamic_storage_buffers = (std::numeric_limits<u32>::max)();
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,338 +0,0 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
|
|
||||||
#include <algorithm>
|
|
||||||
#include <mutex>
|
|
||||||
#include <utility>
|
|
||||||
|
|
||||||
#include "video_core/renderer_vulkan/vk_multi_range_buffer.h"
|
|
||||||
#include "video_core/renderer_vulkan/vk_scheduler.h"
|
|
||||||
#include "video_core/vulkan_common/vulkan_device.h"
|
|
||||||
|
|
||||||
namespace Vulkan {
|
|
||||||
|
|
||||||
MultiRangeBufferCache::MultiRangeBufferCache(const Device& device) {
|
|
||||||
sparse_usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT |
|
|
||||||
VK_BUFFER_USAGE_STORAGE_BUFFER_BIT;
|
|
||||||
if (device.IsBufferDeviceAddressSupported()) {
|
|
||||||
sparse_usage |= VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT;
|
|
||||||
}
|
|
||||||
if (!device.IsSparseBindingSupported()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
u32 memory_type_bits = 0;
|
|
||||||
const VkDeviceSize queried = QueryBlockSize(device, memory_type_bits);
|
|
||||||
if (queried == 0 || memory_type_bits == 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
block_size = queried;
|
|
||||||
sparse_memory_type_bits = memory_type_bits;
|
|
||||||
use_sparse = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
VkDeviceSize MultiRangeBufferCache::QueryBlockSize(const Device& device,
|
|
||||||
u32& memory_type_bits) const {
|
|
||||||
const VkDevice logical = *device.GetLogical();
|
|
||||||
const auto& dld = device.GetDispatchLoader();
|
|
||||||
const VkBufferCreateInfo probe_ci{
|
|
||||||
.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
|
|
||||||
.pNext = nullptr,
|
|
||||||
.flags = VK_BUFFER_CREATE_SPARSE_BINDING_BIT | VK_BUFFER_CREATE_SPARSE_ALIASED_BIT,
|
|
||||||
.size = DEFAULT_BLOCK_SIZE,
|
|
||||||
.usage = sparse_usage,
|
|
||||||
.sharingMode = VK_SHARING_MODE_EXCLUSIVE,
|
|
||||||
.queueFamilyIndexCount = 0,
|
|
||||||
.pQueueFamilyIndices = nullptr,
|
|
||||||
};
|
|
||||||
VkBuffer probe{};
|
|
||||||
if (dld.vkCreateBuffer(logical, &probe_ci, nullptr, &probe) != VK_SUCCESS) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
const SparseBuffer owned{probe, logical, dld};
|
|
||||||
const VkBufferMemoryRequirementsInfo2 reqs_info{
|
|
||||||
.sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_REQUIREMENTS_INFO_2,
|
|
||||||
.pNext = nullptr,
|
|
||||||
.buffer = probe,
|
|
||||||
};
|
|
||||||
VkMemoryRequirements2 reqs2{
|
|
||||||
.sType = VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2,
|
|
||||||
.pNext = nullptr,
|
|
||||||
.memoryRequirements = {},
|
|
||||||
};
|
|
||||||
dld.vkGetBufferMemoryRequirements2(logical, &reqs_info, &reqs2);
|
|
||||||
memory_type_bits = reqs2.memoryRequirements.memoryTypeBits;
|
|
||||||
return reqs2.memoryRequirements.alignment;
|
|
||||||
}
|
|
||||||
|
|
||||||
u64 MultiRangeBufferCache::HashSources(std::span<const MultiRangeSource> sources) const {
|
|
||||||
u64 hash = 0xcbf29ce484222325ULL;
|
|
||||||
const auto mix = [&hash](u64 value) {
|
|
||||||
hash ^= value;
|
|
||||||
hash *= 0x100000001b3ULL;
|
|
||||||
};
|
|
||||||
for (const MultiRangeSource& source : sources) {
|
|
||||||
mix(u64(source.handle));
|
|
||||||
mix(u64(source.offset));
|
|
||||||
mix(u64(source.size));
|
|
||||||
}
|
|
||||||
return hash;
|
|
||||||
}
|
|
||||||
|
|
||||||
u64 MultiRangeBufferCache::HashContent(std::span<const MultiRangeSource> sources) const {
|
|
||||||
u64 hash = 0xcbf29ce484222325ULL;
|
|
||||||
for (const MultiRangeSource& source : sources) {
|
|
||||||
hash ^= source.write_tick;
|
|
||||||
hash *= 0x100000001b3ULL;
|
|
||||||
}
|
|
||||||
return hash;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool MultiRangeBufferCache::CanBindSparse(std::span<const MultiRangeSource> sources) const {
|
|
||||||
return use_sparse &&
|
|
||||||
std::none_of(sources.begin(), sources.end(),
|
|
||||||
[block = block_size, bits = sparse_memory_type_bits](auto const& e) {
|
|
||||||
const VkDeviceSize memory_offset = e.memory_offset + e.offset;
|
|
||||||
return e.memory == VK_NULL_HANDLE || e.memory_type >= 32 ||
|
|
||||||
((bits >> e.memory_type) & 1) == 0 ||
|
|
||||||
(memory_offset % block) != 0 || (e.size % block) != 0;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
SparseBuffer MultiRangeBufferCache::CreateSparse(const Device& device, Scheduler& scheduler,
|
|
||||||
std::span<const MultiRangeSource> sources,
|
|
||||||
VkDeviceSize total) {
|
|
||||||
const VkDevice logical = *device.GetLogical();
|
|
||||||
const auto& dld = device.GetDispatchLoader();
|
|
||||||
const VkBufferCreateInfo buffer_ci{
|
|
||||||
.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
|
|
||||||
.pNext = nullptr,
|
|
||||||
.flags = VK_BUFFER_CREATE_SPARSE_BINDING_BIT | VK_BUFFER_CREATE_SPARSE_ALIASED_BIT,
|
|
||||||
.size = total,
|
|
||||||
.usage = sparse_usage,
|
|
||||||
.sharingMode = VK_SHARING_MODE_EXCLUSIVE,
|
|
||||||
.queueFamilyIndexCount = 0,
|
|
||||||
.pQueueFamilyIndices = nullptr,
|
|
||||||
};
|
|
||||||
VkBuffer raw{};
|
|
||||||
if (dld.vkCreateBuffer(logical, &buffer_ci, nullptr, &raw) != VK_SUCCESS) {
|
|
||||||
return SparseBuffer{};
|
|
||||||
}
|
|
||||||
SparseBuffer handle{raw, logical, dld};
|
|
||||||
std::vector<VkSparseMemoryBind> binds;
|
|
||||||
binds.reserve(sources.size());
|
|
||||||
VkDeviceSize resource_offset = 0;
|
|
||||||
for (const MultiRangeSource& source : sources) {
|
|
||||||
binds.push_back(VkSparseMemoryBind{
|
|
||||||
.resourceOffset = resource_offset,
|
|
||||||
.size = source.size,
|
|
||||||
.memory = source.memory,
|
|
||||||
.memoryOffset = source.memory_offset + source.offset,
|
|
||||||
.flags = 0,
|
|
||||||
});
|
|
||||||
resource_offset += source.size;
|
|
||||||
}
|
|
||||||
const VkSparseBufferMemoryBindInfo buffer_bind{
|
|
||||||
.buffer = raw,
|
|
||||||
.bindCount = static_cast<u32>(binds.size()),
|
|
||||||
.pBinds = binds.data(),
|
|
||||||
};
|
|
||||||
const VkBindSparseInfo bind_info{
|
|
||||||
.sType = VK_STRUCTURE_TYPE_BIND_SPARSE_INFO,
|
|
||||||
.pNext = nullptr,
|
|
||||||
.waitSemaphoreCount = 0,
|
|
||||||
.pWaitSemaphores = nullptr,
|
|
||||||
.bufferBindCount = 1,
|
|
||||||
.pBufferBinds = &buffer_bind,
|
|
||||||
.imageOpaqueBindCount = 0,
|
|
||||||
.pImageOpaqueBinds = nullptr,
|
|
||||||
.imageBindCount = 0,
|
|
||||||
.pImageBinds = nullptr,
|
|
||||||
.signalSemaphoreCount = 0,
|
|
||||||
.pSignalSemaphores = nullptr,
|
|
||||||
};
|
|
||||||
const VkFenceCreateInfo fence_ci{
|
|
||||||
.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO,
|
|
||||||
.pNext = nullptr,
|
|
||||||
.flags = 0,
|
|
||||||
};
|
|
||||||
vk::Fence fence = device.GetLogical().CreateFence(fence_ci);
|
|
||||||
VkResult bind_result = VK_ERROR_UNKNOWN;
|
|
||||||
{
|
|
||||||
std::scoped_lock lock{scheduler.submit_mutex};
|
|
||||||
bind_result = device.GetGraphicsQueue().BindSparse(bind_info, *fence);
|
|
||||||
}
|
|
||||||
if (bind_result != VK_SUCCESS) {
|
|
||||||
return SparseBuffer{};
|
|
||||||
}
|
|
||||||
fence.Wait();
|
|
||||||
return handle;
|
|
||||||
}
|
|
||||||
|
|
||||||
void MultiRangeBufferCache::RetireEntry(Scheduler& scheduler, Entry& entry) {
|
|
||||||
if (!entry.sparse_handle && !entry.gathered) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (retired.size() == retired.capacity()) {
|
|
||||||
DrainRetired(scheduler);
|
|
||||||
}
|
|
||||||
if (retired.size() == retired.capacity()) {
|
|
||||||
u64 oldest = retired.front().tick;
|
|
||||||
for (const Retired& item : retired) {
|
|
||||||
if (item.tick < oldest) {
|
|
||||||
oldest = item.tick;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
scheduler.Wait(oldest);
|
|
||||||
DrainRetired(scheduler);
|
|
||||||
}
|
|
||||||
retired.push_back(Retired{
|
|
||||||
.handle = std::move(entry.sparse_handle),
|
|
||||||
.gathered = std::move(entry.gathered),
|
|
||||||
.tick = scheduler.CurrentTick(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
void MultiRangeBufferCache::DrainRetired(Scheduler& scheduler) {
|
|
||||||
size_t index = 0;
|
|
||||||
while (index < retired.size()) {
|
|
||||||
if (scheduler.IsFree(retired[index].tick)) {
|
|
||||||
if (index + 1 != retired.size()) {
|
|
||||||
retired[index] = std::move(retired.back());
|
|
||||||
}
|
|
||||||
retired.pop_back();
|
|
||||||
} else {
|
|
||||||
++index;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
MultiRangeRef MultiRangeBufferCache::Get(const Device& device, Scheduler& scheduler,
|
|
||||||
MemoryAllocator& memory_allocator, u64 key,
|
|
||||||
std::span<const MultiRangeSource> sources,
|
|
||||||
VkDeviceSize total) {
|
|
||||||
if (sources.empty() || total == 0) {
|
|
||||||
return MultiRangeRef{};
|
|
||||||
}
|
|
||||||
if (!retired.empty()) {
|
|
||||||
DrainRetired(scheduler);
|
|
||||||
}
|
|
||||||
const u64 geometry = HashSources(sources);
|
|
||||||
const u64 content = HashContent(sources);
|
|
||||||
const auto it = entries.find(key);
|
|
||||||
if (it != entries.end() && it->second.geometry == geometry && it->second.size == total) {
|
|
||||||
Entry& entry = it->second;
|
|
||||||
if (entry.content != content) {
|
|
||||||
entry.content = content;
|
|
||||||
entry.dirty = true;
|
|
||||||
}
|
|
||||||
MultiRangeRef ref{
|
|
||||||
.handle = *entry.sparse_handle,
|
|
||||||
.address = entry.address,
|
|
||||||
.size = entry.size,
|
|
||||||
.sparse = true,
|
|
||||||
.needs_gather = false,
|
|
||||||
};
|
|
||||||
if (!entry.sparse_handle) {
|
|
||||||
ref.handle = *entry.gathered;
|
|
||||||
ref.sparse = false;
|
|
||||||
ref.needs_gather = entry.dirty;
|
|
||||||
}
|
|
||||||
return ref;
|
|
||||||
}
|
|
||||||
if (it != entries.end()) {
|
|
||||||
RetireEntry(scheduler, it->second);
|
|
||||||
entries.erase(it);
|
|
||||||
}
|
|
||||||
|
|
||||||
Entry entry{};
|
|
||||||
entry.geometry = geometry;
|
|
||||||
entry.content = content;
|
|
||||||
entry.size = total;
|
|
||||||
if (CanBindSparse(sources)) {
|
|
||||||
entry.sparse_handle = CreateSparse(device, scheduler, sources, total);
|
|
||||||
if (entry.sparse_handle) {
|
|
||||||
entry.owners.reserve(sources.size());
|
|
||||||
for (const MultiRangeSource& source : sources) {
|
|
||||||
entry.owners.push_back(source.handle);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!entry.sparse_handle) {
|
|
||||||
VkBufferUsageFlags flags = VK_BUFFER_USAGE_TRANSFER_SRC_BIT |
|
|
||||||
VK_BUFFER_USAGE_TRANSFER_DST_BIT |
|
|
||||||
VK_BUFFER_USAGE_STORAGE_BUFFER_BIT;
|
|
||||||
if (device.IsBufferDeviceAddressSupported()) {
|
|
||||||
flags |= VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT;
|
|
||||||
}
|
|
||||||
const VkBufferCreateInfo gather_ci{
|
|
||||||
.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
|
|
||||||
.pNext = nullptr,
|
|
||||||
.flags = 0,
|
|
||||||
.size = total,
|
|
||||||
.usage = flags,
|
|
||||||
.sharingMode = VK_SHARING_MODE_EXCLUSIVE,
|
|
||||||
.queueFamilyIndexCount = 0,
|
|
||||||
.pQueueFamilyIndices = nullptr,
|
|
||||||
};
|
|
||||||
entry.gathered = memory_allocator.CreateBuffer(gather_ci, MemoryUsage::DeviceLocal);
|
|
||||||
entry.dirty = true;
|
|
||||||
}
|
|
||||||
if (device.IsBufferDeviceAddressSupported()) {
|
|
||||||
VkBuffer address_handle = *entry.sparse_handle;
|
|
||||||
if (!entry.sparse_handle) {
|
|
||||||
address_handle = *entry.gathered;
|
|
||||||
}
|
|
||||||
entry.address = device.GetLogical().GetBufferDeviceAddress(address_handle);
|
|
||||||
}
|
|
||||||
|
|
||||||
MultiRangeRef ref{
|
|
||||||
.handle = *entry.sparse_handle,
|
|
||||||
.address = entry.address,
|
|
||||||
.size = entry.size,
|
|
||||||
.sparse = true,
|
|
||||||
.needs_gather = false,
|
|
||||||
};
|
|
||||||
if (!entry.sparse_handle) {
|
|
||||||
ref.handle = *entry.gathered;
|
|
||||||
ref.sparse = false;
|
|
||||||
ref.needs_gather = true;
|
|
||||||
}
|
|
||||||
entries.emplace(key, std::move(entry));
|
|
||||||
return ref;
|
|
||||||
}
|
|
||||||
|
|
||||||
void MultiRangeBufferCache::MarkGathered(u64 key) {
|
|
||||||
if (auto const it = entries.find(key); it != entries.end()) {
|
|
||||||
it->second.dirty = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void MultiRangeBufferCache::DropOwner(Scheduler& scheduler, VkBuffer owner) {
|
|
||||||
if (owner == VK_NULL_HANDLE) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
for (auto it = entries.begin(); it != entries.end();) {
|
|
||||||
Entry& entry = it->second;
|
|
||||||
bool owned = false;
|
|
||||||
for (const VkBuffer handle : entry.owners) {
|
|
||||||
if (handle == owner) {
|
|
||||||
owned = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!owned) {
|
|
||||||
++it;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
RetireEntry(scheduler, entry);
|
|
||||||
it = entries.erase(it);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void MultiRangeBufferCache::Invalidate(u64 key) {
|
|
||||||
if (auto const it = entries.find(key); it != entries.end()) {
|
|
||||||
it->second.dirty = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace Vulkan
|
|
||||||
@@ -1,105 +0,0 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <span>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include <boost/container/static_vector.hpp>
|
|
||||||
|
|
||||||
#include "common/common_funcs.h"
|
|
||||||
#include "common/common_types.h"
|
|
||||||
#include "common/container/unordered_map.h"
|
|
||||||
#include "video_core/vulkan_common/vulkan_memory_allocator.h"
|
|
||||||
#include "video_core/vulkan_common/vulkan_wrapper.h"
|
|
||||||
|
|
||||||
namespace Vulkan {
|
|
||||||
|
|
||||||
using SparseBuffer = vk::Handle<VkBuffer, VkDevice, vk::DeviceDispatch>;
|
|
||||||
|
|
||||||
class Device;
|
|
||||||
class Scheduler;
|
|
||||||
|
|
||||||
struct MultiRangeSource {
|
|
||||||
VkBuffer handle{};
|
|
||||||
VkDeviceMemory memory{};
|
|
||||||
VkDeviceSize memory_offset{};
|
|
||||||
VkDeviceSize offset{};
|
|
||||||
VkDeviceSize size{};
|
|
||||||
u64 write_tick{};
|
|
||||||
u32 memory_type{};
|
|
||||||
};
|
|
||||||
|
|
||||||
struct MultiRangeRef {
|
|
||||||
VkBuffer handle{};
|
|
||||||
VkDeviceAddress address{};
|
|
||||||
VkDeviceSize size{};
|
|
||||||
bool sparse{};
|
|
||||||
bool needs_gather{};
|
|
||||||
};
|
|
||||||
|
|
||||||
class MultiRangeBufferCache final {
|
|
||||||
public:
|
|
||||||
static constexpr VkDeviceSize DEFAULT_BLOCK_SIZE = 64 * 1024;
|
|
||||||
static constexpr size_t MAX_RETIRED = 256;
|
|
||||||
|
|
||||||
explicit MultiRangeBufferCache(const Device& device);
|
|
||||||
|
|
||||||
YUZU_NON_COPYABLE(MultiRangeBufferCache);
|
|
||||||
|
|
||||||
[[nodiscard]] MultiRangeRef Get(const Device& device, Scheduler& scheduler,
|
|
||||||
MemoryAllocator& memory_allocator, u64 key,
|
|
||||||
std::span<const MultiRangeSource> sources,
|
|
||||||
VkDeviceSize total);
|
|
||||||
|
|
||||||
void MarkGathered(u64 key);
|
|
||||||
|
|
||||||
void Invalidate(u64 key);
|
|
||||||
|
|
||||||
void DropOwner(Scheduler& scheduler, VkBuffer owner);
|
|
||||||
|
|
||||||
VkDeviceSize block_size{DEFAULT_BLOCK_SIZE};
|
|
||||||
bool use_sparse{};
|
|
||||||
|
|
||||||
private:
|
|
||||||
struct Retired {
|
|
||||||
SparseBuffer handle;
|
|
||||||
vk::Buffer gathered;
|
|
||||||
u64 tick{};
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Entry {
|
|
||||||
vk::Buffer gathered;
|
|
||||||
SparseBuffer sparse_handle;
|
|
||||||
std::vector<VkBuffer> owners;
|
|
||||||
VkDeviceAddress address{};
|
|
||||||
VkDeviceSize size{};
|
|
||||||
u64 geometry{};
|
|
||||||
u64 content{};
|
|
||||||
bool dirty{true};
|
|
||||||
};
|
|
||||||
|
|
||||||
[[nodiscard]] u64 HashSources(std::span<const MultiRangeSource> sources) const;
|
|
||||||
|
|
||||||
[[nodiscard]] u64 HashContent(std::span<const MultiRangeSource> sources) const;
|
|
||||||
|
|
||||||
[[nodiscard]] bool CanBindSparse(std::span<const MultiRangeSource> sources) const;
|
|
||||||
|
|
||||||
[[nodiscard]] SparseBuffer CreateSparse(const Device& device, Scheduler& scheduler,
|
|
||||||
std::span<const MultiRangeSource> sources,
|
|
||||||
VkDeviceSize total);
|
|
||||||
|
|
||||||
[[nodiscard]] VkDeviceSize QueryBlockSize(const Device& device, u32& memory_type_bits) const;
|
|
||||||
|
|
||||||
void RetireEntry(Scheduler& scheduler, Entry& entry);
|
|
||||||
|
|
||||||
void DrainRetired(Scheduler& scheduler);
|
|
||||||
|
|
||||||
::Common::unordered_map<u64, Entry> entries;
|
|
||||||
boost::container::static_vector<Retired, MAX_RETIRED> retired;
|
|
||||||
u32 sparse_memory_type_bits{};
|
|
||||||
VkBufferUsageFlags sparse_usage{};
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace Vulkan
|
|
||||||
@@ -7,6 +7,7 @@
|
|||||||
#include "common/settings.h"
|
#include "common/settings.h"
|
||||||
#include "common/thread.h"
|
#include "common/thread.h"
|
||||||
#include "core/frontend/emu_window.h"
|
#include "core/frontend/emu_window.h"
|
||||||
|
#include "video_core/renderer_base.h"
|
||||||
#ifdef HAS_LSFG
|
#ifdef HAS_LSFG
|
||||||
#include "video_core/renderer_vulkan/present/lsfg_common.h"
|
#include "video_core/renderer_vulkan/present/lsfg_common.h"
|
||||||
#endif
|
#endif
|
||||||
@@ -29,9 +30,6 @@ static_assert(MAX_FRAMES_IN_FLIGHT <= LSFG_MAX_TARGETS);
|
|||||||
|
|
||||||
bool CanStoreToFrame(const vk::PhysicalDevice& physical_device, VkFormat format) {
|
bool CanStoreToFrame(const vk::PhysicalDevice& physical_device, VkFormat format) {
|
||||||
#ifdef HAS_LSFG
|
#ifdef HAS_LSFG
|
||||||
if (!Settings::values.frame_gen.GetValue()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
const VkFormatProperties props{physical_device.GetFormatProperties(format)};
|
const VkFormatProperties props{physical_device.GetFormatProperties(format)};
|
||||||
return (props.optimalTilingFeatures & VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT) != 0;
|
return (props.optimalTilingFeatures & VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT) != 0;
|
||||||
#else
|
#else
|
||||||
@@ -120,6 +118,7 @@ PresentManager::PresentManager(const vk::Instance& instance_,
|
|||||||
const Device& device_,
|
const Device& device_,
|
||||||
MemoryAllocator& memory_allocator_,
|
MemoryAllocator& memory_allocator_,
|
||||||
Scheduler& scheduler_,
|
Scheduler& scheduler_,
|
||||||
|
const VideoCore::RendererSettings& renderer_settings_,
|
||||||
Swapchain& swapchain_,
|
Swapchain& swapchain_,
|
||||||
vk::SurfaceKHR& surface_)
|
vk::SurfaceKHR& surface_)
|
||||||
: instance{instance_}
|
: instance{instance_}
|
||||||
@@ -127,13 +126,14 @@ PresentManager::PresentManager(const vk::Instance& instance_,
|
|||||||
, device{device_}
|
, device{device_}
|
||||||
, memory_allocator{memory_allocator_}
|
, memory_allocator{memory_allocator_}
|
||||||
, scheduler{scheduler_}
|
, scheduler{scheduler_}
|
||||||
|
, renderer_settings{renderer_settings_}
|
||||||
, swapchain{swapchain_}
|
, swapchain{swapchain_}
|
||||||
, surface{surface_}
|
, surface{surface_}
|
||||||
, blit_supported{CanBlitToSwapchain(device.GetPhysical(), swapchain.GetImageViewFormat())}
|
, blit_supported{CanBlitToSwapchain(device.GetPhysical(), swapchain.GetImageViewFormat())}
|
||||||
, storage_supported{CanStoreToFrame(device.GetPhysical(), swapchain.GetImageFormat())}
|
, storage_supported{CanStoreToFrame(device.GetPhysical(), swapchain.GetImageFormat())}
|
||||||
, use_present_thread{Settings::values.async_presentation.GetValue()}
|
, use_present_thread{Settings::values.async_presentation.GetValue()}
|
||||||
{
|
{
|
||||||
SetImageCount();
|
UpdateSwapchainImageCount();
|
||||||
|
|
||||||
auto& dld = device.GetLogical();
|
auto& dld = device.GetLogical();
|
||||||
cmdpool = dld.CreateCommandPool({
|
cmdpool = dld.CreateCommandPool({
|
||||||
@@ -143,9 +143,14 @@ PresentManager::PresentManager(const vk::Instance& instance_,
|
|||||||
VK_COMMAND_POOL_CREATE_TRANSIENT_BIT | VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT,
|
VK_COMMAND_POOL_CREATE_TRANSIENT_BIT | VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT,
|
||||||
.queueFamilyIndex = device.GetGraphicsFamily(),
|
.queueFamilyIndex = device.GetGraphicsFamily(),
|
||||||
});
|
});
|
||||||
auto cmdbuffers = cmdpool.Allocate(image_count);
|
#ifdef HAS_LSFG
|
||||||
|
constexpr size_t frame_capacity = MAX_FRAMES_IN_FLIGHT;
|
||||||
|
#else
|
||||||
|
const size_t frame_capacity = DesiredFrameCount();
|
||||||
|
#endif
|
||||||
|
auto cmdbuffers = cmdpool.Allocate(frame_capacity);
|
||||||
|
|
||||||
frames.resize(image_count);
|
frames.resize(frame_capacity);
|
||||||
for (u32 i = 0; i < frames.size(); i++) {
|
for (u32 i = 0; i < frames.size(); i++) {
|
||||||
Frame& frame = frames[i];
|
Frame& frame = frames[i];
|
||||||
frame.index = i;
|
frame.index = i;
|
||||||
@@ -174,7 +179,10 @@ Frame* PresentManager::GetRenderFrame() {
|
|||||||
|
|
||||||
// Wait for free presentation frames
|
// Wait for free presentation frames
|
||||||
std::unique_lock lock{free_mutex};
|
std::unique_lock lock{free_mutex};
|
||||||
free_cv.wait(lock, [this] { return !free_queue.empty(); });
|
free_cv.wait(lock, [this] {
|
||||||
|
const size_t outstanding = frames.size() - free_queue.size();
|
||||||
|
return !free_queue.empty() && outstanding < DesiredFrameCount();
|
||||||
|
});
|
||||||
|
|
||||||
// Take the frame from the queue
|
// Take the frame from the queue
|
||||||
Frame* frame = free_queue.front();
|
Frame* frame = free_queue.front();
|
||||||
@@ -202,7 +210,8 @@ void PresentManager::Present(Frame* frame) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
size_t PresentManager::MaxExtraFrames() const {
|
size_t PresentManager::MaxExtraFrames() const {
|
||||||
return image_count - 1;
|
const size_t frame_count = DesiredFrameCount();
|
||||||
|
return frame_count > 0 ? frame_count - 1 : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void PresentManager::RecreateFrame(Frame* frame, u32 width, u32 height, VkFormat image_view_format,
|
void PresentManager::RecreateFrame(Frame* frame, u32 width, u32 height, VkFormat image_view_format,
|
||||||
@@ -351,22 +360,30 @@ void PresentManager::PresentThread(std::stop_token token) {
|
|||||||
|
|
||||||
void PresentManager::RecreateSwapchain(Frame* frame) {
|
void PresentManager::RecreateSwapchain(Frame* frame) {
|
||||||
swapchain.Create(*surface, frame->width, frame->height); // Pass raw pointer
|
swapchain.Create(*surface, frame->width, frame->height); // Pass raw pointer
|
||||||
SetImageCount();
|
UpdateSwapchainImageCount();
|
||||||
}
|
}
|
||||||
|
|
||||||
void PresentManager::SetImageCount() {
|
void PresentManager::UpdateSwapchainImageCount() {
|
||||||
|
swapchain_image_count.store(
|
||||||
|
std::min<size_t>(swapchain.GetImageCount(), MAX_FRAMES_IN_FLIGHT),
|
||||||
|
std::memory_order_release);
|
||||||
|
free_cv.notify_all();
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t PresentManager::DesiredFrameCount() const {
|
||||||
// We cannot have more than 7 images in flight at any given time.
|
// We cannot have more than 7 images in flight at any given time.
|
||||||
// FRAMES_IN_FLIGHT is 8, and the cache TICKS_TO_DESTROY is 8.
|
// FRAMES_IN_FLIGHT is 8, and the cache TICKS_TO_DESTROY is 8.
|
||||||
// Mali drivers will give us 6.
|
// Mali drivers will give us 6.
|
||||||
|
const size_t minimum = swapchain_image_count.load(std::memory_order_acquire);
|
||||||
#ifdef HAS_LSFG
|
#ifdef HAS_LSFG
|
||||||
const size_t generations = Settings::FrameGenMaxGenerations();
|
const VideoCore::FrameGenConfig config = renderer_settings.GetFrameGenConfig();
|
||||||
const size_t queued_composites = Settings::values.frame_gen_queue_target.GetValue() + 1;
|
if (config.enabled) {
|
||||||
image_count =
|
const size_t queued_composites = std::clamp<size_t>(config.queue_target, 0, 2) + 1;
|
||||||
std::clamp<size_t>((generations + 1) * queued_composites, swapchain.GetImageCount(),
|
return std::clamp<size_t>((config.MaxGenerations() + 1) * queued_composites, minimum,
|
||||||
MAX_FRAMES_IN_FLIGHT);
|
MAX_FRAMES_IN_FLIGHT);
|
||||||
#else
|
}
|
||||||
image_count = std::min<size_t>(swapchain.GetImageCount(), MAX_FRAMES_IN_FLIGHT);
|
|
||||||
#endif
|
#endif
|
||||||
|
return minimum;
|
||||||
}
|
}
|
||||||
|
|
||||||
void PresentManager::CopyToSwapchain(Frame* frame) {
|
void PresentManager::CopyToSwapchain(Frame* frame) {
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <atomic>
|
||||||
#include <condition_variable>
|
#include <condition_variable>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <boost/container/deque.hpp>
|
#include <boost/container/deque.hpp>
|
||||||
@@ -19,6 +20,10 @@ namespace Core::Frontend {
|
|||||||
class EmuWindow;
|
class EmuWindow;
|
||||||
} // namespace Core::Frontend
|
} // namespace Core::Frontend
|
||||||
|
|
||||||
|
namespace VideoCore {
|
||||||
|
struct RendererSettings;
|
||||||
|
}
|
||||||
|
|
||||||
namespace Vulkan {
|
namespace Vulkan {
|
||||||
|
|
||||||
class Device;
|
class Device;
|
||||||
@@ -45,6 +50,7 @@ public:
|
|||||||
const Device& device,
|
const Device& device,
|
||||||
MemoryAllocator& memory_allocator,
|
MemoryAllocator& memory_allocator,
|
||||||
Scheduler& scheduler,
|
Scheduler& scheduler,
|
||||||
|
const VideoCore::RendererSettings& renderer_settings,
|
||||||
Swapchain& swapchain,
|
Swapchain& swapchain,
|
||||||
vk::SurfaceKHR& surface);
|
vk::SurfaceKHR& surface);
|
||||||
~PresentManager();
|
~PresentManager();
|
||||||
@@ -74,7 +80,9 @@ private:
|
|||||||
|
|
||||||
void RecreateSwapchain(Frame* frame);
|
void RecreateSwapchain(Frame* frame);
|
||||||
|
|
||||||
void SetImageCount();
|
void UpdateSwapchainImageCount();
|
||||||
|
|
||||||
|
[[nodiscard]] size_t DesiredFrameCount() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
const vk::Instance& instance;
|
const vk::Instance& instance;
|
||||||
@@ -82,6 +90,7 @@ private:
|
|||||||
const Device& device;
|
const Device& device;
|
||||||
MemoryAllocator& memory_allocator;
|
MemoryAllocator& memory_allocator;
|
||||||
Scheduler& scheduler;
|
Scheduler& scheduler;
|
||||||
|
const VideoCore::RendererSettings& renderer_settings;
|
||||||
Swapchain& swapchain;
|
Swapchain& swapchain;
|
||||||
vk::SurfaceKHR& surface;
|
vk::SurfaceKHR& surface;
|
||||||
vk::CommandPool cmdpool;
|
vk::CommandPool cmdpool;
|
||||||
@@ -97,7 +106,7 @@ private:
|
|||||||
bool blit_supported;
|
bool blit_supported;
|
||||||
bool storage_supported;
|
bool storage_supported;
|
||||||
bool use_present_thread;
|
bool use_present_thread;
|
||||||
std::size_t image_count{};
|
std::atomic_size_t swapchain_image_count{};
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Vulkan
|
} // namespace Vulkan
|
||||||
|
|||||||
@@ -819,7 +819,6 @@ void RasterizerVulkan::ModifyGPUMemory(size_t as_id, GPUVAddr addr, u64 size) {
|
|||||||
std::scoped_lock lock{texture_cache.mutex};
|
std::scoped_lock lock{texture_cache.mutex};
|
||||||
texture_cache.UnmapGPUMemory(as_id, addr, size);
|
texture_cache.UnmapGPUMemory(as_id, addr, size);
|
||||||
}
|
}
|
||||||
buffer_cache.UnmapGPUMemory(as_id, addr, size);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void RasterizerVulkan::SignalFence(std::function<void()>&& func) {
|
void RasterizerVulkan::SignalFence(std::function<void()>&& func) {
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
#include "common/logging.h"
|
#include "common/logging.h"
|
||||||
#include "common/settings.h"
|
#include "common/settings.h"
|
||||||
#include "common/settings_enums.h"
|
#include "common/settings_enums.h"
|
||||||
|
#include "video_core/renderer_base.h"
|
||||||
#include "video_core/renderer_vulkan/vk_scheduler.h"
|
#include "video_core/renderer_vulkan/vk_scheduler.h"
|
||||||
#include "video_core/renderer_vulkan/vk_swapchain.h"
|
#include "video_core/renderer_vulkan/vk_swapchain.h"
|
||||||
#include "video_core/vulkan_common/vk_enum_string_helper.h"
|
#include "video_core/vulkan_common/vk_enum_string_helper.h"
|
||||||
@@ -42,13 +43,13 @@ VkSurfaceFormatKHR ChooseSwapSurfaceFormat(vk::Span<VkSurfaceFormatKHR> formats)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static VkPresentModeKHR ChooseSwapPresentMode(bool has_imm, bool has_mailbox,
|
static VkPresentModeKHR ChooseSwapPresentMode(bool has_imm, bool has_mailbox,
|
||||||
bool has_fifo_relaxed) {
|
bool has_fifo_relaxed, bool frame_gen_enabled) {
|
||||||
// Mailbox doesn't lock the application like FIFO (vsync)
|
// Mailbox doesn't lock the application like FIFO (vsync)
|
||||||
// FIFO present mode locks the framerate to the monitor's refresh rate
|
// FIFO present mode locks the framerate to the monitor's refresh rate
|
||||||
Settings::VSyncMode setting = [has_imm, has_mailbox]() {
|
Settings::VSyncMode setting = [has_imm, has_mailbox, frame_gen_enabled]() {
|
||||||
// Choose Mailbox or Immediate if unlocked and those modes are supported
|
// Choose Mailbox or Immediate if unlocked and those modes are supported
|
||||||
const auto mode = Settings::values.vsync_mode.GetValue();
|
const auto mode = Settings::values.vsync_mode.GetValue();
|
||||||
if (Settings::values.frame_gen.GetValue()) {
|
if (frame_gen_enabled) {
|
||||||
return mode == Settings::VSyncMode::FifoRelaxed ? mode : Settings::VSyncMode::Fifo;
|
return mode == Settings::VSyncMode::FifoRelaxed ? mode : Settings::VSyncMode::Fifo;
|
||||||
}
|
}
|
||||||
if (Settings::values.use_speed_limit.GetValue() &&
|
if (Settings::values.use_speed_limit.GetValue() &&
|
||||||
@@ -121,11 +122,13 @@ Swapchain::Swapchain(
|
|||||||
VkSurfaceKHR_T* surface_,
|
VkSurfaceKHR_T* surface_,
|
||||||
const Device& device_,
|
const Device& device_,
|
||||||
Scheduler& scheduler_,
|
Scheduler& scheduler_,
|
||||||
|
const VideoCore::RendererSettings& renderer_settings_,
|
||||||
u32 width_,
|
u32 width_,
|
||||||
u32 height_)
|
u32 height_)
|
||||||
: surface(surface_)
|
: surface(surface_)
|
||||||
, device{device_}
|
, device{device_}
|
||||||
, scheduler{scheduler_}
|
, scheduler{scheduler_}
|
||||||
|
, renderer_settings{renderer_settings_}
|
||||||
{
|
{
|
||||||
Create(surface, width_, height_);
|
Create(surface, width_, height_);
|
||||||
}
|
}
|
||||||
@@ -263,7 +266,8 @@ void Swapchain::CreateSwapchain(const VkSurfaceCapabilitiesKHR& capabilities) {
|
|||||||
|
|
||||||
const VkCompositeAlphaFlagBitsKHR alpha_flags{ChooseAlphaFlags(capabilities)};
|
const VkCompositeAlphaFlagBitsKHR alpha_flags{ChooseAlphaFlags(capabilities)};
|
||||||
surface_format = ChooseSwapSurfaceFormat(formats);
|
surface_format = ChooseSwapSurfaceFormat(formats);
|
||||||
present_mode = ChooseSwapPresentMode(has_imm, has_mailbox, has_fifo_relaxed);
|
present_mode = ChooseSwapPresentMode(has_imm, has_mailbox, has_fifo_relaxed,
|
||||||
|
renderer_settings.GetFrameGenConfig().enabled);
|
||||||
|
|
||||||
u32 requested_image_count{capabilities.minImageCount + 1};
|
u32 requested_image_count{capabilities.minImageCount + 1};
|
||||||
// Ensure Triple buffering if possible.
|
// Ensure Triple buffering if possible.
|
||||||
@@ -366,7 +370,9 @@ void Swapchain::Destroy() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool Swapchain::NeedsPresentModeUpdate() const {
|
bool Swapchain::NeedsPresentModeUpdate() const {
|
||||||
const auto requested_mode = ChooseSwapPresentMode(has_imm, has_mailbox, has_fifo_relaxed);
|
const auto requested_mode = ChooseSwapPresentMode(
|
||||||
|
has_imm, has_mailbox, has_fifo_relaxed,
|
||||||
|
renderer_settings.GetFrameGenConfig().enabled);
|
||||||
return present_mode != requested_mode;
|
return present_mode != requested_mode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -15,6 +15,10 @@ namespace Layout {
|
|||||||
struct FramebufferLayout;
|
struct FramebufferLayout;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace VideoCore {
|
||||||
|
struct RendererSettings;
|
||||||
|
}
|
||||||
|
|
||||||
namespace Vulkan {
|
namespace Vulkan {
|
||||||
|
|
||||||
class Device;
|
class Device;
|
||||||
@@ -26,6 +30,7 @@ public:
|
|||||||
VkSurfaceKHR_T* surface,
|
VkSurfaceKHR_T* surface,
|
||||||
const Device& device,
|
const Device& device,
|
||||||
Scheduler& scheduler,
|
Scheduler& scheduler,
|
||||||
|
const VideoCore::RendererSettings& renderer_settings,
|
||||||
u32 width,
|
u32 width,
|
||||||
u32 height);
|
u32 height);
|
||||||
~Swapchain();
|
~Swapchain();
|
||||||
@@ -122,6 +127,7 @@ private:
|
|||||||
|
|
||||||
const Device& device;
|
const Device& device;
|
||||||
Scheduler& scheduler;
|
Scheduler& scheduler;
|
||||||
|
const VideoCore::RendererSettings& renderer_settings;
|
||||||
|
|
||||||
vk::SwapchainKHR swapchain;
|
vk::SwapchainKHR swapchain;
|
||||||
|
|
||||||
|
|||||||
@@ -1570,8 +1570,6 @@ void Device::SetupFamilies(VkSurfaceKHR surface) {
|
|||||||
}
|
}
|
||||||
if (graphics) {
|
if (graphics) {
|
||||||
graphics_family = *graphics;
|
graphics_family = *graphics;
|
||||||
graphics_family_sparse_binding =
|
|
||||||
(queue_family_properties[*graphics].queueFlags & VK_QUEUE_SPARSE_BINDING_BIT) != 0;
|
|
||||||
}
|
}
|
||||||
if (present) {
|
if (present) {
|
||||||
present_family = *present;
|
present_family = *present;
|
||||||
|
|||||||
@@ -317,10 +317,6 @@ public:
|
|||||||
return properties.driver.driverID;
|
return properties.driver.driverID;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IsSparseBindingSupported() const {
|
|
||||||
return features.features.sparseBinding && graphics_family_sparse_binding;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns true for tile-based deferred renderers.
|
/// Returns true for tile-based deferred renderers.
|
||||||
bool IsTiler() const {
|
bool IsTiler() const {
|
||||||
switch (GetDriverID()) {
|
switch (GetDriverID()) {
|
||||||
@@ -1151,7 +1147,6 @@ private:
|
|||||||
u32 instance_version{}; ///< Vulkan instance version.
|
u32 instance_version{}; ///< Vulkan instance version.
|
||||||
u32 graphics_family{}; ///< Main graphics queue family index.
|
u32 graphics_family{}; ///< Main graphics queue family index.
|
||||||
u32 present_family{}; ///< Main present queue family index.
|
u32 present_family{}; ///< Main present queue family index.
|
||||||
bool graphics_family_sparse_binding{};
|
|
||||||
|
|
||||||
struct Extensions {
|
struct Extensions {
|
||||||
#define EXTENSION(prefix, macro_name, var_name) bool var_name{};
|
#define EXTENSION(prefix, macro_name, var_name) bool var_name{};
|
||||||
|
|||||||
@@ -275,63 +275,9 @@ vk::Buffer MemoryAllocator::CreateBuffer(const VkBufferCreateInfo &ci, MemoryUsa
|
|||||||
const std::span<u8> mapped_data = data ? std::span<u8>{data, ci.size} : std::span<u8>{};
|
const std::span<u8> mapped_data = data ? std::span<u8>{data, ci.size} : std::span<u8>{};
|
||||||
const bool is_coherent = (property_flags & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT) != 0;
|
const bool is_coherent = (property_flags & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT) != 0;
|
||||||
|
|
||||||
const vk::MemoryLocation location{
|
return vk::Buffer(handle, *device.GetLogical(), allocator, allocation, mapped_data,
|
||||||
.memory = alloc_info.deviceMemory,
|
is_coherent,
|
||||||
.offset = alloc_info.offset,
|
device.GetDispatchLoader());
|
||||||
.memory_type = alloc_info.memoryType,
|
|
||||||
};
|
|
||||||
return vk::Buffer(handle, *device.GetLogical(), allocator, allocation, mapped_data, is_coherent,
|
|
||||||
location, device.GetDispatchLoader());
|
|
||||||
}
|
|
||||||
|
|
||||||
vk::Buffer MemoryAllocator::CreateBuffer(const VkBufferCreateInfo &ci, MemoryUsage usage,
|
|
||||||
VkDeviceSize min_alignment) const {
|
|
||||||
if (min_alignment <= 1) {
|
|
||||||
return CreateBuffer(ci, usage);
|
|
||||||
}
|
|
||||||
VkMemoryPropertyFlags anv_flags = 0;
|
|
||||||
if (usage == MemoryUsage::Stream &&
|
|
||||||
device.GetDriverID() == VK_DRIVER_ID_INTEL_OPEN_SOURCE_MESA) {
|
|
||||||
anv_flags = VK_MEMORY_PROPERTY_HOST_CACHED_BIT;
|
|
||||||
}
|
|
||||||
u32 memory_type_bits = valid_memory_types;
|
|
||||||
if (usage == MemoryUsage::Stream) {
|
|
||||||
memory_type_bits = 0u;
|
|
||||||
}
|
|
||||||
const VmaAllocationCreateInfo alloc_ci = {
|
|
||||||
.flags = VMA_ALLOCATION_CREATE_WITHIN_BUDGET_BIT | MemoryUsageVmaFlags(usage),
|
|
||||||
.usage = MemoryUsageVma(usage),
|
|
||||||
.requiredFlags = 0,
|
|
||||||
.preferredFlags = MemoryUsagePreferredVmaFlags(usage) | anv_flags,
|
|
||||||
.memoryTypeBits = memory_type_bits,
|
|
||||||
.pool = VK_NULL_HANDLE,
|
|
||||||
.pUserData = nullptr,
|
|
||||||
.priority = 0.f,
|
|
||||||
};
|
|
||||||
|
|
||||||
VkBuffer handle{};
|
|
||||||
VmaAllocationInfo alloc_info{};
|
|
||||||
VmaAllocation allocation{};
|
|
||||||
VkMemoryPropertyFlags property_flags{};
|
|
||||||
|
|
||||||
vk::Check(vmaCreateBufferWithAlignment(allocator, &ci, &alloc_ci, min_alignment, &handle,
|
|
||||||
&allocation, &alloc_info));
|
|
||||||
vmaGetAllocationMemoryProperties(allocator, allocation, &property_flags);
|
|
||||||
|
|
||||||
u8 *data = reinterpret_cast<u8 *>(alloc_info.pMappedData);
|
|
||||||
std::span<u8> mapped_data{};
|
|
||||||
if (data) {
|
|
||||||
mapped_data = std::span<u8>{data, ci.size};
|
|
||||||
}
|
|
||||||
const bool is_coherent = (property_flags & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT) != 0;
|
|
||||||
|
|
||||||
const vk::MemoryLocation location{
|
|
||||||
.memory = alloc_info.deviceMemory,
|
|
||||||
.offset = alloc_info.offset,
|
|
||||||
.memory_type = alloc_info.memoryType,
|
|
||||||
};
|
|
||||||
return vk::Buffer(handle, *device.GetLogical(), allocator, allocation, mapped_data, is_coherent,
|
|
||||||
location, device.GetDispatchLoader());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
MemoryCommit MemoryAllocator::Commit(const VkMemoryRequirements &reqs, MemoryUsage usage)
|
MemoryCommit MemoryAllocator::Commit(const VkMemoryRequirements &reqs, MemoryUsage usage)
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
|
||||||
@@ -107,9 +107,6 @@ namespace Vulkan {
|
|||||||
|
|
||||||
vk::Buffer CreateBuffer(const VkBufferCreateInfo &ci, MemoryUsage usage) const;
|
vk::Buffer CreateBuffer(const VkBufferCreateInfo &ci, MemoryUsage usage) const;
|
||||||
|
|
||||||
vk::Buffer CreateBuffer(const VkBufferCreateInfo &ci, MemoryUsage usage,
|
|
||||||
VkDeviceSize min_alignment) const;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Commits a memory with the specified requirements.
|
* Commits a memory with the specified requirements.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -229,7 +229,6 @@ void Load(VkDevice device, DeviceDispatch& dld) noexcept {
|
|||||||
X(vkGetPipelineExecutableStatisticsKHR);
|
X(vkGetPipelineExecutableStatisticsKHR);
|
||||||
X(vkGetSemaphoreCounterValue);
|
X(vkGetSemaphoreCounterValue);
|
||||||
X(vkMapMemory);
|
X(vkMapMemory);
|
||||||
X(vkQueueBindSparse);
|
|
||||||
X(vkQueueSubmit);
|
X(vkQueueSubmit);
|
||||||
X(vkQueueSubmit2);
|
X(vkQueueSubmit2);
|
||||||
X(vkResetFences);
|
X(vkResetFences);
|
||||||
@@ -460,20 +459,8 @@ Instance Instance::Create(u32 version, Span<const char*> layers, Span<const char
|
|||||||
#else
|
#else
|
||||||
constexpr VkFlags ci_flags{};
|
constexpr VkFlags ci_flags{};
|
||||||
#endif
|
#endif
|
||||||
// DO NOT TOUCH OR CHANGE THE ENGINE NAME/APPLICATION NAME, breaks RNDA3!!
|
// DO NOT TOUCH, breaks RNDA3!!
|
||||||
// AMD drivers have fixes for Yuzu
|
// Don't know why, but gloom + yellow line glitch appears
|
||||||
// if remove => gloom + yellow line glitch appears
|
|
||||||
#ifdef __ANDROID__
|
|
||||||
const VkApplicationInfo application_info{
|
|
||||||
.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO,
|
|
||||||
.pNext = nullptr,
|
|
||||||
.pApplicationName = "PUBGMobile",
|
|
||||||
.applicationVersion = VK_MAKE_VERSION(1, 7, 0),
|
|
||||||
.pEngineName = "UnrealEngine",
|
|
||||||
.engineVersion = VK_MAKE_VERSION(4, 23, 0),
|
|
||||||
.apiVersion = VK_API_VERSION_1_3,
|
|
||||||
};
|
|
||||||
#else
|
|
||||||
const VkApplicationInfo application_info{
|
const VkApplicationInfo application_info{
|
||||||
.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO,
|
.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO,
|
||||||
.pNext = nullptr,
|
.pNext = nullptr,
|
||||||
@@ -483,7 +470,6 @@ Instance Instance::Create(u32 version, Span<const char*> layers, Span<const char
|
|||||||
.engineVersion = VK_MAKE_VERSION(1, 3, 0),
|
.engineVersion = VK_MAKE_VERSION(1, 3, 0),
|
||||||
.apiVersion = VK_API_VERSION_1_3,
|
.apiVersion = VK_API_VERSION_1_3,
|
||||||
};
|
};
|
||||||
#endif
|
|
||||||
const VkInstanceCreateInfo ci{
|
const VkInstanceCreateInfo ci{
|
||||||
.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO,
|
.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO,
|
||||||
.pNext = nullptr,
|
.pNext = nullptr,
|
||||||
|
|||||||
@@ -345,7 +345,6 @@ struct DeviceDispatch : InstanceDispatch {
|
|||||||
PFN_vkGetQueryPoolResults vkGetQueryPoolResults{};
|
PFN_vkGetQueryPoolResults vkGetQueryPoolResults{};
|
||||||
PFN_vkGetSemaphoreCounterValue vkGetSemaphoreCounterValue{};
|
PFN_vkGetSemaphoreCounterValue vkGetSemaphoreCounterValue{};
|
||||||
PFN_vkMapMemory vkMapMemory{};
|
PFN_vkMapMemory vkMapMemory{};
|
||||||
PFN_vkQueueBindSparse vkQueueBindSparse{};
|
|
||||||
PFN_vkQueueSubmit vkQueueSubmit{};
|
PFN_vkQueueSubmit vkQueueSubmit{};
|
||||||
PFN_vkQueueSubmit2 vkQueueSubmit2{};
|
PFN_vkQueueSubmit2 vkQueueSubmit2{};
|
||||||
PFN_vkResetFences vkResetFences{};
|
PFN_vkResetFences vkResetFences{};
|
||||||
@@ -741,20 +740,13 @@ private:
|
|||||||
const DeviceDispatch* dld = nullptr;
|
const DeviceDispatch* dld = nullptr;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct MemoryLocation {
|
|
||||||
VkDeviceMemory memory{};
|
|
||||||
VkDeviceSize offset{};
|
|
||||||
u32 memory_type{};
|
|
||||||
};
|
|
||||||
|
|
||||||
class Buffer {
|
class Buffer {
|
||||||
public:
|
public:
|
||||||
explicit Buffer(VkBuffer handle_, VkDevice owner_, VmaAllocator allocator_,
|
explicit Buffer(VkBuffer handle_, VkDevice owner_, VmaAllocator allocator_,
|
||||||
VmaAllocation allocation_, std::span<u8> mapped_, bool is_coherent_,
|
VmaAllocation allocation_, std::span<u8> mapped_, bool is_coherent_,
|
||||||
MemoryLocation location_, const DeviceDispatch& dld_) noexcept
|
const DeviceDispatch& dld_) noexcept
|
||||||
: handle{handle_}, owner{owner_}, allocator{allocator_},
|
: handle{handle_}, owner{owner_}, allocator{allocator_},
|
||||||
allocation{allocation_}, mapped{mapped_}, location{location_},
|
allocation{allocation_}, mapped{mapped_}, is_coherent{is_coherent_}, dld{&dld_} {}
|
||||||
is_coherent{is_coherent_}, dld{&dld_} {}
|
|
||||||
Buffer() = default;
|
Buffer() = default;
|
||||||
|
|
||||||
Buffer(const Buffer&) = delete;
|
Buffer(const Buffer&) = delete;
|
||||||
@@ -762,7 +754,7 @@ public:
|
|||||||
|
|
||||||
Buffer(Buffer&& rhs) noexcept
|
Buffer(Buffer&& rhs) noexcept
|
||||||
: handle{std::exchange(rhs.handle, VkBuffer{})}, owner{rhs.owner}, allocator{rhs.allocator},
|
: handle{std::exchange(rhs.handle, VkBuffer{})}, owner{rhs.owner}, allocator{rhs.allocator},
|
||||||
allocation{rhs.allocation}, mapped{rhs.mapped}, location{rhs.location},
|
allocation{rhs.allocation}, mapped{rhs.mapped},
|
||||||
is_coherent{rhs.is_coherent}, dld{rhs.dld} {}
|
is_coherent{rhs.is_coherent}, dld{rhs.dld} {}
|
||||||
|
|
||||||
Buffer& operator=(Buffer&& rhs) noexcept {
|
Buffer& operator=(Buffer&& rhs) noexcept {
|
||||||
@@ -772,7 +764,6 @@ public:
|
|||||||
allocator = rhs.allocator;
|
allocator = rhs.allocator;
|
||||||
allocation = rhs.allocation;
|
allocation = rhs.allocation;
|
||||||
mapped = rhs.mapped;
|
mapped = rhs.mapped;
|
||||||
location = rhs.location;
|
|
||||||
is_coherent = rhs.is_coherent;
|
is_coherent = rhs.is_coherent;
|
||||||
dld = rhs.dld;
|
dld = rhs.dld;
|
||||||
return *this;
|
return *this;
|
||||||
@@ -820,10 +811,6 @@ public:
|
|||||||
|
|
||||||
void SetObjectNameEXT(const char* name) const;
|
void SetObjectNameEXT(const char* name) const;
|
||||||
|
|
||||||
MemoryLocation Location() const noexcept {
|
|
||||||
return location;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void Release() const noexcept;
|
void Release() const noexcept;
|
||||||
|
|
||||||
@@ -832,7 +819,6 @@ private:
|
|||||||
VmaAllocator allocator = nullptr;
|
VmaAllocator allocator = nullptr;
|
||||||
VmaAllocation allocation = nullptr;
|
VmaAllocation allocation = nullptr;
|
||||||
std::span<u8> mapped = {};
|
std::span<u8> mapped = {};
|
||||||
MemoryLocation location{};
|
|
||||||
bool is_coherent = false;
|
bool is_coherent = false;
|
||||||
const DeviceDispatch* dld = nullptr;
|
const DeviceDispatch* dld = nullptr;
|
||||||
};
|
};
|
||||||
@@ -857,11 +843,6 @@ public:
|
|||||||
return dld->vkQueueSubmit2(queue, submit_infos.size(), submit_infos.data(), fence);
|
return dld->vkQueueSubmit2(queue, submit_infos.size(), submit_infos.data(), fence);
|
||||||
}
|
}
|
||||||
|
|
||||||
VkResult BindSparse(Span<VkBindSparseInfo> bind_infos,
|
|
||||||
VkFence fence = VK_NULL_HANDLE) const noexcept {
|
|
||||||
return dld->vkQueueBindSparse(queue, bind_infos.size(), bind_infos.data(), fence);
|
|
||||||
}
|
|
||||||
|
|
||||||
VkResult Present(const VkPresentInfoKHR& present_info) const noexcept {
|
VkResult Present(const VkPresentInfoKHR& present_info) const noexcept {
|
||||||
return dld->vkQueuePresentKHR(queue, &present_info);
|
return dld->vkQueuePresentKHR(queue, &present_info);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2936,10 +2936,10 @@ void PlayerControlPreview::DrawArrow(QPainter& p, const QPointF center, const Di
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Draw motion functions
|
// Draw motion functions
|
||||||
void PlayerControlPreview::Draw3dCube(QPainter& p, QPointF center, const Common::Vec<f32, 3>& euler,
|
void PlayerControlPreview::Draw3dCube(QPainter& p, QPointF center, const Common::Vec3f& euler,
|
||||||
float size) {
|
float size) {
|
||||||
std::array<Common::Vec<f32, 3>, 8> cube{
|
std::array<Common::Vec3f, 8> cube{
|
||||||
Common::Vec<f32, 3>{-0.7f, -1, -0.5f},
|
Common::Vec3f{-0.7f, -1, -0.5f},
|
||||||
{-0.7f, 1, -0.5f},
|
{-0.7f, 1, -0.5f},
|
||||||
{0.7f, 1, -0.5f},
|
{0.7f, 1, -0.5f},
|
||||||
{0.7f, -1, -0.5f},
|
{0.7f, -1, -0.5f},
|
||||||
@@ -2949,38 +2949,30 @@ void PlayerControlPreview::Draw3dCube(QPainter& p, QPointF center, const Common:
|
|||||||
{0.7f, -1, 0.5f},
|
{0.7f, -1, 0.5f},
|
||||||
};
|
};
|
||||||
|
|
||||||
for (Common::Vec<f32, 3>& point : cube) {
|
for (Common::Vec3f& point : cube) {
|
||||||
float temp = point[1];
|
point.RotateFromOrigin(euler.x, euler.y, euler.z);
|
||||||
point[1] = std::cos(euler[0]) * point[1] - std::sin(euler[0]) * point[2];
|
|
||||||
point[2] = std::sin(euler[0]) * temp + std::cos(euler[0]) * point[2];
|
|
||||||
temp = point[0];
|
|
||||||
point[0] = std::cos(euler[1]) * point[0] + std::sin(euler[1]) * point[2];
|
|
||||||
point[2] = -std::sin(euler[1]) * temp + std::cos(euler[1]) * point[2];
|
|
||||||
temp = point[0];
|
|
||||||
point[0] = std::cos(euler[2]) * point[0] - std::sin(euler[2]) * point[1];
|
|
||||||
point[1] = std::sin(euler[2]) * temp + std::cos(euler[2]) * point[1];
|
|
||||||
point *= size;
|
point *= size;
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::array<QPointF, 4> front_face{
|
const std::array<QPointF, 4> front_face{
|
||||||
center + QPointF{cube[0][0], cube[0][1]},
|
center + QPointF{cube[0].x, cube[0].y},
|
||||||
center + QPointF{cube[1][0], cube[1][1]},
|
center + QPointF{cube[1].x, cube[1].y},
|
||||||
center + QPointF{cube[2][0], cube[2][1]},
|
center + QPointF{cube[2].x, cube[2].y},
|
||||||
center + QPointF{cube[3][0], cube[3][1]},
|
center + QPointF{cube[3].x, cube[3].y},
|
||||||
};
|
};
|
||||||
const std::array<QPointF, 4> back_face{
|
const std::array<QPointF, 4> back_face{
|
||||||
center + QPointF{cube[4][0], cube[4][1]},
|
center + QPointF{cube[4].x, cube[4].y},
|
||||||
center + QPointF{cube[5][0], cube[5][1]},
|
center + QPointF{cube[5].x, cube[5].y},
|
||||||
center + QPointF{cube[6][0], cube[6][1]},
|
center + QPointF{cube[6].x, cube[6].y},
|
||||||
center + QPointF{cube[7][0], cube[7][1]},
|
center + QPointF{cube[7].x, cube[7].y},
|
||||||
};
|
};
|
||||||
|
|
||||||
DrawPolygon(p, front_face);
|
DrawPolygon(p, front_face);
|
||||||
DrawPolygon(p, back_face);
|
DrawPolygon(p, back_face);
|
||||||
p.drawLine(center + QPointF{cube[0][0], cube[0][1]}, center + QPointF{cube[4][0], cube[4][1]});
|
p.drawLine(center + QPointF{cube[0].x, cube[0].y}, center + QPointF{cube[4].x, cube[4].y});
|
||||||
p.drawLine(center + QPointF{cube[1][0], cube[1][1]}, center + QPointF{cube[5][0], cube[5][1]});
|
p.drawLine(center + QPointF{cube[1].x, cube[1].y}, center + QPointF{cube[5].x, cube[5].y});
|
||||||
p.drawLine(center + QPointF{cube[2][0], cube[2][1]}, center + QPointF{cube[6][0], cube[6][1]});
|
p.drawLine(center + QPointF{cube[2].x, cube[2].y}, center + QPointF{cube[6].x, cube[6].y});
|
||||||
p.drawLine(center + QPointF{cube[3][0], cube[3][1]}, center + QPointF{cube[7][0], cube[7][1]});
|
p.drawLine(center + QPointF{cube[3].x, cube[3].y}, center + QPointF{cube[7].x, cube[7].y});
|
||||||
}
|
}
|
||||||
|
|
||||||
template <size_t N>
|
template <size_t N>
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
|
||||||
@@ -198,7 +198,7 @@ private:
|
|||||||
void DrawArrow(QPainter& p, QPointF center, Direction direction, float size);
|
void DrawArrow(QPainter& p, QPointF center, Direction direction, float size);
|
||||||
|
|
||||||
// Draw motion functions
|
// Draw motion functions
|
||||||
void Draw3dCube(QPainter& p, QPointF center, const Common::Vec<f32, 3>& euler, float size);
|
void Draw3dCube(QPainter& p, QPointF center, const Common::Vec3f& euler, float size);
|
||||||
|
|
||||||
// Draw primitive types
|
// Draw primitive types
|
||||||
template <size_t N>
|
template <size_t N>
|
||||||
|
|||||||
Reference in New Issue
Block a user