Compare commits
100 Commits
__refs_pul
...
__refs_pul
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
48259de0c1 | ||
|
|
604ff1f178 | ||
|
|
718c272421 | ||
|
|
38a30f2255 | ||
|
|
91d5cef90e | ||
|
|
32f119c37a | ||
|
|
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 | ||
|
|
a59ae5e702 | ||
|
|
14ab50defb | ||
|
|
d299d5531f | ||
|
|
c6a9e91784 | ||
|
|
a8a0927d42 | ||
|
|
8f9f142956 | ||
|
|
fd98fcf7f0 | ||
|
|
38165fb7e3 | ||
|
|
38f658d21e | ||
|
|
cf1cd3321d | ||
|
|
299c5594e6 | ||
|
|
0e5c74bc9e | ||
|
|
7a8de138df | ||
|
|
b91b76df4f | ||
|
|
35b17fa5e0 | ||
|
|
20e51402b0 |
@@ -13,7 +13,7 @@ project(yuzu)
|
||||
option(ENABLE_SDL2 "Enable the SDL2 frontend" ON)
|
||||
CMAKE_DEPENDENT_OPTION(YUZU_USE_BUNDLED_SDL2 "Download bundled SDL2 binaries" ON "ENABLE_SDL2;MSVC" OFF)
|
||||
# On Linux system SDL2 is likely to be lacking HIDAPI support which have drawbacks but is needed for SDL motion
|
||||
option(YUZU_ALLOW_SYSTEM_SDL2 "Try using system SDL2 before fallling back to one from externals" OFF)
|
||||
CMAKE_DEPENDENT_OPTION(YUZU_USE_EXTERNAL_SDL2 "Compiled external SDL2" ON "ENABLE_SDL2;NOT MSVC" OFF)
|
||||
|
||||
option(ENABLE_QT "Enable the Qt frontend" ON)
|
||||
option(ENABLE_QT_TRANSLATION "Enable translations for the Qt frontend" OFF)
|
||||
@@ -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
|
||||
@@ -386,26 +387,20 @@ if (ENABLE_SDL2)
|
||||
add_library(SDL2 INTERFACE)
|
||||
target_link_libraries(SDL2 INTERFACE "${SDL2_LIBRARY}")
|
||||
target_include_directories(SDL2 INTERFACE "${SDL2_INCLUDE_DIR}")
|
||||
elseif (YUZU_USE_EXTERNAL_SDL2)
|
||||
message(STATUS "Using SDL2 from externals.")
|
||||
else()
|
||||
if (YUZU_ALLOW_SYSTEM_SDL2)
|
||||
find_package(SDL2 2.0.15 QUIET)
|
||||
find_package(SDL2 2.0.15 REQUIRED)
|
||||
|
||||
if (SDL2_FOUND)
|
||||
# Some installations don't set SDL2_LIBRARIES
|
||||
if("${SDL2_LIBRARIES}" STREQUAL "")
|
||||
message(WARNING "SDL2_LIBRARIES wasn't set, manually setting to SDL2::SDL2")
|
||||
set(SDL2_LIBRARIES "SDL2::SDL2")
|
||||
endif()
|
||||
|
||||
include_directories(SYSTEM ${SDL2_INCLUDE_DIRS})
|
||||
add_library(SDL2 INTERFACE)
|
||||
target_link_libraries(SDL2 INTERFACE "${SDL2_LIBRARIES}")
|
||||
else()
|
||||
message(STATUS "SDL2 2.0.15 or newer not found, falling back to externals.")
|
||||
endif()
|
||||
else()
|
||||
message(STATUS "Using SDL2 from externals.")
|
||||
# Some installations don't set SDL2_LIBRARIES
|
||||
if("${SDL2_LIBRARIES}" STREQUAL "")
|
||||
message(WARNING "SDL2_LIBRARIES wasn't set, manually setting to SDL2::SDL2")
|
||||
set(SDL2_LIBRARIES "SDL2::SDL2")
|
||||
endif()
|
||||
|
||||
include_directories(SYSTEM ${SDL2_INCLUDE_DIRS})
|
||||
add_library(SDL2 INTERFACE)
|
||||
target_link_libraries(SDL2 INTERFACE "${SDL2_LIBRARIES}")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
|
||||
2
externals/CMakeLists.txt
vendored
2
externals/CMakeLists.txt
vendored
@@ -51,7 +51,7 @@ if (NOT LIBUSB_FOUND OR YUZU_USE_BUNDLED_LIBUSB)
|
||||
endif()
|
||||
|
||||
# SDL2
|
||||
if (NOT SDL2_FOUND AND ENABLE_SDL2)
|
||||
if (YUZU_USE_EXTERNAL_SDL2)
|
||||
if (NOT WIN32)
|
||||
# Yuzu itself needs: Events Joystick Haptic Sensor Timers Audio
|
||||
# Yuzu-cmd also needs: Video (depends on Loadso/Dlopen)
|
||||
|
||||
@@ -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>(
|
||||
|
||||
@@ -29,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%
|
||||
|
||||
@@ -57,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))};
|
||||
}
|
||||
@@ -241,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]);
|
||||
@@ -294,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;
|
||||
|
||||
@@ -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) {
|
||||
@@ -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,9 +1021,10 @@ void CommandGenerator::GenerateFinalMixCommand() {
|
||||
}
|
||||
}
|
||||
|
||||
s32 CommandGenerator::DecodePcm16(ServerVoiceInfo& voice_info, VoiceState& dsp_state,
|
||||
s32 sample_start_offset, s32 sample_end_offset, 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) {
|
||||
@@ -1019,24 +1038,37 @@ s32 CommandGenerator::DecodePcm16(ServerVoiceInfo& voice_info, VoiceState& dsp_s
|
||||
}
|
||||
const auto samples_remaining = (sample_end_offset - sample_start_offset) - dsp_state.offset;
|
||||
const auto start_offset =
|
||||
((dsp_state.offset + sample_start_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;
|
||||
@@ -1155,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 {
|
||||
@@ -1171,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) {
|
||||
@@ -1191,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) *
|
||||
@@ -1208,6 +1242,7 @@ void CommandGenerator::DecodeFromWaveBuffers(ServerVoiceInfo& voice_info, s32* o
|
||||
}
|
||||
|
||||
std::size_t temp_mix_offset{};
|
||||
s32 samples_output{};
|
||||
auto samples_remaining = sample_count;
|
||||
while (samples_remaining > 0) {
|
||||
const auto samples_to_output = std::min(samples_remaining, min_required_samples);
|
||||
@@ -1249,10 +1284,25 @@ void CommandGenerator::DecodeFromWaveBuffers(ServerVoiceInfo& voice_info, s32* o
|
||||
|
||||
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_offset_start, samples_offset_end,
|
||||
samples_to_read - samples_read, channel, temp_mix_offset);
|
||||
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 =
|
||||
@@ -1296,20 +1346,21 @@ void CommandGenerator::DecodeFromWaveBuffers(ServerVoiceInfo& voice_info, s32* o
|
||||
|
||||
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_start_offset,
|
||||
s32 sample_end_offset, s32 sample_count, s32 channel, std::size_t mix_offset);
|
||||
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, s32* output, VoiceState& dsp_state,
|
||||
s32 channel, s32 target_sample_rate, s32 sample_count, s32 node_id);
|
||||
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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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{1, "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
|
||||
|
||||
62
src/common/unique_function.h
Normal file
62
src/common/unique_function.h
Normal file
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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 {
|
||||
@@ -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 {
|
||||
@@ -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 {
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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;
|
||||
@@ -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() {}
|
||||
@@ -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 {
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -290,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
|
||||
@@ -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 {
|
||||
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
|
||||
@@ -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{
|
||||
|
||||
@@ -397,7 +397,7 @@ void Reporter::ClearFSAccessLog() const {
|
||||
}
|
||||
|
||||
bool Reporter::IsReportingEnabled() const {
|
||||
return Settings::values.reporting_services;
|
||||
return Settings::values.reporting_services.GetValue();
|
||||
}
|
||||
|
||||
} // namespace Core
|
||||
|
||||
@@ -135,7 +135,7 @@ u64 RegenerateTelemetryId() {
|
||||
|
||||
bool VerifyLogin(const std::string& username, const std::string& token) {
|
||||
#ifdef ENABLE_WEB_SERVICE
|
||||
return WebService::VerifyLogin(Settings::values.web_api_url, username, token);
|
||||
return WebService::VerifyLogin(Settings::values.web_api_url.GetValue(), username, token);
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
@@ -152,7 +152,8 @@ TelemetrySession::~TelemetrySession() {
|
||||
|
||||
#ifdef ENABLE_WEB_SERVICE
|
||||
auto backend = std::make_unique<WebService::TelemetryJson>(
|
||||
Settings::values.web_api_url, Settings::values.yuzu_username, Settings::values.yuzu_token);
|
||||
Settings::values.web_api_url.GetValue(), Settings::values.yuzu_username.GetValue(),
|
||||
Settings::values.yuzu_token.GetValue());
|
||||
#else
|
||||
auto backend = std::make_unique<Telemetry::NullVisitor>();
|
||||
#endif
|
||||
@@ -212,7 +213,7 @@ void TelemetrySession::AddInitialInfo(Loader::AppLoader& app_loader,
|
||||
|
||||
// Log user configuration information
|
||||
constexpr auto field_type = Telemetry::FieldType::UserConfig;
|
||||
AddField(field_type, "Audio_SinkId", Settings::values.sink_id);
|
||||
AddField(field_type, "Audio_SinkId", Settings::values.sink_id.GetValue());
|
||||
AddField(field_type, "Audio_EnableAudioStretching",
|
||||
Settings::values.enable_audio_stretching.GetValue());
|
||||
AddField(field_type, "Core_UseMultiCore", Settings::values.use_multi_core.GetValue());
|
||||
@@ -242,7 +243,8 @@ void TelemetrySession::AddInitialInfo(Loader::AppLoader& app_loader,
|
||||
bool TelemetrySession::SubmitTestcase() {
|
||||
#ifdef ENABLE_WEB_SERVICE
|
||||
auto backend = std::make_unique<WebService::TelemetryJson>(
|
||||
Settings::values.web_api_url, Settings::values.yuzu_username, Settings::values.yuzu_token);
|
||||
Settings::values.web_api_url.GetValue(), Settings::values.yuzu_username.GetValue(),
|
||||
Settings::values.yuzu_token.GetValue());
|
||||
field_collection.Accept(*backend);
|
||||
return backend->SubmitTestcase();
|
||||
#else
|
||||
|
||||
@@ -294,8 +294,8 @@ void InputSubsystem::ReloadInputDevices() {
|
||||
impl->udp->ReloadSockets();
|
||||
}
|
||||
|
||||
std::vector<std::unique_ptr<Polling::DevicePoller>> InputSubsystem::GetPollers(
|
||||
Polling::DeviceType type) const {
|
||||
std::vector<std::unique_ptr<Polling::DevicePoller>> InputSubsystem::GetPollers([
|
||||
[maybe_unused]] Polling::DeviceType type) const {
|
||||
#ifdef HAVE_SDL2
|
||||
return impl->sdl->GetPollers(type);
|
||||
#else
|
||||
|
||||
@@ -84,7 +84,8 @@ public:
|
||||
std::lock_guard lock{mutex};
|
||||
const auto axis_value =
|
||||
static_cast<float>(mouse_input->GetMouseState(button).axis.at(axis));
|
||||
return axis_value * Settings::values.mouse_panning_sensitivity / (100.0f * range);
|
||||
const float sensitivity = Settings::values.mouse_panning_sensitivity.GetValue() * 0.15f;
|
||||
return axis_value * sensitivity / (100.0f * range);
|
||||
}
|
||||
|
||||
std::pair<float, float> GetAnalog(u32 analog_axis_x, u32 analog_axis_y) const {
|
||||
|
||||
@@ -10,15 +10,7 @@
|
||||
#include <thread>
|
||||
#include <unordered_map>
|
||||
|
||||
// Ignore -Wimplicit-fallthrough due to https://github.com/libsdl-org/SDL/issues/4307
|
||||
#ifdef __GNUC__
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wimplicit-fallthrough"
|
||||
#endif
|
||||
#include <SDL.h>
|
||||
#ifdef __GNUC__
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "common/threadsafe_queue.h"
|
||||
|
||||
@@ -13,7 +13,7 @@ class TouchFromButtonDevice final : public Input::TouchDevice {
|
||||
public:
|
||||
TouchFromButtonDevice() {
|
||||
const auto button_index =
|
||||
static_cast<std::size_t>(Settings::values.touch_from_button_map_index);
|
||||
static_cast<u64>(Settings::values.touch_from_button_map_index.GetValue());
|
||||
const auto& buttons = Settings::values.touch_from_button_maps[button_index].buttons;
|
||||
|
||||
for (const auto& config_entry : buttons) {
|
||||
|
||||
@@ -201,7 +201,7 @@ bool Client::DeviceConnected(std::size_t pad) const {
|
||||
void Client::ReloadSockets() {
|
||||
Reset();
|
||||
|
||||
std::stringstream servers_ss(Settings::values.udp_input_servers);
|
||||
std::stringstream servers_ss(static_cast<std::string>(Settings::values.udp_input_servers));
|
||||
std::string server_token;
|
||||
std::size_t client = 0;
|
||||
while (std::getline(servers_ss, server_token, ',')) {
|
||||
@@ -370,7 +370,7 @@ std::optional<std::size_t> Client::GetUnusedFingerID() const {
|
||||
|
||||
void Client::UpdateTouchInput(Response::TouchPad& touch_pad, std::size_t client, std::size_t id) {
|
||||
// TODO: Use custom calibration per device
|
||||
const Common::ParamPackage touch_param(Settings::values.touch_device);
|
||||
const Common::ParamPackage touch_param(Settings::values.touch_device.GetValue());
|
||||
const u16 min_x = static_cast<u16>(touch_param.Get("min_x", 100));
|
||||
const u16 min_y = static_cast<u16>(touch_param.Get("min_y", 50));
|
||||
const u16 max_x = static_cast<u16>(touch_param.Get("max_x", 1800));
|
||||
|
||||
@@ -5,6 +5,7 @@ add_executable(tests
|
||||
common/host_memory.cpp
|
||||
common/param_package.cpp
|
||||
common/ring_buffer.cpp
|
||||
common/unique_function.cpp
|
||||
core/core_timing.cpp
|
||||
core/network/network.cpp
|
||||
tests.cpp
|
||||
|
||||
108
src/tests/common/unique_function.cpp
Normal file
108
src/tests/common/unique_function.cpp
Normal file
@@ -0,0 +1,108 @@
|
||||
// Copyright 2021 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <catch2/catch.hpp>
|
||||
|
||||
#include "common/unique_function.h"
|
||||
|
||||
namespace {
|
||||
struct Noisy {
|
||||
Noisy() : state{"Default constructed"} {}
|
||||
Noisy(Noisy&& rhs) noexcept : state{"Move constructed"} {
|
||||
rhs.state = "Moved away";
|
||||
}
|
||||
Noisy& operator=(Noisy&& rhs) noexcept {
|
||||
state = "Move assigned";
|
||||
rhs.state = "Moved away";
|
||||
}
|
||||
Noisy(const Noisy&) : state{"Copied constructed"} {}
|
||||
Noisy& operator=(const Noisy&) {
|
||||
state = "Copied assigned";
|
||||
}
|
||||
|
||||
std::string state;
|
||||
};
|
||||
} // Anonymous namespace
|
||||
|
||||
TEST_CASE("UniqueFunction", "[common]") {
|
||||
SECTION("Capture reference") {
|
||||
int value = 0;
|
||||
Common::UniqueFunction<void> func = [&value] { value = 5; };
|
||||
func();
|
||||
REQUIRE(value == 5);
|
||||
}
|
||||
SECTION("Capture pointer") {
|
||||
int value = 0;
|
||||
int* pointer = &value;
|
||||
Common::UniqueFunction<void> func = [pointer] { *pointer = 5; };
|
||||
func();
|
||||
REQUIRE(value == 5);
|
||||
}
|
||||
SECTION("Move object") {
|
||||
Noisy noisy;
|
||||
REQUIRE(noisy.state == "Default constructed");
|
||||
|
||||
Common::UniqueFunction<void> func = [noisy = std::move(noisy)] {
|
||||
REQUIRE(noisy.state == "Move constructed");
|
||||
};
|
||||
REQUIRE(noisy.state == "Moved away");
|
||||
func();
|
||||
}
|
||||
SECTION("Move construct function") {
|
||||
int value = 0;
|
||||
Common::UniqueFunction<void> func = [&value] { value = 5; };
|
||||
Common::UniqueFunction<void> new_func = std::move(func);
|
||||
new_func();
|
||||
REQUIRE(value == 5);
|
||||
}
|
||||
SECTION("Move assign function") {
|
||||
int value = 0;
|
||||
Common::UniqueFunction<void> func = [&value] { value = 5; };
|
||||
Common::UniqueFunction<void> new_func;
|
||||
new_func = std::move(func);
|
||||
new_func();
|
||||
REQUIRE(value == 5);
|
||||
}
|
||||
SECTION("Default construct then assign function") {
|
||||
int value = 0;
|
||||
Common::UniqueFunction<void> func;
|
||||
func = [&value] { value = 5; };
|
||||
func();
|
||||
REQUIRE(value == 5);
|
||||
}
|
||||
SECTION("Pass arguments") {
|
||||
int result = 0;
|
||||
Common::UniqueFunction<void, int, int> func = [&result](int a, int b) { result = a + b; };
|
||||
func(5, 4);
|
||||
REQUIRE(result == 9);
|
||||
}
|
||||
SECTION("Pass arguments and return value") {
|
||||
Common::UniqueFunction<int, int, int> func = [](int a, int b) { return a + b; };
|
||||
REQUIRE(func(5, 4) == 9);
|
||||
}
|
||||
SECTION("Destructor") {
|
||||
int num_destroyed = 0;
|
||||
struct Foo {
|
||||
Foo(int* num_) : num{num_} {}
|
||||
Foo(Foo&& rhs) : num{std::exchange(rhs.num, nullptr)} {}
|
||||
Foo(const Foo&) = delete;
|
||||
|
||||
~Foo() {
|
||||
if (num) {
|
||||
++*num;
|
||||
}
|
||||
}
|
||||
|
||||
int* num = nullptr;
|
||||
};
|
||||
Foo object{&num_destroyed};
|
||||
{
|
||||
Common::UniqueFunction<void> func = [object = std::move(object)] {};
|
||||
REQUIRE(num_destroyed == 0);
|
||||
}
|
||||
REQUIRE(num_destroyed == 1);
|
||||
}
|
||||
}
|
||||
@@ -536,7 +536,7 @@ TEST_CASE("BufferBase: Cached write downloads") {
|
||||
REQUIRE(rasterizer.Count() == 63);
|
||||
buffer.MarkRegionAsGpuModified(c + PAGE, PAGE);
|
||||
int num = 0;
|
||||
buffer.ForEachDownloadRange(c, WORD, [&](u64 offset, u64 size) { ++num; });
|
||||
buffer.ForEachDownloadRangeAndClear(c, WORD, [&](u64 offset, u64 size) { ++num; });
|
||||
buffer.ForEachUploadRange(c, WORD, [&](u64 offset, u64 size) { ++num; });
|
||||
REQUIRE(num == 0);
|
||||
REQUIRE(!buffer.IsRegionCpuModified(c + PAGE, PAGE));
|
||||
|
||||
@@ -226,19 +226,24 @@ public:
|
||||
/// Call 'func' for each CPU modified range and unmark those pages as CPU modified
|
||||
template <typename Func>
|
||||
void ForEachUploadRange(VAddr query_cpu_range, u64 size, Func&& func) {
|
||||
ForEachModifiedRange<Type::CPU>(query_cpu_range, size, func);
|
||||
ForEachModifiedRange<Type::CPU>(query_cpu_range, size, true, func);
|
||||
}
|
||||
|
||||
/// Call 'func' for each GPU modified range and unmark those pages as GPU modified
|
||||
template <typename Func>
|
||||
void ForEachDownloadRange(VAddr query_cpu_range, u64 size, Func&& func) {
|
||||
ForEachModifiedRange<Type::GPU>(query_cpu_range, size, func);
|
||||
void ForEachDownloadRange(VAddr query_cpu_range, u64 size, bool clear, Func&& func) {
|
||||
ForEachModifiedRange<Type::GPU>(query_cpu_range, size, clear, func);
|
||||
}
|
||||
|
||||
template <typename Func>
|
||||
void ForEachDownloadRangeAndClear(VAddr query_cpu_range, u64 size, Func&& func) {
|
||||
ForEachModifiedRange<Type::GPU>(query_cpu_range, size, true, func);
|
||||
}
|
||||
|
||||
/// Call 'func' for each GPU modified range and unmark those pages as GPU modified
|
||||
template <typename Func>
|
||||
void ForEachDownloadRange(Func&& func) {
|
||||
ForEachModifiedRange<Type::GPU>(cpu_addr, SizeBytes(), func);
|
||||
ForEachModifiedRange<Type::GPU>(cpu_addr, SizeBytes(), true, func);
|
||||
}
|
||||
|
||||
/// Mark buffer as picked
|
||||
@@ -415,7 +420,7 @@ private:
|
||||
* @param func Function to call for each turned off region
|
||||
*/
|
||||
template <Type type, typename Func>
|
||||
void ForEachModifiedRange(VAddr query_cpu_range, s64 size, Func&& func) {
|
||||
void ForEachModifiedRange(VAddr query_cpu_range, s64 size, bool clear, Func&& func) {
|
||||
static_assert(type != Type::Untracked);
|
||||
|
||||
const s64 difference = query_cpu_range - cpu_addr;
|
||||
@@ -467,7 +472,9 @@ private:
|
||||
bits = (bits << left_offset) >> left_offset;
|
||||
|
||||
const u64 current_word = state_words[word_index] & bits;
|
||||
state_words[word_index] &= ~bits;
|
||||
if (clear) {
|
||||
state_words[word_index] &= ~bits;
|
||||
}
|
||||
|
||||
if constexpr (type == Type::CPU) {
|
||||
const u64 current_bits = untracked_words[word_index] & bits;
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
#include <vector>
|
||||
|
||||
#include <boost/container/small_vector.hpp>
|
||||
#include <boost/icl/interval_set.hpp>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "common/div_ceil.h"
|
||||
@@ -77,6 +78,9 @@ class BufferCache {
|
||||
using Runtime = typename P::Runtime;
|
||||
using Buffer = typename P::Buffer;
|
||||
|
||||
using IntervalSet = boost::icl::interval_set<VAddr>;
|
||||
using IntervalType = typename IntervalSet::interval_type;
|
||||
|
||||
struct Empty {};
|
||||
|
||||
struct OverlapResult {
|
||||
@@ -148,18 +152,26 @@ public:
|
||||
/// Return true when there are uncommitted buffers to be downloaded
|
||||
[[nodiscard]] bool HasUncommittedFlushes() const noexcept;
|
||||
|
||||
void AccumulateFlushes();
|
||||
|
||||
/// Return true when the caller should wait for async downloads
|
||||
[[nodiscard]] bool ShouldWaitAsyncFlushes() const noexcept;
|
||||
|
||||
/// Commit asynchronous downloads
|
||||
void CommitAsyncFlushes();
|
||||
void CommitAsyncFlushesHigh();
|
||||
|
||||
/// Pop asynchronous downloads
|
||||
void PopAsyncFlushes();
|
||||
|
||||
[[nodiscard]] bool DMACopy(GPUVAddr src_address, GPUVAddr dest_address, u64 amount);
|
||||
|
||||
/// Return true when a CPU region is modified from the GPU
|
||||
[[nodiscard]] bool IsRegionGpuModified(VAddr addr, size_t size);
|
||||
|
||||
/// Return true when a CPU region is modified from the CPU
|
||||
[[nodiscard]] bool IsRegionCpuModified(VAddr addr, size_t size);
|
||||
|
||||
std::mutex mutex;
|
||||
|
||||
private:
|
||||
@@ -190,6 +202,36 @@ private:
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Func>
|
||||
void ForEachWrittenRange(VAddr cpu_addr, u64 size, Func&& func) {
|
||||
const VAddr start_address = cpu_addr;
|
||||
const VAddr end_address = start_address + size;
|
||||
const VAddr search_base =
|
||||
static_cast<VAddr>(std::min<s64>(0LL, static_cast<s64>(start_address - size)));
|
||||
const IntervalType search_interval{search_base, search_base + 1};
|
||||
auto it = common_ranges.lower_bound(search_interval);
|
||||
if (it == common_ranges.end()) {
|
||||
it = common_ranges.begin();
|
||||
}
|
||||
for (; it != common_ranges.end(); it++) {
|
||||
VAddr inter_addr_end = it->upper();
|
||||
VAddr inter_addr = it->lower();
|
||||
if (inter_addr >= end_address) {
|
||||
break;
|
||||
}
|
||||
if (inter_addr_end <= start_address) {
|
||||
continue;
|
||||
}
|
||||
if (inter_addr_end > end_address) {
|
||||
inter_addr_end = end_address;
|
||||
}
|
||||
if (inter_addr < start_address) {
|
||||
inter_addr = start_address;
|
||||
}
|
||||
func(inter_addr, inter_addr_end);
|
||||
}
|
||||
}
|
||||
|
||||
static bool IsRangeGranular(VAddr cpu_addr, size_t size) {
|
||||
return (cpu_addr & ~Core::Memory::PAGE_MASK) ==
|
||||
((cpu_addr + size) & ~Core::Memory::PAGE_MASK);
|
||||
@@ -272,8 +314,6 @@ private:
|
||||
|
||||
void DeleteBuffer(BufferId buffer_id);
|
||||
|
||||
void ReplaceBufferDownloads(BufferId old_buffer_id, BufferId new_buffer_id);
|
||||
|
||||
void NotifyBufferDeletion();
|
||||
|
||||
[[nodiscard]] Binding StorageBufferBinding(GPUVAddr ssbo_addr) const;
|
||||
@@ -327,9 +367,9 @@ private:
|
||||
|
||||
std::vector<BufferId> cached_write_buffer_ids;
|
||||
|
||||
// TODO: This data structure is not optimal and it should be reworked
|
||||
std::vector<BufferId> uncommitted_downloads;
|
||||
std::deque<std::vector<BufferId>> committed_downloads;
|
||||
IntervalSet uncommitted_ranges;
|
||||
IntervalSet common_ranges;
|
||||
std::deque<IntervalSet> committed_ranges;
|
||||
|
||||
size_t immediate_buffer_capacity = 0;
|
||||
std::unique_ptr<u8[]> immediate_buffer_alloc;
|
||||
@@ -352,6 +392,7 @@ BufferCache<P>::BufferCache(VideoCore::RasterizerInterface& rasterizer_,
|
||||
// Ensure the first slot is used for the null buffer
|
||||
void(slot_buffers.insert(runtime, NullBufferParams{}));
|
||||
deletion_iterator = slot_buffers.end();
|
||||
common_ranges.clear();
|
||||
}
|
||||
|
||||
template <class P>
|
||||
@@ -421,6 +462,68 @@ void BufferCache<P>::DownloadMemory(VAddr cpu_addr, u64 size) {
|
||||
});
|
||||
}
|
||||
|
||||
template <class P>
|
||||
bool BufferCache<P>::DMACopy(GPUVAddr src_address, GPUVAddr dest_address, u64 amount) {
|
||||
const std::optional<VAddr> cpu_src_address = gpu_memory.GpuToCpuAddress(src_address);
|
||||
const std::optional<VAddr> cpu_dest_address = gpu_memory.GpuToCpuAddress(dest_address);
|
||||
if (!cpu_src_address || !cpu_dest_address) {
|
||||
return false;
|
||||
}
|
||||
const bool source_dirty = IsRegionGpuModified(*cpu_src_address, amount);
|
||||
const bool dest_dirty = IsRegionGpuModified(*cpu_dest_address, amount);
|
||||
if (!source_dirty && !dest_dirty) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const IntervalType subtract_interval{*cpu_dest_address, *cpu_dest_address + amount};
|
||||
uncommitted_ranges.subtract(subtract_interval);
|
||||
for (auto& interval_set : committed_ranges) {
|
||||
interval_set.subtract(subtract_interval);
|
||||
}
|
||||
|
||||
BufferId buffer_a;
|
||||
BufferId buffer_b;
|
||||
do {
|
||||
has_deleted_buffers = false;
|
||||
buffer_a = FindBuffer(*cpu_src_address, static_cast<u32>(amount));
|
||||
buffer_b = FindBuffer(*cpu_dest_address, static_cast<u32>(amount));
|
||||
} while (has_deleted_buffers);
|
||||
auto& src_buffer = slot_buffers[buffer_a];
|
||||
auto& dest_buffer = slot_buffers[buffer_b];
|
||||
SynchronizeBuffer(src_buffer, *cpu_src_address, static_cast<u32>(amount));
|
||||
SynchronizeBuffer(dest_buffer, *cpu_dest_address, static_cast<u32>(amount));
|
||||
std::array copies{BufferCopy{
|
||||
.src_offset = src_buffer.Offset(*cpu_src_address),
|
||||
.dst_offset = dest_buffer.Offset(*cpu_dest_address),
|
||||
.size = amount,
|
||||
}};
|
||||
|
||||
boost::container::small_vector<IntervalType, 4> tmp_intervals;
|
||||
auto mirror = [&](VAddr base_address, VAddr base_address_end) {
|
||||
const u64 size = base_address_end - base_address;
|
||||
const VAddr diff = base_address - *cpu_src_address;
|
||||
const VAddr new_base_address = *cpu_dest_address + diff;
|
||||
const IntervalType add_interval{new_base_address, new_base_address + size};
|
||||
uncommitted_ranges.add(add_interval);
|
||||
tmp_intervals.push_back(add_interval);
|
||||
};
|
||||
ForEachWrittenRange(*cpu_src_address, amount, mirror);
|
||||
// This subtraction in this order is important for overlapping copies.
|
||||
common_ranges.subtract(subtract_interval);
|
||||
for (const IntervalType add_interval : tmp_intervals) {
|
||||
common_ranges.add(add_interval);
|
||||
}
|
||||
|
||||
runtime.CopyBuffer(dest_buffer, src_buffer, copies);
|
||||
if (source_dirty) {
|
||||
dest_buffer.MarkRegionAsGpuModified(*cpu_dest_address, amount);
|
||||
}
|
||||
std::vector<u8> tmp_buffer(amount);
|
||||
cpu_memory.ReadBlockUnsafe(*cpu_src_address, tmp_buffer.data(), amount);
|
||||
cpu_memory.WriteBlockUnsafe(*cpu_dest_address, tmp_buffer.data(), amount);
|
||||
return true;
|
||||
}
|
||||
|
||||
template <class P>
|
||||
void BufferCache<P>::BindGraphicsUniformBuffer(size_t stage, u32 index, GPUVAddr gpu_addr,
|
||||
u32 size) {
|
||||
@@ -547,29 +650,30 @@ void BufferCache<P>::FlushCachedWrites() {
|
||||
|
||||
template <class P>
|
||||
bool BufferCache<P>::HasUncommittedFlushes() const noexcept {
|
||||
return !uncommitted_downloads.empty();
|
||||
return !uncommitted_ranges.empty() || !committed_ranges.empty();
|
||||
}
|
||||
|
||||
template <class P>
|
||||
void BufferCache<P>::AccumulateFlushes() {
|
||||
if (Settings::values.gpu_accuracy.GetValue() != Settings::GPUAccuracy::High) {
|
||||
uncommitted_ranges.clear();
|
||||
return;
|
||||
}
|
||||
if (uncommitted_ranges.empty()) {
|
||||
return;
|
||||
}
|
||||
committed_ranges.emplace_back(std::move(uncommitted_ranges));
|
||||
}
|
||||
|
||||
template <class P>
|
||||
bool BufferCache<P>::ShouldWaitAsyncFlushes() const noexcept {
|
||||
return !committed_downloads.empty() && !committed_downloads.front().empty();
|
||||
return false;
|
||||
}
|
||||
|
||||
template <class P>
|
||||
void BufferCache<P>::CommitAsyncFlushes() {
|
||||
// This is intentionally passing the value by copy
|
||||
committed_downloads.push_front(uncommitted_downloads);
|
||||
uncommitted_downloads.clear();
|
||||
}
|
||||
|
||||
template <class P>
|
||||
void BufferCache<P>::PopAsyncFlushes() {
|
||||
if (committed_downloads.empty()) {
|
||||
return;
|
||||
}
|
||||
auto scope_exit_pop_download = detail::ScopeExit([this] { committed_downloads.pop_back(); });
|
||||
const std::span<const BufferId> download_ids = committed_downloads.back();
|
||||
if (download_ids.empty()) {
|
||||
void BufferCache<P>::CommitAsyncFlushesHigh() {
|
||||
AccumulateFlushes();
|
||||
if (committed_ranges.empty()) {
|
||||
return;
|
||||
}
|
||||
MICROPROFILE_SCOPE(GPU_DownloadMemory);
|
||||
@@ -577,20 +681,43 @@ void BufferCache<P>::PopAsyncFlushes() {
|
||||
boost::container::small_vector<std::pair<BufferCopy, BufferId>, 1> downloads;
|
||||
u64 total_size_bytes = 0;
|
||||
u64 largest_copy = 0;
|
||||
for (const BufferId buffer_id : download_ids) {
|
||||
slot_buffers[buffer_id].ForEachDownloadRange([&](u64 range_offset, u64 range_size) {
|
||||
downloads.push_back({
|
||||
BufferCopy{
|
||||
.src_offset = range_offset,
|
||||
.dst_offset = total_size_bytes,
|
||||
.size = range_size,
|
||||
},
|
||||
buffer_id,
|
||||
for (const IntervalSet& intervals : committed_ranges) {
|
||||
for (auto& interval : intervals) {
|
||||
const std::size_t size = interval.upper() - interval.lower();
|
||||
const VAddr cpu_addr = interval.lower();
|
||||
ForEachBufferInRange(cpu_addr, size, [&](BufferId buffer_id, Buffer& buffer) {
|
||||
boost::container::small_vector<BufferCopy, 1> copies;
|
||||
buffer.ForEachDownloadRangeAndClear(
|
||||
cpu_addr, size, [&](u64 range_offset, u64 range_size) {
|
||||
const VAddr buffer_addr = buffer.CpuAddr();
|
||||
const auto add_download = [&](VAddr start, VAddr end) {
|
||||
const u64 new_offset = start - buffer_addr;
|
||||
const u64 new_size = end - start;
|
||||
downloads.push_back({
|
||||
BufferCopy{
|
||||
.src_offset = new_offset,
|
||||
.dst_offset = total_size_bytes,
|
||||
.size = new_size,
|
||||
},
|
||||
buffer_id,
|
||||
});
|
||||
// Align up to avoid cache conflicts
|
||||
constexpr u64 align = 256ULL;
|
||||
constexpr u64 mask = ~(align - 1ULL);
|
||||
total_size_bytes += (new_size + align - 1) & mask;
|
||||
largest_copy = std::max(largest_copy, new_size);
|
||||
};
|
||||
|
||||
const VAddr start_address = buffer_addr + range_offset;
|
||||
const VAddr end_address = start_address + range_size;
|
||||
ForEachWrittenRange(start_address, range_size, add_download);
|
||||
const IntervalType subtract_interval{start_address, end_address};
|
||||
common_ranges.subtract(subtract_interval);
|
||||
});
|
||||
});
|
||||
total_size_bytes += range_size;
|
||||
largest_copy = std::max(largest_copy, range_size);
|
||||
});
|
||||
}
|
||||
}
|
||||
committed_ranges.clear();
|
||||
if (downloads.empty()) {
|
||||
return;
|
||||
}
|
||||
@@ -622,6 +749,19 @@ void BufferCache<P>::PopAsyncFlushes() {
|
||||
}
|
||||
}
|
||||
|
||||
template <class P>
|
||||
void BufferCache<P>::CommitAsyncFlushes() {
|
||||
if (Settings::values.gpu_accuracy.GetValue() == Settings::GPUAccuracy::High) {
|
||||
CommitAsyncFlushesHigh();
|
||||
} else {
|
||||
uncommitted_ranges.clear();
|
||||
committed_ranges.clear();
|
||||
}
|
||||
}
|
||||
|
||||
template <class P>
|
||||
void BufferCache<P>::PopAsyncFlushes() {}
|
||||
|
||||
template <class P>
|
||||
bool BufferCache<P>::IsRegionGpuModified(VAddr addr, size_t size) {
|
||||
const u64 page_end = Common::DivCeil(addr + size, PAGE_SIZE);
|
||||
@@ -641,6 +781,25 @@ bool BufferCache<P>::IsRegionGpuModified(VAddr addr, size_t size) {
|
||||
return false;
|
||||
}
|
||||
|
||||
template <class P>
|
||||
bool BufferCache<P>::IsRegionCpuModified(VAddr addr, size_t size) {
|
||||
const u64 page_end = Common::DivCeil(addr + size, PAGE_SIZE);
|
||||
for (u64 page = addr >> PAGE_BITS; page < page_end;) {
|
||||
const BufferId image_id = page_table[page];
|
||||
if (!image_id) {
|
||||
++page;
|
||||
continue;
|
||||
}
|
||||
Buffer& buffer = slot_buffers[image_id];
|
||||
if (buffer.IsRegionCpuModified(addr, size)) {
|
||||
return true;
|
||||
}
|
||||
const VAddr end_addr = buffer.CpuAddr() + buffer.SizeBytes();
|
||||
page = Common::DivCeil(end_addr, PAGE_SIZE);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
template <class P>
|
||||
void BufferCache<P>::BindHostIndexBuffer() {
|
||||
Buffer& buffer = slot_buffers[index_buffer.buffer_id];
|
||||
@@ -649,7 +808,9 @@ void BufferCache<P>::BindHostIndexBuffer() {
|
||||
const u32 size = index_buffer.size;
|
||||
SynchronizeBuffer(buffer, index_buffer.cpu_addr, size);
|
||||
if constexpr (HAS_FULL_INDEX_AND_PRIMITIVE_SUPPORT) {
|
||||
runtime.BindIndexBuffer(buffer, offset, size);
|
||||
const u32 new_offset = offset + maxwell3d.regs.index_array.first *
|
||||
maxwell3d.regs.index_array.FormatSizeInBytes();
|
||||
runtime.BindIndexBuffer(buffer, new_offset, size);
|
||||
} else {
|
||||
runtime.BindIndexBuffer(maxwell3d.regs.draw.topology, maxwell3d.regs.index_array.format,
|
||||
maxwell3d.regs.index_array.first, maxwell3d.regs.index_array.count,
|
||||
@@ -863,7 +1024,7 @@ void BufferCache<P>::UpdateIndexBuffer() {
|
||||
const GPUVAddr gpu_addr_end = index_array.EndAddress();
|
||||
const std::optional<VAddr> cpu_addr = gpu_memory.GpuToCpuAddress(gpu_addr_begin);
|
||||
const u32 address_size = static_cast<u32>(gpu_addr_end - gpu_addr_begin);
|
||||
const u32 draw_size = index_array.count * index_array.FormatSizeInBytes();
|
||||
const u32 draw_size = (index_array.count + index_array.first) * index_array.FormatSizeInBytes();
|
||||
const u32 size = std::min(address_size, draw_size);
|
||||
if (size == 0 || !cpu_addr) {
|
||||
index_buffer = NULL_BINDING;
|
||||
@@ -1010,16 +1171,16 @@ void BufferCache<P>::MarkWrittenBuffer(BufferId buffer_id, VAddr cpu_addr, u32 s
|
||||
Buffer& buffer = slot_buffers[buffer_id];
|
||||
buffer.MarkRegionAsGpuModified(cpu_addr, size);
|
||||
|
||||
const bool is_accuracy_high = Settings::IsGPULevelHigh();
|
||||
const IntervalType base_interval{cpu_addr, cpu_addr + size};
|
||||
common_ranges.add(base_interval);
|
||||
|
||||
const bool is_accuracy_high =
|
||||
Settings::values.gpu_accuracy.GetValue() == Settings::GPUAccuracy::High;
|
||||
const bool is_async = Settings::values.use_asynchronous_gpu_emulation.GetValue();
|
||||
if (!is_accuracy_high || !is_async) {
|
||||
if (!is_async && !is_accuracy_high) {
|
||||
return;
|
||||
}
|
||||
if (std::ranges::find(uncommitted_downloads, buffer_id) != uncommitted_downloads.end()) {
|
||||
// Already inserted
|
||||
return;
|
||||
}
|
||||
uncommitted_downloads.push_back(buffer_id);
|
||||
uncommitted_ranges.add(base_interval);
|
||||
}
|
||||
|
||||
template <class P>
|
||||
@@ -1103,7 +1264,6 @@ void BufferCache<P>::JoinOverlap(BufferId new_buffer_id, BufferId overlap_id,
|
||||
if (!copies.empty()) {
|
||||
runtime.CopyBuffer(slot_buffers[new_buffer_id], overlap, copies);
|
||||
}
|
||||
ReplaceBufferDownloads(overlap_id, new_buffer_id);
|
||||
DeleteBuffer(overlap_id);
|
||||
}
|
||||
|
||||
@@ -1244,14 +1404,28 @@ void BufferCache<P>::DownloadBufferMemory(Buffer& buffer, VAddr cpu_addr, u64 si
|
||||
boost::container::small_vector<BufferCopy, 1> copies;
|
||||
u64 total_size_bytes = 0;
|
||||
u64 largest_copy = 0;
|
||||
buffer.ForEachDownloadRange(cpu_addr, size, [&](u64 range_offset, u64 range_size) {
|
||||
copies.push_back(BufferCopy{
|
||||
.src_offset = range_offset,
|
||||
.dst_offset = total_size_bytes,
|
||||
.size = range_size,
|
||||
});
|
||||
total_size_bytes += range_size;
|
||||
largest_copy = std::max(largest_copy, range_size);
|
||||
buffer.ForEachDownloadRangeAndClear(cpu_addr, size, [&](u64 range_offset, u64 range_size) {
|
||||
const VAddr buffer_addr = buffer.CpuAddr();
|
||||
const auto add_download = [&](VAddr start, VAddr end) {
|
||||
const u64 new_offset = start - buffer_addr;
|
||||
const u64 new_size = end - start;
|
||||
copies.push_back(BufferCopy{
|
||||
.src_offset = new_offset,
|
||||
.dst_offset = total_size_bytes,
|
||||
.size = new_size,
|
||||
});
|
||||
// Align up to avoid cache conflicts
|
||||
constexpr u64 align = 256ULL;
|
||||
constexpr u64 mask = ~(align - 1ULL);
|
||||
total_size_bytes += (new_size + align - 1) & mask;
|
||||
largest_copy = std::max(largest_copy, new_size);
|
||||
};
|
||||
|
||||
const VAddr start_address = buffer_addr + range_offset;
|
||||
const VAddr end_address = start_address + range_size;
|
||||
ForEachWrittenRange(start_address, range_size, add_download);
|
||||
const IntervalType subtract_interval{start_address, end_address};
|
||||
common_ranges.subtract(subtract_interval);
|
||||
});
|
||||
if (total_size_bytes == 0) {
|
||||
return;
|
||||
@@ -1315,18 +1489,6 @@ void BufferCache<P>::DeleteBuffer(BufferId buffer_id) {
|
||||
NotifyBufferDeletion();
|
||||
}
|
||||
|
||||
template <class P>
|
||||
void BufferCache<P>::ReplaceBufferDownloads(BufferId old_buffer_id, BufferId new_buffer_id) {
|
||||
const auto replace = [old_buffer_id, new_buffer_id](std::vector<BufferId>& buffers) {
|
||||
std::ranges::replace(buffers, old_buffer_id, new_buffer_id);
|
||||
if (auto it = std::ranges::find(buffers, new_buffer_id); it != buffers.end()) {
|
||||
buffers.erase(std::remove(it + 1, buffers.end(), new_buffer_id), buffers.end());
|
||||
}
|
||||
};
|
||||
replace(uncommitted_downloads);
|
||||
std::ranges::for_each(committed_downloads, replace);
|
||||
}
|
||||
|
||||
template <class P>
|
||||
void BufferCache<P>::NotifyBufferDeletion() {
|
||||
if constexpr (HAS_PERSISTENT_UNIFORM_BUFFER_BINDINGS) {
|
||||
@@ -1349,15 +1511,9 @@ typename BufferCache<P>::Binding BufferCache<P>::StorageBufferBinding(GPUVAddr s
|
||||
if (!cpu_addr || size == 0) {
|
||||
return NULL_BINDING;
|
||||
}
|
||||
// HACK(Rodrigo): This is the number of bytes bound in host beyond the guest API's range.
|
||||
// It exists due to some games like Astral Chain operate out of bounds.
|
||||
// Binding the whole map range would be technically correct, but games have large maps that make
|
||||
// this approach unaffordable for now.
|
||||
static constexpr u32 arbitrary_extra_bytes = 0xc000;
|
||||
const u32 bytes_to_map_end = static_cast<u32>(gpu_memory.BytesToMapEnd(gpu_addr));
|
||||
const Binding binding{
|
||||
.cpu_addr = *cpu_addr,
|
||||
.size = std::min(size + arbitrary_extra_bytes, bytes_to_map_end),
|
||||
.size = size,
|
||||
.buffer_id = BufferId{},
|
||||
};
|
||||
return binding;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user