Compare commits
132 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| cb08e5bdd2 | |||
| 8b53209362 | |||
| c42c3561b8 | |||
| 14d5202da6 | |||
| 046de2cc4e | |||
| b804f77fa5 | |||
| 4c6cc67520 | |||
| a36d2b942b | |||
| 3cd3230295 | |||
| 96703b82bc | |||
| da4ca4f2f9 | |||
| 8012c83a87 | |||
| b7fa264749 | |||
| 0d88a2bc05 | |||
| 8943f2158d | |||
| 3f601ed8bc | |||
| 05feddc252 | |||
| c6d7da88c7 | |||
| 79824d7d1b | |||
| 9a48f252ae | |||
| f2599534f8 | |||
| e0978931e8 | |||
| 00ce8eff65 | |||
| af79911017 | |||
| b8becb0608 | |||
| 81b2ba1479 | |||
| 7d464f73c9 | |||
| 776f391ff6 | |||
| bc19d28963 | |||
| 1ef64112b3 | |||
| 4503a4ac43 | |||
| 69214ef678 | |||
| 7059bb2bf8 | |||
| 1bfe950acb | |||
| ed5f1a45b7 | |||
| 289f59dabd | |||
| be1a3f7a0f | |||
| 907b2324d3 | |||
| 977904dd84 | |||
| 58219d1f36 | |||
| 5e78ad4378 | |||
| 4a09517336 | |||
| 2c8f4ed27f | |||
| f75544a943 | |||
| cf38faee9b | |||
| 73638ca593 | |||
| 63915bf2de | |||
| 35327dbde3 | |||
| 0e4d4b4beb | |||
| 7dca756f30 | |||
| 2794242331 | |||
| 420987c5bf | |||
| 0ed1077763 | |||
| f9139ddab7 | |||
| 8284658bac | |||
| 849c6db335 | |||
| 386cd45f07 | |||
| 975a7b3a78 | |||
| 0ddbbb64e5 | |||
| da34d37044 | |||
| c147e9a90e | |||
| a10e112e64 | |||
| bf5b5c1bf4 | |||
| 2c8d337418 | |||
| f28dd32275 | |||
| dc06e11a7b | |||
| eebf39b3c0 | |||
| 5c541b0b42 | |||
| c8b8674ffc | |||
| 87b6e14d7c | |||
| 7ab5767157 | |||
| 5edc96f4a4 | |||
| a949ee0410 | |||
| c7ad195fd3 | |||
| 7905eb0254 | |||
| 92a3daf029 | |||
| 91a4a924b1 | |||
| 7636fefb71 | |||
| 8542f2f3fc | |||
| 4d0bdef17d | |||
| 2f0e1f5d02 | |||
| 2eb018c80f | |||
| eb3cb3af35 | |||
| b07423f6e2 | |||
| b9f915e07a | |||
| dbcc093d88 | |||
| ebb82b0b83 | |||
| a59ae5e702 | |||
| 14ab50defb | |||
| d299d5531f | |||
| bf50345d4c | |||
| 3d03a6ae02 | |||
| c770fa9823 | |||
| 942c0d6cdd | |||
| c19ec2edd9 | |||
| dd44089f87 | |||
| b188d7792a | |||
| 669cef2da3 | |||
| c6a9e91784 | |||
| a8a0927d42 | |||
| 8f9f142956 | |||
| fd98fcf7f0 | |||
| 38165fb7e3 | |||
| eb0e10cff2 | |||
| 5dfa313d2c | |||
| 0aab55d26a | |||
| ebaa7e391c | |||
| 3a3f4983b6 | |||
| 2fc0a760f0 | |||
| 38f658d21e | |||
| cf1cd3321d | |||
| 208a04dcff | |||
| b455043e45 | |||
| bab400daaf | |||
| 299c5594e6 | |||
| 39be4c3026 | |||
| 0e5c74bc9e | |||
| 7a8de138df | |||
| b91b76df4f | |||
| ec68cba440 | |||
| 511ee03a21 | |||
| 6ac978426c | |||
| 844e0114b0 | |||
| 1664c74a6c | |||
| bfecd395d4 | |||
| 0857d6a3db | |||
| 35b17fa5e0 | |||
| 20e51402b0 | |||
| a01459df3d | |||
| fb16cbb17e | |||
| 2bb0cc8624 | |||
| 932c0184a7 |
+94
-8
@@ -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
|
||||
@@ -254,10 +255,83 @@ 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)
|
||||
endif()
|
||||
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
|
||||
@@ -265,6 +339,8 @@ if(ENABLE_QT)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
set(YUZU_QT_NO_CMAKE_SYSTEM_PATH)
|
||||
|
||||
# Workaround for an issue where conan tries to build Qt from scratch instead of download prebuilt binaries
|
||||
set(QT_PREFIX_HINT)
|
||||
|
||||
@@ -282,8 +358,10 @@ if(ENABLE_QT)
|
||||
endif()
|
||||
|
||||
set(QT_PREFIX_HINT HINTS "${QT_PREFIX}")
|
||||
|
||||
set(YUZU_QT_NO_CMAKE_SYSTEM_PATH "NO_CMAKE_SYSTEM_PATH")
|
||||
endif()
|
||||
find_package(Qt5 ${QT_VERSION} REQUIRED COMPONENTS Widgets ${QT_PREFIX_HINT} NO_CMAKE_SYSTEM_PATH)
|
||||
find_package(Qt5 ${QT_VERSION} REQUIRED COMPONENTS Widgets ${QT_PREFIX_HINT} ${YUZU_QT_NO_CMAKE_SYSTEM_PATH})
|
||||
if (YUZU_USE_QT_WEB_ENGINE)
|
||||
find_package(Qt5 COMPONENTS WebEngineCore WebEngineWidgets)
|
||||
endif()
|
||||
@@ -473,7 +551,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)
|
||||
|
||||
Vendored
+11
@@ -5,6 +5,17 @@ if (MINGW OR (${CMAKE_SYSTEM_NAME} MATCHES "Linux") OR APPLE)
|
||||
# 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")
|
||||
|
||||
|
||||
@@ -55,6 +55,7 @@ if (MSVC)
|
||||
/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
|
||||
/we4267 # 'var': conversion from 'size_t' to 'type', possible loss of data
|
||||
/we4305 # 'context': truncation from 'type1' to 'type2'
|
||||
@@ -81,6 +82,7 @@ else()
|
||||
-Werror=missing-declarations
|
||||
-Werror=missing-field-initializers
|
||||
-Werror=reorder
|
||||
-Werror=sign-compare
|
||||
-Werror=switch
|
||||
-Werror=uninitialized
|
||||
-Werror=unused-function
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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>(
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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++) {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
+27
-2
@@ -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);
|
||||
|
||||
+10
-1
@@ -396,12 +396,21 @@ 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.
|
||||
|
||||
@@ -171,19 +171,22 @@ FileBackend::FileBackend(const std::filesystem::path& filename) {
|
||||
FileBackend::~FileBackend() = default;
|
||||
|
||||
void FileBackend::Write(const Entry& entry) {
|
||||
using namespace Common::Literals;
|
||||
// prevent logs from going over the maximum size (in case its spamming and the user doesn't
|
||||
// know)
|
||||
constexpr std::size_t MAX_BYTES_WRITTEN = 100_MiB;
|
||||
constexpr std::size_t MAX_BYTES_WRITTEN_EXTENDED = 1_GiB;
|
||||
|
||||
if (!file->IsOpen()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (Settings::values.extended_logging && bytes_written > MAX_BYTES_WRITTEN_EXTENDED) {
|
||||
return;
|
||||
} else if (!Settings::values.extended_logging && bytes_written > MAX_BYTES_WRITTEN) {
|
||||
using namespace Common::Literals;
|
||||
// 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;
|
||||
|
||||
const bool write_limit_exceeded =
|
||||
bytes_written > MAX_BYTES_WRITTEN_EXTENDED ||
|
||||
(bytes_written > MAX_BYTES_WRITTEN && !Settings::values.extended_logging);
|
||||
|
||||
// Close the file after the write limit is exceeded.
|
||||
if (write_limit_exceeded) {
|
||||
file->Close();
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
+10
-10
@@ -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;
|
||||
}
|
||||
@@ -103,7 +103,7 @@ float Volume() {
|
||||
if (values.audio_muted) {
|
||||
return 0.0f;
|
||||
}
|
||||
return values.volume.GetValue();
|
||||
return values.volume.GetValue() / 100.0f;
|
||||
}
|
||||
|
||||
void RestoreGlobalState(bool is_powered_on) {
|
||||
|
||||
+311
-128
@@ -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 Setting final {
|
||||
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:
|
||||
Setting() = default;
|
||||
explicit Setting(Type val) : global{val} {}
|
||||
~Setting() = default;
|
||||
void SetGlobal(bool to_global) {
|
||||
use_global = to_global;
|
||||
}
|
||||
bool UsingGlobal() const {
|
||||
return use_global;
|
||||
}
|
||||
Type GetValue(bool need_global = false) const {
|
||||
if (use_global || need_global) {
|
||||
return global;
|
||||
}
|
||||
return local;
|
||||
}
|
||||
void SetValue(const Type& value) {
|
||||
if (use_global) {
|
||||
global = value;
|
||||
} else {
|
||||
local = value;
|
||||
}
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
private:
|
||||
bool use_global = true;
|
||||
Type global{};
|
||||
Type local{};
|
||||
/**
|
||||
* 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 InputSetting class allows for getting a reference to either the global or local members.
|
||||
* 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 : public BasicSetting<Type> {
|
||||
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 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether this setting is using the global setting or not.
|
||||
*
|
||||
* @returns The global state
|
||||
*/
|
||||
[[nodiscard]] bool UsingGlobal() const {
|
||||
return use_global;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 this->global;
|
||||
}
|
||||
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) {
|
||||
std::swap(this->global, temp);
|
||||
} else {
|
||||
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}; ///< 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 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<u8> volume{100, "volume"};
|
||||
|
||||
// Core
|
||||
Setting<bool> use_multi_core;
|
||||
Setting<bool> use_multi_core{true, "use_multi_core"};
|
||||
|
||||
// Cpu
|
||||
Setting<CPUAccuracy> cpu_accuracy;
|
||||
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"};
|
||||
|
||||
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;
|
||||
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;
|
||||
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<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<RendererBackend> renderer_backend{RendererBackend::OpenGL, "backend"};
|
||||
BasicSetting<bool> renderer_debug{false, "debug"};
|
||||
Setting<int> vulkan_device{0, "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<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;
|
||||
Setting<float> bg_green;
|
||||
Setting<float> bg_blue;
|
||||
Setting<u8> bg_red{0, "bg_red"};
|
||||
Setting<u8> bg_green{0, "bg_green"};
|
||||
Setting<u8> bg_blue{0, "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<u8> mouse_panning_sensitivity{10, "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;
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
+36
-38
@@ -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
|
||||
@@ -667,8 +667,6 @@ 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>
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
+5
-5
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
#include <optional>
|
||||
#include <ranges>
|
||||
#include <utility>
|
||||
|
||||
#include "common/logging/log.h"
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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"}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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...");
|
||||
}
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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"
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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)) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
+1
-1
@@ -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 {
|
||||
+1
-1
@@ -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 {
|
||||
+1
-1
@@ -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 {
|
||||
+1
-1
@@ -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 {
|
||||
|
||||
+1
-1
@@ -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 {
|
||||
|
||||
+1
-1
@@ -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;
|
||||
+14
-1
@@ -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() {}
|
||||
+1
-1
@@ -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;
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
|
||||
+1
-1
@@ -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 {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
|
||||
+1
-1
@@ -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 {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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"
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -19,26 +19,29 @@
|
||||
namespace Service::Nvidia::Devices {
|
||||
|
||||
namespace {
|
||||
// Splice vectors will copy count amount of type T from the input vector into the dst vector.
|
||||
// Copies count amount of type T from the input vector into the dst vector.
|
||||
// Returns the number of bytes written into dst.
|
||||
template <typename T>
|
||||
std::size_t SpliceVectors(const std::vector<u8>& input, std::vector<T>& dst, std::size_t count,
|
||||
std::size_t offset) {
|
||||
if (!dst.empty()) {
|
||||
std::memcpy(dst.data(), input.data() + offset, count * sizeof(T));
|
||||
std::size_t SliceVectors(const std::vector<u8>& input, std::vector<T>& dst, std::size_t count,
|
||||
std::size_t offset) {
|
||||
if (dst.empty()) {
|
||||
return 0;
|
||||
}
|
||||
return 0;
|
||||
const size_t bytes_copied = count * sizeof(T);
|
||||
std::memcpy(dst.data(), input.data() + offset, bytes_copied);
|
||||
return bytes_copied;
|
||||
}
|
||||
|
||||
// Write vectors will write data to the output buffer
|
||||
// Writes the data in src to an offset into the dst vector. The offset is specified in bytes
|
||||
// Returns the number of bytes written into dst.
|
||||
template <typename T>
|
||||
std::size_t WriteVectors(std::vector<u8>& dst, const std::vector<T>& src, std::size_t offset) {
|
||||
if (src.empty()) {
|
||||
return 0;
|
||||
} else {
|
||||
std::memcpy(dst.data() + offset, src.data(), src.size() * sizeof(T));
|
||||
offset += src.size() * sizeof(T);
|
||||
return offset;
|
||||
}
|
||||
const size_t bytes_copied = src.size() * sizeof(T);
|
||||
std::memcpy(dst.data() + offset, src.data(), bytes_copied);
|
||||
return bytes_copied;
|
||||
}
|
||||
} // Anonymous namespace
|
||||
|
||||
@@ -62,7 +65,6 @@ NvResult nvhost_nvdec_common::Submit(const std::vector<u8>& input, std::vector<u
|
||||
LOG_DEBUG(Service_NVDRV, "called NVDEC Submit, cmd_buffer_count={}", params.cmd_buffer_count);
|
||||
|
||||
// Instantiate param buffers
|
||||
std::size_t offset = sizeof(IoctlSubmit);
|
||||
std::vector<CommandBuffer> command_buffers(params.cmd_buffer_count);
|
||||
std::vector<Reloc> relocs(params.relocation_count);
|
||||
std::vector<u32> reloc_shifts(params.relocation_count);
|
||||
@@ -70,13 +72,14 @@ NvResult nvhost_nvdec_common::Submit(const std::vector<u8>& input, std::vector<u
|
||||
std::vector<SyncptIncr> wait_checks(params.syncpoint_count);
|
||||
std::vector<Fence> fences(params.fence_count);
|
||||
|
||||
// Splice input into their respective buffers
|
||||
offset = SpliceVectors(input, command_buffers, params.cmd_buffer_count, offset);
|
||||
offset = SpliceVectors(input, relocs, params.relocation_count, offset);
|
||||
offset = SpliceVectors(input, reloc_shifts, params.relocation_count, offset);
|
||||
offset = SpliceVectors(input, syncpt_increments, params.syncpoint_count, offset);
|
||||
offset = SpliceVectors(input, wait_checks, params.syncpoint_count, offset);
|
||||
offset = SpliceVectors(input, fences, params.fence_count, offset);
|
||||
// Slice input into their respective buffers
|
||||
std::size_t offset = sizeof(IoctlSubmit);
|
||||
offset += SliceVectors(input, command_buffers, params.cmd_buffer_count, offset);
|
||||
offset += SliceVectors(input, relocs, params.relocation_count, offset);
|
||||
offset += SliceVectors(input, reloc_shifts, params.relocation_count, offset);
|
||||
offset += SliceVectors(input, syncpt_increments, params.syncpoint_count, offset);
|
||||
offset += SliceVectors(input, wait_checks, params.syncpoint_count, offset);
|
||||
offset += SliceVectors(input, fences, params.fence_count, offset);
|
||||
|
||||
auto& gpu = system.GPU();
|
||||
if (gpu.UseNvdec()) {
|
||||
@@ -88,35 +91,27 @@ NvResult nvhost_nvdec_common::Submit(const std::vector<u8>& input, std::vector<u
|
||||
}
|
||||
}
|
||||
for (const auto& cmd_buffer : command_buffers) {
|
||||
auto object = nvmap_dev->GetObject(cmd_buffer.memory_id);
|
||||
const auto object = nvmap_dev->GetObject(cmd_buffer.memory_id);
|
||||
ASSERT_OR_EXECUTE(object, return NvResult::InvalidState;);
|
||||
const auto map = FindBufferMap(object->dma_map_addr);
|
||||
if (!map) {
|
||||
LOG_ERROR(Service_NVDRV, "Tried to submit an invalid offset 0x{:X} dma 0x{:X}",
|
||||
object->addr, object->dma_map_addr);
|
||||
return NvResult::Success;
|
||||
}
|
||||
Tegra::ChCommandHeaderList cmdlist(cmd_buffer.word_count);
|
||||
gpu.MemoryManager().ReadBlock(map->StartAddr() + cmd_buffer.offset, cmdlist.data(),
|
||||
cmdlist.size() * sizeof(u32));
|
||||
system.Memory().ReadBlock(object->addr + cmd_buffer.offset, cmdlist.data(),
|
||||
cmdlist.size() * sizeof(u32));
|
||||
gpu.PushCommandBuffer(cmdlist);
|
||||
}
|
||||
if (gpu.UseNvdec()) {
|
||||
|
||||
fences[0].value = syncpoint_manager.IncreaseSyncpoint(fences[0].id, 1);
|
||||
|
||||
Tegra::ChCommandHeaderList cmdlist{{(4 << 28) | fences[0].id}};
|
||||
gpu.PushCommandBuffer(cmdlist);
|
||||
}
|
||||
std::memcpy(output.data(), ¶ms, sizeof(IoctlSubmit));
|
||||
// Some games expect command_buffers to be written back
|
||||
offset = sizeof(IoctlSubmit);
|
||||
offset = WriteVectors(output, command_buffers, offset);
|
||||
offset = WriteVectors(output, relocs, offset);
|
||||
offset = WriteVectors(output, reloc_shifts, offset);
|
||||
offset = WriteVectors(output, syncpt_increments, offset);
|
||||
offset = WriteVectors(output, wait_checks, offset);
|
||||
offset = WriteVectors(output, fences, offset);
|
||||
offset += WriteVectors(output, command_buffers, offset);
|
||||
offset += WriteVectors(output, relocs, offset);
|
||||
offset += WriteVectors(output, reloc_shifts, offset);
|
||||
offset += WriteVectors(output, syncpt_increments, offset);
|
||||
offset += WriteVectors(output, wait_checks, offset);
|
||||
offset += WriteVectors(output, fences, offset);
|
||||
|
||||
return NvResult::Success;
|
||||
}
|
||||
@@ -148,14 +143,14 @@ NvResult nvhost_nvdec_common::MapBuffer(const std::vector<u8>& input, std::vecto
|
||||
std::memcpy(¶ms, input.data(), sizeof(IoctlMapBuffer));
|
||||
std::vector<MapBufferEntry> cmd_buffer_handles(params.num_entries);
|
||||
|
||||
SpliceVectors(input, cmd_buffer_handles, params.num_entries, sizeof(IoctlMapBuffer));
|
||||
SliceVectors(input, cmd_buffer_handles, params.num_entries, sizeof(IoctlMapBuffer));
|
||||
|
||||
auto& gpu = system.GPU();
|
||||
|
||||
for (auto& cmf_buff : cmd_buffer_handles) {
|
||||
auto object{nvmap_dev->GetObject(cmf_buff.map_handle)};
|
||||
for (auto& cmd_buffer : cmd_buffer_handles) {
|
||||
auto object{nvmap_dev->GetObject(cmd_buffer.map_handle)};
|
||||
if (!object) {
|
||||
LOG_ERROR(Service_NVDRV, "invalid cmd_buffer nvmap_handle={:X}", cmf_buff.map_handle);
|
||||
LOG_ERROR(Service_NVDRV, "invalid cmd_buffer nvmap_handle={:X}", cmd_buffer.map_handle);
|
||||
std::memcpy(output.data(), ¶ms, output.size());
|
||||
return NvResult::InvalidState;
|
||||
}
|
||||
@@ -170,7 +165,7 @@ NvResult nvhost_nvdec_common::MapBuffer(const std::vector<u8>& input, std::vecto
|
||||
if (!object->dma_map_addr) {
|
||||
LOG_ERROR(Service_NVDRV, "failed to map size={}", object->size);
|
||||
} else {
|
||||
cmf_buff.map_address = object->dma_map_addr;
|
||||
cmd_buffer.map_address = object->dma_map_addr;
|
||||
AddBufferMap(object->dma_map_addr, object->size, object->addr,
|
||||
object->status == nvmap::Object::Status::Allocated);
|
||||
}
|
||||
@@ -186,14 +181,14 @@ NvResult nvhost_nvdec_common::UnmapBuffer(const std::vector<u8>& input, std::vec
|
||||
IoctlMapBuffer params{};
|
||||
std::memcpy(¶ms, input.data(), sizeof(IoctlMapBuffer));
|
||||
std::vector<MapBufferEntry> cmd_buffer_handles(params.num_entries);
|
||||
SpliceVectors(input, cmd_buffer_handles, params.num_entries, sizeof(IoctlMapBuffer));
|
||||
SliceVectors(input, cmd_buffer_handles, params.num_entries, sizeof(IoctlMapBuffer));
|
||||
|
||||
auto& gpu = system.GPU();
|
||||
|
||||
for (auto& cmf_buff : cmd_buffer_handles) {
|
||||
const auto object{nvmap_dev->GetObject(cmf_buff.map_handle)};
|
||||
for (auto& cmd_buffer : cmd_buffer_handles) {
|
||||
const auto object{nvmap_dev->GetObject(cmd_buffer.map_handle)};
|
||||
if (!object) {
|
||||
LOG_ERROR(Service_NVDRV, "invalid cmd_buffer nvmap_handle={:X}", cmf_buff.map_handle);
|
||||
LOG_ERROR(Service_NVDRV, "invalid cmd_buffer nvmap_handle={:X}", cmd_buffer.map_handle);
|
||||
std::memcpy(output.data(), ¶ms, output.size());
|
||||
return NvResult::InvalidState;
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
|
||||
+1
-1
@@ -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 {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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"
|
||||
|
||||
|
||||
+1
-1
@@ -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 {
|
||||
|
||||
@@ -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()), {}};
|
||||
|
||||
@@ -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{
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user