From 5d8f3f7cb158a64f922f869c32748119df8f690b Mon Sep 17 00:00:00 2001 From: ddutchie Date: Fri, 15 Mar 2024 22:26:16 +0000 Subject: [PATCH] Revert "Quality-of-Life Improvements" --- externals/xbyak | 2 +- src/audio_core/in/audio_in.cpp | 4 +- src/audio_core/in/audio_in_system.cpp | 4 +- src/audio_core/out/audio_out.cpp | 4 +- src/audio_core/out/audio_out_system.cpp | 4 +- .../renderer/behavior/info_updater.cpp | 7 +- .../renderer/memory/pool_mapper.cpp | 4 +- src/common/CMakeLists.txt | 2 + src/common/address_space.h | 3 +- src/common/concepts.h | 6 +- src/common/expected.h | 8 +- src/common/intrusive_red_black_tree.h | 6 +- src/common/multi_level_page_table.h | 8 +- src/common/polyfill_ranges.h | 6 +- src/common/settings.h | 5 +- src/common/telemetry.cpp | 119 +++++++ src/common/telemetry.h | 209 +++++++++++++ src/common/tree.h | 6 +- src/common/vector_math.h | 12 +- src/common/virtual_buffer.h | 4 +- src/common/x64/native_clock.cpp | 4 +- src/core/CMakeLists.txt | 2 + src/core/arm/dynarmic/arm_dynarmic_32.cpp | 4 +- src/core/arm/dynarmic/arm_dynarmic_64.cpp | 4 +- src/core/core.cpp | 32 ++ src/core/core.h | 7 + src/core/file_sys/card_image.cpp | 4 +- src/core/file_sys/fs_path_utility.h | 8 +- src/core/file_sys/submission_package.cpp | 6 +- src/core/file_sys/xts_archive.cpp | 8 +- src/core/hle/kernel/k_auto_object.h | 16 +- src/core/hle/kernel/k_memory_layout.cpp | 4 +- src/core/hle/kernel/k_priority_queue.h | 48 +-- src/core/hle/kernel/k_scoped_lock.h | 9 +- src/core/hle/result.h | 8 +- src/core/hle/service/acc/acc.cpp | 4 +- .../service/am/frontend/applet_cabinet.cpp | 5 +- .../service/am/service/application_proxy.cpp | 4 +- .../am/service/library_applet_creator.cpp | 4 +- .../am/service/library_applet_proxy.cpp | 4 +- .../service/am/service/self_controller.cpp | 4 +- .../am/service/system_applet_proxy.cpp | 4 +- .../service/am/service/window_controller.cpp | 4 +- .../service/aoc/purchase_event_manager.cpp | 4 +- src/core/hle/service/audio/audio_in.cpp | 5 +- .../hle/service/audio/audio_in_manager.cpp | 4 +- src/core/hle/service/audio/audio_renderer.cpp | 4 +- .../bcat/news/newly_arrived_event_holder.cpp | 5 +- .../bcat/news/overwrite_event_holder.cpp | 4 +- .../filesystem/fsp/fs_i_filesystem.cpp | 4 +- .../fsp/fs_i_save_data_info_reader.cpp | 4 +- .../hle/service/glue/time/alarm_worker.cpp | 4 +- src/core/hle/service/glue/time/manager.cpp | 6 +- src/core/hle/service/glue/time/time_zone.cpp | 6 +- src/core/hle/service/glue/time/worker.cpp | 8 +- src/core/hle/service/hid/hid_debug_server.cpp | 4 +- src/core/hle/service/ipc_helpers.h | 4 +- src/core/hle/service/jit/jit_context.cpp | 4 +- src/core/hle/service/mii/mii.cpp | 4 +- src/core/hle/service/psc/time/alarms.cpp | 4 +- .../standard_user_system_clock_core.cpp | 4 +- src/core/hle/service/psc/time/common.cpp | 4 +- src/core/hle/service/psc/time/manager.h | 4 +- .../service/psc/time/power_state_service.cpp | 4 +- src/core/hle/service/psc/time/static.cpp | 4 +- .../hle/service/psc/time/steady_clock.cpp | 4 +- .../hle/service/psc/time/system_clock.cpp | 4 +- .../service/psc/time/time_zone_service.cpp | 4 +- src/core/hle/service/sm/sm.cpp | 4 +- .../vi/application_display_service.cpp | 4 +- src/core/memory/cheat_engine.cpp | 4 +- src/core/telemetry_session.cpp | 294 ++++++++++++++++++ src/core/telemetry_session.h | 101 ++++++ src/input_common/input_poller.cpp | 4 +- .../frontend/maxwell/control_flow.cpp | 4 +- .../maxwell/structured_control_flow.cpp | 4 +- src/suyu/applets/qt_web_browser.cpp | 4 +- src/suyu/bootmanager.cpp | 4 +- src/suyu/compatdb.cpp | 11 +- src/suyu/compatdb.h | 5 +- src/suyu/configuration/configure_dialog.cpp | 6 +- .../configuration/configure_input_player.cpp | 10 +- .../configuration/configure_mouse_panning.ui | 4 +- src/suyu/configuration/configure_per_game.cpp | 26 +- src/suyu/configuration/configure_per_game.h | 4 - src/suyu/configuration/configure_per_game.ui | 143 ++++----- src/suyu/configuration/configure_web.cpp | 26 +- src/suyu/configuration/configure_web.h | 1 + src/suyu/configuration/configure_web.ui | 50 +++ src/suyu/configuration/shared_widget.cpp | 4 +- src/suyu/game_list.cpp | 68 +--- src/suyu/game_list.h | 2 - src/suyu/game_list_worker.cpp | 44 +-- src/suyu/main.cpp | 41 ++- src/suyu/main.h | 2 +- src/suyu/main.ui | 14 +- src/suyu/multiplayer/direct_connect.cpp | 4 +- src/suyu/multiplayer/host_room.cpp | 5 +- src/suyu/multiplayer/lobby.cpp | 5 +- src/suyu/uisettings.h | 1 - src/suyu_cmd/suyu.cpp | 4 + src/video_core/cdma_pusher.cpp | 4 +- src/video_core/dma_pusher.cpp | 4 +- src/video_core/engines/maxwell_3d.cpp | 5 +- src/video_core/engines/puller.cpp | 4 +- src/video_core/host1x/codecs/decoder.cpp | 4 +- src/video_core/host1x/host1x.cpp | 4 +- src/video_core/host1x/vic.cpp | 4 +- src/video_core/memory_manager.cpp | 4 +- src/video_core/query_cache/query_cache.h | 4 +- .../renderer_opengl/gl_compute_pipeline.cpp | 4 +- .../renderer_opengl/gl_query_cache.cpp | 5 +- .../renderer_opengl/renderer_opengl.cpp | 33 +- .../renderer_opengl/renderer_opengl.h | 6 +- .../renderer_vulkan/present/fsr.cpp | 4 +- .../renderer_vulkan/renderer_vulkan.cpp | 72 ++++- .../renderer_vulkan/renderer_vulkan.h | 6 +- .../renderer_vulkan/vk_compute_pass.cpp | 4 +- .../renderer_vulkan/vk_compute_pipeline.cpp | 4 +- .../renderer_vulkan/vk_present_manager.cpp | 4 +- .../renderer_vulkan/vk_query_cache.cpp | 7 +- .../vk_staging_buffer_pool.cpp | 4 +- src/video_core/shader_environment.cpp | 4 +- src/video_core/video_core.cpp | 10 +- src/video_core/vulkan_common/vulkan_wrapper.h | 12 +- src/web_service/CMakeLists.txt | 2 + src/web_service/telemetry_json.cpp | 130 ++++++++ src/web_service/telemetry_json.h | 44 +++ 128 files changed, 1530 insertions(+), 499 deletions(-) create mode 100644 src/common/telemetry.cpp create mode 100644 src/common/telemetry.h create mode 100644 src/core/telemetry_session.cpp create mode 100644 src/core/telemetry_session.h create mode 100644 src/web_service/telemetry_json.cpp create mode 100644 src/web_service/telemetry_json.h diff --git a/externals/xbyak b/externals/xbyak index 9c0f5d3ecb..a1ac3750f9 160000 --- a/externals/xbyak +++ b/externals/xbyak @@ -1 +1 @@ -Subproject commit 9c0f5d3ecb06d2c93c2b59becb9b3b763213e74e +Subproject commit a1ac3750f9a639b5a6c6d6c7da4259b8d6790989 diff --git a/src/audio_core/in/audio_in.cpp b/src/audio_core/in/audio_in.cpp index b28eb69786..df8c44d1f2 100644 --- a/src/audio_core/in/audio_in.cpp +++ b/src/audio_core/in/audio_in.cpp @@ -8,8 +8,8 @@ namespace AudioCore::AudioIn { In::In(Core::System& system_, Manager& manager_, Kernel::KEvent* event_, size_t session_id_) - : manager{manager_}, parent_mutex{manager.mutex}, event{event_}, - system{system_, event, session_id_} {} + : manager{manager_}, parent_mutex{manager.mutex}, event{event_}, system{system_, event, + session_id_} {} void In::Free() { std::scoped_lock l{parent_mutex}; diff --git a/src/audio_core/in/audio_in_system.cpp b/src/audio_core/in/audio_in_system.cpp index 84d99877cb..b2dd3ef9f7 100644 --- a/src/audio_core/in/audio_in_system.cpp +++ b/src/audio_core/in/audio_in_system.cpp @@ -14,8 +14,8 @@ namespace AudioCore::AudioIn { System::System(Core::System& system_, Kernel::KEvent* event_, const size_t session_id_) - : system{system_}, buffer_event{event_}, session_id{session_id_}, - session{std::make_unique(system_)} {} + : system{system_}, buffer_event{event_}, + session_id{session_id_}, session{std::make_unique(system_)} {} System::~System() { Finalize(); diff --git a/src/audio_core/out/audio_out.cpp b/src/audio_core/out/audio_out.cpp index d31bced9df..b7ea134055 100644 --- a/src/audio_core/out/audio_out.cpp +++ b/src/audio_core/out/audio_out.cpp @@ -8,8 +8,8 @@ namespace AudioCore::AudioOut { Out::Out(Core::System& system_, Manager& manager_, Kernel::KEvent* event_, size_t session_id_) - : manager{manager_}, parent_mutex{manager.mutex}, event{event_}, - system{system_, event, session_id_} {} + : manager{manager_}, parent_mutex{manager.mutex}, event{event_}, system{system_, event, + session_id_} {} void Out::Free() { std::scoped_lock l{parent_mutex}; diff --git a/src/audio_core/out/audio_out_system.cpp b/src/audio_core/out/audio_out_system.cpp index 4c940be2d3..7b3ff4e881 100644 --- a/src/audio_core/out/audio_out_system.cpp +++ b/src/audio_core/out/audio_out_system.cpp @@ -14,8 +14,8 @@ namespace AudioCore::AudioOut { System::System(Core::System& system_, Kernel::KEvent* event_, size_t session_id_) - : system{system_}, buffer_event{event_}, session_id{session_id_}, - session{std::make_unique(system_)} {} + : system{system_}, buffer_event{event_}, + session_id{session_id_}, session{std::make_unique(system_)} {} System::~System() { Finalize(); diff --git a/src/audio_core/renderer/behavior/info_updater.cpp b/src/audio_core/renderer/behavior/info_updater.cpp index 163127789c..3dae6069f7 100644 --- a/src/audio_core/renderer/behavior/info_updater.cpp +++ b/src/audio_core/renderer/behavior/info_updater.cpp @@ -19,9 +19,10 @@ namespace AudioCore::Renderer { InfoUpdater::InfoUpdater(std::span input_, std::span output_, Kernel::KProcess* process_handle_, BehaviorInfo& behaviour_) - : input{input_.data() + sizeof(UpdateDataHeader)}, input_origin{input_}, - output{output_.data() + sizeof(UpdateDataHeader)}, output_origin{output_}, - in_header{reinterpret_cast(input_origin.data())}, + : input{input_.data() + sizeof(UpdateDataHeader)}, + input_origin{input_}, output{output_.data() + sizeof(UpdateDataHeader)}, + output_origin{output_}, in_header{reinterpret_cast( + input_origin.data())}, out_header{reinterpret_cast(output_origin.data())}, expected_input_size{input_.size()}, expected_output_size{output_.size()}, process_handle{process_handle_}, behaviour{behaviour_} { diff --git a/src/audio_core/renderer/memory/pool_mapper.cpp b/src/audio_core/renderer/memory/pool_mapper.cpp index 1df786feb5..e47eb66d51 100644 --- a/src/audio_core/renderer/memory/pool_mapper.cpp +++ b/src/audio_core/renderer/memory/pool_mapper.cpp @@ -13,8 +13,8 @@ PoolMapper::PoolMapper(Kernel::KProcess* process_handle_, bool force_map_) PoolMapper::PoolMapper(Kernel::KProcess* process_handle_, std::span pool_infos_, u32 pool_count_, bool force_map_) - : process_handle{process_handle_}, pool_infos{pool_infos_.data()}, pool_count{pool_count_}, - force_map{force_map_} {} + : process_handle{process_handle_}, pool_infos{pool_infos_.data()}, + pool_count{pool_count_}, force_map{force_map_} {} void PoolMapper::ClearUseState(std::span pools, const u32 count) { for (u32 i = 0; i < count; i++) { diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index f8d431a770..3baae5a30d 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt @@ -136,6 +136,8 @@ add_library(common STATIC string_util.cpp string_util.h swap.h + telemetry.cpp + telemetry.h thread.cpp thread.h thread_queue_list.h diff --git a/src/common/address_space.h b/src/common/address_space.h index 694bb2aed3..8683c23c39 100644 --- a/src/common/address_space.h +++ b/src/common/address_space.h @@ -12,7 +12,8 @@ namespace Common { template -concept AddressSpaceValid = std::is_unsigned_v && sizeof(VaType) * 8 >= AddressSpaceBits; +concept AddressSpaceValid = std::is_unsigned_v && sizeof(VaType) * 8 >= +AddressSpaceBits; struct EmptyStruct {}; diff --git a/src/common/concepts.h b/src/common/concepts.h index a9acff3e79..61df1d32a2 100644 --- a/src/common/concepts.h +++ b/src/common/concepts.h @@ -16,9 +16,9 @@ concept IsContiguousContainer = std::contiguous_iterator; // is available on all supported platforms. template concept DerivedFrom = requires { - std::is_base_of_v; - std::is_convertible_v; -}; + std::is_base_of_v; + std::is_convertible_v; + }; // TODO: Replace with std::convertible_to when libc++ implements it. template diff --git a/src/common/expected.h b/src/common/expected.h index c62e43164d..5fccfbcbdd 100644 --- a/src/common/expected.h +++ b/src/common/expected.h @@ -598,14 +598,14 @@ public: template >* = nullptr, std::enable_if_t>* = nullptr> constexpr explicit Expected(Unexpected&& e) noexcept(std::is_nothrow_constructible_v) - : impl_base{unexpect_t{}, std::move(e.value())}, - ctor_base{detail::default_constructor_tag{}} {} + : impl_base{unexpect_t{}, std::move(e.value())}, ctor_base{ + detail::default_constructor_tag{}} {} template >* = nullptr, std::enable_if_t>* = nullptr> constexpr Expected(Unexpected&& e) noexcept(std::is_nothrow_constructible_v) - : impl_base{unexpect_t{}, std::move(e.value())}, - ctor_base{detail::default_constructor_tag{}} {} + : impl_base{unexpect_t{}, std::move(e.value())}, ctor_base{ + detail::default_constructor_tag{}} {} template >* = nullptr> constexpr explicit Expected(unexpect_t, Args&&... args) diff --git a/src/common/intrusive_red_black_tree.h b/src/common/intrusive_red_black_tree.h index 554194bb0b..6809acb47d 100644 --- a/src/common/intrusive_red_black_tree.h +++ b/src/common/intrusive_red_black_tree.h @@ -238,8 +238,10 @@ public: template concept HasRedBlackKeyType = requires { - { std::is_same::value } -> std::convertible_to; -}; + { + std::is_same::value + } -> std::convertible_to; + }; namespace impl { diff --git a/src/common/multi_level_page_table.h b/src/common/multi_level_page_table.h index 4eefb4d5f8..31f6676a06 100644 --- a/src/common/multi_level_page_table.h +++ b/src/common/multi_level_page_table.h @@ -25,12 +25,12 @@ public: MultiLevelPageTable(MultiLevelPageTable&& other) noexcept : address_space_bits{std::exchange(other.address_space_bits, 0)}, - first_level_bits{std::exchange(other.first_level_bits, 0)}, - page_bits{std::exchange(other.page_bits, 0)}, + first_level_bits{std::exchange(other.first_level_bits, 0)}, page_bits{std::exchange( + other.page_bits, 0)}, first_level_shift{std::exchange(other.first_level_shift, 0)}, first_level_chunk_size{std::exchange(other.first_level_chunk_size, 0)}, - first_level_map{std::move(other.first_level_map)}, - base_ptr{std::exchange(other.base_ptr, nullptr)} {} + first_level_map{std::move(other.first_level_map)}, base_ptr{std::exchange(other.base_ptr, + nullptr)} {} MultiLevelPageTable& operator=(MultiLevelPageTable&& other) noexcept { address_space_bits = std::exchange(other.address_space_bits, 0); diff --git a/src/common/polyfill_ranges.h b/src/common/polyfill_ranges.h index ea542fe120..512dbcbcb7 100644 --- a/src/common/polyfill_ranges.h +++ b/src/common/polyfill_ranges.h @@ -18,9 +18,9 @@ namespace ranges { template concept range = requires(T& t) { - begin(t); - end(t); -}; + begin(t); + end(t); + }; template concept input_range = range; diff --git a/src/common/settings.h b/src/common/settings.h index d010713ea6..8b86ee991f 100644 --- a/src/common/settings.h +++ b/src/common/settings.h @@ -530,9 +530,9 @@ struct Values { Setting mouse_enabled{linkage, false, "mouse_enabled", Category::Controls}; Setting mouse_panning_x_sensitivity{ - linkage, 50, 1, 200, "mouse_panning_x_sensitivity", Category::Controls}; + linkage, 50, 1, 100, "mouse_panning_x_sensitivity", Category::Controls}; Setting mouse_panning_y_sensitivity{ - linkage, 50, 1, 200, "mouse_panning_y_sensitivity", Category::Controls}; + linkage, 50, 1, 100, "mouse_panning_y_sensitivity", Category::Controls}; Setting mouse_panning_deadzone_counterweight{ linkage, 20, 0, 100, "mouse_panning_deadzone_counterweight", Category::Controls}; Setting mouse_panning_decay_strength{ @@ -611,6 +611,7 @@ struct Values { Category::Network}; // WebService + Setting enable_telemetry{linkage, false, "enable_telemetry", Category::WebService}; Setting web_api_url{linkage, "http://74.113.97.71:3000", "web_api_url", Category::WebService}; Setting suyu_username{linkage, std::string(), "suyu_username", diff --git a/src/common/telemetry.cpp b/src/common/telemetry.cpp new file mode 100644 index 0000000000..929ed67e4a --- /dev/null +++ b/src/common/telemetry.cpp @@ -0,0 +1,119 @@ +// SPDX-FileCopyrightText: 2017 Citra Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include +#include +#include "common/scm_rev.h" +#include "common/telemetry.h" + +#ifdef ARCHITECTURE_x86_64 +#include "common/x64/cpu_detect.h" +#endif + +namespace Common::Telemetry { + +void FieldCollection::Accept(VisitorInterface& visitor) const { + for (const auto& field : fields) { + field.second->Accept(visitor); + } +} + +void FieldCollection::AddField(std::unique_ptr field) { + fields[field->GetName()] = std::move(field); +} + +template +void Field::Accept(VisitorInterface& visitor) const { + visitor.Visit(*this); +} + +template class Field; +template class Field; +template class Field; +template class Field; +template class Field; +template class Field; +template class Field; +template class Field; +template class Field; +template class Field; +template class Field; +template class Field; +template class Field; +template class Field; + +void AppendBuildInfo(FieldCollection& fc) { + const bool is_git_dirty{std::strstr(Common::g_scm_desc, "dirty") != nullptr}; + fc.AddField(FieldType::App, "Git_IsDirty", is_git_dirty); + fc.AddField(FieldType::App, "Git_Branch", Common::g_scm_branch); + fc.AddField(FieldType::App, "Git_Revision", Common::g_scm_rev); + fc.AddField(FieldType::App, "BuildDate", Common::g_build_date); + fc.AddField(FieldType::App, "BuildName", Common::g_build_name); +} + +void AppendCPUInfo(FieldCollection& fc) { +#ifdef ARCHITECTURE_x86_64 + + const auto& caps = Common::GetCPUCaps(); + const auto add_field = [&fc](std::string_view field_name, const auto& field_value) { + fc.AddField(FieldType::UserSystem, field_name, field_value); + }; + add_field("CPU_Model", caps.cpu_string); + add_field("CPU_BrandString", caps.brand_string); + + add_field("CPU_Extension_x64_SSE", caps.sse); + add_field("CPU_Extension_x64_SSE2", caps.sse2); + add_field("CPU_Extension_x64_SSE3", caps.sse3); + add_field("CPU_Extension_x64_SSSE3", caps.ssse3); + add_field("CPU_Extension_x64_SSE41", caps.sse4_1); + add_field("CPU_Extension_x64_SSE42", caps.sse4_2); + + add_field("CPU_Extension_x64_AVX", caps.avx); + add_field("CPU_Extension_x64_AVX_VNNI", caps.avx_vnni); + add_field("CPU_Extension_x64_AVX2", caps.avx2); + + // Skylake-X/SP level AVX512, for compatibility with the previous telemetry field + add_field("CPU_Extension_x64_AVX512", + caps.avx512f && caps.avx512cd && caps.avx512vl && caps.avx512dq && caps.avx512bw); + + add_field("CPU_Extension_x64_AVX512F", caps.avx512f); + add_field("CPU_Extension_x64_AVX512CD", caps.avx512cd); + add_field("CPU_Extension_x64_AVX512VL", caps.avx512vl); + add_field("CPU_Extension_x64_AVX512DQ", caps.avx512dq); + add_field("CPU_Extension_x64_AVX512BW", caps.avx512bw); + add_field("CPU_Extension_x64_AVX512BITALG", caps.avx512bitalg); + add_field("CPU_Extension_x64_AVX512VBMI", caps.avx512vbmi); + + add_field("CPU_Extension_x64_AES", caps.aes); + add_field("CPU_Extension_x64_BMI1", caps.bmi1); + add_field("CPU_Extension_x64_BMI2", caps.bmi2); + add_field("CPU_Extension_x64_F16C", caps.f16c); + add_field("CPU_Extension_x64_FMA", caps.fma); + add_field("CPU_Extension_x64_FMA4", caps.fma4); + add_field("CPU_Extension_x64_GFNI", caps.gfni); + add_field("CPU_Extension_x64_INVARIANT_TSC", caps.invariant_tsc); + add_field("CPU_Extension_x64_LZCNT", caps.lzcnt); + add_field("CPU_Extension_x64_MONITORX", caps.monitorx); + add_field("CPU_Extension_x64_MOVBE", caps.movbe); + add_field("CPU_Extension_x64_PCLMULQDQ", caps.pclmulqdq); + add_field("CPU_Extension_x64_POPCNT", caps.popcnt); + add_field("CPU_Extension_x64_SHA", caps.sha); + add_field("CPU_Extension_x64_WAITPKG", caps.waitpkg); +#else + fc.AddField(FieldType::UserSystem, "CPU_Model", "Other"); +#endif +} + +void AppendOSInfo(FieldCollection& fc) { +#ifdef __APPLE__ + fc.AddField(FieldType::UserSystem, "OsPlatform", "Apple"); +#elif defined(_WIN32) + fc.AddField(FieldType::UserSystem, "OsPlatform", "Windows"); +#elif defined(__linux__) || defined(linux) || defined(__linux) + fc.AddField(FieldType::UserSystem, "OsPlatform", "Linux"); +#else + fc.AddField(FieldType::UserSystem, "OsPlatform", "Unknown"); +#endif +} + +} // namespace Common::Telemetry diff --git a/src/common/telemetry.h b/src/common/telemetry.h new file mode 100644 index 0000000000..124f0dbf09 --- /dev/null +++ b/src/common/telemetry.h @@ -0,0 +1,209 @@ +// SPDX-FileCopyrightText: 2017 Citra Emulator Project & 2024 suyu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include +#include +#include +#include +#include "common/common_funcs.h" +#include "common/common_types.h" + +namespace Common::Telemetry { + +/// Field type, used for grouping fields together in the final submitted telemetry log +enum class FieldType : u8 { + None = 0, ///< No specified field group + App, ///< suyu application fields (e.g. version, branch, etc.) + Session, ///< Emulated session fields (e.g. title ID, log, etc.) + Performance, ///< Emulated performance (e.g. fps, emulated CPU speed, etc.) + UserFeedback, ///< User submitted feedback (e.g. star rating, user notes, etc.) + UserConfig, ///< User configuration fields (e.g. emulated CPU core, renderer, etc.) + UserSystem, ///< User system information (e.g. host CPU type, RAM, etc.) +}; + +struct VisitorInterface; + +/** + * Interface class for telemetry data fields. + */ +class FieldInterface { +public: + virtual ~FieldInterface() = default; + + /** + * Accept method for the visitor pattern. + * @param visitor Reference to the visitor that will visit this field. + */ + virtual void Accept(VisitorInterface& visitor) const = 0; + + /** + * Gets the name of this field. + * @returns Name of this field as a string. + */ + virtual const std::string& GetName() const = 0; +}; + +/** + * Represents a telemetry data field, i.e. a unit of data that gets logged and submitted to our + * telemetry web service. + */ +template +class Field : public FieldInterface { +public: + SUYU_NON_COPYABLE(Field); + + Field(FieldType type_, std::string_view name_, T value_) + : name(name_), type(type_), value(std::move(value_)) {} + + ~Field() override = default; + + Field(Field&&) noexcept = default; + Field& operator=(Field&& other) noexcept = default; + + void Accept(VisitorInterface& visitor) const override; + + [[nodiscard]] const std::string& GetName() const override { + return name; + } + + /** + * Returns the type of the field. + */ + [[nodiscard]] FieldType GetType() const { + return type; + } + + /** + * Returns the value of the field. + */ + [[nodiscard]] const T& GetValue() const { + return value; + } + + [[nodiscard]] bool operator==(const Field& other) const { + return (type == other.type) && (name == other.name) && (value == other.value); + } + + [[nodiscard]] bool operator!=(const Field& other) const { + return !operator==(other); + } + +private: + std::string name; ///< Field name, must be unique + FieldType type{}; ///< Field type, used for grouping fields together + T value; ///< Field value +}; + +/** + * Collection of data fields that have been logged. + */ +class FieldCollection final { +public: + SUYU_NON_COPYABLE(FieldCollection); + + FieldCollection() = default; + ~FieldCollection() = default; + + FieldCollection(FieldCollection&&) noexcept = default; + FieldCollection& operator=(FieldCollection&&) noexcept = default; + + /** + * Accept method for the visitor pattern, visits each field in the collection. + * @param visitor Reference to the visitor that will visit each field. + */ + void Accept(VisitorInterface& visitor) const; + + /** + * Creates a new field and adds it to the field collection. + * @param type Type of the field to add. + * @param name Name of the field to add. + * @param value Value for the field to add. + */ + template + void AddField(FieldType type, std::string_view name, T value) { + return AddField(std::make_unique>(type, name, std::move(value))); + } + + /** + * Adds a new field to the field collection. + * @param field Field to add to the field collection. + */ + void AddField(std::unique_ptr field); + +private: + std::map> fields; +}; + +/** + * Telemetry fields visitor interface class. A backend to log to a web service should implement + * this interface. + */ +struct VisitorInterface { + virtual ~VisitorInterface() = default; + + virtual void Visit(const Field& field) = 0; + virtual void Visit(const Field& field) = 0; + virtual void Visit(const Field& field) = 0; + virtual void Visit(const Field& field) = 0; + virtual void Visit(const Field& field) = 0; + virtual void Visit(const Field& field) = 0; + virtual void Visit(const Field& field) = 0; + virtual void Visit(const Field& field) = 0; + virtual void Visit(const Field& field) = 0; + virtual void Visit(const Field& field) = 0; + virtual void Visit(const Field& field) = 0; + virtual void Visit(const Field& field) = 0; + virtual void Visit(const Field& field) = 0; + virtual void Visit(const Field& field) = 0; + + /// Completion method, called once all fields have been visited + virtual void Complete() = 0; + virtual bool SubmitTestcase() = 0; +}; + +/** + * Empty implementation of VisitorInterface that drops all fields. Used when a functional + * backend implementation is not available. + */ +struct NullVisitor final : public VisitorInterface { + SUYU_NON_COPYABLE(NullVisitor); + + NullVisitor() = default; + ~NullVisitor() override = default; + + void Visit(const Field& /*field*/) override {} + void Visit(const Field& /*field*/) override {} + void Visit(const Field& /*field*/) override {} + void Visit(const Field& /*field*/) override {} + void Visit(const Field& /*field*/) override {} + void Visit(const Field& /*field*/) override {} + void Visit(const Field& /*field*/) override {} + void Visit(const Field& /*field*/) override {} + void Visit(const Field& /*field*/) override {} + void Visit(const Field& /*field*/) override {} + void Visit(const Field& /*field*/) override {} + void Visit(const Field& /*field*/) override {} + void Visit(const Field& /*field*/) override {} + void Visit(const Field& /*field*/) override {} + + void Complete() override {} + bool SubmitTestcase() override { + return false; + } +}; + +/// Appends build-specific information to the given FieldCollection, +/// such as branch name, revision hash, etc. +void AppendBuildInfo(FieldCollection& fc); + +/// Appends CPU-specific information to the given FieldCollection, +/// such as instruction set extensions, etc. +void AppendCPUInfo(FieldCollection& fc); + +/// Appends OS-specific information to the given FieldCollection, +/// such as platform name, etc. +void AppendOSInfo(FieldCollection& fc); + +} // namespace Common::Telemetry diff --git a/src/common/tree.h b/src/common/tree.h index a260e83a32..f4fc43de36 100644 --- a/src/common/tree.h +++ b/src/common/tree.h @@ -103,9 +103,9 @@ concept IsRBEntry = CheckRBEntry::value; template concept HasRBEntry = requires(T& t, const T& ct) { - { t.GetRBEntry() } -> std::same_as&>; - { ct.GetRBEntry() } -> std::same_as&>; -}; + { t.GetRBEntry() } -> std::same_as&>; + { ct.GetRBEntry() } -> std::same_as&>; + }; template requires HasRBEntry diff --git a/src/common/vector_math.h b/src/common/vector_math.h index 720e89f491..b4885835df 100644 --- a/src/common/vector_math.h +++ b/src/common/vector_math.h @@ -362,9 +362,7 @@ public: // _DEFINE_SWIZZLER2 defines a single such function, DEFINE_SWIZZLER2 defines all of them for all // component names (x<->r) and permutations (xy<->yx) #define _DEFINE_SWIZZLER2(a, b, name) \ - [[nodiscard]] constexpr Vec2 name() const { \ - return Vec2(a, b); \ - } + [[nodiscard]] constexpr Vec2 name() const { return Vec2(a, b); } #define DEFINE_SWIZZLER2(a, b, a2, b2, a3, b3, a4, b4) \ _DEFINE_SWIZZLER2(a, b, a##b); \ _DEFINE_SWIZZLER2(a, b, a2##b2); \ @@ -557,9 +555,7 @@ public: // DEFINE_SWIZZLER2_COMP2 defines two component functions for all component names (x<->r) and // permutations (xy<->yx) #define _DEFINE_SWIZZLER2(a, b, name) \ - [[nodiscard]] constexpr Vec2 name() const { \ - return Vec2(a, b); \ - } + [[nodiscard]] constexpr Vec2 name() const { return Vec2(a, b); } #define DEFINE_SWIZZLER2_COMP1(a, a2) \ _DEFINE_SWIZZLER2(a, a, a##a); \ _DEFINE_SWIZZLER2(a, a, a2##a2) @@ -584,9 +580,7 @@ public: #undef _DEFINE_SWIZZLER2 #define _DEFINE_SWIZZLER3(a, b, c, name) \ - [[nodiscard]] constexpr Vec3 name() const { \ - return Vec3(a, b, c); \ - } + [[nodiscard]] constexpr Vec3 name() const { return Vec3(a, b, c); } #define DEFINE_SWIZZLER3_COMP1(a, a2) \ _DEFINE_SWIZZLER3(a, a, a, a##a##a); \ _DEFINE_SWIZZLER3(a, a, a, a2##a2##a2) diff --git a/src/common/virtual_buffer.h b/src/common/virtual_buffer.h index a6cf25baa2..4f6e3e6e5c 100644 --- a/src/common/virtual_buffer.h +++ b/src/common/virtual_buffer.h @@ -33,8 +33,8 @@ public: VirtualBuffer& operator=(const VirtualBuffer&) = delete; VirtualBuffer(VirtualBuffer&& other) noexcept - : alloc_size{std::exchange(other.alloc_size, 0)}, - base_ptr{std::exchange(other.base_ptr), nullptr} {} + : alloc_size{std::exchange(other.alloc_size, 0)}, base_ptr{std::exchange(other.base_ptr), + nullptr} {} VirtualBuffer& operator=(VirtualBuffer&& other) noexcept { alloc_size = std::exchange(other.alloc_size, 0); diff --git a/src/common/x64/native_clock.cpp b/src/common/x64/native_clock.cpp index 80d6e2bc5c..d2d27fafea 100644 --- a/src/common/x64/native_clock.cpp +++ b/src/common/x64/native_clock.cpp @@ -8,8 +8,8 @@ namespace Common::X64 { NativeClock::NativeClock(u64 rdtsc_frequency_) - : rdtsc_frequency{rdtsc_frequency_}, - ns_rdtsc_factor{GetFixedPoint64Factor(NsRatio::den, rdtsc_frequency)}, + : rdtsc_frequency{rdtsc_frequency_}, ns_rdtsc_factor{GetFixedPoint64Factor(NsRatio::den, + rdtsc_frequency)}, us_rdtsc_factor{GetFixedPoint64Factor(UsRatio::den, rdtsc_frequency)}, ms_rdtsc_factor{GetFixedPoint64Factor(MsRatio::den, rdtsc_frequency)}, cntpct_rdtsc_factor{GetFixedPoint64Factor(CNTFRQ, rdtsc_frequency)}, diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index fbaa281b50..5f9bbc543b 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -1136,6 +1136,8 @@ add_library(core STATIC precompiled_headers.h reporter.cpp reporter.h + telemetry_session.cpp + telemetry_session.h tools/freezer.cpp tools/freezer.h tools/renderdoc.cpp diff --git a/src/core/arm/dynarmic/arm_dynarmic_32.cpp b/src/core/arm/dynarmic/arm_dynarmic_32.cpp index c9df0c022e..36478f7224 100644 --- a/src/core/arm/dynarmic/arm_dynarmic_32.cpp +++ b/src/core/arm/dynarmic/arm_dynarmic_32.cpp @@ -16,8 +16,8 @@ using namespace Common::Literals; class DynarmicCallbacks32 : public Dynarmic::A32::UserCallbacks { public: explicit DynarmicCallbacks32(ArmDynarmic32& parent, Kernel::KProcess* process) - : m_parent{parent}, m_memory(process->GetMemory()), m_process(process), - m_debugger_enabled{parent.m_system.DebuggerEnabled()}, + : m_parent{parent}, m_memory(process->GetMemory()), + m_process(process), m_debugger_enabled{parent.m_system.DebuggerEnabled()}, m_check_memory_access{m_debugger_enabled || !Settings::values.cpuopt_ignore_memory_aborts.GetValue()} {} diff --git a/src/core/arm/dynarmic/arm_dynarmic_64.cpp b/src/core/arm/dynarmic/arm_dynarmic_64.cpp index 77a0b352f1..c811c8ad56 100644 --- a/src/core/arm/dynarmic/arm_dynarmic_64.cpp +++ b/src/core/arm/dynarmic/arm_dynarmic_64.cpp @@ -16,8 +16,8 @@ using namespace Common::Literals; class DynarmicCallbacks64 : public Dynarmic::A64::UserCallbacks { public: explicit DynarmicCallbacks64(ArmDynarmic64& parent, Kernel::KProcess* process) - : m_parent{parent}, m_memory(process->GetMemory()), m_process(process), - m_debugger_enabled{parent.m_system.DebuggerEnabled()}, + : m_parent{parent}, m_memory(process->GetMemory()), + m_process(process), m_debugger_enabled{parent.m_system.DebuggerEnabled()}, m_check_memory_access{m_debugger_enabled || !Settings::values.cpuopt_ignore_memory_aborts.GetValue()} {} diff --git a/src/core/core.cpp b/src/core/core.cpp index 0cb81d6d8f..e1c8b41ee5 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp @@ -55,6 +55,7 @@ #include "core/memory/cheat_engine.h" #include "core/perf_stats.h" #include "core/reporter.h" +#include "core/telemetry_session.h" #include "core/tools/freezer.h" #include "core/tools/renderdoc.h" #include "hid_core/hid_core.h" @@ -271,6 +272,8 @@ struct System::Impl { } SystemResultStatus SetupForApplicationProcess(System& system, Frontend::EmuWindow& emu_window) { + telemetry_session = std::make_unique(); + host1x_core = std::make_unique(system); gpu_core = VideoCore::CreateGPU(emu_window, system); if (!gpu_core) { @@ -351,6 +354,8 @@ struct System::Impl { return init_result; } + telemetry_session->AddInitialInfo(*app_loader, fs_controller, *content_provider); + // Initialize cheat engine if (cheat_engine) { cheat_engine->Initialize(); @@ -396,6 +401,21 @@ struct System::Impl { void ShutdownMainProcess() { SetShuttingDown(true); + // Log last frame performance stats if game was loaded + if (perf_stats) { + const auto perf_results = GetAndResetPerfStats(); + constexpr auto performance = Common::Telemetry::FieldType::Performance; + + telemetry_session->AddField(performance, "Shutdown_EmulationSpeed", + perf_results.emulation_speed * 100.0); + telemetry_session->AddField(performance, "Shutdown_Framerate", + perf_results.average_game_fps); + telemetry_session->AddField(performance, "Shutdown_Frametime", + perf_results.frametime * 1000.0); + telemetry_session->AddField(performance, "Mean_Frametime_MS", + perf_stats->GetMeanFrametime()); + } + is_powered_on = false; exit_locked = false; exit_requested = false; @@ -414,6 +434,7 @@ struct System::Impl { service_manager.reset(); fs_controller.Reset(); cheat_engine.reset(); + telemetry_session.reset(); core_timing.ClearPendingEvents(); app_loader.reset(); audio_core.reset(); @@ -513,6 +534,9 @@ struct System::Impl { /// Services std::unique_ptr services; + /// Telemetry session for this emulation session + std::unique_ptr telemetry_session; + /// Network instance Network::NetworkInstance network_instance; @@ -639,6 +663,14 @@ PerfStatsResults System::GetAndResetPerfStats() { return impl->GetAndResetPerfStats(); } +TelemetrySession& System::TelemetrySession() { + return *impl->telemetry_session; +} + +const TelemetrySession& System::TelemetrySession() const { + return *impl->telemetry_session; +} + Kernel::PhysicalCore& System::CurrentPhysicalCore() { return impl->kernel.CurrentPhysicalCore(); } diff --git a/src/core/core.h b/src/core/core.h index 0cc1108b80..90826bd3a1 100644 --- a/src/core/core.h +++ b/src/core/core.h @@ -122,6 +122,7 @@ class GPUDirtyMemoryManager; class PerfStats; class Reporter; class SpeedLimiter; +class TelemetrySession; struct PerfStatsResults; @@ -217,6 +218,12 @@ public: */ [[nodiscard]] bool IsPoweredOn() const; + /// Gets a reference to the telemetry session for this emulation session. + [[nodiscard]] Core::TelemetrySession& TelemetrySession(); + + /// Gets a reference to the telemetry session for this emulation session. + [[nodiscard]] const Core::TelemetrySession& TelemetrySession() const; + /// Prepare the core emulation for a reschedule void PrepareReschedule(u32 core_index); diff --git a/src/core/file_sys/card_image.cpp b/src/core/file_sys/card_image.cpp index 396a40d013..0bcf40cf86 100644 --- a/src/core/file_sys/card_image.cpp +++ b/src/core/file_sys/card_image.cpp @@ -29,8 +29,8 @@ constexpr std::array partition_names{ XCI::XCI(VirtualFile file_, u64 program_id, size_t program_index) : file(std::move(file_)), program_nca_status{Loader::ResultStatus::ErrorXCIMissingProgramNCA}, - partitions(partition_names.size()), partitions_raw(partition_names.size()), - keys{Core::Crypto::KeyManager::Instance()} { + partitions(partition_names.size()), + partitions_raw(partition_names.size()), keys{Core::Crypto::KeyManager::Instance()} { const auto header_status = TryReadHeader(); if (header_status != Loader::ResultStatus::Success) { status = header_status; diff --git a/src/core/file_sys/fs_path_utility.h b/src/core/file_sys/fs_path_utility.h index 51418ee160..cdfd8c7729 100644 --- a/src/core/file_sys/fs_path_utility.h +++ b/src/core/file_sys/fs_path_utility.h @@ -91,12 +91,8 @@ public: } #define DECLARE_PATH_FLAG_HANDLER(__WHICH__) \ - constexpr bool Is##__WHICH__##Allowed() const { \ - return (m_value & __WHICH__##Flag) != 0; \ - } \ - constexpr void Allow##__WHICH__() { \ - m_value |= __WHICH__##Flag; \ - } + constexpr bool Is##__WHICH__##Allowed() const { return (m_value & __WHICH__##Flag) != 0; } \ + constexpr void Allow##__WHICH__() { m_value |= __WHICH__##Flag; } DECLARE_PATH_FLAG_HANDLER(WindowsPath) DECLARE_PATH_FLAG_HANDLER(RelativePath) diff --git a/src/core/file_sys/submission_package.cpp b/src/core/file_sys/submission_package.cpp index 59173c6170..68e8ec22fc 100644 --- a/src/core/file_sys/submission_package.cpp +++ b/src/core/file_sys/submission_package.cpp @@ -19,9 +19,9 @@ namespace FileSys { NSP::NSP(VirtualFile file_, u64 title_id_, std::size_t program_index_) - : file(std::move(file_)), expected_program_id(title_id_), program_index(program_index_), - status{Loader::ResultStatus::Success}, pfs(std::make_shared(file)), - keys{Core::Crypto::KeyManager::Instance()} { + : file(std::move(file_)), expected_program_id(title_id_), + program_index(program_index_), status{Loader::ResultStatus::Success}, + pfs(std::make_shared(file)), keys{Core::Crypto::KeyManager::Instance()} { if (pfs->GetStatus() != Loader::ResultStatus::Success) { status = pfs->GetStatus(); return; diff --git a/src/core/file_sys/xts_archive.cpp b/src/core/file_sys/xts_archive.cpp index 64fa442826..6692211e1d 100644 --- a/src/core/file_sys/xts_archive.cpp +++ b/src/core/file_sys/xts_archive.cpp @@ -44,8 +44,8 @@ static bool CalculateHMAC256(Destination* out, const SourceKey* key, std::size_t } NAX::NAX(VirtualFile file_) - : header(std::make_unique()), file(std::move(file_)), - keys{Core::Crypto::KeyManager::Instance()} { + : header(std::make_unique()), + file(std::move(file_)), keys{Core::Crypto::KeyManager::Instance()} { std::string path = Common::FS::SanitizePath(file->GetFullPath()); static const std::regex nax_path_regex("/registered/(000000[0-9A-F]{2})/([0-9A-F]{32})\\.nca", std::regex_constants::ECMAScript | @@ -62,8 +62,8 @@ NAX::NAX(VirtualFile file_) } NAX::NAX(VirtualFile file_, std::array nca_id) - : header(std::make_unique()), file(std::move(file_)), - keys{Core::Crypto::KeyManager::Instance()} { + : header(std::make_unique()), + file(std::move(file_)), keys{Core::Crypto::KeyManager::Instance()} { Core::Crypto::SHA256Hash hash{}; mbedtls_sha256_ret(nca_id.data(), nca_id.size(), hash.data(), 0); status = Parse(fmt::format("/registered/000000{:02X}/{}.nca", hash[0], diff --git a/src/core/hle/kernel/k_auto_object.h b/src/core/hle/kernel/k_auto_object.h index 6e1a5246de..3ec056119b 100644 --- a/src/core/hle/kernel/k_auto_object.h +++ b/src/core/hle/kernel/k_auto_object.h @@ -24,9 +24,7 @@ private: friend class ::Kernel::KClassTokenGenerator; \ static constexpr inline auto ObjectType = ::Kernel::KClassTokenGenerator::ObjectType::CLASS; \ static constexpr inline const char* const TypeName = #CLASS; \ - static constexpr inline ClassTokenType ClassToken() { \ - return ::Kernel::ClassToken; \ - } \ + static constexpr inline ClassTokenType ClassToken() { return ::Kernel::ClassToken; } \ \ public: \ SUYU_NON_COPYABLE(CLASS); \ @@ -37,15 +35,9 @@ public: constexpr ClassTokenType Token = ClassToken(); \ return TypeObj(TypeName, Token); \ } \ - static constexpr const char* GetStaticTypeName() { \ - return TypeName; \ - } \ - virtual TypeObj GetTypeObj() ATTRIBUTE { \ - return GetStaticTypeObj(); \ - } \ - virtual const char* GetTypeName() ATTRIBUTE { \ - return GetStaticTypeName(); \ - } \ + static constexpr const char* GetStaticTypeName() { return TypeName; } \ + virtual TypeObj GetTypeObj() ATTRIBUTE { return GetStaticTypeObj(); } \ + virtual const char* GetTypeName() ATTRIBUTE { return GetStaticTypeName(); } \ \ private: \ constexpr bool operator!=(const TypeObj& rhs) diff --git a/src/core/hle/kernel/k_memory_layout.cpp b/src/core/hle/kernel/k_memory_layout.cpp index 8aec520010..bec7146688 100644 --- a/src/core/hle/kernel/k_memory_layout.cpp +++ b/src/core/hle/kernel/k_memory_layout.cpp @@ -128,8 +128,8 @@ KVirtualAddress KMemoryRegionTree::GetRandomAlignedRegion(size_t size, size_t al KMemoryLayout::KMemoryLayout() : m_virtual_tree{m_memory_region_allocator}, m_physical_tree{m_memory_region_allocator}, - m_virtual_linear_tree{m_memory_region_allocator}, - m_physical_linear_tree{m_memory_region_allocator} {} + m_virtual_linear_tree{m_memory_region_allocator}, m_physical_linear_tree{ + m_memory_region_allocator} {} void KMemoryLayout::InitializeLinearMemoryRegionTrees(KPhysicalAddress aligned_linear_phys_start, KVirtualAddress linear_virtual_start) { diff --git a/src/core/hle/kernel/k_priority_queue.h b/src/core/hle/kernel/k_priority_queue.h index b250700293..26677ec65c 100644 --- a/src/core/hle/kernel/k_priority_queue.h +++ b/src/core/hle/kernel/k_priority_queue.h @@ -17,32 +17,38 @@ namespace Kernel { class KThread; template -concept KPriorityQueueAffinityMask = !std::is_reference_v && requires(T& t) { - { t.GetAffinityMask() } -> Common::ConvertibleTo; - { t.SetAffinityMask(0) }; +concept KPriorityQueueAffinityMask = ! +std::is_reference_v&& requires(T& t) { + { t.GetAffinityMask() } -> Common::ConvertibleTo; + { t.SetAffinityMask(0) }; - { t.GetAffinity(0) } -> std::same_as; - { t.SetAffinity(0, false) }; - { t.SetAll() }; -}; + { t.GetAffinity(0) } -> std::same_as; + { t.SetAffinity(0, false) }; + { t.SetAll() }; + }; template -concept KPriorityQueueMember = !std::is_reference_v && requires(T& t) { - { typename T::QueueEntry() }; - { (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; - { (typename T::QueueEntry()).GetPrev() } -> std::same_as; - { t.GetPriorityQueueEntry(0) } -> std::same_as; +concept KPriorityQueueMember = ! +std::is_reference_v&& requires(T& t) { + { typename T::QueueEntry() }; + { (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; + { (typename T::QueueEntry()).GetPrev() } -> std::same_as; + { + t.GetPriorityQueueEntry(0) + } -> std::same_as; - { t.GetAffinityMask() }; - { std::remove_cvref_t() } -> KPriorityQueueAffinityMask; + { t.GetAffinityMask() }; + { + std::remove_cvref_t() + } -> KPriorityQueueAffinityMask; - { t.GetActiveCore() } -> Common::ConvertibleTo; - { t.GetPriority() } -> Common::ConvertibleTo; - { t.IsDummyThread() } -> Common::ConvertibleTo; -}; + { t.GetActiveCore() } -> Common::ConvertibleTo; + { t.GetPriority() } -> Common::ConvertibleTo; + { t.IsDummyThread() } -> Common::ConvertibleTo; + }; template requires KPriorityQueueMember diff --git a/src/core/hle/kernel/k_scoped_lock.h b/src/core/hle/kernel/k_scoped_lock.h index bc9830b4da..629a7d20dd 100644 --- a/src/core/hle/kernel/k_scoped_lock.h +++ b/src/core/hle/kernel/k_scoped_lock.h @@ -10,10 +10,11 @@ namespace Kernel { template -concept KLockable = !std::is_reference_v && requires(T& t) { - { t.Lock() } -> std::same_as; - { t.Unlock() } -> std::same_as; -}; +concept KLockable = ! +std::is_reference_v&& requires(T& t) { + { t.Lock() } -> std::same_as; + { t.Unlock() } -> std::same_as; + }; template requires KLockable diff --git a/src/core/hle/result.h b/src/core/hle/result.h index 7016a5ecea..9a7c10efae 100644 --- a/src/core/hle/result.h +++ b/src/core/hle/result.h @@ -458,13 +458,9 @@ constexpr inline Result __TmpCurrentResultReference = ResultSuccess; if (true) #define R_CONVERT(catch_type, convert_type) \ - R_CATCH(catch_type) { \ - R_THROW(static_cast(convert_type)); \ - } + R_CATCH(catch_type) { R_THROW(static_cast(convert_type)); } #define R_CONVERT_ALL(convert_type) \ - R_CATCH_ALL() { \ - R_THROW(static_cast(convert_type)); \ - } + R_CATCH_ALL() { R_THROW(static_cast(convert_type)); } #define R_ASSERT(res_expr) ASSERT(R_SUCCEEDED(res_expr)) diff --git a/src/core/hle/service/acc/acc.cpp b/src/core/hle/service/acc/acc.cpp index 4f176e4f8a..099fdbc368 100644 --- a/src/core/hle/service/acc/acc.cpp +++ b/src/core/hle/service/acc/acc.cpp @@ -1028,8 +1028,8 @@ void Module::Interface::TrySelectUserWithoutInteraction(HLERequestContext& ctx) Module::Interface::Interface(std::shared_ptr module_, std::shared_ptr profile_manager_, Core::System& system_, const char* name) - : ServiceFramework{system_, name}, module{std::move(module_)}, - profile_manager{std::move(profile_manager_)} {} + : ServiceFramework{system_, name}, module{std::move(module_)}, profile_manager{std::move( + profile_manager_)} {} Module::Interface::~Interface() = default; diff --git a/src/core/hle/service/am/frontend/applet_cabinet.cpp b/src/core/hle/service/am/frontend/applet_cabinet.cpp index 2fb9a83531..4cbc80d639 100644 --- a/src/core/hle/service/am/frontend/applet_cabinet.cpp +++ b/src/core/hle/service/am/frontend/applet_cabinet.cpp @@ -18,8 +18,9 @@ namespace Service::AM::Frontend { Cabinet::Cabinet(Core::System& system_, std::shared_ptr applet_, LibraryAppletMode applet_mode_, const Core::Frontend::CabinetApplet& frontend_) - : FrontendApplet{system_, applet_, applet_mode_}, frontend{frontend_}, - service_context{system_, "CabinetApplet"} { + : FrontendApplet{system_, applet_, applet_mode_}, frontend{frontend_}, service_context{ + system_, + "CabinetApplet"} { availability_change_event = service_context.CreateEvent("CabinetApplet:AvailabilityChangeEvent"); diff --git a/src/core/hle/service/am/service/application_proxy.cpp b/src/core/hle/service/am/service/application_proxy.cpp index 633f2c2bbd..6e1328fee7 100644 --- a/src/core/hle/service/am/service/application_proxy.cpp +++ b/src/core/hle/service/am/service/application_proxy.cpp @@ -18,8 +18,8 @@ namespace Service::AM { IApplicationProxy::IApplicationProxy(Core::System& system_, std::shared_ptr applet, Kernel::KProcess* process, WindowSystem& window_system) - : ServiceFramework{system_, "IApplicationProxy"}, m_window_system{window_system}, - m_process{process}, m_applet{std::move(applet)} { + : ServiceFramework{system_, "IApplicationProxy"}, + m_window_system{window_system}, m_process{process}, m_applet{std::move(applet)} { // clang-format off static const FunctionInfo functions[] = { {0, D<&IApplicationProxy::GetCommonStateGetter>, "GetCommonStateGetter"}, diff --git a/src/core/hle/service/am/service/library_applet_creator.cpp b/src/core/hle/service/am/service/library_applet_creator.cpp index d61aa42516..3ffb03bc97 100644 --- a/src/core/hle/service/am/service/library_applet_creator.cpp +++ b/src/core/hle/service/am/service/library_applet_creator.cpp @@ -165,8 +165,8 @@ std::shared_ptr CreateFrontendApplet(Core::System& syste ILibraryAppletCreator::ILibraryAppletCreator(Core::System& system_, std::shared_ptr applet, WindowSystem& window_system) - : ServiceFramework{system_, "ILibraryAppletCreator"}, m_window_system{window_system}, - m_applet{std::move(applet)} { + : ServiceFramework{system_, "ILibraryAppletCreator"}, + m_window_system{window_system}, m_applet{std::move(applet)} { static const FunctionInfo functions[] = { {0, D<&ILibraryAppletCreator::CreateLibraryApplet>, "CreateLibraryApplet"}, {1, nullptr, "TerminateAllLibraryApplets"}, diff --git a/src/core/hle/service/am/service/library_applet_proxy.cpp b/src/core/hle/service/am/service/library_applet_proxy.cpp index 655dd69b29..f9cfb82a90 100644 --- a/src/core/hle/service/am/service/library_applet_proxy.cpp +++ b/src/core/hle/service/am/service/library_applet_proxy.cpp @@ -20,8 +20,8 @@ namespace Service::AM { ILibraryAppletProxy::ILibraryAppletProxy(Core::System& system_, std::shared_ptr applet, Kernel::KProcess* process, WindowSystem& window_system) - : ServiceFramework{system_, "ILibraryAppletProxy"}, m_window_system{window_system}, - m_process{process}, m_applet{std::move(applet)} { + : ServiceFramework{system_, "ILibraryAppletProxy"}, + m_window_system{window_system}, m_process{process}, m_applet{std::move(applet)} { // clang-format off static const FunctionInfo functions[] = { {0, D<&ILibraryAppletProxy::GetCommonStateGetter>, "GetCommonStateGetter"}, diff --git a/src/core/hle/service/am/service/self_controller.cpp b/src/core/hle/service/am/service/self_controller.cpp index 52efda973d..e2b17099a3 100644 --- a/src/core/hle/service/am/service/self_controller.cpp +++ b/src/core/hle/service/am/service/self_controller.cpp @@ -16,8 +16,8 @@ namespace Service::AM { ISelfController::ISelfController(Core::System& system_, std::shared_ptr applet, Kernel::KProcess* process) - : ServiceFramework{system_, "ISelfController"}, m_process{process}, - m_applet{std::move(applet)} { + : ServiceFramework{system_, "ISelfController"}, m_process{process}, m_applet{ + std::move(applet)} { // clang-format off static const FunctionInfo functions[] = { {0, D<&ISelfController::Exit>, "Exit"}, diff --git a/src/core/hle/service/am/service/system_applet_proxy.cpp b/src/core/hle/service/am/service/system_applet_proxy.cpp index 609c4aad3e..c435288a26 100644 --- a/src/core/hle/service/am/service/system_applet_proxy.cpp +++ b/src/core/hle/service/am/service/system_applet_proxy.cpp @@ -20,8 +20,8 @@ namespace Service::AM { ISystemAppletProxy::ISystemAppletProxy(Core::System& system_, std::shared_ptr applet, Kernel::KProcess* process, WindowSystem& window_system) - : ServiceFramework{system_, "ISystemAppletProxy"}, m_window_system{window_system}, - m_process{process}, m_applet{std::move(applet)} { + : ServiceFramework{system_, "ISystemAppletProxy"}, + m_window_system{window_system}, m_process{process}, m_applet{std::move(applet)} { // clang-format off static const FunctionInfo functions[] = { {0, D<&ISystemAppletProxy::GetCommonStateGetter>, "GetCommonStateGetter"}, diff --git a/src/core/hle/service/am/service/window_controller.cpp b/src/core/hle/service/am/service/window_controller.cpp index 6b1759f037..54396affbe 100644 --- a/src/core/hle/service/am/service/window_controller.cpp +++ b/src/core/hle/service/am/service/window_controller.cpp @@ -11,8 +11,8 @@ namespace Service::AM { IWindowController::IWindowController(Core::System& system_, std::shared_ptr applet, WindowSystem& window_system) - : ServiceFramework{system_, "IWindowController"}, m_window_system{window_system}, - m_applet{std::move(applet)} { + : ServiceFramework{system_, "IWindowController"}, + m_window_system{window_system}, m_applet{std::move(applet)} { // clang-format off static const FunctionInfo functions[] = { {0, nullptr, "CreateWindow"}, diff --git a/src/core/hle/service/aoc/purchase_event_manager.cpp b/src/core/hle/service/aoc/purchase_event_manager.cpp index 1192263f8a..9e718510b4 100644 --- a/src/core/hle/service/aoc/purchase_event_manager.cpp +++ b/src/core/hle/service/aoc/purchase_event_manager.cpp @@ -9,8 +9,8 @@ namespace Service::AOC { constexpr Result ResultNoPurchasedProductInfoAvailable{ErrorModule::NIMShop, 400}; IPurchaseEventManager::IPurchaseEventManager(Core::System& system_) - : ServiceFramework{system_, "IPurchaseEventManager"}, - service_context{system, "IPurchaseEventManager"} { + : ServiceFramework{system_, "IPurchaseEventManager"}, service_context{system, + "IPurchaseEventManager"} { // clang-format off static const FunctionInfo functions[] = { {0, D<&IPurchaseEventManager::SetDefaultDeliveryTarget>, "SetDefaultDeliveryTarget"}, diff --git a/src/core/hle/service/audio/audio_in.cpp b/src/core/hle/service/audio/audio_in.cpp index 9240cc3363..416803acc3 100644 --- a/src/core/hle/service/audio/audio_in.cpp +++ b/src/core/hle/service/audio/audio_in.cpp @@ -12,8 +12,9 @@ IAudioIn::IAudioIn(Core::System& system_, Manager& manager, size_t session_id, const std::string& device_name, const AudioInParameter& in_params, Kernel::KProcess* handle, u64 applet_resource_user_id) : ServiceFramework{system_, "IAudioIn"}, process{handle}, service_context{system_, "IAudioIn"}, - event{service_context.CreateEvent("AudioInEvent")}, - impl{std::make_shared(system_, manager, event, session_id)} { + event{service_context.CreateEvent("AudioInEvent")}, impl{std::make_shared(system_, + manager, event, + session_id)} { // clang-format off static const FunctionInfo functions[] = { {0, D<&IAudioIn::GetAudioInState>, "GetAudioInState"}, diff --git a/src/core/hle/service/audio/audio_in_manager.cpp b/src/core/hle/service/audio/audio_in_manager.cpp index d55da17c84..2675a57735 100644 --- a/src/core/hle/service/audio/audio_in_manager.cpp +++ b/src/core/hle/service/audio/audio_in_manager.cpp @@ -10,8 +10,8 @@ namespace Service::Audio { using namespace AudioCore::AudioIn; IAudioInManager::IAudioInManager(Core::System& system_) - : ServiceFramework{system_, "audin:u"}, - impl{std::make_unique(system_)} { + : ServiceFramework{system_, "audin:u"}, impl{std::make_unique( + system_)} { // clang-format off static const FunctionInfo functions[] = { {0, D<&IAudioInManager::ListAudioIns>, "ListAudioIns"}, diff --git a/src/core/hle/service/audio/audio_renderer.cpp b/src/core/hle/service/audio/audio_renderer.cpp index b78660cea6..fc4aad2339 100644 --- a/src/core/hle/service/audio/audio_renderer.cpp +++ b/src/core/hle/service/audio/audio_renderer.cpp @@ -14,8 +14,8 @@ IAudioRenderer::IAudioRenderer(Core::System& system_, Manager& manager_, s32 session_id) : ServiceFramework{system_, "IAudioRenderer"}, service_context{system_, "IAudioRenderer"}, rendered_event{service_context.CreateEvent("IAudioRendererEvent")}, manager{manager_}, - impl{std::make_unique(system_, manager, rendered_event)}, - process_handle{process_handle_} { + impl{std::make_unique(system_, manager, rendered_event)}, process_handle{ + process_handle_} { // clang-format off static const FunctionInfo functions[] = { {0, D<&IAudioRenderer::GetSampleRate>, "GetSampleRate"}, diff --git a/src/core/hle/service/bcat/news/newly_arrived_event_holder.cpp b/src/core/hle/service/bcat/news/newly_arrived_event_holder.cpp index 8d64bd697a..ed393f7a26 100644 --- a/src/core/hle/service/bcat/news/newly_arrived_event_holder.cpp +++ b/src/core/hle/service/bcat/news/newly_arrived_event_holder.cpp @@ -7,8 +7,9 @@ namespace Service::News { INewlyArrivedEventHolder::INewlyArrivedEventHolder(Core::System& system_) - : ServiceFramework{system_, "INewlyArrivedEventHolder"}, - service_context{system_, "INewlyArrivedEventHolder"} { + : ServiceFramework{system_, "INewlyArrivedEventHolder"}, service_context{ + system_, + "INewlyArrivedEventHolder"} { // clang-format off static const FunctionInfo functions[] = { {0, D<&INewlyArrivedEventHolder::Get>, "Get"}, diff --git a/src/core/hle/service/bcat/news/overwrite_event_holder.cpp b/src/core/hle/service/bcat/news/overwrite_event_holder.cpp index 33ea7c8951..1712971e4d 100644 --- a/src/core/hle/service/bcat/news/overwrite_event_holder.cpp +++ b/src/core/hle/service/bcat/news/overwrite_event_holder.cpp @@ -7,8 +7,8 @@ namespace Service::News { IOverwriteEventHolder::IOverwriteEventHolder(Core::System& system_) - : ServiceFramework{system_, "IOverwriteEventHolder"}, - service_context{system_, "IOverwriteEventHolder"} { + : ServiceFramework{system_, "IOverwriteEventHolder"}, service_context{system_, + "IOverwriteEventHolder"} { // clang-format off static const FunctionInfo functions[] = { {0, D<&IOverwriteEventHolder::Get>, "Get"}, diff --git a/src/core/hle/service/filesystem/fsp/fs_i_filesystem.cpp b/src/core/hle/service/filesystem/fsp/fs_i_filesystem.cpp index de7b550544..d881e144d3 100644 --- a/src/core/hle/service/filesystem/fsp/fs_i_filesystem.cpp +++ b/src/core/hle/service/filesystem/fsp/fs_i_filesystem.cpp @@ -11,8 +11,8 @@ namespace Service::FileSystem { IFileSystem::IFileSystem(Core::System& system_, FileSys::VirtualDir dir_, SizeGetter size_getter_) - : ServiceFramework{system_, "IFileSystem"}, - backend{std::make_unique(dir_)}, + : ServiceFramework{system_, "IFileSystem"}, backend{std::make_unique( + dir_)}, size_getter{std::move(size_getter_)} { static const FunctionInfo functions[] = { {0, D<&IFileSystem::CreateFile>, "CreateFile"}, diff --git a/src/core/hle/service/filesystem/fsp/fs_i_save_data_info_reader.cpp b/src/core/hle/service/filesystem/fsp/fs_i_save_data_info_reader.cpp index 9a603e89a2..ff823586b3 100644 --- a/src/core/hle/service/filesystem/fsp/fs_i_save_data_info_reader.cpp +++ b/src/core/hle/service/filesystem/fsp/fs_i_save_data_info_reader.cpp @@ -12,8 +12,8 @@ namespace Service::FileSystem { ISaveDataInfoReader::ISaveDataInfoReader(Core::System& system_, std::shared_ptr save_data_controller_, FileSys::SaveDataSpaceId space) - : ServiceFramework{system_, "ISaveDataInfoReader"}, - save_data_controller{save_data_controller_} { + : ServiceFramework{system_, "ISaveDataInfoReader"}, save_data_controller{ + save_data_controller_} { static const FunctionInfo functions[] = { {0, D<&ISaveDataInfoReader::ReadSaveDataInfo>, "ReadSaveDataInfo"}, }; diff --git a/src/core/hle/service/glue/time/alarm_worker.cpp b/src/core/hle/service/glue/time/alarm_worker.cpp index 21ace76936..3ff071f4a0 100644 --- a/src/core/hle/service/glue/time/alarm_worker.cpp +++ b/src/core/hle/service/glue/time/alarm_worker.cpp @@ -11,8 +11,8 @@ namespace Service::Glue::Time { AlarmWorker::AlarmWorker(Core::System& system, StandardSteadyClockResource& steady_clock_resource) - : m_system{system}, m_ctx{system, "Glue:AlarmWorker"}, - m_steady_clock_resource{steady_clock_resource} {} + : m_system{system}, m_ctx{system, "Glue:AlarmWorker"}, m_steady_clock_resource{ + steady_clock_resource} {} AlarmWorker::~AlarmWorker() { m_system.CoreTiming().UnscheduleEvent(m_timer_timing_event); diff --git a/src/core/hle/service/glue/time/manager.cpp b/src/core/hle/service/glue/time/manager.cpp index c1faf8eaab..77bf8896cd 100644 --- a/src/core/hle/service/glue/time/manager.cpp +++ b/src/core/hle/service/glue/time/manager.cpp @@ -87,8 +87,10 @@ static Service::PSC::Time::LocationName GetTimeZoneString( } TimeManager::TimeManager(Core::System& system) - : m_steady_clock_resource{system}, m_time_zone_binary{system}, - m_worker{system, m_steady_clock_resource, m_file_timestamp_worker} { + : m_steady_clock_resource{system}, m_time_zone_binary{system}, m_worker{ + system, + m_steady_clock_resource, + m_file_timestamp_worker} { m_time_m = system.ServiceManager().GetService("time:m", true); diff --git a/src/core/hle/service/glue/time/time_zone.cpp b/src/core/hle/service/glue/time/time_zone.cpp index a89df753a4..b2e8159653 100644 --- a/src/core/hle/service/glue/time/time_zone.cpp +++ b/src/core/hle/service/glue/time/time_zone.cpp @@ -22,9 +22,9 @@ TimeZoneService::TimeZoneService( std::shared_ptr time_zone_service) : ServiceFramework{system_, "ITimeZoneService"}, m_system{system}, m_can_write_timezone_device_location{can_write_timezone_device_location}, - m_file_timestamp_worker{file_timestamp_worker}, - m_wrapped_service{std::move(time_zone_service)}, m_operation_event{m_system}, - m_time_zone_binary{time_zone_binary} { + m_file_timestamp_worker{file_timestamp_worker}, m_wrapped_service{std::move( + time_zone_service)}, + m_operation_event{m_system}, m_time_zone_binary{time_zone_binary} { // clang-format off static const FunctionInfo functions[] = { {0, D<&TimeZoneService::GetDeviceLocationName>, "GetDeviceLocationName"}, diff --git a/src/core/hle/service/glue/time/worker.cpp b/src/core/hle/service/glue/time/worker.cpp index 90cfe0a973..1dab3e9dcb 100644 --- a/src/core/hle/service/glue/time/worker.cpp +++ b/src/core/hle/service/glue/time/worker.cpp @@ -19,11 +19,11 @@ namespace Service::Glue::Time { TimeWorker::TimeWorker(Core::System& system, StandardSteadyClockResource& steady_clock_resource, FileTimestampWorker& file_timestamp_worker) - : m_system{system}, m_ctx{m_system, "Glue:TimeWorker"}, - m_event{m_ctx.CreateEvent("Glue:TimeWorker:Event")}, + : m_system{system}, m_ctx{m_system, "Glue:TimeWorker"}, m_event{m_ctx.CreateEvent( + "Glue:TimeWorker:Event")}, m_steady_clock_resource{steady_clock_resource}, - m_file_timestamp_worker{file_timestamp_worker}, - m_timer_steady_clock{m_ctx.CreateEvent("Glue:TimeWorker:SteadyClockTimerEvent")}, + m_file_timestamp_worker{file_timestamp_worker}, m_timer_steady_clock{m_ctx.CreateEvent( + "Glue:TimeWorker:SteadyClockTimerEvent")}, m_timer_file_system{m_ctx.CreateEvent("Glue:TimeWorker:FileTimeTimerEvent")}, m_alarm_worker{m_system, m_steady_clock_resource}, m_pm_state_change_handler{m_alarm_worker} { m_timer_steady_clock_timing_event = Core::Timing::CreateEvent( diff --git a/src/core/hle/service/hid/hid_debug_server.cpp b/src/core/hle/service/hid/hid_debug_server.cpp index debc9b4910..4e26636725 100644 --- a/src/core/hle/service/hid/hid_debug_server.cpp +++ b/src/core/hle/service/hid/hid_debug_server.cpp @@ -17,8 +17,8 @@ namespace Service::HID { IHidDebugServer::IHidDebugServer(Core::System& system_, std::shared_ptr resource, std::shared_ptr settings) - : ServiceFramework{system_, "hid:dbg"}, resource_manager{resource}, - firmware_settings{settings} { + : ServiceFramework{system_, "hid:dbg"}, resource_manager{resource}, firmware_settings{ + settings} { // clang-format off static const FunctionInfo functions[] = { {0, nullptr, "DeactivateDebugPad"}, diff --git a/src/core/hle/service/ipc_helpers.h b/src/core/hle/service/ipc_helpers.h index 97b46d5889..4b02872fba 100644 --- a/src/core/hle/service/ipc_helpers.h +++ b/src/core/hle/service/ipc_helpers.h @@ -72,8 +72,8 @@ public: u32 num_handles_to_copy_ = 0, u32 num_objects_to_move_ = 0, Flags flags = Flags::None) : RequestHelperBase(ctx), normal_params_size(normal_params_size_), - num_handles_to_copy(num_handles_to_copy_), num_objects_to_move(num_objects_to_move_), - kernel{ctx.kernel} { + num_handles_to_copy(num_handles_to_copy_), + num_objects_to_move(num_objects_to_move_), kernel{ctx.kernel} { memset(cmdbuf, 0, sizeof(u32) * IPC::COMMAND_BUFFER_LENGTH); diff --git a/src/core/hle/service/jit/jit_context.cpp b/src/core/hle/service/jit/jit_context.cpp index 8df63f9e26..0090e8568d 100644 --- a/src/core/hle/service/jit/jit_context.cpp +++ b/src/core/hle/service/jit/jit_context.cpp @@ -40,8 +40,8 @@ class DynarmicCallbacks64 : public Dynarmic::A64::UserCallbacks { public: explicit DynarmicCallbacks64(Core::Memory::Memory& memory_, std::vector& local_memory_, IntervalSet& mapped_ranges_, JITContextImpl& parent_) - : memory{memory_}, local_memory{local_memory_}, mapped_ranges{mapped_ranges_}, - parent{parent_} {} + : memory{memory_}, local_memory{local_memory_}, + mapped_ranges{mapped_ranges_}, parent{parent_} {} u8 MemoryRead8(u64 vaddr) override { return ReadMemory(vaddr); diff --git a/src/core/hle/service/mii/mii.cpp b/src/core/hle/service/mii/mii.cpp index 5232bcd050..adaaea571a 100644 --- a/src/core/hle/service/mii/mii.cpp +++ b/src/core/hle/service/mii/mii.cpp @@ -23,8 +23,8 @@ class IDatabaseService final : public ServiceFramework { public: explicit IDatabaseService(Core::System& system_, std::shared_ptr mii_manager, bool is_system_) - : ServiceFramework{system_, "IDatabaseService"}, manager{mii_manager}, - is_system{is_system_} { + : ServiceFramework{system_, "IDatabaseService"}, manager{mii_manager}, is_system{ + is_system_} { // clang-format off static const FunctionInfo functions[] = { {0, D<&IDatabaseService::IsUpdated>, "IsUpdated"}, diff --git a/src/core/hle/service/psc/time/alarms.cpp b/src/core/hle/service/psc/time/alarms.cpp index 64af6db573..5e52c19f82 100644 --- a/src/core/hle/service/psc/time/alarms.cpp +++ b/src/core/hle/service/psc/time/alarms.cpp @@ -30,8 +30,8 @@ Alarm::~Alarm() { Alarms::Alarms(Core::System& system, StandardSteadyClockCore& steady_clock, PowerStateRequestManager& power_state_request_manager) : m_system{system}, m_ctx{system, "Psc:Alarms"}, m_steady_clock{steady_clock}, - m_power_state_request_manager{power_state_request_manager}, - m_event{m_ctx.CreateEvent("Psc:Alarms:Event")} {} + m_power_state_request_manager{power_state_request_manager}, m_event{m_ctx.CreateEvent( + "Psc:Alarms:Event")} {} Alarms::~Alarms() { m_ctx.CloseEvent(m_event); diff --git a/src/core/hle/service/psc/time/clocks/standard_user_system_clock_core.cpp b/src/core/hle/service/psc/time/clocks/standard_user_system_clock_core.cpp index aa5b4c33c2..31ed273966 100644 --- a/src/core/hle/service/psc/time/clocks/standard_user_system_clock_core.cpp +++ b/src/core/hle/service/psc/time/clocks/standard_user_system_clock_core.cpp @@ -11,8 +11,8 @@ StandardUserSystemClockCore::StandardUserSystemClockCore( StandardNetworkSystemClockCore& network_clock) : SystemClockCore{local_clock.GetSteadyClock()}, m_system{system}, m_ctx{m_system, "Psc:StandardUserSystemClockCore"}, m_local_system_clock{local_clock}, - m_network_system_clock{network_clock}, - m_event{m_ctx.CreateEvent("Psc:StandardUserSystemClockCore:Event")} {} + m_network_system_clock{network_clock}, m_event{m_ctx.CreateEvent( + "Psc:StandardUserSystemClockCore:Event")} {} StandardUserSystemClockCore::~StandardUserSystemClockCore() { m_ctx.CloseEvent(m_event); diff --git a/src/core/hle/service/psc/time/common.cpp b/src/core/hle/service/psc/time/common.cpp index 1728c84499..a6d9f02ca3 100644 --- a/src/core/hle/service/psc/time/common.cpp +++ b/src/core/hle/service/psc/time/common.cpp @@ -6,8 +6,8 @@ namespace Service::PSC::Time { OperationEvent::OperationEvent(Core::System& system) - : m_ctx{system, "Time:OperationEvent"}, - m_event{m_ctx.CreateEvent("Time:OperationEvent:Event")} {} + : m_ctx{system, "Time:OperationEvent"}, m_event{ + m_ctx.CreateEvent("Time:OperationEvent:Event")} {} OperationEvent::~OperationEvent() { m_ctx.CloseEvent(m_event); diff --git a/src/core/hle/service/psc/time/manager.h b/src/core/hle/service/psc/time/manager.h index 57c682c690..62ded12475 100644 --- a/src/core/hle/service/psc/time/manager.h +++ b/src/core/hle/service/psc/time/manager.h @@ -29,8 +29,8 @@ public: m_standard_user_system_clock{m_system, m_standard_local_system_clock, m_standard_network_system_clock}, m_ephemeral_network_clock{m_tick_based_steady_clock}, m_shared_memory{m_system}, - m_power_state_request_manager{m_system}, - m_alarms{m_system, m_standard_steady_clock, m_power_state_request_manager}, + m_power_state_request_manager{m_system}, m_alarms{m_system, m_standard_steady_clock, + m_power_state_request_manager}, m_local_system_clock_context_writer{m_system, m_shared_memory}, m_network_system_clock_context_writer{m_system, m_shared_memory, m_standard_user_system_clock}, diff --git a/src/core/hle/service/psc/time/power_state_service.cpp b/src/core/hle/service/psc/time/power_state_service.cpp index 6fe03dc2e8..ab1d32c703 100644 --- a/src/core/hle/service/psc/time/power_state_service.cpp +++ b/src/core/hle/service/psc/time/power_state_service.cpp @@ -8,8 +8,8 @@ namespace Service::PSC::Time { IPowerStateRequestHandler::IPowerStateRequestHandler( Core::System& system_, PowerStateRequestManager& power_state_request_manager) - : ServiceFramework{system_, "time:p"}, m_system{system}, - m_power_state_request_manager{power_state_request_manager} { + : ServiceFramework{system_, "time:p"}, m_system{system}, m_power_state_request_manager{ + power_state_request_manager} { // clang-format off static const FunctionInfo functions[] = { {0, D<&IPowerStateRequestHandler::GetPowerStateRequestEventReadableHandle>, "GetPowerStateRequestEventReadableHandle"}, diff --git a/src/core/hle/service/psc/time/static.cpp b/src/core/hle/service/psc/time/static.cpp index 591849813e..9a0adb2955 100644 --- a/src/core/hle/service/psc/time/static.cpp +++ b/src/core/hle/service/psc/time/static.cpp @@ -37,8 +37,8 @@ StaticService::StaticService(Core::System& system_, StaticServiceSetupInfo setup m_user_system_clock{m_time->m_standard_user_system_clock}, m_network_system_clock{m_time->m_standard_network_system_clock}, m_time_zone{m_time->m_time_zone}, - m_ephemeral_network_clock{m_time->m_ephemeral_network_clock}, - m_shared_memory{m_time->m_shared_memory} { + m_ephemeral_network_clock{m_time->m_ephemeral_network_clock}, m_shared_memory{ + m_time->m_shared_memory} { // clang-format off static const FunctionInfo functions[] = { {0, D<&StaticService::GetStandardUserSystemClock>, "GetStandardUserSystemClock"}, diff --git a/src/core/hle/service/psc/time/steady_clock.cpp b/src/core/hle/service/psc/time/steady_clock.cpp index cb10024f2f..78dcf532ce 100644 --- a/src/core/hle/service/psc/time/steady_clock.cpp +++ b/src/core/hle/service/psc/time/steady_clock.cpp @@ -12,8 +12,8 @@ SteadyClock::SteadyClock(Core::System& system_, std::shared_ptr man bool can_write_steady_clock, bool can_write_uninitialized_clock) : ServiceFramework{system_, "ISteadyClock"}, m_system{system}, m_clock_core{manager->m_standard_steady_clock}, - m_can_write_steady_clock{can_write_steady_clock}, - m_can_write_uninitialized_clock{can_write_uninitialized_clock} { + m_can_write_steady_clock{can_write_steady_clock}, m_can_write_uninitialized_clock{ + can_write_uninitialized_clock} { // clang-format off static const FunctionInfo functions[] = { {0, D<&SteadyClock::GetCurrentTimePoint>, "GetCurrentTimePoint"}, diff --git a/src/core/hle/service/psc/time/system_clock.cpp b/src/core/hle/service/psc/time/system_clock.cpp index ed9f098045..9f841d8e04 100644 --- a/src/core/hle/service/psc/time/system_clock.cpp +++ b/src/core/hle/service/psc/time/system_clock.cpp @@ -11,8 +11,8 @@ namespace Service::PSC::Time { SystemClock::SystemClock(Core::System& system_, SystemClockCore& clock_core, bool can_write_clock, bool can_write_uninitialized_clock) : ServiceFramework{system_, "ISystemClock"}, m_system{system}, m_clock_core{clock_core}, - m_can_write_clock{can_write_clock}, - m_can_write_uninitialized_clock{can_write_uninitialized_clock} { + m_can_write_clock{can_write_clock}, m_can_write_uninitialized_clock{ + can_write_uninitialized_clock} { // clang-format off static const FunctionInfo functions[] = { {0, D<&SystemClock::GetCurrentTime>, "GetCurrentTime"}, diff --git a/src/core/hle/service/psc/time/time_zone_service.cpp b/src/core/hle/service/psc/time/time_zone_service.cpp index 92e695a326..9e0674f275 100644 --- a/src/core/hle/service/psc/time/time_zone_service.cpp +++ b/src/core/hle/service/psc/time/time_zone_service.cpp @@ -13,8 +13,8 @@ namespace Service::PSC::Time { TimeZoneService::TimeZoneService(Core::System& system_, StandardSteadyClockCore& clock_core, TimeZone& time_zone, bool can_write_timezone_device_location) : ServiceFramework{system_, "ITimeZoneService"}, m_system{system}, m_clock_core{clock_core}, - m_time_zone{time_zone}, - m_can_write_timezone_device_location{can_write_timezone_device_location} { + m_time_zone{time_zone}, m_can_write_timezone_device_location{ + can_write_timezone_device_location} { // clang-format off static const FunctionInfo functions[] = { {0, D<&TimeZoneService::GetDeviceLocationName>, "GetDeviceLocationName"}, diff --git a/src/core/hle/service/sm/sm.cpp b/src/core/hle/service/sm/sm.cpp index 1373da17fa..1095dcf6c3 100644 --- a/src/core/hle/service/sm/sm.cpp +++ b/src/core/hle/service/sm/sm.cpp @@ -251,8 +251,8 @@ void SM::UnregisterService(HLERequestContext& ctx) { } SM::SM(ServiceManager& service_manager_, Core::System& system_) - : ServiceFramework{system_, "sm:", 4}, service_manager{service_manager_}, - kernel{system_.Kernel()} { + : ServiceFramework{system_, "sm:", 4}, + service_manager{service_manager_}, kernel{system_.Kernel()} { RegisterHandlers({ {0, &SM::Initialize, "Initialize"}, {1, &SM::GetServiceCmif, "GetService"}, diff --git a/src/core/hle/service/vi/application_display_service.cpp b/src/core/hle/service/vi/application_display_service.cpp index 2cc38c24d3..6b0bcb5362 100644 --- a/src/core/hle/service/vi/application_display_service.cpp +++ b/src/core/hle/service/vi/application_display_service.cpp @@ -15,8 +15,8 @@ namespace Service::VI { IApplicationDisplayService::IApplicationDisplayService(Core::System& system_, std::shared_ptr container) - : ServiceFramework{system_, "IApplicationDisplayService"}, m_container{std::move(container)}, - m_context{system, "IApplicationDisplayService"} { + : ServiceFramework{system_, "IApplicationDisplayService"}, + m_container{std::move(container)}, m_context{system, "IApplicationDisplayService"} { // clang-format off static const FunctionInfo functions[] = { {100, C<&IApplicationDisplayService::GetRelayService>, "GetRelayService"}, diff --git a/src/core/memory/cheat_engine.cpp b/src/core/memory/cheat_engine.cpp index ae651631d6..d8921e5658 100644 --- a/src/core/memory/cheat_engine.cpp +++ b/src/core/memory/cheat_engine.cpp @@ -218,8 +218,8 @@ std::vector TextCheatParser::Parse(std::string_view data) const { CheatEngine::CheatEngine(System& system_, std::vector cheats_, const std::array& build_id_) - : vm{std::make_unique(system_, metadata)}, cheats(std::move(cheats_)), - core_timing{system_.CoreTiming()}, system{system_} { + : vm{std::make_unique(system_, metadata)}, + cheats(std::move(cheats_)), core_timing{system_.CoreTiming()}, system{system_} { metadata.main_nso_build_id = build_id_; } diff --git a/src/core/telemetry_session.cpp b/src/core/telemetry_session.cpp new file mode 100644 index 0000000000..4097c421e1 --- /dev/null +++ b/src/core/telemetry_session.cpp @@ -0,0 +1,294 @@ +// SPDX-FileCopyrightText: 2017 Citra Emulator Project & 2024 suyu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include + +#include +#include + +#include "common/assert.h" +#include "common/common_types.h" +#include "common/fs/file.h" +#include "common/fs/fs.h" +#include "common/fs/path_util.h" +#include "common/logging/log.h" + +#include "common/settings.h" +#include "common/settings_enums.h" +#include "core/file_sys/control_metadata.h" +#include "core/file_sys/patch_manager.h" +#include "core/loader/loader.h" +#include "core/telemetry_session.h" + +#ifdef ENABLE_WEB_SERVICE +#include "web_service/telemetry_json.h" +#include "web_service/verify_login.h" +#endif + +namespace Core { + +namespace Telemetry = Common::Telemetry; + +static u64 GenerateTelemetryId() { + u64 telemetry_id{}; + + mbedtls_entropy_context entropy; + mbedtls_entropy_init(&entropy); + mbedtls_ctr_drbg_context ctr_drbg; + static constexpr std::array personalization{{"suyu Telemetry ID"}}; + + mbedtls_ctr_drbg_init(&ctr_drbg); + ASSERT(mbedtls_ctr_drbg_seed(&ctr_drbg, mbedtls_entropy_func, &entropy, + reinterpret_cast(personalization.data()), + personalization.size()) == 0); + ASSERT(mbedtls_ctr_drbg_random(&ctr_drbg, reinterpret_cast(&telemetry_id), + sizeof(u64)) == 0); + + mbedtls_ctr_drbg_free(&ctr_drbg); + mbedtls_entropy_free(&entropy); + + return telemetry_id; +} + +static const char* TranslateRenderer(Settings::RendererBackend backend) { + switch (backend) { + case Settings::RendererBackend::OpenGL: + return "OpenGL"; + case Settings::RendererBackend::Vulkan: + return "Vulkan"; + case Settings::RendererBackend::Null: + return "Null"; + } + return "Unknown"; +} + +static const char* TranslateGPUAccuracyLevel(Settings::GpuAccuracy backend) { + switch (backend) { + case Settings::GpuAccuracy::Normal: + return "Normal"; + case Settings::GpuAccuracy::High: + return "High"; + case Settings::GpuAccuracy::Extreme: + return "Extreme"; + } + return "Unknown"; +} + +static const char* TranslateNvdecEmulation(Settings::NvdecEmulation backend) { + switch (backend) { + case Settings::NvdecEmulation::Off: + return "Off"; + case Settings::NvdecEmulation::Cpu: + return "CPU"; + case Settings::NvdecEmulation::Gpu: + return "GPU"; + } + return "Unknown"; +} + +static constexpr const char* TranslateVSyncMode(Settings::VSyncMode mode) { + switch (mode) { + case Settings::VSyncMode::Immediate: + return "Immediate"; + case Settings::VSyncMode::Mailbox: + return "Mailbox"; + case Settings::VSyncMode::Fifo: + return "FIFO"; + case Settings::VSyncMode::FifoRelaxed: + return "FIFO Relaxed"; + } + return "Unknown"; +} + +static constexpr const char* TranslateASTCDecodeMode(Settings::AstcDecodeMode mode) { + switch (mode) { + case Settings::AstcDecodeMode::Cpu: + return "CPU"; + case Settings::AstcDecodeMode::Gpu: + return "GPU"; + case Settings::AstcDecodeMode::CpuAsynchronous: + return "CPU Asynchronous"; + } + return "Unknown"; +} + +u64 GetTelemetryId() { + u64 telemetry_id{}; + const auto filename = Common::FS::GetSuyuPath(Common::FS::SuyuPath::ConfigDir) / "telemetry_id"; + + bool generate_new_id = !Common::FS::Exists(filename); + + if (!generate_new_id) { + Common::FS::IOFile file{filename, Common::FS::FileAccessMode::Read, + Common::FS::FileType::BinaryFile}; + + if (!file.IsOpen()) { + LOG_ERROR(Core, "failed to open telemetry_id: {}", + Common::FS::PathToUTF8String(filename)); + return {}; + } + + if (!file.ReadObject(telemetry_id) || telemetry_id == 0) { + LOG_ERROR(Frontend, "telemetry_id is 0. Generating a new one.", telemetry_id); + generate_new_id = true; + } + } + + if (generate_new_id) { + Common::FS::IOFile file{filename, Common::FS::FileAccessMode::Write, + Common::FS::FileType::BinaryFile}; + + if (!file.IsOpen()) { + LOG_ERROR(Core, "failed to open telemetry_id: {}", + Common::FS::PathToUTF8String(filename)); + return {}; + } + + telemetry_id = GenerateTelemetryId(); + + if (!file.WriteObject(telemetry_id)) { + LOG_ERROR(Core, "Failed to write telemetry_id to file."); + } + } + + return telemetry_id; +} + +u64 RegenerateTelemetryId() { + const u64 new_telemetry_id{GenerateTelemetryId()}; + const auto filename = Common::FS::GetSuyuPath(Common::FS::SuyuPath::ConfigDir) / "telemetry_id"; + + Common::FS::IOFile file{filename, Common::FS::FileAccessMode::Write, + Common::FS::FileType::BinaryFile}; + + if (!file.IsOpen()) { + LOG_ERROR(Core, "failed to open telemetry_id: {}", Common::FS::PathToUTF8String(filename)); + return {}; + } + + if (!file.WriteObject(new_telemetry_id)) { + LOG_ERROR(Core, "Failed to write telemetry_id to file."); + } + + return new_telemetry_id; +} + +bool VerifyLogin(const std::string& username, const std::string& token) { +#ifdef ENABLE_WEB_SERVICE + return WebService::VerifyLogin(Settings::values.web_api_url.GetValue(), username, token); +#else + return false; +#endif +} + +TelemetrySession::TelemetrySession() = default; + +TelemetrySession::~TelemetrySession() { + // Log one-time session end information + const s64 shutdown_time{std::chrono::duration_cast( + std::chrono::system_clock::now().time_since_epoch()) + .count()}; + AddField(Telemetry::FieldType::Session, "Shutdown_Time", shutdown_time); + +#ifdef ENABLE_WEB_SERVICE + auto backend = std::make_unique( + Settings::values.web_api_url.GetValue(), Settings::values.suyu_username.GetValue(), + Settings::values.suyu_token.GetValue()); +#else + auto backend = std::make_unique(); +#endif + + // Complete the session, submitting to the web service backend if necessary + field_collection.Accept(*backend); + if (Settings::values.enable_telemetry) { + backend->Complete(); + } +} + +void TelemetrySession::AddInitialInfo(Loader::AppLoader& app_loader, + const Service::FileSystem::FileSystemController& fsc, + const FileSys::ContentProvider& content_provider) { + // Log one-time top-level information + AddField(Telemetry::FieldType::None, "TelemetryId", GetTelemetryId()); + + // Log one-time session start information + const s64 init_time{std::chrono::duration_cast( + std::chrono::system_clock::now().time_since_epoch()) + .count()}; + AddField(Telemetry::FieldType::Session, "Init_Time", init_time); + + u64 program_id{}; + const Loader::ResultStatus res{app_loader.ReadProgramId(program_id)}; + if (res == Loader::ResultStatus::Success) { + const std::string formatted_program_id{fmt::format("{:016X}", program_id)}; + AddField(Telemetry::FieldType::Session, "ProgramId", formatted_program_id); + + std::string name; + app_loader.ReadTitle(name); + + if (name.empty()) { + const auto metadata = [&content_provider, &fsc, program_id] { + const FileSys::PatchManager pm{program_id, fsc, content_provider}; + return pm.GetControlMetadata(); + }(); + if (metadata.first != nullptr) { + name = metadata.first->GetApplicationName(); + } + } + + if (!name.empty()) { + AddField(Telemetry::FieldType::Session, "ProgramName", name); + } + } + + AddField(Telemetry::FieldType::Session, "ProgramFormat", + static_cast(app_loader.GetFileType())); + + // Log application information + Telemetry::AppendBuildInfo(field_collection); + + // Log user system information + Telemetry::AppendCPUInfo(field_collection); + Telemetry::AppendOSInfo(field_collection); + + // Log user configuration information + constexpr auto field_type = Telemetry::FieldType::UserConfig; + AddField(field_type, "Audio_SinkId", + Settings::CanonicalizeEnum(Settings::values.sink_id.GetValue())); + AddField(field_type, "Core_UseMultiCore", Settings::values.use_multi_core.GetValue()); + AddField(field_type, "Renderer_Backend", + TranslateRenderer(Settings::values.renderer_backend.GetValue())); + AddField(field_type, "Renderer_UseSpeedLimit", Settings::values.use_speed_limit.GetValue()); + AddField(field_type, "Renderer_SpeedLimit", Settings::values.speed_limit.GetValue()); + AddField(field_type, "Renderer_UseDiskShaderCache", + Settings::values.use_disk_shader_cache.GetValue()); + AddField(field_type, "Renderer_GPUAccuracyLevel", + TranslateGPUAccuracyLevel(Settings::values.gpu_accuracy.GetValue())); + AddField(field_type, "Renderer_UseAsynchronousGpuEmulation", + Settings::values.use_asynchronous_gpu_emulation.GetValue()); + AddField(field_type, "Renderer_NvdecEmulation", + TranslateNvdecEmulation(Settings::values.nvdec_emulation.GetValue())); + AddField(field_type, "Renderer_AccelerateASTC", + TranslateASTCDecodeMode(Settings::values.accelerate_astc.GetValue())); + AddField(field_type, "Renderer_UseVsync", + TranslateVSyncMode(Settings::values.vsync_mode.GetValue())); + AddField(field_type, "Renderer_ShaderBackend", + static_cast(Settings::values.shader_backend.GetValue())); + AddField(field_type, "Renderer_UseAsynchronousShaders", + Settings::values.use_asynchronous_shaders.GetValue()); + AddField(field_type, "System_UseDockedMode", Settings::IsDockedMode()); +} + +bool TelemetrySession::SubmitTestcase() { +#ifdef ENABLE_WEB_SERVICE + auto backend = std::make_unique( + Settings::values.web_api_url.GetValue(), Settings::values.suyu_username.GetValue(), + Settings::values.suyu_token.GetValue()); + field_collection.Accept(*backend); + return backend->SubmitTestcase(); +#else + return false; +#endif +} + +} // namespace Core diff --git a/src/core/telemetry_session.h b/src/core/telemetry_session.h new file mode 100644 index 0000000000..71437ba1de --- /dev/null +++ b/src/core/telemetry_session.h @@ -0,0 +1,101 @@ +// SPDX-FileCopyrightText: 2017 Citra Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include +#include "common/telemetry.h" + +namespace FileSys { +class ContentProvider; +} + +namespace Loader { +class AppLoader; +} + +namespace Service::FileSystem { +class FileSystemController; +} + +namespace Core { + +/** + * Instruments telemetry for this emulation session. Creates a new set of telemetry fields on each + * session, logging any one-time fields. Interfaces with the telemetry backend used for submitting + * data to the web service. Submits session data on close. + */ +class TelemetrySession { +public: + explicit TelemetrySession(); + ~TelemetrySession(); + + TelemetrySession(const TelemetrySession&) = delete; + TelemetrySession& operator=(const TelemetrySession&) = delete; + + TelemetrySession(TelemetrySession&&) = delete; + TelemetrySession& operator=(TelemetrySession&&) = delete; + + /** + * Adds the initial telemetry info necessary when starting up a title. + * + * This includes information such as: + * - Telemetry ID + * - Initialization time + * - Title ID + * - Title name + * - Title file format + * - Miscellaneous settings values. + * + * @param app_loader The application loader to use to retrieve + * title-specific information. + * @param fsc Filesystem controller to use to retrieve info. + * @param content_provider Content provider to use to retrieve info. + */ + void AddInitialInfo(Loader::AppLoader& app_loader, + const Service::FileSystem::FileSystemController& fsc, + const FileSys::ContentProvider& content_provider); + + /** + * Wrapper around the Telemetry::FieldCollection::AddField method. + * @param type Type of the field to add. + * @param name Name of the field to add. + * @param value Value for the field to add. + */ + template + void AddField(Common::Telemetry::FieldType type, const char* name, T value) { + field_collection.AddField(type, name, std::move(value)); + } + + /** + * Submits a Testcase. + * @returns A bool indicating whether the submission succeeded + */ + bool SubmitTestcase(); + +private: + /// Tracks all added fields for the session + Common::Telemetry::FieldCollection field_collection; +}; + +/** + * Gets TelemetryId, a unique identifier used for the user's telemetry sessions. + * @returns The current TelemetryId for the session. + */ +u64 GetTelemetryId(); + +/** + * Regenerates TelemetryId, a unique identifier used for the user's telemetry sessions. + * @returns The new TelemetryId that was generated. + */ +u64 RegenerateTelemetryId(); + +/** + * Verifies the username and token. + * @param username suyu username to use for authentication. + * @param token suyu token to use for authentication. + * @returns Future with bool indicating whether the verification succeeded + */ +bool VerifyLogin(const std::string& username, const std::string& token); + +} // namespace Core diff --git a/src/input_common/input_poller.cpp b/src/input_common/input_poller.cpp index f7e6986938..0cc0fecb34 100644 --- a/src/input_common/input_poller.cpp +++ b/src/input_common/input_poller.cpp @@ -148,8 +148,8 @@ public: Common::Input::AnalogProperties properties_y_, InputEngine* input_engine_) : identifier(identifier_), axis_x(axis_x_), axis_y(axis_y_), properties_x(properties_x_), - properties_y(properties_y_), input_engine(input_engine_), - invert_axis_y{input_engine_->GetEngineName() == "sdl"} { + properties_y(properties_y_), + input_engine(input_engine_), invert_axis_y{input_engine_->GetEngineName() == "sdl"} { UpdateCallback engine_callback{[this]() { OnChange(); }}; const InputIdentifier x_input_identifier{ .identifier = identifier, diff --git a/src/shader_recompiler/frontend/maxwell/control_flow.cpp b/src/shader_recompiler/frontend/maxwell/control_flow.cpp index e0d6315754..dce414cb47 100644 --- a/src/shader_recompiler/frontend/maxwell/control_flow.cpp +++ b/src/shader_recompiler/frontend/maxwell/control_flow.cpp @@ -186,8 +186,8 @@ Function::Function(ObjectPool& block_pool, Location start_address) CFG::CFG(Environment& env_, ObjectPool& block_pool_, Location start_address, bool exits_to_dispatcher_) - : env{env_}, block_pool{block_pool_}, program_start{start_address}, - exits_to_dispatcher{exits_to_dispatcher_} { + : env{env_}, block_pool{block_pool_}, program_start{start_address}, exits_to_dispatcher{ + exits_to_dispatcher_} { if (exits_to_dispatcher) { dispatch_block = block_pool.Create(Block{}); dispatch_block->begin = {}; diff --git a/src/shader_recompiler/frontend/maxwell/structured_control_flow.cpp b/src/shader_recompiler/frontend/maxwell/structured_control_flow.cpp index 93538fa4cf..80c90fe6ac 100644 --- a/src/shader_recompiler/frontend/maxwell/structured_control_flow.cpp +++ b/src/shader_recompiler/frontend/maxwell/structured_control_flow.cpp @@ -112,8 +112,8 @@ struct Statement : ListBaseHook { Statement(SetVariable, u32 id_, Statement* op_, Statement* up_) : op{op_}, id{id_}, up{up_}, type{StatementType::SetVariable} {} Statement(SetIndirectBranchVariable, IR::Reg branch_reg_, s32 branch_offset_, Statement* up_) - : branch_offset{branch_offset_}, branch_reg{branch_reg_}, up{up_}, - type{StatementType::SetIndirectBranchVariable} {} + : branch_offset{branch_offset_}, + branch_reg{branch_reg_}, up{up_}, type{StatementType::SetIndirectBranchVariable} {} Statement(Variable, u32 id_, Statement* up_) : id{id_}, up{up_}, type{StatementType::Variable} {} Statement(IndirectBranchCond, u32 location_, Statement* up_) diff --git a/src/suyu/applets/qt_web_browser.cpp b/src/suyu/applets/qt_web_browser.cpp index 7c06bb6104..a7d7a5ce65 100644 --- a/src/suyu/applets/qt_web_browser.cpp +++ b/src/suyu/applets/qt_web_browser.cpp @@ -54,8 +54,8 @@ QtNXWebEngineView::QtNXWebEngineView(QWidget* parent, Core::System& system, : QWebEngineView(parent), input_subsystem{input_subsystem_}, url_interceptor(std::make_unique()), input_interpreter(std::make_unique(system)), - default_profile{QWebEngineProfile::defaultProfile()}, - global_settings{default_profile->settings()} { + default_profile{QWebEngineProfile::defaultProfile()}, global_settings{ + default_profile->settings()} { default_profile->setPersistentStoragePath(QString::fromStdString(Common::FS::PathToUTF8String( Common::FS::GetSuyuPath(Common::FS::SuyuPath::SuyuDir) / "qtwebengine"))); diff --git a/src/suyu/bootmanager.cpp b/src/suyu/bootmanager.cpp index 470455692b..3c536e3072 100644 --- a/src/suyu/bootmanager.cpp +++ b/src/suyu/bootmanager.cpp @@ -284,8 +284,8 @@ struct NullRenderWidget : public RenderWidget { GRenderWindow::GRenderWindow(GMainWindow* parent, EmuThread* emu_thread_, std::shared_ptr input_subsystem_, Core::System& system_) - : QWidget(parent), emu_thread(emu_thread_), input_subsystem{std::move(input_subsystem_)}, - system{system_} { + : QWidget(parent), + emu_thread(emu_thread_), input_subsystem{std::move(input_subsystem_)}, system{system_} { setWindowTitle(QStringLiteral("suyu %1 | %2-%3") .arg(QString::fromUtf8(Common::g_build_name), QString::fromUtf8(Common::g_scm_branch), diff --git a/src/suyu/compatdb.cpp b/src/suyu/compatdb.cpp index eacc49ea58..8f63a4b555 100644 --- a/src/suyu/compatdb.cpp +++ b/src/suyu/compatdb.cpp @@ -6,12 +6,14 @@ #include #include #include "common/logging/log.h" +#include "common/telemetry.h" +#include "core/telemetry_session.h" #include "suyu/compatdb.h" #include "ui_compatdb.h" -CompatDB::CompatDB(QWidget* parent) +CompatDB::CompatDB(Core::TelemetrySession& telemetry_session_, QWidget* parent) : QWizard(parent, Qt::WindowTitleHint | Qt::WindowCloseButtonHint | Qt::WindowSystemMenuHint), - ui{std::make_unique()} { + ui{std::make_unique()}, telemetry_session{telemetry_session_} { ui->setupUi(this); connect(ui->radioButton_GameBoot_Yes, &QRadioButton::clicked, this, &CompatDB::EnableNext); @@ -112,10 +114,15 @@ void CompatDB::Submit() { case CompatDBPage::Final: back(); LOG_INFO(Frontend, "Compatibility Rating: {}", compatibility); + telemetry_session.AddField(Common::Telemetry::FieldType::UserFeedback, "Compatibility", + compatibility); button(NextButton)->setEnabled(false); button(NextButton)->setText(tr("Submitting")); button(CancelButton)->setVisible(false); + + testcase_watcher.setFuture( + QtConcurrent::run([this] { return telemetry_session.SubmitTestcase(); })); break; default: LOG_ERROR(Frontend, "Unexpected page: {}", currentId()); diff --git a/src/suyu/compatdb.h b/src/suyu/compatdb.h index 4e788d09c4..37e11278b9 100644 --- a/src/suyu/compatdb.h +++ b/src/suyu/compatdb.h @@ -6,6 +6,7 @@ #include #include #include +#include "core/telemetry_session.h" namespace Ui { class CompatDB; @@ -24,7 +25,7 @@ class CompatDB : public QWizard { Q_OBJECT public: - explicit CompatDB(QWidget* parent = nullptr); + explicit CompatDB(Core::TelemetrySession& telemetry_session_, QWidget* parent = nullptr); ~CompatDB(); int nextId() const override; @@ -37,4 +38,6 @@ private: CompatibilityStatus CalculateCompatibility() const; void OnTestcaseSubmitted(); void EnableNext(); + + Core::TelemetrySession& telemetry_session; }; diff --git a/src/suyu/configuration/configure_dialog.cpp b/src/suyu/configuration/configure_dialog.cpp index d81edaa3b2..0d6ac9ad82 100644 --- a/src/suyu/configuration/configure_dialog.cpp +++ b/src/suyu/configuration/configure_dialog.cpp @@ -32,9 +32,9 @@ ConfigureDialog::ConfigureDialog(QWidget* parent, HotkeyRegistry& registry_, InputCommon::InputSubsystem* input_subsystem, std::vector& vk_device_records, Core::System& system_, bool enable_web_config) - : QDialog(parent), ui{std::make_unique()}, registry(registry_), - system{system_}, - builder{std::make_unique(this, !system_.IsPoweredOn())}, + : QDialog(parent), ui{std::make_unique()}, + registry(registry_), system{system_}, builder{std::make_unique( + this, !system_.IsPoweredOn())}, applets_tab{std::make_unique(system_, nullptr, *builder, this)}, audio_tab{std::make_unique(system_, nullptr, *builder, this)}, cpu_tab{std::make_unique(system_, nullptr, *builder, this)}, diff --git a/src/suyu/configuration/configure_input_player.cpp b/src/suyu/configuration/configure_input_player.cpp index ec0c09790e..1fd176e987 100644 --- a/src/suyu/configuration/configure_input_player.cpp +++ b/src/suyu/configuration/configure_input_player.cpp @@ -293,11 +293,11 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i InputCommon::InputSubsystem* input_subsystem_, InputProfiles* profiles_, Core::HID::HIDCore& hid_core_, bool is_powered_on_, bool debug_) - : QWidget(parent), ui(std::make_unique()), - player_index{player_index_}, debug{debug_}, is_powered_on{is_powered_on_}, - input_subsystem{input_subsystem_}, profiles(profiles_), - timeout_timer(std::make_unique()), poll_timer(std::make_unique()), - bottom_row{bottom_row_}, hid_core{hid_core_} { + : QWidget(parent), + ui(std::make_unique()), player_index{player_index_}, debug{debug_}, + is_powered_on{is_powered_on_}, input_subsystem{input_subsystem_}, profiles(profiles_), + timeout_timer(std::make_unique()), + poll_timer(std::make_unique()), bottom_row{bottom_row_}, hid_core{hid_core_} { if (player_index == 0) { auto* emulated_controller_p1 = hid_core.GetEmulatedController(Core::HID::NpadIdType::Player1); diff --git a/src/suyu/configuration/configure_mouse_panning.ui b/src/suyu/configuration/configure_mouse_panning.ui index d6072f4527..84fb7ee809 100644 --- a/src/suyu/configuration/configure_mouse_panning.ui +++ b/src/suyu/configuration/configure_mouse_panning.ui @@ -43,7 +43,7 @@ 1 - 200 + 100 50 @@ -69,7 +69,7 @@ 1 - 200 + 100 50 diff --git a/src/suyu/configuration/configure_per_game.cpp b/src/suyu/configuration/configure_per_game.cpp index 7caff1035b..2cadba72ff 100644 --- a/src/suyu/configuration/configure_per_game.cpp +++ b/src/suyu/configuration/configure_per_game.cpp @@ -17,17 +17,14 @@ #include #include "common/fs/fs_util.h" -#include "common/hex_util.h" #include "common/settings_enums.h" #include "common/settings_input.h" #include "configuration/shared_widget.h" #include "core/core.h" #include "core/file_sys/control_metadata.h" -#include "core/file_sys/ips_layer.h" #include "core/file_sys/patch_manager.h" #include "core/file_sys/xts_archive.h" #include "core/loader/loader.h" -#include "core/loader/nso.h" #include "frontend_common/config.h" #include "suyu/configuration/configuration_shared.h" #include "suyu/configuration/configure_audio.h" @@ -47,12 +44,10 @@ ConfigurePerGame::ConfigurePerGame(QWidget* parent, u64 title_id_, const std::string& file_name, std::vector& vk_device_records, Core::System& system_) - : QDialog(parent), ui(std::make_unique()), - pm{title_id_, system_.GetFileSystemController(), system_.GetContentProvider()}, - title_id{title_id_}, system{system_}, + : QDialog(parent), + ui(std::make_unique()), title_id{title_id_}, system{system_}, builder{std::make_unique(this, !system_.IsPoweredOn())}, tab_group{std::make_shared>()} { - const auto file_path = std::filesystem::path(Common::FS::ToU8String(file_name)); const auto config_file_name = title_id == 0 ? Common::FS::PathToUTF8String(file_path.filename()) : fmt::format("{:016X}", title_id); @@ -146,14 +141,6 @@ void ConfigurePerGame::LoadFromFile(FileSys::VirtualFile file_) { LoadConfiguration(); } -std::string ConfigurePerGame::GetBuildID() { - LOG_INFO(Core, "{}", file->GetExtension()); - - // https://github.com/Ryujinx/Ryujinx/blob/master/src/Ryujinx.UI.Common/App/ApplicationData.cs#L71 - - return "Invalid File"; -} - void ConfigurePerGame::LoadConfiguration() { if (file == nullptr) { return; @@ -161,14 +148,13 @@ void ConfigurePerGame::LoadConfiguration() { addons_tab->LoadFromFile(file); - const auto control = pm.GetControlMetadata(); - const auto loader = Loader::GetLoader(system, file); - ui->display_title_id->setText( QStringLiteral("%1").arg(title_id, 16, 16, QLatin1Char{'0'}).toUpper()); - // TODO: Should get proper build id for UI - // ui->display_build_id->setText(QString::fromStdString(GetBuildID())); + const FileSys::PatchManager pm{title_id, system.GetFileSystemController(), + system.GetContentProvider()}; + const auto control = pm.GetControlMetadata(); + const auto loader = Loader::GetLoader(system, file); if (control.first != nullptr) { ui->display_version->setText(QString::fromStdString(control.first->GetVersionString())); diff --git a/src/suyu/configuration/configure_per_game.h b/src/suyu/configuration/configure_per_game.h index 7e1aff28db..3fa03b1d88 100644 --- a/src/suyu/configuration/configure_per_game.h +++ b/src/suyu/configuration/configure_per_game.h @@ -11,7 +11,6 @@ #include #include "configuration/shared_widget.h" -#include "core/file_sys/patch_manager.h" #include "core/file_sys/vfs/vfs_types.h" #include "frontend_common/config.h" #include "suyu/configuration/configuration_shared.h" @@ -69,11 +68,8 @@ private: void LoadConfiguration(); - std::string GetBuildID(); - std::unique_ptr ui; FileSys::VirtualFile file; - FileSys::PatchManager pm; u64 title_id; QGraphicsScene* scene; diff --git a/src/suyu/configuration/configure_per_game.ui b/src/suyu/configuration/configure_per_game.ui index 712705e082..99ba2fd180 100644 --- a/src/suyu/configuration/configure_per_game.ui +++ b/src/suyu/configuration/configure_per_game.ui @@ -67,18 +67,8 @@ - - - - true - - - true - - - - + true @@ -87,58 +77,6 @@ - - - - Size - - - - - - - Filename - - - - - - - Title ID - - - - - - - Name - - - - - - - true - - - true - - - - - - - Developer - - - - - - - Format - - - @@ -149,14 +87,31 @@ - - + + - Version + Name - + + + + Title ID + + + + + + + true + + + true + + + + true @@ -166,6 +121,23 @@ + + + + true + + + true + + + + + + + Filename + + + @@ -176,8 +148,8 @@ - - + + true @@ -185,22 +157,35 @@ true - - - + + + + + Size + + + + + + + Developer + + + diff --git a/src/suyu/configuration/configure_web.cpp b/src/suyu/configuration/configure_web.cpp index 3b55bb9763..196f5001cf 100644 --- a/src/suyu/configuration/configure_web.cpp +++ b/src/suyu/configuration/configure_web.cpp @@ -5,6 +5,7 @@ #include #include #include "common/settings.h" +#include "core/telemetry_session.h" #include "suyu/configuration/configure_web.h" #include "suyu/uisettings.h" #include "ui_configure_web.h" @@ -37,6 +38,8 @@ static std::string TokenFromDisplayToken(const std::string& display_token) { ConfigureWeb::ConfigureWeb(QWidget* parent) : QWidget(parent), ui(std::make_unique()) { ui->setupUi(this); + connect(ui->button_regenerate_telemetry_id, &QPushButton::clicked, this, + &ConfigureWeb::RefreshTelemetryID); connect(ui->button_verify_login, &QPushButton::clicked, this, &ConfigureWeb::VerifyLogin); connect(&verify_watcher, &QFutureWatcher::finished, this, &ConfigureWeb::OnLoginVerified); @@ -61,6 +64,10 @@ void ConfigureWeb::changeEvent(QEvent* event) { void ConfigureWeb::RetranslateUI() { ui->retranslateUi(this); + ui->telemetry_learn_more->setText( + tr("Learn more")); + ui->web_signup_link->setText( tr("Sign up")); @@ -68,11 +75,15 @@ void ConfigureWeb::RetranslateUI() { ui->web_token_info_link->setText( tr("What is my token?")); + + ui->label_telemetry_id->setText( + tr("Telemetry ID: 0x%1").arg(QString::number(Core::GetTelemetryId(), 16).toUpper())); } void ConfigureWeb::SetConfiguration() { ui->web_credentials_disclaimer->setWordWrap(true); + ui->telemetry_learn_more->setOpenExternalLinks(true); ui->web_signup_link->setOpenExternalLinks(true); ui->web_token_info_link->setOpenExternalLinks(true); @@ -82,6 +93,7 @@ void ConfigureWeb::SetConfiguration() { ui->username->setText(QString::fromStdString(Settings::values.suyu_username.GetValue())); } + ui->toggle_telemetry->setChecked(Settings::values.enable_telemetry.GetValue()); ui->edit_token->setText(QString::fromStdString(GenerateDisplayToken( Settings::values.suyu_username.GetValue(), Settings::values.suyu_token.GetValue()))); @@ -94,6 +106,7 @@ void ConfigureWeb::SetConfiguration() { } void ConfigureWeb::ApplyConfiguration() { + Settings::values.enable_telemetry = ui->toggle_telemetry->isChecked(); UISettings::values.enable_discord_presence = ui->toggle_discordrpc->isChecked(); if (user_verified) { Settings::values.suyu_username = @@ -106,6 +119,12 @@ void ConfigureWeb::ApplyConfiguration() { } } +void ConfigureWeb::RefreshTelemetryID() { + const u64 new_telemetry_id{Core::RegenerateTelemetryId()}; + ui->label_telemetry_id->setText( + tr("Telemetry ID: 0x%1").arg(QString::number(new_telemetry_id, 16).toUpper())); +} + void ConfigureWeb::OnLoginChanged() { if (ui->edit_token->text().isEmpty()) { user_verified = true; @@ -131,12 +150,7 @@ void ConfigureWeb::VerifyLogin() { verify_watcher.setFuture(QtConcurrent::run( [username = UsernameFromDisplayToken(ui->edit_token->text().toStdString()), token = TokenFromDisplayToken(ui->edit_token->text().toStdString())] { -#ifdef ENABLE_WEB_SERVICE - return WebService::VerifyLogin(Settings::values.web_api_url.GetValue(), username, - token); -#else - return false; -#endif + return Core::VerifyLogin(username, token); })); } diff --git a/src/suyu/configuration/configure_web.h b/src/suyu/configuration/configure_web.h index cadbc0fa0a..03feb55f85 100644 --- a/src/suyu/configuration/configure_web.h +++ b/src/suyu/configuration/configure_web.h @@ -25,6 +25,7 @@ private: void changeEvent(QEvent* event) override; void RetranslateUI(); + void RefreshTelemetryID(); void OnLoginChanged(); void VerifyLogin(); void OnLoginVerified(); diff --git a/src/suyu/configuration/configure_web.ui b/src/suyu/configuration/configure_web.ui index 2cdcd1bc60..35779e8285 100644 --- a/src/suyu/configuration/configure_web.ui +++ b/src/suyu/configuration/configure_web.ui @@ -125,6 +125,56 @@ + + + + Telemetry + + + + + + Share anonymous usage data with the suyu team + + + + + + + Learn more + + + + + + + + + Telemetry ID: + + + + + + + + 0 + 0 + + + + Qt::RightToLeft + + + Regenerate + + + + + + + + diff --git a/src/suyu/configuration/shared_widget.cpp b/src/suyu/configuration/shared_widget.cpp index 1e77bb62c3..76a6b417cd 100644 --- a/src/suyu/configuration/shared_widget.cpp +++ b/src/suyu/configuration/shared_widget.cpp @@ -766,8 +766,8 @@ Widget::Widget(Settings::BasicSetting* setting_, const TranslationMap& translati Builder::Builder(QWidget* parent_, bool runtime_lock_) : translations{InitializeTranslations(parent_)}, - combobox_translations{ComboboxEnumeration(parent_)}, parent{parent_}, - runtime_lock{runtime_lock_} {} + combobox_translations{ComboboxEnumeration(parent_)}, parent{parent_}, runtime_lock{ + runtime_lock_} {} Builder::~Builder() = default; diff --git a/src/suyu/game_list.cpp b/src/suyu/game_list.cpp index b12cdb7bf9..478838bc84 100644 --- a/src/suyu/game_list.cpp +++ b/src/suyu/game_list.cpp @@ -3,6 +3,7 @@ // Modified by palfaiate on <2024/03/07> +#include #include #include #include @@ -383,17 +384,6 @@ GameList::~GameList() { UnloadController(); } -void GameList::ClearList() { - // Clear all items - item_model->setRowCount(0); - - // Notify a reload is pending - UISettings::values.is_game_list_reload_pending.exchange(true); - UISettings::values.is_game_list_reload_pending.notify_all(); - - // Load stuff back up -} - void GameList::SetFilterFocus() { if (tree_view->model()->rowCount() > 0) { search_field->setFocus(); @@ -423,10 +413,6 @@ void GameList::AddEntry(const QList& entry_items, GameListDir* p parent->appendRow(entry_items); } -void GameList::AddRootEntry(const QList& entry_items) { - item_model->invisibleRootItem()->appendRow(entry_items); -} - void GameList::ValidateEntry(const QModelIndex& item) { const auto selected = item.sibling(item.row(), 0); @@ -482,9 +468,7 @@ bool GameList::IsEmpty() const { void GameList::DonePopulating(const QStringList& watch_list) { emit ShowList(!IsEmpty()); - if (UISettings::values.show_folders_in_list) { - item_model->invisibleRootItem()->appendRow(new GameListAddDir()); - } + item_model->invisibleRootItem()->appendRow(new GameListAddDir()); // Add favorites row item_model->invisibleRootItem()->insertRow(0, new GameListFavorites()); @@ -501,7 +485,6 @@ void GameList::DonePopulating(const QStringList& watch_list) { if (!watch_dirs.isEmpty()) { watcher->removePaths(watch_dirs); } - // Workaround: Add the watch paths in chunks to allow the gui to refresh // This prevents the UI from stalling when a large number of watch paths are added // Also artificially caps the watcher to a certain number of directories @@ -903,43 +886,22 @@ void GameList::ToggleFavorite(u64 program_id) { void GameList::AddFavorite(u64 program_id) { auto* favorites_row = item_model->item(0); - if (UISettings::values.show_folders_in_list) { - for (int i = 0; i < item_model->rowCount(); i++) { - const auto* folder = item_model->item(i); - for (int j = 0; j < folder->rowCount(); j++) { - if (folder->child(j)->data(GameListItemPath::ProgramIdRole).toULongLong() == - program_id) { - QList list; - for (int k = 0; k < COLUMN_COUNT; k++) { - list.append(folder->child(j, k)->clone()); - } - list[0]->setData(folder->child(j)->data(GameListItem::SortRole), - GameListItem::SortRole); - list[0]->setText(folder->child(j)->data(Qt::DisplayRole).toString()); - - favorites_row->appendRow(list); - return; + for (int i = 1; i < item_model->rowCount() - 1; i++) { + const auto* folder = item_model->item(i); + for (int j = 0; j < folder->rowCount(); j++) { + if (folder->child(j)->data(GameListItemPath::ProgramIdRole).toULongLong() == + program_id) { + QList list; + for (int k = 0; k < COLUMN_COUNT; k++) { + list.append(folder->child(j, k)->clone()); } - } - } - return; - } else { - for (int i = 0; i < item_model->rowCount(); i++) { - const auto* game = item_model->item(i); - if (game->data(GameListItemPath::ProgramIdRole).toULongLong() != program_id) { - continue; - } + list[0]->setData(folder->child(j)->data(GameListItem::SortRole), + GameListItem::SortRole); + list[0]->setText(folder->child(j)->data(Qt::DisplayRole).toString()); - QList list; - for (int j = 0; j < COLUMN_COUNT; j++) { - list.append(item_model->item(i, j)->clone()); + favorites_row->appendRow(list); + return; } - - list[0]->setData(game->data(GameListItem::SortRole), GameListItem::SortRole); - list[0]->setText(game->data(Qt::DisplayRole).toString()); - - favorites_row->appendRow(list); - return; } } } diff --git a/src/suyu/game_list.h b/src/suyu/game_list.h index 45a50fe2f3..f8796fa821 100644 --- a/src/suyu/game_list.h +++ b/src/suyu/game_list.h @@ -84,7 +84,6 @@ public: ~GameList() override; QString GetLastFilterResultItem() const; - void ClearList(); void ClearFilter(); void SetFilterFocus(); void SetFilterVisible(bool visibility); @@ -138,7 +137,6 @@ private: void AddDirEntry(GameListDir* entry_items); void AddEntry(const QList& entry_items, GameListDir* parent); - void AddRootEntry(const QList& entry_items); void DonePopulating(const QStringList& watch_list); private: diff --git a/src/suyu/game_list_worker.cpp b/src/suyu/game_list_worker.cpp index 93eeb40a14..9243464780 100644 --- a/src/suyu/game_list_worker.cpp +++ b/src/suyu/game_list_worker.cpp @@ -234,8 +234,8 @@ GameListWorker::GameListWorker(FileSys::VirtualFilesystem vfs_, const PlayTime::PlayTimeManager& play_time_manager_, Core::System& system_) : vfs{std::move(vfs_)}, provider{provider_}, game_dirs{game_dirs_}, - compatibility_list{compatibility_list_}, play_time_manager{play_time_manager_}, - system{system_} { + compatibility_list{compatibility_list_}, play_time_manager{play_time_manager_}, system{ + system_} { // We want the game list to manage our lifetime. setAutoDelete(false); } @@ -330,13 +330,7 @@ void GameListWorker::AddTitlesToGameList(GameListDir* parent_dir) { auto entry = MakeGameListEntry(file->GetFullPath(), name, file->GetSize(), icon, *loader, program_id, compatibility_list, play_time_manager, patch); - RecordEvent([=](GameList* game_list) { - if (UISettings::values.show_folders_in_list) { - game_list->AddEntry(entry, parent_dir); - } else { - game_list->AddRootEntry(entry); - } - }); + RecordEvent([=](GameList* game_list) { game_list->AddEntry(entry, parent_dir); }); } } @@ -414,7 +408,8 @@ void GameListWorker::ScanFileSystem(ScanTarget target, const std::string& dir_pa physical_name, name, Common::FS::GetSize(physical_name), icon, *loader, id, compatibility_list, play_time_manager, patch); - RecordEvent([=](GameList* game_list) { game_list->AddRootEntry(entry); }); + RecordEvent( + [=](GameList* game_list) { game_list->AddEntry(entry, parent_dir); }); } } else { std::vector icon; @@ -430,13 +425,8 @@ void GameListWorker::ScanFileSystem(ScanTarget target, const std::string& dir_pa physical_name, name, Common::FS::GetSize(physical_name), icon, *loader, program_id, compatibility_list, play_time_manager, patch); - RecordEvent([=](GameList* game_list) { - if (UISettings::values.show_folders_in_list) { - game_list->AddEntry(entry, parent_dir); - } else { - game_list->AddRootEntry(entry); - } - }); + RecordEvent( + [=](GameList* game_list) { game_list->AddEntry(entry, parent_dir); }); } } } else if (is_dir) { @@ -469,32 +459,20 @@ void GameListWorker::run() { if (game_dir.path == std::string("SDMC")) { auto* const game_list_dir = new GameListDir(game_dir, GameListItemType::SdmcDir); - - if (UISettings::values.show_folders_in_list) - DirEntryReady(game_list_dir); - + DirEntryReady(game_list_dir); AddTitlesToGameList(game_list_dir); } else if (game_dir.path == std::string("UserNAND")) { auto* const game_list_dir = new GameListDir(game_dir, GameListItemType::UserNandDir); - - if (UISettings::values.show_folders_in_list) - DirEntryReady(game_list_dir); - + DirEntryReady(game_list_dir); AddTitlesToGameList(game_list_dir); } else if (game_dir.path == std::string("SysNAND")) { auto* const game_list_dir = new GameListDir(game_dir, GameListItemType::SysNandDir); - - if (UISettings::values.show_folders_in_list) - DirEntryReady(game_list_dir); - + DirEntryReady(game_list_dir); AddTitlesToGameList(game_list_dir); } else { watch_list.append(QString::fromStdString(game_dir.path)); auto* const game_list_dir = new GameListDir(game_dir); - - if (UISettings::values.show_folders_in_list) - DirEntryReady(game_list_dir); - + DirEntryReady(game_list_dir); ScanFileSystem(ScanTarget::FillManualContentProvider, game_dir.path, game_dir.deep_scan, game_list_dir); ScanFileSystem(ScanTarget::PopulateGameList, game_dir.path, game_dir.deep_scan, diff --git a/src/suyu/main.cpp b/src/suyu/main.cpp index 8952b324df..4963dde610 100644 --- a/src/suyu/main.cpp +++ b/src/suyu/main.cpp @@ -100,6 +100,7 @@ #include "common/x64/cpu_detect.h" #endif #include "common/settings.h" +#include "common/telemetry.h" #include "core/core.h" #include "core/core_timing.h" #include "core/crypto/key_manager.h" @@ -118,6 +119,7 @@ #include "core/hle/service/sm/sm.h" #include "core/loader/loader.h" #include "core/perf_stats.h" +#include "core/telemetry_session.h" #include "frontend_common/config.h" #include "input_common/drivers/tas_input.h" #include "input_common/drivers/virtual_amiibo.h" @@ -186,9 +188,28 @@ constexpr size_t CopyBufferSize = 1_MiB; * user. This is 32-bits - if we have more than 32 callouts, we should retire and recycle old ones. */ enum class CalloutFlag : uint32_t { + Telemetry = 0x1, DRDDeprecation = 0x2, }; +void GMainWindow::ShowTelemetryCallout() { + if (UISettings::values.callout_flags.GetValue() & + static_cast(CalloutFlag::Telemetry)) { + return; + } + + UISettings::values.callout_flags = + UISettings::values.callout_flags.GetValue() | static_cast(CalloutFlag::Telemetry); + const QString telemetry_message = + tr("Anonymous " + "data is collected to help improve suyu. " + "

Would you like to share your usage data with us?"); + if (!question(this, tr("Telemetry"), telemetry_message)) { + Settings::values.enable_telemetry = false; + system->ApplySettings(); + } +} + const int GMainWindow::max_recent_files_item; static void RemoveCachedContents() { @@ -396,6 +417,9 @@ GMainWindow::GMainWindow(std::unique_ptr config_, bool has_broken_vulk game_list->LoadCompatibilityList(); game_list->PopulateAsync(UISettings::values.game_dirs); + // Show one-time "callout" messages to the user + ShowTelemetryCallout(); + // make sure menubar has the arrow cursor instead of inheriting from this ui->menubar->setCursor(QCursor()); statusBar()->setCursor(QCursor()); @@ -1416,7 +1440,6 @@ void GMainWindow::RestoreUIState() { game_list->SetFilterVisible(ui->action_Show_Filter_Bar->isChecked()); ui->action_Show_Status_Bar->setChecked(UISettings::values.show_status_bar.GetValue()); - ui->action_Show_Folders_In_List->setChecked(UISettings::values.show_folders_in_list.GetValue()); statusBar()->setVisible(ui->action_Show_Status_Bar->isChecked()); Debugger::ToggleConsole(); } @@ -1532,7 +1555,6 @@ void GMainWindow::ConnectMenuEvents() { connect_menu(ui->action_Display_Dock_Widget_Headers, &GMainWindow::OnDisplayTitleBars); connect_menu(ui->action_Show_Filter_Bar, &GMainWindow::OnToggleFilterBar); connect_menu(ui->action_Show_Status_Bar, &GMainWindow::OnToggleStatusBar); - connect_menu(ui->action_Show_Folders_In_List, &GMainWindow::OnToggleFoldersInList); connect_menu(ui->action_Reset_Window_Size_720, &GMainWindow::ResetWindowSize720); connect_menu(ui->action_Reset_Window_Size_900, &GMainWindow::ResetWindowSize900); @@ -1848,6 +1870,8 @@ bool GMainWindow::LoadROM(const QString& filename, Service::AM::FrontendAppletPa return false; } current_game_path = filename; + + system->TelemetrySession().AddField(Common::Telemetry::FieldType::App, "Frontend", "Qt"); return true; } @@ -3356,7 +3380,7 @@ void GMainWindow::OnMenuReportCompatibility() { if (!Settings::values.suyu_token.GetValue().empty() && !Settings::values.suyu_username.GetValue().empty()) { - CompatDB compatdb{this}; + CompatDB compatdb{system->TelemetrySession(), this}; compatdb.exec(); } else { QMessageBox::critical( @@ -3586,6 +3610,8 @@ void GMainWindow::OnConfigure() { SetDefaultUIGeometry(); RestoreUIState(); + + ShowTelemetryCallout(); } InitializeHotkeys(); @@ -4187,14 +4213,6 @@ void GMainWindow::OnToggleStatusBar() { statusBar()->setVisible(ui->action_Show_Status_Bar->isChecked()); } -void GMainWindow::OnToggleFoldersInList() { - UISettings::values.show_folders_in_list = ui->action_Show_Folders_In_List->isChecked(); - - game_list->ClearList(); - game_list->LoadCompatibilityList(); - game_list->PopulateAsync(UISettings::values.game_dirs); -} - void GMainWindow::OnAlbum() { constexpr u64 AlbumId = static_cast(Service::AM::AppletProgramId::PhotoViewer); auto bis_system = system->GetFileSystemController().GetSystemNANDContents(); @@ -4583,7 +4601,6 @@ void GMainWindow::UpdateUISettings() { UISettings::values.display_titlebar = ui->action_Display_Dock_Widget_Headers->isChecked(); UISettings::values.show_filter_bar = ui->action_Show_Filter_Bar->isChecked(); UISettings::values.show_status_bar = ui->action_Show_Status_Bar->isChecked(); - UISettings::values.show_folders_in_list = ui->action_Show_Folders_In_List->isChecked(); UISettings::values.first_start = false; } diff --git a/src/suyu/main.h b/src/suyu/main.h index 7fa28477a7..1705c491bf 100644 --- a/src/suyu/main.h +++ b/src/suyu/main.h @@ -275,6 +275,7 @@ private: void BootGameFromList(const QString& filename, StartGameType with_config); void ShutdownGame(); + void ShowTelemetryCallout(); void SetDiscordEnabled(bool state); void LoadAmiibo(const QString& filename); @@ -383,7 +384,6 @@ private slots: void OnAbout(); void OnToggleFilterBar(); void OnToggleStatusBar(); - void OnToggleFoldersInList(); void OnDisplayTitleBars(bool); void InitializeHotkeys(); void ToggleFullscreen(); diff --git a/src/suyu/main.ui b/src/suyu/main.ui index fece5f7069..439dc4ba36 100644 --- a/src/suyu/main.ui +++ b/src/suyu/main.ui @@ -45,7 +45,7 @@ 0 0 1280 - 21 + 22 @@ -124,7 +124,6 @@ - @@ -289,17 +288,6 @@ Show Status Bar - - - true - - - Show Folders in List - - - Show Status Bar - - true diff --git a/src/suyu/multiplayer/direct_connect.cpp b/src/suyu/multiplayer/direct_connect.cpp index 7adde5168d..5d5cb7aabc 100644 --- a/src/suyu/multiplayer/direct_connect.cpp +++ b/src/suyu/multiplayer/direct_connect.cpp @@ -24,8 +24,8 @@ enum class ConnectionType : u8 { TraversalServer, IP }; DirectConnectWindow::DirectConnectWindow(Core::System& system_, QWidget* parent) : QDialog(parent, Qt::WindowTitleHint | Qt::WindowCloseButtonHint | Qt::WindowSystemMenuHint), - ui(std::make_unique()), system{system_}, - room_network{system.GetRoomNetwork()} { + ui(std::make_unique()), system{system_}, room_network{ + system.GetRoomNetwork()} { ui->setupUi(this); diff --git a/src/suyu/multiplayer/host_room.cpp b/src/suyu/multiplayer/host_room.cpp index 66733837bc..6c71454c8e 100644 --- a/src/suyu/multiplayer/host_room.cpp +++ b/src/suyu/multiplayer/host_room.cpp @@ -31,8 +31,9 @@ HostRoomWindow::HostRoomWindow(QWidget* parent, QStandardItemModel* list, std::shared_ptr session, Core::System& system_) : QDialog(parent, Qt::WindowTitleHint | Qt::WindowCloseButtonHint | Qt::WindowSystemMenuHint), - ui(std::make_unique()), announce_multiplayer_session(session), system{system_}, - room_network{system.GetRoomNetwork()} { + ui(std::make_unique()), + announce_multiplayer_session(session), system{system_}, room_network{ + system.GetRoomNetwork()} { ui->setupUi(this); // set up validation for all of the fields diff --git a/src/suyu/multiplayer/lobby.cpp b/src/suyu/multiplayer/lobby.cpp index 9fca35461c..b4c0596632 100644 --- a/src/suyu/multiplayer/lobby.cpp +++ b/src/suyu/multiplayer/lobby.cpp @@ -27,8 +27,9 @@ Lobby::Lobby(QWidget* parent, QStandardItemModel* list, std::shared_ptr session, Core::System& system_) : QDialog(parent, Qt::WindowTitleHint | Qt::WindowCloseButtonHint | Qt::WindowSystemMenuHint), - ui(std::make_unique()), announce_multiplayer_session(session), system{system_}, - room_network{system.GetRoomNetwork()} { + ui(std::make_unique()), + announce_multiplayer_session(session), system{system_}, room_network{ + system.GetRoomNetwork()} { ui->setupUi(this); // setup the watcher for background connections diff --git a/src/suyu/uisettings.h b/src/suyu/uisettings.h index f1956bddf1..89fc353589 100644 --- a/src/suyu/uisettings.h +++ b/src/suyu/uisettings.h @@ -200,7 +200,6 @@ struct Values { std::atomic_bool is_game_list_reload_pending{false}; Setting cache_game_list{linkage, true, "cache_game_list", Category::UiGameList}; Setting favorites_expanded{linkage, true, "favorites_expanded", Category::UiGameList}; - Setting show_folders_in_list{linkage, true, "show_folders_in_list", Category::UiGameList}; QVector favorited_ids; // Compatibility List diff --git a/src/suyu_cmd/suyu.cpp b/src/suyu_cmd/suyu.cpp index f559c74f5b..cb91dde5d8 100644 --- a/src/suyu_cmd/suyu.cpp +++ b/src/suyu_cmd/suyu.cpp @@ -19,6 +19,7 @@ #include "common/scope_exit.h" #include "common/settings.h" #include "common/string_util.h" +#include "common/telemetry.h" #include "core/core.h" #include "core/core_timing.h" #include "core/cpu_manager.h" @@ -28,6 +29,7 @@ #include "core/hle/service/am/applet_manager.h" #include "core/hle/service/filesystem/filesystem.h" #include "core/loader/loader.h" +#include "core/telemetry_session.h" #include "frontend_common/config.h" #include "input_common/main.h" #include "network/network.h" @@ -401,6 +403,8 @@ int main(int argc, char** argv) { break; } + system.TelemetrySession().AddField(Common::Telemetry::FieldType::App, "Frontend", "SDL"); + if (use_multiplayer) { if (auto member = system.GetRoomNetwork().GetRoomMember().lock()) { member->BindOnChatMessageReceived(OnMessageReceived); diff --git a/src/video_core/cdma_pusher.cpp b/src/video_core/cdma_pusher.cpp index 24d73bd843..3bcf1b0664 100644 --- a/src/video_core/cdma_pusher.cpp +++ b/src/video_core/cdma_pusher.cpp @@ -18,8 +18,8 @@ namespace Tegra { CDmaPusher::CDmaPusher(Host1x::Host1x& host1x_, s32 id) : host1x{host1x_}, memory_manager{host1x.GMMU()}, - host_processor{std::make_unique(host1x_)}, - current_class{static_cast(id)} { + host_processor{std::make_unique(host1x_)}, current_class{ + static_cast(id)} { thread = std::jthread([this](std::stop_token stop_token) { ProcessEntries(stop_token); }); } diff --git a/src/video_core/dma_pusher.cpp b/src/video_core/dma_pusher.cpp index 33ef54edef..fb2060ca43 100644 --- a/src/video_core/dma_pusher.cpp +++ b/src/video_core/dma_pusher.cpp @@ -18,8 +18,8 @@ constexpr u32 ComputeInline = 0x6D; DmaPusher::DmaPusher(Core::System& system_, GPU& gpu_, MemoryManager& memory_manager_, Control::ChannelState& channel_state_) - : gpu{gpu_}, system{system_}, memory_manager{memory_manager_}, - puller{gpu_, memory_manager_, *this, channel_state_} {} + : gpu{gpu_}, system{system_}, memory_manager{memory_manager_}, puller{gpu_, memory_manager_, + *this, channel_state_} {} DmaPusher::~DmaPusher() = default; diff --git a/src/video_core/engines/maxwell_3d.cpp b/src/video_core/engines/maxwell_3d.cpp index 5e070fa3b4..0d47b032c8 100644 --- a/src/video_core/engines/maxwell_3d.cpp +++ b/src/video_core/engines/maxwell_3d.cpp @@ -24,8 +24,9 @@ constexpr u32 MacroRegistersStart = 0xE00; Maxwell3D::Maxwell3D(Core::System& system_, MemoryManager& memory_manager_) : draw_manager{std::make_unique(this)}, system{system_}, - memory_manager{memory_manager_}, macro_engine{GetMacroEngine(*this)}, - upload_state{memory_manager, regs.upload} { + memory_manager{memory_manager_}, macro_engine{GetMacroEngine(*this)}, upload_state{ + memory_manager, + regs.upload} { dirty.flags.flip(); InitializeRegisterDefaults(); execution_mask.reset(); diff --git a/src/video_core/engines/puller.cpp b/src/video_core/engines/puller.cpp index 415bac9c98..8dd34c04ab 100644 --- a/src/video_core/engines/puller.cpp +++ b/src/video_core/engines/puller.cpp @@ -21,8 +21,8 @@ namespace Tegra::Engines { Puller::Puller(GPU& gpu_, MemoryManager& memory_manager_, DmaPusher& dma_pusher_, Control::ChannelState& channel_state_) - : gpu{gpu_}, memory_manager{memory_manager_}, dma_pusher{dma_pusher_}, - channel_state{channel_state_} {} + : gpu{gpu_}, memory_manager{memory_manager_}, dma_pusher{dma_pusher_}, channel_state{ + channel_state_} {} Puller::~Puller() = default; diff --git a/src/video_core/host1x/codecs/decoder.cpp b/src/video_core/host1x/codecs/decoder.cpp index c62c11e28f..49a601969c 100644 --- a/src/video_core/host1x/codecs/decoder.cpp +++ b/src/video_core/host1x/codecs/decoder.cpp @@ -11,8 +11,8 @@ namespace Tegra { Decoder::Decoder(Host1x::Host1x& host1x_, s32 id_, const Host1x::NvdecCommon::NvdecRegisters& regs_, Host1x::FrameQueue& frame_queue_) - : host1x(host1x_), memory_manager{host1x.GMMU()}, regs{regs_}, id{id_}, - frame_queue{frame_queue_} {} + : host1x(host1x_), memory_manager{host1x.GMMU()}, regs{regs_}, id{id_}, frame_queue{ + frame_queue_} {} Decoder::~Decoder() = default; diff --git a/src/video_core/host1x/host1x.cpp b/src/video_core/host1x/host1x.cpp index 7499ee77ec..293bca6d79 100644 --- a/src/video_core/host1x/host1x.cpp +++ b/src/video_core/host1x/host1x.cpp @@ -9,8 +9,8 @@ namespace Tegra::Host1x { Host1x::Host1x(Core::System& system_) - : system{system_}, syncpoint_manager{}, memory_manager(system.DeviceMemory()), - gmmu_manager{system, memory_manager, 32, 0, 12}, + : system{system_}, syncpoint_manager{}, + memory_manager(system.DeviceMemory()), gmmu_manager{system, memory_manager, 32, 0, 12}, allocator{std::make_unique>(1 << 12)} {} Host1x::~Host1x() = default; diff --git a/src/video_core/host1x/vic.cpp b/src/video_core/host1x/vic.cpp index 2d06580609..3ad56bb80c 100644 --- a/src/video_core/host1x/vic.cpp +++ b/src/video_core/host1x/vic.cpp @@ -94,8 +94,8 @@ void SwizzleSurface(std::span output, u32 out_stride, std::span in } // namespace Vic::Vic(Host1x& host1x_, s32 id_, u32 syncpt, FrameQueue& frame_queue_) - : CDmaPusher{host1x_, id_}, id{id_}, syncpoint{syncpt}, frame_queue{frame_queue_}, - has_sse41{HasSSE41()} { + : CDmaPusher{host1x_, id_}, id{id_}, syncpoint{syncpt}, + frame_queue{frame_queue_}, has_sse41{HasSSE41()} { LOG_INFO(HW_GPU, "Created vic {}", id); } diff --git a/src/video_core/memory_manager.cpp b/src/video_core/memory_manager.cpp index aa60938119..ffafc48eff 100644 --- a/src/video_core/memory_manager.cpp +++ b/src/video_core/memory_manager.cpp @@ -28,8 +28,8 @@ MemoryManager::MemoryManager(Core::System& system_, MaxwellDeviceMemoryManager& split_address{split_address_}, page_bits{page_bits_}, big_page_bits{big_page_bits_}, entries{}, big_entries{}, page_table{address_space_bits, address_space_bits + page_bits - 38, page_bits != big_page_bits ? page_bits : 0}, - kind_map{PTEKind::INVALID}, - unique_identifier{unique_identifier_generator.fetch_add(1, std::memory_order_acq_rel)}, + kind_map{PTEKind::INVALID}, unique_identifier{unique_identifier_generator.fetch_add( + 1, std::memory_order_acq_rel)}, accumulator{std::make_unique()} { address_space_size = 1ULL << address_space_bits; page_size = 1ULL << page_bits; diff --git a/src/video_core/query_cache/query_cache.h b/src/video_core/query_cache/query_cache.h index d4ee55dea0..08b7790555 100644 --- a/src/video_core/query_cache/query_cache.h +++ b/src/video_core/query_cache/query_cache.h @@ -115,8 +115,8 @@ struct QueryCacheBase::QueryCacheBaseImpl { QueryCacheBaseImpl(QueryCacheBase* owner_, VideoCore::RasterizerInterface& rasterizer_, Tegra::MaxwellDeviceMemoryManager& device_memory_, RuntimeType& runtime_, Tegra::GPU& gpu_) - : owner{owner_}, rasterizer{rasterizer_}, device_memory{device_memory_}, runtime{runtime_}, - gpu{gpu_} { + : owner{owner_}, rasterizer{rasterizer_}, + device_memory{device_memory_}, runtime{runtime_}, gpu{gpu_} { streamer_mask = 0; for (size_t i = 0; i < static_cast(QueryType::MaxQueryTypes); i++) { streamers[i] = runtime.GetStreamerInterface(static_cast(i)); diff --git a/src/video_core/renderer_opengl/gl_compute_pipeline.cpp b/src/video_core/renderer_opengl/gl_compute_pipeline.cpp index bb974bfd2b..d705018607 100644 --- a/src/video_core/renderer_opengl/gl_compute_pipeline.cpp +++ b/src/video_core/renderer_opengl/gl_compute_pipeline.cpp @@ -31,8 +31,8 @@ ComputePipeline::ComputePipeline(const Device& device, TextureCache& texture_cac BufferCache& buffer_cache_, ProgramManager& program_manager_, const Shader::Info& info_, std::string code, std::vector code_v, bool force_context_flush) - : texture_cache{texture_cache_}, buffer_cache{buffer_cache_}, program_manager{program_manager_}, - info{info_} { + : texture_cache{texture_cache_}, buffer_cache{buffer_cache_}, + program_manager{program_manager_}, info{info_} { switch (device.GetShaderBackend()) { case Settings::ShaderBackend::Glsl: source_program = CreateProgram(code, GL_COMPUTE_SHADER); diff --git a/src/video_core/renderer_opengl/gl_query_cache.cpp b/src/video_core/renderer_opengl/gl_query_cache.cpp index ca00d7c3b7..2147d587f7 100644 --- a/src/video_core/renderer_opengl/gl_query_cache.cpp +++ b/src/video_core/renderer_opengl/gl_query_cache.cpp @@ -66,8 +66,9 @@ bool QueryCache::AnyCommandQueued() const noexcept { HostCounter::HostCounter(QueryCache& cache_, std::shared_ptr dependency_, VideoCore::QueryType type_) - : HostCounterBase{std::move(dependency_)}, cache{cache_}, type{type_}, - query{cache.AllocateQuery(type)} { + : HostCounterBase{std::move(dependency_)}, cache{cache_}, type{type_}, query{ + cache.AllocateQuery( + type)} { glBeginQuery(GetTarget(type), query.handle); } diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp index 5d437299da..452af27872 100644 --- a/src/video_core/renderer_opengl/renderer_opengl.cpp +++ b/src/video_core/renderer_opengl/renderer_opengl.cpp @@ -1,19 +1,27 @@ // SPDX-FileCopyrightText: 2014 Citra Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#include +#include +#include #include #include #include "common/assert.h" #include "common/logging/log.h" +#include "common/microprofile.h" #include "common/settings.h" +#include "common/telemetry.h" +#include "core/core_timing.h" #include "core/frontend/emu_window.h" +#include "core/telemetry_session.h" #include "video_core/capture.h" #include "video_core/present.h" #include "video_core/renderer_opengl/gl_blit_screen.h" #include "video_core/renderer_opengl/gl_rasterizer.h" #include "video_core/renderer_opengl/gl_shader_manager.h" +#include "video_core/renderer_opengl/gl_shader_util.h" #include "video_core/renderer_opengl/renderer_opengl.h" #include "video_core/textures/decoders.h" @@ -82,18 +90,20 @@ void APIENTRY DebugHandler(GLenum source, GLenum type, GLuint id, GLenum severit } } // Anonymous namespace -RendererOpenGL::RendererOpenGL(Core::Frontend::EmuWindow& emu_window_, +RendererOpenGL::RendererOpenGL(Core::TelemetrySession& telemetry_session_, + Core::Frontend::EmuWindow& emu_window_, Tegra::MaxwellDeviceMemoryManager& device_memory_, Tegra::GPU& gpu_, std::unique_ptr context_) - : RendererBase{emu_window_, std::move(context_)}, emu_window{emu_window_}, - device_memory{device_memory_}, gpu{gpu_}, device{emu_window_}, state_tracker{}, - program_manager{device}, + : RendererBase{emu_window_, std::move(context_)}, telemetry_session{telemetry_session_}, + emu_window{emu_window_}, device_memory{device_memory_}, gpu{gpu_}, device{emu_window_}, + state_tracker{}, program_manager{device}, rasterizer(emu_window, gpu, device_memory, device, program_manager, state_tracker) { if (Settings::values.renderer_debug && GLAD_GL_KHR_debug) { glEnable(GL_DEBUG_OUTPUT); glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS); glDebugMessageCallback(DebugHandler, nullptr); } + AddTelemetryFields(); // Initialize default attributes to match hardware's disabled attributes GLint max_attribs{}; @@ -145,6 +155,21 @@ void RendererOpenGL::Composite(std::span framebu render_window.OnFrameDisplayed(); } +void RendererOpenGL::AddTelemetryFields() { + const char* const gl_version{reinterpret_cast(glGetString(GL_VERSION))}; + const char* const gpu_vendor{reinterpret_cast(glGetString(GL_VENDOR))}; + const char* const gpu_model{reinterpret_cast(glGetString(GL_RENDERER))}; + + LOG_INFO(Render_OpenGL, "GL_VERSION: {}", gl_version); + LOG_INFO(Render_OpenGL, "GL_VENDOR: {}", gpu_vendor); + LOG_INFO(Render_OpenGL, "GL_RENDERER: {}", gpu_model); + + constexpr auto user_system = Common::Telemetry::FieldType::UserSystem; + telemetry_session.AddField(user_system, "GPU_Vendor", std::string(gpu_vendor)); + telemetry_session.AddField(user_system, "GPU_Model", std::string(gpu_model)); + telemetry_session.AddField(user_system, "GPU_OpenGL_Version", std::string(gl_version)); +} + void RendererOpenGL::RenderToBuffer(std::span framebuffers, const Layout::FramebufferLayout& layout, void* dst) { GLint old_read_fb; diff --git a/src/video_core/renderer_opengl/renderer_opengl.h b/src/video_core/renderer_opengl/renderer_opengl.h index 20ed3730f0..60d6a1477e 100644 --- a/src/video_core/renderer_opengl/renderer_opengl.h +++ b/src/video_core/renderer_opengl/renderer_opengl.h @@ -34,7 +34,8 @@ class BlitScreen; class RendererOpenGL final : public VideoCore::RendererBase { public: - explicit RendererOpenGL(Core::Frontend::EmuWindow& emu_window_, + explicit RendererOpenGL(Core::TelemetrySession& telemetry_session_, + Core::Frontend::EmuWindow& emu_window_, Tegra::MaxwellDeviceMemoryManager& device_memory_, Tegra::GPU& gpu_, std::unique_ptr context_); ~RendererOpenGL() override; @@ -52,11 +53,14 @@ public: } private: + void AddTelemetryFields(); + void RenderToBuffer(std::span framebuffers, const Layout::FramebufferLayout& layout, void* dst); void RenderScreenshot(std::span framebuffers); void RenderAppletCaptureLayer(std::span framebuffers); + Core::TelemetrySession& telemetry_session; Core::Frontend::EmuWindow& emu_window; Tegra::MaxwellDeviceMemoryManager& device_memory; Tegra::GPU& gpu; diff --git a/src/video_core/renderer_vulkan/present/fsr.cpp b/src/video_core/renderer_vulkan/present/fsr.cpp index 55606deb83..3f708be704 100644 --- a/src/video_core/renderer_vulkan/present/fsr.cpp +++ b/src/video_core/renderer_vulkan/present/fsr.cpp @@ -24,8 +24,8 @@ using PushConstants = std::array; FSR::FSR(const Device& device, MemoryAllocator& memory_allocator, size_t image_count, VkExtent2D extent) - : m_device{device}, m_memory_allocator{memory_allocator}, m_image_count{image_count}, - m_extent{extent} { + : m_device{device}, m_memory_allocator{memory_allocator}, + m_image_count{image_count}, m_extent{extent} { CreateImages(); CreateRenderPasses(); diff --git a/src/video_core/renderer_vulkan/renderer_vulkan.cpp b/src/video_core/renderer_vulkan/renderer_vulkan.cpp index 2ec8c08bf5..c553f5b3d9 100644 --- a/src/video_core/renderer_vulkan/renderer_vulkan.cpp +++ b/src/video_core/renderer_vulkan/renderer_vulkan.cpp @@ -1,17 +1,24 @@ // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#include +#include #include #include #include +#include #include #include #include "common/logging/log.h" +#include "common/polyfill_ranges.h" #include "common/scope_exit.h" #include "common/settings.h" +#include "common/telemetry.h" +#include "core/core_timing.h" #include "core/frontend/graphics_context.h" +#include "core/telemetry_session.h" #include "video_core/capture.h" #include "video_core/gpu.h" #include "video_core/present.h" @@ -46,6 +53,37 @@ constexpr VkExtent3D CaptureImageExtent{ }; constexpr VkFormat CaptureFormat = VK_FORMAT_A8B8G8R8_UNORM_PACK32; + +std::string GetReadableVersion(u32 version) { + return fmt::format("{}.{}.{}", VK_VERSION_MAJOR(version), VK_VERSION_MINOR(version), + VK_VERSION_PATCH(version)); +} + +std::string GetDriverVersion(const Device& device) { + // Extracted from + // https://github.com/SaschaWillems/vulkan.gpuinfo.org/blob/5dddea46ea1120b0df14eef8f15ff8e318e35462/functions.php#L308-L314 + const u32 version = device.GetDriverVersion(); + + if (device.GetDriverID() == VK_DRIVER_ID_NVIDIA_PROPRIETARY) { + const u32 major = (version >> 22) & 0x3ff; + const u32 minor = (version >> 14) & 0x0ff; + const u32 secondary = (version >> 6) & 0x0ff; + const u32 tertiary = version & 0x003f; + return fmt::format("{}.{}.{}.{}", major, minor, secondary, tertiary); + } + if (device.GetDriverID() == VK_DRIVER_ID_INTEL_PROPRIETARY_WINDOWS) { + const u32 major = version >> 14; + const u32 minor = version & 0x3fff; + return fmt::format("{}.{}", major, minor); + } + return GetReadableVersion(version); +} + +std::string BuildCommaSeparatedExtensions( + const std::set>& available_extensions) { + return fmt::format("{}", fmt::join(available_extensions, ",")); +} + } // Anonymous namespace Device CreateDevice(const vk::Instance& instance, const vk::InstanceDispatch& dld, @@ -60,11 +98,12 @@ Device CreateDevice(const vk::Instance& instance, const vk::InstanceDispatch& dl return Device(*instance, physical_device, surface, dld); } -RendererVulkan::RendererVulkan(Core::Frontend::EmuWindow& emu_window, +RendererVulkan::RendererVulkan(Core::TelemetrySession& telemetry_session_, + Core::Frontend::EmuWindow& emu_window, Tegra::MaxwellDeviceMemoryManager& device_memory_, Tegra::GPU& gpu_, std::unique_ptr context_) try - : RendererBase(emu_window, std::move(context_)), device_memory(device_memory_), gpu(gpu_), - library(OpenLibrary(context.get())), + : RendererBase(emu_window, std::move(context_)), telemetry_session(telemetry_session_), + device_memory(device_memory_), gpu(gpu_), library(OpenLibrary(context.get())), instance(CreateInstance(*library, dld, VK_API_VERSION_1_1, render_window.GetWindowInfo().type, Settings::values.renderer_debug.GetValue())), debug_messenger(Settings::values.renderer_debug ? CreateDebugUtilsCallback(instance) @@ -89,6 +128,7 @@ RendererVulkan::RendererVulkan(Core::Frontend::EmuWindow& emu_window, turbo_mode.emplace(instance, dld); scheduler.RegisterOnSubmit([this] { turbo_mode->QueueSubmitted(); }); } + Report(); } catch (const vk::Exception& exception) { LOG_ERROR(Render_Vulkan, "Vulkan initialization failed with error: {}", exception.what()); throw std::runtime_error{fmt::format("Vulkan initialization error {}", exception.what())}; @@ -126,6 +166,32 @@ void RendererVulkan::Composite(std::span framebu rasterizer.TickFrame(); } +void RendererVulkan::Report() const { + using namespace Common::Literals; + const std::string vendor_name{device.GetVendorName()}; + const std::string model_name{device.GetModelName()}; + const std::string driver_version = GetDriverVersion(device); + const std::string driver_name = fmt::format("{} {}", vendor_name, driver_version); + + const std::string api_version = GetReadableVersion(device.ApiVersion()); + + const std::string extensions = BuildCommaSeparatedExtensions(device.GetAvailableExtensions()); + + const auto available_vram = static_cast(device.GetDeviceLocalMemory()) / f64{1_GiB}; + + LOG_INFO(Render_Vulkan, "Driver: {}", driver_name); + LOG_INFO(Render_Vulkan, "Device: {}", model_name); + LOG_INFO(Render_Vulkan, "Vulkan: {}", api_version); + LOG_INFO(Render_Vulkan, "Available VRAM: {:.2f} GiB", available_vram); + + static constexpr auto field = Common::Telemetry::FieldType::UserSystem; + telemetry_session.AddField(field, "GPU_Vendor", vendor_name); + telemetry_session.AddField(field, "GPU_Model", model_name); + telemetry_session.AddField(field, "GPU_Vulkan_Driver", driver_name); + telemetry_session.AddField(field, "GPU_Vulkan_Version", api_version); + telemetry_session.AddField(field, "GPU_Vulkan_Extensions", extensions); +} + vk::Buffer RendererVulkan::RenderToBuffer(std::span framebuffers, const Layout::FramebufferLayout& layout, VkFormat format, VkDeviceSize buffer_size) { diff --git a/src/video_core/renderer_vulkan/renderer_vulkan.h b/src/video_core/renderer_vulkan/renderer_vulkan.h index 77a57f4196..fb9d83412a 100644 --- a/src/video_core/renderer_vulkan/renderer_vulkan.h +++ b/src/video_core/renderer_vulkan/renderer_vulkan.h @@ -40,7 +40,8 @@ Device CreateDevice(const vk::Instance& instance, const vk::InstanceDispatch& dl class RendererVulkan final : public VideoCore::RendererBase { public: - explicit RendererVulkan(Core::Frontend::EmuWindow& emu_window, + explicit RendererVulkan(Core::TelemetrySession& telemtry_session, + Core::Frontend::EmuWindow& emu_window, Tegra::MaxwellDeviceMemoryManager& device_memory_, Tegra::GPU& gpu_, std::unique_ptr context_); ~RendererVulkan() override; @@ -58,12 +59,15 @@ public: } private: + void Report() const; + vk::Buffer RenderToBuffer(std::span framebuffers, const Layout::FramebufferLayout& layout, VkFormat format, VkDeviceSize buffer_size); void RenderScreenshot(std::span framebuffers); void RenderAppletCaptureLayer(std::span framebuffers); + Core::TelemetrySession& telemetry_session; Tegra::MaxwellDeviceMemoryManager& device_memory; Tegra::GPU& gpu; diff --git a/src/video_core/renderer_vulkan/vk_compute_pass.cpp b/src/video_core/renderer_vulkan/vk_compute_pass.cpp index 6455e5eb75..617f92910c 100644 --- a/src/video_core/renderer_vulkan/vk_compute_pass.cpp +++ b/src/video_core/renderer_vulkan/vk_compute_pass.cpp @@ -531,8 +531,8 @@ ASTCDecoderPass::ASTCDecoderPass(const Device& device_, Scheduler& scheduler_, ASTC_PASS_DESCRIPTOR_UPDATE_TEMPLATE_ENTRY, ASTC_BANK_INFO, COMPUTE_PUSH_CONSTANT_RANGE, ASTC_DECODER_COMP_SPV), scheduler{scheduler_}, staging_buffer_pool{staging_buffer_pool_}, - compute_pass_descriptor_queue{compute_pass_descriptor_queue_}, - memory_allocator{memory_allocator_} {} + compute_pass_descriptor_queue{compute_pass_descriptor_queue_}, memory_allocator{ + memory_allocator_} {} ASTCDecoderPass::~ASTCDecoderPass() = default; diff --git a/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp b/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp index 689c7d760c..73e585c2b7 100644 --- a/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp +++ b/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp @@ -31,8 +31,8 @@ ComputePipeline::ComputePipeline(const Device& device_, vk::PipelineCache& pipel PipelineStatistics* pipeline_statistics, VideoCore::ShaderNotify* shader_notify, const Shader::Info& info_, vk::ShaderModule spv_module_) - : device{device_}, pipeline_cache(pipeline_cache_), - guest_descriptor_queue{guest_descriptor_queue_}, info{info_}, + : device{device_}, + pipeline_cache(pipeline_cache_), guest_descriptor_queue{guest_descriptor_queue_}, info{info_}, spv_module(std::move(spv_module_)) { if (shader_notify) { shader_notify->MarkShaderBuilding(); diff --git a/src/video_core/renderer_vulkan/vk_present_manager.cpp b/src/video_core/renderer_vulkan/vk_present_manager.cpp index 8fcc5ede32..5e7518d968 100644 --- a/src/video_core/renderer_vulkan/vk_present_manager.cpp +++ b/src/video_core/renderer_vulkan/vk_present_manager.cpp @@ -100,8 +100,8 @@ PresentManager::PresentManager(const vk::Instance& instance_, Swapchain& swapchain_, vk::SurfaceKHR& surface_) : instance{instance_}, render_window{render_window_}, device{device_}, memory_allocator{memory_allocator_}, scheduler{scheduler_}, swapchain{swapchain_}, - surface{surface_}, - blit_supported{CanBlitToSwapchain(device.GetPhysical(), swapchain.GetImageViewFormat())}, + surface{surface_}, blit_supported{CanBlitToSwapchain(device.GetPhysical(), + swapchain.GetImageViewFormat())}, use_present_thread{Settings::values.async_presentation.GetValue()} { SetImageCount(); diff --git a/src/video_core/renderer_vulkan/vk_query_cache.cpp b/src/video_core/renderer_vulkan/vk_query_cache.cpp index 6c25212819..a28296bdaf 100644 --- a/src/video_core/renderer_vulkan/vk_query_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_query_cache.cpp @@ -1161,9 +1161,10 @@ struct QueryCacheRuntimeImpl { StagingBufferPool& staging_pool_, ComputePassDescriptorQueue& compute_pass_descriptor_queue, DescriptorPool& descriptor_pool) - : rasterizer{rasterizer_}, device_memory{device_memory_}, buffer_cache{buffer_cache_}, - device{device_}, memory_allocator{memory_allocator_}, scheduler{scheduler_}, - staging_pool{staging_pool_}, guest_streamer(0, runtime), + : rasterizer{rasterizer_}, device_memory{device_memory_}, + buffer_cache{buffer_cache_}, device{device_}, + memory_allocator{memory_allocator_}, scheduler{scheduler_}, staging_pool{staging_pool_}, + guest_streamer(0, runtime), sample_streamer(static_cast(QueryType::ZPassPixelCount64), runtime, rasterizer, device, scheduler, memory_allocator, compute_pass_descriptor_queue, descriptor_pool), diff --git a/src/video_core/renderer_vulkan/vk_staging_buffer_pool.cpp b/src/video_core/renderer_vulkan/vk_staging_buffer_pool.cpp index 9f24741493..03a0b7280b 100644 --- a/src/video_core/renderer_vulkan/vk_staging_buffer_pool.cpp +++ b/src/video_core/renderer_vulkan/vk_staging_buffer_pool.cpp @@ -49,8 +49,8 @@ size_t GetStreamBufferSize(const Device& device) { StagingBufferPool::StagingBufferPool(const Device& device_, MemoryAllocator& memory_allocator_, Scheduler& scheduler_) : device{device_}, memory_allocator{memory_allocator_}, scheduler{scheduler_}, - stream_buffer_size{GetStreamBufferSize(device)}, - region_size{stream_buffer_size / StagingBufferPool::NUM_SYNCS} { + stream_buffer_size{GetStreamBufferSize(device)}, region_size{stream_buffer_size / + StagingBufferPool::NUM_SYNCS} { VkBufferCreateInfo stream_ci = { .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, .pNext = nullptr, diff --git a/src/video_core/shader_environment.cpp b/src/video_core/shader_environment.cpp index 54d2b95f29..282bb04ae5 100644 --- a/src/video_core/shader_environment.cpp +++ b/src/video_core/shader_environment.cpp @@ -398,8 +398,8 @@ u32 GraphicsEnvironment::ReadViewportTransformState() { ComputeEnvironment::ComputeEnvironment(Tegra::Engines::KeplerCompute& kepler_compute_, Tegra::MemoryManager& gpu_memory_, GPUVAddr program_base_, u32 start_address_) - : GenericEnvironment{gpu_memory_, program_base_, start_address_}, - kepler_compute{&kepler_compute_} { + : GenericEnvironment{gpu_memory_, program_base_, start_address_}, kepler_compute{ + &kepler_compute_} { const auto& qmd{kepler_compute->launch_description}; stage = Shader::Stage::Compute; local_memory_size = qmd.local_pos_alloc + qmd.local_crs_alloc; diff --git a/src/video_core/video_core.cpp b/src/video_core/video_core.cpp index fba258113d..0efb7b49d5 100644 --- a/src/video_core/video_core.cpp +++ b/src/video_core/video_core.cpp @@ -6,6 +6,7 @@ #include "common/logging/log.h" #include "common/settings.h" #include "core/core.h" +#include "video_core/host1x/gpu_device_memory_manager.h" #include "video_core/host1x/host1x.h" #include "video_core/renderer_base.h" #include "video_core/renderer_null/renderer_null.h" @@ -18,15 +19,16 @@ namespace { std::unique_ptr CreateRenderer( Core::System& system, Core::Frontend::EmuWindow& emu_window, Tegra::GPU& gpu, std::unique_ptr context) { + auto& telemetry_session = system.TelemetrySession(); auto& device_memory = system.Host1x().MemoryManager(); switch (Settings::values.renderer_backend.GetValue()) { case Settings::RendererBackend::OpenGL: - return std::make_unique(emu_window, device_memory, gpu, - std::move(context)); + return std::make_unique(telemetry_session, emu_window, + device_memory, gpu, std::move(context)); case Settings::RendererBackend::Vulkan: - return std::make_unique(emu_window, device_memory, gpu, - std::move(context)); + return std::make_unique(telemetry_session, emu_window, + device_memory, gpu, std::move(context)); case Settings::RendererBackend::Null: return std::make_unique(emu_window, gpu, std::move(context)); default: diff --git a/src/video_core/vulkan_common/vulkan_wrapper.h b/src/video_core/vulkan_common/vulkan_wrapper.h index fd14decd9f..757f3c8afb 100644 --- a/src/video_core/vulkan_common/vulkan_wrapper.h +++ b/src/video_core/vulkan_common/vulkan_wrapper.h @@ -625,8 +625,8 @@ class Image { public: explicit Image(VkImage handle_, VkDevice owner_, VmaAllocator allocator_, VmaAllocation allocation_, const DeviceDispatch& dld_) noexcept - : handle{handle_}, owner{owner_}, allocator{allocator_}, allocation{allocation_}, - dld{&dld_} {} + : handle{handle_}, owner{owner_}, allocator{allocator_}, + allocation{allocation_}, dld{&dld_} {} Image() = default; Image(const Image&) = delete; @@ -680,8 +680,8 @@ public: explicit Buffer(VkBuffer handle_, VkDevice owner_, VmaAllocator allocator_, VmaAllocation allocation_, std::span mapped_, bool is_coherent_, const DeviceDispatch& dld_) noexcept - : handle{handle_}, owner{owner_}, allocator{allocator_}, allocation{allocation_}, - mapped{mapped_}, is_coherent{is_coherent_}, dld{&dld_} {} + : handle{handle_}, owner{owner_}, allocator{allocator_}, + allocation{allocation_}, mapped{mapped_}, is_coherent{is_coherent_}, dld{&dld_} {} Buffer() = default; Buffer(const Buffer&) = delete; @@ -689,8 +689,8 @@ public: Buffer(Buffer&& rhs) noexcept : handle{std::exchange(rhs.handle, nullptr)}, owner{rhs.owner}, allocator{rhs.allocator}, - allocation{rhs.allocation}, mapped{rhs.mapped}, is_coherent{rhs.is_coherent}, - dld{rhs.dld} {} + allocation{rhs.allocation}, mapped{rhs.mapped}, + is_coherent{rhs.is_coherent}, dld{rhs.dld} {} Buffer& operator=(Buffer&& rhs) noexcept { Release(); diff --git a/src/web_service/CMakeLists.txt b/src/web_service/CMakeLists.txt index 6238d99092..1abf366960 100644 --- a/src/web_service/CMakeLists.txt +++ b/src/web_service/CMakeLists.txt @@ -5,6 +5,8 @@ add_library(web_service STATIC announce_room_json.cpp announce_room_json.h precompiled_headers.h + telemetry_json.cpp + telemetry_json.h verify_login.cpp verify_login.h verify_user_jwt.cpp diff --git a/src/web_service/telemetry_json.cpp b/src/web_service/telemetry_json.cpp new file mode 100644 index 0000000000..51c7920045 --- /dev/null +++ b/src/web_service/telemetry_json.cpp @@ -0,0 +1,130 @@ +// SPDX-FileCopyrightText: 2017 Citra Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include +#include "common/detached_tasks.h" +#include "web_service/telemetry_json.h" +#include "web_service/web_backend.h" +#include "web_service/web_result.h" + +namespace WebService { + +namespace Telemetry = Common::Telemetry; + +struct TelemetryJson::Impl { + Impl(std::string host_, std::string username_, std::string token_) + : host{std::move(host_)}, username{std::move(username_)}, token{std::move(token_)} {} + + nlohmann::json& TopSection() { + return sections[static_cast(Telemetry::FieldType::None)]; + } + + const nlohmann::json& TopSection() const { + return sections[static_cast(Telemetry::FieldType::None)]; + } + + template + void Serialize(Telemetry::FieldType type, const std::string& name, T value) { + sections[static_cast(type)][name] = value; + } + + void SerializeSection(Telemetry::FieldType type, const std::string& name) { + TopSection()[name] = sections[static_cast(type)]; + } + + nlohmann::json output; + std::array sections; + std::string host; + std::string username; + std::string token; +}; + +TelemetryJson::TelemetryJson(std::string host, std::string username, std::string token) + : impl{std::make_unique(std::move(host), std::move(username), std::move(token))} {} +TelemetryJson::~TelemetryJson() = default; + +void TelemetryJson::Visit(const Telemetry::Field& field) { + impl->Serialize(field.GetType(), field.GetName(), field.GetValue()); +} + +void TelemetryJson::Visit(const Telemetry::Field& field) { + impl->Serialize(field.GetType(), field.GetName(), field.GetValue()); +} + +void TelemetryJson::Visit(const Telemetry::Field& field) { + impl->Serialize(field.GetType(), field.GetName(), field.GetValue()); +} + +void TelemetryJson::Visit(const Telemetry::Field& field) { + impl->Serialize(field.GetType(), field.GetName(), field.GetValue()); +} + +void TelemetryJson::Visit(const Telemetry::Field& field) { + impl->Serialize(field.GetType(), field.GetName(), field.GetValue()); +} + +void TelemetryJson::Visit(const Telemetry::Field& field) { + impl->Serialize(field.GetType(), field.GetName(), field.GetValue()); +} + +void TelemetryJson::Visit(const Telemetry::Field& field) { + impl->Serialize(field.GetType(), field.GetName(), field.GetValue()); +} + +void TelemetryJson::Visit(const Telemetry::Field& field) { + impl->Serialize(field.GetType(), field.GetName(), field.GetValue()); +} + +void TelemetryJson::Visit(const Telemetry::Field& field) { + impl->Serialize(field.GetType(), field.GetName(), field.GetValue()); +} + +void TelemetryJson::Visit(const Telemetry::Field& field) { + impl->Serialize(field.GetType(), field.GetName(), field.GetValue()); +} + +void TelemetryJson::Visit(const Telemetry::Field& field) { + impl->Serialize(field.GetType(), field.GetName(), field.GetValue()); +} + +void TelemetryJson::Visit(const Telemetry::Field& field) { + impl->Serialize(field.GetType(), field.GetName(), field.GetValue()); +} + +void TelemetryJson::Visit(const Telemetry::Field& field) { + impl->Serialize(field.GetType(), field.GetName(), std::string(field.GetValue())); +} + +void TelemetryJson::Visit(const Telemetry::Field& field) { + impl->Serialize(field.GetType(), field.GetName(), field.GetValue().count()); +} + +void TelemetryJson::Complete() { + impl->SerializeSection(Telemetry::FieldType::App, "App"); + impl->SerializeSection(Telemetry::FieldType::Session, "Session"); + impl->SerializeSection(Telemetry::FieldType::Performance, "Performance"); + impl->SerializeSection(Telemetry::FieldType::UserConfig, "UserConfig"); + impl->SerializeSection(Telemetry::FieldType::UserSystem, "UserSystem"); + + auto content = impl->TopSection().dump(); + // Send the telemetry async but don't handle the errors since they were written to the log + Common::DetachedTasks::AddTask([host{impl->host}, content]() { + Client{host, "", ""}.PostJson("/telemetry", content, true); + }); +} + +bool TelemetryJson::SubmitTestcase() { + impl->SerializeSection(Telemetry::FieldType::App, "App"); + impl->SerializeSection(Telemetry::FieldType::Session, "Session"); + impl->SerializeSection(Telemetry::FieldType::UserFeedback, "UserFeedback"); + impl->SerializeSection(Telemetry::FieldType::UserSystem, "UserSystem"); + impl->SerializeSection(Telemetry::FieldType::UserConfig, "UserConfig"); + + auto content = impl->TopSection().dump(); + Client client(impl->host, impl->username, impl->token); + auto value = client.PostJson("/gamedb/testcase", content, false); + + return value.result_code == WebResult::Code::Success; +} + +} // namespace WebService diff --git a/src/web_service/telemetry_json.h b/src/web_service/telemetry_json.h new file mode 100644 index 0000000000..4cef345d55 --- /dev/null +++ b/src/web_service/telemetry_json.h @@ -0,0 +1,44 @@ +// SPDX-FileCopyrightText: 2017 Citra Emulator Project & 2024 suyu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include +#include +#include "common/telemetry.h" + +namespace WebService { + +/** + * Implementation of VisitorInterface that serialized telemetry into JSON, and submits it to the + * suyu web service + */ +class TelemetryJson : public Common::Telemetry::VisitorInterface { +public: + TelemetryJson(std::string host, std::string username, std::string token); + ~TelemetryJson() override; + + void Visit(const Common::Telemetry::Field& field) override; + void Visit(const Common::Telemetry::Field& field) override; + void Visit(const Common::Telemetry::Field& field) override; + void Visit(const Common::Telemetry::Field& field) override; + void Visit(const Common::Telemetry::Field& field) override; + void Visit(const Common::Telemetry::Field& field) override; + void Visit(const Common::Telemetry::Field& field) override; + void Visit(const Common::Telemetry::Field& field) override; + void Visit(const Common::Telemetry::Field& field) override; + void Visit(const Common::Telemetry::Field& field) override; + void Visit(const Common::Telemetry::Field& field) override; + void Visit(const Common::Telemetry::Field& field) override; + void Visit(const Common::Telemetry::Field& field) override; + void Visit(const Common::Telemetry::Field& field) override; + + void Complete() override; + bool SubmitTestcase() override; + +private: + struct Impl; + std::unique_ptr impl; +}; + +} // namespace WebService