Compare commits

..

85 Commits

Author SHA1 Message Date
lat9nq
596323f89f main: Don't add an extra separator when the title version is absent
Some titles, such as homebrew, do not have any version string. Because
yuzu hard codes the title bar string assuming a version string is
preset, booting homebrew causes yuzu to add an extra separator with no
content between.

This uses a lambda expression to prevent that from happening.
2021-09-30 18:57:37 -04:00
bunnei
8bd5742349 Merge pull request #7061 from ameerj/dma-buffer-misc
buffer_cache, maxwell_dma: Minor refactoring and code fixes
2021-09-30 12:34:28 -07:00
Mai M
b9251155f8 Merge pull request #7104 from Morph1984/style
style: Remove extra space preceding the :: operator
2021-09-29 19:32:39 -04:00
bunnei
91341b421d Merge pull request #7036 from ameerj/ogl-bgr-v2
gl_texture_cache: Unify BGR copy passes using PBOs
2021-09-29 16:10:56 -07:00
bunnei
9ad6c26821 Merge pull request #7106 from astrelsky/invalid_iterator_fix
Fixed invalid iterator usage
2021-09-29 09:35:01 -07:00
Andrew Strelsky
3fc7aceea7 Fixed invalid iterator usage 2021-09-29 06:58:48 -04:00
Morph
e29f3b87f1 style: Remove extra space preceding the :: operator 2021-09-29 01:26:01 -04:00
Morph
781c1d8df8 Merge pull request #7018 from lat9nq/splat-stubs
audin_u: stub Start, RegisterBufferEvent, AppendAudioInBufferAuto
2021-09-28 22:06:11 -04:00
Ameer J
fba301155b Merge pull request #7042 from v1993/patch-7
Hide XInput bypass on non-Windows OSes
2021-09-28 17:07:12 -04:00
bunnei
24c0dde958 Merge pull request #7076 from ameerj/amd-botw
vk_texture_cache: Disable cube compatibility flag on non-mesa AMD GCN4 and earlier
2021-09-28 10:46:56 -07:00
bunnei
90014ada8f Merge pull request #7096 from german77/update_13.0.0
Update function tables to FW 13.0.0
2021-09-27 11:44:17 -07:00
german77
bc4b4e3f56 service/es: Update to 13.0.0 2021-09-26 20:17:07 -05:00
german77
d780eab357 service/npns: Update to 13.0.0 2021-09-26 20:13:09 -05:00
german77
b0e83f949e service/vi: Update to 13.0.0 2021-09-26 20:12:02 -05:00
german77
321c64a122 service/am: Update to 13.0.0 2021-09-26 20:00:12 -05:00
german77
d103e2daf9 service/audio: Update to 13.0.0 2021-09-26 19:49:09 -05:00
german77
e401c77351 service/hid: Update to 13.0.0 2021-09-26 19:45:47 -05:00
german77
4b5f0af3fd service/btdrv: Update to 13.0.0 2021-09-26 19:32:45 -05:00
german77
f5b41068e6 service/usb: Update to 13.0.0 2021-09-26 19:26:52 -05:00
Morph
5114819b6b Merge pull request #7078 from ameerj/vc-jthread-fixes
video_core: Fix jthread related hangs when stopping emulation
2021-09-26 16:40:13 -04:00
bunnei
4f9d58621c Merge pull request #7085 from Morph1984/bsd-read-stub
service: bsd: Stub Read
2021-09-25 05:12:23 -07:00
Morph
7dd7c54add service: bsd: Stub Read
- Used by Diablo II: Resurrected
2021-09-25 08:04:33 -04:00
bunnei
db9b80399b Merge pull request #7082 from Morph1984/bsd-read
service: bsd: Implement Read
2021-09-24 22:34:32 -07:00
Morph
06f22c3d28 service: bsd: Implement Read
- Used by Diablo II: Resurrected
2021-09-24 16:46:52 -04:00
Morph
9a53173e4d Merge pull request #7084 from ameerj/clang-12
general: Update style to clang-format-12
2021-09-24 16:44:25 -04:00
ameerj
9266bad229 CMakeLists: Update to clang format version 12 2021-09-24 16:36:43 -04:00
ameerj
73666fb262 general: Update style to clang-format-12 2021-09-24 15:52:05 -04:00
ameerj
31c0f6ca33 ci: Update clang format version 2021-09-24 15:52:05 -04:00
Morph
c8512839d7 Merge pull request #7069 from lioncash/uuid
common/uuid: Add validity checking functions to interface
2021-09-24 08:02:11 -04:00
bunnei
4baef7905e Merge pull request #7043 from astrelsky/cmake
Fix "Unknown C standard control flag" warning
2021-09-23 19:07:09 -07:00
ameerj
ab63a193d7 video_core: Fix jthread related hangs when stopping emulation
jthread on some compilers is more picky when it comes to the order in which objects are destroyed.
2021-09-23 20:34:02 -04:00
ameerj
1e1ecca691 vk_texture_cache: Disable cube compatibility flag on non-mesa AMD GCN4 and earlier
Fixes rainbow textures on BOTW.
2021-09-23 19:40:53 -04:00
bunnei
326a449ef0 Merge pull request #7068 from behunin/patch-3
Debug Config Ui: Clean-up and nits
2021-09-23 15:19:22 -07:00
bunnei
17bf40f405 Merge pull request #7045 from behunin/patch-1
Tas configure ui nits
2021-09-23 09:29:55 -07:00
Lioncash
091e141142 core/profile_select: Avoid uninitialized read in SelectProfile()
The default constructor of UUID doesn't initialize its data members, so
we need to directly initialize it to be invalid.
2021-09-22 18:10:39 -04:00
Lioncash
40314cc586 common/uuid: Add validity checking functions to interface
Given we have a function to invalidate, we should also have ones to
query the validity. Also makes the code more straightforward to read.
2021-09-22 17:59:00 -04:00
Levi Behunin
2b3c9c61db Clean-up and nits
Remove redundent label, rearange checkboxs to keep same 3 per column layout, remove unneeded properties.
2021-09-22 00:56:03 -06:00
Levi Behunin
77e327dd1a Clean-up
Numerize names, remove unneeded properties and spacer.
2021-09-20 20:02:54 -06:00
bunnei
7b22d61fb1 Merge pull request #7003 from ameerj/unlocked-present-mode
vk_swapchain: Use immediate present mode when mailbox is unavailable and FPS is unlocked
2021-09-20 14:34:21 -07:00
Ameer J
cd973d6037 Merge pull request #7017 from FernandoS27/i-am-barbie-girl
Spir-V: Rescale the frag depth to 0,1 mode when -1,1 mode is used in Vulkan.
2021-09-19 23:56:08 -04:00
ameerj
24049591f6 maxwell_dma: Minor refactoring 2021-09-19 20:36:41 -04:00
ameerj
1ea8073783 buffer_cache: Minor fixes
Loop through the tmp_intervals by reference, rather than by copy, and fix gl clear buffer size calculation.
2021-09-19 20:35:07 -04:00
bunnei
a9c3619d26 Merge pull request #7019 from ameerj/videocore-jthread
videocore: Use std::jthread for worker threads
2021-09-18 20:37:40 -07:00
Levi Behunin
ad85689417 Tas configure ui nits
Text looked cramped on my pc (Ubuntu 21.04). Re-flowed text as well for nicer read.
2021-09-18 19:41:59 -06:00
bunnei
6e376c27a3 Merge pull request #6485 from MonsterDruide1/tas
input_common: TAS with script playback & recording
2021-09-18 16:30:06 -07:00
german77
75d8ec1e9f UI: Relocate tas menu and add brief description 2021-09-18 23:23:03 +02:00
german77
5401cf6eb5 input_common/tas: new update method 2021-09-18 23:22:57 +02:00
german77
33a1d790e8 input_common/tas: Document the main class 2021-09-18 23:22:48 +02:00
german77
e6c4bf52f0 input_common/tas: Add swap controller 2021-09-18 23:22:42 +02:00
german77
9bb6580d89 input_common/tas: overwrite file dialog 2021-09-18 23:22:42 +02:00
MonsterDruide1
f078b15565 input_common/tas: Fallback to simple update 2021-09-18 23:22:30 +02:00
german77
c01a872c8e config: Move TAS options to it's own menu 2021-09-18 23:22:30 +02:00
MonsterDruide1
4297d2fea2 core: Hacky TAS syncing & load pausing
To keep the TAS inputs synced to the game speed even through lag spikes and loading zones, deeper access is required.

First, the `TAS::UpdateThread` has to be executed exactly once per frame. This is done by connecting it to the service method the game calls to pass parameters to the GPU: `Service::VI::QueueBuffer`.

Second, the loading time of new subareas and/or kingdoms (SMO) can vary. To counteract that, the `CPU_BOOST_MODE` can be detected: In the `APM`-interface, the call to enabling/disabling the boost mode can be caught and forwarded to the TASing system, which can pause the script execution if neccessary and enabled in the settings.
2021-09-18 23:22:20 +02:00
MonsterDruide1
3a7b37238b main: TAS Playback state label
During script playback/recording, the user has to see what happens currently. For that, a new label has been added to the bottom-left corner, always displaying the current state of the TASing system.
2021-09-18 23:22:12 +02:00
MonsterDruide1
f25d6ebc45 settings: File selector & other settings
First of all, TASing requires a script to play back. The user can select the parent directory at `System -> Filesystem`, next to an option to pause TAS during loads: This requires a "hacky" setup deeper in the code and will be added in the last commit.

Also, Hotkeys are being introduced: CTRL+F5 for playback start/stop, CTRL+F6 for re-reading the script and CTRL+F7 for recording a new script.
2021-09-18 23:22:11 +02:00
MonsterDruide1
b42c3ce21d input_common/tas: Base playback & recording system
The base playback system supports up to 8 controllers (specified by `PLAYER_NUMBER` in `tas_input.h`), which all change their inputs simulataneously when `TAS::UpdateThread` is called.

The recording system uses the controller debugger to read the state of the first controller and forwards that data to the TASing system for recording. Currently, this process sadly is not frame-perfect and pixel-accurate.

Co-authored-by: Naii-the-Baf <sfabian200@gmail.com>
Co-authored-by: Narr-the-Reg <juangerman-13@hotmail.com>
2021-09-18 23:22:00 +02:00
bunnei
35f46fc079 Merge pull request #7020 from Moonlacer/remove_audio_stretching
Remove audio stretching
2021-09-18 11:18:24 -07:00
Andrew Strelsky
25cd0342c4 Fix "Unknown C standard control flag" warning 2021-09-18 08:37:34 -04:00
Valeri
738cd1896b If not on Windows, disable raw input
This way, if someone copies their Windows config to other OS, they won't be stuck without web applet for no apparent reason.
2021-09-18 15:10:00 +03:00
Valeri
b54bf126f7 Hide XInput bypass on non-Windows OSes
Follow-up to #6950. This option is a no-op on other OSes and only serves to spread confusion there.
2021-09-18 14:41:51 +03:00
bunnei
6d7801deb7 Merge pull request #6950 from german77/multiplay
input_common: Add advanced setting for 8 player support
2021-09-17 17:25:51 -07:00
bunnei
d4ee94165f Merge pull request #7015 from german77/NotGoodForTerra
ngct: Stub Match
2021-09-17 10:58:55 -07:00
bunnei
ff54d9615f Merge pull request #7011 from ameerj/vk-validation-0x0
vulkan_debug_callback: Ignore InvalidCommandBuffer-VkDescriptorSet errors
2021-09-16 17:17:04 -07:00
ameerj
22162f906b host_shaders: Remove opengl_copy_bgra.comp 2021-09-16 19:49:13 -04:00
ameerj
ab808fe7cf gl_texture_cache: Migrate BGRCopyPass from util_shaders
The BGR copies no longer use shaders.
2021-09-16 19:49:12 -04:00
Fernando S
a606b1448b Merge pull request #7027 from ameerj/sorry-amd
vulkan_device: Reorder Float16Int8 declaration
2021-09-16 07:05:58 +02:00
ameerj
e3c153efa4 vulkan_device: Reorder Float16Int8 declaration
This variable was going out of scope before its usage in the vulkan device creation, causing a crash on very specific drivers.
2021-09-16 00:54:24 -04:00
ameerj
5601e1cb00 Revert "Merge pull request #7006 from FernandoS27/a-motherfucking-driver"
This reverts commit 62e88d0e74, reversing
changes made to edf3da346f.
2021-09-16 00:51:22 -04:00
bunnei
f6d5444293 Merge pull request #7010 from Morph1984/fs-timestamp
vfs: Partially implement GetFileTimeStampRaw
2021-09-15 20:13:26 -07:00
Moonlacer
371feaa635 fix_clang_error 2021-09-15 20:20:45 -05:00
ameerj
7f737b022a util_shaders: Unify BGRA copy passes 2021-09-15 21:10:52 -04:00
Moonlacer
09ab819040 fix_accidental_deletion 2021-09-15 19:59:10 -05:00
Moonlacer
44135b011f remove-audio-stretching-setting 2021-09-15 19:52:43 -05:00
ameerj
84f7e7e91c vk_scheduler: Use std::jthread 2021-09-15 20:49:07 -04:00
ameerj
877cd60b00 gpu: Use std::jthread for async gpu thread 2021-09-15 20:49:07 -04:00
ameerj
c2ddda2f51 threadsafe_queue: Add std::stop_token overload to PopWait
Useful for jthreads which make use of the threadsafe queues.
2021-09-15 20:49:07 -04:00
Ameer J
e4d9814ec4 Merge pull request #7014 from Morph1984/log-pipeline-count
renderers: Log total pipeline count
2021-09-15 19:44:08 -04:00
lat9nq
7bc07195c5 audin_u: Return a buffer event in RegisterBufferEvent
Co-authored-by: Morph <39850852+Morph1984@users.noreply.github.com>
2021-09-15 16:38:12 -04:00
lat9nq
17b0955f9a audin_u: stub Start, RegisterBufferEvent, AppendAudioInBufferAuto
This also moves IAudioIn's definition to the header.

Required for Splatoon 2 LAN play.
2021-09-15 15:52:09 -04:00
Fernando Sahmkow
8984abfc76 Spir-V: Rescale the frag depth to 0,1 mode when -1,1 mode is used in Vulkan. 2021-09-15 21:30:33 +02:00
Narr the Reg
b34b3efbb2 ngct: Stub Match
Needed for  Cruis'n Blast
2021-09-15 00:17:31 -05:00
Morph
8d63ebcb64 vfs: Partially implement GetFileTimeStampRaw
Gets rid of homebrew warnings using this func
2021-09-14 08:48:01 -04:00
ameerj
db1c4b125f vulkan_debug_callback: Ignore InvalidCommandBuffer-VkDescriptorSet errors
This validation error is spammed on some titles, asserting that VkDescriptorSet 0x0[] was destroyed.

This is likely a validation layer bug when using VK_KHR_push_descriptor, which can avoid using traditional VkDescriptorSet. It should be safe to ignore for now.
2021-09-13 23:08:59 -04:00
ameerj
35e5a67a83 vk_swapchain: Use immediate present mode when mailbox is unavailable and FPS is unlocked
Allows drivers that do not support VK_PRESENT_MODE_MAILBOX_KHR the ability to present at a framerate higher than the monitor's refresh rate when the FPS is unlocked.
2021-09-12 20:32:23 -04:00
german77
5798537ce4 input_common: Enable steam controllers and 8 player support 2021-09-10 00:58:12 -05:00
115 changed files with 2031 additions and 582 deletions

View File

@@ -7,7 +7,7 @@ if grep -nrI '\s$' src *.yml *.txt *.md Doxyfile .gitignore .gitmodules .ci* dis
fi
# Default clang-format points to default 3.5 version one
CLANG_FORMAT=clang-format-10
CLANG_FORMAT=clang-format-12
$CLANG_FORMAT --version
if [ "$TRAVIS_EVENT_TYPE" = "pull_request" ]; then

View File

