summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/CMakeLists.txt1
-rw-r--r--src/audio_core/audio_renderer.cpp62
-rw-r--r--src/audio_core/audio_renderer.h6
-rw-r--r--src/audio_core/command_generator.cpp76
-rw-r--r--src/audio_core/command_generator.h8
-rw-r--r--src/audio_core/info_updater.cpp3
-rw-r--r--src/audio_core/voice_context.cpp88
-rw-r--r--src/audio_core/voice_context.h13
-rw-r--r--src/core/file_sys/patch_manager.cpp24
-rw-r--r--src/core/file_sys/patch_manager.h3
-rw-r--r--src/core/file_sys/sdmc_factory.cpp31
-rw-r--r--src/core/file_sys/sdmc_factory.h6
-rw-r--r--src/core/hle/service/audio/audren_u.cpp12
-rw-r--r--src/core/hle/service/filesystem/filesystem.cpp24
-rw-r--r--src/core/hle/service/filesystem/filesystem.h1
-rw-r--r--src/core/hle/service/mii/manager.cpp5
-rwxr-xr-xsrc/input_common/analog_from_button.cpp1
-rw-r--r--src/video_core/cdma_pusher.cpp3
-rw-r--r--src/video_core/command_classes/codecs/codec.cpp85
-rw-r--r--src/video_core/command_classes/codecs/codec.h12
-rw-r--r--src/video_core/command_classes/codecs/h264.cpp207
-rw-r--r--src/video_core/command_classes/codecs/h264.h132
-rw-r--r--src/video_core/command_classes/codecs/vp9.cpp4
-rw-r--r--src/video_core/command_classes/codecs/vp9_types.h307
-rw-r--r--src/video_core/command_classes/nvdec.cpp17
-rw-r--r--src/video_core/command_classes/nvdec.h8
-rw-r--r--src/video_core/command_classes/nvdec_common.h103
-rw-r--r--src/video_core/host_shaders/CMakeLists.txt5
-rw-r--r--src/video_core/renderer_base.h2
-rw-r--r--src/video_core/renderer_opengl/gl_device.cpp58
-rw-r--r--src/video_core/renderer_opengl/gl_device.h3
-rw-r--r--src/video_core/renderer_opengl/gl_texture_cache.cpp26
-rw-r--r--src/video_core/renderer_opengl/renderer_opengl.h4
-rw-r--r--src/video_core/renderer_vulkan/renderer_vulkan.h4
-rw-r--r--src/video_core/texture_cache/texture_cache.h3
-rw-r--r--src/video_core/vulkan_common/vulkan_device.cpp21
-rw-r--r--src/video_core/vulkan_common/vulkan_device.h3
-rw-r--r--src/yuzu/debugger/profiler.cpp9
-rw-r--r--src/yuzu/game_list.cpp12
-rw-r--r--src/yuzu/game_list.h7
-rw-r--r--src/yuzu/main.cpp22
-rw-r--r--src/yuzu/main.h7
42 files changed, 933 insertions, 495 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index ed94e5d4e..65a4922ea 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -55,6 +55,7 @@ if (MSVC)
55 /we4018 # 'expression': signed/unsigned mismatch 55 /we4018 # 'expression': signed/unsigned mismatch
56 /we4062 # Enumerator 'identifier' in a switch of enum 'enumeration' is not handled 56 /we4062 # Enumerator 'identifier' in a switch of enum 'enumeration' is not handled
57 /we4101 # 'identifier': unreferenced local variable 57 /we4101 # 'identifier': unreferenced local variable
58 /we4189 # 'identifier': local variable is initialized but not referenced
58 /we4265 # 'class': class has virtual functions, but destructor is not virtual 59 /we4265 # 'class': class has virtual functions, but destructor is not virtual
59 /we4267 # 'var': conversion from 'size_t' to 'type', possible loss of data 60 /we4267 # 'var': conversion from 'size_t' to 'type', possible loss of data
60 /we4305 # 'context': truncation from 'type1' to 'type2' 61 /we4305 # 'context': truncation from 'type1' to 'type2'
diff --git a/src/audio_core/audio_renderer.cpp b/src/audio_core/audio_renderer.cpp
index 80ffddb10..ccd5ca6cc 100644
--- a/src/audio_core/audio_renderer.cpp
+++ b/src/audio_core/audio_renderer.cpp
@@ -12,6 +12,7 @@
12#include "audio_core/voice_context.h" 12#include "audio_core/voice_context.h"
13#include "common/logging/log.h" 13#include "common/logging/log.h"
14#include "common/settings.h" 14#include "common/settings.h"
15#include "core/core_timing.h"
15#include "core/memory.h" 16#include "core/memory.h"
16 17
17namespace { 18namespace {
@@ -68,7 +69,9 @@ namespace {
68} // namespace 69} // namespace
69 70
70namespace AudioCore { 71namespace AudioCore {
71AudioRenderer::AudioRenderer(Core::Timing::CoreTiming& core_timing, Core::Memory::Memory& memory_, 72constexpr s32 NUM_BUFFERS = 2;
73
74AudioRenderer::AudioRenderer(Core::Timing::CoreTiming& core_timing_, Core::Memory::Memory& memory_,
72 AudioCommon::AudioRendererParameter params, 75 AudioCommon::AudioRendererParameter params,
73 Stream::ReleaseCallback&& release_callback, 76 Stream::ReleaseCallback&& release_callback,
74 std::size_t instance_number) 77 std::size_t instance_number)
@@ -77,7 +80,8 @@ AudioRenderer::AudioRenderer(Core::Timing::CoreTiming& core_timing, Core::Memory
77 sink_context(params.sink_count), splitter_context(), 80 sink_context(params.sink_count), splitter_context(),
78 voices(params.voice_count), memory{memory_}, 81 voices(params.voice_count), memory{memory_},
79 command_generator(worker_params, voice_context, mix_context, splitter_context, effect_context, 82 command_generator(worker_params, voice_context, mix_context, splitter_context, effect_context,
80 memory) { 83 memory),
84 core_timing{core_timing_} {
81 behavior_info.SetUserRevision(params.revision); 85 behavior_info.SetUserRevision(params.revision);
82 splitter_context.Initialize(behavior_info, params.splitter_count, 86 splitter_context.Initialize(behavior_info, params.splitter_count,
83 params.num_splitter_send_channels); 87 params.num_splitter_send_channels);
@@ -86,16 +90,27 @@ AudioRenderer::AudioRenderer(Core::Timing::CoreTiming& core_timing, Core::Memory
86 stream = audio_out->OpenStream( 90 stream = audio_out->OpenStream(
87 core_timing, params.sample_rate, AudioCommon::STREAM_NUM_CHANNELS, 91 core_timing, params.sample_rate, AudioCommon::STREAM_NUM_CHANNELS,
88 fmt::format("AudioRenderer-Instance{}", instance_number), std::move(release_callback)); 92 fmt::format("AudioRenderer-Instance{}", instance_number), std::move(release_callback));
89 audio_out->StartStream(stream); 93 process_event = Core::Timing::CreateEvent(
90 94 fmt::format("AudioRenderer-Instance{}-Process", instance_number),
91 QueueMixedBuffer(0); 95 [this](std::uintptr_t, std::chrono::nanoseconds) { ReleaseAndQueueBuffers(); });
92 QueueMixedBuffer(1); 96 for (s32 i = 0; i < NUM_BUFFERS; ++i) {
93 QueueMixedBuffer(2); 97 QueueMixedBuffer(i);
94 QueueMixedBuffer(3); 98 }
95} 99}
96 100
97AudioRenderer::~AudioRenderer() = default; 101AudioRenderer::~AudioRenderer() = default;
98 102
103ResultCode AudioRenderer::Start() {
104 audio_out->StartStream(stream);
105 ReleaseAndQueueBuffers();
106 return ResultSuccess;
107}
108
109ResultCode AudioRenderer::Stop() {
110 audio_out->StopStream(stream);
111 return ResultSuccess;
112}
113
99u32 AudioRenderer::GetSampleRate() const { 114u32 AudioRenderer::GetSampleRate() const {
100 return worker_params.sample_rate; 115 return worker_params.sample_rate;
101} 116}
@@ -114,7 +129,7 @@ Stream::State AudioRenderer::GetStreamState() const {
114 129
115ResultCode AudioRenderer::UpdateAudioRenderer(const std::vector<u8>& input_params, 130ResultCode AudioRenderer::UpdateAudioRenderer(const std::vector<u8>& input_params,
116 std::vector<u8>& output_params) { 131 std::vector<u8>& output_params) {
117 132 std::scoped_lock lock{mutex};
118 InfoUpdater info_updater{input_params, output_params, behavior_info}; 133 InfoUpdater info_updater{input_params, output_params, behavior_info};
119 134
120 if (!info_updater.UpdateBehaviorInfo(behavior_info)) { 135 if (!info_updater.UpdateBehaviorInfo(behavior_info)) {
@@ -194,9 +209,6 @@ ResultCode AudioRenderer::UpdateAudioRenderer(const std::vector<u8>& input_param
194 LOG_ERROR(Audio, "Audio buffers were not consumed!"); 209 LOG_ERROR(Audio, "Audio buffers were not consumed!");
195 return AudioCommon::Audren::ERR_INVALID_PARAMETERS; 210 return AudioCommon::Audren::ERR_INVALID_PARAMETERS;
196 } 211 }
197
198 ReleaseAndQueueBuffers();
199
200 return ResultSuccess; 212 return ResultSuccess;
201} 213}
202 214
@@ -220,10 +232,8 @@ void AudioRenderer::QueueMixedBuffer(Buffer::Tag tag) {
220 command_generator.PostCommand(); 232 command_generator.PostCommand();
221 // Base sample size 233 // Base sample size
222 std::size_t BUFFER_SIZE{worker_params.sample_count}; 234 std::size_t BUFFER_SIZE{worker_params.sample_count};
223 // Samples 235 // Samples, making sure to clear
224 std::vector<s16> buffer(BUFFER_SIZE * stream->GetNumChannels()); 236 std::vector<s16> buffer(BUFFER_SIZE * stream->GetNumChannels(), 0);
225 // Make sure to clear our samples
226 std::memset(buffer.data(), 0, buffer.size() * sizeof(s16));
227 237
228 if (sink_context.InUse()) { 238 if (sink_context.InUse()) {
229 const auto stream_channel_count = stream->GetNumChannels(); 239 const auto stream_channel_count = stream->GetNumChannels();
@@ -315,10 +325,24 @@ void AudioRenderer::QueueMixedBuffer(Buffer::Tag tag) {
315} 325}
316 326
317void AudioRenderer::ReleaseAndQueueBuffers() { 327void AudioRenderer::ReleaseAndQueueBuffers() {
318 const auto released_buffers{audio_out->GetTagsAndReleaseBuffers(stream)}; 328 if (!stream->IsPlaying()) {
319 for (const auto& tag : released_buffers) { 329 return;
320 QueueMixedBuffer(tag);
321 } 330 }
331
332 {
333 std::scoped_lock lock{mutex};
334 const auto released_buffers{audio_out->GetTagsAndReleaseBuffers(stream)};
335 for (const auto& tag : released_buffers) {
336 QueueMixedBuffer(tag);
337 }
338 }
339
340 const f32 sample_rate = static_cast<f32>(GetSampleRate());
341 const f32 sample_count = static_cast<f32>(GetSampleCount());
342 const f32 consume_rate = sample_rate / (sample_count * (sample_count / 240));
343 const s32 ms = (1000 / static_cast<s32>(consume_rate)) - 1;
344 const std::chrono::milliseconds next_event_time(std::max(ms / NUM_BUFFERS, 1));
345 core_timing.ScheduleEvent(next_event_time, process_event, {});
322} 346}
323 347
324} // namespace AudioCore 348} // namespace AudioCore
diff --git a/src/audio_core/audio_renderer.h b/src/audio_core/audio_renderer.h
index 18567f618..88fdd13dd 100644
--- a/src/audio_core/audio_renderer.h
+++ b/src/audio_core/audio_renderer.h
@@ -6,6 +6,7 @@
6 6
7#include <array> 7#include <array>
8#include <memory> 8#include <memory>
9#include <mutex>
9#include <vector> 10#include <vector>
10 11
11#include "audio_core/behavior_info.h" 12#include "audio_core/behavior_info.h"
@@ -45,6 +46,8 @@ public:
45 46
46 [[nodiscard]] ResultCode UpdateAudioRenderer(const std::vector<u8>& input_params, 47 [[nodiscard]] ResultCode UpdateAudioRenderer(const std::vector<u8>& input_params,
47 std::vector<u8>& output_params); 48 std::vector<u8>& output_params);
49 [[nodiscard]] ResultCode Start();
50 [[nodiscard]] ResultCode Stop();
48 void QueueMixedBuffer(Buffer::Tag tag); 51 void QueueMixedBuffer(Buffer::Tag tag);
49 void ReleaseAndQueueBuffers(); 52 void ReleaseAndQueueBuffers();
50 [[nodiscard]] u32 GetSampleRate() const; 53 [[nodiscard]] u32 GetSampleRate() const;
@@ -68,6 +71,9 @@ private:
68 Core::Memory::Memory& memory; 71 Core::Memory::Memory& memory;
69 CommandGenerator command_generator; 72 CommandGenerator command_generator;
70 std::size_t elapsed_frame_count{}; 73 std::size_t elapsed_frame_count{};
74 Core::Timing::CoreTiming& core_timing;
75 std::shared_ptr<Core::Timing::EventType> process_event;
76 std::mutex mutex;
71}; 77};
72 78
73} // namespace AudioCore 79} // namespace AudioCore
diff --git a/src/audio_core/command_generator.cpp b/src/audio_core/command_generator.cpp
index 437cc5ccd..27437f1ea 100644
--- a/src/audio_core/command_generator.cpp
+++ b/src/audio_core/command_generator.cpp
@@ -795,7 +795,7 @@ void CommandGenerator::UpdateI3dl2Reverb(I3dl2ReverbParams& info, I3dl2ReverbSta
795 state.lowpass_1 = 0.0f; 795 state.lowpass_1 = 0.0f;
796 } else { 796 } else {
797 const auto a = 1.0f - hf_gain; 797 const auto a = 1.0f - hf_gain;
798 const auto b = 2.0f * (1.0f - hf_gain * CosD(256.0f * info.hf_reference / 798 const auto b = 2.0f * (2.0f - hf_gain * CosD(256.0f * info.hf_reference /
799 static_cast<f32>(info.sample_rate))); 799 static_cast<f32>(info.sample_rate)));
800 const auto c = std::sqrt(b * b - 4.0f * a * a); 800 const auto c = std::sqrt(b * b - 4.0f * a * a);
801 801
@@ -843,7 +843,7 @@ void CommandGenerator::UpdateI3dl2Reverb(I3dl2ReverbParams& info, I3dl2ReverbSta
843 } 843 }
844 844
845 const auto max_early_delay = state.early_delay_line.GetMaxDelay(); 845 const auto max_early_delay = state.early_delay_line.GetMaxDelay();
846 const auto reflection_time = 1000.0f * (0.0098f * info.reverb_delay + 0.02f); 846 const auto reflection_time = 1000.0f * (0.9998f * info.reverb_delay + 0.02f);
847 for (std::size_t tap = 0; tap < AudioCommon::I3DL2REVERB_TAPS; tap++) { 847 for (std::size_t tap = 0; tap < AudioCommon::I3DL2REVERB_TAPS; tap++) {
848 const auto length = AudioCommon::CalculateDelaySamples( 848 const auto length = AudioCommon::CalculateDelaySamples(
849 sample_rate, 1000.0f * info.reflection_delay + reflection_time * EARLY_TAP_TIMES[tap]); 849 sample_rate, 1000.0f * info.reflection_delay + reflection_time * EARLY_TAP_TIMES[tap]);
@@ -1004,7 +1004,8 @@ void CommandGenerator::GenerateFinalMixCommand() {
1004} 1004}
1005 1005
1006s32 CommandGenerator::DecodePcm16(ServerVoiceInfo& voice_info, VoiceState& dsp_state, 1006s32 CommandGenerator::DecodePcm16(ServerVoiceInfo& voice_info, VoiceState& dsp_state,
1007 s32 sample_count, s32 channel, std::size_t mix_offset) { 1007 s32 sample_start_offset, s32 sample_end_offset, s32 sample_count,
1008 s32 channel, std::size_t mix_offset) {
1008 const auto& in_params = voice_info.GetInParams(); 1009 const auto& in_params = voice_info.GetInParams();
1009 const auto& wave_buffer = in_params.wave_buffer[dsp_state.wave_buffer_index]; 1010 const auto& wave_buffer = in_params.wave_buffer[dsp_state.wave_buffer_index];
1010 if (wave_buffer.buffer_address == 0) { 1011 if (wave_buffer.buffer_address == 0) {
@@ -1013,14 +1014,12 @@ s32 CommandGenerator::DecodePcm16(ServerVoiceInfo& voice_info, VoiceState& dsp_s
1013 if (wave_buffer.buffer_size == 0) { 1014 if (wave_buffer.buffer_size == 0) {
1014 return 0; 1015 return 0;
1015 } 1016 }
1016 if (wave_buffer.end_sample_offset < wave_buffer.start_sample_offset) { 1017 if (sample_end_offset < sample_start_offset) {
1017 return 0; 1018 return 0;
1018 } 1019 }
1019 const auto samples_remaining = 1020 const auto samples_remaining = (sample_end_offset - sample_start_offset) - dsp_state.offset;
1020 (wave_buffer.end_sample_offset - wave_buffer.start_sample_offset) - dsp_state.offset;
1021 const auto start_offset = 1021 const auto start_offset =
1022 ((wave_buffer.start_sample_offset + dsp_state.offset) * in_params.channel_count) * 1022 ((dsp_state.offset + sample_start_offset) * in_params.channel_count) * sizeof(s16);
1023 sizeof(s16);
1024 const auto buffer_pos = wave_buffer.buffer_address + start_offset; 1023 const auto buffer_pos = wave_buffer.buffer_address + start_offset;
1025 const auto samples_processed = std::min(sample_count, samples_remaining); 1024 const auto samples_processed = std::min(sample_count, samples_remaining);
1026 1025
@@ -1044,8 +1043,8 @@ s32 CommandGenerator::DecodePcm16(ServerVoiceInfo& voice_info, VoiceState& dsp_s
1044} 1043}
1045 1044
1046s32 CommandGenerator::DecodeAdpcm(ServerVoiceInfo& voice_info, VoiceState& dsp_state, 1045s32 CommandGenerator::DecodeAdpcm(ServerVoiceInfo& voice_info, VoiceState& dsp_state,
1047 s32 sample_count, [[maybe_unused]] s32 channel, 1046 s32 sample_start_offset, s32 sample_end_offset, s32 sample_count,
1048 std::size_t mix_offset) { 1047 [[maybe_unused]] s32 channel, std::size_t mix_offset) {
1049 const auto& in_params = voice_info.GetInParams(); 1048 const auto& in_params = voice_info.GetInParams();
1050 const auto& wave_buffer = in_params.wave_buffer[dsp_state.wave_buffer_index]; 1049 const auto& wave_buffer = in_params.wave_buffer[dsp_state.wave_buffer_index];
1051 if (wave_buffer.buffer_address == 0) { 1050 if (wave_buffer.buffer_address == 0) {
@@ -1054,7 +1053,7 @@ s32 CommandGenerator::DecodeAdpcm(ServerVoiceInfo& voice_info, VoiceState& dsp_s
1054 if (wave_buffer.buffer_size == 0) { 1053 if (wave_buffer.buffer_size == 0) {
1055 return 0; 1054 return 0;
1056 } 1055 }
1057 if (wave_buffer.end_sample_offset < wave_buffer.start_sample_offset) { 1056 if (sample_end_offset < sample_start_offset) {
1058 return 0; 1057 return 0;
1059 } 1058 }
1060 1059
@@ -1079,10 +1078,9 @@ s32 CommandGenerator::DecodeAdpcm(ServerVoiceInfo& voice_info, VoiceState& dsp_s
1079 s32 coef1 = coeffs[idx * 2]; 1078 s32 coef1 = coeffs[idx * 2];
1080 s32 coef2 = coeffs[idx * 2 + 1]; 1079 s32 coef2 = coeffs[idx * 2 + 1];
1081 1080
1082 const auto samples_remaining = 1081 const auto samples_remaining = (sample_end_offset - sample_start_offset) - dsp_state.offset;
1083 (wave_buffer.end_sample_offset - wave_buffer.start_sample_offset) - dsp_state.offset;
1084 const auto samples_processed = std::min(sample_count, samples_remaining); 1082 const auto samples_processed = std::min(sample_count, samples_remaining);
1085 const auto sample_pos = wave_buffer.start_sample_offset + dsp_state.offset; 1083 const auto sample_pos = dsp_state.offset + sample_start_offset;
1086 1084
1087 const auto samples_remaining_in_frame = sample_pos % SAMPLES_PER_FRAME; 1085 const auto samples_remaining_in_frame = sample_pos % SAMPLES_PER_FRAME;
1088 auto position_in_frame = ((sample_pos / SAMPLES_PER_FRAME) * NIBBLES_PER_SAMPLE) + 1086 auto position_in_frame = ((sample_pos / SAMPLES_PER_FRAME) * NIBBLES_PER_SAMPLE) +
@@ -1210,9 +1208,8 @@ void CommandGenerator::DecodeFromWaveBuffers(ServerVoiceInfo& voice_info, s32* o
1210 } 1208 }
1211 1209
1212 std::size_t temp_mix_offset{}; 1210 std::size_t temp_mix_offset{};
1213 bool is_buffer_completed{false};
1214 auto samples_remaining = sample_count; 1211 auto samples_remaining = sample_count;
1215 while (samples_remaining > 0 && !is_buffer_completed) { 1212 while (samples_remaining > 0) {
1216 const auto samples_to_output = std::min(samples_remaining, min_required_samples); 1213 const auto samples_to_output = std::min(samples_remaining, min_required_samples);
1217 const auto samples_to_read = (samples_to_output * resample_rate + dsp_state.fraction) >> 15; 1214 const auto samples_to_read = (samples_to_output * resample_rate + dsp_state.fraction) >> 15;
1218 1215
@@ -1229,24 +1226,38 @@ void CommandGenerator::DecodeFromWaveBuffers(ServerVoiceInfo& voice_info, s32* o
1229 const auto& wave_buffer = in_params.wave_buffer[dsp_state.wave_buffer_index]; 1226 const auto& wave_buffer = in_params.wave_buffer[dsp_state.wave_buffer_index];
1230 // No more data can be read 1227 // No more data can be read
1231 if (!dsp_state.is_wave_buffer_valid[dsp_state.wave_buffer_index]) { 1228 if (!dsp_state.is_wave_buffer_valid[dsp_state.wave_buffer_index]) {
1232 is_buffer_completed = true;
1233 break; 1229 break;
1234 } 1230 }
1235 1231
1236 if (in_params.sample_format == SampleFormat::Adpcm && dsp_state.offset == 0 && 1232 if (in_params.sample_format == SampleFormat::Adpcm && dsp_state.offset == 0 &&
1237 wave_buffer.context_address != 0 && wave_buffer.context_size != 0) { 1233 wave_buffer.context_address != 0 && wave_buffer.context_size != 0) {
1238 // TODO(ogniK): ADPCM loop context 1234 memory.ReadBlock(wave_buffer.context_address, &dsp_state.context,
1235 sizeof(ADPCMContext));
1236 }
1237
1238 s32 samples_offset_start;
1239 s32 samples_offset_end;
1240 if (dsp_state.loop_count > 0 && wave_buffer.loop_start_sample != 0 &&
1241 wave_buffer.loop_end_sample != 0 &&
1242 wave_buffer.loop_start_sample <= wave_buffer.loop_end_sample) {
1243 samples_offset_start = wave_buffer.loop_start_sample;
1244 samples_offset_end = wave_buffer.loop_end_sample;
1245 } else {
1246 samples_offset_start = wave_buffer.start_sample_offset;
1247 samples_offset_end = wave_buffer.end_sample_offset;
1239 } 1248 }
1240 1249
1241 s32 samples_decoded{0}; 1250 s32 samples_decoded{0};
1242 switch (in_params.sample_format) { 1251 switch (in_params.sample_format) {
1243 case SampleFormat::Pcm16: 1252 case SampleFormat::Pcm16:
1244 samples_decoded = DecodePcm16(voice_info, dsp_state, samples_to_read - samples_read, 1253 samples_decoded =
1245 channel, temp_mix_offset); 1254 DecodePcm16(voice_info, dsp_state, samples_offset_start, samples_offset_end,
1255 samples_to_read - samples_read, channel, temp_mix_offset);
1246 break; 1256 break;
1247 case SampleFormat::Adpcm: 1257 case SampleFormat::Adpcm:
1248 samples_decoded = DecodeAdpcm(voice_info, dsp_state, samples_to_read - samples_read, 1258 samples_decoded =
1249 channel, temp_mix_offset); 1259 DecodeAdpcm(voice_info, dsp_state, samples_offset_start, samples_offset_end,
1260 samples_to_read - samples_read, channel, temp_mix_offset);
1250 break; 1261 break;
1251 default: 1262 default:
1252 UNREACHABLE_MSG("Unimplemented sample format={}", in_params.sample_format); 1263 UNREACHABLE_MSG("Unimplemented sample format={}", in_params.sample_format);
@@ -1257,15 +1268,19 @@ void CommandGenerator::DecodeFromWaveBuffers(ServerVoiceInfo& voice_info, s32* o
1257 dsp_state.offset += samples_decoded; 1268 dsp_state.offset += samples_decoded;
1258 dsp_state.played_sample_count += samples_decoded; 1269 dsp_state.played_sample_count += samples_decoded;
1259 1270
1260 if (dsp_state.offset >= 1271 if (dsp_state.offset >= (samples_offset_end - samples_offset_start) ||
1261 (wave_buffer.end_sample_offset - wave_buffer.start_sample_offset) ||
1262 samples_decoded == 0) { 1272 samples_decoded == 0) {
1263 // Reset our sample offset 1273 // Reset our sample offset
1264 dsp_state.offset = 0; 1274 dsp_state.offset = 0;
1265 if (wave_buffer.is_looping) { 1275 if (wave_buffer.is_looping) {
1266 if (samples_decoded == 0) { 1276 dsp_state.loop_count++;
1277 if (wave_buffer.loop_count > 0 &&
1278 (dsp_state.loop_count > wave_buffer.loop_count || samples_decoded == 0)) {
1267 // End of our buffer 1279 // End of our buffer
1268 is_buffer_completed = true; 1280 voice_info.SetWaveBufferCompleted(dsp_state, wave_buffer);
1281 }
1282
1283 if (samples_decoded == 0) {
1269 break; 1284 break;
1270 } 1285 }
1271 1286
@@ -1273,15 +1288,8 @@ void CommandGenerator::DecodeFromWaveBuffers(ServerVoiceInfo& voice_info, s32* o
1273 dsp_state.played_sample_count = 0; 1288 dsp_state.played_sample_count = 0;
1274 } 1289 }
1275 } else { 1290 } else {
1276
1277 // Update our wave buffer states 1291 // Update our wave buffer states
1278 dsp_state.is_wave_buffer_valid[dsp_state.wave_buffer_index] = false; 1292 voice_info.SetWaveBufferCompleted(dsp_state, wave_buffer);
1279 dsp_state.wave_buffer_consumed++;
1280 dsp_state.wave_buffer_index =
1281 (dsp_state.wave_buffer_index + 1) % AudioCommon::MAX_WAVE_BUFFERS;
1282 if (wave_buffer.end_of_stream) {
1283 dsp_state.played_sample_count = 0;
1284 }
1285 } 1293 }
1286 } 1294 }
1287 } 1295 }
diff --git a/src/audio_core/command_generator.h b/src/audio_core/command_generator.h
index 2ebb755b0..673e4fbef 100644
--- a/src/audio_core/command_generator.h
+++ b/src/audio_core/command_generator.h
@@ -86,10 +86,10 @@ private:
86 std::vector<u8>& work_buffer); 86 std::vector<u8>& work_buffer);
87 void UpdateI3dl2Reverb(I3dl2ReverbParams& info, I3dl2ReverbState& state, bool should_clear); 87 void UpdateI3dl2Reverb(I3dl2ReverbParams& info, I3dl2ReverbState& state, bool should_clear);
88 // DSP Code 88 // DSP Code
89 s32 DecodePcm16(ServerVoiceInfo& voice_info, VoiceState& dsp_state, s32 sample_count, 89 s32 DecodePcm16(ServerVoiceInfo& voice_info, VoiceState& dsp_state, s32 sample_start_offset,
90 s32 channel, std::size_t mix_offset); 90 s32 sample_end_offset, s32 sample_count, s32 channel, std::size_t mix_offset);
91 s32 DecodeAdpcm(ServerVoiceInfo& voice_info, VoiceState& dsp_state, s32 sample_count, 91 s32 DecodeAdpcm(ServerVoiceInfo& voice_info, VoiceState& dsp_state, s32 sample_start_offset,
92 s32 channel, std::size_t mix_offset); 92 s32 sample_end_offset, s32 sample_count, s32 channel, std::size_t mix_offset);
93 void DecodeFromWaveBuffers(ServerVoiceInfo& voice_info, s32* output, VoiceState& dsp_state, 93 void DecodeFromWaveBuffers(ServerVoiceInfo& voice_info, s32* output, VoiceState& dsp_state,
94 s32 channel, s32 target_sample_rate, s32 sample_count, s32 node_id); 94 s32 channel, s32 target_sample_rate, s32 sample_count, s32 node_id);
95 95
diff --git a/src/audio_core/info_updater.cpp b/src/audio_core/info_updater.cpp
index 4a5b1b4ab..9b4ca1851 100644
--- a/src/audio_core/info_updater.cpp
+++ b/src/audio_core/info_updater.cpp
@@ -189,9 +189,6 @@ bool InfoUpdater::UpdateVoices(VoiceContext& voice_context,
189 if (voice_in_params.is_new) { 189 if (voice_in_params.is_new) {
190 // Default our values for our voice 190 // Default our values for our voice
191 voice_info.Initialize(); 191 voice_info.Initialize();
192 if (channel_count == 0 || channel_count > AudioCommon::MAX_CHANNEL_COUNT) {
193 continue;
194 }
195 192
196 // Zero out our voice states 193 // Zero out our voice states
197 for (std::size_t channel = 0; channel < channel_count; channel++) { 194 for (std::size_t channel = 0; channel < channel_count; channel++) {
diff --git a/src/audio_core/voice_context.cpp b/src/audio_core/voice_context.cpp
index 867b8fc6b..d8c954b60 100644
--- a/src/audio_core/voice_context.cpp
+++ b/src/audio_core/voice_context.cpp
@@ -66,7 +66,7 @@ void ServerVoiceInfo::Initialize() {
66 in_params.last_volume = 0.0f; 66 in_params.last_volume = 0.0f;
67 in_params.biquad_filter.fill({}); 67 in_params.biquad_filter.fill({});
68 in_params.wave_buffer_count = 0; 68 in_params.wave_buffer_count = 0;
69 in_params.wave_bufffer_head = 0; 69 in_params.wave_buffer_head = 0;
70 in_params.mix_id = AudioCommon::NO_MIX; 70 in_params.mix_id = AudioCommon::NO_MIX;
71 in_params.splitter_info_id = AudioCommon::NO_SPLITTER; 71 in_params.splitter_info_id = AudioCommon::NO_SPLITTER;
72 in_params.additional_params_address = 0; 72 in_params.additional_params_address = 0;
@@ -75,7 +75,7 @@ void ServerVoiceInfo::Initialize() {
75 out_params.played_sample_count = 0; 75 out_params.played_sample_count = 0;
76 out_params.wave_buffer_consumed = 0; 76 out_params.wave_buffer_consumed = 0;
77 in_params.voice_drop_flag = false; 77 in_params.voice_drop_flag = false;
78 in_params.buffer_mapped = false; 78 in_params.buffer_mapped = true;
79 in_params.wave_buffer_flush_request_count = 0; 79 in_params.wave_buffer_flush_request_count = 0;
80 in_params.was_biquad_filter_enabled.fill(false); 80 in_params.was_biquad_filter_enabled.fill(false);
81 81
@@ -126,7 +126,7 @@ void ServerVoiceInfo::UpdateParameters(const VoiceInfo::InParams& voice_in,
126 in_params.volume = voice_in.volume; 126 in_params.volume = voice_in.volume;
127 in_params.biquad_filter = voice_in.biquad_filter; 127 in_params.biquad_filter = voice_in.biquad_filter;
128 in_params.wave_buffer_count = voice_in.wave_buffer_count; 128 in_params.wave_buffer_count = voice_in.wave_buffer_count;
129 in_params.wave_bufffer_head = voice_in.wave_buffer_head; 129 in_params.wave_buffer_head = voice_in.wave_buffer_head;
130 if (behavior_info.IsFlushVoiceWaveBuffersSupported()) { 130 if (behavior_info.IsFlushVoiceWaveBuffersSupported()) {
131 const auto in_request_count = in_params.wave_buffer_flush_request_count; 131 const auto in_request_count = in_params.wave_buffer_flush_request_count;
132 const auto voice_request_count = voice_in.wave_buffer_flush_request_count; 132 const auto voice_request_count = voice_in.wave_buffer_flush_request_count;
@@ -185,14 +185,16 @@ void ServerVoiceInfo::UpdateWaveBuffers(
185 wave_buffer.buffer_size = 0; 185 wave_buffer.buffer_size = 0;
186 wave_buffer.context_address = 0; 186 wave_buffer.context_address = 0;
187 wave_buffer.context_size = 0; 187 wave_buffer.context_size = 0;
188 wave_buffer.loop_start_sample = 0;
189 wave_buffer.loop_end_sample = 0;
188 wave_buffer.sent_to_dsp = true; 190 wave_buffer.sent_to_dsp = true;
189 } 191 }
190 192
191 // Mark all our wave buffers as invalid 193 // Mark all our wave buffers as invalid
192 for (std::size_t channel = 0; channel < static_cast<std::size_t>(in_params.channel_count); 194 for (std::size_t channel = 0; channel < static_cast<std::size_t>(in_params.channel_count);
193 channel++) { 195 channel++) {
194 for (auto& is_valid : voice_states[channel]->is_wave_buffer_valid) { 196 for (std::size_t i = 0; i < AudioCommon::MAX_WAVE_BUFFERS; ++i) {
195 is_valid = false; 197 voice_states[channel]->is_wave_buffer_valid[i] = false;
196 } 198 }
197 } 199 }
198 } 200 }
@@ -211,7 +213,7 @@ void ServerVoiceInfo::UpdateWaveBuffer(ServerWaveBuffer& out_wavebuffer,
211 const WaveBuffer& in_wave_buffer, SampleFormat sample_format, 213 const WaveBuffer& in_wave_buffer, SampleFormat sample_format,
212 bool is_buffer_valid, 214 bool is_buffer_valid,
213 [[maybe_unused]] BehaviorInfo& behavior_info) { 215 [[maybe_unused]] BehaviorInfo& behavior_info) {
214 if (!is_buffer_valid && out_wavebuffer.sent_to_dsp) { 216 if (!is_buffer_valid && out_wavebuffer.sent_to_dsp && out_wavebuffer.buffer_address != 0) {
215 out_wavebuffer.buffer_address = 0; 217 out_wavebuffer.buffer_address = 0;
216 out_wavebuffer.buffer_size = 0; 218 out_wavebuffer.buffer_size = 0;
217 } 219 }
@@ -219,11 +221,40 @@ void ServerVoiceInfo::UpdateWaveBuffer(ServerWaveBuffer& out_wavebuffer,
219 if (!in_wave_buffer.sent_to_server || !in_params.buffer_mapped) { 221 if (!in_wave_buffer.sent_to_server || !in_params.buffer_mapped) {
220 // Validate sample offset sizings 222 // Validate sample offset sizings
221 if (sample_format == SampleFormat::Pcm16) { 223 if (sample_format == SampleFormat::Pcm16) {
222 const auto buffer_size = in_wave_buffer.buffer_size; 224 const s64 buffer_size = static_cast<s64>(in_wave_buffer.buffer_size);
223 if (in_wave_buffer.start_sample_offset < 0 || in_wave_buffer.end_sample_offset < 0 || 225 const s64 start = sizeof(s16) * in_wave_buffer.start_sample_offset;
224 (buffer_size < (sizeof(s16) * in_wave_buffer.start_sample_offset)) || 226 const s64 end = sizeof(s16) * in_wave_buffer.end_sample_offset;
225 (buffer_size < (sizeof(s16) * in_wave_buffer.end_sample_offset))) { 227 if (0 > start || start > buffer_size || 0 > end || end > buffer_size) {
226 // TODO(ogniK): Write error info 228 // TODO(ogniK): Write error info
229 LOG_ERROR(Audio,
230 "PCM16 wavebuffer has an invalid size. Buffer has size 0x{:08X}, but "
231 "offsets were "
232 "{:08X} - 0x{:08X}",
233 buffer_size, sizeof(s16) * in_wave_buffer.start_sample_offset,
234 sizeof(s16) * in_wave_buffer.end_sample_offset);
235 return;
236 }
237 } else if (sample_format == SampleFormat::Adpcm) {
238 const s64 buffer_size = static_cast<s64>(in_wave_buffer.buffer_size);
239 const s64 start_frames = in_wave_buffer.start_sample_offset / 14;
240 const s64 start_extra = in_wave_buffer.start_sample_offset % 14 == 0
241 ? 0
242 : (in_wave_buffer.start_sample_offset % 14) / 2 + 1 +
243 (in_wave_buffer.start_sample_offset % 2);
244 const s64 start = start_frames * 8 + start_extra;
245 const s64 end_frames = in_wave_buffer.end_sample_offset / 14;
246 const s64 end_extra = in_wave_buffer.end_sample_offset % 14 == 0
247 ? 0
248 : (in_wave_buffer.end_sample_offset % 14) / 2 + 1 +
249 (in_wave_buffer.end_sample_offset % 2);
250 const s64 end = end_frames * 8 + end_extra;
251 if (in_wave_buffer.start_sample_offset < 0 || start > buffer_size ||
252 in_wave_buffer.end_sample_offset < 0 || end > buffer_size) {
253 LOG_ERROR(Audio,
254 "ADPMC wavebuffer has an invalid size. Buffer has size 0x{:08X}, but "
255 "offsets were "
256 "{:08X} - 0x{:08X}",
257 in_wave_buffer.buffer_size, start, end);
227 return; 258 return;
228 } 259 }
229 } 260 }
@@ -239,29 +270,34 @@ void ServerVoiceInfo::UpdateWaveBuffer(ServerWaveBuffer& out_wavebuffer,
239 out_wavebuffer.buffer_size = in_wave_buffer.buffer_size; 270 out_wavebuffer.buffer_size = in_wave_buffer.buffer_size;
240 out_wavebuffer.context_address = in_wave_buffer.context_address; 271 out_wavebuffer.context_address = in_wave_buffer.context_address;
241 out_wavebuffer.context_size = in_wave_buffer.context_size; 272 out_wavebuffer.context_size = in_wave_buffer.context_size;
273 out_wavebuffer.loop_start_sample = in_wave_buffer.loop_start_sample;
274 out_wavebuffer.loop_end_sample = in_wave_buffer.loop_end_sample;
242 in_params.buffer_mapped = 275 in_params.buffer_mapped =
243 in_wave_buffer.buffer_address != 0 && in_wave_buffer.buffer_size != 0; 276 in_wave_buffer.buffer_address != 0 && in_wave_buffer.buffer_size != 0;
244 // TODO(ogniK): Pool mapper attachment 277 // TODO(ogniK): Pool mapper attachment
245 // TODO(ogniK): IsAdpcmLoopContextBugFixed 278 // TODO(ogniK): IsAdpcmLoopContextBugFixed
279 if (sample_format == SampleFormat::Adpcm && in_wave_buffer.context_address != 0 &&
280 in_wave_buffer.context_size != 0 && behavior_info.IsAdpcmLoopContextBugFixed()) {
281 } else {
282 out_wavebuffer.context_address = 0;
283 out_wavebuffer.context_size = 0;
284 }
246 } 285 }
247} 286}
248 287
249void ServerVoiceInfo::WriteOutStatus( 288void ServerVoiceInfo::WriteOutStatus(
250 VoiceInfo::OutParams& voice_out, VoiceInfo::InParams& voice_in, 289 VoiceInfo::OutParams& voice_out, VoiceInfo::InParams& voice_in,
251 std::array<VoiceState*, AudioCommon::MAX_CHANNEL_COUNT>& voice_states) { 290 std::array<VoiceState*, AudioCommon::MAX_CHANNEL_COUNT>& voice_states) {
252 if (voice_in.is_new) { 291 if (voice_in.is_new || in_params.is_new) {
253 in_params.is_new = true; 292 in_params.is_new = true;
254 voice_out.wave_buffer_consumed = 0; 293 voice_out.wave_buffer_consumed = 0;
255 voice_out.played_sample_count = 0; 294 voice_out.played_sample_count = 0;
256 voice_out.voice_dropped = false; 295 voice_out.voice_dropped = false;
257 } else if (!in_params.is_new) {
258 voice_out.wave_buffer_consumed = voice_states[0]->wave_buffer_consumed;
259 voice_out.played_sample_count = voice_states[0]->played_sample_count;
260 voice_out.voice_dropped = in_params.voice_drop_flag;
261 } else { 296 } else {
262 voice_out.wave_buffer_consumed = 0; 297 const auto& state = voice_states[0];
263 voice_out.played_sample_count = 0; 298 voice_out.wave_buffer_consumed = state->wave_buffer_consumed;
264 voice_out.voice_dropped = false; 299 voice_out.played_sample_count = state->played_sample_count;
300 voice_out.voice_dropped = state->voice_dropped;
265 } 301 }
266} 302}
267 303
@@ -283,7 +319,8 @@ ServerVoiceInfo::OutParams& ServerVoiceInfo::GetOutParams() {
283 319
284bool ServerVoiceInfo::ShouldSkip() const { 320bool ServerVoiceInfo::ShouldSkip() const {
285 // TODO(ogniK): Handle unmapped wave buffers or parameters 321 // TODO(ogniK): Handle unmapped wave buffers or parameters
286 return !in_params.in_use || (in_params.wave_buffer_count == 0) || in_params.voice_drop_flag; 322 return !in_params.in_use || in_params.wave_buffer_count == 0 || !in_params.buffer_mapped ||
323 in_params.voice_drop_flag;
287} 324}
288 325
289bool ServerVoiceInfo::UpdateForCommandGeneration(VoiceContext& voice_context) { 326bool ServerVoiceInfo::UpdateForCommandGeneration(VoiceContext& voice_context) {
@@ -381,7 +418,7 @@ bool ServerVoiceInfo::UpdateParametersForCommandGeneration(
381void ServerVoiceInfo::FlushWaveBuffers( 418void ServerVoiceInfo::FlushWaveBuffers(
382 u8 flush_count, std::array<VoiceState*, AudioCommon::MAX_CHANNEL_COUNT>& dsp_voice_states, 419 u8 flush_count, std::array<VoiceState*, AudioCommon::MAX_CHANNEL_COUNT>& dsp_voice_states,
383 s32 channel_count) { 420 s32 channel_count) {
384 auto wave_head = in_params.wave_bufffer_head; 421 auto wave_head = in_params.wave_buffer_head;
385 422
386 for (u8 i = 0; i < flush_count; i++) { 423 for (u8 i = 0; i < flush_count; i++) {
387 in_params.wave_buffer[wave_head].sent_to_dsp = true; 424 in_params.wave_buffer[wave_head].sent_to_dsp = true;
@@ -401,6 +438,17 @@ bool ServerVoiceInfo::HasValidWaveBuffer(const VoiceState* state) const {
401 return std::find(valid_wb.begin(), valid_wb.end(), true) != valid_wb.end(); 438 return std::find(valid_wb.begin(), valid_wb.end(), true) != valid_wb.end();
402} 439}
403 440
441void ServerVoiceInfo::SetWaveBufferCompleted(VoiceState& dsp_state,
442 const ServerWaveBuffer& wave_buffer) {
443 dsp_state.is_wave_buffer_valid[dsp_state.wave_buffer_index] = false;
444 dsp_state.wave_buffer_consumed++;
445 dsp_state.wave_buffer_index = (dsp_state.wave_buffer_index + 1) % AudioCommon::MAX_WAVE_BUFFERS;
446 dsp_state.loop_count = 0;
447 if (wave_buffer.end_of_stream) {
448 dsp_state.played_sample_count = 0;
449 }
450}
451
404VoiceContext::VoiceContext(std::size_t voice_count_) : voice_count{voice_count_} { 452VoiceContext::VoiceContext(std::size_t voice_count_) : voice_count{voice_count_} {
405 for (std::size_t i = 0; i < voice_count; i++) { 453 for (std::size_t i = 0; i < voice_count; i++) {
406 voice_channel_resources.emplace_back(static_cast<s32>(i)); 454 voice_channel_resources.emplace_back(static_cast<s32>(i));
diff --git a/src/audio_core/voice_context.h b/src/audio_core/voice_context.h
index 70359cadb..e1050897b 100644
--- a/src/audio_core/voice_context.h
+++ b/src/audio_core/voice_context.h
@@ -60,10 +60,12 @@ struct WaveBuffer {
60 u8 is_looping{}; 60 u8 is_looping{};
61 u8 end_of_stream{}; 61 u8 end_of_stream{};
62 u8 sent_to_server{}; 62 u8 sent_to_server{};
63 INSERT_PADDING_BYTES(5); 63 INSERT_PADDING_BYTES(1);
64 s32 loop_count{};
64 u64 context_address{}; 65 u64 context_address{};
65 u64 context_size{}; 66 u64 context_size{};
66 INSERT_PADDING_BYTES(8); 67 u32 loop_start_sample{};
68 u32 loop_end_sample{};
67}; 69};
68static_assert(sizeof(WaveBuffer) == 0x38, "WaveBuffer is an invalid size"); 70static_assert(sizeof(WaveBuffer) == 0x38, "WaveBuffer is an invalid size");
69 71
@@ -76,6 +78,9 @@ struct ServerWaveBuffer {
76 bool end_of_stream{}; 78 bool end_of_stream{};
77 VAddr context_address{}; 79 VAddr context_address{};
78 std::size_t context_size{}; 80 std::size_t context_size{};
81 s32 loop_count{};
82 u32 loop_start_sample{};
83 u32 loop_end_sample{};
79 bool sent_to_dsp{true}; 84 bool sent_to_dsp{true};
80}; 85};
81 86
@@ -108,6 +113,7 @@ struct VoiceState {
108 u32 external_context_size; 113 u32 external_context_size;
109 bool is_external_context_used; 114 bool is_external_context_used;
110 bool voice_dropped; 115 bool voice_dropped;
116 s32 loop_count;
111}; 117};
112 118
113class VoiceChannelResource { 119class VoiceChannelResource {
@@ -206,7 +212,7 @@ public:
206 float last_volume{}; 212 float last_volume{};
207 std::array<BiquadFilterParameter, AudioCommon::MAX_BIQUAD_FILTERS> biquad_filter{}; 213 std::array<BiquadFilterParameter, AudioCommon::MAX_BIQUAD_FILTERS> biquad_filter{};
208 s32 wave_buffer_count{}; 214 s32 wave_buffer_count{};
209 s16 wave_bufffer_head{}; 215 s16 wave_buffer_head{};
210 INSERT_PADDING_BYTES(2); 216 INSERT_PADDING_BYTES(2);
211 BehaviorFlags behavior_flags{}; 217 BehaviorFlags behavior_flags{};
212 VAddr additional_params_address{}; 218 VAddr additional_params_address{};
@@ -252,6 +258,7 @@ public:
252 void FlushWaveBuffers(u8 flush_count, 258 void FlushWaveBuffers(u8 flush_count,
253 std::array<VoiceState*, AudioCommon::MAX_CHANNEL_COUNT>& dsp_voice_states, 259 std::array<VoiceState*, AudioCommon::MAX_CHANNEL_COUNT>& dsp_voice_states,
254 s32 channel_count); 260 s32 channel_count);
261 void SetWaveBufferCompleted(VoiceState& dsp_state, const ServerWaveBuffer& wave_buffer);
255 262
256private: 263private:
257 std::vector<s16> stored_samples; 264 std::vector<s16> stored_samples;
diff --git a/src/core/file_sys/patch_manager.cpp b/src/core/file_sys/patch_manager.cpp
index 53b8b7ca0..7c0950bb0 100644
--- a/src/core/file_sys/patch_manager.cpp
+++ b/src/core/file_sys/patch_manager.cpp
@@ -345,8 +345,10 @@ std::vector<Core::Memory::CheatEntry> PatchManager::CreateCheatList(
345static void ApplyLayeredFS(VirtualFile& romfs, u64 title_id, ContentRecordType type, 345static void ApplyLayeredFS(VirtualFile& romfs, u64 title_id, ContentRecordType type,
346 const Service::FileSystem::FileSystemController& fs_controller) { 346 const Service::FileSystem::FileSystemController& fs_controller) {
347 const auto load_dir = fs_controller.GetModificationLoadRoot(title_id); 347 const auto load_dir = fs_controller.GetModificationLoadRoot(title_id);
348 const auto sdmc_load_dir = fs_controller.GetSDMCModificationLoadRoot(title_id);
348 if ((type != ContentRecordType::Program && type != ContentRecordType::Data) || 349 if ((type != ContentRecordType::Program && type != ContentRecordType::Data) ||
349 load_dir == nullptr || load_dir->GetSize() <= 0) { 350 ((load_dir == nullptr || load_dir->GetSize() <= 0) &&
351 (sdmc_load_dir == nullptr || sdmc_load_dir->GetSize() <= 0))) {
350 return; 352 return;
351 } 353 }
352 354
@@ -356,7 +358,10 @@ static void ApplyLayeredFS(VirtualFile& romfs, u64 title_id, ContentRecordType t
356 } 358 }
357 359
358 const auto& disabled = Settings::values.disabled_addons[title_id]; 360 const auto& disabled = Settings::values.disabled_addons[title_id];
359 auto patch_dirs = load_dir->GetSubdirectories(); 361 std::vector<VirtualDir> patch_dirs = load_dir->GetSubdirectories();
362 if (std::find(disabled.cbegin(), disabled.cend(), "SDMC") == disabled.cend()) {
363 patch_dirs.push_back(sdmc_load_dir);
364 }
360 std::sort(patch_dirs.begin(), patch_dirs.end(), 365 std::sort(patch_dirs.begin(), patch_dirs.end(),
361 [](const VirtualDir& l, const VirtualDir& r) { return l->GetName() < r->GetName(); }); 366 [](const VirtualDir& l, const VirtualDir& r) { return l->GetName() < r->GetName(); });
362 367
@@ -402,7 +407,7 @@ static void ApplyLayeredFS(VirtualFile& romfs, u64 title_id, ContentRecordType t
402} 407}
403 408
404VirtualFile PatchManager::PatchRomFS(VirtualFile romfs, u64 ivfc_offset, ContentRecordType type, 409VirtualFile PatchManager::PatchRomFS(VirtualFile romfs, u64 ivfc_offset, ContentRecordType type,
405 VirtualFile update_raw) const { 410 VirtualFile update_raw, bool apply_layeredfs) const {
406 const auto log_string = fmt::format("Patching RomFS for title_id={:016X}, type={:02X}", 411 const auto log_string = fmt::format("Patching RomFS for title_id={:016X}, type={:02X}",
407 title_id, static_cast<u8>(type)); 412 title_id, static_cast<u8>(type));
408 413
@@ -442,7 +447,9 @@ VirtualFile PatchManager::PatchRomFS(VirtualFile romfs, u64 ivfc_offset, Content
442 } 447 }
443 448
444 // LayeredFS 449 // LayeredFS
445 ApplyLayeredFS(romfs, title_id, type, fs_controller); 450 if (apply_layeredfs) {
451 ApplyLayeredFS(romfs, title_id, type, fs_controller);
452 }
446 453
447 return romfs; 454 return romfs;
448} 455}
@@ -524,6 +531,15 @@ PatchManager::PatchVersionNames PatchManager::GetPatchVersionNames(VirtualFile u
524 } 531 }
525 } 532 }
526 533
534 // SDMC mod directory (RomFS LayeredFS)
535 const auto sdmc_mod_dir = fs_controller.GetSDMCModificationLoadRoot(title_id);
536 if (sdmc_mod_dir != nullptr && sdmc_mod_dir->GetSize() > 0 &&
537 IsDirValidAndNonEmpty(FindSubdirectoryCaseless(sdmc_mod_dir, "romfs"))) {
538 const auto mod_disabled =
539 std::find(disabled.begin(), disabled.end(), "SDMC") != disabled.end();
540 out.insert_or_assign(mod_disabled ? "[D] SDMC" : "SDMC", "LayeredFS");
541 }
542
527 // DLC 543 // DLC
528 const auto dlc_entries = 544 const auto dlc_entries =
529 content_provider.ListEntriesFilter(TitleType::AOC, ContentRecordType::Data); 545 content_provider.ListEntriesFilter(TitleType::AOC, ContentRecordType::Data);
diff --git a/src/core/file_sys/patch_manager.h b/src/core/file_sys/patch_manager.h
index fb1853035..3be871f35 100644
--- a/src/core/file_sys/patch_manager.h
+++ b/src/core/file_sys/patch_manager.h
@@ -64,7 +64,8 @@ public:
64 // - LayeredFS 64 // - LayeredFS
65 [[nodiscard]] VirtualFile PatchRomFS(VirtualFile base, u64 ivfc_offset, 65 [[nodiscard]] VirtualFile PatchRomFS(VirtualFile base, u64 ivfc_offset,
66 ContentRecordType type = ContentRecordType::Program, 66 ContentRecordType type = ContentRecordType::Program,
67 VirtualFile update_raw = nullptr) const; 67 VirtualFile update_raw = nullptr,
68 bool apply_layeredfs = true) const;
68 69
69 // Returns a vector of pairs between patch names and patch versions. 70 // Returns a vector of pairs between patch names and patch versions.
70 // i.e. Update 3.2.2 will return {"Update", "3.2.2"} 71 // i.e. Update 3.2.2 will return {"Update", "3.2.2"}
diff --git a/src/core/file_sys/sdmc_factory.cpp b/src/core/file_sys/sdmc_factory.cpp
index cb56d8f2d..e5c72cd4d 100644
--- a/src/core/file_sys/sdmc_factory.cpp
+++ b/src/core/file_sys/sdmc_factory.cpp
@@ -12,23 +12,32 @@ namespace FileSys {
12 12
13constexpr u64 SDMC_TOTAL_SIZE = 0x10000000000; // 1 TiB 13constexpr u64 SDMC_TOTAL_SIZE = 0x10000000000; // 1 TiB
14 14
15SDMCFactory::SDMCFactory(VirtualDir dir_) 15SDMCFactory::SDMCFactory(VirtualDir sd_dir_, VirtualDir sd_mod_dir_)
16 : dir(std::move(dir_)), contents(std::make_unique<RegisteredCache>( 16 : sd_dir(std::move(sd_dir_)), sd_mod_dir(std::move(sd_mod_dir_)),
17 GetOrCreateDirectoryRelative(dir, "/Nintendo/Contents/registered"), 17 contents(std::make_unique<RegisteredCache>(
18 [](const VirtualFile& file, const NcaID& id) { 18 GetOrCreateDirectoryRelative(sd_dir, "/Nintendo/Contents/registered"),
19 return NAX{file, id}.GetDecrypted(); 19 [](const VirtualFile& file, const NcaID& id) {
20 })), 20 return NAX{file, id}.GetDecrypted();
21 })),
21 placeholder(std::make_unique<PlaceholderCache>( 22 placeholder(std::make_unique<PlaceholderCache>(
22 GetOrCreateDirectoryRelative(dir, "/Nintendo/Contents/placehld"))) {} 23 GetOrCreateDirectoryRelative(sd_dir, "/Nintendo/Contents/placehld"))) {}
23 24
24SDMCFactory::~SDMCFactory() = default; 25SDMCFactory::~SDMCFactory() = default;
25 26
26ResultVal<VirtualDir> SDMCFactory::Open() const { 27ResultVal<VirtualDir> SDMCFactory::Open() const {
27 return MakeResult<VirtualDir>(dir); 28 return MakeResult<VirtualDir>(sd_dir);
29}
30
31VirtualDir SDMCFactory::GetSDMCModificationLoadRoot(u64 title_id) const {
32 // LayeredFS doesn't work on updates and title id-less homebrew
33 if (title_id == 0 || (title_id & 0xFFF) == 0x800) {
34 return nullptr;
35 }
36 return GetOrCreateDirectoryRelative(sd_mod_dir, fmt::format("/{:016X}", title_id));
28} 37}
29 38
30VirtualDir SDMCFactory::GetSDMCContentDirectory() const { 39VirtualDir SDMCFactory::GetSDMCContentDirectory() const {
31 return GetOrCreateDirectoryRelative(dir, "/Nintendo/Contents"); 40 return GetOrCreateDirectoryRelative(sd_dir, "/Nintendo/Contents");
32} 41}
33 42
34RegisteredCache* SDMCFactory::GetSDMCContents() const { 43RegisteredCache* SDMCFactory::GetSDMCContents() const {
@@ -40,11 +49,11 @@ PlaceholderCache* SDMCFactory::GetSDMCPlaceholder() const {
40} 49}
41 50
42VirtualDir SDMCFactory::GetImageDirectory() const { 51VirtualDir SDMCFactory::GetImageDirectory() const {
43 return GetOrCreateDirectoryRelative(dir, "/Nintendo/Album"); 52 return GetOrCreateDirectoryRelative(sd_dir, "/Nintendo/Album");
44} 53}
45 54
46u64 SDMCFactory::GetSDMCFreeSpace() const { 55u64 SDMCFactory::GetSDMCFreeSpace() const {
47 return GetSDMCTotalSpace() - dir->GetSize(); 56 return GetSDMCTotalSpace() - sd_dir->GetSize();
48} 57}
49 58
50u64 SDMCFactory::GetSDMCTotalSpace() const { 59u64 SDMCFactory::GetSDMCTotalSpace() const {
diff --git a/src/core/file_sys/sdmc_factory.h b/src/core/file_sys/sdmc_factory.h
index 2bb92ba93..3a3d11f3a 100644
--- a/src/core/file_sys/sdmc_factory.h
+++ b/src/core/file_sys/sdmc_factory.h
@@ -16,11 +16,12 @@ class PlaceholderCache;
16/// File system interface to the SDCard archive 16/// File system interface to the SDCard archive
17class SDMCFactory { 17class SDMCFactory {
18public: 18public:
19 explicit SDMCFactory(VirtualDir dir); 19 explicit SDMCFactory(VirtualDir sd_dir_, VirtualDir sd_mod_dir_);
20 ~SDMCFactory(); 20 ~SDMCFactory();
21 21
22 ResultVal<VirtualDir> Open() const; 22 ResultVal<VirtualDir> Open() const;
23 23
24 VirtualDir GetSDMCModificationLoadRoot(u64 title_id) const;
24 VirtualDir GetSDMCContentDirectory() const; 25 VirtualDir GetSDMCContentDirectory() const;
25 26
26 RegisteredCache* GetSDMCContents() const; 27 RegisteredCache* GetSDMCContents() const;
@@ -32,7 +33,8 @@ public:
32 u64 GetSDMCTotalSpace() const; 33 u64 GetSDMCTotalSpace() const;
33 34
34private: 35private:
35 VirtualDir dir; 36 VirtualDir sd_dir;
37 VirtualDir sd_mod_dir;
36 38
37 std::unique_ptr<RegisteredCache> contents; 39 std::unique_ptr<RegisteredCache> contents;
38 std::unique_ptr<PlaceholderCache> placeholder; 40 std::unique_ptr<PlaceholderCache> placeholder;
diff --git a/src/core/hle/service/audio/audren_u.cpp b/src/core/hle/service/audio/audren_u.cpp
index 800feba6e..7583d68b2 100644
--- a/src/core/hle/service/audio/audren_u.cpp
+++ b/src/core/hle/service/audio/audren_u.cpp
@@ -96,7 +96,7 @@ private:
96 void RequestUpdateImpl(Kernel::HLERequestContext& ctx) { 96 void RequestUpdateImpl(Kernel::HLERequestContext& ctx) {
97 LOG_DEBUG(Service_Audio, "(STUBBED) called"); 97 LOG_DEBUG(Service_Audio, "(STUBBED) called");
98 98
99 std::vector<u8> output_params(ctx.GetWriteBufferSize()); 99 std::vector<u8> output_params(ctx.GetWriteBufferSize(), 0);
100 auto result = renderer->UpdateAudioRenderer(ctx.ReadBuffer(), output_params); 100 auto result = renderer->UpdateAudioRenderer(ctx.ReadBuffer(), output_params);
101 101
102 if (result.IsSuccess()) { 102 if (result.IsSuccess()) {
@@ -110,17 +110,19 @@ private:
110 void Start(Kernel::HLERequestContext& ctx) { 110 void Start(Kernel::HLERequestContext& ctx) {
111 LOG_WARNING(Service_Audio, "(STUBBED) called"); 111 LOG_WARNING(Service_Audio, "(STUBBED) called");
112 112
113 IPC::ResponseBuilder rb{ctx, 2}; 113 const auto result = renderer->Start();
114 114
115 rb.Push(ResultSuccess); 115 IPC::ResponseBuilder rb{ctx, 2};
116 rb.Push(result);
116 } 117 }
117 118
118 void Stop(Kernel::HLERequestContext& ctx) { 119 void Stop(Kernel::HLERequestContext& ctx) {
119 LOG_WARNING(Service_Audio, "(STUBBED) called"); 120 LOG_WARNING(Service_Audio, "(STUBBED) called");
120 121
121 IPC::ResponseBuilder rb{ctx, 2}; 122 const auto result = renderer->Stop();
122 123
123 rb.Push(ResultSuccess); 124 IPC::ResponseBuilder rb{ctx, 2};
125 rb.Push(result);
124 } 126 }
125 127
126 void QuerySystemEvent(Kernel::HLERequestContext& ctx) { 128 void QuerySystemEvent(Kernel::HLERequestContext& ctx) {
diff --git a/src/core/hle/service/filesystem/filesystem.cpp b/src/core/hle/service/filesystem/filesystem.cpp
index 3c16fe6c7..4a9b13e45 100644
--- a/src/core/hle/service/filesystem/filesystem.cpp
+++ b/src/core/hle/service/filesystem/filesystem.cpp
@@ -703,6 +703,16 @@ FileSys::VirtualDir FileSystemController::GetModificationLoadRoot(u64 title_id)
703 return bis_factory->GetModificationLoadRoot(title_id); 703 return bis_factory->GetModificationLoadRoot(title_id);
704} 704}
705 705
706FileSys::VirtualDir FileSystemController::GetSDMCModificationLoadRoot(u64 title_id) const {
707 LOG_TRACE(Service_FS, "Opening SDMC mod load root for tid={:016X}", title_id);
708
709 if (sdmc_factory == nullptr) {
710 return nullptr;
711 }
712
713 return sdmc_factory->GetSDMCModificationLoadRoot(title_id);
714}
715
706FileSys::VirtualDir FileSystemController::GetModificationDumpRoot(u64 title_id) const { 716FileSys::VirtualDir FileSystemController::GetModificationDumpRoot(u64 title_id) const {
707 LOG_TRACE(Service_FS, "Opening mod dump root for tid={:016X}", title_id); 717 LOG_TRACE(Service_FS, "Opening mod dump root for tid={:016X}", title_id);
708 718
@@ -733,20 +743,23 @@ void FileSystemController::CreateFactories(FileSys::VfsFilesystem& vfs, bool ove
733 } 743 }
734 744
735 using YuzuPath = Common::FS::YuzuPath; 745 using YuzuPath = Common::FS::YuzuPath;
746 const auto sdmc_dir_path = Common::FS::GetYuzuPath(YuzuPath::SDMCDir);
747 const auto sdmc_load_dir_path = sdmc_dir_path / "atmosphere/contents";
736 const auto rw_mode = FileSys::Mode::ReadWrite; 748 const auto rw_mode = FileSys::Mode::ReadWrite;
737 749
738 auto nand_directory = 750 auto nand_directory =
739 vfs.OpenDirectory(Common::FS::GetYuzuPathString(YuzuPath::NANDDir), rw_mode); 751 vfs.OpenDirectory(Common::FS::GetYuzuPathString(YuzuPath::NANDDir), rw_mode);
740 auto sd_directory = 752 auto sd_directory = vfs.OpenDirectory(Common::FS::PathToUTF8String(sdmc_dir_path), rw_mode);
741 vfs.OpenDirectory(Common::FS::GetYuzuPathString(YuzuPath::SDMCDir), rw_mode);
742 auto load_directory = 753 auto load_directory =
743 vfs.OpenDirectory(Common::FS::GetYuzuPathString(YuzuPath::LoadDir), FileSys::Mode::Read); 754 vfs.OpenDirectory(Common::FS::GetYuzuPathString(YuzuPath::LoadDir), FileSys::Mode::Read);
755 auto sd_load_directory =
756 vfs.OpenDirectory(Common::FS::PathToUTF8String(sdmc_load_dir_path), FileSys::Mode::Read);
744 auto dump_directory = 757 auto dump_directory =
745 vfs.OpenDirectory(Common::FS::GetYuzuPathString(YuzuPath::DumpDir), rw_mode); 758 vfs.OpenDirectory(Common::FS::GetYuzuPathString(YuzuPath::DumpDir), rw_mode);
746 759
747 if (bis_factory == nullptr) { 760 if (bis_factory == nullptr) {
748 bis_factory = 761 bis_factory = std::make_unique<FileSys::BISFactory>(
749 std::make_unique<FileSys::BISFactory>(nand_directory, load_directory, dump_directory); 762 nand_directory, std::move(load_directory), std::move(dump_directory));
750 system.RegisterContentProvider(FileSys::ContentProviderUnionSlot::SysNAND, 763 system.RegisterContentProvider(FileSys::ContentProviderUnionSlot::SysNAND,
751 bis_factory->GetSystemNANDContents()); 764 bis_factory->GetSystemNANDContents());
752 system.RegisterContentProvider(FileSys::ContentProviderUnionSlot::UserNAND, 765 system.RegisterContentProvider(FileSys::ContentProviderUnionSlot::UserNAND,
@@ -759,7 +772,8 @@ void FileSystemController::CreateFactories(FileSys::VfsFilesystem& vfs, bool ove
759 } 772 }
760 773
761 if (sdmc_factory == nullptr) { 774 if (sdmc_factory == nullptr) {
762 sdmc_factory = std::make_unique<FileSys::SDMCFactory>(std::move(sd_directory)); 775 sdmc_factory = std::make_unique<FileSys::SDMCFactory>(std::move(sd_directory),
776 std::move(sd_load_directory));
763 system.RegisterContentProvider(FileSys::ContentProviderUnionSlot::SDMC, 777 system.RegisterContentProvider(FileSys::ContentProviderUnionSlot::SDMC,
764 sdmc_factory->GetSDMCContents()); 778 sdmc_factory->GetSDMCContents());
765 } 779 }
diff --git a/src/core/hle/service/filesystem/filesystem.h b/src/core/hle/service/filesystem/filesystem.h
index b6b1b9220..d387af3cb 100644
--- a/src/core/hle/service/filesystem/filesystem.h
+++ b/src/core/hle/service/filesystem/filesystem.h
@@ -115,6 +115,7 @@ public:
115 FileSys::VirtualDir GetContentDirectory(ContentStorageId id) const; 115 FileSys::VirtualDir GetContentDirectory(ContentStorageId id) const;
116 FileSys::VirtualDir GetImageDirectory(ImageDirectoryId id) const; 116 FileSys::VirtualDir GetImageDirectory(ImageDirectoryId id) const;
117 117
118 FileSys::VirtualDir GetSDMCModificationLoadRoot(u64 title_id) const;
118 FileSys::VirtualDir GetModificationLoadRoot(u64 title_id) const; 119 FileSys::VirtualDir GetModificationLoadRoot(u64 title_id) const;
119 FileSys::VirtualDir GetModificationDumpRoot(u64 title_id) const; 120 FileSys::VirtualDir GetModificationDumpRoot(u64 title_id) const;
120 121
diff --git a/src/core/hle/service/mii/manager.cpp b/src/core/hle/service/mii/manager.cpp
index 114aff31c..869d2763f 100644
--- a/src/core/hle/service/mii/manager.cpp
+++ b/src/core/hle/service/mii/manager.cpp
@@ -20,6 +20,7 @@ namespace {
20 20
21constexpr ResultCode ERROR_CANNOT_FIND_ENTRY{ErrorModule::Mii, 4}; 21constexpr ResultCode ERROR_CANNOT_FIND_ENTRY{ErrorModule::Mii, 4};
22 22
23constexpr std::size_t BaseMiiCount{2};
23constexpr std::size_t DefaultMiiCount{RawData::DefaultMii.size()}; 24constexpr std::size_t DefaultMiiCount{RawData::DefaultMii.size()};
24 25
25constexpr MiiStoreData::Name DefaultMiiName{u'y', u'u', u'z', u'u'}; 26constexpr MiiStoreData::Name DefaultMiiName{u'y', u'u', u'z', u'u'};
@@ -415,7 +416,7 @@ u32 MiiManager::GetCount(SourceFlag source_flag) const {
415 count += 0; 416 count += 0;
416 } 417 }
417 if ((source_flag & SourceFlag::Default) != SourceFlag::None) { 418 if ((source_flag & SourceFlag::Default) != SourceFlag::None) {
418 count += DefaultMiiCount; 419 count += (DefaultMiiCount - BaseMiiCount);
419 } 420 }
420 return static_cast<u32>(count); 421 return static_cast<u32>(count);
421} 422}
@@ -445,7 +446,7 @@ ResultVal<std::vector<MiiInfoElement>> MiiManager::GetDefault(SourceFlag source_
445 return MakeResult(std::move(result)); 446 return MakeResult(std::move(result));
446 } 447 }
447 448
448 for (std::size_t index = 0; index < DefaultMiiCount; index++) { 449 for (std::size_t index = BaseMiiCount; index < DefaultMiiCount; index++) {
449 result.emplace_back(BuildDefault(index), Source::Default); 450 result.emplace_back(BuildDefault(index), Source::Default);
450 } 451 }
451 452
diff --git a/src/input_common/analog_from_button.cpp b/src/input_common/analog_from_button.cpp
index 100138d11..2fafd077f 100755
--- a/src/input_common/analog_from_button.cpp
+++ b/src/input_common/analog_from_button.cpp
@@ -27,6 +27,7 @@ public:
27 down->SetCallback(callbacks); 27 down->SetCallback(callbacks);
28 left->SetCallback(callbacks); 28 left->SetCallback(callbacks);
29 right->SetCallback(callbacks); 29 right->SetCallback(callbacks);
30 modifier->SetCallback(callbacks);
30 } 31 }
31 32
32 bool IsAngleGreater(float old_angle, float new_angle) const { 33 bool IsAngleGreater(float old_angle, float new_angle) const {
diff --git a/src/video_core/cdma_pusher.cpp b/src/video_core/cdma_pusher.cpp
index a3fda1094..8b86ad050 100644
--- a/src/video_core/cdma_pusher.cpp
+++ b/src/video_core/cdma_pusher.cpp
@@ -103,8 +103,7 @@ void CDmaPusher::ExecuteCommand(u32 state_offset, u32 data) {
103 case ThiMethod::SetMethod1: 103 case ThiMethod::SetMethod1:
104 LOG_DEBUG(Service_NVDRV, "NVDEC method 0x{:X}", 104 LOG_DEBUG(Service_NVDRV, "NVDEC method 0x{:X}",
105 static_cast<u32>(nvdec_thi_state.method_0)); 105 static_cast<u32>(nvdec_thi_state.method_0));
106 nvdec_processor->ProcessMethod(static_cast<Nvdec::Method>(nvdec_thi_state.method_0), 106 nvdec_processor->ProcessMethod(nvdec_thi_state.method_0, data);
107 data);
108 break; 107 break;
109 default: 108 default:
110 break; 109 break;
diff --git a/src/video_core/command_classes/codecs/codec.cpp b/src/video_core/command_classes/codecs/codec.cpp
index d02dc6260..1b4bbc8ac 100644
--- a/src/video_core/command_classes/codecs/codec.cpp
+++ b/src/video_core/command_classes/codecs/codec.cpp
@@ -23,8 +23,8 @@ void AVFrameDeleter(AVFrame* ptr) {
23 av_free(ptr); 23 av_free(ptr);
24} 24}
25 25
26Codec::Codec(GPU& gpu_) 26Codec::Codec(GPU& gpu_, const NvdecCommon::NvdecRegisters& regs)
27 : gpu(gpu_), h264_decoder(std::make_unique<Decoder::H264>(gpu)), 27 : gpu(gpu_), state{regs}, h264_decoder(std::make_unique<Decoder::H264>(gpu)),
28 vp9_decoder(std::make_unique<Decoder::VP9>(gpu)) {} 28 vp9_decoder(std::make_unique<Decoder::VP9>(gpu)) {}
29 29
30Codec::~Codec() { 30Codec::~Codec() {
@@ -43,46 +43,48 @@ Codec::~Codec() {
43 avcodec_close(av_codec_ctx); 43 avcodec_close(av_codec_ctx);
44} 44}
45 45
46void Codec::Initialize() {
47 AVCodecID codec{AV_CODEC_ID_NONE};
48 switch (current_codec) {
49 case NvdecCommon::VideoCodec::H264:
50 codec = AV_CODEC_ID_H264;
51 break;
52 case NvdecCommon::VideoCodec::Vp9:
53 codec = AV_CODEC_ID_VP9;
54 break;
55 default:
56 return;
57 }
58 av_codec = avcodec_find_decoder(codec);
59 av_codec_ctx = avcodec_alloc_context3(av_codec);
60 av_opt_set(av_codec_ctx->priv_data, "tune", "zerolatency", 0);
61
62 // TODO(ameerj): libavcodec gpu hw acceleration
63
64 const auto av_error = avcodec_open2(av_codec_ctx, av_codec, nullptr);
65 if (av_error < 0) {
66 LOG_ERROR(Service_NVDRV, "avcodec_open2() Failed.");
67 avcodec_close(av_codec_ctx);
68 return;
69 }
70 initialized = true;
71 return;
72}
73
46void Codec::SetTargetCodec(NvdecCommon::VideoCodec codec) { 74void Codec::SetTargetCodec(NvdecCommon::VideoCodec codec) {
47 if (current_codec != codec) { 75 if (current_codec != codec) {
48 LOG_INFO(Service_NVDRV, "NVDEC video codec initialized to {}", static_cast<u32>(codec));
49 current_codec = codec; 76 current_codec = codec;
77 LOG_INFO(Service_NVDRV, "NVDEC video codec initialized to {}", GetCurrentCodecName());
50 } 78 }
51} 79}
52 80
53void Codec::StateWrite(u32 offset, u64 arguments) {
54 u8* const state_offset = reinterpret_cast<u8*>(&state) + offset * sizeof(u64);
55 std::memcpy(state_offset, &arguments, sizeof(u64));
56}
57
58void Codec::Decode() { 81void Codec::Decode() {
59 bool is_first_frame = false; 82 const bool is_first_frame = !initialized;
60 if (!initialized) { 83 if (!initialized) {
61 if (current_codec == NvdecCommon::VideoCodec::H264) { 84 Initialize();
62 av_codec = avcodec_find_decoder(AV_CODEC_ID_H264);
63 } else if (current_codec == NvdecCommon::VideoCodec::Vp9) {
64 av_codec = avcodec_find_decoder(AV_CODEC_ID_VP9);
65 } else {
66 LOG_ERROR(Service_NVDRV, "Unknown video codec {}", current_codec);
67 return;
68 }
69
70 av_codec_ctx = avcodec_alloc_context3(av_codec);
71 av_opt_set(av_codec_ctx->priv_data, "tune", "zerolatency", 0);
72
73 // TODO(ameerj): libavcodec gpu hw acceleration
74
75 const auto av_error = avcodec_open2(av_codec_ctx, av_codec, nullptr);
76 if (av_error < 0) {
77 LOG_ERROR(Service_NVDRV, "avcodec_open2() Failed.");
78 avcodec_close(av_codec_ctx);
79 return;
80 }
81 initialized = true;
82 is_first_frame = true;
83 } 85 }
84 bool vp9_hidden_frame = false;
85 86
87 bool vp9_hidden_frame = false;
86 AVPacket packet{}; 88 AVPacket packet{};
87 av_init_packet(&packet); 89 av_init_packet(&packet);
88 std::vector<u8> frame_data; 90 std::vector<u8> frame_data;
@@ -95,7 +97,7 @@ void Codec::Decode() {
95 } 97 }
96 98
97 packet.data = frame_data.data(); 99 packet.data = frame_data.data();
98 packet.size = static_cast<int>(frame_data.size()); 100 packet.size = static_cast<s32>(frame_data.size());
99 101
100 avcodec_send_packet(av_codec_ctx, &packet); 102 avcodec_send_packet(av_codec_ctx, &packet);
101 103
@@ -127,4 +129,21 @@ NvdecCommon::VideoCodec Codec::GetCurrentCodec() const {
127 return current_codec; 129 return current_codec;
128} 130}
129 131
132std::string_view Codec::GetCurrentCodecName() const {
133 switch (current_codec) {
134 case NvdecCommon::VideoCodec::None:
135 return "None";
136 case NvdecCommon::VideoCodec::H264:
137 return "H264";
138 case NvdecCommon::VideoCodec::Vp8:
139 return "VP8";
140 case NvdecCommon::VideoCodec::H265:
141 return "H265";
142 case NvdecCommon::VideoCodec::Vp9:
143 return "VP9";
144 default:
145 return "Unknown";
146 }
147};
148
130} // namespace Tegra 149} // namespace Tegra
diff --git a/src/video_core/command_classes/codecs/codec.h b/src/video_core/command_classes/codecs/codec.h
index 8a2a6c360..96c823c76 100644
--- a/src/video_core/command_classes/codecs/codec.h
+++ b/src/video_core/command_classes/codecs/codec.h
@@ -34,15 +34,15 @@ class VP9;
34 34
35class Codec { 35class Codec {
36public: 36public:
37 explicit Codec(GPU& gpu); 37 explicit Codec(GPU& gpu, const NvdecCommon::NvdecRegisters& regs);
38 ~Codec(); 38 ~Codec();
39 39
40 /// Initialize the codec, returning success or failure
41 void Initialize();
42
40 /// Sets NVDEC video stream codec 43 /// Sets NVDEC video stream codec
41 void SetTargetCodec(NvdecCommon::VideoCodec codec); 44 void SetTargetCodec(NvdecCommon::VideoCodec codec);
42 45
43 /// Populate NvdecRegisters state with argument value at the provided offset
44 void StateWrite(u32 offset, u64 arguments);
45
46 /// Call decoders to construct headers, decode AVFrame with ffmpeg 46 /// Call decoders to construct headers, decode AVFrame with ffmpeg
47 void Decode(); 47 void Decode();
48 48
@@ -51,6 +51,8 @@ public:
51 51
52 /// Returns the value of current_codec 52 /// Returns the value of current_codec
53 [[nodiscard]] NvdecCommon::VideoCodec GetCurrentCodec() const; 53 [[nodiscard]] NvdecCommon::VideoCodec GetCurrentCodec() const;
54 /// Return name of the current codec
55 [[nodiscard]] std::string_view GetCurrentCodecName() const;
54 56
55private: 57private:
56 bool initialized{}; 58 bool initialized{};
@@ -60,10 +62,10 @@ private:
60 AVCodecContext* av_codec_ctx{nullptr}; 62 AVCodecContext* av_codec_ctx{nullptr};
61 63
62 GPU& gpu; 64 GPU& gpu;
65 const NvdecCommon::NvdecRegisters& state;
63 std::unique_ptr<Decoder::H264> h264_decoder; 66 std::unique_ptr<Decoder::H264> h264_decoder;
64 std::unique_ptr<Decoder::VP9> vp9_decoder; 67 std::unique_ptr<Decoder::VP9> vp9_decoder;
65 68
66 NvdecCommon::NvdecRegisters state{};
67 std::queue<AVFramePtr> av_frames{}; 69 std::queue<AVFramePtr> av_frames{};
68}; 70};
69 71
diff --git a/src/video_core/command_classes/codecs/h264.cpp b/src/video_core/command_classes/codecs/h264.cpp
index fea6aed98..5fb6d45ee 100644
--- a/src/video_core/command_classes/codecs/h264.cpp
+++ b/src/video_core/command_classes/codecs/h264.cpp
@@ -45,134 +45,129 @@ H264::~H264() = default;
45 45
46const std::vector<u8>& H264::ComposeFrameHeader(const NvdecCommon::NvdecRegisters& state, 46const std::vector<u8>& H264::ComposeFrameHeader(const NvdecCommon::NvdecRegisters& state,
47 bool is_first_frame) { 47 bool is_first_frame) {
48 H264DecoderContext context{}; 48 H264DecoderContext context;
49 gpu.MemoryManager().ReadBlock(state.picture_info_offset, &context, sizeof(H264DecoderContext)); 49 gpu.MemoryManager().ReadBlock(state.picture_info_offset, &context, sizeof(H264DecoderContext));
50 50
51 const s32 frame_number = static_cast<s32>((context.h264_parameter_set.flags >> 46) & 0x1ffff); 51 const s64 frame_number = context.h264_parameter_set.frame_number.Value();
52 if (!is_first_frame && frame_number != 0) { 52 if (!is_first_frame && frame_number != 0) {
53 frame.resize(context.frame_data_size); 53 frame.resize(context.stream_len);
54
55 gpu.MemoryManager().ReadBlock(state.frame_bitstream_offset, frame.data(), frame.size()); 54 gpu.MemoryManager().ReadBlock(state.frame_bitstream_offset, frame.data(), frame.size());
56 } else { 55 return frame;
57 /// Encode header 56 }
58 H264BitWriter writer{};
59 writer.WriteU(1, 24);
60 writer.WriteU(0, 1);
61 writer.WriteU(3, 2);
62 writer.WriteU(7, 5);
63 writer.WriteU(100, 8);
64 writer.WriteU(0, 8);
65 writer.WriteU(31, 8);
66 writer.WriteUe(0);
67 const auto chroma_format_idc =
68 static_cast<u32>((context.h264_parameter_set.flags >> 12) & 3);
69 writer.WriteUe(chroma_format_idc);
70 if (chroma_format_idc == 3) {
71 writer.WriteBit(false);
72 }
73
74 writer.WriteUe(0);
75 writer.WriteUe(0);
76 writer.WriteBit(false); // QpprimeYZeroTransformBypassFlag
77 writer.WriteBit(false); // Scaling matrix present flag
78
79 const auto order_cnt_type = static_cast<u32>((context.h264_parameter_set.flags >> 14) & 3);
80 writer.WriteUe(static_cast<u32>((context.h264_parameter_set.flags >> 8) & 0xf));
81 writer.WriteUe(order_cnt_type);
82 if (order_cnt_type == 0) {
83 writer.WriteUe(context.h264_parameter_set.log2_max_pic_order_cnt);
84 } else if (order_cnt_type == 1) {
85 writer.WriteBit(context.h264_parameter_set.delta_pic_order_always_zero_flag != 0);
86
87 writer.WriteSe(0);
88 writer.WriteSe(0);
89 writer.WriteUe(0);
90 }
91
92 const s32 pic_height = context.h264_parameter_set.pic_height_in_map_units /
93 (context.h264_parameter_set.frame_mbs_only_flag ? 1 : 2);
94 57
95 writer.WriteUe(16); 58 // Encode header
59 H264BitWriter writer{};
60 writer.WriteU(1, 24);
61 writer.WriteU(0, 1);
62 writer.WriteU(3, 2);
63 writer.WriteU(7, 5);
64 writer.WriteU(100, 8);
65 writer.WriteU(0, 8);
66 writer.WriteU(31, 8);
67 writer.WriteUe(0);
68 const u32 chroma_format_idc =
69 static_cast<u32>(context.h264_parameter_set.chroma_format_idc.Value());
70 writer.WriteUe(chroma_format_idc);
71 if (chroma_format_idc == 3) {
96 writer.WriteBit(false); 72 writer.WriteBit(false);
97 writer.WriteUe(context.h264_parameter_set.pic_width_in_mbs - 1); 73 }
98 writer.WriteUe(pic_height - 1);
99 writer.WriteBit(context.h264_parameter_set.frame_mbs_only_flag != 0);
100
101 if (!context.h264_parameter_set.frame_mbs_only_flag) {
102 writer.WriteBit(((context.h264_parameter_set.flags >> 0) & 1) != 0);
103 }
104 74
105 writer.WriteBit(((context.h264_parameter_set.flags >> 1) & 1) != 0); 75 writer.WriteUe(0);
106 writer.WriteBit(false); // Frame cropping flag 76 writer.WriteUe(0);
107 writer.WriteBit(false); // VUI parameter present flag 77 writer.WriteBit(false); // QpprimeYZeroTransformBypassFlag
78 writer.WriteBit(false); // Scaling matrix present flag
108 79
109 writer.End(); 80 writer.WriteUe(static_cast<u32>(context.h264_parameter_set.log2_max_frame_num_minus4.Value()));
110 81
111 // H264 PPS 82 const auto order_cnt_type =
112 writer.WriteU(1, 24); 83 static_cast<u32>(context.h264_parameter_set.pic_order_cnt_type.Value());
113 writer.WriteU(0, 1); 84 writer.WriteUe(order_cnt_type);
114 writer.WriteU(3, 2); 85 if (order_cnt_type == 0) {
115 writer.WriteU(8, 5); 86 writer.WriteUe(context.h264_parameter_set.log2_max_pic_order_cnt_lsb_minus4);
87 } else if (order_cnt_type == 1) {
88 writer.WriteBit(context.h264_parameter_set.delta_pic_order_always_zero_flag != 0);
116 89
90 writer.WriteSe(0);
91 writer.WriteSe(0);
117 writer.WriteUe(0); 92 writer.WriteUe(0);
118 writer.WriteUe(0); 93 }
119 94
120 writer.WriteBit(context.h264_parameter_set.entropy_coding_mode_flag != 0); 95 const s32 pic_height = context.h264_parameter_set.frame_height_in_map_units /
121 writer.WriteBit(false); 96 (context.h264_parameter_set.frame_mbs_only_flag ? 1 : 2);
122 writer.WriteUe(0); 97
123 writer.WriteUe(context.h264_parameter_set.num_refidx_l0_default_active); 98 writer.WriteUe(16);
124 writer.WriteUe(context.h264_parameter_set.num_refidx_l1_default_active); 99 writer.WriteBit(false);
125 writer.WriteBit(((context.h264_parameter_set.flags >> 2) & 1) != 0); 100 writer.WriteUe(context.h264_parameter_set.pic_width_in_mbs - 1);
126 writer.WriteU(static_cast<s32>((context.h264_parameter_set.flags >> 32) & 0x3), 2); 101 writer.WriteUe(pic_height - 1);
127 s32 pic_init_qp = static_cast<s32>((context.h264_parameter_set.flags >> 16) & 0x3f); 102 writer.WriteBit(context.h264_parameter_set.frame_mbs_only_flag != 0);
128 pic_init_qp = (pic_init_qp << 26) >> 26;
129 writer.WriteSe(pic_init_qp);
130 writer.WriteSe(0);
131 s32 chroma_qp_index_offset =
132 static_cast<s32>((context.h264_parameter_set.flags >> 22) & 0x1f);
133 chroma_qp_index_offset = (chroma_qp_index_offset << 27) >> 27;
134 103
135 writer.WriteSe(chroma_qp_index_offset); 104 if (!context.h264_parameter_set.frame_mbs_only_flag) {
136 writer.WriteBit(context.h264_parameter_set.deblocking_filter_control_flag != 0); 105 writer.WriteBit(context.h264_parameter_set.flags.mbaff_frame.Value() != 0);
137 writer.WriteBit(((context.h264_parameter_set.flags >> 3) & 1) != 0); 106 }
138 writer.WriteBit(context.h264_parameter_set.redundant_pic_count_flag != 0);
139 writer.WriteBit(context.h264_parameter_set.transform_8x8_mode_flag != 0);
140 107
108 writer.WriteBit(context.h264_parameter_set.flags.direct_8x8_inference.Value() != 0);
109 writer.WriteBit(false); // Frame cropping flag
110 writer.WriteBit(false); // VUI parameter present flag
111
112 writer.End();
113
114 // H264 PPS
115 writer.WriteU(1, 24);
116 writer.WriteU(0, 1);
117 writer.WriteU(3, 2);
118 writer.WriteU(8, 5);
119
120 writer.WriteUe(0);
121 writer.WriteUe(0);
122
123 writer.WriteBit(context.h264_parameter_set.entropy_coding_mode_flag != 0);
124 writer.WriteBit(false);
125 writer.WriteUe(0);
126 writer.WriteUe(context.h264_parameter_set.num_refidx_l0_default_active);
127 writer.WriteUe(context.h264_parameter_set.num_refidx_l1_default_active);
128 writer.WriteBit(context.h264_parameter_set.flags.weighted_pred.Value() != 0);
129 writer.WriteU(static_cast<s32>(context.h264_parameter_set.weighted_bipred_idc.Value()), 2);
130 s32 pic_init_qp = static_cast<s32>(context.h264_parameter_set.pic_init_qp_minus26.Value());
131 writer.WriteSe(pic_init_qp);
132 writer.WriteSe(0);
133 s32 chroma_qp_index_offset =
134 static_cast<s32>(context.h264_parameter_set.chroma_qp_index_offset.Value());
135
136 writer.WriteSe(chroma_qp_index_offset);
137 writer.WriteBit(context.h264_parameter_set.deblocking_filter_control_present_flag != 0);
138 writer.WriteBit(context.h264_parameter_set.flags.constrained_intra_pred.Value() != 0);
139 writer.WriteBit(context.h264_parameter_set.redundant_pic_cnt_present_flag != 0);
140 writer.WriteBit(context.h264_parameter_set.transform_8x8_mode_flag != 0);
141
142 writer.WriteBit(true);
143
144 for (s32 index = 0; index < 6; index++) {
141 writer.WriteBit(true); 145 writer.WriteBit(true);
146 std::span<const u8> matrix{context.weight_scale};
147 writer.WriteScalingList(matrix, index * 16, 16);
148 }
142 149
143 for (s32 index = 0; index < 6; index++) { 150 if (context.h264_parameter_set.transform_8x8_mode_flag) {
151 for (s32 index = 0; index < 2; index++) {
144 writer.WriteBit(true); 152 writer.WriteBit(true);
145 const auto matrix_x4 = 153 std::span<const u8> matrix{context.weight_scale_8x8};
146 std::vector<u8>(context.scaling_matrix_4.begin(), context.scaling_matrix_4.end()); 154 writer.WriteScalingList(matrix, index * 64, 64);
147 writer.WriteScalingList(matrix_x4, index * 16, 16);
148 }
149
150 if (context.h264_parameter_set.transform_8x8_mode_flag) {
151 for (s32 index = 0; index < 2; index++) {
152 writer.WriteBit(true);
153 const auto matrix_x8 = std::vector<u8>(context.scaling_matrix_8.begin(),
154 context.scaling_matrix_8.end());
155
156 writer.WriteScalingList(matrix_x8, index * 64, 64);
157 }
158 } 155 }
156 }
159 157
160 s32 chroma_qp_index_offset2 = 158 s32 chroma_qp_index_offset2 =
161 static_cast<s32>((context.h264_parameter_set.flags >> 27) & 0x1f); 159 static_cast<s32>(context.h264_parameter_set.second_chroma_qp_index_offset.Value());
162 chroma_qp_index_offset2 = (chroma_qp_index_offset2 << 27) >> 27;
163 160
164 writer.WriteSe(chroma_qp_index_offset2); 161 writer.WriteSe(chroma_qp_index_offset2);
165 162
166 writer.End(); 163 writer.End();
167 164
168 const auto& encoded_header = writer.GetByteArray(); 165 const auto& encoded_header = writer.GetByteArray();
169 frame.resize(encoded_header.size() + context.frame_data_size); 166 frame.resize(encoded_header.size() + context.stream_len);
170 std::memcpy(frame.data(), encoded_header.data(), encoded_header.size()); 167 std::memcpy(frame.data(), encoded_header.data(), encoded_header.size());
171 168
172 gpu.MemoryManager().ReadBlock(state.frame_bitstream_offset, 169 gpu.MemoryManager().ReadBlock(state.frame_bitstream_offset,
173 frame.data() + encoded_header.size(), 170 frame.data() + encoded_header.size(), context.stream_len);
174 context.frame_data_size);
175 }
176 171
177 return frame; 172 return frame;
178} 173}
@@ -202,7 +197,7 @@ void H264BitWriter::WriteBit(bool state) {
202 WriteBits(state ? 1 : 0, 1); 197 WriteBits(state ? 1 : 0, 1);
203} 198}
204 199
205void H264BitWriter::WriteScalingList(const std::vector<u8>& list, s32 start, s32 count) { 200void H264BitWriter::WriteScalingList(std::span<const u8> list, s32 start, s32 count) {
206 std::vector<u8> scan(count); 201 std::vector<u8> scan(count);
207 if (count == 16) { 202 if (count == 16) {
208 std::memcpy(scan.data(), zig_zag_scan.data(), scan.size()); 203 std::memcpy(scan.data(), zig_zag_scan.data(), scan.size());
diff --git a/src/video_core/command_classes/codecs/h264.h b/src/video_core/command_classes/codecs/h264.h
index 0f3a1d9f3..bfe84a472 100644
--- a/src/video_core/command_classes/codecs/h264.h
+++ b/src/video_core/command_classes/codecs/h264.h
@@ -20,7 +20,9 @@
20 20
21#pragma once 21#pragma once
22 22
23#include <span>
23#include <vector> 24#include <vector>
25#include "common/bit_field.h"
24#include "common/common_funcs.h" 26#include "common/common_funcs.h"
25#include "common/common_types.h" 27#include "common/common_types.h"
26#include "video_core/command_classes/nvdec_common.h" 28#include "video_core/command_classes/nvdec_common.h"
@@ -48,7 +50,7 @@ public:
48 50
49 /// Based on section 7.3.2.1.1.1 and Table 7-4 in the H.264 specification 51 /// Based on section 7.3.2.1.1.1 and Table 7-4 in the H.264 specification
50 /// Writes the scaling matrices of the sream 52 /// Writes the scaling matrices of the sream
51 void WriteScalingList(const std::vector<u8>& list, s32 start, s32 count); 53 void WriteScalingList(std::span<const u8> list, s32 start, s32 count);
52 54
53 /// Return the bitstream as a vector. 55 /// Return the bitstream as a vector.
54 [[nodiscard]] std::vector<u8>& GetByteArray(); 56 [[nodiscard]] std::vector<u8>& GetByteArray();
@@ -78,40 +80,110 @@ public:
78 const NvdecCommon::NvdecRegisters& state, bool is_first_frame = false); 80 const NvdecCommon::NvdecRegisters& state, bool is_first_frame = false);
79 81
80private: 82private:
83 std::vector<u8> frame;
84 GPU& gpu;
85
81 struct H264ParameterSet { 86 struct H264ParameterSet {
82 u32 log2_max_pic_order_cnt{}; 87 s32 log2_max_pic_order_cnt_lsb_minus4; ///< 0x00
83 u32 delta_pic_order_always_zero_flag{}; 88 s32 delta_pic_order_always_zero_flag; ///< 0x04
84 u32 frame_mbs_only_flag{}; 89 s32 frame_mbs_only_flag; ///< 0x08
85 u32 pic_width_in_mbs{}; 90 u32 pic_width_in_mbs; ///< 0x0C
86 u32 pic_height_in_map_units{}; 91 u32 frame_height_in_map_units; ///< 0x10
87 INSERT_PADDING_WORDS(1); 92 union { ///< 0x14
88 u32 entropy_coding_mode_flag{}; 93 BitField<0, 2, u32> tile_format;
89 u32 bottom_field_pic_order_flag{}; 94 BitField<2, 3, u32> gob_height;
90 u32 num_refidx_l0_default_active{}; 95 };
91 u32 num_refidx_l1_default_active{}; 96 u32 entropy_coding_mode_flag; ///< 0x18
92 u32 deblocking_filter_control_flag{}; 97 s32 pic_order_present_flag; ///< 0x1C
93 u32 redundant_pic_count_flag{}; 98 s32 num_refidx_l0_default_active; ///< 0x20
94 u32 transform_8x8_mode_flag{}; 99 s32 num_refidx_l1_default_active; ///< 0x24
95 INSERT_PADDING_WORDS(9); 100 s32 deblocking_filter_control_present_flag; ///< 0x28
96 u64 flags{}; 101 s32 redundant_pic_cnt_present_flag; ///< 0x2C
97 u32 frame_number{}; 102 u32 transform_8x8_mode_flag; ///< 0x30
98 u32 frame_number2{}; 103 u32 pitch_luma; ///< 0x34
104 u32 pitch_chroma; ///< 0x38
105 u32 luma_top_offset; ///< 0x3C
106 u32 luma_bot_offset; ///< 0x40
107 u32 luma_frame_offset; ///< 0x44
108 u32 chroma_top_offset; ///< 0x48
109 u32 chroma_bot_offset; ///< 0x4C
110 u32 chroma_frame_offset; ///< 0x50
111 u32 hist_buffer_size; ///< 0x54
112 union { ///< 0x58
113 union {
114 BitField<0, 1, u64> mbaff_frame;
115 BitField<1, 1, u64> direct_8x8_inference;
116 BitField<2, 1, u64> weighted_pred;
117 BitField<3, 1, u64> constrained_intra_pred;
118 BitField<4, 1, u64> ref_pic;
119 BitField<5, 1, u64> field_pic;
120 BitField<6, 1, u64> bottom_field;
121 BitField<7, 1, u64> second_field;
122 } flags;
123 BitField<8, 4, u64> log2_max_frame_num_minus4;
124 BitField<12, 2, u64> chroma_format_idc;
125 BitField<14, 2, u64> pic_order_cnt_type;
126 BitField<16, 6, s64> pic_init_qp_minus26;
127 BitField<22, 5, s64> chroma_qp_index_offset;
128 BitField<27, 5, s64> second_chroma_qp_index_offset;
129 BitField<32, 2, u64> weighted_bipred_idc;
130 BitField<34, 7, u64> curr_pic_idx;
131 BitField<41, 5, u64> curr_col_idx;
132 BitField<46, 16, u64> frame_number;
133 BitField<62, 1, u64> frame_surfaces;
134 BitField<63, 1, u64> output_memory_layout;
135 };
99 }; 136 };
100 static_assert(sizeof(H264ParameterSet) == 0x68, "H264ParameterSet is an invalid size"); 137 static_assert(sizeof(H264ParameterSet) == 0x60, "H264ParameterSet is an invalid size");
101 138
102 struct H264DecoderContext { 139 struct H264DecoderContext {
103 INSERT_PADDING_BYTES(0x48); 140 INSERT_PADDING_WORDS_NOINIT(18); ///< 0x0000
104 u32 frame_data_size{}; 141 u32 stream_len; ///< 0x0048
105 INSERT_PADDING_BYTES(0xc); 142 INSERT_PADDING_WORDS_NOINIT(3); ///< 0x004C
106 H264ParameterSet h264_parameter_set{}; 143 H264ParameterSet h264_parameter_set; ///< 0x0058
107 INSERT_PADDING_BYTES(0x100); 144 INSERT_PADDING_WORDS_NOINIT(66); ///< 0x00B8
108 std::array<u8, 0x60> scaling_matrix_4; 145 std::array<u8, 0x60> weight_scale; ///< 0x01C0
109 std::array<u8, 0x80> scaling_matrix_8; 146 std::array<u8, 0x80> weight_scale_8x8; ///< 0x0220
110 }; 147 };
111 static_assert(sizeof(H264DecoderContext) == 0x2a0, "H264DecoderContext is an invalid size"); 148 static_assert(sizeof(H264DecoderContext) == 0x2A0, "H264DecoderContext is an invalid size");
112 149
113 std::vector<u8> frame; 150#define ASSERT_POSITION(field_name, position) \
114 GPU& gpu; 151 static_assert(offsetof(H264ParameterSet, field_name) == position, \
152 "Field " #field_name " has invalid position")
153
154 ASSERT_POSITION(log2_max_pic_order_cnt_lsb_minus4, 0x00);
155 ASSERT_POSITION(delta_pic_order_always_zero_flag, 0x04);
156 ASSERT_POSITION(frame_mbs_only_flag, 0x08);
157 ASSERT_POSITION(pic_width_in_mbs, 0x0C);
158 ASSERT_POSITION(frame_height_in_map_units, 0x10);
159 ASSERT_POSITION(tile_format, 0x14);
160 ASSERT_POSITION(entropy_coding_mode_flag, 0x18);
161 ASSERT_POSITION(pic_order_present_flag, 0x1C);
162 ASSERT_POSITION(num_refidx_l0_default_active, 0x20);
163 ASSERT_POSITION(num_refidx_l1_default_active, 0x24);
164 ASSERT_POSITION(deblocking_filter_control_present_flag, 0x28);
165 ASSERT_POSITION(redundant_pic_cnt_present_flag, 0x2C);
166 ASSERT_POSITION(transform_8x8_mode_flag, 0x30);
167 ASSERT_POSITION(pitch_luma, 0x34);
168 ASSERT_POSITION(pitch_chroma, 0x38);
169 ASSERT_POSITION(luma_top_offset, 0x3C);
170 ASSERT_POSITION(luma_bot_offset, 0x40);
171 ASSERT_POSITION(luma_frame_offset, 0x44);
172 ASSERT_POSITION(chroma_top_offset, 0x48);
173 ASSERT_POSITION(chroma_bot_offset, 0x4C);
174 ASSERT_POSITION(chroma_frame_offset, 0x50);
175 ASSERT_POSITION(hist_buffer_size, 0x54);
176 ASSERT_POSITION(flags, 0x58);
177#undef ASSERT_POSITION
178
179#define ASSERT_POSITION(field_name, position) \
180 static_assert(offsetof(H264DecoderContext, field_name) == position, \
181 "Field " #field_name " has invalid position")
182
183 ASSERT_POSITION(stream_len, 0x48);
184 ASSERT_POSITION(h264_parameter_set, 0x58);
185 ASSERT_POSITION(weight_scale, 0x1C0);
186#undef ASSERT_POSITION
115}; 187};
116 188
117} // namespace Decoder 189} // namespace Decoder
diff --git a/src/video_core/command_classes/codecs/vp9.cpp b/src/video_core/command_classes/codecs/vp9.cpp
index 29bb31418..902bc2a98 100644
--- a/src/video_core/command_classes/codecs/vp9.cpp
+++ b/src/video_core/command_classes/codecs/vp9.cpp
@@ -354,7 +354,7 @@ void VP9::WriteMvProbabilityUpdate(VpxRangeEncoder& writer, u8 new_prob, u8 old_
354} 354}
355 355
356Vp9PictureInfo VP9::GetVp9PictureInfo(const NvdecCommon::NvdecRegisters& state) { 356Vp9PictureInfo VP9::GetVp9PictureInfo(const NvdecCommon::NvdecRegisters& state) {
357 PictureInfo picture_info{}; 357 PictureInfo picture_info;
358 gpu.MemoryManager().ReadBlock(state.picture_info_offset, &picture_info, sizeof(PictureInfo)); 358 gpu.MemoryManager().ReadBlock(state.picture_info_offset, &picture_info, sizeof(PictureInfo));
359 Vp9PictureInfo vp9_info = picture_info.Convert(); 359 Vp9PictureInfo vp9_info = picture_info.Convert();
360 360
@@ -370,7 +370,7 @@ Vp9PictureInfo VP9::GetVp9PictureInfo(const NvdecCommon::NvdecRegisters& state)
370} 370}
371 371
372void VP9::InsertEntropy(u64 offset, Vp9EntropyProbs& dst) { 372void VP9::InsertEntropy(u64 offset, Vp9EntropyProbs& dst) {
373 EntropyProbs entropy{}; 373 EntropyProbs entropy;
374 gpu.MemoryManager().ReadBlock(offset, &entropy, sizeof(EntropyProbs)); 374 gpu.MemoryManager().ReadBlock(offset, &entropy, sizeof(EntropyProbs));
375 entropy.Convert(dst); 375 entropy.Convert(dst);
376} 376}
diff --git a/src/video_core/command_classes/codecs/vp9_types.h b/src/video_core/command_classes/codecs/vp9_types.h
index 139501a1c..2da14f3ca 100644
--- a/src/video_core/command_classes/codecs/vp9_types.h
+++ b/src/video_core/command_classes/codecs/vp9_types.h
@@ -15,10 +15,10 @@ class GPU;
15 15
16namespace Decoder { 16namespace Decoder {
17struct Vp9FrameDimensions { 17struct Vp9FrameDimensions {
18 s16 width{}; 18 s16 width;
19 s16 height{}; 19 s16 height;
20 s16 luma_pitch{}; 20 s16 luma_pitch;
21 s16 chroma_pitch{}; 21 s16 chroma_pitch;
22}; 22};
23static_assert(sizeof(Vp9FrameDimensions) == 0x8, "Vp9 Vp9FrameDimensions is an invalid size"); 23static_assert(sizeof(Vp9FrameDimensions) == 0x8, "Vp9 Vp9FrameDimensions is an invalid size");
24 24
@@ -49,87 +49,87 @@ enum class TxMode {
49}; 49};
50 50
51struct Segmentation { 51struct Segmentation {
52 u8 enabled{}; 52 u8 enabled;
53 u8 update_map{}; 53 u8 update_map;
54 u8 temporal_update{}; 54 u8 temporal_update;
55 u8 abs_delta{}; 55 u8 abs_delta;
56 std::array<u32, 8> feature_mask{}; 56 std::array<u32, 8> feature_mask;
57 std::array<std::array<s16, 4>, 8> feature_data{}; 57 std::array<std::array<s16, 4>, 8> feature_data;
58}; 58};
59static_assert(sizeof(Segmentation) == 0x64, "Segmentation is an invalid size"); 59static_assert(sizeof(Segmentation) == 0x64, "Segmentation is an invalid size");
60 60
61struct LoopFilter { 61struct LoopFilter {
62 u8 mode_ref_delta_enabled{}; 62 u8 mode_ref_delta_enabled;
63 std::array<s8, 4> ref_deltas{}; 63 std::array<s8, 4> ref_deltas;
64 std::array<s8, 2> mode_deltas{}; 64 std::array<s8, 2> mode_deltas;
65}; 65};
66static_assert(sizeof(LoopFilter) == 0x7, "LoopFilter is an invalid size"); 66static_assert(sizeof(LoopFilter) == 0x7, "LoopFilter is an invalid size");
67 67
68struct Vp9EntropyProbs { 68struct Vp9EntropyProbs {
69 std::array<u8, 36> y_mode_prob{}; 69 std::array<u8, 36> y_mode_prob; ///< 0x0000
70 std::array<u8, 64> partition_prob{}; 70 std::array<u8, 64> partition_prob; ///< 0x0024
71 std::array<u8, 1728> coef_probs{}; 71 std::array<u8, 1728> coef_probs; ///< 0x0064
72 std::array<u8, 8> switchable_interp_prob{}; 72 std::array<u8, 8> switchable_interp_prob; ///< 0x0724
73 std::array<u8, 28> inter_mode_prob{}; 73 std::array<u8, 28> inter_mode_prob; ///< 0x072C
74 std::array<u8, 4> intra_inter_prob{}; 74 std::array<u8, 4> intra_inter_prob; ///< 0x0748
75 std::array<u8, 5> comp_inter_prob{}; 75 std::array<u8, 5> comp_inter_prob; ///< 0x074C
76 std::array<u8, 10> single_ref_prob{}; 76 std::array<u8, 10> single_ref_prob; ///< 0x0751
77 std::array<u8, 5> comp_ref_prob{}; 77 std::array<u8, 5> comp_ref_prob; ///< 0x075B
78 std::array<u8, 6> tx_32x32_prob{}; 78 std::array<u8, 6> tx_32x32_prob; ///< 0x0760
79 std::array<u8, 4> tx_16x16_prob{}; 79 std::array<u8, 4> tx_16x16_prob; ///< 0x0766
80 std::array<u8, 2> tx_8x8_prob{}; 80 std::array<u8, 2> tx_8x8_prob; ///< 0x076A
81 std::array<u8, 3> skip_probs{}; 81 std::array<u8, 3> skip_probs; ///< 0x076C
82 std::array<u8, 3> joints{}; 82 std::array<u8, 3> joints; ///< 0x076F
83 std::array<u8, 2> sign{}; 83 std::array<u8, 2> sign; ///< 0x0772
84 std::array<u8, 20> classes{}; 84 std::array<u8, 20> classes; ///< 0x0774
85 std::array<u8, 2> class_0{}; 85 std::array<u8, 2> class_0; ///< 0x0788
86 std::array<u8, 20> prob_bits{}; 86 std::array<u8, 20> prob_bits; ///< 0x078A
87 std::array<u8, 12> class_0_fr{}; 87 std::array<u8, 12> class_0_fr; ///< 0x079E
88 std::array<u8, 6> fr{}; 88 std::array<u8, 6> fr; ///< 0x07AA
89 std::array<u8, 2> class_0_hp{}; 89 std::array<u8, 2> class_0_hp; ///< 0x07B0
90 std::array<u8, 2> high_precision{}; 90 std::array<u8, 2> high_precision; ///< 0x07B2
91}; 91};
92static_assert(sizeof(Vp9EntropyProbs) == 0x7B4, "Vp9EntropyProbs is an invalid size"); 92static_assert(sizeof(Vp9EntropyProbs) == 0x7B4, "Vp9EntropyProbs is an invalid size");
93 93
94struct Vp9PictureInfo { 94struct Vp9PictureInfo {
95 bool is_key_frame{}; 95 bool is_key_frame;
96 bool intra_only{}; 96 bool intra_only;
97 bool last_frame_was_key{}; 97 bool last_frame_was_key;
98 bool frame_size_changed{}; 98 bool frame_size_changed;
99 bool error_resilient_mode{}; 99 bool error_resilient_mode;
100 bool last_frame_shown{}; 100 bool last_frame_shown;
101 bool show_frame{}; 101 bool show_frame;
102 std::array<s8, 4> ref_frame_sign_bias{}; 102 std::array<s8, 4> ref_frame_sign_bias;
103 s32 base_q_index{}; 103 s32 base_q_index;
104 s32 y_dc_delta_q{}; 104 s32 y_dc_delta_q;
105 s32 uv_dc_delta_q{}; 105 s32 uv_dc_delta_q;
106 s32 uv_ac_delta_q{}; 106 s32 uv_ac_delta_q;
107 bool lossless{}; 107 bool lossless;
108 s32 transform_mode{}; 108 s32 transform_mode;
109 bool allow_high_precision_mv{}; 109 bool allow_high_precision_mv;
110 s32 interp_filter{}; 110 s32 interp_filter;
111 s32 reference_mode{}; 111 s32 reference_mode;
112 s8 comp_fixed_ref{}; 112 s8 comp_fixed_ref;
113 std::array<s8, 2> comp_var_ref{}; 113 std::array<s8, 2> comp_var_ref;
114 s32 log2_tile_cols{}; 114 s32 log2_tile_cols;
115 s32 log2_tile_rows{}; 115 s32 log2_tile_rows;
116 bool segment_enabled{}; 116 bool segment_enabled;
117 bool segment_map_update{}; 117 bool segment_map_update;
118 bool segment_map_temporal_update{}; 118 bool segment_map_temporal_update;
119 s32 segment_abs_delta{}; 119 s32 segment_abs_delta;
120 std::array<u32, 8> segment_feature_enable{}; 120 std::array<u32, 8> segment_feature_enable;
121 std::array<std::array<s16, 4>, 8> segment_feature_data{}; 121 std::array<std::array<s16, 4>, 8> segment_feature_data;
122 bool mode_ref_delta_enabled{}; 122 bool mode_ref_delta_enabled;
123 bool use_prev_in_find_mv_refs{}; 123 bool use_prev_in_find_mv_refs;
124 std::array<s8, 4> ref_deltas{}; 124 std::array<s8, 4> ref_deltas;
125 std::array<s8, 2> mode_deltas{}; 125 std::array<s8, 2> mode_deltas;
126 Vp9EntropyProbs entropy{}; 126 Vp9EntropyProbs entropy;
127 Vp9FrameDimensions frame_size{}; 127 Vp9FrameDimensions frame_size;
128 u8 first_level{}; 128 u8 first_level;
129 u8 sharpness_level{}; 129 u8 sharpness_level;
130 u32 bitstream_size{}; 130 u32 bitstream_size;
131 std::array<u64, 4> frame_offsets{}; 131 std::array<u64, 4> frame_offsets;
132 std::array<bool, 4> refresh_frame{}; 132 std::array<bool, 4> refresh_frame;
133}; 133};
134 134
135struct Vp9FrameContainer { 135struct Vp9FrameContainer {
@@ -138,35 +138,35 @@ struct Vp9FrameContainer {
138}; 138};
139 139
140struct PictureInfo { 140struct PictureInfo {
141 INSERT_PADDING_WORDS(12); 141 INSERT_PADDING_WORDS_NOINIT(12); ///< 0x00
142 u32 bitstream_size{}; 142 u32 bitstream_size; ///< 0x30
143 INSERT_PADDING_WORDS(5); 143 INSERT_PADDING_WORDS_NOINIT(5); ///< 0x34
144 Vp9FrameDimensions last_frame_size{}; 144 Vp9FrameDimensions last_frame_size; ///< 0x48
145 Vp9FrameDimensions golden_frame_size{}; 145 Vp9FrameDimensions golden_frame_size; ///< 0x50
146 Vp9FrameDimensions alt_frame_size{}; 146 Vp9FrameDimensions alt_frame_size; ///< 0x58
147 Vp9FrameDimensions current_frame_size{}; 147 Vp9FrameDimensions current_frame_size; ///< 0x60
148 u32 vp9_flags{}; 148 u32 vp9_flags; ///< 0x68
149 std::array<s8, 4> ref_frame_sign_bias{}; 149 std::array<s8, 4> ref_frame_sign_bias; ///< 0x6C
150 u8 first_level{}; 150 u8 first_level; ///< 0x70
151 u8 sharpness_level{}; 151 u8 sharpness_level; ///< 0x71
152 u8 base_q_index{}; 152 u8 base_q_index; ///< 0x72
153 u8 y_dc_delta_q{}; 153 u8 y_dc_delta_q; ///< 0x73
154 u8 uv_ac_delta_q{}; 154 u8 uv_ac_delta_q; ///< 0x74
155 u8 uv_dc_delta_q{}; 155 u8 uv_dc_delta_q; ///< 0x75
156 u8 lossless{}; 156 u8 lossless; ///< 0x76
157 u8 tx_mode{}; 157 u8 tx_mode; ///< 0x77
158 u8 allow_high_precision_mv{}; 158 u8 allow_high_precision_mv; ///< 0x78
159 u8 interp_filter{}; 159 u8 interp_filter; ///< 0x79
160 u8 reference_mode{}; 160 u8 reference_mode; ///< 0x7A
161 s8 comp_fixed_ref{}; 161 s8 comp_fixed_ref; ///< 0x7B
162 std::array<s8, 2> comp_var_ref{}; 162 std::array<s8, 2> comp_var_ref; ///< 0x7C
163 u8 log2_tile_cols{}; 163 u8 log2_tile_cols; ///< 0x7E
164 u8 log2_tile_rows{}; 164 u8 log2_tile_rows; ///< 0x7F
165 Segmentation segmentation{}; 165 Segmentation segmentation; ///< 0x80
166 LoopFilter loop_filter{}; 166 LoopFilter loop_filter; ///< 0xE4
167 INSERT_PADDING_BYTES(5); 167 INSERT_PADDING_BYTES_NOINIT(5); ///< 0xEB
168 u32 surface_params{}; 168 u32 surface_params; ///< 0xF0
169 INSERT_PADDING_WORDS(3); 169 INSERT_PADDING_WORDS_NOINIT(3); ///< 0xF4
170 170
171 [[nodiscard]] Vp9PictureInfo Convert() const { 171 [[nodiscard]] Vp9PictureInfo Convert() const {
172 return { 172 return {
@@ -176,6 +176,7 @@ struct PictureInfo {
176 .frame_size_changed = (vp9_flags & FrameFlags::FrameSizeChanged) != 0, 176 .frame_size_changed = (vp9_flags & FrameFlags::FrameSizeChanged) != 0,
177 .error_resilient_mode = (vp9_flags & FrameFlags::ErrorResilientMode) != 0, 177 .error_resilient_mode = (vp9_flags & FrameFlags::ErrorResilientMode) != 0,
178 .last_frame_shown = (vp9_flags & FrameFlags::LastShowFrame) != 0, 178 .last_frame_shown = (vp9_flags & FrameFlags::LastShowFrame) != 0,
179 .show_frame = false,
179 .ref_frame_sign_bias = ref_frame_sign_bias, 180 .ref_frame_sign_bias = ref_frame_sign_bias,
180 .base_q_index = base_q_index, 181 .base_q_index = base_q_index,
181 .y_dc_delta_q = y_dc_delta_q, 182 .y_dc_delta_q = y_dc_delta_q,
@@ -204,45 +205,48 @@ struct PictureInfo {
204 !(vp9_flags == (FrameFlags::LastFrameIsKeyFrame)), 205 !(vp9_flags == (FrameFlags::LastFrameIsKeyFrame)),
205 .ref_deltas = loop_filter.ref_deltas, 206 .ref_deltas = loop_filter.ref_deltas,
206 .mode_deltas = loop_filter.mode_deltas, 207 .mode_deltas = loop_filter.mode_deltas,
208 .entropy{},
207 .frame_size = current_frame_size, 209 .frame_size = current_frame_size,
208 .first_level = first_level, 210 .first_level = first_level,
209 .sharpness_level = sharpness_level, 211 .sharpness_level = sharpness_level,
210 .bitstream_size = bitstream_size, 212 .bitstream_size = bitstream_size,
213 .frame_offsets{},
214 .refresh_frame{},
211 }; 215 };
212 } 216 }
213}; 217};
214static_assert(sizeof(PictureInfo) == 0x100, "PictureInfo is an invalid size"); 218static_assert(sizeof(PictureInfo) == 0x100, "PictureInfo is an invalid size");
215 219
216struct EntropyProbs { 220struct EntropyProbs {
217 INSERT_PADDING_BYTES(1024); 221 INSERT_PADDING_BYTES_NOINIT(1024); ///< 0x0000
218 std::array<u8, 28> inter_mode_prob{}; 222 std::array<u8, 28> inter_mode_prob; ///< 0x0400
219 std::array<u8, 4> intra_inter_prob{}; 223 std::array<u8, 4> intra_inter_prob; ///< 0x041C
220 INSERT_PADDING_BYTES(80); 224 INSERT_PADDING_BYTES_NOINIT(80); ///< 0x0420
221 std::array<u8, 2> tx_8x8_prob{}; 225 std::array<u8, 2> tx_8x8_prob; ///< 0x0470
222 std::array<u8, 4> tx_16x16_prob{}; 226 std::array<u8, 4> tx_16x16_prob; ///< 0x0472
223 std::array<u8, 6> tx_32x32_prob{}; 227 std::array<u8, 6> tx_32x32_prob; ///< 0x0476
224 std::array<u8, 4> y_mode_prob_e8{}; 228 std::array<u8, 4> y_mode_prob_e8; ///< 0x047C
225 std::array<std::array<u8, 8>, 4> y_mode_prob_e0e7{}; 229 std::array<std::array<u8, 8>, 4> y_mode_prob_e0e7; ///< 0x0480
226 INSERT_PADDING_BYTES(64); 230 INSERT_PADDING_BYTES_NOINIT(64); ///< 0x04A0
227 std::array<u8, 64> partition_prob{}; 231 std::array<u8, 64> partition_prob; ///< 0x04E0
228 INSERT_PADDING_BYTES(10); 232 INSERT_PADDING_BYTES_NOINIT(10); ///< 0x0520
229 std::array<u8, 8> switchable_interp_prob{}; 233 std::array<u8, 8> switchable_interp_prob; ///< 0x052A
230 std::array<u8, 5> comp_inter_prob{}; 234 std::array<u8, 5> comp_inter_prob; ///< 0x0532
231 std::array<u8, 3> skip_probs{}; 235 std::array<u8, 3> skip_probs; ///< 0x0537
232 INSERT_PADDING_BYTES(1); 236 INSERT_PADDING_BYTES_NOINIT(1); ///< 0x053A
233 std::array<u8, 3> joints{}; 237 std::array<u8, 3> joints; ///< 0x053B
234 std::array<u8, 2> sign{}; 238 std::array<u8, 2> sign; ///< 0x053E
235 std::array<u8, 2> class_0{}; 239 std::array<u8, 2> class_0; ///< 0x0540
236 std::array<u8, 6> fr{}; 240 std::array<u8, 6> fr; ///< 0x0542
237 std::array<u8, 2> class_0_hp{}; 241 std::array<u8, 2> class_0_hp; ///< 0x0548
238 std::array<u8, 2> high_precision{}; 242 std::array<u8, 2> high_precision; ///< 0x054A
239 std::array<u8, 20> classes{}; 243 std::array<u8, 20> classes; ///< 0x054C
240 std::array<u8, 12> class_0_fr{}; 244 std::array<u8, 12> class_0_fr; ///< 0x0560
241 std::array<u8, 20> pred_bits{}; 245 std::array<u8, 20> pred_bits; ///< 0x056C
242 std::array<u8, 10> single_ref_prob{}; 246 std::array<u8, 10> single_ref_prob; ///< 0x0580
243 std::array<u8, 5> comp_ref_prob{}; 247 std::array<u8, 5> comp_ref_prob; ///< 0x058A
244 INSERT_PADDING_BYTES(17); 248 INSERT_PADDING_BYTES_NOINIT(17); ///< 0x058F
245 std::array<u8, 2304> coef_probs{}; 249 std::array<u8, 2304> coef_probs; ///< 0x05A0
246 250
247 void Convert(Vp9EntropyProbs& fc) { 251 void Convert(Vp9EntropyProbs& fc) {
248 fc.inter_mode_prob = inter_mode_prob; 252 fc.inter_mode_prob = inter_mode_prob;
@@ -293,10 +297,45 @@ struct RefPoolElement {
293}; 297};
294 298
295struct FrameContexts { 299struct FrameContexts {
296 s64 from{}; 300 s64 from;
297 bool adapted{}; 301 bool adapted;
298 Vp9EntropyProbs probs{}; 302 Vp9EntropyProbs probs;
299}; 303};
300 304
305#define ASSERT_POSITION(field_name, position) \
306 static_assert(offsetof(Vp9EntropyProbs, field_name) == position, \
307 "Field " #field_name " has invalid position")
308
309ASSERT_POSITION(partition_prob, 0x0024);
310ASSERT_POSITION(switchable_interp_prob, 0x0724);
311ASSERT_POSITION(sign, 0x0772);
312ASSERT_POSITION(class_0_fr, 0x079E);
313ASSERT_POSITION(high_precision, 0x07B2);
314#undef ASSERT_POSITION
315
316#define ASSERT_POSITION(field_name, position) \
317 static_assert(offsetof(PictureInfo, field_name) == position, \
318 "Field " #field_name " has invalid position")
319
320ASSERT_POSITION(bitstream_size, 0x30);
321ASSERT_POSITION(last_frame_size, 0x48);
322ASSERT_POSITION(first_level, 0x70);
323ASSERT_POSITION(segmentation, 0x80);
324ASSERT_POSITION(loop_filter, 0xE4);
325ASSERT_POSITION(surface_params, 0xF0);
326#undef ASSERT_POSITION
327
328#define ASSERT_POSITION(field_name, position) \
329 static_assert(offsetof(EntropyProbs, field_name) == position, \
330 "Field " #field_name " has invalid position")
331
332ASSERT_POSITION(inter_mode_prob, 0x400);
333ASSERT_POSITION(tx_8x8_prob, 0x470);
334ASSERT_POSITION(partition_prob, 0x4E0);
335ASSERT_POSITION(class_0, 0x540);
336ASSERT_POSITION(class_0_fr, 0x560);
337ASSERT_POSITION(coef_probs, 0x5A0);
338#undef ASSERT_POSITION
339
301}; // namespace Decoder 340}; // namespace Decoder
302}; // namespace Tegra 341}; // namespace Tegra
diff --git a/src/video_core/command_classes/nvdec.cpp b/src/video_core/command_classes/nvdec.cpp
index e4f919afd..b5e3b70fc 100644
--- a/src/video_core/command_classes/nvdec.cpp
+++ b/src/video_core/command_classes/nvdec.cpp
@@ -8,22 +8,21 @@
8 8
9namespace Tegra { 9namespace Tegra {
10 10
11Nvdec::Nvdec(GPU& gpu_) : gpu(gpu_), codec(std::make_unique<Codec>(gpu)) {} 11#define NVDEC_REG_INDEX(field_name) \
12 (offsetof(NvdecCommon::NvdecRegisters, field_name) / sizeof(u64))
13
14Nvdec::Nvdec(GPU& gpu_) : gpu(gpu_), state{}, codec(std::make_unique<Codec>(gpu, state)) {}
12 15
13Nvdec::~Nvdec() = default; 16Nvdec::~Nvdec() = default;
14 17
15void Nvdec::ProcessMethod(Method method, u32 argument) { 18void Nvdec::ProcessMethod(u32 method, u32 argument) {
16 if (method == Method::SetVideoCodec) { 19 state.reg_array[method] = static_cast<u64>(argument) << 8;
17 codec->StateWrite(static_cast<u32>(method), argument);
18 } else {
19 codec->StateWrite(static_cast<u32>(method), static_cast<u64>(argument) << 8);
20 }
21 20
22 switch (method) { 21 switch (method) {
23 case Method::SetVideoCodec: 22 case NVDEC_REG_INDEX(set_codec_id):
24 codec->SetTargetCodec(static_cast<NvdecCommon::VideoCodec>(argument)); 23 codec->SetTargetCodec(static_cast<NvdecCommon::VideoCodec>(argument));
25 break; 24 break;
26 case Method::Execute: 25 case NVDEC_REG_INDEX(execute):
27 Execute(); 26 Execute();
28 break; 27 break;
29 } 28 }
diff --git a/src/video_core/command_classes/nvdec.h b/src/video_core/command_classes/nvdec.h
index e66be80b8..6e1da0b04 100644
--- a/src/video_core/command_classes/nvdec.h
+++ b/src/video_core/command_classes/nvdec.h
@@ -14,16 +14,11 @@ class GPU;
14 14
15class Nvdec { 15class Nvdec {
16public: 16public:
17 enum class Method : u32 {
18 SetVideoCodec = 0x80,
19 Execute = 0xc0,
20 };
21
22 explicit Nvdec(GPU& gpu); 17 explicit Nvdec(GPU& gpu);
23 ~Nvdec(); 18 ~Nvdec();
24 19
25 /// Writes the method into the state, Invoke Execute() if encountered 20 /// Writes the method into the state, Invoke Execute() if encountered
26 void ProcessMethod(Method method, u32 argument); 21 void ProcessMethod(u32 method, u32 argument);
27 22
28 /// Return most recently decoded frame 23 /// Return most recently decoded frame
29 [[nodiscard]] AVFramePtr GetFrame(); 24 [[nodiscard]] AVFramePtr GetFrame();
@@ -33,6 +28,7 @@ private:
33 void Execute(); 28 void Execute();
34 29
35 GPU& gpu; 30 GPU& gpu;
31 NvdecCommon::NvdecRegisters state;
36 std::unique_ptr<Codec> codec; 32 std::unique_ptr<Codec> codec;
37}; 33};
38} // namespace Tegra 34} // namespace Tegra
diff --git a/src/video_core/command_classes/nvdec_common.h b/src/video_core/command_classes/nvdec_common.h
index 01b5e086d..6a24e00a0 100644
--- a/src/video_core/command_classes/nvdec_common.h
+++ b/src/video_core/command_classes/nvdec_common.h
@@ -4,40 +4,13 @@
4 4
5#pragma once 5#pragma once
6 6
7#include "common/bit_field.h"
7#include "common/common_funcs.h" 8#include "common/common_funcs.h"
8#include "common/common_types.h" 9#include "common/common_types.h"
9 10
10namespace Tegra::NvdecCommon { 11namespace Tegra::NvdecCommon {
11 12
12struct NvdecRegisters { 13enum class VideoCodec : u64 {
13 INSERT_PADDING_WORDS(256);
14 u64 set_codec_id{};
15 INSERT_PADDING_WORDS(254);
16 u64 set_platform_id{};
17 u64 picture_info_offset{};
18 u64 frame_bitstream_offset{};
19 u64 frame_number{};
20 u64 h264_slice_data_offsets{};
21 u64 h264_mv_dump_offset{};
22 INSERT_PADDING_WORDS(6);
23 u64 frame_stats_offset{};
24 u64 h264_last_surface_luma_offset{};
25 u64 h264_last_surface_chroma_offset{};
26 std::array<u64, 17> surface_luma_offset{};
27 std::array<u64, 17> surface_chroma_offset{};
28 INSERT_PADDING_WORDS(132);
29 u64 vp9_entropy_probs_offset{};
30 u64 vp9_backward_updates_offset{};
31 u64 vp9_last_frame_segmap_offset{};
32 u64 vp9_curr_frame_segmap_offset{};
33 INSERT_PADDING_WORDS(2);
34 u64 vp9_last_frame_mvs_offset{};
35 u64 vp9_curr_frame_mvs_offset{};
36 INSERT_PADDING_WORDS(2);
37};
38static_assert(sizeof(NvdecRegisters) == (0xBC0), "NvdecRegisters is incorrect size");
39
40enum class VideoCodec : u32 {
41 None = 0x0, 14 None = 0x0,
42 H264 = 0x3, 15 H264 = 0x3,
43 Vp8 = 0x5, 16 Vp8 = 0x5,
@@ -45,4 +18,76 @@ enum class VideoCodec : u32 {
45 Vp9 = 0x9, 18 Vp9 = 0x9,
46}; 19};
47 20
21// NVDEC should use a 32-bit address space, but is mapped to 64-bit,
22// doubling the sizes here is compensating for that.
23struct NvdecRegisters {
24 static constexpr std::size_t NUM_REGS = 0x178;
25
26 union {
27 struct {
28 INSERT_PADDING_WORDS_NOINIT(256); ///< 0x0000
29 VideoCodec set_codec_id; ///< 0x0400
30 INSERT_PADDING_WORDS_NOINIT(126); ///< 0x0408
31 u64 execute; ///< 0x0600
32 INSERT_PADDING_WORDS_NOINIT(126); ///< 0x0608
33 struct { ///< 0x0800
34 union {
35 BitField<0, 3, VideoCodec> codec;
36 BitField<4, 1, u64> gp_timer_on;
37 BitField<13, 1, u64> mb_timer_on;
38 BitField<14, 1, u64> intra_frame_pslc;
39 BitField<17, 1, u64> all_intra_frame;
40 };
41 } control_params;
42 u64 picture_info_offset; ///< 0x0808
43 u64 frame_bitstream_offset; ///< 0x0810
44 u64 frame_number; ///< 0x0818
45 u64 h264_slice_data_offsets; ///< 0x0820
46 u64 h264_mv_dump_offset; ///< 0x0828
47 INSERT_PADDING_WORDS_NOINIT(6); ///< 0x0830
48 u64 frame_stats_offset; ///< 0x0848
49 u64 h264_last_surface_luma_offset; ///< 0x0850
50 u64 h264_last_surface_chroma_offset; ///< 0x0858
51 std::array<u64, 17> surface_luma_offset; ///< 0x0860
52 std::array<u64, 17> surface_chroma_offset; ///< 0x08E8
53 INSERT_PADDING_WORDS_NOINIT(132); ///< 0x0970
54 u64 vp9_entropy_probs_offset; ///< 0x0B80
55 u64 vp9_backward_updates_offset; ///< 0x0B88
56 u64 vp9_last_frame_segmap_offset; ///< 0x0B90
57 u64 vp9_curr_frame_segmap_offset; ///< 0x0B98
58 INSERT_PADDING_WORDS_NOINIT(2); ///< 0x0BA0
59 u64 vp9_last_frame_mvs_offset; ///< 0x0BA8
60 u64 vp9_curr_frame_mvs_offset; ///< 0x0BB0
61 INSERT_PADDING_WORDS_NOINIT(2); ///< 0x0BB8
62 };
63 std::array<u64, NUM_REGS> reg_array;
64 };
65};
66static_assert(sizeof(NvdecRegisters) == (0xBC0), "NvdecRegisters is incorrect size");
67
68#define ASSERT_REG_POSITION(field_name, position) \
69 static_assert(offsetof(NvdecRegisters, field_name) == position * sizeof(u64), \
70 "Field " #field_name " has invalid position")
71
72ASSERT_REG_POSITION(set_codec_id, 0x80);
73ASSERT_REG_POSITION(execute, 0xC0);
74ASSERT_REG_POSITION(control_params, 0x100);
75ASSERT_REG_POSITION(picture_info_offset, 0x101);
76ASSERT_REG_POSITION(frame_bitstream_offset, 0x102);
77ASSERT_REG_POSITION(frame_number, 0x103);
78ASSERT_REG_POSITION(h264_slice_data_offsets, 0x104);
79ASSERT_REG_POSITION(frame_stats_offset, 0x109);
80ASSERT_REG_POSITION(h264_last_surface_luma_offset, 0x10A);
81ASSERT_REG_POSITION(h264_last_surface_chroma_offset, 0x10B);
82ASSERT_REG_POSITION(surface_luma_offset, 0x10C);
83ASSERT_REG_POSITION(surface_chroma_offset, 0x11D);
84ASSERT_REG_POSITION(vp9_entropy_probs_offset, 0x170);
85ASSERT_REG_POSITION(vp9_backward_updates_offset, 0x171);
86ASSERT_REG_POSITION(vp9_last_frame_segmap_offset, 0x172);
87ASSERT_REG_POSITION(vp9_curr_frame_segmap_offset, 0x173);
88ASSERT_REG_POSITION(vp9_last_frame_mvs_offset, 0x175);
89ASSERT_REG_POSITION(vp9_curr_frame_mvs_offset, 0x176);
90
91#undef ASSERT_REG_POSITION
92
48} // namespace Tegra::NvdecCommon 93} // namespace Tegra::NvdecCommon
diff --git a/src/video_core/host_shaders/CMakeLists.txt b/src/video_core/host_shaders/CMakeLists.txt
index 2208e1922..c9cff7450 100644
--- a/src/video_core/host_shaders/CMakeLists.txt
+++ b/src/video_core/host_shaders/CMakeLists.txt
@@ -18,7 +18,10 @@ set(SHADER_FILES
18 vulkan_uint8.comp 18 vulkan_uint8.comp
19) 19)
20 20
21find_program(GLSLANGVALIDATOR "glslangValidator" REQUIRED) 21find_program(GLSLANGVALIDATOR "glslangValidator")
22if ("${GLSLANGVALIDATOR}" STREQUAL "GLSLANGVALIDATOR-NOTFOUND")
23 message(FATAL_ERROR "Required program `glslangValidator` not found.")
24endif()
22 25
23set(GLSL_FLAGS "") 26set(GLSL_FLAGS "")
24set(QUIET_FLAG "--quiet") 27set(QUIET_FLAG "--quiet")
diff --git a/src/video_core/renderer_base.h b/src/video_core/renderer_base.h
index 320ee8d30..63d8ad42a 100644
--- a/src/video_core/renderer_base.h
+++ b/src/video_core/renderer_base.h
@@ -42,6 +42,8 @@ public:
42 42
43 [[nodiscard]] virtual RasterizerInterface* ReadRasterizer() = 0; 43 [[nodiscard]] virtual RasterizerInterface* ReadRasterizer() = 0;
44 44
45 [[nodiscard]] virtual std::string GetDeviceVendor() const = 0;
46
45 // Getter/setter functions: 47 // Getter/setter functions:
46 // ------------------------ 48 // ------------------------
47 49
diff --git a/src/video_core/renderer_opengl/gl_device.cpp b/src/video_core/renderer_opengl/gl_device.cpp
index 3f4532ca7..3b00614e7 100644
--- a/src/video_core/renderer_opengl/gl_device.cpp
+++ b/src/video_core/renderer_opengl/gl_device.cpp
@@ -202,13 +202,13 @@ Device::Device() {
202 LOG_ERROR(Render_OpenGL, "OpenGL 4.6 is not available"); 202 LOG_ERROR(Render_OpenGL, "OpenGL 4.6 is not available");
203 throw std::runtime_error{"Insufficient version"}; 203 throw std::runtime_error{"Insufficient version"};
204 } 204 }
205 const std::string_view vendor = reinterpret_cast<const char*>(glGetString(GL_VENDOR)); 205 vendor_name = reinterpret_cast<const char*>(glGetString(GL_VENDOR));
206 const std::string_view version = reinterpret_cast<const char*>(glGetString(GL_VERSION)); 206 const std::string_view version = reinterpret_cast<const char*>(glGetString(GL_VERSION));
207 const std::vector extensions = GetExtensions(); 207 const std::vector extensions = GetExtensions();
208 208
209 const bool is_nvidia = vendor == "NVIDIA Corporation"; 209 const bool is_nvidia = vendor_name == "NVIDIA Corporation";
210 const bool is_amd = vendor == "ATI Technologies Inc."; 210 const bool is_amd = vendor_name == "ATI Technologies Inc.";
211 const bool is_intel = vendor == "Intel"; 211 const bool is_intel = vendor_name == "Intel";
212 212
213#ifdef __unix__ 213#ifdef __unix__
214 const bool is_linux = true; 214 const bool is_linux = true;
@@ -275,6 +275,56 @@ Device::Device() {
275 } 275 }
276} 276}
277 277
278std::string Device::GetVendorName() const {
279 if (vendor_name == "NVIDIA Corporation") {
280 return "NVIDIA";
281 }
282 if (vendor_name == "ATI Technologies Inc.") {
283 return "AMD";
284 }
285 if (vendor_name == "Intel") {
286 // For Mesa, `Intel` is an overloaded vendor string that could mean crocus or iris.
287 // Simply return `INTEL` for those as well as the Windows driver.
288 return "INTEL";
289 }
290 if (vendor_name == "Intel Open Source Technology Center") {
291 return "I965";
292 }
293 if (vendor_name == "Mesa Project") {
294 return "I915";
295 }
296 if (vendor_name == "Mesa/X.org") {
297 // This vendor string is overloaded between llvmpipe, softpipe, and virgl, so just return
298 // MESA instead of one of those driver names.
299 return "MESA";
300 }
301 if (vendor_name == "AMD") {
302 return "RADEONSI";
303 }
304 if (vendor_name == "nouveau") {
305 return "NOUVEAU";
306 }
307 if (vendor_name == "X.Org") {
308 return "R600";
309 }
310 if (vendor_name == "Collabora Ltd") {
311 return "ZINK";
312 }
313 if (vendor_name == "Intel Corporation") {
314 return "OPENSWR";
315 }
316 if (vendor_name == "Microsoft Corporation") {
317 return "D3D12";
318 }
319 if (vendor_name == "NVIDIA") {
320 // Mesa's tegra driver reports `NVIDIA`. Only present in this list because the default
321 // strategy would have returned `NVIDIA` here for this driver, the same result as the
322 // proprietary driver.
323 return "TEGRA";
324 }
325 return vendor_name;
326}
327
278Device::Device(std::nullptr_t) { 328Device::Device(std::nullptr_t) {
279 max_uniform_buffers.fill(std::numeric_limits<u32>::max()); 329 max_uniform_buffers.fill(std::numeric_limits<u32>::max());
280 uniform_buffer_alignment = 4; 330 uniform_buffer_alignment = 4;
diff --git a/src/video_core/renderer_opengl/gl_device.h b/src/video_core/renderer_opengl/gl_device.h
index f24bd0c7b..2c2b13767 100644
--- a/src/video_core/renderer_opengl/gl_device.h
+++ b/src/video_core/renderer_opengl/gl_device.h
@@ -22,6 +22,8 @@ public:
22 explicit Device(); 22 explicit Device();
23 explicit Device(std::nullptr_t); 23 explicit Device(std::nullptr_t);
24 24
25 [[nodiscard]] std::string GetVendorName() const;
26
25 u32 GetMaxUniformBuffers(Tegra::Engines::ShaderType shader_type) const noexcept { 27 u32 GetMaxUniformBuffers(Tegra::Engines::ShaderType shader_type) const noexcept {
26 return max_uniform_buffers[static_cast<std::size_t>(shader_type)]; 28 return max_uniform_buffers[static_cast<std::size_t>(shader_type)];
27 } 29 }
@@ -130,6 +132,7 @@ private:
130 static bool TestVariableAoffi(); 132 static bool TestVariableAoffi();
131 static bool TestPreciseBug(); 133 static bool TestPreciseBug();
132 134
135 std::string vendor_name;
133 std::array<u32, Tegra::Engines::MaxShaderTypes> max_uniform_buffers{}; 136 std::array<u32, Tegra::Engines::MaxShaderTypes> max_uniform_buffers{};
134 std::array<BaseBindings, Tegra::Engines::MaxShaderTypes> base_bindings{}; 137 std::array<BaseBindings, Tegra::Engines::MaxShaderTypes> base_bindings{};
135 size_t uniform_buffer_alignment{}; 138 size_t uniform_buffer_alignment{};
diff --git a/src/video_core/renderer_opengl/gl_texture_cache.cpp b/src/video_core/renderer_opengl/gl_texture_cache.cpp
index e892bd9ba..ff0f03e99 100644
--- a/src/video_core/renderer_opengl/gl_texture_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_texture_cache.cpp
@@ -342,6 +342,20 @@ void ApplySwizzle(GLuint handle, PixelFormat format, std::array<SwizzleSource, 4
342[[nodiscard]] CopyOrigin MakeCopyOrigin(VideoCommon::Offset3D offset, 342[[nodiscard]] CopyOrigin MakeCopyOrigin(VideoCommon::Offset3D offset,
343 VideoCommon::SubresourceLayers subresource, GLenum target) { 343 VideoCommon::SubresourceLayers subresource, GLenum target) {
344 switch (target) { 344 switch (target) {
345 case GL_TEXTURE_1D:
346 return CopyOrigin{
347 .level = static_cast<GLint>(subresource.base_level),
348 .x = static_cast<GLint>(offset.x),
349 .y = static_cast<GLint>(0),
350 .z = static_cast<GLint>(0),
351 };
352 case GL_TEXTURE_1D_ARRAY:
353 return CopyOrigin{
354 .level = static_cast<GLint>(subresource.base_level),
355 .x = static_cast<GLint>(offset.x),
356 .y = static_cast<GLint>(0),
357 .z = static_cast<GLint>(subresource.base_layer),
358 };
345 case GL_TEXTURE_2D_ARRAY: 359 case GL_TEXTURE_2D_ARRAY:
346 case GL_TEXTURE_2D_MULTISAMPLE_ARRAY: 360 case GL_TEXTURE_2D_MULTISAMPLE_ARRAY:
347 return CopyOrigin{ 361 return CopyOrigin{
@@ -367,6 +381,18 @@ void ApplySwizzle(GLuint handle, PixelFormat format, std::array<SwizzleSource, 4
367 VideoCommon::SubresourceLayers dst_subresource, 381 VideoCommon::SubresourceLayers dst_subresource,
368 GLenum target) { 382 GLenum target) {
369 switch (target) { 383 switch (target) {
384 case GL_TEXTURE_1D:
385 return CopyRegion{
386 .width = static_cast<GLsizei>(extent.width),
387 .height = static_cast<GLsizei>(1),
388 .depth = static_cast<GLsizei>(1),
389 };
390 case GL_TEXTURE_1D_ARRAY:
391 return CopyRegion{
392 .width = static_cast<GLsizei>(extent.width),
393 .height = static_cast<GLsizei>(1),
394 .depth = static_cast<GLsizei>(dst_subresource.num_layers),
395 };
370 case GL_TEXTURE_2D_ARRAY: 396 case GL_TEXTURE_2D_ARRAY:
371 case GL_TEXTURE_2D_MULTISAMPLE_ARRAY: 397 case GL_TEXTURE_2D_MULTISAMPLE_ARRAY:
372 return CopyRegion{ 398 return CopyRegion{
diff --git a/src/video_core/renderer_opengl/renderer_opengl.h b/src/video_core/renderer_opengl/renderer_opengl.h
index cc19a110f..0b66f8332 100644
--- a/src/video_core/renderer_opengl/renderer_opengl.h
+++ b/src/video_core/renderer_opengl/renderer_opengl.h
@@ -70,6 +70,10 @@ public:
70 return &rasterizer; 70 return &rasterizer;
71 } 71 }
72 72
73 [[nodiscard]] std::string GetDeviceVendor() const override {
74 return device.GetVendorName();
75 }
76
73private: 77private:
74 /// Initializes the OpenGL state and creates persistent objects. 78 /// Initializes the OpenGL state and creates persistent objects.
75 void InitOpenGLObjects(); 79 void InitOpenGLObjects();
diff --git a/src/video_core/renderer_vulkan/renderer_vulkan.h b/src/video_core/renderer_vulkan/renderer_vulkan.h
index 72071316c..d7d17e110 100644
--- a/src/video_core/renderer_vulkan/renderer_vulkan.h
+++ b/src/video_core/renderer_vulkan/renderer_vulkan.h
@@ -47,6 +47,10 @@ public:
47 return &rasterizer; 47 return &rasterizer;
48 } 48 }
49 49
50 [[nodiscard]] std::string GetDeviceVendor() const override {
51 return device.GetDriverName();
52 }
53
50private: 54private:
51 void Report() const; 55 void Report() const;
52 56
diff --git a/src/video_core/texture_cache/texture_cache.h b/src/video_core/texture_cache/texture_cache.h
index c7cfd02b6..d8dbd3824 100644
--- a/src/video_core/texture_cache/texture_cache.h
+++ b/src/video_core/texture_cache/texture_cache.h
@@ -1057,9 +1057,6 @@ ImageId TextureCache<P>::JoinImages(const ImageInfo& info, GPUVAddr gpu_addr, VA
1057 std::vector<ImageId> right_aliased_ids; 1057 std::vector<ImageId> right_aliased_ids;
1058 std::vector<ImageId> bad_overlap_ids; 1058 std::vector<ImageId> bad_overlap_ids;
1059 ForEachImageInRegion(cpu_addr, size_bytes, [&](ImageId overlap_id, ImageBase& overlap) { 1059 ForEachImageInRegion(cpu_addr, size_bytes, [&](ImageId overlap_id, ImageBase& overlap) {
1060 if (info.type != overlap.info.type) {
1061 return;
1062 }
1063 if (info.type == ImageType::Linear) { 1060 if (info.type == ImageType::Linear) {
1064 if (info.pitch == overlap.info.pitch && gpu_addr == overlap.gpu_addr) { 1061 if (info.pitch == overlap.info.pitch && gpu_addr == overlap.gpu_addr) {
1065 // Alias linear images with the same pitch 1062 // Alias linear images with the same pitch
diff --git a/src/video_core/vulkan_common/vulkan_device.cpp b/src/video_core/vulkan_common/vulkan_device.cpp
index 23814afd2..f214510da 100644
--- a/src/video_core/vulkan_common/vulkan_device.cpp
+++ b/src/video_core/vulkan_common/vulkan_device.cpp
@@ -532,6 +532,27 @@ bool Device::IsFormatSupported(VkFormat wanted_format, VkFormatFeatureFlags want
532 return (supported_usage & wanted_usage) == wanted_usage; 532 return (supported_usage & wanted_usage) == wanted_usage;
533} 533}
534 534
535std::string Device::GetDriverName() const {
536 switch (driver_id) {
537 case VK_DRIVER_ID_AMD_PROPRIETARY:
538 return "AMD";
539 case VK_DRIVER_ID_AMD_OPEN_SOURCE:
540 return "AMDVLK";
541 case VK_DRIVER_ID_MESA_RADV:
542 return "RADV";
543 case VK_DRIVER_ID_NVIDIA_PROPRIETARY:
544 return "NVIDIA";
545 case VK_DRIVER_ID_INTEL_PROPRIETARY_WINDOWS:
546 return "INTEL";
547 case VK_DRIVER_ID_INTEL_OPEN_SOURCE_MESA:
548 return "ANV";
549 case VK_DRIVER_ID_MESA_LLVMPIPE:
550 return "LAVAPIPE";
551 default:
552 return vendor_name;
553 }
554}
555
535void Device::CheckSuitability(bool requires_swapchain) const { 556void Device::CheckSuitability(bool requires_swapchain) const {
536 std::bitset<REQUIRED_EXTENSIONS.size()> available_extensions; 557 std::bitset<REQUIRED_EXTENSIONS.size()> available_extensions;
537 bool has_swapchain = false; 558 bool has_swapchain = false;
diff --git a/src/video_core/vulkan_common/vulkan_device.h b/src/video_core/vulkan_common/vulkan_device.h
index 88b298196..96c0f8c60 100644
--- a/src/video_core/vulkan_common/vulkan_device.h
+++ b/src/video_core/vulkan_common/vulkan_device.h
@@ -45,6 +45,9 @@ public:
45 /// Reports a shader to Nsight Aftermath. 45 /// Reports a shader to Nsight Aftermath.
46 void SaveShader(const std::vector<u32>& spirv) const; 46 void SaveShader(const std::vector<u32>& spirv) const;
47 47
48 /// Returns the name of the VkDriverId reported from Vulkan.
49 std::string GetDriverName() const;
50
48 /// Returns the dispatch loader with direct function pointers of the device. 51 /// Returns the dispatch loader with direct function pointers of the device.
49 const vk::DeviceDispatch& GetDispatchLoader() const { 52 const vk::DeviceDispatch& GetDispatchLoader() const {
50 return dld; 53 return dld;
diff --git a/src/yuzu/debugger/profiler.cpp b/src/yuzu/debugger/profiler.cpp
index efdc6aa50..7a6f84d96 100644
--- a/src/yuzu/debugger/profiler.cpp
+++ b/src/yuzu/debugger/profiler.cpp
@@ -143,24 +143,25 @@ void MicroProfileWidget::hideEvent(QHideEvent* ev) {
143} 143}
144 144
145void MicroProfileWidget::mouseMoveEvent(QMouseEvent* ev) { 145void MicroProfileWidget::mouseMoveEvent(QMouseEvent* ev) {
146 MicroProfileMousePosition(ev->x() / x_scale, ev->y() / y_scale, 0); 146 MicroProfileMousePosition(ev->pos().x() / x_scale, ev->pos().y() / y_scale, 0);
147 ev->accept(); 147 ev->accept();
148} 148}
149 149
150void MicroProfileWidget::mousePressEvent(QMouseEvent* ev) { 150void MicroProfileWidget::mousePressEvent(QMouseEvent* ev) {
151 MicroProfileMousePosition(ev->x() / x_scale, ev->y() / y_scale, 0); 151 MicroProfileMousePosition(ev->pos().x() / x_scale, ev->pos().y() / y_scale, 0);
152 MicroProfileMouseButton(ev->buttons() & Qt::LeftButton, ev->buttons() & Qt::RightButton); 152 MicroProfileMouseButton(ev->buttons() & Qt::LeftButton, ev->buttons() & Qt::RightButton);
153 ev->accept(); 153 ev->accept();
154} 154}
155 155
156void MicroProfileWidget::mouseReleaseEvent(QMouseEvent* ev) { 156void MicroProfileWidget::mouseReleaseEvent(QMouseEvent* ev) {
157 MicroProfileMousePosition(ev->x() / x_scale, ev->y() / y_scale, 0); 157 MicroProfileMousePosition(ev->pos().x() / x_scale, ev->pos().y() / y_scale, 0);
158 MicroProfileMouseButton(ev->buttons() & Qt::LeftButton, ev->buttons() & Qt::RightButton); 158 MicroProfileMouseButton(ev->buttons() & Qt::LeftButton, ev->buttons() & Qt::RightButton);
159 ev->accept(); 159 ev->accept();
160} 160}
161 161
162void MicroProfileWidget::wheelEvent(QWheelEvent* ev) { 162void MicroProfileWidget::wheelEvent(QWheelEvent* ev) {
163 MicroProfileMousePosition(ev->x() / x_scale, ev->y() / y_scale, ev->delta() / 120); 163 MicroProfileMousePosition(ev->pos().x() / x_scale, ev->pos().y() / y_scale,
164 ev->angleDelta().y() / 120);
164 ev->accept(); 165 ev->accept();
165} 166}
166 167
diff --git a/src/yuzu/game_list.cpp b/src/yuzu/game_list.cpp
index da956c99b..e44907be8 100644
--- a/src/yuzu/game_list.cpp
+++ b/src/yuzu/game_list.cpp
@@ -521,7 +521,9 @@ void GameList::AddGamePopup(QMenu& context_menu, u64 program_id, const std::stri
521 QAction* remove_custom_config = remove_menu->addAction(tr("Remove Custom Configuration")); 521 QAction* remove_custom_config = remove_menu->addAction(tr("Remove Custom Configuration"));
522 remove_menu->addSeparator(); 522 remove_menu->addSeparator();
523 QAction* remove_all_content = remove_menu->addAction(tr("Remove All Installed Contents")); 523 QAction* remove_all_content = remove_menu->addAction(tr("Remove All Installed Contents"));
524 QAction* dump_romfs = context_menu.addAction(tr("Dump RomFS")); 524 QMenu* dump_romfs_menu = context_menu.addMenu(tr("Dump RomFS"));
525 QAction* dump_romfs = dump_romfs_menu->addAction(tr("Dump RomFS"));
526 QAction* dump_romfs_sdmc = dump_romfs_menu->addAction(tr("Dump RomFS to SDMC"));
525 QAction* copy_tid = context_menu.addAction(tr("Copy Title ID to Clipboard")); 527 QAction* copy_tid = context_menu.addAction(tr("Copy Title ID to Clipboard"));
526 QAction* navigate_to_gamedb_entry = context_menu.addAction(tr("Navigate to GameDB entry")); 528 QAction* navigate_to_gamedb_entry = context_menu.addAction(tr("Navigate to GameDB entry"));
527 context_menu.addSeparator(); 529 context_menu.addSeparator();
@@ -570,8 +572,12 @@ void GameList::AddGamePopup(QMenu& context_menu, u64 program_id, const std::stri
570 connect(remove_custom_config, &QAction::triggered, [this, program_id, path]() { 572 connect(remove_custom_config, &QAction::triggered, [this, program_id, path]() {
571 emit RemoveFileRequested(program_id, GameListRemoveTarget::CustomConfiguration, path); 573 emit RemoveFileRequested(program_id, GameListRemoveTarget::CustomConfiguration, path);
572 }); 574 });
573 connect(dump_romfs, &QAction::triggered, 575 connect(dump_romfs, &QAction::triggered, [this, program_id, path]() {
574 [this, program_id, path]() { emit DumpRomFSRequested(program_id, path); }); 576 emit DumpRomFSRequested(program_id, path, DumpRomFSTarget::Normal);
577 });
578 connect(dump_romfs_sdmc, &QAction::triggered, [this, program_id, path]() {
579 emit DumpRomFSRequested(program_id, path, DumpRomFSTarget::SDMC);
580 });
575 connect(copy_tid, &QAction::triggered, 581 connect(copy_tid, &QAction::triggered,
576 [this, program_id]() { emit CopyTIDRequested(program_id); }); 582 [this, program_id]() { emit CopyTIDRequested(program_id); });
577 connect(navigate_to_gamedb_entry, &QAction::triggered, [this, program_id]() { 583 connect(navigate_to_gamedb_entry, &QAction::triggered, [this, program_id]() {
diff --git a/src/yuzu/game_list.h b/src/yuzu/game_list.h
index b630e34ff..50402da51 100644
--- a/src/yuzu/game_list.h
+++ b/src/yuzu/game_list.h
@@ -45,6 +45,11 @@ enum class GameListRemoveTarget {
45 CustomConfiguration, 45 CustomConfiguration,
46}; 46};
47 47
48enum class DumpRomFSTarget {
49 Normal,
50 SDMC,
51};
52
48enum class InstalledEntryType { 53enum class InstalledEntryType {
49 Game, 54 Game,
50 Update, 55 Update,
@@ -92,7 +97,7 @@ signals:
92 void RemoveInstalledEntryRequested(u64 program_id, InstalledEntryType type); 97 void RemoveInstalledEntryRequested(u64 program_id, InstalledEntryType type);
93 void RemoveFileRequested(u64 program_id, GameListRemoveTarget target, 98 void RemoveFileRequested(u64 program_id, GameListRemoveTarget target,
94 const std::string& game_path); 99 const std::string& game_path);
95 void DumpRomFSRequested(u64 program_id, const std::string& game_path); 100 void DumpRomFSRequested(u64 program_id, const std::string& game_path, DumpRomFSTarget target);
96 void CopyTIDRequested(u64 program_id); 101 void CopyTIDRequested(u64 program_id);
97 void NavigateToGamedbEntryRequested(u64 program_id, 102 void NavigateToGamedbEntryRequested(u64 program_id,
98 const CompatibilityList& compatibility_list); 103 const CompatibilityList& compatibility_list);
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index 20f65d233..f462cd072 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -104,6 +104,7 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual
104#include "input_common/main.h" 104#include "input_common/main.h"
105#include "util/overlay_dialog.h" 105#include "util/overlay_dialog.h"
106#include "video_core/gpu.h" 106#include "video_core/gpu.h"
107#include "video_core/renderer_base.h"
107#include "video_core/shader_notify.h" 108#include "video_core/shader_notify.h"
108#include "yuzu/about_dialog.h" 109#include "yuzu/about_dialog.h"
109#include "yuzu/bootmanager.h" 110#include "yuzu/bootmanager.h"
@@ -1426,7 +1427,8 @@ void GMainWindow::BootGame(const QString& filename, std::size_t program_index, S
1426 const auto instruction_set_suffix = is_64bit ? " (64-bit)" : " (32-bit)"; 1427 const auto instruction_set_suffix = is_64bit ? " (64-bit)" : " (32-bit)";
1427 title_name += instruction_set_suffix; 1428 title_name += instruction_set_suffix;
1428 LOG_INFO(Frontend, "Booting game: {:016X} | {} | {}", title_id, title_name, title_version); 1429 LOG_INFO(Frontend, "Booting game: {:016X} | {} | {}", title_id, title_name, title_version);
1429 UpdateWindowTitle(title_name, title_version); 1430 const auto gpu_vendor = system.GPU().Renderer().GetDeviceVendor();
1431 UpdateWindowTitle(title_name, title_version, gpu_vendor);
1430 1432
1431 loading_screen->Prepare(system.GetAppLoader()); 1433 loading_screen->Prepare(system.GetAppLoader());
1432 loading_screen->show(); 1434 loading_screen->show();
@@ -1880,7 +1882,8 @@ void GMainWindow::RemoveCustomConfiguration(u64 program_id, const std::string& g
1880 } 1882 }
1881} 1883}
1882 1884
1883void GMainWindow::OnGameListDumpRomFS(u64 program_id, const std::string& game_path) { 1885void GMainWindow::OnGameListDumpRomFS(u64 program_id, const std::string& game_path,
1886 DumpRomFSTarget target) {
1884 const auto failed = [this] { 1887 const auto failed = [this] {
1885 QMessageBox::warning(this, tr("RomFS Extraction Failed!"), 1888 QMessageBox::warning(this, tr("RomFS Extraction Failed!"),
1886 tr("There was an error copying the RomFS files or the user " 1889 tr("There was an error copying the RomFS files or the user "
@@ -1908,7 +1911,10 @@ void GMainWindow::OnGameListDumpRomFS(u64 program_id, const std::string& game_pa
1908 return; 1911 return;
1909 } 1912 }
1910 1913
1911 const auto dump_dir = Common::FS::GetYuzuPath(Common::FS::YuzuPath::DumpDir); 1914 const auto dump_dir =
1915 target == DumpRomFSTarget::Normal
1916 ? Common::FS::GetYuzuPath(Common::FS::YuzuPath::DumpDir)
1917 : Common::FS::GetYuzuPath(Common::FS::YuzuPath::SDMCDir) / "atmosphere" / "contents";
1912 const auto romfs_dir = fmt::format("{:016X}/romfs", *romfs_title_id); 1918 const auto romfs_dir = fmt::format("{:016X}/romfs", *romfs_title_id);
1913 1919
1914 const auto path = Common::FS::PathToUTF8String(dump_dir / romfs_dir); 1920 const auto path = Common::FS::PathToUTF8String(dump_dir / romfs_dir);
@@ -1918,7 +1924,8 @@ void GMainWindow::OnGameListDumpRomFS(u64 program_id, const std::string& game_pa
1918 if (*romfs_title_id == program_id) { 1924 if (*romfs_title_id == program_id) {
1919 const u64 ivfc_offset = loader->ReadRomFSIVFCOffset(); 1925 const u64 ivfc_offset = loader->ReadRomFSIVFCOffset();
1920 const FileSys::PatchManager pm{program_id, system.GetFileSystemController(), installed}; 1926 const FileSys::PatchManager pm{program_id, system.GetFileSystemController(), installed};
1921 romfs = pm.PatchRomFS(file, ivfc_offset, FileSys::ContentRecordType::Program); 1927 romfs =
1928 pm.PatchRomFS(file, ivfc_offset, FileSys::ContentRecordType::Program, nullptr, false);
1922 } else { 1929 } else {
1923 romfs = installed.GetEntry(*romfs_title_id, FileSys::ContentRecordType::Data)->GetRomFS(); 1930 romfs = installed.GetEntry(*romfs_title_id, FileSys::ContentRecordType::Data)->GetRomFS();
1924 } 1931 }
@@ -2855,8 +2862,8 @@ void GMainWindow::MigrateConfigFiles() {
2855 } 2862 }
2856} 2863}
2857 2864
2858void GMainWindow::UpdateWindowTitle(const std::string& title_name, 2865void GMainWindow::UpdateWindowTitle(std::string_view title_name, std::string_view title_version,
2859 const std::string& title_version) { 2866 std::string_view gpu_vendor) {
2860 const auto branch_name = std::string(Common::g_scm_branch); 2867 const auto branch_name = std::string(Common::g_scm_branch);
2861 const auto description = std::string(Common::g_scm_desc); 2868 const auto description = std::string(Common::g_scm_desc);
2862 const auto build_id = std::string(Common::g_build_id); 2869 const auto build_id = std::string(Common::g_build_id);
@@ -2869,7 +2876,8 @@ void GMainWindow::UpdateWindowTitle(const std::string& title_name,
2869 if (title_name.empty()) { 2876 if (title_name.empty()) {
2870 setWindowTitle(QString::fromStdString(window_title)); 2877 setWindowTitle(QString::fromStdString(window_title));
2871 } else { 2878 } else {
2872 const auto run_title = fmt::format("{} | {} | {}", window_title, title_name, title_version); 2879 const auto run_title =
2880 fmt::format("{} | {} | {} | {}", window_title, title_name, title_version, gpu_vendor);
2873 setWindowTitle(QString::fromStdString(run_title)); 2881 setWindowTitle(QString::fromStdString(run_title));
2874 } 2882 }
2875} 2883}
diff --git a/src/yuzu/main.h b/src/yuzu/main.h
index 11f152cbe..45c8310e1 100644
--- a/src/yuzu/main.h
+++ b/src/yuzu/main.h
@@ -34,6 +34,7 @@ class QProgressDialog;
34class WaitTreeWidget; 34class WaitTreeWidget;
35enum class GameListOpenTarget; 35enum class GameListOpenTarget;
36enum class GameListRemoveTarget; 36enum class GameListRemoveTarget;
37enum class DumpRomFSTarget;
37enum class InstalledEntryType; 38enum class InstalledEntryType;
38class GameListPlaceholder; 39class GameListPlaceholder;
39 40
@@ -244,7 +245,7 @@ private slots:
244 void OnGameListRemoveInstalledEntry(u64 program_id, InstalledEntryType type); 245 void OnGameListRemoveInstalledEntry(u64 program_id, InstalledEntryType type);
245 void OnGameListRemoveFile(u64 program_id, GameListRemoveTarget target, 246 void OnGameListRemoveFile(u64 program_id, GameListRemoveTarget target,
246 const std::string& game_path); 247 const std::string& game_path);
247 void OnGameListDumpRomFS(u64 program_id, const std::string& game_path); 248 void OnGameListDumpRomFS(u64 program_id, const std::string& game_path, DumpRomFSTarget target);
248 void OnGameListCopyTID(u64 program_id); 249 void OnGameListCopyTID(u64 program_id);
249 void OnGameListNavigateToGamedbEntry(u64 program_id, 250 void OnGameListNavigateToGamedbEntry(u64 program_id,
250 const CompatibilityList& compatibility_list); 251 const CompatibilityList& compatibility_list);
@@ -287,8 +288,8 @@ private:
287 InstallResult InstallNSPXCI(const QString& filename); 288 InstallResult InstallNSPXCI(const QString& filename);
288 InstallResult InstallNCA(const QString& filename); 289 InstallResult InstallNCA(const QString& filename);
289 void MigrateConfigFiles(); 290 void MigrateConfigFiles();
290 void UpdateWindowTitle(const std::string& title_name = {}, 291 void UpdateWindowTitle(std::string_view title_name = {}, std::string_view title_version = {},
291 const std::string& title_version = {}); 292 std::string_view gpu_vendor = {});
292 void UpdateStatusBar(); 293 void UpdateStatusBar();
293 void UpdateStatusButtons(); 294 void UpdateStatusButtons();
294 void UpdateUISettings(); 295 void UpdateUISettings();