diff options
Diffstat (limited to '')
217 files changed, 4002 insertions, 2047 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index 60ec58eda..716256cd5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -47,9 +47,10 @@ if (NOT IS_MULTI_CONFIG AND NOT CMAKE_BUILD_TYPE) endif() if(EXISTS ${PROJECT_SOURCE_DIR}/hooks/pre-commit AND NOT EXISTS ${PROJECT_SOURCE_DIR}/.git/hooks/pre-commit) - message(STATUS "Copying pre-commit hook") - file(COPY hooks/pre-commit - DESTINATION ${PROJECT_SOURCE_DIR}/.git/hooks) + if (EXISTS ${PROJECT_SOURCE_DIR}/.git/) + message(STATUS "Copying pre-commit hook") + file(COPY hooks/pre-commit DESTINATION ${PROJECT_SOURCE_DIR}/.git/hooks) + endif() endif() # Sanity check : Check that all submodules are present @@ -253,11 +254,82 @@ if(ENABLE_QT) # Check for system Qt on Linux, fallback to bundled Qt if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux") - if (NOT YUZU_USE_BUNDLED_QT) - find_package(Qt5 ${QT_VERSION} COMPONENTS Widgets QUIET) - if (NOT Qt5_FOUND) - set(YUZU_USE_BUNDLED_QT ON CACHE BOOL "Download bundled Qt" FORCE) + find_package(Qt5 ${QT_VERSION} COMPONENTS Widgets) + if (NOT Qt5_FOUND OR YUZU_USE_BUNDLED_QT) + # Check for dependencies, then enable bundled Qt download + + # Check that the system GLIBCXX version is compatible + find_program(OBJDUMP objdump) + if ("${OBJDUMP}" STREQUAL "OBJDUMP-NOTFOUND") + message(FATAL_ERROR "Required program `objdump` not found.") + endif() + find_library(LIBSTDCXX libstdc++.so.6) + execute_process( + COMMAND + ${OBJDUMP} -T ${LIBSTDCXX} + COMMAND + grep GLIBCXX_3.4.28 + COMMAND + sed "s/[0-9a-f]*.* //" + COMMAND + sed "s/ .*//" + COMMAND + sort -u + OUTPUT_VARIABLE + GLIBCXX_MET + ) + if (NOT GLIBCXX_MET) + message(FATAL_ERROR "Qt too old or not found, and bundled Qt package is not \ + compatible with this system. Either install Qt ${QT_VERSION}, or provide the path \ + to Qt by setting the variable Qt5_ROOT.") + endif() + + # Check for headers + Include(FindPkgConfig REQUIRED) + pkg_check_modules(QT_DEP_GLU QUIET glu>=9.0.0) + if (NOT QT_DEP_GLU_FOUND) + message(FATAL_ERROR "Qt bundled pacakge dependency `glu` not found. \ + Perhaps `libglu1-mesa-dev` needs to be installed?") endif() + pkg_check_modules(QT_DEP_MESA QUIET dri>=20.0.8) + if (NOT QT_DEP_MESA_FOUND) + message(FATAL_ERROR "Qt bundled pacakge dependency `dri` not found. \ + Perhaps `mesa-common-dev` needs to be installed?") + endif() + + # Check for X libraries + set(BUNDLED_QT_REQUIREMENTS + libxcb-icccm.so.4 + libxcb-image.so.0 + libxcb-keysyms.so.1 + libxcb-randr.so.0 + libxcb-render-util.so.0 + libxcb-render.so.0 + libxcb-shape.so.0 + libxcb-shm.so.0 + libxcb-sync.so.1 + libxcb-xfixes.so.0 + libxcb-xinerama.so.0 + libxcb-xkb.so.1 + libxcb.so.1 + libxkbcommon-x11.so.0 + libxkbcommon.so.0 + ) + set(UNRESOLVED_QT_DEPS "") + foreach (REQUIREMENT ${BUNDLED_QT_REQUIREMENTS}) + find_library(BUNDLED_QT_${REQUIREMENT} ${REQUIREMENT}) + if ("${BUNDLED_QT_${REQUIREMENT}}" STREQUAL "BUNDLED_QT_${REQUIREMENT}-NOTFOUND") + set(UNRESOLVED_QT_DEPS ${UNRESOLVED_QT_DEPS} ${REQUIREMENT}) + endif() + unset(BUNDLED_QT_${REQUIREMENT}) + endforeach() + unset(BUNDLED_QT_REQUIREMENTS) + + if (NOT "${UNRESOLVED_QT_DEPS}" STREQUAL "") + message(FATAL_ERROR "Bundled Qt package missing required dependencies: ${UNRESOLVED_QT_DEPS}") + endif() + + set(YUZU_USE_BUNDLED_QT ON CACHE BOOL "Download bundled Qt" FORCE) endif() if (YUZU_USE_BUNDLED_QT) # Binary package currently does not support Qt webengine, so make sure it's disabled @@ -473,7 +545,15 @@ if (YUZU_USE_BUNDLED_FFMPEG) # FFmpeg has source that requires one of nasm or yasm to assemble it. # REQUIRED throws an error if not found here during configuration rather than during compilation. - find_program(ASSEMBLER NAMES nasm yasm REQUIRED) + find_program(ASSEMBLER NAMES nasm yasm) + if ("${ASSEMBLER}" STREQUAL "ASSEMBLER-NOTFOUND") + message(FATAL_ERROR "One of either `nasm` or `yasm` not found but is required.") + endif() + + find_program(AUTOCONF autoconf) + if ("${AUTOCONF}" STREQUAL "AUTOCONF-NOTFOUND") + message(FATAL_ERROR "Required program `autoconf` not found.") + endif() set(FFmpeg_PREFIX ${PROJECT_SOURCE_DIR}/externals/ffmpeg) set(FFmpeg_BUILD_DIR ${PROJECT_BINARY_DIR}/externals/ffmpeg) diff --git a/externals/libusb/CMakeLists.txt b/externals/libusb/CMakeLists.txt index 7180fd42a..151ddc462 100644 --- a/externals/libusb/CMakeLists.txt +++ b/externals/libusb/CMakeLists.txt @@ -1,10 +1,21 @@ -if (MINGW OR (${CMAKE_SYSTEM_NAME} MATCHES "Linux")) +if (MINGW OR (${CMAKE_SYSTEM_NAME} MATCHES "Linux") OR APPLE) set(LIBUSB_FOUND ON CACHE BOOL "libusb is present" FORCE) set(LIBUSB_VERSION "1.0.24" CACHE STRING "libusb version string" FORCE) # GNU toolchains for some reason doesn't work with the later half of this CMakeLists after # updating to 1.0.24, so we do it the old-fashioned way for now. + # Require autoconf and libtoolize here, rather than crash during compilation + find_program(AUTOCONF autoconf) + if ("${AUTOCONF}" STREQUAL "AUTOCONF-NOTFOUND") + message(FATAL_ERROR "Required program `autoconf` not found.") + endif() + + find_program(LIBTOOLIZE libtoolize) + if ("${LIBTOOLIZE}" STREQUAL "LIBTOOLIZE-NOTFOUND") + message(FATAL_ERROR "Required program `libtoolize` not found.") + endif() + set(LIBUSB_PREFIX "${CMAKE_CURRENT_BINARY_DIR}/libusb") set(LIBUSB_SRC_DIR "${CMAKE_CURRENT_SOURCE_DIR}/libusb") diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index f30dd49a3..f8ec8fea8 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -45,13 +45,23 @@ if (MSVC) /Zc:inline /Zc:throwingNew + # External headers diagnostics + /experimental:external # Enables the external headers options. This option isn't required in Visual Studio 2019 version 16.10 and later + /external:anglebrackets # Treats all headers included by #include <header>, where the header file is enclosed in angle brackets (< >), as external headers + /external:W0 # Sets the default warning level to 0 for external headers, effectively turning off warnings for external headers + # Warnings /W3 - /we4062 # enumerator 'identifier' in a switch of enum 'enumeration' is not handled + /we4018 # 'expression': signed/unsigned mismatch + /we4062 # Enumerator 'identifier' in a switch of enum 'enumeration' is not handled /we4101 # 'identifier': unreferenced local variable + /we4189 # 'identifier': local variable is initialized but not referenced /we4265 # 'class': class has virtual functions, but destructor is not virtual - /we4388 # signed/unsigned mismatch - /we4547 # 'operator' : operator before comma has no effect; expected operator with side-effect + /we4267 # 'var': conversion from 'size_t' to 'type', possible loss of data + /we4305 # 'context': truncation from 'type1' to 'type2' + /we4388 # 'expression': signed/unsigned mismatch + /we4389 # 'operator': signed/unsigned mismatch + /we4547 # 'operator': operator before comma has no effect; expected operator with side-effect /we4549 # 'operator1': operator before comma has no effect; did you intend 'operator2'? /we4555 # Expression has no effect; expected expression with side-effect /we4715 # 'function': not all control paths return a value @@ -72,6 +82,7 @@ else() -Werror=missing-declarations -Werror=missing-field-initializers -Werror=reorder + -Werror=sign-compare -Werror=switch -Werror=uninitialized -Werror=unused-function diff --git a/src/audio_core/CMakeLists.txt b/src/audio_core/CMakeLists.txt index d25a1a645..090dd19b1 100644 --- a/src/audio_core/CMakeLists.txt +++ b/src/audio_core/CMakeLists.txt @@ -51,9 +51,6 @@ if (NOT MSVC) target_compile_options(audio_core PRIVATE -Werror=conversion -Werror=ignored-qualifiers - -Werror=implicit-fallthrough - -Werror=reorder - -Werror=sign-compare -Werror=shadow -Werror=unused-parameter -Werror=unused-variable diff --git a/src/audio_core/audio_out.cpp b/src/audio_core/audio_out.cpp index 20a756dce..44a899d08 100644 --- a/src/audio_core/audio_out.cpp +++ b/src/audio_core/audio_out.cpp @@ -30,7 +30,8 @@ StreamPtr AudioOut::OpenStream(Core::Timing::CoreTiming& core_timing, u32 sample u32 num_channels, std::string&& name, Stream::ReleaseCallback&& release_callback) { if (!sink) { - sink = CreateSinkFromID(Settings::values.sink_id, Settings::values.audio_device_id); + sink = CreateSinkFromID(Settings::values.sink_id.GetValue(), + Settings::values.audio_device_id.GetValue()); } return std::make_shared<Stream>( diff --git a/src/audio_core/audio_renderer.cpp b/src/audio_core/audio_renderer.cpp index 80ffddb10..7dba739b4 100644 --- a/src/audio_core/audio_renderer.cpp +++ b/src/audio_core/audio_renderer.cpp @@ -12,6 +12,7 @@ #include "audio_core/voice_context.h" #include "common/logging/log.h" #include "common/settings.h" +#include "core/core_timing.h" #include "core/memory.h" namespace { @@ -28,10 +29,9 @@ namespace { (static_cast<float>(r_channel) * r_mix_amount))); } -[[nodiscard]] static constexpr std::tuple<s16, s16> Mix6To2(s16 fl_channel, s16 fr_channel, - s16 fc_channel, - [[maybe_unused]] s16 lf_channel, - s16 bl_channel, s16 br_channel) { +[[maybe_unused, nodiscard]] static constexpr std::tuple<s16, s16> Mix6To2( + s16 fl_channel, s16 fr_channel, s16 fc_channel, [[maybe_unused]] s16 lf_channel, s16 bl_channel, + s16 br_channel) { // Front channels are mixed 36.94%, Center channels are mixed to be 26.12% & the back channels // are mixed to be 36.94% @@ -56,11 +56,11 @@ namespace { const std::array<float_le, 4>& coeff) { const auto left = static_cast<float>(fl_channel) * coeff[0] + static_cast<float>(fc_channel) * coeff[1] + - static_cast<float>(lf_channel) * coeff[2] + static_cast<float>(bl_channel) * coeff[0]; + static_cast<float>(lf_channel) * coeff[2] + static_cast<float>(bl_channel) * coeff[3]; const auto right = static_cast<float>(fr_channel) * coeff[0] + static_cast<float>(fc_channel) * coeff[1] + - static_cast<float>(lf_channel) * coeff[2] + static_cast<float>(br_channel) * coeff[0]; + static_cast<float>(lf_channel) * coeff[2] + static_cast<float>(br_channel) * coeff[3]; return {ClampToS16(static_cast<s32>(left)), ClampToS16(static_cast<s32>(right))}; } @@ -68,7 +68,9 @@ namespace { } // namespace namespace AudioCore { -AudioRenderer::AudioRenderer(Core::Timing::CoreTiming& core_timing, Core::Memory::Memory& memory_, +constexpr s32 NUM_BUFFERS = 2; + +AudioRenderer::AudioRenderer(Core::Timing::CoreTiming& core_timing_, Core::Memory::Memory& memory_, AudioCommon::AudioRendererParameter params, Stream::ReleaseCallback&& release_callback, std::size_t instance_number) @@ -77,7 +79,8 @@ AudioRenderer::AudioRenderer(Core::Timing::CoreTiming& core_timing, Core::Memory sink_context(params.sink_count), splitter_context(), voices(params.voice_count), memory{memory_}, command_generator(worker_params, voice_context, mix_context, splitter_context, effect_context, - memory) { + memory), + core_timing{core_timing_} { behavior_info.SetUserRevision(params.revision); splitter_context.Initialize(behavior_info, params.splitter_count, params.num_splitter_send_channels); @@ -86,16 +89,27 @@ AudioRenderer::AudioRenderer(Core::Timing::CoreTiming& core_timing, Core::Memory stream = audio_out->OpenStream( core_timing, params.sample_rate, AudioCommon::STREAM_NUM_CHANNELS, fmt::format("AudioRenderer-Instance{}", instance_number), std::move(release_callback)); - audio_out->StartStream(stream); - - QueueMixedBuffer(0); - QueueMixedBuffer(1); - QueueMixedBuffer(2); - QueueMixedBuffer(3); + process_event = Core::Timing::CreateEvent( + fmt::format("AudioRenderer-Instance{}-Process", instance_number), + [this](std::uintptr_t, std::chrono::nanoseconds) { ReleaseAndQueueBuffers(); }); + for (s32 i = 0; i < NUM_BUFFERS; ++i) { + QueueMixedBuffer(i); + } } AudioRenderer::~AudioRenderer() = default; +ResultCode AudioRenderer::Start() { + audio_out->StartStream(stream); + ReleaseAndQueueBuffers(); + return ResultSuccess; +} + +ResultCode AudioRenderer::Stop() { + audio_out->StopStream(stream); + return ResultSuccess; +} + u32 AudioRenderer::GetSampleRate() const { return worker_params.sample_rate; } @@ -114,7 +128,7 @@ Stream::State AudioRenderer::GetStreamState() const { ResultCode AudioRenderer::UpdateAudioRenderer(const std::vector<u8>& input_params, std::vector<u8>& output_params) { - + std::scoped_lock lock{mutex}; InfoUpdater info_updater{input_params, output_params, behavior_info}; if (!info_updater.UpdateBehaviorInfo(behavior_info)) { @@ -194,9 +208,6 @@ ResultCode AudioRenderer::UpdateAudioRenderer(const std::vector<u8>& input_param LOG_ERROR(Audio, "Audio buffers were not consumed!"); return AudioCommon::Audren::ERR_INVALID_PARAMETERS; } - - ReleaseAndQueueBuffers(); - return ResultSuccess; } @@ -220,10 +231,8 @@ void AudioRenderer::QueueMixedBuffer(Buffer::Tag tag) { command_generator.PostCommand(); // Base sample size std::size_t BUFFER_SIZE{worker_params.sample_count}; - // Samples - std::vector<s16> buffer(BUFFER_SIZE * stream->GetNumChannels()); - // Make sure to clear our samples - std::memset(buffer.data(), 0, buffer.size() * sizeof(s16)); + // Samples, making sure to clear + std::vector<s16> buffer(BUFFER_SIZE * stream->GetNumChannels(), 0); if (sink_context.InUse()) { const auto stream_channel_count = stream->GetNumChannels(); @@ -231,7 +240,7 @@ void AudioRenderer::QueueMixedBuffer(Buffer::Tag tag) { const auto channel_count = buffer_offsets.size(); const auto& final_mix = mix_context.GetFinalMixInfo(); const auto& in_params = final_mix.GetInParams(); - std::vector<s32*> mix_buffers(channel_count); + std::vector<std::span<s32>> mix_buffers(channel_count); for (std::size_t i = 0; i < channel_count; i++) { mix_buffers[i] = command_generator.GetMixBuffer(in_params.buffer_offset + buffer_offsets[i]); @@ -284,18 +293,11 @@ void AudioRenderer::QueueMixedBuffer(Buffer::Tag tag) { buffer[i * stream_channel_count + 0] = Mix2To1(fl_sample, fr_sample); } else if (stream_channel_count == 2) { // Mix all channels into 2 channels - if (sink_context.HasDownMixingCoefficients()) { - const auto [left, right] = Mix6To2WithCoefficients( - fl_sample, fr_sample, fc_sample, lf_sample, bl_sample, br_sample, - sink_context.GetDownmixCoefficients()); - buffer[i * stream_channel_count + 0] = left; - buffer[i * stream_channel_count + 1] = right; - } else { - const auto [left, right] = Mix6To2(fl_sample, fr_sample, fc_sample, - lf_sample, bl_sample, br_sample); - buffer[i * stream_channel_count + 0] = left; - buffer[i * stream_channel_count + 1] = right; - } + const auto [left, right] = Mix6To2WithCoefficients( + fl_sample, fr_sample, fc_sample, lf_sample, bl_sample, br_sample, + sink_context.GetDownmixCoefficients()); + buffer[i * stream_channel_count + 0] = left; + buffer[i * stream_channel_count + 1] = right; } else if (stream_channel_count == 6) { // Pass through buffer[i * stream_channel_count + 0] = fl_sample; @@ -315,10 +317,24 @@ void AudioRenderer::QueueMixedBuffer(Buffer::Tag tag) { } void AudioRenderer::ReleaseAndQueueBuffers() { - const auto released_buffers{audio_out->GetTagsAndReleaseBuffers(stream)}; - for (const auto& tag : released_buffers) { - QueueMixedBuffer(tag); + if (!stream->IsPlaying()) { + return; } + + { + std::scoped_lock lock{mutex}; + const auto released_buffers{audio_out->GetTagsAndReleaseBuffers(stream)}; + for (const auto& tag : released_buffers) { + QueueMixedBuffer(tag); + } + } + + const f32 sample_rate = static_cast<f32>(GetSampleRate()); + const f32 sample_count = static_cast<f32>(GetSampleCount()); + const f32 consume_rate = sample_rate / (sample_count * (sample_count / 240)); + const s32 ms = (1000 / static_cast<s32>(consume_rate)) - 1; + const std::chrono::milliseconds next_event_time(std::max(ms / NUM_BUFFERS, 1)); + core_timing.ScheduleEvent(next_event_time, process_event, {}); } } // 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 @@ #include <array> #include <memory> +#include <mutex> #include <vector> #include "audio_core/behavior_info.h" @@ -45,6 +46,8 @@ public: [[nodiscard]] ResultCode UpdateAudioRenderer(const std::vector<u8>& input_params, std::vector<u8>& output_params); + [[nodiscard]] ResultCode Start(); + [[nodiscard]] ResultCode Stop(); void QueueMixedBuffer(Buffer::Tag tag); void ReleaseAndQueueBuffers(); [[nodiscard]] u32 GetSampleRate() const; @@ -68,6 +71,9 @@ private: Core::Memory::Memory& memory; CommandGenerator command_generator; std::size_t elapsed_frame_count{}; + Core::Timing::CoreTiming& core_timing; + std::shared_ptr<Core::Timing::EventType> process_event; + std::mutex mutex; }; } // namespace AudioCore diff --git a/src/audio_core/command_generator.cpp b/src/audio_core/command_generator.cpp index 437cc5ccd..45b2eef52 100644 --- a/src/audio_core/command_generator.cpp +++ b/src/audio_core/command_generator.cpp @@ -31,7 +31,7 @@ constexpr std::array<f32, AudioCommon::I3DL2REVERB_TAPS> EARLY_GAIN{ 0.72867f, 0.69794f, 0.5464f, 0.24563f, 0.45214f, 0.44042f}; template <std::size_t N> -void ApplyMix(s32* output, const s32* input, s32 gain, s32 sample_count) { +void ApplyMix(std::span<s32> output, std::span<const s32> input, s32 gain, s32 sample_count) { for (std::size_t i = 0; i < static_cast<std::size_t>(sample_count); i += N) { for (std::size_t j = 0; j < N; j++) { output[i + j] += @@ -40,7 +40,17 @@ void ApplyMix(s32* output, const s32* input, s32 gain, s32 sample_count) { } } -s32 ApplyMixRamp(s32* output, const s32* input, float gain, float delta, s32 sample_count) { +s32 ApplyMixRamp(std::span<s32> output, std::span<const s32> input, float gain, float delta, + s32 sample_count) { + // XC2 passes in NaN mix volumes, causing further issues as we handle everything as s32 rather + // than float, so the NaN propogation is lost. As the samples get further modified for + // volume etc, they can get out of NaN range, so a later heuristic for catching this is + // more difficult. Handle it here by setting these samples to silence. + if (std::isnan(gain)) { + gain = 0.0f; + delta = 0.0f; + } + s32 x = 0; for (s32 i = 0; i < sample_count; i++) { x = static_cast<s32>(static_cast<float>(input[i]) * gain); @@ -50,20 +60,22 @@ s32 ApplyMixRamp(s32* output, const s32* input, float gain, float delta, s32 sam return x; } -void ApplyGain(s32* output, const s32* input, s32 gain, s32 delta, s32 sample_count) { +void ApplyGain(std::span<s32> output, std::span<const s32> input, s32 gain, s32 delta, + s32 sample_count) { for (s32 i = 0; i < sample_count; i++) { output[i] = static_cast<s32>((static_cast<s64>(input[i]) * gain + 0x4000) >> 15); gain += delta; } } -void ApplyGainWithoutDelta(s32* output, const s32* input, s32 gain, s32 sample_count) { +void ApplyGainWithoutDelta(std::span<s32> output, std::span<const s32> input, s32 gain, + s32 sample_count) { for (s32 i = 0; i < sample_count; i++) { output[i] = static_cast<s32>((static_cast<s64>(input[i]) * gain + 0x4000) >> 15); } } -s32 ApplyMixDepop(s32* output, s32 first_sample, s32 delta, s32 sample_count) { +s32 ApplyMixDepop(std::span<s32> output, s32 first_sample, s32 delta, s32 sample_count) { const bool positive = first_sample > 0; auto final_sample = std::abs(first_sample); for (s32 i = 0; i < sample_count; i++) { @@ -128,10 +140,10 @@ constexpr std::array<std::size_t, 20> REVERB_TAP_INDEX_6CH{4, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 3, 3, 3}; template <std::size_t CHANNEL_COUNT> -void ApplyReverbGeneric(I3dl2ReverbState& state, - const std::array<const s32*, AudioCommon::MAX_CHANNEL_COUNT>& input, - const std::array<s32*, AudioCommon::MAX_CHANNEL_COUNT>& output, - s32 sample_count) { +void ApplyReverbGeneric( + I3dl2ReverbState& state, + const std::array<std::span<const s32>, AudioCommon::MAX_CHANNEL_COUNT>& input, + const std::array<std::span<s32>, AudioCommon::MAX_CHANNEL_COUNT>& output, s32 sample_count) { auto GetTapLookup = []() { if constexpr (CHANNEL_COUNT == 1) { @@ -400,7 +412,10 @@ void CommandGenerator::GenerateDataSourceCommand(ServerVoiceInfo& voice_info, Vo } } else { switch (in_params.sample_format) { + case SampleFormat::Pcm8: case SampleFormat::Pcm16: + case SampleFormat::Pcm32: + case SampleFormat::PcmFloat: DecodeFromWaveBuffers(voice_info, GetChannelMixBuffer(channel), dsp_state, channel, worker_params.sample_rate, worker_params.sample_count, in_params.node_id); @@ -454,8 +469,8 @@ void CommandGenerator::GenerateBiquadFilterCommand([[maybe_unused]] s32 mix_buff "input_mix_buffer={}, output_mix_buffer={}", node_id, input_offset, output_offset); } - const auto* input = GetMixBuffer(input_offset); - auto* output = GetMixBuffer(output_offset); + std::span<const s32> input = GetMixBuffer(input_offset); + std::span<s32> output = GetMixBuffer(output_offset); // Biquad filter parameters const auto [n0, n1, n2] = params.numerator; @@ -548,8 +563,8 @@ void CommandGenerator::GenerateI3dl2ReverbEffectCommand(s32 mix_buffer_offset, E return; } - std::array<const s32*, AudioCommon::MAX_CHANNEL_COUNT> input{}; - std::array<s32*, AudioCommon::MAX_CHANNEL_COUNT> output{}; + std::array<std::span<const s32>, AudioCommon::MAX_CHANNEL_COUNT> input{}; + std::array<std::span<s32>, AudioCommon::MAX_CHANNEL_COUNT> output{}; const auto status = params.status; for (s32 i = 0; i < channel_count; i++) { @@ -584,7 +599,8 @@ void CommandGenerator::GenerateI3dl2ReverbEffectCommand(s32 mix_buffer_offset, E for (s32 i = 0; i < channel_count; i++) { // Only copy if the buffer input and output do not match! if ((mix_buffer_offset + params.input[i]) != (mix_buffer_offset + params.output[i])) { - std::memcpy(output[i], input[i], worker_params.sample_count * sizeof(s32)); + std::memcpy(output[i].data(), input[i].data(), + worker_params.sample_count * sizeof(s32)); } } } @@ -600,8 +616,8 @@ void CommandGenerator::GenerateBiquadFilterEffectCommand(s32 mix_buffer_offset, for (s32 i = 0; i < channel_count; i++) { // TODO(ogniK): Actually implement biquad filter if (params.input[i] != params.output[i]) { - const auto* input = GetMixBuffer(mix_buffer_offset + params.input[i]); - auto* output = GetMixBuffer(mix_buffer_offset + params.output[i]); + std::span<const s32> input = GetMixBuffer(mix_buffer_offset + params.input[i]); + std::span<s32> output = GetMixBuffer(mix_buffer_offset + params.output[i]); ApplyMix<1>(output, input, 32768, worker_params.sample_count); } } @@ -640,14 +656,15 @@ void CommandGenerator::GenerateAuxCommand(s32 mix_buffer_offset, EffectBase* inf if (samples_read != static_cast<int>(worker_params.sample_count) && samples_read <= params.sample_count) { - std::memset(GetMixBuffer(output_index), 0, params.sample_count - samples_read); + std::memset(GetMixBuffer(output_index).data(), 0, + params.sample_count - samples_read); } } else { AuxInfoDSP empty{}; memory.WriteBlock(aux->GetSendInfo(), &empty, sizeof(AuxInfoDSP)); memory.WriteBlock(aux->GetRecvInfo(), &empty, sizeof(AuxInfoDSP)); if (output_index != input_index) { - std::memcpy(GetMixBuffer(output_index), GetMixBuffer(input_index), + std::memcpy(GetMixBuffer(output_index).data(), GetMixBuffer(input_index).data(), worker_params.sample_count * sizeof(s32)); } } @@ -665,7 +682,7 @@ ServerSplitterDestinationData* CommandGenerator::GetDestinationData(s32 splitter } s32 CommandGenerator::WriteAuxBuffer(AuxInfoDSP& dsp_info, VAddr send_buffer, u32 max_samples, - const s32* data, u32 sample_count, u32 write_offset, + std::span<const s32> data, u32 sample_count, u32 write_offset, u32 write_count) { if (max_samples == 0) { return 0; @@ -675,14 +692,14 @@ s32 CommandGenerator::WriteAuxBuffer(AuxInfoDSP& dsp_info, VAddr send_buffer, u3 return 0; } - std::size_t data_offset{}; + s32 data_offset{}; u32 remaining = sample_count; while (remaining > 0) { // Get position in buffer const auto base = send_buffer + (offset * sizeof(u32)); const auto samples_to_grab = std::min(max_samples - offset, remaining); // Write to output - memory.WriteBlock(base, (data + data_offset), samples_to_grab * sizeof(u32)); + memory.WriteBlock(base, (data.data() + data_offset), samples_to_grab * sizeof(u32)); offset = (offset + samples_to_grab) % max_samples; remaining -= samples_to_grab; data_offset += samples_to_grab; @@ -695,7 +712,7 @@ s32 CommandGenerator::WriteAuxBuffer(AuxInfoDSP& dsp_info, VAddr send_buffer, u3 } s32 CommandGenerator::ReadAuxBuffer(AuxInfoDSP& recv_info, VAddr recv_buffer, u32 max_samples, - s32* out_data, u32 sample_count, u32 read_offset, + std::span<s32> out_data, u32 sample_count, u32 read_offset, u32 read_count) { if (max_samples == 0) { return 0; @@ -707,15 +724,16 @@ s32 CommandGenerator::ReadAuxBuffer(AuxInfoDSP& recv_info, VAddr recv_buffer, u3 } u32 remaining = sample_count; + s32 data_offset{}; while (remaining > 0) { const auto base = recv_buffer + (offset * sizeof(u32)); const auto samples_to_grab = std::min(max_samples - offset, remaining); std::vector<s32> buffer(samples_to_grab); memory.ReadBlock(base, buffer.data(), buffer.size() * sizeof(u32)); - std::memcpy(out_data, buffer.data(), buffer.size() * sizeof(u32)); - out_data += samples_to_grab; + std::memcpy(out_data.data() + data_offset, buffer.data(), buffer.size() * sizeof(u32)); offset = (offset + samples_to_grab) % max_samples; remaining -= samples_to_grab; + data_offset += samples_to_grab; } if (read_count != 0) { @@ -795,7 +813,7 @@ void CommandGenerator::UpdateI3dl2Reverb(I3dl2ReverbParams& info, I3dl2ReverbSta state.lowpass_1 = 0.0f; } else { const auto a = 1.0f - hf_gain; - const auto b = 2.0f * (1.0f - hf_gain * CosD(256.0f * info.hf_reference / + const auto b = 2.0f * (2.0f - hf_gain * CosD(256.0f * info.hf_reference / static_cast<f32>(info.sample_rate))); const auto c = std::sqrt(b * b - 4.0f * a * a); @@ -843,7 +861,7 @@ void CommandGenerator::UpdateI3dl2Reverb(I3dl2ReverbParams& info, I3dl2ReverbSta } const auto max_early_delay = state.early_delay_line.GetMaxDelay(); - const auto reflection_time = 1000.0f * (0.0098f * info.reverb_delay + 0.02f); + const auto reflection_time = 1000.0f * (0.9998f * info.reverb_delay + 0.02f); for (std::size_t tap = 0; tap < AudioCommon::I3DL2REVERB_TAPS; tap++) { const auto length = AudioCommon::CalculateDelaySamples( sample_rate, 1000.0f * info.reflection_delay + reflection_time * EARLY_TAP_TIMES[tap]); @@ -962,8 +980,8 @@ void CommandGenerator::GenerateMixCommand(std::size_t output_offset, std::size_t node_id, input_offset, output_offset, volume); } - auto* output = GetMixBuffer(output_offset); - const auto* input = GetMixBuffer(input_offset); + std::span<s32> output = GetMixBuffer(output_offset); + std::span<const s32> input = GetMixBuffer(input_offset); const s32 gain = static_cast<s32>(volume * 32768.0f); // Mix with loop unrolling @@ -1003,8 +1021,10 @@ void CommandGenerator::GenerateFinalMixCommand() { } } -s32 CommandGenerator::DecodePcm16(ServerVoiceInfo& voice_info, VoiceState& dsp_state, - s32 sample_count, s32 channel, std::size_t mix_offset) { +template <typename T> +s32 CommandGenerator::DecodePcm(ServerVoiceInfo& voice_info, VoiceState& dsp_state, + s32 sample_start_offset, s32 sample_end_offset, s32 sample_count, + s32 channel, std::size_t mix_offset) { const auto& in_params = voice_info.GetInParams(); const auto& wave_buffer = in_params.wave_buffer[dsp_state.wave_buffer_index]; if (wave_buffer.buffer_address == 0) { @@ -1013,39 +1033,50 @@ s32 CommandGenerator::DecodePcm16(ServerVoiceInfo& voice_info, VoiceState& dsp_s if (wave_buffer.buffer_size == 0) { return 0; } - if (wave_buffer.end_sample_offset < wave_buffer.start_sample_offset) { + if (sample_end_offset < sample_start_offset) { return 0; } - const auto samples_remaining = - (wave_buffer.end_sample_offset - wave_buffer.start_sample_offset) - dsp_state.offset; + const auto samples_remaining = (sample_end_offset - sample_start_offset) - dsp_state.offset; const auto start_offset = - ((wave_buffer.start_sample_offset + dsp_state.offset) * in_params.channel_count) * - sizeof(s16); + ((dsp_state.offset + sample_start_offset) * in_params.channel_count) * sizeof(T); const auto buffer_pos = wave_buffer.buffer_address + start_offset; const auto samples_processed = std::min(sample_count, samples_remaining); - if (in_params.channel_count == 1) { - std::vector<s16> buffer(samples_processed); - memory.ReadBlock(buffer_pos, buffer.data(), buffer.size() * sizeof(s16)); - for (std::size_t i = 0; i < buffer.size(); i++) { - sample_buffer[mix_offset + i] = buffer[i]; - } - } else { - const auto channel_count = in_params.channel_count; - std::vector<s16> buffer(samples_processed * channel_count); - memory.ReadBlock(buffer_pos, buffer.data(), buffer.size() * sizeof(s16)); + const auto channel_count = in_params.channel_count; + std::vector<T> buffer(samples_processed * channel_count); + memory.ReadBlock(buffer_pos, buffer.data(), buffer.size() * sizeof(T)); + if constexpr (std::is_floating_point_v<T>) { + for (std::size_t i = 0; i < static_cast<std::size_t>(samples_processed); i++) { + sample_buffer[mix_offset + i] = static_cast<s32>(buffer[i * channel_count + channel] * + std::numeric_limits<s16>::max()); + } + } else if constexpr (sizeof(T) == 1) { + for (std::size_t i = 0; i < static_cast<std::size_t>(samples_processed); i++) { + sample_buffer[mix_offset + i] = + static_cast<s32>(static_cast<f32>(buffer[i * channel_count + channel] / + std::numeric_limits<s8>::max()) * + std::numeric_limits<s16>::max()); + } + } else if constexpr (sizeof(T) == 2) { for (std::size_t i = 0; i < static_cast<std::size_t>(samples_processed); i++) { sample_buffer[mix_offset + i] = buffer[i * channel_count + channel]; } + } else { + for (std::size_t i = 0; i < static_cast<std::size_t>(samples_processed); i++) { + sample_buffer[mix_offset + i] = + static_cast<s32>(static_cast<f32>(buffer[i * channel_count + channel] / + std::numeric_limits<s32>::max()) * + std::numeric_limits<s16>::max()); + } } return samples_processed; } s32 CommandGenerator::DecodeAdpcm(ServerVoiceInfo& voice_info, VoiceState& dsp_state, - s32 sample_count, [[maybe_unused]] s32 channel, - std::size_t mix_offset) { + s32 sample_start_offset, s32 sample_end_offset, s32 sample_count, + [[maybe_unused]] s32 channel, std::size_t mix_offset) { const auto& in_params = voice_info.GetInParams(); const auto& wave_buffer = in_params.wave_buffer[dsp_state.wave_buffer_index]; if (wave_buffer.buffer_address == 0) { @@ -1054,7 +1085,7 @@ s32 CommandGenerator::DecodeAdpcm(ServerVoiceInfo& voice_info, VoiceState& dsp_s if (wave_buffer.buffer_size == 0) { return 0; } - if (wave_buffer.end_sample_offset < wave_buffer.start_sample_offset) { + if (sample_end_offset < sample_start_offset) { return 0; } @@ -1079,10 +1110,9 @@ s32 CommandGenerator::DecodeAdpcm(ServerVoiceInfo& voice_info, VoiceState& dsp_s s32 coef1 = coeffs[idx * 2]; s32 coef2 = coeffs[idx * 2 + 1]; - const auto samples_remaining = - (wave_buffer.end_sample_offset - wave_buffer.start_sample_offset) - dsp_state.offset; + const auto samples_remaining = (sample_end_offset - sample_start_offset) - dsp_state.offset; const auto samples_processed = std::min(sample_count, samples_remaining); - const auto sample_pos = wave_buffer.start_sample_offset + dsp_state.offset; + const auto sample_pos = dsp_state.offset + sample_start_offset; const auto samples_remaining_in_frame = sample_pos % SAMPLES_PER_FRAME; auto position_in_frame = ((sample_pos / SAMPLES_PER_FRAME) * NIBBLES_PER_SAMPLE) + @@ -1157,12 +1187,14 @@ s32 CommandGenerator::DecodeAdpcm(ServerVoiceInfo& voice_info, VoiceState& dsp_s return samples_processed; } -s32* CommandGenerator::GetMixBuffer(std::size_t index) { - return mix_buffer.data() + (index * worker_params.sample_count); +std::span<s32> CommandGenerator::GetMixBuffer(std::size_t index) { + return std::span<s32>(mix_buffer.data() + (index * worker_params.sample_count), + worker_params.sample_count); } -const s32* CommandGenerator::GetMixBuffer(std::size_t index) const { - return mix_buffer.data() + (index * worker_params.sample_count); +std::span<const s32> CommandGenerator::GetMixBuffer(std::size_t index) const { + return std::span<const s32>(mix_buffer.data() + (index * worker_params.sample_count), + worker_params.sample_count); } std::size_t CommandGenerator::GetMixChannelBufferOffset(s32 channel) const { @@ -1173,15 +1205,15 @@ std::size_t CommandGenerator::GetTotalMixBufferCount() const { return worker_params.mix_buffer_count + AudioCommon::MAX_CHANNEL_COUNT; } -s32* CommandGenerator::GetChannelMixBuffer(s32 channel) { +std::span<s32> CommandGenerator::GetChannelMixBuffer(s32 channel) { return GetMixBuffer(worker_params.mix_buffer_count + channel); } -const s32* CommandGenerator::GetChannelMixBuffer(s32 channel) const { +std::span<const s32> CommandGenerator::GetChannelMixBuffer(s32 channel) const { return GetMixBuffer(worker_params.mix_buffer_count + channel); } -void CommandGenerator::DecodeFromWaveBuffers(ServerVoiceInfo& voice_info, s32* output, +void CommandGenerator::DecodeFromWaveBuffers(ServerVoiceInfo& voice_info, std::span<s32> output, VoiceState& dsp_state, s32 channel, s32 target_sample_rate, s32 sample_count, s32 node_id) { @@ -1193,7 +1225,7 @@ void CommandGenerator::DecodeFromWaveBuffers(ServerVoiceInfo& voice_info, s32* o node_id, channel, in_params.sample_format, sample_count, in_params.sample_rate, in_params.mix_id, in_params.splitter_info_id); } - ASSERT_OR_EXECUTE(output != nullptr, { return; }); + ASSERT_OR_EXECUTE(output.data() != nullptr, { return; }); const auto resample_rate = static_cast<s32>( static_cast<float>(in_params.sample_rate) / static_cast<float>(target_sample_rate) * @@ -1210,9 +1242,9 @@ void CommandGenerator::DecodeFromWaveBuffers(ServerVoiceInfo& voice_info, s32* o } std::size_t temp_mix_offset{}; - bool is_buffer_completed{false}; + s32 samples_output{}; auto samples_remaining = sample_count; - while (samples_remaining > 0 && !is_buffer_completed) { + while (samples_remaining > 0) { const auto samples_to_output = std::min(samples_remaining, min_required_samples); const auto samples_to_read = (samples_to_output * resample_rate + dsp_state.fraction) >> 15; @@ -1229,24 +1261,53 @@ void CommandGenerator::DecodeFromWaveBuffers(ServerVoiceInfo& voice_info, s32* o const auto& wave_buffer = in_params.wave_buffer[dsp_state.wave_buffer_index]; // No more data can be read if (!dsp_state.is_wave_buffer_valid[dsp_state.wave_buffer_index]) { - is_buffer_completed = true; break; } if (in_params.sample_format == SampleFormat::Adpcm && dsp_state.offset == 0 && wave_buffer.context_address != 0 && wave_buffer.context_size != 0) { - // TODO(ogniK): ADPCM loop context + memory.ReadBlock(wave_buffer.context_address, &dsp_state.context, + sizeof(ADPCMContext)); + } + + s32 samples_offset_start; + s32 samples_offset_end; + if (dsp_state.loop_count > 0 && wave_buffer.loop_start_sample != 0 && + wave_buffer.loop_end_sample != 0 && + wave_buffer.loop_start_sample <= wave_buffer.loop_end_sample) { + samples_offset_start = wave_buffer.loop_start_sample; + samples_offset_end = wave_buffer.loop_end_sample; + } else { + samples_offset_start = wave_buffer.start_sample_offset; + samples_offset_end = wave_buffer.end_sample_offset; } s32 samples_decoded{0}; switch (in_params.sample_format) { + case SampleFormat::Pcm8: + samples_decoded = + DecodePcm<s8>(voice_info, dsp_state, samples_offset_start, samples_offset_end, + samples_to_read - samples_read, channel, temp_mix_offset); + break; case SampleFormat::Pcm16: - samples_decoded = DecodePcm16(voice_info, dsp_state, samples_to_read - samples_read, - channel, temp_mix_offset); + samples_decoded = + DecodePcm<s16>(voice_info, dsp_state, samples_offset_start, samples_offset_end, + samples_to_read - samples_read, channel, temp_mix_offset); + break; + case SampleFormat::Pcm32: + samples_decoded = + DecodePcm<s32>(voice_info, dsp_state, samples_offset_start, samples_offset_end, + samples_to_read - samples_read, channel, temp_mix_offset); + break; + case SampleFormat::PcmFloat: + samples_decoded = + DecodePcm<f32>(voice_info, dsp_state, samples_offset_start, samples_offset_end, + samples_to_read - samples_read, channel, temp_mix_offset); break; case SampleFormat::Adpcm: - samples_decoded = DecodeAdpcm(voice_info, dsp_state, samples_to_read - samples_read, - channel, temp_mix_offset); + samples_decoded = + DecodeAdpcm(voice_info, dsp_state, samples_offset_start, samples_offset_end, + samples_to_read - samples_read, channel, temp_mix_offset); break; default: UNREACHABLE_MSG("Unimplemented sample format={}", in_params.sample_format); @@ -1257,15 +1318,19 @@ void CommandGenerator::DecodeFromWaveBuffers(ServerVoiceInfo& voice_info, s32* o dsp_state.offset += samples_decoded; dsp_state.played_sample_count += samples_decoded; - if (dsp_state.offset >= - (wave_buffer.end_sample_offset - wave_buffer.start_sample_offset) || + if (dsp_state.offset >= (samples_offset_end - samples_offset_start) || samples_decoded == 0) { // Reset our sample offset dsp_state.offset = 0; if (wave_buffer.is_looping) { - if (samples_decoded == 0) { + dsp_state.loop_count++; + if (wave_buffer.loop_count > 0 && + (dsp_state.loop_count > wave_buffer.loop_count || samples_decoded == 0)) { // End of our buffer - is_buffer_completed = true; + voice_info.SetWaveBufferCompleted(dsp_state, wave_buffer); + } + + if (samples_decoded == 0) { break; } @@ -1273,35 +1338,29 @@ void CommandGenerator::DecodeFromWaveBuffers(ServerVoiceInfo& voice_info, s32* o dsp_state.played_sample_count = 0; } } else { - // Update our wave buffer states - dsp_state.is_wave_buffer_valid[dsp_state.wave_buffer_index] = false; - dsp_state.wave_buffer_consumed++; - dsp_state.wave_buffer_index = - (dsp_state.wave_buffer_index + 1) % AudioCommon::MAX_WAVE_BUFFERS; - if (wave_buffer.end_of_stream) { - dsp_state.played_sample_count = 0; - } + voice_info.SetWaveBufferCompleted(dsp_state, wave_buffer); } } } if (in_params.behavior_flags.is_pitch_and_src_skipped.Value()) { // No need to resample - std::memcpy(output, sample_buffer.data(), samples_read * sizeof(s32)); + std::memcpy(output.data() + samples_output, sample_buffer.data(), + samples_read * sizeof(s32)); } else { std::fill(sample_buffer.begin() + temp_mix_offset, sample_buffer.begin() + temp_mix_offset + (samples_to_read - samples_read), 0); - AudioCore::Resample(output, sample_buffer.data(), resample_rate, dsp_state.fraction, - samples_to_output); + AudioCore::Resample(output.data() + samples_output, sample_buffer.data(), resample_rate, + dsp_state.fraction, samples_to_output); // Resample for (std::size_t i = 0; i < AudioCommon::MAX_SAMPLE_HISTORY; i++) { dsp_state.sample_history[i] = sample_buffer[samples_to_read + i]; } } - output += samples_to_output; samples_remaining -= samples_to_output; + samples_output += samples_to_output; } } diff --git a/src/audio_core/command_generator.h b/src/audio_core/command_generator.h index 2ebb755b0..59a33ba76 100644 --- a/src/audio_core/command_generator.h +++ b/src/audio_core/command_generator.h @@ -5,6 +5,7 @@ #pragma once #include <array> +#include <span> #include "audio_core/common.h" #include "audio_core/voice_context.h" #include "common/common_types.h" @@ -41,10 +42,10 @@ public: void PreCommand(); void PostCommand(); - [[nodiscard]] s32* GetChannelMixBuffer(s32 channel); - [[nodiscard]] const s32* GetChannelMixBuffer(s32 channel) const; - [[nodiscard]] s32* GetMixBuffer(std::size_t index); - [[nodiscard]] const s32* GetMixBuffer(std::size_t index) const; + [[nodiscard]] std::span<s32> GetChannelMixBuffer(s32 channel); + [[nodiscard]] std::span<const s32> GetChannelMixBuffer(s32 channel) const; + [[nodiscard]] std::span<s32> GetMixBuffer(std::size_t index); + [[nodiscard]] std::span<const s32> GetMixBuffer(std::size_t index) const; [[nodiscard]] std::size_t GetMixChannelBufferOffset(s32 channel) const; [[nodiscard]] std::size_t GetTotalMixBufferCount() const; @@ -77,21 +78,24 @@ private: void GenerateAuxCommand(s32 mix_buffer_offset, EffectBase* info, bool enabled); [[nodiscard]] ServerSplitterDestinationData* GetDestinationData(s32 splitter_id, s32 index); - s32 WriteAuxBuffer(AuxInfoDSP& dsp_info, VAddr send_buffer, u32 max_samples, const s32* data, - u32 sample_count, u32 write_offset, u32 write_count); - s32 ReadAuxBuffer(AuxInfoDSP& recv_info, VAddr recv_buffer, u32 max_samples, s32* out_data, - u32 sample_count, u32 read_offset, u32 read_count); + s32 WriteAuxBuffer(AuxInfoDSP& dsp_info, VAddr send_buffer, u32 max_samples, + std::span<const s32> data, u32 sample_count, u32 write_offset, + u32 write_count); + s32 ReadAuxBuffer(AuxInfoDSP& recv_info, VAddr recv_buffer, u32 max_samples, + std::span<s32> out_data, u32 sample_count, u32 read_offset, u32 read_count); void InitializeI3dl2Reverb(I3dl2ReverbParams& info, I3dl2ReverbState& state, std::vector<u8>& work_buffer); void UpdateI3dl2Reverb(I3dl2ReverbParams& info, I3dl2ReverbState& state, bool should_clear); // DSP Code - s32 DecodePcm16(ServerVoiceInfo& voice_info, VoiceState& dsp_state, s32 sample_count, - s32 channel, std::size_t mix_offset); - s32 DecodeAdpcm(ServerVoiceInfo& voice_info, VoiceState& dsp_state, s32 sample_count, - s32 channel, std::size_t mix_offset); - void DecodeFromWaveBuffers(ServerVoiceInfo& voice_info, s32* output, VoiceState& dsp_state, - s32 channel, s32 target_sample_rate, s32 sample_count, s32 node_id); + template <typename T> + s32 DecodePcm(ServerVoiceInfo& voice_info, VoiceState& dsp_state, s32 sample_start_offset, + s32 sample_end_offset, s32 sample_count, s32 channel, std::size_t mix_offset); + s32 DecodeAdpcm(ServerVoiceInfo& voice_info, VoiceState& dsp_state, s32 sample_start_offset, + s32 sample_end_offset, s32 sample_count, s32 channel, std::size_t mix_offset); + void DecodeFromWaveBuffers(ServerVoiceInfo& voice_info, std::span<s32> output, + VoiceState& dsp_state, s32 channel, s32 target_sample_rate, + s32 sample_count, s32 node_id); AudioCommon::AudioRendererParameter& worker_params; VoiceContext& voice_context; diff --git a/src/audio_core/common.h b/src/audio_core/common.h index fe546c55d..1ab537588 100644 --- a/src/audio_core/common.h +++ b/src/audio_core/common.h @@ -15,7 +15,7 @@ constexpr ResultCode ERR_INVALID_PARAMETERS{ErrorModule::Audio, 41}; constexpr ResultCode ERR_SPLITTER_SORT_FAILED{ErrorModule::Audio, 43}; } // namespace Audren -constexpr u32_le CURRENT_PROCESS_REVISION = Common::MakeMagic('R', 'E', 'V', '8'); +constexpr u32_le CURRENT_PROCESS_REVISION = Common::MakeMagic('R', 'E', 'V', '9'); constexpr std::size_t MAX_MIX_BUFFERS = 24; constexpr std::size_t MAX_BIQUAD_FILTERS = 2; constexpr std::size_t MAX_CHANNEL_COUNT = 6; 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, if (voice_in_params.is_new) { // Default our values for our voice voice_info.Initialize(); - if (channel_count == 0 || channel_count > AudioCommon::MAX_CHANNEL_COUNT) { - continue; - } // Zero out our voice states for (std::size_t channel = 0; channel < channel_count; channel++) { diff --git a/src/audio_core/sink_context.cpp b/src/audio_core/sink_context.cpp index a69543696..cc55b290c 100644 --- a/src/audio_core/sink_context.cpp +++ b/src/audio_core/sink_context.cpp @@ -15,10 +15,17 @@ std::size_t SinkContext::GetCount() const { void SinkContext::UpdateMainSink(const SinkInfo::InParams& in) { ASSERT(in.type == SinkTypes::Device); - has_downmix_coefs = in.device.down_matrix_enabled; - if (has_downmix_coefs) { + if (in.device.down_matrix_enabled) { downmix_coefficients = in.device.down_matrix_coef; + } else { + downmix_coefficients = { + 1.0f, // front + 0.707f, // center + 0.0f, // lfe + 0.707f, // back + }; } + in_use = in.in_use; use_count = in.device.input_count; buffers = in.device.input; @@ -34,10 +41,6 @@ std::vector<u8> SinkContext::OutputBuffers() const { return buffer_ret; } -bool SinkContext::HasDownMixingCoefficients() const { - return has_downmix_coefs; -} - const DownmixCoefficients& SinkContext::GetDownmixCoefficients() const { return downmix_coefficients; } diff --git a/src/audio_core/sink_context.h b/src/audio_core/sink_context.h index 9e2b69785..254961fe2 100644 --- a/src/audio_core/sink_context.h +++ b/src/audio_core/sink_context.h @@ -84,7 +84,6 @@ public: [[nodiscard]] bool InUse() const; [[nodiscard]] std::vector<u8> OutputBuffers() const; - [[nodiscard]] bool HasDownMixingCoefficients() const; [[nodiscard]] const DownmixCoefficients& GetDownmixCoefficients() const; private: @@ -92,7 +91,6 @@ private: s32 use_count{}; std::array<u8, AudioCommon::MAX_CHANNEL_COUNT> buffers{}; std::size_t sink_count{}; - bool has_downmix_coefs{false}; DownmixCoefficients downmix_coefficients{}; }; } // namespace AudioCore 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() { in_params.last_volume = 0.0f; in_params.biquad_filter.fill({}); in_params.wave_buffer_count = 0; - in_params.wave_bufffer_head = 0; + in_params.wave_buffer_head = 0; in_params.mix_id = AudioCommon::NO_MIX; in_params.splitter_info_id = AudioCommon::NO_SPLITTER; in_params.additional_params_address = 0; @@ -75,7 +75,7 @@ void ServerVoiceInfo::Initialize() { out_params.played_sample_count = 0; out_params.wave_buffer_consumed = 0; in_params.voice_drop_flag = false; - in_params.buffer_mapped = false; + in_params.buffer_mapped = true; in_params.wave_buffer_flush_request_count = 0; in_params.was_biquad_filter_enabled.fill(false); @@ -126,7 +126,7 @@ void ServerVoiceInfo::UpdateParameters(const VoiceInfo::InParams& voice_in, in_params.volume = voice_in.volume; in_params.biquad_filter = voice_in.biquad_filter; in_params.wave_buffer_count = voice_in.wave_buffer_count; - in_params.wave_bufffer_head = voice_in.wave_buffer_head; + in_params.wave_buffer_head = voice_in.wave_buffer_head; if (behavior_info.IsFlushVoiceWaveBuffersSupported()) { const auto in_request_count = in_params.wave_buffer_flush_request_count; const auto voice_request_count = voice_in.wave_buffer_flush_request_count; @@ -185,14 +185,16 @@ void ServerVoiceInfo::UpdateWaveBuffers( wave_buffer.buffer_size = 0; wave_buffer.context_address = 0; wave_buffer.context_size = 0; + wave_buffer.loop_start_sample = 0; + wave_buffer.loop_end_sample = 0; wave_buffer.sent_to_dsp = true; } // Mark all our wave buffers as invalid for (std::size_t channel = 0; channel < static_cast<std::size_t>(in_params.channel_count); channel++) { - for (auto& is_valid : voice_states[channel]->is_wave_buffer_valid) { - is_valid = false; + for (std::size_t i = 0; i < AudioCommon::MAX_WAVE_BUFFERS; ++i) { + voice_states[channel]->is_wave_buffer_valid[i] = false; } } } @@ -211,7 +213,7 @@ void ServerVoiceInfo::UpdateWaveBuffer(ServerWaveBuffer& out_wavebuffer, const WaveBuffer& in_wave_buffer, SampleFormat sample_format, bool is_buffer_valid, [[maybe_unused]] BehaviorInfo& behavior_info) { - if (!is_buffer_valid && out_wavebuffer.sent_to_dsp) { + if (!is_buffer_valid && out_wavebuffer.sent_to_dsp && out_wavebuffer.buffer_address != 0) { out_wavebuffer.buffer_address = 0; out_wavebuffer.buffer_size = 0; } @@ -219,11 +221,40 @@ void ServerVoiceInfo::UpdateWaveBuffer(ServerWaveBuffer& out_wavebuffer, if (!in_wave_buffer.sent_to_server || !in_params.buffer_mapped) { // Validate sample offset sizings if (sample_format == SampleFormat::Pcm16) { - const auto buffer_size = in_wave_buffer.buffer_size; - if (in_wave_buffer.start_sample_offset < 0 || in_wave_buffer.end_sample_offset < 0 || - (buffer_size < (sizeof(s16) * in_wave_buffer.start_sample_offset)) || - (buffer_size < (sizeof(s16) * in_wave_buffer.end_sample_offset))) { + const s64 buffer_size = static_cast<s64>(in_wave_buffer.buffer_size); + const s64 start = sizeof(s16) * in_wave_buffer.start_sample_offset; + const s64 end = sizeof(s16) * in_wave_buffer.end_sample_offset; + if (0 > start || start > buffer_size || 0 > end || end > buffer_size) { // TODO(ogniK): Write error info + LOG_ERROR(Audio, + "PCM16 wavebuffer has an invalid size. Buffer has size 0x{:08X}, but " + "offsets were " + "{:08X} - 0x{:08X}", + buffer_size, sizeof(s16) * in_wave_buffer.start_sample_offset, + sizeof(s16) * in_wave_buffer.end_sample_offset); + return; + } + } else if (sample_format == SampleFormat::Adpcm) { + const s64 buffer_size = static_cast<s64>(in_wave_buffer.buffer_size); + const s64 start_frames = in_wave_buffer.start_sample_offset / 14; + const s64 start_extra = in_wave_buffer.start_sample_offset % 14 == 0 + ? 0 + : (in_wave_buffer.start_sample_offset % 14) / 2 + 1 + + (in_wave_buffer.start_sample_offset % 2); + const s64 start = start_frames * 8 + start_extra; + const s64 end_frames = in_wave_buffer.end_sample_offset / 14; + const s64 end_extra = in_wave_buffer.end_sample_offset % 14 == 0 + ? 0 + : (in_wave_buffer.end_sample_offset % 14) / 2 + 1 + + (in_wave_buffer.end_sample_offset % 2); + const s64 end = end_frames * 8 + end_extra; + if (in_wave_buffer.start_sample_offset < 0 || start > buffer_size || + in_wave_buffer.end_sample_offset < 0 || end > buffer_size) { + LOG_ERROR(Audio, + "ADPMC wavebuffer has an invalid size. Buffer has size 0x{:08X}, but " + "offsets were " + "{:08X} - 0x{:08X}", + in_wave_buffer.buffer_size, start, end); return; } } @@ -239,29 +270,34 @@ void ServerVoiceInfo::UpdateWaveBuffer(ServerWaveBuffer& out_wavebuffer, out_wavebuffer.buffer_size = in_wave_buffer.buffer_size; out_wavebuffer.context_address = in_wave_buffer.context_address; out_wavebuffer.context_size = in_wave_buffer.context_size; + out_wavebuffer.loop_start_sample = in_wave_buffer.loop_start_sample; + out_wavebuffer.loop_end_sample = in_wave_buffer.loop_end_sample; in_params.buffer_mapped = in_wave_buffer.buffer_address != 0 && in_wave_buffer.buffer_size != 0; // TODO(ogniK): Pool mapper attachment // TODO(ogniK): IsAdpcmLoopContextBugFixed + if (sample_format == SampleFormat::Adpcm && in_wave_buffer.context_address != 0 && + in_wave_buffer.context_size != 0 && behavior_info.IsAdpcmLoopContextBugFixed()) { + } else { + out_wavebuffer.context_address = 0; + out_wavebuffer.context_size = 0; + } } } void ServerVoiceInfo::WriteOutStatus( VoiceInfo::OutParams& voice_out, VoiceInfo::InParams& voice_in, std::array<VoiceState*, AudioCommon::MAX_CHANNEL_COUNT>& voice_states) { - if (voice_in.is_new) { + if (voice_in.is_new || in_params.is_new) { in_params.is_new = true; voice_out.wave_buffer_consumed = 0; voice_out.played_sample_count = 0; voice_out.voice_dropped = false; - } else if (!in_params.is_new) { - voice_out.wave_buffer_consumed = voice_states[0]->wave_buffer_consumed; - voice_out.played_sample_count = voice_states[0]->played_sample_count; - voice_out.voice_dropped = in_params.voice_drop_flag; } else { - voice_out.wave_buffer_consumed = 0; - voice_out.played_sample_count = 0; - voice_out.voice_dropped = false; + const auto& state = voice_states[0]; + voice_out.wave_buffer_consumed = state->wave_buffer_consumed; + voice_out.played_sample_count = state->played_sample_count; + voice_out.voice_dropped = state->voice_dropped; } } @@ -283,7 +319,8 @@ ServerVoiceInfo::OutParams& ServerVoiceInfo::GetOutParams() { bool ServerVoiceInfo::ShouldSkip() const { // TODO(ogniK): Handle unmapped wave buffers or parameters - return !in_params.in_use || (in_params.wave_buffer_count == 0) || in_params.voice_drop_flag; + return !in_params.in_use || in_params.wave_buffer_count == 0 || !in_params.buffer_mapped || + in_params.voice_drop_flag; } bool ServerVoiceInfo::UpdateForCommandGeneration(VoiceContext& voice_context) { @@ -381,7 +418,7 @@ bool ServerVoiceInfo::UpdateParametersForCommandGeneration( void ServerVoiceInfo::FlushWaveBuffers( u8 flush_count, std::array<VoiceState*, AudioCommon::MAX_CHANNEL_COUNT>& dsp_voice_states, s32 channel_count) { - auto wave_head = in_params.wave_bufffer_head; + auto wave_head = in_params.wave_buffer_head; for (u8 i = 0; i < flush_count; i++) { in_params.wave_buffer[wave_head].sent_to_dsp = true; @@ -401,6 +438,17 @@ bool ServerVoiceInfo::HasValidWaveBuffer(const VoiceState* state) const { return std::find(valid_wb.begin(), valid_wb.end(), true) != valid_wb.end(); } +void ServerVoiceInfo::SetWaveBufferCompleted(VoiceState& dsp_state, + const ServerWaveBuffer& wave_buffer) { + dsp_state.is_wave_buffer_valid[dsp_state.wave_buffer_index] = false; + dsp_state.wave_buffer_consumed++; + dsp_state.wave_buffer_index = (dsp_state.wave_buffer_index + 1) % AudioCommon::MAX_WAVE_BUFFERS; + dsp_state.loop_count = 0; + if (wave_buffer.end_of_stream) { + dsp_state.played_sample_count = 0; + } +} + VoiceContext::VoiceContext(std::size_t voice_count_) : voice_count{voice_count_} { for (std::size_t i = 0; i < voice_count; i++) { 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 { u8 is_looping{}; u8 end_of_stream{}; u8 sent_to_server{}; - INSERT_PADDING_BYTES(5); + INSERT_PADDING_BYTES(1); + s32 loop_count{}; u64 context_address{}; u64 context_size{}; - INSERT_PADDING_BYTES(8); + u32 loop_start_sample{}; + u32 loop_end_sample{}; }; static_assert(sizeof(WaveBuffer) == 0x38, "WaveBuffer is an invalid size"); @@ -76,6 +78,9 @@ struct ServerWaveBuffer { bool end_of_stream{}; VAddr context_address{}; std::size_t context_size{}; + s32 loop_count{}; + u32 loop_start_sample{}; + u32 loop_end_sample{}; bool sent_to_dsp{true}; }; @@ -108,6 +113,7 @@ struct VoiceState { u32 external_context_size; bool is_external_context_used; bool voice_dropped; + s32 loop_count; }; class VoiceChannelResource { @@ -206,7 +212,7 @@ public: float last_volume{}; std::array<BiquadFilterParameter, AudioCommon::MAX_BIQUAD_FILTERS> biquad_filter{}; s32 wave_buffer_count{}; - s16 wave_bufffer_head{}; + s16 wave_buffer_head{}; INSERT_PADDING_BYTES(2); BehaviorFlags behavior_flags{}; VAddr additional_params_address{}; @@ -252,6 +258,7 @@ public: void FlushWaveBuffers(u8 flush_count, std::array<VoiceState*, AudioCommon::MAX_CHANNEL_COUNT>& dsp_voice_states, s32 channel_count); + void SetWaveBufferCompleted(VoiceState& dsp_state, const ServerWaveBuffer& wave_buffer); private: std::vector<s16> stored_samples; diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index a6fa9a85d..e03fffd8d 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt @@ -180,7 +180,6 @@ add_library(common STATIC thread.cpp thread.h thread_queue_list.h - thread_worker.cpp thread_worker.h threadsafe_queue.h time_zone.cpp @@ -188,6 +187,7 @@ add_library(common STATIC tiny_mt.h tree.h uint128.h + unique_function.h uuid.cpp uuid.h vector_math.h diff --git a/src/common/fs/file.cpp b/src/common/fs/file.cpp index 077f34995..274f57659 100644 --- a/src/common/fs/file.cpp +++ b/src/common/fs/file.cpp @@ -306,9 +306,9 @@ bool IOFile::Flush() const { errno = 0; #ifdef _WIN32 - const auto flush_result = std::fflush(file) == 0 && _commit(fileno(file)) == 0; + const auto flush_result = std::fflush(file) == 0; #else - const auto flush_result = std::fflush(file) == 0 && fsync(fileno(file)) == 0; + const auto flush_result = std::fflush(file) == 0; #endif if (!flush_result) { @@ -320,6 +320,28 @@ bool IOFile::Flush() const { return flush_result; } +bool IOFile::Commit() const { + if (!IsOpen()) { + return false; + } + + errno = 0; + +#ifdef _WIN32 + const auto commit_result = std::fflush(file) == 0 && _commit(fileno(file)) == 0; +#else + const auto commit_result = std::fflush(file) == 0 && fsync(fileno(file)) == 0; +#endif + + if (!commit_result) { + const auto ec = std::error_code{errno, std::generic_category()}; + LOG_ERROR(Common_Filesystem, "Failed to commit the file at path={}, ec_message={}", + PathToUTF8String(file_path), ec.message()); + } + + return commit_result; +} + bool IOFile::SetSize(u64 size) const { if (!IsOpen()) { return false; @@ -347,6 +369,9 @@ u64 IOFile::GetSize() const { return 0; } + // Flush any unwritten buffered data into the file prior to retrieving the file size. + std::fflush(file); + std::error_code ec; const auto file_size = fs::file_size(file_path, ec); diff --git a/src/common/fs/file.h b/src/common/fs/file.h index 588fe619d..2c4ab4332 100644 --- a/src/common/fs/file.h +++ b/src/common/fs/file.h @@ -396,13 +396,22 @@ public: [[nodiscard]] size_t WriteString(std::span<const char> string) const; /** - * Attempts to flush any unwritten buffered data into the file and flush the file into the disk. + * Attempts to flush any unwritten buffered data into the file. * * @returns True if the flush was successful, false otherwise. */ bool Flush() const; /** + * Attempts to commit the file into the disk. + * Note that this is an expensive operation as this forces the operating system to write + * the contents of the file associated with the file descriptor into the disk. + * + * @returns True if the commit was successful, false otherwise. + */ + bool Commit() const; + + /** * Resizes the file to a given size. * If the file is resized to a smaller size, the remainder of the file is discarded. * If the file is resized to a larger size, the new area appears as if zero-filled. diff --git a/src/common/logging/backend.cpp b/src/common/logging/backend.cpp index b6fa4affb..61dddab3f 100644 --- a/src/common/logging/backend.cpp +++ b/src/common/logging/backend.cpp @@ -171,19 +171,22 @@ FileBackend::FileBackend(const std::filesystem::path& filename) { FileBackend::~FileBackend() = default; void FileBackend::Write(const Entry& entry) { + if (!file->IsOpen()) { + return; + } + using namespace Common::Literals; - // prevent logs from going over the maximum size (in case its spamming and the user doesn't - // know) + // Prevent logs from exceeding a set maximum size in the event that log entries are spammed. constexpr std::size_t MAX_BYTES_WRITTEN = 100_MiB; constexpr std::size_t MAX_BYTES_WRITTEN_EXTENDED = 1_GiB; - if (!file->IsOpen()) { - return; - } + const bool write_limit_exceeded = + bytes_written > MAX_BYTES_WRITTEN_EXTENDED || + (bytes_written > MAX_BYTES_WRITTEN && !Settings::values.extended_logging); - if (Settings::values.extended_logging && bytes_written > MAX_BYTES_WRITTEN_EXTENDED) { - return; - } else if (!Settings::values.extended_logging && bytes_written > MAX_BYTES_WRITTEN) { + // Close the file after the write limit is exceeded. + if (write_limit_exceeded) { + file->Close(); return; } diff --git a/src/common/settings.cpp b/src/common/settings.cpp index e1bb4b7ff..e1973af85 100644 --- a/src/common/settings.cpp +++ b/src/common/settings.cpp @@ -41,7 +41,7 @@ void LogSettings() { LOG_INFO(Config, "yuzu Configuration:"); log_setting("Controls_UseDockedMode", values.use_docked_mode.GetValue()); log_setting("System_RngSeed", values.rng_seed.GetValue().value_or(0)); - log_setting("System_CurrentUser", values.current_user); + log_setting("System_CurrentUser", values.current_user.GetValue()); log_setting("System_LanguageIndex", values.language_index.GetValue()); log_setting("System_RegionIndex", values.region_index.GetValue()); log_setting("System_TimeZoneIndex", values.time_zone_index.GetValue()); @@ -61,18 +61,18 @@ void LogSettings() { log_setting("Renderer_UseAsynchronousShaders", values.use_asynchronous_shaders.GetValue()); log_setting("Renderer_UseGarbageCollection", values.use_caches_gc.GetValue()); log_setting("Renderer_AnisotropicFilteringLevel", values.max_anisotropy.GetValue()); - log_setting("Audio_OutputEngine", values.sink_id); + log_setting("Audio_OutputEngine", values.sink_id.GetValue()); log_setting("Audio_EnableAudioStretching", values.enable_audio_stretching.GetValue()); - log_setting("Audio_OutputDevice", values.audio_device_id); - log_setting("DataStorage_UseVirtualSd", values.use_virtual_sd); + log_setting("Audio_OutputDevice", values.audio_device_id.GetValue()); + log_setting("DataStorage_UseVirtualSd", values.use_virtual_sd.GetValue()); log_path("DataStorage_CacheDir", Common::FS::GetYuzuPath(Common::FS::YuzuPath::CacheDir)); log_path("DataStorage_ConfigDir", Common::FS::GetYuzuPath(Common::FS::YuzuPath::ConfigDir)); log_path("DataStorage_LoadDir", Common::FS::GetYuzuPath(Common::FS::YuzuPath::LoadDir)); log_path("DataStorage_NANDDir", Common::FS::GetYuzuPath(Common::FS::YuzuPath::NANDDir)); log_path("DataStorage_SDMCDir", Common::FS::GetYuzuPath(Common::FS::YuzuPath::SDMCDir)); - log_setting("Debugging_ProgramArgs", values.program_args); - log_setting("Services_BCATBackend", values.bcat_backend); - log_setting("Services_BCATBoxcatLocal", values.bcat_boxcat_local); + log_setting("Debugging_ProgramArgs", values.program_args.GetValue()); + log_setting("Services_BCATBackend", values.bcat_backend.GetValue()); + log_setting("Services_BCATBoxcatLocal", values.bcat_boxcat_local.GetValue()); } bool IsConfiguringGlobal() { @@ -93,8 +93,8 @@ bool IsGPULevelHigh() { } bool IsFastmemEnabled() { - if (values.cpu_accuracy.GetValue() == CPUAccuracy::DebugMode) { - return values.cpuopt_fastmem; + if (values.cpu_debug_mode) { + return static_cast<bool>(values.cpuopt_fastmem); } return true; } diff --git a/src/common/settings.h b/src/common/settings.h index 82ec18e27..71d0f864f 100644 --- a/src/common/settings.h +++ b/src/common/settings.h @@ -10,10 +10,12 @@ #include <map> #include <optional> #include <string> +#include <utility> #include <vector> #include "common/common_types.h" #include "common/settings_input.h" +#include "input_common/udp/client.h" namespace Settings { @@ -29,73 +31,240 @@ enum class GPUAccuracy : u32 { }; enum class CPUAccuracy : u32 { - Accurate = 0, - Unsafe = 1, - DebugMode = 2, + Auto = 0, + Accurate = 1, + Unsafe = 2, }; +/** The BasicSetting class is a simple resource manager. It defines a label and default value + * alongside the actual value of the setting for simpler and less-error prone use with frontend + * configurations. Setting a default value and label is required, though subclasses may deviate from + * this requirement. + */ +template <typename Type> +class BasicSetting { +protected: + BasicSetting() = default; + + /** + * Only sets the setting to the given initializer, leaving the other members to their default + * initializers. + * + * @param global_val Initial value of the setting + */ + explicit BasicSetting(const Type& global_val) : global{global_val} {} + +public: + /** + * Sets a default value, label, and setting value. + * + * @param default_val Intial value of the setting, and default value of the setting + * @param name Label for the setting + */ + explicit BasicSetting(const Type& default_val, const std::string& name) + : default_value{default_val}, global{default_val}, label{name} {} + ~BasicSetting() = default; + + /** + * Returns a reference to the setting's value. + * + * @returns A reference to the setting + */ + [[nodiscard]] const Type& GetValue() const { + return global; + } + + /** + * Sets the setting to the given value. + * + * @param value The desired value + */ + void SetValue(const Type& value) { + Type temp{value}; + std::swap(global, temp); + } + + /** + * Returns the value that this setting was created with. + * + * @returns A reference to the default value + */ + [[nodiscard]] const Type& GetDefault() const { + return default_value; + } + + /** + * Returns the label this setting was created with. + * + * @returns A reference to the label + */ + [[nodiscard]] const std::string& GetLabel() const { + return label; + } + + /** + * Assigns a value to the setting. + * + * @param value The desired setting value + * + * @returns A reference to the setting + */ + const Type& operator=(const Type& value) { + Type temp{value}; + std::swap(global, temp); + return global; + } + + /** + * Returns a reference to the setting. + * + * @returns A reference to the setting + */ + explicit operator const Type&() const { + return global; + } + +protected: + const Type default_value{}; ///< The default value + Type global{}; ///< The setting + const std::string label{}; ///< The setting's label +}; + +/** + * The Setting class is a slightly more complex version of the BasicSetting class. This adds a + * custom setting to switch to when a guest application specifically requires it. The effect is that + * other components of the emulator can access the setting's intended value without any need for the + * component to ask whether the custom or global setting is needed at the moment. + * + * By default, the global setting is used. + * + * Like the BasicSetting, this requires setting a default value and label to use. + */ template <typename Type> -class Setting final { +class Setting final : public BasicSetting<Type> { public: - Setting() = default; - explicit Setting(Type val) : global{val} {} + /** + * Sets a default value, label, and setting value. + * + * @param default_val Intial value of the setting, and default value of the setting + * @param name Label for the setting + */ + explicit Setting(const Type& default_val, const std::string& name) + : BasicSetting<Type>(default_val, name) {} ~Setting() = default; + + /** + * Tells this setting to represent either the global or custom setting when other member + * functions are used. + * + * @param to_global Whether to use the global or custom setting. + */ void SetGlobal(bool to_global) { use_global = to_global; } - bool UsingGlobal() const { + + /** + * Returns whether this setting is using the global setting or not. + * + * @returns The global state + */ + [[nodiscard]] bool UsingGlobal() const { return use_global; } - Type GetValue(bool need_global = false) const { + + /** + * Returns either the global or custom setting depending on the values of this setting's global + * state or if the global value was specifically requested. + * + * @param need_global Request global value regardless of setting's state; defaults to false + * + * @returns The required value of the setting + */ + [[nodiscard]] const Type& GetValue(bool need_global = false) const { if (use_global || need_global) { - return global; + return this->global; } - return local; + return custom; } + + /** + * Sets the current setting value depending on the global state. + * + * @param value The new value + */ void SetValue(const Type& value) { + Type temp{value}; if (use_global) { - global = value; + std::swap(this->global, temp); } else { - local = value; + std::swap(custom, temp); + } + } + + /** + * Assigns the current setting value depending on the global state. + * + * @param value The new value + * + * @returns A reference to the current setting value + */ + const Type& operator=(const Type& value) { + Type temp{value}; + if (use_global) { + std::swap(this->global, temp); + return this->global; + } + std::swap(custom, temp); + return custom; + } + + /** + * Returns the current setting value depending on the global state. + * + * @returns A reference to the current setting value + */ + explicit operator const Type&() const { + if (use_global) { + return this->global; } + return custom; } private: - bool use_global = true; - Type global{}; - Type local{}; + bool use_global{true}; ///< The setting's global state + Type custom{}; ///< The custom value of the setting }; /** - * The InputSetting class allows for getting a reference to either the global or local members. + * The InputSetting class allows for getting a reference to either the global or custom members. * This is required as we cannot easily modify the values of user-defined types within containers * using the SetValue() member function found in the Setting class. The primary purpose of this - * class is to store an array of 10 PlayerInput structs for both the global and local (per-game) - * setting and allows for easily accessing and modifying both settings. + * class is to store an array of 10 PlayerInput structs for both the global and custom setting and + * allows for easily accessing and modifying both settings. */ template <typename Type> class InputSetting final { public: InputSetting() = default; - explicit InputSetting(Type val) : global{val} {} + explicit InputSetting(Type val) : BasicSetting<Type>(val) {} ~InputSetting() = default; void SetGlobal(bool to_global) { use_global = to_global; } - bool UsingGlobal() const { + [[nodiscard]] bool UsingGlobal() const { return use_global; } - Type& GetValue(bool need_global = false) { + [[nodiscard]] Type& GetValue(bool need_global = false) { if (use_global || need_global) { return global; } - return local; + return custom; } private: - bool use_global = true; - Type global{}; - Type local{}; + bool use_global{true}; ///< The setting's global state + Type global{}; ///< The setting + Type custom{}; ///< The custom setting value }; struct TouchFromButtonMap { @@ -105,144 +274,158 @@ struct TouchFromButtonMap { struct Values { // Audio - std::string audio_device_id; - std::string sink_id; - bool audio_muted; - Setting<bool> enable_audio_stretching; - Setting<float> volume; + BasicSetting<std::string> audio_device_id{"auto", "output_device"}; + BasicSetting<std::string> sink_id{"auto", "output_engine"}; + BasicSetting<bool> audio_muted{false, "audio_muted"}; + Setting<bool> enable_audio_stretching{true, "enable_audio_stretching"}; + Setting<float> volume{1.0f, "volume"}; // Core - Setting<bool> use_multi_core; + Setting<bool> use_multi_core{true, "use_multi_core"}; // Cpu - Setting<CPUAccuracy> cpu_accuracy; - - bool cpuopt_page_tables; - bool cpuopt_block_linking; - bool cpuopt_return_stack_buffer; - bool cpuopt_fast_dispatcher; - bool cpuopt_context_elimination; - bool cpuopt_const_prop; - bool cpuopt_misc_ir; - bool cpuopt_reduce_misalign_checks; - bool cpuopt_fastmem; - - Setting<bool> cpuopt_unsafe_unfuse_fma; - Setting<bool> cpuopt_unsafe_reduce_fp_error; - Setting<bool> cpuopt_unsafe_ignore_standard_fpcr; - Setting<bool> cpuopt_unsafe_inaccurate_nan; - Setting<bool> cpuopt_unsafe_fastmem_check; + Setting<CPUAccuracy> cpu_accuracy{CPUAccuracy::Auto, "cpu_accuracy"}; + // TODO: remove cpu_accuracy_first_time, migration setting added 8 July 2021 + BasicSetting<bool> cpu_accuracy_first_time{true, "cpu_accuracy_first_time"}; + BasicSetting<bool> cpu_debug_mode{false, "cpu_debug_mode"}; + + BasicSetting<bool> cpuopt_page_tables{true, "cpuopt_page_tables"}; + BasicSetting<bool> cpuopt_block_linking{true, "cpuopt_block_linking"}; + BasicSetting<bool> cpuopt_return_stack_buffer{true, "cpuopt_return_stack_buffer"}; + BasicSetting<bool> cpuopt_fast_dispatcher{true, "cpuopt_fast_dispatcher"}; + BasicSetting<bool> cpuopt_context_elimination{true, "cpuopt_context_elimination"}; + BasicSetting<bool> cpuopt_const_prop{true, "cpuopt_const_prop"}; + BasicSetting<bool> cpuopt_misc_ir{true, "cpuopt_misc_ir"}; + BasicSetting<bool> cpuopt_reduce_misalign_checks{true, "cpuopt_reduce_misalign_checks"}; + BasicSetting<bool> cpuopt_fastmem{true, "cpuopt_fastmem"}; + + Setting<bool> cpuopt_unsafe_unfuse_fma{true, "cpuopt_unsafe_unfuse_fma"}; + Setting<bool> cpuopt_unsafe_reduce_fp_error{true, "cpuopt_unsafe_reduce_fp_error"}; + Setting<bool> cpuopt_unsafe_ignore_standard_fpcr{true, "cpuopt_unsafe_ignore_standard_fpcr"}; + Setting<bool> cpuopt_unsafe_inaccurate_nan{true, "cpuopt_unsafe_inaccurate_nan"}; + Setting<bool> cpuopt_unsafe_fastmem_check{true, "cpuopt_unsafe_fastmem_check"}; // Renderer - Setting<RendererBackend> renderer_backend; - bool renderer_debug; - Setting<int> vulkan_device; - - Setting<u16> resolution_factor{1}; - Setting<int> fullscreen_mode; - Setting<int> aspect_ratio; - Setting<int> max_anisotropy; - Setting<bool> use_frame_limit; - Setting<u16> frame_limit; - Setting<bool> use_disk_shader_cache; - Setting<GPUAccuracy> gpu_accuracy; - Setting<bool> use_asynchronous_gpu_emulation; - Setting<bool> use_nvdec_emulation; - Setting<bool> accelerate_astc; - Setting<bool> use_vsync; - Setting<bool> disable_fps_limit; - Setting<bool> use_assembly_shaders; - Setting<bool> use_asynchronous_shaders; - Setting<bool> use_fast_gpu_time; - Setting<bool> use_caches_gc; - - Setting<float> bg_red; - Setting<float> bg_green; - Setting<float> bg_blue; + Setting<RendererBackend> renderer_backend{RendererBackend::OpenGL, "backend"}; + BasicSetting<bool> renderer_debug{false, "debug"}; + Setting<int> vulkan_device{0, "vulkan_device"}; + + Setting<u16> resolution_factor{1, "resolution_factor"}; + // *nix platforms may have issues with the borderless windowed fullscreen mode. + // Default to exclusive fullscreen on these platforms for now. + Setting<int> fullscreen_mode{ +#ifdef _WIN32 + 0, +#else + 1, +#endif + "fullscreen_mode"}; + Setting<int> aspect_ratio{0, "aspect_ratio"}; + Setting<int> max_anisotropy{0, "max_anisotropy"}; + Setting<bool> use_frame_limit{true, "use_frame_limit"}; + Setting<u16> frame_limit{100, "frame_limit"}; + Setting<bool> use_disk_shader_cache{true, "use_disk_shader_cache"}; + Setting<GPUAccuracy> gpu_accuracy{GPUAccuracy::High, "gpu_accuracy"}; + Setting<bool> use_asynchronous_gpu_emulation{true, "use_asynchronous_gpu_emulation"}; + Setting<bool> use_nvdec_emulation{true, "use_nvdec_emulation"}; + Setting<bool> accelerate_astc{true, "accelerate_astc"}; + Setting<bool> use_vsync{true, "use_vsync"}; + BasicSetting<bool> disable_fps_limit{false, "disable_fps_limit"}; + Setting<bool> use_assembly_shaders{false, "use_assembly_shaders"}; + Setting<bool> use_asynchronous_shaders{false, "use_asynchronous_shaders"}; + Setting<bool> use_fast_gpu_time{true, "use_fast_gpu_time"}; + Setting<bool> use_caches_gc{false, "use_caches_gc"}; + + Setting<float> bg_red{0.0f, "bg_red"}; + Setting<float> bg_green{0.0f, "bg_green"}; + Setting<float> bg_blue{0.0f, "bg_blue"}; // System - Setting<std::optional<u32>> rng_seed; + Setting<std::optional<u32>> rng_seed{std::optional<u32>(), "rng_seed"}; // Measured in seconds since epoch std::optional<std::chrono::seconds> custom_rtc; // Set on game boot, reset on stop. Seconds difference between current time and `custom_rtc` std::chrono::seconds custom_rtc_differential; - s32 current_user; - Setting<s32> language_index; - Setting<s32> region_index; - Setting<s32> time_zone_index; - Setting<s32> sound_index; + BasicSetting<s32> current_user{0, "current_user"}; + Setting<s32> language_index{1, "language_index"}; + Setting<s32> region_index{1, "region_index"}; + Setting<s32> time_zone_index{0, "time_zone_index"}; + Setting<s32> sound_index{1, "sound_index"}; // Controls InputSetting<std::array<PlayerInput, 10>> players; - Setting<bool> use_docked_mode; + Setting<bool> use_docked_mode{true, "use_docked_mode"}; - Setting<bool> vibration_enabled; - Setting<bool> enable_accurate_vibrations; + Setting<bool> vibration_enabled{true, "vibration_enabled"}; + Setting<bool> enable_accurate_vibrations{false, "enable_accurate_vibrations"}; - Setting<bool> motion_enabled; - std::string motion_device; - std::string udp_input_servers; + Setting<bool> motion_enabled{true, "motion_enabled"}; + BasicSetting<std::string> motion_device{"engine:motion_emu,update_period:100,sensitivity:0.01", + "motion_device"}; + BasicSetting<std::string> udp_input_servers{InputCommon::CemuhookUDP::DEFAULT_SRV, + "udp_input_servers"}; - bool mouse_panning; - float mouse_panning_sensitivity; - bool mouse_enabled; + BasicSetting<bool> mouse_panning{false, "mouse_panning"}; + BasicSetting<float> mouse_panning_sensitivity{1.0f, "mouse_panning_sensitivity"}; + BasicSetting<bool> mouse_enabled{false, "mouse_enabled"}; std::string mouse_device; MouseButtonsRaw mouse_buttons; - bool emulate_analog_keyboard; - bool keyboard_enabled; + BasicSetting<bool> emulate_analog_keyboard{false, "emulate_analog_keyboard"}; + BasicSetting<bool> keyboard_enabled{false, "keyboard_enabled"}; KeyboardKeysRaw keyboard_keys; KeyboardModsRaw keyboard_mods; - bool debug_pad_enabled; + BasicSetting<bool> debug_pad_enabled{false, "debug_pad_enabled"}; ButtonsRaw debug_pad_buttons; AnalogsRaw debug_pad_analogs; TouchscreenInput touchscreen; - bool use_touch_from_button; - std::string touch_device; - int touch_from_button_map_index; + BasicSetting<bool> use_touch_from_button{false, "use_touch_from_button"}; + BasicSetting<std::string> touch_device{"min_x:100,min_y:50,max_x:1800,max_y:850", + "touch_device"}; + BasicSetting<int> touch_from_button_map_index{0, "touch_from_button_map"}; std::vector<TouchFromButtonMap> touch_from_button_maps; std::atomic_bool is_device_reload_pending{true}; // Data Storage - bool use_virtual_sd; - bool gamecard_inserted; - bool gamecard_current_game; - std::string gamecard_path; + BasicSetting<bool> use_virtual_sd{true, "use_virtual_sd"}; + BasicSetting<bool> gamecard_inserted{false, "gamecard_inserted"}; + BasicSetting<bool> gamecard_current_game{false, "gamecard_current_game"}; + BasicSetting<std::string> gamecard_path{std::string(), "gamecard_path"}; // Debugging bool record_frame_times; - bool use_gdbstub; - u16 gdbstub_port; - std::string program_args; - bool dump_exefs; - bool dump_nso; - bool enable_fs_access_log; - bool reporting_services; - bool quest_flag; - bool disable_macro_jit; - bool extended_logging; - bool use_debug_asserts; - bool use_auto_stub; + BasicSetting<bool> use_gdbstub{false, "use_gdbstub"}; + BasicSetting<u16> gdbstub_port{0, "gdbstub_port"}; + BasicSetting<std::string> program_args{std::string(), "program_args"}; + BasicSetting<bool> dump_exefs{false, "dump_exefs"}; + BasicSetting<bool> dump_nso{false, "dump_nso"}; + BasicSetting<bool> enable_fs_access_log{false, "enable_fs_access_log"}; + BasicSetting<bool> reporting_services{false, "reporting_services"}; + BasicSetting<bool> quest_flag{false, "quest_flag"}; + BasicSetting<bool> disable_macro_jit{false, "disable_macro_jit"}; + BasicSetting<bool> extended_logging{false, "extended_logging"}; + BasicSetting<bool> use_debug_asserts{false, "use_debug_asserts"}; + BasicSetting<bool> use_auto_stub{false, "use_auto_stub"}; // Miscellaneous - std::string log_filter; - bool use_dev_keys; + BasicSetting<std::string> log_filter{"*:Info", "log_filter"}; + BasicSetting<bool> use_dev_keys{false, "use_dev_keys"}; // Services - std::string bcat_backend; - bool bcat_boxcat_local; + BasicSetting<std::string> bcat_backend{"none", "bcat_backend"}; + BasicSetting<bool> bcat_boxcat_local{false, "bcat_boxcat_local"}; // WebService - bool enable_telemetry; - std::string web_api_url; - std::string yuzu_username; - std::string yuzu_token; + BasicSetting<bool> enable_telemetry{true, "enable_telemetry"}; + BasicSetting<std::string> web_api_url{"https://api.yuzu-emu.org", "web_api_url"}; + BasicSetting<std::string> yuzu_username{std::string(), "yuzu_username"}; + BasicSetting<std::string> yuzu_token{std::string(), "yuzu_token"}; // Add-Ons std::map<u64, std::vector<std::string>> disabled_addons; diff --git a/src/common/thread_worker.cpp b/src/common/thread_worker.cpp deleted file mode 100644 index 8f9bf447a..000000000 --- a/src/common/thread_worker.cpp +++ /dev/null @@ -1,58 +0,0 @@ -// Copyright 2020 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#include "common/thread.h" -#include "common/thread_worker.h" - -namespace Common { - -ThreadWorker::ThreadWorker(std::size_t num_workers, const std::string& name) { - for (std::size_t i = 0; i < num_workers; ++i) - threads.emplace_back([this, thread_name{std::string{name}}] { - Common::SetCurrentThreadName(thread_name.c_str()); - - // Wait for first request - { - std::unique_lock lock{queue_mutex}; - condition.wait(lock, [this] { return stop || !requests.empty(); }); - } - - while (true) { - std::function<void()> task; - - { - std::unique_lock lock{queue_mutex}; - condition.wait(lock, [this] { return stop || !requests.empty(); }); - if (stop || requests.empty()) { - return; - } - task = std::move(requests.front()); - requests.pop(); - } - - task(); - } - }); -} - -ThreadWorker::~ThreadWorker() { - { - std::unique_lock lock{queue_mutex}; - stop = true; - } - condition.notify_all(); - for (std::thread& thread : threads) { - thread.join(); - } -} - -void ThreadWorker::QueueWork(std::function<void()>&& work) { - { - std::unique_lock lock{queue_mutex}; - requests.emplace(work); - } - condition.notify_one(); -} - -} // namespace Common diff --git a/src/common/thread_worker.h b/src/common/thread_worker.h index f1859971f..8272985ff 100644 --- a/src/common/thread_worker.h +++ b/src/common/thread_worker.h @@ -7,24 +7,110 @@ #include <atomic> #include <functional> #include <mutex> +#include <stop_token> #include <string> +#include <thread> +#include <type_traits> #include <vector> #include <queue> +#include "common/thread.h" +#include "common/unique_function.h" + namespace Common { -class ThreadWorker final { +template <class StateType = void> +class StatefulThreadWorker { + static constexpr bool with_state = !std::is_same_v<StateType, void>; + + struct DummyCallable { + int operator()() const noexcept { + return 0; + } + }; + + using Task = + std::conditional_t<with_state, UniqueFunction<void, StateType*>, UniqueFunction<void>>; + using StateMaker = std::conditional_t<with_state, std::function<StateType()>, DummyCallable>; + public: - explicit ThreadWorker(std::size_t num_workers, const std::string& name); - ~ThreadWorker(); - void QueueWork(std::function<void()>&& work); + explicit StatefulThreadWorker(size_t num_workers, std::string name, StateMaker func = {}) + : workers_queued{num_workers}, thread_name{std::move(name)} { + const auto lambda = [this, func](std::stop_token stop_token) { + Common::SetCurrentThreadName(thread_name.c_str()); + { + std::conditional_t<with_state, StateType, int> state{func()}; + while (!stop_token.stop_requested()) { + Task task; + { + std::unique_lock lock{queue_mutex}; + if (requests.empty()) { + wait_condition.notify_all(); + } + condition.wait(lock, stop_token, [this] { return !requests.empty(); }); + if (stop_token.stop_requested()) { + break; + } + task = std::move(requests.front()); + requests.pop(); + } + if constexpr (with_state) { + task(&state); + } else { + task(); + } + ++work_done; + } + } + ++workers_stopped; + wait_condition.notify_all(); + }; + threads.reserve(num_workers); + for (size_t i = 0; i < num_workers; ++i) { + threads.emplace_back(lambda); + } + } + + StatefulThreadWorker& operator=(const StatefulThreadWorker&) = delete; + StatefulThreadWorker(const StatefulThreadWorker&) = delete; + + StatefulThreadWorker& operator=(StatefulThreadWorker&&) = delete; + StatefulThreadWorker(StatefulThreadWorker&&) = delete; + + void QueueWork(Task work) { + { + std::unique_lock lock{queue_mutex}; + requests.emplace(std::move(work)); + ++work_scheduled; + } + condition.notify_one(); + } + + void WaitForRequests(std::stop_token stop_token = {}) { + std::stop_callback callback(stop_token, [this] { + for (auto& thread : threads) { + thread.request_stop(); + } + }); + std::unique_lock lock{queue_mutex}; + wait_condition.wait(lock, [this] { + return workers_stopped >= workers_queued || work_done >= work_scheduled; + }); + } private: - std::vector<std::thread> threads; - std::queue<std::function<void()>> requests; + std::queue<Task> requests; std::mutex queue_mutex; - std::condition_variable condition; - std::atomic_bool stop{}; + std::condition_variable_any condition; + std::condition_variable wait_condition; + std::atomic<size_t> work_scheduled{}; + std::atomic<size_t> work_done{}; + std::atomic<size_t> workers_stopped{}; + std::atomic<size_t> workers_queued{}; + std::string thread_name; + std::vector<std::jthread> threads; }; +using ThreadWorker = StatefulThreadWorker<>; + } // namespace Common diff --git a/src/common/unique_function.h b/src/common/unique_function.h new file mode 100644 index 000000000..ca0559071 --- /dev/null +++ b/src/common/unique_function.h @@ -0,0 +1,62 @@ +// Copyright 2021 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <memory> +#include <utility> + +namespace Common { + +/// General purpose function wrapper similar to std::function. +/// Unlike std::function, the captured values don't have to be copyable. +/// This class can be moved but not copied. +template <typename ResultType, typename... Args> +class UniqueFunction { + class CallableBase { + public: + virtual ~CallableBase() = default; + virtual ResultType operator()(Args&&...) = 0; + }; + + template <typename Functor> + class Callable final : public CallableBase { + public: + Callable(Functor&& functor_) : functor{std::move(functor_)} {} + ~Callable() override = default; + + ResultType operator()(Args&&... args) override { + return functor(std::forward<Args>(args)...); + } + + private: + Functor functor; + }; + +public: + UniqueFunction() = default; + + template <typename Functor> + UniqueFunction(Functor&& functor) + : callable{std::make_unique<Callable<Functor>>(std::move(functor))} {} + + UniqueFunction& operator=(UniqueFunction&& rhs) noexcept = default; + UniqueFunction(UniqueFunction&& rhs) noexcept = default; + + UniqueFunction& operator=(const UniqueFunction&) = delete; + UniqueFunction(const UniqueFunction&) = delete; + + ResultType operator()(Args&&... args) const { + return (*callable)(std::forward<Args>(args)...); + } + + explicit operator bool() const noexcept { + return static_cast<bool>(callable); + } + +private: + std::unique_ptr<CallableBase> callable; +}; + +} // namespace Common diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 83b5b7676..c7b899131 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -272,22 +272,22 @@ add_library(core STATIC hle/service/am/applet_ae.h hle/service/am/applet_oe.cpp hle/service/am/applet_oe.h + hle/service/am/applets/applet_controller.cpp + hle/service/am/applets/applet_controller.h + hle/service/am/applets/applet_error.cpp + hle/service/am/applets/applet_error.h + hle/service/am/applets/applet_general_backend.cpp + hle/service/am/applets/applet_general_backend.h + hle/service/am/applets/applet_profile_select.cpp + hle/service/am/applets/applet_profile_select.h + hle/service/am/applets/applet_software_keyboard.cpp + hle/service/am/applets/applet_software_keyboard.h + hle/service/am/applets/applet_software_keyboard_types.h + hle/service/am/applets/applet_web_browser.cpp + hle/service/am/applets/applet_web_browser.h + hle/service/am/applets/applet_web_browser_types.h hle/service/am/applets/applets.cpp hle/service/am/applets/applets.h - hle/service/am/applets/controller.cpp - hle/service/am/applets/controller.h - hle/service/am/applets/error.cpp - hle/service/am/applets/error.h - hle/service/am/applets/general_backend.cpp - hle/service/am/applets/general_backend.h - hle/service/am/applets/profile_select.cpp - hle/service/am/applets/profile_select.h - hle/service/am/applets/software_keyboard.cpp - hle/service/am/applets/software_keyboard.h - hle/service/am/applets/software_keyboard_types.h - hle/service/am/applets/web_browser.cpp - hle/service/am/applets/web_browser.h - hle/service/am/applets/web_types.h hle/service/am/idle.cpp hle/service/am/idle.h hle/service/am/omm.cpp @@ -300,10 +300,10 @@ add_library(core STATIC hle/service/aoc/aoc_u.h hle/service/apm/apm.cpp hle/service/apm/apm.h - hle/service/apm/controller.cpp - hle/service/apm/controller.h - hle/service/apm/interface.cpp - hle/service/apm/interface.h + hle/service/apm/apm_controller.cpp + hle/service/apm/apm_controller.h + hle/service/apm/apm_interface.cpp + hle/service/apm/apm_interface.h hle/service/audio/audctl.cpp hle/service/audio/audctl.h hle/service/audio/auddbg.cpp @@ -335,8 +335,8 @@ add_library(core STATIC hle/service/bcat/backend/backend.h hle/service/bcat/bcat.cpp hle/service/bcat/bcat.h - hle/service/bcat/module.cpp - hle/service/bcat/module.h + hle/service/bcat/bcat_module.cpp + hle/service/bcat/bcat_module.h hle/service/bpc/bpc.cpp hle/service/bpc/bpc.h hle/service/btdrv/btdrv.cpp @@ -382,8 +382,8 @@ add_library(core STATIC hle/service/friend/errors.h hle/service/friend/friend.cpp hle/service/friend/friend.h - hle/service/friend/interface.cpp - hle/service/friend/interface.h + hle/service/friend/friend_interface.cpp + hle/service/friend/friend_interface.h hle/service/glue/arp.cpp hle/service/glue/arp.h hle/service/glue/bgtc.cpp @@ -393,8 +393,8 @@ add_library(core STATIC hle/service/glue/errors.h hle/service/glue/glue.cpp hle/service/glue/glue.h - hle/service/glue/manager.cpp - hle/service/glue/manager.h + hle/service/glue/glue_manager.cpp + hle/service/glue/glue_manager.h hle/service/grc/grc.cpp hle/service/grc/grc.h hle/service/hid/hid.cpp @@ -435,10 +435,10 @@ add_library(core STATIC hle/service/lm/lm.h hle/service/mig/mig.cpp hle/service/mig/mig.h - hle/service/mii/manager.cpp - hle/service/mii/manager.h hle/service/mii/mii.cpp hle/service/mii/mii.h + hle/service/mii/mii_manager.cpp + hle/service/mii/mii_manager.h hle/service/mii/raw_data.cpp hle/service/mii/raw_data.h hle/service/mii/types.h @@ -486,11 +486,11 @@ add_library(core STATIC hle/service/nvdrv/devices/nvhost_vic.h hle/service/nvdrv/devices/nvmap.cpp hle/service/nvdrv/devices/nvmap.h - hle/service/nvdrv/interface.cpp - hle/service/nvdrv/interface.h hle/service/nvdrv/nvdata.h hle/service/nvdrv/nvdrv.cpp hle/service/nvdrv/nvdrv.h + hle/service/nvdrv/nvdrv_interface.cpp + hle/service/nvdrv/nvdrv_interface.h hle/service/nvdrv/nvmemp.cpp hle/service/nvdrv/nvmemp.h hle/service/nvdrv/syncpoint_manager.cpp @@ -503,10 +503,10 @@ add_library(core STATIC hle/service/olsc/olsc.h hle/service/pcie/pcie.cpp hle/service/pcie/pcie.h - hle/service/pctl/module.cpp - hle/service/pctl/module.h hle/service/pctl/pctl.cpp hle/service/pctl/pctl.h + hle/service/pctl/pctl_module.cpp + hle/service/pctl/pctl_module.h hle/service/pcv/pcv.cpp hle/service/pcv/pcv.h hle/service/pm/pm.cpp @@ -529,10 +529,10 @@ add_library(core STATIC hle/service/set/set_sys.h hle/service/set/settings.cpp hle/service/set/settings.h - hle/service/sm/controller.cpp - hle/service/sm/controller.h hle/service/sm/sm.cpp hle/service/sm/sm.h + hle/service/sm/sm_controller.cpp + hle/service/sm/sm_controller.h hle/service/sockets/bsd.cpp hle/service/sockets/bsd.h hle/service/sockets/ethc.cpp @@ -547,10 +547,10 @@ add_library(core STATIC hle/service/sockets/sockets_translate.h hle/service/spl/csrng.cpp hle/service/spl/csrng.h - hle/service/spl/module.cpp - hle/service/spl/module.h hle/service/spl/spl.cpp hle/service/spl/spl.h + hle/service/spl/spl_module.cpp + hle/service/spl/spl_module.h hle/service/spl/spl_results.h hle/service/spl/spl_types.h hle/service/ssl/ssl.cpp @@ -559,8 +559,6 @@ add_library(core STATIC hle/service/time/ephemeral_network_system_clock_context_writer.h hle/service/time/ephemeral_network_system_clock_core.h hle/service/time/errors.h - hle/service/time/interface.cpp - hle/service/time/interface.h hle/service/time/local_system_clock_context_writer.h hle/service/time/network_system_clock_context_writer.h hle/service/time/standard_local_system_clock_core.h @@ -578,6 +576,8 @@ add_library(core STATIC hle/service/time/tick_based_steady_clock_core.h hle/service/time/time.cpp hle/service/time/time.h + hle/service/time/time_interface.cpp + hle/service/time/time_interface.h hle/service/time/time_manager.cpp hle/service/time/time_manager.h hle/service/time/time_sharedmemory.cpp @@ -654,24 +654,19 @@ endif() if (MSVC) target_compile_options(core PRIVATE - /we4018 # 'expression' : signed/unsigned mismatch - /we4244 # 'argument' : conversion from 'type1' to 'type2', possible loss of data (floating-point) - /we4245 # 'conversion' : conversion from 'type1' to 'type2', signed/unsigned mismatch + /we4242 # 'identifier': conversion from 'type1' to 'type2', possible loss of data + /we4244 # 'conversion': conversion from 'type1' to 'type2', possible loss of data + /we4245 # 'conversion': conversion from 'type1' to 'type2', signed/unsigned mismatch /we4254 # 'operator': conversion from 'type1:field_bits' to 'type2:field_bits', possible loss of data - /we4267 # 'var' : conversion from 'size_t' to 'type', possible loss of data - /we4305 # 'context' : truncation from 'type1' to 'type2' /we4456 # Declaration of 'identifier' hides previous local declaration /we4457 # Declaration of 'identifier' hides function parameter /we4458 # Declaration of 'identifier' hides class member /we4459 # Declaration of 'identifier' hides global declaration - /we4715 # 'function' : not all control paths return a value ) else() target_compile_options(core PRIVATE -Werror=conversion -Werror=ignored-qualifiers - -Werror=implicit-fallthrough - -Werror=sign-compare -Werror=shadow $<$<CXX_COMPILER_ID:GNU>:-Werror=class-memaccess> diff --git a/src/core/arm/dynarmic/arm_dynarmic_32.cpp b/src/core/arm/dynarmic/arm_dynarmic_32.cpp index 77a44f862..b0d89c539 100644 --- a/src/core/arm/dynarmic/arm_dynarmic_32.cpp +++ b/src/core/arm/dynarmic/arm_dynarmic_32.cpp @@ -150,7 +150,7 @@ std::shared_ptr<Dynarmic::A32::Jit> ARM_Dynarmic_32::MakeJit(Common::PageTable* config.far_code_offset = 400_MiB; // Safe optimizations - if (Settings::values.cpu_accuracy.GetValue() == Settings::CPUAccuracy::DebugMode) { + if (Settings::values.cpu_debug_mode) { if (!Settings::values.cpuopt_page_tables) { config.page_table = nullptr; } @@ -183,20 +183,28 @@ std::shared_ptr<Dynarmic::A32::Jit> ARM_Dynarmic_32::MakeJit(Common::PageTable* // Unsafe optimizations if (Settings::values.cpu_accuracy.GetValue() == Settings::CPUAccuracy::Unsafe) { config.unsafe_optimizations = true; - if (Settings::values.cpuopt_unsafe_unfuse_fma.GetValue()) { + if (Settings::values.cpuopt_unsafe_unfuse_fma) { config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_UnfuseFMA; } - if (Settings::values.cpuopt_unsafe_reduce_fp_error.GetValue()) { + if (Settings::values.cpuopt_unsafe_reduce_fp_error) { config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_ReducedErrorFP; } - if (Settings::values.cpuopt_unsafe_ignore_standard_fpcr.GetValue()) { + if (Settings::values.cpuopt_unsafe_ignore_standard_fpcr) { config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_IgnoreStandardFPCRValue; } - if (Settings::values.cpuopt_unsafe_inaccurate_nan.GetValue()) { + if (Settings::values.cpuopt_unsafe_inaccurate_nan) { config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_InaccurateNaN; } } + // Curated optimizations + if (Settings::values.cpu_accuracy.GetValue() == Settings::CPUAccuracy::Auto) { + config.unsafe_optimizations = true; + config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_UnfuseFMA; + config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_IgnoreStandardFPCRValue; + config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_InaccurateNaN; + } + return std::make_unique<Dynarmic::A32::Jit>(config); } diff --git a/src/core/arm/dynarmic/arm_dynarmic_64.cpp b/src/core/arm/dynarmic/arm_dynarmic_64.cpp index 75332e348..bf27ffe71 100644 --- a/src/core/arm/dynarmic/arm_dynarmic_64.cpp +++ b/src/core/arm/dynarmic/arm_dynarmic_64.cpp @@ -190,7 +190,7 @@ std::shared_ptr<Dynarmic::A64::Jit> ARM_Dynarmic_64::MakeJit(Common::PageTable* config.far_code_offset = 400_MiB; // Safe optimizations - if (Settings::values.cpu_accuracy.GetValue() == Settings::CPUAccuracy::DebugMode) { + if (Settings::values.cpu_debug_mode) { if (!Settings::values.cpuopt_page_tables) { config.page_table = nullptr; } @@ -223,20 +223,28 @@ std::shared_ptr<Dynarmic::A64::Jit> ARM_Dynarmic_64::MakeJit(Common::PageTable* // Unsafe optimizations if (Settings::values.cpu_accuracy.GetValue() == Settings::CPUAccuracy::Unsafe) { config.unsafe_optimizations = true; - if (Settings::values.cpuopt_unsafe_unfuse_fma.GetValue()) { + if (Settings::values.cpuopt_unsafe_unfuse_fma) { config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_UnfuseFMA; } - if (Settings::values.cpuopt_unsafe_reduce_fp_error.GetValue()) { + if (Settings::values.cpuopt_unsafe_reduce_fp_error) { config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_ReducedErrorFP; } - if (Settings::values.cpuopt_unsafe_inaccurate_nan.GetValue()) { + if (Settings::values.cpuopt_unsafe_inaccurate_nan) { config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_InaccurateNaN; } - if (Settings::values.cpuopt_unsafe_fastmem_check.GetValue()) { + if (Settings::values.cpuopt_unsafe_fastmem_check) { config.fastmem_address_space_bits = 64; } } + // Curated optimizations + if (Settings::values.cpu_accuracy.GetValue() == Settings::CPUAccuracy::Auto) { + config.unsafe_optimizations = true; + config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_UnfuseFMA; + config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_InaccurateNaN; + config.fastmem_address_space_bits = 64; + } + return std::make_shared<Dynarmic::A64::Jit>(config); } diff --git a/src/core/core.cpp b/src/core/core.cpp index e6f1aa0e7..406320ed6 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp @@ -35,9 +35,9 @@ #include "core/hle/kernel/kernel.h" #include "core/hle/kernel/physical_core.h" #include "core/hle/service/am/applets/applets.h" -#include "core/hle/service/apm/controller.h" +#include "core/hle/service/apm/apm_controller.h" #include "core/hle/service/filesystem/filesystem.h" -#include "core/hle/service/glue/manager.h" +#include "core/hle/service/glue/glue_manager.h" #include "core/hle/service/hid/hid.h" #include "core/hle/service/service.h" #include "core/hle/service/sm/sm.h" @@ -263,9 +263,9 @@ struct System::Impl { if (Settings::values.gamecard_inserted) { if (Settings::values.gamecard_current_game) { fs_controller.SetGameCard(GetGameFileFromPath(virtual_filesystem, filepath)); - } else if (!Settings::values.gamecard_path.empty()) { - fs_controller.SetGameCard( - GetGameFileFromPath(virtual_filesystem, Settings::values.gamecard_path)); + } else if (!Settings::values.gamecard_path.GetValue().empty()) { + const auto gamecard_path = Settings::values.gamecard_path.GetValue(); + fs_controller.SetGameCard(GetGameFileFromPath(virtual_filesystem, gamecard_path)); } } diff --git a/src/core/file_sys/content_archive.cpp b/src/core/file_sys/content_archive.cpp index 24eff210f..7019a7a68 100644 --- a/src/core/file_sys/content_archive.cpp +++ b/src/core/file_sys/content_archive.cpp @@ -5,7 +5,6 @@ #include <algorithm> #include <cstring> #include <optional> -#include <ranges> #include <utility> #include "common/logging/log.h" 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( static void ApplyLayeredFS(VirtualFile& romfs, u64 title_id, ContentRecordType type, const Service::FileSystem::FileSystemController& fs_controller) { const auto load_dir = fs_controller.GetModificationLoadRoot(title_id); + const auto sdmc_load_dir = fs_controller.GetSDMCModificationLoadRoot(title_id); if ((type != ContentRecordType::Program && type != ContentRecordType::Data) || - load_dir == nullptr || load_dir->GetSize() <= 0) { + ((load_dir == nullptr || load_dir->GetSize() <= 0) && + (sdmc_load_dir == nullptr || sdmc_load_dir->GetSize() <= 0))) { return; } @@ -356,7 +358,10 @@ static void ApplyLayeredFS(VirtualFile& romfs, u64 title_id, ContentRecordType t } const auto& disabled = Settings::values.disabled_addons[title_id]; - auto patch_dirs = load_dir->GetSubdirectories(); + std::vector<VirtualDir> patch_dirs = load_dir->GetSubdirectories(); + if (std::find(disabled.cbegin(), disabled.cend(), "SDMC") == disabled.cend()) { + patch_dirs.push_back(sdmc_load_dir); + } std::sort(patch_dirs.begin(), patch_dirs.end(), [](const VirtualDir& l, const VirtualDir& r) { return l->GetName() < r->GetName(); }); @@ -402,7 +407,7 @@ static void ApplyLayeredFS(VirtualFile& romfs, u64 title_id, ContentRecordType t } VirtualFile PatchManager::PatchRomFS(VirtualFile romfs, u64 ivfc_offset, ContentRecordType type, - VirtualFile update_raw) const { + VirtualFile update_raw, bool apply_layeredfs) const { const auto log_string = fmt::format("Patching RomFS for title_id={:016X}, type={:02X}", title_id, static_cast<u8>(type)); @@ -442,7 +447,9 @@ VirtualFile PatchManager::PatchRomFS(VirtualFile romfs, u64 ivfc_offset, Content } // LayeredFS - ApplyLayeredFS(romfs, title_id, type, fs_controller); + if (apply_layeredfs) { + ApplyLayeredFS(romfs, title_id, type, fs_controller); + } return romfs; } @@ -524,6 +531,15 @@ PatchManager::PatchVersionNames PatchManager::GetPatchVersionNames(VirtualFile u } } + // SDMC mod directory (RomFS LayeredFS) + const auto sdmc_mod_dir = fs_controller.GetSDMCModificationLoadRoot(title_id); + if (sdmc_mod_dir != nullptr && sdmc_mod_dir->GetSize() > 0 && + IsDirValidAndNonEmpty(FindSubdirectoryCaseless(sdmc_mod_dir, "romfs"))) { + const auto mod_disabled = + std::find(disabled.begin(), disabled.end(), "SDMC") != disabled.end(); + out.insert_or_assign(mod_disabled ? "[D] SDMC" : "SDMC", "LayeredFS"); + } + // DLC const auto dlc_entries = 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: // - LayeredFS [[nodiscard]] VirtualFile PatchRomFS(VirtualFile base, u64 ivfc_offset, ContentRecordType type = ContentRecordType::Program, - VirtualFile update_raw = nullptr) const; + VirtualFile update_raw = nullptr, + bool apply_layeredfs = true) const; // Returns a vector of pairs between patch names and patch versions. // 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 { constexpr u64 SDMC_TOTAL_SIZE = 0x10000000000; // 1 TiB -SDMCFactory::SDMCFactory(VirtualDir dir_) - : dir(std::move(dir_)), contents(std::make_unique<RegisteredCache>( - GetOrCreateDirectoryRelative(dir, "/Nintendo/Contents/registered"), - [](const VirtualFile& file, const NcaID& id) { - return NAX{file, id}.GetDecrypted(); - })), +SDMCFactory::SDMCFactory(VirtualDir sd_dir_, VirtualDir sd_mod_dir_) + : sd_dir(std::move(sd_dir_)), sd_mod_dir(std::move(sd_mod_dir_)), + contents(std::make_unique<RegisteredCache>( + GetOrCreateDirectoryRelative(sd_dir, "/Nintendo/Contents/registered"), + [](const VirtualFile& file, const NcaID& id) { + return NAX{file, id}.GetDecrypted(); + })), placeholder(std::make_unique<PlaceholderCache>( - GetOrCreateDirectoryRelative(dir, "/Nintendo/Contents/placehld"))) {} + GetOrCreateDirectoryRelative(sd_dir, "/Nintendo/Contents/placehld"))) {} SDMCFactory::~SDMCFactory() = default; ResultVal<VirtualDir> SDMCFactory::Open() const { - return MakeResult<VirtualDir>(dir); + return MakeResult<VirtualDir>(sd_dir); +} + +VirtualDir SDMCFactory::GetSDMCModificationLoadRoot(u64 title_id) const { + // LayeredFS doesn't work on updates and title id-less homebrew + if (title_id == 0 || (title_id & 0xFFF) == 0x800) { + return nullptr; + } + return GetOrCreateDirectoryRelative(sd_mod_dir, fmt::format("/{:016X}", title_id)); } VirtualDir SDMCFactory::GetSDMCContentDirectory() const { - return GetOrCreateDirectoryRelative(dir, "/Nintendo/Contents"); + return GetOrCreateDirectoryRelative(sd_dir, "/Nintendo/Contents"); } RegisteredCache* SDMCFactory::GetSDMCContents() const { @@ -40,11 +49,11 @@ PlaceholderCache* SDMCFactory::GetSDMCPlaceholder() const { } VirtualDir SDMCFactory::GetImageDirectory() const { - return GetOrCreateDirectoryRelative(dir, "/Nintendo/Album"); + return GetOrCreateDirectoryRelative(sd_dir, "/Nintendo/Album"); } u64 SDMCFactory::GetSDMCFreeSpace() const { - return GetSDMCTotalSpace() - dir->GetSize(); + return GetSDMCTotalSpace() - sd_dir->GetSize(); } 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; /// File system interface to the SDCard archive class SDMCFactory { public: - explicit SDMCFactory(VirtualDir dir); + explicit SDMCFactory(VirtualDir sd_dir_, VirtualDir sd_mod_dir_); ~SDMCFactory(); ResultVal<VirtualDir> Open() const; + VirtualDir GetSDMCModificationLoadRoot(u64 title_id) const; VirtualDir GetSDMCContentDirectory() const; RegisteredCache* GetSDMCContents() const; @@ -32,7 +33,8 @@ public: u64 GetSDMCTotalSpace() const; private: - VirtualDir dir; + VirtualDir sd_dir; + VirtualDir sd_mod_dir; std::unique_ptr<RegisteredCache> contents; std::unique_ptr<PlaceholderCache> placeholder; diff --git a/src/core/frontend/applets/profile_select.cpp b/src/core/frontend/applets/profile_select.cpp index 8d960d1ca..4c58c310f 100644 --- a/src/core/frontend/applets/profile_select.cpp +++ b/src/core/frontend/applets/profile_select.cpp @@ -13,7 +13,7 @@ ProfileSelectApplet::~ProfileSelectApplet() = default; void DefaultProfileSelectApplet::SelectProfile( std::function<void(std::optional<Common::UUID>)> callback) const { Service::Account::ProfileManager manager; - callback(manager.GetUser(Settings::values.current_user).value_or(Common::UUID{})); + callback(manager.GetUser(Settings::values.current_user.GetValue()).value_or(Common::UUID{})); LOG_INFO(Service_ACC, "called, selecting current user instead of prompting..."); } diff --git a/src/core/frontend/applets/software_keyboard.h b/src/core/frontend/applets/software_keyboard.h index 506eb35bb..228a548d4 100644 --- a/src/core/frontend/applets/software_keyboard.h +++ b/src/core/frontend/applets/software_keyboard.h @@ -9,7 +9,7 @@ #include "common/common_types.h" -#include "core/hle/service/am/applets/software_keyboard_types.h" +#include "core/hle/service/am/applets/applet_software_keyboard_types.h" namespace Core::Frontend { diff --git a/src/core/frontend/applets/web_browser.h b/src/core/frontend/applets/web_browser.h index d7bd44c27..915dde677 100644 --- a/src/core/frontend/applets/web_browser.h +++ b/src/core/frontend/applets/web_browser.h @@ -7,7 +7,7 @@ #include <functional> #include <string_view> -#include "core/hle/service/am/applets/web_types.h" +#include "core/hle/service/am/applets/applet_web_browser_types.h" namespace Core::Frontend { diff --git a/src/core/hardware_interrupt_manager.cpp b/src/core/hardware_interrupt_manager.cpp index 645f26e91..290db505e 100644 --- a/src/core/hardware_interrupt_manager.cpp +++ b/src/core/hardware_interrupt_manager.cpp @@ -5,7 +5,7 @@ #include "core/core.h" #include "core/core_timing.h" #include "core/hardware_interrupt_manager.h" -#include "core/hle/service/nvdrv/interface.h" +#include "core/hle/service/nvdrv/nvdrv_interface.h" #include "core/hle/service/sm/sm.h" namespace Core::Hardware { diff --git a/src/core/hle/ipc_helpers.h b/src/core/hle/ipc_helpers.h index 61bda3786..ceff2532d 100644 --- a/src/core/hle/ipc_helpers.h +++ b/src/core/hle/ipc_helpers.h @@ -345,8 +345,12 @@ public: explicit RequestParser(u32* command_buffer) : RequestHelperBase(command_buffer) {} explicit RequestParser(Kernel::HLERequestContext& ctx) : RequestHelperBase(ctx) { - ASSERT_MSG(ctx.GetDataPayloadOffset(), "context is incomplete"); - Skip(ctx.GetDataPayloadOffset(), false); + // TIPC does not have data payload offset + if (!ctx.IsTipc()) { + ASSERT_MSG(ctx.GetDataPayloadOffset(), "context is incomplete"); + Skip(ctx.GetDataPayloadOffset(), false); + } + // Skip the u64 command id, it's already stored in the context static constexpr u32 CommandIdSize = 2; Skip(CommandIdSize, false); diff --git a/src/core/hle/service/acc/acc.cpp b/src/core/hle/service/acc/acc.cpp index d1c1fb71d..2e969f2a8 100644 --- a/src/core/hle/service/acc/acc.cpp +++ b/src/core/hle/service/acc/acc.cpp @@ -26,7 +26,7 @@ #include "core/hle/service/acc/errors.h" #include "core/hle/service/acc/profile_manager.h" #include "core/hle/service/glue/arp.h" -#include "core/hle/service/glue/manager.h" +#include "core/hle/service/glue/glue_manager.h" #include "core/hle/service/sm/sm.h" #include "core/loader/loader.h" diff --git a/src/core/hle/service/acc/acc.h b/src/core/hle/service/acc/acc.h index 0e3ad8ec6..a83a480cd 100644 --- a/src/core/hle/service/acc/acc.h +++ b/src/core/hle/service/acc/acc.h @@ -5,7 +5,7 @@ #pragma once #include "common/uuid.h" -#include "core/hle/service/glue/manager.h" +#include "core/hle/service/glue/glue_manager.h" #include "core/hle/service/service.h" namespace Service::Account { diff --git a/src/core/hle/service/acc/profile_manager.cpp b/src/core/hle/service/acc/profile_manager.cpp index f72d5d561..24a1c9157 100644 --- a/src/core/hle/service/acc/profile_manager.cpp +++ b/src/core/hle/service/acc/profile_manager.cpp @@ -48,7 +48,8 @@ ProfileManager::ProfileManager() { CreateNewUser(UUID::Generate(), "yuzu"); } - auto current = std::clamp<int>(Settings::values.current_user, 0, MAX_USERS - 1); + auto current = + std::clamp<int>(static_cast<s32>(Settings::values.current_user), 0, MAX_USERS - 1); // If user index don't exist. Load the first user and change the active user if (!UserExistsIndex(current)) { diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp index b578153d3..a538f82e3 100644 --- a/src/core/hle/service/am/am.cpp +++ b/src/core/hle/service/am/am.cpp @@ -24,16 +24,16 @@ #include "core/hle/service/am/am.h" #include "core/hle/service/am/applet_ae.h" #include "core/hle/service/am/applet_oe.h" +#include "core/hle/service/am/applets/applet_profile_select.h" +#include "core/hle/service/am/applets/applet_software_keyboard.h" +#include "core/hle/service/am/applets/applet_web_browser.h" #include "core/hle/service/am/applets/applets.h" -#include "core/hle/service/am/applets/profile_select.h" -#include "core/hle/service/am/applets/software_keyboard.h" -#include "core/hle/service/am/applets/web_browser.h" #include "core/hle/service/am/idle.h" #include "core/hle/service/am/omm.h" #include "core/hle/service/am/spsm.h" #include "core/hle/service/am/tcap.h" -#include "core/hle/service/apm/controller.h" -#include "core/hle/service/apm/interface.h" +#include "core/hle/service/apm/apm_controller.h" +#include "core/hle/service/apm/apm_interface.h" #include "core/hle/service/bcat/backend/backend.h" #include "core/hle/service/filesystem/filesystem.h" #include "core/hle/service/ns/ns.h" @@ -1443,7 +1443,7 @@ void IApplicationFunctions::PopLaunchParameter(Kernel::HLERequestContext& ctx) { params.is_account_selected = 1; Account::ProfileManager profile_manager{}; - const auto uuid = profile_manager.GetUser(Settings::values.current_user); + const auto uuid = profile_manager.GetUser(static_cast<s32>(Settings::values.current_user)); ASSERT(uuid); params.current_user = uuid->uuid; diff --git a/src/core/hle/service/am/applets/controller.cpp b/src/core/hle/service/am/applets/applet_controller.cpp index 218c8d1e4..12682effe 100644 --- a/src/core/hle/service/am/applets/controller.cpp +++ b/src/core/hle/service/am/applets/applet_controller.cpp @@ -12,7 +12,7 @@ #include "core/frontend/applets/controller.h" #include "core/hle/result.h" #include "core/hle/service/am/am.h" -#include "core/hle/service/am/applets/controller.h" +#include "core/hle/service/am/applets/applet_controller.h" #include "core/hle/service/hid/controllers/npad.h" namespace Service::AM::Applets { diff --git a/src/core/hle/service/am/applets/controller.h b/src/core/hle/service/am/applets/applet_controller.h index 20617e91f..20617e91f 100644 --- a/src/core/hle/service/am/applets/controller.h +++ b/src/core/hle/service/am/applets/applet_controller.h diff --git a/src/core/hle/service/am/applets/error.cpp b/src/core/hle/service/am/applets/applet_error.cpp index c724e5d5b..ef6854d62 100644 --- a/src/core/hle/service/am/applets/error.cpp +++ b/src/core/hle/service/am/applets/applet_error.cpp @@ -11,7 +11,7 @@ #include "core/frontend/applets/error.h" #include "core/hle/kernel/k_process.h" #include "core/hle/service/am/am.h" -#include "core/hle/service/am/applets/error.h" +#include "core/hle/service/am/applets/applet_error.h" #include "core/reporter.h" namespace Service::AM::Applets { diff --git a/src/core/hle/service/am/applets/error.h b/src/core/hle/service/am/applets/applet_error.h index 8aa9046a5..8aa9046a5 100644 --- a/src/core/hle/service/am/applets/error.h +++ b/src/core/hle/service/am/applets/applet_error.h diff --git a/src/core/hle/service/am/applets/general_backend.cpp b/src/core/hle/service/am/applets/applet_general_backend.cpp index 9fcb9f95b..0f413f9a0 100644 --- a/src/core/hle/service/am/applets/general_backend.cpp +++ b/src/core/hle/service/am/applets/applet_general_backend.cpp @@ -12,7 +12,7 @@ #include "core/hle/kernel/k_process.h" #include "core/hle/result.h" #include "core/hle/service/am/am.h" -#include "core/hle/service/am/applets/general_backend.h" +#include "core/hle/service/am/applets/applet_general_backend.h" #include "core/reporter.h" namespace Service::AM::Applets { diff --git a/src/core/hle/service/am/applets/general_backend.h b/src/core/hle/service/am/applets/applet_general_backend.h index 7496ded88..7496ded88 100644 --- a/src/core/hle/service/am/applets/general_backend.h +++ b/src/core/hle/service/am/applets/applet_general_backend.h diff --git a/src/core/hle/service/am/applets/profile_select.cpp b/src/core/hle/service/am/applets/applet_profile_select.cpp index 37048be26..bdc21778e 100644 --- a/src/core/hle/service/am/applets/profile_select.cpp +++ b/src/core/hle/service/am/applets/applet_profile_select.cpp @@ -9,7 +9,7 @@ #include "core/core.h" #include "core/frontend/applets/profile_select.h" #include "core/hle/service/am/am.h" -#include "core/hle/service/am/applets/profile_select.h" +#include "core/hle/service/am/applets/applet_profile_select.h" namespace Service::AM::Applets { diff --git a/src/core/hle/service/am/applets/profile_select.h b/src/core/hle/service/am/applets/applet_profile_select.h index 8fb76e6c4..8fb76e6c4 100644 --- a/src/core/hle/service/am/applets/profile_select.h +++ b/src/core/hle/service/am/applets/applet_profile_select.h diff --git a/src/core/hle/service/am/applets/software_keyboard.cpp b/src/core/hle/service/am/applets/applet_software_keyboard.cpp index 00dfe1675..7cae90609 100644 --- a/src/core/hle/service/am/applets/software_keyboard.cpp +++ b/src/core/hle/service/am/applets/applet_software_keyboard.cpp @@ -6,7 +6,7 @@ #include "core/core.h" #include "core/frontend/applets/software_keyboard.h" #include "core/hle/service/am/am.h" -#include "core/hle/service/am/applets/software_keyboard.h" +#include "core/hle/service/am/applets/applet_software_keyboard.h" namespace Service::AM::Applets { diff --git a/src/core/hle/service/am/applets/software_keyboard.h b/src/core/hle/service/am/applets/applet_software_keyboard.h index e3fc733d0..9aef1bf11 100644 --- a/src/core/hle/service/am/applets/software_keyboard.h +++ b/src/core/hle/service/am/applets/applet_software_keyboard.h @@ -7,8 +7,8 @@ #include "common/common_funcs.h" #include "common/common_types.h" #include "core/hle/result.h" +#include "core/hle/service/am/applets/applet_software_keyboard_types.h" #include "core/hle/service/am/applets/applets.h" -#include "core/hle/service/am/applets/software_keyboard_types.h" namespace Core { class System; diff --git a/src/core/hle/service/am/applets/software_keyboard_types.h b/src/core/hle/service/am/applets/applet_software_keyboard_types.h index 21aa8e800..21aa8e800 100644 --- a/src/core/hle/service/am/applets/software_keyboard_types.h +++ b/src/core/hle/service/am/applets/applet_software_keyboard_types.h diff --git a/src/core/hle/service/am/applets/web_browser.cpp b/src/core/hle/service/am/applets/applet_web_browser.cpp index 3b28e829b..35f194961 100644 --- a/src/core/hle/service/am/applets/web_browser.cpp +++ b/src/core/hle/service/am/applets/applet_web_browser.cpp @@ -21,7 +21,7 @@ #include "core/hle/kernel/k_process.h" #include "core/hle/result.h" #include "core/hle/service/am/am.h" -#include "core/hle/service/am/applets/web_browser.h" +#include "core/hle/service/am/applets/applet_web_browser.h" #include "core/hle/service/filesystem/filesystem.h" #include "core/hle/service/ns/pl_u.h" @@ -58,6 +58,16 @@ std::string GetMainURL(const std::string& url) { return url.substr(0, index); } +std::string ResolveURL(const std::string& url) { + const auto index = url.find_first_of('%'); + + if (index == std::string::npos) { + return url; + } + + return url.substr(0, index) + "lp1" + url.substr(index + 1); +} + WebArgInputTLVMap ReadWebArgs(const std::vector<u8>& web_arg, WebArgHeader& web_arg_header) { std::memcpy(&web_arg_header, web_arg.data(), sizeof(WebArgHeader)); @@ -407,6 +417,9 @@ void WebBrowser::InitializeShare() {} void WebBrowser::InitializeWeb() { external_url = ParseStringValue(GetInputTLVData(WebArgInputTLVType::InitialURL).value()); + + // Resolve Nintendo CDN URLs. + external_url = ResolveURL(external_url); } void WebBrowser::InitializeWifi() {} diff --git a/src/core/hle/service/am/applets/web_browser.h b/src/core/hle/service/am/applets/applet_web_browser.h index 9f81214b6..4f9e81b79 100644 --- a/src/core/hle/service/am/applets/web_browser.h +++ b/src/core/hle/service/am/applets/applet_web_browser.h @@ -11,8 +11,8 @@ #include "common/common_types.h" #include "core/file_sys/vfs_types.h" #include "core/hle/result.h" +#include "core/hle/service/am/applets/applet_web_browser_types.h" #include "core/hle/service/am/applets/applets.h" -#include "core/hle/service/am/applets/web_types.h" namespace Core { class System; diff --git a/src/core/hle/service/am/applets/web_types.h b/src/core/hle/service/am/applets/applet_web_browser_types.h index 419c2bf79..419c2bf79 100644 --- a/src/core/hle/service/am/applets/web_types.h +++ b/src/core/hle/service/am/applets/applet_web_browser_types.h diff --git a/src/core/hle/service/am/applets/applets.cpp b/src/core/hle/service/am/applets/applets.cpp index ae995df6b..2b7685d42 100644 --- a/src/core/hle/service/am/applets/applets.cpp +++ b/src/core/hle/service/am/applets/applets.cpp @@ -17,13 +17,13 @@ #include "core/hle/service/am/am.h" #include "core/hle/service/am/applet_ae.h" #include "core/hle/service/am/applet_oe.h" +#include "core/hle/service/am/applets/applet_controller.h" +#include "core/hle/service/am/applets/applet_error.h" +#include "core/hle/service/am/applets/applet_general_backend.h" +#include "core/hle/service/am/applets/applet_profile_select.h" +#include "core/hle/service/am/applets/applet_software_keyboard.h" +#include "core/hle/service/am/applets/applet_web_browser.h" #include "core/hle/service/am/applets/applets.h" -#include "core/hle/service/am/applets/controller.h" -#include "core/hle/service/am/applets/error.h" -#include "core/hle/service/am/applets/general_backend.h" -#include "core/hle/service/am/applets/profile_select.h" -#include "core/hle/service/am/applets/software_keyboard.h" -#include "core/hle/service/am/applets/web_browser.h" #include "core/hle/service/sm/sm.h" namespace Service::AM::Applets { diff --git a/src/core/hle/service/aoc/aoc_u.cpp b/src/core/hle/service/aoc/aoc_u.cpp index fec704c65..dd945e058 100644 --- a/src/core/hle/service/aoc/aoc_u.cpp +++ b/src/core/hle/service/aoc/aoc_u.cpp @@ -117,7 +117,7 @@ AOC_U::AOC_U(Core::System& system_) {7, &AOC_U::PrepareAddOnContent, "PrepareAddOnContent"}, {8, &AOC_U::GetAddOnContentListChangedEvent, "GetAddOnContentListChangedEvent"}, {9, nullptr, "GetAddOnContentLostErrorCode"}, - {10, nullptr, "GetAddOnContentListChangedEventWithProcessId"}, + {10, &AOC_U::GetAddOnContentListChangedEventWithProcessId, "GetAddOnContentListChangedEventWithProcessId"}, {100, &AOC_U::CreateEcPurchasedEventManager, "CreateEcPurchasedEventManager"}, {101, &AOC_U::CreatePermanentEcPurchasedEventManager, "CreatePermanentEcPurchasedEventManager"}, {110, nullptr, "CreateContentsServiceManager"}, @@ -257,6 +257,14 @@ void AOC_U::GetAddOnContentListChangedEvent(Kernel::HLERequestContext& ctx) { rb.PushCopyObjects(aoc_change_event.GetReadableEvent()); } +void AOC_U::GetAddOnContentListChangedEventWithProcessId(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_AOC, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 2, 1}; + rb.Push(ResultSuccess); + rb.PushCopyObjects(aoc_change_event.GetReadableEvent()); +} + void AOC_U::CreateEcPurchasedEventManager(Kernel::HLERequestContext& ctx) { LOG_WARNING(Service_AOC, "(STUBBED) called"); diff --git a/src/core/hle/service/aoc/aoc_u.h b/src/core/hle/service/aoc/aoc_u.h index 65095baa2..bb6ffb8eb 100644 --- a/src/core/hle/service/aoc/aoc_u.h +++ b/src/core/hle/service/aoc/aoc_u.h @@ -28,6 +28,7 @@ private: void GetAddOnContentBaseId(Kernel::HLERequestContext& ctx); void PrepareAddOnContent(Kernel::HLERequestContext& ctx); void GetAddOnContentListChangedEvent(Kernel::HLERequestContext& ctx); + void GetAddOnContentListChangedEventWithProcessId(Kernel::HLERequestContext& ctx); void CreateEcPurchasedEventManager(Kernel::HLERequestContext& ctx); void CreatePermanentEcPurchasedEventManager(Kernel::HLERequestContext& ctx); diff --git a/src/core/hle/service/apm/apm.cpp b/src/core/hle/service/apm/apm.cpp index 97d6619dd..f5ebfe8d6 100644 --- a/src/core/hle/service/apm/apm.cpp +++ b/src/core/hle/service/apm/apm.cpp @@ -5,7 +5,7 @@ #include "core/core.h" #include "core/hle/ipc_helpers.h" #include "core/hle/service/apm/apm.h" -#include "core/hle/service/apm/interface.h" +#include "core/hle/service/apm/apm_interface.h" namespace Service::APM { diff --git a/src/core/hle/service/apm/controller.cpp b/src/core/hle/service/apm/apm_controller.cpp index 8bfa7c0e4..98839fe97 100644 --- a/src/core/hle/service/apm/controller.cpp +++ b/src/core/hle/service/apm/apm_controller.cpp @@ -9,7 +9,7 @@ #include "common/logging/log.h" #include "common/settings.h" #include "core/core_timing.h" -#include "core/hle/service/apm/controller.h" +#include "core/hle/service/apm/apm_controller.h" namespace Service::APM { diff --git a/src/core/hle/service/apm/controller.h b/src/core/hle/service/apm/apm_controller.h index 8d48e0104..8d48e0104 100644 --- a/src/core/hle/service/apm/controller.h +++ b/src/core/hle/service/apm/apm_controller.h diff --git a/src/core/hle/service/apm/interface.cpp b/src/core/hle/service/apm/apm_interface.cpp index d69ddd135..e58bad083 100644 --- a/src/core/hle/service/apm/interface.cpp +++ b/src/core/hle/service/apm/apm_interface.cpp @@ -5,8 +5,8 @@ #include "common/logging/log.h" #include "core/hle/ipc_helpers.h" #include "core/hle/service/apm/apm.h" -#include "core/hle/service/apm/controller.h" -#include "core/hle/service/apm/interface.h" +#include "core/hle/service/apm/apm_controller.h" +#include "core/hle/service/apm/apm_interface.h" namespace Service::APM { diff --git a/src/core/hle/service/apm/interface.h b/src/core/hle/service/apm/apm_interface.h index 063ad5308..063ad5308 100644 --- a/src/core/hle/service/apm/interface.h +++ b/src/core/hle/service/apm/apm_interface.h diff --git a/src/core/hle/service/audio/audren_u.cpp b/src/core/hle/service/audio/audren_u.cpp index 800feba6e..b769fe959 100644 --- a/src/core/hle/service/audio/audren_u.cpp +++ b/src/core/hle/service/audio/audren_u.cpp @@ -96,7 +96,7 @@ private: void RequestUpdateImpl(Kernel::HLERequestContext& ctx) { LOG_DEBUG(Service_Audio, "(STUBBED) called"); - std::vector<u8> output_params(ctx.GetWriteBufferSize()); + std::vector<u8> output_params(ctx.GetWriteBufferSize(), 0); auto result = renderer->UpdateAudioRenderer(ctx.ReadBuffer(), output_params); if (result.IsSuccess()) { @@ -110,17 +110,19 @@ private: void Start(Kernel::HLERequestContext& ctx) { LOG_WARNING(Service_Audio, "(STUBBED) called"); - IPC::ResponseBuilder rb{ctx, 2}; + const auto result = renderer->Start(); - rb.Push(ResultSuccess); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); } void Stop(Kernel::HLERequestContext& ctx) { LOG_WARNING(Service_Audio, "(STUBBED) called"); - IPC::ResponseBuilder rb{ctx, 2}; + const auto result = renderer->Stop(); - rb.Push(ResultSuccess); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); } void QuerySystemEvent(Kernel::HLERequestContext& ctx) { @@ -288,7 +290,7 @@ private: IPC::ResponseBuilder rb{ctx, 3}; rb.Push(ResultSuccess); - rb.Push<u32>(1); + rb.Push<u32>(2); } // Should be similar to QueryAudioDeviceOutputEvent diff --git a/src/core/hle/service/audio/hwopus.cpp b/src/core/hle/service/audio/hwopus.cpp index 10e6f7a64..33a6dbbb6 100644 --- a/src/core/hle/service/audio/hwopus.cpp +++ b/src/core/hle/service/audio/hwopus.cpp @@ -253,7 +253,11 @@ void HwOpus::GetWorkBufferSize(Kernel::HLERequestContext& ctx) { rb.Push<u32>(worker_buffer_sz); } -void HwOpus::OpenOpusDecoder(Kernel::HLERequestContext& ctx) { +void HwOpus::GetWorkBufferSizeEx(Kernel::HLERequestContext& ctx) { + GetWorkBufferSize(ctx); +} + +void HwOpus::OpenHardwareOpusDecoder(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const auto sample_rate = rp.Pop<u32>(); const auto channel_count = rp.Pop<u32>(); @@ -291,14 +295,47 @@ void HwOpus::OpenOpusDecoder(Kernel::HLERequestContext& ctx) { system, OpusDecoderState{std::move(decoder), sample_rate, channel_count}); } +void HwOpus::OpenHardwareOpusDecoderEx(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto sample_rate = rp.Pop<u32>(); + const auto channel_count = rp.Pop<u32>(); + + LOG_CRITICAL(Audio, "called sample_rate={}, channel_count={}", sample_rate, channel_count); + + ASSERT_MSG(sample_rate == 48000 || sample_rate == 24000 || sample_rate == 16000 || + sample_rate == 12000 || sample_rate == 8000, + "Invalid sample rate"); + ASSERT_MSG(channel_count == 1 || channel_count == 2, "Invalid channel count"); + + const int num_stereo_streams = channel_count == 2 ? 1 : 0; + const auto mapping_table = CreateMappingTable(channel_count); + + int error = 0; + OpusDecoderPtr decoder{ + opus_multistream_decoder_create(sample_rate, static_cast<int>(channel_count), 1, + num_stereo_streams, mapping_table.data(), &error)}; + if (error != OPUS_OK || decoder == nullptr) { + LOG_ERROR(Audio, "Failed to create Opus decoder (error={}).", error); + IPC::ResponseBuilder rb{ctx, 2}; + // TODO(ogniK): Use correct error code + rb.Push(ResultUnknown); + return; + } + + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(ResultSuccess); + rb.PushIpcInterface<IHardwareOpusDecoderManager>( + system, OpusDecoderState{std::move(decoder), sample_rate, channel_count}); +} + HwOpus::HwOpus(Core::System& system_) : ServiceFramework{system_, "hwopus"} { static const FunctionInfo functions[] = { - {0, &HwOpus::OpenOpusDecoder, "OpenOpusDecoder"}, + {0, &HwOpus::OpenHardwareOpusDecoder, "OpenHardwareOpusDecoder"}, {1, &HwOpus::GetWorkBufferSize, "GetWorkBufferSize"}, {2, nullptr, "OpenOpusDecoderForMultiStream"}, {3, nullptr, "GetWorkBufferSizeForMultiStream"}, - {4, nullptr, "OpenHardwareOpusDecoderEx"}, - {5, nullptr, "GetWorkBufferSizeEx"}, + {4, &HwOpus::OpenHardwareOpusDecoderEx, "OpenHardwareOpusDecoderEx"}, + {5, &HwOpus::GetWorkBufferSizeEx, "GetWorkBufferSizeEx"}, {6, nullptr, "OpenHardwareOpusDecoderForMultiStreamEx"}, {7, nullptr, "GetWorkBufferSizeForMultiStreamEx"}, }; diff --git a/src/core/hle/service/audio/hwopus.h b/src/core/hle/service/audio/hwopus.h index 4f921f18e..b74824ff3 100644 --- a/src/core/hle/service/audio/hwopus.h +++ b/src/core/hle/service/audio/hwopus.h @@ -18,8 +18,10 @@ public: ~HwOpus() override; private: - void OpenOpusDecoder(Kernel::HLERequestContext& ctx); + void OpenHardwareOpusDecoder(Kernel::HLERequestContext& ctx); + void OpenHardwareOpusDecoderEx(Kernel::HLERequestContext& ctx); void GetWorkBufferSize(Kernel::HLERequestContext& ctx); + void GetWorkBufferSizeEx(Kernel::HLERequestContext& ctx); }; } // namespace Service::Audio diff --git a/src/core/hle/service/bcat/backend/boxcat.cpp b/src/core/hle/service/bcat/backend/boxcat.cpp index dc15cf58b..7ca7f2aac 100644 --- a/src/core/hle/service/bcat/backend/boxcat.cpp +++ b/src/core/hle/service/bcat/backend/boxcat.cpp @@ -7,6 +7,9 @@ #ifdef __GNUC__ #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wshadow" +#ifndef __clang__ +#pragma GCC diagnostic ignored "-Wmaybe-uninitialized" +#endif #endif #include <httplib.h> #include <mbedtls/sha256.h> diff --git a/src/core/hle/service/bcat/bcat.h b/src/core/hle/service/bcat/bcat.h index d72798980..1eba477da 100644 --- a/src/core/hle/service/bcat/bcat.h +++ b/src/core/hle/service/bcat/bcat.h @@ -4,7 +4,7 @@ #pragma once -#include "core/hle/service/bcat/module.h" +#include "core/hle/service/bcat/bcat_module.h" namespace Core { class System; diff --git a/src/core/hle/service/bcat/module.cpp b/src/core/hle/service/bcat/bcat_module.cpp index 44e4d0509..72294eb2e 100644 --- a/src/core/hle/service/bcat/module.cpp +++ b/src/core/hle/service/bcat/bcat_module.cpp @@ -17,7 +17,7 @@ #include "core/hle/kernel/k_writable_event.h" #include "core/hle/service/bcat/backend/backend.h" #include "core/hle/service/bcat/bcat.h" -#include "core/hle/service/bcat/module.h" +#include "core/hle/service/bcat/bcat_module.h" #include "core/hle/service/filesystem/filesystem.h" namespace Service::BCAT { @@ -579,7 +579,7 @@ void Module::Interface::CreateDeliveryCacheStorageServiceWithApplicationId( std::unique_ptr<Backend> CreateBackendFromSettings([[maybe_unused]] Core::System& system, DirectoryGetter getter) { #ifdef YUZU_ENABLE_BOXCAT - if (Settings::values.bcat_backend == "boxcat") { + if (Settings::values.bcat_backend.GetValue() == "boxcat") { return std::make_unique<Boxcat>(system.GetAppletManager(), std::move(getter)); } #endif diff --git a/src/core/hle/service/bcat/module.h b/src/core/hle/service/bcat/bcat_module.h index 738731c06..738731c06 100644 --- a/src/core/hle/service/bcat/module.h +++ b/src/core/hle/service/bcat/bcat_module.h 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) return bis_factory->GetModificationLoadRoot(title_id); } +FileSys::VirtualDir FileSystemController::GetSDMCModificationLoadRoot(u64 title_id) const { + LOG_TRACE(Service_FS, "Opening SDMC mod load root for tid={:016X}", title_id); + + if (sdmc_factory == nullptr) { + return nullptr; + } + + return sdmc_factory->GetSDMCModificationLoadRoot(title_id); +} + FileSys::VirtualDir FileSystemController::GetModificationDumpRoot(u64 title_id) const { LOG_TRACE(Service_FS, "Opening mod dump root for tid={:016X}", title_id); @@ -733,20 +743,23 @@ void FileSystemController::CreateFactories(FileSys::VfsFilesystem& vfs, bool ove } using YuzuPath = Common::FS::YuzuPath; + const auto sdmc_dir_path = Common::FS::GetYuzuPath(YuzuPath::SDMCDir); + const auto sdmc_load_dir_path = sdmc_dir_path / "atmosphere/contents"; const auto rw_mode = FileSys::Mode::ReadWrite; auto nand_directory = vfs.OpenDirectory(Common::FS::GetYuzuPathString(YuzuPath::NANDDir), rw_mode); - auto sd_directory = - vfs.OpenDirectory(Common::FS::GetYuzuPathString(YuzuPath::SDMCDir), rw_mode); + auto sd_directory = vfs.OpenDirectory(Common::FS::PathToUTF8String(sdmc_dir_path), rw_mode); auto load_directory = vfs.OpenDirectory(Common::FS::GetYuzuPathString(YuzuPath::LoadDir), FileSys::Mode::Read); + auto sd_load_directory = + vfs.OpenDirectory(Common::FS::PathToUTF8String(sdmc_load_dir_path), FileSys::Mode::Read); auto dump_directory = vfs.OpenDirectory(Common::FS::GetYuzuPathString(YuzuPath::DumpDir), rw_mode); if (bis_factory == nullptr) { - bis_factory = - std::make_unique<FileSys::BISFactory>(nand_directory, load_directory, dump_directory); + bis_factory = std::make_unique<FileSys::BISFactory>( + nand_directory, std::move(load_directory), std::move(dump_directory)); system.RegisterContentProvider(FileSys::ContentProviderUnionSlot::SysNAND, bis_factory->GetSystemNANDContents()); system.RegisterContentProvider(FileSys::ContentProviderUnionSlot::UserNAND, @@ -759,7 +772,8 @@ void FileSystemController::CreateFactories(FileSys::VfsFilesystem& vfs, bool ove } if (sdmc_factory == nullptr) { - sdmc_factory = std::make_unique<FileSys::SDMCFactory>(std::move(sd_directory)); + sdmc_factory = std::make_unique<FileSys::SDMCFactory>(std::move(sd_directory), + std::move(sd_load_directory)); system.RegisterContentProvider(FileSys::ContentProviderUnionSlot::SDMC, sdmc_factory->GetSDMCContents()); } 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: FileSys::VirtualDir GetContentDirectory(ContentStorageId id) const; FileSys::VirtualDir GetImageDirectory(ImageDirectoryId id) const; + FileSys::VirtualDir GetSDMCModificationLoadRoot(u64 title_id) const; FileSys::VirtualDir GetModificationLoadRoot(u64 title_id) const; FileSys::VirtualDir GetModificationDumpRoot(u64 title_id) const; diff --git a/src/core/hle/service/friend/friend.cpp b/src/core/hle/service/friend/friend.cpp index c5f88bce7..a3c939c0c 100644 --- a/src/core/hle/service/friend/friend.cpp +++ b/src/core/hle/service/friend/friend.cpp @@ -12,7 +12,7 @@ #include "core/hle/kernel/k_writable_event.h" #include "core/hle/service/friend/errors.h" #include "core/hle/service/friend/friend.h" -#include "core/hle/service/friend/interface.h" +#include "core/hle/service/friend/friend_interface.h" namespace Service::Friend { diff --git a/src/core/hle/service/friend/interface.cpp b/src/core/hle/service/friend/friend_interface.cpp index 7368ccec2..9b18b2a32 100644 --- a/src/core/hle/service/friend/interface.cpp +++ b/src/core/hle/service/friend/friend_interface.cpp @@ -2,7 +2,7 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. -#include "core/hle/service/friend/interface.h" +#include "core/hle/service/friend/friend_interface.h" namespace Service::Friend { diff --git a/src/core/hle/service/friend/interface.h b/src/core/hle/service/friend/friend_interface.h index 43d914b32..43d914b32 100644 --- a/src/core/hle/service/friend/interface.h +++ b/src/core/hle/service/friend/friend_interface.h diff --git a/src/core/hle/service/glue/arp.cpp b/src/core/hle/service/glue/arp.cpp index ca25df67e..5a3b54cc1 100644 --- a/src/core/hle/service/glue/arp.cpp +++ b/src/core/hle/service/glue/arp.cpp @@ -13,7 +13,7 @@ #include "core/hle/kernel/kernel.h" #include "core/hle/service/glue/arp.h" #include "core/hle/service/glue/errors.h" -#include "core/hle/service/glue/manager.h" +#include "core/hle/service/glue/glue_manager.h" #include "core/hle/service/service.h" namespace Service::Glue { diff --git a/src/core/hle/service/glue/manager.cpp b/src/core/hle/service/glue/glue_manager.cpp index 9b1754cf8..aa9d48c0c 100644 --- a/src/core/hle/service/glue/manager.cpp +++ b/src/core/hle/service/glue/glue_manager.cpp @@ -3,7 +3,7 @@ // Refer to the license.txt file included. #include "core/hle/service/glue/errors.h" -#include "core/hle/service/glue/manager.h" +#include "core/hle/service/glue/glue_manager.h" namespace Service::Glue { diff --git a/src/core/hle/service/glue/manager.h b/src/core/hle/service/glue/glue_manager.h index a7f5ce3ee..a7f5ce3ee 100644 --- a/src/core/hle/service/glue/manager.h +++ b/src/core/hle/service/glue/glue_manager.h diff --git a/src/core/hle/service/hid/controllers/npad.cpp b/src/core/hle/service/hid/controllers/npad.cpp index 1eb02aee2..6ce1360e3 100644 --- a/src/core/hle/service/hid/controllers/npad.cpp +++ b/src/core/hle/service/hid/controllers/npad.cpp @@ -941,6 +941,11 @@ void Controller_NPad::InitializeVibrationDevice(const DeviceHandle& vibration_de void Controller_NPad::InitializeVibrationDeviceAtIndex(std::size_t npad_index, std::size_t device_index) { + if (!Settings::values.vibration_enabled.GetValue()) { + vibration_devices_mounted[npad_index][device_index] = false; + return; + } + if (vibrations[npad_index][device_index]) { vibration_devices_mounted[npad_index][device_index] = vibrations[npad_index][device_index]->GetStatus() == 1; diff --git a/src/core/hle/service/mii/mii.cpp b/src/core/hle/service/mii/mii.cpp index bbd81a88a..9d863486a 100644 --- a/src/core/hle/service/mii/mii.cpp +++ b/src/core/hle/service/mii/mii.cpp @@ -7,8 +7,8 @@ #include "common/logging/log.h" #include "core/hle/ipc_helpers.h" #include "core/hle/kernel/hle_ipc.h" -#include "core/hle/service/mii/manager.h" #include "core/hle/service/mii/mii.h" +#include "core/hle/service/mii/mii_manager.h" #include "core/hle/service/service.h" #include "core/hle/service/sm/sm.h" diff --git a/src/core/hle/service/mii/manager.cpp b/src/core/hle/service/mii/mii_manager.cpp index 114aff31c..4fef2aea4 100644 --- a/src/core/hle/service/mii/manager.cpp +++ b/src/core/hle/service/mii/mii_manager.cpp @@ -10,7 +10,7 @@ #include "common/string_util.h" #include "core/hle/service/acc/profile_manager.h" -#include "core/hle/service/mii/manager.h" +#include "core/hle/service/mii/mii_manager.h" #include "core/hle/service/mii/raw_data.h" #include "core/hle/service/mii/types.h" @@ -20,6 +20,7 @@ namespace { constexpr ResultCode ERROR_CANNOT_FIND_ENTRY{ErrorModule::Mii, 4}; +constexpr std::size_t BaseMiiCount{2}; constexpr std::size_t DefaultMiiCount{RawData::DefaultMii.size()}; constexpr MiiStoreData::Name DefaultMiiName{u'y', u'u', u'z', u'u'}; @@ -415,7 +416,7 @@ u32 MiiManager::GetCount(SourceFlag source_flag) const { count += 0; } if ((source_flag & SourceFlag::Default) != SourceFlag::None) { - count += DefaultMiiCount; + count += (DefaultMiiCount - BaseMiiCount); } return static_cast<u32>(count); } @@ -445,7 +446,7 @@ ResultVal<std::vector<MiiInfoElement>> MiiManager::GetDefault(SourceFlag source_ return MakeResult(std::move(result)); } - for (std::size_t index = 0; index < DefaultMiiCount; index++) { + for (std::size_t index = BaseMiiCount; index < DefaultMiiCount; index++) { result.emplace_back(BuildDefault(index), Source::Default); } diff --git a/src/core/hle/service/mii/manager.h b/src/core/hle/service/mii/mii_manager.h index 8e048fc56..8e048fc56 100644 --- a/src/core/hle/service/mii/manager.h +++ b/src/core/hle/service/mii/mii_manager.h diff --git a/src/core/hle/service/mii/raw_data.h b/src/core/hle/service/mii/raw_data.h index 0e35d69d2..a0d2b9d3a 100644 --- a/src/core/hle/service/mii/raw_data.h +++ b/src/core/hle/service/mii/raw_data.h @@ -7,7 +7,7 @@ #include <array> #include "common/common_types.h" -#include "core/hle/service/mii/manager.h" +#include "core/hle/service/mii/mii_manager.h" namespace Service::Mii::RawData { diff --git a/src/core/hle/service/nifm/nifm.cpp b/src/core/hle/service/nifm/nifm.cpp index f03b2666a..e742db48f 100644 --- a/src/core/hle/service/nifm/nifm.cpp +++ b/src/core/hle/service/nifm/nifm.cpp @@ -179,7 +179,7 @@ private: IPC::ResponseBuilder rb{ctx, 3}; rb.Push(ResultSuccess); - if (Settings::values.bcat_backend == "none") { + if (Settings::values.bcat_backend.GetValue() == "none") { rb.PushEnum(RequestState::NotSubmitted); } else { rb.PushEnum(RequestState::Connected); @@ -384,7 +384,7 @@ private: IPC::ResponseBuilder rb{ctx, 3}; rb.Push(ResultSuccess); - if (Settings::values.bcat_backend == "none") { + if (Settings::values.bcat_backend.GetValue() == "none") { rb.Push<u8>(0); } else { rb.Push<u8>(1); @@ -395,7 +395,7 @@ private: IPC::ResponseBuilder rb{ctx, 3}; rb.Push(ResultSuccess); - if (Settings::values.bcat_backend == "none") { + if (Settings::values.bcat_backend.GetValue() == "none") { rb.Push<u8>(0); } else { rb.Push<u8>(1); diff --git a/src/core/hle/service/nvdrv/nvdrv.cpp b/src/core/hle/service/nvdrv/nvdrv.cpp index 74796dce1..03992af5e 100644 --- a/src/core/hle/service/nvdrv/nvdrv.cpp +++ b/src/core/hle/service/nvdrv/nvdrv.cpp @@ -20,8 +20,8 @@ #include "core/hle/service/nvdrv/devices/nvhost_nvjpg.h" #include "core/hle/service/nvdrv/devices/nvhost_vic.h" #include "core/hle/service/nvdrv/devices/nvmap.h" -#include "core/hle/service/nvdrv/interface.h" #include "core/hle/service/nvdrv/nvdrv.h" +#include "core/hle/service/nvdrv/nvdrv_interface.h" #include "core/hle/service/nvdrv/nvmemp.h" #include "core/hle/service/nvdrv/syncpoint_manager.h" #include "core/hle/service/nvflinger/nvflinger.h" diff --git a/src/core/hle/service/nvdrv/interface.cpp b/src/core/hle/service/nvdrv/nvdrv_interface.cpp index e4d495000..d61fb73dc 100644 --- a/src/core/hle/service/nvdrv/interface.cpp +++ b/src/core/hle/service/nvdrv/nvdrv_interface.cpp @@ -10,9 +10,9 @@ #include "core/hle/kernel/k_thread.h" #include "core/hle/kernel/k_writable_event.h" #include "core/hle/kernel/kernel.h" -#include "core/hle/service/nvdrv/interface.h" #include "core/hle/service/nvdrv/nvdata.h" #include "core/hle/service/nvdrv/nvdrv.h" +#include "core/hle/service/nvdrv/nvdrv_interface.h" namespace Service::Nvidia { diff --git a/src/core/hle/service/nvdrv/interface.h b/src/core/hle/service/nvdrv/nvdrv_interface.h index 0e764c53f..0e764c53f 100644 --- a/src/core/hle/service/nvdrv/interface.h +++ b/src/core/hle/service/nvdrv/nvdrv_interface.h diff --git a/src/core/hle/service/pctl/pctl.h b/src/core/hle/service/pctl/pctl.h index ea3b97823..1d28900b2 100644 --- a/src/core/hle/service/pctl/pctl.h +++ b/src/core/hle/service/pctl/pctl.h @@ -4,7 +4,7 @@ #pragma once -#include "core/hle/service/pctl/module.h" +#include "core/hle/service/pctl/pctl_module.h" namespace Core { class System; diff --git a/src/core/hle/service/pctl/module.cpp b/src/core/hle/service/pctl/pctl_module.cpp index 1e31d05a6..6949fcf3b 100644 --- a/src/core/hle/service/pctl/module.cpp +++ b/src/core/hle/service/pctl/pctl_module.cpp @@ -8,8 +8,8 @@ #include "core/file_sys/patch_manager.h" #include "core/hle/ipc_helpers.h" #include "core/hle/kernel/k_process.h" -#include "core/hle/service/pctl/module.h" #include "core/hle/service/pctl/pctl.h" +#include "core/hle/service/pctl/pctl_module.h" namespace Service::PCTL { diff --git a/src/core/hle/service/pctl/module.h b/src/core/hle/service/pctl/pctl_module.h index f25c5c557..f25c5c557 100644 --- a/src/core/hle/service/pctl/module.h +++ b/src/core/hle/service/pctl/pctl_module.h diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp index 663b83cd3..e6fba88b2 100644 --- a/src/core/hle/service/service.cpp +++ b/src/core/hle/service/service.cpp @@ -21,7 +21,7 @@ #include "core/hle/service/aoc/aoc_u.h" #include "core/hle/service/apm/apm.h" #include "core/hle/service/audio/audio.h" -#include "core/hle/service/bcat/module.h" +#include "core/hle/service/bcat/bcat_module.h" #include "core/hle/service/bpc/bpc.h" #include "core/hle/service/btdrv/btdrv.h" #include "core/hle/service/btm/btm.h" @@ -54,7 +54,7 @@ #include "core/hle/service/nvflinger/nvflinger.h" #include "core/hle/service/olsc/olsc.h" #include "core/hle/service/pcie/pcie.h" -#include "core/hle/service/pctl/module.h" +#include "core/hle/service/pctl/pctl_module.h" #include "core/hle/service/pcv/pcv.h" #include "core/hle/service/pm/pm.h" #include "core/hle/service/prepo/prepo.h" @@ -64,7 +64,7 @@ #include "core/hle/service/set/settings.h" #include "core/hle/service/sm/sm.h" #include "core/hle/service/sockets/sockets.h" -#include "core/hle/service/spl/module.h" +#include "core/hle/service/spl/spl_module.h" #include "core/hle/service/ssl/ssl.h" #include "core/hle/service/time/time.h" #include "core/hle/service/usb/usb.h" diff --git a/src/core/hle/service/set/set.cpp b/src/core/hle/service/set/set.cpp index ece2a74c6..522a604a5 100644 --- a/src/core/hle/service/set/set.cpp +++ b/src/core/hle/service/set/set.cpp @@ -160,7 +160,7 @@ void SET::GetQuestFlag(Kernel::HLERequestContext& ctx) { IPC::ResponseBuilder rb{ctx, 3}; rb.Push(ResultSuccess); - rb.Push(static_cast<u32>(Settings::values.quest_flag)); + rb.Push(static_cast<u32>(Settings::values.quest_flag.GetValue())); } void SET::GetLanguageCode(Kernel::HLERequestContext& ctx) { diff --git a/src/core/hle/service/sm/sm.cpp b/src/core/hle/service/sm/sm.cpp index c7828c3bd..15034abed 100644 --- a/src/core/hle/service/sm/sm.cpp +++ b/src/core/hle/service/sm/sm.cpp @@ -14,8 +14,8 @@ #include "core/hle/kernel/k_server_session.h" #include "core/hle/kernel/k_session.h" #include "core/hle/result.h" -#include "core/hle/service/sm/controller.h" #include "core/hle/service/sm/sm.h" +#include "core/hle/service/sm/sm_controller.h" namespace Service::SM { diff --git a/src/core/hle/service/sm/controller.cpp b/src/core/hle/service/sm/sm_controller.cpp index 8b9418e0f..b5fbc4569 100644 --- a/src/core/hle/service/sm/controller.cpp +++ b/src/core/hle/service/sm/sm_controller.cpp @@ -13,7 +13,7 @@ #include "core/hle/kernel/k_server_port.h" #include "core/hle/kernel/k_server_session.h" #include "core/hle/kernel/k_session.h" -#include "core/hle/service/sm/controller.h" +#include "core/hle/service/sm/sm_controller.h" namespace Service::SM { diff --git a/src/core/hle/service/sm/controller.h b/src/core/hle/service/sm/sm_controller.h index 7494f898d..7494f898d 100644 --- a/src/core/hle/service/sm/controller.h +++ b/src/core/hle/service/sm/sm_controller.h diff --git a/src/core/hle/service/spl/csrng.h b/src/core/hle/service/spl/csrng.h index 5c0bd2199..0d03cc6cb 100644 --- a/src/core/hle/service/spl/csrng.h +++ b/src/core/hle/service/spl/csrng.h @@ -4,7 +4,7 @@ #pragma once -#include "core/hle/service/spl/module.h" +#include "core/hle/service/spl/spl_module.h" namespace Core { class System; diff --git a/src/core/hle/service/spl/spl.h b/src/core/hle/service/spl/spl.h index 9b35012ed..5599c0c01 100644 --- a/src/core/hle/service/spl/spl.h +++ b/src/core/hle/service/spl/spl.h @@ -4,7 +4,7 @@ #pragma once -#include "core/hle/service/spl/module.h" +#include "core/hle/service/spl/spl_module.h" namespace Core { class System; diff --git a/src/core/hle/service/spl/module.cpp b/src/core/hle/service/spl/spl_module.cpp index ebb179aa8..918633af5 100644 --- a/src/core/hle/service/spl/module.cpp +++ b/src/core/hle/service/spl/spl_module.cpp @@ -13,8 +13,8 @@ #include "core/hle/api_version.h" #include "core/hle/ipc_helpers.h" #include "core/hle/service/spl/csrng.h" -#include "core/hle/service/spl/module.h" #include "core/hle/service/spl/spl.h" +#include "core/hle/service/spl/spl_module.h" namespace Service::SPL { diff --git a/src/core/hle/service/spl/module.h b/src/core/hle/service/spl/spl_module.h index 61630df80..61630df80 100644 --- a/src/core/hle/service/spl/module.h +++ b/src/core/hle/service/spl/spl_module.h diff --git a/src/core/hle/service/time/time.cpp b/src/core/hle/service/time/time.cpp index d6f710eba..8fdd5076f 100644 --- a/src/core/hle/service/time/time.cpp +++ b/src/core/hle/service/time/time.cpp @@ -11,8 +11,8 @@ #include "core/hle/kernel/k_client_port.h" #include "core/hle/kernel/k_scheduler.h" #include "core/hle/kernel/kernel.h" -#include "core/hle/service/time/interface.h" #include "core/hle/service/time/time.h" +#include "core/hle/service/time/time_interface.h" #include "core/hle/service/time/time_sharedmemory.h" #include "core/hle/service/time/time_zone_service.h" diff --git a/src/core/hle/service/time/interface.cpp b/src/core/hle/service/time/time_interface.cpp index 53a204796..bb7b6b5c1 100644 --- a/src/core/hle/service/time/interface.cpp +++ b/src/core/hle/service/time/time_interface.cpp @@ -2,7 +2,7 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. -#include "core/hle/service/time/interface.h" +#include "core/hle/service/time/time_interface.h" namespace Service::Time { diff --git a/src/core/hle/service/time/interface.h b/src/core/hle/service/time/time_interface.h index c41766f1a..c41766f1a 100644 --- a/src/core/hle/service/time/interface.h +++ b/src/core/hle/service/time/time_interface.h diff --git a/src/core/loader/nro.cpp b/src/core/loader/nro.cpp index 618555202..951ea966e 100644 --- a/src/core/loader/nro.cpp +++ b/src/core/loader/nro.cpp @@ -155,8 +155,8 @@ static bool LoadNroImpl(Kernel::KProcess& process, const std::vector<u8>& data) codeset.segments[i].size = PageAlignSize(nro_header.segments[i].size); } - if (!Settings::values.program_args.empty()) { - const auto arg_data = Settings::values.program_args; + if (!Settings::values.program_args.GetValue().empty()) { + const auto arg_data = Settings::values.program_args.GetValue(); codeset.DataSegment().size += NSO_ARGUMENT_DATA_ALLOCATION_SIZE; NSOArgumentHeader args_header{ NSO_ARGUMENT_DATA_ALLOCATION_SIZE, static_cast<u32_le>(arg_data.size()), {}}; diff --git a/src/core/loader/nso.cpp b/src/core/loader/nso.cpp index 0f5cfda68..4a2224c02 100644 --- a/src/core/loader/nso.cpp +++ b/src/core/loader/nso.cpp @@ -104,8 +104,8 @@ std::optional<VAddr> AppLoader_NSO::LoadModule(Kernel::KProcess& process, Core:: codeset.segments[i].size = nso_header.segments[i].size; } - if (should_pass_arguments && !Settings::values.program_args.empty()) { - const auto arg_data{Settings::values.program_args}; + if (should_pass_arguments && !Settings::values.program_args.GetValue().empty()) { + const auto arg_data{Settings::values.program_args.GetValue()}; codeset.DataSegment().size += NSO_ARGUMENT_DATA_ALLOCATION_SIZE; NSOArgumentHeader args_header{ diff --git a/src/core/reporter.cpp b/src/core/reporter.cpp index 82b0f535a..cfaf50105 100644 --- a/src/core/reporter.cpp +++ b/src/core/reporter.cpp @@ -397,7 +397,7 @@ void Reporter::ClearFSAccessLog() const { } bool Reporter::IsReportingEnabled() const { - return Settings::values.reporting_services; + return Settings::values.reporting_services.GetValue(); } } // namespace Core diff --git a/src/core/telemetry_session.cpp b/src/core/telemetry_session.cpp index d4c23ced2..066cb23e4 100644 --- a/src/core/telemetry_session.cpp +++ b/src/core/telemetry_session.cpp @@ -135,7 +135,7 @@ u64 RegenerateTelemetryId() { bool VerifyLogin(const std::string& username, const std::string& token) { #ifdef ENABLE_WEB_SERVICE - return WebService::VerifyLogin(Settings::values.web_api_url, username, token); + return WebService::VerifyLogin(Settings::values.web_api_url.GetValue(), username, token); #else return false; #endif @@ -152,7 +152,8 @@ TelemetrySession::~TelemetrySession() { #ifdef ENABLE_WEB_SERVICE auto backend = std::make_unique<WebService::TelemetryJson>( - Settings::values.web_api_url, Settings::values.yuzu_username, Settings::values.yuzu_token); + Settings::values.web_api_url.GetValue(), Settings::values.yuzu_username.GetValue(), + Settings::values.yuzu_token.GetValue()); #else auto backend = std::make_unique<Telemetry::NullVisitor>(); #endif @@ -212,7 +213,7 @@ void TelemetrySession::AddInitialInfo(Loader::AppLoader& app_loader, // Log user configuration information constexpr auto field_type = Telemetry::FieldType::UserConfig; - AddField(field_type, "Audio_SinkId", Settings::values.sink_id); + AddField(field_type, "Audio_SinkId", Settings::values.sink_id.GetValue()); AddField(field_type, "Audio_EnableAudioStretching", Settings::values.enable_audio_stretching.GetValue()); AddField(field_type, "Core_UseMultiCore", Settings::values.use_multi_core.GetValue()); @@ -242,7 +243,8 @@ void TelemetrySession::AddInitialInfo(Loader::AppLoader& app_loader, bool TelemetrySession::SubmitTestcase() { #ifdef ENABLE_WEB_SERVICE auto backend = std::make_unique<WebService::TelemetryJson>( - Settings::values.web_api_url, Settings::values.yuzu_username, Settings::values.yuzu_token); + Settings::values.web_api_url.GetValue(), Settings::values.yuzu_username.GetValue(), + Settings::values.yuzu_token.GetValue()); field_collection.Accept(*backend); return backend->SubmitTestcase(); #else diff --git a/src/input_common/CMakeLists.txt b/src/input_common/CMakeLists.txt index 7c5763f9c..c4283a952 100644 --- a/src/input_common/CMakeLists.txt +++ b/src/input_common/CMakeLists.txt @@ -34,28 +34,17 @@ if (MSVC) /W4 /WX - # 'expression' : signed/unsigned mismatch - /we4018 - # 'argument' : conversion from 'type1' to 'type2', possible loss of data (floating-point) - /we4244 - # 'conversion' : conversion from 'type1' to 'type2', signed/unsigned mismatch - /we4245 - # 'operator': conversion from 'type1:field_bits' to 'type2:field_bits', possible loss of data - /we4254 - # 'var' : conversion from 'size_t' to 'type', possible loss of data - /we4267 - # 'context' : truncation from 'type1' to 'type2' - /we4305 + /we4242 # 'identifier': conversion from 'type1' to 'type2', possible loss of data + /we4244 # 'conversion': conversion from 'type1' to 'type2', possible loss of data + /we4245 # 'conversion': conversion from 'type1' to 'type2', signed/unsigned mismatch + /we4254 # 'operator': conversion from 'type1:field_bits' to 'type2:field_bits', possible loss of data ) else() target_compile_options(input_common PRIVATE -Werror -Werror=conversion -Werror=ignored-qualifiers - -Werror=implicit-fallthrough - -Werror=reorder -Werror=shadow - -Werror=sign-compare $<$<CXX_COMPILER_ID:GNU>:-Werror=unused-but-set-parameter> $<$<CXX_COMPILER_ID:GNU>:-Werror=unused-but-set-variable> -Werror=unused-variable 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: down->SetCallback(callbacks); left->SetCallback(callbacks); right->SetCallback(callbacks); + modifier->SetCallback(callbacks); } bool IsAngleGreater(float old_angle, float new_angle) const { diff --git a/src/input_common/gcadapter/gc_adapter.cpp b/src/input_common/gcadapter/gc_adapter.cpp index 320f51ee6..a2f1bb67c 100644 --- a/src/input_common/gcadapter/gc_adapter.cpp +++ b/src/input_common/gcadapter/gc_adapter.cpp @@ -5,14 +5,7 @@ #include <chrono> #include <thread> -#ifdef _MSC_VER -#pragma warning(push) -#pragma warning(disable : 4200) // nonstandard extension used : zero-sized array in struct/union -#endif #include <libusb.h> -#ifdef _MSC_VER -#pragma warning(pop) -#endif #include "common/logging/log.h" #include "common/param_package.h" diff --git a/src/input_common/main.cpp b/src/input_common/main.cpp index 7399c3648..8de3d4520 100644 --- a/src/input_common/main.cpp +++ b/src/input_common/main.cpp @@ -294,8 +294,8 @@ void InputSubsystem::ReloadInputDevices() { impl->udp->ReloadSockets(); } -std::vector<std::unique_ptr<Polling::DevicePoller>> InputSubsystem::GetPollers( - Polling::DeviceType type) const { +std::vector<std::unique_ptr<Polling::DevicePoller>> InputSubsystem::GetPollers([ + [maybe_unused]] Polling::DeviceType type) const { #ifdef HAVE_SDL2 return impl->sdl->GetPollers(type); #else diff --git a/src/input_common/mouse/mouse_poller.cpp b/src/input_common/mouse/mouse_poller.cpp index 758f7af1f..45b3d7340 100644 --- a/src/input_common/mouse/mouse_poller.cpp +++ b/src/input_common/mouse/mouse_poller.cpp @@ -84,7 +84,8 @@ public: std::lock_guard lock{mutex}; const auto axis_value = static_cast<float>(mouse_input->GetMouseState(button).axis.at(axis)); - return axis_value * Settings::values.mouse_panning_sensitivity / (100.0f * range); + const float sensitivity = Settings::values.mouse_panning_sensitivity.GetValue(); + return axis_value * sensitivity / (100.0f * range); } std::pair<float, float> GetAnalog(u32 analog_axis_x, u32 analog_axis_y) const { diff --git a/src/input_common/touch_from_button.cpp b/src/input_common/touch_from_button.cpp index 5b24fd8bf..7878a56d7 100644 --- a/src/input_common/touch_from_button.cpp +++ b/src/input_common/touch_from_button.cpp @@ -13,7 +13,7 @@ class TouchFromButtonDevice final : public Input::TouchDevice { public: TouchFromButtonDevice() { const auto button_index = - static_cast<std::size_t>(Settings::values.touch_from_button_map_index); + static_cast<u64>(Settings::values.touch_from_button_map_index.GetValue()); const auto& buttons = Settings::values.touch_from_button_maps[button_index].buttons; for (const auto& config_entry : buttons) { diff --git a/src/input_common/udp/client.cpp b/src/input_common/udp/client.cpp index bc1dfab3d..9b0aec797 100644 --- a/src/input_common/udp/client.cpp +++ b/src/input_common/udp/client.cpp @@ -201,7 +201,7 @@ bool Client::DeviceConnected(std::size_t pad) const { void Client::ReloadSockets() { Reset(); - std::stringstream servers_ss(Settings::values.udp_input_servers); + std::stringstream servers_ss(static_cast<std::string>(Settings::values.udp_input_servers)); std::string server_token; std::size_t client = 0; while (std::getline(servers_ss, server_token, ',')) { @@ -370,7 +370,7 @@ std::optional<std::size_t> Client::GetUnusedFingerID() const { void Client::UpdateTouchInput(Response::TouchPad& touch_pad, std::size_t client, std::size_t id) { // TODO: Use custom calibration per device - const Common::ParamPackage touch_param(Settings::values.touch_device); + const Common::ParamPackage touch_param(Settings::values.touch_device.GetValue()); const u16 min_x = static_cast<u16>(touch_param.Get("min_x", 100)); const u16 min_y = static_cast<u16>(touch_param.Get("min_y", 50)); const u16 max_x = static_cast<u16>(touch_param.Get("max_x", 1800)); diff --git a/src/input_common/udp/protocol.h b/src/input_common/udp/protocol.h index a3d276697..1bdc9209e 100644 --- a/src/input_common/udp/protocol.h +++ b/src/input_common/udp/protocol.h @@ -8,14 +8,7 @@ #include <optional> #include <type_traits> -#ifdef _MSC_VER -#pragma warning(push) -#pragma warning(disable : 4701) -#endif #include <boost/crc.hpp> -#ifdef _MSC_VER -#pragma warning(pop) -#endif #include "common/bit_field.h" #include "common/swap.h" diff --git a/src/tests/CMakeLists.txt b/src/tests/CMakeLists.txt index 96bc30cac..c4c012f3d 100644 --- a/src/tests/CMakeLists.txt +++ b/src/tests/CMakeLists.txt @@ -5,6 +5,7 @@ add_executable(tests common/host_memory.cpp common/param_package.cpp common/ring_buffer.cpp + common/unique_function.cpp core/core_timing.cpp core/network/network.cpp tests.cpp diff --git a/src/tests/common/unique_function.cpp b/src/tests/common/unique_function.cpp new file mode 100644 index 000000000..ac9912738 --- /dev/null +++ b/src/tests/common/unique_function.cpp @@ -0,0 +1,108 @@ +// Copyright 2021 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <string> + +#include <catch2/catch.hpp> + +#include "common/unique_function.h" + +namespace { +struct Noisy { + Noisy() : state{"Default constructed"} {} + Noisy(Noisy&& rhs) noexcept : state{"Move constructed"} { + rhs.state = "Moved away"; + } + Noisy& operator=(Noisy&& rhs) noexcept { + state = "Move assigned"; + rhs.state = "Moved away"; + } + Noisy(const Noisy&) : state{"Copied constructed"} {} + Noisy& operator=(const Noisy&) { + state = "Copied assigned"; + } + + std::string state; +}; +} // Anonymous namespace + +TEST_CASE("UniqueFunction", "[common]") { + SECTION("Capture reference") { + int value = 0; + Common::UniqueFunction<void> func = [&value] { value = 5; }; + func(); + REQUIRE(value == 5); + } + SECTION("Capture pointer") { + int value = 0; + int* pointer = &value; + Common::UniqueFunction<void> func = [pointer] { *pointer = 5; }; + func(); + REQUIRE(value == 5); + } + SECTION("Move object") { + Noisy noisy; + REQUIRE(noisy.state == "Default constructed"); + + Common::UniqueFunction<void> func = [noisy = std::move(noisy)] { + REQUIRE(noisy.state == "Move constructed"); + }; + REQUIRE(noisy.state == "Moved away"); + func(); + } + SECTION("Move construct function") { + int value = 0; + Common::UniqueFunction<void> func = [&value] { value = 5; }; + Common::UniqueFunction<void> new_func = std::move(func); + new_func(); + REQUIRE(value == 5); + } + SECTION("Move assign function") { + int value = 0; + Common::UniqueFunction<void> func = [&value] { value = 5; }; + Common::UniqueFunction<void> new_func; + new_func = std::move(func); + new_func(); + REQUIRE(value == 5); + } + SECTION("Default construct then assign function") { + int value = 0; + Common::UniqueFunction<void> func; + func = [&value] { value = 5; }; + func(); + REQUIRE(value == 5); + } + SECTION("Pass arguments") { + int result = 0; + Common::UniqueFunction<void, int, int> func = [&result](int a, int b) { result = a + b; }; + func(5, 4); + REQUIRE(result == 9); + } + SECTION("Pass arguments and return value") { + Common::UniqueFunction<int, int, int> func = [](int a, int b) { return a + b; }; + REQUIRE(func(5, 4) == 9); + } + SECTION("Destructor") { + int num_destroyed = 0; + struct Foo { + Foo(int* num_) : num{num_} {} + Foo(Foo&& rhs) : num{std::exchange(rhs.num, nullptr)} {} + Foo(const Foo&) = delete; + + ~Foo() { + if (num) { + ++*num; + } + } + + int* num = nullptr; + }; + Foo object{&num_destroyed}; + { + Common::UniqueFunction<void> func = [object = std::move(object)] {}; + REQUIRE(num_destroyed == 0); + } + REQUIRE(num_destroyed == 1); + } +} diff --git a/src/tests/video_core/buffer_base.cpp b/src/tests/video_core/buffer_base.cpp index edced69bb..9f5a54de4 100644 --- a/src/tests/video_core/buffer_base.cpp +++ b/src/tests/video_core/buffer_base.cpp @@ -536,7 +536,7 @@ TEST_CASE("BufferBase: Cached write downloads") { REQUIRE(rasterizer.Count() == 63); buffer.MarkRegionAsGpuModified(c + PAGE, PAGE); int num = 0; - buffer.ForEachDownloadRange(c, WORD, [&](u64 offset, u64 size) { ++num; }); + buffer.ForEachDownloadRangeAndClear(c, WORD, [&](u64 offset, u64 size) { ++num; }); buffer.ForEachUploadRange(c, WORD, [&](u64 offset, u64 size) { ++num; }); REQUIRE(num == 0); REQUIRE(!buffer.IsRegionCpuModified(c + PAGE, PAGE)); diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt index f9454bbaa..e4de55f4d 100644 --- a/src/video_core/CMakeLists.txt +++ b/src/video_core/CMakeLists.txt @@ -292,12 +292,12 @@ endif() if (MSVC) target_compile_options(video_core PRIVATE - /we4267 # 'var' : conversion from 'size_t' to 'type', possible loss of data + /we4242 # 'identifier': conversion from 'type1' to 'type2', possible loss of data + /we4244 # 'conversion': conversion from 'type1' to 'type2', possible loss of data /we4456 # Declaration of 'identifier' hides previous local declaration /we4457 # Declaration of 'identifier' hides function parameter /we4458 # Declaration of 'identifier' hides class member /we4459 # Declaration of 'identifier' hides global declaration - /we4715 # 'function' : not all control paths return a value ) else() target_compile_options(video_core PRIVATE diff --git a/src/video_core/buffer_cache/buffer_base.h b/src/video_core/buffer_cache/buffer_base.h index b121d36a3..c3318095c 100644 --- a/src/video_core/buffer_cache/buffer_base.h +++ b/src/video_core/buffer_cache/buffer_base.h @@ -226,19 +226,24 @@ public: /// Call 'func' for each CPU modified range and unmark those pages as CPU modified template <typename Func> void ForEachUploadRange(VAddr query_cpu_range, u64 size, Func&& func) { - ForEachModifiedRange<Type::CPU>(query_cpu_range, size, func); + ForEachModifiedRange<Type::CPU>(query_cpu_range, size, true, func); } /// Call 'func' for each GPU modified range and unmark those pages as GPU modified template <typename Func> - void ForEachDownloadRange(VAddr query_cpu_range, u64 size, Func&& func) { - ForEachModifiedRange<Type::GPU>(query_cpu_range, size, func); + void ForEachDownloadRange(VAddr query_cpu_range, u64 size, bool clear, Func&& func) { + ForEachModifiedRange<Type::GPU>(query_cpu_range, size, clear, func); + } + + template <typename Func> + void ForEachDownloadRangeAndClear(VAddr query_cpu_range, u64 size, Func&& func) { + ForEachModifiedRange<Type::GPU>(query_cpu_range, size, true, func); } /// Call 'func' for each GPU modified range and unmark those pages as GPU modified template <typename Func> void ForEachDownloadRange(Func&& func) { - ForEachModifiedRange<Type::GPU>(cpu_addr, SizeBytes(), func); + ForEachModifiedRange<Type::GPU>(cpu_addr, SizeBytes(), true, func); } /// Mark buffer as picked @@ -415,7 +420,7 @@ private: * @param func Function to call for each turned off region */ template <Type type, typename Func> - void ForEachModifiedRange(VAddr query_cpu_range, s64 size, Func&& func) { + void ForEachModifiedRange(VAddr query_cpu_range, s64 size, bool clear, Func&& func) { static_assert(type != Type::Untracked); const s64 difference = query_cpu_range - cpu_addr; @@ -467,7 +472,9 @@ private: bits = (bits << left_offset) >> left_offset; const u64 current_word = state_words[word_index] & bits; - state_words[word_index] &= ~bits; + if (clear) { + state_words[word_index] &= ~bits; + } if constexpr (type == Type::CPU) { const u64 current_bits = untracked_words[word_index] & bits; diff --git a/src/video_core/buffer_cache/buffer_cache.h b/src/video_core/buffer_cache/buffer_cache.h index 9d726a6fb..2871682f6 100644 --- a/src/video_core/buffer_cache/buffer_cache.h +++ b/src/video_core/buffer_cache/buffer_cache.h @@ -15,6 +15,7 @@ #include <vector> #include <boost/container/small_vector.hpp> +#include <boost/icl/interval_set.hpp> #include "common/common_types.h" #include "common/div_ceil.h" @@ -77,6 +78,9 @@ class BufferCache { using Runtime = typename P::Runtime; using Buffer = typename P::Buffer; + using IntervalSet = boost::icl::interval_set<VAddr>; + using IntervalType = typename IntervalSet::interval_type; + struct Empty {}; struct OverlapResult { @@ -99,7 +103,7 @@ class BufferCache { }; public: - static constexpr u32 DEFAULT_SKIP_CACHE_SIZE = 4_KiB; + static constexpr u32 DEFAULT_SKIP_CACHE_SIZE = static_cast<u32>(4_KiB); explicit BufferCache(VideoCore::RasterizerInterface& rasterizer_, Tegra::Engines::Maxwell3D& maxwell3d_, @@ -109,8 +113,6 @@ public: void TickFrame(); - void RunGarbageCollector(); - void WriteMemory(VAddr cpu_addr, u64 size); void CachedWriteMemory(VAddr cpu_addr, u64 size); @@ -150,18 +152,26 @@ public: /// Return true when there are uncommitted buffers to be downloaded [[nodiscard]] bool HasUncommittedFlushes() const noexcept; + void AccumulateFlushes(); + /// Return true when the caller should wait for async downloads [[nodiscard]] bool ShouldWaitAsyncFlushes() const noexcept; /// Commit asynchronous downloads void CommitAsyncFlushes(); + void CommitAsyncFlushesHigh(); /// Pop asynchronous downloads void PopAsyncFlushes(); + [[nodiscard]] bool DMACopy(GPUVAddr src_address, GPUVAddr dest_address, u64 amount); + /// Return true when a CPU region is modified from the GPU [[nodiscard]] bool IsRegionGpuModified(VAddr addr, size_t size); + /// Return true when a CPU region is modified from the CPU + [[nodiscard]] bool IsRegionCpuModified(VAddr addr, size_t size); + std::mutex mutex; private: @@ -192,11 +202,43 @@ private: } } + template <typename Func> + void ForEachWrittenRange(VAddr cpu_addr, u64 size, Func&& func) { + const VAddr start_address = cpu_addr; + const VAddr end_address = start_address + size; + const VAddr search_base = + static_cast<VAddr>(std::min<s64>(0LL, static_cast<s64>(start_address - size))); + const IntervalType search_interval{search_base, search_base + 1}; + auto it = common_ranges.lower_bound(search_interval); + if (it == common_ranges.end()) { + it = common_ranges.begin(); + } + for (; it != common_ranges.end(); it++) { + VAddr inter_addr_end = it->upper(); + VAddr inter_addr = it->lower(); + if (inter_addr >= end_address) { + break; + } + if (inter_addr_end <= start_address) { + continue; + } + if (inter_addr_end > end_address) { + inter_addr_end = end_address; + } + if (inter_addr < start_address) { + inter_addr = start_address; + } + func(inter_addr, inter_addr_end); + } + } + static bool IsRangeGranular(VAddr cpu_addr, size_t size) { return (cpu_addr & ~Core::Memory::PAGE_MASK) == ((cpu_addr + size) & ~Core::Memory::PAGE_MASK); } + void RunGarbageCollector(); + void BindHostIndexBuffer(); void BindHostVertexBuffers(); @@ -272,8 +314,6 @@ private: void DeleteBuffer(BufferId buffer_id); - void ReplaceBufferDownloads(BufferId old_buffer_id, BufferId new_buffer_id); - void NotifyBufferDeletion(); [[nodiscard]] Binding StorageBufferBinding(GPUVAddr ssbo_addr) const; @@ -327,9 +367,9 @@ private: std::vector<BufferId> cached_write_buffer_ids; - // TODO: This data structure is not optimal and it should be reworked - std::vector<BufferId> uncommitted_downloads; - std::deque<std::vector<BufferId>> committed_downloads; + IntervalSet uncommitted_ranges; + IntervalSet common_ranges; + std::deque<IntervalSet> committed_ranges; size_t immediate_buffer_capacity = 0; std::unique_ptr<u8[]> immediate_buffer_alloc; @@ -352,6 +392,7 @@ BufferCache<P>::BufferCache(VideoCore::RasterizerInterface& rasterizer_, // Ensure the first slot is used for the null buffer void(slot_buffers.insert(runtime, NullBufferParams{})); deletion_iterator = slot_buffers.end(); + common_ranges.clear(); } template <class P> @@ -416,8 +457,71 @@ void BufferCache<P>::CachedWriteMemory(VAddr cpu_addr, u64 size) { template <class P> void BufferCache<P>::DownloadMemory(VAddr cpu_addr, u64 size) { - ForEachBufferInRange(cpu_addr, size, - [&](BufferId, Buffer& buffer) { DownloadBufferMemory(buffer); }); + ForEachBufferInRange(cpu_addr, size, [&](BufferId, Buffer& buffer) { + DownloadBufferMemory(buffer, cpu_addr, size); + }); +} + +template <class P> +bool BufferCache<P>::DMACopy(GPUVAddr src_address, GPUVAddr dest_address, u64 amount) { + const std::optional<VAddr> cpu_src_address = gpu_memory.GpuToCpuAddress(src_address); + const std::optional<VAddr> cpu_dest_address = gpu_memory.GpuToCpuAddress(dest_address); + if (!cpu_src_address || !cpu_dest_address) { + return false; + } + const bool source_dirty = IsRegionGpuModified(*cpu_src_address, amount); + const bool dest_dirty = IsRegionGpuModified(*cpu_dest_address, amount); + if (!source_dirty && !dest_dirty) { + return false; + } + + const IntervalType subtract_interval{*cpu_dest_address, *cpu_dest_address + amount}; + uncommitted_ranges.subtract(subtract_interval); + for (auto& interval_set : committed_ranges) { + interval_set.subtract(subtract_interval); + } + + BufferId buffer_a; + BufferId buffer_b; + do { + has_deleted_buffers = false; + buffer_a = FindBuffer(*cpu_src_address, static_cast<u32>(amount)); + buffer_b = FindBuffer(*cpu_dest_address, static_cast<u32>(amount)); + } while (has_deleted_buffers); + auto& src_buffer = slot_buffers[buffer_a]; + auto& dest_buffer = slot_buffers[buffer_b]; + SynchronizeBuffer(src_buffer, *cpu_src_address, static_cast<u32>(amount)); + SynchronizeBuffer(dest_buffer, *cpu_dest_address, static_cast<u32>(amount)); + std::array copies{BufferCopy{ + .src_offset = src_buffer.Offset(*cpu_src_address), + .dst_offset = dest_buffer.Offset(*cpu_dest_address), + .size = amount, + }}; + + boost::container::small_vector<IntervalType, 4> tmp_intervals; + auto mirror = [&](VAddr base_address, VAddr base_address_end) { + const u64 size = base_address_end - base_address; + const VAddr diff = base_address - *cpu_src_address; + const VAddr new_base_address = *cpu_dest_address + diff; + const IntervalType add_interval{new_base_address, new_base_address + size}; + uncommitted_ranges.add(add_interval); + tmp_intervals.push_back(add_interval); + }; + ForEachWrittenRange(*cpu_src_address, amount, mirror); + // This subtraction in this order is important for overlapping copies. + common_ranges.subtract(subtract_interval); + for (const IntervalType add_interval : tmp_intervals) { + common_ranges.add(add_interval); + } + + runtime.CopyBuffer(dest_buffer, src_buffer, copies); + if (source_dirty) { + dest_buffer.MarkRegionAsGpuModified(*cpu_dest_address, amount); + } + std::vector<u8> tmp_buffer(amount); + cpu_memory.ReadBlockUnsafe(*cpu_src_address, tmp_buffer.data(), amount); + cpu_memory.WriteBlockUnsafe(*cpu_dest_address, tmp_buffer.data(), amount); + return true; } template <class P> @@ -546,29 +650,30 @@ void BufferCache<P>::FlushCachedWrites() { template <class P> bool BufferCache<P>::HasUncommittedFlushes() const noexcept { - return !uncommitted_downloads.empty(); + return !uncommitted_ranges.empty() || !committed_ranges.empty(); } template <class P> -bool BufferCache<P>::ShouldWaitAsyncFlushes() const noexcept { - return !committed_downloads.empty() && !committed_downloads.front().empty(); +void BufferCache<P>::AccumulateFlushes() { + if (Settings::values.gpu_accuracy.GetValue() != Settings::GPUAccuracy::High) { + uncommitted_ranges.clear(); + return; + } + if (uncommitted_ranges.empty()) { + return; + } + committed_ranges.emplace_back(std::move(uncommitted_ranges)); } template <class P> -void BufferCache<P>::CommitAsyncFlushes() { - // This is intentionally passing the value by copy - committed_downloads.push_front(uncommitted_downloads); - uncommitted_downloads.clear(); +bool BufferCache<P>::ShouldWaitAsyncFlushes() const noexcept { + return false; } template <class P> -void BufferCache<P>::PopAsyncFlushes() { - if (committed_downloads.empty()) { - return; - } - auto scope_exit_pop_download = detail::ScopeExit([this] { committed_downloads.pop_back(); }); - const std::span<const BufferId> download_ids = committed_downloads.back(); - if (download_ids.empty()) { +void BufferCache<P>::CommitAsyncFlushesHigh() { + AccumulateFlushes(); + if (committed_ranges.empty()) { return; } MICROPROFILE_SCOPE(GPU_DownloadMemory); @@ -576,20 +681,43 @@ void BufferCache<P>::PopAsyncFlushes() { boost::container::small_vector<std::pair<BufferCopy, BufferId>, 1> downloads; u64 total_size_bytes = 0; u64 largest_copy = 0; - for (const BufferId buffer_id : download_ids) { - slot_buffers[buffer_id].ForEachDownloadRange([&](u64 range_offset, u64 range_size) { - downloads.push_back({ - BufferCopy{ - .src_offset = range_offset, - .dst_offset = total_size_bytes, - .size = range_size, - }, - buffer_id, + for (const IntervalSet& intervals : committed_ranges) { + for (auto& interval : intervals) { + const std::size_t size = interval.upper() - interval.lower(); + const VAddr cpu_addr = interval.lower(); + ForEachBufferInRange(cpu_addr, size, [&](BufferId buffer_id, Buffer& buffer) { + boost::container::small_vector<BufferCopy, 1> copies; + buffer.ForEachDownloadRangeAndClear( + cpu_addr, size, [&](u64 range_offset, u64 range_size) { + const VAddr buffer_addr = buffer.CpuAddr(); + const auto add_download = [&](VAddr start, VAddr end) { + const u64 new_offset = start - buffer_addr; + const u64 new_size = end - start; + downloads.push_back({ + BufferCopy{ + .src_offset = new_offset, + .dst_offset = total_size_bytes, + .size = new_size, + }, + buffer_id, + }); + // Align up to avoid cache conflicts + constexpr u64 align = 256ULL; + constexpr u64 mask = ~(align - 1ULL); + total_size_bytes += (new_size + align - 1) & mask; + largest_copy = std::max(largest_copy, new_size); + }; + + const VAddr start_address = buffer_addr + range_offset; + const VAddr end_address = start_address + range_size; + ForEachWrittenRange(start_address, range_size, add_download); + const IntervalType subtract_interval{start_address, end_address}; + common_ranges.subtract(subtract_interval); + }); }); - total_size_bytes += range_size; - largest_copy = std::max(largest_copy, range_size); - }); + } } + committed_ranges.clear(); if (downloads.empty()) { return; } @@ -622,6 +750,19 @@ void BufferCache<P>::PopAsyncFlushes() { } template <class P> +void BufferCache<P>::CommitAsyncFlushes() { + if (Settings::values.gpu_accuracy.GetValue() == Settings::GPUAccuracy::High) { + CommitAsyncFlushesHigh(); + } else { + uncommitted_ranges.clear(); + committed_ranges.clear(); + } +} + +template <class P> +void BufferCache<P>::PopAsyncFlushes() {} + +template <class P> bool BufferCache<P>::IsRegionGpuModified(VAddr addr, size_t size) { const u64 page_end = Common::DivCeil(addr + size, PAGE_SIZE); for (u64 page = addr >> PAGE_BITS; page < page_end;) { @@ -641,6 +782,25 @@ bool BufferCache<P>::IsRegionGpuModified(VAddr addr, size_t size) { } template <class P> +bool BufferCache<P>::IsRegionCpuModified(VAddr addr, size_t size) { + const u64 page_end = Common::DivCeil(addr + size, PAGE_SIZE); + for (u64 page = addr >> PAGE_BITS; page < page_end;) { + const BufferId image_id = page_table[page]; + if (!image_id) { + ++page; + continue; + } + Buffer& buffer = slot_buffers[image_id]; + if (buffer.IsRegionCpuModified(addr, size)) { + return true; + } + const VAddr end_addr = buffer.CpuAddr() + buffer.SizeBytes(); + page = Common::DivCeil(end_addr, PAGE_SIZE); + } + return false; +} + +template <class P> void BufferCache<P>::BindHostIndexBuffer() { Buffer& buffer = slot_buffers[index_buffer.buffer_id]; TouchBuffer(buffer); @@ -648,7 +808,9 @@ void BufferCache<P>::BindHostIndexBuffer() { const u32 size = index_buffer.size; SynchronizeBuffer(buffer, index_buffer.cpu_addr, size); if constexpr (HAS_FULL_INDEX_AND_PRIMITIVE_SUPPORT) { - runtime.BindIndexBuffer(buffer, offset, size); + const u32 new_offset = offset + maxwell3d.regs.index_array.first * + maxwell3d.regs.index_array.FormatSizeInBytes(); + runtime.BindIndexBuffer(buffer, new_offset, size); } else { runtime.BindIndexBuffer(maxwell3d.regs.draw.topology, maxwell3d.regs.index_array.format, maxwell3d.regs.index_array.first, maxwell3d.regs.index_array.count, @@ -862,7 +1024,7 @@ void BufferCache<P>::UpdateIndexBuffer() { const GPUVAddr gpu_addr_end = index_array.EndAddress(); const std::optional<VAddr> cpu_addr = gpu_memory.GpuToCpuAddress(gpu_addr_begin); const u32 address_size = static_cast<u32>(gpu_addr_end - gpu_addr_begin); - const u32 draw_size = index_array.count * index_array.FormatSizeInBytes(); + const u32 draw_size = (index_array.count + index_array.first) * index_array.FormatSizeInBytes(); const u32 size = std::min(address_size, draw_size); if (size == 0 || !cpu_addr) { index_buffer = NULL_BINDING; @@ -1009,16 +1171,16 @@ void BufferCache<P>::MarkWrittenBuffer(BufferId buffer_id, VAddr cpu_addr, u32 s Buffer& buffer = slot_buffers[buffer_id]; buffer.MarkRegionAsGpuModified(cpu_addr, size); - const bool is_accuracy_high = Settings::IsGPULevelHigh(); + const IntervalType base_interval{cpu_addr, cpu_addr + size}; + common_ranges.add(base_interval); + + const bool is_accuracy_high = + Settings::values.gpu_accuracy.GetValue() == Settings::GPUAccuracy::High; const bool is_async = Settings::values.use_asynchronous_gpu_emulation.GetValue(); - if (!is_accuracy_high || !is_async) { + if (!is_async && !is_accuracy_high) { return; } - if (std::ranges::find(uncommitted_downloads, buffer_id) != uncommitted_downloads.end()) { - // Already inserted - return; - } - uncommitted_downloads.push_back(buffer_id); + uncommitted_ranges.add(base_interval); } template <class P> @@ -1102,7 +1264,6 @@ void BufferCache<P>::JoinOverlap(BufferId new_buffer_id, BufferId overlap_id, if (!copies.empty()) { runtime.CopyBuffer(slot_buffers[new_buffer_id], overlap, copies); } - ReplaceBufferDownloads(overlap_id, new_buffer_id); DeleteBuffer(overlap_id); } @@ -1243,14 +1404,28 @@ void BufferCache<P>::DownloadBufferMemory(Buffer& buffer, VAddr cpu_addr, u64 si boost::container::small_vector<BufferCopy, 1> copies; u64 total_size_bytes = 0; u64 largest_copy = 0; - buffer.ForEachDownloadRange(cpu_addr, size, [&](u64 range_offset, u64 range_size) { - copies.push_back(BufferCopy{ - .src_offset = range_offset, - .dst_offset = total_size_bytes, - .size = range_size, - }); - total_size_bytes += range_size; - largest_copy = std::max(largest_copy, range_size); + buffer.ForEachDownloadRangeAndClear(cpu_addr, size, [&](u64 range_offset, u64 range_size) { + const VAddr buffer_addr = buffer.CpuAddr(); + const auto add_download = [&](VAddr start, VAddr end) { + const u64 new_offset = start - buffer_addr; + const u64 new_size = end - start; + copies.push_back(BufferCopy{ + .src_offset = new_offset, + .dst_offset = total_size_bytes, + .size = new_size, + }); + // Align up to avoid cache conflicts + constexpr u64 align = 256ULL; + constexpr u64 mask = ~(align - 1ULL); + total_size_bytes += (new_size + align - 1) & mask; + largest_copy = std::max(largest_copy, new_size); + }; + + const VAddr start_address = buffer_addr + range_offset; + const VAddr end_address = start_address + range_size; + ForEachWrittenRange(start_address, range_size, add_download); + const IntervalType subtract_interval{start_address, end_address}; + common_ranges.subtract(subtract_interval); }); if (total_size_bytes == 0) { return; @@ -1315,18 +1490,6 @@ void BufferCache<P>::DeleteBuffer(BufferId buffer_id) { } template <class P> -void BufferCache<P>::ReplaceBufferDownloads(BufferId old_buffer_id, BufferId new_buffer_id) { - const auto replace = [old_buffer_id, new_buffer_id](std::vector<BufferId>& buffers) { - std::ranges::replace(buffers, old_buffer_id, new_buffer_id); - if (auto it = std::ranges::find(buffers, new_buffer_id); it != buffers.end()) { - buffers.erase(std::remove(it + 1, buffers.end(), new_buffer_id), buffers.end()); - } - }; - replace(uncommitted_downloads); - std::ranges::for_each(committed_downloads, replace); -} - -template <class P> void BufferCache<P>::NotifyBufferDeletion() { if constexpr (HAS_PERSISTENT_UNIFORM_BUFFER_BINDINGS) { dirty_uniform_buffers.fill(~u32{0}); @@ -1348,15 +1511,9 @@ typename BufferCache<P>::Binding BufferCache<P>::StorageBufferBinding(GPUVAddr s if (!cpu_addr || size == 0) { return NULL_BINDING; } - // HACK(Rodrigo): This is the number of bytes bound in host beyond the guest API's range. - // It exists due to some games like Astral Chain operate out of bounds. - // Binding the whole map range would be technically correct, but games have large maps that make - // this approach unaffordable for now. - static constexpr u32 arbitrary_extra_bytes = 0xc000; - const u32 bytes_to_map_end = static_cast<u32>(gpu_memory.BytesToMapEnd(gpu_addr)); const Binding binding{ .cpu_addr = *cpu_addr, - .size = std::min(size + arbitrary_extra_bytes, bytes_to_map_end), + .size = size, .buffer_id = BufferId{}, }; return binding; 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) { case ThiMethod::SetMethod1: LOG_DEBUG(Service_NVDRV, "NVDEC method 0x{:X}", static_cast<u32>(nvdec_thi_state.method_0)); - nvdec_processor->ProcessMethod(static_cast<Nvdec::Method>(nvdec_thi_state.method_0), - data); + nvdec_processor->ProcessMethod(nvdec_thi_state.method_0, data); break; default: 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) { av_free(ptr); } -Codec::Codec(GPU& gpu_) - : gpu(gpu_), h264_decoder(std::make_unique<Decoder::H264>(gpu)), +Codec::Codec(GPU& gpu_, const NvdecCommon::NvdecRegisters& regs) + : gpu(gpu_), state{regs}, h264_decoder(std::make_unique<Decoder::H264>(gpu)), vp9_decoder(std::make_unique<Decoder::VP9>(gpu)) {} Codec::~Codec() { @@ -43,46 +43,48 @@ Codec::~Codec() { avcodec_close(av_codec_ctx); } +void Codec::Initialize() { + AVCodecID codec{AV_CODEC_ID_NONE}; + switch (current_codec) { + case NvdecCommon::VideoCodec::H264: + codec = AV_CODEC_ID_H264; + break; + case NvdecCommon::VideoCodec::Vp9: + codec = AV_CODEC_ID_VP9; + break; + default: + return; + } + av_codec = avcodec_find_decoder(codec); + av_codec_ctx = avcodec_alloc_context3(av_codec); + av_opt_set(av_codec_ctx->priv_data, "tune", "zerolatency", 0); + + // TODO(ameerj): libavcodec gpu hw acceleration + + const auto av_error = avcodec_open2(av_codec_ctx, av_codec, nullptr); + if (av_error < 0) { + LOG_ERROR(Service_NVDRV, "avcodec_open2() Failed."); + avcodec_close(av_codec_ctx); + return; + } + initialized = true; + return; +} + void Codec::SetTargetCodec(NvdecCommon::VideoCodec codec) { if (current_codec != codec) { - LOG_INFO(Service_NVDRV, "NVDEC video codec initialized to {}", static_cast<u32>(codec)); current_codec = codec; + LOG_INFO(Service_NVDRV, "NVDEC video codec initialized to {}", GetCurrentCodecName()); } } -void Codec::StateWrite(u32 offset, u64 arguments) { - u8* const state_offset = reinterpret_cast<u8*>(&state) + offset * sizeof(u64); - std::memcpy(state_offset, &arguments, sizeof(u64)); -} - void Codec::Decode() { - bool is_first_frame = false; + const bool is_first_frame = !initialized; if (!initialized) { - if (current_codec == NvdecCommon::VideoCodec::H264) { - av_codec = avcodec_find_decoder(AV_CODEC_ID_H264); - } else if (current_codec == NvdecCommon::VideoCodec::Vp9) { - av_codec = avcodec_find_decoder(AV_CODEC_ID_VP9); - } else { - LOG_ERROR(Service_NVDRV, "Unknown video codec {}", current_codec); - return; - } - - av_codec_ctx = avcodec_alloc_context3(av_codec); - av_opt_set(av_codec_ctx->priv_data, "tune", "zerolatency", 0); - - // TODO(ameerj): libavcodec gpu hw acceleration - - const auto av_error = avcodec_open2(av_codec_ctx, av_codec, nullptr); - if (av_error < 0) { - LOG_ERROR(Service_NVDRV, "avcodec_open2() Failed."); - avcodec_close(av_codec_ctx); - return; - } - initialized = true; - is_first_frame = true; + Initialize(); } - bool vp9_hidden_frame = false; + bool vp9_hidden_frame = false; AVPacket packet{}; av_init_packet(&packet); std::vector<u8> frame_data; @@ -95,7 +97,7 @@ void Codec::Decode() { } packet.data = frame_data.data(); - packet.size = static_cast<int>(frame_data.size()); + packet.size = static_cast<s32>(frame_data.size()); avcodec_send_packet(av_codec_ctx, &packet); @@ -127,4 +129,21 @@ NvdecCommon::VideoCodec Codec::GetCurrentCodec() const { return current_codec; } +std::string_view Codec::GetCurrentCodecName() const { + switch (current_codec) { + case NvdecCommon::VideoCodec::None: + return "None"; + case NvdecCommon::VideoCodec::H264: + return "H264"; + case NvdecCommon::VideoCodec::Vp8: + return "VP8"; + case NvdecCommon::VideoCodec::H265: + return "H265"; + case NvdecCommon::VideoCodec::Vp9: + return "VP9"; + default: + return "Unknown"; + } +}; + } // 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; class Codec { public: - explicit Codec(GPU& gpu); + explicit Codec(GPU& gpu, const NvdecCommon::NvdecRegisters& regs); ~Codec(); + /// Initialize the codec, returning success or failure + void Initialize(); + /// Sets NVDEC video stream codec void SetTargetCodec(NvdecCommon::VideoCodec codec); - /// Populate NvdecRegisters state with argument value at the provided offset - void StateWrite(u32 offset, u64 arguments); - /// Call decoders to construct headers, decode AVFrame with ffmpeg void Decode(); @@ -51,6 +51,8 @@ public: /// Returns the value of current_codec [[nodiscard]] NvdecCommon::VideoCodec GetCurrentCodec() const; + /// Return name of the current codec + [[nodiscard]] std::string_view GetCurrentCodecName() const; private: bool initialized{}; @@ -60,10 +62,10 @@ private: AVCodecContext* av_codec_ctx{nullptr}; GPU& gpu; + const NvdecCommon::NvdecRegisters& state; std::unique_ptr<Decoder::H264> h264_decoder; std::unique_ptr<Decoder::VP9> vp9_decoder; - NvdecCommon::NvdecRegisters state{}; std::queue<AVFramePtr> av_frames{}; }; 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; const std::vector<u8>& H264::ComposeFrameHeader(const NvdecCommon::NvdecRegisters& state, bool is_first_frame) { - H264DecoderContext context{}; + H264DecoderContext context; gpu.MemoryManager().ReadBlock(state.picture_info_offset, &context, sizeof(H264DecoderContext)); - const s32 frame_number = static_cast<s32>((context.h264_parameter_set.flags >> 46) & 0x1ffff); + const s64 frame_number = context.h264_parameter_set.frame_number.Value(); if (!is_first_frame && frame_number != 0) { - frame.resize(context.frame_data_size); - + frame.resize(context.stream_len); gpu.MemoryManager().ReadBlock(state.frame_bitstream_offset, frame.data(), frame.size()); - } else { - /// Encode header - H264BitWriter writer{}; - writer.WriteU(1, 24); - writer.WriteU(0, 1); - writer.WriteU(3, 2); - writer.WriteU(7, 5); - writer.WriteU(100, 8); - writer.WriteU(0, 8); - writer.WriteU(31, 8); - writer.WriteUe(0); - const auto chroma_format_idc = - static_cast<u32>((context.h264_parameter_set.flags >> 12) & 3); - writer.WriteUe(chroma_format_idc); - if (chroma_format_idc == 3) { - writer.WriteBit(false); - } - - writer.WriteUe(0); - writer.WriteUe(0); - writer.WriteBit(false); // QpprimeYZeroTransformBypassFlag - writer.WriteBit(false); // Scaling matrix present flag - - const auto order_cnt_type = static_cast<u32>((context.h264_parameter_set.flags >> 14) & 3); - writer.WriteUe(static_cast<u32>((context.h264_parameter_set.flags >> 8) & 0xf)); - writer.WriteUe(order_cnt_type); - if (order_cnt_type == 0) { - writer.WriteUe(context.h264_parameter_set.log2_max_pic_order_cnt); - } else if (order_cnt_type == 1) { - writer.WriteBit(context.h264_parameter_set.delta_pic_order_always_zero_flag != 0); - - writer.WriteSe(0); - writer.WriteSe(0); - writer.WriteUe(0); - } - - const s32 pic_height = context.h264_parameter_set.pic_height_in_map_units / - (context.h264_parameter_set.frame_mbs_only_flag ? 1 : 2); + return frame; + } - writer.WriteUe(16); + // Encode header + H264BitWriter writer{}; + writer.WriteU(1, 24); + writer.WriteU(0, 1); + writer.WriteU(3, 2); + writer.WriteU(7, 5); + writer.WriteU(100, 8); + writer.WriteU(0, 8); + writer.WriteU(31, 8); + writer.WriteUe(0); + const u32 chroma_format_idc = + static_cast<u32>(context.h264_parameter_set.chroma_format_idc.Value()); + writer.WriteUe(chroma_format_idc); + if (chroma_format_idc == 3) { writer.WriteBit(false); - writer.WriteUe(context.h264_parameter_set.pic_width_in_mbs - 1); - writer.WriteUe(pic_height - 1); - writer.WriteBit(context.h264_parameter_set.frame_mbs_only_flag != 0); - - if (!context.h264_parameter_set.frame_mbs_only_flag) { - writer.WriteBit(((context.h264_parameter_set.flags >> 0) & 1) != 0); - } + } - writer.WriteBit(((context.h264_parameter_set.flags >> 1) & 1) != 0); - writer.WriteBit(false); // Frame cropping flag - writer.WriteBit(false); // VUI parameter present flag + writer.WriteUe(0); + writer.WriteUe(0); + writer.WriteBit(false); // QpprimeYZeroTransformBypassFlag + writer.WriteBit(false); // Scaling matrix present flag - writer.End(); + writer.WriteUe(static_cast<u32>(context.h264_parameter_set.log2_max_frame_num_minus4.Value())); - // H264 PPS - writer.WriteU(1, 24); - writer.WriteU(0, 1); - writer.WriteU(3, 2); - writer.WriteU(8, 5); + const auto order_cnt_type = + static_cast<u32>(context.h264_parameter_set.pic_order_cnt_type.Value()); + writer.WriteUe(order_cnt_type); + if (order_cnt_type == 0) { + writer.WriteUe(context.h264_parameter_set.log2_max_pic_order_cnt_lsb_minus4); + } else if (order_cnt_type == 1) { + writer.WriteBit(context.h264_parameter_set.delta_pic_order_always_zero_flag != 0); + writer.WriteSe(0); + writer.WriteSe(0); writer.WriteUe(0); - writer.WriteUe(0); + } - writer.WriteBit(context.h264_parameter_set.entropy_coding_mode_flag != 0); - writer.WriteBit(false); - writer.WriteUe(0); - writer.WriteUe(context.h264_parameter_set.num_refidx_l0_default_active); - writer.WriteUe(context.h264_parameter_set.num_refidx_l1_default_active); - writer.WriteBit(((context.h264_parameter_set.flags >> 2) & 1) != 0); - writer.WriteU(static_cast<s32>((context.h264_parameter_set.flags >> 32) & 0x3), 2); - s32 pic_init_qp = static_cast<s32>((context.h264_parameter_set.flags >> 16) & 0x3f); - pic_init_qp = (pic_init_qp << 26) >> 26; - writer.WriteSe(pic_init_qp); - writer.WriteSe(0); - s32 chroma_qp_index_offset = - static_cast<s32>((context.h264_parameter_set.flags >> 22) & 0x1f); - chroma_qp_index_offset = (chroma_qp_index_offset << 27) >> 27; + const s32 pic_height = context.h264_parameter_set.frame_height_in_map_units / + (context.h264_parameter_set.frame_mbs_only_flag ? 1 : 2); + + writer.WriteUe(16); + writer.WriteBit(false); + writer.WriteUe(context.h264_parameter_set.pic_width_in_mbs - 1); + writer.WriteUe(pic_height - 1); + writer.WriteBit(context.h264_parameter_set.frame_mbs_only_flag != 0); - writer.WriteSe(chroma_qp_index_offset); - writer.WriteBit(context.h264_parameter_set.deblocking_filter_control_flag != 0); - writer.WriteBit(((context.h264_parameter_set.flags >> 3) & 1) != 0); - writer.WriteBit(context.h264_parameter_set.redundant_pic_count_flag != 0); - writer.WriteBit(context.h264_parameter_set.transform_8x8_mode_flag != 0); + if (!context.h264_parameter_set.frame_mbs_only_flag) { + writer.WriteBit(context.h264_parameter_set.flags.mbaff_frame.Value() != 0); + } + writer.WriteBit(context.h264_parameter_set.flags.direct_8x8_inference.Value() != 0); + writer.WriteBit(false); // Frame cropping flag + writer.WriteBit(false); // VUI parameter present flag + + writer.End(); + + // H264 PPS + writer.WriteU(1, 24); + writer.WriteU(0, 1); + writer.WriteU(3, 2); + writer.WriteU(8, 5); + + writer.WriteUe(0); + writer.WriteUe(0); + + writer.WriteBit(context.h264_parameter_set.entropy_coding_mode_flag != 0); + writer.WriteBit(false); + writer.WriteUe(0); + writer.WriteUe(context.h264_parameter_set.num_refidx_l0_default_active); + writer.WriteUe(context.h264_parameter_set.num_refidx_l1_default_active); + writer.WriteBit(context.h264_parameter_set.flags.weighted_pred.Value() != 0); + writer.WriteU(static_cast<s32>(context.h264_parameter_set.weighted_bipred_idc.Value()), 2); + s32 pic_init_qp = static_cast<s32>(context.h264_parameter_set.pic_init_qp_minus26.Value()); + writer.WriteSe(pic_init_qp); + writer.WriteSe(0); + s32 chroma_qp_index_offset = + static_cast<s32>(context.h264_parameter_set.chroma_qp_index_offset.Value()); + + writer.WriteSe(chroma_qp_index_offset); + writer.WriteBit(context.h264_parameter_set.deblocking_filter_control_present_flag != 0); + writer.WriteBit(context.h264_parameter_set.flags.constrained_intra_pred.Value() != 0); + writer.WriteBit(context.h264_parameter_set.redundant_pic_cnt_present_flag != 0); + writer.WriteBit(context.h264_parameter_set.transform_8x8_mode_flag != 0); + + writer.WriteBit(true); + + for (s32 index = 0; index < 6; index++) { writer.WriteBit(true); + std::span<const u8> matrix{context.weight_scale}; + writer.WriteScalingList(matrix, index * 16, 16); + } - for (s32 index = 0; index < 6; index++) { + if (context.h264_parameter_set.transform_8x8_mode_flag) { + for (s32 index = 0; index < 2; index++) { writer.WriteBit(true); - const auto matrix_x4 = - std::vector<u8>(context.scaling_matrix_4.begin(), context.scaling_matrix_4.end()); - writer.WriteScalingList(matrix_x4, index * 16, 16); - } - - if (context.h264_parameter_set.transform_8x8_mode_flag) { - for (s32 index = 0; index < 2; index++) { - writer.WriteBit(true); - const auto matrix_x8 = std::vector<u8>(context.scaling_matrix_8.begin(), - context.scaling_matrix_8.end()); - - writer.WriteScalingList(matrix_x8, index * 64, 64); - } + std::span<const u8> matrix{context.weight_scale_8x8}; + writer.WriteScalingList(matrix, index * 64, 64); } + } - s32 chroma_qp_index_offset2 = - static_cast<s32>((context.h264_parameter_set.flags >> 27) & 0x1f); - chroma_qp_index_offset2 = (chroma_qp_index_offset2 << 27) >> 27; + s32 chroma_qp_index_offset2 = + static_cast<s32>(context.h264_parameter_set.second_chroma_qp_index_offset.Value()); - writer.WriteSe(chroma_qp_index_offset2); + writer.WriteSe(chroma_qp_index_offset2); - writer.End(); + writer.End(); - const auto& encoded_header = writer.GetByteArray(); - frame.resize(encoded_header.size() + context.frame_data_size); - std::memcpy(frame.data(), encoded_header.data(), encoded_header.size()); + const auto& encoded_header = writer.GetByteArray(); + frame.resize(encoded_header.size() + context.stream_len); + std::memcpy(frame.data(), encoded_header.data(), encoded_header.size()); - gpu.MemoryManager().ReadBlock(state.frame_bitstream_offset, - frame.data() + encoded_header.size(), - context.frame_data_size); - } + gpu.MemoryManager().ReadBlock(state.frame_bitstream_offset, + frame.data() + encoded_header.size(), context.stream_len); return frame; } @@ -202,7 +197,7 @@ void H264BitWriter::WriteBit(bool state) { WriteBits(state ? 1 : 0, 1); } -void H264BitWriter::WriteScalingList(const std::vector<u8>& list, s32 start, s32 count) { +void H264BitWriter::WriteScalingList(std::span<const u8> list, s32 start, s32 count) { std::vector<u8> scan(count); if (count == 16) { 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 @@ #pragma once +#include <span> #include <vector> +#include "common/bit_field.h" #include "common/common_funcs.h" #include "common/common_types.h" #include "video_core/command_classes/nvdec_common.h" @@ -48,7 +50,7 @@ public: /// Based on section 7.3.2.1.1.1 and Table 7-4 in the H.264 specification /// Writes the scaling matrices of the sream - void WriteScalingList(const std::vector<u8>& list, s32 start, s32 count); + void WriteScalingList(std::span<const u8> list, s32 start, s32 count); /// Return the bitstream as a vector. [[nodiscard]] std::vector<u8>& GetByteArray(); @@ -78,40 +80,110 @@ public: const NvdecCommon::NvdecRegisters& state, bool is_first_frame = false); private: + std::vector<u8> frame; + GPU& gpu; + struct H264ParameterSet { - u32 log2_max_pic_order_cnt{}; - u32 delta_pic_order_always_zero_flag{}; - u32 frame_mbs_only_flag{}; - u32 pic_width_in_mbs{}; - u32 pic_height_in_map_units{}; - INSERT_PADDING_WORDS(1); - u32 entropy_coding_mode_flag{}; - u32 bottom_field_pic_order_flag{}; - u32 num_refidx_l0_default_active{}; - u32 num_refidx_l1_default_active{}; - u32 deblocking_filter_control_flag{}; - u32 redundant_pic_count_flag{}; - u32 transform_8x8_mode_flag{}; - INSERT_PADDING_WORDS(9); - u64 flags{}; - u32 frame_number{}; - u32 frame_number2{}; + s32 log2_max_pic_order_cnt_lsb_minus4; ///< 0x00 + s32 delta_pic_order_always_zero_flag; ///< 0x04 + s32 frame_mbs_only_flag; ///< 0x08 + u32 pic_width_in_mbs; ///< 0x0C + u32 frame_height_in_map_units; ///< 0x10 + union { ///< 0x14 + BitField<0, 2, u32> tile_format; + BitField<2, 3, u32> gob_height; + }; + u32 entropy_coding_mode_flag; ///< 0x18 + s32 pic_order_present_flag; ///< 0x1C + s32 num_refidx_l0_default_active; ///< 0x20 + s32 num_refidx_l1_default_active; ///< 0x24 + s32 deblocking_filter_control_present_flag; ///< 0x28 + s32 redundant_pic_cnt_present_flag; ///< 0x2C + u32 transform_8x8_mode_flag; ///< 0x30 + u32 pitch_luma; ///< 0x34 + u32 pitch_chroma; ///< 0x38 + u32 luma_top_offset; ///< 0x3C + u32 luma_bot_offset; ///< 0x40 + u32 luma_frame_offset; ///< 0x44 + u32 chroma_top_offset; ///< 0x48 + u32 chroma_bot_offset; ///< 0x4C + u32 chroma_frame_offset; ///< 0x50 + u32 hist_buffer_size; ///< 0x54 + union { ///< 0x58 + union { + BitField<0, 1, u64> mbaff_frame; + BitField<1, 1, u64> direct_8x8_inference; + BitField<2, 1, u64> weighted_pred; + BitField<3, 1, u64> constrained_intra_pred; + BitField<4, 1, u64> ref_pic; + BitField<5, 1, u64> field_pic; + BitField<6, 1, u64> bottom_field; + BitField<7, 1, u64> second_field; + } flags; + BitField<8, 4, u64> log2_max_frame_num_minus4; + BitField<12, 2, u64> chroma_format_idc; + BitField<14, 2, u64> pic_order_cnt_type; + BitField<16, 6, s64> pic_init_qp_minus26; + BitField<22, 5, s64> chroma_qp_index_offset; + BitField<27, 5, s64> second_chroma_qp_index_offset; + BitField<32, 2, u64> weighted_bipred_idc; + BitField<34, 7, u64> curr_pic_idx; + BitField<41, 5, u64> curr_col_idx; + BitField<46, 16, u64> frame_number; + BitField<62, 1, u64> frame_surfaces; + BitField<63, 1, u64> output_memory_layout; + }; }; - static_assert(sizeof(H264ParameterSet) == 0x68, "H264ParameterSet is an invalid size"); + static_assert(sizeof(H264ParameterSet) == 0x60, "H264ParameterSet is an invalid size"); struct H264DecoderContext { - INSERT_PADDING_BYTES(0x48); - u32 frame_data_size{}; - INSERT_PADDING_BYTES(0xc); - H264ParameterSet h264_parameter_set{}; - INSERT_PADDING_BYTES(0x100); - std::array<u8, 0x60> scaling_matrix_4; - std::array<u8, 0x80> scaling_matrix_8; + INSERT_PADDING_WORDS_NOINIT(18); ///< 0x0000 + u32 stream_len; ///< 0x0048 + INSERT_PADDING_WORDS_NOINIT(3); ///< 0x004C + H264ParameterSet h264_parameter_set; ///< 0x0058 + INSERT_PADDING_WORDS_NOINIT(66); ///< 0x00B8 + std::array<u8, 0x60> weight_scale; ///< 0x01C0 + std::array<u8, 0x80> weight_scale_8x8; ///< 0x0220 }; - static_assert(sizeof(H264DecoderContext) == 0x2a0, "H264DecoderContext is an invalid size"); - - std::vector<u8> frame; - GPU& gpu; + static_assert(sizeof(H264DecoderContext) == 0x2A0, "H264DecoderContext is an invalid size"); + +#define ASSERT_POSITION(field_name, position) \ + static_assert(offsetof(H264ParameterSet, field_name) == position, \ + "Field " #field_name " has invalid position") + + ASSERT_POSITION(log2_max_pic_order_cnt_lsb_minus4, 0x00); + ASSERT_POSITION(delta_pic_order_always_zero_flag, 0x04); + ASSERT_POSITION(frame_mbs_only_flag, 0x08); + ASSERT_POSITION(pic_width_in_mbs, 0x0C); + ASSERT_POSITION(frame_height_in_map_units, 0x10); + ASSERT_POSITION(tile_format, 0x14); + ASSERT_POSITION(entropy_coding_mode_flag, 0x18); + ASSERT_POSITION(pic_order_present_flag, 0x1C); + ASSERT_POSITION(num_refidx_l0_default_active, 0x20); + ASSERT_POSITION(num_refidx_l1_default_active, 0x24); + ASSERT_POSITION(deblocking_filter_control_present_flag, 0x28); + ASSERT_POSITION(redundant_pic_cnt_present_flag, 0x2C); + ASSERT_POSITION(transform_8x8_mode_flag, 0x30); + ASSERT_POSITION(pitch_luma, 0x34); + ASSERT_POSITION(pitch_chroma, 0x38); + ASSERT_POSITION(luma_top_offset, 0x3C); + ASSERT_POSITION(luma_bot_offset, 0x40); + ASSERT_POSITION(luma_frame_offset, 0x44); + ASSERT_POSITION(chroma_top_offset, 0x48); + ASSERT_POSITION(chroma_bot_offset, 0x4C); + ASSERT_POSITION(chroma_frame_offset, 0x50); + ASSERT_POSITION(hist_buffer_size, 0x54); + ASSERT_POSITION(flags, 0x58); +#undef ASSERT_POSITION + +#define ASSERT_POSITION(field_name, position) \ + static_assert(offsetof(H264DecoderContext, field_name) == position, \ + "Field " #field_name " has invalid position") + + ASSERT_POSITION(stream_len, 0x48); + ASSERT_POSITION(h264_parameter_set, 0x58); + ASSERT_POSITION(weight_scale, 0x1C0); +#undef ASSERT_POSITION }; } // 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_ } Vp9PictureInfo VP9::GetVp9PictureInfo(const NvdecCommon::NvdecRegisters& state) { - PictureInfo picture_info{}; + PictureInfo picture_info; gpu.MemoryManager().ReadBlock(state.picture_info_offset, &picture_info, sizeof(PictureInfo)); Vp9PictureInfo vp9_info = picture_info.Convert(); @@ -370,7 +370,7 @@ Vp9PictureInfo VP9::GetVp9PictureInfo(const NvdecCommon::NvdecRegisters& state) } void VP9::InsertEntropy(u64 offset, Vp9EntropyProbs& dst) { - EntropyProbs entropy{}; + EntropyProbs entropy; gpu.MemoryManager().ReadBlock(offset, &entropy, sizeof(EntropyProbs)); entropy.Convert(dst); } 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; namespace Decoder { struct Vp9FrameDimensions { - s16 width{}; - s16 height{}; - s16 luma_pitch{}; - s16 chroma_pitch{}; + s16 width; + s16 height; + s16 luma_pitch; + s16 chroma_pitch; }; static_assert(sizeof(Vp9FrameDimensions) == 0x8, "Vp9 Vp9FrameDimensions is an invalid size"); @@ -49,87 +49,87 @@ enum class TxMode { }; struct Segmentation { - u8 enabled{}; - u8 update_map{}; - u8 temporal_update{}; - u8 abs_delta{}; - std::array<u32, 8> feature_mask{}; - std::array<std::array<s16, 4>, 8> feature_data{}; + u8 enabled; + u8 update_map; + u8 temporal_update; + u8 abs_delta; + std::array<u32, 8> feature_mask; + std::array<std::array<s16, 4>, 8> feature_data; }; static_assert(sizeof(Segmentation) == 0x64, "Segmentation is an invalid size"); struct LoopFilter { - u8 mode_ref_delta_enabled{}; - std::array<s8, 4> ref_deltas{}; - std::array<s8, 2> mode_deltas{}; + u8 mode_ref_delta_enabled; + std::array<s8, 4> ref_deltas; + std::array<s8, 2> mode_deltas; }; static_assert(sizeof(LoopFilter) == 0x7, "LoopFilter is an invalid size"); struct Vp9EntropyProbs { - std::array<u8, 36> y_mode_prob{}; - std::array<u8, 64> partition_prob{}; - std::array<u8, 1728> coef_probs{}; - std::array<u8, 8> switchable_interp_prob{}; - std::array<u8, 28> inter_mode_prob{}; - std::array<u8, 4> intra_inter_prob{}; - std::array<u8, 5> comp_inter_prob{}; - std::array<u8, 10> single_ref_prob{}; - std::array<u8, 5> comp_ref_prob{}; - std::array<u8, 6> tx_32x32_prob{}; - std::array<u8, 4> tx_16x16_prob{}; - std::array<u8, 2> tx_8x8_prob{}; - std::array<u8, 3> skip_probs{}; - std::array<u8, 3> joints{}; - std::array<u8, 2> sign{}; - std::array<u8, 20> classes{}; - std::array<u8, 2> class_0{}; - std::array<u8, 20> prob_bits{}; - std::array<u8, 12> class_0_fr{}; - std::array<u8, 6> fr{}; - std::array<u8, 2> class_0_hp{}; - std::array<u8, 2> high_precision{}; + std::array<u8, 36> y_mode_prob; ///< 0x0000 + std::array<u8, 64> partition_prob; ///< 0x0024 + std::array<u8, 1728> coef_probs; ///< 0x0064 + std::array<u8, 8> switchable_interp_prob; ///< 0x0724 + std::array<u8, 28> inter_mode_prob; ///< 0x072C + std::array<u8, 4> intra_inter_prob; ///< 0x0748 + std::array<u8, 5> comp_inter_prob; ///< 0x074C + std::array<u8, 10> single_ref_prob; ///< 0x0751 + std::array<u8, 5> comp_ref_prob; ///< 0x075B + std::array<u8, 6> tx_32x32_prob; ///< 0x0760 + std::array<u8, 4> tx_16x16_prob; ///< 0x0766 + std::array<u8, 2> tx_8x8_prob; ///< 0x076A + std::array<u8, 3> skip_probs; ///< 0x076C + std::array<u8, 3> joints; ///< 0x076F + std::array<u8, 2> sign; ///< 0x0772 + std::array<u8, 20> classes; ///< 0x0774 + std::array<u8, 2> class_0; ///< 0x0788 + std::array<u8, 20> prob_bits; ///< 0x078A + std::array<u8, 12> class_0_fr; ///< 0x079E + std::array<u8, 6> fr; ///< 0x07AA + std::array<u8, 2> class_0_hp; ///< 0x07B0 + std::array<u8, 2> high_precision; ///< 0x07B2 }; static_assert(sizeof(Vp9EntropyProbs) == 0x7B4, "Vp9EntropyProbs is an invalid size"); struct Vp9PictureInfo { - bool is_key_frame{}; - bool intra_only{}; - bool last_frame_was_key{}; - bool frame_size_changed{}; - bool error_resilient_mode{}; - bool last_frame_shown{}; - bool show_frame{}; - std::array<s8, 4> ref_frame_sign_bias{}; - s32 base_q_index{}; - s32 y_dc_delta_q{}; - s32 uv_dc_delta_q{}; - s32 uv_ac_delta_q{}; - bool lossless{}; - s32 transform_mode{}; - bool allow_high_precision_mv{}; - s32 interp_filter{}; - s32 reference_mode{}; - s8 comp_fixed_ref{}; - std::array<s8, 2> comp_var_ref{}; - s32 log2_tile_cols{}; - s32 log2_tile_rows{}; - bool segment_enabled{}; - bool segment_map_update{}; - bool segment_map_temporal_update{}; - s32 segment_abs_delta{}; - std::array<u32, 8> segment_feature_enable{}; - std::array<std::array<s16, 4>, 8> segment_feature_data{}; - bool mode_ref_delta_enabled{}; - bool use_prev_in_find_mv_refs{}; - std::array<s8, 4> ref_deltas{}; - std::array<s8, 2> mode_deltas{}; - Vp9EntropyProbs entropy{}; - Vp9FrameDimensions frame_size{}; - u8 first_level{}; - u8 sharpness_level{}; - u32 bitstream_size{}; - std::array<u64, 4> frame_offsets{}; - std::array<bool, 4> refresh_frame{}; + bool is_key_frame; + bool intra_only; + bool last_frame_was_key; + bool frame_size_changed; + bool error_resilient_mode; + bool last_frame_shown; + bool show_frame; + std::array<s8, 4> ref_frame_sign_bias; + s32 base_q_index; + s32 y_dc_delta_q; + s32 uv_dc_delta_q; + s32 uv_ac_delta_q; + bool lossless; + s32 transform_mode; + bool allow_high_precision_mv; + s32 interp_filter; + s32 reference_mode; + s8 comp_fixed_ref; + std::array<s8, 2> comp_var_ref; + s32 log2_tile_cols; + s32 log2_tile_rows; + bool segment_enabled; + bool segment_map_update; + bool segment_map_temporal_update; + s32 segment_abs_delta; + std::array<u32, 8> segment_feature_enable; + std::array<std::array<s16, 4>, 8> segment_feature_data; + bool mode_ref_delta_enabled; + bool use_prev_in_find_mv_refs; + std::array<s8, 4> ref_deltas; + std::array<s8, 2> mode_deltas; + Vp9EntropyProbs entropy; + Vp9FrameDimensions frame_size; + u8 first_level; + u8 sharpness_level; + u32 bitstream_size; + std::array<u64, 4> frame_offsets; + std::array<bool, 4> refresh_frame; }; struct Vp9FrameContainer { @@ -138,35 +138,35 @@ struct Vp9FrameContainer { }; struct PictureInfo { - INSERT_PADDING_WORDS(12); - u32 bitstream_size{}; - INSERT_PADDING_WORDS(5); - Vp9FrameDimensions last_frame_size{}; - Vp9FrameDimensions golden_frame_size{}; - Vp9FrameDimensions alt_frame_size{}; - Vp9FrameDimensions current_frame_size{}; - u32 vp9_flags{}; - std::array<s8, 4> ref_frame_sign_bias{}; - u8 first_level{}; - u8 sharpness_level{}; - u8 base_q_index{}; - u8 y_dc_delta_q{}; - u8 uv_ac_delta_q{}; - u8 uv_dc_delta_q{}; - u8 lossless{}; - u8 tx_mode{}; - u8 allow_high_precision_mv{}; - u8 interp_filter{}; - u8 reference_mode{}; - s8 comp_fixed_ref{}; - std::array<s8, 2> comp_var_ref{}; - u8 log2_tile_cols{}; - u8 log2_tile_rows{}; - Segmentation segmentation{}; - LoopFilter loop_filter{}; - INSERT_PADDING_BYTES(5); - u32 surface_params{}; - INSERT_PADDING_WORDS(3); + INSERT_PADDING_WORDS_NOINIT(12); ///< 0x00 + u32 bitstream_size; ///< 0x30 + INSERT_PADDING_WORDS_NOINIT(5); ///< 0x34 + Vp9FrameDimensions last_frame_size; ///< 0x48 + Vp9FrameDimensions golden_frame_size; ///< 0x50 + Vp9FrameDimensions alt_frame_size; ///< 0x58 + Vp9FrameDimensions current_frame_size; ///< 0x60 + u32 vp9_flags; ///< 0x68 + std::array<s8, 4> ref_frame_sign_bias; ///< 0x6C + u8 first_level; ///< 0x70 + u8 sharpness_level; ///< 0x71 + u8 base_q_index; ///< 0x72 + u8 y_dc_delta_q; ///< 0x73 + u8 uv_ac_delta_q; ///< 0x74 + u8 uv_dc_delta_q; ///< 0x75 + u8 lossless; ///< 0x76 + u8 tx_mode; ///< 0x77 + u8 allow_high_precision_mv; ///< 0x78 + u8 interp_filter; ///< 0x79 + u8 reference_mode; ///< 0x7A + s8 comp_fixed_ref; ///< 0x7B + std::array<s8, 2> comp_var_ref; ///< 0x7C + u8 log2_tile_cols; ///< 0x7E + u8 log2_tile_rows; ///< 0x7F + Segmentation segmentation; ///< 0x80 + LoopFilter loop_filter; ///< 0xE4 + INSERT_PADDING_BYTES_NOINIT(5); ///< 0xEB + u32 surface_params; ///< 0xF0 + INSERT_PADDING_WORDS_NOINIT(3); ///< 0xF4 [[nodiscard]] Vp9PictureInfo Convert() const { return { @@ -176,6 +176,7 @@ struct PictureInfo { .frame_size_changed = (vp9_flags & FrameFlags::FrameSizeChanged) != 0, .error_resilient_mode = (vp9_flags & FrameFlags::ErrorResilientMode) != 0, .last_frame_shown = (vp9_flags & FrameFlags::LastShowFrame) != 0, + .show_frame = false, .ref_frame_sign_bias = ref_frame_sign_bias, .base_q_index = base_q_index, .y_dc_delta_q = y_dc_delta_q, @@ -204,45 +205,48 @@ struct PictureInfo { !(vp9_flags == (FrameFlags::LastFrameIsKeyFrame)), .ref_deltas = loop_filter.ref_deltas, .mode_deltas = loop_filter.mode_deltas, + .entropy{}, .frame_size = current_frame_size, .first_level = first_level, .sharpness_level = sharpness_level, .bitstream_size = bitstream_size, + .frame_offsets{}, + .refresh_frame{}, }; } }; static_assert(sizeof(PictureInfo) == 0x100, "PictureInfo is an invalid size"); struct EntropyProbs { - INSERT_PADDING_BYTES(1024); - std::array<u8, 28> inter_mode_prob{}; - std::array<u8, 4> intra_inter_prob{}; - INSERT_PADDING_BYTES(80); - std::array<u8, 2> tx_8x8_prob{}; - std::array<u8, 4> tx_16x16_prob{}; - std::array<u8, 6> tx_32x32_prob{}; - std::array<u8, 4> y_mode_prob_e8{}; - std::array<std::array<u8, 8>, 4> y_mode_prob_e0e7{}; - INSERT_PADDING_BYTES(64); - std::array<u8, 64> partition_prob{}; - INSERT_PADDING_BYTES(10); - std::array<u8, 8> switchable_interp_prob{}; - std::array<u8, 5> comp_inter_prob{}; - std::array<u8, 3> skip_probs{}; - INSERT_PADDING_BYTES(1); - std::array<u8, 3> joints{}; - std::array<u8, 2> sign{}; - std::array<u8, 2> class_0{}; - std::array<u8, 6> fr{}; - std::array<u8, 2> class_0_hp{}; - std::array<u8, 2> high_precision{}; - std::array<u8, 20> classes{}; - std::array<u8, 12> class_0_fr{}; - std::array<u8, 20> pred_bits{}; - std::array<u8, 10> single_ref_prob{}; - std::array<u8, 5> comp_ref_prob{}; - INSERT_PADDING_BYTES(17); - std::array<u8, 2304> coef_probs{}; + INSERT_PADDING_BYTES_NOINIT(1024); ///< 0x0000 + std::array<u8, 28> inter_mode_prob; ///< 0x0400 + std::array<u8, 4> intra_inter_prob; ///< 0x041C + INSERT_PADDING_BYTES_NOINIT(80); ///< 0x0420 + std::array<u8, 2> tx_8x8_prob; ///< 0x0470 + std::array<u8, 4> tx_16x16_prob; ///< 0x0472 + std::array<u8, 6> tx_32x32_prob; ///< 0x0476 + std::array<u8, 4> y_mode_prob_e8; ///< 0x047C + std::array<std::array<u8, 8>, 4> y_mode_prob_e0e7; ///< 0x0480 + INSERT_PADDING_BYTES_NOINIT(64); ///< 0x04A0 + std::array<u8, 64> partition_prob; ///< 0x04E0 + INSERT_PADDING_BYTES_NOINIT(10); ///< 0x0520 + std::array<u8, 8> switchable_interp_prob; ///< 0x052A + std::array<u8, 5> comp_inter_prob; ///< 0x0532 + std::array<u8, 3> skip_probs; ///< 0x0537 + INSERT_PADDING_BYTES_NOINIT(1); ///< 0x053A + std::array<u8, 3> joints; ///< 0x053B + std::array<u8, 2> sign; ///< 0x053E + std::array<u8, 2> class_0; ///< 0x0540 + std::array<u8, 6> fr; ///< 0x0542 + std::array<u8, 2> class_0_hp; ///< 0x0548 + std::array<u8, 2> high_precision; ///< 0x054A + std::array<u8, 20> classes; ///< 0x054C + std::array<u8, 12> class_0_fr; ///< 0x0560 + std::array<u8, 20> pred_bits; ///< 0x056C + std::array<u8, 10> single_ref_prob; ///< 0x0580 + std::array<u8, 5> comp_ref_prob; ///< 0x058A + INSERT_PADDING_BYTES_NOINIT(17); ///< 0x058F + std::array<u8, 2304> coef_probs; ///< 0x05A0 void Convert(Vp9EntropyProbs& fc) { fc.inter_mode_prob = inter_mode_prob; @@ -293,10 +297,45 @@ struct RefPoolElement { }; struct FrameContexts { - s64 from{}; - bool adapted{}; - Vp9EntropyProbs probs{}; + s64 from; + bool adapted; + Vp9EntropyProbs probs; }; +#define ASSERT_POSITION(field_name, position) \ + static_assert(offsetof(Vp9EntropyProbs, field_name) == position, \ + "Field " #field_name " has invalid position") + +ASSERT_POSITION(partition_prob, 0x0024); +ASSERT_POSITION(switchable_interp_prob, 0x0724); +ASSERT_POSITION(sign, 0x0772); +ASSERT_POSITION(class_0_fr, 0x079E); +ASSERT_POSITION(high_precision, 0x07B2); +#undef ASSERT_POSITION + +#define ASSERT_POSITION(field_name, position) \ + static_assert(offsetof(PictureInfo, field_name) == position, \ + "Field " #field_name " has invalid position") + +ASSERT_POSITION(bitstream_size, 0x30); +ASSERT_POSITION(last_frame_size, 0x48); +ASSERT_POSITION(first_level, 0x70); +ASSERT_POSITION(segmentation, 0x80); +ASSERT_POSITION(loop_filter, 0xE4); +ASSERT_POSITION(surface_params, 0xF0); +#undef ASSERT_POSITION + +#define ASSERT_POSITION(field_name, position) \ + static_assert(offsetof(EntropyProbs, field_name) == position, \ + "Field " #field_name " has invalid position") + +ASSERT_POSITION(inter_mode_prob, 0x400); +ASSERT_POSITION(tx_8x8_prob, 0x470); +ASSERT_POSITION(partition_prob, 0x4E0); +ASSERT_POSITION(class_0, 0x540); +ASSERT_POSITION(class_0_fr, 0x560); +ASSERT_POSITION(coef_probs, 0x5A0); +#undef ASSERT_POSITION + }; // namespace Decoder }; // 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 @@ namespace Tegra { -Nvdec::Nvdec(GPU& gpu_) : gpu(gpu_), codec(std::make_unique<Codec>(gpu)) {} +#define NVDEC_REG_INDEX(field_name) \ + (offsetof(NvdecCommon::NvdecRegisters, field_name) / sizeof(u64)) + +Nvdec::Nvdec(GPU& gpu_) : gpu(gpu_), state{}, codec(std::make_unique<Codec>(gpu, state)) {} Nvdec::~Nvdec() = default; -void Nvdec::ProcessMethod(Method method, u32 argument) { - if (method == Method::SetVideoCodec) { - codec->StateWrite(static_cast<u32>(method), argument); - } else { - codec->StateWrite(static_cast<u32>(method), static_cast<u64>(argument) << 8); - } +void Nvdec::ProcessMethod(u32 method, u32 argument) { + state.reg_array[method] = static_cast<u64>(argument) << 8; switch (method) { - case Method::SetVideoCodec: + case NVDEC_REG_INDEX(set_codec_id): codec->SetTargetCodec(static_cast<NvdecCommon::VideoCodec>(argument)); break; - case Method::Execute: + case NVDEC_REG_INDEX(execute): Execute(); break; } 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; class Nvdec { public: - enum class Method : u32 { - SetVideoCodec = 0x80, - Execute = 0xc0, - }; - explicit Nvdec(GPU& gpu); ~Nvdec(); /// Writes the method into the state, Invoke Execute() if encountered - void ProcessMethod(Method method, u32 argument); + void ProcessMethod(u32 method, u32 argument); /// Return most recently decoded frame [[nodiscard]] AVFramePtr GetFrame(); @@ -33,6 +28,7 @@ private: void Execute(); GPU& gpu; + NvdecCommon::NvdecRegisters state; std::unique_ptr<Codec> codec; }; } // 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 @@ #pragma once +#include "common/bit_field.h" #include "common/common_funcs.h" #include "common/common_types.h" namespace Tegra::NvdecCommon { -struct NvdecRegisters { - INSERT_PADDING_WORDS(256); - u64 set_codec_id{}; - INSERT_PADDING_WORDS(254); - u64 set_platform_id{}; - u64 picture_info_offset{}; - u64 frame_bitstream_offset{}; - u64 frame_number{}; - u64 h264_slice_data_offsets{}; - u64 h264_mv_dump_offset{}; - INSERT_PADDING_WORDS(6); - u64 frame_stats_offset{}; - u64 h264_last_surface_luma_offset{}; - u64 h264_last_surface_chroma_offset{}; - std::array<u64, 17> surface_luma_offset{}; - std::array<u64, 17> surface_chroma_offset{}; - INSERT_PADDING_WORDS(132); - u64 vp9_entropy_probs_offset{}; - u64 vp9_backward_updates_offset{}; - u64 vp9_last_frame_segmap_offset{}; - u64 vp9_curr_frame_segmap_offset{}; - INSERT_PADDING_WORDS(2); - u64 vp9_last_frame_mvs_offset{}; - u64 vp9_curr_frame_mvs_offset{}; - INSERT_PADDING_WORDS(2); -}; -static_assert(sizeof(NvdecRegisters) == (0xBC0), "NvdecRegisters is incorrect size"); - -enum class VideoCodec : u32 { +enum class VideoCodec : u64 { None = 0x0, H264 = 0x3, Vp8 = 0x5, @@ -45,4 +18,76 @@ enum class VideoCodec : u32 { Vp9 = 0x9, }; +// NVDEC should use a 32-bit address space, but is mapped to 64-bit, +// doubling the sizes here is compensating for that. +struct NvdecRegisters { + static constexpr std::size_t NUM_REGS = 0x178; + + union { + struct { + INSERT_PADDING_WORDS_NOINIT(256); ///< 0x0000 + VideoCodec set_codec_id; ///< 0x0400 + INSERT_PADDING_WORDS_NOINIT(126); ///< 0x0408 + u64 execute; ///< 0x0600 + INSERT_PADDING_WORDS_NOINIT(126); ///< 0x0608 + struct { ///< 0x0800 + union { + BitField<0, 3, VideoCodec> codec; + BitField<4, 1, u64> gp_timer_on; + BitField<13, 1, u64> mb_timer_on; + BitField<14, 1, u64> intra_frame_pslc; + BitField<17, 1, u64> all_intra_frame; + }; + } control_params; + u64 picture_info_offset; ///< 0x0808 + u64 frame_bitstream_offset; ///< 0x0810 + u64 frame_number; ///< 0x0818 + u64 h264_slice_data_offsets; ///< 0x0820 + u64 h264_mv_dump_offset; ///< 0x0828 + INSERT_PADDING_WORDS_NOINIT(6); ///< 0x0830 + u64 frame_stats_offset; ///< 0x0848 + u64 h264_last_surface_luma_offset; ///< 0x0850 + u64 h264_last_surface_chroma_offset; ///< 0x0858 + std::array<u64, 17> surface_luma_offset; ///< 0x0860 + std::array<u64, 17> surface_chroma_offset; ///< 0x08E8 + INSERT_PADDING_WORDS_NOINIT(132); ///< 0x0970 + u64 vp9_entropy_probs_offset; ///< 0x0B80 + u64 vp9_backward_updates_offset; ///< 0x0B88 + u64 vp9_last_frame_segmap_offset; ///< 0x0B90 + u64 vp9_curr_frame_segmap_offset; ///< 0x0B98 + INSERT_PADDING_WORDS_NOINIT(2); ///< 0x0BA0 + u64 vp9_last_frame_mvs_offset; ///< 0x0BA8 + u64 vp9_curr_frame_mvs_offset; ///< 0x0BB0 + INSERT_PADDING_WORDS_NOINIT(2); ///< 0x0BB8 + }; + std::array<u64, NUM_REGS> reg_array; + }; +}; +static_assert(sizeof(NvdecRegisters) == (0xBC0), "NvdecRegisters is incorrect size"); + +#define ASSERT_REG_POSITION(field_name, position) \ + static_assert(offsetof(NvdecRegisters, field_name) == position * sizeof(u64), \ + "Field " #field_name " has invalid position") + +ASSERT_REG_POSITION(set_codec_id, 0x80); +ASSERT_REG_POSITION(execute, 0xC0); +ASSERT_REG_POSITION(control_params, 0x100); +ASSERT_REG_POSITION(picture_info_offset, 0x101); +ASSERT_REG_POSITION(frame_bitstream_offset, 0x102); +ASSERT_REG_POSITION(frame_number, 0x103); +ASSERT_REG_POSITION(h264_slice_data_offsets, 0x104); +ASSERT_REG_POSITION(frame_stats_offset, 0x109); +ASSERT_REG_POSITION(h264_last_surface_luma_offset, 0x10A); +ASSERT_REG_POSITION(h264_last_surface_chroma_offset, 0x10B); +ASSERT_REG_POSITION(surface_luma_offset, 0x10C); +ASSERT_REG_POSITION(surface_chroma_offset, 0x11D); +ASSERT_REG_POSITION(vp9_entropy_probs_offset, 0x170); +ASSERT_REG_POSITION(vp9_backward_updates_offset, 0x171); +ASSERT_REG_POSITION(vp9_last_frame_segmap_offset, 0x172); +ASSERT_REG_POSITION(vp9_curr_frame_segmap_offset, 0x173); +ASSERT_REG_POSITION(vp9_last_frame_mvs_offset, 0x175); +ASSERT_REG_POSITION(vp9_curr_frame_mvs_offset, 0x176); + +#undef ASSERT_REG_POSITION + } // namespace Tegra::NvdecCommon diff --git a/src/video_core/command_classes/vic.cpp b/src/video_core/command_classes/vic.cpp index 43ac20728..ffb7c82a1 100644 --- a/src/video_core/command_classes/vic.cpp +++ b/src/video_core/command_classes/vic.cpp @@ -3,7 +3,21 @@ // Refer to the license.txt file included. #include <array> + +extern "C" { +#if defined(__GNUC__) || defined(__clang__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wconversion" +#endif +#include <libswscale/swscale.h> +#if defined(__GNUC__) || defined(__clang__) +#pragma GCC diagnostic pop +#endif +} + #include "common/assert.h" +#include "common/logging/log.h" + #include "video_core/command_classes/nvdec.h" #include "video_core/command_classes/vic.h" #include "video_core/engines/maxwell_3d.h" @@ -11,10 +25,6 @@ #include "video_core/memory_manager.h" #include "video_core/textures/decoders.h" -extern "C" { -#include <libswscale/swscale.h> -} - namespace Tegra { Vic::Vic(GPU& gpu_, std::shared_ptr<Nvdec> nvdec_processor_) diff --git a/src/video_core/dma_pusher.cpp b/src/video_core/dma_pusher.cpp index 8b33c04ab..8d28bd884 100644 --- a/src/video_core/dma_pusher.cpp +++ b/src/video_core/dma_pusher.cpp @@ -4,6 +4,7 @@ #include "common/cityhash.h" #include "common/microprofile.h" +#include "common/settings.h" #include "core/core.h" #include "core/memory.h" #include "video_core/dma_pusher.h" @@ -76,8 +77,13 @@ bool DmaPusher::Step() { // Push buffer non-empty, read a word command_headers.resize(command_list_header.size); - gpu.MemoryManager().ReadBlockUnsafe(dma_get, command_headers.data(), - command_list_header.size * sizeof(u32)); + if (Settings::IsGPULevelHigh()) { + gpu.MemoryManager().ReadBlock(dma_get, command_headers.data(), + command_list_header.size * sizeof(u32)); + } else { + gpu.MemoryManager().ReadBlockUnsafe(dma_get, command_headers.data(), + command_list_header.size * sizeof(u32)); + } } for (std::size_t index = 0; index < command_headers.size();) { const CommandHeader& command_header = command_headers[index]; diff --git a/src/video_core/engines/fermi_2d.cpp b/src/video_core/engines/fermi_2d.cpp index 0f640fdae..f26530ede 100644 --- a/src/video_core/engines/fermi_2d.cpp +++ b/src/video_core/engines/fermi_2d.cpp @@ -7,6 +7,10 @@ #include "video_core/engines/fermi_2d.h" #include "video_core/memory_manager.h" #include "video_core/rasterizer_interface.h" +#include "video_core/surface.h" + +using VideoCore::Surface::BytesPerBlock; +using VideoCore::Surface::PixelFormatFromRenderTargetFormat; namespace Tegra::Engines { @@ -49,7 +53,7 @@ void Fermi2D::Blit() { UNIMPLEMENTED_IF_MSG(regs.clip_enable != 0, "Clipped blit enabled"); const auto& args = regs.pixels_from_memory; - const Config config{ + Config config{ .operation = regs.operation, .filter = args.sample_mode.filter, .dst_x0 = args.dst_x0, @@ -61,7 +65,21 @@ void Fermi2D::Blit() { .src_x1 = static_cast<s32>((args.du_dx * args.dst_width + args.src_x0) >> 32), .src_y1 = static_cast<s32>((args.dv_dy * args.dst_height + args.src_y0) >> 32), }; - if (!rasterizer->AccelerateSurfaceCopy(regs.src, regs.dst, config)) { + Surface src = regs.src; + const auto bytes_per_pixel = BytesPerBlock(PixelFormatFromRenderTargetFormat(src.format)); + const auto need_align_to_pitch = + src.linear == Tegra::Engines::Fermi2D::MemoryLayout::Pitch && + static_cast<s32>(src.width) == config.src_x1 && + config.src_x1 > static_cast<s32>(src.pitch / bytes_per_pixel) && config.src_x0 > 0; + if (need_align_to_pitch) { + auto address = src.Address() + config.src_x0 * bytes_per_pixel; + src.addr_upper = static_cast<u32>(address >> 32); + src.addr_lower = static_cast<u32>(address); + src.width -= config.src_x0; + config.src_x1 -= config.src_x0; + config.src_x0 = 0; + } + if (!rasterizer->AccelerateSurfaceCopy(src, regs.dst, config)) { UNIMPLEMENTED(); } } diff --git a/src/video_core/engines/maxwell_dma.cpp b/src/video_core/engines/maxwell_dma.cpp index 2ee980bab..24481952b 100644 --- a/src/video_core/engines/maxwell_dma.cpp +++ b/src/video_core/engines/maxwell_dma.cpp @@ -21,6 +21,10 @@ MaxwellDMA::MaxwellDMA(Core::System& system_, MemoryManager& memory_manager_) MaxwellDMA::~MaxwellDMA() = default; +void MaxwellDMA::BindRasterizer(VideoCore::RasterizerInterface* rasterizer_) { + rasterizer = rasterizer_; +} + void MaxwellDMA::CallMethod(u32 method, u32 method_argument, bool is_last_call) { ASSERT_MSG(method < NUM_REGS, "Invalid MaxwellDMA register"); @@ -44,7 +48,6 @@ void MaxwellDMA::Launch() { // TODO(Subv): Perform more research and implement all features of this engine. const LaunchDMA& launch = regs.launch_dma; - ASSERT(launch.remap_enable == 0); ASSERT(launch.semaphore_type == LaunchDMA::SemaphoreType::NONE); ASSERT(launch.interrupt_type == LaunchDMA::InterruptType::NONE); ASSERT(launch.data_transfer_type == LaunchDMA::DataTransferType::NON_PIPELINED); @@ -77,11 +80,29 @@ void MaxwellDMA::CopyPitchToPitch() { // When `multi_line_enable` bit is disabled the copy is performed as if we were copying a 1D // buffer of length `line_length_in`. // Otherwise we copy a 2D image of dimensions (line_length_in, line_count). + auto& accelerate = rasterizer->AccessAccelerateDMA(); if (!regs.launch_dma.multi_line_enable) { - memory_manager.CopyBlock(regs.offset_out, regs.offset_in, regs.line_length_in); + const bool is_buffer_clear = regs.launch_dma.remap_enable != 0 && + regs.remap_const.dst_x == RemapConst::Swizzle::CONST_A; + // TODO: allow multisized components. + if (is_buffer_clear) { + ASSERT(regs.remap_const.component_size_minus_one == 3); + std::vector<u32> tmp_buffer(regs.line_length_in, regs.remap_consta_value); + memory_manager.WriteBlock(regs.offset_out, reinterpret_cast<u8*>(tmp_buffer.data()), + regs.line_length_in * sizeof(u32)); + return; + } + UNIMPLEMENTED_IF(regs.launch_dma.remap_enable != 0); + if (!accelerate.BufferCopy(regs.offset_in, regs.offset_out, regs.line_length_in)) { + std::vector<u8> tmp_buffer(regs.line_length_in); + memory_manager.ReadBlockUnsafe(regs.offset_in, tmp_buffer.data(), regs.line_length_in); + memory_manager.WriteBlock(regs.offset_out, tmp_buffer.data(), regs.line_length_in); + } return; } + UNIMPLEMENTED_IF(regs.launch_dma.remap_enable != 0); + // Perform a line-by-line copy. // We're going to take a subrect of size (line_length_in, line_count) from the source rectangle. // There is no need to manually flush/invalidate the regions because CopyBlock does that for us. @@ -105,6 +126,7 @@ void MaxwellDMA::CopyBlockLinearToPitch() { } // Deswizzle the input and copy it over. + UNIMPLEMENTED_IF(regs.launch_dma.remap_enable != 0); const u32 bytes_per_pixel = regs.pitch_out / regs.line_length_in; const Parameters& src_params = regs.src_params; const u32 width = src_params.width; @@ -134,6 +156,7 @@ void MaxwellDMA::CopyBlockLinearToPitch() { void MaxwellDMA::CopyPitchToBlockLinear() { UNIMPLEMENTED_IF_MSG(regs.dst_params.block_size.width != 0, "Block width is not one"); + UNIMPLEMENTED_IF(regs.launch_dma.remap_enable != 0); const auto& dst_params = regs.dst_params; const u32 bytes_per_pixel = regs.pitch_in / regs.line_length_in; @@ -156,13 +179,8 @@ void MaxwellDMA::CopyPitchToBlockLinear() { write_buffer.resize(dst_size); } - if (Settings::IsGPULevelExtreme()) { - memory_manager.ReadBlock(regs.offset_in, read_buffer.data(), src_size); - memory_manager.ReadBlock(regs.offset_out, write_buffer.data(), dst_size); - } else { - memory_manager.ReadBlockUnsafe(regs.offset_in, read_buffer.data(), src_size); - memory_manager.ReadBlockUnsafe(regs.offset_out, write_buffer.data(), dst_size); - } + memory_manager.ReadBlock(regs.offset_in, read_buffer.data(), src_size); + memory_manager.ReadBlock(regs.offset_out, write_buffer.data(), dst_size); // If the input is linear and the output is tiled, swizzle the input and copy it over. if (regs.dst_params.block_size.depth > 0) { diff --git a/src/video_core/engines/maxwell_dma.h b/src/video_core/engines/maxwell_dma.h index c77f02a22..4ed0d0996 100644 --- a/src/video_core/engines/maxwell_dma.h +++ b/src/video_core/engines/maxwell_dma.h @@ -21,8 +21,18 @@ namespace Tegra { class MemoryManager; } +namespace VideoCore { +class RasterizerInterface; +} + namespace Tegra::Engines { +class AccelerateDMAInterface { +public: + /// Write the value to the register identified by method. + virtual bool BufferCopy(GPUVAddr src_address, GPUVAddr dest_address, u64 amount) = 0; +}; + /** * This engine is known as gk104_copy. Documentation can be found in: * https://github.com/NVIDIA/open-gpu-doc/blob/master/classes/dma-copy/clb0b5.h @@ -187,6 +197,8 @@ public: }; static_assert(sizeof(RemapConst) == 12); + void BindRasterizer(VideoCore::RasterizerInterface* rasterizer); + explicit MaxwellDMA(Core::System& system_, MemoryManager& memory_manager_); ~MaxwellDMA() override; @@ -213,6 +225,7 @@ private: Core::System& system; MemoryManager& memory_manager; + VideoCore::RasterizerInterface* rasterizer; std::vector<u8> read_buffer; std::vector<u8> write_buffer; @@ -240,7 +253,9 @@ private: u32 pitch_out; u32 line_length_in; u32 line_count; - u32 reserved06[0xb8]; + u32 reserved06[0xb6]; + u32 remap_consta_value; + u32 remap_constb_value; RemapConst remap_const; Parameters dst_params; u32 reserved07[0x1]; diff --git a/src/video_core/fence_manager.h b/src/video_core/fence_manager.h index f055b61e9..34dc6c596 100644 --- a/src/video_core/fence_manager.h +++ b/src/video_core/fence_manager.h @@ -8,6 +8,7 @@ #include <queue> #include "common/common_types.h" +#include "common/settings.h" #include "core/core.h" #include "video_core/delayed_destruction_ring.h" #include "video_core/gpu.h" @@ -53,6 +54,12 @@ public: delayed_destruction_ring.Tick(); } + // Unlike other fences, this one doesn't + void SignalOrdering() { + std::scoped_lock lock{buffer_cache.mutex}; + buffer_cache.AccumulateFlushes(); + } + void SignalSemaphore(GPUVAddr addr, u32 value) { TryReleasePendingFences(); const bool should_flush = ShouldFlush(); diff --git a/src/video_core/gpu.cpp b/src/video_core/gpu.cpp index 35cc561be..ff024f530 100644 --- a/src/video_core/gpu.cpp +++ b/src/video_core/gpu.cpp @@ -50,6 +50,7 @@ void GPU::BindRenderer(std::unique_ptr<VideoCore::RendererBase> renderer_) { maxwell_3d->BindRasterizer(rasterizer); fermi_2d->BindRasterizer(rasterizer); kepler_compute->BindRasterizer(rasterizer); + maxwell_dma->BindRasterizer(rasterizer); } Engines::Maxwell3D& GPU::Maxwell3D() { @@ -268,11 +269,13 @@ void GPU::CallPullerMethod(const MethodCall& method_call) { case BufferMethods::SemaphoreAddressHigh: case BufferMethods::SemaphoreAddressLow: case BufferMethods::SemaphoreSequence: - case BufferMethods::RefCnt: case BufferMethods::UnkCacheFlush: case BufferMethods::WrcacheFlush: case BufferMethods::FenceValue: break; + case BufferMethods::RefCnt: + rasterizer->SignalReference(); + break; case BufferMethods::FenceAction: ProcessFenceActionMethod(); break; 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 vulkan_uint8.comp ) -find_program(GLSLANGVALIDATOR "glslangValidator" REQUIRED) +find_program(GLSLANGVALIDATOR "glslangValidator") +if ("${GLSLANGVALIDATOR}" STREQUAL "GLSLANGVALIDATOR-NOTFOUND") + message(FATAL_ERROR "Required program `glslangValidator` not found.") +endif() set(GLSL_FLAGS "") set(QUIET_FLAG "--quiet") diff --git a/src/video_core/memory_manager.cpp b/src/video_core/memory_manager.cpp index 7124c755c..d2b9d5f2b 100644 --- a/src/video_core/memory_manager.cpp +++ b/src/video_core/memory_manager.cpp @@ -69,11 +69,16 @@ void MemoryManager::Unmap(GPUVAddr gpu_addr, std::size_t size) { } else { UNREACHABLE_MSG("Unmapping non-existent GPU address=0x{:x}", gpu_addr); } - // Flush and invalidate through the GPU interface, to be asynchronous if possible. - const std::optional<VAddr> cpu_addr = GpuToCpuAddress(gpu_addr); - ASSERT(cpu_addr); - rasterizer->UnmapMemory(*cpu_addr, size); + const auto submapped_ranges = GetSubmappedRange(gpu_addr, size); + + for (const auto& map : submapped_ranges) { + // Flush and invalidate through the GPU interface, to be asynchronous if possible. + const std::optional<VAddr> cpu_addr = GpuToCpuAddress(map.first); + ASSERT(cpu_addr); + + rasterizer->UnmapMemory(*cpu_addr, map.second); + } UpdateRange(gpu_addr, PageEntry::State::Unmapped, size); } @@ -127,8 +132,14 @@ void MemoryManager::SetPageEntry(GPUVAddr gpu_addr, PageEntry page_entry, std::s //// Lock the new page // TryLockPage(page_entry, size); + auto& current_page = page_table[PageEntryIndex(gpu_addr)]; - page_table[PageEntryIndex(gpu_addr)] = page_entry; + if ((!current_page.IsValid() && page_entry.IsValid()) || + current_page.ToAddress() != page_entry.ToAddress()) { + rasterizer->ModifyGPUMemory(gpu_addr, size); + } + + current_page = page_entry; } std::optional<GPUVAddr> MemoryManager::FindFreeRange(std::size_t size, std::size_t align, @@ -174,6 +185,19 @@ std::optional<VAddr> MemoryManager::GpuToCpuAddress(GPUVAddr gpu_addr) const { return page_entry.ToAddress() + (gpu_addr & page_mask); } +std::optional<VAddr> MemoryManager::GpuToCpuAddress(GPUVAddr addr, std::size_t size) const { + size_t page_index{addr >> page_bits}; + const size_t page_last{(addr + size + page_size - 1) >> page_bits}; + while (page_index < page_last) { + const auto page_addr{GpuToCpuAddress(page_index << page_bits)}; + if (page_addr && *page_addr != 0) { + return page_addr; + } + ++page_index; + } + return std::nullopt; +} + template <typename T> T MemoryManager::Read(GPUVAddr addr) const { if (auto page_pointer{GetPointer(addr)}; page_pointer) { @@ -370,4 +394,79 @@ bool MemoryManager::IsGranularRange(GPUVAddr gpu_addr, std::size_t size) const { return page <= Core::Memory::PAGE_SIZE; } +bool MemoryManager::IsContinousRange(GPUVAddr gpu_addr, std::size_t size) const { + size_t page_index{gpu_addr >> page_bits}; + const size_t page_last{(gpu_addr + size + page_size - 1) >> page_bits}; + std::optional<VAddr> old_page_addr{}; + while (page_index != page_last) { + const auto page_addr{GpuToCpuAddress(page_index << page_bits)}; + if (!page_addr || *page_addr == 0) { + return false; + } + if (old_page_addr) { + if (*old_page_addr + page_size != *page_addr) { + return false; + } + } + old_page_addr = page_addr; + ++page_index; + } + return true; +} + +bool MemoryManager::IsFullyMappedRange(GPUVAddr gpu_addr, std::size_t size) const { + size_t page_index{gpu_addr >> page_bits}; + const size_t page_last{(gpu_addr + size + page_size - 1) >> page_bits}; + while (page_index < page_last) { + if (!page_table[page_index].IsValid() || page_table[page_index].ToAddress() == 0) { + return false; + } + ++page_index; + } + return true; +} + +std::vector<std::pair<GPUVAddr, std::size_t>> MemoryManager::GetSubmappedRange( + GPUVAddr gpu_addr, std::size_t size) const { + std::vector<std::pair<GPUVAddr, std::size_t>> result{}; + size_t page_index{gpu_addr >> page_bits}; + size_t remaining_size{size}; + size_t page_offset{gpu_addr & page_mask}; + std::optional<std::pair<GPUVAddr, std::size_t>> last_segment{}; + std::optional<VAddr> old_page_addr{}; + const auto extend_size = [this, &last_segment, &page_index](std::size_t bytes) { + if (!last_segment) { + GPUVAddr new_base_addr = page_index << page_bits; + last_segment = {new_base_addr, bytes}; + } else { + last_segment->second += bytes; + } + }; + const auto split = [this, &last_segment, &result] { + if (last_segment) { + result.push_back(*last_segment); + last_segment = std::nullopt; + } + }; + while (remaining_size > 0) { + const size_t num_bytes{std::min(page_size - page_offset, remaining_size)}; + const auto page_addr{GpuToCpuAddress(page_index << page_bits)}; + if (!page_addr) { + split(); + } else if (old_page_addr) { + if (*old_page_addr + page_size != *page_addr) { + split(); + } + extend_size(num_bytes); + } else { + extend_size(num_bytes); + } + ++page_index; + page_offset = 0; + remaining_size -= num_bytes; + } + split(); + return result; +} + } // namespace Tegra diff --git a/src/video_core/memory_manager.h b/src/video_core/memory_manager.h index b3538d503..99d13e7f6 100644 --- a/src/video_core/memory_manager.h +++ b/src/video_core/memory_manager.h @@ -76,6 +76,8 @@ public: [[nodiscard]] std::optional<VAddr> GpuToCpuAddress(GPUVAddr addr) const; + [[nodiscard]] std::optional<VAddr> GpuToCpuAddress(GPUVAddr addr, std::size_t size) const; + template <typename T> [[nodiscard]] T Read(GPUVAddr addr) const; @@ -112,10 +114,28 @@ public: void WriteBlockUnsafe(GPUVAddr gpu_dest_addr, const void* src_buffer, std::size_t size); /** - * IsGranularRange checks if a gpu region can be simply read with a pointer. + * Checks if a gpu region can be simply read with a pointer. */ [[nodiscard]] bool IsGranularRange(GPUVAddr gpu_addr, std::size_t size) const; + /** + * Checks if a gpu region is mapped by a single range of cpu addresses. + */ + [[nodiscard]] bool IsContinousRange(GPUVAddr gpu_addr, std::size_t size) const; + + /** + * Checks if a gpu region is mapped entirely. + */ + [[nodiscard]] bool IsFullyMappedRange(GPUVAddr gpu_addr, std::size_t size) const; + + /** + * Returns a vector with all the subranges of cpu addresses mapped beneath. + * if the region is continous, a single pair will be returned. If it's unmapped, an empty vector + * will be returned; + */ + std::vector<std::pair<GPUVAddr, std::size_t>> GetSubmappedRange(GPUVAddr gpu_addr, + std::size_t size) const; + [[nodiscard]] GPUVAddr Map(VAddr cpu_addr, GPUVAddr gpu_addr, std::size_t size); [[nodiscard]] GPUVAddr MapAllocate(VAddr cpu_addr, std::size_t size, std::size_t align); [[nodiscard]] GPUVAddr MapAllocate32(VAddr cpu_addr, std::size_t size); diff --git a/src/video_core/rasterizer_interface.h b/src/video_core/rasterizer_interface.h index 07939432f..58014c1c3 100644 --- a/src/video_core/rasterizer_interface.h +++ b/src/video_core/rasterizer_interface.h @@ -15,7 +15,10 @@ namespace Tegra { class MemoryManager; +namespace Engines { +class AccelerateDMAInterface; } +} // namespace Tegra namespace VideoCore { @@ -63,6 +66,9 @@ public: /// Signal a GPU based syncpoint as a fence virtual void SignalSyncPoint(u32 value) = 0; + /// Signal a GPU based reference as point + virtual void SignalReference() = 0; + /// Release all pending fences. virtual void ReleaseFences() = 0; @@ -87,6 +93,9 @@ public: /// Unmap memory range virtual void UnmapMemory(VAddr addr, u64 size) = 0; + /// Remap GPU memory range. This means underneath backing memory changed + virtual void ModifyGPUMemory(GPUVAddr addr, u64 size) = 0; + /// Notify rasterizer that any caches of the specified region should be flushed to Switch memory /// and invalidated virtual void FlushAndInvalidateRegion(VAddr addr, u64 size) = 0; @@ -113,6 +122,8 @@ public: return false; } + [[nodiscard]] virtual Tegra::Engines::AccelerateDMAInterface& AccessAccelerateDMA() = 0; + /// Attempt to use a faster method to display the framebuffer to screen [[nodiscard]] virtual bool AccelerateDisplay(const Tegra::FramebufferConfig& config, VAddr framebuffer_addr, u32 pixel_stride) { 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: [[nodiscard]] virtual RasterizerInterface* ReadRasterizer() = 0; + [[nodiscard]] virtual std::string GetDeviceVendor() const = 0; + // Getter/setter functions: // ------------------------ 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() { LOG_ERROR(Render_OpenGL, "OpenGL 4.6 is not available"); throw std::runtime_error{"Insufficient version"}; } - const std::string_view vendor = reinterpret_cast<const char*>(glGetString(GL_VENDOR)); + vendor_name = reinterpret_cast<const char*>(glGetString(GL_VENDOR)); const std::string_view version = reinterpret_cast<const char*>(glGetString(GL_VERSION)); const std::vector extensions = GetExtensions(); - const bool is_nvidia = vendor == "NVIDIA Corporation"; - const bool is_amd = vendor == "ATI Technologies Inc."; - const bool is_intel = vendor == "Intel"; + const bool is_nvidia = vendor_name == "NVIDIA Corporation"; + const bool is_amd = vendor_name == "ATI Technologies Inc."; + const bool is_intel = vendor_name == "Intel"; #ifdef __unix__ const bool is_linux = true; @@ -275,6 +275,56 @@ Device::Device() { } } +std::string Device::GetVendorName() const { + if (vendor_name == "NVIDIA Corporation") { + return "NVIDIA"; + } + if (vendor_name == "ATI Technologies Inc.") { + return "AMD"; + } + if (vendor_name == "Intel") { + // For Mesa, `Intel` is an overloaded vendor string that could mean crocus or iris. + // Simply return `INTEL` for those as well as the Windows driver. + return "INTEL"; + } + if (vendor_name == "Intel Open Source Technology Center") { + return "I965"; + } + if (vendor_name == "Mesa Project") { + return "I915"; + } + if (vendor_name == "Mesa/X.org") { + // This vendor string is overloaded between llvmpipe, softpipe, and virgl, so just return + // MESA instead of one of those driver names. + return "MESA"; + } + if (vendor_name == "AMD") { + return "RADEONSI"; + } + if (vendor_name == "nouveau") { + return "NOUVEAU"; + } + if (vendor_name == "X.Org") { + return "R600"; + } + if (vendor_name == "Collabora Ltd") { + return "ZINK"; + } + if (vendor_name == "Intel Corporation") { + return "OPENSWR"; + } + if (vendor_name == "Microsoft Corporation") { + return "D3D12"; + } + if (vendor_name == "NVIDIA") { + // Mesa's tegra driver reports `NVIDIA`. Only present in this list because the default + // strategy would have returned `NVIDIA` here for this driver, the same result as the + // proprietary driver. + return "TEGRA"; + } + return vendor_name; +} + Device::Device(std::nullptr_t) { max_uniform_buffers.fill(std::numeric_limits<u32>::max()); 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: explicit Device(); explicit Device(std::nullptr_t); + [[nodiscard]] std::string GetVendorName() const; + u32 GetMaxUniformBuffers(Tegra::Engines::ShaderType shader_type) const noexcept { return max_uniform_buffers[static_cast<std::size_t>(shader_type)]; } @@ -130,6 +132,7 @@ private: static bool TestVariableAoffi(); static bool TestPreciseBug(); + std::string vendor_name; std::array<u32, Tegra::Engines::MaxShaderTypes> max_uniform_buffers{}; std::array<BaseBindings, Tegra::Engines::MaxShaderTypes> base_bindings{}; size_t uniform_buffer_alignment{}; diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp index eb8bdaa85..82c84127a 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp @@ -171,7 +171,7 @@ RasterizerOpenGL::RasterizerOpenGL(Core::Frontend::EmuWindow& emu_window_, Tegra buffer_cache_runtime(device), buffer_cache(*this, maxwell3d, kepler_compute, gpu_memory, cpu_memory_, buffer_cache_runtime), shader_cache(*this, emu_window_, gpu, maxwell3d, kepler_compute, gpu_memory, device), - query_cache(*this, maxwell3d, gpu_memory), + query_cache(*this, maxwell3d, gpu_memory), accelerate_dma(buffer_cache), fence_manager(*this, gpu, texture_cache, buffer_cache, query_cache), async_shaders(emu_window_) { if (device.UseAsynchronousShaders()) { @@ -611,6 +611,13 @@ void RasterizerOpenGL::UnmapMemory(VAddr addr, u64 size) { shader_cache.OnCPUWrite(addr, size); } +void RasterizerOpenGL::ModifyGPUMemory(GPUVAddr addr, u64 size) { + { + std::scoped_lock lock{texture_cache.mutex}; + texture_cache.UnmapGPUMemory(addr, size); + } +} + void RasterizerOpenGL::SignalSemaphore(GPUVAddr addr, u32 value) { if (!gpu.IsAsync()) { gpu_memory.Write<u32>(addr, value); @@ -627,6 +634,13 @@ void RasterizerOpenGL::SignalSyncPoint(u32 value) { fence_manager.SignalSyncPoint(value); } +void RasterizerOpenGL::SignalReference() { + if (!gpu.IsAsync()) { + return; + } + fence_manager.SignalOrdering(); +} + void RasterizerOpenGL::ReleaseFences() { if (!gpu.IsAsync()) { return; @@ -643,6 +657,7 @@ void RasterizerOpenGL::FlushAndInvalidateRegion(VAddr addr, u64 size) { void RasterizerOpenGL::WaitForIdle() { glMemoryBarrier(GL_ALL_BARRIER_BITS); + SignalReference(); } void RasterizerOpenGL::FragmentBarrier() { @@ -686,6 +701,10 @@ bool RasterizerOpenGL::AccelerateSurfaceCopy(const Tegra::Engines::Fermi2D::Surf return true; } +Tegra::Engines::AccelerateDMAInterface& RasterizerOpenGL::AccessAccelerateDMA() { + return accelerate_dma; +} + bool RasterizerOpenGL::AccelerateDisplay(const Tegra::FramebufferConfig& config, VAddr framebuffer_addr, u32 pixel_stride) { if (framebuffer_addr == 0) { @@ -1381,4 +1400,11 @@ void RasterizerOpenGL::EndTransformFeedback() { glEndTransformFeedback(); } +AccelerateDMA::AccelerateDMA(BufferCache& buffer_cache_) : buffer_cache{buffer_cache_} {} + +bool AccelerateDMA::BufferCopy(GPUVAddr src_address, GPUVAddr dest_address, u64 amount) { + std::scoped_lock lock{buffer_cache.mutex}; + return buffer_cache.DMACopy(src_address, dest_address, amount); +} + } // namespace OpenGL diff --git a/src/video_core/renderer_opengl/gl_rasterizer.h b/src/video_core/renderer_opengl/gl_rasterizer.h index 9995a563b..ccee9ba33 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.h +++ b/src/video_core/renderer_opengl/gl_rasterizer.h @@ -19,6 +19,7 @@ #include "common/common_types.h" #include "video_core/engines/const_buffer_info.h" #include "video_core/engines/maxwell_3d.h" +#include "video_core/engines/maxwell_dma.h" #include "video_core/rasterizer_accelerated.h" #include "video_core/rasterizer_interface.h" #include "video_core/renderer_opengl/gl_buffer_cache.h" @@ -58,6 +59,16 @@ struct BindlessSSBO { }; static_assert(sizeof(BindlessSSBO) * CHAR_BIT == 128); +class AccelerateDMA : public Tegra::Engines::AccelerateDMAInterface { +public: + explicit AccelerateDMA(BufferCache& buffer_cache); + + bool BufferCopy(GPUVAddr src_address, GPUVAddr dest_address, u64 amount) override; + +private: + BufferCache& buffer_cache; +}; + class RasterizerOpenGL : public VideoCore::RasterizerAccelerated { public: explicit RasterizerOpenGL(Core::Frontend::EmuWindow& emu_window_, Tegra::GPU& gpu_, @@ -80,8 +91,10 @@ public: void OnCPUWrite(VAddr addr, u64 size) override; void SyncGuestHost() override; void UnmapMemory(VAddr addr, u64 size) override; + void ModifyGPUMemory(GPUVAddr addr, u64 size) override; void SignalSemaphore(GPUVAddr addr, u32 value) override; void SignalSyncPoint(u32 value) override; + void SignalReference() override; void ReleaseFences() override; void FlushAndInvalidateRegion(VAddr addr, u64 size) override; void WaitForIdle() override; @@ -92,6 +105,7 @@ public: bool AccelerateSurfaceCopy(const Tegra::Engines::Fermi2D::Surface& src, const Tegra::Engines::Fermi2D::Surface& dst, const Tegra::Engines::Fermi2D::Config& copy_config) override; + Tegra::Engines::AccelerateDMAInterface& AccessAccelerateDMA() override; bool AccelerateDisplay(const Tegra::FramebufferConfig& config, VAddr framebuffer_addr, u32 pixel_stride) override; void LoadDiskResources(u64 title_id, std::stop_token stop_loading, @@ -232,6 +246,7 @@ private: BufferCache buffer_cache; ShaderCacheOpenGL shader_cache; QueryCache query_cache; + AccelerateDMA accelerate_dma; FenceManagerOpenGL fence_manager; VideoCommon::Shader::AsyncShaders async_shaders; diff --git a/src/video_core/renderer_opengl/gl_texture_cache.cpp b/src/video_core/renderer_opengl/gl_texture_cache.cpp index 23948feed..ff0f03e99 100644 --- a/src/video_core/renderer_opengl/gl_texture_cache.cpp +++ b/src/video_core/renderer_opengl/gl_texture_cache.cpp @@ -327,7 +327,8 @@ void ApplySwizzle(GLuint handle, PixelFormat format, std::array<SwizzleSource, 4 if (format_info.is_compressed) { return false; } - if (std::ranges::find(ACCELERATED_FORMATS, internal_format) == ACCELERATED_FORMATS.end()) { + if (std::ranges::find(ACCELERATED_FORMATS, static_cast<int>(internal_format)) == + ACCELERATED_FORMATS.end()) { return false; } if (format_info.compatibility_by_size) { @@ -341,6 +342,20 @@ void ApplySwizzle(GLuint handle, PixelFormat format, std::array<SwizzleSource, 4 [[nodiscard]] CopyOrigin MakeCopyOrigin(VideoCommon::Offset3D offset, VideoCommon::SubresourceLayers subresource, GLenum target) { switch (target) { + case GL_TEXTURE_1D: + return CopyOrigin{ + .level = static_cast<GLint>(subresource.base_level), + .x = static_cast<GLint>(offset.x), + .y = static_cast<GLint>(0), + .z = static_cast<GLint>(0), + }; + case GL_TEXTURE_1D_ARRAY: + return CopyOrigin{ + .level = static_cast<GLint>(subresource.base_level), + .x = static_cast<GLint>(offset.x), + .y = static_cast<GLint>(0), + .z = static_cast<GLint>(subresource.base_layer), + }; case GL_TEXTURE_2D_ARRAY: case GL_TEXTURE_2D_MULTISAMPLE_ARRAY: return CopyOrigin{ @@ -366,6 +381,18 @@ void ApplySwizzle(GLuint handle, PixelFormat format, std::array<SwizzleSource, 4 VideoCommon::SubresourceLayers dst_subresource, GLenum target) { switch (target) { + case GL_TEXTURE_1D: + return CopyRegion{ + .width = static_cast<GLsizei>(extent.width), + .height = static_cast<GLsizei>(1), + .depth = static_cast<GLsizei>(1), + }; + case GL_TEXTURE_1D_ARRAY: + return CopyRegion{ + .width = static_cast<GLsizei>(extent.width), + .height = static_cast<GLsizei>(1), + .depth = static_cast<GLsizei>(dst_subresource.num_layers), + }; case GL_TEXTURE_2D_ARRAY: case GL_TEXTURE_2D_MULTISAMPLE_ARRAY: 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: return &rasterizer; } + [[nodiscard]] std::string GetDeviceVendor() const override { + return device.GetVendorName(); + } + private: /// Initializes the OpenGL state and creates persistent objects. void InitOpenGLObjects(); diff --git a/src/video_core/renderer_opengl/util_shaders.cpp b/src/video_core/renderer_opengl/util_shaders.cpp index abaf1ee6a..8fb5be393 100644 --- a/src/video_core/renderer_opengl/util_shaders.cpp +++ b/src/video_core/renderer_opengl/util_shaders.cpp @@ -261,9 +261,9 @@ void UtilShaders::CopyBC4(Image& dst_image, Image& src_image, std::span<const Im glUniform3ui(LOC_SRC_OFFSET, copy.src_offset.x, copy.src_offset.y, copy.src_offset.z); glUniform3ui(LOC_DST_OFFSET, copy.dst_offset.x, copy.dst_offset.y, copy.dst_offset.z); glBindImageTexture(BINDING_INPUT_IMAGE, src_image.StorageHandle(), - copy.src_subresource.base_level, GL_FALSE, 0, GL_READ_ONLY, GL_RG32UI); + copy.src_subresource.base_level, GL_TRUE, 0, GL_READ_ONLY, GL_RG32UI); glBindImageTexture(BINDING_OUTPUT_IMAGE, dst_image.StorageHandle(), - copy.dst_subresource.base_level, GL_FALSE, 0, GL_WRITE_ONLY, GL_RGBA8UI); + copy.dst_subresource.base_level, GL_TRUE, 0, GL_WRITE_ONLY, GL_RGBA8UI); glDispatchCompute(copy.extent.width, copy.extent.height, copy.extent.depth); } program_manager.RestoreGuestCompute(); diff --git a/src/video_core/renderer_vulkan/renderer_vulkan.cpp b/src/video_core/renderer_vulkan/renderer_vulkan.cpp index 3986eb172..bec3a81d9 100644 --- a/src/video_core/renderer_vulkan/renderer_vulkan.cpp +++ b/src/video_core/renderer_vulkan/renderer_vulkan.cpp @@ -103,7 +103,7 @@ RendererVulkan::RendererVulkan(Core::TelemetrySession& telemetry_session_, gpu(gpu_), library(OpenLibrary()), instance(CreateInstance(library, dld, VK_API_VERSION_1_1, render_window.GetWindowInfo().type, - true, Settings::values.renderer_debug)), + true, Settings::values.renderer_debug.GetValue())), debug_callback(Settings::values.renderer_debug ? CreateDebugCallback(instance) : nullptr), surface(CreateSurface(instance, render_window)), device(CreateDevice(instance, dld, *surface)), 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: return &rasterizer; } + [[nodiscard]] std::string GetDeviceVendor() const override { + return device.GetDriverName(); + } + private: void Report() const; diff --git a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp index 8cb65e588..0df4e1a1c 100644 --- a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp @@ -55,8 +55,9 @@ size_t BytesPerIndex(VkIndexType index_type) { template <typename T> std::array<T, 6> MakeQuadIndices(u32 quad, u32 first) { std::array<T, 6> indices{0, 1, 2, 0, 2, 3}; - std::ranges::transform(indices, indices.begin(), - [quad, first](u32 index) { return first + index + quad * 4; }); + for (T& index : indices) { + index = static_cast<T>(first + index + quad * 4); + } return indices; } } // Anonymous namespace diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index 1c9120170..e378a5679 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -251,7 +251,7 @@ RasterizerVulkan::RasterizerVulkan(Core::Frontend::EmuWindow& emu_window_, Tegra buffer_cache(*this, maxwell3d, kepler_compute, gpu_memory, cpu_memory_, buffer_cache_runtime), pipeline_cache(*this, gpu, maxwell3d, kepler_compute, gpu_memory, device, scheduler, descriptor_pool, update_descriptor_queue), - query_cache{*this, maxwell3d, gpu_memory, device, scheduler}, + query_cache{*this, maxwell3d, gpu_memory, device, scheduler}, accelerate_dma{buffer_cache}, fence_manager(*this, gpu, texture_cache, buffer_cache, query_cache, device, scheduler), wfi_event(device.GetLogical().CreateEvent()), async_shaders(emu_window_) { scheduler.SetQueryCache(query_cache); @@ -557,6 +557,13 @@ void RasterizerVulkan::UnmapMemory(VAddr addr, u64 size) { pipeline_cache.OnCPUWrite(addr, size); } +void RasterizerVulkan::ModifyGPUMemory(GPUVAddr addr, u64 size) { + { + std::scoped_lock lock{texture_cache.mutex}; + texture_cache.UnmapGPUMemory(addr, size); + } +} + void RasterizerVulkan::SignalSemaphore(GPUVAddr addr, u32 value) { if (!gpu.IsAsync()) { gpu_memory.Write<u32>(addr, value); @@ -573,6 +580,13 @@ void RasterizerVulkan::SignalSyncPoint(u32 value) { fence_manager.SignalSyncPoint(value); } +void RasterizerVulkan::SignalReference() { + if (!gpu.IsAsync()) { + return; + } + fence_manager.SignalOrdering(); +} + void RasterizerVulkan::ReleaseFences() { if (!gpu.IsAsync()) { return; @@ -605,6 +619,7 @@ void RasterizerVulkan::WaitForIdle() { cmdbuf.SetEvent(event, flags); cmdbuf.WaitEvents(event, flags, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, {}, {}, {}); }); + SignalReference(); } void RasterizerVulkan::FragmentBarrier() { @@ -645,6 +660,10 @@ bool RasterizerVulkan::AccelerateSurfaceCopy(const Tegra::Engines::Fermi2D::Surf return true; } +Tegra::Engines::AccelerateDMAInterface& RasterizerVulkan::AccessAccelerateDMA() { + return accelerate_dma; +} + bool RasterizerVulkan::AccelerateDisplay(const Tegra::FramebufferConfig& config, VAddr framebuffer_addr, u32 pixel_stride) { if (!framebuffer_addr) { @@ -683,6 +702,13 @@ void RasterizerVulkan::FlushWork() { draw_counter = 0; } +AccelerateDMA::AccelerateDMA(BufferCache& buffer_cache_) : buffer_cache{buffer_cache_} {} + +bool AccelerateDMA::BufferCopy(GPUVAddr src_address, GPUVAddr dest_address, u64 amount) { + std::scoped_lock lock{buffer_cache.mutex}; + return buffer_cache.DMACopy(src_address, dest_address, amount); +} + void RasterizerVulkan::SetupShaderDescriptors( const std::array<Shader*, Maxwell::MaxShaderProgram>& shaders, bool is_indexed) { image_view_indices.clear(); diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.h b/src/video_core/renderer_vulkan/vk_rasterizer.h index cb8c5c279..3a78de258 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.h +++ b/src/video_core/renderer_vulkan/vk_rasterizer.h @@ -13,6 +13,7 @@ #include <boost/container/static_vector.hpp> #include "common/common_types.h" +#include "video_core/engines/maxwell_dma.h" #include "video_core/rasterizer_accelerated.h" #include "video_core/rasterizer_interface.h" #include "video_core/renderer_vulkan/blit_image.h" @@ -49,6 +50,16 @@ struct VKScreenInfo; class StateTracker; +class AccelerateDMA : public Tegra::Engines::AccelerateDMAInterface { +public: + explicit AccelerateDMA(BufferCache& buffer_cache); + + bool BufferCopy(GPUVAddr start_address, GPUVAddr end_address, u64 amount) override; + +private: + BufferCache& buffer_cache; +}; + class RasterizerVulkan final : public VideoCore::RasterizerAccelerated { public: explicit RasterizerVulkan(Core::Frontend::EmuWindow& emu_window_, Tegra::GPU& gpu_, @@ -72,8 +83,10 @@ public: void OnCPUWrite(VAddr addr, u64 size) override; void SyncGuestHost() override; void UnmapMemory(VAddr addr, u64 size) override; + void ModifyGPUMemory(GPUVAddr addr, u64 size) override; void SignalSemaphore(GPUVAddr addr, u32 value) override; void SignalSyncPoint(u32 value) override; + void SignalReference() override; void ReleaseFences() override; void FlushAndInvalidateRegion(VAddr addr, u64 size) override; void WaitForIdle() override; @@ -84,6 +97,7 @@ public: bool AccelerateSurfaceCopy(const Tegra::Engines::Fermi2D::Surface& src, const Tegra::Engines::Fermi2D::Surface& dst, const Tegra::Engines::Fermi2D::Config& copy_config) override; + Tegra::Engines::AccelerateDMAInterface& AccessAccelerateDMA() override; bool AccelerateDisplay(const Tegra::FramebufferConfig& config, VAddr framebuffer_addr, u32 pixel_stride) override; @@ -184,6 +198,7 @@ private: BufferCache buffer_cache; VKPipelineCache pipeline_cache; VKQueryCache query_cache; + AccelerateDMA accelerate_dma; VKFenceManager fence_manager; vk::Event wfi_event; diff --git a/src/video_core/texture_cache/image_base.cpp b/src/video_core/texture_cache/image_base.cpp index ad69d32d1..6052d148a 100644 --- a/src/video_core/texture_cache/image_base.cpp +++ b/src/video_core/texture_cache/image_base.cpp @@ -69,6 +69,9 @@ ImageBase::ImageBase(const ImageInfo& info_, GPUVAddr gpu_addr_, VAddr cpu_addr_ } } +ImageMapView::ImageMapView(GPUVAddr gpu_addr_, VAddr cpu_addr_, size_t size_, ImageId image_id_) + : gpu_addr{gpu_addr_}, cpu_addr{cpu_addr_}, size{size_}, image_id{image_id_} {} + std::optional<SubresourceBase> ImageBase::TryFindBase(GPUVAddr other_addr) const noexcept { if (other_addr < gpu_addr) { // Subresource address can't be lower than the base @@ -82,7 +85,7 @@ std::optional<SubresourceBase> ImageBase::TryFindBase(GPUVAddr other_addr) const if (info.type != ImageType::e3D) { const auto [layer, mip_offset] = LayerMipOffset(diff, info.layer_stride); const auto end = mip_level_offsets.begin() + info.resources.levels; - const auto it = std::find(mip_level_offsets.begin(), end, mip_offset); + const auto it = std::find(mip_level_offsets.begin(), end, static_cast<u32>(mip_offset)); if (layer > info.resources.layers || it == end) { return std::nullopt; } diff --git a/src/video_core/texture_cache/image_base.h b/src/video_core/texture_cache/image_base.h index e326cab71..ff1feda9b 100644 --- a/src/video_core/texture_cache/image_base.h +++ b/src/video_core/texture_cache/image_base.h @@ -25,12 +25,14 @@ enum class ImageFlagBits : u32 { Strong = 1 << 5, ///< Exists in the image table, the dimensions are can be trusted Registered = 1 << 6, ///< True when the image is registered Picked = 1 << 7, ///< Temporary flag to mark the image as picked + Remapped = 1 << 8, ///< Image has been remapped. + Sparse = 1 << 9, ///< Image has non continous submemory. // Garbage Collection Flags - BadOverlap = 1 << 8, ///< This image overlaps other but doesn't fit, has higher - ///< garbage collection priority - Alias = 1 << 9, ///< This image has aliases and has priority on garbage - ///< collection + BadOverlap = 1 << 10, ///< This image overlaps other but doesn't fit, has higher + ///< garbage collection priority + Alias = 1 << 11, ///< This image has aliases and has priority on garbage + ///< collection }; DECLARE_ENUM_FLAG_OPERATORS(ImageFlagBits) @@ -57,6 +59,12 @@ struct ImageBase { return cpu_addr < overlap_end && overlap_cpu_addr < cpu_addr_end; } + [[nodiscard]] bool OverlapsGPU(GPUVAddr overlap_gpu_addr, size_t overlap_size) const noexcept { + const VAddr overlap_end = overlap_gpu_addr + overlap_size; + const GPUVAddr gpu_addr_end = gpu_addr + guest_size_bytes; + return gpu_addr < overlap_end && overlap_gpu_addr < gpu_addr_end; + } + void CheckBadOverlapState(); void CheckAliasState(); @@ -84,6 +92,29 @@ struct ImageBase { std::vector<AliasedImage> aliased_images; std::vector<ImageId> overlapping_images; + ImageMapId map_view_id{}; +}; + +struct ImageMapView { + explicit ImageMapView(GPUVAddr gpu_addr, VAddr cpu_addr, size_t size, ImageId image_id); + + [[nodiscard]] bool Overlaps(VAddr overlap_cpu_addr, size_t overlap_size) const noexcept { + const VAddr overlap_end = overlap_cpu_addr + overlap_size; + const VAddr cpu_addr_end = cpu_addr + size; + return cpu_addr < overlap_end && overlap_cpu_addr < cpu_addr_end; + } + + [[nodiscard]] bool OverlapsGPU(GPUVAddr overlap_gpu_addr, size_t overlap_size) const noexcept { + const GPUVAddr overlap_end = overlap_gpu_addr + overlap_size; + const GPUVAddr gpu_addr_end = gpu_addr + size; + return gpu_addr < overlap_end && overlap_gpu_addr < gpu_addr_end; + } + + GPUVAddr gpu_addr; + VAddr cpu_addr; + size_t size; + ImageId image_id; + bool picked{}; }; struct ImageAllocBase { diff --git a/src/video_core/texture_cache/texture_cache.h b/src/video_core/texture_cache/texture_cache.h index 84530a179..01de2d498 100644 --- a/src/video_core/texture_cache/texture_cache.h +++ b/src/video_core/texture_cache/texture_cache.h @@ -13,6 +13,7 @@ #include <span> #include <type_traits> #include <unordered_map> +#include <unordered_set> #include <utility> #include <vector> @@ -110,9 +111,6 @@ public: /// Notify the cache that a new frame has been queued void TickFrame(); - /// Runs the Garbage Collector. - void RunGarbageCollector(); - /// Return a constant reference to the given image view id [[nodiscard]] const ImageView& GetImageView(ImageViewId id) const noexcept; @@ -155,12 +153,13 @@ public: /// Remove images in a region void UnmapMemory(VAddr cpu_addr, size_t size); + /// Remove images in a region + void UnmapGPUMemory(GPUVAddr gpu_addr, size_t size); + /// Blit an image with the given parameters void BlitImage(const Tegra::Engines::Fermi2D::Surface& dst, const Tegra::Engines::Fermi2D::Surface& src, - const Tegra::Engines::Fermi2D::Config& copy, - std::optional<Region2D> src_region_override = {}, - std::optional<Region2D> dst_region_override = {}); + const Tegra::Engines::Fermi2D::Config& copy); /// Invalidate the contents of the color buffer index /// These contents become unspecified, the cache can assume aggressive optimizations. @@ -193,7 +192,22 @@ public: private: /// Iterate over all page indices in a range template <typename Func> - static void ForEachPage(VAddr addr, size_t size, Func&& func) { + static void ForEachCPUPage(VAddr addr, size_t size, Func&& func) { + static constexpr bool RETURNS_BOOL = std::is_same_v<std::invoke_result<Func, u64>, bool>; + const u64 page_end = (addr + size - 1) >> PAGE_BITS; + for (u64 page = addr >> PAGE_BITS; page <= page_end; ++page) { + if constexpr (RETURNS_BOOL) { + if (func(page)) { + break; + } + } else { + func(page); + } + } + } + + template <typename Func> + static void ForEachGPUPage(GPUVAddr addr, size_t size, Func&& func) { static constexpr bool RETURNS_BOOL = std::is_same_v<std::invoke_result<Func, u64>, bool>; const u64 page_end = (addr + size - 1) >> PAGE_BITS; for (u64 page = addr >> PAGE_BITS; page <= page_end; ++page) { @@ -207,6 +221,9 @@ private: } } + /// Runs the Garbage Collector. + void RunGarbageCollector(); + /// Fills image_view_ids in the image views in indices void FillImageViews(DescriptorTable<TICEntry>& table, std::span<ImageViewId> cached_image_view_ids, std::span<const u32> indices, @@ -220,7 +237,7 @@ private: FramebufferId GetFramebufferId(const RenderTargets& key); /// Refresh the contents (pixel data) of an image - void RefreshContents(Image& image); + void RefreshContents(Image& image, ImageId image_id); /// Upload data from guest to an image template <typename StagingBuffer> @@ -269,6 +286,16 @@ private: template <typename Func> void ForEachImageInRegion(VAddr cpu_addr, size_t size, Func&& func); + template <typename Func> + void ForEachImageInRegionGPU(GPUVAddr gpu_addr, size_t size, Func&& func); + + template <typename Func> + void ForEachSparseImageInRegion(GPUVAddr gpu_addr, size_t size, Func&& func); + + /// Iterates over all the images in a region calling func + template <typename Func> + void ForEachSparseSegment(ImageBase& image, Func&& func); + /// Find or create an image view in the given image with the passed parameters [[nodiscard]] ImageViewId FindOrEmplaceImageView(ImageId image_id, const ImageViewInfo& info); @@ -279,10 +306,10 @@ private: void UnregisterImage(ImageId image); /// Track CPU reads and writes for image - void TrackImage(ImageBase& image); + void TrackImage(ImageBase& image, ImageId image_id); /// Stop tracking CPU reads and writes for image - void UntrackImage(ImageBase& image); + void UntrackImage(ImageBase& image, ImageId image_id); /// Delete image from the cache void DeleteImage(ImageId image); @@ -340,7 +367,13 @@ private: std::unordered_map<TSCEntry, SamplerId> samplers; std::unordered_map<RenderTargets, FramebufferId> framebuffers; - std::unordered_map<u64, std::vector<ImageId>, IdentityHash<u64>> page_table; + std::unordered_map<u64, std::vector<ImageMapId>, IdentityHash<u64>> page_table; + std::unordered_map<u64, std::vector<ImageId>, IdentityHash<u64>> gpu_page_table; + std::unordered_map<u64, std::vector<ImageId>, IdentityHash<u64>> sparse_page_table; + + std::unordered_map<ImageId, std::vector<ImageViewId>> sparse_views; + + VAddr virtual_invalid_space{}; bool has_deleted_images = false; u64 total_used_memory = 0; @@ -349,6 +382,7 @@ private: u64 critical_memory; SlotVector<Image> slot_images; + SlotVector<ImageMapView> slot_map_views; SlotVector<ImageView> slot_image_views; SlotVector<ImageAlloc> slot_image_allocs; SlotVector<Sampler> slot_samplers; @@ -459,7 +493,7 @@ void TextureCache<P>::RunGarbageCollector() { } } if (True(image->flags & ImageFlagBits::Tracked)) { - UntrackImage(*image); + UntrackImage(*image, image_id); } UnregisterImage(image_id); DeleteImage(image_id); @@ -658,7 +692,9 @@ void TextureCache<P>::WriteMemory(VAddr cpu_addr, size_t size) { return; } image.flags |= ImageFlagBits::CpuModified; - UntrackImage(image); + if (True(image.flags & ImageFlagBits::Tracked)) { + UntrackImage(image, image_id); + } }); } @@ -695,7 +731,7 @@ void TextureCache<P>::UnmapMemory(VAddr cpu_addr, size_t size) { for (const ImageId id : deleted_images) { Image& image = slot_images[id]; if (True(image.flags & ImageFlagBits::Tracked)) { - UntrackImage(image); + UntrackImage(image, id); } UnregisterImage(id); DeleteImage(id); @@ -703,11 +739,26 @@ void TextureCache<P>::UnmapMemory(VAddr cpu_addr, size_t size) { } template <class P> +void TextureCache<P>::UnmapGPUMemory(GPUVAddr gpu_addr, size_t size) { + std::vector<ImageId> deleted_images; + ForEachImageInRegionGPU(gpu_addr, size, + [&](ImageId id, Image&) { deleted_images.push_back(id); }); + for (const ImageId id : deleted_images) { + Image& image = slot_images[id]; + if (True(image.flags & ImageFlagBits::Remapped)) { + continue; + } + image.flags |= ImageFlagBits::Remapped; + if (True(image.flags & ImageFlagBits::Tracked)) { + UntrackImage(image, id); + } + } +} + +template <class P> void TextureCache<P>::BlitImage(const Tegra::Engines::Fermi2D::Surface& dst, const Tegra::Engines::Fermi2D::Surface& src, - const Tegra::Engines::Fermi2D::Config& copy, - std::optional<Region2D> src_override, - std::optional<Region2D> dst_override) { + const Tegra::Engines::Fermi2D::Config& copy) { const BlitImages images = GetBlitImages(dst, src); const ImageId dst_id = images.dst_id; const ImageId src_id = images.src_id; @@ -718,47 +769,25 @@ void TextureCache<P>::BlitImage(const Tegra::Engines::Fermi2D::Surface& dst, const ImageBase& src_image = slot_images[src_id]; // TODO: Deduplicate - const std::optional dst_base = dst_image.TryFindBase(dst.Address()); - const SubresourceRange dst_range{.base = dst_base.value(), .extent = {1, 1}}; - const ImageViewInfo dst_view_info(ImageViewType::e2D, images.dst_format, dst_range); - const auto [dst_framebuffer_id, dst_view_id] = RenderTargetFromImage(dst_id, dst_view_info); - const auto [src_samples_x, src_samples_y] = SamplesLog2(src_image.info.num_samples); - - // out of bounds texture blit checking - const bool use_override = src_override.has_value(); - const s32 src_x0 = copy.src_x0 >> src_samples_x; - s32 src_x1 = use_override ? src_override->end.x : copy.src_x1 >> src_samples_x; - const s32 src_y0 = copy.src_y0 >> src_samples_y; - const s32 src_y1 = copy.src_y1 >> src_samples_y; - - const auto src_width = static_cast<s32>(src_image.info.size.width); - const bool width_oob = src_x1 > src_width; - const auto width_diff = width_oob ? src_x1 - src_width : 0; - if (width_oob) { - src_x1 = src_width; - } - - const Region2D src_dimensions{ - Offset2D{.x = src_x0, .y = src_y0}, - Offset2D{.x = src_x1, .y = src_y1}, - }; - const auto src_region = use_override ? *src_override : src_dimensions; - const std::optional src_base = src_image.TryFindBase(src.Address()); const SubresourceRange src_range{.base = src_base.value(), .extent = {1, 1}}; const ImageViewInfo src_view_info(ImageViewType::e2D, images.src_format, src_range); const auto [src_framebuffer_id, src_view_id] = RenderTargetFromImage(src_id, src_view_info); - const auto [dst_samples_x, dst_samples_y] = SamplesLog2(dst_image.info.num_samples); + const auto [src_samples_x, src_samples_y] = SamplesLog2(src_image.info.num_samples); + const Region2D src_region{ + Offset2D{.x = copy.src_x0 >> src_samples_x, .y = copy.src_y0 >> src_samples_y}, + Offset2D{.x = copy.src_x1 >> src_samples_x, .y = copy.src_y1 >> src_samples_y}, + }; - const s32 dst_x0 = copy.dst_x0 >> dst_samples_x; - const s32 dst_x1 = copy.dst_x1 >> dst_samples_x; - const s32 dst_y0 = copy.dst_y0 >> dst_samples_y; - const s32 dst_y1 = copy.dst_y1 >> dst_samples_y; - const Region2D dst_dimensions{ - Offset2D{.x = dst_x0, .y = dst_y0}, - Offset2D{.x = dst_x1 - width_diff, .y = dst_y1}, + const std::optional dst_base = dst_image.TryFindBase(dst.Address()); + const SubresourceRange dst_range{.base = dst_base.value(), .extent = {1, 1}}; + const ImageViewInfo dst_view_info(ImageViewType::e2D, images.dst_format, dst_range); + const auto [dst_framebuffer_id, dst_view_id] = RenderTargetFromImage(dst_id, dst_view_info); + const auto [dst_samples_x, dst_samples_y] = SamplesLog2(dst_image.info.num_samples); + const Region2D dst_region{ + Offset2D{.x = copy.dst_x0 >> dst_samples_x, .y = copy.dst_y0 >> dst_samples_y}, + Offset2D{.x = copy.dst_x1 >> dst_samples_x, .y = copy.dst_y1 >> dst_samples_y}, }; - const auto dst_region = use_override ? *dst_override : dst_dimensions; // Always call this after src_framebuffer_id was queried, as the address might be invalidated. Framebuffer* const dst_framebuffer = &slot_framebuffers[dst_framebuffer_id]; @@ -775,21 +804,6 @@ void TextureCache<P>::BlitImage(const Tegra::Engines::Fermi2D::Surface& dst, runtime.BlitImage(dst_framebuffer, dst_view, src_view, dst_region, src_region, copy.filter, copy.operation); } - - if (width_oob) { - // Continue copy of the oob region of the texture on the next row - auto oob_src = src; - oob_src.height++; - const Region2D src_region_override{ - Offset2D{.x = 0, .y = src_y0 + 1}, - Offset2D{.x = width_diff, .y = src_y1 + 1}, - }; - const Region2D dst_region_override{ - Offset2D{.x = dst_x1 - width_diff, .y = dst_y0}, - Offset2D{.x = dst_x1, .y = dst_y1}, - }; - BlitImage(dst, oob_src, copy, src_region_override, dst_region_override); - } } template <class P> @@ -833,9 +847,10 @@ typename P::ImageView* TextureCache<P>::TryFindFramebufferImageView(VAddr cpu_ad if (it == page_table.end()) { return nullptr; } - const auto& image_ids = it->second; - for (const ImageId image_id : image_ids) { - const ImageBase& image = slot_images[image_id]; + const auto& image_map_ids = it->second; + for (const ImageMapId map_id : image_map_ids) { + const ImageMapView& map = slot_map_views[map_id]; + const ImageBase& image = slot_images[map.image_id]; if (image.cpu_addr != cpu_addr) { continue; } @@ -915,13 +930,13 @@ bool TextureCache<P>::IsRegionGpuModified(VAddr addr, size_t size) { } template <class P> -void TextureCache<P>::RefreshContents(Image& image) { +void TextureCache<P>::RefreshContents(Image& image, ImageId image_id) { if (False(image.flags & ImageFlagBits::CpuModified)) { // Only upload modified images return; } image.flags &= ~ImageFlagBits::CpuModified; - TrackImage(image); + TrackImage(image, image_id); if (image.info.num_samples > 1) { LOG_WARNING(HW_GPU, "MSAA image uploads are not implemented"); @@ -958,7 +973,7 @@ void TextureCache<P>::UploadImageContents(Image& image, StagingBuffer& staging) template <class P> ImageViewId TextureCache<P>::FindImageView(const TICEntry& config) { - if (!IsValidAddress(gpu_memory, config)) { + if (!IsValidEntry(gpu_memory, config)) { return NULL_IMAGE_VIEW_ID; } const auto [pair, is_new] = image_views.try_emplace(config); @@ -1000,14 +1015,20 @@ ImageId TextureCache<P>::FindOrInsertImage(const ImageInfo& info, GPUVAddr gpu_a template <class P> ImageId TextureCache<P>::FindImage(const ImageInfo& info, GPUVAddr gpu_addr, RelaxedOptions options) { - const std::optional<VAddr> cpu_addr = gpu_memory.GpuToCpuAddress(gpu_addr); + std::optional<VAddr> cpu_addr = gpu_memory.GpuToCpuAddress(gpu_addr); if (!cpu_addr) { - return ImageId{}; + cpu_addr = gpu_memory.GpuToCpuAddress(gpu_addr, CalculateGuestSizeInBytes(info)); + if (!cpu_addr) { + return ImageId{}; + } } const bool broken_views = runtime.HasBrokenTextureViewFormats(); const bool native_bgr = runtime.HasNativeBgr(); ImageId image_id; const auto lambda = [&](ImageId existing_image_id, ImageBase& existing_image) { + if (True(existing_image.flags & ImageFlagBits::Remapped)) { + return false; + } if (info.type == ImageType::Linear || existing_image.info.type == ImageType::Linear) { const bool strict_size = False(options & RelaxedOptions::Size) && True(existing_image.flags & ImageFlagBits::Strong); @@ -1033,7 +1054,16 @@ ImageId TextureCache<P>::FindImage(const ImageInfo& info, GPUVAddr gpu_addr, template <class P> ImageId TextureCache<P>::InsertImage(const ImageInfo& info, GPUVAddr gpu_addr, RelaxedOptions options) { - const std::optional<VAddr> cpu_addr = gpu_memory.GpuToCpuAddress(gpu_addr); + std::optional<VAddr> cpu_addr = gpu_memory.GpuToCpuAddress(gpu_addr); + if (!cpu_addr) { + const auto size = CalculateGuestSizeInBytes(info); + cpu_addr = gpu_memory.GpuToCpuAddress(gpu_addr, size); + if (!cpu_addr) { + const VAddr fake_addr = ~(1ULL << 40ULL) + virtual_invalid_space; + virtual_invalid_space += Common::AlignUp(size, 32); + cpu_addr = std::optional<VAddr>(fake_addr); + } + } ASSERT_MSG(cpu_addr, "Tried to insert an image to an invalid gpu_addr=0x{:x}", gpu_addr); const ImageId image_id = JoinImages(info, gpu_addr, *cpu_addr); const Image& image = slot_images[image_id]; @@ -1053,11 +1083,14 @@ ImageId TextureCache<P>::JoinImages(const ImageInfo& info, GPUVAddr gpu_addr, VA const bool broken_views = runtime.HasBrokenTextureViewFormats(); const bool native_bgr = runtime.HasNativeBgr(); std::vector<ImageId> overlap_ids; + std::unordered_set<ImageId> overlaps_found; std::vector<ImageId> left_aliased_ids; std::vector<ImageId> right_aliased_ids; + std::unordered_set<ImageId> ignore_textures; std::vector<ImageId> bad_overlap_ids; - ForEachImageInRegion(cpu_addr, size_bytes, [&](ImageId overlap_id, ImageBase& overlap) { - if (info.type != overlap.info.type) { + const auto region_check = [&](ImageId overlap_id, ImageBase& overlap) { + if (True(overlap.flags & ImageFlagBits::Remapped)) { + ignore_textures.insert(overlap_id); return; } if (info.type == ImageType::Linear) { @@ -1067,6 +1100,7 @@ ImageId TextureCache<P>::JoinImages(const ImageInfo& info, GPUVAddr gpu_addr, VA } return; } + overlaps_found.insert(overlap_id); static constexpr bool strict_size = true; const std::optional<OverlapResult> solution = ResolveOverlap( new_info, gpu_addr, cpu_addr, overlap, strict_size, broken_views, native_bgr); @@ -1090,12 +1124,40 @@ ImageId TextureCache<P>::JoinImages(const ImageInfo& info, GPUVAddr gpu_addr, VA bad_overlap_ids.push_back(overlap_id); overlap.flags |= ImageFlagBits::BadOverlap; } - }); + }; + ForEachImageInRegion(cpu_addr, size_bytes, region_check); + const auto region_check_gpu = [&](ImageId overlap_id, ImageBase& overlap) { + if (!overlaps_found.contains(overlap_id)) { + if (True(overlap.flags & ImageFlagBits::Remapped)) { + ignore_textures.insert(overlap_id); + } + if (overlap.gpu_addr == gpu_addr && overlap.guest_size_bytes == size_bytes) { + ignore_textures.insert(overlap_id); + } + } + }; + ForEachSparseImageInRegion(gpu_addr, size_bytes, region_check_gpu); const ImageId new_image_id = slot_images.insert(runtime, new_info, gpu_addr, cpu_addr); Image& new_image = slot_images[new_image_id]; + if (!gpu_memory.IsContinousRange(new_image.gpu_addr, new_image.guest_size_bytes)) { + new_image.flags |= ImageFlagBits::Sparse; + } + + for (const ImageId overlap_id : ignore_textures) { + Image& overlap = slot_images[overlap_id]; + if (True(overlap.flags & ImageFlagBits::GpuModified)) { + UNIMPLEMENTED(); + } + if (True(overlap.flags & ImageFlagBits::Tracked)) { + UntrackImage(overlap, overlap_id); + } + UnregisterImage(overlap_id); + DeleteImage(overlap_id); + } + // TODO: Only upload what we need - RefreshContents(new_image); + RefreshContents(new_image, new_image_id); for (const ImageId overlap_id : overlap_ids) { Image& overlap = slot_images[overlap_id]; @@ -1107,7 +1169,7 @@ ImageId TextureCache<P>::JoinImages(const ImageInfo& info, GPUVAddr gpu_addr, VA runtime.CopyImage(new_image, overlap, copies); } if (True(overlap.flags & ImageFlagBits::Tracked)) { - UntrackImage(overlap); + UntrackImage(overlap, overlap_id); } UnregisterImage(overlap_id); DeleteImage(overlap_id); @@ -1242,7 +1304,8 @@ void TextureCache<P>::ForEachImageInRegion(VAddr cpu_addr, size_t size, Func&& f using FuncReturn = typename std::invoke_result<Func, ImageId, Image&>::type; static constexpr bool BOOL_BREAK = std::is_same_v<FuncReturn, bool>; boost::container::small_vector<ImageId, 32> images; - ForEachPage(cpu_addr, size, [this, &images, cpu_addr, size, func](u64 page) { + boost::container::small_vector<ImageMapId, 32> maps; + ForEachCPUPage(cpu_addr, size, [this, &images, &maps, cpu_addr, size, func](u64 page) { const auto it = page_table.find(page); if (it == page_table.end()) { if constexpr (BOOL_BREAK) { @@ -1251,12 +1314,105 @@ void TextureCache<P>::ForEachImageInRegion(VAddr cpu_addr, size_t size, Func&& f return; } } + for (const ImageMapId map_id : it->second) { + ImageMapView& map = slot_map_views[map_id]; + if (map.picked) { + continue; + } + if (!map.Overlaps(cpu_addr, size)) { + continue; + } + map.picked = true; + maps.push_back(map_id); + Image& image = slot_images[map.image_id]; + if (True(image.flags & ImageFlagBits::Picked)) { + continue; + } + image.flags |= ImageFlagBits::Picked; + images.push_back(map.image_id); + if constexpr (BOOL_BREAK) { + if (func(map.image_id, image)) { + return true; + } + } else { + func(map.image_id, image); + } + } + if constexpr (BOOL_BREAK) { + return false; + } + }); + for (const ImageId image_id : images) { + slot_images[image_id].flags &= ~ImageFlagBits::Picked; + } + for (const ImageMapId map_id : maps) { + slot_map_views[map_id].picked = false; + } +} + +template <class P> +template <typename Func> +void TextureCache<P>::ForEachImageInRegionGPU(GPUVAddr gpu_addr, size_t size, Func&& func) { + using FuncReturn = typename std::invoke_result<Func, ImageId, Image&>::type; + static constexpr bool BOOL_BREAK = std::is_same_v<FuncReturn, bool>; + boost::container::small_vector<ImageId, 8> images; + ForEachGPUPage(gpu_addr, size, [this, &images, gpu_addr, size, func](u64 page) { + const auto it = gpu_page_table.find(page); + if (it == gpu_page_table.end()) { + if constexpr (BOOL_BREAK) { + return false; + } else { + return; + } + } + for (const ImageId image_id : it->second) { + Image& image = slot_images[image_id]; + if (True(image.flags & ImageFlagBits::Picked)) { + continue; + } + if (!image.OverlapsGPU(gpu_addr, size)) { + continue; + } + image.flags |= ImageFlagBits::Picked; + images.push_back(image_id); + if constexpr (BOOL_BREAK) { + if (func(image_id, image)) { + return true; + } + } else { + func(image_id, image); + } + } + if constexpr (BOOL_BREAK) { + return false; + } + }); + for (const ImageId image_id : images) { + slot_images[image_id].flags &= ~ImageFlagBits::Picked; + } +} + +template <class P> +template <typename Func> +void TextureCache<P>::ForEachSparseImageInRegion(GPUVAddr gpu_addr, size_t size, Func&& func) { + using FuncReturn = typename std::invoke_result<Func, ImageId, Image&>::type; + static constexpr bool BOOL_BREAK = std::is_same_v<FuncReturn, bool>; + boost::container::small_vector<ImageId, 8> images; + ForEachGPUPage(gpu_addr, size, [this, &images, gpu_addr, size, func](u64 page) { + const auto it = sparse_page_table.find(page); + if (it == sparse_page_table.end()) { + if constexpr (BOOL_BREAK) { + return false; + } else { + return; + } + } for (const ImageId image_id : it->second) { Image& image = slot_images[image_id]; if (True(image.flags & ImageFlagBits::Picked)) { continue; } - if (!image.Overlaps(cpu_addr, size)) { + if (!image.OverlapsGPU(gpu_addr, size)) { continue; } image.flags |= ImageFlagBits::Picked; @@ -1279,6 +1435,27 @@ void TextureCache<P>::ForEachImageInRegion(VAddr cpu_addr, size_t size, Func&& f } template <class P> +template <typename Func> +void TextureCache<P>::ForEachSparseSegment(ImageBase& image, Func&& func) { + using FuncReturn = typename std::invoke_result<Func, GPUVAddr, VAddr, size_t>::type; + static constexpr bool RETURNS_BOOL = std::is_same_v<FuncReturn, bool>; + const auto segments = gpu_memory.GetSubmappedRange(image.gpu_addr, image.guest_size_bytes); + for (auto& segment : segments) { + const auto gpu_addr = segment.first; + const auto size = segment.second; + std::optional<VAddr> cpu_addr = gpu_memory.GpuToCpuAddress(gpu_addr); + ASSERT(cpu_addr); + if constexpr (RETURNS_BOOL) { + if (func(gpu_addr, *cpu_addr, size)) { + break; + } + } else { + func(gpu_addr, *cpu_addr, size); + } + } +} + +template <class P> ImageViewId TextureCache<P>::FindOrEmplaceImageView(ImageId image_id, const ImageViewInfo& info) { Image& image = slot_images[image_id]; if (const ImageViewId image_view_id = image.FindView(info); image_view_id) { @@ -1295,8 +1472,6 @@ void TextureCache<P>::RegisterImage(ImageId image_id) { ASSERT_MSG(False(image.flags & ImageFlagBits::Registered), "Trying to register an already registered image"); image.flags |= ImageFlagBits::Registered; - ForEachPage(image.cpu_addr, image.guest_size_bytes, - [this, image_id](u64 page) { page_table[page].push_back(image_id); }); u64 tentative_size = std::max(image.guest_size_bytes, image.unswizzled_size_bytes); if ((IsPixelFormatASTC(image.info.format) && True(image.flags & ImageFlagBits::AcceleratedUpload)) || @@ -1304,6 +1479,27 @@ void TextureCache<P>::RegisterImage(ImageId image_id) { tentative_size = EstimatedDecompressedSize(tentative_size, image.info.format); } total_used_memory += Common::AlignUp(tentative_size, 1024); + ForEachGPUPage(image.gpu_addr, image.guest_size_bytes, + [this, image_id](u64 page) { gpu_page_table[page].push_back(image_id); }); + if (False(image.flags & ImageFlagBits::Sparse)) { + auto map_id = + slot_map_views.insert(image.gpu_addr, image.cpu_addr, image.guest_size_bytes, image_id); + ForEachCPUPage(image.cpu_addr, image.guest_size_bytes, + [this, map_id](u64 page) { page_table[page].push_back(map_id); }); + image.map_view_id = map_id; + return; + } + std::vector<ImageViewId> sparse_maps{}; + ForEachSparseSegment( + image, [this, image_id, &sparse_maps](GPUVAddr gpu_addr, VAddr cpu_addr, size_t size) { + auto map_id = slot_map_views.insert(gpu_addr, cpu_addr, size, image_id); + ForEachCPUPage(cpu_addr, size, + [this, map_id](u64 page) { page_table[page].push_back(map_id); }); + sparse_maps.push_back(map_id); + }); + sparse_views.emplace(image_id, std::move(sparse_maps)); + ForEachGPUPage(image.gpu_addr, image.guest_size_bytes, + [this, image_id](u64 page) { sparse_page_table[page].push_back(image_id); }); } template <class P> @@ -1320,34 +1516,125 @@ void TextureCache<P>::UnregisterImage(ImageId image_id) { tentative_size = EstimatedDecompressedSize(tentative_size, image.info.format); } total_used_memory -= Common::AlignUp(tentative_size, 1024); - ForEachPage(image.cpu_addr, image.guest_size_bytes, [this, image_id](u64 page) { - const auto page_it = page_table.find(page); - if (page_it == page_table.end()) { - UNREACHABLE_MSG("Unregistering unregistered page=0x{:x}", page << PAGE_BITS); - return; - } - std::vector<ImageId>& image_ids = page_it->second; - const auto vector_it = std::ranges::find(image_ids, image_id); - if (vector_it == image_ids.end()) { - UNREACHABLE_MSG("Unregistering unregistered image in page=0x{:x}", page << PAGE_BITS); - return; - } - image_ids.erase(vector_it); + const auto& clear_page_table = + [this, image_id]( + u64 page, + std::unordered_map<u64, std::vector<ImageId>, IdentityHash<u64>>& selected_page_table) { + const auto page_it = selected_page_table.find(page); + if (page_it == selected_page_table.end()) { + UNREACHABLE_MSG("Unregistering unregistered page=0x{:x}", page << PAGE_BITS); + return; + } + std::vector<ImageId>& image_ids = page_it->second; + const auto vector_it = std::ranges::find(image_ids, image_id); + if (vector_it == image_ids.end()) { + UNREACHABLE_MSG("Unregistering unregistered image in page=0x{:x}", + page << PAGE_BITS); + return; + } + image_ids.erase(vector_it); + }; + ForEachGPUPage(image.gpu_addr, image.guest_size_bytes, + [this, &clear_page_table](u64 page) { clear_page_table(page, gpu_page_table); }); + if (False(image.flags & ImageFlagBits::Sparse)) { + const auto map_id = image.map_view_id; + ForEachCPUPage(image.cpu_addr, image.guest_size_bytes, [this, map_id](u64 page) { + const auto page_it = page_table.find(page); + if (page_it == page_table.end()) { + UNREACHABLE_MSG("Unregistering unregistered page=0x{:x}", page << PAGE_BITS); + return; + } + std::vector<ImageMapId>& image_map_ids = page_it->second; + const auto vector_it = std::ranges::find(image_map_ids, map_id); + if (vector_it == image_map_ids.end()) { + UNREACHABLE_MSG("Unregistering unregistered image in page=0x{:x}", + page << PAGE_BITS); + return; + } + image_map_ids.erase(vector_it); + }); + slot_map_views.erase(map_id); + return; + } + ForEachGPUPage(image.gpu_addr, image.guest_size_bytes, [this, &clear_page_table](u64 page) { + clear_page_table(page, sparse_page_table); }); + auto it = sparse_views.find(image_id); + ASSERT(it != sparse_views.end()); + auto& sparse_maps = it->second; + for (auto& map_view_id : sparse_maps) { + const auto& map_range = slot_map_views[map_view_id]; + const VAddr cpu_addr = map_range.cpu_addr; + const std::size_t size = map_range.size; + ForEachCPUPage(cpu_addr, size, [this, image_id](u64 page) { + const auto page_it = page_table.find(page); + if (page_it == page_table.end()) { + UNREACHABLE_MSG("Unregistering unregistered page=0x{:x}", page << PAGE_BITS); + return; + } + std::vector<ImageMapId>& image_map_ids = page_it->second; + auto vector_it = image_map_ids.begin(); + while (vector_it != image_map_ids.end()) { + ImageMapView& map = slot_map_views[*vector_it]; + if (map.image_id != image_id) { + vector_it++; + continue; + } + if (!map.picked) { + map.picked = true; + } + vector_it = image_map_ids.erase(vector_it); + } + }); + slot_map_views.erase(map_view_id); + } + sparse_views.erase(it); } template <class P> -void TextureCache<P>::TrackImage(ImageBase& image) { +void TextureCache<P>::TrackImage(ImageBase& image, ImageId image_id) { ASSERT(False(image.flags & ImageFlagBits::Tracked)); image.flags |= ImageFlagBits::Tracked; - rasterizer.UpdatePagesCachedCount(image.cpu_addr, image.guest_size_bytes, 1); + if (False(image.flags & ImageFlagBits::Sparse)) { + rasterizer.UpdatePagesCachedCount(image.cpu_addr, image.guest_size_bytes, 1); + return; + } + if (True(image.flags & ImageFlagBits::Registered)) { + auto it = sparse_views.find(image_id); + ASSERT(it != sparse_views.end()); + auto& sparse_maps = it->second; + for (auto& map_view_id : sparse_maps) { + const auto& map = slot_map_views[map_view_id]; + const VAddr cpu_addr = map.cpu_addr; + const std::size_t size = map.size; + rasterizer.UpdatePagesCachedCount(cpu_addr, size, 1); + } + return; + } + ForEachSparseSegment(image, + [this]([[maybe_unused]] GPUVAddr gpu_addr, VAddr cpu_addr, size_t size) { + rasterizer.UpdatePagesCachedCount(cpu_addr, size, 1); + }); } template <class P> -void TextureCache<P>::UntrackImage(ImageBase& image) { +void TextureCache<P>::UntrackImage(ImageBase& image, ImageId image_id) { ASSERT(True(image.flags & ImageFlagBits::Tracked)); image.flags &= ~ImageFlagBits::Tracked; - rasterizer.UpdatePagesCachedCount(image.cpu_addr, image.guest_size_bytes, -1); + if (False(image.flags & ImageFlagBits::Sparse)) { + rasterizer.UpdatePagesCachedCount(image.cpu_addr, image.guest_size_bytes, -1); + return; + } + ASSERT(True(image.flags & ImageFlagBits::Registered)); + auto it = sparse_views.find(image_id); + ASSERT(it != sparse_views.end()); + auto& sparse_maps = it->second; + for (auto& map_view_id : sparse_maps) { + const auto& map = slot_map_views[map_view_id]; + const VAddr cpu_addr = map.cpu_addr; + const std::size_t size = map.size; + rasterizer.UpdatePagesCachedCount(cpu_addr, size, -1); + } } template <class P> @@ -1489,10 +1776,10 @@ void TextureCache<P>::PrepareImage(ImageId image_id, bool is_modification, bool if (invalidate) { image.flags &= ~(ImageFlagBits::CpuModified | ImageFlagBits::GpuModified); if (False(image.flags & ImageFlagBits::Tracked)) { - TrackImage(image); + TrackImage(image, image_id); } } else { - RefreshContents(image); + RefreshContents(image, image_id); SynchronizeAliases(image_id); } if (is_modification) { diff --git a/src/video_core/texture_cache/types.h b/src/video_core/texture_cache/types.h index c9571f7e4..47a11cb2f 100644 --- a/src/video_core/texture_cache/types.h +++ b/src/video_core/texture_cache/types.h @@ -16,6 +16,7 @@ constexpr size_t MAX_MIP_LEVELS = 14; constexpr SlotId CORRUPT_ID{0xfffffffe}; using ImageId = SlotId; +using ImageMapId = SlotId; using ImageViewId = SlotId; using ImageAllocId = SlotId; using SamplerId = SlotId; @@ -132,8 +133,8 @@ struct BufferImageCopy { }; struct BufferCopy { - size_t src_offset; - size_t dst_offset; + u64 src_offset; + u64 dst_offset; size_t size; }; diff --git a/src/video_core/texture_cache/util.cpp b/src/video_core/texture_cache/util.cpp index 4efe042b6..c872517b8 100644 --- a/src/video_core/texture_cache/util.cpp +++ b/src/video_core/texture_cache/util.cpp @@ -394,7 +394,7 @@ template <u32 GOB_EXTENT> const s32 mip_offset = diff % layer_stride; const std::array offsets = CalculateMipLevelOffsets(new_info); const auto end = offsets.begin() + new_info.resources.levels; - const auto it = std::find(offsets.begin(), end, mip_offset); + const auto it = std::find(offsets.begin(), end, static_cast<u32>(mip_offset)); if (it == end) { // Mipmap is not aligned to any valid size return std::nullopt; @@ -664,6 +664,16 @@ LevelArray CalculateMipLevelOffsets(const ImageInfo& info) noexcept { return offsets; } +LevelArray CalculateMipLevelSizes(const ImageInfo& info) noexcept { + const u32 num_levels = info.resources.levels; + const LevelInfo level_info = MakeLevelInfo(info); + LevelArray sizes{}; + for (u32 level = 0; level < num_levels; ++level) { + sizes[level] = CalculateLevelSize(level_info, level); + } + return sizes; +} + std::vector<u32> CalculateSliceOffsets(const ImageInfo& info) { ASSERT(info.type == ImageType::e3D); std::vector<u32> offsets; @@ -776,14 +786,20 @@ std::vector<ImageCopy> MakeShrinkImageCopies(const ImageInfo& dst, const ImageIn return copies; } -bool IsValidAddress(const Tegra::MemoryManager& gpu_memory, const TICEntry& config) { - if (config.Address() == 0) { +bool IsValidEntry(const Tegra::MemoryManager& gpu_memory, const TICEntry& config) { + const GPUVAddr address = config.Address(); + if (address == 0) { return false; } - if (config.Address() > (u64(1) << 48)) { + if (address > (1ULL << 48)) { return false; } - return gpu_memory.GpuToCpuAddress(config.Address()).has_value(); + if (gpu_memory.GpuToCpuAddress(address).has_value()) { + return true; + } + const ImageInfo info{config}; + const size_t guest_size_bytes = CalculateGuestSizeInBytes(info); + return gpu_memory.GpuToCpuAddress(address, guest_size_bytes).has_value(); } std::vector<BufferImageCopy> UnswizzleImage(Tegra::MemoryManager& gpu_memory, GPUVAddr gpu_addr, diff --git a/src/video_core/texture_cache/util.h b/src/video_core/texture_cache/util.h index cdc5cbc75..766502908 100644 --- a/src/video_core/texture_cache/util.h +++ b/src/video_core/texture_cache/util.h @@ -40,6 +40,8 @@ struct OverlapResult { [[nodiscard]] LevelArray CalculateMipLevelOffsets(const ImageInfo& info) noexcept; +[[nodiscard]] LevelArray CalculateMipLevelSizes(const ImageInfo& info) noexcept; + [[nodiscard]] std::vector<u32> CalculateSliceOffsets(const ImageInfo& info); [[nodiscard]] std::vector<SubresourceBase> CalculateSliceSubresources(const ImageInfo& info); @@ -55,7 +57,7 @@ struct OverlapResult { const ImageInfo& src, SubresourceBase base); -[[nodiscard]] bool IsValidAddress(const Tegra::MemoryManager& gpu_memory, const TICEntry& config); +[[nodiscard]] bool IsValidEntry(const Tegra::MemoryManager& gpu_memory, const TICEntry& config); [[nodiscard]] std::vector<BufferImageCopy> UnswizzleImage(Tegra::MemoryManager& gpu_memory, GPUVAddr gpu_addr, const ImageInfo& info, diff --git a/src/video_core/textures/astc.cpp b/src/video_core/textures/astc.cpp index 7b756ba41..3ab500760 100644 --- a/src/video_core/textures/astc.cpp +++ b/src/video_core/textures/astc.cpp @@ -1365,8 +1365,8 @@ static void DecompressBlock(std::span<const u8, 16> inBuf, const u32 blockWidth, // each partition. // Determine partitions, partition index, and color endpoint modes - s32 planeIdx = -1; - u32 partitionIndex; + u32 planeIdx{UINT32_MAX}; + u32 partitionIndex{}; u32 colorEndpointMode[4] = {0, 0, 0, 0}; // Define color data. diff --git a/src/video_core/vulkan_common/vulkan_device.cpp b/src/video_core/vulkan_common/vulkan_device.cpp index 707a8b8fb..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 return (supported_usage & wanted_usage) == wanted_usage; } +std::string Device::GetDriverName() const { + switch (driver_id) { + case VK_DRIVER_ID_AMD_PROPRIETARY: + return "AMD"; + case VK_DRIVER_ID_AMD_OPEN_SOURCE: + return "AMDVLK"; + case VK_DRIVER_ID_MESA_RADV: + return "RADV"; + case VK_DRIVER_ID_NVIDIA_PROPRIETARY: + return "NVIDIA"; + case VK_DRIVER_ID_INTEL_PROPRIETARY_WINDOWS: + return "INTEL"; + case VK_DRIVER_ID_INTEL_OPEN_SOURCE_MESA: + return "ANV"; + case VK_DRIVER_ID_MESA_LLVMPIPE: + return "LAVAPIPE"; + default: + return vendor_name; + } +} + void Device::CheckSuitability(bool requires_swapchain) const { std::bitset<REQUIRED_EXTENSIONS.size()> available_extensions; bool has_swapchain = false; @@ -821,9 +842,9 @@ void Device::CollectTelemetryParameters() { void Device::CollectPhysicalMemoryInfo() { const auto mem_properties = physical.GetMemoryProperties(); - const std::size_t num_properties = mem_properties.memoryHeapCount; + const size_t num_properties = mem_properties.memoryHeapCount; device_access_memory = 0; - for (std::size_t element = 0; element < num_properties; element++) { + for (size_t element = 0; element < num_properties; ++element) { if ((mem_properties.memoryHeaps[element].flags & VK_MEMORY_HEAP_DEVICE_LOCAL_BIT) != 0) { device_access_memory += mem_properties.memoryHeaps[element].size; } diff --git a/src/video_core/vulkan_common/vulkan_device.h b/src/video_core/vulkan_common/vulkan_device.h index a1aba973b..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: /// Reports a shader to Nsight Aftermath. void SaveShader(const std::vector<u32>& spirv) const; + /// Returns the name of the VkDriverId reported from Vulkan. + std::string GetDriverName() const; + /// Returns the dispatch loader with direct function pointers of the device. const vk::DeviceDispatch& GetDispatchLoader() const { return dld; @@ -264,21 +267,22 @@ private: bool IsFormatSupported(VkFormat wanted_format, VkFormatFeatureFlags wanted_usage, FormatType format_type) const; - VkInstance instance; ///< Vulkan instance. - vk::DeviceDispatch dld; ///< Device function pointers. - vk::PhysicalDevice physical; ///< Physical device. - VkPhysicalDeviceProperties properties; ///< Device properties. - vk::Device logical; ///< Logical device. - vk::Queue graphics_queue; ///< Main graphics queue. - vk::Queue present_queue; ///< Main present queue. - u32 instance_version{}; ///< Vulkan onstance version. - u32 graphics_family{}; ///< Main graphics queue family index. - u32 present_family{}; ///< Main present queue family index. - VkDriverIdKHR driver_id{}; ///< Driver ID. - VkShaderStageFlags guest_warp_stages{}; ///< Stages where the guest warp size can be forced.ed - bool is_optimal_astc_supported{}; ///< Support for native ASTC. - bool is_float16_supported{}; ///< Support for float16 arithmetics. - bool is_warp_potentially_bigger{}; ///< Host warp size can be bigger than guest. + VkInstance instance; ///< Vulkan instance. + vk::DeviceDispatch dld; ///< Device function pointers. + vk::PhysicalDevice physical; ///< Physical device. + VkPhysicalDeviceProperties properties; ///< Device properties. + vk::Device logical; ///< Logical device. + vk::Queue graphics_queue; ///< Main graphics queue. + vk::Queue present_queue; ///< Main present queue. + u32 instance_version{}; ///< Vulkan onstance version. + u32 graphics_family{}; ///< Main graphics queue family index. + u32 present_family{}; ///< Main present queue family index. + VkDriverIdKHR driver_id{}; ///< Driver ID. + VkShaderStageFlags guest_warp_stages{}; ///< Stages where the guest warp size can be forced. + u64 device_access_memory{}; ///< Total size of device local memory in bytes. + bool is_optimal_astc_supported{}; ///< Support for native ASTC. + bool is_float16_supported{}; ///< Support for float16 arithmetics. + bool is_warp_potentially_bigger{}; ///< Host warp size can be bigger than guest. bool is_formatless_image_load_supported{}; ///< Support for shader image read without format. bool is_shader_storage_image_multisample{}; ///< Support for image operations on MSAA images. bool is_blit_depth_stencil_supported{}; ///< Support for blitting from and to depth stencil. @@ -309,8 +313,6 @@ private: /// Nsight Aftermath GPU crash tracker std::unique_ptr<NsightAftermathTracker> nsight_aftermath_tracker; - - u64 device_access_memory; }; } // namespace Vulkan diff --git a/src/web_service/web_backend.cpp b/src/web_service/web_backend.cpp index e04f7dfc6..b1e02c57a 100644 --- a/src/web_service/web_backend.cpp +++ b/src/web_service/web_backend.cpp @@ -8,7 +8,17 @@ #include <string> #include <fmt/format.h> + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#ifndef __clang__ +#pragma GCC diagnostic ignored "-Wmaybe-uninitialized" +#endif +#endif #include <httplib.h> +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif #include "common/logging/log.h" #include "web_service/web_backend.h" diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt index 634fe66a5..cb4bdcc7e 100644 --- a/src/yuzu/CMakeLists.txt +++ b/src/yuzu/CMakeLists.txt @@ -15,18 +15,19 @@ add_executable(yuzu about_dialog.cpp about_dialog.h aboutdialog.ui - applets/controller.cpp - applets/controller.h - applets/controller.ui - applets/error.cpp - applets/error.h - applets/profile_select.cpp - applets/profile_select.h - applets/software_keyboard.cpp - applets/software_keyboard.h - applets/software_keyboard.ui - applets/web_browser.cpp - applets/web_browser.h + applets/qt_controller.cpp + applets/qt_controller.h + applets/qt_controller.ui + applets/qt_error.cpp + applets/qt_error.h + applets/qt_profile_select.cpp + applets/qt_profile_select.h + applets/qt_software_keyboard.cpp + applets/qt_software_keyboard.h + applets/qt_software_keyboard.ui + applets/qt_web_browser.cpp + applets/qt_web_browser.h + applets/qt_web_browser_scripts.h bootmanager.cpp bootmanager.h compatdb.ui @@ -52,6 +53,9 @@ add_executable(yuzu configuration/configure_debug_controller.cpp configuration/configure_debug_controller.h configuration/configure_debug_controller.ui + configuration/configure_debug_tab.cpp + configuration/configure_debug_tab.h + configuration/configure_debug_tab.ui configuration/configure_dialog.cpp configuration/configure_dialog.h configuration/configure_filesystem.cpp diff --git a/src/yuzu/applets/controller.cpp b/src/yuzu/applets/qt_controller.cpp index 836d90fda..97106d2cc 100644 --- a/src/yuzu/applets/controller.cpp +++ b/src/yuzu/applets/qt_controller.cpp @@ -12,8 +12,8 @@ #include "core/hle/service/hid/controllers/npad.h" #include "core/hle/service/hid/hid.h" #include "core/hle/service/sm/sm.h" -#include "ui_controller.h" -#include "yuzu/applets/controller.h" +#include "ui_qt_controller.h" +#include "yuzu/applets/qt_controller.h" #include "yuzu/configuration/configure_input.h" #include "yuzu/configuration/configure_input_profile_dialog.h" #include "yuzu/configuration/configure_motion_touch.h" diff --git a/src/yuzu/applets/controller.h b/src/yuzu/applets/qt_controller.h index 9b57aea1a..9b57aea1a 100644 --- a/src/yuzu/applets/controller.h +++ b/src/yuzu/applets/qt_controller.h diff --git a/src/yuzu/applets/controller.ui b/src/yuzu/applets/qt_controller.ui index c8cb6bcf3..c8cb6bcf3 100644 --- a/src/yuzu/applets/controller.ui +++ b/src/yuzu/applets/qt_controller.ui diff --git a/src/yuzu/applets/error.cpp b/src/yuzu/applets/qt_error.cpp index 085688cd4..45cf64603 100644 --- a/src/yuzu/applets/error.cpp +++ b/src/yuzu/applets/qt_error.cpp @@ -4,7 +4,7 @@ #include <QDateTime> #include "core/hle/lock.h" -#include "yuzu/applets/error.h" +#include "yuzu/applets/qt_error.h" #include "yuzu/main.h" QtErrorDisplay::QtErrorDisplay(GMainWindow& parent) { diff --git a/src/yuzu/applets/error.h b/src/yuzu/applets/qt_error.h index 8bd895a32..8bd895a32 100644 --- a/src/yuzu/applets/error.h +++ b/src/yuzu/applets/qt_error.h diff --git a/src/yuzu/applets/profile_select.cpp b/src/yuzu/applets/qt_profile_select.cpp index 62fd1141c..a56638e21 100644 --- a/src/yuzu/applets/profile_select.cpp +++ b/src/yuzu/applets/qt_profile_select.cpp @@ -14,7 +14,7 @@ #include "common/string_util.h" #include "core/constants.h" #include "core/hle/lock.h" -#include "yuzu/applets/profile_select.h" +#include "yuzu/applets/qt_profile_select.h" #include "yuzu/main.h" namespace { diff --git a/src/yuzu/applets/profile_select.h b/src/yuzu/applets/qt_profile_select.h index 4e9037488..4e9037488 100644 --- a/src/yuzu/applets/profile_select.h +++ b/src/yuzu/applets/qt_profile_select.h diff --git a/src/yuzu/applets/software_keyboard.cpp b/src/yuzu/applets/qt_software_keyboard.cpp index aa453a79f..848801cec 100644 --- a/src/yuzu/applets/software_keyboard.cpp +++ b/src/yuzu/applets/qt_software_keyboard.cpp @@ -11,8 +11,8 @@ #include "common/string_util.h" #include "core/core.h" #include "core/frontend/input_interpreter.h" -#include "ui_software_keyboard.h" -#include "yuzu/applets/software_keyboard.h" +#include "ui_qt_software_keyboard.h" +#include "yuzu/applets/qt_software_keyboard.h" #include "yuzu/main.h" #include "yuzu/util/overlay_dialog.h" diff --git a/src/yuzu/applets/software_keyboard.h b/src/yuzu/applets/qt_software_keyboard.h index 1a03c098c..1a03c098c 100644 --- a/src/yuzu/applets/software_keyboard.h +++ b/src/yuzu/applets/qt_software_keyboard.h diff --git a/src/yuzu/applets/software_keyboard.ui b/src/yuzu/applets/qt_software_keyboard.ui index b0a1fcde9..b0a1fcde9 100644 --- a/src/yuzu/applets/software_keyboard.ui +++ b/src/yuzu/applets/qt_software_keyboard.ui diff --git a/src/yuzu/applets/web_browser.cpp b/src/yuzu/applets/qt_web_browser.cpp index 34d3feb55..b112dd7b0 100644 --- a/src/yuzu/applets/web_browser.cpp +++ b/src/yuzu/applets/qt_web_browser.cpp @@ -17,8 +17,8 @@ #include "core/frontend/input_interpreter.h" #include "input_common/keyboard.h" #include "input_common/main.h" -#include "yuzu/applets/web_browser.h" -#include "yuzu/applets/web_browser_scripts.h" +#include "yuzu/applets/qt_web_browser.h" +#include "yuzu/applets/qt_web_browser_scripts.h" #include "yuzu/main.h" #include "yuzu/util/url_request_interceptor.h" diff --git a/src/yuzu/applets/web_browser.h b/src/yuzu/applets/qt_web_browser.h index 7ad07409f..7ad07409f 100644 --- a/src/yuzu/applets/web_browser.h +++ b/src/yuzu/applets/qt_web_browser.h diff --git a/src/yuzu/applets/web_browser_scripts.h b/src/yuzu/applets/qt_web_browser_scripts.h index 992837a85..992837a85 100644 --- a/src/yuzu/applets/web_browser_scripts.h +++ b/src/yuzu/applets/qt_web_browser_scripts.h diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp index 62bafc453..8c71ad5c1 100644 --- a/src/yuzu/configuration/config.cpp +++ b/src/yuzu/configuration/config.cpp @@ -272,6 +272,91 @@ void Config::Initialize(const std::string& config_name) { } } +/* {Read,Write}BasicSetting and WriteGlobalSetting templates must be defined here before their + * usages later in this file. This allows explicit definition of some types that don't work + * nicely with the general version. + */ + +// Explicit std::string definition: Qt can't implicitly convert a std::string to a QVariant, nor +// can it implicitly convert a QVariant back to a {std::,Q}string +template <> +void Config::ReadBasicSetting(Settings::BasicSetting<std::string>& setting) { + const QString name = QString::fromStdString(setting.GetLabel()); + const auto default_value = QString::fromStdString(setting.GetDefault()); + if (qt_config->value(name + QStringLiteral("/default"), false).toBool()) { + setting.SetValue(default_value.toStdString()); + } else { + setting.SetValue(qt_config->value(name, default_value).toString().toStdString()); + } +} + +template <typename Type> +void Config::ReadBasicSetting(Settings::BasicSetting<Type>& setting) { + const QString name = QString::fromStdString(setting.GetLabel()); + const Type default_value = setting.GetDefault(); + if (qt_config->value(name + QStringLiteral("/default"), false).toBool()) { + setting.SetValue(default_value); + } else { + setting.SetValue( + static_cast<QVariant>(qt_config->value(name, default_value)).value<Type>()); + } +} + +// Explicit std::string definition: Qt can't implicitly convert a std::string to a QVariant +template <> +void Config::WriteBasicSetting(const Settings::BasicSetting<std::string>& setting) { + const QString name = QString::fromStdString(setting.GetLabel()); + const std::string& value = setting.GetValue(); + qt_config->setValue(name + QStringLiteral("/default"), value == setting.GetDefault()); + qt_config->setValue(name, QString::fromStdString(value)); +} + +// Explicit float definition: use a double as Qt doesn't write legible floats to config files +template <> +void Config::WriteBasicSetting(const Settings::BasicSetting<float>& setting) { + const QString name = QString::fromStdString(setting.GetLabel()); + const double value = setting.GetValue(); + qt_config->setValue(name + QStringLiteral("/default"), + setting.GetValue() == setting.GetDefault()); + qt_config->setValue(name, value); +} + +template <typename Type> +void Config::WriteBasicSetting(const Settings::BasicSetting<Type>& setting) { + const QString name = QString::fromStdString(setting.GetLabel()); + const Type value = setting.GetValue(); + qt_config->setValue(name + QStringLiteral("/default"), value == setting.GetDefault()); + qt_config->setValue(name, value); +} + +// Explicit float definition: use a double as Qt doesn't write legible floats to config files +template <> +void Config::WriteGlobalSetting(const Settings::Setting<float>& setting) { + const QString name = QString::fromStdString(setting.GetLabel()); + const double value = setting.GetValue(global); + if (!global) { + qt_config->setValue(name + QStringLiteral("/use_global"), setting.UsingGlobal()); + } + if (global || !setting.UsingGlobal()) { + qt_config->setValue(name + QStringLiteral("/default"), + setting.GetValue(global) == setting.GetDefault()); + qt_config->setValue(name, value); + } +} + +template <typename Type> +void Config::WriteGlobalSetting(const Settings::Setting<Type>& setting) { + const QString name = QString::fromStdString(setting.GetLabel()); + const Type& value = setting.GetValue(global); + if (!global) { + qt_config->setValue(name + QStringLiteral("/use_global"), setting.UsingGlobal()); + } + if (global || !setting.UsingGlobal()) { + qt_config->setValue(name + QStringLiteral("/default"), value == setting.GetDefault()); + qt_config->setValue(name, value); + } +} + void Config::ReadPlayerValue(std::size_t player_index) { const QString player_prefix = [this, player_index] { if (type == ConfigType::InputProfile) { @@ -395,8 +480,7 @@ void Config::ReadPlayerValue(std::size_t player_index) { } void Config::ReadDebugValues() { - Settings::values.debug_pad_enabled = - ReadSetting(QStringLiteral("debug_pad_enabled"), false).toBool(); + ReadBasicSetting(Settings::values.debug_pad_enabled); for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) { const std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]); @@ -432,8 +516,7 @@ void Config::ReadDebugValues() { } void Config::ReadKeyboardValues() { - Settings::values.keyboard_enabled = - ReadSetting(QStringLiteral("keyboard_enabled"), false).toBool(); + ReadBasicSetting(Settings::values.keyboard_enabled); std::transform(default_keyboard_keys.begin(), default_keyboard_keys.end(), Settings::values.keyboard_keys.begin(), InputCommon::GenerateKeyboardParam); @@ -446,7 +529,7 @@ void Config::ReadKeyboardValues() { } void Config::ReadMouseValues() { - Settings::values.mouse_enabled = ReadSetting(QStringLiteral("mouse_enabled"), false).toBool(); + ReadBasicSetting(Settings::values.mouse_enabled); for (int i = 0; i < Settings::NativeMouseButton::NumMouseButtons; ++i) { const std::string default_param = @@ -481,18 +564,11 @@ void Config::ReadAudioValues() { qt_config->beginGroup(QStringLiteral("Audio")); if (global) { - Settings::values.sink_id = - ReadSetting(QStringLiteral("output_engine"), QStringLiteral("auto")) - .toString() - .toStdString(); - Settings::values.audio_device_id = - ReadSetting(QStringLiteral("output_device"), QStringLiteral("auto")) - .toString() - .toStdString(); + ReadBasicSetting(Settings::values.audio_device_id); + ReadBasicSetting(Settings::values.sink_id); } - ReadSettingGlobal(Settings::values.enable_audio_stretching, - QStringLiteral("enable_audio_stretching"), true); - ReadSettingGlobal(Settings::values.volume, QStringLiteral("volume"), 1); + ReadGlobalSetting(Settings::values.enable_audio_stretching); + ReadGlobalSetting(Settings::values.volume); qt_config->endGroup(); } @@ -509,13 +585,11 @@ void Config::ReadControlValues() { ReadTouchscreenValues(); ReadMotionTouchValues(); - Settings::values.emulate_analog_keyboard = - ReadSetting(QStringLiteral("emulate_analog_keyboard"), false).toBool(); + ReadBasicSetting(Settings::values.emulate_analog_keyboard); Settings::values.mouse_panning = false; - Settings::values.mouse_panning_sensitivity = - ReadSetting(QStringLiteral("mouse_panning_sensitivity"), 1).toFloat(); + ReadBasicSetting(Settings::values.mouse_panning_sensitivity); - ReadSettingGlobal(Settings::values.use_docked_mode, QStringLiteral("use_docked_mode"), true); + ReadGlobalSetting(Settings::values.use_docked_mode); // Disable docked mode if handheld is selected const auto controller_type = Settings::values.players.GetValue()[0].controller_type; @@ -523,11 +597,9 @@ void Config::ReadControlValues() { Settings::values.use_docked_mode.SetValue(false); } - ReadSettingGlobal(Settings::values.vibration_enabled, QStringLiteral("vibration_enabled"), - true); - ReadSettingGlobal(Settings::values.enable_accurate_vibrations, - QStringLiteral("enable_accurate_vibrations"), false); - ReadSettingGlobal(Settings::values.motion_enabled, QStringLiteral("motion_enabled"), true); + ReadGlobalSetting(Settings::values.vibration_enabled); + ReadGlobalSetting(Settings::values.enable_accurate_vibrations); + ReadGlobalSetting(Settings::values.motion_enabled); qt_config->endGroup(); } @@ -565,33 +637,19 @@ void Config::ReadMotionTouchValues() { } qt_config->endArray(); - Settings::values.motion_device = - ReadSetting(QStringLiteral("motion_device"), - QStringLiteral("engine:motion_emu,update_period:100,sensitivity:0.01")) - .toString() - .toStdString(); - Settings::values.touch_device = - ReadSetting(QStringLiteral("touch_device"), - QStringLiteral("min_x:100,min_y:50,max_x:1800,max_y:850")) - .toString() - .toStdString(); - Settings::values.use_touch_from_button = - ReadSetting(QStringLiteral("use_touch_from_button"), false).toBool(); - Settings::values.touch_from_button_map_index = - ReadSetting(QStringLiteral("touch_from_button_map"), 0).toInt(); - Settings::values.touch_from_button_map_index = - std::clamp(Settings::values.touch_from_button_map_index, 0, num_touch_from_button_maps - 1); - Settings::values.udp_input_servers = - ReadSetting(QStringLiteral("udp_input_servers"), - QString::fromUtf8(InputCommon::CemuhookUDP::DEFAULT_SRV)) - .toString() - .toStdString(); + ReadBasicSetting(Settings::values.motion_device); + ReadBasicSetting(Settings::values.touch_device); + ReadBasicSetting(Settings::values.use_touch_from_button); + ReadBasicSetting(Settings::values.touch_from_button_map_index); + Settings::values.touch_from_button_map_index = std::clamp( + Settings::values.touch_from_button_map_index.GetValue(), 0, num_touch_from_button_maps - 1); + ReadBasicSetting(Settings::values.udp_input_servers); } void Config::ReadCoreValues() { qt_config->beginGroup(QStringLiteral("Core")); - ReadSettingGlobal(Settings::values.use_multi_core, QStringLiteral("use_multi_core"), true); + ReadGlobalSetting(Settings::values.use_multi_core); qt_config->endGroup(); } @@ -599,7 +657,7 @@ void Config::ReadCoreValues() { void Config::ReadDataStorageValues() { qt_config->beginGroup(QStringLiteral("Data Storage")); - Settings::values.use_virtual_sd = ReadSetting(QStringLiteral("use_virtual_sd"), true).toBool(); + ReadBasicSetting(Settings::values.use_virtual_sd); FS::SetYuzuPath( FS::YuzuPath::NANDDir, qt_config @@ -628,12 +686,9 @@ void Config::ReadDataStorageValues() { QString::fromStdString(FS::GetYuzuPathString(FS::YuzuPath::DumpDir))) .toString() .toStdString()); - Settings::values.gamecard_inserted = - ReadSetting(QStringLiteral("gamecard_inserted"), false).toBool(); - Settings::values.gamecard_current_game = - ReadSetting(QStringLiteral("gamecard_current_game"), false).toBool(); - Settings::values.gamecard_path = - ReadSetting(QStringLiteral("gamecard_path"), QString{}).toString().toStdString(); + ReadBasicSetting(Settings::values.gamecard_inserted); + ReadBasicSetting(Settings::values.gamecard_current_game); + ReadBasicSetting(Settings::values.gamecard_path); qt_config->endGroup(); } @@ -644,34 +699,24 @@ void Config::ReadDebuggingValues() { // Intentionally not using the QT default setting as this is intended to be changed in the ini Settings::values.record_frame_times = qt_config->value(QStringLiteral("record_frame_times"), false).toBool(); - Settings::values.program_args = - ReadSetting(QStringLiteral("program_args"), QString{}).toString().toStdString(); - Settings::values.dump_exefs = ReadSetting(QStringLiteral("dump_exefs"), false).toBool(); - Settings::values.dump_nso = ReadSetting(QStringLiteral("dump_nso"), false).toBool(); - Settings::values.enable_fs_access_log = - ReadSetting(QStringLiteral("enable_fs_access_log"), false).toBool(); - Settings::values.reporting_services = - ReadSetting(QStringLiteral("reporting_services"), false).toBool(); - Settings::values.quest_flag = ReadSetting(QStringLiteral("quest_flag"), false).toBool(); - Settings::values.disable_macro_jit = - ReadSetting(QStringLiteral("disable_macro_jit"), false).toBool(); - Settings::values.extended_logging = - ReadSetting(QStringLiteral("extended_logging"), false).toBool(); - Settings::values.use_debug_asserts = - ReadSetting(QStringLiteral("use_debug_asserts"), false).toBool(); - Settings::values.use_auto_stub = ReadSetting(QStringLiteral("use_auto_stub"), false).toBool(); + ReadBasicSetting(Settings::values.program_args); + ReadBasicSetting(Settings::values.dump_exefs); + ReadBasicSetting(Settings::values.dump_nso); + ReadBasicSetting(Settings::values.enable_fs_access_log); + ReadBasicSetting(Settings::values.reporting_services); + ReadBasicSetting(Settings::values.quest_flag); + ReadBasicSetting(Settings::values.disable_macro_jit); + ReadBasicSetting(Settings::values.extended_logging); + ReadBasicSetting(Settings::values.use_debug_asserts); + ReadBasicSetting(Settings::values.use_auto_stub); qt_config->endGroup(); } void Config::ReadServiceValues() { qt_config->beginGroup(QStringLiteral("Services")); - Settings::values.bcat_backend = - ReadSetting(QStringLiteral("bcat_backend"), QStringLiteral("none")) - .toString() - .toStdString(); - Settings::values.bcat_boxcat_local = - ReadSetting(QStringLiteral("bcat_boxcat_local"), false).toBool(); + ReadBasicSetting(Settings::values.bcat_backend); + ReadBasicSetting(Settings::values.bcat_boxcat_local); qt_config->endGroup(); } @@ -697,11 +742,8 @@ void Config::ReadDisabledAddOnValues() { void Config::ReadMiscellaneousValues() { qt_config->beginGroup(QStringLiteral("Miscellaneous")); - Settings::values.log_filter = - ReadSetting(QStringLiteral("log_filter"), QStringLiteral("*:Info")) - .toString() - .toStdString(); - Settings::values.use_dev_keys = ReadSetting(QStringLiteral("use_dev_keys"), false).toBool(); + ReadBasicSetting(Settings::values.log_filter); + ReadBasicSetting(Settings::values.use_dev_keys); qt_config->endGroup(); } @@ -751,38 +793,31 @@ void Config::ReadPathValues() { void Config::ReadCpuValues() { qt_config->beginGroup(QStringLiteral("Cpu")); - ReadSettingGlobal(Settings::values.cpu_accuracy, QStringLiteral("cpu_accuracy"), 0); + ReadBasicSetting(Settings::values.cpu_accuracy_first_time); + if (Settings::values.cpu_accuracy_first_time) { + Settings::values.cpu_accuracy.SetValue(Settings::values.cpu_accuracy.GetDefault()); + Settings::values.cpu_accuracy_first_time.SetValue(false); + } else { + ReadGlobalSetting(Settings::values.cpu_accuracy); + } - ReadSettingGlobal(Settings::values.cpuopt_unsafe_unfuse_fma, - QStringLiteral("cpuopt_unsafe_unfuse_fma"), true); - ReadSettingGlobal(Settings::values.cpuopt_unsafe_reduce_fp_error, - QStringLiteral("cpuopt_unsafe_reduce_fp_error"), true); - ReadSettingGlobal(Settings::values.cpuopt_unsafe_ignore_standard_fpcr, - QStringLiteral("cpuopt_unsafe_ignore_standard_fpcr"), true); - ReadSettingGlobal(Settings::values.cpuopt_unsafe_inaccurate_nan, - QStringLiteral("cpuopt_unsafe_inaccurate_nan"), true); - ReadSettingGlobal(Settings::values.cpuopt_unsafe_fastmem_check, - QStringLiteral("cpuopt_unsafe_fastmem_check"), true); + ReadGlobalSetting(Settings::values.cpuopt_unsafe_unfuse_fma); + ReadGlobalSetting(Settings::values.cpuopt_unsafe_reduce_fp_error); + ReadGlobalSetting(Settings::values.cpuopt_unsafe_ignore_standard_fpcr); + ReadGlobalSetting(Settings::values.cpuopt_unsafe_inaccurate_nan); + ReadGlobalSetting(Settings::values.cpuopt_unsafe_fastmem_check); if (global) { - Settings::values.cpuopt_page_tables = - ReadSetting(QStringLiteral("cpuopt_page_tables"), true).toBool(); - Settings::values.cpuopt_block_linking = - ReadSetting(QStringLiteral("cpuopt_block_linking"), true).toBool(); - Settings::values.cpuopt_return_stack_buffer = - ReadSetting(QStringLiteral("cpuopt_return_stack_buffer"), true).toBool(); - Settings::values.cpuopt_fast_dispatcher = - ReadSetting(QStringLiteral("cpuopt_fast_dispatcher"), true).toBool(); - Settings::values.cpuopt_context_elimination = - ReadSetting(QStringLiteral("cpuopt_context_elimination"), true).toBool(); - Settings::values.cpuopt_const_prop = - ReadSetting(QStringLiteral("cpuopt_const_prop"), true).toBool(); - Settings::values.cpuopt_misc_ir = - ReadSetting(QStringLiteral("cpuopt_misc_ir"), true).toBool(); - Settings::values.cpuopt_reduce_misalign_checks = - ReadSetting(QStringLiteral("cpuopt_reduce_misalign_checks"), true).toBool(); - Settings::values.cpuopt_fastmem = - ReadSetting(QStringLiteral("cpuopt_fastmem"), true).toBool(); + ReadBasicSetting(Settings::values.cpu_debug_mode); + ReadBasicSetting(Settings::values.cpuopt_page_tables); + ReadBasicSetting(Settings::values.cpuopt_block_linking); + ReadBasicSetting(Settings::values.cpuopt_return_stack_buffer); + ReadBasicSetting(Settings::values.cpuopt_fast_dispatcher); + ReadBasicSetting(Settings::values.cpuopt_context_elimination); + ReadBasicSetting(Settings::values.cpuopt_const_prop); + ReadBasicSetting(Settings::values.cpuopt_misc_ir); + ReadBasicSetting(Settings::values.cpuopt_reduce_misalign_checks); + ReadBasicSetting(Settings::values.cpuopt_fastmem); } qt_config->endGroup(); @@ -791,41 +826,30 @@ void Config::ReadCpuValues() { void Config::ReadRendererValues() { qt_config->beginGroup(QStringLiteral("Renderer")); - ReadSettingGlobal(Settings::values.renderer_backend, QStringLiteral("backend"), 0); - ReadSettingGlobal(Settings::values.renderer_debug, QStringLiteral("debug"), false); - ReadSettingGlobal(Settings::values.vulkan_device, QStringLiteral("vulkan_device"), 0); -#ifdef _WIN32 - ReadSettingGlobal(Settings::values.fullscreen_mode, QStringLiteral("fullscreen_mode"), 0); -#else - // *nix platforms may have issues with the borderless windowed fullscreen mode. - // Default to exclusive fullscreen on these platforms for now. - ReadSettingGlobal(Settings::values.fullscreen_mode, QStringLiteral("fullscreen_mode"), 1); -#endif - ReadSettingGlobal(Settings::values.aspect_ratio, QStringLiteral("aspect_ratio"), 0); - ReadSettingGlobal(Settings::values.max_anisotropy, QStringLiteral("max_anisotropy"), 0); - ReadSettingGlobal(Settings::values.use_frame_limit, QStringLiteral("use_frame_limit"), true); - ReadSettingGlobal(Settings::values.frame_limit, QStringLiteral("frame_limit"), 100); - ReadSettingGlobal(Settings::values.use_disk_shader_cache, - QStringLiteral("use_disk_shader_cache"), true); - ReadSettingGlobal(Settings::values.gpu_accuracy, QStringLiteral("gpu_accuracy"), 1); - ReadSettingGlobal(Settings::values.use_asynchronous_gpu_emulation, - QStringLiteral("use_asynchronous_gpu_emulation"), true); - ReadSettingGlobal(Settings::values.use_nvdec_emulation, QStringLiteral("use_nvdec_emulation"), - true); - ReadSettingGlobal(Settings::values.accelerate_astc, QStringLiteral("accelerate_astc"), true); - ReadSettingGlobal(Settings::values.use_vsync, QStringLiteral("use_vsync"), true); - ReadSettingGlobal(Settings::values.disable_fps_limit, QStringLiteral("disable_fps_limit"), - false); - ReadSettingGlobal(Settings::values.use_assembly_shaders, QStringLiteral("use_assembly_shaders"), - false); - ReadSettingGlobal(Settings::values.use_asynchronous_shaders, - QStringLiteral("use_asynchronous_shaders"), false); - ReadSettingGlobal(Settings::values.use_fast_gpu_time, QStringLiteral("use_fast_gpu_time"), - true); - ReadSettingGlobal(Settings::values.use_caches_gc, QStringLiteral("use_caches_gc"), false); - ReadSettingGlobal(Settings::values.bg_red, QStringLiteral("bg_red"), 0.0); - ReadSettingGlobal(Settings::values.bg_green, QStringLiteral("bg_green"), 0.0); - ReadSettingGlobal(Settings::values.bg_blue, QStringLiteral("bg_blue"), 0.0); + ReadGlobalSetting(Settings::values.renderer_backend); + ReadGlobalSetting(Settings::values.vulkan_device); + ReadGlobalSetting(Settings::values.fullscreen_mode); + ReadGlobalSetting(Settings::values.aspect_ratio); + ReadGlobalSetting(Settings::values.max_anisotropy); + ReadGlobalSetting(Settings::values.use_frame_limit); + ReadGlobalSetting(Settings::values.frame_limit); + ReadGlobalSetting(Settings::values.use_disk_shader_cache); + ReadGlobalSetting(Settings::values.gpu_accuracy); + ReadGlobalSetting(Settings::values.use_asynchronous_gpu_emulation); + ReadGlobalSetting(Settings::values.use_nvdec_emulation); + ReadGlobalSetting(Settings::values.accelerate_astc); + ReadGlobalSetting(Settings::values.use_vsync); + ReadGlobalSetting(Settings::values.use_assembly_shaders); + ReadGlobalSetting(Settings::values.use_asynchronous_shaders); + ReadGlobalSetting(Settings::values.use_fast_gpu_time); + ReadGlobalSetting(Settings::values.use_caches_gc); + ReadGlobalSetting(Settings::values.bg_red); + ReadGlobalSetting(Settings::values.bg_green); + ReadGlobalSetting(Settings::values.bg_blue); + + if (global) { + ReadBasicSetting(Settings::values.renderer_debug); + } qt_config->endGroup(); } @@ -870,15 +894,15 @@ void Config::ReadShortcutValues() { void Config::ReadSystemValues() { qt_config->beginGroup(QStringLiteral("System")); - ReadSettingGlobal(Settings::values.current_user, QStringLiteral("current_user"), 0); - Settings::values.current_user = - std::clamp<int>(Settings::values.current_user, 0, Service::Account::MAX_USERS - 1); + ReadBasicSetting(Settings::values.current_user); + Settings::values.current_user = std::clamp<int>(Settings::values.current_user.GetValue(), 0, + Service::Account::MAX_USERS - 1); - ReadSettingGlobal(Settings::values.language_index, QStringLiteral("language_index"), 1); + ReadGlobalSetting(Settings::values.language_index); - ReadSettingGlobal(Settings::values.region_index, QStringLiteral("region_index"), 1); + ReadGlobalSetting(Settings::values.region_index); - ReadSettingGlobal(Settings::values.time_zone_index, QStringLiteral("time_zone_index"), 0); + ReadGlobalSetting(Settings::values.time_zone_index); bool rng_seed_enabled; ReadSettingGlobal(rng_seed_enabled, QStringLiteral("rng_seed_enabled"), false); @@ -904,7 +928,7 @@ void Config::ReadSystemValues() { } } - ReadSettingGlobal(Settings::values.sound_index, QStringLiteral("sound_index"), 1); + ReadGlobalSetting(Settings::values.sound_index); qt_config->endGroup(); } @@ -915,10 +939,8 @@ void Config::ReadUIValues() { UISettings::values.theme = ReadSetting(QStringLiteral("theme"), QString::fromUtf8(UISettings::themes[0].second)) .toString(); - UISettings::values.enable_discord_presence = - ReadSetting(QStringLiteral("enable_discord_presence"), true).toBool(); - UISettings::values.select_user_on_boot = - ReadSetting(QStringLiteral("select_user_on_boot"), false).toBool(); + ReadBasicSetting(UISettings::values.enable_discord_presence); + ReadBasicSetting(UISettings::values.select_user_on_boot); ReadUIGamelistValues(); ReadUILayoutValues(); @@ -926,24 +948,17 @@ void Config::ReadUIValues() { ReadScreenshotValues(); ReadShortcutValues(); - UISettings::values.single_window_mode = - ReadSetting(QStringLiteral("singleWindowMode"), true).toBool(); - UISettings::values.fullscreen = ReadSetting(QStringLiteral("fullscreen"), false).toBool(); - UISettings::values.display_titlebar = - ReadSetting(QStringLiteral("displayTitleBars"), true).toBool(); - UISettings::values.show_filter_bar = - ReadSetting(QStringLiteral("showFilterBar"), true).toBool(); - UISettings::values.show_status_bar = - ReadSetting(QStringLiteral("showStatusBar"), true).toBool(); - UISettings::values.confirm_before_closing = - ReadSetting(QStringLiteral("confirmClose"), true).toBool(); - UISettings::values.first_start = ReadSetting(QStringLiteral("firstStart"), true).toBool(); - UISettings::values.callout_flags = ReadSetting(QStringLiteral("calloutFlags"), 0).toUInt(); - UISettings::values.show_console = ReadSetting(QStringLiteral("showConsole"), false).toBool(); - UISettings::values.pause_when_in_background = - ReadSetting(QStringLiteral("pauseWhenInBackground"), false).toBool(); - UISettings::values.hide_mouse = - ReadSetting(QStringLiteral("hideInactiveMouse"), false).toBool(); + ReadBasicSetting(UISettings::values.single_window_mode); + ReadBasicSetting(UISettings::values.fullscreen); + ReadBasicSetting(UISettings::values.display_titlebar); + ReadBasicSetting(UISettings::values.show_filter_bar); + ReadBasicSetting(UISettings::values.show_status_bar); + ReadBasicSetting(UISettings::values.confirm_before_closing); + ReadBasicSetting(UISettings::values.first_start); + ReadBasicSetting(UISettings::values.callout_flags); + ReadBasicSetting(UISettings::values.show_console); + ReadBasicSetting(UISettings::values.pause_when_in_background); + ReadBasicSetting(UISettings::values.hide_mouse); qt_config->endGroup(); } @@ -951,12 +966,11 @@ void Config::ReadUIValues() { void Config::ReadUIGamelistValues() { qt_config->beginGroup(QStringLiteral("UIGameList")); - UISettings::values.show_add_ons = ReadSetting(QStringLiteral("show_add_ons"), true).toBool(); - UISettings::values.icon_size = ReadSetting(QStringLiteral("icon_size"), 64).toUInt(); - UISettings::values.row_1_text_id = ReadSetting(QStringLiteral("row_1_text_id"), 3).toUInt(); - UISettings::values.row_2_text_id = ReadSetting(QStringLiteral("row_2_text_id"), 2).toUInt(); - UISettings::values.cache_game_list = - ReadSetting(QStringLiteral("cache_game_list"), true).toBool(); + ReadBasicSetting(UISettings::values.show_add_ons); + ReadBasicSetting(UISettings::values.icon_size); + ReadBasicSetting(UISettings::values.row_1_text_id); + ReadBasicSetting(UISettings::values.row_2_text_id); + ReadBasicSetting(UISettings::values.cache_game_list); const int favorites_size = qt_config->beginReadArray(QStringLiteral("favorites")); for (int i = 0; i < favorites_size; i++) { qt_config->setArrayIndex(i); @@ -979,8 +993,7 @@ void Config::ReadUILayoutValues() { ReadSetting(QStringLiteral("gameListHeaderState")).toByteArray(); UISettings::values.microprofile_geometry = ReadSetting(QStringLiteral("microProfileDialogGeometry")).toByteArray(); - UISettings::values.microprofile_visible = - ReadSetting(QStringLiteral("microProfileDialogVisible"), false).toBool(); + ReadBasicSetting(UISettings::values.microprofile_visible); qt_config->endGroup(); } @@ -988,16 +1001,10 @@ void Config::ReadUILayoutValues() { void Config::ReadWebServiceValues() { qt_config->beginGroup(QStringLiteral("WebService")); - Settings::values.enable_telemetry = - ReadSetting(QStringLiteral("enable_telemetry"), true).toBool(); - Settings::values.web_api_url = - ReadSetting(QStringLiteral("web_api_url"), QStringLiteral("https://api.yuzu-emu.org")) - .toString() - .toStdString(); - Settings::values.yuzu_username = - ReadSetting(QStringLiteral("yuzu_username")).toString().toStdString(); - Settings::values.yuzu_token = - ReadSetting(QStringLiteral("yuzu_token")).toString().toStdString(); + ReadBasicSetting(Settings::values.enable_telemetry); + ReadBasicSetting(Settings::values.web_api_url); + ReadBasicSetting(Settings::values.yuzu_username); + ReadBasicSetting(Settings::values.yuzu_token); qt_config->endGroup(); } @@ -1083,7 +1090,7 @@ void Config::SavePlayerValue(std::size_t player_index) { } void Config::SaveDebugValues() { - WriteSetting(QStringLiteral("debug_pad_enabled"), Settings::values.debug_pad_enabled, false); + WriteBasicSetting(Settings::values.debug_pad_enabled); for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) { const std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]); WriteSetting(QStringLiteral("debug_pad_") + @@ -1103,7 +1110,7 @@ void Config::SaveDebugValues() { } void Config::SaveMouseValues() { - WriteSetting(QStringLiteral("mouse_enabled"), Settings::values.mouse_enabled, false); + WriteBasicSetting(Settings::values.mouse_enabled); for (int i = 0; i < Settings::NativeMouseButton::NumMouseButtons; ++i) { const std::string default_param = @@ -1126,19 +1133,11 @@ void Config::SaveTouchscreenValues() { } void Config::SaveMotionTouchValues() { - WriteSetting(QStringLiteral("motion_device"), - QString::fromStdString(Settings::values.motion_device), - QStringLiteral("engine:motion_emu,update_period:100,sensitivity:0.01")); - WriteSetting(QStringLiteral("touch_device"), - QString::fromStdString(Settings::values.touch_device), - QStringLiteral("engine:emu_window")); - WriteSetting(QStringLiteral("use_touch_from_button"), Settings::values.use_touch_from_button, - false); - WriteSetting(QStringLiteral("touch_from_button_map"), - Settings::values.touch_from_button_map_index, 0); - WriteSetting(QStringLiteral("udp_input_servers"), - QString::fromStdString(Settings::values.udp_input_servers), - QString::fromUtf8(InputCommon::CemuhookUDP::DEFAULT_SRV)); + WriteBasicSetting(Settings::values.motion_device); + WriteBasicSetting(Settings::values.touch_device); + WriteBasicSetting(Settings::values.use_touch_from_button); + WriteBasicSetting(Settings::values.touch_from_button_map_index); + WriteBasicSetting(Settings::values.udp_input_servers); qt_config->beginWriteArray(QStringLiteral("touch_from_button_maps")); for (std::size_t p = 0; p < Settings::values.touch_from_button_maps.size(); ++p) { @@ -1181,15 +1180,11 @@ void Config::SaveAudioValues() { qt_config->beginGroup(QStringLiteral("Audio")); if (global) { - WriteSetting(QStringLiteral("output_engine"), - QString::fromStdString(Settings::values.sink_id), QStringLiteral("auto")); - WriteSetting(QStringLiteral("output_device"), - QString::fromStdString(Settings::values.audio_device_id), - QStringLiteral("auto")); + WriteBasicSetting(Settings::values.sink_id); + WriteBasicSetting(Settings::values.audio_device_id); } - WriteSettingGlobal(QStringLiteral("enable_audio_stretching"), - Settings::values.enable_audio_stretching, true); - WriteSettingGlobal(QStringLiteral("volume"), Settings::values.volume, 1.0f); + WriteGlobalSetting(Settings::values.enable_audio_stretching); + WriteGlobalSetting(Settings::values.volume); qt_config->endGroup(); } @@ -1205,30 +1200,21 @@ void Config::SaveControlValues() { SaveTouchscreenValues(); SaveMotionTouchValues(); - WriteSettingGlobal(QStringLiteral("use_docked_mode"), Settings::values.use_docked_mode, true); - WriteSettingGlobal(QStringLiteral("vibration_enabled"), Settings::values.vibration_enabled, - true); - WriteSettingGlobal(QStringLiteral("enable_accurate_vibrations"), - Settings::values.enable_accurate_vibrations, false); - WriteSettingGlobal(QStringLiteral("motion_enabled"), Settings::values.motion_enabled, true); - WriteSetting(QStringLiteral("motion_device"), - QString::fromStdString(Settings::values.motion_device), - QStringLiteral("engine:motion_emu,update_period:100,sensitivity:0.01")); - WriteSetting(QStringLiteral("touch_device"), - QString::fromStdString(Settings::values.touch_device), - QStringLiteral("engine:emu_window")); - WriteSetting(QStringLiteral("keyboard_enabled"), Settings::values.keyboard_enabled, false); - WriteSetting(QStringLiteral("emulate_analog_keyboard"), - Settings::values.emulate_analog_keyboard, false); - WriteSetting(QStringLiteral("mouse_panning_sensitivity"), - Settings::values.mouse_panning_sensitivity, 1.0f); + WriteGlobalSetting(Settings::values.use_docked_mode); + WriteGlobalSetting(Settings::values.vibration_enabled); + WriteGlobalSetting(Settings::values.enable_accurate_vibrations); + WriteGlobalSetting(Settings::values.motion_enabled); + WriteBasicSetting(Settings::values.keyboard_enabled); + WriteBasicSetting(Settings::values.emulate_analog_keyboard); + WriteBasicSetting(Settings::values.mouse_panning_sensitivity); + qt_config->endGroup(); } void Config::SaveCoreValues() { qt_config->beginGroup(QStringLiteral("Core")); - WriteSettingGlobal(QStringLiteral("use_multi_core"), Settings::values.use_multi_core, true); + WriteGlobalSetting(Settings::values.use_multi_core); qt_config->endGroup(); } @@ -1236,7 +1222,7 @@ void Config::SaveCoreValues() { void Config::SaveDataStorageValues() { qt_config->beginGroup(QStringLiteral("Data Storage")); - WriteSetting(QStringLiteral("use_virtual_sd"), Settings::values.use_virtual_sd, true); + WriteBasicSetting(Settings::values.use_virtual_sd); WriteSetting(QStringLiteral("nand_directory"), QString::fromStdString(FS::GetYuzuPathString(FS::YuzuPath::NANDDir)), QString::fromStdString(FS::GetYuzuPathString(FS::YuzuPath::NANDDir))); @@ -1249,11 +1235,9 @@ void Config::SaveDataStorageValues() { WriteSetting(QStringLiteral("dump_directory"), QString::fromStdString(FS::GetYuzuPathString(FS::YuzuPath::DumpDir)), QString::fromStdString(FS::GetYuzuPathString(FS::YuzuPath::DumpDir))); - WriteSetting(QStringLiteral("gamecard_inserted"), Settings::values.gamecard_inserted, false); - WriteSetting(QStringLiteral("gamecard_current_game"), Settings::values.gamecard_current_game, - false); - WriteSetting(QStringLiteral("gamecard_path"), - QString::fromStdString(Settings::values.gamecard_path), QString{}); + WriteBasicSetting(Settings::values.gamecard_inserted); + WriteBasicSetting(Settings::values.gamecard_current_game); + WriteBasicSetting(Settings::values.gamecard_path); qt_config->endGroup(); } @@ -1263,24 +1247,23 @@ void Config::SaveDebuggingValues() { // Intentionally not using the QT default setting as this is intended to be changed in the ini qt_config->setValue(QStringLiteral("record_frame_times"), Settings::values.record_frame_times); - WriteSetting(QStringLiteral("program_args"), - QString::fromStdString(Settings::values.program_args), QString{}); - WriteSetting(QStringLiteral("dump_exefs"), Settings::values.dump_exefs, false); - WriteSetting(QStringLiteral("dump_nso"), Settings::values.dump_nso, false); - WriteSetting(QStringLiteral("enable_fs_access_log"), Settings::values.enable_fs_access_log, - false); - WriteSetting(QStringLiteral("quest_flag"), Settings::values.quest_flag, false); - WriteSetting(QStringLiteral("use_debug_asserts"), Settings::values.use_debug_asserts, false); - WriteSetting(QStringLiteral("disable_macro_jit"), Settings::values.disable_macro_jit, false); + WriteBasicSetting(Settings::values.program_args); + WriteBasicSetting(Settings::values.dump_exefs); + WriteBasicSetting(Settings::values.dump_nso); + WriteBasicSetting(Settings::values.enable_fs_access_log); + WriteBasicSetting(Settings::values.quest_flag); + WriteBasicSetting(Settings::values.use_debug_asserts); + WriteBasicSetting(Settings::values.disable_macro_jit); qt_config->endGroup(); } void Config::SaveServiceValues() { qt_config->beginGroup(QStringLiteral("Services")); - WriteSetting(QStringLiteral("bcat_backend"), - QString::fromStdString(Settings::values.bcat_backend), QStringLiteral("none")); - WriteSetting(QStringLiteral("bcat_boxcat_local"), Settings::values.bcat_boxcat_local, false); + + WriteBasicSetting(Settings::values.bcat_backend); + WriteBasicSetting(Settings::values.bcat_boxcat_local); + qt_config->endGroup(); } @@ -1306,9 +1289,8 @@ void Config::SaveDisabledAddOnValues() { void Config::SaveMiscellaneousValues() { qt_config->beginGroup(QStringLiteral("Miscellaneous")); - WriteSetting(QStringLiteral("log_filter"), QString::fromStdString(Settings::values.log_filter), - QStringLiteral("*:Info")); - WriteSetting(QStringLiteral("use_dev_keys"), Settings::values.use_dev_keys, false); + WriteBasicSetting(Settings::values.log_filter); + WriteBasicSetting(Settings::values.use_dev_keys); qt_config->endGroup(); } @@ -1336,38 +1318,29 @@ void Config::SavePathValues() { void Config::SaveCpuValues() { qt_config->beginGroup(QStringLiteral("Cpu")); - WriteSettingGlobal(QStringLiteral("cpu_accuracy"), - static_cast<u32>(Settings::values.cpu_accuracy.GetValue(global)), - Settings::values.cpu_accuracy.UsingGlobal(), - static_cast<u32>(Settings::CPUAccuracy::Accurate)); - - WriteSettingGlobal(QStringLiteral("cpuopt_unsafe_unfuse_fma"), - Settings::values.cpuopt_unsafe_unfuse_fma, true); - WriteSettingGlobal(QStringLiteral("cpuopt_unsafe_reduce_fp_error"), - Settings::values.cpuopt_unsafe_reduce_fp_error, true); - WriteSettingGlobal(QStringLiteral("cpuopt_unsafe_ignore_standard_fpcr"), - Settings::values.cpuopt_unsafe_ignore_standard_fpcr, true); - WriteSettingGlobal(QStringLiteral("cpuopt_unsafe_inaccurate_nan"), - Settings::values.cpuopt_unsafe_inaccurate_nan, true); - WriteSettingGlobal(QStringLiteral("cpuopt_unsafe_fastmem_check"), - Settings::values.cpuopt_unsafe_fastmem_check, true); + WriteBasicSetting(Settings::values.cpu_accuracy_first_time); + WriteSetting(QStringLiteral("cpu_accuracy"), + static_cast<u32>(Settings::values.cpu_accuracy.GetValue(global)), + static_cast<u32>(Settings::values.cpu_accuracy.GetDefault()), + Settings::values.cpu_accuracy.UsingGlobal()); + + WriteGlobalSetting(Settings::values.cpuopt_unsafe_unfuse_fma); + WriteGlobalSetting(Settings::values.cpuopt_unsafe_reduce_fp_error); + WriteGlobalSetting(Settings::values.cpuopt_unsafe_ignore_standard_fpcr); + WriteGlobalSetting(Settings::values.cpuopt_unsafe_inaccurate_nan); + WriteGlobalSetting(Settings::values.cpuopt_unsafe_fastmem_check); if (global) { - WriteSetting(QStringLiteral("cpuopt_page_tables"), Settings::values.cpuopt_page_tables, - true); - WriteSetting(QStringLiteral("cpuopt_block_linking"), Settings::values.cpuopt_block_linking, - true); - WriteSetting(QStringLiteral("cpuopt_return_stack_buffer"), - Settings::values.cpuopt_return_stack_buffer, true); - WriteSetting(QStringLiteral("cpuopt_fast_dispatcher"), - Settings::values.cpuopt_fast_dispatcher, true); - WriteSetting(QStringLiteral("cpuopt_context_elimination"), - Settings::values.cpuopt_context_elimination, true); - WriteSetting(QStringLiteral("cpuopt_const_prop"), Settings::values.cpuopt_const_prop, true); - WriteSetting(QStringLiteral("cpuopt_misc_ir"), Settings::values.cpuopt_misc_ir, true); - WriteSetting(QStringLiteral("cpuopt_reduce_misalign_checks"), - Settings::values.cpuopt_reduce_misalign_checks, true); - WriteSetting(QStringLiteral("cpuopt_fastmem"), Settings::values.cpuopt_fastmem, true); + WriteBasicSetting(Settings::values.cpu_debug_mode); + WriteBasicSetting(Settings::values.cpuopt_page_tables); + WriteBasicSetting(Settings::values.cpuopt_block_linking); + WriteBasicSetting(Settings::values.cpuopt_return_stack_buffer); + WriteBasicSetting(Settings::values.cpuopt_fast_dispatcher); + WriteBasicSetting(Settings::values.cpuopt_context_elimination); + WriteBasicSetting(Settings::values.cpuopt_const_prop); + WriteBasicSetting(Settings::values.cpuopt_misc_ir); + WriteBasicSetting(Settings::values.cpuopt_reduce_misalign_checks); + WriteBasicSetting(Settings::values.cpuopt_fastmem); } qt_config->endGroup(); @@ -1376,46 +1349,36 @@ void Config::SaveCpuValues() { void Config::SaveRendererValues() { qt_config->beginGroup(QStringLiteral("Renderer")); - WriteSettingGlobal(QStringLiteral("backend"), - static_cast<int>(Settings::values.renderer_backend.GetValue(global)), - Settings::values.renderer_backend.UsingGlobal(), 0); - WriteSetting(QStringLiteral("debug"), Settings::values.renderer_debug, false); - WriteSettingGlobal(QStringLiteral("vulkan_device"), Settings::values.vulkan_device, 0); -#ifdef _WIN32 - WriteSettingGlobal(QStringLiteral("fullscreen_mode"), Settings::values.fullscreen_mode, 0); -#else - // *nix platforms may have issues with the borderless windowed fullscreen mode. - // Default to exclusive fullscreen on these platforms for now. - WriteSettingGlobal(QStringLiteral("fullscreen_mode"), Settings::values.fullscreen_mode, 1); -#endif - WriteSettingGlobal(QStringLiteral("aspect_ratio"), Settings::values.aspect_ratio, 0); - WriteSettingGlobal(QStringLiteral("max_anisotropy"), Settings::values.max_anisotropy, 0); - WriteSettingGlobal(QStringLiteral("use_frame_limit"), Settings::values.use_frame_limit, true); - WriteSettingGlobal(QStringLiteral("frame_limit"), Settings::values.frame_limit, 100); - WriteSettingGlobal(QStringLiteral("use_disk_shader_cache"), - Settings::values.use_disk_shader_cache, true); - WriteSettingGlobal(QStringLiteral("gpu_accuracy"), - static_cast<int>(Settings::values.gpu_accuracy.GetValue(global)), - Settings::values.gpu_accuracy.UsingGlobal(), 1); - WriteSettingGlobal(QStringLiteral("use_asynchronous_gpu_emulation"), - Settings::values.use_asynchronous_gpu_emulation, true); - WriteSettingGlobal(QStringLiteral("use_nvdec_emulation"), Settings::values.use_nvdec_emulation, - true); - WriteSettingGlobal(QStringLiteral("accelerate_astc"), Settings::values.accelerate_astc, true); - WriteSettingGlobal(QStringLiteral("use_vsync"), Settings::values.use_vsync, true); - WriteSettingGlobal(QStringLiteral("disable_fps_limit"), Settings::values.disable_fps_limit, - false); - WriteSettingGlobal(QStringLiteral("use_assembly_shaders"), - Settings::values.use_assembly_shaders, false); - WriteSettingGlobal(QStringLiteral("use_asynchronous_shaders"), - Settings::values.use_asynchronous_shaders, false); - WriteSettingGlobal(QStringLiteral("use_fast_gpu_time"), Settings::values.use_fast_gpu_time, - true); - WriteSettingGlobal(QStringLiteral("use_caches_gc"), Settings::values.use_caches_gc, false); - // Cast to double because Qt's written float values are not human-readable - WriteSettingGlobal(QStringLiteral("bg_red"), Settings::values.bg_red, 0.0); - WriteSettingGlobal(QStringLiteral("bg_green"), Settings::values.bg_green, 0.0); - WriteSettingGlobal(QStringLiteral("bg_blue"), Settings::values.bg_blue, 0.0); + WriteSetting(QString::fromStdString(Settings::values.renderer_backend.GetLabel()), + static_cast<u32>(Settings::values.renderer_backend.GetValue(global)), + static_cast<u32>(Settings::values.renderer_backend.GetDefault()), + Settings::values.renderer_backend.UsingGlobal()); + WriteGlobalSetting(Settings::values.vulkan_device); + WriteGlobalSetting(Settings::values.fullscreen_mode); + WriteGlobalSetting(Settings::values.aspect_ratio); + WriteGlobalSetting(Settings::values.max_anisotropy); + WriteGlobalSetting(Settings::values.use_frame_limit); + WriteGlobalSetting(Settings::values.frame_limit); + WriteGlobalSetting(Settings::values.use_disk_shader_cache); + WriteSetting(QString::fromStdString(Settings::values.gpu_accuracy.GetLabel()), + static_cast<u32>(Settings::values.gpu_accuracy.GetValue(global)), + static_cast<u32>(Settings::values.gpu_accuracy.GetDefault()), + Settings::values.gpu_accuracy.UsingGlobal()); + WriteGlobalSetting(Settings::values.use_asynchronous_gpu_emulation); + WriteGlobalSetting(Settings::values.use_nvdec_emulation); + WriteGlobalSetting(Settings::values.accelerate_astc); + WriteGlobalSetting(Settings::values.use_vsync); + WriteGlobalSetting(Settings::values.use_assembly_shaders); + WriteGlobalSetting(Settings::values.use_asynchronous_shaders); + WriteGlobalSetting(Settings::values.use_fast_gpu_time); + WriteGlobalSetting(Settings::values.use_caches_gc); + WriteGlobalSetting(Settings::values.bg_red); + WriteGlobalSetting(Settings::values.bg_green); + WriteGlobalSetting(Settings::values.bg_blue); + + if (global) { + WriteBasicSetting(Settings::values.renderer_debug); + } qt_config->endGroup(); } @@ -1423,8 +1386,7 @@ void Config::SaveRendererValues() { void Config::SaveScreenshotValues() { qt_config->beginGroup(QStringLiteral("Screenshots")); - WriteSetting(QStringLiteral("enable_screenshot_save_as"), - UISettings::values.enable_screenshot_save_as); + WriteBasicSetting(UISettings::values.enable_screenshot_save_as); WriteSetting(QStringLiteral("screenshot_path"), QString::fromStdString(FS::GetYuzuPathString(FS::YuzuPath::ScreenshotsDir))); @@ -1454,17 +1416,16 @@ void Config::SaveShortcutValues() { void Config::SaveSystemValues() { qt_config->beginGroup(QStringLiteral("System")); - WriteSetting(QStringLiteral("current_user"), Settings::values.current_user, 0); - WriteSettingGlobal(QStringLiteral("language_index"), Settings::values.language_index, 1); - WriteSettingGlobal(QStringLiteral("region_index"), Settings::values.region_index, 1); - WriteSettingGlobal(QStringLiteral("time_zone_index"), Settings::values.time_zone_index, 0); + WriteBasicSetting(Settings::values.current_user); + WriteGlobalSetting(Settings::values.language_index); + WriteGlobalSetting(Settings::values.region_index); + WriteGlobalSetting(Settings::values.time_zone_index); - WriteSettingGlobal(QStringLiteral("rng_seed_enabled"), - Settings::values.rng_seed.GetValue(global).has_value(), - Settings::values.rng_seed.UsingGlobal(), false); - WriteSettingGlobal(QStringLiteral("rng_seed"), - Settings::values.rng_seed.GetValue(global).value_or(0), - Settings::values.rng_seed.UsingGlobal(), 0); + WriteSetting(QStringLiteral("rng_seed_enabled"), + Settings::values.rng_seed.GetValue(global).has_value(), false, + Settings::values.rng_seed.UsingGlobal()); + WriteSetting(QStringLiteral("rng_seed"), Settings::values.rng_seed.GetValue(global).value_or(0), + 0, Settings::values.rng_seed.UsingGlobal()); if (global) { WriteSetting(QStringLiteral("custom_rtc_enabled"), Settings::values.custom_rtc.has_value(), @@ -1475,7 +1436,7 @@ void Config::SaveSystemValues() { 0); } - WriteSettingGlobal(QStringLiteral("sound_index"), Settings::values.sound_index, 1); + WriteGlobalSetting(Settings::values.sound_index); qt_config->endGroup(); } @@ -1485,10 +1446,8 @@ void Config::SaveUIValues() { WriteSetting(QStringLiteral("theme"), UISettings::values.theme, QString::fromUtf8(UISettings::themes[0].second)); - WriteSetting(QStringLiteral("enable_discord_presence"), - UISettings::values.enable_discord_presence, true); - WriteSetting(QStringLiteral("select_user_on_boot"), UISettings::values.select_user_on_boot, - false); + WriteBasicSetting(UISettings::values.enable_discord_presence); + WriteBasicSetting(UISettings::values.select_user_on_boot); SaveUIGamelistValues(); SaveUILayoutValues(); @@ -1496,18 +1455,17 @@ void Config::SaveUIValues() { SaveScreenshotValues(); SaveShortcutValues(); - WriteSetting(QStringLiteral("singleWindowMode"), UISettings::values.single_window_mode, true); - WriteSetting(QStringLiteral("fullscreen"), UISettings::values.fullscreen, false); - WriteSetting(QStringLiteral("displayTitleBars"), UISettings::values.display_titlebar, true); - WriteSetting(QStringLiteral("showFilterBar"), UISettings::values.show_filter_bar, true); - WriteSetting(QStringLiteral("showStatusBar"), UISettings::values.show_status_bar, true); - WriteSetting(QStringLiteral("confirmClose"), UISettings::values.confirm_before_closing, true); - WriteSetting(QStringLiteral("firstStart"), UISettings::values.first_start, true); - WriteSetting(QStringLiteral("calloutFlags"), UISettings::values.callout_flags, 0); - WriteSetting(QStringLiteral("showConsole"), UISettings::values.show_console, false); - WriteSetting(QStringLiteral("pauseWhenInBackground"), - UISettings::values.pause_when_in_background, false); - WriteSetting(QStringLiteral("hideInactiveMouse"), UISettings::values.hide_mouse, false); + WriteBasicSetting(UISettings::values.single_window_mode); + WriteBasicSetting(UISettings::values.fullscreen); + WriteBasicSetting(UISettings::values.display_titlebar); + WriteBasicSetting(UISettings::values.show_filter_bar); + WriteBasicSetting(UISettings::values.show_status_bar); + WriteBasicSetting(UISettings::values.confirm_before_closing); + WriteBasicSetting(UISettings::values.first_start); + WriteBasicSetting(UISettings::values.callout_flags); + WriteBasicSetting(UISettings::values.show_console); + WriteBasicSetting(UISettings::values.pause_when_in_background); + WriteBasicSetting(UISettings::values.hide_mouse); qt_config->endGroup(); } @@ -1515,11 +1473,11 @@ void Config::SaveUIValues() { void Config::SaveUIGamelistValues() { qt_config->beginGroup(QStringLiteral("UIGameList")); - WriteSetting(QStringLiteral("show_add_ons"), UISettings::values.show_add_ons, true); - WriteSetting(QStringLiteral("icon_size"), UISettings::values.icon_size, 64); - WriteSetting(QStringLiteral("row_1_text_id"), UISettings::values.row_1_text_id, 3); - WriteSetting(QStringLiteral("row_2_text_id"), UISettings::values.row_2_text_id, 2); - WriteSetting(QStringLiteral("cache_game_list"), UISettings::values.cache_game_list, true); + WriteBasicSetting(UISettings::values.show_add_ons); + WriteBasicSetting(UISettings::values.icon_size); + WriteBasicSetting(UISettings::values.row_1_text_id); + WriteBasicSetting(UISettings::values.row_2_text_id); + WriteBasicSetting(UISettings::values.cache_game_list); qt_config->beginWriteArray(QStringLiteral("favorites")); for (int i = 0; i < UISettings::values.favorited_ids.size(); i++) { qt_config->setArrayIndex(i); @@ -1540,8 +1498,7 @@ void Config::SaveUILayoutValues() { WriteSetting(QStringLiteral("gameListHeaderState"), UISettings::values.gamelist_header_state); WriteSetting(QStringLiteral("microProfileDialogGeometry"), UISettings::values.microprofile_geometry); - WriteSetting(QStringLiteral("microProfileDialogVisible"), - UISettings::values.microprofile_visible, false); + WriteBasicSetting(UISettings::values.microprofile_visible); qt_config->endGroup(); } @@ -1549,13 +1506,10 @@ void Config::SaveUILayoutValues() { void Config::SaveWebServiceValues() { qt_config->beginGroup(QStringLiteral("WebService")); - WriteSetting(QStringLiteral("enable_telemetry"), Settings::values.enable_telemetry, true); - WriteSetting(QStringLiteral("web_api_url"), - QString::fromStdString(Settings::values.web_api_url), - QStringLiteral("https://api.yuzu-emu.org")); - WriteSetting(QStringLiteral("yuzu_username"), - QString::fromStdString(Settings::values.yuzu_username)); - WriteSetting(QStringLiteral("yuzu_token"), QString::fromStdString(Settings::values.yuzu_token)); + WriteBasicSetting(Settings::values.enable_telemetry); + WriteBasicSetting(Settings::values.web_api_url); + WriteBasicSetting(Settings::values.yuzu_username); + WriteBasicSetting(Settings::values.yuzu_token); qt_config->endGroup(); } @@ -1575,21 +1529,14 @@ QVariant Config::ReadSetting(const QString& name, const QVariant& default_value) } template <typename Type> -void Config::ReadSettingGlobal(Settings::Setting<Type>& setting, const QString& name) { - const bool use_global = qt_config->value(name + QStringLiteral("/use_global"), true).toBool(); - setting.SetGlobal(use_global); - if (global || !use_global) { - setting.SetValue(ReadSetting(name).value<Type>()); - } -} - -template <typename Type> -void Config::ReadSettingGlobal(Settings::Setting<Type>& setting, const QString& name, - const QVariant& default_value) { +void Config::ReadGlobalSetting(Settings::Setting<Type>& setting) { + QString name = QString::fromStdString(setting.GetLabel()); const bool use_global = qt_config->value(name + QStringLiteral("/use_global"), true).toBool(); setting.SetGlobal(use_global); if (global || !use_global) { - setting.SetValue(ReadSetting(name, default_value).value<Type>()); + setting.SetValue(static_cast<QVariant>( + ReadSetting(name, QVariant::fromValue<Type>(setting.GetDefault()))) + .value<Type>()); } } @@ -1612,31 +1559,8 @@ void Config::WriteSetting(const QString& name, const QVariant& value, qt_config->setValue(name, value); } -template <typename Type> -void Config::WriteSettingGlobal(const QString& name, const Settings::Setting<Type>& setting) { - if (!global) { - qt_config->setValue(name + QStringLiteral("/use_global"), setting.UsingGlobal()); - } - if (global || !setting.UsingGlobal()) { - qt_config->setValue(name, setting.GetValue(global)); - } -} - -template <typename Type> -void Config::WriteSettingGlobal(const QString& name, const Settings::Setting<Type>& setting, - const QVariant& default_value) { - if (!global) { - qt_config->setValue(name + QStringLiteral("/use_global"), setting.UsingGlobal()); - } - if (global || !setting.UsingGlobal()) { - qt_config->setValue(name + QStringLiteral("/default"), - setting.GetValue(global) == default_value.value<Type>()); - qt_config->setValue(name, setting.GetValue(global)); - } -} - -void Config::WriteSettingGlobal(const QString& name, const QVariant& value, bool use_global, - const QVariant& default_value) { +void Config::WriteSetting(const QString& name, const QVariant& value, const QVariant& default_value, + bool use_global) { if (!global) { qt_config->setValue(name + QStringLiteral("/use_global"), use_global); } diff --git a/src/yuzu/configuration/config.h b/src/yuzu/configuration/config.h index 3c1de0ac9..96f9b6de1 100644 --- a/src/yuzu/configuration/config.h +++ b/src/yuzu/configuration/config.h @@ -102,28 +102,75 @@ private: void SaveUILayoutValues(); void SaveWebServiceValues(); + /** + * Reads a setting from the qt_config. + * + * @param name The setting's identifier + * @param default_value The value to use when the setting is not already present in the config + */ QVariant ReadSetting(const QString& name) const; QVariant ReadSetting(const QString& name, const QVariant& default_value) const; - // Templated ReadSettingGlobal functions will also look for the use_global setting and set - // both the value and the global state properly - template <typename Type> - void ReadSettingGlobal(Settings::Setting<Type>& setting, const QString& name); - template <typename Type> - void ReadSettingGlobal(Settings::Setting<Type>& setting, const QString& name, - const QVariant& default_value); + + /** + * Only reads a setting from the qt_config if the current config is a global config, or if the + * current config is a custom config and the setting is overriding the global setting. Otherwise + * it does nothing. + * + * @param setting The variable to be modified + * @param name The setting's identifier + * @param default_value The value to use when the setting is not already present in the config + */ template <typename Type> void ReadSettingGlobal(Type& setting, const QString& name, const QVariant& default_value) const; - // Templated WriteSettingGlobal functions will also write the global state if needed and will - // skip writing the actual setting if it defers to the global value + + /** + * Writes a setting to the qt_config. + * + * @param name The setting's idetentifier + * @param value Value of the setting + * @param default_value Default of the setting if not present in qt_config + * @param use_global Specifies if the custom or global config should be in use, for custom + * configs + */ void WriteSetting(const QString& name, const QVariant& value); void WriteSetting(const QString& name, const QVariant& value, const QVariant& default_value); + void WriteSetting(const QString& name, const QVariant& value, const QVariant& default_value, + bool use_global); + + /** + * Reads a value from the qt_config and applies it to the setting, using its label and default + * value. If the config is a custom config, this will also read the global state of the setting + * and apply that information to it. + * + * @param The setting + */ template <typename Type> - void WriteSettingGlobal(const QString& name, const Settings::Setting<Type>& setting); + void ReadGlobalSetting(Settings::Setting<Type>& setting); + + /** + * Sets a value to the qt_config using the setting's label and default value. If the config is a + * custom config, it will apply the global state, and the custom value if needed. + * + * @param The setting + */ + template <typename Type> + void WriteGlobalSetting(const Settings::Setting<Type>& setting); + + /** + * Reads a value from the qt_config using the setting's label and default value and applies the + * value to the setting. + * + * @param The setting + */ + template <typename Type> + void ReadBasicSetting(Settings::BasicSetting<Type>& setting); + + /** Sets a value from the setting in the qt_config using the setting's label and default value. + * + * @param The setting + */ template <typename Type> - void WriteSettingGlobal(const QString& name, const Settings::Setting<Type>& setting, - const QVariant& default_value); - void WriteSettingGlobal(const QString& name, const QVariant& value, bool use_global, - const QVariant& default_value); + void WriteBasicSetting(const Settings::BasicSetting<Type>& setting); ConfigType type; std::unique_ptr<QSettings> qt_config; diff --git a/src/yuzu/configuration/configure.ui b/src/yuzu/configuration/configure.ui index f92c3aff3..fca9aed5f 100644 --- a/src/yuzu/configuration/configure.ui +++ b/src/yuzu/configuration/configure.ui @@ -41,7 +41,7 @@ <item> <widget class="QTabWidget" name="tabWidget"> <property name="currentIndex"> - <number>0</number> + <number>11</number> </property> <widget class="ConfigureGeneral" name="generalTab"> <property name="accessibleName"> @@ -107,14 +107,6 @@ <string>CPU</string> </attribute> </widget> - <widget class="ConfigureCpuDebug" name="cpuDebugTab"> - <property name="accessibleName"> - <string>Debug</string> - </property> - <attribute name="title"> - <string>Debug</string> - </attribute> - </widget> <widget class="ConfigureGraphics" name="graphicsTab"> <property name="accessibleName"> <string>Graphics</string> @@ -139,7 +131,7 @@ <string>Audio</string> </attribute> </widget> - <widget class="ConfigureDebug" name="debugTab"> + <widget class="ConfigureDebugTab" name="debugTab"> <property name="accessibleName"> <string>Debug</string> </property> @@ -208,24 +200,12 @@ <container>1</container> </customwidget> <customwidget> - <class>ConfigureDebug</class> - <extends>QWidget</extends> - <header>configuration/configure_debug.h</header> - <container>1</container> - </customwidget> - <customwidget> <class>ConfigureCpu</class> <extends>QWidget</extends> <header>configuration/configure_cpu.h</header> <container>1</container> </customwidget> <customwidget> - <class>ConfigureCpuDebug</class> - <extends>QWidget</extends> - <header>configuration/configure_cpu_debug.h</header> - <container>1</container> - </customwidget> - <customwidget> <class>ConfigureGraphics</class> <extends>QWidget</extends> <header>configuration/configure_graphics.h</header> @@ -267,6 +247,12 @@ <header>configuration/configure_service.h</header> <container>1</container> </customwidget> + <customwidget> + <class>ConfigureDebugTab</class> + <extends>QWidget</extends> + <header>configuration/configure_debug_tab.h</header> + <container>1</container> + </customwidget> </customwidgets> <resources/> <connections> @@ -275,12 +261,32 @@ <signal>accepted()</signal> <receiver>ConfigureDialog</receiver> <slot>accept()</slot> + <hints> + <hint type="sourcelabel"> + <x>20</x> + <y>20</y> + </hint> + <hint type="destinationlabel"> + <x>20</x> + <y>20</y> + </hint> + </hints> </connection> <connection> <sender>buttonBox</sender> <signal>rejected()</signal> <receiver>ConfigureDialog</receiver> <slot>reject()</slot> + <hints> + <hint type="sourcelabel"> + <x>20</x> + <y>20</y> + </hint> + <hint type="destinationlabel"> + <x>20</x> + <y>20</y> + </hint> + </hints> </connection> </connections> </ui> diff --git a/src/yuzu/configuration/configure_audio.cpp b/src/yuzu/configuration/configure_audio.cpp index fc0191432..5aba1a3b2 100644 --- a/src/yuzu/configuration/configure_audio.cpp +++ b/src/yuzu/configuration/configure_audio.cpp @@ -69,7 +69,7 @@ void ConfigureAudio::SetOutputSinkFromSinkID() { [[maybe_unused]] const QSignalBlocker blocker(ui->output_sink_combo_box); int new_sink_index = 0; - const QString sink_id = QString::fromStdString(Settings::values.sink_id); + const QString sink_id = QString::fromStdString(Settings::values.sink_id.GetValue()); for (int index = 0; index < ui->output_sink_combo_box->count(); index++) { if (ui->output_sink_combo_box->itemText(index) == sink_id) { new_sink_index = index; @@ -83,7 +83,7 @@ void ConfigureAudio::SetOutputSinkFromSinkID() { void ConfigureAudio::SetAudioDeviceFromDeviceID() { int new_device_index = -1; - const QString device_id = QString::fromStdString(Settings::values.audio_device_id); + const QString device_id = QString::fromStdString(Settings::values.audio_device_id.GetValue()); for (int index = 0; index < ui->audio_device_combo_box->count(); index++) { if (ui->audio_device_combo_box->itemText(index) == device_id) { new_device_index = index; @@ -106,9 +106,9 @@ void ConfigureAudio::ApplyConfiguration() { Settings::values.sink_id = ui->output_sink_combo_box->itemText(ui->output_sink_combo_box->currentIndex()) .toStdString(); - Settings::values.audio_device_id = + Settings::values.audio_device_id.SetValue( ui->audio_device_combo_box->itemText(ui->audio_device_combo_box->currentIndex()) - .toStdString(); + .toStdString()); // Guard if during game and set to game-specific value if (Settings::values.volume.UsingGlobal()) { diff --git a/src/yuzu/configuration/configure_cpu.cpp b/src/yuzu/configuration/configure_cpu.cpp index 13db2ba98..8d7171487 100644 --- a/src/yuzu/configuration/configure_cpu.cpp +++ b/src/yuzu/configuration/configure_cpu.cpp @@ -20,8 +20,6 @@ ConfigureCpu::ConfigureCpu(QWidget* parent) : QWidget(parent), ui(new Ui::Config SetConfiguration(); - connect(ui->accuracy, qOverload<int>(&QComboBox::activated), this, - &ConfigureCpu::AccuracyUpdated); connect(ui->accuracy, qOverload<int>(&QComboBox::currentIndexChanged), this, &ConfigureCpu::UpdateGroup); } @@ -58,20 +56,6 @@ void ConfigureCpu::SetConfiguration() { UpdateGroup(ui->accuracy->currentIndex()); } -void ConfigureCpu::AccuracyUpdated(int index) { - if (Settings::IsConfiguringGlobal() && - static_cast<Settings::CPUAccuracy>(index) == Settings::CPUAccuracy::DebugMode) { - const auto result = QMessageBox::warning(this, tr("Setting CPU to Debug Mode"), - tr("CPU Debug Mode is only intended for developer " - "use. Are you sure you want to enable this?"), - QMessageBox::Yes | QMessageBox::No); - if (result == QMessageBox::No) { - ui->accuracy->setCurrentIndex(static_cast<int>(Settings::CPUAccuracy::Accurate)); - UpdateGroup(static_cast<int>(Settings::CPUAccuracy::Accurate)); - } - } -} - void ConfigureCpu::UpdateGroup(int index) { if (!Settings::IsConfiguringGlobal()) { index -= ConfigurationShared::USE_GLOBAL_OFFSET; @@ -134,8 +118,6 @@ void ConfigureCpu::SetupPerGameUI() { ConfigurationShared::SetColoredComboBox( ui->accuracy, ui->widget_accuracy, static_cast<u32>(Settings::values.cpu_accuracy.GetValue(true))); - ui->accuracy->removeItem(static_cast<u32>(Settings::CPUAccuracy::DebugMode) + - ConfigurationShared::USE_GLOBAL_OFFSET); ConfigurationShared::SetColoredTristate(ui->cpuopt_unsafe_unfuse_fma, Settings::values.cpuopt_unsafe_unfuse_fma, diff --git a/src/yuzu/configuration/configure_cpu.h b/src/yuzu/configuration/configure_cpu.h index b2b5f1671..154931482 100644 --- a/src/yuzu/configuration/configure_cpu.h +++ b/src/yuzu/configuration/configure_cpu.h @@ -29,7 +29,6 @@ private: void changeEvent(QEvent* event) override; void RetranslateUI(); - void AccuracyUpdated(int index); void UpdateGroup(int index); void SetConfiguration(); diff --git a/src/yuzu/configuration/configure_cpu.ui b/src/yuzu/configuration/configure_cpu.ui index 0e296d4e5..5b9457faf 100644 --- a/src/yuzu/configuration/configure_cpu.ui +++ b/src/yuzu/configuration/configure_cpu.ui @@ -6,8 +6,8 @@ <rect> <x>0</x> <y>0</y> - <width>400</width> - <height>321</height> + <width>448</width> + <height>433</height> </rect> </property> <property name="windowTitle"> @@ -17,7 +17,7 @@ <item> <layout class="QVBoxLayout"> <item> - <widget class="QGroupBox"> + <widget class="QGroupBox" name="groupBox"> <property name="title"> <string>General</string> </property> @@ -36,17 +36,17 @@ <widget class="QComboBox" name="accuracy"> <item> <property name="text"> - <string>Accurate</string> + <string>Auto</string> </property> </item> <item> <property name="text"> - <string>Unsafe</string> + <string>Accurate</string> </property> </item> <item> <property name="text"> - <string>Enable Debug Mode</string> + <string>Unsafe</string> </property> </item> </widget> @@ -57,7 +57,7 @@ <item> <widget class="QLabel" name="label_recommended_accuracy"> <property name="text"> - <string>We recommend setting accuracy to "Accurate".</string> + <string>We recommend setting accuracy to "Auto".</string> </property> <property name="wordWrap"> <bool>false</bool> diff --git a/src/yuzu/configuration/configure_cpu_debug.cpp b/src/yuzu/configuration/configure_cpu_debug.cpp index e25c52baf..98e2d2be5 100644 --- a/src/yuzu/configuration/configure_cpu_debug.cpp +++ b/src/yuzu/configuration/configure_cpu_debug.cpp @@ -24,23 +24,26 @@ void ConfigureCpuDebug::SetConfiguration() { const bool runtime_lock = !Core::System::GetInstance().IsPoweredOn(); ui->cpuopt_page_tables->setEnabled(runtime_lock); - ui->cpuopt_page_tables->setChecked(Settings::values.cpuopt_page_tables); + ui->cpuopt_page_tables->setChecked(Settings::values.cpuopt_page_tables.GetValue()); ui->cpuopt_block_linking->setEnabled(runtime_lock); - ui->cpuopt_block_linking->setChecked(Settings::values.cpuopt_block_linking); + ui->cpuopt_block_linking->setChecked(Settings::values.cpuopt_block_linking.GetValue()); ui->cpuopt_return_stack_buffer->setEnabled(runtime_lock); - ui->cpuopt_return_stack_buffer->setChecked(Settings::values.cpuopt_return_stack_buffer); + ui->cpuopt_return_stack_buffer->setChecked( + Settings::values.cpuopt_return_stack_buffer.GetValue()); ui->cpuopt_fast_dispatcher->setEnabled(runtime_lock); - ui->cpuopt_fast_dispatcher->setChecked(Settings::values.cpuopt_fast_dispatcher); + ui->cpuopt_fast_dispatcher->setChecked(Settings::values.cpuopt_fast_dispatcher.GetValue()); ui->cpuopt_context_elimination->setEnabled(runtime_lock); - ui->cpuopt_context_elimination->setChecked(Settings::values.cpuopt_context_elimination); + ui->cpuopt_context_elimination->setChecked( + Settings::values.cpuopt_context_elimination.GetValue()); ui->cpuopt_const_prop->setEnabled(runtime_lock); - ui->cpuopt_const_prop->setChecked(Settings::values.cpuopt_const_prop); + ui->cpuopt_const_prop->setChecked(Settings::values.cpuopt_const_prop.GetValue()); ui->cpuopt_misc_ir->setEnabled(runtime_lock); - ui->cpuopt_misc_ir->setChecked(Settings::values.cpuopt_misc_ir); + ui->cpuopt_misc_ir->setChecked(Settings::values.cpuopt_misc_ir.GetValue()); ui->cpuopt_reduce_misalign_checks->setEnabled(runtime_lock); - ui->cpuopt_reduce_misalign_checks->setChecked(Settings::values.cpuopt_reduce_misalign_checks); + ui->cpuopt_reduce_misalign_checks->setChecked( + Settings::values.cpuopt_reduce_misalign_checks.GetValue()); ui->cpuopt_fastmem->setEnabled(runtime_lock); - ui->cpuopt_fastmem->setChecked(Settings::values.cpuopt_fastmem); + ui->cpuopt_fastmem->setChecked(Settings::values.cpuopt_fastmem.GetValue()); } void ConfigureCpuDebug::ApplyConfiguration() { diff --git a/src/yuzu/configuration/configure_cpu_debug.h b/src/yuzu/configuration/configure_cpu_debug.h index 10de55099..1b0d8050c 100644 --- a/src/yuzu/configuration/configure_cpu_debug.h +++ b/src/yuzu/configuration/configure_cpu_debug.h @@ -6,7 +6,6 @@ #include <memory> #include <QWidget> -#include "common/settings.h" namespace Ui { class ConfigureCpuDebug; diff --git a/src/yuzu/configuration/configure_cpu_debug.ui b/src/yuzu/configuration/configure_cpu_debug.ui index c43f89a5a..abf469b55 100644 --- a/src/yuzu/configuration/configure_cpu_debug.ui +++ b/src/yuzu/configuration/configure_cpu_debug.ui @@ -6,8 +6,8 @@ <rect> <x>0</x> <y>0</y> - <width>400</width> - <height>321</height> + <width>592</width> + <height>503</height> </rect> </property> <property name="windowTitle"> @@ -17,140 +17,132 @@ <item> <layout class="QVBoxLayout"> <item> - <widget class="QGroupBox"> + <widget class="QGroupBox" name="groupBox"> <property name="title"> <string>Toggle CPU Optimizations</string> </property> <layout class="QVBoxLayout"> <item> - <widget class="QLabel"> - <property name="wordWrap"> - <bool>1</bool> - </property> + <widget class="QLabel" name="label"> <property name="text"> - <string> - <div> - <b>For debugging only.</b> - <br> - If you're not sure what these do, keep all of these enabled. - <br> - These settings, when disabled, only take effect when CPU Accuracy is "Debug Mode". - </div> - </string> + <string><html><head/><body><p><span style=" font-weight:600;">For debugging only.</span><br/>If you're not sure what these do, keep all of these enabled. <br/>These settings, when disabled, only take effect when CPU Debugging is enabled. </p></body></html></string> + </property> + <property name="wordWrap"> + <bool>false</bool> </property> </widget> </item> <item> <widget class="QCheckBox" name="cpuopt_page_tables"> - <property name="text"> - <string>Enable inline page tables</string> - </property> <property name="toolTip"> <string> - <div style="white-space: nowrap">This optimization speeds up memory accesses by the guest program.</div> - <div style="white-space: nowrap">Enabling it inlines accesses to PageTable::pointers into emitted code.</div> - <div style="white-space: nowrap">Disabling this forces all memory accesses to go through the Memory::Read/Memory::Write functions.</div> + <div style="white-space: nowrap">This optimization speeds up memory accesses by the guest program.</div> + <div style="white-space: nowrap">Enabling it inlines accesses to PageTable::pointers into emitted code.</div> + <div style="white-space: nowrap">Disabling this forces all memory accesses to go through the Memory::Read/Memory::Write functions.</div> </string> </property> + <property name="text"> + <string>Enable inline page tables</string> + </property> </widget> </item> <item> <widget class="QCheckBox" name="cpuopt_block_linking"> - <property name="text"> - <string>Enable block linking</string> - </property> <property name="toolTip"> <string> <div>This optimization avoids dispatcher lookups by allowing emitted basic blocks to jump directly to other basic blocks if the destination PC is static.</div> </string> </property> + <property name="text"> + <string>Enable block linking</string> + </property> </widget> </item> <item> <widget class="QCheckBox" name="cpuopt_return_stack_buffer"> - <property name="text"> - <string>Enable return stack buffer</string> - </property> <property name="toolTip"> <string> <div>This optimization avoids dispatcher lookups by keeping track potential return addresses of BL instructions. This approximates what happens with a return stack buffer on a real CPU.</div> </string> </property> + <property name="text"> + <string>Enable return stack buffer</string> + </property> </widget> </item> <item> <widget class="QCheckBox" name="cpuopt_fast_dispatcher"> - <property name="text"> - <string>Enable fast dispatcher</string> - </property> <property name="toolTip"> <string> <div>Enable a two-tiered dispatch system. A faster dispatcher written in assembly has a small MRU cache of jump destinations is used first. If that fails, dispatch falls back to the slower C++ dispatcher.</div> </string> </property> + <property name="text"> + <string>Enable fast dispatcher</string> + </property> </widget> </item> <item> <widget class="QCheckBox" name="cpuopt_context_elimination"> - <property name="text"> - <string>Enable context elimination</string> - </property> <property name="toolTip"> <string> <div>Enables an IR optimization that reduces unnecessary accesses to the CPU context structure.</div> </string> </property> + <property name="text"> + <string>Enable context elimination</string> + </property> </widget> </item> <item> <widget class="QCheckBox" name="cpuopt_const_prop"> - <property name="text"> - <string>Enable constant propagation</string> - </property> <property name="toolTip"> <string> <div>Enables IR optimizations that involve constant propagation.</div> </string> </property> + <property name="text"> + <string>Enable constant propagation</string> + </property> </widget> </item> <item> <widget class="QCheckBox" name="cpuopt_misc_ir"> - <property name="text"> - <string>Enable miscellaneous optimizations</string> - </property> <property name="toolTip"> <string> <div>Enables miscellaneous IR optimizations.</div> </string> </property> + <property name="text"> + <string>Enable miscellaneous optimizations</string> + </property> </widget> </item> <item> <widget class="QCheckBox" name="cpuopt_reduce_misalign_checks"> - <property name="text"> - <string>Enable misalignment check reduction</string> - </property> <property name="toolTip"> <string> - <div style="white-space: nowrap">When enabled, a misalignment is only triggered when an access crosses a page boundary.</div> - <div style="white-space: nowrap">When disabled, a misalignment is triggered on all misaligned accesses.</div> + <div style="white-space: nowrap">When enabled, a misalignment is only triggered when an access crosses a page boundary.</div> + <div style="white-space: nowrap">When disabled, a misalignment is triggered on all misaligned accesses.</div> </string> </property> + <property name="text"> + <string>Enable misalignment check reduction</string> + </property> </widget> </item> <item> <widget class="QCheckBox" name="cpuopt_fastmem"> - <property name="text"> - <string>Enable Host MMU Emulation</string> - </property> <property name="toolTip"> <string> - <div style="white-space: nowrap">This optimization speeds up memory accesses by the guest program.</div> - <div style="white-space: nowrap">Enabling it causes guest memory reads/writes to be done directly into memory and make use of Host's MMU.</div> - <div style="white-space: nowrap">Disabling this forces all memory accesses to use Software MMU Emulation.</div> + <div style="white-space: nowrap">This optimization speeds up memory accesses by the guest program.</div> + <div style="white-space: nowrap">Enabling it causes guest memory reads/writes to be done directly into memory and make use of Host's MMU.</div> + <div style="white-space: nowrap">Disabling this forces all memory accesses to use Software MMU Emulation.</div> </string> </property> + <property name="text"> + <string>Enable Host MMU Emulation</string> + </property> </widget> </item> </layout> diff --git a/src/yuzu/configuration/configure_debug.cpp b/src/yuzu/configuration/configure_debug.cpp index 15d6a5ad7..8fceb3878 100644 --- a/src/yuzu/configuration/configure_debug.cpp +++ b/src/yuzu/configuration/configure_debug.cpp @@ -31,20 +31,23 @@ void ConfigureDebug::SetConfiguration() { const bool runtime_lock = !Core::System::GetInstance().IsPoweredOn(); ui->toggle_console->setEnabled(runtime_lock); - ui->toggle_console->setChecked(UISettings::values.show_console); - ui->log_filter_edit->setText(QString::fromStdString(Settings::values.log_filter)); - ui->homebrew_args_edit->setText(QString::fromStdString(Settings::values.program_args)); + ui->toggle_console->setChecked(UISettings::values.show_console.GetValue()); + ui->log_filter_edit->setText(QString::fromStdString(Settings::values.log_filter.GetValue())); + ui->homebrew_args_edit->setText( + QString::fromStdString(Settings::values.program_args.GetValue())); ui->fs_access_log->setEnabled(runtime_lock); - ui->fs_access_log->setChecked(Settings::values.enable_fs_access_log); - ui->reporting_services->setChecked(Settings::values.reporting_services); - ui->quest_flag->setChecked(Settings::values.quest_flag); - ui->use_debug_asserts->setChecked(Settings::values.use_debug_asserts); - ui->use_auto_stub->setChecked(Settings::values.use_auto_stub); + ui->fs_access_log->setChecked(Settings::values.enable_fs_access_log.GetValue()); + ui->reporting_services->setChecked(Settings::values.reporting_services.GetValue()); + ui->quest_flag->setChecked(Settings::values.quest_flag.GetValue()); + ui->use_debug_asserts->setChecked(Settings::values.use_debug_asserts.GetValue()); + ui->use_auto_stub->setChecked(Settings::values.use_auto_stub.GetValue()); ui->enable_graphics_debugging->setEnabled(runtime_lock); - ui->enable_graphics_debugging->setChecked(Settings::values.renderer_debug); + ui->enable_graphics_debugging->setChecked(Settings::values.renderer_debug.GetValue()); + ui->enable_cpu_debugging->setEnabled(runtime_lock); + ui->enable_cpu_debugging->setChecked(Settings::values.cpu_debug_mode.GetValue()); ui->disable_macro_jit->setEnabled(runtime_lock); - ui->disable_macro_jit->setChecked(Settings::values.disable_macro_jit); - ui->extended_logging->setChecked(Settings::values.extended_logging); + ui->disable_macro_jit->setChecked(Settings::values.disable_macro_jit.GetValue()); + ui->extended_logging->setChecked(Settings::values.extended_logging.GetValue()); } void ConfigureDebug::ApplyConfiguration() { @@ -57,11 +60,12 @@ void ConfigureDebug::ApplyConfiguration() { Settings::values.use_debug_asserts = ui->use_debug_asserts->isChecked(); Settings::values.use_auto_stub = ui->use_auto_stub->isChecked(); Settings::values.renderer_debug = ui->enable_graphics_debugging->isChecked(); + Settings::values.cpu_debug_mode = ui->enable_cpu_debugging->isChecked(); Settings::values.disable_macro_jit = ui->disable_macro_jit->isChecked(); Settings::values.extended_logging = ui->extended_logging->isChecked(); Debugger::ToggleConsole(); Common::Log::Filter filter; - filter.ParseFilterString(Settings::values.log_filter); + filter.ParseFilterString(Settings::values.log_filter.GetValue()); Common::Log::SetGlobalFilter(filter); } diff --git a/src/yuzu/configuration/configure_debug.ui b/src/yuzu/configuration/configure_debug.ui index c8087542f..1260ad6f0 100644 --- a/src/yuzu/configuration/configure_debug.ui +++ b/src/yuzu/configuration/configure_debug.ui @@ -7,7 +7,7 @@ <x>0</x> <y>0</y> <width>400</width> - <height>486</height> + <height>777</height> </rect> </property> <property name="windowTitle"> @@ -192,34 +192,41 @@ </property> </widget> </item> - <item> - <widget class="QCheckBox" name="use_debug_asserts"> - <property name="text"> - <string>Enable Debug Asserts</string> - </property> - </widget> - </item> - <item> - <widget class="QCheckBox" name="use_auto_stub"> - <property name="text"> - <string>Enable Auto-Stub</string> - </property> - </widget> - </item> <item> - <widget class="QLabel" name="label_5"> - <property name="font"> - <font> - <italic>true</italic> - </font> - </property> - <property name="text"> - <string>This will be reset automatically when yuzu closes.</string> - </property> - <property name="indent"> - <number>20</number> - </property> - </widget> + <widget class="QCheckBox" name="enable_cpu_debugging"> + <property name="text"> + <string>Enable CPU Debugging</string> + </property> + </widget> + </item> + <item> + <widget class="QCheckBox" name="use_debug_asserts"> + <property name="text"> + <string>Enable Debug Asserts</string> + </property> + </widget> + </item> + <item> + <widget class="QCheckBox" name="use_auto_stub"> + <property name="text"> + <string>Enable Auto-Stub</string> + </property> + </widget> + </item> + <item> + <widget class="QLabel" name="label_5"> + <property name="font"> + <font> + <italic>true</italic> + </font> + </property> + <property name="text"> + <string>This will be reset automatically when yuzu closes.</string> + </property> + <property name="indent"> + <number>20</number> + </property> + </widget> </item> </layout> </widget> diff --git a/src/yuzu/configuration/configure_debug_tab.cpp b/src/yuzu/configuration/configure_debug_tab.cpp new file mode 100644 index 000000000..67d369249 --- /dev/null +++ b/src/yuzu/configuration/configure_debug_tab.cpp @@ -0,0 +1,38 @@ +// Copyright 2021 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "ui_configure_debug_tab.h" +#include "yuzu/configuration/configure_debug_tab.h" + +ConfigureDebugTab::ConfigureDebugTab(QWidget* parent) + : QWidget(parent), ui(new Ui::ConfigureDebugTab) { + ui->setupUi(this); + + SetConfiguration(); +} + +ConfigureDebugTab::~ConfigureDebugTab() = default; + +void ConfigureDebugTab::ApplyConfiguration() { + ui->debugTab->ApplyConfiguration(); + ui->cpuDebugTab->ApplyConfiguration(); +} + +void ConfigureDebugTab::SetCurrentIndex(int index) { + ui->tabWidget->setCurrentIndex(index); +} + +void ConfigureDebugTab::changeEvent(QEvent* event) { + if (event->type() == QEvent::LanguageChange) { + RetranslateUI(); + } + + QWidget::changeEvent(event); +} + +void ConfigureDebugTab::RetranslateUI() { + ui->retranslateUi(this); +} + +void ConfigureDebugTab::SetConfiguration() {} diff --git a/src/yuzu/configuration/configure_debug_tab.h b/src/yuzu/configuration/configure_debug_tab.h new file mode 100644 index 000000000..0a96d43d0 --- /dev/null +++ b/src/yuzu/configuration/configure_debug_tab.h @@ -0,0 +1,32 @@ +// Copyright 2021 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <memory> +#include <QWidget> + +namespace Ui { +class ConfigureDebugTab; +} + +class ConfigureDebugTab : public QWidget { + Q_OBJECT + +public: + explicit ConfigureDebugTab(QWidget* parent = nullptr); + ~ConfigureDebugTab() override; + + void ApplyConfiguration(); + + void SetCurrentIndex(int index); + +private: + void changeEvent(QEvent* event) override; + void RetranslateUI(); + + void SetConfiguration(); + + std::unique_ptr<Ui::ConfigureDebugTab> ui; +}; diff --git a/src/yuzu/configuration/configure_debug_tab.ui b/src/yuzu/configuration/configure_debug_tab.ui new file mode 100644 index 000000000..7dc6dd704 --- /dev/null +++ b/src/yuzu/configuration/configure_debug_tab.ui @@ -0,0 +1,52 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>ConfigureDebugTab</class> + <widget class="QWidget" name="ConfigureDebugTab"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>320</width> + <height>240</height> + </rect> + </property> + <property name="windowTitle"> + <string>Form</string> + </property> + <layout class="QVBoxLayout" name="verticalLayout"> + <item> + <widget class="QTabWidget" name="tabWidget"> + <property name="currentIndex"> + <number>1</number> + </property> + <widget class="ConfigureDebug" name="debugTab"> + <attribute name="title"> + <string>General</string> + </attribute> + </widget> + <widget class="ConfigureCpuDebug" name="cpuDebugTab"> + <attribute name="title"> + <string>CPU</string> + </attribute> + </widget> + </widget> + </item> + </layout> + </widget> + <customwidgets> + <customwidget> + <class>ConfigureDebug</class> + <extends>QWidget</extends> + <header>configuration/configure_debug.h</header> + <container>1</container> + </customwidget> + <customwidget> + <class>ConfigureCpuDebug</class> + <extends>QWidget</extends> + <header>configuration/configure_cpu_debug.h</header> + <container>1</container> + </customwidget> + </customwidgets> + <resources/> + <connections/> +</ui> diff --git a/src/yuzu/configuration/configure_dialog.cpp b/src/yuzu/configuration/configure_dialog.cpp index 371bc01b1..bc009b6b3 100644 --- a/src/yuzu/configuration/configure_dialog.cpp +++ b/src/yuzu/configuration/configure_dialog.cpp @@ -8,6 +8,7 @@ #include <QListWidgetItem> #include <QPushButton> #include <QSignalBlocker> +#include <QTabWidget> #include "common/settings.h" #include "core/core.h" #include "ui_configure.h" @@ -32,6 +33,8 @@ ConfigureDialog::ConfigureDialog(QWidget* parent, HotkeyRegistry& registry, SetConfiguration(); PopulateSelectionList(); + connect(ui->tabWidget, &QTabWidget::currentChanged, this, + [this]() { ui->debugTab->SetCurrentIndex(0); }); connect(ui->uiTab, &ConfigureUi::LanguageChanged, this, &ConfigureDialog::OnLanguageChanged); connect(ui->selectorList, &QListWidget::itemSelectionChanged, this, &ConfigureDialog::UpdateVisibleTabs); @@ -59,7 +62,6 @@ void ConfigureDialog::ApplyConfiguration() { ui->inputTab->ApplyConfiguration(); ui->hotkeysTab->ApplyConfiguration(registry); ui->cpuTab->ApplyConfiguration(); - ui->cpuDebugTab->ApplyConfiguration(); ui->graphicsTab->ApplyConfiguration(); ui->graphicsAdvancedTab->ApplyConfiguration(); ui->audioTab->ApplyConfiguration(); @@ -102,7 +104,7 @@ void ConfigureDialog::PopulateSelectionList() { const std::array<std::pair<QString, QList<QWidget*>>, 6> items{ {{tr("General"), {ui->generalTab, ui->hotkeysTab, ui->uiTab, ui->webTab, ui->debugTab}}, {tr("System"), {ui->systemTab, ui->profileManagerTab, ui->serviceTab, ui->filesystemTab}}, - {tr("CPU"), {ui->cpuTab, ui->cpuDebugTab}}, + {tr("CPU"), {ui->cpuTab}}, {tr("Graphics"), {ui->graphicsTab, ui->graphicsAdvancedTab}}, {tr("Audio"), {ui->audioTab}}, {tr("Controls"), ui->inputTab->GetSubTabs()}}, diff --git a/src/yuzu/configuration/configure_filesystem.cpp b/src/yuzu/configuration/configure_filesystem.cpp index d223c40ea..9cb317822 100644 --- a/src/yuzu/configuration/configure_filesystem.cpp +++ b/src/yuzu/configuration/configure_filesystem.cpp @@ -43,18 +43,19 @@ void ConfigureFilesystem::setConfiguration() { QString::fromStdString(Common::FS::GetYuzuPathString(Common::FS::YuzuPath::NANDDir))); ui->sdmc_directory_edit->setText( QString::fromStdString(Common::FS::GetYuzuPathString(Common::FS::YuzuPath::SDMCDir))); - ui->gamecard_path_edit->setText(QString::fromStdString(Settings::values.gamecard_path)); + ui->gamecard_path_edit->setText( + QString::fromStdString(Settings::values.gamecard_path.GetValue())); ui->dump_path_edit->setText( QString::fromStdString(Common::FS::GetYuzuPathString(Common::FS::YuzuPath::DumpDir))); ui->load_path_edit->setText( QString::fromStdString(Common::FS::GetYuzuPathString(Common::FS::YuzuPath::LoadDir))); - ui->gamecard_inserted->setChecked(Settings::values.gamecard_inserted); - ui->gamecard_current_game->setChecked(Settings::values.gamecard_current_game); - ui->dump_exefs->setChecked(Settings::values.dump_exefs); - ui->dump_nso->setChecked(Settings::values.dump_nso); + ui->gamecard_inserted->setChecked(Settings::values.gamecard_inserted.GetValue()); + ui->gamecard_current_game->setChecked(Settings::values.gamecard_current_game.GetValue()); + ui->dump_exefs->setChecked(Settings::values.dump_exefs.GetValue()); + ui->dump_nso->setChecked(Settings::values.dump_nso.GetValue()); - ui->cache_game_list->setChecked(UISettings::values.cache_game_list); + ui->cache_game_list->setChecked(UISettings::values.cache_game_list.GetValue()); UpdateEnabledControls(); } diff --git a/src/yuzu/configuration/configure_general.cpp b/src/yuzu/configuration/configure_general.cpp index 38edb4d8d..18f25def6 100644 --- a/src/yuzu/configuration/configure_general.cpp +++ b/src/yuzu/configuration/configure_general.cpp @@ -40,10 +40,10 @@ void ConfigureGeneral::SetConfiguration() { ui->use_multi_core->setEnabled(runtime_lock); ui->use_multi_core->setChecked(Settings::values.use_multi_core.GetValue()); - ui->toggle_check_exit->setChecked(UISettings::values.confirm_before_closing); - ui->toggle_user_on_boot->setChecked(UISettings::values.select_user_on_boot); - ui->toggle_background_pause->setChecked(UISettings::values.pause_when_in_background); - ui->toggle_hide_mouse->setChecked(UISettings::values.hide_mouse); + ui->toggle_check_exit->setChecked(UISettings::values.confirm_before_closing.GetValue()); + ui->toggle_user_on_boot->setChecked(UISettings::values.select_user_on_boot.GetValue()); + ui->toggle_background_pause->setChecked(UISettings::values.pause_when_in_background.GetValue()); + ui->toggle_hide_mouse->setChecked(UISettings::values.hide_mouse.GetValue()); ui->toggle_frame_limit->setChecked(Settings::values.use_frame_limit.GetValue()); ui->frame_limit->setValue(Settings::values.frame_limit.GetValue()); diff --git a/src/yuzu/configuration/configure_graphics_advanced.cpp b/src/yuzu/configuration/configure_graphics_advanced.cpp index 8d13c9857..a9e611125 100644 --- a/src/yuzu/configuration/configure_graphics_advanced.cpp +++ b/src/yuzu/configuration/configure_graphics_advanced.cpp @@ -28,7 +28,6 @@ void ConfigureGraphicsAdvanced::SetConfiguration() { ui->anisotropic_filtering_combobox->setEnabled(runtime_lock); ui->use_vsync->setChecked(Settings::values.use_vsync.GetValue()); - ui->disable_fps_limit->setChecked(Settings::values.disable_fps_limit.GetValue()); ui->use_assembly_shaders->setChecked(Settings::values.use_assembly_shaders.GetValue()); ui->use_asynchronous_shaders->setChecked(Settings::values.use_asynchronous_shaders.GetValue()); ui->use_caches_gc->setChecked(Settings::values.use_caches_gc.GetValue()); @@ -59,8 +58,6 @@ void ConfigureGraphicsAdvanced::ApplyConfiguration() { ConfigurationShared::ApplyPerGameSetting(&Settings::values.max_anisotropy, ui->anisotropic_filtering_combobox); ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_vsync, ui->use_vsync, use_vsync); - ConfigurationShared::ApplyPerGameSetting(&Settings::values.disable_fps_limit, - ui->disable_fps_limit, disable_fps_limit); ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_assembly_shaders, ui->use_assembly_shaders, use_assembly_shaders); ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_asynchronous_shaders, @@ -103,7 +100,6 @@ void ConfigureGraphicsAdvanced::SetupPerGameUI() { if (Settings::IsConfiguringGlobal()) { ui->gpu_accuracy->setEnabled(Settings::values.gpu_accuracy.UsingGlobal()); ui->use_vsync->setEnabled(Settings::values.use_vsync.UsingGlobal()); - ui->disable_fps_limit->setEnabled(Settings::values.disable_fps_limit.UsingGlobal()); ui->use_assembly_shaders->setEnabled(Settings::values.use_assembly_shaders.UsingGlobal()); ui->use_asynchronous_shaders->setEnabled( Settings::values.use_asynchronous_shaders.UsingGlobal()); @@ -116,8 +112,6 @@ void ConfigureGraphicsAdvanced::SetupPerGameUI() { } ConfigurationShared::SetColoredTristate(ui->use_vsync, Settings::values.use_vsync, use_vsync); - ConfigurationShared::SetColoredTristate(ui->disable_fps_limit, - Settings::values.disable_fps_limit, disable_fps_limit); ConfigurationShared::SetColoredTristate( ui->use_assembly_shaders, Settings::values.use_assembly_shaders, use_assembly_shaders); ConfigurationShared::SetColoredTristate(ui->use_asynchronous_shaders, diff --git a/src/yuzu/configuration/configure_graphics_advanced.h b/src/yuzu/configuration/configure_graphics_advanced.h index 6ac5f20ec..9148aacf2 100644 --- a/src/yuzu/configuration/configure_graphics_advanced.h +++ b/src/yuzu/configuration/configure_graphics_advanced.h @@ -35,7 +35,6 @@ private: std::unique_ptr<Ui::ConfigureGraphicsAdvanced> ui; ConfigurationShared::CheckState use_vsync; - ConfigurationShared::CheckState disable_fps_limit; ConfigurationShared::CheckState use_assembly_shaders; ConfigurationShared::CheckState use_asynchronous_shaders; ConfigurationShared::CheckState use_fast_gpu_time; diff --git a/src/yuzu/configuration/configure_graphics_advanced.ui b/src/yuzu/configuration/configure_graphics_advanced.ui index 18c43629e..ad0840355 100644 --- a/src/yuzu/configuration/configure_graphics_advanced.ui +++ b/src/yuzu/configuration/configure_graphics_advanced.ui @@ -77,24 +77,6 @@ </widget> </item> <item> - <widget class="QCheckBox" name="disable_fps_limit"> - <property name="enabled"> - <bool>true</bool> - </property> - <property name="toolTip"> - <string> - <html><head/><body> - <p>Presents guest frames as they become available, disabling the FPS limit in most titles.</p> - <p>NOTE: Will cause instabilities.</p> - </body></html> - </string> - </property> - <property name="text"> - <string>Disable framerate limit (experimental)</string> - </property> - </widget> - </item> - <item> <widget class="QCheckBox" name="use_assembly_shaders"> <property name="toolTip"> <string>Enabling this reduces shader stutter. Enables OpenGL assembly shaders on supported Nvidia devices (NV_gpu_program5 is required). This feature is experimental.</string> diff --git a/src/yuzu/configuration/configure_input_advanced.cpp b/src/yuzu/configuration/configure_input_advanced.cpp index d8d3b83dc..2f1419b5b 100644 --- a/src/yuzu/configuration/configure_input_advanced.cpp +++ b/src/yuzu/configuration/configure_input_advanced.cpp @@ -148,12 +148,12 @@ void ConfigureInputAdvanced::LoadConfiguration() { } } - ui->debug_enabled->setChecked(Settings::values.debug_pad_enabled); - ui->mouse_enabled->setChecked(Settings::values.mouse_enabled); - ui->keyboard_enabled->setChecked(Settings::values.keyboard_enabled); - ui->emulate_analog_keyboard->setChecked(Settings::values.emulate_analog_keyboard); - ui->mouse_panning->setChecked(Settings::values.mouse_panning); - ui->mouse_panning_sensitivity->setValue(Settings::values.mouse_panning_sensitivity); + ui->debug_enabled->setChecked(Settings::values.debug_pad_enabled.GetValue()); + ui->mouse_enabled->setChecked(Settings::values.mouse_enabled.GetValue()); + ui->keyboard_enabled->setChecked(Settings::values.keyboard_enabled.GetValue()); + ui->emulate_analog_keyboard->setChecked(Settings::values.emulate_analog_keyboard.GetValue()); + ui->mouse_panning->setChecked(Settings::values.mouse_panning.GetValue()); + ui->mouse_panning_sensitivity->setValue(Settings::values.mouse_panning_sensitivity.GetValue()); ui->touchscreen_enabled->setChecked(Settings::values.touchscreen.enabled); UpdateUIEnabled(); diff --git a/src/yuzu/configuration/configure_motion_touch.cpp b/src/yuzu/configuration/configure_motion_touch.cpp index 6a5d625df..f8e08c422 100644 --- a/src/yuzu/configuration/configure_motion_touch.cpp +++ b/src/yuzu/configuration/configure_motion_touch.cpp @@ -101,15 +101,16 @@ ConfigureMotionTouch::ConfigureMotionTouch(QWidget* parent, ConfigureMotionTouch::~ConfigureMotionTouch() = default; void ConfigureMotionTouch::SetConfiguration() { - const Common::ParamPackage motion_param(Settings::values.motion_device); - const Common::ParamPackage touch_param(Settings::values.touch_device); + const Common::ParamPackage motion_param(Settings::values.motion_device.GetValue()); + const Common::ParamPackage touch_param(Settings::values.touch_device.GetValue()); - ui->touch_from_button_checkbox->setChecked(Settings::values.use_touch_from_button); + ui->touch_from_button_checkbox->setChecked(Settings::values.use_touch_from_button.GetValue()); touch_from_button_maps = Settings::values.touch_from_button_maps; for (const auto& touch_map : touch_from_button_maps) { ui->touch_from_button_map->addItem(QString::fromStdString(touch_map.name)); } - ui->touch_from_button_map->setCurrentIndex(Settings::values.touch_from_button_map_index); + ui->touch_from_button_map->setCurrentIndex( + Settings::values.touch_from_button_map_index.GetValue()); ui->motion_sensitivity->setValue(motion_param.Get("sensitivity", 0.01f)); min_x = touch_param.Get("min_x", 100); @@ -124,7 +125,7 @@ void ConfigureMotionTouch::SetConfiguration() { udp_server_list_model->setStringList({}); ui->udp_server_list->setModel(udp_server_list_model); - std::stringstream ss(Settings::values.udp_input_servers); + std::stringstream ss(Settings::values.udp_input_servers.GetValue()); std::string token; while (std::getline(ss, token, ',')) { diff --git a/src/yuzu/configuration/configure_profile_manager.cpp b/src/yuzu/configuration/configure_profile_manager.cpp index f5881e58d..ac849b01d 100644 --- a/src/yuzu/configuration/configure_profile_manager.cpp +++ b/src/yuzu/configuration/configure_profile_manager.cpp @@ -166,7 +166,7 @@ void ConfigureProfileManager::PopulateUserList() { void ConfigureProfileManager::UpdateCurrentUser() { ui->pm_add->setEnabled(profile_manager->GetUserCount() < Service::Account::MAX_USERS); - const auto& current_user = profile_manager->GetUser(Settings::values.current_user); + const auto& current_user = profile_manager->GetUser(Settings::values.current_user.GetValue()); ASSERT(current_user); const auto username = GetAccountUsername(*profile_manager, *current_user); @@ -245,15 +245,18 @@ void ConfigureProfileManager::DeleteUser() { this, tr("Confirm Delete"), tr("You are about to delete user with name \"%1\". Are you sure?").arg(username)); - if (confirm == QMessageBox::No) + if (confirm == QMessageBox::No) { return; + } - if (Settings::values.current_user == tree_view->currentIndex().row()) + if (Settings::values.current_user.GetValue() == tree_view->currentIndex().row()) { Settings::values.current_user = 0; + } UpdateCurrentUser(); - if (!profile_manager->RemoveUser(*uuid)) + if (!profile_manager->RemoveUser(*uuid)) { return; + } item_model->removeRows(tree_view->currentIndex().row(), 1); tree_view->clearSelection(); diff --git a/src/yuzu/configuration/configure_service.cpp b/src/yuzu/configuration/configure_service.cpp index 6d954a67f..4aa424803 100644 --- a/src/yuzu/configuration/configure_service.cpp +++ b/src/yuzu/configuration/configure_service.cpp @@ -65,7 +65,7 @@ void ConfigureService::RetranslateUi() { void ConfigureService::SetConfiguration() { const int index = - ui->bcat_source->findData(QString::fromStdString(Settings::values.bcat_backend)); + ui->bcat_source->findData(QString::fromStdString(Settings::values.bcat_backend.GetValue())); ui->bcat_source->setCurrentIndex(index == -1 ? 0 : index); } diff --git a/src/yuzu/configuration/configure_ui.cpp b/src/yuzu/configuration/configure_ui.cpp index 9674119e1..e8f41bf65 100644 --- a/src/yuzu/configuration/configure_ui.cpp +++ b/src/yuzu/configuration/configure_ui.cpp @@ -113,11 +113,12 @@ void ConfigureUi::SetConfiguration() { ui->theme_combobox->setCurrentIndex(ui->theme_combobox->findData(UISettings::values.theme)); ui->language_combobox->setCurrentIndex( ui->language_combobox->findData(UISettings::values.language)); - ui->show_add_ons->setChecked(UISettings::values.show_add_ons); + ui->show_add_ons->setChecked(UISettings::values.show_add_ons.GetValue()); ui->icon_size_combobox->setCurrentIndex( - ui->icon_size_combobox->findData(UISettings::values.icon_size)); + ui->icon_size_combobox->findData(UISettings::values.icon_size.GetValue())); - ui->enable_screenshot_save_as->setChecked(UISettings::values.enable_screenshot_save_as); + ui->enable_screenshot_save_as->setChecked( + UISettings::values.enable_screenshot_save_as.GetValue()); ui->screenshot_path_edit->setText(QString::fromStdString( Common::FS::GetYuzuPathString(Common::FS::YuzuPath::ScreenshotsDir))); } @@ -178,7 +179,7 @@ void ConfigureUi::InitializeRowComboBoxes() { void ConfigureUi::UpdateFirstRowComboBox(bool init) { const int currentIndex = - init ? UISettings::values.row_1_text_id + init ? UISettings::values.row_1_text_id.GetValue() : ui->row_1_text_combobox->findData(ui->row_1_text_combobox->currentData()); ui->row_1_text_combobox->clear(); @@ -197,7 +198,7 @@ void ConfigureUi::UpdateFirstRowComboBox(bool init) { void ConfigureUi::UpdateSecondRowComboBox(bool init) { const int currentIndex = - init ? UISettings::values.row_2_text_id + init ? UISettings::values.row_2_text_id.GetValue() : ui->row_2_text_combobox->findData(ui->row_2_text_combobox->currentData()); ui->row_2_text_combobox->clear(); diff --git a/src/yuzu/configuration/configure_web.cpp b/src/yuzu/configuration/configure_web.cpp index f3f3b54d6..d779251b4 100644 --- a/src/yuzu/configuration/configure_web.cpp +++ b/src/yuzu/configuration/configure_web.cpp @@ -88,22 +88,22 @@ void ConfigureWeb::SetConfiguration() { ui->web_signup_link->setOpenExternalLinks(true); ui->web_token_info_link->setOpenExternalLinks(true); - if (Settings::values.yuzu_username.empty()) { + if (Settings::values.yuzu_username.GetValue().empty()) { ui->username->setText(tr("Unspecified")); } else { - ui->username->setText(QString::fromStdString(Settings::values.yuzu_username)); + ui->username->setText(QString::fromStdString(Settings::values.yuzu_username.GetValue())); } - ui->toggle_telemetry->setChecked(Settings::values.enable_telemetry); - ui->edit_token->setText(QString::fromStdString( - GenerateDisplayToken(Settings::values.yuzu_username, Settings::values.yuzu_token))); + ui->toggle_telemetry->setChecked(Settings::values.enable_telemetry.GetValue()); + ui->edit_token->setText(QString::fromStdString(GenerateDisplayToken( + Settings::values.yuzu_username.GetValue(), Settings::values.yuzu_token.GetValue()))); // Connect after setting the values, to avoid calling OnLoginChanged now connect(ui->edit_token, &QLineEdit::textChanged, this, &ConfigureWeb::OnLoginChanged); user_verified = true; - ui->toggle_discordrpc->setChecked(UISettings::values.enable_discord_presence); + ui->toggle_discordrpc->setChecked(UISettings::values.enable_discord_presence.GetValue()); } void ConfigureWeb::ApplyConfiguration() { diff --git a/src/yuzu/debugger/console.cpp b/src/yuzu/debugger/console.cpp index c11a326ac..22ca1285d 100644 --- a/src/yuzu/debugger/console.cpp +++ b/src/yuzu/debugger/console.cpp @@ -15,10 +15,10 @@ namespace Debugger { void ToggleConsole() { static bool console_shown = false; - if (console_shown == UISettings::values.show_console) { + if (console_shown == UISettings::values.show_console.GetValue()) { return; } else { - console_shown = UISettings::values.show_console; + console_shown = UISettings::values.show_console.GetValue(); } #if defined(_WIN32) && !defined(_DEBUG) 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) { } void MicroProfileWidget::mouseMoveEvent(QMouseEvent* ev) { - MicroProfileMousePosition(ev->x() / x_scale, ev->y() / y_scale, 0); + MicroProfileMousePosition(ev->pos().x() / x_scale, ev->pos().y() / y_scale, 0); ev->accept(); } void MicroProfileWidget::mousePressEvent(QMouseEvent* ev) { - MicroProfileMousePosition(ev->x() / x_scale, ev->y() / y_scale, 0); + MicroProfileMousePosition(ev->pos().x() / x_scale, ev->pos().y() / y_scale, 0); MicroProfileMouseButton(ev->buttons() & Qt::LeftButton, ev->buttons() & Qt::RightButton); ev->accept(); } void MicroProfileWidget::mouseReleaseEvent(QMouseEvent* ev) { - MicroProfileMousePosition(ev->x() / x_scale, ev->y() / y_scale, 0); + MicroProfileMousePosition(ev->pos().x() / x_scale, ev->pos().y() / y_scale, 0); MicroProfileMouseButton(ev->buttons() & Qt::LeftButton, ev->buttons() & Qt::RightButton); ev->accept(); } void MicroProfileWidget::wheelEvent(QWheelEvent* ev) { - MicroProfileMousePosition(ev->x() / x_scale, ev->y() / y_scale, ev->delta() / 120); + MicroProfileMousePosition(ev->pos().x() / x_scale, ev->pos().y() / y_scale, + ev->angleDelta().y() / 120); ev->accept(); } diff --git a/src/yuzu/game_list.cpp b/src/yuzu/game_list.cpp index da956c99b..218b4782b 100644 --- a/src/yuzu/game_list.cpp +++ b/src/yuzu/game_list.cpp @@ -244,7 +244,8 @@ void GameList::OnUpdateThemedIcons() { for (int i = 0; i < item_model->invisibleRootItem()->rowCount(); i++) { QStandardItem* child = item_model->invisibleRootItem()->child(i); - const int icon_size = std::min(static_cast<int>(UISettings::values.icon_size), 64); + const int icon_size = + std::min(static_cast<int>(UISettings::values.icon_size.GetValue()), 64); switch (child->data(GameListItem::TypeRole).value<GameListItemType>()) { case GameListItemType::SdmcDir: child->setData( @@ -521,7 +522,9 @@ void GameList::AddGamePopup(QMenu& context_menu, u64 program_id, const std::stri QAction* remove_custom_config = remove_menu->addAction(tr("Remove Custom Configuration")); remove_menu->addSeparator(); QAction* remove_all_content = remove_menu->addAction(tr("Remove All Installed Contents")); - QAction* dump_romfs = context_menu.addAction(tr("Dump RomFS")); + QMenu* dump_romfs_menu = context_menu.addMenu(tr("Dump RomFS")); + QAction* dump_romfs = dump_romfs_menu->addAction(tr("Dump RomFS")); + QAction* dump_romfs_sdmc = dump_romfs_menu->addAction(tr("Dump RomFS to SDMC")); QAction* copy_tid = context_menu.addAction(tr("Copy Title ID to Clipboard")); QAction* navigate_to_gamedb_entry = context_menu.addAction(tr("Navigate to GameDB entry")); context_menu.addSeparator(); @@ -570,8 +573,12 @@ void GameList::AddGamePopup(QMenu& context_menu, u64 program_id, const std::stri connect(remove_custom_config, &QAction::triggered, [this, program_id, path]() { emit RemoveFileRequested(program_id, GameListRemoveTarget::CustomConfiguration, path); }); - connect(dump_romfs, &QAction::triggered, - [this, program_id, path]() { emit DumpRomFSRequested(program_id, path); }); + connect(dump_romfs, &QAction::triggered, [this, program_id, path]() { + emit DumpRomFSRequested(program_id, path, DumpRomFSTarget::Normal); + }); + connect(dump_romfs_sdmc, &QAction::triggered, [this, program_id, path]() { + emit DumpRomFSRequested(program_id, path, DumpRomFSTarget::SDMC); + }); connect(copy_tid, &QAction::triggered, [this, program_id]() { emit CopyTIDRequested(program_id); }); 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 { CustomConfiguration, }; +enum class DumpRomFSTarget { + Normal, + SDMC, +}; + enum class InstalledEntryType { Game, Update, @@ -92,7 +97,7 @@ signals: void RemoveInstalledEntryRequested(u64 program_id, InstalledEntryType type); void RemoveFileRequested(u64 program_id, GameListRemoveTarget target, const std::string& game_path); - void DumpRomFSRequested(u64 program_id, const std::string& game_path); + void DumpRomFSRequested(u64 program_id, const std::string& game_path, DumpRomFSTarget target); void CopyTIDRequested(u64 program_id); void NavigateToGamedbEntryRequested(u64 program_id, const CompatibilityList& compatibility_list); diff --git a/src/yuzu/game_list_p.h b/src/yuzu/game_list_p.h index 978d27325..982c0789d 100644 --- a/src/yuzu/game_list_p.h +++ b/src/yuzu/game_list_p.h @@ -80,7 +80,7 @@ public: setData(qulonglong(program_id), ProgramIdRole); setData(game_type, FileTypeRole); - const u32 size = UISettings::values.icon_size; + const u32 size = UISettings::values.icon_size.GetValue(); QPixmap picture; if (!picture.loadFromData(picture_data.data(), static_cast<u32>(picture_data.size()))) { @@ -108,8 +108,8 @@ public: data(TitleRole).toString(), }}; - const auto& row1 = row_data.at(UISettings::values.row_1_text_id); - const int row2_id = UISettings::values.row_2_text_id; + const auto& row1 = row_data.at(UISettings::values.row_1_text_id.GetValue()); + const int row2_id = UISettings::values.row_2_text_id.GetValue(); if (role == SortRole) { return row1.toLower(); @@ -233,7 +233,8 @@ public: UISettings::GameDir* game_dir = &directory; setData(QVariant(UISettings::values.game_dirs.indexOf(directory)), GameDirRole); - const int icon_size = std::min(static_cast<int>(UISettings::values.icon_size), 64); + const int icon_size = + std::min(static_cast<int>(UISettings::values.icon_size.GetValue()), 64); switch (dir_type) { case GameListItemType::SdmcDir: setData( @@ -294,7 +295,8 @@ public: explicit GameListAddDir() { setData(type(), TypeRole); - const int icon_size = std::min(static_cast<int>(UISettings::values.icon_size), 64); + const int icon_size = + std::min(static_cast<int>(UISettings::values.icon_size.GetValue()), 64); setData(QIcon::fromTheme(QStringLiteral("plus")) .pixmap(icon_size) .scaled(icon_size, icon_size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation), @@ -316,7 +318,8 @@ public: explicit GameListFavorites() { setData(type(), TypeRole); - const int icon_size = std::min(static_cast<int>(UISettings::values.icon_size), 64); + const int icon_size = + std::min(static_cast<int>(UISettings::values.icon_size.GetValue()), 64); setData(QIcon::fromTheme(QStringLiteral("star")) .pixmap(icon_size) .scaled(icon_size, icon_size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation), diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index 6f5b2f6d6..b7fd33ae7 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -11,11 +11,11 @@ #endif // VFS includes must be before glad as they will conflict with Windows file api, which uses defines. -#include "applets/controller.h" -#include "applets/error.h" -#include "applets/profile_select.h" -#include "applets/software_keyboard.h" -#include "applets/web_browser.h" +#include "applets/qt_controller.h" +#include "applets/qt_error.h" +#include "applets/qt_profile_select.h" +#include "applets/qt_software_keyboard.h" +#include "applets/qt_web_browser.h" #include "common/nvidia_flags.h" #include "configuration/configure_input.h" #include "configuration/configure_per_game.h" @@ -104,6 +104,7 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual #include "input_common/main.h" #include "util/overlay_dialog.h" #include "video_core/gpu.h" +#include "video_core/renderer_base.h" #include "video_core/shader_notify.h" #include "yuzu/about_dialog.h" #include "yuzu/bootmanager.h" @@ -155,11 +156,13 @@ enum class CalloutFlag : uint32_t { }; void GMainWindow::ShowTelemetryCallout() { - if (UISettings::values.callout_flags & static_cast<uint32_t>(CalloutFlag::Telemetry)) { + if (UISettings::values.callout_flags.GetValue() & + static_cast<uint32_t>(CalloutFlag::Telemetry)) { return; } - UISettings::values.callout_flags |= static_cast<uint32_t>(CalloutFlag::Telemetry); + UISettings::values.callout_flags = + UISettings::values.callout_flags.GetValue() | static_cast<uint32_t>(CalloutFlag::Telemetry); const QString telemetry_message = tr("<a href='https://yuzu-emu.org/help/feature/telemetry/'>Anonymous " "data is collected</a> to help improve yuzu. " @@ -176,7 +179,7 @@ static void InitializeLogging() { using namespace Common; Log::Filter log_filter; - log_filter.ParseFilterString(Settings::values.log_filter); + log_filter.ParseFilterString(Settings::values.log_filter.GetValue()); Log::SetGlobalFilter(log_filter); const auto log_dir = FS::GetYuzuPath(FS::YuzuPath::LogDir); @@ -215,7 +218,7 @@ GMainWindow::GMainWindow() default_theme_paths = QIcon::themeSearchPaths(); UpdateUITheme(); - SetDiscordEnabled(UISettings::values.enable_discord_presence); + SetDiscordEnabled(UISettings::values.enable_discord_presence.GetValue()); discord_rpc->Update(); RegisterMetaTypes(); @@ -1059,23 +1062,24 @@ void GMainWindow::RestoreUIState() { render_window->restoreGeometry(UISettings::values.renderwindow_geometry); #if MICROPROFILE_ENABLED microProfileDialog->restoreGeometry(UISettings::values.microprofile_geometry); - microProfileDialog->setVisible(UISettings::values.microprofile_visible); + microProfileDialog->setVisible(UISettings::values.microprofile_visible.GetValue()); #endif game_list->LoadInterfaceLayout(); - ui.action_Single_Window_Mode->setChecked(UISettings::values.single_window_mode); + ui.action_Single_Window_Mode->setChecked(UISettings::values.single_window_mode.GetValue()); ToggleWindowMode(); - ui.action_Fullscreen->setChecked(UISettings::values.fullscreen); + ui.action_Fullscreen->setChecked(UISettings::values.fullscreen.GetValue()); - ui.action_Display_Dock_Widget_Headers->setChecked(UISettings::values.display_titlebar); + ui.action_Display_Dock_Widget_Headers->setChecked( + UISettings::values.display_titlebar.GetValue()); OnDisplayTitleBars(ui.action_Display_Dock_Widget_Headers->isChecked()); - ui.action_Show_Filter_Bar->setChecked(UISettings::values.show_filter_bar); + ui.action_Show_Filter_Bar->setChecked(UISettings::values.show_filter_bar.GetValue()); game_list->SetFilterVisible(ui.action_Show_Filter_Bar->isChecked()); - ui.action_Show_Status_Bar->setChecked(UISettings::values.show_status_bar); + ui.action_Show_Status_Bar->setChecked(UISettings::values.show_status_bar.GetValue()); statusBar()->setVisible(ui.action_Show_Status_Bar->isChecked()); Debugger::ToggleConsole(); } @@ -1242,13 +1246,14 @@ bool GMainWindow::LoadROM(const QString& filename, std::size_t program_index) { const Core::System::ResultStatus result{ system.Load(*render_window, filename.toStdString(), program_index)}; - const auto drd_callout = - (UISettings::values.callout_flags & static_cast<u32>(CalloutFlag::DRDDeprecation)) == 0; + const auto drd_callout = (UISettings::values.callout_flags.GetValue() & + static_cast<u32>(CalloutFlag::DRDDeprecation)) == 0; if (result == Core::System::ResultStatus::Success && system.GetAppLoader().GetFileType() == Loader::FileType::DeconstructedRomDirectory && drd_callout) { - UISettings::values.callout_flags |= static_cast<u32>(CalloutFlag::DRDDeprecation); + UISettings::values.callout_flags = UISettings::values.callout_flags.GetValue() | + static_cast<u32>(CalloutFlag::DRDDeprecation); QMessageBox::warning( this, tr("Warning Outdated Game Format"), tr("You are using the deconstructed ROM directory format for this game, which is an " @@ -1350,6 +1355,9 @@ void GMainWindow::BootGame(const QString& filename, std::size_t program_index, S ConfigureVibration::SetAllVibrationDevices(); + // Disable fps limit toggle when booting a new title + Settings::values.disable_fps_limit.SetValue(false); + // Save configurations UpdateUISettings(); game_list->SaveInterfaceLayout(); @@ -1422,8 +1430,14 @@ void GMainWindow::BootGame(const QString& filename, std::size_t program_index, S title_name = Common::FS::PathToUTF8String( std::filesystem::path{filename.toStdU16String()}.filename()); } + const bool is_64bit = system.Kernel().CurrentProcess()->Is64BitProcess(); + const auto instruction_set_suffix = is_64bit ? tr("(64-bit)") : tr("(32-bit)"); + title_name = tr("%1 %2", "%1 is the title name. %2 indicates if the title is 64-bit or 32-bit") + .arg(QString::fromStdString(title_name), instruction_set_suffix) + .toStdString(); LOG_INFO(Frontend, "Booting game: {:016X} | {} | {}", title_id, title_name, title_version); - UpdateWindowTitle(title_name, title_version); + const auto gpu_vendor = system.GPU().Renderer().GetDeviceVendor(); + UpdateWindowTitle(title_name, title_version, gpu_vendor); loading_screen->Prepare(system.GetAppLoader()); loading_screen->show(); @@ -1877,7 +1891,8 @@ void GMainWindow::RemoveCustomConfiguration(u64 program_id, const std::string& g } } -void GMainWindow::OnGameListDumpRomFS(u64 program_id, const std::string& game_path) { +void GMainWindow::OnGameListDumpRomFS(u64 program_id, const std::string& game_path, + DumpRomFSTarget target) { const auto failed = [this] { QMessageBox::warning(this, tr("RomFS Extraction Failed!"), tr("There was an error copying the RomFS files or the user " @@ -1905,7 +1920,10 @@ void GMainWindow::OnGameListDumpRomFS(u64 program_id, const std::string& game_pa return; } - const auto dump_dir = Common::FS::GetYuzuPath(Common::FS::YuzuPath::DumpDir); + const auto dump_dir = + target == DumpRomFSTarget::Normal + ? Common::FS::GetYuzuPath(Common::FS::YuzuPath::DumpDir) + : Common::FS::GetYuzuPath(Common::FS::YuzuPath::SDMCDir) / "atmosphere" / "contents"; const auto romfs_dir = fmt::format("{:016X}/romfs", *romfs_title_id); const auto path = Common::FS::PathToUTF8String(dump_dir / romfs_dir); @@ -1915,7 +1933,8 @@ void GMainWindow::OnGameListDumpRomFS(u64 program_id, const std::string& game_pa if (*romfs_title_id == program_id) { const u64 ivfc_offset = loader->ReadRomFSIVFCOffset(); const FileSys::PatchManager pm{program_id, system.GetFileSystemController(), installed}; - romfs = pm.PatchRomFS(file, ivfc_offset, FileSys::ContentRecordType::Program); + romfs = + pm.PatchRomFS(file, ivfc_offset, FileSys::ContentRecordType::Program, nullptr, false); } else { romfs = installed.GetEntry(*romfs_title_id, FileSys::ContentRecordType::Data)->GetRomFS(); } @@ -2443,7 +2462,8 @@ void GMainWindow::ErrorDisplayDisplayError(QString error_code, QString error_tex } void GMainWindow::OnMenuReportCompatibility() { - if (!Settings::values.yuzu_token.empty() && !Settings::values.yuzu_username.empty()) { + if (!Settings::values.yuzu_token.GetValue().empty() && + !Settings::values.yuzu_username.GetValue().empty()) { CompatDB compatdb{this}; compatdb.exec(); } else { @@ -2608,7 +2628,7 @@ void GMainWindow::ResetWindowSize1080() { void GMainWindow::OnConfigure() { const auto old_theme = UISettings::values.theme; - const bool old_discord_presence = UISettings::values.enable_discord_presence; + const bool old_discord_presence = UISettings::values.enable_discord_presence.GetValue(); ConfigureDialog configure_dialog(this, hotkey_registry, input_subsystem.get()); connect(&configure_dialog, &ConfigureDialog::LanguageChanged, this, @@ -2665,8 +2685,8 @@ void GMainWindow::OnConfigure() { if (UISettings::values.theme != old_theme) { UpdateUITheme(); } - if (UISettings::values.enable_discord_presence != old_discord_presence) { - SetDiscordEnabled(UISettings::values.enable_discord_presence); + if (UISettings::values.enable_discord_presence.GetValue() != old_discord_presence) { + SetDiscordEnabled(UISettings::values.enable_discord_presence.GetValue()); } emit UpdateThemedIcons(); @@ -2822,7 +2842,8 @@ void GMainWindow::OnCaptureScreenshot() { } } #endif - render_window->CaptureScreenshot(UISettings::values.screenshot_resolution_factor, filename); + render_window->CaptureScreenshot(UISettings::values.screenshot_resolution_factor.GetValue(), + filename); OnStartGame(); } @@ -2852,8 +2873,8 @@ void GMainWindow::MigrateConfigFiles() { } } -void GMainWindow::UpdateWindowTitle(const std::string& title_name, - const std::string& title_version) { +void GMainWindow::UpdateWindowTitle(std::string_view title_name, std::string_view title_version, + std::string_view gpu_vendor) { const auto branch_name = std::string(Common::g_scm_branch); const auto description = std::string(Common::g_scm_desc); const auto build_id = std::string(Common::g_build_id); @@ -2866,7 +2887,8 @@ void GMainWindow::UpdateWindowTitle(const std::string& title_name, if (title_name.empty()) { setWindowTitle(QString::fromStdString(window_title)); } else { - const auto run_title = fmt::format("{} | {} | {}", window_title, title_name, title_version); + const auto run_title = + fmt::format("{} | {} | {} | {}", window_title, title_name, title_version, gpu_vendor); setWindowTitle(QString::fromStdString(run_title)); } } @@ -2896,7 +2918,12 @@ void GMainWindow::UpdateStatusBar() { } else { emu_speed_label->setText(tr("Speed: %1%").arg(results.emulation_speed * 100.0, 0, 'f', 0)); } - game_fps_label->setText(tr("Game: %1 FPS").arg(results.average_game_fps, 0, 'f', 0)); + if (Settings::values.disable_fps_limit) { + game_fps_label->setText( + tr("Game: %1 FPS (Limit off)").arg(results.average_game_fps, 0, 'f', 0)); + } else { + game_fps_label->setText(tr("Game: %1 FPS").arg(results.average_game_fps, 0, 'f', 0)); + } emu_frametime_label->setText(tr("Frame: %1 ms").arg(results.frametime * 1000.0, 0, 'f', 2)); emu_speed_label->setVisible(!Settings::values.use_multi_core.GetValue()); 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; class WaitTreeWidget; enum class GameListOpenTarget; enum class GameListRemoveTarget; +enum class DumpRomFSTarget; enum class InstalledEntryType; class GameListPlaceholder; @@ -244,7 +245,7 @@ private slots: void OnGameListRemoveInstalledEntry(u64 program_id, InstalledEntryType type); void OnGameListRemoveFile(u64 program_id, GameListRemoveTarget target, const std::string& game_path); - void OnGameListDumpRomFS(u64 program_id, const std::string& game_path); + void OnGameListDumpRomFS(u64 program_id, const std::string& game_path, DumpRomFSTarget target); void OnGameListCopyTID(u64 program_id); void OnGameListNavigateToGamedbEntry(u64 program_id, const CompatibilityList& compatibility_list); @@ -287,8 +288,8 @@ private: InstallResult InstallNSPXCI(const QString& filename); InstallResult InstallNCA(const QString& filename); void MigrateConfigFiles(); - void UpdateWindowTitle(const std::string& title_name = {}, - const std::string& title_version = {}); + void UpdateWindowTitle(std::string_view title_name = {}, std::string_view title_version = {}, + std::string_view gpu_vendor = {}); void UpdateStatusBar(); void UpdateStatusButtons(); void UpdateUISettings(); diff --git a/src/yuzu/uisettings.h b/src/yuzu/uisettings.h index cdcb83f9f..7b9d2dd53 100644 --- a/src/yuzu/uisettings.h +++ b/src/yuzu/uisettings.h @@ -13,6 +13,7 @@ #include <QStringList> #include <QVector> #include "common/common_types.h" +#include "common/settings.h" namespace UISettings { @@ -48,26 +49,26 @@ struct Values { QByteArray gamelist_header_state; QByteArray microprofile_geometry; - bool microprofile_visible; + Settings::BasicSetting<bool> microprofile_visible{false, "microProfileDialogVisible"}; - bool single_window_mode; - bool fullscreen; - bool display_titlebar; - bool show_filter_bar; - bool show_status_bar; + Settings::BasicSetting<bool> single_window_mode{true, "singleWindowMode"}; + Settings::BasicSetting<bool> fullscreen{false, "fullscreen"}; + Settings::BasicSetting<bool> display_titlebar{true, "displayTitleBars"}; + Settings::BasicSetting<bool> show_filter_bar{true, "showFilterBar"}; + Settings::BasicSetting<bool> show_status_bar{true, "showStatusBar"}; - bool confirm_before_closing; - bool first_start; - bool pause_when_in_background; - bool hide_mouse; + Settings::BasicSetting<bool> confirm_before_closing{true, "confirmClose"}; + Settings::BasicSetting<bool> first_start{true, "firstStart"}; + Settings::BasicSetting<bool> pause_when_in_background{false, "pauseWhenInBackground"}; + Settings::BasicSetting<bool> hide_mouse{false, "hideInactiveMouse"}; - bool select_user_on_boot; + Settings::BasicSetting<bool> select_user_on_boot{false, "select_user_on_boot"}; // Discord RPC - bool enable_discord_presence; + Settings::BasicSetting<bool> enable_discord_presence{true, "enable_discord_presence"}; - bool enable_screenshot_save_as; - u16 screenshot_resolution_factor; + Settings::BasicSetting<bool> enable_screenshot_save_as{true, "enable_screenshot_save_as"}; + Settings::BasicSetting<u16> screenshot_resolution_factor{0, "screenshot_resolution_factor"}; QString roms_path; QString symbols_path; @@ -83,18 +84,18 @@ struct Values { // Shortcut name <Shortcut, context> std::vector<Shortcut> shortcuts; - uint32_t callout_flags; + Settings::BasicSetting<uint32_t> callout_flags{0, "calloutFlags"}; // logging - bool show_console; + Settings::BasicSetting<bool> show_console{false, "showConsole"}; // Game List - bool show_add_ons; - uint32_t icon_size; - uint8_t row_1_text_id; - uint8_t row_2_text_id; + Settings::BasicSetting<bool> show_add_ons{true, "show_add_ons"}; + Settings::BasicSetting<uint32_t> icon_size{64, "icon_size"}; + Settings::BasicSetting<uint8_t> row_1_text_id{3, "row_1_text_id"}; + Settings::BasicSetting<uint8_t> row_2_text_id{2, "row_2_text_id"}; std::atomic_bool is_game_list_reload_pending{false}; - bool cache_game_list; + Settings::BasicSetting<bool> cache_game_list{true, "cache_game_list"}; bool configuration_applied; bool reset_to_defaults; diff --git a/src/yuzu_cmd/config.cpp b/src/yuzu_cmd/config.cpp index 60bf66ec0..325584a1a 100644 --- a/src/yuzu_cmd/config.cpp +++ b/src/yuzu_cmd/config.cpp @@ -241,6 +241,24 @@ static const std::array<int, 8> keyboard_mods{ SDL_SCANCODE_RCTRL, SDL_SCANCODE_RSHIFT, SDL_SCANCODE_RALT, SDL_SCANCODE_RGUI, }; +template <> +void Config::ReadSetting(const std::string& group, Settings::BasicSetting<float>& setting) { + setting = sdl2_config->GetReal(group, setting.GetLabel(), setting.GetDefault()); +} +template <> +void Config::ReadSetting(const std::string& group, Settings::BasicSetting<std::string>& setting) { + setting = sdl2_config->Get(group, setting.GetLabel(), setting.GetDefault()); +} +template <> +void Config::ReadSetting(const std::string& group, Settings::BasicSetting<bool>& setting) { + setting = sdl2_config->GetBoolean(group, setting.GetLabel(), setting.GetDefault()); +} +template <typename Type> +void Config::ReadSetting(const std::string& group, Settings::BasicSetting<Type>& setting) { + setting = static_cast<Type>(sdl2_config->GetInteger(group, setting.GetLabel(), + static_cast<long>(setting.GetDefault()))); +} + void Config::ReadValues() { // Controls for (std::size_t p = 0; p < Settings::values.players.GetValue().size(); ++p) { @@ -264,8 +282,7 @@ void Config::ReadValues() { } } - Settings::values.mouse_enabled = - sdl2_config->GetBoolean("ControlsGeneral", "mouse_enabled", false); + ReadSetting("ControlsGeneral", Settings::values.mouse_enabled); for (int i = 0; i < Settings::NativeMouseButton::NumMouseButtons; ++i) { std::string default_param = InputCommon::GenerateKeyboardParam(default_mouse_buttons[i]); Settings::values.mouse_buttons[i] = sdl2_config->Get( @@ -275,14 +292,11 @@ void Config::ReadValues() { Settings::values.mouse_buttons[i] = default_param; } - Settings::values.motion_device = sdl2_config->Get( - "ControlsGeneral", "motion_device", "engine:motion_emu,update_period:100,sensitivity:0.01"); + ReadSetting("ControlsGeneral", Settings::values.motion_device); - Settings::values.keyboard_enabled = - sdl2_config->GetBoolean("ControlsGeneral", "keyboard_enabled", false); + ReadSetting("ControlsGeneral", Settings::values.keyboard_enabled); - Settings::values.debug_pad_enabled = - sdl2_config->GetBoolean("ControlsGeneral", "debug_pad_enabled", false); + ReadSetting("ControlsGeneral", Settings::values.debug_pad_enabled); for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) { std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]); Settings::values.debug_pad_buttons[i] = sdl2_config->Get( @@ -303,12 +317,9 @@ void Config::ReadValues() { Settings::values.debug_pad_analogs[i] = default_param; } - Settings::values.vibration_enabled.SetValue( - sdl2_config->GetBoolean("ControlsGeneral", "vibration_enabled", true)); - Settings::values.enable_accurate_vibrations.SetValue( - sdl2_config->GetBoolean("ControlsGeneral", "enable_accurate_vibrations", false)); - Settings::values.motion_enabled.SetValue( - sdl2_config->GetBoolean("ControlsGeneral", "motion_enabled", true)); + ReadSetting("ControlsGeneral", Settings::values.vibration_enabled); + ReadSetting("ControlsGeneral", Settings::values.enable_accurate_vibrations); + ReadSetting("ControlsGeneral", Settings::values.motion_enabled); Settings::values.touchscreen.enabled = sdl2_config->GetBoolean("ControlsGeneral", "touch_enabled", true); Settings::values.touchscreen.rotation_angle = @@ -349,13 +360,11 @@ void Config::ReadValues() { Settings::TouchFromButtonMap{"default", {}}); num_touch_from_button_maps = 1; } - Settings::values.use_touch_from_button = - sdl2_config->GetBoolean("ControlsGeneral", "use_touch_from_button", false); - Settings::values.touch_from_button_map_index = - std::clamp(Settings::values.touch_from_button_map_index, 0, num_touch_from_button_maps - 1); + ReadSetting("ControlsGeneral", Settings::values.use_touch_from_button); + Settings::values.touch_from_button_map_index = std::clamp( + Settings::values.touch_from_button_map_index.GetValue(), 0, num_touch_from_button_maps - 1); - Settings::values.udp_input_servers = - sdl2_config->Get("Controls", "udp_input_address", InputCommon::CemuhookUDP::DEFAULT_SRV); + ReadSetting("ControlsGeneral", Settings::values.udp_input_servers); std::transform(keyboard_keys.begin(), keyboard_keys.end(), Settings::values.keyboard_keys.begin(), InputCommon::GenerateKeyboardParam); @@ -367,8 +376,7 @@ void Config::ReadValues() { Settings::values.keyboard_mods.begin(), InputCommon::GenerateKeyboardParam); // Data Storage - Settings::values.use_virtual_sd = - sdl2_config->GetBoolean("Data Storage", "use_virtual_sd", true); + ReadSetting("Data Storage", Settings::values.use_virtual_sd); FS::SetYuzuPath(FS::YuzuPath::NANDDir, sdl2_config->Get("Data Storage", "nand_directory", FS::GetYuzuPathString(FS::YuzuPath::NANDDir))); @@ -381,18 +389,16 @@ void Config::ReadValues() { FS::SetYuzuPath(FS::YuzuPath::DumpDir, sdl2_config->Get("Data Storage", "dump_directory", FS::GetYuzuPathString(FS::YuzuPath::DumpDir))); - Settings::values.gamecard_inserted = - sdl2_config->GetBoolean("Data Storage", "gamecard_inserted", false); - Settings::values.gamecard_current_game = - sdl2_config->GetBoolean("Data Storage", "gamecard_current_game", false); - Settings::values.gamecard_path = sdl2_config->Get("Data Storage", "gamecard_path", ""); + ReadSetting("Data Storage", Settings::values.gamecard_inserted); + ReadSetting("Data Storage", Settings::values.gamecard_current_game); + ReadSetting("Data Storage", Settings::values.gamecard_path); // System - Settings::values.use_docked_mode.SetValue( - sdl2_config->GetBoolean("System", "use_docked_mode", true)); + ReadSetting("System", Settings::values.use_docked_mode); - Settings::values.current_user = std::clamp<int>( - sdl2_config->GetInteger("System", "current_user", 0), 0, Service::Account::MAX_USERS - 1); + ReadSetting("System", Settings::values.current_user); + Settings::values.current_user = std::clamp<int>(Settings::values.current_user.GetValue(), 0, + Service::Account::MAX_USERS - 1); const auto rng_seed_enabled = sdl2_config->GetBoolean("System", "rng_seed_enabled", false); if (rng_seed_enabled) { @@ -409,89 +415,60 @@ void Config::ReadValues() { Settings::values.custom_rtc = std::nullopt; } - Settings::values.language_index.SetValue( - sdl2_config->GetInteger("System", "language_index", 1)); - Settings::values.time_zone_index.SetValue( - sdl2_config->GetInteger("System", "time_zone_index", 0)); + ReadSetting("System", Settings::values.language_index); + ReadSetting("System", Settings::values.time_zone_index); // Core - Settings::values.use_multi_core.SetValue( - sdl2_config->GetBoolean("Core", "use_multi_core", true)); + ReadSetting("Core", Settings::values.use_multi_core); // Renderer - const int renderer_backend = sdl2_config->GetInteger( - "Renderer", "backend", static_cast<int>(Settings::RendererBackend::OpenGL)); - Settings::values.renderer_backend.SetValue( - static_cast<Settings::RendererBackend>(renderer_backend)); - Settings::values.renderer_debug = sdl2_config->GetBoolean("Renderer", "debug", false); - Settings::values.vulkan_device.SetValue( - sdl2_config->GetInteger("Renderer", "vulkan_device", 0)); - - Settings::values.aspect_ratio.SetValue( - static_cast<int>(sdl2_config->GetInteger("Renderer", "aspect_ratio", 0))); - Settings::values.max_anisotropy.SetValue( - static_cast<int>(sdl2_config->GetInteger("Renderer", "max_anisotropy", 0))); - Settings::values.use_frame_limit.SetValue( - sdl2_config->GetBoolean("Renderer", "use_frame_limit", true)); - Settings::values.frame_limit.SetValue( - static_cast<u16>(sdl2_config->GetInteger("Renderer", "frame_limit", 100))); - Settings::values.use_disk_shader_cache.SetValue( - sdl2_config->GetBoolean("Renderer", "use_disk_shader_cache", false)); - const int gpu_accuracy_level = sdl2_config->GetInteger("Renderer", "gpu_accuracy", 1); - Settings::values.gpu_accuracy.SetValue(static_cast<Settings::GPUAccuracy>(gpu_accuracy_level)); - Settings::values.use_asynchronous_gpu_emulation.SetValue( - sdl2_config->GetBoolean("Renderer", "use_asynchronous_gpu_emulation", true)); - Settings::values.use_vsync.SetValue( - static_cast<u16>(sdl2_config->GetInteger("Renderer", "use_vsync", 1))); - Settings::values.disable_fps_limit.SetValue( - sdl2_config->GetBoolean("Renderer", "disable_fps_limit", false)); - Settings::values.use_assembly_shaders.SetValue( - sdl2_config->GetBoolean("Renderer", "use_assembly_shaders", true)); - Settings::values.use_asynchronous_shaders.SetValue( - sdl2_config->GetBoolean("Renderer", "use_asynchronous_shaders", false)); - Settings::values.use_nvdec_emulation.SetValue( - sdl2_config->GetBoolean("Renderer", "use_nvdec_emulation", true)); - Settings::values.accelerate_astc.SetValue( - sdl2_config->GetBoolean("Renderer", "accelerate_astc", true)); - Settings::values.use_fast_gpu_time.SetValue( - sdl2_config->GetBoolean("Renderer", "use_fast_gpu_time", true)); - - Settings::values.bg_red.SetValue( - static_cast<float>(sdl2_config->GetReal("Renderer", "bg_red", 0.0))); - Settings::values.bg_green.SetValue( - static_cast<float>(sdl2_config->GetReal("Renderer", "bg_green", 0.0))); - Settings::values.bg_blue.SetValue( - static_cast<float>(sdl2_config->GetReal("Renderer", "bg_blue", 0.0))); + ReadSetting("Renderer", Settings::values.renderer_backend); + ReadSetting("Renderer", Settings::values.renderer_debug); + ReadSetting("Renderer", Settings::values.vulkan_device); + + ReadSetting("Renderer", Settings::values.aspect_ratio); + ReadSetting("Renderer", Settings::values.max_anisotropy); + ReadSetting("Renderer", Settings::values.use_frame_limit); + ReadSetting("Renderer", Settings::values.frame_limit); + ReadSetting("Renderer", Settings::values.use_disk_shader_cache); + ReadSetting("Renderer", Settings::values.gpu_accuracy); + ReadSetting("Renderer", Settings::values.use_asynchronous_gpu_emulation); + ReadSetting("Renderer", Settings::values.use_vsync); + ReadSetting("Renderer", Settings::values.disable_fps_limit); + ReadSetting("Renderer", Settings::values.use_assembly_shaders); + ReadSetting("Renderer", Settings::values.use_asynchronous_shaders); + ReadSetting("Renderer", Settings::values.use_nvdec_emulation); + ReadSetting("Renderer", Settings::values.accelerate_astc); + ReadSetting("Renderer", Settings::values.use_fast_gpu_time); + + ReadSetting("Renderer", Settings::values.bg_red); + ReadSetting("Renderer", Settings::values.bg_green); + ReadSetting("Renderer", Settings::values.bg_blue); // Audio - Settings::values.sink_id = sdl2_config->Get("Audio", "output_engine", "auto"); - Settings::values.enable_audio_stretching.SetValue( - sdl2_config->GetBoolean("Audio", "enable_audio_stretching", true)); - Settings::values.audio_device_id = sdl2_config->Get("Audio", "output_device", "auto"); - Settings::values.volume.SetValue( - static_cast<float>(sdl2_config->GetReal("Audio", "volume", 1))); + ReadSetting("Audio", Settings::values.sink_id); + ReadSetting("Audio", Settings::values.enable_audio_stretching); + ReadSetting("Audio", Settings::values.audio_device_id); + ReadSetting("Audio", Settings::values.volume); // Miscellaneous - Settings::values.log_filter = sdl2_config->Get("Miscellaneous", "log_filter", "*:Trace"); - Settings::values.use_dev_keys = sdl2_config->GetBoolean("Miscellaneous", "use_dev_keys", false); + // log_filter has a different default here than from common + Settings::values.log_filter = + sdl2_config->Get("Miscellaneous", Settings::values.log_filter.GetLabel(), "*:Trace"); + ReadSetting("Miscellaneous", Settings::values.use_dev_keys); // Debugging Settings::values.record_frame_times = sdl2_config->GetBoolean("Debugging", "record_frame_times", false); - Settings::values.program_args = sdl2_config->Get("Debugging", "program_args", ""); - Settings::values.dump_exefs = sdl2_config->GetBoolean("Debugging", "dump_exefs", false); - Settings::values.dump_nso = sdl2_config->GetBoolean("Debugging", "dump_nso", false); - Settings::values.enable_fs_access_log = - sdl2_config->GetBoolean("Debugging", "enable_fs_access_log", false); - Settings::values.reporting_services = - sdl2_config->GetBoolean("Debugging", "reporting_services", false); - Settings::values.quest_flag = sdl2_config->GetBoolean("Debugging", "quest_flag", false); - Settings::values.use_debug_asserts = - sdl2_config->GetBoolean("Debugging", "use_debug_asserts", false); - Settings::values.use_auto_stub = sdl2_config->GetBoolean("Debugging", "use_auto_stub", false); - - Settings::values.disable_macro_jit = - sdl2_config->GetBoolean("Debugging", "disable_macro_jit", false); + ReadSetting("Debugging", Settings::values.program_args); + ReadSetting("Debugging", Settings::values.dump_exefs); + ReadSetting("Debugging", Settings::values.dump_nso); + ReadSetting("Debugging", Settings::values.enable_fs_access_log); + ReadSetting("Debugging", Settings::values.reporting_services); + ReadSetting("Debugging", Settings::values.quest_flag); + ReadSetting("Debugging", Settings::values.use_debug_asserts); + ReadSetting("Debugging", Settings::values.use_auto_stub); + ReadSetting("Debugging", Settings::values.disable_macro_jit); const auto title_list = sdl2_config->Get("AddOns", "title_ids", ""); std::stringstream ss(title_list); @@ -511,17 +488,14 @@ void Config::ReadValues() { } // Web Service - Settings::values.enable_telemetry = - sdl2_config->GetBoolean("WebService", "enable_telemetry", true); - Settings::values.web_api_url = - sdl2_config->Get("WebService", "web_api_url", "https://api.yuzu-emu.org"); - Settings::values.yuzu_username = sdl2_config->Get("WebService", "yuzu_username", ""); - Settings::values.yuzu_token = sdl2_config->Get("WebService", "yuzu_token", ""); + ReadSetting("WebService", Settings::values.enable_telemetry); + ReadSetting("WebService", Settings::values.web_api_url); + ReadSetting("WebService", Settings::values.yuzu_username); + ReadSetting("WebService", Settings::values.yuzu_token); // Services - Settings::values.bcat_backend = sdl2_config->Get("Services", "bcat_backend", "none"); - Settings::values.bcat_boxcat_local = - sdl2_config->GetBoolean("Services", "bcat_boxcat_local", false); + ReadSetting("Services", Settings::values.bcat_backend); + ReadSetting("Services", Settings::values.bcat_boxcat_local); } void Config::Reload() { diff --git a/src/yuzu_cmd/config.h b/src/yuzu_cmd/config.h index 807199278..1ee932be2 100644 --- a/src/yuzu_cmd/config.h +++ b/src/yuzu_cmd/config.h @@ -8,6 +8,8 @@ #include <memory> #include <string> +#include "common/settings.h" + class INIReader; class Config { @@ -22,4 +24,14 @@ public: ~Config(); void Reload(); + +private: + /** + * Applies a value read from the sdl2_config to a BasicSetting. + * + * @param group The name of the INI group + * @param setting The yuzu setting to modify + */ + template <typename Type> + void ReadSetting(const std::string& group, Settings::BasicSetting<Type>& setting); }; diff --git a/src/yuzu_cmd/yuzu.cpp b/src/yuzu_cmd/yuzu.cpp index 50e388312..9607cdcb1 100644 --- a/src/yuzu_cmd/yuzu.cpp +++ b/src/yuzu_cmd/yuzu.cpp @@ -78,7 +78,7 @@ static void InitializeLogging() { using namespace Common; Log::Filter log_filter(Log::Level::Debug); - log_filter.ParseFilterString(Settings::values.log_filter); + log_filter.ParseFilterString(static_cast<std::string>(Settings::values.log_filter)); Log::SetGlobalFilter(log_filter); Log::AddBackend(std::make_unique<Log::ColorConsoleBackend>()); |