@@ -135,7 +135,7 @@ endif()
# boost asio's concept usage doesn't play nicely with some compilers yet.
add_definitions(-DBOOST_ASIO_DISABLE_CONCEPTS)
if (MSVC)
add_compile_options(/std:c++latest)
add_compile_options($<$<COMPILE_LANGUAGE:CXX>:/std:c++latest>)
# cubeb and boost still make use of deprecated result_of.
add_definitions(-D_HAS_DEPRECATED_RESULT_OF)
@@ -780,7 +780,7 @@ endif()
# against all the src files. This should be used before making a pull request.
# =======================================================================
set(CLANG_FORMAT_POSTFIX "-10")
set(CLANG_FORMAT_POSTFIX "-12")
find_program(CLANG_FORMAT
NAMES clang-format${CLANG_FORMAT_POSTFIX}
clang-format

View File

@@ -9,41 +9,48 @@
namespace Common {
template <typename T>
requires std::is_unsigned_v<T>[[nodiscard]] constexpr T AlignUp(T value, size_t size) {
requires std::is_unsigned_v<T>
[[nodiscard]] constexpr T AlignUp(T value, size_t size) {
auto mod{static_cast<T>(value % size)};
value -= mod;
return static_cast<T>(mod == T{0} ? value : value + size);
}
template <typename T>
requires std::is_unsigned_v<T>[[nodiscard]] constexpr T AlignUpLog2(T value, size_t align_log2) {
requires std::is_unsigned_v<T>
[[nodiscard]] constexpr T AlignUpLog2(T value, size_t align_log2) {
return static_cast<T>((value + ((1ULL << align_log2) - 1)) >> align_log2 << align_log2);
}
template <typename T>
requires std::is_unsigned_v<T>[[nodiscard]] constexpr T AlignDown(T value, size_t size) {
requires std::is_unsigned_v<T>
[[nodiscard]] constexpr T AlignDown(T value, size_t size) {
return static_cast<T>(value - value % size);
}
template <typename T>
requires std::is_unsigned_v<T>[[nodiscard]] constexpr bool Is4KBAligned(T value) {
requires std::is_unsigned_v<T>
[[nodiscard]] constexpr bool Is4KBAligned(T value) {
return (value & 0xFFF) == 0;
}
template <typename T>
requires std::is_unsigned_v<T>[[nodiscard]] constexpr bool IsWordAligned(T value) {
requires std::is_unsigned_v<T>
[[nodiscard]] constexpr bool IsWordAligned(T value) {
return (value & 0b11) == 0;
}
template <typename T>
requires std::is_integral_v<T>[[nodiscard]] constexpr bool IsAligned(T value, size_t alignment) {
requires std::is_integral_v<T>
[[nodiscard]] constexpr bool IsAligned(T value, size_t alignment) {
using U = typename std::make_unsigned_t<T>;
const U mask = static_cast<U>(alignment - 1);
return (value & mask) == 0;
}
template <typename T, typename U>
requires std::is_integral_v<T>[[nodiscard]] constexpr T DivideUp(T x, U y) {
requires std::is_integral_v<T>
[[nodiscard]] constexpr T DivideUp(T x, U y) {
return (x + (y - 1)) / y;
}

View File

@@ -11,15 +11,15 @@ namespace Common {
/// Ceiled integer division.
template <typename N, typename D>
requires std::is_integral_v<N>&& std::is_unsigned_v<D>[[nodiscard]] constexpr N DivCeil(N number,
D divisor) {
requires std::is_integral_v<N> && std::is_unsigned_v<D>
[[nodiscard]] constexpr N DivCeil(N number, D divisor) {
return static_cast<N>((static_cast<D>(number) + divisor - 1) / divisor);
}
/// Ceiled integer division with logarithmic divisor in base 2
template <typename N, typename D>
requires std::is_integral_v<N>&& std::is_unsigned_v<D>[[nodiscard]] constexpr N DivCeilLog2(
N value, D alignment_log2) {
requires std::is_integral_v<N> && std::is_unsigned_v<D>
[[nodiscard]] constexpr N DivCeilLog2(N value, D alignment_log2) {
return static_cast<N>((static_cast<D>(value) + (D(1) << alignment_log2) - 1) >> alignment_log2);
}

View File

@@ -21,6 +21,7 @@
#define SCREENSHOTS_DIR "screenshots"
#define SDMC_DIR "sdmc"
#define SHADER_DIR "shader"
#define TAS_DIR "tas"
// yuzu-specific files

View File

@@ -116,6 +116,7 @@ private:
GenerateYuzuPath(YuzuPath::ScreenshotsDir, yuzu_path / SCREENSHOTS_DIR);
GenerateYuzuPath(YuzuPath::SDMCDir, yuzu_path / SDMC_DIR);
GenerateYuzuPath(YuzuPath::ShaderDir, yuzu_path / SHADER_DIR);
GenerateYuzuPath(YuzuPath::TASDir, yuzu_path / TAS_DIR);
}
~PathManagerImpl() = default;

View File

@@ -23,6 +23,7 @@ enum class YuzuPath {
ScreenshotsDir, // Where yuzu screenshots are stored.
SDMCDir, // Where the emulated SDMC is stored.
ShaderDir, // Where shaders are stored.
TASDir, // Where TAS scripts are stored.
};
/**

View File

@@ -314,8 +314,8 @@ private:
}
void UntrackPlaceholder(boost::icl::separate_interval_set<size_t>::iterator it) {
placeholders.erase(it);
placeholder_host_pointers.erase(it->lower());
placeholders.erase(it);
}
/// Return true when a given memory region is a "nieche" and the placeholders don't have to be

View File

@@ -235,20 +235,19 @@ public:
template <typename T>
concept HasLightCompareType = requires {
{ std::is_same<typename T::LightCompareType, void>::value }
->std::convertible_to<bool>;
{ std::is_same<typename T::LightCompareType, void>::value } -> std::convertible_to<bool>;
};
namespace impl {
template <typename T, typename Default>
consteval auto* GetLightCompareType() {
if constexpr (HasLightCompareType<T>) {
return static_cast<typename T::LightCompareType*>(nullptr);
} else {
return static_cast<Default*>(nullptr);
template <typename T, typename Default>
consteval auto* GetLightCompareType() {
if constexpr (HasLightCompareType<T>) {
return static_cast<typename T::LightCompareType*>(nullptr);
} else {
return static_cast<Default*>(nullptr);
}
}
}
} // namespace impl

View File

@@ -61,7 +61,6 @@ void LogSettings() {
log_setting("Renderer_UseAsynchronousShaders", values.use_asynchronous_shaders.GetValue());
log_setting("Renderer_AnisotropicFilteringLevel", values.max_anisotropy.GetValue());
log_setting("Audio_OutputEngine", values.sink_id.GetValue());
log_setting("Audio_EnableAudioStretching", values.enable_audio_stretching.GetValue());
log_setting("Audio_OutputDevice", values.audio_device_id.GetValue());
log_setting("DataStorage_UseVirtualSd", values.use_virtual_sd.GetValue());
log_path("DataStorage_CacheDir", Common::FS::GetYuzuPath(Common::FS::YuzuPath::CacheDir));
@@ -72,6 +71,9 @@ void LogSettings() {
log_setting("Debugging_ProgramArgs", values.program_args.GetValue());
log_setting("Services_BCATBackend", values.bcat_backend.GetValue());
log_setting("Services_BCATBoxcatLocal", values.bcat_boxcat_local.GetValue());
log_setting("Input_EnableMotion", values.motion_enabled.GetValue());
log_setting("Input_EnableVibration", values.vibration_enabled.GetValue());
log_setting("Input_EnableRawInput", values.enable_raw_input.GetValue());
}
bool IsConfiguringGlobal() {
@@ -112,7 +114,6 @@ void RestoreGlobalState(bool is_powered_on) {
}
// Audio
values.enable_audio_stretching.SetGlobal(true);
values.volume.SetGlobal(true);
// Core

View File

@@ -16,7 +16,6 @@
#include "common/common_types.h"
#include "common/settings_input.h"
#include "input_common/udp/client.h"
namespace Settings {
@@ -415,7 +414,6 @@ struct Values {
BasicSetting<std::string> audio_device_id{"auto", "output_device"};
BasicSetting<std::string> sink_id{"auto", "output_engine"};
BasicSetting<bool> audio_muted{false, "audio_muted"};
Setting<bool> enable_audio_stretching{true, "enable_audio_stretching"};
RangedSetting<u8> volume{100, 0, 100, "volume"};
// Core
@@ -504,14 +502,20 @@ struct Values {
Setting<bool> use_docked_mode{true, "use_docked_mode"};
BasicSetting<bool> enable_raw_input{false, "enable_raw_input"};
Setting<bool> vibration_enabled{true, "vibration_enabled"};
Setting<bool> enable_accurate_vibrations{false, "enable_accurate_vibrations"};
Setting<bool> motion_enabled{true, "motion_enabled"};
BasicSetting<std::string> motion_device{"engine:motion_emu,update_period:100,sensitivity:0.01",
"motion_device"};
BasicSetting<std::string> udp_input_servers{InputCommon::CemuhookUDP::DEFAULT_SRV,
"udp_input_servers"};
BasicSetting<std::string> udp_input_servers{"127.0.0.1:26760", "udp_input_servers"};
BasicSetting<bool> pause_tas_on_load{true, "pause_tas_on_load"};
BasicSetting<bool> tas_enable{false, "tas_enable"};
BasicSetting<bool> tas_loop{false, "tas_loop"};
BasicSetting<bool> tas_swap_controllers{true, "tas_swap_controllers"};
BasicSetting<bool> mouse_panning{false, "mouse_panning"};
BasicRangedSetting<u8> mouse_panning_sensitivity{10, 1, 100, "mouse_panning_sensitivity"};

View File

@@ -14,7 +14,7 @@
#include <utility>
namespace Common {
template <typename T>
template <typename T, bool with_stop_token = false>
class SPSCQueue {
public:
SPSCQueue() {
@@ -84,7 +84,7 @@ public:
void Wait() {
if (Empty()) {
std::unique_lock lock{cv_mutex};
cv.wait(lock, [this]() { return !Empty(); });
cv.wait(lock, [this] { return !Empty(); });
}
}
@@ -95,6 +95,19 @@ public:
return t;
}
T PopWait(std::stop_token stop_token) {
if (Empty()) {
std::unique_lock lock{cv_mutex};
cv.wait(lock, stop_token, [this] { return !Empty(); });
}
if (stop_token.stop_requested()) {
return T{};
}
T t;
Pop(t);
return t;
}
// not thread-safe
void Clear() {
size.store(0);
@@ -123,13 +136,13 @@ private:
ElementPtr* read_ptr;
std::atomic_size_t size{0};
std::mutex cv_mutex;
std::condition_variable cv;
std::conditional_t<with_stop_token, std::condition_variable_any, std::condition_variable> cv;
};
// a simple thread-safe,
// single reader, multiple writer queue
template <typename T>
template <typename T, bool with_stop_token = false>
class MPSCQueue {
public:
[[nodiscard]] std::size_t Size() const {
@@ -166,13 +179,17 @@ public:
return spsc_queue.PopWait();
}
T PopWait(std::stop_token stop_token) {
return spsc_queue.PopWait(stop_token);
}
// not thread-safe
void Clear() {
spsc_queue.Clear();
}
private:
SPSCQueue<T> spsc_queue;
SPSCQueue<T, with_stop_token> spsc_queue;
std::mutex write_lock;
};
} // namespace Common

View File

@@ -58,6 +58,13 @@ struct UUID {
uuid = INVALID_UUID;
}
[[nodiscard]] constexpr bool IsInvalid() const {
return uuid == INVALID_UUID;
}
[[nodiscard]] constexpr bool IsValid() const {
return !IsInvalid();
}
// TODO(ogniK): Properly generate a Nintendo ID
[[nodiscard]] constexpr u64 GetNintendoID() const {
return uuid[0];

View File

@@ -667,8 +667,8 @@ template <typename T>
// 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) {
[[nodiscard]] constexpr decltype(X{} * float{} + X{} * float{})
Lerp(const X& begin, const X& end, const float t) {
return begin * (1.f - t) + end * t;
}

View File

@@ -305,10 +305,6 @@ struct System::Impl {
is_powered_on = false;
exit_lock = false;
if (gpu_core) {
gpu_core->ShutDown();
}
services.reset();
service_manager.reset();
cheat_engine.reset();
@@ -317,8 +313,8 @@ struct System::Impl {
time_manager.Shutdown();
core_timing.Shutdown();
app_loader.reset();
gpu_core.reset();
perf_stats.reset();
gpu_core.reset();
kernel.Shutdown();
memory.Reset();
applet_manager.ClearAll();

View File

@@ -77,7 +77,7 @@ void ProgramMetadata::LoadManual(bool is_64_bit, ProgramAddressSpaceType address
aci_header.title_id = title_id;
aci_file_access.permissions = filesystem_permissions;
npdm_header.system_resource_size = system_resource_size;
aci_kernel_capabilities = std ::move(capabilities);
aci_kernel_capabilities = std::move(capabilities);
}
bool ProgramMetadata::Is64BitProgram() const {

View File

@@ -273,6 +273,10 @@ VirtualFile VfsDirectory::GetFile(std::string_view name) const {
return iter == files.end() ? nullptr : *iter;
}
FileTimeStampRaw VfsDirectory::GetFileTimeStamp([[maybe_unused]] std::string_view path) const {
return {};
}
VirtualDir VfsDirectory::GetSubdirectory(std::string_view name) const {
const auto& subs = GetSubdirectories();
const auto iter = std::find_if(subs.begin(), subs.end(),

View File

@@ -199,6 +199,9 @@ public:
// file with name.
virtual VirtualFile GetFile(std::string_view name) const;
// Returns a struct containing the file's timestamp.
virtual FileTimeStampRaw GetFileTimeStamp(std::string_view path) const;
// Returns a vector containing all of the subdirectories in this directory.
virtual std::vector<VirtualDir> GetSubdirectories() const = 0;
// Returns the directory with name matching name. Returns nullptr if directory dosen't have a

View File

@@ -13,6 +13,13 @@
#include "common/logging/log.h"
#include "core/file_sys/vfs_real.h"
// For FileTimeStampRaw
#include <sys/stat.h>
#ifdef _MSC_VER
#define stat _stat64
#endif
namespace FileSys {
namespace FS = Common::FS;
@@ -392,6 +399,28 @@ std::vector<VirtualFile> RealVfsDirectory::GetFiles() const {
return IterateEntries<RealVfsFile, VfsFile>();
}
FileTimeStampRaw RealVfsDirectory::GetFileTimeStamp(std::string_view path_) const {
const auto full_path = FS::SanitizePath(path + '/' + std::string(path_));
const auto fs_path = std::filesystem::path{FS::ToU8String(full_path)};
struct stat file_status;
#ifdef _WIN32
const auto stat_result = _wstat64(fs_path.c_str(), &file_status);
#else
const auto stat_result = stat(fs_path.c_str(), &file_status);
#endif
if (stat_result != 0) {
return {};
}
return {
.created{static_cast<u64>(file_status.st_ctime)},
.accessed{static_cast<u64>(file_status.st_atime)},
.modified{static_cast<u64>(file_status.st_mtime)},
};
}
std::vector<VirtualDir> RealVfsDirectory::GetSubdirectories() const {
return IterateEntries<RealVfsDirectory, VfsDirectory>();
}

View File

@@ -86,6 +86,7 @@ public:
VirtualDir CreateDirectoryRelative(std::string_view relative_path) override;
bool DeleteSubdirectoryRecursive(std::string_view name) override;
std::vector<VirtualFile> GetFiles() const override;
FileTimeStampRaw GetFileTimeStamp(std::string_view path) const override;
std::vector<VirtualDir> GetSubdirectories() const override;
bool IsWritable() const override;
bool IsReadable() const override;

View File

@@ -6,6 +6,8 @@
#include <memory>
#include "common/common_types.h"
namespace FileSys {
class VfsDirectory;
@@ -18,4 +20,11 @@ using VirtualDir = std::shared_ptr<VfsDirectory>;
using VirtualFile = std::shared_ptr<VfsFile>;
using VirtualFilesystem = std::shared_ptr<VfsFilesystem>;
struct FileTimeStampRaw {
u64 created{};
u64 accessed{};
u64 modified{};
u64 padding{};
};
} // namespace FileSys

View File

@@ -13,7 +13,8 @@ ProfileSelectApplet::~ProfileSelectApplet() = default;
void DefaultProfileSelectApplet::SelectProfile(
std::function<void(std::optional<Common::UUID>)> callback) const {
Service::Account::ProfileManager manager;
callback(manager.GetUser(Settings::values.current_user.GetValue()).value_or(Common::UUID{}));
callback(manager.GetUser(Settings::values.current_user.GetValue())
.value_or(Common::UUID{Common::INVALID_UUID}));
LOG_INFO(Service_ACC, "called, selecting current user instead of prompting...");
}

View File

@@ -7,7 +7,7 @@
namespace Kernel {
KHandleTable::KHandleTable(KernelCore& kernel_) : kernel{kernel_} {}
KHandleTable ::~KHandleTable() = default;
KHandleTable::~KHandleTable() = default;
ResultCode KHandleTable::Finalize() {
// Get the table and clear our record of it.

View File

@@ -22,12 +22,10 @@ class KThread;
template <typename T>
concept KPriorityQueueAffinityMask = !std::is_reference_v<T> && requires(T & t) {
{ t.GetAffinityMask() }
->Common::ConvertibleTo<u64>;
{ t.GetAffinityMask() } -> Common::ConvertibleTo<u64>;
{t.SetAffinityMask(0)};
{ t.GetAffinity(0) }
->std::same_as<bool>;
{ t.GetAffinity(0) } -> std::same_as<bool>;
{t.SetAffinity(0, false)};
{t.SetAll()};
};
@@ -38,25 +36,20 @@ concept KPriorityQueueMember = !std::is_reference_v<T> && requires(T & t) {
{(typename T::QueueEntry()).Initialize()};
{(typename T::QueueEntry()).SetPrev(std::addressof(t))};
{(typename T::QueueEntry()).SetNext(std::addressof(t))};
{ (typename T::QueueEntry()).GetNext() }
->std::same_as<T*>;
{ (typename T::QueueEntry()).GetPrev() }
->std::same_as<T*>;
{ t.GetPriorityQueueEntry(0) }
->std::same_as<typename T::QueueEntry&>;
{ (typename T::QueueEntry()).GetNext() } -> std::same_as<T*>;
{ (typename T::QueueEntry()).GetPrev() } -> std::same_as<T*>;
{ t.GetPriorityQueueEntry(0) } -> std::same_as<typename T::QueueEntry&>;
{t.GetAffinityMask()};
{ std::remove_cvref_t<decltype(t.GetAffinityMask())>() }
->KPriorityQueueAffinityMask;
{ std::remove_cvref_t<decltype(t.GetAffinityMask())>() } -> KPriorityQueueAffinityMask;
{ t.GetActiveCore() }
->Common::ConvertibleTo<s32>;
{ t.GetPriority() }
->Common::ConvertibleTo<s32>;
{ t.GetActiveCore() } -> Common::ConvertibleTo<s32>;
{ t.GetPriority() } -> Common::ConvertibleTo<s32>;
};
template <typename Member, size_t NumCores_, int LowestPriority, int HighestPriority>
requires KPriorityQueueMember<Member> class KPriorityQueue {
requires KPriorityQueueMember<Member>
class KPriorityQueue {
public:
using AffinityMaskType = std::remove_cv_t<
std::remove_reference_t<decltype(std::declval<Member>().GetAffinityMask())>>;

View File

@@ -197,7 +197,7 @@ private:
class [[nodiscard]] KScopedSchedulerLock : KScopedLock<GlobalSchedulerContext::LockType> {
public:
explicit KScopedSchedulerLock(KernelCore & kernel);
explicit KScopedSchedulerLock(KernelCore& kernel);
~KScopedSchedulerLock();
};

View File

@@ -13,19 +13,18 @@ namespace Kernel {
template <typename T>
concept KLockable = !std::is_reference_v<T> && requires(T & t) {
{ t.Lock() }
->std::same_as<void>;
{ t.Unlock() }
->std::same_as<void>;
{ t.Lock() } -> std::same_as<void>;
{ t.Unlock() } -> std::same_as<void>;
};
template <typename T>
requires KLockable<T> class [[nodiscard]] KScopedLock {
requires KLockable<T>
class [[nodiscard]] KScopedLock {
public:
explicit KScopedLock(T * l) : lock_ptr(l) {
explicit KScopedLock(T* l) : lock_ptr(l) {
this->lock_ptr->Lock();
}
explicit KScopedLock(T & l) : KScopedLock(std::addressof(l)) {}
explicit KScopedLock(T& l) : KScopedLock(std::addressof(l)) {}
~KScopedLock() {
this->lock_ptr->Unlock();
@@ -34,7 +33,7 @@ public:
KScopedLock(const KScopedLock&) = delete;
KScopedLock& operator=(const KScopedLock&) = delete;
KScopedLock(KScopedLock &&) = delete;
KScopedLock(KScopedLock&&) = delete;
KScopedLock& operator=(KScopedLock&&) = delete;
private:

View File

@@ -17,7 +17,7 @@ namespace Kernel {
class [[nodiscard]] KScopedSchedulerLockAndSleep {
public:
explicit KScopedSchedulerLockAndSleep(KernelCore & kernel_, KThread * t, s64 timeout)
explicit KScopedSchedulerLockAndSleep(KernelCore& kernel_, KThread* t, s64 timeout)
: kernel(kernel_), thread(t), timeout_tick(timeout) {
// Lock the scheduler.
kernel.GlobalSchedulerContext().scheduler_lock.Lock();

View File

@@ -929,8 +929,7 @@ void Module::Interface::TrySelectUserWithoutInteraction(Kernel::HLERequestContex
}
const auto user_list = profile_manager->GetAllUsers();
if (std::all_of(user_list.begin(), user_list.end(),
[](const auto& user) { return user.uuid == Common::INVALID_UUID; })) {
if (std::ranges::all_of(user_list, [](const auto& user) { return user.IsInvalid(); })) {
rb.Push(ResultUnknown); // TODO(ogniK): Find the correct error code
rb.PushRaw<u128>(Common::INVALID_UUID);
return;

View File

@@ -208,9 +208,10 @@ bool ProfileManager::UserExists(UUID uuid) const {
}
bool ProfileManager::UserExistsIndex(std::size_t index) const {
if (index >= MAX_USERS)
if (index >= MAX_USERS) {
return false;
return profiles[index].user_uuid.uuid != Common::INVALID_UUID;
}
return profiles[index].user_uuid.IsValid();
}
/// Opens a specific user
@@ -304,7 +305,7 @@ bool ProfileManager::RemoveUser(UUID uuid) {
bool ProfileManager::SetProfileBase(UUID uuid, const ProfileBase& profile_new) {
const auto index = GetUserIndex(uuid);
if (!index || profile_new.user_uuid == UUID(Common::INVALID_UUID)) {
if (!index || profile_new.user_uuid.IsInvalid()) {
return false;
}
@@ -346,7 +347,7 @@ void ProfileManager::ParseUserSaveFile() {
}
for (const auto& user : data.users) {
if (user.uuid == UUID(Common::INVALID_UUID)) {
if (user.uuid.IsInvalid()) {
continue;
}

View File

@@ -275,12 +275,14 @@ ISelfController::ISelfController(Core::System& system_, NVFlinger::NVFlinger& nv
{18, nullptr, "SetRequiresCaptureButtonShortPressedMessage"},
{19, &ISelfController::SetAlbumImageOrientation, "SetAlbumImageOrientation"},
{20, nullptr, "SetDesirableKeyboardLayout"},
{21, nullptr, "GetScreenShotProgramId"},
{40, &ISelfController::CreateManagedDisplayLayer, "CreateManagedDisplayLayer"},
{41, nullptr, "IsSystemBufferSharingEnabled"},
{42, nullptr, "GetSystemSharedLayerHandle"},
{43, nullptr, "GetSystemSharedBufferHandle"},
{44, &ISelfController::CreateManagedDisplaySeparableLayer, "CreateManagedDisplaySeparableLayer"},
{45, nullptr, "SetManagedDisplayLayerSeparationMode"},
{46, nullptr, "SetRecordingLayerCompositionEnabled"},
{50, &ISelfController::SetHandlesRequestToDisplay, "SetHandlesRequestToDisplay"},
{51, nullptr, "ApproveToDisplay"},
{60, nullptr, "OverrideAutoSleepTimeAndDimmingTime"},
@@ -302,6 +304,7 @@ ISelfController::ISelfController(Core::System& system_, NVFlinger::NVFlinger& nv
{100, &ISelfController::SetAlbumImageTakenNotificationEnabled, "SetAlbumImageTakenNotificationEnabled"},
{110, nullptr, "SetApplicationAlbumUserData"},
{120, nullptr, "SaveCurrentScreenshot"},
{130, nullptr, "SetRecordVolumeMuted"},
{1000, nullptr, "GetDebugStorageChannel"},
};
// clang-format on
@@ -683,6 +686,7 @@ ICommonStateGetter::ICommonStateGetter(Core::System& system_,
{91, nullptr, "GetCurrentPerformanceConfiguration"},
{100, nullptr, "SetHandlingHomeButtonShortPressedEnabled"},
{110, nullptr, "OpenMyGpuErrorHandler"},
{120, nullptr, "GetAppletLaunchedHistory"},
{200, nullptr, "GetOperationModeSystemInfo"},
{300, nullptr, "GetSettingsPlatformRegion"},
{400, nullptr, "ActivateMigrationService"},

View File

@@ -60,7 +60,7 @@ void ProfileSelect::Execute() {
void ProfileSelect::SelectionComplete(std::optional<Common::UUID> uuid) {
UserSelectionOutput output{};
if (uuid.has_value() && uuid->uuid != Common::INVALID_UUID) {
if (uuid.has_value() && uuid->IsValid()) {
output.result = 0;
output.uuid_selected = uuid->uuid;
} else {

View File

@@ -41,6 +41,14 @@ AudCtl::AudCtl(Core::System& system_) : ServiceFramework{system_, "audctl"} {
{27, nullptr, "SetVolumeMappingTableForDev"},
{28, nullptr, "GetAudioOutputChannelCountForPlayReport"},
{29, nullptr, "BindAudioOutputChannelCountUpdateEventForPlayReport"},
{30, nullptr, "Unknown30"},
{31, nullptr, "Unknown31"},
{32, nullptr, "Unknown32"},
{33, nullptr, "Unknown33"},
{34, nullptr, "Unknown34"},
{10000, nullptr, "Unknown10000"},
{10001, nullptr, "Unknown10001"},
{10002, nullptr, "Unknown10002"},
};
// clang-format on

View File

@@ -3,38 +3,65 @@
// Refer to the license.txt file included.
#include "common/logging/log.h"
#include "core/core.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/hle_ipc.h"
#include "core/hle/kernel/k_event.h"
#include "core/hle/service/audio/audin_u.h"
namespace Service::Audio {
class IAudioIn final : public ServiceFramework<IAudioIn> {
public:
explicit IAudioIn(Core::System& system_) : ServiceFramework{system_, "IAudioIn"} {
// clang-format off
static const FunctionInfo functions[] = {
{0, nullptr, "GetAudioInState"},
{1, nullptr, "Start"},
{2, nullptr, "Stop"},
{3, nullptr, "AppendAudioInBuffer"},
{4, nullptr, "RegisterBufferEvent"},
{5, nullptr, "GetReleasedAudioInBuffer"},
{6, nullptr, "ContainsAudioInBuffer"},
{7, nullptr, "AppendUacInBuffer"},
{8, nullptr, "AppendAudioInBufferAuto"},
{9, nullptr, "GetReleasedAudioInBuffersAuto"},
{10, nullptr, "AppendUacInBufferAuto"},
{11, nullptr, "GetAudioInBufferCount"},
{12, nullptr, "SetDeviceGain"},
{13, nullptr, "GetDeviceGain"},
{14, nullptr, "FlushAudioInBuffers"},
};
// clang-format on
IAudioIn::IAudioIn(Core::System& system_)
: ServiceFramework{system_, "IAudioIn"}, buffer_event{system_.Kernel()} {
// clang-format off
static const FunctionInfo functions[] = {
{0, nullptr, "GetAudioInState"},
{1, &IAudioIn::Start, "Start"},
{2, nullptr, "Stop"},
{3, nullptr, "AppendAudioInBuffer"},
{4, &IAudioIn::RegisterBufferEvent, "RegisterBufferEvent"},
{5, nullptr, "GetReleasedAudioInBuffer"},
{6, nullptr, "ContainsAudioInBuffer"},
{7, nullptr, "AppendUacInBuffer"},
{8, &IAudioIn::AppendAudioInBufferAuto, "AppendAudioInBufferAuto"},
{9, nullptr, "GetReleasedAudioInBuffersAuto"},
{10, nullptr, "AppendUacInBufferAuto"},
{11, nullptr, "GetAudioInBufferCount"},
{12, nullptr, "SetDeviceGain"},
{13, nullptr, "GetDeviceGain"},
{14, nullptr, "FlushAudioInBuffers"},
};
// clang-format on
RegisterHandlers(functions);
}
};
RegisterHandlers(functions);
Kernel::KAutoObject::Create(std::addressof(buffer_event));
buffer_event.Initialize("IAudioIn:BufferEvent");
}
IAudioIn::~IAudioIn() = default;
void IAudioIn::Start(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_Audio, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void IAudioIn::RegisterBufferEvent(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_Audio, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(ResultSuccess);
rb.PushCopyObjects(buffer_event.GetReadableEvent());
}
void IAudioIn::AppendAudioInBufferAuto(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_Audio, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
AudInU::AudInU(Core::System& system_) : ServiceFramework{system_, "audin:u"} {
// clang-format off

View File

@@ -4,6 +4,7 @@
#pragma once
#include "core/hle/kernel/k_event.h"
#include "core/hle/service/service.h"
namespace Core {
@@ -16,6 +17,19 @@ class HLERequestContext;
namespace Service::Audio {
class IAudioIn final : public ServiceFramework<IAudioIn> {
public:
explicit IAudioIn(Core::System& system_);
~IAudioIn() override;
private:
void Start(Kernel::HLERequestContext& ctx);
void RegisterBufferEvent(Kernel::HLERequestContext& ctx);
void AppendAudioInBufferAuto(Kernel::HLERequestContext& ctx);
Kernel::KEvent buffer_event;
};
class AudInU final : public ServiceFramework<AudInU> {
public:
explicit AudInU(Core::System& system_);

View File

@@ -187,7 +187,8 @@ public:
{10, &IAudioDevice::GetActiveAudioDeviceName, "GetActiveAudioDeviceNameAuto"},
{11, &IAudioDevice::QueryAudioDeviceInputEvent, "QueryAudioDeviceInputEvent"},
{12, &IAudioDevice::QueryAudioDeviceOutputEvent, "QueryAudioDeviceOutputEvent"},
{13, nullptr, "GetAudioSystemMasterVolumeSetting"},
{13, nullptr, "GetActiveAudioOutputDeviceName"},
{14, nullptr, "ListAudioOutputDeviceName"},
};
RegisterHandlers(functions);
}

View File

@@ -175,6 +175,10 @@ public:
{143, nullptr, "GetAudioControlInputState"},
{144, nullptr, "AcquireAudioConnectionStateChangedEvent"},
{145, nullptr, "GetConnectedAudioDevice"},
{146, nullptr, "CloseAudioControlInput"},
{147, nullptr, "RegisterAudioControlNotification"},
{148, nullptr, "SendAudioControlPassthroughCommand"},
{149, nullptr, "SendAudioControlSetAbsoluteVolumeCommand"},
{256, nullptr, "IsManufacturingMode"},
{257, nullptr, "EmulateBluetoothCrash"},
{258, nullptr, "GetBleChannelMap"},

View File

@@ -15,6 +15,7 @@ CAPS_SS::CAPS_SS(Core::System& system_) : ServiceFramework{system_, "caps:ss"} {
{204, nullptr, "SaveEditedScreenShotEx0"},
{206, nullptr, "Unknown206"},
{208, nullptr, "SaveScreenShotOfMovieEx1"},
{1000, nullptr, "Unknown1000"},
};
// clang-format on

View File

@@ -55,6 +55,8 @@ public:
{36, nullptr, "DeleteAllInactiveELicenseRequiredPersonalizedTicket"},
{37, nullptr, "OwnTicket2"},
{38, nullptr, "OwnTicket3"},
{39, nullptr, "DeleteAllInactivePersonalizedTicket"},
{40, nullptr, "DeletePrepurchaseRecordByNintendoAccountId"},
{501, nullptr, "Unknown501"},
{502, nullptr, "Unknown502"},
{503, nullptr, "GetTitleKey"},
@@ -88,11 +90,15 @@ public:
{1503, nullptr, "Unknown1503"},
{1504, nullptr, "Unknown1504"},
{1505, nullptr, "Unknown1505"},
{1506, nullptr, "Unknown1506"},
{2000, nullptr, "Unknown2000"},
{2001, nullptr, "Unknown2001"},
{2002, nullptr, "Unknown2002"},
{2003, nullptr, "Unknown2003"},
{2100, nullptr, "Unknown2100"},
{2501, nullptr, "Unknown2501"},
{2502, nullptr, "Unknown2502"},
{2601, nullptr, "Unknown2601"},
{3001, nullptr, "Unknown3001"},
{3002, nullptr, "Unknown3002"},
};

View File

@@ -261,6 +261,18 @@ ResultVal<FileSys::EntryType> VfsDirectoryServiceWrapper::GetEntryType(
return FileSys::ERROR_PATH_NOT_FOUND;
}
ResultVal<FileSys::FileTimeStampRaw> VfsDirectoryServiceWrapper::GetFileTimeStampRaw(
const std::string& path) const {
auto dir = GetDirectoryRelativeWrapped(backing, Common::FS::GetParentPath(path));
if (dir == nullptr) {
return FileSys::ERROR_PATH_NOT_FOUND;
}
if (GetEntryType(path).Failed()) {
return FileSys::ERROR_PATH_NOT_FOUND;
}
return MakeResult(dir->GetFileTimeStamp(Common::FS::GetFilename(path)));
}
FileSystemController::FileSystemController(Core::System& system_) : system{system_} {}
FileSystemController::~FileSystemController() = default;

View File

@@ -240,6 +240,12 @@ public:
*/
ResultVal<FileSys::EntryType> GetEntryType(const std::string& path) const;
/**
* Get the timestamp of the specified path
* @return The timestamp of the specified path or error code
*/
ResultVal<FileSys::FileTimeStampRaw> GetFileTimeStampRaw(const std::string& path) const;
private:
FileSys::VirtualDir backing;
};

View File

@@ -326,7 +326,7 @@ public:
{11, &IFileSystem::GetFreeSpaceSize, "GetFreeSpaceSize"},
{12, &IFileSystem::GetTotalSpaceSize, "GetTotalSpaceSize"},
{13, &IFileSystem::CleanDirectoryRecursively, "CleanDirectoryRecursively"},
{14, nullptr, "GetFileTimeStampRaw"},
{14, &IFileSystem::GetFileTimeStampRaw, "GetFileTimeStampRaw"},
{15, nullptr, "QueryEntry"},
};
RegisterHandlers(functions);
@@ -501,6 +501,24 @@ public:
rb.Push(size.get_total_size());
}
void GetFileTimeStampRaw(Kernel::HLERequestContext& ctx) {
const auto file_buffer = ctx.ReadBuffer();
const std::string name = Common::StringFromBuffer(file_buffer);
LOG_WARNING(Service_FS, "(Partial Implementation) called. file={}", name);
auto result = backend.GetFileTimeStampRaw(name);
if (result.Failed()) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result.Code());
return;
}
IPC::ResponseBuilder rb{ctx, 10};
rb.Push(ResultSuccess);
rb.PushRaw(*result);
}
private:
VfsDirectoryServiceWrapper backend;
SizeGetter size;

View File

@@ -507,6 +507,7 @@ private:
LarkNesRight = 18,
Lucia = 19,
Verification = 20,
Lagon = 21,
};
struct NPadEntry {

View File

@@ -106,7 +106,7 @@ void IAppletResource::DeactivateController(HidController controller) {
controllers[static_cast<size_t>(controller)]->DeactivateController();
}
IAppletResource ::~IAppletResource() {
IAppletResource::~IAppletResource() {
system.CoreTiming().UnscheduleEvent(pad_update_event, 0);
system.CoreTiming().UnscheduleEvent(motion_update_event, 0);
}
@@ -239,6 +239,12 @@ Hid::Hid(Core::System& system_)
{81, &Hid::ResetGyroscopeZeroDriftMode, "ResetGyroscopeZeroDriftMode"},
{82, &Hid::IsSixAxisSensorAtRest, "IsSixAxisSensorAtRest"},
{83, &Hid::IsFirmwareUpdateAvailableForSixAxisSensor, "IsFirmwareUpdateAvailableForSixAxisSensor"},
{84, nullptr, "EnableSixAxisSensorUnalteredPassthrough"},
{85, nullptr, "IsSixAxisSensorUnalteredPassthroughEnabled"},
{86, nullptr, "StoreSixAxisSensorCalibrationParameter"},
{87, nullptr, "LoadSixAxisSensorCalibrationParameter"},
{88, nullptr, "GetSixAxisSensorIcInformation"},
{89, nullptr, "ResetIsSixAxisSensorDeviceNewlyAssigned"},
{91, &Hid::ActivateGesture, "ActivateGesture"},
{100, &Hid::SetSupportedNpadStyleSet, "SetSupportedNpadStyleSet"},
{101, &Hid::GetSupportedNpadStyleSet, "GetSupportedNpadStyleSet"},
@@ -1656,6 +1662,9 @@ public:
{12, nullptr, "UnsetTouchScreenAutoPilotState"},
{13, nullptr, "GetTouchScreenConfiguration"},
{14, nullptr, "ProcessTouchScreenAutoTune"},
{15, nullptr, "ForceStopTouchScreenManagement"},
{16, nullptr, "ForceRestartTouchScreenManagement"},
{17, nullptr, "IsTouchScreenManaged"},
{20, nullptr, "DeactivateMouse"},
{21, nullptr, "SetMouseAutoPilotState"},
{22, nullptr, "UnsetMouseAutoPilotState"},

View File

@@ -15,7 +15,7 @@ public:
explicit IService(Core::System& system_) : ServiceFramework{system_, "ngct:u"} {
// clang-format off
static const FunctionInfo functions[] = {
{0, nullptr, "Match"},
{0, &IService::Match, "Match"},
{1, &IService::Filter, "Filter"},
};
// clang-format on
@@ -24,6 +24,19 @@ public:
}
private:
void Match(Kernel::HLERequestContext& ctx) {
const auto buffer = ctx.ReadBuffer();
const auto text = Common::StringFromFixedZeroTerminatedBuffer(
reinterpret_cast<const char*>(buffer.data()), buffer.size());
LOG_WARNING(Service_NGCT, "(STUBBED) called, text={}", text);
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
// Return false since we don't censor anything
rb.Push(false);
}
void Filter(Kernel::HLERequestContext& ctx) {
const auto buffer = ctx.ReadBuffer();
const auto text = Common::StringFromFixedZeroTerminatedBuffer(

View File

@@ -31,6 +31,7 @@ public:
{24, nullptr, "DestroyTokenWithApplicationId"},
{25, nullptr, "QueryIsTokenValid"},
{26, nullptr, "ListenToMyApplicationId"},
{27, nullptr, "DestroyTokenAll"},
{31, nullptr, "UploadTokenToBaaS"},
{32, nullptr, "DestroyTokenForBaaS"},
{33, nullptr, "CreateTokenForBaaS"},

View File

@@ -16,7 +16,7 @@ namespace Service::Nvidia::Devices {
nvdisp_disp0::nvdisp_disp0(Core::System& system_, std::shared_ptr<nvmap> nvmap_dev_)
: nvdevice{system_}, nvmap_dev{std::move(nvmap_dev_)} {}
nvdisp_disp0 ::~nvdisp_disp0() = default;
nvdisp_disp0::~nvdisp_disp0() = default;
NvResult nvdisp_disp0::Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input,
std::vector<u8>& output) {

View File

@@ -415,6 +415,18 @@ void BSD::Write(Kernel::HLERequestContext& ctx) {
});
}
void BSD::Read(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const s32 fd = rp.Pop<s32>();
LOG_WARNING(Service, "(STUBBED) called. fd={} len={}", fd, ctx.GetWriteBufferSize());
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(ResultSuccess);
rb.Push<u32>(0); // ret
rb.Push<u32>(0); // bsd errno
}
void BSD::Close(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const s32 fd = rp.Pop<s32>();
@@ -855,7 +867,7 @@ BSD::BSD(Core::System& system_, const char* name) : ServiceFramework{system_, na
{22, &BSD::Shutdown, "Shutdown"},
{23, nullptr, "ShutdownAllSockets"},
{24, &BSD::Write, "Write"},
{25, nullptr, "Read"},
{25, &BSD::Read, "Read"},
{26, &BSD::Close, "Close"},
{27, nullptr, "DuplicateSocket"},
{28, nullptr, "GetResourceStatistics"},

View File

@@ -135,6 +135,7 @@ private:
void Send(Kernel::HLERequestContext& ctx);
void SendTo(Kernel::HLERequestContext& ctx);
void Write(Kernel::HLERequestContext& ctx);
void Read(Kernel::HLERequestContext& ctx);
void Close(Kernel::HLERequestContext& ctx);
void EventFd(Kernel::HLERequestContext& ctx);

View File

@@ -13,7 +13,7 @@ SystemClockCore::SystemClockCore(SteadyClockCore& steady_clock_core_)
context.steady_time_point.clock_source_id = steady_clock_core.GetClockSourceId();
}
SystemClockCore ::~SystemClockCore() = default;
SystemClockCore::~SystemClockCore() = default;
ResultCode SystemClockCore::GetCurrentTime(Core::System& system, s64& posix_time) const {
posix_time = 0;

View File

@@ -10,8 +10,8 @@
namespace Service::Time {
ITimeZoneService ::ITimeZoneService(Core::System& system_,
TimeZone::TimeZoneContentManager& time_zone_manager_)
ITimeZoneService::ITimeZoneService(Core::System& system_,
TimeZone::TimeZoneContentManager& time_zone_manager_)
: ServiceFramework{system_, "ITimeZoneService"}, time_zone_content_manager{time_zone_manager_} {
static const FunctionInfo functions[] = {
{0, &ITimeZoneService::GetDeviceLocationName, "GetDeviceLocationName"},

View File

@@ -97,7 +97,7 @@ public:
{3, nullptr, "GetAlternateInterface"},
{4, nullptr, "GetCurrentFrame"},
{5, nullptr, "CtrlXferAsync"},
{6, nullptr, "Unknown6"},
{6, nullptr, "GetCtrlXferCompletionEvent"},
{7, nullptr, "GetCtrlXferReport"},
{8, nullptr, "ResetDevice"},
{9, nullptr, "OpenUsbEp"},
@@ -183,8 +183,8 @@ public:
{4, nullptr, "GetHostPdcFirmwareRevision"},
{5, nullptr, "GetHostPdcManufactureId"},
{6, nullptr, "GetHostPdcDeviceId"},
{7, nullptr, "AwakeCradle"},
{8, nullptr, "SleepCradle"},
{7, nullptr, "EnableCradleRecovery"},
{8, nullptr, "DisableCradleRecovery"},
};
// clang-format on

View File

@@ -831,6 +831,7 @@ public:
{6010, nullptr, "GetLayerPresentationAllFencesExpiredEvent"},
{6011, nullptr, "EnableLayerAutoClearTransitionBuffer"},
{6012, nullptr, "DisableLayerAutoClearTransitionBuffer"},
{6013, nullptr, "SetLayerOpacity"},
{7000, nullptr, "SetContentVisibility"},
{8000, nullptr, "SetConductorLayer"},
{8001, nullptr, "SetTimestampTracking"},

View File

@@ -226,8 +226,6 @@ void TelemetrySession::AddInitialInfo(Loader::AppLoader& app_loader,
// Log user configuration information
constexpr auto field_type = Telemetry::FieldType::UserConfig;
AddField(field_type, "Audio_SinkId", Settings::values.sink_id.GetValue());
AddField(field_type, "Audio_EnableAudioStretching",
Settings::values.enable_audio_stretching.GetValue());
AddField(field_type, "Core_UseMultiCore", Settings::values.use_multi_core.GetValue());
AddField(field_type, "Renderer_Backend",
TranslateRenderer(Settings::values.renderer_backend.GetValue()));

View File

@@ -21,6 +21,10 @@ add_library(input_common STATIC
mouse/mouse_poller.h
sdl/sdl.cpp
sdl/sdl.h
tas/tas_input.cpp
tas/tas_input.h
tas/tas_poller.cpp
tas/tas_poller.h
udp/client.cpp
udp/client.h
udp/protocol.cpp

View File

@@ -5,6 +5,7 @@
#include <memory>
#include <thread>
#include "common/param_package.h"
#include "common/settings.h"
#include "input_common/analog_from_button.h"
#include "input_common/gcadapter/gc_adapter.h"
#include "input_common/gcadapter/gc_poller.h"
@@ -13,6 +14,8 @@
#include "input_common/motion_from_button.h"
#include "input_common/mouse/mouse_input.h"
#include "input_common/mouse/mouse_poller.h"
#include "input_common/tas/tas_input.h"
#include "input_common/tas/tas_poller.h"
#include "input_common/touch_from_button.h"
#include "input_common/udp/client.h"
#include "input_common/udp/udp.h"
@@ -60,6 +63,12 @@ struct InputSubsystem::Impl {
Input::RegisterFactory<Input::MotionDevice>("mouse", mousemotion);
mousetouch = std::make_shared<MouseTouchFactory>(mouse);
Input::RegisterFactory<Input::TouchDevice>("mouse", mousetouch);
tas = std::make_shared<TasInput::Tas>();
tasbuttons = std::make_shared<TasButtonFactory>(tas);
Input::RegisterFactory<Input::ButtonDevice>("tas", tasbuttons);
tasanalog = std::make_shared<TasAnalogFactory>(tas);
Input::RegisterFactory<Input::AnalogDevice>("tas", tasanalog);
}
void Shutdown() {
@@ -94,6 +103,12 @@ struct InputSubsystem::Impl {
mouseanalog.reset();
mousemotion.reset();
mousetouch.reset();
Input::UnregisterFactory<Input::ButtonDevice>("tas");
Input::UnregisterFactory<Input::AnalogDevice>("tas");
tasbuttons.reset();
tasanalog.reset();
}
[[nodiscard]] std::vector<Common::ParamPackage> GetInputDevices() const {
@@ -101,6 +116,10 @@ struct InputSubsystem::Impl {
Common::ParamPackage{{"display", "Any"}, {"class", "any"}},
Common::ParamPackage{{"display", "Keyboard/Mouse"}, {"class", "keyboard"}},
};
if (Settings::values.tas_enable) {
devices.emplace_back(
Common::ParamPackage{{"display", "TAS Controller"}, {"class", "tas"}});
}
#ifdef HAVE_SDL2
auto sdl_devices = sdl->GetInputDevices();
devices.insert(devices.end(), sdl_devices.begin(), sdl_devices.end());
@@ -120,6 +139,9 @@ struct InputSubsystem::Impl {
if (params.Get("class", "") == "gcpad") {
return gcadapter->GetAnalogMappingForDevice(params);
}
if (params.Get("class", "") == "tas") {
return tas->GetAnalogMappingForDevice(params);
}
#ifdef HAVE_SDL2
if (params.Get("class", "") == "sdl") {
return sdl->GetAnalogMappingForDevice(params);
@@ -136,6 +158,9 @@ struct InputSubsystem::Impl {
if (params.Get("class", "") == "gcpad") {
return gcadapter->GetButtonMappingForDevice(params);
}
if (params.Get("class", "") == "tas") {
return tas->GetButtonMappingForDevice(params);
}
#ifdef HAVE_SDL2
if (params.Get("class", "") == "sdl") {
return sdl->GetButtonMappingForDevice(params);
@@ -174,9 +199,12 @@ struct InputSubsystem::Impl {
std::shared_ptr<MouseAnalogFactory> mouseanalog;
std::shared_ptr<MouseMotionFactory> mousemotion;
std::shared_ptr<MouseTouchFactory> mousetouch;
std::shared_ptr<TasButtonFactory> tasbuttons;
std::shared_ptr<TasAnalogFactory> tasanalog;
std::shared_ptr<CemuhookUDP::Client> udp;
std::shared_ptr<GCAdapter::Adapter> gcadapter;
std::shared_ptr<MouseInput::Mouse> mouse;
std::shared_ptr<TasInput::Tas> tas;
};
InputSubsystem::InputSubsystem() : impl{std::make_unique<Impl>()} {}
@@ -207,6 +235,14 @@ const MouseInput::Mouse* InputSubsystem::GetMouse() const {
return impl->mouse.get();
}
TasInput::Tas* InputSubsystem::GetTas() {
return impl->tas.get();
}
const TasInput::Tas* InputSubsystem::GetTas() const {
return impl->tas.get();
}
std::vector<Common::ParamPackage> InputSubsystem::GetInputDevices() const {
return impl->GetInputDevices();
}
@@ -287,6 +323,22 @@ const MouseTouchFactory* InputSubsystem::GetMouseTouch() const {
return impl->mousetouch.get();
}
TasButtonFactory* InputSubsystem::GetTasButtons() {
return impl->tasbuttons.get();
}
const TasButtonFactory* InputSubsystem::GetTasButtons() const {
return impl->tasbuttons.get();
}
TasAnalogFactory* InputSubsystem::GetTasAnalogs() {
return impl->tasanalog.get();
}
const TasAnalogFactory* InputSubsystem::GetTasAnalogs() const {
return impl->tasanalog.get();
}
void InputSubsystem::ReloadInputDevices() {
if (!impl->udp) {
return;
@@ -294,8 +346,8 @@ void InputSubsystem::ReloadInputDevices() {
impl->udp->ReloadSockets();
}
std::vector<std::unique_ptr<Polling::DevicePoller>> InputSubsystem::GetPollers([
[maybe_unused]] Polling::DeviceType type) const {
std::vector<std::unique_ptr<Polling::DevicePoller>> InputSubsystem::GetPollers(
[[maybe_unused]] Polling::DeviceType type) const {
#ifdef HAVE_SDL2
return impl->sdl->GetPollers(type);
#else

View File

@@ -29,6 +29,10 @@ namespace MouseInput {
class Mouse;
}
namespace TasInput {
class Tas;
}
namespace InputCommon {
namespace Polling {
@@ -64,6 +68,8 @@ class MouseButtonFactory;
class MouseAnalogFactory;
class MouseMotionFactory;
class MouseTouchFactory;
class TasButtonFactory;
class TasAnalogFactory;
class Keyboard;
/**
@@ -103,6 +109,11 @@ public:
/// Retrieves the underlying mouse device.
[[nodiscard]] const MouseInput::Mouse* GetMouse() const;
/// Retrieves the underlying tas device.
[[nodiscard]] TasInput::Tas* GetTas();
/// Retrieves the underlying tas device.
[[nodiscard]] const TasInput::Tas* GetTas() const;
/**
* Returns all available input devices that this Factory can create a new device with.
* Each returned ParamPackage should have a `display` field used for display, a class field for
@@ -144,30 +155,42 @@ public:
/// Retrieves the underlying udp touch handler.
[[nodiscard]] const UDPTouchFactory* GetUDPTouch() const;
/// Retrieves the underlying GameCube button handler.
/// Retrieves the underlying mouse button handler.
[[nodiscard]] MouseButtonFactory* GetMouseButtons();
/// Retrieves the underlying GameCube button handler.
/// Retrieves the underlying mouse button handler.
[[nodiscard]] const MouseButtonFactory* GetMouseButtons() const;
/// Retrieves the underlying udp touch handler.
/// Retrieves the underlying mouse analog handler.
[[nodiscard]] MouseAnalogFactory* GetMouseAnalogs();
/// Retrieves the underlying udp touch handler.
/// Retrieves the underlying mouse analog handler.
[[nodiscard]] const MouseAnalogFactory* GetMouseAnalogs() const;
/// Retrieves the underlying udp motion handler.
/// Retrieves the underlying mouse motion handler.
[[nodiscard]] MouseMotionFactory* GetMouseMotions();
/// Retrieves the underlying udp motion handler.
/// Retrieves the underlying mouse motion handler.
[[nodiscard]] const MouseMotionFactory* GetMouseMotions() const;
/// Retrieves the underlying udp touch handler.
/// Retrieves the underlying mouse touch handler.
[[nodiscard]] MouseTouchFactory* GetMouseTouch();
/// Retrieves the underlying udp touch handler.
/// Retrieves the underlying mouse touch handler.
[[nodiscard]] const MouseTouchFactory* GetMouseTouch() const;
/// Retrieves the underlying tas button handler.
[[nodiscard]] TasButtonFactory* GetTasButtons();
/// Retrieves the underlying tas button handler.
[[nodiscard]] const TasButtonFactory* GetTasButtons() const;
/// Retrieves the underlying tas analogs handler.
[[nodiscard]] TasAnalogFactory* GetTasAnalogs();
/// Retrieves the underlying tas analogs handler.
[[nodiscard]] const TasAnalogFactory* GetTasAnalogs() const;
/// Reloads the input devices
void ReloadInputDevices();

View File

@@ -21,7 +21,7 @@
#include "common/logging/log.h"
#include "common/math_util.h"
#include "common/param_package.h"
#include "common/settings_input.h"
#include "common/settings.h"
#include "common/threadsafe_queue.h"
#include "core/frontend/input.h"
#include "input_common/motion_input.h"
@@ -889,8 +889,10 @@ SDLState::SDLState() {
RegisterFactory<VibrationDevice>("sdl", vibration_factory);
RegisterFactory<MotionDevice>("sdl", motion_factory);
// Disable raw input. When enabled this setting causes SDL to die when a web applet opens
SDL_SetHint(SDL_HINT_JOYSTICK_RAWINPUT, "0");
if (!Settings::values.enable_raw_input) {
// Disable raw input. When enabled this setting causes SDL to die when a web applet opens
SDL_SetHint(SDL_HINT_JOYSTICK_RAWINPUT, "0");
}
// Enable HIDAPI rumble. This prevents SDL from disabling motion on PS4 and PS5 controllers
SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_PS4_RUMBLE, "1");
@@ -898,10 +900,10 @@ SDLState::SDLState() {
// Tell SDL2 to use the hidapi driver. This will allow joycons to be detected as a
// GameController and not a generic one
SDL_SetHint("SDL_JOYSTICK_HIDAPI_JOY_CONS", "1");
SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_JOY_CONS, "1");
// Turn off Pro controller home led
SDL_SetHint("SDL_JOYSTICK_HIDAPI_SWITCH_HOME_LED", "0");
SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_SWITCH_HOME_LED, "0");
// If the frontend is going to manage the event loop, then we don't start one here
start_thread = SDL_WasInit(SDL_INIT_JOYSTICK) == 0;

View File

@@ -0,0 +1,455 @@
// Copyright 2021 yuzu Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.
#include <cstring>
#include <regex>
#include "common/fs/file.h"
#include "common/fs/fs_types.h"
#include "common/fs/path_util.h"
#include "common/logging/log.h"
#include "common/settings.h"
#include "input_common/tas/tas_input.h"
namespace TasInput {
// Supported keywords and buttons from a TAS file
constexpr std::array<std::pair<std::string_view, TasButton>, 20> text_to_tas_button = {
std::pair{"KEY_A", TasButton::BUTTON_A},
{"KEY_B", TasButton::BUTTON_B},
{"KEY_X", TasButton::BUTTON_X},
{"KEY_Y", TasButton::BUTTON_Y},
{"KEY_LSTICK", TasButton::STICK_L},
{"KEY_RSTICK", TasButton::STICK_R},
{"KEY_L", TasButton::TRIGGER_L},
{"KEY_R", TasButton::TRIGGER_R},
{"KEY_PLUS", TasButton::BUTTON_PLUS},
{"KEY_MINUS", TasButton::BUTTON_MINUS},
{"KEY_DLEFT", TasButton::BUTTON_LEFT},
{"KEY_DUP", TasButton::BUTTON_UP},
{"KEY_DRIGHT", TasButton::BUTTON_RIGHT},
{"KEY_DDOWN", TasButton::BUTTON_DOWN},
{"KEY_SL", TasButton::BUTTON_SL},
{"KEY_SR", TasButton::BUTTON_SR},
{"KEY_CAPTURE", TasButton::BUTTON_CAPTURE},
{"KEY_HOME", TasButton::BUTTON_HOME},
{"KEY_ZL", TasButton::TRIGGER_ZL},
{"KEY_ZR", TasButton::TRIGGER_ZR},
};
Tas::Tas() {
if (!Settings::values.tas_enable) {
needs_reset = true;
return;
}
LoadTasFiles();
}
Tas::~Tas() {
Stop();
};
void Tas::LoadTasFiles() {
script_length = 0;
for (size_t i = 0; i < commands.size(); i++) {
LoadTasFile(i);
if (commands[i].size() > script_length) {
script_length = commands[i].size();
}
}
}
void Tas::LoadTasFile(size_t player_index) {
if (!commands[player_index].empty()) {
commands[player_index].clear();
}
std::string file =
Common::FS::ReadStringFromFile(Common::FS::GetYuzuPath(Common::FS::YuzuPath::TASDir) /
fmt::format("script0-{}.txt", player_index + 1),
Common::FS::FileType::BinaryFile);
std::stringstream command_line(file);
std::string line;
int frame_no = 0;
while (std::getline(command_line, line, '\n')) {
if (line.empty()) {
continue;
}
LOG_DEBUG(Input, "Loading line: {}", line);
std::smatch m;
std::stringstream linestream(line);
std::string segment;
std::vector<std::string> seglist;
while (std::getline(linestream, segment, ' ')) {
seglist.push_back(segment);
}
if (seglist.size() < 4) {
continue;
}
while (frame_no < std::stoi(seglist.at(0))) {
commands[player_index].push_back({});
frame_no++;
}
TASCommand command = {
.buttons = ReadCommandButtons(seglist.at(1)),
.l_axis = ReadCommandAxis(seglist.at(2)),
.r_axis = ReadCommandAxis(seglist.at(3)),
};
commands[player_index].push_back(command);
frame_no++;
}
LOG_INFO(Input, "TAS file loaded! {} frames", frame_no);
}
void Tas::WriteTasFile(std::u8string file_name) {
std::string output_text;
for (size_t frame = 0; frame < record_commands.size(); frame++) {
if (!output_text.empty()) {
output_text += "\n";
}
const TASCommand& line = record_commands[frame];
output_text += std::to_string(frame) + " " + WriteCommandButtons(line.buttons) + " " +
WriteCommandAxis(line.l_axis) + " " + WriteCommandAxis(line.r_axis);
}
const auto bytes_written = Common::FS::WriteStringToFile(
Common::FS::GetYuzuPath(Common::FS::YuzuPath::TASDir) / file_name,
Common::FS::FileType::TextFile, output_text);
if (bytes_written == output_text.size()) {
LOG_INFO(Input, "TAS file written to file!");
} else {
LOG_ERROR(Input, "Writing the TAS-file has failed! {} / {} bytes written", bytes_written,
output_text.size());
}
}
std::pair<float, float> Tas::FlipAxisY(std::pair<float, float> old) {
auto [x, y] = old;
return {x, -y};
}
void Tas::RecordInput(u32 buttons, const std::array<std::pair<float, float>, 2>& axes) {
last_input = {buttons, FlipAxisY(axes[0]), FlipAxisY(axes[1])};
}
std::tuple<TasState, size_t, size_t> Tas::GetStatus() const {
TasState state;
if (is_recording) {
return {TasState::Recording, 0, record_commands.size()};
}
if (is_running) {
state = TasState::Running;
} else {
state = TasState::Stopped;
}
return {state, current_command, script_length};
}
std::string Tas::DebugButtons(u32 buttons) const {
return fmt::format("{{ {} }}", TasInput::Tas::ButtonsToString(buttons));
}
std::string Tas::DebugJoystick(float x, float y) const {
return fmt::format("[ {} , {} ]", std::to_string(x), std::to_string(y));
}
std::string Tas::DebugInput(const TasData& data) const {
return fmt::format("{{ {} , {} , {} }}", DebugButtons(data.buttons),
DebugJoystick(data.axis[0], data.axis[1]),
DebugJoystick(data.axis[2], data.axis[3]));
}
std::string Tas::DebugInputs(const std::array<TasData, PLAYER_NUMBER>& arr) const {
std::string returns = "[ ";
for (size_t i = 0; i < arr.size(); i++) {
returns += DebugInput(arr[i]);
if (i != arr.size() - 1) {
returns += " , ";
}
}
return returns + "]";
}
std::string Tas::ButtonsToString(u32 button) const {
std::string returns;
for (auto [text_button, tas_button] : text_to_tas_button) {
if ((button & static_cast<u32>(tas_button)) != 0)
returns += fmt::format(", {}", text_button.substr(4));
}
return returns.empty() ? "" : returns.substr(2);
}
void Tas::UpdateThread() {
if (!Settings::values.tas_enable) {
if (is_running) {
Stop();
}
return;
}
if (is_recording) {
record_commands.push_back(last_input);
}
if (needs_reset) {
current_command = 0;
needs_reset = false;
LoadTasFiles();
LOG_DEBUG(Input, "tas_reset done");
}
if (!is_running) {
tas_data.fill({});
return;
}
if (current_command < script_length) {
LOG_DEBUG(Input, "Playing TAS {}/{}", current_command, script_length);
size_t frame = current_command++;
for (size_t i = 0; i < commands.size(); i++) {
if (frame < commands[i].size()) {
TASCommand command = commands[i][frame];
tas_data[i].buttons = command.buttons;
auto [l_axis_x, l_axis_y] = command.l_axis;
tas_data[i].axis[0] = l_axis_x;
tas_data[i].axis[1] = l_axis_y;
auto [r_axis_x, r_axis_y] = command.r_axis;
tas_data[i].axis[2] = r_axis_x;
tas_data[i].axis[3] = r_axis_y;
} else {
tas_data[i] = {};
}
}
} else {
is_running = Settings::values.tas_loop.GetValue();
current_command = 0;
tas_data.fill({});
if (!is_running) {
SwapToStoredController();
}
}
LOG_DEBUG(Input, "TAS inputs: {}", DebugInputs(tas_data));
}
TasAnalog Tas::ReadCommandAxis(const std::string& line) const {
std::stringstream linestream(line);
std::string segment;
std::vector<std::string> seglist;
while (std::getline(linestream, segment, ';')) {
seglist.push_back(segment);
}
const float x = std::stof(seglist.at(0)) / 32767.0f;
const float y = std::stof(seglist.at(1)) / 32767.0f;
return {x, y};
}
u32 Tas::ReadCommandButtons(const std::string& data) const {
std::stringstream button_text(data);
std::string line;
u32 buttons = 0;
while (std::getline(button_text, line, ';')) {
for (auto [text, tas_button] : text_to_tas_button) {
if (text == line) {
buttons |= static_cast<u32>(tas_button);
break;
}
}
}
return buttons;
}
std::string Tas::WriteCommandAxis(TasAnalog data) const {
auto [x, y] = data;
std::string line;
line += std::to_string(static_cast<int>(x * 32767));
line += ";";
line += std::to_string(static_cast<int>(y * 32767));
return line;
}
std::string Tas::WriteCommandButtons(u32 data) const {
if (data == 0) {
return "NONE";
}
std::string line;
u32 index = 0;
while (data > 0) {
if ((data & 1) == 1) {
for (auto [text, tas_button] : text_to_tas_button) {
if (tas_button == static_cast<TasButton>(1 << index)) {
if (line.size() > 0) {
line += ";";
}
line += text;
break;
}
}
}
index++;
data >>= 1;
}
return line;
}
void Tas::StartStop() {
if (!Settings::values.tas_enable) {
return;
}
if (is_running) {
Stop();
} else {
is_running = true;
SwapToTasController();
}
}
void Tas::Stop() {
is_running = false;
SwapToStoredController();
}
void Tas::SwapToTasController() {
if (!Settings::values.tas_swap_controllers) {
return;
}
auto& players = Settings::values.players.GetValue();
for (std::size_t index = 0; index < players.size(); index++) {
auto& player = players[index];
player_mappings[index] = player;
// Only swap active controllers
if (!player.connected) {
continue;
}
Common::ParamPackage tas_param;
tas_param.Set("pad", static_cast<u8>(index));
auto button_mapping = GetButtonMappingForDevice(tas_param);
auto analog_mapping = GetAnalogMappingForDevice(tas_param);
auto& buttons = player.buttons;
auto& analogs = player.analogs;
for (std::size_t i = 0; i < buttons.size(); ++i) {
buttons[i] = button_mapping[static_cast<Settings::NativeButton::Values>(i)].Serialize();
}
for (std::size_t i = 0; i < analogs.size(); ++i) {
analogs[i] = analog_mapping[static_cast<Settings::NativeAnalog::Values>(i)].Serialize();
}
}
is_old_input_saved = true;
Settings::values.is_device_reload_pending.store(true);
}
void Tas::SwapToStoredController() {
if (!is_old_input_saved) {
return;
}
auto& players = Settings::values.players.GetValue();
for (std::size_t index = 0; index < players.size(); index++) {
players[index] = player_mappings[index];
}
is_old_input_saved = false;
Settings::values.is_device_reload_pending.store(true);
}
void Tas::Reset() {
if (!Settings::values.tas_enable) {
return;
}
needs_reset = true;
}
bool Tas::Record() {
if (!Settings::values.tas_enable) {
return true;
}
is_recording = !is_recording;
return is_recording;
}
void Tas::SaveRecording(bool overwrite_file) {
if (is_recording) {
return;
}
if (record_commands.empty()) {
return;
}
WriteTasFile(u8"record.txt");
if (overwrite_file) {
WriteTasFile(u8"script0-1.txt");
}
needs_reset = true;
record_commands.clear();
}
InputCommon::ButtonMapping Tas::GetButtonMappingForDevice(
const Common::ParamPackage& params) const {
// This list is missing ZL/ZR since those are not considered buttons.
// We will add those afterwards
// This list also excludes any button that can't be really mapped
static constexpr std::array<std::pair<Settings::NativeButton::Values, TasButton>, 20>
switch_to_tas_button = {
std::pair{Settings::NativeButton::A, TasButton::BUTTON_A},
{Settings::NativeButton::B, TasButton::BUTTON_B},
{Settings::NativeButton::X, TasButton::BUTTON_X},
{Settings::NativeButton::Y, TasButton::BUTTON_Y},
{Settings::NativeButton::LStick, TasButton::STICK_L},
{Settings::NativeButton::RStick, TasButton::STICK_R},
{Settings::NativeButton::L, TasButton::TRIGGER_L},
{Settings::NativeButton::R, TasButton::TRIGGER_R},
{Settings::NativeButton::Plus, TasButton::BUTTON_PLUS},
{Settings::NativeButton::Minus, TasButton::BUTTON_MINUS},
{Settings::NativeButton::DLeft, TasButton::BUTTON_LEFT},
{Settings::NativeButton::DUp, TasButton::BUTTON_UP},
{Settings::NativeButton::DRight, TasButton::BUTTON_RIGHT},
{Settings::NativeButton::DDown, TasButton::BUTTON_DOWN},
{Settings::NativeButton::SL, TasButton::BUTTON_SL},
{Settings::NativeButton::SR, TasButton::BUTTON_SR},
{Settings::NativeButton::Screenshot, TasButton::BUTTON_CAPTURE},
{Settings::NativeButton::Home, TasButton::BUTTON_HOME},
{Settings::NativeButton::ZL, TasButton::TRIGGER_ZL},
{Settings::NativeButton::ZR, TasButton::TRIGGER_ZR},
};
InputCommon::ButtonMapping mapping{};
for (const auto& [switch_button, tas_button] : switch_to_tas_button) {
Common::ParamPackage button_params({{"engine", "tas"}});
button_params.Set("pad", params.Get("pad", 0));
button_params.Set("button", static_cast<int>(tas_button));
mapping.insert_or_assign(switch_button, std::move(button_params));
}
return mapping;
}
InputCommon::AnalogMapping Tas::GetAnalogMappingForDevice(
const Common::ParamPackage& params) const {
InputCommon::AnalogMapping mapping = {};
Common::ParamPackage left_analog_params;
left_analog_params.Set("engine", "tas");
left_analog_params.Set("pad", params.Get("pad", 0));
left_analog_params.Set("axis_x", static_cast<int>(TasAxes::StickX));
left_analog_params.Set("axis_y", static_cast<int>(TasAxes::StickY));
mapping.insert_or_assign(Settings::NativeAnalog::LStick, std::move(left_analog_params));
Common::ParamPackage right_analog_params;
right_analog_params.Set("engine", "tas");
right_analog_params.Set("pad", params.Get("pad", 0));
right_analog_params.Set("axis_x", static_cast<int>(TasAxes::SubstickX));
right_analog_params.Set("axis_y", static_cast<int>(TasAxes::SubstickY));
mapping.insert_or_assign(Settings::NativeAnalog::RStick, std::move(right_analog_params));
return mapping;
}
const TasData& Tas::GetTasState(std::size_t pad) const {
return tas_data[pad];
}
} // namespace TasInput

View File

@@ -0,0 +1,237 @@
// Copyright 2020 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <array>
#include "common/common_types.h"
#include "common/settings_input.h"
#include "core/frontend/input.h"
#include "input_common/main.h"
/*
To play back TAS scripts on Yuzu, select the folder with scripts in the configuration menu below
Tools -> Configure TAS. The file itself has normal text format and has to be called script0-1.txt
for controller 1, script0-2.txt for controller 2 and so forth (with max. 8 players).
A script file has the same format as TAS-nx uses, so final files will look like this:
1 KEY_B 0;0 0;0
6 KEY_ZL 0;0 0;0
41 KEY_ZL;KEY_Y 0;0 0;0
43 KEY_X;KEY_A 32767;0 0;0
44 KEY_A 32767;0 0;0
45 KEY_A 32767;0 0;0
46 KEY_A 32767;0 0;0
47 KEY_A 32767;0 0;0
After placing the file at the correct location, it can be read into Yuzu with the (default) hotkey
CTRL+F6 (refresh). In the bottom left corner, it will display the amount of frames the script file
has. Playback can be started or stopped using CTRL+F5.
However, for playback to actually work, the correct input device has to be selected: In the Controls
menu, select TAS from the device list for the controller that the script should be played on.
Recording a new script file is really simple: Just make sure that the proper device (not TAS) is
connected on P1, and press CTRL+F7 to start recording. When done, just press the same keystroke
again (CTRL+F7). The new script will be saved at the location previously selected, as the filename
record.txt.
For debugging purposes, the common controller debugger can be used (View -> Debugging -> Controller
P1).
*/
namespace TasInput {
constexpr size_t PLAYER_NUMBER = 8;
using TasAnalog = std::pair<float, float>;
enum class TasState {
Running,
Recording,
Stopped,
};
enum class TasButton : u32 {
BUTTON_A = 1U << 0,
BUTTON_B = 1U << 1,
BUTTON_X = 1U << 2,
BUTTON_Y = 1U << 3,
STICK_L = 1U << 4,
STICK_R = 1U << 5,
TRIGGER_L = 1U << 6,
TRIGGER_R = 1U << 7,
TRIGGER_ZL = 1U << 8,
TRIGGER_ZR = 1U << 9,
BUTTON_PLUS = 1U << 10,
BUTTON_MINUS = 1U << 11,
BUTTON_LEFT = 1U << 12,
BUTTON_UP = 1U << 13,
BUTTON_RIGHT = 1U << 14,
BUTTON_DOWN = 1U << 15,
BUTTON_SL = 1U << 16,
BUTTON_SR = 1U << 17,
BUTTON_HOME = 1U << 18,
BUTTON_CAPTURE = 1U << 19,
};
enum class TasAxes : u8 {
StickX,
StickY,
SubstickX,
SubstickY,
Undefined,
};
struct TasData {
u32 buttons{};
std::array<float, 4> axis{};
};
class Tas {
public:
Tas();
~Tas();
// Changes the input status that will be stored in each frame
void RecordInput(u32 buttons, const std::array<std::pair<float, float>, 2>& axes);
// Main loop that records or executes input
void UpdateThread();
// Sets the flag to start or stop the TAS command excecution and swaps controllers profiles
void StartStop();
// Stop the TAS and reverts any controller profile
void Stop();
// Sets the flag to reload the file and start from the begining in the next update
void Reset();
/**
* Sets the flag to enable or disable recording of inputs
* @return Returns true if the current recording status is enabled
*/
bool Record();
// Saves contents of record_commands on a file if overwrite is enabled player 1 will be
// overwritten with the recorded commands
void SaveRecording(bool overwrite_file);
/**
* Returns the current status values of TAS playback/recording
* @return Tuple of
* TasState indicating the current state out of Running, Recording or Stopped ;
* Current playback progress or amount of frames (so far) for Recording ;
* Total length of script file currently loaded or amount of frames (so far) for Recording
*/
std::tuple<TasState, size_t, size_t> GetStatus() const;
// Retuns an array of the default button mappings
InputCommon::ButtonMapping GetButtonMappingForDevice(const Common::ParamPackage& params) const;
// Retuns an array of the default analog mappings
InputCommon::AnalogMapping GetAnalogMappingForDevice(const Common::ParamPackage& params) const;
[[nodiscard]] const TasData& GetTasState(std::size_t pad) const;
private:
struct TASCommand {
u32 buttons{};
TasAnalog l_axis{};
TasAnalog r_axis{};
};
// Loads TAS files from all players
void LoadTasFiles();
// Loads TAS file from the specified player
void LoadTasFile(size_t player_index);
// Writes a TAS file from the recorded commands
void WriteTasFile(std::u8string file_name);
/**
* Parses a string containing the axis values with the following format "x;y"
* X and Y have a range from -32767 to 32767
* @return Returns a TAS analog object with axis values with range from -1.0 to 1.0
*/
TasAnalog ReadCommandAxis(const std::string& line) const;
/**
* Parses a string containing the button values with the following format "a;b;c;d..."
* Each button is represented by it's text format specified in text_to_tas_button array
* @return Returns a u32 with each bit representing the status of a button
*/
u32 ReadCommandButtons(const std::string& line) const;
/**
* Converts an u32 containing the button status into the text equivalent
* @return Returns a string with the name of the buttons to be written to the file
*/
std::string WriteCommandButtons(u32 data) const;
/**
* Converts an TAS analog object containing the axis status into the text equivalent
* @return Returns a string with the value of the axis to be written to the file
*/
std::string WriteCommandAxis(TasAnalog data) const;
// Inverts the Y axis polarity
std::pair<float, float> FlipAxisY(std::pair<float, float> old);
/**
* Converts an u32 containing the button status into the text equivalent
* @return Returns a string with the name of the buttons to be printed on console
*/
std::string DebugButtons(u32 buttons) const;
/**
* Converts an TAS analog object containing the axis status into the text equivalent
* @return Returns a string with the value of the axis to be printed on console
*/
std::string DebugJoystick(float x, float y) const;
/**
* Converts the given TAS status into the text equivalent
* @return Returns a string with the value of the TAS status to be printed on console
*/
std::string DebugInput(const TasData& data) const;
/**
* Converts the given TAS status of multiple players into the text equivalent
* @return Returns a string with the value of the status of all TAS players to be printed on
* console
*/
std::string DebugInputs(const std::array<TasData, PLAYER_NUMBER>& arr) const;
/**
* Converts an u32 containing the button status into the text equivalent
* @return Returns a string with the name of the buttons
*/
std::string ButtonsToString(u32 button) const;
// Stores current controller configuration and sets a TAS controller for every active controller
// to the current config
void SwapToTasController();
// Sets the stored controller configuration to the current config
void SwapToStoredController();
size_t script_length{0};
std::array<TasData, PLAYER_NUMBER> tas_data;
bool is_old_input_saved{false};
bool is_recording{false};
bool is_running{false};
bool needs_reset{false};
std::array<std::vector<TASCommand>, PLAYER_NUMBER> commands{};
std::vector<TASCommand> record_commands{};
size_t current_command{0};
TASCommand last_input{}; // only used for recording
// Old settings for swapping controllers
std::array<Settings::PlayerInput, 10> player_mappings;
};
} // namespace TasInput

View File

@@ -0,0 +1,101 @@
// Copyright 2021 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <mutex>
#include <utility>
#include "common/settings.h"
#include "common/threadsafe_queue.h"
#include "input_common/tas/tas_input.h"
#include "input_common/tas/tas_poller.h"
namespace InputCommon {
class TasButton final : public Input::ButtonDevice {
public:
explicit TasButton(u32 button_, u32 pad_, const TasInput::Tas* tas_input_)
: button(button_), pad(pad_), tas_input(tas_input_) {}
bool GetStatus() const override {
return (tas_input->GetTasState(pad).buttons & button) != 0;
}
private:
const u32 button;
const u32 pad;
const TasInput::Tas* tas_input;
};
TasButtonFactory::TasButtonFactory(std::shared_ptr<TasInput::Tas> tas_input_)
: tas_input(std::move(tas_input_)) {}
std::unique_ptr<Input::ButtonDevice> TasButtonFactory::Create(const Common::ParamPackage& params) {
const auto button_id = params.Get("button", 0);
const auto pad = params.Get("pad", 0);
return std::make_unique<TasButton>(button_id, pad, tas_input.get());
}
class TasAnalog final : public Input::AnalogDevice {
public:
explicit TasAnalog(u32 pad_, u32 axis_x_, u32 axis_y_, const TasInput::Tas* tas_input_)
: pad(pad_), axis_x(axis_x_), axis_y(axis_y_), tas_input(tas_input_) {}
float GetAxis(u32 axis) const {
std::lock_guard lock{mutex};
return tas_input->GetTasState(pad).axis.at(axis);
}
std::pair<float, float> GetAnalog(u32 analog_axis_x, u32 analog_axis_y) const {
float x = GetAxis(analog_axis_x);
float y = GetAxis(analog_axis_y);
// Make sure the coordinates are in the unit circle,
// otherwise normalize it.
float r = x * x + y * y;
if (r > 1.0f) {
r = std::sqrt(r);
x /= r;
y /= r;
}
return {x, y};
}
std::tuple<float, float> GetStatus() const override {
return GetAnalog(axis_x, axis_y);
}
Input::AnalogProperties GetAnalogProperties() const override {
return {0.0f, 1.0f, 0.5f};
}
private:
const u32 pad;
const u32 axis_x;
const u32 axis_y;
const TasInput::Tas* tas_input;
mutable std::mutex mutex;
};
/// An analog device factory that creates analog devices from GC Adapter
TasAnalogFactory::TasAnalogFactory(std::shared_ptr<TasInput::Tas> tas_input_)
: tas_input(std::move(tas_input_)) {}
/**
* Creates analog device from joystick axes
* @param params contains parameters for creating the device:
* - "port": the nth gcpad on the adapter
* - "axis_x": the index of the axis to be bind as x-axis
* - "axis_y": the index of the axis to be bind as y-axis
*/
std::unique_ptr<Input::AnalogDevice> TasAnalogFactory::Create(const Common::ParamPackage& params) {
const auto pad = static_cast<u32>(params.Get("pad", 0));
const auto axis_x = static_cast<u32>(params.Get("axis_x", 0));
const auto axis_y = static_cast<u32>(params.Get("axis_y", 1));
return std::make_unique<TasAnalog>(pad, axis_x, axis_y, tas_input.get());
}
} // namespace InputCommon

View File

@@ -0,0 +1,43 @@
// Copyright 2021 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <memory>
#include "core/frontend/input.h"
#include "input_common/tas/tas_input.h"
namespace InputCommon {
/**
* A button device factory representing a tas bot. It receives tas events and forward them
* to all button devices it created.
*/
class TasButtonFactory final : public Input::Factory<Input::ButtonDevice> {
public:
explicit TasButtonFactory(std::shared_ptr<TasInput::Tas> tas_input_);
/**
* Creates a button device from a button press
* @param params contains parameters for creating the device:
* - "code": the code of the key to bind with the button
*/
std::unique_ptr<Input::ButtonDevice> Create(const Common::ParamPackage& params) override;
private:
std::shared_ptr<TasInput::Tas> tas_input;
};
/// An analog device factory that creates analog devices from tas
class TasAnalogFactory final : public Input::Factory<Input::AnalogDevice> {
public:
explicit TasAnalogFactory(std::shared_ptr<TasInput::Tas> tas_input_);
std::unique_ptr<Input::AnalogDevice> Create(const Common::ParamPackage& params) override;
private:
std::shared_ptr<TasInput::Tas> tas_input;
};
} // namespace InputCommon

View File

@@ -21,8 +21,6 @@
namespace InputCommon::CemuhookUDP {
constexpr char DEFAULT_SRV[] = "127.0.0.1:26760";
class Socket;
namespace Response {

View File

@@ -58,8 +58,8 @@ void GetCbuf(EmitContext& ctx, std::string_view ret, const IR::Value& binding,
const auto cbuf{fmt::format("{}_cbuf{}", ctx.stage_name, binding.U32())};
const auto cbuf_cast{fmt::format("{}({}[{}]{{}})", cast, cbuf, index)};
const auto extraction{num_bits == 32 ? cbuf_cast
: fmt ::format("bitfieldExtract({},int({}),{})", cbuf_cast,
bit_offset, num_bits)};
: fmt::format("bitfieldExtract({},int({}),{})", cbuf_cast,
bit_offset, num_bits)};
if (!component_indexing_bug) {
const auto result{fmt::format(fmt::runtime(extraction), swizzle)};
ctx.Add("{}={};", ret, result);

View File

@@ -477,7 +477,13 @@ void EmitSetSampleMask(EmitContext& ctx, Id value) {
}
void EmitSetFragDepth(EmitContext& ctx, Id value) {
ctx.OpStore(ctx.frag_depth, value);
if (!ctx.runtime_info.convert_depth_mode) {
ctx.OpStore(ctx.frag_depth, value);
return;
}
const Id unit{ctx.Const(0.5f)};
const Id new_depth{ctx.OpFma(ctx.F32[1], value, unit, unit)};
ctx.OpStore(ctx.frag_depth, new_depth);
}
void EmitGetZFlag(EmitContext&) {

View File

@@ -11,14 +11,16 @@
namespace Shader {
template <typename T>
requires std::is_destructible_v<T> class ObjectPool {
requires std::is_destructible_v<T>
class ObjectPool {
public:
explicit ObjectPool(size_t chunk_size = 8192) : new_chunk_size{chunk_size} {
node = &chunks.emplace_back(new_chunk_size);
}
template <typename... Args>
requires std::is_constructible_v<T, Args...>[[nodiscard]] T* Create(Args&&... args) {
requires std::is_constructible_v<T, Args...>
[[nodiscard]] T* Create(Args&&... args) {
return std::construct_at(Memory(), std::forward<Args>(args)...);
}

View File

@@ -570,13 +570,12 @@ bool BufferCache<P>::DMACopy(GPUVAddr src_address, GPUVAddr dest_address, u64 am
ForEachWrittenRange(*cpu_src_address, amount, mirror);
// This subtraction in this order is important for overlapping copies.
common_ranges.subtract(subtract_interval);
bool atleast_1_download = tmp_intervals.size() != 0;
for (const IntervalType add_interval : tmp_intervals) {
const bool has_new_downloads = tmp_intervals.size() != 0;
for (const IntervalType& add_interval : tmp_intervals) {
common_ranges.add(add_interval);
}
runtime.CopyBuffer(dest_buffer, src_buffer, copies);
if (atleast_1_download) {
if (has_new_downloads) {
dest_buffer.MarkRegionAsGpuModified(*cpu_dest_address, amount);
}
std::vector<u8> tmp_buffer(amount);

View File

@@ -82,41 +82,41 @@ void MaxwellDMA::Launch() {
}
void MaxwellDMA::CopyPitchToPitch() {
// When `multi_line_enable` bit is disabled the copy is performed as if we were copying a 1D
// buffer of length `line_length_in`.
// Otherwise we copy a 2D image of dimensions (line_length_in, line_count).
auto& accelerate = rasterizer->AccessAccelerateDMA();
if (!regs.launch_dma.multi_line_enable) {
const bool is_buffer_clear = regs.launch_dma.remap_enable != 0 &&
regs.remap_const.dst_x == RemapConst::Swizzle::CONST_A;
// TODO: allow multisized components.
if (is_buffer_clear) {
ASSERT(regs.remap_const.component_size_minus_one == 3);
accelerate.BufferClear(regs.offset_out, regs.line_length_in, regs.remap_consta_value);
std::vector<u32> tmp_buffer(regs.line_length_in, regs.remap_consta_value);
memory_manager.WriteBlockUnsafe(regs.offset_out,
reinterpret_cast<u8*>(tmp_buffer.data()),
regs.line_length_in * sizeof(u32));
return;
}
UNIMPLEMENTED_IF(regs.launch_dma.remap_enable != 0);
if (!accelerate.BufferCopy(regs.offset_in, regs.offset_out, regs.line_length_in)) {
std::vector<u8> tmp_buffer(regs.line_length_in);
memory_manager.ReadBlockUnsafe(regs.offset_in, tmp_buffer.data(), regs.line_length_in);
memory_manager.WriteBlock(regs.offset_out, tmp_buffer.data(), regs.line_length_in);
// When `multi_line_enable` bit is enabled we copy a 2D image of dimensions
// (line_length_in, line_count).
// Otherwise the copy is performed as if we were copying a 1D buffer of length line_length_in.
const bool remap_enabled = regs.launch_dma.remap_enable != 0;
if (regs.launch_dma.multi_line_enable) {
UNIMPLEMENTED_IF(remap_enabled);
// Perform a line-by-line copy.
// We're going to take a subrect of size (line_length_in, line_count) from the source
// rectangle. There is no need to manually flush/invalidate the regions because CopyBlock
// does that for us.
for (u32 line = 0; line < regs.line_count; ++line) {
const GPUVAddr source_line = regs.offset_in + static_cast<size_t>(line) * regs.pitch_in;
const GPUVAddr dest_line = regs.offset_out + static_cast<size_t>(line) * regs.pitch_out;
memory_manager.CopyBlock(dest_line, source_line, regs.line_length_in);
}
return;
}
UNIMPLEMENTED_IF(regs.launch_dma.remap_enable != 0);
// Perform a line-by-line copy.
// We're going to take a subrect of size (line_length_in, line_count) from the source rectangle.
// There is no need to manually flush/invalidate the regions because CopyBlock does that for us.
for (u32 line = 0; line < regs.line_count; ++line) {
const GPUVAddr source_line = regs.offset_in + static_cast<size_t>(line) * regs.pitch_in;
const GPUVAddr dest_line = regs.offset_out + static_cast<size_t>(line) * regs.pitch_out;
memory_manager.CopyBlock(dest_line, source_line, regs.line_length_in);
// TODO: allow multisized components.
auto& accelerate = rasterizer->AccessAccelerateDMA();
const bool is_const_a_dst = regs.remap_const.dst_x == RemapConst::Swizzle::CONST_A;
const bool is_buffer_clear = remap_enabled && is_const_a_dst;
if (is_buffer_clear) {
ASSERT(regs.remap_const.component_size_minus_one == 3);
accelerate.BufferClear(regs.offset_out, regs.line_length_in, regs.remap_consta_value);
std::vector<u32> tmp_buffer(regs.line_length_in, regs.remap_consta_value);
memory_manager.WriteBlockUnsafe(regs.offset_out, reinterpret_cast<u8*>(tmp_buffer.data()),
regs.line_length_in * sizeof(u32));
return;
}
UNIMPLEMENTED_IF(remap_enabled);
if (!accelerate.BufferCopy(regs.offset_in, regs.offset_out, regs.line_length_in)) {
std::vector<u8> tmp_buffer(regs.line_length_in);
memory_manager.ReadBlockUnsafe(regs.offset_in, tmp_buffer.data(), regs.line_length_in);
memory_manager.WriteBlock(regs.offset_out, tmp_buffer.data(), regs.line_length_in);
}
}

View File

@@ -175,7 +175,7 @@ public:
static_assert(sizeof(LaunchDMA) == 4);
struct RemapConst {
enum Swizzle : u32 {
enum class Swizzle : u32 {
SRC_X = 0,
SRC_Y = 1,
SRC_Z = 2,

View File

@@ -531,14 +531,6 @@ void GPU::TriggerCpuInterrupt(const u32 syncpoint_id, const u32 value) const {
interrupt_manager.GPUInterruptSyncpt(syncpoint_id, value);
}
void GPU::ShutDown() {
// Signal that threads should no longer block on syncpoint fences
shutting_down.store(true, std::memory_order_relaxed);
sync_cv.notify_all();
gpu_thread.ShutDown();
}
void GPU::OnCommandListEnd() {
if (is_async) {
// This command only applies to asynchronous GPU mode

View File

@@ -219,9 +219,6 @@ public:
return *shader_notify;
}
// Stops the GPU execution and waits for the GPU to finish working
void ShutDown();
/// Allows the CPU/NvFlinger to wait on the GPU before presenting a frame.
void WaitFence(u32 syncpoint_id, u32 value);

View File

@@ -17,9 +17,9 @@
namespace VideoCommon::GPUThread {
/// Runs the GPU thread
static void RunThread(Core::System& system, VideoCore::RendererBase& renderer,
Core::Frontend::GraphicsContext& context, Tegra::DmaPusher& dma_pusher,
SynchState& state) {
static void RunThread(std::stop_token stop_token, Core::System& system,
VideoCore::RendererBase& renderer, Core::Frontend::GraphicsContext& context,
Tegra::DmaPusher& dma_pusher, SynchState& state) {
std::string name = "yuzu:GPU";
MicroProfileOnThreadCreate(name.c_str());
SCOPE_EXIT({ MicroProfileOnThreadExit(); });
@@ -28,20 +28,14 @@ static void RunThread(Core::System& system, VideoCore::RendererBase& renderer,
Common::SetCurrentThreadPriority(Common::ThreadPriority::High);
system.RegisterHostThread();
// Wait for first GPU command before acquiring the window context
state.queue.Wait();
// If emulation was stopped during disk shader loading, abort before trying to acquire context
if (!state.is_running) {
return;
}
auto current_context = context.Acquire();
VideoCore::RasterizerInterface* const rasterizer = renderer.ReadRasterizer();
CommandDataContainer next;
while (state.is_running) {
next = state.queue.PopWait();
while (!stop_token.stop_requested()) {
CommandDataContainer next = state.queue.PopWait(stop_token);
if (stop_token.stop_requested()) {
break;
}
if (auto* submit_list = std::get_if<SubmitListCommand>(&next.data)) {
dma_pusher.Push(std::move(submit_list->entries));
dma_pusher.DispatchCalls();
@@ -55,8 +49,6 @@ static void RunThread(Core::System& system, VideoCore::RendererBase& renderer,
rasterizer->FlushRegion(flush->addr, flush->size);
} else if (const auto* invalidate = std::get_if<InvalidateRegionCommand>(&next.data)) {
rasterizer->OnCPUWrite(invalidate->addr, invalidate->size);
} else if (std::holds_alternative<EndProcessingCommand>(next.data)) {
ASSERT(state.is_running == false);
} else {
UNREACHABLE();
}
@@ -73,16 +65,14 @@ static void RunThread(Core::System& system, VideoCore::RendererBase& renderer,
ThreadManager::ThreadManager(Core::System& system_, bool is_async_)
: system{system_}, is_async{is_async_} {}
ThreadManager::~ThreadManager() {
ShutDown();
}
ThreadManager::~ThreadManager() = default;
void ThreadManager::StartThread(VideoCore::RendererBase& renderer,
Core::Frontend::GraphicsContext& context,
Tegra::DmaPusher& dma_pusher) {
rasterizer = renderer.ReadRasterizer();
thread = std::thread(RunThread, std::ref(system), std::ref(renderer), std::ref(context),
std::ref(dma_pusher), std::ref(state));
thread = std::jthread(RunThread, std::ref(system), std::ref(renderer), std::ref(context),
std::ref(dma_pusher), std::ref(state));
}
void ThreadManager::SubmitList(Tegra::CommandList&& entries) {
@@ -117,26 +107,6 @@ void ThreadManager::FlushAndInvalidateRegion(VAddr addr, u64 size) {
rasterizer->OnCPUWrite(addr, size);
}
void ThreadManager::ShutDown() {
if (!state.is_running) {
return;
}
{
std::lock_guard lk(state.write_lock);
state.is_running = false;
state.cv.notify_all();
}
if (!thread.joinable()) {
return;
}
// Notify GPU thread that a shutdown is pending
PushCommand(EndProcessingCommand());
thread.join();
}
void ThreadManager::OnCommandListEnd() {
PushCommand(OnCommandListEndCommand());
}
@@ -152,9 +122,8 @@ u64 ThreadManager::PushCommand(CommandData&& command_data, bool block) {
state.queue.Push(CommandDataContainer(std::move(command_data), fence, block));
if (block) {
state.cv.wait(lk, [this, fence] {
return fence <= state.signaled_fence.load(std::memory_order_relaxed) ||
!state.is_running;
state.cv.wait(lk, thread.get_stop_token(), [this, fence] {
return fence <= state.signaled_fence.load(std::memory_order_relaxed);
});
}

View File

@@ -33,9 +33,6 @@ class RendererBase;
namespace VideoCommon::GPUThread {
/// Command to signal to the GPU thread that processing has ended
struct EndProcessingCommand final {};
/// Command to signal to the GPU thread that a command list is ready for processing
struct SubmitListCommand final {
explicit SubmitListCommand(Tegra::CommandList&& entries_) : entries{std::move(entries_)} {}
@@ -83,7 +80,7 @@ struct OnCommandListEndCommand final {};
struct GPUTickCommand final {};
using CommandData =
std::variant<EndProcessingCommand, SubmitListCommand, SwapBuffersCommand, FlushRegionCommand,
std::variant<std::monostate, SubmitListCommand, SwapBuffersCommand, FlushRegionCommand,
InvalidateRegionCommand, FlushAndInvalidateRegionCommand, OnCommandListEndCommand,
GPUTickCommand>;
@@ -100,14 +97,12 @@ struct CommandDataContainer {
/// Struct used to synchronize the GPU thread
struct SynchState final {
std::atomic_bool is_running{true};
using CommandQueue = Common::SPSCQueue<CommandDataContainer>;
using CommandQueue = Common::SPSCQueue<CommandDataContainer, true>;
std::mutex write_lock;
CommandQueue queue;
u64 last_fence{};
std::atomic<u64> signaled_fence{};
std::condition_variable cv;
std::condition_variable_any cv;
};
/// Class used to manage the GPU thread
@@ -149,7 +144,7 @@ private:
VideoCore::RasterizerInterface* rasterizer = nullptr;
SynchState state;
std::thread thread;
std::jthread thread;
};
} // namespace VideoCommon::GPUThread

View File

@@ -6,7 +6,6 @@ set(SHADER_FILES
convert_float_to_depth.frag
full_screen_triangle.vert
opengl_copy_bc4.comp
opengl_copy_bgra.comp
opengl_present.frag
opengl_present.vert
pitch_unswizzle.comp

View File

@@ -1,15 +0,0 @@
// Copyright 2021 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#version 430 core
layout (local_size_x = 4, local_size_y = 4) in;
layout(binding = 0, rgba8) readonly uniform image2DArray bgr_input;
layout(binding = 1, rgba8) writeonly uniform image2DArray bgr_output;
void main() {
vec4 color = imageLoad(bgr_input, ivec3(gl_GlobalInvocationID));
imageStore(bgr_output, ivec3(gl_GlobalInvocationID), color.bgra);
}

View File

@@ -147,8 +147,7 @@ void BufferCacheRuntime::CopyBuffer(Buffer& dst_buffer, Buffer& src_buffer,
void BufferCacheRuntime::ClearBuffer(Buffer& dest_buffer, u32 offset, size_t size, u32 value) {
glClearNamedBufferSubData(dest_buffer.Handle(), GL_R32UI, static_cast<GLintptr>(offset),
static_cast<GLsizeiptr>(size / sizeof(u32)), GL_RED, GL_UNSIGNED_INT,
&value);
static_cast<GLsizeiptr>(size), GL_RED, GL_UNSIGNED_INT, &value);
}
void BufferCacheRuntime::BindIndexBuffer(Buffer& buffer, u32 offset, u32 size) {

View File

@@ -461,7 +461,7 @@ bool TextureCacheRuntime::CanImageBeCopied(const Image& dst, const Image& src) {
if (dst.info.type == ImageType::e3D && dst.info.format == PixelFormat::BC4_UNORM) {
return false;
}
if (IsPixelFormatBGR(dst.info.format) || IsPixelFormatBGR(src.info.format)) {
if (IsPixelFormatBGR(dst.info.format) != IsPixelFormatBGR(src.info.format)) {
return false;
}
return true;
@@ -473,7 +473,7 @@ void TextureCacheRuntime::EmulateCopyImage(Image& dst, Image& src,
ASSERT(src.info.type == ImageType::e3D);
util_shaders.CopyBC4(dst, src, copies);
} else if (IsPixelFormatBGR(dst.info.format) || IsPixelFormatBGR(src.info.format)) {
util_shaders.CopyBGR(dst, src, copies);
bgr_copy_pass.CopyBGR(dst, src, copies);
} else {
UNREACHABLE();
}
@@ -1112,4 +1112,37 @@ Framebuffer::Framebuffer(TextureCacheRuntime& runtime, std::span<ImageView*, NUM
framebuffer.handle = handle;
}
void BGRCopyPass::CopyBGR(Image& dst_image, Image& src_image,
std::span<const VideoCommon::ImageCopy> copies) {
static constexpr VideoCommon::Offset3D zero_offset{0, 0, 0};
const u32 requested_pbo_size =
std::max(src_image.unswizzled_size_bytes, dst_image.unswizzled_size_bytes);
if (bgr_pbo_size < requested_pbo_size) {
bgr_pbo.Create();
bgr_pbo_size = requested_pbo_size;
glNamedBufferData(bgr_pbo.handle, bgr_pbo_size, nullptr, GL_STREAM_COPY);
}
for (const ImageCopy& copy : copies) {
ASSERT(copy.src_offset == zero_offset);
ASSERT(copy.dst_offset == zero_offset);
// Copy from source to PBO
glPixelStorei(GL_PACK_ALIGNMENT, 1);
glPixelStorei(GL_PACK_ROW_LENGTH, copy.extent.width);
glBindBuffer(GL_PIXEL_PACK_BUFFER, bgr_pbo.handle);
glGetTextureSubImage(src_image.Handle(), 0, 0, 0, 0, copy.extent.width, copy.extent.height,
copy.src_subresource.num_layers, src_image.GlFormat(),
src_image.GlType(), static_cast<GLsizei>(bgr_pbo_size), nullptr);
// Copy from PBO to destination in desired GL format
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
glPixelStorei(GL_UNPACK_ROW_LENGTH, copy.extent.width);
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, bgr_pbo.handle);
glTextureSubImage3D(dst_image.Handle(), 0, 0, 0, 0, copy.extent.width, copy.extent.height,
copy.dst_subresource.num_layers, dst_image.GlFormat(),
dst_image.GlType(), nullptr);
}
}
} // namespace OpenGL

View File

@@ -47,6 +47,19 @@ struct FormatProperties {
bool is_compressed;
};
class BGRCopyPass {
public:
BGRCopyPass() = default;
~BGRCopyPass() = default;
void CopyBGR(Image& dst_image, Image& src_image,
std::span<const VideoCommon::ImageCopy> copies);
private:
OGLBuffer bgr_pbo;
size_t bgr_pbo_size{};
};
class TextureCacheRuntime {
friend Framebuffer;
friend Image;
@@ -118,6 +131,7 @@ private:
const Device& device;
StateTracker& state_tracker;
UtilShaders util_shaders;
BGRCopyPass bgr_copy_pass;
std::array<std::unordered_map<GLenum, FormatProperties>, 3> format_properties;
bool has_broken_texture_view_formats = false;
@@ -162,6 +176,14 @@ public:
return texture.handle;
}
GLuint GlFormat() const noexcept {
return gl_format;
}
GLuint GlType() const noexcept {
return gl_type;
}
private:
void CopyBufferToImage(const VideoCommon::BufferImageCopy& copy, size_t buffer_offset);

View File

@@ -52,7 +52,7 @@ constexpr std::array<FormatTuple, VideoCore::Surface::MaxPixelFormat> FORMAT_TAB
{GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT}, // BC6H_UFLOAT
{GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT}, // BC6H_SFLOAT
{GL_COMPRESSED_RGBA_ASTC_4x4_KHR}, // ASTC_2D_4X4_UNORM
{GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE}, // B8G8R8A8_UNORM
{GL_RGBA8, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV}, // B8G8R8A8_UNORM
{GL_RGBA32F, GL_RGBA, GL_FLOAT}, // R32G32B32A32_FLOAT
{GL_RGBA32I, GL_RGBA_INTEGER, GL_INT}, // R32G32B32A32_SINT
{GL_RG32F, GL_RG, GL_FLOAT}, // R32G32_FLOAT
@@ -81,7 +81,7 @@ constexpr std::array<FormatTuple, VideoCore::Surface::MaxPixelFormat> FORMAT_TAB
{GL_COMPRESSED_RGBA_ASTC_8x8_KHR}, // ASTC_2D_8X8_UNORM
{GL_COMPRESSED_RGBA_ASTC_8x5_KHR}, // ASTC_2D_8X5_UNORM
{GL_COMPRESSED_RGBA_ASTC_5x4_KHR}, // ASTC_2D_5X4_UNORM
{GL_SRGB8_ALPHA8, GL_RGBA, GL_UNSIGNED_BYTE}, // B8G8R8A8_SRGB
{GL_SRGB8_ALPHA8, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV}, // B8G8R8A8_SRGB
{GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT}, // BC1_RGBA_SRGB
{GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT}, // BC2_SRGB
{GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT}, // BC3_SRGB

View File

@@ -14,7 +14,6 @@
#include "video_core/host_shaders/block_linear_unswizzle_2d_comp.h"
#include "video_core/host_shaders/block_linear_unswizzle_3d_comp.h"
#include "video_core/host_shaders/opengl_copy_bc4_comp.h"
#include "video_core/host_shaders/opengl_copy_bgra_comp.h"
#include "video_core/host_shaders/pitch_unswizzle_comp.h"
#include "video_core/renderer_opengl/gl_shader_manager.h"
#include "video_core/renderer_opengl/gl_shader_util.h"
@@ -44,11 +43,6 @@ namespace {
OGLProgram MakeProgram(std::string_view source) {
return CreateProgram(source, GL_COMPUTE_SHADER);
}
size_t NumPixelsInCopy(const VideoCommon::ImageCopy& copy) {
return static_cast<size_t>(copy.extent.width * copy.extent.height *
copy.src_subresource.num_layers);
}
} // Anonymous namespace
UtilShaders::UtilShaders(ProgramManager& program_manager_)
@@ -56,7 +50,6 @@ UtilShaders::UtilShaders(ProgramManager& program_manager_)
block_linear_unswizzle_2d_program(MakeProgram(BLOCK_LINEAR_UNSWIZZLE_2D_COMP)),
block_linear_unswizzle_3d_program(MakeProgram(BLOCK_LINEAR_UNSWIZZLE_3D_COMP)),
pitch_unswizzle_program(MakeProgram(PITCH_UNSWIZZLE_COMP)),
copy_bgra_program(MakeProgram(OPENGL_COPY_BGRA_COMP)),
copy_bc4_program(MakeProgram(OPENGL_COPY_BC4_COMP)) {
const auto swizzle_table = Tegra::Texture::MakeSwizzleTable();
swizzle_table_buffer.Create();
@@ -255,43 +248,6 @@ void UtilShaders::CopyBC4(Image& dst_image, Image& src_image, std::span<const Im
program_manager.RestoreGuestCompute();
}
void UtilShaders::CopyBGR(Image& dst_image, Image& src_image,
std::span<const VideoCommon::ImageCopy> copies) {
static constexpr GLuint BINDING_INPUT_IMAGE = 0;
static constexpr GLuint BINDING_OUTPUT_IMAGE = 1;
static constexpr VideoCommon::Offset3D zero_offset{0, 0, 0};
const u32 bytes_per_block = BytesPerBlock(dst_image.info.format);
switch (bytes_per_block) {
case 2:
// BGR565 copy
for (const ImageCopy& copy : copies) {
ASSERT(copy.src_offset == zero_offset);
ASSERT(copy.dst_offset == zero_offset);
bgr_copy_pass.Execute(dst_image, src_image, copy);
}
break;
case 4: {
// BGRA8 copy
program_manager.BindComputeProgram(copy_bgra_program.handle);
constexpr GLenum FORMAT = GL_RGBA8;
for (const ImageCopy& copy : copies) {
ASSERT(copy.src_offset == zero_offset);
ASSERT(copy.dst_offset == zero_offset);
glBindImageTexture(BINDING_INPUT_IMAGE, src_image.StorageHandle(),
copy.src_subresource.base_level, GL_FALSE, 0, GL_READ_ONLY, FORMAT);
glBindImageTexture(BINDING_OUTPUT_IMAGE, dst_image.StorageHandle(),
copy.dst_subresource.base_level, GL_FALSE, 0, GL_WRITE_ONLY, FORMAT);
glDispatchCompute(copy.extent.width, copy.extent.height, copy.extent.depth);
}
program_manager.RestoreGuestCompute();
break;
}
default:
UNREACHABLE();
break;
}
}
GLenum StoreFormat(u32 bytes_per_block) {
switch (bytes_per_block) {
case 1:
@@ -309,36 +265,4 @@ GLenum StoreFormat(u32 bytes_per_block) {
return GL_R8UI;
}
void Bgr565CopyPass::Execute(const Image& dst_image, const Image& src_image,
const ImageCopy& copy) {
if (CopyBufferCreationNeeded(copy)) {
CreateNewCopyBuffer(copy, GL_TEXTURE_2D_ARRAY, GL_RGB565);
}
// Copy from source to PBO
glPixelStorei(GL_PACK_ALIGNMENT, 1);
glPixelStorei(GL_PACK_ROW_LENGTH, copy.extent.width);
glBindBuffer(GL_PIXEL_PACK_BUFFER, bgr16_pbo.handle);
glGetTextureSubImage(src_image.Handle(), 0, 0, 0, 0, copy.extent.width, copy.extent.height,
copy.src_subresource.num_layers, GL_RGB, GL_UNSIGNED_SHORT_5_6_5,
static_cast<GLsizei>(bgr16_pbo_size), nullptr);
// Copy from PBO to destination in reverse order
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
glPixelStorei(GL_UNPACK_ROW_LENGTH, copy.extent.width);
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, bgr16_pbo.handle);
glTextureSubImage3D(dst_image.Handle(), 0, 0, 0, 0, copy.extent.width, copy.extent.height,
copy.dst_subresource.num_layers, GL_RGB, GL_UNSIGNED_SHORT_5_6_5_REV,
nullptr);
}
bool Bgr565CopyPass::CopyBufferCreationNeeded(const ImageCopy& copy) {
return bgr16_pbo_size < NumPixelsInCopy(copy) * sizeof(u16);
}
void Bgr565CopyPass::CreateNewCopyBuffer(const ImageCopy& copy, GLenum target, GLuint format) {
bgr16_pbo.Create();
bgr16_pbo_size = NumPixelsInCopy(copy) * sizeof(u16);
glNamedBufferData(bgr16_pbo.handle, bgr16_pbo_size, nullptr, GL_STREAM_COPY);
}
} // namespace OpenGL

View File

@@ -19,22 +19,6 @@ class ProgramManager;
struct ImageBufferMap;
class Bgr565CopyPass {
public:
Bgr565CopyPass() = default;
~Bgr565CopyPass() = default;
void Execute(const Image& dst_image, const Image& src_image,
const VideoCommon::ImageCopy& copy);
private:
[[nodiscard]] bool CopyBufferCreationNeeded(const VideoCommon::ImageCopy& copy);
void CreateNewCopyBuffer(const VideoCommon::ImageCopy& copy, GLenum target, GLuint format);
OGLBuffer bgr16_pbo;
size_t bgr16_pbo_size{};
};
class UtilShaders {
public:
explicit UtilShaders(ProgramManager& program_manager);
@@ -55,9 +39,6 @@ public:
void CopyBC4(Image& dst_image, Image& src_image,
std::span<const VideoCommon::ImageCopy> copies);
void CopyBGR(Image& dst_image, Image& src_image,
std::span<const VideoCommon::ImageCopy> copies);
private:
ProgramManager& program_manager;
@@ -67,10 +48,7 @@ private:
OGLProgram block_linear_unswizzle_2d_program;
OGLProgram block_linear_unswizzle_3d_program;
OGLProgram pitch_unswizzle_program;
OGLProgram copy_bgra_program;
OGLProgram copy_bc4_program;
Bgr565CopyPass bgr_copy_pass;
};
GLenum StoreFormat(u32 bytes_per_block);

View File

@@ -97,19 +97,14 @@ RendererVulkan::RendererVulkan(Core::TelemetrySession& telemetry_session_,
Core::Frontend::EmuWindow& emu_window,
Core::Memory::Memory& cpu_memory_, Tegra::GPU& gpu_,
std::unique_ptr<Core::Frontend::GraphicsContext> context_) try
: RendererBase(emu_window, std::move(context_)),
telemetry_session(telemetry_session_),
cpu_memory(cpu_memory_),
gpu(gpu_),
library(OpenLibrary()),
: RendererBase(emu_window, std::move(context_)), telemetry_session(telemetry_session_),
cpu_memory(cpu_memory_), gpu(gpu_), library(OpenLibrary()),
instance(CreateInstance(library, dld, VK_API_VERSION_1_1, render_window.GetWindowInfo().type,
true, Settings::values.renderer_debug.GetValue())),
debug_callback(Settings::values.renderer_debug ? CreateDebugCallback(instance) : nullptr),
surface(CreateSurface(instance, render_window)),
device(CreateDevice(instance, dld, *surface)),
memory_allocator(device, false),
state_tracker(gpu),
scheduler(device, state_tracker),
device(CreateDevice(instance, dld, *surface)), memory_allocator(device, false),
state_tracker(gpu), scheduler(device, state_tracker),
swapchain(*surface, device, scheduler, render_window.GetFramebufferLayout().width,
render_window.GetFramebufferLayout().height, false),
blit_screen(cpu_memory, render_window, device, memory_allocator, swapchain, scheduler,
@@ -149,7 +144,7 @@ void RendererVulkan::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) {
const Layout::FramebufferLayout layout = render_window.GetFramebufferLayout();
swapchain.Create(layout.width, layout.height, is_srgb);
};
if (swapchain.IsSubOptimal() || swapchain.HasColorSpaceChanged(is_srgb)) {
if (swapchain.NeedsRecreation(is_srgb)) {
recreate_swapchain();
}
bool is_outdated;

View File

@@ -507,8 +507,9 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) {
vertex_attributes.push_back({
.location = static_cast<u32>(index),
.binding = 0,
.format = type == 1 ? VK_FORMAT_R32_SFLOAT
: type == 2 ? VK_FORMAT_R32_SINT : VK_FORMAT_R32_UINT,
.format = type == 1 ? VK_FORMAT_R32_SFLOAT
: type == 2 ? VK_FORMAT_R32_SINT
: VK_FORMAT_R32_UINT,
.offset = 0,
});
}

View File

@@ -43,17 +43,10 @@ VKScheduler::VKScheduler(const Device& device_, StateTracker& state_tracker_)
command_pool{std::make_unique<CommandPool>(*master_semaphore, device)} {
AcquireNewChunk();
AllocateWorkerCommandBuffer();
worker_thread = std::thread(&VKScheduler::WorkerThread, this);
worker_thread = std::jthread([this](std::stop_token token) { WorkerThread(token); });
}
VKScheduler::~VKScheduler() {
{
std::lock_guard lock{work_mutex};
quit = true;
}
work_cv.notify_all();
worker_thread.join();
}
VKScheduler::~VKScheduler() = default;
void VKScheduler::Flush(VkSemaphore signal_semaphore, VkSemaphore wait_semaphore) {
SubmitExecution(signal_semaphore, wait_semaphore);
@@ -135,7 +128,7 @@ bool VKScheduler::UpdateGraphicsPipeline(GraphicsPipeline* pipeline) {
return true;
}
void VKScheduler::WorkerThread() {
void VKScheduler::WorkerThread(std::stop_token stop_token) {
Common::SetCurrentThreadName("yuzu:VulkanWorker");
do {
if (work_queue.empty()) {
@@ -144,8 +137,8 @@ void VKScheduler::WorkerThread() {
std::unique_ptr<CommandChunk> work;
{
std::unique_lock lock{work_mutex};
work_cv.wait(lock, [this] { return !work_queue.empty() || quit; });
if (quit) {
work_cv.wait(lock, stop_token, [this] { return !work_queue.empty(); });
if (stop_token.stop_requested()) {
continue;
}
work = std::move(work_queue.front());
@@ -158,7 +151,7 @@ void VKScheduler::WorkerThread() {
}
std::lock_guard reserve_lock{reserve_mutex};
chunk_reserve.push_back(std::move(work));
} while (!quit);
} while (!stop_token.stop_requested());
}
void VKScheduler::AllocateWorkerCommandBuffer() {

View File

@@ -187,7 +187,7 @@ private:
GraphicsPipeline* graphics_pipeline = nullptr;
};
void WorkerThread();
void WorkerThread(std::stop_token stop_token);
void AllocateWorkerCommandBuffer();
@@ -212,7 +212,6 @@ private:
vk::CommandBuffer current_cmdbuf;
std::unique_ptr<CommandChunk> chunk;
std::thread worker_thread;
State state;
@@ -224,9 +223,9 @@ private:
std::vector<std::unique_ptr<CommandChunk>> chunk_reserve;
std::mutex reserve_mutex;
std::mutex work_mutex;
std::condition_variable work_cv;
std::condition_variable_any work_cv;
std::condition_variable wait_cv;
std::atomic_bool quit{};
std::jthread worker_thread;
};
} // namespace Vulkan

View File

@@ -9,6 +9,7 @@
#include "common/assert.h"
#include "common/logging/log.h"
#include "common/settings.h"
#include "core/core.h"
#include "core/frontend/framebuffer_layout.h"
#include "video_core/renderer_vulkan/vk_scheduler.h"
@@ -36,8 +37,19 @@ VkSurfaceFormatKHR ChooseSwapSurfaceFormat(vk::Span<VkSurfaceFormatKHR> formats)
VkPresentModeKHR ChooseSwapPresentMode(vk::Span<VkPresentModeKHR> modes) {
// Mailbox doesn't lock the application like fifo (vsync), prefer it
const auto found = std::find(modes.begin(), modes.end(), VK_PRESENT_MODE_MAILBOX_KHR);
return found != modes.end() ? *found : VK_PRESENT_MODE_FIFO_KHR;
const auto found_mailbox = std::find(modes.begin(), modes.end(), VK_PRESENT_MODE_MAILBOX_KHR);
if (found_mailbox != modes.end()) {
return VK_PRESENT_MODE_MAILBOX_KHR;
}
if (Settings::values.disable_fps_limit.GetValue()) {
// FIFO present mode locks the framerate to the monitor's refresh rate,
// Find an alternative to surpass this limitation if FPS is unlocked.
const auto found_imm = std::find(modes.begin(), modes.end(), VK_PRESENT_MODE_IMMEDIATE_KHR);
if (found_imm != modes.end()) {
return VK_PRESENT_MODE_IMMEDIATE_KHR;
}
}
return VK_PRESENT_MODE_FIFO_KHR;
}
VkExtent2D ChooseSwapExtent(const VkSurfaceCapabilitiesKHR& capabilities, u32 width, u32 height) {
@@ -143,7 +155,7 @@ void VKSwapchain::CreateSwapchain(const VkSurfaceCapabilitiesKHR& capabilities,
const auto present_modes{physical_device.GetSurfacePresentModesKHR(surface)};
const VkSurfaceFormatKHR surface_format{ChooseSwapSurfaceFormat(formats)};
const VkPresentModeKHR present_mode{ChooseSwapPresentMode(present_modes)};
present_mode = ChooseSwapPresentMode(present_modes);
u32 requested_image_count{capabilities.minImageCount + 1};
if (capabilities.maxImageCount > 0 && requested_image_count > capabilities.maxImageCount) {
@@ -196,6 +208,7 @@ void VKSwapchain::CreateSwapchain(const VkSurfaceCapabilitiesKHR& capabilities,
extent = swapchain_ci.imageExtent;
current_srgb = srgb;
current_fps_unlocked = Settings::values.disable_fps_limit.GetValue();
images = swapchain.GetImages();
image_count = static_cast<u32>(images.size());
@@ -248,4 +261,14 @@ void VKSwapchain::Destroy() {
swapchain.reset();
}
bool VKSwapchain::HasFpsUnlockChanged() const {
return current_fps_unlocked != Settings::values.disable_fps_limit.GetValue();
}
bool VKSwapchain::NeedsPresentModeUpdate() const {
// Mailbox present mode is the ideal for all scenarios. If it is not available,
// A different present mode is needed to support unlocked FPS above the monitor's refresh rate.
return present_mode != VK_PRESENT_MODE_MAILBOX_KHR && HasFpsUnlockChanged();
}
} // namespace Vulkan

View File

@@ -33,6 +33,11 @@ public:
/// Presents the rendered image to the swapchain.
void Present(VkSemaphore render_semaphore);
/// Returns true when the swapchain needs to be recreated.
bool NeedsRecreation(bool is_srgb) const {
return HasColorSpaceChanged(is_srgb) || IsSubOptimal() || NeedsPresentModeUpdate();
}
/// Returns true when the color space has changed.
bool HasColorSpaceChanged(bool is_srgb) const {
return current_srgb != is_srgb;
@@ -84,6 +89,10 @@ private:
void Destroy();
bool HasFpsUnlockChanged() const;
bool NeedsPresentModeUpdate() const;
const VkSurfaceKHR surface;
const Device& device;
VKScheduler& scheduler;
@@ -102,8 +111,10 @@ private:
VkFormat image_view_format{};
VkExtent2D extent{};
VkPresentModeKHR present_mode{};
bool current_srgb{};
bool current_fps_unlocked{};
bool is_outdated{};
bool is_suboptimal{};
};

View File

@@ -127,7 +127,7 @@ constexpr VkBorderColor ConvertBorderColor(const std::array<float, 4>& color) {
const auto format_info = MaxwellToVK::SurfaceFormat(device, FormatType::Optimal, false, format);
VkImageCreateFlags flags = VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT;
if (info.type == ImageType::e2D && info.resources.layers >= 6 &&
info.size.width == info.size.height) {
info.size.width == info.size.height && !device.HasBrokenCubeImageCompability()) {
flags |= VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT;
}
if (info.type == ImageType::e3D) {

View File

@@ -31,8 +31,8 @@ struct SlotId {
};
template <class T>
requires std::is_nothrow_move_assignable_v<T>&&
std::is_nothrow_move_constructible_v<T> class SlotVector {
requires std::is_nothrow_move_assignable_v<T> && std::is_nothrow_move_constructible_v<T>
class SlotVector {
public:
class Iterator {
friend SlotVector<T>;

View File

@@ -16,6 +16,7 @@ VkBool32 Callback(VkDebugUtilsMessageSeverityFlagBitsEXT severity,
switch (static_cast<u32>(data->messageIdNumber)) {
case 0x682a878au: // VUID-vkCmdBindVertexBuffers2EXT-pBuffers-parameter
case 0x99fb7dfdu: // UNASSIGNED-RequiredParameter (vkCmdBindVertexBuffers2EXT pBuffers[0])
case 0xe8616bf2u: // Bound VkDescriptorSet 0x0[] was destroyed. Likely push_descriptor related
return VK_FALSE;
default:
break;

View File

@@ -243,7 +243,6 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
SetupFamilies(surface);
SetupFeatures();
SetupProperties();
CollectTelemetryParameters();
const auto queue_cis = GetDeviceQueueCreateInfos();
const std::vector extensions = LoadExtensions(surface != nullptr);
@@ -369,20 +368,9 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
};
SetNext(next, demote);
if (driver_id == VK_DRIVER_ID_AMD_PROPRIETARY || driver_id == VK_DRIVER_ID_AMD_OPEN_SOURCE) {
const u32 version = properties.driverVersion;
// Broken in this driver
if (version > VK_MAKE_API_VERSION(0, 2, 0, 193)) {
LOG_WARNING(Render_Vulkan, "AMD proprietary driver versions newer than 21.9.1 "
"(windows) / 0.2.0.194 (amdvlk) have "
"broken VkPhysicalDeviceFloat16Int8FeaturesKHR");
is_int8_supported = false;
is_float16_supported = false;
}
}
VkPhysicalDeviceFloat16Int8FeaturesKHR float16_int8;
if (is_int8_supported || is_float16_supported) {
VkPhysicalDeviceFloat16Int8FeaturesKHR float16_int8{
float16_int8 = {
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FLOAT16_INT8_FEATURES_KHR,
.pNext = nullptr,
.shaderFloat16 = is_float16_supported,
@@ -573,6 +561,7 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
logical = vk::Device::Create(physical, queue_cis, extensions, first_next, dld);
CollectPhysicalMemoryInfo();
CollectTelemetryParameters();
CollectToolingInfo();
if (driver_id == VK_DRIVER_ID_NVIDIA_PROPRIETARY_KHR) {
@@ -599,22 +588,27 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
ext_extended_dynamic_state = false;
}
}
sets_per_pool = 64;
if (driver_id == VK_DRIVER_ID_AMD_PROPRIETARY || driver_id == VK_DRIVER_ID_AMD_OPEN_SOURCE) {
const bool is_amd =
driver_id == VK_DRIVER_ID_AMD_PROPRIETARY || driver_id == VK_DRIVER_ID_AMD_OPEN_SOURCE;
if (is_amd) {
// AMD drivers need a higher amount of Sets per Pool in certain circunstances like in XC2.
sets_per_pool = 96;
}
const bool is_amd = driver_id == VK_DRIVER_ID_AMD_PROPRIETARY ||
driver_id == VK_DRIVER_ID_MESA_RADV ||
driver_id == VK_DRIVER_ID_AMD_OPEN_SOURCE;
if (ext_sampler_filter_minmax && is_amd) {
// Disable ext_sampler_filter_minmax on AMD GCN4 and lower as it is broken.
// Disable VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT on AMD GCN4 and lower as it is broken.
if (!is_float16_supported) {
LOG_WARNING(
Render_Vulkan,
"Blacklisting AMD GCN4 and lower for VK_EXT_SAMPLER_FILTER_MINMAX_EXTENSION_NAME");
"AMD GCN4 and earlier do not properly support VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT");
has_broken_cube_compatibility = true;
}
}
const bool is_amd_or_radv = is_amd || driver_id == VK_DRIVER_ID_MESA_RADV;
if (ext_sampler_filter_minmax && is_amd_or_radv) {
// Disable ext_sampler_filter_minmax on AMD GCN4 and lower as it is broken.
if (!is_float16_supported) {
LOG_WARNING(Render_Vulkan,
"Blacklisting AMD GCN4 and earlier for VK_EXT_sampler_filter_minmax");
ext_sampler_filter_minmax = false;
}
}

View File

@@ -309,6 +309,11 @@ public:
return has_renderdoc || has_nsight_graphics;
}
/// Returns true when the device does not properly support cube compatibility.
bool HasBrokenCubeImageCompability() const {
return has_broken_cube_compatibility;
}
/// Returns the vendor name reported from Vulkan.
std::string_view GetVendorName() const {
return vendor_name;
@@ -417,6 +422,7 @@ private:
bool ext_conservative_rasterization{}; ///< Support for VK_EXT_conservative_rasterization.
bool ext_provoking_vertex{}; ///< Support for VK_EXT_provoking_vertex.
bool nv_device_diagnostics_config{}; ///< Support for VK_NV_device_diagnostics_config.
bool has_broken_cube_compatibility{}; ///< Has broken cube compatiblity bit
bool has_renderdoc{}; ///< Has RenderDoc attached
bool has_nsight_graphics{}; ///< Has Nsight Graphics attached

View File

@@ -108,6 +108,9 @@ add_executable(yuzu
configuration/configure_system.cpp
configuration/configure_system.h
configuration/configure_system.ui
configuration/configure_tas.cpp
configuration/configure_tas.h
configuration/configure_tas.ui
configuration/configure_touch_from_button.cpp
configuration/configure_touch_from_button.h
configuration/configure_touch_from_button.ui

View File

@@ -36,6 +36,7 @@
#include "input_common/keyboard.h"
#include "input_common/main.h"
#include "input_common/mouse/mouse_input.h"
#include "input_common/tas/tas_input.h"
#include "video_core/renderer_base.h"
#include "video_core/video_core.h"
#include "yuzu/bootmanager.h"
@@ -312,6 +313,7 @@ GRenderWindow::~GRenderWindow() {
}
void GRenderWindow::OnFrameDisplayed() {
input_subsystem->GetTas()->UpdateThread();
if (!first_frame) {
first_frame = true;
emit FirstFrameDisplayed();

View File

@@ -221,7 +221,7 @@ const std::array<int, Settings::NativeKeyboard::NumKeyboardMods> Config::default
// This must be in alphabetical order according to action name as it must have the same order as
// UISetting::values.shortcuts, which is alphabetically ordered.
// clang-format off
const std::array<UISettings::Shortcut, 18> Config::default_hotkeys{{
const std::array<UISettings::Shortcut, 21> Config::default_hotkeys{{
{QStringLiteral("Capture Screenshot"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+P"), Qt::WidgetWithChildrenShortcut}},
{QStringLiteral("Change Docked Mode"), QStringLiteral("Main Window"), {QStringLiteral("F10"), Qt::ApplicationShortcut}},
{QStringLiteral("Continue/Pause Emulation"), QStringLiteral("Main Window"), {QStringLiteral("F4"), Qt::WindowShortcut}},
@@ -235,6 +235,9 @@ const std::array<UISettings::Shortcut, 18> Config::default_hotkeys{{
{QStringLiteral("Mute Audio"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+M"), Qt::WindowShortcut}},
{QStringLiteral("Restart Emulation"), QStringLiteral("Main Window"), {QStringLiteral("F6"), Qt::WindowShortcut}},
{QStringLiteral("Stop Emulation"), QStringLiteral("Main Window"), {QStringLiteral("F5"), Qt::WindowShortcut}},
{QStringLiteral("TAS Start/Stop"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+F5"), Qt::ApplicationShortcut}},
{QStringLiteral("TAS Reset"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+F6"), Qt::ApplicationShortcut}},
{QStringLiteral("TAS Record"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+F7"), Qt::ApplicationShortcut}},
{QStringLiteral("Toggle Filter Bar"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+F"), Qt::WindowShortcut}},
{QStringLiteral("Toggle Framerate Limit"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+U"), Qt::ApplicationShortcut}},
{QStringLiteral("Toggle Mouse Panning"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+F9"), Qt::ApplicationShortcut}},
@@ -542,7 +545,6 @@ void Config::ReadAudioValues() {
ReadBasicSetting(Settings::values.audio_device_id);
ReadBasicSetting(Settings::values.sink_id);
}
ReadGlobalSetting(Settings::values.enable_audio_stretching);
ReadGlobalSetting(Settings::values.volume);
qt_config->endGroup();
@@ -560,10 +562,20 @@ void Config::ReadControlValues() {
ReadTouchscreenValues();
ReadMotionTouchValues();
#ifdef _WIN32
ReadBasicSetting(Settings::values.enable_raw_input);
#else
Settings::values.enable_raw_input = false;
#endif
ReadBasicSetting(Settings::values.emulate_analog_keyboard);
Settings::values.mouse_panning = false;
ReadBasicSetting(Settings::values.mouse_panning_sensitivity);
ReadBasicSetting(Settings::values.tas_enable);
ReadBasicSetting(Settings::values.tas_loop);
ReadBasicSetting(Settings::values.tas_swap_controllers);
ReadBasicSetting(Settings::values.pause_tas_on_load);
ReadGlobalSetting(Settings::values.use_docked_mode);
// Disable docked mode if handheld is selected
@@ -661,6 +673,13 @@ void Config::ReadDataStorageValues() {
QString::fromStdString(FS::GetYuzuPathString(FS::YuzuPath::DumpDir)))
.toString()
.toStdString());
FS::SetYuzuPath(FS::YuzuPath::TASDir,
qt_config
->value(QStringLiteral("tas_directory"),
QString::fromStdString(FS::GetYuzuPathString(FS::YuzuPath::TASDir)))
.toString()
.toStdString());
ReadBasicSetting(Settings::values.gamecard_inserted);
ReadBasicSetting(Settings::values.gamecard_current_game);
ReadBasicSetting(Settings::values.gamecard_path);
@@ -1163,7 +1182,6 @@ void Config::SaveAudioValues() {
WriteBasicSetting(Settings::values.sink_id);
WriteBasicSetting(Settings::values.audio_device_id);
}
WriteGlobalSetting(Settings::values.enable_audio_stretching);
WriteGlobalSetting(Settings::values.volume);
qt_config->endGroup();
@@ -1184,10 +1202,16 @@ void Config::SaveControlValues() {
WriteGlobalSetting(Settings::values.vibration_enabled);
WriteGlobalSetting(Settings::values.enable_accurate_vibrations);
WriteGlobalSetting(Settings::values.motion_enabled);
WriteBasicSetting(Settings::values.enable_raw_input);
WriteBasicSetting(Settings::values.keyboard_enabled);
WriteBasicSetting(Settings::values.emulate_analog_keyboard);
WriteBasicSetting(Settings::values.mouse_panning_sensitivity);
WriteBasicSetting(Settings::values.tas_enable);
WriteBasicSetting(Settings::values.tas_loop);
WriteBasicSetting(Settings::values.tas_swap_controllers);
WriteBasicSetting(Settings::values.pause_tas_on_load);
qt_config->endGroup();
}
@@ -1215,6 +1239,10 @@ void Config::SaveDataStorageValues() {
WriteSetting(QStringLiteral("dump_directory"),
QString::fromStdString(FS::GetYuzuPathString(FS::YuzuPath::DumpDir)),
QString::fromStdString(FS::GetYuzuPathString(FS::YuzuPath::DumpDir)));
WriteSetting(QStringLiteral("tas_directory"),
QString::fromStdString(FS::GetYuzuPathString(FS::YuzuPath::TASDir)),
QString::fromStdString(FS::GetYuzuPathString(FS::YuzuPath::TASDir)));
WriteBasicSetting(Settings::values.gamecard_inserted);
WriteBasicSetting(Settings::values.gamecard_current_game);
WriteBasicSetting(Settings::values.gamecard_path);

View File

@@ -42,7 +42,7 @@ public:
default_mouse_buttons;
static const std::array<int, Settings::NativeKeyboard::NumKeyboardKeys> default_keyboard_keys;
static const std::array<int, Settings::NativeKeyboard::NumKeyboardMods> default_keyboard_mods;
static const std::array<UISettings::Shortcut, 18> default_hotkeys;
static const std::array<UISettings::Shortcut, 21> default_hotkeys;
private:
void Initialize(const std::string& config_name);

View File

@@ -50,8 +50,6 @@ void ConfigureAudio::SetConfiguration() {
const auto volume_value = static_cast<int>(Settings::values.volume.GetValue());
ui->volume_slider->setValue(volume_value);
ui->toggle_audio_stretching->setChecked(Settings::values.enable_audio_stretching.GetValue());
if (!Settings::IsConfiguringGlobal()) {
if (Settings::values.volume.UsingGlobal()) {
ui->volume_combo_box->setCurrentIndex(0);
@@ -100,8 +98,6 @@ void ConfigureAudio::SetVolumeIndicatorText(int percentage) {
}
void ConfigureAudio::ApplyConfiguration() {
ConfigurationShared::ApplyPerGameSetting(&Settings::values.enable_audio_stretching,
ui->toggle_audio_stretching, enable_audio_stretching);
if (Settings::IsConfiguringGlobal()) {
Settings::values.sink_id =
@@ -162,15 +158,10 @@ void ConfigureAudio::RetranslateUI() {
void ConfigureAudio::SetupPerGameUI() {
if (Settings::IsConfiguringGlobal()) {
ui->volume_slider->setEnabled(Settings::values.volume.UsingGlobal());
ui->toggle_audio_stretching->setEnabled(
Settings::values.enable_audio_stretching.UsingGlobal());
return;
}
ConfigurationShared::SetColoredTristate(ui->toggle_audio_stretching,
Settings::values.enable_audio_stretching,
enable_audio_stretching);
connect(ui->volume_combo_box, qOverload<int>(&QComboBox::activated), this, [this](int index) {
ui->volume_slider->setEnabled(index == 1);
ConfigurationShared::SetHighlight(ui->volume_layout, index == 1);

View File

@@ -41,6 +41,4 @@ private:
void SetupPerGameUI();
std::unique_ptr<Ui::ConfigureAudio> ui;
ConfigurationShared::CheckState enable_audio_stretching;
};

View File

@@ -31,16 +31,6 @@
</item>
</layout>
</item>
<item>
<widget class="QCheckBox" name="toggle_audio_stretching">
<property name="toolTip">
<string>This post-processing effect adjusts audio speed to match emulation speed and helps prevent audio stutter. This however increases audio latency.</string>
</property>
<property name="text">
<string>Enable audio stretching</string>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="_2">
<item>

View File

@@ -2,85 +2,55 @@
<ui version="4.0">
<class>ConfigureDebug</class>
<widget class="QWidget" name="ConfigureDebug">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>777</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_1">
<item>
<widget class="QGroupBox" name="groupBox_2">
<property name="title">
<string>Logging</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_4">
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QLabel" name="label_1">
<property name="text">
<string>Global Log Filter</string>
<layout class="QGridLayout" name="gridLayout_1">
<item row="0" column="0" colspan="2">
<layout class="QHBoxLayout" name="horizontalLayout_1">
<item>
<widget class="QLabel" name="label_1">
<property name="text">
<string>Global Log Filter</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="log_filter_edit"/>
</item>
</layout>
</item>
<item row="1" column="0">
<widget class="QCheckBox" name="toggle_console">
<property name="text">
<string>Show Log in Console</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QPushButton" name="open_log_button">
<property name="text">
<string>Open Log Location</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QCheckBox" name="extended_logging">
<property name="enabled">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="log_filter_edit"/>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<widget class="QCheckBox" name="toggle_console">
<property name="text">
<string>Show Log in Console</string>
<property name="toolTip">
<string>When checked, the max size of the log increases from 100 MB to 1 GB</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="open_log_button">
<property name="text">
<string>Open Log Location</string>
<string>Enable Extended Logging**</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QCheckBox" name="extended_logging">
<property name="enabled">
<bool>true</bool>
</property>
<property name="toolTip">
<string>When checked, the max size of the log increases from 100 MB to 1 GB</string>
</property>
<property name="text">
<string>Enable Extended Logging</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_2">
<property name="font">
<font>
<italic>true</italic>
</font>
</property>
<property name="text">
<string>This will be reset automatically when yuzu closes.</string>
</property>
<property name="indent">
<number>20</number>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
</item>
<item>
@@ -111,7 +81,7 @@
<property name="title">
<string>Graphics</string>
</property>
<layout class="QGridLayout" name="gridLayout_3">
<layout class="QGridLayout" name="gridLayout_2">
<item row="0" column="0">
<widget class="QCheckBox" name="enable_graphics_debugging">
<property name="enabled">
@@ -176,33 +146,18 @@
<property name="title">
<string>Debugging</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_7">
<item>
<layout class="QGridLayout" name="gridLayout_3">
<item row="0" column="0">
<widget class="QCheckBox" name="fs_access_log">
<property name="text">
<string>Enable FS Access Log</string>
</property>
</widget>
</item>
<item>
<item row="1" column="0">
<widget class="QCheckBox" name="reporting_services">
<property name="text">
<string>Enable Verbose Reporting Services</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_4">
<property name="font">
<font>
<italic>true</italic>
</font>
</property>
<property name="text">
<string>This will be reset automatically when yuzu closes.</string>
</property>
<property name="indent">
<number>20</number>
<string>Enable Verbose Reporting Services**</string>
</property>
</widget>
</item>
@@ -214,47 +169,32 @@
<property name="title">
<string>Advanced</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_8">
<item>
<layout class="QGridLayout" name="gridLayout_4">
<item> row="0" column="0">
<widget class="QCheckBox" name="quest_flag">
<property name="text">
<string>Kiosk (Quest) Mode</string>
</property>
</widget>
</item>
<item>
<item row="1" column="0">
<widget class="QCheckBox" name="enable_cpu_debugging">
<property name="text">
<string>Enable CPU Debugging</string>
</property>
</widget>
</item>
<item>
<item row="2" column="0">
<widget class="QCheckBox" name="use_debug_asserts">
<property name="text">
<string>Enable Debug Asserts</string>
</property>
</widget>
</item>
<item>
<item row="0" column="1">
<widget class="QCheckBox" name="use_auto_stub">
<property name="text">
<string>Enable Auto-Stub</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_5">
<property name="font">
<font>
<italic>true</italic>
</font>
</property>
<property name="text">
<string>This will be reset automatically when yuzu closes.</string>
</property>
<property name="indent">
<number>20</number>
<string>Enable Auto-Stub**</string>
</property>
</widget>
</item>
@@ -262,20 +202,19 @@
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
<widget class="QLabel" name="label_5">
<property name="font">
<font>
<italic>true</italic>
</font>
</property>
<property name="sizeType">
<enum>QSizePolicy::Expanding</enum>
<property name="text">
<string>**This will be reset automatically when yuzu closes.</string>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
<property name="indent">
<number>20</number>
</property>
</spacer>
</widget>
</item>
</layout>
</widget>

View File

@@ -88,6 +88,10 @@ ConfigureInputAdvanced::ConfigureInputAdvanced(QWidget* parent)
connect(ui->buttonMotionTouch, &QPushButton::clicked, this,
&ConfigureInputAdvanced::CallMotionTouchConfigDialog);
#ifndef _WIN32
ui->enable_raw_input->setVisible(false);
#endif
LoadConfiguration();
}
@@ -126,6 +130,7 @@ void ConfigureInputAdvanced::ApplyConfiguration() {
Settings::values.mouse_panning_sensitivity =
static_cast<float>(ui->mouse_panning_sensitivity->value());
Settings::values.touchscreen.enabled = ui->touchscreen_enabled->isChecked();
Settings::values.enable_raw_input = ui->enable_raw_input->isChecked();
}
void ConfigureInputAdvanced::LoadConfiguration() {
@@ -155,6 +160,7 @@ void ConfigureInputAdvanced::LoadConfiguration() {
ui->mouse_panning->setChecked(Settings::values.mouse_panning.GetValue());
ui->mouse_panning_sensitivity->setValue(Settings::values.mouse_panning_sensitivity.GetValue());
ui->touchscreen_enabled->setChecked(Settings::values.touchscreen.enabled);
ui->enable_raw_input->setChecked(Settings::values.enable_raw_input.GetValue());
UpdateUIEnabled();
}

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