summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/audio_core/CMakeLists.txt8
-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/common/logging/backend.cpp2
-rw-r--r--src/common/logging/log.h2
-rw-r--r--src/core/CMakeLists.txt4
-rw-r--r--src/core/arm/unicorn/arm_unicorn.cpp2
-rw-r--r--src/core/core.cpp14
-rw-r--r--src/core/core.h33
-rw-r--r--src/core/core_timing.cpp14
-rw-r--r--src/core/core_timing.h11
-rw-r--r--src/core/crypto/aes_util.cpp35
-rw-r--r--src/core/crypto/aes_util.h12
-rw-r--r--src/core/crypto/encryption_layer.h1
-rw-r--r--src/core/crypto/key_manager.cpp15
-rw-r--r--src/core/crypto/key_manager.h7
-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/client_port.cpp12
-rw-r--r--src/core/hle/kernel/client_port.h14
-rw-r--r--src/core/hle/kernel/event.h12
-rw-r--r--src/core/hle/kernel/server_session.cpp2
-rw-r--r--src/core/hle/service/apm/apm.cpp1
-rw-r--r--src/core/hle/service/apm/interface.cpp25
-rw-r--r--src/core/hle/service/apm/interface.h8
-rw-r--r--src/core/hle/service/arp/arp.cpp75
-rw-r--r--src/core/hle/service/arp/arp.h16
-rw-r--r--src/core/hle/service/audio/audin_a.cpp2
-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/audrec_a.cpp2
-rw-r--r--src/core/hle/service/audio/audren_a.cpp2
-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/hle/service/filesystem/fsp_ldr.cpp2
-rw-r--r--src/core/hle/service/filesystem/fsp_pr.cpp2
-rw-r--r--src/core/hle/service/hid/hid.cpp1
-rw-r--r--src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp8
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp16
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp3
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_ctrl.h2
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp2
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp6
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_gpu.h1
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp2
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_nvdec.h4
-rw-r--r--src/core/hle/service/nvdrv/devices/nvmap.cpp2
-rw-r--r--src/core/hle/service/nvdrv/interface.h1
-rw-r--r--src/core/hle/service/nvdrv/nvdrv.cpp2
-rw-r--r--src/core/hle/service/nvdrv/nvmemp.cpp2
-rw-r--r--src/core/hle/service/nvflinger/nvflinger.cpp13
-rw-r--r--src/core/hle/service/nvflinger/nvflinger.h8
-rw-r--r--src/core/hle/service/service.cpp4
-rw-r--r--src/core/hle/service/time/time.cpp4
-rw-r--r--src/core/hle/service/usb/usb.cpp238
-rw-r--r--src/core/hle/service/usb/usb.h15
-rw-r--r--src/core/loader/deconstructed_rom_directory.cpp11
-rw-r--r--src/core/loader/deconstructed_rom_directory.h4
-rw-r--r--src/core/loader/nca.cpp44
-rw-r--r--src/core/loader/nca.h2
-rw-r--r--src/core/memory.cpp24
-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/core/settings.cpp6
-rw-r--r--src/video_core/engines/maxwell_3d.cpp12
-rw-r--r--src/video_core/engines/maxwell_3d.h8
-rw-r--r--src/video_core/gpu.cpp5
-rw-r--r--src/video_core/gpu.h6
-rw-r--r--src/video_core/renderer_base.cpp4
-rw-r--r--src/video_core/renderer_base.h14
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.cpp10
-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/video_core/renderer_opengl/gl_shader_decompiler.cpp7
-rw-r--r--src/video_core/renderer_opengl/renderer_opengl.cpp6
-rw-r--r--src/video_core/renderer_opengl/renderer_opengl.h2
-rw-r--r--src/video_core/video_core.cpp25
-rw-r--r--src/video_core/video_core.h20
-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.cpp7
-rw-r--r--src/yuzu/configuration/configure_dialog.h4
-rw-r--r--src/yuzu/configuration/configure_general.cpp6
-rw-r--r--src/yuzu/configuration/configure_general.h4
-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/hotkeys.cpp67
-rw-r--r--src/yuzu/hotkeys.h107
-rw-r--r--src/yuzu/main.cpp130
-rw-r--r--src/yuzu/main.h5
-rw-r--r--src/yuzu_cmd/yuzu.cpp2
115 files changed, 1706 insertions, 831 deletions
diff --git a/src/audio_core/CMakeLists.txt b/src/audio_core/CMakeLists.txt
index 81121167d..ec71524a3 100644
--- a/src/audio_core/CMakeLists.txt
+++ b/src/audio_core/CMakeLists.txt
@@ -1,9 +1,11 @@
add_library(audio_core STATIC
audio_out.cpp
audio_out.h
+ audio_renderer.cpp
+ audio_renderer.h
buffer.h
- cubeb_sink.cpp
- cubeb_sink.h
+ codec.cpp
+ codec.h
null_sink.h
stream.cpp
stream.h
@@ -11,6 +13,8 @@ add_library(audio_core STATIC
sink_details.cpp
sink_details.h
sink_stream.h
+
+ $<$<BOOL:${ENABLE_CUBEB}>:cubeb_sink.cpp cubeb_sink.h>
)
create_target_directory_groups(audio_core)
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) {
return {};
}
-StreamPtr AudioOut::OpenStream(u32 sample_rate, u32 num_channels,
+StreamPtr AudioOut::OpenStream(u32 sample_rate, u32 num_channels, std::string&& name,
Stream::ReleaseCallback&& release_callback) {
if (!sink) {
const SinkDetails& sink_details = GetSinkDetails(Settings::values.sink_id);
sink = sink_details.factory(Settings::values.audio_device_id);
}
- return std::make_shared<Stream>(sample_rate, ChannelsToStreamFormat(num_channels),
- std::move(release_callback),
- sink->AcquireSinkStream(sample_rate, num_channels));
+ return std::make_shared<Stream>(
+ sample_rate, ChannelsToStreamFormat(num_channels), std::move(release_callback),
+ sink->AcquireSinkStream(sample_rate, num_channels, name), std::move(name));
}
std::vector<Buffer::Tag> AudioOut::GetTagsAndReleaseBuffers(StreamPtr stream, size_t max_count) {
@@ -51,7 +51,7 @@ void AudioOut::StopStream(StreamPtr stream) {
stream->Stop();
}
-bool AudioOut::QueueBuffer(StreamPtr stream, Buffer::Tag tag, std::vector<u8>&& data) {
+bool AudioOut::QueueBuffer(StreamPtr stream, Buffer::Tag tag, std::vector<s16>&& data) {
return stream->QueueBuffer(std::make_shared<Buffer>(tag, std::move(data)));
}
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 @@
#pragma once
#include <memory>
+#include <string>
#include <vector>
#include "audio_core/buffer.h"
@@ -20,7 +21,7 @@ namespace AudioCore {
class AudioOut {
public:
/// Opens a new audio stream
- StreamPtr OpenStream(u32 sample_rate, u32 num_channels,
+ StreamPtr OpenStream(u32 sample_rate, u32 num_channels, std::string&& name,
Stream::ReleaseCallback&& release_callback);
/// Returns a vector of recently released buffers specified by tag for the specified stream
@@ -33,7 +34,7 @@ public:
void StopStream(StreamPtr stream);
/// Queues a buffer into the specified audio stream, returns true on success
- bool QueueBuffer(StreamPtr stream, Buffer::Tag tag, std::vector<u8>&& data);
+ bool QueueBuffer(StreamPtr stream, Buffer::Tag tag, std::vector<s16>&& data);
private:
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 @@
+// Copyright 2018 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "audio_core/audio_renderer.h"
+#include "common/assert.h"
+#include "common/logging/log.h"
+#include "core/memory.h"
+
+namespace AudioCore {
+
+constexpr u32 STREAM_SAMPLE_RATE{48000};
+constexpr u32 STREAM_NUM_CHANNELS{2};
+
+AudioRenderer::AudioRenderer(AudioRendererParameter params,
+ Kernel::SharedPtr<Kernel::Event> buffer_event)
+ : worker_params{params}, buffer_event{buffer_event}, voices(params.voice_count) {
+
+ audio_core = std::make_unique<AudioCore::AudioOut>();
+ stream = audio_core->OpenStream(STREAM_SAMPLE_RATE, STREAM_NUM_CHANNELS, "AudioRenderer",
+ [=]() { buffer_event->Signal(); });
+ audio_core->StartStream(stream);
+
+ QueueMixedBuffer(0);
+ QueueMixedBuffer(1);
+ QueueMixedBuffer(2);
+}
+
+std::vector<u8> AudioRenderer::UpdateAudioRenderer(const std::vector<u8>& input_params) {
+ // Copy UpdateDataHeader struct
+ UpdateDataHeader config{};
+ std::memcpy(&config, input_params.data(), sizeof(UpdateDataHeader));
+ u32 memory_pool_count = worker_params.effect_count + (worker_params.voice_count * 4);
+
+ // Copy MemoryPoolInfo structs
+ std::vector<MemoryPoolInfo> mem_pool_info(memory_pool_count);
+ std::memcpy(mem_pool_info.data(),
+ input_params.data() + sizeof(UpdateDataHeader) + config.behavior_size,
+ memory_pool_count * sizeof(MemoryPoolInfo));
+
+ // Copy VoiceInfo structs
+ size_t offset{sizeof(UpdateDataHeader) + config.behavior_size + config.memory_pools_size +
+ config.voice_resource_size};
+ for (auto& voice : voices) {
+ std::memcpy(&voice.Info(), input_params.data() + offset, sizeof(VoiceInfo));
+ offset += sizeof(VoiceInfo);
+ }
+
+ // Update voices
+ for (auto& voice : voices) {
+ voice.UpdateState();
+ if (!voice.GetInfo().is_in_use) {
+ continue;
+ }
+ if (voice.GetInfo().is_new) {
+ voice.SetWaveIndex(voice.GetInfo().wave_buffer_head);
+ }
+ }
+
+ // Update memory pool state
+ std::vector<MemoryPoolEntry> memory_pool(memory_pool_count);
+ for (size_t index = 0; index < memory_pool.size(); ++index) {
+ if (mem_pool_info[index].pool_state == MemoryPoolStates::RequestAttach) {
+ memory_pool[index].state = MemoryPoolStates::Attached;
+ } else if (mem_pool_info[index].pool_state == MemoryPoolStates::RequestDetach) {
+ memory_pool[index].state = MemoryPoolStates::Detached;
+ }
+ }
+
+ // Release previous buffers and queue next ones for playback
+ ReleaseAndQueueBuffers();
+
+ // Copy output header
+ UpdateDataHeader response_data{worker_params};
+ std::vector<u8> output_params(response_data.total_size);
+ std::memcpy(output_params.data(), &response_data, sizeof(UpdateDataHeader));
+
+ // Copy output memory pool entries
+ std::memcpy(output_params.data() + sizeof(UpdateDataHeader), memory_pool.data(),
+ response_data.memory_pools_size);
+
+ // Copy output voice status
+ size_t voice_out_status_offset{sizeof(UpdateDataHeader) + response_data.memory_pools_size};
+ for (const auto& voice : voices) {
+ std::memcpy(output_params.data() + voice_out_status_offset, &voice.GetOutStatus(),
+ sizeof(VoiceOutStatus));
+ voice_out_status_offset += sizeof(VoiceOutStatus);
+ }
+
+ return output_params;
+}
+
+void AudioRenderer::VoiceState::SetWaveIndex(size_t index) {
+ wave_index = index & 3;
+ is_refresh_pending = true;
+}
+
+std::vector<s16> AudioRenderer::VoiceState::DequeueSamples(size_t sample_count) {
+ if (!IsPlaying()) {
+ return {};
+ }
+
+ if (is_refresh_pending) {
+ RefreshBuffer();
+ }
+
+ const size_t max_size{samples.size() - offset};
+ const size_t dequeue_offset{offset};
+ size_t size{sample_count * STREAM_NUM_CHANNELS};
+ if (size > max_size) {
+ size = max_size;
+ }
+
+ out_status.played_sample_count += size / STREAM_NUM_CHANNELS;
+ offset += size;
+
+ const auto& wave_buffer{info.wave_buffer[wave_index]};
+ if (offset == samples.size()) {
+ offset = 0;
+
+ if (!wave_buffer.is_looping) {
+ SetWaveIndex(wave_index + 1);
+ }
+
+ out_status.wave_buffer_consumed++;
+
+ if (wave_buffer.end_of_stream) {
+ info.play_state = PlayState::Paused;
+ }
+ }
+
+ return {samples.begin() + dequeue_offset, samples.begin() + dequeue_offset + size};
+}
+
+void AudioRenderer::VoiceState::UpdateState() {
+ if (is_in_use && !info.is_in_use) {
+ // No longer in use, reset state
+ is_refresh_pending = true;
+ wave_index = 0;
+ offset = 0;
+ out_status = {};
+ }
+ is_in_use = info.is_in_use;
+}
+
+void AudioRenderer::VoiceState::RefreshBuffer() {
+ std::vector<s16> new_samples(info.wave_buffer[wave_index].buffer_sz / sizeof(s16));
+ Memory::ReadBlock(info.wave_buffer[wave_index].buffer_addr, new_samples.data(),
+ info.wave_buffer[wave_index].buffer_sz);
+
+ switch (static_cast<Codec::PcmFormat>(info.sample_format)) {
+ case Codec::PcmFormat::Int16: {
+ // PCM16 is played as-is
+ break;
+ }
+ case Codec::PcmFormat::Adpcm: {
+ // Decode ADPCM to PCM16
+ Codec::ADPCM_Coeff coeffs;
+ Memory::ReadBlock(info.additional_params_addr, coeffs.data(), sizeof(Codec::ADPCM_Coeff));
+ new_samples = Codec::DecodeADPCM(reinterpret_cast<u8*>(new_samples.data()),
+ new_samples.size() * sizeof(s16), coeffs, adpcm_state);
+ break;
+ }
+ default:
+ LOG_CRITICAL(Audio, "Unimplemented sample_format={}", info.sample_format);
+ UNREACHABLE();
+ break;
+ }
+
+ switch (info.channel_count) {
+ case 1:
+ // 1 channel is upsampled to 2 channel
+ samples.resize(new_samples.size() * 2);
+ for (size_t index = 0; index < new_samples.size(); ++index) {
+ samples[index * 2] = new_samples[index];
+ samples[index * 2 + 1] = new_samples[index];
+ }
+ break;
+ case 2: {
+ // 2 channel is played as is
+ samples = std::move(new_samples);
+ break;
+ }
+ default:
+ LOG_CRITICAL(Audio, "Unimplemented channel_count={}", info.channel_count);
+ UNREACHABLE();
+ break;
+ }
+
+ is_refresh_pending = false;
+}
+
+static constexpr s16 ClampToS16(s32 value) {
+ return static_cast<s16>(std::clamp(value, -32768, 32767));
+}
+
+void AudioRenderer::QueueMixedBuffer(Buffer::Tag tag) {
+ constexpr size_t BUFFER_SIZE{512};
+ std::vector<s16> buffer(BUFFER_SIZE * stream->GetNumChannels());
+
+ for (auto& voice : voices) {
+ if (!voice.IsPlaying()) {
+ continue;
+ }
+
+ size_t offset{};
+ s64 samples_remaining{BUFFER_SIZE};
+ while (samples_remaining > 0) {
+ const std::vector<s16> samples{voice.DequeueSamples(samples_remaining)};
+
+ if (samples.empty()) {
+ break;
+ }
+
+ samples_remaining -= samples.size();
+
+ for (const auto& sample : samples) {
+ const s32 buffer_sample{buffer[offset]};
+ buffer[offset++] =
+ ClampToS16(buffer_sample + static_cast<s32>(sample * voice.GetInfo().volume));
+ }
+ }
+ }
+ audio_core->QueueBuffer(stream, tag, std::move(buffer));
+}
+
+void AudioRenderer::ReleaseAndQueueBuffers() {
+ const auto released_buffers{audio_core->GetTagsAndReleaseBuffers(stream, 2)};
+ for (const auto& tag : released_buffers) {
+ QueueMixedBuffer(tag);
+ }
+}
+
+} // 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 @@
+// Copyright 2018 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <array>
+#include <memory>
+#include <vector>
+
+#include "audio_core/audio_out.h"
+#include "audio_core/codec.h"
+#include "audio_core/stream.h"
+#include "common/common_types.h"
+#include "common/swap.h"
+#include "core/hle/kernel/event.h"
+
+namespace AudioCore {
+
+enum class PlayState : u8 {
+ Started = 0,
+ Stopped = 1,
+ Paused = 2,
+};
+
+struct AudioRendererParameter {
+ u32_le sample_rate;
+ u32_le sample_count;
+ u32_le unknown_8;
+ u32_le unknown_c;
+ u32_le voice_count;
+ u32_le sink_count;
+ u32_le effect_count;
+ u32_le unknown_1c;
+ u8 unknown_20;
+ INSERT_PADDING_BYTES(3);
+ u32_le splitter_count;
+ u32_le unknown_2c;
+ INSERT_PADDING_WORDS(1);
+ u32_le revision;
+};
+static_assert(sizeof(AudioRendererParameter) == 52, "AudioRendererParameter is an invalid size");
+
+enum class MemoryPoolStates : u32 { // Should be LE
+ Invalid = 0x0,
+ Unknown = 0x1,
+ RequestDetach = 0x2,
+ Detached = 0x3,
+ RequestAttach = 0x4,
+ Attached = 0x5,
+ Released = 0x6,
+};
+
+struct MemoryPoolEntry {
+ MemoryPoolStates state;
+ u32_le unknown_4;
+ u32_le unknown_8;
+ u32_le unknown_c;
+};
+static_assert(sizeof(MemoryPoolEntry) == 0x10, "MemoryPoolEntry has wrong size");
+
+struct MemoryPoolInfo {
+ u64_le pool_address;
+ u64_le pool_size;
+ MemoryPoolStates pool_state;
+ INSERT_PADDING_WORDS(3); // Unknown
+};
+static_assert(sizeof(MemoryPoolInfo) == 0x20, "MemoryPoolInfo has wrong size");
+struct BiquadFilter {
+ u8 enable;
+ INSERT_PADDING_BYTES(1);
+ std::array<s16_le, 3> numerator;
+ std::array<s16_le, 2> denominator;
+};
+static_assert(sizeof(BiquadFilter) == 0xc, "BiquadFilter has wrong size");
+
+struct WaveBuffer {
+ u64_le buffer_addr;
+ u64_le buffer_sz;
+ s32_le start_sample_offset;
+ s32_le end_sample_offset;
+ u8 is_looping;
+ u8 end_of_stream;
+ u8 sent_to_server;
+ INSERT_PADDING_BYTES(5);
+ u64 context_addr;
+ u64 context_sz;
+ INSERT_PADDING_BYTES(8);
+};
+static_assert(sizeof(WaveBuffer) == 0x38, "WaveBuffer has wrong size");
+
+struct VoiceInfo {
+ u32_le id;
+ u32_le node_id;
+ u8 is_new;
+ u8 is_in_use;
+ PlayState play_state;
+ u8 sample_format;
+ u32_le sample_rate;
+ u32_le priority;
+ u32_le sorting_order;
+ u32_le channel_count;
+ float_le pitch;
+ float_le volume;
+ std::array<BiquadFilter, 2> biquad_filter;
+ u32_le wave_buffer_count;
+ u32_le wave_buffer_head;
+ INSERT_PADDING_WORDS(1);
+ u64_le additional_params_addr;
+ u64_le additional_params_sz;
+ u32_le mix_id;
+ u32_le splitter_info_id;
+ std::array<WaveBuffer, 4> wave_buffer;
+ std::array<u32_le, 6> voice_channel_resource_ids;
+ INSERT_PADDING_BYTES(24);
+};
+static_assert(sizeof(VoiceInfo) == 0x170, "VoiceInfo is wrong size");
+
+struct VoiceOutStatus {
+ u64_le played_sample_count;
+ u32_le wave_buffer_consumed;
+ u32_le voice_drops_count;
+};
+static_assert(sizeof(VoiceOutStatus) == 0x10, "VoiceOutStatus has wrong size");
+
+struct UpdateDataHeader {
+ UpdateDataHeader() {}
+
+ explicit UpdateDataHeader(const AudioRendererParameter& config) {
+ revision = Common::MakeMagic('R', 'E', 'V', '4'); // 5.1.0 Revision
+ behavior_size = 0xb0;
+ memory_pools_size = (config.effect_count + (config.voice_count * 4)) * 0x10;
+ voices_size = config.voice_count * 0x10;
+ voice_resource_size = 0x0;
+ effects_size = config.effect_count * 0x10;
+ mixes_size = 0x0;
+ sinks_size = config.sink_count * 0x20;
+ performance_manager_size = 0x10;
+ total_size = sizeof(UpdateDataHeader) + behavior_size + memory_pools_size + voices_size +
+ effects_size + sinks_size + performance_manager_size;
+ }
+
+ u32_le revision;
+ u32_le behavior_size;
+ u32_le memory_pools_size;
+ u32_le voices_size;
+ u32_le voice_resource_size;
+ u32_le effects_size;
+ u32_le mixes_size;
+ u32_le sinks_size;
+ u32_le performance_manager_size;
+ INSERT_PADDING_WORDS(6);
+ u32_le total_size;
+};
+static_assert(sizeof(UpdateDataHeader) == 0x40, "UpdateDataHeader has wrong size");
+
+class AudioRenderer {
+public:
+ AudioRenderer(AudioRendererParameter params, Kernel::SharedPtr<Kernel::Event> buffer_event);
+ std::vector<u8> UpdateAudioRenderer(const std::vector<u8>& input_params);
+ void QueueMixedBuffer(Buffer::Tag tag);
+ void ReleaseAndQueueBuffers();
+
+private:
+ class VoiceState {
+ public:
+ bool IsPlaying() const {
+ return is_in_use && info.play_state == PlayState::Started;
+ }
+
+ const VoiceOutStatus& GetOutStatus() const {
+ return out_status;
+ }
+
+ const VoiceInfo& GetInfo() const {
+ return info;
+ }
+
+ VoiceInfo& Info() {
+ return info;
+ }
+
+ void SetWaveIndex(size_t index);
+ std::vector<s16> DequeueSamples(size_t sample_count);
+ void UpdateState();
+ void RefreshBuffer();
+
+ private:
+ bool is_in_use{};
+ bool is_refresh_pending{};
+ size_t wave_index{};
+ size_t offset{};
+ Codec::ADPCMState adpcm_state{};
+ std::vector<s16> samples;
+ VoiceOutStatus out_status{};
+ VoiceInfo info{};
+ };
+
+ AudioRendererParameter worker_params;
+ Kernel::SharedPtr<Kernel::Event> buffer_event;
+ std::vector<VoiceState> voices;
+ std::unique_ptr<AudioCore::AudioOut> audio_core;
+ AudioCore::StreamPtr stream;
+};
+
+} // 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 {
public:
using Tag = u64;
- Buffer(Tag tag, std::vector<u8>&& data) : tag{tag}, data{std::move(data)} {}
+ Buffer(Tag tag, std::vector<s16>&& samples) : tag{tag}, samples{std::move(samples)} {}
/// Returns the raw audio data for the buffer
- const std::vector<u8>& GetData() const {
- return data;
+ std::vector<s16>& Samples() {
+ return samples;
+ }
+
+ /// Returns the raw audio data for the buffer
+ const std::vector<s16>& GetSamples() const {
+ return samples;
}
/// Returns the buffer tag, this is provided by the game to the audout service
@@ -32,7 +37,7 @@ public:
private:
Tag tag;
- std::vector<u8> data;
+ std::vector<s16> samples;
};
using 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 @@
+// Copyright 2018 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include <algorithm>
+
+#include "audio_core/codec.h"
+
+namespace AudioCore::Codec {
+
+std::vector<s16> DecodeADPCM(const u8* const data, size_t size, const ADPCM_Coeff& coeff,
+ ADPCMState& state) {
+ // GC-ADPCM with scale factor and variable coefficients.
+ // Frames are 8 bytes long containing 14 samples each.
+ // Samples are 4 bits (one nibble) long.
+
+ constexpr size_t FRAME_LEN = 8;
+ constexpr size_t SAMPLES_PER_FRAME = 14;
+ constexpr std::array<int, 16> SIGNED_NIBBLES = {
+ {0, 1, 2, 3, 4, 5, 6, 7, -8, -7, -6, -5, -4, -3, -2, -1}};
+
+ const size_t sample_count = (size / FRAME_LEN) * SAMPLES_PER_FRAME;
+ const size_t ret_size =
+ sample_count % 2 == 0 ? sample_count : sample_count + 1; // Ensure multiple of two.
+ std::vector<s16> ret(ret_size);
+
+ int yn1 = state.yn1, yn2 = state.yn2;
+
+ const size_t NUM_FRAMES =
+ (sample_count + (SAMPLES_PER_FRAME - 1)) / SAMPLES_PER_FRAME; // Round up.
+ for (size_t framei = 0; framei < NUM_FRAMES; framei++) {
+ const int frame_header = data[framei * FRAME_LEN];
+ const int scale = 1 << (frame_header & 0xF);
+ const int idx = (frame_header >> 4) & 0x7;
+
+ // Coefficients are fixed point with 11 bits fractional part.
+ const int coef1 = coeff[idx * 2 + 0];
+ const int coef2 = coeff[idx * 2 + 1];
+
+ // Decodes an audio sample. One nibble produces one sample.
+ const auto decode_sample = [&](const int nibble) -> s16 {
+ const int xn = nibble * scale;
+ // We first transform everything into 11 bit fixed point, perform the second order
+ // digital filter, then transform back.
+ // 0x400 == 0.5 in 11 bit fixed point.
+ // Filter: y[n] = x[n] + 0.5 + c1 * y[n-1] + c2 * y[n-2]
+ int val = ((xn << 11) + 0x400 + coef1 * yn1 + coef2 * yn2) >> 11;
+ // Clamp to output range.
+ val = std::clamp<s32>(val, -32768, 32767);
+ // Advance output feedback.
+ yn2 = yn1;
+ yn1 = val;
+ return static_cast<s16>(val);
+ };
+
+ size_t outputi = framei * SAMPLES_PER_FRAME;
+ size_t datai = framei * FRAME_LEN + 1;
+ for (size_t i = 0; i < SAMPLES_PER_FRAME && outputi < sample_count; i += 2) {
+ const s16 sample1 = decode_sample(SIGNED_NIBBLES[data[datai] >> 4]);
+ ret[outputi] = sample1;
+ outputi++;
+
+ const s16 sample2 = decode_sample(SIGNED_NIBBLES[data[datai] & 0xF]);
+ ret[outputi] = sample2;
+ outputi++;
+
+ datai++;
+ }
+ }
+
+ state.yn1 = yn1;
+ state.yn2 = yn2;
+
+ return ret;
+}
+
+} // 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 @@
+// Copyright 2018 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <array>
+#include <vector>
+
+#include "common/common_types.h"
+
+namespace AudioCore::Codec {
+
+enum class PcmFormat : u32 {
+ Invalid = 0,
+ Int8 = 1,
+ Int16 = 2,
+ Int24 = 3,
+ Int32 = 4,
+ PcmFloat = 5,
+ Adpcm = 6,
+};
+
+/// See: Codec::DecodeADPCM
+struct ADPCMState {
+ // Two historical samples from previous processed buffer,
+ // required for ADPCM decoding
+ s16 yn1; ///< y[n-1]
+ s16 yn2; ///< y[n-2]
+};
+
+using ADPCM_Coeff = std::array<s16, 16>;
+
+/**
+ * @param data Pointer to buffer that contains ADPCM data to decode
+ * @param size Size of buffer in bytes
+ * @param coeff ADPCM coefficients
+ * @param state ADPCM state, this is updated with new state
+ * @return Decoded stereo signed PCM16 data, sample_count in length
+ */
+std::vector<s16> DecodeADPCM(const u8* const data, size_t size, const ADPCM_Coeff& coeff,
+ ADPCMState& state);
+
+}; // 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 {
class SinkStreamImpl final : public SinkStream {
public:
- SinkStreamImpl(cubeb* ctx, cubeb_devid output_device) : ctx{ctx} {
- cubeb_stream_params params;
- params.rate = 48000;
- params.channels = GetNumChannels();
+ SinkStreamImpl(cubeb* ctx, u32 sample_rate, u32 num_channels_, cubeb_devid output_device,
+ const std::string& name)
+ : ctx{ctx}, num_channels{num_channels_} {
+
+ if (num_channels == 6) {
+ // 6-channel audio does not seem to work with cubeb + SDL, so we downsample this to 2
+ // channel for now
+ is_6_channel = true;
+ num_channels = 2;
+ }
+
+ cubeb_stream_params params{};
+ params.rate = sample_rate;
+ params.channels = num_channels;
params.format = CUBEB_SAMPLE_S16NE;
- params.layout = CUBEB_LAYOUT_STEREO;
+ params.layout = num_channels == 1 ? CUBEB_LAYOUT_MONO : CUBEB_LAYOUT_STEREO;
- u32 minimum_latency = 0;
+ u32 minimum_latency{};
if (cubeb_get_min_latency(ctx, &params, &minimum_latency) != CUBEB_OK) {
LOG_CRITICAL(Audio_Sink, "Error getting minimum latency");
}
- if (cubeb_stream_init(ctx, &stream_backend, "yuzu Audio Output", nullptr, nullptr,
- output_device, &params, std::max(512u, minimum_latency),
+ if (cubeb_stream_init(ctx, &stream_backend, name.c_str(), nullptr, nullptr, output_device,
+ &params, std::max(512u, minimum_latency),
&SinkStreamImpl::DataCallback, &SinkStreamImpl::StateCallback,
this) != CUBEB_OK) {
LOG_CRITICAL(Audio_Sink, "Error initializing cubeb stream");
@@ -51,33 +61,29 @@ public:
cubeb_stream_destroy(stream_backend);
}
- void EnqueueSamples(u32 num_channels, const s16* samples, size_t sample_count) override {
+ void EnqueueSamples(u32 num_channels, const std::vector<s16>& samples) override {
if (!ctx) {
return;
}
- queue.reserve(queue.size() + sample_count * GetNumChannels());
+ queue.reserve(queue.size() + samples.size() * GetNumChannels());
- if (num_channels == 2) {
- // Copy as-is
- std::copy(samples, samples + sample_count * GetNumChannels(),
- std::back_inserter(queue));
- } else if (num_channels == 6) {
+ if (is_6_channel) {
// Downsample 6 channels to 2
- const size_t sample_count_copy_size = sample_count * num_channels * 2;
+ const size_t sample_count_copy_size = samples.size() * 2;
queue.reserve(sample_count_copy_size);
- for (size_t i = 0; i < sample_count * num_channels; i += num_channels) {
+ for (size_t i = 0; i < samples.size(); i += num_channels) {
queue.push_back(samples[i]);
queue.push_back(samples[i + 1]);
}
} else {
- ASSERT_MSG(false, "Unimplemented");
+ // Copy as-is
+ std::copy(samples.begin(), samples.end(), std::back_inserter(queue));
}
}
u32 GetNumChannels() const {
- // Only support 2-channel stereo output for now
- return 2;
+ return num_channels;
}
private:
@@ -85,6 +91,8 @@ private:
cubeb* ctx{};
cubeb_stream* stream_backend{};
+ u32 num_channels{};
+ bool is_6_channel{};
std::vector<s16> queue;
@@ -129,8 +137,10 @@ CubebSink::~CubebSink() {
cubeb_destroy(ctx);
}
-SinkStream& CubebSink::AcquireSinkStream(u32 sample_rate, u32 num_channels) {
- sink_streams.push_back(std::make_unique<SinkStreamImpl>(ctx, output_device));
+SinkStream& CubebSink::AcquireSinkStream(u32 sample_rate, u32 num_channels,
+ const std::string& name) {
+ sink_streams.push_back(
+ std::make_unique<SinkStreamImpl>(ctx, sample_rate, num_channels, output_device, name));
return *sink_streams.back();
}
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:
explicit CubebSink(std::string device_id);
~CubebSink() override;
- SinkStream& AcquireSinkStream(u32 sample_rate, u32 num_channels) override;
+ SinkStream& AcquireSinkStream(u32 sample_rate, u32 num_channels,
+ const std::string& name) override;
private:
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:
explicit NullSink(std::string){};
~NullSink() override = default;
- SinkStream& AcquireSinkStream(u32 /*sample_rate*/, u32 /*num_channels*/) override {
+ SinkStream& AcquireSinkStream(u32 /*sample_rate*/, u32 /*num_channels*/,
+ const std::string& /*name*/) override {
return null_sink_stream;
}
private:
struct NullSinkStreamImpl final : SinkStream {
- void EnqueueSamples(u32 /*num_channels*/, const s16* /*samples*/,
- size_t /*sample_count*/) override {}
+ void EnqueueSamples(u32 /*num_channels*/, const std::vector<s16>& /*samples*/) override {}
} null_sink_stream;
};
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 @@
#pragma once
#include <memory>
+#include <string>
#include "audio_core/sink_stream.h"
#include "common/common_types.h"
@@ -21,7 +22,8 @@ constexpr char auto_device_name[] = "auto";
class Sink {
public:
virtual ~Sink() = default;
- virtual SinkStream& AcquireSinkStream(u32 sample_rate, u32 num_channels) = 0;
+ virtual SinkStream& AcquireSinkStream(u32 sample_rate, u32 num_channels,
+ const std::string& name) = 0;
};
using 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 @@
#pragma once
#include <memory>
+#include <vector>
#include "common/common_types.h"
@@ -22,9 +23,8 @@ public:
* Feed stereo samples to sink.
* @param num_channels Number of channels used.
* @param samples Samples in interleaved stereo PCM16 format.
- * @param sample_count Number of samples.
*/
- virtual void EnqueueSamples(u32 num_channels, const s16* samples, size_t sample_count) = 0;
+ virtual void EnqueueSamples(u32 num_channels, const std::vector<s16>& samples) = 0;
};
using 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 {
return {};
}
-u32 Stream::GetSampleSize() const {
- return GetNumChannels() * 2;
-}
-
Stream::Stream(u32 sample_rate, Format format, ReleaseCallback&& release_callback,
- SinkStream& sink_stream)
+ SinkStream& sink_stream, std::string&& name_)
: sample_rate{sample_rate}, format{format}, release_callback{std::move(release_callback)},
- sink_stream{sink_stream} {
+ sink_stream{sink_stream}, name{std::move(name_)} {
release_event = CoreTiming::RegisterEvent(
- "Stream::Release", [this](u64 userdata, int cycles_late) { ReleaseActiveBuffer(); });
+ name, [this](u64 userdata, int cycles_late) { ReleaseActiveBuffer(); });
}
void Stream::Play() {
@@ -55,17 +51,15 @@ void Stream::Stop() {
}
s64 Stream::GetBufferReleaseCycles(const Buffer& buffer) const {
- const size_t num_samples{buffer.GetData().size() / GetSampleSize()};
+ const size_t num_samples{buffer.GetSamples().size() / GetNumChannels()};
return CoreTiming::usToCycles((static_cast<u64>(num_samples) * 1000000) / sample_rate);
}
-static std::vector<s16> GetVolumeAdjustedSamples(const std::vector<u8>& data) {
- std::vector<s16> samples(data.size() / sizeof(s16));
- std::memcpy(samples.data(), data.data(), data.size());
+static void VolumeAdjustSamples(std::vector<s16>& samples) {
const float volume{std::clamp(Settings::values.volume, 0.0f, 1.0f)};
if (volume == 1.0f) {
- return samples;
+ return;
}
// 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) {
for (auto& sample : samples) {
sample = static_cast<s16>(sample * volume_scale_factor);
}
-
- return samples;
}
void Stream::PlayNextBuffer() {
@@ -96,14 +88,14 @@ void Stream::PlayNextBuffer() {
active_buffer = queued_buffers.front();
queued_buffers.pop();
- const size_t sample_count{active_buffer->GetData().size() / GetSampleSize()};
- sink_stream.EnqueueSamples(
- GetNumChannels(), GetVolumeAdjustedSamples(active_buffer->GetData()).data(), sample_count);
+ VolumeAdjustSamples(active_buffer->Samples());
+ sink_stream.EnqueueSamples(GetNumChannels(), active_buffer->GetSamples());
CoreTiming::ScheduleEventThreadsafe(GetBufferReleaseCycles(*active_buffer), release_event, {});
}
void Stream::ReleaseActiveBuffer() {
+ ASSERT(active_buffer);
released_buffers.push(std::move(active_buffer));
release_callback();
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 @@
#include <functional>
#include <memory>
+#include <string>
#include <vector>
#include <queue>
@@ -33,7 +34,7 @@ public:
using ReleaseCallback = std::function<void()>;
Stream(u32 sample_rate, Format format, ReleaseCallback&& release_callback,
- SinkStream& sink_stream);
+ SinkStream& sink_stream, std::string&& name_);
/// Plays the audio stream
void Play();
@@ -68,9 +69,6 @@ public:
/// Gets the number of channels
u32 GetNumChannels() const;
- /// Gets the sample size in bytes
- u32 GetSampleSize() const;
-
private:
/// Current state of the stream
enum class State {
@@ -96,6 +94,7 @@ private:
std::queue<BufferPtr> queued_buffers; ///< Buffers queued to be played in the stream
std::queue<BufferPtr> released_buffers; ///< Buffers recently released from the stream
SinkStream& sink_stream; ///< Output sink for the stream
+ std::string name; ///< Name of the stream, must be unique
};
using StreamPtr = std::shared_ptr<Stream>;
diff --git a/src/common/logging/backend.cpp b/src/common/logging/backend.cpp
index 34dec06fe..355abd682 100644
--- a/src/common/logging/backend.cpp
+++ b/src/common/logging/backend.cpp
@@ -168,6 +168,7 @@ void FileBackend::Write(const Entry& entry) {
SUB(Service, AM) \
SUB(Service, AOC) \
SUB(Service, APM) \
+ SUB(Service, ARP) \
SUB(Service, BCAT) \
SUB(Service, BPC) \
SUB(Service, BTM) \
@@ -199,6 +200,7 @@ void FileBackend::Write(const Entry& entry) {
SUB(Service, SPL) \
SUB(Service, SSL) \
SUB(Service, Time) \
+ SUB(Service, USB) \
SUB(Service, VI) \
SUB(Service, WLAN) \
CLS(HW) \
diff --git a/src/common/logging/log.h b/src/common/logging/log.h
index dd5c9e6be..a889ebefa 100644
--- a/src/common/logging/log.h
+++ b/src/common/logging/log.h
@@ -54,6 +54,7 @@ enum class Class : ClassType {
Service_AM, ///< The AM (Applet manager) service
Service_AOC, ///< The AOC (AddOn Content) service
Service_APM, ///< The APM (Performance) service
+ Service_ARP, ///< The ARP service
Service_Audio, ///< The Audio (Audio control) service
Service_BCAT, ///< The BCAT service
Service_BPC, ///< The BPC service
@@ -86,6 +87,7 @@ enum class Class : ClassType {
Service_SPL, ///< The SPL service
Service_SSL, ///< The SSL service
Service_Time, ///< The time service
+ Service_USB, ///< The USB (Universal Serial Bus) service
Service_VI, ///< The VI (Video interface) service
Service_WLAN, ///< The WLAN (Wireless local area network) service
HW, ///< Low-level hardware emulation
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index 28de22398..0abf7edc1 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -134,6 +134,8 @@ add_library(core STATIC
hle/service/apm/apm.h
hle/service/apm/interface.cpp
hle/service/apm/interface.h
+ hle/service/arp/arp.cpp
+ hle/service/arp/arp.h
hle/service/audio/audctl.cpp
hle/service/audio/audctl.h
hle/service/audio/auddbg.cpp
@@ -313,6 +315,8 @@ add_library(core STATIC
hle/service/time/interface.h
hle/service/time/time.cpp
hle/service/time/time.h
+ hle/service/usb/usb.cpp
+ hle/service/usb/usb.h
hle/service/vi/vi.cpp
hle/service/vi/vi.h
hle/service/vi/vi_m.cpp
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) {
}
Kernel::Thread* thread = Kernel::GetCurrentThread();
SaveContext(thread->context);
- if (last_bkpt_hit || (num_instructions == 1)) {
+ if (last_bkpt_hit || GDBStub::GetCpuStepFlag()) {
last_bkpt_hit = false;
GDBStub::Break();
GDBStub::SendTrap(thread, 5);
diff --git a/src/core/core.cpp b/src/core/core.cpp
index e51e66550..085ba68d0 100644
--- a/src/core/core.cpp
+++ b/src/core/core.cpp
@@ -18,6 +18,7 @@
#include "core/loader/loader.h"
#include "core/settings.h"
#include "file_sys/vfs_real.h"
+#include "video_core/renderer_base.h"
#include "video_core/video_core.h"
namespace Core {
@@ -61,7 +62,6 @@ System::ResultStatus System::RunLoop(bool tight_loop) {
// execute. Otherwise, get out of the loop function.
if (GDBStub::GetCpuHaltFlag()) {
if (GDBStub::GetCpuStepFlag()) {
- GDBStub::SetCpuStepFlag(false);
tight_loop = false;
} else {
return ResultStatus::Success;
@@ -77,6 +77,10 @@ System::ResultStatus System::RunLoop(bool tight_loop) {
}
}
+ if (GDBStub::IsServerEnabled()) {
+ GDBStub::SetCpuStepFlag(false);
+ }
+
return status;
}
@@ -178,7 +182,6 @@ System::ResultStatus System::Init(EmuWindow& emu_window) {
cpu_cores[index] = std::make_shared<Cpu>(cpu_exclusive_monitor, cpu_barrier, index);
}
- gpu_core = std::make_unique<Tegra::GPU>();
telemetry_session = std::make_unique<Core::TelemetrySession>();
service_manager = std::make_shared<Service::SM::ServiceManager>();
@@ -186,10 +189,13 @@ System::ResultStatus System::Init(EmuWindow& emu_window) {
Service::Init(service_manager);
GDBStub::Init();
- if (!VideoCore::Init(emu_window)) {
+ renderer = VideoCore::CreateRenderer(emu_window);
+ if (!renderer->Init()) {
return ResultStatus::ErrorVideoCore;
}
+ gpu_core = std::make_unique<Tegra::GPU>(renderer->Rasterizer());
+
// Create threads for CPU cores 1-3, and build thread_to_cpu map
// CPU core 0 is run on the main thread
thread_to_cpu[std::this_thread::get_id()] = cpu_cores[0];
@@ -221,7 +227,7 @@ void System::Shutdown() {
perf_results.frametime * 1000.0);
// Shutdown emulation session
- VideoCore::Shutdown();
+ renderer.reset();
GDBStub::Shutdown();
Service::Shutdown();
Kernel::Shutdown();
diff --git a/src/core/core.h b/src/core/core.h
index 4c9967559..c8ca4b247 100644
--- a/src/core/core.h
+++ b/src/core/core.h
@@ -27,6 +27,10 @@ namespace Service::SM {
class ServiceManager;
}
+namespace VideoCore {
+class RendererBase;
+}
+
namespace Core {
class System {
@@ -78,6 +82,17 @@ public:
*/
ResultStatus SingleStep();
+ /**
+ * Invalidate the CPU instruction caches
+ * This function should only be used by GDB Stub to support breakpoints, memory updates and
+ * step/continue commands.
+ */
+ void InvalidateCpuInstructionCaches() {
+ for (auto& cpu : cpu_cores) {
+ cpu->ArmInterface().ClearInstructionCache();
+ }
+ }
+
/// Shutdown the emulated system.
void Shutdown();
@@ -129,11 +144,26 @@ public:
/// Gets a CPU interface to the CPU core with the specified index
Cpu& CpuCore(size_t core_index);
- /// Gets the GPU interface
+ /// Gets a mutable reference to the GPU interface
Tegra::GPU& GPU() {
return *gpu_core;
}
+ /// Gets an immutable reference to the GPU interface.
+ const Tegra::GPU& GPU() const {
+ return *gpu_core;
+ }
+
+ /// Gets a mutable reference to the renderer.
+ VideoCore::RendererBase& Renderer() {
+ return *renderer;
+ }
+
+ /// Gets an immutable reference to the renderer.
+ const VideoCore::RendererBase& Renderer() const {
+ return *renderer;
+ }
+
/// Gets the scheduler for the CPU core that is currently running
Kernel::Scheduler& CurrentScheduler() {
return *CurrentCpuCore().Scheduler();
@@ -197,6 +227,7 @@ private:
/// AppLoader used to load the current executing application
std::unique_ptr<Loader::AppLoader> app_loader;
+ std::unique_ptr<VideoCore::RendererBase> renderer;
std::unique_ptr<Tegra::GPU> gpu_core;
std::shared_ptr<Tegra::DebugContext> debug_context;
Kernel::SharedPtr<Kernel::Process> current_process;
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
ForceExceptionCheck(cycles_into_future);
event_queue.emplace_back(Event{timeout, event_fifo_id++, userdata, event_type});
- std::push_heap(event_queue.begin(), event_queue.end(), std::greater<Event>());
+ std::push_heap(event_queue.begin(), event_queue.end(), std::greater<>());
}
void ScheduleEventThreadsafe(s64 cycles_into_future, const EventType* event_type, u64 userdata) {
@@ -156,7 +156,7 @@ void UnscheduleEvent(const EventType* event_type, u64 userdata) {
// Removing random items breaks the invariant so we have to re-establish it.
if (itr != event_queue.end()) {
event_queue.erase(itr, event_queue.end());
- std::make_heap(event_queue.begin(), event_queue.end(), std::greater<Event>());
+ std::make_heap(event_queue.begin(), event_queue.end(), std::greater<>());
}
}
@@ -167,7 +167,7 @@ void RemoveEvent(const EventType* event_type) {
// Removing random items breaks the invariant so we have to re-establish it.
if (itr != event_queue.end()) {
event_queue.erase(itr, event_queue.end());
- std::make_heap(event_queue.begin(), event_queue.end(), std::greater<Event>());
+ std::make_heap(event_queue.begin(), event_queue.end(), std::greater<>());
}
}
@@ -190,7 +190,7 @@ void MoveEvents() {
for (Event ev; ts_queue.Pop(ev);) {
ev.fifo_order = event_fifo_id++;
event_queue.emplace_back(std::move(ev));
- std::push_heap(event_queue.begin(), event_queue.end(), std::greater<Event>());
+ std::push_heap(event_queue.begin(), event_queue.end(), std::greater<>());
}
}
@@ -205,7 +205,7 @@ void Advance() {
while (!event_queue.empty() && event_queue.front().time <= global_timer) {
Event evt = std::move(event_queue.front());
- std::pop_heap(event_queue.begin(), event_queue.end(), std::greater<Event>());
+ std::pop_heap(event_queue.begin(), event_queue.end(), std::greater<>());
event_queue.pop_back();
evt.type->callback(evt.userdata, static_cast<int>(global_timer - evt.time));
}
@@ -226,8 +226,8 @@ void Idle() {
downcount = 0;
}
-u64 GetGlobalTimeUs() {
- return GetTicks() * 1000000 / BASE_CLOCK_RATE;
+std::chrono::microseconds GetGlobalTimeUs() {
+ return std::chrono::microseconds{GetTicks() * 1000000 / BASE_CLOCK_RATE};
}
int 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 @@
* ScheduleEvent(periodInCycles - cyclesLate, callback, "whatever")
*/
+#include <chrono>
#include <functional>
#include <string>
#include "common/common_types.h"
namespace CoreTiming {
+struct EventType;
+
+using TimedCallback = std::function<void(u64 userdata, int cycles_late)>;
+
/**
* CoreTiming begins at the boundary of timing slice -1. An initial call to Advance() is
* required to end slice -1 and start slice 0 before the first cycle of code is executed.
@@ -30,8 +35,6 @@ namespace CoreTiming {
void Init();
void Shutdown();
-typedef std::function<void(u64 userdata, int cycles_late)> TimedCallback;
-
/**
* This should only be called from the emu thread, if you are calling it any other thread, you are
* doing something evil
@@ -40,8 +43,6 @@ u64 GetTicks();
u64 GetIdleTicks();
void AddTicks(u64 ticks);
-struct EventType;
-
/**
* Returns the event_type identifier. if name is not unique, it will assert.
*/
@@ -86,7 +87,7 @@ void ClearPendingEvents();
void ForceExceptionCheck(s64 cycles);
-u64 GetGlobalTimeUs();
+std::chrono::microseconds GetGlobalTimeUs();
int GetDowncount();
diff --git a/src/core/crypto/aes_util.cpp b/src/core/crypto/aes_util.cpp
index 4690af5f8..a9876c83e 100644
--- a/src/core/crypto/aes_util.cpp
+++ b/src/core/crypto/aes_util.cpp
@@ -3,10 +3,22 @@
// Refer to the license.txt file included.
#include <mbedtls/cipher.h>
+#include "common/assert.h"
+#include "common/logging/log.h"
#include "core/crypto/aes_util.h"
#include "core/crypto/key_manager.h"
namespace Core::Crypto {
+namespace {
+std::vector<u8> CalculateNintendoTweak(size_t sector_id) {
+ std::vector<u8> out(0x10);
+ for (size_t i = 0xF; i <= 0xF; --i) {
+ out[i] = sector_id & 0xFF;
+ sector_id >>= 8;
+ }
+ return out;
+}
+} // Anonymous namespace
static_assert(static_cast<size_t>(Mode::CTR) == static_cast<size_t>(MBEDTLS_CIPHER_AES_128_CTR),
"CTR has incorrect value.");
@@ -56,27 +68,28 @@ void AESCipher<Key, KeySize>::SetIV(std::vector<u8> iv) {
}
template <typename Key, size_t KeySize>
-void AESCipher<Key, KeySize>::Transcode(const u8* src, size_t size, u8* dest, Op op) {
- size_t written = 0;
-
- const auto context = op == Op::Encrypt ? &ctx->encryption_context : &ctx->decryption_context;
+void AESCipher<Key, KeySize>::Transcode(const u8* src, size_t size, u8* dest, Op op) const {
+ auto* const context = op == Op::Encrypt ? &ctx->encryption_context : &ctx->decryption_context;
mbedtls_cipher_reset(context);
+ size_t written = 0;
if (mbedtls_cipher_get_cipher_mode(context) == MBEDTLS_MODE_XTS) {
mbedtls_cipher_update(context, src, size, dest, &written);
- if (written != size)
+ if (written != size) {
LOG_WARNING(Crypto, "Not all data was decrypted requested={:016X}, actual={:016X}.",
size, written);
+ }
} else {
const auto block_size = mbedtls_cipher_get_block_size(context);
for (size_t offset = 0; offset < size; offset += block_size) {
auto length = std::min<size_t>(block_size, size - offset);
mbedtls_cipher_update(context, src + offset, length, dest + offset, &written);
- if (written != length)
+ if (written != length) {
LOG_WARNING(Crypto, "Not all data was decrypted requested={:016X}, actual={:016X}.",
length, written);
+ }
}
}
@@ -97,16 +110,6 @@ void AESCipher<Key, KeySize>::XTSTranscode(const u8* src, size_t size, u8* dest,
}
}
-template <typename Key, size_t KeySize>
-std::vector<u8> AESCipher<Key, KeySize>::CalculateNintendoTweak(size_t sector_id) {
- std::vector<u8> out(0x10);
- for (size_t i = 0xF; i <= 0xF; --i) {
- out[i] = sector_id & 0xFF;
- sector_id >>= 8;
- }
- return out;
-}
-
template class AESCipher<Key128>;
template class AESCipher<Key256>;
} // namespace Core::Crypto \ No newline at end of file
diff --git a/src/core/crypto/aes_util.h b/src/core/crypto/aes_util.h
index 5b0b02738..8ce9d6612 100644
--- a/src/core/crypto/aes_util.h
+++ b/src/core/crypto/aes_util.h
@@ -7,7 +7,7 @@
#include <memory>
#include <type_traits>
#include <vector>
-#include "common/assert.h"
+#include "common/common_types.h"
#include "core/file_sys/vfs.h"
namespace Core::Crypto {
@@ -38,15 +38,19 @@ public:
void SetIV(std::vector<u8> iv);
template <typename Source, typename Dest>
- void Transcode(const Source* src, size_t size, Dest* dest, Op op) {
+ void Transcode(const Source* src, size_t size, Dest* dest, Op op) const {
+ static_assert(std::is_trivially_copyable_v<Source> && std::is_trivially_copyable_v<Dest>,
+ "Transcode source and destination types must be trivially copyable.");
Transcode(reinterpret_cast<const u8*>(src), size, reinterpret_cast<u8*>(dest), op);
}
- void Transcode(const u8* src, size_t size, u8* dest, Op op);
+ void Transcode(const u8* src, size_t size, u8* dest, Op op) const;
template <typename Source, typename Dest>
void XTSTranscode(const Source* src, size_t size, Dest* dest, size_t sector_id,
size_t sector_size, Op op) {
+ static_assert(std::is_trivially_copyable_v<Source> && std::is_trivially_copyable_v<Dest>,
+ "XTSTranscode source and destination types must be trivially copyable.");
XTSTranscode(reinterpret_cast<const u8*>(src), size, reinterpret_cast<u8*>(dest), sector_id,
sector_size, op);
}
@@ -56,7 +60,5 @@ public:
private:
std::unique_ptr<CipherContext> ctx;
-
- static std::vector<u8> CalculateNintendoTweak(size_t sector_id);
};
} // namespace Core::Crypto
diff --git a/src/core/crypto/encryption_layer.h b/src/core/crypto/encryption_layer.h
index 71bca1f23..7f05af9b4 100644
--- a/src/core/crypto/encryption_layer.h
+++ b/src/core/crypto/encryption_layer.h
@@ -4,6 +4,7 @@
#pragma once
+#include "common/common_types.h"
#include "core/file_sys/vfs.h"
namespace Core::Crypto {
diff --git a/src/core/crypto/key_manager.cpp b/src/core/crypto/key_manager.cpp
index 678ac5752..fc45e7ab5 100644
--- a/src/core/crypto/key_manager.cpp
+++ b/src/core/crypto/key_manager.cpp
@@ -2,19 +2,16 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
+#include <algorithm>
#include <array>
#include <fstream>
#include <locale>
#include <sstream>
#include <string_view>
-#include <mbedtls/sha256.h>
-#include "common/assert.h"
#include "common/common_paths.h"
#include "common/file_util.h"
-#include "common/logging/log.h"
#include "core/crypto/key_manager.h"
#include "core/settings.h"
-#include "key_manager.h"
namespace Core::Crypto {
@@ -66,8 +63,7 @@ KeyManager::KeyManager() {
AttemptLoadKeyFile(yuzu_keys_dir, hactool_keys_dir, "title.keys", true);
}
-void KeyManager::LoadFromFile(std::string_view filename_, bool is_title_keys) {
- const auto filename = std::string(filename_);
+void KeyManager::LoadFromFile(const std::string& filename, bool is_title_keys) {
std::ifstream file(filename);
if (!file.is_open())
return;
@@ -107,11 +103,8 @@ void KeyManager::LoadFromFile(std::string_view filename_, bool is_title_keys) {
}
}
-void KeyManager::AttemptLoadKeyFile(std::string_view dir1_, std::string_view dir2_,
- std::string_view filename_, bool title) {
- const std::string dir1(dir1_);
- const std::string dir2(dir2_);
- const std::string filename(filename_);
+void KeyManager::AttemptLoadKeyFile(const std::string& dir1, const std::string& dir2,
+ const std::string& filename, bool title) {
if (FileUtil::Exists(dir1 + DIR_SEP + filename))
LoadFromFile(dir1 + DIR_SEP + filename, title);
else if (FileUtil::Exists(dir2 + DIR_SEP + filename))
diff --git a/src/core/crypto/key_manager.h b/src/core/crypto/key_manager.h
index 03152a12c..c4c53cefc 100644
--- a/src/core/crypto/key_manager.h
+++ b/src/core/crypto/key_manager.h
@@ -5,6 +5,7 @@
#pragma once
#include <array>
+#include <string>
#include <type_traits>
#include <unordered_map>
#include <vector>
@@ -109,9 +110,9 @@ private:
std::unordered_map<KeyIndex<S256KeyType>, Key256> s256_keys;
bool dev_mode;
- void LoadFromFile(std::string_view filename, bool is_title_keys);
- void AttemptLoadKeyFile(std::string_view dir1, std::string_view dir2, std::string_view filename,
- bool title);
+ void LoadFromFile(const std::string& filename, bool is_title_keys);
+ void AttemptLoadKeyFile(const std::string& dir1, const std::string& dir2,
+ const std::string& filename, bool title);
static const std::unordered_map<std::string, KeyIndex<S128KeyType>> s128_file_id;
static const std::unordered_map<std::string, KeyIndex<S256KeyType>> s256_file_id;
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 @@
#include "core/loader/loader.h"
#include "core/memory.h"
-const int GDB_BUFFER_SIZE = 10000;
+namespace GDBStub {
+namespace {
+constexpr int GDB_BUFFER_SIZE = 10000;
-const char GDB_STUB_START = '$';
-const char GDB_STUB_END = '#';
-const char GDB_STUB_ACK = '+';
-const char GDB_STUB_NACK = '-';
+constexpr char GDB_STUB_START = '$';
+constexpr char GDB_STUB_END = '#';
+constexpr char GDB_STUB_ACK = '+';
+constexpr char GDB_STUB_NACK = '-';
#ifndef SIGTRAP
-const u32 SIGTRAP = 5;
+constexpr u32 SIGTRAP = 5;
#endif
#ifndef SIGTERM
-const u32 SIGTERM = 15;
+constexpr u32 SIGTERM = 15;
#endif
#ifndef MSG_WAITALL
-const u32 MSG_WAITALL = 8;
+constexpr u32 MSG_WAITALL = 8;
#endif
-const u32 LR_REGISTER = 30;
-const u32 SP_REGISTER = 31;
-const u32 PC_REGISTER = 32;
-const u32 CPSR_REGISTER = 33;
-const u32 UC_ARM64_REG_Q0 = 34;
-const u32 FPSCR_REGISTER = 66;
+constexpr u32 LR_REGISTER = 30;
+constexpr u32 SP_REGISTER = 31;
+constexpr u32 PC_REGISTER = 32;
+constexpr u32 CPSR_REGISTER = 33;
+constexpr u32 UC_ARM64_REG_Q0 = 34;
+constexpr u32 FPSCR_REGISTER = 66;
// TODO/WiP - Used while working on support for FPU
-const u32 TODO_DUMMY_REG_997 = 997;
-const u32 TODO_DUMMY_REG_998 = 998;
+constexpr u32 TODO_DUMMY_REG_997 = 997;
+constexpr u32 TODO_DUMMY_REG_998 = 998;
// For sample XML files see the GDB source /gdb/features
// GDB also wants the l character at the start
// This XML defines what the registers are for this specific ARM device
-static const char* target_xml =
+constexpr char target_xml[] =
R"(l<?xml version="1.0"?>
<!DOCTYPE target SYSTEM "gdb-target.dtd">
<target version="1.0">
@@ -140,30 +142,28 @@ static const char* target_xml =
</target>
)";
-namespace GDBStub {
-
-static int gdbserver_socket = -1;
+int gdbserver_socket = -1;
-static u8 command_buffer[GDB_BUFFER_SIZE];
-static u32 command_length;
+u8 command_buffer[GDB_BUFFER_SIZE];
+u32 command_length;
-static u32 latest_signal = 0;
-static bool memory_break = false;
+u32 latest_signal = 0;
+bool memory_break = false;
-static Kernel::Thread* current_thread = nullptr;
-static u32 current_core = 0;
+Kernel::Thread* current_thread = nullptr;
+u32 current_core = 0;
// Binding to a port within the reserved ports range (0-1023) requires root permissions,
// so default to a port outside of that range.
-static u16 gdbstub_port = 24689;
+u16 gdbstub_port = 24689;
-static bool halt_loop = true;
-static bool step_loop = false;
-static bool send_trap = false;
+bool halt_loop = true;
+bool step_loop = false;
+bool send_trap = false;
// If set to false, the server will never be started and no
// gdbstub-related functions will be executed.
-static std::atomic<bool> server_enabled(false);
+std::atomic<bool> server_enabled(false);
#ifdef _WIN32
WSADATA InitData;
@@ -171,23 +171,26 @@ WSADATA InitData;
struct Breakpoint {
bool active;
- PAddr addr;
+ VAddr addr;
u64 len;
+ std::array<u8, 4> inst;
};
-static std::map<u64, Breakpoint> breakpoints_execute;
-static std::map<u64, Breakpoint> breakpoints_read;
-static std::map<u64, Breakpoint> breakpoints_write;
+using BreakpointMap = std::map<VAddr, Breakpoint>;
+BreakpointMap breakpoints_execute;
+BreakpointMap breakpoints_read;
+BreakpointMap breakpoints_write;
struct Module {
std::string name;
- PAddr beg;
- PAddr end;
+ VAddr beg;
+ VAddr end;
};
-static std::vector<Module> modules;
+std::vector<Module> modules;
+} // Anonymous namespace
-void RegisterModule(std::string name, PAddr beg, PAddr end, bool add_elf_ext) {
+void RegisterModule(std::string name, VAddr beg, VAddr end, bool add_elf_ext) {
Module module;
if (add_elf_ext) {
Common::SplitPath(name, nullptr, &module.name, nullptr);
@@ -418,11 +421,11 @@ static u8 CalculateChecksum(const u8* buffer, size_t length) {
}
/**
- * Get the list of breakpoints for a given breakpoint type.
+ * Get the map of breakpoints for a given breakpoint type.
*
- * @param type Type of breakpoint list.
+ * @param type Type of breakpoint map.
*/
-static std::map<u64, Breakpoint>& GetBreakpointList(BreakpointType type) {
+static BreakpointMap& GetBreakpointMap(BreakpointType type) {
switch (type) {
case BreakpointType::Execute:
return breakpoints_execute;
@@ -441,20 +444,24 @@ static std::map<u64, Breakpoint>& GetBreakpointList(BreakpointType type) {
* @param type Type of breakpoint.
* @param addr Address of breakpoint.
*/
-static void RemoveBreakpoint(BreakpointType type, PAddr addr) {
- std::map<u64, Breakpoint>& p = GetBreakpointList(type);
+static void RemoveBreakpoint(BreakpointType type, VAddr addr) {
+ BreakpointMap& p = GetBreakpointMap(type);
- auto bp = p.find(static_cast<u64>(addr));
- if (bp != p.end()) {
- LOG_DEBUG(Debug_GDBStub, "gdb: removed a breakpoint: {:016X} bytes at {:016X} of type {}",
- bp->second.len, bp->second.addr, static_cast<int>(type));
- p.erase(static_cast<u64>(addr));
+ const auto bp = p.find(addr);
+ if (bp == p.end()) {
+ return;
}
+
+ LOG_DEBUG(Debug_GDBStub, "gdb: removed a breakpoint: {:016X} bytes at {:016X} of type {}",
+ bp->second.len, bp->second.addr, static_cast<int>(type));
+ Memory::WriteBlock(bp->second.addr, bp->second.inst.data(), bp->second.inst.size());
+ Core::System::GetInstance().InvalidateCpuInstructionCaches();
+ p.erase(addr);
}
-BreakpointAddress GetNextBreakpointFromAddress(PAddr addr, BreakpointType type) {
- std::map<u64, Breakpoint>& p = GetBreakpointList(type);
- auto next_breakpoint = p.lower_bound(static_cast<u64>(addr));
+BreakpointAddress GetNextBreakpointFromAddress(VAddr addr, BreakpointType type) {
+ const BreakpointMap& p = GetBreakpointMap(type);
+ const auto next_breakpoint = p.lower_bound(addr);
BreakpointAddress breakpoint;
if (next_breakpoint != p.end()) {
@@ -468,36 +475,38 @@ BreakpointAddress GetNextBreakpointFromAddress(PAddr addr, BreakpointType type)
return breakpoint;
}
-bool CheckBreakpoint(PAddr addr, BreakpointType type) {
+bool CheckBreakpoint(VAddr addr, BreakpointType type) {
if (!IsConnected()) {
return false;
}
- std::map<u64, Breakpoint>& p = GetBreakpointList(type);
+ const BreakpointMap& p = GetBreakpointMap(type);
+ const auto bp = p.find(addr);
- auto bp = p.find(static_cast<u64>(addr));
- if (bp != p.end()) {
- u64 len = bp->second.len;
+ if (bp == p.end()) {
+ return false;
+ }
- // IDA Pro defaults to 4-byte breakpoints for all non-hardware breakpoints
- // no matter if it's a 4-byte or 2-byte instruction. When you execute a
- // Thumb instruction with a 4-byte breakpoint set, it will set a breakpoint on
- // two instructions instead of the single instruction you placed the breakpoint
- // on. So, as a way to make sure that execution breakpoints are only breaking
- // on the instruction that was specified, set the length of an execution
- // breakpoint to 1. This should be fine since the CPU should never begin executing
- // an instruction anywhere except the beginning of the instruction.
- if (type == BreakpointType::Execute) {
- len = 1;
- }
+ u64 len = bp->second.len;
- if (bp->second.active && (addr >= bp->second.addr && addr < bp->second.addr + len)) {
- LOG_DEBUG(Debug_GDBStub,
- "Found breakpoint type {} @ {:016X}, range: {:016X}"
- " - {:016X} ({:X} bytes)",
- static_cast<int>(type), addr, bp->second.addr, bp->second.addr + len, len);
- return true;
- }
+ // IDA Pro defaults to 4-byte breakpoints for all non-hardware breakpoints
+ // no matter if it's a 4-byte or 2-byte instruction. When you execute a
+ // Thumb instruction with a 4-byte breakpoint set, it will set a breakpoint on
+ // two instructions instead of the single instruction you placed the breakpoint
+ // on. So, as a way to make sure that execution breakpoints are only breaking
+ // on the instruction that was specified, set the length of an execution
+ // breakpoint to 1. This should be fine since the CPU should never begin executing
+ // an instruction anywhere except the beginning of the instruction.
+ if (type == BreakpointType::Execute) {
+ len = 1;
+ }
+
+ if (bp->second.active && (addr >= bp->second.addr && addr < bp->second.addr + len)) {
+ LOG_DEBUG(Debug_GDBStub,
+ "Found breakpoint type {} @ {:016X}, range: {:016X}"
+ " - {:016X} ({:X} bytes)",
+ static_cast<int>(type), addr, bp->second.addr, bp->second.addr + len, len);
+ return true;
}
return false;
@@ -931,6 +940,7 @@ static void WriteMemory() {
GdbHexToMem(data.data(), len_pos + 1, len);
Memory::WriteBlock(addr, data.data(), len);
+ Core::System::GetInstance().InvalidateCpuInstructionCaches();
SendReply("OK");
}
@@ -950,6 +960,7 @@ static void Step() {
step_loop = true;
halt_loop = true;
send_trap = true;
+ Core::System::GetInstance().InvalidateCpuInstructionCaches();
}
/// Tell the CPU if we hit a memory breakpoint.
@@ -966,6 +977,7 @@ static void Continue() {
memory_break = false;
step_loop = false;
halt_loop = false;
+ Core::System::GetInstance().InvalidateCpuInstructionCaches();
}
/**
@@ -975,13 +987,17 @@ static void Continue() {
* @param addr Address of breakpoint.
* @param len Length of breakpoint.
*/
-static bool CommitBreakpoint(BreakpointType type, PAddr addr, u64 len) {
- std::map<u64, Breakpoint>& p = GetBreakpointList(type);
+static bool CommitBreakpoint(BreakpointType type, VAddr addr, u64 len) {
+ BreakpointMap& p = GetBreakpointMap(type);
Breakpoint breakpoint;
breakpoint.active = true;
breakpoint.addr = addr;
breakpoint.len = len;
+ Memory::ReadBlock(addr, breakpoint.inst.data(), breakpoint.inst.size());
+ static constexpr std::array<u8, 4> btrap{{0xd4, 0x20, 0x7d, 0x0}};
+ Memory::WriteBlock(addr, btrap.data(), btrap.size());
+ Core::System::GetInstance().InvalidateCpuInstructionCaches();
p.insert({addr, breakpoint});
LOG_DEBUG(Debug_GDBStub, "gdb: added {} breakpoint: {:016X} bytes at {:016X}",
@@ -1015,7 +1031,7 @@ static void AddBreakpoint() {
auto start_offset = command_buffer + 3;
auto addr_pos = std::find(start_offset, command_buffer + command_length, ',');
- PAddr addr = HexToLong(start_offset, static_cast<u64>(addr_pos - start_offset));
+ VAddr addr = HexToLong(start_offset, static_cast<u64>(addr_pos - start_offset));
start_offset = addr_pos + 1;
u64 len =
@@ -1064,7 +1080,7 @@ static void RemoveBreakpoint() {
auto start_offset = command_buffer + 3;
auto addr_pos = std::find(start_offset, command_buffer + command_length, ',');
- PAddr addr = HexToLong(start_offset, static_cast<u64>(addr_pos - start_offset));
+ VAddr addr = HexToLong(start_offset, static_cast<u64>(addr_pos - start_offset));
if (type == BreakpointType::Access) {
// 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 {
};
struct BreakpointAddress {
- PAddr address;
+ VAddr address;
BreakpointType type;
};
@@ -53,7 +53,7 @@ bool IsServerEnabled();
bool IsConnected();
/// Register module.
-void RegisterModule(std::string name, PAddr beg, PAddr end, bool add_elf_ext = true);
+void RegisterModule(std::string name, VAddr beg, VAddr end, bool add_elf_ext = true);
/**
* Signal to the gdbstub server that it should halt CPU execution.
@@ -74,7 +74,7 @@ void HandlePacket();
* @param addr Address to search from.
* @param type Type of breakpoint.
*/
-BreakpointAddress GetNextBreakpointFromAddress(PAddr addr, GDBStub::BreakpointType type);
+BreakpointAddress GetNextBreakpointFromAddress(VAddr addr, GDBStub::BreakpointType type);
/**
* Check if a breakpoint of the specified type exists at the given address.
@@ -82,7 +82,7 @@ BreakpointAddress GetNextBreakpointFromAddress(PAddr addr, GDBStub::BreakpointTy
* @param addr Address of breakpoint.
* @param type Type of breakpoint.
*/
-bool CheckBreakpoint(PAddr addr, GDBStub::BreakpointType type);
+bool CheckBreakpoint(VAddr addr, GDBStub::BreakpointType type);
/// If set to true, the CPU will halt at the beginning of the next CPU loop.
bool 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) {
}
// Gets the threads waiting on an address.
-static void GetThreadsWaitingOnAddress(std::vector<SharedPtr<Thread>>& waiting_threads,
- VAddr address) {
- auto RetrieveWaitingThreads =
+static std::vector<SharedPtr<Thread>> GetThreadsWaitingOnAddress(VAddr address) {
+ const auto RetrieveWaitingThreads =
[](size_t core_index, std::vector<SharedPtr<Thread>>& waiting_threads, VAddr arb_addr) {
const auto& scheduler = Core::System::GetInstance().Scheduler(core_index);
auto& thread_list = scheduler->GetThreadList();
@@ -45,16 +44,20 @@ static void GetThreadsWaitingOnAddress(std::vector<SharedPtr<Thread>>& waiting_t
}
};
- // Retrieve a list of all threads that are waiting for this address.
- RetrieveWaitingThreads(0, waiting_threads, address);
- RetrieveWaitingThreads(1, waiting_threads, address);
- RetrieveWaitingThreads(2, waiting_threads, address);
- RetrieveWaitingThreads(3, waiting_threads, address);
+ // Retrieve all threads that are waiting for this address.
+ std::vector<SharedPtr<Thread>> threads;
+ RetrieveWaitingThreads(0, threads, address);
+ RetrieveWaitingThreads(1, threads, address);
+ RetrieveWaitingThreads(2, threads, address);
+ RetrieveWaitingThreads(3, threads, address);
+
// Sort them by priority, such that the highest priority ones come first.
- std::sort(waiting_threads.begin(), waiting_threads.end(),
+ std::sort(threads.begin(), threads.end(),
[](const SharedPtr<Thread>& lhs, const SharedPtr<Thread>& rhs) {
return lhs->current_priority < rhs->current_priority;
});
+
+ return threads;
}
// 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
// Signals an address being waited on.
ResultCode SignalToAddress(VAddr address, s32 num_to_wake) {
- // Get threads waiting on the address.
- std::vector<SharedPtr<Thread>> waiting_threads;
- GetThreadsWaitingOnAddress(waiting_threads, address);
+ std::vector<SharedPtr<Thread>> waiting_threads = GetThreadsWaitingOnAddress(address);
WakeThreads(waiting_threads, num_to_wake);
return RESULT_SUCCESS;
@@ -110,12 +111,11 @@ ResultCode ModifyByWaitingCountAndSignalToAddressIfEqual(VAddr address, s32 valu
}
// Get threads waiting on the address.
- std::vector<SharedPtr<Thread>> waiting_threads;
- GetThreadsWaitingOnAddress(waiting_threads, address);
+ std::vector<SharedPtr<Thread>> waiting_threads = GetThreadsWaitingOnAddress(address);
// Determine the modified value depending on the waiting count.
s32 updated_value;
- if (waiting_threads.size() == 0) {
+ if (waiting_threads.empty()) {
updated_value = value - 1;
} else if (num_to_wake <= 0 || waiting_threads.size() <= static_cast<u32>(num_to_wake)) {
updated_value = value + 1;
diff --git a/src/core/hle/kernel/client_port.cpp b/src/core/hle/kernel/client_port.cpp
index 7933c105c..134e41ebc 100644
--- a/src/core/hle/kernel/client_port.cpp
+++ b/src/core/hle/kernel/client_port.cpp
@@ -14,8 +14,8 @@
namespace Kernel {
-ClientPort::ClientPort() {}
-ClientPort::~ClientPort() {}
+ClientPort::ClientPort() = default;
+ClientPort::~ClientPort() = default;
ResultVal<SharedPtr<ClientSession>> ClientPort::Connect() {
// Note: Threads do not wait for the server endpoint to call
@@ -40,4 +40,12 @@ ResultVal<SharedPtr<ClientSession>> ClientPort::Connect() {
return MakeResult(std::get<SharedPtr<ClientSession>>(sessions));
}
+void ClientPort::ConnectionClosed() {
+ if (active_sessions == 0) {
+ return;
+ }
+
+ --active_sessions;
+}
+
} // namespace Kernel
diff --git a/src/core/hle/kernel/client_port.h b/src/core/hle/kernel/client_port.h
index b42c94bde..b1269ea5c 100644
--- a/src/core/hle/kernel/client_port.h
+++ b/src/core/hle/kernel/client_port.h
@@ -37,14 +37,20 @@ public:
*/
ResultVal<SharedPtr<ClientSession>> Connect();
- SharedPtr<ServerPort> server_port; ///< ServerPort associated with this client port.
- u32 max_sessions; ///< Maximum number of simultaneous sessions the port can have
- u32 active_sessions; ///< Number of currently open sessions to this port
- std::string name; ///< Name of client port (optional)
+ /**
+ * Signifies that a previously active connection has been closed,
+ * decreasing the total number of active connections to this port.
+ */
+ void ConnectionClosed();
private:
ClientPort();
~ClientPort() override;
+
+ SharedPtr<ServerPort> server_port; ///< ServerPort associated with this client port.
+ u32 max_sessions = 0; ///< Maximum number of simultaneous sessions the port can have
+ u32 active_sessions = 0; ///< Number of currently open sessions to this port
+ std::string name; ///< Name of client port (optional)
};
} // namespace Kernel
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:
return HANDLE_TYPE;
}
- ResetType reset_type; ///< Current ResetType
-
- bool signaled; ///< Whether the event has already been signaled
- std::string name; ///< Name of event (optional)
+ ResetType GetResetType() const {
+ return reset_type;
+ }
bool ShouldWait(Thread* thread) const override;
void Acquire(Thread* thread) override;
@@ -47,6 +46,11 @@ public:
private:
Event();
~Event() override;
+
+ ResetType reset_type; ///< Current ResetType
+
+ bool signaled; ///< Whether the event has already been signaled
+ std::string name; ///< Name of event (optional)
};
} // namespace Kernel
diff --git a/src/core/hle/kernel/server_session.cpp b/src/core/hle/kernel/server_session.cpp
index 60370e9ec..93560152f 100644
--- a/src/core/hle/kernel/server_session.cpp
+++ b/src/core/hle/kernel/server_session.cpp
@@ -27,7 +27,7 @@ ServerSession::~ServerSession() {
// Decrease the port's connection count.
if (parent->port)
- parent->port->active_sessions--;
+ parent->port->ConnectionClosed();
// TODO(Subv): Wake up all the ClientSession's waiting threads and set
// the SendSyncRequest result to 0xC920181A.
diff --git a/src/core/hle/service/apm/apm.cpp b/src/core/hle/service/apm/apm.cpp
index 7a185c6c8..4109cb7f7 100644
--- a/src/core/hle/service/apm/apm.cpp
+++ b/src/core/hle/service/apm/apm.cpp
@@ -13,6 +13,7 @@ void InstallInterfaces(SM::ServiceManager& service_manager) {
auto module_ = std::make_shared<Module>();
std::make_shared<APM>(module_, "apm")->InstallAsService(service_manager);
std::make_shared<APM>(module_, "apm:p")->InstallAsService(service_manager);
+ std::make_shared<APM_Sys>()->InstallAsService(service_manager);
}
} // namespace Service::APM
diff --git a/src/core/hle/service/apm/interface.cpp b/src/core/hle/service/apm/interface.cpp
index ce943d829..4cd8132f5 100644
--- a/src/core/hle/service/apm/interface.cpp
+++ b/src/core/hle/service/apm/interface.cpp
@@ -74,6 +74,31 @@ void APM::OpenSession(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<ISession>();
+
+ LOG_DEBUG(Service_APM, "called");
+}
+
+APM_Sys::APM_Sys() : ServiceFramework{"apm:sys"} {
+ // clang-format off
+ static const FunctionInfo functions[] = {
+ {0, nullptr, "RequestPerformanceMode"},
+ {1, &APM_Sys::GetPerformanceEvent, "GetPerformanceEvent"},
+ {2, nullptr, "GetThrottlingState"},
+ {3, nullptr, "GetLastThrottlingState"},
+ {4, nullptr, "ClearLastThrottlingState"},
+ {5, nullptr, "LoadAndApplySettings"},
+ };
+ // clang-format on
+
+ RegisterHandlers(functions);
+}
+
+void APM_Sys::GetPerformanceEvent(Kernel::HLERequestContext& ctx) {
+ IPC::ResponseBuilder rb{ctx, 2, 0, 1};
+ rb.Push(RESULT_SUCCESS);
+ rb.PushIpcInterface<ISession>();
+
+ LOG_DEBUG(Service_APM, "called");
}
} // namespace Service::APM
diff --git a/src/core/hle/service/apm/interface.h b/src/core/hle/service/apm/interface.h
index fa68c7d93..d14264ad7 100644
--- a/src/core/hle/service/apm/interface.h
+++ b/src/core/hle/service/apm/interface.h
@@ -19,4 +19,12 @@ private:
std::shared_ptr<Module> apm;
};
+class APM_Sys final : public ServiceFramework<APM_Sys> {
+public:
+ explicit APM_Sys();
+
+private:
+ void GetPerformanceEvent(Kernel::HLERequestContext& ctx);
+};
+
} // namespace Service::APM
diff --git a/src/core/hle/service/arp/arp.cpp b/src/core/hle/service/arp/arp.cpp
new file mode 100644
index 000000000..358ef2576
--- /dev/null
+++ b/src/core/hle/service/arp/arp.cpp
@@ -0,0 +1,75 @@
+// Copyright 2018 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include <memory>
+
+#include "common/logging/log.h"
+#include "core/hle/ipc_helpers.h"
+#include "core/hle/kernel/hle_ipc.h"
+#include "core/hle/service/arp/arp.h"
+#include "core/hle/service/service.h"
+#include "core/hle/service/sm/sm.h"
+
+namespace Service::ARP {
+
+class ARP_R final : public ServiceFramework<ARP_R> {
+public:
+ explicit ARP_R() : ServiceFramework{"arp:r"} {
+ // clang-format off
+ static const FunctionInfo functions[] = {
+ {0, nullptr, "GetApplicationLaunchProperty"},
+ {1, nullptr, "GetApplicationLaunchPropertyWithApplicationId"},
+ {2, nullptr, "GetApplicationControlProperty"},
+ {3, nullptr, "GetApplicationControlPropertyWithApplicationId"},
+ };
+ // clang-format on
+
+ RegisterHandlers(functions);
+ }
+};
+
+class IRegistrar final : public ServiceFramework<IRegistrar> {
+public:
+ explicit IRegistrar() : ServiceFramework{"IRegistrar"} {
+ // clang-format off
+ static const FunctionInfo functions[] = {
+ {0, nullptr, "Issue"},
+ {1, nullptr, "SetApplicationLaunchProperty"},
+ {2, nullptr, "SetApplicationControlProperty"},
+ };
+ // clang-format on
+
+ RegisterHandlers(functions);
+ }
+};
+
+class ARP_W final : public ServiceFramework<ARP_W> {
+public:
+ explicit ARP_W() : ServiceFramework{"arp:w"} {
+ // clang-format off
+ static const FunctionInfo functions[] = {
+ {0, &ARP_W::AcquireRegistrar, "AcquireRegistrar"},
+ {1, nullptr, "DeleteProperties"},
+ };
+ // clang-format on
+
+ RegisterHandlers(functions);
+ }
+
+private:
+ void AcquireRegistrar(Kernel::HLERequestContext& ctx) {
+ IPC::ResponseBuilder rb{ctx, 2, 0, 1};
+ rb.Push(RESULT_SUCCESS);
+ rb.PushIpcInterface<IRegistrar>();
+
+ LOG_DEBUG(Service_ARP, "called");
+ }
+};
+
+void InstallInterfaces(SM::ServiceManager& sm) {
+ std::make_shared<ARP_R>()->InstallAsService(sm);
+ std::make_shared<ARP_W>()->InstallAsService(sm);
+}
+
+} // namespace Service::ARP
diff --git a/src/core/hle/service/arp/arp.h b/src/core/hle/service/arp/arp.h
new file mode 100644
index 000000000..9d100187c
--- /dev/null
+++ b/src/core/hle/service/arp/arp.h
@@ -0,0 +1,16 @@
+// Copyright 2018 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+namespace Service::SM {
+class ServiceManager;
+}
+
+namespace Service::ARP {
+
+/// Registers all ARP services with the specified service manager.
+void InstallInterfaces(SM::ServiceManager& sm);
+
+} // namespace Service::ARP
diff --git a/src/core/hle/service/audio/audin_a.cpp b/src/core/hle/service/audio/audin_a.cpp
index e62a27945..a70d5bca4 100644
--- a/src/core/hle/service/audio/audin_a.cpp
+++ b/src/core/hle/service/audio/audin_a.cpp
@@ -2,8 +2,6 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
-#pragma once
-
#include "core/hle/service/audio/audin_a.h"
namespace Service::Audio {
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 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
-#pragma once
-
#include "core/hle/service/audio/audout_a.h"
namespace 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 @@
#include <array>
#include <vector>
+
+#include "audio_core/codec.h"
#include "common/logging/log.h"
#include "core/core.h"
#include "core/hle/ipc_helpers.h"
@@ -48,7 +50,7 @@ public:
buffer_event = Kernel::Event::Create(Kernel::ResetType::Sticky, "IAudioOutBufferReleased");
stream = audio_core.OpenStream(audio_params.sample_rate, audio_params.channel_count,
- [=]() { buffer_event->Signal(); });
+ "IAudioOut", [=]() { buffer_event->Signal(); });
}
private:
@@ -111,10 +113,10 @@ private:
std::memcpy(&audio_buffer, input_buffer.data(), sizeof(AudioBuffer));
const u64 tag{rp.Pop<u64>()};
- std::vector<u8> data(audio_buffer.buffer_size);
- Memory::ReadBlock(audio_buffer.buffer, data.data(), data.size());
+ std::vector<s16> samples(audio_buffer.buffer_size / sizeof(s16));
+ Memory::ReadBlock(audio_buffer.buffer, samples.data(), audio_buffer.buffer_size);
- if (!audio_core.QueueBuffer(stream, tag, std::move(data))) {
+ if (!audio_core.QueueBuffer(stream, tag, std::move(samples))) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultCode(ErrorModule::Audio, ErrCodes::BufferCountExceeded));
}
@@ -200,7 +202,7 @@ void AudOutU::OpenAudioOutImpl(Kernel::HLERequestContext& ctx) {
rb.Push(RESULT_SUCCESS);
rb.Push<u32>(DefaultSampleRate);
rb.Push<u32>(params.channel_count);
- rb.Push<u32>(static_cast<u32>(PcmFormat::Int16));
+ rb.Push<u32>(static_cast<u32>(AudioCore::Codec::PcmFormat::Int16));
rb.Push<u32>(static_cast<u32>(AudioState::Stopped));
rb.PushIpcInterface<Audio::IAudioOut>(audio_out_interface);
}
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:
void ListAudioOutsImpl(Kernel::HLERequestContext& ctx);
void OpenAudioOutImpl(Kernel::HLERequestContext& ctx);
-
- enum class PcmFormat : u32 {
- Invalid = 0,
- Int8 = 1,
- Int16 = 2,
- Int24 = 3,
- Int32 = 4,
- PcmFloat = 5,
- Adpcm = 6,
- };
};
} // namespace Service::Audio
diff --git a/src/core/hle/service/audio/audrec_a.cpp b/src/core/hle/service/audio/audrec_a.cpp
index 9c32f9b98..016eabf53 100644
--- a/src/core/hle/service/audio/audrec_a.cpp
+++ b/src/core/hle/service/audio/audrec_a.cpp
@@ -2,8 +2,6 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
-#pragma once
-
#include "core/hle/service/audio/audrec_a.h"
namespace Service::Audio {
diff --git a/src/core/hle/service/audio/audren_a.cpp b/src/core/hle/service/audio/audren_a.cpp
index bc9930d79..616ff3dc4 100644
--- a/src/core/hle/service/audio/audren_a.cpp
+++ b/src/core/hle/service/audio/audren_a.cpp
@@ -2,8 +2,6 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
-#pragma once
-
#include "core/hle/service/audio/audren_a.h"
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 @@
namespace Service::Audio {
-/// TODO(bunnei): Find a proper value for the audio_ticks
-constexpr u64 audio_ticks{static_cast<u64>(CoreTiming::BASE_CLOCK_RATE / 200)};
-
class IAudioRenderer final : public ServiceFramework<IAudioRenderer> {
public:
- explicit IAudioRenderer(AudioRendererParameter audren_params)
- : ServiceFramework("IAudioRenderer"), worker_params(audren_params) {
+ explicit IAudioRenderer(AudioCore::AudioRendererParameter audren_params)
+ : ServiceFramework("IAudioRenderer") {
static const FunctionInfo functions[] = {
{0, nullptr, "GetAudioRendererSampleRate"},
{1, nullptr, "GetAudioRendererSampleCount"},
@@ -39,21 +36,8 @@ public:
RegisterHandlers(functions);
system_event =
- Kernel::Event::Create(Kernel::ResetType::OneShot, "IAudioRenderer:SystemEvent");
-
- // Register event callback to update the Audio Buffer
- audio_event = CoreTiming::RegisterEvent(
- "IAudioRenderer::UpdateAudioCallback", [this](u64 userdata, int cycles_late) {
- UpdateAudioCallback();
- CoreTiming::ScheduleEvent(audio_ticks - cycles_late, audio_event);
- });
-
- // Start the audio event
- CoreTiming::ScheduleEvent(audio_ticks, audio_event);
- voice_status_list.resize(worker_params.voice_count);
- }
- ~IAudioRenderer() {
- CoreTiming::UnscheduleEvent(audio_event, 0);
+ Kernel::Event::Create(Kernel::ResetType::Sticky, "IAudioRenderer:SystemEvent");
+ renderer = std::make_unique<AudioCore::AudioRenderer>(audren_params, system_event);
}
private:
@@ -62,60 +46,9 @@ private:
}
void RequestUpdateAudioRenderer(Kernel::HLERequestContext& ctx) {
- UpdateDataHeader config{};
- auto buf = ctx.ReadBuffer();
- std::memcpy(&config, buf.data(), sizeof(UpdateDataHeader));
- u32 memory_pool_count = worker_params.effect_count + (worker_params.voice_count * 4);
-
- std::vector<MemoryPoolInfo> mem_pool_info(memory_pool_count);
- std::memcpy(mem_pool_info.data(),
- buf.data() + sizeof(UpdateDataHeader) + config.behavior_size,
- memory_pool_count * sizeof(MemoryPoolInfo));
-
- std::vector<VoiceInfo> voice_info(worker_params.voice_count);
- std::memcpy(voice_info.data(),
- buf.data() + sizeof(UpdateDataHeader) + config.behavior_size +
- config.memory_pools_size + config.voice_resource_size,
- worker_params.voice_count * sizeof(VoiceInfo));
-
- UpdateDataHeader response_data{worker_params};
-
- ASSERT(ctx.GetWriteBufferSize() == response_data.total_size);
-
- std::vector<u8> output(response_data.total_size);
- std::memcpy(output.data(), &response_data, sizeof(UpdateDataHeader));
- std::vector<MemoryPoolEntry> memory_pool(memory_pool_count);
- for (unsigned i = 0; i < memory_pool.size(); i++) {
- if (mem_pool_info[i].pool_state == MemoryPoolStates::RequestAttach)
- memory_pool[i].state = MemoryPoolStates::Attached;
- else if (mem_pool_info[i].pool_state == MemoryPoolStates::RequestDetach)
- memory_pool[i].state = MemoryPoolStates::Detached;
- }
- std::memcpy(output.data() + sizeof(UpdateDataHeader), memory_pool.data(),
- response_data.memory_pools_size);
-
- for (unsigned i = 0; i < voice_info.size(); i++) {
- if (voice_info[i].is_new) {
- voice_status_list[i].played_sample_count = 0;
- voice_status_list[i].wave_buffer_consumed = 0;
- } else if (voice_info[i].play_state == (u8)PlayStates::Started) {
- for (u32 buff_idx = 0; buff_idx < voice_info[i].wave_buffer_count; buff_idx++) {
- voice_status_list[i].played_sample_count +=
- (voice_info[i].wave_buffer[buff_idx].end_sample_offset -
- voice_info[i].wave_buffer[buff_idx].start_sample_offset) /
- 2;
- voice_status_list[i].wave_buffer_consumed++;
- }
- }
- }
- std::memcpy(output.data() + sizeof(UpdateDataHeader) + response_data.memory_pools_size,
- voice_status_list.data(), response_data.voices_size);
-
- ctx.WriteBuffer(output);
-
+ ctx.WriteBuffer(renderer->UpdateAudioRenderer(ctx.ReadBuffer()));
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
-
LOG_WARNING(Service_Audio, "(STUBBED) called");
}
@@ -136,8 +69,6 @@ private:
}
void QuerySystemEvent(Kernel::HLERequestContext& ctx) {
- // system_event->Signal();
-
IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(RESULT_SUCCESS);
rb.PushCopyObjects(system_event);
@@ -145,131 +76,8 @@ private:
LOG_WARNING(Service_Audio, "(STUBBED) called");
}
- enum class MemoryPoolStates : u32 { // Should be LE
- Invalid = 0x0,
- Unknown = 0x1,
- RequestDetach = 0x2,
- Detached = 0x3,
- RequestAttach = 0x4,
- Attached = 0x5,
- Released = 0x6,
- };
-
- enum class PlayStates : u8 {
- Started = 0,
- Stopped = 1,
- };
-
- struct MemoryPoolEntry {
- MemoryPoolStates state;
- u32_le unknown_4;
- u32_le unknown_8;
- u32_le unknown_c;
- };
- static_assert(sizeof(MemoryPoolEntry) == 0x10, "MemoryPoolEntry has wrong size");
-
- struct MemoryPoolInfo {
- u64_le pool_address;
- u64_le pool_size;
- MemoryPoolStates pool_state;
- INSERT_PADDING_WORDS(3); // Unknown
- };
- static_assert(sizeof(MemoryPoolInfo) == 0x20, "MemoryPoolInfo has wrong size");
-
- struct UpdateDataHeader {
- UpdateDataHeader() {}
-
- explicit UpdateDataHeader(const AudioRendererParameter& config) {
- revision = Common::MakeMagic('R', 'E', 'V', '4'); // 5.1.0 Revision
- behavior_size = 0xb0;
- memory_pools_size = (config.effect_count + (config.voice_count * 4)) * 0x10;
- voices_size = config.voice_count * 0x10;
- voice_resource_size = 0x0;
- effects_size = config.effect_count * 0x10;
- mixes_size = 0x0;
- sinks_size = config.sink_count * 0x20;
- performance_manager_size = 0x10;
- total_size = sizeof(UpdateDataHeader) + behavior_size + memory_pools_size +
- voices_size + effects_size + sinks_size + performance_manager_size;
- }
-
- u32_le revision;
- u32_le behavior_size;
- u32_le memory_pools_size;
- u32_le voices_size;
- u32_le voice_resource_size;
- u32_le effects_size;
- u32_le mixes_size;
- u32_le sinks_size;
- u32_le performance_manager_size;
- INSERT_PADDING_WORDS(6);
- u32_le total_size;
- };
- static_assert(sizeof(UpdateDataHeader) == 0x40, "UpdateDataHeader has wrong size");
-
- struct BiquadFilter {
- u8 enable;
- INSERT_PADDING_BYTES(1);
- s16_le numerator[3];
- s16_le denominator[2];
- };
- static_assert(sizeof(BiquadFilter) == 0xc, "BiquadFilter has wrong size");
-
- struct WaveBuffer {
- u64_le buffer_addr;
- u64_le buffer_sz;
- s32_le start_sample_offset;
- s32_le end_sample_offset;
- u8 loop;
- u8 end_of_stream;
- u8 sent_to_server;
- INSERT_PADDING_BYTES(5);
- u64 context_addr;
- u64 context_sz;
- INSERT_PADDING_BYTES(8);
- };
- static_assert(sizeof(WaveBuffer) == 0x38, "WaveBuffer has wrong size");
-
- struct VoiceInfo {
- u32_le id;
- u32_le node_id;
- u8 is_new;
- u8 is_in_use;
- u8 play_state;
- u8 sample_format;
- u32_le sample_rate;
- u32_le priority;
- u32_le sorting_order;
- u32_le channel_count;
- float_le pitch;
- float_le volume;
- BiquadFilter biquad_filter[2];
- u32_le wave_buffer_count;
- u16_le wave_buffer_head;
- INSERT_PADDING_BYTES(6);
- u64_le additional_params_addr;
- u64_le additional_params_sz;
- u32_le mix_id;
- u32_le splitter_info_id;
- WaveBuffer wave_buffer[4];
- u32_le voice_channel_resource_ids[6];
- INSERT_PADDING_BYTES(24);
- };
- static_assert(sizeof(VoiceInfo) == 0x170, "VoiceInfo is wrong size");
-
- struct VoiceOutStatus {
- u64_le played_sample_count;
- u32_le wave_buffer_consumed;
- INSERT_PADDING_WORDS(1);
- };
- static_assert(sizeof(VoiceOutStatus) == 0x10, "VoiceOutStatus has wrong size");
-
- /// This is used to trigger the audio event callback.
- CoreTiming::EventType* audio_event;
-
Kernel::SharedPtr<Kernel::Event> system_event;
- AudioRendererParameter worker_params;
- std::vector<VoiceOutStatus> voice_status_list;
+ std::unique_ptr<AudioCore::AudioRenderer> renderer;
};
class IAudioDevice final : public ServiceFramework<IAudioDevice> {
@@ -368,7 +176,7 @@ AudRenU::AudRenU() : ServiceFramework("audren:u") {
void AudRenU::OpenAudioRenderer(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
- auto params = rp.PopRaw<AudioRendererParameter>();
+ auto params = rp.PopRaw<AudioCore::AudioRendererParameter>();
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
@@ -379,7 +187,7 @@ void AudRenU::OpenAudioRenderer(Kernel::HLERequestContext& ctx) {
void AudRenU::GetAudioRendererWorkBufferSize(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
- auto params = rp.PopRaw<AudioRendererParameter>();
+ auto params = rp.PopRaw<AudioCore::AudioRendererParameter>();
u64 buffer_sz = Common::AlignUp(4 * params.unknown_8, 0x40);
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 @@
#pragma once
+#include "audio_core/audio_renderer.h"
#include "core/hle/service/service.h"
namespace Kernel {
@@ -12,24 +13,6 @@ class HLERequestContext;
namespace Service::Audio {
-struct AudioRendererParameter {
- u32_le sample_rate;
- u32_le sample_count;
- u32_le unknown_8;
- u32_le unknown_c;
- u32_le voice_count;
- u32_le sink_count;
- u32_le effect_count;
- u32_le unknown_1c;
- u8 unknown_20;
- INSERT_PADDING_BYTES(3);
- u32_le splitter_count;
- u32_le unknown_2c;
- INSERT_PADDING_WORDS(1);
- u32_le revision;
-};
-static_assert(sizeof(AudioRendererParameter) == 52, "AudioRendererParameter is an invalid size");
-
class AudRenU final : public ServiceFramework<AudRenU> {
public:
explicit AudRenU();
diff --git a/src/core/hle/service/filesystem/fsp_ldr.cpp b/src/core/hle/service/filesystem/fsp_ldr.cpp
index ee6d4d055..0ab9c2606 100644
--- a/src/core/hle/service/filesystem/fsp_ldr.cpp
+++ b/src/core/hle/service/filesystem/fsp_ldr.cpp
@@ -2,8 +2,6 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
-#pragma once
-
#include "core/hle/service/filesystem/fsp_ldr.h"
#include "core/hle/service/service.h"
diff --git a/src/core/hle/service/filesystem/fsp_pr.cpp b/src/core/hle/service/filesystem/fsp_pr.cpp
index 0b51385ee..32b0ae454 100644
--- a/src/core/hle/service/filesystem/fsp_pr.cpp
+++ b/src/core/hle/service/filesystem/fsp_pr.cpp
@@ -2,8 +2,6 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
-#pragma once
-
#include "core/hle/service/filesystem/fsp_pr.h"
#include "core/hle/service/service.h"
diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp
index ed53f96c5..8f0262e34 100644
--- a/src/core/hle/service/hid/hid.cpp
+++ b/src/core/hle/service/hid/hid.cpp
@@ -337,6 +337,7 @@ public:
"AcquireNpadStyleSetUpdateEventHandle"},
{107, nullptr, "DisconnectNpad"},
{108, &Hid::GetPlayerLedPattern, "GetPlayerLedPattern"},
+ {109, nullptr, "ActivateNpadWithRevision"},
{120, &Hid::SetNpadJoyHoldType, "SetNpadJoyHoldType"},
{121, &Hid::GetNpadJoyHoldType, "GetNpadJoyHoldType"},
{122, &Hid::SetNpadJoyAssignmentModeSingleByDefault,
diff --git a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp
index ed69a4325..8bc49935a 100644
--- a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp
@@ -7,8 +7,8 @@
#include "core/core.h"
#include "core/hle/service/nvdrv/devices/nvdisp_disp0.h"
#include "core/hle/service/nvdrv/devices/nvmap.h"
+#include "video_core/gpu.h"
#include "video_core/renderer_base.h"
-#include "video_core/video_core.h"
namespace Service::Nvidia::Devices {
@@ -30,9 +30,9 @@ void nvdisp_disp0::flip(u32 buffer_handle, u32 offset, u32 format, u32 width, u3
addr, offset, width, height, stride, static_cast<PixelFormat>(format),
transform, crop_rect};
- Core::System::GetInstance().perf_stats.EndGameFrame();
-
- VideoCore::g_renderer->SwapBuffers(framebuffer);
+ auto& instance = Core::System::GetInstance();
+ instance.perf_stats.EndGameFrame();
+ instance.Renderer().SwapBuffers(framebuffer);
}
} // namespace Service::Nvidia::Devices
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp
index 57b128b40..be2b79256 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp
@@ -2,14 +2,15 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
-#include <cinttypes>
+#include <cstring>
#include "common/assert.h"
#include "common/logging/log.h"
#include "core/core.h"
#include "core/hle/service/nvdrv/devices/nvhost_as_gpu.h"
#include "core/hle/service/nvdrv/devices/nvmap.h"
+#include "video_core/memory_manager.h"
+#include "video_core/rasterizer_interface.h"
#include "video_core/renderer_base.h"
-#include "video_core/video_core.h"
namespace Service::Nvidia::Devices {
@@ -150,15 +151,16 @@ u32 nvhost_as_gpu::UnmapBuffer(const std::vector<u8>& input, std::vector<u8>& ou
LOG_DEBUG(Service_NVDRV, "called, offset=0x{:X}", params.offset);
- auto& gpu = Core::System::GetInstance().GPU();
-
- auto itr = buffer_mappings.find(params.offset);
-
+ const auto itr = buffer_mappings.find(params.offset);
ASSERT_MSG(itr != buffer_mappings.end(), "Tried to unmap invalid mapping");
+ auto& system_instance = Core::System::GetInstance();
+
// Remove this memory region from the rasterizer cache.
- VideoCore::g_renderer->Rasterizer()->FlushAndInvalidateRegion(params.offset, itr->second.size);
+ system_instance.Renderer().Rasterizer().FlushAndInvalidateRegion(params.offset,
+ itr->second.size);
+ auto& gpu = system_instance.GPU();
params.offset = gpu.memory_manager->UnmapBuffer(params.offset, itr->second.size);
buffer_mappings.erase(itr->second.offset);
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp
index 671b092e1..5685eb2be 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp
@@ -2,6 +2,9 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
+#include <cstdlib>
+#include <cstring>
+
#include "common/assert.h"
#include "common/logging/log.h"
#include "core/hle/service/nvdrv/devices/nvhost_ctrl.h"
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h
index 090261a60..6b496e9fe 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h
@@ -5,8 +5,6 @@
#pragma once
#include <array>
-#include <cstdlib>
-#include <cstring>
#include <vector>
#include "common/common_types.h"
#include "core/hle/service/nvdrv/devices/nvdevice.h"
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp
index 010072a5b..ae421247d 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp
@@ -2,7 +2,7 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
-#include <cinttypes>
+#include <cstring>
#include "common/assert.h"
#include "common/logging/log.h"
#include "core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h"
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp
index 5a1123ad2..116dabedb 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp
@@ -2,12 +2,14 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
-#include <cinttypes>
-#include <map>
+#include <cstring>
#include "common/assert.h"
#include "common/logging/log.h"
#include "core/core.h"
#include "core/hle/service/nvdrv/devices/nvhost_gpu.h"
+#include "core/memory.h"
+#include "video_core/gpu.h"
+#include "video_core/memory_manager.h"
namespace Service::Nvidia::Devices {
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_gpu.h b/src/core/hle/service/nvdrv/devices/nvhost_gpu.h
index aa8df2e6e..650ed8fbc 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_gpu.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_gpu.h
@@ -6,6 +6,7 @@
#include <memory>
#include <vector>
+#include "common/bit_field.h"
#include "common/common_types.h"
#include "common/swap.h"
#include "core/hle/service/nvdrv/devices/nvdevice.h"
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp
index b51c73ee8..364619e67 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp
@@ -2,6 +2,8 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
+#include <cstring>
+
#include "common/assert.h"
#include "common/logging/log.h"
#include "core/hle/service/nvdrv/devices/nvhost_nvdec.h"
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h
index 0192aecdd..6ad74421b 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h
@@ -4,11 +4,9 @@
#pragma once
-#include <array>
-#include <cstdlib>
-#include <cstring>
#include <vector>
#include "common/common_types.h"
+#include "common/swap.h"
#include "core/hle/service/nvdrv/devices/nvdevice.h"
namespace Service::Nvidia::Devices {
diff --git a/src/core/hle/service/nvdrv/devices/nvmap.cpp b/src/core/hle/service/nvdrv/devices/nvmap.cpp
index 724eeb139..e9305bfb3 100644
--- a/src/core/hle/service/nvdrv/devices/nvmap.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvmap.cpp
@@ -3,7 +3,7 @@
// Refer to the license.txt file included.
#include <algorithm>
-#include <cinttypes>
+#include <cstring>
#include "common/assert.h"
#include "common/logging/log.h"
diff --git a/src/core/hle/service/nvdrv/interface.h b/src/core/hle/service/nvdrv/interface.h
index 959b5ba29..1c3529bb6 100644
--- a/src/core/hle/service/nvdrv/interface.h
+++ b/src/core/hle/service/nvdrv/interface.h
@@ -5,7 +5,6 @@
#pragma once
#include <memory>
-#include <string>
#include "core/hle/kernel/event.h"
#include "core/hle/service/nvdrv/nvdrv.h"
#include "core/hle/service/service.h"
diff --git a/src/core/hle/service/nvdrv/nvdrv.cpp b/src/core/hle/service/nvdrv/nvdrv.cpp
index 1555ea806..e8b30921a 100644
--- a/src/core/hle/service/nvdrv/nvdrv.cpp
+++ b/src/core/hle/service/nvdrv/nvdrv.cpp
@@ -54,7 +54,7 @@ u32 Module::Open(const std::string& device_name) {
return fd;
}
-u32 Module::Ioctl(u32 fd, u32_le command, const std::vector<u8>& input, std::vector<u8>& output) {
+u32 Module::Ioctl(u32 fd, u32 command, const std::vector<u8>& input, std::vector<u8>& output) {
auto itr = open_files.find(fd);
ASSERT_MSG(itr != open_files.end(), "Tried to talk to an invalid device");
diff --git a/src/core/hle/service/nvdrv/nvmemp.cpp b/src/core/hle/service/nvdrv/nvmemp.cpp
index 9ca6e5512..0e8e21bad 100644
--- a/src/core/hle/service/nvdrv/nvmemp.cpp
+++ b/src/core/hle/service/nvdrv/nvmemp.cpp
@@ -4,8 +4,6 @@
#include "common/assert.h"
#include "common/logging/log.h"
-#include "core/hle/ipc_helpers.h"
-#include "core/hle/service/nvdrv/nvdrv.h"
#include "core/hle/service/nvdrv/nvmemp.h"
namespace Service::Nvidia {
diff --git a/src/core/hle/service/nvflinger/nvflinger.cpp b/src/core/hle/service/nvflinger/nvflinger.cpp
index 5344441e1..570aa8493 100644
--- a/src/core/hle/service/nvflinger/nvflinger.cpp
+++ b/src/core/hle/service/nvflinger/nvflinger.cpp
@@ -3,8 +3,11 @@
// Refer to the license.txt file included.
#include <algorithm>
+#include <boost/optional.hpp>
#include "common/alignment.h"
+#include "common/assert.h"
+#include "common/logging/log.h"
#include "common/microprofile.h"
#include "common/scope_exit.h"
#include "core/core.h"
@@ -31,7 +34,7 @@ NVFlinger::NVFlinger() {
// Schedule the screen composition events
composition_event =
- CoreTiming::RegisterEvent("ScreenCompositioin", [this](u64 userdata, int cycles_late) {
+ CoreTiming::RegisterEvent("ScreenComposition", [this](u64 userdata, int cycles_late) {
Compose();
CoreTiming::ScheduleEvent(frame_ticks - cycles_late, composition_event);
});
@@ -43,7 +46,7 @@ NVFlinger::~NVFlinger() {
CoreTiming::UnscheduleEvent(composition_event, 0);
}
-u64 NVFlinger::OpenDisplay(const std::string& name) {
+u64 NVFlinger::OpenDisplay(std::string_view name) {
LOG_WARNING(Service, "Opening display {}", name);
// TODO(Subv): Currently we only support the Default display.
@@ -127,9 +130,11 @@ void NVFlinger::Compose() {
MicroProfileFlip();
if (buffer == boost::none) {
+ auto& system_instance = Core::System::GetInstance();
+
// There was no queued buffer to draw, render previous frame
- Core::System::GetInstance().perf_stats.EndGameFrame();
- VideoCore::g_renderer->SwapBuffers({});
+ system_instance.perf_stats.EndGameFrame();
+ system_instance.Renderer().SwapBuffers({});
continue;
}
diff --git a/src/core/hle/service/nvflinger/nvflinger.h b/src/core/hle/service/nvflinger/nvflinger.h
index 2c908297b..5374df175 100644
--- a/src/core/hle/service/nvflinger/nvflinger.h
+++ b/src/core/hle/service/nvflinger/nvflinger.h
@@ -5,7 +5,11 @@
#pragma once
#include <memory>
-#include <boost/optional.hpp>
+#include <string>
+#include <string_view>
+#include <vector>
+
+#include "common/common_types.h"
#include "core/hle/kernel/event.h"
namespace CoreTiming {
@@ -41,7 +45,7 @@ public:
~NVFlinger();
/// Opens the specified display and returns the id.
- u64 OpenDisplay(const std::string& name);
+ u64 OpenDisplay(std::string_view name);
/// Creates a layer on the specified display and returns the layer id.
u64 CreateLayer(u64 display_id);
diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp
index 61e0c34a0..889cdd41a 100644
--- a/src/core/hle/service/service.cpp
+++ b/src/core/hle/service/service.cpp
@@ -19,6 +19,7 @@
#include "core/hle/service/am/am.h"
#include "core/hle/service/aoc/aoc_u.h"
#include "core/hle/service/apm/apm.h"
+#include "core/hle/service/arp/arp.h"
#include "core/hle/service/audio/audio.h"
#include "core/hle/service/bcat/bcat.h"
#include "core/hle/service/bpc/bpc.h"
@@ -62,6 +63,7 @@
#include "core/hle/service/spl/module.h"
#include "core/hle/service/ssl/ssl.h"
#include "core/hle/service/time/time.h"
+#include "core/hle/service/usb/usb.h"
#include "core/hle/service/vi/vi.h"
#include "core/hle/service/wlan/wlan.h"
@@ -207,6 +209,7 @@ void Init(std::shared_ptr<SM::ServiceManager>& sm) {
AM::InstallInterfaces(*sm, nv_flinger);
AOC::InstallInterfaces(*sm);
APM::InstallInterfaces(*sm);
+ ARP::InstallInterfaces(*sm);
Audio::InstallInterfaces(*sm);
BCAT::InstallInterfaces(*sm);
BPC::InstallInterfaces(*sm);
@@ -247,6 +250,7 @@ void Init(std::shared_ptr<SM::ServiceManager>& sm) {
SPL::InstallInterfaces(*sm);
SSL::InstallInterfaces(*sm);
Time::InstallInterfaces(*sm);
+ USB::InstallInterfaces(*sm);
VI::InstallInterfaces(*sm, nv_flinger);
WLAN::InstallInterfaces(*sm);
diff --git a/src/core/hle/service/time/time.cpp b/src/core/hle/service/time/time.cpp
index 37b58bb77..2172c681b 100644
--- a/src/core/hle/service/time/time.cpp
+++ b/src/core/hle/service/time/time.cpp
@@ -80,8 +80,8 @@ public:
{5, nullptr, "GetTimeZoneRuleVersion"},
{100, &ITimeZoneService::ToCalendarTime, "ToCalendarTime"},
{101, &ITimeZoneService::ToCalendarTimeWithMyRule, "ToCalendarTimeWithMyRule"},
- {200, nullptr, "ToPosixTime"},
- {201, nullptr, "ToPosixTimeWithMyRule"},
+ {201, nullptr, "ToPosixTime"},
+ {202, nullptr, "ToPosixTimeWithMyRule"},
};
RegisterHandlers(functions);
}
diff --git a/src/core/hle/service/usb/usb.cpp b/src/core/hle/service/usb/usb.cpp
new file mode 100644
index 000000000..e7fb5a419
--- /dev/null
+++ b/src/core/hle/service/usb/usb.cpp
@@ -0,0 +1,238 @@
+// Copyright 2018 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include <memory>
+
+#include "common/logging/log.h"
+#include "core/hle/ipc_helpers.h"
+#include "core/hle/kernel/hle_ipc.h"
+#include "core/hle/service/service.h"
+#include "core/hle/service/sm/sm.h"
+#include "core/hle/service/usb/usb.h"
+
+namespace Service::USB {
+
+class IDsInterface final : public ServiceFramework<IDsInterface> {
+public:
+ explicit IDsInterface() : ServiceFramework{"IDsInterface"} {
+ // clang-format off
+ static const FunctionInfo functions[] = {
+ {0, nullptr, "GetDsEndpoint"},
+ {1, nullptr, "GetSetupEvent"},
+ {2, nullptr, "Unknown"},
+ {3, nullptr, "EnableInterface"},
+ {4, nullptr, "DisableInterface"},
+ {5, nullptr, "CtrlInPostBufferAsync"},
+ {6, nullptr, "CtrlOutPostBufferAsync"},
+ {7, nullptr, "GetCtrlInCompletionEvent"},
+ {8, nullptr, "GetCtrlInReportData"},
+ {9, nullptr, "GetCtrlOutCompletionEvent"},
+ {10, nullptr, "GetCtrlOutReportData"},
+ {11, nullptr, "StallCtrl"},
+ {12, nullptr, "AppendConfigurationData"},
+ };
+ // clang-format on
+
+ RegisterHandlers(functions);
+ }
+};
+
+class USB_DS final : public ServiceFramework<USB_DS> {
+public:
+ explicit USB_DS() : ServiceFramework{"usb:ds"} {
+ // clang-format off
+ static const FunctionInfo functions[] = {
+ {0, nullptr, "BindDevice"},
+ {1, nullptr, "BindClientProcess"},
+ {2, nullptr, "GetDsInterface"},
+ {3, nullptr, "GetStateChangeEvent"},
+ {4, nullptr, "GetState"},
+ {5, nullptr, "ClearDeviceData"},
+ {6, nullptr, "AddUsbStringDescriptor"},
+ {7, nullptr, "DeleteUsbStringDescriptor"},
+ {8, nullptr, "SetUsbDeviceDescriptor"},
+ {9, nullptr, "SetBinaryObjectStore"},
+ {10, nullptr, "Enable"},
+ {11, nullptr, "Disable"},
+ };
+ // clang-format on
+
+ RegisterHandlers(functions);
+ }
+};
+
+class IClientEpSession final : public ServiceFramework<IClientEpSession> {
+public:
+ explicit IClientEpSession() : ServiceFramework{"IClientEpSession"} {
+ // clang-format off
+ static const FunctionInfo functions[] = {
+ {0, nullptr, "Unknown1"},
+ {1, nullptr, "Unknown2"},
+ {2, nullptr, "Unknown3"},
+ {3, nullptr, "Unknown4"},
+ {4, nullptr, "PostBufferAsync"},
+ {5, nullptr, "Unknown5"},
+ {6, nullptr, "Unknown6"},
+ {7, nullptr, "Unknown7"},
+ {8, nullptr, "Unknown8"},
+ };
+ // clang-format on
+
+ RegisterHandlers(functions);
+ }
+};
+
+class IClientIfSession final : public ServiceFramework<IClientIfSession> {
+public:
+ explicit IClientIfSession() : ServiceFramework{"IClientIfSession"} {
+ // clang-format off
+ static const FunctionInfo functions[] = {
+ {0, nullptr, "Unknown1"},
+ {1, nullptr, "Unknown2"},
+ {2, nullptr, "Unknown3"},
+ {3, nullptr, "Unknown4"},
+ {4, nullptr, "Unknown5"},
+ {5, nullptr, "CtrlXferAsync"},
+ {6, nullptr, "Unknown6"},
+ {7, nullptr, "GetCtrlXferReport"},
+ {8, nullptr, "Unknown7"},
+ {9, nullptr, "GetClientEpSession"},
+ };
+ // clang-format on
+
+ RegisterHandlers(functions);
+ }
+};
+
+class USB_HS final : public ServiceFramework<USB_HS> {
+public:
+ explicit USB_HS() : ServiceFramework{"usb:hs"} {
+ // clang-format off
+ static const FunctionInfo functions[] = {
+ {0, nullptr, "BindClientProcess"},
+ {1, nullptr, "Unknown1"},
+ {2, nullptr, "Unknown2"},
+ {3, nullptr, "Unknown3"},
+ {4, nullptr, "Unknown4"},
+ {5, nullptr, "Unknown5"},
+ {6, nullptr, "GetInterfaceStateChangeEvent"},
+ {7, nullptr, "GetClientIfSession"},
+ };
+ // clang-format on
+
+ RegisterHandlers(functions);
+ }
+};
+
+class IPdSession final : public ServiceFramework<IPdSession> {
+public:
+ explicit IPdSession() : ServiceFramework{"IPdSession"} {
+ // clang-format off
+ static const FunctionInfo functions[] = {
+ {0, nullptr, "BindNoticeEvent"},
+ {1, nullptr, "Unknown1"},
+ {2, nullptr, "GetStatus"},
+ {3, nullptr, "GetNotice"},
+ {4, nullptr, "Unknown2"},
+ {5, nullptr, "Unknown3"},
+ {6, nullptr, "ReplyPowerRequest"},
+ };
+ // clang-format on
+
+ RegisterHandlers(functions);
+ }
+};
+
+class USB_PD final : public ServiceFramework<USB_PD> {
+public:
+ explicit USB_PD() : ServiceFramework{"usb:pd"} {
+ // clang-format off
+ static const FunctionInfo functions[] = {
+ {0, &USB_PD::GetPdSession, "GetPdSession"},
+ };
+ // clang-format on
+
+ RegisterHandlers(functions);
+ }
+
+private:
+ void GetPdSession(Kernel::HLERequestContext& ctx) {
+ IPC::ResponseBuilder rb{ctx, 2, 0, 1};
+ rb.Push(RESULT_SUCCESS);
+ rb.PushIpcInterface<IPdSession>();
+
+ LOG_DEBUG(Service_USB, "called");
+ }
+};
+
+class IPdCradleSession final : public ServiceFramework<IPdCradleSession> {
+public:
+ explicit IPdCradleSession() : ServiceFramework{"IPdCradleSession"} {
+ // clang-format off
+ static const FunctionInfo functions[] = {
+ {0, nullptr, "VdmUserWrite"},
+ {1, nullptr, "VdmUserRead"},
+ {2, nullptr, "Vdm20Init"},
+ {3, nullptr, "GetFwType"},
+ {4, nullptr, "GetFwRevision"},
+ {5, nullptr, "GetManufacturerId"},
+ {6, nullptr, "GetDeviceId"},
+ {7, nullptr, "Unknown1"},
+ {8, nullptr, "Unknown2"},
+ };
+ // clang-format on
+
+ RegisterHandlers(functions);
+ }
+};
+
+class USB_PD_C final : public ServiceFramework<USB_PD_C> {
+public:
+ explicit USB_PD_C() : ServiceFramework{"usb:pd:c"} {
+ // clang-format off
+ static const FunctionInfo functions[] = {
+ {0, &USB_PD_C::GetPdCradleSession, "GetPdCradleSession"},
+ };
+ // clang-format on
+
+ RegisterHandlers(functions);
+ }
+
+private:
+ void GetPdCradleSession(Kernel::HLERequestContext& ctx) {
+ IPC::ResponseBuilder rb{ctx, 2, 0, 1};
+ rb.Push(RESULT_SUCCESS);
+ rb.PushIpcInterface<IPdCradleSession>();
+
+ LOG_DEBUG(Service_USB, "called");
+ }
+};
+
+class USB_PM final : public ServiceFramework<USB_PM> {
+public:
+ explicit USB_PM() : ServiceFramework{"usb:pm"} {
+ // clang-format off
+ static const FunctionInfo functions[] = {
+ {0, nullptr, "Unknown1"},
+ {1, nullptr, "Unknown2"},
+ {2, nullptr, "Unknown3"},
+ {3, nullptr, "Unknown4"},
+ {4, nullptr, "Unknown5"},
+ {5, nullptr, "Unknown6"},
+ };
+ // clang-format on
+
+ RegisterHandlers(functions);
+ }
+};
+
+void InstallInterfaces(SM::ServiceManager& sm) {
+ std::make_shared<USB_DS>()->InstallAsService(sm);
+ std::make_shared<USB_HS>()->InstallAsService(sm);
+ std::make_shared<USB_PD>()->InstallAsService(sm);
+ std::make_shared<USB_PD_C>()->InstallAsService(sm);
+ std::make_shared<USB_PM>()->InstallAsService(sm);
+}
+
+} // namespace Service::USB
diff --git a/src/core/hle/service/usb/usb.h b/src/core/hle/service/usb/usb.h
new file mode 100644
index 000000000..970a11fe8
--- /dev/null
+++ b/src/core/hle/service/usb/usb.h
@@ -0,0 +1,15 @@
+// Copyright 2018 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+namespace Service::SM {
+class ServiceManager;
+}
+
+namespace Service::USB {
+
+void InstallInterfaces(SM::ServiceManager& sm);
+
+} // namespace Service::USB
diff --git a/src/core/loader/deconstructed_rom_directory.cpp b/src/core/loader/deconstructed_rom_directory.cpp
index b0277a875..9a8cdd0ff 100644
--- a/src/core/loader/deconstructed_rom_directory.cpp
+++ b/src/core/loader/deconstructed_rom_directory.cpp
@@ -20,6 +20,10 @@ namespace Loader {
AppLoader_DeconstructedRomDirectory::AppLoader_DeconstructedRomDirectory(FileSys::VirtualFile file)
: AppLoader(std::move(file)) {}
+AppLoader_DeconstructedRomDirectory::AppLoader_DeconstructedRomDirectory(
+ FileSys::VirtualDir directory)
+ : AppLoader(directory->GetFile("main")), dir(std::move(directory)) {}
+
FileType AppLoader_DeconstructedRomDirectory::IdentifyType(const FileSys::VirtualFile& file) {
if (FileSys::IsDirectoryExeFS(file->GetContainingDirectory())) {
return FileType::DeconstructedRomDirectory;
@@ -34,7 +38,12 @@ ResultStatus AppLoader_DeconstructedRomDirectory::Load(
return ResultStatus::ErrorAlreadyLoaded;
}
- const FileSys::VirtualDir dir = file->GetContainingDirectory();
+ if (dir == nullptr) {
+ if (file == nullptr)
+ return ResultStatus::ErrorInvalidFormat;
+ dir = file->GetContainingDirectory();
+ }
+
const FileSys::VirtualFile npdm = dir->GetFile("main.npdm");
if (npdm == nullptr)
return ResultStatus::ErrorInvalidFormat;
diff --git a/src/core/loader/deconstructed_rom_directory.h b/src/core/loader/deconstructed_rom_directory.h
index 7319ba6ea..7d5433563 100644
--- a/src/core/loader/deconstructed_rom_directory.h
+++ b/src/core/loader/deconstructed_rom_directory.h
@@ -22,6 +22,9 @@ class AppLoader_DeconstructedRomDirectory final : public AppLoader {
public:
explicit AppLoader_DeconstructedRomDirectory(FileSys::VirtualFile main_file);
+ // Overload to accept exefs directory. Must contain 'main' and 'main.npdm'
+ explicit AppLoader_DeconstructedRomDirectory(FileSys::VirtualDir directory);
+
/**
* Returns the type of the file
* @param file std::shared_ptr<VfsFile> open file
@@ -40,6 +43,7 @@ public:
private:
FileSys::ProgramMetadata metadata;
FileSys::VirtualFile romfs;
+ FileSys::VirtualDir dir;
};
} // namespace Loader
diff --git a/src/core/loader/nca.cpp b/src/core/loader/nca.cpp
index a1f8235d1..dbc67c0b5 100644
--- a/src/core/loader/nca.cpp
+++ b/src/core/loader/nca.cpp
@@ -22,7 +22,8 @@
namespace Loader {
-AppLoader_NCA::AppLoader_NCA(FileSys::VirtualFile file) : AppLoader(std::move(file)) {}
+AppLoader_NCA::AppLoader_NCA(FileSys::VirtualFile file_)
+ : AppLoader(std::move(file_)), nca(std::make_unique<FileSys::NCA>(file)) {}
FileType AppLoader_NCA::IdentifyType(const FileSys::VirtualFile& file) {
FileSys::NCA nca(file);
@@ -39,8 +40,7 @@ ResultStatus AppLoader_NCA::Load(Kernel::SharedPtr<Kernel::Process>& process) {
return ResultStatus::ErrorAlreadyLoaded;
}
- nca = std::make_unique<FileSys::NCA>(file);
- ResultStatus result = nca->GetStatus();
+ const auto result = nca->GetStatus();
if (result != ResultStatus::Success) {
return result;
}
@@ -48,44 +48,16 @@ ResultStatus AppLoader_NCA::Load(Kernel::SharedPtr<Kernel::Process>& process) {
if (nca->GetType() != FileSys::NCAContentType::Program)
return ResultStatus::ErrorInvalidFormat;
- auto exefs = nca->GetExeFS();
+ const auto exefs = nca->GetExeFS();
if (exefs == nullptr)
return ResultStatus::ErrorInvalidFormat;
- result = metadata.Load(exefs->GetFile("main.npdm"));
- if (result != ResultStatus::Success) {
- return result;
- }
- metadata.Print();
-
- const FileSys::ProgramAddressSpaceType arch_bits{metadata.GetAddressSpaceType()};
- if (arch_bits == FileSys::ProgramAddressSpaceType::Is32Bit) {
- return ResultStatus::ErrorUnsupportedArch;
- }
-
- VAddr next_load_addr{Memory::PROCESS_IMAGE_VADDR};
- for (const auto& module : {"rtld", "main", "subsdk0", "subsdk1", "subsdk2", "subsdk3",
- "subsdk4", "subsdk5", "subsdk6", "subsdk7", "sdk"}) {
- const VAddr load_addr = next_load_addr;
-
- next_load_addr = AppLoader_NSO::LoadModule(exefs->GetFile(module), load_addr);
- if (next_load_addr) {
- LOG_DEBUG(Loader, "loaded module {} @ 0x{:X}", module, load_addr);
- // Register module with GDBStub
- GDBStub::RegisterModule(module, load_addr, next_load_addr - 1, false);
- } else {
- next_load_addr = load_addr;
- }
- }
+ directory_loader = std::make_unique<AppLoader_DeconstructedRomDirectory>(exefs);
- process->program_id = metadata.GetTitleID();
- process->svc_access_mask.set();
- process->address_mappings = default_address_mappings;
- process->resource_limit =
- Kernel::ResourceLimit::GetForCategory(Kernel::ResourceLimitCategory::APPLICATION);
- process->Run(Memory::PROCESS_IMAGE_VADDR, metadata.GetMainThreadPriority(),
- metadata.GetMainThreadStackSize());
+ const auto load_result = directory_loader->Load(process);
+ if (load_result != ResultStatus::Success)
+ return load_result;
if (nca->GetRomFS() != nullptr && nca->GetRomFS()->GetSize() > 0)
Service::FileSystem::RegisterRomFS(std::make_unique<FileSys::RomFSFactory>(*this));
diff --git a/src/core/loader/nca.h b/src/core/loader/nca.h
index e14d618b3..0fd2d0417 100644
--- a/src/core/loader/nca.h
+++ b/src/core/loader/nca.h
@@ -10,6 +10,7 @@
#include "core/file_sys/program_metadata.h"
#include "core/hle/kernel/object.h"
#include "core/loader/loader.h"
+#include "deconstructed_rom_directory.h"
namespace Loader {
@@ -41,6 +42,7 @@ private:
FileSys::ProgramMetadata metadata;
std::unique_ptr<FileSys::NCA> nca;
+ std::unique_ptr<AppLoader_DeconstructedRomDirectory> directory_loader;
};
} // namespace Loader
diff --git a/src/core/memory.cpp b/src/core/memory.cpp
index 4b3bb7b31..1133bcbaf 100644
--- a/src/core/memory.cpp
+++ b/src/core/memory.cpp
@@ -326,43 +326,45 @@ void RasterizerMarkRegionCached(Tegra::GPUVAddr gpu_addr, u64 size, bool cached)
}
void RasterizerFlushVirtualRegion(VAddr start, u64 size, FlushMode mode) {
+ auto& system_instance = Core::System::GetInstance();
+
// Since pages are unmapped on shutdown after video core is shutdown, the renderer may be
// null here
- if (VideoCore::g_renderer == nullptr) {
+ if (!system_instance.IsPoweredOn()) {
return;
}
VAddr end = start + size;
- auto CheckRegion = [&](VAddr region_start, VAddr region_end) {
+ const auto CheckRegion = [&](VAddr region_start, VAddr region_end) {
if (start >= region_end || end <= region_start) {
// No overlap with region
return;
}
- VAddr overlap_start = std::max(start, region_start);
- VAddr overlap_end = std::min(end, region_end);
+ const VAddr overlap_start = std::max(start, region_start);
+ const VAddr overlap_end = std::min(end, region_end);
- std::vector<Tegra::GPUVAddr> gpu_addresses =
- Core::System::GetInstance().GPU().memory_manager->CpuToGpuAddress(overlap_start);
+ const std::vector<Tegra::GPUVAddr> gpu_addresses =
+ system_instance.GPU().memory_manager->CpuToGpuAddress(overlap_start);
if (gpu_addresses.empty()) {
return;
}
- u64 overlap_size = overlap_end - overlap_start;
+ const u64 overlap_size = overlap_end - overlap_start;
for (const auto& gpu_address : gpu_addresses) {
- auto* rasterizer = VideoCore::g_renderer->Rasterizer();
+ auto& rasterizer = system_instance.Renderer().Rasterizer();
switch (mode) {
case FlushMode::Flush:
- rasterizer->FlushRegion(gpu_address, overlap_size);
+ rasterizer.FlushRegion(gpu_address, overlap_size);
break;
case FlushMode::Invalidate:
- rasterizer->InvalidateRegion(gpu_address, overlap_size);
+ rasterizer.InvalidateRegion(gpu_address, overlap_size);
break;
case FlushMode::FlushAndInvalidate:
- rasterizer->FlushAndInvalidateRegion(gpu_address, overlap_size);
+ rasterizer.FlushAndInvalidateRegion(gpu_address, overlap_size);
break;
}
}
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);
PageTable* GetCurrentPageTable();
/// Determines if the given VAddr is valid for the specified process.
-bool IsValidVirtualAddress(const Kernel::Process& process, const VAddr vaddr);
-bool IsValidVirtualAddress(const VAddr addr);
+bool IsValidVirtualAddress(const Kernel::Process& process, VAddr vaddr);
+bool IsValidVirtualAddress(VAddr vaddr);
/// Determines if the given VAddr is a kernel address
-bool IsKernelVirtualAddress(const VAddr addr);
+bool IsKernelVirtualAddress(VAddr vaddr);
u8 Read8(VAddr addr);
u16 Read16(VAddr addr);
@@ -155,18 +155,17 @@ void Write16(VAddr addr, u16 data);
void Write32(VAddr addr, u32 data);
void Write64(VAddr addr, u64 data);
-void ReadBlock(const Kernel::Process& process, const VAddr src_addr, void* dest_buffer,
- size_t size);
-void ReadBlock(const VAddr src_addr, void* dest_buffer, size_t size);
-void WriteBlock(const Kernel::Process& process, const VAddr dest_addr, const void* src_buffer,
+void ReadBlock(const Kernel::Process& process, VAddr src_addr, void* dest_buffer, size_t size);
+void ReadBlock(VAddr src_addr, void* dest_buffer, size_t size);
+void WriteBlock(const Kernel::Process& process, VAddr dest_addr, const void* src_buffer,
size_t size);
-void WriteBlock(const VAddr dest_addr, const void* src_buffer, size_t size);
-void ZeroBlock(const VAddr dest_addr, const size_t size);
+void WriteBlock(VAddr dest_addr, const void* src_buffer, size_t size);
+void ZeroBlock(const Kernel::Process& process, VAddr dest_addr, size_t size);
void CopyBlock(VAddr dest_addr, VAddr src_addr, size_t size);
-u8* GetPointer(VAddr virtual_address);
+u8* GetPointer(VAddr vaddr);
-std::string ReadCString(VAddr virtual_address, std::size_t max_length);
+std::string ReadCString(VAddr vaddr, std::size_t max_length);
enum class FlushMode {
/// Write back modified surfaces to RAM
@@ -180,7 +179,7 @@ enum class FlushMode {
/**
* Mark each page touching the region as cached.
*/
-void RasterizerMarkRegionCached(Tegra::GPUVAddr start, u64 size, bool cached);
+void RasterizerMarkRegionCached(Tegra::GPUVAddr gpu_addr, u64 size, bool cached);
/**
* 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() {
game_frames += 1;
}
-PerfStats::Results PerfStats::GetAndResetStats(u64 current_system_time_us) {
+PerfStats::Results PerfStats::GetAndResetStats(microseconds current_system_time_us) {
std::lock_guard<std::mutex> lock(object_mutex);
- auto now = Clock::now();
+ const auto now = Clock::now();
// Walltime elapsed since stats were reset
- auto interval = duration_cast<DoubleSecs>(now - reset_point).count();
+ const auto interval = duration_cast<DoubleSecs>(now - reset_point).count();
- auto system_us_per_second =
- static_cast<double>(current_system_time_us - reset_point_system_us) / interval;
+ const auto system_us_per_second = (current_system_time_us - reset_point_system_us) / interval;
Results results{};
results.system_fps = static_cast<double>(system_frames) / interval;
results.game_fps = static_cast<double>(game_frames) / interval;
results.frametime = duration_cast<DoubleSecs>(accumulated_frametime).count() /
static_cast<double>(system_frames);
- results.emulation_speed = system_us_per_second / 1'000'000.0;
+ results.emulation_speed = system_us_per_second.count() / 1'000'000.0;
// Reset counters
reset_point = now;
@@ -74,10 +73,10 @@ double PerfStats::GetLastFrameTimeScale() {
return duration_cast<DoubleSecs>(previous_frame_length).count() / FRAME_LENGTH;
}
-void FrameLimiter::DoFrameLimiting(u64 current_system_time_us) {
+void FrameLimiter::DoFrameLimiting(microseconds current_system_time_us) {
// Max lag caused by slow frames. Can be adjusted to compensate for too many slow frames. Higher
// values increase the time needed to recover and limit framerate again after spikes.
- constexpr microseconds MAX_LAG_TIME_US = 25ms;
+ constexpr microseconds MAX_LAG_TIME_US = 25us;
if (!Settings::values.toggle_framelimit) {
return;
@@ -85,7 +84,7 @@ void FrameLimiter::DoFrameLimiting(u64 current_system_time_us) {
auto now = Clock::now();
- frame_limiting_delta_err += microseconds(current_system_time_us - previous_system_time_us);
+ frame_limiting_delta_err += current_system_time_us - previous_system_time_us;
frame_limiting_delta_err -= duration_cast<microseconds>(now - previous_walltime);
frame_limiting_delta_err =
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:
void EndSystemFrame();
void EndGameFrame();
- Results GetAndResetStats(u64 current_system_time_us);
+ Results GetAndResetStats(std::chrono::microseconds current_system_time_us);
/**
* Gets the ratio between walltime and the emulated time of the previous system frame. This is
@@ -47,7 +47,7 @@ private:
/// Point when the cumulative counters were reset
Clock::time_point reset_point = Clock::now();
/// System time when the cumulative counters were reset
- u64 reset_point_system_us = 0;
+ std::chrono::microseconds reset_point_system_us{0};
/// Cumulative duration (excluding v-sync/frame-limiting) of frames since last reset
Clock::duration accumulated_frametime = Clock::duration::zero();
@@ -68,11 +68,11 @@ class FrameLimiter {
public:
using Clock = std::chrono::high_resolution_clock;
- void DoFrameLimiting(u64 current_system_time_us);
+ void DoFrameLimiting(std::chrono::microseconds current_system_time_us);
private:
/// Emulated system time (in microseconds) at the last limiter invocation
- u64 previous_system_time_us = 0;
+ std::chrono::microseconds previous_system_time_us{0};
/// Walltime at the last limiter invocation
Clock::time_point previous_walltime = Clock::now();
diff --git a/src/core/settings.cpp b/src/core/settings.cpp
index 79e0b347b..a4623223d 100644
--- a/src/core/settings.cpp
+++ b/src/core/settings.cpp
@@ -2,6 +2,7 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
+#include "core/core.h"
#include "core/gdbstub/gdbstub.h"
#include "core/hle/service/hid/hid.h"
#include "core/settings.h"
@@ -19,8 +20,9 @@ void Apply() {
VideoCore::g_toggle_framelimit_enabled = values.toggle_framelimit;
- if (VideoCore::g_renderer) {
- VideoCore::g_renderer->UpdateCurrentFramebufferLayout();
+ auto& system_instance = Core::System::GetInstance();
+ if (system_instance.IsPoweredOn()) {
+ system_instance.Renderer().UpdateCurrentFramebufferLayout();
}
Service::HID::ReloadInputDevices();
diff --git a/src/video_core/engines/maxwell_3d.cpp b/src/video_core/engines/maxwell_3d.cpp
index 0e205ed72..5c0ae8009 100644
--- a/src/video_core/engines/maxwell_3d.cpp
+++ b/src/video_core/engines/maxwell_3d.cpp
@@ -19,8 +19,8 @@ namespace Engines {
/// First register id that is actually a Macro call.
constexpr u32 MacroRegistersStart = 0xE00;
-Maxwell3D::Maxwell3D(MemoryManager& memory_manager)
- : memory_manager(memory_manager), macro_interpreter(*this) {}
+Maxwell3D::Maxwell3D(VideoCore::RasterizerInterface& rasterizer, MemoryManager& memory_manager)
+ : memory_manager(memory_manager), rasterizer{rasterizer}, macro_interpreter(*this) {}
void Maxwell3D::CallMacroMethod(u32 method, std::vector<u32> parameters) {
auto macro_code = uploaded_macros.find(method);
@@ -130,7 +130,7 @@ void Maxwell3D::WriteReg(u32 method, u32 value, u32 remaining_params) {
break;
}
- VideoCore::g_renderer->Rasterizer()->NotifyMaxwellRegisterChanged(method);
+ rasterizer.NotifyMaxwellRegisterChanged(method);
if (debug_context) {
debug_context->OnEvent(Tegra::DebugContext::Event::MaxwellCommandProcessed, nullptr);
@@ -218,7 +218,7 @@ void Maxwell3D::DrawArrays() {
}
const bool is_indexed{regs.index_array.count && !regs.vertex_buffer.count};
- VideoCore::g_renderer->Rasterizer()->AccelerateDrawBatch(is_indexed);
+ rasterizer.AccelerateDrawBatch(is_indexed);
// TODO(bunnei): Below, we reset vertex count so that we can use these registers to determine if
// the game is trying to draw indexed or direct mode. This needs to be verified on HW still -
@@ -285,8 +285,6 @@ Texture::TICEntry Maxwell3D::GetTICEntry(u32 tic_index) const {
// TODO(Subv): Different data types for separate components are not supported
ASSERT(r_type == g_type && r_type == b_type && r_type == a_type);
- // TODO(Subv): Only UNORM formats are supported for now.
- ASSERT(r_type == Texture::ComponentType::UNORM);
return tic_entry;
}
@@ -393,7 +391,7 @@ void Maxwell3D::ProcessClearBuffers() {
regs.clear_buffers.R == regs.clear_buffers.B &&
regs.clear_buffers.R == regs.clear_buffers.A);
- VideoCore::g_renderer->Rasterizer()->Clear();
+ rasterizer.Clear();
}
} // namespace Engines
diff --git a/src/video_core/engines/maxwell_3d.h b/src/video_core/engines/maxwell_3d.h
index 3c32f1067..4d0ff96a5 100644
--- a/src/video_core/engines/maxwell_3d.h
+++ b/src/video_core/engines/maxwell_3d.h
@@ -17,6 +17,10 @@
#include "video_core/memory_manager.h"
#include "video_core/textures/texture.h"
+namespace VideoCore {
+class RasterizerInterface;
+}
+
namespace Tegra::Engines {
#define MAXWELL3D_REG_INDEX(field_name) \
@@ -24,7 +28,7 @@ namespace Tegra::Engines {
class Maxwell3D final {
public:
- explicit Maxwell3D(MemoryManager& memory_manager);
+ explicit Maxwell3D(VideoCore::RasterizerInterface& rasterizer, MemoryManager& memory_manager);
~Maxwell3D() = default;
/// Register structure of the Maxwell3D engine.
@@ -818,6 +822,8 @@ public:
Texture::FullTextureInfo GetStageTexture(Regs::ShaderStage stage, size_t offset) const;
private:
+ VideoCore::RasterizerInterface& rasterizer;
+
std::unordered_map<u32, std::vector<u32>> uploaded_macros;
/// Macro method that is currently being executed / being fed parameters.
diff --git a/src/video_core/gpu.cpp b/src/video_core/gpu.cpp
index 141e20444..b2a83ce0b 100644
--- a/src/video_core/gpu.cpp
+++ b/src/video_core/gpu.cpp
@@ -7,12 +7,13 @@
#include "video_core/engines/maxwell_compute.h"
#include "video_core/engines/maxwell_dma.h"
#include "video_core/gpu.h"
+#include "video_core/rasterizer_interface.h"
namespace Tegra {
-GPU::GPU() {
+GPU::GPU(VideoCore::RasterizerInterface& rasterizer) {
memory_manager = std::make_unique<MemoryManager>();
- maxwell_3d = std::make_unique<Engines::Maxwell3D>(*memory_manager);
+ maxwell_3d = std::make_unique<Engines::Maxwell3D>(rasterizer, *memory_manager);
fermi_2d = std::make_unique<Engines::Fermi2D>(*memory_manager);
maxwell_compute = std::make_unique<Engines::MaxwellCompute>();
maxwell_dma = std::make_unique<Engines::MaxwellDMA>(*memory_manager);
diff --git a/src/video_core/gpu.h b/src/video_core/gpu.h
index 08aa75503..440505c9d 100644
--- a/src/video_core/gpu.h
+++ b/src/video_core/gpu.h
@@ -11,6 +11,10 @@
#include "core/hle/service/nvflinger/buffer_queue.h"
#include "video_core/memory_manager.h"
+namespace VideoCore {
+class RasterizerInterface;
+}
+
namespace Tegra {
enum class RenderTargetFormat : u32 {
@@ -98,7 +102,7 @@ enum class EngineID {
class GPU final {
public:
- GPU();
+ explicit GPU(VideoCore::RasterizerInterface& rasterizer);
~GPU();
/// Processes a command list stored at the specified address in GPU memory.
diff --git a/src/video_core/renderer_base.cpp b/src/video_core/renderer_base.cpp
index dbe3edf09..3ca350243 100644
--- a/src/video_core/renderer_base.cpp
+++ b/src/video_core/renderer_base.cpp
@@ -7,6 +7,8 @@
#include "video_core/renderer_base.h"
#include "video_core/renderer_opengl/gl_rasterizer.h"
+namespace VideoCore {
+
RendererBase::RendererBase(EmuWindow& window) : render_window{window} {}
RendererBase::~RendererBase() = default;
@@ -21,3 +23,5 @@ void RendererBase::RefreshRasterizerSetting() {
rasterizer = std::make_unique<RasterizerOpenGL>(render_window);
}
}
+
+} // namespace VideoCore
diff --git a/src/video_core/renderer_base.h b/src/video_core/renderer_base.h
index 1cb161b7f..235de23a1 100644
--- a/src/video_core/renderer_base.h
+++ b/src/video_core/renderer_base.h
@@ -13,6 +13,8 @@
class EmuWindow;
+namespace VideoCore {
+
class RendererBase : NonCopyable {
public:
/// Used to reference a framebuffer
@@ -44,15 +46,21 @@ public:
return m_current_frame;
}
- VideoCore::RasterizerInterface* Rasterizer() const {
- return rasterizer.get();
+ RasterizerInterface& Rasterizer() {
+ return *rasterizer;
+ }
+
+ const RasterizerInterface& Rasterizer() const {
+ return *rasterizer;
}
void RefreshRasterizerSetting();
protected:
EmuWindow& render_window; ///< Reference to the render window handle.
- std::unique_ptr<VideoCore::RasterizerInterface> rasterizer;
+ std::unique_ptr<RasterizerInterface> rasterizer;
f32 m_current_fps = 0.0f; ///< Current framerate, should be set by the renderer
int m_current_frame = 0; ///< Current frame, should be set by the renderer
};
+
+} // namespace VideoCore
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index 6555db5bb..c2a931469 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -169,8 +169,14 @@ std::pair<u8*, GLintptr> RasterizerOpenGL::SetupVertexArrays(u8* array_ptr,
ASSERT(buffer.IsEnabled());
glEnableVertexAttribArray(index);
- glVertexAttribFormat(index, attrib.ComponentCount(), MaxwellToGL::VertexType(attrib),
- attrib.IsNormalized() ? GL_TRUE : GL_FALSE, attrib.offset);
+ if (attrib.type == Tegra::Engines::Maxwell3D::Regs::VertexAttribute::Type::SignedInt ||
+ attrib.type == Tegra::Engines::Maxwell3D::Regs::VertexAttribute::Type::UnsignedInt) {
+ glVertexAttribIFormat(index, attrib.ComponentCount(), MaxwellToGL::VertexType(attrib),
+ attrib.offset);
+ } else {
+ glVertexAttribFormat(index, attrib.ComponentCount(), MaxwellToGL::VertexType(attrib),
+ attrib.IsNormalized() ? GL_TRUE : GL_FALSE, attrib.offset);
+ }
glVertexAttribBinding(index, attrib.buffer);
}
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 {
params.height = Common::AlignUp(config.tic.Height(), GetCompressionFactor(params.pixel_format));
params.unaligned_height = config.tic.Height();
params.size_in_bytes = params.SizeInBytes();
+ params.cache_width = Common::AlignUp(params.width, 16);
+ params.cache_height = Common::AlignUp(params.height, 16);
return params;
}
@@ -63,6 +65,8 @@ struct FormatTuple {
params.height = config.height;
params.unaligned_height = config.height;
params.size_in_bytes = params.SizeInBytes();
+ params.cache_width = Common::AlignUp(params.width, 16);
+ params.cache_height = Common::AlignUp(params.height, 16);
return params;
}
@@ -82,6 +86,8 @@ struct FormatTuple {
params.height = zeta_height;
params.unaligned_height = zeta_height;
params.size_in_bytes = params.SizeInBytes();
+ params.cache_width = Common::AlignUp(params.width, 16);
+ params.cache_height = Common::AlignUp(params.height, 16);
return params;
}
@@ -680,12 +686,12 @@ Surface RasterizerCacheOpenGL::GetSurface(const SurfaceParams& params) {
// If use_accurate_framebuffers is enabled, always load from memory
FlushSurface(surface);
UnregisterSurface(surface);
- } else if (surface->GetSurfaceParams() != params) {
- // If surface parameters changed, recreate the surface from the old one
- return RecreateSurface(surface, params);
- } else {
+ } else if (surface->GetSurfaceParams().IsCompatibleSurface(params)) {
// Use the cached surface as-is
return surface;
+ } else {
+ // If surface parameters changed, recreate the surface from the old one
+ return RecreateSurface(surface, params);
}
}
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 @@
#include <memory>
#include <vector>
#include <boost/icl/interval_map.hpp>
+
#include "common/common_types.h"
#include "common/math_util.h"
#include "video_core/engines/maxwell_3d.h"
@@ -546,6 +547,12 @@ struct SurfaceParams {
return !operator==(other);
}
+ /// Checks if surfaces are compatible for caching
+ bool IsCompatibleSurface(const SurfaceParams& other) const {
+ return std::tie(pixel_format, type, cache_width, cache_height) ==
+ std::tie(other.pixel_format, other.type, other.cache_width, other.cache_height);
+ }
+
Tegra::GPUVAddr addr;
bool is_tiled;
u32 block_height;
@@ -556,6 +563,10 @@ struct SurfaceParams {
u32 height;
u32 unaligned_height;
size_t size_in_bytes;
+
+ // Parameters used for caching only
+ u32 cache_width;
+ u32 cache_height;
};
class CachedSurface final {
diff --git a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
index 68db3c22a..e3217db81 100644
--- a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
@@ -766,13 +766,16 @@ private:
// goes into gpr28+0 and gpr28+1
size_t texs_offset{};
+ size_t src_elem{};
for (const auto& dest : {instr.gpr0.Value(), instr.gpr28.Value()}) {
+ size_t dest_elem{};
for (unsigned elem = 0; elem < 2; ++elem) {
- if (!instr.texs.IsComponentEnabled(elem)) {
+ if (!instr.texs.IsComponentEnabled(src_elem++)) {
// Skip disabled components
continue;
}
- regs.SetRegisterToFloat(dest, elem + texs_offset, texture, 1, 4, false, elem);
+ regs.SetRegisterToFloat(dest, elem + texs_offset, texture, 1, 4, false,
+ dest_elem++);
}
if (!instr.texs.HasTwoDestinations()) {
diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp
index 74383c7cf..bf9131193 100644
--- a/src/video_core/renderer_opengl/renderer_opengl.cpp
+++ b/src/video_core/renderer_opengl/renderer_opengl.cpp
@@ -103,7 +103,7 @@ ScopeAcquireGLContext::~ScopeAcquireGLContext() {
}
}
-RendererOpenGL::RendererOpenGL(EmuWindow& window) : RendererBase{window} {}
+RendererOpenGL::RendererOpenGL(EmuWindow& window) : VideoCore::RendererBase{window} {}
RendererOpenGL::~RendererOpenGL() = default;
/// Swap buffers (render frame)
@@ -160,8 +160,8 @@ void RendererOpenGL::LoadFBToScreenInfo(const Tegra::FramebufferConfig& framebuf
// only allows rows to have a memory alignement of 4.
ASSERT(framebuffer.stride % 4 == 0);
- if (!Rasterizer()->AccelerateDisplay(framebuffer, framebuffer_addr, framebuffer.stride,
- screen_info)) {
+ if (!rasterizer->AccelerateDisplay(framebuffer, framebuffer_addr, framebuffer.stride,
+ screen_info)) {
// Reset the screen info's display texture to its own permanent texture
screen_info.display_texture = screen_info.texture.resource.handle;
diff --git a/src/video_core/renderer_opengl/renderer_opengl.h b/src/video_core/renderer_opengl/renderer_opengl.h
index ab7de41c8..428afa3b7 100644
--- a/src/video_core/renderer_opengl/renderer_opengl.h
+++ b/src/video_core/renderer_opengl/renderer_opengl.h
@@ -41,7 +41,7 @@ private:
EmuWindow& emu_window;
};
-class RendererOpenGL : public RendererBase {
+class RendererOpenGL : public VideoCore::RendererBase {
public:
explicit RendererOpenGL(EmuWindow& window);
~RendererOpenGL() override;
diff --git a/src/video_core/video_core.cpp b/src/video_core/video_core.cpp
index 06b13e681..5085ef96b 100644
--- a/src/video_core/video_core.cpp
+++ b/src/video_core/video_core.cpp
@@ -3,37 +3,16 @@
// Refer to the license.txt file included.
#include <memory>
-#include "common/logging/log.h"
#include "video_core/renderer_base.h"
#include "video_core/renderer_opengl/renderer_opengl.h"
#include "video_core/video_core.h"
-////////////////////////////////////////////////////////////////////////////////////////////////////
-// Video Core namespace
-
namespace VideoCore {
-std::unique_ptr<RendererBase> g_renderer; ///< Renderer plugin
-
std::atomic<bool> g_toggle_framelimit_enabled;
-/// Initialize the video core
-bool Init(EmuWindow& emu_window) {
- g_renderer = std::make_unique<RendererOpenGL>(emu_window);
- if (g_renderer->Init()) {
- LOG_DEBUG(Render, "initialized OK");
- } else {
- LOG_CRITICAL(Render, "initialization failed !");
- return false;
- }
- return true;
-}
-
-/// Shutdown the video core
-void Shutdown() {
- g_renderer.reset();
-
- LOG_DEBUG(Render, "shutdown OK");
+std::unique_ptr<RendererBase> CreateRenderer(EmuWindow& emu_window) {
+ return std::make_unique<RendererOpenGL>(emu_window);
}
} // namespace VideoCore
diff --git a/src/video_core/video_core.h b/src/video_core/video_core.h
index 519b757f5..7c01c0b8d 100644
--- a/src/video_core/video_core.h
+++ b/src/video_core/video_core.h
@@ -8,25 +8,23 @@
#include <memory>
class EmuWindow;
-class RendererBase;
-
-////////////////////////////////////////////////////////////////////////////////////////////////////
-// Video Core namespace
namespace VideoCore {
-enum class Renderer { Software, OpenGL };
+class RendererBase;
-extern std::unique_ptr<RendererBase> g_renderer; ///< Renderer plugin
+enum class Renderer { Software, OpenGL };
// TODO: Wrap these in a user settings struct along with any other graphics settings (often set from
// qt ui)
extern std::atomic<bool> g_toggle_framelimit_enabled;
-/// Initialize the video core
-bool Init(EmuWindow& emu_window);
-
-/// Shutdown the video core
-void Shutdown();
+/**
+ * Creates a renderer instance.
+ *
+ * @note The returned renderer instance is simply allocated. Its Init()
+ * function still needs to be called to fully complete its setup.
+ */
+std::unique_ptr<RendererBase> CreateRenderer(EmuWindow& emu_window);
} // namespace VideoCore
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
Common::g_scm_desc, QString(Common::g_build_date).left(10)));
}
-AboutDialog::~AboutDialog() {}
+AboutDialog::~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 {
public:
explicit AboutDialog(QWidget* parent);
- ~AboutDialog();
+ ~AboutDialog() override;
private:
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 {
public:
GRenderWindow(QWidget* parent, EmuThread* emu_thread);
- ~GRenderWindow();
+ ~GRenderWindow() override;
// EmuWindow implementation
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
});
}
-ConfigureDebug::~ConfigureDebug() {}
+ConfigureDebug::~ConfigureDebug() = default;
void ConfigureDebug::setConfiguration() {
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 @@
</property>
<layout class="QVBoxLayout" name="verticalLayout_3">
<item>
- <widget class="QLabel" name="label_1">
- <property name="text">
- <string>The GDB Stub only works correctly when the CPU JIT is off.</string>
- </property>
- </widget>
- </item>
- <item>
<layout class="QHBoxLayout" name="horizontalLayout_1">
<item>
<widget class="QCheckBox" name="toggle_gdbstub">
diff --git a/src/yuzu/configuration/configure_dialog.cpp b/src/yuzu/configuration/configure_dialog.cpp
index f66abf870..cc4b326ae 100644
--- a/src/yuzu/configuration/configure_dialog.cpp
+++ b/src/yuzu/configuration/configure_dialog.cpp
@@ -6,13 +6,16 @@
#include "ui_configure.h"
#include "yuzu/configuration/config.h"
#include "yuzu/configuration/configure_dialog.h"
+#include "yuzu/hotkeys.h"
-ConfigureDialog::ConfigureDialog(QWidget* parent) : QDialog(parent), ui(new Ui::ConfigureDialog) {
+ConfigureDialog::ConfigureDialog(QWidget* parent, const HotkeyRegistry& registry)
+ : QDialog(parent), ui(new Ui::ConfigureDialog) {
ui->setupUi(this);
+ ui->generalTab->PopulateHotkeyList(registry);
this->setConfiguration();
}
-ConfigureDialog::~ConfigureDialog() {}
+ConfigureDialog::~ConfigureDialog() = default;
void ConfigureDialog::setConfiguration() {}
diff --git a/src/yuzu/configuration/configure_dialog.h b/src/yuzu/configuration/configure_dialog.h
index 21fa1f501..bbbdacc29 100644
--- a/src/yuzu/configuration/configure_dialog.h
+++ b/src/yuzu/configuration/configure_dialog.h
@@ -7,6 +7,8 @@
#include <memory>
#include <QDialog>
+class HotkeyRegistry;
+
namespace Ui {
class ConfigureDialog;
}
@@ -15,7 +17,7 @@ class ConfigureDialog : public QDialog {
Q_OBJECT
public:
- explicit ConfigureDialog(QWidget* parent);
+ explicit ConfigureDialog(QWidget* parent, const HotkeyRegistry& registry);
~ConfigureDialog();
void applyConfiguration();
diff --git a/src/yuzu/configuration/configure_general.cpp b/src/yuzu/configuration/configure_general.cpp
index cb7d3f8bf..d8caee1e8 100644
--- a/src/yuzu/configuration/configure_general.cpp
+++ b/src/yuzu/configuration/configure_general.cpp
@@ -24,7 +24,7 @@ ConfigureGeneral::ConfigureGeneral(QWidget* parent)
ui->use_docked_mode->setEnabled(!Core::System::GetInstance().IsPoweredOn());
}
-ConfigureGeneral::~ConfigureGeneral() {}
+ConfigureGeneral::~ConfigureGeneral() = default;
void ConfigureGeneral::setConfiguration() {
ui->toggle_deepscan->setChecked(UISettings::values.gamedir_deepscan);
@@ -35,6 +35,10 @@ void ConfigureGeneral::setConfiguration() {
ui->use_docked_mode->setChecked(Settings::values.use_docked_mode);
}
+void ConfigureGeneral::PopulateHotkeyList(const HotkeyRegistry& registry) {
+ ui->widget->Populate(registry);
+}
+
void ConfigureGeneral::applyConfiguration() {
UISettings::values.gamedir_deepscan = ui->toggle_deepscan->isChecked();
UISettings::values.confirm_before_closing = ui->toggle_check_exit->isChecked();
diff --git a/src/yuzu/configuration/configure_general.h b/src/yuzu/configuration/configure_general.h
index 447552d8c..4770034cc 100644
--- a/src/yuzu/configuration/configure_general.h
+++ b/src/yuzu/configuration/configure_general.h
@@ -7,6 +7,8 @@
#include <memory>
#include <QWidget>
+class HotkeyRegistry;
+
namespace Ui {
class ConfigureGeneral;
}
@@ -18,11 +20,11 @@ public:
explicit ConfigureGeneral(QWidget* parent = nullptr);
~ConfigureGeneral();
+ void PopulateHotkeyList(const HotkeyRegistry& registry);
void applyConfiguration();
private:
void setConfiguration();
-private:
std::unique_ptr<Ui::ConfigureGeneral> ui;
};
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)
this->setConfiguration();
}
-ConfigureGraphics::~ConfigureGraphics() {}
+ConfigureGraphics::~ConfigureGraphics() = default;
enum class Resolution : int {
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::
this->setConfiguration();
}
-ConfigureSystem::~ConfigureSystem() {}
+ConfigureSystem::~ConfigureSystem() = default;
void ConfigureSystem::setConfiguration() {
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(
SurfacePicture::SurfacePicture(QWidget* parent, GraphicsSurfaceWidget* surface_widget_)
: QLabel(parent), surface_widget(surface_widget_) {}
-SurfacePicture::~SurfacePicture() {}
+
+SurfacePicture::~SurfacePicture() = default;
void SurfacePicture::mousePressEvent(QMouseEvent* event) {
// 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 {
public:
explicit SurfacePicture(QWidget* parent = nullptr,
GraphicsSurfaceWidget* surface_widget = nullptr);
- ~SurfacePicture();
+ ~SurfacePicture() override;
protected slots:
- virtual void mouseMoveEvent(QMouseEvent* event);
- virtual void mousePressEvent(QMouseEvent* event);
+ void mouseMoveEvent(QMouseEvent* event) override;
+ void mousePressEvent(QMouseEvent* event) override;
private:
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 @@
#include "core/hle/kernel/timer.h"
#include "core/hle/kernel/wait_object.h"
-WaitTreeItem::~WaitTreeItem() {}
+WaitTreeItem::~WaitTreeItem() = default;
QColor WaitTreeItem::GetColor() const {
return QColor(Qt::GlobalColor::black);
@@ -316,7 +316,7 @@ std::vector<std::unique_ptr<WaitTreeItem>> WaitTreeEvent::GetChildren() const {
list.push_back(std::make_unique<WaitTreeText>(
tr("reset type = %1")
- .arg(GetResetTypeQString(static_cast<const Kernel::Event&>(object).reset_type))));
+ .arg(GetResetTypeQString(static_cast<const Kernel::Event&>(object).GetResetType()))));
return list;
}
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;
class WaitTreeItem : public QObject {
Q_OBJECT
public:
+ ~WaitTreeItem() override;
+
virtual bool IsExpandable() const;
virtual std::vector<std::unique_ptr<WaitTreeItem>> GetChildren() const;
virtual QString GetText() const = 0;
virtual QColor GetColor() const;
- virtual ~WaitTreeItem();
+
void Expand();
WaitTreeItem* Parent() const;
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) {
}
search_field->setFilterResult(rowCount, rowCount);
} else {
- QStandardItem* child_file;
- QString file_path, file_name, file_title, file_programmid;
int result_count = 0;
for (int i = 0; i < rowCount; ++i) {
- child_file = item_model->item(i, 0);
- file_path = child_file->data(GameListItemPath::FullPathRole).toString().toLower();
- file_name = file_path.mid(file_path.lastIndexOf("/") + 1);
- file_title = child_file->data(GameListItemPath::TitleRole).toString().toLower();
- file_programmid =
+ const QStandardItem* child_file = item_model->item(i, 0);
+ const QString file_path =
+ child_file->data(GameListItemPath::FullPathRole).toString().toLower();
+ QString file_name = file_path.mid(file_path.lastIndexOf('/') + 1);
+ const QString file_title =
+ child_file->data(GameListItemPath::TitleRole).toString().toLower();
+ const QString file_programmid =
child_file->data(GameListItemPath::ProgramIdRole).toString().toLower();
// Only items which filename in combination with its title contains all words
@@ -258,18 +258,20 @@ void GameList::AddEntry(const QList<QStandardItem*>& entry_items) {
void GameList::ValidateEntry(const QModelIndex& item) {
// We don't care about the individual QStandardItem that was selected, but its row.
- int row = item_model->itemFromIndex(item)->row();
- QStandardItem* child_file = item_model->invisibleRootItem()->child(row, COLUMN_NAME);
- QString file_path = child_file->data(GameListItemPath::FullPathRole).toString();
+ const int row = item_model->itemFromIndex(item)->row();
+ const QStandardItem* child_file = item_model->invisibleRootItem()->child(row, COLUMN_NAME);
+ const QString file_path = child_file->data(GameListItemPath::FullPathRole).toString();
if (file_path.isEmpty())
return;
- std::string std_file_path(file_path.toStdString());
- if (!FileUtil::Exists(std_file_path))
+
+ if (!QFileInfo::exists(file_path))
return;
- if (FileUtil::IsDirectory(std_file_path)) {
- QDir dir(std_file_path.c_str());
- QStringList matching_main = dir.entryList(QStringList("main"), QDir::Files);
+
+ const QFileInfo file_info{file_path};
+ if (file_info.isDir()) {
+ const QDir dir{file_path};
+ const QStringList matching_main = dir.entryList(QStringList("main"), QDir::Files);
if (matching_main.size() == 1) {
emit GameChosen(dir.path() + DIR_SEP + matching_main[0]);
}
@@ -368,21 +370,23 @@ void GameList::LoadInterfaceLayout() {
const QStringList GameList::supported_file_extensions = {"nso", "nro", "nca", "xci"};
static bool HasSupportedFileExtension(const std::string& file_name) {
- QFileInfo file = QFileInfo(file_name.c_str());
+ const QFileInfo file = QFileInfo(QString::fromStdString(file_name));
return GameList::supported_file_extensions.contains(file.suffix(), Qt::CaseInsensitive);
}
static bool IsExtractedNCAMain(const std::string& file_name) {
- return QFileInfo(file_name.c_str()).fileName() == "main";
+ return QFileInfo(QString::fromStdString(file_name)).fileName() == "main";
}
static QString FormatGameName(const std::string& physical_name) {
- QFileInfo file_info(physical_name.c_str());
+ const QString physical_name_as_qstring = QString::fromStdString(physical_name);
+ const QFileInfo file_info(physical_name_as_qstring);
+
if (IsExtractedNCAMain(physical_name)) {
return file_info.dir().path();
- } else {
- return QString::fromStdString(physical_name);
}
+
+ return physical_name_as_qstring;
}
void 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 @@
#pragma once
#include <atomic>
+#include <utility>
#include <QImage>
#include <QRunnable>
#include <QStandardItem>
@@ -27,9 +28,8 @@ static QPixmap GetDefaultIcon(bool large) {
class GameListItem : public QStandardItem {
public:
- GameListItem() : QStandardItem() {}
- GameListItem(const QString& string) : QStandardItem(string) {}
- virtual ~GameListItem() override {}
+ GameListItem() = default;
+ explicit GameListItem(const QString& string) : QStandardItem(string) {}
};
/**
@@ -45,9 +45,8 @@ public:
static const int TitleRole = Qt::UserRole + 2;
static const int ProgramIdRole = Qt::UserRole + 3;
- GameListItemPath() : GameListItem() {}
- GameListItemPath(const QString& game_path, const std::vector<u8>& smdh_data, u64 program_id)
- : GameListItem() {
+ GameListItemPath() = default;
+ GameListItemPath(const QString& game_path, const std::vector<u8>& smdh_data, u64 program_id) {
setData(game_path, FullPathRole);
setData(qulonglong(program_id), ProgramIdRole);
}
@@ -75,8 +74,8 @@ class GameListItemSize : public GameListItem {
public:
static const int SizeRole = Qt::UserRole + 1;
- GameListItemSize() : GameListItem() {}
- GameListItemSize(const qulonglong size_bytes) : GameListItem() {
+ GameListItemSize() = default;
+ explicit GameListItemSize(const qulonglong size_bytes) {
setData(size_bytes, SizeRole);
}
@@ -111,7 +110,7 @@ class GameListWorker : public QObject, public QRunnable {
public:
GameListWorker(QString dir_path, bool deep_scan)
- : QObject(), QRunnable(), dir_path(dir_path), deep_scan(deep_scan) {}
+ : dir_path(std::move(dir_path)), deep_scan(deep_scan) {}
public slots:
/// Starts the processing of directory tree information.
diff --git a/src/yuzu/hotkeys.cpp b/src/yuzu/hotkeys.cpp
index 61acb38ee..dce399774 100644
--- a/src/yuzu/hotkeys.cpp
+++ b/src/yuzu/hotkeys.cpp
@@ -10,58 +10,53 @@
#include "yuzu/hotkeys.h"
#include "yuzu/ui_settings.h"
-struct Hotkey {
- Hotkey() : shortcut(nullptr), context(Qt::WindowShortcut) {}
+HotkeyRegistry::HotkeyRegistry() = default;
+HotkeyRegistry::~HotkeyRegistry() = default;
- QKeySequence keyseq;
- QShortcut* shortcut;
- Qt::ShortcutContext context;
-};
-
-typedef std::map<QString, Hotkey> HotkeyMap;
-typedef std::map<QString, HotkeyMap> HotkeyGroupMap;
-
-HotkeyGroupMap hotkey_groups;
-
-void SaveHotkeys() {
- UISettings::values.shortcuts.clear();
- for (auto group : hotkey_groups) {
- for (auto hotkey : group.second) {
- UISettings::values.shortcuts.emplace_back(
- UISettings::Shortcut(group.first + "/" + hotkey.first,
- UISettings::ContextualShortcut(hotkey.second.keyseq.toString(),
- hotkey.second.context)));
- }
- }
-}
-
-void LoadHotkeys() {
+void HotkeyRegistry::LoadHotkeys() {
// Make sure NOT to use a reference here because it would become invalid once we call
// beginGroup()
for (auto shortcut : UISettings::values.shortcuts) {
- QStringList cat = shortcut.first.split("/");
+ const QStringList cat = shortcut.first.split('/');
Q_ASSERT(cat.size() >= 2);
// RegisterHotkey assigns default keybindings, so use old values as default parameters
Hotkey& hk = hotkey_groups[cat[0]][cat[1]];
if (!shortcut.second.first.isEmpty()) {
hk.keyseq = QKeySequence::fromString(shortcut.second.first);
- hk.context = (Qt::ShortcutContext)shortcut.second.second;
+ hk.context = static_cast<Qt::ShortcutContext>(shortcut.second.second);
}
if (hk.shortcut)
hk.shortcut->setKey(hk.keyseq);
}
}
-void RegisterHotkey(const QString& group, const QString& action, const QKeySequence& default_keyseq,
- Qt::ShortcutContext default_context) {
- if (hotkey_groups[group].find(action) == hotkey_groups[group].end()) {
- hotkey_groups[group][action].keyseq = default_keyseq;
- hotkey_groups[group][action].context = default_context;
+void HotkeyRegistry::SaveHotkeys() {
+ UISettings::values.shortcuts.clear();
+ for (const auto& group : hotkey_groups) {
+ for (const auto& hotkey : group.second) {
+ UISettings::values.shortcuts.emplace_back(
+ UISettings::Shortcut(group.first + '/' + hotkey.first,
+ UISettings::ContextualShortcut(hotkey.second.keyseq.toString(),
+ hotkey.second.context)));
+ }
}
}
-QShortcut* GetHotkey(const QString& group, const QString& action, QWidget* widget) {
+void HotkeyRegistry::RegisterHotkey(const QString& group, const QString& action,
+ const QKeySequence& default_keyseq,
+ Qt::ShortcutContext default_context) {
+ auto& hotkey_group = hotkey_groups[group];
+ if (hotkey_group.find(action) != hotkey_group.end()) {
+ return;
+ }
+
+ auto& hotkey_action = hotkey_groups[group][action];
+ hotkey_action.keyseq = default_keyseq;
+ hotkey_action.context = default_context;
+}
+
+QShortcut* HotkeyRegistry::GetHotkey(const QString& group, const QString& action, QWidget* widget) {
Hotkey& hk = hotkey_groups[group][action];
if (!hk.shortcut)
@@ -72,10 +67,12 @@ QShortcut* GetHotkey(const QString& group, const QString& action, QWidget* widge
GHotkeysDialog::GHotkeysDialog(QWidget* parent) : QWidget(parent) {
ui.setupUi(this);
+}
- for (auto group : hotkey_groups) {
+void GHotkeysDialog::Populate(const HotkeyRegistry& registry) {
+ for (const auto& group : registry.hotkey_groups) {
QTreeWidgetItem* toplevel_item = new QTreeWidgetItem(QStringList(group.first));
- for (auto hotkey : group.second) {
+ for (const auto& hotkey : group.second) {
QStringList columns;
columns << hotkey.first << hotkey.second.keyseq.toString();
QTreeWidgetItem* item = new QTreeWidgetItem(columns);
diff --git a/src/yuzu/hotkeys.h b/src/yuzu/hotkeys.h
index a4ccc193b..f38e6c002 100644
--- a/src/yuzu/hotkeys.h
+++ b/src/yuzu/hotkeys.h
@@ -4,6 +4,7 @@
#pragma once
+#include <map>
#include "ui_hotkeys.h"
class QDialog;
@@ -11,47 +12,69 @@ class QKeySequence;
class QSettings;
class QShortcut;
-/**
- * Register a hotkey.
- *
- * @param group General group this hotkey belongs to (e.g. "Main Window", "Debugger")
- * @param action Name of the action (e.g. "Start Emulation", "Load Image")
- * @param default_keyseq Default key sequence to assign if the hotkey wasn't present in the settings
- * file before
- * @param default_context Default context to assign if the hotkey wasn't present in the settings
- * file before
- * @warning Both the group and action strings will be displayed in the hotkey settings dialog
- */
-void RegisterHotkey(const QString& group, const QString& action,
- const QKeySequence& default_keyseq = QKeySequence(),
- Qt::ShortcutContext default_context = Qt::WindowShortcut);
-
-/**
- * Returns a QShortcut object whose activated() signal can be connected to other QObjects' slots.
- *
- * @param group General group this hotkey belongs to (e.g. "Main Window", "Debugger").
- * @param action Name of the action (e.g. "Start Emulation", "Load Image").
- * @param widget Parent widget of the returned QShortcut.
- * @warning If multiple QWidgets' call this function for the same action, the returned QShortcut
- * will be the same. Thus, you shouldn't rely on the caller really being the QShortcut's parent.
- */
-QShortcut* GetHotkey(const QString& group, const QString& action, QWidget* widget);
-
-/**
- * Saves all registered hotkeys to the settings file.
- *
- * @note Each hotkey group will be stored a settings group; For each hotkey inside that group, a
- * settings group will be created to store the key sequence and the hotkey context.
- */
-void SaveHotkeys();
-
-/**
- * Loads hotkeys from the settings file.
- *
- * @note Yet unregistered hotkeys which are present in the settings will automatically be
- * registered.
- */
-void LoadHotkeys();
+class HotkeyRegistry final {
+public:
+ friend class GHotkeysDialog;
+
+ explicit HotkeyRegistry();
+ ~HotkeyRegistry();
+
+ /**
+ * Loads hotkeys from the settings file.
+ *
+ * @note Yet unregistered hotkeys which are present in the settings will automatically be
+ * registered.
+ */
+ void LoadHotkeys();
+
+ /**
+ * Saves all registered hotkeys to the settings file.
+ *
+ * @note Each hotkey group will be stored a settings group; For each hotkey inside that group, a
+ * settings group will be created to store the key sequence and the hotkey context.
+ */
+ void SaveHotkeys();
+
+ /**
+ * Returns a QShortcut object whose activated() signal can be connected to other QObjects'
+ * slots.
+ *
+ * @param group General group this hotkey belongs to (e.g. "Main Window", "Debugger").
+ * @param action Name of the action (e.g. "Start Emulation", "Load Image").
+ * @param widget Parent widget of the returned QShortcut.
+ * @warning If multiple QWidgets' call this function for the same action, the returned QShortcut
+ * will be the same. Thus, you shouldn't rely on the caller really being the
+ * QShortcut's parent.
+ */
+ QShortcut* GetHotkey(const QString& group, const QString& action, QWidget* widget);
+
+ /**
+ * Register a hotkey.
+ *
+ * @param group General group this hotkey belongs to (e.g. "Main Window", "Debugger")
+ * @param action Name of the action (e.g. "Start Emulation", "Load Image")
+ * @param default_keyseq Default key sequence to assign if the hotkey wasn't present in the
+ * settings file before
+ * @param default_context Default context to assign if the hotkey wasn't present in the settings
+ * file before
+ * @warning Both the group and action strings will be displayed in the hotkey settings dialog
+ */
+ void RegisterHotkey(const QString& group, const QString& action,
+ const QKeySequence& default_keyseq = {},
+ Qt::ShortcutContext default_context = Qt::WindowShortcut);
+
+private:
+ struct Hotkey {
+ QKeySequence keyseq;
+ QShortcut* shortcut = nullptr;
+ Qt::ShortcutContext context = Qt::WindowShortcut;
+ };
+
+ using HotkeyMap = std::map<QString, Hotkey>;
+ using HotkeyGroupMap = std::map<QString, HotkeyMap>;
+
+ HotkeyGroupMap hotkey_groups;
+};
class GHotkeysDialog : public QWidget {
Q_OBJECT
@@ -59,6 +82,8 @@ class GHotkeysDialog : public QWidget {
public:
explicit GHotkeysDialog(QWidget* parent = nullptr);
+ void Populate(const HotkeyRegistry& registry);
+
private:
Ui::hotkeys ui;
};
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index eac41553a..17ed62c72 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -81,6 +81,8 @@ static void ShowCalloutMessage(const QString& message, CalloutFlag flag) {
void GMainWindow::ShowCallouts() {}
+const int GMainWindow::max_recent_files_item;
+
GMainWindow::GMainWindow() : config(new Config()), emu_thread(nullptr) {
debug_context = Tegra::DebugContext::Construct();
@@ -206,43 +208,46 @@ void GMainWindow::InitializeRecentFileMenuActions() {
}
void GMainWindow::InitializeHotkeys() {
- RegisterHotkey("Main Window", "Load File", QKeySequence::Open);
- RegisterHotkey("Main Window", "Start Emulation");
- RegisterHotkey("Main Window", "Continue/Pause", QKeySequence(Qt::Key_F4));
- RegisterHotkey("Main Window", "Fullscreen", QKeySequence::FullScreen);
- RegisterHotkey("Main Window", "Exit Fullscreen", QKeySequence(Qt::Key_Escape),
- Qt::ApplicationShortcut);
- RegisterHotkey("Main Window", "Toggle Speed Limit", QKeySequence("CTRL+Z"),
- Qt::ApplicationShortcut);
- LoadHotkeys();
-
- connect(GetHotkey("Main Window", "Load File", this), &QShortcut::activated, this,
- &GMainWindow::OnMenuLoadFile);
- connect(GetHotkey("Main Window", "Start Emulation", this), &QShortcut::activated, this,
- &GMainWindow::OnStartGame);
- connect(GetHotkey("Main Window", "Continue/Pause", this), &QShortcut::activated, this, [&] {
- if (emulation_running) {
- if (emu_thread->IsRunning()) {
- OnPauseGame();
- } else {
- OnStartGame();
- }
- }
- });
- connect(GetHotkey("Main Window", "Fullscreen", render_window), &QShortcut::activated,
- ui.action_Fullscreen, &QAction::trigger);
- connect(GetHotkey("Main Window", "Fullscreen", render_window), &QShortcut::activatedAmbiguously,
- ui.action_Fullscreen, &QAction::trigger);
- connect(GetHotkey("Main Window", "Exit Fullscreen", this), &QShortcut::activated, this, [&] {
- if (emulation_running) {
- ui.action_Fullscreen->setChecked(false);
- ToggleFullscreen();
- }
- });
- connect(GetHotkey("Main Window", "Toggle Speed Limit", this), &QShortcut::activated, this, [&] {
- Settings::values.toggle_framelimit = !Settings::values.toggle_framelimit;
- UpdateStatusBar();
- });
+ hotkey_registry.RegisterHotkey("Main Window", "Load File", QKeySequence::Open);
+ hotkey_registry.RegisterHotkey("Main Window", "Start Emulation");
+ hotkey_registry.RegisterHotkey("Main Window", "Continue/Pause", QKeySequence(Qt::Key_F4));
+ hotkey_registry.RegisterHotkey("Main Window", "Fullscreen", QKeySequence::FullScreen);
+ hotkey_registry.RegisterHotkey("Main Window", "Exit Fullscreen", QKeySequence(Qt::Key_Escape),
+ Qt::ApplicationShortcut);
+ hotkey_registry.RegisterHotkey("Main Window", "Toggle Speed Limit", QKeySequence("CTRL+Z"),
+ Qt::ApplicationShortcut);
+ hotkey_registry.LoadHotkeys();
+
+ connect(hotkey_registry.GetHotkey("Main Window", "Load File", this), &QShortcut::activated,
+ this, &GMainWindow::OnMenuLoadFile);
+ connect(hotkey_registry.GetHotkey("Main Window", "Start Emulation", this),
+ &QShortcut::activated, this, &GMainWindow::OnStartGame);
+ connect(hotkey_registry.GetHotkey("Main Window", "Continue/Pause", this), &QShortcut::activated,
+ this, [&] {
+ if (emulation_running) {
+ if (emu_thread->IsRunning()) {
+ OnPauseGame();
+ } else {
+ OnStartGame();
+ }
+ }
+ });
+ connect(hotkey_registry.GetHotkey("Main Window", "Fullscreen", render_window),
+ &QShortcut::activated, ui.action_Fullscreen, &QAction::trigger);
+ connect(hotkey_registry.GetHotkey("Main Window", "Fullscreen", render_window),
+ &QShortcut::activatedAmbiguously, ui.action_Fullscreen, &QAction::trigger);
+ connect(hotkey_registry.GetHotkey("Main Window", "Exit Fullscreen", this),
+ &QShortcut::activated, this, [&] {
+ if (emulation_running) {
+ ui.action_Fullscreen->setChecked(false);
+ ToggleFullscreen();
+ }
+ });
+ connect(hotkey_registry.GetHotkey("Main Window", "Toggle Speed Limit", this),
+ &QShortcut::activated, this, [&] {
+ Settings::values.toggle_framelimit = !Settings::values.toggle_framelimit;
+ UpdateStatusBar();
+ });
}
void GMainWindow::SetDefaultUIGeometry() {
@@ -321,7 +326,8 @@ void GMainWindow::ConnectMenuEvents() {
connect(ui.action_Show_Status_Bar, &QAction::triggered, statusBar(), &QStatusBar::setVisible);
// Fullscreen
- ui.action_Fullscreen->setShortcut(GetHotkey("Main Window", "Fullscreen", this)->key());
+ ui.action_Fullscreen->setShortcut(
+ hotkey_registry.GetHotkey("Main Window", "Fullscreen", this)->key());
connect(ui.action_Fullscreen, &QAction::triggered, this, &GMainWindow::ToggleFullscreen);
// Help
@@ -477,7 +483,7 @@ bool GMainWindow::LoadROM(const QString& filename) {
case Core::System::ResultStatus::ErrorVideoCore:
QMessageBox::critical(
- this, tr("An error occured in the video core."),
+ this, tr("An error occurred initializing the video core."),
tr("yuzu has encountered an error while running the video core, please see the "
"log for more details."
"For more information on accessing the log, please see the following page: "
@@ -491,7 +497,7 @@ bool GMainWindow::LoadROM(const QString& filename) {
default:
QMessageBox::critical(
this, tr("Error while loading ROM!"),
- tr("An unknown error occured. Please see the log for more details."));
+ tr("An unknown error occurred. Please see the log for more details."));
break;
}
return false;
@@ -579,11 +585,11 @@ void GMainWindow::StoreRecentFile(const QString& filename) {
}
void GMainWindow::UpdateRecentFiles() {
- unsigned int num_recent_files =
- std::min(UISettings::values.recent_files.size(), static_cast<int>(max_recent_files_item));
+ const int num_recent_files =
+ std::min(UISettings::values.recent_files.size(), max_recent_files_item);
- for (unsigned int i = 0; i < num_recent_files; i++) {
- QString text = QString("&%1. %2").arg(i + 1).arg(
+ for (int i = 0; i < num_recent_files; i++) {
+ const QString text = QString("&%1. %2").arg(i + 1).arg(
QFileInfo(UISettings::values.recent_files[i]).fileName());
actions_recent_files[i]->setText(text);
actions_recent_files[i]->setData(UISettings::values.recent_files[i]);
@@ -595,12 +601,8 @@ void GMainWindow::UpdateRecentFiles() {
actions_recent_files[j]->setVisible(false);
}
- // Grey out the recent files menu if the list is empty
- if (num_recent_files == 0) {
- ui.menu_recent_files->setEnabled(false);
- } else {
- ui.menu_recent_files->setEnabled(true);
- }
+ // Enable the recent files menu if the list isn't empty
+ ui.menu_recent_files->setEnabled(num_recent_files != 0);
}
void GMainWindow::OnGameListLoadFile(QString game_path) {
@@ -631,9 +633,15 @@ void GMainWindow::OnMenuLoadFile() {
}
void GMainWindow::OnMenuLoadFolder() {
- QDir dir = QFileDialog::getExistingDirectory(this, tr("Open Extracted ROM Directory"));
+ const QString dir_path =
+ QFileDialog::getExistingDirectory(this, tr("Open Extracted ROM Directory"));
+
+ if (dir_path.isNull()) {
+ return;
+ }
- QStringList matching_main = dir.entryList(QStringList("main"), QDir::Files);
+ const QDir dir{dir_path};
+ const QStringList matching_main = dir.entryList(QStringList("main"), QDir::Files);
if (matching_main.size() == 1) {
BootGame(dir.path() + DIR_SEP + matching_main[0]);
} else {
@@ -654,9 +662,8 @@ void GMainWindow::OnMenuRecentFile() {
QAction* action = qobject_cast<QAction*>(sender());
assert(action);
- QString filename = action->data().toString();
- QFileInfo file_info(filename);
- if (file_info.exists()) {
+ const QString filename = action->data().toString();
+ if (QFileInfo::exists(filename)) {
BootGame(filename);
} else {
// Display an error message and remove the file from the list.
@@ -754,7 +761,7 @@ void GMainWindow::ToggleWindowMode() {
}
void GMainWindow::OnConfigure() {
- ConfigureDialog configureDialog(this);
+ ConfigureDialog configureDialog(this, hotkey_registry);
auto old_theme = UISettings::values.theme;
auto result = configureDialog.exec();
if (result == QDialog::Accepted) {
@@ -893,7 +900,7 @@ void GMainWindow::closeEvent(QCloseEvent* event) {
UISettings::values.first_start = false;
game_list->SaveInterfaceLayout();
- SaveHotkeys();
+ hotkey_registry.SaveHotkeys();
// Shutdown session if the emu thread is active...
if (emu_thread != nullptr)
@@ -947,15 +954,14 @@ void GMainWindow::UpdateUITheme() {
QStringList theme_paths(default_theme_paths);
if (UISettings::values.theme != UISettings::themes[0].second &&
!UISettings::values.theme.isEmpty()) {
- QString theme_uri(":" + UISettings::values.theme + "/style.qss");
+ const QString theme_uri(":" + UISettings::values.theme + "/style.qss");
QFile f(theme_uri);
- if (!f.exists()) {
- LOG_ERROR(Frontend, "Unable to set style, stylesheet file not found");
- } else {
- f.open(QFile::ReadOnly | QFile::Text);
+ if (f.open(QFile::ReadOnly | QFile::Text)) {
QTextStream ts(&f);
qApp->setStyleSheet(ts.readAll());
GMainWindow::setStyleSheet(ts.readAll());
+ } else {
+ LOG_ERROR(Frontend, "Unable to set style, stylesheet file not found");
}
theme_paths.append(QStringList{":/icons/default", ":/icons/" + UISettings::values.theme});
QIcon::setThemeName(":/icons/" + UISettings::values.theme);
diff --git a/src/yuzu/main.h b/src/yuzu/main.h
index 074bba3f9..6e335b8f8 100644
--- a/src/yuzu/main.h
+++ b/src/yuzu/main.h
@@ -9,6 +9,7 @@
#include <QTimer>
#include "core/core.h"
#include "ui_main.h"
+#include "yuzu/hotkeys.h"
class Config;
class EmuThread;
@@ -43,7 +44,7 @@ public:
void filterBarSetChecked(bool state);
void UpdateUITheme();
GMainWindow();
- ~GMainWindow();
+ ~GMainWindow() override;
signals:
@@ -172,6 +173,8 @@ private:
// stores default icon theme search paths for the platform
QStringList default_theme_paths;
+ HotkeyRegistry hotkey_registry;
+
protected:
void dropEvent(QDropEvent* event) override;
void dragEnterEvent(QDragEnterEvent* event) override;
diff --git a/src/yuzu_cmd/yuzu.cpp b/src/yuzu_cmd/yuzu.cpp
index b23213cf6..d637dbd0c 100644
--- a/src/yuzu_cmd/yuzu.cpp
+++ b/src/yuzu_cmd/yuzu.cpp
@@ -193,7 +193,7 @@ int main(int argc, char** argv) {
LOG_CRITICAL(Frontend, "Failed to determine system mode!");
return -1;
case Core::System::ResultStatus::ErrorVideoCore:
- LOG_CRITICAL(Frontend, "VideoCore not initialized");
+ LOG_CRITICAL(Frontend, "Failed to initialize VideoCore!");
return -1;
case Core::System::ResultStatus::Success:
break; // Expected case