summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/audio_core/CMakeLists.txt4
-rw-r--r--src/audio_core/audio_out.cpp10
-rw-r--r--src/audio_core/audio_out.h5
-rw-r--r--src/audio_core/audio_renderer.cpp234
-rw-r--r--src/audio_core/audio_renderer.h206
-rw-r--r--src/audio_core/buffer.h13
-rw-r--r--src/audio_core/codec.cpp77
-rw-r--r--src/audio_core/codec.h44
-rw-r--r--src/audio_core/cubeb_sink.cpp54
-rw-r--r--src/audio_core/cubeb_sink.h3
-rw-r--r--src/audio_core/null_sink.h6
-rw-r--r--src/audio_core/sink.h4
-rw-r--r--src/audio_core/sink_stream.h4
-rw-r--r--src/audio_core/stream.cpp26
-rw-r--r--src/audio_core/stream.h7
-rw-r--r--src/core/arm/unicorn/arm_unicorn.cpp2
-rw-r--r--src/core/core.cpp5
-rw-r--r--src/core/core.h11
-rw-r--r--src/core/core_timing.cpp14
-rw-r--r--src/core/core_timing.h11
-rw-r--r--src/core/gdbstub/gdbstub.cpp174
-rw-r--r--src/core/gdbstub/gdbstub.h8
-rw-r--r--src/core/hle/kernel/address_arbiter.cpp30
-rw-r--r--src/core/hle/kernel/event.h12
-rw-r--r--src/core/hle/service/audio/audout_a.cpp2
-rw-r--r--src/core/hle/service/audio/audout_u.cpp12
-rw-r--r--src/core/hle/service/audio/audout_u.h10
-rw-r--r--src/core/hle/service/audio/audren_u.cpp208
-rw-r--r--src/core/hle/service/audio/audren_u.h19
-rw-r--r--src/core/memory.h23
-rw-r--r--src/core/perf_stats.cpp17
-rw-r--r--src/core/perf_stats.h8
-rw-r--r--src/video_core/engines/maxwell_3d.cpp2
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer_cache.cpp14
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer_cache.h11
-rw-r--r--src/yuzu/about_dialog.cpp2
-rw-r--r--src/yuzu/about_dialog.h2
-rw-r--r--src/yuzu/bootmanager.h2
-rw-r--r--src/yuzu/configuration/configure_debug.cpp2
-rw-r--r--src/yuzu/configuration/configure_debug.ui7
-rw-r--r--src/yuzu/configuration/configure_dialog.cpp2
-rw-r--r--src/yuzu/configuration/configure_general.cpp2
-rw-r--r--src/yuzu/configuration/configure_graphics.cpp2
-rw-r--r--src/yuzu/configuration/configure_system.cpp2
-rw-r--r--src/yuzu/debugger/graphics/graphics_surface.cpp3
-rw-r--r--src/yuzu/debugger/graphics/graphics_surface.h6
-rw-r--r--src/yuzu/debugger/wait_tree.cpp4
-rw-r--r--src/yuzu/debugger/wait_tree.h4
-rw-r--r--src/yuzu/game_list.cpp44
-rw-r--r--src/yuzu/game_list_p.h17
-rw-r--r--src/yuzu/main.cpp42
-rw-r--r--src/yuzu/main.h2
52 files changed, 920 insertions, 515 deletions
diff --git a/src/audio_core/CMakeLists.txt b/src/audio_core/CMakeLists.txt
index 81121167d..827ab0ac7 100644
--- a/src/audio_core/CMakeLists.txt
+++ b/src/audio_core/CMakeLists.txt
@@ -1,9 +1,13 @@
1add_library(audio_core STATIC 1add_library(audio_core STATIC
2 audio_out.cpp 2 audio_out.cpp
3 audio_out.h 3 audio_out.h
4 audio_renderer.cpp
5 audio_renderer.h
4 buffer.h 6 buffer.h
5 cubeb_sink.cpp 7 cubeb_sink.cpp
6 cubeb_sink.h 8 cubeb_sink.h
9 codec.cpp
10 codec.h
7 null_sink.h 11 null_sink.h
8 stream.cpp 12 stream.cpp
9 stream.h 13 stream.h
diff --git a/src/audio_core/audio_out.cpp b/src/audio_core/audio_out.cpp
index 3dfdf61f9..12632a95c 100644
--- a/src/audio_core/audio_out.cpp
+++ b/src/audio_core/audio_out.cpp
@@ -27,16 +27,16 @@ static Stream::Format ChannelsToStreamFormat(u32 num_channels) {
27 return {}; 27 return {};
28} 28}
29 29
30StreamPtr AudioOut::OpenStream(u32 sample_rate, u32 num_channels, 30StreamPtr AudioOut::OpenStream(u32 sample_rate, u32 num_channels, std::string&& name,
31 Stream::ReleaseCallback&& release_callback) { 31 Stream::ReleaseCallback&& release_callback) {
32 if (!sink) { 32 if (!sink) {
33 const SinkDetails& sink_details = GetSinkDetails(Settings::values.sink_id); 33 const SinkDetails& sink_details = GetSinkDetails(Settings::values.sink_id);
34 sink = sink_details.factory(Settings::values.audio_device_id); 34 sink = sink_details.factory(Settings::values.audio_device_id);
35 } 35 }
36 36
37 return std::make_shared<Stream>(sample_rate, ChannelsToStreamFormat(num_channels), 37 return std::make_shared<Stream>(
38 std::move(release_callback), 38 sample_rate, ChannelsToStreamFormat(num_channels), std::move(release_callback),
39 sink->AcquireSinkStream(sample_rate, num_channels)); 39 sink->AcquireSinkStream(sample_rate, num_channels, name), std::move(name));
40} 40}
41 41
42std::vector<Buffer::Tag> AudioOut::GetTagsAndReleaseBuffers(StreamPtr stream, size_t max_count) { 42std::vector<Buffer::Tag> AudioOut::GetTagsAndReleaseBuffers(StreamPtr stream, size_t max_count) {
@@ -51,7 +51,7 @@ void AudioOut::StopStream(StreamPtr stream) {
51 stream->Stop(); 51 stream->Stop();
52} 52}
53 53
54bool AudioOut::QueueBuffer(StreamPtr stream, Buffer::Tag tag, std::vector<u8>&& data) { 54bool AudioOut::QueueBuffer(StreamPtr stream, Buffer::Tag tag, std::vector<s16>&& data) {
55 return stream->QueueBuffer(std::make_shared<Buffer>(tag, std::move(data))); 55 return stream->QueueBuffer(std::make_shared<Buffer>(tag, std::move(data)));
56} 56}
57 57
diff --git a/src/audio_core/audio_out.h b/src/audio_core/audio_out.h
index 95e9b53fe..39b7e656b 100644
--- a/src/audio_core/audio_out.h
+++ b/src/audio_core/audio_out.h
@@ -5,6 +5,7 @@
5#pragma once 5#pragma once
6 6
7#include <memory> 7#include <memory>
8#include <string>
8#include <vector> 9#include <vector>
9 10
10#include "audio_core/buffer.h" 11#include "audio_core/buffer.h"
@@ -20,7 +21,7 @@ namespace AudioCore {
20class AudioOut { 21class AudioOut {
21public: 22public:
22 /// Opens a new audio stream 23 /// Opens a new audio stream
23 StreamPtr OpenStream(u32 sample_rate, u32 num_channels, 24 StreamPtr OpenStream(u32 sample_rate, u32 num_channels, std::string&& name,
24 Stream::ReleaseCallback&& release_callback); 25 Stream::ReleaseCallback&& release_callback);
25 26
26 /// Returns a vector of recently released buffers specified by tag for the specified stream 27 /// Returns a vector of recently released buffers specified by tag for the specified stream
@@ -33,7 +34,7 @@ public:
33 void StopStream(StreamPtr stream); 34 void StopStream(StreamPtr stream);
34 35
35 /// Queues a buffer into the specified audio stream, returns true on success 36 /// Queues a buffer into the specified audio stream, returns true on success
36 bool QueueBuffer(StreamPtr stream, Buffer::Tag tag, std::vector<u8>&& data); 37 bool QueueBuffer(StreamPtr stream, Buffer::Tag tag, std::vector<s16>&& data);
37 38
38private: 39private:
39 SinkPtr sink; 40 SinkPtr sink;
diff --git a/src/audio_core/audio_renderer.cpp b/src/audio_core/audio_renderer.cpp
new file mode 100644
index 000000000..282f345c5
--- /dev/null
+++ b/src/audio_core/audio_renderer.cpp
@@ -0,0 +1,234 @@
1// Copyright 2018 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "audio_core/audio_renderer.h"
6#include "common/assert.h"
7#include "common/logging/log.h"
8#include "core/memory.h"
9
10namespace AudioCore {
11
12constexpr u32 STREAM_SAMPLE_RATE{48000};
13constexpr u32 STREAM_NUM_CHANNELS{2};
14
15AudioRenderer::AudioRenderer(AudioRendererParameter params,
16 Kernel::SharedPtr<Kernel::Event> buffer_event)
17 : worker_params{params}, buffer_event{buffer_event}, voices(params.voice_count) {
18
19 audio_core = std::make_unique<AudioCore::AudioOut>();
20 stream = audio_core->OpenStream(STREAM_SAMPLE_RATE, STREAM_NUM_CHANNELS, "AudioRenderer",
21 [=]() { buffer_event->Signal(); });
22 audio_core->StartStream(stream);
23
24 QueueMixedBuffer(0);
25 QueueMixedBuffer(1);
26 QueueMixedBuffer(2);
27}
28
29std::vector<u8> AudioRenderer::UpdateAudioRenderer(const std::vector<u8>& input_params) {
30 // Copy UpdateDataHeader struct
31 UpdateDataHeader config{};
32 std::memcpy(&config, input_params.data(), sizeof(UpdateDataHeader));
33 u32 memory_pool_count = worker_params.effect_count + (worker_params.voice_count * 4);
34
35 // Copy MemoryPoolInfo structs
36 std::vector<MemoryPoolInfo> mem_pool_info(memory_pool_count);
37 std::memcpy(mem_pool_info.data(),
38 input_params.data() + sizeof(UpdateDataHeader) + config.behavior_size,
39 memory_pool_count * sizeof(MemoryPoolInfo));
40
41 // Copy VoiceInfo structs
42 size_t offset{sizeof(UpdateDataHeader) + config.behavior_size + config.memory_pools_size +
43 config.voice_resource_size};
44 for (auto& voice : voices) {
45 std::memcpy(&voice.Info(), input_params.data() + offset, sizeof(VoiceInfo));
46 offset += sizeof(VoiceInfo);
47 }
48
49 // Update voices
50 for (auto& voice : voices) {
51 voice.UpdateState();
52 if (!voice.GetInfo().is_in_use) {
53 continue;
54 }
55 if (voice.GetInfo().is_new) {
56 voice.SetWaveIndex(voice.GetInfo().wave_buffer_head);
57 }
58 }
59
60 // Update memory pool state
61 std::vector<MemoryPoolEntry> memory_pool(memory_pool_count);
62 for (size_t index = 0; index < memory_pool.size(); ++index) {
63 if (mem_pool_info[index].pool_state == MemoryPoolStates::RequestAttach) {
64 memory_pool[index].state = MemoryPoolStates::Attached;
65 } else if (mem_pool_info[index].pool_state == MemoryPoolStates::RequestDetach) {
66 memory_pool[index].state = MemoryPoolStates::Detached;
67 }
68 }
69
70 // Release previous buffers and queue next ones for playback
71 ReleaseAndQueueBuffers();
72
73 // Copy output header
74 UpdateDataHeader response_data{worker_params};
75 std::vector<u8> output_params(response_data.total_size);
76 std::memcpy(output_params.data(), &response_data, sizeof(UpdateDataHeader));
77
78 // Copy output memory pool entries
79 std::memcpy(output_params.data() + sizeof(UpdateDataHeader), memory_pool.data(),
80 response_data.memory_pools_size);
81
82 // Copy output voice status
83 size_t voice_out_status_offset{sizeof(UpdateDataHeader) + response_data.memory_pools_size};
84 for (const auto& voice : voices) {
85 std::memcpy(output_params.data() + voice_out_status_offset, &voice.GetOutStatus(),
86 sizeof(VoiceOutStatus));
87 voice_out_status_offset += sizeof(VoiceOutStatus);
88 }
89
90 return output_params;
91}
92
93void AudioRenderer::VoiceState::SetWaveIndex(size_t index) {
94 wave_index = index & 3;
95 is_refresh_pending = true;
96}
97
98std::vector<s16> AudioRenderer::VoiceState::DequeueSamples(size_t sample_count) {
99 if (!IsPlaying()) {
100 return {};
101 }
102
103 if (is_refresh_pending) {
104 RefreshBuffer();
105 }
106
107 const size_t max_size{samples.size() - offset};
108 const size_t dequeue_offset{offset};
109 size_t size{sample_count * STREAM_NUM_CHANNELS};
110 if (size > max_size) {
111 size = max_size;
112 }
113
114 out_status.played_sample_count += size / STREAM_NUM_CHANNELS;
115 offset += size;
116
117 const auto& wave_buffer{info.wave_buffer[wave_index]};
118 if (offset == samples.size()) {
119 offset = 0;
120
121 if (!wave_buffer.is_looping) {
122 SetWaveIndex(wave_index + 1);
123 }
124
125 out_status.wave_buffer_consumed++;
126
127 if (wave_buffer.end_of_stream) {
128 info.play_state = PlayState::Paused;
129 }
130 }
131
132 return {samples.begin() + dequeue_offset, samples.begin() + dequeue_offset + size};
133}
134
135void AudioRenderer::VoiceState::UpdateState() {
136 if (is_in_use && !info.is_in_use) {
137 // No longer in use, reset state
138 is_refresh_pending = true;
139 wave_index = 0;
140 offset = 0;
141 out_status = {};
142 }
143 is_in_use = info.is_in_use;
144}
145
146void AudioRenderer::VoiceState::RefreshBuffer() {
147 std::vector<s16> new_samples(info.wave_buffer[wave_index].buffer_sz / sizeof(s16));
148 Memory::ReadBlock(info.wave_buffer[wave_index].buffer_addr, new_samples.data(),
149 info.wave_buffer[wave_index].buffer_sz);
150
151 switch (static_cast<Codec::PcmFormat>(info.sample_format)) {
152 case Codec::PcmFormat::Int16: {
153 // PCM16 is played as-is
154 break;
155 }
156 case Codec::PcmFormat::Adpcm: {
157 // Decode ADPCM to PCM16
158 Codec::ADPCM_Coeff coeffs;
159 Memory::ReadBlock(info.additional_params_addr, coeffs.data(), sizeof(Codec::ADPCM_Coeff));
160 new_samples = Codec::DecodeADPCM(reinterpret_cast<u8*>(new_samples.data()),
161 new_samples.size() * sizeof(s16), coeffs, adpcm_state);
162 break;
163 }
164 default:
165 LOG_CRITICAL(Audio, "Unimplemented sample_format={}", info.sample_format);
166 UNREACHABLE();
167 break;
168 }
169
170 switch (info.channel_count) {
171 case 1:
172 // 1 channel is upsampled to 2 channel
173 samples.resize(new_samples.size() * 2);
174 for (size_t index = 0; index < new_samples.size(); ++index) {
175 samples[index * 2] = new_samples[index];
176 samples[index * 2 + 1] = new_samples[index];
177 }
178 break;
179 case 2: {
180 // 2 channel is played as is
181 samples = std::move(new_samples);
182 break;
183 }
184 default:
185 LOG_CRITICAL(Audio, "Unimplemented channel_count={}", info.channel_count);
186 UNREACHABLE();
187 break;
188 }
189
190 is_refresh_pending = false;
191}
192
193static constexpr s16 ClampToS16(s32 value) {
194 return static_cast<s16>(std::clamp(value, -32768, 32767));
195}
196
197void AudioRenderer::QueueMixedBuffer(Buffer::Tag tag) {
198 constexpr size_t BUFFER_SIZE{512};
199 std::vector<s16> buffer(BUFFER_SIZE * stream->GetNumChannels());
200
201 for (auto& voice : voices) {
202 if (!voice.IsPlaying()) {
203 continue;
204 }
205
206 size_t offset{};
207 s64 samples_remaining{BUFFER_SIZE};
208 while (samples_remaining > 0) {
209 const std::vector<s16> samples{voice.DequeueSamples(samples_remaining)};
210
211 if (samples.empty()) {
212 break;
213 }
214
215 samples_remaining -= samples.size();
216
217 for (const auto& sample : samples) {
218 const s32 buffer_sample{buffer[offset]};
219 buffer[offset++] =
220 ClampToS16(buffer_sample + static_cast<s32>(sample * voice.GetInfo().volume));
221 }
222 }
223 }
224 audio_core->QueueBuffer(stream, tag, std::move(buffer));
225}
226
227void AudioRenderer::ReleaseAndQueueBuffers() {
228 const auto released_buffers{audio_core->GetTagsAndReleaseBuffers(stream, 2)};
229 for (const auto& tag : released_buffers) {
230 QueueMixedBuffer(tag);
231 }
232}
233
234} // namespace AudioCore
diff --git a/src/audio_core/audio_renderer.h b/src/audio_core/audio_renderer.h
new file mode 100644
index 000000000..6950a4681
--- /dev/null
+++ b/src/audio_core/audio_renderer.h
@@ -0,0 +1,206 @@
1// Copyright 2018 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <array>
8#include <memory>
9#include <vector>
10
11#include "audio_core/audio_out.h"
12#include "audio_core/codec.h"
13#include "audio_core/stream.h"
14#include "common/common_types.h"
15#include "common/swap.h"
16#include "core/hle/kernel/event.h"
17
18namespace AudioCore {
19
20enum class PlayState : u8 {
21 Started = 0,
22 Stopped = 1,
23 Paused = 2,
24};
25
26struct AudioRendererParameter {
27 u32_le sample_rate;
28 u32_le sample_count;
29 u32_le unknown_8;
30 u32_le unknown_c;
31 u32_le voice_count;
32 u32_le sink_count;
33 u32_le effect_count;
34 u32_le unknown_1c;
35 u8 unknown_20;
36 INSERT_PADDING_BYTES(3);
37 u32_le splitter_count;
38 u32_le unknown_2c;
39 INSERT_PADDING_WORDS(1);
40 u32_le revision;
41};
42static_assert(sizeof(AudioRendererParameter) == 52, "AudioRendererParameter is an invalid size");
43
44enum class MemoryPoolStates : u32 { // Should be LE
45 Invalid = 0x0,
46 Unknown = 0x1,
47 RequestDetach = 0x2,
48 Detached = 0x3,
49 RequestAttach = 0x4,
50 Attached = 0x5,
51 Released = 0x6,
52};
53
54struct MemoryPoolEntry {
55 MemoryPoolStates state;
56 u32_le unknown_4;
57 u32_le unknown_8;
58 u32_le unknown_c;
59};
60static_assert(sizeof(MemoryPoolEntry) == 0x10, "MemoryPoolEntry has wrong size");
61
62struct MemoryPoolInfo {
63 u64_le pool_address;
64 u64_le pool_size;
65 MemoryPoolStates pool_state;
66 INSERT_PADDING_WORDS(3); // Unknown
67};
68static_assert(sizeof(MemoryPoolInfo) == 0x20, "MemoryPoolInfo has wrong size");
69struct BiquadFilter {
70 u8 enable;
71 INSERT_PADDING_BYTES(1);
72 std::array<s16_le, 3> numerator;
73 std::array<s16_le, 2> denominator;
74};
75static_assert(sizeof(BiquadFilter) == 0xc, "BiquadFilter has wrong size");
76
77struct WaveBuffer {
78 u64_le buffer_addr;
79 u64_le buffer_sz;
80 s32_le start_sample_offset;
81 s32_le end_sample_offset;
82 u8 is_looping;
83 u8 end_of_stream;
84 u8 sent_to_server;
85 INSERT_PADDING_BYTES(5);
86 u64 context_addr;
87 u64 context_sz;
88 INSERT_PADDING_BYTES(8);
89};
90static_assert(sizeof(WaveBuffer) == 0x38, "WaveBuffer has wrong size");
91
92struct VoiceInfo {
93 u32_le id;
94 u32_le node_id;
95 u8 is_new;
96 u8 is_in_use;
97 PlayState play_state;
98 u8 sample_format;
99 u32_le sample_rate;
100 u32_le priority;
101 u32_le sorting_order;
102 u32_le channel_count;
103 float_le pitch;
104 float_le volume;
105 std::array<BiquadFilter, 2> biquad_filter;
106 u32_le wave_buffer_count;
107 u32_le wave_buffer_head;
108 INSERT_PADDING_WORDS(1);
109 u64_le additional_params_addr;
110 u64_le additional_params_sz;
111 u32_le mix_id;
112 u32_le splitter_info_id;
113 std::array<WaveBuffer, 4> wave_buffer;
114 std::array<u32_le, 6> voice_channel_resource_ids;
115 INSERT_PADDING_BYTES(24);
116};
117static_assert(sizeof(VoiceInfo) == 0x170, "VoiceInfo is wrong size");
118
119struct VoiceOutStatus {
120 u64_le played_sample_count;
121 u32_le wave_buffer_consumed;
122 u32_le voice_drops_count;
123};
124static_assert(sizeof(VoiceOutStatus) == 0x10, "VoiceOutStatus has wrong size");
125
126struct UpdateDataHeader {
127 UpdateDataHeader() {}
128
129 explicit UpdateDataHeader(const AudioRendererParameter& config) {
130 revision = Common::MakeMagic('R', 'E', 'V', '4'); // 5.1.0 Revision
131 behavior_size = 0xb0;
132 memory_pools_size = (config.effect_count + (config.voice_count * 4)) * 0x10;
133 voices_size = config.voice_count * 0x10;
134 voice_resource_size = 0x0;
135 effects_size = config.effect_count * 0x10;
136 mixes_size = 0x0;
137 sinks_size = config.sink_count * 0x20;
138 performance_manager_size = 0x10;
139 total_size = sizeof(UpdateDataHeader) + behavior_size + memory_pools_size + voices_size +
140 effects_size + sinks_size + performance_manager_size;
141 }
142
143 u32_le revision;
144 u32_le behavior_size;
145 u32_le memory_pools_size;
146 u32_le voices_size;
147 u32_le voice_resource_size;
148 u32_le effects_size;
149 u32_le mixes_size;
150 u32_le sinks_size;
151 u32_le performance_manager_size;
152 INSERT_PADDING_WORDS(6);
153 u32_le total_size;
154};
155static_assert(sizeof(UpdateDataHeader) == 0x40, "UpdateDataHeader has wrong size");
156
157class AudioRenderer {
158public:
159 AudioRenderer(AudioRendererParameter params, Kernel::SharedPtr<Kernel::Event> buffer_event);
160 std::vector<u8> UpdateAudioRenderer(const std::vector<u8>& input_params);
161 void QueueMixedBuffer(Buffer::Tag tag);
162 void ReleaseAndQueueBuffers();
163
164private:
165 class VoiceState {
166 public:
167 bool IsPlaying() const {
168 return is_in_use && info.play_state == PlayState::Started;
169 }
170
171 const VoiceOutStatus& GetOutStatus() const {
172 return out_status;
173 }
174
175 const VoiceInfo& GetInfo() const {
176 return info;
177 }
178
179 VoiceInfo& Info() {
180 return info;
181 }
182
183 void SetWaveIndex(size_t index);
184 std::vector<s16> DequeueSamples(size_t sample_count);
185 void UpdateState();
186 void RefreshBuffer();
187
188 private:
189 bool is_in_use{};
190 bool is_refresh_pending{};
191 size_t wave_index{};
192 size_t offset{};
193 Codec::ADPCMState adpcm_state{};
194 std::vector<s16> samples;
195 VoiceOutStatus out_status{};
196 VoiceInfo info{};
197 };
198
199 AudioRendererParameter worker_params;
200 Kernel::SharedPtr<Kernel::Event> buffer_event;
201 std::vector<VoiceState> voices;
202 std::unique_ptr<AudioCore::AudioOut> audio_core;
203 AudioCore::StreamPtr stream;
204};
205
206} // namespace AudioCore
diff --git a/src/audio_core/buffer.h b/src/audio_core/buffer.h
index 4bf5fd58a..a323b23ec 100644
--- a/src/audio_core/buffer.h
+++ b/src/audio_core/buffer.h
@@ -18,11 +18,16 @@ class Buffer {
18public: 18public:
19 using Tag = u64; 19 using Tag = u64;
20 20
21 Buffer(Tag tag, std::vector<u8>&& data) : tag{tag}, data{std::move(data)} {} 21 Buffer(Tag tag, std::vector<s16>&& samples) : tag{tag}, samples{std::move(samples)} {}
22 22
23 /// Returns the raw audio data for the buffer 23 /// Returns the raw audio data for the buffer
24 const std::vector<u8>& GetData() const { 24 std::vector<s16>& Samples() {
25 return data; 25 return samples;
26 }
27
28 /// Returns the raw audio data for the buffer
29 const std::vector<s16>& GetSamples() const {
30 return samples;
26 } 31 }
27 32
28 /// Returns the buffer tag, this is provided by the game to the audout service 33 /// Returns the buffer tag, this is provided by the game to the audout service
@@ -32,7 +37,7 @@ public:
32 37
33private: 38private:
34 Tag tag; 39 Tag tag;
35 std::vector<u8> data; 40 std::vector<s16> samples;
36}; 41};
37 42
38using BufferPtr = std::shared_ptr<Buffer>; 43using BufferPtr = std::shared_ptr<Buffer>;
diff --git a/src/audio_core/codec.cpp b/src/audio_core/codec.cpp
new file mode 100644
index 000000000..c3021403f
--- /dev/null
+++ b/src/audio_core/codec.cpp
@@ -0,0 +1,77 @@
1// Copyright 2018 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <algorithm>
6
7#include "audio_core/codec.h"
8
9namespace AudioCore::Codec {
10
11std::vector<s16> DecodeADPCM(const u8* const data, size_t size, const ADPCM_Coeff& coeff,
12 ADPCMState& state) {
13 // GC-ADPCM with scale factor and variable coefficients.
14 // Frames are 8 bytes long containing 14 samples each.
15 // Samples are 4 bits (one nibble) long.
16
17 constexpr size_t FRAME_LEN = 8;
18 constexpr size_t SAMPLES_PER_FRAME = 14;
19 constexpr std::array<int, 16> SIGNED_NIBBLES = {
20 {0, 1, 2, 3, 4, 5, 6, 7, -8, -7, -6, -5, -4, -3, -2, -1}};
21
22 const size_t sample_count = (size / FRAME_LEN) * SAMPLES_PER_FRAME;
23 const size_t ret_size =
24 sample_count % 2 == 0 ? sample_count : sample_count + 1; // Ensure multiple of two.
25 std::vector<s16> ret(ret_size);
26
27 int yn1 = state.yn1, yn2 = state.yn2;
28
29 const size_t NUM_FRAMES =
30 (sample_count + (SAMPLES_PER_FRAME - 1)) / SAMPLES_PER_FRAME; // Round up.
31 for (size_t framei = 0; framei < NUM_FRAMES; framei++) {
32 const int frame_header = data[framei * FRAME_LEN];
33 const int scale = 1 << (frame_header & 0xF);
34 const int idx = (frame_header >> 4) & 0x7;
35
36 // Coefficients are fixed point with 11 bits fractional part.
37 const int coef1 = coeff[idx * 2 + 0];
38 const int coef2 = coeff[idx * 2 + 1];
39
40 // Decodes an audio sample. One nibble produces one sample.
41 const auto decode_sample = [&](const int nibble) -> s16 {
42 const int xn = nibble * scale;
43 // We first transform everything into 11 bit fixed point, perform the second order
44 // digital filter, then transform back.
45 // 0x400 == 0.5 in 11 bit fixed point.
46 // Filter: y[n] = x[n] + 0.5 + c1 * y[n-1] + c2 * y[n-2]
47 int val = ((xn << 11) + 0x400 + coef1 * yn1 + coef2 * yn2) >> 11;
48 // Clamp to output range.
49 val = std::clamp<s32>(val, -32768, 32767);
50 // Advance output feedback.
51 yn2 = yn1;
52 yn1 = val;
53 return static_cast<s16>(val);
54 };
55
56 size_t outputi = framei * SAMPLES_PER_FRAME;
57 size_t datai = framei * FRAME_LEN + 1;
58 for (size_t i = 0; i < SAMPLES_PER_FRAME && outputi < sample_count; i += 2) {
59 const s16 sample1 = decode_sample(SIGNED_NIBBLES[data[datai] >> 4]);
60 ret[outputi] = sample1;
61 outputi++;
62
63 const s16 sample2 = decode_sample(SIGNED_NIBBLES[data[datai] & 0xF]);
64 ret[outputi] = sample2;
65 outputi++;
66
67 datai++;
68 }
69 }
70
71 state.yn1 = yn1;
72 state.yn2 = yn2;
73
74 return ret;
75}
76
77} // namespace AudioCore::Codec
diff --git a/src/audio_core/codec.h b/src/audio_core/codec.h
new file mode 100644
index 000000000..3f845c42c
--- /dev/null
+++ b/src/audio_core/codec.h
@@ -0,0 +1,44 @@
1// Copyright 2018 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <array>
8#include <vector>
9
10#include "common/common_types.h"
11
12namespace AudioCore::Codec {
13
14enum class PcmFormat : u32 {
15 Invalid = 0,
16 Int8 = 1,
17 Int16 = 2,
18 Int24 = 3,
19 Int32 = 4,
20 PcmFloat = 5,
21 Adpcm = 6,
22};
23
24/// See: Codec::DecodeADPCM
25struct ADPCMState {
26 // Two historical samples from previous processed buffer,
27 // required for ADPCM decoding
28 s16 yn1; ///< y[n-1]
29 s16 yn2; ///< y[n-2]
30};
31
32using ADPCM_Coeff = std::array<s16, 16>;
33
34/**
35 * @param data Pointer to buffer that contains ADPCM data to decode
36 * @param size Size of buffer in bytes
37 * @param coeff ADPCM coefficients
38 * @param state ADPCM state, this is updated with new state
39 * @return Decoded stereo signed PCM16 data, sample_count in length
40 */
41std::vector<s16> DecodeADPCM(const u8* const data, size_t size, const ADPCM_Coeff& coeff,
42 ADPCMState& state);
43
44}; // namespace AudioCore::Codec
diff --git a/src/audio_core/cubeb_sink.cpp b/src/audio_core/cubeb_sink.cpp
index 34ae5b062..1501ef1f4 100644
--- a/src/audio_core/cubeb_sink.cpp
+++ b/src/audio_core/cubeb_sink.cpp
@@ -13,20 +13,30 @@ namespace AudioCore {
13 13
14class SinkStreamImpl final : public SinkStream { 14class SinkStreamImpl final : public SinkStream {
15public: 15public:
16 SinkStreamImpl(cubeb* ctx, cubeb_devid output_device) : ctx{ctx} { 16 SinkStreamImpl(cubeb* ctx, u32 sample_rate, u32 num_channels_, cubeb_devid output_device,
17 cubeb_stream_params params; 17 const std::string& name)
18 params.rate = 48000; 18 : ctx{ctx}, num_channels{num_channels_} {
19 params.channels = GetNumChannels(); 19
20 if (num_channels == 6) {
21 // 6-channel audio does not seem to work with cubeb + SDL, so we downsample this to 2
22 // channel for now
23 is_6_channel = true;
24 num_channels = 2;
25 }
26
27 cubeb_stream_params params{};
28 params.rate = sample_rate;
29 params.channels = num_channels;
20 params.format = CUBEB_SAMPLE_S16NE; 30 params.format = CUBEB_SAMPLE_S16NE;
21 params.layout = CUBEB_LAYOUT_STEREO; 31 params.layout = num_channels == 1 ? CUBEB_LAYOUT_MONO : CUBEB_LAYOUT_STEREO;
22 32
23 u32 minimum_latency = 0; 33 u32 minimum_latency{};
24 if (cubeb_get_min_latency(ctx, &params, &minimum_latency) != CUBEB_OK) { 34 if (cubeb_get_min_latency(ctx, &params, &minimum_latency) != CUBEB_OK) {
25 LOG_CRITICAL(Audio_Sink, "Error getting minimum latency"); 35 LOG_CRITICAL(Audio_Sink, "Error getting minimum latency");
26 } 36 }
27 37
28 if (cubeb_stream_init(ctx, &stream_backend, "yuzu Audio Output", nullptr, nullptr, 38 if (cubeb_stream_init(ctx, &stream_backend, name.c_str(), nullptr, nullptr, output_device,
29 output_device, &params, std::max(512u, minimum_latency), 39 &params, std::max(512u, minimum_latency),
30 &SinkStreamImpl::DataCallback, &SinkStreamImpl::StateCallback, 40 &SinkStreamImpl::DataCallback, &SinkStreamImpl::StateCallback,
31 this) != CUBEB_OK) { 41 this) != CUBEB_OK) {
32 LOG_CRITICAL(Audio_Sink, "Error initializing cubeb stream"); 42 LOG_CRITICAL(Audio_Sink, "Error initializing cubeb stream");
@@ -51,33 +61,29 @@ public:
51 cubeb_stream_destroy(stream_backend); 61 cubeb_stream_destroy(stream_backend);
52 } 62 }
53 63
54 void EnqueueSamples(u32 num_channels, const s16* samples, size_t sample_count) override { 64 void EnqueueSamples(u32 num_channels, const std::vector<s16>& samples) override {
55 if (!ctx) { 65 if (!ctx) {
56 return; 66 return;
57 } 67 }
58 68
59 queue.reserve(queue.size() + sample_count * GetNumChannels()); 69 queue.reserve(queue.size() + samples.size() * GetNumChannels());
60 70
61 if (num_channels == 2) { 71 if (is_6_channel) {
62 // Copy as-is
63 std::copy(samples, samples + sample_count * GetNumChannels(),
64 std::back_inserter(queue));
65 } else if (num_channels == 6) {
66 // Downsample 6 channels to 2 72 // Downsample 6 channels to 2
67 const size_t sample_count_copy_size = sample_count * num_channels * 2; 73 const size_t sample_count_copy_size = samples.size() * 2;
68 queue.reserve(sample_count_copy_size); 74 queue.reserve(sample_count_copy_size);
69 for (size_t i = 0; i < sample_count * num_channels; i += num_channels) { 75 for (size_t i = 0; i < samples.size(); i += num_channels) {
70 queue.push_back(samples[i]); 76 queue.push_back(samples[i]);
71 queue.push_back(samples[i + 1]); 77 queue.push_back(samples[i + 1]);
72 } 78 }
73 } else { 79 } else {
74 ASSERT_MSG(false, "Unimplemented"); 80 // Copy as-is
81 std::copy(samples.begin(), samples.end(), std::back_inserter(queue));
75 } 82 }
76 } 83 }
77 84
78 u32 GetNumChannels() const { 85 u32 GetNumChannels() const {
79 // Only support 2-channel stereo output for now 86 return num_channels;
80 return 2;
81 } 87 }
82 88
83private: 89private:
@@ -85,6 +91,8 @@ private:
85 91
86 cubeb* ctx{}; 92 cubeb* ctx{};
87 cubeb_stream* stream_backend{}; 93 cubeb_stream* stream_backend{};
94 u32 num_channels{};
95 bool is_6_channel{};
88 96
89 std::vector<s16> queue; 97 std::vector<s16> queue;
90 98
@@ -129,8 +137,10 @@ CubebSink::~CubebSink() {
129 cubeb_destroy(ctx); 137 cubeb_destroy(ctx);
130} 138}
131 139
132SinkStream& CubebSink::AcquireSinkStream(u32 sample_rate, u32 num_channels) { 140SinkStream& CubebSink::AcquireSinkStream(u32 sample_rate, u32 num_channels,
133 sink_streams.push_back(std::make_unique<SinkStreamImpl>(ctx, output_device)); 141 const std::string& name) {
142 sink_streams.push_back(
143 std::make_unique<SinkStreamImpl>(ctx, sample_rate, num_channels, output_device, name));
134 return *sink_streams.back(); 144 return *sink_streams.back();
135} 145}
136 146
diff --git a/src/audio_core/cubeb_sink.h b/src/audio_core/cubeb_sink.h
index d07113f1f..59cbf05e9 100644
--- a/src/audio_core/cubeb_sink.h
+++ b/src/audio_core/cubeb_sink.h
@@ -18,7 +18,8 @@ public:
18 explicit CubebSink(std::string device_id); 18 explicit CubebSink(std::string device_id);
19 ~CubebSink() override; 19 ~CubebSink() override;
20 20
21 SinkStream& AcquireSinkStream(u32 sample_rate, u32 num_channels) override; 21 SinkStream& AcquireSinkStream(u32 sample_rate, u32 num_channels,
22 const std::string& name) override;
22 23
23private: 24private:
24 cubeb* ctx{}; 25 cubeb* ctx{};
diff --git a/src/audio_core/null_sink.h b/src/audio_core/null_sink.h
index 2e04438f7..f235d93e5 100644
--- a/src/audio_core/null_sink.h
+++ b/src/audio_core/null_sink.h
@@ -13,14 +13,14 @@ public:
13 explicit NullSink(std::string){}; 13 explicit NullSink(std::string){};
14 ~NullSink() override = default; 14 ~NullSink() override = default;
15 15
16 SinkStream& AcquireSinkStream(u32 /*sample_rate*/, u32 /*num_channels*/) override { 16 SinkStream& AcquireSinkStream(u32 /*sample_rate*/, u32 /*num_channels*/,
17 const std::string& /*name*/) override {
17 return null_sink_stream; 18 return null_sink_stream;
18 } 19 }
19 20
20private: 21private:
21 struct NullSinkStreamImpl final : SinkStream { 22 struct NullSinkStreamImpl final : SinkStream {
22 void EnqueueSamples(u32 /*num_channels*/, const s16* /*samples*/, 23 void EnqueueSamples(u32 /*num_channels*/, const std::vector<s16>& /*samples*/) override {}
23 size_t /*sample_count*/) override {}
24 } null_sink_stream; 24 } null_sink_stream;
25}; 25};
26 26
diff --git a/src/audio_core/sink.h b/src/audio_core/sink.h
index d1bb98c3d..95c7b2b6e 100644
--- a/src/audio_core/sink.h
+++ b/src/audio_core/sink.h
@@ -5,6 +5,7 @@
5#pragma once 5#pragma once
6 6
7#include <memory> 7#include <memory>
8#include <string>
8 9
9#include "audio_core/sink_stream.h" 10#include "audio_core/sink_stream.h"
10#include "common/common_types.h" 11#include "common/common_types.h"
@@ -21,7 +22,8 @@ constexpr char auto_device_name[] = "auto";
21class Sink { 22class Sink {
22public: 23public:
23 virtual ~Sink() = default; 24 virtual ~Sink() = default;
24 virtual SinkStream& AcquireSinkStream(u32 sample_rate, u32 num_channels) = 0; 25 virtual SinkStream& AcquireSinkStream(u32 sample_rate, u32 num_channels,
26 const std::string& name) = 0;
25}; 27};
26 28
27using SinkPtr = std::unique_ptr<Sink>; 29using SinkPtr = std::unique_ptr<Sink>;
diff --git a/src/audio_core/sink_stream.h b/src/audio_core/sink_stream.h
index e7a3f01b0..41b6736d8 100644
--- a/src/audio_core/sink_stream.h
+++ b/src/audio_core/sink_stream.h
@@ -5,6 +5,7 @@
5#pragma once 5#pragma once
6 6
7#include <memory> 7#include <memory>
8#include <vector>
8 9
9#include "common/common_types.h" 10#include "common/common_types.h"
10 11
@@ -22,9 +23,8 @@ public:
22 * Feed stereo samples to sink. 23 * Feed stereo samples to sink.
23 * @param num_channels Number of channels used. 24 * @param num_channels Number of channels used.
24 * @param samples Samples in interleaved stereo PCM16 format. 25 * @param samples Samples in interleaved stereo PCM16 format.
25 * @param sample_count Number of samples.
26 */ 26 */
27 virtual void EnqueueSamples(u32 num_channels, const s16* samples, size_t sample_count) = 0; 27 virtual void EnqueueSamples(u32 num_channels, const std::vector<s16>& samples) = 0;
28}; 28};
29 29
30using SinkStreamPtr = std::unique_ptr<SinkStream>; 30using SinkStreamPtr = std::unique_ptr<SinkStream>;
diff --git a/src/audio_core/stream.cpp b/src/audio_core/stream.cpp
index a0045b7a1..ad9e2915c 100644
--- a/src/audio_core/stream.cpp
+++ b/src/audio_core/stream.cpp
@@ -32,17 +32,13 @@ u32 Stream::GetNumChannels() const {
32 return {}; 32 return {};
33} 33}
34 34
35u32 Stream::GetSampleSize() const {
36 return GetNumChannels() * 2;
37}
38
39Stream::Stream(u32 sample_rate, Format format, ReleaseCallback&& release_callback, 35Stream::Stream(u32 sample_rate, Format format, ReleaseCallback&& release_callback,
40 SinkStream& sink_stream) 36 SinkStream& sink_stream, std::string&& name_)
41 : sample_rate{sample_rate}, format{format}, release_callback{std::move(release_callback)}, 37 : sample_rate{sample_rate}, format{format}, release_callback{std::move(release_callback)},
42 sink_stream{sink_stream} { 38 sink_stream{sink_stream}, name{std::move(name_)} {
43 39
44 release_event = CoreTiming::RegisterEvent( 40 release_event = CoreTiming::RegisterEvent(
45 "Stream::Release", [this](u64 userdata, int cycles_late) { ReleaseActiveBuffer(); }); 41 name, [this](u64 userdata, int cycles_late) { ReleaseActiveBuffer(); });
46} 42}
47 43
48void Stream::Play() { 44void Stream::Play() {
@@ -55,17 +51,15 @@ void Stream::Stop() {
55} 51}
56 52
57s64 Stream::GetBufferReleaseCycles(const Buffer& buffer) const { 53s64 Stream::GetBufferReleaseCycles(const Buffer& buffer) const {
58 const size_t num_samples{buffer.GetData().size() / GetSampleSize()}; 54 const size_t num_samples{buffer.GetSamples().size() / GetNumChannels()};
59 return CoreTiming::usToCycles((static_cast<u64>(num_samples) * 1000000) / sample_rate); 55 return CoreTiming::usToCycles((static_cast<u64>(num_samples) * 1000000) / sample_rate);
60} 56}
61 57
62static std::vector<s16> GetVolumeAdjustedSamples(const std::vector<u8>& data) { 58static void VolumeAdjustSamples(std::vector<s16>& samples) {
63 std::vector<s16> samples(data.size() / sizeof(s16));
64 std::memcpy(samples.data(), data.data(), data.size());
65 const float volume{std::clamp(Settings::values.volume, 0.0f, 1.0f)}; 59 const float volume{std::clamp(Settings::values.volume, 0.0f, 1.0f)};
66 60
67 if (volume == 1.0f) { 61 if (volume == 1.0f) {
68 return samples; 62 return;
69 } 63 }
70 64
71 // Implementation of a volume slider with a dynamic range of 60 dB 65 // Implementation of a volume slider with a dynamic range of 60 dB
@@ -73,8 +67,6 @@ static std::vector<s16> GetVolumeAdjustedSamples(const std::vector<u8>& data) {
73 for (auto& sample : samples) { 67 for (auto& sample : samples) {
74 sample = static_cast<s16>(sample * volume_scale_factor); 68 sample = static_cast<s16>(sample * volume_scale_factor);
75 } 69 }
76
77 return samples;
78} 70}
79 71
80void Stream::PlayNextBuffer() { 72void Stream::PlayNextBuffer() {
@@ -96,14 +88,14 @@ void Stream::PlayNextBuffer() {
96 active_buffer = queued_buffers.front(); 88 active_buffer = queued_buffers.front();
97 queued_buffers.pop(); 89 queued_buffers.pop();
98 90
99 const size_t sample_count{active_buffer->GetData().size() / GetSampleSize()}; 91 VolumeAdjustSamples(active_buffer->Samples());
100 sink_stream.EnqueueSamples( 92 sink_stream.EnqueueSamples(GetNumChannels(), active_buffer->GetSamples());
101 GetNumChannels(), GetVolumeAdjustedSamples(active_buffer->GetData()).data(), sample_count);
102 93
103 CoreTiming::ScheduleEventThreadsafe(GetBufferReleaseCycles(*active_buffer), release_event, {}); 94 CoreTiming::ScheduleEventThreadsafe(GetBufferReleaseCycles(*active_buffer), release_event, {});
104} 95}
105 96
106void Stream::ReleaseActiveBuffer() { 97void Stream::ReleaseActiveBuffer() {
98 ASSERT(active_buffer);
107 released_buffers.push(std::move(active_buffer)); 99 released_buffers.push(std::move(active_buffer));
108 release_callback(); 100 release_callback();
109 PlayNextBuffer(); 101 PlayNextBuffer();
diff --git a/src/audio_core/stream.h b/src/audio_core/stream.h
index 35253920e..049b92ca9 100644
--- a/src/audio_core/stream.h
+++ b/src/audio_core/stream.h
@@ -6,6 +6,7 @@
6 6
7#include <functional> 7#include <functional>
8#include <memory> 8#include <memory>
9#include <string>
9#include <vector> 10#include <vector>
10#include <queue> 11#include <queue>
11 12
@@ -33,7 +34,7 @@ public:
33 using ReleaseCallback = std::function<void()>; 34 using ReleaseCallback = std::function<void()>;
34 35
35 Stream(u32 sample_rate, Format format, ReleaseCallback&& release_callback, 36 Stream(u32 sample_rate, Format format, ReleaseCallback&& release_callback,
36 SinkStream& sink_stream); 37 SinkStream& sink_stream, std::string&& name_);
37 38
38 /// Plays the audio stream 39 /// Plays the audio stream
39 void Play(); 40 void Play();
@@ -68,9 +69,6 @@ public:
68 /// Gets the number of channels 69 /// Gets the number of channels
69 u32 GetNumChannels() const; 70 u32 GetNumChannels() const;
70 71
71 /// Gets the sample size in bytes
72 u32 GetSampleSize() const;
73
74private: 72private:
75 /// Current state of the stream 73 /// Current state of the stream
76 enum class State { 74 enum class State {
@@ -96,6 +94,7 @@ private:
96 std::queue<BufferPtr> queued_buffers; ///< Buffers queued to be played in the stream 94 std::queue<BufferPtr> queued_buffers; ///< Buffers queued to be played in the stream
97 std::queue<BufferPtr> released_buffers; ///< Buffers recently released from the stream 95 std::queue<BufferPtr> released_buffers; ///< Buffers recently released from the stream
98 SinkStream& sink_stream; ///< Output sink for the stream 96 SinkStream& sink_stream; ///< Output sink for the stream
97 std::string name; ///< Name of the stream, must be unique
99}; 98};
100 99
101using StreamPtr = std::shared_ptr<Stream>; 100using StreamPtr = std::shared_ptr<Stream>;
diff --git a/src/core/arm/unicorn/arm_unicorn.cpp b/src/core/arm/unicorn/arm_unicorn.cpp
index 4c11f35a4..6bc349460 100644
--- a/src/core/arm/unicorn/arm_unicorn.cpp
+++ b/src/core/arm/unicorn/arm_unicorn.cpp
@@ -203,7 +203,7 @@ void ARM_Unicorn::ExecuteInstructions(int num_instructions) {
203 } 203 }
204 Kernel::Thread* thread = Kernel::GetCurrentThread(); 204 Kernel::Thread* thread = Kernel::GetCurrentThread();
205 SaveContext(thread->context); 205 SaveContext(thread->context);
206 if (last_bkpt_hit || (num_instructions == 1)) { 206 if (last_bkpt_hit || GDBStub::GetCpuStepFlag()) {
207 last_bkpt_hit = false; 207 last_bkpt_hit = false;
208 GDBStub::Break(); 208 GDBStub::Break();
209 GDBStub::SendTrap(thread, 5); 209 GDBStub::SendTrap(thread, 5);
diff --git a/src/core/core.cpp b/src/core/core.cpp
index e01c45cdd..085ba68d0 100644
--- a/src/core/core.cpp
+++ b/src/core/core.cpp
@@ -62,7 +62,6 @@ System::ResultStatus System::RunLoop(bool tight_loop) {
62 // execute. Otherwise, get out of the loop function. 62 // execute. Otherwise, get out of the loop function.
63 if (GDBStub::GetCpuHaltFlag()) { 63 if (GDBStub::GetCpuHaltFlag()) {
64 if (GDBStub::GetCpuStepFlag()) { 64 if (GDBStub::GetCpuStepFlag()) {
65 GDBStub::SetCpuStepFlag(false);
66 tight_loop = false; 65 tight_loop = false;
67 } else { 66 } else {
68 return ResultStatus::Success; 67 return ResultStatus::Success;
@@ -78,6 +77,10 @@ System::ResultStatus System::RunLoop(bool tight_loop) {
78 } 77 }
79 } 78 }
80 79
80 if (GDBStub::IsServerEnabled()) {
81 GDBStub::SetCpuStepFlag(false);
82 }
83
81 return status; 84 return status;
82} 85}
83 86
diff --git a/src/core/core.h b/src/core/core.h
index a3be88aa8..c8ca4b247 100644
--- a/src/core/core.h
+++ b/src/core/core.h
@@ -82,6 +82,17 @@ public:
82 */ 82 */
83 ResultStatus SingleStep(); 83 ResultStatus SingleStep();
84 84
85 /**
86 * Invalidate the CPU instruction caches
87 * This function should only be used by GDB Stub to support breakpoints, memory updates and
88 * step/continue commands.
89 */
90 void InvalidateCpuInstructionCaches() {
91 for (auto& cpu : cpu_cores) {
92 cpu->ArmInterface().ClearInstructionCache();
93 }
94 }
95
85 /// Shutdown the emulated system. 96 /// Shutdown the emulated system.
86 void Shutdown(); 97 void Shutdown();
87 98
diff --git a/src/core/core_timing.cpp b/src/core/core_timing.cpp
index a1b6f96f1..d3bb6f818 100644
--- a/src/core/core_timing.cpp
+++ b/src/core/core_timing.cpp
@@ -141,7 +141,7 @@ void ScheduleEvent(s64 cycles_into_future, const EventType* event_type, u64 user
141 ForceExceptionCheck(cycles_into_future); 141 ForceExceptionCheck(cycles_into_future);
142 142
143 event_queue.emplace_back(Event{timeout, event_fifo_id++, userdata, event_type}); 143 event_queue.emplace_back(Event{timeout, event_fifo_id++, userdata, event_type});
144 std::push_heap(event_queue.begin(), event_queue.end(), std::greater<Event>()); 144 std::push_heap(event_queue.begin(), event_queue.end(), std::greater<>());
145} 145}
146 146
147void ScheduleEventThreadsafe(s64 cycles_into_future, const EventType* event_type, u64 userdata) { 147void ScheduleEventThreadsafe(s64 cycles_into_future, const EventType* event_type, u64 userdata) {
@@ -156,7 +156,7 @@ void UnscheduleEvent(const EventType* event_type, u64 userdata) {
156 // Removing random items breaks the invariant so we have to re-establish it. 156 // Removing random items breaks the invariant so we have to re-establish it.
157 if (itr != event_queue.end()) { 157 if (itr != event_queue.end()) {
158 event_queue.erase(itr, event_queue.end()); 158 event_queue.erase(itr, event_queue.end());
159 std::make_heap(event_queue.begin(), event_queue.end(), std::greater<Event>()); 159 std::make_heap(event_queue.begin(), event_queue.end(), std::greater<>());
160 } 160 }
161} 161}
162 162
@@ -167,7 +167,7 @@ void RemoveEvent(const EventType* event_type) {
167 // Removing random items breaks the invariant so we have to re-establish it. 167 // Removing random items breaks the invariant so we have to re-establish it.
168 if (itr != event_queue.end()) { 168 if (itr != event_queue.end()) {
169 event_queue.erase(itr, event_queue.end()); 169 event_queue.erase(itr, event_queue.end());
170 std::make_heap(event_queue.begin(), event_queue.end(), std::greater<Event>()); 170 std::make_heap(event_queue.begin(), event_queue.end(), std::greater<>());
171 } 171 }
172} 172}
173 173
@@ -190,7 +190,7 @@ void MoveEvents() {
190 for (Event ev; ts_queue.Pop(ev);) { 190 for (Event ev; ts_queue.Pop(ev);) {
191 ev.fifo_order = event_fifo_id++; 191 ev.fifo_order = event_fifo_id++;
192 event_queue.emplace_back(std::move(ev)); 192 event_queue.emplace_back(std::move(ev));
193 std::push_heap(event_queue.begin(), event_queue.end(), std::greater<Event>()); 193 std::push_heap(event_queue.begin(), event_queue.end(), std::greater<>());
194 } 194 }
195} 195}
196 196
@@ -205,7 +205,7 @@ void Advance() {
205 205
206 while (!event_queue.empty() && event_queue.front().time <= global_timer) { 206 while (!event_queue.empty() && event_queue.front().time <= global_timer) {
207 Event evt = std::move(event_queue.front()); 207 Event evt = std::move(event_queue.front());
208 std::pop_heap(event_queue.begin(), event_queue.end(), std::greater<Event>()); 208 std::pop_heap(event_queue.begin(), event_queue.end(), std::greater<>());
209 event_queue.pop_back(); 209 event_queue.pop_back();
210 evt.type->callback(evt.userdata, static_cast<int>(global_timer - evt.time)); 210 evt.type->callback(evt.userdata, static_cast<int>(global_timer - evt.time));
211 } 211 }
@@ -226,8 +226,8 @@ void Idle() {
226 downcount = 0; 226 downcount = 0;
227} 227}
228 228
229u64 GetGlobalTimeUs() { 229std::chrono::microseconds GetGlobalTimeUs() {
230 return GetTicks() * 1000000 / BASE_CLOCK_RATE; 230 return std::chrono::microseconds{GetTicks() * 1000000 / BASE_CLOCK_RATE};
231} 231}
232 232
233int GetDowncount() { 233int GetDowncount() {
diff --git a/src/core/core_timing.h b/src/core/core_timing.h
index 7fe6380ad..dfa161c0d 100644
--- a/src/core/core_timing.h
+++ b/src/core/core_timing.h
@@ -17,12 +17,17 @@
17 * ScheduleEvent(periodInCycles - cyclesLate, callback, "whatever") 17 * ScheduleEvent(periodInCycles - cyclesLate, callback, "whatever")
18 */ 18 */
19 19
20#include <chrono>
20#include <functional> 21#include <functional>
21#include <string> 22#include <string>
22#include "common/common_types.h" 23#include "common/common_types.h"
23 24
24namespace CoreTiming { 25namespace CoreTiming {
25 26
27struct EventType;
28
29using TimedCallback = std::function<void(u64 userdata, int cycles_late)>;
30
26/** 31/**
27 * CoreTiming begins at the boundary of timing slice -1. An initial call to Advance() is 32 * CoreTiming begins at the boundary of timing slice -1. An initial call to Advance() is
28 * required to end slice -1 and start slice 0 before the first cycle of code is executed. 33 * required to end slice -1 and start slice 0 before the first cycle of code is executed.
@@ -30,8 +35,6 @@ namespace CoreTiming {
30void Init(); 35void Init();
31void Shutdown(); 36void Shutdown();
32 37
33typedef std::function<void(u64 userdata, int cycles_late)> TimedCallback;
34
35/** 38/**
36 * This should only be called from the emu thread, if you are calling it any other thread, you are 39 * This should only be called from the emu thread, if you are calling it any other thread, you are
37 * doing something evil 40 * doing something evil
@@ -40,8 +43,6 @@ u64 GetTicks();
40u64 GetIdleTicks(); 43u64 GetIdleTicks();
41void AddTicks(u64 ticks); 44void AddTicks(u64 ticks);
42 45
43struct EventType;
44
45/** 46/**
46 * Returns the event_type identifier. if name is not unique, it will assert. 47 * Returns the event_type identifier. if name is not unique, it will assert.
47 */ 48 */
@@ -86,7 +87,7 @@ void ClearPendingEvents();
86 87
87void ForceExceptionCheck(s64 cycles); 88void ForceExceptionCheck(s64 cycles);
88 89
89u64 GetGlobalTimeUs(); 90std::chrono::microseconds GetGlobalTimeUs();
90 91
91int GetDowncount(); 92int GetDowncount();
92 93
diff --git a/src/core/gdbstub/gdbstub.cpp b/src/core/gdbstub/gdbstub.cpp
index 75f6b8235..332e5c3d0 100644
--- a/src/core/gdbstub/gdbstub.cpp
+++ b/src/core/gdbstub/gdbstub.cpp
@@ -41,40 +41,42 @@
41#include "core/loader/loader.h" 41#include "core/loader/loader.h"
42#include "core/memory.h" 42#include "core/memory.h"
43 43
44const int GDB_BUFFER_SIZE = 10000; 44namespace GDBStub {
45namespace {
46constexpr int GDB_BUFFER_SIZE = 10000;
45 47
46const char GDB_STUB_START = '$'; 48constexpr char GDB_STUB_START = '$';
47const char GDB_STUB_END = '#'; 49constexpr char GDB_STUB_END = '#';
48const char GDB_STUB_ACK = '+'; 50constexpr char GDB_STUB_ACK = '+';
49const char GDB_STUB_NACK = '-'; 51constexpr char GDB_STUB_NACK = '-';
50 52
51#ifndef SIGTRAP 53#ifndef SIGTRAP
52const u32 SIGTRAP = 5; 54constexpr u32 SIGTRAP = 5;
53#endif 55#endif
54 56
55#ifndef SIGTERM 57#ifndef SIGTERM
56const u32 SIGTERM = 15; 58constexpr u32 SIGTERM = 15;
57#endif 59#endif
58 60
59#ifndef MSG_WAITALL 61#ifndef MSG_WAITALL
60const u32 MSG_WAITALL = 8; 62constexpr u32 MSG_WAITALL = 8;
61#endif 63#endif
62 64
63const u32 LR_REGISTER = 30; 65constexpr u32 LR_REGISTER = 30;
64const u32 SP_REGISTER = 31; 66constexpr u32 SP_REGISTER = 31;
65const u32 PC_REGISTER = 32; 67constexpr u32 PC_REGISTER = 32;
66const u32 CPSR_REGISTER = 33; 68constexpr u32 CPSR_REGISTER = 33;
67const u32 UC_ARM64_REG_Q0 = 34; 69constexpr u32 UC_ARM64_REG_Q0 = 34;
68const u32 FPSCR_REGISTER = 66; 70constexpr u32 FPSCR_REGISTER = 66;
69 71
70// TODO/WiP - Used while working on support for FPU 72// TODO/WiP - Used while working on support for FPU
71const u32 TODO_DUMMY_REG_997 = 997; 73constexpr u32 TODO_DUMMY_REG_997 = 997;
72const u32 TODO_DUMMY_REG_998 = 998; 74constexpr u32 TODO_DUMMY_REG_998 = 998;
73 75
74// For sample XML files see the GDB source /gdb/features 76// For sample XML files see the GDB source /gdb/features
75// GDB also wants the l character at the start 77// GDB also wants the l character at the start
76// This XML defines what the registers are for this specific ARM device 78// This XML defines what the registers are for this specific ARM device
77static const char* target_xml = 79constexpr char target_xml[] =
78 R"(l<?xml version="1.0"?> 80 R"(l<?xml version="1.0"?>
79<!DOCTYPE target SYSTEM "gdb-target.dtd"> 81<!DOCTYPE target SYSTEM "gdb-target.dtd">
80<target version="1.0"> 82<target version="1.0">
@@ -140,30 +142,28 @@ static const char* target_xml =
140</target> 142</target>
141)"; 143)";
142 144
143namespace GDBStub { 145int gdbserver_socket = -1;
144
145static int gdbserver_socket = -1;
146 146
147static u8 command_buffer[GDB_BUFFER_SIZE]; 147u8 command_buffer[GDB_BUFFER_SIZE];
148static u32 command_length; 148u32 command_length;
149 149
150static u32 latest_signal = 0; 150u32 latest_signal = 0;
151static bool memory_break = false; 151bool memory_break = false;
152 152
153static Kernel::Thread* current_thread = nullptr; 153Kernel::Thread* current_thread = nullptr;
154static u32 current_core = 0; 154u32 current_core = 0;
155 155
156// Binding to a port within the reserved ports range (0-1023) requires root permissions, 156// Binding to a port within the reserved ports range (0-1023) requires root permissions,
157// so default to a port outside of that range. 157// so default to a port outside of that range.
158static u16 gdbstub_port = 24689; 158u16 gdbstub_port = 24689;
159 159
160static bool halt_loop = true; 160bool halt_loop = true;
161static bool step_loop = false; 161bool step_loop = false;
162static bool send_trap = false; 162bool send_trap = false;
163 163
164// If set to false, the server will never be started and no 164// If set to false, the server will never be started and no
165// gdbstub-related functions will be executed. 165// gdbstub-related functions will be executed.
166static std::atomic<bool> server_enabled(false); 166std::atomic<bool> server_enabled(false);
167 167
168#ifdef _WIN32 168#ifdef _WIN32
169WSADATA InitData; 169WSADATA InitData;
@@ -171,23 +171,26 @@ WSADATA InitData;
171 171
172struct Breakpoint { 172struct Breakpoint {
173 bool active; 173 bool active;
174 PAddr addr; 174 VAddr addr;
175 u64 len; 175 u64 len;
176 std::array<u8, 4> inst;
176}; 177};
177 178
178static std::map<u64, Breakpoint> breakpoints_execute; 179using BreakpointMap = std::map<VAddr, Breakpoint>;
179static std::map<u64, Breakpoint> breakpoints_read; 180BreakpointMap breakpoints_execute;
180static std::map<u64, Breakpoint> breakpoints_write; 181BreakpointMap breakpoints_read;
182BreakpointMap breakpoints_write;
181 183
182struct Module { 184struct Module {
183 std::string name; 185 std::string name;
184 PAddr beg; 186 VAddr beg;
185 PAddr end; 187 VAddr end;
186}; 188};
187 189
188static std::vector<Module> modules; 190std::vector<Module> modules;
191} // Anonymous namespace
189 192
190void RegisterModule(std::string name, PAddr beg, PAddr end, bool add_elf_ext) { 193void RegisterModule(std::string name, VAddr beg, VAddr end, bool add_elf_ext) {
191 Module module; 194 Module module;
192 if (add_elf_ext) { 195 if (add_elf_ext) {
193 Common::SplitPath(name, nullptr, &module.name, nullptr); 196 Common::SplitPath(name, nullptr, &module.name, nullptr);
@@ -418,11 +421,11 @@ static u8 CalculateChecksum(const u8* buffer, size_t length) {
418} 421}
419 422
420/** 423/**
421 * Get the list of breakpoints for a given breakpoint type. 424 * Get the map of breakpoints for a given breakpoint type.
422 * 425 *
423 * @param type Type of breakpoint list. 426 * @param type Type of breakpoint map.
424 */ 427 */
425static std::map<u64, Breakpoint>& GetBreakpointList(BreakpointType type) { 428static BreakpointMap& GetBreakpointMap(BreakpointType type) {
426 switch (type) { 429 switch (type) {
427 case BreakpointType::Execute: 430 case BreakpointType::Execute:
428 return breakpoints_execute; 431 return breakpoints_execute;
@@ -441,20 +444,24 @@ static std::map<u64, Breakpoint>& GetBreakpointList(BreakpointType type) {
441 * @param type Type of breakpoint. 444 * @param type Type of breakpoint.
442 * @param addr Address of breakpoint. 445 * @param addr Address of breakpoint.
443 */ 446 */
444static void RemoveBreakpoint(BreakpointType type, PAddr addr) { 447static void RemoveBreakpoint(BreakpointType type, VAddr addr) {
445 std::map<u64, Breakpoint>& p = GetBreakpointList(type); 448 BreakpointMap& p = GetBreakpointMap(type);
446 449
447 auto bp = p.find(static_cast<u64>(addr)); 450 const auto bp = p.find(addr);
448 if (bp != p.end()) { 451 if (bp == p.end()) {
449 LOG_DEBUG(Debug_GDBStub, "gdb: removed a breakpoint: {:016X} bytes at {:016X} of type {}", 452 return;
450 bp->second.len, bp->second.addr, static_cast<int>(type));
451 p.erase(static_cast<u64>(addr));
452 } 453 }
454
455 LOG_DEBUG(Debug_GDBStub, "gdb: removed a breakpoint: {:016X} bytes at {:016X} of type {}",
456 bp->second.len, bp->second.addr, static_cast<int>(type));
457 Memory::WriteBlock(bp->second.addr, bp->second.inst.data(), bp->second.inst.size());
458 Core::System::GetInstance().InvalidateCpuInstructionCaches();
459 p.erase(addr);
453} 460}
454 461
455BreakpointAddress GetNextBreakpointFromAddress(PAddr addr, BreakpointType type) { 462BreakpointAddress GetNextBreakpointFromAddress(VAddr addr, BreakpointType type) {
456 std::map<u64, Breakpoint>& p = GetBreakpointList(type); 463 const BreakpointMap& p = GetBreakpointMap(type);
457 auto next_breakpoint = p.lower_bound(static_cast<u64>(addr)); 464 const auto next_breakpoint = p.lower_bound(addr);
458 BreakpointAddress breakpoint; 465 BreakpointAddress breakpoint;
459 466
460 if (next_breakpoint != p.end()) { 467 if (next_breakpoint != p.end()) {
@@ -468,36 +475,38 @@ BreakpointAddress GetNextBreakpointFromAddress(PAddr addr, BreakpointType type)
468 return breakpoint; 475 return breakpoint;
469} 476}
470 477
471bool CheckBreakpoint(PAddr addr, BreakpointType type) { 478bool CheckBreakpoint(VAddr addr, BreakpointType type) {
472 if (!IsConnected()) { 479 if (!IsConnected()) {
473 return false; 480 return false;
474 } 481 }
475 482
476 std::map<u64, Breakpoint>& p = GetBreakpointList(type); 483 const BreakpointMap& p = GetBreakpointMap(type);
484 const auto bp = p.find(addr);
477 485
478 auto bp = p.find(static_cast<u64>(addr)); 486 if (bp == p.end()) {
479 if (bp != p.end()) { 487 return false;
480 u64 len = bp->second.len; 488 }
481 489
482 // IDA Pro defaults to 4-byte breakpoints for all non-hardware breakpoints 490 u64 len = bp->second.len;
483 // no matter if it's a 4-byte or 2-byte instruction. When you execute a
484 // Thumb instruction with a 4-byte breakpoint set, it will set a breakpoint on
485 // two instructions instead of the single instruction you placed the breakpoint
486 // on. So, as a way to make sure that execution breakpoints are only breaking
487 // on the instruction that was specified, set the length of an execution
488 // breakpoint to 1. This should be fine since the CPU should never begin executing
489 // an instruction anywhere except the beginning of the instruction.
490 if (type == BreakpointType::Execute) {
491 len = 1;
492 }
493 491
494 if (bp->second.active && (addr >= bp->second.addr && addr < bp->second.addr + len)) { 492 // IDA Pro defaults to 4-byte breakpoints for all non-hardware breakpoints
495 LOG_DEBUG(Debug_GDBStub, 493 // no matter if it's a 4-byte or 2-byte instruction. When you execute a
496 "Found breakpoint type {} @ {:016X}, range: {:016X}" 494 // Thumb instruction with a 4-byte breakpoint set, it will set a breakpoint on
497 " - {:016X} ({:X} bytes)", 495 // two instructions instead of the single instruction you placed the breakpoint
498 static_cast<int>(type), addr, bp->second.addr, bp->second.addr + len, len); 496 // on. So, as a way to make sure that execution breakpoints are only breaking
499 return true; 497 // on the instruction that was specified, set the length of an execution
500 } 498 // breakpoint to 1. This should be fine since the CPU should never begin executing
499 // an instruction anywhere except the beginning of the instruction.
500 if (type == BreakpointType::Execute) {
501 len = 1;
502 }
503
504 if (bp->second.active && (addr >= bp->second.addr && addr < bp->second.addr + len)) {
505 LOG_DEBUG(Debug_GDBStub,
506 "Found breakpoint type {} @ {:016X}, range: {:016X}"
507 " - {:016X} ({:X} bytes)",
508 static_cast<int>(type), addr, bp->second.addr, bp->second.addr + len, len);
509 return true;
501 } 510 }
502 511
503 return false; 512 return false;
@@ -931,6 +940,7 @@ static void WriteMemory() {
931 940
932 GdbHexToMem(data.data(), len_pos + 1, len); 941 GdbHexToMem(data.data(), len_pos + 1, len);
933 Memory::WriteBlock(addr, data.data(), len); 942 Memory::WriteBlock(addr, data.data(), len);
943 Core::System::GetInstance().InvalidateCpuInstructionCaches();
934 SendReply("OK"); 944 SendReply("OK");
935} 945}
936 946
@@ -950,6 +960,7 @@ static void Step() {
950 step_loop = true; 960 step_loop = true;
951 halt_loop = true; 961 halt_loop = true;
952 send_trap = true; 962 send_trap = true;
963 Core::System::GetInstance().InvalidateCpuInstructionCaches();
953} 964}
954 965
955/// Tell the CPU if we hit a memory breakpoint. 966/// Tell the CPU if we hit a memory breakpoint.
@@ -966,6 +977,7 @@ static void Continue() {
966 memory_break = false; 977 memory_break = false;
967 step_loop = false; 978 step_loop = false;
968 halt_loop = false; 979 halt_loop = false;
980 Core::System::GetInstance().InvalidateCpuInstructionCaches();
969} 981}
970 982
971/** 983/**
@@ -975,13 +987,17 @@ static void Continue() {
975 * @param addr Address of breakpoint. 987 * @param addr Address of breakpoint.
976 * @param len Length of breakpoint. 988 * @param len Length of breakpoint.
977 */ 989 */
978static bool CommitBreakpoint(BreakpointType type, PAddr addr, u64 len) { 990static bool CommitBreakpoint(BreakpointType type, VAddr addr, u64 len) {
979 std::map<u64, Breakpoint>& p = GetBreakpointList(type); 991 BreakpointMap& p = GetBreakpointMap(type);
980 992
981 Breakpoint breakpoint; 993 Breakpoint breakpoint;
982 breakpoint.active = true; 994 breakpoint.active = true;
983 breakpoint.addr = addr; 995 breakpoint.addr = addr;
984 breakpoint.len = len; 996 breakpoint.len = len;
997 Memory::ReadBlock(addr, breakpoint.inst.data(), breakpoint.inst.size());
998 static constexpr std::array<u8, 4> btrap{{0xd4, 0x20, 0x7d, 0x0}};
999 Memory::WriteBlock(addr, btrap.data(), btrap.size());
1000 Core::System::GetInstance().InvalidateCpuInstructionCaches();
985 p.insert({addr, breakpoint}); 1001 p.insert({addr, breakpoint});
986 1002
987 LOG_DEBUG(Debug_GDBStub, "gdb: added {} breakpoint: {:016X} bytes at {:016X}", 1003 LOG_DEBUG(Debug_GDBStub, "gdb: added {} breakpoint: {:016X} bytes at {:016X}",
@@ -1015,7 +1031,7 @@ static void AddBreakpoint() {
1015 1031
1016 auto start_offset = command_buffer + 3; 1032 auto start_offset = command_buffer + 3;
1017 auto addr_pos = std::find(start_offset, command_buffer + command_length, ','); 1033 auto addr_pos = std::find(start_offset, command_buffer + command_length, ',');
1018 PAddr addr = HexToLong(start_offset, static_cast<u64>(addr_pos - start_offset)); 1034 VAddr addr = HexToLong(start_offset, static_cast<u64>(addr_pos - start_offset));
1019 1035
1020 start_offset = addr_pos + 1; 1036 start_offset = addr_pos + 1;
1021 u64 len = 1037 u64 len =
@@ -1064,7 +1080,7 @@ static void RemoveBreakpoint() {
1064 1080
1065 auto start_offset = command_buffer + 3; 1081 auto start_offset = command_buffer + 3;
1066 auto addr_pos = std::find(start_offset, command_buffer + command_length, ','); 1082 auto addr_pos = std::find(start_offset, command_buffer + command_length, ',');
1067 PAddr addr = HexToLong(start_offset, static_cast<u64>(addr_pos - start_offset)); 1083 VAddr addr = HexToLong(start_offset, static_cast<u64>(addr_pos - start_offset));
1068 1084
1069 if (type == BreakpointType::Access) { 1085 if (type == BreakpointType::Access) {
1070 // Access is made up of Read and Write types, so add both breakpoints 1086 // Access is made up of Read and Write types, so add both breakpoints
diff --git a/src/core/gdbstub/gdbstub.h b/src/core/gdbstub/gdbstub.h
index a6b50c26c..5a36524b2 100644
--- a/src/core/gdbstub/gdbstub.h
+++ b/src/core/gdbstub/gdbstub.h
@@ -22,7 +22,7 @@ enum class BreakpointType {
22}; 22};
23 23
24struct BreakpointAddress { 24struct BreakpointAddress {
25 PAddr address; 25 VAddr address;
26 BreakpointType type; 26 BreakpointType type;
27}; 27};
28 28
@@ -53,7 +53,7 @@ bool IsServerEnabled();
53bool IsConnected(); 53bool IsConnected();
54 54
55/// Register module. 55/// Register module.
56void RegisterModule(std::string name, PAddr beg, PAddr end, bool add_elf_ext = true); 56void RegisterModule(std::string name, VAddr beg, VAddr end, bool add_elf_ext = true);
57 57
58/** 58/**
59 * Signal to the gdbstub server that it should halt CPU execution. 59 * Signal to the gdbstub server that it should halt CPU execution.
@@ -74,7 +74,7 @@ void HandlePacket();
74 * @param addr Address to search from. 74 * @param addr Address to search from.
75 * @param type Type of breakpoint. 75 * @param type Type of breakpoint.
76 */ 76 */
77BreakpointAddress GetNextBreakpointFromAddress(PAddr addr, GDBStub::BreakpointType type); 77BreakpointAddress GetNextBreakpointFromAddress(VAddr addr, GDBStub::BreakpointType type);
78 78
79/** 79/**
80 * Check if a breakpoint of the specified type exists at the given address. 80 * Check if a breakpoint of the specified type exists at the given address.
@@ -82,7 +82,7 @@ BreakpointAddress GetNextBreakpointFromAddress(PAddr addr, GDBStub::BreakpointTy
82 * @param addr Address of breakpoint. 82 * @param addr Address of breakpoint.
83 * @param type Type of breakpoint. 83 * @param type Type of breakpoint.
84 */ 84 */
85bool CheckBreakpoint(PAddr addr, GDBStub::BreakpointType type); 85bool CheckBreakpoint(VAddr addr, GDBStub::BreakpointType type);
86 86
87/// If set to true, the CPU will halt at the beginning of the next CPU loop. 87/// If set to true, the CPU will halt at the beginning of the next CPU loop.
88bool GetCpuHaltFlag(); 88bool GetCpuHaltFlag();
diff --git a/src/core/hle/kernel/address_arbiter.cpp b/src/core/hle/kernel/address_arbiter.cpp
index 7a17ed162..03a954a9f 100644
--- a/src/core/hle/kernel/address_arbiter.cpp
+++ b/src/core/hle/kernel/address_arbiter.cpp
@@ -32,9 +32,8 @@ static ResultCode WaitForAddress(VAddr address, s64 timeout) {
32} 32}
33 33
34// Gets the threads waiting on an address. 34// Gets the threads waiting on an address.
35static void GetThreadsWaitingOnAddress(std::vector<SharedPtr<Thread>>& waiting_threads, 35static std::vector<SharedPtr<Thread>> GetThreadsWaitingOnAddress(VAddr address) {
36 VAddr address) { 36 const auto RetrieveWaitingThreads =
37 auto RetrieveWaitingThreads =
38 [](size_t core_index, std::vector<SharedPtr<Thread>>& waiting_threads, VAddr arb_addr) { 37 [](size_t core_index, std::vector<SharedPtr<Thread>>& waiting_threads, VAddr arb_addr) {
39 const auto& scheduler = Core::System::GetInstance().Scheduler(core_index); 38 const auto& scheduler = Core::System::GetInstance().Scheduler(core_index);
40 auto& thread_list = scheduler->GetThreadList(); 39 auto& thread_list = scheduler->GetThreadList();
@@ -45,16 +44,20 @@ static void GetThreadsWaitingOnAddress(std::vector<SharedPtr<Thread>>& waiting_t
45 } 44 }
46 }; 45 };
47 46
48 // Retrieve a list of all threads that are waiting for this address. 47 // Retrieve all threads that are waiting for this address.
49 RetrieveWaitingThreads(0, waiting_threads, address); 48 std::vector<SharedPtr<Thread>> threads;
50 RetrieveWaitingThreads(1, waiting_threads, address); 49 RetrieveWaitingThreads(0, threads, address);
51 RetrieveWaitingThreads(2, waiting_threads, address); 50 RetrieveWaitingThreads(1, threads, address);
52 RetrieveWaitingThreads(3, waiting_threads, address); 51 RetrieveWaitingThreads(2, threads, address);
52 RetrieveWaitingThreads(3, threads, address);
53
53 // Sort them by priority, such that the highest priority ones come first. 54 // Sort them by priority, such that the highest priority ones come first.
54 std::sort(waiting_threads.begin(), waiting_threads.end(), 55 std::sort(threads.begin(), threads.end(),
55 [](const SharedPtr<Thread>& lhs, const SharedPtr<Thread>& rhs) { 56 [](const SharedPtr<Thread>& lhs, const SharedPtr<Thread>& rhs) {
56 return lhs->current_priority < rhs->current_priority; 57 return lhs->current_priority < rhs->current_priority;
57 }); 58 });
59
60 return threads;
58} 61}
59 62
60// Wake up num_to_wake (or all) threads in a vector. 63// Wake up num_to_wake (or all) threads in a vector.
@@ -76,9 +79,7 @@ static void WakeThreads(std::vector<SharedPtr<Thread>>& waiting_threads, s32 num
76 79
77// Signals an address being waited on. 80// Signals an address being waited on.
78ResultCode SignalToAddress(VAddr address, s32 num_to_wake) { 81ResultCode SignalToAddress(VAddr address, s32 num_to_wake) {
79 // Get threads waiting on the address. 82 std::vector<SharedPtr<Thread>> waiting_threads = GetThreadsWaitingOnAddress(address);
80 std::vector<SharedPtr<Thread>> waiting_threads;
81 GetThreadsWaitingOnAddress(waiting_threads, address);
82 83
83 WakeThreads(waiting_threads, num_to_wake); 84 WakeThreads(waiting_threads, num_to_wake);
84 return RESULT_SUCCESS; 85 return RESULT_SUCCESS;
@@ -110,12 +111,11 @@ ResultCode ModifyByWaitingCountAndSignalToAddressIfEqual(VAddr address, s32 valu
110 } 111 }
111 112
112 // Get threads waiting on the address. 113 // Get threads waiting on the address.
113 std::vector<SharedPtr<Thread>> waiting_threads; 114 std::vector<SharedPtr<Thread>> waiting_threads = GetThreadsWaitingOnAddress(address);
114 GetThreadsWaitingOnAddress(waiting_threads, address);
115 115
116 // Determine the modified value depending on the waiting count. 116 // Determine the modified value depending on the waiting count.
117 s32 updated_value; 117 s32 updated_value;
118 if (waiting_threads.size() == 0) { 118 if (waiting_threads.empty()) {
119 updated_value = value - 1; 119 updated_value = value - 1;
120 } else if (num_to_wake <= 0 || waiting_threads.size() <= static_cast<u32>(num_to_wake)) { 120 } else if (num_to_wake <= 0 || waiting_threads.size() <= static_cast<u32>(num_to_wake)) {
121 updated_value = value + 1; 121 updated_value = value + 1;
diff --git a/src/core/hle/kernel/event.h b/src/core/hle/kernel/event.h
index 1c99911b2..3c20c05e8 100644
--- a/src/core/hle/kernel/event.h
+++ b/src/core/hle/kernel/event.h
@@ -31,10 +31,9 @@ public:
31 return HANDLE_TYPE; 31 return HANDLE_TYPE;
32 } 32 }
33 33
34 ResetType reset_type; ///< Current ResetType 34 ResetType GetResetType() const {
35 35 return reset_type;
36 bool signaled; ///< Whether the event has already been signaled 36 }
37 std::string name; ///< Name of event (optional)
38 37
39 bool ShouldWait(Thread* thread) const override; 38 bool ShouldWait(Thread* thread) const override;
40 void Acquire(Thread* thread) override; 39 void Acquire(Thread* thread) override;
@@ -47,6 +46,11 @@ public:
47private: 46private:
48 Event(); 47 Event();
49 ~Event() override; 48 ~Event() override;
49
50 ResetType reset_type; ///< Current ResetType
51
52 bool signaled; ///< Whether the event has already been signaled
53 std::string name; ///< Name of event (optional)
50}; 54};
51 55
52} // namespace Kernel 56} // namespace Kernel
diff --git a/src/core/hle/service/audio/audout_a.cpp b/src/core/hle/service/audio/audout_a.cpp
index 57b934dd6..bf8d40157 100644
--- a/src/core/hle/service/audio/audout_a.cpp
+++ b/src/core/hle/service/audio/audout_a.cpp
@@ -2,8 +2,6 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#pragma once
6
7#include "core/hle/service/audio/audout_a.h" 5#include "core/hle/service/audio/audout_a.h"
8 6
9namespace Service::Audio { 7namespace Service::Audio {
diff --git a/src/core/hle/service/audio/audout_u.cpp b/src/core/hle/service/audio/audout_u.cpp
index b317027b6..108a7c6eb 100644
--- a/src/core/hle/service/audio/audout_u.cpp
+++ b/src/core/hle/service/audio/audout_u.cpp
@@ -4,6 +4,8 @@
4 4
5#include <array> 5#include <array>
6#include <vector> 6#include <vector>
7
8#include "audio_core/codec.h"
7#include "common/logging/log.h" 9#include "common/logging/log.h"
8#include "core/core.h" 10#include "core/core.h"
9#include "core/hle/ipc_helpers.h" 11#include "core/hle/ipc_helpers.h"
@@ -48,7 +50,7 @@ public:
48 buffer_event = Kernel::Event::Create(Kernel::ResetType::Sticky, "IAudioOutBufferReleased"); 50 buffer_event = Kernel::Event::Create(Kernel::ResetType::Sticky, "IAudioOutBufferReleased");
49 51
50 stream = audio_core.OpenStream(audio_params.sample_rate, audio_params.channel_count, 52 stream = audio_core.OpenStream(audio_params.sample_rate, audio_params.channel_count,
51 [=]() { buffer_event->Signal(); }); 53 "IAudioOut", [=]() { buffer_event->Signal(); });
52 } 54 }
53 55
54private: 56private:
@@ -111,10 +113,10 @@ private:
111 std::memcpy(&audio_buffer, input_buffer.data(), sizeof(AudioBuffer)); 113 std::memcpy(&audio_buffer, input_buffer.data(), sizeof(AudioBuffer));
112 const u64 tag{rp.Pop<u64>()}; 114 const u64 tag{rp.Pop<u64>()};
113 115
114 std::vector<u8> data(audio_buffer.buffer_size); 116 std::vector<s16> samples(audio_buffer.buffer_size / sizeof(s16));
115 Memory::ReadBlock(audio_buffer.buffer, data.data(), data.size()); 117 Memory::ReadBlock(audio_buffer.buffer, samples.data(), audio_buffer.buffer_size);
116 118
117 if (!audio_core.QueueBuffer(stream, tag, std::move(data))) { 119 if (!audio_core.QueueBuffer(stream, tag, std::move(samples))) {
118 IPC::ResponseBuilder rb{ctx, 2}; 120 IPC::ResponseBuilder rb{ctx, 2};
119 rb.Push(ResultCode(ErrorModule::Audio, ErrCodes::BufferCountExceeded)); 121 rb.Push(ResultCode(ErrorModule::Audio, ErrCodes::BufferCountExceeded));
120 } 122 }
@@ -200,7 +202,7 @@ void AudOutU::OpenAudioOutImpl(Kernel::HLERequestContext& ctx) {
200 rb.Push(RESULT_SUCCESS); 202 rb.Push(RESULT_SUCCESS);
201 rb.Push<u32>(DefaultSampleRate); 203 rb.Push<u32>(DefaultSampleRate);
202 rb.Push<u32>(params.channel_count); 204 rb.Push<u32>(params.channel_count);
203 rb.Push<u32>(static_cast<u32>(PcmFormat::Int16)); 205 rb.Push<u32>(static_cast<u32>(AudioCore::Codec::PcmFormat::Int16));
204 rb.Push<u32>(static_cast<u32>(AudioState::Stopped)); 206 rb.Push<u32>(static_cast<u32>(AudioState::Stopped));
205 rb.PushIpcInterface<Audio::IAudioOut>(audio_out_interface); 207 rb.PushIpcInterface<Audio::IAudioOut>(audio_out_interface);
206} 208}
diff --git a/src/core/hle/service/audio/audout_u.h b/src/core/hle/service/audio/audout_u.h
index e5c2184d5..fd491f65d 100644
--- a/src/core/hle/service/audio/audout_u.h
+++ b/src/core/hle/service/audio/audout_u.h
@@ -38,16 +38,6 @@ private:
38 38
39 void ListAudioOutsImpl(Kernel::HLERequestContext& ctx); 39 void ListAudioOutsImpl(Kernel::HLERequestContext& ctx);
40 void OpenAudioOutImpl(Kernel::HLERequestContext& ctx); 40 void OpenAudioOutImpl(Kernel::HLERequestContext& ctx);
41
42 enum class PcmFormat : u32 {
43 Invalid = 0,
44 Int8 = 1,
45 Int16 = 2,
46 Int24 = 3,
47 Int32 = 4,
48 PcmFloat = 5,
49 Adpcm = 6,
50 };
51}; 41};
52 42
53} // namespace Service::Audio 43} // namespace Service::Audio
diff --git a/src/core/hle/service/audio/audren_u.cpp b/src/core/hle/service/audio/audren_u.cpp
index 6aed9e2fa..f99304de5 100644
--- a/src/core/hle/service/audio/audren_u.cpp
+++ b/src/core/hle/service/audio/audren_u.cpp
@@ -15,13 +15,10 @@
15 15
16namespace Service::Audio { 16namespace Service::Audio {
17 17
18/// TODO(bunnei): Find a proper value for the audio_ticks
19constexpr u64 audio_ticks{static_cast<u64>(CoreTiming::BASE_CLOCK_RATE / 200)};
20
21class IAudioRenderer final : public ServiceFramework<IAudioRenderer> { 18class IAudioRenderer final : public ServiceFramework<IAudioRenderer> {
22public: 19public:
23 explicit IAudioRenderer(AudioRendererParameter audren_params) 20 explicit IAudioRenderer(AudioCore::AudioRendererParameter audren_params)
24 : ServiceFramework("IAudioRenderer"), worker_params(audren_params) { 21 : ServiceFramework("IAudioRenderer") {
25 static const FunctionInfo functions[] = { 22 static const FunctionInfo functions[] = {
26 {0, nullptr, "GetAudioRendererSampleRate"}, 23 {0, nullptr, "GetAudioRendererSampleRate"},
27 {1, nullptr, "GetAudioRendererSampleCount"}, 24 {1, nullptr, "GetAudioRendererSampleCount"},
@@ -39,21 +36,8 @@ public:
39 RegisterHandlers(functions); 36 RegisterHandlers(functions);
40 37
41 system_event = 38 system_event =
42 Kernel::Event::Create(Kernel::ResetType::OneShot, "IAudioRenderer:SystemEvent"); 39 Kernel::Event::Create(Kernel::ResetType::Sticky, "IAudioRenderer:SystemEvent");
43 40 renderer = std::make_unique<AudioCore::AudioRenderer>(audren_params, system_event);
44 // Register event callback to update the Audio Buffer
45 audio_event = CoreTiming::RegisterEvent(
46 "IAudioRenderer::UpdateAudioCallback", [this](u64 userdata, int cycles_late) {
47 UpdateAudioCallback();
48 CoreTiming::ScheduleEvent(audio_ticks - cycles_late, audio_event);
49 });
50
51 // Start the audio event
52 CoreTiming::ScheduleEvent(audio_ticks, audio_event);
53 voice_status_list.resize(worker_params.voice_count);
54 }
55 ~IAudioRenderer() {
56 CoreTiming::UnscheduleEvent(audio_event, 0);
57 } 41 }
58 42
59private: 43private:
@@ -62,60 +46,9 @@ private:
62 } 46 }
63 47
64 void RequestUpdateAudioRenderer(Kernel::HLERequestContext& ctx) { 48 void RequestUpdateAudioRenderer(Kernel::HLERequestContext& ctx) {
65 UpdateDataHeader config{}; 49 ctx.WriteBuffer(renderer->UpdateAudioRenderer(ctx.ReadBuffer()));
66 auto buf = ctx.ReadBuffer();
67 std::memcpy(&config, buf.data(), sizeof(UpdateDataHeader));
68 u32 memory_pool_count = worker_params.effect_count + (worker_params.voice_count * 4);
69
70 std::vector<MemoryPoolInfo> mem_pool_info(memory_pool_count);
71 std::memcpy(mem_pool_info.data(),
72 buf.data() + sizeof(UpdateDataHeader) + config.behavior_size,
73 memory_pool_count * sizeof(MemoryPoolInfo));
74
75 std::vector<VoiceInfo> voice_info(worker_params.voice_count);
76 std::memcpy(voice_info.data(),
77 buf.data() + sizeof(UpdateDataHeader) + config.behavior_size +
78 config.memory_pools_size + config.voice_resource_size,
79 worker_params.voice_count * sizeof(VoiceInfo));
80
81 UpdateDataHeader response_data{worker_params};
82
83 ASSERT(ctx.GetWriteBufferSize() == response_data.total_size);
84
85 std::vector<u8> output(response_data.total_size);
86 std::memcpy(output.data(), &response_data, sizeof(UpdateDataHeader));
87 std::vector<MemoryPoolEntry> memory_pool(memory_pool_count);
88 for (unsigned i = 0; i < memory_pool.size(); i++) {
89 if (mem_pool_info[i].pool_state == MemoryPoolStates::RequestAttach)
90 memory_pool[i].state = MemoryPoolStates::Attached;
91 else if (mem_pool_info[i].pool_state == MemoryPoolStates::RequestDetach)
92 memory_pool[i].state = MemoryPoolStates::Detached;
93 }
94 std::memcpy(output.data() + sizeof(UpdateDataHeader), memory_pool.data(),
95 response_data.memory_pools_size);
96
97 for (unsigned i = 0; i < voice_info.size(); i++) {
98 if (voice_info[i].is_new) {
99 voice_status_list[i].played_sample_count = 0;
100 voice_status_list[i].wave_buffer_consumed = 0;
101 } else if (voice_info[i].play_state == (u8)PlayStates::Started) {
102 for (u32 buff_idx = 0; buff_idx < voice_info[i].wave_buffer_count; buff_idx++) {
103 voice_status_list[i].played_sample_count +=
104 (voice_info[i].wave_buffer[buff_idx].end_sample_offset -
105 voice_info[i].wave_buffer[buff_idx].start_sample_offset) /
106 2;
107 voice_status_list[i].wave_buffer_consumed++;
108 }
109 }
110 }
111 std::memcpy(output.data() + sizeof(UpdateDataHeader) + response_data.memory_pools_size,
112 voice_status_list.data(), response_data.voices_size);
113
114 ctx.WriteBuffer(output);
115
116 IPC::ResponseBuilder rb{ctx, 2}; 50 IPC::ResponseBuilder rb{ctx, 2};
117 rb.Push(RESULT_SUCCESS); 51 rb.Push(RESULT_SUCCESS);
118
119 LOG_WARNING(Service_Audio, "(STUBBED) called"); 52 LOG_WARNING(Service_Audio, "(STUBBED) called");
120 } 53 }
121 54
@@ -136,8 +69,6 @@ private:
136 } 69 }
137 70
138 void QuerySystemEvent(Kernel::HLERequestContext& ctx) { 71 void QuerySystemEvent(Kernel::HLERequestContext& ctx) {
139 // system_event->Signal();
140
141 IPC::ResponseBuilder rb{ctx, 2, 1}; 72 IPC::ResponseBuilder rb{ctx, 2, 1};
142 rb.Push(RESULT_SUCCESS); 73 rb.Push(RESULT_SUCCESS);
143 rb.PushCopyObjects(system_event); 74 rb.PushCopyObjects(system_event);
@@ -145,131 +76,8 @@ private:
145 LOG_WARNING(Service_Audio, "(STUBBED) called"); 76 LOG_WARNING(Service_Audio, "(STUBBED) called");
146 } 77 }
147 78
148 enum class MemoryPoolStates : u32 { // Should be LE
149 Invalid = 0x0,
150 Unknown = 0x1,
151 RequestDetach = 0x2,
152 Detached = 0x3,
153 RequestAttach = 0x4,
154 Attached = 0x5,
155 Released = 0x6,
156 };
157
158 enum class PlayStates : u8 {
159 Started = 0,
160 Stopped = 1,
161 };
162
163 struct MemoryPoolEntry {
164 MemoryPoolStates state;
165 u32_le unknown_4;
166 u32_le unknown_8;
167 u32_le unknown_c;
168 };
169 static_assert(sizeof(MemoryPoolEntry) == 0x10, "MemoryPoolEntry has wrong size");
170
171 struct MemoryPoolInfo {
172 u64_le pool_address;
173 u64_le pool_size;
174 MemoryPoolStates pool_state;
175 INSERT_PADDING_WORDS(3); // Unknown
176 };
177 static_assert(sizeof(MemoryPoolInfo) == 0x20, "MemoryPoolInfo has wrong size");
178
179 struct UpdateDataHeader {
180 UpdateDataHeader() {}
181
182 explicit UpdateDataHeader(const AudioRendererParameter& config) {
183 revision = Common::MakeMagic('R', 'E', 'V', '4'); // 5.1.0 Revision
184 behavior_size = 0xb0;
185 memory_pools_size = (config.effect_count + (config.voice_count * 4)) * 0x10;
186 voices_size = config.voice_count * 0x10;
187 voice_resource_size = 0x0;
188 effects_size = config.effect_count * 0x10;
189 mixes_size = 0x0;
190 sinks_size = config.sink_count * 0x20;
191 performance_manager_size = 0x10;
192 total_size = sizeof(UpdateDataHeader) + behavior_size + memory_pools_size +
193 voices_size + effects_size + sinks_size + performance_manager_size;
194 }
195
196 u32_le revision;
197 u32_le behavior_size;
198 u32_le memory_pools_size;
199 u32_le voices_size;
200 u32_le voice_resource_size;
201 u32_le effects_size;
202 u32_le mixes_size;
203 u32_le sinks_size;
204 u32_le performance_manager_size;
205 INSERT_PADDING_WORDS(6);
206 u32_le total_size;
207 };
208 static_assert(sizeof(UpdateDataHeader) == 0x40, "UpdateDataHeader has wrong size");
209
210 struct BiquadFilter {
211 u8 enable;
212 INSERT_PADDING_BYTES(1);
213 s16_le numerator[3];
214 s16_le denominator[2];
215 };
216 static_assert(sizeof(BiquadFilter) == 0xc, "BiquadFilter has wrong size");
217
218 struct WaveBuffer {
219 u64_le buffer_addr;
220 u64_le buffer_sz;
221 s32_le start_sample_offset;
222 s32_le end_sample_offset;
223 u8 loop;
224 u8 end_of_stream;
225 u8 sent_to_server;
226 INSERT_PADDING_BYTES(5);
227 u64 context_addr;
228 u64 context_sz;
229 INSERT_PADDING_BYTES(8);
230 };
231 static_assert(sizeof(WaveBuffer) == 0x38, "WaveBuffer has wrong size");
232
233 struct VoiceInfo {
234 u32_le id;
235 u32_le node_id;
236 u8 is_new;
237 u8 is_in_use;
238 u8 play_state;
239 u8 sample_format;
240 u32_le sample_rate;
241 u32_le priority;
242 u32_le sorting_order;
243 u32_le channel_count;
244 float_le pitch;
245 float_le volume;
246 BiquadFilter biquad_filter[2];
247 u32_le wave_buffer_count;
248 u16_le wave_buffer_head;
249 INSERT_PADDING_BYTES(6);
250 u64_le additional_params_addr;
251 u64_le additional_params_sz;
252 u32_le mix_id;
253 u32_le splitter_info_id;
254 WaveBuffer wave_buffer[4];
255 u32_le voice_channel_resource_ids[6];
256 INSERT_PADDING_BYTES(24);
257 };
258 static_assert(sizeof(VoiceInfo) == 0x170, "VoiceInfo is wrong size");
259
260 struct VoiceOutStatus {
261 u64_le played_sample_count;
262 u32_le wave_buffer_consumed;
263 INSERT_PADDING_WORDS(1);
264 };
265 static_assert(sizeof(VoiceOutStatus) == 0x10, "VoiceOutStatus has wrong size");
266
267 /// This is used to trigger the audio event callback.
268 CoreTiming::EventType* audio_event;
269
270 Kernel::SharedPtr<Kernel::Event> system_event; 79 Kernel::SharedPtr<Kernel::Event> system_event;
271 AudioRendererParameter worker_params; 80 std::unique_ptr<AudioCore::AudioRenderer> renderer;
272 std::vector<VoiceOutStatus> voice_status_list;
273}; 81};
274 82
275class IAudioDevice final : public ServiceFramework<IAudioDevice> { 83class IAudioDevice final : public ServiceFramework<IAudioDevice> {
@@ -368,7 +176,7 @@ AudRenU::AudRenU() : ServiceFramework("audren:u") {
368 176
369void AudRenU::OpenAudioRenderer(Kernel::HLERequestContext& ctx) { 177void AudRenU::OpenAudioRenderer(Kernel::HLERequestContext& ctx) {
370 IPC::RequestParser rp{ctx}; 178 IPC::RequestParser rp{ctx};
371 auto params = rp.PopRaw<AudioRendererParameter>(); 179 auto params = rp.PopRaw<AudioCore::AudioRendererParameter>();
372 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 180 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
373 181
374 rb.Push(RESULT_SUCCESS); 182 rb.Push(RESULT_SUCCESS);
@@ -379,7 +187,7 @@ void AudRenU::OpenAudioRenderer(Kernel::HLERequestContext& ctx) {
379 187
380void AudRenU::GetAudioRendererWorkBufferSize(Kernel::HLERequestContext& ctx) { 188void AudRenU::GetAudioRendererWorkBufferSize(Kernel::HLERequestContext& ctx) {
381 IPC::RequestParser rp{ctx}; 189 IPC::RequestParser rp{ctx};
382 auto params = rp.PopRaw<AudioRendererParameter>(); 190 auto params = rp.PopRaw<AudioCore::AudioRendererParameter>();
383 191
384 u64 buffer_sz = Common::AlignUp(4 * params.unknown_8, 0x40); 192 u64 buffer_sz = Common::AlignUp(4 * params.unknown_8, 0x40);
385 buffer_sz += params.unknown_c * 1024; 193 buffer_sz += params.unknown_c * 1024;
diff --git a/src/core/hle/service/audio/audren_u.h b/src/core/hle/service/audio/audren_u.h
index b9b81db4f..14907f8ae 100644
--- a/src/core/hle/service/audio/audren_u.h
+++ b/src/core/hle/service/audio/audren_u.h
@@ -4,6 +4,7 @@
4 4
5#pragma once 5#pragma once
6 6
7#include "audio_core/audio_renderer.h"
7#include "core/hle/service/service.h" 8#include "core/hle/service/service.h"
8 9
9namespace Kernel { 10namespace Kernel {
@@ -12,24 +13,6 @@ class HLERequestContext;
12 13
13namespace Service::Audio { 14namespace Service::Audio {
14 15
15struct AudioRendererParameter {
16 u32_le sample_rate;
17 u32_le sample_count;
18 u32_le unknown_8;
19 u32_le unknown_c;
20 u32_le voice_count;
21 u32_le sink_count;
22 u32_le effect_count;
23 u32_le unknown_1c;
24 u8 unknown_20;
25 INSERT_PADDING_BYTES(3);
26 u32_le splitter_count;
27 u32_le unknown_2c;
28 INSERT_PADDING_WORDS(1);
29 u32_le revision;
30};
31static_assert(sizeof(AudioRendererParameter) == 52, "AudioRendererParameter is an invalid size");
32
33class AudRenU final : public ServiceFramework<AudRenU> { 16class AudRenU final : public ServiceFramework<AudRenU> {
34public: 17public:
35 explicit AudRenU(); 18 explicit AudRenU();
diff --git a/src/core/memory.h b/src/core/memory.h
index b5d885b8a..b7fb3b9ed 100644
--- a/src/core/memory.h
+++ b/src/core/memory.h
@@ -140,10 +140,10 @@ void SetCurrentPageTable(PageTable* page_table);
140PageTable* GetCurrentPageTable(); 140PageTable* GetCurrentPageTable();
141 141
142/// Determines if the given VAddr is valid for the specified process. 142/// Determines if the given VAddr is valid for the specified process.
143bool IsValidVirtualAddress(const Kernel::Process& process, const VAddr vaddr); 143bool IsValidVirtualAddress(const Kernel::Process& process, VAddr vaddr);
144bool IsValidVirtualAddress(const VAddr addr); 144bool IsValidVirtualAddress(VAddr vaddr);
145/// Determines if the given VAddr is a kernel address 145/// Determines if the given VAddr is a kernel address
146bool IsKernelVirtualAddress(const VAddr addr); 146bool IsKernelVirtualAddress(VAddr vaddr);
147 147
148u8 Read8(VAddr addr); 148u8 Read8(VAddr addr);
149u16 Read16(VAddr addr); 149u16 Read16(VAddr addr);
@@ -155,18 +155,17 @@ void Write16(VAddr addr, u16 data);
155void Write32(VAddr addr, u32 data); 155void Write32(VAddr addr, u32 data);
156void Write64(VAddr addr, u64 data); 156void Write64(VAddr addr, u64 data);
157 157
158void ReadBlock(const Kernel::Process& process, const VAddr src_addr, void* dest_buffer, 158void ReadBlock(const Kernel::Process& process, VAddr src_addr, void* dest_buffer, size_t size);
159 size_t size); 159void ReadBlock(VAddr src_addr, void* dest_buffer, size_t size);
160void ReadBlock(const VAddr src_addr, void* dest_buffer, size_t size); 160void WriteBlock(const Kernel::Process& process, VAddr dest_addr, const void* src_buffer,
161void WriteBlock(const Kernel::Process& process, const VAddr dest_addr, const void* src_buffer,
162 size_t size); 161 size_t size);
163void WriteBlock(const VAddr dest_addr, const void* src_buffer, size_t size); 162void WriteBlock(VAddr dest_addr, const void* src_buffer, size_t size);
164void ZeroBlock(const VAddr dest_addr, const size_t size); 163void ZeroBlock(const Kernel::Process& process, VAddr dest_addr, size_t size);
165void CopyBlock(VAddr dest_addr, VAddr src_addr, size_t size); 164void CopyBlock(VAddr dest_addr, VAddr src_addr, size_t size);
166 165
167u8* GetPointer(VAddr virtual_address); 166u8* GetPointer(VAddr vaddr);
168 167
169std::string ReadCString(VAddr virtual_address, std::size_t max_length); 168std::string ReadCString(VAddr vaddr, std::size_t max_length);
170 169
171enum class FlushMode { 170enum class FlushMode {
172 /// Write back modified surfaces to RAM 171 /// Write back modified surfaces to RAM
@@ -180,7 +179,7 @@ enum class FlushMode {
180/** 179/**
181 * Mark each page touching the region as cached. 180 * Mark each page touching the region as cached.
182 */ 181 */
183void RasterizerMarkRegionCached(Tegra::GPUVAddr start, u64 size, bool cached); 182void RasterizerMarkRegionCached(Tegra::GPUVAddr gpu_addr, u64 size, bool cached);
184 183
185/** 184/**
186 * Flushes and invalidates any externally cached rasterizer resources touching the given virtual 185 * Flushes and invalidates any externally cached rasterizer resources touching the given virtual
diff --git a/src/core/perf_stats.cpp b/src/core/perf_stats.cpp
index 5f53b16d3..8e09b9b63 100644
--- a/src/core/perf_stats.cpp
+++ b/src/core/perf_stats.cpp
@@ -40,22 +40,21 @@ void PerfStats::EndGameFrame() {
40 game_frames += 1; 40 game_frames += 1;
41} 41}
42 42
43PerfStats::Results PerfStats::GetAndResetStats(u64 current_system_time_us) { 43PerfStats::Results PerfStats::GetAndResetStats(microseconds current_system_time_us) {
44 std::lock_guard<std::mutex> lock(object_mutex); 44 std::lock_guard<std::mutex> lock(object_mutex);
45 45
46 auto now = Clock::now(); 46 const auto now = Clock::now();
47 // Walltime elapsed since stats were reset 47 // Walltime elapsed since stats were reset
48 auto interval = duration_cast<DoubleSecs>(now - reset_point).count(); 48 const auto interval = duration_cast<DoubleSecs>(now - reset_point).count();
49 49
50 auto system_us_per_second = 50 const auto system_us_per_second = (current_system_time_us - reset_point_system_us) / interval;
51 static_cast<double>(current_system_time_us - reset_point_system_us) / interval;
52 51
53 Results results{}; 52 Results results{};
54 results.system_fps = static_cast<double>(system_frames) / interval; 53 results.system_fps = static_cast<double>(system_frames) / interval;
55 results.game_fps = static_cast<double>(game_frames) / interval; 54 results.game_fps = static_cast<double>(game_frames) / interval;
56 results.frametime = duration_cast<DoubleSecs>(accumulated_frametime).count() / 55 results.frametime = duration_cast<DoubleSecs>(accumulated_frametime).count() /
57 static_cast<double>(system_frames); 56 static_cast<double>(system_frames);
58 results.emulation_speed = system_us_per_second / 1'000'000.0; 57 results.emulation_speed = system_us_per_second.count() / 1'000'000.0;
59 58
60 // Reset counters 59 // Reset counters
61 reset_point = now; 60 reset_point = now;
@@ -74,10 +73,10 @@ double PerfStats::GetLastFrameTimeScale() {
74 return duration_cast<DoubleSecs>(previous_frame_length).count() / FRAME_LENGTH; 73 return duration_cast<DoubleSecs>(previous_frame_length).count() / FRAME_LENGTH;
75} 74}
76 75
77void FrameLimiter::DoFrameLimiting(u64 current_system_time_us) { 76void FrameLimiter::DoFrameLimiting(microseconds current_system_time_us) {
78 // Max lag caused by slow frames. Can be adjusted to compensate for too many slow frames. Higher 77 // Max lag caused by slow frames. Can be adjusted to compensate for too many slow frames. Higher
79 // values increase the time needed to recover and limit framerate again after spikes. 78 // values increase the time needed to recover and limit framerate again after spikes.
80 constexpr microseconds MAX_LAG_TIME_US = 25ms; 79 constexpr microseconds MAX_LAG_TIME_US = 25us;
81 80
82 if (!Settings::values.toggle_framelimit) { 81 if (!Settings::values.toggle_framelimit) {
83 return; 82 return;
@@ -85,7 +84,7 @@ void FrameLimiter::DoFrameLimiting(u64 current_system_time_us) {
85 84
86 auto now = Clock::now(); 85 auto now = Clock::now();
87 86
88 frame_limiting_delta_err += microseconds(current_system_time_us - previous_system_time_us); 87 frame_limiting_delta_err += current_system_time_us - previous_system_time_us;
89 frame_limiting_delta_err -= duration_cast<microseconds>(now - previous_walltime); 88 frame_limiting_delta_err -= duration_cast<microseconds>(now - previous_walltime);
90 frame_limiting_delta_err = 89 frame_limiting_delta_err =
91 std::clamp(frame_limiting_delta_err, -MAX_LAG_TIME_US, MAX_LAG_TIME_US); 90 std::clamp(frame_limiting_delta_err, -MAX_LAG_TIME_US, MAX_LAG_TIME_US);
diff --git a/src/core/perf_stats.h b/src/core/perf_stats.h
index 362b205c8..6e4619701 100644
--- a/src/core/perf_stats.h
+++ b/src/core/perf_stats.h
@@ -33,7 +33,7 @@ public:
33 void EndSystemFrame(); 33 void EndSystemFrame();
34 void EndGameFrame(); 34 void EndGameFrame();
35 35
36 Results GetAndResetStats(u64 current_system_time_us); 36 Results GetAndResetStats(std::chrono::microseconds current_system_time_us);
37 37
38 /** 38 /**
39 * Gets the ratio between walltime and the emulated time of the previous system frame. This is 39 * Gets the ratio between walltime and the emulated time of the previous system frame. This is
@@ -47,7 +47,7 @@ private:
47 /// Point when the cumulative counters were reset 47 /// Point when the cumulative counters were reset
48 Clock::time_point reset_point = Clock::now(); 48 Clock::time_point reset_point = Clock::now();
49 /// System time when the cumulative counters were reset 49 /// System time when the cumulative counters were reset
50 u64 reset_point_system_us = 0; 50 std::chrono::microseconds reset_point_system_us{0};
51 51
52 /// Cumulative duration (excluding v-sync/frame-limiting) of frames since last reset 52 /// Cumulative duration (excluding v-sync/frame-limiting) of frames since last reset
53 Clock::duration accumulated_frametime = Clock::duration::zero(); 53 Clock::duration accumulated_frametime = Clock::duration::zero();
@@ -68,11 +68,11 @@ class FrameLimiter {
68public: 68public:
69 using Clock = std::chrono::high_resolution_clock; 69 using Clock = std::chrono::high_resolution_clock;
70 70
71 void DoFrameLimiting(u64 current_system_time_us); 71 void DoFrameLimiting(std::chrono::microseconds current_system_time_us);
72 72
73private: 73private:
74 /// Emulated system time (in microseconds) at the last limiter invocation 74 /// Emulated system time (in microseconds) at the last limiter invocation
75 u64 previous_system_time_us = 0; 75 std::chrono::microseconds previous_system_time_us{0};
76 /// Walltime at the last limiter invocation 76 /// Walltime at the last limiter invocation
77 Clock::time_point previous_walltime = Clock::now(); 77 Clock::time_point previous_walltime = Clock::now();
78 78
diff --git a/src/video_core/engines/maxwell_3d.cpp b/src/video_core/engines/maxwell_3d.cpp
index a235b543e..5c0ae8009 100644
--- a/src/video_core/engines/maxwell_3d.cpp
+++ b/src/video_core/engines/maxwell_3d.cpp
@@ -285,8 +285,6 @@ Texture::TICEntry Maxwell3D::GetTICEntry(u32 tic_index) const {
285 285
286 // TODO(Subv): Different data types for separate components are not supported 286 // TODO(Subv): Different data types for separate components are not supported
287 ASSERT(r_type == g_type && r_type == b_type && r_type == a_type); 287 ASSERT(r_type == g_type && r_type == b_type && r_type == a_type);
288 // TODO(Subv): Only UNORM formats are supported for now.
289 ASSERT(r_type == Texture::ComponentType::UNORM);
290 288
291 return tic_entry; 289 return tic_entry;
292} 290}
diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
index c8f0c4e28..257aa9571 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
@@ -46,6 +46,8 @@ struct FormatTuple {
46 params.height = Common::AlignUp(config.tic.Height(), GetCompressionFactor(params.pixel_format)); 46 params.height = Common::AlignUp(config.tic.Height(), GetCompressionFactor(params.pixel_format));
47 params.unaligned_height = config.tic.Height(); 47 params.unaligned_height = config.tic.Height();
48 params.size_in_bytes = params.SizeInBytes(); 48 params.size_in_bytes = params.SizeInBytes();
49 params.cache_width = Common::AlignUp(params.width, 16);
50 params.cache_height = Common::AlignUp(params.height, 16);
49 return params; 51 return params;
50} 52}
51 53
@@ -63,6 +65,8 @@ struct FormatTuple {
63 params.height = config.height; 65 params.height = config.height;
64 params.unaligned_height = config.height; 66 params.unaligned_height = config.height;
65 params.size_in_bytes = params.SizeInBytes(); 67 params.size_in_bytes = params.SizeInBytes();
68 params.cache_width = Common::AlignUp(params.width, 16);
69 params.cache_height = Common::AlignUp(params.height, 16);
66 return params; 70 return params;
67} 71}
68 72
@@ -82,6 +86,8 @@ struct FormatTuple {
82 params.height = zeta_height; 86 params.height = zeta_height;
83 params.unaligned_height = zeta_height; 87 params.unaligned_height = zeta_height;
84 params.size_in_bytes = params.SizeInBytes(); 88 params.size_in_bytes = params.SizeInBytes();
89 params.cache_width = Common::AlignUp(params.width, 16);
90 params.cache_height = Common::AlignUp(params.height, 16);
85 return params; 91 return params;
86} 92}
87 93
@@ -680,12 +686,12 @@ Surface RasterizerCacheOpenGL::GetSurface(const SurfaceParams& params) {
680 // If use_accurate_framebuffers is enabled, always load from memory 686 // If use_accurate_framebuffers is enabled, always load from memory
681 FlushSurface(surface); 687 FlushSurface(surface);
682 UnregisterSurface(surface); 688 UnregisterSurface(surface);
683 } else if (surface->GetSurfaceParams() != params) { 689 } else if (surface->GetSurfaceParams().IsCompatibleSurface(params)) {
684 // If surface parameters changed, recreate the surface from the old one
685 return RecreateSurface(surface, params);
686 } else {
687 // Use the cached surface as-is 690 // Use the cached surface as-is
688 return surface; 691 return surface;
692 } else {
693 // If surface parameters changed, recreate the surface from the old one
694 return RecreateSurface(surface, params);
689 } 695 }
690 } 696 }
691 697
diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.h b/src/video_core/renderer_opengl/gl_rasterizer_cache.h
index 4e1e18d9c..39fcf22b4 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer_cache.h
+++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.h
@@ -9,6 +9,7 @@
9#include <memory> 9#include <memory>
10#include <vector> 10#include <vector>
11#include <boost/icl/interval_map.hpp> 11#include <boost/icl/interval_map.hpp>
12
12#include "common/common_types.h" 13#include "common/common_types.h"
13#include "common/math_util.h" 14#include "common/math_util.h"
14#include "video_core/engines/maxwell_3d.h" 15#include "video_core/engines/maxwell_3d.h"
@@ -546,6 +547,12 @@ struct SurfaceParams {
546 return !operator==(other); 547 return !operator==(other);
547 } 548 }
548 549
550 /// Checks if surfaces are compatible for caching
551 bool IsCompatibleSurface(const SurfaceParams& other) const {
552 return std::tie(pixel_format, type, cache_width, cache_height) ==
553 std::tie(other.pixel_format, other.type, other.cache_width, other.cache_height);
554 }
555
549 Tegra::GPUVAddr addr; 556 Tegra::GPUVAddr addr;
550 bool is_tiled; 557 bool is_tiled;
551 u32 block_height; 558 u32 block_height;
@@ -556,6 +563,10 @@ struct SurfaceParams {
556 u32 height; 563 u32 height;
557 u32 unaligned_height; 564 u32 unaligned_height;
558 size_t size_in_bytes; 565 size_t size_in_bytes;
566
567 // Parameters used for caching only
568 u32 cache_width;
569 u32 cache_height;
559}; 570};
560 571
561class CachedSurface final { 572class CachedSurface final {
diff --git a/src/yuzu/about_dialog.cpp b/src/yuzu/about_dialog.cpp
index 39ed3bccf..a81ad2888 100644
--- a/src/yuzu/about_dialog.cpp
+++ b/src/yuzu/about_dialog.cpp
@@ -15,4 +15,4 @@ AboutDialog::AboutDialog(QWidget* parent) : QDialog(parent), ui(new Ui::AboutDia
15 Common::g_scm_desc, QString(Common::g_build_date).left(10))); 15 Common::g_scm_desc, QString(Common::g_build_date).left(10)));
16} 16}
17 17
18AboutDialog::~AboutDialog() {} 18AboutDialog::~AboutDialog() = default;
diff --git a/src/yuzu/about_dialog.h b/src/yuzu/about_dialog.h
index 2eb6e28f5..18e8c11a7 100644
--- a/src/yuzu/about_dialog.h
+++ b/src/yuzu/about_dialog.h
@@ -16,7 +16,7 @@ class AboutDialog : public QDialog {
16 16
17public: 17public:
18 explicit AboutDialog(QWidget* parent); 18 explicit AboutDialog(QWidget* parent);
19 ~AboutDialog(); 19 ~AboutDialog() override;
20 20
21private: 21private:
22 std::unique_ptr<Ui::AboutDialog> ui; 22 std::unique_ptr<Ui::AboutDialog> ui;
diff --git a/src/yuzu/bootmanager.h b/src/yuzu/bootmanager.h
index 130bc613b..d0f990c64 100644
--- a/src/yuzu/bootmanager.h
+++ b/src/yuzu/bootmanager.h
@@ -106,7 +106,7 @@ class GRenderWindow : public QWidget, public EmuWindow {
106 106
107public: 107public:
108 GRenderWindow(QWidget* parent, EmuThread* emu_thread); 108 GRenderWindow(QWidget* parent, EmuThread* emu_thread);
109 ~GRenderWindow(); 109 ~GRenderWindow() override;
110 110
111 // EmuWindow implementation 111 // EmuWindow implementation
112 void SwapBuffers() override; 112 void SwapBuffers() override;
diff --git a/src/yuzu/configuration/configure_debug.cpp b/src/yuzu/configuration/configure_debug.cpp
index 7fd07539a..45d84f19a 100644
--- a/src/yuzu/configuration/configure_debug.cpp
+++ b/src/yuzu/configuration/configure_debug.cpp
@@ -24,7 +24,7 @@ ConfigureDebug::ConfigureDebug(QWidget* parent) : QWidget(parent), ui(new Ui::Co
24 }); 24 });
25} 25}
26 26
27ConfigureDebug::~ConfigureDebug() {} 27ConfigureDebug::~ConfigureDebug() = default;
28 28
29void ConfigureDebug::setConfiguration() { 29void ConfigureDebug::setConfiguration() {
30 ui->toggle_gdbstub->setChecked(Settings::values.use_gdbstub); 30 ui->toggle_gdbstub->setChecked(Settings::values.use_gdbstub);
diff --git a/src/yuzu/configuration/configure_debug.ui b/src/yuzu/configuration/configure_debug.ui
index 118e91cf1..5ae7276bd 100644
--- a/src/yuzu/configuration/configure_debug.ui
+++ b/src/yuzu/configuration/configure_debug.ui
@@ -23,13 +23,6 @@
23 </property> 23 </property>
24 <layout class="QVBoxLayout" name="verticalLayout_3"> 24 <layout class="QVBoxLayout" name="verticalLayout_3">
25 <item> 25 <item>
26 <widget class="QLabel" name="label_1">
27 <property name="text">
28 <string>The GDB Stub only works correctly when the CPU JIT is off.</string>
29 </property>
30 </widget>
31 </item>
32 <item>
33 <layout class="QHBoxLayout" name="horizontalLayout_1"> 26 <layout class="QHBoxLayout" name="horizontalLayout_1">
34 <item> 27 <item>
35 <widget class="QCheckBox" name="toggle_gdbstub"> 28 <widget class="QCheckBox" name="toggle_gdbstub">
diff --git a/src/yuzu/configuration/configure_dialog.cpp b/src/yuzu/configuration/configure_dialog.cpp
index f66abf870..1ca7e876c 100644
--- a/src/yuzu/configuration/configure_dialog.cpp
+++ b/src/yuzu/configuration/configure_dialog.cpp
@@ -12,7 +12,7 @@ ConfigureDialog::ConfigureDialog(QWidget* parent) : QDialog(parent), ui(new Ui::
12 this->setConfiguration(); 12 this->setConfiguration();
13} 13}
14 14
15ConfigureDialog::~ConfigureDialog() {} 15ConfigureDialog::~ConfigureDialog() = default;
16 16
17void ConfigureDialog::setConfiguration() {} 17void ConfigureDialog::setConfiguration() {}
18 18
diff --git a/src/yuzu/configuration/configure_general.cpp b/src/yuzu/configuration/configure_general.cpp
index cb7d3f8bf..04afc8724 100644
--- a/src/yuzu/configuration/configure_general.cpp
+++ b/src/yuzu/configuration/configure_general.cpp
@@ -24,7 +24,7 @@ ConfigureGeneral::ConfigureGeneral(QWidget* parent)
24 ui->use_docked_mode->setEnabled(!Core::System::GetInstance().IsPoweredOn()); 24 ui->use_docked_mode->setEnabled(!Core::System::GetInstance().IsPoweredOn());
25} 25}
26 26
27ConfigureGeneral::~ConfigureGeneral() {} 27ConfigureGeneral::~ConfigureGeneral() = default;
28 28
29void ConfigureGeneral::setConfiguration() { 29void ConfigureGeneral::setConfiguration() {
30 ui->toggle_deepscan->setChecked(UISettings::values.gamedir_deepscan); 30 ui->toggle_deepscan->setChecked(UISettings::values.gamedir_deepscan);
diff --git a/src/yuzu/configuration/configure_graphics.cpp b/src/yuzu/configuration/configure_graphics.cpp
index 3379b7963..4afe0f81b 100644
--- a/src/yuzu/configuration/configure_graphics.cpp
+++ b/src/yuzu/configuration/configure_graphics.cpp
@@ -14,7 +14,7 @@ ConfigureGraphics::ConfigureGraphics(QWidget* parent)
14 this->setConfiguration(); 14 this->setConfiguration();
15} 15}
16 16
17ConfigureGraphics::~ConfigureGraphics() {} 17ConfigureGraphics::~ConfigureGraphics() = default;
18 18
19enum class Resolution : int { 19enum class Resolution : int {
20 Auto, 20 Auto,
diff --git a/src/yuzu/configuration/configure_system.cpp b/src/yuzu/configuration/configure_system.cpp
index 9be2c939c..e9ed9c38f 100644
--- a/src/yuzu/configuration/configure_system.cpp
+++ b/src/yuzu/configuration/configure_system.cpp
@@ -35,7 +35,7 @@ ConfigureSystem::ConfigureSystem(QWidget* parent) : QWidget(parent), ui(new Ui::
35 this->setConfiguration(); 35 this->setConfiguration();
36} 36}
37 37
38ConfigureSystem::~ConfigureSystem() {} 38ConfigureSystem::~ConfigureSystem() = default;
39 39
40void ConfigureSystem::setConfiguration() { 40void ConfigureSystem::setConfiguration() {
41 enabled = !Core::System::GetInstance().IsPoweredOn(); 41 enabled = !Core::System::GetInstance().IsPoweredOn();
diff --git a/src/yuzu/debugger/graphics/graphics_surface.cpp b/src/yuzu/debugger/graphics/graphics_surface.cpp
index ff3efcdaa..3f7103ab9 100644
--- a/src/yuzu/debugger/graphics/graphics_surface.cpp
+++ b/src/yuzu/debugger/graphics/graphics_surface.cpp
@@ -34,7 +34,8 @@ static Tegra::Texture::TextureFormat ConvertToTextureFormat(
34 34
35SurfacePicture::SurfacePicture(QWidget* parent, GraphicsSurfaceWidget* surface_widget_) 35SurfacePicture::SurfacePicture(QWidget* parent, GraphicsSurfaceWidget* surface_widget_)
36 : QLabel(parent), surface_widget(surface_widget_) {} 36 : QLabel(parent), surface_widget(surface_widget_) {}
37SurfacePicture::~SurfacePicture() {} 37
38SurfacePicture::~SurfacePicture() = default;
38 39
39void SurfacePicture::mousePressEvent(QMouseEvent* event) { 40void SurfacePicture::mousePressEvent(QMouseEvent* event) {
40 // Only do something while the left mouse button is held down 41 // Only do something while the left mouse button is held down
diff --git a/src/yuzu/debugger/graphics/graphics_surface.h b/src/yuzu/debugger/graphics/graphics_surface.h
index 58f9db465..323e39d94 100644
--- a/src/yuzu/debugger/graphics/graphics_surface.h
+++ b/src/yuzu/debugger/graphics/graphics_surface.h
@@ -22,11 +22,11 @@ class SurfacePicture : public QLabel {
22public: 22public:
23 explicit SurfacePicture(QWidget* parent = nullptr, 23 explicit SurfacePicture(QWidget* parent = nullptr,
24 GraphicsSurfaceWidget* surface_widget = nullptr); 24 GraphicsSurfaceWidget* surface_widget = nullptr);
25 ~SurfacePicture(); 25 ~SurfacePicture() override;
26 26
27protected slots: 27protected slots:
28 virtual void mouseMoveEvent(QMouseEvent* event); 28 void mouseMoveEvent(QMouseEvent* event) override;
29 virtual void mousePressEvent(QMouseEvent* event); 29 void mousePressEvent(QMouseEvent* event) override;
30 30
31private: 31private:
32 GraphicsSurfaceWidget* surface_widget; 32 GraphicsSurfaceWidget* surface_widget;
diff --git a/src/yuzu/debugger/wait_tree.cpp b/src/yuzu/debugger/wait_tree.cpp
index f5a5697a0..d0926d723 100644
--- a/src/yuzu/debugger/wait_tree.cpp
+++ b/src/yuzu/debugger/wait_tree.cpp
@@ -14,7 +14,7 @@
14#include "core/hle/kernel/timer.h" 14#include "core/hle/kernel/timer.h"
15#include "core/hle/kernel/wait_object.h" 15#include "core/hle/kernel/wait_object.h"
16 16
17WaitTreeItem::~WaitTreeItem() {} 17WaitTreeItem::~WaitTreeItem() = default;
18 18
19QColor WaitTreeItem::GetColor() const { 19QColor WaitTreeItem::GetColor() const {
20 return QColor(Qt::GlobalColor::black); 20 return QColor(Qt::GlobalColor::black);
@@ -316,7 +316,7 @@ std::vector<std::unique_ptr<WaitTreeItem>> WaitTreeEvent::GetChildren() const {
316 316
317 list.push_back(std::make_unique<WaitTreeText>( 317 list.push_back(std::make_unique<WaitTreeText>(
318 tr("reset type = %1") 318 tr("reset type = %1")
319 .arg(GetResetTypeQString(static_cast<const Kernel::Event&>(object).reset_type)))); 319 .arg(GetResetTypeQString(static_cast<const Kernel::Event&>(object).GetResetType()))));
320 return list; 320 return list;
321} 321}
322 322
diff --git a/src/yuzu/debugger/wait_tree.h b/src/yuzu/debugger/wait_tree.h
index 6cbce6856..513b3c45d 100644
--- a/src/yuzu/debugger/wait_tree.h
+++ b/src/yuzu/debugger/wait_tree.h
@@ -25,11 +25,13 @@ class WaitTreeThread;
25class WaitTreeItem : public QObject { 25class WaitTreeItem : public QObject {
26 Q_OBJECT 26 Q_OBJECT
27public: 27public:
28 ~WaitTreeItem() override;
29
28 virtual bool IsExpandable() const; 30 virtual bool IsExpandable() const;
29 virtual std::vector<std::unique_ptr<WaitTreeItem>> GetChildren() const; 31 virtual std::vector<std::unique_ptr<WaitTreeItem>> GetChildren() const;
30 virtual QString GetText() const = 0; 32 virtual QString GetText() const = 0;
31 virtual QColor GetColor() const; 33 virtual QColor GetColor() const;
32 virtual ~WaitTreeItem(); 34
33 void Expand(); 35 void Expand();
34 WaitTreeItem* Parent() const; 36 WaitTreeItem* Parent() const;
35 const std::vector<std::unique_ptr<WaitTreeItem>>& Children() const; 37 const std::vector<std::unique_ptr<WaitTreeItem>>& Children() const;
diff --git a/src/yuzu/game_list.cpp b/src/yuzu/game_list.cpp
index 71e24a9e2..24f38a3c7 100644
--- a/src/yuzu/game_list.cpp
+++ b/src/yuzu/game_list.cpp
@@ -162,15 +162,15 @@ void GameList::onTextChanged(const QString& newText) {
162 } 162 }
163 search_field->setFilterResult(rowCount, rowCount); 163 search_field->setFilterResult(rowCount, rowCount);
164 } else { 164 } else {
165 QStandardItem* child_file;
166 QString file_path, file_name, file_title, file_programmid;
167 int result_count = 0; 165 int result_count = 0;
168 for (int i = 0; i < rowCount; ++i) { 166 for (int i = 0; i < rowCount; ++i) {
169 child_file = item_model->item(i, 0); 167 const QStandardItem* child_file = item_model->item(i, 0);
170 file_path = child_file->data(GameListItemPath::FullPathRole).toString().toLower(); 168 const QString file_path =
171 file_name = file_path.mid(file_path.lastIndexOf("/") + 1); 169 child_file->data(GameListItemPath::FullPathRole).toString().toLower();
172 file_title = child_file->data(GameListItemPath::TitleRole).toString().toLower(); 170 QString file_name = file_path.mid(file_path.lastIndexOf('/') + 1);
173 file_programmid = 171 const QString file_title =
172 child_file->data(GameListItemPath::TitleRole).toString().toLower();
173 const QString file_programmid =
174 child_file->data(GameListItemPath::ProgramIdRole).toString().toLower(); 174 child_file->data(GameListItemPath::ProgramIdRole).toString().toLower();
175 175
176 // Only items which filename in combination with its title contains all words 176 // Only items which filename in combination with its title contains all words
@@ -258,18 +258,20 @@ void GameList::AddEntry(const QList<QStandardItem*>& entry_items) {
258 258
259void GameList::ValidateEntry(const QModelIndex& item) { 259void GameList::ValidateEntry(const QModelIndex& item) {
260 // We don't care about the individual QStandardItem that was selected, but its row. 260 // We don't care about the individual QStandardItem that was selected, but its row.
261 int row = item_model->itemFromIndex(item)->row(); 261 const int row = item_model->itemFromIndex(item)->row();
262 QStandardItem* child_file = item_model->invisibleRootItem()->child(row, COLUMN_NAME); 262 const QStandardItem* child_file = item_model->invisibleRootItem()->child(row, COLUMN_NAME);
263 QString file_path = child_file->data(GameListItemPath::FullPathRole).toString(); 263 const QString file_path = child_file->data(GameListItemPath::FullPathRole).toString();
264 264
265 if (file_path.isEmpty()) 265 if (file_path.isEmpty())
266 return; 266 return;
267 std::string std_file_path(file_path.toStdString()); 267
268 if (!FileUtil::Exists(std_file_path)) 268 if (!QFileInfo::exists(file_path))
269 return; 269 return;
270 if (FileUtil::IsDirectory(std_file_path)) { 270
271 QDir dir(std_file_path.c_str()); 271 const QFileInfo file_info{file_path};
272 QStringList matching_main = dir.entryList(QStringList("main"), QDir::Files); 272 if (file_info.isDir()) {
273 const QDir dir{file_path};
274 const QStringList matching_main = dir.entryList(QStringList("main"), QDir::Files);
273 if (matching_main.size() == 1) { 275 if (matching_main.size() == 1) {
274 emit GameChosen(dir.path() + DIR_SEP + matching_main[0]); 276 emit GameChosen(dir.path() + DIR_SEP + matching_main[0]);
275 } 277 }
@@ -368,21 +370,23 @@ void GameList::LoadInterfaceLayout() {
368const QStringList GameList::supported_file_extensions = {"nso", "nro", "nca", "xci"}; 370const QStringList GameList::supported_file_extensions = {"nso", "nro", "nca", "xci"};
369 371
370static bool HasSupportedFileExtension(const std::string& file_name) { 372static bool HasSupportedFileExtension(const std::string& file_name) {
371 QFileInfo file = QFileInfo(file_name.c_str()); 373 const QFileInfo file = QFileInfo(QString::fromStdString(file_name));
372 return GameList::supported_file_extensions.contains(file.suffix(), Qt::CaseInsensitive); 374 return GameList::supported_file_extensions.contains(file.suffix(), Qt::CaseInsensitive);
373} 375}
374 376
375static bool IsExtractedNCAMain(const std::string& file_name) { 377static bool IsExtractedNCAMain(const std::string& file_name) {
376 return QFileInfo(file_name.c_str()).fileName() == "main"; 378 return QFileInfo(QString::fromStdString(file_name)).fileName() == "main";
377} 379}
378 380
379static QString FormatGameName(const std::string& physical_name) { 381static QString FormatGameName(const std::string& physical_name) {
380 QFileInfo file_info(physical_name.c_str()); 382 const QString physical_name_as_qstring = QString::fromStdString(physical_name);
383 const QFileInfo file_info(physical_name_as_qstring);
384
381 if (IsExtractedNCAMain(physical_name)) { 385 if (IsExtractedNCAMain(physical_name)) {
382 return file_info.dir().path(); 386 return file_info.dir().path();
383 } else {
384 return QString::fromStdString(physical_name);
385 } 387 }
388
389 return physical_name_as_qstring;
386} 390}
387 391
388void GameList::RefreshGameDirectory() { 392void GameList::RefreshGameDirectory() {
diff --git a/src/yuzu/game_list_p.h b/src/yuzu/game_list_p.h
index a758b77aa..aa69a098f 100644
--- a/src/yuzu/game_list_p.h
+++ b/src/yuzu/game_list_p.h
@@ -5,6 +5,7 @@
5#pragma once 5#pragma once
6 6
7#include <atomic> 7#include <atomic>
8#include <utility>
8#include <QImage> 9#include <QImage>
9#include <QRunnable> 10#include <QRunnable>
10#include <QStandardItem> 11#include <QStandardItem>
@@ -27,9 +28,8 @@ static QPixmap GetDefaultIcon(bool large) {
27class GameListItem : public QStandardItem { 28class GameListItem : public QStandardItem {
28 29
29public: 30public:
30 GameListItem() : QStandardItem() {} 31 GameListItem() = default;
31 GameListItem(const QString& string) : QStandardItem(string) {} 32 explicit GameListItem(const QString& string) : QStandardItem(string) {}
32 virtual ~GameListItem() override {}
33}; 33};
34 34
35/** 35/**
@@ -45,9 +45,8 @@ public:
45 static const int TitleRole = Qt::UserRole + 2; 45 static const int TitleRole = Qt::UserRole + 2;
46 static const int ProgramIdRole = Qt::UserRole + 3; 46 static const int ProgramIdRole = Qt::UserRole + 3;
47 47
48 GameListItemPath() : GameListItem() {} 48 GameListItemPath() = default;
49 GameListItemPath(const QString& game_path, const std::vector<u8>& smdh_data, u64 program_id) 49 GameListItemPath(const QString& game_path, const std::vector<u8>& smdh_data, u64 program_id) {
50 : GameListItem() {
51 setData(game_path, FullPathRole); 50 setData(game_path, FullPathRole);
52 setData(qulonglong(program_id), ProgramIdRole); 51 setData(qulonglong(program_id), ProgramIdRole);
53 } 52 }
@@ -75,8 +74,8 @@ class GameListItemSize : public GameListItem {
75public: 74public:
76 static const int SizeRole = Qt::UserRole + 1; 75 static const int SizeRole = Qt::UserRole + 1;
77 76
78 GameListItemSize() : GameListItem() {} 77 GameListItemSize() = default;
79 GameListItemSize(const qulonglong size_bytes) : GameListItem() { 78 explicit GameListItemSize(const qulonglong size_bytes) {
80 setData(size_bytes, SizeRole); 79 setData(size_bytes, SizeRole);
81 } 80 }
82 81
@@ -111,7 +110,7 @@ class GameListWorker : public QObject, public QRunnable {
111 110
112public: 111public:
113 GameListWorker(QString dir_path, bool deep_scan) 112 GameListWorker(QString dir_path, bool deep_scan)
114 : QObject(), QRunnable(), dir_path(dir_path), deep_scan(deep_scan) {} 113 : dir_path(std::move(dir_path)), deep_scan(deep_scan) {}
115 114
116public slots: 115public slots:
117 /// Starts the processing of directory tree information. 116 /// Starts the processing of directory tree information.
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index e28679cd1..dd71bd763 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -81,6 +81,8 @@ static void ShowCalloutMessage(const QString& message, CalloutFlag flag) {
81 81
82void GMainWindow::ShowCallouts() {} 82void GMainWindow::ShowCallouts() {}
83 83
84const int GMainWindow::max_recent_files_item;
85
84GMainWindow::GMainWindow() : config(new Config()), emu_thread(nullptr) { 86GMainWindow::GMainWindow() : config(new Config()), emu_thread(nullptr) {
85 87
86 debug_context = Tegra::DebugContext::Construct(); 88 debug_context = Tegra::DebugContext::Construct();
@@ -579,11 +581,11 @@ void GMainWindow::StoreRecentFile(const QString& filename) {
579} 581}
580 582
581void GMainWindow::UpdateRecentFiles() { 583void GMainWindow::UpdateRecentFiles() {
582 unsigned int num_recent_files = 584 const int num_recent_files =
583 std::min(UISettings::values.recent_files.size(), static_cast<int>(max_recent_files_item)); 585 std::min(UISettings::values.recent_files.size(), max_recent_files_item);
584 586
585 for (unsigned int i = 0; i < num_recent_files; i++) { 587 for (int i = 0; i < num_recent_files; i++) {
586 QString text = QString("&%1. %2").arg(i + 1).arg( 588 const QString text = QString("&%1. %2").arg(i + 1).arg(
587 QFileInfo(UISettings::values.recent_files[i]).fileName()); 589 QFileInfo(UISettings::values.recent_files[i]).fileName());
588 actions_recent_files[i]->setText(text); 590 actions_recent_files[i]->setText(text);
589 actions_recent_files[i]->setData(UISettings::values.recent_files[i]); 591 actions_recent_files[i]->setData(UISettings::values.recent_files[i]);
@@ -595,12 +597,8 @@ void GMainWindow::UpdateRecentFiles() {
595 actions_recent_files[j]->setVisible(false); 597 actions_recent_files[j]->setVisible(false);
596 } 598 }
597 599
598 // Grey out the recent files menu if the list is empty 600 // Enable the recent files menu if the list isn't empty
599 if (num_recent_files == 0) { 601 ui.menu_recent_files->setEnabled(num_recent_files != 0);
600 ui.menu_recent_files->setEnabled(false);
601 } else {
602 ui.menu_recent_files->setEnabled(true);
603 }
604} 602}
605 603
606void GMainWindow::OnGameListLoadFile(QString game_path) { 604void GMainWindow::OnGameListLoadFile(QString game_path) {
@@ -631,9 +629,15 @@ void GMainWindow::OnMenuLoadFile() {
631} 629}
632 630
633void GMainWindow::OnMenuLoadFolder() { 631void GMainWindow::OnMenuLoadFolder() {
634 QDir dir = QFileDialog::getExistingDirectory(this, tr("Open Extracted ROM Directory")); 632 const QString dir_path =
633 QFileDialog::getExistingDirectory(this, tr("Open Extracted ROM Directory"));
634
635 if (dir_path.isNull()) {
636 return;
637 }
635 638
636 QStringList matching_main = dir.entryList(QStringList("main"), QDir::Files); 639 const QDir dir{dir_path};
640 const QStringList matching_main = dir.entryList(QStringList("main"), QDir::Files);
637 if (matching_main.size() == 1) { 641 if (matching_main.size() == 1) {
638 BootGame(dir.path() + DIR_SEP + matching_main[0]); 642 BootGame(dir.path() + DIR_SEP + matching_main[0]);
639 } else { 643 } else {
@@ -654,9 +658,8 @@ void GMainWindow::OnMenuRecentFile() {
654 QAction* action = qobject_cast<QAction*>(sender()); 658 QAction* action = qobject_cast<QAction*>(sender());
655 assert(action); 659 assert(action);
656 660
657 QString filename = action->data().toString(); 661 const QString filename = action->data().toString();
658 QFileInfo file_info(filename); 662 if (QFileInfo::exists(filename)) {
659 if (file_info.exists()) {
660 BootGame(filename); 663 BootGame(filename);
661 } else { 664 } else {
662 // Display an error message and remove the file from the list. 665 // Display an error message and remove the file from the list.
@@ -947,15 +950,14 @@ void GMainWindow::UpdateUITheme() {
947 QStringList theme_paths(default_theme_paths); 950 QStringList theme_paths(default_theme_paths);
948 if (UISettings::values.theme != UISettings::themes[0].second && 951 if (UISettings::values.theme != UISettings::themes[0].second &&
949 !UISettings::values.theme.isEmpty()) { 952 !UISettings::values.theme.isEmpty()) {
950 QString theme_uri(":" + UISettings::values.theme + "/style.qss"); 953 const QString theme_uri(":" + UISettings::values.theme + "/style.qss");
951 QFile f(theme_uri); 954 QFile f(theme_uri);
952 if (!f.exists()) { 955 if (f.open(QFile::ReadOnly | QFile::Text)) {
953 LOG_ERROR(Frontend, "Unable to set style, stylesheet file not found");
954 } else {
955 f.open(QFile::ReadOnly | QFile::Text);
956 QTextStream ts(&f); 956 QTextStream ts(&f);
957 qApp->setStyleSheet(ts.readAll()); 957 qApp->setStyleSheet(ts.readAll());
958 GMainWindow::setStyleSheet(ts.readAll()); 958 GMainWindow::setStyleSheet(ts.readAll());
959 } else {
960 LOG_ERROR(Frontend, "Unable to set style, stylesheet file not found");
959 } 961 }
960 theme_paths.append(QStringList{":/icons/default", ":/icons/" + UISettings::values.theme}); 962 theme_paths.append(QStringList{":/icons/default", ":/icons/" + UISettings::values.theme});
961 QIcon::setThemeName(":/icons/" + UISettings::values.theme); 963 QIcon::setThemeName(":/icons/" + UISettings::values.theme);
diff --git a/src/yuzu/main.h b/src/yuzu/main.h
index 074bba3f9..a60d831b9 100644
--- a/src/yuzu/main.h
+++ b/src/yuzu/main.h
@@ -43,7 +43,7 @@ public:
43 void filterBarSetChecked(bool state); 43 void filterBarSetChecked(bool state);
44 void UpdateUITheme(); 44 void UpdateUITheme();
45 GMainWindow(); 45 GMainWindow();
46 ~GMainWindow(); 46 ~GMainWindow() override;
47 47
48signals: 48signals:
49 49