diff options
Diffstat (limited to 'src')
217 files changed, 4558 insertions, 2852 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index fc177fa52..54de1dc94 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt | |||
| @@ -162,6 +162,7 @@ add_subdirectory(video_core) | |||
| 162 | add_subdirectory(network) | 162 | add_subdirectory(network) |
| 163 | add_subdirectory(input_common) | 163 | add_subdirectory(input_common) |
| 164 | add_subdirectory(shader_recompiler) | 164 | add_subdirectory(shader_recompiler) |
| 165 | add_subdirectory(dedicated_room) | ||
| 165 | 166 | ||
| 166 | if (YUZU_TESTS) | 167 | if (YUZU_TESTS) |
| 167 | add_subdirectory(tests) | 168 | add_subdirectory(tests) |
diff --git a/src/audio_core/CMakeLists.txt b/src/audio_core/CMakeLists.txt index 5fe1d5fa5..144f1bab2 100644 --- a/src/audio_core/CMakeLists.txt +++ b/src/audio_core/CMakeLists.txt | |||
| @@ -194,6 +194,7 @@ add_library(audio_core STATIC | |||
| 194 | sink/sink.h | 194 | sink/sink.h |
| 195 | sink/sink_details.cpp | 195 | sink/sink_details.cpp |
| 196 | sink/sink_details.h | 196 | sink/sink_details.h |
| 197 | sink/sink_stream.cpp | ||
| 197 | sink/sink_stream.h | 198 | sink/sink_stream.h |
| 198 | ) | 199 | ) |
| 199 | 200 | ||
diff --git a/src/audio_core/audio_core.cpp b/src/audio_core/audio_core.cpp index 78e615a10..c845330cd 100644 --- a/src/audio_core/audio_core.cpp +++ b/src/audio_core/audio_core.cpp | |||
| @@ -47,22 +47,12 @@ AudioRenderer::ADSP::ADSP& AudioCore::GetADSP() { | |||
| 47 | return *adsp; | 47 | return *adsp; |
| 48 | } | 48 | } |
| 49 | 49 | ||
| 50 | void AudioCore::PauseSinks(const bool pausing) const { | 50 | void AudioCore::SetNVDECActive(bool active) { |
| 51 | if (pausing) { | 51 | nvdec_active = active; |
| 52 | output_sink->PauseStreams(); | ||
| 53 | input_sink->PauseStreams(); | ||
| 54 | } else { | ||
| 55 | output_sink->UnpauseStreams(); | ||
| 56 | input_sink->UnpauseStreams(); | ||
| 57 | } | ||
| 58 | } | 52 | } |
| 59 | 53 | ||
| 60 | u32 AudioCore::GetStreamQueue() const { | 54 | bool AudioCore::IsNVDECActive() const { |
| 61 | return estimated_queue.load(); | 55 | return nvdec_active; |
| 62 | } | ||
| 63 | |||
| 64 | void AudioCore::SetStreamQueue(u32 size) { | ||
| 65 | estimated_queue.store(size); | ||
| 66 | } | 56 | } |
| 67 | 57 | ||
| 68 | } // namespace AudioCore | 58 | } // namespace AudioCore |
diff --git a/src/audio_core/audio_core.h b/src/audio_core/audio_core.h index 0f7d61ee4..e33e00a3e 100644 --- a/src/audio_core/audio_core.h +++ b/src/audio_core/audio_core.h | |||
| @@ -17,7 +17,7 @@ namespace AudioCore { | |||
| 17 | 17 | ||
| 18 | class AudioManager; | 18 | class AudioManager; |
| 19 | /** | 19 | /** |
| 20 | * Main audio class, sotred inside the core, and holding the audio manager, all sinks, and the ADSP. | 20 | * Main audio class, stored inside the core, and holding the audio manager, all sinks, and the ADSP. |
| 21 | */ | 21 | */ |
| 22 | class AudioCore { | 22 | class AudioCore { |
| 23 | public: | 23 | public: |
| @@ -58,26 +58,16 @@ public: | |||
| 58 | AudioRenderer::ADSP::ADSP& GetADSP(); | 58 | AudioRenderer::ADSP::ADSP& GetADSP(); |
| 59 | 59 | ||
| 60 | /** | 60 | /** |
| 61 | * Pause the sink. Called from the core. | 61 | * Toggle NVDEC state, used to avoid stall in playback. |
| 62 | * | 62 | * |
| 63 | * @param pausing - Is this pause due to an actual pause, or shutdown? | 63 | * @param active - Set true if nvdec is active, otherwise false. |
| 64 | * Unfortunately, shutdown also pauses streams, which can cause issues. | ||
| 65 | */ | 64 | */ |
| 66 | void PauseSinks(bool pausing) const; | 65 | void SetNVDECActive(bool active); |
| 67 | 66 | ||
| 68 | /** | 67 | /** |
| 69 | * Get the size of the current stream queue. | 68 | * Get NVDEC state. |
| 70 | * | ||
| 71 | * @return Current stream queue size. | ||
| 72 | */ | ||
| 73 | u32 GetStreamQueue() const; | ||
| 74 | |||
| 75 | /** | ||
| 76 | * Get the size of the current stream queue. | ||
| 77 | * | ||
| 78 | * @param size - New stream size. | ||
| 79 | */ | 69 | */ |
| 80 | void SetStreamQueue(u32 size); | 70 | bool IsNVDECActive() const; |
| 81 | 71 | ||
| 82 | private: | 72 | private: |
| 83 | /** | 73 | /** |
| @@ -93,8 +83,8 @@ private: | |||
| 93 | std::unique_ptr<Sink::Sink> input_sink; | 83 | std::unique_ptr<Sink::Sink> input_sink; |
| 94 | /// The ADSP in the sysmodule | 84 | /// The ADSP in the sysmodule |
| 95 | std::unique_ptr<AudioRenderer::ADSP::ADSP> adsp; | 85 | std::unique_ptr<AudioRenderer::ADSP::ADSP> adsp; |
| 96 | /// Current size of the stream queue | 86 | /// Is NVDec currently active? |
| 97 | std::atomic<u32> estimated_queue{0}; | 87 | bool nvdec_active{false}; |
| 98 | }; | 88 | }; |
| 99 | 89 | ||
| 100 | } // namespace AudioCore | 90 | } // namespace AudioCore |
diff --git a/src/audio_core/audio_event.h b/src/audio_core/audio_event.h index 82dd32dca..012d2ed70 100644 --- a/src/audio_core/audio_event.h +++ b/src/audio_core/audio_event.h | |||
| @@ -14,7 +14,7 @@ namespace AudioCore { | |||
| 14 | * Responsible for the input/output events, set by the stream backend when buffers are consumed, and | 14 | * Responsible for the input/output events, set by the stream backend when buffers are consumed, and |
| 15 | * waited on by the audio manager. These callbacks signal the game's events to keep the audio buffer | 15 | * waited on by the audio manager. These callbacks signal the game's events to keep the audio buffer |
| 16 | * recycling going. | 16 | * recycling going. |
| 17 | * In a real Switch this is not a seprate class, and exists entirely within the audio manager. | 17 | * In a real Switch this is not a separate class, and exists entirely within the audio manager. |
| 18 | * On the Switch it's implemented more simply through a MultiWaitEventHolder, where it can | 18 | * On the Switch it's implemented more simply through a MultiWaitEventHolder, where it can |
| 19 | * wait on multiple events at once, and the events are not needed by the backend. | 19 | * wait on multiple events at once, and the events are not needed by the backend. |
| 20 | */ | 20 | */ |
| @@ -81,7 +81,7 @@ public: | |||
| 81 | void ClearEvents(); | 81 | void ClearEvents(); |
| 82 | 82 | ||
| 83 | private: | 83 | private: |
| 84 | /// Lock, used bythe audio manager | 84 | /// Lock, used by the audio manager |
| 85 | std::mutex event_lock; | 85 | std::mutex event_lock; |
| 86 | /// Array of events, one per system type (see Type), last event is used to terminate | 86 | /// Array of events, one per system type (see Type), last event is used to terminate |
| 87 | std::array<std::atomic<bool>, 4> events_signalled; | 87 | std::array<std::atomic<bool>, 4> events_signalled; |
diff --git a/src/audio_core/audio_in_manager.cpp b/src/audio_core/audio_in_manager.cpp index 4aadb7fd6..f39fb4002 100644 --- a/src/audio_core/audio_in_manager.cpp +++ b/src/audio_core/audio_in_manager.cpp | |||
| @@ -82,7 +82,7 @@ u32 Manager::GetDeviceNames(std::vector<AudioRenderer::AudioDevice::AudioDeviceN | |||
| 82 | 82 | ||
| 83 | auto input_devices{Sink::GetDeviceListForSink(Settings::values.sink_id.GetValue(), true)}; | 83 | auto input_devices{Sink::GetDeviceListForSink(Settings::values.sink_id.GetValue(), true)}; |
| 84 | if (input_devices.size() > 1) { | 84 | if (input_devices.size() > 1) { |
| 85 | names.push_back(AudioRenderer::AudioDevice::AudioDeviceName("Uac")); | 85 | names.emplace_back("Uac"); |
| 86 | return 1; | 86 | return 1; |
| 87 | } | 87 | } |
| 88 | return 0; | 88 | return 0; |
diff --git a/src/audio_core/audio_in_manager.h b/src/audio_core/audio_in_manager.h index 75b73a0b6..8a519df99 100644 --- a/src/audio_core/audio_in_manager.h +++ b/src/audio_core/audio_in_manager.h | |||
| @@ -59,9 +59,10 @@ public: | |||
| 59 | /** | 59 | /** |
| 60 | * Get a list of audio in device names. | 60 | * Get a list of audio in device names. |
| 61 | * | 61 | * |
| 62 | * @oaram names - Output container to write names to. | 62 | * @param names - Output container to write names to. |
| 63 | * @param max_count - Maximum numebr of deivce names to write. Unused | 63 | * @param max_count - Maximum number of device names to write. Unused |
| 64 | * @param filter - Should the list be filtered? Unused. | 64 | * @param filter - Should the list be filtered? Unused. |
| 65 | * | ||
| 65 | * @return Number of names written. | 66 | * @return Number of names written. |
| 66 | */ | 67 | */ |
| 67 | u32 GetDeviceNames(std::vector<AudioRenderer::AudioDevice::AudioDeviceName>& names, | 68 | u32 GetDeviceNames(std::vector<AudioRenderer::AudioDevice::AudioDeviceName>& names, |
diff --git a/src/audio_core/audio_manager.h b/src/audio_core/audio_manager.h index 70316e9cb..8cbd95e22 100644 --- a/src/audio_core/audio_manager.h +++ b/src/audio_core/audio_manager.h | |||
| @@ -76,7 +76,7 @@ public: | |||
| 76 | 76 | ||
| 77 | private: | 77 | private: |
| 78 | /** | 78 | /** |
| 79 | * Main thread, waiting on a manager signal and calling the registered fucntion. | 79 | * Main thread, waiting on a manager signal and calling the registered function. |
| 80 | */ | 80 | */ |
| 81 | void ThreadFunc(); | 81 | void ThreadFunc(); |
| 82 | 82 | ||
diff --git a/src/audio_core/audio_out_manager.cpp b/src/audio_core/audio_out_manager.cpp index 71d67de64..1766efde1 100644 --- a/src/audio_core/audio_out_manager.cpp +++ b/src/audio_core/audio_out_manager.cpp | |||
| @@ -74,7 +74,7 @@ void Manager::BufferReleaseAndRegister() { | |||
| 74 | 74 | ||
| 75 | u32 Manager::GetAudioOutDeviceNames( | 75 | u32 Manager::GetAudioOutDeviceNames( |
| 76 | std::vector<AudioRenderer::AudioDevice::AudioDeviceName>& names) const { | 76 | std::vector<AudioRenderer::AudioDevice::AudioDeviceName>& names) const { |
| 77 | names.push_back({"DeviceOut"}); | 77 | names.emplace_back("DeviceOut"); |
| 78 | return 1; | 78 | return 1; |
| 79 | } | 79 | } |
| 80 | 80 | ||
diff --git a/src/audio_core/audio_render_manager.h b/src/audio_core/audio_render_manager.h index 6a508ec56..7119e1b99 100644 --- a/src/audio_core/audio_render_manager.h +++ b/src/audio_core/audio_render_manager.h | |||
| @@ -64,10 +64,10 @@ public: | |||
| 64 | 64 | ||
| 65 | /** | 65 | /** |
| 66 | * Add a renderer system to the manager. | 66 | * Add a renderer system to the manager. |
| 67 | * The system will be reguarly called to generate commands for the AudioRenderer. | 67 | * The system will be regularly called to generate commands for the AudioRenderer. |
| 68 | * | 68 | * |
| 69 | * @param system - The system to add. | 69 | * @param system - The system to add. |
| 70 | * @return True if the system was sucessfully added, otherwise false. | 70 | * @return True if the system was successfully added, otherwise false. |
| 71 | */ | 71 | */ |
| 72 | bool AddSystem(System& system); | 72 | bool AddSystem(System& system); |
| 73 | 73 | ||
| @@ -75,7 +75,7 @@ public: | |||
| 75 | * Remove a renderer system from the manager. | 75 | * Remove a renderer system from the manager. |
| 76 | * | 76 | * |
| 77 | * @param system - The system to remove. | 77 | * @param system - The system to remove. |
| 78 | * @return True if the system was sucessfully removed, otherwise false. | 78 | * @return True if the system was successfully removed, otherwise false. |
| 79 | */ | 79 | */ |
| 80 | bool RemoveSystem(System& system); | 80 | bool RemoveSystem(System& system); |
| 81 | 81 | ||
diff --git a/src/audio_core/device/audio_buffer.h b/src/audio_core/device/audio_buffer.h index cae7fa970..7128ef72a 100644 --- a/src/audio_core/device/audio_buffer.h +++ b/src/audio_core/device/audio_buffer.h | |||
| @@ -8,6 +8,10 @@ | |||
| 8 | namespace AudioCore { | 8 | namespace AudioCore { |
| 9 | 9 | ||
| 10 | struct AudioBuffer { | 10 | struct AudioBuffer { |
| 11 | /// Timestamp this buffer started playing. | ||
| 12 | u64 start_timestamp; | ||
| 13 | /// Timestamp this buffer should finish playing. | ||
| 14 | u64 end_timestamp; | ||
| 11 | /// Timestamp this buffer completed playing. | 15 | /// Timestamp this buffer completed playing. |
| 12 | s64 played_timestamp; | 16 | s64 played_timestamp; |
| 13 | /// Game memory address for these samples. | 17 | /// Game memory address for these samples. |
diff --git a/src/audio_core/device/audio_buffers.h b/src/audio_core/device/audio_buffers.h index 5d1979ea0..3ecbbb63f 100644 --- a/src/audio_core/device/audio_buffers.h +++ b/src/audio_core/device/audio_buffers.h | |||
| @@ -58,6 +58,7 @@ public: | |||
| 58 | if (index < 0) { | 58 | if (index < 0) { |
| 59 | index += N; | 59 | index += N; |
| 60 | } | 60 | } |
| 61 | |||
| 61 | out_buffers.push_back(buffers[index]); | 62 | out_buffers.push_back(buffers[index]); |
| 62 | registered_count++; | 63 | registered_count++; |
| 63 | registered_index = (registered_index + 1) % append_limit; | 64 | registered_index = (registered_index + 1) % append_limit; |
| @@ -87,7 +88,9 @@ public: | |||
| 87 | /** | 88 | /** |
| 88 | * Release all registered buffers. | 89 | * Release all registered buffers. |
| 89 | * | 90 | * |
| 90 | * @param timestamp - The released timestamp for this buffer. | 91 | * @param core_timing - The CoreTiming instance |
| 92 | * @param session - The device session | ||
| 93 | * | ||
| 91 | * @return Is the buffer was released. | 94 | * @return Is the buffer was released. |
| 92 | */ | 95 | */ |
| 93 | bool ReleaseBuffers(Core::Timing::CoreTiming& core_timing, DeviceSession& session) { | 96 | bool ReleaseBuffers(Core::Timing::CoreTiming& core_timing, DeviceSession& session) { |
| @@ -100,7 +103,7 @@ public: | |||
| 100 | } | 103 | } |
| 101 | 104 | ||
| 102 | // Check with the backend if this buffer can be released yet. | 105 | // Check with the backend if this buffer can be released yet. |
| 103 | if (!session.IsBufferConsumed(buffers[index].tag)) { | 106 | if (!session.IsBufferConsumed(buffers[index])) { |
| 104 | break; | 107 | break; |
| 105 | } | 108 | } |
| 106 | 109 | ||
| @@ -280,6 +283,16 @@ public: | |||
| 280 | return true; | 283 | return true; |
| 281 | } | 284 | } |
| 282 | 285 | ||
| 286 | u64 GetNextTimestamp() const { | ||
| 287 | // Iterate backwards through the buffer queue, and take the most recent buffer's end | ||
| 288 | std::scoped_lock l{lock}; | ||
| 289 | auto index{appended_index - 1}; | ||
| 290 | if (index < 0) { | ||
| 291 | index += append_limit; | ||
| 292 | } | ||
| 293 | return buffers[index].end_timestamp; | ||
| 294 | } | ||
| 295 | |||
| 283 | private: | 296 | private: |
| 284 | /// Buffer lock | 297 | /// Buffer lock |
| 285 | mutable std::recursive_mutex lock{}; | 298 | mutable std::recursive_mutex lock{}; |
diff --git a/src/audio_core/device/device_session.cpp b/src/audio_core/device/device_session.cpp index 095fc96ce..c71c3a376 100644 --- a/src/audio_core/device/device_session.cpp +++ b/src/audio_core/device/device_session.cpp | |||
| @@ -7,11 +7,20 @@ | |||
| 7 | #include "audio_core/device/device_session.h" | 7 | #include "audio_core/device/device_session.h" |
| 8 | #include "audio_core/sink/sink_stream.h" | 8 | #include "audio_core/sink/sink_stream.h" |
| 9 | #include "core/core.h" | 9 | #include "core/core.h" |
| 10 | #include "core/core_timing.h" | ||
| 10 | #include "core/memory.h" | 11 | #include "core/memory.h" |
| 11 | 12 | ||
| 12 | namespace AudioCore { | 13 | namespace AudioCore { |
| 13 | 14 | ||
| 14 | DeviceSession::DeviceSession(Core::System& system_) : system{system_} {} | 15 | using namespace std::literals; |
| 16 | constexpr auto INCREMENT_TIME{5ms}; | ||
| 17 | |||
| 18 | DeviceSession::DeviceSession(Core::System& system_) | ||
| 19 | : system{system_}, thread_event{Core::Timing::CreateEvent( | ||
| 20 | "AudioOutSampleTick", | ||
| 21 | [this](std::uintptr_t, s64 time, std::chrono::nanoseconds) { | ||
| 22 | return ThreadFunc(); | ||
| 23 | })} {} | ||
| 15 | 24 | ||
| 16 | DeviceSession::~DeviceSession() { | 25 | DeviceSession::~DeviceSession() { |
| 17 | Finalize(); | 26 | Finalize(); |
| @@ -50,20 +59,21 @@ void DeviceSession::Finalize() { | |||
| 50 | } | 59 | } |
| 51 | 60 | ||
| 52 | void DeviceSession::Start() { | 61 | void DeviceSession::Start() { |
| 53 | stream->SetPlayedSampleCount(played_sample_count); | 62 | if (stream) { |
| 54 | stream->Start(); | 63 | stream->Start(); |
| 64 | system.CoreTiming().ScheduleLoopingEvent(std::chrono::nanoseconds::zero(), INCREMENT_TIME, | ||
| 65 | thread_event); | ||
| 66 | } | ||
| 55 | } | 67 | } |
| 56 | 68 | ||
| 57 | void DeviceSession::Stop() { | 69 | void DeviceSession::Stop() { |
| 58 | if (stream) { | 70 | if (stream) { |
| 59 | played_sample_count = stream->GetPlayedSampleCount(); | ||
| 60 | stream->Stop(); | 71 | stream->Stop(); |
| 72 | system.CoreTiming().UnscheduleEvent(thread_event, {}); | ||
| 61 | } | 73 | } |
| 62 | } | 74 | } |
| 63 | 75 | ||
| 64 | void DeviceSession::AppendBuffers(std::span<AudioBuffer> buffers) const { | 76 | void DeviceSession::AppendBuffers(std::span<AudioBuffer> buffers) const { |
| 65 | auto& memory{system.Memory()}; | ||
| 66 | |||
| 67 | for (size_t i = 0; i < buffers.size(); i++) { | 77 | for (size_t i = 0; i < buffers.size(); i++) { |
| 68 | Sink::SinkBuffer new_buffer{ | 78 | Sink::SinkBuffer new_buffer{ |
| 69 | .frames = buffers[i].size / (channel_count * sizeof(s16)), | 79 | .frames = buffers[i].size / (channel_count * sizeof(s16)), |
| @@ -77,7 +87,7 @@ void DeviceSession::AppendBuffers(std::span<AudioBuffer> buffers) const { | |||
| 77 | stream->AppendBuffer(new_buffer, samples); | 87 | stream->AppendBuffer(new_buffer, samples); |
| 78 | } else { | 88 | } else { |
| 79 | std::vector<s16> samples(buffers[i].size / sizeof(s16)); | 89 | std::vector<s16> samples(buffers[i].size / sizeof(s16)); |
| 80 | memory.ReadBlockUnsafe(buffers[i].samples, samples.data(), buffers[i].size); | 90 | system.Memory().ReadBlockUnsafe(buffers[i].samples, samples.data(), buffers[i].size); |
| 81 | stream->AppendBuffer(new_buffer, samples); | 91 | stream->AppendBuffer(new_buffer, samples); |
| 82 | } | 92 | } |
| 83 | } | 93 | } |
| @@ -85,17 +95,13 @@ void DeviceSession::AppendBuffers(std::span<AudioBuffer> buffers) const { | |||
| 85 | 95 | ||
| 86 | void DeviceSession::ReleaseBuffer(AudioBuffer& buffer) const { | 96 | void DeviceSession::ReleaseBuffer(AudioBuffer& buffer) const { |
| 87 | if (type == Sink::StreamType::In) { | 97 | if (type == Sink::StreamType::In) { |
| 88 | auto& memory{system.Memory()}; | ||
| 89 | auto samples{stream->ReleaseBuffer(buffer.size / sizeof(s16))}; | 98 | auto samples{stream->ReleaseBuffer(buffer.size / sizeof(s16))}; |
| 90 | memory.WriteBlockUnsafe(buffer.samples, samples.data(), buffer.size); | 99 | system.Memory().WriteBlockUnsafe(buffer.samples, samples.data(), buffer.size); |
| 91 | } | 100 | } |
| 92 | } | 101 | } |
| 93 | 102 | ||
| 94 | bool DeviceSession::IsBufferConsumed(u64 tag) const { | 103 | bool DeviceSession::IsBufferConsumed(AudioBuffer& buffer) const { |
| 95 | if (stream) { | 104 | return played_sample_count >= buffer.end_timestamp; |
| 96 | return stream->IsBufferConsumed(tag); | ||
| 97 | } | ||
| 98 | return true; | ||
| 99 | } | 105 | } |
| 100 | 106 | ||
| 101 | void DeviceSession::SetVolume(f32 volume) const { | 107 | void DeviceSession::SetVolume(f32 volume) const { |
| @@ -105,10 +111,22 @@ void DeviceSession::SetVolume(f32 volume) const { | |||
| 105 | } | 111 | } |
| 106 | 112 | ||
| 107 | u64 DeviceSession::GetPlayedSampleCount() const { | 113 | u64 DeviceSession::GetPlayedSampleCount() const { |
| 108 | if (stream) { | 114 | return played_sample_count; |
| 109 | return stream->GetPlayedSampleCount(); | 115 | } |
| 116 | |||
| 117 | std::optional<std::chrono::nanoseconds> DeviceSession::ThreadFunc() { | ||
| 118 | // Add 5ms of samples at a 48K sample rate. | ||
| 119 | played_sample_count += 48'000 * INCREMENT_TIME / 1s; | ||
| 120 | if (type == Sink::StreamType::Out) { | ||
| 121 | system.AudioCore().GetAudioManager().SetEvent(Event::Type::AudioOutManager, true); | ||
| 122 | } else { | ||
| 123 | system.AudioCore().GetAudioManager().SetEvent(Event::Type::AudioInManager, true); | ||
| 110 | } | 124 | } |
| 111 | return 0; | 125 | return std::nullopt; |
| 126 | } | ||
| 127 | |||
| 128 | void DeviceSession::SetRingSize(u32 ring_size) { | ||
| 129 | stream->SetRingSize(ring_size); | ||
| 112 | } | 130 | } |
| 113 | 131 | ||
| 114 | } // namespace AudioCore | 132 | } // namespace AudioCore |
diff --git a/src/audio_core/device/device_session.h b/src/audio_core/device/device_session.h index 4a031b765..53b649c61 100644 --- a/src/audio_core/device/device_session.h +++ b/src/audio_core/device/device_session.h | |||
| @@ -3,6 +3,9 @@ | |||
| 3 | 3 | ||
| 4 | #pragma once | 4 | #pragma once |
| 5 | 5 | ||
| 6 | #include <chrono> | ||
| 7 | #include <memory> | ||
| 8 | #include <optional> | ||
| 6 | #include <span> | 9 | #include <span> |
| 7 | 10 | ||
| 8 | #include "audio_core/common/common.h" | 11 | #include "audio_core/common/common.h" |
| @@ -11,9 +14,13 @@ | |||
| 11 | 14 | ||
| 12 | namespace Core { | 15 | namespace Core { |
| 13 | class System; | 16 | class System; |
| 14 | } | 17 | namespace Timing { |
| 18 | struct EventType; | ||
| 19 | } // namespace Timing | ||
| 20 | } // namespace Core | ||
| 15 | 21 | ||
| 16 | namespace AudioCore { | 22 | namespace AudioCore { |
| 23 | |||
| 17 | namespace Sink { | 24 | namespace Sink { |
| 18 | class SinkStream; | 25 | class SinkStream; |
| 19 | struct SinkBuffer; | 26 | struct SinkBuffer; |
| @@ -67,10 +74,11 @@ public: | |||
| 67 | /** | 74 | /** |
| 68 | * Check if the buffer for the given tag has been consumed by the backend. | 75 | * Check if the buffer for the given tag has been consumed by the backend. |
| 69 | * | 76 | * |
| 70 | * @param tag - Unqiue tag of the buffer to check. | 77 | * @param buffer - the buffer to check. |
| 78 | * | ||
| 71 | * @return true if the buffer has been consumed, otherwise false. | 79 | * @return true if the buffer has been consumed, otherwise false. |
| 72 | */ | 80 | */ |
| 73 | bool IsBufferConsumed(u64 tag) const; | 81 | bool IsBufferConsumed(AudioBuffer& buffer) const; |
| 74 | 82 | ||
| 75 | /** | 83 | /** |
| 76 | * Start this device session, starting the backend stream. | 84 | * Start this device session, starting the backend stream. |
| @@ -96,6 +104,16 @@ public: | |||
| 96 | */ | 104 | */ |
| 97 | u64 GetPlayedSampleCount() const; | 105 | u64 GetPlayedSampleCount() const; |
| 98 | 106 | ||
| 107 | /* | ||
| 108 | * CoreTiming callback to increment played_sample_count over time. | ||
| 109 | */ | ||
| 110 | std::optional<std::chrono::nanoseconds> ThreadFunc(); | ||
| 111 | |||
| 112 | /* | ||
| 113 | * Set the size of the ring buffer. | ||
| 114 | */ | ||
| 115 | void SetRingSize(u32 ring_size); | ||
| 116 | |||
| 99 | private: | 117 | private: |
| 100 | /// System | 118 | /// System |
| 101 | Core::System& system; | 119 | Core::System& system; |
| @@ -118,9 +136,13 @@ private: | |||
| 118 | /// Applet resource user id of this device session | 136 | /// Applet resource user id of this device session |
| 119 | u64 applet_resource_user_id{}; | 137 | u64 applet_resource_user_id{}; |
| 120 | /// Total number of samples played by this device session | 138 | /// Total number of samples played by this device session |
| 121 | u64 played_sample_count{}; | 139 | std::atomic<u64> played_sample_count{}; |
| 140 | /// Event increasing the played sample count every 5ms | ||
| 141 | std::shared_ptr<Core::Timing::EventType> thread_event; | ||
| 122 | /// Is this session initialised? | 142 | /// Is this session initialised? |
| 123 | bool initialized{}; | 143 | bool initialized{}; |
| 144 | /// Buffer queue | ||
| 145 | std::vector<AudioBuffer> buffer_queue{}; | ||
| 124 | }; | 146 | }; |
| 125 | 147 | ||
| 126 | } // namespace AudioCore | 148 | } // namespace AudioCore |
diff --git a/src/audio_core/in/audio_in_system.cpp b/src/audio_core/in/audio_in_system.cpp index ec5d37ed4..7e80ba03c 100644 --- a/src/audio_core/in/audio_in_system.cpp +++ b/src/audio_core/in/audio_in_system.cpp | |||
| @@ -93,6 +93,7 @@ Result System::Start() { | |||
| 93 | std::vector<AudioBuffer> buffers_to_flush{}; | 93 | std::vector<AudioBuffer> buffers_to_flush{}; |
| 94 | buffers.RegisterBuffers(buffers_to_flush); | 94 | buffers.RegisterBuffers(buffers_to_flush); |
| 95 | session->AppendBuffers(buffers_to_flush); | 95 | session->AppendBuffers(buffers_to_flush); |
| 96 | session->SetRingSize(static_cast<u32>(buffers_to_flush.size())); | ||
| 96 | 97 | ||
| 97 | return ResultSuccess; | 98 | return ResultSuccess; |
| 98 | } | 99 | } |
| @@ -112,8 +113,13 @@ bool System::AppendBuffer(const AudioInBuffer& buffer, const u64 tag) { | |||
| 112 | return false; | 113 | return false; |
| 113 | } | 114 | } |
| 114 | 115 | ||
| 115 | AudioBuffer new_buffer{ | 116 | const auto timestamp{buffers.GetNextTimestamp()}; |
| 116 | .played_timestamp = 0, .samples = buffer.samples, .tag = tag, .size = buffer.size}; | 117 | AudioBuffer new_buffer{.start_timestamp = timestamp, |
| 118 | .end_timestamp = timestamp + buffer.size / (channel_count * sizeof(s16)), | ||
| 119 | .played_timestamp = 0, | ||
| 120 | .samples = buffer.samples, | ||
| 121 | .tag = tag, | ||
| 122 | .size = buffer.size}; | ||
| 117 | 123 | ||
| 118 | buffers.AppendBuffer(new_buffer); | 124 | buffers.AppendBuffer(new_buffer); |
| 119 | RegisterBuffers(); | 125 | RegisterBuffers(); |
diff --git a/src/audio_core/in/audio_in_system.h b/src/audio_core/in/audio_in_system.h index 165e35d83..9ddc8daae 100644 --- a/src/audio_core/in/audio_in_system.h +++ b/src/audio_core/in/audio_in_system.h | |||
| @@ -208,7 +208,7 @@ public: | |||
| 208 | /** | 208 | /** |
| 209 | * Set this system's current volume. | 209 | * Set this system's current volume. |
| 210 | * | 210 | * |
| 211 | * @param The new volume. | 211 | * @param volume The new volume. |
| 212 | */ | 212 | */ |
| 213 | void SetVolume(f32 volume); | 213 | void SetVolume(f32 volume); |
| 214 | 214 | ||
diff --git a/src/audio_core/out/audio_out_system.cpp b/src/audio_core/out/audio_out_system.cpp index 35afddf06..8941b09a0 100644 --- a/src/audio_core/out/audio_out_system.cpp +++ b/src/audio_core/out/audio_out_system.cpp | |||
| @@ -92,6 +92,7 @@ Result System::Start() { | |||
| 92 | std::vector<AudioBuffer> buffers_to_flush{}; | 92 | std::vector<AudioBuffer> buffers_to_flush{}; |
| 93 | buffers.RegisterBuffers(buffers_to_flush); | 93 | buffers.RegisterBuffers(buffers_to_flush); |
| 94 | session->AppendBuffers(buffers_to_flush); | 94 | session->AppendBuffers(buffers_to_flush); |
| 95 | session->SetRingSize(static_cast<u32>(buffers_to_flush.size())); | ||
| 95 | 96 | ||
| 96 | return ResultSuccess; | 97 | return ResultSuccess; |
| 97 | } | 98 | } |
| @@ -111,8 +112,13 @@ bool System::AppendBuffer(const AudioOutBuffer& buffer, u64 tag) { | |||
| 111 | return false; | 112 | return false; |
| 112 | } | 113 | } |
| 113 | 114 | ||
| 114 | AudioBuffer new_buffer{ | 115 | const auto timestamp{buffers.GetNextTimestamp()}; |
| 115 | .played_timestamp = 0, .samples = buffer.samples, .tag = tag, .size = buffer.size}; | 116 | AudioBuffer new_buffer{.start_timestamp = timestamp, |
| 117 | .end_timestamp = timestamp + buffer.size / (channel_count * sizeof(s16)), | ||
| 118 | .played_timestamp = 0, | ||
| 119 | .samples = buffer.samples, | ||
| 120 | .tag = tag, | ||
| 121 | .size = buffer.size}; | ||
| 116 | 122 | ||
| 117 | buffers.AppendBuffer(new_buffer); | 123 | buffers.AppendBuffer(new_buffer); |
| 118 | RegisterBuffers(); | 124 | RegisterBuffers(); |
diff --git a/src/audio_core/out/audio_out_system.h b/src/audio_core/out/audio_out_system.h index 4ca2f3417..205ead861 100644 --- a/src/audio_core/out/audio_out_system.h +++ b/src/audio_core/out/audio_out_system.h | |||
| @@ -199,7 +199,7 @@ public: | |||
| 199 | /** | 199 | /** |
| 200 | * Set this system's current volume. | 200 | * Set this system's current volume. |
| 201 | * | 201 | * |
| 202 | * @param The new volume. | 202 | * @param volume The new volume. |
| 203 | */ | 203 | */ |
| 204 | void SetVolume(f32 volume); | 204 | void SetVolume(f32 volume); |
| 205 | 205 | ||
diff --git a/src/audio_core/renderer/adsp/adsp.h b/src/audio_core/renderer/adsp/adsp.h index 4dfcef4a5..523184dc2 100644 --- a/src/audio_core/renderer/adsp/adsp.h +++ b/src/audio_core/renderer/adsp/adsp.h | |||
| @@ -63,8 +63,6 @@ public: | |||
| 63 | 63 | ||
| 64 | /** | 64 | /** |
| 65 | * Stop the ADSP. | 65 | * Stop the ADSP. |
| 66 | * | ||
| 67 | * @return True if started or already running, otherwise false. | ||
| 68 | */ | 66 | */ |
| 69 | void Stop(); | 67 | void Stop(); |
| 70 | 68 | ||
diff --git a/src/audio_core/renderer/adsp/audio_renderer.cpp b/src/audio_core/renderer/adsp/audio_renderer.cpp index 3967ccfe6..bcd889ecb 100644 --- a/src/audio_core/renderer/adsp/audio_renderer.cpp +++ b/src/audio_core/renderer/adsp/audio_renderer.cpp | |||
| @@ -106,9 +106,6 @@ void AudioRenderer::Start(AudioRenderer_Mailbox* mailbox_) { | |||
| 106 | 106 | ||
| 107 | mailbox = mailbox_; | 107 | mailbox = mailbox_; |
| 108 | thread = std::thread(&AudioRenderer::ThreadFunc, this); | 108 | thread = std::thread(&AudioRenderer::ThreadFunc, this); |
| 109 | for (auto& stream : streams) { | ||
| 110 | stream->Start(); | ||
| 111 | } | ||
| 112 | running = true; | 109 | running = true; |
| 113 | } | 110 | } |
| 114 | 111 | ||
| @@ -130,6 +127,7 @@ void AudioRenderer::CreateSinkStreams() { | |||
| 130 | std::string name{fmt::format("ADSP_RenderStream-{}", i)}; | 127 | std::string name{fmt::format("ADSP_RenderStream-{}", i)}; |
| 131 | streams[i] = | 128 | streams[i] = |
| 132 | sink.AcquireSinkStream(system, channels, name, ::AudioCore::Sink::StreamType::Render); | 129 | sink.AcquireSinkStream(system, channels, name, ::AudioCore::Sink::StreamType::Render); |
| 130 | streams[i]->SetRingSize(4); | ||
| 133 | } | 131 | } |
| 134 | } | 132 | } |
| 135 | 133 | ||
| @@ -198,11 +196,6 @@ void AudioRenderer::ThreadFunc() { | |||
| 198 | command_list_processor.Process(index) - start_time; | 196 | command_list_processor.Process(index) - start_time; |
| 199 | } | 197 | } |
| 200 | 198 | ||
| 201 | if (index == 0) { | ||
| 202 | auto stream{command_list_processor.GetOutputSinkStream()}; | ||
| 203 | system.AudioCore().SetStreamQueue(stream->GetQueueSize()); | ||
| 204 | } | ||
| 205 | |||
| 206 | const auto end_time{system.CoreTiming().GetClockTicks()}; | 199 | const auto end_time{system.CoreTiming().GetClockTicks()}; |
| 207 | 200 | ||
| 208 | command_buffer.remaining_command_count = | 201 | command_buffer.remaining_command_count = |
diff --git a/src/audio_core/renderer/adsp/audio_renderer.h b/src/audio_core/renderer/adsp/audio_renderer.h index b6ced9d2b..49f66f21c 100644 --- a/src/audio_core/renderer/adsp/audio_renderer.h +++ b/src/audio_core/renderer/adsp/audio_renderer.h | |||
| @@ -52,7 +52,7 @@ public: | |||
| 52 | /** | 52 | /** |
| 53 | * Send a message from the host to the AudioRenderer. | 53 | * Send a message from the host to the AudioRenderer. |
| 54 | * | 54 | * |
| 55 | * @param message_ - The message to send to the AudioRenderer. | 55 | * @param message - The message to send to the AudioRenderer. |
| 56 | */ | 56 | */ |
| 57 | void HostSendMessage(RenderMessage message); | 57 | void HostSendMessage(RenderMessage message); |
| 58 | 58 | ||
| @@ -66,7 +66,7 @@ public: | |||
| 66 | /** | 66 | /** |
| 67 | * Send a message from the AudioRenderer to the host. | 67 | * Send a message from the AudioRenderer to the host. |
| 68 | * | 68 | * |
| 69 | * @param message_ - The message to send to the host. | 69 | * @param message - The message to send to the host. |
| 70 | */ | 70 | */ |
| 71 | void ADSPSendMessage(RenderMessage message); | 71 | void ADSPSendMessage(RenderMessage message); |
| 72 | 72 | ||
| @@ -163,7 +163,7 @@ public: | |||
| 163 | /** | 163 | /** |
| 164 | * Start the AudioRenderer. | 164 | * Start the AudioRenderer. |
| 165 | * | 165 | * |
| 166 | * @param The mailbox to use for this session. | 166 | * @param mailbox The mailbox to use for this session. |
| 167 | */ | 167 | */ |
| 168 | void Start(AudioRenderer_Mailbox* mailbox); | 168 | void Start(AudioRenderer_Mailbox* mailbox); |
| 169 | 169 | ||
diff --git a/src/audio_core/renderer/adsp/command_list_processor.h b/src/audio_core/renderer/adsp/command_list_processor.h index 3f99173e3..d78269e1d 100644 --- a/src/audio_core/renderer/adsp/command_list_processor.h +++ b/src/audio_core/renderer/adsp/command_list_processor.h | |||
| @@ -33,10 +33,10 @@ public: | |||
| 33 | /** | 33 | /** |
| 34 | * Initialize the processor. | 34 | * Initialize the processor. |
| 35 | * | 35 | * |
| 36 | * @param system_ - The core system. | 36 | * @param system - The core system. |
| 37 | * @param buffer - The command buffer to process. | 37 | * @param buffer - The command buffer to process. |
| 38 | * @param size - The size of the buffer. | 38 | * @param size - The size of the buffer. |
| 39 | * @param stream_ - The stream to be used for sending the samples. | 39 | * @param stream - The stream to be used for sending the samples. |
| 40 | */ | 40 | */ |
| 41 | void Initialize(Core::System& system, CpuAddr buffer, u64 size, Sink::SinkStream* stream); | 41 | void Initialize(Core::System& system, CpuAddr buffer, u64 size, Sink::SinkStream* stream); |
| 42 | 42 | ||
| @@ -72,7 +72,8 @@ public: | |||
| 72 | /** | 72 | /** |
| 73 | * Process the command list. | 73 | * Process the command list. |
| 74 | * | 74 | * |
| 75 | * @param index - Index of the current command list. | 75 | * @param session_id - Session ID for the commands being processed. |
| 76 | * | ||
| 76 | * @return The time taken to process. | 77 | * @return The time taken to process. |
| 77 | */ | 78 | */ |
| 78 | u64 Process(u32 session_id); | 79 | u64 Process(u32 session_id); |
| @@ -89,7 +90,7 @@ public: | |||
| 89 | u8* commands{}; | 90 | u8* commands{}; |
| 90 | /// The command buffer size | 91 | /// The command buffer size |
| 91 | u64 commands_buffer_size{}; | 92 | u64 commands_buffer_size{}; |
| 92 | /// The maximum processing time alloted | 93 | /// The maximum processing time allotted |
| 93 | u64 max_process_time{}; | 94 | u64 max_process_time{}; |
| 94 | /// The number of commands in the buffer | 95 | /// The number of commands in the buffer |
| 95 | u32 command_count{}; | 96 | u32 command_count{}; |
diff --git a/src/audio_core/renderer/audio_device.cpp b/src/audio_core/renderer/audio_device.cpp index d5886e55e..0d9d8f6ce 100644 --- a/src/audio_core/renderer/audio_device.cpp +++ b/src/audio_core/renderer/audio_device.cpp | |||
| @@ -1,6 +1,9 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project | 1 | // SPDX-FileCopyrightText: Copyright 2022 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 <array> | ||
| 5 | #include <span> | ||
| 6 | |||
| 4 | #include "audio_core/audio_core.h" | 7 | #include "audio_core/audio_core.h" |
| 5 | #include "audio_core/common/feature_support.h" | 8 | #include "audio_core/common/feature_support.h" |
| 6 | #include "audio_core/renderer/audio_device.h" | 9 | #include "audio_core/renderer/audio_device.h" |
| @@ -9,14 +12,33 @@ | |||
| 9 | 12 | ||
| 10 | namespace AudioCore::AudioRenderer { | 13 | namespace AudioCore::AudioRenderer { |
| 11 | 14 | ||
| 15 | constexpr std::array usb_device_names{ | ||
| 16 | AudioDevice::AudioDeviceName{"AudioStereoJackOutput"}, | ||
| 17 | AudioDevice::AudioDeviceName{"AudioBuiltInSpeakerOutput"}, | ||
| 18 | AudioDevice::AudioDeviceName{"AudioTvOutput"}, | ||
| 19 | AudioDevice::AudioDeviceName{"AudioUsbDeviceOutput"}, | ||
| 20 | }; | ||
| 21 | |||
| 22 | constexpr std::array device_names{ | ||
| 23 | AudioDevice::AudioDeviceName{"AudioStereoJackOutput"}, | ||
| 24 | AudioDevice::AudioDeviceName{"AudioBuiltInSpeakerOutput"}, | ||
| 25 | AudioDevice::AudioDeviceName{"AudioTvOutput"}, | ||
| 26 | }; | ||
| 27 | |||
| 28 | constexpr std::array output_device_names{ | ||
| 29 | AudioDevice::AudioDeviceName{"AudioBuiltInSpeakerOutput"}, | ||
| 30 | AudioDevice::AudioDeviceName{"AudioTvOutput"}, | ||
| 31 | AudioDevice::AudioDeviceName{"AudioExternalOutput"}, | ||
| 32 | }; | ||
| 33 | |||
| 12 | AudioDevice::AudioDevice(Core::System& system, const u64 applet_resource_user_id_, | 34 | AudioDevice::AudioDevice(Core::System& system, const u64 applet_resource_user_id_, |
| 13 | const u32 revision) | 35 | const u32 revision) |
| 14 | : output_sink{system.AudioCore().GetOutputSink()}, | 36 | : output_sink{system.AudioCore().GetOutputSink()}, |
| 15 | applet_resource_user_id{applet_resource_user_id_}, user_revision{revision} {} | 37 | applet_resource_user_id{applet_resource_user_id_}, user_revision{revision} {} |
| 16 | 38 | ||
| 17 | u32 AudioDevice::ListAudioDeviceName(std::vector<AudioDeviceName>& out_buffer, | 39 | u32 AudioDevice::ListAudioDeviceName(std::vector<AudioDeviceName>& out_buffer, |
| 18 | const size_t max_count) { | 40 | const size_t max_count) const { |
| 19 | std::span<AudioDeviceName> names{}; | 41 | std::span<const AudioDeviceName> names{}; |
| 20 | 42 | ||
| 21 | if (CheckFeatureSupported(SupportTags::AudioUsbDeviceOutput, user_revision)) { | 43 | if (CheckFeatureSupported(SupportTags::AudioUsbDeviceOutput, user_revision)) { |
| 22 | names = usb_device_names; | 44 | names = usb_device_names; |
| @@ -24,7 +46,7 @@ u32 AudioDevice::ListAudioDeviceName(std::vector<AudioDeviceName>& out_buffer, | |||
| 24 | names = device_names; | 46 | names = device_names; |
| 25 | } | 47 | } |
| 26 | 48 | ||
| 27 | u32 out_count{static_cast<u32>(std::min(max_count, names.size()))}; | 49 | const u32 out_count{static_cast<u32>(std::min(max_count, names.size()))}; |
| 28 | for (u32 i = 0; i < out_count; i++) { | 50 | for (u32 i = 0; i < out_count; i++) { |
| 29 | out_buffer.push_back(names[i]); | 51 | out_buffer.push_back(names[i]); |
| 30 | } | 52 | } |
| @@ -32,8 +54,8 @@ u32 AudioDevice::ListAudioDeviceName(std::vector<AudioDeviceName>& out_buffer, | |||
| 32 | } | 54 | } |
| 33 | 55 | ||
| 34 | u32 AudioDevice::ListAudioOutputDeviceName(std::vector<AudioDeviceName>& out_buffer, | 56 | u32 AudioDevice::ListAudioOutputDeviceName(std::vector<AudioDeviceName>& out_buffer, |
| 35 | const size_t max_count) { | 57 | const size_t max_count) const { |
| 36 | u32 out_count{static_cast<u32>(std::min(max_count, output_device_names.size()))}; | 58 | const u32 out_count{static_cast<u32>(std::min(max_count, output_device_names.size()))}; |
| 37 | 59 | ||
| 38 | for (u32 i = 0; i < out_count; i++) { | 60 | for (u32 i = 0; i < out_count; i++) { |
| 39 | out_buffer.push_back(output_device_names[i]); | 61 | out_buffer.push_back(output_device_names[i]); |
| @@ -45,7 +67,7 @@ void AudioDevice::SetDeviceVolumes(const f32 volume) { | |||
| 45 | output_sink.SetDeviceVolume(volume); | 67 | output_sink.SetDeviceVolume(volume); |
| 46 | } | 68 | } |
| 47 | 69 | ||
| 48 | f32 AudioDevice::GetDeviceVolume([[maybe_unused]] std::string_view name) { | 70 | f32 AudioDevice::GetDeviceVolume([[maybe_unused]] std::string_view name) const { |
| 49 | return output_sink.GetDeviceVolume(); | 71 | return output_sink.GetDeviceVolume(); |
| 50 | } | 72 | } |
| 51 | 73 | ||
diff --git a/src/audio_core/renderer/audio_device.h b/src/audio_core/renderer/audio_device.h index 1f449f261..dd6be70ee 100644 --- a/src/audio_core/renderer/audio_device.h +++ b/src/audio_core/renderer/audio_device.h | |||
| @@ -3,7 +3,7 @@ | |||
| 3 | 3 | ||
| 4 | #pragma once | 4 | #pragma once |
| 5 | 5 | ||
| 6 | #include <span> | 6 | #include <string_view> |
| 7 | 7 | ||
| 8 | #include "audio_core/audio_render_manager.h" | 8 | #include "audio_core/audio_render_manager.h" |
| 9 | 9 | ||
| @@ -23,21 +23,13 @@ namespace AudioRenderer { | |||
| 23 | class AudioDevice { | 23 | class AudioDevice { |
| 24 | public: | 24 | public: |
| 25 | struct AudioDeviceName { | 25 | struct AudioDeviceName { |
| 26 | std::array<char, 0x100> name; | 26 | std::array<char, 0x100> name{}; |
| 27 | 27 | ||
| 28 | AudioDeviceName(const char* name_) { | 28 | constexpr AudioDeviceName(std::string_view name_) { |
| 29 | std::strncpy(name.data(), name_, name.size()); | 29 | name_.copy(name.data(), name.size() - 1); |
| 30 | } | 30 | } |
| 31 | }; | 31 | }; |
| 32 | 32 | ||
| 33 | std::array<AudioDeviceName, 4> usb_device_names{"AudioStereoJackOutput", | ||
| 34 | "AudioBuiltInSpeakerOutput", "AudioTvOutput", | ||
| 35 | "AudioUsbDeviceOutput"}; | ||
| 36 | std::array<AudioDeviceName, 3> device_names{"AudioStereoJackOutput", | ||
| 37 | "AudioBuiltInSpeakerOutput", "AudioTvOutput"}; | ||
| 38 | std::array<AudioDeviceName, 3> output_device_names{"AudioBuiltInSpeakerOutput", "AudioTvOutput", | ||
| 39 | "AudioExternalOutput"}; | ||
| 40 | |||
| 41 | explicit AudioDevice(Core::System& system, u64 applet_resource_user_id, u32 revision); | 33 | explicit AudioDevice(Core::System& system, u64 applet_resource_user_id, u32 revision); |
| 42 | 34 | ||
| 43 | /** | 35 | /** |
| @@ -47,7 +39,7 @@ public: | |||
| 47 | * @param max_count - Maximum number of devices to write (count of out_buffer). | 39 | * @param max_count - Maximum number of devices to write (count of out_buffer). |
| 48 | * @return Number of device names written. | 40 | * @return Number of device names written. |
| 49 | */ | 41 | */ |
| 50 | u32 ListAudioDeviceName(std::vector<AudioDeviceName>& out_buffer, size_t max_count); | 42 | u32 ListAudioDeviceName(std::vector<AudioDeviceName>& out_buffer, size_t max_count) const; |
| 51 | 43 | ||
| 52 | /** | 44 | /** |
| 53 | * Get a list of the available output devices. | 45 | * Get a list of the available output devices. |
| @@ -57,7 +49,7 @@ public: | |||
| 57 | * @param max_count - Maximum number of devices to write (count of out_buffer). | 49 | * @param max_count - Maximum number of devices to write (count of out_buffer). |
| 58 | * @return Number of device names written. | 50 | * @return Number of device names written. |
| 59 | */ | 51 | */ |
| 60 | u32 ListAudioOutputDeviceName(std::vector<AudioDeviceName>& out_buffer, size_t max_count); | 52 | u32 ListAudioOutputDeviceName(std::vector<AudioDeviceName>& out_buffer, size_t max_count) const; |
| 61 | 53 | ||
| 62 | /** | 54 | /** |
| 63 | * Set the volume of all streams in the backend sink. | 55 | * Set the volume of all streams in the backend sink. |
| @@ -73,7 +65,7 @@ public: | |||
| 73 | * @param name - Name of the device to check. Unused. | 65 | * @param name - Name of the device to check. Unused. |
| 74 | * @return Volume of the device. | 66 | * @return Volume of the device. |
| 75 | */ | 67 | */ |
| 76 | f32 GetDeviceVolume(std::string_view name); | 68 | f32 GetDeviceVolume(std::string_view name) const; |
| 77 | 69 | ||
| 78 | private: | 70 | private: |
| 79 | /// Backend output sink for the device | 71 | /// Backend output sink for the device |
diff --git a/src/audio_core/renderer/behavior/behavior_info.cpp b/src/audio_core/renderer/behavior/behavior_info.cpp index c5d4d66d8..92140aaea 100644 --- a/src/audio_core/renderer/behavior/behavior_info.cpp +++ b/src/audio_core/renderer/behavior/behavior_info.cpp | |||
| @@ -43,13 +43,15 @@ void BehaviorInfo::AppendError(ErrorInfo& error) { | |||
| 43 | } | 43 | } |
| 44 | 44 | ||
| 45 | void BehaviorInfo::CopyErrorInfo(std::span<ErrorInfo> out_errors, u32& out_count) { | 45 | void BehaviorInfo::CopyErrorInfo(std::span<ErrorInfo> out_errors, u32& out_count) { |
| 46 | auto error_count_{std::min(error_count, MaxErrors)}; | 46 | out_count = std::min(error_count, MaxErrors); |
| 47 | std::memset(out_errors.data(), 0, MaxErrors * sizeof(ErrorInfo)); | 47 | |
| 48 | 48 | for (size_t i = 0; i < MaxErrors; i++) { | |
| 49 | for (size_t i = 0; i < error_count_; i++) { | 49 | if (i < out_count) { |
| 50 | out_errors[i] = errors[i]; | 50 | out_errors[i] = errors[i]; |
| 51 | } else { | ||
| 52 | out_errors[i] = {}; | ||
| 53 | } | ||
| 51 | } | 54 | } |
| 52 | out_count = error_count_; | ||
| 53 | } | 55 | } |
| 54 | 56 | ||
| 55 | void BehaviorInfo::UpdateFlags(const Flags flags_) { | 57 | void BehaviorInfo::UpdateFlags(const Flags flags_) { |
diff --git a/src/audio_core/renderer/command/command_buffer.h b/src/audio_core/renderer/command/command_buffer.h index 496b0e50a..162170846 100644 --- a/src/audio_core/renderer/command/command_buffer.h +++ b/src/audio_core/renderer/command/command_buffer.h | |||
| @@ -191,6 +191,7 @@ public: | |||
| 191 | * @param volume - Current mix volume used for calculating the ramp. | 191 | * @param volume - Current mix volume used for calculating the ramp. |
| 192 | * @param prev_volume - Previous mix volume, used for calculating the ramp, | 192 | * @param prev_volume - Previous mix volume, used for calculating the ramp, |
| 193 | * also applied to the input. | 193 | * also applied to the input. |
| 194 | * @param prev_samples - Previous sample buffer. Used for depopping. | ||
| 194 | * @param precision - Number of decimal bits for fixed point operations. | 195 | * @param precision - Number of decimal bits for fixed point operations. |
| 195 | */ | 196 | */ |
| 196 | void GenerateMixRampCommand(s32 node_id, s16 buffer_count, s16 input_index, s16 output_index, | 197 | void GenerateMixRampCommand(s32 node_id, s16 buffer_count, s16 input_index, s16 output_index, |
| @@ -208,6 +209,7 @@ public: | |||
| 208 | * @param volumes - Current mix volumes used for calculating the ramp. | 209 | * @param volumes - Current mix volumes used for calculating the ramp. |
| 209 | * @param prev_volumes - Previous mix volumes, used for calculating the ramp, | 210 | * @param prev_volumes - Previous mix volumes, used for calculating the ramp, |
| 210 | * also applied to the input. | 211 | * also applied to the input. |
| 212 | * @param prev_samples - Previous sample buffer. Used for depopping. | ||
| 211 | * @param precision - Number of decimal bits for fixed point operations. | 213 | * @param precision - Number of decimal bits for fixed point operations. |
| 212 | */ | 214 | */ |
| 213 | void GenerateMixRampGroupedCommand(s32 node_id, s16 buffer_count, s16 input_index, | 215 | void GenerateMixRampGroupedCommand(s32 node_id, s16 buffer_count, s16 input_index, |
| @@ -297,11 +299,11 @@ public: | |||
| 297 | /** | 299 | /** |
| 298 | * Generate a device sink command, adding it to the command list. | 300 | * Generate a device sink command, adding it to the command list. |
| 299 | * | 301 | * |
| 300 | * @param node_id - Node id of the voice this command is generated for. | 302 | * @param node_id - Node id of the voice this command is generated for. |
| 301 | * @param buffer_offset - Base mix buffer offset to use. | 303 | * @param buffer_offset - Base mix buffer offset to use. |
| 302 | * @param sink_info - The sink_info to generate this command from. | 304 | * @param sink_info - The sink_info to generate this command from. |
| 303 | * @session_id - System session id this command is generated from. | 305 | * @param session_id - System session id this command is generated from. |
| 304 | * @samples_buffer - The buffer to be sent to the sink if upsampling is not used. | 306 | * @param samples_buffer - The buffer to be sent to the sink if upsampling is not used. |
| 305 | */ | 307 | */ |
| 306 | void GenerateDeviceSinkCommand(s32 node_id, s16 buffer_offset, SinkInfoBase& sink_info, | 308 | void GenerateDeviceSinkCommand(s32 node_id, s16 buffer_offset, SinkInfoBase& sink_info, |
| 307 | u32 session_id, std::span<s32> samples_buffer); | 309 | u32 session_id, std::span<s32> samples_buffer); |
diff --git a/src/audio_core/renderer/command/command_generator.h b/src/audio_core/renderer/command/command_generator.h index d80d9b0d8..b3cd7b408 100644 --- a/src/audio_core/renderer/command/command_generator.h +++ b/src/audio_core/renderer/command/command_generator.h | |||
| @@ -197,9 +197,9 @@ public: | |||
| 197 | /** | 197 | /** |
| 198 | * Generate an I3DL2 reverb effect command. | 198 | * Generate an I3DL2 reverb effect command. |
| 199 | * | 199 | * |
| 200 | * @param buffer_offset - Base mix buffer offset to use. | 200 | * @param buffer_offset - Base mix buffer offset to use. |
| 201 | * @param effect_info_base - I3DL2Reverb effect info. | 201 | * @param effect_info - I3DL2Reverb effect info. |
| 202 | * @param node_id - Node id of the mix this command is generated for. | 202 | * @param node_id - Node id of the mix this command is generated for. |
| 203 | */ | 203 | */ |
| 204 | void GenerateI3dl2ReverbEffectCommand(s16 buffer_offset, EffectInfoBase& effect_info, | 204 | void GenerateI3dl2ReverbEffectCommand(s16 buffer_offset, EffectInfoBase& effect_info, |
| 205 | s32 node_id); | 205 | s32 node_id); |
| @@ -207,18 +207,18 @@ public: | |||
| 207 | /** | 207 | /** |
| 208 | * Generate an aux effect command. | 208 | * Generate an aux effect command. |
| 209 | * | 209 | * |
| 210 | * @param buffer_offset - Base mix buffer offset to use. | 210 | * @param buffer_offset - Base mix buffer offset to use. |
| 211 | * @param effect_info_base - Aux effect info. | 211 | * @param effect_info - Aux effect info. |
| 212 | * @param node_id - Node id of the mix this command is generated for. | 212 | * @param node_id - Node id of the mix this command is generated for. |
| 213 | */ | 213 | */ |
| 214 | void GenerateAuxCommand(s16 buffer_offset, EffectInfoBase& effect_info, s32 node_id); | 214 | void GenerateAuxCommand(s16 buffer_offset, EffectInfoBase& effect_info, s32 node_id); |
| 215 | 215 | ||
| 216 | /** | 216 | /** |
| 217 | * Generate a biquad filter effect command. | 217 | * Generate a biquad filter effect command. |
| 218 | * | 218 | * |
| 219 | * @param buffer_offset - Base mix buffer offset to use. | 219 | * @param buffer_offset - Base mix buffer offset to use. |
| 220 | * @param effect_info_base - Aux effect info. | 220 | * @param effect_info - Aux effect info. |
| 221 | * @param node_id - Node id of the mix this command is generated for. | 221 | * @param node_id - Node id of the mix this command is generated for. |
| 222 | */ | 222 | */ |
| 223 | void GenerateBiquadFilterEffectCommand(s16 buffer_offset, EffectInfoBase& effect_info, | 223 | void GenerateBiquadFilterEffectCommand(s16 buffer_offset, EffectInfoBase& effect_info, |
| 224 | s32 node_id); | 224 | s32 node_id); |
| @@ -226,10 +226,10 @@ public: | |||
| 226 | /** | 226 | /** |
| 227 | * Generate a light limiter effect command. | 227 | * Generate a light limiter effect command. |
| 228 | * | 228 | * |
| 229 | * @param buffer_offset - Base mix buffer offset to use. | 229 | * @param buffer_offset - Base mix buffer offset to use. |
| 230 | * @param effect_info_base - Limiter effect info. | 230 | * @param effect_info - Limiter effect info. |
| 231 | * @param node_id - Node id of the mix this command is generated for. | 231 | * @param node_id - Node id of the mix this command is generated for. |
| 232 | * @param effect_index - Index for the statistics state. | 232 | * @param effect_index - Index for the statistics state. |
| 233 | */ | 233 | */ |
| 234 | void GenerateLightLimiterEffectCommand(s16 buffer_offset, EffectInfoBase& effect_info, | 234 | void GenerateLightLimiterEffectCommand(s16 buffer_offset, EffectInfoBase& effect_info, |
| 235 | s32 node_id, u32 effect_index); | 235 | s32 node_id, u32 effect_index); |
| @@ -238,21 +238,20 @@ public: | |||
| 238 | * Generate a capture effect command. | 238 | * Generate a capture effect command. |
| 239 | * Writes a mix buffer back to game memory. | 239 | * Writes a mix buffer back to game memory. |
| 240 | * | 240 | * |
| 241 | * @param buffer_offset - Base mix buffer offset to use. | 241 | * @param buffer_offset - Base mix buffer offset to use. |
| 242 | * @param effect_info_base - Capture effect info. | 242 | * @param effect_info - Capture effect info. |
| 243 | * @param node_id - Node id of the mix this command is generated for. | 243 | * @param node_id - Node id of the mix this command is generated for. |
| 244 | */ | 244 | */ |
| 245 | void GenerateCaptureCommand(s16 buffer_offset, EffectInfoBase& effect_info, s32 node_id); | 245 | void GenerateCaptureCommand(s16 buffer_offset, EffectInfoBase& effect_info, s32 node_id); |
| 246 | 246 | ||
| 247 | /** | 247 | /** |
| 248 | * Generate a compressor effect command. | 248 | * Generate a compressor effect command. |
| 249 | * | 249 | * |
| 250 | * @param buffer_offset - Base mix buffer offset to use. | 250 | * @param buffer_offset - Base mix buffer offset to use. |
| 251 | * @param effect_info_base - Compressor effect info. | 251 | * @param effect_info - Compressor effect info. |
| 252 | * @param node_id - Node id of the mix this command is generated for. | 252 | * @param node_id - Node id of the mix this command is generated for. |
| 253 | */ | 253 | */ |
| 254 | void GenerateCompressorCommand(const s16 buffer_offset, EffectInfoBase& effect_info, | 254 | void GenerateCompressorCommand(s16 buffer_offset, EffectInfoBase& effect_info, s32 node_id); |
| 255 | const s32 node_id); | ||
| 256 | 255 | ||
| 257 | /** | 256 | /** |
| 258 | * Generate all effect commands for a mix. | 257 | * Generate all effect commands for a mix. |
| @@ -318,8 +317,9 @@ public: | |||
| 318 | * Generate a performance command. | 317 | * Generate a performance command. |
| 319 | * Used to report performance metrics of the AudioRenderer back to the game. | 318 | * Used to report performance metrics of the AudioRenderer back to the game. |
| 320 | * | 319 | * |
| 321 | * @param buffer_offset - Base mix buffer offset to use. | 320 | * @param node_id - Node ID of the mix this command is generated for |
| 322 | * @param sink_info - Sink info to generate the commands from. | 321 | * @param state - Output state of the generated performance command |
| 322 | * @param entry_addresses - Addresses to be written | ||
| 323 | */ | 323 | */ |
| 324 | void GeneratePerformanceCommand(s32 node_id, PerformanceState state, | 324 | void GeneratePerformanceCommand(s32 node_id, PerformanceState state, |
| 325 | const PerformanceEntryAddresses& entry_addresses); | 325 | const PerformanceEntryAddresses& entry_addresses); |
diff --git a/src/audio_core/renderer/command/effect/compressor.cpp b/src/audio_core/renderer/command/effect/compressor.cpp index 2ebc140f1..7229618e8 100644 --- a/src/audio_core/renderer/command/effect/compressor.cpp +++ b/src/audio_core/renderer/command/effect/compressor.cpp | |||
| @@ -11,7 +11,7 @@ | |||
| 11 | 11 | ||
| 12 | namespace AudioCore::AudioRenderer { | 12 | namespace AudioCore::AudioRenderer { |
| 13 | 13 | ||
| 14 | static void SetCompressorEffectParameter(CompressorInfo::ParameterVersion2& params, | 14 | static void SetCompressorEffectParameter(const CompressorInfo::ParameterVersion2& params, |
| 15 | CompressorInfo::State& state) { | 15 | CompressorInfo::State& state) { |
| 16 | const auto ratio{1.0f / params.compressor_ratio}; | 16 | const auto ratio{1.0f / params.compressor_ratio}; |
| 17 | auto makeup_gain{0.0f}; | 17 | auto makeup_gain{0.0f}; |
| @@ -31,9 +31,9 @@ static void SetCompressorEffectParameter(CompressorInfo::ParameterVersion2& para | |||
| 31 | state.unk_20 = c; | 31 | state.unk_20 = c; |
| 32 | } | 32 | } |
| 33 | 33 | ||
| 34 | static void InitializeCompressorEffect(CompressorInfo::ParameterVersion2& params, | 34 | static void InitializeCompressorEffect(const CompressorInfo::ParameterVersion2& params, |
| 35 | CompressorInfo::State& state) { | 35 | CompressorInfo::State& state) { |
| 36 | std::memset(&state, 0, sizeof(CompressorInfo::State)); | 36 | state = {}; |
| 37 | 37 | ||
| 38 | state.unk_00 = 0; | 38 | state.unk_00 = 0; |
| 39 | state.unk_04 = 1.0f; | 39 | state.unk_04 = 1.0f; |
| @@ -42,7 +42,7 @@ static void InitializeCompressorEffect(CompressorInfo::ParameterVersion2& params | |||
| 42 | SetCompressorEffectParameter(params, state); | 42 | SetCompressorEffectParameter(params, state); |
| 43 | } | 43 | } |
| 44 | 44 | ||
| 45 | static void ApplyCompressorEffect(CompressorInfo::ParameterVersion2& params, | 45 | static void ApplyCompressorEffect(const CompressorInfo::ParameterVersion2& params, |
| 46 | CompressorInfo::State& state, bool enabled, | 46 | CompressorInfo::State& state, bool enabled, |
| 47 | std::vector<std::span<const s32>> input_buffers, | 47 | std::vector<std::span<const s32>> input_buffers, |
| 48 | std::vector<std::span<s32>> output_buffers, u32 sample_count) { | 48 | std::vector<std::span<s32>> output_buffers, u32 sample_count) { |
| @@ -103,8 +103,7 @@ static void ApplyCompressorEffect(CompressorInfo::ParameterVersion2& params, | |||
| 103 | } else { | 103 | } else { |
| 104 | for (s16 channel = 0; channel < params.channel_count; channel++) { | 104 | for (s16 channel = 0; channel < params.channel_count; channel++) { |
| 105 | if (params.inputs[channel] != params.outputs[channel]) { | 105 | if (params.inputs[channel] != params.outputs[channel]) { |
| 106 | std::memcpy((char*)output_buffers[channel].data(), | 106 | std::memcpy(output_buffers[channel].data(), input_buffers[channel].data(), |
| 107 | (char*)input_buffers[channel].data(), | ||
| 108 | output_buffers[channel].size_bytes()); | 107 | output_buffers[channel].size_bytes()); |
| 109 | } | 108 | } |
| 110 | } | 109 | } |
diff --git a/src/audio_core/renderer/command/mix/mix_ramp.cpp b/src/audio_core/renderer/command/mix/mix_ramp.cpp index ffdafa1c8..d67123cd8 100644 --- a/src/audio_core/renderer/command/mix/mix_ramp.cpp +++ b/src/audio_core/renderer/command/mix/mix_ramp.cpp | |||
| @@ -7,17 +7,7 @@ | |||
| 7 | #include "common/logging/log.h" | 7 | #include "common/logging/log.h" |
| 8 | 8 | ||
| 9 | namespace AudioCore::AudioRenderer { | 9 | namespace AudioCore::AudioRenderer { |
| 10 | /** | 10 | |
| 11 | * Mix input mix buffer into output mix buffer, with volume applied to the input. | ||
| 12 | * | ||
| 13 | * @tparam Q - Number of bits for fixed point operations. | ||
| 14 | * @param output - Output mix buffer. | ||
| 15 | * @param input - Input mix buffer. | ||
| 16 | * @param volume - Volume applied to the input. | ||
| 17 | * @param ramp - Ramp applied to volume every sample. | ||
| 18 | * @param sample_count - Number of samples to process. | ||
| 19 | * @return The final gained input sample, used for depopping. | ||
| 20 | */ | ||
| 21 | template <size_t Q> | 11 | template <size_t Q> |
| 22 | s32 ApplyMixRamp(std::span<s32> output, std::span<const s32> input, const f32 volume_, | 12 | s32 ApplyMixRamp(std::span<s32> output, std::span<const s32> input, const f32 volume_, |
| 23 | const f32 ramp_, const u32 sample_count) { | 13 | const f32 ramp_, const u32 sample_count) { |
| @@ -40,10 +30,8 @@ s32 ApplyMixRamp(std::span<s32> output, std::span<const s32> input, const f32 vo | |||
| 40 | return sample.to_int(); | 30 | return sample.to_int(); |
| 41 | } | 31 | } |
| 42 | 32 | ||
| 43 | template s32 ApplyMixRamp<15>(std::span<s32>, std::span<const s32>, const f32, const f32, | 33 | template s32 ApplyMixRamp<15>(std::span<s32>, std::span<const s32>, f32, f32, u32); |
| 44 | const u32); | 34 | template s32 ApplyMixRamp<23>(std::span<s32>, std::span<const s32>, f32, f32, u32); |
| 45 | template s32 ApplyMixRamp<23>(std::span<s32>, std::span<const s32>, const f32, const f32, | ||
| 46 | const u32); | ||
| 47 | 35 | ||
| 48 | void MixRampCommand::Dump(const ADSP::CommandListProcessor& processor, std::string& string) { | 36 | void MixRampCommand::Dump(const ADSP::CommandListProcessor& processor, std::string& string) { |
| 49 | const auto ramp{(volume - prev_volume) / static_cast<f32>(processor.sample_count)}; | 37 | const auto ramp{(volume - prev_volume) / static_cast<f32>(processor.sample_count)}; |
diff --git a/src/audio_core/renderer/command/mix/mix_ramp.h b/src/audio_core/renderer/command/mix/mix_ramp.h index 770f57e80..52f74a273 100644 --- a/src/audio_core/renderer/command/mix/mix_ramp.h +++ b/src/audio_core/renderer/command/mix/mix_ramp.h | |||
| @@ -61,13 +61,13 @@ struct MixRampCommand : ICommand { | |||
| 61 | * @tparam Q - Number of bits for fixed point operations. | 61 | * @tparam Q - Number of bits for fixed point operations. |
| 62 | * @param output - Output mix buffer. | 62 | * @param output - Output mix buffer. |
| 63 | * @param input - Input mix buffer. | 63 | * @param input - Input mix buffer. |
| 64 | * @param volume - Volume applied to the input. | 64 | * @param volume_ - Volume applied to the input. |
| 65 | * @param ramp - Ramp applied to volume every sample. | 65 | * @param ramp_ - Ramp applied to volume every sample. |
| 66 | * @param sample_count - Number of samples to process. | 66 | * @param sample_count - Number of samples to process. |
| 67 | * @return The final gained input sample, used for depopping. | 67 | * @return The final gained input sample, used for depopping. |
| 68 | */ | 68 | */ |
| 69 | template <size_t Q> | 69 | template <size_t Q> |
| 70 | s32 ApplyMixRamp(std::span<s32> output, std::span<const s32> input, const f32 volume_, | 70 | s32 ApplyMixRamp(std::span<s32> output, std::span<const s32> input, f32 volume_, f32 ramp_, |
| 71 | const f32 ramp_, const u32 sample_count); | 71 | u32 sample_count); |
| 72 | 72 | ||
| 73 | } // namespace AudioCore::AudioRenderer | 73 | } // namespace AudioCore::AudioRenderer |
diff --git a/src/audio_core/renderer/command/mix/mix_ramp_grouped.h b/src/audio_core/renderer/command/mix/mix_ramp_grouped.h index 027276e5a..3b0ce67ef 100644 --- a/src/audio_core/renderer/command/mix/mix_ramp_grouped.h +++ b/src/audio_core/renderer/command/mix/mix_ramp_grouped.h | |||
| @@ -50,9 +50,9 @@ struct MixRampGroupedCommand : ICommand { | |||
| 50 | std::array<s16, MaxMixBuffers> inputs; | 50 | std::array<s16, MaxMixBuffers> inputs; |
| 51 | /// Output mix buffer indexes for each mix buffer | 51 | /// Output mix buffer indexes for each mix buffer |
| 52 | std::array<s16, MaxMixBuffers> outputs; | 52 | std::array<s16, MaxMixBuffers> outputs; |
| 53 | /// Previous mix vloumes for each mix buffer | 53 | /// Previous mix volumes for each mix buffer |
| 54 | std::array<f32, MaxMixBuffers> prev_volumes; | 54 | std::array<f32, MaxMixBuffers> prev_volumes; |
| 55 | /// Current mix vloumes for each mix buffer | 55 | /// Current mix volumes for each mix buffer |
| 56 | std::array<f32, MaxMixBuffers> volumes; | 56 | std::array<f32, MaxMixBuffers> volumes; |
| 57 | /// Pointer to the previous sample buffer, used for depop | 57 | /// Pointer to the previous sample buffer, used for depop |
| 58 | CpuAddr previous_samples; | 58 | CpuAddr previous_samples; |
diff --git a/src/audio_core/renderer/command/sink/device.cpp b/src/audio_core/renderer/command/sink/device.cpp index 47e0c6722..e88372a75 100644 --- a/src/audio_core/renderer/command/sink/device.cpp +++ b/src/audio_core/renderer/command/sink/device.cpp | |||
| @@ -46,6 +46,10 @@ void DeviceSinkCommand::Process(const ADSP::CommandListProcessor& processor) { | |||
| 46 | 46 | ||
| 47 | out_buffer.tag = reinterpret_cast<u64>(samples.data()); | 47 | out_buffer.tag = reinterpret_cast<u64>(samples.data()); |
| 48 | stream->AppendBuffer(out_buffer, samples); | 48 | stream->AppendBuffer(out_buffer, samples); |
| 49 | |||
| 50 | if (stream->IsPaused()) { | ||
| 51 | stream->Start(); | ||
| 52 | } | ||
| 49 | } | 53 | } |
| 50 | 54 | ||
| 51 | bool DeviceSinkCommand::Verify(const ADSP::CommandListProcessor& processor) { | 55 | bool DeviceSinkCommand::Verify(const ADSP::CommandListProcessor& processor) { |
diff --git a/src/audio_core/renderer/effect/effect_context.h b/src/audio_core/renderer/effect/effect_context.h index 85955bd9c..8f6d6e7d8 100644 --- a/src/audio_core/renderer/effect/effect_context.h +++ b/src/audio_core/renderer/effect/effect_context.h | |||
| @@ -15,15 +15,15 @@ class EffectContext { | |||
| 15 | public: | 15 | public: |
| 16 | /** | 16 | /** |
| 17 | * Initialize the effect context | 17 | * Initialize the effect context |
| 18 | * @param effect_infos List of effect infos for this context | 18 | * @param effect_infos_ - List of effect infos for this context |
| 19 | * @param effect_count The number of effects in the list | 19 | * @param effect_count_ - The number of effects in the list |
| 20 | * @param result_states_cpu The workbuffer of result states for the CPU for this context | 20 | * @param result_states_cpu_ - The workbuffer of result states for the CPU for this context |
| 21 | * @param result_states_dsp The workbuffer of result states for the DSP for this context | 21 | * @param result_states_dsp_ - The workbuffer of result states for the DSP for this context |
| 22 | * @param state_count The number of result states | 22 | * @param dsp_state_count - The number of result states |
| 23 | */ | 23 | */ |
| 24 | void Initialize(std::span<EffectInfoBase> effect_infos_, const u32 effect_count_, | 24 | void Initialize(std::span<EffectInfoBase> effect_infos_, u32 effect_count_, |
| 25 | std::span<EffectResultState> result_states_cpu_, | 25 | std::span<EffectResultState> result_states_cpu_, |
| 26 | std::span<EffectResultState> result_states_dsp_, const size_t dsp_state_count); | 26 | std::span<EffectResultState> result_states_dsp_, size_t dsp_state_count); |
| 27 | 27 | ||
| 28 | /** | 28 | /** |
| 29 | * Get the EffectInfo for a given index | 29 | * Get the EffectInfo for a given index |
diff --git a/src/audio_core/renderer/effect/effect_info_base.h b/src/audio_core/renderer/effect/effect_info_base.h index 8c9583878..8525fde05 100644 --- a/src/audio_core/renderer/effect/effect_info_base.h +++ b/src/audio_core/renderer/effect/effect_info_base.h | |||
| @@ -291,7 +291,7 @@ public: | |||
| 291 | * Update the info with new parameters, version 1. | 291 | * Update the info with new parameters, version 1. |
| 292 | * | 292 | * |
| 293 | * @param error_info - Used to write call result code. | 293 | * @param error_info - Used to write call result code. |
| 294 | * @param in_params - New parameters to update the info with. | 294 | * @param params - New parameters to update the info with. |
| 295 | * @param pool_mapper - Pool for mapping buffers. | 295 | * @param pool_mapper - Pool for mapping buffers. |
| 296 | */ | 296 | */ |
| 297 | virtual void Update(BehaviorInfo::ErrorInfo& error_info, | 297 | virtual void Update(BehaviorInfo::ErrorInfo& error_info, |
| @@ -305,7 +305,7 @@ public: | |||
| 305 | * Update the info with new parameters, version 2. | 305 | * Update the info with new parameters, version 2. |
| 306 | * | 306 | * |
| 307 | * @param error_info - Used to write call result code. | 307 | * @param error_info - Used to write call result code. |
| 308 | * @param in_params - New parameters to update the info with. | 308 | * @param params - New parameters to update the info with. |
| 309 | * @param pool_mapper - Pool for mapping buffers. | 309 | * @param pool_mapper - Pool for mapping buffers. |
| 310 | */ | 310 | */ |
| 311 | virtual void Update(BehaviorInfo::ErrorInfo& error_info, | 311 | virtual void Update(BehaviorInfo::ErrorInfo& error_info, |
diff --git a/src/audio_core/renderer/memory/address_info.h b/src/audio_core/renderer/memory/address_info.h index 4cfefea8e..bb5c930e1 100644 --- a/src/audio_core/renderer/memory/address_info.h +++ b/src/audio_core/renderer/memory/address_info.h | |||
| @@ -19,8 +19,8 @@ public: | |||
| 19 | /** | 19 | /** |
| 20 | * Setup a new AddressInfo. | 20 | * Setup a new AddressInfo. |
| 21 | * | 21 | * |
| 22 | * @param cpu_address - The CPU address of this region. | 22 | * @param cpu_address_ - The CPU address of this region. |
| 23 | * @param size - The size of this region. | 23 | * @param size_ - The size of this region. |
| 24 | */ | 24 | */ |
| 25 | void Setup(CpuAddr cpu_address_, u64 size_) { | 25 | void Setup(CpuAddr cpu_address_, u64 size_) { |
| 26 | cpu_address = cpu_address_; | 26 | cpu_address = cpu_address_; |
| @@ -42,7 +42,6 @@ public: | |||
| 42 | * Assign this region to a memory pool. | 42 | * Assign this region to a memory pool. |
| 43 | * | 43 | * |
| 44 | * @param memory_pool_ - Memory pool to assign. | 44 | * @param memory_pool_ - Memory pool to assign. |
| 45 | * @return The CpuAddr address of this region. | ||
| 46 | */ | 45 | */ |
| 47 | void SetPool(MemoryPoolInfo* memory_pool_) { | 46 | void SetPool(MemoryPoolInfo* memory_pool_) { |
| 48 | memory_pool = memory_pool_; | 47 | memory_pool = memory_pool_; |
diff --git a/src/audio_core/renderer/nodes/node_states.h b/src/audio_core/renderer/nodes/node_states.h index a1e0958a2..c0fced56f 100644 --- a/src/audio_core/renderer/nodes/node_states.h +++ b/src/audio_core/renderer/nodes/node_states.h | |||
| @@ -112,11 +112,11 @@ public: | |||
| 112 | /** | 112 | /** |
| 113 | * Initialize the node states. | 113 | * Initialize the node states. |
| 114 | * | 114 | * |
| 115 | * @param buffer - The workbuffer to use. Unused. | 115 | * @param buffer_ - The workbuffer to use. Unused. |
| 116 | * @param node_buffer_size - The size of the workbuffer. Unused. | 116 | * @param node_buffer_size - The size of the workbuffer. Unused. |
| 117 | * @param count - The number of nodes in the graph. | 117 | * @param count - The number of nodes in the graph. |
| 118 | */ | 118 | */ |
| 119 | void Initialize(std::span<u8> nodes, u64 node_buffer_size, u32 count); | 119 | void Initialize(std::span<u8> buffer_, u64 node_buffer_size, u32 count); |
| 120 | 120 | ||
| 121 | /** | 121 | /** |
| 122 | * Sort the graph. Only calls DepthFirstSearch. | 122 | * Sort the graph. Only calls DepthFirstSearch. |
diff --git a/src/audio_core/renderer/performance/performance_manager.h b/src/audio_core/renderer/performance/performance_manager.h index b82176bef..b65caa9b6 100644 --- a/src/audio_core/renderer/performance/performance_manager.h +++ b/src/audio_core/renderer/performance/performance_manager.h | |||
| @@ -73,7 +73,8 @@ public: | |||
| 73 | * Calculate the required size for the performance workbuffer. | 73 | * Calculate the required size for the performance workbuffer. |
| 74 | * | 74 | * |
| 75 | * @param behavior - Check which version is supported. | 75 | * @param behavior - Check which version is supported. |
| 76 | * @param params - Input parameters. | 76 | * @param params - Input parameters. |
| 77 | * | ||
| 77 | * @return Required workbuffer size. | 78 | * @return Required workbuffer size. |
| 78 | */ | 79 | */ |
| 79 | static u64 GetRequiredBufferSizeForPerformanceMetricsPerFrame( | 80 | static u64 GetRequiredBufferSizeForPerformanceMetricsPerFrame( |
| @@ -104,7 +105,7 @@ public: | |||
| 104 | * @param workbuffer - Workbuffer to use for performance frames. | 105 | * @param workbuffer - Workbuffer to use for performance frames. |
| 105 | * @param workbuffer_size - Size of the workbuffer. | 106 | * @param workbuffer_size - Size of the workbuffer. |
| 106 | * @param params - Input parameters. | 107 | * @param params - Input parameters. |
| 107 | * @param behavior - Behaviour to check version and data format. | 108 | * @param behavior - Behaviour to check version and data format. |
| 108 | * @param memory_pool - Used to translate the workbuffer address for the DSP. | 109 | * @param memory_pool - Used to translate the workbuffer address for the DSP. |
| 109 | */ | 110 | */ |
| 110 | virtual void Initialize(std::span<u8> workbuffer, u64 workbuffer_size, | 111 | virtual void Initialize(std::span<u8> workbuffer, u64 workbuffer_size, |
| @@ -160,7 +161,8 @@ public: | |||
| 160 | * workbuffer, to be written by the AudioRenderer. | 161 | * workbuffer, to be written by the AudioRenderer. |
| 161 | * | 162 | * |
| 162 | * @param addresses - Filled with pointers to the new detail, which should be passed | 163 | * @param addresses - Filled with pointers to the new detail, which should be passed |
| 163 | * to the AudioRenderer with Performance commands to be written. | 164 | * to the AudioRenderer with Performance commands to be written. |
| 165 | * @param detail_type - Performance detail type. | ||
| 164 | * @param entry_type - The type of this detail. See PerformanceEntryType | 166 | * @param entry_type - The type of this detail. See PerformanceEntryType |
| 165 | * @param node_id - Node id for this detail. | 167 | * @param node_id - Node id for this detail. |
| 166 | * @return True if a new detail was created and the offsets are valid, otherwise false. | 168 | * @return True if a new detail was created and the offsets are valid, otherwise false. |
diff --git a/src/audio_core/renderer/system_manager.cpp b/src/audio_core/renderer/system_manager.cpp index b326819ed..9c1331e19 100644 --- a/src/audio_core/renderer/system_manager.cpp +++ b/src/audio_core/renderer/system_manager.cpp | |||
| @@ -15,17 +15,14 @@ MICROPROFILE_DEFINE(Audio_RenderSystemManager, "Audio", "Render System Manager", | |||
| 15 | MP_RGB(60, 19, 97)); | 15 | MP_RGB(60, 19, 97)); |
| 16 | 16 | ||
| 17 | namespace AudioCore::AudioRenderer { | 17 | namespace AudioCore::AudioRenderer { |
| 18 | constexpr std::chrono::nanoseconds BaseRenderTime{5'000'000UL}; | 18 | constexpr std::chrono::nanoseconds RENDER_TIME{5'000'000UL}; |
| 19 | constexpr std::chrono::nanoseconds RenderTimeOffset{400'000UL}; | ||
| 20 | 19 | ||
| 21 | SystemManager::SystemManager(Core::System& core_) | 20 | SystemManager::SystemManager(Core::System& core_) |
| 22 | : core{core_}, adsp{core.AudioCore().GetADSP()}, mailbox{adsp.GetRenderMailbox()}, | 21 | : core{core_}, adsp{core.AudioCore().GetADSP()}, mailbox{adsp.GetRenderMailbox()}, |
| 23 | thread_event{Core::Timing::CreateEvent( | 22 | thread_event{Core::Timing::CreateEvent( |
| 24 | "AudioRendererSystemManager", [this](std::uintptr_t, s64 time, std::chrono::nanoseconds) { | 23 | "AudioRendererSystemManager", [this](std::uintptr_t, s64 time, std::chrono::nanoseconds) { |
| 25 | return ThreadFunc2(time); | 24 | return ThreadFunc2(time); |
| 26 | })} { | 25 | })} {} |
| 27 | core.CoreTiming().RegisterPauseCallback([this](bool paused) { PauseCallback(paused); }); | ||
| 28 | } | ||
| 29 | 26 | ||
| 30 | SystemManager::~SystemManager() { | 27 | SystemManager::~SystemManager() { |
| 31 | Stop(); | 28 | Stop(); |
| @@ -36,8 +33,8 @@ bool SystemManager::InitializeUnsafe() { | |||
| 36 | if (adsp.Start()) { | 33 | if (adsp.Start()) { |
| 37 | active = true; | 34 | active = true; |
| 38 | thread = std::jthread([this](std::stop_token stop_token) { ThreadFunc(); }); | 35 | thread = std::jthread([this](std::stop_token stop_token) { ThreadFunc(); }); |
| 39 | core.CoreTiming().ScheduleLoopingEvent(std::chrono::nanoseconds(0), | 36 | core.CoreTiming().ScheduleLoopingEvent(std::chrono::nanoseconds(0), RENDER_TIME, |
| 40 | BaseRenderTime - RenderTimeOffset, thread_event); | 37 | thread_event); |
| 41 | } | 38 | } |
| 42 | } | 39 | } |
| 43 | 40 | ||
| @@ -121,42 +118,9 @@ void SystemManager::ThreadFunc() { | |||
| 121 | } | 118 | } |
| 122 | 119 | ||
| 123 | std::optional<std::chrono::nanoseconds> SystemManager::ThreadFunc2(s64 time) { | 120 | std::optional<std::chrono::nanoseconds> SystemManager::ThreadFunc2(s64 time) { |
| 124 | std::optional<std::chrono::nanoseconds> new_schedule_time{std::nullopt}; | ||
| 125 | const auto queue_size{core.AudioCore().GetStreamQueue()}; | ||
| 126 | switch (state) { | ||
| 127 | case StreamState::Filling: | ||
| 128 | if (queue_size >= 5) { | ||
| 129 | new_schedule_time = BaseRenderTime; | ||
| 130 | state = StreamState::Steady; | ||
| 131 | } | ||
| 132 | break; | ||
| 133 | case StreamState::Steady: | ||
| 134 | if (queue_size <= 2) { | ||
| 135 | new_schedule_time = BaseRenderTime - RenderTimeOffset; | ||
| 136 | state = StreamState::Filling; | ||
| 137 | } else if (queue_size > 5) { | ||
| 138 | new_schedule_time = BaseRenderTime + RenderTimeOffset; | ||
| 139 | state = StreamState::Draining; | ||
| 140 | } | ||
| 141 | break; | ||
| 142 | case StreamState::Draining: | ||
| 143 | if (queue_size <= 5) { | ||
| 144 | new_schedule_time = BaseRenderTime; | ||
| 145 | state = StreamState::Steady; | ||
| 146 | } | ||
| 147 | break; | ||
| 148 | } | ||
| 149 | |||
| 150 | update.store(true); | 121 | update.store(true); |
| 151 | update.notify_all(); | 122 | update.notify_all(); |
| 152 | return new_schedule_time; | 123 | return std::nullopt; |
| 153 | } | ||
| 154 | |||
| 155 | void SystemManager::PauseCallback(bool paused) { | ||
| 156 | if (paused && core.IsPoweredOn() && core.IsShuttingDown()) { | ||
| 157 | update.store(true); | ||
| 158 | update.notify_all(); | ||
| 159 | } | ||
| 160 | } | 124 | } |
| 161 | 125 | ||
| 162 | } // namespace AudioCore::AudioRenderer | 126 | } // namespace AudioCore::AudioRenderer |
diff --git a/src/audio_core/renderer/system_manager.h b/src/audio_core/renderer/system_manager.h index 1291e9e0e..81457a3a1 100644 --- a/src/audio_core/renderer/system_manager.h +++ b/src/audio_core/renderer/system_manager.h | |||
| @@ -73,13 +73,6 @@ private: | |||
| 73 | */ | 73 | */ |
| 74 | std::optional<std::chrono::nanoseconds> ThreadFunc2(s64 time); | 74 | std::optional<std::chrono::nanoseconds> ThreadFunc2(s64 time); |
| 75 | 75 | ||
| 76 | /** | ||
| 77 | * Callback from core timing when pausing, used to detect shutdowns and stop ThreadFunc. | ||
| 78 | * | ||
| 79 | * @param paused - Are we pausing or resuming? | ||
| 80 | */ | ||
| 81 | void PauseCallback(bool paused); | ||
| 82 | |||
| 83 | enum class StreamState { | 76 | enum class StreamState { |
| 84 | Filling, | 77 | Filling, |
| 85 | Steady, | 78 | Steady, |
| @@ -106,8 +99,6 @@ private: | |||
| 106 | std::shared_ptr<Core::Timing::EventType> thread_event; | 99 | std::shared_ptr<Core::Timing::EventType> thread_event; |
| 107 | /// Atomic for main thread to wait on | 100 | /// Atomic for main thread to wait on |
| 108 | std::atomic<bool> update{}; | 101 | std::atomic<bool> update{}; |
| 109 | /// Current state of the streams | ||
| 110 | StreamState state{StreamState::Filling}; | ||
| 111 | }; | 102 | }; |
| 112 | 103 | ||
| 113 | } // namespace AudioCore::AudioRenderer | 104 | } // namespace AudioCore::AudioRenderer |
diff --git a/src/audio_core/renderer/upsampler/upsampler_manager.h b/src/audio_core/renderer/upsampler/upsampler_manager.h index 70cd42b08..83c697c0c 100644 --- a/src/audio_core/renderer/upsampler/upsampler_manager.h +++ b/src/audio_core/renderer/upsampler/upsampler_manager.h | |||
| @@ -27,7 +27,7 @@ public: | |||
| 27 | /** | 27 | /** |
| 28 | * Free the given upsampler. | 28 | * Free the given upsampler. |
| 29 | * | 29 | * |
| 30 | * @param The upsampler to be freed. | 30 | * @param info The upsampler to be freed. |
| 31 | */ | 31 | */ |
| 32 | void Free(UpsamplerInfo* info); | 32 | void Free(UpsamplerInfo* info); |
| 33 | 33 | ||
diff --git a/src/audio_core/renderer/voice/voice_info.h b/src/audio_core/renderer/voice/voice_info.h index 896723e0c..930180895 100644 --- a/src/audio_core/renderer/voice/voice_info.h +++ b/src/audio_core/renderer/voice/voice_info.h | |||
| @@ -185,7 +185,8 @@ public: | |||
| 185 | /** | 185 | /** |
| 186 | * Does this voice ned an update? | 186 | * Does this voice ned an update? |
| 187 | * | 187 | * |
| 188 | * @param params - Input parametetrs to check matching. | 188 | * @param params - Input parameters to check matching. |
| 189 | * | ||
| 189 | * @return True if this voice needs an update, otherwise false. | 190 | * @return True if this voice needs an update, otherwise false. |
| 190 | */ | 191 | */ |
| 191 | bool ShouldUpdateParameters(const InParameter& params) const; | 192 | bool ShouldUpdateParameters(const InParameter& params) const; |
| @@ -194,9 +195,9 @@ public: | |||
| 194 | * Update the parameters of this voice. | 195 | * Update the parameters of this voice. |
| 195 | * | 196 | * |
| 196 | * @param error_info - Output error code. | 197 | * @param error_info - Output error code. |
| 197 | * @param params - Input parametters to udpate from. | 198 | * @param params - Input parameters to update from. |
| 198 | * @param pool_mapper - Used to map buffers. | 199 | * @param pool_mapper - Used to map buffers. |
| 199 | * @param behavior - behavior to check supported features. | 200 | * @param behavior - behavior to check supported features. |
| 200 | */ | 201 | */ |
| 201 | void UpdateParameters(BehaviorInfo::ErrorInfo& error_info, const InParameter& params, | 202 | void UpdateParameters(BehaviorInfo::ErrorInfo& error_info, const InParameter& params, |
| 202 | const PoolMapper& pool_mapper, const BehaviorInfo& behavior); | 203 | const PoolMapper& pool_mapper, const BehaviorInfo& behavior); |
| @@ -218,12 +219,12 @@ public: | |||
| 218 | /** | 219 | /** |
| 219 | * Update all wavebuffers. | 220 | * Update all wavebuffers. |
| 220 | * | 221 | * |
| 221 | * @param error_infos - Output 2D array of errors, 2 per wavebuffer. | 222 | * @param error_infos - Output 2D array of errors, 2 per wavebuffer. |
| 222 | * @param error_count - Number of errors provided. Unused. | 223 | * @param error_count - Number of errors provided. Unused. |
| 223 | * @param params - Input parametters to be used for the update. | 224 | * @param params - Input parameters to be used for the update. |
| 224 | * @param voice_states - The voice states for each channel in this voice to be updated. | 225 | * @param voice_states - The voice states for each channel in this voice to be updated. |
| 225 | * @param pool_mapper - Used to map the wavebuffers. | 226 | * @param pool_mapper - Used to map the wavebuffers. |
| 226 | * @param behavior - Used to check for supported features. | 227 | * @param behavior - Used to check for supported features. |
| 227 | */ | 228 | */ |
| 228 | void UpdateWaveBuffers(std::span<std::array<BehaviorInfo::ErrorInfo, 2>> error_infos, | 229 | void UpdateWaveBuffers(std::span<std::array<BehaviorInfo::ErrorInfo, 2>> error_infos, |
| 229 | u32 error_count, const InParameter& params, | 230 | u32 error_count, const InParameter& params, |
| @@ -233,13 +234,13 @@ public: | |||
| 233 | /** | 234 | /** |
| 234 | * Update a wavebuffer. | 235 | * Update a wavebuffer. |
| 235 | * | 236 | * |
| 236 | * @param error_infos - Output array of errors. | 237 | * @param error_info - Output array of errors. |
| 237 | * @param wave_buffer - The wavebuffer to be updated. | 238 | * @param wave_buffer - The wavebuffer to be updated. |
| 238 | * @param wave_buffer_internal - Input parametters to be used for the update. | 239 | * @param wave_buffer_internal - Input parametters to be used for the update. |
| 239 | * @param sample_format - Sample format of the wavebuffer. | 240 | * @param sample_format - Sample format of the wavebuffer. |
| 240 | * @param valid - Is this wavebuffer valid? | 241 | * @param valid - Is this wavebuffer valid? |
| 241 | * @param pool_mapper - Used to map the wavebuffers. | 242 | * @param pool_mapper - Used to map the wavebuffers. |
| 242 | * @param behavior - Used to check for supported features. | 243 | * @param behavior - Used to check for supported features. |
| 243 | */ | 244 | */ |
| 244 | void UpdateWaveBuffer(std::span<BehaviorInfo::ErrorInfo> error_info, WaveBuffer& wave_buffer, | 245 | void UpdateWaveBuffer(std::span<BehaviorInfo::ErrorInfo> error_info, WaveBuffer& wave_buffer, |
| 245 | const WaveBufferInternal& wave_buffer_internal, | 246 | const WaveBufferInternal& wave_buffer_internal, |
| @@ -276,7 +277,7 @@ public: | |||
| 276 | /** | 277 | /** |
| 277 | * Check if this voice has any mixing connections. | 278 | * Check if this voice has any mixing connections. |
| 278 | * | 279 | * |
| 279 | * @return True if this voice participes in mixing, otherwise false. | 280 | * @return True if this voice participates in mixing, otherwise false. |
| 280 | */ | 281 | */ |
| 281 | bool HasAnyConnection() const; | 282 | bool HasAnyConnection() const; |
| 282 | 283 | ||
| @@ -301,7 +302,8 @@ public: | |||
| 301 | /** | 302 | /** |
| 302 | * Update this voice on command generation. | 303 | * Update this voice on command generation. |
| 303 | * | 304 | * |
| 304 | * @param voice_states - Voice states for these wavebuffers. | 305 | * @param voice_context - Voice context for these wavebuffers. |
| 306 | * | ||
| 305 | * @return True if this voice should be generated, otherwise false. | 307 | * @return True if this voice should be generated, otherwise false. |
| 306 | */ | 308 | */ |
| 307 | bool UpdateForCommandGeneration(VoiceContext& voice_context); | 309 | bool UpdateForCommandGeneration(VoiceContext& voice_context); |
diff --git a/src/audio_core/sink/cubeb_sink.cpp b/src/audio_core/sink/cubeb_sink.cpp index a4e28de6d..36b115ad6 100644 --- a/src/audio_core/sink/cubeb_sink.cpp +++ b/src/audio_core/sink/cubeb_sink.cpp | |||
| @@ -1,21 +1,13 @@ | |||
| 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 <algorithm> | ||
| 5 | #include <atomic> | ||
| 6 | #include <span> | 4 | #include <span> |
| 5 | #include <vector> | ||
| 7 | 6 | ||
| 8 | #include "audio_core/audio_core.h" | 7 | #include "audio_core/common/common.h" |
| 9 | #include "audio_core/audio_event.h" | ||
| 10 | #include "audio_core/audio_manager.h" | ||
| 11 | #include "audio_core/sink/cubeb_sink.h" | 8 | #include "audio_core/sink/cubeb_sink.h" |
| 12 | #include "audio_core/sink/sink_stream.h" | 9 | #include "audio_core/sink/sink_stream.h" |
| 13 | #include "common/assert.h" | ||
| 14 | #include "common/fixed_point.h" | ||
| 15 | #include "common/logging/log.h" | 10 | #include "common/logging/log.h" |
| 16 | #include "common/reader_writer_queue.h" | ||
| 17 | #include "common/ring_buffer.h" | ||
| 18 | #include "common/settings.h" | ||
| 19 | #include "core/core.h" | 11 | #include "core/core.h" |
| 20 | 12 | ||
| 21 | #ifdef _WIN32 | 13 | #ifdef _WIN32 |
| @@ -42,10 +34,10 @@ public: | |||
| 42 | * @param system_ - Core system. | 34 | * @param system_ - Core system. |
| 43 | * @param event - Event used only for audio renderer, signalled on buffer consume. | 35 | * @param event - Event used only for audio renderer, signalled on buffer consume. |
| 44 | */ | 36 | */ |
| 45 | CubebSinkStream(cubeb* ctx_, const u32 device_channels_, const u32 system_channels_, | 37 | CubebSinkStream(cubeb* ctx_, u32 device_channels_, u32 system_channels_, |
| 46 | cubeb_devid output_device, cubeb_devid input_device, const std::string& name_, | 38 | cubeb_devid output_device, cubeb_devid input_device, const std::string& name_, |
| 47 | const StreamType type_, Core::System& system_) | 39 | StreamType type_, Core::System& system_) |
| 48 | : ctx{ctx_}, type{type_}, system{system_} { | 40 | : SinkStream(system_, type_), ctx{ctx_} { |
| 49 | #ifdef _WIN32 | 41 | #ifdef _WIN32 |
| 50 | CoInitializeEx(nullptr, COINIT_MULTITHREADED); | 42 | CoInitializeEx(nullptr, COINIT_MULTITHREADED); |
| 51 | #endif | 43 | #endif |
| @@ -79,12 +71,10 @@ public: | |||
| 79 | 71 | ||
| 80 | minimum_latency = std::max(minimum_latency, 256u); | 72 | minimum_latency = std::max(minimum_latency, 256u); |
| 81 | 73 | ||
| 82 | playing_buffer.consumed = true; | 74 | LOG_INFO(Service_Audio, |
| 83 | 75 | "Opening cubeb stream {} type {} with: rate {} channels {} (system channels {}) " | |
| 84 | LOG_DEBUG(Service_Audio, | 76 | "latency {}", |
| 85 | "Opening cubeb stream {} type {} with: rate {} channels {} (system channels {}) " | 77 | name, type, params.rate, params.channels, system_channels, minimum_latency); |
| 86 | "latency {}", | ||
| 87 | name, type, params.rate, params.channels, system_channels, minimum_latency); | ||
| 88 | 78 | ||
| 89 | auto init_error{0}; | 79 | auto init_error{0}; |
| 90 | if (type == StreamType::In) { | 80 | if (type == StreamType::In) { |
| @@ -111,6 +101,8 @@ public: | |||
| 111 | ~CubebSinkStream() override { | 101 | ~CubebSinkStream() override { |
| 112 | LOG_DEBUG(Service_Audio, "Destructing cubeb stream {}", name); | 102 | LOG_DEBUG(Service_Audio, "Destructing cubeb stream {}", name); |
| 113 | 103 | ||
| 104 | Unstall(); | ||
| 105 | |||
| 114 | if (!ctx) { | 106 | if (!ctx) { |
| 115 | return; | 107 | return; |
| 116 | } | 108 | } |
| @@ -136,21 +128,14 @@ public: | |||
| 136 | * @param resume - Set to true if this is resuming the stream a previously-active stream. | 128 | * @param resume - Set to true if this is resuming the stream a previously-active stream. |
| 137 | * Default false. | 129 | * Default false. |
| 138 | */ | 130 | */ |
| 139 | void Start(const bool resume = false) override { | 131 | void Start(bool resume = false) override { |
| 140 | if (!ctx) { | 132 | if (!ctx || !paused) { |
| 141 | return; | 133 | return; |
| 142 | } | 134 | } |
| 143 | 135 | ||
| 144 | if (resume && was_playing) { | 136 | paused = false; |
| 145 | if (cubeb_stream_start(stream_backend) != CUBEB_OK) { | 137 | if (cubeb_stream_start(stream_backend) != CUBEB_OK) { |
| 146 | LOG_CRITICAL(Audio_Sink, "Error starting cubeb stream"); | 138 | LOG_CRITICAL(Audio_Sink, "Error starting cubeb stream"); |
| 147 | } | ||
| 148 | paused = false; | ||
| 149 | } else if (!resume) { | ||
| 150 | if (cubeb_stream_start(stream_backend) != CUBEB_OK) { | ||
| 151 | LOG_CRITICAL(Audio_Sink, "Error starting cubeb stream"); | ||
| 152 | } | ||
| 153 | paused = false; | ||
| 154 | } | 139 | } |
| 155 | } | 140 | } |
| 156 | 141 | ||
| @@ -158,204 +143,20 @@ public: | |||
| 158 | * Stop the sink stream. | 143 | * Stop the sink stream. |
| 159 | */ | 144 | */ |
| 160 | void Stop() override { | 145 | void Stop() override { |
| 161 | if (!ctx) { | 146 | Unstall(); |
| 147 | |||
| 148 | if (!ctx || paused) { | ||
| 162 | return; | 149 | return; |
| 163 | } | 150 | } |
| 164 | 151 | ||
| 152 | paused = true; | ||
| 165 | if (cubeb_stream_stop(stream_backend) != CUBEB_OK) { | 153 | if (cubeb_stream_stop(stream_backend) != CUBEB_OK) { |
| 166 | LOG_CRITICAL(Audio_Sink, "Error stopping cubeb stream"); | 154 | LOG_CRITICAL(Audio_Sink, "Error stopping cubeb stream"); |
| 167 | } | 155 | } |
| 168 | |||
| 169 | was_playing.store(!paused); | ||
| 170 | paused = true; | ||
| 171 | } | ||
| 172 | |||
| 173 | /** | ||
| 174 | * Append a new buffer and its samples to a waiting queue to play. | ||
| 175 | * | ||
| 176 | * @param buffer - Audio buffer information to be queued. | ||
| 177 | * @param samples - The s16 samples to be queue for playback. | ||
| 178 | */ | ||
| 179 | void AppendBuffer(::AudioCore::Sink::SinkBuffer& buffer, std::vector<s16>& samples) override { | ||
| 180 | if (type == StreamType::In) { | ||
| 181 | queue.enqueue(buffer); | ||
| 182 | queued_buffers++; | ||
| 183 | } else { | ||
| 184 | constexpr s32 min{std::numeric_limits<s16>::min()}; | ||
| 185 | constexpr s32 max{std::numeric_limits<s16>::max()}; | ||
| 186 | |||
| 187 | auto yuzu_volume{Settings::Volume()}; | ||
| 188 | auto volume{system_volume * device_volume * yuzu_volume}; | ||
| 189 | |||
| 190 | if (system_channels == 6 && device_channels == 2) { | ||
| 191 | // We're given 6 channels, but our device only outputs 2, so downmix. | ||
| 192 | constexpr std::array<f32, 4> down_mix_coeff{1.0f, 0.707f, 0.251f, 0.707f}; | ||
| 193 | |||
| 194 | for (u32 read_index = 0, write_index = 0; read_index < samples.size(); | ||
| 195 | read_index += system_channels, write_index += device_channels) { | ||
| 196 | const auto left_sample{ | ||
| 197 | ((Common::FixedPoint<49, 15>( | ||
| 198 | samples[read_index + static_cast<u32>(Channels::FrontLeft)]) * | ||
| 199 | down_mix_coeff[0] + | ||
| 200 | samples[read_index + static_cast<u32>(Channels::Center)] * | ||
| 201 | down_mix_coeff[1] + | ||
| 202 | samples[read_index + static_cast<u32>(Channels::LFE)] * | ||
| 203 | down_mix_coeff[2] + | ||
| 204 | samples[read_index + static_cast<u32>(Channels::BackLeft)] * | ||
| 205 | down_mix_coeff[3]) * | ||
| 206 | volume) | ||
| 207 | .to_int()}; | ||
| 208 | |||
| 209 | const auto right_sample{ | ||
| 210 | ((Common::FixedPoint<49, 15>( | ||
| 211 | samples[read_index + static_cast<u32>(Channels::FrontRight)]) * | ||
| 212 | down_mix_coeff[0] + | ||
| 213 | samples[read_index + static_cast<u32>(Channels::Center)] * | ||
| 214 | down_mix_coeff[1] + | ||
| 215 | samples[read_index + static_cast<u32>(Channels::LFE)] * | ||
| 216 | down_mix_coeff[2] + | ||
| 217 | samples[read_index + static_cast<u32>(Channels::BackRight)] * | ||
| 218 | down_mix_coeff[3]) * | ||
| 219 | volume) | ||
| 220 | .to_int()}; | ||
| 221 | |||
| 222 | samples[write_index + static_cast<u32>(Channels::FrontLeft)] = | ||
| 223 | static_cast<s16>(std::clamp(left_sample, min, max)); | ||
| 224 | samples[write_index + static_cast<u32>(Channels::FrontRight)] = | ||
| 225 | static_cast<s16>(std::clamp(right_sample, min, max)); | ||
| 226 | } | ||
| 227 | |||
| 228 | samples.resize(samples.size() / system_channels * device_channels); | ||
| 229 | |||
| 230 | } else if (system_channels == 2 && device_channels == 6) { | ||
| 231 | // We need moar samples! Not all games will provide 6 channel audio. | ||
| 232 | // TODO: Implement some upmixing here. Currently just passthrough, with other | ||
| 233 | // channels left as silence. | ||
| 234 | std::vector<s16> new_samples(samples.size() / system_channels * device_channels, 0); | ||
| 235 | |||
| 236 | for (u32 read_index = 0, write_index = 0; read_index < samples.size(); | ||
| 237 | read_index += system_channels, write_index += device_channels) { | ||
| 238 | const auto left_sample{static_cast<s16>(std::clamp( | ||
| 239 | static_cast<s32>( | ||
| 240 | static_cast<f32>( | ||
| 241 | samples[read_index + static_cast<u32>(Channels::FrontLeft)]) * | ||
| 242 | volume), | ||
| 243 | min, max))}; | ||
| 244 | |||
| 245 | new_samples[write_index + static_cast<u32>(Channels::FrontLeft)] = left_sample; | ||
| 246 | |||
| 247 | const auto right_sample{static_cast<s16>(std::clamp( | ||
| 248 | static_cast<s32>( | ||
| 249 | static_cast<f32>( | ||
| 250 | samples[read_index + static_cast<u32>(Channels::FrontRight)]) * | ||
| 251 | volume), | ||
| 252 | min, max))}; | ||
| 253 | |||
| 254 | new_samples[write_index + static_cast<u32>(Channels::FrontRight)] = | ||
| 255 | right_sample; | ||
| 256 | } | ||
| 257 | samples = std::move(new_samples); | ||
| 258 | |||
| 259 | } else if (volume != 1.0f) { | ||
| 260 | for (u32 i = 0; i < samples.size(); i++) { | ||
| 261 | samples[i] = static_cast<s16>(std::clamp( | ||
| 262 | static_cast<s32>(static_cast<f32>(samples[i]) * volume), min, max)); | ||
| 263 | } | ||
| 264 | } | ||
| 265 | |||
| 266 | samples_buffer.Push(samples); | ||
| 267 | queue.enqueue(buffer); | ||
| 268 | queued_buffers++; | ||
| 269 | } | ||
| 270 | } | ||
| 271 | |||
| 272 | /** | ||
| 273 | * Release a buffer. Audio In only, will fill a buffer with recorded samples. | ||
| 274 | * | ||
| 275 | * @param num_samples - Maximum number of samples to receive. | ||
| 276 | * @return Vector of recorded samples. May have fewer than num_samples. | ||
| 277 | */ | ||
| 278 | std::vector<s16> ReleaseBuffer(const u64 num_samples) override { | ||
| 279 | static constexpr s32 min = std::numeric_limits<s16>::min(); | ||
| 280 | static constexpr s32 max = std::numeric_limits<s16>::max(); | ||
| 281 | |||
| 282 | auto samples{samples_buffer.Pop(num_samples)}; | ||
| 283 | |||
| 284 | // TODO: Up-mix to 6 channels if the game expects it. | ||
| 285 | // For audio input this is unlikely to ever be the case though. | ||
| 286 | |||
| 287 | // Incoming mic volume seems to always be very quiet, so multiply by an additional 8 here. | ||
| 288 | // TODO: Play with this and find something that works better. | ||
| 289 | auto volume{system_volume * device_volume * 8}; | ||
| 290 | for (u32 i = 0; i < samples.size(); i++) { | ||
| 291 | samples[i] = static_cast<s16>( | ||
| 292 | std::clamp(static_cast<s32>(static_cast<f32>(samples[i]) * volume), min, max)); | ||
| 293 | } | ||
| 294 | |||
| 295 | if (samples.size() < num_samples) { | ||
| 296 | samples.resize(num_samples, 0); | ||
| 297 | } | ||
| 298 | return samples; | ||
| 299 | } | ||
| 300 | |||
| 301 | /** | ||
| 302 | * Check if a certain buffer has been consumed (fully played). | ||
| 303 | * | ||
| 304 | * @param tag - Unique tag of a buffer to check for. | ||
| 305 | * @return True if the buffer has been played, otherwise false. | ||
| 306 | */ | ||
| 307 | bool IsBufferConsumed(const u64 tag) override { | ||
| 308 | if (released_buffer.tag == 0) { | ||
| 309 | if (!released_buffers.try_dequeue(released_buffer)) { | ||
| 310 | return false; | ||
| 311 | } | ||
| 312 | } | ||
| 313 | |||
| 314 | if (released_buffer.tag == tag) { | ||
| 315 | released_buffer.tag = 0; | ||
| 316 | return true; | ||
| 317 | } | ||
| 318 | return false; | ||
| 319 | } | ||
| 320 | |||
| 321 | /** | ||
| 322 | * Empty out the buffer queue. | ||
| 323 | */ | ||
| 324 | void ClearQueue() override { | ||
| 325 | samples_buffer.Pop(); | ||
| 326 | while (queue.pop()) { | ||
| 327 | } | ||
| 328 | while (released_buffers.pop()) { | ||
| 329 | } | ||
| 330 | queued_buffers = 0; | ||
| 331 | released_buffer = {}; | ||
| 332 | playing_buffer = {}; | ||
| 333 | playing_buffer.consumed = true; | ||
| 334 | } | 156 | } |
| 335 | 157 | ||
| 336 | private: | 158 | private: |
| 337 | /** | 159 | /** |
| 338 | * Signal events back to the audio system that a buffer was played/can be filled. | ||
| 339 | * | ||
| 340 | * @param buffer - Consumed audio buffer to be released. | ||
| 341 | */ | ||
| 342 | void SignalEvent(const ::AudioCore::Sink::SinkBuffer& buffer) { | ||
| 343 | auto& manager{system.AudioCore().GetAudioManager()}; | ||
| 344 | switch (type) { | ||
| 345 | case StreamType::Out: | ||
| 346 | released_buffers.enqueue(buffer); | ||
| 347 | manager.SetEvent(Event::Type::AudioOutManager, true); | ||
| 348 | break; | ||
| 349 | case StreamType::In: | ||
| 350 | released_buffers.enqueue(buffer); | ||
| 351 | manager.SetEvent(Event::Type::AudioInManager, true); | ||
| 352 | break; | ||
| 353 | case StreamType::Render: | ||
| 354 | break; | ||
| 355 | } | ||
| 356 | } | ||
| 357 | |||
| 358 | /** | ||
| 359 | * Main callback from Cubeb. Either expects samples from us (audio render/audio out), or will | 160 | * Main callback from Cubeb. Either expects samples from us (audio render/audio out), or will |
| 360 | * provide samples to be copied (audio in). | 161 | * provide samples to be copied (audio in). |
| 361 | * | 162 | * |
| @@ -375,106 +176,15 @@ private: | |||
| 375 | 176 | ||
| 376 | const std::size_t num_channels = impl->GetDeviceChannels(); | 177 | const std::size_t num_channels = impl->GetDeviceChannels(); |
| 377 | const std::size_t frame_size = num_channels; | 178 | const std::size_t frame_size = num_channels; |
| 378 | const std::size_t frame_size_bytes = frame_size * sizeof(s16); | ||
| 379 | const std::size_t num_frames{static_cast<size_t>(num_frames_)}; | 179 | const std::size_t num_frames{static_cast<size_t>(num_frames_)}; |
| 380 | size_t frames_written{0}; | ||
| 381 | [[maybe_unused]] bool underrun{false}; | ||
| 382 | 180 | ||
| 383 | if (impl->type == StreamType::In) { | 181 | if (impl->type == StreamType::In) { |
| 384 | // INPUT | ||
| 385 | std::span<const s16> input_buffer{reinterpret_cast<const s16*>(in_buff), | 182 | std::span<const s16> input_buffer{reinterpret_cast<const s16*>(in_buff), |
| 386 | num_frames * frame_size}; | 183 | num_frames * frame_size}; |
| 387 | 184 | impl->ProcessAudioIn(input_buffer, num_frames); | |
| 388 | while (frames_written < num_frames) { | ||
| 389 | auto& playing_buffer{impl->playing_buffer}; | ||
| 390 | |||
| 391 | // If the playing buffer has been consumed or has no frames, we need a new one | ||
| 392 | if (playing_buffer.consumed || playing_buffer.frames == 0) { | ||
| 393 | if (!impl->queue.try_dequeue(impl->playing_buffer)) { | ||
| 394 | // If no buffer was available we've underrun, just push the samples and | ||
| 395 | // continue. | ||
| 396 | underrun = true; | ||
| 397 | impl->samples_buffer.Push(&input_buffer[frames_written * frame_size], | ||
| 398 | (num_frames - frames_written) * frame_size); | ||
| 399 | frames_written = num_frames; | ||
| 400 | continue; | ||
| 401 | } else { | ||
| 402 | // Successfully got a new buffer, mark the old one as consumed and signal. | ||
| 403 | impl->queued_buffers--; | ||
| 404 | impl->SignalEvent(impl->playing_buffer); | ||
| 405 | } | ||
| 406 | } | ||
| 407 | |||
| 408 | // Get the minimum frames available between the currently playing buffer, and the | ||
| 409 | // amount we have left to fill | ||
| 410 | size_t frames_available{ | ||
| 411 | std::min(playing_buffer.frames - playing_buffer.frames_played, | ||
| 412 | num_frames - frames_written)}; | ||
| 413 | |||
| 414 | impl->samples_buffer.Push(&input_buffer[frames_written * frame_size], | ||
| 415 | frames_available * frame_size); | ||
| 416 | |||
| 417 | frames_written += frames_available; | ||
| 418 | playing_buffer.frames_played += frames_available; | ||
| 419 | |||
| 420 | // If that's all the frames in the current buffer, add its samples and mark it as | ||
| 421 | // consumed | ||
| 422 | if (playing_buffer.frames_played >= playing_buffer.frames) { | ||
| 423 | impl->AddPlayedSampleCount(playing_buffer.frames_played * num_channels); | ||
| 424 | impl->playing_buffer.consumed = true; | ||
| 425 | } | ||
| 426 | } | ||
| 427 | |||
| 428 | std::memcpy(&impl->last_frame[0], &input_buffer[(frames_written - 1) * frame_size], | ||
| 429 | frame_size_bytes); | ||
| 430 | } else { | 185 | } else { |
| 431 | // OUTPUT | ||
| 432 | std::span<s16> output_buffer{reinterpret_cast<s16*>(out_buff), num_frames * frame_size}; | 186 | std::span<s16> output_buffer{reinterpret_cast<s16*>(out_buff), num_frames * frame_size}; |
| 433 | 187 | impl->ProcessAudioOutAndRender(output_buffer, num_frames); | |
| 434 | while (frames_written < num_frames) { | ||
| 435 | auto& playing_buffer{impl->playing_buffer}; | ||
| 436 | |||
| 437 | // If the playing buffer has been consumed or has no frames, we need a new one | ||
| 438 | if (playing_buffer.consumed || playing_buffer.frames == 0) { | ||
| 439 | if (!impl->queue.try_dequeue(impl->playing_buffer)) { | ||
| 440 | // If no buffer was available we've underrun, fill the remaining buffer with | ||
| 441 | // the last written frame and continue. | ||
| 442 | underrun = true; | ||
| 443 | for (size_t i = frames_written; i < num_frames; i++) { | ||
| 444 | std::memcpy(&output_buffer[i * frame_size], &impl->last_frame[0], | ||
| 445 | frame_size_bytes); | ||
| 446 | } | ||
| 447 | frames_written = num_frames; | ||
| 448 | continue; | ||
| 449 | } else { | ||
| 450 | // Successfully got a new buffer, mark the old one as consumed and signal. | ||
| 451 | impl->queued_buffers--; | ||
| 452 | impl->SignalEvent(impl->playing_buffer); | ||
| 453 | } | ||
| 454 | } | ||
| 455 | |||
| 456 | // Get the minimum frames available between the currently playing buffer, and the | ||
| 457 | // amount we have left to fill | ||
| 458 | size_t frames_available{ | ||
| 459 | std::min(playing_buffer.frames - playing_buffer.frames_played, | ||
| 460 | num_frames - frames_written)}; | ||
| 461 | |||
| 462 | impl->samples_buffer.Pop(&output_buffer[frames_written * frame_size], | ||
| 463 | frames_available * frame_size); | ||
| 464 | |||
| 465 | frames_written += frames_available; | ||
| 466 | playing_buffer.frames_played += frames_available; | ||
| 467 | |||
| 468 | // If that's all the frames in the current buffer, add its samples and mark it as | ||
| 469 | // consumed | ||
| 470 | if (playing_buffer.frames_played >= playing_buffer.frames) { | ||
| 471 | impl->AddPlayedSampleCount(playing_buffer.frames_played * num_channels); | ||
| 472 | impl->playing_buffer.consumed = true; | ||
| 473 | } | ||
| 474 | } | ||
| 475 | |||
| 476 | std::memcpy(&impl->last_frame[0], &output_buffer[(frames_written - 1) * frame_size], | ||
| 477 | frame_size_bytes); | ||
| 478 | } | 188 | } |
| 479 | 189 | ||
| 480 | return num_frames_; | 190 | return num_frames_; |
| @@ -487,32 +197,12 @@ private: | |||
| 487 | * @param user_data - Custom data pointer passed along, points to a CubebSinkStream. | 197 | * @param user_data - Custom data pointer passed along, points to a CubebSinkStream. |
| 488 | * @param state - New state of the device. | 198 | * @param state - New state of the device. |
| 489 | */ | 199 | */ |
| 490 | static void StateCallback([[maybe_unused]] cubeb_stream* stream, | 200 | static void StateCallback(cubeb_stream*, void*, cubeb_state) {} |
| 491 | [[maybe_unused]] void* user_data, | ||
| 492 | [[maybe_unused]] cubeb_state state) {} | ||
| 493 | 201 | ||
| 494 | /// Main Cubeb context | 202 | /// Main Cubeb context |
| 495 | cubeb* ctx{}; | 203 | cubeb* ctx{}; |
| 496 | /// Cubeb stream backend | 204 | /// Cubeb stream backend |
| 497 | cubeb_stream* stream_backend{}; | 205 | cubeb_stream* stream_backend{}; |
| 498 | /// Name of this stream | ||
| 499 | std::string name{}; | ||
| 500 | /// Type of this stream | ||
| 501 | StreamType type; | ||
| 502 | /// Core system | ||
| 503 | Core::System& system; | ||
| 504 | /// Ring buffer of the samples waiting to be played or consumed | ||
| 505 | Common::RingBuffer<s16, 0x10000> samples_buffer; | ||
| 506 | /// Audio buffers queued and waiting to play | ||
| 507 | Common::ReaderWriterQueue<::AudioCore::Sink::SinkBuffer> queue; | ||
| 508 | /// The currently-playing audio buffer | ||
| 509 | ::AudioCore::Sink::SinkBuffer playing_buffer{}; | ||
| 510 | /// Audio buffers which have been played and are in queue to be released by the audio system | ||
| 511 | Common::ReaderWriterQueue<::AudioCore::Sink::SinkBuffer> released_buffers{}; | ||
| 512 | /// Currently released buffer waiting to be taken by the audio system | ||
| 513 | ::AudioCore::Sink::SinkBuffer released_buffer{}; | ||
| 514 | /// The last played (or received) frame of audio, used when the callback underruns | ||
| 515 | std::array<s16, MaxChannels> last_frame{}; | ||
| 516 | }; | 206 | }; |
| 517 | 207 | ||
| 518 | CubebSink::CubebSink(std::string_view target_device_name) { | 208 | CubebSink::CubebSink(std::string_view target_device_name) { |
| @@ -566,15 +256,15 @@ CubebSink::~CubebSink() { | |||
| 566 | #endif | 256 | #endif |
| 567 | } | 257 | } |
| 568 | 258 | ||
| 569 | SinkStream* CubebSink::AcquireSinkStream(Core::System& system, const u32 system_channels, | 259 | SinkStream* CubebSink::AcquireSinkStream(Core::System& system, u32 system_channels, |
| 570 | const std::string& name, const StreamType type) { | 260 | const std::string& name, StreamType type) { |
| 571 | SinkStreamPtr& stream = sink_streams.emplace_back(std::make_unique<CubebSinkStream>( | 261 | SinkStreamPtr& stream = sink_streams.emplace_back(std::make_unique<CubebSinkStream>( |
| 572 | ctx, device_channels, system_channels, output_device, input_device, name, type, system)); | 262 | ctx, device_channels, system_channels, output_device, input_device, name, type, system)); |
| 573 | 263 | ||
| 574 | return stream.get(); | 264 | return stream.get(); |
| 575 | } | 265 | } |
| 576 | 266 | ||
| 577 | void CubebSink::CloseStream(const SinkStream* stream) { | 267 | void CubebSink::CloseStream(SinkStream* stream) { |
| 578 | for (size_t i = 0; i < sink_streams.size(); i++) { | 268 | for (size_t i = 0; i < sink_streams.size(); i++) { |
| 579 | if (sink_streams[i].get() == stream) { | 269 | if (sink_streams[i].get() == stream) { |
| 580 | sink_streams[i].reset(); | 270 | sink_streams[i].reset(); |
| @@ -588,18 +278,6 @@ void CubebSink::CloseStreams() { | |||
| 588 | sink_streams.clear(); | 278 | sink_streams.clear(); |
| 589 | } | 279 | } |
| 590 | 280 | ||
| 591 | void CubebSink::PauseStreams() { | ||
| 592 | for (auto& stream : sink_streams) { | ||
| 593 | stream->Stop(); | ||
| 594 | } | ||
| 595 | } | ||
| 596 | |||
| 597 | void CubebSink::UnpauseStreams() { | ||
| 598 | for (auto& stream : sink_streams) { | ||
| 599 | stream->Start(true); | ||
| 600 | } | ||
| 601 | } | ||
| 602 | |||
| 603 | f32 CubebSink::GetDeviceVolume() const { | 281 | f32 CubebSink::GetDeviceVolume() const { |
| 604 | if (sink_streams.empty()) { | 282 | if (sink_streams.empty()) { |
| 605 | return 1.0f; | 283 | return 1.0f; |
| @@ -608,19 +286,19 @@ f32 CubebSink::GetDeviceVolume() const { | |||
| 608 | return sink_streams[0]->GetDeviceVolume(); | 286 | return sink_streams[0]->GetDeviceVolume(); |
| 609 | } | 287 | } |
| 610 | 288 | ||
| 611 | void CubebSink::SetDeviceVolume(const f32 volume) { | 289 | void CubebSink::SetDeviceVolume(f32 volume) { |
| 612 | for (auto& stream : sink_streams) { | 290 | for (auto& stream : sink_streams) { |
| 613 | stream->SetDeviceVolume(volume); | 291 | stream->SetDeviceVolume(volume); |
| 614 | } | 292 | } |
| 615 | } | 293 | } |
| 616 | 294 | ||
| 617 | void CubebSink::SetSystemVolume(const f32 volume) { | 295 | void CubebSink::SetSystemVolume(f32 volume) { |
| 618 | for (auto& stream : sink_streams) { | 296 | for (auto& stream : sink_streams) { |
| 619 | stream->SetSystemVolume(volume); | 297 | stream->SetSystemVolume(volume); |
| 620 | } | 298 | } |
| 621 | } | 299 | } |
| 622 | 300 | ||
| 623 | std::vector<std::string> ListCubebSinkDevices(const bool capture) { | 301 | std::vector<std::string> ListCubebSinkDevices(bool capture) { |
| 624 | std::vector<std::string> device_list; | 302 | std::vector<std::string> device_list; |
| 625 | cubeb* ctx; | 303 | cubeb* ctx; |
| 626 | 304 | ||
diff --git a/src/audio_core/sink/cubeb_sink.h b/src/audio_core/sink/cubeb_sink.h index f0f43dfa1..4b0cb160d 100644 --- a/src/audio_core/sink/cubeb_sink.h +++ b/src/audio_core/sink/cubeb_sink.h | |||
| @@ -34,8 +34,7 @@ public: | |||
| 34 | * May differ from the device's channel count. | 34 | * May differ from the device's channel count. |
| 35 | * @param name - Name of this stream. | 35 | * @param name - Name of this stream. |
| 36 | * @param type - Type of this stream, render/in/out. | 36 | * @param type - Type of this stream, render/in/out. |
| 37 | * @param event - Audio render only, a signal used to prevent the renderer running too | 37 | * |
| 38 | * fast. | ||
| 39 | * @return A pointer to the created SinkStream | 38 | * @return A pointer to the created SinkStream |
| 40 | */ | 39 | */ |
| 41 | SinkStream* AcquireSinkStream(Core::System& system, u32 system_channels, | 40 | SinkStream* AcquireSinkStream(Core::System& system, u32 system_channels, |
| @@ -46,7 +45,7 @@ public: | |||
| 46 | * | 45 | * |
| 47 | * @param stream - The stream to close. | 46 | * @param stream - The stream to close. |
| 48 | */ | 47 | */ |
| 49 | void CloseStream(const SinkStream* stream) override; | 48 | void CloseStream(SinkStream* stream) override; |
| 50 | 49 | ||
| 51 | /** | 50 | /** |
| 52 | * Close all streams. | 51 | * Close all streams. |
| @@ -54,16 +53,6 @@ public: | |||
| 54 | void CloseStreams() override; | 53 | void CloseStreams() override; |
| 55 | 54 | ||
| 56 | /** | 55 | /** |
| 57 | * Pause all streams. | ||
| 58 | */ | ||
| 59 | void PauseStreams() override; | ||
| 60 | |||
| 61 | /** | ||
| 62 | * Unpause all streams. | ||
| 63 | */ | ||
| 64 | void UnpauseStreams() override; | ||
| 65 | |||
| 66 | /** | ||
| 67 | * Get the device volume. Set from calls to the IAudioDevice service. | 56 | * Get the device volume. Set from calls to the IAudioDevice service. |
| 68 | * | 57 | * |
| 69 | * @return Volume of the device. | 58 | * @return Volume of the device. |
| @@ -101,7 +90,7 @@ private: | |||
| 101 | }; | 90 | }; |
| 102 | 91 | ||
| 103 | /** | 92 | /** |
| 104 | * Get a list of conencted devices from Cubeb. | 93 | * Get a list of connected devices from Cubeb. |
| 105 | * | 94 | * |
| 106 | * @param capture - Return input (capture) devices if true, otherwise output devices. | 95 | * @param capture - Return input (capture) devices if true, otherwise output devices. |
| 107 | */ | 96 | */ |
diff --git a/src/audio_core/sink/null_sink.h b/src/audio_core/sink/null_sink.h index 47a342171..1215d3cd2 100644 --- a/src/audio_core/sink/null_sink.h +++ b/src/audio_core/sink/null_sink.h | |||
| @@ -3,10 +3,29 @@ | |||
| 3 | 3 | ||
| 4 | #pragma once | 4 | #pragma once |
| 5 | 5 | ||
| 6 | #include <string> | ||
| 7 | #include <string_view> | ||
| 8 | #include <vector> | ||
| 9 | |||
| 6 | #include "audio_core/sink/sink.h" | 10 | #include "audio_core/sink/sink.h" |
| 7 | #include "audio_core/sink/sink_stream.h" | 11 | #include "audio_core/sink/sink_stream.h" |
| 8 | 12 | ||
| 13 | namespace Core { | ||
| 14 | class System; | ||
| 15 | } // namespace Core | ||
| 16 | |||
| 9 | namespace AudioCore::Sink { | 17 | namespace AudioCore::Sink { |
| 18 | class NullSinkStreamImpl final : public SinkStream { | ||
| 19 | public: | ||
| 20 | explicit NullSinkStreamImpl(Core::System& system_, StreamType type_) | ||
| 21 | : SinkStream{system_, type_} {} | ||
| 22 | ~NullSinkStreamImpl() override {} | ||
| 23 | void AppendBuffer(SinkBuffer&, std::vector<s16>&) override {} | ||
| 24 | std::vector<s16> ReleaseBuffer(u64) override { | ||
| 25 | return {}; | ||
| 26 | } | ||
| 27 | }; | ||
| 28 | |||
| 10 | /** | 29 | /** |
| 11 | * A no-op sink for when no audio out is wanted. | 30 | * A no-op sink for when no audio out is wanted. |
| 12 | */ | 31 | */ |
| @@ -15,17 +34,16 @@ public: | |||
| 15 | explicit NullSink(std::string_view) {} | 34 | explicit NullSink(std::string_view) {} |
| 16 | ~NullSink() override = default; | 35 | ~NullSink() override = default; |
| 17 | 36 | ||
| 18 | SinkStream* AcquireSinkStream([[maybe_unused]] Core::System& system, | 37 | SinkStream* AcquireSinkStream(Core::System& system, u32, const std::string&, |
| 19 | [[maybe_unused]] u32 system_channels, | 38 | StreamType type) override { |
| 20 | [[maybe_unused]] const std::string& name, | 39 | if (null_sink == nullptr) { |
| 21 | [[maybe_unused]] StreamType type) override { | 40 | null_sink = std::make_unique<NullSinkStreamImpl>(system, type); |
| 22 | return &null_sink_stream; | 41 | } |
| 42 | return null_sink.get(); | ||
| 23 | } | 43 | } |
| 24 | 44 | ||
| 25 | void CloseStream([[maybe_unused]] const SinkStream* stream) override {} | 45 | void CloseStream(SinkStream*) override {} |
| 26 | void CloseStreams() override {} | 46 | void CloseStreams() override {} |
| 27 | void PauseStreams() override {} | ||
| 28 | void UnpauseStreams() override {} | ||
| 29 | f32 GetDeviceVolume() const override { | 47 | f32 GetDeviceVolume() const override { |
| 30 | return 1.0f; | 48 | return 1.0f; |
| 31 | } | 49 | } |
| @@ -33,20 +51,7 @@ public: | |||
| 33 | void SetSystemVolume(f32 volume) override {} | 51 | void SetSystemVolume(f32 volume) override {} |
| 34 | 52 | ||
| 35 | private: | 53 | private: |
| 36 | struct NullSinkStreamImpl final : SinkStream { | 54 | SinkStreamPtr null_sink{}; |
| 37 | void Finalize() override {} | ||
| 38 | void Start(bool resume = false) override {} | ||
| 39 | void Stop() override {} | ||
| 40 | void AppendBuffer([[maybe_unused]] ::AudioCore::Sink::SinkBuffer& buffer, | ||
| 41 | [[maybe_unused]] std::vector<s16>& samples) override {} | ||
| 42 | std::vector<s16> ReleaseBuffer([[maybe_unused]] u64 num_samples) override { | ||
| 43 | return {}; | ||
| 44 | } | ||
| 45 | bool IsBufferConsumed([[maybe_unused]] const u64 tag) { | ||
| 46 | return true; | ||
| 47 | } | ||
| 48 | void ClearQueue() override {} | ||
| 49 | } null_sink_stream; | ||
| 50 | }; | 55 | }; |
| 51 | 56 | ||
| 52 | } // namespace AudioCore::Sink | 57 | } // namespace AudioCore::Sink |
diff --git a/src/audio_core/sink/sdl2_sink.cpp b/src/audio_core/sink/sdl2_sink.cpp index d6c9ec90d..1bd001b94 100644 --- a/src/audio_core/sink/sdl2_sink.cpp +++ b/src/audio_core/sink/sdl2_sink.cpp | |||
| @@ -1,20 +1,13 @@ | |||
| 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 <algorithm> | 4 | #include <span> |
| 5 | #include <atomic> | 5 | #include <vector> |
| 6 | 6 | ||
| 7 | #include "audio_core/audio_core.h" | 7 | #include "audio_core/common/common.h" |
| 8 | #include "audio_core/audio_event.h" | ||
| 9 | #include "audio_core/audio_manager.h" | ||
| 10 | #include "audio_core/sink/sdl2_sink.h" | 8 | #include "audio_core/sink/sdl2_sink.h" |
| 11 | #include "audio_core/sink/sink_stream.h" | 9 | #include "audio_core/sink/sink_stream.h" |
| 12 | #include "common/assert.h" | ||
| 13 | #include "common/fixed_point.h" | ||
| 14 | #include "common/logging/log.h" | 10 | #include "common/logging/log.h" |
| 15 | #include "common/reader_writer_queue.h" | ||
| 16 | #include "common/ring_buffer.h" | ||
| 17 | #include "common/settings.h" | ||
| 18 | #include "core/core.h" | 11 | #include "core/core.h" |
| 19 | 12 | ||
| 20 | // Ignore -Wimplicit-fallthrough due to https://github.com/libsdl-org/SDL/issues/4307 | 13 | // Ignore -Wimplicit-fallthrough due to https://github.com/libsdl-org/SDL/issues/4307 |
| @@ -44,10 +37,9 @@ public: | |||
| 44 | * @param system_ - Core system. | 37 | * @param system_ - Core system. |
| 45 | * @param event - Event used only for audio renderer, signalled on buffer consume. | 38 | * @param event - Event used only for audio renderer, signalled on buffer consume. |
| 46 | */ | 39 | */ |
| 47 | SDLSinkStream(u32 device_channels_, const u32 system_channels_, | 40 | SDLSinkStream(u32 device_channels_, u32 system_channels_, const std::string& output_device, |
| 48 | const std::string& output_device, const std::string& input_device, | 41 | const std::string& input_device, StreamType type_, Core::System& system_) |
| 49 | const StreamType type_, Core::System& system_) | 42 | : SinkStream{system_, type_} { |
| 50 | : type{type_}, system{system_} { | ||
| 51 | system_channels = system_channels_; | 43 | system_channels = system_channels_; |
| 52 | device_channels = device_channels_; | 44 | device_channels = device_channels_; |
| 53 | 45 | ||
| @@ -63,8 +55,6 @@ public: | |||
| 63 | spec.callback = &SDLSinkStream::DataCallback; | 55 | spec.callback = &SDLSinkStream::DataCallback; |
| 64 | spec.userdata = this; | 56 | spec.userdata = this; |
| 65 | 57 | ||
| 66 | playing_buffer.consumed = true; | ||
| 67 | |||
| 68 | std::string device_name{output_device}; | 58 | std::string device_name{output_device}; |
| 69 | bool capture{false}; | 59 | bool capture{false}; |
| 70 | if (type == StreamType::In) { | 60 | if (type == StreamType::In) { |
| @@ -84,31 +74,30 @@ public: | |||
| 84 | return; | 74 | return; |
| 85 | } | 75 | } |
| 86 | 76 | ||
| 87 | LOG_DEBUG(Service_Audio, | 77 | LOG_INFO(Service_Audio, |
| 88 | "Opening sdl stream {} with: rate {} channels {} (system channels {}) " | 78 | "Opening SDL stream {} with: rate {} channels {} (system channels {}) " |
| 89 | " samples {}", | 79 | " samples {}", |
| 90 | device, obtained.freq, obtained.channels, system_channels, obtained.samples); | 80 | device, obtained.freq, obtained.channels, system_channels, obtained.samples); |
| 91 | } | 81 | } |
| 92 | 82 | ||
| 93 | /** | 83 | /** |
| 94 | * Destroy the sink stream. | 84 | * Destroy the sink stream. |
| 95 | */ | 85 | */ |
| 96 | ~SDLSinkStream() override { | 86 | ~SDLSinkStream() override { |
| 97 | if (device == 0) { | 87 | LOG_DEBUG(Service_Audio, "Destructing SDL stream {}", name); |
| 98 | return; | 88 | Finalize(); |
| 99 | } | ||
| 100 | |||
| 101 | SDL_CloseAudioDevice(device); | ||
| 102 | } | 89 | } |
| 103 | 90 | ||
| 104 | /** | 91 | /** |
| 105 | * Finalize the sink stream. | 92 | * Finalize the sink stream. |
| 106 | */ | 93 | */ |
| 107 | void Finalize() override { | 94 | void Finalize() override { |
| 95 | Unstall(); | ||
| 108 | if (device == 0) { | 96 | if (device == 0) { |
| 109 | return; | 97 | return; |
| 110 | } | 98 | } |
| 111 | 99 | ||
| 100 | Stop(); | ||
| 112 | SDL_CloseAudioDevice(device); | 101 | SDL_CloseAudioDevice(device); |
| 113 | } | 102 | } |
| 114 | 103 | ||
| @@ -118,217 +107,29 @@ public: | |||
| 118 | * @param resume - Set to true if this is resuming the stream a previously-active stream. | 107 | * @param resume - Set to true if this is resuming the stream a previously-active stream. |
| 119 | * Default false. | 108 | * Default false. |
| 120 | */ | 109 | */ |
| 121 | void Start(const bool resume = false) override { | 110 | void Start(bool resume = false) override { |
| 122 | if (device == 0) { | 111 | if (device == 0 || !paused) { |
| 123 | return; | 112 | return; |
| 124 | } | 113 | } |
| 125 | 114 | ||
| 126 | if (resume && was_playing) { | 115 | paused = false; |
| 127 | SDL_PauseAudioDevice(device, 0); | 116 | SDL_PauseAudioDevice(device, 0); |
| 128 | paused = false; | ||
| 129 | } else if (!resume) { | ||
| 130 | SDL_PauseAudioDevice(device, 0); | ||
| 131 | paused = false; | ||
| 132 | } | ||
| 133 | } | 117 | } |
| 134 | 118 | ||
| 135 | /** | 119 | /** |
| 136 | * Stop the sink stream. | 120 | * Stop the sink stream. |
| 137 | */ | 121 | */ |
| 138 | void Stop() { | 122 | void Stop() override { |
| 139 | if (device == 0) { | 123 | Unstall(); |
| 124 | if (device == 0 || paused) { | ||
| 140 | return; | 125 | return; |
| 141 | } | 126 | } |
| 142 | SDL_PauseAudioDevice(device, 1); | ||
| 143 | paused = true; | 127 | paused = true; |
| 144 | } | 128 | SDL_PauseAudioDevice(device, 1); |
| 145 | |||
| 146 | /** | ||
| 147 | * Append a new buffer and its samples to a waiting queue to play. | ||
| 148 | * | ||
| 149 | * @param buffer - Audio buffer information to be queued. | ||
| 150 | * @param samples - The s16 samples to be queue for playback. | ||
| 151 | */ | ||
| 152 | void AppendBuffer(::AudioCore::Sink::SinkBuffer& buffer, std::vector<s16>& samples) override { | ||
| 153 | if (type == StreamType::In) { | ||
| 154 | queue.enqueue(buffer); | ||
| 155 | queued_buffers++; | ||
| 156 | } else { | ||
| 157 | constexpr s32 min = std::numeric_limits<s16>::min(); | ||
| 158 | constexpr s32 max = std::numeric_limits<s16>::max(); | ||
| 159 | |||
| 160 | auto yuzu_volume{Settings::Volume()}; | ||
| 161 | auto volume{system_volume * device_volume * yuzu_volume}; | ||
| 162 | |||
| 163 | if (system_channels == 6 && device_channels == 2) { | ||
| 164 | // We're given 6 channels, but our device only outputs 2, so downmix. | ||
| 165 | constexpr std::array<f32, 4> down_mix_coeff{1.0f, 0.707f, 0.251f, 0.707f}; | ||
| 166 | |||
| 167 | for (u32 read_index = 0, write_index = 0; read_index < samples.size(); | ||
| 168 | read_index += system_channels, write_index += device_channels) { | ||
| 169 | const auto left_sample{ | ||
| 170 | ((Common::FixedPoint<49, 15>( | ||
| 171 | samples[read_index + static_cast<u32>(Channels::FrontLeft)]) * | ||
| 172 | down_mix_coeff[0] + | ||
| 173 | samples[read_index + static_cast<u32>(Channels::Center)] * | ||
| 174 | down_mix_coeff[1] + | ||
| 175 | samples[read_index + static_cast<u32>(Channels::LFE)] * | ||
| 176 | down_mix_coeff[2] + | ||
| 177 | samples[read_index + static_cast<u32>(Channels::BackLeft)] * | ||
| 178 | down_mix_coeff[3]) * | ||
| 179 | volume) | ||
| 180 | .to_int()}; | ||
| 181 | |||
| 182 | const auto right_sample{ | ||
| 183 | ((Common::FixedPoint<49, 15>( | ||
| 184 | samples[read_index + static_cast<u32>(Channels::FrontRight)]) * | ||
| 185 | down_mix_coeff[0] + | ||
| 186 | samples[read_index + static_cast<u32>(Channels::Center)] * | ||
| 187 | down_mix_coeff[1] + | ||
| 188 | samples[read_index + static_cast<u32>(Channels::LFE)] * | ||
| 189 | down_mix_coeff[2] + | ||
| 190 | samples[read_index + static_cast<u32>(Channels::BackRight)] * | ||
| 191 | down_mix_coeff[3]) * | ||
| 192 | volume) | ||
| 193 | .to_int()}; | ||
| 194 | |||
| 195 | samples[write_index + static_cast<u32>(Channels::FrontLeft)] = | ||
| 196 | static_cast<s16>(std::clamp(left_sample, min, max)); | ||
| 197 | samples[write_index + static_cast<u32>(Channels::FrontRight)] = | ||
| 198 | static_cast<s16>(std::clamp(right_sample, min, max)); | ||
| 199 | } | ||
| 200 | |||
| 201 | samples.resize(samples.size() / system_channels * device_channels); | ||
| 202 | |||
| 203 | } else if (system_channels == 2 && device_channels == 6) { | ||
| 204 | // We need moar samples! Not all games will provide 6 channel audio. | ||
| 205 | // TODO: Implement some upmixing here. Currently just passthrough, with other | ||
| 206 | // channels left as silence. | ||
| 207 | std::vector<s16> new_samples(samples.size() / system_channels * device_channels, 0); | ||
| 208 | |||
| 209 | for (u32 read_index = 0, write_index = 0; read_index < samples.size(); | ||
| 210 | read_index += system_channels, write_index += device_channels) { | ||
| 211 | const auto left_sample{static_cast<s16>(std::clamp( | ||
| 212 | static_cast<s32>( | ||
| 213 | static_cast<f32>( | ||
| 214 | samples[read_index + static_cast<u32>(Channels::FrontLeft)]) * | ||
| 215 | volume), | ||
| 216 | min, max))}; | ||
| 217 | |||
| 218 | new_samples[write_index + static_cast<u32>(Channels::FrontLeft)] = left_sample; | ||
| 219 | |||
| 220 | const auto right_sample{static_cast<s16>(std::clamp( | ||
| 221 | static_cast<s32>( | ||
| 222 | static_cast<f32>( | ||
| 223 | samples[read_index + static_cast<u32>(Channels::FrontRight)]) * | ||
| 224 | volume), | ||
| 225 | min, max))}; | ||
| 226 | |||
| 227 | new_samples[write_index + static_cast<u32>(Channels::FrontRight)] = | ||
| 228 | right_sample; | ||
| 229 | } | ||
| 230 | samples = std::move(new_samples); | ||
| 231 | |||
| 232 | } else if (volume != 1.0f) { | ||
| 233 | for (u32 i = 0; i < samples.size(); i++) { | ||
| 234 | samples[i] = static_cast<s16>(std::clamp( | ||
| 235 | static_cast<s32>(static_cast<f32>(samples[i]) * volume), min, max)); | ||
| 236 | } | ||
| 237 | } | ||
| 238 | |||
| 239 | samples_buffer.Push(samples); | ||
| 240 | queue.enqueue(buffer); | ||
| 241 | queued_buffers++; | ||
| 242 | } | ||
| 243 | } | ||
| 244 | |||
| 245 | /** | ||
| 246 | * Release a buffer. Audio In only, will fill a buffer with recorded samples. | ||
| 247 | * | ||
| 248 | * @param num_samples - Maximum number of samples to receive. | ||
| 249 | * @return Vector of recorded samples. May have fewer than num_samples. | ||
| 250 | */ | ||
| 251 | std::vector<s16> ReleaseBuffer(const u64 num_samples) override { | ||
| 252 | static constexpr s32 min = std::numeric_limits<s16>::min(); | ||
| 253 | static constexpr s32 max = std::numeric_limits<s16>::max(); | ||
| 254 | |||
| 255 | auto samples{samples_buffer.Pop(num_samples)}; | ||
| 256 | |||
| 257 | // TODO: Up-mix to 6 channels if the game expects it. | ||
| 258 | // For audio input this is unlikely to ever be the case though. | ||
| 259 | |||
| 260 | // Incoming mic volume seems to always be very quiet, so multiply by an additional 8 here. | ||
| 261 | // TODO: Play with this and find something that works better. | ||
| 262 | auto volume{system_volume * device_volume * 8}; | ||
| 263 | for (u32 i = 0; i < samples.size(); i++) { | ||
| 264 | samples[i] = static_cast<s16>( | ||
| 265 | std::clamp(static_cast<s32>(static_cast<f32>(samples[i]) * volume), min, max)); | ||
| 266 | } | ||
| 267 | |||
| 268 | if (samples.size() < num_samples) { | ||
| 269 | samples.resize(num_samples, 0); | ||
| 270 | } | ||
| 271 | return samples; | ||
| 272 | } | ||
| 273 | |||
| 274 | /** | ||
| 275 | * Check if a certain buffer has been consumed (fully played). | ||
| 276 | * | ||
| 277 | * @param tag - Unique tag of a buffer to check for. | ||
| 278 | * @return True if the buffer has been played, otherwise false. | ||
| 279 | */ | ||
| 280 | bool IsBufferConsumed(const u64 tag) override { | ||
| 281 | if (released_buffer.tag == 0) { | ||
| 282 | if (!released_buffers.try_dequeue(released_buffer)) { | ||
| 283 | return false; | ||
| 284 | } | ||
| 285 | } | ||
| 286 | |||
| 287 | if (released_buffer.tag == tag) { | ||
| 288 | released_buffer.tag = 0; | ||
| 289 | return true; | ||
| 290 | } | ||
| 291 | return false; | ||
| 292 | } | ||
| 293 | |||
| 294 | /** | ||
| 295 | * Empty out the buffer queue. | ||
| 296 | */ | ||
| 297 | void ClearQueue() override { | ||
| 298 | samples_buffer.Pop(); | ||
| 299 | while (queue.pop()) { | ||
| 300 | } | ||
| 301 | while (released_buffers.pop()) { | ||
| 302 | } | ||
| 303 | released_buffer = {}; | ||
| 304 | playing_buffer = {}; | ||
| 305 | playing_buffer.consumed = true; | ||
| 306 | queued_buffers = 0; | ||
| 307 | } | 129 | } |
| 308 | 130 | ||
| 309 | private: | 131 | private: |
| 310 | /** | 132 | /** |
| 311 | * Signal events back to the audio system that a buffer was played/can be filled. | ||
| 312 | * | ||
| 313 | * @param buffer - Consumed audio buffer to be released. | ||
| 314 | */ | ||
| 315 | void SignalEvent(const ::AudioCore::Sink::SinkBuffer& buffer) { | ||
| 316 | auto& manager{system.AudioCore().GetAudioManager()}; | ||
| 317 | switch (type) { | ||
| 318 | case StreamType::Out: | ||
| 319 | released_buffers.enqueue(buffer); | ||
| 320 | manager.SetEvent(Event::Type::AudioOutManager, true); | ||
| 321 | break; | ||
| 322 | case StreamType::In: | ||
| 323 | released_buffers.enqueue(buffer); | ||
| 324 | manager.SetEvent(Event::Type::AudioInManager, true); | ||
| 325 | break; | ||
| 326 | case StreamType::Render: | ||
| 327 | break; | ||
| 328 | } | ||
| 329 | } | ||
| 330 | |||
| 331 | /** | ||
| 332 | * Main callback from SDL. Either expects samples from us (audio render/audio out), or will | 133 | * Main callback from SDL. Either expects samples from us (audio render/audio out), or will |
| 333 | * provide samples to be copied (audio in). | 134 | * provide samples to be copied (audio in). |
| 334 | * | 135 | * |
| @@ -345,122 +146,20 @@ private: | |||
| 345 | 146 | ||
| 346 | const std::size_t num_channels = impl->GetDeviceChannels(); | 147 | const std::size_t num_channels = impl->GetDeviceChannels(); |
| 347 | const std::size_t frame_size = num_channels; | 148 | const std::size_t frame_size = num_channels; |
| 348 | const std::size_t frame_size_bytes = frame_size * sizeof(s16); | ||
| 349 | const std::size_t num_frames{len / num_channels / sizeof(s16)}; | 149 | const std::size_t num_frames{len / num_channels / sizeof(s16)}; |
| 350 | size_t frames_written{0}; | ||
| 351 | [[maybe_unused]] bool underrun{false}; | ||
| 352 | 150 | ||
| 353 | if (impl->type == StreamType::In) { | 151 | if (impl->type == StreamType::In) { |
| 354 | std::span<s16> input_buffer{reinterpret_cast<s16*>(stream), num_frames * frame_size}; | 152 | std::span<const s16> input_buffer{reinterpret_cast<const s16*>(stream), |
| 355 | 153 | num_frames * frame_size}; | |
| 356 | while (frames_written < num_frames) { | 154 | impl->ProcessAudioIn(input_buffer, num_frames); |
| 357 | auto& playing_buffer{impl->playing_buffer}; | ||
| 358 | |||
| 359 | // If the playing buffer has been consumed or has no frames, we need a new one | ||
| 360 | if (playing_buffer.consumed || playing_buffer.frames == 0) { | ||
| 361 | if (!impl->queue.try_dequeue(impl->playing_buffer)) { | ||
| 362 | // If no buffer was available we've underrun, just push the samples and | ||
| 363 | // continue. | ||
| 364 | underrun = true; | ||
| 365 | impl->samples_buffer.Push(&input_buffer[frames_written * frame_size], | ||
| 366 | (num_frames - frames_written) * frame_size); | ||
| 367 | frames_written = num_frames; | ||
| 368 | continue; | ||
| 369 | } else { | ||
| 370 | impl->queued_buffers--; | ||
| 371 | impl->SignalEvent(impl->playing_buffer); | ||
| 372 | } | ||
| 373 | } | ||
| 374 | |||
| 375 | // Get the minimum frames available between the currently playing buffer, and the | ||
| 376 | // amount we have left to fill | ||
| 377 | size_t frames_available{ | ||
| 378 | std::min(playing_buffer.frames - playing_buffer.frames_played, | ||
| 379 | num_frames - frames_written)}; | ||
| 380 | |||
| 381 | impl->samples_buffer.Push(&input_buffer[frames_written * frame_size], | ||
| 382 | frames_available * frame_size); | ||
| 383 | |||
| 384 | frames_written += frames_available; | ||
| 385 | playing_buffer.frames_played += frames_available; | ||
| 386 | |||
| 387 | // If that's all the frames in the current buffer, add its samples and mark it as | ||
| 388 | // consumed | ||
| 389 | if (playing_buffer.frames_played >= playing_buffer.frames) { | ||
| 390 | impl->AddPlayedSampleCount(playing_buffer.frames_played * num_channels); | ||
| 391 | impl->playing_buffer.consumed = true; | ||
| 392 | } | ||
| 393 | } | ||
| 394 | |||
| 395 | std::memcpy(&impl->last_frame[0], &input_buffer[(frames_written - 1) * frame_size], | ||
| 396 | frame_size_bytes); | ||
| 397 | } else { | 155 | } else { |
| 398 | std::span<s16> output_buffer{reinterpret_cast<s16*>(stream), num_frames * frame_size}; | 156 | std::span<s16> output_buffer{reinterpret_cast<s16*>(stream), num_frames * frame_size}; |
| 399 | 157 | impl->ProcessAudioOutAndRender(output_buffer, num_frames); | |
| 400 | while (frames_written < num_frames) { | ||
| 401 | auto& playing_buffer{impl->playing_buffer}; | ||
| 402 | |||
| 403 | // If the playing buffer has been consumed or has no frames, we need a new one | ||
| 404 | if (playing_buffer.consumed || playing_buffer.frames == 0) { | ||
| 405 | if (!impl->queue.try_dequeue(impl->playing_buffer)) { | ||
| 406 | // If no buffer was available we've underrun, fill the remaining buffer with | ||
| 407 | // the last written frame and continue. | ||
| 408 | underrun = true; | ||
| 409 | for (size_t i = frames_written; i < num_frames; i++) { | ||
| 410 | std::memcpy(&output_buffer[i * frame_size], &impl->last_frame[0], | ||
| 411 | frame_size_bytes); | ||
| 412 | } | ||
| 413 | frames_written = num_frames; | ||
| 414 | continue; | ||
| 415 | } else { | ||
| 416 | impl->queued_buffers--; | ||
| 417 | impl->SignalEvent(impl->playing_buffer); | ||
| 418 | } | ||
| 419 | } | ||
| 420 | |||
| 421 | // Get the minimum frames available between the currently playing buffer, and the | ||
| 422 | // amount we have left to fill | ||
| 423 | size_t frames_available{ | ||
| 424 | std::min(playing_buffer.frames - playing_buffer.frames_played, | ||
| 425 | num_frames - frames_written)}; | ||
| 426 | |||
| 427 | impl->samples_buffer.Pop(&output_buffer[frames_written * frame_size], | ||
| 428 | frames_available * frame_size); | ||
| 429 | |||
| 430 | frames_written += frames_available; | ||
| 431 | playing_buffer.frames_played += frames_available; | ||
| 432 | |||
| 433 | // If that's all the frames in the current buffer, add its samples and mark it as | ||
| 434 | // consumed | ||
| 435 | if (playing_buffer.frames_played >= playing_buffer.frames) { | ||
| 436 | impl->AddPlayedSampleCount(playing_buffer.frames_played * num_channels); | ||
| 437 | impl->playing_buffer.consumed = true; | ||
| 438 | } | ||
| 439 | } | ||
| 440 | |||
| 441 | std::memcpy(&impl->last_frame[0], &output_buffer[(frames_written - 1) * frame_size], | ||
| 442 | frame_size_bytes); | ||
| 443 | } | 158 | } |
| 444 | } | 159 | } |
| 445 | 160 | ||
| 446 | /// SDL device id of the opened input/output device | 161 | /// SDL device id of the opened input/output device |
| 447 | SDL_AudioDeviceID device{}; | 162 | SDL_AudioDeviceID device{}; |
| 448 | /// Type of this stream | ||
| 449 | StreamType type; | ||
| 450 | /// Core system | ||
| 451 | Core::System& system; | ||
| 452 | /// Ring buffer of the samples waiting to be played or consumed | ||
| 453 | Common::RingBuffer<s16, 0x10000> samples_buffer; | ||
| 454 | /// Audio buffers queued and waiting to play | ||
| 455 | Common::ReaderWriterQueue<::AudioCore::Sink::SinkBuffer> queue; | ||
| 456 | /// The currently-playing audio buffer | ||
| 457 | ::AudioCore::Sink::SinkBuffer playing_buffer{}; | ||
| 458 | /// Audio buffers which have been played and are in queue to be released by the audio system | ||
| 459 | Common::ReaderWriterQueue<::AudioCore::Sink::SinkBuffer> released_buffers{}; | ||
| 460 | /// Currently released buffer waiting to be taken by the audio system | ||
| 461 | ::AudioCore::Sink::SinkBuffer released_buffer{}; | ||
| 462 | /// The last played (or received) frame of audio, used when the callback underruns | ||
| 463 | std::array<s16, MaxChannels> last_frame{}; | ||
| 464 | }; | 163 | }; |
| 465 | 164 | ||
| 466 | SDLSink::SDLSink(std::string_view target_device_name) { | 165 | SDLSink::SDLSink(std::string_view target_device_name) { |
| @@ -482,14 +181,14 @@ SDLSink::SDLSink(std::string_view target_device_name) { | |||
| 482 | 181 | ||
| 483 | SDLSink::~SDLSink() = default; | 182 | SDLSink::~SDLSink() = default; |
| 484 | 183 | ||
| 485 | SinkStream* SDLSink::AcquireSinkStream(Core::System& system, const u32 system_channels, | 184 | SinkStream* SDLSink::AcquireSinkStream(Core::System& system, u32 system_channels, |
| 486 | const std::string&, const StreamType type) { | 185 | const std::string&, StreamType type) { |
| 487 | SinkStreamPtr& stream = sink_streams.emplace_back(std::make_unique<SDLSinkStream>( | 186 | SinkStreamPtr& stream = sink_streams.emplace_back(std::make_unique<SDLSinkStream>( |
| 488 | device_channels, system_channels, output_device, input_device, type, system)); | 187 | device_channels, system_channels, output_device, input_device, type, system)); |
| 489 | return stream.get(); | 188 | return stream.get(); |
| 490 | } | 189 | } |
| 491 | 190 | ||
| 492 | void SDLSink::CloseStream(const SinkStream* stream) { | 191 | void SDLSink::CloseStream(SinkStream* stream) { |
| 493 | for (size_t i = 0; i < sink_streams.size(); i++) { | 192 | for (size_t i = 0; i < sink_streams.size(); i++) { |
| 494 | if (sink_streams[i].get() == stream) { | 193 | if (sink_streams[i].get() == stream) { |
| 495 | sink_streams[i].reset(); | 194 | sink_streams[i].reset(); |
| @@ -503,18 +202,6 @@ void SDLSink::CloseStreams() { | |||
| 503 | sink_streams.clear(); | 202 | sink_streams.clear(); |
| 504 | } | 203 | } |
| 505 | 204 | ||
| 506 | void SDLSink::PauseStreams() { | ||
| 507 | for (auto& stream : sink_streams) { | ||
| 508 | stream->Stop(); | ||
| 509 | } | ||
| 510 | } | ||
| 511 | |||
| 512 | void SDLSink::UnpauseStreams() { | ||
| 513 | for (auto& stream : sink_streams) { | ||
| 514 | stream->Start(); | ||
| 515 | } | ||
| 516 | } | ||
| 517 | |||
| 518 | f32 SDLSink::GetDeviceVolume() const { | 205 | f32 SDLSink::GetDeviceVolume() const { |
| 519 | if (sink_streams.empty()) { | 206 | if (sink_streams.empty()) { |
| 520 | return 1.0f; | 207 | return 1.0f; |
| @@ -523,19 +210,19 @@ f32 SDLSink::GetDeviceVolume() const { | |||
| 523 | return sink_streams[0]->GetDeviceVolume(); | 210 | return sink_streams[0]->GetDeviceVolume(); |
| 524 | } | 211 | } |
| 525 | 212 | ||
| 526 | void SDLSink::SetDeviceVolume(const f32 volume) { | 213 | void SDLSink::SetDeviceVolume(f32 volume) { |
| 527 | for (auto& stream : sink_streams) { | 214 | for (auto& stream : sink_streams) { |
| 528 | stream->SetDeviceVolume(volume); | 215 | stream->SetDeviceVolume(volume); |
| 529 | } | 216 | } |
| 530 | } | 217 | } |
| 531 | 218 | ||
| 532 | void SDLSink::SetSystemVolume(const f32 volume) { | 219 | void SDLSink::SetSystemVolume(f32 volume) { |
| 533 | for (auto& stream : sink_streams) { | 220 | for (auto& stream : sink_streams) { |
| 534 | stream->SetSystemVolume(volume); | 221 | stream->SetSystemVolume(volume); |
| 535 | } | 222 | } |
| 536 | } | 223 | } |
| 537 | 224 | ||
| 538 | std::vector<std::string> ListSDLSinkDevices(const bool capture) { | 225 | std::vector<std::string> ListSDLSinkDevices(bool capture) { |
| 539 | std::vector<std::string> device_list; | 226 | std::vector<std::string> device_list; |
| 540 | 227 | ||
| 541 | if (!SDL_WasInit(SDL_INIT_AUDIO)) { | 228 | if (!SDL_WasInit(SDL_INIT_AUDIO)) { |
diff --git a/src/audio_core/sink/sdl2_sink.h b/src/audio_core/sink/sdl2_sink.h index 186bc2fa3..f01eddc1b 100644 --- a/src/audio_core/sink/sdl2_sink.h +++ b/src/audio_core/sink/sdl2_sink.h | |||
| @@ -32,8 +32,7 @@ public: | |||
| 32 | * May differ from the device's channel count. | 32 | * May differ from the device's channel count. |
| 33 | * @param name - Name of this stream. | 33 | * @param name - Name of this stream. |
| 34 | * @param type - Type of this stream, render/in/out. | 34 | * @param type - Type of this stream, render/in/out. |
| 35 | * @param event - Audio render only, a signal used to prevent the renderer running too | 35 | * |
| 36 | * fast. | ||
| 37 | * @return A pointer to the created SinkStream | 36 | * @return A pointer to the created SinkStream |
| 38 | */ | 37 | */ |
| 39 | SinkStream* AcquireSinkStream(Core::System& system, u32 system_channels, | 38 | SinkStream* AcquireSinkStream(Core::System& system, u32 system_channels, |
| @@ -44,7 +43,7 @@ public: | |||
| 44 | * | 43 | * |
| 45 | * @param stream - The stream to close. | 44 | * @param stream - The stream to close. |
| 46 | */ | 45 | */ |
| 47 | void CloseStream(const SinkStream* stream) override; | 46 | void CloseStream(SinkStream* stream) override; |
| 48 | 47 | ||
| 49 | /** | 48 | /** |
| 50 | * Close all streams. | 49 | * Close all streams. |
| @@ -52,16 +51,6 @@ public: | |||
| 52 | void CloseStreams() override; | 51 | void CloseStreams() override; |
| 53 | 52 | ||
| 54 | /** | 53 | /** |
| 55 | * Pause all streams. | ||
| 56 | */ | ||
| 57 | void PauseStreams() override; | ||
| 58 | |||
| 59 | /** | ||
| 60 | * Unpause all streams. | ||
| 61 | */ | ||
| 62 | void UnpauseStreams() override; | ||
| 63 | |||
| 64 | /** | ||
| 65 | * Get the device volume. Set from calls to the IAudioDevice service. | 54 | * Get the device volume. Set from calls to the IAudioDevice service. |
| 66 | * | 55 | * |
| 67 | * @return Volume of the device. | 56 | * @return Volume of the device. |
| @@ -92,7 +81,7 @@ private: | |||
| 92 | }; | 81 | }; |
| 93 | 82 | ||
| 94 | /** | 83 | /** |
| 95 | * Get a list of conencted devices from Cubeb. | 84 | * Get a list of connected devices from SDL. |
| 96 | * | 85 | * |
| 97 | * @param capture - Return input (capture) devices if true, otherwise output devices. | 86 | * @param capture - Return input (capture) devices if true, otherwise output devices. |
| 98 | */ | 87 | */ |
diff --git a/src/audio_core/sink/sink.h b/src/audio_core/sink/sink.h index 91fe455e4..f28c6d126 100644 --- a/src/audio_core/sink/sink.h +++ b/src/audio_core/sink/sink.h | |||
| @@ -32,7 +32,7 @@ public: | |||
| 32 | * | 32 | * |
| 33 | * @param stream - The stream to close. | 33 | * @param stream - The stream to close. |
| 34 | */ | 34 | */ |
| 35 | virtual void CloseStream(const SinkStream* stream) = 0; | 35 | virtual void CloseStream(SinkStream* stream) = 0; |
| 36 | 36 | ||
| 37 | /** | 37 | /** |
| 38 | * Close all streams. | 38 | * Close all streams. |
| @@ -40,16 +40,6 @@ public: | |||
| 40 | virtual void CloseStreams() = 0; | 40 | virtual void CloseStreams() = 0; |
| 41 | 41 | ||
| 42 | /** | 42 | /** |
| 43 | * Pause all streams. | ||
| 44 | */ | ||
| 45 | virtual void PauseStreams() = 0; | ||
| 46 | |||
| 47 | /** | ||
| 48 | * Unpause all streams. | ||
| 49 | */ | ||
| 50 | virtual void UnpauseStreams() = 0; | ||
| 51 | |||
| 52 | /** | ||
| 53 | * Create a new sink stream, kept within this sink, with a pointer returned for use. | 43 | * Create a new sink stream, kept within this sink, with a pointer returned for use. |
| 54 | * Do not free the returned pointer. When done with the stream, call CloseStream on the sink. | 44 | * Do not free the returned pointer. When done with the stream, call CloseStream on the sink. |
| 55 | * | 45 | * |
| @@ -58,8 +48,7 @@ public: | |||
| 58 | * May differ from the device's channel count. | 48 | * May differ from the device's channel count. |
| 59 | * @param name - Name of this stream. | 49 | * @param name - Name of this stream. |
| 60 | * @param type - Type of this stream, render/in/out. | 50 | * @param type - Type of this stream, render/in/out. |
| 61 | * @param event - Audio render only, a signal used to prevent the renderer running too | 51 | * |
| 62 | * fast. | ||
| 63 | * @return A pointer to the created SinkStream | 52 | * @return A pointer to the created SinkStream |
| 64 | */ | 53 | */ |
| 65 | virtual SinkStream* AcquireSinkStream(Core::System& system, u32 system_channels, | 54 | virtual SinkStream* AcquireSinkStream(Core::System& system, u32 system_channels, |
diff --git a/src/audio_core/sink/sink_details.cpp b/src/audio_core/sink/sink_details.cpp index 253c0fd1e..67bdab779 100644 --- a/src/audio_core/sink/sink_details.cpp +++ b/src/audio_core/sink/sink_details.cpp | |||
| @@ -5,7 +5,7 @@ | |||
| 5 | #include <memory> | 5 | #include <memory> |
| 6 | #include <string> | 6 | #include <string> |
| 7 | #include <vector> | 7 | #include <vector> |
| 8 | #include "audio_core/sink/null_sink.h" | 8 | |
| 9 | #include "audio_core/sink/sink_details.h" | 9 | #include "audio_core/sink/sink_details.h" |
| 10 | #ifdef HAVE_CUBEB | 10 | #ifdef HAVE_CUBEB |
| 11 | #include "audio_core/sink/cubeb_sink.h" | 11 | #include "audio_core/sink/cubeb_sink.h" |
| @@ -13,6 +13,7 @@ | |||
| 13 | #ifdef HAVE_SDL2 | 13 | #ifdef HAVE_SDL2 |
| 14 | #include "audio_core/sink/sdl2_sink.h" | 14 | #include "audio_core/sink/sdl2_sink.h" |
| 15 | #endif | 15 | #endif |
| 16 | #include "audio_core/sink/null_sink.h" | ||
| 16 | #include "common/logging/log.h" | 17 | #include "common/logging/log.h" |
| 17 | 18 | ||
| 18 | namespace AudioCore::Sink { | 19 | namespace AudioCore::Sink { |
| @@ -59,8 +60,7 @@ const SinkDetails& GetOutputSinkDetails(std::string_view sink_id) { | |||
| 59 | 60 | ||
| 60 | if (sink_id == "auto" || iter == std::end(sink_details)) { | 61 | if (sink_id == "auto" || iter == std::end(sink_details)) { |
| 61 | if (sink_id != "auto") { | 62 | if (sink_id != "auto") { |
| 62 | LOG_ERROR(Audio, "AudioCore::Sink::GetOutputSinkDetails given invalid sink_id {}", | 63 | LOG_ERROR(Audio, "Invalid sink_id {}", sink_id); |
| 63 | sink_id); | ||
| 64 | } | 64 | } |
| 65 | // Auto-select. | 65 | // Auto-select. |
| 66 | // sink_details is ordered in terms of desirability, with the best choice at the front. | 66 | // sink_details is ordered in terms of desirability, with the best choice at the front. |
diff --git a/src/audio_core/sink/sink_stream.cpp b/src/audio_core/sink/sink_stream.cpp new file mode 100644 index 000000000..37fe725e4 --- /dev/null +++ b/src/audio_core/sink/sink_stream.cpp | |||
| @@ -0,0 +1,279 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #include <array> | ||
| 5 | #include <atomic> | ||
| 6 | #include <memory> | ||
| 7 | #include <span> | ||
| 8 | #include <vector> | ||
| 9 | |||
| 10 | #include "audio_core/audio_core.h" | ||
| 11 | #include "audio_core/common/common.h" | ||
| 12 | #include "audio_core/sink/sink_stream.h" | ||
| 13 | #include "common/common_types.h" | ||
| 14 | #include "common/fixed_point.h" | ||
| 15 | #include "common/settings.h" | ||
| 16 | #include "core/core.h" | ||
| 17 | |||
| 18 | namespace AudioCore::Sink { | ||
| 19 | |||
| 20 | void SinkStream::AppendBuffer(SinkBuffer& buffer, std::vector<s16>& samples) { | ||
| 21 | if (type == StreamType::In) { | ||
| 22 | queue.enqueue(buffer); | ||
| 23 | queued_buffers++; | ||
| 24 | return; | ||
| 25 | } | ||
| 26 | |||
| 27 | constexpr s32 min{std::numeric_limits<s16>::min()}; | ||
| 28 | constexpr s32 max{std::numeric_limits<s16>::max()}; | ||
| 29 | |||
| 30 | auto yuzu_volume{Settings::Volume()}; | ||
| 31 | if (yuzu_volume > 1.0f) { | ||
| 32 | yuzu_volume = 0.6f + 20 * std::log10(yuzu_volume); | ||
| 33 | } | ||
| 34 | auto volume{system_volume * device_volume * yuzu_volume}; | ||
| 35 | |||
| 36 | if (system_channels == 6 && device_channels == 2) { | ||
| 37 | // We're given 6 channels, but our device only outputs 2, so downmix. | ||
| 38 | constexpr std::array<f32, 4> down_mix_coeff{1.0f, 0.707f, 0.251f, 0.707f}; | ||
| 39 | |||
| 40 | for (u32 read_index = 0, write_index = 0; read_index < samples.size(); | ||
| 41 | read_index += system_channels, write_index += device_channels) { | ||
| 42 | const auto left_sample{ | ||
| 43 | ((Common::FixedPoint<49, 15>( | ||
| 44 | samples[read_index + static_cast<u32>(Channels::FrontLeft)]) * | ||
| 45 | down_mix_coeff[0] + | ||
| 46 | samples[read_index + static_cast<u32>(Channels::Center)] * down_mix_coeff[1] + | ||
| 47 | samples[read_index + static_cast<u32>(Channels::LFE)] * down_mix_coeff[2] + | ||
| 48 | samples[read_index + static_cast<u32>(Channels::BackLeft)] * down_mix_coeff[3]) * | ||
| 49 | volume) | ||
| 50 | .to_int()}; | ||
| 51 | |||
| 52 | const auto right_sample{ | ||
| 53 | ((Common::FixedPoint<49, 15>( | ||
| 54 | samples[read_index + static_cast<u32>(Channels::FrontRight)]) * | ||
| 55 | down_mix_coeff[0] + | ||
| 56 | samples[read_index + static_cast<u32>(Channels::Center)] * down_mix_coeff[1] + | ||
| 57 | samples[read_index + static_cast<u32>(Channels::LFE)] * down_mix_coeff[2] + | ||
| 58 | samples[read_index + static_cast<u32>(Channels::BackRight)] * down_mix_coeff[3]) * | ||
| 59 | volume) | ||
| 60 | .to_int()}; | ||
| 61 | |||
| 62 | samples[write_index + static_cast<u32>(Channels::FrontLeft)] = | ||
| 63 | static_cast<s16>(std::clamp(left_sample, min, max)); | ||
| 64 | samples[write_index + static_cast<u32>(Channels::FrontRight)] = | ||
| 65 | static_cast<s16>(std::clamp(right_sample, min, max)); | ||
| 66 | } | ||
| 67 | |||
| 68 | samples.resize(samples.size() / system_channels * device_channels); | ||
| 69 | |||
| 70 | } else if (system_channels == 2 && device_channels == 6) { | ||
| 71 | // We need moar samples! Not all games will provide 6 channel audio. | ||
| 72 | // TODO: Implement some upmixing here. Currently just passthrough, with other | ||
| 73 | // channels left as silence. | ||
| 74 | std::vector<s16> new_samples(samples.size() / system_channels * device_channels, 0); | ||
| 75 | |||
| 76 | for (u32 read_index = 0, write_index = 0; read_index < samples.size(); | ||
| 77 | read_index += system_channels, write_index += device_channels) { | ||
| 78 | const auto left_sample{static_cast<s16>(std::clamp( | ||
| 79 | static_cast<s32>( | ||
| 80 | static_cast<f32>(samples[read_index + static_cast<u32>(Channels::FrontLeft)]) * | ||
| 81 | volume), | ||
| 82 | min, max))}; | ||
| 83 | |||
| 84 | new_samples[write_index + static_cast<u32>(Channels::FrontLeft)] = left_sample; | ||
| 85 | |||
| 86 | const auto right_sample{static_cast<s16>(std::clamp( | ||
| 87 | static_cast<s32>( | ||
| 88 | static_cast<f32>(samples[read_index + static_cast<u32>(Channels::FrontRight)]) * | ||
| 89 | volume), | ||
| 90 | min, max))}; | ||
| 91 | |||
| 92 | new_samples[write_index + static_cast<u32>(Channels::FrontRight)] = right_sample; | ||
| 93 | } | ||
| 94 | samples = std::move(new_samples); | ||
| 95 | |||
| 96 | } else if (volume != 1.0f) { | ||
| 97 | for (u32 i = 0; i < samples.size(); i++) { | ||
| 98 | samples[i] = static_cast<s16>( | ||
| 99 | std::clamp(static_cast<s32>(static_cast<f32>(samples[i]) * volume), min, max)); | ||
| 100 | } | ||
| 101 | } | ||
| 102 | |||
| 103 | samples_buffer.Push(samples); | ||
| 104 | queue.enqueue(buffer); | ||
| 105 | queued_buffers++; | ||
| 106 | } | ||
| 107 | |||
| 108 | std::vector<s16> SinkStream::ReleaseBuffer(u64 num_samples) { | ||
| 109 | constexpr s32 min = std::numeric_limits<s16>::min(); | ||
| 110 | constexpr s32 max = std::numeric_limits<s16>::max(); | ||
| 111 | |||
| 112 | auto samples{samples_buffer.Pop(num_samples)}; | ||
| 113 | |||
| 114 | // TODO: Up-mix to 6 channels if the game expects it. | ||
| 115 | // For audio input this is unlikely to ever be the case though. | ||
| 116 | |||
| 117 | // Incoming mic volume seems to always be very quiet, so multiply by an additional 8 here. | ||
| 118 | // TODO: Play with this and find something that works better. | ||
| 119 | auto volume{system_volume * device_volume * 8}; | ||
| 120 | for (u32 i = 0; i < samples.size(); i++) { | ||
| 121 | samples[i] = static_cast<s16>( | ||
| 122 | std::clamp(static_cast<s32>(static_cast<f32>(samples[i]) * volume), min, max)); | ||
| 123 | } | ||
| 124 | |||
| 125 | if (samples.size() < num_samples) { | ||
| 126 | samples.resize(num_samples, 0); | ||
| 127 | } | ||
| 128 | return samples; | ||
| 129 | } | ||
| 130 | |||
| 131 | void SinkStream::ClearQueue() { | ||
| 132 | samples_buffer.Pop(); | ||
| 133 | while (queue.pop()) { | ||
| 134 | } | ||
| 135 | queued_buffers = 0; | ||
| 136 | playing_buffer = {}; | ||
| 137 | playing_buffer.consumed = true; | ||
| 138 | } | ||
| 139 | |||
| 140 | void SinkStream::ProcessAudioIn(std::span<const s16> input_buffer, std::size_t num_frames) { | ||
| 141 | const std::size_t num_channels = GetDeviceChannels(); | ||
| 142 | const std::size_t frame_size = num_channels; | ||
| 143 | const std::size_t frame_size_bytes = frame_size * sizeof(s16); | ||
| 144 | size_t frames_written{0}; | ||
| 145 | |||
| 146 | // If we're paused or going to shut down, we don't want to consume buffers as coretiming is | ||
| 147 | // paused and we'll desync, so just return. | ||
| 148 | if (system.IsPaused() || system.IsShuttingDown()) { | ||
| 149 | return; | ||
| 150 | } | ||
| 151 | |||
| 152 | if (queued_buffers > max_queue_size) { | ||
| 153 | Stall(); | ||
| 154 | } | ||
| 155 | |||
| 156 | while (frames_written < num_frames) { | ||
| 157 | // If the playing buffer has been consumed or has no frames, we need a new one | ||
| 158 | if (playing_buffer.consumed || playing_buffer.frames == 0) { | ||
| 159 | if (!queue.try_dequeue(playing_buffer)) { | ||
| 160 | // If no buffer was available we've underrun, just push the samples and | ||
| 161 | // continue. | ||
| 162 | samples_buffer.Push(&input_buffer[frames_written * frame_size], | ||
| 163 | (num_frames - frames_written) * frame_size); | ||
| 164 | frames_written = num_frames; | ||
| 165 | continue; | ||
| 166 | } | ||
| 167 | // Successfully dequeued a new buffer. | ||
| 168 | queued_buffers--; | ||
| 169 | } | ||
| 170 | |||
| 171 | // Get the minimum frames available between the currently playing buffer, and the | ||
| 172 | // amount we have left to fill | ||
| 173 | size_t frames_available{std::min(playing_buffer.frames - playing_buffer.frames_played, | ||
| 174 | num_frames - frames_written)}; | ||
| 175 | |||
| 176 | samples_buffer.Push(&input_buffer[frames_written * frame_size], | ||
| 177 | frames_available * frame_size); | ||
| 178 | |||
| 179 | frames_written += frames_available; | ||
| 180 | playing_buffer.frames_played += frames_available; | ||
| 181 | |||
| 182 | // If that's all the frames in the current buffer, add its samples and mark it as | ||
| 183 | // consumed | ||
| 184 | if (playing_buffer.frames_played >= playing_buffer.frames) { | ||
| 185 | playing_buffer.consumed = true; | ||
| 186 | } | ||
| 187 | } | ||
| 188 | |||
| 189 | std::memcpy(&last_frame[0], &input_buffer[(frames_written - 1) * frame_size], frame_size_bytes); | ||
| 190 | |||
| 191 | if (queued_buffers <= max_queue_size) { | ||
| 192 | Unstall(); | ||
| 193 | } | ||
| 194 | } | ||
| 195 | |||
| 196 | void SinkStream::ProcessAudioOutAndRender(std::span<s16> output_buffer, std::size_t num_frames) { | ||
| 197 | const std::size_t num_channels = GetDeviceChannels(); | ||
| 198 | const std::size_t frame_size = num_channels; | ||
| 199 | const std::size_t frame_size_bytes = frame_size * sizeof(s16); | ||
| 200 | size_t frames_written{0}; | ||
| 201 | |||
| 202 | // If we're paused or going to shut down, we don't want to consume buffers as coretiming is | ||
| 203 | // paused and we'll desync, so just play silence. | ||
| 204 | if (system.IsPaused() || system.IsShuttingDown()) { | ||
| 205 | constexpr std::array<s16, 6> silence{}; | ||
| 206 | for (size_t i = frames_written; i < num_frames; i++) { | ||
| 207 | std::memcpy(&output_buffer[i * frame_size], &silence[0], frame_size_bytes); | ||
| 208 | } | ||
| 209 | return; | ||
| 210 | } | ||
| 211 | |||
| 212 | // Due to many frames being queued up with nvdec (5 frames or so?), a lot of buffers also get | ||
| 213 | // queued up (30+) but not all at once, which causes constant stalling here, so just let the | ||
| 214 | // video play out without attempting to stall. | ||
| 215 | // Can hopefully remove this later with a more complete NVDEC implementation. | ||
| 216 | const auto nvdec_active{system.AudioCore().IsNVDECActive()}; | ||
| 217 | if (!nvdec_active && queued_buffers > max_queue_size) { | ||
| 218 | Stall(); | ||
| 219 | } | ||
| 220 | |||
| 221 | while (frames_written < num_frames) { | ||
| 222 | // If the playing buffer has been consumed or has no frames, we need a new one | ||
| 223 | if (playing_buffer.consumed || playing_buffer.frames == 0) { | ||
| 224 | if (!queue.try_dequeue(playing_buffer)) { | ||
| 225 | // If no buffer was available we've underrun, fill the remaining buffer with | ||
| 226 | // the last written frame and continue. | ||
| 227 | for (size_t i = frames_written; i < num_frames; i++) { | ||
| 228 | std::memcpy(&output_buffer[i * frame_size], &last_frame[0], frame_size_bytes); | ||
| 229 | } | ||
| 230 | frames_written = num_frames; | ||
| 231 | continue; | ||
| 232 | } | ||
| 233 | // Successfully dequeued a new buffer. | ||
| 234 | queued_buffers--; | ||
| 235 | } | ||
| 236 | |||
| 237 | // Get the minimum frames available between the currently playing buffer, and the | ||
| 238 | // amount we have left to fill | ||
| 239 | size_t frames_available{std::min(playing_buffer.frames - playing_buffer.frames_played, | ||
| 240 | num_frames - frames_written)}; | ||
| 241 | |||
| 242 | samples_buffer.Pop(&output_buffer[frames_written * frame_size], | ||
| 243 | frames_available * frame_size); | ||
| 244 | |||
| 245 | frames_written += frames_available; | ||
| 246 | playing_buffer.frames_played += frames_available; | ||
| 247 | |||
| 248 | // If that's all the frames in the current buffer, add its samples and mark it as | ||
| 249 | // consumed | ||
| 250 | if (playing_buffer.frames_played >= playing_buffer.frames) { | ||
| 251 | playing_buffer.consumed = true; | ||
| 252 | } | ||
| 253 | } | ||
| 254 | |||
| 255 | std::memcpy(&last_frame[0], &output_buffer[(frames_written - 1) * frame_size], | ||
| 256 | frame_size_bytes); | ||
| 257 | |||
| 258 | if (stalled && queued_buffers <= max_queue_size) { | ||
| 259 | Unstall(); | ||
| 260 | } | ||
| 261 | } | ||
| 262 | |||
| 263 | void SinkStream::Stall() { | ||
| 264 | if (stalled) { | ||
| 265 | return; | ||
| 266 | } | ||
| 267 | stalled = true; | ||
| 268 | system.StallProcesses(); | ||
| 269 | } | ||
| 270 | |||
| 271 | void SinkStream::Unstall() { | ||
| 272 | if (!stalled) { | ||
| 273 | return; | ||
| 274 | } | ||
| 275 | system.UnstallProcesses(); | ||
| 276 | stalled = false; | ||
| 277 | } | ||
| 278 | |||
| 279 | } // namespace AudioCore::Sink | ||
diff --git a/src/audio_core/sink/sink_stream.h b/src/audio_core/sink/sink_stream.h index 17ed6593f..9366ebbd3 100644 --- a/src/audio_core/sink/sink_stream.h +++ b/src/audio_core/sink/sink_stream.h | |||
| @@ -3,12 +3,20 @@ | |||
| 3 | 3 | ||
| 4 | #pragma once | 4 | #pragma once |
| 5 | 5 | ||
| 6 | #include <array> | ||
| 6 | #include <atomic> | 7 | #include <atomic> |
| 7 | #include <memory> | 8 | #include <memory> |
| 9 | #include <span> | ||
| 8 | #include <vector> | 10 | #include <vector> |
| 9 | 11 | ||
| 10 | #include "audio_core/common/common.h" | 12 | #include "audio_core/common/common.h" |
| 11 | #include "common/common_types.h" | 13 | #include "common/common_types.h" |
| 14 | #include "common/reader_writer_queue.h" | ||
| 15 | #include "common/ring_buffer.h" | ||
| 16 | |||
| 17 | namespace Core { | ||
| 18 | class System; | ||
| 19 | } // namespace Core | ||
| 12 | 20 | ||
| 13 | namespace AudioCore::Sink { | 21 | namespace AudioCore::Sink { |
| 14 | 22 | ||
| @@ -34,20 +42,24 @@ struct SinkBuffer { | |||
| 34 | * You should regularly call IsBufferConsumed with the unique SinkBuffer tag to check if the buffer | 42 | * You should regularly call IsBufferConsumed with the unique SinkBuffer tag to check if the buffer |
| 35 | * has been consumed. | 43 | * has been consumed. |
| 36 | * | 44 | * |
| 37 | * Since these are a FIFO queue, always check IsBufferConsumed in the same order you appended the | 45 | * Since these are a FIFO queue, IsBufferConsumed must be checked in the same order buffers were |
| 38 | * buffers, skipping a buffer will result in all following buffers to never release. | 46 | * appended, skipping a buffer will result in the queue getting stuck, and all following buffers to |
| 47 | * never release. | ||
| 39 | * | 48 | * |
| 40 | * If the buffers appear to be stuck, you can stop and re-open an IAudioIn/IAudioOut service (this | 49 | * If the buffers appear to be stuck, you can stop and re-open an IAudioIn/IAudioOut service (this |
| 41 | * is what games do), or call ClearQueue to flush all of the buffers without a full restart. | 50 | * is what games do), or call ClearQueue to flush all of the buffers without a full restart. |
| 42 | */ | 51 | */ |
| 43 | class SinkStream { | 52 | class SinkStream { |
| 44 | public: | 53 | public: |
| 45 | virtual ~SinkStream() = default; | 54 | explicit SinkStream(Core::System& system_, StreamType type_) : system{system_}, type{type_} {} |
| 55 | virtual ~SinkStream() { | ||
| 56 | Unstall(); | ||
| 57 | } | ||
| 46 | 58 | ||
| 47 | /** | 59 | /** |
| 48 | * Finalize the sink stream. | 60 | * Finalize the sink stream. |
| 49 | */ | 61 | */ |
| 50 | virtual void Finalize() = 0; | 62 | virtual void Finalize() {} |
| 51 | 63 | ||
| 52 | /** | 64 | /** |
| 53 | * Start the sink stream. | 65 | * Start the sink stream. |
| @@ -55,48 +67,19 @@ public: | |||
| 55 | * @param resume - Set to true if this is resuming the stream a previously-active stream. | 67 | * @param resume - Set to true if this is resuming the stream a previously-active stream. |
| 56 | * Default false. | 68 | * Default false. |
| 57 | */ | 69 | */ |
| 58 | virtual void Start(bool resume = false) = 0; | 70 | virtual void Start(bool resume = false) {} |
| 59 | 71 | ||
| 60 | /** | 72 | /** |
| 61 | * Stop the sink stream. | 73 | * Stop the sink stream. |
| 62 | */ | 74 | */ |
| 63 | virtual void Stop() = 0; | 75 | virtual void Stop() {} |
| 64 | |||
| 65 | /** | ||
| 66 | * Append a new buffer and its samples to a waiting queue to play. | ||
| 67 | * | ||
| 68 | * @param buffer - Audio buffer information to be queued. | ||
| 69 | * @param samples - The s16 samples to be queue for playback. | ||
| 70 | */ | ||
| 71 | virtual void AppendBuffer(SinkBuffer& buffer, std::vector<s16>& samples) = 0; | ||
| 72 | |||
| 73 | /** | ||
| 74 | * Release a buffer. Audio In only, will fill a buffer with recorded samples. | ||
| 75 | * | ||
| 76 | * @param num_samples - Maximum number of samples to receive. | ||
| 77 | * @return Vector of recorded samples. May have fewer than num_samples. | ||
| 78 | */ | ||
| 79 | virtual std::vector<s16> ReleaseBuffer(u64 num_samples) = 0; | ||
| 80 | |||
| 81 | /** | ||
| 82 | * Check if a certain buffer has been consumed (fully played). | ||
| 83 | * | ||
| 84 | * @param tag - Unique tag of a buffer to check for. | ||
| 85 | * @return True if the buffer has been played, otherwise false. | ||
| 86 | */ | ||
| 87 | virtual bool IsBufferConsumed(u64 tag) = 0; | ||
| 88 | |||
| 89 | /** | ||
| 90 | * Empty out the buffer queue. | ||
| 91 | */ | ||
| 92 | virtual void ClearQueue() = 0; | ||
| 93 | 76 | ||
| 94 | /** | 77 | /** |
| 95 | * Check if the stream is paused. | 78 | * Check if the stream is paused. |
| 96 | * | 79 | * |
| 97 | * @return True if paused, otherwise false. | 80 | * @return True if paused, otherwise false. |
| 98 | */ | 81 | */ |
| 99 | bool IsPaused() { | 82 | bool IsPaused() const { |
| 100 | return paused; | 83 | return paused; |
| 101 | } | 84 | } |
| 102 | 85 | ||
| @@ -128,34 +111,6 @@ public: | |||
| 128 | } | 111 | } |
| 129 | 112 | ||
| 130 | /** | 113 | /** |
| 131 | * Get the total number of samples played by this stream. | ||
| 132 | * | ||
| 133 | * @return Number of samples played. | ||
| 134 | */ | ||
| 135 | u64 GetPlayedSampleCount() const { | ||
| 136 | return played_sample_count; | ||
| 137 | } | ||
| 138 | |||
| 139 | /** | ||
| 140 | * Set the number of samples played. | ||
| 141 | * This is started and stopped on system start/stop. | ||
| 142 | * | ||
| 143 | * @param played_sample_count_ - Number of samples to set. | ||
| 144 | */ | ||
| 145 | void SetPlayedSampleCount(u64 played_sample_count_) { | ||
| 146 | played_sample_count = played_sample_count_; | ||
| 147 | } | ||
| 148 | |||
| 149 | /** | ||
| 150 | * Add to the played sample count. | ||
| 151 | * | ||
| 152 | * @param num_samples - Number of samples to add. | ||
| 153 | */ | ||
| 154 | void AddPlayedSampleCount(u64 num_samples) { | ||
| 155 | played_sample_count += num_samples; | ||
| 156 | } | ||
| 157 | |||
| 158 | /** | ||
| 159 | * Get the system volume. | 114 | * Get the system volume. |
| 160 | * | 115 | * |
| 161 | * @return The current system volume. | 116 | * @return The current system volume. |
| @@ -200,23 +155,93 @@ public: | |||
| 200 | return queued_buffers.load(); | 155 | return queued_buffers.load(); |
| 201 | } | 156 | } |
| 202 | 157 | ||
| 158 | /** | ||
| 159 | * Set the maximum buffer queue size. | ||
| 160 | */ | ||
| 161 | void SetRingSize(u32 ring_size) { | ||
| 162 | max_queue_size = ring_size; | ||
| 163 | } | ||
| 164 | |||
| 165 | /** | ||
| 166 | * Append a new buffer and its samples to a waiting queue to play. | ||
| 167 | * | ||
| 168 | * @param buffer - Audio buffer information to be queued. | ||
| 169 | * @param samples - The s16 samples to be queue for playback. | ||
| 170 | */ | ||
| 171 | virtual void AppendBuffer(SinkBuffer& buffer, std::vector<s16>& samples); | ||
| 172 | |||
| 173 | /** | ||
| 174 | * Release a buffer. Audio In only, will fill a buffer with recorded samples. | ||
| 175 | * | ||
| 176 | * @param num_samples - Maximum number of samples to receive. | ||
| 177 | * @return Vector of recorded samples. May have fewer than num_samples. | ||
| 178 | */ | ||
| 179 | virtual std::vector<s16> ReleaseBuffer(u64 num_samples); | ||
| 180 | |||
| 181 | /** | ||
| 182 | * Empty out the buffer queue. | ||
| 183 | */ | ||
| 184 | void ClearQueue(); | ||
| 185 | |||
| 186 | /** | ||
| 187 | * Callback for AudioIn. | ||
| 188 | * | ||
| 189 | * @param input_buffer - Input buffer to be filled with samples. | ||
| 190 | * @param num_frames - Number of frames to be filled. | ||
| 191 | */ | ||
| 192 | void ProcessAudioIn(std::span<const s16> input_buffer, std::size_t num_frames); | ||
| 193 | |||
| 194 | /** | ||
| 195 | * Callback for AudioOut and AudioRenderer. | ||
| 196 | * | ||
| 197 | * @param output_buffer - Output buffer to be filled with samples. | ||
| 198 | * @param num_frames - Number of frames to be filled. | ||
| 199 | */ | ||
| 200 | void ProcessAudioOutAndRender(std::span<s16> output_buffer, std::size_t num_frames); | ||
| 201 | |||
| 202 | /** | ||
| 203 | * Stall core processes if the audio thread falls too far behind. | ||
| 204 | */ | ||
| 205 | void Stall(); | ||
| 206 | |||
| 207 | /** | ||
| 208 | * Unstall core processes. | ||
| 209 | */ | ||
| 210 | void Unstall(); | ||
| 211 | |||
| 203 | protected: | 212 | protected: |
| 204 | /// Number of buffers waiting to be played | 213 | /// Core system |
| 205 | std::atomic<u32> queued_buffers{}; | 214 | Core::System& system; |
| 206 | /// Total samples played by this stream | 215 | /// Type of this stream |
| 207 | std::atomic<u64> played_sample_count{}; | 216 | StreamType type; |
| 208 | /// Set by the audio render/in/out system which uses this stream | 217 | /// Set by the audio render/in/out system which uses this stream |
| 209 | f32 system_volume{1.0f}; | ||
| 210 | /// Set via IAudioDevice service calls | ||
| 211 | f32 device_volume{1.0f}; | ||
| 212 | /// Set by the audio render/in/out systen which uses this stream | ||
| 213 | u32 system_channels{2}; | 218 | u32 system_channels{2}; |
| 214 | /// Channels supported by hardware | 219 | /// Channels supported by hardware |
| 215 | u32 device_channels{2}; | 220 | u32 device_channels{2}; |
| 216 | /// Is this stream currently paused? | 221 | /// Is this stream currently paused? |
| 217 | std::atomic<bool> paused{true}; | 222 | std::atomic<bool> paused{true}; |
| 218 | /// Was this stream previously playing? | 223 | /// Name of this stream |
| 219 | std::atomic<bool> was_playing{false}; | 224 | std::string name{}; |
| 225 | |||
| 226 | private: | ||
| 227 | /// Ring buffer of the samples waiting to be played or consumed | ||
| 228 | Common::RingBuffer<s16, 0x10000> samples_buffer; | ||
| 229 | /// Audio buffers queued and waiting to play | ||
| 230 | Common::ReaderWriterQueue<SinkBuffer> queue; | ||
| 231 | /// The currently-playing audio buffer | ||
| 232 | SinkBuffer playing_buffer{}; | ||
| 233 | /// The last played (or received) frame of audio, used when the callback underruns | ||
| 234 | std::array<s16, MaxChannels> last_frame{}; | ||
| 235 | /// Number of buffers waiting to be played | ||
| 236 | std::atomic<u32> queued_buffers{}; | ||
| 237 | /// The ring size for audio out buffers (usually 4, rarely 2 or 8) | ||
| 238 | u32 max_queue_size{}; | ||
| 239 | /// Set by the audio render/in/out system which uses this stream | ||
| 240 | f32 system_volume{1.0f}; | ||
| 241 | /// Set via IAudioDevice service calls | ||
| 242 | f32 device_volume{1.0f}; | ||
| 243 | /// True if coretiming has been stalled | ||
| 244 | bool stalled{false}; | ||
| 220 | }; | 245 | }; |
| 221 | 246 | ||
| 222 | using SinkStreamPtr = std::unique_ptr<SinkStream>; | 247 | using SinkStreamPtr = std::unique_ptr<SinkStream>; |
diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index a6dc31b53..b1e0ba6cc 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt | |||
| @@ -124,6 +124,7 @@ add_library(common STATIC | |||
| 124 | settings.h | 124 | settings.h |
| 125 | settings_input.cpp | 125 | settings_input.cpp |
| 126 | settings_input.h | 126 | settings_input.h |
| 127 | socket_types.h | ||
| 127 | spin_lock.cpp | 128 | spin_lock.cpp |
| 128 | spin_lock.h | 129 | spin_lock.h |
| 129 | stream.cpp | 130 | stream.cpp |
| @@ -165,6 +166,7 @@ if(ARCHITECTURE_x86_64) | |||
| 165 | x64/xbyak_abi.h | 166 | x64/xbyak_abi.h |
| 166 | x64/xbyak_util.h | 167 | x64/xbyak_util.h |
| 167 | ) | 168 | ) |
| 169 | target_link_libraries(common PRIVATE xbyak) | ||
| 168 | endif() | 170 | endif() |
| 169 | 171 | ||
| 170 | if (MSVC) | 172 | if (MSVC) |
| @@ -188,7 +190,7 @@ endif() | |||
| 188 | create_target_directory_groups(common) | 190 | create_target_directory_groups(common) |
| 189 | 191 | ||
| 190 | target_link_libraries(common PUBLIC ${Boost_LIBRARIES} fmt::fmt microprofile Threads::Threads) | 192 | target_link_libraries(common PUBLIC ${Boost_LIBRARIES} fmt::fmt microprofile Threads::Threads) |
| 191 | target_link_libraries(common PRIVATE lz4::lz4 xbyak) | 193 | target_link_libraries(common PRIVATE lz4::lz4) |
| 192 | if (TARGET zstd::zstd) | 194 | if (TARGET zstd::zstd) |
| 193 | target_link_libraries(common PRIVATE zstd::zstd) | 195 | target_link_libraries(common PRIVATE zstd::zstd) |
| 194 | else() | 196 | else() |
diff --git a/src/common/announce_multiplayer_room.h b/src/common/announce_multiplayer_room.h index 0ad9da2be..4a3100fa4 100644 --- a/src/common/announce_multiplayer_room.h +++ b/src/common/announce_multiplayer_room.h | |||
| @@ -8,15 +8,15 @@ | |||
| 8 | #include <string> | 8 | #include <string> |
| 9 | #include <vector> | 9 | #include <vector> |
| 10 | #include "common/common_types.h" | 10 | #include "common/common_types.h" |
| 11 | #include "common/socket_types.h" | ||
| 11 | #include "web_service/web_result.h" | 12 | #include "web_service/web_result.h" |
| 12 | 13 | ||
| 13 | namespace AnnounceMultiplayerRoom { | 14 | namespace AnnounceMultiplayerRoom { |
| 14 | 15 | ||
| 15 | using MacAddress = std::array<u8, 6>; | ||
| 16 | |||
| 17 | struct GameInfo { | 16 | struct GameInfo { |
| 18 | std::string name{""}; | 17 | std::string name{""}; |
| 19 | u64 id{0}; | 18 | u64 id{0}; |
| 19 | std::string version{""}; | ||
| 20 | }; | 20 | }; |
| 21 | 21 | ||
| 22 | struct Member { | 22 | struct Member { |
| @@ -24,7 +24,7 @@ struct Member { | |||
| 24 | std::string nickname; | 24 | std::string nickname; |
| 25 | std::string display_name; | 25 | std::string display_name; |
| 26 | std::string avatar_url; | 26 | std::string avatar_url; |
| 27 | MacAddress mac_address; | 27 | Network::IPv4Address fake_ip; |
| 28 | GameInfo game; | 28 | GameInfo game; |
| 29 | }; | 29 | }; |
| 30 | 30 | ||
| @@ -75,10 +75,7 @@ public: | |||
| 75 | const bool has_password, const GameInfo& preferred_game) = 0; | 75 | const bool has_password, const GameInfo& preferred_game) = 0; |
| 76 | /** | 76 | /** |
| 77 | * Adds a player information to the data that gets announced | 77 | * Adds a player information to the data that gets announced |
| 78 | * @param nickname The nickname of the player | 78 | * @param member The player to add |
| 79 | * @param mac_address The MAC Address of the player | ||
| 80 | * @param game_id The title id of the game the player plays | ||
| 81 | * @param game_name The name of the game the player plays | ||
| 82 | */ | 79 | */ |
| 83 | virtual void AddPlayer(const Member& member) = 0; | 80 | virtual void AddPlayer(const Member& member) = 0; |
| 84 | 81 | ||
diff --git a/src/common/input.h b/src/common/input.h index 213aa2384..825b0d650 100644 --- a/src/common/input.h +++ b/src/common/input.h | |||
| @@ -102,6 +102,8 @@ struct AnalogProperties { | |||
| 102 | float offset{}; | 102 | float offset{}; |
| 103 | // Invert direction of the sensor data | 103 | // Invert direction of the sensor data |
| 104 | bool inverted{}; | 104 | bool inverted{}; |
| 105 | // Press once to activate, press again to release | ||
| 106 | bool toggle{}; | ||
| 105 | }; | 107 | }; |
| 106 | 108 | ||
| 107 | // Single analog sensor data | 109 | // Single analog sensor data |
| @@ -115,8 +117,11 @@ struct AnalogStatus { | |||
| 115 | struct ButtonStatus { | 117 | struct ButtonStatus { |
| 116 | Common::UUID uuid{}; | 118 | Common::UUID uuid{}; |
| 117 | bool value{}; | 119 | bool value{}; |
| 120 | // Invert value of the button | ||
| 118 | bool inverted{}; | 121 | bool inverted{}; |
| 122 | // Press once to activate, press again to release | ||
| 119 | bool toggle{}; | 123 | bool toggle{}; |
| 124 | // Internal lock for the toggle status | ||
| 120 | bool locked{}; | 125 | bool locked{}; |
| 121 | }; | 126 | }; |
| 122 | 127 | ||
diff --git a/src/common/microprofile.h b/src/common/microprofile.h index 91d14d5e1..56ef0a2dc 100644 --- a/src/common/microprofile.h +++ b/src/common/microprofile.h | |||
| @@ -22,12 +22,3 @@ typedef void* HANDLE; | |||
| 22 | #include <microprofile.h> | 22 | #include <microprofile.h> |
| 23 | 23 | ||
| 24 | #define MP_RGB(r, g, b) ((r) << 16 | (g) << 8 | (b) << 0) | 24 | #define MP_RGB(r, g, b) ((r) << 16 | (g) << 8 | (b) << 0) |
| 25 | |||
| 26 | // On OS X, some Mach header included by MicroProfile defines these as macros, conflicting with | ||
| 27 | // identifiers we use. | ||
| 28 | #ifdef PAGE_SIZE | ||
| 29 | #undef PAGE_SIZE | ||
| 30 | #endif | ||
| 31 | #ifdef PAGE_MASK | ||
| 32 | #undef PAGE_MASK | ||
| 33 | #endif | ||
diff --git a/src/common/parent_of_member.h b/src/common/parent_of_member.h index 70b1c5624..8e03f17d8 100644 --- a/src/common/parent_of_member.h +++ b/src/common/parent_of_member.h | |||
| @@ -11,7 +11,7 @@ namespace Common { | |||
| 11 | namespace detail { | 11 | namespace detail { |
| 12 | template <typename T, size_t Size, size_t Align> | 12 | template <typename T, size_t Size, size_t Align> |
| 13 | struct TypedStorageImpl { | 13 | struct TypedStorageImpl { |
| 14 | std::aligned_storage_t<Size, Align> storage_; | 14 | alignas(Align) u8 storage_[Size]; |
| 15 | }; | 15 | }; |
| 16 | } // namespace detail | 16 | } // namespace detail |
| 17 | 17 | ||
diff --git a/src/common/settings.cpp b/src/common/settings.cpp index 1c7b6dfae..0a560ebb7 100644 --- a/src/common/settings.cpp +++ b/src/common/settings.cpp | |||
| @@ -105,7 +105,7 @@ float Volume() { | |||
| 105 | if (values.audio_muted) { | 105 | if (values.audio_muted) { |
| 106 | return 0.0f; | 106 | return 0.0f; |
| 107 | } | 107 | } |
| 108 | return values.volume.GetValue() / 100.0f; | 108 | return values.volume.GetValue() / static_cast<f32>(values.volume.GetDefault()); |
| 109 | } | 109 | } |
| 110 | 110 | ||
| 111 | void UpdateRescalingInfo() { | 111 | void UpdateRescalingInfo() { |
| @@ -195,6 +195,7 @@ void RestoreGlobalState(bool is_powered_on) { | |||
| 195 | values.shader_backend.SetGlobal(true); | 195 | values.shader_backend.SetGlobal(true); |
| 196 | values.use_asynchronous_shaders.SetGlobal(true); | 196 | values.use_asynchronous_shaders.SetGlobal(true); |
| 197 | values.use_fast_gpu_time.SetGlobal(true); | 197 | values.use_fast_gpu_time.SetGlobal(true); |
| 198 | values.use_pessimistic_flushes.SetGlobal(true); | ||
| 198 | values.bg_red.SetGlobal(true); | 199 | values.bg_red.SetGlobal(true); |
| 199 | values.bg_green.SetGlobal(true); | 200 | values.bg_green.SetGlobal(true); |
| 200 | values.bg_blue.SetGlobal(true); | 201 | values.bg_blue.SetGlobal(true); |
diff --git a/src/common/settings.h b/src/common/settings.h index 1079cf8cb..13651de57 100644 --- a/src/common/settings.h +++ b/src/common/settings.h | |||
| @@ -374,7 +374,7 @@ struct Values { | |||
| 374 | Setting<std::string> audio_output_device_id{"auto", "output_device"}; | 374 | Setting<std::string> audio_output_device_id{"auto", "output_device"}; |
| 375 | Setting<std::string> audio_input_device_id{"auto", "input_device"}; | 375 | Setting<std::string> audio_input_device_id{"auto", "input_device"}; |
| 376 | Setting<bool> audio_muted{false, "audio_muted"}; | 376 | Setting<bool> audio_muted{false, "audio_muted"}; |
| 377 | SwitchableSetting<u8, true> volume{100, 0, 100, "volume"}; | 377 | SwitchableSetting<u8, true> volume{100, 0, 200, "volume"}; |
| 378 | Setting<bool> dump_audio_commands{false, "dump_audio_commands"}; | 378 | Setting<bool> dump_audio_commands{false, "dump_audio_commands"}; |
| 379 | 379 | ||
| 380 | // Core | 380 | // Core |
| @@ -446,6 +446,7 @@ struct Values { | |||
| 446 | ShaderBackend::SPIRV, "shader_backend"}; | 446 | ShaderBackend::SPIRV, "shader_backend"}; |
| 447 | SwitchableSetting<bool> use_asynchronous_shaders{false, "use_asynchronous_shaders"}; | 447 | SwitchableSetting<bool> use_asynchronous_shaders{false, "use_asynchronous_shaders"}; |
| 448 | SwitchableSetting<bool> use_fast_gpu_time{true, "use_fast_gpu_time"}; | 448 | SwitchableSetting<bool> use_fast_gpu_time{true, "use_fast_gpu_time"}; |
| 449 | SwitchableSetting<bool> use_pessimistic_flushes{false, "use_pessimistic_flushes"}; | ||
| 449 | 450 | ||
| 450 | SwitchableSetting<u8> bg_red{0, "bg_red"}; | 451 | SwitchableSetting<u8> bg_red{0, "bg_red"}; |
| 451 | SwitchableSetting<u8> bg_green{0, "bg_green"}; | 452 | SwitchableSetting<u8> bg_green{0, "bg_green"}; |
diff --git a/src/common/socket_types.h b/src/common/socket_types.h new file mode 100644 index 000000000..0a801a443 --- /dev/null +++ b/src/common/socket_types.h | |||
| @@ -0,0 +1,51 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include "common/common_types.h" | ||
| 7 | |||
| 8 | namespace Network { | ||
| 9 | |||
| 10 | /// Address families | ||
| 11 | enum class Domain : u8 { | ||
| 12 | INET, ///< Address family for IPv4 | ||
| 13 | }; | ||
| 14 | |||
| 15 | /// Socket types | ||
| 16 | enum class Type { | ||
| 17 | STREAM, | ||
| 18 | DGRAM, | ||
| 19 | RAW, | ||
| 20 | SEQPACKET, | ||
| 21 | }; | ||
| 22 | |||
| 23 | /// Protocol values for sockets | ||
| 24 | enum class Protocol : u8 { | ||
| 25 | ICMP, | ||
| 26 | TCP, | ||
| 27 | UDP, | ||
| 28 | }; | ||
| 29 | |||
| 30 | /// Shutdown mode | ||
| 31 | enum class ShutdownHow { | ||
| 32 | RD, | ||
| 33 | WR, | ||
| 34 | RDWR, | ||
| 35 | }; | ||
| 36 | |||
| 37 | /// Array of IPv4 address | ||
| 38 | using IPv4Address = std::array<u8, 4>; | ||
| 39 | |||
| 40 | /// Cross-platform sockaddr structure | ||
| 41 | struct SockAddrIn { | ||
| 42 | Domain family; | ||
| 43 | IPv4Address ip; | ||
| 44 | u16 portno; | ||
| 45 | }; | ||
| 46 | |||
| 47 | constexpr u32 FLAG_MSG_PEEK = 0x2; | ||
| 48 | constexpr u32 FLAG_MSG_DONTWAIT = 0x80; | ||
| 49 | constexpr u32 FLAG_O_NONBLOCK = 0x800; | ||
| 50 | |||
| 51 | } // namespace Network | ||
diff --git a/src/common/uint128.h b/src/common/uint128.h index f890ffec2..f450a6db9 100644 --- a/src/common/uint128.h +++ b/src/common/uint128.h | |||
| @@ -12,7 +12,6 @@ | |||
| 12 | #pragma intrinsic(_udiv128) | 12 | #pragma intrinsic(_udiv128) |
| 13 | #else | 13 | #else |
| 14 | #include <cstring> | 14 | #include <cstring> |
| 15 | #include <x86intrin.h> | ||
| 16 | #endif | 15 | #endif |
| 17 | 16 | ||
| 18 | #include "common/common_types.h" | 17 | #include "common/common_types.h" |
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 40b1ea4a2..806e7ff6c 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt | |||
| @@ -2,12 +2,8 @@ | |||
| 2 | # SPDX-License-Identifier: GPL-2.0-or-later | 2 | # SPDX-License-Identifier: GPL-2.0-or-later |
| 3 | 3 | ||
| 4 | add_library(core STATIC | 4 | add_library(core STATIC |
| 5 | announce_multiplayer_session.cpp | ||
| 6 | announce_multiplayer_session.h | ||
| 7 | arm/arm_interface.h | 5 | arm/arm_interface.h |
| 8 | arm/arm_interface.cpp | 6 | arm/arm_interface.cpp |
| 9 | arm/cpu_interrupt_handler.cpp | ||
| 10 | arm/cpu_interrupt_handler.h | ||
| 11 | arm/dynarmic/arm_dynarmic_32.cpp | 7 | arm/dynarmic/arm_dynarmic_32.cpp |
| 12 | arm/dynarmic/arm_dynarmic_32.h | 8 | arm/dynarmic/arm_dynarmic_32.h |
| 13 | arm/dynarmic/arm_dynarmic_64.cpp | 9 | arm/dynarmic/arm_dynarmic_64.cpp |
| @@ -504,9 +500,10 @@ add_library(core STATIC | |||
| 504 | hle/service/jit/jit.h | 500 | hle/service/jit/jit.h |
| 505 | hle/service/lbl/lbl.cpp | 501 | hle/service/lbl/lbl.cpp |
| 506 | hle/service/lbl/lbl.h | 502 | hle/service/lbl/lbl.h |
| 507 | hle/service/ldn/errors.h | 503 | hle/service/ldn/ldn_results.h |
| 508 | hle/service/ldn/ldn.cpp | 504 | hle/service/ldn/ldn.cpp |
| 509 | hle/service/ldn/ldn.h | 505 | hle/service/ldn/ldn.h |
| 506 | hle/service/ldn/ldn_types.h | ||
| 510 | hle/service/ldr/ldr.cpp | 507 | hle/service/ldr/ldr.cpp |
| 511 | hle/service/ldr/ldr.h | 508 | hle/service/ldr/ldr.h |
| 512 | hle/service/lm/lm.cpp | 509 | hle/service/lm/lm.cpp |
| @@ -541,14 +538,14 @@ add_library(core STATIC | |||
| 541 | hle/service/npns/npns.cpp | 538 | hle/service/npns/npns.cpp |
| 542 | hle/service/npns/npns.h | 539 | hle/service/npns/npns.h |
| 543 | hle/service/ns/errors.h | 540 | hle/service/ns/errors.h |
| 541 | hle/service/ns/iplatform_service_manager.cpp | ||
| 542 | hle/service/ns/iplatform_service_manager.h | ||
| 544 | hle/service/ns/language.cpp | 543 | hle/service/ns/language.cpp |
| 545 | hle/service/ns/language.h | 544 | hle/service/ns/language.h |
| 546 | hle/service/ns/ns.cpp | 545 | hle/service/ns/ns.cpp |
| 547 | hle/service/ns/ns.h | 546 | hle/service/ns/ns.h |
| 548 | hle/service/ns/pdm_qry.cpp | 547 | hle/service/ns/pdm_qry.cpp |
| 549 | hle/service/ns/pdm_qry.h | 548 | hle/service/ns/pdm_qry.h |
| 550 | hle/service/ns/pl_u.cpp | ||
| 551 | hle/service/ns/pl_u.h | ||
| 552 | hle/service/nvdrv/devices/nvdevice.h | 549 | hle/service/nvdrv/devices/nvdevice.h |
| 553 | hle/service/nvdrv/devices/nvdisp_disp0.cpp | 550 | hle/service/nvdrv/devices/nvdisp_disp0.cpp |
| 554 | hle/service/nvdrv/devices/nvdisp_disp0.h | 551 | hle/service/nvdrv/devices/nvdisp_disp0.h |
| @@ -725,10 +722,10 @@ add_library(core STATIC | |||
| 725 | internal_network/network_interface.cpp | 722 | internal_network/network_interface.cpp |
| 726 | internal_network/network_interface.h | 723 | internal_network/network_interface.h |
| 727 | internal_network/sockets.h | 724 | internal_network/sockets.h |
| 725 | internal_network/socket_proxy.cpp | ||
| 726 | internal_network/socket_proxy.h | ||
| 728 | loader/deconstructed_rom_directory.cpp | 727 | loader/deconstructed_rom_directory.cpp |
| 729 | loader/deconstructed_rom_directory.h | 728 | loader/deconstructed_rom_directory.h |
| 730 | loader/elf.cpp | ||
| 731 | loader/elf.h | ||
| 732 | loader/kip.cpp | 729 | loader/kip.cpp |
| 733 | loader/kip.h | 730 | loader/kip.h |
| 734 | loader/loader.cpp | 731 | loader/loader.cpp |
| @@ -787,7 +784,7 @@ endif() | |||
| 787 | create_target_directory_groups(core) | 784 | create_target_directory_groups(core) |
| 788 | 785 | ||
| 789 | target_link_libraries(core PUBLIC common PRIVATE audio_core network video_core) | 786 | target_link_libraries(core PUBLIC common PRIVATE audio_core network video_core) |
| 790 | target_link_libraries(core PUBLIC Boost::boost PRIVATE fmt::fmt nlohmann_json::nlohmann_json mbedtls Opus::Opus) | 787 | target_link_libraries(core PUBLIC Boost::boost PRIVATE fmt::fmt nlohmann_json::nlohmann_json mbedtls Opus::opus) |
| 791 | if (MINGW) | 788 | if (MINGW) |
| 792 | target_link_libraries(core PRIVATE ${MSWSOCK_LIBRARY}) | 789 | target_link_libraries(core PRIVATE ${MSWSOCK_LIBRARY}) |
| 793 | endif() | 790 | endif() |
diff --git a/src/core/arm/arm_interface.h b/src/core/arm/arm_interface.h index 73f259525..7d62d030e 100644 --- a/src/core/arm/arm_interface.h +++ b/src/core/arm/arm_interface.h | |||
| @@ -27,7 +27,6 @@ namespace Core { | |||
| 27 | class System; | 27 | class System; |
| 28 | class CPUInterruptHandler; | 28 | class CPUInterruptHandler; |
| 29 | 29 | ||
| 30 | using CPUInterrupts = std::array<CPUInterruptHandler, Core::Hardware::NUM_CPU_CORES>; | ||
| 31 | using WatchpointArray = std::array<Kernel::DebugWatchpoint, Core::Hardware::NUM_WATCHPOINTS>; | 30 | using WatchpointArray = std::array<Kernel::DebugWatchpoint, Core::Hardware::NUM_WATCHPOINTS>; |
| 32 | 31 | ||
| 33 | /// Generic ARMv8 CPU interface | 32 | /// Generic ARMv8 CPU interface |
| @@ -36,10 +35,8 @@ public: | |||
| 36 | YUZU_NON_COPYABLE(ARM_Interface); | 35 | YUZU_NON_COPYABLE(ARM_Interface); |
| 37 | YUZU_NON_MOVEABLE(ARM_Interface); | 36 | YUZU_NON_MOVEABLE(ARM_Interface); |
| 38 | 37 | ||
| 39 | explicit ARM_Interface(System& system_, CPUInterrupts& interrupt_handlers_, | 38 | explicit ARM_Interface(System& system_, bool uses_wall_clock_) |
| 40 | bool uses_wall_clock_) | 39 | : system{system_}, uses_wall_clock{uses_wall_clock_} {} |
| 41 | : system{system_}, interrupt_handlers{interrupt_handlers_}, uses_wall_clock{ | ||
| 42 | uses_wall_clock_} {} | ||
| 43 | virtual ~ARM_Interface() = default; | 40 | virtual ~ARM_Interface() = default; |
| 44 | 41 | ||
| 45 | struct ThreadContext32 { | 42 | struct ThreadContext32 { |
| @@ -181,6 +178,9 @@ public: | |||
| 181 | /// Signal an interrupt and ask the core to halt as soon as possible. | 178 | /// Signal an interrupt and ask the core to halt as soon as possible. |
| 182 | virtual void SignalInterrupt() = 0; | 179 | virtual void SignalInterrupt() = 0; |
| 183 | 180 | ||
| 181 | /// Clear a previous interrupt. | ||
| 182 | virtual void ClearInterrupt() = 0; | ||
| 183 | |||
| 184 | struct BacktraceEntry { | 184 | struct BacktraceEntry { |
| 185 | std::string module; | 185 | std::string module; |
| 186 | u64 address; | 186 | u64 address; |
| @@ -208,7 +208,6 @@ public: | |||
| 208 | protected: | 208 | protected: |
| 209 | /// System context that this ARM interface is running under. | 209 | /// System context that this ARM interface is running under. |
| 210 | System& system; | 210 | System& system; |
| 211 | CPUInterrupts& interrupt_handlers; | ||
| 212 | const WatchpointArray* watchpoints; | 211 | const WatchpointArray* watchpoints; |
| 213 | bool uses_wall_clock; | 212 | bool uses_wall_clock; |
| 214 | 213 | ||
diff --git a/src/core/arm/cpu_interrupt_handler.cpp b/src/core/arm/cpu_interrupt_handler.cpp deleted file mode 100644 index 77b6194d7..000000000 --- a/src/core/arm/cpu_interrupt_handler.cpp +++ /dev/null | |||
| @@ -1,24 +0,0 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #include "common/thread.h" | ||
| 5 | #include "core/arm/cpu_interrupt_handler.h" | ||
| 6 | |||
| 7 | namespace Core { | ||
| 8 | |||
| 9 | CPUInterruptHandler::CPUInterruptHandler() : interrupt_event{std::make_unique<Common::Event>()} {} | ||
| 10 | |||
| 11 | CPUInterruptHandler::~CPUInterruptHandler() = default; | ||
| 12 | |||
| 13 | void CPUInterruptHandler::SetInterrupt(bool is_interrupted_) { | ||
| 14 | if (is_interrupted_) { | ||
| 15 | interrupt_event->Set(); | ||
| 16 | } | ||
| 17 | is_interrupted = is_interrupted_; | ||
| 18 | } | ||
| 19 | |||
| 20 | void CPUInterruptHandler::AwaitInterrupt() { | ||
| 21 | interrupt_event->Wait(); | ||
| 22 | } | ||
| 23 | |||
| 24 | } // namespace Core | ||
diff --git a/src/core/arm/cpu_interrupt_handler.h b/src/core/arm/cpu_interrupt_handler.h deleted file mode 100644 index 286e12e53..000000000 --- a/src/core/arm/cpu_interrupt_handler.h +++ /dev/null | |||
| @@ -1,39 +0,0 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include <atomic> | ||
| 7 | #include <memory> | ||
| 8 | |||
| 9 | namespace Common { | ||
| 10 | class Event; | ||
| 11 | } | ||
| 12 | |||
| 13 | namespace Core { | ||
| 14 | |||
| 15 | class CPUInterruptHandler { | ||
| 16 | public: | ||
| 17 | CPUInterruptHandler(); | ||
| 18 | ~CPUInterruptHandler(); | ||
| 19 | |||
| 20 | CPUInterruptHandler(const CPUInterruptHandler&) = delete; | ||
| 21 | CPUInterruptHandler& operator=(const CPUInterruptHandler&) = delete; | ||
| 22 | |||
| 23 | CPUInterruptHandler(CPUInterruptHandler&&) = delete; | ||
| 24 | CPUInterruptHandler& operator=(CPUInterruptHandler&&) = delete; | ||
| 25 | |||
| 26 | bool IsInterrupted() const { | ||
| 27 | return is_interrupted; | ||
| 28 | } | ||
| 29 | |||
| 30 | void SetInterrupt(bool is_interrupted); | ||
| 31 | |||
| 32 | void AwaitInterrupt(); | ||
| 33 | |||
| 34 | private: | ||
| 35 | std::unique_ptr<Common::Event> interrupt_event; | ||
| 36 | std::atomic_bool is_interrupted{false}; | ||
| 37 | }; | ||
| 38 | |||
| 39 | } // namespace Core | ||
diff --git a/src/core/arm/dynarmic/arm_dynarmic_32.cpp b/src/core/arm/dynarmic/arm_dynarmic_32.cpp index b8d2ce224..d1e70f19d 100644 --- a/src/core/arm/dynarmic/arm_dynarmic_32.cpp +++ b/src/core/arm/dynarmic/arm_dynarmic_32.cpp | |||
| @@ -11,7 +11,6 @@ | |||
| 11 | #include "common/logging/log.h" | 11 | #include "common/logging/log.h" |
| 12 | #include "common/page_table.h" | 12 | #include "common/page_table.h" |
| 13 | #include "common/settings.h" | 13 | #include "common/settings.h" |
| 14 | #include "core/arm/cpu_interrupt_handler.h" | ||
| 15 | #include "core/arm/dynarmic/arm_dynarmic_32.h" | 14 | #include "core/arm/dynarmic/arm_dynarmic_32.h" |
| 16 | #include "core/arm/dynarmic/arm_dynarmic_cp15.h" | 15 | #include "core/arm/dynarmic/arm_dynarmic_cp15.h" |
| 17 | #include "core/arm/dynarmic/arm_exclusive_monitor.h" | 16 | #include "core/arm/dynarmic/arm_exclusive_monitor.h" |
| @@ -125,7 +124,9 @@ public: | |||
| 125 | } | 124 | } |
| 126 | 125 | ||
| 127 | void AddTicks(u64 ticks) override { | 126 | void AddTicks(u64 ticks) override { |
| 128 | ASSERT_MSG(!parent.uses_wall_clock, "This should never happen - dynarmic ticking disabled"); | 127 | if (parent.uses_wall_clock) { |
| 128 | return; | ||
| 129 | } | ||
| 129 | 130 | ||
| 130 | // Divide the number of ticks by the amount of CPU cores. TODO(Subv): This yields only a | 131 | // Divide the number of ticks by the amount of CPU cores. TODO(Subv): This yields only a |
| 131 | // rough approximation of the amount of executed ticks in the system, it may be thrown off | 132 | // rough approximation of the amount of executed ticks in the system, it may be thrown off |
| @@ -142,7 +143,12 @@ public: | |||
| 142 | } | 143 | } |
| 143 | 144 | ||
| 144 | u64 GetTicksRemaining() override { | 145 | u64 GetTicksRemaining() override { |
| 145 | ASSERT_MSG(!parent.uses_wall_clock, "This should never happen - dynarmic ticking disabled"); | 146 | if (parent.uses_wall_clock) { |
| 147 | if (!IsInterrupted()) { | ||
| 148 | return minimum_run_cycles; | ||
| 149 | } | ||
| 150 | return 0U; | ||
| 151 | } | ||
| 146 | 152 | ||
| 147 | return std::max<s64>(parent.system.CoreTiming().GetDowncount(), 0); | 153 | return std::max<s64>(parent.system.CoreTiming().GetDowncount(), 0); |
| 148 | } | 154 | } |
| @@ -168,11 +174,15 @@ public: | |||
| 168 | parent.jit.load()->HaltExecution(hr); | 174 | parent.jit.load()->HaltExecution(hr); |
| 169 | } | 175 | } |
| 170 | 176 | ||
| 177 | bool IsInterrupted() { | ||
| 178 | return parent.system.Kernel().PhysicalCore(parent.core_index).IsInterrupted(); | ||
| 179 | } | ||
| 180 | |||
| 171 | ARM_Dynarmic_32& parent; | 181 | ARM_Dynarmic_32& parent; |
| 172 | Core::Memory::Memory& memory; | 182 | Core::Memory::Memory& memory; |
| 173 | std::size_t num_interpreted_instructions{}; | 183 | std::size_t num_interpreted_instructions{}; |
| 174 | bool debugger_enabled{}; | 184 | bool debugger_enabled{}; |
| 175 | static constexpr u64 minimum_run_cycles = 1000U; | 185 | static constexpr u64 minimum_run_cycles = 10000U; |
| 176 | }; | 186 | }; |
| 177 | 187 | ||
| 178 | std::shared_ptr<Dynarmic::A32::Jit> ARM_Dynarmic_32::MakeJit(Common::PageTable* page_table) const { | 188 | std::shared_ptr<Dynarmic::A32::Jit> ARM_Dynarmic_32::MakeJit(Common::PageTable* page_table) const { |
| @@ -180,19 +190,21 @@ std::shared_ptr<Dynarmic::A32::Jit> ARM_Dynarmic_32::MakeJit(Common::PageTable* | |||
| 180 | config.callbacks = cb.get(); | 190 | config.callbacks = cb.get(); |
| 181 | config.coprocessors[15] = cp15; | 191 | config.coprocessors[15] = cp15; |
| 182 | config.define_unpredictable_behaviour = true; | 192 | config.define_unpredictable_behaviour = true; |
| 183 | static constexpr std::size_t PAGE_BITS = 12; | 193 | static constexpr std::size_t YUZU_PAGEBITS = 12; |
| 184 | static constexpr std::size_t NUM_PAGE_TABLE_ENTRIES = 1 << (32 - PAGE_BITS); | 194 | static constexpr std::size_t NUM_PAGE_TABLE_ENTRIES = 1 << (32 - YUZU_PAGEBITS); |
| 185 | if (page_table) { | 195 | if (page_table) { |
| 186 | config.page_table = reinterpret_cast<std::array<std::uint8_t*, NUM_PAGE_TABLE_ENTRIES>*>( | 196 | config.page_table = reinterpret_cast<std::array<std::uint8_t*, NUM_PAGE_TABLE_ENTRIES>*>( |
| 187 | page_table->pointers.data()); | 197 | page_table->pointers.data()); |
| 198 | config.absolute_offset_page_table = true; | ||
| 199 | config.page_table_pointer_mask_bits = Common::PageTable::ATTRIBUTE_BITS; | ||
| 200 | config.detect_misaligned_access_via_page_table = 16 | 32 | 64 | 128; | ||
| 201 | config.only_detect_misalignment_via_page_table_on_page_boundary = true; | ||
| 202 | |||
| 188 | config.fastmem_pointer = page_table->fastmem_arena; | 203 | config.fastmem_pointer = page_table->fastmem_arena; |
| 204 | |||
| 205 | config.fastmem_exclusive_access = config.fastmem_pointer != nullptr; | ||
| 206 | config.recompile_on_exclusive_fastmem_failure = true; | ||
| 189 | } | 207 | } |
| 190 | config.absolute_offset_page_table = true; | ||
| 191 | config.page_table_pointer_mask_bits = Common::PageTable::ATTRIBUTE_BITS; | ||
| 192 | config.detect_misaligned_access_via_page_table = 16 | 32 | 64 | 128; | ||
| 193 | config.only_detect_misalignment_via_page_table_on_page_boundary = true; | ||
| 194 | config.fastmem_exclusive_access = true; | ||
| 195 | config.recompile_on_exclusive_fastmem_failure = true; | ||
| 196 | 208 | ||
| 197 | // Multi-process state | 209 | // Multi-process state |
| 198 | config.processor_id = core_index; | 210 | config.processor_id = core_index; |
| @@ -200,7 +212,7 @@ std::shared_ptr<Dynarmic::A32::Jit> ARM_Dynarmic_32::MakeJit(Common::PageTable* | |||
| 200 | 212 | ||
| 201 | // Timing | 213 | // Timing |
| 202 | config.wall_clock_cntpct = uses_wall_clock; | 214 | config.wall_clock_cntpct = uses_wall_clock; |
| 203 | config.enable_cycle_counting = !uses_wall_clock; | 215 | config.enable_cycle_counting = true; |
| 204 | 216 | ||
| 205 | // Code cache size | 217 | // Code cache size |
| 206 | config.code_cache_size = 512_MiB; | 218 | config.code_cache_size = 512_MiB; |
| @@ -244,6 +256,7 @@ std::shared_ptr<Dynarmic::A32::Jit> ARM_Dynarmic_32::MakeJit(Common::PageTable* | |||
| 244 | } | 256 | } |
| 245 | if (!Settings::values.cpuopt_fastmem) { | 257 | if (!Settings::values.cpuopt_fastmem) { |
| 246 | config.fastmem_pointer = nullptr; | 258 | config.fastmem_pointer = nullptr; |
| 259 | config.fastmem_exclusive_access = false; | ||
| 247 | } | 260 | } |
| 248 | if (!Settings::values.cpuopt_fastmem_exclusives) { | 261 | if (!Settings::values.cpuopt_fastmem_exclusives) { |
| 249 | config.fastmem_exclusive_access = false; | 262 | config.fastmem_exclusive_access = false; |
| @@ -311,11 +324,9 @@ void ARM_Dynarmic_32::RewindBreakpointInstruction() { | |||
| 311 | LoadContext(breakpoint_context); | 324 | LoadContext(breakpoint_context); |
| 312 | } | 325 | } |
| 313 | 326 | ||
| 314 | ARM_Dynarmic_32::ARM_Dynarmic_32(System& system_, CPUInterrupts& interrupt_handlers_, | 327 | ARM_Dynarmic_32::ARM_Dynarmic_32(System& system_, bool uses_wall_clock_, |
| 315 | bool uses_wall_clock_, ExclusiveMonitor& exclusive_monitor_, | 328 | ExclusiveMonitor& exclusive_monitor_, std::size_t core_index_) |
| 316 | std::size_t core_index_) | 329 | : ARM_Interface{system_, uses_wall_clock_}, cb(std::make_unique<DynarmicCallbacks32>(*this)), |
| 317 | : ARM_Interface{system_, interrupt_handlers_, uses_wall_clock_}, | ||
| 318 | cb(std::make_unique<DynarmicCallbacks32>(*this)), | ||
| 319 | cp15(std::make_shared<DynarmicCP15>(*this)), core_index{core_index_}, | 330 | cp15(std::make_shared<DynarmicCP15>(*this)), core_index{core_index_}, |
| 320 | exclusive_monitor{dynamic_cast<DynarmicExclusiveMonitor&>(exclusive_monitor_)}, | 331 | exclusive_monitor{dynamic_cast<DynarmicExclusiveMonitor&>(exclusive_monitor_)}, |
| 321 | null_jit{MakeJit(nullptr)}, jit{null_jit.get()} {} | 332 | null_jit{MakeJit(nullptr)}, jit{null_jit.get()} {} |
| @@ -394,6 +405,10 @@ void ARM_Dynarmic_32::SignalInterrupt() { | |||
| 394 | jit.load()->HaltExecution(break_loop); | 405 | jit.load()->HaltExecution(break_loop); |
| 395 | } | 406 | } |
| 396 | 407 | ||
| 408 | void ARM_Dynarmic_32::ClearInterrupt() { | ||
| 409 | jit.load()->ClearHalt(break_loop); | ||
| 410 | } | ||
| 411 | |||
| 397 | void ARM_Dynarmic_32::ClearInstructionCache() { | 412 | void ARM_Dynarmic_32::ClearInstructionCache() { |
| 398 | jit.load()->ClearCache(); | 413 | jit.load()->ClearCache(); |
| 399 | } | 414 | } |
diff --git a/src/core/arm/dynarmic/arm_dynarmic_32.h b/src/core/arm/dynarmic/arm_dynarmic_32.h index 346e9abf8..d24ba2289 100644 --- a/src/core/arm/dynarmic/arm_dynarmic_32.h +++ b/src/core/arm/dynarmic/arm_dynarmic_32.h | |||
| @@ -28,8 +28,8 @@ class System; | |||
| 28 | 28 | ||
| 29 | class ARM_Dynarmic_32 final : public ARM_Interface { | 29 | class ARM_Dynarmic_32 final : public ARM_Interface { |
| 30 | public: | 30 | public: |
| 31 | ARM_Dynarmic_32(System& system_, CPUInterrupts& interrupt_handlers_, bool uses_wall_clock_, | 31 | ARM_Dynarmic_32(System& system_, bool uses_wall_clock_, ExclusiveMonitor& exclusive_monitor_, |
| 32 | ExclusiveMonitor& exclusive_monitor_, std::size_t core_index_); | 32 | std::size_t core_index_); |
| 33 | ~ARM_Dynarmic_32() override; | 33 | ~ARM_Dynarmic_32() override; |
| 34 | 34 | ||
| 35 | void SetPC(u64 pc) override; | 35 | void SetPC(u64 pc) override; |
| @@ -56,6 +56,7 @@ public: | |||
| 56 | void LoadContext(const ThreadContext64& ctx) override {} | 56 | void LoadContext(const ThreadContext64& ctx) override {} |
| 57 | 57 | ||
| 58 | void SignalInterrupt() override; | 58 | void SignalInterrupt() override; |
| 59 | void ClearInterrupt() override; | ||
| 59 | void ClearExclusiveState() override; | 60 | void ClearExclusiveState() override; |
| 60 | 61 | ||
| 61 | void ClearInstructionCache() override; | 62 | void ClearInstructionCache() override; |
diff --git a/src/core/arm/dynarmic/arm_dynarmic_64.cpp b/src/core/arm/dynarmic/arm_dynarmic_64.cpp index 1a4d37cbc..1d46f6d40 100644 --- a/src/core/arm/dynarmic/arm_dynarmic_64.cpp +++ b/src/core/arm/dynarmic/arm_dynarmic_64.cpp | |||
| @@ -10,7 +10,6 @@ | |||
| 10 | #include "common/logging/log.h" | 10 | #include "common/logging/log.h" |
| 11 | #include "common/page_table.h" | 11 | #include "common/page_table.h" |
| 12 | #include "common/settings.h" | 12 | #include "common/settings.h" |
| 13 | #include "core/arm/cpu_interrupt_handler.h" | ||
| 14 | #include "core/arm/dynarmic/arm_dynarmic_64.h" | 13 | #include "core/arm/dynarmic/arm_dynarmic_64.h" |
| 15 | #include "core/arm/dynarmic/arm_exclusive_monitor.h" | 14 | #include "core/arm/dynarmic/arm_exclusive_monitor.h" |
| 16 | #include "core/core.h" | 15 | #include "core/core.h" |
| @@ -166,7 +165,9 @@ public: | |||
| 166 | } | 165 | } |
| 167 | 166 | ||
| 168 | void AddTicks(u64 ticks) override { | 167 | void AddTicks(u64 ticks) override { |
| 169 | ASSERT_MSG(!parent.uses_wall_clock, "This should never happen - dynarmic ticking disabled"); | 168 | if (parent.uses_wall_clock) { |
| 169 | return; | ||
| 170 | } | ||
| 170 | 171 | ||
| 171 | // Divide the number of ticks by the amount of CPU cores. TODO(Subv): This yields only a | 172 | // Divide the number of ticks by the amount of CPU cores. TODO(Subv): This yields only a |
| 172 | // rough approximation of the amount of executed ticks in the system, it may be thrown off | 173 | // rough approximation of the amount of executed ticks in the system, it may be thrown off |
| @@ -181,7 +182,12 @@ public: | |||
| 181 | } | 182 | } |
| 182 | 183 | ||
| 183 | u64 GetTicksRemaining() override { | 184 | u64 GetTicksRemaining() override { |
| 184 | ASSERT_MSG(!parent.uses_wall_clock, "This should never happen - dynarmic ticking disabled"); | 185 | if (parent.uses_wall_clock) { |
| 186 | if (!IsInterrupted()) { | ||
| 187 | return minimum_run_cycles; | ||
| 188 | } | ||
| 189 | return 0U; | ||
| 190 | } | ||
| 185 | 191 | ||
| 186 | return std::max<s64>(parent.system.CoreTiming().GetDowncount(), 0); | 192 | return std::max<s64>(parent.system.CoreTiming().GetDowncount(), 0); |
| 187 | } | 193 | } |
| @@ -211,12 +217,16 @@ public: | |||
| 211 | parent.jit.load()->HaltExecution(hr); | 217 | parent.jit.load()->HaltExecution(hr); |
| 212 | } | 218 | } |
| 213 | 219 | ||
| 220 | bool IsInterrupted() { | ||
| 221 | return parent.system.Kernel().PhysicalCore(parent.core_index).IsInterrupted(); | ||
| 222 | } | ||
| 223 | |||
| 214 | ARM_Dynarmic_64& parent; | 224 | ARM_Dynarmic_64& parent; |
| 215 | Core::Memory::Memory& memory; | 225 | Core::Memory::Memory& memory; |
| 216 | u64 tpidrro_el0 = 0; | 226 | u64 tpidrro_el0 = 0; |
| 217 | u64 tpidr_el0 = 0; | 227 | u64 tpidr_el0 = 0; |
| 218 | bool debugger_enabled{}; | 228 | bool debugger_enabled{}; |
| 219 | static constexpr u64 minimum_run_cycles = 1000U; | 229 | static constexpr u64 minimum_run_cycles = 10000U; |
| 220 | }; | 230 | }; |
| 221 | 231 | ||
| 222 | std::shared_ptr<Dynarmic::A64::Jit> ARM_Dynarmic_64::MakeJit(Common::PageTable* page_table, | 232 | std::shared_ptr<Dynarmic::A64::Jit> ARM_Dynarmic_64::MakeJit(Common::PageTable* page_table, |
| @@ -240,7 +250,7 @@ std::shared_ptr<Dynarmic::A64::Jit> ARM_Dynarmic_64::MakeJit(Common::PageTable* | |||
| 240 | config.fastmem_address_space_bits = address_space_bits; | 250 | config.fastmem_address_space_bits = address_space_bits; |
| 241 | config.silently_mirror_fastmem = false; | 251 | config.silently_mirror_fastmem = false; |
| 242 | 252 | ||
| 243 | config.fastmem_exclusive_access = true; | 253 | config.fastmem_exclusive_access = config.fastmem_pointer != nullptr; |
| 244 | config.recompile_on_exclusive_fastmem_failure = true; | 254 | config.recompile_on_exclusive_fastmem_failure = true; |
| 245 | } | 255 | } |
| 246 | 256 | ||
| @@ -260,7 +270,7 @@ std::shared_ptr<Dynarmic::A64::Jit> ARM_Dynarmic_64::MakeJit(Common::PageTable* | |||
| 260 | 270 | ||
| 261 | // Timing | 271 | // Timing |
| 262 | config.wall_clock_cntpct = uses_wall_clock; | 272 | config.wall_clock_cntpct = uses_wall_clock; |
| 263 | config.enable_cycle_counting = !uses_wall_clock; | 273 | config.enable_cycle_counting = true; |
| 264 | 274 | ||
| 265 | // Code cache size | 275 | // Code cache size |
| 266 | config.code_cache_size = 512_MiB; | 276 | config.code_cache_size = 512_MiB; |
| @@ -304,6 +314,7 @@ std::shared_ptr<Dynarmic::A64::Jit> ARM_Dynarmic_64::MakeJit(Common::PageTable* | |||
| 304 | } | 314 | } |
| 305 | if (!Settings::values.cpuopt_fastmem) { | 315 | if (!Settings::values.cpuopt_fastmem) { |
| 306 | config.fastmem_pointer = nullptr; | 316 | config.fastmem_pointer = nullptr; |
| 317 | config.fastmem_exclusive_access = false; | ||
| 307 | } | 318 | } |
| 308 | if (!Settings::values.cpuopt_fastmem_exclusives) { | 319 | if (!Settings::values.cpuopt_fastmem_exclusives) { |
| 309 | config.fastmem_exclusive_access = false; | 320 | config.fastmem_exclusive_access = false; |
| @@ -371,10 +382,9 @@ void ARM_Dynarmic_64::RewindBreakpointInstruction() { | |||
| 371 | LoadContext(breakpoint_context); | 382 | LoadContext(breakpoint_context); |
| 372 | } | 383 | } |
| 373 | 384 | ||
| 374 | ARM_Dynarmic_64::ARM_Dynarmic_64(System& system_, CPUInterrupts& interrupt_handlers_, | 385 | ARM_Dynarmic_64::ARM_Dynarmic_64(System& system_, bool uses_wall_clock_, |
| 375 | bool uses_wall_clock_, ExclusiveMonitor& exclusive_monitor_, | 386 | ExclusiveMonitor& exclusive_monitor_, std::size_t core_index_) |
| 376 | std::size_t core_index_) | 387 | : ARM_Interface{system_, uses_wall_clock_}, |
| 377 | : ARM_Interface{system_, interrupt_handlers_, uses_wall_clock_}, | ||
| 378 | cb(std::make_unique<DynarmicCallbacks64>(*this)), core_index{core_index_}, | 388 | cb(std::make_unique<DynarmicCallbacks64>(*this)), core_index{core_index_}, |
| 379 | exclusive_monitor{dynamic_cast<DynarmicExclusiveMonitor&>(exclusive_monitor_)}, | 389 | exclusive_monitor{dynamic_cast<DynarmicExclusiveMonitor&>(exclusive_monitor_)}, |
| 380 | null_jit{MakeJit(nullptr, 48)}, jit{null_jit.get()} {} | 390 | null_jit{MakeJit(nullptr, 48)}, jit{null_jit.get()} {} |
| @@ -461,6 +471,10 @@ void ARM_Dynarmic_64::SignalInterrupt() { | |||
| 461 | jit.load()->HaltExecution(break_loop); | 471 | jit.load()->HaltExecution(break_loop); |
| 462 | } | 472 | } |
| 463 | 473 | ||
| 474 | void ARM_Dynarmic_64::ClearInterrupt() { | ||
| 475 | jit.load()->ClearHalt(break_loop); | ||
| 476 | } | ||
| 477 | |||
| 464 | void ARM_Dynarmic_64::ClearInstructionCache() { | 478 | void ARM_Dynarmic_64::ClearInstructionCache() { |
| 465 | jit.load()->ClearCache(); | 479 | jit.load()->ClearCache(); |
| 466 | } | 480 | } |
diff --git a/src/core/arm/dynarmic/arm_dynarmic_64.h b/src/core/arm/dynarmic/arm_dynarmic_64.h index c77a83ad7..ed1a5eb96 100644 --- a/src/core/arm/dynarmic/arm_dynarmic_64.h +++ b/src/core/arm/dynarmic/arm_dynarmic_64.h | |||
| @@ -20,14 +20,13 @@ class Memory; | |||
| 20 | namespace Core { | 20 | namespace Core { |
| 21 | 21 | ||
| 22 | class DynarmicCallbacks64; | 22 | class DynarmicCallbacks64; |
| 23 | class CPUInterruptHandler; | ||
| 24 | class DynarmicExclusiveMonitor; | 23 | class DynarmicExclusiveMonitor; |
| 25 | class System; | 24 | class System; |
| 26 | 25 | ||
| 27 | class ARM_Dynarmic_64 final : public ARM_Interface { | 26 | class ARM_Dynarmic_64 final : public ARM_Interface { |
| 28 | public: | 27 | public: |
| 29 | ARM_Dynarmic_64(System& system_, CPUInterrupts& interrupt_handlers_, bool uses_wall_clock_, | 28 | ARM_Dynarmic_64(System& system_, bool uses_wall_clock_, ExclusiveMonitor& exclusive_monitor_, |
| 30 | ExclusiveMonitor& exclusive_monitor_, std::size_t core_index_); | 29 | std::size_t core_index_); |
| 31 | ~ARM_Dynarmic_64() override; | 30 | ~ARM_Dynarmic_64() override; |
| 32 | 31 | ||
| 33 | void SetPC(u64 pc) override; | 32 | void SetPC(u64 pc) override; |
| @@ -50,6 +49,7 @@ public: | |||
| 50 | void LoadContext(const ThreadContext64& ctx) override; | 49 | void LoadContext(const ThreadContext64& ctx) override; |
| 51 | 50 | ||
| 52 | void SignalInterrupt() override; | 51 | void SignalInterrupt() override; |
| 52 | void ClearInterrupt() override; | ||
| 53 | void ClearExclusiveState() override; | 53 | void ClearExclusiveState() override; |
| 54 | 54 | ||
| 55 | void ClearInstructionCache() override; | 55 | void ClearInstructionCache() override; |
diff --git a/src/core/arm/dynarmic/arm_dynarmic_cp15.cpp b/src/core/arm/dynarmic/arm_dynarmic_cp15.cpp index e9123c13d..200efe4db 100644 --- a/src/core/arm/dynarmic/arm_dynarmic_cp15.cpp +++ b/src/core/arm/dynarmic/arm_dynarmic_cp15.cpp | |||
| @@ -8,6 +8,10 @@ | |||
| 8 | #include "core/core.h" | 8 | #include "core/core.h" |
| 9 | #include "core/core_timing.h" | 9 | #include "core/core_timing.h" |
| 10 | 10 | ||
| 11 | #ifdef _MSC_VER | ||
| 12 | #include <intrin.h> | ||
| 13 | #endif | ||
| 14 | |||
| 11 | using Callback = Dynarmic::A32::Coprocessor::Callback; | 15 | using Callback = Dynarmic::A32::Coprocessor::Callback; |
| 12 | using CallbackOrAccessOneWord = Dynarmic::A32::Coprocessor::CallbackOrAccessOneWord; | 16 | using CallbackOrAccessOneWord = Dynarmic::A32::Coprocessor::CallbackOrAccessOneWord; |
| 13 | using CallbackOrAccessTwoWords = Dynarmic::A32::Coprocessor::CallbackOrAccessTwoWords; | 17 | using CallbackOrAccessTwoWords = Dynarmic::A32::Coprocessor::CallbackOrAccessTwoWords; |
| @@ -47,12 +51,31 @@ CallbackOrAccessOneWord DynarmicCP15::CompileSendOneWord(bool two, unsigned opc1 | |||
| 47 | switch (opc2) { | 51 | switch (opc2) { |
| 48 | case 4: | 52 | case 4: |
| 49 | // CP15_DATA_SYNC_BARRIER | 53 | // CP15_DATA_SYNC_BARRIER |
| 50 | // This is a dummy write, we ignore the value written here. | 54 | return Callback{ |
| 51 | return &dummy_value; | 55 | [](Dynarmic::A32::Jit*, void*, std::uint32_t, std::uint32_t) -> std::uint64_t { |
| 56 | #ifdef _MSC_VER | ||
| 57 | _mm_mfence(); | ||
| 58 | _mm_lfence(); | ||
| 59 | #else | ||
| 60 | asm volatile("mfence\n\tlfence\n\t" : : : "memory"); | ||
| 61 | #endif | ||
| 62 | return 0; | ||
| 63 | }, | ||
| 64 | std::nullopt, | ||
| 65 | }; | ||
| 52 | case 5: | 66 | case 5: |
| 53 | // CP15_DATA_MEMORY_BARRIER | 67 | // CP15_DATA_MEMORY_BARRIER |
| 54 | // This is a dummy write, we ignore the value written here. | 68 | return Callback{ |
| 55 | return &dummy_value; | 69 | [](Dynarmic::A32::Jit*, void*, std::uint32_t, std::uint32_t) -> std::uint64_t { |
| 70 | #ifdef _MSC_VER | ||
| 71 | _mm_mfence(); | ||
| 72 | #else | ||
| 73 | asm volatile("mfence\n\t" : : : "memory"); | ||
| 74 | #endif | ||
| 75 | return 0; | ||
| 76 | }, | ||
| 77 | std::nullopt, | ||
| 78 | }; | ||
| 56 | } | 79 | } |
| 57 | } | 80 | } |
| 58 | 81 | ||
diff --git a/src/core/arm/dynarmic/arm_dynarmic_cp15.h b/src/core/arm/dynarmic/arm_dynarmic_cp15.h index 5b2a51636..d90b3e568 100644 --- a/src/core/arm/dynarmic/arm_dynarmic_cp15.h +++ b/src/core/arm/dynarmic/arm_dynarmic_cp15.h | |||
| @@ -35,6 +35,8 @@ public: | |||
| 35 | ARM_Dynarmic_32& parent; | 35 | ARM_Dynarmic_32& parent; |
| 36 | u32 uprw = 0; | 36 | u32 uprw = 0; |
| 37 | u32 uro = 0; | 37 | u32 uro = 0; |
| 38 | |||
| 39 | friend class ARM_Dynarmic_32; | ||
| 38 | }; | 40 | }; |
| 39 | 41 | ||
| 40 | } // namespace Core | 42 | } // namespace Core |
diff --git a/src/core/core.cpp b/src/core/core.cpp index ea32a4a8d..121092868 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp | |||
| @@ -141,8 +141,6 @@ struct System::Impl { | |||
| 141 | core_timing.SyncPause(false); | 141 | core_timing.SyncPause(false); |
| 142 | is_paused = false; | 142 | is_paused = false; |
| 143 | 143 | ||
| 144 | audio_core->PauseSinks(false); | ||
| 145 | |||
| 146 | return status; | 144 | return status; |
| 147 | } | 145 | } |
| 148 | 146 | ||
| @@ -150,8 +148,6 @@ struct System::Impl { | |||
| 150 | std::unique_lock<std::mutex> lk(suspend_guard); | 148 | std::unique_lock<std::mutex> lk(suspend_guard); |
| 151 | status = SystemResultStatus::Success; | 149 | status = SystemResultStatus::Success; |
| 152 | 150 | ||
| 153 | audio_core->PauseSinks(true); | ||
| 154 | |||
| 155 | core_timing.SyncPause(true); | 151 | core_timing.SyncPause(true); |
| 156 | kernel.Suspend(true); | 152 | kernel.Suspend(true); |
| 157 | is_paused = true; | 153 | is_paused = true; |
| @@ -319,10 +315,19 @@ struct System::Impl { | |||
| 319 | if (app_loader->ReadTitle(name) != Loader::ResultStatus::Success) { | 315 | if (app_loader->ReadTitle(name) != Loader::ResultStatus::Success) { |
| 320 | LOG_ERROR(Core, "Failed to read title for ROM (Error {})", load_result); | 316 | LOG_ERROR(Core, "Failed to read title for ROM (Error {})", load_result); |
| 321 | } | 317 | } |
| 318 | |||
| 319 | std::string title_version; | ||
| 320 | const FileSys::PatchManager pm(program_id, system.GetFileSystemController(), | ||
| 321 | system.GetContentProvider()); | ||
| 322 | const auto metadata = pm.GetControlMetadata(); | ||
| 323 | if (metadata.first != nullptr) { | ||
| 324 | title_version = metadata.first->GetVersionString(); | ||
| 325 | } | ||
| 322 | if (auto room_member = room_network.GetRoomMember().lock()) { | 326 | if (auto room_member = room_network.GetRoomMember().lock()) { |
| 323 | Network::GameInfo game_info; | 327 | Network::GameInfo game_info; |
| 324 | game_info.name = name; | 328 | game_info.name = name; |
| 325 | game_info.id = program_id; | 329 | game_info.id = program_id; |
| 330 | game_info.version = title_version; | ||
| 326 | room_member->SendGameInfo(game_info); | 331 | room_member->SendGameInfo(game_info); |
| 327 | } | 332 | } |
| 328 | 333 | ||
diff --git a/src/core/core_timing.cpp b/src/core/core_timing.cpp index 2dbb99c8b..5375a5d59 100644 --- a/src/core/core_timing.cpp +++ b/src/core/core_timing.cpp | |||
| @@ -73,7 +73,6 @@ void CoreTiming::Shutdown() { | |||
| 73 | if (timer_thread) { | 73 | if (timer_thread) { |
| 74 | timer_thread->join(); | 74 | timer_thread->join(); |
| 75 | } | 75 | } |
| 76 | pause_callbacks.clear(); | ||
| 77 | ClearPendingEvents(); | 76 | ClearPendingEvents(); |
| 78 | timer_thread.reset(); | 77 | timer_thread.reset(); |
| 79 | has_started = false; | 78 | has_started = false; |
| @@ -86,10 +85,6 @@ void CoreTiming::Pause(bool is_paused) { | |||
| 86 | if (!is_paused) { | 85 | if (!is_paused) { |
| 87 | pause_end_time = GetGlobalTimeNs().count(); | 86 | pause_end_time = GetGlobalTimeNs().count(); |
| 88 | } | 87 | } |
| 89 | |||
| 90 | for (auto& cb : pause_callbacks) { | ||
| 91 | cb(is_paused); | ||
| 92 | } | ||
| 93 | } | 88 | } |
| 94 | 89 | ||
| 95 | void CoreTiming::SyncPause(bool is_paused) { | 90 | void CoreTiming::SyncPause(bool is_paused) { |
| @@ -110,10 +105,6 @@ void CoreTiming::SyncPause(bool is_paused) { | |||
| 110 | if (!is_paused) { | 105 | if (!is_paused) { |
| 111 | pause_end_time = GetGlobalTimeNs().count(); | 106 | pause_end_time = GetGlobalTimeNs().count(); |
| 112 | } | 107 | } |
| 113 | |||
| 114 | for (auto& cb : pause_callbacks) { | ||
| 115 | cb(is_paused); | ||
| 116 | } | ||
| 117 | } | 108 | } |
| 118 | 109 | ||
| 119 | bool CoreTiming::IsRunning() const { | 110 | bool CoreTiming::IsRunning() const { |
| @@ -219,11 +210,6 @@ void CoreTiming::RemoveEvent(const std::shared_ptr<EventType>& event_type) { | |||
| 219 | } | 210 | } |
| 220 | } | 211 | } |
| 221 | 212 | ||
| 222 | void CoreTiming::RegisterPauseCallback(PauseCallback&& callback) { | ||
| 223 | std::scoped_lock lock{basic_lock}; | ||
| 224 | pause_callbacks.emplace_back(std::move(callback)); | ||
| 225 | } | ||
| 226 | |||
| 227 | std::optional<s64> CoreTiming::Advance() { | 213 | std::optional<s64> CoreTiming::Advance() { |
| 228 | std::scoped_lock lock{advance_lock, basic_lock}; | 214 | std::scoped_lock lock{advance_lock, basic_lock}; |
| 229 | global_timer = GetGlobalTimeNs().count(); | 215 | global_timer = GetGlobalTimeNs().count(); |
diff --git a/src/core/core_timing.h b/src/core/core_timing.h index 6aa3ae923..3259397b2 100644 --- a/src/core/core_timing.h +++ b/src/core/core_timing.h | |||
| @@ -22,7 +22,6 @@ namespace Core::Timing { | |||
| 22 | /// A callback that may be scheduled for a particular core timing event. | 22 | /// A callback that may be scheduled for a particular core timing event. |
| 23 | using TimedCallback = std::function<std::optional<std::chrono::nanoseconds>( | 23 | using TimedCallback = std::function<std::optional<std::chrono::nanoseconds>( |
| 24 | std::uintptr_t user_data, s64 time, std::chrono::nanoseconds ns_late)>; | 24 | std::uintptr_t user_data, s64 time, std::chrono::nanoseconds ns_late)>; |
| 25 | using PauseCallback = std::function<void(bool paused)>; | ||
| 26 | 25 | ||
| 27 | /// Contains the characteristics of a particular event. | 26 | /// Contains the characteristics of a particular event. |
| 28 | struct EventType { | 27 | struct EventType { |
| @@ -134,9 +133,6 @@ public: | |||
| 134 | /// Checks for events manually and returns time in nanoseconds for next event, threadsafe. | 133 | /// Checks for events manually and returns time in nanoseconds for next event, threadsafe. |
| 135 | std::optional<s64> Advance(); | 134 | std::optional<s64> Advance(); |
| 136 | 135 | ||
| 137 | /// Register a callback function to be called when coretiming pauses. | ||
| 138 | void RegisterPauseCallback(PauseCallback&& callback); | ||
| 139 | |||
| 140 | private: | 136 | private: |
| 141 | struct Event; | 137 | struct Event; |
| 142 | 138 | ||
| @@ -176,8 +172,6 @@ private: | |||
| 176 | /// Cycle timing | 172 | /// Cycle timing |
| 177 | u64 ticks{}; | 173 | u64 ticks{}; |
| 178 | s64 downcount{}; | 174 | s64 downcount{}; |
| 179 | |||
| 180 | std::vector<PauseCallback> pause_callbacks{}; | ||
| 181 | }; | 175 | }; |
| 182 | 176 | ||
| 183 | /// Creates a core timing event with the given name and callback. | 177 | /// Creates a core timing event with the given name and callback. |
diff --git a/src/core/debugger/debugger.cpp b/src/core/debugger/debugger.cpp index ac64d2f9d..e42bdd17d 100644 --- a/src/core/debugger/debugger.cpp +++ b/src/core/debugger/debugger.cpp | |||
| @@ -15,6 +15,7 @@ | |||
| 15 | #include "core/debugger/debugger_interface.h" | 15 | #include "core/debugger/debugger_interface.h" |
| 16 | #include "core/debugger/gdbstub.h" | 16 | #include "core/debugger/gdbstub.h" |
| 17 | #include "core/hle/kernel/global_scheduler_context.h" | 17 | #include "core/hle/kernel/global_scheduler_context.h" |
| 18 | #include "core/hle/kernel/k_scheduler.h" | ||
| 18 | 19 | ||
| 19 | template <typename Readable, typename Buffer, typename Callback> | 20 | template <typename Readable, typename Buffer, typename Callback> |
| 20 | static void AsyncReceiveInto(Readable& r, Buffer& buffer, Callback&& c) { | 21 | static void AsyncReceiveInto(Readable& r, Buffer& buffer, Callback&& c) { |
| @@ -230,13 +231,12 @@ private: | |||
| 230 | } | 231 | } |
| 231 | 232 | ||
| 232 | void PauseEmulation() { | 233 | void PauseEmulation() { |
| 234 | Kernel::KScopedSchedulerLock sl{system.Kernel()}; | ||
| 235 | |||
| 233 | // Put all threads to sleep on next scheduler round. | 236 | // Put all threads to sleep on next scheduler round. |
| 234 | for (auto* thread : ThreadList()) { | 237 | for (auto* thread : ThreadList()) { |
| 235 | thread->RequestSuspend(Kernel::SuspendType::Debug); | 238 | thread->RequestSuspend(Kernel::SuspendType::Debug); |
| 236 | } | 239 | } |
| 237 | |||
| 238 | // Signal an interrupt so that scheduler will fire. | ||
| 239 | system.Kernel().InterruptAllPhysicalCores(); | ||
| 240 | } | 240 | } |
| 241 | 241 | ||
| 242 | void ResumeEmulation(Kernel::KThread* except = nullptr) { | 242 | void ResumeEmulation(Kernel::KThread* except = nullptr) { |
| @@ -253,7 +253,8 @@ private: | |||
| 253 | 253 | ||
| 254 | template <typename Callback> | 254 | template <typename Callback> |
| 255 | void MarkResumed(Callback&& cb) { | 255 | void MarkResumed(Callback&& cb) { |
| 256 | std::scoped_lock lk{connection_lock}; | 256 | Kernel::KScopedSchedulerLock sl{system.Kernel()}; |
| 257 | std::scoped_lock cl{connection_lock}; | ||
| 257 | stopped = false; | 258 | stopped = false; |
| 258 | cb(); | 259 | cb(); |
| 259 | } | 260 | } |
diff --git a/src/core/file_sys/ips_layer.cpp b/src/core/file_sys/ips_layer.cpp index 4b35ca82f..5aab428bb 100644 --- a/src/core/file_sys/ips_layer.cpp +++ b/src/core/file_sys/ips_layer.cpp | |||
| @@ -217,9 +217,7 @@ void IPSwitchCompiler::Parse() { | |||
| 217 | break; | 217 | break; |
| 218 | } else if (StartsWith(line, "@nsobid-")) { | 218 | } else if (StartsWith(line, "@nsobid-")) { |
| 219 | // NSO Build ID Specifier | 219 | // NSO Build ID Specifier |
| 220 | auto raw_build_id = line.substr(8); | 220 | const auto raw_build_id = fmt::format("{:0<64}", line.substr(8)); |
| 221 | if (raw_build_id.size() != 0x40) | ||
| 222 | raw_build_id.resize(0x40, '0'); | ||
| 223 | nso_build_id = Common::HexStringToArray<0x20>(raw_build_id); | 221 | nso_build_id = Common::HexStringToArray<0x20>(raw_build_id); |
| 224 | } else if (StartsWith(line, "#")) { | 222 | } else if (StartsWith(line, "#")) { |
| 225 | // Mandatory Comment | 223 | // Mandatory Comment |
| @@ -287,7 +285,8 @@ void IPSwitchCompiler::Parse() { | |||
| 287 | std::copy(value.begin(), value.end(), std::back_inserter(replace)); | 285 | std::copy(value.begin(), value.end(), std::back_inserter(replace)); |
| 288 | } else { | 286 | } else { |
| 289 | // hex replacement | 287 | // hex replacement |
| 290 | const auto value = patch_line.substr(9); | 288 | const auto value = |
| 289 | patch_line.substr(9, patch_line.find_first_of(" /\r\n", 9) - 9); | ||
| 291 | replace = Common::HexStringToVector(value, is_little_endian); | 290 | replace = Common::HexStringToVector(value, is_little_endian); |
| 292 | } | 291 | } |
| 293 | 292 | ||
diff --git a/src/core/file_sys/patch_manager.cpp b/src/core/file_sys/patch_manager.cpp index bd525b26c..4c80e13a9 100644 --- a/src/core/file_sys/patch_manager.cpp +++ b/src/core/file_sys/patch_manager.cpp | |||
| @@ -191,6 +191,7 @@ VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const { | |||
| 191 | std::vector<VirtualFile> PatchManager::CollectPatches(const std::vector<VirtualDir>& patch_dirs, | 191 | std::vector<VirtualFile> PatchManager::CollectPatches(const std::vector<VirtualDir>& patch_dirs, |
| 192 | const std::string& build_id) const { | 192 | const std::string& build_id) const { |
| 193 | const auto& disabled = Settings::values.disabled_addons[title_id]; | 193 | const auto& disabled = Settings::values.disabled_addons[title_id]; |
| 194 | const auto nso_build_id = fmt::format("{:0<64}", build_id); | ||
| 194 | 195 | ||
| 195 | std::vector<VirtualFile> out; | 196 | std::vector<VirtualFile> out; |
| 196 | out.reserve(patch_dirs.size()); | 197 | out.reserve(patch_dirs.size()); |
| @@ -203,21 +204,18 @@ std::vector<VirtualFile> PatchManager::CollectPatches(const std::vector<VirtualD | |||
| 203 | for (const auto& file : exefs_dir->GetFiles()) { | 204 | for (const auto& file : exefs_dir->GetFiles()) { |
| 204 | if (file->GetExtension() == "ips") { | 205 | if (file->GetExtension() == "ips") { |
| 205 | auto name = file->GetName(); | 206 | auto name = file->GetName(); |
| 206 | const auto p1 = name.substr(0, name.find('.')); | ||
| 207 | const auto this_build_id = p1.substr(0, p1.find_last_not_of('0') + 1); | ||
| 208 | 207 | ||
| 209 | if (build_id == this_build_id) | 208 | const auto this_build_id = |
| 209 | fmt::format("{:0<64}", name.substr(0, name.find('.'))); | ||
| 210 | if (nso_build_id == this_build_id) | ||
| 210 | out.push_back(file); | 211 | out.push_back(file); |
| 211 | } else if (file->GetExtension() == "pchtxt") { | 212 | } else if (file->GetExtension() == "pchtxt") { |
| 212 | IPSwitchCompiler compiler{file}; | 213 | IPSwitchCompiler compiler{file}; |
| 213 | if (!compiler.IsValid()) | 214 | if (!compiler.IsValid()) |
| 214 | continue; | 215 | continue; |
| 215 | 216 | ||
| 216 | auto this_build_id = Common::HexToString(compiler.GetBuildID()); | 217 | const auto this_build_id = Common::HexToString(compiler.GetBuildID()); |
| 217 | this_build_id = | 218 | if (nso_build_id == this_build_id) |
| 218 | this_build_id.substr(0, this_build_id.find_last_not_of('0') + 1); | ||
| 219 | |||
| 220 | if (build_id == this_build_id) | ||
| 221 | out.push_back(file); | 219 | out.push_back(file); |
| 222 | } | 220 | } |
| 223 | } | 221 | } |
diff --git a/src/core/file_sys/system_archive/shared_font.cpp b/src/core/file_sys/system_archive/shared_font.cpp index f841988ff..3210583f0 100644 --- a/src/core/file_sys/system_archive/shared_font.cpp +++ b/src/core/file_sys/system_archive/shared_font.cpp | |||
| @@ -9,7 +9,7 @@ | |||
| 9 | #include "core/file_sys/system_archive/data/font_standard.h" | 9 | #include "core/file_sys/system_archive/data/font_standard.h" |
| 10 | #include "core/file_sys/system_archive/shared_font.h" | 10 | #include "core/file_sys/system_archive/shared_font.h" |
| 11 | #include "core/file_sys/vfs_vector.h" | 11 | #include "core/file_sys/vfs_vector.h" |
| 12 | #include "core/hle/service/ns/pl_u.h" | 12 | #include "core/hle/service/ns/iplatform_service_manager.h" |
| 13 | 13 | ||
| 14 | namespace FileSys::SystemArchive { | 14 | namespace FileSys::SystemArchive { |
| 15 | 15 | ||
diff --git a/src/core/hid/emulated_controller.cpp b/src/core/hid/emulated_controller.cpp index 8c3895937..01c43be93 100644 --- a/src/core/hid/emulated_controller.cpp +++ b/src/core/hid/emulated_controller.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/thread.h" | ||
| 4 | #include "core/hid/emulated_controller.h" | 5 | #include "core/hid/emulated_controller.h" |
| 5 | #include "core/hid/input_converter.h" | 6 | #include "core/hid/input_converter.h" |
| 6 | 7 | ||
| @@ -84,23 +85,26 @@ void EmulatedController::ReloadFromSettings() { | |||
| 84 | motion_params[index] = Common::ParamPackage(player.motions[index]); | 85 | motion_params[index] = Common::ParamPackage(player.motions[index]); |
| 85 | } | 86 | } |
| 86 | 87 | ||
| 88 | controller.colors_state.fullkey = { | ||
| 89 | .body = GetNpadColor(player.body_color_left), | ||
| 90 | .button = GetNpadColor(player.button_color_left), | ||
| 91 | }; | ||
| 87 | controller.colors_state.left = { | 92 | controller.colors_state.left = { |
| 88 | .body = player.body_color_left, | 93 | .body = GetNpadColor(player.body_color_left), |
| 89 | .button = player.button_color_left, | 94 | .button = GetNpadColor(player.button_color_left), |
| 90 | }; | 95 | }; |
| 91 | 96 | controller.colors_state.left = { | |
| 92 | controller.colors_state.right = { | 97 | .body = GetNpadColor(player.body_color_right), |
| 93 | .body = player.body_color_right, | 98 | .button = GetNpadColor(player.button_color_right), |
| 94 | .button = player.button_color_right, | ||
| 95 | }; | 99 | }; |
| 96 | 100 | ||
| 97 | controller.colors_state.fullkey = controller.colors_state.left; | ||
| 98 | |||
| 99 | // Other or debug controller should always be a pro controller | 101 | // Other or debug controller should always be a pro controller |
| 100 | if (npad_id_type != NpadIdType::Other) { | 102 | if (npad_id_type != NpadIdType::Other) { |
| 101 | SetNpadStyleIndex(MapSettingsTypeToNPad(player.controller_type)); | 103 | SetNpadStyleIndex(MapSettingsTypeToNPad(player.controller_type)); |
| 104 | original_npad_type = npad_type; | ||
| 102 | } else { | 105 | } else { |
| 103 | SetNpadStyleIndex(NpadStyleIndex::ProController); | 106 | SetNpadStyleIndex(NpadStyleIndex::ProController); |
| 107 | original_npad_type = npad_type; | ||
| 104 | } | 108 | } |
| 105 | 109 | ||
| 106 | if (player.connected) { | 110 | if (player.connected) { |
| @@ -352,6 +356,7 @@ void EmulatedController::DisableConfiguration() { | |||
| 352 | Disconnect(); | 356 | Disconnect(); |
| 353 | } | 357 | } |
| 354 | SetNpadStyleIndex(tmp_npad_type); | 358 | SetNpadStyleIndex(tmp_npad_type); |
| 359 | original_npad_type = tmp_npad_type; | ||
| 355 | } | 360 | } |
| 356 | 361 | ||
| 357 | // Apply temporary connected status to the real controller | 362 | // Apply temporary connected status to the real controller |
| @@ -557,6 +562,16 @@ void EmulatedController::SetButton(const Common::Input::CallbackStatus& callback | |||
| 557 | return; | 562 | return; |
| 558 | } | 563 | } |
| 559 | 564 | ||
| 565 | // GC controllers have triggers not buttons | ||
| 566 | if (npad_type == NpadStyleIndex::GameCube) { | ||
| 567 | if (index == Settings::NativeButton::ZR) { | ||
| 568 | return; | ||
| 569 | } | ||
| 570 | if (index == Settings::NativeButton::ZL) { | ||
| 571 | return; | ||
| 572 | } | ||
| 573 | } | ||
| 574 | |||
| 560 | switch (index) { | 575 | switch (index) { |
| 561 | case Settings::NativeButton::A: | 576 | case Settings::NativeButton::A: |
| 562 | controller.npad_button_state.a.Assign(current_status.value); | 577 | controller.npad_button_state.a.Assign(current_status.value); |
| @@ -733,6 +748,11 @@ void EmulatedController::SetTrigger(const Common::Input::CallbackStatus& callbac | |||
| 733 | return; | 748 | return; |
| 734 | } | 749 | } |
| 735 | 750 | ||
| 751 | // Only GC controllers have analog triggers | ||
| 752 | if (npad_type != NpadStyleIndex::GameCube) { | ||
| 753 | return; | ||
| 754 | } | ||
| 755 | |||
| 736 | const auto& trigger = controller.trigger_values[index]; | 756 | const auto& trigger = controller.trigger_values[index]; |
| 737 | 757 | ||
| 738 | switch (index) { | 758 | switch (index) { |
| @@ -949,6 +969,9 @@ bool EmulatedController::TestVibration(std::size_t device_index) { | |||
| 949 | // Send a slight vibration to test for rumble support | 969 | // Send a slight vibration to test for rumble support |
| 950 | output_devices[device_index]->SetVibration(test_vibration); | 970 | output_devices[device_index]->SetVibration(test_vibration); |
| 951 | 971 | ||
| 972 | // Wait for about 15ms to ensure the controller is ready for the stop command | ||
| 973 | std::this_thread::sleep_for(std::chrono::milliseconds(15)); | ||
| 974 | |||
| 952 | // Stop any vibration and return the result | 975 | // Stop any vibration and return the result |
| 953 | return output_devices[device_index]->SetVibration(zero_vibration) == | 976 | return output_devices[device_index]->SetVibration(zero_vibration) == |
| 954 | Common::Input::VibrationError::None; | 977 | Common::Input::VibrationError::None; |
| @@ -999,13 +1022,27 @@ void EmulatedController::SetSupportedNpadStyleTag(NpadStyleTag supported_styles) | |||
| 999 | if (!is_connected) { | 1022 | if (!is_connected) { |
| 1000 | return; | 1023 | return; |
| 1001 | } | 1024 | } |
| 1025 | |||
| 1026 | // Attempt to reconnect with the original type | ||
| 1027 | if (npad_type != original_npad_type) { | ||
| 1028 | Disconnect(); | ||
| 1029 | const auto current_npad_type = npad_type; | ||
| 1030 | SetNpadStyleIndex(original_npad_type); | ||
| 1031 | if (IsControllerSupported()) { | ||
| 1032 | Connect(); | ||
| 1033 | return; | ||
| 1034 | } | ||
| 1035 | SetNpadStyleIndex(current_npad_type); | ||
| 1036 | Connect(); | ||
| 1037 | } | ||
| 1038 | |||
| 1002 | if (IsControllerSupported()) { | 1039 | if (IsControllerSupported()) { |
| 1003 | return; | 1040 | return; |
| 1004 | } | 1041 | } |
| 1005 | 1042 | ||
| 1006 | Disconnect(); | 1043 | Disconnect(); |
| 1007 | 1044 | ||
| 1008 | // Fallback fullkey controllers to Pro controllers | 1045 | // Fallback Fullkey controllers to Pro controllers |
| 1009 | if (IsControllerFullkey() && supported_style_tag.fullkey) { | 1046 | if (IsControllerFullkey() && supported_style_tag.fullkey) { |
| 1010 | LOG_WARNING(Service_HID, "Reconnecting controller type {} as Pro controller", npad_type); | 1047 | LOG_WARNING(Service_HID, "Reconnecting controller type {} as Pro controller", npad_type); |
| 1011 | SetNpadStyleIndex(NpadStyleIndex::ProController); | 1048 | SetNpadStyleIndex(NpadStyleIndex::ProController); |
| @@ -1013,6 +1050,22 @@ void EmulatedController::SetSupportedNpadStyleTag(NpadStyleTag supported_styles) | |||
| 1013 | return; | 1050 | return; |
| 1014 | } | 1051 | } |
| 1015 | 1052 | ||
| 1053 | // Fallback Dual joycon controllers to Pro controllers | ||
| 1054 | if (npad_type == NpadStyleIndex::JoyconDual && supported_style_tag.fullkey) { | ||
| 1055 | LOG_WARNING(Service_HID, "Reconnecting controller type {} as Pro controller", npad_type); | ||
| 1056 | SetNpadStyleIndex(NpadStyleIndex::ProController); | ||
| 1057 | Connect(); | ||
| 1058 | return; | ||
| 1059 | } | ||
| 1060 | |||
| 1061 | // Fallback Pro controllers to Dual joycon | ||
| 1062 | if (npad_type == NpadStyleIndex::ProController && supported_style_tag.joycon_dual) { | ||
| 1063 | LOG_WARNING(Service_HID, "Reconnecting controller type {} as Dual Joycons", npad_type); | ||
| 1064 | SetNpadStyleIndex(NpadStyleIndex::JoyconDual); | ||
| 1065 | Connect(); | ||
| 1066 | return; | ||
| 1067 | } | ||
| 1068 | |||
| 1016 | LOG_ERROR(Service_HID, "Controller type {} is not supported. Disconnecting controller", | 1069 | LOG_ERROR(Service_HID, "Controller type {} is not supported. Disconnecting controller", |
| 1017 | npad_type); | 1070 | npad_type); |
| 1018 | } | 1071 | } |
| @@ -1310,6 +1363,15 @@ const CameraState& EmulatedController::GetCamera() const { | |||
| 1310 | return controller.camera_state; | 1363 | return controller.camera_state; |
| 1311 | } | 1364 | } |
| 1312 | 1365 | ||
| 1366 | NpadColor EmulatedController::GetNpadColor(u32 color) { | ||
| 1367 | return { | ||
| 1368 | .r = static_cast<u8>((color >> 16) & 0xFF), | ||
| 1369 | .g = static_cast<u8>((color >> 8) & 0xFF), | ||
| 1370 | .b = static_cast<u8>(color & 0xFF), | ||
| 1371 | .a = 0xff, | ||
| 1372 | }; | ||
| 1373 | } | ||
| 1374 | |||
| 1313 | void EmulatedController::TriggerOnChange(ControllerTriggerType type, bool is_npad_service_update) { | 1375 | void EmulatedController::TriggerOnChange(ControllerTriggerType type, bool is_npad_service_update) { |
| 1314 | std::scoped_lock lock{callback_mutex}; | 1376 | std::scoped_lock lock{callback_mutex}; |
| 1315 | for (const auto& poller_pair : callback_list) { | 1377 | for (const auto& poller_pair : callback_list) { |
diff --git a/src/core/hid/emulated_controller.h b/src/core/hid/emulated_controller.h index 823c1700c..c3aa8f9d3 100644 --- a/src/core/hid/emulated_controller.h +++ b/src/core/hid/emulated_controller.h | |||
| @@ -425,6 +425,13 @@ private: | |||
| 425 | void SetCamera(const Common::Input::CallbackStatus& callback); | 425 | void SetCamera(const Common::Input::CallbackStatus& callback); |
| 426 | 426 | ||
| 427 | /** | 427 | /** |
| 428 | * Converts a color format from bgra to rgba | ||
| 429 | * @param color in bgra format | ||
| 430 | * @return NpadColor in rgba format | ||
| 431 | */ | ||
| 432 | NpadColor GetNpadColor(u32 color); | ||
| 433 | |||
| 434 | /** | ||
| 428 | * Triggers a callback that something has changed on the controller status | 435 | * Triggers a callback that something has changed on the controller status |
| 429 | * @param type Input type of the event to trigger | 436 | * @param type Input type of the event to trigger |
| 430 | * @param is_service_update indicates if this event should only be sent to HID services | 437 | * @param is_service_update indicates if this event should only be sent to HID services |
| @@ -433,6 +440,7 @@ private: | |||
| 433 | 440 | ||
| 434 | const NpadIdType npad_id_type; | 441 | const NpadIdType npad_id_type; |
| 435 | NpadStyleIndex npad_type{NpadStyleIndex::None}; | 442 | NpadStyleIndex npad_type{NpadStyleIndex::None}; |
| 443 | NpadStyleIndex original_npad_type{NpadStyleIndex::None}; | ||
| 436 | NpadStyleTag supported_style_tag{NpadStyleSet::All}; | 444 | NpadStyleTag supported_style_tag{NpadStyleSet::All}; |
| 437 | bool is_connected{false}; | 445 | bool is_connected{false}; |
| 438 | bool is_configuring{false}; | 446 | bool is_configuring{false}; |
diff --git a/src/core/hid/hid_types.h b/src/core/hid/hid_types.h index e49223016..e3b1cfbc6 100644 --- a/src/core/hid/hid_types.h +++ b/src/core/hid/hid_types.h | |||
| @@ -327,10 +327,18 @@ struct TouchState { | |||
| 327 | }; | 327 | }; |
| 328 | static_assert(sizeof(TouchState) == 0x28, "Touchstate is an invalid size"); | 328 | static_assert(sizeof(TouchState) == 0x28, "Touchstate is an invalid size"); |
| 329 | 329 | ||
| 330 | struct NpadColor { | ||
| 331 | u8 r{}; | ||
| 332 | u8 g{}; | ||
| 333 | u8 b{}; | ||
| 334 | u8 a{}; | ||
| 335 | }; | ||
| 336 | static_assert(sizeof(NpadColor) == 4, "NpadColor is an invalid size"); | ||
| 337 | |||
| 330 | // This is nn::hid::NpadControllerColor | 338 | // This is nn::hid::NpadControllerColor |
| 331 | struct NpadControllerColor { | 339 | struct NpadControllerColor { |
| 332 | u32 body{}; | 340 | NpadColor body{}; |
| 333 | u32 button{}; | 341 | NpadColor button{}; |
| 334 | }; | 342 | }; |
| 335 | static_assert(sizeof(NpadControllerColor) == 8, "NpadControllerColor is an invalid size"); | 343 | static_assert(sizeof(NpadControllerColor) == 8, "NpadControllerColor is an invalid size"); |
| 336 | 344 | ||
diff --git a/src/core/hid/input_converter.cpp b/src/core/hid/input_converter.cpp index 68d143a01..52fb69e9c 100644 --- a/src/core/hid/input_converter.cpp +++ b/src/core/hid/input_converter.cpp | |||
| @@ -52,6 +52,9 @@ Common::Input::ButtonStatus TransformToButton(const Common::Input::CallbackStatu | |||
| 52 | Common::Input::ButtonStatus status{}; | 52 | Common::Input::ButtonStatus status{}; |
| 53 | switch (callback.type) { | 53 | switch (callback.type) { |
| 54 | case Common::Input::InputType::Analog: | 54 | case Common::Input::InputType::Analog: |
| 55 | status.value = TransformToTrigger(callback).pressed.value; | ||
| 56 | status.toggle = callback.analog_status.properties.toggle; | ||
| 57 | break; | ||
| 55 | case Common::Input::InputType::Trigger: | 58 | case Common::Input::InputType::Trigger: |
| 56 | status.value = TransformToTrigger(callback).pressed.value; | 59 | status.value = TransformToTrigger(callback).pressed.value; |
| 57 | break; | 60 | break; |
diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp index f4072e1c3..ce7fa8275 100644 --- a/src/core/hle/kernel/kernel.cpp +++ b/src/core/hle/kernel/kernel.cpp | |||
| @@ -17,7 +17,6 @@ | |||
| 17 | #include "common/thread.h" | 17 | #include "common/thread.h" |
| 18 | #include "common/thread_worker.h" | 18 | #include "common/thread_worker.h" |
| 19 | #include "core/arm/arm_interface.h" | 19 | #include "core/arm/arm_interface.h" |
| 20 | #include "core/arm/cpu_interrupt_handler.h" | ||
| 21 | #include "core/arm/exclusive_monitor.h" | 20 | #include "core/arm/exclusive_monitor.h" |
| 22 | #include "core/core.h" | 21 | #include "core/core.h" |
| 23 | #include "core/core_timing.h" | 22 | #include "core/core_timing.h" |
| @@ -82,7 +81,7 @@ struct KernelCore::Impl { | |||
| 82 | 81 | ||
| 83 | void InitializeCores() { | 82 | void InitializeCores() { |
| 84 | for (u32 core_id = 0; core_id < Core::Hardware::NUM_CPU_CORES; core_id++) { | 83 | for (u32 core_id = 0; core_id < Core::Hardware::NUM_CPU_CORES; core_id++) { |
| 85 | cores[core_id].Initialize((*current_process).Is64BitProcess()); | 84 | cores[core_id]->Initialize((*current_process).Is64BitProcess()); |
| 86 | system.Memory().SetCurrentPageTable(*current_process, core_id); | 85 | system.Memory().SetCurrentPageTable(*current_process, core_id); |
| 87 | } | 86 | } |
| 88 | } | 87 | } |
| @@ -100,7 +99,9 @@ struct KernelCore::Impl { | |||
| 100 | next_user_process_id = KProcess::ProcessIDMin; | 99 | next_user_process_id = KProcess::ProcessIDMin; |
| 101 | next_thread_id = 1; | 100 | next_thread_id = 1; |
| 102 | 101 | ||
| 103 | cores.clear(); | 102 | for (auto& core : cores) { |
| 103 | core = nullptr; | ||
| 104 | } | ||
| 104 | 105 | ||
| 105 | global_handle_table->Finalize(); | 106 | global_handle_table->Finalize(); |
| 106 | global_handle_table.reset(); | 107 | global_handle_table.reset(); |
| @@ -199,7 +200,7 @@ struct KernelCore::Impl { | |||
| 199 | const s32 core{static_cast<s32>(i)}; | 200 | const s32 core{static_cast<s32>(i)}; |
| 200 | 201 | ||
| 201 | schedulers[i] = std::make_unique<Kernel::KScheduler>(system.Kernel()); | 202 | schedulers[i] = std::make_unique<Kernel::KScheduler>(system.Kernel()); |
| 202 | cores.emplace_back(i, system, *schedulers[i], interrupts); | 203 | cores[i] = std::make_unique<Kernel::PhysicalCore>(i, system, *schedulers[i]); |
| 203 | 204 | ||
| 204 | auto* main_thread{Kernel::KThread::Create(system.Kernel())}; | 205 | auto* main_thread{Kernel::KThread::Create(system.Kernel())}; |
| 205 | main_thread->SetName(fmt::format("MainThread:{}", core)); | 206 | main_thread->SetName(fmt::format("MainThread:{}", core)); |
| @@ -761,7 +762,7 @@ struct KernelCore::Impl { | |||
| 761 | std::unordered_set<KAutoObject*> registered_in_use_objects; | 762 | std::unordered_set<KAutoObject*> registered_in_use_objects; |
| 762 | 763 | ||
| 763 | std::unique_ptr<Core::ExclusiveMonitor> exclusive_monitor; | 764 | std::unique_ptr<Core::ExclusiveMonitor> exclusive_monitor; |
| 764 | std::vector<Kernel::PhysicalCore> cores; | 765 | std::array<std::unique_ptr<Kernel::PhysicalCore>, Core::Hardware::NUM_CPU_CORES> cores; |
| 765 | 766 | ||
| 766 | // Next host thead ID to use, 0-3 IDs represent core threads, >3 represent others | 767 | // Next host thead ID to use, 0-3 IDs represent core threads, >3 represent others |
| 767 | std::atomic<u32> next_host_thread_id{Core::Hardware::NUM_CPU_CORES}; | 768 | std::atomic<u32> next_host_thread_id{Core::Hardware::NUM_CPU_CORES}; |
| @@ -785,7 +786,6 @@ struct KernelCore::Impl { | |||
| 785 | Common::ThreadWorker service_threads_manager; | 786 | Common::ThreadWorker service_threads_manager; |
| 786 | 787 | ||
| 787 | std::array<KThread*, Core::Hardware::NUM_CPU_CORES> shutdown_threads; | 788 | std::array<KThread*, Core::Hardware::NUM_CPU_CORES> shutdown_threads; |
| 788 | std::array<Core::CPUInterruptHandler, Core::Hardware::NUM_CPU_CORES> interrupts{}; | ||
| 789 | std::array<std::unique_ptr<Kernel::KScheduler>, Core::Hardware::NUM_CPU_CORES> schedulers{}; | 789 | std::array<std::unique_ptr<Kernel::KScheduler>, Core::Hardware::NUM_CPU_CORES> schedulers{}; |
| 790 | 790 | ||
| 791 | bool is_multicore{}; | 791 | bool is_multicore{}; |
| @@ -874,11 +874,11 @@ const Kernel::KScheduler& KernelCore::Scheduler(std::size_t id) const { | |||
| 874 | } | 874 | } |
| 875 | 875 | ||
| 876 | Kernel::PhysicalCore& KernelCore::PhysicalCore(std::size_t id) { | 876 | Kernel::PhysicalCore& KernelCore::PhysicalCore(std::size_t id) { |
| 877 | return impl->cores[id]; | 877 | return *impl->cores[id]; |
| 878 | } | 878 | } |
| 879 | 879 | ||
| 880 | const Kernel::PhysicalCore& KernelCore::PhysicalCore(std::size_t id) const { | 880 | const Kernel::PhysicalCore& KernelCore::PhysicalCore(std::size_t id) const { |
| 881 | return impl->cores[id]; | 881 | return *impl->cores[id]; |
| 882 | } | 882 | } |
| 883 | 883 | ||
| 884 | size_t KernelCore::CurrentPhysicalCoreIndex() const { | 884 | size_t KernelCore::CurrentPhysicalCoreIndex() const { |
| @@ -890,11 +890,11 @@ size_t KernelCore::CurrentPhysicalCoreIndex() const { | |||
| 890 | } | 890 | } |
| 891 | 891 | ||
| 892 | Kernel::PhysicalCore& KernelCore::CurrentPhysicalCore() { | 892 | Kernel::PhysicalCore& KernelCore::CurrentPhysicalCore() { |
| 893 | return impl->cores[CurrentPhysicalCoreIndex()]; | 893 | return *impl->cores[CurrentPhysicalCoreIndex()]; |
| 894 | } | 894 | } |
| 895 | 895 | ||
| 896 | const Kernel::PhysicalCore& KernelCore::CurrentPhysicalCore() const { | 896 | const Kernel::PhysicalCore& KernelCore::CurrentPhysicalCore() const { |
| 897 | return impl->cores[CurrentPhysicalCoreIndex()]; | 897 | return *impl->cores[CurrentPhysicalCoreIndex()]; |
| 898 | } | 898 | } |
| 899 | 899 | ||
| 900 | Kernel::KScheduler* KernelCore::CurrentScheduler() { | 900 | Kernel::KScheduler* KernelCore::CurrentScheduler() { |
| @@ -906,15 +906,6 @@ Kernel::KScheduler* KernelCore::CurrentScheduler() { | |||
| 906 | return impl->schedulers[core_id].get(); | 906 | return impl->schedulers[core_id].get(); |
| 907 | } | 907 | } |
| 908 | 908 | ||
| 909 | std::array<Core::CPUInterruptHandler, Core::Hardware::NUM_CPU_CORES>& KernelCore::Interrupts() { | ||
| 910 | return impl->interrupts; | ||
| 911 | } | ||
| 912 | |||
| 913 | const std::array<Core::CPUInterruptHandler, Core::Hardware::NUM_CPU_CORES>& KernelCore::Interrupts() | ||
| 914 | const { | ||
| 915 | return impl->interrupts; | ||
| 916 | } | ||
| 917 | |||
| 918 | Kernel::TimeManager& KernelCore::TimeManager() { | 909 | Kernel::TimeManager& KernelCore::TimeManager() { |
| 919 | return impl->time_manager; | 910 | return impl->time_manager; |
| 920 | } | 911 | } |
| @@ -939,24 +930,18 @@ const KAutoObjectWithListContainer& KernelCore::ObjectListContainer() const { | |||
| 939 | return *impl->global_object_list_container; | 930 | return *impl->global_object_list_container; |
| 940 | } | 931 | } |
| 941 | 932 | ||
| 942 | void KernelCore::InterruptAllPhysicalCores() { | ||
| 943 | for (auto& physical_core : impl->cores) { | ||
| 944 | physical_core.Interrupt(); | ||
| 945 | } | ||
| 946 | } | ||
| 947 | |||
| 948 | void KernelCore::InvalidateAllInstructionCaches() { | 933 | void KernelCore::InvalidateAllInstructionCaches() { |
| 949 | for (auto& physical_core : impl->cores) { | 934 | for (auto& physical_core : impl->cores) { |
| 950 | physical_core.ArmInterface().ClearInstructionCache(); | 935 | physical_core->ArmInterface().ClearInstructionCache(); |
| 951 | } | 936 | } |
| 952 | } | 937 | } |
| 953 | 938 | ||
| 954 | void KernelCore::InvalidateCpuInstructionCacheRange(VAddr addr, std::size_t size) { | 939 | void KernelCore::InvalidateCpuInstructionCacheRange(VAddr addr, std::size_t size) { |
| 955 | for (auto& physical_core : impl->cores) { | 940 | for (auto& physical_core : impl->cores) { |
| 956 | if (!physical_core.IsInitialized()) { | 941 | if (!physical_core->IsInitialized()) { |
| 957 | continue; | 942 | continue; |
| 958 | } | 943 | } |
| 959 | physical_core.ArmInterface().InvalidateCacheRange(addr, size); | 944 | physical_core->ArmInterface().InvalidateCacheRange(addr, size); |
| 960 | } | 945 | } |
| 961 | } | 946 | } |
| 962 | 947 | ||
diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h index 6c7cf6af2..bcf016a97 100644 --- a/src/core/hle/kernel/kernel.h +++ b/src/core/hle/kernel/kernel.h | |||
| @@ -9,14 +9,12 @@ | |||
| 9 | #include <string> | 9 | #include <string> |
| 10 | #include <unordered_map> | 10 | #include <unordered_map> |
| 11 | #include <vector> | 11 | #include <vector> |
| 12 | #include "core/arm/cpu_interrupt_handler.h" | ||
| 13 | #include "core/hardware_properties.h" | 12 | #include "core/hardware_properties.h" |
| 14 | #include "core/hle/kernel/k_auto_object.h" | 13 | #include "core/hle/kernel/k_auto_object.h" |
| 15 | #include "core/hle/kernel/k_slab_heap.h" | 14 | #include "core/hle/kernel/k_slab_heap.h" |
| 16 | #include "core/hle/kernel/svc_common.h" | 15 | #include "core/hle/kernel/svc_common.h" |
| 17 | 16 | ||
| 18 | namespace Core { | 17 | namespace Core { |
| 19 | class CPUInterruptHandler; | ||
| 20 | class ExclusiveMonitor; | 18 | class ExclusiveMonitor; |
| 21 | class System; | 19 | class System; |
| 22 | } // namespace Core | 20 | } // namespace Core |
| @@ -183,12 +181,6 @@ public: | |||
| 183 | 181 | ||
| 184 | const KAutoObjectWithListContainer& ObjectListContainer() const; | 182 | const KAutoObjectWithListContainer& ObjectListContainer() const; |
| 185 | 183 | ||
| 186 | std::array<Core::CPUInterruptHandler, Core::Hardware::NUM_CPU_CORES>& Interrupts(); | ||
| 187 | |||
| 188 | const std::array<Core::CPUInterruptHandler, Core::Hardware::NUM_CPU_CORES>& Interrupts() const; | ||
| 189 | |||
| 190 | void InterruptAllPhysicalCores(); | ||
| 191 | |||
| 192 | void InvalidateAllInstructionCaches(); | 184 | void InvalidateAllInstructionCaches(); |
| 193 | 185 | ||
| 194 | void InvalidateCpuInstructionCacheRange(VAddr addr, std::size_t size); | 186 | void InvalidateCpuInstructionCacheRange(VAddr addr, std::size_t size); |
diff --git a/src/core/hle/kernel/physical_core.cpp b/src/core/hle/kernel/physical_core.cpp index 6e7dacf97..d4375962f 100644 --- a/src/core/hle/kernel/physical_core.cpp +++ b/src/core/hle/kernel/physical_core.cpp | |||
| @@ -1,7 +1,6 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project | 1 | // SPDX-FileCopyrightText: Copyright 2020 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 "core/arm/cpu_interrupt_handler.h" | ||
| 5 | #include "core/arm/dynarmic/arm_dynarmic_32.h" | 4 | #include "core/arm/dynarmic/arm_dynarmic_32.h" |
| 6 | #include "core/arm/dynarmic/arm_dynarmic_64.h" | 5 | #include "core/arm/dynarmic/arm_dynarmic_64.h" |
| 7 | #include "core/core.h" | 6 | #include "core/core.h" |
| @@ -11,16 +10,14 @@ | |||
| 11 | 10 | ||
| 12 | namespace Kernel { | 11 | namespace Kernel { |
| 13 | 12 | ||
| 14 | PhysicalCore::PhysicalCore(std::size_t core_index_, Core::System& system_, KScheduler& scheduler_, | 13 | PhysicalCore::PhysicalCore(std::size_t core_index_, Core::System& system_, KScheduler& scheduler_) |
| 15 | Core::CPUInterrupts& interrupts_) | 14 | : core_index{core_index_}, system{system_}, scheduler{scheduler_} { |
| 16 | : core_index{core_index_}, system{system_}, scheduler{scheduler_}, | ||
| 17 | interrupts{interrupts_}, guard{std::make_unique<std::mutex>()} { | ||
| 18 | #ifdef ARCHITECTURE_x86_64 | 15 | #ifdef ARCHITECTURE_x86_64 |
| 19 | // TODO(bunnei): Initialization relies on a core being available. We may later replace this with | 16 | // TODO(bunnei): Initialization relies on a core being available. We may later replace this with |
| 20 | // a 32-bit instance of Dynarmic. This should be abstracted out to a CPU manager. | 17 | // a 32-bit instance of Dynarmic. This should be abstracted out to a CPU manager. |
| 21 | auto& kernel = system.Kernel(); | 18 | auto& kernel = system.Kernel(); |
| 22 | arm_interface = std::make_unique<Core::ARM_Dynarmic_64>( | 19 | arm_interface = std::make_unique<Core::ARM_Dynarmic_64>( |
| 23 | system, interrupts, kernel.IsMulticore(), kernel.GetExclusiveMonitor(), core_index); | 20 | system, kernel.IsMulticore(), kernel.GetExclusiveMonitor(), core_index); |
| 24 | #else | 21 | #else |
| 25 | #error Platform not supported yet. | 22 | #error Platform not supported yet. |
| 26 | #endif | 23 | #endif |
| @@ -34,7 +31,7 @@ void PhysicalCore::Initialize([[maybe_unused]] bool is_64_bit) { | |||
| 34 | if (!is_64_bit) { | 31 | if (!is_64_bit) { |
| 35 | // We already initialized a 64-bit core, replace with a 32-bit one. | 32 | // We already initialized a 64-bit core, replace with a 32-bit one. |
| 36 | arm_interface = std::make_unique<Core::ARM_Dynarmic_32>( | 33 | arm_interface = std::make_unique<Core::ARM_Dynarmic_32>( |
| 37 | system, interrupts, kernel.IsMulticore(), kernel.GetExclusiveMonitor(), core_index); | 34 | system, kernel.IsMulticore(), kernel.GetExclusiveMonitor(), core_index); |
| 38 | } | 35 | } |
| 39 | #else | 36 | #else |
| 40 | #error Platform not supported yet. | 37 | #error Platform not supported yet. |
| @@ -47,24 +44,26 @@ void PhysicalCore::Run() { | |||
| 47 | } | 44 | } |
| 48 | 45 | ||
| 49 | void PhysicalCore::Idle() { | 46 | void PhysicalCore::Idle() { |
| 50 | interrupts[core_index].AwaitInterrupt(); | 47 | std::unique_lock lk{guard}; |
| 48 | on_interrupt.wait(lk, [this] { return is_interrupted; }); | ||
| 51 | } | 49 | } |
| 52 | 50 | ||
| 53 | bool PhysicalCore::IsInterrupted() const { | 51 | bool PhysicalCore::IsInterrupted() const { |
| 54 | return interrupts[core_index].IsInterrupted(); | 52 | return is_interrupted; |
| 55 | } | 53 | } |
| 56 | 54 | ||
| 57 | void PhysicalCore::Interrupt() { | 55 | void PhysicalCore::Interrupt() { |
| 58 | guard->lock(); | 56 | std::unique_lock lk{guard}; |
| 59 | interrupts[core_index].SetInterrupt(true); | 57 | is_interrupted = true; |
| 60 | arm_interface->SignalInterrupt(); | 58 | arm_interface->SignalInterrupt(); |
| 61 | guard->unlock(); | 59 | on_interrupt.notify_all(); |
| 62 | } | 60 | } |
| 63 | 61 | ||
| 64 | void PhysicalCore::ClearInterrupt() { | 62 | void PhysicalCore::ClearInterrupt() { |
| 65 | guard->lock(); | 63 | std::unique_lock lk{guard}; |
| 66 | interrupts[core_index].SetInterrupt(false); | 64 | is_interrupted = false; |
| 67 | guard->unlock(); | 65 | arm_interface->ClearInterrupt(); |
| 66 | on_interrupt.notify_all(); | ||
| 68 | } | 67 | } |
| 69 | 68 | ||
| 70 | } // namespace Kernel | 69 | } // namespace Kernel |
diff --git a/src/core/hle/kernel/physical_core.h b/src/core/hle/kernel/physical_core.h index 898d1e5db..2fc8d4be2 100644 --- a/src/core/hle/kernel/physical_core.h +++ b/src/core/hle/kernel/physical_core.h | |||
| @@ -14,7 +14,6 @@ class KScheduler; | |||
| 14 | } // namespace Kernel | 14 | } // namespace Kernel |
| 15 | 15 | ||
| 16 | namespace Core { | 16 | namespace Core { |
| 17 | class CPUInterruptHandler; | ||
| 18 | class ExclusiveMonitor; | 17 | class ExclusiveMonitor; |
| 19 | class System; | 18 | class System; |
| 20 | } // namespace Core | 19 | } // namespace Core |
| @@ -23,15 +22,11 @@ namespace Kernel { | |||
| 23 | 22 | ||
| 24 | class PhysicalCore { | 23 | class PhysicalCore { |
| 25 | public: | 24 | public: |
| 26 | PhysicalCore(std::size_t core_index_, Core::System& system_, KScheduler& scheduler_, | 25 | PhysicalCore(std::size_t core_index_, Core::System& system_, KScheduler& scheduler_); |
| 27 | Core::CPUInterrupts& interrupts_); | ||
| 28 | ~PhysicalCore(); | 26 | ~PhysicalCore(); |
| 29 | 27 | ||
| 30 | PhysicalCore(const PhysicalCore&) = delete; | 28 | YUZU_NON_COPYABLE(PhysicalCore); |
| 31 | PhysicalCore& operator=(const PhysicalCore&) = delete; | 29 | YUZU_NON_MOVEABLE(PhysicalCore); |
| 32 | |||
| 33 | PhysicalCore(PhysicalCore&&) = default; | ||
| 34 | PhysicalCore& operator=(PhysicalCore&&) = delete; | ||
| 35 | 30 | ||
| 36 | /// Initialize the core for the specified parameters. | 31 | /// Initialize the core for the specified parameters. |
| 37 | void Initialize(bool is_64_bit); | 32 | void Initialize(bool is_64_bit); |
| @@ -86,9 +81,11 @@ private: | |||
| 86 | const std::size_t core_index; | 81 | const std::size_t core_index; |
| 87 | Core::System& system; | 82 | Core::System& system; |
| 88 | Kernel::KScheduler& scheduler; | 83 | Kernel::KScheduler& scheduler; |
| 89 | Core::CPUInterrupts& interrupts; | 84 | |
| 90 | std::unique_ptr<std::mutex> guard; | 85 | std::mutex guard; |
| 86 | std::condition_variable on_interrupt; | ||
| 91 | std::unique_ptr<Core::ARM_Interface> arm_interface; | 87 | std::unique_ptr<Core::ARM_Interface> arm_interface; |
| 88 | bool is_interrupted; | ||
| 92 | }; | 89 | }; |
| 93 | 90 | ||
| 94 | } // namespace Kernel | 91 | } // namespace Kernel |
diff --git a/src/core/hle/result.h b/src/core/hle/result.h index 4de44cd06..47a1b829b 100644 --- a/src/core/hle/result.h +++ b/src/core/hle/result.h | |||
| @@ -117,6 +117,7 @@ union Result { | |||
| 117 | BitField<0, 9, ErrorModule> module; | 117 | BitField<0, 9, ErrorModule> module; |
| 118 | BitField<9, 13, u32> description; | 118 | BitField<9, 13, u32> description; |
| 119 | 119 | ||
| 120 | Result() = default; | ||
| 120 | constexpr explicit Result(u32 raw_) : raw(raw_) {} | 121 | constexpr explicit Result(u32 raw_) : raw(raw_) {} |
| 121 | 122 | ||
| 122 | constexpr Result(ErrorModule module_, u32 description_) | 123 | constexpr Result(ErrorModule module_, u32 description_) |
| @@ -130,6 +131,7 @@ union Result { | |||
| 130 | return !IsSuccess(); | 131 | return !IsSuccess(); |
| 131 | } | 132 | } |
| 132 | }; | 133 | }; |
| 134 | static_assert(std::is_trivial_v<Result>); | ||
| 133 | 135 | ||
| 134 | [[nodiscard]] constexpr bool operator==(const Result& a, const Result& b) { | 136 | [[nodiscard]] constexpr bool operator==(const Result& a, const Result& b) { |
| 135 | return a.raw == b.raw; | 137 | return a.raw == b.raw; |
diff --git a/src/core/hle/service/acc/acc.cpp b/src/core/hle/service/acc/acc.cpp index def105832..bb838e285 100644 --- a/src/core/hle/service/acc/acc.cpp +++ b/src/core/hle/service/acc/acc.cpp | |||
| @@ -534,7 +534,7 @@ public: | |||
| 534 | 534 | ||
| 535 | private: | 535 | private: |
| 536 | void CheckAvailability(Kernel::HLERequestContext& ctx) { | 536 | void CheckAvailability(Kernel::HLERequestContext& ctx) { |
| 537 | LOG_WARNING(Service_ACC, "(STUBBED) called"); | 537 | LOG_DEBUG(Service_ACC, "(STUBBED) called"); |
| 538 | IPC::ResponseBuilder rb{ctx, 3}; | 538 | IPC::ResponseBuilder rb{ctx, 3}; |
| 539 | rb.Push(ResultSuccess); | 539 | rb.Push(ResultSuccess); |
| 540 | rb.Push(false); // TODO: Check when this is supposed to return true and when not | 540 | rb.Push(false); // TODO: Check when this is supposed to return true and when not |
diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp index 118f226e4..6fb7e198e 100644 --- a/src/core/hle/service/am/am.cpp +++ b/src/core/hle/service/am/am.cpp | |||
| @@ -754,7 +754,7 @@ void ICommonStateGetter::ReceiveMessage(Kernel::HLERequestContext& ctx) { | |||
| 754 | } | 754 | } |
| 755 | 755 | ||
| 756 | void ICommonStateGetter::GetCurrentFocusState(Kernel::HLERequestContext& ctx) { | 756 | void ICommonStateGetter::GetCurrentFocusState(Kernel::HLERequestContext& ctx) { |
| 757 | LOG_WARNING(Service_AM, "(STUBBED) called"); | 757 | LOG_DEBUG(Service_AM, "(STUBBED) called"); |
| 758 | 758 | ||
| 759 | IPC::ResponseBuilder rb{ctx, 3}; | 759 | IPC::ResponseBuilder rb{ctx, 3}; |
| 760 | rb.Push(ResultSuccess); | 760 | rb.Push(ResultSuccess); |
diff --git a/src/core/hle/service/am/applets/applet_web_browser.cpp b/src/core/hle/service/am/applets/applet_web_browser.cpp index 4b804b78c..14aa6f69e 100644 --- a/src/core/hle/service/am/applets/applet_web_browser.cpp +++ b/src/core/hle/service/am/applets/applet_web_browser.cpp | |||
| @@ -21,7 +21,7 @@ | |||
| 21 | #include "core/hle/service/am/am.h" | 21 | #include "core/hle/service/am/am.h" |
| 22 | #include "core/hle/service/am/applets/applet_web_browser.h" | 22 | #include "core/hle/service/am/applets/applet_web_browser.h" |
| 23 | #include "core/hle/service/filesystem/filesystem.h" | 23 | #include "core/hle/service/filesystem/filesystem.h" |
| 24 | #include "core/hle/service/ns/pl_u.h" | 24 | #include "core/hle/service/ns/iplatform_service_manager.h" |
| 25 | #include "core/loader/loader.h" | 25 | #include "core/loader/loader.h" |
| 26 | 26 | ||
| 27 | namespace Service::AM::Applets { | 27 | namespace Service::AM::Applets { |
diff --git a/src/core/hle/service/apm/apm_controller.cpp b/src/core/hle/service/apm/apm_controller.cpp index 4e710491b..d6de84066 100644 --- a/src/core/hle/service/apm/apm_controller.cpp +++ b/src/core/hle/service/apm/apm_controller.cpp | |||
| @@ -80,7 +80,7 @@ PerformanceConfiguration Controller::GetCurrentPerformanceConfiguration(Performa | |||
| 80 | } | 80 | } |
| 81 | 81 | ||
| 82 | void Controller::SetClockSpeed(u32 mhz) { | 82 | void Controller::SetClockSpeed(u32 mhz) { |
| 83 | LOG_INFO(Service_APM, "called, mhz={:08X}", mhz); | 83 | LOG_DEBUG(Service_APM, "called, mhz={:08X}", mhz); |
| 84 | // TODO(DarkLordZach): Actually signal core_timing to change clock speed. | 84 | // TODO(DarkLordZach): Actually signal core_timing to change clock speed. |
| 85 | // TODO(Rodrigo): Remove [[maybe_unused]] when core_timing is used. | 85 | // TODO(Rodrigo): Remove [[maybe_unused]] when core_timing is used. |
| 86 | } | 86 | } |
diff --git a/src/core/hle/service/audio/audout_u.cpp b/src/core/hle/service/audio/audout_u.cpp index a44dd842a..49c092301 100644 --- a/src/core/hle/service/audio/audout_u.cpp +++ b/src/core/hle/service/audio/audout_u.cpp | |||
| @@ -246,9 +246,8 @@ void AudOutU::ListAudioOuts(Kernel::HLERequestContext& ctx) { | |||
| 246 | const auto write_count = | 246 | const auto write_count = |
| 247 | static_cast<u32>(ctx.GetWriteBufferSize() / sizeof(AudioDevice::AudioDeviceName)); | 247 | static_cast<u32>(ctx.GetWriteBufferSize() / sizeof(AudioDevice::AudioDeviceName)); |
| 248 | std::vector<AudioDevice::AudioDeviceName> device_names{}; | 248 | std::vector<AudioDevice::AudioDeviceName> device_names{}; |
| 249 | std::string print_names{}; | ||
| 250 | if (write_count > 0) { | 249 | if (write_count > 0) { |
| 251 | device_names.push_back(AudioDevice::AudioDeviceName("DeviceOut")); | 250 | device_names.emplace_back("DeviceOut"); |
| 252 | LOG_DEBUG(Service_Audio, "called. \nName=DeviceOut"); | 251 | LOG_DEBUG(Service_Audio, "called. \nName=DeviceOut"); |
| 253 | } else { | 252 | } else { |
| 254 | LOG_DEBUG(Service_Audio, "called. Empty buffer passed in."); | 253 | LOG_DEBUG(Service_Audio, "called. Empty buffer passed in."); |
diff --git a/src/core/hle/service/audio/audren_u.cpp b/src/core/hle/service/audio/audren_u.cpp index 381a66ba5..6fb07c37d 100644 --- a/src/core/hle/service/audio/audren_u.cpp +++ b/src/core/hle/service/audio/audren_u.cpp | |||
| @@ -50,7 +50,7 @@ public: | |||
| 50 | {7, &IAudioRenderer::QuerySystemEvent, "QuerySystemEvent"}, | 50 | {7, &IAudioRenderer::QuerySystemEvent, "QuerySystemEvent"}, |
| 51 | {8, &IAudioRenderer::SetRenderingTimeLimit, "SetRenderingTimeLimit"}, | 51 | {8, &IAudioRenderer::SetRenderingTimeLimit, "SetRenderingTimeLimit"}, |
| 52 | {9, &IAudioRenderer::GetRenderingTimeLimit, "GetRenderingTimeLimit"}, | 52 | {9, &IAudioRenderer::GetRenderingTimeLimit, "GetRenderingTimeLimit"}, |
| 53 | {10, nullptr, "RequestUpdateAuto"}, | 53 | {10, &IAudioRenderer::RequestUpdate, "RequestUpdateAuto"}, |
| 54 | {11, nullptr, "ExecuteAudioRendererRendering"}, | 54 | {11, nullptr, "ExecuteAudioRendererRendering"}, |
| 55 | }; | 55 | }; |
| 56 | // clang-format on | 56 | // clang-format on |
| @@ -113,15 +113,30 @@ private: | |||
| 113 | 113 | ||
| 114 | // These buffers are written manually to avoid an issue with WriteBuffer throwing errors for | 114 | // These buffers are written manually to avoid an issue with WriteBuffer throwing errors for |
| 115 | // checking size 0. Performance size is 0 for most games. | 115 | // checking size 0. Performance size is 0 for most games. |
| 116 | const auto buffers{ctx.BufferDescriptorB()}; | 116 | |
| 117 | std::vector<u8> output(buffers[0].Size(), 0); | 117 | std::vector<u8> output{}; |
| 118 | std::vector<u8> performance(buffers[1].Size(), 0); | 118 | std::vector<u8> performance{}; |
| 119 | auto is_buffer_b{ctx.BufferDescriptorB()[0].Size() != 0}; | ||
| 120 | if (is_buffer_b) { | ||
| 121 | const auto buffersB{ctx.BufferDescriptorB()}; | ||
| 122 | output.resize(buffersB[0].Size(), 0); | ||
| 123 | performance.resize(buffersB[1].Size(), 0); | ||
| 124 | } else { | ||
| 125 | const auto buffersC{ctx.BufferDescriptorC()}; | ||
| 126 | output.resize(buffersC[0].Size(), 0); | ||
| 127 | performance.resize(buffersC[1].Size(), 0); | ||
| 128 | } | ||
| 119 | 129 | ||
| 120 | auto result = impl->RequestUpdate(input, performance, output); | 130 | auto result = impl->RequestUpdate(input, performance, output); |
| 121 | 131 | ||
| 122 | if (result.IsSuccess()) { | 132 | if (result.IsSuccess()) { |
| 123 | ctx.WriteBufferB(output.data(), output.size(), 0); | 133 | if (is_buffer_b) { |
| 124 | ctx.WriteBufferB(performance.data(), performance.size(), 1); | 134 | ctx.WriteBufferB(output.data(), output.size(), 0); |
| 135 | ctx.WriteBufferB(performance.data(), performance.size(), 1); | ||
| 136 | } else { | ||
| 137 | ctx.WriteBufferC(output.data(), output.size(), 0); | ||
| 138 | ctx.WriteBufferC(performance.data(), performance.size(), 1); | ||
| 139 | } | ||
| 125 | } else { | 140 | } else { |
| 126 | LOG_ERROR(Service_Audio, "RequestUpdate failed error 0x{:02X}!", result.description); | 141 | LOG_ERROR(Service_Audio, "RequestUpdate failed error 0x{:02X}!", result.description); |
| 127 | } | 142 | } |
| @@ -237,7 +252,7 @@ private: | |||
| 237 | 252 | ||
| 238 | std::vector<AudioDevice::AudioDeviceName> out_names{}; | 253 | std::vector<AudioDevice::AudioDeviceName> out_names{}; |
| 239 | 254 | ||
| 240 | u32 out_count = impl->ListAudioDeviceName(out_names, in_count); | 255 | const u32 out_count = impl->ListAudioDeviceName(out_names, in_count); |
| 241 | 256 | ||
| 242 | std::string out{}; | 257 | std::string out{}; |
| 243 | for (u32 i = 0; i < out_count; i++) { | 258 | for (u32 i = 0; i < out_count; i++) { |
| @@ -350,7 +365,7 @@ private: | |||
| 350 | 365 | ||
| 351 | std::vector<AudioDevice::AudioDeviceName> out_names{}; | 366 | std::vector<AudioDevice::AudioDeviceName> out_names{}; |
| 352 | 367 | ||
| 353 | u32 out_count = impl->ListAudioOutputDeviceName(out_names, in_count); | 368 | const u32 out_count = impl->ListAudioOutputDeviceName(out_names, in_count); |
| 354 | 369 | ||
| 355 | std::string out{}; | 370 | std::string out{}; |
| 356 | for (u32 i = 0; i < out_count; i++) { | 371 | for (u32 i = 0; i < out_count; i++) { |
diff --git a/src/core/hle/service/filesystem/fsp_srv.cpp b/src/core/hle/service/filesystem/fsp_srv.cpp index fae6e5aff..e23eae36a 100644 --- a/src/core/hle/service/filesystem/fsp_srv.cpp +++ b/src/core/hle/service/filesystem/fsp_srv.cpp | |||
| @@ -246,7 +246,8 @@ static void BuildEntryIndex(std::vector<FileSys::Entry>& entries, const std::vec | |||
| 246 | entries.reserve(entries.size() + new_data.size()); | 246 | entries.reserve(entries.size() + new_data.size()); |
| 247 | 247 | ||
| 248 | for (const auto& new_entry : new_data) { | 248 | for (const auto& new_entry : new_data) { |
| 249 | entries.emplace_back(new_entry->GetName(), type, new_entry->GetSize()); | 249 | entries.emplace_back(new_entry->GetName(), type, |
| 250 | type == FileSys::EntryType::Directory ? 0 : new_entry->GetSize()); | ||
| 250 | } | 251 | } |
| 251 | } | 252 | } |
| 252 | 253 | ||
diff --git a/src/core/hle/service/hid/controllers/npad.cpp b/src/core/hle/service/hid/controllers/npad.cpp index 3c28dee76..cb29004e8 100644 --- a/src/core/hle/service/hid/controllers/npad.cpp +++ b/src/core/hle/service/hid/controllers/npad.cpp | |||
| @@ -163,28 +163,51 @@ void Controller_NPad::InitNewlyAddedController(Core::HID::NpadIdType npad_id) { | |||
| 163 | } | 163 | } |
| 164 | LOG_DEBUG(Service_HID, "Npad connected {}", npad_id); | 164 | LOG_DEBUG(Service_HID, "Npad connected {}", npad_id); |
| 165 | const auto controller_type = controller.device->GetNpadStyleIndex(); | 165 | const auto controller_type = controller.device->GetNpadStyleIndex(); |
| 166 | const auto& body_colors = controller.device->GetColors(); | ||
| 167 | const auto& battery_level = controller.device->GetBattery(); | ||
| 166 | auto* shared_memory = controller.shared_memory; | 168 | auto* shared_memory = controller.shared_memory; |
| 167 | if (controller_type == Core::HID::NpadStyleIndex::None) { | 169 | if (controller_type == Core::HID::NpadStyleIndex::None) { |
| 168 | controller.styleset_changed_event->GetWritableEvent().Signal(); | 170 | controller.styleset_changed_event->GetWritableEvent().Signal(); |
| 169 | return; | 171 | return; |
| 170 | } | 172 | } |
| 173 | |||
| 174 | // Reset memory values | ||
| 171 | shared_memory->style_tag.raw = Core::HID::NpadStyleSet::None; | 175 | shared_memory->style_tag.raw = Core::HID::NpadStyleSet::None; |
| 172 | shared_memory->device_type.raw = 0; | 176 | shared_memory->device_type.raw = 0; |
| 173 | shared_memory->system_properties.raw = 0; | 177 | shared_memory->system_properties.raw = 0; |
| 178 | shared_memory->joycon_color.attribute = ColorAttribute::NoController; | ||
| 179 | shared_memory->joycon_color.attribute = ColorAttribute::NoController; | ||
| 180 | shared_memory->fullkey_color = {}; | ||
| 181 | shared_memory->joycon_color.left = {}; | ||
| 182 | shared_memory->joycon_color.right = {}; | ||
| 183 | shared_memory->battery_level_dual = {}; | ||
| 184 | shared_memory->battery_level_left = {}; | ||
| 185 | shared_memory->battery_level_right = {}; | ||
| 186 | |||
| 174 | switch (controller_type) { | 187 | switch (controller_type) { |
| 175 | case Core::HID::NpadStyleIndex::None: | 188 | case Core::HID::NpadStyleIndex::None: |
| 176 | ASSERT(false); | 189 | ASSERT(false); |
| 177 | break; | 190 | break; |
| 178 | case Core::HID::NpadStyleIndex::ProController: | 191 | case Core::HID::NpadStyleIndex::ProController: |
| 192 | shared_memory->fullkey_color.attribute = ColorAttribute::Ok; | ||
| 193 | shared_memory->fullkey_color.fullkey = body_colors.fullkey; | ||
| 194 | shared_memory->battery_level_dual = battery_level.dual.battery_level; | ||
| 179 | shared_memory->style_tag.fullkey.Assign(1); | 195 | shared_memory->style_tag.fullkey.Assign(1); |
| 180 | shared_memory->device_type.fullkey.Assign(1); | 196 | shared_memory->device_type.fullkey.Assign(1); |
| 181 | shared_memory->system_properties.is_vertical.Assign(1); | 197 | shared_memory->system_properties.is_vertical.Assign(1); |
| 182 | shared_memory->system_properties.use_plus.Assign(1); | 198 | shared_memory->system_properties.use_plus.Assign(1); |
| 183 | shared_memory->system_properties.use_minus.Assign(1); | 199 | shared_memory->system_properties.use_minus.Assign(1); |
| 200 | shared_memory->system_properties.is_charging_joy_dual.Assign( | ||
| 201 | battery_level.dual.is_charging); | ||
| 184 | shared_memory->applet_nfc_xcd.applet_footer.type = AppletFooterUiType::SwitchProController; | 202 | shared_memory->applet_nfc_xcd.applet_footer.type = AppletFooterUiType::SwitchProController; |
| 185 | shared_memory->sixaxis_fullkey_properties.is_newly_assigned.Assign(1); | 203 | shared_memory->sixaxis_fullkey_properties.is_newly_assigned.Assign(1); |
| 186 | break; | 204 | break; |
| 187 | case Core::HID::NpadStyleIndex::Handheld: | 205 | case Core::HID::NpadStyleIndex::Handheld: |
| 206 | shared_memory->fullkey_color.attribute = ColorAttribute::Ok; | ||
| 207 | shared_memory->joycon_color.attribute = ColorAttribute::Ok; | ||
| 208 | shared_memory->fullkey_color.fullkey = body_colors.fullkey; | ||
| 209 | shared_memory->joycon_color.left = body_colors.left; | ||
| 210 | shared_memory->joycon_color.right = body_colors.right; | ||
| 188 | shared_memory->style_tag.handheld.Assign(1); | 211 | shared_memory->style_tag.handheld.Assign(1); |
| 189 | shared_memory->device_type.handheld_left.Assign(1); | 212 | shared_memory->device_type.handheld_left.Assign(1); |
| 190 | shared_memory->device_type.handheld_right.Assign(1); | 213 | shared_memory->device_type.handheld_right.Assign(1); |
| @@ -192,47 +215,86 @@ void Controller_NPad::InitNewlyAddedController(Core::HID::NpadIdType npad_id) { | |||
| 192 | shared_memory->system_properties.use_plus.Assign(1); | 215 | shared_memory->system_properties.use_plus.Assign(1); |
| 193 | shared_memory->system_properties.use_minus.Assign(1); | 216 | shared_memory->system_properties.use_minus.Assign(1); |
| 194 | shared_memory->system_properties.use_directional_buttons.Assign(1); | 217 | shared_memory->system_properties.use_directional_buttons.Assign(1); |
| 218 | shared_memory->system_properties.is_charging_joy_dual.Assign( | ||
| 219 | battery_level.left.is_charging); | ||
| 220 | shared_memory->system_properties.is_charging_joy_left.Assign( | ||
| 221 | battery_level.left.is_charging); | ||
| 222 | shared_memory->system_properties.is_charging_joy_right.Assign( | ||
| 223 | battery_level.right.is_charging); | ||
| 195 | shared_memory->assignment_mode = NpadJoyAssignmentMode::Dual; | 224 | shared_memory->assignment_mode = NpadJoyAssignmentMode::Dual; |
| 196 | shared_memory->applet_nfc_xcd.applet_footer.type = | 225 | shared_memory->applet_nfc_xcd.applet_footer.type = |
| 197 | AppletFooterUiType::HandheldJoyConLeftJoyConRight; | 226 | AppletFooterUiType::HandheldJoyConLeftJoyConRight; |
| 198 | shared_memory->sixaxis_handheld_properties.is_newly_assigned.Assign(1); | 227 | shared_memory->sixaxis_handheld_properties.is_newly_assigned.Assign(1); |
| 199 | break; | 228 | break; |
| 200 | case Core::HID::NpadStyleIndex::JoyconDual: | 229 | case Core::HID::NpadStyleIndex::JoyconDual: |
| 230 | shared_memory->fullkey_color.attribute = ColorAttribute::Ok; | ||
| 231 | shared_memory->joycon_color.attribute = ColorAttribute::Ok; | ||
| 201 | shared_memory->style_tag.joycon_dual.Assign(1); | 232 | shared_memory->style_tag.joycon_dual.Assign(1); |
| 202 | if (controller.is_dual_left_connected) { | 233 | if (controller.is_dual_left_connected) { |
| 234 | shared_memory->joycon_color.left = body_colors.left; | ||
| 235 | shared_memory->battery_level_left = battery_level.left.battery_level; | ||
| 203 | shared_memory->device_type.joycon_left.Assign(1); | 236 | shared_memory->device_type.joycon_left.Assign(1); |
| 204 | shared_memory->system_properties.use_minus.Assign(1); | 237 | shared_memory->system_properties.use_minus.Assign(1); |
| 238 | shared_memory->system_properties.is_charging_joy_left.Assign( | ||
| 239 | battery_level.left.is_charging); | ||
| 205 | shared_memory->sixaxis_dual_left_properties.is_newly_assigned.Assign(1); | 240 | shared_memory->sixaxis_dual_left_properties.is_newly_assigned.Assign(1); |
| 206 | } | 241 | } |
| 207 | if (controller.is_dual_right_connected) { | 242 | if (controller.is_dual_right_connected) { |
| 243 | shared_memory->joycon_color.right = body_colors.right; | ||
| 244 | shared_memory->battery_level_right = battery_level.right.battery_level; | ||
| 208 | shared_memory->device_type.joycon_right.Assign(1); | 245 | shared_memory->device_type.joycon_right.Assign(1); |
| 209 | shared_memory->system_properties.use_plus.Assign(1); | 246 | shared_memory->system_properties.use_plus.Assign(1); |
| 247 | shared_memory->system_properties.is_charging_joy_right.Assign( | ||
| 248 | battery_level.right.is_charging); | ||
| 210 | shared_memory->sixaxis_dual_right_properties.is_newly_assigned.Assign(1); | 249 | shared_memory->sixaxis_dual_right_properties.is_newly_assigned.Assign(1); |
| 211 | } | 250 | } |
| 212 | shared_memory->system_properties.use_directional_buttons.Assign(1); | 251 | shared_memory->system_properties.use_directional_buttons.Assign(1); |
| 213 | shared_memory->system_properties.is_vertical.Assign(1); | 252 | shared_memory->system_properties.is_vertical.Assign(1); |
| 214 | shared_memory->assignment_mode = NpadJoyAssignmentMode::Dual; | 253 | shared_memory->assignment_mode = NpadJoyAssignmentMode::Dual; |
| 254 | |||
| 215 | if (controller.is_dual_left_connected && controller.is_dual_right_connected) { | 255 | if (controller.is_dual_left_connected && controller.is_dual_right_connected) { |
| 216 | shared_memory->applet_nfc_xcd.applet_footer.type = AppletFooterUiType::JoyDual; | 256 | shared_memory->applet_nfc_xcd.applet_footer.type = AppletFooterUiType::JoyDual; |
| 257 | shared_memory->fullkey_color.fullkey = body_colors.left; | ||
| 258 | shared_memory->battery_level_dual = battery_level.left.battery_level; | ||
| 259 | shared_memory->system_properties.is_charging_joy_dual.Assign( | ||
| 260 | battery_level.left.is_charging); | ||
| 217 | } else if (controller.is_dual_left_connected) { | 261 | } else if (controller.is_dual_left_connected) { |
| 218 | shared_memory->applet_nfc_xcd.applet_footer.type = AppletFooterUiType::JoyDualLeftOnly; | 262 | shared_memory->applet_nfc_xcd.applet_footer.type = AppletFooterUiType::JoyDualLeftOnly; |
| 263 | shared_memory->fullkey_color.fullkey = body_colors.left; | ||
| 264 | shared_memory->battery_level_dual = battery_level.left.battery_level; | ||
| 265 | shared_memory->system_properties.is_charging_joy_dual.Assign( | ||
| 266 | battery_level.left.is_charging); | ||
| 219 | } else { | 267 | } else { |
| 220 | shared_memory->applet_nfc_xcd.applet_footer.type = AppletFooterUiType::JoyDualRightOnly; | 268 | shared_memory->applet_nfc_xcd.applet_footer.type = AppletFooterUiType::JoyDualRightOnly; |
| 269 | shared_memory->fullkey_color.fullkey = body_colors.right; | ||
| 270 | shared_memory->battery_level_dual = battery_level.right.battery_level; | ||
| 271 | shared_memory->system_properties.is_charging_joy_dual.Assign( | ||
| 272 | battery_level.right.is_charging); | ||
| 221 | } | 273 | } |
| 222 | break; | 274 | break; |
| 223 | case Core::HID::NpadStyleIndex::JoyconLeft: | 275 | case Core::HID::NpadStyleIndex::JoyconLeft: |
| 276 | shared_memory->joycon_color.attribute = ColorAttribute::Ok; | ||
| 277 | shared_memory->joycon_color.left = body_colors.left; | ||
| 278 | shared_memory->battery_level_dual = battery_level.left.battery_level; | ||
| 224 | shared_memory->style_tag.joycon_left.Assign(1); | 279 | shared_memory->style_tag.joycon_left.Assign(1); |
| 225 | shared_memory->device_type.joycon_left.Assign(1); | 280 | shared_memory->device_type.joycon_left.Assign(1); |
| 226 | shared_memory->system_properties.is_horizontal.Assign(1); | 281 | shared_memory->system_properties.is_horizontal.Assign(1); |
| 227 | shared_memory->system_properties.use_minus.Assign(1); | 282 | shared_memory->system_properties.use_minus.Assign(1); |
| 283 | shared_memory->system_properties.is_charging_joy_left.Assign( | ||
| 284 | battery_level.left.is_charging); | ||
| 228 | shared_memory->applet_nfc_xcd.applet_footer.type = AppletFooterUiType::JoyLeftHorizontal; | 285 | shared_memory->applet_nfc_xcd.applet_footer.type = AppletFooterUiType::JoyLeftHorizontal; |
| 229 | shared_memory->sixaxis_left_properties.is_newly_assigned.Assign(1); | 286 | shared_memory->sixaxis_left_properties.is_newly_assigned.Assign(1); |
| 230 | break; | 287 | break; |
| 231 | case Core::HID::NpadStyleIndex::JoyconRight: | 288 | case Core::HID::NpadStyleIndex::JoyconRight: |
| 289 | shared_memory->joycon_color.attribute = ColorAttribute::Ok; | ||
| 290 | shared_memory->joycon_color.right = body_colors.right; | ||
| 291 | shared_memory->battery_level_right = battery_level.right.battery_level; | ||
| 232 | shared_memory->style_tag.joycon_right.Assign(1); | 292 | shared_memory->style_tag.joycon_right.Assign(1); |
| 233 | shared_memory->device_type.joycon_right.Assign(1); | 293 | shared_memory->device_type.joycon_right.Assign(1); |
| 234 | shared_memory->system_properties.is_horizontal.Assign(1); | 294 | shared_memory->system_properties.is_horizontal.Assign(1); |
| 235 | shared_memory->system_properties.use_plus.Assign(1); | 295 | shared_memory->system_properties.use_plus.Assign(1); |
| 296 | shared_memory->system_properties.is_charging_joy_right.Assign( | ||
| 297 | battery_level.right.is_charging); | ||
| 236 | shared_memory->applet_nfc_xcd.applet_footer.type = AppletFooterUiType::JoyRightHorizontal; | 298 | shared_memory->applet_nfc_xcd.applet_footer.type = AppletFooterUiType::JoyRightHorizontal; |
| 237 | shared_memory->sixaxis_right_properties.is_newly_assigned.Assign(1); | 299 | shared_memory->sixaxis_right_properties.is_newly_assigned.Assign(1); |
| 238 | break; | 300 | break; |
| @@ -269,21 +331,6 @@ void Controller_NPad::InitNewlyAddedController(Core::HID::NpadIdType npad_id) { | |||
| 269 | break; | 331 | break; |
| 270 | } | 332 | } |
| 271 | 333 | ||
| 272 | const auto& body_colors = controller.device->GetColors(); | ||
| 273 | |||
| 274 | shared_memory->fullkey_color.attribute = ColorAttribute::Ok; | ||
| 275 | shared_memory->fullkey_color.fullkey = body_colors.fullkey; | ||
| 276 | |||
| 277 | shared_memory->joycon_color.attribute = ColorAttribute::Ok; | ||
| 278 | shared_memory->joycon_color.left = body_colors.left; | ||
| 279 | shared_memory->joycon_color.right = body_colors.right; | ||
| 280 | |||
| 281 | // TODO: Investigate when we should report all batery types | ||
| 282 | const auto& battery_level = controller.device->GetBattery(); | ||
| 283 | shared_memory->battery_level_dual = battery_level.dual.battery_level; | ||
| 284 | shared_memory->battery_level_left = battery_level.left.battery_level; | ||
| 285 | shared_memory->battery_level_right = battery_level.right.battery_level; | ||
| 286 | |||
| 287 | controller.is_connected = true; | 334 | controller.is_connected = true; |
| 288 | controller.device->Connect(); | 335 | controller.device->Connect(); |
| 289 | SignalStyleSetChangedEvent(npad_id); | 336 | SignalStyleSetChangedEvent(npad_id); |
diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp index 5ecbddf94..3d3457160 100644 --- a/src/core/hle/service/hid/hid.cpp +++ b/src/core/hle/service/hid/hid.cpp | |||
| @@ -819,12 +819,12 @@ void Hid::EnableSixAxisSensorUnalteredPassthrough(Kernel::HLERequestContext& ctx | |||
| 819 | const auto result = controller.EnableSixAxisSensorUnalteredPassthrough( | 819 | const auto result = controller.EnableSixAxisSensorUnalteredPassthrough( |
| 820 | parameters.sixaxis_handle, parameters.enabled); | 820 | parameters.sixaxis_handle, parameters.enabled); |
| 821 | 821 | ||
| 822 | LOG_WARNING(Service_HID, | 822 | LOG_DEBUG(Service_HID, |
| 823 | "(STUBBED) called, enabled={}, npad_type={}, npad_id={}, device_index={}, " | 823 | "(STUBBED) called, enabled={}, npad_type={}, npad_id={}, device_index={}, " |
| 824 | "applet_resource_user_id={}", | 824 | "applet_resource_user_id={}", |
| 825 | parameters.enabled, parameters.sixaxis_handle.npad_type, | 825 | parameters.enabled, parameters.sixaxis_handle.npad_type, |
| 826 | parameters.sixaxis_handle.npad_id, parameters.sixaxis_handle.device_index, | 826 | parameters.sixaxis_handle.npad_id, parameters.sixaxis_handle.device_index, |
| 827 | parameters.applet_resource_user_id); | 827 | parameters.applet_resource_user_id); |
| 828 | 828 | ||
| 829 | IPC::ResponseBuilder rb{ctx, 2}; | 829 | IPC::ResponseBuilder rb{ctx, 2}; |
| 830 | rb.Push(result); | 830 | rb.Push(result); |
| @@ -846,7 +846,7 @@ void Hid::IsSixAxisSensorUnalteredPassthroughEnabled(Kernel::HLERequestContext& | |||
| 846 | const auto result = controller.IsSixAxisSensorUnalteredPassthroughEnabled( | 846 | const auto result = controller.IsSixAxisSensorUnalteredPassthroughEnabled( |
| 847 | parameters.sixaxis_handle, is_unaltered_sisxaxis_enabled); | 847 | parameters.sixaxis_handle, is_unaltered_sisxaxis_enabled); |
| 848 | 848 | ||
| 849 | LOG_WARNING( | 849 | LOG_DEBUG( |
| 850 | Service_HID, | 850 | Service_HID, |
| 851 | "(STUBBED) called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", | 851 | "(STUBBED) called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", |
| 852 | parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id, | 852 | parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id, |
| @@ -2146,12 +2146,18 @@ public: | |||
| 2146 | {324, nullptr, "GetUniquePadButtonSet"}, | 2146 | {324, nullptr, "GetUniquePadButtonSet"}, |
| 2147 | {325, nullptr, "GetUniquePadColor"}, | 2147 | {325, nullptr, "GetUniquePadColor"}, |
| 2148 | {326, nullptr, "GetUniquePadAppletDetailedUiType"}, | 2148 | {326, nullptr, "GetUniquePadAppletDetailedUiType"}, |
| 2149 | {327, nullptr, "GetAbstractedPadIdDataFromNpad"}, | ||
| 2150 | {328, nullptr, "AttachAbstractedPadToNpad"}, | ||
| 2151 | {329, nullptr, "DetachAbstractedPadAll"}, | ||
| 2152 | {330, nullptr, "CheckAbstractedPadConnection"}, | ||
| 2149 | {500, nullptr, "SetAppletResourceUserId"}, | 2153 | {500, nullptr, "SetAppletResourceUserId"}, |
| 2150 | {501, nullptr, "RegisterAppletResourceUserId"}, | 2154 | {501, nullptr, "RegisterAppletResourceUserId"}, |
| 2151 | {502, nullptr, "UnregisterAppletResourceUserId"}, | 2155 | {502, nullptr, "UnregisterAppletResourceUserId"}, |
| 2152 | {503, nullptr, "EnableAppletToGetInput"}, | 2156 | {503, nullptr, "EnableAppletToGetInput"}, |
| 2153 | {504, nullptr, "SetAruidValidForVibration"}, | 2157 | {504, nullptr, "SetAruidValidForVibration"}, |
| 2154 | {505, nullptr, "EnableAppletToGetSixAxisSensor"}, | 2158 | {505, nullptr, "EnableAppletToGetSixAxisSensor"}, |
| 2159 | {506, nullptr, "EnableAppletToGetPadInput"}, | ||
| 2160 | {507, nullptr, "EnableAppletToGetTouchScreen"}, | ||
| 2155 | {510, nullptr, "SetVibrationMasterVolume"}, | 2161 | {510, nullptr, "SetVibrationMasterVolume"}, |
| 2156 | {511, nullptr, "GetVibrationMasterVolume"}, | 2162 | {511, nullptr, "GetVibrationMasterVolume"}, |
| 2157 | {512, nullptr, "BeginPermitVibrationSession"}, | 2163 | {512, nullptr, "BeginPermitVibrationSession"}, |
diff --git a/src/core/hle/service/ldn/errors.h b/src/core/hle/service/ldn/errors.h deleted file mode 100644 index 972a74806..000000000 --- a/src/core/hle/service/ldn/errors.h +++ /dev/null | |||
| @@ -1,12 +0,0 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include "core/hle/result.h" | ||
| 7 | |||
| 8 | namespace Service::LDN { | ||
| 9 | |||
| 10 | constexpr Result ERROR_DISABLED{ErrorModule::LDN, 22}; | ||
| 11 | |||
| 12 | } // namespace Service::LDN | ||
diff --git a/src/core/hle/service/ldn/ldn.cpp b/src/core/hle/service/ldn/ldn.cpp index 125d4dc4c..c11daff54 100644 --- a/src/core/hle/service/ldn/ldn.cpp +++ b/src/core/hle/service/ldn/ldn.cpp | |||
| @@ -3,11 +3,15 @@ | |||
| 3 | 3 | ||
| 4 | #include <memory> | 4 | #include <memory> |
| 5 | 5 | ||
| 6 | #include "core/hle/ipc_helpers.h" | 6 | #include "core/core.h" |
| 7 | #include "core/hle/result.h" | ||
| 8 | #include "core/hle/service/ldn/errors.h" | ||
| 9 | #include "core/hle/service/ldn/ldn.h" | 7 | #include "core/hle/service/ldn/ldn.h" |
| 10 | #include "core/hle/service/sm/sm.h" | 8 | #include "core/hle/service/ldn/ldn_results.h" |
| 9 | #include "core/hle/service/ldn/ldn_types.h" | ||
| 10 | #include "core/internal_network/network.h" | ||
| 11 | #include "core/internal_network/network_interface.h" | ||
| 12 | |||
| 13 | // This is defined by synchapi.h and conflicts with ServiceContext::CreateEvent | ||
| 14 | #undef CreateEvent | ||
| 11 | 15 | ||
| 12 | namespace Service::LDN { | 16 | namespace Service::LDN { |
| 13 | 17 | ||
| @@ -100,74 +104,418 @@ class IUserLocalCommunicationService final | |||
| 100 | : public ServiceFramework<IUserLocalCommunicationService> { | 104 | : public ServiceFramework<IUserLocalCommunicationService> { |
| 101 | public: | 105 | public: |
| 102 | explicit IUserLocalCommunicationService(Core::System& system_) | 106 | explicit IUserLocalCommunicationService(Core::System& system_) |
| 103 | : ServiceFramework{system_, "IUserLocalCommunicationService"} { | 107 | : ServiceFramework{system_, "IUserLocalCommunicationService", ServiceThreadType::CreateNew}, |
| 108 | service_context{system, "IUserLocalCommunicationService"}, room_network{ | ||
| 109 | system_.GetRoomNetwork()} { | ||
| 104 | // clang-format off | 110 | // clang-format off |
| 105 | static const FunctionInfo functions[] = { | 111 | static const FunctionInfo functions[] = { |
| 106 | {0, &IUserLocalCommunicationService::GetState, "GetState"}, | 112 | {0, &IUserLocalCommunicationService::GetState, "GetState"}, |
| 107 | {1, nullptr, "GetNetworkInfo"}, | 113 | {1, &IUserLocalCommunicationService::GetNetworkInfo, "GetNetworkInfo"}, |
| 108 | {2, nullptr, "GetIpv4Address"}, | 114 | {2, nullptr, "GetIpv4Address"}, |
| 109 | {3, nullptr, "GetDisconnectReason"}, | 115 | {3, &IUserLocalCommunicationService::GetDisconnectReason, "GetDisconnectReason"}, |
| 110 | {4, nullptr, "GetSecurityParameter"}, | 116 | {4, &IUserLocalCommunicationService::GetSecurityParameter, "GetSecurityParameter"}, |
| 111 | {5, nullptr, "GetNetworkConfig"}, | 117 | {5, &IUserLocalCommunicationService::GetNetworkConfig, "GetNetworkConfig"}, |
| 112 | {100, nullptr, "AttachStateChangeEvent"}, | 118 | {100, &IUserLocalCommunicationService::AttachStateChangeEvent, "AttachStateChangeEvent"}, |
| 113 | {101, nullptr, "GetNetworkInfoLatestUpdate"}, | 119 | {101, &IUserLocalCommunicationService::GetNetworkInfoLatestUpdate, "GetNetworkInfoLatestUpdate"}, |
| 114 | {102, nullptr, "Scan"}, | 120 | {102, &IUserLocalCommunicationService::Scan, "Scan"}, |
| 115 | {103, nullptr, "ScanPrivate"}, | 121 | {103, &IUserLocalCommunicationService::ScanPrivate, "ScanPrivate"}, |
| 116 | {104, nullptr, "SetWirelessControllerRestriction"}, | 122 | {104, nullptr, "SetWirelessControllerRestriction"}, |
| 117 | {200, nullptr, "OpenAccessPoint"}, | 123 | {200, &IUserLocalCommunicationService::OpenAccessPoint, "OpenAccessPoint"}, |
| 118 | {201, nullptr, "CloseAccessPoint"}, | 124 | {201, &IUserLocalCommunicationService::CloseAccessPoint, "CloseAccessPoint"}, |
| 119 | {202, nullptr, "CreateNetwork"}, | 125 | {202, &IUserLocalCommunicationService::CreateNetwork, "CreateNetwork"}, |
| 120 | {203, nullptr, "CreateNetworkPrivate"}, | 126 | {203, &IUserLocalCommunicationService::CreateNetworkPrivate, "CreateNetworkPrivate"}, |
| 121 | {204, nullptr, "DestroyNetwork"}, | 127 | {204, &IUserLocalCommunicationService::DestroyNetwork, "DestroyNetwork"}, |
| 122 | {205, nullptr, "Reject"}, | 128 | {205, nullptr, "Reject"}, |
| 123 | {206, nullptr, "SetAdvertiseData"}, | 129 | {206, &IUserLocalCommunicationService::SetAdvertiseData, "SetAdvertiseData"}, |
| 124 | {207, nullptr, "SetStationAcceptPolicy"}, | 130 | {207, &IUserLocalCommunicationService::SetStationAcceptPolicy, "SetStationAcceptPolicy"}, |
| 125 | {208, nullptr, "AddAcceptFilterEntry"}, | 131 | {208, &IUserLocalCommunicationService::AddAcceptFilterEntry, "AddAcceptFilterEntry"}, |
| 126 | {209, nullptr, "ClearAcceptFilter"}, | 132 | {209, nullptr, "ClearAcceptFilter"}, |
| 127 | {300, nullptr, "OpenStation"}, | 133 | {300, &IUserLocalCommunicationService::OpenStation, "OpenStation"}, |
| 128 | {301, nullptr, "CloseStation"}, | 134 | {301, &IUserLocalCommunicationService::CloseStation, "CloseStation"}, |
| 129 | {302, nullptr, "Connect"}, | 135 | {302, &IUserLocalCommunicationService::Connect, "Connect"}, |
| 130 | {303, nullptr, "ConnectPrivate"}, | 136 | {303, nullptr, "ConnectPrivate"}, |
| 131 | {304, nullptr, "Disconnect"}, | 137 | {304, &IUserLocalCommunicationService::Disconnect, "Disconnect"}, |
| 132 | {400, nullptr, "Initialize"}, | 138 | {400, &IUserLocalCommunicationService::Initialize, "Initialize"}, |
| 133 | {401, nullptr, "Finalize"}, | 139 | {401, &IUserLocalCommunicationService::Finalize, "Finalize"}, |
| 134 | {402, &IUserLocalCommunicationService::Initialize2, "Initialize2"}, // 7.0.0+ | 140 | {402, &IUserLocalCommunicationService::Initialize2, "Initialize2"}, |
| 135 | }; | 141 | }; |
| 136 | // clang-format on | 142 | // clang-format on |
| 137 | 143 | ||
| 138 | RegisterHandlers(functions); | 144 | RegisterHandlers(functions); |
| 145 | |||
| 146 | state_change_event = | ||
| 147 | service_context.CreateEvent("IUserLocalCommunicationService:StateChangeEvent"); | ||
| 148 | } | ||
| 149 | |||
| 150 | ~IUserLocalCommunicationService() { | ||
| 151 | service_context.CloseEvent(state_change_event); | ||
| 152 | } | ||
| 153 | |||
| 154 | void OnEventFired() { | ||
| 155 | state_change_event->GetWritableEvent().Signal(); | ||
| 139 | } | 156 | } |
| 140 | 157 | ||
| 141 | void GetState(Kernel::HLERequestContext& ctx) { | 158 | void GetState(Kernel::HLERequestContext& ctx) { |
| 159 | State state = State::Error; | ||
| 160 | LOG_WARNING(Service_LDN, "(STUBBED) called, state = {}", state); | ||
| 161 | |||
| 162 | IPC::ResponseBuilder rb{ctx, 3}; | ||
| 163 | rb.Push(ResultSuccess); | ||
| 164 | rb.PushEnum(state); | ||
| 165 | } | ||
| 166 | |||
| 167 | void GetNetworkInfo(Kernel::HLERequestContext& ctx) { | ||
| 168 | const auto write_buffer_size = ctx.GetWriteBufferSize(); | ||
| 169 | |||
| 170 | if (write_buffer_size != sizeof(NetworkInfo)) { | ||
| 171 | LOG_ERROR(Service_LDN, "Invalid buffer size {}", write_buffer_size); | ||
| 172 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 173 | rb.Push(ResultBadInput); | ||
| 174 | return; | ||
| 175 | } | ||
| 176 | |||
| 177 | NetworkInfo network_info{}; | ||
| 178 | const auto rc = ResultSuccess; | ||
| 179 | if (rc.IsError()) { | ||
| 180 | LOG_ERROR(Service_LDN, "NetworkInfo is not valid {}", rc.raw); | ||
| 181 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 182 | rb.Push(rc); | ||
| 183 | return; | ||
| 184 | } | ||
| 185 | |||
| 186 | LOG_WARNING(Service_LDN, "(STUBBED) called, ssid='{}', nodes={}", | ||
| 187 | network_info.common.ssid.GetStringValue(), network_info.ldn.node_count); | ||
| 188 | |||
| 189 | ctx.WriteBuffer<NetworkInfo>(network_info); | ||
| 190 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 191 | rb.Push(rc); | ||
| 192 | } | ||
| 193 | |||
| 194 | void GetDisconnectReason(Kernel::HLERequestContext& ctx) { | ||
| 195 | const auto disconnect_reason = DisconnectReason::None; | ||
| 196 | |||
| 197 | LOG_WARNING(Service_LDN, "(STUBBED) called, disconnect_reason={}", disconnect_reason); | ||
| 198 | |||
| 199 | IPC::ResponseBuilder rb{ctx, 3}; | ||
| 200 | rb.Push(ResultSuccess); | ||
| 201 | rb.PushEnum(disconnect_reason); | ||
| 202 | } | ||
| 203 | |||
| 204 | void GetSecurityParameter(Kernel::HLERequestContext& ctx) { | ||
| 205 | SecurityParameter security_parameter{}; | ||
| 206 | NetworkInfo info{}; | ||
| 207 | const Result rc = ResultSuccess; | ||
| 208 | |||
| 209 | if (rc.IsError()) { | ||
| 210 | LOG_ERROR(Service_LDN, "NetworkInfo is not valid {}", rc.raw); | ||
| 211 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 212 | rb.Push(rc); | ||
| 213 | return; | ||
| 214 | } | ||
| 215 | |||
| 216 | security_parameter.session_id = info.network_id.session_id; | ||
| 217 | std::memcpy(security_parameter.data.data(), info.ldn.security_parameter.data(), | ||
| 218 | sizeof(SecurityParameter::data)); | ||
| 219 | |||
| 142 | LOG_WARNING(Service_LDN, "(STUBBED) called"); | 220 | LOG_WARNING(Service_LDN, "(STUBBED) called"); |
| 143 | 221 | ||
| 222 | IPC::ResponseBuilder rb{ctx, 10}; | ||
| 223 | rb.Push(rc); | ||
| 224 | rb.PushRaw<SecurityParameter>(security_parameter); | ||
| 225 | } | ||
| 226 | |||
| 227 | void GetNetworkConfig(Kernel::HLERequestContext& ctx) { | ||
| 228 | NetworkConfig config{}; | ||
| 229 | NetworkInfo info{}; | ||
| 230 | const Result rc = ResultSuccess; | ||
| 231 | |||
| 232 | if (rc.IsError()) { | ||
| 233 | LOG_ERROR(Service_LDN, "NetworkConfig is not valid {}", rc.raw); | ||
| 234 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 235 | rb.Push(rc); | ||
| 236 | return; | ||
| 237 | } | ||
| 238 | |||
| 239 | config.intent_id = info.network_id.intent_id; | ||
| 240 | config.channel = info.common.channel; | ||
| 241 | config.node_count_max = info.ldn.node_count_max; | ||
| 242 | config.local_communication_version = info.ldn.nodes[0].local_communication_version; | ||
| 243 | |||
| 244 | LOG_WARNING(Service_LDN, | ||
| 245 | "(STUBBED) called, intent_id={}/{}, channel={}, node_count_max={}, " | ||
| 246 | "local_communication_version={}", | ||
| 247 | config.intent_id.local_communication_id, config.intent_id.scene_id, | ||
| 248 | config.channel, config.node_count_max, config.local_communication_version); | ||
| 249 | |||
| 250 | IPC::ResponseBuilder rb{ctx, 10}; | ||
| 251 | rb.Push(rc); | ||
| 252 | rb.PushRaw<NetworkConfig>(config); | ||
| 253 | } | ||
| 254 | |||
| 255 | void AttachStateChangeEvent(Kernel::HLERequestContext& ctx) { | ||
| 256 | LOG_INFO(Service_LDN, "called"); | ||
| 257 | |||
| 258 | IPC::ResponseBuilder rb{ctx, 2, 1}; | ||
| 259 | rb.Push(ResultSuccess); | ||
| 260 | rb.PushCopyObjects(state_change_event->GetReadableEvent()); | ||
| 261 | } | ||
| 262 | |||
| 263 | void GetNetworkInfoLatestUpdate(Kernel::HLERequestContext& ctx) { | ||
| 264 | const std::size_t network_buffer_size = ctx.GetWriteBufferSize(0); | ||
| 265 | const std::size_t node_buffer_count = ctx.GetWriteBufferSize(1) / sizeof(NodeLatestUpdate); | ||
| 266 | |||
| 267 | if (node_buffer_count == 0 || network_buffer_size != sizeof(NetworkInfo)) { | ||
| 268 | LOG_ERROR(Service_LDN, "Invalid buffer size {}, {}", network_buffer_size, | ||
| 269 | node_buffer_count); | ||
| 270 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 271 | rb.Push(ResultBadInput); | ||
| 272 | return; | ||
| 273 | } | ||
| 274 | |||
| 275 | NetworkInfo info; | ||
| 276 | std::vector<NodeLatestUpdate> latest_update(node_buffer_count); | ||
| 277 | |||
| 278 | const auto rc = ResultSuccess; | ||
| 279 | if (rc.IsError()) { | ||
| 280 | LOG_ERROR(Service_LDN, "NetworkInfo is not valid {}", rc.raw); | ||
| 281 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 282 | rb.Push(rc); | ||
| 283 | return; | ||
| 284 | } | ||
| 285 | |||
| 286 | LOG_WARNING(Service_LDN, "(STUBBED) called, ssid='{}', nodes={}", | ||
| 287 | info.common.ssid.GetStringValue(), info.ldn.node_count); | ||
| 288 | |||
| 289 | ctx.WriteBuffer(info, 0); | ||
| 290 | ctx.WriteBuffer(latest_update, 1); | ||
| 291 | |||
| 292 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 293 | rb.Push(ResultSuccess); | ||
| 294 | } | ||
| 295 | |||
| 296 | void Scan(Kernel::HLERequestContext& ctx) { | ||
| 297 | ScanImpl(ctx); | ||
| 298 | } | ||
| 299 | |||
| 300 | void ScanPrivate(Kernel::HLERequestContext& ctx) { | ||
| 301 | ScanImpl(ctx, true); | ||
| 302 | } | ||
| 303 | |||
| 304 | void ScanImpl(Kernel::HLERequestContext& ctx, bool is_private = false) { | ||
| 305 | IPC::RequestParser rp{ctx}; | ||
| 306 | const auto channel{rp.PopEnum<WifiChannel>()}; | ||
| 307 | const auto scan_filter{rp.PopRaw<ScanFilter>()}; | ||
| 308 | |||
| 309 | const std::size_t network_info_size = ctx.GetWriteBufferSize() / sizeof(NetworkInfo); | ||
| 310 | |||
| 311 | if (network_info_size == 0) { | ||
| 312 | LOG_ERROR(Service_LDN, "Invalid buffer size {}", network_info_size); | ||
| 313 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 314 | rb.Push(ResultBadInput); | ||
| 315 | return; | ||
| 316 | } | ||
| 317 | |||
| 318 | u16 count = 0; | ||
| 319 | std::vector<NetworkInfo> network_infos(network_info_size); | ||
| 320 | |||
| 321 | LOG_WARNING(Service_LDN, | ||
| 322 | "(STUBBED) called, channel={}, filter_scan_flag={}, filter_network_type={}", | ||
| 323 | channel, scan_filter.flag, scan_filter.network_type); | ||
| 324 | |||
| 325 | ctx.WriteBuffer(network_infos); | ||
| 326 | |||
| 144 | IPC::ResponseBuilder rb{ctx, 3}; | 327 | IPC::ResponseBuilder rb{ctx, 3}; |
| 328 | rb.Push(ResultSuccess); | ||
| 329 | rb.Push<u32>(count); | ||
| 330 | } | ||
| 331 | |||
| 332 | void OpenAccessPoint(Kernel::HLERequestContext& ctx) { | ||
| 333 | LOG_WARNING(Service_LDN, "(STUBBED) called"); | ||
| 334 | |||
| 335 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 336 | rb.Push(ResultSuccess); | ||
| 337 | } | ||
| 338 | |||
| 339 | void CloseAccessPoint(Kernel::HLERequestContext& ctx) { | ||
| 340 | LOG_WARNING(Service_LDN, "(STUBBED) called"); | ||
| 341 | |||
| 342 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 343 | rb.Push(ResultSuccess); | ||
| 344 | } | ||
| 345 | |||
| 346 | void CreateNetwork(Kernel::HLERequestContext& ctx) { | ||
| 347 | IPC::RequestParser rp{ctx}; | ||
| 348 | struct Parameters { | ||
| 349 | SecurityConfig security_config; | ||
| 350 | UserConfig user_config; | ||
| 351 | INSERT_PADDING_WORDS_NOINIT(1); | ||
| 352 | NetworkConfig network_config; | ||
| 353 | }; | ||
| 354 | static_assert(sizeof(Parameters) == 0x98, "Parameters has incorrect size."); | ||
| 355 | |||
| 356 | const auto parameters{rp.PopRaw<Parameters>()}; | ||
| 357 | |||
| 358 | LOG_WARNING(Service_LDN, | ||
| 359 | "(STUBBED) called, passphrase_size={}, security_mode={}, " | ||
| 360 | "local_communication_version={}", | ||
| 361 | parameters.security_config.passphrase_size, | ||
| 362 | parameters.security_config.security_mode, | ||
| 363 | parameters.network_config.local_communication_version); | ||
| 364 | |||
| 365 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 366 | rb.Push(ResultSuccess); | ||
| 367 | } | ||
| 368 | |||
| 369 | void CreateNetworkPrivate(Kernel::HLERequestContext& ctx) { | ||
| 370 | IPC::RequestParser rp{ctx}; | ||
| 371 | struct Parameters { | ||
| 372 | SecurityConfig security_config; | ||
| 373 | SecurityParameter security_parameter; | ||
| 374 | UserConfig user_config; | ||
| 375 | NetworkConfig network_config; | ||
| 376 | }; | ||
| 377 | static_assert(sizeof(Parameters) == 0xB8, "Parameters has incorrect size."); | ||
| 378 | |||
| 379 | const auto parameters{rp.PopRaw<Parameters>()}; | ||
| 380 | |||
| 381 | LOG_WARNING(Service_LDN, | ||
| 382 | "(STUBBED) called, passphrase_size={}, security_mode={}, " | ||
| 383 | "local_communication_version={}", | ||
| 384 | parameters.security_config.passphrase_size, | ||
| 385 | parameters.security_config.security_mode, | ||
| 386 | parameters.network_config.local_communication_version); | ||
| 387 | |||
| 388 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 389 | rb.Push(ResultSuccess); | ||
| 390 | } | ||
| 391 | |||
| 392 | void DestroyNetwork(Kernel::HLERequestContext& ctx) { | ||
| 393 | LOG_WARNING(Service_LDN, "(STUBBED) called"); | ||
| 394 | |||
| 395 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 396 | rb.Push(ResultSuccess); | ||
| 397 | } | ||
| 398 | |||
| 399 | void SetAdvertiseData(Kernel::HLERequestContext& ctx) { | ||
| 400 | std::vector<u8> read_buffer = ctx.ReadBuffer(); | ||
| 401 | |||
| 402 | LOG_WARNING(Service_LDN, "(STUBBED) called, size {}", read_buffer.size()); | ||
| 403 | |||
| 404 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 405 | rb.Push(ResultSuccess); | ||
| 406 | } | ||
| 407 | |||
| 408 | void SetStationAcceptPolicy(Kernel::HLERequestContext& ctx) { | ||
| 409 | LOG_WARNING(Service_LDN, "(STUBBED) called"); | ||
| 410 | |||
| 411 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 412 | rb.Push(ResultSuccess); | ||
| 413 | } | ||
| 145 | 414 | ||
| 146 | // Indicate a network error, as we do not actually emulate LDN | 415 | void AddAcceptFilterEntry(Kernel::HLERequestContext& ctx) { |
| 147 | rb.Push(static_cast<u32>(State::Error)); | 416 | LOG_WARNING(Service_LDN, "(STUBBED) called"); |
| 148 | 417 | ||
| 418 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 419 | rb.Push(ResultSuccess); | ||
| 420 | } | ||
| 421 | |||
| 422 | void OpenStation(Kernel::HLERequestContext& ctx) { | ||
| 423 | LOG_WARNING(Service_LDN, "(STUBBED) called"); | ||
| 424 | |||
| 425 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 426 | rb.Push(ResultSuccess); | ||
| 427 | } | ||
| 428 | |||
| 429 | void CloseStation(Kernel::HLERequestContext& ctx) { | ||
| 430 | LOG_WARNING(Service_LDN, "(STUBBED) called"); | ||
| 431 | |||
| 432 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 433 | rb.Push(ResultSuccess); | ||
| 434 | } | ||
| 435 | |||
| 436 | void Connect(Kernel::HLERequestContext& ctx) { | ||
| 437 | IPC::RequestParser rp{ctx}; | ||
| 438 | struct Parameters { | ||
| 439 | SecurityConfig security_config; | ||
| 440 | UserConfig user_config; | ||
| 441 | u32 local_communication_version; | ||
| 442 | u32 option; | ||
| 443 | }; | ||
| 444 | static_assert(sizeof(Parameters) == 0x7C, "Parameters has incorrect size."); | ||
| 445 | |||
| 446 | const auto parameters{rp.PopRaw<Parameters>()}; | ||
| 447 | |||
| 448 | LOG_WARNING(Service_LDN, | ||
| 449 | "(STUBBED) called, passphrase_size={}, security_mode={}, " | ||
| 450 | "local_communication_version={}", | ||
| 451 | parameters.security_config.passphrase_size, | ||
| 452 | parameters.security_config.security_mode, | ||
| 453 | parameters.local_communication_version); | ||
| 454 | |||
| 455 | const std::vector<u8> read_buffer = ctx.ReadBuffer(); | ||
| 456 | NetworkInfo network_info{}; | ||
| 457 | |||
| 458 | if (read_buffer.size() != sizeof(NetworkInfo)) { | ||
| 459 | LOG_ERROR(Frontend, "NetworkInfo doesn't match read_buffer size!"); | ||
| 460 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 461 | rb.Push(ResultBadInput); | ||
| 462 | return; | ||
| 463 | } | ||
| 464 | |||
| 465 | std::memcpy(&network_info, read_buffer.data(), read_buffer.size()); | ||
| 466 | |||
| 467 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 468 | rb.Push(ResultSuccess); | ||
| 469 | } | ||
| 470 | |||
| 471 | void Disconnect(Kernel::HLERequestContext& ctx) { | ||
| 472 | LOG_WARNING(Service_LDN, "(STUBBED) called"); | ||
| 473 | |||
| 474 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 475 | rb.Push(ResultSuccess); | ||
| 476 | } | ||
| 477 | void Initialize(Kernel::HLERequestContext& ctx) { | ||
| 478 | LOG_WARNING(Service_LDN, "(STUBBED) called"); | ||
| 479 | |||
| 480 | const auto rc = InitializeImpl(ctx); | ||
| 481 | |||
| 482 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 483 | rb.Push(rc); | ||
| 484 | } | ||
| 485 | |||
| 486 | void Finalize(Kernel::HLERequestContext& ctx) { | ||
| 487 | LOG_WARNING(Service_LDN, "(STUBBED) called"); | ||
| 488 | |||
| 489 | is_initialized = false; | ||
| 490 | |||
| 491 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 149 | rb.Push(ResultSuccess); | 492 | rb.Push(ResultSuccess); |
| 150 | } | 493 | } |
| 151 | 494 | ||
| 152 | void Initialize2(Kernel::HLERequestContext& ctx) { | 495 | void Initialize2(Kernel::HLERequestContext& ctx) { |
| 153 | LOG_DEBUG(Service_LDN, "called"); | 496 | LOG_WARNING(Service_LDN, "(STUBBED) called"); |
| 154 | 497 | ||
| 155 | is_initialized = true; | 498 | const auto rc = InitializeImpl(ctx); |
| 156 | 499 | ||
| 157 | IPC::ResponseBuilder rb{ctx, 2}; | 500 | IPC::ResponseBuilder rb{ctx, 2}; |
| 158 | rb.Push(ERROR_DISABLED); | 501 | rb.Push(rc); |
| 502 | } | ||
| 503 | |||
| 504 | Result InitializeImpl(Kernel::HLERequestContext& ctx) { | ||
| 505 | const auto network_interface = Network::GetSelectedNetworkInterface(); | ||
| 506 | if (!network_interface) { | ||
| 507 | LOG_ERROR(Service_LDN, "No network interface is set"); | ||
| 508 | return ResultAirplaneModeEnabled; | ||
| 509 | } | ||
| 510 | |||
| 511 | is_initialized = true; | ||
| 512 | // TODO (flTobi): Change this to ResultSuccess when LDN is fully implemented | ||
| 513 | return ResultAirplaneModeEnabled; | ||
| 159 | } | 514 | } |
| 160 | 515 | ||
| 161 | private: | 516 | KernelHelpers::ServiceContext service_context; |
| 162 | enum class State { | 517 | Kernel::KEvent* state_change_event; |
| 163 | None, | 518 | Network::RoomNetwork& room_network; |
| 164 | Initialized, | ||
| 165 | AccessPointOpened, | ||
| 166 | AccessPointCreated, | ||
| 167 | StationOpened, | ||
| 168 | StationConnected, | ||
| 169 | Error, | ||
| 170 | }; | ||
| 171 | 519 | ||
| 172 | bool is_initialized{}; | 520 | bool is_initialized{}; |
| 173 | }; | 521 | }; |
| @@ -273,7 +621,7 @@ public: | |||
| 273 | LOG_WARNING(Service_LDN, "(STUBBED) called"); | 621 | LOG_WARNING(Service_LDN, "(STUBBED) called"); |
| 274 | 622 | ||
| 275 | IPC::ResponseBuilder rb{ctx, 2}; | 623 | IPC::ResponseBuilder rb{ctx, 2}; |
| 276 | rb.Push(ERROR_DISABLED); | 624 | rb.Push(ResultDisabled); |
| 277 | } | 625 | } |
| 278 | }; | 626 | }; |
| 279 | 627 | ||
diff --git a/src/core/hle/service/ldn/ldn.h b/src/core/hle/service/ldn/ldn.h index a0031ac71..6afe2ea6f 100644 --- a/src/core/hle/service/ldn/ldn.h +++ b/src/core/hle/service/ldn/ldn.h | |||
| @@ -3,6 +3,12 @@ | |||
| 3 | 3 | ||
| 4 | #pragma once | 4 | #pragma once |
| 5 | 5 | ||
| 6 | #include "core/hle/ipc_helpers.h" | ||
| 7 | #include "core/hle/kernel/k_event.h" | ||
| 8 | #include "core/hle/result.h" | ||
| 9 | #include "core/hle/service/kernel_helpers.h" | ||
| 10 | #include "core/hle/service/sm/sm.h" | ||
| 11 | |||
| 6 | namespace Core { | 12 | namespace Core { |
| 7 | class System; | 13 | class System; |
| 8 | } | 14 | } |
diff --git a/src/core/hle/service/ldn/ldn_results.h b/src/core/hle/service/ldn/ldn_results.h new file mode 100644 index 000000000..f340bda42 --- /dev/null +++ b/src/core/hle/service/ldn/ldn_results.h | |||
| @@ -0,0 +1,27 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-3.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include "core/hle/result.h" | ||
| 7 | |||
| 8 | namespace Service::LDN { | ||
| 9 | |||
| 10 | constexpr Result ResultAdvertiseDataTooLarge{ErrorModule::LDN, 10}; | ||
| 11 | constexpr Result ResultAuthenticationFailed{ErrorModule::LDN, 20}; | ||
| 12 | constexpr Result ResultDisabled{ErrorModule::LDN, 22}; | ||
| 13 | constexpr Result ResultAirplaneModeEnabled{ErrorModule::LDN, 23}; | ||
| 14 | constexpr Result ResultInvalidNodeCount{ErrorModule::LDN, 30}; | ||
| 15 | constexpr Result ResultConnectionFailed{ErrorModule::LDN, 31}; | ||
| 16 | constexpr Result ResultBadState{ErrorModule::LDN, 32}; | ||
| 17 | constexpr Result ResultNoIpAddress{ErrorModule::LDN, 33}; | ||
| 18 | constexpr Result ResultInvalidBufferCount{ErrorModule::LDN, 50}; | ||
| 19 | constexpr Result ResultAccessPointConnectionFailed{ErrorModule::LDN, 65}; | ||
| 20 | constexpr Result ResultAuthenticationTimeout{ErrorModule::LDN, 66}; | ||
| 21 | constexpr Result ResultMaximumNodeCount{ErrorModule::LDN, 67}; | ||
| 22 | constexpr Result ResultBadInput{ErrorModule::LDN, 96}; | ||
| 23 | constexpr Result ResultLocalCommunicationIdNotFound{ErrorModule::LDN, 97}; | ||
| 24 | constexpr Result ResultLocalCommunicationVersionTooLow{ErrorModule::LDN, 113}; | ||
| 25 | constexpr Result ResultLocalCommunicationVersionTooHigh{ErrorModule::LDN, 114}; | ||
| 26 | |||
| 27 | } // namespace Service::LDN | ||
diff --git a/src/core/hle/service/ldn/ldn_types.h b/src/core/hle/service/ldn/ldn_types.h new file mode 100644 index 000000000..6231e936d --- /dev/null +++ b/src/core/hle/service/ldn/ldn_types.h | |||
| @@ -0,0 +1,292 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-3.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include <fmt/format.h> | ||
| 7 | |||
| 8 | #include "common/common_funcs.h" | ||
| 9 | #include "common/common_types.h" | ||
| 10 | #include "network/network.h" | ||
| 11 | |||
| 12 | namespace Service::LDN { | ||
| 13 | |||
| 14 | constexpr size_t SsidLengthMax = 32; | ||
| 15 | constexpr size_t AdvertiseDataSizeMax = 384; | ||
| 16 | constexpr size_t UserNameBytesMax = 32; | ||
| 17 | constexpr int NodeCountMax = 8; | ||
| 18 | constexpr int StationCountMax = NodeCountMax - 1; | ||
| 19 | constexpr size_t PassphraseLengthMax = 64; | ||
| 20 | |||
| 21 | enum class SecurityMode : u16 { | ||
| 22 | All, | ||
| 23 | Retail, | ||
| 24 | Debug, | ||
| 25 | }; | ||
| 26 | |||
| 27 | enum class NodeStateChange : u8 { | ||
| 28 | None, | ||
| 29 | Connect, | ||
| 30 | Disconnect, | ||
| 31 | DisconnectAndConnect, | ||
| 32 | }; | ||
| 33 | |||
| 34 | enum class ScanFilterFlag : u32 { | ||
| 35 | None = 0, | ||
| 36 | LocalCommunicationId = 1 << 0, | ||
| 37 | SessionId = 1 << 1, | ||
| 38 | NetworkType = 1 << 2, | ||
| 39 | Ssid = 1 << 4, | ||
| 40 | SceneId = 1 << 5, | ||
| 41 | IntentId = LocalCommunicationId | SceneId, | ||
| 42 | NetworkId = IntentId | SessionId, | ||
| 43 | }; | ||
| 44 | |||
| 45 | enum class NetworkType : u32 { | ||
| 46 | None, | ||
| 47 | General, | ||
| 48 | Ldn, | ||
| 49 | All, | ||
| 50 | }; | ||
| 51 | |||
| 52 | enum class PackedNetworkType : u8 { | ||
| 53 | None, | ||
| 54 | General, | ||
| 55 | Ldn, | ||
| 56 | All, | ||
| 57 | }; | ||
| 58 | |||
| 59 | enum class State : u32 { | ||
| 60 | None, | ||
| 61 | Initialized, | ||
| 62 | AccessPointOpened, | ||
| 63 | AccessPointCreated, | ||
| 64 | StationOpened, | ||
| 65 | StationConnected, | ||
| 66 | Error, | ||
| 67 | }; | ||
| 68 | |||
| 69 | enum class DisconnectReason : s16 { | ||
| 70 | Unknown = -1, | ||
| 71 | None, | ||
| 72 | DisconnectedByUser, | ||
| 73 | DisconnectedBySystem, | ||
| 74 | DestroyedByUser, | ||
| 75 | DestroyedBySystem, | ||
| 76 | Rejected, | ||
| 77 | SignalLost, | ||
| 78 | }; | ||
| 79 | |||
| 80 | enum class NetworkError { | ||
| 81 | Unknown = -1, | ||
| 82 | None = 0, | ||
| 83 | PortUnreachable, | ||
| 84 | TooManyPlayers, | ||
| 85 | VersionTooLow, | ||
| 86 | VersionTooHigh, | ||
| 87 | ConnectFailure, | ||
| 88 | ConnectNotFound, | ||
| 89 | ConnectTimeout, | ||
| 90 | ConnectRejected, | ||
| 91 | RejectFailed, | ||
| 92 | }; | ||
| 93 | |||
| 94 | enum class AcceptPolicy : u8 { | ||
| 95 | AcceptAll, | ||
| 96 | RejectAll, | ||
| 97 | BlackList, | ||
| 98 | WhiteList, | ||
| 99 | }; | ||
| 100 | |||
| 101 | enum class WifiChannel : s16 { | ||
| 102 | Default = 0, | ||
| 103 | wifi24_1 = 1, | ||
| 104 | wifi24_6 = 6, | ||
| 105 | wifi24_11 = 11, | ||
| 106 | wifi50_36 = 36, | ||
| 107 | wifi50_40 = 40, | ||
| 108 | wifi50_44 = 44, | ||
| 109 | wifi50_48 = 48, | ||
| 110 | }; | ||
| 111 | |||
| 112 | enum class LinkLevel : s8 { | ||
| 113 | Bad, | ||
| 114 | Low, | ||
| 115 | Good, | ||
| 116 | Excellent, | ||
| 117 | }; | ||
| 118 | |||
| 119 | struct NodeLatestUpdate { | ||
| 120 | NodeStateChange state_change; | ||
| 121 | INSERT_PADDING_BYTES(0x7); // Unknown | ||
| 122 | }; | ||
| 123 | static_assert(sizeof(NodeLatestUpdate) == 0x8, "NodeLatestUpdate is an invalid size"); | ||
| 124 | |||
| 125 | struct SessionId { | ||
| 126 | u64 high; | ||
| 127 | u64 low; | ||
| 128 | |||
| 129 | bool operator==(const SessionId&) const = default; | ||
| 130 | }; | ||
| 131 | static_assert(sizeof(SessionId) == 0x10, "SessionId is an invalid size"); | ||
| 132 | |||
| 133 | struct IntentId { | ||
| 134 | u64 local_communication_id; | ||
| 135 | INSERT_PADDING_BYTES(0x2); // Reserved | ||
| 136 | u16 scene_id; | ||
| 137 | INSERT_PADDING_BYTES(0x4); // Reserved | ||
| 138 | }; | ||
| 139 | static_assert(sizeof(IntentId) == 0x10, "IntentId is an invalid size"); | ||
| 140 | |||
| 141 | struct NetworkId { | ||
| 142 | IntentId intent_id; | ||
| 143 | SessionId session_id; | ||
| 144 | }; | ||
| 145 | static_assert(sizeof(NetworkId) == 0x20, "NetworkId is an invalid size"); | ||
| 146 | |||
| 147 | struct Ssid { | ||
| 148 | u8 length{}; | ||
| 149 | std::array<char, SsidLengthMax + 1> raw{}; | ||
| 150 | |||
| 151 | Ssid() = default; | ||
| 152 | |||
| 153 | explicit Ssid(std::string_view data) { | ||
| 154 | length = static_cast<u8>(std::min(data.size(), SsidLengthMax)); | ||
| 155 | data.copy(raw.data(), length); | ||
| 156 | raw[length] = 0; | ||
| 157 | } | ||
| 158 | |||
| 159 | std::string GetStringValue() const { | ||
| 160 | return std::string(raw.data()); | ||
| 161 | } | ||
| 162 | }; | ||
| 163 | static_assert(sizeof(Ssid) == 0x22, "Ssid is an invalid size"); | ||
| 164 | |||
| 165 | struct Ipv4Address { | ||
| 166 | union { | ||
| 167 | u32 raw{}; | ||
| 168 | std::array<u8, 4> bytes; | ||
| 169 | }; | ||
| 170 | |||
| 171 | std::string GetStringValue() const { | ||
| 172 | return fmt::format("{}.{}.{}.{}", bytes[3], bytes[2], bytes[1], bytes[0]); | ||
| 173 | } | ||
| 174 | }; | ||
| 175 | static_assert(sizeof(Ipv4Address) == 0x4, "Ipv4Address is an invalid size"); | ||
| 176 | |||
| 177 | struct MacAddress { | ||
| 178 | std::array<u8, 6> raw{}; | ||
| 179 | |||
| 180 | friend bool operator==(const MacAddress& lhs, const MacAddress& rhs) = default; | ||
| 181 | }; | ||
| 182 | static_assert(sizeof(MacAddress) == 0x6, "MacAddress is an invalid size"); | ||
| 183 | |||
| 184 | struct ScanFilter { | ||
| 185 | NetworkId network_id; | ||
| 186 | NetworkType network_type; | ||
| 187 | MacAddress mac_address; | ||
| 188 | Ssid ssid; | ||
| 189 | INSERT_PADDING_BYTES(0x10); | ||
| 190 | ScanFilterFlag flag; | ||
| 191 | }; | ||
| 192 | static_assert(sizeof(ScanFilter) == 0x60, "ScanFilter is an invalid size"); | ||
| 193 | |||
| 194 | struct CommonNetworkInfo { | ||
| 195 | MacAddress bssid; | ||
| 196 | Ssid ssid; | ||
| 197 | WifiChannel channel; | ||
| 198 | LinkLevel link_level; | ||
| 199 | PackedNetworkType network_type; | ||
| 200 | INSERT_PADDING_BYTES(0x4); | ||
| 201 | }; | ||
| 202 | static_assert(sizeof(CommonNetworkInfo) == 0x30, "CommonNetworkInfo is an invalid size"); | ||
| 203 | |||
| 204 | struct NodeInfo { | ||
| 205 | Ipv4Address ipv4_address; | ||
| 206 | MacAddress mac_address; | ||
| 207 | s8 node_id; | ||
| 208 | u8 is_connected; | ||
| 209 | std::array<u8, UserNameBytesMax + 1> user_name; | ||
| 210 | INSERT_PADDING_BYTES(0x1); // Reserved | ||
| 211 | s16 local_communication_version; | ||
| 212 | INSERT_PADDING_BYTES(0x10); // Reserved | ||
| 213 | }; | ||
| 214 | static_assert(sizeof(NodeInfo) == 0x40, "NodeInfo is an invalid size"); | ||
| 215 | |||
| 216 | struct LdnNetworkInfo { | ||
| 217 | std::array<u8, 0x10> security_parameter; | ||
| 218 | SecurityMode security_mode; | ||
| 219 | AcceptPolicy station_accept_policy; | ||
| 220 | u8 has_action_frame; | ||
| 221 | INSERT_PADDING_BYTES(0x2); // Padding | ||
| 222 | u8 node_count_max; | ||
| 223 | u8 node_count; | ||
| 224 | std::array<NodeInfo, NodeCountMax> nodes; | ||
| 225 | INSERT_PADDING_BYTES(0x2); // Reserved | ||
| 226 | u16 advertise_data_size; | ||
| 227 | std::array<u8, AdvertiseDataSizeMax> advertise_data; | ||
| 228 | INSERT_PADDING_BYTES(0x8C); // Reserved | ||
| 229 | u64 random_authentication_id; | ||
| 230 | }; | ||
| 231 | static_assert(sizeof(LdnNetworkInfo) == 0x430, "LdnNetworkInfo is an invalid size"); | ||
| 232 | |||
| 233 | struct NetworkInfo { | ||
| 234 | NetworkId network_id; | ||
| 235 | CommonNetworkInfo common; | ||
| 236 | LdnNetworkInfo ldn; | ||
| 237 | }; | ||
| 238 | static_assert(sizeof(NetworkInfo) == 0x480, "NetworkInfo is an invalid size"); | ||
| 239 | |||
| 240 | struct SecurityConfig { | ||
| 241 | SecurityMode security_mode; | ||
| 242 | u16 passphrase_size; | ||
| 243 | std::array<u8, PassphraseLengthMax> passphrase; | ||
| 244 | }; | ||
| 245 | static_assert(sizeof(SecurityConfig) == 0x44, "SecurityConfig is an invalid size"); | ||
| 246 | |||
| 247 | struct UserConfig { | ||
| 248 | std::array<u8, UserNameBytesMax + 1> user_name; | ||
| 249 | INSERT_PADDING_BYTES(0xF); // Reserved | ||
| 250 | }; | ||
| 251 | static_assert(sizeof(UserConfig) == 0x30, "UserConfig is an invalid size"); | ||
| 252 | |||
| 253 | #pragma pack(push, 4) | ||
| 254 | struct ConnectRequest { | ||
| 255 | SecurityConfig security_config; | ||
| 256 | UserConfig user_config; | ||
| 257 | u32 local_communication_version; | ||
| 258 | u32 option_unknown; | ||
| 259 | NetworkInfo network_info; | ||
| 260 | }; | ||
| 261 | static_assert(sizeof(ConnectRequest) == 0x4FC, "ConnectRequest is an invalid size"); | ||
| 262 | #pragma pack(pop) | ||
| 263 | |||
| 264 | struct SecurityParameter { | ||
| 265 | std::array<u8, 0x10> data; // Data, used with the same key derivation as SecurityConfig | ||
| 266 | SessionId session_id; | ||
| 267 | }; | ||
| 268 | static_assert(sizeof(SecurityParameter) == 0x20, "SecurityParameter is an invalid size"); | ||
| 269 | |||
| 270 | struct NetworkConfig { | ||
| 271 | IntentId intent_id; | ||
| 272 | WifiChannel channel; | ||
| 273 | u8 node_count_max; | ||
| 274 | INSERT_PADDING_BYTES(0x1); // Reserved | ||
| 275 | u16 local_communication_version; | ||
| 276 | INSERT_PADDING_BYTES(0xA); // Reserved | ||
| 277 | }; | ||
| 278 | static_assert(sizeof(NetworkConfig) == 0x20, "NetworkConfig is an invalid size"); | ||
| 279 | |||
| 280 | struct AddressEntry { | ||
| 281 | Ipv4Address ipv4_address; | ||
| 282 | MacAddress mac_address; | ||
| 283 | INSERT_PADDING_BYTES(0x2); // Reserved | ||
| 284 | }; | ||
| 285 | static_assert(sizeof(AddressEntry) == 0xC, "AddressEntry is an invalid size"); | ||
| 286 | |||
| 287 | struct AddressList { | ||
| 288 | std::array<AddressEntry, 0x8> addresses; | ||
| 289 | }; | ||
| 290 | static_assert(sizeof(AddressList) == 0x60, "AddressList is an invalid size"); | ||
| 291 | |||
| 292 | } // namespace Service::LDN | ||
diff --git a/src/core/hle/service/mm/mm_u.cpp b/src/core/hle/service/mm/mm_u.cpp index 5ebb124a7..ba8c0e230 100644 --- a/src/core/hle/service/mm/mm_u.cpp +++ b/src/core/hle/service/mm/mm_u.cpp | |||
| @@ -46,7 +46,7 @@ private: | |||
| 46 | IPC::RequestParser rp{ctx}; | 46 | IPC::RequestParser rp{ctx}; |
| 47 | min = rp.Pop<u32>(); | 47 | min = rp.Pop<u32>(); |
| 48 | max = rp.Pop<u32>(); | 48 | max = rp.Pop<u32>(); |
| 49 | LOG_WARNING(Service_MM, "(STUBBED) called, min=0x{:X}, max=0x{:X}", min, max); | 49 | LOG_DEBUG(Service_MM, "(STUBBED) called, min=0x{:X}, max=0x{:X}", min, max); |
| 50 | 50 | ||
| 51 | current = min; | 51 | current = min; |
| 52 | IPC::ResponseBuilder rb{ctx, 2}; | 52 | IPC::ResponseBuilder rb{ctx, 2}; |
| @@ -54,7 +54,7 @@ private: | |||
| 54 | } | 54 | } |
| 55 | 55 | ||
| 56 | void GetOld(Kernel::HLERequestContext& ctx) { | 56 | void GetOld(Kernel::HLERequestContext& ctx) { |
| 57 | LOG_WARNING(Service_MM, "(STUBBED) called"); | 57 | LOG_DEBUG(Service_MM, "(STUBBED) called"); |
| 58 | 58 | ||
| 59 | IPC::ResponseBuilder rb{ctx, 3}; | 59 | IPC::ResponseBuilder rb{ctx, 3}; |
| 60 | rb.Push(ResultSuccess); | 60 | rb.Push(ResultSuccess); |
| @@ -81,8 +81,8 @@ private: | |||
| 81 | u32 input_id = rp.Pop<u32>(); | 81 | u32 input_id = rp.Pop<u32>(); |
| 82 | min = rp.Pop<u32>(); | 82 | min = rp.Pop<u32>(); |
| 83 | max = rp.Pop<u32>(); | 83 | max = rp.Pop<u32>(); |
| 84 | LOG_WARNING(Service_MM, "(STUBBED) called, input_id=0x{:X}, min=0x{:X}, max=0x{:X}", | 84 | LOG_DEBUG(Service_MM, "(STUBBED) called, input_id=0x{:X}, min=0x{:X}, max=0x{:X}", input_id, |
| 85 | input_id, min, max); | 85 | min, max); |
| 86 | 86 | ||
| 87 | current = min; | 87 | current = min; |
| 88 | IPC::ResponseBuilder rb{ctx, 2}; | 88 | IPC::ResponseBuilder rb{ctx, 2}; |
| @@ -90,7 +90,7 @@ private: | |||
| 90 | } | 90 | } |
| 91 | 91 | ||
| 92 | void Get(Kernel::HLERequestContext& ctx) { | 92 | void Get(Kernel::HLERequestContext& ctx) { |
| 93 | LOG_WARNING(Service_MM, "(STUBBED) called"); | 93 | LOG_DEBUG(Service_MM, "(STUBBED) called"); |
| 94 | 94 | ||
| 95 | IPC::ResponseBuilder rb{ctx, 3}; | 95 | IPC::ResponseBuilder rb{ctx, 3}; |
| 96 | rb.Push(ResultSuccess); | 96 | rb.Push(ResultSuccess); |
diff --git a/src/core/hle/service/nifm/nifm.cpp b/src/core/hle/service/nifm/nifm.cpp index 2889973e4..e3ef06481 100644 --- a/src/core/hle/service/nifm/nifm.cpp +++ b/src/core/hle/service/nifm/nifm.cpp | |||
| @@ -6,7 +6,6 @@ | |||
| 6 | #include "core/hle/kernel/k_event.h" | 6 | #include "core/hle/kernel/k_event.h" |
| 7 | #include "core/hle/service/kernel_helpers.h" | 7 | #include "core/hle/service/kernel_helpers.h" |
| 8 | #include "core/hle/service/nifm/nifm.h" | 8 | #include "core/hle/service/nifm/nifm.h" |
| 9 | #include "core/hle/service/service.h" | ||
| 10 | 9 | ||
| 11 | namespace { | 10 | namespace { |
| 12 | 11 | ||
| @@ -271,213 +270,228 @@ public: | |||
| 271 | } | 270 | } |
| 272 | }; | 271 | }; |
| 273 | 272 | ||
| 274 | class IGeneralService final : public ServiceFramework<IGeneralService> { | 273 | void IGeneralService::GetClientId(Kernel::HLERequestContext& ctx) { |
| 275 | public: | 274 | static constexpr u32 client_id = 1; |
| 276 | explicit IGeneralService(Core::System& system_); | 275 | LOG_WARNING(Service_NIFM, "(STUBBED) called"); |
| 277 | 276 | ||
| 278 | private: | 277 | IPC::ResponseBuilder rb{ctx, 4}; |
| 279 | void GetClientId(Kernel::HLERequestContext& ctx) { | 278 | rb.Push(ResultSuccess); |
| 280 | static constexpr u32 client_id = 1; | 279 | rb.Push<u64>(client_id); // Client ID needs to be non zero otherwise it's considered invalid |
| 281 | LOG_WARNING(Service_NIFM, "(STUBBED) called"); | 280 | } |
| 282 | 281 | ||
| 283 | IPC::ResponseBuilder rb{ctx, 4}; | 282 | void IGeneralService::CreateScanRequest(Kernel::HLERequestContext& ctx) { |
| 284 | rb.Push(ResultSuccess); | 283 | LOG_DEBUG(Service_NIFM, "called"); |
| 285 | rb.Push<u64>(client_id); // Client ID needs to be non zero otherwise it's considered invalid | ||
| 286 | } | ||
| 287 | 284 | ||
| 288 | void CreateScanRequest(Kernel::HLERequestContext& ctx) { | 285 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; |
| 289 | LOG_DEBUG(Service_NIFM, "called"); | ||
| 290 | 286 | ||
| 291 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; | 287 | rb.Push(ResultSuccess); |
| 288 | rb.PushIpcInterface<IScanRequest>(system); | ||
| 289 | } | ||
| 292 | 290 | ||
| 293 | rb.Push(ResultSuccess); | 291 | void IGeneralService::CreateRequest(Kernel::HLERequestContext& ctx) { |
| 294 | rb.PushIpcInterface<IScanRequest>(system); | 292 | LOG_DEBUG(Service_NIFM, "called"); |
| 295 | } | ||
| 296 | 293 | ||
| 297 | void CreateRequest(Kernel::HLERequestContext& ctx) { | 294 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; |
| 298 | LOG_DEBUG(Service_NIFM, "called"); | ||
| 299 | 295 | ||
| 300 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; | 296 | rb.Push(ResultSuccess); |
| 297 | rb.PushIpcInterface<IRequest>(system); | ||
| 298 | } | ||
| 301 | 299 | ||
| 302 | rb.Push(ResultSuccess); | 300 | void IGeneralService::GetCurrentNetworkProfile(Kernel::HLERequestContext& ctx) { |
| 303 | rb.PushIpcInterface<IRequest>(system); | 301 | LOG_WARNING(Service_NIFM, "(STUBBED) called"); |
| 304 | } | ||
| 305 | 302 | ||
| 306 | void GetCurrentNetworkProfile(Kernel::HLERequestContext& ctx) { | 303 | const auto net_iface = Network::GetSelectedNetworkInterface(); |
| 307 | LOG_WARNING(Service_NIFM, "(STUBBED) called"); | ||
| 308 | 304 | ||
| 309 | const auto net_iface = Network::GetSelectedNetworkInterface(); | 305 | SfNetworkProfileData network_profile_data = [&net_iface] { |
| 310 | 306 | if (!net_iface) { | |
| 311 | const SfNetworkProfileData network_profile_data = [&net_iface] { | 307 | return SfNetworkProfileData{}; |
| 312 | if (!net_iface) { | 308 | } |
| 313 | return SfNetworkProfileData{}; | 309 | |
| 314 | } | 310 | return SfNetworkProfileData{ |
| 315 | 311 | .ip_setting_data{ | |
| 316 | return SfNetworkProfileData{ | 312 | .ip_address_setting{ |
| 317 | .ip_setting_data{ | 313 | .is_automatic{true}, |
| 318 | .ip_address_setting{ | 314 | .current_address{Network::TranslateIPv4(net_iface->ip_address)}, |
| 319 | .is_automatic{true}, | 315 | .subnet_mask{Network::TranslateIPv4(net_iface->subnet_mask)}, |
| 320 | .current_address{Network::TranslateIPv4(net_iface->ip_address)}, | 316 | .gateway{Network::TranslateIPv4(net_iface->gateway)}, |
| 321 | .subnet_mask{Network::TranslateIPv4(net_iface->subnet_mask)}, | ||
| 322 | .gateway{Network::TranslateIPv4(net_iface->gateway)}, | ||
| 323 | }, | ||
| 324 | .dns_setting{ | ||
| 325 | .is_automatic{true}, | ||
| 326 | .primary_dns{1, 1, 1, 1}, | ||
| 327 | .secondary_dns{1, 0, 0, 1}, | ||
| 328 | }, | ||
| 329 | .proxy_setting{ | ||
| 330 | .enabled{false}, | ||
| 331 | .port{}, | ||
| 332 | .proxy_server{}, | ||
| 333 | .automatic_auth_enabled{}, | ||
| 334 | .user{}, | ||
| 335 | .password{}, | ||
| 336 | }, | ||
| 337 | .mtu{1500}, | ||
| 338 | }, | 317 | }, |
| 339 | .uuid{0xdeadbeef, 0xdeadbeef}, | 318 | .dns_setting{ |
| 340 | .network_name{"yuzu Network"}, | 319 | .is_automatic{true}, |
| 341 | .wireless_setting_data{ | 320 | .primary_dns{1, 1, 1, 1}, |
| 342 | .ssid_length{12}, | 321 | .secondary_dns{1, 0, 0, 1}, |
| 343 | .ssid{"yuzu Network"}, | ||
| 344 | .passphrase{"yuzupassword"}, | ||
| 345 | }, | 322 | }, |
| 346 | }; | 323 | .proxy_setting{ |
| 347 | }(); | 324 | .enabled{false}, |
| 348 | 325 | .port{}, | |
| 349 | ctx.WriteBuffer(network_profile_data); | 326 | .proxy_server{}, |
| 327 | .automatic_auth_enabled{}, | ||
| 328 | .user{}, | ||
| 329 | .password{}, | ||
| 330 | }, | ||
| 331 | .mtu{1500}, | ||
| 332 | }, | ||
| 333 | .uuid{0xdeadbeef, 0xdeadbeef}, | ||
| 334 | .network_name{"yuzu Network"}, | ||
| 335 | .wireless_setting_data{ | ||
| 336 | .ssid_length{12}, | ||
| 337 | .ssid{"yuzu Network"}, | ||
| 338 | .passphrase{"yuzupassword"}, | ||
| 339 | }, | ||
| 340 | }; | ||
| 341 | }(); | ||
| 350 | 342 | ||
| 351 | IPC::ResponseBuilder rb{ctx, 2}; | 343 | // When we're connected to a room, spoof the hosts IP address |
| 352 | rb.Push(ResultSuccess); | 344 | if (auto room_member = network.GetRoomMember().lock()) { |
| 345 | if (room_member->IsConnected()) { | ||
| 346 | network_profile_data.ip_setting_data.ip_address_setting.current_address = | ||
| 347 | room_member->GetFakeIpAddress(); | ||
| 348 | } | ||
| 353 | } | 349 | } |
| 354 | 350 | ||
| 355 | void RemoveNetworkProfile(Kernel::HLERequestContext& ctx) { | 351 | ctx.WriteBuffer(network_profile_data); |
| 356 | LOG_WARNING(Service_NIFM, "(STUBBED) called"); | ||
| 357 | 352 | ||
| 358 | IPC::ResponseBuilder rb{ctx, 2}; | 353 | IPC::ResponseBuilder rb{ctx, 2}; |
| 359 | rb.Push(ResultSuccess); | 354 | rb.Push(ResultSuccess); |
| 360 | } | 355 | } |
| 361 | 356 | ||
| 362 | void GetCurrentIpAddress(Kernel::HLERequestContext& ctx) { | 357 | void IGeneralService::RemoveNetworkProfile(Kernel::HLERequestContext& ctx) { |
| 363 | LOG_WARNING(Service_NIFM, "(STUBBED) called"); | 358 | LOG_WARNING(Service_NIFM, "(STUBBED) called"); |
| 364 | 359 | ||
| 365 | auto ipv4 = Network::GetHostIPv4Address(); | 360 | IPC::ResponseBuilder rb{ctx, 2}; |
| 366 | if (!ipv4) { | 361 | rb.Push(ResultSuccess); |
| 367 | LOG_ERROR(Service_NIFM, "Couldn't get host IPv4 address, defaulting to 0.0.0.0"); | 362 | } |
| 368 | ipv4.emplace(Network::IPv4Address{0, 0, 0, 0}); | ||
| 369 | } | ||
| 370 | 363 | ||
| 371 | IPC::ResponseBuilder rb{ctx, 3}; | 364 | void IGeneralService::GetCurrentIpAddress(Kernel::HLERequestContext& ctx) { |
| 372 | rb.Push(ResultSuccess); | 365 | LOG_WARNING(Service_NIFM, "(STUBBED) called"); |
| 373 | rb.PushRaw(*ipv4); | 366 | |
| 367 | auto ipv4 = Network::GetHostIPv4Address(); | ||
| 368 | if (!ipv4) { | ||
| 369 | LOG_ERROR(Service_NIFM, "Couldn't get host IPv4 address, defaulting to 0.0.0.0"); | ||
| 370 | ipv4.emplace(Network::IPv4Address{0, 0, 0, 0}); | ||
| 374 | } | 371 | } |
| 375 | 372 | ||
| 376 | void CreateTemporaryNetworkProfile(Kernel::HLERequestContext& ctx) { | 373 | // When we're connected to a room, spoof the hosts IP address |
| 377 | LOG_DEBUG(Service_NIFM, "called"); | 374 | if (auto room_member = network.GetRoomMember().lock()) { |
| 375 | if (room_member->IsConnected()) { | ||
| 376 | ipv4 = room_member->GetFakeIpAddress(); | ||
| 377 | } | ||
| 378 | } | ||
| 378 | 379 | ||
| 379 | ASSERT_MSG(ctx.GetReadBufferSize() == 0x17c, | 380 | IPC::ResponseBuilder rb{ctx, 3}; |
| 380 | "SfNetworkProfileData is not the correct size"); | 381 | rb.Push(ResultSuccess); |
| 381 | u128 uuid{}; | 382 | rb.PushRaw(*ipv4); |
| 382 | auto buffer = ctx.ReadBuffer(); | 383 | } |
| 383 | std::memcpy(&uuid, buffer.data() + 8, sizeof(u128)); | ||
| 384 | 384 | ||
| 385 | IPC::ResponseBuilder rb{ctx, 6, 0, 1}; | 385 | void IGeneralService::CreateTemporaryNetworkProfile(Kernel::HLERequestContext& ctx) { |
| 386 | LOG_DEBUG(Service_NIFM, "called"); | ||
| 386 | 387 | ||
| 387 | rb.Push(ResultSuccess); | 388 | ASSERT_MSG(ctx.GetReadBufferSize() == 0x17c, "SfNetworkProfileData is not the correct size"); |
| 388 | rb.PushIpcInterface<INetworkProfile>(system); | 389 | u128 uuid{}; |
| 389 | rb.PushRaw<u128>(uuid); | 390 | auto buffer = ctx.ReadBuffer(); |
| 390 | } | 391 | std::memcpy(&uuid, buffer.data() + 8, sizeof(u128)); |
| 391 | 392 | ||
| 392 | void GetCurrentIpConfigInfo(Kernel::HLERequestContext& ctx) { | 393 | IPC::ResponseBuilder rb{ctx, 6, 0, 1}; |
| 393 | LOG_WARNING(Service_NIFM, "(STUBBED) called"); | ||
| 394 | 394 | ||
| 395 | struct IpConfigInfo { | 395 | rb.Push(ResultSuccess); |
| 396 | IpAddressSetting ip_address_setting{}; | 396 | rb.PushIpcInterface<INetworkProfile>(system); |
| 397 | DnsSetting dns_setting{}; | 397 | rb.PushRaw<u128>(uuid); |
| 398 | }; | 398 | } |
| 399 | static_assert(sizeof(IpConfigInfo) == sizeof(IpAddressSetting) + sizeof(DnsSetting), | ||
| 400 | "IpConfigInfo has incorrect size."); | ||
| 401 | 399 | ||
| 402 | const auto net_iface = Network::GetSelectedNetworkInterface(); | 400 | void IGeneralService::GetCurrentIpConfigInfo(Kernel::HLERequestContext& ctx) { |
| 401 | LOG_WARNING(Service_NIFM, "(STUBBED) called"); | ||
| 403 | 402 | ||
| 404 | const IpConfigInfo ip_config_info = [&net_iface] { | 403 | struct IpConfigInfo { |
| 405 | if (!net_iface) { | 404 | IpAddressSetting ip_address_setting{}; |
| 406 | return IpConfigInfo{}; | 405 | DnsSetting dns_setting{}; |
| 407 | } | 406 | }; |
| 407 | static_assert(sizeof(IpConfigInfo) == sizeof(IpAddressSetting) + sizeof(DnsSetting), | ||
| 408 | "IpConfigInfo has incorrect size."); | ||
| 408 | 409 | ||
| 409 | return IpConfigInfo{ | 410 | const auto net_iface = Network::GetSelectedNetworkInterface(); |
| 410 | .ip_address_setting{ | ||
| 411 | .is_automatic{true}, | ||
| 412 | .current_address{Network::TranslateIPv4(net_iface->ip_address)}, | ||
| 413 | .subnet_mask{Network::TranslateIPv4(net_iface->subnet_mask)}, | ||
| 414 | .gateway{Network::TranslateIPv4(net_iface->gateway)}, | ||
| 415 | }, | ||
| 416 | .dns_setting{ | ||
| 417 | .is_automatic{true}, | ||
| 418 | .primary_dns{1, 1, 1, 1}, | ||
| 419 | .secondary_dns{1, 0, 0, 1}, | ||
| 420 | }, | ||
| 421 | }; | ||
| 422 | }(); | ||
| 423 | 411 | ||
| 424 | IPC::ResponseBuilder rb{ctx, 2 + (sizeof(IpConfigInfo) + 3) / sizeof(u32)}; | 412 | IpConfigInfo ip_config_info = [&net_iface] { |
| 425 | rb.Push(ResultSuccess); | 413 | if (!net_iface) { |
| 426 | rb.PushRaw<IpConfigInfo>(ip_config_info); | 414 | return IpConfigInfo{}; |
| 427 | } | 415 | } |
| 428 | 416 | ||
| 429 | void IsWirelessCommunicationEnabled(Kernel::HLERequestContext& ctx) { | 417 | return IpConfigInfo{ |
| 430 | LOG_WARNING(Service_NIFM, "(STUBBED) called"); | 418 | .ip_address_setting{ |
| 419 | .is_automatic{true}, | ||
| 420 | .current_address{Network::TranslateIPv4(net_iface->ip_address)}, | ||
| 421 | .subnet_mask{Network::TranslateIPv4(net_iface->subnet_mask)}, | ||
| 422 | .gateway{Network::TranslateIPv4(net_iface->gateway)}, | ||
| 423 | }, | ||
| 424 | .dns_setting{ | ||
| 425 | .is_automatic{true}, | ||
| 426 | .primary_dns{1, 1, 1, 1}, | ||
| 427 | .secondary_dns{1, 0, 0, 1}, | ||
| 428 | }, | ||
| 429 | }; | ||
| 430 | }(); | ||
| 431 | 431 | ||
| 432 | IPC::ResponseBuilder rb{ctx, 3}; | 432 | // When we're connected to a room, spoof the hosts IP address |
| 433 | rb.Push(ResultSuccess); | 433 | if (auto room_member = network.GetRoomMember().lock()) { |
| 434 | rb.Push<u8>(0); | 434 | if (room_member->IsConnected()) { |
| 435 | ip_config_info.ip_address_setting.current_address = room_member->GetFakeIpAddress(); | ||
| 436 | } | ||
| 435 | } | 437 | } |
| 436 | 438 | ||
| 437 | void GetInternetConnectionStatus(Kernel::HLERequestContext& ctx) { | 439 | IPC::ResponseBuilder rb{ctx, 2 + (sizeof(IpConfigInfo) + 3) / sizeof(u32)}; |
| 438 | LOG_WARNING(Service_NIFM, "(STUBBED) called"); | 440 | rb.Push(ResultSuccess); |
| 441 | rb.PushRaw<IpConfigInfo>(ip_config_info); | ||
| 442 | } | ||
| 439 | 443 | ||
| 440 | struct Output { | 444 | void IGeneralService::IsWirelessCommunicationEnabled(Kernel::HLERequestContext& ctx) { |
| 441 | InternetConnectionType type{InternetConnectionType::WiFi}; | 445 | LOG_WARNING(Service_NIFM, "(STUBBED) called"); |
| 442 | u8 wifi_strength{3}; | ||
| 443 | InternetConnectionStatus state{InternetConnectionStatus::Connected}; | ||
| 444 | }; | ||
| 445 | static_assert(sizeof(Output) == 0x3, "Output has incorrect size."); | ||
| 446 | 446 | ||
| 447 | constexpr Output out{}; | 447 | IPC::ResponseBuilder rb{ctx, 3}; |
| 448 | rb.Push(ResultSuccess); | ||
| 449 | rb.Push<u8>(1); | ||
| 450 | } | ||
| 448 | 451 | ||
| 449 | IPC::ResponseBuilder rb{ctx, 3}; | 452 | void IGeneralService::GetInternetConnectionStatus(Kernel::HLERequestContext& ctx) { |
| 450 | rb.Push(ResultSuccess); | 453 | LOG_WARNING(Service_NIFM, "(STUBBED) called"); |
| 451 | rb.PushRaw(out); | ||
| 452 | } | ||
| 453 | 454 | ||
| 454 | void IsEthernetCommunicationEnabled(Kernel::HLERequestContext& ctx) { | 455 | struct Output { |
| 455 | LOG_WARNING(Service_NIFM, "(STUBBED) called"); | 456 | InternetConnectionType type{InternetConnectionType::WiFi}; |
| 457 | u8 wifi_strength{3}; | ||
| 458 | InternetConnectionStatus state{InternetConnectionStatus::Connected}; | ||
| 459 | }; | ||
| 460 | static_assert(sizeof(Output) == 0x3, "Output has incorrect size."); | ||
| 456 | 461 | ||
| 457 | IPC::ResponseBuilder rb{ctx, 3}; | 462 | constexpr Output out{}; |
| 458 | rb.Push(ResultSuccess); | 463 | |
| 459 | if (Network::GetHostIPv4Address().has_value()) { | 464 | IPC::ResponseBuilder rb{ctx, 3}; |
| 460 | rb.Push<u8>(1); | 465 | rb.Push(ResultSuccess); |
| 461 | } else { | 466 | rb.PushRaw(out); |
| 462 | rb.Push<u8>(0); | 467 | } |
| 463 | } | 468 | |
| 469 | void IGeneralService::IsEthernetCommunicationEnabled(Kernel::HLERequestContext& ctx) { | ||
| 470 | LOG_WARNING(Service_NIFM, "(STUBBED) called"); | ||
| 471 | |||
| 472 | IPC::ResponseBuilder rb{ctx, 3}; | ||
| 473 | rb.Push(ResultSuccess); | ||
| 474 | if (Network::GetHostIPv4Address().has_value()) { | ||
| 475 | rb.Push<u8>(1); | ||
| 476 | } else { | ||
| 477 | rb.Push<u8>(0); | ||
| 464 | } | 478 | } |
| 479 | } | ||
| 465 | 480 | ||
| 466 | void IsAnyInternetRequestAccepted(Kernel::HLERequestContext& ctx) { | 481 | void IGeneralService::IsAnyInternetRequestAccepted(Kernel::HLERequestContext& ctx) { |
| 467 | LOG_WARNING(Service_NIFM, "(STUBBED) called"); | 482 | LOG_ERROR(Service_NIFM, "(STUBBED) called"); |
| 468 | 483 | ||
| 469 | IPC::ResponseBuilder rb{ctx, 3}; | 484 | IPC::ResponseBuilder rb{ctx, 3}; |
| 470 | rb.Push(ResultSuccess); | 485 | rb.Push(ResultSuccess); |
| 471 | if (Network::GetHostIPv4Address().has_value()) { | 486 | if (Network::GetHostIPv4Address().has_value()) { |
| 472 | rb.Push<u8>(1); | 487 | rb.Push<u8>(1); |
| 473 | } else { | 488 | } else { |
| 474 | rb.Push<u8>(0); | 489 | rb.Push<u8>(0); |
| 475 | } | ||
| 476 | } | 490 | } |
| 477 | }; | 491 | } |
| 478 | 492 | ||
| 479 | IGeneralService::IGeneralService(Core::System& system_) | 493 | IGeneralService::IGeneralService(Core::System& system_) |
| 480 | : ServiceFramework{system_, "IGeneralService"} { | 494 | : ServiceFramework{system_, "IGeneralService"}, network{system_.GetRoomNetwork()} { |
| 481 | // clang-format off | 495 | // clang-format off |
| 482 | static const FunctionInfo functions[] = { | 496 | static const FunctionInfo functions[] = { |
| 483 | {1, &IGeneralService::GetClientId, "GetClientId"}, | 497 | {1, &IGeneralService::GetClientId, "GetClientId"}, |
| @@ -528,6 +542,8 @@ IGeneralService::IGeneralService(Core::System& system_) | |||
| 528 | RegisterHandlers(functions); | 542 | RegisterHandlers(functions); |
| 529 | } | 543 | } |
| 530 | 544 | ||
| 545 | IGeneralService::~IGeneralService() = default; | ||
| 546 | |||
| 531 | class NetworkInterface final : public ServiceFramework<NetworkInterface> { | 547 | class NetworkInterface final : public ServiceFramework<NetworkInterface> { |
| 532 | public: | 548 | public: |
| 533 | explicit NetworkInterface(const char* name, Core::System& system_) | 549 | explicit NetworkInterface(const char* name, Core::System& system_) |
diff --git a/src/core/hle/service/nifm/nifm.h b/src/core/hle/service/nifm/nifm.h index 5f62d0014..48161be28 100644 --- a/src/core/hle/service/nifm/nifm.h +++ b/src/core/hle/service/nifm/nifm.h | |||
| @@ -3,6 +3,11 @@ | |||
| 3 | 3 | ||
| 4 | #pragma once | 4 | #pragma once |
| 5 | 5 | ||
| 6 | #include "core/hle/service/service.h" | ||
| 7 | #include "network/network.h" | ||
| 8 | #include "network/room.h" | ||
| 9 | #include "network/room_member.h" | ||
| 10 | |||
| 6 | namespace Core { | 11 | namespace Core { |
| 7 | class System; | 12 | class System; |
| 8 | } | 13 | } |
| @@ -16,4 +21,26 @@ namespace Service::NIFM { | |||
| 16 | /// Registers all NIFM services with the specified service manager. | 21 | /// Registers all NIFM services with the specified service manager. |
| 17 | void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system); | 22 | void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system); |
| 18 | 23 | ||
| 24 | class IGeneralService final : public ServiceFramework<IGeneralService> { | ||
| 25 | public: | ||
| 26 | explicit IGeneralService(Core::System& system_); | ||
| 27 | ~IGeneralService() override; | ||
| 28 | |||
| 29 | private: | ||
| 30 | void GetClientId(Kernel::HLERequestContext& ctx); | ||
| 31 | void CreateScanRequest(Kernel::HLERequestContext& ctx); | ||
| 32 | void CreateRequest(Kernel::HLERequestContext& ctx); | ||
| 33 | void GetCurrentNetworkProfile(Kernel::HLERequestContext& ctx); | ||
| 34 | void RemoveNetworkProfile(Kernel::HLERequestContext& ctx); | ||
| 35 | void GetCurrentIpAddress(Kernel::HLERequestContext& ctx); | ||
| 36 | void CreateTemporaryNetworkProfile(Kernel::HLERequestContext& ctx); | ||
| 37 | void GetCurrentIpConfigInfo(Kernel::HLERequestContext& ctx); | ||
| 38 | void IsWirelessCommunicationEnabled(Kernel::HLERequestContext& ctx); | ||
| 39 | void GetInternetConnectionStatus(Kernel::HLERequestContext& ctx); | ||
| 40 | void IsEthernetCommunicationEnabled(Kernel::HLERequestContext& ctx); | ||
| 41 | void IsAnyInternetRequestAccepted(Kernel::HLERequestContext& ctx); | ||
| 42 | |||
| 43 | Network::RoomNetwork& network; | ||
| 44 | }; | ||
| 45 | |||
| 19 | } // namespace Service::NIFM | 46 | } // namespace Service::NIFM |
diff --git a/src/core/hle/service/ns/pl_u.cpp b/src/core/hle/service/ns/iplatform_service_manager.cpp index cc11f3e08..fd047ff26 100644 --- a/src/core/hle/service/ns/pl_u.cpp +++ b/src/core/hle/service/ns/iplatform_service_manager.cpp | |||
| @@ -20,7 +20,7 @@ | |||
| 20 | #include "core/hle/kernel/kernel.h" | 20 | #include "core/hle/kernel/kernel.h" |
| 21 | #include "core/hle/kernel/physical_memory.h" | 21 | #include "core/hle/kernel/physical_memory.h" |
| 22 | #include "core/hle/service/filesystem/filesystem.h" | 22 | #include "core/hle/service/filesystem/filesystem.h" |
| 23 | #include "core/hle/service/ns/pl_u.h" | 23 | #include "core/hle/service/ns/iplatform_service_manager.h" |
| 24 | 24 | ||
| 25 | namespace Service::NS { | 25 | namespace Service::NS { |
| 26 | 26 | ||
| @@ -99,7 +99,7 @@ static u32 GetU32Swapped(const u8* data) { | |||
| 99 | return Common::swap32(value); | 99 | return Common::swap32(value); |
| 100 | } | 100 | } |
| 101 | 101 | ||
| 102 | struct PL_U::Impl { | 102 | struct IPlatformServiceManager::Impl { |
| 103 | const FontRegion& GetSharedFontRegion(std::size_t index) const { | 103 | const FontRegion& GetSharedFontRegion(std::size_t index) const { |
| 104 | if (index >= shared_font_regions.size() || shared_font_regions.empty()) { | 104 | if (index >= shared_font_regions.size() || shared_font_regions.empty()) { |
| 105 | // No font fallback | 105 | // No font fallback |
| @@ -134,16 +134,16 @@ struct PL_U::Impl { | |||
| 134 | std::vector<FontRegion> shared_font_regions; | 134 | std::vector<FontRegion> shared_font_regions; |
| 135 | }; | 135 | }; |
| 136 | 136 | ||
| 137 | PL_U::PL_U(Core::System& system_) | 137 | IPlatformServiceManager::IPlatformServiceManager(Core::System& system_, const char* service_name_) |
| 138 | : ServiceFramework{system_, "pl:u"}, impl{std::make_unique<Impl>()} { | 138 | : ServiceFramework{system_, service_name_}, impl{std::make_unique<Impl>()} { |
| 139 | // clang-format off | 139 | // clang-format off |
| 140 | static const FunctionInfo functions[] = { | 140 | static const FunctionInfo functions[] = { |
| 141 | {0, &PL_U::RequestLoad, "RequestLoad"}, | 141 | {0, &IPlatformServiceManager::RequestLoad, "RequestLoad"}, |
| 142 | {1, &PL_U::GetLoadState, "GetLoadState"}, | 142 | {1, &IPlatformServiceManager::GetLoadState, "GetLoadState"}, |
| 143 | {2, &PL_U::GetSize, "GetSize"}, | 143 | {2, &IPlatformServiceManager::GetSize, "GetSize"}, |
| 144 | {3, &PL_U::GetSharedMemoryAddressOffset, "GetSharedMemoryAddressOffset"}, | 144 | {3, &IPlatformServiceManager::GetSharedMemoryAddressOffset, "GetSharedMemoryAddressOffset"}, |
| 145 | {4, &PL_U::GetSharedMemoryNativeHandle, "GetSharedMemoryNativeHandle"}, | 145 | {4, &IPlatformServiceManager::GetSharedMemoryNativeHandle, "GetSharedMemoryNativeHandle"}, |
| 146 | {5, &PL_U::GetSharedFontInOrderOfPriority, "GetSharedFontInOrderOfPriority"}, | 146 | {5, &IPlatformServiceManager::GetSharedFontInOrderOfPriority, "GetSharedFontInOrderOfPriority"}, |
| 147 | {6, nullptr, "GetSharedFontInOrderOfPriorityForSystem"}, | 147 | {6, nullptr, "GetSharedFontInOrderOfPriorityForSystem"}, |
| 148 | {100, nullptr, "RequestApplicationFunctionAuthorization"}, | 148 | {100, nullptr, "RequestApplicationFunctionAuthorization"}, |
| 149 | {101, nullptr, "RequestApplicationFunctionAuthorizationByProcessId"}, | 149 | {101, nullptr, "RequestApplicationFunctionAuthorizationByProcessId"}, |
| @@ -206,9 +206,9 @@ PL_U::PL_U(Core::System& system_) | |||
| 206 | } | 206 | } |
| 207 | } | 207 | } |
| 208 | 208 | ||
| 209 | PL_U::~PL_U() = default; | 209 | IPlatformServiceManager::~IPlatformServiceManager() = default; |
| 210 | 210 | ||
| 211 | void PL_U::RequestLoad(Kernel::HLERequestContext& ctx) { | 211 | void IPlatformServiceManager::RequestLoad(Kernel::HLERequestContext& ctx) { |
| 212 | IPC::RequestParser rp{ctx}; | 212 | IPC::RequestParser rp{ctx}; |
| 213 | const u32 shared_font_type{rp.Pop<u32>()}; | 213 | const u32 shared_font_type{rp.Pop<u32>()}; |
| 214 | // Games don't call this so all fonts should be loaded | 214 | // Games don't call this so all fonts should be loaded |
| @@ -218,7 +218,7 @@ void PL_U::RequestLoad(Kernel::HLERequestContext& ctx) { | |||
| 218 | rb.Push(ResultSuccess); | 218 | rb.Push(ResultSuccess); |
| 219 | } | 219 | } |
| 220 | 220 | ||
| 221 | void PL_U::GetLoadState(Kernel::HLERequestContext& ctx) { | 221 | void IPlatformServiceManager::GetLoadState(Kernel::HLERequestContext& ctx) { |
| 222 | IPC::RequestParser rp{ctx}; | 222 | IPC::RequestParser rp{ctx}; |
| 223 | const u32 font_id{rp.Pop<u32>()}; | 223 | const u32 font_id{rp.Pop<u32>()}; |
| 224 | LOG_DEBUG(Service_NS, "called, font_id={}", font_id); | 224 | LOG_DEBUG(Service_NS, "called, font_id={}", font_id); |
| @@ -228,7 +228,7 @@ void PL_U::GetLoadState(Kernel::HLERequestContext& ctx) { | |||
| 228 | rb.Push<u32>(static_cast<u32>(LoadState::Done)); | 228 | rb.Push<u32>(static_cast<u32>(LoadState::Done)); |
| 229 | } | 229 | } |
| 230 | 230 | ||
| 231 | void PL_U::GetSize(Kernel::HLERequestContext& ctx) { | 231 | void IPlatformServiceManager::GetSize(Kernel::HLERequestContext& ctx) { |
| 232 | IPC::RequestParser rp{ctx}; | 232 | IPC::RequestParser rp{ctx}; |
| 233 | const u32 font_id{rp.Pop<u32>()}; | 233 | const u32 font_id{rp.Pop<u32>()}; |
| 234 | LOG_DEBUG(Service_NS, "called, font_id={}", font_id); | 234 | LOG_DEBUG(Service_NS, "called, font_id={}", font_id); |
| @@ -238,7 +238,7 @@ void PL_U::GetSize(Kernel::HLERequestContext& ctx) { | |||
| 238 | rb.Push<u32>(impl->GetSharedFontRegion(font_id).size); | 238 | rb.Push<u32>(impl->GetSharedFontRegion(font_id).size); |
| 239 | } | 239 | } |
| 240 | 240 | ||
| 241 | void PL_U::GetSharedMemoryAddressOffset(Kernel::HLERequestContext& ctx) { | 241 | void IPlatformServiceManager::GetSharedMemoryAddressOffset(Kernel::HLERequestContext& ctx) { |
| 242 | IPC::RequestParser rp{ctx}; | 242 | IPC::RequestParser rp{ctx}; |
| 243 | const u32 font_id{rp.Pop<u32>()}; | 243 | const u32 font_id{rp.Pop<u32>()}; |
| 244 | LOG_DEBUG(Service_NS, "called, font_id={}", font_id); | 244 | LOG_DEBUG(Service_NS, "called, font_id={}", font_id); |
| @@ -248,7 +248,7 @@ void PL_U::GetSharedMemoryAddressOffset(Kernel::HLERequestContext& ctx) { | |||
| 248 | rb.Push<u32>(impl->GetSharedFontRegion(font_id).offset); | 248 | rb.Push<u32>(impl->GetSharedFontRegion(font_id).offset); |
| 249 | } | 249 | } |
| 250 | 250 | ||
| 251 | void PL_U::GetSharedMemoryNativeHandle(Kernel::HLERequestContext& ctx) { | 251 | void IPlatformServiceManager::GetSharedMemoryNativeHandle(Kernel::HLERequestContext& ctx) { |
| 252 | // Map backing memory for the font data | 252 | // Map backing memory for the font data |
| 253 | LOG_DEBUG(Service_NS, "called"); | 253 | LOG_DEBUG(Service_NS, "called"); |
| 254 | 254 | ||
| @@ -261,7 +261,7 @@ void PL_U::GetSharedMemoryNativeHandle(Kernel::HLERequestContext& ctx) { | |||
| 261 | rb.PushCopyObjects(&kernel.GetFontSharedMem()); | 261 | rb.PushCopyObjects(&kernel.GetFontSharedMem()); |
| 262 | } | 262 | } |
| 263 | 263 | ||
| 264 | void PL_U::GetSharedFontInOrderOfPriority(Kernel::HLERequestContext& ctx) { | 264 | void IPlatformServiceManager::GetSharedFontInOrderOfPriority(Kernel::HLERequestContext& ctx) { |
| 265 | IPC::RequestParser rp{ctx}; | 265 | IPC::RequestParser rp{ctx}; |
| 266 | const u64 language_code{rp.Pop<u64>()}; // TODO(ogniK): Find out what this is used for | 266 | const u64 language_code{rp.Pop<u64>()}; // TODO(ogniK): Find out what this is used for |
| 267 | LOG_DEBUG(Service_NS, "called, language_code={:X}", language_code); | 267 | LOG_DEBUG(Service_NS, "called, language_code={:X}", language_code); |
diff --git a/src/core/hle/service/ns/pl_u.h b/src/core/hle/service/ns/iplatform_service_manager.h index 07d0ac934..ed6eda89f 100644 --- a/src/core/hle/service/ns/pl_u.h +++ b/src/core/hle/service/ns/iplatform_service_manager.h | |||
| @@ -36,10 +36,10 @@ constexpr std::array<std::pair<FontArchives, const char*>, 7> SHARED_FONTS{ | |||
| 36 | void DecryptSharedFontToTTF(const std::vector<u32>& input, std::vector<u8>& output); | 36 | void DecryptSharedFontToTTF(const std::vector<u32>& input, std::vector<u8>& output); |
| 37 | void EncryptSharedFont(const std::vector<u32>& input, std::vector<u8>& output, std::size_t& offset); | 37 | void EncryptSharedFont(const std::vector<u32>& input, std::vector<u8>& output, std::size_t& offset); |
| 38 | 38 | ||
| 39 | class PL_U final : public ServiceFramework<PL_U> { | 39 | class IPlatformServiceManager final : public ServiceFramework<IPlatformServiceManager> { |
| 40 | public: | 40 | public: |
| 41 | explicit PL_U(Core::System& system_); | 41 | explicit IPlatformServiceManager(Core::System& system_, const char* service_name_); |
| 42 | ~PL_U() override; | 42 | ~IPlatformServiceManager() override; |
| 43 | 43 | ||
| 44 | private: | 44 | private: |
| 45 | void RequestLoad(Kernel::HLERequestContext& ctx); | 45 | void RequestLoad(Kernel::HLERequestContext& ctx); |
diff --git a/src/core/hle/service/ns/ns.cpp b/src/core/hle/service/ns/ns.cpp index aafc8fe03..f7318c3cb 100644 --- a/src/core/hle/service/ns/ns.cpp +++ b/src/core/hle/service/ns/ns.cpp | |||
| @@ -9,10 +9,10 @@ | |||
| 9 | #include "core/file_sys/vfs.h" | 9 | #include "core/file_sys/vfs.h" |
| 10 | #include "core/hle/ipc_helpers.h" | 10 | #include "core/hle/ipc_helpers.h" |
| 11 | #include "core/hle/service/ns/errors.h" | 11 | #include "core/hle/service/ns/errors.h" |
| 12 | #include "core/hle/service/ns/iplatform_service_manager.h" | ||
| 12 | #include "core/hle/service/ns/language.h" | 13 | #include "core/hle/service/ns/language.h" |
| 13 | #include "core/hle/service/ns/ns.h" | 14 | #include "core/hle/service/ns/ns.h" |
| 14 | #include "core/hle/service/ns/pdm_qry.h" | 15 | #include "core/hle/service/ns/pdm_qry.h" |
| 15 | #include "core/hle/service/ns/pl_u.h" | ||
| 16 | #include "core/hle/service/set/set.h" | 16 | #include "core/hle/service/set/set.h" |
| 17 | 17 | ||
| 18 | namespace Service::NS { | 18 | namespace Service::NS { |
| @@ -764,7 +764,8 @@ void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system | |||
| 764 | 764 | ||
| 765 | std::make_shared<PDM_QRY>(system)->InstallAsService(service_manager); | 765 | std::make_shared<PDM_QRY>(system)->InstallAsService(service_manager); |
| 766 | 766 | ||
| 767 | std::make_shared<PL_U>(system)->InstallAsService(service_manager); | 767 | std::make_shared<IPlatformServiceManager>(system, "pl:s")->InstallAsService(service_manager); |
| 768 | std::make_shared<IPlatformServiceManager>(system, "pl:u")->InstallAsService(service_manager); | ||
| 768 | } | 769 | } |
| 769 | 770 | ||
| 770 | } // namespace Service::NS | 771 | } // namespace Service::NS |
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp index 2a5128c60..a7385fce8 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.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 "audio_core/audio_core.h" | ||
| 4 | #include "common/assert.h" | 5 | #include "common/assert.h" |
| 5 | #include "common/logging/log.h" | 6 | #include "common/logging/log.h" |
| 6 | #include "core/core.h" | 7 | #include "core/core.h" |
| @@ -65,7 +66,10 @@ NvResult nvhost_nvdec::Ioctl3(DeviceFD fd, Ioctl command, const std::vector<u8>& | |||
| 65 | return NvResult::NotImplemented; | 66 | return NvResult::NotImplemented; |
| 66 | } | 67 | } |
| 67 | 68 | ||
| 68 | void nvhost_nvdec::OnOpen(DeviceFD fd) {} | 69 | void nvhost_nvdec::OnOpen(DeviceFD fd) { |
| 70 | LOG_INFO(Service_NVDRV, "NVDEC video stream started"); | ||
| 71 | system.AudioCore().SetNVDECActive(true); | ||
| 72 | } | ||
| 69 | 73 | ||
| 70 | void nvhost_nvdec::OnClose(DeviceFD fd) { | 74 | void nvhost_nvdec::OnClose(DeviceFD fd) { |
| 71 | LOG_INFO(Service_NVDRV, "NVDEC video stream ended"); | 75 | LOG_INFO(Service_NVDRV, "NVDEC video stream ended"); |
| @@ -73,6 +77,7 @@ void nvhost_nvdec::OnClose(DeviceFD fd) { | |||
| 73 | if (iter != fd_to_id.end()) { | 77 | if (iter != fd_to_id.end()) { |
| 74 | system.GPU().ClearCdmaInstance(iter->second); | 78 | system.GPU().ClearCdmaInstance(iter->second); |
| 75 | } | 79 | } |
| 80 | system.AudioCore().SetNVDECActive(false); | ||
| 76 | } | 81 | } |
| 77 | 82 | ||
| 78 | } // namespace Service::Nvidia::Devices | 83 | } // namespace Service::Nvidia::Devices |
diff --git a/src/core/hle/service/nvdrv/devices/nvmap.cpp b/src/core/hle/service/nvdrv/devices/nvmap.cpp index 728bfa00b..d8518149d 100644 --- a/src/core/hle/service/nvdrv/devices/nvmap.cpp +++ b/src/core/hle/service/nvdrv/devices/nvmap.cpp | |||
| @@ -198,7 +198,7 @@ NvResult nvmap::IocParam(const std::vector<u8>& input, std::vector<u8>& output) | |||
| 198 | IocParamParams params; | 198 | IocParamParams params; |
| 199 | std::memcpy(¶ms, input.data(), sizeof(params)); | 199 | std::memcpy(¶ms, input.data(), sizeof(params)); |
| 200 | 200 | ||
| 201 | LOG_WARNING(Service_NVDRV, "(STUBBED) called type={}", params.param); | 201 | LOG_DEBUG(Service_NVDRV, "(STUBBED) called type={}", params.param); |
| 202 | 202 | ||
| 203 | auto object = GetObject(params.handle); | 203 | auto object = GetObject(params.handle); |
| 204 | if (!object) { | 204 | if (!object) { |
| @@ -243,7 +243,7 @@ NvResult nvmap::IocFree(const std::vector<u8>& input, std::vector<u8>& output) { | |||
| 243 | IocFreeParams params; | 243 | IocFreeParams params; |
| 244 | std::memcpy(¶ms, input.data(), sizeof(params)); | 244 | std::memcpy(¶ms, input.data(), sizeof(params)); |
| 245 | 245 | ||
| 246 | LOG_WARNING(Service_NVDRV, "(STUBBED) called"); | 246 | LOG_DEBUG(Service_NVDRV, "(STUBBED) called"); |
| 247 | 247 | ||
| 248 | auto itr = handles.find(params.handle); | 248 | auto itr = handles.find(params.handle); |
| 249 | if (itr == handles.end()) { | 249 | if (itr == handles.end()) { |
diff --git a/src/core/hle/service/pcv/pcv.cpp b/src/core/hle/service/pcv/pcv.cpp index 0989474be..f7a497a14 100644 --- a/src/core/hle/service/pcv/pcv.cpp +++ b/src/core/hle/service/pcv/pcv.cpp | |||
| @@ -3,6 +3,7 @@ | |||
| 3 | 3 | ||
| 4 | #include <memory> | 4 | #include <memory> |
| 5 | 5 | ||
| 6 | #include "core/hle/ipc_helpers.h" | ||
| 6 | #include "core/hle/service/pcv/pcv.h" | 7 | #include "core/hle/service/pcv/pcv.h" |
| 7 | #include "core/hle/service/service.h" | 8 | #include "core/hle/service/service.h" |
| 8 | #include "core/hle/service/sm/sm.h" | 9 | #include "core/hle/service/sm/sm.h" |
| @@ -77,10 +78,102 @@ public: | |||
| 77 | } | 78 | } |
| 78 | }; | 79 | }; |
| 79 | 80 | ||
| 81 | class IClkrstSession final : public ServiceFramework<IClkrstSession> { | ||
| 82 | public: | ||
| 83 | explicit IClkrstSession(Core::System& system_, DeviceCode deivce_code_) | ||
| 84 | : ServiceFramework{system_, "IClkrstSession"}, deivce_code(deivce_code_) { | ||
| 85 | // clang-format off | ||
| 86 | static const FunctionInfo functions[] = { | ||
| 87 | {0, nullptr, "SetClockEnabled"}, | ||
| 88 | {1, nullptr, "SetClockDisabled"}, | ||
| 89 | {2, nullptr, "SetResetAsserted"}, | ||
| 90 | {3, nullptr, "SetResetDeasserted"}, | ||
| 91 | {4, nullptr, "SetPowerEnabled"}, | ||
| 92 | {5, nullptr, "SetPowerDisabled"}, | ||
| 93 | {6, nullptr, "GetState"}, | ||
| 94 | {7, &IClkrstSession::SetClockRate, "SetClockRate"}, | ||
| 95 | {8, &IClkrstSession::GetClockRate, "GetClockRate"}, | ||
| 96 | {9, nullptr, "SetMinVClockRate"}, | ||
| 97 | {10, nullptr, "GetPossibleClockRates"}, | ||
| 98 | {11, nullptr, "GetDvfsTable"}, | ||
| 99 | }; | ||
| 100 | // clang-format on | ||
| 101 | RegisterHandlers(functions); | ||
| 102 | } | ||
| 103 | |||
| 104 | private: | ||
| 105 | void SetClockRate(Kernel::HLERequestContext& ctx) { | ||
| 106 | IPC::RequestParser rp{ctx}; | ||
| 107 | clock_rate = rp.Pop<u32>(); | ||
| 108 | LOG_DEBUG(Service_PCV, "(STUBBED) called, clock_rate={}", clock_rate); | ||
| 109 | |||
| 110 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 111 | rb.Push(ResultSuccess); | ||
| 112 | } | ||
| 113 | |||
| 114 | void GetClockRate(Kernel::HLERequestContext& ctx) { | ||
| 115 | LOG_DEBUG(Service_PCV, "(STUBBED) called"); | ||
| 116 | |||
| 117 | IPC::ResponseBuilder rb{ctx, 3}; | ||
| 118 | rb.Push(ResultSuccess); | ||
| 119 | rb.Push<u32>(clock_rate); | ||
| 120 | } | ||
| 121 | |||
| 122 | DeviceCode deivce_code; | ||
| 123 | u32 clock_rate{}; | ||
| 124 | }; | ||
| 125 | |||
| 126 | class CLKRST final : public ServiceFramework<CLKRST> { | ||
| 127 | public: | ||
| 128 | explicit CLKRST(Core::System& system_, const char* name) : ServiceFramework{system_, name} { | ||
| 129 | // clang-format off | ||
| 130 | static const FunctionInfo functions[] = { | ||
| 131 | {0, &CLKRST::OpenSession, "OpenSession"}, | ||
| 132 | {1, nullptr, "GetTemperatureThresholds"}, | ||
| 133 | {2, nullptr, "SetTemperature"}, | ||
| 134 | {3, nullptr, "GetModuleStateTable"}, | ||
| 135 | {4, nullptr, "GetModuleStateTableEvent"}, | ||
| 136 | {5, nullptr, "GetModuleStateTableMaxCount"}, | ||
| 137 | }; | ||
| 138 | // clang-format on | ||
| 139 | |||
| 140 | RegisterHandlers(functions); | ||
| 141 | } | ||
| 142 | |||
| 143 | private: | ||
| 144 | void OpenSession(Kernel::HLERequestContext& ctx) { | ||
| 145 | IPC::RequestParser rp{ctx}; | ||
| 146 | const auto device_code = static_cast<DeviceCode>(rp.Pop<u32>()); | ||
| 147 | const auto unkonwn_input = rp.Pop<u32>(); | ||
| 148 | |||
| 149 | LOG_DEBUG(Service_PCV, "called, device_code={}, input={}", device_code, unkonwn_input); | ||
| 150 | |||
| 151 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; | ||
| 152 | rb.Push(ResultSuccess); | ||
| 153 | rb.PushIpcInterface<IClkrstSession>(system, device_code); | ||
| 154 | } | ||
| 155 | }; | ||
| 156 | |||
| 157 | class CLKRST_A final : public ServiceFramework<CLKRST_A> { | ||
| 158 | public: | ||
| 159 | explicit CLKRST_A(Core::System& system_) : ServiceFramework{system_, "clkrst:a"} { | ||
| 160 | // clang-format off | ||
| 161 | static const FunctionInfo functions[] = { | ||
| 162 | {0, nullptr, "ReleaseControl"}, | ||
| 163 | }; | ||
| 164 | // clang-format on | ||
| 165 | |||
| 166 | RegisterHandlers(functions); | ||
| 167 | } | ||
| 168 | }; | ||
| 169 | |||
| 80 | void InstallInterfaces(SM::ServiceManager& sm, Core::System& system) { | 170 | void InstallInterfaces(SM::ServiceManager& sm, Core::System& system) { |
| 81 | std::make_shared<PCV>(system)->InstallAsService(sm); | 171 | std::make_shared<PCV>(system)->InstallAsService(sm); |
| 82 | std::make_shared<PCV_ARB>(system)->InstallAsService(sm); | 172 | std::make_shared<PCV_ARB>(system)->InstallAsService(sm); |
| 83 | std::make_shared<PCV_IMM>(system)->InstallAsService(sm); | 173 | std::make_shared<PCV_IMM>(system)->InstallAsService(sm); |
| 174 | std::make_shared<CLKRST>(system, "clkrst")->InstallAsService(sm); | ||
| 175 | std::make_shared<CLKRST>(system, "clkrst:i")->InstallAsService(sm); | ||
| 176 | std::make_shared<CLKRST_A>(system)->InstallAsService(sm); | ||
| 84 | } | 177 | } |
| 85 | 178 | ||
| 86 | } // namespace Service::PCV | 179 | } // namespace Service::PCV |
diff --git a/src/core/hle/service/pcv/pcv.h b/src/core/hle/service/pcv/pcv.h index a42e6f8f6..6b26b6fa7 100644 --- a/src/core/hle/service/pcv/pcv.h +++ b/src/core/hle/service/pcv/pcv.h | |||
| @@ -13,6 +13,97 @@ class ServiceManager; | |||
| 13 | 13 | ||
| 14 | namespace Service::PCV { | 14 | namespace Service::PCV { |
| 15 | 15 | ||
| 16 | enum class DeviceCode : u32 { | ||
| 17 | Cpu = 0x40000001, | ||
| 18 | Gpu = 0x40000002, | ||
| 19 | I2s1 = 0x40000003, | ||
| 20 | I2s2 = 0x40000004, | ||
| 21 | I2s3 = 0x40000005, | ||
| 22 | Pwm = 0x40000006, | ||
| 23 | I2c1 = 0x02000001, | ||
| 24 | I2c2 = 0x02000002, | ||
| 25 | I2c3 = 0x02000003, | ||
| 26 | I2c4 = 0x02000004, | ||
| 27 | I2c5 = 0x02000005, | ||
| 28 | I2c6 = 0x02000006, | ||
| 29 | Spi1 = 0x07000000, | ||
| 30 | Spi2 = 0x07000001, | ||
| 31 | Spi3 = 0x07000002, | ||
| 32 | Spi4 = 0x07000003, | ||
| 33 | Disp1 = 0x40000011, | ||
| 34 | Disp2 = 0x40000012, | ||
| 35 | Isp = 0x40000013, | ||
| 36 | Vi = 0x40000014, | ||
| 37 | Sdmmc1 = 0x40000015, | ||
| 38 | Sdmmc2 = 0x40000016, | ||
| 39 | Sdmmc3 = 0x40000017, | ||
| 40 | Sdmmc4 = 0x40000018, | ||
| 41 | Owr = 0x40000019, | ||
| 42 | Csite = 0x4000001A, | ||
| 43 | Tsec = 0x4000001B, | ||
| 44 | Mselect = 0x4000001C, | ||
| 45 | Hda2codec2x = 0x4000001D, | ||
| 46 | Actmon = 0x4000001E, | ||
| 47 | I2cSlow = 0x4000001F, | ||
| 48 | Sor1 = 0x40000020, | ||
| 49 | Sata = 0x40000021, | ||
| 50 | Hda = 0x40000022, | ||
| 51 | XusbCoreHostSrc = 0x40000023, | ||
| 52 | XusbFalconSrc = 0x40000024, | ||
| 53 | XusbFsSrc = 0x40000025, | ||
| 54 | XusbCoreDevSrc = 0x40000026, | ||
| 55 | XusbSsSrc = 0x40000027, | ||
| 56 | UartA = 0x03000001, | ||
| 57 | UartB = 0x35000405, | ||
| 58 | UartC = 0x3500040F, | ||
| 59 | UartD = 0x37000001, | ||
| 60 | Host1x = 0x4000002C, | ||
| 61 | Entropy = 0x4000002D, | ||
| 62 | SocTherm = 0x4000002E, | ||
| 63 | Vic = 0x4000002F, | ||
| 64 | Nvenc = 0x40000030, | ||
| 65 | Nvjpg = 0x40000031, | ||
| 66 | Nvdec = 0x40000032, | ||
| 67 | Qspi = 0x40000033, | ||
| 68 | ViI2c = 0x40000034, | ||
| 69 | Tsecb = 0x40000035, | ||
| 70 | Ape = 0x40000036, | ||
| 71 | AudioDsp = 0x40000037, | ||
| 72 | AudioUart = 0x40000038, | ||
| 73 | Emc = 0x40000039, | ||
| 74 | Plle = 0x4000003A, | ||
| 75 | PlleHwSeq = 0x4000003B, | ||
| 76 | Dsi = 0x4000003C, | ||
| 77 | Maud = 0x4000003D, | ||
| 78 | Dpaux1 = 0x4000003E, | ||
| 79 | MipiCal = 0x4000003F, | ||
| 80 | UartFstMipiCal = 0x40000040, | ||
| 81 | Osc = 0x40000041, | ||
| 82 | SysBus = 0x40000042, | ||
| 83 | SorSafe = 0x40000043, | ||
| 84 | XusbSs = 0x40000044, | ||
| 85 | XusbHost = 0x40000045, | ||
| 86 | XusbDevice = 0x40000046, | ||
| 87 | Extperiph1 = 0x40000047, | ||
| 88 | Ahub = 0x40000048, | ||
| 89 | Hda2hdmicodec = 0x40000049, | ||
| 90 | Gpuaux = 0x4000004A, | ||
| 91 | UsbD = 0x4000004B, | ||
| 92 | Usb2 = 0x4000004C, | ||
| 93 | Pcie = 0x4000004D, | ||
| 94 | Afi = 0x4000004E, | ||
| 95 | PciExClk = 0x4000004F, | ||
| 96 | PExUsbPhy = 0x40000050, | ||
| 97 | XUsbPadCtl = 0x40000051, | ||
| 98 | Apbdma = 0x40000052, | ||
| 99 | Usb2TrkClk = 0x40000053, | ||
| 100 | XUsbIoPll = 0x40000054, | ||
| 101 | XUsbIoPllHwSeq = 0x40000055, | ||
| 102 | Cec = 0x40000056, | ||
| 103 | Extperiph2 = 0x40000057, | ||
| 104 | OscClk = 0x40000080 | ||
| 105 | }; | ||
| 106 | |||
| 16 | void InstallInterfaces(SM::ServiceManager& sm, Core::System& system); | 107 | void InstallInterfaces(SM::ServiceManager& sm, Core::System& system); |
| 17 | 108 | ||
| 18 | } // namespace Service::PCV | 109 | } // namespace Service::PCV |
diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp index c64291e7f..dadaf897f 100644 --- a/src/core/hle/service/service.cpp +++ b/src/core/hle/service/service.cpp | |||
| @@ -194,13 +194,16 @@ Result ServiceFrameworkBase::HandleSyncRequest(Kernel::KServerSession& session, | |||
| 194 | Kernel::HLERequestContext& ctx) { | 194 | Kernel::HLERequestContext& ctx) { |
| 195 | const auto guard = LockService(); | 195 | const auto guard = LockService(); |
| 196 | 196 | ||
| 197 | Result result = ResultSuccess; | ||
| 198 | |||
| 197 | switch (ctx.GetCommandType()) { | 199 | switch (ctx.GetCommandType()) { |
| 198 | case IPC::CommandType::Close: | 200 | case IPC::CommandType::Close: |
| 199 | case IPC::CommandType::TIPC_Close: { | 201 | case IPC::CommandType::TIPC_Close: { |
| 200 | session.Close(); | 202 | session.Close(); |
| 201 | IPC::ResponseBuilder rb{ctx, 2}; | 203 | IPC::ResponseBuilder rb{ctx, 2}; |
| 202 | rb.Push(ResultSuccess); | 204 | rb.Push(ResultSuccess); |
| 203 | return IPC::ERR_REMOTE_PROCESS_DEAD; | 205 | result = IPC::ERR_REMOTE_PROCESS_DEAD; |
| 206 | break; | ||
| 204 | } | 207 | } |
| 205 | case IPC::CommandType::ControlWithContext: | 208 | case IPC::CommandType::ControlWithContext: |
| 206 | case IPC::CommandType::Control: { | 209 | case IPC::CommandType::Control: { |
| @@ -227,7 +230,7 @@ Result ServiceFrameworkBase::HandleSyncRequest(Kernel::KServerSession& session, | |||
| 227 | ctx.WriteToOutgoingCommandBuffer(ctx.GetThread()); | 230 | ctx.WriteToOutgoingCommandBuffer(ctx.GetThread()); |
| 228 | } | 231 | } |
| 229 | 232 | ||
| 230 | return ResultSuccess; | 233 | return result; |
| 231 | } | 234 | } |
| 232 | 235 | ||
| 233 | /// Initialize Services | 236 | /// Initialize Services |
diff --git a/src/core/hle/service/sockets/bsd.cpp b/src/core/hle/service/sockets/bsd.cpp index c7194731e..cc679cc81 100644 --- a/src/core/hle/service/sockets/bsd.cpp +++ b/src/core/hle/service/sockets/bsd.cpp | |||
| @@ -9,12 +9,16 @@ | |||
| 9 | #include <fmt/format.h> | 9 | #include <fmt/format.h> |
| 10 | 10 | ||
| 11 | #include "common/microprofile.h" | 11 | #include "common/microprofile.h" |
| 12 | #include "common/socket_types.h" | ||
| 13 | #include "core/core.h" | ||
| 12 | #include "core/hle/ipc_helpers.h" | 14 | #include "core/hle/ipc_helpers.h" |
| 13 | #include "core/hle/kernel/k_thread.h" | 15 | #include "core/hle/kernel/k_thread.h" |
| 14 | #include "core/hle/service/sockets/bsd.h" | 16 | #include "core/hle/service/sockets/bsd.h" |
| 15 | #include "core/hle/service/sockets/sockets_translate.h" | 17 | #include "core/hle/service/sockets/sockets_translate.h" |
| 16 | #include "core/internal_network/network.h" | 18 | #include "core/internal_network/network.h" |
| 19 | #include "core/internal_network/socket_proxy.h" | ||
| 17 | #include "core/internal_network/sockets.h" | 20 | #include "core/internal_network/sockets.h" |
| 21 | #include "network/network.h" | ||
| 18 | 22 | ||
| 19 | namespace Service::Sockets { | 23 | namespace Service::Sockets { |
| 20 | 24 | ||
| @@ -472,7 +476,13 @@ std::pair<s32, Errno> BSD::SocketImpl(Domain domain, Type type, Protocol protoco | |||
| 472 | 476 | ||
| 473 | LOG_INFO(Service, "New socket fd={}", fd); | 477 | LOG_INFO(Service, "New socket fd={}", fd); |
| 474 | 478 | ||
| 475 | descriptor.socket = std::make_unique<Network::Socket>(); | 479 | auto room_member = room_network.GetRoomMember().lock(); |
| 480 | if (room_member && room_member->IsConnected()) { | ||
| 481 | descriptor.socket = std::make_unique<Network::ProxySocket>(room_network); | ||
| 482 | } else { | ||
| 483 | descriptor.socket = std::make_unique<Network::Socket>(); | ||
| 484 | } | ||
| 485 | |||
| 476 | descriptor.socket->Initialize(Translate(domain), Translate(type), Translate(type, protocol)); | 486 | descriptor.socket->Initialize(Translate(domain), Translate(type), Translate(type, protocol)); |
| 477 | descriptor.is_connection_based = IsConnectionBased(type); | 487 | descriptor.is_connection_based = IsConnectionBased(type); |
| 478 | 488 | ||
| @@ -648,7 +658,7 @@ std::pair<s32, Errno> BSD::FcntlImpl(s32 fd, FcntlCmd cmd, s32 arg) { | |||
| 648 | ASSERT(arg == 0); | 658 | ASSERT(arg == 0); |
| 649 | return {descriptor.flags, Errno::SUCCESS}; | 659 | return {descriptor.flags, Errno::SUCCESS}; |
| 650 | case FcntlCmd::SETFL: { | 660 | case FcntlCmd::SETFL: { |
| 651 | const bool enable = (arg & FLAG_O_NONBLOCK) != 0; | 661 | const bool enable = (arg & Network::FLAG_O_NONBLOCK) != 0; |
| 652 | const Errno bsd_errno = Translate(descriptor.socket->SetNonBlock(enable)); | 662 | const Errno bsd_errno = Translate(descriptor.socket->SetNonBlock(enable)); |
| 653 | if (bsd_errno != Errno::SUCCESS) { | 663 | if (bsd_errno != Errno::SUCCESS) { |
| 654 | return {-1, bsd_errno}; | 664 | return {-1, bsd_errno}; |
| @@ -669,7 +679,7 @@ Errno BSD::SetSockOptImpl(s32 fd, u32 level, OptName optname, size_t optlen, con | |||
| 669 | return Errno::BADF; | 679 | return Errno::BADF; |
| 670 | } | 680 | } |
| 671 | 681 | ||
| 672 | Network::Socket* const socket = file_descriptors[fd]->socket.get(); | 682 | Network::SocketBase* const socket = file_descriptors[fd]->socket.get(); |
| 673 | 683 | ||
| 674 | if (optname == OptName::LINGER) { | 684 | if (optname == OptName::LINGER) { |
| 675 | ASSERT(optlen == sizeof(Linger)); | 685 | ASSERT(optlen == sizeof(Linger)); |
| @@ -724,6 +734,8 @@ std::pair<s32, Errno> BSD::RecvImpl(s32 fd, u32 flags, std::vector<u8>& message) | |||
| 724 | FileDescriptor& descriptor = *file_descriptors[fd]; | 734 | FileDescriptor& descriptor = *file_descriptors[fd]; |
| 725 | 735 | ||
| 726 | // Apply flags | 736 | // Apply flags |
| 737 | using Network::FLAG_MSG_DONTWAIT; | ||
| 738 | using Network::FLAG_O_NONBLOCK; | ||
| 727 | if ((flags & FLAG_MSG_DONTWAIT) != 0) { | 739 | if ((flags & FLAG_MSG_DONTWAIT) != 0) { |
| 728 | flags &= ~FLAG_MSG_DONTWAIT; | 740 | flags &= ~FLAG_MSG_DONTWAIT; |
| 729 | if ((descriptor.flags & FLAG_O_NONBLOCK) == 0) { | 741 | if ((descriptor.flags & FLAG_O_NONBLOCK) == 0) { |
| @@ -759,6 +771,8 @@ std::pair<s32, Errno> BSD::RecvFromImpl(s32 fd, u32 flags, std::vector<u8>& mess | |||
| 759 | } | 771 | } |
| 760 | 772 | ||
| 761 | // Apply flags | 773 | // Apply flags |
| 774 | using Network::FLAG_MSG_DONTWAIT; | ||
| 775 | using Network::FLAG_O_NONBLOCK; | ||
| 762 | if ((flags & FLAG_MSG_DONTWAIT) != 0) { | 776 | if ((flags & FLAG_MSG_DONTWAIT) != 0) { |
| 763 | flags &= ~FLAG_MSG_DONTWAIT; | 777 | flags &= ~FLAG_MSG_DONTWAIT; |
| 764 | if ((descriptor.flags & FLAG_O_NONBLOCK) == 0) { | 778 | if ((descriptor.flags & FLAG_O_NONBLOCK) == 0) { |
| @@ -857,8 +871,19 @@ void BSD::BuildErrnoResponse(Kernel::HLERequestContext& ctx, Errno bsd_errno) co | |||
| 857 | rb.PushEnum(bsd_errno); | 871 | rb.PushEnum(bsd_errno); |
| 858 | } | 872 | } |
| 859 | 873 | ||
| 874 | void BSD::OnProxyPacketReceived(const Network::ProxyPacket& packet) { | ||
| 875 | for (auto& optional_descriptor : file_descriptors) { | ||
| 876 | if (!optional_descriptor.has_value()) { | ||
| 877 | continue; | ||
| 878 | } | ||
| 879 | FileDescriptor& descriptor = *optional_descriptor; | ||
| 880 | descriptor.socket.get()->HandleProxyPacket(packet); | ||
| 881 | } | ||
| 882 | } | ||
| 883 | |||
| 860 | BSD::BSD(Core::System& system_, const char* name) | 884 | BSD::BSD(Core::System& system_, const char* name) |
| 861 | : ServiceFramework{system_, name, ServiceThreadType::CreateNew} { | 885 | : ServiceFramework{system_, name, ServiceThreadType::CreateNew}, room_network{ |
| 886 | system_.GetRoomNetwork()} { | ||
| 862 | // clang-format off | 887 | // clang-format off |
| 863 | static const FunctionInfo functions[] = { | 888 | static const FunctionInfo functions[] = { |
| 864 | {0, &BSD::RegisterClient, "RegisterClient"}, | 889 | {0, &BSD::RegisterClient, "RegisterClient"}, |
| @@ -899,9 +924,20 @@ BSD::BSD(Core::System& system_, const char* name) | |||
| 899 | // clang-format on | 924 | // clang-format on |
| 900 | 925 | ||
| 901 | RegisterHandlers(functions); | 926 | RegisterHandlers(functions); |
| 927 | |||
| 928 | if (auto room_member = room_network.GetRoomMember().lock()) { | ||
| 929 | proxy_packet_received = room_member->BindOnProxyPacketReceived( | ||
| 930 | [this](const Network::ProxyPacket& packet) { OnProxyPacketReceived(packet); }); | ||
| 931 | } else { | ||
| 932 | LOG_ERROR(Service, "Network isn't initalized"); | ||
| 933 | } | ||
| 902 | } | 934 | } |
| 903 | 935 | ||
| 904 | BSD::~BSD() = default; | 936 | BSD::~BSD() { |
| 937 | if (auto room_member = room_network.GetRoomMember().lock()) { | ||
| 938 | room_member->Unbind(proxy_packet_received); | ||
| 939 | } | ||
| 940 | } | ||
| 905 | 941 | ||
| 906 | BSDCFG::BSDCFG(Core::System& system_) : ServiceFramework{system_, "bsdcfg"} { | 942 | BSDCFG::BSDCFG(Core::System& system_) : ServiceFramework{system_, "bsdcfg"} { |
| 907 | // clang-format off | 943 | // clang-format off |
diff --git a/src/core/hle/service/sockets/bsd.h b/src/core/hle/service/sockets/bsd.h index 9ea36428d..81e855e0f 100644 --- a/src/core/hle/service/sockets/bsd.h +++ b/src/core/hle/service/sockets/bsd.h | |||
| @@ -7,14 +7,17 @@ | |||
| 7 | #include <vector> | 7 | #include <vector> |
| 8 | 8 | ||
| 9 | #include "common/common_types.h" | 9 | #include "common/common_types.h" |
| 10 | #include "common/socket_types.h" | ||
| 10 | #include "core/hle/service/service.h" | 11 | #include "core/hle/service/service.h" |
| 11 | #include "core/hle/service/sockets/sockets.h" | 12 | #include "core/hle/service/sockets/sockets.h" |
| 13 | #include "network/network.h" | ||
| 12 | 14 | ||
| 13 | namespace Core { | 15 | namespace Core { |
| 14 | class System; | 16 | class System; |
| 15 | } | 17 | } |
| 16 | 18 | ||
| 17 | namespace Network { | 19 | namespace Network { |
| 20 | class SocketBase; | ||
| 18 | class Socket; | 21 | class Socket; |
| 19 | } // namespace Network | 22 | } // namespace Network |
| 20 | 23 | ||
| @@ -30,7 +33,7 @@ private: | |||
| 30 | static constexpr size_t MAX_FD = 128; | 33 | static constexpr size_t MAX_FD = 128; |
| 31 | 34 | ||
| 32 | struct FileDescriptor { | 35 | struct FileDescriptor { |
| 33 | std::unique_ptr<Network::Socket> socket; | 36 | std::unique_ptr<Network::SocketBase> socket; |
| 34 | s32 flags = 0; | 37 | s32 flags = 0; |
| 35 | bool is_connection_based = false; | 38 | bool is_connection_based = false; |
| 36 | }; | 39 | }; |
| @@ -165,6 +168,14 @@ private: | |||
| 165 | void BuildErrnoResponse(Kernel::HLERequestContext& ctx, Errno bsd_errno) const noexcept; | 168 | void BuildErrnoResponse(Kernel::HLERequestContext& ctx, Errno bsd_errno) const noexcept; |
| 166 | 169 | ||
| 167 | std::array<std::optional<FileDescriptor>, MAX_FD> file_descriptors; | 170 | std::array<std::optional<FileDescriptor>, MAX_FD> file_descriptors; |
| 171 | |||
| 172 | Network::RoomNetwork& room_network; | ||
| 173 | |||
| 174 | /// Callback to parse and handle a received wifi packet. | ||
| 175 | void OnProxyPacketReceived(const Network::ProxyPacket& packet); | ||
| 176 | |||
| 177 | // Callback identifier for the OnProxyPacketReceived event. | ||
| 178 | Network::RoomMember::CallbackHandle<Network::ProxyPacket> proxy_packet_received; | ||
| 168 | }; | 179 | }; |
| 169 | 180 | ||
| 170 | class BSDCFG final : public ServiceFramework<BSDCFG> { | 181 | class BSDCFG final : public ServiceFramework<BSDCFG> { |
diff --git a/src/core/hle/service/sockets/sockets.h b/src/core/hle/service/sockets/sockets.h index b735b00fc..31b7dad33 100644 --- a/src/core/hle/service/sockets/sockets.h +++ b/src/core/hle/service/sockets/sockets.h | |||
| @@ -22,7 +22,9 @@ enum class Errno : u32 { | |||
| 22 | AGAIN = 11, | 22 | AGAIN = 11, |
| 23 | INVAL = 22, | 23 | INVAL = 22, |
| 24 | MFILE = 24, | 24 | MFILE = 24, |
| 25 | MSGSIZE = 90, | ||
| 25 | NOTCONN = 107, | 26 | NOTCONN = 107, |
| 27 | TIMEDOUT = 110, | ||
| 26 | }; | 28 | }; |
| 27 | 29 | ||
| 28 | enum class Domain : u32 { | 30 | enum class Domain : u32 { |
| @@ -96,10 +98,6 @@ struct Linger { | |||
| 96 | u32 linger; | 98 | u32 linger; |
| 97 | }; | 99 | }; |
| 98 | 100 | ||
| 99 | constexpr u32 FLAG_MSG_DONTWAIT = 0x80; | ||
| 100 | |||
| 101 | constexpr u32 FLAG_O_NONBLOCK = 0x800; | ||
| 102 | |||
| 103 | /// Registers all Sockets services with the specified service manager. | 101 | /// Registers all Sockets services with the specified service manager. |
| 104 | void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system); | 102 | void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system); |
| 105 | 103 | ||
diff --git a/src/core/hle/service/sockets/sockets_translate.cpp b/src/core/hle/service/sockets/sockets_translate.cpp index 2db10ec81..023aa0486 100644 --- a/src/core/hle/service/sockets/sockets_translate.cpp +++ b/src/core/hle/service/sockets/sockets_translate.cpp | |||
| @@ -25,6 +25,8 @@ Errno Translate(Network::Errno value) { | |||
| 25 | return Errno::MFILE; | 25 | return Errno::MFILE; |
| 26 | case Network::Errno::NOTCONN: | 26 | case Network::Errno::NOTCONN: |
| 27 | return Errno::NOTCONN; | 27 | return Errno::NOTCONN; |
| 28 | case Network::Errno::TIMEDOUT: | ||
| 29 | return Errno::TIMEDOUT; | ||
| 28 | default: | 30 | default: |
| 29 | UNIMPLEMENTED_MSG("Unimplemented errno={}", value); | 31 | UNIMPLEMENTED_MSG("Unimplemented errno={}", value); |
| 30 | return Errno::SUCCESS; | 32 | return Errno::SUCCESS; |
diff --git a/src/core/internal_network/network.cpp b/src/core/internal_network/network.cpp index 36c43cc8f..cdf38a2a4 100644 --- a/src/core/internal_network/network.cpp +++ b/src/core/internal_network/network.cpp | |||
| @@ -32,6 +32,7 @@ | |||
| 32 | #include "core/internal_network/network.h" | 32 | #include "core/internal_network/network.h" |
| 33 | #include "core/internal_network/network_interface.h" | 33 | #include "core/internal_network/network_interface.h" |
| 34 | #include "core/internal_network/sockets.h" | 34 | #include "core/internal_network/sockets.h" |
| 35 | #include "network/network.h" | ||
| 35 | 36 | ||
| 36 | namespace Network { | 37 | namespace Network { |
| 37 | 38 | ||
| @@ -114,7 +115,10 @@ Errno TranslateNativeError(int e) { | |||
| 114 | return Errno::NETDOWN; | 115 | return Errno::NETDOWN; |
| 115 | case WSAENETUNREACH: | 116 | case WSAENETUNREACH: |
| 116 | return Errno::NETUNREACH; | 117 | return Errno::NETUNREACH; |
| 118 | case WSAEMSGSIZE: | ||
| 119 | return Errno::MSGSIZE; | ||
| 117 | default: | 120 | default: |
| 121 | UNIMPLEMENTED_MSG("Unimplemented errno={}", e); | ||
| 118 | return Errno::OTHER; | 122 | return Errno::OTHER; |
| 119 | } | 123 | } |
| 120 | } | 124 | } |
| @@ -125,7 +129,6 @@ using SOCKET = int; | |||
| 125 | using WSAPOLLFD = pollfd; | 129 | using WSAPOLLFD = pollfd; |
| 126 | using ULONG = u64; | 130 | using ULONG = u64; |
| 127 | 131 | ||
| 128 | constexpr SOCKET INVALID_SOCKET = -1; | ||
| 129 | constexpr SOCKET SOCKET_ERROR = -1; | 132 | constexpr SOCKET SOCKET_ERROR = -1; |
| 130 | 133 | ||
| 131 | constexpr int SD_RECEIVE = SHUT_RD; | 134 | constexpr int SD_RECEIVE = SHUT_RD; |
| @@ -206,7 +209,10 @@ Errno TranslateNativeError(int e) { | |||
| 206 | return Errno::NETDOWN; | 209 | return Errno::NETDOWN; |
| 207 | case ENETUNREACH: | 210 | case ENETUNREACH: |
| 208 | return Errno::NETUNREACH; | 211 | return Errno::NETUNREACH; |
| 212 | case EMSGSIZE: | ||
| 213 | return Errno::MSGSIZE; | ||
| 209 | default: | 214 | default: |
| 215 | UNIMPLEMENTED_MSG("Unimplemented errno={}", e); | ||
| 210 | return Errno::OTHER; | 216 | return Errno::OTHER; |
| 211 | } | 217 | } |
| 212 | } | 218 | } |
| @@ -329,16 +335,6 @@ PollEvents TranslatePollRevents(short revents) { | |||
| 329 | return result; | 335 | return result; |
| 330 | } | 336 | } |
| 331 | 337 | ||
| 332 | template <typename T> | ||
| 333 | Errno SetSockOpt(SOCKET fd, int option, T value) { | ||
| 334 | const int result = | ||
| 335 | setsockopt(fd, SOL_SOCKET, option, reinterpret_cast<const char*>(&value), sizeof(value)); | ||
| 336 | if (result != SOCKET_ERROR) { | ||
| 337 | return Errno::SUCCESS; | ||
| 338 | } | ||
| 339 | return GetAndLogLastError(); | ||
| 340 | } | ||
| 341 | |||
| 342 | } // Anonymous namespace | 338 | } // Anonymous namespace |
| 343 | 339 | ||
| 344 | NetworkInstance::NetworkInstance() { | 340 | NetworkInstance::NetworkInstance() { |
| @@ -350,26 +346,16 @@ NetworkInstance::~NetworkInstance() { | |||
| 350 | } | 346 | } |
| 351 | 347 | ||
| 352 | std::optional<IPv4Address> GetHostIPv4Address() { | 348 | std::optional<IPv4Address> GetHostIPv4Address() { |
| 353 | const std::string& selected_network_interface = Settings::values.network_interface.GetValue(); | 349 | const auto network_interface = Network::GetSelectedNetworkInterface(); |
| 354 | const auto network_interfaces = Network::GetAvailableNetworkInterfaces(); | 350 | if (!network_interface.has_value()) { |
| 355 | if (network_interfaces.size() == 0) { | 351 | LOG_ERROR(Network, "GetSelectedNetworkInterface returned no interface"); |
| 356 | LOG_ERROR(Network, "GetAvailableNetworkInterfaces returned no interfaces"); | ||
| 357 | return {}; | 352 | return {}; |
| 358 | } | 353 | } |
| 359 | 354 | ||
| 360 | const auto res = | 355 | std::array<char, 16> ip_addr = {}; |
| 361 | std::ranges::find_if(network_interfaces, [&selected_network_interface](const auto& iface) { | 356 | ASSERT(inet_ntop(AF_INET, &network_interface->ip_address, ip_addr.data(), sizeof(ip_addr)) != |
| 362 | return iface.name == selected_network_interface; | 357 | nullptr); |
| 363 | }); | 358 | return TranslateIPv4(network_interface->ip_address); |
| 364 | |||
| 365 | if (res != network_interfaces.end()) { | ||
| 366 | char ip_addr[16] = {}; | ||
| 367 | ASSERT(inet_ntop(AF_INET, &res->ip_address, ip_addr, sizeof(ip_addr)) != nullptr); | ||
| 368 | return TranslateIPv4(res->ip_address); | ||
| 369 | } else { | ||
| 370 | LOG_ERROR(Network, "Couldn't find selected interface \"{}\"", selected_network_interface); | ||
| 371 | return {}; | ||
| 372 | } | ||
| 373 | } | 359 | } |
| 374 | 360 | ||
| 375 | std::pair<s32, Errno> Poll(std::vector<PollFD>& pollfds, s32 timeout) { | 361 | std::pair<s32, Errno> Poll(std::vector<PollFD>& pollfds, s32 timeout) { |
| @@ -412,7 +398,19 @@ Socket::~Socket() { | |||
| 412 | fd = INVALID_SOCKET; | 398 | fd = INVALID_SOCKET; |
| 413 | } | 399 | } |
| 414 | 400 | ||
| 415 | Socket::Socket(Socket&& rhs) noexcept : fd{std::exchange(rhs.fd, INVALID_SOCKET)} {} | 401 | Socket::Socket(Socket&& rhs) noexcept { |
| 402 | fd = std::exchange(rhs.fd, INVALID_SOCKET); | ||
| 403 | } | ||
| 404 | |||
| 405 | template <typename T> | ||
| 406 | Errno Socket::SetSockOpt(SOCKET fd_, int option, T value) { | ||
| 407 | const int result = | ||
| 408 | setsockopt(fd_, SOL_SOCKET, option, reinterpret_cast<const char*>(&value), sizeof(value)); | ||
| 409 | if (result != SOCKET_ERROR) { | ||
| 410 | return Errno::SUCCESS; | ||
| 411 | } | ||
| 412 | return GetAndLogLastError(); | ||
| 413 | } | ||
| 416 | 414 | ||
| 417 | Errno Socket::Initialize(Domain domain, Type type, Protocol protocol) { | 415 | Errno Socket::Initialize(Domain domain, Type type, Protocol protocol) { |
| 418 | fd = socket(TranslateDomain(domain), TranslateType(type), TranslateProtocol(protocol)); | 416 | fd = socket(TranslateDomain(domain), TranslateType(type), TranslateProtocol(protocol)); |
| @@ -423,7 +421,7 @@ Errno Socket::Initialize(Domain domain, Type type, Protocol protocol) { | |||
| 423 | return GetAndLogLastError(); | 421 | return GetAndLogLastError(); |
| 424 | } | 422 | } |
| 425 | 423 | ||
| 426 | std::pair<Socket::AcceptResult, Errno> Socket::Accept() { | 424 | std::pair<SocketBase::AcceptResult, Errno> Socket::Accept() { |
| 427 | sockaddr addr; | 425 | sockaddr addr; |
| 428 | socklen_t addrlen = sizeof(addr); | 426 | socklen_t addrlen = sizeof(addr); |
| 429 | const SOCKET new_socket = accept(fd, &addr, &addrlen); | 427 | const SOCKET new_socket = accept(fd, &addr, &addrlen); |
| @@ -634,4 +632,8 @@ bool Socket::IsOpened() const { | |||
| 634 | return fd != INVALID_SOCKET; | 632 | return fd != INVALID_SOCKET; |
| 635 | } | 633 | } |
| 636 | 634 | ||
| 635 | void Socket::HandleProxyPacket(const ProxyPacket& packet) { | ||
| 636 | LOG_WARNING(Network, "ProxyPacket received, but not in Proxy mode!"); | ||
| 637 | } | ||
| 638 | |||
| 637 | } // namespace Network | 639 | } // namespace Network |
diff --git a/src/core/internal_network/network.h b/src/core/internal_network/network.h index 10e5ef10d..36994c22e 100644 --- a/src/core/internal_network/network.h +++ b/src/core/internal_network/network.h | |||
| @@ -8,6 +8,7 @@ | |||
| 8 | 8 | ||
| 9 | #include "common/common_funcs.h" | 9 | #include "common/common_funcs.h" |
| 10 | #include "common/common_types.h" | 10 | #include "common/common_types.h" |
| 11 | #include "common/socket_types.h" | ||
| 11 | 12 | ||
| 12 | #ifdef _WIN32 | 13 | #ifdef _WIN32 |
| 13 | #include <winsock2.h> | 14 | #include <winsock2.h> |
| @@ -17,6 +18,7 @@ | |||
| 17 | 18 | ||
| 18 | namespace Network { | 19 | namespace Network { |
| 19 | 20 | ||
| 21 | class SocketBase; | ||
| 20 | class Socket; | 22 | class Socket; |
| 21 | 23 | ||
| 22 | /// Error code for network functions | 24 | /// Error code for network functions |
| @@ -31,46 +33,11 @@ enum class Errno { | |||
| 31 | HOSTUNREACH, | 33 | HOSTUNREACH, |
| 32 | NETDOWN, | 34 | NETDOWN, |
| 33 | NETUNREACH, | 35 | NETUNREACH, |
| 36 | TIMEDOUT, | ||
| 37 | MSGSIZE, | ||
| 34 | OTHER, | 38 | OTHER, |
| 35 | }; | 39 | }; |
| 36 | 40 | ||
| 37 | /// Address families | ||
| 38 | enum class Domain { | ||
| 39 | INET, ///< Address family for IPv4 | ||
| 40 | }; | ||
| 41 | |||
| 42 | /// Socket types | ||
| 43 | enum class Type { | ||
| 44 | STREAM, | ||
| 45 | DGRAM, | ||
| 46 | RAW, | ||
| 47 | SEQPACKET, | ||
| 48 | }; | ||
| 49 | |||
| 50 | /// Protocol values for sockets | ||
| 51 | enum class Protocol { | ||
| 52 | ICMP, | ||
| 53 | TCP, | ||
| 54 | UDP, | ||
| 55 | }; | ||
| 56 | |||
| 57 | /// Shutdown mode | ||
| 58 | enum class ShutdownHow { | ||
| 59 | RD, | ||
| 60 | WR, | ||
| 61 | RDWR, | ||
| 62 | }; | ||
| 63 | |||
| 64 | /// Array of IPv4 address | ||
| 65 | using IPv4Address = std::array<u8, 4>; | ||
| 66 | |||
| 67 | /// Cross-platform sockaddr structure | ||
| 68 | struct SockAddrIn { | ||
| 69 | Domain family; | ||
| 70 | IPv4Address ip; | ||
| 71 | u16 portno; | ||
| 72 | }; | ||
| 73 | |||
| 74 | /// Cross-platform poll fd structure | 41 | /// Cross-platform poll fd structure |
| 75 | 42 | ||
| 76 | enum class PollEvents : u16 { | 43 | enum class PollEvents : u16 { |
| @@ -86,7 +53,7 @@ enum class PollEvents : u16 { | |||
| 86 | DECLARE_ENUM_FLAG_OPERATORS(PollEvents); | 53 | DECLARE_ENUM_FLAG_OPERATORS(PollEvents); |
| 87 | 54 | ||
| 88 | struct PollFD { | 55 | struct PollFD { |
| 89 | Socket* socket; | 56 | SocketBase* socket; |
| 90 | PollEvents events; | 57 | PollEvents events; |
| 91 | PollEvents revents; | 58 | PollEvents revents; |
| 92 | }; | 59 | }; |
diff --git a/src/core/internal_network/socket_proxy.cpp b/src/core/internal_network/socket_proxy.cpp new file mode 100644 index 000000000..0c746bd82 --- /dev/null +++ b/src/core/internal_network/socket_proxy.cpp | |||
| @@ -0,0 +1,290 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #include <chrono> | ||
| 5 | #include <thread> | ||
| 6 | |||
| 7 | #include "common/assert.h" | ||
| 8 | #include "common/logging/log.h" | ||
| 9 | #include "core/internal_network/network.h" | ||
| 10 | #include "core/internal_network/network_interface.h" | ||
| 11 | #include "core/internal_network/socket_proxy.h" | ||
| 12 | |||
| 13 | namespace Network { | ||
| 14 | |||
| 15 | ProxySocket::ProxySocket(RoomNetwork& room_network_) noexcept : room_network{room_network_} {} | ||
| 16 | |||
| 17 | ProxySocket::~ProxySocket() { | ||
| 18 | if (fd == INVALID_SOCKET) { | ||
| 19 | return; | ||
| 20 | } | ||
| 21 | fd = INVALID_SOCKET; | ||
| 22 | } | ||
| 23 | |||
| 24 | void ProxySocket::HandleProxyPacket(const ProxyPacket& packet) { | ||
| 25 | if (protocol != packet.protocol || local_endpoint.portno != packet.remote_endpoint.portno || | ||
| 26 | closed) { | ||
| 27 | return; | ||
| 28 | } | ||
| 29 | |||
| 30 | if (!broadcast && packet.broadcast) { | ||
| 31 | LOG_INFO(Network, "Received broadcast packet, but not configured for broadcast mode"); | ||
| 32 | return; | ||
| 33 | } | ||
| 34 | |||
| 35 | std::lock_guard guard(packets_mutex); | ||
| 36 | received_packets.push(packet); | ||
| 37 | } | ||
| 38 | |||
| 39 | template <typename T> | ||
| 40 | Errno ProxySocket::SetSockOpt(SOCKET fd_, int option, T value) { | ||
| 41 | LOG_DEBUG(Network, "(STUBBED) called"); | ||
| 42 | return Errno::SUCCESS; | ||
| 43 | } | ||
| 44 | |||
| 45 | Errno ProxySocket::Initialize(Domain domain, Type type, Protocol socket_protocol) { | ||
| 46 | protocol = socket_protocol; | ||
| 47 | SetSockOpt(fd, SO_TYPE, type); | ||
| 48 | |||
| 49 | return Errno::SUCCESS; | ||
| 50 | } | ||
| 51 | |||
| 52 | std::pair<ProxySocket::AcceptResult, Errno> ProxySocket::Accept() { | ||
| 53 | LOG_WARNING(Network, "(STUBBED) called"); | ||
| 54 | return {AcceptResult{}, Errno::SUCCESS}; | ||
| 55 | } | ||
| 56 | |||
| 57 | Errno ProxySocket::Connect(SockAddrIn addr_in) { | ||
| 58 | LOG_WARNING(Network, "(STUBBED) called"); | ||
| 59 | return Errno::SUCCESS; | ||
| 60 | } | ||
| 61 | |||
| 62 | std::pair<SockAddrIn, Errno> ProxySocket::GetPeerName() { | ||
| 63 | LOG_WARNING(Network, "(STUBBED) called"); | ||
| 64 | return {SockAddrIn{}, Errno::SUCCESS}; | ||
| 65 | } | ||
| 66 | |||
| 67 | std::pair<SockAddrIn, Errno> ProxySocket::GetSockName() { | ||
| 68 | LOG_WARNING(Network, "(STUBBED) called"); | ||
| 69 | return {SockAddrIn{}, Errno::SUCCESS}; | ||
| 70 | } | ||
| 71 | |||
| 72 | Errno ProxySocket::Bind(SockAddrIn addr) { | ||
| 73 | if (is_bound) { | ||
| 74 | LOG_WARNING(Network, "Rebinding Socket is unimplemented!"); | ||
| 75 | return Errno::SUCCESS; | ||
| 76 | } | ||
| 77 | local_endpoint = addr; | ||
| 78 | is_bound = true; | ||
| 79 | |||
| 80 | return Errno::SUCCESS; | ||
| 81 | } | ||
| 82 | |||
| 83 | Errno ProxySocket::Listen(s32 backlog) { | ||
| 84 | LOG_WARNING(Network, "(STUBBED) called"); | ||
| 85 | return Errno::SUCCESS; | ||
| 86 | } | ||
| 87 | |||
| 88 | Errno ProxySocket::Shutdown(ShutdownHow how) { | ||
| 89 | LOG_WARNING(Network, "(STUBBED) called"); | ||
| 90 | return Errno::SUCCESS; | ||
| 91 | } | ||
| 92 | |||
| 93 | std::pair<s32, Errno> ProxySocket::Recv(int flags, std::vector<u8>& message) { | ||
| 94 | LOG_WARNING(Network, "(STUBBED) called"); | ||
| 95 | ASSERT(flags == 0); | ||
| 96 | ASSERT(message.size() < static_cast<size_t>(std::numeric_limits<int>::max())); | ||
| 97 | |||
| 98 | return {static_cast<s32>(0), Errno::SUCCESS}; | ||
| 99 | } | ||
| 100 | |||
| 101 | std::pair<s32, Errno> ProxySocket::RecvFrom(int flags, std::vector<u8>& message, SockAddrIn* addr) { | ||
| 102 | ASSERT(flags == 0); | ||
| 103 | ASSERT(message.size() < static_cast<size_t>(std::numeric_limits<int>::max())); | ||
| 104 | |||
| 105 | // TODO (flTobi): Verify the timeout behavior and break when connection is lost | ||
| 106 | const auto timestamp = std::chrono::steady_clock::now(); | ||
| 107 | // When receive_timeout is set to zero, the socket is supposed to wait indefinitely until a | ||
| 108 | // packet arrives. In order to prevent lost packets from hanging the emulation thread, we set | ||
| 109 | // the timeout to 5s instead | ||
| 110 | const auto timeout = receive_timeout == 0 ? 5000 : receive_timeout; | ||
| 111 | while (true) { | ||
| 112 | { | ||
| 113 | std::lock_guard guard(packets_mutex); | ||
| 114 | if (received_packets.size() > 0) { | ||
| 115 | return ReceivePacket(flags, message, addr, message.size()); | ||
| 116 | } | ||
| 117 | } | ||
| 118 | |||
| 119 | if (!blocking) { | ||
| 120 | return {-1, Errno::AGAIN}; | ||
| 121 | } | ||
| 122 | |||
| 123 | std::this_thread::yield(); | ||
| 124 | |||
| 125 | const auto time_diff = std::chrono::steady_clock::now() - timestamp; | ||
| 126 | const auto time_diff_ms = | ||
| 127 | std::chrono::duration_cast<std::chrono::milliseconds>(time_diff).count(); | ||
| 128 | |||
| 129 | if (time_diff_ms > timeout) { | ||
| 130 | return {-1, Errno::TIMEDOUT}; | ||
| 131 | } | ||
| 132 | } | ||
| 133 | } | ||
| 134 | |||
| 135 | std::pair<s32, Errno> ProxySocket::ReceivePacket(int flags, std::vector<u8>& message, | ||
| 136 | SockAddrIn* addr, std::size_t max_length) { | ||
| 137 | ProxyPacket& packet = received_packets.front(); | ||
| 138 | if (addr) { | ||
| 139 | addr->family = Domain::INET; | ||
| 140 | addr->ip = packet.local_endpoint.ip; // The senders ip address | ||
| 141 | addr->portno = packet.local_endpoint.portno; // The senders port number | ||
| 142 | } | ||
| 143 | |||
| 144 | bool peek = (flags & FLAG_MSG_PEEK) != 0; | ||
| 145 | std::size_t read_bytes; | ||
| 146 | if (packet.data.size() > max_length) { | ||
| 147 | read_bytes = max_length; | ||
| 148 | message.clear(); | ||
| 149 | std::copy(packet.data.begin(), packet.data.begin() + read_bytes, | ||
| 150 | std::back_inserter(message)); | ||
| 151 | message.resize(max_length); | ||
| 152 | |||
| 153 | if (protocol == Protocol::UDP) { | ||
| 154 | if (!peek) { | ||
| 155 | received_packets.pop(); | ||
| 156 | } | ||
| 157 | return {-1, Errno::MSGSIZE}; | ||
| 158 | } else if (protocol == Protocol::TCP) { | ||
| 159 | std::vector<u8> numArray(packet.data.size() - max_length); | ||
| 160 | std::copy(packet.data.begin() + max_length, packet.data.end(), | ||
| 161 | std::back_inserter(numArray)); | ||
| 162 | packet.data = numArray; | ||
| 163 | } | ||
| 164 | } else { | ||
| 165 | read_bytes = packet.data.size(); | ||
| 166 | message.clear(); | ||
| 167 | std::copy(packet.data.begin(), packet.data.end(), std::back_inserter(message)); | ||
| 168 | message.resize(max_length); | ||
| 169 | if (!peek) { | ||
| 170 | received_packets.pop(); | ||
| 171 | } | ||
| 172 | } | ||
| 173 | |||
| 174 | return {static_cast<u32>(read_bytes), Errno::SUCCESS}; | ||
| 175 | } | ||
| 176 | |||
| 177 | std::pair<s32, Errno> ProxySocket::Send(const std::vector<u8>& message, int flags) { | ||
| 178 | LOG_WARNING(Network, "(STUBBED) called"); | ||
| 179 | ASSERT(message.size() < static_cast<size_t>(std::numeric_limits<int>::max())); | ||
| 180 | ASSERT(flags == 0); | ||
| 181 | |||
| 182 | return {static_cast<s32>(0), Errno::SUCCESS}; | ||
| 183 | } | ||
| 184 | |||
| 185 | void ProxySocket::SendPacket(ProxyPacket& packet) { | ||
| 186 | if (auto room_member = room_network.GetRoomMember().lock()) { | ||
| 187 | if (room_member->IsConnected()) { | ||
| 188 | room_member->SendProxyPacket(packet); | ||
| 189 | } | ||
| 190 | } | ||
| 191 | } | ||
| 192 | |||
| 193 | std::pair<s32, Errno> ProxySocket::SendTo(u32 flags, const std::vector<u8>& message, | ||
| 194 | const SockAddrIn* addr) { | ||
| 195 | ASSERT(flags == 0); | ||
| 196 | |||
| 197 | if (!is_bound) { | ||
| 198 | LOG_ERROR(Network, "ProxySocket is not bound!"); | ||
| 199 | return {static_cast<s32>(message.size()), Errno::SUCCESS}; | ||
| 200 | } | ||
| 201 | |||
| 202 | if (auto room_member = room_network.GetRoomMember().lock()) { | ||
| 203 | if (!room_member->IsConnected()) { | ||
| 204 | return {static_cast<s32>(message.size()), Errno::SUCCESS}; | ||
| 205 | } | ||
| 206 | } | ||
| 207 | |||
| 208 | ProxyPacket packet; | ||
| 209 | packet.local_endpoint = local_endpoint; | ||
| 210 | packet.remote_endpoint = *addr; | ||
| 211 | packet.protocol = protocol; | ||
| 212 | packet.broadcast = broadcast && packet.remote_endpoint.ip[3] == 255; | ||
| 213 | |||
| 214 | auto& ip = local_endpoint.ip; | ||
| 215 | auto ipv4 = Network::GetHostIPv4Address(); | ||
| 216 | // If the ip is all zeroes (INADDR_ANY) or if it matches the hosts ip address, | ||
| 217 | // replace it with a "fake" routing address | ||
| 218 | if (std::all_of(ip.begin(), ip.end(), [](u8 i) { return i == 0; }) || (ipv4 && ipv4 == ip)) { | ||
| 219 | if (auto room_member = room_network.GetRoomMember().lock()) { | ||
| 220 | packet.local_endpoint.ip = room_member->GetFakeIpAddress(); | ||
| 221 | } | ||
| 222 | } | ||
| 223 | |||
| 224 | packet.data.clear(); | ||
| 225 | std::copy(message.begin(), message.end(), std::back_inserter(packet.data)); | ||
| 226 | |||
| 227 | SendPacket(packet); | ||
| 228 | |||
| 229 | return {static_cast<s32>(message.size()), Errno::SUCCESS}; | ||
| 230 | } | ||
| 231 | |||
| 232 | Errno ProxySocket::Close() { | ||
| 233 | fd = INVALID_SOCKET; | ||
| 234 | closed = true; | ||
| 235 | |||
| 236 | return Errno::SUCCESS; | ||
| 237 | } | ||
| 238 | |||
| 239 | Errno ProxySocket::SetLinger(bool enable, u32 linger) { | ||
| 240 | struct Linger { | ||
| 241 | u16 linger_enable; | ||
| 242 | u16 linger_time; | ||
| 243 | } values; | ||
| 244 | values.linger_enable = enable ? 1 : 0; | ||
| 245 | values.linger_time = static_cast<u16>(linger); | ||
| 246 | |||
| 247 | return SetSockOpt(fd, SO_LINGER, values); | ||
| 248 | } | ||
| 249 | |||
| 250 | Errno ProxySocket::SetReuseAddr(bool enable) { | ||
| 251 | return SetSockOpt<u32>(fd, SO_REUSEADDR, enable ? 1 : 0); | ||
| 252 | } | ||
| 253 | |||
| 254 | Errno ProxySocket::SetBroadcast(bool enable) { | ||
| 255 | broadcast = enable; | ||
| 256 | return SetSockOpt<u32>(fd, SO_BROADCAST, enable ? 1 : 0); | ||
| 257 | } | ||
| 258 | |||
| 259 | Errno ProxySocket::SetSndBuf(u32 value) { | ||
| 260 | return SetSockOpt(fd, SO_SNDBUF, value); | ||
| 261 | } | ||
| 262 | |||
| 263 | Errno ProxySocket::SetKeepAlive(bool enable) { | ||
| 264 | return Errno::SUCCESS; | ||
| 265 | } | ||
| 266 | |||
| 267 | Errno ProxySocket::SetRcvBuf(u32 value) { | ||
| 268 | return SetSockOpt(fd, SO_RCVBUF, value); | ||
| 269 | } | ||
| 270 | |||
| 271 | Errno ProxySocket::SetSndTimeo(u32 value) { | ||
| 272 | send_timeout = value; | ||
| 273 | return SetSockOpt(fd, SO_SNDTIMEO, static_cast<int>(value)); | ||
| 274 | } | ||
| 275 | |||
| 276 | Errno ProxySocket::SetRcvTimeo(u32 value) { | ||
| 277 | receive_timeout = value; | ||
| 278 | return SetSockOpt(fd, SO_RCVTIMEO, static_cast<int>(value)); | ||
| 279 | } | ||
| 280 | |||
| 281 | Errno ProxySocket::SetNonBlock(bool enable) { | ||
| 282 | blocking = !enable; | ||
| 283 | return Errno::SUCCESS; | ||
| 284 | } | ||
| 285 | |||
| 286 | bool ProxySocket::IsOpened() const { | ||
| 287 | return fd != INVALID_SOCKET; | ||
| 288 | } | ||
| 289 | |||
| 290 | } // namespace Network | ||
diff --git a/src/core/internal_network/socket_proxy.h b/src/core/internal_network/socket_proxy.h new file mode 100644 index 000000000..f12b5f567 --- /dev/null +++ b/src/core/internal_network/socket_proxy.h | |||
| @@ -0,0 +1,97 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include <mutex> | ||
| 7 | #include <vector> | ||
| 8 | #include <queue> | ||
| 9 | |||
| 10 | #include "common/common_funcs.h" | ||
| 11 | #include "core/internal_network/sockets.h" | ||
| 12 | #include "network/network.h" | ||
| 13 | |||
| 14 | namespace Network { | ||
| 15 | |||
| 16 | class ProxySocket : public SocketBase { | ||
| 17 | public: | ||
| 18 | YUZU_NON_COPYABLE(ProxySocket); | ||
| 19 | YUZU_NON_MOVEABLE(ProxySocket); | ||
| 20 | |||
| 21 | explicit ProxySocket(RoomNetwork& room_network_) noexcept; | ||
| 22 | ~ProxySocket() override; | ||
| 23 | |||
| 24 | void HandleProxyPacket(const ProxyPacket& packet) override; | ||
| 25 | |||
| 26 | Errno Initialize(Domain domain, Type type, Protocol socket_protocol) override; | ||
| 27 | |||
| 28 | Errno Close() override; | ||
| 29 | |||
| 30 | std::pair<AcceptResult, Errno> Accept() override; | ||
| 31 | |||
| 32 | Errno Connect(SockAddrIn addr_in) override; | ||
| 33 | |||
| 34 | std::pair<SockAddrIn, Errno> GetPeerName() override; | ||
| 35 | |||
| 36 | std::pair<SockAddrIn, Errno> GetSockName() override; | ||
| 37 | |||
| 38 | Errno Bind(SockAddrIn addr) override; | ||
| 39 | |||
| 40 | Errno Listen(s32 backlog) override; | ||
| 41 | |||
| 42 | Errno Shutdown(ShutdownHow how) override; | ||
| 43 | |||
| 44 | std::pair<s32, Errno> Recv(int flags, std::vector<u8>& message) override; | ||
| 45 | |||
| 46 | std::pair<s32, Errno> RecvFrom(int flags, std::vector<u8>& message, SockAddrIn* addr) override; | ||
| 47 | |||
| 48 | std::pair<s32, Errno> ReceivePacket(int flags, std::vector<u8>& message, SockAddrIn* addr, | ||
| 49 | std::size_t max_length); | ||
| 50 | |||
| 51 | std::pair<s32, Errno> Send(const std::vector<u8>& message, int flags) override; | ||
| 52 | |||
| 53 | void SendPacket(ProxyPacket& packet); | ||
| 54 | |||
| 55 | std::pair<s32, Errno> SendTo(u32 flags, const std::vector<u8>& message, | ||
| 56 | const SockAddrIn* addr) override; | ||
| 57 | |||
| 58 | Errno SetLinger(bool enable, u32 linger) override; | ||
| 59 | |||
| 60 | Errno SetReuseAddr(bool enable) override; | ||
| 61 | |||
| 62 | Errno SetBroadcast(bool enable) override; | ||
| 63 | |||
| 64 | Errno SetKeepAlive(bool enable) override; | ||
| 65 | |||
| 66 | Errno SetSndBuf(u32 value) override; | ||
| 67 | |||
| 68 | Errno SetRcvBuf(u32 value) override; | ||
| 69 | |||
| 70 | Errno SetSndTimeo(u32 value) override; | ||
| 71 | |||
| 72 | Errno SetRcvTimeo(u32 value) override; | ||
| 73 | |||
| 74 | Errno SetNonBlock(bool enable) override; | ||
| 75 | |||
| 76 | template <typename T> | ||
| 77 | Errno SetSockOpt(SOCKET fd, int option, T value); | ||
| 78 | |||
| 79 | bool IsOpened() const override; | ||
| 80 | |||
| 81 | private: | ||
| 82 | bool broadcast = false; | ||
| 83 | bool closed = false; | ||
| 84 | u32 send_timeout = 0; | ||
| 85 | u32 receive_timeout = 0; | ||
| 86 | bool is_bound = false; | ||
| 87 | SockAddrIn local_endpoint{}; | ||
| 88 | bool blocking = true; | ||
| 89 | std::queue<ProxyPacket> received_packets; | ||
| 90 | Protocol protocol; | ||
| 91 | |||
| 92 | std::mutex packets_mutex; | ||
| 93 | |||
| 94 | RoomNetwork& room_network; | ||
| 95 | }; | ||
| 96 | |||
| 97 | } // namespace Network | ||
diff --git a/src/core/internal_network/sockets.h b/src/core/internal_network/sockets.h index 77e27e928..a70429b19 100644 --- a/src/core/internal_network/sockets.h +++ b/src/core/internal_network/sockets.h | |||
| @@ -14,20 +14,88 @@ | |||
| 14 | 14 | ||
| 15 | #include "common/common_types.h" | 15 | #include "common/common_types.h" |
| 16 | #include "core/internal_network/network.h" | 16 | #include "core/internal_network/network.h" |
| 17 | #include "network/network.h" | ||
| 17 | 18 | ||
| 18 | // TODO: C++20 Replace std::vector usages with std::span | 19 | // TODO: C++20 Replace std::vector usages with std::span |
| 19 | 20 | ||
| 20 | namespace Network { | 21 | namespace Network { |
| 21 | 22 | ||
| 22 | class Socket { | 23 | class SocketBase { |
| 23 | public: | 24 | public: |
| 25 | #ifdef YUZU_UNIX | ||
| 26 | using SOCKET = int; | ||
| 27 | static constexpr SOCKET INVALID_SOCKET = -1; | ||
| 28 | static constexpr SOCKET SOCKET_ERROR = -1; | ||
| 29 | #endif | ||
| 30 | |||
| 24 | struct AcceptResult { | 31 | struct AcceptResult { |
| 25 | std::unique_ptr<Socket> socket; | 32 | std::unique_ptr<SocketBase> socket; |
| 26 | SockAddrIn sockaddr_in; | 33 | SockAddrIn sockaddr_in; |
| 27 | }; | 34 | }; |
| 35 | virtual ~SocketBase() = default; | ||
| 36 | |||
| 37 | virtual SocketBase& operator=(const SocketBase&) = delete; | ||
| 38 | |||
| 39 | // Avoid closing sockets implicitly | ||
| 40 | virtual SocketBase& operator=(SocketBase&&) noexcept = delete; | ||
| 41 | |||
| 42 | virtual Errno Initialize(Domain domain, Type type, Protocol protocol) = 0; | ||
| 43 | |||
| 44 | virtual Errno Close() = 0; | ||
| 45 | |||
| 46 | virtual std::pair<AcceptResult, Errno> Accept() = 0; | ||
| 47 | |||
| 48 | virtual Errno Connect(SockAddrIn addr_in) = 0; | ||
| 49 | |||
| 50 | virtual std::pair<SockAddrIn, Errno> GetPeerName() = 0; | ||
| 51 | |||
| 52 | virtual std::pair<SockAddrIn, Errno> GetSockName() = 0; | ||
| 53 | |||
| 54 | virtual Errno Bind(SockAddrIn addr) = 0; | ||
| 55 | |||
| 56 | virtual Errno Listen(s32 backlog) = 0; | ||
| 57 | |||
| 58 | virtual Errno Shutdown(ShutdownHow how) = 0; | ||
| 59 | |||
| 60 | virtual std::pair<s32, Errno> Recv(int flags, std::vector<u8>& message) = 0; | ||
| 61 | |||
| 62 | virtual std::pair<s32, Errno> RecvFrom(int flags, std::vector<u8>& message, | ||
| 63 | SockAddrIn* addr) = 0; | ||
| 64 | |||
| 65 | virtual std::pair<s32, Errno> Send(const std::vector<u8>& message, int flags) = 0; | ||
| 66 | |||
| 67 | virtual std::pair<s32, Errno> SendTo(u32 flags, const std::vector<u8>& message, | ||
| 68 | const SockAddrIn* addr) = 0; | ||
| 69 | |||
| 70 | virtual Errno SetLinger(bool enable, u32 linger) = 0; | ||
| 28 | 71 | ||
| 29 | explicit Socket() = default; | 72 | virtual Errno SetReuseAddr(bool enable) = 0; |
| 30 | ~Socket(); | 73 | |
| 74 | virtual Errno SetKeepAlive(bool enable) = 0; | ||
| 75 | |||
| 76 | virtual Errno SetBroadcast(bool enable) = 0; | ||
| 77 | |||
| 78 | virtual Errno SetSndBuf(u32 value) = 0; | ||
| 79 | |||
| 80 | virtual Errno SetRcvBuf(u32 value) = 0; | ||
| 81 | |||
| 82 | virtual Errno SetSndTimeo(u32 value) = 0; | ||
| 83 | |||
| 84 | virtual Errno SetRcvTimeo(u32 value) = 0; | ||
| 85 | |||
| 86 | virtual Errno SetNonBlock(bool enable) = 0; | ||
| 87 | |||
| 88 | virtual bool IsOpened() const = 0; | ||
| 89 | |||
| 90 | virtual void HandleProxyPacket(const ProxyPacket& packet) = 0; | ||
| 91 | |||
| 92 | SOCKET fd = INVALID_SOCKET; | ||
| 93 | }; | ||
| 94 | |||
| 95 | class Socket : public SocketBase { | ||
| 96 | public: | ||
| 97 | Socket() = default; | ||
| 98 | ~Socket() override; | ||
| 31 | 99 | ||
| 32 | Socket(const Socket&) = delete; | 100 | Socket(const Socket&) = delete; |
| 33 | Socket& operator=(const Socket&) = delete; | 101 | Socket& operator=(const Socket&) = delete; |
| @@ -37,57 +105,57 @@ public: | |||
| 37 | // Avoid closing sockets implicitly | 105 | // Avoid closing sockets implicitly |
| 38 | Socket& operator=(Socket&&) noexcept = delete; | 106 | Socket& operator=(Socket&&) noexcept = delete; |
| 39 | 107 | ||
| 40 | Errno Initialize(Domain domain, Type type, Protocol protocol); | 108 | Errno Initialize(Domain domain, Type type, Protocol protocol) override; |
| 41 | 109 | ||
| 42 | Errno Close(); | 110 | Errno Close() override; |
| 43 | 111 | ||
| 44 | std::pair<AcceptResult, Errno> Accept(); | 112 | std::pair<AcceptResult, Errno> Accept() override; |
| 45 | 113 | ||
| 46 | Errno Connect(SockAddrIn addr_in); | 114 | Errno Connect(SockAddrIn addr_in) override; |
| 47 | 115 | ||
| 48 | std::pair<SockAddrIn, Errno> GetPeerName(); | 116 | std::pair<SockAddrIn, Errno> GetPeerName() override; |
| 49 | 117 | ||
| 50 | std::pair<SockAddrIn, Errno> GetSockName(); | 118 | std::pair<SockAddrIn, Errno> GetSockName() override; |
| 51 | 119 | ||
| 52 | Errno Bind(SockAddrIn addr); | 120 | Errno Bind(SockAddrIn addr) override; |
| 53 | 121 | ||
| 54 | Errno Listen(s32 backlog); | 122 | Errno Listen(s32 backlog) override; |
| 55 | 123 | ||
| 56 | Errno Shutdown(ShutdownHow how); | 124 | Errno Shutdown(ShutdownHow how) override; |
| 57 | 125 | ||
| 58 | std::pair<s32, Errno> Recv(int flags, std::vector<u8>& message); | 126 | std::pair<s32, Errno> Recv(int flags, std::vector<u8>& message) override; |
| 59 | 127 | ||
| 60 | std::pair<s32, Errno> RecvFrom(int flags, std::vector<u8>& message, SockAddrIn* addr); | 128 | std::pair<s32, Errno> RecvFrom(int flags, std::vector<u8>& message, SockAddrIn* addr) override; |
| 61 | 129 | ||
| 62 | std::pair<s32, Errno> Send(const std::vector<u8>& message, int flags); | 130 | std::pair<s32, Errno> Send(const std::vector<u8>& message, int flags) override; |
| 63 | 131 | ||
| 64 | std::pair<s32, Errno> SendTo(u32 flags, const std::vector<u8>& message, const SockAddrIn* addr); | 132 | std::pair<s32, Errno> SendTo(u32 flags, const std::vector<u8>& message, |
| 133 | const SockAddrIn* addr) override; | ||
| 65 | 134 | ||
| 66 | Errno SetLinger(bool enable, u32 linger); | 135 | Errno SetLinger(bool enable, u32 linger) override; |
| 67 | 136 | ||
| 68 | Errno SetReuseAddr(bool enable); | 137 | Errno SetReuseAddr(bool enable) override; |
| 69 | 138 | ||
| 70 | Errno SetKeepAlive(bool enable); | 139 | Errno SetKeepAlive(bool enable) override; |
| 71 | 140 | ||
| 72 | Errno SetBroadcast(bool enable); | 141 | Errno SetBroadcast(bool enable) override; |
| 73 | 142 | ||
| 74 | Errno SetSndBuf(u32 value); | 143 | Errno SetSndBuf(u32 value) override; |
| 75 | 144 | ||
| 76 | Errno SetRcvBuf(u32 value); | 145 | Errno SetRcvBuf(u32 value) override; |
| 77 | 146 | ||
| 78 | Errno SetSndTimeo(u32 value); | 147 | Errno SetSndTimeo(u32 value) override; |
| 79 | 148 | ||
| 80 | Errno SetRcvTimeo(u32 value); | 149 | Errno SetRcvTimeo(u32 value) override; |
| 81 | 150 | ||
| 82 | Errno SetNonBlock(bool enable); | 151 | Errno SetNonBlock(bool enable) override; |
| 83 | 152 | ||
| 84 | bool IsOpened() const; | 153 | template <typename T> |
| 154 | Errno SetSockOpt(SOCKET fd, int option, T value); | ||
| 85 | 155 | ||
| 86 | #if defined(_WIN32) | 156 | bool IsOpened() const override; |
| 87 | SOCKET fd = INVALID_SOCKET; | 157 | |
| 88 | #elif YUZU_UNIX | 158 | void HandleProxyPacket(const ProxyPacket& packet) override; |
| 89 | int fd = -1; | ||
| 90 | #endif | ||
| 91 | }; | 159 | }; |
| 92 | 160 | ||
| 93 | std::pair<s32, Errno> Poll(std::vector<PollFD>& poll_fds, s32 timeout); | 161 | std::pair<s32, Errno> Poll(std::vector<PollFD>& poll_fds, s32 timeout); |
diff --git a/src/core/loader/elf.cpp b/src/core/loader/elf.cpp deleted file mode 100644 index dfb10c34f..000000000 --- a/src/core/loader/elf.cpp +++ /dev/null | |||
| @@ -1,263 +0,0 @@ | |||
| 1 | // SPDX-FileCopyrightText: 2013 Dolphin Emulator Project | ||
| 2 | // SPDX-FileCopyrightText: 2014 Citra Emulator Project | ||
| 3 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 4 | |||
| 5 | #include <cstring> | ||
| 6 | #include <memory> | ||
| 7 | #include "common/common_funcs.h" | ||
| 8 | #include "common/common_types.h" | ||
| 9 | #include "common/elf.h" | ||
| 10 | #include "common/logging/log.h" | ||
| 11 | #include "core/hle/kernel/code_set.h" | ||
| 12 | #include "core/hle/kernel/k_page_table.h" | ||
| 13 | #include "core/hle/kernel/k_process.h" | ||
| 14 | #include "core/loader/elf.h" | ||
| 15 | #include "core/memory.h" | ||
| 16 | |||
| 17 | using namespace Common::ELF; | ||
| 18 | |||
| 19 | //////////////////////////////////////////////////////////////////////////////////////////////////// | ||
| 20 | // ElfReader class | ||
| 21 | |||
| 22 | typedef int SectionID; | ||
| 23 | |||
| 24 | class ElfReader { | ||
| 25 | private: | ||
| 26 | char* base; | ||
| 27 | u32* base32; | ||
| 28 | |||
| 29 | Elf32_Ehdr* header; | ||
| 30 | Elf32_Phdr* segments; | ||
| 31 | Elf32_Shdr* sections; | ||
| 32 | |||
| 33 | u32* sectionAddrs; | ||
| 34 | bool relocate; | ||
| 35 | VAddr entryPoint; | ||
| 36 | |||
| 37 | public: | ||
| 38 | explicit ElfReader(void* ptr); | ||
| 39 | |||
| 40 | u32 Read32(int off) const { | ||
| 41 | return base32[off >> 2]; | ||
| 42 | } | ||
| 43 | |||
| 44 | // Quick accessors | ||
| 45 | u16 GetType() const { | ||
| 46 | return header->e_type; | ||
| 47 | } | ||
| 48 | u16 GetMachine() const { | ||
| 49 | return header->e_machine; | ||
| 50 | } | ||
| 51 | VAddr GetEntryPoint() const { | ||
| 52 | return entryPoint; | ||
| 53 | } | ||
| 54 | u32 GetFlags() const { | ||
| 55 | return (u32)(header->e_flags); | ||
| 56 | } | ||
| 57 | Kernel::CodeSet LoadInto(VAddr vaddr); | ||
| 58 | |||
| 59 | int GetNumSegments() const { | ||
| 60 | return (int)(header->e_phnum); | ||
| 61 | } | ||
| 62 | int GetNumSections() const { | ||
| 63 | return (int)(header->e_shnum); | ||
| 64 | } | ||
| 65 | const u8* GetPtr(int offset) const { | ||
| 66 | return (u8*)base + offset; | ||
| 67 | } | ||
| 68 | const char* GetSectionName(int section) const; | ||
| 69 | const u8* GetSectionDataPtr(int section) const { | ||
| 70 | if (section < 0 || section >= header->e_shnum) | ||
| 71 | return nullptr; | ||
| 72 | if (sections[section].sh_type != ElfShtNobits) | ||
| 73 | return GetPtr(sections[section].sh_offset); | ||
| 74 | else | ||
| 75 | return nullptr; | ||
| 76 | } | ||
| 77 | bool IsCodeSection(int section) const { | ||
| 78 | return sections[section].sh_type == ElfShtProgBits; | ||
| 79 | } | ||
| 80 | const u8* GetSegmentPtr(int segment) { | ||
| 81 | return GetPtr(segments[segment].p_offset); | ||
| 82 | } | ||
| 83 | u32 GetSectionAddr(SectionID section) const { | ||
| 84 | return sectionAddrs[section]; | ||
| 85 | } | ||
| 86 | unsigned int GetSectionSize(SectionID section) const { | ||
| 87 | return sections[section].sh_size; | ||
| 88 | } | ||
| 89 | SectionID GetSectionByName(const char* name, int firstSection = 0) const; //-1 for not found | ||
| 90 | |||
| 91 | bool DidRelocate() const { | ||
| 92 | return relocate; | ||
| 93 | } | ||
| 94 | }; | ||
| 95 | |||
| 96 | ElfReader::ElfReader(void* ptr) { | ||
| 97 | base = (char*)ptr; | ||
| 98 | base32 = (u32*)ptr; | ||
| 99 | header = (Elf32_Ehdr*)ptr; | ||
| 100 | |||
| 101 | segments = (Elf32_Phdr*)(base + header->e_phoff); | ||
| 102 | sections = (Elf32_Shdr*)(base + header->e_shoff); | ||
| 103 | |||
| 104 | entryPoint = header->e_entry; | ||
| 105 | } | ||
| 106 | |||
| 107 | const char* ElfReader::GetSectionName(int section) const { | ||
| 108 | if (sections[section].sh_type == ElfShtNull) | ||
| 109 | return nullptr; | ||
| 110 | |||
| 111 | int name_offset = sections[section].sh_name; | ||
| 112 | const char* ptr = reinterpret_cast<const char*>(GetSectionDataPtr(header->e_shstrndx)); | ||
| 113 | |||
| 114 | if (ptr) | ||
| 115 | return ptr + name_offset; | ||
| 116 | |||
| 117 | return nullptr; | ||
| 118 | } | ||
| 119 | |||
| 120 | Kernel::CodeSet ElfReader::LoadInto(VAddr vaddr) { | ||
| 121 | LOG_DEBUG(Loader, "String section: {}", header->e_shstrndx); | ||
| 122 | |||
| 123 | // Should we relocate? | ||
| 124 | relocate = (header->e_type != ElfTypeExec); | ||
| 125 | |||
| 126 | if (relocate) { | ||
| 127 | LOG_DEBUG(Loader, "Relocatable module"); | ||
| 128 | entryPoint += vaddr; | ||
| 129 | } else { | ||
| 130 | LOG_DEBUG(Loader, "Prerelocated executable"); | ||
| 131 | } | ||
| 132 | LOG_DEBUG(Loader, "{} segments:", header->e_phnum); | ||
| 133 | |||
| 134 | // First pass : Get the bits into RAM | ||
| 135 | const VAddr base_addr = relocate ? vaddr : 0; | ||
| 136 | |||
| 137 | u64 total_image_size = 0; | ||
| 138 | for (unsigned int i = 0; i < header->e_phnum; ++i) { | ||
| 139 | const Elf32_Phdr* p = &segments[i]; | ||
| 140 | if (p->p_type == ElfPtLoad) { | ||
| 141 | total_image_size += (p->p_memsz + 0xFFF) & ~0xFFF; | ||
| 142 | } | ||
| 143 | } | ||
| 144 | |||
| 145 | Kernel::PhysicalMemory program_image(total_image_size); | ||
| 146 | std::size_t current_image_position = 0; | ||
| 147 | |||
| 148 | Kernel::CodeSet codeset; | ||
| 149 | |||
| 150 | for (unsigned int i = 0; i < header->e_phnum; ++i) { | ||
| 151 | const Elf32_Phdr* p = &segments[i]; | ||
| 152 | LOG_DEBUG(Loader, "Type: {} Vaddr: {:08X} Filesz: {:08X} Memsz: {:08X} ", p->p_type, | ||
| 153 | p->p_vaddr, p->p_filesz, p->p_memsz); | ||
| 154 | |||
| 155 | if (p->p_type == ElfPtLoad) { | ||
| 156 | Kernel::CodeSet::Segment* codeset_segment; | ||
| 157 | u32 permission_flags = p->p_flags & (ElfPfRead | ElfPfWrite | ElfPfExec); | ||
| 158 | if (permission_flags == (ElfPfRead | ElfPfExec)) { | ||
| 159 | codeset_segment = &codeset.CodeSegment(); | ||
| 160 | } else if (permission_flags == (ElfPfRead)) { | ||
| 161 | codeset_segment = &codeset.RODataSegment(); | ||
| 162 | } else if (permission_flags == (ElfPfRead | ElfPfWrite)) { | ||
| 163 | codeset_segment = &codeset.DataSegment(); | ||
| 164 | } else { | ||
| 165 | LOG_ERROR(Loader, "Unexpected ELF PT_LOAD segment id {} with flags {:X}", i, | ||
| 166 | p->p_flags); | ||
| 167 | continue; | ||
| 168 | } | ||
| 169 | |||
| 170 | if (codeset_segment->size != 0) { | ||
| 171 | LOG_ERROR(Loader, | ||
| 172 | "ELF has more than one segment of the same type. Skipping extra " | ||
| 173 | "segment (id {})", | ||
| 174 | i); | ||
| 175 | continue; | ||
| 176 | } | ||
| 177 | |||
| 178 | const VAddr segment_addr = base_addr + p->p_vaddr; | ||
| 179 | const u32 aligned_size = (p->p_memsz + 0xFFF) & ~0xFFF; | ||
| 180 | |||
| 181 | codeset_segment->offset = current_image_position; | ||
| 182 | codeset_segment->addr = segment_addr; | ||
| 183 | codeset_segment->size = aligned_size; | ||
| 184 | |||
| 185 | std::memcpy(program_image.data() + current_image_position, GetSegmentPtr(i), | ||
| 186 | p->p_filesz); | ||
| 187 | current_image_position += aligned_size; | ||
| 188 | } | ||
| 189 | } | ||
| 190 | |||
| 191 | codeset.entrypoint = base_addr + header->e_entry; | ||
| 192 | codeset.memory = std::move(program_image); | ||
| 193 | |||
| 194 | LOG_DEBUG(Loader, "Done loading."); | ||
| 195 | |||
| 196 | return codeset; | ||
| 197 | } | ||
| 198 | |||
| 199 | SectionID ElfReader::GetSectionByName(const char* name, int firstSection) const { | ||
| 200 | for (int i = firstSection; i < header->e_shnum; i++) { | ||
| 201 | const char* secname = GetSectionName(i); | ||
| 202 | |||
| 203 | if (secname != nullptr && strcmp(name, secname) == 0) | ||
| 204 | return i; | ||
| 205 | } | ||
| 206 | return -1; | ||
| 207 | } | ||
| 208 | |||
| 209 | //////////////////////////////////////////////////////////////////////////////////////////////////// | ||
| 210 | // Loader namespace | ||
| 211 | |||
| 212 | namespace Loader { | ||
| 213 | |||
| 214 | AppLoader_ELF::AppLoader_ELF(FileSys::VirtualFile file_) : AppLoader(std::move(file_)) {} | ||
| 215 | |||
| 216 | FileType AppLoader_ELF::IdentifyType(const FileSys::VirtualFile& elf_file) { | ||
| 217 | static constexpr u16 ELF_MACHINE_ARM{0x28}; | ||
| 218 | |||
| 219 | u32 magic = 0; | ||
| 220 | if (4 != elf_file->ReadObject(&magic)) { | ||
| 221 | return FileType::Error; | ||
| 222 | } | ||
| 223 | |||
| 224 | u16 machine = 0; | ||
| 225 | if (2 != elf_file->ReadObject(&machine, 18)) { | ||
| 226 | return FileType::Error; | ||
| 227 | } | ||
| 228 | |||
| 229 | if (Common::MakeMagic('\x7f', 'E', 'L', 'F') == magic && ELF_MACHINE_ARM == machine) { | ||
| 230 | return FileType::ELF; | ||
| 231 | } | ||
| 232 | |||
| 233 | return FileType::Error; | ||
| 234 | } | ||
| 235 | |||
| 236 | AppLoader_ELF::LoadResult AppLoader_ELF::Load(Kernel::KProcess& process, | ||
| 237 | [[maybe_unused]] Core::System& system) { | ||
| 238 | if (is_loaded) { | ||
| 239 | return {ResultStatus::ErrorAlreadyLoaded, {}}; | ||
| 240 | } | ||
| 241 | |||
| 242 | std::vector<u8> buffer = file->ReadAllBytes(); | ||
| 243 | if (buffer.size() != file->GetSize()) { | ||
| 244 | return {ResultStatus::ErrorIncorrectELFFileSize, {}}; | ||
| 245 | } | ||
| 246 | |||
| 247 | const VAddr base_address = process.PageTable().GetCodeRegionStart(); | ||
| 248 | ElfReader elf_reader(&buffer[0]); | ||
| 249 | Kernel::CodeSet codeset = elf_reader.LoadInto(base_address); | ||
| 250 | const VAddr entry_point = codeset.entrypoint; | ||
| 251 | |||
| 252 | // Setup the process code layout | ||
| 253 | if (process.LoadFromMetadata(FileSys::ProgramMetadata::GetDefault(), buffer.size()).IsError()) { | ||
| 254 | return {ResultStatus::ErrorNotInitialized, {}}; | ||
| 255 | } | ||
| 256 | |||
| 257 | process.LoadModule(std::move(codeset), entry_point); | ||
| 258 | |||
| 259 | is_loaded = true; | ||
| 260 | return {ResultStatus::Success, LoadParameters{48, Core::Memory::DEFAULT_STACK_SIZE}}; | ||
| 261 | } | ||
| 262 | |||
| 263 | } // namespace Loader | ||
diff --git a/src/core/loader/elf.h b/src/core/loader/elf.h deleted file mode 100644 index acd33dc3d..000000000 --- a/src/core/loader/elf.h +++ /dev/null | |||
| @@ -1,36 +0,0 @@ | |||
| 1 | // SPDX-FileCopyrightText: 2013 Dolphin Emulator Project | ||
| 2 | // SPDX-FileCopyrightText: 2014 Citra Emulator Project | ||
| 3 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include "core/loader/loader.h" | ||
| 8 | |||
| 9 | namespace Core { | ||
| 10 | class System; | ||
| 11 | } | ||
| 12 | |||
| 13 | namespace Loader { | ||
| 14 | |||
| 15 | /// Loads an ELF/AXF file | ||
| 16 | class AppLoader_ELF final : public AppLoader { | ||
| 17 | public: | ||
| 18 | explicit AppLoader_ELF(FileSys::VirtualFile file); | ||
| 19 | |||
| 20 | /** | ||
| 21 | * Identifies whether or not the given file is an ELF file. | ||
| 22 | * | ||
| 23 | * @param elf_file The file to identify. | ||
| 24 | * | ||
| 25 | * @return FileType::ELF, or FileType::Error if the file is not an ELF file. | ||
| 26 | */ | ||
| 27 | static FileType IdentifyType(const FileSys::VirtualFile& elf_file); | ||
| 28 | |||
| 29 | FileType GetFileType() const override { | ||
| 30 | return IdentifyType(file); | ||
| 31 | } | ||
| 32 | |||
| 33 | LoadResult Load(Kernel::KProcess& process, Core::System& system) override; | ||
| 34 | }; | ||
| 35 | |||
| 36 | } // namespace Loader | ||
diff --git a/src/core/loader/kip.cpp b/src/core/loader/kip.cpp index 9af46a0f7..d8a1bf82a 100644 --- a/src/core/loader/kip.cpp +++ b/src/core/loader/kip.cpp | |||
| @@ -14,7 +14,7 @@ namespace Loader { | |||
| 14 | 14 | ||
| 15 | namespace { | 15 | namespace { |
| 16 | constexpr u32 PageAlignSize(u32 size) { | 16 | constexpr u32 PageAlignSize(u32 size) { |
| 17 | return static_cast<u32>((size + Core::Memory::PAGE_MASK) & ~Core::Memory::PAGE_MASK); | 17 | return static_cast<u32>((size + Core::Memory::YUZU_PAGEMASK) & ~Core::Memory::YUZU_PAGEMASK); |
| 18 | } | 18 | } |
| 19 | } // Anonymous namespace | 19 | } // Anonymous namespace |
| 20 | 20 | ||
diff --git a/src/core/loader/loader.cpp b/src/core/loader/loader.cpp index 994ee891f..104d16efa 100644 --- a/src/core/loader/loader.cpp +++ b/src/core/loader/loader.cpp | |||
| @@ -12,7 +12,6 @@ | |||
| 12 | #include "core/core.h" | 12 | #include "core/core.h" |
| 13 | #include "core/hle/kernel/k_process.h" | 13 | #include "core/hle/kernel/k_process.h" |
| 14 | #include "core/loader/deconstructed_rom_directory.h" | 14 | #include "core/loader/deconstructed_rom_directory.h" |
| 15 | #include "core/loader/elf.h" | ||
| 16 | #include "core/loader/kip.h" | 15 | #include "core/loader/kip.h" |
| 17 | #include "core/loader/nax.h" | 16 | #include "core/loader/nax.h" |
| 18 | #include "core/loader/nca.h" | 17 | #include "core/loader/nca.h" |
| @@ -39,8 +38,6 @@ std::optional<FileType> IdentifyFileLoader(FileSys::VirtualFile file) { | |||
| 39 | FileType IdentifyFile(FileSys::VirtualFile file) { | 38 | FileType IdentifyFile(FileSys::VirtualFile file) { |
| 40 | if (const auto romdir_type = IdentifyFileLoader<AppLoader_DeconstructedRomDirectory>(file)) { | 39 | if (const auto romdir_type = IdentifyFileLoader<AppLoader_DeconstructedRomDirectory>(file)) { |
| 41 | return *romdir_type; | 40 | return *romdir_type; |
| 42 | } else if (const auto elf_type = IdentifyFileLoader<AppLoader_ELF>(file)) { | ||
| 43 | return *elf_type; | ||
| 44 | } else if (const auto nso_type = IdentifyFileLoader<AppLoader_NSO>(file)) { | 41 | } else if (const auto nso_type = IdentifyFileLoader<AppLoader_NSO>(file)) { |
| 45 | return *nso_type; | 42 | return *nso_type; |
| 46 | } else if (const auto nro_type = IdentifyFileLoader<AppLoader_NRO>(file)) { | 43 | } else if (const auto nro_type = IdentifyFileLoader<AppLoader_NRO>(file)) { |
| @@ -69,8 +66,6 @@ FileType GuessFromFilename(const std::string& name) { | |||
| 69 | const std::string extension = | 66 | const std::string extension = |
| 70 | Common::ToLower(std::string(Common::FS::GetExtensionFromFilename(name))); | 67 | Common::ToLower(std::string(Common::FS::GetExtensionFromFilename(name))); |
| 71 | 68 | ||
| 72 | if (extension == "elf") | ||
| 73 | return FileType::ELF; | ||
| 74 | if (extension == "nro") | 69 | if (extension == "nro") |
| 75 | return FileType::NRO; | 70 | return FileType::NRO; |
| 76 | if (extension == "nso") | 71 | if (extension == "nso") |
| @@ -89,8 +84,6 @@ FileType GuessFromFilename(const std::string& name) { | |||
| 89 | 84 | ||
| 90 | std::string GetFileTypeString(FileType type) { | 85 | std::string GetFileTypeString(FileType type) { |
| 91 | switch (type) { | 86 | switch (type) { |
| 92 | case FileType::ELF: | ||
| 93 | return "ELF"; | ||
| 94 | case FileType::NRO: | 87 | case FileType::NRO: |
| 95 | return "NRO"; | 88 | return "NRO"; |
| 96 | case FileType::NSO: | 89 | case FileType::NSO: |
| @@ -208,10 +201,6 @@ static std::unique_ptr<AppLoader> GetFileLoader(Core::System& system, FileSys::V | |||
| 208 | FileType type, u64 program_id, | 201 | FileType type, u64 program_id, |
| 209 | std::size_t program_index) { | 202 | std::size_t program_index) { |
| 210 | switch (type) { | 203 | switch (type) { |
| 211 | // Standard ELF file format. | ||
| 212 | case FileType::ELF: | ||
| 213 | return std::make_unique<AppLoader_ELF>(std::move(file)); | ||
| 214 | |||
| 215 | // NX NSO file format. | 204 | // NX NSO file format. |
| 216 | case FileType::NSO: | 205 | case FileType::NSO: |
| 217 | return std::make_unique<AppLoader_NSO>(std::move(file)); | 206 | return std::make_unique<AppLoader_NSO>(std::move(file)); |
diff --git a/src/core/loader/loader.h b/src/core/loader/loader.h index 7bf4faaf1..7b43f70ed 100644 --- a/src/core/loader/loader.h +++ b/src/core/loader/loader.h | |||
| @@ -34,7 +34,6 @@ namespace Loader { | |||
| 34 | enum class FileType { | 34 | enum class FileType { |
| 35 | Error, | 35 | Error, |
| 36 | Unknown, | 36 | Unknown, |
| 37 | ELF, | ||
| 38 | NSO, | 37 | NSO, |
| 39 | NRO, | 38 | NRO, |
| 40 | NCA, | 39 | NCA, |
diff --git a/src/core/loader/nro.cpp b/src/core/loader/nro.cpp index 1b0bb0876..73d04d7ee 100644 --- a/src/core/loader/nro.cpp +++ b/src/core/loader/nro.cpp | |||
| @@ -125,7 +125,7 @@ FileType AppLoader_NRO::IdentifyType(const FileSys::VirtualFile& nro_file) { | |||
| 125 | } | 125 | } |
| 126 | 126 | ||
| 127 | static constexpr u32 PageAlignSize(u32 size) { | 127 | static constexpr u32 PageAlignSize(u32 size) { |
| 128 | return static_cast<u32>((size + Core::Memory::PAGE_MASK) & ~Core::Memory::PAGE_MASK); | 128 | return static_cast<u32>((size + Core::Memory::YUZU_PAGEMASK) & ~Core::Memory::YUZU_PAGEMASK); |
| 129 | } | 129 | } |
| 130 | 130 | ||
| 131 | static bool LoadNroImpl(Kernel::KProcess& process, const std::vector<u8>& data) { | 131 | static bool LoadNroImpl(Kernel::KProcess& process, const std::vector<u8>& data) { |
diff --git a/src/core/loader/nso.cpp b/src/core/loader/nso.cpp index 8dd956fc6..4c3b3c655 100644 --- a/src/core/loader/nso.cpp +++ b/src/core/loader/nso.cpp | |||
| @@ -45,7 +45,7 @@ std::vector<u8> DecompressSegment(const std::vector<u8>& compressed_data, | |||
| 45 | } | 45 | } |
| 46 | 46 | ||
| 47 | constexpr u32 PageAlignSize(u32 size) { | 47 | constexpr u32 PageAlignSize(u32 size) { |
| 48 | return static_cast<u32>((size + Core::Memory::PAGE_MASK) & ~Core::Memory::PAGE_MASK); | 48 | return static_cast<u32>((size + Core::Memory::YUZU_PAGEMASK) & ~Core::Memory::YUZU_PAGEMASK); |
| 49 | } | 49 | } |
| 50 | } // Anonymous namespace | 50 | } // Anonymous namespace |
| 51 | 51 | ||
diff --git a/src/core/memory.cpp b/src/core/memory.cpp index 1b44280b5..34ad7cadd 100644 --- a/src/core/memory.cpp +++ b/src/core/memory.cpp | |||
| @@ -36,10 +36,11 @@ struct Memory::Impl { | |||
| 36 | } | 36 | } |
| 37 | 37 | ||
| 38 | void MapMemoryRegion(Common::PageTable& page_table, VAddr base, u64 size, PAddr target) { | 38 | void MapMemoryRegion(Common::PageTable& page_table, VAddr base, u64 size, PAddr target) { |
| 39 | ASSERT_MSG((size & PAGE_MASK) == 0, "non-page aligned size: {:016X}", size); | 39 | ASSERT_MSG((size & YUZU_PAGEMASK) == 0, "non-page aligned size: {:016X}", size); |
| 40 | ASSERT_MSG((base & PAGE_MASK) == 0, "non-page aligned base: {:016X}", base); | 40 | ASSERT_MSG((base & YUZU_PAGEMASK) == 0, "non-page aligned base: {:016X}", base); |
| 41 | ASSERT_MSG(target >= DramMemoryMap::Base, "Out of bounds target: {:016X}", target); | 41 | ASSERT_MSG(target >= DramMemoryMap::Base, "Out of bounds target: {:016X}", target); |
| 42 | MapPages(page_table, base / PAGE_SIZE, size / PAGE_SIZE, target, Common::PageType::Memory); | 42 | MapPages(page_table, base / YUZU_PAGESIZE, size / YUZU_PAGESIZE, target, |
| 43 | Common::PageType::Memory); | ||
| 43 | 44 | ||
| 44 | if (Settings::IsFastmemEnabled()) { | 45 | if (Settings::IsFastmemEnabled()) { |
| 45 | system.DeviceMemory().buffer.Map(base, target - DramMemoryMap::Base, size); | 46 | system.DeviceMemory().buffer.Map(base, target - DramMemoryMap::Base, size); |
| @@ -47,9 +48,10 @@ struct Memory::Impl { | |||
| 47 | } | 48 | } |
| 48 | 49 | ||
| 49 | void UnmapRegion(Common::PageTable& page_table, VAddr base, u64 size) { | 50 | void UnmapRegion(Common::PageTable& page_table, VAddr base, u64 size) { |
| 50 | ASSERT_MSG((size & PAGE_MASK) == 0, "non-page aligned size: {:016X}", size); | 51 | ASSERT_MSG((size & YUZU_PAGEMASK) == 0, "non-page aligned size: {:016X}", size); |
| 51 | ASSERT_MSG((base & PAGE_MASK) == 0, "non-page aligned base: {:016X}", base); | 52 | ASSERT_MSG((base & YUZU_PAGEMASK) == 0, "non-page aligned base: {:016X}", base); |
| 52 | MapPages(page_table, base / PAGE_SIZE, size / PAGE_SIZE, 0, Common::PageType::Unmapped); | 53 | MapPages(page_table, base / YUZU_PAGESIZE, size / YUZU_PAGESIZE, 0, |
| 54 | Common::PageType::Unmapped); | ||
| 53 | 55 | ||
| 54 | if (Settings::IsFastmemEnabled()) { | 56 | if (Settings::IsFastmemEnabled()) { |
| 55 | system.DeviceMemory().buffer.Unmap(base, size); | 57 | system.DeviceMemory().buffer.Unmap(base, size); |
| @@ -57,7 +59,7 @@ struct Memory::Impl { | |||
| 57 | } | 59 | } |
| 58 | 60 | ||
| 59 | [[nodiscard]] u8* GetPointerFromRasterizerCachedMemory(VAddr vaddr) const { | 61 | [[nodiscard]] u8* GetPointerFromRasterizerCachedMemory(VAddr vaddr) const { |
| 60 | const PAddr paddr{current_page_table->backing_addr[vaddr >> PAGE_BITS]}; | 62 | const PAddr paddr{current_page_table->backing_addr[vaddr >> YUZU_PAGEBITS]}; |
| 61 | 63 | ||
| 62 | if (!paddr) { | 64 | if (!paddr) { |
| 63 | return {}; | 65 | return {}; |
| @@ -67,7 +69,7 @@ struct Memory::Impl { | |||
| 67 | } | 69 | } |
| 68 | 70 | ||
| 69 | [[nodiscard]] u8* GetPointerFromDebugMemory(VAddr vaddr) const { | 71 | [[nodiscard]] u8* GetPointerFromDebugMemory(VAddr vaddr) const { |
| 70 | const PAddr paddr{current_page_table->backing_addr[vaddr >> PAGE_BITS]}; | 72 | const PAddr paddr{current_page_table->backing_addr[vaddr >> YUZU_PAGEBITS]}; |
| 71 | 73 | ||
| 72 | if (paddr == 0) { | 74 | if (paddr == 0) { |
| 73 | return {}; | 75 | return {}; |
| @@ -176,13 +178,14 @@ struct Memory::Impl { | |||
| 176 | auto on_unmapped, auto on_memory, auto on_rasterizer, auto increment) { | 178 | auto on_unmapped, auto on_memory, auto on_rasterizer, auto increment) { |
| 177 | const auto& page_table = process.PageTable().PageTableImpl(); | 179 | const auto& page_table = process.PageTable().PageTableImpl(); |
| 178 | std::size_t remaining_size = size; | 180 | std::size_t remaining_size = size; |
| 179 | std::size_t page_index = addr >> PAGE_BITS; | 181 | std::size_t page_index = addr >> YUZU_PAGEBITS; |
| 180 | std::size_t page_offset = addr & PAGE_MASK; | 182 | std::size_t page_offset = addr & YUZU_PAGEMASK; |
| 181 | 183 | ||
| 182 | while (remaining_size) { | 184 | while (remaining_size) { |
| 183 | const std::size_t copy_amount = | 185 | const std::size_t copy_amount = |
| 184 | std::min(static_cast<std::size_t>(PAGE_SIZE) - page_offset, remaining_size); | 186 | std::min(static_cast<std::size_t>(YUZU_PAGESIZE) - page_offset, remaining_size); |
| 185 | const auto current_vaddr = static_cast<VAddr>((page_index << PAGE_BITS) + page_offset); | 187 | const auto current_vaddr = |
| 188 | static_cast<VAddr>((page_index << YUZU_PAGEBITS) + page_offset); | ||
| 186 | 189 | ||
| 187 | const auto [pointer, type] = page_table.pointers[page_index].PointerType(); | 190 | const auto [pointer, type] = page_table.pointers[page_index].PointerType(); |
| 188 | switch (type) { | 191 | switch (type) { |
| @@ -192,7 +195,7 @@ struct Memory::Impl { | |||
| 192 | } | 195 | } |
| 193 | case Common::PageType::Memory: { | 196 | case Common::PageType::Memory: { |
| 194 | DEBUG_ASSERT(pointer); | 197 | DEBUG_ASSERT(pointer); |
| 195 | u8* mem_ptr = pointer + page_offset + (page_index << PAGE_BITS); | 198 | u8* mem_ptr = pointer + page_offset + (page_index << YUZU_PAGEBITS); |
| 196 | on_memory(copy_amount, mem_ptr); | 199 | on_memory(copy_amount, mem_ptr); |
| 197 | break; | 200 | break; |
| 198 | } | 201 | } |
| @@ -339,10 +342,10 @@ struct Memory::Impl { | |||
| 339 | // Iterate over a contiguous CPU address space, marking/unmarking the region. | 342 | // Iterate over a contiguous CPU address space, marking/unmarking the region. |
| 340 | // The region is at a granularity of CPU pages. | 343 | // The region is at a granularity of CPU pages. |
| 341 | 344 | ||
| 342 | const u64 num_pages = ((vaddr + size - 1) >> PAGE_BITS) - (vaddr >> PAGE_BITS) + 1; | 345 | const u64 num_pages = ((vaddr + size - 1) >> YUZU_PAGEBITS) - (vaddr >> YUZU_PAGEBITS) + 1; |
| 343 | for (u64 i = 0; i < num_pages; ++i, vaddr += PAGE_SIZE) { | 346 | for (u64 i = 0; i < num_pages; ++i, vaddr += YUZU_PAGESIZE) { |
| 344 | const Common::PageType page_type{ | 347 | const Common::PageType page_type{ |
| 345 | current_page_table->pointers[vaddr >> PAGE_BITS].Type()}; | 348 | current_page_table->pointers[vaddr >> YUZU_PAGEBITS].Type()}; |
| 346 | if (debug) { | 349 | if (debug) { |
| 347 | // Switch page type to debug if now debug | 350 | // Switch page type to debug if now debug |
| 348 | switch (page_type) { | 351 | switch (page_type) { |
| @@ -354,7 +357,7 @@ struct Memory::Impl { | |||
| 354 | // Page is already marked. | 357 | // Page is already marked. |
| 355 | break; | 358 | break; |
| 356 | case Common::PageType::Memory: | 359 | case Common::PageType::Memory: |
| 357 | current_page_table->pointers[vaddr >> PAGE_BITS].Store( | 360 | current_page_table->pointers[vaddr >> YUZU_PAGEBITS].Store( |
| 358 | nullptr, Common::PageType::DebugMemory); | 361 | nullptr, Common::PageType::DebugMemory); |
| 359 | break; | 362 | break; |
| 360 | default: | 363 | default: |
| @@ -371,9 +374,9 @@ struct Memory::Impl { | |||
| 371 | // Don't mess with already non-debug or rasterizer memory. | 374 | // Don't mess with already non-debug or rasterizer memory. |
| 372 | break; | 375 | break; |
| 373 | case Common::PageType::DebugMemory: { | 376 | case Common::PageType::DebugMemory: { |
| 374 | u8* const pointer{GetPointerFromDebugMemory(vaddr & ~PAGE_MASK)}; | 377 | u8* const pointer{GetPointerFromDebugMemory(vaddr & ~YUZU_PAGEMASK)}; |
| 375 | current_page_table->pointers[vaddr >> PAGE_BITS].Store( | 378 | current_page_table->pointers[vaddr >> YUZU_PAGEBITS].Store( |
| 376 | pointer - (vaddr & ~PAGE_MASK), Common::PageType::Memory); | 379 | pointer - (vaddr & ~YUZU_PAGEMASK), Common::PageType::Memory); |
| 377 | break; | 380 | break; |
| 378 | } | 381 | } |
| 379 | default: | 382 | default: |
| @@ -398,10 +401,10 @@ struct Memory::Impl { | |||
| 398 | // granularity of CPU pages, hence why we iterate on a CPU page basis (note: GPU page size | 401 | // granularity of CPU pages, hence why we iterate on a CPU page basis (note: GPU page size |
| 399 | // is different). This assumes the specified GPU address region is contiguous as well. | 402 | // is different). This assumes the specified GPU address region is contiguous as well. |
| 400 | 403 | ||
| 401 | const u64 num_pages = ((vaddr + size - 1) >> PAGE_BITS) - (vaddr >> PAGE_BITS) + 1; | 404 | const u64 num_pages = ((vaddr + size - 1) >> YUZU_PAGEBITS) - (vaddr >> YUZU_PAGEBITS) + 1; |
| 402 | for (u64 i = 0; i < num_pages; ++i, vaddr += PAGE_SIZE) { | 405 | for (u64 i = 0; i < num_pages; ++i, vaddr += YUZU_PAGESIZE) { |
| 403 | const Common::PageType page_type{ | 406 | const Common::PageType page_type{ |
| 404 | current_page_table->pointers[vaddr >> PAGE_BITS].Type()}; | 407 | current_page_table->pointers[vaddr >> YUZU_PAGEBITS].Type()}; |
| 405 | if (cached) { | 408 | if (cached) { |
| 406 | // Switch page type to cached if now cached | 409 | // Switch page type to cached if now cached |
| 407 | switch (page_type) { | 410 | switch (page_type) { |
| @@ -411,7 +414,7 @@ struct Memory::Impl { | |||
| 411 | break; | 414 | break; |
| 412 | case Common::PageType::DebugMemory: | 415 | case Common::PageType::DebugMemory: |
| 413 | case Common::PageType::Memory: | 416 | case Common::PageType::Memory: |
| 414 | current_page_table->pointers[vaddr >> PAGE_BITS].Store( | 417 | current_page_table->pointers[vaddr >> YUZU_PAGEBITS].Store( |
| 415 | nullptr, Common::PageType::RasterizerCachedMemory); | 418 | nullptr, Common::PageType::RasterizerCachedMemory); |
| 416 | break; | 419 | break; |
| 417 | case Common::PageType::RasterizerCachedMemory: | 420 | case Common::PageType::RasterizerCachedMemory: |
| @@ -434,16 +437,16 @@ struct Memory::Impl { | |||
| 434 | // that this area is already unmarked as cached. | 437 | // that this area is already unmarked as cached. |
| 435 | break; | 438 | break; |
| 436 | case Common::PageType::RasterizerCachedMemory: { | 439 | case Common::PageType::RasterizerCachedMemory: { |
| 437 | u8* const pointer{GetPointerFromRasterizerCachedMemory(vaddr & ~PAGE_MASK)}; | 440 | u8* const pointer{GetPointerFromRasterizerCachedMemory(vaddr & ~YUZU_PAGEMASK)}; |
| 438 | if (pointer == nullptr) { | 441 | if (pointer == nullptr) { |
| 439 | // It's possible that this function has been called while updating the | 442 | // It's possible that this function has been called while updating the |
| 440 | // pagetable after unmapping a VMA. In that case the underlying VMA will no | 443 | // pagetable after unmapping a VMA. In that case the underlying VMA will no |
| 441 | // longer exist, and we should just leave the pagetable entry blank. | 444 | // longer exist, and we should just leave the pagetable entry blank. |
| 442 | current_page_table->pointers[vaddr >> PAGE_BITS].Store( | 445 | current_page_table->pointers[vaddr >> YUZU_PAGEBITS].Store( |
| 443 | nullptr, Common::PageType::Unmapped); | 446 | nullptr, Common::PageType::Unmapped); |
| 444 | } else { | 447 | } else { |
| 445 | current_page_table->pointers[vaddr >> PAGE_BITS].Store( | 448 | current_page_table->pointers[vaddr >> YUZU_PAGEBITS].Store( |
| 446 | pointer - (vaddr & ~PAGE_MASK), Common::PageType::Memory); | 449 | pointer - (vaddr & ~YUZU_PAGEMASK), Common::PageType::Memory); |
| 447 | } | 450 | } |
| 448 | break; | 451 | break; |
| 449 | } | 452 | } |
| @@ -465,8 +468,8 @@ struct Memory::Impl { | |||
| 465 | */ | 468 | */ |
| 466 | void MapPages(Common::PageTable& page_table, VAddr base, u64 size, PAddr target, | 469 | void MapPages(Common::PageTable& page_table, VAddr base, u64 size, PAddr target, |
| 467 | Common::PageType type) { | 470 | Common::PageType type) { |
| 468 | LOG_DEBUG(HW_Memory, "Mapping {:016X} onto {:016X}-{:016X}", target, base * PAGE_SIZE, | 471 | LOG_DEBUG(HW_Memory, "Mapping {:016X} onto {:016X}-{:016X}", target, base * YUZU_PAGESIZE, |
| 469 | (base + size) * PAGE_SIZE); | 472 | (base + size) * YUZU_PAGESIZE); |
| 470 | 473 | ||
| 471 | // During boot, current_page_table might not be set yet, in which case we need not flush | 474 | // During boot, current_page_table might not be set yet, in which case we need not flush |
| 472 | if (system.IsPoweredOn()) { | 475 | if (system.IsPoweredOn()) { |
| @@ -474,7 +477,7 @@ struct Memory::Impl { | |||
| 474 | for (u64 i = 0; i < size; i++) { | 477 | for (u64 i = 0; i < size; i++) { |
| 475 | const auto page = base + i; | 478 | const auto page = base + i; |
| 476 | if (page_table.pointers[page].Type() == Common::PageType::RasterizerCachedMemory) { | 479 | if (page_table.pointers[page].Type() == Common::PageType::RasterizerCachedMemory) { |
| 477 | gpu.FlushAndInvalidateRegion(page << PAGE_BITS, PAGE_SIZE); | 480 | gpu.FlushAndInvalidateRegion(page << YUZU_PAGEBITS, YUZU_PAGESIZE); |
| 478 | } | 481 | } |
| 479 | } | 482 | } |
| 480 | } | 483 | } |
| @@ -485,7 +488,7 @@ struct Memory::Impl { | |||
| 485 | 488 | ||
| 486 | if (!target) { | 489 | if (!target) { |
| 487 | ASSERT_MSG(type != Common::PageType::Memory, | 490 | ASSERT_MSG(type != Common::PageType::Memory, |
| 488 | "Mapping memory page without a pointer @ {:016x}", base * PAGE_SIZE); | 491 | "Mapping memory page without a pointer @ {:016x}", base * YUZU_PAGESIZE); |
| 489 | 492 | ||
| 490 | while (base != end) { | 493 | while (base != end) { |
| 491 | page_table.pointers[base].Store(nullptr, type); | 494 | page_table.pointers[base].Store(nullptr, type); |
| @@ -496,14 +499,14 @@ struct Memory::Impl { | |||
| 496 | } else { | 499 | } else { |
| 497 | while (base != end) { | 500 | while (base != end) { |
| 498 | page_table.pointers[base].Store( | 501 | page_table.pointers[base].Store( |
| 499 | system.DeviceMemory().GetPointer(target) - (base << PAGE_BITS), type); | 502 | system.DeviceMemory().GetPointer(target) - (base << YUZU_PAGEBITS), type); |
| 500 | page_table.backing_addr[base] = target - (base << PAGE_BITS); | 503 | page_table.backing_addr[base] = target - (base << YUZU_PAGEBITS); |
| 501 | 504 | ||
| 502 | ASSERT_MSG(page_table.pointers[base].Pointer(), | 505 | ASSERT_MSG(page_table.pointers[base].Pointer(), |
| 503 | "memory mapping base yield a nullptr within the table"); | 506 | "memory mapping base yield a nullptr within the table"); |
| 504 | 507 | ||
| 505 | base += 1; | 508 | base += 1; |
| 506 | target += PAGE_SIZE; | 509 | target += YUZU_PAGESIZE; |
| 507 | } | 510 | } |
| 508 | } | 511 | } |
| 509 | } | 512 | } |
| @@ -518,7 +521,7 @@ struct Memory::Impl { | |||
| 518 | } | 521 | } |
| 519 | 522 | ||
| 520 | // Avoid adding any extra logic to this fast-path block | 523 | // Avoid adding any extra logic to this fast-path block |
| 521 | const uintptr_t raw_pointer = current_page_table->pointers[vaddr >> PAGE_BITS].Raw(); | 524 | const uintptr_t raw_pointer = current_page_table->pointers[vaddr >> YUZU_PAGEBITS].Raw(); |
| 522 | if (u8* const pointer = Common::PageTable::PageInfo::ExtractPointer(raw_pointer)) { | 525 | if (u8* const pointer = Common::PageTable::PageInfo::ExtractPointer(raw_pointer)) { |
| 523 | return &pointer[vaddr]; | 526 | return &pointer[vaddr]; |
| 524 | } | 527 | } |
| @@ -657,7 +660,7 @@ void Memory::UnmapRegion(Common::PageTable& page_table, VAddr base, u64 size) { | |||
| 657 | bool Memory::IsValidVirtualAddress(const VAddr vaddr) const { | 660 | bool Memory::IsValidVirtualAddress(const VAddr vaddr) const { |
| 658 | const Kernel::KProcess& process = *system.CurrentProcess(); | 661 | const Kernel::KProcess& process = *system.CurrentProcess(); |
| 659 | const auto& page_table = process.PageTable().PageTableImpl(); | 662 | const auto& page_table = process.PageTable().PageTableImpl(); |
| 660 | const size_t page = vaddr >> PAGE_BITS; | 663 | const size_t page = vaddr >> YUZU_PAGEBITS; |
| 661 | if (page >= page_table.pointers.size()) { | 664 | if (page >= page_table.pointers.size()) { |
| 662 | return false; | 665 | return false; |
| 663 | } | 666 | } |
| @@ -668,9 +671,9 @@ bool Memory::IsValidVirtualAddress(const VAddr vaddr) const { | |||
| 668 | 671 | ||
| 669 | bool Memory::IsValidVirtualAddressRange(VAddr base, u64 size) const { | 672 | bool Memory::IsValidVirtualAddressRange(VAddr base, u64 size) const { |
| 670 | VAddr end = base + size; | 673 | VAddr end = base + size; |
| 671 | VAddr page = Common::AlignDown(base, PAGE_SIZE); | 674 | VAddr page = Common::AlignDown(base, YUZU_PAGESIZE); |
| 672 | 675 | ||
| 673 | for (; page < end; page += PAGE_SIZE) { | 676 | for (; page < end; page += YUZU_PAGESIZE) { |
| 674 | if (!IsValidVirtualAddress(page)) { | 677 | if (!IsValidVirtualAddress(page)) { |
| 675 | return false; | 678 | return false; |
| 676 | } | 679 | } |
diff --git a/src/core/memory.h b/src/core/memory.h index 2a21fbcfd..a11ff8766 100644 --- a/src/core/memory.h +++ b/src/core/memory.h | |||
| @@ -27,9 +27,9 @@ namespace Core::Memory { | |||
| 27 | * Page size used by the ARM architecture. This is the smallest granularity with which memory can | 27 | * Page size used by the ARM architecture. This is the smallest granularity with which memory can |
| 28 | * be mapped. | 28 | * be mapped. |
| 29 | */ | 29 | */ |
| 30 | constexpr std::size_t PAGE_BITS = 12; | 30 | constexpr std::size_t YUZU_PAGEBITS = 12; |
| 31 | constexpr u64 PAGE_SIZE = 1ULL << PAGE_BITS; | 31 | constexpr u64 YUZU_PAGESIZE = 1ULL << YUZU_PAGEBITS; |
| 32 | constexpr u64 PAGE_MASK = PAGE_SIZE - 1; | 32 | constexpr u64 YUZU_PAGEMASK = YUZU_PAGESIZE - 1; |
| 33 | 33 | ||
| 34 | /// Virtual user-space memory regions | 34 | /// Virtual user-space memory regions |
| 35 | enum : VAddr { | 35 | enum : VAddr { |
diff --git a/src/dedicated_room/CMakeLists.txt b/src/dedicated_room/CMakeLists.txt new file mode 100644 index 000000000..1efdbc1f7 --- /dev/null +++ b/src/dedicated_room/CMakeLists.txt | |||
| @@ -0,0 +1,27 @@ | |||
| 1 | # SPDX-FileCopyrightText: 2017 Citra Emulator Project | ||
| 2 | # SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${PROJECT_SOURCE_DIR}/CMakeModules) | ||
| 5 | |||
| 6 | add_executable(yuzu-room | ||
| 7 | yuzu_room.cpp | ||
| 8 | yuzu_room.rc | ||
| 9 | ) | ||
| 10 | |||
| 11 | create_target_directory_groups(yuzu-room) | ||
| 12 | |||
| 13 | target_link_libraries(yuzu-room PRIVATE common network) | ||
| 14 | if (ENABLE_WEB_SERVICE) | ||
| 15 | target_compile_definitions(yuzu-room PRIVATE -DENABLE_WEB_SERVICE) | ||
| 16 | target_link_libraries(yuzu-room PRIVATE web_service) | ||
| 17 | endif() | ||
| 18 | |||
| 19 | target_link_libraries(yuzu-room PRIVATE mbedtls mbedcrypto) | ||
| 20 | if (MSVC) | ||
| 21 | target_link_libraries(yuzu-room PRIVATE getopt) | ||
| 22 | endif() | ||
| 23 | target_link_libraries(yuzu-room PRIVATE ${PLATFORM_LIBRARIES} Threads::Threads) | ||
| 24 | |||
| 25 | if(UNIX AND NOT APPLE) | ||
| 26 | install(TARGETS yuzu-room RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}/bin") | ||
| 27 | endif() | ||
diff --git a/src/dedicated_room/yuzu_room.cpp b/src/dedicated_room/yuzu_room.cpp new file mode 100644 index 000000000..7b6deba41 --- /dev/null +++ b/src/dedicated_room/yuzu_room.cpp | |||
| @@ -0,0 +1,382 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2017 Citra Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #include <chrono> | ||
| 5 | #include <fstream> | ||
| 6 | #include <iostream> | ||
| 7 | #include <memory> | ||
| 8 | #include <regex> | ||
| 9 | #include <string> | ||
| 10 | #include <thread> | ||
| 11 | |||
| 12 | #ifdef _WIN32 | ||
| 13 | // windows.h needs to be included before shellapi.h | ||
| 14 | #include <windows.h> | ||
| 15 | |||
| 16 | #include <shellapi.h> | ||
| 17 | #endif | ||
| 18 | |||
| 19 | #include <mbedtls/base64.h> | ||
| 20 | #include "common/common_types.h" | ||
| 21 | #include "common/detached_tasks.h" | ||
| 22 | #include "common/fs/file.h" | ||
| 23 | #include "common/fs/fs.h" | ||
| 24 | #include "common/fs/path_util.h" | ||
| 25 | #include "common/logging/backend.h" | ||
| 26 | #include "common/logging/log.h" | ||
| 27 | #include "common/scm_rev.h" | ||
| 28 | #include "common/settings.h" | ||
| 29 | #include "common/string_util.h" | ||
| 30 | #include "core/core.h" | ||
| 31 | #include "network/announce_multiplayer_session.h" | ||
| 32 | #include "network/network.h" | ||
| 33 | #include "network/room.h" | ||
| 34 | #include "network/verify_user.h" | ||
| 35 | |||
| 36 | #ifdef ENABLE_WEB_SERVICE | ||
| 37 | #include "web_service/verify_user_jwt.h" | ||
| 38 | #endif | ||
| 39 | |||
| 40 | #undef _UNICODE | ||
| 41 | #include <getopt.h> | ||
| 42 | #ifndef _MSC_VER | ||
| 43 | #include <unistd.h> | ||
| 44 | #endif | ||
| 45 | |||
| 46 | static void PrintHelp(const char* argv0) { | ||
| 47 | LOG_INFO(Network, | ||
| 48 | "Usage: {}" | ||
| 49 | " [options] <filename>\n" | ||
| 50 | "--room-name The name of the room\n" | ||
| 51 | "--room-description The room description\n" | ||
| 52 | "--port The port used for the room\n" | ||
| 53 | "--max_members The maximum number of players for this room\n" | ||
| 54 | "--password The password for the room\n" | ||
| 55 | "--preferred-game The preferred game for this room\n" | ||
| 56 | "--preferred-game-id The preferred game-id for this room\n" | ||
| 57 | "--username The username used for announce\n" | ||
| 58 | "--token The token used for announce\n" | ||
| 59 | "--web-api-url yuzu Web API url\n" | ||
| 60 | "--ban-list-file The file for storing the room ban list\n" | ||
| 61 | "--log-file The file for storing the room log\n" | ||
| 62 | "--enable-yuzu-mods Allow yuzu Community Moderators to moderate on your room\n" | ||
| 63 | "-h, --help Display this help and exit\n" | ||
| 64 | "-v, --version Output version information and exit\n", | ||
| 65 | argv0); | ||
| 66 | } | ||
| 67 | |||
| 68 | static void PrintVersion() { | ||
| 69 | LOG_INFO(Network, "yuzu dedicated room {} {} Libnetwork: {}", Common::g_scm_branch, | ||
| 70 | Common::g_scm_desc, Network::network_version); | ||
| 71 | } | ||
| 72 | |||
| 73 | /// The magic text at the beginning of a yuzu-room ban list file. | ||
| 74 | static constexpr char BanListMagic[] = "YuzuRoom-BanList-1"; | ||
| 75 | |||
| 76 | static constexpr char token_delimiter{':'}; | ||
| 77 | |||
| 78 | static void PadToken(std::string& token) { | ||
| 79 | while (token.size() % 4 != 0) { | ||
| 80 | token.push_back('='); | ||
| 81 | } | ||
| 82 | } | ||
| 83 | |||
| 84 | static std::string UsernameFromDisplayToken(const std::string& display_token) { | ||
| 85 | std::size_t outlen; | ||
| 86 | |||
| 87 | std::array<unsigned char, 512> output{}; | ||
| 88 | mbedtls_base64_decode(output.data(), output.size(), &outlen, | ||
| 89 | reinterpret_cast<const unsigned char*>(display_token.c_str()), | ||
| 90 | display_token.length()); | ||
| 91 | std::string decoded_display_token(reinterpret_cast<char*>(&output), outlen); | ||
| 92 | return decoded_display_token.substr(0, decoded_display_token.find(token_delimiter)); | ||
| 93 | } | ||
| 94 | |||
| 95 | static std::string TokenFromDisplayToken(const std::string& display_token) { | ||
| 96 | std::size_t outlen; | ||
| 97 | |||
| 98 | std::array<unsigned char, 512> output{}; | ||
| 99 | mbedtls_base64_decode(output.data(), output.size(), &outlen, | ||
| 100 | reinterpret_cast<const unsigned char*>(display_token.c_str()), | ||
| 101 | display_token.length()); | ||
| 102 | std::string decoded_display_token(reinterpret_cast<char*>(&output), outlen); | ||
| 103 | return decoded_display_token.substr(decoded_display_token.find(token_delimiter) + 1); | ||
| 104 | } | ||
| 105 | |||
| 106 | static Network::Room::BanList LoadBanList(const std::string& path) { | ||
| 107 | std::ifstream file; | ||
| 108 | Common::FS::OpenFileStream(file, path, std::ios_base::in); | ||
| 109 | if (!file || file.eof()) { | ||
| 110 | LOG_ERROR(Network, "Could not open ban list!"); | ||
| 111 | return {}; | ||
| 112 | } | ||
| 113 | std::string magic; | ||
| 114 | std::getline(file, magic); | ||
| 115 | if (magic != BanListMagic) { | ||
| 116 | LOG_ERROR(Network, "Ban list is not valid!"); | ||
| 117 | return {}; | ||
| 118 | } | ||
| 119 | |||
| 120 | // false = username ban list, true = ip ban list | ||
| 121 | bool ban_list_type = false; | ||
| 122 | Network::Room::UsernameBanList username_ban_list; | ||
| 123 | Network::Room::IPBanList ip_ban_list; | ||
| 124 | while (!file.eof()) { | ||
| 125 | std::string line; | ||
| 126 | std::getline(file, line); | ||
| 127 | line.erase(std::remove(line.begin(), line.end(), '\0'), line.end()); | ||
| 128 | line = Common::StripSpaces(line); | ||
| 129 | if (line.empty()) { | ||
| 130 | // An empty line marks start of the IP ban list | ||
| 131 | ban_list_type = true; | ||
| 132 | continue; | ||
| 133 | } | ||
| 134 | if (ban_list_type) { | ||
| 135 | ip_ban_list.emplace_back(line); | ||
| 136 | } else { | ||
| 137 | username_ban_list.emplace_back(line); | ||
| 138 | } | ||
| 139 | } | ||
| 140 | |||
| 141 | return {username_ban_list, ip_ban_list}; | ||
| 142 | } | ||
| 143 | |||
| 144 | static void SaveBanList(const Network::Room::BanList& ban_list, const std::string& path) { | ||
| 145 | std::ofstream file; | ||
| 146 | Common::FS::OpenFileStream(file, path, std::ios_base::out); | ||
| 147 | if (!file) { | ||
| 148 | LOG_ERROR(Network, "Could not save ban list!"); | ||
| 149 | return; | ||
| 150 | } | ||
| 151 | |||
| 152 | file << BanListMagic << "\n"; | ||
| 153 | |||
| 154 | // Username ban list | ||
| 155 | for (const auto& username : ban_list.first) { | ||
| 156 | file << username << "\n"; | ||
| 157 | } | ||
| 158 | file << "\n"; | ||
| 159 | |||
| 160 | // IP ban list | ||
| 161 | for (const auto& ip : ban_list.second) { | ||
| 162 | file << ip << "\n"; | ||
| 163 | } | ||
| 164 | } | ||
| 165 | |||
| 166 | static void InitializeLogging(const std::string& log_file) { | ||
| 167 | Common::Log::Initialize(); | ||
| 168 | Common::Log::SetColorConsoleBackendEnabled(true); | ||
| 169 | Common::Log::Start(); | ||
| 170 | } | ||
| 171 | |||
| 172 | /// Application entry point | ||
| 173 | int main(int argc, char** argv) { | ||
| 174 | Common::DetachedTasks detached_tasks; | ||
| 175 | int option_index = 0; | ||
| 176 | char* endarg; | ||
| 177 | |||
| 178 | std::string room_name; | ||
| 179 | std::string room_description; | ||
| 180 | std::string password; | ||
| 181 | std::string preferred_game; | ||
| 182 | std::string username; | ||
| 183 | std::string token; | ||
| 184 | std::string web_api_url; | ||
| 185 | std::string ban_list_file; | ||
| 186 | std::string log_file = "yuzu-room.log"; | ||
| 187 | u64 preferred_game_id = 0; | ||
| 188 | u32 port = Network::DefaultRoomPort; | ||
| 189 | u32 max_members = 16; | ||
| 190 | bool enable_yuzu_mods = false; | ||
| 191 | |||
| 192 | static struct option long_options[] = { | ||
| 193 | {"room-name", required_argument, 0, 'n'}, | ||
| 194 | {"room-description", required_argument, 0, 'd'}, | ||
| 195 | {"port", required_argument, 0, 'p'}, | ||
| 196 | {"max_members", required_argument, 0, 'm'}, | ||
| 197 | {"password", required_argument, 0, 'w'}, | ||
| 198 | {"preferred-game", required_argument, 0, 'g'}, | ||
| 199 | {"preferred-game-id", required_argument, 0, 'i'}, | ||
| 200 | {"username", optional_argument, 0, 'u'}, | ||
| 201 | {"token", required_argument, 0, 't'}, | ||
| 202 | {"web-api-url", required_argument, 0, 'a'}, | ||
| 203 | {"ban-list-file", required_argument, 0, 'b'}, | ||
| 204 | {"log-file", required_argument, 0, 'l'}, | ||
| 205 | {"enable-yuzu-mods", no_argument, 0, 'e'}, | ||
| 206 | {"help", no_argument, 0, 'h'}, | ||
| 207 | {"version", no_argument, 0, 'v'}, | ||
| 208 | {0, 0, 0, 0}, | ||
| 209 | }; | ||
| 210 | |||
| 211 | InitializeLogging(log_file); | ||
| 212 | |||
| 213 | while (optind < argc) { | ||
| 214 | int arg = getopt_long(argc, argv, "n:d:p:m:w:g:u:t:a:i:l:hv", long_options, &option_index); | ||
| 215 | if (arg != -1) { | ||
| 216 | switch (static_cast<char>(arg)) { | ||
| 217 | case 'n': | ||
| 218 | room_name.assign(optarg); | ||
| 219 | break; | ||
| 220 | case 'd': | ||
| 221 | room_description.assign(optarg); | ||
| 222 | break; | ||
| 223 | case 'p': | ||
| 224 | port = strtoul(optarg, &endarg, 0); | ||
| 225 | break; | ||
| 226 | case 'm': | ||
| 227 | max_members = strtoul(optarg, &endarg, 0); | ||
| 228 | break; | ||
| 229 | case 'w': | ||
| 230 | password.assign(optarg); | ||
| 231 | break; | ||
| 232 | case 'g': | ||
| 233 | preferred_game.assign(optarg); | ||
| 234 | break; | ||
| 235 | case 'i': | ||
| 236 | preferred_game_id = strtoull(optarg, &endarg, 16); | ||
| 237 | break; | ||
| 238 | case 'u': | ||
| 239 | username.assign(optarg); | ||
| 240 | break; | ||
| 241 | case 't': | ||
| 242 | token.assign(optarg); | ||
| 243 | break; | ||
| 244 | case 'a': | ||
| 245 | web_api_url.assign(optarg); | ||
| 246 | break; | ||
| 247 | case 'b': | ||
| 248 | ban_list_file.assign(optarg); | ||
| 249 | break; | ||
| 250 | case 'l': | ||
| 251 | log_file.assign(optarg); | ||
| 252 | break; | ||
| 253 | case 'e': | ||
| 254 | enable_yuzu_mods = true; | ||
| 255 | break; | ||
| 256 | case 'h': | ||
| 257 | PrintHelp(argv[0]); | ||
| 258 | return 0; | ||
| 259 | case 'v': | ||
| 260 | PrintVersion(); | ||
| 261 | return 0; | ||
| 262 | } | ||
| 263 | } | ||
| 264 | } | ||
| 265 | |||
| 266 | if (room_name.empty()) { | ||
| 267 | LOG_ERROR(Network, "Room name is empty!"); | ||
| 268 | PrintHelp(argv[0]); | ||
| 269 | return -1; | ||
| 270 | } | ||
| 271 | if (preferred_game.empty()) { | ||
| 272 | LOG_ERROR(Network, "Preferred game is empty!"); | ||
| 273 | PrintHelp(argv[0]); | ||
| 274 | return -1; | ||
| 275 | } | ||
| 276 | if (preferred_game_id == 0) { | ||
| 277 | LOG_ERROR(Network, | ||
| 278 | "preferred-game-id not set!\nThis should get set to allow users to find your " | ||
| 279 | "room.\nSet with --preferred-game-id id"); | ||
| 280 | } | ||
| 281 | if (max_members > Network::MaxConcurrentConnections || max_members < 2) { | ||
| 282 | LOG_ERROR(Network, "max_members needs to be in the range 2 - {}!", | ||
| 283 | Network::MaxConcurrentConnections); | ||
| 284 | PrintHelp(argv[0]); | ||
| 285 | return -1; | ||
| 286 | } | ||
| 287 | if (port > UINT16_MAX) { | ||
| 288 | LOG_ERROR(Network, "Port needs to be in the range 0 - 65535!"); | ||
| 289 | PrintHelp(argv[0]); | ||
| 290 | return -1; | ||
| 291 | } | ||
| 292 | if (ban_list_file.empty()) { | ||
| 293 | LOG_ERROR(Network, "Ban list file not set!\nThis should get set to load and save room ban " | ||
| 294 | "list.\nSet with --ban-list-file <file>"); | ||
| 295 | } | ||
| 296 | bool announce = true; | ||
| 297 | if (token.empty() && announce) { | ||
| 298 | announce = false; | ||
| 299 | LOG_INFO(Network, "Token is empty: Hosting a private room"); | ||
| 300 | } | ||
| 301 | if (web_api_url.empty() && announce) { | ||
| 302 | announce = false; | ||
| 303 | LOG_INFO(Network, "Endpoint url is empty: Hosting a private room"); | ||
| 304 | } | ||
| 305 | if (announce) { | ||
| 306 | if (username.empty()) { | ||
| 307 | LOG_INFO(Network, "Hosting a public room"); | ||
| 308 | Settings::values.web_api_url = web_api_url; | ||
| 309 | PadToken(token); | ||
| 310 | Settings::values.yuzu_username = UsernameFromDisplayToken(token); | ||
| 311 | username = Settings::values.yuzu_username.GetValue(); | ||
| 312 | Settings::values.yuzu_token = TokenFromDisplayToken(token); | ||
| 313 | } else { | ||
| 314 | LOG_INFO(Network, "Hosting a public room"); | ||
| 315 | Settings::values.web_api_url = web_api_url; | ||
| 316 | Settings::values.yuzu_username = username; | ||
| 317 | Settings::values.yuzu_token = token; | ||
| 318 | } | ||
| 319 | } | ||
| 320 | if (!announce && enable_yuzu_mods) { | ||
| 321 | enable_yuzu_mods = false; | ||
| 322 | LOG_INFO(Network, "Can not enable yuzu Moderators for private rooms"); | ||
| 323 | } | ||
| 324 | |||
| 325 | // Load the ban list | ||
| 326 | Network::Room::BanList ban_list; | ||
| 327 | if (!ban_list_file.empty()) { | ||
| 328 | ban_list = LoadBanList(ban_list_file); | ||
| 329 | } | ||
| 330 | |||
| 331 | std::unique_ptr<Network::VerifyUser::Backend> verify_backend; | ||
| 332 | if (announce) { | ||
| 333 | #ifdef ENABLE_WEB_SERVICE | ||
| 334 | verify_backend = | ||
| 335 | std::make_unique<WebService::VerifyUserJWT>(Settings::values.web_api_url.GetValue()); | ||
| 336 | #else | ||
| 337 | LOG_INFO(Network, | ||
| 338 | "yuzu Web Services is not available with this build: validation is disabled."); | ||
| 339 | verify_backend = std::make_unique<Network::VerifyUser::NullBackend>(); | ||
| 340 | #endif | ||
| 341 | } else { | ||
| 342 | verify_backend = std::make_unique<Network::VerifyUser::NullBackend>(); | ||
| 343 | } | ||
| 344 | |||
| 345 | Network::RoomNetwork network{}; | ||
| 346 | network.Init(); | ||
| 347 | if (auto room = network.GetRoom().lock()) { | ||
| 348 | AnnounceMultiplayerRoom::GameInfo preferred_game_info{.name = preferred_game, | ||
| 349 | .id = preferred_game_id}; | ||
| 350 | if (!room->Create(room_name, room_description, "", port, password, max_members, username, | ||
| 351 | preferred_game_info, std::move(verify_backend), ban_list, | ||
| 352 | enable_yuzu_mods)) { | ||
| 353 | LOG_INFO(Network, "Failed to create room: "); | ||
| 354 | return -1; | ||
| 355 | } | ||
| 356 | LOG_INFO(Network, "Room is open. Close with Q+Enter..."); | ||
| 357 | auto announce_session = std::make_unique<Core::AnnounceMultiplayerSession>(network); | ||
| 358 | if (announce) { | ||
| 359 | announce_session->Start(); | ||
| 360 | } | ||
| 361 | while (room->GetState() == Network::Room::State::Open) { | ||
| 362 | std::string in; | ||
| 363 | std::cin >> in; | ||
| 364 | if (in.size() > 0) { | ||
| 365 | break; | ||
| 366 | } | ||
| 367 | std::this_thread::sleep_for(std::chrono::milliseconds(100)); | ||
| 368 | } | ||
| 369 | if (announce) { | ||
| 370 | announce_session->Stop(); | ||
| 371 | } | ||
| 372 | announce_session.reset(); | ||
| 373 | // Save the ban list | ||
| 374 | if (!ban_list_file.empty()) { | ||
| 375 | SaveBanList(room->GetBanList(), ban_list_file); | ||
| 376 | } | ||
| 377 | room->Destroy(); | ||
| 378 | } | ||
| 379 | network.Shutdown(); | ||
| 380 | detached_tasks.WaitForAllTasks(); | ||
| 381 | return 0; | ||
| 382 | } | ||
diff --git a/src/dedicated_room/yuzu_room.rc b/src/dedicated_room/yuzu_room.rc new file mode 100644 index 000000000..a08957684 --- /dev/null +++ b/src/dedicated_room/yuzu_room.rc | |||
| @@ -0,0 +1,20 @@ | |||
| 1 | // SPDX-FileCopyrightText: 2017 Citra Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #include "winresrc.h" | ||
| 5 | ///////////////////////////////////////////////////////////////////////////// | ||
| 6 | // | ||
| 7 | // Icon | ||
| 8 | // | ||
| 9 | |||
| 10 | // Icon with lowest ID value placed first to ensure application icon | ||
| 11 | // remains consistent on all systems. | ||
| 12 | YUZU_ICON ICON "../../dist/yuzu.ico" | ||
| 13 | |||
| 14 | |||
| 15 | ///////////////////////////////////////////////////////////////////////////// | ||
| 16 | // | ||
| 17 | // RT_MANIFEST | ||
| 18 | // | ||
| 19 | |||
| 20 | 0 RT_MANIFEST "../../dist/yuzu.manifest" | ||
diff --git a/src/input_common/drivers/sdl_driver.cpp b/src/input_common/drivers/sdl_driver.cpp index de388ec4c..5cc1ccbd9 100644 --- a/src/input_common/drivers/sdl_driver.cpp +++ b/src/input_common/drivers/sdl_driver.cpp | |||
| @@ -40,13 +40,13 @@ 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 | if (SDL_GameControllerHasSensor(controller, SDL_SENSOR_ACCEL) && !has_accel) { | 43 | has_accel = SDL_GameControllerHasSensor(controller, SDL_SENSOR_ACCEL); |
| 44 | has_gyro = SDL_GameControllerHasSensor(controller, SDL_SENSOR_GYRO); | ||
| 45 | if (has_accel) { | ||
| 44 | SDL_GameControllerSetSensorEnabled(controller, SDL_SENSOR_ACCEL, SDL_TRUE); | 46 | SDL_GameControllerSetSensorEnabled(controller, SDL_SENSOR_ACCEL, SDL_TRUE); |
| 45 | has_accel = true; | ||
| 46 | } | 47 | } |
| 47 | if (SDL_GameControllerHasSensor(controller, SDL_SENSOR_GYRO) && !has_gyro) { | 48 | if (has_gyro) { |
| 48 | SDL_GameControllerSetSensorEnabled(controller, SDL_SENSOR_GYRO, SDL_TRUE); | 49 | SDL_GameControllerSetSensorEnabled(controller, SDL_SENSOR_GYRO, SDL_TRUE); |
| 49 | has_gyro = true; | ||
| 50 | } | 50 | } |
| 51 | } | 51 | } |
| 52 | } | 52 | } |
| @@ -305,6 +305,7 @@ void SDLDriver::InitJoystick(int joystick_index) { | |||
| 305 | auto joystick = std::make_shared<SDLJoystick>(guid, 0, sdl_joystick, sdl_gamecontroller); | 305 | auto joystick = std::make_shared<SDLJoystick>(guid, 0, sdl_joystick, sdl_gamecontroller); |
| 306 | PreSetController(joystick->GetPadIdentifier()); | 306 | PreSetController(joystick->GetPadIdentifier()); |
| 307 | SetBattery(joystick->GetPadIdentifier(), joystick->GetBatteryLevel()); | 307 | SetBattery(joystick->GetPadIdentifier(), joystick->GetBatteryLevel()); |
| 308 | joystick->EnableMotion(); | ||
| 308 | joystick_map[guid].emplace_back(std::move(joystick)); | 309 | joystick_map[guid].emplace_back(std::move(joystick)); |
| 309 | return; | 310 | return; |
| 310 | } | 311 | } |
| @@ -316,6 +317,7 @@ void SDLDriver::InitJoystick(int joystick_index) { | |||
| 316 | 317 | ||
| 317 | if (joystick_it != joystick_guid_list.end()) { | 318 | if (joystick_it != joystick_guid_list.end()) { |
| 318 | (*joystick_it)->SetSDLJoystick(sdl_joystick, sdl_gamecontroller); | 319 | (*joystick_it)->SetSDLJoystick(sdl_joystick, sdl_gamecontroller); |
| 320 | (*joystick_it)->EnableMotion(); | ||
| 319 | return; | 321 | return; |
| 320 | } | 322 | } |
| 321 | 323 | ||
| @@ -323,6 +325,7 @@ void SDLDriver::InitJoystick(int joystick_index) { | |||
| 323 | auto joystick = std::make_shared<SDLJoystick>(guid, port, sdl_joystick, sdl_gamecontroller); | 325 | auto joystick = std::make_shared<SDLJoystick>(guid, port, sdl_joystick, sdl_gamecontroller); |
| 324 | PreSetController(joystick->GetPadIdentifier()); | 326 | PreSetController(joystick->GetPadIdentifier()); |
| 325 | SetBattery(joystick->GetPadIdentifier(), joystick->GetBatteryLevel()); | 327 | SetBattery(joystick->GetPadIdentifier(), joystick->GetBatteryLevel()); |
| 328 | joystick->EnableMotion(); | ||
| 326 | joystick_guid_list.emplace_back(std::move(joystick)); | 329 | joystick_guid_list.emplace_back(std::move(joystick)); |
| 327 | } | 330 | } |
| 328 | 331 | ||
diff --git a/src/input_common/input_poller.cpp b/src/input_common/input_poller.cpp index 133422d5c..ffb9b945e 100644 --- a/src/input_common/input_poller.cpp +++ b/src/input_common/input_poller.cpp | |||
| @@ -824,6 +824,7 @@ std::unique_ptr<Common::Input::InputDevice> InputFactory::CreateAnalogDevice( | |||
| 824 | .threshold = std::clamp(params.Get("threshold", 0.5f), 0.0f, 1.0f), | 824 | .threshold = std::clamp(params.Get("threshold", 0.5f), 0.0f, 1.0f), |
| 825 | .offset = std::clamp(params.Get("offset", 0.0f), -1.0f, 1.0f), | 825 | .offset = std::clamp(params.Get("offset", 0.0f), -1.0f, 1.0f), |
| 826 | .inverted = params.Get("invert", "+") == "-", | 826 | .inverted = params.Get("invert", "+") == "-", |
| 827 | .toggle = static_cast<bool>(params.Get("toggle", false)), | ||
| 827 | }; | 828 | }; |
| 828 | input_engine->PreSetController(identifier); | 829 | input_engine->PreSetController(identifier); |
| 829 | input_engine->PreSetAxis(identifier, axis); | 830 | input_engine->PreSetAxis(identifier, axis); |
diff --git a/src/network/CMakeLists.txt b/src/network/CMakeLists.txt index 312f79b68..6f8ca4b90 100644 --- a/src/network/CMakeLists.txt +++ b/src/network/CMakeLists.txt | |||
| @@ -2,6 +2,8 @@ | |||
| 2 | # SPDX-License-Identifier: GPL-3.0-or-later | 2 | # SPDX-License-Identifier: GPL-3.0-or-later |
| 3 | 3 | ||
| 4 | add_library(network STATIC | 4 | add_library(network STATIC |
| 5 | announce_multiplayer_session.cpp | ||
| 6 | announce_multiplayer_session.h | ||
| 5 | network.cpp | 7 | network.cpp |
| 6 | network.h | 8 | network.h |
| 7 | packet.cpp | 9 | packet.cpp |
| @@ -17,3 +19,7 @@ add_library(network STATIC | |||
| 17 | create_target_directory_groups(network) | 19 | create_target_directory_groups(network) |
| 18 | 20 | ||
| 19 | target_link_libraries(network PRIVATE common enet Boost::boost) | 21 | target_link_libraries(network PRIVATE common enet Boost::boost) |
| 22 | if (ENABLE_WEB_SERVICE) | ||
| 23 | target_compile_definitions(network PRIVATE -DENABLE_WEB_SERVICE) | ||
| 24 | target_link_libraries(network PRIVATE web_service) | ||
| 25 | endif() | ||
diff --git a/src/core/announce_multiplayer_session.cpp b/src/network/announce_multiplayer_session.cpp index d73a488cf..6737ce85a 100644 --- a/src/core/announce_multiplayer_session.cpp +++ b/src/network/announce_multiplayer_session.cpp | |||
| @@ -31,7 +31,7 @@ AnnounceMultiplayerSession::AnnounceMultiplayerSession(Network::RoomNetwork& roo | |||
| 31 | } | 31 | } |
| 32 | 32 | ||
| 33 | WebService::WebResult AnnounceMultiplayerSession::Register() { | 33 | WebService::WebResult AnnounceMultiplayerSession::Register() { |
| 34 | std::shared_ptr<Network::Room> room = room_network.GetRoom().lock(); | 34 | auto room = room_network.GetRoom().lock(); |
| 35 | if (!room) { | 35 | if (!room) { |
| 36 | return WebService::WebResult{WebService::WebResult::Code::LibError, | 36 | return WebService::WebResult{WebService::WebResult::Code::LibError, |
| 37 | "Network is not initialized", ""}; | 37 | "Network is not initialized", ""}; |
| @@ -102,7 +102,7 @@ void AnnounceMultiplayerSession::UpdateBackendData(std::shared_ptr<Network::Room | |||
| 102 | void AnnounceMultiplayerSession::AnnounceMultiplayerLoop() { | 102 | void AnnounceMultiplayerSession::AnnounceMultiplayerLoop() { |
| 103 | // Invokes all current bound error callbacks. | 103 | // Invokes all current bound error callbacks. |
| 104 | const auto ErrorCallback = [this](WebService::WebResult result) { | 104 | const auto ErrorCallback = [this](WebService::WebResult result) { |
| 105 | std::lock_guard<std::mutex> lock(callback_mutex); | 105 | std::lock_guard lock(callback_mutex); |
| 106 | for (auto callback : error_callbacks) { | 106 | for (auto callback : error_callbacks) { |
| 107 | (*callback)(result); | 107 | (*callback)(result); |
| 108 | } | 108 | } |
| @@ -120,7 +120,7 @@ void AnnounceMultiplayerSession::AnnounceMultiplayerLoop() { | |||
| 120 | std::future<WebService::WebResult> future; | 120 | std::future<WebService::WebResult> future; |
| 121 | while (!shutdown_event.WaitUntil(update_time)) { | 121 | while (!shutdown_event.WaitUntil(update_time)) { |
| 122 | update_time += announce_time_interval; | 122 | update_time += announce_time_interval; |
| 123 | std::shared_ptr<Network::Room> room = room_network.GetRoom().lock(); | 123 | auto room = room_network.GetRoom().lock(); |
| 124 | if (!room) { | 124 | if (!room) { |
| 125 | break; | 125 | break; |
| 126 | } | 126 | } |
diff --git a/src/core/announce_multiplayer_session.h b/src/network/announce_multiplayer_session.h index db790f7d2..db790f7d2 100644 --- a/src/core/announce_multiplayer_session.h +++ b/src/network/announce_multiplayer_session.h | |||
diff --git a/src/network/room.cpp b/src/network/room.cpp index 3fc3a0383..8c63b255b 100644 --- a/src/network/room.cpp +++ b/src/network/room.cpp | |||
| @@ -20,9 +20,7 @@ namespace Network { | |||
| 20 | 20 | ||
| 21 | class Room::RoomImpl { | 21 | class Room::RoomImpl { |
| 22 | public: | 22 | public: |
| 23 | // This MAC address is used to generate a 'Nintendo' like Mac address. | 23 | std::mt19937 random_gen; ///< Random number generator. Used for GenerateFakeIPAddress |
| 24 | const MacAddress NintendoOUI; | ||
| 25 | std::mt19937 random_gen; ///< Random number generator. Used for GenerateMacAddress | ||
| 26 | 24 | ||
| 27 | ENetHost* server = nullptr; ///< Network interface. | 25 | ENetHost* server = nullptr; ///< Network interface. |
| 28 | 26 | ||
| @@ -35,10 +33,9 @@ public: | |||
| 35 | std::string password; ///< The password required to connect to this room. | 33 | std::string password; ///< The password required to connect to this room. |
| 36 | 34 | ||
| 37 | struct Member { | 35 | struct Member { |
| 38 | std::string nickname; ///< The nickname of the member. | 36 | std::string nickname; ///< The nickname of the member. |
| 39 | std::string console_id_hash; ///< A hash of the console ID of the member. | 37 | GameInfo game_info; ///< The current game of the member |
| 40 | GameInfo game_info; ///< The current game of the member | 38 | IPv4Address fake_ip; ///< The assigned fake ip address of the member. |
| 41 | MacAddress mac_address; ///< The assigned mac address of the member. | ||
| 42 | /// Data of the user, often including authenticated forum username. | 39 | /// Data of the user, often including authenticated forum username. |
| 43 | VerifyUser::UserData user_data; | 40 | VerifyUser::UserData user_data; |
| 44 | ENetPeer* peer; ///< The remote peer. | 41 | ENetPeer* peer; ///< The remote peer. |
| @@ -51,8 +48,7 @@ public: | |||
| 51 | IPBanList ip_ban_list; ///< List of banned IP addresses | 48 | IPBanList ip_ban_list; ///< List of banned IP addresses |
| 52 | mutable std::mutex ban_list_mutex; ///< Mutex for the ban lists | 49 | mutable std::mutex ban_list_mutex; ///< Mutex for the ban lists |
| 53 | 50 | ||
| 54 | RoomImpl() | 51 | RoomImpl() : random_gen(std::random_device()()) {} |
| 55 | : NintendoOUI{0x00, 0x1F, 0x32, 0x00, 0x00, 0x00}, random_gen(std::random_device()()) {} | ||
| 56 | 52 | ||
| 57 | /// Thread that receives and dispatches network packets | 53 | /// Thread that receives and dispatches network packets |
| 58 | std::unique_ptr<std::thread> room_thread; | 54 | std::unique_ptr<std::thread> room_thread; |
| @@ -101,16 +97,10 @@ public: | |||
| 101 | bool IsValidNickname(const std::string& nickname) const; | 97 | bool IsValidNickname(const std::string& nickname) const; |
| 102 | 98 | ||
| 103 | /** | 99 | /** |
| 104 | * Returns whether the MAC address is valid, ie. isn't already taken by someone else in the | 100 | * Returns whether the fake ip address is valid, ie. isn't already taken by someone else in the |
| 105 | * room. | 101 | * room. |
| 106 | */ | 102 | */ |
| 107 | bool IsValidMacAddress(const MacAddress& address) const; | 103 | bool IsValidFakeIPAddress(const IPv4Address& address) const; |
| 108 | |||
| 109 | /** | ||
| 110 | * Returns whether the console ID (hash) is valid, ie. isn't already taken by someone else in | ||
| 111 | * the room. | ||
| 112 | */ | ||
| 113 | bool IsValidConsoleId(const std::string& console_id_hash) const; | ||
| 114 | 104 | ||
| 115 | /** | 105 | /** |
| 116 | * Returns whether a user has mod permissions. | 106 | * Returns whether a user has mod permissions. |
| @@ -128,15 +118,9 @@ public: | |||
| 128 | void SendNameCollision(ENetPeer* client); | 118 | void SendNameCollision(ENetPeer* client); |
| 129 | 119 | ||
| 130 | /** | 120 | /** |
| 131 | * Sends a ID_ROOM_MAC_COLLISION message telling the client that the MAC is invalid. | 121 | * Sends a ID_ROOM_IP_COLLISION message telling the client that the IP is invalid. |
| 132 | */ | 122 | */ |
| 133 | void SendMacCollision(ENetPeer* client); | 123 | void SendIPCollision(ENetPeer* client); |
| 134 | |||
| 135 | /** | ||
| 136 | * Sends a IdConsoleIdCollison message telling the client that another member with the same | ||
| 137 | * console ID exists. | ||
| 138 | */ | ||
| 139 | void SendConsoleIdCollision(ENetPeer* client); | ||
| 140 | 124 | ||
| 141 | /** | 125 | /** |
| 142 | * Sends a ID_ROOM_VERSION_MISMATCH message telling the client that the version is invalid. | 126 | * Sends a ID_ROOM_VERSION_MISMATCH message telling the client that the version is invalid. |
| @@ -152,13 +136,13 @@ public: | |||
| 152 | * Notifies the member that its connection attempt was successful, | 136 | * Notifies the member that its connection attempt was successful, |
| 153 | * and it is now part of the room. | 137 | * and it is now part of the room. |
| 154 | */ | 138 | */ |
| 155 | void SendJoinSuccess(ENetPeer* client, MacAddress mac_address); | 139 | void SendJoinSuccess(ENetPeer* client, IPv4Address fake_ip); |
| 156 | 140 | ||
| 157 | /** | 141 | /** |
| 158 | * Notifies the member that its connection attempt was successful, | 142 | * Notifies the member that its connection attempt was successful, |
| 159 | * and it is now part of the room, and it has been granted mod permissions. | 143 | * and it is now part of the room, and it has been granted mod permissions. |
| 160 | */ | 144 | */ |
| 161 | void SendJoinSuccessAsMod(ENetPeer* client, MacAddress mac_address); | 145 | void SendJoinSuccessAsMod(ENetPeer* client, IPv4Address fake_ip); |
| 162 | 146 | ||
| 163 | /** | 147 | /** |
| 164 | * Sends a IdHostKicked message telling the client that they have been kicked. | 148 | * Sends a IdHostKicked message telling the client that they have been kicked. |
| @@ -210,7 +194,7 @@ public: | |||
| 210 | * <u32> num_members: the number of currently joined clients | 194 | * <u32> num_members: the number of currently joined clients |
| 211 | * This is followed by the following three values for each member: | 195 | * This is followed by the following three values for each member: |
| 212 | * <String> nickname of that member | 196 | * <String> nickname of that member |
| 213 | * <MacAddress> mac_address of that member | 197 | * <IPv4Address> fake_ip of that member |
| 214 | * <String> game_name of that member | 198 | * <String> game_name of that member |
| 215 | */ | 199 | */ |
| 216 | void BroadcastRoomInformation(); | 200 | void BroadcastRoomInformation(); |
| @@ -219,13 +203,13 @@ public: | |||
| 219 | * Generates a free MAC address to assign to a new client. | 203 | * Generates a free MAC address to assign to a new client. |
| 220 | * The first 3 bytes are the NintendoOUI 0x00, 0x1F, 0x32 | 204 | * The first 3 bytes are the NintendoOUI 0x00, 0x1F, 0x32 |
| 221 | */ | 205 | */ |
| 222 | MacAddress GenerateMacAddress(); | 206 | IPv4Address GenerateFakeIPAddress(); |
| 223 | 207 | ||
| 224 | /** | 208 | /** |
| 225 | * Broadcasts this packet to all members except the sender. | 209 | * Broadcasts this packet to all members except the sender. |
| 226 | * @param event The ENet event containing the data | 210 | * @param event The ENet event containing the data |
| 227 | */ | 211 | */ |
| 228 | void HandleWifiPacket(const ENetEvent* event); | 212 | void HandleProxyPacket(const ENetEvent* event); |
| 229 | 213 | ||
| 230 | /** | 214 | /** |
| 231 | * Extracts a chat entry from a received ENet packet and adds it to the chat queue. | 215 | * Extracts a chat entry from a received ENet packet and adds it to the chat queue. |
| @@ -237,7 +221,7 @@ public: | |||
| 237 | * Extracts the game name from a received ENet packet and broadcasts it. | 221 | * Extracts the game name from a received ENet packet and broadcasts it. |
| 238 | * @param event The ENet event that was received. | 222 | * @param event The ENet event that was received. |
| 239 | */ | 223 | */ |
| 240 | void HandleGameNamePacket(const ENetEvent* event); | 224 | void HandleGameInfoPacket(const ENetEvent* event); |
| 241 | 225 | ||
| 242 | /** | 226 | /** |
| 243 | * Removes the client from the members list if it was in it and announces the change | 227 | * Removes the client from the members list if it was in it and announces the change |
| @@ -250,7 +234,7 @@ public: | |||
| 250 | void Room::RoomImpl::ServerLoop() { | 234 | void Room::RoomImpl::ServerLoop() { |
| 251 | while (state != State::Closed) { | 235 | while (state != State::Closed) { |
| 252 | ENetEvent event; | 236 | ENetEvent event; |
| 253 | if (enet_host_service(server, &event, 16) > 0) { | 237 | if (enet_host_service(server, &event, 5) > 0) { |
| 254 | switch (event.type) { | 238 | switch (event.type) { |
| 255 | case ENET_EVENT_TYPE_RECEIVE: | 239 | case ENET_EVENT_TYPE_RECEIVE: |
| 256 | switch (event.packet->data[0]) { | 240 | switch (event.packet->data[0]) { |
| @@ -258,10 +242,10 @@ void Room::RoomImpl::ServerLoop() { | |||
| 258 | HandleJoinRequest(&event); | 242 | HandleJoinRequest(&event); |
| 259 | break; | 243 | break; |
| 260 | case IdSetGameInfo: | 244 | case IdSetGameInfo: |
| 261 | HandleGameNamePacket(&event); | 245 | HandleGameInfoPacket(&event); |
| 262 | break; | 246 | break; |
| 263 | case IdWifiPacket: | 247 | case IdProxyPacket: |
| 264 | HandleWifiPacket(&event); | 248 | HandleProxyPacket(&event); |
| 265 | break; | 249 | break; |
| 266 | case IdChatMessage: | 250 | case IdChatMessage: |
| 267 | HandleChatPacket(&event); | 251 | HandleChatPacket(&event); |
| @@ -313,11 +297,8 @@ void Room::RoomImpl::HandleJoinRequest(const ENetEvent* event) { | |||
| 313 | std::string nickname; | 297 | std::string nickname; |
| 314 | packet.Read(nickname); | 298 | packet.Read(nickname); |
| 315 | 299 | ||
| 316 | std::string console_id_hash; | 300 | IPv4Address preferred_fake_ip; |
| 317 | packet.Read(console_id_hash); | 301 | packet.Read(preferred_fake_ip); |
| 318 | |||
| 319 | MacAddress preferred_mac; | ||
| 320 | packet.Read(preferred_mac); | ||
| 321 | 302 | ||
| 322 | u32 client_version; | 303 | u32 client_version; |
| 323 | packet.Read(client_version); | 304 | packet.Read(client_version); |
| @@ -338,20 +319,15 @@ void Room::RoomImpl::HandleJoinRequest(const ENetEvent* event) { | |||
| 338 | return; | 319 | return; |
| 339 | } | 320 | } |
| 340 | 321 | ||
| 341 | if (preferred_mac != NoPreferredMac) { | 322 | if (preferred_fake_ip != NoPreferredIP) { |
| 342 | // Verify if the preferred mac is available | 323 | // Verify if the preferred fake ip is available |
| 343 | if (!IsValidMacAddress(preferred_mac)) { | 324 | if (!IsValidFakeIPAddress(preferred_fake_ip)) { |
| 344 | SendMacCollision(event->peer); | 325 | SendIPCollision(event->peer); |
| 345 | return; | 326 | return; |
| 346 | } | 327 | } |
| 347 | } else { | 328 | } else { |
| 348 | // Assign a MAC address of this client automatically | 329 | // Assign a fake ip address of this client automatically |
| 349 | preferred_mac = GenerateMacAddress(); | 330 | preferred_fake_ip = GenerateFakeIPAddress(); |
| 350 | } | ||
| 351 | |||
| 352 | if (!IsValidConsoleId(console_id_hash)) { | ||
| 353 | SendConsoleIdCollision(event->peer); | ||
| 354 | return; | ||
| 355 | } | 331 | } |
| 356 | 332 | ||
| 357 | if (client_version != network_version) { | 333 | if (client_version != network_version) { |
| @@ -361,8 +337,7 @@ void Room::RoomImpl::HandleJoinRequest(const ENetEvent* event) { | |||
| 361 | 337 | ||
| 362 | // At this point the client is ready to be added to the room. | 338 | // At this point the client is ready to be added to the room. |
| 363 | Member member{}; | 339 | Member member{}; |
| 364 | member.mac_address = preferred_mac; | 340 | member.fake_ip = preferred_fake_ip; |
| 365 | member.console_id_hash = console_id_hash; | ||
| 366 | member.nickname = nickname; | 341 | member.nickname = nickname; |
| 367 | member.peer = event->peer; | 342 | member.peer = event->peer; |
| 368 | 343 | ||
| @@ -408,9 +383,9 @@ void Room::RoomImpl::HandleJoinRequest(const ENetEvent* event) { | |||
| 408 | // Notify everyone that the room information has changed. | 383 | // Notify everyone that the room information has changed. |
| 409 | BroadcastRoomInformation(); | 384 | BroadcastRoomInformation(); |
| 410 | if (HasModPermission(event->peer)) { | 385 | if (HasModPermission(event->peer)) { |
| 411 | SendJoinSuccessAsMod(event->peer, preferred_mac); | 386 | SendJoinSuccessAsMod(event->peer, preferred_fake_ip); |
| 412 | } else { | 387 | } else { |
| 413 | SendJoinSuccess(event->peer, preferred_mac); | 388 | SendJoinSuccess(event->peer, preferred_fake_ip); |
| 414 | } | 389 | } |
| 415 | } | 390 | } |
| 416 | 391 | ||
| @@ -575,19 +550,11 @@ bool Room::RoomImpl::IsValidNickname(const std::string& nickname) const { | |||
| 575 | [&nickname](const auto& member) { return member.nickname != nickname; }); | 550 | [&nickname](const auto& member) { return member.nickname != nickname; }); |
| 576 | } | 551 | } |
| 577 | 552 | ||
| 578 | bool Room::RoomImpl::IsValidMacAddress(const MacAddress& address) const { | 553 | bool Room::RoomImpl::IsValidFakeIPAddress(const IPv4Address& address) const { |
| 579 | // A MAC address is valid if it is not already taken by anybody else in the room. | 554 | // An IP address is valid if it is not already taken by anybody else in the room. |
| 580 | std::lock_guard lock(member_mutex); | 555 | std::lock_guard lock(member_mutex); |
| 581 | return std::all_of(members.begin(), members.end(), | 556 | return std::all_of(members.begin(), members.end(), |
| 582 | [&address](const auto& member) { return member.mac_address != address; }); | 557 | [&address](const auto& member) { return member.fake_ip != address; }); |
| 583 | } | ||
| 584 | |||
| 585 | bool Room::RoomImpl::IsValidConsoleId(const std::string& console_id_hash) const { | ||
| 586 | // A Console ID is valid if it is not already taken by anybody else in the room. | ||
| 587 | std::lock_guard lock(member_mutex); | ||
| 588 | return std::all_of(members.begin(), members.end(), [&console_id_hash](const auto& member) { | ||
| 589 | return member.console_id_hash != console_id_hash; | ||
| 590 | }); | ||
| 591 | } | 558 | } |
| 592 | 559 | ||
| 593 | bool Room::RoomImpl::HasModPermission(const ENetPeer* client) const { | 560 | bool Room::RoomImpl::HasModPermission(const ENetPeer* client) const { |
| @@ -621,19 +588,9 @@ void Room::RoomImpl::SendNameCollision(ENetPeer* client) { | |||
| 621 | enet_host_flush(server); | 588 | enet_host_flush(server); |
| 622 | } | 589 | } |
| 623 | 590 | ||
| 624 | void Room::RoomImpl::SendMacCollision(ENetPeer* client) { | 591 | void Room::RoomImpl::SendIPCollision(ENetPeer* client) { |
| 625 | Packet packet; | 592 | Packet packet; |
| 626 | packet.Write(static_cast<u8>(IdMacCollision)); | 593 | packet.Write(static_cast<u8>(IdIpCollision)); |
| 627 | |||
| 628 | ENetPacket* enet_packet = | ||
| 629 | enet_packet_create(packet.GetData(), packet.GetDataSize(), ENET_PACKET_FLAG_RELIABLE); | ||
| 630 | enet_peer_send(client, 0, enet_packet); | ||
| 631 | enet_host_flush(server); | ||
| 632 | } | ||
| 633 | |||
| 634 | void Room::RoomImpl::SendConsoleIdCollision(ENetPeer* client) { | ||
| 635 | Packet packet; | ||
| 636 | packet.Write(static_cast<u8>(IdConsoleIdCollision)); | ||
| 637 | 594 | ||
| 638 | ENetPacket* enet_packet = | 595 | ENetPacket* enet_packet = |
| 639 | enet_packet_create(packet.GetData(), packet.GetDataSize(), ENET_PACKET_FLAG_RELIABLE); | 596 | enet_packet_create(packet.GetData(), packet.GetDataSize(), ENET_PACKET_FLAG_RELIABLE); |
| @@ -672,20 +629,20 @@ void Room::RoomImpl::SendVersionMismatch(ENetPeer* client) { | |||
| 672 | enet_host_flush(server); | 629 | enet_host_flush(server); |
| 673 | } | 630 | } |
| 674 | 631 | ||
| 675 | void Room::RoomImpl::SendJoinSuccess(ENetPeer* client, MacAddress mac_address) { | 632 | void Room::RoomImpl::SendJoinSuccess(ENetPeer* client, IPv4Address fake_ip) { |
| 676 | Packet packet; | 633 | Packet packet; |
| 677 | packet.Write(static_cast<u8>(IdJoinSuccess)); | 634 | packet.Write(static_cast<u8>(IdJoinSuccess)); |
| 678 | packet.Write(mac_address); | 635 | packet.Write(fake_ip); |
| 679 | ENetPacket* enet_packet = | 636 | ENetPacket* enet_packet = |
| 680 | enet_packet_create(packet.GetData(), packet.GetDataSize(), ENET_PACKET_FLAG_RELIABLE); | 637 | enet_packet_create(packet.GetData(), packet.GetDataSize(), ENET_PACKET_FLAG_RELIABLE); |
| 681 | enet_peer_send(client, 0, enet_packet); | 638 | enet_peer_send(client, 0, enet_packet); |
| 682 | enet_host_flush(server); | 639 | enet_host_flush(server); |
| 683 | } | 640 | } |
| 684 | 641 | ||
| 685 | void Room::RoomImpl::SendJoinSuccessAsMod(ENetPeer* client, MacAddress mac_address) { | 642 | void Room::RoomImpl::SendJoinSuccessAsMod(ENetPeer* client, IPv4Address fake_ip) { |
| 686 | Packet packet; | 643 | Packet packet; |
| 687 | packet.Write(static_cast<u8>(IdJoinSuccessAsMod)); | 644 | packet.Write(static_cast<u8>(IdJoinSuccessAsMod)); |
| 688 | packet.Write(mac_address); | 645 | packet.Write(fake_ip); |
| 689 | ENetPacket* enet_packet = | 646 | ENetPacket* enet_packet = |
| 690 | enet_packet_create(packet.GetData(), packet.GetDataSize(), ENET_PACKET_FLAG_RELIABLE); | 647 | enet_packet_create(packet.GetData(), packet.GetDataSize(), ENET_PACKET_FLAG_RELIABLE); |
| 691 | enet_peer_send(client, 0, enet_packet); | 648 | enet_peer_send(client, 0, enet_packet); |
| @@ -818,9 +775,10 @@ void Room::RoomImpl::BroadcastRoomInformation() { | |||
| 818 | std::lock_guard lock(member_mutex); | 775 | std::lock_guard lock(member_mutex); |
| 819 | for (const auto& member : members) { | 776 | for (const auto& member : members) { |
| 820 | packet.Write(member.nickname); | 777 | packet.Write(member.nickname); |
| 821 | packet.Write(member.mac_address); | 778 | packet.Write(member.fake_ip); |
| 822 | packet.Write(member.game_info.name); | 779 | packet.Write(member.game_info.name); |
| 823 | packet.Write(member.game_info.id); | 780 | packet.Write(member.game_info.id); |
| 781 | packet.Write(member.game_info.version); | ||
| 824 | packet.Write(member.user_data.username); | 782 | packet.Write(member.user_data.username); |
| 825 | packet.Write(member.user_data.display_name); | 783 | packet.Write(member.user_data.display_name); |
| 826 | packet.Write(member.user_data.avatar_url); | 784 | packet.Write(member.user_data.avatar_url); |
| @@ -833,34 +791,44 @@ void Room::RoomImpl::BroadcastRoomInformation() { | |||
| 833 | enet_host_flush(server); | 791 | enet_host_flush(server); |
| 834 | } | 792 | } |
| 835 | 793 | ||
| 836 | MacAddress Room::RoomImpl::GenerateMacAddress() { | 794 | IPv4Address Room::RoomImpl::GenerateFakeIPAddress() { |
| 837 | MacAddress result_mac = | 795 | IPv4Address result_ip{192, 168, 0, 0}; |
| 838 | NintendoOUI; // The first three bytes of each MAC address will be the NintendoOUI | 796 | std::uniform_int_distribution<> dis(0x01, 0xFE); // Random byte between 1 and 0xFE |
| 839 | std::uniform_int_distribution<> dis(0x00, 0xFF); // Random byte between 0 and 0xFF | ||
| 840 | do { | 797 | do { |
| 841 | for (std::size_t i = 3; i < result_mac.size(); ++i) { | 798 | for (std::size_t i = 2; i < result_ip.size(); ++i) { |
| 842 | result_mac[i] = dis(random_gen); | 799 | result_ip[i] = dis(random_gen); |
| 843 | } | 800 | } |
| 844 | } while (!IsValidMacAddress(result_mac)); | 801 | } while (!IsValidFakeIPAddress(result_ip)); |
| 845 | return result_mac; | 802 | |
| 803 | return result_ip; | ||
| 846 | } | 804 | } |
| 847 | 805 | ||
| 848 | void Room::RoomImpl::HandleWifiPacket(const ENetEvent* event) { | 806 | void Room::RoomImpl::HandleProxyPacket(const ENetEvent* event) { |
| 849 | Packet in_packet; | 807 | Packet in_packet; |
| 850 | in_packet.Append(event->packet->data, event->packet->dataLength); | 808 | in_packet.Append(event->packet->data, event->packet->dataLength); |
| 851 | in_packet.IgnoreBytes(sizeof(u8)); // Message type | 809 | in_packet.IgnoreBytes(sizeof(u8)); // Message type |
| 852 | in_packet.IgnoreBytes(sizeof(u8)); // WifiPacket Type | 810 | |
| 853 | in_packet.IgnoreBytes(sizeof(u8)); // WifiPacket Channel | 811 | in_packet.IgnoreBytes(sizeof(u8)); // Domain |
| 854 | in_packet.IgnoreBytes(sizeof(MacAddress)); // WifiPacket Transmitter Address | 812 | in_packet.IgnoreBytes(sizeof(IPv4Address)); // IP |
| 855 | MacAddress destination_address; | 813 | in_packet.IgnoreBytes(sizeof(u16)); // Port |
| 856 | in_packet.Read(destination_address); | 814 | |
| 815 | in_packet.IgnoreBytes(sizeof(u8)); // Domain | ||
| 816 | IPv4Address remote_ip; | ||
| 817 | in_packet.Read(remote_ip); // IP | ||
| 818 | in_packet.IgnoreBytes(sizeof(u16)); // Port | ||
| 819 | |||
| 820 | in_packet.IgnoreBytes(sizeof(u8)); // Protocol | ||
| 821 | |||
| 822 | bool broadcast; | ||
| 823 | in_packet.Read(broadcast); // Broadcast | ||
| 857 | 824 | ||
| 858 | Packet out_packet; | 825 | Packet out_packet; |
| 859 | out_packet.Append(event->packet->data, event->packet->dataLength); | 826 | out_packet.Append(event->packet->data, event->packet->dataLength); |
| 860 | ENetPacket* enet_packet = enet_packet_create(out_packet.GetData(), out_packet.GetDataSize(), | 827 | ENetPacket* enet_packet = enet_packet_create(out_packet.GetData(), out_packet.GetDataSize(), |
| 861 | ENET_PACKET_FLAG_RELIABLE); | 828 | ENET_PACKET_FLAG_RELIABLE); |
| 862 | 829 | ||
| 863 | if (destination_address == BroadcastMac) { // Send the data to everyone except the sender | 830 | const auto& destination_address = remote_ip; |
| 831 | if (broadcast) { // Send the data to everyone except the sender | ||
| 864 | std::lock_guard lock(member_mutex); | 832 | std::lock_guard lock(member_mutex); |
| 865 | bool sent_packet = false; | 833 | bool sent_packet = false; |
| 866 | for (const auto& member : members) { | 834 | for (const auto& member : members) { |
| @@ -877,16 +845,16 @@ void Room::RoomImpl::HandleWifiPacket(const ENetEvent* event) { | |||
| 877 | std::lock_guard lock(member_mutex); | 845 | std::lock_guard lock(member_mutex); |
| 878 | auto member = std::find_if(members.begin(), members.end(), | 846 | auto member = std::find_if(members.begin(), members.end(), |
| 879 | [destination_address](const Member& member_entry) -> bool { | 847 | [destination_address](const Member& member_entry) -> bool { |
| 880 | return member_entry.mac_address == destination_address; | 848 | return member_entry.fake_ip == destination_address; |
| 881 | }); | 849 | }); |
| 882 | if (member != members.end()) { | 850 | if (member != members.end()) { |
| 883 | enet_peer_send(member->peer, 0, enet_packet); | 851 | enet_peer_send(member->peer, 0, enet_packet); |
| 884 | } else { | 852 | } else { |
| 885 | LOG_ERROR(Network, | 853 | LOG_ERROR(Network, |
| 886 | "Attempting to send to unknown MAC address: " | 854 | "Attempting to send to unknown IP address: " |
| 887 | "{:02X}:{:02X}:{:02X}:{:02X}:{:02X}:{:02X}", | 855 | "{}.{}.{}.{}", |
| 888 | destination_address[0], destination_address[1], destination_address[2], | 856 | destination_address[0], destination_address[1], destination_address[2], |
| 889 | destination_address[3], destination_address[4], destination_address[5]); | 857 | destination_address[3]); |
| 890 | enet_packet_destroy(enet_packet); | 858 | enet_packet_destroy(enet_packet); |
| 891 | } | 859 | } |
| 892 | } | 860 | } |
| @@ -943,7 +911,7 @@ void Room::RoomImpl::HandleChatPacket(const ENetEvent* event) { | |||
| 943 | } | 911 | } |
| 944 | } | 912 | } |
| 945 | 913 | ||
| 946 | void Room::RoomImpl::HandleGameNamePacket(const ENetEvent* event) { | 914 | void Room::RoomImpl::HandleGameInfoPacket(const ENetEvent* event) { |
| 947 | Packet in_packet; | 915 | Packet in_packet; |
| 948 | in_packet.Append(event->packet->data, event->packet->dataLength); | 916 | in_packet.Append(event->packet->data, event->packet->dataLength); |
| 949 | 917 | ||
| @@ -951,6 +919,7 @@ void Room::RoomImpl::HandleGameNamePacket(const ENetEvent* event) { | |||
| 951 | GameInfo game_info; | 919 | GameInfo game_info; |
| 952 | in_packet.Read(game_info.name); | 920 | in_packet.Read(game_info.name); |
| 953 | in_packet.Read(game_info.id); | 921 | in_packet.Read(game_info.id); |
| 922 | in_packet.Read(game_info.version); | ||
| 954 | 923 | ||
| 955 | { | 924 | { |
| 956 | std::lock_guard lock(member_mutex); | 925 | std::lock_guard lock(member_mutex); |
| @@ -969,7 +938,8 @@ void Room::RoomImpl::HandleGameNamePacket(const ENetEvent* event) { | |||
| 969 | if (game_info.name.empty()) { | 938 | if (game_info.name.empty()) { |
| 970 | LOG_INFO(Network, "{} is not playing", display_name); | 939 | LOG_INFO(Network, "{} is not playing", display_name); |
| 971 | } else { | 940 | } else { |
| 972 | LOG_INFO(Network, "{} is playing {}", display_name, game_info.name); | 941 | LOG_INFO(Network, "{} is playing {} ({})", display_name, game_info.name, |
| 942 | game_info.version); | ||
| 973 | } | 943 | } |
| 974 | } | 944 | } |
| 975 | } | 945 | } |
| @@ -1073,7 +1043,7 @@ std::vector<Member> Room::GetRoomMemberList() const { | |||
| 1073 | member.username = member_impl.user_data.username; | 1043 | member.username = member_impl.user_data.username; |
| 1074 | member.display_name = member_impl.user_data.display_name; | 1044 | member.display_name = member_impl.user_data.display_name; |
| 1075 | member.avatar_url = member_impl.user_data.avatar_url; | 1045 | member.avatar_url = member_impl.user_data.avatar_url; |
| 1076 | member.mac_address = member_impl.mac_address; | 1046 | member.fake_ip = member_impl.fake_ip; |
| 1077 | member.game = member_impl.game_info; | 1047 | member.game = member_impl.game_info; |
| 1078 | member_list.push_back(member); | 1048 | member_list.push_back(member); |
| 1079 | } | 1049 | } |
diff --git a/src/network/room.h b/src/network/room.h index 6f7e3b5b5..c2a4b1a70 100644 --- a/src/network/room.h +++ b/src/network/room.h | |||
| @@ -9,12 +9,12 @@ | |||
| 9 | #include <vector> | 9 | #include <vector> |
| 10 | #include "common/announce_multiplayer_room.h" | 10 | #include "common/announce_multiplayer_room.h" |
| 11 | #include "common/common_types.h" | 11 | #include "common/common_types.h" |
| 12 | #include "common/socket_types.h" | ||
| 12 | #include "network/verify_user.h" | 13 | #include "network/verify_user.h" |
| 13 | 14 | ||
| 14 | namespace Network { | 15 | namespace Network { |
| 15 | 16 | ||
| 16 | using AnnounceMultiplayerRoom::GameInfo; | 17 | using AnnounceMultiplayerRoom::GameInfo; |
| 17 | using AnnounceMultiplayerRoom::MacAddress; | ||
| 18 | using AnnounceMultiplayerRoom::Member; | 18 | using AnnounceMultiplayerRoom::Member; |
| 19 | using AnnounceMultiplayerRoom::RoomInformation; | 19 | using AnnounceMultiplayerRoom::RoomInformation; |
| 20 | 20 | ||
| @@ -29,12 +29,9 @@ static constexpr u32 MaxConcurrentConnections = 254; | |||
| 29 | 29 | ||
| 30 | constexpr std::size_t NumChannels = 1; // Number of channels used for the connection | 30 | constexpr std::size_t NumChannels = 1; // Number of channels used for the connection |
| 31 | 31 | ||
| 32 | /// A special MAC address that tells the room we're joining to assign us a MAC address | 32 | /// A special IP address that tells the room we're joining to assign us a IP address |
| 33 | /// automatically. | 33 | /// automatically. |
| 34 | constexpr MacAddress NoPreferredMac = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; | 34 | constexpr IPv4Address NoPreferredIP = {0xFF, 0xFF, 0xFF, 0xFF}; |
| 35 | |||
| 36 | // 802.11 broadcast MAC address | ||
| 37 | constexpr MacAddress BroadcastMac = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; | ||
| 38 | 35 | ||
| 39 | // The different types of messages that can be sent. The first byte of each packet defines the type | 36 | // The different types of messages that can be sent. The first byte of each packet defines the type |
| 40 | enum RoomMessageTypes : u8 { | 37 | enum RoomMessageTypes : u8 { |
| @@ -42,15 +39,14 @@ enum RoomMessageTypes : u8 { | |||
| 42 | IdJoinSuccess, | 39 | IdJoinSuccess, |
| 43 | IdRoomInformation, | 40 | IdRoomInformation, |
| 44 | IdSetGameInfo, | 41 | IdSetGameInfo, |
| 45 | IdWifiPacket, | 42 | IdProxyPacket, |
| 46 | IdChatMessage, | 43 | IdChatMessage, |
| 47 | IdNameCollision, | 44 | IdNameCollision, |
| 48 | IdMacCollision, | 45 | IdIpCollision, |
| 49 | IdVersionMismatch, | 46 | IdVersionMismatch, |
| 50 | IdWrongPassword, | 47 | IdWrongPassword, |
| 51 | IdCloseRoom, | 48 | IdCloseRoom, |
| 52 | IdRoomIsFull, | 49 | IdRoomIsFull, |
| 53 | IdConsoleIdCollision, | ||
| 54 | IdStatusMessage, | 50 | IdStatusMessage, |
| 55 | IdHostKicked, | 51 | IdHostKicked, |
| 56 | IdHostBanned, | 52 | IdHostBanned, |
diff --git a/src/network/room_member.cpp b/src/network/room_member.cpp index e4f823e98..06818af78 100644 --- a/src/network/room_member.cpp +++ b/src/network/room_member.cpp | |||
| @@ -7,6 +7,7 @@ | |||
| 7 | #include <set> | 7 | #include <set> |
| 8 | #include <thread> | 8 | #include <thread> |
| 9 | #include "common/assert.h" | 9 | #include "common/assert.h" |
| 10 | #include "common/socket_types.h" | ||
| 10 | #include "enet/enet.h" | 11 | #include "enet/enet.h" |
| 11 | #include "network/packet.h" | 12 | #include "network/packet.h" |
| 12 | #include "network/room_member.h" | 13 | #include "network/room_member.h" |
| @@ -38,7 +39,7 @@ public: | |||
| 38 | std::string username; ///< The username of this member. | 39 | std::string username; ///< The username of this member. |
| 39 | mutable std::mutex username_mutex; ///< Mutex for locking username. | 40 | mutable std::mutex username_mutex; ///< Mutex for locking username. |
| 40 | 41 | ||
| 41 | MacAddress mac_address; ///< The mac_address of this member. | 42 | IPv4Address fake_ip; ///< The fake ip of this member. |
| 42 | 43 | ||
| 43 | std::mutex network_mutex; ///< Mutex that controls access to the `client` variable. | 44 | std::mutex network_mutex; ///< Mutex that controls access to the `client` variable. |
| 44 | /// Thread that receives and dispatches network packets | 45 | /// Thread that receives and dispatches network packets |
| @@ -56,7 +57,7 @@ public: | |||
| 56 | CallbackSet<T>& Get(); | 57 | CallbackSet<T>& Get(); |
| 57 | 58 | ||
| 58 | private: | 59 | private: |
| 59 | CallbackSet<WifiPacket> callback_set_wifi_packet; | 60 | CallbackSet<ProxyPacket> callback_set_proxy_packet; |
| 60 | CallbackSet<ChatEntry> callback_set_chat_messages; | 61 | CallbackSet<ChatEntry> callback_set_chat_messages; |
| 61 | CallbackSet<StatusMessageEntry> callback_set_status_messages; | 62 | CallbackSet<StatusMessageEntry> callback_set_status_messages; |
| 62 | CallbackSet<RoomInformation> callback_set_room_information; | 63 | CallbackSet<RoomInformation> callback_set_room_information; |
| @@ -78,15 +79,15 @@ public: | |||
| 78 | 79 | ||
| 79 | /** | 80 | /** |
| 80 | * Sends a request to the server, asking for permission to join a room with the specified | 81 | * Sends a request to the server, asking for permission to join a room with the specified |
| 81 | * nickname and preferred mac. | 82 | * nickname and preferred fake ip. |
| 82 | * @params nickname The desired nickname. | 83 | * @params nickname The desired nickname. |
| 83 | * @params console_id_hash A hash of the Console ID. | 84 | * @params preferred_fake_ip The preferred IP address to use in the room, the NoPreferredIP |
| 84 | * @params preferred_mac The preferred MAC address to use in the room, the NoPreferredMac tells | 85 | * tells |
| 85 | * @params password The password for the room | 86 | * @params password The password for the room |
| 86 | * the server to assign one for us. | 87 | * the server to assign one for us. |
| 87 | */ | 88 | */ |
| 88 | void SendJoinRequest(const std::string& nickname_, const std::string& console_id_hash, | 89 | void SendJoinRequest(const std::string& nickname_, |
| 89 | const MacAddress& preferred_mac = NoPreferredMac, | 90 | const IPv4Address& preferred_fake_ip = NoPreferredIP, |
| 90 | const std::string& password = "", const std::string& token = ""); | 91 | const std::string& password = "", const std::string& token = ""); |
| 91 | 92 | ||
| 92 | /** | 93 | /** |
| @@ -101,10 +102,10 @@ public: | |||
| 101 | void HandleRoomInformationPacket(const ENetEvent* event); | 102 | void HandleRoomInformationPacket(const ENetEvent* event); |
| 102 | 103 | ||
| 103 | /** | 104 | /** |
| 104 | * Extracts a WifiPacket from a received ENet packet. | 105 | * Extracts a ProxyPacket from a received ENet packet. |
| 105 | * @param event The ENet event that was received. | 106 | * @param event The ENet event that was received. |
| 106 | */ | 107 | */ |
| 107 | void HandleWifiPackets(const ENetEvent* event); | 108 | void HandleProxyPackets(const ENetEvent* event); |
| 108 | 109 | ||
| 109 | /** | 110 | /** |
| 110 | * Extracts a chat entry from a received ENet packet and adds it to the chat queue. | 111 | * Extracts a chat entry from a received ENet packet and adds it to the chat queue. |
| @@ -158,12 +159,12 @@ void RoomMember::RoomMemberImpl::MemberLoop() { | |||
| 158 | while (IsConnected()) { | 159 | while (IsConnected()) { |
| 159 | std::lock_guard lock(network_mutex); | 160 | std::lock_guard lock(network_mutex); |
| 160 | ENetEvent event; | 161 | ENetEvent event; |
| 161 | if (enet_host_service(client, &event, 16) > 0) { | 162 | if (enet_host_service(client, &event, 5) > 0) { |
| 162 | switch (event.type) { | 163 | switch (event.type) { |
| 163 | case ENET_EVENT_TYPE_RECEIVE: | 164 | case ENET_EVENT_TYPE_RECEIVE: |
| 164 | switch (event.packet->data[0]) { | 165 | switch (event.packet->data[0]) { |
| 165 | case IdWifiPacket: | 166 | case IdProxyPacket: |
| 166 | HandleWifiPackets(&event); | 167 | HandleProxyPackets(&event); |
| 167 | break; | 168 | break; |
| 168 | case IdChatMessage: | 169 | case IdChatMessage: |
| 169 | HandleChatPacket(&event); | 170 | HandleChatPacket(&event); |
| @@ -198,13 +199,9 @@ void RoomMember::RoomMemberImpl::MemberLoop() { | |||
| 198 | SetState(State::Idle); | 199 | SetState(State::Idle); |
| 199 | SetError(Error::NameCollision); | 200 | SetError(Error::NameCollision); |
| 200 | break; | 201 | break; |
| 201 | case IdMacCollision: | 202 | case IdIpCollision: |
| 202 | SetState(State::Idle); | ||
| 203 | SetError(Error::MacCollision); | ||
| 204 | break; | ||
| 205 | case IdConsoleIdCollision: | ||
| 206 | SetState(State::Idle); | 203 | SetState(State::Idle); |
| 207 | SetError(Error::ConsoleIdCollision); | 204 | SetError(Error::IpCollision); |
| 208 | break; | 205 | break; |
| 209 | case IdVersionMismatch: | 206 | case IdVersionMismatch: |
| 210 | SetState(State::Idle); | 207 | SetState(State::Idle); |
| @@ -275,15 +272,13 @@ void RoomMember::RoomMemberImpl::Send(Packet&& packet) { | |||
| 275 | } | 272 | } |
| 276 | 273 | ||
| 277 | void RoomMember::RoomMemberImpl::SendJoinRequest(const std::string& nickname_, | 274 | void RoomMember::RoomMemberImpl::SendJoinRequest(const std::string& nickname_, |
| 278 | const std::string& console_id_hash, | 275 | const IPv4Address& preferred_fake_ip, |
| 279 | const MacAddress& preferred_mac, | ||
| 280 | const std::string& password, | 276 | const std::string& password, |
| 281 | const std::string& token) { | 277 | const std::string& token) { |
| 282 | Packet packet; | 278 | Packet packet; |
| 283 | packet.Write(static_cast<u8>(IdJoinRequest)); | 279 | packet.Write(static_cast<u8>(IdJoinRequest)); |
| 284 | packet.Write(nickname_); | 280 | packet.Write(nickname_); |
| 285 | packet.Write(console_id_hash); | 281 | packet.Write(preferred_fake_ip); |
| 286 | packet.Write(preferred_mac); | ||
| 287 | packet.Write(network_version); | 282 | packet.Write(network_version); |
| 288 | packet.Write(password); | 283 | packet.Write(password); |
| 289 | packet.Write(token); | 284 | packet.Write(token); |
| @@ -317,9 +312,10 @@ void RoomMember::RoomMemberImpl::HandleRoomInformationPacket(const ENetEvent* ev | |||
| 317 | 312 | ||
| 318 | for (auto& member : member_information) { | 313 | for (auto& member : member_information) { |
| 319 | packet.Read(member.nickname); | 314 | packet.Read(member.nickname); |
| 320 | packet.Read(member.mac_address); | 315 | packet.Read(member.fake_ip); |
| 321 | packet.Read(member.game_info.name); | 316 | packet.Read(member.game_info.name); |
| 322 | packet.Read(member.game_info.id); | 317 | packet.Read(member.game_info.id); |
| 318 | packet.Read(member.game_info.version); | ||
| 323 | packet.Read(member.username); | 319 | packet.Read(member.username); |
| 324 | packet.Read(member.display_name); | 320 | packet.Read(member.display_name); |
| 325 | packet.Read(member.avatar_url); | 321 | packet.Read(member.avatar_url); |
| @@ -342,29 +338,38 @@ void RoomMember::RoomMemberImpl::HandleJoinPacket(const ENetEvent* event) { | |||
| 342 | packet.IgnoreBytes(sizeof(u8)); // Ignore the message type | 338 | packet.IgnoreBytes(sizeof(u8)); // Ignore the message type |
| 343 | 339 | ||
| 344 | // Parse the MAC Address from the packet | 340 | // Parse the MAC Address from the packet |
| 345 | packet.Read(mac_address); | 341 | packet.Read(fake_ip); |
| 346 | } | 342 | } |
| 347 | 343 | ||
| 348 | void RoomMember::RoomMemberImpl::HandleWifiPackets(const ENetEvent* event) { | 344 | void RoomMember::RoomMemberImpl::HandleProxyPackets(const ENetEvent* event) { |
| 349 | WifiPacket wifi_packet{}; | 345 | ProxyPacket proxy_packet{}; |
| 350 | Packet packet; | 346 | Packet packet; |
| 351 | packet.Append(event->packet->data, event->packet->dataLength); | 347 | packet.Append(event->packet->data, event->packet->dataLength); |
| 352 | 348 | ||
| 353 | // Ignore the first byte, which is the message id. | 349 | // Ignore the first byte, which is the message id. |
| 354 | packet.IgnoreBytes(sizeof(u8)); // Ignore the message type | 350 | packet.IgnoreBytes(sizeof(u8)); // Ignore the message type |
| 355 | 351 | ||
| 356 | // Parse the WifiPacket from the packet | 352 | // Parse the ProxyPacket from the packet |
| 357 | u8 frame_type; | 353 | u8 local_family; |
| 358 | packet.Read(frame_type); | 354 | packet.Read(local_family); |
| 359 | WifiPacket::PacketType type = static_cast<WifiPacket::PacketType>(frame_type); | 355 | proxy_packet.local_endpoint.family = static_cast<Domain>(local_family); |
| 356 | packet.Read(proxy_packet.local_endpoint.ip); | ||
| 357 | packet.Read(proxy_packet.local_endpoint.portno); | ||
| 358 | |||
| 359 | u8 remote_family; | ||
| 360 | packet.Read(remote_family); | ||
| 361 | proxy_packet.remote_endpoint.family = static_cast<Domain>(remote_family); | ||
| 362 | packet.Read(proxy_packet.remote_endpoint.ip); | ||
| 363 | packet.Read(proxy_packet.remote_endpoint.portno); | ||
| 364 | |||
| 365 | u8 protocol_type; | ||
| 366 | packet.Read(protocol_type); | ||
| 367 | proxy_packet.protocol = static_cast<Protocol>(protocol_type); | ||
| 360 | 368 | ||
| 361 | wifi_packet.type = type; | 369 | packet.Read(proxy_packet.broadcast); |
| 362 | packet.Read(wifi_packet.channel); | 370 | packet.Read(proxy_packet.data); |
| 363 | packet.Read(wifi_packet.transmitter_address); | ||
| 364 | packet.Read(wifi_packet.destination_address); | ||
| 365 | packet.Read(wifi_packet.data); | ||
| 366 | 371 | ||
| 367 | Invoke<WifiPacket>(wifi_packet); | 372 | Invoke<ProxyPacket>(proxy_packet); |
| 368 | } | 373 | } |
| 369 | 374 | ||
| 370 | void RoomMember::RoomMemberImpl::HandleChatPacket(const ENetEvent* event) { | 375 | void RoomMember::RoomMemberImpl::HandleChatPacket(const ENetEvent* event) { |
| @@ -440,8 +445,8 @@ void RoomMember::RoomMemberImpl::Disconnect() { | |||
| 440 | } | 445 | } |
| 441 | 446 | ||
| 442 | template <> | 447 | template <> |
| 443 | RoomMember::RoomMemberImpl::CallbackSet<WifiPacket>& RoomMember::RoomMemberImpl::Callbacks::Get() { | 448 | RoomMember::RoomMemberImpl::CallbackSet<ProxyPacket>& RoomMember::RoomMemberImpl::Callbacks::Get() { |
| 444 | return callback_set_wifi_packet; | 449 | return callback_set_proxy_packet; |
| 445 | } | 450 | } |
| 446 | 451 | ||
| 447 | template <> | 452 | template <> |
| @@ -525,19 +530,18 @@ const std::string& RoomMember::GetUsername() const { | |||
| 525 | return room_member_impl->username; | 530 | return room_member_impl->username; |
| 526 | } | 531 | } |
| 527 | 532 | ||
| 528 | const MacAddress& RoomMember::GetMacAddress() const { | 533 | const IPv4Address& RoomMember::GetFakeIpAddress() const { |
| 529 | ASSERT_MSG(IsConnected(), "Tried to get MAC address while not connected"); | 534 | ASSERT_MSG(IsConnected(), "Tried to get fake ip address while not connected"); |
| 530 | return room_member_impl->mac_address; | 535 | return room_member_impl->fake_ip; |
| 531 | } | 536 | } |
| 532 | 537 | ||
| 533 | RoomInformation RoomMember::GetRoomInformation() const { | 538 | RoomInformation RoomMember::GetRoomInformation() const { |
| 534 | return room_member_impl->room_information; | 539 | return room_member_impl->room_information; |
| 535 | } | 540 | } |
| 536 | 541 | ||
| 537 | void RoomMember::Join(const std::string& nick, const std::string& console_id_hash, | 542 | void RoomMember::Join(const std::string& nick, const char* server_addr, u16 server_port, |
| 538 | const char* server_addr, u16 server_port, u16 client_port, | 543 | u16 client_port, const IPv4Address& preferred_fake_ip, |
| 539 | const MacAddress& preferred_mac, const std::string& password, | 544 | const std::string& password, const std::string& token) { |
| 540 | const std::string& token) { | ||
| 541 | // If the member is connected, kill the connection first | 545 | // If the member is connected, kill the connection first |
| 542 | if (room_member_impl->loop_thread && room_member_impl->loop_thread->joinable()) { | 546 | if (room_member_impl->loop_thread && room_member_impl->loop_thread->joinable()) { |
| 543 | Leave(); | 547 | Leave(); |
| @@ -571,7 +575,7 @@ void RoomMember::Join(const std::string& nick, const std::string& console_id_has | |||
| 571 | if (net > 0 && event.type == ENET_EVENT_TYPE_CONNECT) { | 575 | if (net > 0 && event.type == ENET_EVENT_TYPE_CONNECT) { |
| 572 | room_member_impl->nickname = nick; | 576 | room_member_impl->nickname = nick; |
| 573 | room_member_impl->StartLoop(); | 577 | room_member_impl->StartLoop(); |
| 574 | room_member_impl->SendJoinRequest(nick, console_id_hash, preferred_mac, password, token); | 578 | room_member_impl->SendJoinRequest(nick, preferred_fake_ip, password, token); |
| 575 | SendGameInfo(room_member_impl->current_game_info); | 579 | SendGameInfo(room_member_impl->current_game_info); |
| 576 | } else { | 580 | } else { |
| 577 | enet_peer_disconnect(room_member_impl->server, 0); | 581 | enet_peer_disconnect(room_member_impl->server, 0); |
| @@ -584,14 +588,22 @@ bool RoomMember::IsConnected() const { | |||
| 584 | return room_member_impl->IsConnected(); | 588 | return room_member_impl->IsConnected(); |
| 585 | } | 589 | } |
| 586 | 590 | ||
| 587 | void RoomMember::SendWifiPacket(const WifiPacket& wifi_packet) { | 591 | void RoomMember::SendProxyPacket(const ProxyPacket& proxy_packet) { |
| 588 | Packet packet; | 592 | Packet packet; |
| 589 | packet.Write(static_cast<u8>(IdWifiPacket)); | 593 | packet.Write(static_cast<u8>(IdProxyPacket)); |
| 590 | packet.Write(static_cast<u8>(wifi_packet.type)); | 594 | |
| 591 | packet.Write(wifi_packet.channel); | 595 | packet.Write(static_cast<u8>(proxy_packet.local_endpoint.family)); |
| 592 | packet.Write(wifi_packet.transmitter_address); | 596 | packet.Write(proxy_packet.local_endpoint.ip); |
| 593 | packet.Write(wifi_packet.destination_address); | 597 | packet.Write(proxy_packet.local_endpoint.portno); |
| 594 | packet.Write(wifi_packet.data); | 598 | |
| 599 | packet.Write(static_cast<u8>(proxy_packet.remote_endpoint.family)); | ||
| 600 | packet.Write(proxy_packet.remote_endpoint.ip); | ||
| 601 | packet.Write(proxy_packet.remote_endpoint.portno); | ||
| 602 | |||
| 603 | packet.Write(static_cast<u8>(proxy_packet.protocol)); | ||
| 604 | packet.Write(proxy_packet.broadcast); | ||
| 605 | packet.Write(proxy_packet.data); | ||
| 606 | |||
| 595 | room_member_impl->Send(std::move(packet)); | 607 | room_member_impl->Send(std::move(packet)); |
| 596 | } | 608 | } |
| 597 | 609 | ||
| @@ -611,6 +623,7 @@ void RoomMember::SendGameInfo(const GameInfo& game_info) { | |||
| 611 | packet.Write(static_cast<u8>(IdSetGameInfo)); | 623 | packet.Write(static_cast<u8>(IdSetGameInfo)); |
| 612 | packet.Write(game_info.name); | 624 | packet.Write(game_info.name); |
| 613 | packet.Write(game_info.id); | 625 | packet.Write(game_info.id); |
| 626 | packet.Write(game_info.version); | ||
| 614 | room_member_impl->Send(std::move(packet)); | 627 | room_member_impl->Send(std::move(packet)); |
| 615 | } | 628 | } |
| 616 | 629 | ||
| @@ -645,8 +658,8 @@ RoomMember::CallbackHandle<RoomMember::Error> RoomMember::BindOnError( | |||
| 645 | return room_member_impl->Bind(callback); | 658 | return room_member_impl->Bind(callback); |
| 646 | } | 659 | } |
| 647 | 660 | ||
| 648 | RoomMember::CallbackHandle<WifiPacket> RoomMember::BindOnWifiPacketReceived( | 661 | RoomMember::CallbackHandle<ProxyPacket> RoomMember::BindOnProxyPacketReceived( |
| 649 | std::function<void(const WifiPacket&)> callback) { | 662 | std::function<void(const ProxyPacket&)> callback) { |
| 650 | return room_member_impl->Bind(callback); | 663 | return room_member_impl->Bind(callback); |
| 651 | } | 664 | } |
| 652 | 665 | ||
| @@ -685,7 +698,7 @@ void RoomMember::Leave() { | |||
| 685 | room_member_impl->client = nullptr; | 698 | room_member_impl->client = nullptr; |
| 686 | } | 699 | } |
| 687 | 700 | ||
| 688 | template void RoomMember::Unbind(CallbackHandle<WifiPacket>); | 701 | template void RoomMember::Unbind(CallbackHandle<ProxyPacket>); |
| 689 | template void RoomMember::Unbind(CallbackHandle<RoomMember::State>); | 702 | template void RoomMember::Unbind(CallbackHandle<RoomMember::State>); |
| 690 | template void RoomMember::Unbind(CallbackHandle<RoomMember::Error>); | 703 | template void RoomMember::Unbind(CallbackHandle<RoomMember::Error>); |
| 691 | template void RoomMember::Unbind(CallbackHandle<RoomInformation>); | 704 | template void RoomMember::Unbind(CallbackHandle<RoomInformation>); |
diff --git a/src/network/room_member.h b/src/network/room_member.h index bbb7d13d4..f578f7f6a 100644 --- a/src/network/room_member.h +++ b/src/network/room_member.h | |||
| @@ -9,6 +9,7 @@ | |||
| 9 | #include <vector> | 9 | #include <vector> |
| 10 | #include "common/announce_multiplayer_room.h" | 10 | #include "common/announce_multiplayer_room.h" |
| 11 | #include "common/common_types.h" | 11 | #include "common/common_types.h" |
| 12 | #include "common/socket_types.h" | ||
| 12 | #include "network/room.h" | 13 | #include "network/room.h" |
| 13 | 14 | ||
| 14 | namespace Network { | 15 | namespace Network { |
| @@ -17,22 +18,12 @@ using AnnounceMultiplayerRoom::GameInfo; | |||
| 17 | using AnnounceMultiplayerRoom::RoomInformation; | 18 | using AnnounceMultiplayerRoom::RoomInformation; |
| 18 | 19 | ||
| 19 | /// Information about the received WiFi packets. | 20 | /// Information about the received WiFi packets. |
| 20 | /// Acts as our own 802.11 header. | 21 | struct ProxyPacket { |
| 21 | struct WifiPacket { | 22 | SockAddrIn local_endpoint; |
| 22 | enum class PacketType : u8 { | 23 | SockAddrIn remote_endpoint; |
| 23 | Beacon, | 24 | Protocol protocol; |
| 24 | Data, | 25 | bool broadcast; |
| 25 | Authentication, | 26 | std::vector<u8> data; |
| 26 | AssociationResponse, | ||
| 27 | Deauthentication, | ||
| 28 | NodeMap | ||
| 29 | }; | ||
| 30 | PacketType type; ///< The type of 802.11 frame. | ||
| 31 | std::vector<u8> data; ///< Raw 802.11 frame data, starting at the management frame header | ||
| 32 | /// for management frames. | ||
| 33 | MacAddress transmitter_address; ///< Mac address of the transmitter. | ||
| 34 | MacAddress destination_address; ///< Mac address of the receiver. | ||
| 35 | u8 channel; ///< WiFi channel where this frame was transmitted. | ||
| 36 | }; | 27 | }; |
| 37 | 28 | ||
| 38 | /// Represents a chat message. | 29 | /// Represents a chat message. |
| @@ -72,15 +63,14 @@ public: | |||
| 72 | HostKicked, ///< Kicked by the host | 63 | HostKicked, ///< Kicked by the host |
| 73 | 64 | ||
| 74 | // Reasons why connection was rejected | 65 | // Reasons why connection was rejected |
| 75 | UnknownError, ///< Some error [permissions to network device missing or something] | 66 | UnknownError, ///< Some error [permissions to network device missing or something] |
| 76 | NameCollision, ///< Somebody is already using this name | 67 | NameCollision, ///< Somebody is already using this name |
| 77 | MacCollision, ///< Somebody is already using that mac-address | 68 | IpCollision, ///< Somebody is already using that fake-ip-address |
| 78 | ConsoleIdCollision, ///< Somebody in the room has the same Console ID | 69 | WrongVersion, ///< The room version is not the same as for this RoomMember |
| 79 | WrongVersion, ///< The room version is not the same as for this RoomMember | 70 | WrongPassword, ///< The password doesn't match the one from the Room |
| 80 | WrongPassword, ///< The password doesn't match the one from the Room | 71 | CouldNotConnect, ///< The room is not responding to a connection attempt |
| 81 | CouldNotConnect, ///< The room is not responding to a connection attempt | 72 | RoomIsFull, ///< Room is already at the maximum number of players |
| 82 | RoomIsFull, ///< Room is already at the maximum number of players | 73 | HostBanned, ///< The user is banned by the host |
| 83 | HostBanned, ///< The user is banned by the host | ||
| 84 | 74 | ||
| 85 | // Reasons why moderation request failed | 75 | // Reasons why moderation request failed |
| 86 | PermissionDenied, ///< The user does not have mod permissions | 76 | PermissionDenied, ///< The user does not have mod permissions |
| @@ -92,9 +82,9 @@ public: | |||
| 92 | std::string username; ///< The web services username of the member. Can be empty. | 82 | std::string username; ///< The web services username of the member. Can be empty. |
| 93 | std::string display_name; ///< The web services display name of the member. Can be empty. | 83 | std::string display_name; ///< The web services display name of the member. Can be empty. |
| 94 | std::string avatar_url; ///< Url to the member's avatar. Can be empty. | 84 | std::string avatar_url; ///< Url to the member's avatar. Can be empty. |
| 95 | GameInfo game_info; ///< Name of the game they're currently playing, or empty if they're | 85 | GameInfo game_info; ///< Name of the game they're currently playing, or empty if they're |
| 96 | /// not playing anything. | 86 | /// not playing anything. |
| 97 | MacAddress mac_address; ///< MAC address associated with this member. | 87 | IPv4Address fake_ip; ///< Fake Ip address associated with this member. |
| 98 | }; | 88 | }; |
| 99 | using MemberList = std::vector<MemberInformation>; | 89 | using MemberList = std::vector<MemberInformation>; |
| 100 | 90 | ||
| @@ -135,7 +125,7 @@ public: | |||
| 135 | /** | 125 | /** |
| 136 | * Returns the MAC address of the RoomMember. | 126 | * Returns the MAC address of the RoomMember. |
| 137 | */ | 127 | */ |
| 138 | const MacAddress& GetMacAddress() const; | 128 | const IPv4Address& GetFakeIpAddress() const; |
| 139 | 129 | ||
| 140 | /** | 130 | /** |
| 141 | * Returns information about the room we're currently connected to. | 131 | * Returns information about the room we're currently connected to. |
| @@ -149,19 +139,17 @@ public: | |||
| 149 | 139 | ||
| 150 | /** | 140 | /** |
| 151 | * Attempts to join a room at the specified address and port, using the specified nickname. | 141 | * Attempts to join a room at the specified address and port, using the specified nickname. |
| 152 | * A console ID hash is passed in to check console ID conflicts. | ||
| 153 | * This may fail if the username or console ID is already taken. | ||
| 154 | */ | 142 | */ |
| 155 | void Join(const std::string& nickname, const std::string& console_id_hash, | 143 | void Join(const std::string& nickname, const char* server_addr = "127.0.0.1", |
| 156 | const char* server_addr = "127.0.0.1", u16 server_port = DefaultRoomPort, | 144 | u16 server_port = DefaultRoomPort, u16 client_port = 0, |
| 157 | u16 client_port = 0, const MacAddress& preferred_mac = NoPreferredMac, | 145 | const IPv4Address& preferred_fake_ip = NoPreferredIP, |
| 158 | const std::string& password = "", const std::string& token = ""); | 146 | const std::string& password = "", const std::string& token = ""); |
| 159 | 147 | ||
| 160 | /** | 148 | /** |
| 161 | * Sends a WiFi packet to the room. | 149 | * Sends a Proxy packet to the room. |
| 162 | * @param packet The WiFi packet to send. | 150 | * @param packet The WiFi packet to send. |
| 163 | */ | 151 | */ |
| 164 | void SendWifiPacket(const WifiPacket& packet); | 152 | void SendProxyPacket(const ProxyPacket& packet); |
| 165 | 153 | ||
| 166 | /** | 154 | /** |
| 167 | * Sends a chat message to the room. | 155 | * Sends a chat message to the room. |
| @@ -207,14 +195,14 @@ public: | |||
| 207 | CallbackHandle<Error> BindOnError(std::function<void(const Error&)> callback); | 195 | CallbackHandle<Error> BindOnError(std::function<void(const Error&)> callback); |
| 208 | 196 | ||
| 209 | /** | 197 | /** |
| 210 | * Binds a function to an event that will be triggered every time a WifiPacket is received. | 198 | * Binds a function to an event that will be triggered every time a ProxyPacket is received. |
| 211 | * The function wil be called everytime the event is triggered. | 199 | * The function wil be called everytime the event is triggered. |
| 212 | * The callback function must not bind or unbind a function. Doing so will cause a deadlock | 200 | * The callback function must not bind or unbind a function. Doing so will cause a deadlock |
| 213 | * @param callback The function to call | 201 | * @param callback The function to call |
| 214 | * @return A handle used for removing the function from the registered list | 202 | * @return A handle used for removing the function from the registered list |
| 215 | */ | 203 | */ |
| 216 | CallbackHandle<WifiPacket> BindOnWifiPacketReceived( | 204 | CallbackHandle<ProxyPacket> BindOnProxyPacketReceived( |
| 217 | std::function<void(const WifiPacket&)> callback); | 205 | std::function<void(const ProxyPacket&)> callback); |
| 218 | 206 | ||
| 219 | /** | 207 | /** |
| 220 | * Binds a function to an event that will be triggered every time the RoomInformation changes. | 208 | * Binds a function to an event that will be triggered every time the RoomInformation changes. |
| @@ -292,10 +280,8 @@ inline const char* GetErrorStr(const RoomMember::Error& e) { | |||
| 292 | return "UnknownError"; | 280 | return "UnknownError"; |
| 293 | case RoomMember::Error::NameCollision: | 281 | case RoomMember::Error::NameCollision: |
| 294 | return "NameCollision"; | 282 | return "NameCollision"; |
| 295 | case RoomMember::Error::MacCollision: | 283 | case RoomMember::Error::IpCollision: |
| 296 | return "MaxCollision"; | 284 | return "IpCollision"; |
| 297 | case RoomMember::Error::ConsoleIdCollision: | ||
| 298 | return "ConsoleIdCollision"; | ||
| 299 | case RoomMember::Error::WrongVersion: | 285 | case RoomMember::Error::WrongVersion: |
| 300 | return "WrongVersion"; | 286 | return "WrongVersion"; |
| 301 | case RoomMember::Error::WrongPassword: | 287 | case RoomMember::Error::WrongPassword: |
diff --git a/src/shader_recompiler/backend/glasm/emit_glasm_image.cpp b/src/shader_recompiler/backend/glasm/emit_glasm_image.cpp index a97b143e4..e67e80fac 100644 --- a/src/shader_recompiler/backend/glasm/emit_glasm_image.cpp +++ b/src/shader_recompiler/backend/glasm/emit_glasm_image.cpp | |||
| @@ -67,6 +67,7 @@ std::string_view TextureType(IR::TextureInstInfo info) { | |||
| 67 | case TextureType::ColorArray1D: | 67 | case TextureType::ColorArray1D: |
| 68 | return "SHADOWARRAY1D"; | 68 | return "SHADOWARRAY1D"; |
| 69 | case TextureType::Color2D: | 69 | case TextureType::Color2D: |
| 70 | case TextureType::Color2DRect: | ||
| 70 | return "SHADOW2D"; | 71 | return "SHADOW2D"; |
| 71 | case TextureType::ColorArray2D: | 72 | case TextureType::ColorArray2D: |
| 72 | return "SHADOWARRAY2D"; | 73 | return "SHADOWARRAY2D"; |
| @@ -86,6 +87,7 @@ std::string_view TextureType(IR::TextureInstInfo info) { | |||
| 86 | case TextureType::ColorArray1D: | 87 | case TextureType::ColorArray1D: |
| 87 | return "ARRAY1D"; | 88 | return "ARRAY1D"; |
| 88 | case TextureType::Color2D: | 89 | case TextureType::Color2D: |
| 90 | case TextureType::Color2DRect: | ||
| 89 | return "2D"; | 91 | return "2D"; |
| 90 | case TextureType::ColorArray2D: | 92 | case TextureType::ColorArray2D: |
| 91 | return "ARRAY2D"; | 93 | return "ARRAY2D"; |
diff --git a/src/shader_recompiler/backend/glsl/emit_glsl_image.cpp b/src/shader_recompiler/backend/glsl/emit_glsl_image.cpp index 6af7e3fe6..cecdbb9d6 100644 --- a/src/shader_recompiler/backend/glsl/emit_glsl_image.cpp +++ b/src/shader_recompiler/backend/glsl/emit_glsl_image.cpp | |||
| @@ -466,6 +466,7 @@ void EmitImageQueryDimensions(EmitContext& ctx, IR::Inst& inst, const IR::Value& | |||
| 466 | case TextureType::ColorArray1D: | 466 | case TextureType::ColorArray1D: |
| 467 | case TextureType::Color2D: | 467 | case TextureType::Color2D: |
| 468 | case TextureType::ColorCube: | 468 | case TextureType::ColorCube: |
| 469 | case TextureType::Color2DRect: | ||
| 469 | return ctx.AddU32x4( | 470 | return ctx.AddU32x4( |
| 470 | "{}=uvec4(uvec2(textureSize({},int({}))),0u,uint(textureQueryLevels({})));", inst, | 471 | "{}=uvec4(uvec2(textureSize({},int({}))),0u,uint(textureQueryLevels({})));", inst, |
| 471 | texture, lod, texture); | 472 | texture, lod, texture); |
diff --git a/src/shader_recompiler/backend/glsl/glsl_emit_context.cpp b/src/shader_recompiler/backend/glsl/glsl_emit_context.cpp index 221b06328..c767a9dc3 100644 --- a/src/shader_recompiler/backend/glsl/glsl_emit_context.cpp +++ b/src/shader_recompiler/backend/glsl/glsl_emit_context.cpp | |||
| @@ -86,6 +86,7 @@ std::string_view SamplerType(TextureType type, bool is_depth) { | |||
| 86 | case TextureType::ColorArray1D: | 86 | case TextureType::ColorArray1D: |
| 87 | return "sampler1DArray"; | 87 | return "sampler1DArray"; |
| 88 | case TextureType::Color2D: | 88 | case TextureType::Color2D: |
| 89 | case TextureType::Color2DRect: | ||
| 89 | return "sampler2D"; | 90 | return "sampler2D"; |
| 90 | case TextureType::ColorArray2D: | 91 | case TextureType::ColorArray2D: |
| 91 | return "sampler2DArray"; | 92 | return "sampler2DArray"; |
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp index d8d86c91a..fb5799c42 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp +++ b/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp | |||
| @@ -453,6 +453,7 @@ Id EmitImageQueryDimensions(EmitContext& ctx, IR::Inst* inst, const IR::Value& i | |||
| 453 | case TextureType::ColorArray1D: | 453 | case TextureType::ColorArray1D: |
| 454 | case TextureType::Color2D: | 454 | case TextureType::Color2D: |
| 455 | case TextureType::ColorCube: | 455 | case TextureType::ColorCube: |
| 456 | case TextureType::Color2DRect: | ||
| 456 | return ctx.OpCompositeConstruct(ctx.U32[4], ctx.OpImageQuerySizeLod(ctx.U32[2], image, lod), | 457 | return ctx.OpCompositeConstruct(ctx.U32[4], ctx.OpImageQuerySizeLod(ctx.U32[2], image, lod), |
| 457 | zero, mips()); | 458 | zero, mips()); |
| 458 | case TextureType::ColorArray2D: | 459 | case TextureType::ColorArray2D: |
diff --git a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp index d4a49ea99..aecc4c612 100644 --- a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp +++ b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp | |||
| @@ -41,6 +41,7 @@ Id ImageType(EmitContext& ctx, const TextureDescriptor& desc) { | |||
| 41 | case TextureType::ColorArray1D: | 41 | case TextureType::ColorArray1D: |
| 42 | return ctx.TypeImage(type, spv::Dim::Dim1D, depth, true, false, 1, format); | 42 | return ctx.TypeImage(type, spv::Dim::Dim1D, depth, true, false, 1, format); |
| 43 | case TextureType::Color2D: | 43 | case TextureType::Color2D: |
| 44 | case TextureType::Color2DRect: | ||
| 44 | return ctx.TypeImage(type, spv::Dim::Dim2D, depth, false, false, 1, format); | 45 | return ctx.TypeImage(type, spv::Dim::Dim2D, depth, false, false, 1, format); |
| 45 | case TextureType::ColorArray2D: | 46 | case TextureType::ColorArray2D: |
| 46 | return ctx.TypeImage(type, spv::Dim::Dim2D, depth, true, false, 1, format); | 47 | return ctx.TypeImage(type, spv::Dim::Dim2D, depth, true, false, 1, format); |
| @@ -1306,7 +1307,7 @@ void EmitContext::DefineInputs(const IR::Program& program) { | |||
| 1306 | subgroup_mask_gt = DefineInput(*this, U32[4], false, spv::BuiltIn::SubgroupGtMaskKHR); | 1307 | subgroup_mask_gt = DefineInput(*this, U32[4], false, spv::BuiltIn::SubgroupGtMaskKHR); |
| 1307 | subgroup_mask_ge = DefineInput(*this, U32[4], false, spv::BuiltIn::SubgroupGeMaskKHR); | 1308 | subgroup_mask_ge = DefineInput(*this, U32[4], false, spv::BuiltIn::SubgroupGeMaskKHR); |
| 1308 | } | 1309 | } |
| 1309 | if (info.uses_subgroup_invocation_id || info.uses_subgroup_shuffles || | 1310 | if (info.uses_fswzadd || info.uses_subgroup_invocation_id || info.uses_subgroup_shuffles || |
| 1310 | (profile.warp_size_potentially_larger_than_guest && | 1311 | (profile.warp_size_potentially_larger_than_guest && |
| 1311 | (info.uses_subgroup_vote || info.uses_subgroup_mask))) { | 1312 | (info.uses_subgroup_vote || info.uses_subgroup_mask))) { |
| 1312 | subgroup_local_invocation_id = | 1313 | subgroup_local_invocation_id = |
| @@ -1411,7 +1412,8 @@ void EmitContext::DefineInputs(const IR::Program& program) { | |||
| 1411 | void EmitContext::DefineOutputs(const IR::Program& program) { | 1412 | void EmitContext::DefineOutputs(const IR::Program& program) { |
| 1412 | const Info& info{program.info}; | 1413 | const Info& info{program.info}; |
| 1413 | const std::optional<u32> invocations{program.invocations}; | 1414 | const std::optional<u32> invocations{program.invocations}; |
| 1414 | if (info.stores.AnyComponent(IR::Attribute::PositionX) || stage == Stage::VertexB) { | 1415 | if (runtime_info.convert_depth_mode || info.stores.AnyComponent(IR::Attribute::PositionX) || |
| 1416 | stage == Stage::VertexB) { | ||
| 1415 | output_position = DefineOutput(*this, F32[4], invocations, spv::BuiltIn::Position); | 1417 | output_position = DefineOutput(*this, F32[4], invocations, spv::BuiltIn::Position); |
| 1416 | } | 1418 | } |
| 1417 | if (info.stores[IR::Attribute::PointSize] || runtime_info.fixed_state_point_size) { | 1419 | if (info.stores[IR::Attribute::PointSize] || runtime_info.fixed_state_point_size) { |
diff --git a/src/shader_recompiler/frontend/ir/ir_emitter.cpp b/src/shader_recompiler/frontend/ir/ir_emitter.cpp index d2b658bca..11086ed8c 100644 --- a/src/shader_recompiler/frontend/ir/ir_emitter.cpp +++ b/src/shader_recompiler/frontend/ir/ir_emitter.cpp | |||
| @@ -1832,6 +1832,11 @@ Value IREmitter::ImageQueryDimension(const Value& handle, const IR::U32& lod) { | |||
| 1832 | return Inst(op, handle, lod); | 1832 | return Inst(op, handle, lod); |
| 1833 | } | 1833 | } |
| 1834 | 1834 | ||
| 1835 | Value IREmitter::ImageQueryDimension(const Value& handle, const IR::U32& lod, | ||
| 1836 | TextureInstInfo info) { | ||
| 1837 | return Inst(Opcode::ImageQueryDimensions, Flags{info}, handle, lod); | ||
| 1838 | } | ||
| 1839 | |||
| 1835 | Value IREmitter::ImageQueryLod(const Value& handle, const Value& coords, TextureInstInfo info) { | 1840 | Value IREmitter::ImageQueryLod(const Value& handle, const Value& coords, TextureInstInfo info) { |
| 1836 | const Opcode op{handle.IsImmediate() ? Opcode::BoundImageQueryLod | 1841 | const Opcode op{handle.IsImmediate() ? Opcode::BoundImageQueryLod |
| 1837 | : Opcode::BindlessImageQueryLod}; | 1842 | : Opcode::BindlessImageQueryLod}; |
diff --git a/src/shader_recompiler/frontend/ir/ir_emitter.h b/src/shader_recompiler/frontend/ir/ir_emitter.h index c29bda558..25839a371 100644 --- a/src/shader_recompiler/frontend/ir/ir_emitter.h +++ b/src/shader_recompiler/frontend/ir/ir_emitter.h | |||
| @@ -315,6 +315,8 @@ public: | |||
| 315 | const F32& dref, const F32& lod, | 315 | const F32& dref, const F32& lod, |
| 316 | const Value& offset, TextureInstInfo info); | 316 | const Value& offset, TextureInstInfo info); |
| 317 | [[nodiscard]] Value ImageQueryDimension(const Value& handle, const IR::U32& lod); | 317 | [[nodiscard]] Value ImageQueryDimension(const Value& handle, const IR::U32& lod); |
| 318 | [[nodiscard]] Value ImageQueryDimension(const Value& handle, const IR::U32& lod, | ||
| 319 | TextureInstInfo info); | ||
| 318 | 320 | ||
| 319 | [[nodiscard]] Value ImageQueryLod(const Value& handle, const Value& coords, | 321 | [[nodiscard]] Value ImageQueryLod(const Value& handle, const Value& coords, |
| 320 | TextureInstInfo info); | 322 | TextureInstInfo info); |
diff --git a/src/shader_recompiler/ir_opt/rescaling_pass.cpp b/src/shader_recompiler/ir_opt/rescaling_pass.cpp index 0d5f2e4d8..9198fa5f2 100644 --- a/src/shader_recompiler/ir_opt/rescaling_pass.cpp +++ b/src/shader_recompiler/ir_opt/rescaling_pass.cpp | |||
| @@ -16,6 +16,7 @@ namespace { | |||
| 16 | switch (type) { | 16 | switch (type) { |
| 17 | case TextureType::Color2D: | 17 | case TextureType::Color2D: |
| 18 | case TextureType::ColorArray2D: | 18 | case TextureType::ColorArray2D: |
| 19 | case TextureType::Color2DRect: | ||
| 19 | return true; | 20 | return true; |
| 20 | case TextureType::Color1D: | 21 | case TextureType::Color1D: |
| 21 | case TextureType::ColorArray1D: | 22 | case TextureType::ColorArray1D: |
| @@ -132,7 +133,8 @@ void PatchImageQueryDimensions(IR::Block& block, IR::Inst& inst) { | |||
| 132 | const IR::U1 is_scaled{ir.IsTextureScaled(ir.Imm32(info.descriptor_index))}; | 133 | const IR::U1 is_scaled{ir.IsTextureScaled(ir.Imm32(info.descriptor_index))}; |
| 133 | switch (info.type) { | 134 | switch (info.type) { |
| 134 | case TextureType::Color2D: | 135 | case TextureType::Color2D: |
| 135 | case TextureType::ColorArray2D: { | 136 | case TextureType::ColorArray2D: |
| 137 | case TextureType::Color2DRect: { | ||
| 136 | const IR::Value new_inst{&*block.PrependNewInst(it, inst)}; | 138 | const IR::Value new_inst{&*block.PrependNewInst(it, inst)}; |
| 137 | const IR::U32 width{DownScale(ir, is_scaled, IR::U32{ir.CompositeExtract(new_inst, 0)})}; | 139 | const IR::U32 width{DownScale(ir, is_scaled, IR::U32{ir.CompositeExtract(new_inst, 0)})}; |
| 138 | const IR::U32 height{DownScale(ir, is_scaled, IR::U32{ir.CompositeExtract(new_inst, 1)})}; | 140 | const IR::U32 height{DownScale(ir, is_scaled, IR::U32{ir.CompositeExtract(new_inst, 1)})}; |
| @@ -163,6 +165,7 @@ void ScaleIntegerComposite(IR::IREmitter& ir, IR::Inst& inst, const IR::U1& is_s | |||
| 163 | const IR::U32 y{Scale(ir, is_scaled, IR::U32{ir.CompositeExtract(composite, 1)})}; | 165 | const IR::U32 y{Scale(ir, is_scaled, IR::U32{ir.CompositeExtract(composite, 1)})}; |
| 164 | switch (info.type) { | 166 | switch (info.type) { |
| 165 | case TextureType::Color2D: | 167 | case TextureType::Color2D: |
| 168 | case TextureType::Color2DRect: | ||
| 166 | inst.SetArg(index, ir.CompositeConstruct(x, y)); | 169 | inst.SetArg(index, ir.CompositeConstruct(x, y)); |
| 167 | break; | 170 | break; |
| 168 | case TextureType::ColorArray2D: { | 171 | case TextureType::ColorArray2D: { |
| @@ -193,6 +196,7 @@ void ScaleIntegerOffsetComposite(IR::IREmitter& ir, IR::Inst& inst, const IR::U1 | |||
| 193 | switch (info.type) { | 196 | switch (info.type) { |
| 194 | case TextureType::ColorArray2D: | 197 | case TextureType::ColorArray2D: |
| 195 | case TextureType::Color2D: | 198 | case TextureType::Color2D: |
| 199 | case TextureType::Color2DRect: | ||
| 196 | inst.SetArg(index, ir.CompositeConstruct(x, y)); | 200 | inst.SetArg(index, ir.CompositeConstruct(x, y)); |
| 197 | break; | 201 | break; |
| 198 | case TextureType::Color1D: | 202 | case TextureType::Color1D: |
| @@ -216,6 +220,7 @@ void SubScaleCoord(IR::IREmitter& ir, IR::Inst& inst, const IR::U1& is_scaled) { | |||
| 216 | const IR::U32 scaled_y{SubScale(ir, is_scaled, coord_y, IR::Attribute::PositionY)}; | 220 | const IR::U32 scaled_y{SubScale(ir, is_scaled, coord_y, IR::Attribute::PositionY)}; |
| 217 | switch (info.type) { | 221 | switch (info.type) { |
| 218 | case TextureType::Color2D: | 222 | case TextureType::Color2D: |
| 223 | case TextureType::Color2DRect: | ||
| 219 | inst.SetArg(1, ir.CompositeConstruct(scaled_x, scaled_y)); | 224 | inst.SetArg(1, ir.CompositeConstruct(scaled_x, scaled_y)); |
| 220 | break; | 225 | break; |
| 221 | case TextureType::ColorArray2D: { | 226 | case TextureType::ColorArray2D: { |
diff --git a/src/shader_recompiler/ir_opt/texture_pass.cpp b/src/shader_recompiler/ir_opt/texture_pass.cpp index ca3e306e8..597112ba4 100644 --- a/src/shader_recompiler/ir_opt/texture_pass.cpp +++ b/src/shader_recompiler/ir_opt/texture_pass.cpp | |||
| @@ -362,6 +362,21 @@ private: | |||
| 362 | TextureDescriptors& texture_descriptors; | 362 | TextureDescriptors& texture_descriptors; |
| 363 | ImageDescriptors& image_descriptors; | 363 | ImageDescriptors& image_descriptors; |
| 364 | }; | 364 | }; |
| 365 | |||
| 366 | void PatchImageSampleImplicitLod(IR::Block& block, IR::Inst& inst) { | ||
| 367 | IR::IREmitter ir{block, IR::Block::InstructionList::s_iterator_to(inst)}; | ||
| 368 | const auto info{inst.Flags<IR::TextureInstInfo>()}; | ||
| 369 | const IR::Value coord(inst.Arg(1)); | ||
| 370 | const IR::Value handle(ir.Imm32(0)); | ||
| 371 | const IR::U32 lod{ir.Imm32(0)}; | ||
| 372 | const IR::Value texture_size = ir.ImageQueryDimension(handle, lod, info); | ||
| 373 | inst.SetArg( | ||
| 374 | 1, ir.CompositeConstruct( | ||
| 375 | ir.FPMul(IR::F32(ir.CompositeExtract(coord, 0)), | ||
| 376 | ir.FPRecip(ir.ConvertUToF(32, 32, ir.CompositeExtract(texture_size, 0)))), | ||
| 377 | ir.FPMul(IR::F32(ir.CompositeExtract(coord, 1)), | ||
| 378 | ir.FPRecip(ir.ConvertUToF(32, 32, ir.CompositeExtract(texture_size, 1)))))); | ||
| 379 | } | ||
| 365 | } // Anonymous namespace | 380 | } // Anonymous namespace |
| 366 | 381 | ||
| 367 | void TexturePass(Environment& env, IR::Program& program) { | 382 | void TexturePass(Environment& env, IR::Program& program) { |
| @@ -399,6 +414,14 @@ void TexturePass(Environment& env, IR::Program& program) { | |||
| 399 | flags.type.Assign(ReadTextureType(env, cbuf)); | 414 | flags.type.Assign(ReadTextureType(env, cbuf)); |
| 400 | inst->SetFlags(flags); | 415 | inst->SetFlags(flags); |
| 401 | break; | 416 | break; |
| 417 | case IR::Opcode::ImageSampleImplicitLod: | ||
| 418 | if (flags.type != TextureType::Color2D) { | ||
| 419 | break; | ||
| 420 | } | ||
| 421 | if (ReadTextureType(env, cbuf) == TextureType::Color2DRect) { | ||
| 422 | PatchImageSampleImplicitLod(*texture_inst.block, *texture_inst.inst); | ||
| 423 | } | ||
| 424 | break; | ||
| 402 | case IR::Opcode::ImageFetch: | 425 | case IR::Opcode::ImageFetch: |
| 403 | if (flags.type != TextureType::Color1D) { | 426 | if (flags.type != TextureType::Color1D) { |
| 404 | break; | 427 | break; |
diff --git a/src/shader_recompiler/shader_info.h b/src/shader_recompiler/shader_info.h index fd2ef5336..f5690805c 100644 --- a/src/shader_recompiler/shader_info.h +++ b/src/shader_recompiler/shader_info.h | |||
| @@ -24,8 +24,9 @@ enum class TextureType : u32 { | |||
| 24 | ColorCube, | 24 | ColorCube, |
| 25 | ColorArrayCube, | 25 | ColorArrayCube, |
| 26 | Buffer, | 26 | Buffer, |
| 27 | Color2DRect, | ||
| 27 | }; | 28 | }; |
| 28 | constexpr u32 NUM_TEXTURE_TYPES = 8; | 29 | constexpr u32 NUM_TEXTURE_TYPES = 9; |
| 29 | 30 | ||
| 30 | enum class ImageFormat : u32 { | 31 | enum class ImageFormat : u32 { |
| 31 | Typeless, | 32 | Typeless, |
diff --git a/src/tests/video_core/buffer_base.cpp b/src/tests/video_core/buffer_base.cpp index a1be8dcf1..71121e42a 100644 --- a/src/tests/video_core/buffer_base.cpp +++ b/src/tests/video_core/buffer_base.cpp | |||
| @@ -22,8 +22,9 @@ constexpr VAddr c = 0x1328914000; | |||
| 22 | class RasterizerInterface { | 22 | class RasterizerInterface { |
| 23 | public: | 23 | public: |
| 24 | void UpdatePagesCachedCount(VAddr addr, u64 size, int delta) { | 24 | void UpdatePagesCachedCount(VAddr addr, u64 size, int delta) { |
| 25 | const u64 page_start{addr >> Core::Memory::PAGE_BITS}; | 25 | const u64 page_start{addr >> Core::Memory::YUZU_PAGEBITS}; |
| 26 | const u64 page_end{(addr + size + Core::Memory::PAGE_SIZE - 1) >> Core::Memory::PAGE_BITS}; | 26 | const u64 page_end{(addr + size + Core::Memory::YUZU_PAGESIZE - 1) >> |
| 27 | Core::Memory::YUZU_PAGEBITS}; | ||
| 27 | for (u64 page = page_start; page < page_end; ++page) { | 28 | for (u64 page = page_start; page < page_end; ++page) { |
| 28 | int& value = page_table[page]; | 29 | int& value = page_table[page]; |
| 29 | value += delta; | 30 | value += delta; |
| @@ -37,7 +38,7 @@ public: | |||
| 37 | } | 38 | } |
| 38 | 39 | ||
| 39 | [[nodiscard]] int Count(VAddr addr) const noexcept { | 40 | [[nodiscard]] int Count(VAddr addr) const noexcept { |
| 40 | const auto it = page_table.find(addr >> Core::Memory::PAGE_BITS); | 41 | const auto it = page_table.find(addr >> Core::Memory::YUZU_PAGEBITS); |
| 41 | return it == page_table.end() ? 0 : it->second; | 42 | return it == page_table.end() ? 0 : it->second; |
| 42 | } | 43 | } |
| 43 | 44 | ||
diff --git a/src/video_core/buffer_cache/buffer_base.h b/src/video_core/buffer_cache/buffer_base.h index 3e20608ca..f9a6472cf 100644 --- a/src/video_core/buffer_cache/buffer_base.h +++ b/src/video_core/buffer_cache/buffer_base.h | |||
| @@ -12,6 +12,7 @@ | |||
| 12 | #include "common/common_funcs.h" | 12 | #include "common/common_funcs.h" |
| 13 | #include "common/common_types.h" | 13 | #include "common/common_types.h" |
| 14 | #include "common/div_ceil.h" | 14 | #include "common/div_ceil.h" |
| 15 | #include "common/settings.h" | ||
| 15 | #include "core/memory.h" | 16 | #include "core/memory.h" |
| 16 | 17 | ||
| 17 | namespace VideoCommon { | 18 | namespace VideoCommon { |
| @@ -36,7 +37,7 @@ struct NullBufferParams {}; | |||
| 36 | template <class RasterizerInterface> | 37 | template <class RasterizerInterface> |
| 37 | class BufferBase { | 38 | class BufferBase { |
| 38 | static constexpr u64 PAGES_PER_WORD = 64; | 39 | static constexpr u64 PAGES_PER_WORD = 64; |
| 39 | static constexpr u64 BYTES_PER_PAGE = Core::Memory::PAGE_SIZE; | 40 | static constexpr u64 BYTES_PER_PAGE = Core::Memory::YUZU_PAGESIZE; |
| 40 | static constexpr u64 BYTES_PER_WORD = PAGES_PER_WORD * BYTES_PER_PAGE; | 41 | static constexpr u64 BYTES_PER_WORD = PAGES_PER_WORD * BYTES_PER_PAGE; |
| 41 | 42 | ||
| 42 | /// Vector tracking modified pages tightly packed with small vector optimization | 43 | /// Vector tracking modified pages tightly packed with small vector optimization |
| @@ -219,7 +220,9 @@ public: | |||
| 219 | NotifyRasterizer<false>(word_index, untracked_words[word_index], cached_bits); | 220 | NotifyRasterizer<false>(word_index, untracked_words[word_index], cached_bits); |
| 220 | untracked_words[word_index] |= cached_bits; | 221 | untracked_words[word_index] |= cached_bits; |
| 221 | cpu_words[word_index] |= cached_bits; | 222 | cpu_words[word_index] |= cached_bits; |
| 222 | cached_words[word_index] = 0; | 223 | if (!Settings::values.use_pessimistic_flushes) { |
| 224 | cached_words[word_index] = 0; | ||
| 225 | } | ||
| 223 | } | 226 | } |
| 224 | } | 227 | } |
| 225 | 228 | ||
diff --git a/src/video_core/buffer_cache/buffer_cache.h b/src/video_core/buffer_cache/buffer_cache.h index b74ad7900..f015dae56 100644 --- a/src/video_core/buffer_cache/buffer_cache.h +++ b/src/video_core/buffer_cache/buffer_cache.h | |||
| @@ -60,8 +60,8 @@ class BufferCache { | |||
| 60 | 60 | ||
| 61 | // Page size for caching purposes. | 61 | // Page size for caching purposes. |
| 62 | // This is unrelated to the CPU page size and it can be changed as it seems optimal. | 62 | // This is unrelated to the CPU page size and it can be changed as it seems optimal. |
| 63 | static constexpr u32 PAGE_BITS = 16; | 63 | static constexpr u32 YUZU_PAGEBITS = 16; |
| 64 | static constexpr u64 PAGE_SIZE = u64{1} << PAGE_BITS; | 64 | static constexpr u64 YUZU_PAGESIZE = u64{1} << YUZU_PAGEBITS; |
| 65 | 65 | ||
| 66 | static constexpr bool IS_OPENGL = P::IS_OPENGL; | 66 | static constexpr bool IS_OPENGL = P::IS_OPENGL; |
| 67 | static constexpr bool HAS_PERSISTENT_UNIFORM_BUFFER_BINDINGS = | 67 | static constexpr bool HAS_PERSISTENT_UNIFORM_BUFFER_BINDINGS = |
| @@ -216,8 +216,8 @@ private: | |||
| 216 | 216 | ||
| 217 | template <typename Func> | 217 | template <typename Func> |
| 218 | void ForEachBufferInRange(VAddr cpu_addr, u64 size, Func&& func) { | 218 | void ForEachBufferInRange(VAddr cpu_addr, u64 size, Func&& func) { |
| 219 | const u64 page_end = Common::DivCeil(cpu_addr + size, PAGE_SIZE); | 219 | const u64 page_end = Common::DivCeil(cpu_addr + size, YUZU_PAGESIZE); |
| 220 | for (u64 page = cpu_addr >> PAGE_BITS; page < page_end;) { | 220 | for (u64 page = cpu_addr >> YUZU_PAGEBITS; page < page_end;) { |
| 221 | const BufferId buffer_id = page_table[page]; | 221 | const BufferId buffer_id = page_table[page]; |
| 222 | if (!buffer_id) { | 222 | if (!buffer_id) { |
| 223 | ++page; | 223 | ++page; |
| @@ -227,7 +227,7 @@ private: | |||
| 227 | func(buffer_id, buffer); | 227 | func(buffer_id, buffer); |
| 228 | 228 | ||
| 229 | const VAddr end_addr = buffer.CpuAddr() + buffer.SizeBytes(); | 229 | const VAddr end_addr = buffer.CpuAddr() + buffer.SizeBytes(); |
| 230 | page = Common::DivCeil(end_addr, PAGE_SIZE); | 230 | page = Common::DivCeil(end_addr, YUZU_PAGESIZE); |
| 231 | } | 231 | } |
| 232 | } | 232 | } |
| 233 | 233 | ||
| @@ -262,8 +262,8 @@ private: | |||
| 262 | } | 262 | } |
| 263 | 263 | ||
| 264 | static bool IsRangeGranular(VAddr cpu_addr, size_t size) { | 264 | static bool IsRangeGranular(VAddr cpu_addr, size_t size) { |
| 265 | return (cpu_addr & ~Core::Memory::PAGE_MASK) == | 265 | return (cpu_addr & ~Core::Memory::YUZU_PAGEMASK) == |
| 266 | ((cpu_addr + size) & ~Core::Memory::PAGE_MASK); | 266 | ((cpu_addr + size) & ~Core::Memory::YUZU_PAGEMASK); |
| 267 | } | 267 | } |
| 268 | 268 | ||
| 269 | void RunGarbageCollector(); | 269 | void RunGarbageCollector(); |
| @@ -439,7 +439,7 @@ private: | |||
| 439 | u64 minimum_memory = 0; | 439 | u64 minimum_memory = 0; |
| 440 | u64 critical_memory = 0; | 440 | u64 critical_memory = 0; |
| 441 | 441 | ||
| 442 | std::array<BufferId, ((1ULL << 39) >> PAGE_BITS)> page_table; | 442 | std::array<BufferId, ((1ULL << 39) >> YUZU_PAGEBITS)> page_table; |
| 443 | }; | 443 | }; |
| 444 | 444 | ||
| 445 | template <class P> | 445 | template <class P> |
| @@ -926,8 +926,8 @@ void BufferCache<P>::PopAsyncFlushes() {} | |||
| 926 | 926 | ||
| 927 | template <class P> | 927 | template <class P> |
| 928 | bool BufferCache<P>::IsRegionGpuModified(VAddr addr, size_t size) { | 928 | bool BufferCache<P>::IsRegionGpuModified(VAddr addr, size_t size) { |
| 929 | const u64 page_end = Common::DivCeil(addr + size, PAGE_SIZE); | 929 | const u64 page_end = Common::DivCeil(addr + size, YUZU_PAGESIZE); |
| 930 | for (u64 page = addr >> PAGE_BITS; page < page_end;) { | 930 | for (u64 page = addr >> YUZU_PAGEBITS; page < page_end;) { |
| 931 | const BufferId image_id = page_table[page]; | 931 | const BufferId image_id = page_table[page]; |
| 932 | if (!image_id) { | 932 | if (!image_id) { |
| 933 | ++page; | 933 | ++page; |
| @@ -938,7 +938,7 @@ bool BufferCache<P>::IsRegionGpuModified(VAddr addr, size_t size) { | |||
| 938 | return true; | 938 | return true; |
| 939 | } | 939 | } |
| 940 | const VAddr end_addr = buffer.CpuAddr() + buffer.SizeBytes(); | 940 | const VAddr end_addr = buffer.CpuAddr() + buffer.SizeBytes(); |
| 941 | page = Common::DivCeil(end_addr, PAGE_SIZE); | 941 | page = Common::DivCeil(end_addr, YUZU_PAGESIZE); |
| 942 | } | 942 | } |
| 943 | return false; | 943 | return false; |
| 944 | } | 944 | } |
| @@ -946,8 +946,8 @@ bool BufferCache<P>::IsRegionGpuModified(VAddr addr, size_t size) { | |||
| 946 | template <class P> | 946 | template <class P> |
| 947 | bool BufferCache<P>::IsRegionRegistered(VAddr addr, size_t size) { | 947 | bool BufferCache<P>::IsRegionRegistered(VAddr addr, size_t size) { |
| 948 | const VAddr end_addr = addr + size; | 948 | const VAddr end_addr = addr + size; |
| 949 | const u64 page_end = Common::DivCeil(end_addr, PAGE_SIZE); | 949 | const u64 page_end = Common::DivCeil(end_addr, YUZU_PAGESIZE); |
| 950 | for (u64 page = addr >> PAGE_BITS; page < page_end;) { | 950 | for (u64 page = addr >> YUZU_PAGEBITS; page < page_end;) { |
| 951 | const BufferId buffer_id = page_table[page]; | 951 | const BufferId buffer_id = page_table[page]; |
| 952 | if (!buffer_id) { | 952 | if (!buffer_id) { |
| 953 | ++page; | 953 | ++page; |
| @@ -959,15 +959,15 @@ bool BufferCache<P>::IsRegionRegistered(VAddr addr, size_t size) { | |||
| 959 | if (buf_start_addr < end_addr && addr < buf_end_addr) { | 959 | if (buf_start_addr < end_addr && addr < buf_end_addr) { |
| 960 | return true; | 960 | return true; |
| 961 | } | 961 | } |
| 962 | page = Common::DivCeil(end_addr, PAGE_SIZE); | 962 | page = Common::DivCeil(end_addr, YUZU_PAGESIZE); |
| 963 | } | 963 | } |
| 964 | return false; | 964 | return false; |
| 965 | } | 965 | } |
| 966 | 966 | ||
| 967 | template <class P> | 967 | template <class P> |
| 968 | bool BufferCache<P>::IsRegionCpuModified(VAddr addr, size_t size) { | 968 | bool BufferCache<P>::IsRegionCpuModified(VAddr addr, size_t size) { |
| 969 | const u64 page_end = Common::DivCeil(addr + size, PAGE_SIZE); | 969 | const u64 page_end = Common::DivCeil(addr + size, YUZU_PAGESIZE); |
| 970 | for (u64 page = addr >> PAGE_BITS; page < page_end;) { | 970 | for (u64 page = addr >> YUZU_PAGEBITS; page < page_end;) { |
| 971 | const BufferId image_id = page_table[page]; | 971 | const BufferId image_id = page_table[page]; |
| 972 | if (!image_id) { | 972 | if (!image_id) { |
| 973 | ++page; | 973 | ++page; |
| @@ -978,7 +978,7 @@ bool BufferCache<P>::IsRegionCpuModified(VAddr addr, size_t size) { | |||
| 978 | return true; | 978 | return true; |
| 979 | } | 979 | } |
| 980 | const VAddr end_addr = buffer.CpuAddr() + buffer.SizeBytes(); | 980 | const VAddr end_addr = buffer.CpuAddr() + buffer.SizeBytes(); |
| 981 | page = Common::DivCeil(end_addr, PAGE_SIZE); | 981 | page = Common::DivCeil(end_addr, YUZU_PAGESIZE); |
| 982 | } | 982 | } |
| 983 | return false; | 983 | return false; |
| 984 | } | 984 | } |
| @@ -1472,7 +1472,7 @@ BufferId BufferCache<P>::FindBuffer(VAddr cpu_addr, u32 size) { | |||
| 1472 | if (cpu_addr == 0) { | 1472 | if (cpu_addr == 0) { |
| 1473 | return NULL_BUFFER_ID; | 1473 | return NULL_BUFFER_ID; |
| 1474 | } | 1474 | } |
| 1475 | const u64 page = cpu_addr >> PAGE_BITS; | 1475 | const u64 page = cpu_addr >> YUZU_PAGEBITS; |
| 1476 | const BufferId buffer_id = page_table[page]; | 1476 | const BufferId buffer_id = page_table[page]; |
| 1477 | if (!buffer_id) { | 1477 | if (!buffer_id) { |
| 1478 | return CreateBuffer(cpu_addr, size); | 1478 | return CreateBuffer(cpu_addr, size); |
| @@ -1493,8 +1493,9 @@ typename BufferCache<P>::OverlapResult BufferCache<P>::ResolveOverlaps(VAddr cpu | |||
| 1493 | VAddr end = cpu_addr + wanted_size; | 1493 | VAddr end = cpu_addr + wanted_size; |
| 1494 | int stream_score = 0; | 1494 | int stream_score = 0; |
| 1495 | bool has_stream_leap = false; | 1495 | bool has_stream_leap = false; |
| 1496 | for (; cpu_addr >> PAGE_BITS < Common::DivCeil(end, PAGE_SIZE); cpu_addr += PAGE_SIZE) { | 1496 | for (; cpu_addr >> YUZU_PAGEBITS < Common::DivCeil(end, YUZU_PAGESIZE); |
| 1497 | const BufferId overlap_id = page_table[cpu_addr >> PAGE_BITS]; | 1497 | cpu_addr += YUZU_PAGESIZE) { |
| 1498 | const BufferId overlap_id = page_table[cpu_addr >> YUZU_PAGEBITS]; | ||
| 1498 | if (!overlap_id) { | 1499 | if (!overlap_id) { |
| 1499 | continue; | 1500 | continue; |
| 1500 | } | 1501 | } |
| @@ -1520,11 +1521,11 @@ typename BufferCache<P>::OverlapResult BufferCache<P>::ResolveOverlaps(VAddr cpu | |||
| 1520 | // as a stream buffer. Increase the size to skip constantly recreating buffers. | 1521 | // as a stream buffer. Increase the size to skip constantly recreating buffers. |
| 1521 | has_stream_leap = true; | 1522 | has_stream_leap = true; |
| 1522 | if (expands_right) { | 1523 | if (expands_right) { |
| 1523 | begin -= PAGE_SIZE * 256; | 1524 | begin -= YUZU_PAGESIZE * 256; |
| 1524 | cpu_addr = begin; | 1525 | cpu_addr = begin; |
| 1525 | } | 1526 | } |
| 1526 | if (expands_left) { | 1527 | if (expands_left) { |
| 1527 | end += PAGE_SIZE * 256; | 1528 | end += YUZU_PAGESIZE * 256; |
| 1528 | } | 1529 | } |
| 1529 | } | 1530 | } |
| 1530 | } | 1531 | } |
| @@ -1598,8 +1599,8 @@ void BufferCache<P>::ChangeRegister(BufferId buffer_id) { | |||
| 1598 | } | 1599 | } |
| 1599 | const VAddr cpu_addr_begin = buffer.CpuAddr(); | 1600 | const VAddr cpu_addr_begin = buffer.CpuAddr(); |
| 1600 | const VAddr cpu_addr_end = cpu_addr_begin + size; | 1601 | const VAddr cpu_addr_end = cpu_addr_begin + size; |
| 1601 | const u64 page_begin = cpu_addr_begin / PAGE_SIZE; | 1602 | const u64 page_begin = cpu_addr_begin / YUZU_PAGESIZE; |
| 1602 | const u64 page_end = Common::DivCeil(cpu_addr_end, PAGE_SIZE); | 1603 | const u64 page_end = Common::DivCeil(cpu_addr_end, YUZU_PAGESIZE); |
| 1603 | for (u64 page = page_begin; page != page_end; ++page) { | 1604 | for (u64 page = page_begin; page != page_end; ++page) { |
| 1604 | if constexpr (insert) { | 1605 | if constexpr (insert) { |
| 1605 | page_table[page] = buffer_id; | 1606 | page_table[page] = buffer_id; |
diff --git a/src/video_core/memory_manager.cpp b/src/video_core/memory_manager.cpp index d373be0ba..bf9eb735d 100644 --- a/src/video_core/memory_manager.cpp +++ b/src/video_core/memory_manager.cpp | |||
| @@ -369,8 +369,8 @@ bool MemoryManager::IsGranularRange(GPUVAddr gpu_addr, std::size_t size) const { | |||
| 369 | if (!cpu_addr) { | 369 | if (!cpu_addr) { |
| 370 | return false; | 370 | return false; |
| 371 | } | 371 | } |
| 372 | const std::size_t page{(*cpu_addr & Core::Memory::PAGE_MASK) + size}; | 372 | const std::size_t page{(*cpu_addr & Core::Memory::YUZU_PAGEMASK) + size}; |
| 373 | return page <= Core::Memory::PAGE_SIZE; | 373 | return page <= Core::Memory::YUZU_PAGESIZE; |
| 374 | } | 374 | } |
| 375 | 375 | ||
| 376 | bool MemoryManager::IsContinousRange(GPUVAddr gpu_addr, std::size_t size) const { | 376 | bool MemoryManager::IsContinousRange(GPUVAddr gpu_addr, std::size_t size) const { |
diff --git a/src/video_core/query_cache.h b/src/video_core/query_cache.h index fcce87acb..889b606b3 100644 --- a/src/video_core/query_cache.h +++ b/src/video_core/query_cache.h | |||
| @@ -214,8 +214,8 @@ private: | |||
| 214 | return cache_begin < addr_end && addr_begin < cache_end; | 214 | return cache_begin < addr_end && addr_begin < cache_end; |
| 215 | }; | 215 | }; |
| 216 | 216 | ||
| 217 | const u64 page_end = addr_end >> PAGE_BITS; | 217 | const u64 page_end = addr_end >> YUZU_PAGEBITS; |
| 218 | for (u64 page = addr_begin >> PAGE_BITS; page <= page_end; ++page) { | 218 | for (u64 page = addr_begin >> YUZU_PAGEBITS; page <= page_end; ++page) { |
| 219 | const auto& it = cached_queries.find(page); | 219 | const auto& it = cached_queries.find(page); |
| 220 | if (it == std::end(cached_queries)) { | 220 | if (it == std::end(cached_queries)) { |
| 221 | continue; | 221 | continue; |
| @@ -235,14 +235,14 @@ private: | |||
| 235 | /// Registers the passed parameters as cached and returns a pointer to the stored cached query. | 235 | /// Registers the passed parameters as cached and returns a pointer to the stored cached query. |
| 236 | CachedQuery* Register(VideoCore::QueryType type, VAddr cpu_addr, u8* host_ptr, bool timestamp) { | 236 | CachedQuery* Register(VideoCore::QueryType type, VAddr cpu_addr, u8* host_ptr, bool timestamp) { |
| 237 | rasterizer.UpdatePagesCachedCount(cpu_addr, CachedQuery::SizeInBytes(timestamp), 1); | 237 | rasterizer.UpdatePagesCachedCount(cpu_addr, CachedQuery::SizeInBytes(timestamp), 1); |
| 238 | const u64 page = static_cast<u64>(cpu_addr) >> PAGE_BITS; | 238 | const u64 page = static_cast<u64>(cpu_addr) >> YUZU_PAGEBITS; |
| 239 | return &cached_queries[page].emplace_back(static_cast<QueryCache&>(*this), type, cpu_addr, | 239 | return &cached_queries[page].emplace_back(static_cast<QueryCache&>(*this), type, cpu_addr, |
| 240 | host_ptr); | 240 | host_ptr); |
| 241 | } | 241 | } |
| 242 | 242 | ||
| 243 | /// Tries to a get a cached query. Returns nullptr on failure. | 243 | /// Tries to a get a cached query. Returns nullptr on failure. |
| 244 | CachedQuery* TryGet(VAddr addr) { | 244 | CachedQuery* TryGet(VAddr addr) { |
| 245 | const u64 page = static_cast<u64>(addr) >> PAGE_BITS; | 245 | const u64 page = static_cast<u64>(addr) >> YUZU_PAGEBITS; |
| 246 | const auto it = cached_queries.find(page); | 246 | const auto it = cached_queries.find(page); |
| 247 | if (it == std::end(cached_queries)) { | 247 | if (it == std::end(cached_queries)) { |
| 248 | return nullptr; | 248 | return nullptr; |
| @@ -260,8 +260,8 @@ private: | |||
| 260 | uncommitted_flushes->push_back(addr); | 260 | uncommitted_flushes->push_back(addr); |
| 261 | } | 261 | } |
| 262 | 262 | ||
| 263 | static constexpr std::uintptr_t PAGE_SIZE = 4096; | 263 | static constexpr std::uintptr_t YUZU_PAGESIZE = 4096; |
| 264 | static constexpr unsigned PAGE_BITS = 12; | 264 | static constexpr unsigned YUZU_PAGEBITS = 12; |
| 265 | 265 | ||
| 266 | VideoCore::RasterizerInterface& rasterizer; | 266 | VideoCore::RasterizerInterface& rasterizer; |
| 267 | Tegra::Engines::Maxwell3D& maxwell3d; | 267 | Tegra::Engines::Maxwell3D& maxwell3d; |
diff --git a/src/video_core/rasterizer_accelerated.cpp b/src/video_core/rasterizer_accelerated.cpp index 87a29e144..4a197d65d 100644 --- a/src/video_core/rasterizer_accelerated.cpp +++ b/src/video_core/rasterizer_accelerated.cpp | |||
| @@ -24,8 +24,8 @@ void RasterizerAccelerated::UpdatePagesCachedCount(VAddr addr, u64 size, int del | |||
| 24 | u64 cache_bytes = 0; | 24 | u64 cache_bytes = 0; |
| 25 | 25 | ||
| 26 | std::atomic_thread_fence(std::memory_order_acquire); | 26 | std::atomic_thread_fence(std::memory_order_acquire); |
| 27 | const u64 page_end = Common::DivCeil(addr + size, PAGE_SIZE); | 27 | const u64 page_end = Common::DivCeil(addr + size, YUZU_PAGESIZE); |
| 28 | for (u64 page = addr >> PAGE_BITS; page != page_end; ++page) { | 28 | for (u64 page = addr >> YUZU_PAGEBITS; page != page_end; ++page) { |
| 29 | std::atomic_uint16_t& count = cached_pages.at(page >> 2).Count(page); | 29 | std::atomic_uint16_t& count = cached_pages.at(page >> 2).Count(page); |
| 30 | 30 | ||
| 31 | if (delta > 0) { | 31 | if (delta > 0) { |
| @@ -44,26 +44,27 @@ void RasterizerAccelerated::UpdatePagesCachedCount(VAddr addr, u64 size, int del | |||
| 44 | if (uncache_bytes == 0) { | 44 | if (uncache_bytes == 0) { |
| 45 | uncache_begin = page; | 45 | uncache_begin = page; |
| 46 | } | 46 | } |
| 47 | uncache_bytes += PAGE_SIZE; | 47 | uncache_bytes += YUZU_PAGESIZE; |
| 48 | } else if (uncache_bytes > 0) { | 48 | } else if (uncache_bytes > 0) { |
| 49 | cpu_memory.RasterizerMarkRegionCached(uncache_begin << PAGE_BITS, uncache_bytes, false); | 49 | cpu_memory.RasterizerMarkRegionCached(uncache_begin << YUZU_PAGEBITS, uncache_bytes, |
| 50 | false); | ||
| 50 | uncache_bytes = 0; | 51 | uncache_bytes = 0; |
| 51 | } | 52 | } |
| 52 | if (count.load(std::memory_order::relaxed) == 1 && delta > 0) { | 53 | if (count.load(std::memory_order::relaxed) == 1 && delta > 0) { |
| 53 | if (cache_bytes == 0) { | 54 | if (cache_bytes == 0) { |
| 54 | cache_begin = page; | 55 | cache_begin = page; |
| 55 | } | 56 | } |
| 56 | cache_bytes += PAGE_SIZE; | 57 | cache_bytes += YUZU_PAGESIZE; |
| 57 | } else if (cache_bytes > 0) { | 58 | } else if (cache_bytes > 0) { |
| 58 | cpu_memory.RasterizerMarkRegionCached(cache_begin << PAGE_BITS, cache_bytes, true); | 59 | cpu_memory.RasterizerMarkRegionCached(cache_begin << YUZU_PAGEBITS, cache_bytes, true); |
| 59 | cache_bytes = 0; | 60 | cache_bytes = 0; |
| 60 | } | 61 | } |
| 61 | } | 62 | } |
| 62 | if (uncache_bytes > 0) { | 63 | if (uncache_bytes > 0) { |
| 63 | cpu_memory.RasterizerMarkRegionCached(uncache_begin << PAGE_BITS, uncache_bytes, false); | 64 | cpu_memory.RasterizerMarkRegionCached(uncache_begin << YUZU_PAGEBITS, uncache_bytes, false); |
| 64 | } | 65 | } |
| 65 | if (cache_bytes > 0) { | 66 | if (cache_bytes > 0) { |
| 66 | cpu_memory.RasterizerMarkRegionCached(cache_begin << PAGE_BITS, cache_bytes, true); | 67 | cpu_memory.RasterizerMarkRegionCached(cache_begin << YUZU_PAGEBITS, cache_bytes, true); |
| 67 | } | 68 | } |
| 68 | } | 69 | } |
| 69 | 70 | ||
diff --git a/src/video_core/renderer_opengl/gl_shader_cache.cpp b/src/video_core/renderer_opengl/gl_shader_cache.cpp index 07d4b7cf0..ddb70934c 100644 --- a/src/video_core/renderer_opengl/gl_shader_cache.cpp +++ b/src/video_core/renderer_opengl/gl_shader_cache.cpp | |||
| @@ -49,7 +49,7 @@ using VideoCommon::LoadPipelines; | |||
| 49 | using VideoCommon::SerializePipeline; | 49 | using VideoCommon::SerializePipeline; |
| 50 | using Context = ShaderContext::Context; | 50 | using Context = ShaderContext::Context; |
| 51 | 51 | ||
| 52 | constexpr u32 CACHE_VERSION = 5; | 52 | constexpr u32 CACHE_VERSION = 6; |
| 53 | 53 | ||
| 54 | template <typename Container> | 54 | template <typename Container> |
| 55 | auto MakeSpan(Container& container) { | 55 | auto MakeSpan(Container& container) { |
| @@ -299,7 +299,7 @@ void ShaderCache::LoadDiskResources(u64 title_id, std::stop_token stop_loading, | |||
| 299 | state.has_loaded = true; | 299 | state.has_loaded = true; |
| 300 | lock.unlock(); | 300 | lock.unlock(); |
| 301 | 301 | ||
| 302 | workers->WaitForRequests(); | 302 | workers->WaitForRequests(stop_loading); |
| 303 | if (!use_asynchronous_shaders) { | 303 | if (!use_asynchronous_shaders) { |
| 304 | workers.reset(); | 304 | workers.reset(); |
| 305 | } | 305 | } |
diff --git a/src/video_core/renderer_opengl/gl_shader_util.cpp b/src/video_core/renderer_opengl/gl_shader_util.cpp index f83ad0a5b..a0d9d10ef 100644 --- a/src/video_core/renderer_opengl/gl_shader_util.cpp +++ b/src/video_core/renderer_opengl/gl_shader_util.cpp | |||
| @@ -17,6 +17,7 @@ static OGLProgram LinkSeparableProgram(GLuint shader) { | |||
| 17 | glProgramParameteri(program.handle, GL_PROGRAM_SEPARABLE, GL_TRUE); | 17 | glProgramParameteri(program.handle, GL_PROGRAM_SEPARABLE, GL_TRUE); |
| 18 | glAttachShader(program.handle, shader); | 18 | glAttachShader(program.handle, shader); |
| 19 | glLinkProgram(program.handle); | 19 | glLinkProgram(program.handle); |
| 20 | glDetachShader(program.handle, shader); | ||
| 20 | if (!Settings::values.renderer_debug) { | 21 | if (!Settings::values.renderer_debug) { |
| 21 | return program; | 22 | return program; |
| 22 | } | 23 | } |
diff --git a/src/video_core/renderer_opengl/gl_texture_cache.cpp b/src/video_core/renderer_opengl/gl_texture_cache.cpp index 8c0fffc67..99cd11d1e 100644 --- a/src/video_core/renderer_opengl/gl_texture_cache.cpp +++ b/src/video_core/renderer_opengl/gl_texture_cache.cpp | |||
| @@ -93,6 +93,7 @@ GLenum ImageTarget(Shader::TextureType type, int num_samples = 1) { | |||
| 93 | case Shader::TextureType::Color1D: | 93 | case Shader::TextureType::Color1D: |
| 94 | return GL_TEXTURE_1D; | 94 | return GL_TEXTURE_1D; |
| 95 | case Shader::TextureType::Color2D: | 95 | case Shader::TextureType::Color2D: |
| 96 | case Shader::TextureType::Color2DRect: | ||
| 96 | return is_multisampled ? GL_TEXTURE_2D_MULTISAMPLE : GL_TEXTURE_2D; | 97 | return is_multisampled ? GL_TEXTURE_2D_MULTISAMPLE : GL_TEXTURE_2D; |
| 97 | case Shader::TextureType::ColorCube: | 98 | case Shader::TextureType::ColorCube: |
| 98 | return GL_TEXTURE_CUBE_MAP; | 99 | return GL_TEXTURE_CUBE_MAP; |
| @@ -502,6 +503,7 @@ TextureCacheRuntime::TextureCacheRuntime(const Device& device_, ProgramManager& | |||
| 502 | set_view(Shader::TextureType::ColorArray1D, null_image_1d_array.handle); | 503 | set_view(Shader::TextureType::ColorArray1D, null_image_1d_array.handle); |
| 503 | set_view(Shader::TextureType::ColorArray2D, null_image_view_2d_array.handle); | 504 | set_view(Shader::TextureType::ColorArray2D, null_image_view_2d_array.handle); |
| 504 | set_view(Shader::TextureType::ColorArrayCube, null_image_cube_array.handle); | 505 | set_view(Shader::TextureType::ColorArrayCube, null_image_cube_array.handle); |
| 506 | set_view(Shader::TextureType::Color2DRect, null_image_view_2d.handle); | ||
| 505 | 507 | ||
| 506 | if (resolution.active) { | 508 | if (resolution.active) { |
| 507 | for (size_t i = 0; i < rescale_draw_fbos.size(); ++i) { | 509 | for (size_t i = 0; i < rescale_draw_fbos.size(); ++i) { |
| @@ -1110,6 +1112,7 @@ ImageView::ImageView(TextureCacheRuntime& runtime, const VideoCommon::ImageViewI | |||
| 1110 | flat_range.extent.layers = 1; | 1112 | flat_range.extent.layers = 1; |
| 1111 | [[fallthrough]]; | 1113 | [[fallthrough]]; |
| 1112 | case ImageViewType::e2D: | 1114 | case ImageViewType::e2D: |
| 1115 | case ImageViewType::Rect: | ||
| 1113 | if (True(flags & VideoCommon::ImageViewFlagBits::Slice)) { | 1116 | if (True(flags & VideoCommon::ImageViewFlagBits::Slice)) { |
| 1114 | // 2D and 2D array views on a 3D textures are used exclusively for render targets | 1117 | // 2D and 2D array views on a 3D textures are used exclusively for render targets |
| 1115 | ASSERT(info.range.extent.levels == 1); | 1118 | ASSERT(info.range.extent.levels == 1); |
| @@ -1135,9 +1138,6 @@ ImageView::ImageView(TextureCacheRuntime& runtime, const VideoCommon::ImageViewI | |||
| 1135 | SetupView(Shader::TextureType::ColorCube); | 1138 | SetupView(Shader::TextureType::ColorCube); |
| 1136 | SetupView(Shader::TextureType::ColorArrayCube); | 1139 | SetupView(Shader::TextureType::ColorArrayCube); |
| 1137 | break; | 1140 | break; |
| 1138 | case ImageViewType::Rect: | ||
| 1139 | UNIMPLEMENTED(); | ||
| 1140 | break; | ||
| 1141 | case ImageViewType::Buffer: | 1141 | case ImageViewType::Buffer: |
| 1142 | ASSERT(false); | 1142 | ASSERT(false); |
| 1143 | break; | 1143 | break; |
| @@ -1150,6 +1150,7 @@ ImageView::ImageView(TextureCacheRuntime& runtime, const VideoCommon::ImageViewI | |||
| 1150 | default_handle = Handle(Shader::TextureType::ColorArray1D); | 1150 | default_handle = Handle(Shader::TextureType::ColorArray1D); |
| 1151 | break; | 1151 | break; |
| 1152 | case ImageViewType::e2D: | 1152 | case ImageViewType::e2D: |
| 1153 | case ImageViewType::Rect: | ||
| 1153 | default_handle = Handle(Shader::TextureType::Color2D); | 1154 | default_handle = Handle(Shader::TextureType::Color2D); |
| 1154 | break; | 1155 | break; |
| 1155 | case ImageViewType::e2DArray: | 1156 | case ImageViewType::e2DArray: |
| @@ -1210,6 +1211,7 @@ GLuint ImageView::MakeView(Shader::TextureType view_type, GLenum view_format) { | |||
| 1210 | case Shader::TextureType::Color1D: | 1211 | case Shader::TextureType::Color1D: |
| 1211 | case Shader::TextureType::Color2D: | 1212 | case Shader::TextureType::Color2D: |
| 1212 | case Shader::TextureType::ColorCube: | 1213 | case Shader::TextureType::ColorCube: |
| 1214 | case Shader::TextureType::Color2DRect: | ||
| 1213 | view_range = flat_range; | 1215 | view_range = flat_range; |
| 1214 | break; | 1216 | break; |
| 1215 | case Shader::TextureType::ColorArray1D: | 1217 | case Shader::TextureType::ColorArray1D: |
| @@ -1250,7 +1252,6 @@ Sampler::Sampler(TextureCacheRuntime& runtime, const TSCEntry& config) { | |||
| 1250 | const GLint seamless = config.cubemap_interface_filtering ? GL_TRUE : GL_FALSE; | 1252 | const GLint seamless = config.cubemap_interface_filtering ? GL_TRUE : GL_FALSE; |
| 1251 | 1253 | ||
| 1252 | UNIMPLEMENTED_IF(config.cubemap_anisotropy != 1); | 1254 | UNIMPLEMENTED_IF(config.cubemap_anisotropy != 1); |
| 1253 | UNIMPLEMENTED_IF(config.float_coord_normalization != 0); | ||
| 1254 | 1255 | ||
| 1255 | sampler.Create(); | 1256 | sampler.Create(); |
| 1256 | const GLuint handle = sampler.handle; | 1257 | const GLuint handle = sampler.handle; |
diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp index 01028cee0..34f3f7a67 100644 --- a/src/video_core/renderer_opengl/renderer_opengl.cpp +++ b/src/video_core/renderer_opengl/renderer_opengl.cpp | |||
| @@ -478,13 +478,16 @@ void RendererOpenGL::DrawScreen(const Layout::FramebufferLayout& layout) { | |||
| 478 | } | 478 | } |
| 479 | } | 479 | } |
| 480 | 480 | ||
| 481 | ASSERT_MSG(framebuffer_crop_rect.top == 0, "Unimplemented"); | ||
| 482 | ASSERT_MSG(framebuffer_crop_rect.left == 0, "Unimplemented"); | 481 | ASSERT_MSG(framebuffer_crop_rect.left == 0, "Unimplemented"); |
| 483 | 482 | ||
| 483 | f32 left_start{}; | ||
| 484 | if (framebuffer_crop_rect.Top() > 0) { | ||
| 485 | left_start = static_cast<f32>(framebuffer_crop_rect.Top()) / | ||
| 486 | static_cast<f32>(framebuffer_crop_rect.Bottom()); | ||
| 487 | } | ||
| 484 | f32 scale_u = static_cast<f32>(framebuffer_width) / static_cast<f32>(screen_info.texture.width); | 488 | f32 scale_u = static_cast<f32>(framebuffer_width) / static_cast<f32>(screen_info.texture.width); |
| 485 | f32 scale_v = | 489 | f32 scale_v = |
| 486 | static_cast<f32>(framebuffer_height) / static_cast<f32>(screen_info.texture.height); | 490 | static_cast<f32>(framebuffer_height) / static_cast<f32>(screen_info.texture.height); |
| 487 | |||
| 488 | // Scale the output by the crop width/height. This is commonly used with 1280x720 rendering | 491 | // Scale the output by the crop width/height. This is commonly used with 1280x720 rendering |
| 489 | // (e.g. handheld mode) on a 1920x1080 framebuffer. | 492 | // (e.g. handheld mode) on a 1920x1080 framebuffer. |
| 490 | if (framebuffer_crop_rect.GetWidth() > 0) { | 493 | if (framebuffer_crop_rect.GetWidth() > 0) { |
| @@ -503,10 +506,14 @@ void RendererOpenGL::DrawScreen(const Layout::FramebufferLayout& layout) { | |||
| 503 | 506 | ||
| 504 | const auto& screen = layout.screen; | 507 | const auto& screen = layout.screen; |
| 505 | const std::array vertices = { | 508 | const std::array vertices = { |
| 506 | ScreenRectVertex(screen.left, screen.top, texcoords.top * scale_u, left * scale_v), | 509 | ScreenRectVertex(screen.left, screen.top, texcoords.top * scale_u, |
| 507 | ScreenRectVertex(screen.right, screen.top, texcoords.bottom * scale_u, left * scale_v), | 510 | left_start + left * scale_v), |
| 508 | ScreenRectVertex(screen.left, screen.bottom, texcoords.top * scale_u, right * scale_v), | 511 | ScreenRectVertex(screen.right, screen.top, texcoords.bottom * scale_u, |
| 509 | ScreenRectVertex(screen.right, screen.bottom, texcoords.bottom * scale_u, right * scale_v), | 512 | left_start + left * scale_v), |
| 513 | ScreenRectVertex(screen.left, screen.bottom, texcoords.top * scale_u, | ||
| 514 | left_start + right * scale_v), | ||
| 515 | ScreenRectVertex(screen.right, screen.bottom, texcoords.bottom * scale_u, | ||
| 516 | left_start + right * scale_v), | ||
| 510 | }; | 517 | }; |
| 511 | glNamedBufferSubData(vertex_buffer.handle, 0, sizeof(vertices), std::data(vertices)); | 518 | glNamedBufferSubData(vertex_buffer.handle, 0, sizeof(vertices), std::data(vertices)); |
| 512 | 519 | ||
diff --git a/src/video_core/renderer_vulkan/maxwell_to_vk.cpp b/src/video_core/renderer_vulkan/maxwell_to_vk.cpp index 689164a6a..bdb71dc53 100644 --- a/src/video_core/renderer_vulkan/maxwell_to_vk.cpp +++ b/src/video_core/renderer_vulkan/maxwell_to_vk.cpp | |||
| @@ -172,7 +172,7 @@ struct FormatTuple { | |||
| 172 | {VK_FORMAT_R8G8_SINT, Attachable | Storage}, // R8G8_SINT | 172 | {VK_FORMAT_R8G8_SINT, Attachable | Storage}, // R8G8_SINT |
| 173 | {VK_FORMAT_R8G8_UINT, Attachable | Storage}, // R8G8_UINT | 173 | {VK_FORMAT_R8G8_UINT, Attachable | Storage}, // R8G8_UINT |
| 174 | {VK_FORMAT_R32G32_UINT, Attachable | Storage}, // R32G32_UINT | 174 | {VK_FORMAT_R32G32_UINT, Attachable | Storage}, // R32G32_UINT |
| 175 | {VK_FORMAT_UNDEFINED}, // R16G16B16X16_FLOAT | 175 | {VK_FORMAT_R16G16B16A16_SFLOAT, Attachable | Storage}, // R16G16B16X16_FLOAT |
| 176 | {VK_FORMAT_R32_UINT, Attachable | Storage}, // R32_UINT | 176 | {VK_FORMAT_R32_UINT, Attachable | Storage}, // R32_UINT |
| 177 | {VK_FORMAT_R32_SINT, Attachable | Storage}, // R32_SINT | 177 | {VK_FORMAT_R32_SINT, Attachable | Storage}, // R32_SINT |
| 178 | {VK_FORMAT_ASTC_8x8_UNORM_BLOCK}, // ASTC_2D_8X8_UNORM | 178 | {VK_FORMAT_ASTC_8x8_UNORM_BLOCK}, // ASTC_2D_8X8_UNORM |
| @@ -317,195 +317,204 @@ VkPrimitiveTopology PrimitiveTopology([[maybe_unused]] const Device& device, | |||
| 317 | } | 317 | } |
| 318 | } | 318 | } |
| 319 | 319 | ||
| 320 | VkFormat VertexFormat(Maxwell::VertexAttribute::Type type, Maxwell::VertexAttribute::Size size) { | 320 | VkFormat VertexFormat(const Device& device, Maxwell::VertexAttribute::Type type, |
| 321 | switch (type) { | 321 | Maxwell::VertexAttribute::Size size) { |
| 322 | case Maxwell::VertexAttribute::Type::UnsignedNorm: | 322 | const VkFormat format{([&]() { |
| 323 | switch (size) { | 323 | switch (type) { |
| 324 | case Maxwell::VertexAttribute::Size::Size_8: | 324 | case Maxwell::VertexAttribute::Type::UnsignedNorm: |
| 325 | return VK_FORMAT_R8_UNORM; | 325 | switch (size) { |
| 326 | case Maxwell::VertexAttribute::Size::Size_8_8: | 326 | case Maxwell::VertexAttribute::Size::Size_8: |
| 327 | return VK_FORMAT_R8G8_UNORM; | 327 | return VK_FORMAT_R8_UNORM; |
| 328 | case Maxwell::VertexAttribute::Size::Size_8_8_8: | 328 | case Maxwell::VertexAttribute::Size::Size_8_8: |
| 329 | return VK_FORMAT_R8G8B8_UNORM; | 329 | return VK_FORMAT_R8G8_UNORM; |
| 330 | case Maxwell::VertexAttribute::Size::Size_8_8_8_8: | 330 | case Maxwell::VertexAttribute::Size::Size_8_8_8: |
| 331 | return VK_FORMAT_R8G8B8A8_UNORM; | 331 | return VK_FORMAT_R8G8B8_UNORM; |
| 332 | case Maxwell::VertexAttribute::Size::Size_16: | 332 | case Maxwell::VertexAttribute::Size::Size_8_8_8_8: |
| 333 | return VK_FORMAT_R16_UNORM; | 333 | return VK_FORMAT_R8G8B8A8_UNORM; |
| 334 | case Maxwell::VertexAttribute::Size::Size_16_16: | 334 | case Maxwell::VertexAttribute::Size::Size_16: |
| 335 | return VK_FORMAT_R16G16_UNORM; | 335 | return VK_FORMAT_R16_UNORM; |
| 336 | case Maxwell::VertexAttribute::Size::Size_16_16_16: | 336 | case Maxwell::VertexAttribute::Size::Size_16_16: |
| 337 | return VK_FORMAT_R16G16B16_UNORM; | 337 | return VK_FORMAT_R16G16_UNORM; |
| 338 | case Maxwell::VertexAttribute::Size::Size_16_16_16_16: | 338 | case Maxwell::VertexAttribute::Size::Size_16_16_16: |
| 339 | return VK_FORMAT_R16G16B16A16_UNORM; | 339 | return VK_FORMAT_R16G16B16_UNORM; |
| 340 | case Maxwell::VertexAttribute::Size::Size_10_10_10_2: | 340 | case Maxwell::VertexAttribute::Size::Size_16_16_16_16: |
| 341 | return VK_FORMAT_A2B10G10R10_UNORM_PACK32; | 341 | return VK_FORMAT_R16G16B16A16_UNORM; |
| 342 | default: | 342 | case Maxwell::VertexAttribute::Size::Size_10_10_10_2: |
| 343 | return VK_FORMAT_A2B10G10R10_UNORM_PACK32; | ||
| 344 | default: | ||
| 345 | break; | ||
| 346 | } | ||
| 343 | break; | 347 | break; |
| 344 | } | 348 | case Maxwell::VertexAttribute::Type::SignedNorm: |
| 345 | break; | 349 | switch (size) { |
| 346 | case Maxwell::VertexAttribute::Type::SignedNorm: | 350 | case Maxwell::VertexAttribute::Size::Size_8: |
| 347 | switch (size) { | 351 | return VK_FORMAT_R8_SNORM; |
| 348 | case Maxwell::VertexAttribute::Size::Size_8: | 352 | case Maxwell::VertexAttribute::Size::Size_8_8: |
| 349 | return VK_FORMAT_R8_SNORM; | 353 | return VK_FORMAT_R8G8_SNORM; |
| 350 | case Maxwell::VertexAttribute::Size::Size_8_8: | 354 | case Maxwell::VertexAttribute::Size::Size_8_8_8: |
| 351 | return VK_FORMAT_R8G8_SNORM; | 355 | return VK_FORMAT_R8G8B8_SNORM; |
| 352 | case Maxwell::VertexAttribute::Size::Size_8_8_8: | 356 | case Maxwell::VertexAttribute::Size::Size_8_8_8_8: |
| 353 | return VK_FORMAT_R8G8B8_SNORM; | 357 | return VK_FORMAT_R8G8B8A8_SNORM; |
| 354 | case Maxwell::VertexAttribute::Size::Size_8_8_8_8: | 358 | case Maxwell::VertexAttribute::Size::Size_16: |
| 355 | return VK_FORMAT_R8G8B8A8_SNORM; | 359 | return VK_FORMAT_R16_SNORM; |
| 356 | case Maxwell::VertexAttribute::Size::Size_16: | 360 | case Maxwell::VertexAttribute::Size::Size_16_16: |
| 357 | return VK_FORMAT_R16_SNORM; | 361 | return VK_FORMAT_R16G16_SNORM; |
| 358 | case Maxwell::VertexAttribute::Size::Size_16_16: | 362 | case Maxwell::VertexAttribute::Size::Size_16_16_16: |
| 359 | return VK_FORMAT_R16G16_SNORM; | 363 | return VK_FORMAT_R16G16B16_SNORM; |
| 360 | case Maxwell::VertexAttribute::Size::Size_16_16_16: | 364 | case Maxwell::VertexAttribute::Size::Size_16_16_16_16: |
| 361 | return VK_FORMAT_R16G16B16_SNORM; | 365 | return VK_FORMAT_R16G16B16A16_SNORM; |
| 362 | case Maxwell::VertexAttribute::Size::Size_16_16_16_16: | 366 | case Maxwell::VertexAttribute::Size::Size_10_10_10_2: |
| 363 | return VK_FORMAT_R16G16B16A16_SNORM; | 367 | return VK_FORMAT_A2B10G10R10_SNORM_PACK32; |
| 364 | case Maxwell::VertexAttribute::Size::Size_10_10_10_2: | 368 | default: |
| 365 | return VK_FORMAT_A2B10G10R10_SNORM_PACK32; | 369 | break; |
| 366 | default: | 370 | } |
| 367 | break; | 371 | break; |
| 368 | } | 372 | case Maxwell::VertexAttribute::Type::UnsignedScaled: |
| 369 | break; | 373 | switch (size) { |
| 370 | case Maxwell::VertexAttribute::Type::UnsignedScaled: | 374 | case Maxwell::VertexAttribute::Size::Size_8: |
| 371 | switch (size) { | 375 | return VK_FORMAT_R8_USCALED; |
| 372 | case Maxwell::VertexAttribute::Size::Size_8: | 376 | case Maxwell::VertexAttribute::Size::Size_8_8: |
| 373 | return VK_FORMAT_R8_USCALED; | 377 | return VK_FORMAT_R8G8_USCALED; |
| 374 | case Maxwell::VertexAttribute::Size::Size_8_8: | 378 | case Maxwell::VertexAttribute::Size::Size_8_8_8: |
| 375 | return VK_FORMAT_R8G8_USCALED; | 379 | return VK_FORMAT_R8G8B8_USCALED; |
| 376 | case Maxwell::VertexAttribute::Size::Size_8_8_8: | 380 | case Maxwell::VertexAttribute::Size::Size_8_8_8_8: |
| 377 | return VK_FORMAT_R8G8B8_USCALED; | 381 | return VK_FORMAT_R8G8B8A8_USCALED; |
| 378 | case Maxwell::VertexAttribute::Size::Size_8_8_8_8: | 382 | case Maxwell::VertexAttribute::Size::Size_16: |
| 379 | return VK_FORMAT_R8G8B8A8_USCALED; | 383 | return VK_FORMAT_R16_USCALED; |
| 380 | case Maxwell::VertexAttribute::Size::Size_16: | 384 | case Maxwell::VertexAttribute::Size::Size_16_16: |
| 381 | return VK_FORMAT_R16_USCALED; | 385 | return VK_FORMAT_R16G16_USCALED; |
| 382 | case Maxwell::VertexAttribute::Size::Size_16_16: | 386 | case Maxwell::VertexAttribute::Size::Size_16_16_16: |
| 383 | return VK_FORMAT_R16G16_USCALED; | 387 | return VK_FORMAT_R16G16B16_USCALED; |
| 384 | case Maxwell::VertexAttribute::Size::Size_16_16_16: | 388 | case Maxwell::VertexAttribute::Size::Size_16_16_16_16: |
| 385 | return VK_FORMAT_R16G16B16_USCALED; | 389 | return VK_FORMAT_R16G16B16A16_USCALED; |
| 386 | case Maxwell::VertexAttribute::Size::Size_16_16_16_16: | 390 | case Maxwell::VertexAttribute::Size::Size_10_10_10_2: |
| 387 | return VK_FORMAT_R16G16B16A16_USCALED; | 391 | return VK_FORMAT_A2B10G10R10_USCALED_PACK32; |
| 388 | case Maxwell::VertexAttribute::Size::Size_10_10_10_2: | 392 | default: |
| 389 | return VK_FORMAT_A2B10G10R10_USCALED_PACK32; | 393 | break; |
| 390 | default: | 394 | } |
| 391 | break; | 395 | break; |
| 392 | } | 396 | case Maxwell::VertexAttribute::Type::SignedScaled: |
| 393 | break; | 397 | switch (size) { |
| 394 | case Maxwell::VertexAttribute::Type::SignedScaled: | 398 | case Maxwell::VertexAttribute::Size::Size_8: |
| 395 | switch (size) { | 399 | return VK_FORMAT_R8_SSCALED; |
| 396 | case Maxwell::VertexAttribute::Size::Size_8: | 400 | case Maxwell::VertexAttribute::Size::Size_8_8: |
| 397 | return VK_FORMAT_R8_SSCALED; | 401 | return VK_FORMAT_R8G8_SSCALED; |
| 398 | case Maxwell::VertexAttribute::Size::Size_8_8: | 402 | case Maxwell::VertexAttribute::Size::Size_8_8_8: |
| 399 | return VK_FORMAT_R8G8_SSCALED; | 403 | return VK_FORMAT_R8G8B8_SSCALED; |
| 400 | case Maxwell::VertexAttribute::Size::Size_8_8_8: | 404 | case Maxwell::VertexAttribute::Size::Size_8_8_8_8: |
| 401 | return VK_FORMAT_R8G8B8_SSCALED; | 405 | return VK_FORMAT_R8G8B8A8_SSCALED; |
| 402 | case Maxwell::VertexAttribute::Size::Size_8_8_8_8: | 406 | case Maxwell::VertexAttribute::Size::Size_16: |
| 403 | return VK_FORMAT_R8G8B8A8_SSCALED; | 407 | return VK_FORMAT_R16_SSCALED; |
| 404 | case Maxwell::VertexAttribute::Size::Size_16: | 408 | case Maxwell::VertexAttribute::Size::Size_16_16: |
| 405 | return VK_FORMAT_R16_SSCALED; | 409 | return VK_FORMAT_R16G16_SSCALED; |
| 406 | case Maxwell::VertexAttribute::Size::Size_16_16: | 410 | case Maxwell::VertexAttribute::Size::Size_16_16_16: |
| 407 | return VK_FORMAT_R16G16_SSCALED; | 411 | return VK_FORMAT_R16G16B16_SSCALED; |
| 408 | case Maxwell::VertexAttribute::Size::Size_16_16_16: | 412 | case Maxwell::VertexAttribute::Size::Size_16_16_16_16: |
| 409 | return VK_FORMAT_R16G16B16_SSCALED; | 413 | return VK_FORMAT_R16G16B16A16_SSCALED; |
| 410 | case Maxwell::VertexAttribute::Size::Size_16_16_16_16: | 414 | case Maxwell::VertexAttribute::Size::Size_10_10_10_2: |
| 411 | return VK_FORMAT_R16G16B16A16_SSCALED; | 415 | return VK_FORMAT_A2B10G10R10_SSCALED_PACK32; |
| 412 | case Maxwell::VertexAttribute::Size::Size_10_10_10_2: | 416 | default: |
| 413 | return VK_FORMAT_A2B10G10R10_SSCALED_PACK32; | 417 | break; |
| 414 | default: | 418 | } |
| 415 | break; | 419 | break; |
| 416 | } | 420 | case Maxwell::VertexAttribute::Type::UnsignedInt: |
| 417 | break; | 421 | switch (size) { |
| 418 | case Maxwell::VertexAttribute::Type::UnsignedInt: | 422 | case Maxwell::VertexAttribute::Size::Size_8: |
| 419 | switch (size) { | 423 | return VK_FORMAT_R8_UINT; |
| 420 | case Maxwell::VertexAttribute::Size::Size_8: | 424 | case Maxwell::VertexAttribute::Size::Size_8_8: |
| 421 | return VK_FORMAT_R8_UINT; | 425 | return VK_FORMAT_R8G8_UINT; |
| 422 | case Maxwell::VertexAttribute::Size::Size_8_8: | 426 | case Maxwell::VertexAttribute::Size::Size_8_8_8: |
| 423 | return VK_FORMAT_R8G8_UINT; | 427 | return VK_FORMAT_R8G8B8_UINT; |
| 424 | case Maxwell::VertexAttribute::Size::Size_8_8_8: | 428 | case Maxwell::VertexAttribute::Size::Size_8_8_8_8: |
| 425 | return VK_FORMAT_R8G8B8_UINT; | 429 | return VK_FORMAT_R8G8B8A8_UINT; |
| 426 | case Maxwell::VertexAttribute::Size::Size_8_8_8_8: | 430 | case Maxwell::VertexAttribute::Size::Size_16: |
| 427 | return VK_FORMAT_R8G8B8A8_UINT; | 431 | return VK_FORMAT_R16_UINT; |
| 428 | case Maxwell::VertexAttribute::Size::Size_16: | 432 | case Maxwell::VertexAttribute::Size::Size_16_16: |
| 429 | return VK_FORMAT_R16_UINT; | 433 | return VK_FORMAT_R16G16_UINT; |
| 430 | case Maxwell::VertexAttribute::Size::Size_16_16: | 434 | case Maxwell::VertexAttribute::Size::Size_16_16_16: |
| 431 | return VK_FORMAT_R16G16_UINT; | 435 | return VK_FORMAT_R16G16B16_UINT; |
| 432 | case Maxwell::VertexAttribute::Size::Size_16_16_16: | 436 | case Maxwell::VertexAttribute::Size::Size_16_16_16_16: |
| 433 | return VK_FORMAT_R16G16B16_UINT; | 437 | return VK_FORMAT_R16G16B16A16_UINT; |
| 434 | case Maxwell::VertexAttribute::Size::Size_16_16_16_16: | 438 | case Maxwell::VertexAttribute::Size::Size_32: |
| 435 | return VK_FORMAT_R16G16B16A16_UINT; | 439 | return VK_FORMAT_R32_UINT; |
| 436 | case Maxwell::VertexAttribute::Size::Size_32: | 440 | case Maxwell::VertexAttribute::Size::Size_32_32: |
| 437 | return VK_FORMAT_R32_UINT; | 441 | return VK_FORMAT_R32G32_UINT; |
| 438 | case Maxwell::VertexAttribute::Size::Size_32_32: | 442 | case Maxwell::VertexAttribute::Size::Size_32_32_32: |
| 439 | return VK_FORMAT_R32G32_UINT; | 443 | return VK_FORMAT_R32G32B32_UINT; |
| 440 | case Maxwell::VertexAttribute::Size::Size_32_32_32: | 444 | case Maxwell::VertexAttribute::Size::Size_32_32_32_32: |
| 441 | return VK_FORMAT_R32G32B32_UINT; | 445 | return VK_FORMAT_R32G32B32A32_UINT; |
| 442 | case Maxwell::VertexAttribute::Size::Size_32_32_32_32: | 446 | case Maxwell::VertexAttribute::Size::Size_10_10_10_2: |
| 443 | return VK_FORMAT_R32G32B32A32_UINT; | 447 | return VK_FORMAT_A2B10G10R10_UINT_PACK32; |
| 444 | case Maxwell::VertexAttribute::Size::Size_10_10_10_2: | 448 | default: |
| 445 | return VK_FORMAT_A2B10G10R10_UINT_PACK32; | 449 | break; |
| 446 | default: | 450 | } |
| 447 | break; | 451 | break; |
| 448 | } | 452 | case Maxwell::VertexAttribute::Type::SignedInt: |
| 449 | break; | 453 | switch (size) { |
| 450 | case Maxwell::VertexAttribute::Type::SignedInt: | 454 | case Maxwell::VertexAttribute::Size::Size_8: |
| 451 | switch (size) { | 455 | return VK_FORMAT_R8_SINT; |
| 452 | case Maxwell::VertexAttribute::Size::Size_8: | 456 | case Maxwell::VertexAttribute::Size::Size_8_8: |
| 453 | return VK_FORMAT_R8_SINT; | 457 | return VK_FORMAT_R8G8_SINT; |
| 454 | case Maxwell::VertexAttribute::Size::Size_8_8: | 458 | case Maxwell::VertexAttribute::Size::Size_8_8_8: |
| 455 | return VK_FORMAT_R8G8_SINT; | 459 | return VK_FORMAT_R8G8B8_SINT; |
| 456 | case Maxwell::VertexAttribute::Size::Size_8_8_8: | 460 | case Maxwell::VertexAttribute::Size::Size_8_8_8_8: |
| 457 | return VK_FORMAT_R8G8B8_SINT; | 461 | return VK_FORMAT_R8G8B8A8_SINT; |
| 458 | case Maxwell::VertexAttribute::Size::Size_8_8_8_8: | 462 | case Maxwell::VertexAttribute::Size::Size_16: |
| 459 | return VK_FORMAT_R8G8B8A8_SINT; | 463 | return VK_FORMAT_R16_SINT; |
| 460 | case Maxwell::VertexAttribute::Size::Size_16: | 464 | case Maxwell::VertexAttribute::Size::Size_16_16: |
| 461 | return VK_FORMAT_R16_SINT; | 465 | return VK_FORMAT_R16G16_SINT; |
| 462 | case Maxwell::VertexAttribute::Size::Size_16_16: | 466 | case Maxwell::VertexAttribute::Size::Size_16_16_16: |
| 463 | return VK_FORMAT_R16G16_SINT; | 467 | return VK_FORMAT_R16G16B16_SINT; |
| 464 | case Maxwell::VertexAttribute::Size::Size_16_16_16: | 468 | case Maxwell::VertexAttribute::Size::Size_16_16_16_16: |
| 465 | return VK_FORMAT_R16G16B16_SINT; | 469 | return VK_FORMAT_R16G16B16A16_SINT; |
| 466 | case Maxwell::VertexAttribute::Size::Size_16_16_16_16: | 470 | case Maxwell::VertexAttribute::Size::Size_32: |
| 467 | return VK_FORMAT_R16G16B16A16_SINT; | 471 | return VK_FORMAT_R32_SINT; |
| 468 | case Maxwell::VertexAttribute::Size::Size_32: | 472 | case Maxwell::VertexAttribute::Size::Size_32_32: |
| 469 | return VK_FORMAT_R32_SINT; | 473 | return VK_FORMAT_R32G32_SINT; |
| 470 | case Maxwell::VertexAttribute::Size::Size_32_32: | 474 | case Maxwell::VertexAttribute::Size::Size_32_32_32: |
| 471 | return VK_FORMAT_R32G32_SINT; | 475 | return VK_FORMAT_R32G32B32_SINT; |
| 472 | case Maxwell::VertexAttribute::Size::Size_32_32_32: | 476 | case Maxwell::VertexAttribute::Size::Size_32_32_32_32: |
| 473 | return VK_FORMAT_R32G32B32_SINT; | 477 | return VK_FORMAT_R32G32B32A32_SINT; |
| 474 | case Maxwell::VertexAttribute::Size::Size_32_32_32_32: | 478 | case Maxwell::VertexAttribute::Size::Size_10_10_10_2: |
| 475 | return VK_FORMAT_R32G32B32A32_SINT; | 479 | return VK_FORMAT_A2B10G10R10_SINT_PACK32; |
| 476 | case Maxwell::VertexAttribute::Size::Size_10_10_10_2: | 480 | default: |
| 477 | return VK_FORMAT_A2B10G10R10_SINT_PACK32; | 481 | break; |
| 478 | default: | 482 | } |
| 479 | break; | 483 | break; |
| 480 | } | 484 | case Maxwell::VertexAttribute::Type::Float: |
| 481 | break; | 485 | switch (size) { |
| 482 | case Maxwell::VertexAttribute::Type::Float: | 486 | case Maxwell::VertexAttribute::Size::Size_16: |
| 483 | switch (size) { | 487 | return VK_FORMAT_R16_SFLOAT; |
| 484 | case Maxwell::VertexAttribute::Size::Size_16: | 488 | case Maxwell::VertexAttribute::Size::Size_16_16: |
| 485 | return VK_FORMAT_R16_SFLOAT; | 489 | return VK_FORMAT_R16G16_SFLOAT; |
| 486 | case Maxwell::VertexAttribute::Size::Size_16_16: | 490 | case Maxwell::VertexAttribute::Size::Size_16_16_16: |
| 487 | return VK_FORMAT_R16G16_SFLOAT; | 491 | return VK_FORMAT_R16G16B16_SFLOAT; |
| 488 | case Maxwell::VertexAttribute::Size::Size_16_16_16: | 492 | case Maxwell::VertexAttribute::Size::Size_16_16_16_16: |
| 489 | return VK_FORMAT_R16G16B16_SFLOAT; | 493 | return VK_FORMAT_R16G16B16A16_SFLOAT; |
| 490 | case Maxwell::VertexAttribute::Size::Size_16_16_16_16: | 494 | case Maxwell::VertexAttribute::Size::Size_32: |
| 491 | return VK_FORMAT_R16G16B16A16_SFLOAT; | 495 | return VK_FORMAT_R32_SFLOAT; |
| 492 | case Maxwell::VertexAttribute::Size::Size_32: | 496 | case Maxwell::VertexAttribute::Size::Size_32_32: |
| 493 | return VK_FORMAT_R32_SFLOAT; | 497 | return VK_FORMAT_R32G32_SFLOAT; |
| 494 | case Maxwell::VertexAttribute::Size::Size_32_32: | 498 | case Maxwell::VertexAttribute::Size::Size_32_32_32: |
| 495 | return VK_FORMAT_R32G32_SFLOAT; | 499 | return VK_FORMAT_R32G32B32_SFLOAT; |
| 496 | case Maxwell::VertexAttribute::Size::Size_32_32_32: | 500 | case Maxwell::VertexAttribute::Size::Size_32_32_32_32: |
| 497 | return VK_FORMAT_R32G32B32_SFLOAT; | 501 | return VK_FORMAT_R32G32B32A32_SFLOAT; |
| 498 | case Maxwell::VertexAttribute::Size::Size_32_32_32_32: | 502 | case Maxwell::VertexAttribute::Size::Size_11_11_10: |
| 499 | return VK_FORMAT_R32G32B32A32_SFLOAT; | 503 | return VK_FORMAT_B10G11R11_UFLOAT_PACK32; |
| 500 | case Maxwell::VertexAttribute::Size::Size_11_11_10: | 504 | default: |
| 501 | return VK_FORMAT_B10G11R11_UFLOAT_PACK32; | 505 | break; |
| 502 | default: | 506 | } |
| 503 | break; | 507 | break; |
| 504 | } | 508 | } |
| 505 | break; | 509 | return VK_FORMAT_UNDEFINED; |
| 510 | })()}; | ||
| 511 | |||
| 512 | if (format == VK_FORMAT_UNDEFINED) { | ||
| 513 | UNIMPLEMENTED_MSG("Unimplemented vertex format of type={} and size={}", type, size); | ||
| 506 | } | 514 | } |
| 507 | UNIMPLEMENTED_MSG("Unimplemented vertex format of type={} and size={}", type, size); | 515 | |
| 508 | return {}; | 516 | return device.GetSupportedFormat(format, VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT, |
| 517 | FormatType::Buffer); | ||
| 509 | } | 518 | } |
| 510 | 519 | ||
| 511 | VkCompareOp ComparisonOp(Maxwell::ComparisonOp comparison) { | 520 | VkCompareOp ComparisonOp(Maxwell::ComparisonOp comparison) { |
diff --git a/src/video_core/renderer_vulkan/maxwell_to_vk.h b/src/video_core/renderer_vulkan/maxwell_to_vk.h index 9edd6af6a..356d46292 100644 --- a/src/video_core/renderer_vulkan/maxwell_to_vk.h +++ b/src/video_core/renderer_vulkan/maxwell_to_vk.h | |||
| @@ -48,7 +48,8 @@ VkShaderStageFlagBits ShaderStage(Shader::Stage stage); | |||
| 48 | 48 | ||
| 49 | VkPrimitiveTopology PrimitiveTopology(const Device& device, Maxwell::PrimitiveTopology topology); | 49 | VkPrimitiveTopology PrimitiveTopology(const Device& device, Maxwell::PrimitiveTopology topology); |
| 50 | 50 | ||
| 51 | VkFormat VertexFormat(Maxwell::VertexAttribute::Type type, Maxwell::VertexAttribute::Size size); | 51 | VkFormat VertexFormat(const Device& device, Maxwell::VertexAttribute::Type type, |
| 52 | Maxwell::VertexAttribute::Size size); | ||
| 52 | 53 | ||
| 53 | VkCompareOp ComparisonOp(Maxwell::ComparisonOp comparison); | 54 | VkCompareOp ComparisonOp(Maxwell::ComparisonOp comparison); |
| 54 | 55 | ||
diff --git a/src/video_core/renderer_vulkan/vk_blit_screen.cpp b/src/video_core/renderer_vulkan/vk_blit_screen.cpp index 4a1d96322..444c29f68 100644 --- a/src/video_core/renderer_vulkan/vk_blit_screen.cpp +++ b/src/video_core/renderer_vulkan/vk_blit_screen.cpp | |||
| @@ -87,12 +87,8 @@ u32 GetBytesPerPixel(const Tegra::FramebufferConfig& framebuffer) { | |||
| 87 | } | 87 | } |
| 88 | 88 | ||
| 89 | std::size_t GetSizeInBytes(const Tegra::FramebufferConfig& framebuffer) { | 89 | std::size_t GetSizeInBytes(const Tegra::FramebufferConfig& framebuffer) { |
| 90 | // TODO(Rodrigo): Read this from HLE | 90 | return static_cast<std::size_t>(framebuffer.stride) * |
| 91 | constexpr u32 block_height_log2 = 4; | 91 | static_cast<std::size_t>(framebuffer.height) * GetBytesPerPixel(framebuffer); |
| 92 | const u32 bytes_per_pixel = GetBytesPerPixel(framebuffer); | ||
| 93 | const u64 size_bytes{Tegra::Texture::CalculateSize( | ||
| 94 | true, bytes_per_pixel, framebuffer.stride, framebuffer.height, 1, block_height_log2, 0)}; | ||
| 95 | return size_bytes; | ||
| 96 | } | 92 | } |
| 97 | 93 | ||
| 98 | VkFormat GetFormat(const Tegra::FramebufferConfig& framebuffer) { | 94 | VkFormat GetFormat(const Tegra::FramebufferConfig& framebuffer) { |
| @@ -173,10 +169,12 @@ VkSemaphore BlitScreen::Draw(const Tegra::FramebufferConfig& framebuffer, | |||
| 173 | // TODO(Rodrigo): Read this from HLE | 169 | // TODO(Rodrigo): Read this from HLE |
| 174 | constexpr u32 block_height_log2 = 4; | 170 | constexpr u32 block_height_log2 = 4; |
| 175 | const u32 bytes_per_pixel = GetBytesPerPixel(framebuffer); | 171 | const u32 bytes_per_pixel = GetBytesPerPixel(framebuffer); |
| 176 | const u64 size_bytes{GetSizeInBytes(framebuffer)}; | 172 | const u64 linear_size{GetSizeInBytes(framebuffer)}; |
| 177 | 173 | const u64 tiled_size{Tegra::Texture::CalculateSize(true, bytes_per_pixel, | |
| 174 | framebuffer.stride, framebuffer.height, | ||
| 175 | 1, block_height_log2, 0)}; | ||
| 178 | Tegra::Texture::UnswizzleTexture( | 176 | Tegra::Texture::UnswizzleTexture( |
| 179 | mapped_span.subspan(image_offset, size_bytes), std::span(host_ptr, size_bytes), | 177 | mapped_span.subspan(image_offset, linear_size), std::span(host_ptr, tiled_size), |
| 180 | bytes_per_pixel, framebuffer.width, framebuffer.height, 1, block_height_log2, 0); | 178 | bytes_per_pixel, framebuffer.width, framebuffer.height, 1, block_height_log2, 0); |
| 181 | 179 | ||
| 182 | const VkBufferImageCopy copy{ | 180 | const VkBufferImageCopy copy{ |
| @@ -1404,12 +1402,15 @@ void BlitScreen::SetVertexData(BufferData& data, const Tegra::FramebufferConfig& | |||
| 1404 | break; | 1402 | break; |
| 1405 | } | 1403 | } |
| 1406 | 1404 | ||
| 1407 | UNIMPLEMENTED_IF(framebuffer_crop_rect.top != 0); | ||
| 1408 | UNIMPLEMENTED_IF(framebuffer_crop_rect.left != 0); | 1405 | UNIMPLEMENTED_IF(framebuffer_crop_rect.left != 0); |
| 1409 | 1406 | ||
| 1407 | f32 left_start{}; | ||
| 1408 | if (framebuffer_crop_rect.Top() > 0) { | ||
| 1409 | left_start = static_cast<f32>(framebuffer_crop_rect.Top()) / | ||
| 1410 | static_cast<f32>(framebuffer_crop_rect.Bottom()); | ||
| 1411 | } | ||
| 1410 | f32 scale_u = static_cast<f32>(framebuffer.width) / static_cast<f32>(screen_info.width); | 1412 | f32 scale_u = static_cast<f32>(framebuffer.width) / static_cast<f32>(screen_info.width); |
| 1411 | f32 scale_v = static_cast<f32>(framebuffer.height) / static_cast<f32>(screen_info.height); | 1413 | f32 scale_v = static_cast<f32>(framebuffer.height) / static_cast<f32>(screen_info.height); |
| 1412 | |||
| 1413 | // Scale the output by the crop width/height. This is commonly used with 1280x720 rendering | 1414 | // Scale the output by the crop width/height. This is commonly used with 1280x720 rendering |
| 1414 | // (e.g. handheld mode) on a 1920x1080 framebuffer. | 1415 | // (e.g. handheld mode) on a 1920x1080 framebuffer. |
| 1415 | if (!fsr) { | 1416 | if (!fsr) { |
| @@ -1428,10 +1429,13 @@ void BlitScreen::SetVertexData(BufferData& data, const Tegra::FramebufferConfig& | |||
| 1428 | const auto y = static_cast<f32>(screen.top); | 1429 | const auto y = static_cast<f32>(screen.top); |
| 1429 | const auto w = static_cast<f32>(screen.GetWidth()); | 1430 | const auto w = static_cast<f32>(screen.GetWidth()); |
| 1430 | const auto h = static_cast<f32>(screen.GetHeight()); | 1431 | const auto h = static_cast<f32>(screen.GetHeight()); |
| 1431 | data.vertices[0] = ScreenRectVertex(x, y, texcoords.top * scale_u, left * scale_v); | 1432 | data.vertices[0] = ScreenRectVertex(x, y, texcoords.top * scale_u, left_start + left * scale_v); |
| 1432 | data.vertices[1] = ScreenRectVertex(x + w, y, texcoords.bottom * scale_u, left * scale_v); | 1433 | data.vertices[1] = |
| 1433 | data.vertices[2] = ScreenRectVertex(x, y + h, texcoords.top * scale_u, right * scale_v); | 1434 | ScreenRectVertex(x + w, y, texcoords.bottom * scale_u, left_start + left * scale_v); |
| 1434 | data.vertices[3] = ScreenRectVertex(x + w, y + h, texcoords.bottom * scale_u, right * scale_v); | 1435 | data.vertices[2] = |
| 1436 | ScreenRectVertex(x, y + h, texcoords.top * scale_u, left_start + right * scale_v); | ||
| 1437 | data.vertices[3] = | ||
| 1438 | ScreenRectVertex(x + w, y + h, texcoords.bottom * scale_u, left_start + right * scale_v); | ||
| 1435 | } | 1439 | } |
| 1436 | 1440 | ||
| 1437 | void BlitScreen::CreateFSR() { | 1441 | void BlitScreen::CreateFSR() { |
diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp index 682f05335..5aca8f038 100644 --- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp +++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp | |||
| @@ -559,7 +559,7 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) { | |||
| 559 | vertex_attributes.push_back({ | 559 | vertex_attributes.push_back({ |
| 560 | .location = static_cast<u32>(index), | 560 | .location = static_cast<u32>(index), |
| 561 | .binding = attribute.buffer, | 561 | .binding = attribute.buffer, |
| 562 | .format = MaxwellToVK::VertexFormat(attribute.Type(), attribute.Size()), | 562 | .format = MaxwellToVK::VertexFormat(device, attribute.Type(), attribute.Size()), |
| 563 | .offset = attribute.offset, | 563 | .offset = attribute.offset, |
| 564 | }); | 564 | }); |
| 565 | } | 565 | } |
diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp index 09e035799..9708dc45e 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp | |||
| @@ -53,7 +53,7 @@ using VideoCommon::FileEnvironment; | |||
| 53 | using VideoCommon::GenericEnvironment; | 53 | using VideoCommon::GenericEnvironment; |
| 54 | using VideoCommon::GraphicsEnvironment; | 54 | using VideoCommon::GraphicsEnvironment; |
| 55 | 55 | ||
| 56 | constexpr u32 CACHE_VERSION = 5; | 56 | constexpr u32 CACHE_VERSION = 6; |
| 57 | 57 | ||
| 58 | template <typename Container> | 58 | template <typename Container> |
| 59 | auto MakeSpan(Container& container) { | 59 | auto MakeSpan(Container& container) { |
| @@ -434,7 +434,9 @@ void PipelineCache::LoadDiskResources(u64 title_id, std::stop_token stop_loading | |||
| 434 | state.statistics.get(), false)}; | 434 | state.statistics.get(), false)}; |
| 435 | 435 | ||
| 436 | std::scoped_lock lock{state.mutex}; | 436 | std::scoped_lock lock{state.mutex}; |
| 437 | graphics_cache.emplace(key, std::move(pipeline)); | 437 | if (pipeline) { |
| 438 | graphics_cache.emplace(key, std::move(pipeline)); | ||
| 439 | } | ||
| 438 | ++state.built; | 440 | ++state.built; |
| 439 | if (state.has_loaded) { | 441 | if (state.has_loaded) { |
| 440 | callback(VideoCore::LoadCallbackStage::Build, state.built, state.total); | 442 | callback(VideoCore::LoadCallbackStage::Build, state.built, state.total); |
| @@ -452,7 +454,7 @@ void PipelineCache::LoadDiskResources(u64 title_id, std::stop_token stop_loading | |||
| 452 | state.has_loaded = true; | 454 | state.has_loaded = true; |
| 453 | lock.unlock(); | 455 | lock.unlock(); |
| 454 | 456 | ||
| 455 | workers.WaitForRequests(); | 457 | workers.WaitForRequests(stop_loading); |
| 456 | 458 | ||
| 457 | if (state.statistics) { | 459 | if (state.statistics) { |
| 458 | state.statistics->Report(); | 460 | state.statistics->Report(); |
diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index 10f9fe7fe..7e40c2df1 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp | |||
| @@ -69,10 +69,17 @@ VkViewport GetViewportState(const Device& device, const Maxwell& regs, size_t in | |||
| 69 | const float width = conv(src.scale_x * 2.0f); | 69 | const float width = conv(src.scale_x * 2.0f); |
| 70 | float y = conv(src.translate_y - src.scale_y); | 70 | float y = conv(src.translate_y - src.scale_y); |
| 71 | float height = conv(src.scale_y * 2.0f); | 71 | float height = conv(src.scale_y * 2.0f); |
| 72 | if (regs.screen_y_control.y_negate) { | 72 | bool y_negate = regs.screen_y_control.y_negate; |
| 73 | |||
| 74 | if (!device.IsNvViewportSwizzleSupported()) { | ||
| 75 | y_negate = y_negate != (src.swizzle.y == Maxwell::ViewportSwizzle::NegativeY); | ||
| 76 | } | ||
| 77 | |||
| 78 | if (y_negate) { | ||
| 73 | y += height; | 79 | y += height; |
| 74 | height = -height; | 80 | height = -height; |
| 75 | } | 81 | } |
| 82 | |||
| 76 | const float reduce_z = regs.depth_mode == Maxwell::DepthMode::MinusOneToOne ? 1.0f : 0.0f; | 83 | const float reduce_z = regs.depth_mode == Maxwell::DepthMode::MinusOneToOne ? 1.0f : 0.0f; |
| 77 | VkViewport viewport{ | 84 | VkViewport viewport{ |
| 78 | .x = x, | 85 | .x = x, |
| @@ -939,7 +946,7 @@ void RasterizerVulkan::UpdateVertexInput(Tegra::Engines::Maxwell3D::Regs& regs) | |||
| 939 | .pNext = nullptr, | 946 | .pNext = nullptr, |
| 940 | .location = static_cast<u32>(index), | 947 | .location = static_cast<u32>(index), |
| 941 | .binding = binding, | 948 | .binding = binding, |
| 942 | .format = MaxwellToVK::VertexFormat(attribute.type, attribute.size), | 949 | .format = MaxwellToVK::VertexFormat(device, attribute.type, attribute.size), |
| 943 | .offset = attribute.offset, | 950 | .offset = attribute.offset, |
| 944 | }); | 951 | }); |
| 945 | } | 952 | } |
diff --git a/src/video_core/renderer_vulkan/vk_swapchain.cpp b/src/video_core/renderer_vulkan/vk_swapchain.cpp index fa8efd22e..a69ae7725 100644 --- a/src/video_core/renderer_vulkan/vk_swapchain.cpp +++ b/src/video_core/renderer_vulkan/vk_swapchain.cpp | |||
| @@ -33,9 +33,10 @@ VkSurfaceFormatKHR ChooseSwapSurfaceFormat(vk::Span<VkSurfaceFormatKHR> formats) | |||
| 33 | } | 33 | } |
| 34 | 34 | ||
| 35 | VkPresentModeKHR ChooseSwapPresentMode(vk::Span<VkPresentModeKHR> modes) { | 35 | VkPresentModeKHR ChooseSwapPresentMode(vk::Span<VkPresentModeKHR> modes) { |
| 36 | // Mailbox doesn't lock the application like fifo (vsync), prefer it | 36 | // Mailbox (triple buffering) doesn't lock the application like fifo (vsync), |
| 37 | // prefer it if vsync option is not selected | ||
| 37 | const auto found_mailbox = std::find(modes.begin(), modes.end(), VK_PRESENT_MODE_MAILBOX_KHR); | 38 | const auto found_mailbox = std::find(modes.begin(), modes.end(), VK_PRESENT_MODE_MAILBOX_KHR); |
| 38 | if (found_mailbox != modes.end()) { | 39 | if (found_mailbox != modes.end() && !Settings::values.use_vsync.GetValue()) { |
| 39 | return VK_PRESENT_MODE_MAILBOX_KHR; | 40 | return VK_PRESENT_MODE_MAILBOX_KHR; |
| 40 | } | 41 | } |
| 41 | if (!Settings::values.use_speed_limit.GetValue()) { | 42 | if (!Settings::values.use_speed_limit.GetValue()) { |
diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.cpp b/src/video_core/renderer_vulkan/vk_texture_cache.cpp index ba6d81420..caca79d79 100644 --- a/src/video_core/renderer_vulkan/vk_texture_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_texture_cache.cpp | |||
| @@ -230,6 +230,7 @@ constexpr VkBorderColor ConvertBorderColor(const std::array<float, 4>& color) { | |||
| 230 | case Shader::TextureType::Color1D: | 230 | case Shader::TextureType::Color1D: |
| 231 | return VK_IMAGE_VIEW_TYPE_1D; | 231 | return VK_IMAGE_VIEW_TYPE_1D; |
| 232 | case Shader::TextureType::Color2D: | 232 | case Shader::TextureType::Color2D: |
| 233 | case Shader::TextureType::Color2DRect: | ||
| 233 | return VK_IMAGE_VIEW_TYPE_2D; | 234 | return VK_IMAGE_VIEW_TYPE_2D; |
| 234 | case Shader::TextureType::ColorCube: | 235 | case Shader::TextureType::ColorCube: |
| 235 | return VK_IMAGE_VIEW_TYPE_CUBE; | 236 | return VK_IMAGE_VIEW_TYPE_CUBE; |
| @@ -254,6 +255,7 @@ constexpr VkBorderColor ConvertBorderColor(const std::array<float, 4>& color) { | |||
| 254 | case VideoCommon::ImageViewType::e1D: | 255 | case VideoCommon::ImageViewType::e1D: |
| 255 | return VK_IMAGE_VIEW_TYPE_1D; | 256 | return VK_IMAGE_VIEW_TYPE_1D; |
| 256 | case VideoCommon::ImageViewType::e2D: | 257 | case VideoCommon::ImageViewType::e2D: |
| 258 | case VideoCommon::ImageViewType::Rect: | ||
| 257 | return VK_IMAGE_VIEW_TYPE_2D; | 259 | return VK_IMAGE_VIEW_TYPE_2D; |
| 258 | case VideoCommon::ImageViewType::Cube: | 260 | case VideoCommon::ImageViewType::Cube: |
| 259 | return VK_IMAGE_VIEW_TYPE_CUBE; | 261 | return VK_IMAGE_VIEW_TYPE_CUBE; |
| @@ -265,9 +267,6 @@ constexpr VkBorderColor ConvertBorderColor(const std::array<float, 4>& color) { | |||
| 265 | return VK_IMAGE_VIEW_TYPE_2D_ARRAY; | 267 | return VK_IMAGE_VIEW_TYPE_2D_ARRAY; |
| 266 | case VideoCommon::ImageViewType::CubeArray: | 268 | case VideoCommon::ImageViewType::CubeArray: |
| 267 | return VK_IMAGE_VIEW_TYPE_CUBE_ARRAY; | 269 | return VK_IMAGE_VIEW_TYPE_CUBE_ARRAY; |
| 268 | case VideoCommon::ImageViewType::Rect: | ||
| 269 | UNIMPLEMENTED_MSG("Rect image view"); | ||
| 270 | return VK_IMAGE_VIEW_TYPE_2D; | ||
| 271 | case VideoCommon::ImageViewType::Buffer: | 270 | case VideoCommon::ImageViewType::Buffer: |
| 272 | ASSERT_MSG(false, "Texture buffers can't be image views"); | 271 | ASSERT_MSG(false, "Texture buffers can't be image views"); |
| 273 | return VK_IMAGE_VIEW_TYPE_1D; | 272 | return VK_IMAGE_VIEW_TYPE_1D; |
| @@ -1579,6 +1578,7 @@ ImageView::ImageView(TextureCacheRuntime& runtime, const VideoCommon::ImageViewI | |||
| 1579 | break; | 1578 | break; |
| 1580 | case VideoCommon::ImageViewType::e2D: | 1579 | case VideoCommon::ImageViewType::e2D: |
| 1581 | case VideoCommon::ImageViewType::e2DArray: | 1580 | case VideoCommon::ImageViewType::e2DArray: |
| 1581 | case VideoCommon::ImageViewType::Rect: | ||
| 1582 | create(TextureType::Color2D, 1); | 1582 | create(TextureType::Color2D, 1); |
| 1583 | create(TextureType::ColorArray2D, std::nullopt); | 1583 | create(TextureType::ColorArray2D, std::nullopt); |
| 1584 | render_target = Handle(Shader::TextureType::ColorArray2D); | 1584 | render_target = Handle(Shader::TextureType::ColorArray2D); |
| @@ -1592,9 +1592,6 @@ ImageView::ImageView(TextureCacheRuntime& runtime, const VideoCommon::ImageViewI | |||
| 1592 | create(TextureType::ColorCube, 6); | 1592 | create(TextureType::ColorCube, 6); |
| 1593 | create(TextureType::ColorArrayCube, std::nullopt); | 1593 | create(TextureType::ColorArrayCube, std::nullopt); |
| 1594 | break; | 1594 | break; |
| 1595 | case VideoCommon::ImageViewType::Rect: | ||
| 1596 | UNIMPLEMENTED(); | ||
| 1597 | break; | ||
| 1598 | case VideoCommon::ImageViewType::Buffer: | 1595 | case VideoCommon::ImageViewType::Buffer: |
| 1599 | ASSERT(false); | 1596 | ASSERT(false); |
| 1600 | break; | 1597 | break; |
| @@ -1618,6 +1615,9 @@ ImageView::ImageView(TextureCacheRuntime&, const VideoCommon::NullImageViewParam | |||
| 1618 | ImageView::~ImageView() = default; | 1615 | ImageView::~ImageView() = default; |
| 1619 | 1616 | ||
| 1620 | VkImageView ImageView::DepthView() { | 1617 | VkImageView ImageView::DepthView() { |
| 1618 | if (!image_handle) { | ||
| 1619 | return VK_NULL_HANDLE; | ||
| 1620 | } | ||
| 1621 | if (depth_view) { | 1621 | if (depth_view) { |
| 1622 | return *depth_view; | 1622 | return *depth_view; |
| 1623 | } | 1623 | } |
| @@ -1627,6 +1627,9 @@ VkImageView ImageView::DepthView() { | |||
| 1627 | } | 1627 | } |
| 1628 | 1628 | ||
| 1629 | VkImageView ImageView::StencilView() { | 1629 | VkImageView ImageView::StencilView() { |
| 1630 | if (!image_handle) { | ||
| 1631 | return VK_NULL_HANDLE; | ||
| 1632 | } | ||
| 1630 | if (stencil_view) { | 1633 | if (stencil_view) { |
| 1631 | return *stencil_view; | 1634 | return *stencil_view; |
| 1632 | } | 1635 | } |
| @@ -1636,6 +1639,9 @@ VkImageView ImageView::StencilView() { | |||
| 1636 | } | 1639 | } |
| 1637 | 1640 | ||
| 1638 | VkImageView ImageView::ColorView() { | 1641 | VkImageView ImageView::ColorView() { |
| 1642 | if (!image_handle) { | ||
| 1643 | return VK_NULL_HANDLE; | ||
| 1644 | } | ||
| 1639 | if (color_view) { | 1645 | if (color_view) { |
| 1640 | return *color_view; | 1646 | return *color_view; |
| 1641 | } | 1647 | } |
| @@ -1645,6 +1651,9 @@ VkImageView ImageView::ColorView() { | |||
| 1645 | 1651 | ||
| 1646 | VkImageView ImageView::StorageView(Shader::TextureType texture_type, | 1652 | VkImageView ImageView::StorageView(Shader::TextureType texture_type, |
| 1647 | Shader::ImageFormat image_format) { | 1653 | Shader::ImageFormat image_format) { |
| 1654 | if (!image_handle) { | ||
| 1655 | return VK_NULL_HANDLE; | ||
| 1656 | } | ||
| 1648 | if (image_format == Shader::ImageFormat::Typeless) { | 1657 | if (image_format == Shader::ImageFormat::Typeless) { |
| 1649 | return Handle(texture_type); | 1658 | return Handle(texture_type); |
| 1650 | } | 1659 | } |
diff --git a/src/video_core/shader_cache.cpp b/src/video_core/shader_cache.cpp index 4b1101f7c..164e4ee0e 100644 --- a/src/video_core/shader_cache.cpp +++ b/src/video_core/shader_cache.cpp | |||
| @@ -123,8 +123,8 @@ void ShaderCache::Register(std::unique_ptr<ShaderInfo> data, VAddr addr, size_t | |||
| 123 | const VAddr addr_end = addr + size; | 123 | const VAddr addr_end = addr + size; |
| 124 | Entry* const entry = NewEntry(addr, addr_end, data.get()); | 124 | Entry* const entry = NewEntry(addr, addr_end, data.get()); |
| 125 | 125 | ||
| 126 | const u64 page_end = (addr_end + PAGE_SIZE - 1) >> PAGE_BITS; | 126 | const u64 page_end = (addr_end + YUZU_PAGESIZE - 1) >> YUZU_PAGEBITS; |
| 127 | for (u64 page = addr >> PAGE_BITS; page < page_end; ++page) { | 127 | for (u64 page = addr >> YUZU_PAGEBITS; page < page_end; ++page) { |
| 128 | invalidation_cache[page].push_back(entry); | 128 | invalidation_cache[page].push_back(entry); |
| 129 | } | 129 | } |
| 130 | 130 | ||
| @@ -135,8 +135,8 @@ void ShaderCache::Register(std::unique_ptr<ShaderInfo> data, VAddr addr, size_t | |||
| 135 | 135 | ||
| 136 | void ShaderCache::InvalidatePagesInRegion(VAddr addr, size_t size) { | 136 | void ShaderCache::InvalidatePagesInRegion(VAddr addr, size_t size) { |
| 137 | const VAddr addr_end = addr + size; | 137 | const VAddr addr_end = addr + size; |
| 138 | const u64 page_end = (addr_end + PAGE_SIZE - 1) >> PAGE_BITS; | 138 | const u64 page_end = (addr_end + YUZU_PAGESIZE - 1) >> YUZU_PAGEBITS; |
| 139 | for (u64 page = addr >> PAGE_BITS; page < page_end; ++page) { | 139 | for (u64 page = addr >> YUZU_PAGEBITS; page < page_end; ++page) { |
| 140 | auto it = invalidation_cache.find(page); | 140 | auto it = invalidation_cache.find(page); |
| 141 | if (it == invalidation_cache.end()) { | 141 | if (it == invalidation_cache.end()) { |
| 142 | continue; | 142 | continue; |
| @@ -189,8 +189,8 @@ void ShaderCache::InvalidatePageEntries(std::vector<Entry*>& entries, VAddr addr | |||
| 189 | } | 189 | } |
| 190 | 190 | ||
| 191 | void ShaderCache::RemoveEntryFromInvalidationCache(const Entry* entry) { | 191 | void ShaderCache::RemoveEntryFromInvalidationCache(const Entry* entry) { |
| 192 | const u64 page_end = (entry->addr_end + PAGE_SIZE - 1) >> PAGE_BITS; | 192 | const u64 page_end = (entry->addr_end + YUZU_PAGESIZE - 1) >> YUZU_PAGEBITS; |
| 193 | for (u64 page = entry->addr_start >> PAGE_BITS; page < page_end; ++page) { | 193 | for (u64 page = entry->addr_start >> YUZU_PAGEBITS; page < page_end; ++page) { |
| 194 | const auto entries_it = invalidation_cache.find(page); | 194 | const auto entries_it = invalidation_cache.find(page); |
| 195 | ASSERT(entries_it != invalidation_cache.end()); | 195 | ASSERT(entries_it != invalidation_cache.end()); |
| 196 | std::vector<Entry*>& entries = entries_it->second; | 196 | std::vector<Entry*>& entries = entries_it->second; |
diff --git a/src/video_core/shader_cache.h b/src/video_core/shader_cache.h index 1109cfe83..f67cea8c4 100644 --- a/src/video_core/shader_cache.h +++ b/src/video_core/shader_cache.h | |||
| @@ -29,8 +29,8 @@ struct ShaderInfo { | |||
| 29 | }; | 29 | }; |
| 30 | 30 | ||
| 31 | class ShaderCache { | 31 | class ShaderCache { |
| 32 | static constexpr u64 PAGE_BITS = 14; | 32 | static constexpr u64 YUZU_PAGEBITS = 14; |
| 33 | static constexpr u64 PAGE_SIZE = u64(1) << PAGE_BITS; | 33 | static constexpr u64 YUZU_PAGESIZE = u64(1) << YUZU_PAGEBITS; |
| 34 | 34 | ||
| 35 | static constexpr size_t NUM_PROGRAMS = 6; | 35 | static constexpr size_t NUM_PROGRAMS = 6; |
| 36 | 36 | ||
diff --git a/src/video_core/shader_environment.cpp b/src/video_core/shader_environment.cpp index c4e923bbf..5f7625947 100644 --- a/src/video_core/shader_environment.cpp +++ b/src/video_core/shader_environment.cpp | |||
| @@ -39,7 +39,8 @@ static Shader::TextureType ConvertType(const Tegra::Texture::TICEntry& entry) { | |||
| 39 | return Shader::TextureType::Color1D; | 39 | return Shader::TextureType::Color1D; |
| 40 | case Tegra::Texture::TextureType::Texture2D: | 40 | case Tegra::Texture::TextureType::Texture2D: |
| 41 | case Tegra::Texture::TextureType::Texture2DNoMipmap: | 41 | case Tegra::Texture::TextureType::Texture2DNoMipmap: |
| 42 | return Shader::TextureType::Color2D; | 42 | return entry.normalized_coords ? Shader::TextureType::Color2D |
| 43 | : Shader::TextureType::Color2DRect; | ||
| 43 | case Tegra::Texture::TextureType::Texture3D: | 44 | case Tegra::Texture::TextureType::Texture3D: |
| 44 | return Shader::TextureType::Color3D; | 45 | return Shader::TextureType::Color3D; |
| 45 | case Tegra::Texture::TextureType::TextureCubemap: | 46 | case Tegra::Texture::TextureType::TextureCubemap: |
| @@ -53,7 +54,8 @@ static Shader::TextureType ConvertType(const Tegra::Texture::TICEntry& entry) { | |||
| 53 | case Tegra::Texture::TextureType::TextureCubeArray: | 54 | case Tegra::Texture::TextureType::TextureCubeArray: |
| 54 | return Shader::TextureType::ColorArrayCube; | 55 | return Shader::TextureType::ColorArrayCube; |
| 55 | default: | 56 | default: |
| 56 | throw Shader::NotImplementedException("Unknown texture type"); | 57 | UNIMPLEMENTED(); |
| 58 | return Shader::TextureType::Color2D; | ||
| 57 | } | 59 | } |
| 58 | } | 60 | } |
| 59 | 61 | ||
diff --git a/src/video_core/texture_cache/texture_cache.h b/src/video_core/texture_cache/texture_cache.h index cf3ca06a6..1dbe01bc0 100644 --- a/src/video_core/texture_cache/texture_cache.h +++ b/src/video_core/texture_cache/texture_cache.h | |||
| @@ -589,7 +589,7 @@ void TextureCache<P>::BlitImage(const Tegra::Engines::Fermi2D::Surface& dst, | |||
| 589 | template <class P> | 589 | template <class P> |
| 590 | typename P::ImageView* TextureCache<P>::TryFindFramebufferImageView(VAddr cpu_addr) { | 590 | typename P::ImageView* TextureCache<P>::TryFindFramebufferImageView(VAddr cpu_addr) { |
| 591 | // TODO: Properly implement this | 591 | // TODO: Properly implement this |
| 592 | const auto it = page_table.find(cpu_addr >> PAGE_BITS); | 592 | const auto it = page_table.find(cpu_addr >> YUZU_PAGEBITS); |
| 593 | if (it == page_table.end()) { | 593 | if (it == page_table.end()) { |
| 594 | return nullptr; | 594 | return nullptr; |
| 595 | } | 595 | } |
| @@ -1485,14 +1485,14 @@ void TextureCache<P>::UnregisterImage(ImageId image_id) { | |||
| 1485 | std::unordered_map<u64, std::vector<ImageId>, IdentityHash<u64>>& selected_page_table) { | 1485 | std::unordered_map<u64, std::vector<ImageId>, IdentityHash<u64>>& selected_page_table) { |
| 1486 | const auto page_it = selected_page_table.find(page); | 1486 | const auto page_it = selected_page_table.find(page); |
| 1487 | if (page_it == selected_page_table.end()) { | 1487 | if (page_it == selected_page_table.end()) { |
| 1488 | ASSERT_MSG(false, "Unregistering unregistered page=0x{:x}", page << PAGE_BITS); | 1488 | ASSERT_MSG(false, "Unregistering unregistered page=0x{:x}", page << YUZU_PAGEBITS); |
| 1489 | return; | 1489 | return; |
| 1490 | } | 1490 | } |
| 1491 | std::vector<ImageId>& image_ids = page_it->second; | 1491 | std::vector<ImageId>& image_ids = page_it->second; |
| 1492 | const auto vector_it = std::ranges::find(image_ids, image_id); | 1492 | const auto vector_it = std::ranges::find(image_ids, image_id); |
| 1493 | if (vector_it == image_ids.end()) { | 1493 | if (vector_it == image_ids.end()) { |
| 1494 | ASSERT_MSG(false, "Unregistering unregistered image in page=0x{:x}", | 1494 | ASSERT_MSG(false, "Unregistering unregistered image in page=0x{:x}", |
| 1495 | page << PAGE_BITS); | 1495 | page << YUZU_PAGEBITS); |
| 1496 | return; | 1496 | return; |
| 1497 | } | 1497 | } |
| 1498 | image_ids.erase(vector_it); | 1498 | image_ids.erase(vector_it); |
| @@ -1504,14 +1504,14 @@ void TextureCache<P>::UnregisterImage(ImageId image_id) { | |||
| 1504 | ForEachCPUPage(image.cpu_addr, image.guest_size_bytes, [this, map_id](u64 page) { | 1504 | ForEachCPUPage(image.cpu_addr, image.guest_size_bytes, [this, map_id](u64 page) { |
| 1505 | const auto page_it = page_table.find(page); | 1505 | const auto page_it = page_table.find(page); |
| 1506 | if (page_it == page_table.end()) { | 1506 | if (page_it == page_table.end()) { |
| 1507 | ASSERT_MSG(false, "Unregistering unregistered page=0x{:x}", page << PAGE_BITS); | 1507 | ASSERT_MSG(false, "Unregistering unregistered page=0x{:x}", page << YUZU_PAGEBITS); |
| 1508 | return; | 1508 | return; |
| 1509 | } | 1509 | } |
| 1510 | std::vector<ImageMapId>& image_map_ids = page_it->second; | 1510 | std::vector<ImageMapId>& image_map_ids = page_it->second; |
| 1511 | const auto vector_it = std::ranges::find(image_map_ids, map_id); | 1511 | const auto vector_it = std::ranges::find(image_map_ids, map_id); |
| 1512 | if (vector_it == image_map_ids.end()) { | 1512 | if (vector_it == image_map_ids.end()) { |
| 1513 | ASSERT_MSG(false, "Unregistering unregistered image in page=0x{:x}", | 1513 | ASSERT_MSG(false, "Unregistering unregistered image in page=0x{:x}", |
| 1514 | page << PAGE_BITS); | 1514 | page << YUZU_PAGEBITS); |
| 1515 | return; | 1515 | return; |
| 1516 | } | 1516 | } |
| 1517 | image_map_ids.erase(vector_it); | 1517 | image_map_ids.erase(vector_it); |
| @@ -1532,7 +1532,7 @@ void TextureCache<P>::UnregisterImage(ImageId image_id) { | |||
| 1532 | ForEachCPUPage(cpu_addr, size, [this, image_id](u64 page) { | 1532 | ForEachCPUPage(cpu_addr, size, [this, image_id](u64 page) { |
| 1533 | const auto page_it = page_table.find(page); | 1533 | const auto page_it = page_table.find(page); |
| 1534 | if (page_it == page_table.end()) { | 1534 | if (page_it == page_table.end()) { |
| 1535 | ASSERT_MSG(false, "Unregistering unregistered page=0x{:x}", page << PAGE_BITS); | 1535 | ASSERT_MSG(false, "Unregistering unregistered page=0x{:x}", page << YUZU_PAGEBITS); |
| 1536 | return; | 1536 | return; |
| 1537 | } | 1537 | } |
| 1538 | std::vector<ImageMapId>& image_map_ids = page_it->second; | 1538 | std::vector<ImageMapId>& image_map_ids = page_it->second; |
diff --git a/src/video_core/texture_cache/texture_cache_base.h b/src/video_core/texture_cache/texture_cache_base.h index e2f8f84c9..7e6c6cef2 100644 --- a/src/video_core/texture_cache/texture_cache_base.h +++ b/src/video_core/texture_cache/texture_cache_base.h | |||
| @@ -47,7 +47,7 @@ struct ImageViewInOut { | |||
| 47 | template <class P> | 47 | template <class P> |
| 48 | class TextureCache { | 48 | class TextureCache { |
| 49 | /// Address shift for caching images into a hash table | 49 | /// Address shift for caching images into a hash table |
| 50 | static constexpr u64 PAGE_BITS = 20; | 50 | static constexpr u64 YUZU_PAGEBITS = 20; |
| 51 | 51 | ||
| 52 | /// Enables debugging features to the texture cache | 52 | /// Enables debugging features to the texture cache |
| 53 | static constexpr bool ENABLE_VALIDATION = P::ENABLE_VALIDATION; | 53 | static constexpr bool ENABLE_VALIDATION = P::ENABLE_VALIDATION; |
| @@ -178,8 +178,8 @@ private: | |||
| 178 | template <typename Func> | 178 | template <typename Func> |
| 179 | static void ForEachCPUPage(VAddr addr, size_t size, Func&& func) { | 179 | static void ForEachCPUPage(VAddr addr, size_t size, Func&& func) { |
| 180 | static constexpr bool RETURNS_BOOL = std::is_same_v<std::invoke_result<Func, u64>, bool>; | 180 | static constexpr bool RETURNS_BOOL = std::is_same_v<std::invoke_result<Func, u64>, bool>; |
| 181 | const u64 page_end = (addr + size - 1) >> PAGE_BITS; | 181 | const u64 page_end = (addr + size - 1) >> YUZU_PAGEBITS; |
| 182 | for (u64 page = addr >> PAGE_BITS; page <= page_end; ++page) { | 182 | for (u64 page = addr >> YUZU_PAGEBITS; page <= page_end; ++page) { |
| 183 | if constexpr (RETURNS_BOOL) { | 183 | if constexpr (RETURNS_BOOL) { |
| 184 | if (func(page)) { | 184 | if (func(page)) { |
| 185 | break; | 185 | break; |
| @@ -193,8 +193,8 @@ private: | |||
| 193 | template <typename Func> | 193 | template <typename Func> |
| 194 | static void ForEachGPUPage(GPUVAddr addr, size_t size, Func&& func) { | 194 | static void ForEachGPUPage(GPUVAddr addr, size_t size, Func&& func) { |
| 195 | static constexpr bool RETURNS_BOOL = std::is_same_v<std::invoke_result<Func, u64>, bool>; | 195 | static constexpr bool RETURNS_BOOL = std::is_same_v<std::invoke_result<Func, u64>, bool>; |
| 196 | const u64 page_end = (addr + size - 1) >> PAGE_BITS; | 196 | const u64 page_end = (addr + size - 1) >> YUZU_PAGEBITS; |
| 197 | for (u64 page = addr >> PAGE_BITS; page <= page_end; ++page) { | 197 | for (u64 page = addr >> YUZU_PAGEBITS; page <= page_end; ++page) { |
| 198 | if constexpr (RETURNS_BOOL) { | 198 | if constexpr (RETURNS_BOOL) { |
| 199 | if (func(page)) { | 199 | if (func(page)) { |
| 200 | break; | 200 | break; |
diff --git a/src/video_core/textures/decoders.cpp b/src/video_core/textures/decoders.cpp index 9b6b8527b..913f8ebcb 100644 --- a/src/video_core/textures/decoders.cpp +++ b/src/video_core/textures/decoders.cpp | |||
| @@ -15,6 +15,24 @@ | |||
| 15 | 15 | ||
| 16 | namespace Tegra::Texture { | 16 | namespace Tegra::Texture { |
| 17 | namespace { | 17 | namespace { |
| 18 | template <u32 mask> | ||
| 19 | constexpr u32 pdep(u32 value) { | ||
| 20 | u32 result = 0; | ||
| 21 | u32 m = mask; | ||
| 22 | for (u32 bit = 1; m; bit += bit) { | ||
| 23 | if (value & bit) | ||
| 24 | result |= m & -m; | ||
| 25 | m &= m - 1; | ||
| 26 | } | ||
| 27 | return result; | ||
| 28 | } | ||
| 29 | |||
| 30 | template <u32 mask, u32 incr_amount> | ||
| 31 | void incrpdep(u32& value) { | ||
| 32 | constexpr u32 swizzled_incr = pdep<mask>(incr_amount); | ||
| 33 | value = ((value | ~mask) + swizzled_incr) & mask; | ||
| 34 | } | ||
| 35 | |||
| 18 | template <bool TO_LINEAR, u32 BYTES_PER_PIXEL> | 36 | template <bool TO_LINEAR, u32 BYTES_PER_PIXEL> |
| 19 | void SwizzleImpl(std::span<u8> output, std::span<const u8> input, u32 width, u32 height, u32 depth, | 37 | void SwizzleImpl(std::span<u8> output, std::span<const u8> input, u32 width, u32 height, u32 depth, |
| 20 | u32 block_height, u32 block_depth, u32 stride_alignment) { | 38 | u32 block_height, u32 block_depth, u32 stride_alignment) { |
| @@ -44,18 +62,20 @@ void SwizzleImpl(std::span<u8> output, std::span<const u8> input, u32 width, u32 | |||
| 44 | ((z & block_depth_mask) << (GOB_SIZE_SHIFT + block_height)); | 62 | ((z & block_depth_mask) << (GOB_SIZE_SHIFT + block_height)); |
| 45 | for (u32 line = 0; line < height; ++line) { | 63 | for (u32 line = 0; line < height; ++line) { |
| 46 | const u32 y = line + origin_y; | 64 | const u32 y = line + origin_y; |
| 47 | const auto& table = SWIZZLE_TABLE[y % GOB_SIZE_Y]; | 65 | const u32 swizzled_y = pdep<SWIZZLE_Y_BITS>(y); |
| 48 | 66 | ||
| 49 | const u32 block_y = y >> GOB_SIZE_Y_SHIFT; | 67 | const u32 block_y = y >> GOB_SIZE_Y_SHIFT; |
| 50 | const u32 offset_y = (block_y >> block_height) * block_size + | 68 | const u32 offset_y = (block_y >> block_height) * block_size + |
| 51 | ((block_y & block_height_mask) << GOB_SIZE_SHIFT); | 69 | ((block_y & block_height_mask) << GOB_SIZE_SHIFT); |
| 52 | 70 | ||
| 53 | for (u32 column = 0; column < width; ++column) { | 71 | u32 swizzled_x = pdep<SWIZZLE_X_BITS>(origin_x * BYTES_PER_PIXEL); |
| 72 | for (u32 column = 0; column < width; | ||
| 73 | ++column, incrpdep<SWIZZLE_X_BITS, BYTES_PER_PIXEL>(swizzled_x)) { | ||
| 54 | const u32 x = (column + origin_x) * BYTES_PER_PIXEL; | 74 | const u32 x = (column + origin_x) * BYTES_PER_PIXEL; |
| 55 | const u32 offset_x = (x >> GOB_SIZE_X_SHIFT) << x_shift; | 75 | const u32 offset_x = (x >> GOB_SIZE_X_SHIFT) << x_shift; |
| 56 | 76 | ||
| 57 | const u32 base_swizzled_offset = offset_z + offset_y + offset_x; | 77 | const u32 base_swizzled_offset = offset_z + offset_y + offset_x; |
| 58 | const u32 swizzled_offset = base_swizzled_offset + table[x % GOB_SIZE_X]; | 78 | const u32 swizzled_offset = base_swizzled_offset + (swizzled_x | swizzled_y); |
| 59 | 79 | ||
| 60 | const u32 unswizzled_offset = | 80 | const u32 unswizzled_offset = |
| 61 | slice * pitch * height + line * pitch + column * BYTES_PER_PIXEL; | 81 | slice * pitch * height + line * pitch + column * BYTES_PER_PIXEL; |
| @@ -103,12 +123,15 @@ void SwizzleSubrect(u32 subrect_width, u32 subrect_height, u32 source_pitch, u32 | |||
| 103 | const u32 gob_address_y = | 123 | const u32 gob_address_y = |
| 104 | (dst_y / (GOB_SIZE_Y * block_height)) * GOB_SIZE * block_height * image_width_in_gobs + | 124 | (dst_y / (GOB_SIZE_Y * block_height)) * GOB_SIZE * block_height * image_width_in_gobs + |
| 105 | ((dst_y % (GOB_SIZE_Y * block_height)) / GOB_SIZE_Y) * GOB_SIZE; | 125 | ((dst_y % (GOB_SIZE_Y * block_height)) / GOB_SIZE_Y) * GOB_SIZE; |
| 106 | const auto& table = SWIZZLE_TABLE[dst_y % GOB_SIZE_Y]; | 126 | |
| 107 | for (u32 x = 0; x < subrect_width; ++x) { | 127 | const u32 swizzled_y = pdep<SWIZZLE_Y_BITS>(dst_y); |
| 128 | u32 swizzled_x = pdep<SWIZZLE_X_BITS>(offset_x * BYTES_PER_PIXEL); | ||
| 129 | for (u32 x = 0; x < subrect_width; | ||
| 130 | ++x, incrpdep<SWIZZLE_X_BITS, BYTES_PER_PIXEL>(swizzled_x)) { | ||
| 108 | const u32 dst_x = x + offset_x; | 131 | const u32 dst_x = x + offset_x; |
| 109 | const u32 gob_address = | 132 | const u32 gob_address = |
| 110 | gob_address_y + (dst_x * BYTES_PER_PIXEL / GOB_SIZE_X) * GOB_SIZE * block_height; | 133 | gob_address_y + (dst_x * BYTES_PER_PIXEL / GOB_SIZE_X) * GOB_SIZE * block_height; |
| 111 | const u32 swizzled_offset = gob_address + table[(dst_x * BYTES_PER_PIXEL) % GOB_SIZE_X]; | 134 | const u32 swizzled_offset = gob_address + (swizzled_x | swizzled_y); |
| 112 | const u32 unswizzled_offset = line * source_pitch + x * BYTES_PER_PIXEL; | 135 | const u32 unswizzled_offset = line * source_pitch + x * BYTES_PER_PIXEL; |
| 113 | 136 | ||
| 114 | const u8* const source_line = unswizzled_data + unswizzled_offset; | 137 | const u8* const source_line = unswizzled_data + unswizzled_offset; |
| @@ -130,16 +153,19 @@ void UnswizzleSubrect(u32 line_length_in, u32 line_count, u32 pitch, u32 width, | |||
| 130 | 153 | ||
| 131 | for (u32 line = 0; line < line_count; ++line) { | 154 | for (u32 line = 0; line < line_count; ++line) { |
| 132 | const u32 src_y = line + origin_y; | 155 | const u32 src_y = line + origin_y; |
| 133 | const auto& table = SWIZZLE_TABLE[src_y % GOB_SIZE_Y]; | 156 | const u32 swizzled_y = pdep<SWIZZLE_Y_BITS>(src_y); |
| 134 | 157 | ||
| 135 | const u32 block_y = src_y >> GOB_SIZE_Y_SHIFT; | 158 | const u32 block_y = src_y >> GOB_SIZE_Y_SHIFT; |
| 136 | const u32 src_offset_y = (block_y >> block_height) * block_size + | 159 | const u32 src_offset_y = (block_y >> block_height) * block_size + |
| 137 | ((block_y & block_height_mask) << GOB_SIZE_SHIFT); | 160 | ((block_y & block_height_mask) << GOB_SIZE_SHIFT); |
| 138 | for (u32 column = 0; column < line_length_in; ++column) { | 161 | |
| 162 | u32 swizzled_x = pdep<SWIZZLE_X_BITS>(origin_x * BYTES_PER_PIXEL); | ||
| 163 | for (u32 column = 0; column < line_length_in; | ||
| 164 | ++column, incrpdep<SWIZZLE_X_BITS, BYTES_PER_PIXEL>(swizzled_x)) { | ||
| 139 | const u32 src_x = (column + origin_x) * BYTES_PER_PIXEL; | 165 | const u32 src_x = (column + origin_x) * BYTES_PER_PIXEL; |
| 140 | const u32 src_offset_x = (src_x >> GOB_SIZE_X_SHIFT) << x_shift; | 166 | const u32 src_offset_x = (src_x >> GOB_SIZE_X_SHIFT) << x_shift; |
| 141 | 167 | ||
| 142 | const u32 swizzled_offset = src_offset_y + src_offset_x + table[src_x % GOB_SIZE_X]; | 168 | const u32 swizzled_offset = src_offset_y + src_offset_x + (swizzled_x | swizzled_y); |
| 143 | const u32 unswizzled_offset = line * pitch + column * BYTES_PER_PIXEL; | 169 | const u32 unswizzled_offset = line * pitch + column * BYTES_PER_PIXEL; |
| 144 | 170 | ||
| 145 | std::memcpy(output + unswizzled_offset, input + swizzled_offset, BYTES_PER_PIXEL); | 171 | std::memcpy(output + unswizzled_offset, input + swizzled_offset, BYTES_PER_PIXEL); |
| @@ -162,13 +188,15 @@ void SwizzleSliceToVoxel(u32 line_length_in, u32 line_count, u32 pitch, u32 widt | |||
| 162 | const u32 x_shift = static_cast<u32>(GOB_SIZE_SHIFT) + block_height + block_depth; | 188 | const u32 x_shift = static_cast<u32>(GOB_SIZE_SHIFT) + block_height + block_depth; |
| 163 | 189 | ||
| 164 | for (u32 line = 0; line < line_count; ++line) { | 190 | for (u32 line = 0; line < line_count; ++line) { |
| 165 | const auto& table = SWIZZLE_TABLE[line % GOB_SIZE_Y]; | 191 | const u32 swizzled_y = pdep<SWIZZLE_Y_BITS>(line); |
| 166 | const u32 block_y = line / GOB_SIZE_Y; | 192 | const u32 block_y = line / GOB_SIZE_Y; |
| 167 | const u32 dst_offset_y = | 193 | const u32 dst_offset_y = |
| 168 | (block_y >> block_height) * block_size + (block_y & block_height_mask) * GOB_SIZE; | 194 | (block_y >> block_height) * block_size + (block_y & block_height_mask) * GOB_SIZE; |
| 169 | for (u32 x = 0; x < line_length_in; ++x) { | 195 | |
| 196 | u32 swizzled_x = 0; | ||
| 197 | for (u32 x = 0; x < line_length_in; ++x, incrpdep<SWIZZLE_X_BITS, 1>(swizzled_x)) { | ||
| 170 | const u32 dst_offset = | 198 | const u32 dst_offset = |
| 171 | ((x / GOB_SIZE_X) << x_shift) + dst_offset_y + table[x % GOB_SIZE_X]; | 199 | ((x / GOB_SIZE_X) << x_shift) + dst_offset_y + (swizzled_x | swizzled_y); |
| 172 | const u32 src_offset = x * BYTES_PER_PIXEL + line * pitch; | 200 | const u32 src_offset = x * BYTES_PER_PIXEL + line * pitch; |
| 173 | std::memcpy(output + dst_offset, input + src_offset, BYTES_PER_PIXEL); | 201 | std::memcpy(output + dst_offset, input + src_offset, BYTES_PER_PIXEL); |
| 174 | } | 202 | } |
| @@ -267,11 +295,13 @@ void SwizzleKepler(const u32 width, const u32 height, const u32 dst_x, const u32 | |||
| 267 | const std::size_t gob_address_y = | 295 | const std::size_t gob_address_y = |
| 268 | (y / (GOB_SIZE_Y * block_height)) * GOB_SIZE * block_height * image_width_in_gobs + | 296 | (y / (GOB_SIZE_Y * block_height)) * GOB_SIZE * block_height * image_width_in_gobs + |
| 269 | ((y % (GOB_SIZE_Y * block_height)) / GOB_SIZE_Y) * GOB_SIZE; | 297 | ((y % (GOB_SIZE_Y * block_height)) / GOB_SIZE_Y) * GOB_SIZE; |
| 270 | const auto& table = SWIZZLE_TABLE[y % GOB_SIZE_Y]; | 298 | const u32 swizzled_y = pdep<SWIZZLE_Y_BITS>(static_cast<u32>(y)); |
| 271 | for (std::size_t x = dst_x; x < width && count < copy_size; ++x) { | 299 | u32 swizzled_x = pdep<SWIZZLE_X_BITS>(dst_x); |
| 300 | for (std::size_t x = dst_x; x < width && count < copy_size; | ||
| 301 | ++x, incrpdep<SWIZZLE_X_BITS, 1>(swizzled_x)) { | ||
| 272 | const std::size_t gob_address = | 302 | const std::size_t gob_address = |
| 273 | gob_address_y + (x / GOB_SIZE_X) * GOB_SIZE * block_height; | 303 | gob_address_y + (x / GOB_SIZE_X) * GOB_SIZE * block_height; |
| 274 | const std::size_t swizzled_offset = gob_address + table[x % GOB_SIZE_X]; | 304 | const std::size_t swizzled_offset = gob_address + (swizzled_x | swizzled_y); |
| 275 | const u8* source_line = source_data + count; | 305 | const u8* source_line = source_data + count; |
| 276 | u8* dest_addr = swizzle_data + swizzled_offset; | 306 | u8* dest_addr = swizzle_data + swizzled_offset; |
| 277 | count++; | 307 | count++; |
diff --git a/src/video_core/textures/decoders.h b/src/video_core/textures/decoders.h index 59dfd1621..31a11708f 100644 --- a/src/video_core/textures/decoders.h +++ b/src/video_core/textures/decoders.h | |||
| @@ -20,6 +20,9 @@ constexpr u32 GOB_SIZE_Y_SHIFT = 3; | |||
| 20 | constexpr u32 GOB_SIZE_Z_SHIFT = 0; | 20 | constexpr u32 GOB_SIZE_Z_SHIFT = 0; |
| 21 | constexpr u32 GOB_SIZE_SHIFT = GOB_SIZE_X_SHIFT + GOB_SIZE_Y_SHIFT + GOB_SIZE_Z_SHIFT; | 21 | constexpr u32 GOB_SIZE_SHIFT = GOB_SIZE_X_SHIFT + GOB_SIZE_Y_SHIFT + GOB_SIZE_Z_SHIFT; |
| 22 | 22 | ||
| 23 | constexpr u32 SWIZZLE_X_BITS = 0b100101111; | ||
| 24 | constexpr u32 SWIZZLE_Y_BITS = 0b011010000; | ||
| 25 | |||
| 23 | using SwizzleTable = std::array<std::array<u32, GOB_SIZE_X>, GOB_SIZE_Y>; | 26 | using SwizzleTable = std::array<std::array<u32, GOB_SIZE_X>, GOB_SIZE_Y>; |
| 24 | 27 | ||
| 25 | /** | 28 | /** |
diff --git a/src/video_core/vulkan_common/vulkan_device.cpp b/src/video_core/vulkan_common/vulkan_device.cpp index 743ac09f6..ddecfca13 100644 --- a/src/video_core/vulkan_common/vulkan_device.cpp +++ b/src/video_core/vulkan_common/vulkan_device.cpp | |||
| @@ -50,6 +50,21 @@ constexpr std::array R4G4_UNORM_PACK8{ | |||
| 50 | VK_FORMAT_UNDEFINED, | 50 | VK_FORMAT_UNDEFINED, |
| 51 | }; | 51 | }; |
| 52 | 52 | ||
| 53 | constexpr std::array R16G16B16_SFLOAT{ | ||
| 54 | VK_FORMAT_R16G16B16A16_SFLOAT, | ||
| 55 | VK_FORMAT_UNDEFINED, | ||
| 56 | }; | ||
| 57 | |||
| 58 | constexpr std::array R16G16B16_SSCALED{ | ||
| 59 | VK_FORMAT_R16G16B16A16_SSCALED, | ||
| 60 | VK_FORMAT_UNDEFINED, | ||
| 61 | }; | ||
| 62 | |||
| 63 | constexpr std::array R8G8B8_SSCALED{ | ||
| 64 | VK_FORMAT_R8G8B8A8_SSCALED, | ||
| 65 | VK_FORMAT_UNDEFINED, | ||
| 66 | }; | ||
| 67 | |||
| 53 | } // namespace Alternatives | 68 | } // namespace Alternatives |
| 54 | 69 | ||
| 55 | enum class NvidiaArchitecture { | 70 | enum class NvidiaArchitecture { |
| @@ -102,6 +117,12 @@ constexpr const VkFormat* GetFormatAlternatives(VkFormat format) { | |||
| 102 | return Alternatives::B5G6R5_UNORM_PACK16.data(); | 117 | return Alternatives::B5G6R5_UNORM_PACK16.data(); |
| 103 | case VK_FORMAT_R4G4_UNORM_PACK8: | 118 | case VK_FORMAT_R4G4_UNORM_PACK8: |
| 104 | return Alternatives::R4G4_UNORM_PACK8.data(); | 119 | return Alternatives::R4G4_UNORM_PACK8.data(); |
| 120 | case VK_FORMAT_R16G16B16_SFLOAT: | ||
| 121 | return Alternatives::R16G16B16_SFLOAT.data(); | ||
| 122 | case VK_FORMAT_R16G16B16_SSCALED: | ||
| 123 | return Alternatives::R16G16B16_SSCALED.data(); | ||
| 124 | case VK_FORMAT_R8G8B8_SSCALED: | ||
| 125 | return Alternatives::R8G8B8_SSCALED.data(); | ||
| 105 | default: | 126 | default: |
| 106 | return nullptr; | 127 | return nullptr; |
| 107 | } | 128 | } |
| @@ -122,109 +143,142 @@ VkFormatFeatureFlags GetFormatFeatures(VkFormatProperties properties, FormatType | |||
| 122 | 143 | ||
| 123 | std::unordered_map<VkFormat, VkFormatProperties> GetFormatProperties(vk::PhysicalDevice physical) { | 144 | std::unordered_map<VkFormat, VkFormatProperties> GetFormatProperties(vk::PhysicalDevice physical) { |
| 124 | static constexpr std::array formats{ | 145 | static constexpr std::array formats{ |
| 125 | VK_FORMAT_A8B8G8R8_UNORM_PACK32, | 146 | VK_FORMAT_A1R5G5B5_UNORM_PACK16, |
| 126 | VK_FORMAT_A8B8G8R8_UINT_PACK32, | 147 | VK_FORMAT_A2B10G10R10_SINT_PACK32, |
| 127 | VK_FORMAT_A8B8G8R8_SNORM_PACK32, | 148 | VK_FORMAT_A2B10G10R10_SNORM_PACK32, |
| 149 | VK_FORMAT_A2B10G10R10_SSCALED_PACK32, | ||
| 150 | VK_FORMAT_A2B10G10R10_UINT_PACK32, | ||
| 151 | VK_FORMAT_A2B10G10R10_UNORM_PACK32, | ||
| 152 | VK_FORMAT_A2B10G10R10_USCALED_PACK32, | ||
| 128 | VK_FORMAT_A8B8G8R8_SINT_PACK32, | 153 | VK_FORMAT_A8B8G8R8_SINT_PACK32, |
| 154 | VK_FORMAT_A8B8G8R8_SNORM_PACK32, | ||
| 129 | VK_FORMAT_A8B8G8R8_SRGB_PACK32, | 155 | VK_FORMAT_A8B8G8R8_SRGB_PACK32, |
| 130 | VK_FORMAT_R5G6B5_UNORM_PACK16, | 156 | VK_FORMAT_A8B8G8R8_UINT_PACK32, |
| 131 | VK_FORMAT_B5G6R5_UNORM_PACK16, | 157 | VK_FORMAT_A8B8G8R8_UNORM_PACK32, |
| 132 | VK_FORMAT_R5G5B5A1_UNORM_PACK16, | 158 | VK_FORMAT_ASTC_10x10_SRGB_BLOCK, |
| 159 | VK_FORMAT_ASTC_10x10_UNORM_BLOCK, | ||
| 160 | VK_FORMAT_ASTC_10x5_SRGB_BLOCK, | ||
| 161 | VK_FORMAT_ASTC_10x5_UNORM_BLOCK, | ||
| 162 | VK_FORMAT_ASTC_10x6_SRGB_BLOCK, | ||
| 163 | VK_FORMAT_ASTC_10x6_UNORM_BLOCK, | ||
| 164 | VK_FORMAT_ASTC_10x8_SRGB_BLOCK, | ||
| 165 | VK_FORMAT_ASTC_10x8_UNORM_BLOCK, | ||
| 166 | VK_FORMAT_ASTC_12x10_SRGB_BLOCK, | ||
| 167 | VK_FORMAT_ASTC_12x10_UNORM_BLOCK, | ||
| 168 | VK_FORMAT_ASTC_12x12_SRGB_BLOCK, | ||
| 169 | VK_FORMAT_ASTC_12x12_UNORM_BLOCK, | ||
| 170 | VK_FORMAT_ASTC_4x4_SRGB_BLOCK, | ||
| 171 | VK_FORMAT_ASTC_4x4_UNORM_BLOCK, | ||
| 172 | VK_FORMAT_ASTC_5x4_SRGB_BLOCK, | ||
| 173 | VK_FORMAT_ASTC_5x4_UNORM_BLOCK, | ||
| 174 | VK_FORMAT_ASTC_5x5_SRGB_BLOCK, | ||
| 175 | VK_FORMAT_ASTC_5x5_UNORM_BLOCK, | ||
| 176 | VK_FORMAT_ASTC_6x5_SRGB_BLOCK, | ||
| 177 | VK_FORMAT_ASTC_6x5_UNORM_BLOCK, | ||
| 178 | VK_FORMAT_ASTC_6x6_SRGB_BLOCK, | ||
| 179 | VK_FORMAT_ASTC_6x6_UNORM_BLOCK, | ||
| 180 | VK_FORMAT_ASTC_8x5_SRGB_BLOCK, | ||
| 181 | VK_FORMAT_ASTC_8x5_UNORM_BLOCK, | ||
| 182 | VK_FORMAT_ASTC_8x6_SRGB_BLOCK, | ||
| 183 | VK_FORMAT_ASTC_8x6_UNORM_BLOCK, | ||
| 184 | VK_FORMAT_ASTC_8x8_SRGB_BLOCK, | ||
| 185 | VK_FORMAT_ASTC_8x8_UNORM_BLOCK, | ||
| 186 | VK_FORMAT_B10G11R11_UFLOAT_PACK32, | ||
| 187 | VK_FORMAT_B4G4R4A4_UNORM_PACK16, | ||
| 133 | VK_FORMAT_B5G5R5A1_UNORM_PACK16, | 188 | VK_FORMAT_B5G5R5A1_UNORM_PACK16, |
| 134 | VK_FORMAT_A2B10G10R10_UNORM_PACK32, | 189 | VK_FORMAT_B5G6R5_UNORM_PACK16, |
| 135 | VK_FORMAT_A2B10G10R10_UINT_PACK32, | 190 | VK_FORMAT_B8G8R8A8_SRGB, |
| 136 | VK_FORMAT_A1R5G5B5_UNORM_PACK16, | 191 | VK_FORMAT_B8G8R8A8_UNORM, |
| 137 | VK_FORMAT_R32G32B32A32_SFLOAT, | 192 | VK_FORMAT_BC1_RGBA_SRGB_BLOCK, |
| 138 | VK_FORMAT_R32G32B32A32_SINT, | 193 | VK_FORMAT_BC1_RGBA_UNORM_BLOCK, |
| 139 | VK_FORMAT_R32G32B32A32_UINT, | 194 | VK_FORMAT_BC2_SRGB_BLOCK, |
| 140 | VK_FORMAT_R32G32_SFLOAT, | 195 | VK_FORMAT_BC2_UNORM_BLOCK, |
| 141 | VK_FORMAT_R32G32_SINT, | 196 | VK_FORMAT_BC3_SRGB_BLOCK, |
| 142 | VK_FORMAT_R32G32_UINT, | 197 | VK_FORMAT_BC3_UNORM_BLOCK, |
| 198 | VK_FORMAT_BC4_SNORM_BLOCK, | ||
| 199 | VK_FORMAT_BC4_UNORM_BLOCK, | ||
| 200 | VK_FORMAT_BC5_SNORM_BLOCK, | ||
| 201 | VK_FORMAT_BC5_UNORM_BLOCK, | ||
| 202 | VK_FORMAT_BC6H_SFLOAT_BLOCK, | ||
| 203 | VK_FORMAT_BC6H_UFLOAT_BLOCK, | ||
| 204 | VK_FORMAT_BC7_SRGB_BLOCK, | ||
| 205 | VK_FORMAT_BC7_UNORM_BLOCK, | ||
| 206 | VK_FORMAT_D16_UNORM, | ||
| 207 | VK_FORMAT_D16_UNORM_S8_UINT, | ||
| 208 | VK_FORMAT_D24_UNORM_S8_UINT, | ||
| 209 | VK_FORMAT_D32_SFLOAT, | ||
| 210 | VK_FORMAT_D32_SFLOAT_S8_UINT, | ||
| 211 | VK_FORMAT_E5B9G9R9_UFLOAT_PACK32, | ||
| 212 | VK_FORMAT_R16G16B16A16_SFLOAT, | ||
| 143 | VK_FORMAT_R16G16B16A16_SINT, | 213 | VK_FORMAT_R16G16B16A16_SINT, |
| 144 | VK_FORMAT_R16G16B16A16_UINT, | ||
| 145 | VK_FORMAT_R16G16B16A16_SNORM, | 214 | VK_FORMAT_R16G16B16A16_SNORM, |
| 215 | VK_FORMAT_R16G16B16A16_SSCALED, | ||
| 216 | VK_FORMAT_R16G16B16A16_UINT, | ||
| 146 | VK_FORMAT_R16G16B16A16_UNORM, | 217 | VK_FORMAT_R16G16B16A16_UNORM, |
| 147 | VK_FORMAT_R16G16_UNORM, | 218 | VK_FORMAT_R16G16B16A16_USCALED, |
| 148 | VK_FORMAT_R16G16_SNORM, | 219 | VK_FORMAT_R16G16B16_SFLOAT, |
| 220 | VK_FORMAT_R16G16B16_SINT, | ||
| 221 | VK_FORMAT_R16G16B16_SNORM, | ||
| 222 | VK_FORMAT_R16G16B16_SSCALED, | ||
| 223 | VK_FORMAT_R16G16B16_UINT, | ||
| 224 | VK_FORMAT_R16G16B16_UNORM, | ||
| 225 | VK_FORMAT_R16G16B16_USCALED, | ||
| 149 | VK_FORMAT_R16G16_SFLOAT, | 226 | VK_FORMAT_R16G16_SFLOAT, |
| 150 | VK_FORMAT_R16G16_UINT, | ||
| 151 | VK_FORMAT_R16G16_SINT, | 227 | VK_FORMAT_R16G16_SINT, |
| 152 | VK_FORMAT_R16_UNORM, | 228 | VK_FORMAT_R16G16_SNORM, |
| 229 | VK_FORMAT_R16G16_SSCALED, | ||
| 230 | VK_FORMAT_R16G16_UINT, | ||
| 231 | VK_FORMAT_R16G16_UNORM, | ||
| 232 | VK_FORMAT_R16G16_USCALED, | ||
| 233 | VK_FORMAT_R16_SFLOAT, | ||
| 234 | VK_FORMAT_R16_SINT, | ||
| 153 | VK_FORMAT_R16_SNORM, | 235 | VK_FORMAT_R16_SNORM, |
| 236 | VK_FORMAT_R16_SSCALED, | ||
| 154 | VK_FORMAT_R16_UINT, | 237 | VK_FORMAT_R16_UINT, |
| 238 | VK_FORMAT_R16_UNORM, | ||
| 239 | VK_FORMAT_R16_USCALED, | ||
| 240 | VK_FORMAT_R32G32B32A32_SFLOAT, | ||
| 241 | VK_FORMAT_R32G32B32A32_SINT, | ||
| 242 | VK_FORMAT_R32G32B32A32_UINT, | ||
| 243 | VK_FORMAT_R32G32B32_SFLOAT, | ||
| 244 | VK_FORMAT_R32G32B32_SINT, | ||
| 245 | VK_FORMAT_R32G32B32_UINT, | ||
| 246 | VK_FORMAT_R32G32_SFLOAT, | ||
| 247 | VK_FORMAT_R32G32_SINT, | ||
| 248 | VK_FORMAT_R32G32_UINT, | ||
| 249 | VK_FORMAT_R32_SFLOAT, | ||
| 250 | VK_FORMAT_R32_SINT, | ||
| 251 | VK_FORMAT_R32_UINT, | ||
| 252 | VK_FORMAT_R4G4B4A4_UNORM_PACK16, | ||
| 253 | VK_FORMAT_R4G4_UNORM_PACK8, | ||
| 254 | VK_FORMAT_R5G5B5A1_UNORM_PACK16, | ||
| 255 | VK_FORMAT_R5G6B5_UNORM_PACK16, | ||
| 256 | VK_FORMAT_R8G8B8A8_SINT, | ||
| 257 | VK_FORMAT_R8G8B8A8_SNORM, | ||
| 155 | VK_FORMAT_R8G8B8A8_SRGB, | 258 | VK_FORMAT_R8G8B8A8_SRGB, |
| 156 | VK_FORMAT_R8G8_UNORM, | 259 | VK_FORMAT_R8G8B8A8_SSCALED, |
| 157 | VK_FORMAT_R8G8_SNORM, | 260 | VK_FORMAT_R8G8B8A8_UINT, |
| 261 | VK_FORMAT_R8G8B8A8_UNORM, | ||
| 262 | VK_FORMAT_R8G8B8A8_USCALED, | ||
| 263 | VK_FORMAT_R8G8B8_SINT, | ||
| 264 | VK_FORMAT_R8G8B8_SNORM, | ||
| 265 | VK_FORMAT_R8G8B8_SSCALED, | ||
| 266 | VK_FORMAT_R8G8B8_UINT, | ||
| 267 | VK_FORMAT_R8G8B8_UNORM, | ||
| 268 | VK_FORMAT_R8G8B8_USCALED, | ||
| 158 | VK_FORMAT_R8G8_SINT, | 269 | VK_FORMAT_R8G8_SINT, |
| 270 | VK_FORMAT_R8G8_SNORM, | ||
| 271 | VK_FORMAT_R8G8_SSCALED, | ||
| 159 | VK_FORMAT_R8G8_UINT, | 272 | VK_FORMAT_R8G8_UINT, |
| 160 | VK_FORMAT_R8_UNORM, | 273 | VK_FORMAT_R8G8_UNORM, |
| 161 | VK_FORMAT_R8_SNORM, | 274 | VK_FORMAT_R8G8_USCALED, |
| 162 | VK_FORMAT_R8_SINT, | 275 | VK_FORMAT_R8_SINT, |
| 276 | VK_FORMAT_R8_SNORM, | ||
| 277 | VK_FORMAT_R8_SSCALED, | ||
| 163 | VK_FORMAT_R8_UINT, | 278 | VK_FORMAT_R8_UINT, |
| 164 | VK_FORMAT_B10G11R11_UFLOAT_PACK32, | 279 | VK_FORMAT_R8_UNORM, |
| 165 | VK_FORMAT_R32_SFLOAT, | 280 | VK_FORMAT_R8_USCALED, |
| 166 | VK_FORMAT_R32_UINT, | ||
| 167 | VK_FORMAT_R32_SINT, | ||
| 168 | VK_FORMAT_R16_SFLOAT, | ||
| 169 | VK_FORMAT_R16G16B16A16_SFLOAT, | ||
| 170 | VK_FORMAT_B8G8R8A8_UNORM, | ||
| 171 | VK_FORMAT_B8G8R8A8_SRGB, | ||
| 172 | VK_FORMAT_R4G4_UNORM_PACK8, | ||
| 173 | VK_FORMAT_R4G4B4A4_UNORM_PACK16, | ||
| 174 | VK_FORMAT_B4G4R4A4_UNORM_PACK16, | ||
| 175 | VK_FORMAT_D32_SFLOAT, | ||
| 176 | VK_FORMAT_D16_UNORM, | ||
| 177 | VK_FORMAT_S8_UINT, | 281 | VK_FORMAT_S8_UINT, |
| 178 | VK_FORMAT_D16_UNORM_S8_UINT, | ||
| 179 | VK_FORMAT_D24_UNORM_S8_UINT, | ||
| 180 | VK_FORMAT_D32_SFLOAT_S8_UINT, | ||
| 181 | VK_FORMAT_BC1_RGBA_UNORM_BLOCK, | ||
| 182 | VK_FORMAT_BC2_UNORM_BLOCK, | ||
| 183 | VK_FORMAT_BC3_UNORM_BLOCK, | ||
| 184 | VK_FORMAT_BC4_UNORM_BLOCK, | ||
| 185 | VK_FORMAT_BC4_SNORM_BLOCK, | ||
| 186 | VK_FORMAT_BC5_UNORM_BLOCK, | ||
| 187 | VK_FORMAT_BC5_SNORM_BLOCK, | ||
| 188 | VK_FORMAT_BC7_UNORM_BLOCK, | ||
| 189 | VK_FORMAT_BC6H_UFLOAT_BLOCK, | ||
| 190 | VK_FORMAT_BC6H_SFLOAT_BLOCK, | ||
| 191 | VK_FORMAT_BC1_RGBA_SRGB_BLOCK, | ||
| 192 | VK_FORMAT_BC2_SRGB_BLOCK, | ||
| 193 | VK_FORMAT_BC3_SRGB_BLOCK, | ||
| 194 | VK_FORMAT_BC7_SRGB_BLOCK, | ||
| 195 | VK_FORMAT_ASTC_4x4_UNORM_BLOCK, | ||
| 196 | VK_FORMAT_ASTC_4x4_SRGB_BLOCK, | ||
| 197 | VK_FORMAT_ASTC_5x4_UNORM_BLOCK, | ||
| 198 | VK_FORMAT_ASTC_5x4_SRGB_BLOCK, | ||
| 199 | VK_FORMAT_ASTC_5x5_UNORM_BLOCK, | ||
| 200 | VK_FORMAT_ASTC_5x5_SRGB_BLOCK, | ||
| 201 | VK_FORMAT_ASTC_6x5_UNORM_BLOCK, | ||
| 202 | VK_FORMAT_ASTC_6x5_SRGB_BLOCK, | ||
| 203 | VK_FORMAT_ASTC_6x6_UNORM_BLOCK, | ||
| 204 | VK_FORMAT_ASTC_6x6_SRGB_BLOCK, | ||
| 205 | VK_FORMAT_ASTC_8x5_UNORM_BLOCK, | ||
| 206 | VK_FORMAT_ASTC_8x5_SRGB_BLOCK, | ||
| 207 | VK_FORMAT_ASTC_8x6_UNORM_BLOCK, | ||
| 208 | VK_FORMAT_ASTC_8x6_SRGB_BLOCK, | ||
| 209 | VK_FORMAT_ASTC_8x8_UNORM_BLOCK, | ||
| 210 | VK_FORMAT_ASTC_8x8_SRGB_BLOCK, | ||
| 211 | VK_FORMAT_ASTC_10x5_UNORM_BLOCK, | ||
| 212 | VK_FORMAT_ASTC_10x5_SRGB_BLOCK, | ||
| 213 | VK_FORMAT_ASTC_10x6_UNORM_BLOCK, | ||
| 214 | VK_FORMAT_ASTC_10x6_SRGB_BLOCK, | ||
| 215 | VK_FORMAT_ASTC_10x8_UNORM_BLOCK, | ||
| 216 | VK_FORMAT_ASTC_10x8_SRGB_BLOCK, | ||
| 217 | VK_FORMAT_ASTC_10x10_UNORM_BLOCK, | ||
| 218 | VK_FORMAT_ASTC_10x10_SRGB_BLOCK, | ||
| 219 | VK_FORMAT_ASTC_12x10_UNORM_BLOCK, | ||
| 220 | VK_FORMAT_ASTC_12x10_SRGB_BLOCK, | ||
| 221 | VK_FORMAT_ASTC_12x12_UNORM_BLOCK, | ||
| 222 | VK_FORMAT_ASTC_12x12_SRGB_BLOCK, | ||
| 223 | VK_FORMAT_ASTC_8x6_UNORM_BLOCK, | ||
| 224 | VK_FORMAT_ASTC_8x6_SRGB_BLOCK, | ||
| 225 | VK_FORMAT_ASTC_6x5_UNORM_BLOCK, | ||
| 226 | VK_FORMAT_ASTC_6x5_SRGB_BLOCK, | ||
| 227 | VK_FORMAT_E5B9G9R9_UFLOAT_PACK32, | ||
| 228 | }; | 282 | }; |
| 229 | std::unordered_map<VkFormat, VkFormatProperties> format_properties; | 283 | std::unordered_map<VkFormat, VkFormatProperties> format_properties; |
| 230 | for (const auto format : formats) { | 284 | for (const auto format : formats) { |
| @@ -739,9 +793,9 @@ VkFormat Device::GetSupportedFormat(VkFormat wanted_format, VkFormatFeatureFlags | |||
| 739 | if (!IsFormatSupported(alternative, wanted_usage, format_type)) { | 793 | if (!IsFormatSupported(alternative, wanted_usage, format_type)) { |
| 740 | continue; | 794 | continue; |
| 741 | } | 795 | } |
| 742 | LOG_WARNING(Render_Vulkan, | 796 | LOG_DEBUG(Render_Vulkan, |
| 743 | "Emulating format={} with alternative format={} with usage={} and type={}", | 797 | "Emulating format={} with alternative format={} with usage={} and type={}", |
| 744 | wanted_format, alternative, wanted_usage, format_type); | 798 | wanted_format, alternative, wanted_usage, format_type); |
| 745 | return alternative; | 799 | return alternative; |
| 746 | } | 800 | } |
| 747 | 801 | ||
diff --git a/src/web_service/verify_user_jwt.cpp b/src/web_service/verify_user_jwt.cpp index 3bff46f0a..129eb1968 100644 --- a/src/web_service/verify_user_jwt.cpp +++ b/src/web_service/verify_user_jwt.cpp | |||
| @@ -39,8 +39,10 @@ Network::VerifyUser::UserData VerifyUserJWT::LoadUserData(const std::string& ver | |||
| 39 | const std::string audience = fmt::format("external-{}", verify_uid); | 39 | const std::string audience = fmt::format("external-{}", verify_uid); |
| 40 | using namespace jwt::params; | 40 | using namespace jwt::params; |
| 41 | std::error_code error; | 41 | std::error_code error; |
| 42 | |||
| 43 | // We use the Citra backend so the issuer is citra-core | ||
| 42 | auto decoded = | 44 | auto decoded = |
| 43 | jwt::decode(token, algorithms({"rs256"}), error, secret(pub_key), issuer("yuzu-core"), | 45 | jwt::decode(token, algorithms({"rs256"}), error, secret(pub_key), issuer("citra-core"), |
| 44 | aud(audience), validate_iat(true), validate_jti(true)); | 46 | aud(audience), validate_iat(true), validate_jti(true)); |
| 45 | if (error) { | 47 | if (error) { |
| 46 | LOG_INFO(WebService, "Verification failed: category={}, code={}, message={}", | 48 | LOG_INFO(WebService, "Verification failed: category={}, code={}, message={}", |
diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt index f6b389ede..50007338f 100644 --- a/src/yuzu/CMakeLists.txt +++ b/src/yuzu/CMakeLists.txt | |||
| @@ -221,6 +221,9 @@ if (ENABLE_QT_TRANSLATION) | |||
| 221 | # Update source TS file if enabled | 221 | # Update source TS file if enabled |
| 222 | if (GENERATE_QT_TRANSLATION) | 222 | if (GENERATE_QT_TRANSLATION) |
| 223 | get_target_property(SRCS yuzu SOURCES) | 223 | get_target_property(SRCS yuzu SOURCES) |
| 224 | # these calls to qt_create_translation also creates a rule to generate en.qm which conflicts with providing english plurals | ||
| 225 | # so we have to set a OUTPUT_LOCATION so that we don't have multiple rules to generate en.qm | ||
| 226 | set_source_files_properties(${YUZU_QT_LANGUAGES}/en.ts PROPERTIES OUTPUT_LOCATION "${CMAKE_CURRENT_BINARY_DIR}/translations") | ||
| 224 | qt_create_translation(QM_FILES | 227 | qt_create_translation(QM_FILES |
| 225 | ${SRCS} | 228 | ${SRCS} |
| 226 | ${UIS} | 229 | ${UIS} |
| @@ -229,7 +232,13 @@ if (ENABLE_QT_TRANSLATION) | |||
| 229 | -source-language en_US | 232 | -source-language en_US |
| 230 | -target-language en_US | 233 | -target-language en_US |
| 231 | ) | 234 | ) |
| 232 | add_custom_target(translation ALL DEPENDS ${YUZU_QT_LANGUAGES}/en.ts) | 235 | |
| 236 | # Generate plurals into dist/english_plurals/generated_en.ts so it can be used to revise dist/english_plurals/en.ts | ||
| 237 | set(GENERATED_PLURALS_FILE ${PROJECT_SOURCE_DIR}/dist/english_plurals/generated_en.ts) | ||
| 238 | set_source_files_properties(${GENERATED_PLURALS_FILE} PROPERTIES OUTPUT_LOCATION "${CMAKE_CURRENT_BINARY_DIR}/plurals") | ||
| 239 | qt_create_translation(QM_FILES ${SRCS} ${UIS} ${GENERATED_PLURALS_FILE} OPTIONS -pluralonly -source-language en_US -target-language en_US) | ||
| 240 | |||
| 241 | add_custom_target(translation ALL DEPENDS ${YUZU_QT_LANGUAGES}/en.ts ${GENERATED_PLURALS_FILE}) | ||
| 233 | endif() | 242 | endif() |
| 234 | 243 | ||
| 235 | # Find all TS files except en.ts | 244 | # Find all TS files except en.ts |
| @@ -239,6 +248,9 @@ if (ENABLE_QT_TRANSLATION) | |||
| 239 | # Compile TS files to QM files | 248 | # Compile TS files to QM files |
| 240 | qt_add_translation(LANGUAGES_QM ${LANGUAGES_TS}) | 249 | qt_add_translation(LANGUAGES_QM ${LANGUAGES_TS}) |
| 241 | 250 | ||
| 251 | # Compile english plurals TS file to en.qm | ||
| 252 | qt_add_translation(LANGUAGES_QM ${PROJECT_SOURCE_DIR}/dist/english_plurals/en.ts) | ||
| 253 | |||
| 242 | # Build a QRC file from the QM file list | 254 | # Build a QRC file from the QM file list |
| 243 | set(LANGUAGES_QRC ${CMAKE_CURRENT_BINARY_DIR}/languages.qrc) | 255 | set(LANGUAGES_QRC ${CMAKE_CURRENT_BINARY_DIR}/languages.qrc) |
| 244 | file(WRITE ${LANGUAGES_QRC} "<RCC><qresource prefix=\"languages\">\n") | 256 | file(WRITE ${LANGUAGES_QRC} "<RCC><qresource prefix=\"languages\">\n") |
diff --git a/src/yuzu/aboutdialog.ui b/src/yuzu/aboutdialog.ui index c4ffb293e..aea82809d 100644 --- a/src/yuzu/aboutdialog.ui +++ b/src/yuzu/aboutdialog.ui | |||
| @@ -7,7 +7,7 @@ | |||
| 7 | <x>0</x> | 7 | <x>0</x> |
| 8 | <y>0</y> | 8 | <y>0</y> |
| 9 | <width>616</width> | 9 | <width>616</width> |
| 10 | <height>261</height> | 10 | <height>294</height> |
| 11 | </rect> | 11 | </rect> |
| 12 | </property> | 12 | </property> |
| 13 | <property name="windowTitle"> | 13 | <property name="windowTitle"> |
| @@ -165,6 +165,7 @@ p, li { white-space: pre-wrap; } | |||
| 165 | </widget> | 165 | </widget> |
| 166 | <resources> | 166 | <resources> |
| 167 | <include location="../../dist/qt_themes_default/default/default.qrc"/> | 167 | <include location="../../dist/qt_themes_default/default/default.qrc"/> |
| 168 | <include location="../../dist/qt_themes/default/default.qrc"/> | ||
| 168 | </resources> | 169 | </resources> |
| 169 | <connections> | 170 | <connections> |
| 170 | <connection> | 171 | <connection> |
diff --git a/src/yuzu/applets/qt_profile_select.cpp b/src/yuzu/applets/qt_profile_select.cpp index 826c6c224..c8bcfb223 100644 --- a/src/yuzu/applets/qt_profile_select.cpp +++ b/src/yuzu/applets/qt_profile_select.cpp | |||
| @@ -100,6 +100,7 @@ QtProfileSelectionDialog::QtProfileSelectionDialog(Core::HID::HIDCore& hid_core, | |||
| 100 | } | 100 | } |
| 101 | QKeyEvent* event = new QKeyEvent(QEvent::KeyPress, key, Qt::NoModifier); | 101 | QKeyEvent* event = new QKeyEvent(QEvent::KeyPress, key, Qt::NoModifier); |
| 102 | QCoreApplication::postEvent(tree_view, event); | 102 | QCoreApplication::postEvent(tree_view, event); |
| 103 | SelectUser(tree_view->currentIndex()); | ||
| 103 | }); | 104 | }); |
| 104 | 105 | ||
| 105 | const auto& profiles = profile_manager->GetAllUsers(); | 106 | const auto& profiles = profile_manager->GetAllUsers(); |
diff --git a/src/yuzu/bootmanager.cpp b/src/yuzu/bootmanager.cpp index ef3bdfb1a..d3fbdb09d 100644 --- a/src/yuzu/bootmanager.cpp +++ b/src/yuzu/bootmanager.cpp | |||
| @@ -815,6 +815,12 @@ void GRenderWindow::InitializeCamera() { | |||
| 815 | if (Settings::values.ir_sensor_device.GetValue() == cameraInfo.deviceName().toStdString() || | 815 | if (Settings::values.ir_sensor_device.GetValue() == cameraInfo.deviceName().toStdString() || |
| 816 | Settings::values.ir_sensor_device.GetValue() == "Auto") { | 816 | Settings::values.ir_sensor_device.GetValue() == "Auto") { |
| 817 | camera = std::make_unique<QCamera>(cameraInfo); | 817 | camera = std::make_unique<QCamera>(cameraInfo); |
| 818 | if (!camera->isCaptureModeSupported(QCamera::CaptureMode::CaptureViewfinder) && | ||
| 819 | !camera->isCaptureModeSupported(QCamera::CaptureMode::CaptureStillImage)) { | ||
| 820 | LOG_ERROR(Frontend, | ||
| 821 | "Camera doesn't support CaptureViewfinder or CaptureStillImage"); | ||
| 822 | continue; | ||
| 823 | } | ||
| 818 | camera_found = true; | 824 | camera_found = true; |
| 819 | break; | 825 | break; |
| 820 | } | 826 | } |
| @@ -825,10 +831,22 @@ void GRenderWindow::InitializeCamera() { | |||
| 825 | } | 831 | } |
| 826 | 832 | ||
| 827 | camera_capture = std::make_unique<QCameraImageCapture>(camera.get()); | 833 | camera_capture = std::make_unique<QCameraImageCapture>(camera.get()); |
| 834 | |||
| 835 | if (!camera_capture->isCaptureDestinationSupported( | ||
| 836 | QCameraImageCapture::CaptureDestination::CaptureToBuffer)) { | ||
| 837 | LOG_ERROR(Frontend, "Camera doesn't support saving to buffer"); | ||
| 838 | return; | ||
| 839 | } | ||
| 840 | |||
| 841 | camera_capture->setCaptureDestination(QCameraImageCapture::CaptureDestination::CaptureToBuffer); | ||
| 828 | connect(camera_capture.get(), &QCameraImageCapture::imageCaptured, this, | 842 | connect(camera_capture.get(), &QCameraImageCapture::imageCaptured, this, |
| 829 | &GRenderWindow::OnCameraCapture); | 843 | &GRenderWindow::OnCameraCapture); |
| 830 | camera->unload(); | 844 | camera->unload(); |
| 831 | camera->setCaptureMode(QCamera::CaptureViewfinder); | 845 | if (camera->isCaptureModeSupported(QCamera::CaptureMode::CaptureViewfinder)) { |
| 846 | camera->setCaptureMode(QCamera::CaptureViewfinder); | ||
| 847 | } else if (camera->isCaptureModeSupported(QCamera::CaptureMode::CaptureStillImage)) { | ||
| 848 | camera->setCaptureMode(QCamera::CaptureStillImage); | ||
| 849 | } | ||
| 832 | camera->load(); | 850 | camera->load(); |
| 833 | camera->start(); | 851 | camera->start(); |
| 834 | 852 | ||
| @@ -1089,8 +1107,8 @@ QStringList GRenderWindow::GetUnsupportedGLExtensions() const { | |||
| 1089 | } | 1107 | } |
| 1090 | 1108 | ||
| 1091 | if (!unsupported_ext.empty()) { | 1109 | if (!unsupported_ext.empty()) { |
| 1092 | LOG_ERROR(Frontend, "GPU does not support all required extensions: {}", | 1110 | const std::string gl_renderer{reinterpret_cast<const char*>(glGetString(GL_RENDERER))}; |
| 1093 | glGetString(GL_RENDERER)); | 1111 | LOG_ERROR(Frontend, "GPU does not support all required extensions: {}", gl_renderer); |
| 1094 | } | 1112 | } |
| 1095 | for (const QString& ext : unsupported_ext) { | 1113 | for (const QString& ext : unsupported_ext) { |
| 1096 | LOG_ERROR(Frontend, "Unsupported GL extension: {}", ext.toStdString()); | 1114 | LOG_ERROR(Frontend, "Unsupported GL extension: {}", ext.toStdString()); |
diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp index 58f1239bf..8ecd87150 100644 --- a/src/yuzu/configuration/config.cpp +++ b/src/yuzu/configuration/config.cpp | |||
| @@ -73,7 +73,7 @@ const std::array<int, 2> Config::default_ringcon_analogs{{ | |||
| 73 | const std::array<UISettings::Shortcut, 22> Config::default_hotkeys{{ | 73 | const std::array<UISettings::Shortcut, 22> Config::default_hotkeys{{ |
| 74 | {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Audio Mute/Unmute")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+M"), QStringLiteral("Home+Dpad_Right"), Qt::WindowShortcut}}, | 74 | {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Audio Mute/Unmute")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+M"), QStringLiteral("Home+Dpad_Right"), Qt::WindowShortcut}}, |
| 75 | {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Audio Volume Down")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("-"), QStringLiteral("Home+Dpad_Down"), Qt::ApplicationShortcut}}, | 75 | {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Audio Volume Down")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("-"), QStringLiteral("Home+Dpad_Down"), Qt::ApplicationShortcut}}, |
| 76 | {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Audio Volume Up")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("+"), QStringLiteral("Home+Dpad_Up"), Qt::ApplicationShortcut}}, | 76 | {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Audio Volume Up")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("="), QStringLiteral("Home+Dpad_Up"), Qt::ApplicationShortcut}}, |
| 77 | {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Capture Screenshot")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+P"), QStringLiteral("Screenshot"), Qt::WidgetWithChildrenShortcut}}, | 77 | {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Capture Screenshot")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+P"), QStringLiteral("Screenshot"), Qt::WidgetWithChildrenShortcut}}, |
| 78 | {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Change Adapting Filter")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F8"), QStringLiteral("Home+L"), Qt::ApplicationShortcut}}, | 78 | {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Change Adapting Filter")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F8"), QStringLiteral("Home+L"), Qt::ApplicationShortcut}}, |
| 79 | {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Change Docked Mode")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F10"), QStringLiteral("Home+X"), Qt::ApplicationShortcut}}, | 79 | {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Change Docked Mode")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F10"), QStringLiteral("Home+X"), Qt::ApplicationShortcut}}, |
| @@ -684,6 +684,7 @@ void Config::ReadRendererValues() { | |||
| 684 | ReadGlobalSetting(Settings::values.shader_backend); | 684 | ReadGlobalSetting(Settings::values.shader_backend); |
| 685 | ReadGlobalSetting(Settings::values.use_asynchronous_shaders); | 685 | ReadGlobalSetting(Settings::values.use_asynchronous_shaders); |
| 686 | ReadGlobalSetting(Settings::values.use_fast_gpu_time); | 686 | ReadGlobalSetting(Settings::values.use_fast_gpu_time); |
| 687 | ReadGlobalSetting(Settings::values.use_pessimistic_flushes); | ||
| 687 | ReadGlobalSetting(Settings::values.bg_red); | 688 | ReadGlobalSetting(Settings::values.bg_red); |
| 688 | ReadGlobalSetting(Settings::values.bg_green); | 689 | ReadGlobalSetting(Settings::values.bg_green); |
| 689 | ReadGlobalSetting(Settings::values.bg_blue); | 690 | ReadGlobalSetting(Settings::values.bg_blue); |
| @@ -1300,6 +1301,7 @@ void Config::SaveRendererValues() { | |||
| 1300 | Settings::values.shader_backend.UsingGlobal()); | 1301 | Settings::values.shader_backend.UsingGlobal()); |
| 1301 | WriteGlobalSetting(Settings::values.use_asynchronous_shaders); | 1302 | WriteGlobalSetting(Settings::values.use_asynchronous_shaders); |
| 1302 | WriteGlobalSetting(Settings::values.use_fast_gpu_time); | 1303 | WriteGlobalSetting(Settings::values.use_fast_gpu_time); |
| 1304 | WriteGlobalSetting(Settings::values.use_pessimistic_flushes); | ||
| 1303 | WriteGlobalSetting(Settings::values.bg_red); | 1305 | WriteGlobalSetting(Settings::values.bg_red); |
| 1304 | WriteGlobalSetting(Settings::values.bg_green); | 1306 | WriteGlobalSetting(Settings::values.bg_green); |
| 1305 | WriteGlobalSetting(Settings::values.bg_blue); | 1307 | WriteGlobalSetting(Settings::values.bg_blue); |
diff --git a/src/yuzu/configuration/configure_audio.ui b/src/yuzu/configuration/configure_audio.ui index a5bcee415..6034d8581 100644 --- a/src/yuzu/configuration/configure_audio.ui +++ b/src/yuzu/configuration/configure_audio.ui | |||
| @@ -120,10 +120,10 @@ | |||
| 120 | </sizepolicy> | 120 | </sizepolicy> |
| 121 | </property> | 121 | </property> |
| 122 | <property name="maximum"> | 122 | <property name="maximum"> |
| 123 | <number>100</number> | 123 | <number>200</number> |
| 124 | </property> | 124 | </property> |
| 125 | <property name="pageStep"> | 125 | <property name="pageStep"> |
| 126 | <number>10</number> | 126 | <number>5</number> |
| 127 | </property> | 127 | </property> |
| 128 | <property name="orientation"> | 128 | <property name="orientation"> |
| 129 | <enum>Qt::Horizontal</enum> | 129 | <enum>Qt::Horizontal</enum> |
diff --git a/src/yuzu/configuration/configure_camera.cpp b/src/yuzu/configuration/configure_camera.cpp index 73cdcf3f2..2a61de2a1 100644 --- a/src/yuzu/configuration/configure_camera.cpp +++ b/src/yuzu/configuration/configure_camera.cpp | |||
| @@ -42,6 +42,12 @@ void ConfigureCamera::PreviewCamera() { | |||
| 42 | LOG_INFO(Frontend, "Selected Camera {} {}", cameraInfo.description().toStdString(), | 42 | LOG_INFO(Frontend, "Selected Camera {} {}", cameraInfo.description().toStdString(), |
| 43 | cameraInfo.deviceName().toStdString()); | 43 | cameraInfo.deviceName().toStdString()); |
| 44 | camera = std::make_unique<QCamera>(cameraInfo); | 44 | camera = std::make_unique<QCamera>(cameraInfo); |
| 45 | if (!camera->isCaptureModeSupported(QCamera::CaptureMode::CaptureViewfinder) && | ||
| 46 | !camera->isCaptureModeSupported(QCamera::CaptureMode::CaptureStillImage)) { | ||
| 47 | LOG_ERROR(Frontend, | ||
| 48 | "Camera doesn't support CaptureViewfinder or CaptureStillImage"); | ||
| 49 | continue; | ||
| 50 | } | ||
| 45 | camera_found = true; | 51 | camera_found = true; |
| 46 | break; | 52 | break; |
| 47 | } | 53 | } |
| @@ -57,10 +63,22 @@ void ConfigureCamera::PreviewCamera() { | |||
| 57 | } | 63 | } |
| 58 | 64 | ||
| 59 | camera_capture = std::make_unique<QCameraImageCapture>(camera.get()); | 65 | camera_capture = std::make_unique<QCameraImageCapture>(camera.get()); |
| 66 | |||
| 67 | if (!camera_capture->isCaptureDestinationSupported( | ||
| 68 | QCameraImageCapture::CaptureDestination::CaptureToBuffer)) { | ||
| 69 | LOG_ERROR(Frontend, "Camera doesn't support saving to buffer"); | ||
| 70 | return; | ||
| 71 | } | ||
| 72 | |||
| 73 | camera_capture->setCaptureDestination(QCameraImageCapture::CaptureDestination::CaptureToBuffer); | ||
| 60 | connect(camera_capture.get(), &QCameraImageCapture::imageCaptured, this, | 74 | connect(camera_capture.get(), &QCameraImageCapture::imageCaptured, this, |
| 61 | &ConfigureCamera::DisplayCapturedFrame); | 75 | &ConfigureCamera::DisplayCapturedFrame); |
| 62 | camera->unload(); | 76 | camera->unload(); |
| 63 | camera->setCaptureMode(QCamera::CaptureViewfinder); | 77 | if (camera->isCaptureModeSupported(QCamera::CaptureMode::CaptureViewfinder)) { |
| 78 | camera->setCaptureMode(QCamera::CaptureViewfinder); | ||
| 79 | } else if (camera->isCaptureModeSupported(QCamera::CaptureMode::CaptureStillImage)) { | ||
| 80 | camera->setCaptureMode(QCamera::CaptureStillImage); | ||
| 81 | } | ||
| 64 | camera->load(); | 82 | camera->load(); |
| 65 | camera->start(); | 83 | camera->start(); |
| 66 | 84 | ||
diff --git a/src/yuzu/configuration/configure_debug.cpp b/src/yuzu/configuration/configure_debug.cpp index e16d127a8..04d397750 100644 --- a/src/yuzu/configuration/configure_debug.cpp +++ b/src/yuzu/configuration/configure_debug.cpp | |||
| @@ -14,7 +14,7 @@ | |||
| 14 | #include "yuzu/uisettings.h" | 14 | #include "yuzu/uisettings.h" |
| 15 | 15 | ||
| 16 | ConfigureDebug::ConfigureDebug(const Core::System& system_, QWidget* parent) | 16 | ConfigureDebug::ConfigureDebug(const Core::System& system_, QWidget* parent) |
| 17 | : QWidget(parent), ui{std::make_unique<Ui::ConfigureDebug>()}, system{system_} { | 17 | : QScrollArea(parent), ui{std::make_unique<Ui::ConfigureDebug>()}, system{system_} { |
| 18 | ui->setupUi(this); | 18 | ui->setupUi(this); |
| 19 | SetConfiguration(); | 19 | SetConfiguration(); |
| 20 | 20 | ||
diff --git a/src/yuzu/configuration/configure_debug.h b/src/yuzu/configuration/configure_debug.h index 64d68ab8f..42d30f170 100644 --- a/src/yuzu/configuration/configure_debug.h +++ b/src/yuzu/configuration/configure_debug.h | |||
| @@ -4,7 +4,7 @@ | |||
| 4 | #pragma once | 4 | #pragma once |
| 5 | 5 | ||
| 6 | #include <memory> | 6 | #include <memory> |
| 7 | #include <QWidget> | 7 | #include <QScrollArea> |
| 8 | 8 | ||
| 9 | namespace Core { | 9 | namespace Core { |
| 10 | class System; | 10 | class System; |
| @@ -14,7 +14,7 @@ namespace Ui { | |||
| 14 | class ConfigureDebug; | 14 | class ConfigureDebug; |
| 15 | } | 15 | } |
| 16 | 16 | ||
| 17 | class ConfigureDebug : public QWidget { | 17 | class ConfigureDebug : public QScrollArea { |
| 18 | Q_OBJECT | 18 | Q_OBJECT |
| 19 | 19 | ||
| 20 | public: | 20 | public: |
diff --git a/src/yuzu/configuration/configure_debug.ui b/src/yuzu/configuration/configure_debug.ui index 4c16274fc..47b8b80f1 100644 --- a/src/yuzu/configuration/configure_debug.ui +++ b/src/yuzu/configuration/configure_debug.ui | |||
| @@ -1,7 +1,11 @@ | |||
| 1 | <?xml version="1.0" encoding="UTF-8"?> | 1 | <?xml version="1.0" encoding="UTF-8"?> |
| 2 | <ui version="4.0"> | 2 | <ui version="4.0"> |
| 3 | <class>ConfigureDebug</class> | 3 | <class>ConfigureDebug</class> |
| 4 | <widget class="QWidget" name="ConfigureDebug"> | 4 | <widget class="QScrollArea" name="ConfigureDebug"> |
| 5 | <property name="widgetResizable"> | ||
| 6 | <bool>true</bool> | ||
| 7 | </property> | ||
| 8 | <widget class="QWidget"> | ||
| 5 | <layout class="QVBoxLayout" name="verticalLayout_1"> | 9 | <layout class="QVBoxLayout" name="verticalLayout_1"> |
| 6 | <item> | 10 | <item> |
| 7 | <layout class="QVBoxLayout" name="verticalLayout_2"> | 11 | <layout class="QVBoxLayout" name="verticalLayout_2"> |
| @@ -322,6 +326,7 @@ | |||
| 322 | </item> | 326 | </item> |
| 323 | </layout> | 327 | </layout> |
| 324 | </widget> | 328 | </widget> |
| 329 | </widget> | ||
| 325 | <tabstops> | 330 | <tabstops> |
| 326 | <tabstop>log_filter_edit</tabstop> | 331 | <tabstop>log_filter_edit</tabstop> |
| 327 | <tabstop>toggle_console</tabstop> | 332 | <tabstop>toggle_console</tabstop> |
diff --git a/src/yuzu/configuration/configure_graphics_advanced.cpp b/src/yuzu/configuration/configure_graphics_advanced.cpp index 7c3196c83..01f074699 100644 --- a/src/yuzu/configuration/configure_graphics_advanced.cpp +++ b/src/yuzu/configuration/configure_graphics_advanced.cpp | |||
| @@ -28,6 +28,7 @@ void ConfigureGraphicsAdvanced::SetConfiguration() { | |||
| 28 | ui->use_vsync->setChecked(Settings::values.use_vsync.GetValue()); | 28 | ui->use_vsync->setChecked(Settings::values.use_vsync.GetValue()); |
| 29 | ui->use_asynchronous_shaders->setChecked(Settings::values.use_asynchronous_shaders.GetValue()); | 29 | ui->use_asynchronous_shaders->setChecked(Settings::values.use_asynchronous_shaders.GetValue()); |
| 30 | ui->use_fast_gpu_time->setChecked(Settings::values.use_fast_gpu_time.GetValue()); | 30 | ui->use_fast_gpu_time->setChecked(Settings::values.use_fast_gpu_time.GetValue()); |
| 31 | ui->use_pessimistic_flushes->setChecked(Settings::values.use_pessimistic_flushes.GetValue()); | ||
| 31 | 32 | ||
| 32 | if (Settings::IsConfiguringGlobal()) { | 33 | if (Settings::IsConfiguringGlobal()) { |
| 33 | ui->gpu_accuracy->setCurrentIndex( | 34 | ui->gpu_accuracy->setCurrentIndex( |
| @@ -55,6 +56,8 @@ void ConfigureGraphicsAdvanced::ApplyConfiguration() { | |||
| 55 | use_asynchronous_shaders); | 56 | use_asynchronous_shaders); |
| 56 | ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_fast_gpu_time, | 57 | ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_fast_gpu_time, |
| 57 | ui->use_fast_gpu_time, use_fast_gpu_time); | 58 | ui->use_fast_gpu_time, use_fast_gpu_time); |
| 59 | ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_pessimistic_flushes, | ||
| 60 | ui->use_pessimistic_flushes, use_pessimistic_flushes); | ||
| 58 | } | 61 | } |
| 59 | 62 | ||
| 60 | void ConfigureGraphicsAdvanced::changeEvent(QEvent* event) { | 63 | void ConfigureGraphicsAdvanced::changeEvent(QEvent* event) { |
| @@ -77,6 +80,8 @@ void ConfigureGraphicsAdvanced::SetupPerGameUI() { | |||
| 77 | ui->use_asynchronous_shaders->setEnabled( | 80 | ui->use_asynchronous_shaders->setEnabled( |
| 78 | Settings::values.use_asynchronous_shaders.UsingGlobal()); | 81 | Settings::values.use_asynchronous_shaders.UsingGlobal()); |
| 79 | ui->use_fast_gpu_time->setEnabled(Settings::values.use_fast_gpu_time.UsingGlobal()); | 82 | ui->use_fast_gpu_time->setEnabled(Settings::values.use_fast_gpu_time.UsingGlobal()); |
| 83 | ui->use_pessimistic_flushes->setEnabled( | ||
| 84 | Settings::values.use_pessimistic_flushes.UsingGlobal()); | ||
| 80 | ui->anisotropic_filtering_combobox->setEnabled( | 85 | ui->anisotropic_filtering_combobox->setEnabled( |
| 81 | Settings::values.max_anisotropy.UsingGlobal()); | 86 | Settings::values.max_anisotropy.UsingGlobal()); |
| 82 | 87 | ||
| @@ -89,6 +94,9 @@ void ConfigureGraphicsAdvanced::SetupPerGameUI() { | |||
| 89 | use_asynchronous_shaders); | 94 | use_asynchronous_shaders); |
| 90 | ConfigurationShared::SetColoredTristate(ui->use_fast_gpu_time, | 95 | ConfigurationShared::SetColoredTristate(ui->use_fast_gpu_time, |
| 91 | Settings::values.use_fast_gpu_time, use_fast_gpu_time); | 96 | Settings::values.use_fast_gpu_time, use_fast_gpu_time); |
| 97 | ConfigurationShared::SetColoredTristate(ui->use_pessimistic_flushes, | ||
| 98 | Settings::values.use_pessimistic_flushes, | ||
| 99 | use_pessimistic_flushes); | ||
| 92 | ConfigurationShared::SetColoredComboBox( | 100 | ConfigurationShared::SetColoredComboBox( |
| 93 | ui->gpu_accuracy, ui->label_gpu_accuracy, | 101 | ui->gpu_accuracy, ui->label_gpu_accuracy, |
| 94 | static_cast<int>(Settings::values.gpu_accuracy.GetValue(true))); | 102 | static_cast<int>(Settings::values.gpu_accuracy.GetValue(true))); |
diff --git a/src/yuzu/configuration/configure_graphics_advanced.h b/src/yuzu/configuration/configure_graphics_advanced.h index 1ef7bd916..12e816905 100644 --- a/src/yuzu/configuration/configure_graphics_advanced.h +++ b/src/yuzu/configuration/configure_graphics_advanced.h | |||
| @@ -39,6 +39,7 @@ private: | |||
| 39 | ConfigurationShared::CheckState use_vsync; | 39 | ConfigurationShared::CheckState use_vsync; |
| 40 | ConfigurationShared::CheckState use_asynchronous_shaders; | 40 | ConfigurationShared::CheckState use_asynchronous_shaders; |
| 41 | ConfigurationShared::CheckState use_fast_gpu_time; | 41 | ConfigurationShared::CheckState use_fast_gpu_time; |
| 42 | ConfigurationShared::CheckState use_pessimistic_flushes; | ||
| 42 | 43 | ||
| 43 | const Core::System& system; | 44 | const Core::System& system; |
| 44 | }; | 45 | }; |
diff --git a/src/yuzu/configuration/configure_graphics_advanced.ui b/src/yuzu/configuration/configure_graphics_advanced.ui index 96de0b3d1..87a121471 100644 --- a/src/yuzu/configuration/configure_graphics_advanced.ui +++ b/src/yuzu/configuration/configure_graphics_advanced.ui | |||
| @@ -75,7 +75,7 @@ | |||
| 75 | <string>VSync prevents the screen from tearing, but some graphics cards have lower performance with VSync enabled. Keep it enabled if you don't notice a performance difference.</string> | 75 | <string>VSync prevents the screen from tearing, but some graphics cards have lower performance with VSync enabled. Keep it enabled if you don't notice a performance difference.</string> |
| 76 | </property> | 76 | </property> |
| 77 | <property name="text"> | 77 | <property name="text"> |
| 78 | <string>Use VSync (OpenGL only)</string> | 78 | <string>Use VSync</string> |
| 79 | </property> | 79 | </property> |
| 80 | </widget> | 80 | </widget> |
| 81 | </item> | 81 | </item> |
| @@ -100,6 +100,16 @@ | |||
| 100 | </widget> | 100 | </widget> |
| 101 | </item> | 101 | </item> |
| 102 | <item> | 102 | <item> |
| 103 | <widget class="QCheckBox" name="use_pessimistic_flushes"> | ||
| 104 | <property name="toolTip"> | ||
| 105 | <string>Enables pessimistic buffer flushes. This option will force unmodified buffers to be flushed, which can cost performance.</string> | ||
| 106 | </property> | ||
| 107 | <property name="text"> | ||
| 108 | <string>Use pessimistic buffer flushes (Hack)</string> | ||
| 109 | </property> | ||
| 110 | </widget> | ||
| 111 | </item> | ||
| 112 | <item> | ||
| 103 | <widget class="QWidget" name="af_layout" native="true"> | 113 | <widget class="QWidget" name="af_layout" native="true"> |
| 104 | <layout class="QHBoxLayout" name="horizontalLayout_1"> | 114 | <layout class="QHBoxLayout" name="horizontalLayout_1"> |
| 105 | <property name="leftMargin"> | 115 | <property name="leftMargin"> |
diff --git a/src/yuzu/configuration/configure_input_player.cpp b/src/yuzu/configuration/configure_input_player.cpp index 00bee85b2..9e5a40fe7 100644 --- a/src/yuzu/configuration/configure_input_player.cpp +++ b/src/yuzu/configuration/configure_input_player.cpp | |||
| @@ -161,6 +161,7 @@ QString ConfigureInputPlayer::ButtonToText(const Common::ParamPackage& param) { | |||
| 161 | 161 | ||
| 162 | const QString toggle = QString::fromStdString(param.Get("toggle", false) ? "~" : ""); | 162 | const QString toggle = QString::fromStdString(param.Get("toggle", false) ? "~" : ""); |
| 163 | const QString inverted = QString::fromStdString(param.Get("inverted", false) ? "!" : ""); | 163 | const QString inverted = QString::fromStdString(param.Get("inverted", false) ? "!" : ""); |
| 164 | const QString invert = QString::fromStdString(param.Get("invert", "+") == "-" ? "-" : ""); | ||
| 164 | const auto common_button_name = input_subsystem->GetButtonName(param); | 165 | const auto common_button_name = input_subsystem->GetButtonName(param); |
| 165 | 166 | ||
| 166 | // Retrieve the names from Qt | 167 | // Retrieve the names from Qt |
| @@ -184,7 +185,7 @@ QString ConfigureInputPlayer::ButtonToText(const Common::ParamPackage& param) { | |||
| 184 | } | 185 | } |
| 185 | if (param.Has("axis")) { | 186 | if (param.Has("axis")) { |
| 186 | const QString axis = QString::fromStdString(param.Get("axis", "")); | 187 | const QString axis = QString::fromStdString(param.Get("axis", "")); |
| 187 | return QObject::tr("%1%2Axis %3").arg(toggle, inverted, axis); | 188 | return QObject::tr("%1%2Axis %3").arg(toggle, invert, axis); |
| 188 | } | 189 | } |
| 189 | if (param.Has("axis_x") && param.Has("axis_y") && param.Has("axis_z")) { | 190 | if (param.Has("axis_x") && param.Has("axis_y") && param.Has("axis_z")) { |
| 190 | const QString axis_x = QString::fromStdString(param.Get("axis_x", "")); | 191 | const QString axis_x = QString::fromStdString(param.Get("axis_x", "")); |
| @@ -362,18 +363,18 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i | |||
| 362 | button_map[button_id]->setText(tr("[not set]")); | 363 | button_map[button_id]->setText(tr("[not set]")); |
| 363 | }); | 364 | }); |
| 364 | if (param.Has("code") || param.Has("button") || param.Has("hat")) { | 365 | if (param.Has("code") || param.Has("button") || param.Has("hat")) { |
| 365 | context_menu.addAction(tr("Toggle button"), [&] { | ||
| 366 | const bool toggle_value = !param.Get("toggle", false); | ||
| 367 | param.Set("toggle", toggle_value); | ||
| 368 | button_map[button_id]->setText(ButtonToText(param)); | ||
| 369 | emulated_controller->SetButtonParam(button_id, param); | ||
| 370 | }); | ||
| 371 | context_menu.addAction(tr("Invert button"), [&] { | 366 | context_menu.addAction(tr("Invert button"), [&] { |
| 372 | const bool invert_value = !param.Get("inverted", false); | 367 | const bool invert_value = !param.Get("inverted", false); |
| 373 | param.Set("inverted", invert_value); | 368 | param.Set("inverted", invert_value); |
| 374 | button_map[button_id]->setText(ButtonToText(param)); | 369 | button_map[button_id]->setText(ButtonToText(param)); |
| 375 | emulated_controller->SetButtonParam(button_id, param); | 370 | emulated_controller->SetButtonParam(button_id, param); |
| 376 | }); | 371 | }); |
| 372 | context_menu.addAction(tr("Toggle button"), [&] { | ||
| 373 | const bool toggle_value = !param.Get("toggle", false); | ||
| 374 | param.Set("toggle", toggle_value); | ||
| 375 | button_map[button_id]->setText(ButtonToText(param)); | ||
| 376 | emulated_controller->SetButtonParam(button_id, param); | ||
| 377 | }); | ||
| 377 | } | 378 | } |
| 378 | if (param.Has("axis")) { | 379 | if (param.Has("axis")) { |
| 379 | context_menu.addAction(tr("Invert axis"), [&] { | 380 | context_menu.addAction(tr("Invert axis"), [&] { |
| @@ -398,6 +399,12 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i | |||
| 398 | } | 399 | } |
| 399 | emulated_controller->SetButtonParam(button_id, param); | 400 | emulated_controller->SetButtonParam(button_id, param); |
| 400 | }); | 401 | }); |
| 402 | context_menu.addAction(tr("Toggle axis"), [&] { | ||
| 403 | const bool toggle_value = !param.Get("toggle", false); | ||
| 404 | param.Set("toggle", toggle_value); | ||
| 405 | button_map[button_id]->setText(ButtonToText(param)); | ||
| 406 | emulated_controller->SetButtonParam(button_id, param); | ||
| 407 | }); | ||
| 401 | } | 408 | } |
| 402 | context_menu.exec(button_map[button_id]->mapToGlobal(menu_location)); | 409 | context_menu.exec(button_map[button_id]->mapToGlobal(menu_location)); |
| 403 | }); | 410 | }); |
| @@ -1410,7 +1417,7 @@ void ConfigureInputPlayer::HandleClick( | |||
| 1410 | ui->controllerFrame->BeginMappingAnalog(button_id); | 1417 | ui->controllerFrame->BeginMappingAnalog(button_id); |
| 1411 | } | 1418 | } |
| 1412 | 1419 | ||
| 1413 | timeout_timer->start(2500); // Cancel after 2.5 seconds | 1420 | timeout_timer->start(4000); // Cancel after 4 seconds |
| 1414 | poll_timer->start(25); // Check for new inputs every 25ms | 1421 | poll_timer->start(25); // Check for new inputs every 25ms |
| 1415 | } | 1422 | } |
| 1416 | 1423 | ||
| @@ -1475,7 +1482,7 @@ void ConfigureInputPlayer::keyPressEvent(QKeyEvent* event) { | |||
| 1475 | 1482 | ||
| 1476 | void ConfigureInputPlayer::CreateProfile() { | 1483 | void ConfigureInputPlayer::CreateProfile() { |
| 1477 | const auto profile_name = | 1484 | const auto profile_name = |
| 1478 | LimitableInputDialog::GetText(this, tr("New Profile"), tr("Enter a profile name:"), 1, 20, | 1485 | LimitableInputDialog::GetText(this, tr("New Profile"), tr("Enter a profile name:"), 1, 30, |
| 1479 | LimitableInputDialog::InputLimiter::Filesystem); | 1486 | LimitableInputDialog::InputLimiter::Filesystem); |
| 1480 | 1487 | ||
| 1481 | if (profile_name.isEmpty()) { | 1488 | if (profile_name.isEmpty()) { |
diff --git a/src/yuzu/configuration/configure_tas.ui b/src/yuzu/configuration/configure_tas.ui index cf88a5bf0..625af0c89 100644 --- a/src/yuzu/configuration/configure_tas.ui +++ b/src/yuzu/configuration/configure_tas.ui | |||
| @@ -16,6 +16,9 @@ | |||
| 16 | <property name="text"> | 16 | <property name="text"> |
| 17 | <string><html><head/><body><p>Reads controller input from scripts in the same format as TAS-nx scripts.<br/>For a more detailed explanation, please consult the <a href="https://yuzu-emu.org/help/feature/tas/"><span style=" text-decoration: underline; color:#039be5;">help page</span></a> on the yuzu website.</p></body></html></string> | 17 | <string><html><head/><body><p>Reads controller input from scripts in the same format as TAS-nx scripts.<br/>For a more detailed explanation, please consult the <a href="https://yuzu-emu.org/help/feature/tas/"><span style=" text-decoration: underline; color:#039be5;">help page</span></a> on the yuzu website.</p></body></html></string> |
| 18 | </property> | 18 | </property> |
| 19 | <property name="openExternalLinks"> | ||
| 20 | <bool>true</bool> | ||
| 21 | </property> | ||
| 19 | </widget> | 22 | </widget> |
| 20 | </item> | 23 | </item> |
| 21 | <item row="1" column="0" colspan="4"> | 24 | <item row="1" column="0" colspan="4"> |
diff --git a/src/yuzu/configuration/configure_ui.cpp b/src/yuzu/configuration/configure_ui.cpp index 2e98ede8e..48f71b53c 100644 --- a/src/yuzu/configuration/configure_ui.cpp +++ b/src/yuzu/configuration/configure_ui.cpp | |||
| @@ -219,6 +219,7 @@ void ConfigureUi::InitializeLanguageComboBox() { | |||
| 219 | for (const auto& lang : languages) { | 219 | for (const auto& lang : languages) { |
| 220 | if (QString::fromLatin1(lang.id) == QStringLiteral("en")) { | 220 | if (QString::fromLatin1(lang.id) == QStringLiteral("en")) { |
| 221 | ui->language_combobox->addItem(lang.name, QStringLiteral("en")); | 221 | ui->language_combobox->addItem(lang.name, QStringLiteral("en")); |
| 222 | language_files.removeOne(QStringLiteral("en.qm")); | ||
| 222 | continue; | 223 | continue; |
| 223 | } | 224 | } |
| 224 | for (int i = 0; i < language_files.size(); ++i) { | 225 | for (int i = 0; i < language_files.size(); ++i) { |
diff --git a/src/yuzu/game_list.cpp b/src/yuzu/game_list.cpp index 041e6ac11..b127badc2 100644 --- a/src/yuzu/game_list.cpp +++ b/src/yuzu/game_list.cpp | |||
| @@ -126,10 +126,8 @@ GameListSearchField::GameListSearchField(GameList* parent) : QWidget{parent} { | |||
| 126 | layout_filter = new QHBoxLayout; | 126 | layout_filter = new QHBoxLayout; |
| 127 | layout_filter->setContentsMargins(8, 8, 8, 8); | 127 | layout_filter->setContentsMargins(8, 8, 8, 8); |
| 128 | label_filter = new QLabel; | 128 | label_filter = new QLabel; |
| 129 | label_filter->setText(tr("Filter:")); | ||
| 130 | edit_filter = new QLineEdit; | 129 | edit_filter = new QLineEdit; |
| 131 | edit_filter->clear(); | 130 | edit_filter->clear(); |
| 132 | edit_filter->setPlaceholderText(tr("Enter pattern to filter")); | ||
| 133 | edit_filter->installEventFilter(key_release_eater); | 131 | edit_filter->installEventFilter(key_release_eater); |
| 134 | edit_filter->setClearButtonEnabled(true); | 132 | edit_filter->setClearButtonEnabled(true); |
| 135 | connect(edit_filter, &QLineEdit::textChanged, parent, &GameList::OnTextChanged); | 133 | connect(edit_filter, &QLineEdit::textChanged, parent, &GameList::OnTextChanged); |
| @@ -149,6 +147,7 @@ GameListSearchField::GameListSearchField(GameList* parent) : QWidget{parent} { | |||
| 149 | layout_filter->addWidget(label_filter_result); | 147 | layout_filter->addWidget(label_filter_result); |
| 150 | layout_filter->addWidget(button_filter_close); | 148 | layout_filter->addWidget(button_filter_close); |
| 151 | setLayout(layout_filter); | 149 | setLayout(layout_filter); |
| 150 | RetranslateUI(); | ||
| 152 | } | 151 | } |
| 153 | 152 | ||
| 154 | /** | 153 | /** |
| @@ -286,7 +285,7 @@ void GameList::OnUpdateThemedIcons() { | |||
| 286 | } | 285 | } |
| 287 | case GameListItemType::AddDir: | 286 | case GameListItemType::AddDir: |
| 288 | child->setData( | 287 | child->setData( |
| 289 | QIcon::fromTheme(QStringLiteral("plus")) | 288 | QIcon::fromTheme(QStringLiteral("list-add")) |
| 290 | .pixmap(icon_size) | 289 | .pixmap(icon_size) |
| 291 | .scaled(icon_size, icon_size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation), | 290 | .scaled(icon_size, icon_size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation), |
| 292 | Qt::DecorationRole); | 291 | Qt::DecorationRole); |
| @@ -333,13 +332,9 @@ GameList::GameList(FileSys::VirtualFilesystem vfs_, FileSys::ManualContentProvid | |||
| 333 | tree_view->setStyleSheet(QStringLiteral("QTreeView{ border: none; }")); | 332 | tree_view->setStyleSheet(QStringLiteral("QTreeView{ border: none; }")); |
| 334 | 333 | ||
| 335 | item_model->insertColumns(0, COLUMN_COUNT); | 334 | item_model->insertColumns(0, COLUMN_COUNT); |
| 336 | item_model->setHeaderData(COLUMN_NAME, Qt::Horizontal, tr("Name")); | 335 | RetranslateUI(); |
| 337 | item_model->setHeaderData(COLUMN_COMPATIBILITY, Qt::Horizontal, tr("Compatibility")); | ||
| 338 | 336 | ||
| 339 | item_model->setHeaderData(COLUMN_ADD_ONS, Qt::Horizontal, tr("Add-ons")); | ||
| 340 | tree_view->setColumnHidden(COLUMN_ADD_ONS, !UISettings::values.show_add_ons); | 337 | tree_view->setColumnHidden(COLUMN_ADD_ONS, !UISettings::values.show_add_ons); |
| 341 | item_model->setHeaderData(COLUMN_FILE_TYPE, Qt::Horizontal, tr("File type")); | ||
| 342 | item_model->setHeaderData(COLUMN_SIZE, Qt::Horizontal, tr("Size")); | ||
| 343 | item_model->setSortRole(GameListItemPath::SortRole); | 338 | item_model->setSortRole(GameListItemPath::SortRole); |
| 344 | 339 | ||
| 345 | connect(main_window, &GMainWindow::UpdateThemedIcons, this, &GameList::OnUpdateThemedIcons); | 340 | connect(main_window, &GMainWindow::UpdateThemedIcons, this, &GameList::OnUpdateThemedIcons); |
| @@ -753,6 +748,35 @@ void GameList::LoadCompatibilityList() { | |||
| 753 | } | 748 | } |
| 754 | } | 749 | } |
| 755 | 750 | ||
| 751 | void GameList::changeEvent(QEvent* event) { | ||
| 752 | if (event->type() == QEvent::LanguageChange) { | ||
| 753 | RetranslateUI(); | ||
| 754 | } | ||
| 755 | |||
| 756 | QWidget::changeEvent(event); | ||
| 757 | } | ||
| 758 | |||
| 759 | void GameList::RetranslateUI() { | ||
| 760 | item_model->setHeaderData(COLUMN_NAME, Qt::Horizontal, tr("Name")); | ||
| 761 | item_model->setHeaderData(COLUMN_COMPATIBILITY, Qt::Horizontal, tr("Compatibility")); | ||
| 762 | item_model->setHeaderData(COLUMN_ADD_ONS, Qt::Horizontal, tr("Add-ons")); | ||
| 763 | item_model->setHeaderData(COLUMN_FILE_TYPE, Qt::Horizontal, tr("File type")); | ||
| 764 | item_model->setHeaderData(COLUMN_SIZE, Qt::Horizontal, tr("Size")); | ||
| 765 | } | ||
| 766 | |||
| 767 | void GameListSearchField::changeEvent(QEvent* event) { | ||
| 768 | if (event->type() == QEvent::LanguageChange) { | ||
| 769 | RetranslateUI(); | ||
| 770 | } | ||
| 771 | |||
| 772 | QWidget::changeEvent(event); | ||
| 773 | } | ||
| 774 | |||
| 775 | void GameListSearchField::RetranslateUI() { | ||
| 776 | label_filter->setText(tr("Filter:")); | ||
| 777 | edit_filter->setPlaceholderText(tr("Enter pattern to filter")); | ||
| 778 | } | ||
| 779 | |||
| 756 | QStandardItemModel* GameList::GetModel() const { | 780 | QStandardItemModel* GameList::GetModel() const { |
| 757 | return item_model; | 781 | return item_model; |
| 758 | } | 782 | } |
diff --git a/src/yuzu/game_list.h b/src/yuzu/game_list.h index f783283c9..cdf085019 100644 --- a/src/yuzu/game_list.h +++ b/src/yuzu/game_list.h | |||
| @@ -140,6 +140,9 @@ private: | |||
| 140 | void AddPermDirPopup(QMenu& context_menu, QModelIndex selected); | 140 | void AddPermDirPopup(QMenu& context_menu, QModelIndex selected); |
| 141 | void AddFavoritesPopup(QMenu& context_menu); | 141 | void AddFavoritesPopup(QMenu& context_menu); |
| 142 | 142 | ||
| 143 | void changeEvent(QEvent*) override; | ||
| 144 | void RetranslateUI(); | ||
| 145 | |||
| 143 | std::shared_ptr<FileSys::VfsFilesystem> vfs; | 146 | std::shared_ptr<FileSys::VfsFilesystem> vfs; |
| 144 | FileSys::ManualContentProvider* provider; | 147 | FileSys::ManualContentProvider* provider; |
| 145 | GameListSearchField* search_field; | 148 | GameListSearchField* search_field; |
diff --git a/src/yuzu/game_list_p.h b/src/yuzu/game_list_p.h index e7667cf60..6198d1e4e 100644 --- a/src/yuzu/game_list_p.h +++ b/src/yuzu/game_list_p.h | |||
| @@ -294,7 +294,7 @@ public: | |||
| 294 | 294 | ||
| 295 | const int icon_size = UISettings::values.folder_icon_size.GetValue(); | 295 | const int icon_size = UISettings::values.folder_icon_size.GetValue(); |
| 296 | 296 | ||
| 297 | setData(QIcon::fromTheme(QStringLiteral("plus")) | 297 | setData(QIcon::fromTheme(QStringLiteral("list-add")) |
| 298 | .pixmap(icon_size) | 298 | .pixmap(icon_size) |
| 299 | .scaled(icon_size, icon_size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation), | 299 | .scaled(icon_size, icon_size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation), |
| 300 | Qt::DecorationRole); | 300 | Qt::DecorationRole); |
| @@ -353,6 +353,9 @@ public: | |||
| 353 | void setFocus(); | 353 | void setFocus(); |
| 354 | 354 | ||
| 355 | private: | 355 | private: |
| 356 | void changeEvent(QEvent*) override; | ||
| 357 | void RetranslateUI(); | ||
| 358 | |||
| 356 | class KeyReleaseEater : public QObject { | 359 | class KeyReleaseEater : public QObject { |
| 357 | public: | 360 | public: |
| 358 | explicit KeyReleaseEater(GameList* gamelist_, QObject* parent = nullptr); | 361 | explicit KeyReleaseEater(GameList* gamelist_, QObject* parent = nullptr); |
diff --git a/src/yuzu/loading_screen.cpp b/src/yuzu/loading_screen.cpp index e273744fd..e263a07a7 100644 --- a/src/yuzu/loading_screen.cpp +++ b/src/yuzu/loading_screen.cpp | |||
| @@ -147,6 +147,10 @@ void LoadingScreen::OnLoadProgress(VideoCore::LoadCallbackStage stage, std::size | |||
| 147 | ui->progress_bar->setMaximum(static_cast<int>(total)); | 147 | ui->progress_bar->setMaximum(static_cast<int>(total)); |
| 148 | previous_total = total; | 148 | previous_total = total; |
| 149 | } | 149 | } |
| 150 | // Reset the progress bar ranges if compilation is done | ||
| 151 | if (stage == VideoCore::LoadCallbackStage::Complete) { | ||
| 152 | ui->progress_bar->setRange(0, 0); | ||
| 153 | } | ||
| 150 | 154 | ||
| 151 | QString estimate; | 155 | QString estimate; |
| 152 | // If theres a drastic slowdown in the rate, then display an estimate | 156 | // If theres a drastic slowdown in the rate, then display an estimate |
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index f8c234082..a85adc072 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp | |||
| @@ -257,6 +257,18 @@ static QString PrettyProductName() { | |||
| 257 | return QSysInfo::prettyProductName(); | 257 | return QSysInfo::prettyProductName(); |
| 258 | } | 258 | } |
| 259 | 259 | ||
| 260 | bool GMainWindow::CheckDarkMode() { | ||
| 261 | #ifdef __linux__ | ||
| 262 | const QPalette test_palette(qApp->palette()); | ||
| 263 | const QColor text_color = test_palette.color(QPalette::Active, QPalette::Text); | ||
| 264 | const QColor window_color = test_palette.color(QPalette::Active, QPalette::Window); | ||
| 265 | return (text_color.value() > window_color.value()); | ||
| 266 | #else | ||
| 267 | // TODO: Windows | ||
| 268 | return false; | ||
| 269 | #endif // __linux__ | ||
| 270 | } | ||
| 271 | |||
| 260 | GMainWindow::GMainWindow(bool has_broken_vulkan) | 272 | GMainWindow::GMainWindow(bool has_broken_vulkan) |
| 261 | : ui{std::make_unique<Ui::MainWindow>()}, system{std::make_unique<Core::System>()}, | 273 | : ui{std::make_unique<Ui::MainWindow>()}, system{std::make_unique<Core::System>()}, |
| 262 | input_subsystem{std::make_shared<InputCommon::InputSubsystem>()}, | 274 | input_subsystem{std::make_shared<InputCommon::InputSubsystem>()}, |
| @@ -274,6 +286,13 @@ GMainWindow::GMainWindow(bool has_broken_vulkan) | |||
| 274 | ui->setupUi(this); | 286 | ui->setupUi(this); |
| 275 | statusBar()->hide(); | 287 | statusBar()->hide(); |
| 276 | 288 | ||
| 289 | // Check dark mode before a theme is loaded | ||
| 290 | os_dark_mode = CheckDarkMode(); | ||
| 291 | startup_icon_theme = QIcon::themeName(); | ||
| 292 | // fallback can only be set once, colorful theme icons are okay on both light/dark | ||
| 293 | QIcon::setFallbackThemeName(QStringLiteral("colorful")); | ||
| 294 | QIcon::setFallbackSearchPaths(QStringList(QStringLiteral(":/icons"))); | ||
| 295 | |||
| 277 | default_theme_paths = QIcon::themeSearchPaths(); | 296 | default_theme_paths = QIcon::themeSearchPaths(); |
| 278 | UpdateUITheme(); | 297 | UpdateUITheme(); |
| 279 | 298 | ||
| @@ -473,8 +492,6 @@ GMainWindow::~GMainWindow() { | |||
| 473 | delete render_window; | 492 | delete render_window; |
| 474 | } | 493 | } |
| 475 | 494 | ||
| 476 | system->GetRoomNetwork().Shutdown(); | ||
| 477 | |||
| 478 | #ifdef __linux__ | 495 | #ifdef __linux__ |
| 479 | ::close(sig_interrupt_fds[0]); | 496 | ::close(sig_interrupt_fds[0]); |
| 480 | ::close(sig_interrupt_fds[1]); | 497 | ::close(sig_interrupt_fds[1]); |
| @@ -843,7 +860,7 @@ void GMainWindow::InitializeWidgets() { | |||
| 843 | }); | 860 | }); |
| 844 | 861 | ||
| 845 | multiplayer_state = new MultiplayerState(this, game_list->GetModel(), ui->action_Leave_Room, | 862 | multiplayer_state = new MultiplayerState(this, game_list->GetModel(), ui->action_Leave_Room, |
| 846 | ui->action_Show_Room, system->GetRoomNetwork()); | 863 | ui->action_Show_Room, *system); |
| 847 | multiplayer_state->setVisible(false); | 864 | multiplayer_state->setVisible(false); |
| 848 | 865 | ||
| 849 | // Create status bar | 866 | // Create status bar |
| @@ -1075,7 +1092,7 @@ void GMainWindow::InitializeHotkeys() { | |||
| 1075 | connect_shortcut(QStringLiteral("Audio Mute/Unmute"), | 1092 | connect_shortcut(QStringLiteral("Audio Mute/Unmute"), |
| 1076 | [] { Settings::values.audio_muted = !Settings::values.audio_muted; }); | 1093 | [] { Settings::values.audio_muted = !Settings::values.audio_muted; }); |
| 1077 | connect_shortcut(QStringLiteral("Audio Volume Down"), [] { | 1094 | connect_shortcut(QStringLiteral("Audio Volume Down"), [] { |
| 1078 | const auto current_volume = static_cast<int>(Settings::values.volume.GetValue()); | 1095 | const auto current_volume = static_cast<s32>(Settings::values.volume.GetValue()); |
| 1079 | int step = 5; | 1096 | int step = 5; |
| 1080 | if (current_volume <= 30) { | 1097 | if (current_volume <= 30) { |
| 1081 | step = 2; | 1098 | step = 2; |
| @@ -1083,11 +1100,10 @@ void GMainWindow::InitializeHotkeys() { | |||
| 1083 | if (current_volume <= 6) { | 1100 | if (current_volume <= 6) { |
| 1084 | step = 1; | 1101 | step = 1; |
| 1085 | } | 1102 | } |
| 1086 | const auto new_volume = std::max(current_volume - step, 0); | 1103 | Settings::values.volume.SetValue(std::max(current_volume - step, 0)); |
| 1087 | Settings::values.volume.SetValue(static_cast<u8>(new_volume)); | ||
| 1088 | }); | 1104 | }); |
| 1089 | connect_shortcut(QStringLiteral("Audio Volume Up"), [] { | 1105 | connect_shortcut(QStringLiteral("Audio Volume Up"), [] { |
| 1090 | const auto current_volume = static_cast<int>(Settings::values.volume.GetValue()); | 1106 | const auto current_volume = static_cast<s32>(Settings::values.volume.GetValue()); |
| 1091 | int step = 5; | 1107 | int step = 5; |
| 1092 | if (current_volume < 30) { | 1108 | if (current_volume < 30) { |
| 1093 | step = 2; | 1109 | step = 2; |
| @@ -1095,8 +1111,7 @@ void GMainWindow::InitializeHotkeys() { | |||
| 1095 | if (current_volume < 6) { | 1111 | if (current_volume < 6) { |
| 1096 | step = 1; | 1112 | step = 1; |
| 1097 | } | 1113 | } |
| 1098 | const auto new_volume = std::min(current_volume + step, 100); | 1114 | Settings::values.volume.SetValue(current_volume + step); |
| 1099 | Settings::values.volume.SetValue(static_cast<u8>(new_volume)); | ||
| 1100 | }); | 1115 | }); |
| 1101 | connect_shortcut(QStringLiteral("Toggle Framerate Limit"), [] { | 1116 | connect_shortcut(QStringLiteral("Toggle Framerate Limit"), [] { |
| 1102 | Settings::values.use_speed_limit.SetValue(!Settings::values.use_speed_limit.GetValue()); | 1117 | Settings::values.use_speed_limit.SetValue(!Settings::values.use_speed_limit.GetValue()); |
| @@ -1588,17 +1603,18 @@ bool GMainWindow::LoadROM(const QString& filename, u64 program_id, std::size_t p | |||
| 1588 | return true; | 1603 | return true; |
| 1589 | } | 1604 | } |
| 1590 | 1605 | ||
| 1591 | void GMainWindow::SelectAndSetCurrentUser() { | 1606 | bool GMainWindow::SelectAndSetCurrentUser() { |
| 1592 | QtProfileSelectionDialog dialog(system->HIDCore(), this); | 1607 | QtProfileSelectionDialog dialog(system->HIDCore(), this); |
| 1593 | dialog.setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint | Qt::WindowTitleHint | | 1608 | dialog.setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint | Qt::WindowTitleHint | |
| 1594 | Qt::WindowSystemMenuHint | Qt::WindowCloseButtonHint); | 1609 | Qt::WindowSystemMenuHint | Qt::WindowCloseButtonHint); |
| 1595 | dialog.setWindowModality(Qt::WindowModal); | 1610 | dialog.setWindowModality(Qt::WindowModal); |
| 1596 | 1611 | ||
| 1597 | if (dialog.exec() == QDialog::Rejected) { | 1612 | if (dialog.exec() == QDialog::Rejected) { |
| 1598 | return; | 1613 | return false; |
| 1599 | } | 1614 | } |
| 1600 | 1615 | ||
| 1601 | Settings::values.current_user = dialog.GetIndex(); | 1616 | Settings::values.current_user = dialog.GetIndex(); |
| 1617 | return true; | ||
| 1602 | } | 1618 | } |
| 1603 | 1619 | ||
| 1604 | void GMainWindow::BootGame(const QString& filename, u64 program_id, std::size_t program_index, | 1620 | void GMainWindow::BootGame(const QString& filename, u64 program_id, std::size_t program_index, |
| @@ -1632,11 +1648,14 @@ void GMainWindow::BootGame(const QString& filename, u64 program_id, std::size_t | |||
| 1632 | Settings::LogSettings(); | 1648 | Settings::LogSettings(); |
| 1633 | 1649 | ||
| 1634 | if (UISettings::values.select_user_on_boot) { | 1650 | if (UISettings::values.select_user_on_boot) { |
| 1635 | SelectAndSetCurrentUser(); | 1651 | if (SelectAndSetCurrentUser() == false) { |
| 1652 | return; | ||
| 1653 | } | ||
| 1636 | } | 1654 | } |
| 1637 | 1655 | ||
| 1638 | if (!LoadROM(filename, program_id, program_index)) | 1656 | if (!LoadROM(filename, program_id, program_index)) { |
| 1639 | return; | 1657 | return; |
| 1658 | } | ||
| 1640 | 1659 | ||
| 1641 | system->SetShuttingDown(false); | 1660 | system->SetShuttingDown(false); |
| 1642 | 1661 | ||
| @@ -3334,7 +3353,8 @@ void GMainWindow::MigrateConfigFiles() { | |||
| 3334 | } | 3353 | } |
| 3335 | const auto origin = config_dir_fs_path / filename; | 3354 | const auto origin = config_dir_fs_path / filename; |
| 3336 | const auto destination = config_dir_fs_path / "custom" / filename; | 3355 | const auto destination = config_dir_fs_path / "custom" / filename; |
| 3337 | LOG_INFO(Frontend, "Migrating config file from {} to {}", origin, destination); | 3356 | LOG_INFO(Frontend, "Migrating config file from {} to {}", origin.string(), |
| 3357 | destination.string()); | ||
| 3338 | if (!Common::FS::RenameFile(origin, destination)) { | 3358 | if (!Common::FS::RenameFile(origin, destination)) { |
| 3339 | // Delete the old config file if one already exists in the new location. | 3359 | // Delete the old config file if one already exists in the new location. |
| 3340 | Common::FS::RemoveFile(origin); | 3360 | Common::FS::RemoveFile(origin); |
| @@ -3809,6 +3829,7 @@ void GMainWindow::closeEvent(QCloseEvent* event) { | |||
| 3809 | 3829 | ||
| 3810 | render_window->close(); | 3830 | render_window->close(); |
| 3811 | multiplayer_state->Close(); | 3831 | multiplayer_state->Close(); |
| 3832 | system->GetRoomNetwork().Shutdown(); | ||
| 3812 | 3833 | ||
| 3813 | QWidget::closeEvent(event); | 3834 | QWidget::closeEvent(event); |
| 3814 | } | 3835 | } |
| @@ -3930,8 +3951,21 @@ void GMainWindow::filterBarSetChecked(bool state) { | |||
| 3930 | emit(OnToggleFilterBar()); | 3951 | emit(OnToggleFilterBar()); |
| 3931 | } | 3952 | } |
| 3932 | 3953 | ||
| 3954 | static void AdjustLinkColor() { | ||
| 3955 | QPalette new_pal(qApp->palette()); | ||
| 3956 | if (UISettings::IsDarkTheme()) { | ||
| 3957 | new_pal.setColor(QPalette::Link, QColor(0, 190, 255, 255)); | ||
| 3958 | } else { | ||
| 3959 | new_pal.setColor(QPalette::Link, QColor(0, 140, 200, 255)); | ||
| 3960 | } | ||
| 3961 | if (qApp->palette().color(QPalette::Link) != new_pal.color(QPalette::Link)) { | ||
| 3962 | qApp->setPalette(new_pal); | ||
| 3963 | } | ||
| 3964 | } | ||
| 3965 | |||
| 3933 | void GMainWindow::UpdateUITheme() { | 3966 | void GMainWindow::UpdateUITheme() { |
| 3934 | const QString default_theme = QStringLiteral("default"); | 3967 | const QString default_theme = |
| 3968 | QString::fromUtf8(UISettings::themes[static_cast<size_t>(Config::default_theme)].second); | ||
| 3935 | QString current_theme = UISettings::values.theme; | 3969 | QString current_theme = UISettings::values.theme; |
| 3936 | QStringList theme_paths(default_theme_paths); | 3970 | QStringList theme_paths(default_theme_paths); |
| 3937 | 3971 | ||
| @@ -3939,6 +3973,23 @@ void GMainWindow::UpdateUITheme() { | |||
| 3939 | current_theme = default_theme; | 3973 | current_theme = default_theme; |
| 3940 | } | 3974 | } |
| 3941 | 3975 | ||
| 3976 | #ifdef _WIN32 | ||
| 3977 | QIcon::setThemeName(current_theme); | ||
| 3978 | AdjustLinkColor(); | ||
| 3979 | #else | ||
| 3980 | if (current_theme == QStringLiteral("default") || current_theme == QStringLiteral("colorful")) { | ||
| 3981 | QIcon::setThemeName(current_theme == QStringLiteral("colorful") ? current_theme | ||
| 3982 | : startup_icon_theme); | ||
| 3983 | QIcon::setThemeSearchPaths(theme_paths); | ||
| 3984 | if (CheckDarkMode()) { | ||
| 3985 | current_theme = QStringLiteral("default_dark"); | ||
| 3986 | } | ||
| 3987 | } else { | ||
| 3988 | QIcon::setThemeName(current_theme); | ||
| 3989 | QIcon::setThemeSearchPaths(QStringList(QStringLiteral(":/icons"))); | ||
| 3990 | AdjustLinkColor(); | ||
| 3991 | } | ||
| 3992 | #endif | ||
| 3942 | if (current_theme != default_theme) { | 3993 | if (current_theme != default_theme) { |
| 3943 | QString theme_uri{QStringLiteral(":%1/style.qss").arg(current_theme)}; | 3994 | QString theme_uri{QStringLiteral(":%1/style.qss").arg(current_theme)}; |
| 3944 | QFile f(theme_uri); | 3995 | QFile f(theme_uri); |
| @@ -3961,25 +4012,9 @@ void GMainWindow::UpdateUITheme() { | |||
| 3961 | qApp->setStyleSheet({}); | 4012 | qApp->setStyleSheet({}); |
| 3962 | setStyleSheet({}); | 4013 | setStyleSheet({}); |
| 3963 | } | 4014 | } |
| 3964 | |||
| 3965 | QPalette new_pal(qApp->palette()); | ||
| 3966 | if (UISettings::IsDarkTheme()) { | ||
| 3967 | new_pal.setColor(QPalette::Link, QColor(0, 190, 255, 255)); | ||
| 3968 | } else { | ||
| 3969 | new_pal.setColor(QPalette::Link, QColor(0, 140, 200, 255)); | ||
| 3970 | } | ||
| 3971 | qApp->setPalette(new_pal); | ||
| 3972 | |||
| 3973 | QIcon::setThemeName(current_theme); | ||
| 3974 | QIcon::setThemeSearchPaths(theme_paths); | ||
| 3975 | } | 4015 | } |
| 3976 | 4016 | ||
| 3977 | void GMainWindow::LoadTranslation() { | 4017 | void GMainWindow::LoadTranslation() { |
| 3978 | // If the selected language is English, no need to install any translation | ||
| 3979 | if (UISettings::values.language == QStringLiteral("en")) { | ||
| 3980 | return; | ||
| 3981 | } | ||
| 3982 | |||
| 3983 | bool loaded; | 4018 | bool loaded; |
| 3984 | 4019 | ||
| 3985 | if (UISettings::values.language.isEmpty()) { | 4020 | if (UISettings::values.language.isEmpty()) { |
| @@ -4022,6 +4057,26 @@ void GMainWindow::SetDiscordEnabled([[maybe_unused]] bool state) { | |||
| 4022 | discord_rpc->Update(); | 4057 | discord_rpc->Update(); |
| 4023 | } | 4058 | } |
| 4024 | 4059 | ||
| 4060 | void GMainWindow::changeEvent(QEvent* event) { | ||
| 4061 | #ifdef __linux__ | ||
| 4062 | // PaletteChange event appears to only reach so far into the GUI, explicitly asking to | ||
| 4063 | // UpdateUITheme is a decent work around | ||
| 4064 | if (event->type() == QEvent::PaletteChange) { | ||
| 4065 | const QPalette test_palette(qApp->palette()); | ||
| 4066 | const QString current_theme = UISettings::values.theme; | ||
| 4067 | // Keeping eye on QPalette::Window to avoid looping. QPalette::Text might be useful too | ||
| 4068 | static QColor last_window_color; | ||
| 4069 | const QColor window_color = test_palette.color(QPalette::Active, QPalette::Window); | ||
| 4070 | if (last_window_color != window_color && (current_theme == QStringLiteral("default") || | ||
| 4071 | current_theme == QStringLiteral("colorful"))) { | ||
| 4072 | UpdateUITheme(); | ||
| 4073 | } | ||
| 4074 | last_window_color = window_color; | ||
| 4075 | } | ||
| 4076 | #endif // __linux__ | ||
| 4077 | QWidget::changeEvent(event); | ||
| 4078 | } | ||
| 4079 | |||
| 4025 | #ifdef main | 4080 | #ifdef main |
| 4026 | #undef main | 4081 | #undef main |
| 4027 | #endif | 4082 | #endif |
| @@ -4067,6 +4122,15 @@ int main(int argc, char* argv[]) { | |||
| 4067 | QCoreApplication::setAttribute(Qt::AA_DontCheckOpenGLContextThreadAffinity); | 4122 | QCoreApplication::setAttribute(Qt::AA_DontCheckOpenGLContextThreadAffinity); |
| 4068 | QApplication app(argc, argv); | 4123 | QApplication app(argc, argv); |
| 4069 | 4124 | ||
| 4125 | // Workaround for QTBUG-85409, for Suzhou numerals the number 1 is actually \u3021 | ||
| 4126 | // so we can see if we get \u3008 instead | ||
| 4127 | // TL;DR all other number formats are consecutive in unicode code points | ||
| 4128 | // This bug is fixed in Qt6, specifically 6.0.0-alpha1 | ||
| 4129 | const QLocale locale = QLocale::system(); | ||
| 4130 | if (QStringLiteral("\u3008") == locale.toString(1)) { | ||
| 4131 | QLocale::setDefault(QLocale::system().name()); | ||
| 4132 | } | ||
| 4133 | |||
| 4070 | // Qt changes the locale and causes issues in float conversion using std::to_string() when | 4134 | // Qt changes the locale and causes issues in float conversion using std::to_string() when |
| 4071 | // generating shaders | 4135 | // generating shaders |
| 4072 | setlocale(LC_ALL, "C"); | 4136 | setlocale(LC_ALL, "C"); |
diff --git a/src/yuzu/main.h b/src/yuzu/main.h index 23b67a14e..1ae2b93d9 100644 --- a/src/yuzu/main.h +++ b/src/yuzu/main.h | |||
| @@ -218,7 +218,7 @@ private: | |||
| 218 | void SetDiscordEnabled(bool state); | 218 | void SetDiscordEnabled(bool state); |
| 219 | void LoadAmiibo(const QString& filename); | 219 | void LoadAmiibo(const QString& filename); |
| 220 | 220 | ||
| 221 | void SelectAndSetCurrentUser(); | 221 | bool SelectAndSetCurrentUser(); |
| 222 | 222 | ||
| 223 | /** | 223 | /** |
| 224 | * Stores the filename in the recently loaded files list. | 224 | * Stores the filename in the recently loaded files list. |
| @@ -251,6 +251,7 @@ private: | |||
| 251 | bool ConfirmForceLockedExit(); | 251 | bool ConfirmForceLockedExit(); |
| 252 | void RequestGameExit(); | 252 | void RequestGameExit(); |
| 253 | void RequestGameResume(); | 253 | void RequestGameResume(); |
| 254 | void changeEvent(QEvent* event) override; | ||
| 254 | void closeEvent(QCloseEvent* event) override; | 255 | void closeEvent(QCloseEvent* event) override; |
| 255 | 256 | ||
| 256 | #ifdef __linux__ | 257 | #ifdef __linux__ |
| @@ -347,6 +348,7 @@ private: | |||
| 347 | void OpenURL(const QUrl& url); | 348 | void OpenURL(const QUrl& url); |
| 348 | void LoadTranslation(); | 349 | void LoadTranslation(); |
| 349 | void OpenPerGameConfiguration(u64 title_id, const std::string& file_name); | 350 | void OpenPerGameConfiguration(u64 title_id, const std::string& file_name); |
| 351 | bool CheckDarkMode(); | ||
| 350 | 352 | ||
| 351 | QString GetTasStateDescription() const; | 353 | QString GetTasStateDescription() const; |
| 352 | 354 | ||
| @@ -392,6 +394,9 @@ private: | |||
| 392 | QTimer mouse_hide_timer; | 394 | QTimer mouse_hide_timer; |
| 393 | QTimer mouse_center_timer; | 395 | QTimer mouse_center_timer; |
| 394 | 396 | ||
| 397 | QString startup_icon_theme; | ||
| 398 | bool os_dark_mode = false; | ||
| 399 | |||
| 395 | // FS | 400 | // FS |
| 396 | std::shared_ptr<FileSys::VfsFilesystem> vfs; | 401 | std::shared_ptr<FileSys::VfsFilesystem> vfs; |
| 397 | std::unique_ptr<FileSys::ManualContentProvider> provider; | 402 | std::unique_ptr<FileSys::ManualContentProvider> provider; |
diff --git a/src/yuzu/multiplayer/chat_room.cpp b/src/yuzu/multiplayer/chat_room.cpp index 5837b36ab..9e672f82e 100644 --- a/src/yuzu/multiplayer/chat_room.cpp +++ b/src/yuzu/multiplayer/chat_room.cpp | |||
| @@ -16,7 +16,7 @@ | |||
| 16 | #include <QUrl> | 16 | #include <QUrl> |
| 17 | #include <QtConcurrent/QtConcurrentRun> | 17 | #include <QtConcurrent/QtConcurrentRun> |
| 18 | #include "common/logging/log.h" | 18 | #include "common/logging/log.h" |
| 19 | #include "core/announce_multiplayer_session.h" | 19 | #include "network/announce_multiplayer_session.h" |
| 20 | #include "ui_chat_room.h" | 20 | #include "ui_chat_room.h" |
| 21 | #include "yuzu/game_list_p.h" | 21 | #include "yuzu/game_list_p.h" |
| 22 | #include "yuzu/multiplayer/chat_room.h" | 22 | #include "yuzu/multiplayer/chat_room.h" |
| @@ -122,19 +122,22 @@ public: | |||
| 122 | static const int UsernameRole = Qt::UserRole + 2; | 122 | static const int UsernameRole = Qt::UserRole + 2; |
| 123 | static const int AvatarUrlRole = Qt::UserRole + 3; | 123 | static const int AvatarUrlRole = Qt::UserRole + 3; |
| 124 | static const int GameNameRole = Qt::UserRole + 4; | 124 | static const int GameNameRole = Qt::UserRole + 4; |
| 125 | static const int GameVersionRole = Qt::UserRole + 5; | ||
| 125 | 126 | ||
| 126 | PlayerListItem() = default; | 127 | PlayerListItem() = default; |
| 127 | explicit PlayerListItem(const std::string& nickname, const std::string& username, | 128 | explicit PlayerListItem(const std::string& nickname, const std::string& username, |
| 128 | const std::string& avatar_url, const std::string& game_name) { | 129 | const std::string& avatar_url, |
| 130 | const AnnounceMultiplayerRoom::GameInfo& game_info) { | ||
| 129 | setEditable(false); | 131 | setEditable(false); |
| 130 | setData(QString::fromStdString(nickname), NicknameRole); | 132 | setData(QString::fromStdString(nickname), NicknameRole); |
| 131 | setData(QString::fromStdString(username), UsernameRole); | 133 | setData(QString::fromStdString(username), UsernameRole); |
| 132 | setData(QString::fromStdString(avatar_url), AvatarUrlRole); | 134 | setData(QString::fromStdString(avatar_url), AvatarUrlRole); |
| 133 | if (game_name.empty()) { | 135 | if (game_info.name.empty()) { |
| 134 | setData(QObject::tr("Not playing a game"), GameNameRole); | 136 | setData(QObject::tr("Not playing a game"), GameNameRole); |
| 135 | } else { | 137 | } else { |
| 136 | setData(QString::fromStdString(game_name), GameNameRole); | 138 | setData(QString::fromStdString(game_info.name), GameNameRole); |
| 137 | } | 139 | } |
| 140 | setData(QString::fromStdString(game_info.version), GameVersionRole); | ||
| 138 | } | 141 | } |
| 139 | 142 | ||
| 140 | QVariant data(int role) const override { | 143 | QVariant data(int role) const override { |
| @@ -149,7 +152,13 @@ public: | |||
| 149 | } else { | 152 | } else { |
| 150 | name = QStringLiteral("%1 (%2)").arg(nickname, username); | 153 | name = QStringLiteral("%1 (%2)").arg(nickname, username); |
| 151 | } | 154 | } |
| 152 | return QStringLiteral("%1\n %2").arg(name, data(GameNameRole).toString()); | 155 | const QString version = data(GameVersionRole).toString(); |
| 156 | QString version_string; | ||
| 157 | if (!version.isEmpty()) { | ||
| 158 | version_string = QStringLiteral("(%1)").arg(version); | ||
| 159 | } | ||
| 160 | return QStringLiteral("%1\n %2 %3") | ||
| 161 | .arg(name, data(GameNameRole).toString(), version_string); | ||
| 153 | } | 162 | } |
| 154 | }; | 163 | }; |
| 155 | 164 | ||
| @@ -167,6 +176,10 @@ ChatRoom::ChatRoom(QWidget* parent) : QWidget(parent), ui(std::make_unique<Ui::C | |||
| 167 | 176 | ||
| 168 | ui->chat_history->document()->setMaximumBlockCount(max_chat_lines); | 177 | ui->chat_history->document()->setMaximumBlockCount(max_chat_lines); |
| 169 | 178 | ||
| 179 | auto font = ui->chat_history->font(); | ||
| 180 | font.setPointSizeF(10); | ||
| 181 | ui->chat_history->setFont(font); | ||
| 182 | |||
| 170 | // register the network structs to use in slots and signals | 183 | // register the network structs to use in slots and signals |
| 171 | qRegisterMetaType<Network::ChatEntry>(); | 184 | qRegisterMetaType<Network::ChatEntry>(); |
| 172 | qRegisterMetaType<Network::StatusMessageEntry>(); | 185 | qRegisterMetaType<Network::StatusMessageEntry>(); |
| @@ -316,21 +329,19 @@ void ChatRoom::OnStatusMessageReceive(const Network::StatusMessageEntry& status_ | |||
| 316 | } | 329 | } |
| 317 | 330 | ||
| 318 | void ChatRoom::OnSendChat() { | 331 | void ChatRoom::OnSendChat() { |
| 319 | if (auto room = room_network->GetRoomMember().lock()) { | 332 | if (auto room_member = room_network->GetRoomMember().lock()) { |
| 320 | if (room->GetState() != Network::RoomMember::State::Joined && | 333 | if (!room_member->IsConnected()) { |
| 321 | room->GetState() != Network::RoomMember::State::Moderator) { | ||
| 322 | |||
| 323 | return; | 334 | return; |
| 324 | } | 335 | } |
| 325 | auto message = ui->chat_message->text().toStdString(); | 336 | auto message = ui->chat_message->text().toStdString(); |
| 326 | if (!ValidateMessage(message)) { | 337 | if (!ValidateMessage(message)) { |
| 327 | return; | 338 | return; |
| 328 | } | 339 | } |
| 329 | auto nick = room->GetNickname(); | 340 | auto nick = room_member->GetNickname(); |
| 330 | auto username = room->GetUsername(); | 341 | auto username = room_member->GetUsername(); |
| 331 | Network::ChatEntry chat{nick, username, message}; | 342 | Network::ChatEntry chat{nick, username, message}; |
| 332 | 343 | ||
| 333 | auto members = room->GetMemberInformation(); | 344 | auto members = room_member->GetMemberInformation(); |
| 334 | auto it = std::find_if(members.begin(), members.end(), | 345 | auto it = std::find_if(members.begin(), members.end(), |
| 335 | [&chat](const Network::RoomMember::MemberInformation& member) { | 346 | [&chat](const Network::RoomMember::MemberInformation& member) { |
| 336 | return member.nickname == chat.nickname && | 347 | return member.nickname == chat.nickname && |
| @@ -341,7 +352,7 @@ void ChatRoom::OnSendChat() { | |||
| 341 | } | 352 | } |
| 342 | auto player = std::distance(members.begin(), it); | 353 | auto player = std::distance(members.begin(), it); |
| 343 | ChatMessage m(chat, *room_network); | 354 | ChatMessage m(chat, *room_network); |
| 344 | room->SendChatMessage(message); | 355 | room_member->SendChatMessage(message); |
| 345 | AppendChatMessage(m.GetPlayerChatMessage(player)); | 356 | AppendChatMessage(m.GetPlayerChatMessage(player)); |
| 346 | ui->chat_message->clear(); | 357 | ui->chat_message->clear(); |
| 347 | } | 358 | } |
| @@ -368,7 +379,7 @@ void ChatRoom::SetPlayerList(const Network::RoomMember::MemberList& member_list) | |||
| 368 | if (member.nickname.empty()) | 379 | if (member.nickname.empty()) |
| 369 | continue; | 380 | continue; |
| 370 | QStandardItem* name_item = new PlayerListItem(member.nickname, member.username, | 381 | QStandardItem* name_item = new PlayerListItem(member.nickname, member.username, |
| 371 | member.avatar_url, member.game_info.name); | 382 | member.avatar_url, member.game_info); |
| 372 | 383 | ||
| 373 | #ifdef ENABLE_WEB_SERVICE | 384 | #ifdef ENABLE_WEB_SERVICE |
| 374 | if (!icon_cache.count(member.avatar_url) && !member.avatar_url.empty()) { | 385 | if (!icon_cache.count(member.avatar_url) && !member.avatar_url.empty()) { |
diff --git a/src/yuzu/multiplayer/client_room.cpp b/src/yuzu/multiplayer/client_room.cpp index a9859ed70..b34a8d004 100644 --- a/src/yuzu/multiplayer/client_room.cpp +++ b/src/yuzu/multiplayer/client_room.cpp | |||
| @@ -10,7 +10,7 @@ | |||
| 10 | #include <QTime> | 10 | #include <QTime> |
| 11 | #include <QtConcurrent/QtConcurrentRun> | 11 | #include <QtConcurrent/QtConcurrentRun> |
| 12 | #include "common/logging/log.h" | 12 | #include "common/logging/log.h" |
| 13 | #include "core/announce_multiplayer_session.h" | 13 | #include "network/announce_multiplayer_session.h" |
| 14 | #include "ui_client_room.h" | 14 | #include "ui_client_room.h" |
| 15 | #include "yuzu/game_list_p.h" | 15 | #include "yuzu/game_list_p.h" |
| 16 | #include "yuzu/multiplayer/client_room.h" | 16 | #include "yuzu/multiplayer/client_room.h" |
| @@ -74,7 +74,6 @@ void ClientRoomWindow::OnRoomUpdate(const Network::RoomInformation& info) { | |||
| 74 | void ClientRoomWindow::OnStateChange(const Network::RoomMember::State& state) { | 74 | void ClientRoomWindow::OnStateChange(const Network::RoomMember::State& state) { |
| 75 | if (state == Network::RoomMember::State::Joined || | 75 | if (state == Network::RoomMember::State::Joined || |
| 76 | state == Network::RoomMember::State::Moderator) { | 76 | state == Network::RoomMember::State::Moderator) { |
| 77 | |||
| 78 | ui->chat->Clear(); | 77 | ui->chat->Clear(); |
| 79 | ui->chat->AppendStatusMessage(tr("Connected")); | 78 | ui->chat->AppendStatusMessage(tr("Connected")); |
| 80 | SetModPerms(state == Network::RoomMember::State::Moderator); | 79 | SetModPerms(state == Network::RoomMember::State::Moderator); |
diff --git a/src/yuzu/multiplayer/direct_connect.cpp b/src/yuzu/multiplayer/direct_connect.cpp index 9000c4531..017063074 100644 --- a/src/yuzu/multiplayer/direct_connect.cpp +++ b/src/yuzu/multiplayer/direct_connect.cpp | |||
| @@ -8,6 +8,8 @@ | |||
| 8 | #include <QString> | 8 | #include <QString> |
| 9 | #include <QtConcurrent/QtConcurrentRun> | 9 | #include <QtConcurrent/QtConcurrentRun> |
| 10 | #include "common/settings.h" | 10 | #include "common/settings.h" |
| 11 | #include "core/core.h" | ||
| 12 | #include "core/internal_network/network_interface.h" | ||
| 11 | #include "network/network.h" | 13 | #include "network/network.h" |
| 12 | #include "ui_direct_connect.h" | 14 | #include "ui_direct_connect.h" |
| 13 | #include "yuzu/main.h" | 15 | #include "yuzu/main.h" |
| @@ -20,9 +22,10 @@ | |||
| 20 | 22 | ||
| 21 | enum class ConnectionType : u8 { TraversalServer, IP }; | 23 | enum class ConnectionType : u8 { TraversalServer, IP }; |
| 22 | 24 | ||
| 23 | DirectConnectWindow::DirectConnectWindow(Network::RoomNetwork& room_network_, QWidget* parent) | 25 | DirectConnectWindow::DirectConnectWindow(Core::System& system_, QWidget* parent) |
| 24 | : QDialog(parent, Qt::WindowTitleHint | Qt::WindowCloseButtonHint | Qt::WindowSystemMenuHint), | 26 | : QDialog(parent, Qt::WindowTitleHint | Qt::WindowCloseButtonHint | Qt::WindowSystemMenuHint), |
| 25 | ui(std::make_unique<Ui::DirectConnect>()), room_network{room_network_} { | 27 | ui(std::make_unique<Ui::DirectConnect>()), system{system_}, room_network{ |
| 28 | system.GetRoomNetwork()} { | ||
| 26 | 29 | ||
| 27 | ui->setupUi(this); | 30 | ui->setupUi(this); |
| 28 | 31 | ||
| @@ -53,10 +56,20 @@ void DirectConnectWindow::RetranslateUi() { | |||
| 53 | } | 56 | } |
| 54 | 57 | ||
| 55 | void DirectConnectWindow::Connect() { | 58 | void DirectConnectWindow::Connect() { |
| 59 | if (!Network::GetSelectedNetworkInterface()) { | ||
| 60 | NetworkMessage::ErrorManager::ShowError( | ||
| 61 | NetworkMessage::ErrorManager::NO_INTERFACE_SELECTED); | ||
| 62 | return; | ||
| 63 | } | ||
| 56 | if (!ui->nickname->hasAcceptableInput()) { | 64 | if (!ui->nickname->hasAcceptableInput()) { |
| 57 | NetworkMessage::ErrorManager::ShowError(NetworkMessage::ErrorManager::USERNAME_NOT_VALID); | 65 | NetworkMessage::ErrorManager::ShowError(NetworkMessage::ErrorManager::USERNAME_NOT_VALID); |
| 58 | return; | 66 | return; |
| 59 | } | 67 | } |
| 68 | if (system.IsPoweredOn()) { | ||
| 69 | if (!NetworkMessage::WarnGameRunning()) { | ||
| 70 | return; | ||
| 71 | } | ||
| 72 | } | ||
| 60 | if (const auto member = room_network.GetRoomMember().lock()) { | 73 | if (const auto member = room_network.GetRoomMember().lock()) { |
| 61 | // Prevent the user from trying to join a room while they are already joining. | 74 | // Prevent the user from trying to join a room while they are already joining. |
| 62 | if (member->GetState() == Network::RoomMember::State::Joining) { | 75 | if (member->GetState() == Network::RoomMember::State::Joining) { |
| @@ -97,9 +110,9 @@ void DirectConnectWindow::Connect() { | |||
| 97 | QFuture<void> f = QtConcurrent::run([&] { | 110 | QFuture<void> f = QtConcurrent::run([&] { |
| 98 | if (auto room_member = room_network.GetRoomMember().lock()) { | 111 | if (auto room_member = room_network.GetRoomMember().lock()) { |
| 99 | auto port = UISettings::values.multiplayer_port.GetValue(); | 112 | auto port = UISettings::values.multiplayer_port.GetValue(); |
| 100 | room_member->Join(ui->nickname->text().toStdString(), "", | 113 | room_member->Join(ui->nickname->text().toStdString(), |
| 101 | ui->ip->text().toStdString().c_str(), port, 0, | 114 | ui->ip->text().toStdString().c_str(), port, 0, Network::NoPreferredIP, |
| 102 | Network::NoPreferredMac, ui->password->text().toStdString().c_str()); | 115 | ui->password->text().toStdString().c_str()); |
| 103 | } | 116 | } |
| 104 | }); | 117 | }); |
| 105 | watcher->setFuture(f); | 118 | watcher->setFuture(f); |
| @@ -121,9 +134,7 @@ void DirectConnectWindow::OnConnection() { | |||
| 121 | EndConnecting(); | 134 | EndConnecting(); |
| 122 | 135 | ||
| 123 | if (auto room_member = room_network.GetRoomMember().lock()) { | 136 | if (auto room_member = room_network.GetRoomMember().lock()) { |
| 124 | if (room_member->GetState() == Network::RoomMember::State::Joined || | 137 | if (room_member->IsConnected()) { |
| 125 | room_member->GetState() == Network::RoomMember::State::Moderator) { | ||
| 126 | |||
| 127 | close(); | 138 | close(); |
| 128 | } | 139 | } |
| 129 | } | 140 | } |
diff --git a/src/yuzu/multiplayer/direct_connect.h b/src/yuzu/multiplayer/direct_connect.h index 4e1043053..e39dd1e0d 100644 --- a/src/yuzu/multiplayer/direct_connect.h +++ b/src/yuzu/multiplayer/direct_connect.h | |||
| @@ -12,11 +12,15 @@ namespace Ui { | |||
| 12 | class DirectConnect; | 12 | class DirectConnect; |
| 13 | } | 13 | } |
| 14 | 14 | ||
| 15 | namespace Core { | ||
| 16 | class System; | ||
| 17 | } | ||
| 18 | |||
| 15 | class DirectConnectWindow : public QDialog { | 19 | class DirectConnectWindow : public QDialog { |
| 16 | Q_OBJECT | 20 | Q_OBJECT |
| 17 | 21 | ||
| 18 | public: | 22 | public: |
| 19 | explicit DirectConnectWindow(Network::RoomNetwork& room_network_, QWidget* parent = nullptr); | 23 | explicit DirectConnectWindow(Core::System& system_, QWidget* parent = nullptr); |
| 20 | ~DirectConnectWindow(); | 24 | ~DirectConnectWindow(); |
| 21 | 25 | ||
| 22 | void RetranslateUi(); | 26 | void RetranslateUi(); |
| @@ -39,5 +43,6 @@ private: | |||
| 39 | QFutureWatcher<void>* watcher; | 43 | QFutureWatcher<void>* watcher; |
| 40 | std::unique_ptr<Ui::DirectConnect> ui; | 44 | std::unique_ptr<Ui::DirectConnect> ui; |
| 41 | Validation validation; | 45 | Validation validation; |
| 46 | Core::System& system; | ||
| 42 | Network::RoomNetwork& room_network; | 47 | Network::RoomNetwork& room_network; |
| 43 | }; | 48 | }; |
diff --git a/src/yuzu/multiplayer/direct_connect.ui b/src/yuzu/multiplayer/direct_connect.ui index 681b6bf69..57d6ec25a 100644 --- a/src/yuzu/multiplayer/direct_connect.ui +++ b/src/yuzu/multiplayer/direct_connect.ui | |||
| @@ -83,7 +83,7 @@ | |||
| 83 | <number>5</number> | 83 | <number>5</number> |
| 84 | </property> | 84 | </property> |
| 85 | <property name="placeholderText"> | 85 | <property name="placeholderText"> |
| 86 | <string>24872</string> | 86 | <string notr="true" extracomment="placeholder string that tells user default port">24872</string> |
| 87 | </property> | 87 | </property> |
| 88 | </widget> | 88 | </widget> |
| 89 | </item> | 89 | </item> |
diff --git a/src/yuzu/multiplayer/host_room.cpp b/src/yuzu/multiplayer/host_room.cpp index cb9464b2b..0c6adfd04 100644 --- a/src/yuzu/multiplayer/host_room.cpp +++ b/src/yuzu/multiplayer/host_room.cpp | |||
| @@ -12,7 +12,9 @@ | |||
| 12 | #include <QtConcurrent/QtConcurrentRun> | 12 | #include <QtConcurrent/QtConcurrentRun> |
| 13 | #include "common/logging/log.h" | 13 | #include "common/logging/log.h" |
| 14 | #include "common/settings.h" | 14 | #include "common/settings.h" |
| 15 | #include "core/announce_multiplayer_session.h" | 15 | #include "core/core.h" |
| 16 | #include "core/internal_network/network_interface.h" | ||
| 17 | #include "network/announce_multiplayer_session.h" | ||
| 16 | #include "ui_host_room.h" | 18 | #include "ui_host_room.h" |
| 17 | #include "yuzu/game_list_p.h" | 19 | #include "yuzu/game_list_p.h" |
| 18 | #include "yuzu/main.h" | 20 | #include "yuzu/main.h" |
| @@ -27,10 +29,11 @@ | |||
| 27 | 29 | ||
| 28 | HostRoomWindow::HostRoomWindow(QWidget* parent, QStandardItemModel* list, | 30 | HostRoomWindow::HostRoomWindow(QWidget* parent, QStandardItemModel* list, |
| 29 | std::shared_ptr<Core::AnnounceMultiplayerSession> session, | 31 | std::shared_ptr<Core::AnnounceMultiplayerSession> session, |
| 30 | Network::RoomNetwork& room_network_) | 32 | Core::System& system_) |
| 31 | : QDialog(parent, Qt::WindowTitleHint | Qt::WindowCloseButtonHint | Qt::WindowSystemMenuHint), | 33 | : QDialog(parent, Qt::WindowTitleHint | Qt::WindowCloseButtonHint | Qt::WindowSystemMenuHint), |
| 32 | ui(std::make_unique<Ui::HostRoom>()), | 34 | ui(std::make_unique<Ui::HostRoom>()), |
| 33 | announce_multiplayer_session(session), room_network{room_network_} { | 35 | announce_multiplayer_session(session), system{system_}, room_network{ |
| 36 | system.GetRoomNetwork()} { | ||
| 34 | ui->setupUi(this); | 37 | ui->setupUi(this); |
| 35 | 38 | ||
| 36 | // set up validation for all of the fields | 39 | // set up validation for all of the fields |
| @@ -105,6 +108,11 @@ std::unique_ptr<Network::VerifyUser::Backend> HostRoomWindow::CreateVerifyBacken | |||
| 105 | } | 108 | } |
| 106 | 109 | ||
| 107 | void HostRoomWindow::Host() { | 110 | void HostRoomWindow::Host() { |
| 111 | if (!Network::GetSelectedNetworkInterface()) { | ||
| 112 | NetworkMessage::ErrorManager::ShowError( | ||
| 113 | NetworkMessage::ErrorManager::NO_INTERFACE_SELECTED); | ||
| 114 | return; | ||
| 115 | } | ||
| 108 | if (!ui->username->hasAcceptableInput()) { | 116 | if (!ui->username->hasAcceptableInput()) { |
| 109 | NetworkMessage::ErrorManager::ShowError(NetworkMessage::ErrorManager::USERNAME_NOT_VALID); | 117 | NetworkMessage::ErrorManager::ShowError(NetworkMessage::ErrorManager::USERNAME_NOT_VALID); |
| 110 | return; | 118 | return; |
| @@ -121,6 +129,11 @@ void HostRoomWindow::Host() { | |||
| 121 | NetworkMessage::ErrorManager::ShowError(NetworkMessage::ErrorManager::GAME_NOT_SELECTED); | 129 | NetworkMessage::ErrorManager::ShowError(NetworkMessage::ErrorManager::GAME_NOT_SELECTED); |
| 122 | return; | 130 | return; |
| 123 | } | 131 | } |
| 132 | if (system.IsPoweredOn()) { | ||
| 133 | if (!NetworkMessage::WarnGameRunning()) { | ||
| 134 | return; | ||
| 135 | } | ||
| 136 | } | ||
| 124 | if (auto member = room_network.GetRoomMember().lock()) { | 137 | if (auto member = room_network.GetRoomMember().lock()) { |
| 125 | if (member->GetState() == Network::RoomMember::State::Joining) { | 138 | if (member->GetState() == Network::RoomMember::State::Joining) { |
| 126 | return; | 139 | return; |
| @@ -201,8 +214,8 @@ void HostRoomWindow::Host() { | |||
| 201 | } | 214 | } |
| 202 | #endif | 215 | #endif |
| 203 | // TODO: Check what to do with this | 216 | // TODO: Check what to do with this |
| 204 | member->Join(ui->username->text().toStdString(), "", "127.0.0.1", port, 0, | 217 | member->Join(ui->username->text().toStdString(), "127.0.0.1", port, 0, |
| 205 | Network::NoPreferredMac, password, token); | 218 | Network::NoPreferredIP, password, token); |
| 206 | 219 | ||
| 207 | // Store settings | 220 | // Store settings |
| 208 | UISettings::values.multiplayer_room_nickname = ui->username->text(); | 221 | UISettings::values.multiplayer_room_nickname = ui->username->text(); |
diff --git a/src/yuzu/multiplayer/host_room.h b/src/yuzu/multiplayer/host_room.h index a968042d0..034cb2eef 100644 --- a/src/yuzu/multiplayer/host_room.h +++ b/src/yuzu/multiplayer/host_room.h | |||
| @@ -17,8 +17,9 @@ class HostRoom; | |||
| 17 | } | 17 | } |
| 18 | 18 | ||
| 19 | namespace Core { | 19 | namespace Core { |
| 20 | class System; | ||
| 20 | class AnnounceMultiplayerSession; | 21 | class AnnounceMultiplayerSession; |
| 21 | } | 22 | } // namespace Core |
| 22 | 23 | ||
| 23 | class ConnectionError; | 24 | class ConnectionError; |
| 24 | class ComboBoxProxyModel; | 25 | class ComboBoxProxyModel; |
| @@ -35,7 +36,7 @@ class HostRoomWindow : public QDialog { | |||
| 35 | public: | 36 | public: |
| 36 | explicit HostRoomWindow(QWidget* parent, QStandardItemModel* list, | 37 | explicit HostRoomWindow(QWidget* parent, QStandardItemModel* list, |
| 37 | std::shared_ptr<Core::AnnounceMultiplayerSession> session, | 38 | std::shared_ptr<Core::AnnounceMultiplayerSession> session, |
| 38 | Network::RoomNetwork& room_network_); | 39 | Core::System& system_); |
| 39 | ~HostRoomWindow(); | 40 | ~HostRoomWindow(); |
| 40 | 41 | ||
| 41 | /** | 42 | /** |
| @@ -54,6 +55,7 @@ private: | |||
| 54 | QStandardItemModel* game_list; | 55 | QStandardItemModel* game_list; |
| 55 | ComboBoxProxyModel* proxy; | 56 | ComboBoxProxyModel* proxy; |
| 56 | Validation validation; | 57 | Validation validation; |
| 58 | Core::System& system; | ||
| 57 | Network::RoomNetwork& room_network; | 59 | Network::RoomNetwork& room_network; |
| 58 | }; | 60 | }; |
| 59 | 61 | ||
diff --git a/src/yuzu/multiplayer/lobby.cpp b/src/yuzu/multiplayer/lobby.cpp index 23c2f21ab..107d40547 100644 --- a/src/yuzu/multiplayer/lobby.cpp +++ b/src/yuzu/multiplayer/lobby.cpp | |||
| @@ -6,6 +6,8 @@ | |||
| 6 | #include <QtConcurrent/QtConcurrentRun> | 6 | #include <QtConcurrent/QtConcurrentRun> |
| 7 | #include "common/logging/log.h" | 7 | #include "common/logging/log.h" |
| 8 | #include "common/settings.h" | 8 | #include "common/settings.h" |
| 9 | #include "core/core.h" | ||
| 10 | #include "core/internal_network/network_interface.h" | ||
| 9 | #include "network/network.h" | 11 | #include "network/network.h" |
| 10 | #include "ui_lobby.h" | 12 | #include "ui_lobby.h" |
| 11 | #include "yuzu/game_list_p.h" | 13 | #include "yuzu/game_list_p.h" |
| @@ -22,11 +24,11 @@ | |||
| 22 | #endif | 24 | #endif |
| 23 | 25 | ||
| 24 | Lobby::Lobby(QWidget* parent, QStandardItemModel* list, | 26 | Lobby::Lobby(QWidget* parent, QStandardItemModel* list, |
| 25 | std::shared_ptr<Core::AnnounceMultiplayerSession> session, | 27 | std::shared_ptr<Core::AnnounceMultiplayerSession> session, Core::System& system_) |
| 26 | Network::RoomNetwork& room_network_) | ||
| 27 | : QDialog(parent, Qt::WindowTitleHint | Qt::WindowCloseButtonHint | Qt::WindowSystemMenuHint), | 28 | : QDialog(parent, Qt::WindowTitleHint | Qt::WindowCloseButtonHint | Qt::WindowSystemMenuHint), |
| 28 | ui(std::make_unique<Ui::Lobby>()), | 29 | ui(std::make_unique<Ui::Lobby>()), |
| 29 | announce_multiplayer_session(session), room_network{room_network_} { | 30 | announce_multiplayer_session(session), system{system_}, room_network{ |
| 31 | system.GetRoomNetwork()} { | ||
| 30 | ui->setupUi(this); | 32 | ui->setupUi(this); |
| 31 | 33 | ||
| 32 | // setup the watcher for background connections | 34 | // setup the watcher for background connections |
| @@ -114,6 +116,18 @@ void Lobby::OnExpandRoom(const QModelIndex& index) { | |||
| 114 | } | 116 | } |
| 115 | 117 | ||
| 116 | void Lobby::OnJoinRoom(const QModelIndex& source) { | 118 | void Lobby::OnJoinRoom(const QModelIndex& source) { |
| 119 | if (!Network::GetSelectedNetworkInterface()) { | ||
| 120 | NetworkMessage::ErrorManager::ShowError( | ||
| 121 | NetworkMessage::ErrorManager::NO_INTERFACE_SELECTED); | ||
| 122 | return; | ||
| 123 | } | ||
| 124 | |||
| 125 | if (system.IsPoweredOn()) { | ||
| 126 | if (!NetworkMessage::WarnGameRunning()) { | ||
| 127 | return; | ||
| 128 | } | ||
| 129 | } | ||
| 130 | |||
| 117 | if (const auto member = room_network.GetRoomMember().lock()) { | 131 | if (const auto member = room_network.GetRoomMember().lock()) { |
| 118 | // Prevent the user from trying to join a room while they are already joining. | 132 | // Prevent the user from trying to join a room while they are already joining. |
| 119 | if (member->GetState() == Network::RoomMember::State::Joining) { | 133 | if (member->GetState() == Network::RoomMember::State::Joining) { |
| @@ -169,7 +183,7 @@ void Lobby::OnJoinRoom(const QModelIndex& source) { | |||
| 169 | } | 183 | } |
| 170 | #endif | 184 | #endif |
| 171 | if (auto room_member = room_network.GetRoomMember().lock()) { | 185 | if (auto room_member = room_network.GetRoomMember().lock()) { |
| 172 | room_member->Join(nickname, "", ip.c_str(), port, 0, Network::NoPreferredMac, password, | 186 | room_member->Join(nickname, ip.c_str(), port, 0, Network::NoPreferredIP, password, |
| 173 | token); | 187 | token); |
| 174 | } | 188 | } |
| 175 | }); | 189 | }); |
diff --git a/src/yuzu/multiplayer/lobby.h b/src/yuzu/multiplayer/lobby.h index 82744ca94..2696aec21 100644 --- a/src/yuzu/multiplayer/lobby.h +++ b/src/yuzu/multiplayer/lobby.h | |||
| @@ -9,7 +9,7 @@ | |||
| 9 | #include <QSortFilterProxyModel> | 9 | #include <QSortFilterProxyModel> |
| 10 | #include <QStandardItemModel> | 10 | #include <QStandardItemModel> |
| 11 | #include "common/announce_multiplayer_room.h" | 11 | #include "common/announce_multiplayer_room.h" |
| 12 | #include "core/announce_multiplayer_session.h" | 12 | #include "network/announce_multiplayer_session.h" |
| 13 | #include "network/network.h" | 13 | #include "network/network.h" |
| 14 | #include "yuzu/multiplayer/validation.h" | 14 | #include "yuzu/multiplayer/validation.h" |
| 15 | 15 | ||
| @@ -20,6 +20,10 @@ class Lobby; | |||
| 20 | class LobbyModel; | 20 | class LobbyModel; |
| 21 | class LobbyFilterProxyModel; | 21 | class LobbyFilterProxyModel; |
| 22 | 22 | ||
| 23 | namespace Core { | ||
| 24 | class System; | ||
| 25 | } | ||
| 26 | |||
| 23 | /** | 27 | /** |
| 24 | * Listing of all public games pulled from services. The lobby should be simple enough for users to | 28 | * Listing of all public games pulled from services. The lobby should be simple enough for users to |
| 25 | * find the game they want to play, and join it. | 29 | * find the game they want to play, and join it. |
| @@ -30,7 +34,7 @@ class Lobby : public QDialog { | |||
| 30 | public: | 34 | public: |
| 31 | explicit Lobby(QWidget* parent, QStandardItemModel* list, | 35 | explicit Lobby(QWidget* parent, QStandardItemModel* list, |
| 32 | std::shared_ptr<Core::AnnounceMultiplayerSession> session, | 36 | std::shared_ptr<Core::AnnounceMultiplayerSession> session, |
| 33 | Network::RoomNetwork& room_network_); | 37 | Core::System& system_); |
| 34 | ~Lobby() override; | 38 | ~Lobby() override; |
| 35 | 39 | ||
| 36 | /** | 40 | /** |
| @@ -94,6 +98,7 @@ private: | |||
| 94 | std::weak_ptr<Core::AnnounceMultiplayerSession> announce_multiplayer_session; | 98 | std::weak_ptr<Core::AnnounceMultiplayerSession> announce_multiplayer_session; |
| 95 | QFutureWatcher<void>* watcher; | 99 | QFutureWatcher<void>* watcher; |
| 96 | Validation validation; | 100 | Validation validation; |
| 101 | Core::System& system; | ||
| 97 | Network::RoomNetwork& room_network; | 102 | Network::RoomNetwork& room_network; |
| 98 | }; | 103 | }; |
| 99 | 104 | ||
diff --git a/src/yuzu/multiplayer/message.cpp b/src/yuzu/multiplayer/message.cpp index 76ec276ad..758b5b731 100644 --- a/src/yuzu/multiplayer/message.cpp +++ b/src/yuzu/multiplayer/message.cpp | |||
| @@ -43,15 +43,15 @@ const ConnectionError ErrorManager::LOST_CONNECTION( | |||
| 43 | QT_TR_NOOP("Connection to room lost. Try to reconnect.")); | 43 | QT_TR_NOOP("Connection to room lost. Try to reconnect.")); |
| 44 | const ConnectionError ErrorManager::HOST_KICKED( | 44 | const ConnectionError ErrorManager::HOST_KICKED( |
| 45 | QT_TR_NOOP("You have been kicked by the room host.")); | 45 | QT_TR_NOOP("You have been kicked by the room host.")); |
| 46 | const ConnectionError ErrorManager::MAC_COLLISION( | 46 | const ConnectionError ErrorManager::IP_COLLISION( |
| 47 | QT_TR_NOOP("MAC address is already in use. Please choose another.")); | 47 | QT_TR_NOOP("IP address is already in use. Please choose another.")); |
| 48 | const ConnectionError ErrorManager::CONSOLE_ID_COLLISION(QT_TR_NOOP( | ||
| 49 | "Your Console ID conflicted with someone else's in the room.\n\nPlease go to Emulation " | ||
| 50 | "> Configure > System to regenerate your Console ID.")); | ||
| 51 | const ConnectionError ErrorManager::PERMISSION_DENIED( | 48 | const ConnectionError ErrorManager::PERMISSION_DENIED( |
| 52 | QT_TR_NOOP("You do not have enough permission to perform this action.")); | 49 | QT_TR_NOOP("You do not have enough permission to perform this action.")); |
| 53 | const ConnectionError ErrorManager::NO_SUCH_USER(QT_TR_NOOP( | 50 | const ConnectionError ErrorManager::NO_SUCH_USER(QT_TR_NOOP( |
| 54 | "The user you are trying to kick/ban could not be found.\nThey may have left the room.")); | 51 | "The user you are trying to kick/ban could not be found.\nThey may have left the room.")); |
| 52 | const ConnectionError ErrorManager::NO_INTERFACE_SELECTED( | ||
| 53 | QT_TR_NOOP("No network interface is selected.\nPlease go to Configure -> System -> Network and " | ||
| 54 | "make a selection.")); | ||
| 55 | 55 | ||
| 56 | static bool WarnMessage(const std::string& title, const std::string& text) { | 56 | static bool WarnMessage(const std::string& title, const std::string& text) { |
| 57 | return QMessageBox::Ok == QMessageBox::warning(nullptr, QObject::tr(title.c_str()), | 57 | return QMessageBox::Ok == QMessageBox::warning(nullptr, QObject::tr(title.c_str()), |
| @@ -63,6 +63,13 @@ void ErrorManager::ShowError(const ConnectionError& e) { | |||
| 63 | QMessageBox::critical(nullptr, tr("Error"), tr(e.GetString().c_str())); | 63 | QMessageBox::critical(nullptr, tr("Error"), tr(e.GetString().c_str())); |
| 64 | } | 64 | } |
| 65 | 65 | ||
| 66 | bool WarnGameRunning() { | ||
| 67 | return WarnMessage( | ||
| 68 | QT_TR_NOOP("Game already running"), | ||
| 69 | QT_TR_NOOP("Joining a room when the game is already running is discouraged " | ||
| 70 | "and can cause the room feature not to work correctly.\nProceed anyway?")); | ||
| 71 | } | ||
| 72 | |||
| 66 | bool WarnCloseRoom() { | 73 | bool WarnCloseRoom() { |
| 67 | return WarnMessage( | 74 | return WarnMessage( |
| 68 | QT_TR_NOOP("Leave Room"), | 75 | QT_TR_NOOP("Leave Room"), |
diff --git a/src/yuzu/multiplayer/message.h b/src/yuzu/multiplayer/message.h index eb5c8d1be..f038b9a1f 100644 --- a/src/yuzu/multiplayer/message.h +++ b/src/yuzu/multiplayer/message.h | |||
| @@ -40,15 +40,23 @@ public: | |||
| 40 | static const ConnectionError GENERIC_ERROR; | 40 | static const ConnectionError GENERIC_ERROR; |
| 41 | static const ConnectionError LOST_CONNECTION; | 41 | static const ConnectionError LOST_CONNECTION; |
| 42 | static const ConnectionError HOST_KICKED; | 42 | static const ConnectionError HOST_KICKED; |
| 43 | static const ConnectionError MAC_COLLISION; | 43 | static const ConnectionError IP_COLLISION; |
| 44 | static const ConnectionError CONSOLE_ID_COLLISION; | ||
| 45 | static const ConnectionError PERMISSION_DENIED; | 44 | static const ConnectionError PERMISSION_DENIED; |
| 46 | static const ConnectionError NO_SUCH_USER; | 45 | static const ConnectionError NO_SUCH_USER; |
| 46 | static const ConnectionError NO_INTERFACE_SELECTED; | ||
| 47 | /** | 47 | /** |
| 48 | * Shows a standard QMessageBox with a error message | 48 | * Shows a standard QMessageBox with a error message |
| 49 | */ | 49 | */ |
| 50 | static void ShowError(const ConnectionError& e); | 50 | static void ShowError(const ConnectionError& e); |
| 51 | }; | 51 | }; |
| 52 | |||
| 53 | /** | ||
| 54 | * Show a standard QMessageBox with a warning message about joining a room when | ||
| 55 | * the game is already running | ||
| 56 | * return true if the user wants to close the network connection | ||
| 57 | */ | ||
| 58 | bool WarnGameRunning(); | ||
| 59 | |||
| 52 | /** | 60 | /** |
| 53 | * Show a standard QMessageBox with a warning message about leaving the room | 61 | * Show a standard QMessageBox with a warning message about leaving the room |
| 54 | * return true if the user wants to close the network connection | 62 | * return true if the user wants to close the network connection |
diff --git a/src/yuzu/multiplayer/state.cpp b/src/yuzu/multiplayer/state.cpp index 4149b5232..66e098296 100644 --- a/src/yuzu/multiplayer/state.cpp +++ b/src/yuzu/multiplayer/state.cpp | |||
| @@ -8,6 +8,7 @@ | |||
| 8 | #include <QStandardItemModel> | 8 | #include <QStandardItemModel> |
| 9 | #include "common/announce_multiplayer_room.h" | 9 | #include "common/announce_multiplayer_room.h" |
| 10 | #include "common/logging/log.h" | 10 | #include "common/logging/log.h" |
| 11 | #include "core/core.h" | ||
| 11 | #include "yuzu/game_list.h" | 12 | #include "yuzu/game_list.h" |
| 12 | #include "yuzu/multiplayer/client_room.h" | 13 | #include "yuzu/multiplayer/client_room.h" |
| 13 | #include "yuzu/multiplayer/direct_connect.h" | 14 | #include "yuzu/multiplayer/direct_connect.h" |
| @@ -19,10 +20,9 @@ | |||
| 19 | #include "yuzu/util/clickable_label.h" | 20 | #include "yuzu/util/clickable_label.h" |
| 20 | 21 | ||
| 21 | MultiplayerState::MultiplayerState(QWidget* parent, QStandardItemModel* game_list_model_, | 22 | MultiplayerState::MultiplayerState(QWidget* parent, QStandardItemModel* game_list_model_, |
| 22 | QAction* leave_room_, QAction* show_room_, | 23 | QAction* leave_room_, QAction* show_room_, Core::System& system_) |
| 23 | Network::RoomNetwork& room_network_) | ||
| 24 | : QWidget(parent), game_list_model(game_list_model_), leave_room(leave_room_), | 24 | : QWidget(parent), game_list_model(game_list_model_), leave_room(leave_room_), |
| 25 | show_room(show_room_), room_network{room_network_} { | 25 | show_room(show_room_), system{system_}, room_network{system.GetRoomNetwork()} { |
| 26 | if (auto member = room_network.GetRoomMember().lock()) { | 26 | if (auto member = room_network.GetRoomMember().lock()) { |
| 27 | // register the network structs to use in slots and signals | 27 | // register the network structs to use in slots and signals |
| 28 | state_callback_handle = member->BindOnStateChanged( | 28 | state_callback_handle = member->BindOnStateChanged( |
| @@ -59,7 +59,9 @@ MultiplayerState::MultiplayerState(QWidget* parent, QStandardItemModel* game_lis | |||
| 59 | }); | 59 | }); |
| 60 | } | 60 | } |
| 61 | 61 | ||
| 62 | MultiplayerState::~MultiplayerState() { | 62 | MultiplayerState::~MultiplayerState() = default; |
| 63 | |||
| 64 | void MultiplayerState::Close() { | ||
| 63 | if (state_callback_handle) { | 65 | if (state_callback_handle) { |
| 64 | if (auto member = room_network.GetRoomMember().lock()) { | 66 | if (auto member = room_network.GetRoomMember().lock()) { |
| 65 | member->Unbind(state_callback_handle); | 67 | member->Unbind(state_callback_handle); |
| @@ -71,9 +73,6 @@ MultiplayerState::~MultiplayerState() { | |||
| 71 | member->Unbind(error_callback_handle); | 73 | member->Unbind(error_callback_handle); |
| 72 | } | 74 | } |
| 73 | } | 75 | } |
| 74 | } | ||
| 75 | |||
| 76 | void MultiplayerState::Close() { | ||
| 77 | if (host_room) { | 76 | if (host_room) { |
| 78 | host_room->close(); | 77 | host_room->close(); |
| 79 | } | 78 | } |
| @@ -95,7 +94,6 @@ void MultiplayerState::retranslateUi() { | |||
| 95 | status_text->setText(tr("Not Connected. Click here to find a room!")); | 94 | status_text->setText(tr("Not Connected. Click here to find a room!")); |
| 96 | } else if (current_state == Network::RoomMember::State::Joined || | 95 | } else if (current_state == Network::RoomMember::State::Joined || |
| 97 | current_state == Network::RoomMember::State::Moderator) { | 96 | current_state == Network::RoomMember::State::Moderator) { |
| 98 | |||
| 99 | status_text->setText(tr("Connected")); | 97 | status_text->setText(tr("Connected")); |
| 100 | } else { | 98 | } else { |
| 101 | status_text->setText(tr("Not Connected")); | 99 | status_text->setText(tr("Not Connected")); |
| @@ -151,11 +149,8 @@ void MultiplayerState::OnNetworkError(const Network::RoomMember::Error& error) { | |||
| 151 | NetworkMessage::ErrorManager::ShowError( | 149 | NetworkMessage::ErrorManager::ShowError( |
| 152 | NetworkMessage::ErrorManager::USERNAME_NOT_VALID_SERVER); | 150 | NetworkMessage::ErrorManager::USERNAME_NOT_VALID_SERVER); |
| 153 | break; | 151 | break; |
| 154 | case Network::RoomMember::Error::MacCollision: | 152 | case Network::RoomMember::Error::IpCollision: |
| 155 | NetworkMessage::ErrorManager::ShowError(NetworkMessage::ErrorManager::MAC_COLLISION); | 153 | NetworkMessage::ErrorManager::ShowError(NetworkMessage::ErrorManager::IP_COLLISION); |
| 156 | break; | ||
| 157 | case Network::RoomMember::Error::ConsoleIdCollision: | ||
| 158 | NetworkMessage::ErrorManager::ShowError(NetworkMessage::ErrorManager::CONSOLE_ID_COLLISION); | ||
| 159 | break; | 154 | break; |
| 160 | case Network::RoomMember::Error::RoomIsFull: | 155 | case Network::RoomMember::Error::RoomIsFull: |
| 161 | NetworkMessage::ErrorManager::ShowError(NetworkMessage::ErrorManager::ROOM_IS_FULL); | 156 | NetworkMessage::ErrorManager::ShowError(NetworkMessage::ErrorManager::ROOM_IS_FULL); |
| @@ -213,15 +208,14 @@ static void BringWidgetToFront(QWidget* widget) { | |||
| 213 | 208 | ||
| 214 | void MultiplayerState::OnViewLobby() { | 209 | void MultiplayerState::OnViewLobby() { |
| 215 | if (lobby == nullptr) { | 210 | if (lobby == nullptr) { |
| 216 | lobby = new Lobby(this, game_list_model, announce_multiplayer_session, room_network); | 211 | lobby = new Lobby(this, game_list_model, announce_multiplayer_session, system); |
| 217 | } | 212 | } |
| 218 | BringWidgetToFront(lobby); | 213 | BringWidgetToFront(lobby); |
| 219 | } | 214 | } |
| 220 | 215 | ||
| 221 | void MultiplayerState::OnCreateRoom() { | 216 | void MultiplayerState::OnCreateRoom() { |
| 222 | if (host_room == nullptr) { | 217 | if (host_room == nullptr) { |
| 223 | host_room = | 218 | host_room = new HostRoomWindow(this, game_list_model, announce_multiplayer_session, system); |
| 224 | new HostRoomWindow(this, game_list_model, announce_multiplayer_session, room_network); | ||
| 225 | } | 219 | } |
| 226 | BringWidgetToFront(host_room); | 220 | BringWidgetToFront(host_room); |
| 227 | } | 221 | } |
| @@ -284,7 +278,7 @@ void MultiplayerState::OnOpenNetworkRoom() { | |||
| 284 | 278 | ||
| 285 | void MultiplayerState::OnDirectConnectToRoom() { | 279 | void MultiplayerState::OnDirectConnectToRoom() { |
| 286 | if (direct_connect == nullptr) { | 280 | if (direct_connect == nullptr) { |
| 287 | direct_connect = new DirectConnectWindow(room_network, this); | 281 | direct_connect = new DirectConnectWindow(system, this); |
| 288 | } | 282 | } |
| 289 | BringWidgetToFront(direct_connect); | 283 | BringWidgetToFront(direct_connect); |
| 290 | } | 284 | } |
diff --git a/src/yuzu/multiplayer/state.h b/src/yuzu/multiplayer/state.h index 9c60712d5..c92496413 100644 --- a/src/yuzu/multiplayer/state.h +++ b/src/yuzu/multiplayer/state.h | |||
| @@ -4,7 +4,7 @@ | |||
| 4 | #pragma once | 4 | #pragma once |
| 5 | 5 | ||
| 6 | #include <QWidget> | 6 | #include <QWidget> |
| 7 | #include "core/announce_multiplayer_session.h" | 7 | #include "network/announce_multiplayer_session.h" |
| 8 | #include "network/network.h" | 8 | #include "network/network.h" |
| 9 | 9 | ||
| 10 | class QStandardItemModel; | 10 | class QStandardItemModel; |
| @@ -14,12 +14,16 @@ class ClientRoomWindow; | |||
| 14 | class DirectConnectWindow; | 14 | class DirectConnectWindow; |
| 15 | class ClickableLabel; | 15 | class ClickableLabel; |
| 16 | 16 | ||
| 17 | namespace Core { | ||
| 18 | class System; | ||
| 19 | } | ||
| 20 | |||
| 17 | class MultiplayerState : public QWidget { | 21 | class MultiplayerState : public QWidget { |
| 18 | Q_OBJECT; | 22 | Q_OBJECT; |
| 19 | 23 | ||
| 20 | public: | 24 | public: |
| 21 | explicit MultiplayerState(QWidget* parent, QStandardItemModel* game_list, QAction* leave_room, | 25 | explicit MultiplayerState(QWidget* parent, QStandardItemModel* game_list, QAction* leave_room, |
| 22 | QAction* show_room, Network::RoomNetwork& room_network_); | 26 | QAction* show_room, Core::System& system_); |
| 23 | ~MultiplayerState(); | 27 | ~MultiplayerState(); |
| 24 | 28 | ||
| 25 | /** | 29 | /** |
| @@ -86,6 +90,7 @@ private: | |||
| 86 | Network::RoomMember::CallbackHandle<Network::RoomMember::Error> error_callback_handle; | 90 | Network::RoomMember::CallbackHandle<Network::RoomMember::Error> error_callback_handle; |
| 87 | 91 | ||
| 88 | bool show_notification = false; | 92 | bool show_notification = false; |
| 93 | Core::System& system; | ||
| 89 | Network::RoomNetwork& room_network; | 94 | Network::RoomNetwork& room_network; |
| 90 | }; | 95 | }; |
| 91 | 96 | ||
diff --git a/src/yuzu/multiplayer/validation.h b/src/yuzu/multiplayer/validation.h index 7d48e589d..dabf860be 100644 --- a/src/yuzu/multiplayer/validation.h +++ b/src/yuzu/multiplayer/validation.h | |||
| @@ -10,7 +10,7 @@ | |||
| 10 | class Validation { | 10 | class Validation { |
| 11 | public: | 11 | public: |
| 12 | Validation() | 12 | Validation() |
| 13 | : room_name(room_name_regex), nickname(nickname_regex), ip(ip_regex), port(0, 65535) {} | 13 | : room_name(room_name_regex), nickname(nickname_regex), ip(ip_regex), port(0, UINT16_MAX) {} |
| 14 | 14 | ||
| 15 | ~Validation() = default; | 15 | ~Validation() = default; |
| 16 | 16 | ||
diff --git a/src/yuzu/uisettings.h b/src/yuzu/uisettings.h index 25d1bf1e6..e12d414d9 100644 --- a/src/yuzu/uisettings.h +++ b/src/yuzu/uisettings.h | |||
| @@ -104,11 +104,12 @@ struct Values { | |||
| 104 | // multiplayer settings | 104 | // multiplayer settings |
| 105 | Settings::Setting<QString> multiplayer_nickname{QStringLiteral("yuzu"), "nickname"}; | 105 | Settings::Setting<QString> multiplayer_nickname{QStringLiteral("yuzu"), "nickname"}; |
| 106 | Settings::Setting<QString> multiplayer_ip{{}, "ip"}; | 106 | Settings::Setting<QString> multiplayer_ip{{}, "ip"}; |
| 107 | Settings::SwitchableSetting<uint, true> multiplayer_port{24872, 0, 65535, "port"}; | 107 | Settings::SwitchableSetting<uint, true> multiplayer_port{24872, 0, UINT16_MAX, "port"}; |
| 108 | Settings::Setting<QString> multiplayer_room_nickname{{}, "room_nickname"}; | 108 | Settings::Setting<QString> multiplayer_room_nickname{{}, "room_nickname"}; |
| 109 | Settings::Setting<QString> multiplayer_room_name{{}, "room_name"}; | 109 | Settings::Setting<QString> multiplayer_room_name{{}, "room_name"}; |
| 110 | Settings::SwitchableSetting<uint, true> multiplayer_max_player{8, 0, 8, "max_player"}; | 110 | Settings::SwitchableSetting<uint, true> multiplayer_max_player{8, 0, 8, "max_player"}; |
| 111 | Settings::SwitchableSetting<uint, true> multiplayer_room_port{24872, 0, 65535, "room_port"}; | 111 | Settings::SwitchableSetting<uint, true> multiplayer_room_port{24872, 0, UINT16_MAX, |
| 112 | "room_port"}; | ||
| 112 | Settings::SwitchableSetting<uint, true> multiplayer_host_type{0, 0, 1, "host_type"}; | 113 | Settings::SwitchableSetting<uint, true> multiplayer_host_type{0, 0, 1, "host_type"}; |
| 113 | Settings::Setting<qulonglong> multiplayer_game_id{{}, "game_id"}; | 114 | Settings::Setting<qulonglong> multiplayer_game_id{{}, "game_id"}; |
| 114 | Settings::Setting<QString> multiplayer_room_description{{}, "room_description"}; | 115 | Settings::Setting<QString> multiplayer_room_description{{}, "room_description"}; |
diff --git a/src/yuzu_cmd/config.cpp b/src/yuzu_cmd/config.cpp index bd0fb75f8..66dd0dc15 100644 --- a/src/yuzu_cmd/config.cpp +++ b/src/yuzu_cmd/config.cpp | |||
| @@ -314,6 +314,7 @@ void Config::ReadValues() { | |||
| 314 | ReadSetting("Renderer", Settings::values.nvdec_emulation); | 314 | ReadSetting("Renderer", Settings::values.nvdec_emulation); |
| 315 | ReadSetting("Renderer", Settings::values.accelerate_astc); | 315 | ReadSetting("Renderer", Settings::values.accelerate_astc); |
| 316 | ReadSetting("Renderer", Settings::values.use_fast_gpu_time); | 316 | ReadSetting("Renderer", Settings::values.use_fast_gpu_time); |
| 317 | ReadSetting("Renderer", Settings::values.use_pessimistic_flushes); | ||
| 317 | 318 | ||
| 318 | ReadSetting("Renderer", Settings::values.bg_red); | 319 | ReadSetting("Renderer", Settings::values.bg_red); |
| 319 | ReadSetting("Renderer", Settings::values.bg_green); | 320 | ReadSetting("Renderer", Settings::values.bg_green); |
diff --git a/src/yuzu_cmd/default_ini.h b/src/yuzu_cmd/default_ini.h index 1168cf136..d214771b0 100644 --- a/src/yuzu_cmd/default_ini.h +++ b/src/yuzu_cmd/default_ini.h | |||
| @@ -319,6 +319,10 @@ use_asynchronous_gpu_emulation = | |||
| 319 | # 0: Off, 1 (default): On | 319 | # 0: Off, 1 (default): On |
| 320 | use_fast_gpu_time = | 320 | use_fast_gpu_time = |
| 321 | 321 | ||
| 322 | # Force unmodified buffers to be flushed, which can cost performance. | ||
| 323 | # 0: Off (default), 1: On | ||
| 324 | use_pessimistic_flushes = | ||
| 325 | |||
| 322 | # Whether to use garbage collection or not for GPU caches. | 326 | # Whether to use garbage collection or not for GPU caches. |
| 323 | # 0 (default): Off, 1: On | 327 | # 0 (default): Off, 1: On |
| 324 | use_caches_gc = | 328 | use_caches_gc = |
diff --git a/src/yuzu_cmd/yuzu.cpp b/src/yuzu_cmd/yuzu.cpp index 003890c07..3a0f33cba 100644 --- a/src/yuzu_cmd/yuzu.cpp +++ b/src/yuzu_cmd/yuzu.cpp | |||
| @@ -108,15 +108,11 @@ static void OnNetworkError(const Network::RoomMember::Error& error) { | |||
| 108 | "You tried to use the same nickname as another user that is connected to the Room"); | 108 | "You tried to use the same nickname as another user that is connected to the Room"); |
| 109 | exit(1); | 109 | exit(1); |
| 110 | break; | 110 | break; |
| 111 | case Network::RoomMember::Error::MacCollision: | 111 | case Network::RoomMember::Error::IpCollision: |
| 112 | LOG_ERROR(Network, "You tried to use the same MAC-Address as another user that is " | 112 | LOG_ERROR(Network, "You tried to use the same fake IP-Address as another user that is " |
| 113 | "connected to the Room"); | 113 | "connected to the Room"); |
| 114 | exit(1); | 114 | exit(1); |
| 115 | break; | 115 | break; |
| 116 | case Network::RoomMember::Error::ConsoleIdCollision: | ||
| 117 | LOG_ERROR(Network, "Your Console ID conflicted with someone else in the Room"); | ||
| 118 | exit(1); | ||
| 119 | break; | ||
| 120 | case Network::RoomMember::Error::WrongPassword: | 116 | case Network::RoomMember::Error::WrongPassword: |
| 121 | LOG_ERROR(Network, "Room replied with: Wrong password"); | 117 | LOG_ERROR(Network, "Room replied with: Wrong password"); |
| 122 | exit(1); | 118 | exit(1); |
| @@ -365,7 +361,7 @@ int main(int argc, char** argv) { | |||
| 365 | member->BindOnError(OnNetworkError); | 361 | member->BindOnError(OnNetworkError); |
| 366 | LOG_DEBUG(Network, "Start connection to {}:{} with nickname {}", address, port, | 362 | LOG_DEBUG(Network, "Start connection to {}:{} with nickname {}", address, port, |
| 367 | nickname); | 363 | nickname); |
| 368 | member->Join(nickname, "", address.c_str(), port, 0, Network::NoPreferredMac, password); | 364 | member->Join(nickname, address.c_str(), port, 0, Network::NoPreferredIP, password); |
| 369 | } else { | 365 | } else { |
| 370 | LOG_ERROR(Network, "Could not access RoomMember"); | 366 | LOG_ERROR(Network, "Could not access RoomMember"); |
| 371 | return 0; | 367 | return 0; |