diff options
Diffstat (limited to 'src')
133 files changed, 1631 insertions, 1085 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 3575a3cb3..0ac3d254e 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt | |||
| @@ -58,13 +58,11 @@ if (MSVC) | |||
| 58 | 58 | ||
| 59 | # Warnings | 59 | # Warnings |
| 60 | /W3 | 60 | /W3 |
| 61 | /we4018 # 'expression': signed/unsigned mismatch | 61 | /WX |
| 62 | |||
| 62 | /we4062 # Enumerator 'identifier' in a switch of enum 'enumeration' is not handled | 63 | /we4062 # Enumerator 'identifier' in a switch of enum 'enumeration' is not handled |
| 63 | /we4101 # 'identifier': unreferenced local variable | ||
| 64 | /we4189 # 'identifier': local variable is initialized but not referenced | 64 | /we4189 # 'identifier': local variable is initialized but not referenced |
| 65 | /we4265 # 'class': class has virtual functions, but destructor is not virtual | 65 | /we4265 # 'class': class has virtual functions, but destructor is not virtual |
| 66 | /we4267 # 'var': conversion from 'size_t' to 'type', possible loss of data | ||
| 67 | /we4305 # 'context': truncation from 'type1' to 'type2' | ||
| 68 | /we4388 # 'expression': signed/unsigned mismatch | 66 | /we4388 # 'expression': signed/unsigned mismatch |
| 69 | /we4389 # 'operator': signed/unsigned mismatch | 67 | /we4389 # 'operator': signed/unsigned mismatch |
| 70 | /we4456 # Declaration of 'identifier' hides previous local declaration | 68 | /we4456 # Declaration of 'identifier' hides previous local declaration |
| @@ -75,10 +73,13 @@ if (MSVC) | |||
| 75 | /we4547 # 'operator': operator before comma has no effect; expected operator with side-effect | 73 | /we4547 # 'operator': operator before comma has no effect; expected operator with side-effect |
| 76 | /we4549 # 'operator1': operator before comma has no effect; did you intend 'operator2'? | 74 | /we4549 # 'operator1': operator before comma has no effect; did you intend 'operator2'? |
| 77 | /we4555 # Expression has no effect; expected expression with side-effect | 75 | /we4555 # Expression has no effect; expected expression with side-effect |
| 78 | /we4715 # 'function': not all control paths return a value | 76 | /we4826 # Conversion from 'type1' to 'type2' is sign-extended. This may cause unexpected runtime behavior. |
| 79 | /we4834 # Discarding return value of function with 'nodiscard' attribute | ||
| 80 | /we5038 # data member 'member1' will be initialized after data member 'member2' | 77 | /we5038 # data member 'member1' will be initialized after data member 'member2' |
| 78 | /we5233 # explicit lambda capture 'identifier' is not used | ||
| 81 | /we5245 # 'function': unreferenced function with internal linkage has been removed | 79 | /we5245 # 'function': unreferenced function with internal linkage has been removed |
| 80 | |||
| 81 | /wd4100 # 'identifier': unreferenced formal parameter | ||
| 82 | /wd4324 # 'struct_name': structure was padded due to __declspec(align()) | ||
| 82 | ) | 83 | ) |
| 83 | 84 | ||
| 84 | if (USE_CCACHE) | 85 | if (USE_CCACHE) |
| @@ -99,24 +100,18 @@ if (MSVC) | |||
| 99 | set(CMAKE_EXE_LINKER_FLAGS_RELEASE "/DEBUG /MANIFEST:NO /INCREMENTAL:NO /OPT:REF,ICF" CACHE STRING "" FORCE) | 100 | set(CMAKE_EXE_LINKER_FLAGS_RELEASE "/DEBUG /MANIFEST:NO /INCREMENTAL:NO /OPT:REF,ICF" CACHE STRING "" FORCE) |
| 100 | else() | 101 | else() |
| 101 | add_compile_options( | 102 | add_compile_options( |
| 102 | -Wall | 103 | -Werror=all |
| 103 | -Werror=array-bounds | 104 | -Werror=extra |
| 104 | -Werror=implicit-fallthrough | ||
| 105 | -Werror=missing-declarations | 105 | -Werror=missing-declarations |
| 106 | -Werror=missing-field-initializers | ||
| 107 | -Werror=reorder | ||
| 108 | -Werror=shadow | 106 | -Werror=shadow |
| 109 | -Werror=sign-compare | 107 | -Werror=unused |
| 110 | -Werror=switch | 108 | |
| 111 | -Werror=uninitialized | ||
| 112 | -Werror=unused-function | ||
| 113 | -Werror=unused-result | ||
| 114 | -Werror=unused-variable | ||
| 115 | -Wextra | ||
| 116 | -Wmissing-declarations | ||
| 117 | -Wno-attributes | 109 | -Wno-attributes |
| 118 | -Wno-invalid-offsetof | 110 | -Wno-invalid-offsetof |
| 119 | -Wno-unused-parameter | 111 | -Wno-unused-parameter |
| 112 | |||
| 113 | $<$<CXX_COMPILER_ID:Clang>:-Wno-braced-scalar-init> | ||
| 114 | $<$<CXX_COMPILER_ID:Clang>:-Wno-unused-private-field> | ||
| 120 | ) | 115 | ) |
| 121 | 116 | ||
| 122 | if (ARCHITECTURE_x86_64) | 117 | if (ARCHITECTURE_x86_64) |
diff --git a/src/audio_core/CMakeLists.txt b/src/audio_core/CMakeLists.txt index 144f1bab2..0a1f3bf18 100644 --- a/src/audio_core/CMakeLists.txt +++ b/src/audio_core/CMakeLists.txt | |||
| @@ -206,20 +206,11 @@ if (MSVC) | |||
| 206 | /we4244 # 'conversion': conversion from 'type1' to 'type2', possible loss of data | 206 | /we4244 # 'conversion': conversion from 'type1' to 'type2', possible loss of data |
| 207 | /we4245 # 'conversion': conversion from 'type1' to 'type2', signed/unsigned mismatch | 207 | /we4245 # 'conversion': conversion from 'type1' to 'type2', signed/unsigned mismatch |
| 208 | /we4254 # 'operator': conversion from 'type1:field_bits' to 'type2:field_bits', possible loss of data | 208 | /we4254 # 'operator': conversion from 'type1:field_bits' to 'type2:field_bits', possible loss of data |
| 209 | /we4456 # Declaration of 'identifier' hides previous local declaration | 209 | /we4800 # Implicit conversion from 'type' to bool. Possible information loss |
| 210 | /we4457 # Declaration of 'identifier' hides function parameter | ||
| 211 | /we4458 # Declaration of 'identifier' hides class member | ||
| 212 | /we4459 # Declaration of 'identifier' hides global declaration | ||
| 213 | ) | 210 | ) |
| 214 | else() | 211 | else() |
| 215 | target_compile_options(audio_core PRIVATE | 212 | target_compile_options(audio_core PRIVATE |
| 216 | -Werror=conversion | 213 | -Werror=conversion |
| 217 | -Werror=ignored-qualifiers | ||
| 218 | -Werror=shadow | ||
| 219 | -Werror=unused-variable | ||
| 220 | |||
| 221 | $<$<CXX_COMPILER_ID:GNU>:-Werror=unused-but-set-parameter> | ||
| 222 | $<$<CXX_COMPILER_ID:GNU>:-Werror=unused-but-set-variable> | ||
| 223 | 214 | ||
| 224 | -Wno-sign-conversion | 215 | -Wno-sign-conversion |
| 225 | ) | 216 | ) |
diff --git a/src/audio_core/in/audio_in_system.cpp b/src/audio_core/in/audio_in_system.cpp index 6b7e6715c..4324cafd8 100644 --- a/src/audio_core/in/audio_in_system.cpp +++ b/src/audio_core/in/audio_in_system.cpp | |||
| @@ -56,7 +56,7 @@ Result System::IsConfigValid(const std::string_view device_name, | |||
| 56 | return ResultSuccess; | 56 | return ResultSuccess; |
| 57 | } | 57 | } |
| 58 | 58 | ||
| 59 | Result System::Initialize(std::string& device_name, const AudioInParameter& in_params, | 59 | Result System::Initialize(std::string device_name, const AudioInParameter& in_params, |
| 60 | const u32 handle_, const u64 applet_resource_user_id_) { | 60 | const u32 handle_, const u64 applet_resource_user_id_) { |
| 61 | auto result{IsConfigValid(device_name, in_params)}; | 61 | auto result{IsConfigValid(device_name, in_params)}; |
| 62 | if (result.IsError()) { | 62 | if (result.IsError()) { |
diff --git a/src/audio_core/in/audio_in_system.h b/src/audio_core/in/audio_in_system.h index b9dc0e60f..1c5154638 100644 --- a/src/audio_core/in/audio_in_system.h +++ b/src/audio_core/in/audio_in_system.h | |||
| @@ -97,7 +97,7 @@ public: | |||
| 97 | * @param applet_resource_user_id - Unused. | 97 | * @param applet_resource_user_id - Unused. |
| 98 | * @return Result code. | 98 | * @return Result code. |
| 99 | */ | 99 | */ |
| 100 | Result Initialize(std::string& device_name, const AudioInParameter& in_params, u32 handle, | 100 | Result Initialize(std::string device_name, const AudioInParameter& in_params, u32 handle, |
| 101 | u64 applet_resource_user_id); | 101 | u64 applet_resource_user_id); |
| 102 | 102 | ||
| 103 | /** | 103 | /** |
diff --git a/src/audio_core/out/audio_out_system.cpp b/src/audio_core/out/audio_out_system.cpp index 48a801923..a66208ed9 100644 --- a/src/audio_core/out/audio_out_system.cpp +++ b/src/audio_core/out/audio_out_system.cpp | |||
| @@ -49,8 +49,8 @@ Result System::IsConfigValid(std::string_view device_name, | |||
| 49 | return Service::Audio::ERR_INVALID_CHANNEL_COUNT; | 49 | return Service::Audio::ERR_INVALID_CHANNEL_COUNT; |
| 50 | } | 50 | } |
| 51 | 51 | ||
| 52 | Result System::Initialize(std::string& device_name, const AudioOutParameter& in_params, u32 handle_, | 52 | Result System::Initialize(std::string device_name, const AudioOutParameter& in_params, u32 handle_, |
| 53 | u64& applet_resource_user_id_) { | 53 | u64 applet_resource_user_id_) { |
| 54 | auto result = IsConfigValid(device_name, in_params); | 54 | auto result = IsConfigValid(device_name, in_params); |
| 55 | if (result.IsError()) { | 55 | if (result.IsError()) { |
| 56 | return result; | 56 | return result; |
diff --git a/src/audio_core/out/audio_out_system.h b/src/audio_core/out/audio_out_system.h index 0817b2f37..b95cb91be 100644 --- a/src/audio_core/out/audio_out_system.h +++ b/src/audio_core/out/audio_out_system.h | |||
| @@ -88,8 +88,8 @@ public: | |||
| 88 | * @param applet_resource_user_id - Unused. | 88 | * @param applet_resource_user_id - Unused. |
| 89 | * @return Result code. | 89 | * @return Result code. |
| 90 | */ | 90 | */ |
| 91 | Result Initialize(std::string& device_name, const AudioOutParameter& in_params, u32 handle, | 91 | Result Initialize(std::string device_name, const AudioOutParameter& in_params, u32 handle, |
| 92 | u64& applet_resource_user_id); | 92 | u64 applet_resource_user_id); |
| 93 | 93 | ||
| 94 | /** | 94 | /** |
| 95 | * Start this system. | 95 | * Start this system. |
diff --git a/src/audio_core/renderer/behavior/info_updater.cpp b/src/audio_core/renderer/behavior/info_updater.cpp index c0a307b89..574cf0982 100644 --- a/src/audio_core/renderer/behavior/info_updater.cpp +++ b/src/audio_core/renderer/behavior/info_updater.cpp | |||
| @@ -91,7 +91,7 @@ Result InfoUpdater::UpdateVoices(VoiceContext& voice_context, | |||
| 91 | voice_info.Initialize(); | 91 | voice_info.Initialize(); |
| 92 | 92 | ||
| 93 | for (u32 channel = 0; channel < in_param.channel_count; channel++) { | 93 | for (u32 channel = 0; channel < in_param.channel_count; channel++) { |
| 94 | std::memset(voice_states[channel], 0, sizeof(VoiceState)); | 94 | *voice_states[channel] = {}; |
| 95 | } | 95 | } |
| 96 | } | 96 | } |
| 97 | 97 | ||
diff --git a/src/audio_core/renderer/command/effect/biquad_filter.cpp b/src/audio_core/renderer/command/effect/biquad_filter.cpp index 1baae74fd..edb30ce72 100644 --- a/src/audio_core/renderer/command/effect/biquad_filter.cpp +++ b/src/audio_core/renderer/command/effect/biquad_filter.cpp | |||
| @@ -94,7 +94,7 @@ void BiquadFilterCommand::Dump([[maybe_unused]] const ADSP::CommandListProcessor | |||
| 94 | void BiquadFilterCommand::Process(const ADSP::CommandListProcessor& processor) { | 94 | void BiquadFilterCommand::Process(const ADSP::CommandListProcessor& processor) { |
| 95 | auto state_{reinterpret_cast<VoiceState::BiquadFilterState*>(state)}; | 95 | auto state_{reinterpret_cast<VoiceState::BiquadFilterState*>(state)}; |
| 96 | if (needs_init) { | 96 | if (needs_init) { |
| 97 | std::memset(state_, 0, sizeof(VoiceState::BiquadFilterState)); | 97 | *state_ = {}; |
| 98 | } | 98 | } |
| 99 | 99 | ||
| 100 | auto input_buffer{ | 100 | auto input_buffer{ |
diff --git a/src/audio_core/renderer/command/effect/multi_tap_biquad_filter.cpp b/src/audio_core/renderer/command/effect/multi_tap_biquad_filter.cpp index b3c3ba4ba..48a7cba8a 100644 --- a/src/audio_core/renderer/command/effect/multi_tap_biquad_filter.cpp +++ b/src/audio_core/renderer/command/effect/multi_tap_biquad_filter.cpp | |||
| @@ -30,7 +30,7 @@ void MultiTapBiquadFilterCommand::Process(const ADSP::CommandListProcessor& proc | |||
| 30 | for (u32 i = 0; i < filter_tap_count; i++) { | 30 | for (u32 i = 0; i < filter_tap_count; i++) { |
| 31 | auto state{reinterpret_cast<VoiceState::BiquadFilterState*>(states[i])}; | 31 | auto state{reinterpret_cast<VoiceState::BiquadFilterState*>(states[i])}; |
| 32 | if (needs_init[i]) { | 32 | if (needs_init[i]) { |
| 33 | std::memset(state, 0, sizeof(VoiceState::BiquadFilterState)); | 33 | *state = {}; |
| 34 | } | 34 | } |
| 35 | 35 | ||
| 36 | ApplyBiquadFilterFloat(output_buffer, input_buffer, biquads[i].b, biquads[i].a, *state, | 36 | ApplyBiquadFilterFloat(output_buffer, input_buffer, biquads[i].b, biquads[i].a, *state, |
diff --git a/src/audio_core/renderer/system.cpp b/src/audio_core/renderer/system.cpp index bde794cd1..4fac30c7c 100644 --- a/src/audio_core/renderer/system.cpp +++ b/src/audio_core/renderer/system.cpp | |||
| @@ -98,9 +98,8 @@ System::System(Core::System& core_, Kernel::KEvent* adsp_rendered_event_) | |||
| 98 | : core{core_}, adsp{core.AudioCore().GetADSP()}, adsp_rendered_event{adsp_rendered_event_} {} | 98 | : core{core_}, adsp{core.AudioCore().GetADSP()}, adsp_rendered_event{adsp_rendered_event_} {} |
| 99 | 99 | ||
| 100 | Result System::Initialize(const AudioRendererParameterInternal& params, | 100 | Result System::Initialize(const AudioRendererParameterInternal& params, |
| 101 | Kernel::KTransferMemory* transfer_memory, const u64 transfer_memory_size, | 101 | Kernel::KTransferMemory* transfer_memory, u64 transfer_memory_size, |
| 102 | const u32 process_handle_, const u64 applet_resource_user_id_, | 102 | u32 process_handle_, u64 applet_resource_user_id_, s32 session_id_) { |
| 103 | const s32 session_id_) { | ||
| 104 | if (!CheckValidRevision(params.revision)) { | 103 | if (!CheckValidRevision(params.revision)) { |
| 105 | return Service::Audio::ERR_INVALID_REVISION; | 104 | return Service::Audio::ERR_INVALID_REVISION; |
| 106 | } | 105 | } |
| @@ -354,6 +353,8 @@ Result System::Initialize(const AudioRendererParameterInternal& params, | |||
| 354 | 353 | ||
| 355 | render_time_limit_percent = 100; | 354 | render_time_limit_percent = 100; |
| 356 | drop_voice = params.voice_drop_enabled && params.execution_mode == ExecutionMode::Auto; | 355 | drop_voice = params.voice_drop_enabled && params.execution_mode == ExecutionMode::Auto; |
| 356 | drop_voice_param = 1.0f; | ||
| 357 | num_voices_dropped = 0; | ||
| 357 | 358 | ||
| 358 | allocator.Align(0x40); | 359 | allocator.Align(0x40); |
| 359 | command_workbuffer_size = allocator.GetRemainingSize(); | 360 | command_workbuffer_size = allocator.GetRemainingSize(); |
| @@ -547,7 +548,7 @@ u32 System::GetRenderingTimeLimit() const { | |||
| 547 | return render_time_limit_percent; | 548 | return render_time_limit_percent; |
| 548 | } | 549 | } |
| 549 | 550 | ||
| 550 | void System::SetRenderingTimeLimit(const u32 limit) { | 551 | void System::SetRenderingTimeLimit(u32 limit) { |
| 551 | render_time_limit_percent = limit; | 552 | render_time_limit_percent = limit; |
| 552 | } | 553 | } |
| 553 | 554 | ||
| @@ -635,7 +636,7 @@ void System::SendCommandToDsp() { | |||
| 635 | } | 636 | } |
| 636 | 637 | ||
| 637 | u64 System::GenerateCommand(std::span<u8> in_command_buffer, | 638 | u64 System::GenerateCommand(std::span<u8> in_command_buffer, |
| 638 | [[maybe_unused]] const u64 command_buffer_size_) { | 639 | [[maybe_unused]] u64 command_buffer_size_) { |
| 639 | PoolMapper::ClearUseState(memory_pool_workbuffer, memory_pool_count); | 640 | PoolMapper::ClearUseState(memory_pool_workbuffer, memory_pool_count); |
| 640 | const auto start_time{core.CoreTiming().GetClockTicks()}; | 641 | const auto start_time{core.CoreTiming().GetClockTicks()}; |
| 641 | 642 | ||
| @@ -693,7 +694,8 @@ u64 System::GenerateCommand(std::span<u8> in_command_buffer, | |||
| 693 | 694 | ||
| 694 | voice_context.SortInfo(); | 695 | voice_context.SortInfo(); |
| 695 | 696 | ||
| 696 | const auto start_estimated_time{command_buffer.estimated_process_time}; | 697 | const auto start_estimated_time{drop_voice_param * |
| 698 | static_cast<f32>(command_buffer.estimated_process_time)}; | ||
| 697 | 699 | ||
| 698 | command_generator.GenerateVoiceCommands(); | 700 | command_generator.GenerateVoiceCommands(); |
| 699 | command_generator.GenerateSubMixCommands(); | 701 | command_generator.GenerateSubMixCommands(); |
| @@ -712,11 +714,16 @@ u64 System::GenerateCommand(std::span<u8> in_command_buffer, | |||
| 712 | render_context.behavior->IsAudioRendererProcessingTimeLimit70PercentSupported(); | 714 | render_context.behavior->IsAudioRendererProcessingTimeLimit70PercentSupported(); |
| 713 | time_limit_percent = 70.0f; | 715 | time_limit_percent = 70.0f; |
| 714 | } | 716 | } |
| 717 | |||
| 718 | const auto end_estimated_time{drop_voice_param * | ||
| 719 | static_cast<f32>(command_buffer.estimated_process_time)}; | ||
| 720 | const auto estimated_time{start_estimated_time - end_estimated_time}; | ||
| 721 | |||
| 715 | const auto time_limit{static_cast<u32>( | 722 | const auto time_limit{static_cast<u32>( |
| 716 | static_cast<f32>(start_estimated_time - command_buffer.estimated_process_time) + | 723 | estimated_time + (((time_limit_percent / 100.0f) * 2'880'000.0) * |
| 717 | (((time_limit_percent / 100.0f) * 2'880'000.0) * | 724 | (static_cast<f32>(render_time_limit_percent) / 100.0f)))}; |
| 718 | (static_cast<f32>(render_time_limit_percent) / 100.0f)))}; | 725 | num_voices_dropped = |
| 719 | num_voices_dropped = DropVoices(command_buffer, start_estimated_time, time_limit); | 726 | DropVoices(command_buffer, static_cast<u32>(start_estimated_time), time_limit); |
| 720 | } | 727 | } |
| 721 | 728 | ||
| 722 | command_list_header->buffer_size = command_buffer.size; | 729 | command_list_header->buffer_size = command_buffer.size; |
| @@ -737,24 +744,33 @@ u64 System::GenerateCommand(std::span<u8> in_command_buffer, | |||
| 737 | return command_buffer.size; | 744 | return command_buffer.size; |
| 738 | } | 745 | } |
| 739 | 746 | ||
| 740 | u32 System::DropVoices(CommandBuffer& command_buffer, const u32 estimated_process_time, | 747 | f32 System::GetVoiceDropParameter() const { |
| 741 | const u32 time_limit) { | 748 | return drop_voice_param; |
| 749 | } | ||
| 750 | |||
| 751 | void System::SetVoiceDropParameter(f32 voice_drop_) { | ||
| 752 | drop_voice_param = voice_drop_; | ||
| 753 | } | ||
| 754 | |||
| 755 | u32 System::DropVoices(CommandBuffer& command_buffer, u32 estimated_process_time, u32 time_limit) { | ||
| 742 | u32 i{0}; | 756 | u32 i{0}; |
| 743 | auto command_list{command_buffer.command_list.data() + sizeof(CommandListHeader)}; | 757 | auto command_list{command_buffer.command_list.data() + sizeof(CommandListHeader)}; |
| 744 | ICommand* cmd{}; | 758 | ICommand* cmd{nullptr}; |
| 745 | 759 | ||
| 746 | for (; i < command_buffer.count; i++) { | 760 | // Find a first valid voice to drop |
| 761 | while (i < command_buffer.count) { | ||
| 747 | cmd = reinterpret_cast<ICommand*>(command_list); | 762 | cmd = reinterpret_cast<ICommand*>(command_list); |
| 748 | if (cmd->type != CommandId::Performance && | 763 | if (cmd->type == CommandId::Performance || |
| 749 | cmd->type != CommandId::DataSourcePcmInt16Version1 && | 764 | cmd->type == CommandId::DataSourcePcmInt16Version1 || |
| 750 | cmd->type != CommandId::DataSourcePcmInt16Version2 && | 765 | cmd->type == CommandId::DataSourcePcmInt16Version2 || |
| 751 | cmd->type != CommandId::DataSourcePcmFloatVersion1 && | 766 | cmd->type == CommandId::DataSourcePcmFloatVersion1 || |
| 752 | cmd->type != CommandId::DataSourcePcmFloatVersion2 && | 767 | cmd->type == CommandId::DataSourcePcmFloatVersion2 || |
| 753 | cmd->type != CommandId::DataSourceAdpcmVersion1 && | 768 | cmd->type == CommandId::DataSourceAdpcmVersion1 || |
| 754 | cmd->type != CommandId::DataSourceAdpcmVersion2) { | 769 | cmd->type == CommandId::DataSourceAdpcmVersion2) { |
| 755 | break; | 770 | break; |
| 756 | } | 771 | } |
| 757 | command_list += cmd->size; | 772 | command_list += cmd->size; |
| 773 | i++; | ||
| 758 | } | 774 | } |
| 759 | 775 | ||
| 760 | if (cmd == nullptr || command_buffer.count == 0 || i >= command_buffer.count) { | 776 | if (cmd == nullptr || command_buffer.count == 0 || i >= command_buffer.count) { |
| @@ -767,6 +783,7 @@ u32 System::DropVoices(CommandBuffer& command_buffer, const u32 estimated_proces | |||
| 767 | const auto node_id_type{cmd->node_id >> 28}; | 783 | const auto node_id_type{cmd->node_id >> 28}; |
| 768 | const auto node_id_base{cmd->node_id & 0xFFF}; | 784 | const auto node_id_base{cmd->node_id & 0xFFF}; |
| 769 | 785 | ||
| 786 | // If the new estimated process time falls below the limit, we're done dropping. | ||
| 770 | if (estimated_process_time <= time_limit) { | 787 | if (estimated_process_time <= time_limit) { |
| 771 | break; | 788 | break; |
| 772 | } | 789 | } |
| @@ -775,6 +792,7 @@ u32 System::DropVoices(CommandBuffer& command_buffer, const u32 estimated_proces | |||
| 775 | break; | 792 | break; |
| 776 | } | 793 | } |
| 777 | 794 | ||
| 795 | // Don't drop voices marked with the highest priority. | ||
| 778 | auto& voice_info{voice_context.GetInfo(node_id_base)}; | 796 | auto& voice_info{voice_context.GetInfo(node_id_base)}; |
| 779 | if (voice_info.priority == HighestVoicePriority) { | 797 | if (voice_info.priority == HighestVoicePriority) { |
| 780 | break; | 798 | break; |
| @@ -783,18 +801,23 @@ u32 System::DropVoices(CommandBuffer& command_buffer, const u32 estimated_proces | |||
| 783 | voices_dropped++; | 801 | voices_dropped++; |
| 784 | voice_info.voice_dropped = true; | 802 | voice_info.voice_dropped = true; |
| 785 | 803 | ||
| 786 | if (i < command_buffer.count) { | 804 | // First iteration should drop the voice, and then iterate through all of the commands tied |
| 787 | while (cmd->node_id == node_id) { | 805 | // to the voice. We don't need reverb on a voice which we've just removed, for example. |
| 788 | if (cmd->type == CommandId::DepopPrepare) { | 806 | // Depops can't be removed otherwise we'll introduce audio popping, and we don't |
| 789 | cmd->enabled = true; | 807 | // remove perf commands. Lower the estimated time for each command dropped. |
| 790 | } else if (cmd->type == CommandId::Performance || !cmd->enabled) { | 808 | while (i < command_buffer.count && cmd->node_id == node_id) { |
| 791 | cmd->enabled = false; | 809 | if (cmd->type == CommandId::DepopPrepare) { |
| 792 | } | 810 | cmd->enabled = true; |
| 793 | i++; | 811 | } else if (cmd->enabled && cmd->type != CommandId::Performance) { |
| 794 | command_list += cmd->size; | 812 | cmd->enabled = false; |
| 795 | cmd = reinterpret_cast<ICommand*>(command_list); | 813 | estimated_process_time -= static_cast<u32>( |
| 814 | drop_voice_param * static_cast<f32>(cmd->estimated_process_time)); | ||
| 796 | } | 815 | } |
| 816 | command_list += cmd->size; | ||
| 817 | cmd = reinterpret_cast<ICommand*>(command_list); | ||
| 818 | i++; | ||
| 797 | } | 819 | } |
| 820 | i++; | ||
| 798 | } | 821 | } |
| 799 | return voices_dropped; | 822 | return voices_dropped; |
| 800 | } | 823 | } |
diff --git a/src/audio_core/renderer/system.h b/src/audio_core/renderer/system.h index bcbe65b07..429196e41 100644 --- a/src/audio_core/renderer/system.h +++ b/src/audio_core/renderer/system.h | |||
| @@ -196,6 +196,20 @@ public: | |||
| 196 | */ | 196 | */ |
| 197 | u32 DropVoices(CommandBuffer& command_buffer, u32 estimated_process_time, u32 time_limit); | 197 | u32 DropVoices(CommandBuffer& command_buffer, u32 estimated_process_time, u32 time_limit); |
| 198 | 198 | ||
| 199 | /** | ||
| 200 | * Get the current voice drop parameter. | ||
| 201 | * | ||
| 202 | * @return The current voice drop. | ||
| 203 | */ | ||
| 204 | f32 GetVoiceDropParameter() const; | ||
| 205 | |||
| 206 | /** | ||
| 207 | * Set the voice drop parameter. | ||
| 208 | * | ||
| 209 | * @param The new voice drop. | ||
| 210 | */ | ||
| 211 | void SetVoiceDropParameter(f32 voice_drop); | ||
| 212 | |||
| 199 | private: | 213 | private: |
| 200 | /// Core system | 214 | /// Core system |
| 201 | Core::System& core; | 215 | Core::System& core; |
| @@ -301,6 +315,8 @@ private: | |||
| 301 | u32 num_voices_dropped{}; | 315 | u32 num_voices_dropped{}; |
| 302 | /// Tick that rendering started | 316 | /// Tick that rendering started |
| 303 | u64 render_start_tick{}; | 317 | u64 render_start_tick{}; |
| 318 | /// Parameter to control the threshold for dropping voices if the audio graph gets too large | ||
| 319 | f32 drop_voice_param{1.0f}; | ||
| 304 | }; | 320 | }; |
| 305 | 321 | ||
| 306 | } // namespace AudioRenderer | 322 | } // namespace AudioRenderer |
diff --git a/src/audio_core/renderer/voice/voice_context.cpp b/src/audio_core/renderer/voice/voice_context.cpp index eafb51b01..a501a677d 100644 --- a/src/audio_core/renderer/voice/voice_context.cpp +++ b/src/audio_core/renderer/voice/voice_context.cpp | |||
| @@ -74,8 +74,8 @@ void VoiceContext::SortInfo() { | |||
| 74 | } | 74 | } |
| 75 | 75 | ||
| 76 | std::ranges::sort(sorted_voice_info, [](const VoiceInfo* a, const VoiceInfo* b) { | 76 | std::ranges::sort(sorted_voice_info, [](const VoiceInfo* a, const VoiceInfo* b) { |
| 77 | return a->priority != b->priority ? a->priority < b->priority | 77 | return a->priority != b->priority ? a->priority > b->priority |
| 78 | : a->sort_order < b->sort_order; | 78 | : a->sort_order > b->sort_order; |
| 79 | }); | 79 | }); |
| 80 | } | 80 | } |
| 81 | 81 | ||
diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index 46cf75fde..c0555f840 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt | |||
| @@ -156,12 +156,13 @@ if (MSVC) | |||
| 156 | ) | 156 | ) |
| 157 | target_compile_options(common PRIVATE | 157 | target_compile_options(common PRIVATE |
| 158 | /W4 | 158 | /W4 |
| 159 | /WX | 159 | |
| 160 | /we4242 # 'identifier': conversion from 'type1' to 'type2', possible loss of data | ||
| 161 | /we4254 # 'operator': conversion from 'type1:field_bits' to 'type2:field_bits', possible loss of data | ||
| 162 | /we4800 # Implicit conversion from 'type' to bool. Possible information loss | ||
| 160 | ) | 163 | ) |
| 161 | else() | 164 | else() |
| 162 | target_compile_options(common PRIVATE | 165 | target_compile_options(common PRIVATE |
| 163 | -Werror | ||
| 164 | |||
| 165 | $<$<CXX_COMPILER_ID:Clang>:-fsized-deallocation> | 166 | $<$<CXX_COMPILER_ID:Clang>:-fsized-deallocation> |
| 166 | ) | 167 | ) |
| 167 | endif() | 168 | endif() |
diff --git a/src/common/bit_field.h b/src/common/bit_field.h index 7e1df62b1..e4e58ea45 100644 --- a/src/common/bit_field.h +++ b/src/common/bit_field.h | |||
| @@ -141,10 +141,6 @@ public: | |||
| 141 | constexpr BitField(BitField&&) noexcept = default; | 141 | constexpr BitField(BitField&&) noexcept = default; |
| 142 | constexpr BitField& operator=(BitField&&) noexcept = default; | 142 | constexpr BitField& operator=(BitField&&) noexcept = default; |
| 143 | 143 | ||
| 144 | [[nodiscard]] constexpr operator T() const { | ||
| 145 | return Value(); | ||
| 146 | } | ||
| 147 | |||
| 148 | constexpr void Assign(const T& value) { | 144 | constexpr void Assign(const T& value) { |
| 149 | #ifdef _MSC_VER | 145 | #ifdef _MSC_VER |
| 150 | storage = static_cast<StorageType>((storage & ~mask) | FormatValue(value)); | 146 | storage = static_cast<StorageType>((storage & ~mask) | FormatValue(value)); |
| @@ -162,6 +158,17 @@ public: | |||
| 162 | return ExtractValue(storage); | 158 | return ExtractValue(storage); |
| 163 | } | 159 | } |
| 164 | 160 | ||
| 161 | template <typename ConvertedToType> | ||
| 162 | [[nodiscard]] constexpr ConvertedToType As() const { | ||
| 163 | static_assert(!std::is_same_v<T, ConvertedToType>, | ||
| 164 | "Unnecessary cast. Use Value() instead."); | ||
| 165 | return static_cast<ConvertedToType>(Value()); | ||
| 166 | } | ||
| 167 | |||
| 168 | [[nodiscard]] constexpr operator T() const { | ||
| 169 | return Value(); | ||
| 170 | } | ||
| 171 | |||
| 165 | [[nodiscard]] constexpr explicit operator bool() const { | 172 | [[nodiscard]] constexpr explicit operator bool() const { |
| 166 | return Value() != 0; | 173 | return Value() != 0; |
| 167 | } | 174 | } |
diff --git a/src/common/bounded_threadsafe_queue.h b/src/common/bounded_threadsafe_queue.h index 7e465549b..21217801e 100644 --- a/src/common/bounded_threadsafe_queue.h +++ b/src/common/bounded_threadsafe_queue.h | |||
| @@ -21,11 +21,6 @@ constexpr size_t hardware_interference_size = std::hardware_destructive_interfer | |||
| 21 | constexpr size_t hardware_interference_size = 64; | 21 | constexpr size_t hardware_interference_size = 64; |
| 22 | #endif | 22 | #endif |
| 23 | 23 | ||
| 24 | #ifdef _MSC_VER | ||
| 25 | #pragma warning(push) | ||
| 26 | #pragma warning(disable : 4324) | ||
| 27 | #endif | ||
| 28 | |||
| 29 | template <typename T, size_t capacity = 0x400> | 24 | template <typename T, size_t capacity = 0x400> |
| 30 | class MPSCQueue { | 25 | class MPSCQueue { |
| 31 | public: | 26 | public: |
| @@ -160,8 +155,4 @@ private: | |||
| 160 | static_assert(std::is_nothrow_destructible_v<T>, "T must be nothrow destructible"); | 155 | static_assert(std::is_nothrow_destructible_v<T>, "T must be nothrow destructible"); |
| 161 | }; | 156 | }; |
| 162 | 157 | ||
| 163 | #ifdef _MSC_VER | ||
| 164 | #pragma warning(pop) | ||
| 165 | #endif | ||
| 166 | |||
| 167 | } // namespace Common | 158 | } // namespace Common |
diff --git a/src/common/concepts.h b/src/common/concepts.h index a97555f6a..a9acff3e7 100644 --- a/src/common/concepts.h +++ b/src/common/concepts.h | |||
| @@ -3,24 +3,14 @@ | |||
| 3 | 3 | ||
| 4 | #pragma once | 4 | #pragma once |
| 5 | 5 | ||
| 6 | #include <iterator> | ||
| 6 | #include <type_traits> | 7 | #include <type_traits> |
| 7 | 8 | ||
| 8 | namespace Common { | 9 | namespace Common { |
| 9 | 10 | ||
| 10 | // Check if type is like an STL container | 11 | // Check if type satisfies the ContiguousContainer named requirement. |
| 11 | template <typename T> | 12 | template <typename T> |
| 12 | concept IsSTLContainer = requires(T t) { | 13 | concept IsContiguousContainer = std::contiguous_iterator<typename T::iterator>; |
| 13 | typename T::value_type; | ||
| 14 | typename T::iterator; | ||
| 15 | typename T::const_iterator; | ||
| 16 | // TODO(ogniK): Replace below is std::same_as<void> when MSVC supports it. | ||
| 17 | t.begin(); | ||
| 18 | t.end(); | ||
| 19 | t.cbegin(); | ||
| 20 | t.cend(); | ||
| 21 | t.data(); | ||
| 22 | t.size(); | ||
| 23 | }; | ||
| 24 | 14 | ||
| 25 | // TODO: Replace with std::derived_from when the <concepts> header | 15 | // TODO: Replace with std::derived_from when the <concepts> header |
| 26 | // is available on all supported platforms. | 16 | // is available on all supported platforms. |
| @@ -34,4 +24,12 @@ concept DerivedFrom = requires { | |||
| 34 | template <typename From, typename To> | 24 | template <typename From, typename To> |
| 35 | concept ConvertibleTo = std::is_convertible_v<From, To>; | 25 | concept ConvertibleTo = std::is_convertible_v<From, To>; |
| 36 | 26 | ||
| 27 | // No equivalents in the stdlib | ||
| 28 | |||
| 29 | template <typename T> | ||
| 30 | concept IsArithmetic = std::is_arithmetic_v<T>; | ||
| 31 | |||
| 32 | template <typename T> | ||
| 33 | concept IsIntegral = std::is_integral_v<T>; | ||
| 34 | |||
| 37 | } // namespace Common | 35 | } // namespace Common |
diff --git a/src/common/fixed_point.h b/src/common/fixed_point.h index 6eb6afe2f..f899b0d54 100644 --- a/src/common/fixed_point.h +++ b/src/common/fixed_point.h | |||
| @@ -12,6 +12,8 @@ | |||
| 12 | #include <ostream> | 12 | #include <ostream> |
| 13 | #include <type_traits> | 13 | #include <type_traits> |
| 14 | 14 | ||
| 15 | #include <common/concepts.h> | ||
| 16 | |||
| 15 | namespace Common { | 17 | namespace Common { |
| 16 | 18 | ||
| 17 | template <size_t I, size_t F> | 19 | template <size_t I, size_t F> |
| @@ -50,8 +52,8 @@ struct type_from_size<64> { | |||
| 50 | static constexpr size_t size = 64; | 52 | static constexpr size_t size = 64; |
| 51 | 53 | ||
| 52 | using value_type = int64_t; | 54 | using value_type = int64_t; |
| 53 | using unsigned_type = std::make_unsigned<value_type>::type; | 55 | using unsigned_type = std::make_unsigned_t<value_type>; |
| 54 | using signed_type = std::make_signed<value_type>::type; | 56 | using signed_type = std::make_signed_t<value_type>; |
| 55 | using next_size = type_from_size<128>; | 57 | using next_size = type_from_size<128>; |
| 56 | }; | 58 | }; |
| 57 | 59 | ||
| @@ -61,8 +63,8 @@ struct type_from_size<32> { | |||
| 61 | static constexpr size_t size = 32; | 63 | static constexpr size_t size = 32; |
| 62 | 64 | ||
| 63 | using value_type = int32_t; | 65 | using value_type = int32_t; |
| 64 | using unsigned_type = std::make_unsigned<value_type>::type; | 66 | using unsigned_type = std::make_unsigned_t<value_type>; |
| 65 | using signed_type = std::make_signed<value_type>::type; | 67 | using signed_type = std::make_signed_t<value_type>; |
| 66 | using next_size = type_from_size<64>; | 68 | using next_size = type_from_size<64>; |
| 67 | }; | 69 | }; |
| 68 | 70 | ||
| @@ -72,8 +74,8 @@ struct type_from_size<16> { | |||
| 72 | static constexpr size_t size = 16; | 74 | static constexpr size_t size = 16; |
| 73 | 75 | ||
| 74 | using value_type = int16_t; | 76 | using value_type = int16_t; |
| 75 | using unsigned_type = std::make_unsigned<value_type>::type; | 77 | using unsigned_type = std::make_unsigned_t<value_type>; |
| 76 | using signed_type = std::make_signed<value_type>::type; | 78 | using signed_type = std::make_signed_t<value_type>; |
| 77 | using next_size = type_from_size<32>; | 79 | using next_size = type_from_size<32>; |
| 78 | }; | 80 | }; |
| 79 | 81 | ||
| @@ -83,8 +85,8 @@ struct type_from_size<8> { | |||
| 83 | static constexpr size_t size = 8; | 85 | static constexpr size_t size = 8; |
| 84 | 86 | ||
| 85 | using value_type = int8_t; | 87 | using value_type = int8_t; |
| 86 | using unsigned_type = std::make_unsigned<value_type>::type; | 88 | using unsigned_type = std::make_unsigned_t<value_type>; |
| 87 | using signed_type = std::make_signed<value_type>::type; | 89 | using signed_type = std::make_signed_t<value_type>; |
| 88 | using next_size = type_from_size<16>; | 90 | using next_size = type_from_size<16>; |
| 89 | }; | 91 | }; |
| 90 | 92 | ||
| @@ -101,7 +103,7 @@ struct divide_by_zero : std::exception {}; | |||
| 101 | template <size_t I, size_t F> | 103 | template <size_t I, size_t F> |
| 102 | constexpr FixedPoint<I, F> divide( | 104 | constexpr FixedPoint<I, F> divide( |
| 103 | FixedPoint<I, F> numerator, FixedPoint<I, F> denominator, FixedPoint<I, F>& remainder, | 105 | FixedPoint<I, F> numerator, FixedPoint<I, F> denominator, FixedPoint<I, F>& remainder, |
| 104 | typename std::enable_if<type_from_size<I + F>::next_size::is_specialized>::type* = nullptr) { | 106 | std::enable_if_t<type_from_size<I + F>::next_size::is_specialized>* = nullptr) { |
| 105 | 107 | ||
| 106 | using next_type = typename FixedPoint<I, F>::next_type; | 108 | using next_type = typename FixedPoint<I, F>::next_type; |
| 107 | using base_type = typename FixedPoint<I, F>::base_type; | 109 | using base_type = typename FixedPoint<I, F>::base_type; |
| @@ -121,7 +123,7 @@ constexpr FixedPoint<I, F> divide( | |||
| 121 | template <size_t I, size_t F> | 123 | template <size_t I, size_t F> |
| 122 | constexpr FixedPoint<I, F> divide( | 124 | constexpr FixedPoint<I, F> divide( |
| 123 | FixedPoint<I, F> numerator, FixedPoint<I, F> denominator, FixedPoint<I, F>& remainder, | 125 | FixedPoint<I, F> numerator, FixedPoint<I, F> denominator, FixedPoint<I, F>& remainder, |
| 124 | typename std::enable_if<!type_from_size<I + F>::next_size::is_specialized>::type* = nullptr) { | 126 | std::enable_if_t<!type_from_size<I + F>::next_size::is_specialized>* = nullptr) { |
| 125 | 127 | ||
| 126 | using unsigned_type = typename FixedPoint<I, F>::unsigned_type; | 128 | using unsigned_type = typename FixedPoint<I, F>::unsigned_type; |
| 127 | 129 | ||
| @@ -191,7 +193,7 @@ constexpr FixedPoint<I, F> divide( | |||
| 191 | template <size_t I, size_t F> | 193 | template <size_t I, size_t F> |
| 192 | constexpr FixedPoint<I, F> multiply( | 194 | constexpr FixedPoint<I, F> multiply( |
| 193 | FixedPoint<I, F> lhs, FixedPoint<I, F> rhs, | 195 | FixedPoint<I, F> lhs, FixedPoint<I, F> rhs, |
| 194 | typename std::enable_if<type_from_size<I + F>::next_size::is_specialized>::type* = nullptr) { | 196 | std::enable_if_t<type_from_size<I + F>::next_size::is_specialized>* = nullptr) { |
| 195 | 197 | ||
| 196 | using next_type = typename FixedPoint<I, F>::next_type; | 198 | using next_type = typename FixedPoint<I, F>::next_type; |
| 197 | using base_type = typename FixedPoint<I, F>::base_type; | 199 | using base_type = typename FixedPoint<I, F>::base_type; |
| @@ -210,7 +212,7 @@ constexpr FixedPoint<I, F> multiply( | |||
| 210 | template <size_t I, size_t F> | 212 | template <size_t I, size_t F> |
| 211 | constexpr FixedPoint<I, F> multiply( | 213 | constexpr FixedPoint<I, F> multiply( |
| 212 | FixedPoint<I, F> lhs, FixedPoint<I, F> rhs, | 214 | FixedPoint<I, F> lhs, FixedPoint<I, F> rhs, |
| 213 | typename std::enable_if<!type_from_size<I + F>::next_size::is_specialized>::type* = nullptr) { | 215 | std::enable_if_t<!type_from_size<I + F>::next_size::is_specialized>* = nullptr) { |
| 214 | 216 | ||
| 215 | using base_type = typename FixedPoint<I, F>::base_type; | 217 | using base_type = typename FixedPoint<I, F>::base_type; |
| 216 | 218 | ||
| @@ -265,15 +267,16 @@ public: | |||
| 265 | static constexpr base_type one = base_type(1) << fractional_bits; | 267 | static constexpr base_type one = base_type(1) << fractional_bits; |
| 266 | 268 | ||
| 267 | public: // constructors | 269 | public: // constructors |
| 268 | FixedPoint() = default; | 270 | constexpr FixedPoint() = default; |
| 269 | FixedPoint(const FixedPoint&) = default; | 271 | |
| 270 | FixedPoint(FixedPoint&&) = default; | 272 | constexpr FixedPoint(const FixedPoint&) = default; |
| 271 | FixedPoint& operator=(const FixedPoint&) = default; | 273 | constexpr FixedPoint& operator=(const FixedPoint&) = default; |
| 272 | 274 | ||
| 273 | template <class Number> | 275 | constexpr FixedPoint(FixedPoint&&) noexcept = default; |
| 274 | constexpr FixedPoint( | 276 | constexpr FixedPoint& operator=(FixedPoint&&) noexcept = default; |
| 275 | Number n, typename std::enable_if<std::is_arithmetic<Number>::value>::type* = nullptr) | 277 | |
| 276 | : data_(static_cast<base_type>(n * one)) {} | 278 | template <IsArithmetic Number> |
| 279 | constexpr FixedPoint(Number n) : data_(static_cast<base_type>(n * one)) {} | ||
| 277 | 280 | ||
| 278 | public: // conversion | 281 | public: // conversion |
| 279 | template <size_t I2, size_t F2> | 282 | template <size_t I2, size_t F2> |
| @@ -301,36 +304,14 @@ public: | |||
| 301 | } | 304 | } |
| 302 | 305 | ||
| 303 | public: // comparison operators | 306 | public: // comparison operators |
| 304 | constexpr bool operator==(FixedPoint rhs) const { | 307 | friend constexpr auto operator<=>(FixedPoint lhs, FixedPoint rhs) = default; |
| 305 | return data_ == rhs.data_; | ||
| 306 | } | ||
| 307 | |||
| 308 | constexpr bool operator!=(FixedPoint rhs) const { | ||
| 309 | return data_ != rhs.data_; | ||
| 310 | } | ||
| 311 | |||
| 312 | constexpr bool operator<(FixedPoint rhs) const { | ||
| 313 | return data_ < rhs.data_; | ||
| 314 | } | ||
| 315 | |||
| 316 | constexpr bool operator>(FixedPoint rhs) const { | ||
| 317 | return data_ > rhs.data_; | ||
| 318 | } | ||
| 319 | |||
| 320 | constexpr bool operator<=(FixedPoint rhs) const { | ||
| 321 | return data_ <= rhs.data_; | ||
| 322 | } | ||
| 323 | |||
| 324 | constexpr bool operator>=(FixedPoint rhs) const { | ||
| 325 | return data_ >= rhs.data_; | ||
| 326 | } | ||
| 327 | 308 | ||
| 328 | public: // unary operators | 309 | public: // unary operators |
| 329 | constexpr bool operator!() const { | 310 | [[nodiscard]] constexpr bool operator!() const { |
| 330 | return !data_; | 311 | return !data_; |
| 331 | } | 312 | } |
| 332 | 313 | ||
| 333 | constexpr FixedPoint operator~() const { | 314 | [[nodiscard]] constexpr FixedPoint operator~() const { |
| 334 | // NOTE(eteran): this will often appear to "just negate" the value | 315 | // NOTE(eteran): this will often appear to "just negate" the value |
| 335 | // that is not an error, it is because -x == (~x+1) | 316 | // that is not an error, it is because -x == (~x+1) |
| 336 | // and that "+1" is adding an infinitesimally small fraction to the | 317 | // and that "+1" is adding an infinitesimally small fraction to the |
| @@ -338,11 +319,11 @@ public: // unary operators | |||
| 338 | return FixedPoint::from_base(~data_); | 319 | return FixedPoint::from_base(~data_); |
| 339 | } | 320 | } |
| 340 | 321 | ||
| 341 | constexpr FixedPoint operator-() const { | 322 | [[nodiscard]] constexpr FixedPoint operator-() const { |
| 342 | return FixedPoint::from_base(-data_); | 323 | return FixedPoint::from_base(-data_); |
| 343 | } | 324 | } |
| 344 | 325 | ||
| 345 | constexpr FixedPoint operator+() const { | 326 | [[nodiscard]] constexpr FixedPoint operator+() const { |
| 346 | return FixedPoint::from_base(+data_); | 327 | return FixedPoint::from_base(+data_); |
| 347 | } | 328 | } |
| 348 | 329 | ||
| @@ -411,15 +392,13 @@ public: // binary math operators, effects underlying bit pattern since these | |||
| 411 | return *this; | 392 | return *this; |
| 412 | } | 393 | } |
| 413 | 394 | ||
| 414 | template <class Integer, | 395 | template <IsIntegral Integer> |
| 415 | class = typename std::enable_if<std::is_integral<Integer>::value>::type> | ||
| 416 | constexpr FixedPoint& operator>>=(Integer n) { | 396 | constexpr FixedPoint& operator>>=(Integer n) { |
| 417 | data_ >>= n; | 397 | data_ >>= n; |
| 418 | return *this; | 398 | return *this; |
| 419 | } | 399 | } |
| 420 | 400 | ||
| 421 | template <class Integer, | 401 | template <IsIntegral Integer> |
| 422 | class = typename std::enable_if<std::is_integral<Integer>::value>::type> | ||
| 423 | constexpr FixedPoint& operator<<=(Integer n) { | 402 | constexpr FixedPoint& operator<<=(Integer n) { |
| 424 | data_ <<= n; | 403 | data_ <<= n; |
| 425 | return *this; | 404 | return *this; |
| @@ -430,42 +409,42 @@ public: // conversion to basic types | |||
| 430 | data_ += (data_ & fractional_mask) >> 1; | 409 | data_ += (data_ & fractional_mask) >> 1; |
| 431 | } | 410 | } |
| 432 | 411 | ||
| 433 | constexpr int to_int() { | 412 | [[nodiscard]] constexpr int to_int() { |
| 434 | round_up(); | 413 | round_up(); |
| 435 | return static_cast<int>((data_ & integer_mask) >> fractional_bits); | 414 | return static_cast<int>((data_ & integer_mask) >> fractional_bits); |
| 436 | } | 415 | } |
| 437 | 416 | ||
| 438 | constexpr unsigned int to_uint() const { | 417 | [[nodiscard]] constexpr unsigned int to_uint() { |
| 439 | round_up(); | 418 | round_up(); |
| 440 | return static_cast<unsigned int>((data_ & integer_mask) >> fractional_bits); | 419 | return static_cast<unsigned int>((data_ & integer_mask) >> fractional_bits); |
| 441 | } | 420 | } |
| 442 | 421 | ||
| 443 | constexpr int64_t to_long() { | 422 | [[nodiscard]] constexpr int64_t to_long() { |
| 444 | round_up(); | 423 | round_up(); |
| 445 | return static_cast<int64_t>((data_ & integer_mask) >> fractional_bits); | 424 | return static_cast<int64_t>((data_ & integer_mask) >> fractional_bits); |
| 446 | } | 425 | } |
| 447 | 426 | ||
| 448 | constexpr int to_int_floor() const { | 427 | [[nodiscard]] constexpr int to_int_floor() const { |
| 449 | return static_cast<int>((data_ & integer_mask) >> fractional_bits); | 428 | return static_cast<int>((data_ & integer_mask) >> fractional_bits); |
| 450 | } | 429 | } |
| 451 | 430 | ||
| 452 | constexpr int64_t to_long_floor() { | 431 | [[nodiscard]] constexpr int64_t to_long_floor() const { |
| 453 | return static_cast<int64_t>((data_ & integer_mask) >> fractional_bits); | 432 | return static_cast<int64_t>((data_ & integer_mask) >> fractional_bits); |
| 454 | } | 433 | } |
| 455 | 434 | ||
| 456 | constexpr unsigned int to_uint_floor() const { | 435 | [[nodiscard]] constexpr unsigned int to_uint_floor() const { |
| 457 | return static_cast<unsigned int>((data_ & integer_mask) >> fractional_bits); | 436 | return static_cast<unsigned int>((data_ & integer_mask) >> fractional_bits); |
| 458 | } | 437 | } |
| 459 | 438 | ||
| 460 | constexpr float to_float() const { | 439 | [[nodiscard]] constexpr float to_float() const { |
| 461 | return static_cast<float>(data_) / FixedPoint::one; | 440 | return static_cast<float>(data_) / FixedPoint::one; |
| 462 | } | 441 | } |
| 463 | 442 | ||
| 464 | constexpr double to_double() const { | 443 | [[nodiscard]] constexpr double to_double() const { |
| 465 | return static_cast<double>(data_) / FixedPoint::one; | 444 | return static_cast<double>(data_) / FixedPoint::one; |
| 466 | } | 445 | } |
| 467 | 446 | ||
| 468 | constexpr base_type to_raw() const { | 447 | [[nodiscard]] constexpr base_type to_raw() const { |
| 469 | return data_; | 448 | return data_; |
| 470 | } | 449 | } |
| 471 | 450 | ||
| @@ -473,27 +452,27 @@ public: // conversion to basic types | |||
| 473 | data_ &= fractional_mask; | 452 | data_ &= fractional_mask; |
| 474 | } | 453 | } |
| 475 | 454 | ||
| 476 | constexpr base_type get_frac() const { | 455 | [[nodiscard]] constexpr base_type get_frac() const { |
| 477 | return data_ & fractional_mask; | 456 | return data_ & fractional_mask; |
| 478 | } | 457 | } |
| 479 | 458 | ||
| 480 | public: | 459 | public: |
| 481 | constexpr void swap(FixedPoint& rhs) { | 460 | constexpr void swap(FixedPoint& rhs) noexcept { |
| 482 | using std::swap; | 461 | using std::swap; |
| 483 | swap(data_, rhs.data_); | 462 | swap(data_, rhs.data_); |
| 484 | } | 463 | } |
| 485 | 464 | ||
| 486 | public: | 465 | public: |
| 487 | base_type data_; | 466 | base_type data_{}; |
| 488 | }; | 467 | }; |
| 489 | 468 | ||
| 490 | // if we have the same fractional portion, but differing integer portions, we trivially upgrade the | 469 | // if we have the same fractional portion, but differing integer portions, we trivially upgrade the |
| 491 | // smaller type | 470 | // smaller type |
| 492 | template <size_t I1, size_t I2, size_t F> | 471 | template <size_t I1, size_t I2, size_t F> |
| 493 | constexpr typename std::conditional<I1 >= I2, FixedPoint<I1, F>, FixedPoint<I2, F>>::type operator+( | 472 | constexpr std::conditional_t<I1 >= I2, FixedPoint<I1, F>, FixedPoint<I2, F>> operator+( |
| 494 | FixedPoint<I1, F> lhs, FixedPoint<I2, F> rhs) { | 473 | FixedPoint<I1, F> lhs, FixedPoint<I2, F> rhs) { |
| 495 | 474 | ||
| 496 | using T = typename std::conditional<I1 >= I2, FixedPoint<I1, F>, FixedPoint<I2, F>>::type; | 475 | using T = std::conditional_t<I1 >= I2, FixedPoint<I1, F>, FixedPoint<I2, F>>; |
| 497 | 476 | ||
| 498 | const T l = T::from_base(lhs.to_raw()); | 477 | const T l = T::from_base(lhs.to_raw()); |
| 499 | const T r = T::from_base(rhs.to_raw()); | 478 | const T r = T::from_base(rhs.to_raw()); |
| @@ -501,10 +480,10 @@ constexpr typename std::conditional<I1 >= I2, FixedPoint<I1, F>, FixedPoint<I2, | |||
| 501 | } | 480 | } |
| 502 | 481 | ||
| 503 | template <size_t I1, size_t I2, size_t F> | 482 | template <size_t I1, size_t I2, size_t F> |
| 504 | constexpr typename std::conditional<I1 >= I2, FixedPoint<I1, F>, FixedPoint<I2, F>>::type operator-( | 483 | constexpr std::conditional_t<I1 >= I2, FixedPoint<I1, F>, FixedPoint<I2, F>> operator-( |
| 505 | FixedPoint<I1, F> lhs, FixedPoint<I2, F> rhs) { | 484 | FixedPoint<I1, F> lhs, FixedPoint<I2, F> rhs) { |
| 506 | 485 | ||
| 507 | using T = typename std::conditional<I1 >= I2, FixedPoint<I1, F>, FixedPoint<I2, F>>::type; | 486 | using T = std::conditional_t<I1 >= I2, FixedPoint<I1, F>, FixedPoint<I2, F>>; |
| 508 | 487 | ||
| 509 | const T l = T::from_base(lhs.to_raw()); | 488 | const T l = T::from_base(lhs.to_raw()); |
| 510 | const T r = T::from_base(rhs.to_raw()); | 489 | const T r = T::from_base(rhs.to_raw()); |
| @@ -512,10 +491,10 @@ constexpr typename std::conditional<I1 >= I2, FixedPoint<I1, F>, FixedPoint<I2, | |||
| 512 | } | 491 | } |
| 513 | 492 | ||
| 514 | template <size_t I1, size_t I2, size_t F> | 493 | template <size_t I1, size_t I2, size_t F> |
| 515 | constexpr typename std::conditional<I1 >= I2, FixedPoint<I1, F>, FixedPoint<I2, F>>::type operator*( | 494 | constexpr std::conditional_t<I1 >= I2, FixedPoint<I1, F>, FixedPoint<I2, F>> operator*( |
| 516 | FixedPoint<I1, F> lhs, FixedPoint<I2, F> rhs) { | 495 | FixedPoint<I1, F> lhs, FixedPoint<I2, F> rhs) { |
| 517 | 496 | ||
| 518 | using T = typename std::conditional<I1 >= I2, FixedPoint<I1, F>, FixedPoint<I2, F>>::type; | 497 | using T = std::conditional_t<I1 >= I2, FixedPoint<I1, F>, FixedPoint<I2, F>>; |
| 519 | 498 | ||
| 520 | const T l = T::from_base(lhs.to_raw()); | 499 | const T l = T::from_base(lhs.to_raw()); |
| 521 | const T r = T::from_base(rhs.to_raw()); | 500 | const T r = T::from_base(rhs.to_raw()); |
| @@ -523,10 +502,10 @@ constexpr typename std::conditional<I1 >= I2, FixedPoint<I1, F>, FixedPoint<I2, | |||
| 523 | } | 502 | } |
| 524 | 503 | ||
| 525 | template <size_t I1, size_t I2, size_t F> | 504 | template <size_t I1, size_t I2, size_t F> |
| 526 | constexpr typename std::conditional<I1 >= I2, FixedPoint<I1, F>, FixedPoint<I2, F>>::type operator/( | 505 | constexpr std::conditional_t<I1 >= I2, FixedPoint<I1, F>, FixedPoint<I2, F>> operator/( |
| 527 | FixedPoint<I1, F> lhs, FixedPoint<I2, F> rhs) { | 506 | FixedPoint<I1, F> lhs, FixedPoint<I2, F> rhs) { |
| 528 | 507 | ||
| 529 | using T = typename std::conditional<I1 >= I2, FixedPoint<I1, F>, FixedPoint<I2, F>>::type; | 508 | using T = std::conditional_t<I1 >= I2, FixedPoint<I1, F>, FixedPoint<I2, F>>; |
| 530 | 509 | ||
| 531 | const T l = T::from_base(lhs.to_raw()); | 510 | const T l = T::from_base(lhs.to_raw()); |
| 532 | const T r = T::from_base(rhs.to_raw()); | 511 | const T r = T::from_base(rhs.to_raw()); |
| @@ -561,54 +540,46 @@ constexpr FixedPoint<I, F> operator/(FixedPoint<I, F> lhs, FixedPoint<I, F> rhs) | |||
| 561 | return lhs; | 540 | return lhs; |
| 562 | } | 541 | } |
| 563 | 542 | ||
| 564 | template <size_t I, size_t F, class Number, | 543 | template <size_t I, size_t F, IsArithmetic Number> |
| 565 | class = typename std::enable_if<std::is_arithmetic<Number>::value>::type> | ||
| 566 | constexpr FixedPoint<I, F> operator+(FixedPoint<I, F> lhs, Number rhs) { | 544 | constexpr FixedPoint<I, F> operator+(FixedPoint<I, F> lhs, Number rhs) { |
| 567 | lhs += FixedPoint<I, F>(rhs); | 545 | lhs += FixedPoint<I, F>(rhs); |
| 568 | return lhs; | 546 | return lhs; |
| 569 | } | 547 | } |
| 570 | template <size_t I, size_t F, class Number, | 548 | template <size_t I, size_t F, IsArithmetic Number> |
| 571 | class = typename std::enable_if<std::is_arithmetic<Number>::value>::type> | ||
| 572 | constexpr FixedPoint<I, F> operator-(FixedPoint<I, F> lhs, Number rhs) { | 549 | constexpr FixedPoint<I, F> operator-(FixedPoint<I, F> lhs, Number rhs) { |
| 573 | lhs -= FixedPoint<I, F>(rhs); | 550 | lhs -= FixedPoint<I, F>(rhs); |
| 574 | return lhs; | 551 | return lhs; |
| 575 | } | 552 | } |
| 576 | template <size_t I, size_t F, class Number, | 553 | template <size_t I, size_t F, IsArithmetic Number> |
| 577 | class = typename std::enable_if<std::is_arithmetic<Number>::value>::type> | ||
| 578 | constexpr FixedPoint<I, F> operator*(FixedPoint<I, F> lhs, Number rhs) { | 554 | constexpr FixedPoint<I, F> operator*(FixedPoint<I, F> lhs, Number rhs) { |
| 579 | lhs *= FixedPoint<I, F>(rhs); | 555 | lhs *= FixedPoint<I, F>(rhs); |
| 580 | return lhs; | 556 | return lhs; |
| 581 | } | 557 | } |
| 582 | template <size_t I, size_t F, class Number, | 558 | template <size_t I, size_t F, IsArithmetic Number> |
| 583 | class = typename std::enable_if<std::is_arithmetic<Number>::value>::type> | ||
| 584 | constexpr FixedPoint<I, F> operator/(FixedPoint<I, F> lhs, Number rhs) { | 559 | constexpr FixedPoint<I, F> operator/(FixedPoint<I, F> lhs, Number rhs) { |
| 585 | lhs /= FixedPoint<I, F>(rhs); | 560 | lhs /= FixedPoint<I, F>(rhs); |
| 586 | return lhs; | 561 | return lhs; |
| 587 | } | 562 | } |
| 588 | 563 | ||
| 589 | template <size_t I, size_t F, class Number, | 564 | template <size_t I, size_t F, IsArithmetic Number> |
| 590 | class = typename std::enable_if<std::is_arithmetic<Number>::value>::type> | ||
| 591 | constexpr FixedPoint<I, F> operator+(Number lhs, FixedPoint<I, F> rhs) { | 565 | constexpr FixedPoint<I, F> operator+(Number lhs, FixedPoint<I, F> rhs) { |
| 592 | FixedPoint<I, F> tmp(lhs); | 566 | FixedPoint<I, F> tmp(lhs); |
| 593 | tmp += rhs; | 567 | tmp += rhs; |
| 594 | return tmp; | 568 | return tmp; |
| 595 | } | 569 | } |
| 596 | template <size_t I, size_t F, class Number, | 570 | template <size_t I, size_t F, IsArithmetic Number> |
| 597 | class = typename std::enable_if<std::is_arithmetic<Number>::value>::type> | ||
| 598 | constexpr FixedPoint<I, F> operator-(Number lhs, FixedPoint<I, F> rhs) { | 571 | constexpr FixedPoint<I, F> operator-(Number lhs, FixedPoint<I, F> rhs) { |
| 599 | FixedPoint<I, F> tmp(lhs); | 572 | FixedPoint<I, F> tmp(lhs); |
| 600 | tmp -= rhs; | 573 | tmp -= rhs; |
| 601 | return tmp; | 574 | return tmp; |
| 602 | } | 575 | } |
| 603 | template <size_t I, size_t F, class Number, | 576 | template <size_t I, size_t F, IsArithmetic Number> |
| 604 | class = typename std::enable_if<std::is_arithmetic<Number>::value>::type> | ||
| 605 | constexpr FixedPoint<I, F> operator*(Number lhs, FixedPoint<I, F> rhs) { | 577 | constexpr FixedPoint<I, F> operator*(Number lhs, FixedPoint<I, F> rhs) { |
| 606 | FixedPoint<I, F> tmp(lhs); | 578 | FixedPoint<I, F> tmp(lhs); |
| 607 | tmp *= rhs; | 579 | tmp *= rhs; |
| 608 | return tmp; | 580 | return tmp; |
| 609 | } | 581 | } |
| 610 | template <size_t I, size_t F, class Number, | 582 | template <size_t I, size_t F, IsArithmetic Number> |
| 611 | class = typename std::enable_if<std::is_arithmetic<Number>::value>::type> | ||
| 612 | constexpr FixedPoint<I, F> operator/(Number lhs, FixedPoint<I, F> rhs) { | 583 | constexpr FixedPoint<I, F> operator/(Number lhs, FixedPoint<I, F> rhs) { |
| 613 | FixedPoint<I, F> tmp(lhs); | 584 | FixedPoint<I, F> tmp(lhs); |
| 614 | tmp /= rhs; | 585 | tmp /= rhs; |
| @@ -616,78 +587,64 @@ constexpr FixedPoint<I, F> operator/(Number lhs, FixedPoint<I, F> rhs) { | |||
| 616 | } | 587 | } |
| 617 | 588 | ||
| 618 | // shift operators | 589 | // shift operators |
| 619 | template <size_t I, size_t F, class Integer, | 590 | template <size_t I, size_t F, IsIntegral Integer> |
| 620 | class = typename std::enable_if<std::is_integral<Integer>::value>::type> | ||
| 621 | constexpr FixedPoint<I, F> operator<<(FixedPoint<I, F> lhs, Integer rhs) { | 591 | constexpr FixedPoint<I, F> operator<<(FixedPoint<I, F> lhs, Integer rhs) { |
| 622 | lhs <<= rhs; | 592 | lhs <<= rhs; |
| 623 | return lhs; | 593 | return lhs; |
| 624 | } | 594 | } |
| 625 | template <size_t I, size_t F, class Integer, | 595 | template <size_t I, size_t F, IsIntegral Integer> |
| 626 | class = typename std::enable_if<std::is_integral<Integer>::value>::type> | ||
| 627 | constexpr FixedPoint<I, F> operator>>(FixedPoint<I, F> lhs, Integer rhs) { | 596 | constexpr FixedPoint<I, F> operator>>(FixedPoint<I, F> lhs, Integer rhs) { |
| 628 | lhs >>= rhs; | 597 | lhs >>= rhs; |
| 629 | return lhs; | 598 | return lhs; |
| 630 | } | 599 | } |
| 631 | 600 | ||
| 632 | // comparison operators | 601 | // comparison operators |
| 633 | template <size_t I, size_t F, class Number, | 602 | template <size_t I, size_t F, IsArithmetic Number> |
| 634 | class = typename std::enable_if<std::is_arithmetic<Number>::value>::type> | ||
| 635 | constexpr bool operator>(FixedPoint<I, F> lhs, Number rhs) { | 603 | constexpr bool operator>(FixedPoint<I, F> lhs, Number rhs) { |
| 636 | return lhs > FixedPoint<I, F>(rhs); | 604 | return lhs > FixedPoint<I, F>(rhs); |
| 637 | } | 605 | } |
| 638 | template <size_t I, size_t F, class Number, | 606 | template <size_t I, size_t F, IsArithmetic Number> |
| 639 | class = typename std::enable_if<std::is_arithmetic<Number>::value>::type> | ||
| 640 | constexpr bool operator<(FixedPoint<I, F> lhs, Number rhs) { | 607 | constexpr bool operator<(FixedPoint<I, F> lhs, Number rhs) { |
| 641 | return lhs < FixedPoint<I, F>(rhs); | 608 | return lhs < FixedPoint<I, F>(rhs); |
| 642 | } | 609 | } |
| 643 | template <size_t I, size_t F, class Number, | 610 | template <size_t I, size_t F, IsArithmetic Number> |
| 644 | class = typename std::enable_if<std::is_arithmetic<Number>::value>::type> | ||
| 645 | constexpr bool operator>=(FixedPoint<I, F> lhs, Number rhs) { | 611 | constexpr bool operator>=(FixedPoint<I, F> lhs, Number rhs) { |
| 646 | return lhs >= FixedPoint<I, F>(rhs); | 612 | return lhs >= FixedPoint<I, F>(rhs); |
| 647 | } | 613 | } |
| 648 | template <size_t I, size_t F, class Number, | 614 | template <size_t I, size_t F, IsArithmetic Number> |
| 649 | class = typename std::enable_if<std::is_arithmetic<Number>::value>::type> | ||
| 650 | constexpr bool operator<=(FixedPoint<I, F> lhs, Number rhs) { | 615 | constexpr bool operator<=(FixedPoint<I, F> lhs, Number rhs) { |
| 651 | return lhs <= FixedPoint<I, F>(rhs); | 616 | return lhs <= FixedPoint<I, F>(rhs); |
| 652 | } | 617 | } |
| 653 | template <size_t I, size_t F, class Number, | 618 | template <size_t I, size_t F, IsArithmetic Number> |
| 654 | class = typename std::enable_if<std::is_arithmetic<Number>::value>::type> | ||
| 655 | constexpr bool operator==(FixedPoint<I, F> lhs, Number rhs) { | 619 | constexpr bool operator==(FixedPoint<I, F> lhs, Number rhs) { |
| 656 | return lhs == FixedPoint<I, F>(rhs); | 620 | return lhs == FixedPoint<I, F>(rhs); |
| 657 | } | 621 | } |
| 658 | template <size_t I, size_t F, class Number, | 622 | template <size_t I, size_t F, IsArithmetic Number> |
| 659 | class = typename std::enable_if<std::is_arithmetic<Number>::value>::type> | ||
| 660 | constexpr bool operator!=(FixedPoint<I, F> lhs, Number rhs) { | 623 | constexpr bool operator!=(FixedPoint<I, F> lhs, Number rhs) { |
| 661 | return lhs != FixedPoint<I, F>(rhs); | 624 | return lhs != FixedPoint<I, F>(rhs); |
| 662 | } | 625 | } |
| 663 | 626 | ||
| 664 | template <size_t I, size_t F, class Number, | 627 | template <size_t I, size_t F, IsArithmetic Number> |
| 665 | class = typename std::enable_if<std::is_arithmetic<Number>::value>::type> | ||
| 666 | constexpr bool operator>(Number lhs, FixedPoint<I, F> rhs) { | 628 | constexpr bool operator>(Number lhs, FixedPoint<I, F> rhs) { |
| 667 | return FixedPoint<I, F>(lhs) > rhs; | 629 | return FixedPoint<I, F>(lhs) > rhs; |
| 668 | } | 630 | } |
| 669 | template <size_t I, size_t F, class Number, | 631 | template <size_t I, size_t F, IsArithmetic Number> |
| 670 | class = typename std::enable_if<std::is_arithmetic<Number>::value>::type> | ||
| 671 | constexpr bool operator<(Number lhs, FixedPoint<I, F> rhs) { | 632 | constexpr bool operator<(Number lhs, FixedPoint<I, F> rhs) { |
| 672 | return FixedPoint<I, F>(lhs) < rhs; | 633 | return FixedPoint<I, F>(lhs) < rhs; |
| 673 | } | 634 | } |
| 674 | template <size_t I, size_t F, class Number, | 635 | template <size_t I, size_t F, IsArithmetic Number> |
| 675 | class = typename std::enable_if<std::is_arithmetic<Number>::value>::type> | ||
| 676 | constexpr bool operator>=(Number lhs, FixedPoint<I, F> rhs) { | 636 | constexpr bool operator>=(Number lhs, FixedPoint<I, F> rhs) { |
| 677 | return FixedPoint<I, F>(lhs) >= rhs; | 637 | return FixedPoint<I, F>(lhs) >= rhs; |
| 678 | } | 638 | } |
| 679 | template <size_t I, size_t F, class Number, | 639 | template <size_t I, size_t F, IsArithmetic Number> |
| 680 | class = typename std::enable_if<std::is_arithmetic<Number>::value>::type> | ||
| 681 | constexpr bool operator<=(Number lhs, FixedPoint<I, F> rhs) { | 640 | constexpr bool operator<=(Number lhs, FixedPoint<I, F> rhs) { |
| 682 | return FixedPoint<I, F>(lhs) <= rhs; | 641 | return FixedPoint<I, F>(lhs) <= rhs; |
| 683 | } | 642 | } |
| 684 | template <size_t I, size_t F, class Number, | 643 | template <size_t I, size_t F, IsArithmetic Number> |
| 685 | class = typename std::enable_if<std::is_arithmetic<Number>::value>::type> | ||
| 686 | constexpr bool operator==(Number lhs, FixedPoint<I, F> rhs) { | 644 | constexpr bool operator==(Number lhs, FixedPoint<I, F> rhs) { |
| 687 | return FixedPoint<I, F>(lhs) == rhs; | 645 | return FixedPoint<I, F>(lhs) == rhs; |
| 688 | } | 646 | } |
| 689 | template <size_t I, size_t F, class Number, | 647 | template <size_t I, size_t F, IsArithmetic Number> |
| 690 | class = typename std::enable_if<std::is_arithmetic<Number>::value>::type> | ||
| 691 | constexpr bool operator!=(Number lhs, FixedPoint<I, F> rhs) { | 648 | constexpr bool operator!=(Number lhs, FixedPoint<I, F> rhs) { |
| 692 | return FixedPoint<I, F>(lhs) != rhs; | 649 | return FixedPoint<I, F>(lhs) != rhs; |
| 693 | } | 650 | } |
diff --git a/src/common/fs/file.h b/src/common/fs/file.h index 69b53384c..167c4d826 100644 --- a/src/common/fs/file.h +++ b/src/common/fs/file.h | |||
| @@ -209,8 +209,8 @@ public: | |||
| 209 | 209 | ||
| 210 | /** | 210 | /** |
| 211 | * Helper function which deduces the value type of a contiguous STL container used in ReadSpan. | 211 | * Helper function which deduces the value type of a contiguous STL container used in ReadSpan. |
| 212 | * If T is not a contiguous STL container as defined by the concept IsSTLContainer, this calls | 212 | * If T is not a contiguous container as defined by the concept IsContiguousContainer, this |
| 213 | * ReadObject and T must be a trivially copyable object. | 213 | * calls ReadObject and T must be a trivially copyable object. |
| 214 | * | 214 | * |
| 215 | * See ReadSpan for more details if T is a contiguous container. | 215 | * See ReadSpan for more details if T is a contiguous container. |
| 216 | * See ReadObject for more details if T is a trivially copyable object. | 216 | * See ReadObject for more details if T is a trivially copyable object. |
| @@ -223,7 +223,7 @@ public: | |||
| 223 | */ | 223 | */ |
| 224 | template <typename T> | 224 | template <typename T> |
| 225 | [[nodiscard]] size_t Read(T& data) const { | 225 | [[nodiscard]] size_t Read(T& data) const { |
| 226 | if constexpr (IsSTLContainer<T>) { | 226 | if constexpr (IsContiguousContainer<T>) { |
| 227 | using ContiguousType = typename T::value_type; | 227 | using ContiguousType = typename T::value_type; |
| 228 | static_assert(std::is_trivially_copyable_v<ContiguousType>, | 228 | static_assert(std::is_trivially_copyable_v<ContiguousType>, |
| 229 | "Data type must be trivially copyable."); | 229 | "Data type must be trivially copyable."); |
| @@ -235,8 +235,8 @@ public: | |||
| 235 | 235 | ||
| 236 | /** | 236 | /** |
| 237 | * Helper function which deduces the value type of a contiguous STL container used in WriteSpan. | 237 | * Helper function which deduces the value type of a contiguous STL container used in WriteSpan. |
| 238 | * If T is not a contiguous STL container as defined by the concept IsSTLContainer, this calls | 238 | * If T is not a contiguous STL container as defined by the concept IsContiguousContainer, this |
| 239 | * WriteObject and T must be a trivially copyable object. | 239 | * calls WriteObject and T must be a trivially copyable object. |
| 240 | * | 240 | * |
| 241 | * See WriteSpan for more details if T is a contiguous container. | 241 | * See WriteSpan for more details if T is a contiguous container. |
| 242 | * See WriteObject for more details if T is a trivially copyable object. | 242 | * See WriteObject for more details if T is a trivially copyable object. |
| @@ -249,7 +249,7 @@ public: | |||
| 249 | */ | 249 | */ |
| 250 | template <typename T> | 250 | template <typename T> |
| 251 | [[nodiscard]] size_t Write(const T& data) const { | 251 | [[nodiscard]] size_t Write(const T& data) const { |
| 252 | if constexpr (IsSTLContainer<T>) { | 252 | if constexpr (IsContiguousContainer<T>) { |
| 253 | using ContiguousType = typename T::value_type; | 253 | using ContiguousType = typename T::value_type; |
| 254 | static_assert(std::is_trivially_copyable_v<ContiguousType>, | 254 | static_assert(std::is_trivially_copyable_v<ContiguousType>, |
| 255 | "Data type must be trivially copyable."); | 255 | "Data type must be trivially copyable."); |
diff --git a/src/common/input.h b/src/common/input.h index b533f3844..cb30b7254 100644 --- a/src/common/input.h +++ b/src/common/input.h | |||
| @@ -100,7 +100,6 @@ enum class CameraError { | |||
| 100 | enum class VibrationAmplificationType { | 100 | enum class VibrationAmplificationType { |
| 101 | Linear, | 101 | Linear, |
| 102 | Exponential, | 102 | Exponential, |
| 103 | Test, | ||
| 104 | }; | 103 | }; |
| 105 | 104 | ||
| 106 | // Analog properties for calibration | 105 | // Analog properties for calibration |
| @@ -325,6 +324,10 @@ public: | |||
| 325 | return VibrationError::NotSupported; | 324 | return VibrationError::NotSupported; |
| 326 | } | 325 | } |
| 327 | 326 | ||
| 327 | virtual bool IsVibrationEnabled() { | ||
| 328 | return false; | ||
| 329 | } | ||
| 330 | |||
| 328 | virtual PollingError SetPollingMode([[maybe_unused]] PollingMode polling_mode) { | 331 | virtual PollingError SetPollingMode([[maybe_unused]] PollingMode polling_mode) { |
| 329 | return PollingError::NotSupported; | 332 | return PollingError::NotSupported; |
| 330 | } | 333 | } |
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index e7fe675cb..113e663b5 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt | |||
| @@ -243,6 +243,8 @@ add_library(core STATIC | |||
| 243 | hle/kernel/k_server_session.h | 243 | hle/kernel/k_server_session.h |
| 244 | hle/kernel/k_session.cpp | 244 | hle/kernel/k_session.cpp |
| 245 | hle/kernel/k_session.h | 245 | hle/kernel/k_session.h |
| 246 | hle/kernel/k_session_request.cpp | ||
| 247 | hle/kernel/k_session_request.h | ||
| 246 | hle/kernel/k_shared_memory.cpp | 248 | hle/kernel/k_shared_memory.cpp |
| 247 | hle/kernel/k_shared_memory.h | 249 | hle/kernel/k_shared_memory.h |
| 248 | hle/kernel/k_shared_memory_info.h | 250 | hle/kernel/k_shared_memory_info.h |
| @@ -772,19 +774,15 @@ if (MSVC) | |||
| 772 | /we4244 # 'conversion': conversion from 'type1' to 'type2', possible loss of data | 774 | /we4244 # 'conversion': conversion from 'type1' to 'type2', possible loss of data |
| 773 | /we4245 # 'conversion': conversion from 'type1' to 'type2', signed/unsigned mismatch | 775 | /we4245 # 'conversion': conversion from 'type1' to 'type2', signed/unsigned mismatch |
| 774 | /we4254 # 'operator': conversion from 'type1:field_bits' to 'type2:field_bits', possible loss of data | 776 | /we4254 # 'operator': conversion from 'type1:field_bits' to 'type2:field_bits', possible loss of data |
| 777 | /we4800 # Implicit conversion from 'type' to bool. Possible information loss | ||
| 775 | ) | 778 | ) |
| 776 | else() | 779 | else() |
| 777 | target_compile_options(core PRIVATE | 780 | target_compile_options(core PRIVATE |
| 778 | -Werror=conversion | 781 | -Werror=conversion |
| 779 | -Werror=ignored-qualifiers | ||
| 780 | 782 | ||
| 781 | $<$<CXX_COMPILER_ID:GNU>:-Werror=class-memaccess> | 783 | -Wno-sign-conversion |
| 782 | $<$<CXX_COMPILER_ID:GNU>:-Werror=unused-but-set-parameter> | ||
| 783 | $<$<CXX_COMPILER_ID:GNU>:-Werror=unused-but-set-variable> | ||
| 784 | 784 | ||
| 785 | $<$<CXX_COMPILER_ID:Clang>:-fsized-deallocation> | 785 | $<$<CXX_COMPILER_ID:Clang>:-fsized-deallocation> |
| 786 | |||
| 787 | -Wno-sign-conversion | ||
| 788 | ) | 786 | ) |
| 789 | endif() | 787 | endif() |
| 790 | 788 | ||
diff --git a/src/core/arm/dynarmic/arm_dynarmic_32.cpp b/src/core/arm/dynarmic/arm_dynarmic_32.cpp index d1e70f19d..287ba102e 100644 --- a/src/core/arm/dynarmic/arm_dynarmic_32.cpp +++ b/src/core/arm/dynarmic/arm_dynarmic_32.cpp | |||
| @@ -450,7 +450,7 @@ std::vector<ARM_Interface::BacktraceEntry> ARM_Dynarmic_32::GetBacktrace(Core::S | |||
| 450 | // Frame records are two words long: | 450 | // Frame records are two words long: |
| 451 | // fp+0 : pointer to previous frame record | 451 | // fp+0 : pointer to previous frame record |
| 452 | // fp+4 : value of lr for frame | 452 | // fp+4 : value of lr for frame |
| 453 | while (true) { | 453 | for (size_t i = 0; i < 256; i++) { |
| 454 | out.push_back({"", 0, lr, 0, ""}); | 454 | out.push_back({"", 0, lr, 0, ""}); |
| 455 | if (!fp || (fp % 4 != 0) || !memory.IsValidVirtualAddressRange(fp, 8)) { | 455 | if (!fp || (fp % 4 != 0) || !memory.IsValidVirtualAddressRange(fp, 8)) { |
| 456 | break; | 456 | break; |
diff --git a/src/core/arm/dynarmic/arm_dynarmic_64.cpp b/src/core/arm/dynarmic/arm_dynarmic_64.cpp index 22b5d5656..afb7fb3a0 100644 --- a/src/core/arm/dynarmic/arm_dynarmic_64.cpp +++ b/src/core/arm/dynarmic/arm_dynarmic_64.cpp | |||
| @@ -517,7 +517,7 @@ std::vector<ARM_Interface::BacktraceEntry> ARM_Dynarmic_64::GetBacktrace(Core::S | |||
| 517 | // Frame records are two words long: | 517 | // Frame records are two words long: |
| 518 | // fp+0 : pointer to previous frame record | 518 | // fp+0 : pointer to previous frame record |
| 519 | // fp+8 : value of lr for frame | 519 | // fp+8 : value of lr for frame |
| 520 | while (true) { | 520 | for (size_t i = 0; i < 256; i++) { |
| 521 | out.push_back({"", 0, lr, 0, ""}); | 521 | out.push_back({"", 0, lr, 0, ""}); |
| 522 | if (!fp || (fp % 4 != 0) || !memory.IsValidVirtualAddressRange(fp, 16)) { | 522 | if (!fp || (fp % 4 != 0) || !memory.IsValidVirtualAddressRange(fp, 16)) { |
| 523 | break; | 523 | break; |
diff --git a/src/core/core.cpp b/src/core/core.cpp index 7fb8bc019..d8934be52 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp | |||
| @@ -137,6 +137,7 @@ struct System::Impl { | |||
| 137 | device_memory = std::make_unique<Core::DeviceMemory>(); | 137 | device_memory = std::make_unique<Core::DeviceMemory>(); |
| 138 | 138 | ||
| 139 | is_multicore = Settings::values.use_multi_core.GetValue(); | 139 | is_multicore = Settings::values.use_multi_core.GetValue(); |
| 140 | extended_memory_layout = Settings::values.use_extended_memory_layout.GetValue(); | ||
| 140 | 141 | ||
| 141 | core_timing.SetMulticore(is_multicore); | 142 | core_timing.SetMulticore(is_multicore); |
| 142 | core_timing.Initialize([&system]() { system.RegisterHostThread(); }); | 143 | core_timing.Initialize([&system]() { system.RegisterHostThread(); }); |
| @@ -166,13 +167,18 @@ struct System::Impl { | |||
| 166 | } | 167 | } |
| 167 | 168 | ||
| 168 | void ReinitializeIfNecessary(System& system) { | 169 | void ReinitializeIfNecessary(System& system) { |
| 169 | if (is_multicore == Settings::values.use_multi_core.GetValue()) { | 170 | const bool must_reinitialize = |
| 171 | is_multicore != Settings::values.use_multi_core.GetValue() || | ||
| 172 | extended_memory_layout != Settings::values.use_extended_memory_layout.GetValue(); | ||
| 173 | |||
| 174 | if (!must_reinitialize) { | ||
| 170 | return; | 175 | return; |
| 171 | } | 176 | } |
| 172 | 177 | ||
| 173 | LOG_DEBUG(Kernel, "Re-initializing"); | 178 | LOG_DEBUG(Kernel, "Re-initializing"); |
| 174 | 179 | ||
| 175 | is_multicore = Settings::values.use_multi_core.GetValue(); | 180 | is_multicore = Settings::values.use_multi_core.GetValue(); |
| 181 | extended_memory_layout = Settings::values.use_extended_memory_layout.GetValue(); | ||
| 176 | 182 | ||
| 177 | Initialize(system); | 183 | Initialize(system); |
| 178 | } | 184 | } |
| @@ -384,6 +390,7 @@ struct System::Impl { | |||
| 384 | kernel.ShutdownCores(); | 390 | kernel.ShutdownCores(); |
| 385 | cpu_manager.Shutdown(); | 391 | cpu_manager.Shutdown(); |
| 386 | debugger.reset(); | 392 | debugger.reset(); |
| 393 | services->KillNVNFlinger(); | ||
| 387 | kernel.CloseServices(); | 394 | kernel.CloseServices(); |
| 388 | services.reset(); | 395 | services.reset(); |
| 389 | service_manager.reset(); | 396 | service_manager.reset(); |
| @@ -520,6 +527,7 @@ struct System::Impl { | |||
| 520 | 527 | ||
| 521 | bool is_multicore{}; | 528 | bool is_multicore{}; |
| 522 | bool is_async_gpu{}; | 529 | bool is_async_gpu{}; |
| 530 | bool extended_memory_layout{}; | ||
| 523 | 531 | ||
| 524 | ExecuteProgramCallback execute_program_callback; | 532 | ExecuteProgramCallback execute_program_callback; |
| 525 | ExitCallback exit_callback; | 533 | ExitCallback exit_callback; |
diff --git a/src/core/file_sys/card_image.cpp b/src/core/file_sys/card_image.cpp index f23d9373b..5d02865f4 100644 --- a/src/core/file_sys/card_image.cpp +++ b/src/core/file_sys/card_image.cpp | |||
| @@ -232,8 +232,8 @@ const std::vector<std::shared_ptr<NCA>>& XCI::GetNCAs() const { | |||
| 232 | 232 | ||
| 233 | std::shared_ptr<NCA> XCI::GetNCAByType(NCAContentType type) const { | 233 | std::shared_ptr<NCA> XCI::GetNCAByType(NCAContentType type) const { |
| 234 | const auto program_id = secure_partition->GetProgramTitleID(); | 234 | const auto program_id = secure_partition->GetProgramTitleID(); |
| 235 | const auto iter = std::find_if( | 235 | const auto iter = |
| 236 | ncas.begin(), ncas.end(), [this, type, program_id](const std::shared_ptr<NCA>& nca) { | 236 | std::find_if(ncas.begin(), ncas.end(), [type, program_id](const std::shared_ptr<NCA>& nca) { |
| 237 | return nca->GetType() == type && nca->GetTitleId() == program_id; | 237 | return nca->GetType() == type && nca->GetTitleId() == program_id; |
| 238 | }); | 238 | }); |
| 239 | return iter == ncas.end() ? nullptr : *iter; | 239 | return iter == ncas.end() ? nullptr : *iter; |
diff --git a/src/core/file_sys/control_metadata.cpp b/src/core/file_sys/control_metadata.cpp index be25da2f6..50f44f598 100644 --- a/src/core/file_sys/control_metadata.cpp +++ b/src/core/file_sys/control_metadata.cpp | |||
| @@ -1,6 +1,7 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project | 1 | // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project |
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | 2 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 3 | 3 | ||
| 4 | #include "common/settings.h" | ||
| 4 | #include "common/string_util.h" | 5 | #include "common/string_util.h" |
| 5 | #include "common/swap.h" | 6 | #include "common/swap.h" |
| 6 | #include "core/file_sys/control_metadata.h" | 7 | #include "core/file_sys/control_metadata.h" |
| @@ -37,6 +38,27 @@ std::string LanguageEntry::GetDeveloperName() const { | |||
| 37 | developer_name.size()); | 38 | developer_name.size()); |
| 38 | } | 39 | } |
| 39 | 40 | ||
| 41 | constexpr std::array<Language, 18> language_to_codes = {{ | ||
| 42 | Language::Japanese, | ||
| 43 | Language::AmericanEnglish, | ||
| 44 | Language::French, | ||
| 45 | Language::German, | ||
| 46 | Language::Italian, | ||
| 47 | Language::Spanish, | ||
| 48 | Language::Chinese, | ||
| 49 | Language::Korean, | ||
| 50 | Language::Dutch, | ||
| 51 | Language::Portuguese, | ||
| 52 | Language::Russian, | ||
| 53 | Language::Taiwanese, | ||
| 54 | Language::BritishEnglish, | ||
| 55 | Language::CanadianFrench, | ||
| 56 | Language::LatinAmericanSpanish, | ||
| 57 | Language::Chinese, | ||
| 58 | Language::Taiwanese, | ||
| 59 | Language::BrazilianPortuguese, | ||
| 60 | }}; | ||
| 61 | |||
| 40 | NACP::NACP() = default; | 62 | NACP::NACP() = default; |
| 41 | 63 | ||
| 42 | NACP::NACP(VirtualFile file) { | 64 | NACP::NACP(VirtualFile file) { |
| @@ -45,9 +67,13 @@ NACP::NACP(VirtualFile file) { | |||
| 45 | 67 | ||
| 46 | NACP::~NACP() = default; | 68 | NACP::~NACP() = default; |
| 47 | 69 | ||
| 48 | const LanguageEntry& NACP::GetLanguageEntry(Language language) const { | 70 | const LanguageEntry& NACP::GetLanguageEntry() const { |
| 49 | if (language != Language::Default) { | 71 | Language language = language_to_codes[Settings::values.language_index.GetValue()]; |
| 50 | return raw.language_entries.at(static_cast<u8>(language)); | 72 | |
| 73 | { | ||
| 74 | const auto& language_entry = raw.language_entries.at(static_cast<u8>(language)); | ||
| 75 | if (!language_entry.GetApplicationName().empty()) | ||
| 76 | return language_entry; | ||
| 51 | } | 77 | } |
| 52 | 78 | ||
| 53 | for (const auto& language_entry : raw.language_entries) { | 79 | for (const auto& language_entry : raw.language_entries) { |
| @@ -55,16 +81,15 @@ const LanguageEntry& NACP::GetLanguageEntry(Language language) const { | |||
| 55 | return language_entry; | 81 | return language_entry; |
| 56 | } | 82 | } |
| 57 | 83 | ||
| 58 | // Fallback to English | 84 | return raw.language_entries.at(static_cast<u8>(Language::AmericanEnglish)); |
| 59 | return GetLanguageEntry(Language::AmericanEnglish); | ||
| 60 | } | 85 | } |
| 61 | 86 | ||
| 62 | std::string NACP::GetApplicationName(Language language) const { | 87 | std::string NACP::GetApplicationName() const { |
| 63 | return GetLanguageEntry(language).GetApplicationName(); | 88 | return GetLanguageEntry().GetApplicationName(); |
| 64 | } | 89 | } |
| 65 | 90 | ||
| 66 | std::string NACP::GetDeveloperName(Language language) const { | 91 | std::string NACP::GetDeveloperName() const { |
| 67 | return GetLanguageEntry(language).GetDeveloperName(); | 92 | return GetLanguageEntry().GetDeveloperName(); |
| 68 | } | 93 | } |
| 69 | 94 | ||
| 70 | u64 NACP::GetTitleId() const { | 95 | u64 NACP::GetTitleId() const { |
diff --git a/src/core/file_sys/control_metadata.h b/src/core/file_sys/control_metadata.h index 75295519c..6a81873b1 100644 --- a/src/core/file_sys/control_metadata.h +++ b/src/core/file_sys/control_metadata.h | |||
| @@ -101,9 +101,9 @@ public: | |||
| 101 | explicit NACP(VirtualFile file); | 101 | explicit NACP(VirtualFile file); |
| 102 | ~NACP(); | 102 | ~NACP(); |
| 103 | 103 | ||
| 104 | const LanguageEntry& GetLanguageEntry(Language language = Language::Default) const; | 104 | const LanguageEntry& GetLanguageEntry() const; |
| 105 | std::string GetApplicationName(Language language = Language::Default) const; | 105 | std::string GetApplicationName() const; |
| 106 | std::string GetDeveloperName(Language language = Language::Default) const; | 106 | std::string GetDeveloperName() const; |
| 107 | u64 GetTitleId() const; | 107 | u64 GetTitleId() const; |
| 108 | u64 GetDLCBaseTitleId() const; | 108 | u64 GetDLCBaseTitleId() const; |
| 109 | std::string GetVersionString() const; | 109 | std::string GetVersionString() const; |
diff --git a/src/core/file_sys/program_metadata.cpp b/src/core/file_sys/program_metadata.cpp index 08d489eab..f00479bd3 100644 --- a/src/core/file_sys/program_metadata.cpp +++ b/src/core/file_sys/program_metadata.cpp | |||
| @@ -127,7 +127,7 @@ void ProgramMetadata::LoadManual(bool is_64_bit, ProgramAddressSpaceType address | |||
| 127 | } | 127 | } |
| 128 | 128 | ||
| 129 | bool ProgramMetadata::Is64BitProgram() const { | 129 | bool ProgramMetadata::Is64BitProgram() const { |
| 130 | return npdm_header.has_64_bit_instructions; | 130 | return npdm_header.has_64_bit_instructions.As<bool>(); |
| 131 | } | 131 | } |
| 132 | 132 | ||
| 133 | ProgramAddressSpaceType ProgramMetadata::GetAddressSpaceType() const { | 133 | ProgramAddressSpaceType ProgramMetadata::GetAddressSpaceType() const { |
diff --git a/src/core/hid/emulated_controller.cpp b/src/core/hid/emulated_controller.cpp index 025f1c78e..ec1364452 100644 --- a/src/core/hid/emulated_controller.cpp +++ b/src/core/hid/emulated_controller.cpp | |||
| @@ -970,14 +970,7 @@ bool EmulatedController::SetVibration(std::size_t device_index, VibrationValue v | |||
| 970 | Common::Input::VibrationError::None; | 970 | Common::Input::VibrationError::None; |
| 971 | } | 971 | } |
| 972 | 972 | ||
| 973 | bool EmulatedController::TestVibration(std::size_t device_index) { | 973 | bool EmulatedController::IsVibrationEnabled(std::size_t device_index) { |
| 974 | if (device_index >= output_devices.size()) { | ||
| 975 | return false; | ||
| 976 | } | ||
| 977 | if (!output_devices[device_index]) { | ||
| 978 | return false; | ||
| 979 | } | ||
| 980 | |||
| 981 | const auto player_index = NpadIdTypeToIndex(npad_id_type); | 974 | const auto player_index = NpadIdTypeToIndex(npad_id_type); |
| 982 | const auto& player = Settings::values.players.GetValue()[player_index]; | 975 | const auto& player = Settings::values.players.GetValue()[player_index]; |
| 983 | 976 | ||
| @@ -985,31 +978,15 @@ bool EmulatedController::TestVibration(std::size_t device_index) { | |||
| 985 | return false; | 978 | return false; |
| 986 | } | 979 | } |
| 987 | 980 | ||
| 988 | const Common::Input::VibrationStatus test_vibration = { | 981 | if (device_index >= output_devices.size()) { |
| 989 | .low_amplitude = 0.001f, | 982 | return false; |
| 990 | .low_frequency = DEFAULT_VIBRATION_VALUE.low_frequency, | 983 | } |
| 991 | .high_amplitude = 0.001f, | ||
| 992 | .high_frequency = DEFAULT_VIBRATION_VALUE.high_frequency, | ||
| 993 | .type = Common::Input::VibrationAmplificationType::Test, | ||
| 994 | }; | ||
| 995 | |||
| 996 | const Common::Input::VibrationStatus zero_vibration = { | ||
| 997 | .low_amplitude = DEFAULT_VIBRATION_VALUE.low_amplitude, | ||
| 998 | .low_frequency = DEFAULT_VIBRATION_VALUE.low_frequency, | ||
| 999 | .high_amplitude = DEFAULT_VIBRATION_VALUE.high_amplitude, | ||
| 1000 | .high_frequency = DEFAULT_VIBRATION_VALUE.high_frequency, | ||
| 1001 | .type = Common::Input::VibrationAmplificationType::Test, | ||
| 1002 | }; | ||
| 1003 | |||
| 1004 | // Send a slight vibration to test for rumble support | ||
| 1005 | output_devices[device_index]->SetVibration(test_vibration); | ||
| 1006 | 984 | ||
| 1007 | // Wait for about 15ms to ensure the controller is ready for the stop command | 985 | if (!output_devices[device_index]) { |
| 1008 | std::this_thread::sleep_for(std::chrono::milliseconds(15)); | 986 | return false; |
| 987 | } | ||
| 1009 | 988 | ||
| 1010 | // Stop any vibration and return the result | 989 | return output_devices[device_index]->IsVibrationEnabled(); |
| 1011 | return output_devices[device_index]->SetVibration(zero_vibration) == | ||
| 1012 | Common::Input::VibrationError::None; | ||
| 1013 | } | 990 | } |
| 1014 | 991 | ||
| 1015 | bool EmulatedController::SetPollingMode(Common::Input::PollingMode polling_mode) { | 992 | bool EmulatedController::SetPollingMode(Common::Input::PollingMode polling_mode) { |
| @@ -1048,6 +1025,7 @@ bool EmulatedController::HasNfc() const { | |||
| 1048 | case NpadStyleIndex::JoyconRight: | 1025 | case NpadStyleIndex::JoyconRight: |
| 1049 | case NpadStyleIndex::JoyconDual: | 1026 | case NpadStyleIndex::JoyconDual: |
| 1050 | case NpadStyleIndex::ProController: | 1027 | case NpadStyleIndex::ProController: |
| 1028 | case NpadStyleIndex::Handheld: | ||
| 1051 | break; | 1029 | break; |
| 1052 | default: | 1030 | default: |
| 1053 | return false; | 1031 | return false; |
| @@ -1158,27 +1136,27 @@ bool EmulatedController::IsControllerSupported(bool use_temporary_value) const { | |||
| 1158 | const auto type = is_configuring && use_temporary_value ? tmp_npad_type : npad_type; | 1136 | const auto type = is_configuring && use_temporary_value ? tmp_npad_type : npad_type; |
| 1159 | switch (type) { | 1137 | switch (type) { |
| 1160 | case NpadStyleIndex::ProController: | 1138 | case NpadStyleIndex::ProController: |
| 1161 | return supported_style_tag.fullkey; | 1139 | return supported_style_tag.fullkey.As<bool>(); |
| 1162 | case NpadStyleIndex::Handheld: | 1140 | case NpadStyleIndex::Handheld: |
| 1163 | return supported_style_tag.handheld; | 1141 | return supported_style_tag.handheld.As<bool>(); |
| 1164 | case NpadStyleIndex::JoyconDual: | 1142 | case NpadStyleIndex::JoyconDual: |
| 1165 | return supported_style_tag.joycon_dual; | 1143 | return supported_style_tag.joycon_dual.As<bool>(); |
| 1166 | case NpadStyleIndex::JoyconLeft: | 1144 | case NpadStyleIndex::JoyconLeft: |
| 1167 | return supported_style_tag.joycon_left; | 1145 | return supported_style_tag.joycon_left.As<bool>(); |
| 1168 | case NpadStyleIndex::JoyconRight: | 1146 | case NpadStyleIndex::JoyconRight: |
| 1169 | return supported_style_tag.joycon_right; | 1147 | return supported_style_tag.joycon_right.As<bool>(); |
| 1170 | case NpadStyleIndex::GameCube: | 1148 | case NpadStyleIndex::GameCube: |
| 1171 | return supported_style_tag.gamecube; | 1149 | return supported_style_tag.gamecube.As<bool>(); |
| 1172 | case NpadStyleIndex::Pokeball: | 1150 | case NpadStyleIndex::Pokeball: |
| 1173 | return supported_style_tag.palma; | 1151 | return supported_style_tag.palma.As<bool>(); |
| 1174 | case NpadStyleIndex::NES: | 1152 | case NpadStyleIndex::NES: |
| 1175 | return supported_style_tag.lark; | 1153 | return supported_style_tag.lark.As<bool>(); |
| 1176 | case NpadStyleIndex::SNES: | 1154 | case NpadStyleIndex::SNES: |
| 1177 | return supported_style_tag.lucia; | 1155 | return supported_style_tag.lucia.As<bool>(); |
| 1178 | case NpadStyleIndex::N64: | 1156 | case NpadStyleIndex::N64: |
| 1179 | return supported_style_tag.lagoon; | 1157 | return supported_style_tag.lagoon.As<bool>(); |
| 1180 | case NpadStyleIndex::SegaGenesis: | 1158 | case NpadStyleIndex::SegaGenesis: |
| 1181 | return supported_style_tag.lager; | 1159 | return supported_style_tag.lager.As<bool>(); |
| 1182 | default: | 1160 | default: |
| 1183 | return false; | 1161 | return false; |
| 1184 | } | 1162 | } |
| @@ -1234,12 +1212,6 @@ bool EmulatedController::IsConnected(bool get_temporary_value) const { | |||
| 1234 | return is_connected; | 1212 | return is_connected; |
| 1235 | } | 1213 | } |
| 1236 | 1214 | ||
| 1237 | bool EmulatedController::IsVibrationEnabled() const { | ||
| 1238 | const auto player_index = NpadIdTypeToIndex(npad_id_type); | ||
| 1239 | const auto& player = Settings::values.players.GetValue()[player_index]; | ||
| 1240 | return player.vibration_enabled; | ||
| 1241 | } | ||
| 1242 | |||
| 1243 | NpadIdType EmulatedController::GetNpadIdType() const { | 1215 | NpadIdType EmulatedController::GetNpadIdType() const { |
| 1244 | std::scoped_lock lock{mutex}; | 1216 | std::scoped_lock lock{mutex}; |
| 1245 | return npad_id_type; | 1217 | return npad_id_type; |
diff --git a/src/core/hid/emulated_controller.h b/src/core/hid/emulated_controller.h index 319226bf8..d004ca56a 100644 --- a/src/core/hid/emulated_controller.h +++ b/src/core/hid/emulated_controller.h | |||
| @@ -206,9 +206,6 @@ public: | |||
| 206 | */ | 206 | */ |
| 207 | bool IsConnected(bool get_temporary_value = false) const; | 207 | bool IsConnected(bool get_temporary_value = false) const; |
| 208 | 208 | ||
| 209 | /// Returns true if vibration is enabled | ||
| 210 | bool IsVibrationEnabled() const; | ||
| 211 | |||
| 212 | /// Removes all callbacks created from input devices | 209 | /// Removes all callbacks created from input devices |
| 213 | void UnloadInput(); | 210 | void UnloadInput(); |
| 214 | 211 | ||
| @@ -339,7 +336,7 @@ public: | |||
| 339 | * Sends a small vibration to the output device | 336 | * Sends a small vibration to the output device |
| 340 | * @return true if SetVibration was successfull | 337 | * @return true if SetVibration was successfull |
| 341 | */ | 338 | */ |
| 342 | bool TestVibration(std::size_t device_index); | 339 | bool IsVibrationEnabled(std::size_t device_index); |
| 343 | 340 | ||
| 344 | /** | 341 | /** |
| 345 | * Sets the desired data to be polled from a controller | 342 | * Sets the desired data to be polled from a controller |
diff --git a/src/core/hle/ipc_helpers.h b/src/core/hle/ipc_helpers.h index 0cc26a211..18fde8bd6 100644 --- a/src/core/hle/ipc_helpers.h +++ b/src/core/hle/ipc_helpers.h | |||
| @@ -86,13 +86,13 @@ public: | |||
| 86 | u32 num_domain_objects{}; | 86 | u32 num_domain_objects{}; |
| 87 | const bool always_move_handles{ | 87 | const bool always_move_handles{ |
| 88 | (static_cast<u32>(flags) & static_cast<u32>(Flags::AlwaysMoveHandles)) != 0}; | 88 | (static_cast<u32>(flags) & static_cast<u32>(Flags::AlwaysMoveHandles)) != 0}; |
| 89 | if (!ctx.Session()->IsDomain() || always_move_handles) { | 89 | if (!ctx.Session()->GetSessionRequestManager()->IsDomain() || always_move_handles) { |
| 90 | num_handles_to_move = num_objects_to_move; | 90 | num_handles_to_move = num_objects_to_move; |
| 91 | } else { | 91 | } else { |
| 92 | num_domain_objects = num_objects_to_move; | 92 | num_domain_objects = num_objects_to_move; |
| 93 | } | 93 | } |
| 94 | 94 | ||
| 95 | if (ctx.Session()->IsDomain()) { | 95 | if (ctx.Session()->GetSessionRequestManager()->IsDomain()) { |
| 96 | raw_data_size += | 96 | raw_data_size += |
| 97 | static_cast<u32>(sizeof(DomainMessageHeader) / sizeof(u32) + num_domain_objects); | 97 | static_cast<u32>(sizeof(DomainMessageHeader) / sizeof(u32) + num_domain_objects); |
| 98 | ctx.write_size += num_domain_objects; | 98 | ctx.write_size += num_domain_objects; |
| @@ -125,7 +125,8 @@ public: | |||
| 125 | if (!ctx.IsTipc()) { | 125 | if (!ctx.IsTipc()) { |
| 126 | AlignWithPadding(); | 126 | AlignWithPadding(); |
| 127 | 127 | ||
| 128 | if (ctx.Session()->IsDomain() && ctx.HasDomainMessageHeader()) { | 128 | if (ctx.Session()->GetSessionRequestManager()->IsDomain() && |
| 129 | ctx.HasDomainMessageHeader()) { | ||
| 129 | IPC::DomainMessageHeader domain_header{}; | 130 | IPC::DomainMessageHeader domain_header{}; |
| 130 | domain_header.num_objects = num_domain_objects; | 131 | domain_header.num_objects = num_domain_objects; |
| 131 | PushRaw(domain_header); | 132 | PushRaw(domain_header); |
| @@ -145,7 +146,7 @@ public: | |||
| 145 | 146 | ||
| 146 | template <class T> | 147 | template <class T> |
| 147 | void PushIpcInterface(std::shared_ptr<T> iface) { | 148 | void PushIpcInterface(std::shared_ptr<T> iface) { |
| 148 | if (context->Session()->IsDomain()) { | 149 | if (context->Session()->GetSessionRequestManager()->IsDomain()) { |
| 149 | context->AddDomainObject(std::move(iface)); | 150 | context->AddDomainObject(std::move(iface)); |
| 150 | } else { | 151 | } else { |
| 151 | kernel.CurrentProcess()->GetResourceLimit()->Reserve( | 152 | kernel.CurrentProcess()->GetResourceLimit()->Reserve( |
| @@ -386,7 +387,7 @@ public: | |||
| 386 | 387 | ||
| 387 | template <class T> | 388 | template <class T> |
| 388 | std::weak_ptr<T> PopIpcInterface() { | 389 | std::weak_ptr<T> PopIpcInterface() { |
| 389 | ASSERT(context->Session()->IsDomain()); | 390 | ASSERT(context->Session()->GetSessionRequestManager()->IsDomain()); |
| 390 | ASSERT(context->GetDomainMessageHeader().input_object_count > 0); | 391 | ASSERT(context->GetDomainMessageHeader().input_object_count > 0); |
| 391 | return context->GetDomainHandler<T>(Pop<u32>() - 1); | 392 | return context->GetDomainHandler<T>(Pop<u32>() - 1); |
| 392 | } | 393 | } |
| @@ -405,7 +406,7 @@ inline s32 RequestParser::Pop() { | |||
| 405 | } | 406 | } |
| 406 | 407 | ||
| 407 | // Ignore the -Wclass-memaccess warning on memcpy for non-trivially default constructible objects. | 408 | // Ignore the -Wclass-memaccess warning on memcpy for non-trivially default constructible objects. |
| 408 | #if defined(__GNUC__) | 409 | #if defined(__GNUC__) && !defined(__clang__) && !defined(__INTEL_COMPILER) |
| 409 | #pragma GCC diagnostic push | 410 | #pragma GCC diagnostic push |
| 410 | #pragma GCC diagnostic ignored "-Wclass-memaccess" | 411 | #pragma GCC diagnostic ignored "-Wclass-memaccess" |
| 411 | #endif | 412 | #endif |
| @@ -416,7 +417,7 @@ void RequestParser::PopRaw(T& value) { | |||
| 416 | std::memcpy(&value, cmdbuf + index, sizeof(T)); | 417 | std::memcpy(&value, cmdbuf + index, sizeof(T)); |
| 417 | index += (sizeof(T) + 3) / 4; // round up to word length | 418 | index += (sizeof(T) + 3) / 4; // round up to word length |
| 418 | } | 419 | } |
| 419 | #if defined(__GNUC__) | 420 | #if defined(__GNUC__) && !defined(__clang__) && !defined(__INTEL_COMPILER) |
| 420 | #pragma GCC diagnostic pop | 421 | #pragma GCC diagnostic pop |
| 421 | #endif | 422 | #endif |
| 422 | 423 | ||
diff --git a/src/core/hle/kernel/global_scheduler_context.cpp b/src/core/hle/kernel/global_scheduler_context.cpp index 65576b8c4..fd911a3a5 100644 --- a/src/core/hle/kernel/global_scheduler_context.cpp +++ b/src/core/hle/kernel/global_scheduler_context.cpp | |||
| @@ -49,4 +49,26 @@ bool GlobalSchedulerContext::IsLocked() const { | |||
| 49 | return scheduler_lock.IsLockedByCurrentThread(); | 49 | return scheduler_lock.IsLockedByCurrentThread(); |
| 50 | } | 50 | } |
| 51 | 51 | ||
| 52 | void GlobalSchedulerContext::RegisterDummyThreadForWakeup(KThread* thread) { | ||
| 53 | ASSERT(IsLocked()); | ||
| 54 | |||
| 55 | woken_dummy_threads.insert(thread); | ||
| 56 | } | ||
| 57 | |||
| 58 | void GlobalSchedulerContext::UnregisterDummyThreadForWakeup(KThread* thread) { | ||
| 59 | ASSERT(IsLocked()); | ||
| 60 | |||
| 61 | woken_dummy_threads.erase(thread); | ||
| 62 | } | ||
| 63 | |||
| 64 | void GlobalSchedulerContext::WakeupWaitingDummyThreads() { | ||
| 65 | ASSERT(IsLocked()); | ||
| 66 | |||
| 67 | for (auto* thread : woken_dummy_threads) { | ||
| 68 | thread->DummyThreadEndWait(); | ||
| 69 | } | ||
| 70 | |||
| 71 | woken_dummy_threads.clear(); | ||
| 72 | } | ||
| 73 | |||
| 52 | } // namespace Kernel | 74 | } // namespace Kernel |
diff --git a/src/core/hle/kernel/global_scheduler_context.h b/src/core/hle/kernel/global_scheduler_context.h index 67bb9852d..220ed6192 100644 --- a/src/core/hle/kernel/global_scheduler_context.h +++ b/src/core/hle/kernel/global_scheduler_context.h | |||
| @@ -4,6 +4,7 @@ | |||
| 4 | #pragma once | 4 | #pragma once |
| 5 | 5 | ||
| 6 | #include <atomic> | 6 | #include <atomic> |
| 7 | #include <set> | ||
| 7 | #include <vector> | 8 | #include <vector> |
| 8 | 9 | ||
| 9 | #include "common/common_types.h" | 10 | #include "common/common_types.h" |
| @@ -58,6 +59,10 @@ public: | |||
| 58 | /// Returns true if the global scheduler lock is acquired | 59 | /// Returns true if the global scheduler lock is acquired |
| 59 | bool IsLocked() const; | 60 | bool IsLocked() const; |
| 60 | 61 | ||
| 62 | void UnregisterDummyThreadForWakeup(KThread* thread); | ||
| 63 | void RegisterDummyThreadForWakeup(KThread* thread); | ||
| 64 | void WakeupWaitingDummyThreads(); | ||
| 65 | |||
| 61 | [[nodiscard]] LockType& SchedulerLock() { | 66 | [[nodiscard]] LockType& SchedulerLock() { |
| 62 | return scheduler_lock; | 67 | return scheduler_lock; |
| 63 | } | 68 | } |
| @@ -76,6 +81,9 @@ private: | |||
| 76 | KSchedulerPriorityQueue priority_queue; | 81 | KSchedulerPriorityQueue priority_queue; |
| 77 | LockType scheduler_lock; | 82 | LockType scheduler_lock; |
| 78 | 83 | ||
| 84 | /// Lists dummy threads pending wakeup on lock release | ||
| 85 | std::set<KThread*> woken_dummy_threads; | ||
| 86 | |||
| 79 | /// Lists all thread ids that aren't deleted/etc. | 87 | /// Lists all thread ids that aren't deleted/etc. |
| 80 | std::vector<KThread*> thread_list; | 88 | std::vector<KThread*> thread_list; |
| 81 | std::mutex global_list_guard; | 89 | std::mutex global_list_guard; |
diff --git a/src/core/hle/kernel/hle_ipc.cpp b/src/core/hle/kernel/hle_ipc.cpp index 5b3feec66..e4f43a053 100644 --- a/src/core/hle/kernel/hle_ipc.cpp +++ b/src/core/hle/kernel/hle_ipc.cpp | |||
| @@ -19,6 +19,7 @@ | |||
| 19 | #include "core/hle/kernel/k_server_session.h" | 19 | #include "core/hle/kernel/k_server_session.h" |
| 20 | #include "core/hle/kernel/k_thread.h" | 20 | #include "core/hle/kernel/k_thread.h" |
| 21 | #include "core/hle/kernel/kernel.h" | 21 | #include "core/hle/kernel/kernel.h" |
| 22 | #include "core/hle/kernel/service_thread.h" | ||
| 22 | #include "core/memory.h" | 23 | #include "core/memory.h" |
| 23 | 24 | ||
| 24 | namespace Kernel { | 25 | namespace Kernel { |
| @@ -56,16 +57,103 @@ bool SessionRequestManager::HasSessionRequestHandler(const HLERequestContext& co | |||
| 56 | } | 57 | } |
| 57 | } | 58 | } |
| 58 | 59 | ||
| 60 | Result SessionRequestManager::CompleteSyncRequest(KServerSession* server_session, | ||
| 61 | HLERequestContext& context) { | ||
| 62 | Result result = ResultSuccess; | ||
| 63 | |||
| 64 | // If the session has been converted to a domain, handle the domain request | ||
| 65 | if (this->HasSessionRequestHandler(context)) { | ||
| 66 | if (IsDomain() && context.HasDomainMessageHeader()) { | ||
| 67 | result = HandleDomainSyncRequest(server_session, context); | ||
| 68 | // If there is no domain header, the regular session handler is used | ||
| 69 | } else if (this->HasSessionHandler()) { | ||
| 70 | // If this manager has an associated HLE handler, forward the request to it. | ||
| 71 | result = this->SessionHandler().HandleSyncRequest(*server_session, context); | ||
| 72 | } | ||
| 73 | } else { | ||
| 74 | ASSERT_MSG(false, "Session handler is invalid, stubbing response!"); | ||
| 75 | IPC::ResponseBuilder rb(context, 2); | ||
| 76 | rb.Push(ResultSuccess); | ||
| 77 | } | ||
| 78 | |||
| 79 | if (convert_to_domain) { | ||
| 80 | ASSERT_MSG(!IsDomain(), "ServerSession is already a domain instance."); | ||
| 81 | this->ConvertToDomain(); | ||
| 82 | convert_to_domain = false; | ||
| 83 | } | ||
| 84 | |||
| 85 | return result; | ||
| 86 | } | ||
| 87 | |||
| 88 | Result SessionRequestManager::HandleDomainSyncRequest(KServerSession* server_session, | ||
| 89 | HLERequestContext& context) { | ||
| 90 | if (!context.HasDomainMessageHeader()) { | ||
| 91 | return ResultSuccess; | ||
| 92 | } | ||
| 93 | |||
| 94 | // Set domain handlers in HLE context, used for domain objects (IPC interfaces) as inputs | ||
| 95 | context.SetSessionRequestManager(server_session->GetSessionRequestManager()); | ||
| 96 | |||
| 97 | // If there is a DomainMessageHeader, then this is CommandType "Request" | ||
| 98 | const auto& domain_message_header = context.GetDomainMessageHeader(); | ||
| 99 | const u32 object_id{domain_message_header.object_id}; | ||
| 100 | switch (domain_message_header.command) { | ||
| 101 | case IPC::DomainMessageHeader::CommandType::SendMessage: | ||
| 102 | if (object_id > this->DomainHandlerCount()) { | ||
| 103 | LOG_CRITICAL(IPC, | ||
| 104 | "object_id {} is too big! This probably means a recent service call " | ||
| 105 | "needed to return a new interface!", | ||
| 106 | object_id); | ||
| 107 | ASSERT(false); | ||
| 108 | return ResultSuccess; // Ignore error if asserts are off | ||
| 109 | } | ||
| 110 | if (auto strong_ptr = this->DomainHandler(object_id - 1).lock()) { | ||
| 111 | return strong_ptr->HandleSyncRequest(*server_session, context); | ||
| 112 | } else { | ||
| 113 | ASSERT(false); | ||
| 114 | return ResultSuccess; | ||
| 115 | } | ||
| 116 | |||
| 117 | case IPC::DomainMessageHeader::CommandType::CloseVirtualHandle: { | ||
| 118 | LOG_DEBUG(IPC, "CloseVirtualHandle, object_id=0x{:08X}", object_id); | ||
| 119 | |||
| 120 | this->CloseDomainHandler(object_id - 1); | ||
| 121 | |||
| 122 | IPC::ResponseBuilder rb{context, 2}; | ||
| 123 | rb.Push(ResultSuccess); | ||
| 124 | return ResultSuccess; | ||
| 125 | } | ||
| 126 | } | ||
| 127 | |||
| 128 | LOG_CRITICAL(IPC, "Unknown domain command={}", domain_message_header.command.Value()); | ||
| 129 | ASSERT(false); | ||
| 130 | return ResultSuccess; | ||
| 131 | } | ||
| 132 | |||
| 133 | Result SessionRequestManager::QueueSyncRequest(KSession* parent, | ||
| 134 | std::shared_ptr<HLERequestContext>&& context) { | ||
| 135 | // Ensure we have a session request handler | ||
| 136 | if (this->HasSessionRequestHandler(*context)) { | ||
| 137 | if (auto strong_ptr = this->GetServiceThread().lock()) { | ||
| 138 | strong_ptr->QueueSyncRequest(*parent, std::move(context)); | ||
| 139 | } else { | ||
| 140 | ASSERT_MSG(false, "strong_ptr is nullptr!"); | ||
| 141 | } | ||
| 142 | } else { | ||
| 143 | ASSERT_MSG(false, "handler is invalid!"); | ||
| 144 | } | ||
| 145 | |||
| 146 | return ResultSuccess; | ||
| 147 | } | ||
| 148 | |||
| 59 | void SessionRequestHandler::ClientConnected(KServerSession* session) { | 149 | void SessionRequestHandler::ClientConnected(KServerSession* session) { |
| 60 | session->ClientConnected(shared_from_this()); | 150 | session->GetSessionRequestManager()->SetSessionHandler(shared_from_this()); |
| 61 | 151 | ||
| 62 | // Ensure our server session is tracked globally. | 152 | // Ensure our server session is tracked globally. |
| 63 | kernel.RegisterServerObject(session); | 153 | kernel.RegisterServerObject(session); |
| 64 | } | 154 | } |
| 65 | 155 | ||
| 66 | void SessionRequestHandler::ClientDisconnected(KServerSession* session) { | 156 | void SessionRequestHandler::ClientDisconnected(KServerSession* session) {} |
| 67 | session->ClientDisconnected(); | ||
| 68 | } | ||
| 69 | 157 | ||
| 70 | HLERequestContext::HLERequestContext(KernelCore& kernel_, Core::Memory::Memory& memory_, | 158 | HLERequestContext::HLERequestContext(KernelCore& kernel_, Core::Memory::Memory& memory_, |
| 71 | KServerSession* server_session_, KThread* thread_) | 159 | KServerSession* server_session_, KThread* thread_) |
| @@ -126,7 +214,7 @@ void HLERequestContext::ParseCommandBuffer(const KHandleTable& handle_table, u32 | |||
| 126 | // Padding to align to 16 bytes | 214 | // Padding to align to 16 bytes |
| 127 | rp.AlignWithPadding(); | 215 | rp.AlignWithPadding(); |
| 128 | 216 | ||
| 129 | if (Session()->IsDomain() && | 217 | if (Session()->GetSessionRequestManager()->IsDomain() && |
| 130 | ((command_header->type == IPC::CommandType::Request || | 218 | ((command_header->type == IPC::CommandType::Request || |
| 131 | command_header->type == IPC::CommandType::RequestWithContext) || | 219 | command_header->type == IPC::CommandType::RequestWithContext) || |
| 132 | !incoming)) { | 220 | !incoming)) { |
| @@ -135,7 +223,7 @@ void HLERequestContext::ParseCommandBuffer(const KHandleTable& handle_table, u32 | |||
| 135 | if (incoming || domain_message_header) { | 223 | if (incoming || domain_message_header) { |
| 136 | domain_message_header = rp.PopRaw<IPC::DomainMessageHeader>(); | 224 | domain_message_header = rp.PopRaw<IPC::DomainMessageHeader>(); |
| 137 | } else { | 225 | } else { |
| 138 | if (Session()->IsDomain()) { | 226 | if (Session()->GetSessionRequestManager()->IsDomain()) { |
| 139 | LOG_WARNING(IPC, "Domain request has no DomainMessageHeader!"); | 227 | LOG_WARNING(IPC, "Domain request has no DomainMessageHeader!"); |
| 140 | } | 228 | } |
| 141 | } | 229 | } |
| @@ -228,12 +316,12 @@ Result HLERequestContext::WriteToOutgoingCommandBuffer(KThread& requesting_threa | |||
| 228 | // Write the domain objects to the command buffer, these go after the raw untranslated data. | 316 | // Write the domain objects to the command buffer, these go after the raw untranslated data. |
| 229 | // TODO(Subv): This completely ignores C buffers. | 317 | // TODO(Subv): This completely ignores C buffers. |
| 230 | 318 | ||
| 231 | if (Session()->IsDomain()) { | 319 | if (server_session->GetSessionRequestManager()->IsDomain()) { |
| 232 | current_offset = domain_offset - static_cast<u32>(outgoing_domain_objects.size()); | 320 | current_offset = domain_offset - static_cast<u32>(outgoing_domain_objects.size()); |
| 233 | for (const auto& object : outgoing_domain_objects) { | 321 | for (auto& object : outgoing_domain_objects) { |
| 234 | server_session->AppendDomainHandler(object); | 322 | server_session->GetSessionRequestManager()->AppendDomainHandler(std::move(object)); |
| 235 | cmd_buf[current_offset++] = | 323 | cmd_buf[current_offset++] = static_cast<u32_le>( |
| 236 | static_cast<u32_le>(server_session->NumDomainRequestHandlers()); | 324 | server_session->GetSessionRequestManager()->DomainHandlerCount()); |
| 237 | } | 325 | } |
| 238 | } | 326 | } |
| 239 | 327 | ||
diff --git a/src/core/hle/kernel/hle_ipc.h b/src/core/hle/kernel/hle_ipc.h index e258e2cdf..1083638a9 100644 --- a/src/core/hle/kernel/hle_ipc.h +++ b/src/core/hle/kernel/hle_ipc.h | |||
| @@ -121,6 +121,10 @@ public: | |||
| 121 | is_domain = true; | 121 | is_domain = true; |
| 122 | } | 122 | } |
| 123 | 123 | ||
| 124 | void ConvertToDomainOnRequestEnd() { | ||
| 125 | convert_to_domain = true; | ||
| 126 | } | ||
| 127 | |||
| 124 | std::size_t DomainHandlerCount() const { | 128 | std::size_t DomainHandlerCount() const { |
| 125 | return domain_handlers.size(); | 129 | return domain_handlers.size(); |
| 126 | } | 130 | } |
| @@ -164,7 +168,12 @@ public: | |||
| 164 | 168 | ||
| 165 | bool HasSessionRequestHandler(const HLERequestContext& context) const; | 169 | bool HasSessionRequestHandler(const HLERequestContext& context) const; |
| 166 | 170 | ||
| 171 | Result HandleDomainSyncRequest(KServerSession* server_session, HLERequestContext& context); | ||
| 172 | Result CompleteSyncRequest(KServerSession* server_session, HLERequestContext& context); | ||
| 173 | Result QueueSyncRequest(KSession* parent, std::shared_ptr<HLERequestContext>&& context); | ||
| 174 | |||
| 167 | private: | 175 | private: |
| 176 | bool convert_to_domain{}; | ||
| 168 | bool is_domain{}; | 177 | bool is_domain{}; |
| 169 | SessionRequestHandlerPtr session_handler; | 178 | SessionRequestHandlerPtr session_handler; |
| 170 | std::vector<SessionRequestHandlerPtr> domain_handlers; | 179 | std::vector<SessionRequestHandlerPtr> domain_handlers; |
| @@ -295,7 +304,7 @@ public: | |||
| 295 | */ | 304 | */ |
| 296 | template <typename T, typename = std::enable_if_t<!std::is_pointer_v<T>>> | 305 | template <typename T, typename = std::enable_if_t<!std::is_pointer_v<T>>> |
| 297 | std::size_t WriteBuffer(const T& data, std::size_t buffer_index = 0) const { | 306 | std::size_t WriteBuffer(const T& data, std::size_t buffer_index = 0) const { |
| 298 | if constexpr (Common::IsSTLContainer<T>) { | 307 | if constexpr (Common::IsContiguousContainer<T>) { |
| 299 | using ContiguousType = typename T::value_type; | 308 | using ContiguousType = typename T::value_type; |
| 300 | static_assert(std::is_trivially_copyable_v<ContiguousType>, | 309 | static_assert(std::is_trivially_copyable_v<ContiguousType>, |
| 301 | "Container to WriteBuffer must contain trivially copyable objects"); | 310 | "Container to WriteBuffer must contain trivially copyable objects"); |
diff --git a/src/core/hle/kernel/init/init_slab_setup.cpp b/src/core/hle/kernel/init/init_slab_setup.cpp index c84d36c8c..477e4e407 100644 --- a/src/core/hle/kernel/init/init_slab_setup.cpp +++ b/src/core/hle/kernel/init/init_slab_setup.cpp | |||
| @@ -18,6 +18,7 @@ | |||
| 18 | #include "core/hle/kernel/k_process.h" | 18 | #include "core/hle/kernel/k_process.h" |
| 19 | #include "core/hle/kernel/k_resource_limit.h" | 19 | #include "core/hle/kernel/k_resource_limit.h" |
| 20 | #include "core/hle/kernel/k_session.h" | 20 | #include "core/hle/kernel/k_session.h" |
| 21 | #include "core/hle/kernel/k_session_request.h" | ||
| 21 | #include "core/hle/kernel/k_shared_memory.h" | 22 | #include "core/hle/kernel/k_shared_memory.h" |
| 22 | #include "core/hle/kernel/k_shared_memory_info.h" | 23 | #include "core/hle/kernel/k_shared_memory_info.h" |
| 23 | #include "core/hle/kernel/k_system_control.h" | 24 | #include "core/hle/kernel/k_system_control.h" |
| @@ -34,6 +35,7 @@ namespace Kernel::Init { | |||
| 34 | HANDLER(KThread, (SLAB_COUNT(KThread)), ##__VA_ARGS__) \ | 35 | HANDLER(KThread, (SLAB_COUNT(KThread)), ##__VA_ARGS__) \ |
| 35 | HANDLER(KEvent, (SLAB_COUNT(KEvent)), ##__VA_ARGS__) \ | 36 | HANDLER(KEvent, (SLAB_COUNT(KEvent)), ##__VA_ARGS__) \ |
| 36 | HANDLER(KPort, (SLAB_COUNT(KPort)), ##__VA_ARGS__) \ | 37 | HANDLER(KPort, (SLAB_COUNT(KPort)), ##__VA_ARGS__) \ |
| 38 | HANDLER(KSessionRequest, (SLAB_COUNT(KSession) * 2), ##__VA_ARGS__) \ | ||
| 37 | HANDLER(KSharedMemory, (SLAB_COUNT(KSharedMemory)), ##__VA_ARGS__) \ | 39 | HANDLER(KSharedMemory, (SLAB_COUNT(KSharedMemory)), ##__VA_ARGS__) \ |
| 38 | HANDLER(KSharedMemoryInfo, (SLAB_COUNT(KSharedMemory) * 8), ##__VA_ARGS__) \ | 40 | HANDLER(KSharedMemoryInfo, (SLAB_COUNT(KSharedMemory) * 8), ##__VA_ARGS__) \ |
| 39 | HANDLER(KTransferMemory, (SLAB_COUNT(KTransferMemory)), ##__VA_ARGS__) \ | 41 | HANDLER(KTransferMemory, (SLAB_COUNT(KTransferMemory)), ##__VA_ARGS__) \ |
diff --git a/src/core/hle/kernel/k_client_session.cpp b/src/core/hle/kernel/k_client_session.cpp index 8892c5b7c..b4197a8d5 100644 --- a/src/core/hle/kernel/k_client_session.cpp +++ b/src/core/hle/kernel/k_client_session.cpp | |||
| @@ -1,6 +1,7 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project | 1 | // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project |
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | 2 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 3 | 3 | ||
| 4 | #include "common/scope_exit.h" | ||
| 4 | #include "core/hle/kernel/hle_ipc.h" | 5 | #include "core/hle/kernel/hle_ipc.h" |
| 5 | #include "core/hle/kernel/k_client_session.h" | 6 | #include "core/hle/kernel/k_client_session.h" |
| 6 | #include "core/hle/kernel/k_server_session.h" | 7 | #include "core/hle/kernel/k_server_session.h" |
| @@ -10,6 +11,8 @@ | |||
| 10 | 11 | ||
| 11 | namespace Kernel { | 12 | namespace Kernel { |
| 12 | 13 | ||
| 14 | static constexpr u32 MessageBufferSize = 0x100; | ||
| 15 | |||
| 13 | KClientSession::KClientSession(KernelCore& kernel_) | 16 | KClientSession::KClientSession(KernelCore& kernel_) |
| 14 | : KAutoObjectWithSlabHeapAndContainer{kernel_} {} | 17 | : KAutoObjectWithSlabHeapAndContainer{kernel_} {} |
| 15 | KClientSession::~KClientSession() = default; | 18 | KClientSession::~KClientSession() = default; |
| @@ -22,8 +25,16 @@ void KClientSession::Destroy() { | |||
| 22 | void KClientSession::OnServerClosed() {} | 25 | void KClientSession::OnServerClosed() {} |
| 23 | 26 | ||
| 24 | Result KClientSession::SendSyncRequest() { | 27 | Result KClientSession::SendSyncRequest() { |
| 25 | // Signal the server session that new data is available | 28 | // Create a session request. |
| 26 | return parent->GetServerSession().OnRequest(); | 29 | KSessionRequest* request = KSessionRequest::Create(kernel); |
| 30 | R_UNLESS(request != nullptr, ResultOutOfResource); | ||
| 31 | SCOPE_EXIT({ request->Close(); }); | ||
| 32 | |||
| 33 | // Initialize the request. | ||
| 34 | request->Initialize(nullptr, GetCurrentThread(kernel).GetTLSAddress(), MessageBufferSize); | ||
| 35 | |||
| 36 | // Send the request. | ||
| 37 | return parent->GetServerSession().OnRequest(request); | ||
| 27 | } | 38 | } |
| 28 | 39 | ||
| 29 | } // namespace Kernel | 40 | } // namespace Kernel |
diff --git a/src/core/hle/kernel/k_linked_list.h b/src/core/hle/kernel/k_linked_list.h index 78859ced3..29ebd16b7 100644 --- a/src/core/hle/kernel/k_linked_list.h +++ b/src/core/hle/kernel/k_linked_list.h | |||
| @@ -16,6 +16,7 @@ class KLinkedListNode : public boost::intrusive::list_base_hook<>, | |||
| 16 | public KSlabAllocated<KLinkedListNode> { | 16 | public KSlabAllocated<KLinkedListNode> { |
| 17 | 17 | ||
| 18 | public: | 18 | public: |
| 19 | explicit KLinkedListNode(KernelCore&) {} | ||
| 19 | KLinkedListNode() = default; | 20 | KLinkedListNode() = default; |
| 20 | 21 | ||
| 21 | void Initialize(void* it) { | 22 | void Initialize(void* it) { |
diff --git a/src/core/hle/kernel/k_page_buffer.h b/src/core/hle/kernel/k_page_buffer.h index 7e50dc1d1..aef06e213 100644 --- a/src/core/hle/kernel/k_page_buffer.h +++ b/src/core/hle/kernel/k_page_buffer.h | |||
| @@ -13,6 +13,7 @@ namespace Kernel { | |||
| 13 | 13 | ||
| 14 | class KPageBuffer final : public KSlabAllocated<KPageBuffer> { | 14 | class KPageBuffer final : public KSlabAllocated<KPageBuffer> { |
| 15 | public: | 15 | public: |
| 16 | explicit KPageBuffer(KernelCore&) {} | ||
| 16 | KPageBuffer() = default; | 17 | KPageBuffer() = default; |
| 17 | 18 | ||
| 18 | static KPageBuffer* FromPhysicalAddress(Core::System& system, PAddr phys_addr); | 19 | static KPageBuffer* FromPhysicalAddress(Core::System& system, PAddr phys_addr); |
diff --git a/src/core/hle/kernel/k_scheduler.cpp b/src/core/hle/kernel/k_scheduler.cpp index c34ce7a17..b1cabbca0 100644 --- a/src/core/hle/kernel/k_scheduler.cpp +++ b/src/core/hle/kernel/k_scheduler.cpp | |||
| @@ -81,8 +81,8 @@ void KScheduler::RescheduleCurrentHLEThread(KernelCore& kernel) { | |||
| 81 | // HACK: we cannot schedule from this thread, it is not a core thread | 81 | // HACK: we cannot schedule from this thread, it is not a core thread |
| 82 | ASSERT(GetCurrentThread(kernel).GetDisableDispatchCount() == 1); | 82 | ASSERT(GetCurrentThread(kernel).GetDisableDispatchCount() == 1); |
| 83 | 83 | ||
| 84 | // Special case to ensure dummy threads that are waiting block | 84 | // Ensure dummy threads that are waiting block. |
| 85 | GetCurrentThread(kernel).IfDummyThreadTryWait(); | 85 | GetCurrentThread(kernel).DummyThreadBeginWait(); |
| 86 | 86 | ||
| 87 | ASSERT(GetCurrentThread(kernel).GetState() != ThreadState::Waiting); | 87 | ASSERT(GetCurrentThread(kernel).GetState() != ThreadState::Waiting); |
| 88 | GetCurrentThread(kernel).EnableDispatch(); | 88 | GetCurrentThread(kernel).EnableDispatch(); |
| @@ -314,6 +314,16 @@ u64 KScheduler::UpdateHighestPriorityThreadsImpl(KernelCore& kernel) { | |||
| 314 | idle_cores &= ~(1ULL << core_id); | 314 | idle_cores &= ~(1ULL << core_id); |
| 315 | } | 315 | } |
| 316 | 316 | ||
| 317 | // HACK: any waiting dummy threads can wake up now. | ||
| 318 | kernel.GlobalSchedulerContext().WakeupWaitingDummyThreads(); | ||
| 319 | |||
| 320 | // HACK: if we are a dummy thread, and we need to go sleep, indicate | ||
| 321 | // that for when the lock is released. | ||
| 322 | KThread* const cur_thread = GetCurrentThreadPointer(kernel); | ||
| 323 | if (cur_thread->IsDummyThread() && cur_thread->GetState() != ThreadState::Runnable) { | ||
| 324 | cur_thread->RequestDummyThreadWait(); | ||
| 325 | } | ||
| 326 | |||
| 317 | return cores_needing_scheduling; | 327 | return cores_needing_scheduling; |
| 318 | } | 328 | } |
| 319 | 329 | ||
| @@ -531,11 +541,23 @@ void KScheduler::OnThreadStateChanged(KernelCore& kernel, KThread* thread, Threa | |||
| 531 | GetPriorityQueue(kernel).Remove(thread); | 541 | GetPriorityQueue(kernel).Remove(thread); |
| 532 | IncrementScheduledCount(thread); | 542 | IncrementScheduledCount(thread); |
| 533 | SetSchedulerUpdateNeeded(kernel); | 543 | SetSchedulerUpdateNeeded(kernel); |
| 544 | |||
| 545 | if (thread->IsDummyThread()) { | ||
| 546 | // HACK: if this is a dummy thread, it should no longer wake up when the | ||
| 547 | // scheduler lock is released. | ||
| 548 | kernel.GlobalSchedulerContext().UnregisterDummyThreadForWakeup(thread); | ||
| 549 | } | ||
| 534 | } else if (cur_state == ThreadState::Runnable) { | 550 | } else if (cur_state == ThreadState::Runnable) { |
| 535 | // If we're now runnable, then we weren't previously, and we should add. | 551 | // If we're now runnable, then we weren't previously, and we should add. |
| 536 | GetPriorityQueue(kernel).PushBack(thread); | 552 | GetPriorityQueue(kernel).PushBack(thread); |
| 537 | IncrementScheduledCount(thread); | 553 | IncrementScheduledCount(thread); |
| 538 | SetSchedulerUpdateNeeded(kernel); | 554 | SetSchedulerUpdateNeeded(kernel); |
| 555 | |||
| 556 | if (thread->IsDummyThread()) { | ||
| 557 | // HACK: if this is a dummy thread, it should wake up when the scheduler | ||
| 558 | // lock is released. | ||
| 559 | kernel.GlobalSchedulerContext().RegisterDummyThreadForWakeup(thread); | ||
| 560 | } | ||
| 539 | } | 561 | } |
| 540 | } | 562 | } |
| 541 | 563 | ||
diff --git a/src/core/hle/kernel/k_server_session.cpp b/src/core/hle/kernel/k_server_session.cpp index 4252c9adb..faf03fcc8 100644 --- a/src/core/hle/kernel/k_server_session.cpp +++ b/src/core/hle/kernel/k_server_session.cpp | |||
| @@ -22,15 +22,12 @@ | |||
| 22 | #include "core/hle/kernel/k_thread.h" | 22 | #include "core/hle/kernel/k_thread.h" |
| 23 | #include "core/hle/kernel/k_thread_queue.h" | 23 | #include "core/hle/kernel/k_thread_queue.h" |
| 24 | #include "core/hle/kernel/kernel.h" | 24 | #include "core/hle/kernel/kernel.h" |
| 25 | #include "core/hle/kernel/service_thread.h" | ||
| 26 | #include "core/memory.h" | 25 | #include "core/memory.h" |
| 27 | 26 | ||
| 28 | namespace Kernel { | 27 | namespace Kernel { |
| 29 | 28 | ||
| 30 | using ThreadQueueImplForKServerSessionRequest = KThreadQueue; | 29 | using ThreadQueueImplForKServerSessionRequest = KThreadQueue; |
| 31 | 30 | ||
| 32 | static constexpr u32 MessageBufferSize = 0x100; | ||
| 33 | |||
| 34 | KServerSession::KServerSession(KernelCore& kernel_) | 31 | KServerSession::KServerSession(KernelCore& kernel_) |
| 35 | : KSynchronizationObject{kernel_}, m_lock{kernel_} {} | 32 | : KSynchronizationObject{kernel_}, m_lock{kernel_} {} |
| 36 | 33 | ||
| @@ -73,59 +70,7 @@ bool KServerSession::IsSignaled() const { | |||
| 73 | } | 70 | } |
| 74 | 71 | ||
| 75 | // Otherwise, we're signaled if we have a request and aren't handling one. | 72 | // Otherwise, we're signaled if we have a request and aren't handling one. |
| 76 | return !m_thread_request_list.empty() && m_current_thread_request == nullptr; | 73 | return !m_request_list.empty() && m_current_request == nullptr; |
| 77 | } | ||
| 78 | |||
| 79 | void KServerSession::AppendDomainHandler(SessionRequestHandlerPtr handler) { | ||
| 80 | manager->AppendDomainHandler(std::move(handler)); | ||
| 81 | } | ||
| 82 | |||
| 83 | std::size_t KServerSession::NumDomainRequestHandlers() const { | ||
| 84 | return manager->DomainHandlerCount(); | ||
| 85 | } | ||
| 86 | |||
| 87 | Result KServerSession::HandleDomainSyncRequest(Kernel::HLERequestContext& context) { | ||
| 88 | if (!context.HasDomainMessageHeader()) { | ||
| 89 | return ResultSuccess; | ||
| 90 | } | ||
| 91 | |||
| 92 | // Set domain handlers in HLE context, used for domain objects (IPC interfaces) as inputs | ||
| 93 | context.SetSessionRequestManager(manager); | ||
| 94 | |||
| 95 | // If there is a DomainMessageHeader, then this is CommandType "Request" | ||
| 96 | const auto& domain_message_header = context.GetDomainMessageHeader(); | ||
| 97 | const u32 object_id{domain_message_header.object_id}; | ||
| 98 | switch (domain_message_header.command) { | ||
| 99 | case IPC::DomainMessageHeader::CommandType::SendMessage: | ||
| 100 | if (object_id > manager->DomainHandlerCount()) { | ||
| 101 | LOG_CRITICAL(IPC, | ||
| 102 | "object_id {} is too big! This probably means a recent service call " | ||
| 103 | "to {} needed to return a new interface!", | ||
| 104 | object_id, name); | ||
| 105 | ASSERT(false); | ||
| 106 | return ResultSuccess; // Ignore error if asserts are off | ||
| 107 | } | ||
| 108 | if (auto strong_ptr = manager->DomainHandler(object_id - 1).lock()) { | ||
| 109 | return strong_ptr->HandleSyncRequest(*this, context); | ||
| 110 | } else { | ||
| 111 | ASSERT(false); | ||
| 112 | return ResultSuccess; | ||
| 113 | } | ||
| 114 | |||
| 115 | case IPC::DomainMessageHeader::CommandType::CloseVirtualHandle: { | ||
| 116 | LOG_DEBUG(IPC, "CloseVirtualHandle, object_id=0x{:08X}", object_id); | ||
| 117 | |||
| 118 | manager->CloseDomainHandler(object_id - 1); | ||
| 119 | |||
| 120 | IPC::ResponseBuilder rb{context, 2}; | ||
| 121 | rb.Push(ResultSuccess); | ||
| 122 | return ResultSuccess; | ||
| 123 | } | ||
| 124 | } | ||
| 125 | |||
| 126 | LOG_CRITICAL(IPC, "Unknown domain command={}", domain_message_header.command.Value()); | ||
| 127 | ASSERT(false); | ||
| 128 | return ResultSuccess; | ||
| 129 | } | 74 | } |
| 130 | 75 | ||
| 131 | Result KServerSession::QueueSyncRequest(KThread* thread, Core::Memory::Memory& memory) { | 76 | Result KServerSession::QueueSyncRequest(KThread* thread, Core::Memory::Memory& memory) { |
| @@ -134,43 +79,11 @@ Result KServerSession::QueueSyncRequest(KThread* thread, Core::Memory::Memory& m | |||
| 134 | 79 | ||
| 135 | context->PopulateFromIncomingCommandBuffer(kernel.CurrentProcess()->GetHandleTable(), cmd_buf); | 80 | context->PopulateFromIncomingCommandBuffer(kernel.CurrentProcess()->GetHandleTable(), cmd_buf); |
| 136 | 81 | ||
| 137 | // Ensure we have a session request handler | 82 | return manager->QueueSyncRequest(parent, std::move(context)); |
| 138 | if (manager->HasSessionRequestHandler(*context)) { | ||
| 139 | if (auto strong_ptr = manager->GetServiceThread().lock()) { | ||
| 140 | strong_ptr->QueueSyncRequest(*parent, std::move(context)); | ||
| 141 | } else { | ||
| 142 | ASSERT_MSG(false, "strong_ptr is nullptr!"); | ||
| 143 | } | ||
| 144 | } else { | ||
| 145 | ASSERT_MSG(false, "handler is invalid!"); | ||
| 146 | } | ||
| 147 | |||
| 148 | return ResultSuccess; | ||
| 149 | } | 83 | } |
| 150 | 84 | ||
| 151 | Result KServerSession::CompleteSyncRequest(HLERequestContext& context) { | 85 | Result KServerSession::CompleteSyncRequest(HLERequestContext& context) { |
| 152 | Result result = ResultSuccess; | 86 | Result result = manager->CompleteSyncRequest(this, context); |
| 153 | |||
| 154 | // If the session has been converted to a domain, handle the domain request | ||
| 155 | if (manager->HasSessionRequestHandler(context)) { | ||
| 156 | if (IsDomain() && context.HasDomainMessageHeader()) { | ||
| 157 | result = HandleDomainSyncRequest(context); | ||
| 158 | // If there is no domain header, the regular session handler is used | ||
| 159 | } else if (manager->HasSessionHandler()) { | ||
| 160 | // If this ServerSession has an associated HLE handler, forward the request to it. | ||
| 161 | result = manager->SessionHandler().HandleSyncRequest(*this, context); | ||
| 162 | } | ||
| 163 | } else { | ||
| 164 | ASSERT_MSG(false, "Session handler is invalid, stubbing response!"); | ||
| 165 | IPC::ResponseBuilder rb(context, 2); | ||
| 166 | rb.Push(ResultSuccess); | ||
| 167 | } | ||
| 168 | |||
| 169 | if (convert_to_domain) { | ||
| 170 | ASSERT_MSG(!IsDomain(), "ServerSession is already a domain instance."); | ||
| 171 | manager->ConvertToDomain(); | ||
| 172 | convert_to_domain = false; | ||
| 173 | } | ||
| 174 | 87 | ||
| 175 | // The calling thread is waiting for this request to complete, so wake it up. | 88 | // The calling thread is waiting for this request to complete, so wake it up. |
| 176 | context.GetThread().EndWait(result); | 89 | context.GetThread().EndWait(result); |
| @@ -178,7 +91,7 @@ Result KServerSession::CompleteSyncRequest(HLERequestContext& context) { | |||
| 178 | return result; | 91 | return result; |
| 179 | } | 92 | } |
| 180 | 93 | ||
| 181 | Result KServerSession::OnRequest() { | 94 | Result KServerSession::OnRequest(KSessionRequest* request) { |
| 182 | // Create the wait queue. | 95 | // Create the wait queue. |
| 183 | ThreadQueueImplForKServerSessionRequest wait_queue{kernel}; | 96 | ThreadQueueImplForKServerSessionRequest wait_queue{kernel}; |
| 184 | 97 | ||
| @@ -198,14 +111,13 @@ Result KServerSession::OnRequest() { | |||
| 198 | this->QueueSyncRequest(GetCurrentThreadPointer(kernel), memory); | 111 | this->QueueSyncRequest(GetCurrentThreadPointer(kernel), memory); |
| 199 | } else { | 112 | } else { |
| 200 | // Non-HLE request. | 113 | // Non-HLE request. |
| 201 | auto* thread{GetCurrentThreadPointer(kernel)}; | ||
| 202 | 114 | ||
| 203 | // Get whether we're empty. | 115 | // Get whether we're empty. |
| 204 | const bool was_empty = m_thread_request_list.empty(); | 116 | const bool was_empty = m_request_list.empty(); |
| 205 | 117 | ||
| 206 | // Add the thread to the list. | 118 | // Add the request to the list. |
| 207 | thread->Open(); | 119 | request->Open(); |
| 208 | m_thread_request_list.push_back(thread); | 120 | m_request_list.push_back(*request); |
| 209 | 121 | ||
| 210 | // If we were empty, signal. | 122 | // If we were empty, signal. |
| 211 | if (was_empty) { | 123 | if (was_empty) { |
| @@ -213,6 +125,9 @@ Result KServerSession::OnRequest() { | |||
| 213 | } | 125 | } |
| 214 | } | 126 | } |
| 215 | 127 | ||
| 128 | // If we have a request event, this is asynchronous, and we don't need to wait. | ||
| 129 | R_SUCCEED_IF(request->GetEvent() != nullptr); | ||
| 130 | |||
| 216 | // This is a synchronous request, so we should wait for our request to complete. | 131 | // This is a synchronous request, so we should wait for our request to complete. |
| 217 | GetCurrentThread(kernel).SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::IPC); | 132 | GetCurrentThread(kernel).SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::IPC); |
| 218 | GetCurrentThread(kernel).BeginWait(&wait_queue); | 133 | GetCurrentThread(kernel).BeginWait(&wait_queue); |
| @@ -223,32 +138,32 @@ Result KServerSession::OnRequest() { | |||
| 223 | 138 | ||
| 224 | Result KServerSession::SendReply() { | 139 | Result KServerSession::SendReply() { |
| 225 | // Lock the session. | 140 | // Lock the session. |
| 226 | KScopedLightLock lk(m_lock); | 141 | KScopedLightLock lk{m_lock}; |
| 227 | 142 | ||
| 228 | // Get the request. | 143 | // Get the request. |
| 229 | KThread* client_thread; | 144 | KSessionRequest* request; |
| 230 | { | 145 | { |
| 231 | KScopedSchedulerLock sl{kernel}; | 146 | KScopedSchedulerLock sl{kernel}; |
| 232 | 147 | ||
| 233 | // Get the current request. | 148 | // Get the current request. |
| 234 | client_thread = m_current_thread_request; | 149 | request = m_current_request; |
| 235 | R_UNLESS(client_thread != nullptr, ResultInvalidState); | 150 | R_UNLESS(request != nullptr, ResultInvalidState); |
| 236 | 151 | ||
| 237 | // Clear the current request, since we're processing it. | 152 | // Clear the current request, since we're processing it. |
| 238 | m_current_thread_request = nullptr; | 153 | m_current_request = nullptr; |
| 239 | if (!m_thread_request_list.empty()) { | 154 | if (!m_request_list.empty()) { |
| 240 | this->NotifyAvailable(); | 155 | this->NotifyAvailable(); |
| 241 | } | 156 | } |
| 242 | } | 157 | } |
| 243 | 158 | ||
| 244 | // Close reference to the request once we're done processing it. | 159 | // Close reference to the request once we're done processing it. |
| 245 | SCOPE_EXIT({ client_thread->Close(); }); | 160 | SCOPE_EXIT({ request->Close(); }); |
| 246 | 161 | ||
| 247 | // Extract relevant information from the request. | 162 | // Extract relevant information from the request. |
| 248 | // const uintptr_t client_message = request->GetAddress(); | 163 | const uintptr_t client_message = request->GetAddress(); |
| 249 | // const size_t client_buffer_size = request->GetSize(); | 164 | const size_t client_buffer_size = request->GetSize(); |
| 250 | // KThread *client_thread = request->GetThread(); | 165 | KThread* client_thread = request->GetThread(); |
| 251 | // KEvent *event = request->GetEvent(); | 166 | KEvent* event = request->GetEvent(); |
| 252 | 167 | ||
| 253 | // Check whether we're closed. | 168 | // Check whether we're closed. |
| 254 | const bool closed = (client_thread == nullptr || parent->IsClientClosed()); | 169 | const bool closed = (client_thread == nullptr || parent->IsClientClosed()); |
| @@ -261,8 +176,8 @@ Result KServerSession::SendReply() { | |||
| 261 | UNIMPLEMENTED_IF(server_thread->GetOwnerProcess() != client_thread->GetOwnerProcess()); | 176 | UNIMPLEMENTED_IF(server_thread->GetOwnerProcess() != client_thread->GetOwnerProcess()); |
| 262 | 177 | ||
| 263 | auto* src_msg_buffer = memory.GetPointer(server_thread->GetTLSAddress()); | 178 | auto* src_msg_buffer = memory.GetPointer(server_thread->GetTLSAddress()); |
| 264 | auto* dst_msg_buffer = memory.GetPointer(client_thread->GetTLSAddress()); | 179 | auto* dst_msg_buffer = memory.GetPointer(client_message); |
| 265 | std::memcpy(dst_msg_buffer, src_msg_buffer, MessageBufferSize); | 180 | std::memcpy(dst_msg_buffer, src_msg_buffer, client_buffer_size); |
| 266 | } else { | 181 | } else { |
| 267 | result = ResultSessionClosed; | 182 | result = ResultSessionClosed; |
| 268 | } | 183 | } |
| @@ -278,11 +193,30 @@ Result KServerSession::SendReply() { | |||
| 278 | 193 | ||
| 279 | // If there's a client thread, update it. | 194 | // If there's a client thread, update it. |
| 280 | if (client_thread != nullptr) { | 195 | if (client_thread != nullptr) { |
| 281 | // End the client thread's wait. | 196 | if (event != nullptr) { |
| 282 | KScopedSchedulerLock sl{kernel}; | 197 | // // Get the client process/page table. |
| 198 | // KProcess *client_process = client_thread->GetOwnerProcess(); | ||
| 199 | // KPageTable *client_page_table = &client_process->PageTable(); | ||
| 200 | |||
| 201 | // // If we need to, reply with an async error. | ||
| 202 | // if (R_FAILED(client_result)) { | ||
| 203 | // ReplyAsyncError(client_process, client_message, client_buffer_size, | ||
| 204 | // client_result); | ||
| 205 | // } | ||
| 206 | |||
| 207 | // // Unlock the client buffer. | ||
| 208 | // // NOTE: Nintendo does not check the result of this. | ||
| 209 | // client_page_table->UnlockForIpcUserBuffer(client_message, client_buffer_size); | ||
| 210 | |||
| 211 | // Signal the event. | ||
| 212 | event->Signal(); | ||
| 213 | } else { | ||
| 214 | // End the client thread's wait. | ||
| 215 | KScopedSchedulerLock sl{kernel}; | ||
| 283 | 216 | ||
| 284 | if (!client_thread->IsTerminationRequested()) { | 217 | if (!client_thread->IsTerminationRequested()) { |
| 285 | client_thread->EndWait(client_result); | 218 | client_thread->EndWait(client_result); |
| 219 | } | ||
| 286 | } | 220 | } |
| 287 | } | 221 | } |
| 288 | 222 | ||
| @@ -291,10 +225,10 @@ Result KServerSession::SendReply() { | |||
| 291 | 225 | ||
| 292 | Result KServerSession::ReceiveRequest() { | 226 | Result KServerSession::ReceiveRequest() { |
| 293 | // Lock the session. | 227 | // Lock the session. |
| 294 | KScopedLightLock lk(m_lock); | 228 | KScopedLightLock lk{m_lock}; |
| 295 | 229 | ||
| 296 | // Get the request and client thread. | 230 | // Get the request and client thread. |
| 297 | // KSessionRequest *request; | 231 | KSessionRequest* request; |
| 298 | KThread* client_thread; | 232 | KThread* client_thread; |
| 299 | 233 | ||
| 300 | { | 234 | { |
| @@ -304,35 +238,41 @@ Result KServerSession::ReceiveRequest() { | |||
| 304 | R_UNLESS(!parent->IsClientClosed(), ResultSessionClosed); | 238 | R_UNLESS(!parent->IsClientClosed(), ResultSessionClosed); |
| 305 | 239 | ||
| 306 | // Ensure we aren't already servicing a request. | 240 | // Ensure we aren't already servicing a request. |
| 307 | R_UNLESS(m_current_thread_request == nullptr, ResultNotFound); | 241 | R_UNLESS(m_current_request == nullptr, ResultNotFound); |
| 308 | 242 | ||
| 309 | // Ensure we have a request to service. | 243 | // Ensure we have a request to service. |
| 310 | R_UNLESS(!m_thread_request_list.empty(), ResultNotFound); | 244 | R_UNLESS(!m_request_list.empty(), ResultNotFound); |
| 311 | 245 | ||
| 312 | // Pop the first request from the list. | 246 | // Pop the first request from the list. |
| 313 | client_thread = m_thread_request_list.front(); | 247 | request = &m_request_list.front(); |
| 314 | m_thread_request_list.pop_front(); | 248 | m_request_list.pop_front(); |
| 315 | 249 | ||
| 316 | // Get the thread for the request. | 250 | // Get the thread for the request. |
| 251 | client_thread = request->GetThread(); | ||
| 317 | R_UNLESS(client_thread != nullptr, ResultSessionClosed); | 252 | R_UNLESS(client_thread != nullptr, ResultSessionClosed); |
| 318 | 253 | ||
| 319 | // Open the client thread. | 254 | // Open the client thread. |
| 320 | client_thread->Open(); | 255 | client_thread->Open(); |
| 321 | } | 256 | } |
| 322 | 257 | ||
| 323 | // SCOPE_EXIT({ client_thread->Close(); }); | 258 | SCOPE_EXIT({ client_thread->Close(); }); |
| 324 | 259 | ||
| 325 | // Set the request as our current. | 260 | // Set the request as our current. |
| 326 | m_current_thread_request = client_thread; | 261 | m_current_request = request; |
| 262 | |||
| 263 | // Get the client address. | ||
| 264 | uintptr_t client_message = request->GetAddress(); | ||
| 265 | size_t client_buffer_size = request->GetSize(); | ||
| 266 | // bool recv_list_broken = false; | ||
| 327 | 267 | ||
| 328 | // Receive the message. | 268 | // Receive the message. |
| 329 | Core::Memory::Memory& memory{kernel.System().Memory()}; | 269 | Core::Memory::Memory& memory{kernel.System().Memory()}; |
| 330 | KThread* server_thread{GetCurrentThreadPointer(kernel)}; | 270 | KThread* server_thread{GetCurrentThreadPointer(kernel)}; |
| 331 | UNIMPLEMENTED_IF(server_thread->GetOwnerProcess() != client_thread->GetOwnerProcess()); | 271 | UNIMPLEMENTED_IF(server_thread->GetOwnerProcess() != client_thread->GetOwnerProcess()); |
| 332 | 272 | ||
| 333 | auto* src_msg_buffer = memory.GetPointer(client_thread->GetTLSAddress()); | 273 | auto* src_msg_buffer = memory.GetPointer(client_message); |
| 334 | auto* dst_msg_buffer = memory.GetPointer(server_thread->GetTLSAddress()); | 274 | auto* dst_msg_buffer = memory.GetPointer(server_thread->GetTLSAddress()); |
| 335 | std::memcpy(dst_msg_buffer, src_msg_buffer, MessageBufferSize); | 275 | std::memcpy(dst_msg_buffer, src_msg_buffer, client_buffer_size); |
| 336 | 276 | ||
| 337 | // We succeeded. | 277 | // We succeeded. |
| 338 | return ResultSuccess; | 278 | return ResultSuccess; |
| @@ -344,35 +284,34 @@ void KServerSession::CleanupRequests() { | |||
| 344 | // Clean up any pending requests. | 284 | // Clean up any pending requests. |
| 345 | while (true) { | 285 | while (true) { |
| 346 | // Get the next request. | 286 | // Get the next request. |
| 347 | // KSessionRequest *request = nullptr; | 287 | KSessionRequest* request = nullptr; |
| 348 | KThread* client_thread = nullptr; | ||
| 349 | { | 288 | { |
| 350 | KScopedSchedulerLock sl{kernel}; | 289 | KScopedSchedulerLock sl{kernel}; |
| 351 | 290 | ||
| 352 | if (m_current_thread_request) { | 291 | if (m_current_request) { |
| 353 | // Choose the current request if we have one. | 292 | // Choose the current request if we have one. |
| 354 | client_thread = m_current_thread_request; | 293 | request = m_current_request; |
| 355 | m_current_thread_request = nullptr; | 294 | m_current_request = nullptr; |
| 356 | } else if (!m_thread_request_list.empty()) { | 295 | } else if (!m_request_list.empty()) { |
| 357 | // Pop the request from the front of the list. | 296 | // Pop the request from the front of the list. |
| 358 | client_thread = m_thread_request_list.front(); | 297 | request = &m_request_list.front(); |
| 359 | m_thread_request_list.pop_front(); | 298 | m_request_list.pop_front(); |
| 360 | } | 299 | } |
| 361 | } | 300 | } |
| 362 | 301 | ||
| 363 | // If there's no request, we're done. | 302 | // If there's no request, we're done. |
| 364 | if (client_thread == nullptr) { | 303 | if (request == nullptr) { |
| 365 | break; | 304 | break; |
| 366 | } | 305 | } |
| 367 | 306 | ||
| 368 | // Close a reference to the request once it's cleaned up. | 307 | // Close a reference to the request once it's cleaned up. |
| 369 | SCOPE_EXIT({ client_thread->Close(); }); | 308 | SCOPE_EXIT({ request->Close(); }); |
| 370 | 309 | ||
| 371 | // Extract relevant information from the request. | 310 | // Extract relevant information from the request. |
| 372 | // const uintptr_t client_message = request->GetAddress(); | 311 | // const uintptr_t client_message = request->GetAddress(); |
| 373 | // const size_t client_buffer_size = request->GetSize(); | 312 | // const size_t client_buffer_size = request->GetSize(); |
| 374 | // KThread *client_thread = request->GetThread(); | 313 | KThread* client_thread = request->GetThread(); |
| 375 | // KEvent *event = request->GetEvent(); | 314 | KEvent* event = request->GetEvent(); |
| 376 | 315 | ||
| 377 | // KProcess *server_process = request->GetServerProcess(); | 316 | // KProcess *server_process = request->GetServerProcess(); |
| 378 | // KProcess *client_process = (client_thread != nullptr) ? | 317 | // KProcess *client_process = (client_thread != nullptr) ? |
| @@ -385,11 +324,24 @@ void KServerSession::CleanupRequests() { | |||
| 385 | 324 | ||
| 386 | // If there's a client thread, update it. | 325 | // If there's a client thread, update it. |
| 387 | if (client_thread != nullptr) { | 326 | if (client_thread != nullptr) { |
| 388 | // End the client thread's wait. | 327 | if (event != nullptr) { |
| 389 | KScopedSchedulerLock sl{kernel}; | 328 | // // We need to reply async. |
| 390 | 329 | // ReplyAsyncError(client_process, client_message, client_buffer_size, | |
| 391 | if (!client_thread->IsTerminationRequested()) { | 330 | // (R_SUCCEEDED(result) ? ResultSessionClosed : result)); |
| 392 | client_thread->EndWait(ResultSessionClosed); | 331 | |
| 332 | // // Unlock the client buffer. | ||
| 333 | // NOTE: Nintendo does not check the result of this. | ||
| 334 | // client_page_table->UnlockForIpcUserBuffer(client_message, client_buffer_size); | ||
| 335 | |||
| 336 | // Signal the event. | ||
| 337 | event->Signal(); | ||
| 338 | } else { | ||
| 339 | // End the client thread's wait. | ||
| 340 | KScopedSchedulerLock sl{kernel}; | ||
| 341 | |||
| 342 | if (!client_thread->IsTerminationRequested()) { | ||
| 343 | client_thread->EndWait(ResultSessionClosed); | ||
| 344 | } | ||
| 393 | } | 345 | } |
| 394 | } | 346 | } |
| 395 | } | 347 | } |
diff --git a/src/core/hle/kernel/k_server_session.h b/src/core/hle/kernel/k_server_session.h index 748d52826..188aef4af 100644 --- a/src/core/hle/kernel/k_server_session.h +++ b/src/core/hle/kernel/k_server_session.h | |||
| @@ -12,6 +12,7 @@ | |||
| 12 | 12 | ||
| 13 | #include "core/hle/kernel/hle_ipc.h" | 13 | #include "core/hle/kernel/hle_ipc.h" |
| 14 | #include "core/hle/kernel/k_light_lock.h" | 14 | #include "core/hle/kernel/k_light_lock.h" |
| 15 | #include "core/hle/kernel/k_session_request.h" | ||
| 15 | #include "core/hle/kernel/k_synchronization_object.h" | 16 | #include "core/hle/kernel/k_synchronization_object.h" |
| 16 | #include "core/hle/result.h" | 17 | #include "core/hle/result.h" |
| 17 | 18 | ||
| @@ -57,44 +58,15 @@ public: | |||
| 57 | } | 58 | } |
| 58 | 59 | ||
| 59 | bool IsSignaled() const override; | 60 | bool IsSignaled() const override; |
| 60 | |||
| 61 | void OnClientClosed(); | 61 | void OnClientClosed(); |
| 62 | 62 | ||
| 63 | void ClientConnected(SessionRequestHandlerPtr handler) { | ||
| 64 | if (manager) { | ||
| 65 | manager->SetSessionHandler(std::move(handler)); | ||
| 66 | } | ||
| 67 | } | ||
| 68 | |||
| 69 | void ClientDisconnected() { | ||
| 70 | manager = nullptr; | ||
| 71 | } | ||
| 72 | |||
| 73 | /// Adds a new domain request handler to the collection of request handlers within | ||
| 74 | /// this ServerSession instance. | ||
| 75 | void AppendDomainHandler(SessionRequestHandlerPtr handler); | ||
| 76 | |||
| 77 | /// Retrieves the total number of domain request handlers that have been | ||
| 78 | /// appended to this ServerSession instance. | ||
| 79 | std::size_t NumDomainRequestHandlers() const; | ||
| 80 | |||
| 81 | /// Returns true if the session has been converted to a domain, otherwise False | ||
| 82 | bool IsDomain() const { | ||
| 83 | return manager && manager->IsDomain(); | ||
| 84 | } | ||
| 85 | |||
| 86 | /// Converts the session to a domain at the end of the current command | ||
| 87 | void ConvertToDomain() { | ||
| 88 | convert_to_domain = true; | ||
| 89 | } | ||
| 90 | |||
| 91 | /// Gets the session request manager, which forwards requests to the underlying service | 63 | /// Gets the session request manager, which forwards requests to the underlying service |
| 92 | std::shared_ptr<SessionRequestManager>& GetSessionRequestManager() { | 64 | std::shared_ptr<SessionRequestManager>& GetSessionRequestManager() { |
| 93 | return manager; | 65 | return manager; |
| 94 | } | 66 | } |
| 95 | 67 | ||
| 96 | /// TODO: flesh these out to match the real kernel | 68 | /// TODO: flesh these out to match the real kernel |
| 97 | Result OnRequest(); | 69 | Result OnRequest(KSessionRequest* request); |
| 98 | Result SendReply(); | 70 | Result SendReply(); |
| 99 | Result ReceiveRequest(); | 71 | Result ReceiveRequest(); |
| 100 | 72 | ||
| @@ -108,10 +80,6 @@ private: | |||
| 108 | /// Completes a sync request from the emulated application. | 80 | /// Completes a sync request from the emulated application. |
| 109 | Result CompleteSyncRequest(HLERequestContext& context); | 81 | Result CompleteSyncRequest(HLERequestContext& context); |
| 110 | 82 | ||
| 111 | /// Handles a SyncRequest to a domain, forwarding the request to the proper object or closing an | ||
| 112 | /// object handle. | ||
| 113 | Result HandleDomainSyncRequest(Kernel::HLERequestContext& context); | ||
| 114 | |||
| 115 | /// This session's HLE request handlers; if nullptr, this is not an HLE server | 83 | /// This session's HLE request handlers; if nullptr, this is not an HLE server |
| 116 | std::shared_ptr<SessionRequestManager> manager; | 84 | std::shared_ptr<SessionRequestManager> manager; |
| 117 | 85 | ||
| @@ -122,9 +90,8 @@ private: | |||
| 122 | KSession* parent{}; | 90 | KSession* parent{}; |
| 123 | 91 | ||
| 124 | /// List of threads which are pending a reply. | 92 | /// List of threads which are pending a reply. |
| 125 | /// FIXME: KSessionRequest | 93 | boost::intrusive::list<KSessionRequest> m_request_list; |
| 126 | std::list<KThread*> m_thread_request_list; | 94 | KSessionRequest* m_current_request{}; |
| 127 | KThread* m_current_thread_request{}; | ||
| 128 | 95 | ||
| 129 | KLightLock m_lock; | 96 | KLightLock m_lock; |
| 130 | }; | 97 | }; |
diff --git a/src/core/hle/kernel/k_session_request.cpp b/src/core/hle/kernel/k_session_request.cpp new file mode 100644 index 000000000..520da6aa7 --- /dev/null +++ b/src/core/hle/kernel/k_session_request.cpp | |||
| @@ -0,0 +1,61 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #include "core/hle/kernel/k_page_buffer.h" | ||
| 5 | #include "core/hle/kernel/k_session_request.h" | ||
| 6 | |||
| 7 | namespace Kernel { | ||
| 8 | |||
| 9 | Result KSessionRequest::SessionMappings::PushMap(VAddr client, VAddr server, size_t size, | ||
| 10 | KMemoryState state, size_t index) { | ||
| 11 | // At most 15 buffers of each type (4-bit descriptor counts). | ||
| 12 | ASSERT(index < ((1ul << 4) - 1) * 3); | ||
| 13 | |||
| 14 | // Get the mapping. | ||
| 15 | Mapping* mapping; | ||
| 16 | if (index < NumStaticMappings) { | ||
| 17 | mapping = &m_static_mappings[index]; | ||
| 18 | } else { | ||
| 19 | // Allocate a page for the extra mappings. | ||
| 20 | if (m_mappings == nullptr) { | ||
| 21 | KPageBuffer* page_buffer = KPageBuffer::Allocate(kernel); | ||
| 22 | R_UNLESS(page_buffer != nullptr, ResultOutOfMemory); | ||
| 23 | |||
| 24 | m_mappings = reinterpret_cast<Mapping*>(page_buffer); | ||
| 25 | } | ||
| 26 | |||
| 27 | mapping = &m_mappings[index - NumStaticMappings]; | ||
| 28 | } | ||
| 29 | |||
| 30 | // Set the mapping. | ||
| 31 | mapping->Set(client, server, size, state); | ||
| 32 | |||
| 33 | return ResultSuccess; | ||
| 34 | } | ||
| 35 | |||
| 36 | Result KSessionRequest::SessionMappings::PushSend(VAddr client, VAddr server, size_t size, | ||
| 37 | KMemoryState state) { | ||
| 38 | ASSERT(m_num_recv == 0); | ||
| 39 | ASSERT(m_num_exch == 0); | ||
| 40 | return this->PushMap(client, server, size, state, m_num_send++); | ||
| 41 | } | ||
| 42 | |||
| 43 | Result KSessionRequest::SessionMappings::PushReceive(VAddr client, VAddr server, size_t size, | ||
| 44 | KMemoryState state) { | ||
| 45 | ASSERT(m_num_exch == 0); | ||
| 46 | return this->PushMap(client, server, size, state, m_num_send + m_num_recv++); | ||
| 47 | } | ||
| 48 | |||
| 49 | Result KSessionRequest::SessionMappings::PushExchange(VAddr client, VAddr server, size_t size, | ||
| 50 | KMemoryState state) { | ||
| 51 | return this->PushMap(client, server, size, state, m_num_send + m_num_recv + m_num_exch++); | ||
| 52 | } | ||
| 53 | |||
| 54 | void KSessionRequest::SessionMappings::Finalize() { | ||
| 55 | if (m_mappings) { | ||
| 56 | KPageBuffer::Free(kernel, reinterpret_cast<KPageBuffer*>(m_mappings)); | ||
| 57 | m_mappings = nullptr; | ||
| 58 | } | ||
| 59 | } | ||
| 60 | |||
| 61 | } // namespace Kernel | ||
diff --git a/src/core/hle/kernel/k_session_request.h b/src/core/hle/kernel/k_session_request.h new file mode 100644 index 000000000..e5558bc2c --- /dev/null +++ b/src/core/hle/kernel/k_session_request.h | |||
| @@ -0,0 +1,306 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include <array> | ||
| 7 | |||
| 8 | #include "core/hle/kernel/k_auto_object.h" | ||
| 9 | #include "core/hle/kernel/k_event.h" | ||
| 10 | #include "core/hle/kernel/k_memory_block.h" | ||
| 11 | #include "core/hle/kernel/k_process.h" | ||
| 12 | #include "core/hle/kernel/k_thread.h" | ||
| 13 | #include "core/hle/kernel/slab_helpers.h" | ||
| 14 | |||
| 15 | namespace Kernel { | ||
| 16 | |||
| 17 | class KSessionRequest final : public KSlabAllocated<KSessionRequest>, | ||
| 18 | public KAutoObject, | ||
| 19 | public boost::intrusive::list_base_hook<> { | ||
| 20 | KERNEL_AUTOOBJECT_TRAITS(KSessionRequest, KAutoObject); | ||
| 21 | |||
| 22 | public: | ||
| 23 | class SessionMappings { | ||
| 24 | private: | ||
| 25 | static constexpr size_t NumStaticMappings = 8; | ||
| 26 | |||
| 27 | class Mapping { | ||
| 28 | public: | ||
| 29 | constexpr void Set(VAddr c, VAddr s, size_t sz, KMemoryState st) { | ||
| 30 | m_client_address = c; | ||
| 31 | m_server_address = s; | ||
| 32 | m_size = sz; | ||
| 33 | m_state = st; | ||
| 34 | } | ||
| 35 | |||
| 36 | constexpr VAddr GetClientAddress() const { | ||
| 37 | return m_client_address; | ||
| 38 | } | ||
| 39 | constexpr VAddr GetServerAddress() const { | ||
| 40 | return m_server_address; | ||
| 41 | } | ||
| 42 | constexpr size_t GetSize() const { | ||
| 43 | return m_size; | ||
| 44 | } | ||
| 45 | constexpr KMemoryState GetMemoryState() const { | ||
| 46 | return m_state; | ||
| 47 | } | ||
| 48 | |||
| 49 | private: | ||
| 50 | VAddr m_client_address; | ||
| 51 | VAddr m_server_address; | ||
| 52 | size_t m_size; | ||
| 53 | KMemoryState m_state; | ||
| 54 | }; | ||
| 55 | |||
| 56 | public: | ||
| 57 | explicit SessionMappings(KernelCore& kernel_) : kernel(kernel_) {} | ||
| 58 | |||
| 59 | void Initialize() {} | ||
| 60 | void Finalize(); | ||
| 61 | |||
| 62 | size_t GetSendCount() const { | ||
| 63 | return m_num_send; | ||
| 64 | } | ||
| 65 | size_t GetReceiveCount() const { | ||
| 66 | return m_num_recv; | ||
| 67 | } | ||
| 68 | size_t GetExchangeCount() const { | ||
| 69 | return m_num_exch; | ||
| 70 | } | ||
| 71 | |||
| 72 | Result PushSend(VAddr client, VAddr server, size_t size, KMemoryState state); | ||
| 73 | Result PushReceive(VAddr client, VAddr server, size_t size, KMemoryState state); | ||
| 74 | Result PushExchange(VAddr client, VAddr server, size_t size, KMemoryState state); | ||
| 75 | |||
| 76 | VAddr GetSendClientAddress(size_t i) const { | ||
| 77 | return GetSendMapping(i).GetClientAddress(); | ||
| 78 | } | ||
| 79 | VAddr GetSendServerAddress(size_t i) const { | ||
| 80 | return GetSendMapping(i).GetServerAddress(); | ||
| 81 | } | ||
| 82 | size_t GetSendSize(size_t i) const { | ||
| 83 | return GetSendMapping(i).GetSize(); | ||
| 84 | } | ||
| 85 | KMemoryState GetSendMemoryState(size_t i) const { | ||
| 86 | return GetSendMapping(i).GetMemoryState(); | ||
| 87 | } | ||
| 88 | |||
| 89 | VAddr GetReceiveClientAddress(size_t i) const { | ||
| 90 | return GetReceiveMapping(i).GetClientAddress(); | ||
| 91 | } | ||
| 92 | VAddr GetReceiveServerAddress(size_t i) const { | ||
| 93 | return GetReceiveMapping(i).GetServerAddress(); | ||
| 94 | } | ||
| 95 | size_t GetReceiveSize(size_t i) const { | ||
| 96 | return GetReceiveMapping(i).GetSize(); | ||
| 97 | } | ||
| 98 | KMemoryState GetReceiveMemoryState(size_t i) const { | ||
| 99 | return GetReceiveMapping(i).GetMemoryState(); | ||
| 100 | } | ||
| 101 | |||
| 102 | VAddr GetExchangeClientAddress(size_t i) const { | ||
| 103 | return GetExchangeMapping(i).GetClientAddress(); | ||
| 104 | } | ||
| 105 | VAddr GetExchangeServerAddress(size_t i) const { | ||
| 106 | return GetExchangeMapping(i).GetServerAddress(); | ||
| 107 | } | ||
| 108 | size_t GetExchangeSize(size_t i) const { | ||
| 109 | return GetExchangeMapping(i).GetSize(); | ||
| 110 | } | ||
| 111 | KMemoryState GetExchangeMemoryState(size_t i) const { | ||
| 112 | return GetExchangeMapping(i).GetMemoryState(); | ||
| 113 | } | ||
| 114 | |||
| 115 | private: | ||
| 116 | Result PushMap(VAddr client, VAddr server, size_t size, KMemoryState state, size_t index); | ||
| 117 | |||
| 118 | const Mapping& GetSendMapping(size_t i) const { | ||
| 119 | ASSERT(i < m_num_send); | ||
| 120 | |||
| 121 | const size_t index = i; | ||
| 122 | if (index < NumStaticMappings) { | ||
| 123 | return m_static_mappings[index]; | ||
| 124 | } else { | ||
| 125 | return m_mappings[index - NumStaticMappings]; | ||
| 126 | } | ||
| 127 | } | ||
| 128 | |||
| 129 | const Mapping& GetReceiveMapping(size_t i) const { | ||
| 130 | ASSERT(i < m_num_recv); | ||
| 131 | |||
| 132 | const size_t index = m_num_send + i; | ||
| 133 | if (index < NumStaticMappings) { | ||
| 134 | return m_static_mappings[index]; | ||
| 135 | } else { | ||
| 136 | return m_mappings[index - NumStaticMappings]; | ||
| 137 | } | ||
| 138 | } | ||
| 139 | |||
| 140 | const Mapping& GetExchangeMapping(size_t i) const { | ||
| 141 | ASSERT(i < m_num_exch); | ||
| 142 | |||
| 143 | const size_t index = m_num_send + m_num_recv + i; | ||
| 144 | if (index < NumStaticMappings) { | ||
| 145 | return m_static_mappings[index]; | ||
| 146 | } else { | ||
| 147 | return m_mappings[index - NumStaticMappings]; | ||
| 148 | } | ||
| 149 | } | ||
| 150 | |||
| 151 | private: | ||
| 152 | KernelCore& kernel; | ||
| 153 | std::array<Mapping, NumStaticMappings> m_static_mappings; | ||
| 154 | Mapping* m_mappings{}; | ||
| 155 | u8 m_num_send{}; | ||
| 156 | u8 m_num_recv{}; | ||
| 157 | u8 m_num_exch{}; | ||
| 158 | }; | ||
| 159 | |||
| 160 | public: | ||
| 161 | explicit KSessionRequest(KernelCore& kernel_) : KAutoObject(kernel_), m_mappings(kernel_) {} | ||
| 162 | |||
| 163 | static KSessionRequest* Create(KernelCore& kernel) { | ||
| 164 | KSessionRequest* req = KSessionRequest::Allocate(kernel); | ||
| 165 | if (req != nullptr) [[likely]] { | ||
| 166 | KAutoObject::Create(req); | ||
| 167 | } | ||
| 168 | return req; | ||
| 169 | } | ||
| 170 | |||
| 171 | void Destroy() override { | ||
| 172 | this->Finalize(); | ||
| 173 | KSessionRequest::Free(kernel, this); | ||
| 174 | } | ||
| 175 | |||
| 176 | void Initialize(KEvent* event, uintptr_t address, size_t size) { | ||
| 177 | m_mappings.Initialize(); | ||
| 178 | |||
| 179 | m_thread = GetCurrentThreadPointer(kernel); | ||
| 180 | m_event = event; | ||
| 181 | m_address = address; | ||
| 182 | m_size = size; | ||
| 183 | |||
| 184 | m_thread->Open(); | ||
| 185 | if (m_event != nullptr) { | ||
| 186 | m_event->Open(); | ||
| 187 | } | ||
| 188 | } | ||
| 189 | |||
| 190 | static void PostDestroy(uintptr_t arg) {} | ||
| 191 | |||
| 192 | KThread* GetThread() const { | ||
| 193 | return m_thread; | ||
| 194 | } | ||
| 195 | KEvent* GetEvent() const { | ||
| 196 | return m_event; | ||
| 197 | } | ||
| 198 | uintptr_t GetAddress() const { | ||
| 199 | return m_address; | ||
| 200 | } | ||
| 201 | size_t GetSize() const { | ||
| 202 | return m_size; | ||
| 203 | } | ||
| 204 | KProcess* GetServerProcess() const { | ||
| 205 | return m_server; | ||
| 206 | } | ||
| 207 | |||
| 208 | void SetServerProcess(KProcess* process) { | ||
| 209 | m_server = process; | ||
| 210 | m_server->Open(); | ||
| 211 | } | ||
| 212 | |||
| 213 | void ClearThread() { | ||
| 214 | m_thread = nullptr; | ||
| 215 | } | ||
| 216 | void ClearEvent() { | ||
| 217 | m_event = nullptr; | ||
| 218 | } | ||
| 219 | |||
| 220 | size_t GetSendCount() const { | ||
| 221 | return m_mappings.GetSendCount(); | ||
| 222 | } | ||
| 223 | size_t GetReceiveCount() const { | ||
| 224 | return m_mappings.GetReceiveCount(); | ||
| 225 | } | ||
| 226 | size_t GetExchangeCount() const { | ||
| 227 | return m_mappings.GetExchangeCount(); | ||
| 228 | } | ||
| 229 | |||
| 230 | Result PushSend(VAddr client, VAddr server, size_t size, KMemoryState state) { | ||
| 231 | return m_mappings.PushSend(client, server, size, state); | ||
| 232 | } | ||
| 233 | |||
| 234 | Result PushReceive(VAddr client, VAddr server, size_t size, KMemoryState state) { | ||
| 235 | return m_mappings.PushReceive(client, server, size, state); | ||
| 236 | } | ||
| 237 | |||
| 238 | Result PushExchange(VAddr client, VAddr server, size_t size, KMemoryState state) { | ||
| 239 | return m_mappings.PushExchange(client, server, size, state); | ||
| 240 | } | ||
| 241 | |||
| 242 | VAddr GetSendClientAddress(size_t i) const { | ||
| 243 | return m_mappings.GetSendClientAddress(i); | ||
| 244 | } | ||
| 245 | VAddr GetSendServerAddress(size_t i) const { | ||
| 246 | return m_mappings.GetSendServerAddress(i); | ||
| 247 | } | ||
| 248 | size_t GetSendSize(size_t i) const { | ||
| 249 | return m_mappings.GetSendSize(i); | ||
| 250 | } | ||
| 251 | KMemoryState GetSendMemoryState(size_t i) const { | ||
| 252 | return m_mappings.GetSendMemoryState(i); | ||
| 253 | } | ||
| 254 | |||
| 255 | VAddr GetReceiveClientAddress(size_t i) const { | ||
| 256 | return m_mappings.GetReceiveClientAddress(i); | ||
| 257 | } | ||
| 258 | VAddr GetReceiveServerAddress(size_t i) const { | ||
| 259 | return m_mappings.GetReceiveServerAddress(i); | ||
| 260 | } | ||
| 261 | size_t GetReceiveSize(size_t i) const { | ||
| 262 | return m_mappings.GetReceiveSize(i); | ||
| 263 | } | ||
| 264 | KMemoryState GetReceiveMemoryState(size_t i) const { | ||
| 265 | return m_mappings.GetReceiveMemoryState(i); | ||
| 266 | } | ||
| 267 | |||
| 268 | VAddr GetExchangeClientAddress(size_t i) const { | ||
| 269 | return m_mappings.GetExchangeClientAddress(i); | ||
| 270 | } | ||
| 271 | VAddr GetExchangeServerAddress(size_t i) const { | ||
| 272 | return m_mappings.GetExchangeServerAddress(i); | ||
| 273 | } | ||
| 274 | size_t GetExchangeSize(size_t i) const { | ||
| 275 | return m_mappings.GetExchangeSize(i); | ||
| 276 | } | ||
| 277 | KMemoryState GetExchangeMemoryState(size_t i) const { | ||
| 278 | return m_mappings.GetExchangeMemoryState(i); | ||
| 279 | } | ||
| 280 | |||
| 281 | private: | ||
| 282 | // NOTE: This is public and virtual in Nintendo's kernel. | ||
| 283 | void Finalize() override { | ||
| 284 | m_mappings.Finalize(); | ||
| 285 | |||
| 286 | if (m_thread) { | ||
| 287 | m_thread->Close(); | ||
| 288 | } | ||
| 289 | if (m_event) { | ||
| 290 | m_event->Close(); | ||
| 291 | } | ||
| 292 | if (m_server) { | ||
| 293 | m_server->Close(); | ||
| 294 | } | ||
| 295 | } | ||
| 296 | |||
| 297 | private: | ||
| 298 | SessionMappings m_mappings; | ||
| 299 | KThread* m_thread{}; | ||
| 300 | KProcess* m_server{}; | ||
| 301 | KEvent* m_event{}; | ||
| 302 | uintptr_t m_address{}; | ||
| 303 | size_t m_size{}; | ||
| 304 | }; | ||
| 305 | |||
| 306 | } // namespace Kernel | ||
diff --git a/src/core/hle/kernel/k_shared_memory_info.h b/src/core/hle/kernel/k_shared_memory_info.h index e43db8515..2bb6b6d08 100644 --- a/src/core/hle/kernel/k_shared_memory_info.h +++ b/src/core/hle/kernel/k_shared_memory_info.h | |||
| @@ -15,7 +15,8 @@ class KSharedMemoryInfo final : public KSlabAllocated<KSharedMemoryInfo>, | |||
| 15 | public boost::intrusive::list_base_hook<> { | 15 | public boost::intrusive::list_base_hook<> { |
| 16 | 16 | ||
| 17 | public: | 17 | public: |
| 18 | explicit KSharedMemoryInfo() = default; | 18 | explicit KSharedMemoryInfo(KernelCore&) {} |
| 19 | KSharedMemoryInfo() = default; | ||
| 19 | 20 | ||
| 20 | constexpr void Initialize(KSharedMemory* shmem) { | 21 | constexpr void Initialize(KSharedMemory* shmem) { |
| 21 | shared_memory = shmem; | 22 | shared_memory = shmem; |
diff --git a/src/core/hle/kernel/k_thread.cpp b/src/core/hle/kernel/k_thread.cpp index b7bfcdce3..cc88d08f0 100644 --- a/src/core/hle/kernel/k_thread.cpp +++ b/src/core/hle/kernel/k_thread.cpp | |||
| @@ -148,7 +148,9 @@ Result KThread::Initialize(KThreadFunction func, uintptr_t arg, VAddr user_stack | |||
| 148 | physical_affinity_mask.SetAffinity(phys_core, true); | 148 | physical_affinity_mask.SetAffinity(phys_core, true); |
| 149 | 149 | ||
| 150 | // Set the thread state. | 150 | // Set the thread state. |
| 151 | thread_state = (type == ThreadType::Main) ? ThreadState::Runnable : ThreadState::Initialized; | 151 | thread_state = (type == ThreadType::Main || type == ThreadType::Dummy) |
| 152 | ? ThreadState::Runnable | ||
| 153 | : ThreadState::Initialized; | ||
| 152 | 154 | ||
| 153 | // Set TLS address. | 155 | // Set TLS address. |
| 154 | tls_address = 0; | 156 | tls_address = 0; |
| @@ -1174,30 +1176,31 @@ Result KThread::Sleep(s64 timeout) { | |||
| 1174 | R_SUCCEED(); | 1176 | R_SUCCEED(); |
| 1175 | } | 1177 | } |
| 1176 | 1178 | ||
| 1177 | void KThread::IfDummyThreadTryWait() { | 1179 | void KThread::RequestDummyThreadWait() { |
| 1178 | if (!IsDummyThread()) { | 1180 | ASSERT(KScheduler::IsSchedulerLockedByCurrentThread(kernel)); |
| 1179 | return; | 1181 | ASSERT(this->IsDummyThread()); |
| 1180 | } | 1182 | |
| 1183 | // We will block when the scheduler lock is released. | ||
| 1184 | dummy_thread_runnable.store(false); | ||
| 1185 | } | ||
| 1181 | 1186 | ||
| 1182 | if (GetState() != ThreadState::Waiting) { | 1187 | void KThread::DummyThreadBeginWait() { |
| 1188 | if (!this->IsDummyThread() || kernel.IsPhantomModeForSingleCore()) { | ||
| 1189 | // Occurs in single core mode. | ||
| 1183 | return; | 1190 | return; |
| 1184 | } | 1191 | } |
| 1185 | 1192 | ||
| 1186 | ASSERT(!kernel.IsPhantomModeForSingleCore()); | 1193 | // Block until runnable is no longer false. |
| 1187 | 1194 | dummy_thread_runnable.wait(false); | |
| 1188 | // Block until we are no longer waiting. | ||
| 1189 | std::unique_lock lk(dummy_wait_lock); | ||
| 1190 | dummy_wait_cv.wait( | ||
| 1191 | lk, [&] { return GetState() != ThreadState::Waiting || kernel.IsShuttingDown(); }); | ||
| 1192 | } | 1195 | } |
| 1193 | 1196 | ||
| 1194 | void KThread::IfDummyThreadEndWait() { | 1197 | void KThread::DummyThreadEndWait() { |
| 1195 | if (!IsDummyThread()) { | 1198 | ASSERT(KScheduler::IsSchedulerLockedByCurrentThread(kernel)); |
| 1196 | return; | 1199 | ASSERT(this->IsDummyThread()); |
| 1197 | } | ||
| 1198 | 1200 | ||
| 1199 | // Wake up the waiting thread. | 1201 | // Wake up the waiting thread. |
| 1200 | dummy_wait_cv.notify_one(); | 1202 | dummy_thread_runnable.store(true); |
| 1203 | dummy_thread_runnable.notify_one(); | ||
| 1201 | } | 1204 | } |
| 1202 | 1205 | ||
| 1203 | void KThread::BeginWait(KThreadQueue* queue) { | 1206 | void KThread::BeginWait(KThreadQueue* queue) { |
| @@ -1231,9 +1234,6 @@ void KThread::EndWait(Result wait_result_) { | |||
| 1231 | } | 1234 | } |
| 1232 | 1235 | ||
| 1233 | wait_queue->EndWait(this, wait_result_); | 1236 | wait_queue->EndWait(this, wait_result_); |
| 1234 | |||
| 1235 | // Special case for dummy threads to wakeup if necessary. | ||
| 1236 | IfDummyThreadEndWait(); | ||
| 1237 | } | 1237 | } |
| 1238 | } | 1238 | } |
| 1239 | 1239 | ||
diff --git a/src/core/hle/kernel/k_thread.h b/src/core/hle/kernel/k_thread.h index e2a27d603..30aa10c9a 100644 --- a/src/core/hle/kernel/k_thread.h +++ b/src/core/hle/kernel/k_thread.h | |||
| @@ -643,8 +643,9 @@ public: | |||
| 643 | // therefore will not block on guest kernel synchronization primitives. These methods handle | 643 | // therefore will not block on guest kernel synchronization primitives. These methods handle |
| 644 | // blocking as needed. | 644 | // blocking as needed. |
| 645 | 645 | ||
| 646 | void IfDummyThreadTryWait(); | 646 | void RequestDummyThreadWait(); |
| 647 | void IfDummyThreadEndWait(); | 647 | void DummyThreadBeginWait(); |
| 648 | void DummyThreadEndWait(); | ||
| 648 | 649 | ||
| 649 | [[nodiscard]] uintptr_t GetArgument() const { | 650 | [[nodiscard]] uintptr_t GetArgument() const { |
| 650 | return argument; | 651 | return argument; |
| @@ -777,8 +778,7 @@ private: | |||
| 777 | bool is_single_core{}; | 778 | bool is_single_core{}; |
| 778 | ThreadType thread_type{}; | 779 | ThreadType thread_type{}; |
| 779 | StepState step_state{}; | 780 | StepState step_state{}; |
| 780 | std::mutex dummy_wait_lock; | 781 | std::atomic<bool> dummy_thread_runnable{true}; |
| 781 | std::condition_variable dummy_wait_cv; | ||
| 782 | 782 | ||
| 783 | // For debugging | 783 | // For debugging |
| 784 | std::vector<KSynchronizationObject*> wait_objects_for_debugging; | 784 | std::vector<KSynchronizationObject*> wait_objects_for_debugging; |
diff --git a/src/core/hle/kernel/k_thread_local_page.h b/src/core/hle/kernel/k_thread_local_page.h index 0a7f22680..5d466ace7 100644 --- a/src/core/hle/kernel/k_thread_local_page.h +++ b/src/core/hle/kernel/k_thread_local_page.h | |||
| @@ -26,7 +26,7 @@ public: | |||
| 26 | static_assert(RegionsPerPage > 0); | 26 | static_assert(RegionsPerPage > 0); |
| 27 | 27 | ||
| 28 | public: | 28 | public: |
| 29 | constexpr explicit KThreadLocalPage(VAddr addr = {}) : m_virt_addr(addr) { | 29 | constexpr explicit KThreadLocalPage(KernelCore&, VAddr addr = {}) : m_virt_addr(addr) { |
| 30 | m_is_region_free.fill(true); | 30 | m_is_region_free.fill(true); |
| 31 | } | 31 | } |
| 32 | 32 | ||
diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp index eed2dc9f3..fdc774e30 100644 --- a/src/core/hle/kernel/kernel.cpp +++ b/src/core/hle/kernel/kernel.cpp | |||
| @@ -48,8 +48,8 @@ namespace Kernel { | |||
| 48 | 48 | ||
| 49 | struct KernelCore::Impl { | 49 | struct KernelCore::Impl { |
| 50 | explicit Impl(Core::System& system_, KernelCore& kernel_) | 50 | explicit Impl(Core::System& system_, KernelCore& kernel_) |
| 51 | : time_manager{system_}, | 51 | : time_manager{system_}, service_threads_manager{1, "ServiceThreadsManager"}, |
| 52 | service_threads_manager{1, "ServiceThreadsManager"}, system{system_} {} | 52 | service_thread_barrier{2}, system{system_} {} |
| 53 | 53 | ||
| 54 | void SetMulticore(bool is_multi) { | 54 | void SetMulticore(bool is_multi) { |
| 55 | is_multicore = is_multi; | 55 | is_multicore = is_multi; |
| @@ -737,7 +737,12 @@ struct KernelCore::Impl { | |||
| 737 | } | 737 | } |
| 738 | 738 | ||
| 739 | void ClearServiceThreads() { | 739 | void ClearServiceThreads() { |
| 740 | service_threads_manager.QueueWork([this]() { service_threads.clear(); }); | 740 | service_threads_manager.QueueWork([this] { |
| 741 | service_threads.clear(); | ||
| 742 | default_service_thread.reset(); | ||
| 743 | service_thread_barrier.Sync(); | ||
| 744 | }); | ||
| 745 | service_thread_barrier.Sync(); | ||
| 741 | } | 746 | } |
| 742 | 747 | ||
| 743 | std::mutex server_objects_lock; | 748 | std::mutex server_objects_lock; |
| @@ -802,6 +807,7 @@ struct KernelCore::Impl { | |||
| 802 | std::unordered_set<std::shared_ptr<ServiceThread>> service_threads; | 807 | std::unordered_set<std::shared_ptr<ServiceThread>> service_threads; |
| 803 | std::weak_ptr<ServiceThread> default_service_thread; | 808 | std::weak_ptr<ServiceThread> default_service_thread; |
| 804 | Common::ThreadWorker service_threads_manager; | 809 | Common::ThreadWorker service_threads_manager; |
| 810 | Common::Barrier service_thread_barrier; | ||
| 805 | 811 | ||
| 806 | std::array<KThread*, Core::Hardware::NUM_CPU_CORES> shutdown_threads; | 812 | std::array<KThread*, Core::Hardware::NUM_CPU_CORES> shutdown_threads; |
| 807 | std::array<std::unique_ptr<Kernel::KScheduler>, Core::Hardware::NUM_CPU_CORES> schedulers{}; | 813 | std::array<std::unique_ptr<Kernel::KScheduler>, Core::Hardware::NUM_CPU_CORES> schedulers{}; |
diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h index 6eded9539..266be2bc4 100644 --- a/src/core/hle/kernel/kernel.h +++ b/src/core/hle/kernel/kernel.h | |||
| @@ -47,6 +47,7 @@ class KResourceLimit; | |||
| 47 | class KScheduler; | 47 | class KScheduler; |
| 48 | class KServerSession; | 48 | class KServerSession; |
| 49 | class KSession; | 49 | class KSession; |
| 50 | class KSessionRequest; | ||
| 50 | class KSharedMemory; | 51 | class KSharedMemory; |
| 51 | class KSharedMemoryInfo; | 52 | class KSharedMemoryInfo; |
| 52 | class KThread; | 53 | class KThread; |
| @@ -360,6 +361,8 @@ public: | |||
| 360 | return slab_heap_container->page_buffer; | 361 | return slab_heap_container->page_buffer; |
| 361 | } else if constexpr (std::is_same_v<T, KThreadLocalPage>) { | 362 | } else if constexpr (std::is_same_v<T, KThreadLocalPage>) { |
| 362 | return slab_heap_container->thread_local_page; | 363 | return slab_heap_container->thread_local_page; |
| 364 | } else if constexpr (std::is_same_v<T, KSessionRequest>) { | ||
| 365 | return slab_heap_container->session_request; | ||
| 363 | } | 366 | } |
| 364 | } | 367 | } |
| 365 | 368 | ||
| @@ -422,6 +425,7 @@ private: | |||
| 422 | KSlabHeap<KCodeMemory> code_memory; | 425 | KSlabHeap<KCodeMemory> code_memory; |
| 423 | KSlabHeap<KPageBuffer> page_buffer; | 426 | KSlabHeap<KPageBuffer> page_buffer; |
| 424 | KSlabHeap<KThreadLocalPage> thread_local_page; | 427 | KSlabHeap<KThreadLocalPage> thread_local_page; |
| 428 | KSlabHeap<KSessionRequest> session_request; | ||
| 425 | }; | 429 | }; |
| 426 | 430 | ||
| 427 | std::unique_ptr<SlabHeapContainer> slab_heap_container; | 431 | std::unique_ptr<SlabHeapContainer> slab_heap_container; |
diff --git a/src/core/hle/kernel/slab_helpers.h b/src/core/hle/kernel/slab_helpers.h index 299a981a8..06b51e919 100644 --- a/src/core/hle/kernel/slab_helpers.h +++ b/src/core/hle/kernel/slab_helpers.h | |||
| @@ -24,7 +24,7 @@ public: | |||
| 24 | } | 24 | } |
| 25 | 25 | ||
| 26 | static Derived* Allocate(KernelCore& kernel) { | 26 | static Derived* Allocate(KernelCore& kernel) { |
| 27 | return kernel.SlabHeap<Derived>().Allocate(); | 27 | return kernel.SlabHeap<Derived>().Allocate(kernel); |
| 28 | } | 28 | } |
| 29 | 29 | ||
| 30 | static void Free(KernelCore& kernel, Derived* obj) { | 30 | static void Free(KernelCore& kernel, Derived* obj) { |
diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp index b07ae3f02..4aca5b27d 100644 --- a/src/core/hle/kernel/svc.cpp +++ b/src/core/hle/kernel/svc.cpp | |||
| @@ -751,8 +751,8 @@ static void Break(Core::System& system, u32 reason, u64 info1, u64 info2) { | |||
| 751 | } | 751 | } |
| 752 | 752 | ||
| 753 | system.GetReporter().SaveSvcBreakReport( | 753 | system.GetReporter().SaveSvcBreakReport( |
| 754 | static_cast<u32>(break_reason.break_type.Value()), break_reason.signal_debugger, info1, | 754 | static_cast<u32>(break_reason.break_type.Value()), break_reason.signal_debugger.As<bool>(), |
| 755 | info2, has_dumped_buffer ? std::make_optional(debug_buffer) : std::nullopt); | 755 | info1, info2, has_dumped_buffer ? std::make_optional(debug_buffer) : std::nullopt); |
| 756 | 756 | ||
| 757 | if (!break_reason.signal_debugger) { | 757 | if (!break_reason.signal_debugger) { |
| 758 | LOG_CRITICAL( | 758 | LOG_CRITICAL( |
diff --git a/src/core/hle/service/acc/acc.cpp b/src/core/hle/service/acc/acc.cpp index bb838e285..85a3f0802 100644 --- a/src/core/hle/service/acc/acc.cpp +++ b/src/core/hle/service/acc/acc.cpp | |||
| @@ -512,10 +512,11 @@ protected: | |||
| 512 | 512 | ||
| 513 | class IManagerForApplication final : public ServiceFramework<IManagerForApplication> { | 513 | class IManagerForApplication final : public ServiceFramework<IManagerForApplication> { |
| 514 | public: | 514 | public: |
| 515 | explicit IManagerForApplication(Core::System& system_, Common::UUID user_id_) | 515 | explicit IManagerForApplication(Core::System& system_, |
| 516 | const std::shared_ptr<ProfileManager>& profile_manager_) | ||
| 516 | : ServiceFramework{system_, "IManagerForApplication"}, | 517 | : ServiceFramework{system_, "IManagerForApplication"}, |
| 517 | ensure_token_id{std::make_shared<EnsureTokenIdCacheAsyncInterface>(system)}, | 518 | ensure_token_id{std::make_shared<EnsureTokenIdCacheAsyncInterface>(system)}, |
| 518 | user_id{user_id_} { | 519 | profile_manager{profile_manager_} { |
| 519 | // clang-format off | 520 | // clang-format off |
| 520 | static const FunctionInfo functions[] = { | 521 | static const FunctionInfo functions[] = { |
| 521 | {0, &IManagerForApplication::CheckAvailability, "CheckAvailability"}, | 522 | {0, &IManagerForApplication::CheckAvailability, "CheckAvailability"}, |
| @@ -545,7 +546,7 @@ private: | |||
| 545 | 546 | ||
| 546 | IPC::ResponseBuilder rb{ctx, 4}; | 547 | IPC::ResponseBuilder rb{ctx, 4}; |
| 547 | rb.Push(ResultSuccess); | 548 | rb.Push(ResultSuccess); |
| 548 | rb.PushRaw<u64>(user_id.Hash()); | 549 | rb.PushRaw<u64>(profile_manager->GetLastOpenedUser().Hash()); |
| 549 | } | 550 | } |
| 550 | 551 | ||
| 551 | void EnsureIdTokenCacheAsync(Kernel::HLERequestContext& ctx) { | 552 | void EnsureIdTokenCacheAsync(Kernel::HLERequestContext& ctx) { |
| @@ -575,17 +576,20 @@ private: | |||
| 575 | 576 | ||
| 576 | IPC::ResponseBuilder rb{ctx, 4}; | 577 | IPC::ResponseBuilder rb{ctx, 4}; |
| 577 | rb.Push(ResultSuccess); | 578 | rb.Push(ResultSuccess); |
| 578 | rb.PushRaw<u64>(user_id.Hash()); | 579 | rb.PushRaw<u64>(profile_manager->GetLastOpenedUser().Hash()); |
| 579 | } | 580 | } |
| 580 | 581 | ||
| 581 | void StoreOpenContext(Kernel::HLERequestContext& ctx) { | 582 | void StoreOpenContext(Kernel::HLERequestContext& ctx) { |
| 582 | LOG_WARNING(Service_ACC, "(STUBBED) called"); | 583 | LOG_DEBUG(Service_ACC, "called"); |
| 584 | |||
| 585 | profile_manager->StoreOpenedUsers(); | ||
| 586 | |||
| 583 | IPC::ResponseBuilder rb{ctx, 2}; | 587 | IPC::ResponseBuilder rb{ctx, 2}; |
| 584 | rb.Push(ResultSuccess); | 588 | rb.Push(ResultSuccess); |
| 585 | } | 589 | } |
| 586 | 590 | ||
| 587 | std::shared_ptr<EnsureTokenIdCacheAsyncInterface> ensure_token_id{}; | 591 | std::shared_ptr<EnsureTokenIdCacheAsyncInterface> ensure_token_id{}; |
| 588 | Common::UUID user_id{}; | 592 | std::shared_ptr<ProfileManager> profile_manager; |
| 589 | }; | 593 | }; |
| 590 | 594 | ||
| 591 | // 6.0.0+ | 595 | // 6.0.0+ |
| @@ -790,7 +794,7 @@ void Module::Interface::GetBaasAccountManagerForApplication(Kernel::HLERequestCo | |||
| 790 | LOG_DEBUG(Service_ACC, "called"); | 794 | LOG_DEBUG(Service_ACC, "called"); |
| 791 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; | 795 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; |
| 792 | rb.Push(ResultSuccess); | 796 | rb.Push(ResultSuccess); |
| 793 | rb.PushIpcInterface<IManagerForApplication>(system, profile_manager->GetLastOpenedUser()); | 797 | rb.PushIpcInterface<IManagerForApplication>(system, profile_manager); |
| 794 | } | 798 | } |
| 795 | 799 | ||
| 796 | void Module::Interface::IsUserAccountSwitchLocked(Kernel::HLERequestContext& ctx) { | 800 | void Module::Interface::IsUserAccountSwitchLocked(Kernel::HLERequestContext& ctx) { |
| @@ -849,22 +853,10 @@ void Module::Interface::ListQualifiedUsers(Kernel::HLERequestContext& ctx) { | |||
| 849 | rb.Push(ResultSuccess); | 853 | rb.Push(ResultSuccess); |
| 850 | } | 854 | } |
| 851 | 855 | ||
| 852 | void Module::Interface::LoadOpenContext(Kernel::HLERequestContext& ctx) { | ||
| 853 | LOG_WARNING(Service_ACC, "(STUBBED) called"); | ||
| 854 | |||
| 855 | // This is similar to GetBaasAccountManagerForApplication | ||
| 856 | // This command is used concurrently with ListOpenContextStoredUsers | ||
| 857 | // TODO: Find the differences between this and GetBaasAccountManagerForApplication | ||
| 858 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; | ||
| 859 | rb.Push(ResultSuccess); | ||
| 860 | rb.PushIpcInterface<IManagerForApplication>(system, profile_manager->GetLastOpenedUser()); | ||
| 861 | } | ||
| 862 | |||
| 863 | void Module::Interface::ListOpenContextStoredUsers(Kernel::HLERequestContext& ctx) { | 856 | void Module::Interface::ListOpenContextStoredUsers(Kernel::HLERequestContext& ctx) { |
| 864 | LOG_WARNING(Service_ACC, "(STUBBED) called"); | 857 | LOG_DEBUG(Service_ACC, "called"); |
| 865 | 858 | ||
| 866 | // TODO(ogniK): Handle open contexts | 859 | ctx.WriteBuffer(profile_manager->GetStoredOpenedUsers()); |
| 867 | ctx.WriteBuffer(profile_manager->GetOpenUsers()); | ||
| 868 | IPC::ResponseBuilder rb{ctx, 2}; | 860 | IPC::ResponseBuilder rb{ctx, 2}; |
| 869 | rb.Push(ResultSuccess); | 861 | rb.Push(ResultSuccess); |
| 870 | } | 862 | } |
diff --git a/src/core/hle/service/acc/acc.h b/src/core/hle/service/acc/acc.h index 1621e7c0a..9411b0b92 100644 --- a/src/core/hle/service/acc/acc.h +++ b/src/core/hle/service/acc/acc.h | |||
| @@ -35,7 +35,6 @@ public: | |||
| 35 | void InitializeApplicationInfoV2(Kernel::HLERequestContext& ctx); | 35 | void InitializeApplicationInfoV2(Kernel::HLERequestContext& ctx); |
| 36 | void GetProfileEditor(Kernel::HLERequestContext& ctx); | 36 | void GetProfileEditor(Kernel::HLERequestContext& ctx); |
| 37 | void ListQualifiedUsers(Kernel::HLERequestContext& ctx); | 37 | void ListQualifiedUsers(Kernel::HLERequestContext& ctx); |
| 38 | void LoadOpenContext(Kernel::HLERequestContext& ctx); | ||
| 39 | void ListOpenContextStoredUsers(Kernel::HLERequestContext& ctx); | 38 | void ListOpenContextStoredUsers(Kernel::HLERequestContext& ctx); |
| 40 | void StoreSaveDataThumbnailApplication(Kernel::HLERequestContext& ctx); | 39 | void StoreSaveDataThumbnailApplication(Kernel::HLERequestContext& ctx); |
| 41 | void StoreSaveDataThumbnailSystem(Kernel::HLERequestContext& ctx); | 40 | void StoreSaveDataThumbnailSystem(Kernel::HLERequestContext& ctx); |
diff --git a/src/core/hle/service/acc/acc_u0.cpp b/src/core/hle/service/acc/acc_u0.cpp index 65023b8c2..54844bfe7 100644 --- a/src/core/hle/service/acc/acc_u0.cpp +++ b/src/core/hle/service/acc/acc_u0.cpp | |||
| @@ -28,7 +28,7 @@ ACC_U0::ACC_U0(std::shared_ptr<Module> module_, std::shared_ptr<ProfileManager> | |||
| 28 | {110, &ACC_U0::StoreSaveDataThumbnailApplication, "StoreSaveDataThumbnail"}, | 28 | {110, &ACC_U0::StoreSaveDataThumbnailApplication, "StoreSaveDataThumbnail"}, |
| 29 | {111, nullptr, "ClearSaveDataThumbnail"}, | 29 | {111, nullptr, "ClearSaveDataThumbnail"}, |
| 30 | {120, nullptr, "CreateGuestLoginRequest"}, | 30 | {120, nullptr, "CreateGuestLoginRequest"}, |
| 31 | {130, &ACC_U0::LoadOpenContext, "LoadOpenContext"}, // 5.0.0+ | 31 | {130, nullptr, "LoadOpenContext"}, // 5.0.0+ |
| 32 | {131, &ACC_U0::ListOpenContextStoredUsers, "ListOpenContextStoredUsers"}, // 6.0.0+ | 32 | {131, &ACC_U0::ListOpenContextStoredUsers, "ListOpenContextStoredUsers"}, // 6.0.0+ |
| 33 | {140, &ACC_U0::InitializeApplicationInfoRestricted, "InitializeApplicationInfoRestricted"}, // 6.0.0+ | 33 | {140, &ACC_U0::InitializeApplicationInfoRestricted, "InitializeApplicationInfoRestricted"}, // 6.0.0+ |
| 34 | {141, &ACC_U0::ListQualifiedUsers, "ListQualifiedUsers"}, // 6.0.0+ | 34 | {141, &ACC_U0::ListQualifiedUsers, "ListQualifiedUsers"}, // 6.0.0+ |
diff --git a/src/core/hle/service/acc/profile_manager.cpp b/src/core/hle/service/acc/profile_manager.cpp index a58da4d5f..481e0d141 100644 --- a/src/core/hle/service/acc/profile_manager.cpp +++ b/src/core/hle/service/acc/profile_manager.cpp | |||
| @@ -261,6 +261,31 @@ UUID ProfileManager::GetLastOpenedUser() const { | |||
| 261 | return last_opened_user; | 261 | return last_opened_user; |
| 262 | } | 262 | } |
| 263 | 263 | ||
| 264 | /// Gets the list of stored opened users. | ||
| 265 | UserIDArray ProfileManager::GetStoredOpenedUsers() const { | ||
| 266 | UserIDArray output{}; | ||
| 267 | std::ranges::transform(stored_opened_profiles, output.begin(), [](const ProfileInfo& p) { | ||
| 268 | if (p.is_open) | ||
| 269 | return p.user_uuid; | ||
| 270 | return Common::InvalidUUID; | ||
| 271 | }); | ||
| 272 | std::stable_partition(output.begin(), output.end(), | ||
| 273 | [](const UUID& uuid) { return uuid.IsValid(); }); | ||
| 274 | return output; | ||
| 275 | } | ||
| 276 | |||
| 277 | /// Captures the opened users, which can be queried across process launches with | ||
| 278 | /// ListOpenContextStoredUsers. | ||
| 279 | void ProfileManager::StoreOpenedUsers() { | ||
| 280 | size_t profile_index{}; | ||
| 281 | stored_opened_profiles = {}; | ||
| 282 | std::for_each(profiles.begin(), profiles.end(), [&](const auto& profile) { | ||
| 283 | if (profile.is_open) { | ||
| 284 | stored_opened_profiles[profile_index++] = profile; | ||
| 285 | } | ||
| 286 | }); | ||
| 287 | } | ||
| 288 | |||
| 264 | /// Return the users profile base and the unknown arbitary data. | 289 | /// Return the users profile base and the unknown arbitary data. |
| 265 | bool ProfileManager::GetProfileBaseAndData(std::optional<std::size_t> index, ProfileBase& profile, | 290 | bool ProfileManager::GetProfileBaseAndData(std::optional<std::size_t> index, ProfileBase& profile, |
| 266 | UserData& data) const { | 291 | UserData& data) const { |
diff --git a/src/core/hle/service/acc/profile_manager.h b/src/core/hle/service/acc/profile_manager.h index 135f7d0d5..993a5a57a 100644 --- a/src/core/hle/service/acc/profile_manager.h +++ b/src/core/hle/service/acc/profile_manager.h | |||
| @@ -86,6 +86,8 @@ public: | |||
| 86 | UserIDArray GetOpenUsers() const; | 86 | UserIDArray GetOpenUsers() const; |
| 87 | UserIDArray GetAllUsers() const; | 87 | UserIDArray GetAllUsers() const; |
| 88 | Common::UUID GetLastOpenedUser() const; | 88 | Common::UUID GetLastOpenedUser() const; |
| 89 | UserIDArray GetStoredOpenedUsers() const; | ||
| 90 | void StoreOpenedUsers(); | ||
| 89 | 91 | ||
| 90 | bool CanSystemRegisterUser() const; | 92 | bool CanSystemRegisterUser() const; |
| 91 | 93 | ||
| @@ -101,6 +103,7 @@ private: | |||
| 101 | bool RemoveProfileAtIndex(std::size_t index); | 103 | bool RemoveProfileAtIndex(std::size_t index); |
| 102 | 104 | ||
| 103 | std::array<ProfileInfo, MAX_USERS> profiles{}; | 105 | std::array<ProfileInfo, MAX_USERS> profiles{}; |
| 106 | std::array<ProfileInfo, MAX_USERS> stored_opened_profiles{}; | ||
| 104 | std::size_t user_count{}; | 107 | std::size_t user_count{}; |
| 105 | Common::UUID last_opened_user{}; | 108 | Common::UUID last_opened_user{}; |
| 106 | }; | 109 | }; |
diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp index e55233054..8ea7fd760 100644 --- a/src/core/hle/service/am/am.cpp +++ b/src/core/hle/service/am/am.cpp | |||
| @@ -299,7 +299,7 @@ ISelfController::ISelfController(Core::System& system_, NVFlinger::NVFlinger& nv | |||
| 299 | {100, &ISelfController::SetAlbumImageTakenNotificationEnabled, "SetAlbumImageTakenNotificationEnabled"}, | 299 | {100, &ISelfController::SetAlbumImageTakenNotificationEnabled, "SetAlbumImageTakenNotificationEnabled"}, |
| 300 | {110, nullptr, "SetApplicationAlbumUserData"}, | 300 | {110, nullptr, "SetApplicationAlbumUserData"}, |
| 301 | {120, &ISelfController::SaveCurrentScreenshot, "SaveCurrentScreenshot"}, | 301 | {120, &ISelfController::SaveCurrentScreenshot, "SaveCurrentScreenshot"}, |
| 302 | {130, nullptr, "SetRecordVolumeMuted"}, | 302 | {130, &ISelfController::SetRecordVolumeMuted, "SetRecordVolumeMuted"}, |
| 303 | {1000, nullptr, "GetDebugStorageChannel"}, | 303 | {1000, nullptr, "GetDebugStorageChannel"}, |
| 304 | }; | 304 | }; |
| 305 | // clang-format on | 305 | // clang-format on |
| @@ -597,6 +597,17 @@ void ISelfController::SaveCurrentScreenshot(Kernel::HLERequestContext& ctx) { | |||
| 597 | rb.Push(ResultSuccess); | 597 | rb.Push(ResultSuccess); |
| 598 | } | 598 | } |
| 599 | 599 | ||
| 600 | void ISelfController::SetRecordVolumeMuted(Kernel::HLERequestContext& ctx) { | ||
| 601 | IPC::RequestParser rp{ctx}; | ||
| 602 | |||
| 603 | const auto is_record_volume_muted = rp.Pop<bool>(); | ||
| 604 | |||
| 605 | LOG_WARNING(Service_AM, "(STUBBED) called. is_record_volume_muted={}", is_record_volume_muted); | ||
| 606 | |||
| 607 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 608 | rb.Push(ResultSuccess); | ||
| 609 | } | ||
| 610 | |||
| 600 | AppletMessageQueue::AppletMessageQueue(Core::System& system) | 611 | AppletMessageQueue::AppletMessageQueue(Core::System& system) |
| 601 | : service_context{system, "AppletMessageQueue"} { | 612 | : service_context{system, "AppletMessageQueue"} { |
| 602 | on_new_message = service_context.CreateEvent("AMMessageQueue:OnMessageReceived"); | 613 | on_new_message = service_context.CreateEvent("AMMessageQueue:OnMessageReceived"); |
diff --git a/src/core/hle/service/am/am.h b/src/core/hle/service/am/am.h index bb75c6281..a0fbfcfc5 100644 --- a/src/core/hle/service/am/am.h +++ b/src/core/hle/service/am/am.h | |||
| @@ -182,6 +182,7 @@ private: | |||
| 182 | void GetAccumulatedSuspendedTickChangedEvent(Kernel::HLERequestContext& ctx); | 182 | void GetAccumulatedSuspendedTickChangedEvent(Kernel::HLERequestContext& ctx); |
| 183 | void SetAlbumImageTakenNotificationEnabled(Kernel::HLERequestContext& ctx); | 183 | void SetAlbumImageTakenNotificationEnabled(Kernel::HLERequestContext& ctx); |
| 184 | void SaveCurrentScreenshot(Kernel::HLERequestContext& ctx); | 184 | void SaveCurrentScreenshot(Kernel::HLERequestContext& ctx); |
| 185 | void SetRecordVolumeMuted(Kernel::HLERequestContext& ctx); | ||
| 185 | 186 | ||
| 186 | enum class ScreenshotPermission : u32 { | 187 | enum class ScreenshotPermission : u32 { |
| 187 | Inherit = 0, | 188 | Inherit = 0, |
diff --git a/src/core/hle/service/am/applets/applets.h b/src/core/hle/service/am/applets/applets.h index e78a57657..12c6a5b1a 100644 --- a/src/core/hle/service/am/applets/applets.h +++ b/src/core/hle/service/am/applets/applets.h | |||
| @@ -164,7 +164,7 @@ protected: | |||
| 164 | u32_le size; | 164 | u32_le size; |
| 165 | u32_le library_version; | 165 | u32_le library_version; |
| 166 | u32_le theme_color; | 166 | u32_le theme_color; |
| 167 | u8 play_startup_sound; | 167 | bool play_startup_sound; |
| 168 | u64_le system_tick; | 168 | u64_le system_tick; |
| 169 | }; | 169 | }; |
| 170 | static_assert(sizeof(CommonArguments) == 0x20, "CommonArguments has incorrect size."); | 170 | static_assert(sizeof(CommonArguments) == 0x20, "CommonArguments has incorrect size."); |
diff --git a/src/core/hle/service/audio/audctl.cpp b/src/core/hle/service/audio/audctl.cpp index 4a2ae5f88..5abf22ba4 100644 --- a/src/core/hle/service/audio/audctl.cpp +++ b/src/core/hle/service/audio/audctl.cpp | |||
| @@ -45,9 +45,25 @@ AudCtl::AudCtl(Core::System& system_) : ServiceFramework{system_, "audctl"} { | |||
| 45 | {32, nullptr, "GetActiveOutputTarget"}, | 45 | {32, nullptr, "GetActiveOutputTarget"}, |
| 46 | {33, nullptr, "GetTargetDeviceInfo"}, | 46 | {33, nullptr, "GetTargetDeviceInfo"}, |
| 47 | {34, nullptr, "AcquireTargetNotification"}, | 47 | {34, nullptr, "AcquireTargetNotification"}, |
| 48 | {35, nullptr, "SetHearingProtectionSafeguardTimerRemainingTimeForDebug"}, | ||
| 49 | {36, nullptr, "GetHearingProtectionSafeguardTimerRemainingTimeForDebug"}, | ||
| 50 | {37, nullptr, "SetHearingProtectionSafeguardEnabled"}, | ||
| 51 | {38, nullptr, "IsHearingProtectionSafeguardEnabled"}, | ||
| 52 | {39, nullptr, "IsHearingProtectionSafeguardMonitoringOutputForDebug"}, | ||
| 53 | {40, nullptr, "GetSystemInformationForDebug"}, | ||
| 54 | {41, nullptr, "SetVolumeButtonLongPressTime"}, | ||
| 55 | {42, nullptr, "SetNativeVolumeForDebug"}, | ||
| 48 | {10000, nullptr, "NotifyAudioOutputTargetForPlayReport"}, | 56 | {10000, nullptr, "NotifyAudioOutputTargetForPlayReport"}, |
| 49 | {10001, nullptr, "NotifyAudioOutputChannelCountForPlayReport"}, | 57 | {10001, nullptr, "NotifyAudioOutputChannelCountForPlayReport"}, |
| 50 | {10002, nullptr, "NotifyUnsupportedUsbOutputDeviceAttachedForPlayReport"}, | 58 | {10002, nullptr, "NotifyUnsupportedUsbOutputDeviceAttachedForPlayReport"}, |
| 59 | {10100, nullptr, "GetAudioVolumeDataForPlayReport"}, | ||
| 60 | {10101, nullptr, "BindAudioVolumeUpdateEventForPlayReport"}, | ||
| 61 | {10102, nullptr, "BindAudioOutputTargetUpdateEventForPlayReport"}, | ||
| 62 | {10103, nullptr, "GetAudioOutputTargetForPlayReport"}, | ||
| 63 | {10104, nullptr, "GetAudioOutputChannelCountForPlayReport"}, | ||
| 64 | {10105, nullptr, "BindAudioOutputChannelCountUpdateEventForPlayReport"}, | ||
| 65 | {10106, nullptr, "GetDefaultAudioOutputTargetForPlayReport"}, | ||
| 66 | {50000, nullptr, "SetAnalogInputBoostGainForPrototyping"}, | ||
| 51 | }; | 67 | }; |
| 52 | // clang-format on | 68 | // clang-format on |
| 53 | 69 | ||
diff --git a/src/core/hle/service/audio/audin_u.cpp b/src/core/hle/service/audio/audin_u.cpp index 48a9a73a0..608925dfc 100644 --- a/src/core/hle/service/audio/audin_u.cpp +++ b/src/core/hle/service/audio/audin_u.cpp | |||
| @@ -17,7 +17,7 @@ using namespace AudioCore::AudioIn; | |||
| 17 | class IAudioIn final : public ServiceFramework<IAudioIn> { | 17 | class IAudioIn final : public ServiceFramework<IAudioIn> { |
| 18 | public: | 18 | public: |
| 19 | explicit IAudioIn(Core::System& system_, Manager& manager, size_t session_id, | 19 | explicit IAudioIn(Core::System& system_, Manager& manager, size_t session_id, |
| 20 | std::string& device_name, const AudioInParameter& in_params, u32 handle, | 20 | const std::string& device_name, const AudioInParameter& in_params, u32 handle, |
| 21 | u64 applet_resource_user_id) | 21 | u64 applet_resource_user_id) |
| 22 | : ServiceFramework{system_, "IAudioIn"}, | 22 | : ServiceFramework{system_, "IAudioIn"}, |
| 23 | service_context{system_, "IAudioIn"}, event{service_context.CreateEvent("AudioInEvent")}, | 23 | service_context{system_, "IAudioIn"}, event{service_context.CreateEvent("AudioInEvent")}, |
diff --git a/src/core/hle/service/audio/audout_u.cpp b/src/core/hle/service/audio/audout_u.cpp index 49c092301..122290c6a 100644 --- a/src/core/hle/service/audio/audout_u.cpp +++ b/src/core/hle/service/audio/audout_u.cpp | |||
| @@ -24,7 +24,7 @@ using namespace AudioCore::AudioOut; | |||
| 24 | class IAudioOut final : public ServiceFramework<IAudioOut> { | 24 | class IAudioOut final : public ServiceFramework<IAudioOut> { |
| 25 | public: | 25 | public: |
| 26 | explicit IAudioOut(Core::System& system_, AudioCore::AudioOut::Manager& manager, | 26 | explicit IAudioOut(Core::System& system_, AudioCore::AudioOut::Manager& manager, |
| 27 | size_t session_id, std::string& device_name, | 27 | size_t session_id, const std::string& device_name, |
| 28 | const AudioOutParameter& in_params, u32 handle, u64 applet_resource_user_id) | 28 | const AudioOutParameter& in_params, u32 handle, u64 applet_resource_user_id) |
| 29 | : ServiceFramework{system_, "IAudioOut", ServiceThreadType::CreateNew}, | 29 | : ServiceFramework{system_, "IAudioOut", ServiceThreadType::CreateNew}, |
| 30 | service_context{system_, "IAudioOut"}, event{service_context.CreateEvent( | 30 | service_context{system_, "IAudioOut"}, event{service_context.CreateEvent( |
diff --git a/src/core/hle/service/audio/audren_u.cpp b/src/core/hle/service/audio/audren_u.cpp index 60c30cd5b..13423dca6 100644 --- a/src/core/hle/service/audio/audren_u.cpp +++ b/src/core/hle/service/audio/audren_u.cpp | |||
| @@ -52,6 +52,8 @@ public: | |||
| 52 | {9, &IAudioRenderer::GetRenderingTimeLimit, "GetRenderingTimeLimit"}, | 52 | {9, &IAudioRenderer::GetRenderingTimeLimit, "GetRenderingTimeLimit"}, |
| 53 | {10, &IAudioRenderer::RequestUpdate, "RequestUpdateAuto"}, | 53 | {10, &IAudioRenderer::RequestUpdate, "RequestUpdateAuto"}, |
| 54 | {11, nullptr, "ExecuteAudioRendererRendering"}, | 54 | {11, nullptr, "ExecuteAudioRendererRendering"}, |
| 55 | {12, &IAudioRenderer::SetVoiceDropParameter, "SetVoiceDropParameter"}, | ||
| 56 | {13, &IAudioRenderer::GetVoiceDropParameter, "GetVoiceDropParameter"}, | ||
| 55 | }; | 57 | }; |
| 56 | // clang-format on | 58 | // clang-format on |
| 57 | RegisterHandlers(functions); | 59 | RegisterHandlers(functions); |
| @@ -205,6 +207,30 @@ private: | |||
| 205 | LOG_DEBUG(Service_Audio, "called"); | 207 | LOG_DEBUG(Service_Audio, "called"); |
| 206 | } | 208 | } |
| 207 | 209 | ||
| 210 | void SetVoiceDropParameter(Kernel::HLERequestContext& ctx) { | ||
| 211 | LOG_DEBUG(Service_Audio, "called"); | ||
| 212 | |||
| 213 | IPC::RequestParser rp{ctx}; | ||
| 214 | auto voice_drop_param{rp.Pop<f32>()}; | ||
| 215 | |||
| 216 | auto& system_ = impl->GetSystem(); | ||
| 217 | system_.SetVoiceDropParameter(voice_drop_param); | ||
| 218 | |||
| 219 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 220 | rb.Push(ResultSuccess); | ||
| 221 | } | ||
| 222 | |||
| 223 | void GetVoiceDropParameter(Kernel::HLERequestContext& ctx) { | ||
| 224 | LOG_DEBUG(Service_Audio, "called"); | ||
| 225 | |||
| 226 | auto& system_ = impl->GetSystem(); | ||
| 227 | auto voice_drop_param{system_.GetVoiceDropParameter()}; | ||
| 228 | |||
| 229 | IPC::ResponseBuilder rb{ctx, 3}; | ||
| 230 | rb.Push(ResultSuccess); | ||
| 231 | rb.Push(voice_drop_param); | ||
| 232 | } | ||
| 233 | |||
| 208 | KernelHelpers::ServiceContext service_context; | 234 | KernelHelpers::ServiceContext service_context; |
| 209 | Kernel::KEvent* rendered_event; | 235 | Kernel::KEvent* rendered_event; |
| 210 | Manager& manager; | 236 | Manager& manager; |
diff --git a/src/core/hle/service/hid/controllers/npad.cpp b/src/core/hle/service/hid/controllers/npad.cpp index 98e4f2af7..2f871de31 100644 --- a/src/core/hle/service/hid/controllers/npad.cpp +++ b/src/core/hle/service/hid/controllers/npad.cpp | |||
| @@ -745,8 +745,9 @@ void Controller_NPad::SetSupportedNpadIdTypes(u8* data, std::size_t length) { | |||
| 745 | } | 745 | } |
| 746 | 746 | ||
| 747 | void Controller_NPad::GetSupportedNpadIdTypes(u32* data, std::size_t max_length) { | 747 | void Controller_NPad::GetSupportedNpadIdTypes(u32* data, std::size_t max_length) { |
| 748 | ASSERT(max_length < supported_npad_id_types.size()); | 748 | const auto copy_amount = supported_npad_id_types.size() * sizeof(u32); |
| 749 | std::memcpy(data, supported_npad_id_types.data(), supported_npad_id_types.size()); | 749 | ASSERT(max_length <= copy_amount); |
| 750 | std::memcpy(data, supported_npad_id_types.data(), copy_amount); | ||
| 750 | } | 751 | } |
| 751 | 752 | ||
| 752 | std::size_t Controller_NPad::GetSupportedNpadIdTypesSize() const { | 753 | std::size_t Controller_NPad::GetSupportedNpadIdTypesSize() const { |
| @@ -867,7 +868,7 @@ bool Controller_NPad::VibrateControllerAtIndex(Core::HID::NpadIdType npad_id, | |||
| 867 | return false; | 868 | return false; |
| 868 | } | 869 | } |
| 869 | 870 | ||
| 870 | if (!controller.device->IsVibrationEnabled()) { | 871 | if (!controller.device->IsVibrationEnabled(device_index)) { |
| 871 | if (controller.vibration[device_index].latest_vibration_value.low_amplitude != 0.0f || | 872 | if (controller.vibration[device_index].latest_vibration_value.low_amplitude != 0.0f || |
| 872 | controller.vibration[device_index].latest_vibration_value.high_amplitude != 0.0f) { | 873 | controller.vibration[device_index].latest_vibration_value.high_amplitude != 0.0f) { |
| 873 | // Send an empty vibration to stop any vibrations. | 874 | // Send an empty vibration to stop any vibrations. |
| @@ -1000,7 +1001,7 @@ void Controller_NPad::InitializeVibrationDeviceAtIndex(Core::HID::NpadIdType npa | |||
| 1000 | } | 1001 | } |
| 1001 | 1002 | ||
| 1002 | controller.vibration[device_index].device_mounted = | 1003 | controller.vibration[device_index].device_mounted = |
| 1003 | controller.device->TestVibration(device_index); | 1004 | controller.device->IsVibrationEnabled(device_index); |
| 1004 | } | 1005 | } |
| 1005 | 1006 | ||
| 1006 | void Controller_NPad::SetPermitVibrationSession(bool permit_vibration_session) { | 1007 | void Controller_NPad::SetPermitVibrationSession(bool permit_vibration_session) { |
| @@ -1501,25 +1502,25 @@ bool Controller_NPad::IsControllerSupported(Core::HID::NpadStyleIndex controller | |||
| 1501 | Core::HID::NpadStyleTag style = GetSupportedStyleSet(); | 1502 | Core::HID::NpadStyleTag style = GetSupportedStyleSet(); |
| 1502 | switch (controller) { | 1503 | switch (controller) { |
| 1503 | case Core::HID::NpadStyleIndex::ProController: | 1504 | case Core::HID::NpadStyleIndex::ProController: |
| 1504 | return style.fullkey; | 1505 | return style.fullkey.As<bool>(); |
| 1505 | case Core::HID::NpadStyleIndex::JoyconDual: | 1506 | case Core::HID::NpadStyleIndex::JoyconDual: |
| 1506 | return style.joycon_dual; | 1507 | return style.joycon_dual.As<bool>(); |
| 1507 | case Core::HID::NpadStyleIndex::JoyconLeft: | 1508 | case Core::HID::NpadStyleIndex::JoyconLeft: |
| 1508 | return style.joycon_left; | 1509 | return style.joycon_left.As<bool>(); |
| 1509 | case Core::HID::NpadStyleIndex::JoyconRight: | 1510 | case Core::HID::NpadStyleIndex::JoyconRight: |
| 1510 | return style.joycon_right; | 1511 | return style.joycon_right.As<bool>(); |
| 1511 | case Core::HID::NpadStyleIndex::GameCube: | 1512 | case Core::HID::NpadStyleIndex::GameCube: |
| 1512 | return style.gamecube; | 1513 | return style.gamecube.As<bool>(); |
| 1513 | case Core::HID::NpadStyleIndex::Pokeball: | 1514 | case Core::HID::NpadStyleIndex::Pokeball: |
| 1514 | return style.palma; | 1515 | return style.palma.As<bool>(); |
| 1515 | case Core::HID::NpadStyleIndex::NES: | 1516 | case Core::HID::NpadStyleIndex::NES: |
| 1516 | return style.lark; | 1517 | return style.lark.As<bool>(); |
| 1517 | case Core::HID::NpadStyleIndex::SNES: | 1518 | case Core::HID::NpadStyleIndex::SNES: |
| 1518 | return style.lucia; | 1519 | return style.lucia.As<bool>(); |
| 1519 | case Core::HID::NpadStyleIndex::N64: | 1520 | case Core::HID::NpadStyleIndex::N64: |
| 1520 | return style.lagoon; | 1521 | return style.lagoon.As<bool>(); |
| 1521 | case Core::HID::NpadStyleIndex::SegaGenesis: | 1522 | case Core::HID::NpadStyleIndex::SegaGenesis: |
| 1522 | return style.lager; | 1523 | return style.lager.As<bool>(); |
| 1523 | default: | 1524 | default: |
| 1524 | return false; | 1525 | return false; |
| 1525 | } | 1526 | } |
diff --git a/src/core/hle/service/nfp/amiibo_crypto.cpp b/src/core/hle/service/nfp/amiibo_crypto.cpp index c32a6816b..167e29572 100644 --- a/src/core/hle/service/nfp/amiibo_crypto.cpp +++ b/src/core/hle/service/nfp/amiibo_crypto.cpp | |||
| @@ -9,6 +9,7 @@ | |||
| 9 | #include <mbedtls/hmac_drbg.h> | 9 | #include <mbedtls/hmac_drbg.h> |
| 10 | 10 | ||
| 11 | #include "common/fs/file.h" | 11 | #include "common/fs/file.h" |
| 12 | #include "common/fs/fs.h" | ||
| 12 | #include "common/fs/path_util.h" | 13 | #include "common/fs/path_util.h" |
| 13 | #include "common/logging/log.h" | 14 | #include "common/logging/log.h" |
| 14 | #include "core/hle/service/mii/mii_manager.h" | 15 | #include "core/hle/service/mii/mii_manager.h" |
| @@ -279,7 +280,7 @@ bool LoadKeys(InternalKey& locked_secret, InternalKey& unfixed_info) { | |||
| 279 | Common::FS::FileType::BinaryFile}; | 280 | Common::FS::FileType::BinaryFile}; |
| 280 | 281 | ||
| 281 | if (!keys_file.IsOpen()) { | 282 | if (!keys_file.IsOpen()) { |
| 282 | LOG_ERROR(Service_NFP, "No keys detected"); | 283 | LOG_ERROR(Service_NFP, "Failed to open key file"); |
| 283 | return false; | 284 | return false; |
| 284 | } | 285 | } |
| 285 | 286 | ||
| @@ -295,6 +296,11 @@ bool LoadKeys(InternalKey& locked_secret, InternalKey& unfixed_info) { | |||
| 295 | return true; | 296 | return true; |
| 296 | } | 297 | } |
| 297 | 298 | ||
| 299 | bool IsKeyAvailable() { | ||
| 300 | const auto yuzu_keys_dir = Common::FS::GetYuzuPath(Common::FS::YuzuPath::KeysDir); | ||
| 301 | return Common::FS::Exists(yuzu_keys_dir / "key_retail.bin"); | ||
| 302 | } | ||
| 303 | |||
| 298 | bool DecodeAmiibo(const EncryptedNTAG215File& encrypted_tag_data, NTAG215File& tag_data) { | 304 | bool DecodeAmiibo(const EncryptedNTAG215File& encrypted_tag_data, NTAG215File& tag_data) { |
| 299 | InternalKey locked_secret{}; | 305 | InternalKey locked_secret{}; |
| 300 | InternalKey unfixed_info{}; | 306 | InternalKey unfixed_info{}; |
diff --git a/src/core/hle/service/nfp/amiibo_crypto.h b/src/core/hle/service/nfp/amiibo_crypto.h index 0175ced91..1fa61174e 100644 --- a/src/core/hle/service/nfp/amiibo_crypto.h +++ b/src/core/hle/service/nfp/amiibo_crypto.h | |||
| @@ -91,6 +91,9 @@ void Cipher(const DerivedKeys& keys, const NTAG215File& in_data, NTAG215File& ou | |||
| 91 | /// Loads both amiibo keys from key_retail.bin | 91 | /// Loads both amiibo keys from key_retail.bin |
| 92 | bool LoadKeys(InternalKey& locked_secret, InternalKey& unfixed_info); | 92 | bool LoadKeys(InternalKey& locked_secret, InternalKey& unfixed_info); |
| 93 | 93 | ||
| 94 | /// Returns true if key_retail.bin exist | ||
| 95 | bool IsKeyAvailable(); | ||
| 96 | |||
| 94 | /// Decodes encripted amiibo data returns true if output is valid | 97 | /// Decodes encripted amiibo data returns true if output is valid |
| 95 | bool DecodeAmiibo(const EncryptedNTAG215File& encrypted_tag_data, NTAG215File& tag_data); | 98 | bool DecodeAmiibo(const EncryptedNTAG215File& encrypted_tag_data, NTAG215File& tag_data); |
| 96 | 99 | ||
diff --git a/src/core/hle/service/nfp/nfp_device.cpp b/src/core/hle/service/nfp/nfp_device.cpp index 76f8a267a..b19672560 100644 --- a/src/core/hle/service/nfp/nfp_device.cpp +++ b/src/core/hle/service/nfp/nfp_device.cpp | |||
| @@ -17,6 +17,7 @@ | |||
| 17 | #include "core/hle/ipc_helpers.h" | 17 | #include "core/hle/ipc_helpers.h" |
| 18 | #include "core/hle/kernel/k_event.h" | 18 | #include "core/hle/kernel/k_event.h" |
| 19 | #include "core/hle/service/mii/mii_manager.h" | 19 | #include "core/hle/service/mii/mii_manager.h" |
| 20 | #include "core/hle/service/mii/types.h" | ||
| 20 | #include "core/hle/service/nfp/amiibo_crypto.h" | 21 | #include "core/hle/service/nfp/amiibo_crypto.h" |
| 21 | #include "core/hle/service/nfp/nfp.h" | 22 | #include "core/hle/service/nfp/nfp.h" |
| 22 | #include "core/hle/service/nfp/nfp_device.h" | 23 | #include "core/hle/service/nfp/nfp_device.h" |
| @@ -233,6 +234,14 @@ Result NfpDevice::Mount(MountTarget mount_target_) { | |||
| 233 | return NotAnAmiibo; | 234 | return NotAnAmiibo; |
| 234 | } | 235 | } |
| 235 | 236 | ||
| 237 | // Mark amiibos as read only when keys are missing | ||
| 238 | if (!AmiiboCrypto::IsKeyAvailable()) { | ||
| 239 | LOG_ERROR(Service_NFP, "No keys detected"); | ||
| 240 | device_state = DeviceState::TagMounted; | ||
| 241 | mount_target = MountTarget::Rom; | ||
| 242 | return ResultSuccess; | ||
| 243 | } | ||
| 244 | |||
| 236 | if (!AmiiboCrypto::DecodeAmiibo(encrypted_tag_data, tag_data)) { | 245 | if (!AmiiboCrypto::DecodeAmiibo(encrypted_tag_data, tag_data)) { |
| 237 | LOG_ERROR(Service_NFP, "Can't decode amiibo {}", device_state); | 246 | LOG_ERROR(Service_NFP, "Can't decode amiibo {}", device_state); |
| 238 | return CorruptedData; | 247 | return CorruptedData; |
diff --git a/src/core/hle/service/nfp/nfp_device.h b/src/core/hle/service/nfp/nfp_device.h index a5b72cf19..76d0e9ae4 100644 --- a/src/core/hle/service/nfp/nfp_device.h +++ b/src/core/hle/service/nfp/nfp_device.h | |||
| @@ -8,7 +8,6 @@ | |||
| 8 | 8 | ||
| 9 | #include "common/common_funcs.h" | 9 | #include "common/common_funcs.h" |
| 10 | #include "core/hle/service/kernel_helpers.h" | 10 | #include "core/hle/service/kernel_helpers.h" |
| 11 | #include "core/hle/service/mii/types.h" | ||
| 12 | #include "core/hle/service/nfp/nfp_types.h" | 11 | #include "core/hle/service/nfp/nfp_types.h" |
| 13 | #include "core/hle/service/service.h" | 12 | #include "core/hle/service/service.h" |
| 14 | 13 | ||
diff --git a/src/core/hle/service/nfp/nfp_types.h b/src/core/hle/service/nfp/nfp_types.h index c09f9ddb6..63d5917cb 100644 --- a/src/core/hle/service/nfp/nfp_types.h +++ b/src/core/hle/service/nfp/nfp_types.h | |||
| @@ -17,11 +17,6 @@ enum class ServiceType : u32 { | |||
| 17 | System, | 17 | System, |
| 18 | }; | 18 | }; |
| 19 | 19 | ||
| 20 | enum class State : u32 { | ||
| 21 | NonInitialized, | ||
| 22 | Initialized, | ||
| 23 | }; | ||
| 24 | |||
| 25 | enum class DeviceState : u32 { | 20 | enum class DeviceState : u32 { |
| 26 | Initialized, | 21 | Initialized, |
| 27 | SearchingForTag, | 22 | SearchingForTag, |
diff --git a/src/core/hle/service/nfp/nfp_user.cpp b/src/core/hle/service/nfp/nfp_user.cpp index 4ed53b534..33e2ef518 100644 --- a/src/core/hle/service/nfp/nfp_user.cpp +++ b/src/core/hle/service/nfp/nfp_user.cpp | |||
| @@ -6,12 +6,9 @@ | |||
| 6 | 6 | ||
| 7 | #include "common/logging/log.h" | 7 | #include "common/logging/log.h" |
| 8 | #include "core/core.h" | 8 | #include "core/core.h" |
| 9 | #include "core/hid/emulated_controller.h" | ||
| 10 | #include "core/hid/hid_core.h" | ||
| 11 | #include "core/hid/hid_types.h" | 9 | #include "core/hid/hid_types.h" |
| 12 | #include "core/hle/ipc_helpers.h" | 10 | #include "core/hle/ipc_helpers.h" |
| 13 | #include "core/hle/kernel/k_event.h" | 11 | #include "core/hle/kernel/k_event.h" |
| 14 | #include "core/hle/service/mii/mii_manager.h" | ||
| 15 | #include "core/hle/service/nfp/nfp_device.h" | 12 | #include "core/hle/service/nfp/nfp_device.h" |
| 16 | #include "core/hle/service/nfp/nfp_result.h" | 13 | #include "core/hle/service/nfp/nfp_result.h" |
| 17 | #include "core/hle/service/nfp/nfp_user.h" | 14 | #include "core/hle/service/nfp/nfp_user.h" |
diff --git a/src/core/hle/service/nfp/nfp_user.h b/src/core/hle/service/nfp/nfp_user.h index 68c60ae82..47aff3695 100644 --- a/src/core/hle/service/nfp/nfp_user.h +++ b/src/core/hle/service/nfp/nfp_user.h | |||
| @@ -4,8 +4,7 @@ | |||
| 4 | #pragma once | 4 | #pragma once |
| 5 | 5 | ||
| 6 | #include "core/hle/service/kernel_helpers.h" | 6 | #include "core/hle/service/kernel_helpers.h" |
| 7 | #include "core/hle/service/nfp/nfp.h" | 7 | #include "core/hle/service/service.h" |
| 8 | #include "core/hle/service/nfp/nfp_types.h" | ||
| 9 | 8 | ||
| 10 | namespace Service::NFP { | 9 | namespace Service::NFP { |
| 11 | class NfpDevice; | 10 | class NfpDevice; |
| @@ -15,6 +14,11 @@ public: | |||
| 15 | explicit IUser(Core::System& system_); | 14 | explicit IUser(Core::System& system_); |
| 16 | 15 | ||
| 17 | private: | 16 | private: |
| 17 | enum class State : u32 { | ||
| 18 | NonInitialized, | ||
| 19 | Initialized, | ||
| 20 | }; | ||
| 21 | |||
| 18 | void Initialize(Kernel::HLERequestContext& ctx); | 22 | void Initialize(Kernel::HLERequestContext& ctx); |
| 19 | void Finalize(Kernel::HLERequestContext& ctx); | 23 | void Finalize(Kernel::HLERequestContext& ctx); |
| 20 | void ListDevices(Kernel::HLERequestContext& ctx); | 24 | void ListDevices(Kernel::HLERequestContext& ctx); |
diff --git a/src/core/hle/service/nvdrv/core/nvmap.cpp b/src/core/hle/service/nvdrv/core/nvmap.cpp index fbd8a74a5..a51ca5444 100644 --- a/src/core/hle/service/nvdrv/core/nvmap.cpp +++ b/src/core/hle/service/nvdrv/core/nvmap.cpp | |||
| @@ -255,15 +255,16 @@ std::optional<NvMap::FreeInfo> NvMap::FreeHandle(Handle::Id handle, bool interna | |||
| 255 | .address = handle_description->address, | 255 | .address = handle_description->address, |
| 256 | .size = handle_description->size, | 256 | .size = handle_description->size, |
| 257 | .was_uncached = handle_description->flags.map_uncached.Value() != 0, | 257 | .was_uncached = handle_description->flags.map_uncached.Value() != 0, |
| 258 | .can_unlock = true, | ||
| 258 | }; | 259 | }; |
| 259 | } else { | 260 | } else { |
| 260 | return std::nullopt; | 261 | return std::nullopt; |
| 261 | } | 262 | } |
| 262 | 263 | ||
| 263 | // Handle hasn't been freed from memory, set address to 0 to mark that the handle wasn't freed | 264 | // If the handle hasn't been freed from memory, mark that |
| 264 | if (!hWeak.expired()) { | 265 | if (!hWeak.expired()) { |
| 265 | LOG_DEBUG(Service_NVDRV, "nvmap handle: {} wasn't freed as it is still in use", handle); | 266 | LOG_DEBUG(Service_NVDRV, "nvmap handle: {} wasn't freed as it is still in use", handle); |
| 266 | freeInfo.address = 0; | 267 | freeInfo.can_unlock = false; |
| 267 | } | 268 | } |
| 268 | 269 | ||
| 269 | return freeInfo; | 270 | return freeInfo; |
diff --git a/src/core/hle/service/nvdrv/core/nvmap.h b/src/core/hle/service/nvdrv/core/nvmap.h index b9dd3801f..a8e573890 100644 --- a/src/core/hle/service/nvdrv/core/nvmap.h +++ b/src/core/hle/service/nvdrv/core/nvmap.h | |||
| @@ -105,6 +105,7 @@ public: | |||
| 105 | u64 address; //!< Address the handle referred to before deletion | 105 | u64 address; //!< Address the handle referred to before deletion |
| 106 | u64 size; //!< Page-aligned handle size | 106 | u64 size; //!< Page-aligned handle size |
| 107 | bool was_uncached; //!< If the handle was allocated as uncached | 107 | bool was_uncached; //!< If the handle was allocated as uncached |
| 108 | bool can_unlock; //!< If the address region is ready to be unlocked | ||
| 108 | }; | 109 | }; |
| 109 | 110 | ||
| 110 | explicit NvMap(Tegra::Host1x::Host1x& host1x); | 111 | explicit NvMap(Tegra::Host1x::Host1x& host1x); |
diff --git a/src/core/hle/service/nvdrv/devices/nvmap.cpp b/src/core/hle/service/nvdrv/devices/nvmap.cpp index b60679021..44388655d 100644 --- a/src/core/hle/service/nvdrv/devices/nvmap.cpp +++ b/src/core/hle/service/nvdrv/devices/nvmap.cpp | |||
| @@ -251,10 +251,12 @@ NvResult nvmap::IocFree(const std::vector<u8>& input, std::vector<u8>& output) { | |||
| 251 | } | 251 | } |
| 252 | 252 | ||
| 253 | if (auto freeInfo{file.FreeHandle(params.handle, false)}) { | 253 | if (auto freeInfo{file.FreeHandle(params.handle, false)}) { |
| 254 | ASSERT(system.CurrentProcess() | 254 | if (freeInfo->can_unlock) { |
| 255 | ->PageTable() | 255 | ASSERT(system.CurrentProcess() |
| 256 | .UnlockForDeviceAddressSpace(freeInfo->address, freeInfo->size) | 256 | ->PageTable() |
| 257 | .IsSuccess()); | 257 | .UnlockForDeviceAddressSpace(freeInfo->address, freeInfo->size) |
| 258 | .IsSuccess()); | ||
| 259 | } | ||
| 258 | params.address = freeInfo->address; | 260 | params.address = freeInfo->address; |
| 259 | params.size = static_cast<u32>(freeInfo->size); | 261 | params.size = static_cast<u32>(freeInfo->size); |
| 260 | params.flags.raw = 0; | 262 | params.flags.raw = 0; |
diff --git a/src/core/hle/service/nvdrv/nvdrv.cpp b/src/core/hle/service/nvdrv/nvdrv.cpp index 9d9924395..9f4c7c99a 100644 --- a/src/core/hle/service/nvdrv/nvdrv.cpp +++ b/src/core/hle/service/nvdrv/nvdrv.cpp | |||
| @@ -53,7 +53,7 @@ void InstallInterfaces(SM::ServiceManager& service_manager, NVFlinger::NVFlinger | |||
| 53 | } | 53 | } |
| 54 | 54 | ||
| 55 | Module::Module(Core::System& system) | 55 | Module::Module(Core::System& system) |
| 56 | : service_context{system, "nvdrv"}, events_interface{*this}, container{system.Host1x()} { | 56 | : container{system.Host1x()}, service_context{system, "nvdrv"}, events_interface{*this} { |
| 57 | builders["/dev/nvhost-as-gpu"] = [this, &system](DeviceFD fd) { | 57 | builders["/dev/nvhost-as-gpu"] = [this, &system](DeviceFD fd) { |
| 58 | std::shared_ptr<Devices::nvdevice> device = | 58 | std::shared_ptr<Devices::nvdevice> device = |
| 59 | std::make_shared<Devices::nvhost_as_gpu>(system, *this, container); | 59 | std::make_shared<Devices::nvhost_as_gpu>(system, *this, container); |
diff --git a/src/core/hle/service/nvdrv/nvdrv.h b/src/core/hle/service/nvdrv/nvdrv.h index 146d046a9..f3c81bd88 100644 --- a/src/core/hle/service/nvdrv/nvdrv.h +++ b/src/core/hle/service/nvdrv/nvdrv.h | |||
| @@ -97,6 +97,9 @@ private: | |||
| 97 | friend class EventInterface; | 97 | friend class EventInterface; |
| 98 | friend class Service::NVFlinger::NVFlinger; | 98 | friend class Service::NVFlinger::NVFlinger; |
| 99 | 99 | ||
| 100 | /// Manages syncpoints on the host | ||
| 101 | NvCore::Container container; | ||
| 102 | |||
| 100 | /// Id to use for the next open file descriptor. | 103 | /// Id to use for the next open file descriptor. |
| 101 | DeviceFD next_fd = 1; | 104 | DeviceFD next_fd = 1; |
| 102 | 105 | ||
| @@ -108,9 +111,6 @@ private: | |||
| 108 | 111 | ||
| 109 | EventInterface events_interface; | 112 | EventInterface events_interface; |
| 110 | 113 | ||
| 111 | /// Manages syncpoints on the host | ||
| 112 | NvCore::Container container; | ||
| 113 | |||
| 114 | std::unordered_map<std::string, std::function<FilesContainerType::iterator(DeviceFD)>> builders; | 114 | std::unordered_map<std::string, std::function<FilesContainerType::iterator(DeviceFD)>> builders; |
| 115 | }; | 115 | }; |
| 116 | 116 | ||
diff --git a/src/core/hle/service/nvflinger/buffer_queue_producer.cpp b/src/core/hle/service/nvflinger/buffer_queue_producer.cpp index 77ddbb6ef..41ba44b21 100644 --- a/src/core/hle/service/nvflinger/buffer_queue_producer.cpp +++ b/src/core/hle/service/nvflinger/buffer_queue_producer.cpp | |||
| @@ -742,6 +742,13 @@ Status BufferQueueProducer::Disconnect(NativeWindowApi api) { | |||
| 742 | return Status::NoError; | 742 | return Status::NoError; |
| 743 | } | 743 | } |
| 744 | 744 | ||
| 745 | // HACK: We are not Android. Remove handle for items in queue, and clear queue. | ||
| 746 | // Allows synchronous destruction of nvmap handles. | ||
| 747 | for (auto& item : core->queue) { | ||
| 748 | nvmap.FreeHandle(item.graphic_buffer->BufferId(), true); | ||
| 749 | } | ||
| 750 | core->queue.clear(); | ||
| 751 | |||
| 745 | switch (api) { | 752 | switch (api) { |
| 746 | case NativeWindowApi::Egl: | 753 | case NativeWindowApi::Egl: |
| 747 | case NativeWindowApi::Cpu: | 754 | case NativeWindowApi::Cpu: |
diff --git a/src/core/hle/service/nvflinger/nvflinger.cpp b/src/core/hle/service/nvflinger/nvflinger.cpp index aa14d2cbc..c3af12c90 100644 --- a/src/core/hle/service/nvflinger/nvflinger.cpp +++ b/src/core/hle/service/nvflinger/nvflinger.cpp | |||
| @@ -102,15 +102,19 @@ NVFlinger::~NVFlinger() { | |||
| 102 | system.CoreTiming().UnscheduleEvent(single_composition_event, {}); | 102 | system.CoreTiming().UnscheduleEvent(single_composition_event, {}); |
| 103 | } | 103 | } |
| 104 | 104 | ||
| 105 | ShutdownLayers(); | ||
| 106 | |||
| 107 | if (nvdrv) { | ||
| 108 | nvdrv->Close(disp_fd); | ||
| 109 | } | ||
| 110 | } | ||
| 111 | |||
| 112 | void NVFlinger::ShutdownLayers() { | ||
| 105 | for (auto& display : displays) { | 113 | for (auto& display : displays) { |
| 106 | for (size_t layer = 0; layer < display.GetNumLayers(); ++layer) { | 114 | for (size_t layer = 0; layer < display.GetNumLayers(); ++layer) { |
| 107 | display.GetLayer(layer).Core().NotifyShutdown(); | 115 | display.GetLayer(layer).Core().NotifyShutdown(); |
| 108 | } | 116 | } |
| 109 | } | 117 | } |
| 110 | |||
| 111 | if (nvdrv) { | ||
| 112 | nvdrv->Close(disp_fd); | ||
| 113 | } | ||
| 114 | } | 118 | } |
| 115 | 119 | ||
| 116 | void NVFlinger::SetNVDrvInstance(std::shared_ptr<Nvidia::Module> instance) { | 120 | void NVFlinger::SetNVDrvInstance(std::shared_ptr<Nvidia::Module> instance) { |
| @@ -134,6 +138,19 @@ std::optional<u64> NVFlinger::OpenDisplay(std::string_view name) { | |||
| 134 | return itr->GetID(); | 138 | return itr->GetID(); |
| 135 | } | 139 | } |
| 136 | 140 | ||
| 141 | bool NVFlinger::CloseDisplay(u64 display_id) { | ||
| 142 | const auto lock_guard = Lock(); | ||
| 143 | auto* const display = FindDisplay(display_id); | ||
| 144 | |||
| 145 | if (display == nullptr) { | ||
| 146 | return false; | ||
| 147 | } | ||
| 148 | |||
| 149 | display->Reset(); | ||
| 150 | |||
| 151 | return true; | ||
| 152 | } | ||
| 153 | |||
| 137 | std::optional<u64> NVFlinger::CreateLayer(u64 display_id) { | 154 | std::optional<u64> NVFlinger::CreateLayer(u64 display_id) { |
| 138 | const auto lock_guard = Lock(); | 155 | const auto lock_guard = Lock(); |
| 139 | auto* const display = FindDisplay(display_id); | 156 | auto* const display = FindDisplay(display_id); |
diff --git a/src/core/hle/service/nvflinger/nvflinger.h b/src/core/hle/service/nvflinger/nvflinger.h index 99509bc5b..460bef976 100644 --- a/src/core/hle/service/nvflinger/nvflinger.h +++ b/src/core/hle/service/nvflinger/nvflinger.h | |||
| @@ -48,6 +48,8 @@ public: | |||
| 48 | explicit NVFlinger(Core::System& system_, HosBinderDriverServer& hos_binder_driver_server_); | 48 | explicit NVFlinger(Core::System& system_, HosBinderDriverServer& hos_binder_driver_server_); |
| 49 | ~NVFlinger(); | 49 | ~NVFlinger(); |
| 50 | 50 | ||
| 51 | void ShutdownLayers(); | ||
| 52 | |||
| 51 | /// Sets the NVDrv module instance to use to send buffers to the GPU. | 53 | /// Sets the NVDrv module instance to use to send buffers to the GPU. |
| 52 | void SetNVDrvInstance(std::shared_ptr<Nvidia::Module> instance); | 54 | void SetNVDrvInstance(std::shared_ptr<Nvidia::Module> instance); |
| 53 | 55 | ||
| @@ -56,6 +58,11 @@ public: | |||
| 56 | /// If an invalid display name is provided, then an empty optional is returned. | 58 | /// If an invalid display name is provided, then an empty optional is returned. |
| 57 | [[nodiscard]] std::optional<u64> OpenDisplay(std::string_view name); | 59 | [[nodiscard]] std::optional<u64> OpenDisplay(std::string_view name); |
| 58 | 60 | ||
| 61 | /// Closes the specified display by its ID. | ||
| 62 | /// | ||
| 63 | /// Returns false if an invalid display ID is provided. | ||
| 64 | [[nodiscard]] bool CloseDisplay(u64 display_id); | ||
| 65 | |||
| 59 | /// Creates a layer on the specified display and returns the layer ID. | 66 | /// Creates a layer on the specified display and returns the layer ID. |
| 60 | /// | 67 | /// |
| 61 | /// If an invalid display ID is specified, then an empty optional is returned. | 68 | /// If an invalid display ID is specified, then an empty optional is returned. |
diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp index dadaf897f..5db6588e4 100644 --- a/src/core/hle/service/service.cpp +++ b/src/core/hle/service/service.cpp | |||
| @@ -303,4 +303,8 @@ Services::Services(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system | |||
| 303 | 303 | ||
| 304 | Services::~Services() = default; | 304 | Services::~Services() = default; |
| 305 | 305 | ||
| 306 | void Services::KillNVNFlinger() { | ||
| 307 | nv_flinger->ShutdownLayers(); | ||
| 308 | } | ||
| 309 | |||
| 306 | } // namespace Service | 310 | } // namespace Service |
diff --git a/src/core/hle/service/service.h b/src/core/hle/service/service.h index 5bf197c51..ec9deeee4 100644 --- a/src/core/hle/service/service.h +++ b/src/core/hle/service/service.h | |||
| @@ -238,6 +238,8 @@ public: | |||
| 238 | explicit Services(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system); | 238 | explicit Services(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system); |
| 239 | ~Services(); | 239 | ~Services(); |
| 240 | 240 | ||
| 241 | void KillNVNFlinger(); | ||
| 242 | |||
| 241 | private: | 243 | private: |
| 242 | std::unique_ptr<NVFlinger::HosBinderDriverServer> hos_binder_driver_server; | 244 | std::unique_ptr<NVFlinger::HosBinderDriverServer> hos_binder_driver_server; |
| 243 | std::unique_ptr<NVFlinger::NVFlinger> nv_flinger; | 245 | std::unique_ptr<NVFlinger::NVFlinger> nv_flinger; |
diff --git a/src/core/hle/service/sm/sm.cpp b/src/core/hle/service/sm/sm.cpp index 48e70f93c..cb6c0e96f 100644 --- a/src/core/hle/service/sm/sm.cpp +++ b/src/core/hle/service/sm/sm.cpp | |||
| @@ -80,7 +80,6 @@ ResultVal<Kernel::KPort*> ServiceManager::GetServicePort(const std::string& name | |||
| 80 | } | 80 | } |
| 81 | 81 | ||
| 82 | auto* port = Kernel::KPort::Create(kernel); | 82 | auto* port = Kernel::KPort::Create(kernel); |
| 83 | SCOPE_EXIT({ port->Close(); }); | ||
| 84 | 83 | ||
| 85 | port->Initialize(ServerSessionCountMax, false, name); | 84 | port->Initialize(ServerSessionCountMax, false, name); |
| 86 | auto handler = it->second; | 85 | auto handler = it->second; |
| @@ -150,9 +149,10 @@ ResultVal<Kernel::KClientSession*> SM::GetServiceImpl(Kernel::HLERequestContext& | |||
| 150 | return port_result.Code(); | 149 | return port_result.Code(); |
| 151 | } | 150 | } |
| 152 | auto& port = port_result.Unwrap(); | 151 | auto& port = port_result.Unwrap(); |
| 153 | SCOPE_EXIT({ port->GetClientPort().Close(); }); | 152 | SCOPE_EXIT({ |
| 154 | 153 | port->GetClientPort().Close(); | |
| 155 | kernel.RegisterServerObject(&port->GetServerPort()); | 154 | port->GetServerPort().Close(); |
| 155 | }); | ||
| 156 | 156 | ||
| 157 | // Create a new session. | 157 | // Create a new session. |
| 158 | Kernel::KClientSession* session{}; | 158 | Kernel::KClientSession* session{}; |
diff --git a/src/core/hle/service/sm/sm_controller.cpp b/src/core/hle/service/sm/sm_controller.cpp index 2a4bd64ab..46a8439d8 100644 --- a/src/core/hle/service/sm/sm_controller.cpp +++ b/src/core/hle/service/sm/sm_controller.cpp | |||
| @@ -15,9 +15,10 @@ | |||
| 15 | namespace Service::SM { | 15 | namespace Service::SM { |
| 16 | 16 | ||
| 17 | void Controller::ConvertCurrentObjectToDomain(Kernel::HLERequestContext& ctx) { | 17 | void Controller::ConvertCurrentObjectToDomain(Kernel::HLERequestContext& ctx) { |
| 18 | ASSERT_MSG(!ctx.Session()->IsDomain(), "Session is already a domain"); | 18 | ASSERT_MSG(!ctx.Session()->GetSessionRequestManager()->IsDomain(), |
| 19 | "Session is already a domain"); | ||
| 19 | LOG_DEBUG(Service, "called, server_session={}", ctx.Session()->GetId()); | 20 | LOG_DEBUG(Service, "called, server_session={}", ctx.Session()->GetId()); |
| 20 | ctx.Session()->ConvertToDomain(); | 21 | ctx.Session()->GetSessionRequestManager()->ConvertToDomainOnRequestEnd(); |
| 21 | 22 | ||
| 22 | IPC::ResponseBuilder rb{ctx, 3}; | 23 | IPC::ResponseBuilder rb{ctx, 3}; |
| 23 | rb.Push(ResultSuccess); | 24 | rb.Push(ResultSuccess); |
| @@ -27,23 +28,36 @@ void Controller::ConvertCurrentObjectToDomain(Kernel::HLERequestContext& ctx) { | |||
| 27 | void Controller::CloneCurrentObject(Kernel::HLERequestContext& ctx) { | 28 | void Controller::CloneCurrentObject(Kernel::HLERequestContext& ctx) { |
| 28 | LOG_DEBUG(Service, "called"); | 29 | LOG_DEBUG(Service, "called"); |
| 29 | 30 | ||
| 31 | auto& process = *ctx.GetThread().GetOwnerProcess(); | ||
| 30 | auto& parent_session = *ctx.Session()->GetParent(); | 32 | auto& parent_session = *ctx.Session()->GetParent(); |
| 31 | auto& parent_port = parent_session.GetParent()->GetParent()->GetClientPort(); | ||
| 32 | auto& session_manager = parent_session.GetServerSession().GetSessionRequestManager(); | 33 | auto& session_manager = parent_session.GetServerSession().GetSessionRequestManager(); |
| 34 | auto& session_handler = session_manager->SessionHandler(); | ||
| 33 | 35 | ||
| 34 | // Create a session. | 36 | // FIXME: this is duplicated from the SVC, it should just call it instead |
| 35 | Kernel::KClientSession* session{}; | 37 | // once this is a proper process |
| 36 | const Result result = parent_port.CreateSession(std::addressof(session), session_manager); | 38 | |
| 37 | if (result.IsError()) { | 39 | // Reserve a new session from the process resource limit. |
| 38 | LOG_CRITICAL(Service, "CreateSession failed with error 0x{:08X}", result.raw); | 40 | Kernel::KScopedResourceReservation session_reservation(&process, |
| 39 | IPC::ResponseBuilder rb{ctx, 2}; | 41 | Kernel::LimitableResource::Sessions); |
| 40 | rb.Push(result); | 42 | ASSERT(session_reservation.Succeeded()); |
| 41 | } | 43 | |
| 44 | // Create the session. | ||
| 45 | Kernel::KSession* session = Kernel::KSession::Create(system.Kernel()); | ||
| 46 | ASSERT(session != nullptr); | ||
| 47 | |||
| 48 | // Initialize the session. | ||
| 49 | session->Initialize(nullptr, parent_session.GetName(), session_manager); | ||
| 50 | |||
| 51 | // Commit the session reservation. | ||
| 52 | session_reservation.Commit(); | ||
| 53 | |||
| 54 | // Register the session. | ||
| 55 | session_handler.ClientConnected(&session->GetServerSession()); | ||
| 42 | 56 | ||
| 43 | // We succeeded. | 57 | // We succeeded. |
| 44 | IPC::ResponseBuilder rb{ctx, 2, 0, 1, IPC::ResponseBuilder::Flags::AlwaysMoveHandles}; | 58 | IPC::ResponseBuilder rb{ctx, 2, 0, 1, IPC::ResponseBuilder::Flags::AlwaysMoveHandles}; |
| 45 | rb.Push(ResultSuccess); | 59 | rb.Push(ResultSuccess); |
| 46 | rb.PushMoveObjects(session); | 60 | rb.PushMoveObjects(session->GetClientSession()); |
| 47 | } | 61 | } |
| 48 | 62 | ||
| 49 | void Controller::CloneCurrentObjectEx(Kernel::HLERequestContext& ctx) { | 63 | void Controller::CloneCurrentObjectEx(Kernel::HLERequestContext& ctx) { |
diff --git a/src/core/hle/service/vi/display/vi_display.h b/src/core/hle/service/vi/display/vi_display.h index 33d5f398c..0b65a65da 100644 --- a/src/core/hle/service/vi/display/vi_display.h +++ b/src/core/hle/service/vi/display/vi_display.h | |||
| @@ -106,6 +106,12 @@ public: | |||
| 106 | /// | 106 | /// |
| 107 | void CloseLayer(u64 layer_id); | 107 | void CloseLayer(u64 layer_id); |
| 108 | 108 | ||
| 109 | /// Resets the display for a new connection. | ||
| 110 | void Reset() { | ||
| 111 | layers.clear(); | ||
| 112 | got_vsync_event = false; | ||
| 113 | } | ||
| 114 | |||
| 109 | /// Attempts to find a layer with the given ID. | 115 | /// Attempts to find a layer with the given ID. |
| 110 | /// | 116 | /// |
| 111 | /// @param layer_id The layer ID. | 117 | /// @param layer_id The layer ID. |
diff --git a/src/core/hle/service/vi/vi.cpp b/src/core/hle/service/vi/vi.cpp index 9c917cacf..bb283e74e 100644 --- a/src/core/hle/service/vi/vi.cpp +++ b/src/core/hle/service/vi/vi.cpp | |||
| @@ -324,10 +324,10 @@ private: | |||
| 324 | IPC::RequestParser rp{ctx}; | 324 | IPC::RequestParser rp{ctx}; |
| 325 | const u64 display = rp.Pop<u64>(); | 325 | const u64 display = rp.Pop<u64>(); |
| 326 | 326 | ||
| 327 | LOG_WARNING(Service_VI, "(STUBBED) called. display=0x{:016X}", display); | 327 | const Result rc = nv_flinger.CloseDisplay(display) ? ResultSuccess : ResultUnknown; |
| 328 | 328 | ||
| 329 | IPC::ResponseBuilder rb{ctx, 2}; | 329 | IPC::ResponseBuilder rb{ctx, 2}; |
| 330 | rb.Push(ResultSuccess); | 330 | rb.Push(rc); |
| 331 | } | 331 | } |
| 332 | 332 | ||
| 333 | void CreateManagedLayer(Kernel::HLERequestContext& ctx) { | 333 | void CreateManagedLayer(Kernel::HLERequestContext& ctx) { |
| @@ -508,10 +508,10 @@ private: | |||
| 508 | IPC::RequestParser rp{ctx}; | 508 | IPC::RequestParser rp{ctx}; |
| 509 | const u64 display_id = rp.Pop<u64>(); | 509 | const u64 display_id = rp.Pop<u64>(); |
| 510 | 510 | ||
| 511 | LOG_WARNING(Service_VI, "(STUBBED) called. display_id=0x{:016X}", display_id); | 511 | const Result rc = nv_flinger.CloseDisplay(display_id) ? ResultSuccess : ResultUnknown; |
| 512 | 512 | ||
| 513 | IPC::ResponseBuilder rb{ctx, 2}; | 513 | IPC::ResponseBuilder rb{ctx, 2}; |
| 514 | rb.Push(ResultSuccess); | 514 | rb.Push(rc); |
| 515 | } | 515 | } |
| 516 | 516 | ||
| 517 | // This literally does nothing internally in the actual service itself, | 517 | // This literally does nothing internally in the actual service itself, |
diff --git a/src/core/memory.cpp b/src/core/memory.cpp index 9637cb5b1..3ca80c8ff 100644 --- a/src/core/memory.cpp +++ b/src/core/memory.cpp | |||
| @@ -233,18 +233,17 @@ struct Memory::Impl { | |||
| 233 | current_vaddr, src_addr, size); | 233 | current_vaddr, src_addr, size); |
| 234 | std::memset(dest_buffer, 0, copy_amount); | 234 | std::memset(dest_buffer, 0, copy_amount); |
| 235 | }, | 235 | }, |
| 236 | [&dest_buffer](const std::size_t copy_amount, const u8* const src_ptr) { | 236 | [&](const std::size_t copy_amount, const u8* const src_ptr) { |
| 237 | std::memcpy(dest_buffer, src_ptr, copy_amount); | 237 | std::memcpy(dest_buffer, src_ptr, copy_amount); |
| 238 | }, | 238 | }, |
| 239 | [&system = system, &dest_buffer](const VAddr current_vaddr, | 239 | [&](const VAddr current_vaddr, const std::size_t copy_amount, |
| 240 | const std::size_t copy_amount, | 240 | const u8* const host_ptr) { |
| 241 | const u8* const host_ptr) { | ||
| 242 | if constexpr (!UNSAFE) { | 241 | if constexpr (!UNSAFE) { |
| 243 | system.GPU().FlushRegion(current_vaddr, copy_amount); | 242 | system.GPU().FlushRegion(current_vaddr, copy_amount); |
| 244 | } | 243 | } |
| 245 | std::memcpy(dest_buffer, host_ptr, copy_amount); | 244 | std::memcpy(dest_buffer, host_ptr, copy_amount); |
| 246 | }, | 245 | }, |
| 247 | [&dest_buffer](const std::size_t copy_amount) { | 246 | [&](const std::size_t copy_amount) { |
| 248 | dest_buffer = static_cast<u8*>(dest_buffer) + copy_amount; | 247 | dest_buffer = static_cast<u8*>(dest_buffer) + copy_amount; |
| 249 | }); | 248 | }); |
| 250 | } | 249 | } |
| @@ -267,17 +266,16 @@ struct Memory::Impl { | |||
| 267 | "Unmapped WriteBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})", | 266 | "Unmapped WriteBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})", |
| 268 | current_vaddr, dest_addr, size); | 267 | current_vaddr, dest_addr, size); |
| 269 | }, | 268 | }, |
| 270 | [&src_buffer](const std::size_t copy_amount, u8* const dest_ptr) { | 269 | [&](const std::size_t copy_amount, u8* const dest_ptr) { |
| 271 | std::memcpy(dest_ptr, src_buffer, copy_amount); | 270 | std::memcpy(dest_ptr, src_buffer, copy_amount); |
| 272 | }, | 271 | }, |
| 273 | [&system = system, &src_buffer](const VAddr current_vaddr, | 272 | [&](const VAddr current_vaddr, const std::size_t copy_amount, u8* const host_ptr) { |
| 274 | const std::size_t copy_amount, u8* const host_ptr) { | ||
| 275 | if constexpr (!UNSAFE) { | 273 | if constexpr (!UNSAFE) { |
| 276 | system.GPU().InvalidateRegion(current_vaddr, copy_amount); | 274 | system.GPU().InvalidateRegion(current_vaddr, copy_amount); |
| 277 | } | 275 | } |
| 278 | std::memcpy(host_ptr, src_buffer, copy_amount); | 276 | std::memcpy(host_ptr, src_buffer, copy_amount); |
| 279 | }, | 277 | }, |
| 280 | [&src_buffer](const std::size_t copy_amount) { | 278 | [&](const std::size_t copy_amount) { |
| 281 | src_buffer = static_cast<const u8*>(src_buffer) + copy_amount; | 279 | src_buffer = static_cast<const u8*>(src_buffer) + copy_amount; |
| 282 | }); | 280 | }); |
| 283 | } | 281 | } |
| @@ -301,8 +299,7 @@ struct Memory::Impl { | |||
| 301 | [](const std::size_t copy_amount, u8* const dest_ptr) { | 299 | [](const std::size_t copy_amount, u8* const dest_ptr) { |
| 302 | std::memset(dest_ptr, 0, copy_amount); | 300 | std::memset(dest_ptr, 0, copy_amount); |
| 303 | }, | 301 | }, |
| 304 | [&system = system](const VAddr current_vaddr, const std::size_t copy_amount, | 302 | [&](const VAddr current_vaddr, const std::size_t copy_amount, u8* const host_ptr) { |
| 305 | u8* const host_ptr) { | ||
| 306 | system.GPU().InvalidateRegion(current_vaddr, copy_amount); | 303 | system.GPU().InvalidateRegion(current_vaddr, copy_amount); |
| 307 | std::memset(host_ptr, 0, copy_amount); | 304 | std::memset(host_ptr, 0, copy_amount); |
| 308 | }, | 305 | }, |
| @@ -313,22 +310,20 @@ struct Memory::Impl { | |||
| 313 | const std::size_t size) { | 310 | const std::size_t size) { |
| 314 | WalkBlock( | 311 | WalkBlock( |
| 315 | process, dest_addr, size, | 312 | process, dest_addr, size, |
| 316 | [this, &process, &dest_addr, &src_addr, size](const std::size_t copy_amount, | 313 | [&](const std::size_t copy_amount, const VAddr current_vaddr) { |
| 317 | const VAddr current_vaddr) { | ||
| 318 | LOG_ERROR(HW_Memory, | 314 | LOG_ERROR(HW_Memory, |
| 319 | "Unmapped CopyBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})", | 315 | "Unmapped CopyBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})", |
| 320 | current_vaddr, src_addr, size); | 316 | current_vaddr, src_addr, size); |
| 321 | ZeroBlock(process, dest_addr, copy_amount); | 317 | ZeroBlock(process, dest_addr, copy_amount); |
| 322 | }, | 318 | }, |
| 323 | [this, &process, &dest_addr](const std::size_t copy_amount, const u8* const src_ptr) { | 319 | [&](const std::size_t copy_amount, const u8* const src_ptr) { |
| 324 | WriteBlockImpl<false>(process, dest_addr, src_ptr, copy_amount); | 320 | WriteBlockImpl<false>(process, dest_addr, src_ptr, copy_amount); |
| 325 | }, | 321 | }, |
| 326 | [this, &system = system, &process, &dest_addr]( | 322 | [&](const VAddr current_vaddr, const std::size_t copy_amount, u8* const host_ptr) { |
| 327 | const VAddr current_vaddr, const std::size_t copy_amount, u8* const host_ptr) { | ||
| 328 | system.GPU().FlushRegion(current_vaddr, copy_amount); | 323 | system.GPU().FlushRegion(current_vaddr, copy_amount); |
| 329 | WriteBlockImpl<false>(process, dest_addr, host_ptr, copy_amount); | 324 | WriteBlockImpl<false>(process, dest_addr, host_ptr, copy_amount); |
| 330 | }, | 325 | }, |
| 331 | [&dest_addr, &src_addr](const std::size_t copy_amount) { | 326 | [&](const std::size_t copy_amount) { |
| 332 | dest_addr += static_cast<VAddr>(copy_amount); | 327 | dest_addr += static_cast<VAddr>(copy_amount); |
| 333 | src_addr += static_cast<VAddr>(copy_amount); | 328 | src_addr += static_cast<VAddr>(copy_amount); |
| 334 | }); | 329 | }); |
| @@ -575,7 +570,7 @@ struct Memory::Impl { | |||
| 575 | [vaddr]() { | 570 | [vaddr]() { |
| 576 | LOG_ERROR(HW_Memory, "Unmapped Read{} @ 0x{:016X}", sizeof(T) * 8, vaddr); | 571 | LOG_ERROR(HW_Memory, "Unmapped Read{} @ 0x{:016X}", sizeof(T) * 8, vaddr); |
| 577 | }, | 572 | }, |
| 578 | [&system = system, vaddr]() { system.GPU().FlushRegion(vaddr, sizeof(T)); }); | 573 | [&]() { system.GPU().FlushRegion(vaddr, sizeof(T)); }); |
| 579 | if (ptr) { | 574 | if (ptr) { |
| 580 | std::memcpy(&result, ptr, sizeof(T)); | 575 | std::memcpy(&result, ptr, sizeof(T)); |
| 581 | } | 576 | } |
| @@ -599,7 +594,7 @@ struct Memory::Impl { | |||
| 599 | LOG_ERROR(HW_Memory, "Unmapped Write{} @ 0x{:016X} = 0x{:016X}", sizeof(T) * 8, | 594 | LOG_ERROR(HW_Memory, "Unmapped Write{} @ 0x{:016X} = 0x{:016X}", sizeof(T) * 8, |
| 600 | vaddr, static_cast<u64>(data)); | 595 | vaddr, static_cast<u64>(data)); |
| 601 | }, | 596 | }, |
| 602 | [&system = system, vaddr]() { system.GPU().InvalidateRegion(vaddr, sizeof(T)); }); | 597 | [&]() { system.GPU().InvalidateRegion(vaddr, sizeof(T)); }); |
| 603 | if (ptr) { | 598 | if (ptr) { |
| 604 | std::memcpy(ptr, &data, sizeof(T)); | 599 | std::memcpy(ptr, &data, sizeof(T)); |
| 605 | } | 600 | } |
| @@ -613,7 +608,7 @@ struct Memory::Impl { | |||
| 613 | LOG_ERROR(HW_Memory, "Unmapped WriteExclusive{} @ 0x{:016X} = 0x{:016X}", | 608 | LOG_ERROR(HW_Memory, "Unmapped WriteExclusive{} @ 0x{:016X} = 0x{:016X}", |
| 614 | sizeof(T) * 8, vaddr, static_cast<u64>(data)); | 609 | sizeof(T) * 8, vaddr, static_cast<u64>(data)); |
| 615 | }, | 610 | }, |
| 616 | [&system = system, vaddr]() { system.GPU().InvalidateRegion(vaddr, sizeof(T)); }); | 611 | [&]() { system.GPU().InvalidateRegion(vaddr, sizeof(T)); }); |
| 617 | if (ptr) { | 612 | if (ptr) { |
| 618 | const auto volatile_pointer = reinterpret_cast<volatile T*>(ptr); | 613 | const auto volatile_pointer = reinterpret_cast<volatile T*>(ptr); |
| 619 | return Common::AtomicCompareAndSwap(volatile_pointer, data, expected); | 614 | return Common::AtomicCompareAndSwap(volatile_pointer, data, expected); |
| @@ -628,7 +623,7 @@ struct Memory::Impl { | |||
| 628 | LOG_ERROR(HW_Memory, "Unmapped WriteExclusive128 @ 0x{:016X} = 0x{:016X}{:016X}", | 623 | LOG_ERROR(HW_Memory, "Unmapped WriteExclusive128 @ 0x{:016X} = 0x{:016X}{:016X}", |
| 629 | vaddr, static_cast<u64>(data[1]), static_cast<u64>(data[0])); | 624 | vaddr, static_cast<u64>(data[1]), static_cast<u64>(data[0])); |
| 630 | }, | 625 | }, |
| 631 | [&system = system, vaddr]() { system.GPU().InvalidateRegion(vaddr, sizeof(u128)); }); | 626 | [&]() { system.GPU().InvalidateRegion(vaddr, sizeof(u128)); }); |
| 632 | if (ptr) { | 627 | if (ptr) { |
| 633 | const auto volatile_pointer = reinterpret_cast<volatile u64*>(ptr); | 628 | const auto volatile_pointer = reinterpret_cast<volatile u64*>(ptr); |
| 634 | return Common::AtomicCompareAndSwap(volatile_pointer, data, expected); | 629 | return Common::AtomicCompareAndSwap(volatile_pointer, data, expected); |
diff --git a/src/input_common/CMakeLists.txt b/src/input_common/CMakeLists.txt index 2cf9eb97f..cc6f0ffc0 100644 --- a/src/input_common/CMakeLists.txt +++ b/src/input_common/CMakeLists.txt | |||
| @@ -39,21 +39,14 @@ add_library(input_common STATIC | |||
| 39 | if (MSVC) | 39 | if (MSVC) |
| 40 | target_compile_options(input_common PRIVATE | 40 | target_compile_options(input_common PRIVATE |
| 41 | /W4 | 41 | /W4 |
| 42 | /WX | ||
| 43 | 42 | ||
| 44 | /we4242 # 'identifier': conversion from 'type1' to 'type2', possible loss of data | 43 | /we4242 # 'identifier': conversion from 'type1' to 'type2', possible loss of data |
| 45 | /we4244 # 'conversion': conversion from 'type1' to 'type2', possible loss of data | ||
| 46 | /we4245 # 'conversion': conversion from 'type1' to 'type2', signed/unsigned mismatch | ||
| 47 | /we4254 # 'operator': conversion from 'type1:field_bits' to 'type2:field_bits', possible loss of data | 44 | /we4254 # 'operator': conversion from 'type1:field_bits' to 'type2:field_bits', possible loss of data |
| 45 | /we4800 # Implicit conversion from 'type' to bool. Possible information loss | ||
| 48 | ) | 46 | ) |
| 49 | else() | 47 | else() |
| 50 | target_compile_options(input_common PRIVATE | 48 | target_compile_options(input_common PRIVATE |
| 51 | -Werror | ||
| 52 | -Werror=conversion | 49 | -Werror=conversion |
| 53 | -Werror=ignored-qualifiers | ||
| 54 | $<$<CXX_COMPILER_ID:GNU>:-Werror=unused-but-set-parameter> | ||
| 55 | $<$<CXX_COMPILER_ID:GNU>:-Werror=unused-but-set-variable> | ||
| 56 | -Werror=unused-variable | ||
| 57 | ) | 50 | ) |
| 58 | endif() | 51 | endif() |
| 59 | 52 | ||
diff --git a/src/input_common/drivers/gc_adapter.cpp b/src/input_common/drivers/gc_adapter.cpp index f4dd24e7d..826fa2109 100644 --- a/src/input_common/drivers/gc_adapter.cpp +++ b/src/input_common/drivers/gc_adapter.cpp | |||
| @@ -324,7 +324,7 @@ bool GCAdapter::GetGCEndpoint(libusb_device* device) { | |||
| 324 | return true; | 324 | return true; |
| 325 | } | 325 | } |
| 326 | 326 | ||
| 327 | Common::Input::VibrationError GCAdapter::SetRumble( | 327 | Common::Input::VibrationError GCAdapter::SetVibration( |
| 328 | const PadIdentifier& identifier, const Common::Input::VibrationStatus& vibration) { | 328 | const PadIdentifier& identifier, const Common::Input::VibrationStatus& vibration) { |
| 329 | const auto mean_amplitude = (vibration.low_amplitude + vibration.high_amplitude) * 0.5f; | 329 | const auto mean_amplitude = (vibration.low_amplitude + vibration.high_amplitude) * 0.5f; |
| 330 | const auto processed_amplitude = | 330 | const auto processed_amplitude = |
| @@ -338,6 +338,10 @@ Common::Input::VibrationError GCAdapter::SetRumble( | |||
| 338 | return Common::Input::VibrationError::None; | 338 | return Common::Input::VibrationError::None; |
| 339 | } | 339 | } |
| 340 | 340 | ||
| 341 | bool GCAdapter::IsVibrationEnabled([[maybe_unused]] const PadIdentifier& identifier) { | ||
| 342 | return rumble_enabled; | ||
| 343 | } | ||
| 344 | |||
| 341 | void GCAdapter::UpdateVibrations() { | 345 | void GCAdapter::UpdateVibrations() { |
| 342 | // Use 8 states to keep the switching between on/off fast enough for | 346 | // Use 8 states to keep the switching between on/off fast enough for |
| 343 | // a human to feel different vibration strenght | 347 | // a human to feel different vibration strenght |
diff --git a/src/input_common/drivers/gc_adapter.h b/src/input_common/drivers/gc_adapter.h index 8682da847..7f81767f7 100644 --- a/src/input_common/drivers/gc_adapter.h +++ b/src/input_common/drivers/gc_adapter.h | |||
| @@ -25,9 +25,11 @@ public: | |||
| 25 | explicit GCAdapter(std::string input_engine_); | 25 | explicit GCAdapter(std::string input_engine_); |
| 26 | ~GCAdapter() override; | 26 | ~GCAdapter() override; |
| 27 | 27 | ||
| 28 | Common::Input::VibrationError SetRumble( | 28 | Common::Input::VibrationError SetVibration( |
| 29 | const PadIdentifier& identifier, const Common::Input::VibrationStatus& vibration) override; | 29 | const PadIdentifier& identifier, const Common::Input::VibrationStatus& vibration) override; |
| 30 | 30 | ||
| 31 | bool IsVibrationEnabled(const PadIdentifier& identifier) override; | ||
| 32 | |||
| 31 | /// Used for automapping features | 33 | /// Used for automapping features |
| 32 | std::vector<Common::ParamPackage> GetInputDevices() const override; | 34 | std::vector<Common::ParamPackage> GetInputDevices() const override; |
| 33 | ButtonMapping GetButtonMappingForDevice(const Common::ParamPackage& params) override; | 35 | ButtonMapping GetButtonMappingForDevice(const Common::ParamPackage& params) override; |
diff --git a/src/input_common/drivers/sdl_driver.cpp b/src/input_common/drivers/sdl_driver.cpp index b72e4b397..45ce588f0 100644 --- a/src/input_common/drivers/sdl_driver.cpp +++ b/src/input_common/drivers/sdl_driver.cpp | |||
| @@ -40,8 +40,8 @@ public: | |||
| 40 | void EnableMotion() { | 40 | void EnableMotion() { |
| 41 | if (sdl_controller) { | 41 | if (sdl_controller) { |
| 42 | SDL_GameController* controller = sdl_controller.get(); | 42 | SDL_GameController* controller = sdl_controller.get(); |
| 43 | has_accel = SDL_GameControllerHasSensor(controller, SDL_SENSOR_ACCEL); | 43 | has_accel = SDL_GameControllerHasSensor(controller, SDL_SENSOR_ACCEL) == SDL_TRUE; |
| 44 | has_gyro = SDL_GameControllerHasSensor(controller, SDL_SENSOR_GYRO); | 44 | has_gyro = SDL_GameControllerHasSensor(controller, SDL_SENSOR_GYRO) == SDL_TRUE; |
| 45 | if (has_accel) { | 45 | if (has_accel) { |
| 46 | SDL_GameControllerSetSensorEnabled(controller, SDL_SENSOR_ACCEL, SDL_TRUE); | 46 | SDL_GameControllerSetSensorEnabled(controller, SDL_SENSOR_ACCEL, SDL_TRUE); |
| 47 | } | 47 | } |
| @@ -114,6 +114,20 @@ public: | |||
| 114 | } | 114 | } |
| 115 | return false; | 115 | return false; |
| 116 | } | 116 | } |
| 117 | |||
| 118 | void EnableVibration(bool is_enabled) { | ||
| 119 | has_vibration = is_enabled; | ||
| 120 | is_vibration_tested = true; | ||
| 121 | } | ||
| 122 | |||
| 123 | bool HasVibration() const { | ||
| 124 | return has_vibration; | ||
| 125 | } | ||
| 126 | |||
| 127 | bool IsVibrationTested() const { | ||
| 128 | return is_vibration_tested; | ||
| 129 | } | ||
| 130 | |||
| 117 | /** | 131 | /** |
| 118 | * The Pad identifier of the joystick | 132 | * The Pad identifier of the joystick |
| 119 | */ | 133 | */ |
| @@ -236,6 +250,8 @@ private: | |||
| 236 | u64 last_motion_update{}; | 250 | u64 last_motion_update{}; |
| 237 | bool has_gyro{false}; | 251 | bool has_gyro{false}; |
| 238 | bool has_accel{false}; | 252 | bool has_accel{false}; |
| 253 | bool has_vibration{false}; | ||
| 254 | bool is_vibration_tested{false}; | ||
| 239 | BasicMotion motion; | 255 | BasicMotion motion; |
| 240 | }; | 256 | }; |
| 241 | 257 | ||
| @@ -517,7 +533,7 @@ std::vector<Common::ParamPackage> SDLDriver::GetInputDevices() const { | |||
| 517 | return devices; | 533 | return devices; |
| 518 | } | 534 | } |
| 519 | 535 | ||
| 520 | Common::Input::VibrationError SDLDriver::SetRumble( | 536 | Common::Input::VibrationError SDLDriver::SetVibration( |
| 521 | const PadIdentifier& identifier, const Common::Input::VibrationStatus& vibration) { | 537 | const PadIdentifier& identifier, const Common::Input::VibrationStatus& vibration) { |
| 522 | const auto joystick = | 538 | const auto joystick = |
| 523 | GetSDLJoystickByGUID(identifier.guid.RawString(), static_cast<int>(identifier.port)); | 539 | GetSDLJoystickByGUID(identifier.guid.RawString(), static_cast<int>(identifier.port)); |
| @@ -546,13 +562,6 @@ Common::Input::VibrationError SDLDriver::SetRumble( | |||
| 546 | .type = Common::Input::VibrationAmplificationType::Exponential, | 562 | .type = Common::Input::VibrationAmplificationType::Exponential, |
| 547 | }; | 563 | }; |
| 548 | 564 | ||
| 549 | if (vibration.type == Common::Input::VibrationAmplificationType::Test) { | ||
| 550 | if (!joystick->RumblePlay(new_vibration)) { | ||
| 551 | return Common::Input::VibrationError::Unknown; | ||
| 552 | } | ||
| 553 | return Common::Input::VibrationError::None; | ||
| 554 | } | ||
| 555 | |||
| 556 | vibration_queue.Push(VibrationRequest{ | 565 | vibration_queue.Push(VibrationRequest{ |
| 557 | .identifier = identifier, | 566 | .identifier = identifier, |
| 558 | .vibration = new_vibration, | 567 | .vibration = new_vibration, |
| @@ -561,6 +570,45 @@ Common::Input::VibrationError SDLDriver::SetRumble( | |||
| 561 | return Common::Input::VibrationError::None; | 570 | return Common::Input::VibrationError::None; |
| 562 | } | 571 | } |
| 563 | 572 | ||
| 573 | bool SDLDriver::IsVibrationEnabled(const PadIdentifier& identifier) { | ||
| 574 | const auto joystick = | ||
| 575 | GetSDLJoystickByGUID(identifier.guid.RawString(), static_cast<int>(identifier.port)); | ||
| 576 | |||
| 577 | constexpr Common::Input::VibrationStatus test_vibration{ | ||
| 578 | .low_amplitude = 1, | ||
| 579 | .low_frequency = 160.0f, | ||
| 580 | .high_amplitude = 1, | ||
| 581 | .high_frequency = 320.0f, | ||
| 582 | .type = Common::Input::VibrationAmplificationType::Exponential, | ||
| 583 | }; | ||
| 584 | |||
| 585 | constexpr Common::Input::VibrationStatus zero_vibration{ | ||
| 586 | .low_amplitude = 0, | ||
| 587 | .low_frequency = 160.0f, | ||
| 588 | .high_amplitude = 0, | ||
| 589 | .high_frequency = 320.0f, | ||
| 590 | .type = Common::Input::VibrationAmplificationType::Exponential, | ||
| 591 | }; | ||
| 592 | |||
| 593 | if (joystick->IsVibrationTested()) { | ||
| 594 | return joystick->HasVibration(); | ||
| 595 | } | ||
| 596 | |||
| 597 | // First vibration might fail | ||
| 598 | joystick->RumblePlay(test_vibration); | ||
| 599 | |||
| 600 | // Wait for about 15ms to ensure the controller is ready for the stop command | ||
| 601 | std::this_thread::sleep_for(std::chrono::milliseconds(15)); | ||
| 602 | |||
| 603 | if (!joystick->RumblePlay(zero_vibration)) { | ||
| 604 | joystick->EnableVibration(false); | ||
| 605 | return false; | ||
| 606 | } | ||
| 607 | |||
| 608 | joystick->EnableVibration(true); | ||
| 609 | return true; | ||
| 610 | } | ||
| 611 | |||
| 564 | void SDLDriver::SendVibrations() { | 612 | void SDLDriver::SendVibrations() { |
| 565 | while (!vibration_queue.Empty()) { | 613 | while (!vibration_queue.Empty()) { |
| 566 | VibrationRequest request; | 614 | VibrationRequest request; |
diff --git a/src/input_common/drivers/sdl_driver.h b/src/input_common/drivers/sdl_driver.h index fc3a44572..d1b4471cf 100644 --- a/src/input_common/drivers/sdl_driver.h +++ b/src/input_common/drivers/sdl_driver.h | |||
| @@ -61,9 +61,11 @@ public: | |||
| 61 | 61 | ||
| 62 | bool IsStickInverted(const Common::ParamPackage& params) override; | 62 | bool IsStickInverted(const Common::ParamPackage& params) override; |
| 63 | 63 | ||
| 64 | Common::Input::VibrationError SetRumble( | 64 | Common::Input::VibrationError SetVibration( |
| 65 | const PadIdentifier& identifier, const Common::Input::VibrationStatus& vibration) override; | 65 | const PadIdentifier& identifier, const Common::Input::VibrationStatus& vibration) override; |
| 66 | 66 | ||
| 67 | bool IsVibrationEnabled(const PadIdentifier& identifier) override; | ||
| 68 | |||
| 67 | private: | 69 | private: |
| 68 | struct VibrationRequest { | 70 | struct VibrationRequest { |
| 69 | PadIdentifier identifier; | 71 | PadIdentifier identifier; |
diff --git a/src/input_common/input_engine.h b/src/input_common/input_engine.h index cfbdb26bd..d4c264a8e 100644 --- a/src/input_common/input_engine.h +++ b/src/input_common/input_engine.h | |||
| @@ -108,12 +108,17 @@ public: | |||
| 108 | [[maybe_unused]] const Common::Input::LedStatus& led_status) {} | 108 | [[maybe_unused]] const Common::Input::LedStatus& led_status) {} |
| 109 | 109 | ||
| 110 | // Sets rumble to a controller | 110 | // Sets rumble to a controller |
| 111 | virtual Common::Input::VibrationError SetRumble( | 111 | virtual Common::Input::VibrationError SetVibration( |
| 112 | [[maybe_unused]] const PadIdentifier& identifier, | 112 | [[maybe_unused]] const PadIdentifier& identifier, |
| 113 | [[maybe_unused]] const Common::Input::VibrationStatus& vibration) { | 113 | [[maybe_unused]] const Common::Input::VibrationStatus& vibration) { |
| 114 | return Common::Input::VibrationError::NotSupported; | 114 | return Common::Input::VibrationError::NotSupported; |
| 115 | } | 115 | } |
| 116 | 116 | ||
| 117 | // Returns true if device supports vibrations | ||
| 118 | virtual bool IsVibrationEnabled([[maybe_unused]] const PadIdentifier& identifier) { | ||
| 119 | return false; | ||
| 120 | } | ||
| 121 | |||
| 117 | // Sets polling mode to a controller | 122 | // Sets polling mode to a controller |
| 118 | virtual Common::Input::PollingError SetPollingMode( | 123 | virtual Common::Input::PollingError SetPollingMode( |
| 119 | [[maybe_unused]] const PadIdentifier& identifier, | 124 | [[maybe_unused]] const PadIdentifier& identifier, |
diff --git a/src/input_common/input_poller.cpp b/src/input_common/input_poller.cpp index ca33fb4eb..4ac182147 100644 --- a/src/input_common/input_poller.cpp +++ b/src/input_common/input_poller.cpp | |||
| @@ -763,7 +763,11 @@ public: | |||
| 763 | 763 | ||
| 764 | Common::Input::VibrationError SetVibration( | 764 | Common::Input::VibrationError SetVibration( |
| 765 | const Common::Input::VibrationStatus& vibration_status) override { | 765 | const Common::Input::VibrationStatus& vibration_status) override { |
| 766 | return input_engine->SetRumble(identifier, vibration_status); | 766 | return input_engine->SetVibration(identifier, vibration_status); |
| 767 | } | ||
| 768 | |||
| 769 | bool IsVibrationEnabled() override { | ||
| 770 | return input_engine->IsVibrationEnabled(identifier); | ||
| 767 | } | 771 | } |
| 768 | 772 | ||
| 769 | Common::Input::PollingError SetPollingMode(Common::Input::PollingMode polling_mode) override { | 773 | Common::Input::PollingError SetPollingMode(Common::Input::PollingMode polling_mode) override { |
| @@ -797,8 +801,8 @@ std::unique_ptr<Common::Input::InputDevice> InputFactory::CreateButtonDevice( | |||
| 797 | 801 | ||
| 798 | const auto button_id = params.Get("button", 0); | 802 | const auto button_id = params.Get("button", 0); |
| 799 | const auto keyboard_key = params.Get("code", 0); | 803 | const auto keyboard_key = params.Get("code", 0); |
| 800 | const auto toggle = params.Get("toggle", false); | 804 | const auto toggle = params.Get("toggle", false) != 0; |
| 801 | const auto inverted = params.Get("inverted", false); | 805 | const auto inverted = params.Get("inverted", false) != 0; |
| 802 | input_engine->PreSetController(identifier); | 806 | input_engine->PreSetController(identifier); |
| 803 | input_engine->PreSetButton(identifier, button_id); | 807 | input_engine->PreSetButton(identifier, button_id); |
| 804 | input_engine->PreSetButton(identifier, keyboard_key); | 808 | input_engine->PreSetButton(identifier, keyboard_key); |
| @@ -820,8 +824,8 @@ std::unique_ptr<Common::Input::InputDevice> InputFactory::CreateHatButtonDevice( | |||
| 820 | 824 | ||
| 821 | const auto button_id = params.Get("hat", 0); | 825 | const auto button_id = params.Get("hat", 0); |
| 822 | const auto direction = input_engine->GetHatButtonId(params.Get("direction", "")); | 826 | const auto direction = input_engine->GetHatButtonId(params.Get("direction", "")); |
| 823 | const auto toggle = params.Get("toggle", false); | 827 | const auto toggle = params.Get("toggle", false) != 0; |
| 824 | const auto inverted = params.Get("inverted", false); | 828 | const auto inverted = params.Get("inverted", false) != 0; |
| 825 | 829 | ||
| 826 | input_engine->PreSetController(identifier); | 830 | input_engine->PreSetController(identifier); |
| 827 | input_engine->PreSetHatButton(identifier, button_id); | 831 | input_engine->PreSetHatButton(identifier, button_id); |
| @@ -879,7 +883,7 @@ std::unique_ptr<Common::Input::InputDevice> InputFactory::CreateAnalogDevice( | |||
| 879 | .threshold = std::clamp(params.Get("threshold", 0.5f), 0.0f, 1.0f), | 883 | .threshold = std::clamp(params.Get("threshold", 0.5f), 0.0f, 1.0f), |
| 880 | .offset = std::clamp(params.Get("offset", 0.0f), -1.0f, 1.0f), | 884 | .offset = std::clamp(params.Get("offset", 0.0f), -1.0f, 1.0f), |
| 881 | .inverted = params.Get("invert", "+") == "-", | 885 | .inverted = params.Get("invert", "+") == "-", |
| 882 | .toggle = static_cast<bool>(params.Get("toggle", false)), | 886 | .toggle = params.Get("toggle", false) != 0, |
| 883 | }; | 887 | }; |
| 884 | input_engine->PreSetController(identifier); | 888 | input_engine->PreSetController(identifier); |
| 885 | input_engine->PreSetAxis(identifier, axis); | 889 | input_engine->PreSetAxis(identifier, axis); |
| @@ -895,8 +899,8 @@ std::unique_ptr<Common::Input::InputDevice> InputFactory::CreateTriggerDevice( | |||
| 895 | }; | 899 | }; |
| 896 | 900 | ||
| 897 | const auto button = params.Get("button", 0); | 901 | const auto button = params.Get("button", 0); |
| 898 | const auto toggle = params.Get("toggle", false); | 902 | const auto toggle = params.Get("toggle", false) != 0; |
| 899 | const auto inverted = params.Get("inverted", false); | 903 | const auto inverted = params.Get("inverted", false) != 0; |
| 900 | 904 | ||
| 901 | const auto axis = params.Get("axis", 0); | 905 | const auto axis = params.Get("axis", 0); |
| 902 | const Common::Input::AnalogProperties properties = { | 906 | const Common::Input::AnalogProperties properties = { |
| @@ -926,8 +930,8 @@ std::unique_ptr<Common::Input::InputDevice> InputFactory::CreateTouchDevice( | |||
| 926 | }; | 930 | }; |
| 927 | 931 | ||
| 928 | const auto button = params.Get("button", 0); | 932 | const auto button = params.Get("button", 0); |
| 929 | const auto toggle = params.Get("toggle", false); | 933 | const auto toggle = params.Get("toggle", false) != 0; |
| 930 | const auto inverted = params.Get("inverted", false); | 934 | const auto inverted = params.Get("inverted", false) != 0; |
| 931 | 935 | ||
| 932 | const auto axis_x = params.Get("axis_x", 0); | 936 | const auto axis_x = params.Get("axis_x", 0); |
| 933 | const Common::Input::AnalogProperties properties_x = { | 937 | const Common::Input::AnalogProperties properties_x = { |
diff --git a/src/shader_recompiler/CMakeLists.txt b/src/shader_recompiler/CMakeLists.txt index af8e51fe8..bcdd60db9 100644 --- a/src/shader_recompiler/CMakeLists.txt +++ b/src/shader_recompiler/CMakeLists.txt | |||
| @@ -241,24 +241,14 @@ target_link_libraries(shader_recompiler PUBLIC common fmt::fmt sirit) | |||
| 241 | if (MSVC) | 241 | if (MSVC) |
| 242 | target_compile_options(shader_recompiler PRIVATE | 242 | target_compile_options(shader_recompiler PRIVATE |
| 243 | /W4 | 243 | /W4 |
| 244 | /WX | 244 | |
| 245 | /we4018 # 'expression' : signed/unsigned mismatch | 245 | /we4242 # 'identifier': conversion from 'type1' to 'type2', possible loss of data |
| 246 | /we4244 # 'argument' : conversion from 'type1' to 'type2', possible loss of data (floating-point) | ||
| 247 | /we4245 # 'conversion' : conversion from 'type1' to 'type2', signed/unsigned mismatch | ||
| 248 | /we4254 # 'operator': conversion from 'type1:field_bits' to 'type2:field_bits', possible loss of data | 246 | /we4254 # 'operator': conversion from 'type1:field_bits' to 'type2:field_bits', possible loss of data |
| 249 | /we4267 # 'var' : conversion from 'size_t' to 'type', possible loss of data | ||
| 250 | /we4305 # 'context' : truncation from 'type1' to 'type2' | ||
| 251 | /we4800 # Implicit conversion from 'type' to bool. Possible information loss | 247 | /we4800 # Implicit conversion from 'type' to bool. Possible information loss |
| 252 | /we4826 # Conversion from 'type1' to 'type2' is sign-extended. This may cause unexpected runtime behavior. | ||
| 253 | ) | 248 | ) |
| 254 | else() | 249 | else() |
| 255 | target_compile_options(shader_recompiler PRIVATE | 250 | target_compile_options(shader_recompiler PRIVATE |
| 256 | -Werror | ||
| 257 | -Werror=conversion | 251 | -Werror=conversion |
| 258 | -Werror=ignored-qualifiers | ||
| 259 | $<$<CXX_COMPILER_ID:GNU>:-Werror=unused-but-set-parameter> | ||
| 260 | $<$<CXX_COMPILER_ID:GNU>:-Werror=unused-but-set-variable> | ||
| 261 | -Werror=unused-variable | ||
| 262 | 252 | ||
| 263 | # Bracket depth determines maximum size of a fold expression in Clang since 9c9974c3ccb6. | 253 | # Bracket depth determines maximum size of a fold expression in Clang since 9c9974c3ccb6. |
| 264 | # And this in turns limits the size of a std::array. | 254 | # And this in turns limits the size of a std::array. |
diff --git a/src/shader_recompiler/backend/glasm/emit_glasm_not_implemented.cpp b/src/shader_recompiler/backend/glasm/emit_glasm_not_implemented.cpp index 7094d8e42..1f4ffdd62 100644 --- a/src/shader_recompiler/backend/glasm/emit_glasm_not_implemented.cpp +++ b/src/shader_recompiler/backend/glasm/emit_glasm_not_implemented.cpp | |||
| @@ -5,10 +5,6 @@ | |||
| 5 | #include "shader_recompiler/backend/glasm/glasm_emit_context.h" | 5 | #include "shader_recompiler/backend/glasm/glasm_emit_context.h" |
| 6 | #include "shader_recompiler/frontend/ir/value.h" | 6 | #include "shader_recompiler/frontend/ir/value.h" |
| 7 | 7 | ||
| 8 | #ifdef _MSC_VER | ||
| 9 | #pragma warning(disable : 4100) | ||
| 10 | #endif | ||
| 11 | |||
| 12 | namespace Shader::Backend::GLASM { | 8 | namespace Shader::Backend::GLASM { |
| 13 | 9 | ||
| 14 | #define NotImplemented() throw NotImplementedException("GLASM instruction {}", __LINE__) | 10 | #define NotImplemented() throw NotImplementedException("GLASM instruction {}", __LINE__) |
diff --git a/src/shader_recompiler/backend/glsl/emit_glsl_not_implemented.cpp b/src/shader_recompiler/backend/glsl/emit_glsl_not_implemented.cpp index b03a8ba1e..9f1ed95a4 100644 --- a/src/shader_recompiler/backend/glsl/emit_glsl_not_implemented.cpp +++ b/src/shader_recompiler/backend/glsl/emit_glsl_not_implemented.cpp | |||
| @@ -7,10 +7,6 @@ | |||
| 7 | #include "shader_recompiler/backend/glsl/glsl_emit_context.h" | 7 | #include "shader_recompiler/backend/glsl/glsl_emit_context.h" |
| 8 | #include "shader_recompiler/frontend/ir/value.h" | 8 | #include "shader_recompiler/frontend/ir/value.h" |
| 9 | 9 | ||
| 10 | #ifdef _MSC_VER | ||
| 11 | #pragma warning(disable : 4100) | ||
| 12 | #endif | ||
| 13 | |||
| 14 | namespace Shader::Backend::GLSL { | 10 | namespace Shader::Backend::GLSL { |
| 15 | 11 | ||
| 16 | void EmitGetRegister(EmitContext& ctx) { | 12 | void EmitGetRegister(EmitContext& ctx) { |
diff --git a/src/shader_recompiler/frontend/ir/microinstruction.cpp b/src/shader_recompiler/frontend/ir/microinstruction.cpp index 468782eb1..84417980b 100644 --- a/src/shader_recompiler/frontend/ir/microinstruction.cpp +++ b/src/shader_recompiler/frontend/ir/microinstruction.cpp | |||
| @@ -325,11 +325,6 @@ void Inst::AddPhiOperand(Block* predecessor, const Value& value) { | |||
| 325 | phi_args.emplace_back(predecessor, value); | 325 | phi_args.emplace_back(predecessor, value); |
| 326 | } | 326 | } |
| 327 | 327 | ||
| 328 | void Inst::ErasePhiOperand(size_t index) { | ||
| 329 | const auto operand_it{phi_args.begin() + static_cast<ptrdiff_t>(index)}; | ||
| 330 | phi_args.erase(operand_it); | ||
| 331 | } | ||
| 332 | |||
| 333 | void Inst::OrderPhiArgs() { | 328 | void Inst::OrderPhiArgs() { |
| 334 | if (op != Opcode::Phi) { | 329 | if (op != Opcode::Phi) { |
| 335 | throw LogicError("{} is not a Phi instruction", op); | 330 | throw LogicError("{} is not a Phi instruction", op); |
diff --git a/src/shader_recompiler/frontend/ir/value.h b/src/shader_recompiler/frontend/ir/value.h index 1a2e4ccb6..6a673ca05 100644 --- a/src/shader_recompiler/frontend/ir/value.h +++ b/src/shader_recompiler/frontend/ir/value.h | |||
| @@ -178,13 +178,9 @@ public: | |||
| 178 | 178 | ||
| 179 | /// Get a pointer to the block of a phi argument. | 179 | /// Get a pointer to the block of a phi argument. |
| 180 | [[nodiscard]] Block* PhiBlock(size_t index) const; | 180 | [[nodiscard]] Block* PhiBlock(size_t index) const; |
| 181 | |||
| 182 | /// Add phi operand to a phi instruction. | 181 | /// Add phi operand to a phi instruction. |
| 183 | void AddPhiOperand(Block* predecessor, const Value& value); | 182 | void AddPhiOperand(Block* predecessor, const Value& value); |
| 184 | 183 | ||
| 185 | // Erase the phi operand at the given index. | ||
| 186 | void ErasePhiOperand(size_t index); | ||
| 187 | |||
| 188 | /// Orders the Phi arguments from farthest away to nearest. | 184 | /// Orders the Phi arguments from farthest away to nearest. |
| 189 | void OrderPhiArgs(); | 185 | void OrderPhiArgs(); |
| 190 | 186 | ||
diff --git a/src/shader_recompiler/frontend/maxwell/translate_program.cpp b/src/shader_recompiler/frontend/maxwell/translate_program.cpp index 77efb4f57..b58741d4d 100644 --- a/src/shader_recompiler/frontend/maxwell/translate_program.cpp +++ b/src/shader_recompiler/frontend/maxwell/translate_program.cpp | |||
| @@ -137,28 +137,35 @@ bool IsLegacyAttribute(IR::Attribute attribute) { | |||
| 137 | } | 137 | } |
| 138 | 138 | ||
| 139 | std::map<IR::Attribute, IR::Attribute> GenerateLegacyToGenericMappings( | 139 | std::map<IR::Attribute, IR::Attribute> GenerateLegacyToGenericMappings( |
| 140 | const VaryingState& state, std::queue<IR::Attribute> ununsed_generics) { | 140 | const VaryingState& state, std::queue<IR::Attribute> unused_generics, |
| 141 | const std::map<IR::Attribute, IR::Attribute>& previous_stage_mapping) { | ||
| 141 | std::map<IR::Attribute, IR::Attribute> mapping; | 142 | std::map<IR::Attribute, IR::Attribute> mapping; |
| 143 | auto update_mapping = [&mapping, &unused_generics, previous_stage_mapping](IR::Attribute attr, | ||
| 144 | size_t count) { | ||
| 145 | if (previous_stage_mapping.find(attr) != previous_stage_mapping.end()) { | ||
| 146 | for (size_t i = 0; i < count; ++i) { | ||
| 147 | mapping.insert({attr + i, previous_stage_mapping.at(attr + i)}); | ||
| 148 | } | ||
| 149 | } else { | ||
| 150 | for (size_t i = 0; i < count; ++i) { | ||
| 151 | mapping.insert({attr + i, unused_generics.front() + i}); | ||
| 152 | } | ||
| 153 | unused_generics.pop(); | ||
| 154 | } | ||
| 155 | }; | ||
| 142 | for (size_t index = 0; index < 4; ++index) { | 156 | for (size_t index = 0; index < 4; ++index) { |
| 143 | auto attr = IR::Attribute::ColorFrontDiffuseR + index * 4; | 157 | auto attr = IR::Attribute::ColorFrontDiffuseR + index * 4; |
| 144 | if (state.AnyComponent(attr)) { | 158 | if (state.AnyComponent(attr)) { |
| 145 | for (size_t i = 0; i < 4; ++i) { | 159 | update_mapping(attr, 4); |
| 146 | mapping.insert({attr + i, ununsed_generics.front() + i}); | ||
| 147 | } | ||
| 148 | ununsed_generics.pop(); | ||
| 149 | } | 160 | } |
| 150 | } | 161 | } |
| 151 | if (state[IR::Attribute::FogCoordinate]) { | 162 | if (state[IR::Attribute::FogCoordinate]) { |
| 152 | mapping.insert({IR::Attribute::FogCoordinate, ununsed_generics.front()}); | 163 | update_mapping(IR::Attribute::FogCoordinate, 1); |
| 153 | ununsed_generics.pop(); | ||
| 154 | } | 164 | } |
| 155 | for (size_t index = 0; index < IR::NUM_FIXEDFNCTEXTURE; ++index) { | 165 | for (size_t index = 0; index < IR::NUM_FIXEDFNCTEXTURE; ++index) { |
| 156 | auto attr = IR::Attribute::FixedFncTexture0S + index * 4; | 166 | auto attr = IR::Attribute::FixedFncTexture0S + index * 4; |
| 157 | if (state.AnyComponent(attr)) { | 167 | if (state.AnyComponent(attr)) { |
| 158 | for (size_t i = 0; i < 4; ++i) { | 168 | update_mapping(attr, 4); |
| 159 | mapping.insert({attr + i, ununsed_generics.front() + i}); | ||
| 160 | } | ||
| 161 | ununsed_generics.pop(); | ||
| 162 | } | 169 | } |
| 163 | } | 170 | } |
| 164 | return mapping; | 171 | return mapping; |
| @@ -265,21 +272,22 @@ IR::Program MergeDualVertexPrograms(IR::Program& vertex_a, IR::Program& vertex_b | |||
| 265 | void ConvertLegacyToGeneric(IR::Program& program, const Shader::RuntimeInfo& runtime_info) { | 272 | void ConvertLegacyToGeneric(IR::Program& program, const Shader::RuntimeInfo& runtime_info) { |
| 266 | auto& stores = program.info.stores; | 273 | auto& stores = program.info.stores; |
| 267 | if (stores.Legacy()) { | 274 | if (stores.Legacy()) { |
| 268 | std::queue<IR::Attribute> ununsed_output_generics{}; | 275 | std::queue<IR::Attribute> unused_output_generics{}; |
| 269 | for (size_t index = 0; index < IR::NUM_GENERICS; ++index) { | 276 | for (size_t index = 0; index < IR::NUM_GENERICS; ++index) { |
| 270 | if (!stores.Generic(index)) { | 277 | if (!stores.Generic(index)) { |
| 271 | ununsed_output_generics.push(IR::Attribute::Generic0X + index * 4); | 278 | unused_output_generics.push(IR::Attribute::Generic0X + index * 4); |
| 272 | } | 279 | } |
| 273 | } | 280 | } |
| 274 | auto mappings = GenerateLegacyToGenericMappings(stores, ununsed_output_generics); | 281 | program.info.legacy_stores_mapping = |
| 282 | GenerateLegacyToGenericMappings(stores, unused_output_generics, {}); | ||
| 275 | for (IR::Block* const block : program.post_order_blocks) { | 283 | for (IR::Block* const block : program.post_order_blocks) { |
| 276 | for (IR::Inst& inst : block->Instructions()) { | 284 | for (IR::Inst& inst : block->Instructions()) { |
| 277 | switch (inst.GetOpcode()) { | 285 | switch (inst.GetOpcode()) { |
| 278 | case IR::Opcode::SetAttribute: { | 286 | case IR::Opcode::SetAttribute: { |
| 279 | const auto attr = inst.Arg(0).Attribute(); | 287 | const auto attr = inst.Arg(0).Attribute(); |
| 280 | if (IsLegacyAttribute(attr)) { | 288 | if (IsLegacyAttribute(attr)) { |
| 281 | stores.Set(mappings[attr], true); | 289 | stores.Set(program.info.legacy_stores_mapping[attr], true); |
| 282 | inst.SetArg(0, Shader::IR::Value(mappings[attr])); | 290 | inst.SetArg(0, Shader::IR::Value(program.info.legacy_stores_mapping[attr])); |
| 283 | } | 291 | } |
| 284 | break; | 292 | break; |
| 285 | } | 293 | } |
| @@ -292,15 +300,16 @@ void ConvertLegacyToGeneric(IR::Program& program, const Shader::RuntimeInfo& run | |||
| 292 | 300 | ||
| 293 | auto& loads = program.info.loads; | 301 | auto& loads = program.info.loads; |
| 294 | if (loads.Legacy()) { | 302 | if (loads.Legacy()) { |
| 295 | std::queue<IR::Attribute> ununsed_input_generics{}; | 303 | std::queue<IR::Attribute> unused_input_generics{}; |
| 296 | for (size_t index = 0; index < IR::NUM_GENERICS; ++index) { | 304 | for (size_t index = 0; index < IR::NUM_GENERICS; ++index) { |
| 297 | const AttributeType input_type{runtime_info.generic_input_types[index]}; | 305 | const AttributeType input_type{runtime_info.generic_input_types[index]}; |
| 298 | if (!runtime_info.previous_stage_stores.Generic(index) || !loads.Generic(index) || | 306 | if (!runtime_info.previous_stage_stores.Generic(index) || !loads.Generic(index) || |
| 299 | input_type == AttributeType::Disabled) { | 307 | input_type == AttributeType::Disabled) { |
| 300 | ununsed_input_generics.push(IR::Attribute::Generic0X + index * 4); | 308 | unused_input_generics.push(IR::Attribute::Generic0X + index * 4); |
| 301 | } | 309 | } |
| 302 | } | 310 | } |
| 303 | auto mappings = GenerateLegacyToGenericMappings(loads, ununsed_input_generics); | 311 | auto mappings = GenerateLegacyToGenericMappings( |
| 312 | loads, unused_input_generics, runtime_info.previous_stage_legacy_stores_mapping); | ||
| 304 | for (IR::Block* const block : program.post_order_blocks) { | 313 | for (IR::Block* const block : program.post_order_blocks) { |
| 305 | for (IR::Inst& inst : block->Instructions()) { | 314 | for (IR::Inst& inst : block->Instructions()) { |
| 306 | switch (inst.GetOpcode()) { | 315 | switch (inst.GetOpcode()) { |
diff --git a/src/shader_recompiler/ir_opt/dead_code_elimination_pass.cpp b/src/shader_recompiler/ir_opt/dead_code_elimination_pass.cpp index 9a7d47344..1bd8afd6f 100644 --- a/src/shader_recompiler/ir_opt/dead_code_elimination_pass.cpp +++ b/src/shader_recompiler/ir_opt/dead_code_elimination_pass.cpp | |||
| @@ -1,104 +1,24 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project | 1 | // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project |
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | 2 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 3 | 3 | ||
| 4 | #include <algorithm> | ||
| 5 | |||
| 6 | #include <boost/container/small_vector.hpp> | ||
| 7 | |||
| 8 | #include "shader_recompiler/frontend/ir/basic_block.h" | 4 | #include "shader_recompiler/frontend/ir/basic_block.h" |
| 9 | #include "shader_recompiler/frontend/ir/value.h" | 5 | #include "shader_recompiler/frontend/ir/value.h" |
| 10 | #include "shader_recompiler/ir_opt/passes.h" | 6 | #include "shader_recompiler/ir_opt/passes.h" |
| 11 | 7 | ||
| 12 | namespace Shader::Optimization { | 8 | namespace Shader::Optimization { |
| 13 | namespace { | 9 | |
| 14 | template <bool TEST_USES> | 10 | void DeadCodeEliminationPass(IR::Program& program) { |
| 15 | void DeadInstElimination(IR::Block* const block) { | ||
| 16 | // We iterate over the instructions in reverse order. | 11 | // We iterate over the instructions in reverse order. |
| 17 | // This is because removing an instruction reduces the number of uses for earlier instructions. | 12 | // This is because removing an instruction reduces the number of uses for earlier instructions. |
| 18 | auto it{block->end()}; | 13 | for (IR::Block* const block : program.post_order_blocks) { |
| 19 | while (it != block->begin()) { | 14 | auto it{block->end()}; |
| 20 | --it; | 15 | while (it != block->begin()) { |
| 21 | if constexpr (TEST_USES) { | 16 | --it; |
| 22 | if (it->HasUses() || it->MayHaveSideEffects()) { | 17 | if (!it->HasUses() && !it->MayHaveSideEffects()) { |
| 23 | continue; | 18 | it->Invalidate(); |
| 24 | } | 19 | it = block->Instructions().erase(it); |
| 25 | } | ||
| 26 | it->Invalidate(); | ||
| 27 | it = block->Instructions().erase(it); | ||
| 28 | } | ||
| 29 | } | ||
| 30 | |||
| 31 | void DeletedPhiArgElimination(IR::Program& program, std::span<const IR::Block*> dead_blocks) { | ||
| 32 | for (IR::Block* const block : program.blocks) { | ||
| 33 | for (IR::Inst& phi : *block) { | ||
| 34 | if (!IR::IsPhi(phi)) { | ||
| 35 | continue; | ||
| 36 | } | ||
| 37 | for (size_t i = 0; i < phi.NumArgs(); ++i) { | ||
| 38 | if (std::ranges::find(dead_blocks, phi.PhiBlock(i)) == dead_blocks.end()) { | ||
| 39 | continue; | ||
| 40 | } | ||
| 41 | // Phi operand at this index is an unreachable block | ||
| 42 | phi.ErasePhiOperand(i); | ||
| 43 | --i; | ||
| 44 | } | ||
| 45 | } | ||
| 46 | } | ||
| 47 | } | ||
| 48 | |||
| 49 | void DeadBranchElimination(IR::Program& program) { | ||
| 50 | boost::container::small_vector<const IR::Block*, 3> dead_blocks; | ||
| 51 | const auto begin_it{program.syntax_list.begin()}; | ||
| 52 | for (auto node_it = begin_it; node_it != program.syntax_list.end(); ++node_it) { | ||
| 53 | if (node_it->type != IR::AbstractSyntaxNode::Type::If) { | ||
| 54 | continue; | ||
| 55 | } | ||
| 56 | IR::Inst* const cond_ref{node_it->data.if_node.cond.Inst()}; | ||
| 57 | const IR::U1 cond{cond_ref->Arg(0)}; | ||
| 58 | if (!cond.IsImmediate()) { | ||
| 59 | continue; | ||
| 60 | } | ||
| 61 | if (cond.U1()) { | ||
| 62 | continue; | ||
| 63 | } | ||
| 64 | // False immediate condition. Remove condition ref, erase the entire branch. | ||
| 65 | cond_ref->Invalidate(); | ||
| 66 | // Account for nested if-statements within the if(false) branch | ||
| 67 | u32 nested_ifs{1u}; | ||
| 68 | while (node_it->type != IR::AbstractSyntaxNode::Type::EndIf || nested_ifs > 0) { | ||
| 69 | node_it = program.syntax_list.erase(node_it); | ||
| 70 | switch (node_it->type) { | ||
| 71 | case IR::AbstractSyntaxNode::Type::If: | ||
| 72 | ++nested_ifs; | ||
| 73 | break; | ||
| 74 | case IR::AbstractSyntaxNode::Type::EndIf: | ||
| 75 | --nested_ifs; | ||
| 76 | break; | ||
| 77 | case IR::AbstractSyntaxNode::Type::Block: { | ||
| 78 | IR::Block* const block{node_it->data.block}; | ||
| 79 | DeadInstElimination<false>(block); | ||
| 80 | dead_blocks.push_back(block); | ||
| 81 | break; | ||
| 82 | } | ||
| 83 | default: | ||
| 84 | break; | ||
| 85 | } | 20 | } |
| 86 | } | 21 | } |
| 87 | // Erase EndIf node of the if(false) branch | ||
| 88 | node_it = program.syntax_list.erase(node_it); | ||
| 89 | // Account for loop increment | ||
| 90 | --node_it; | ||
| 91 | } | ||
| 92 | if (!dead_blocks.empty()) { | ||
| 93 | DeletedPhiArgElimination(program, std::span(dead_blocks.data(), dead_blocks.size())); | ||
| 94 | } | ||
| 95 | } | ||
| 96 | } // namespace | ||
| 97 | |||
| 98 | void DeadCodeEliminationPass(IR::Program& program) { | ||
| 99 | DeadBranchElimination(program); | ||
| 100 | for (IR::Block* const block : program.post_order_blocks) { | ||
| 101 | DeadInstElimination<true>(block); | ||
| 102 | } | 22 | } |
| 103 | } | 23 | } |
| 104 | 24 | ||
diff --git a/src/shader_recompiler/runtime_info.h b/src/shader_recompiler/runtime_info.h index dcb5ab158..549b81ef7 100644 --- a/src/shader_recompiler/runtime_info.h +++ b/src/shader_recompiler/runtime_info.h | |||
| @@ -4,6 +4,7 @@ | |||
| 4 | #pragma once | 4 | #pragma once |
| 5 | 5 | ||
| 6 | #include <array> | 6 | #include <array> |
| 7 | #include <map> | ||
| 7 | #include <optional> | 8 | #include <optional> |
| 8 | #include <vector> | 9 | #include <vector> |
| 9 | 10 | ||
| @@ -60,6 +61,7 @@ struct TransformFeedbackVarying { | |||
| 60 | struct RuntimeInfo { | 61 | struct RuntimeInfo { |
| 61 | std::array<AttributeType, 32> generic_input_types{}; | 62 | std::array<AttributeType, 32> generic_input_types{}; |
| 62 | VaryingState previous_stage_stores; | 63 | VaryingState previous_stage_stores; |
| 64 | std::map<IR::Attribute, IR::Attribute> previous_stage_legacy_stores_mapping; | ||
| 63 | 65 | ||
| 64 | bool convert_depth_mode{}; | 66 | bool convert_depth_mode{}; |
| 65 | bool force_early_z{}; | 67 | bool force_early_z{}; |
diff --git a/src/shader_recompiler/shader_info.h b/src/shader_recompiler/shader_info.h index cc596da4f..81097bf1a 100644 --- a/src/shader_recompiler/shader_info.h +++ b/src/shader_recompiler/shader_info.h | |||
| @@ -5,6 +5,7 @@ | |||
| 5 | 5 | ||
| 6 | #include <array> | 6 | #include <array> |
| 7 | #include <bitset> | 7 | #include <bitset> |
| 8 | #include <map> | ||
| 8 | 9 | ||
| 9 | #include "common/common_types.h" | 10 | #include "common/common_types.h" |
| 10 | #include "shader_recompiler/frontend/ir/type.h" | 11 | #include "shader_recompiler/frontend/ir/type.h" |
| @@ -127,6 +128,8 @@ struct Info { | |||
| 127 | VaryingState stores; | 128 | VaryingState stores; |
| 128 | VaryingState passthrough; | 129 | VaryingState passthrough; |
| 129 | 130 | ||
| 131 | std::map<IR::Attribute, IR::Attribute> legacy_stores_mapping; | ||
| 132 | |||
| 130 | bool loads_indexed_attributes{}; | 133 | bool loads_indexed_attributes{}; |
| 131 | 134 | ||
| 132 | std::array<bool, 8> stores_frag_color{}; | 135 | std::array<bool, 8> stores_frag_color{}; |
diff --git a/src/tests/video_core/buffer_base.cpp b/src/tests/video_core/buffer_base.cpp index 71121e42a..f7236afab 100644 --- a/src/tests/video_core/buffer_base.cpp +++ b/src/tests/video_core/buffer_base.cpp | |||
| @@ -44,7 +44,7 @@ public: | |||
| 44 | 44 | ||
| 45 | [[nodiscard]] unsigned Count() const noexcept { | 45 | [[nodiscard]] unsigned Count() const noexcept { |
| 46 | unsigned count = 0; | 46 | unsigned count = 0; |
| 47 | for (const auto [index, value] : page_table) { | 47 | for (const auto& [index, value] : page_table) { |
| 48 | count += value; | 48 | count += value; |
| 49 | } | 49 | } |
| 50 | return count; | 50 | return count; |
diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt index cb8b46edf..106991969 100644 --- a/src/video_core/CMakeLists.txt +++ b/src/video_core/CMakeLists.txt | |||
| @@ -279,14 +279,8 @@ if (MSVC) | |||
| 279 | else() | 279 | else() |
| 280 | target_compile_options(video_core PRIVATE | 280 | target_compile_options(video_core PRIVATE |
| 281 | -Werror=conversion | 281 | -Werror=conversion |
| 282 | -Wno-error=sign-conversion | ||
| 283 | -Werror=pessimizing-move | ||
| 284 | -Werror=redundant-move | ||
| 285 | -Werror=type-limits | ||
| 286 | 282 | ||
| 287 | $<$<CXX_COMPILER_ID:GNU>:-Werror=class-memaccess> | 283 | -Wno-sign-conversion |
| 288 | $<$<CXX_COMPILER_ID:GNU>:-Werror=unused-but-set-parameter> | ||
| 289 | $<$<CXX_COMPILER_ID:GNU>:-Werror=unused-but-set-variable> | ||
| 290 | ) | 284 | ) |
| 291 | endif() | 285 | endif() |
| 292 | 286 | ||
diff --git a/src/video_core/engines/maxwell_3d.cpp b/src/video_core/engines/maxwell_3d.cpp index 89a9d1f5a..f9794dfe4 100644 --- a/src/video_core/engines/maxwell_3d.cpp +++ b/src/video_core/engines/maxwell_3d.cpp | |||
| @@ -117,10 +117,15 @@ void Maxwell3D::InitializeRegisterDefaults() { | |||
| 117 | 117 | ||
| 118 | shadow_state = regs; | 118 | shadow_state = regs; |
| 119 | 119 | ||
| 120 | mme_inline[MAXWELL3D_REG_INDEX(draw.end)] = true; | 120 | draw_command[MAXWELL3D_REG_INDEX(draw.end)] = true; |
| 121 | mme_inline[MAXWELL3D_REG_INDEX(draw.begin)] = true; | 121 | draw_command[MAXWELL3D_REG_INDEX(draw.begin)] = true; |
| 122 | mme_inline[MAXWELL3D_REG_INDEX(vertex_buffer.count)] = true; | 122 | draw_command[MAXWELL3D_REG_INDEX(vertex_buffer.first)] = true; |
| 123 | mme_inline[MAXWELL3D_REG_INDEX(index_buffer.count)] = true; | 123 | draw_command[MAXWELL3D_REG_INDEX(vertex_buffer.count)] = true; |
| 124 | draw_command[MAXWELL3D_REG_INDEX(index_buffer.first)] = true; | ||
| 125 | draw_command[MAXWELL3D_REG_INDEX(index_buffer.count)] = true; | ||
| 126 | draw_command[MAXWELL3D_REG_INDEX(draw_inline_index)] = true; | ||
| 127 | draw_command[MAXWELL3D_REG_INDEX(inline_index_2x16.even)] = true; | ||
| 128 | draw_command[MAXWELL3D_REG_INDEX(inline_index_4x8.index0)] = true; | ||
| 124 | } | 129 | } |
| 125 | 130 | ||
| 126 | void Maxwell3D::ProcessMacro(u32 method, const u32* base_start, u32 amount, bool is_last_call) { | 131 | void Maxwell3D::ProcessMacro(u32 method, const u32* base_start, u32 amount, bool is_last_call) { |
| @@ -208,25 +213,21 @@ void Maxwell3D::ProcessMethodCall(u32 method, u32 argument, u32 nonshadow_argume | |||
| 208 | return ProcessCBBind(3); | 213 | return ProcessCBBind(3); |
| 209 | case MAXWELL3D_REG_INDEX(bind_groups[4].raw_config): | 214 | case MAXWELL3D_REG_INDEX(bind_groups[4].raw_config): |
| 210 | return ProcessCBBind(4); | 215 | return ProcessCBBind(4); |
| 211 | case MAXWELL3D_REG_INDEX(draw.end): | ||
| 212 | return DrawArrays(); | ||
| 213 | case MAXWELL3D_REG_INDEX(index_buffer32_first): | 216 | case MAXWELL3D_REG_INDEX(index_buffer32_first): |
| 214 | regs.index_buffer.count = regs.index_buffer32_first.count; | 217 | regs.index_buffer.count = regs.index_buffer32_first.count; |
| 215 | regs.index_buffer.first = regs.index_buffer32_first.first; | 218 | regs.index_buffer.first = regs.index_buffer32_first.first; |
| 216 | dirty.flags[VideoCommon::Dirty::IndexBuffer] = true; | 219 | dirty.flags[VideoCommon::Dirty::IndexBuffer] = true; |
| 217 | return DrawArrays(); | 220 | return ProcessDraw(); |
| 218 | case MAXWELL3D_REG_INDEX(index_buffer16_first): | 221 | case MAXWELL3D_REG_INDEX(index_buffer16_first): |
| 219 | regs.index_buffer.count = regs.index_buffer16_first.count; | 222 | regs.index_buffer.count = regs.index_buffer16_first.count; |
| 220 | regs.index_buffer.first = regs.index_buffer16_first.first; | 223 | regs.index_buffer.first = regs.index_buffer16_first.first; |
| 221 | dirty.flags[VideoCommon::Dirty::IndexBuffer] = true; | 224 | dirty.flags[VideoCommon::Dirty::IndexBuffer] = true; |
| 222 | return DrawArrays(); | 225 | return ProcessDraw(); |
| 223 | case MAXWELL3D_REG_INDEX(index_buffer8_first): | 226 | case MAXWELL3D_REG_INDEX(index_buffer8_first): |
| 224 | regs.index_buffer.count = regs.index_buffer8_first.count; | 227 | regs.index_buffer.count = regs.index_buffer8_first.count; |
| 225 | regs.index_buffer.first = regs.index_buffer8_first.first; | 228 | regs.index_buffer.first = regs.index_buffer8_first.first; |
| 226 | dirty.flags[VideoCommon::Dirty::IndexBuffer] = true; | 229 | dirty.flags[VideoCommon::Dirty::IndexBuffer] = true; |
| 227 | // a macro calls this one over and over, should it increase instancing? | 230 | return ProcessDraw(); |
| 228 | // Used by Hades and likely other Vulkan games. | ||
| 229 | return DrawArrays(); | ||
| 230 | case MAXWELL3D_REG_INDEX(topology_override): | 231 | case MAXWELL3D_REG_INDEX(topology_override): |
| 231 | use_topology_override = true; | 232 | use_topology_override = true; |
| 232 | return; | 233 | return; |
| @@ -261,14 +262,13 @@ void Maxwell3D::CallMacroMethod(u32 method, const std::vector<u32>& parameters) | |||
| 261 | 262 | ||
| 262 | // Execute the current macro. | 263 | // Execute the current macro. |
| 263 | macro_engine->Execute(macro_positions[entry], parameters); | 264 | macro_engine->Execute(macro_positions[entry], parameters); |
| 264 | if (mme_draw.current_mode != MMEDrawMode::Undefined) { | 265 | |
| 265 | FlushMMEInlineDraw(); | 266 | ProcessDeferredDraw(); |
| 266 | } | ||
| 267 | } | 267 | } |
| 268 | 268 | ||
| 269 | void Maxwell3D::CallMethod(u32 method, u32 method_argument, bool is_last_call) { | 269 | void Maxwell3D::CallMethod(u32 method, u32 method_argument, bool is_last_call) { |
| 270 | // It is an error to write to a register other than the current macro's ARG register before it | 270 | // It is an error to write to a register other than the current macro's ARG register before |
| 271 | // has finished execution. | 271 | // it has finished execution. |
| 272 | if (executing_macro != 0) { | 272 | if (executing_macro != 0) { |
| 273 | ASSERT(method == executing_macro + 1); | 273 | ASSERT(method == executing_macro + 1); |
| 274 | } | 274 | } |
| @@ -283,9 +283,33 @@ void Maxwell3D::CallMethod(u32 method, u32 method_argument, bool is_last_call) { | |||
| 283 | ASSERT_MSG(method < Regs::NUM_REGS, | 283 | ASSERT_MSG(method < Regs::NUM_REGS, |
| 284 | "Invalid Maxwell3D register, increase the size of the Regs structure"); | 284 | "Invalid Maxwell3D register, increase the size of the Regs structure"); |
| 285 | 285 | ||
| 286 | const u32 argument = ProcessShadowRam(method, method_argument); | 286 | if (draw_command[method]) { |
| 287 | ProcessDirtyRegisters(method, argument); | 287 | regs.reg_array[method] = method_argument; |
| 288 | ProcessMethodCall(method, argument, method_argument, is_last_call); | 288 | deferred_draw_method.push_back(method); |
| 289 | auto u32_to_u8 = [&](const u32 argument) { | ||
| 290 | inline_index_draw_indexes.push_back(static_cast<u8>(argument & 0x000000ff)); | ||
| 291 | inline_index_draw_indexes.push_back(static_cast<u8>((argument & 0x0000ff00) >> 8)); | ||
| 292 | inline_index_draw_indexes.push_back(static_cast<u8>((argument & 0x00ff0000) >> 16)); | ||
| 293 | inline_index_draw_indexes.push_back(static_cast<u8>((argument & 0xff000000) >> 24)); | ||
| 294 | }; | ||
| 295 | if (MAXWELL3D_REG_INDEX(draw_inline_index) == method) { | ||
| 296 | u32_to_u8(method_argument); | ||
| 297 | } else if (MAXWELL3D_REG_INDEX(inline_index_2x16.even) == method) { | ||
| 298 | u32_to_u8(regs.inline_index_2x16.even); | ||
| 299 | u32_to_u8(regs.inline_index_2x16.odd); | ||
| 300 | } else if (MAXWELL3D_REG_INDEX(inline_index_4x8.index0) == method) { | ||
| 301 | u32_to_u8(regs.inline_index_4x8.index0); | ||
| 302 | u32_to_u8(regs.inline_index_4x8.index1); | ||
| 303 | u32_to_u8(regs.inline_index_4x8.index2); | ||
| 304 | u32_to_u8(regs.inline_index_4x8.index3); | ||
| 305 | } | ||
| 306 | } else { | ||
| 307 | ProcessDeferredDraw(); | ||
| 308 | |||
| 309 | const u32 argument = ProcessShadowRam(method, method_argument); | ||
| 310 | ProcessDirtyRegisters(method, argument); | ||
| 311 | ProcessMethodCall(method, argument, method_argument, is_last_call); | ||
| 312 | } | ||
| 289 | } | 313 | } |
| 290 | 314 | ||
| 291 | void Maxwell3D::CallMultiMethod(u32 method, const u32* base_start, u32 amount, | 315 | void Maxwell3D::CallMultiMethod(u32 method, const u32* base_start, u32 amount, |
| @@ -326,55 +350,6 @@ void Maxwell3D::CallMultiMethod(u32 method, const u32* base_start, u32 amount, | |||
| 326 | } | 350 | } |
| 327 | } | 351 | } |
| 328 | 352 | ||
| 329 | void Maxwell3D::StepInstance(const MMEDrawMode expected_mode, const u32 count) { | ||
| 330 | if (mme_draw.current_mode == MMEDrawMode::Undefined) { | ||
| 331 | if (mme_draw.gl_begin_consume) { | ||
| 332 | mme_draw.current_mode = expected_mode; | ||
| 333 | mme_draw.current_count = count; | ||
| 334 | mme_draw.instance_count = 1; | ||
| 335 | mme_draw.gl_begin_consume = false; | ||
| 336 | mme_draw.gl_end_count = 0; | ||
| 337 | } | ||
| 338 | return; | ||
| 339 | } else { | ||
| 340 | if (mme_draw.current_mode == expected_mode && count == mme_draw.current_count && | ||
| 341 | mme_draw.instance_mode && mme_draw.gl_begin_consume) { | ||
| 342 | mme_draw.instance_count++; | ||
| 343 | mme_draw.gl_begin_consume = false; | ||
| 344 | return; | ||
| 345 | } else { | ||
| 346 | FlushMMEInlineDraw(); | ||
| 347 | } | ||
| 348 | } | ||
| 349 | // Tail call in case it needs to retry. | ||
| 350 | StepInstance(expected_mode, count); | ||
| 351 | } | ||
| 352 | |||
| 353 | void Maxwell3D::CallMethodFromMME(u32 method, u32 method_argument) { | ||
| 354 | if (mme_inline[method]) { | ||
| 355 | regs.reg_array[method] = method_argument; | ||
| 356 | if (method == MAXWELL3D_REG_INDEX(vertex_buffer.count) || | ||
| 357 | method == MAXWELL3D_REG_INDEX(index_buffer.count)) { | ||
| 358 | const MMEDrawMode expected_mode = method == MAXWELL3D_REG_INDEX(vertex_buffer.count) | ||
| 359 | ? MMEDrawMode::Array | ||
| 360 | : MMEDrawMode::Indexed; | ||
| 361 | StepInstance(expected_mode, method_argument); | ||
| 362 | } else if (method == MAXWELL3D_REG_INDEX(draw.begin)) { | ||
| 363 | mme_draw.instance_mode = | ||
| 364 | (regs.draw.instance_id == Maxwell3D::Regs::Draw::InstanceId::Subsequent) || | ||
| 365 | (regs.draw.instance_id == Maxwell3D::Regs::Draw::InstanceId::Unchanged); | ||
| 366 | mme_draw.gl_begin_consume = true; | ||
| 367 | } else { | ||
| 368 | mme_draw.gl_end_count++; | ||
| 369 | } | ||
| 370 | } else { | ||
| 371 | if (mme_draw.current_mode != MMEDrawMode::Undefined) { | ||
| 372 | FlushMMEInlineDraw(); | ||
| 373 | } | ||
| 374 | CallMethod(method, method_argument, true); | ||
| 375 | } | ||
| 376 | } | ||
| 377 | |||
| 378 | void Maxwell3D::ProcessTopologyOverride() { | 353 | void Maxwell3D::ProcessTopologyOverride() { |
| 379 | using PrimitiveTopology = Maxwell3D::Regs::PrimitiveTopology; | 354 | using PrimitiveTopology = Maxwell3D::Regs::PrimitiveTopology; |
| 380 | using PrimitiveTopologyOverride = Maxwell3D::Regs::PrimitiveTopologyOverride; | 355 | using PrimitiveTopologyOverride = Maxwell3D::Regs::PrimitiveTopologyOverride; |
| @@ -404,41 +379,6 @@ void Maxwell3D::ProcessTopologyOverride() { | |||
| 404 | } | 379 | } |
| 405 | } | 380 | } |
| 406 | 381 | ||
| 407 | void Maxwell3D::FlushMMEInlineDraw() { | ||
| 408 | LOG_TRACE(HW_GPU, "called, topology={}, count={}", regs.draw.topology.Value(), | ||
| 409 | regs.vertex_buffer.count); | ||
| 410 | ASSERT_MSG(!(regs.index_buffer.count && regs.vertex_buffer.count), "Both indexed and direct?"); | ||
| 411 | ASSERT(mme_draw.instance_count == mme_draw.gl_end_count); | ||
| 412 | |||
| 413 | // Both instance configuration registers can not be set at the same time. | ||
| 414 | ASSERT_MSG(regs.draw.instance_id == Maxwell3D::Regs::Draw::InstanceId::First || | ||
| 415 | regs.draw.instance_id != Maxwell3D::Regs::Draw::InstanceId::Unchanged, | ||
| 416 | "Illegal combination of instancing parameters"); | ||
| 417 | |||
| 418 | ProcessTopologyOverride(); | ||
| 419 | |||
| 420 | const bool is_indexed = mme_draw.current_mode == MMEDrawMode::Indexed; | ||
| 421 | if (ShouldExecute()) { | ||
| 422 | rasterizer->Draw(is_indexed, true); | ||
| 423 | } | ||
| 424 | |||
| 425 | // TODO(bunnei): Below, we reset vertex count so that we can use these registers to determine if | ||
| 426 | // the game is trying to draw indexed or direct mode. This needs to be verified on HW still - | ||
| 427 | // it's possible that it is incorrect and that there is some other register used to specify the | ||
| 428 | // drawing mode. | ||
| 429 | if (is_indexed) { | ||
| 430 | regs.index_buffer.count = 0; | ||
| 431 | } else { | ||
| 432 | regs.vertex_buffer.count = 0; | ||
| 433 | } | ||
| 434 | mme_draw.current_mode = MMEDrawMode::Undefined; | ||
| 435 | mme_draw.current_count = 0; | ||
| 436 | mme_draw.instance_count = 0; | ||
| 437 | mme_draw.instance_mode = false; | ||
| 438 | mme_draw.gl_begin_consume = false; | ||
| 439 | mme_draw.gl_end_count = 0; | ||
| 440 | } | ||
| 441 | |||
| 442 | void Maxwell3D::ProcessMacroUpload(u32 data) { | 382 | void Maxwell3D::ProcessMacroUpload(u32 data) { |
| 443 | macro_engine->AddCode(regs.load_mme.instruction_ptr++, data); | 383 | macro_engine->AddCode(regs.load_mme.instruction_ptr++, data); |
| 444 | } | 384 | } |
| @@ -473,9 +413,7 @@ void Maxwell3D::ProcessQueryGet() { | |||
| 473 | 413 | ||
| 474 | switch (regs.report_semaphore.query.operation) { | 414 | switch (regs.report_semaphore.query.operation) { |
| 475 | case Regs::ReportSemaphore::Operation::Release: | 415 | case Regs::ReportSemaphore::Operation::Release: |
| 476 | if (regs.report_semaphore.query.release == | 416 | if (regs.report_semaphore.query.short_query != 0) { |
| 477 | Regs::ReportSemaphore::Release::AfterAllPreceedingWrites || | ||
| 478 | regs.report_semaphore.query.short_query != 0) { | ||
| 479 | const GPUVAddr sequence_address{regs.report_semaphore.Address()}; | 417 | const GPUVAddr sequence_address{regs.report_semaphore.Address()}; |
| 480 | const u32 payload = regs.report_semaphore.payload; | 418 | const u32 payload = regs.report_semaphore.payload; |
| 481 | std::function<void()> operation([this, sequence_address, payload] { | 419 | std::function<void()> operation([this, sequence_address, payload] { |
| @@ -489,11 +427,10 @@ void Maxwell3D::ProcessQueryGet() { | |||
| 489 | }; | 427 | }; |
| 490 | const GPUVAddr sequence_address{regs.report_semaphore.Address()}; | 428 | const GPUVAddr sequence_address{regs.report_semaphore.Address()}; |
| 491 | const u32 payload = regs.report_semaphore.payload; | 429 | const u32 payload = regs.report_semaphore.payload; |
| 492 | std::function<void()> operation([this, sequence_address, payload] { | 430 | [this, sequence_address, payload] { |
| 493 | memory_manager.Write<u64>(sequence_address + sizeof(u64), system.GPU().GetTicks()); | 431 | memory_manager.Write<u64>(sequence_address + sizeof(u64), system.GPU().GetTicks()); |
| 494 | memory_manager.Write<u64>(sequence_address, payload); | 432 | memory_manager.Write<u64>(sequence_address, payload); |
| 495 | }); | 433 | }(); |
| 496 | rasterizer->SyncOperation(std::move(operation)); | ||
| 497 | } | 434 | } |
| 498 | break; | 435 | break; |
| 499 | case Regs::ReportSemaphore::Operation::Acquire: | 436 | case Regs::ReportSemaphore::Operation::Acquire: |
| @@ -569,47 +506,11 @@ void Maxwell3D::ProcessCounterReset() { | |||
| 569 | 506 | ||
| 570 | void Maxwell3D::ProcessSyncPoint() { | 507 | void Maxwell3D::ProcessSyncPoint() { |
| 571 | const u32 sync_point = regs.sync_info.sync_point.Value(); | 508 | const u32 sync_point = regs.sync_info.sync_point.Value(); |
| 572 | const auto condition = regs.sync_info.condition.Value(); | 509 | const u32 cache_flush = regs.sync_info.clean_l2.Value(); |
| 573 | [[maybe_unused]] const u32 cache_flush = regs.sync_info.clean_l2.Value(); | 510 | if (cache_flush != 0) { |
| 574 | if (condition == Regs::SyncInfo::Condition::RopWritesDone) { | 511 | rasterizer->InvalidateGPUCache(); |
| 575 | rasterizer->SignalSyncPoint(sync_point); | ||
| 576 | } | ||
| 577 | } | ||
| 578 | |||
| 579 | void Maxwell3D::DrawArrays() { | ||
| 580 | LOG_TRACE(HW_GPU, "called, topology={}, count={}", regs.draw.topology.Value(), | ||
| 581 | regs.vertex_buffer.count); | ||
| 582 | ASSERT_MSG(!(regs.index_buffer.count && regs.vertex_buffer.count), "Both indexed and direct?"); | ||
| 583 | |||
| 584 | // Both instance configuration registers can not be set at the same time. | ||
| 585 | ASSERT_MSG(regs.draw.instance_id == Maxwell3D::Regs::Draw::InstanceId::First || | ||
| 586 | regs.draw.instance_id != Maxwell3D::Regs::Draw::InstanceId::Unchanged, | ||
| 587 | "Illegal combination of instancing parameters"); | ||
| 588 | |||
| 589 | ProcessTopologyOverride(); | ||
| 590 | |||
| 591 | if (regs.draw.instance_id == Maxwell3D::Regs::Draw::InstanceId::Subsequent) { | ||
| 592 | // Increment the current instance *before* drawing. | ||
| 593 | state.current_instance++; | ||
| 594 | } else if (regs.draw.instance_id != Maxwell3D::Regs::Draw::InstanceId::Unchanged) { | ||
| 595 | // Reset the current instance to 0. | ||
| 596 | state.current_instance = 0; | ||
| 597 | } | ||
| 598 | |||
| 599 | const bool is_indexed{regs.index_buffer.count && !regs.vertex_buffer.count}; | ||
| 600 | if (ShouldExecute()) { | ||
| 601 | rasterizer->Draw(is_indexed, false); | ||
| 602 | } | ||
| 603 | |||
| 604 | // TODO(bunnei): Below, we reset vertex count so that we can use these registers to determine if | ||
| 605 | // the game is trying to draw indexed or direct mode. This needs to be verified on HW still - | ||
| 606 | // it's possible that it is incorrect and that there is some other register used to specify the | ||
| 607 | // drawing mode. | ||
| 608 | if (is_indexed) { | ||
| 609 | regs.index_buffer.count = 0; | ||
| 610 | } else { | ||
| 611 | regs.vertex_buffer.count = 0; | ||
| 612 | } | 512 | } |
| 513 | rasterizer->SignalSyncPoint(sync_point); | ||
| 613 | } | 514 | } |
| 614 | 515 | ||
| 615 | std::optional<u64> Maxwell3D::GetQueryResult() { | 516 | std::optional<u64> Maxwell3D::GetQueryResult() { |
| @@ -694,4 +595,90 @@ void Maxwell3D::ProcessClearBuffers() { | |||
| 694 | rasterizer->Clear(); | 595 | rasterizer->Clear(); |
| 695 | } | 596 | } |
| 696 | 597 | ||
| 598 | void Maxwell3D::ProcessDraw(u32 instance_count) { | ||
| 599 | LOG_TRACE(HW_GPU, "called, topology={}, count={}", regs.draw.topology.Value(), | ||
| 600 | regs.vertex_buffer.count); | ||
| 601 | |||
| 602 | ASSERT_MSG(!(regs.index_buffer.count && regs.vertex_buffer.count), "Both indexed and direct?"); | ||
| 603 | |||
| 604 | // Both instance configuration registers can not be set at the same time. | ||
| 605 | ASSERT_MSG(regs.draw.instance_id == Maxwell3D::Regs::Draw::InstanceId::First || | ||
| 606 | regs.draw.instance_id != Maxwell3D::Regs::Draw::InstanceId::Unchanged, | ||
| 607 | "Illegal combination of instancing parameters"); | ||
| 608 | |||
| 609 | ProcessTopologyOverride(); | ||
| 610 | |||
| 611 | const bool is_indexed = regs.index_buffer.count && !regs.vertex_buffer.count; | ||
| 612 | if (ShouldExecute()) { | ||
| 613 | rasterizer->Draw(is_indexed, instance_count); | ||
| 614 | } | ||
| 615 | |||
| 616 | if (is_indexed) { | ||
| 617 | regs.index_buffer.count = 0; | ||
| 618 | } else { | ||
| 619 | regs.vertex_buffer.count = 0; | ||
| 620 | } | ||
| 621 | } | ||
| 622 | |||
| 623 | void Maxwell3D::ProcessDeferredDraw() { | ||
| 624 | if (deferred_draw_method.empty()) { | ||
| 625 | return; | ||
| 626 | } | ||
| 627 | |||
| 628 | enum class DrawMode { | ||
| 629 | Undefined, | ||
| 630 | General, | ||
| 631 | Instance, | ||
| 632 | }; | ||
| 633 | DrawMode draw_mode{DrawMode::Undefined}; | ||
| 634 | u32 instance_count = 1; | ||
| 635 | |||
| 636 | u32 index = 0; | ||
| 637 | u32 method = 0; | ||
| 638 | u32 method_count = static_cast<u32>(deferred_draw_method.size()); | ||
| 639 | for (; index < method_count && | ||
| 640 | (method = deferred_draw_method[index]) != MAXWELL3D_REG_INDEX(draw.begin); | ||
| 641 | ++index) | ||
| 642 | ; | ||
| 643 | |||
| 644 | if (MAXWELL3D_REG_INDEX(draw.begin) != method) { | ||
| 645 | return; | ||
| 646 | } | ||
| 647 | |||
| 648 | // The minimum number of methods for drawing must be greater than or equal to | ||
| 649 | // 3[draw.begin->vertex(index)count(first)->draw.end] to avoid errors in index mode drawing | ||
| 650 | if ((method_count - index) < 3) { | ||
| 651 | return; | ||
| 652 | } | ||
| 653 | draw_mode = (regs.draw.instance_id == Maxwell3D::Regs::Draw::InstanceId::Subsequent) || | ||
| 654 | (regs.draw.instance_id == Maxwell3D::Regs::Draw::InstanceId::Unchanged) | ||
| 655 | ? DrawMode::Instance | ||
| 656 | : DrawMode::General; | ||
| 657 | |||
| 658 | // Drawing will only begin with draw.begin or index_buffer method, other methods directly | ||
| 659 | // clear | ||
| 660 | if (draw_mode == DrawMode::Undefined) { | ||
| 661 | deferred_draw_method.clear(); | ||
| 662 | return; | ||
| 663 | } | ||
| 664 | |||
| 665 | if (draw_mode == DrawMode::Instance) { | ||
| 666 | ASSERT_MSG(deferred_draw_method.size() % 4 == 0, "Instance mode method size error"); | ||
| 667 | instance_count = static_cast<u32>(method_count - index) / 4; | ||
| 668 | } else { | ||
| 669 | method = deferred_draw_method[index + 1]; | ||
| 670 | if (MAXWELL3D_REG_INDEX(draw_inline_index) == method || | ||
| 671 | MAXWELL3D_REG_INDEX(inline_index_2x16.even) == method || | ||
| 672 | MAXWELL3D_REG_INDEX(inline_index_4x8.index0) == method) { | ||
| 673 | regs.index_buffer.count = static_cast<u32>(inline_index_draw_indexes.size() / 4); | ||
| 674 | regs.index_buffer.format = Regs::IndexFormat::UnsignedInt; | ||
| 675 | } | ||
| 676 | } | ||
| 677 | |||
| 678 | ProcessDraw(instance_count); | ||
| 679 | |||
| 680 | deferred_draw_method.clear(); | ||
| 681 | inline_index_draw_indexes.clear(); | ||
| 682 | } | ||
| 683 | |||
| 697 | } // namespace Tegra::Engines | 684 | } // namespace Tegra::Engines |
diff --git a/src/video_core/engines/maxwell_3d.h b/src/video_core/engines/maxwell_3d.h index 75e3b868d..a948fcb14 100644 --- a/src/video_core/engines/maxwell_3d.h +++ b/src/video_core/engines/maxwell_3d.h | |||
| @@ -1739,14 +1739,11 @@ public: | |||
| 1739 | Footprint_1x1_Virtual = 2, | 1739 | Footprint_1x1_Virtual = 2, |
| 1740 | }; | 1740 | }; |
| 1741 | 1741 | ||
| 1742 | struct InlineIndex4x8Align { | 1742 | struct InlineIndex4x8 { |
| 1743 | union { | 1743 | union { |
| 1744 | BitField<0, 30, u32> count; | 1744 | BitField<0, 30, u32> count; |
| 1745 | BitField<30, 2, u32> start; | 1745 | BitField<30, 2, u32> start; |
| 1746 | }; | 1746 | }; |
| 1747 | }; | ||
| 1748 | |||
| 1749 | struct InlineIndex4x8Index { | ||
| 1750 | union { | 1747 | union { |
| 1751 | BitField<0, 8, u32> index0; | 1748 | BitField<0, 8, u32> index0; |
| 1752 | BitField<8, 8, u32> index1; | 1749 | BitField<8, 8, u32> index1; |
| @@ -2836,8 +2833,7 @@ public: | |||
| 2836 | u32 depth_write_enabled; ///< 0x12E8 | 2833 | u32 depth_write_enabled; ///< 0x12E8 |
| 2837 | u32 alpha_test_enabled; ///< 0x12EC | 2834 | u32 alpha_test_enabled; ///< 0x12EC |
| 2838 | INSERT_PADDING_BYTES_NOINIT(0x10); | 2835 | INSERT_PADDING_BYTES_NOINIT(0x10); |
| 2839 | InlineIndex4x8Align inline_index_4x8_align; ///< 0x1300 | 2836 | InlineIndex4x8 inline_index_4x8; ///< 0x1300 |
| 2840 | InlineIndex4x8Index inline_index_4x8_index; ///< 0x1304 | ||
| 2841 | D3DCullMode d3d_cull_mode; ///< 0x1308 | 2837 | D3DCullMode d3d_cull_mode; ///< 0x1308 |
| 2842 | ComparisonOp depth_test_func; ///< 0x130C | 2838 | ComparisonOp depth_test_func; ///< 0x130C |
| 2843 | f32 alpha_test_ref; ///< 0x1310 | 2839 | f32 alpha_test_ref; ///< 0x1310 |
| @@ -3048,8 +3044,6 @@ public: | |||
| 3048 | }; | 3044 | }; |
| 3049 | 3045 | ||
| 3050 | std::array<ShaderStageInfo, Regs::MaxShaderStage> shader_stages; | 3046 | std::array<ShaderStageInfo, Regs::MaxShaderStage> shader_stages; |
| 3051 | |||
| 3052 | u32 current_instance = 0; ///< Current instance to be used to simulate instanced rendering. | ||
| 3053 | }; | 3047 | }; |
| 3054 | 3048 | ||
| 3055 | State state{}; | 3049 | State state{}; |
| @@ -3064,11 +3058,6 @@ public: | |||
| 3064 | void CallMultiMethod(u32 method, const u32* base_start, u32 amount, | 3058 | void CallMultiMethod(u32 method, const u32* base_start, u32 amount, |
| 3065 | u32 methods_pending) override; | 3059 | u32 methods_pending) override; |
| 3066 | 3060 | ||
| 3067 | /// Write the value to the register identified by method. | ||
| 3068 | void CallMethodFromMME(u32 method, u32 method_argument); | ||
| 3069 | |||
| 3070 | void FlushMMEInlineDraw(); | ||
| 3071 | |||
| 3072 | bool ShouldExecute() const { | 3061 | bool ShouldExecute() const { |
| 3073 | return execute_on; | 3062 | return execute_on; |
| 3074 | } | 3063 | } |
| @@ -3081,21 +3070,6 @@ public: | |||
| 3081 | return *rasterizer; | 3070 | return *rasterizer; |
| 3082 | } | 3071 | } |
| 3083 | 3072 | ||
| 3084 | enum class MMEDrawMode : u32 { | ||
| 3085 | Undefined, | ||
| 3086 | Array, | ||
| 3087 | Indexed, | ||
| 3088 | }; | ||
| 3089 | |||
| 3090 | struct MMEDrawState { | ||
| 3091 | MMEDrawMode current_mode{MMEDrawMode::Undefined}; | ||
| 3092 | u32 current_count{}; | ||
| 3093 | u32 instance_count{}; | ||
| 3094 | bool instance_mode{}; | ||
| 3095 | bool gl_begin_consume{}; | ||
| 3096 | u32 gl_end_count{}; | ||
| 3097 | } mme_draw; | ||
| 3098 | |||
| 3099 | struct DirtyState { | 3073 | struct DirtyState { |
| 3100 | using Flags = std::bitset<std::numeric_limits<u8>::max()>; | 3074 | using Flags = std::bitset<std::numeric_limits<u8>::max()>; |
| 3101 | using Table = std::array<u8, Regs::NUM_REGS>; | 3075 | using Table = std::array<u8, Regs::NUM_REGS>; |
| @@ -3105,6 +3079,8 @@ public: | |||
| 3105 | Tables tables{}; | 3079 | Tables tables{}; |
| 3106 | } dirty; | 3080 | } dirty; |
| 3107 | 3081 | ||
| 3082 | std::vector<u8> inline_index_draw_indexes; | ||
| 3083 | |||
| 3108 | private: | 3084 | private: |
| 3109 | void InitializeRegisterDefaults(); | 3085 | void InitializeRegisterDefaults(); |
| 3110 | 3086 | ||
| @@ -3164,14 +3140,12 @@ private: | |||
| 3164 | /// Handles a write to the CB_BIND register. | 3140 | /// Handles a write to the CB_BIND register. |
| 3165 | void ProcessCBBind(size_t stage_index); | 3141 | void ProcessCBBind(size_t stage_index); |
| 3166 | 3142 | ||
| 3167 | /// Handles a write to the VERTEX_END_GL register, triggering a draw. | ||
| 3168 | void DrawArrays(); | ||
| 3169 | |||
| 3170 | /// Handles use of topology overrides (e.g., to avoid using a topology assigned from a macro) | 3143 | /// Handles use of topology overrides (e.g., to avoid using a topology assigned from a macro) |
| 3171 | void ProcessTopologyOverride(); | 3144 | void ProcessTopologyOverride(); |
| 3172 | 3145 | ||
| 3173 | // Handles a instance drawcall from MME | 3146 | void ProcessDraw(u32 instance_count = 1); |
| 3174 | void StepInstance(MMEDrawMode expected_mode, u32 count); | 3147 | |
| 3148 | void ProcessDeferredDraw(); | ||
| 3175 | 3149 | ||
| 3176 | /// Returns a query's value or an empty object if the value will be deferred through a cache. | 3150 | /// Returns a query's value or an empty object if the value will be deferred through a cache. |
| 3177 | std::optional<u64> GetQueryResult(); | 3151 | std::optional<u64> GetQueryResult(); |
| @@ -3184,8 +3158,6 @@ private: | |||
| 3184 | /// Start offsets of each macro in macro_memory | 3158 | /// Start offsets of each macro in macro_memory |
| 3185 | std::array<u32, 0x80> macro_positions{}; | 3159 | std::array<u32, 0x80> macro_positions{}; |
| 3186 | 3160 | ||
| 3187 | std::array<bool, Regs::NUM_REGS> mme_inline{}; | ||
| 3188 | |||
| 3189 | /// Macro method that is currently being executed / being fed parameters. | 3161 | /// Macro method that is currently being executed / being fed parameters. |
| 3190 | u32 executing_macro = 0; | 3162 | u32 executing_macro = 0; |
| 3191 | /// Parameters that have been submitted to the macro call so far. | 3163 | /// Parameters that have been submitted to the macro call so far. |
| @@ -3198,6 +3170,9 @@ private: | |||
| 3198 | 3170 | ||
| 3199 | bool execute_on{true}; | 3171 | bool execute_on{true}; |
| 3200 | bool use_topology_override{false}; | 3172 | bool use_topology_override{false}; |
| 3173 | |||
| 3174 | std::array<bool, Regs::NUM_REGS> draw_command{}; | ||
| 3175 | std::vector<u32> deferred_draw_method; | ||
| 3201 | }; | 3176 | }; |
| 3202 | 3177 | ||
| 3203 | #define ASSERT_REG_POSITION(field_name, position) \ | 3178 | #define ASSERT_REG_POSITION(field_name, position) \ |
| @@ -3402,8 +3377,7 @@ ASSERT_REG_POSITION(alpha_to_coverage_dither, 0x12E0); | |||
| 3402 | ASSERT_REG_POSITION(blend_per_target_enabled, 0x12E4); | 3377 | ASSERT_REG_POSITION(blend_per_target_enabled, 0x12E4); |
| 3403 | ASSERT_REG_POSITION(depth_write_enabled, 0x12E8); | 3378 | ASSERT_REG_POSITION(depth_write_enabled, 0x12E8); |
| 3404 | ASSERT_REG_POSITION(alpha_test_enabled, 0x12EC); | 3379 | ASSERT_REG_POSITION(alpha_test_enabled, 0x12EC); |
| 3405 | ASSERT_REG_POSITION(inline_index_4x8_align, 0x1300); | 3380 | ASSERT_REG_POSITION(inline_index_4x8, 0x1300); |
| 3406 | ASSERT_REG_POSITION(inline_index_4x8_index, 0x1304); | ||
| 3407 | ASSERT_REG_POSITION(d3d_cull_mode, 0x1308); | 3381 | ASSERT_REG_POSITION(d3d_cull_mode, 0x1308); |
| 3408 | ASSERT_REG_POSITION(depth_test_func, 0x130C); | 3382 | ASSERT_REG_POSITION(depth_test_func, 0x130C); |
| 3409 | ASSERT_REG_POSITION(alpha_test_ref, 0x1310); | 3383 | ASSERT_REG_POSITION(alpha_test_ref, 0x1310); |
diff --git a/src/video_core/engines/puller.cpp b/src/video_core/engines/puller.cpp index cca890792..3977bb0fb 100644 --- a/src/video_core/engines/puller.cpp +++ b/src/video_core/engines/puller.cpp | |||
| @@ -75,11 +75,10 @@ void Puller::ProcessSemaphoreTriggerMethod() { | |||
| 75 | if (op == GpuSemaphoreOperation::WriteLong) { | 75 | if (op == GpuSemaphoreOperation::WriteLong) { |
| 76 | const GPUVAddr sequence_address{regs.semaphore_address.SemaphoreAddress()}; | 76 | const GPUVAddr sequence_address{regs.semaphore_address.SemaphoreAddress()}; |
| 77 | const u32 payload = regs.semaphore_sequence; | 77 | const u32 payload = regs.semaphore_sequence; |
| 78 | std::function<void()> operation([this, sequence_address, payload] { | 78 | [this, sequence_address, payload] { |
| 79 | memory_manager.Write<u64>(sequence_address + sizeof(u64), gpu.GetTicks()); | 79 | memory_manager.Write<u64>(sequence_address + sizeof(u64), gpu.GetTicks()); |
| 80 | memory_manager.Write<u64>(sequence_address, payload); | 80 | memory_manager.Write<u64>(sequence_address, payload); |
| 81 | }); | 81 | }(); |
| 82 | rasterizer->SignalFence(std::move(operation)); | ||
| 83 | } else { | 82 | } else { |
| 84 | do { | 83 | do { |
| 85 | const u32 word{memory_manager.Read<u32>(regs.semaphore_address.SemaphoreAddress())}; | 84 | const u32 word{memory_manager.Read<u32>(regs.semaphore_address.SemaphoreAddress())}; |
diff --git a/src/video_core/macro/macro_hle.cpp b/src/video_core/macro/macro_hle.cpp index 8a8adbb42..f896591bf 100644 --- a/src/video_core/macro/macro_hle.cpp +++ b/src/video_core/macro/macro_hle.cpp | |||
| @@ -22,35 +22,29 @@ void HLE_771BB18C62444DA0(Engines::Maxwell3D& maxwell3d, const std::vector<u32>& | |||
| 22 | maxwell3d.regs.draw.topology.Assign( | 22 | maxwell3d.regs.draw.topology.Assign( |
| 23 | static_cast<Tegra::Engines::Maxwell3D::Regs::PrimitiveTopology>(parameters[0] & 0x3ffffff)); | 23 | static_cast<Tegra::Engines::Maxwell3D::Regs::PrimitiveTopology>(parameters[0] & 0x3ffffff)); |
| 24 | maxwell3d.regs.global_base_instance_index = parameters[5]; | 24 | maxwell3d.regs.global_base_instance_index = parameters[5]; |
| 25 | maxwell3d.mme_draw.instance_count = instance_count; | ||
| 26 | maxwell3d.regs.global_base_vertex_index = parameters[3]; | 25 | maxwell3d.regs.global_base_vertex_index = parameters[3]; |
| 27 | maxwell3d.regs.index_buffer.count = parameters[1]; | 26 | maxwell3d.regs.index_buffer.count = parameters[1]; |
| 28 | maxwell3d.regs.index_buffer.first = parameters[4]; | 27 | maxwell3d.regs.index_buffer.first = parameters[4]; |
| 29 | 28 | ||
| 30 | if (maxwell3d.ShouldExecute()) { | 29 | if (maxwell3d.ShouldExecute()) { |
| 31 | maxwell3d.Rasterizer().Draw(true, true); | 30 | maxwell3d.Rasterizer().Draw(true, instance_count); |
| 32 | } | 31 | } |
| 33 | maxwell3d.regs.index_buffer.count = 0; | 32 | maxwell3d.regs.index_buffer.count = 0; |
| 34 | maxwell3d.mme_draw.instance_count = 0; | ||
| 35 | maxwell3d.mme_draw.current_mode = Engines::Maxwell3D::MMEDrawMode::Undefined; | ||
| 36 | } | 33 | } |
| 37 | 34 | ||
| 38 | void HLE_0D61FC9FAAC9FCAD(Engines::Maxwell3D& maxwell3d, const std::vector<u32>& parameters) { | 35 | void HLE_0D61FC9FAAC9FCAD(Engines::Maxwell3D& maxwell3d, const std::vector<u32>& parameters) { |
| 39 | const u32 count = (maxwell3d.GetRegisterValue(0xD1B) & parameters[2]); | 36 | const u32 instance_count = (maxwell3d.GetRegisterValue(0xD1B) & parameters[2]); |
| 40 | 37 | ||
| 41 | maxwell3d.regs.vertex_buffer.first = parameters[3]; | 38 | maxwell3d.regs.vertex_buffer.first = parameters[3]; |
| 42 | maxwell3d.regs.vertex_buffer.count = parameters[1]; | 39 | maxwell3d.regs.vertex_buffer.count = parameters[1]; |
| 43 | maxwell3d.regs.global_base_instance_index = parameters[4]; | 40 | maxwell3d.regs.global_base_instance_index = parameters[4]; |
| 44 | maxwell3d.regs.draw.topology.Assign( | 41 | maxwell3d.regs.draw.topology.Assign( |
| 45 | static_cast<Tegra::Engines::Maxwell3D::Regs::PrimitiveTopology>(parameters[0])); | 42 | static_cast<Tegra::Engines::Maxwell3D::Regs::PrimitiveTopology>(parameters[0])); |
| 46 | maxwell3d.mme_draw.instance_count = count; | ||
| 47 | 43 | ||
| 48 | if (maxwell3d.ShouldExecute()) { | 44 | if (maxwell3d.ShouldExecute()) { |
| 49 | maxwell3d.Rasterizer().Draw(false, true); | 45 | maxwell3d.Rasterizer().Draw(false, instance_count); |
| 50 | } | 46 | } |
| 51 | maxwell3d.regs.vertex_buffer.count = 0; | 47 | maxwell3d.regs.vertex_buffer.count = 0; |
| 52 | maxwell3d.mme_draw.instance_count = 0; | ||
| 53 | maxwell3d.mme_draw.current_mode = Engines::Maxwell3D::MMEDrawMode::Undefined; | ||
| 54 | } | 48 | } |
| 55 | 49 | ||
| 56 | void HLE_0217920100488FF7(Engines::Maxwell3D& maxwell3d, const std::vector<u32>& parameters) { | 50 | void HLE_0217920100488FF7(Engines::Maxwell3D& maxwell3d, const std::vector<u32>& parameters) { |
| @@ -63,24 +57,21 @@ void HLE_0217920100488FF7(Engines::Maxwell3D& maxwell3d, const std::vector<u32>& | |||
| 63 | maxwell3d.dirty.flags[VideoCommon::Dirty::IndexBuffer] = true; | 57 | maxwell3d.dirty.flags[VideoCommon::Dirty::IndexBuffer] = true; |
| 64 | maxwell3d.regs.global_base_vertex_index = element_base; | 58 | maxwell3d.regs.global_base_vertex_index = element_base; |
| 65 | maxwell3d.regs.global_base_instance_index = base_instance; | 59 | maxwell3d.regs.global_base_instance_index = base_instance; |
| 66 | maxwell3d.mme_draw.instance_count = instance_count; | 60 | maxwell3d.CallMethod(0x8e3, 0x640, true); |
| 67 | maxwell3d.CallMethodFromMME(0x8e3, 0x640); | 61 | maxwell3d.CallMethod(0x8e4, element_base, true); |
| 68 | maxwell3d.CallMethodFromMME(0x8e4, element_base); | 62 | maxwell3d.CallMethod(0x8e5, base_instance, true); |
| 69 | maxwell3d.CallMethodFromMME(0x8e5, base_instance); | ||
| 70 | maxwell3d.regs.draw.topology.Assign( | 63 | maxwell3d.regs.draw.topology.Assign( |
| 71 | static_cast<Tegra::Engines::Maxwell3D::Regs::PrimitiveTopology>(parameters[0])); | 64 | static_cast<Tegra::Engines::Maxwell3D::Regs::PrimitiveTopology>(parameters[0])); |
| 72 | if (maxwell3d.ShouldExecute()) { | 65 | if (maxwell3d.ShouldExecute()) { |
| 73 | maxwell3d.Rasterizer().Draw(true, true); | 66 | maxwell3d.Rasterizer().Draw(true, instance_count); |
| 74 | } | 67 | } |
| 75 | maxwell3d.regs.vertex_id_base = 0x0; | 68 | maxwell3d.regs.vertex_id_base = 0x0; |
| 76 | maxwell3d.regs.index_buffer.count = 0; | 69 | maxwell3d.regs.index_buffer.count = 0; |
| 77 | maxwell3d.regs.global_base_vertex_index = 0x0; | 70 | maxwell3d.regs.global_base_vertex_index = 0x0; |
| 78 | maxwell3d.regs.global_base_instance_index = 0x0; | 71 | maxwell3d.regs.global_base_instance_index = 0x0; |
| 79 | maxwell3d.mme_draw.instance_count = 0; | 72 | maxwell3d.CallMethod(0x8e3, 0x640, true); |
| 80 | maxwell3d.CallMethodFromMME(0x8e3, 0x640); | 73 | maxwell3d.CallMethod(0x8e4, 0x0, true); |
| 81 | maxwell3d.CallMethodFromMME(0x8e4, 0x0); | 74 | maxwell3d.CallMethod(0x8e5, 0x0, true); |
| 82 | maxwell3d.CallMethodFromMME(0x8e5, 0x0); | ||
| 83 | maxwell3d.mme_draw.current_mode = Engines::Maxwell3D::MMEDrawMode::Undefined; | ||
| 84 | } | 75 | } |
| 85 | 76 | ||
| 86 | // Multidraw Indirect | 77 | // Multidraw Indirect |
| @@ -91,11 +82,9 @@ void HLE_3F5E74B9C9A50164(Engines::Maxwell3D& maxwell3d, const std::vector<u32>& | |||
| 91 | maxwell3d.regs.index_buffer.count = 0; | 82 | maxwell3d.regs.index_buffer.count = 0; |
| 92 | maxwell3d.regs.global_base_vertex_index = 0x0; | 83 | maxwell3d.regs.global_base_vertex_index = 0x0; |
| 93 | maxwell3d.regs.global_base_instance_index = 0x0; | 84 | maxwell3d.regs.global_base_instance_index = 0x0; |
| 94 | maxwell3d.mme_draw.instance_count = 0; | 85 | maxwell3d.CallMethod(0x8e3, 0x640, true); |
| 95 | maxwell3d.CallMethodFromMME(0x8e3, 0x640); | 86 | maxwell3d.CallMethod(0x8e4, 0x0, true); |
| 96 | maxwell3d.CallMethodFromMME(0x8e4, 0x0); | 87 | maxwell3d.CallMethod(0x8e5, 0x0, true); |
| 97 | maxwell3d.CallMethodFromMME(0x8e5, 0x0); | ||
| 98 | maxwell3d.mme_draw.current_mode = Engines::Maxwell3D::MMEDrawMode::Undefined; | ||
| 99 | maxwell3d.dirty.flags[VideoCommon::Dirty::IndexBuffer] = true; | 88 | maxwell3d.dirty.flags[VideoCommon::Dirty::IndexBuffer] = true; |
| 100 | }); | 89 | }); |
| 101 | const u32 start_indirect = parameters[0]; | 90 | const u32 start_indirect = parameters[0]; |
| @@ -127,15 +116,13 @@ void HLE_3F5E74B9C9A50164(Engines::Maxwell3D& maxwell3d, const std::vector<u32>& | |||
| 127 | maxwell3d.regs.index_buffer.count = num_vertices; | 116 | maxwell3d.regs.index_buffer.count = num_vertices; |
| 128 | maxwell3d.regs.global_base_vertex_index = base_vertex; | 117 | maxwell3d.regs.global_base_vertex_index = base_vertex; |
| 129 | maxwell3d.regs.global_base_instance_index = base_instance; | 118 | maxwell3d.regs.global_base_instance_index = base_instance; |
| 130 | maxwell3d.mme_draw.instance_count = instance_count; | 119 | maxwell3d.CallMethod(0x8e3, 0x640, true); |
| 131 | maxwell3d.CallMethodFromMME(0x8e3, 0x640); | 120 | maxwell3d.CallMethod(0x8e4, base_vertex, true); |
| 132 | maxwell3d.CallMethodFromMME(0x8e4, base_vertex); | 121 | maxwell3d.CallMethod(0x8e5, base_instance, true); |
| 133 | maxwell3d.CallMethodFromMME(0x8e5, base_instance); | ||
| 134 | maxwell3d.dirty.flags[VideoCommon::Dirty::IndexBuffer] = true; | 122 | maxwell3d.dirty.flags[VideoCommon::Dirty::IndexBuffer] = true; |
| 135 | if (maxwell3d.ShouldExecute()) { | 123 | if (maxwell3d.ShouldExecute()) { |
| 136 | maxwell3d.Rasterizer().Draw(true, true); | 124 | maxwell3d.Rasterizer().Draw(true, instance_count); |
| 137 | } | 125 | } |
| 138 | maxwell3d.mme_draw.current_mode = Engines::Maxwell3D::MMEDrawMode::Undefined; | ||
| 139 | } | 126 | } |
| 140 | } | 127 | } |
| 141 | 128 | ||
diff --git a/src/video_core/macro/macro_interpreter.cpp b/src/video_core/macro/macro_interpreter.cpp index f670b1bca..c0d32c112 100644 --- a/src/video_core/macro/macro_interpreter.cpp +++ b/src/video_core/macro/macro_interpreter.cpp | |||
| @@ -335,7 +335,7 @@ void MacroInterpreterImpl::SetMethodAddress(u32 address) { | |||
| 335 | } | 335 | } |
| 336 | 336 | ||
| 337 | void MacroInterpreterImpl::Send(u32 value) { | 337 | void MacroInterpreterImpl::Send(u32 value) { |
| 338 | maxwell3d.CallMethodFromMME(method_address.address, value); | 338 | maxwell3d.CallMethod(method_address.address, value, true); |
| 339 | // Increment the method address by the method increment. | 339 | // Increment the method address by the method increment. |
| 340 | method_address.address.Assign(method_address.address.Value() + | 340 | method_address.address.Assign(method_address.address.Value() + |
| 341 | method_address.increment.Value()); | 341 | method_address.increment.Value()); |
diff --git a/src/video_core/macro/macro_jit_x64.cpp b/src/video_core/macro/macro_jit_x64.cpp index a302a9603..25c1ce798 100644 --- a/src/video_core/macro/macro_jit_x64.cpp +++ b/src/video_core/macro/macro_jit_x64.cpp | |||
| @@ -346,7 +346,7 @@ void MacroJITx64Impl::Compile_Read(Macro::Opcode opcode) { | |||
| 346 | } | 346 | } |
| 347 | 347 | ||
| 348 | void Send(Engines::Maxwell3D* maxwell3d, Macro::MethodAddress method_address, u32 value) { | 348 | void Send(Engines::Maxwell3D* maxwell3d, Macro::MethodAddress method_address, u32 value) { |
| 349 | maxwell3d->CallMethodFromMME(method_address.address, value); | 349 | maxwell3d->CallMethod(method_address.address, value, true); |
| 350 | } | 350 | } |
| 351 | 351 | ||
| 352 | void MacroJITx64Impl::Compile_Send(Xbyak::Reg32 value) { | 352 | void MacroJITx64Impl::Compile_Send(Xbyak::Reg32 value) { |
diff --git a/src/video_core/memory_manager.cpp b/src/video_core/memory_manager.cpp index d07b21bd6..384350dbd 100644 --- a/src/video_core/memory_manager.cpp +++ b/src/video_core/memory_manager.cpp | |||
| @@ -133,7 +133,7 @@ inline void MemoryManager::SetBigPageContinous(size_t big_page_index, bool value | |||
| 133 | template <MemoryManager::EntryType entry_type> | 133 | template <MemoryManager::EntryType entry_type> |
| 134 | GPUVAddr MemoryManager::PageTableOp(GPUVAddr gpu_addr, [[maybe_unused]] VAddr cpu_addr, size_t size, | 134 | GPUVAddr MemoryManager::PageTableOp(GPUVAddr gpu_addr, [[maybe_unused]] VAddr cpu_addr, size_t size, |
| 135 | PTEKind kind) { | 135 | PTEKind kind) { |
| 136 | u64 remaining_size{size}; | 136 | [[maybe_unused]] u64 remaining_size{size}; |
| 137 | if constexpr (entry_type == EntryType::Mapped) { | 137 | if constexpr (entry_type == EntryType::Mapped) { |
| 138 | page_table.ReserveRange(gpu_addr, size); | 138 | page_table.ReserveRange(gpu_addr, size); |
| 139 | } | 139 | } |
| @@ -159,7 +159,7 @@ GPUVAddr MemoryManager::PageTableOp(GPUVAddr gpu_addr, [[maybe_unused]] VAddr cp | |||
| 159 | template <MemoryManager::EntryType entry_type> | 159 | template <MemoryManager::EntryType entry_type> |
| 160 | GPUVAddr MemoryManager::BigPageTableOp(GPUVAddr gpu_addr, [[maybe_unused]] VAddr cpu_addr, | 160 | GPUVAddr MemoryManager::BigPageTableOp(GPUVAddr gpu_addr, [[maybe_unused]] VAddr cpu_addr, |
| 161 | size_t size, PTEKind kind) { | 161 | size_t size, PTEKind kind) { |
| 162 | u64 remaining_size{size}; | 162 | [[maybe_unused]] u64 remaining_size{size}; |
| 163 | for (u64 offset{}; offset < size; offset += big_page_size) { | 163 | for (u64 offset{}; offset < size; offset += big_page_size) { |
| 164 | const GPUVAddr current_gpu_addr = gpu_addr + offset; | 164 | const GPUVAddr current_gpu_addr = gpu_addr + offset; |
| 165 | [[maybe_unused]] const auto current_entry_type = GetEntry<true>(current_gpu_addr); | 165 | [[maybe_unused]] const auto current_entry_type = GetEntry<true>(current_gpu_addr); |
diff --git a/src/video_core/rasterizer_interface.h b/src/video_core/rasterizer_interface.h index d2d40884c..1cbfef090 100644 --- a/src/video_core/rasterizer_interface.h +++ b/src/video_core/rasterizer_interface.h | |||
| @@ -40,7 +40,7 @@ public: | |||
| 40 | virtual ~RasterizerInterface() = default; | 40 | virtual ~RasterizerInterface() = default; |
| 41 | 41 | ||
| 42 | /// Dispatches a draw invocation | 42 | /// Dispatches a draw invocation |
| 43 | virtual void Draw(bool is_indexed, bool is_instanced) = 0; | 43 | virtual void Draw(bool is_indexed, u32 instance_count) = 0; |
| 44 | 44 | ||
| 45 | /// Clear the current framebuffer | 45 | /// Clear the current framebuffer |
| 46 | virtual void Clear() = 0; | 46 | virtual void Clear() = 0; |
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp index e5c09a969..1590b21de 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp | |||
| @@ -205,7 +205,7 @@ void RasterizerOpenGL::Clear() { | |||
| 205 | ++num_queued_commands; | 205 | ++num_queued_commands; |
| 206 | } | 206 | } |
| 207 | 207 | ||
| 208 | void RasterizerOpenGL::Draw(bool is_indexed, bool is_instanced) { | 208 | void RasterizerOpenGL::Draw(bool is_indexed, u32 instance_count) { |
| 209 | MICROPROFILE_SCOPE(OpenGL_Drawing); | 209 | MICROPROFILE_SCOPE(OpenGL_Drawing); |
| 210 | 210 | ||
| 211 | SCOPE_EXIT({ gpu.TickWork(); }); | 211 | SCOPE_EXIT({ gpu.TickWork(); }); |
| @@ -222,14 +222,15 @@ void RasterizerOpenGL::Draw(bool is_indexed, bool is_instanced) { | |||
| 222 | pipeline->SetEngine(maxwell3d, gpu_memory); | 222 | pipeline->SetEngine(maxwell3d, gpu_memory); |
| 223 | pipeline->Configure(is_indexed); | 223 | pipeline->Configure(is_indexed); |
| 224 | 224 | ||
| 225 | BindInlineIndexBuffer(); | ||
| 226 | |||
| 225 | SyncState(); | 227 | SyncState(); |
| 226 | 228 | ||
| 227 | const GLenum primitive_mode = MaxwellToGL::PrimitiveTopology(maxwell3d->regs.draw.topology); | 229 | const GLenum primitive_mode = MaxwellToGL::PrimitiveTopology(maxwell3d->regs.draw.topology); |
| 228 | BeginTransformFeedback(pipeline, primitive_mode); | 230 | BeginTransformFeedback(pipeline, primitive_mode); |
| 229 | 231 | ||
| 230 | const GLuint base_instance = static_cast<GLuint>(maxwell3d->regs.global_base_instance_index); | 232 | const GLuint base_instance = static_cast<GLuint>(maxwell3d->regs.global_base_instance_index); |
| 231 | const GLsizei num_instances = | 233 | const GLsizei num_instances = static_cast<GLsizei>(instance_count); |
| 232 | static_cast<GLsizei>(is_instanced ? maxwell3d->mme_draw.instance_count : 1); | ||
| 233 | if (is_indexed) { | 234 | if (is_indexed) { |
| 234 | const GLint base_vertex = static_cast<GLint>(maxwell3d->regs.global_base_vertex_index); | 235 | const GLint base_vertex = static_cast<GLint>(maxwell3d->regs.global_base_vertex_index); |
| 235 | const GLsizei num_vertices = static_cast<GLsizei>(maxwell3d->regs.index_buffer.count); | 236 | const GLsizei num_vertices = static_cast<GLsizei>(maxwell3d->regs.index_buffer.count); |
| @@ -1129,6 +1130,16 @@ void RasterizerOpenGL::ReleaseChannel(s32 channel_id) { | |||
| 1129 | query_cache.EraseChannel(channel_id); | 1130 | query_cache.EraseChannel(channel_id); |
| 1130 | } | 1131 | } |
| 1131 | 1132 | ||
| 1133 | void RasterizerOpenGL::BindInlineIndexBuffer() { | ||
| 1134 | if (maxwell3d->inline_index_draw_indexes.empty()) { | ||
| 1135 | return; | ||
| 1136 | } | ||
| 1137 | const auto data_count = static_cast<u32>(maxwell3d->inline_index_draw_indexes.size()); | ||
| 1138 | auto buffer = Buffer(buffer_cache_runtime, *this, 0, data_count); | ||
| 1139 | buffer.ImmediateUpload(0, maxwell3d->inline_index_draw_indexes); | ||
| 1140 | buffer_cache_runtime.BindIndexBuffer(buffer, 0, data_count); | ||
| 1141 | } | ||
| 1142 | |||
| 1132 | AccelerateDMA::AccelerateDMA(BufferCache& buffer_cache_) : buffer_cache{buffer_cache_} {} | 1143 | AccelerateDMA::AccelerateDMA(BufferCache& buffer_cache_) : buffer_cache{buffer_cache_} {} |
| 1133 | 1144 | ||
| 1134 | bool AccelerateDMA::BufferCopy(GPUVAddr src_address, GPUVAddr dest_address, u64 amount) { | 1145 | bool AccelerateDMA::BufferCopy(GPUVAddr src_address, GPUVAddr dest_address, u64 amount) { |
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.h b/src/video_core/renderer_opengl/gl_rasterizer.h index 45131b785..793e0d608 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.h +++ b/src/video_core/renderer_opengl/gl_rasterizer.h | |||
| @@ -68,7 +68,7 @@ public: | |||
| 68 | StateTracker& state_tracker_); | 68 | StateTracker& state_tracker_); |
| 69 | ~RasterizerOpenGL() override; | 69 | ~RasterizerOpenGL() override; |
| 70 | 70 | ||
| 71 | void Draw(bool is_indexed, bool is_instanced) override; | 71 | void Draw(bool is_indexed, u32 instance_count) override; |
| 72 | void Clear() override; | 72 | void Clear() override; |
| 73 | void DispatchCompute() override; | 73 | void DispatchCompute() override; |
| 74 | void ResetCounter(VideoCore::QueryType type) override; | 74 | void ResetCounter(VideoCore::QueryType type) override; |
| @@ -199,6 +199,8 @@ private: | |||
| 199 | /// End a transform feedback | 199 | /// End a transform feedback |
| 200 | void EndTransformFeedback(); | 200 | void EndTransformFeedback(); |
| 201 | 201 | ||
| 202 | void BindInlineIndexBuffer(); | ||
| 203 | |||
| 202 | Tegra::GPU& gpu; | 204 | Tegra::GPU& gpu; |
| 203 | 205 | ||
| 204 | const Device& device; | 206 | const Device& device; |
diff --git a/src/video_core/renderer_opengl/gl_shader_cache.cpp b/src/video_core/renderer_opengl/gl_shader_cache.cpp index 609f0a772..e94cfdb1a 100644 --- a/src/video_core/renderer_opengl/gl_shader_cache.cpp +++ b/src/video_core/renderer_opengl/gl_shader_cache.cpp | |||
| @@ -63,6 +63,7 @@ Shader::RuntimeInfo MakeRuntimeInfo(const GraphicsPipelineKey& key, | |||
| 63 | Shader::RuntimeInfo info; | 63 | Shader::RuntimeInfo info; |
| 64 | if (previous_program) { | 64 | if (previous_program) { |
| 65 | info.previous_stage_stores = previous_program->info.stores; | 65 | info.previous_stage_stores = previous_program->info.stores; |
| 66 | info.previous_stage_legacy_stores_mapping = previous_program->info.legacy_stores_mapping; | ||
| 66 | } else { | 67 | } else { |
| 67 | // Mark all stores as available for vertex shaders | 68 | // Mark all stores as available for vertex shaders |
| 68 | info.previous_stage_stores.mask.set(); | 69 | info.previous_stage_stores.mask.set(); |
diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp index 20f1d6584..13d5a1f67 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp | |||
| @@ -134,6 +134,7 @@ Shader::RuntimeInfo MakeRuntimeInfo(std::span<const Shader::IR::Program> program | |||
| 134 | Shader::RuntimeInfo info; | 134 | Shader::RuntimeInfo info; |
| 135 | if (previous_program) { | 135 | if (previous_program) { |
| 136 | info.previous_stage_stores = previous_program->info.stores; | 136 | info.previous_stage_stores = previous_program->info.stores; |
| 137 | info.previous_stage_legacy_stores_mapping = previous_program->info.legacy_stores_mapping; | ||
| 137 | if (previous_program->is_geometry_passthrough) { | 138 | if (previous_program->is_geometry_passthrough) { |
| 138 | info.previous_stage_stores.mask |= previous_program->info.passthrough.mask; | 139 | info.previous_stage_stores.mask |= previous_program->info.passthrough.mask; |
| 139 | } | 140 | } |
diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index 6d5ea99dd..d94dbf873 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp | |||
| @@ -127,11 +127,10 @@ VkRect2D GetScissorState(const Maxwell& regs, size_t index, u32 up_scale = 1, u3 | |||
| 127 | return scissor; | 127 | return scissor; |
| 128 | } | 128 | } |
| 129 | 129 | ||
| 130 | DrawParams MakeDrawParams(const Maxwell& regs, u32 num_instances, bool is_instanced, | 130 | DrawParams MakeDrawParams(const Maxwell& regs, u32 num_instances, bool is_indexed) { |
| 131 | bool is_indexed) { | ||
| 132 | DrawParams params{ | 131 | DrawParams params{ |
| 133 | .base_instance = regs.global_base_instance_index, | 132 | .base_instance = regs.global_base_instance_index, |
| 134 | .num_instances = is_instanced ? num_instances : 1, | 133 | .num_instances = num_instances, |
| 135 | .base_vertex = is_indexed ? regs.global_base_vertex_index : regs.vertex_buffer.first, | 134 | .base_vertex = is_indexed ? regs.global_base_vertex_index : regs.vertex_buffer.first, |
| 136 | .num_vertices = is_indexed ? regs.index_buffer.count : regs.vertex_buffer.count, | 135 | .num_vertices = is_indexed ? regs.index_buffer.count : regs.vertex_buffer.count, |
| 137 | .first_index = is_indexed ? regs.index_buffer.first : 0, | 136 | .first_index = is_indexed ? regs.index_buffer.first : 0, |
| @@ -175,7 +174,7 @@ RasterizerVulkan::RasterizerVulkan(Core::Frontend::EmuWindow& emu_window_, Tegra | |||
| 175 | 174 | ||
| 176 | RasterizerVulkan::~RasterizerVulkan() = default; | 175 | RasterizerVulkan::~RasterizerVulkan() = default; |
| 177 | 176 | ||
| 178 | void RasterizerVulkan::Draw(bool is_indexed, bool is_instanced) { | 177 | void RasterizerVulkan::Draw(bool is_indexed, u32 instance_count) { |
| 179 | MICROPROFILE_SCOPE(Vulkan_Drawing); | 178 | MICROPROFILE_SCOPE(Vulkan_Drawing); |
| 180 | 179 | ||
| 181 | SCOPE_EXIT({ gpu.TickWork(); }); | 180 | SCOPE_EXIT({ gpu.TickWork(); }); |
| @@ -192,13 +191,15 @@ void RasterizerVulkan::Draw(bool is_indexed, bool is_instanced) { | |||
| 192 | pipeline->SetEngine(maxwell3d, gpu_memory); | 191 | pipeline->SetEngine(maxwell3d, gpu_memory); |
| 193 | pipeline->Configure(is_indexed); | 192 | pipeline->Configure(is_indexed); |
| 194 | 193 | ||
| 194 | BindInlineIndexBuffer(); | ||
| 195 | |||
| 195 | BeginTransformFeedback(); | 196 | BeginTransformFeedback(); |
| 196 | 197 | ||
| 197 | UpdateDynamicStates(); | 198 | UpdateDynamicStates(); |
| 198 | 199 | ||
| 199 | const auto& regs{maxwell3d->regs}; | 200 | const auto& regs{maxwell3d->regs}; |
| 200 | const u32 num_instances{maxwell3d->mme_draw.instance_count}; | 201 | const u32 num_instances{instance_count}; |
| 201 | const DrawParams draw_params{MakeDrawParams(regs, num_instances, is_instanced, is_indexed)}; | 202 | const DrawParams draw_params{MakeDrawParams(regs, num_instances, is_indexed)}; |
| 202 | scheduler.Record([draw_params](vk::CommandBuffer cmdbuf) { | 203 | scheduler.Record([draw_params](vk::CommandBuffer cmdbuf) { |
| 203 | if (draw_params.is_indexed) { | 204 | if (draw_params.is_indexed) { |
| 204 | cmdbuf.DrawIndexed(draw_params.num_vertices, draw_params.num_instances, | 205 | cmdbuf.DrawIndexed(draw_params.num_vertices, draw_params.num_instances, |
| @@ -302,14 +303,19 @@ void RasterizerVulkan::Clear() { | |||
| 302 | } | 303 | } |
| 303 | } | 304 | } |
| 304 | 305 | ||
| 305 | scheduler.Record([color_attachment, clear_value, clear_rect](vk::CommandBuffer cmdbuf) { | 306 | if (regs.clear_surface.R && regs.clear_surface.G && regs.clear_surface.B && |
| 306 | const VkClearAttachment attachment{ | 307 | regs.clear_surface.A) { |
| 307 | .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, | 308 | scheduler.Record([color_attachment, clear_value, clear_rect](vk::CommandBuffer cmdbuf) { |
| 308 | .colorAttachment = color_attachment, | 309 | const VkClearAttachment attachment{ |
| 309 | .clearValue = clear_value, | 310 | .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, |
| 310 | }; | 311 | .colorAttachment = color_attachment, |
| 311 | cmdbuf.ClearAttachments(attachment, clear_rect); | 312 | .clearValue = clear_value, |
| 312 | }); | 313 | }; |
| 314 | cmdbuf.ClearAttachments(attachment, clear_rect); | ||
| 315 | }); | ||
| 316 | } else { | ||
| 317 | UNIMPLEMENTED_MSG("Unimplemented Clear only the specified channel"); | ||
| 318 | } | ||
| 313 | } | 319 | } |
| 314 | 320 | ||
| 315 | if (!use_depth && !use_stencil) { | 321 | if (!use_depth && !use_stencil) { |
| @@ -1007,4 +1013,17 @@ void RasterizerVulkan::ReleaseChannel(s32 channel_id) { | |||
| 1007 | query_cache.EraseChannel(channel_id); | 1013 | query_cache.EraseChannel(channel_id); |
| 1008 | } | 1014 | } |
| 1009 | 1015 | ||
| 1016 | void RasterizerVulkan::BindInlineIndexBuffer() { | ||
| 1017 | if (maxwell3d->inline_index_draw_indexes.empty()) { | ||
| 1018 | return; | ||
| 1019 | } | ||
| 1020 | const auto data_count = static_cast<u32>(maxwell3d->inline_index_draw_indexes.size()); | ||
| 1021 | auto buffer = buffer_cache_runtime.UploadStagingBuffer(data_count); | ||
| 1022 | std::memcpy(buffer.mapped_span.data(), maxwell3d->inline_index_draw_indexes.data(), data_count); | ||
| 1023 | buffer_cache_runtime.BindIndexBuffer( | ||
| 1024 | maxwell3d->regs.draw.topology, maxwell3d->regs.index_buffer.format, | ||
| 1025 | maxwell3d->regs.index_buffer.first, maxwell3d->regs.index_buffer.count, buffer.buffer, | ||
| 1026 | static_cast<u32>(buffer.offset), data_count); | ||
| 1027 | } | ||
| 1028 | |||
| 1010 | } // namespace Vulkan | 1029 | } // namespace Vulkan |
diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.h b/src/video_core/renderer_vulkan/vk_rasterizer.h index e7cdeaed1..b0bc306f5 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.h +++ b/src/video_core/renderer_vulkan/vk_rasterizer.h | |||
| @@ -64,7 +64,7 @@ public: | |||
| 64 | StateTracker& state_tracker_, Scheduler& scheduler_); | 64 | StateTracker& state_tracker_, Scheduler& scheduler_); |
| 65 | ~RasterizerVulkan() override; | 65 | ~RasterizerVulkan() override; |
| 66 | 66 | ||
| 67 | void Draw(bool is_indexed, bool is_instanced) override; | 67 | void Draw(bool is_indexed, u32 instance_count) override; |
| 68 | void Clear() override; | 68 | void Clear() override; |
| 69 | void DispatchCompute() override; | 69 | void DispatchCompute() override; |
| 70 | void ResetCounter(VideoCore::QueryType type) override; | 70 | void ResetCounter(VideoCore::QueryType type) override; |
| @@ -141,6 +141,8 @@ private: | |||
| 141 | 141 | ||
| 142 | void UpdateVertexInput(Tegra::Engines::Maxwell3D::Regs& regs); | 142 | void UpdateVertexInput(Tegra::Engines::Maxwell3D::Regs& regs); |
| 143 | 143 | ||
| 144 | void BindInlineIndexBuffer(); | ||
| 145 | |||
| 144 | Tegra::GPU& gpu; | 146 | Tegra::GPU& gpu; |
| 145 | 147 | ||
| 146 | ScreenInfo& screen_info; | 148 | ScreenInfo& screen_info; |
diff --git a/src/video_core/renderer_vulkan/vk_scheduler.h b/src/video_core/renderer_vulkan/vk_scheduler.h index c04aad08f..929216749 100644 --- a/src/video_core/renderer_vulkan/vk_scheduler.h +++ b/src/video_core/renderer_vulkan/vk_scheduler.h | |||
| @@ -144,7 +144,6 @@ private: | |||
| 144 | using FuncType = TypedCommand<T>; | 144 | using FuncType = TypedCommand<T>; |
| 145 | static_assert(sizeof(FuncType) < sizeof(data), "Lambda is too large"); | 145 | static_assert(sizeof(FuncType) < sizeof(data), "Lambda is too large"); |
| 146 | 146 | ||
| 147 | recorded_counts++; | ||
| 148 | command_offset = Common::AlignUp(command_offset, alignof(FuncType)); | 147 | command_offset = Common::AlignUp(command_offset, alignof(FuncType)); |
| 149 | if (command_offset > sizeof(data) - sizeof(FuncType)) { | 148 | if (command_offset > sizeof(data) - sizeof(FuncType)) { |
| 150 | return false; | 149 | return false; |
| @@ -166,7 +165,7 @@ private: | |||
| 166 | } | 165 | } |
| 167 | 166 | ||
| 168 | bool Empty() const { | 167 | bool Empty() const { |
| 169 | return recorded_counts == 0; | 168 | return command_offset == 0; |
| 170 | } | 169 | } |
| 171 | 170 | ||
| 172 | bool HasSubmit() const { | 171 | bool HasSubmit() const { |
| @@ -177,7 +176,6 @@ private: | |||
| 177 | Command* first = nullptr; | 176 | Command* first = nullptr; |
| 178 | Command* last = nullptr; | 177 | Command* last = nullptr; |
| 179 | 178 | ||
| 180 | size_t recorded_counts = 0; | ||
| 181 | size_t command_offset = 0; | 179 | size_t command_offset = 0; |
| 182 | bool submit = false; | 180 | bool submit = false; |
| 183 | alignas(std::max_align_t) std::array<u8, 0x8000> data{}; | 181 | alignas(std::max_align_t) std::array<u8, 0x8000> data{}; |
diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.cpp b/src/video_core/renderer_vulkan/vk_texture_cache.cpp index 20e56b0d1..853b80d8a 100644 --- a/src/video_core/renderer_vulkan/vk_texture_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_texture_cache.cpp | |||
| @@ -1787,17 +1787,17 @@ void Framebuffer::CreateFramebuffer(TextureCacheRuntime& runtime, | |||
| 1787 | 1787 | ||
| 1788 | const auto& resolution = runtime.resolution; | 1788 | const auto& resolution = runtime.resolution; |
| 1789 | 1789 | ||
| 1790 | u32 width = 0; | 1790 | u32 width = std::numeric_limits<u32>::max(); |
| 1791 | u32 height = 0; | 1791 | u32 height = std::numeric_limits<u32>::max(); |
| 1792 | for (size_t index = 0; index < NUM_RT; ++index) { | 1792 | for (size_t index = 0; index < NUM_RT; ++index) { |
| 1793 | const ImageView* const color_buffer = color_buffers[index]; | 1793 | const ImageView* const color_buffer = color_buffers[index]; |
| 1794 | if (!color_buffer) { | 1794 | if (!color_buffer) { |
| 1795 | renderpass_key.color_formats[index] = PixelFormat::Invalid; | 1795 | renderpass_key.color_formats[index] = PixelFormat::Invalid; |
| 1796 | continue; | 1796 | continue; |
| 1797 | } | 1797 | } |
| 1798 | width = std::max(width, is_rescaled ? resolution.ScaleUp(color_buffer->size.width) | 1798 | width = std::min(width, is_rescaled ? resolution.ScaleUp(color_buffer->size.width) |
| 1799 | : color_buffer->size.width); | 1799 | : color_buffer->size.width); |
| 1800 | height = std::max(height, is_rescaled ? resolution.ScaleUp(color_buffer->size.height) | 1800 | height = std::min(height, is_rescaled ? resolution.ScaleUp(color_buffer->size.height) |
| 1801 | : color_buffer->size.height); | 1801 | : color_buffer->size.height); |
| 1802 | attachments.push_back(color_buffer->RenderTarget()); | 1802 | attachments.push_back(color_buffer->RenderTarget()); |
| 1803 | renderpass_key.color_formats[index] = color_buffer->format; | 1803 | renderpass_key.color_formats[index] = color_buffer->format; |
| @@ -1809,9 +1809,9 @@ void Framebuffer::CreateFramebuffer(TextureCacheRuntime& runtime, | |||
| 1809 | } | 1809 | } |
| 1810 | const size_t num_colors = attachments.size(); | 1810 | const size_t num_colors = attachments.size(); |
| 1811 | if (depth_buffer) { | 1811 | if (depth_buffer) { |
| 1812 | width = std::max(width, is_rescaled ? resolution.ScaleUp(depth_buffer->size.width) | 1812 | width = std::min(width, is_rescaled ? resolution.ScaleUp(depth_buffer->size.width) |
| 1813 | : depth_buffer->size.width); | 1813 | : depth_buffer->size.width); |
| 1814 | height = std::max(height, is_rescaled ? resolution.ScaleUp(depth_buffer->size.height) | 1814 | height = std::min(height, is_rescaled ? resolution.ScaleUp(depth_buffer->size.height) |
| 1815 | : depth_buffer->size.height); | 1815 | : depth_buffer->size.height); |
| 1816 | attachments.push_back(depth_buffer->RenderTarget()); | 1816 | attachments.push_back(depth_buffer->RenderTarget()); |
| 1817 | renderpass_key.depth_format = depth_buffer->format; | 1817 | renderpass_key.depth_format = depth_buffer->format; |
diff --git a/src/video_core/texture_cache/format_lookup_table.cpp b/src/video_core/texture_cache/format_lookup_table.cpp index ad935d386..08aa8ca33 100644 --- a/src/video_core/texture_cache/format_lookup_table.cpp +++ b/src/video_core/texture_cache/format_lookup_table.cpp | |||
| @@ -150,6 +150,8 @@ PixelFormat PixelFormatFromTextureInfo(TextureFormat format, ComponentType red, | |||
| 150 | return PixelFormat::D24_UNORM_S8_UINT; | 150 | return PixelFormat::D24_UNORM_S8_UINT; |
| 151 | case Hash(TextureFormat::D32S8, FLOAT, UINT, UNORM, UNORM, LINEAR): | 151 | case Hash(TextureFormat::D32S8, FLOAT, UINT, UNORM, UNORM, LINEAR): |
| 152 | return PixelFormat::D32_FLOAT_S8_UINT; | 152 | return PixelFormat::D32_FLOAT_S8_UINT; |
| 153 | case Hash(TextureFormat::R32_B24G8, FLOAT, UINT, UNORM, UNORM, LINEAR): | ||
| 154 | return PixelFormat::D32_FLOAT_S8_UINT; | ||
| 153 | case Hash(TextureFormat::BC1_RGBA, UNORM, LINEAR): | 155 | case Hash(TextureFormat::BC1_RGBA, UNORM, LINEAR): |
| 154 | return PixelFormat::BC1_RGBA_UNORM; | 156 | return PixelFormat::BC1_RGBA_UNORM; |
| 155 | case Hash(TextureFormat::BC1_RGBA, UNORM, SRGB): | 157 | case Hash(TextureFormat::BC1_RGBA, UNORM, SRGB): |
diff --git a/src/video_core/texture_cache/texture_cache.h b/src/video_core/texture_cache/texture_cache.h index 0e0fd410f..8ef75fe73 100644 --- a/src/video_core/texture_cache/texture_cache.h +++ b/src/video_core/texture_cache/texture_cache.h | |||
| @@ -442,7 +442,7 @@ void TextureCache<P>::WriteMemory(VAddr cpu_addr, size_t size) { | |||
| 442 | template <class P> | 442 | template <class P> |
| 443 | void TextureCache<P>::DownloadMemory(VAddr cpu_addr, size_t size) { | 443 | void TextureCache<P>::DownloadMemory(VAddr cpu_addr, size_t size) { |
| 444 | std::vector<ImageId> images; | 444 | std::vector<ImageId> images; |
| 445 | ForEachImageInRegion(cpu_addr, size, [this, &images](ImageId image_id, ImageBase& image) { | 445 | ForEachImageInRegion(cpu_addr, size, [&images](ImageId image_id, ImageBase& image) { |
| 446 | if (!image.IsSafeDownload()) { | 446 | if (!image.IsSafeDownload()) { |
| 447 | return; | 447 | return; |
| 448 | } | 448 | } |
| @@ -1502,9 +1502,9 @@ void TextureCache<P>::UnregisterImage(ImageId image_id) { | |||
| 1502 | image.flags &= ~ImageFlagBits::BadOverlap; | 1502 | image.flags &= ~ImageFlagBits::BadOverlap; |
| 1503 | lru_cache.Free(image.lru_index); | 1503 | lru_cache.Free(image.lru_index); |
| 1504 | const auto& clear_page_table = | 1504 | const auto& clear_page_table = |
| 1505 | [this, image_id](u64 page, | 1505 | [image_id](u64 page, |
| 1506 | std::unordered_map<u64, std::vector<ImageId>, Common::IdentityHash<u64>>& | 1506 | std::unordered_map<u64, std::vector<ImageId>, Common::IdentityHash<u64>>& |
| 1507 | selected_page_table) { | 1507 | selected_page_table) { |
| 1508 | const auto page_it = selected_page_table.find(page); | 1508 | const auto page_it = selected_page_table.find(page); |
| 1509 | if (page_it == selected_page_table.end()) { | 1509 | if (page_it == selected_page_table.end()) { |
| 1510 | ASSERT_MSG(false, "Unregistering unregistered page=0x{:x}", page << YUZU_PAGEBITS); | 1510 | ASSERT_MSG(false, "Unregistering unregistered page=0x{:x}", page << YUZU_PAGEBITS); |
diff --git a/src/video_core/textures/astc.cpp b/src/video_core/textures/astc.cpp index 15b9d4182..69a32819a 100644 --- a/src/video_core/textures/astc.cpp +++ b/src/video_core/textures/astc.cpp | |||
| @@ -1661,8 +1661,8 @@ void Decompress(std::span<const uint8_t> data, uint32_t width, uint32_t height, | |||
| 1661 | for (u32 z = 0; z < depth; ++z) { | 1661 | for (u32 z = 0; z < depth; ++z) { |
| 1662 | const u32 depth_offset = z * height * width * 4; | 1662 | const u32 depth_offset = z * height * width * 4; |
| 1663 | for (u32 y_index = 0; y_index < rows; ++y_index) { | 1663 | for (u32 y_index = 0; y_index < rows; ++y_index) { |
| 1664 | auto decompress_stride = [data, width, height, depth, block_width, block_height, output, | 1664 | auto decompress_stride = [data, width, height, block_width, block_height, output, rows, |
| 1665 | rows, cols, z, depth_offset, y_index] { | 1665 | cols, z, depth_offset, y_index] { |
| 1666 | const u32 y = y_index * block_height; | 1666 | const u32 y = y_index * block_height; |
| 1667 | for (u32 x_index = 0; x_index < cols; ++x_index) { | 1667 | for (u32 x_index = 0; x_index < cols; ++x_index) { |
| 1668 | const u32 block_index = (z * rows * cols) + (y_index * cols) + x_index; | 1668 | const u32 block_index = (z * rows * cols) + (y_index * cols) + x_index; |
diff --git a/src/video_core/textures/decoders.cpp b/src/video_core/textures/decoders.cpp index 52d067a2d..fd1a4b987 100644 --- a/src/video_core/textures/decoders.cpp +++ b/src/video_core/textures/decoders.cpp | |||
| @@ -21,7 +21,7 @@ constexpr u32 pdep(u32 value) { | |||
| 21 | u32 m = mask; | 21 | u32 m = mask; |
| 22 | for (u32 bit = 1; m; bit += bit) { | 22 | for (u32 bit = 1; m; bit += bit) { |
| 23 | if (value & bit) | 23 | if (value & bit) |
| 24 | result |= m & -m; | 24 | result |= m & (~m + 1); |
| 25 | m &= m - 1; | 25 | m &= m - 1; |
| 26 | } | 26 | } |
| 27 | return result; | 27 | return result; |
diff --git a/src/yuzu/applets/qt_controller.ui b/src/yuzu/applets/qt_controller.ui index c8cb6bcf3..f5eccba70 100644 --- a/src/yuzu/applets/qt_controller.ui +++ b/src/yuzu/applets/qt_controller.ui | |||
| @@ -2300,7 +2300,7 @@ | |||
| 2300 | <item> | 2300 | <item> |
| 2301 | <widget class="QRadioButton" name="radioUndocked"> | 2301 | <widget class="QRadioButton" name="radioUndocked"> |
| 2302 | <property name="text"> | 2302 | <property name="text"> |
| 2303 | <string>Undocked</string> | 2303 | <string>Handheld</string> |
| 2304 | </property> | 2304 | </property> |
| 2305 | </widget> | 2305 | </widget> |
| 2306 | </item> | 2306 | </item> |
diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp index 195074bf2..927dd1069 100644 --- a/src/yuzu/configuration/config.cpp +++ b/src/yuzu/configuration/config.cpp | |||
| @@ -819,6 +819,7 @@ void Config::ReadUIGamelistValues() { | |||
| 819 | qt_config->beginGroup(QStringLiteral("UIGameList")); | 819 | qt_config->beginGroup(QStringLiteral("UIGameList")); |
| 820 | 820 | ||
| 821 | ReadBasicSetting(UISettings::values.show_add_ons); | 821 | ReadBasicSetting(UISettings::values.show_add_ons); |
| 822 | ReadBasicSetting(UISettings::values.show_compat); | ||
| 822 | ReadBasicSetting(UISettings::values.game_icon_size); | 823 | ReadBasicSetting(UISettings::values.game_icon_size); |
| 823 | ReadBasicSetting(UISettings::values.folder_icon_size); | 824 | ReadBasicSetting(UISettings::values.folder_icon_size); |
| 824 | ReadBasicSetting(UISettings::values.row_1_text_id); | 825 | ReadBasicSetting(UISettings::values.row_1_text_id); |
| @@ -1414,6 +1415,7 @@ void Config::SaveUIGamelistValues() { | |||
| 1414 | qt_config->beginGroup(QStringLiteral("UIGameList")); | 1415 | qt_config->beginGroup(QStringLiteral("UIGameList")); |
| 1415 | 1416 | ||
| 1416 | WriteBasicSetting(UISettings::values.show_add_ons); | 1417 | WriteBasicSetting(UISettings::values.show_add_ons); |
| 1418 | WriteBasicSetting(UISettings::values.show_compat); | ||
| 1417 | WriteBasicSetting(UISettings::values.game_icon_size); | 1419 | WriteBasicSetting(UISettings::values.game_icon_size); |
| 1418 | WriteBasicSetting(UISettings::values.folder_icon_size); | 1420 | WriteBasicSetting(UISettings::values.folder_icon_size); |
| 1419 | WriteBasicSetting(UISettings::values.row_1_text_id); | 1421 | WriteBasicSetting(UISettings::values.row_1_text_id); |
diff --git a/src/yuzu/configuration/configure_ui.cpp b/src/yuzu/configuration/configure_ui.cpp index 48f71b53c..92e6da6ee 100644 --- a/src/yuzu/configuration/configure_ui.cpp +++ b/src/yuzu/configuration/configure_ui.cpp | |||
| @@ -72,6 +72,7 @@ ConfigureUi::ConfigureUi(Core::System& system_, QWidget* parent) | |||
| 72 | 72 | ||
| 73 | // Force game list reload if any of the relevant settings are changed. | 73 | // Force game list reload if any of the relevant settings are changed. |
| 74 | connect(ui->show_add_ons, &QCheckBox::stateChanged, this, &ConfigureUi::RequestGameListUpdate); | 74 | connect(ui->show_add_ons, &QCheckBox::stateChanged, this, &ConfigureUi::RequestGameListUpdate); |
| 75 | connect(ui->show_compat, &QCheckBox::stateChanged, this, &ConfigureUi::RequestGameListUpdate); | ||
| 75 | connect(ui->game_icon_size_combobox, QOverload<int>::of(&QComboBox::currentIndexChanged), this, | 76 | connect(ui->game_icon_size_combobox, QOverload<int>::of(&QComboBox::currentIndexChanged), this, |
| 76 | &ConfigureUi::RequestGameListUpdate); | 77 | &ConfigureUi::RequestGameListUpdate); |
| 77 | connect(ui->folder_icon_size_combobox, QOverload<int>::of(&QComboBox::currentIndexChanged), | 78 | connect(ui->folder_icon_size_combobox, QOverload<int>::of(&QComboBox::currentIndexChanged), |
| @@ -109,6 +110,7 @@ void ConfigureUi::ApplyConfiguration() { | |||
| 109 | UISettings::values.theme = | 110 | UISettings::values.theme = |
| 110 | ui->theme_combobox->itemData(ui->theme_combobox->currentIndex()).toString(); | 111 | ui->theme_combobox->itemData(ui->theme_combobox->currentIndex()).toString(); |
| 111 | UISettings::values.show_add_ons = ui->show_add_ons->isChecked(); | 112 | UISettings::values.show_add_ons = ui->show_add_ons->isChecked(); |
| 113 | UISettings::values.show_compat = ui->show_compat->isChecked(); | ||
| 112 | UISettings::values.game_icon_size = ui->game_icon_size_combobox->currentData().toUInt(); | 114 | UISettings::values.game_icon_size = ui->game_icon_size_combobox->currentData().toUInt(); |
| 113 | UISettings::values.folder_icon_size = ui->folder_icon_size_combobox->currentData().toUInt(); | 115 | UISettings::values.folder_icon_size = ui->folder_icon_size_combobox->currentData().toUInt(); |
| 114 | UISettings::values.row_1_text_id = ui->row_1_text_combobox->currentData().toUInt(); | 116 | UISettings::values.row_1_text_id = ui->row_1_text_combobox->currentData().toUInt(); |
| @@ -129,6 +131,7 @@ void ConfigureUi::SetConfiguration() { | |||
| 129 | ui->language_combobox->setCurrentIndex( | 131 | ui->language_combobox->setCurrentIndex( |
| 130 | ui->language_combobox->findData(UISettings::values.language)); | 132 | ui->language_combobox->findData(UISettings::values.language)); |
| 131 | ui->show_add_ons->setChecked(UISettings::values.show_add_ons.GetValue()); | 133 | ui->show_add_ons->setChecked(UISettings::values.show_add_ons.GetValue()); |
| 134 | ui->show_compat->setChecked(UISettings::values.show_compat.GetValue()); | ||
| 132 | ui->game_icon_size_combobox->setCurrentIndex( | 135 | ui->game_icon_size_combobox->setCurrentIndex( |
| 133 | ui->game_icon_size_combobox->findData(UISettings::values.game_icon_size.GetValue())); | 136 | ui->game_icon_size_combobox->findData(UISettings::values.game_icon_size.GetValue())); |
| 134 | ui->folder_icon_size_combobox->setCurrentIndex( | 137 | ui->folder_icon_size_combobox->setCurrentIndex( |
diff --git a/src/yuzu/configuration/configure_ui.ui b/src/yuzu/configuration/configure_ui.ui index a50df7f6f..f0b719ba3 100644 --- a/src/yuzu/configuration/configure_ui.ui +++ b/src/yuzu/configuration/configure_ui.ui | |||
| @@ -77,6 +77,13 @@ | |||
| 77 | <item> | 77 | <item> |
| 78 | <layout class="QVBoxLayout" name="GeneralVerticalLayout"> | 78 | <layout class="QVBoxLayout" name="GeneralVerticalLayout"> |
| 79 | <item> | 79 | <item> |
| 80 | <widget class="QCheckBox" name="show_compat"> | ||
| 81 | <property name="text"> | ||
| 82 | <string>Show Compatibility List</string> | ||
| 83 | </property> | ||
| 84 | </widget> | ||
| 85 | </item> | ||
| 86 | <item> | ||
| 80 | <widget class="QCheckBox" name="show_add_ons"> | 87 | <widget class="QCheckBox" name="show_add_ons"> |
| 81 | <property name="text"> | 88 | <property name="text"> |
| 82 | <string>Show Add-Ons Column</string> | 89 | <string>Show Add-Ons Column</string> |
diff --git a/src/yuzu/game_list.cpp b/src/yuzu/game_list.cpp index b127badc2..d6adfca16 100644 --- a/src/yuzu/game_list.cpp +++ b/src/yuzu/game_list.cpp | |||
| @@ -335,6 +335,7 @@ GameList::GameList(FileSys::VirtualFilesystem vfs_, FileSys::ManualContentProvid | |||
| 335 | RetranslateUI(); | 335 | RetranslateUI(); |
| 336 | 336 | ||
| 337 | tree_view->setColumnHidden(COLUMN_ADD_ONS, !UISettings::values.show_add_ons); | 337 | tree_view->setColumnHidden(COLUMN_ADD_ONS, !UISettings::values.show_add_ons); |
| 338 | tree_view->setColumnHidden(COLUMN_COMPATIBILITY, !UISettings::values.show_compat); | ||
| 338 | item_model->setSortRole(GameListItemPath::SortRole); | 339 | item_model->setSortRole(GameListItemPath::SortRole); |
| 339 | 340 | ||
| 340 | connect(main_window, &GMainWindow::UpdateThemedIcons, this, &GameList::OnUpdateThemedIcons); | 341 | connect(main_window, &GMainWindow::UpdateThemedIcons, this, &GameList::OnUpdateThemedIcons); |
| @@ -786,6 +787,7 @@ void GameList::PopulateAsync(QVector<UISettings::GameDir>& game_dirs) { | |||
| 786 | 787 | ||
| 787 | // Update the columns in case UISettings has changed | 788 | // Update the columns in case UISettings has changed |
| 788 | tree_view->setColumnHidden(COLUMN_ADD_ONS, !UISettings::values.show_add_ons); | 789 | tree_view->setColumnHidden(COLUMN_ADD_ONS, !UISettings::values.show_add_ons); |
| 790 | tree_view->setColumnHidden(COLUMN_COMPATIBILITY, !UISettings::values.show_compat); | ||
| 789 | 791 | ||
| 790 | // Delete any rows that might already exist if we're repopulating | 792 | // Delete any rows that might already exist if we're repopulating |
| 791 | item_model->removeRows(0, item_model->rowCount()); | 793 | item_model->removeRows(0, item_model->rowCount()); |
diff --git a/src/yuzu/main.ui b/src/yuzu/main.ui index 74d49dbd4..e670acc30 100644 --- a/src/yuzu/main.ui +++ b/src/yuzu/main.ui | |||
| @@ -55,7 +55,6 @@ | |||
| 55 | <addaction name="separator"/> | 55 | <addaction name="separator"/> |
| 56 | <addaction name="menu_recent_files"/> | 56 | <addaction name="menu_recent_files"/> |
| 57 | <addaction name="separator"/> | 57 | <addaction name="separator"/> |
| 58 | <addaction name="separator"/> | ||
| 59 | <addaction name="action_Load_Amiibo"/> | 58 | <addaction name="action_Load_Amiibo"/> |
| 60 | <addaction name="separator"/> | 59 | <addaction name="separator"/> |
| 61 | <addaction name="action_Open_yuzu_Folder"/> | 60 | <addaction name="action_Open_yuzu_Folder"/> |
diff --git a/src/yuzu/multiplayer/state.cpp b/src/yuzu/multiplayer/state.cpp index ae2738ad4..285bb150d 100644 --- a/src/yuzu/multiplayer/state.cpp +++ b/src/yuzu/multiplayer/state.cpp | |||
| @@ -268,7 +268,7 @@ bool MultiplayerState::OnCloseRoom() { | |||
| 268 | return true; | 268 | return true; |
| 269 | } | 269 | } |
| 270 | // Save ban list | 270 | // Save ban list |
| 271 | UISettings::values.multiplayer_ban_list = std::move(room->GetBanList()); | 271 | UISettings::values.multiplayer_ban_list = room->GetBanList(); |
| 272 | 272 | ||
| 273 | room->Destroy(); | 273 | room->Destroy(); |
| 274 | announce_multiplayer_session->Stop(); | 274 | announce_multiplayer_session->Stop(); |
diff --git a/src/yuzu/startup_checks.cpp b/src/yuzu/startup_checks.cpp index fc2693f9d..6a91212e2 100644 --- a/src/yuzu/startup_checks.cpp +++ b/src/yuzu/startup_checks.cpp | |||
| @@ -49,7 +49,7 @@ bool CheckEnvVars(bool* is_child) { | |||
| 49 | *is_child = true; | 49 | *is_child = true; |
| 50 | return false; | 50 | return false; |
| 51 | } else if (!SetEnvironmentVariableA(IS_CHILD_ENV_VAR, ENV_VAR_ENABLED_TEXT)) { | 51 | } else if (!SetEnvironmentVariableA(IS_CHILD_ENV_VAR, ENV_VAR_ENABLED_TEXT)) { |
| 52 | std::fprintf(stderr, "SetEnvironmentVariableA failed to set %s with error %d\n", | 52 | std::fprintf(stderr, "SetEnvironmentVariableA failed to set %s with error %lu\n", |
| 53 | IS_CHILD_ENV_VAR, GetLastError()); | 53 | IS_CHILD_ENV_VAR, GetLastError()); |
| 54 | return true; | 54 | return true; |
| 55 | } | 55 | } |
| @@ -62,7 +62,7 @@ bool StartupChecks(const char* arg0, bool* has_broken_vulkan, bool perform_vulka | |||
| 62 | // Set the startup variable for child processes | 62 | // Set the startup variable for child processes |
| 63 | const bool env_var_set = SetEnvironmentVariableA(STARTUP_CHECK_ENV_VAR, ENV_VAR_ENABLED_TEXT); | 63 | const bool env_var_set = SetEnvironmentVariableA(STARTUP_CHECK_ENV_VAR, ENV_VAR_ENABLED_TEXT); |
| 64 | if (!env_var_set) { | 64 | if (!env_var_set) { |
| 65 | std::fprintf(stderr, "SetEnvironmentVariableA failed to set %s with error %d\n", | 65 | std::fprintf(stderr, "SetEnvironmentVariableA failed to set %s with error %lu\n", |
| 66 | STARTUP_CHECK_ENV_VAR, GetLastError()); | 66 | STARTUP_CHECK_ENV_VAR, GetLastError()); |
| 67 | return false; | 67 | return false; |
| 68 | } | 68 | } |
| @@ -81,22 +81,22 @@ bool StartupChecks(const char* arg0, bool* has_broken_vulkan, bool perform_vulka | |||
| 81 | DWORD exit_code = STILL_ACTIVE; | 81 | DWORD exit_code = STILL_ACTIVE; |
| 82 | const int err = GetExitCodeProcess(process_info.hProcess, &exit_code); | 82 | const int err = GetExitCodeProcess(process_info.hProcess, &exit_code); |
| 83 | if (err == 0) { | 83 | if (err == 0) { |
| 84 | std::fprintf(stderr, "GetExitCodeProcess failed with error %d\n", GetLastError()); | 84 | std::fprintf(stderr, "GetExitCodeProcess failed with error %lu\n", GetLastError()); |
| 85 | } | 85 | } |
| 86 | 86 | ||
| 87 | // Vulkan is broken if the child crashed (return value is not zero) | 87 | // Vulkan is broken if the child crashed (return value is not zero) |
| 88 | *has_broken_vulkan = (exit_code != 0); | 88 | *has_broken_vulkan = (exit_code != 0); |
| 89 | 89 | ||
| 90 | if (CloseHandle(process_info.hProcess) == 0) { | 90 | if (CloseHandle(process_info.hProcess) == 0) { |
| 91 | std::fprintf(stderr, "CloseHandle failed with error %d\n", GetLastError()); | 91 | std::fprintf(stderr, "CloseHandle failed with error %lu\n", GetLastError()); |
| 92 | } | 92 | } |
| 93 | if (CloseHandle(process_info.hThread) == 0) { | 93 | if (CloseHandle(process_info.hThread) == 0) { |
| 94 | std::fprintf(stderr, "CloseHandle failed with error %d\n", GetLastError()); | 94 | std::fprintf(stderr, "CloseHandle failed with error %lu\n", GetLastError()); |
| 95 | } | 95 | } |
| 96 | } | 96 | } |
| 97 | 97 | ||
| 98 | if (!SetEnvironmentVariableA(STARTUP_CHECK_ENV_VAR, nullptr)) { | 98 | if (!SetEnvironmentVariableA(STARTUP_CHECK_ENV_VAR, nullptr)) { |
| 99 | std::fprintf(stderr, "SetEnvironmentVariableA failed to clear %s with error %d\n", | 99 | std::fprintf(stderr, "SetEnvironmentVariableA failed to clear %s with error %lu\n", |
| 100 | STARTUP_CHECK_ENV_VAR, GetLastError()); | 100 | STARTUP_CHECK_ENV_VAR, GetLastError()); |
| 101 | } | 101 | } |
| 102 | 102 | ||
| @@ -135,7 +135,8 @@ bool SpawnChild(const char* arg0, PROCESS_INFORMATION* pi, int flags) { | |||
| 135 | startup_info.cb = sizeof(startup_info); | 135 | startup_info.cb = sizeof(startup_info); |
| 136 | 136 | ||
| 137 | char p_name[255]; | 137 | char p_name[255]; |
| 138 | std::strncpy(p_name, arg0, 255); | 138 | std::strncpy(p_name, arg0, 254); |
| 139 | p_name[254] = '\0'; | ||
| 139 | 140 | ||
| 140 | const bool process_created = CreateProcessA(nullptr, // lpApplicationName | 141 | const bool process_created = CreateProcessA(nullptr, // lpApplicationName |
| 141 | p_name, // lpCommandLine | 142 | p_name, // lpCommandLine |
| @@ -149,7 +150,7 @@ bool SpawnChild(const char* arg0, PROCESS_INFORMATION* pi, int flags) { | |||
| 149 | pi // lpProcessInformation | 150 | pi // lpProcessInformation |
| 150 | ); | 151 | ); |
| 151 | if (!process_created) { | 152 | if (!process_created) { |
| 152 | std::fprintf(stderr, "CreateProcessA failed with error %d\n", GetLastError()); | 153 | std::fprintf(stderr, "CreateProcessA failed with error %lu\n", GetLastError()); |
| 153 | return false; | 154 | return false; |
| 154 | } | 155 | } |
| 155 | 156 | ||
diff --git a/src/yuzu/uisettings.h b/src/yuzu/uisettings.h index 753797efc..4f5b2a99d 100644 --- a/src/yuzu/uisettings.h +++ b/src/yuzu/uisettings.h | |||
| @@ -129,6 +129,9 @@ struct Values { | |||
| 129 | Settings::Setting<bool> favorites_expanded{true, "favorites_expanded"}; | 129 | Settings::Setting<bool> favorites_expanded{true, "favorites_expanded"}; |
| 130 | QVector<u64> favorited_ids; | 130 | QVector<u64> favorited_ids; |
| 131 | 131 | ||
| 132 | // Compatibility List | ||
| 133 | Settings::Setting<bool> show_compat{false, "show_compat"}; | ||
| 134 | |||
| 132 | bool configuration_applied; | 135 | bool configuration_applied; |
| 133 | bool reset_to_defaults; | 136 | bool reset_to_defaults; |
| 134 | Settings::Setting<bool> disable_web_applet{true, "disable_web_applet"}; | 137 | Settings::Setting<bool> disable_web_applet{true, "disable_web_applet"}; |