diff options
Diffstat (limited to 'src')
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 | ||
| 17 | namespace { | 18 | namespace { |
| @@ -68,7 +69,9 @@ namespace { | |||
| 68 | } // namespace | 69 | } // namespace |
| 69 | 70 | ||
| 70 | namespace AudioCore { | 71 | namespace AudioCore { |
| 71 | AudioRenderer::AudioRenderer(Core::Timing::CoreTiming& core_timing, Core::Memory::Memory& memory_, | 72 | constexpr s32 NUM_BUFFERS = 2; |
| 73 | |||
| 74 | AudioRenderer::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 | ||
| 97 | AudioRenderer::~AudioRenderer() = default; | 101 | AudioRenderer::~AudioRenderer() = default; |
| 98 | 102 | ||
| 103 | ResultCode AudioRenderer::Start() { | ||
| 104 | audio_out->StartStream(stream); | ||
| 105 | ReleaseAndQueueBuffers(); | ||
| 106 | return ResultSuccess; | ||
| 107 | } | ||
| 108 | |||
| 109 | ResultCode AudioRenderer::Stop() { | ||
| 110 | audio_out->StopStream(stream); | ||
| 111 | return ResultSuccess; | ||
| 112 | } | ||
| 113 | |||
| 99 | u32 AudioRenderer::GetSampleRate() const { | 114 | u32 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 | ||
| 115 | ResultCode AudioRenderer::UpdateAudioRenderer(const std::vector<u8>& input_params, | 130 | ResultCode 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 | ||
| 317 | void AudioRenderer::ReleaseAndQueueBuffers() { | 327 | void 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 | ||
| 1006 | s32 CommandGenerator::DecodePcm16(ServerVoiceInfo& voice_info, VoiceState& dsp_state, | 1006 | s32 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 | ||
| 1046 | s32 CommandGenerator::DecodeAdpcm(ServerVoiceInfo& voice_info, VoiceState& dsp_state, | 1045 | s32 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 | ||
| 249 | void ServerVoiceInfo::WriteOutStatus( | 288 | void 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 | ||
| 284 | bool ServerVoiceInfo::ShouldSkip() const { | 320 | bool 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 | ||
| 289 | bool ServerVoiceInfo::UpdateForCommandGeneration(VoiceContext& voice_context) { | 326 | bool ServerVoiceInfo::UpdateForCommandGeneration(VoiceContext& voice_context) { |
| @@ -381,7 +418,7 @@ bool ServerVoiceInfo::UpdateParametersForCommandGeneration( | |||
| 381 | void ServerVoiceInfo::FlushWaveBuffers( | 418 | void 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 | ||
| 441 | void 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 | |||
| 404 | VoiceContext::VoiceContext(std::size_t voice_count_) : voice_count{voice_count_} { | 452 | VoiceContext::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 | }; |
| 68 | static_assert(sizeof(WaveBuffer) == 0x38, "WaveBuffer is an invalid size"); | 70 | static_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 | ||
| 113 | class VoiceChannelResource { | 119 | class 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 | ||
| 256 | private: | 263 | private: |
| 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( | |||
| 345 | static void ApplyLayeredFS(VirtualFile& romfs, u64 title_id, ContentRecordType type, | 345 | static 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 | ||
| 404 | VirtualFile PatchManager::PatchRomFS(VirtualFile romfs, u64 ivfc_offset, ContentRecordType type, | 409 | VirtualFile 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 | ||
| 13 | constexpr u64 SDMC_TOTAL_SIZE = 0x10000000000; // 1 TiB | 13 | constexpr u64 SDMC_TOTAL_SIZE = 0x10000000000; // 1 TiB |
| 14 | 14 | ||
| 15 | SDMCFactory::SDMCFactory(VirtualDir dir_) | 15 | SDMCFactory::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 | ||
| 24 | SDMCFactory::~SDMCFactory() = default; | 25 | SDMCFactory::~SDMCFactory() = default; |
| 25 | 26 | ||
| 26 | ResultVal<VirtualDir> SDMCFactory::Open() const { | 27 | ResultVal<VirtualDir> SDMCFactory::Open() const { |
| 27 | return MakeResult<VirtualDir>(dir); | 28 | return MakeResult<VirtualDir>(sd_dir); |
| 29 | } | ||
| 30 | |||
| 31 | VirtualDir 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 | ||
| 30 | VirtualDir SDMCFactory::GetSDMCContentDirectory() const { | 39 | VirtualDir SDMCFactory::GetSDMCContentDirectory() const { |
| 31 | return GetOrCreateDirectoryRelative(dir, "/Nintendo/Contents"); | 40 | return GetOrCreateDirectoryRelative(sd_dir, "/Nintendo/Contents"); |
| 32 | } | 41 | } |
| 33 | 42 | ||
| 34 | RegisteredCache* SDMCFactory::GetSDMCContents() const { | 43 | RegisteredCache* SDMCFactory::GetSDMCContents() const { |
| @@ -40,11 +49,11 @@ PlaceholderCache* SDMCFactory::GetSDMCPlaceholder() const { | |||
| 40 | } | 49 | } |
| 41 | 50 | ||
| 42 | VirtualDir SDMCFactory::GetImageDirectory() const { | 51 | VirtualDir SDMCFactory::GetImageDirectory() const { |
| 43 | return GetOrCreateDirectoryRelative(dir, "/Nintendo/Album"); | 52 | return GetOrCreateDirectoryRelative(sd_dir, "/Nintendo/Album"); |
| 44 | } | 53 | } |
| 45 | 54 | ||
| 46 | u64 SDMCFactory::GetSDMCFreeSpace() const { | 55 | u64 SDMCFactory::GetSDMCFreeSpace() const { |
| 47 | return GetSDMCTotalSpace() - dir->GetSize(); | 56 | return GetSDMCTotalSpace() - sd_dir->GetSize(); |
| 48 | } | 57 | } |
| 49 | 58 | ||
| 50 | u64 SDMCFactory::GetSDMCTotalSpace() const { | 59 | u64 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 |
| 17 | class SDMCFactory { | 17 | class SDMCFactory { |
| 18 | public: | 18 | public: |
| 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 | ||
| 34 | private: | 35 | private: |
| 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 | ||
| 706 | FileSys::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 | |||
| 706 | FileSys::VirtualDir FileSystemController::GetModificationDumpRoot(u64 title_id) const { | 716 | FileSys::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 | ||
| 21 | constexpr ResultCode ERROR_CANNOT_FIND_ENTRY{ErrorModule::Mii, 4}; | 21 | constexpr ResultCode ERROR_CANNOT_FIND_ENTRY{ErrorModule::Mii, 4}; |
| 22 | 22 | ||
| 23 | constexpr std::size_t BaseMiiCount{2}; | ||
| 23 | constexpr std::size_t DefaultMiiCount{RawData::DefaultMii.size()}; | 24 | constexpr std::size_t DefaultMiiCount{RawData::DefaultMii.size()}; |
| 24 | 25 | ||
| 25 | constexpr MiiStoreData::Name DefaultMiiName{u'y', u'u', u'z', u'u'}; | 26 | constexpr 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 | ||
| 26 | Codec::Codec(GPU& gpu_) | 26 | Codec::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 | ||
| 30 | Codec::~Codec() { | 30 | Codec::~Codec() { |
| @@ -43,46 +43,48 @@ Codec::~Codec() { | |||
| 43 | avcodec_close(av_codec_ctx); | 43 | avcodec_close(av_codec_ctx); |
| 44 | } | 44 | } |
| 45 | 45 | ||
| 46 | void 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 | |||
| 46 | void Codec::SetTargetCodec(NvdecCommon::VideoCodec codec) { | 74 | void 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 | ||
| 53 | void 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 | |||
| 58 | void Codec::Decode() { | 81 | void 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 | ||
| 132 | std::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 | ||
| 35 | class Codec { | 35 | class Codec { |
| 36 | public: | 36 | public: |
| 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 | ||
| 55 | private: | 57 | private: |
| 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 | ||
| 46 | const std::vector<u8>& H264::ComposeFrameHeader(const NvdecCommon::NvdecRegisters& state, | 46 | const 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 | ||
| 205 | void H264BitWriter::WriteScalingList(const std::vector<u8>& list, s32 start, s32 count) { | 200 | void 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 | ||
| 80 | private: | 82 | private: |
| 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 | ||
| 356 | Vp9PictureInfo VP9::GetVp9PictureInfo(const NvdecCommon::NvdecRegisters& state) { | 356 | Vp9PictureInfo 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 | ||
| 372 | void VP9::InsertEntropy(u64 offset, Vp9EntropyProbs& dst) { | 372 | void 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 | ||
| 16 | namespace Decoder { | 16 | namespace Decoder { |
| 17 | struct Vp9FrameDimensions { | 17 | struct 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 | }; |
| 23 | static_assert(sizeof(Vp9FrameDimensions) == 0x8, "Vp9 Vp9FrameDimensions is an invalid size"); | 23 | static_assert(sizeof(Vp9FrameDimensions) == 0x8, "Vp9 Vp9FrameDimensions is an invalid size"); |
| 24 | 24 | ||
| @@ -49,87 +49,87 @@ enum class TxMode { | |||
| 49 | }; | 49 | }; |
| 50 | 50 | ||
| 51 | struct Segmentation { | 51 | struct 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 | }; |
| 59 | static_assert(sizeof(Segmentation) == 0x64, "Segmentation is an invalid size"); | 59 | static_assert(sizeof(Segmentation) == 0x64, "Segmentation is an invalid size"); |
| 60 | 60 | ||
| 61 | struct LoopFilter { | 61 | struct 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 | }; |
| 66 | static_assert(sizeof(LoopFilter) == 0x7, "LoopFilter is an invalid size"); | 66 | static_assert(sizeof(LoopFilter) == 0x7, "LoopFilter is an invalid size"); |
| 67 | 67 | ||
| 68 | struct Vp9EntropyProbs { | 68 | struct 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 | }; |
| 92 | static_assert(sizeof(Vp9EntropyProbs) == 0x7B4, "Vp9EntropyProbs is an invalid size"); | 92 | static_assert(sizeof(Vp9EntropyProbs) == 0x7B4, "Vp9EntropyProbs is an invalid size"); |
| 93 | 93 | ||
| 94 | struct Vp9PictureInfo { | 94 | struct 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 | ||
| 135 | struct Vp9FrameContainer { | 135 | struct Vp9FrameContainer { |
| @@ -138,35 +138,35 @@ struct Vp9FrameContainer { | |||
| 138 | }; | 138 | }; |
| 139 | 139 | ||
| 140 | struct PictureInfo { | 140 | struct 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 | }; |
| 214 | static_assert(sizeof(PictureInfo) == 0x100, "PictureInfo is an invalid size"); | 218 | static_assert(sizeof(PictureInfo) == 0x100, "PictureInfo is an invalid size"); |
| 215 | 219 | ||
| 216 | struct EntropyProbs { | 220 | struct 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 | ||
| 295 | struct FrameContexts { | 299 | struct 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 | |||
| 309 | ASSERT_POSITION(partition_prob, 0x0024); | ||
| 310 | ASSERT_POSITION(switchable_interp_prob, 0x0724); | ||
| 311 | ASSERT_POSITION(sign, 0x0772); | ||
| 312 | ASSERT_POSITION(class_0_fr, 0x079E); | ||
| 313 | ASSERT_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 | |||
| 320 | ASSERT_POSITION(bitstream_size, 0x30); | ||
| 321 | ASSERT_POSITION(last_frame_size, 0x48); | ||
| 322 | ASSERT_POSITION(first_level, 0x70); | ||
| 323 | ASSERT_POSITION(segmentation, 0x80); | ||
| 324 | ASSERT_POSITION(loop_filter, 0xE4); | ||
| 325 | ASSERT_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 | |||
| 332 | ASSERT_POSITION(inter_mode_prob, 0x400); | ||
| 333 | ASSERT_POSITION(tx_8x8_prob, 0x470); | ||
| 334 | ASSERT_POSITION(partition_prob, 0x4E0); | ||
| 335 | ASSERT_POSITION(class_0, 0x540); | ||
| 336 | ASSERT_POSITION(class_0_fr, 0x560); | ||
| 337 | ASSERT_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 | ||
| 9 | namespace Tegra { | 9 | namespace Tegra { |
| 10 | 10 | ||
| 11 | Nvdec::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 | |||
| 14 | Nvdec::Nvdec(GPU& gpu_) : gpu(gpu_), state{}, codec(std::make_unique<Codec>(gpu, state)) {} | ||
| 12 | 15 | ||
| 13 | Nvdec::~Nvdec() = default; | 16 | Nvdec::~Nvdec() = default; |
| 14 | 17 | ||
| 15 | void Nvdec::ProcessMethod(Method method, u32 argument) { | 18 | void 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 | ||
| 15 | class Nvdec { | 15 | class Nvdec { |
| 16 | public: | 16 | public: |
| 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 | ||
| 10 | namespace Tegra::NvdecCommon { | 11 | namespace Tegra::NvdecCommon { |
| 11 | 12 | ||
| 12 | struct NvdecRegisters { | 13 | enum 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 | }; | ||
| 38 | static_assert(sizeof(NvdecRegisters) == (0xBC0), "NvdecRegisters is incorrect size"); | ||
| 39 | |||
| 40 | enum 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. | ||
| 23 | struct 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 | }; | ||
| 66 | static_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 | |||
| 72 | ASSERT_REG_POSITION(set_codec_id, 0x80); | ||
| 73 | ASSERT_REG_POSITION(execute, 0xC0); | ||
| 74 | ASSERT_REG_POSITION(control_params, 0x100); | ||
| 75 | ASSERT_REG_POSITION(picture_info_offset, 0x101); | ||
| 76 | ASSERT_REG_POSITION(frame_bitstream_offset, 0x102); | ||
| 77 | ASSERT_REG_POSITION(frame_number, 0x103); | ||
| 78 | ASSERT_REG_POSITION(h264_slice_data_offsets, 0x104); | ||
| 79 | ASSERT_REG_POSITION(frame_stats_offset, 0x109); | ||
| 80 | ASSERT_REG_POSITION(h264_last_surface_luma_offset, 0x10A); | ||
| 81 | ASSERT_REG_POSITION(h264_last_surface_chroma_offset, 0x10B); | ||
| 82 | ASSERT_REG_POSITION(surface_luma_offset, 0x10C); | ||
| 83 | ASSERT_REG_POSITION(surface_chroma_offset, 0x11D); | ||
| 84 | ASSERT_REG_POSITION(vp9_entropy_probs_offset, 0x170); | ||
| 85 | ASSERT_REG_POSITION(vp9_backward_updates_offset, 0x171); | ||
| 86 | ASSERT_REG_POSITION(vp9_last_frame_segmap_offset, 0x172); | ||
| 87 | ASSERT_REG_POSITION(vp9_curr_frame_segmap_offset, 0x173); | ||
| 88 | ASSERT_REG_POSITION(vp9_last_frame_mvs_offset, 0x175); | ||
| 89 | ASSERT_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 | ||
| 21 | find_program(GLSLANGVALIDATOR "glslangValidator" REQUIRED) | 21 | find_program(GLSLANGVALIDATOR "glslangValidator") |
| 22 | if ("${GLSLANGVALIDATOR}" STREQUAL "GLSLANGVALIDATOR-NOTFOUND") | ||
| 23 | message(FATAL_ERROR "Required program `glslangValidator` not found.") | ||
| 24 | endif() | ||
| 22 | 25 | ||
| 23 | set(GLSL_FLAGS "") | 26 | set(GLSL_FLAGS "") |
| 24 | set(QUIET_FLAG "--quiet") | 27 | set(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 | ||
| 278 | std::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 | |||
| 278 | Device::Device(std::nullptr_t) { | 328 | Device::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 | |||
| 73 | private: | 77 | private: |
| 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 | |||
| 50 | private: | 54 | private: |
| 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 | ||
| 535 | std::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 | |||
| 535 | void Device::CheckSuitability(bool requires_swapchain) const { | 556 | void 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 | ||
| 145 | void MicroProfileWidget::mouseMoveEvent(QMouseEvent* ev) { | 145 | void 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 | ||
| 150 | void MicroProfileWidget::mousePressEvent(QMouseEvent* ev) { | 150 | void 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 | ||
| 156 | void MicroProfileWidget::mouseReleaseEvent(QMouseEvent* ev) { | 156 | void 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 | ||
| 162 | void MicroProfileWidget::wheelEvent(QWheelEvent* ev) { | 162 | void 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 | ||
| 48 | enum class DumpRomFSTarget { | ||
| 49 | Normal, | ||
| 50 | SDMC, | ||
| 51 | }; | ||
| 52 | |||
| 48 | enum class InstalledEntryType { | 53 | enum 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 | ||
| 1883 | void GMainWindow::OnGameListDumpRomFS(u64 program_id, const std::string& game_path) { | 1885 | void 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 | ||
| 2858 | void GMainWindow::UpdateWindowTitle(const std::string& title_name, | 2865 | void 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; | |||
| 34 | class WaitTreeWidget; | 34 | class WaitTreeWidget; |
| 35 | enum class GameListOpenTarget; | 35 | enum class GameListOpenTarget; |
| 36 | enum class GameListRemoveTarget; | 36 | enum class GameListRemoveTarget; |
| 37 | enum class DumpRomFSTarget; | ||
| 37 | enum class InstalledEntryType; | 38 | enum class InstalledEntryType; |
| 38 | class GameListPlaceholder; | 39 | class 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(); |