Compare commits
48 Commits
__refs_pul
...
__refs_pul
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0ed1077763 | ||
|
|
f9139ddab7 | ||
|
|
8284658bac | ||
|
|
386cd45f07 | ||
|
|
5edc96f4a4 | ||
|
|
a949ee0410 | ||
|
|
c7ad195fd3 | ||
|
|
92a3daf029 | ||
|
|
91a4a924b1 | ||
|
|
7636fefb71 | ||
|
|
8542f2f3fc | ||
|
|
4d0bdef17d | ||
|
|
2f0e1f5d02 | ||
|
|
2eb018c80f | ||
|
|
eb3cb3af35 | ||
|
|
b07423f6e2 | ||
|
|
b9f915e07a | ||
|
|
dbcc093d88 | ||
|
|
ebb82b0b83 | ||
|
|
a59ae5e702 | ||
|
|
14ab50defb | ||
|
|
d299d5531f | ||
|
|
bf50345d4c | ||
|
|
3d03a6ae02 | ||
|
|
c770fa9823 | ||
|
|
c6a9e91784 | ||
|
|
a8a0927d42 | ||
|
|
8f9f142956 | ||
|
|
fd98fcf7f0 | ||
|
|
38165fb7e3 | ||
|
|
5dfa313d2c | ||
|
|
38f658d21e | ||
|
|
cf1cd3321d | ||
|
|
208a04dcff | ||
|
|
299c5594e6 | ||
|
|
0e5c74bc9e | ||
|
|
7a8de138df | ||
|
|
b91b76df4f | ||
|
|
248a146ab7 | ||
|
|
22d7b89c15 | ||
|
|
954259312e | ||
|
|
0eae00e263 | ||
|
|
e828c5a559 | ||
|
|
58550cfcdc | ||
|
|
a47704f4dd | ||
|
|
d3d6613d33 | ||
|
|
35b17fa5e0 | ||
|
|
20e51402b0 |
@@ -45,14 +45,23 @@ if (MSVC)
|
||||
/Zc:inline
|
||||
/Zc:throwingNew
|
||||
|
||||
# External headers diagnostics
|
||||
/experimental:external # Enables the external headers options. This option isn't required in Visual Studio 2019 version 16.10 and later
|
||||
/external:anglebrackets # Treats all headers included by #include <header>, where the header file is enclosed in angle brackets (< >), as external headers
|
||||
/external:W0 # Sets the default warning level to 0 for external headers, effectively turning off warnings for external headers
|
||||
|
||||
# Warnings
|
||||
/W3
|
||||
/we4062 # enumerator 'identifier' in a switch of enum 'enumeration' is not handled
|
||||
/we4018 # 'expression': signed/unsigned mismatch
|
||||
/we4062 # Enumerator 'identifier' in a switch of enum 'enumeration' is not handled
|
||||
/we4101 # 'identifier': unreferenced local variable
|
||||
/we4189 # 'identifier': local variable is initialized but not referenced
|
||||
/we4265 # 'class': class has virtual functions, but destructor is not virtual
|
||||
/we4388 # signed/unsigned mismatch
|
||||
/we4547 # 'operator' : operator before comma has no effect; expected operator with side-effect
|
||||
/we4267 # 'var': conversion from 'size_t' to 'type', possible loss of data
|
||||
/we4305 # 'context': truncation from 'type1' to 'type2'
|
||||
/we4388 # 'expression': signed/unsigned mismatch
|
||||
/we4389 # 'operator': signed/unsigned mismatch
|
||||
/we4547 # 'operator': operator before comma has no effect; expected operator with side-effect
|
||||
/we4549 # 'operator1': operator before comma has no effect; did you intend 'operator2'?
|
||||
/we4555 # Expression has no effect; expected expression with side-effect
|
||||
/we4715 # 'function': not all control paths return a value
|
||||
@@ -73,6 +82,7 @@ else()
|
||||
-Werror=missing-declarations
|
||||
-Werror=missing-field-initializers
|
||||
-Werror=reorder
|
||||
-Werror=sign-compare
|
||||
-Werror=switch
|
||||
-Werror=uninitialized
|
||||
-Werror=unused-function
|
||||
|
||||
@@ -51,9 +51,6 @@ if (NOT MSVC)
|
||||
target_compile_options(audio_core PRIVATE
|
||||
-Werror=conversion
|
||||
-Werror=ignored-qualifiers
|
||||
-Werror=implicit-fallthrough
|
||||
-Werror=reorder
|
||||
-Werror=sign-compare
|
||||
-Werror=shadow
|
||||
-Werror=unused-parameter
|
||||
-Werror=unused-variable
|
||||
|
||||
@@ -30,7 +30,8 @@ StreamPtr AudioOut::OpenStream(Core::Timing::CoreTiming& core_timing, u32 sample
|
||||
u32 num_channels, std::string&& name,
|
||||
Stream::ReleaseCallback&& release_callback) {
|
||||
if (!sink) {
|
||||
sink = CreateSinkFromID(Settings::values.sink_id, Settings::values.audio_device_id);
|
||||
sink = CreateSinkFromID(Settings::values.sink_id.GetValue(),
|
||||
Settings::values.audio_device_id.GetValue());
|
||||
}
|
||||
|
||||
return std::make_shared<Stream>(
|
||||
|
||||
@@ -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,8 @@ 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) {
|
||||
s32 x = 0;
|
||||
for (s32 i = 0; i < sample_count; i++) {
|
||||
x = static_cast<s32>(static_cast<float>(input[i]) * gain);
|
||||
@@ -50,20 +51,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 +131,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 +403,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 +460,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 +554,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 +590,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 +607,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 +647,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 +673,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 +683,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 +703,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 +715,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 +971,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 +1012,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 +1029,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 +1178,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 +1196,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 +1216,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 +1233,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 +1275,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 +1337,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
|
||||
|
||||
@@ -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() {
|
||||
@@ -94,7 +94,7 @@ bool IsGPULevelHigh() {
|
||||
|
||||
bool IsFastmemEnabled() {
|
||||
if (values.cpu_accuracy.GetValue() == CPUAccuracy::DebugMode) {
|
||||
return values.cpuopt_fastmem;
|
||||
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 {
|
||||
|
||||
@@ -34,68 +36,235 @@ enum class CPUAccuracy : u32 {
|
||||
DebugMode = 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,155 @@ 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::Accurate, "cpu_accuracy"};
|
||||
|
||||
bool cpuopt_page_tables;
|
||||
bool cpuopt_block_linking;
|
||||
bool cpuopt_return_stack_buffer;
|
||||
bool cpuopt_fast_dispatcher;
|
||||
bool cpuopt_context_elimination;
|
||||
bool cpuopt_const_prop;
|
||||
bool cpuopt_misc_ir;
|
||||
bool cpuopt_reduce_misalign_checks;
|
||||
bool cpuopt_fastmem;
|
||||
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"};
|
||||
Setting<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;
|
||||
|
||||
@@ -654,24 +654,19 @@ endif()
|
||||
|
||||
if (MSVC)
|
||||
target_compile_options(core PRIVATE
|
||||
/we4018 # 'expression' : signed/unsigned mismatch
|
||||
/we4244 # 'argument' : conversion from 'type1' to 'type2', possible loss of data (floating-point)
|
||||
/we4245 # 'conversion' : conversion from 'type1' to 'type2', signed/unsigned mismatch
|
||||
/we4242 # 'identifier': conversion from 'type1' to 'type2', possible loss of data
|
||||
/we4244 # 'conversion': conversion from 'type1' to 'type2', possible loss of data
|
||||
/we4245 # 'conversion': conversion from 'type1' to 'type2', signed/unsigned mismatch
|
||||
/we4254 # 'operator': conversion from 'type1:field_bits' to 'type2:field_bits', possible loss of data
|
||||
/we4267 # 'var' : conversion from 'size_t' to 'type', possible loss of data
|
||||
/we4305 # 'context' : truncation from 'type1' to 'type2'
|
||||
/we4456 # Declaration of 'identifier' hides previous local declaration
|
||||
/we4457 # Declaration of 'identifier' hides function parameter
|
||||
/we4458 # Declaration of 'identifier' hides class member
|
||||
/we4459 # Declaration of 'identifier' hides global declaration
|
||||
/we4715 # 'function' : not all control paths return a value
|
||||
)
|
||||
else()
|
||||
target_compile_options(core PRIVATE
|
||||
-Werror=conversion
|
||||
-Werror=ignored-qualifiers
|
||||
-Werror=implicit-fallthrough
|
||||
-Werror=sign-compare
|
||||
-Werror=shadow
|
||||
|
||||
$<$<CXX_COMPILER_ID:GNU>:-Werror=class-memaccess>
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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...");
|
||||
}
|
||||
|
||||
|
||||
@@ -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)) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -20,6 +20,7 @@ namespace {
|
||||
|
||||
constexpr ResultCode ERROR_CANNOT_FIND_ENTRY{ErrorModule::Mii, 4};
|
||||
|
||||
constexpr std::size_t BaseMiiCount{2};
|
||||
constexpr std::size_t DefaultMiiCount{RawData::DefaultMii.size()};
|
||||
|
||||
constexpr MiiStoreData::Name DefaultMiiName{u'y', u'u', u'z', u'u'};
|
||||
@@ -415,7 +416,7 @@ u32 MiiManager::GetCount(SourceFlag source_flag) const {
|
||||
count += 0;
|
||||
}
|
||||
if ((source_flag & SourceFlag::Default) != SourceFlag::None) {
|
||||
count += DefaultMiiCount;
|
||||
count += (DefaultMiiCount - BaseMiiCount);
|
||||
}
|
||||
return static_cast<u32>(count);
|
||||
}
|
||||
@@ -445,7 +446,7 @@ ResultVal<std::vector<MiiInfoElement>> MiiManager::GetDefault(SourceFlag source_
|
||||
return MakeResult(std::move(result));
|
||||
}
|
||||
|
||||
for (std::size_t index = 0; index < DefaultMiiCount; index++) {
|
||||
for (std::size_t index = BaseMiiCount; index < DefaultMiiCount; index++) {
|
||||
result.emplace_back(BuildDefault(index), Source::Default);
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -34,28 +34,17 @@ if (MSVC)
|
||||
/W4
|
||||
/WX
|
||||
|
||||
# 'expression' : signed/unsigned mismatch
|
||||
/we4018
|
||||
# 'argument' : conversion from 'type1' to 'type2', possible loss of data (floating-point)
|
||||
/we4244
|
||||
# 'conversion' : conversion from 'type1' to 'type2', signed/unsigned mismatch
|
||||
/we4245
|
||||
# 'operator': conversion from 'type1:field_bits' to 'type2:field_bits', possible loss of data
|
||||
/we4254
|
||||
# 'var' : conversion from 'size_t' to 'type', possible loss of data
|
||||
/we4267
|
||||
# 'context' : truncation from 'type1' to 'type2'
|
||||
/we4305
|
||||
/we4242 # 'identifier': conversion from 'type1' to 'type2', possible loss of data
|
||||
/we4244 # 'conversion': conversion from 'type1' to 'type2', possible loss of data
|
||||
/we4245 # 'conversion': conversion from 'type1' to 'type2', signed/unsigned mismatch
|
||||
/we4254 # 'operator': conversion from 'type1:field_bits' to 'type2:field_bits', possible loss of data
|
||||
)
|
||||
else()
|
||||
target_compile_options(input_common PRIVATE
|
||||
-Werror
|
||||
-Werror=conversion
|
||||
-Werror=ignored-qualifiers
|
||||
-Werror=implicit-fallthrough
|
||||
-Werror=reorder
|
||||
-Werror=shadow
|
||||
-Werror=sign-compare
|
||||
$<$<CXX_COMPILER_ID:GNU>:-Werror=unused-but-set-parameter>
|
||||
$<$<CXX_COMPILER_ID:GNU>:-Werror=unused-but-set-variable>
|
||||
-Werror=unused-variable
|
||||
|
||||
@@ -5,14 +5,7 @@
|
||||
#include <chrono>
|
||||
#include <thread>
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable : 4200) // nonstandard extension used : zero-sized array in struct/union
|
||||
#endif
|
||||
#include <libusb.h>
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(pop)
|
||||
#endif
|
||||
|
||||
#include "common/logging/log.h"
|
||||
#include "common/param_package.h"
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -8,14 +8,7 @@
|
||||
#include <optional>
|
||||
#include <type_traits>
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable : 4701)
|
||||
#endif
|
||||
#include <boost/crc.hpp>
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(pop)
|
||||
#endif
|
||||
|
||||
#include "common/bit_field.h"
|
||||
#include "common/swap.h"
|
||||
|
||||
@@ -292,13 +292,12 @@ endif()
|
||||
|
||||
if (MSVC)
|
||||
target_compile_options(video_core PRIVATE
|
||||
/we4267 # 'var' : conversion from 'size_t' to 'type', possible loss of data
|
||||
/we4244 # 'var' : conversion from integer to 'type', possible loss of data
|
||||
/we4242 # 'identifier': conversion from 'type1' to 'type2', possible loss of data
|
||||
/we4244 # 'conversion': conversion from 'type1' to 'type2', possible loss of data
|
||||
/we4456 # Declaration of 'identifier' hides previous local declaration
|
||||
/we4457 # Declaration of 'identifier' hides function parameter
|
||||
/we4458 # Declaration of 'identifier' hides class member
|
||||
/we4459 # Declaration of 'identifier' hides global declaration
|
||||
/we4715 # 'function' : not all control paths return a value
|
||||
)
|
||||
else()
|
||||
target_compile_options(video_core PRIVATE
|
||||
|
||||
@@ -103,8 +103,7 @@ void CDmaPusher::ExecuteCommand(u32 state_offset, u32 data) {
|
||||
case ThiMethod::SetMethod1:
|
||||
LOG_DEBUG(Service_NVDRV, "NVDEC method 0x{:X}",
|
||||
static_cast<u32>(nvdec_thi_state.method_0));
|
||||
nvdec_processor->ProcessMethod(static_cast<Nvdec::Method>(nvdec_thi_state.method_0),
|
||||
data);
|
||||
nvdec_processor->ProcessMethod(nvdec_thi_state.method_0, data);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
||||
@@ -23,8 +23,8 @@ void AVFrameDeleter(AVFrame* ptr) {
|
||||
av_free(ptr);
|
||||
}
|
||||
|
||||
Codec::Codec(GPU& gpu_)
|
||||
: gpu(gpu_), h264_decoder(std::make_unique<Decoder::H264>(gpu)),
|
||||
Codec::Codec(GPU& gpu_, const NvdecCommon::NvdecRegisters& regs)
|
||||
: gpu(gpu_), state{regs}, h264_decoder(std::make_unique<Decoder::H264>(gpu)),
|
||||
vp9_decoder(std::make_unique<Decoder::VP9>(gpu)) {}
|
||||
|
||||
Codec::~Codec() {
|
||||
@@ -43,46 +43,48 @@ Codec::~Codec() {
|
||||
avcodec_close(av_codec_ctx);
|
||||
}
|
||||
|
||||
void Codec::SetTargetCodec(NvdecCommon::VideoCodec codec) {
|
||||
if (current_codec != codec) {
|
||||
LOG_INFO(Service_NVDRV, "NVDEC video codec initialized to {}", static_cast<u32>(codec));
|
||||
current_codec = codec;
|
||||
void Codec::Initialize() {
|
||||
AVCodecID codec{AV_CODEC_ID_NONE};
|
||||
switch (current_codec) {
|
||||
case NvdecCommon::VideoCodec::H264:
|
||||
codec = AV_CODEC_ID_H264;
|
||||
break;
|
||||
case NvdecCommon::VideoCodec::Vp9:
|
||||
codec = AV_CODEC_ID_VP9;
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
av_codec = avcodec_find_decoder(codec);
|
||||
av_codec_ctx = avcodec_alloc_context3(av_codec);
|
||||
av_opt_set(av_codec_ctx->priv_data, "tune", "zerolatency", 0);
|
||||
|
||||
// TODO(ameerj): libavcodec gpu hw acceleration
|
||||
|
||||
const auto av_error = avcodec_open2(av_codec_ctx, av_codec, nullptr);
|
||||
if (av_error < 0) {
|
||||
LOG_ERROR(Service_NVDRV, "avcodec_open2() Failed.");
|
||||
avcodec_close(av_codec_ctx);
|
||||
return;
|
||||
}
|
||||
initialized = true;
|
||||
return;
|
||||
}
|
||||
|
||||
void Codec::StateWrite(u32 offset, u64 arguments) {
|
||||
u8* const state_offset = reinterpret_cast<u8*>(&state) + offset * sizeof(u64);
|
||||
std::memcpy(state_offset, &arguments, sizeof(u64));
|
||||
void Codec::SetTargetCodec(NvdecCommon::VideoCodec codec) {
|
||||
if (current_codec != codec) {
|
||||
current_codec = codec;
|
||||
LOG_INFO(Service_NVDRV, "NVDEC video codec initialized to {}", GetCurrentCodecName());
|
||||
}
|
||||
}
|
||||
|
||||
void Codec::Decode() {
|
||||
bool is_first_frame = false;
|
||||
const bool is_first_frame = !initialized;
|
||||
if (!initialized) {
|
||||
if (current_codec == NvdecCommon::VideoCodec::H264) {
|
||||
av_codec = avcodec_find_decoder(AV_CODEC_ID_H264);
|
||||
} else if (current_codec == NvdecCommon::VideoCodec::Vp9) {
|
||||
av_codec = avcodec_find_decoder(AV_CODEC_ID_VP9);
|
||||
} else {
|
||||
LOG_ERROR(Service_NVDRV, "Unknown video codec {}", current_codec);
|
||||
return;
|
||||
}
|
||||
|
||||
av_codec_ctx = avcodec_alloc_context3(av_codec);
|
||||
av_opt_set(av_codec_ctx->priv_data, "tune", "zerolatency", 0);
|
||||
|
||||
// TODO(ameerj): libavcodec gpu hw acceleration
|
||||
|
||||
const auto av_error = avcodec_open2(av_codec_ctx, av_codec, nullptr);
|
||||
if (av_error < 0) {
|
||||
LOG_ERROR(Service_NVDRV, "avcodec_open2() Failed.");
|
||||
avcodec_close(av_codec_ctx);
|
||||
return;
|
||||
}
|
||||
initialized = true;
|
||||
is_first_frame = true;
|
||||
Initialize();
|
||||
}
|
||||
bool vp9_hidden_frame = false;
|
||||
|
||||
bool vp9_hidden_frame = false;
|
||||
AVPacket packet{};
|
||||
av_init_packet(&packet);
|
||||
std::vector<u8> frame_data;
|
||||
@@ -95,7 +97,7 @@ void Codec::Decode() {
|
||||
}
|
||||
|
||||
packet.data = frame_data.data();
|
||||
packet.size = static_cast<int>(frame_data.size());
|
||||
packet.size = static_cast<s32>(frame_data.size());
|
||||
|
||||
avcodec_send_packet(av_codec_ctx, &packet);
|
||||
|
||||
@@ -127,4 +129,21 @@ NvdecCommon::VideoCodec Codec::GetCurrentCodec() const {
|
||||
return current_codec;
|
||||
}
|
||||
|
||||
std::string_view Codec::GetCurrentCodecName() const {
|
||||
switch (current_codec) {
|
||||
case NvdecCommon::VideoCodec::None:
|
||||
return "None";
|
||||
case NvdecCommon::VideoCodec::H264:
|
||||
return "H264";
|
||||
case NvdecCommon::VideoCodec::Vp8:
|
||||
return "VP8";
|
||||
case NvdecCommon::VideoCodec::H265:
|
||||
return "H265";
|
||||
case NvdecCommon::VideoCodec::Vp9:
|
||||
return "VP9";
|
||||
default:
|
||||
return "Unknown";
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace Tegra
|
||||
|
||||
@@ -14,18 +14,10 @@ extern "C" {
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wconversion"
|
||||
#endif
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable : 4242) // conversion from 'type' to 'type', possible loss of data
|
||||
#pragma warning(disable : 4244) // conversion from 'type' to 'type', possible loss of data
|
||||
#endif
|
||||
#include <libavcodec/avcodec.h>
|
||||
#if defined(__GNUC__) || defined(__clang__)
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(pop)
|
||||
#endif
|
||||
}
|
||||
|
||||
namespace Tegra {
|
||||
@@ -42,15 +34,15 @@ class VP9;
|
||||
|
||||
class Codec {
|
||||
public:
|
||||
explicit Codec(GPU& gpu);
|
||||
explicit Codec(GPU& gpu, const NvdecCommon::NvdecRegisters& regs);
|
||||
~Codec();
|
||||
|
||||
/// Initialize the codec, returning success or failure
|
||||
void Initialize();
|
||||
|
||||
/// Sets NVDEC video stream codec
|
||||
void SetTargetCodec(NvdecCommon::VideoCodec codec);
|
||||
|
||||
/// Populate NvdecRegisters state with argument value at the provided offset
|
||||
void StateWrite(u32 offset, u64 arguments);
|
||||
|
||||
/// Call decoders to construct headers, decode AVFrame with ffmpeg
|
||||
void Decode();
|
||||
|
||||
@@ -59,6 +51,8 @@ public:
|
||||
|
||||
/// Returns the value of current_codec
|
||||
[[nodiscard]] NvdecCommon::VideoCodec GetCurrentCodec() const;
|
||||
/// Return name of the current codec
|
||||
[[nodiscard]] std::string_view GetCurrentCodecName() const;
|
||||
|
||||
private:
|
||||
bool initialized{};
|
||||
@@ -68,10 +62,10 @@ private:
|
||||
AVCodecContext* av_codec_ctx{nullptr};
|
||||
|
||||
GPU& gpu;
|
||||
const NvdecCommon::NvdecRegisters& state;
|
||||
std::unique_ptr<Decoder::H264> h264_decoder;
|
||||
std::unique_ptr<Decoder::VP9> vp9_decoder;
|
||||
|
||||
NvdecCommon::NvdecRegisters state{};
|
||||
std::queue<AVFramePtr> av_frames{};
|
||||
};
|
||||
|
||||
|
||||
@@ -45,135 +45,130 @@ H264::~H264() = default;
|
||||
|
||||
const std::vector<u8>& H264::ComposeFrameHeader(const NvdecCommon::NvdecRegisters& state,
|
||||
bool is_first_frame) {
|
||||
H264DecoderContext context{};
|
||||
H264DecoderContext context;
|
||||
gpu.MemoryManager().ReadBlock(state.picture_info_offset, &context, sizeof(H264DecoderContext));
|
||||
|
||||
const s32 frame_number = static_cast<s32>((context.h264_parameter_set.flags >> 46) & 0x1ffff);
|
||||
const s64 frame_number = context.h264_parameter_set.frame_number.Value();
|
||||
if (!is_first_frame && frame_number != 0) {
|
||||
frame.resize(context.frame_data_size);
|
||||
|
||||
frame.resize(context.stream_len);
|
||||
gpu.MemoryManager().ReadBlock(state.frame_bitstream_offset, frame.data(), frame.size());
|
||||
} else {
|
||||
/// Encode header
|
||||
H264BitWriter writer{};
|
||||
writer.WriteU(1, 24);
|
||||
writer.WriteU(0, 1);
|
||||
writer.WriteU(3, 2);
|
||||
writer.WriteU(7, 5);
|
||||
writer.WriteU(100, 8);
|
||||
writer.WriteU(0, 8);
|
||||
writer.WriteU(31, 8);
|
||||
writer.WriteUe(0);
|
||||
const auto chroma_format_idc =
|
||||
static_cast<u32>((context.h264_parameter_set.flags >> 12) & 3);
|
||||
writer.WriteUe(chroma_format_idc);
|
||||
if (chroma_format_idc == 3) {
|
||||
writer.WriteBit(false);
|
||||
}
|
||||
|
||||
writer.WriteUe(0);
|
||||
writer.WriteUe(0);
|
||||
writer.WriteBit(false); // QpprimeYZeroTransformBypassFlag
|
||||
writer.WriteBit(false); // Scaling matrix present flag
|
||||
|
||||
const auto order_cnt_type = static_cast<u32>((context.h264_parameter_set.flags >> 14) & 3);
|
||||
writer.WriteUe(static_cast<u32>((context.h264_parameter_set.flags >> 8) & 0xf));
|
||||
writer.WriteUe(order_cnt_type);
|
||||
if (order_cnt_type == 0) {
|
||||
writer.WriteUe(context.h264_parameter_set.log2_max_pic_order_cnt);
|
||||
} else if (order_cnt_type == 1) {
|
||||
writer.WriteBit(context.h264_parameter_set.delta_pic_order_always_zero_flag != 0);
|
||||
|
||||
writer.WriteSe(0);
|
||||
writer.WriteSe(0);
|
||||
writer.WriteUe(0);
|
||||
}
|
||||
|
||||
const s32 pic_height = context.h264_parameter_set.pic_height_in_map_units /
|
||||
(context.h264_parameter_set.frame_mbs_only_flag ? 1 : 2);
|
||||
|
||||
writer.WriteUe(16);
|
||||
writer.WriteBit(false);
|
||||
writer.WriteUe(context.h264_parameter_set.pic_width_in_mbs - 1);
|
||||
writer.WriteUe(pic_height - 1);
|
||||
writer.WriteBit(context.h264_parameter_set.frame_mbs_only_flag != 0);
|
||||
|
||||
if (!context.h264_parameter_set.frame_mbs_only_flag) {
|
||||
writer.WriteBit(((context.h264_parameter_set.flags >> 0) & 1) != 0);
|
||||
}
|
||||
|
||||
writer.WriteBit(((context.h264_parameter_set.flags >> 1) & 1) != 0);
|
||||
writer.WriteBit(false); // Frame cropping flag
|
||||
writer.WriteBit(false); // VUI parameter present flag
|
||||
|
||||
writer.End();
|
||||
|
||||
// H264 PPS
|
||||
writer.WriteU(1, 24);
|
||||
writer.WriteU(0, 1);
|
||||
writer.WriteU(3, 2);
|
||||
writer.WriteU(8, 5);
|
||||
|
||||
writer.WriteUe(0);
|
||||
writer.WriteUe(0);
|
||||
|
||||
writer.WriteBit(context.h264_parameter_set.entropy_coding_mode_flag != 0);
|
||||
writer.WriteBit(false);
|
||||
writer.WriteUe(0);
|
||||
writer.WriteUe(context.h264_parameter_set.num_refidx_l0_default_active);
|
||||
writer.WriteUe(context.h264_parameter_set.num_refidx_l1_default_active);
|
||||
writer.WriteBit(((context.h264_parameter_set.flags >> 2) & 1) != 0);
|
||||
writer.WriteU(static_cast<s32>((context.h264_parameter_set.flags >> 32) & 0x3), 2);
|
||||
s32 pic_init_qp = static_cast<s32>((context.h264_parameter_set.flags >> 16) & 0x3f);
|
||||
pic_init_qp = (pic_init_qp << 26) >> 26;
|
||||
writer.WriteSe(pic_init_qp);
|
||||
writer.WriteSe(0);
|
||||
s32 chroma_qp_index_offset =
|
||||
static_cast<s32>((context.h264_parameter_set.flags >> 22) & 0x1f);
|
||||
chroma_qp_index_offset = (chroma_qp_index_offset << 27) >> 27;
|
||||
|
||||
writer.WriteSe(chroma_qp_index_offset);
|
||||
writer.WriteBit(context.h264_parameter_set.deblocking_filter_control_flag != 0);
|
||||
writer.WriteBit(((context.h264_parameter_set.flags >> 3) & 1) != 0);
|
||||
writer.WriteBit(context.h264_parameter_set.redundant_pic_count_flag != 0);
|
||||
writer.WriteBit(context.h264_parameter_set.transform_8x8_mode_flag != 0);
|
||||
|
||||
writer.WriteBit(true);
|
||||
|
||||
for (s32 index = 0; index < 6; index++) {
|
||||
writer.WriteBit(true);
|
||||
const auto matrix_x4 =
|
||||
std::vector<u8>(context.scaling_matrix_4.begin(), context.scaling_matrix_4.end());
|
||||
writer.WriteScalingList(matrix_x4, index * 16, 16);
|
||||
}
|
||||
|
||||
if (context.h264_parameter_set.transform_8x8_mode_flag) {
|
||||
for (s32 index = 0; index < 2; index++) {
|
||||
writer.WriteBit(true);
|
||||
const auto matrix_x8 = std::vector<u8>(context.scaling_matrix_8.begin(),
|
||||
context.scaling_matrix_8.end());
|
||||
|
||||
writer.WriteScalingList(matrix_x8, index * 64, 64);
|
||||
}
|
||||
}
|
||||
|
||||
s32 chroma_qp_index_offset2 =
|
||||
static_cast<s32>((context.h264_parameter_set.flags >> 27) & 0x1f);
|
||||
chroma_qp_index_offset2 = (chroma_qp_index_offset2 << 27) >> 27;
|
||||
|
||||
writer.WriteSe(chroma_qp_index_offset2);
|
||||
|
||||
writer.End();
|
||||
|
||||
const auto& encoded_header = writer.GetByteArray();
|
||||
frame.resize(encoded_header.size() + context.frame_data_size);
|
||||
std::memcpy(frame.data(), encoded_header.data(), encoded_header.size());
|
||||
|
||||
gpu.MemoryManager().ReadBlock(state.frame_bitstream_offset,
|
||||
frame.data() + encoded_header.size(),
|
||||
context.frame_data_size);
|
||||
return frame;
|
||||
}
|
||||
|
||||
// Encode header
|
||||
H264BitWriter writer{};
|
||||
writer.WriteU(1, 24);
|
||||
writer.WriteU(0, 1);
|
||||
writer.WriteU(3, 2);
|
||||
writer.WriteU(7, 5);
|
||||
writer.WriteU(100, 8);
|
||||
writer.WriteU(0, 8);
|
||||
writer.WriteU(31, 8);
|
||||
writer.WriteUe(0);
|
||||
const u32 chroma_format_idc =
|
||||
static_cast<u32>(context.h264_parameter_set.chroma_format_idc.Value());
|
||||
writer.WriteUe(chroma_format_idc);
|
||||
if (chroma_format_idc == 3) {
|
||||
writer.WriteBit(false);
|
||||
}
|
||||
|
||||
writer.WriteUe(0);
|
||||
writer.WriteUe(0);
|
||||
writer.WriteBit(false); // QpprimeYZeroTransformBypassFlag
|
||||
writer.WriteBit(false); // Scaling matrix present flag
|
||||
|
||||
writer.WriteUe(static_cast<u32>(context.h264_parameter_set.log2_max_frame_num_minus4.Value()));
|
||||
|
||||
const auto order_cnt_type =
|
||||
static_cast<u32>(context.h264_parameter_set.pic_order_cnt_type.Value());
|
||||
writer.WriteUe(order_cnt_type);
|
||||
if (order_cnt_type == 0) {
|
||||
writer.WriteUe(context.h264_parameter_set.log2_max_pic_order_cnt_lsb_minus4);
|
||||
} else if (order_cnt_type == 1) {
|
||||
writer.WriteBit(context.h264_parameter_set.delta_pic_order_always_zero_flag != 0);
|
||||
|
||||
writer.WriteSe(0);
|
||||
writer.WriteSe(0);
|
||||
writer.WriteUe(0);
|
||||
}
|
||||
|
||||
const s32 pic_height = context.h264_parameter_set.frame_height_in_map_units /
|
||||
(context.h264_parameter_set.frame_mbs_only_flag ? 1 : 2);
|
||||
|
||||
writer.WriteUe(16);
|
||||
writer.WriteBit(false);
|
||||
writer.WriteUe(context.h264_parameter_set.pic_width_in_mbs - 1);
|
||||
writer.WriteUe(pic_height - 1);
|
||||
writer.WriteBit(context.h264_parameter_set.frame_mbs_only_flag != 0);
|
||||
|
||||
if (!context.h264_parameter_set.frame_mbs_only_flag) {
|
||||
writer.WriteBit(context.h264_parameter_set.flags.mbaff_frame.Value() != 0);
|
||||
}
|
||||
|
||||
writer.WriteBit(context.h264_parameter_set.flags.direct_8x8_inference.Value() != 0);
|
||||
writer.WriteBit(false); // Frame cropping flag
|
||||
writer.WriteBit(false); // VUI parameter present flag
|
||||
|
||||
writer.End();
|
||||
|
||||
// H264 PPS
|
||||
writer.WriteU(1, 24);
|
||||
writer.WriteU(0, 1);
|
||||
writer.WriteU(3, 2);
|
||||
writer.WriteU(8, 5);
|
||||
|
||||
writer.WriteUe(0);
|
||||
writer.WriteUe(0);
|
||||
|
||||
writer.WriteBit(context.h264_parameter_set.entropy_coding_mode_flag != 0);
|
||||
writer.WriteBit(false);
|
||||
writer.WriteUe(0);
|
||||
writer.WriteUe(context.h264_parameter_set.num_refidx_l0_default_active);
|
||||
writer.WriteUe(context.h264_parameter_set.num_refidx_l1_default_active);
|
||||
writer.WriteBit(context.h264_parameter_set.flags.weighted_pred.Value() != 0);
|
||||
writer.WriteU(static_cast<s32>(context.h264_parameter_set.weighted_bipred_idc.Value()), 2);
|
||||
s32 pic_init_qp = static_cast<s32>(context.h264_parameter_set.pic_init_qp_minus26.Value());
|
||||
writer.WriteSe(pic_init_qp);
|
||||
writer.WriteSe(0);
|
||||
s32 chroma_qp_index_offset =
|
||||
static_cast<s32>(context.h264_parameter_set.chroma_qp_index_offset.Value());
|
||||
|
||||
writer.WriteSe(chroma_qp_index_offset);
|
||||
writer.WriteBit(context.h264_parameter_set.deblocking_filter_control_present_flag != 0);
|
||||
writer.WriteBit(context.h264_parameter_set.flags.constrained_intra_pred.Value() != 0);
|
||||
writer.WriteBit(context.h264_parameter_set.redundant_pic_cnt_present_flag != 0);
|
||||
writer.WriteBit(context.h264_parameter_set.transform_8x8_mode_flag != 0);
|
||||
|
||||
writer.WriteBit(true);
|
||||
|
||||
for (s32 index = 0; index < 6; index++) {
|
||||
writer.WriteBit(true);
|
||||
std::span<const u8> matrix{context.weight_scale};
|
||||
writer.WriteScalingList(matrix, index * 16, 16);
|
||||
}
|
||||
|
||||
if (context.h264_parameter_set.transform_8x8_mode_flag) {
|
||||
for (s32 index = 0; index < 2; index++) {
|
||||
writer.WriteBit(true);
|
||||
std::span<const u8> matrix{context.weight_scale_8x8};
|
||||
writer.WriteScalingList(matrix, index * 64, 64);
|
||||
}
|
||||
}
|
||||
|
||||
s32 chroma_qp_index_offset2 =
|
||||
static_cast<s32>(context.h264_parameter_set.second_chroma_qp_index_offset.Value());
|
||||
|
||||
writer.WriteSe(chroma_qp_index_offset2);
|
||||
|
||||
writer.End();
|
||||
|
||||
const auto& encoded_header = writer.GetByteArray();
|
||||
frame.resize(encoded_header.size() + context.stream_len);
|
||||
std::memcpy(frame.data(), encoded_header.data(), encoded_header.size());
|
||||
|
||||
gpu.MemoryManager().ReadBlock(state.frame_bitstream_offset,
|
||||
frame.data() + encoded_header.size(), context.stream_len);
|
||||
|
||||
return frame;
|
||||
}
|
||||
|
||||
@@ -202,7 +197,7 @@ void H264BitWriter::WriteBit(bool state) {
|
||||
WriteBits(state ? 1 : 0, 1);
|
||||
}
|
||||
|
||||
void H264BitWriter::WriteScalingList(const std::vector<u8>& list, s32 start, s32 count) {
|
||||
void H264BitWriter::WriteScalingList(std::span<const u8> list, s32 start, s32 count) {
|
||||
std::vector<u8> scan(count);
|
||||
if (count == 16) {
|
||||
std::memcpy(scan.data(), zig_zag_scan.data(), scan.size());
|
||||
|
||||
@@ -20,7 +20,9 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <span>
|
||||
#include <vector>
|
||||
#include "common/bit_field.h"
|
||||
#include "common/common_funcs.h"
|
||||
#include "common/common_types.h"
|
||||
#include "video_core/command_classes/nvdec_common.h"
|
||||
@@ -48,7 +50,7 @@ public:
|
||||
|
||||
/// Based on section 7.3.2.1.1.1 and Table 7-4 in the H.264 specification
|
||||
/// Writes the scaling matrices of the sream
|
||||
void WriteScalingList(const std::vector<u8>& list, s32 start, s32 count);
|
||||
void WriteScalingList(std::span<const u8> list, s32 start, s32 count);
|
||||
|
||||
/// Return the bitstream as a vector.
|
||||
[[nodiscard]] std::vector<u8>& GetByteArray();
|
||||
@@ -78,40 +80,110 @@ public:
|
||||
const NvdecCommon::NvdecRegisters& state, bool is_first_frame = false);
|
||||
|
||||
private:
|
||||
struct H264ParameterSet {
|
||||
u32 log2_max_pic_order_cnt{};
|
||||
u32 delta_pic_order_always_zero_flag{};
|
||||
u32 frame_mbs_only_flag{};
|
||||
u32 pic_width_in_mbs{};
|
||||
u32 pic_height_in_map_units{};
|
||||
INSERT_PADDING_WORDS(1);
|
||||
u32 entropy_coding_mode_flag{};
|
||||
u32 bottom_field_pic_order_flag{};
|
||||
u32 num_refidx_l0_default_active{};
|
||||
u32 num_refidx_l1_default_active{};
|
||||
u32 deblocking_filter_control_flag{};
|
||||
u32 redundant_pic_count_flag{};
|
||||
u32 transform_8x8_mode_flag{};
|
||||
INSERT_PADDING_WORDS(9);
|
||||
u64 flags{};
|
||||
u32 frame_number{};
|
||||
u32 frame_number2{};
|
||||
};
|
||||
static_assert(sizeof(H264ParameterSet) == 0x68, "H264ParameterSet is an invalid size");
|
||||
|
||||
struct H264DecoderContext {
|
||||
INSERT_PADDING_BYTES(0x48);
|
||||
u32 frame_data_size{};
|
||||
INSERT_PADDING_BYTES(0xc);
|
||||
H264ParameterSet h264_parameter_set{};
|
||||
INSERT_PADDING_BYTES(0x100);
|
||||
std::array<u8, 0x60> scaling_matrix_4;
|
||||
std::array<u8, 0x80> scaling_matrix_8;
|
||||
};
|
||||
static_assert(sizeof(H264DecoderContext) == 0x2a0, "H264DecoderContext is an invalid size");
|
||||
|
||||
std::vector<u8> frame;
|
||||
GPU& gpu;
|
||||
|
||||
struct H264ParameterSet {
|
||||
s32 log2_max_pic_order_cnt_lsb_minus4; ///< 0x00
|
||||
s32 delta_pic_order_always_zero_flag; ///< 0x04
|
||||
s32 frame_mbs_only_flag; ///< 0x08
|
||||
u32 pic_width_in_mbs; ///< 0x0C
|
||||
u32 frame_height_in_map_units; ///< 0x10
|
||||
union { ///< 0x14
|
||||
BitField<0, 2, u32> tile_format;
|
||||
BitField<2, 3, u32> gob_height;
|
||||
};
|
||||
u32 entropy_coding_mode_flag; ///< 0x18
|
||||
s32 pic_order_present_flag; ///< 0x1C
|
||||
s32 num_refidx_l0_default_active; ///< 0x20
|
||||
s32 num_refidx_l1_default_active; ///< 0x24
|
||||
s32 deblocking_filter_control_present_flag; ///< 0x28
|
||||
s32 redundant_pic_cnt_present_flag; ///< 0x2C
|
||||
u32 transform_8x8_mode_flag; ///< 0x30
|
||||
u32 pitch_luma; ///< 0x34
|
||||
u32 pitch_chroma; ///< 0x38
|
||||
u32 luma_top_offset; ///< 0x3C
|
||||
u32 luma_bot_offset; ///< 0x40
|
||||
u32 luma_frame_offset; ///< 0x44
|
||||
u32 chroma_top_offset; ///< 0x48
|
||||
u32 chroma_bot_offset; ///< 0x4C
|
||||
u32 chroma_frame_offset; ///< 0x50
|
||||
u32 hist_buffer_size; ///< 0x54
|
||||
union { ///< 0x58
|
||||
union {
|
||||
BitField<0, 1, u64> mbaff_frame;
|
||||
BitField<1, 1, u64> direct_8x8_inference;
|
||||
BitField<2, 1, u64> weighted_pred;
|
||||
BitField<3, 1, u64> constrained_intra_pred;
|
||||
BitField<4, 1, u64> ref_pic;
|
||||
BitField<5, 1, u64> field_pic;
|
||||
BitField<6, 1, u64> bottom_field;
|
||||
BitField<7, 1, u64> second_field;
|
||||
} flags;
|
||||
BitField<8, 4, u64> log2_max_frame_num_minus4;
|
||||
BitField<12, 2, u64> chroma_format_idc;
|
||||
BitField<14, 2, u64> pic_order_cnt_type;
|
||||
BitField<16, 6, s64> pic_init_qp_minus26;
|
||||
BitField<22, 5, s64> chroma_qp_index_offset;
|
||||
BitField<27, 5, s64> second_chroma_qp_index_offset;
|
||||
BitField<32, 2, u64> weighted_bipred_idc;
|
||||
BitField<34, 7, u64> curr_pic_idx;
|
||||
BitField<41, 5, u64> curr_col_idx;
|
||||
BitField<46, 16, u64> frame_number;
|
||||
BitField<62, 1, u64> frame_surfaces;
|
||||
BitField<63, 1, u64> output_memory_layout;
|
||||
};
|
||||
};
|
||||
static_assert(sizeof(H264ParameterSet) == 0x60, "H264ParameterSet is an invalid size");
|
||||
|
||||
struct H264DecoderContext {
|
||||
INSERT_PADDING_WORDS_NOINIT(18); ///< 0x0000
|
||||
u32 stream_len; ///< 0x0048
|
||||
INSERT_PADDING_WORDS_NOINIT(3); ///< 0x004C
|
||||
H264ParameterSet h264_parameter_set; ///< 0x0058
|
||||
INSERT_PADDING_WORDS_NOINIT(66); ///< 0x00B8
|
||||
std::array<u8, 0x60> weight_scale; ///< 0x01C0
|
||||
std::array<u8, 0x80> weight_scale_8x8; ///< 0x0220
|
||||
};
|
||||
static_assert(sizeof(H264DecoderContext) == 0x2A0, "H264DecoderContext is an invalid size");
|
||||
|
||||
#define ASSERT_POSITION(field_name, position) \
|
||||
static_assert(offsetof(H264ParameterSet, field_name) == position, \
|
||||
"Field " #field_name " has invalid position")
|
||||
|
||||
ASSERT_POSITION(log2_max_pic_order_cnt_lsb_minus4, 0x00);
|
||||
ASSERT_POSITION(delta_pic_order_always_zero_flag, 0x04);
|
||||
ASSERT_POSITION(frame_mbs_only_flag, 0x08);
|
||||
ASSERT_POSITION(pic_width_in_mbs, 0x0C);
|
||||
ASSERT_POSITION(frame_height_in_map_units, 0x10);
|
||||
ASSERT_POSITION(tile_format, 0x14);
|
||||
ASSERT_POSITION(entropy_coding_mode_flag, 0x18);
|
||||
ASSERT_POSITION(pic_order_present_flag, 0x1C);
|
||||
ASSERT_POSITION(num_refidx_l0_default_active, 0x20);
|
||||
ASSERT_POSITION(num_refidx_l1_default_active, 0x24);
|
||||
ASSERT_POSITION(deblocking_filter_control_present_flag, 0x28);
|
||||
ASSERT_POSITION(redundant_pic_cnt_present_flag, 0x2C);
|
||||
ASSERT_POSITION(transform_8x8_mode_flag, 0x30);
|
||||
ASSERT_POSITION(pitch_luma, 0x34);
|
||||
ASSERT_POSITION(pitch_chroma, 0x38);
|
||||
ASSERT_POSITION(luma_top_offset, 0x3C);
|
||||
ASSERT_POSITION(luma_bot_offset, 0x40);
|
||||
ASSERT_POSITION(luma_frame_offset, 0x44);
|
||||
ASSERT_POSITION(chroma_top_offset, 0x48);
|
||||
ASSERT_POSITION(chroma_bot_offset, 0x4C);
|
||||
ASSERT_POSITION(chroma_frame_offset, 0x50);
|
||||
ASSERT_POSITION(hist_buffer_size, 0x54);
|
||||
ASSERT_POSITION(flags, 0x58);
|
||||
#undef ASSERT_POSITION
|
||||
|
||||
#define ASSERT_POSITION(field_name, position) \
|
||||
static_assert(offsetof(H264DecoderContext, field_name) == position, \
|
||||
"Field " #field_name " has invalid position")
|
||||
|
||||
ASSERT_POSITION(stream_len, 0x48);
|
||||
ASSERT_POSITION(h264_parameter_set, 0x58);
|
||||
ASSERT_POSITION(weight_scale, 0x1C0);
|
||||
#undef ASSERT_POSITION
|
||||
};
|
||||
|
||||
} // namespace Decoder
|
||||
|
||||
@@ -354,7 +354,7 @@ void VP9::WriteMvProbabilityUpdate(VpxRangeEncoder& writer, u8 new_prob, u8 old_
|
||||
}
|
||||
|
||||
Vp9PictureInfo VP9::GetVp9PictureInfo(const NvdecCommon::NvdecRegisters& state) {
|
||||
PictureInfo picture_info{};
|
||||
PictureInfo picture_info;
|
||||
gpu.MemoryManager().ReadBlock(state.picture_info_offset, &picture_info, sizeof(PictureInfo));
|
||||
Vp9PictureInfo vp9_info = picture_info.Convert();
|
||||
|
||||
@@ -370,7 +370,7 @@ Vp9PictureInfo VP9::GetVp9PictureInfo(const NvdecCommon::NvdecRegisters& state)
|
||||
}
|
||||
|
||||
void VP9::InsertEntropy(u64 offset, Vp9EntropyProbs& dst) {
|
||||
EntropyProbs entropy{};
|
||||
EntropyProbs entropy;
|
||||
gpu.MemoryManager().ReadBlock(offset, &entropy, sizeof(EntropyProbs));
|
||||
entropy.Convert(dst);
|
||||
}
|
||||
|
||||
@@ -15,10 +15,10 @@ class GPU;
|
||||
|
||||
namespace Decoder {
|
||||
struct Vp9FrameDimensions {
|
||||
s16 width{};
|
||||
s16 height{};
|
||||
s16 luma_pitch{};
|
||||
s16 chroma_pitch{};
|
||||
s16 width;
|
||||
s16 height;
|
||||
s16 luma_pitch;
|
||||
s16 chroma_pitch;
|
||||
};
|
||||
static_assert(sizeof(Vp9FrameDimensions) == 0x8, "Vp9 Vp9FrameDimensions is an invalid size");
|
||||
|
||||
@@ -49,87 +49,87 @@ enum class TxMode {
|
||||
};
|
||||
|
||||
struct Segmentation {
|
||||
u8 enabled{};
|
||||
u8 update_map{};
|
||||
u8 temporal_update{};
|
||||
u8 abs_delta{};
|
||||
std::array<u32, 8> feature_mask{};
|
||||
std::array<std::array<s16, 4>, 8> feature_data{};
|
||||
u8 enabled;
|
||||
u8 update_map;
|
||||
u8 temporal_update;
|
||||
u8 abs_delta;
|
||||
std::array<u32, 8> feature_mask;
|
||||
std::array<std::array<s16, 4>, 8> feature_data;
|
||||
};
|
||||
static_assert(sizeof(Segmentation) == 0x64, "Segmentation is an invalid size");
|
||||
|
||||
struct LoopFilter {
|
||||
u8 mode_ref_delta_enabled{};
|
||||
std::array<s8, 4> ref_deltas{};
|
||||
std::array<s8, 2> mode_deltas{};
|
||||
u8 mode_ref_delta_enabled;
|
||||
std::array<s8, 4> ref_deltas;
|
||||
std::array<s8, 2> mode_deltas;
|
||||
};
|
||||
static_assert(sizeof(LoopFilter) == 0x7, "LoopFilter is an invalid size");
|
||||
|
||||
struct Vp9EntropyProbs {
|
||||
std::array<u8, 36> y_mode_prob{};
|
||||
std::array<u8, 64> partition_prob{};
|
||||
std::array<u8, 1728> coef_probs{};
|
||||
std::array<u8, 8> switchable_interp_prob{};
|
||||
std::array<u8, 28> inter_mode_prob{};
|
||||
std::array<u8, 4> intra_inter_prob{};
|
||||
std::array<u8, 5> comp_inter_prob{};
|
||||
std::array<u8, 10> single_ref_prob{};
|
||||
std::array<u8, 5> comp_ref_prob{};
|
||||
std::array<u8, 6> tx_32x32_prob{};
|
||||
std::array<u8, 4> tx_16x16_prob{};
|
||||
std::array<u8, 2> tx_8x8_prob{};
|
||||
std::array<u8, 3> skip_probs{};
|
||||
std::array<u8, 3> joints{};
|
||||
std::array<u8, 2> sign{};
|
||||
std::array<u8, 20> classes{};
|
||||
std::array<u8, 2> class_0{};
|
||||
std::array<u8, 20> prob_bits{};
|
||||
std::array<u8, 12> class_0_fr{};
|
||||
std::array<u8, 6> fr{};
|
||||
std::array<u8, 2> class_0_hp{};
|
||||
std::array<u8, 2> high_precision{};
|
||||
std::array<u8, 36> y_mode_prob; ///< 0x0000
|
||||
std::array<u8, 64> partition_prob; ///< 0x0024
|
||||
std::array<u8, 1728> coef_probs; ///< 0x0064
|
||||
std::array<u8, 8> switchable_interp_prob; ///< 0x0724
|
||||
std::array<u8, 28> inter_mode_prob; ///< 0x072C
|
||||
std::array<u8, 4> intra_inter_prob; ///< 0x0748
|
||||
std::array<u8, 5> comp_inter_prob; ///< 0x074C
|
||||
std::array<u8, 10> single_ref_prob; ///< 0x0751
|
||||
std::array<u8, 5> comp_ref_prob; ///< 0x075B
|
||||
std::array<u8, 6> tx_32x32_prob; ///< 0x0760
|
||||
std::array<u8, 4> tx_16x16_prob; ///< 0x0766
|
||||
std::array<u8, 2> tx_8x8_prob; ///< 0x076A
|
||||
std::array<u8, 3> skip_probs; ///< 0x076C
|
||||
std::array<u8, 3> joints; ///< 0x076F
|
||||
std::array<u8, 2> sign; ///< 0x0772
|
||||
std::array<u8, 20> classes; ///< 0x0774
|
||||
std::array<u8, 2> class_0; ///< 0x0788
|
||||
std::array<u8, 20> prob_bits; ///< 0x078A
|
||||
std::array<u8, 12> class_0_fr; ///< 0x079E
|
||||
std::array<u8, 6> fr; ///< 0x07AA
|
||||
std::array<u8, 2> class_0_hp; ///< 0x07B0
|
||||
std::array<u8, 2> high_precision; ///< 0x07B2
|
||||
};
|
||||
static_assert(sizeof(Vp9EntropyProbs) == 0x7B4, "Vp9EntropyProbs is an invalid size");
|
||||
|
||||
struct Vp9PictureInfo {
|
||||
bool is_key_frame{};
|
||||
bool intra_only{};
|
||||
bool last_frame_was_key{};
|
||||
bool frame_size_changed{};
|
||||
bool error_resilient_mode{};
|
||||
bool last_frame_shown{};
|
||||
bool show_frame{};
|
||||
std::array<s8, 4> ref_frame_sign_bias{};
|
||||
s32 base_q_index{};
|
||||
s32 y_dc_delta_q{};
|
||||
s32 uv_dc_delta_q{};
|
||||
s32 uv_ac_delta_q{};
|
||||
bool lossless{};
|
||||
s32 transform_mode{};
|
||||
bool allow_high_precision_mv{};
|
||||
s32 interp_filter{};
|
||||
s32 reference_mode{};
|
||||
s8 comp_fixed_ref{};
|
||||
std::array<s8, 2> comp_var_ref{};
|
||||
s32 log2_tile_cols{};
|
||||
s32 log2_tile_rows{};
|
||||
bool segment_enabled{};
|
||||
bool segment_map_update{};
|
||||
bool segment_map_temporal_update{};
|
||||
s32 segment_abs_delta{};
|
||||
std::array<u32, 8> segment_feature_enable{};
|
||||
std::array<std::array<s16, 4>, 8> segment_feature_data{};
|
||||
bool mode_ref_delta_enabled{};
|
||||
bool use_prev_in_find_mv_refs{};
|
||||
std::array<s8, 4> ref_deltas{};
|
||||
std::array<s8, 2> mode_deltas{};
|
||||
Vp9EntropyProbs entropy{};
|
||||
Vp9FrameDimensions frame_size{};
|
||||
u8 first_level{};
|
||||
u8 sharpness_level{};
|
||||
u32 bitstream_size{};
|
||||
std::array<u64, 4> frame_offsets{};
|
||||
std::array<bool, 4> refresh_frame{};
|
||||
bool is_key_frame;
|
||||
bool intra_only;
|
||||
bool last_frame_was_key;
|
||||
bool frame_size_changed;
|
||||
bool error_resilient_mode;
|
||||
bool last_frame_shown;
|
||||
bool show_frame;
|
||||
std::array<s8, 4> ref_frame_sign_bias;
|
||||
s32 base_q_index;
|
||||
s32 y_dc_delta_q;
|
||||
s32 uv_dc_delta_q;
|
||||
s32 uv_ac_delta_q;
|
||||
bool lossless;
|
||||
s32 transform_mode;
|
||||
bool allow_high_precision_mv;
|
||||
s32 interp_filter;
|
||||
s32 reference_mode;
|
||||
s8 comp_fixed_ref;
|
||||
std::array<s8, 2> comp_var_ref;
|
||||
s32 log2_tile_cols;
|
||||
s32 log2_tile_rows;
|
||||
bool segment_enabled;
|
||||
bool segment_map_update;
|
||||
bool segment_map_temporal_update;
|
||||
s32 segment_abs_delta;
|
||||
std::array<u32, 8> segment_feature_enable;
|
||||
std::array<std::array<s16, 4>, 8> segment_feature_data;
|
||||
bool mode_ref_delta_enabled;
|
||||
bool use_prev_in_find_mv_refs;
|
||||
std::array<s8, 4> ref_deltas;
|
||||
std::array<s8, 2> mode_deltas;
|
||||
Vp9EntropyProbs entropy;
|
||||
Vp9FrameDimensions frame_size;
|
||||
u8 first_level;
|
||||
u8 sharpness_level;
|
||||
u32 bitstream_size;
|
||||
std::array<u64, 4> frame_offsets;
|
||||
std::array<bool, 4> refresh_frame;
|
||||
};
|
||||
|
||||
struct Vp9FrameContainer {
|
||||
@@ -138,35 +138,35 @@ struct Vp9FrameContainer {
|
||||
};
|
||||
|
||||
struct PictureInfo {
|
||||
INSERT_PADDING_WORDS(12);
|
||||
u32 bitstream_size{};
|
||||
INSERT_PADDING_WORDS(5);
|
||||
Vp9FrameDimensions last_frame_size{};
|
||||
Vp9FrameDimensions golden_frame_size{};
|
||||
Vp9FrameDimensions alt_frame_size{};
|
||||
Vp9FrameDimensions current_frame_size{};
|
||||
u32 vp9_flags{};
|
||||
std::array<s8, 4> ref_frame_sign_bias{};
|
||||
u8 first_level{};
|
||||
u8 sharpness_level{};
|
||||
u8 base_q_index{};
|
||||
u8 y_dc_delta_q{};
|
||||
u8 uv_ac_delta_q{};
|
||||
u8 uv_dc_delta_q{};
|
||||
u8 lossless{};
|
||||
u8 tx_mode{};
|
||||
u8 allow_high_precision_mv{};
|
||||
u8 interp_filter{};
|
||||
u8 reference_mode{};
|
||||
s8 comp_fixed_ref{};
|
||||
std::array<s8, 2> comp_var_ref{};
|
||||
u8 log2_tile_cols{};
|
||||
u8 log2_tile_rows{};
|
||||
Segmentation segmentation{};
|
||||
LoopFilter loop_filter{};
|
||||
INSERT_PADDING_BYTES(5);
|
||||
u32 surface_params{};
|
||||
INSERT_PADDING_WORDS(3);
|
||||
INSERT_PADDING_WORDS_NOINIT(12); ///< 0x00
|
||||
u32 bitstream_size; ///< 0x30
|
||||
INSERT_PADDING_WORDS_NOINIT(5); ///< 0x34
|
||||
Vp9FrameDimensions last_frame_size; ///< 0x48
|
||||
Vp9FrameDimensions golden_frame_size; ///< 0x50
|
||||
Vp9FrameDimensions alt_frame_size; ///< 0x58
|
||||
Vp9FrameDimensions current_frame_size; ///< 0x60
|
||||
u32 vp9_flags; ///< 0x68
|
||||
std::array<s8, 4> ref_frame_sign_bias; ///< 0x6C
|
||||
u8 first_level; ///< 0x70
|
||||
u8 sharpness_level; ///< 0x71
|
||||
u8 base_q_index; ///< 0x72
|
||||
u8 y_dc_delta_q; ///< 0x73
|
||||
u8 uv_ac_delta_q; ///< 0x74
|
||||
u8 uv_dc_delta_q; ///< 0x75
|
||||
u8 lossless; ///< 0x76
|
||||
u8 tx_mode; ///< 0x77
|
||||
u8 allow_high_precision_mv; ///< 0x78
|
||||
u8 interp_filter; ///< 0x79
|
||||
u8 reference_mode; ///< 0x7A
|
||||
s8 comp_fixed_ref; ///< 0x7B
|
||||
std::array<s8, 2> comp_var_ref; ///< 0x7C
|
||||
u8 log2_tile_cols; ///< 0x7E
|
||||
u8 log2_tile_rows; ///< 0x7F
|
||||
Segmentation segmentation; ///< 0x80
|
||||
LoopFilter loop_filter; ///< 0xE4
|
||||
INSERT_PADDING_BYTES_NOINIT(5); ///< 0xEB
|
||||
u32 surface_params; ///< 0xF0
|
||||
INSERT_PADDING_WORDS_NOINIT(3); ///< 0xF4
|
||||
|
||||
[[nodiscard]] Vp9PictureInfo Convert() const {
|
||||
return {
|
||||
@@ -176,6 +176,7 @@ struct PictureInfo {
|
||||
.frame_size_changed = (vp9_flags & FrameFlags::FrameSizeChanged) != 0,
|
||||
.error_resilient_mode = (vp9_flags & FrameFlags::ErrorResilientMode) != 0,
|
||||
.last_frame_shown = (vp9_flags & FrameFlags::LastShowFrame) != 0,
|
||||
.show_frame = false,
|
||||
.ref_frame_sign_bias = ref_frame_sign_bias,
|
||||
.base_q_index = base_q_index,
|
||||
.y_dc_delta_q = y_dc_delta_q,
|
||||
@@ -204,45 +205,48 @@ struct PictureInfo {
|
||||
!(vp9_flags == (FrameFlags::LastFrameIsKeyFrame)),
|
||||
.ref_deltas = loop_filter.ref_deltas,
|
||||
.mode_deltas = loop_filter.mode_deltas,
|
||||
.entropy{},
|
||||
.frame_size = current_frame_size,
|
||||
.first_level = first_level,
|
||||
.sharpness_level = sharpness_level,
|
||||
.bitstream_size = bitstream_size,
|
||||
.frame_offsets{},
|
||||
.refresh_frame{},
|
||||
};
|
||||
}
|
||||
};
|
||||
static_assert(sizeof(PictureInfo) == 0x100, "PictureInfo is an invalid size");
|
||||
|
||||
struct EntropyProbs {
|
||||
INSERT_PADDING_BYTES(1024);
|
||||
std::array<u8, 28> inter_mode_prob{};
|
||||
std::array<u8, 4> intra_inter_prob{};
|
||||
INSERT_PADDING_BYTES(80);
|
||||
std::array<u8, 2> tx_8x8_prob{};
|
||||
std::array<u8, 4> tx_16x16_prob{};
|
||||
std::array<u8, 6> tx_32x32_prob{};
|
||||
std::array<u8, 4> y_mode_prob_e8{};
|
||||
std::array<std::array<u8, 8>, 4> y_mode_prob_e0e7{};
|
||||
INSERT_PADDING_BYTES(64);
|
||||
std::array<u8, 64> partition_prob{};
|
||||
INSERT_PADDING_BYTES(10);
|
||||
std::array<u8, 8> switchable_interp_prob{};
|
||||
std::array<u8, 5> comp_inter_prob{};
|
||||
std::array<u8, 3> skip_probs{};
|
||||
INSERT_PADDING_BYTES(1);
|
||||
std::array<u8, 3> joints{};
|
||||
std::array<u8, 2> sign{};
|
||||
std::array<u8, 2> class_0{};
|
||||
std::array<u8, 6> fr{};
|
||||
std::array<u8, 2> class_0_hp{};
|
||||
std::array<u8, 2> high_precision{};
|
||||
std::array<u8, 20> classes{};
|
||||
std::array<u8, 12> class_0_fr{};
|
||||
std::array<u8, 20> pred_bits{};
|
||||
std::array<u8, 10> single_ref_prob{};
|
||||
std::array<u8, 5> comp_ref_prob{};
|
||||
INSERT_PADDING_BYTES(17);
|
||||
std::array<u8, 2304> coef_probs{};
|
||||
INSERT_PADDING_BYTES_NOINIT(1024); ///< 0x0000
|
||||
std::array<u8, 28> inter_mode_prob; ///< 0x0400
|
||||
std::array<u8, 4> intra_inter_prob; ///< 0x041C
|
||||
INSERT_PADDING_BYTES_NOINIT(80); ///< 0x0420
|
||||
std::array<u8, 2> tx_8x8_prob; ///< 0x0470
|
||||
std::array<u8, 4> tx_16x16_prob; ///< 0x0472
|
||||
std::array<u8, 6> tx_32x32_prob; ///< 0x0476
|
||||
std::array<u8, 4> y_mode_prob_e8; ///< 0x047C
|
||||
std::array<std::array<u8, 8>, 4> y_mode_prob_e0e7; ///< 0x0480
|
||||
INSERT_PADDING_BYTES_NOINIT(64); ///< 0x04A0
|
||||
std::array<u8, 64> partition_prob; ///< 0x04E0
|
||||
INSERT_PADDING_BYTES_NOINIT(10); ///< 0x0520
|
||||
std::array<u8, 8> switchable_interp_prob; ///< 0x052A
|
||||
std::array<u8, 5> comp_inter_prob; ///< 0x0532
|
||||
std::array<u8, 3> skip_probs; ///< 0x0537
|
||||
INSERT_PADDING_BYTES_NOINIT(1); ///< 0x053A
|
||||
std::array<u8, 3> joints; ///< 0x053B
|
||||
std::array<u8, 2> sign; ///< 0x053E
|
||||
std::array<u8, 2> class_0; ///< 0x0540
|
||||
std::array<u8, 6> fr; ///< 0x0542
|
||||
std::array<u8, 2> class_0_hp; ///< 0x0548
|
||||
std::array<u8, 2> high_precision; ///< 0x054A
|
||||
std::array<u8, 20> classes; ///< 0x054C
|
||||
std::array<u8, 12> class_0_fr; ///< 0x0560
|
||||
std::array<u8, 20> pred_bits; ///< 0x056C
|
||||
std::array<u8, 10> single_ref_prob; ///< 0x0580
|
||||
std::array<u8, 5> comp_ref_prob; ///< 0x058A
|
||||
INSERT_PADDING_BYTES_NOINIT(17); ///< 0x058F
|
||||
std::array<u8, 2304> coef_probs; ///< 0x05A0
|
||||
|
||||
void Convert(Vp9EntropyProbs& fc) {
|
||||
fc.inter_mode_prob = inter_mode_prob;
|
||||
@@ -293,10 +297,45 @@ struct RefPoolElement {
|
||||
};
|
||||
|
||||
struct FrameContexts {
|
||||
s64 from{};
|
||||
bool adapted{};
|
||||
Vp9EntropyProbs probs{};
|
||||
s64 from;
|
||||
bool adapted;
|
||||
Vp9EntropyProbs probs;
|
||||
};
|
||||
|
||||
#define ASSERT_POSITION(field_name, position) \
|
||||
static_assert(offsetof(Vp9EntropyProbs, field_name) == position, \
|
||||
"Field " #field_name " has invalid position")
|
||||
|
||||
ASSERT_POSITION(partition_prob, 0x0024);
|
||||
ASSERT_POSITION(switchable_interp_prob, 0x0724);
|
||||
ASSERT_POSITION(sign, 0x0772);
|
||||
ASSERT_POSITION(class_0_fr, 0x079E);
|
||||
ASSERT_POSITION(high_precision, 0x07B2);
|
||||
#undef ASSERT_POSITION
|
||||
|
||||
#define ASSERT_POSITION(field_name, position) \
|
||||
static_assert(offsetof(PictureInfo, field_name) == position, \
|
||||
"Field " #field_name " has invalid position")
|
||||
|
||||
ASSERT_POSITION(bitstream_size, 0x30);
|
||||
ASSERT_POSITION(last_frame_size, 0x48);
|
||||
ASSERT_POSITION(first_level, 0x70);
|
||||
ASSERT_POSITION(segmentation, 0x80);
|
||||
ASSERT_POSITION(loop_filter, 0xE4);
|
||||
ASSERT_POSITION(surface_params, 0xF0);
|
||||
#undef ASSERT_POSITION
|
||||
|
||||
#define ASSERT_POSITION(field_name, position) \
|
||||
static_assert(offsetof(EntropyProbs, field_name) == position, \
|
||||
"Field " #field_name " has invalid position")
|
||||
|
||||
ASSERT_POSITION(inter_mode_prob, 0x400);
|
||||
ASSERT_POSITION(tx_8x8_prob, 0x470);
|
||||
ASSERT_POSITION(partition_prob, 0x4E0);
|
||||
ASSERT_POSITION(class_0, 0x540);
|
||||
ASSERT_POSITION(class_0_fr, 0x560);
|
||||
ASSERT_POSITION(coef_probs, 0x5A0);
|
||||
#undef ASSERT_POSITION
|
||||
|
||||
}; // namespace Decoder
|
||||
}; // namespace Tegra
|
||||
|
||||
@@ -8,22 +8,21 @@
|
||||
|
||||
namespace Tegra {
|
||||
|
||||
Nvdec::Nvdec(GPU& gpu_) : gpu(gpu_), codec(std::make_unique<Codec>(gpu)) {}
|
||||
#define NVDEC_REG_INDEX(field_name) \
|
||||
(offsetof(NvdecCommon::NvdecRegisters, field_name) / sizeof(u64))
|
||||
|
||||
Nvdec::Nvdec(GPU& gpu_) : gpu(gpu_), state{}, codec(std::make_unique<Codec>(gpu, state)) {}
|
||||
|
||||
Nvdec::~Nvdec() = default;
|
||||
|
||||
void Nvdec::ProcessMethod(Method method, u32 argument) {
|
||||
if (method == Method::SetVideoCodec) {
|
||||
codec->StateWrite(static_cast<u32>(method), argument);
|
||||
} else {
|
||||
codec->StateWrite(static_cast<u32>(method), static_cast<u64>(argument) << 8);
|
||||
}
|
||||
void Nvdec::ProcessMethod(u32 method, u32 argument) {
|
||||
state.reg_array[method] = static_cast<u64>(argument) << 8;
|
||||
|
||||
switch (method) {
|
||||
case Method::SetVideoCodec:
|
||||
case NVDEC_REG_INDEX(set_codec_id):
|
||||
codec->SetTargetCodec(static_cast<NvdecCommon::VideoCodec>(argument));
|
||||
break;
|
||||
case Method::Execute:
|
||||
case NVDEC_REG_INDEX(execute):
|
||||
Execute();
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -14,16 +14,11 @@ class GPU;
|
||||
|
||||
class Nvdec {
|
||||
public:
|
||||
enum class Method : u32 {
|
||||
SetVideoCodec = 0x80,
|
||||
Execute = 0xc0,
|
||||
};
|
||||
|
||||
explicit Nvdec(GPU& gpu);
|
||||
~Nvdec();
|
||||
|
||||
/// Writes the method into the state, Invoke Execute() if encountered
|
||||
void ProcessMethod(Method method, u32 argument);
|
||||
void ProcessMethod(u32 method, u32 argument);
|
||||
|
||||
/// Return most recently decoded frame
|
||||
[[nodiscard]] AVFramePtr GetFrame();
|
||||
@@ -33,6 +28,7 @@ private:
|
||||
void Execute();
|
||||
|
||||
GPU& gpu;
|
||||
NvdecCommon::NvdecRegisters state;
|
||||
std::unique_ptr<Codec> codec;
|
||||
};
|
||||
} // namespace Tegra
|
||||
|
||||
@@ -4,40 +4,13 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/bit_field.h"
|
||||
#include "common/common_funcs.h"
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace Tegra::NvdecCommon {
|
||||
|
||||
struct NvdecRegisters {
|
||||
INSERT_PADDING_WORDS(256);
|
||||
u64 set_codec_id{};
|
||||
INSERT_PADDING_WORDS(254);
|
||||
u64 set_platform_id{};
|
||||
u64 picture_info_offset{};
|
||||
u64 frame_bitstream_offset{};
|
||||
u64 frame_number{};
|
||||
u64 h264_slice_data_offsets{};
|
||||
u64 h264_mv_dump_offset{};
|
||||
INSERT_PADDING_WORDS(6);
|
||||
u64 frame_stats_offset{};
|
||||
u64 h264_last_surface_luma_offset{};
|
||||
u64 h264_last_surface_chroma_offset{};
|
||||
std::array<u64, 17> surface_luma_offset{};
|
||||
std::array<u64, 17> surface_chroma_offset{};
|
||||
INSERT_PADDING_WORDS(132);
|
||||
u64 vp9_entropy_probs_offset{};
|
||||
u64 vp9_backward_updates_offset{};
|
||||
u64 vp9_last_frame_segmap_offset{};
|
||||
u64 vp9_curr_frame_segmap_offset{};
|
||||
INSERT_PADDING_WORDS(2);
|
||||
u64 vp9_last_frame_mvs_offset{};
|
||||
u64 vp9_curr_frame_mvs_offset{};
|
||||
INSERT_PADDING_WORDS(2);
|
||||
};
|
||||
static_assert(sizeof(NvdecRegisters) == (0xBC0), "NvdecRegisters is incorrect size");
|
||||
|
||||
enum class VideoCodec : u32 {
|
||||
enum class VideoCodec : u64 {
|
||||
None = 0x0,
|
||||
H264 = 0x3,
|
||||
Vp8 = 0x5,
|
||||
@@ -45,4 +18,76 @@ enum class VideoCodec : u32 {
|
||||
Vp9 = 0x9,
|
||||
};
|
||||
|
||||
// NVDEC should use a 32-bit address space, but is mapped to 64-bit,
|
||||
// doubling the sizes here is compensating for that.
|
||||
struct NvdecRegisters {
|
||||
static constexpr std::size_t NUM_REGS = 0x178;
|
||||
|
||||
union {
|
||||
struct {
|
||||
INSERT_PADDING_WORDS_NOINIT(256); ///< 0x0000
|
||||
VideoCodec set_codec_id; ///< 0x0400
|
||||
INSERT_PADDING_WORDS_NOINIT(126); ///< 0x0408
|
||||
u64 execute; ///< 0x0600
|
||||
INSERT_PADDING_WORDS_NOINIT(126); ///< 0x0608
|
||||
struct { ///< 0x0800
|
||||
union {
|
||||
BitField<0, 3, VideoCodec> codec;
|
||||
BitField<4, 1, u64> gp_timer_on;
|
||||
BitField<13, 1, u64> mb_timer_on;
|
||||
BitField<14, 1, u64> intra_frame_pslc;
|
||||
BitField<17, 1, u64> all_intra_frame;
|
||||
};
|
||||
} control_params;
|
||||
u64 picture_info_offset; ///< 0x0808
|
||||
u64 frame_bitstream_offset; ///< 0x0810
|
||||
u64 frame_number; ///< 0x0818
|
||||
u64 h264_slice_data_offsets; ///< 0x0820
|
||||
u64 h264_mv_dump_offset; ///< 0x0828
|
||||
INSERT_PADDING_WORDS_NOINIT(6); ///< 0x0830
|
||||
u64 frame_stats_offset; ///< 0x0848
|
||||
u64 h264_last_surface_luma_offset; ///< 0x0850
|
||||
u64 h264_last_surface_chroma_offset; ///< 0x0858
|
||||
std::array<u64, 17> surface_luma_offset; ///< 0x0860
|
||||
std::array<u64, 17> surface_chroma_offset; ///< 0x08E8
|
||||
INSERT_PADDING_WORDS_NOINIT(132); ///< 0x0970
|
||||
u64 vp9_entropy_probs_offset; ///< 0x0B80
|
||||
u64 vp9_backward_updates_offset; ///< 0x0B88
|
||||
u64 vp9_last_frame_segmap_offset; ///< 0x0B90
|
||||
u64 vp9_curr_frame_segmap_offset; ///< 0x0B98
|
||||
INSERT_PADDING_WORDS_NOINIT(2); ///< 0x0BA0
|
||||
u64 vp9_last_frame_mvs_offset; ///< 0x0BA8
|
||||
u64 vp9_curr_frame_mvs_offset; ///< 0x0BB0
|
||||
INSERT_PADDING_WORDS_NOINIT(2); ///< 0x0BB8
|
||||
};
|
||||
std::array<u64, NUM_REGS> reg_array;
|
||||
};
|
||||
};
|
||||
static_assert(sizeof(NvdecRegisters) == (0xBC0), "NvdecRegisters is incorrect size");
|
||||
|
||||
#define ASSERT_REG_POSITION(field_name, position) \
|
||||
static_assert(offsetof(NvdecRegisters, field_name) == position * sizeof(u64), \
|
||||
"Field " #field_name " has invalid position")
|
||||
|
||||
ASSERT_REG_POSITION(set_codec_id, 0x80);
|
||||
ASSERT_REG_POSITION(execute, 0xC0);
|
||||
ASSERT_REG_POSITION(control_params, 0x100);
|
||||
ASSERT_REG_POSITION(picture_info_offset, 0x101);
|
||||
ASSERT_REG_POSITION(frame_bitstream_offset, 0x102);
|
||||
ASSERT_REG_POSITION(frame_number, 0x103);
|
||||
ASSERT_REG_POSITION(h264_slice_data_offsets, 0x104);
|
||||
ASSERT_REG_POSITION(frame_stats_offset, 0x109);
|
||||
ASSERT_REG_POSITION(h264_last_surface_luma_offset, 0x10A);
|
||||
ASSERT_REG_POSITION(h264_last_surface_chroma_offset, 0x10B);
|
||||
ASSERT_REG_POSITION(surface_luma_offset, 0x10C);
|
||||
ASSERT_REG_POSITION(surface_chroma_offset, 0x11D);
|
||||
ASSERT_REG_POSITION(vp9_entropy_probs_offset, 0x170);
|
||||
ASSERT_REG_POSITION(vp9_backward_updates_offset, 0x171);
|
||||
ASSERT_REG_POSITION(vp9_last_frame_segmap_offset, 0x172);
|
||||
ASSERT_REG_POSITION(vp9_curr_frame_segmap_offset, 0x173);
|
||||
ASSERT_REG_POSITION(vp9_last_frame_mvs_offset, 0x175);
|
||||
ASSERT_REG_POSITION(vp9_curr_frame_mvs_offset, 0x176);
|
||||
|
||||
#undef ASSERT_REG_POSITION
|
||||
|
||||
} // namespace Tegra::NvdecCommon
|
||||
|
||||
@@ -9,17 +9,10 @@ extern "C" {
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wconversion"
|
||||
#endif
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(disable : 4244) // conversion from 'type' to 'type', possible loss of data
|
||||
#pragma warning(push)
|
||||
#endif
|
||||
#include <libswscale/swscale.h>
|
||||
#if defined(__GNUC__) || defined(__clang__)
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(pop)
|
||||
#endif
|
||||
}
|
||||
|
||||
#include "common/assert.h"
|
||||
|
||||
@@ -7,6 +7,10 @@
|
||||
#include "video_core/engines/fermi_2d.h"
|
||||
#include "video_core/memory_manager.h"
|
||||
#include "video_core/rasterizer_interface.h"
|
||||
#include "video_core/surface.h"
|
||||
|
||||
using VideoCore::Surface::BytesPerBlock;
|
||||
using VideoCore::Surface::PixelFormatFromRenderTargetFormat;
|
||||
|
||||
namespace Tegra::Engines {
|
||||
|
||||
@@ -49,7 +53,7 @@ void Fermi2D::Blit() {
|
||||
UNIMPLEMENTED_IF_MSG(regs.clip_enable != 0, "Clipped blit enabled");
|
||||
|
||||
const auto& args = regs.pixels_from_memory;
|
||||
const Config config{
|
||||
Config config{
|
||||
.operation = regs.operation,
|
||||
.filter = args.sample_mode.filter,
|
||||
.dst_x0 = args.dst_x0,
|
||||
@@ -61,7 +65,21 @@ void Fermi2D::Blit() {
|
||||
.src_x1 = static_cast<s32>((args.du_dx * args.dst_width + args.src_x0) >> 32),
|
||||
.src_y1 = static_cast<s32>((args.dv_dy * args.dst_height + args.src_y0) >> 32),
|
||||
};
|
||||
if (!rasterizer->AccelerateSurfaceCopy(regs.src, regs.dst, config)) {
|
||||
Surface src = regs.src;
|
||||
const auto bytes_per_pixel = BytesPerBlock(PixelFormatFromRenderTargetFormat(src.format));
|
||||
const auto need_align_to_pitch =
|
||||
src.linear == Tegra::Engines::Fermi2D::MemoryLayout::Pitch &&
|
||||
static_cast<s32>(src.width) == config.src_x1 &&
|
||||
config.src_x1 > static_cast<s32>(src.pitch / bytes_per_pixel) && config.src_x0 > 0;
|
||||
if (need_align_to_pitch) {
|
||||
auto address = src.Address() + config.src_x0 * bytes_per_pixel;
|
||||
src.addr_upper = static_cast<u32>(address >> 32);
|
||||
src.addr_lower = static_cast<u32>(address);
|
||||
src.width -= config.src_x0;
|
||||
config.src_x1 -= config.src_x0;
|
||||
config.src_x0 = 0;
|
||||
}
|
||||
if (!rasterizer->AccelerateSurfaceCopy(src, regs.dst, config)) {
|
||||
UNIMPLEMENTED();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -69,11 +69,16 @@ void MemoryManager::Unmap(GPUVAddr gpu_addr, std::size_t size) {
|
||||
} else {
|
||||
UNREACHABLE_MSG("Unmapping non-existent GPU address=0x{:x}", gpu_addr);
|
||||
}
|
||||
// Flush and invalidate through the GPU interface, to be asynchronous if possible.
|
||||
const std::optional<VAddr> cpu_addr = GpuToCpuAddress(gpu_addr);
|
||||
ASSERT(cpu_addr);
|
||||
|
||||
rasterizer->UnmapMemory(*cpu_addr, size);
|
||||
const auto submapped_ranges = GetSubmappedRange(gpu_addr, size);
|
||||
|
||||
for (const auto& map : submapped_ranges) {
|
||||
// Flush and invalidate through the GPU interface, to be asynchronous if possible.
|
||||
const std::optional<VAddr> cpu_addr = GpuToCpuAddress(map.first);
|
||||
ASSERT(cpu_addr);
|
||||
|
||||
rasterizer->UnmapMemory(*cpu_addr, map.second);
|
||||
}
|
||||
|
||||
UpdateRange(gpu_addr, PageEntry::State::Unmapped, size);
|
||||
}
|
||||
@@ -127,8 +132,14 @@ void MemoryManager::SetPageEntry(GPUVAddr gpu_addr, PageEntry page_entry, std::s
|
||||
|
||||
//// Lock the new page
|
||||
// TryLockPage(page_entry, size);
|
||||
auto& current_page = page_table[PageEntryIndex(gpu_addr)];
|
||||
|
||||
page_table[PageEntryIndex(gpu_addr)] = page_entry;
|
||||
if ((!current_page.IsValid() && page_entry.IsValid()) ||
|
||||
current_page.ToAddress() != page_entry.ToAddress()) {
|
||||
rasterizer->ModifyGPUMemory(gpu_addr, size);
|
||||
}
|
||||
|
||||
current_page = page_entry;
|
||||
}
|
||||
|
||||
std::optional<GPUVAddr> MemoryManager::FindFreeRange(std::size_t size, std::size_t align,
|
||||
@@ -174,6 +185,19 @@ std::optional<VAddr> MemoryManager::GpuToCpuAddress(GPUVAddr gpu_addr) const {
|
||||
return page_entry.ToAddress() + (gpu_addr & page_mask);
|
||||
}
|
||||
|
||||
std::optional<VAddr> MemoryManager::GpuToCpuAddress(GPUVAddr addr, std::size_t size) const {
|
||||
size_t page_index{addr >> page_bits};
|
||||
const size_t page_last{(addr + size + page_size - 1) >> page_bits};
|
||||
while (page_index < page_last) {
|
||||
const auto page_addr{GpuToCpuAddress(page_index << page_bits)};
|
||||
if (page_addr && *page_addr != 0) {
|
||||
return page_addr;
|
||||
}
|
||||
++page_index;
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T MemoryManager::Read(GPUVAddr addr) const {
|
||||
if (auto page_pointer{GetPointer(addr)}; page_pointer) {
|
||||
@@ -370,4 +394,79 @@ bool MemoryManager::IsGranularRange(GPUVAddr gpu_addr, std::size_t size) const {
|
||||
return page <= Core::Memory::PAGE_SIZE;
|
||||
}
|
||||
|
||||
bool MemoryManager::IsContinousRange(GPUVAddr gpu_addr, std::size_t size) const {
|
||||
size_t page_index{gpu_addr >> page_bits};
|
||||
const size_t page_last{(gpu_addr + size + page_size - 1) >> page_bits};
|
||||
std::optional<VAddr> old_page_addr{};
|
||||
while (page_index != page_last) {
|
||||
const auto page_addr{GpuToCpuAddress(page_index << page_bits)};
|
||||
if (!page_addr || *page_addr == 0) {
|
||||
return false;
|
||||
}
|
||||
if (old_page_addr) {
|
||||
if (*old_page_addr + page_size != *page_addr) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
old_page_addr = page_addr;
|
||||
++page_index;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MemoryManager::IsFullyMappedRange(GPUVAddr gpu_addr, std::size_t size) const {
|
||||
size_t page_index{gpu_addr >> page_bits};
|
||||
const size_t page_last{(gpu_addr + size + page_size - 1) >> page_bits};
|
||||
while (page_index < page_last) {
|
||||
if (!page_table[page_index].IsValid() || page_table[page_index].ToAddress() == 0) {
|
||||
return false;
|
||||
}
|
||||
++page_index;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
std::vector<std::pair<GPUVAddr, std::size_t>> MemoryManager::GetSubmappedRange(
|
||||
GPUVAddr gpu_addr, std::size_t size) const {
|
||||
std::vector<std::pair<GPUVAddr, std::size_t>> result{};
|
||||
size_t page_index{gpu_addr >> page_bits};
|
||||
size_t remaining_size{size};
|
||||
size_t page_offset{gpu_addr & page_mask};
|
||||
std::optional<std::pair<GPUVAddr, std::size_t>> last_segment{};
|
||||
std::optional<VAddr> old_page_addr{};
|
||||
const auto extend_size = [this, &last_segment, &page_index](std::size_t bytes) {
|
||||
if (!last_segment) {
|
||||
GPUVAddr new_base_addr = page_index << page_bits;
|
||||
last_segment = {new_base_addr, bytes};
|
||||
} else {
|
||||
last_segment->second += bytes;
|
||||
}
|
||||
};
|
||||
const auto split = [this, &last_segment, &result] {
|
||||
if (last_segment) {
|
||||
result.push_back(*last_segment);
|
||||
last_segment = std::nullopt;
|
||||
}
|
||||
};
|
||||
while (remaining_size > 0) {
|
||||
const size_t num_bytes{std::min(page_size - page_offset, remaining_size)};
|
||||
const auto page_addr{GpuToCpuAddress(page_index << page_bits)};
|
||||
if (!page_addr) {
|
||||
split();
|
||||
} else if (old_page_addr) {
|
||||
if (*old_page_addr + page_size != *page_addr) {
|
||||
split();
|
||||
}
|
||||
extend_size(num_bytes);
|
||||
} else {
|
||||
extend_size(num_bytes);
|
||||
}
|
||||
++page_index;
|
||||
page_offset = 0;
|
||||
remaining_size -= num_bytes;
|
||||
}
|
||||
split();
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace Tegra
|
||||
|
||||
@@ -76,6 +76,8 @@ public:
|
||||
|
||||
[[nodiscard]] std::optional<VAddr> GpuToCpuAddress(GPUVAddr addr) const;
|
||||
|
||||
[[nodiscard]] std::optional<VAddr> GpuToCpuAddress(GPUVAddr addr, std::size_t size) const;
|
||||
|
||||
template <typename T>
|
||||
[[nodiscard]] T Read(GPUVAddr addr) const;
|
||||
|
||||
@@ -112,10 +114,28 @@ public:
|
||||
void WriteBlockUnsafe(GPUVAddr gpu_dest_addr, const void* src_buffer, std::size_t size);
|
||||
|
||||
/**
|
||||
* IsGranularRange checks if a gpu region can be simply read with a pointer.
|
||||
* Checks if a gpu region can be simply read with a pointer.
|
||||
*/
|
||||
[[nodiscard]] bool IsGranularRange(GPUVAddr gpu_addr, std::size_t size) const;
|
||||
|
||||
/**
|
||||
* Checks if a gpu region is mapped by a single range of cpu addresses.
|
||||
*/
|
||||
[[nodiscard]] bool IsContinousRange(GPUVAddr gpu_addr, std::size_t size) const;
|
||||
|
||||
/**
|
||||
* Checks if a gpu region is mapped entirely.
|
||||
*/
|
||||
[[nodiscard]] bool IsFullyMappedRange(GPUVAddr gpu_addr, std::size_t size) const;
|
||||
|
||||
/**
|
||||
* Returns a vector with all the subranges of cpu addresses mapped beneath.
|
||||
* if the region is continous, a single pair will be returned. If it's unmapped, an empty vector
|
||||
* will be returned;
|
||||
*/
|
||||
std::vector<std::pair<GPUVAddr, std::size_t>> GetSubmappedRange(GPUVAddr gpu_addr,
|
||||
std::size_t size) const;
|
||||
|
||||
[[nodiscard]] GPUVAddr Map(VAddr cpu_addr, GPUVAddr gpu_addr, std::size_t size);
|
||||
[[nodiscard]] GPUVAddr MapAllocate(VAddr cpu_addr, std::size_t size, std::size_t align);
|
||||
[[nodiscard]] GPUVAddr MapAllocate32(VAddr cpu_addr, std::size_t size);
|
||||
|
||||
@@ -87,6 +87,9 @@ public:
|
||||
/// Unmap memory range
|
||||
virtual void UnmapMemory(VAddr addr, u64 size) = 0;
|
||||
|
||||
/// Remap GPU memory range. This means underneath backing memory changed
|
||||
virtual void ModifyGPUMemory(GPUVAddr addr, u64 size) = 0;
|
||||
|
||||
/// Notify rasterizer that any caches of the specified region should be flushed to Switch memory
|
||||
/// and invalidated
|
||||
virtual void FlushAndInvalidateRegion(VAddr addr, u64 size) = 0;
|
||||
|
||||
@@ -611,6 +611,13 @@ void RasterizerOpenGL::UnmapMemory(VAddr addr, u64 size) {
|
||||
shader_cache.OnCPUWrite(addr, size);
|
||||
}
|
||||
|
||||
void RasterizerOpenGL::ModifyGPUMemory(GPUVAddr addr, u64 size) {
|
||||
{
|
||||
std::scoped_lock lock{texture_cache.mutex};
|
||||
texture_cache.UnmapGPUMemory(addr, size);
|
||||
}
|
||||
}
|
||||
|
||||
void RasterizerOpenGL::SignalSemaphore(GPUVAddr addr, u32 value) {
|
||||
if (!gpu.IsAsync()) {
|
||||
gpu_memory.Write<u32>(addr, value);
|
||||
|
||||
@@ -80,6 +80,7 @@ public:
|
||||
void OnCPUWrite(VAddr addr, u64 size) override;
|
||||
void SyncGuestHost() override;
|
||||
void UnmapMemory(VAddr addr, u64 size) override;
|
||||
void ModifyGPUMemory(GPUVAddr addr, u64 size) override;
|
||||
void SignalSemaphore(GPUVAddr addr, u32 value) override;
|
||||
void SignalSyncPoint(u32 value) override;
|
||||
void ReleaseFences() override;
|
||||
|
||||
@@ -327,7 +327,8 @@ void ApplySwizzle(GLuint handle, PixelFormat format, std::array<SwizzleSource, 4
|
||||
if (format_info.is_compressed) {
|
||||
return false;
|
||||
}
|
||||
if (std::ranges::find(ACCELERATED_FORMATS, internal_format) == ACCELERATED_FORMATS.end()) {
|
||||
if (std::ranges::find(ACCELERATED_FORMATS, static_cast<int>(internal_format)) ==
|
||||
ACCELERATED_FORMATS.end()) {
|
||||
return false;
|
||||
}
|
||||
if (format_info.compatibility_by_size) {
|
||||
|
||||
@@ -229,9 +229,6 @@ void RendererOpenGL::LoadColorToActiveGLTexture(u8 color_r, u8 color_g, u8 color
|
||||
}
|
||||
|
||||
void RendererOpenGL::InitOpenGLObjects() {
|
||||
glClearColor(Settings::values.bg_red.GetValue(), Settings::values.bg_green.GetValue(),
|
||||
Settings::values.bg_blue.GetValue(), 0.0f);
|
||||
|
||||
// Create shader programs
|
||||
OGLShader vertex_shader;
|
||||
vertex_shader.Create(HostShaders::OPENGL_PRESENT_VERT, GL_VERTEX_SHADER);
|
||||
@@ -337,8 +334,9 @@ void RendererOpenGL::ConfigureFramebufferTexture(TextureInfo& texture,
|
||||
void RendererOpenGL::DrawScreen(const Layout::FramebufferLayout& layout) {
|
||||
if (renderer_settings.set_background_color) {
|
||||
// Update background color before drawing
|
||||
glClearColor(Settings::values.bg_red.GetValue(), Settings::values.bg_green.GetValue(),
|
||||
Settings::values.bg_blue.GetValue(), 0.0f);
|
||||
glClearColor(Settings::values.bg_red.GetValue() / 255.0f,
|
||||
Settings::values.bg_green.GetValue() / 255.0f,
|
||||
Settings::values.bg_blue.GetValue() / 255.0f, 1.0f);
|
||||
}
|
||||
|
||||
// Set projection matrix
|
||||
|
||||
@@ -261,9 +261,9 @@ void UtilShaders::CopyBC4(Image& dst_image, Image& src_image, std::span<const Im
|
||||
glUniform3ui(LOC_SRC_OFFSET, copy.src_offset.x, copy.src_offset.y, copy.src_offset.z);
|
||||
glUniform3ui(LOC_DST_OFFSET, copy.dst_offset.x, copy.dst_offset.y, copy.dst_offset.z);
|
||||
glBindImageTexture(BINDING_INPUT_IMAGE, src_image.StorageHandle(),
|
||||
copy.src_subresource.base_level, GL_FALSE, 0, GL_READ_ONLY, GL_RG32UI);
|
||||
copy.src_subresource.base_level, GL_TRUE, 0, GL_READ_ONLY, GL_RG32UI);
|
||||
glBindImageTexture(BINDING_OUTPUT_IMAGE, dst_image.StorageHandle(),
|
||||
copy.dst_subresource.base_level, GL_FALSE, 0, GL_WRITE_ONLY, GL_RGBA8UI);
|
||||
copy.dst_subresource.base_level, GL_TRUE, 0, GL_WRITE_ONLY, GL_RGBA8UI);
|
||||
glDispatchCompute(copy.extent.width, copy.extent.height, copy.extent.depth);
|
||||
}
|
||||
program_manager.RestoreGuestCompute();
|
||||
|
||||
@@ -103,7 +103,7 @@ RendererVulkan::RendererVulkan(Core::TelemetrySession& telemetry_session_,
|
||||
gpu(gpu_),
|
||||
library(OpenLibrary()),
|
||||
instance(CreateInstance(library, dld, VK_API_VERSION_1_1, render_window.GetWindowInfo().type,
|
||||
true, Settings::values.renderer_debug)),
|
||||
true, Settings::values.renderer_debug.GetValue())),
|
||||
debug_callback(Settings::values.renderer_debug ? CreateDebugCallback(instance) : nullptr),
|
||||
surface(CreateSurface(instance, render_window)),
|
||||
device(CreateDevice(instance, dld, *surface)),
|
||||
|
||||
@@ -225,8 +225,11 @@ VkSemaphore VKBlitScreen::Draw(const Tegra::FramebufferConfig& framebuffer, bool
|
||||
descriptor_set = descriptor_sets[image_index], buffer = *buffer,
|
||||
size = swapchain.GetSize(), pipeline = *pipeline,
|
||||
layout = *pipeline_layout](vk::CommandBuffer cmdbuf) {
|
||||
const f32 bg_red = Settings::values.bg_red.GetValue() / 255.0f;
|
||||
const f32 bg_green = Settings::values.bg_green.GetValue() / 255.0f;
|
||||
const f32 bg_blue = Settings::values.bg_blue.GetValue() / 255.0f;
|
||||
const VkClearValue clear_color{
|
||||
.color = {.float32 = {0.0f, 0.0f, 0.0f, 0.0f}},
|
||||
.color = {.float32 = {bg_red, bg_green, bg_blue, 1.0f}},
|
||||
};
|
||||
const VkRenderPassBeginInfo renderpass_bi{
|
||||
.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO,
|
||||
|
||||
@@ -557,6 +557,13 @@ void RasterizerVulkan::UnmapMemory(VAddr addr, u64 size) {
|
||||
pipeline_cache.OnCPUWrite(addr, size);
|
||||
}
|
||||
|
||||
void RasterizerVulkan::ModifyGPUMemory(GPUVAddr addr, u64 size) {
|
||||
{
|
||||
std::scoped_lock lock{texture_cache.mutex};
|
||||
texture_cache.UnmapGPUMemory(addr, size);
|
||||
}
|
||||
}
|
||||
|
||||
void RasterizerVulkan::SignalSemaphore(GPUVAddr addr, u32 value) {
|
||||
if (!gpu.IsAsync()) {
|
||||
gpu_memory.Write<u32>(addr, value);
|
||||
|
||||
@@ -72,6 +72,7 @@ public:
|
||||
void OnCPUWrite(VAddr addr, u64 size) override;
|
||||
void SyncGuestHost() override;
|
||||
void UnmapMemory(VAddr addr, u64 size) override;
|
||||
void ModifyGPUMemory(GPUVAddr addr, u64 size) override;
|
||||
void SignalSemaphore(GPUVAddr addr, u32 value) override;
|
||||
void SignalSyncPoint(u32 value) override;
|
||||
void ReleaseFences() override;
|
||||
|
||||
@@ -69,6 +69,9 @@ ImageBase::ImageBase(const ImageInfo& info_, GPUVAddr gpu_addr_, VAddr cpu_addr_
|
||||
}
|
||||
}
|
||||
|
||||
ImageMapView::ImageMapView(GPUVAddr gpu_addr_, VAddr cpu_addr_, size_t size_, ImageId image_id_)
|
||||
: gpu_addr{gpu_addr_}, cpu_addr{cpu_addr_}, size{size_}, image_id{image_id_} {}
|
||||
|
||||
std::optional<SubresourceBase> ImageBase::TryFindBase(GPUVAddr other_addr) const noexcept {
|
||||
if (other_addr < gpu_addr) {
|
||||
// Subresource address can't be lower than the base
|
||||
@@ -82,7 +85,7 @@ std::optional<SubresourceBase> ImageBase::TryFindBase(GPUVAddr other_addr) const
|
||||
if (info.type != ImageType::e3D) {
|
||||
const auto [layer, mip_offset] = LayerMipOffset(diff, info.layer_stride);
|
||||
const auto end = mip_level_offsets.begin() + info.resources.levels;
|
||||
const auto it = std::find(mip_level_offsets.begin(), end, mip_offset);
|
||||
const auto it = std::find(mip_level_offsets.begin(), end, static_cast<u32>(mip_offset));
|
||||
if (layer > info.resources.layers || it == end) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
@@ -25,12 +25,14 @@ enum class ImageFlagBits : u32 {
|
||||
Strong = 1 << 5, ///< Exists in the image table, the dimensions are can be trusted
|
||||
Registered = 1 << 6, ///< True when the image is registered
|
||||
Picked = 1 << 7, ///< Temporary flag to mark the image as picked
|
||||
Remapped = 1 << 8, ///< Image has been remapped.
|
||||
Sparse = 1 << 9, ///< Image has non continous submemory.
|
||||
|
||||
// Garbage Collection Flags
|
||||
BadOverlap = 1 << 8, ///< This image overlaps other but doesn't fit, has higher
|
||||
///< garbage collection priority
|
||||
Alias = 1 << 9, ///< This image has aliases and has priority on garbage
|
||||
///< collection
|
||||
BadOverlap = 1 << 10, ///< This image overlaps other but doesn't fit, has higher
|
||||
///< garbage collection priority
|
||||
Alias = 1 << 11, ///< This image has aliases and has priority on garbage
|
||||
///< collection
|
||||
};
|
||||
DECLARE_ENUM_FLAG_OPERATORS(ImageFlagBits)
|
||||
|
||||
@@ -57,6 +59,12 @@ struct ImageBase {
|
||||
return cpu_addr < overlap_end && overlap_cpu_addr < cpu_addr_end;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool OverlapsGPU(GPUVAddr overlap_gpu_addr, size_t overlap_size) const noexcept {
|
||||
const VAddr overlap_end = overlap_gpu_addr + overlap_size;
|
||||
const GPUVAddr gpu_addr_end = gpu_addr + guest_size_bytes;
|
||||
return gpu_addr < overlap_end && overlap_gpu_addr < gpu_addr_end;
|
||||
}
|
||||
|
||||
void CheckBadOverlapState();
|
||||
void CheckAliasState();
|
||||
|
||||
@@ -84,6 +92,29 @@ struct ImageBase {
|
||||
|
||||
std::vector<AliasedImage> aliased_images;
|
||||
std::vector<ImageId> overlapping_images;
|
||||
ImageMapId map_view_id{};
|
||||
};
|
||||
|
||||
struct ImageMapView {
|
||||
explicit ImageMapView(GPUVAddr gpu_addr, VAddr cpu_addr, size_t size, ImageId image_id);
|
||||
|
||||
[[nodiscard]] bool Overlaps(VAddr overlap_cpu_addr, size_t overlap_size) const noexcept {
|
||||
const VAddr overlap_end = overlap_cpu_addr + overlap_size;
|
||||
const VAddr cpu_addr_end = cpu_addr + size;
|
||||
return cpu_addr < overlap_end && overlap_cpu_addr < cpu_addr_end;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool OverlapsGPU(GPUVAddr overlap_gpu_addr, size_t overlap_size) const noexcept {
|
||||
const GPUVAddr overlap_end = overlap_gpu_addr + overlap_size;
|
||||
const GPUVAddr gpu_addr_end = gpu_addr + size;
|
||||
return gpu_addr < overlap_end && overlap_gpu_addr < gpu_addr_end;
|
||||
}
|
||||
|
||||
GPUVAddr gpu_addr;
|
||||
VAddr cpu_addr;
|
||||
size_t size;
|
||||
ImageId image_id;
|
||||
bool picked{};
|
||||
};
|
||||
|
||||
struct ImageAllocBase {
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#include <span>
|
||||
#include <type_traits>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
@@ -152,12 +153,13 @@ public:
|
||||
/// Remove images in a region
|
||||
void UnmapMemory(VAddr cpu_addr, size_t size);
|
||||
|
||||
/// Remove images in a region
|
||||
void UnmapGPUMemory(GPUVAddr gpu_addr, size_t size);
|
||||
|
||||
/// Blit an image with the given parameters
|
||||
void BlitImage(const Tegra::Engines::Fermi2D::Surface& dst,
|
||||
const Tegra::Engines::Fermi2D::Surface& src,
|
||||
const Tegra::Engines::Fermi2D::Config& copy,
|
||||
std::optional<Region2D> src_region_override = {},
|
||||
std::optional<Region2D> dst_region_override = {});
|
||||
const Tegra::Engines::Fermi2D::Config& copy);
|
||||
|
||||
/// Invalidate the contents of the color buffer index
|
||||
/// These contents become unspecified, the cache can assume aggressive optimizations.
|
||||
@@ -190,7 +192,22 @@ public:
|
||||
private:
|
||||
/// Iterate over all page indices in a range
|
||||
template <typename Func>
|
||||
static void ForEachPage(VAddr addr, size_t size, Func&& func) {
|
||||
static void ForEachCPUPage(VAddr addr, size_t size, Func&& func) {
|
||||
static constexpr bool RETURNS_BOOL = std::is_same_v<std::invoke_result<Func, u64>, bool>;
|
||||
const u64 page_end = (addr + size - 1) >> PAGE_BITS;
|
||||
for (u64 page = addr >> PAGE_BITS; page <= page_end; ++page) {
|
||||
if constexpr (RETURNS_BOOL) {
|
||||
if (func(page)) {
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
func(page);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Func>
|
||||
static void ForEachGPUPage(GPUVAddr addr, size_t size, Func&& func) {
|
||||
static constexpr bool RETURNS_BOOL = std::is_same_v<std::invoke_result<Func, u64>, bool>;
|
||||
const u64 page_end = (addr + size - 1) >> PAGE_BITS;
|
||||
for (u64 page = addr >> PAGE_BITS; page <= page_end; ++page) {
|
||||
@@ -220,7 +237,7 @@ private:
|
||||
FramebufferId GetFramebufferId(const RenderTargets& key);
|
||||
|
||||
/// Refresh the contents (pixel data) of an image
|
||||
void RefreshContents(Image& image);
|
||||
void RefreshContents(Image& image, ImageId image_id);
|
||||
|
||||
/// Upload data from guest to an image
|
||||
template <typename StagingBuffer>
|
||||
@@ -269,6 +286,16 @@ private:
|
||||
template <typename Func>
|
||||
void ForEachImageInRegion(VAddr cpu_addr, size_t size, Func&& func);
|
||||
|
||||
template <typename Func>
|
||||
void ForEachImageInRegionGPU(GPUVAddr gpu_addr, size_t size, Func&& func);
|
||||
|
||||
template <typename Func>
|
||||
void ForEachSparseImageInRegion(GPUVAddr gpu_addr, size_t size, Func&& func);
|
||||
|
||||
/// Iterates over all the images in a region calling func
|
||||
template <typename Func>
|
||||
void ForEachSparseSegment(ImageBase& image, Func&& func);
|
||||
|
||||
/// Find or create an image view in the given image with the passed parameters
|
||||
[[nodiscard]] ImageViewId FindOrEmplaceImageView(ImageId image_id, const ImageViewInfo& info);
|
||||
|
||||
@@ -279,10 +306,10 @@ private:
|
||||
void UnregisterImage(ImageId image);
|
||||
|
||||
/// Track CPU reads and writes for image
|
||||
void TrackImage(ImageBase& image);
|
||||
void TrackImage(ImageBase& image, ImageId image_id);
|
||||
|
||||
/// Stop tracking CPU reads and writes for image
|
||||
void UntrackImage(ImageBase& image);
|
||||
void UntrackImage(ImageBase& image, ImageId image_id);
|
||||
|
||||
/// Delete image from the cache
|
||||
void DeleteImage(ImageId image);
|
||||
@@ -340,7 +367,13 @@ private:
|
||||
std::unordered_map<TSCEntry, SamplerId> samplers;
|
||||
std::unordered_map<RenderTargets, FramebufferId> framebuffers;
|
||||
|
||||
std::unordered_map<u64, std::vector<ImageId>, IdentityHash<u64>> page_table;
|
||||
std::unordered_map<u64, std::vector<ImageMapId>, IdentityHash<u64>> page_table;
|
||||
std::unordered_map<u64, std::vector<ImageId>, IdentityHash<u64>> gpu_page_table;
|
||||
std::unordered_map<u64, std::vector<ImageId>, IdentityHash<u64>> sparse_page_table;
|
||||
|
||||
std::unordered_map<ImageId, std::vector<ImageViewId>> sparse_views;
|
||||
|
||||
VAddr virtual_invalid_space{};
|
||||
|
||||
bool has_deleted_images = false;
|
||||
u64 total_used_memory = 0;
|
||||
@@ -349,6 +382,7 @@ private:
|
||||
u64 critical_memory;
|
||||
|
||||
SlotVector<Image> slot_images;
|
||||
SlotVector<ImageMapView> slot_map_views;
|
||||
SlotVector<ImageView> slot_image_views;
|
||||
SlotVector<ImageAlloc> slot_image_allocs;
|
||||
SlotVector<Sampler> slot_samplers;
|
||||
@@ -459,7 +493,7 @@ void TextureCache<P>::RunGarbageCollector() {
|
||||
}
|
||||
}
|
||||
if (True(image->flags & ImageFlagBits::Tracked)) {
|
||||
UntrackImage(*image);
|
||||
UntrackImage(*image, image_id);
|
||||
}
|
||||
UnregisterImage(image_id);
|
||||
DeleteImage(image_id);
|
||||
@@ -658,7 +692,9 @@ void TextureCache<P>::WriteMemory(VAddr cpu_addr, size_t size) {
|
||||
return;
|
||||
}
|
||||
image.flags |= ImageFlagBits::CpuModified;
|
||||
UntrackImage(image);
|
||||
if (True(image.flags & ImageFlagBits::Tracked)) {
|
||||
UntrackImage(image, image_id);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -695,19 +731,34 @@ void TextureCache<P>::UnmapMemory(VAddr cpu_addr, size_t size) {
|
||||
for (const ImageId id : deleted_images) {
|
||||
Image& image = slot_images[id];
|
||||
if (True(image.flags & ImageFlagBits::Tracked)) {
|
||||
UntrackImage(image);
|
||||
UntrackImage(image, id);
|
||||
}
|
||||
UnregisterImage(id);
|
||||
DeleteImage(id);
|
||||
}
|
||||
}
|
||||
|
||||
template <class P>
|
||||
void TextureCache<P>::UnmapGPUMemory(GPUVAddr gpu_addr, size_t size) {
|
||||
std::vector<ImageId> deleted_images;
|
||||
ForEachImageInRegionGPU(gpu_addr, size,
|
||||
[&](ImageId id, Image&) { deleted_images.push_back(id); });
|
||||
for (const ImageId id : deleted_images) {
|
||||
Image& image = slot_images[id];
|
||||
if (True(image.flags & ImageFlagBits::Remapped)) {
|
||||
continue;
|
||||
}
|
||||
image.flags |= ImageFlagBits::Remapped;
|
||||
if (True(image.flags & ImageFlagBits::Tracked)) {
|
||||
UntrackImage(image, id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <class P>
|
||||
void TextureCache<P>::BlitImage(const Tegra::Engines::Fermi2D::Surface& dst,
|
||||
const Tegra::Engines::Fermi2D::Surface& src,
|
||||
const Tegra::Engines::Fermi2D::Config& copy,
|
||||
std::optional<Region2D> src_override,
|
||||
std::optional<Region2D> dst_override) {
|
||||
const Tegra::Engines::Fermi2D::Config& copy) {
|
||||
const BlitImages images = GetBlitImages(dst, src);
|
||||
const ImageId dst_id = images.dst_id;
|
||||
const ImageId src_id = images.src_id;
|
||||
@@ -718,47 +769,25 @@ void TextureCache<P>::BlitImage(const Tegra::Engines::Fermi2D::Surface& dst,
|
||||
const ImageBase& src_image = slot_images[src_id];
|
||||
|
||||
// TODO: Deduplicate
|
||||
const std::optional dst_base = dst_image.TryFindBase(dst.Address());
|
||||
const SubresourceRange dst_range{.base = dst_base.value(), .extent = {1, 1}};
|
||||
const ImageViewInfo dst_view_info(ImageViewType::e2D, images.dst_format, dst_range);
|
||||
const auto [dst_framebuffer_id, dst_view_id] = RenderTargetFromImage(dst_id, dst_view_info);
|
||||
const auto [src_samples_x, src_samples_y] = SamplesLog2(src_image.info.num_samples);
|
||||
|
||||
// out of bounds texture blit checking
|
||||
const bool use_override = src_override.has_value();
|
||||
const s32 src_x0 = copy.src_x0 >> src_samples_x;
|
||||
s32 src_x1 = use_override ? src_override->end.x : copy.src_x1 >> src_samples_x;
|
||||
const s32 src_y0 = copy.src_y0 >> src_samples_y;
|
||||
const s32 src_y1 = copy.src_y1 >> src_samples_y;
|
||||
|
||||
const auto src_width = static_cast<s32>(src_image.info.size.width);
|
||||
const bool width_oob = src_x1 > src_width;
|
||||
const auto width_diff = width_oob ? src_x1 - src_width : 0;
|
||||
if (width_oob) {
|
||||
src_x1 = src_width;
|
||||
}
|
||||
|
||||
const Region2D src_dimensions{
|
||||
Offset2D{.x = src_x0, .y = src_y0},
|
||||
Offset2D{.x = src_x1, .y = src_y1},
|
||||
};
|
||||
const auto src_region = use_override ? *src_override : src_dimensions;
|
||||
|
||||
const std::optional src_base = src_image.TryFindBase(src.Address());
|
||||
const SubresourceRange src_range{.base = src_base.value(), .extent = {1, 1}};
|
||||
const ImageViewInfo src_view_info(ImageViewType::e2D, images.src_format, src_range);
|
||||
const auto [src_framebuffer_id, src_view_id] = RenderTargetFromImage(src_id, src_view_info);
|
||||
const auto [dst_samples_x, dst_samples_y] = SamplesLog2(dst_image.info.num_samples);
|
||||
|
||||
const s32 dst_x0 = copy.dst_x0 >> dst_samples_x;
|
||||
const s32 dst_x1 = copy.dst_x1 >> dst_samples_x;
|
||||
const s32 dst_y0 = copy.dst_y0 >> dst_samples_y;
|
||||
const s32 dst_y1 = copy.dst_y1 >> dst_samples_y;
|
||||
const Region2D dst_dimensions{
|
||||
Offset2D{.x = dst_x0, .y = dst_y0},
|
||||
Offset2D{.x = dst_x1 - width_diff, .y = dst_y1},
|
||||
const auto [src_samples_x, src_samples_y] = SamplesLog2(src_image.info.num_samples);
|
||||
const Region2D src_region{
|
||||
Offset2D{.x = copy.src_x0 >> src_samples_x, .y = copy.src_y0 >> src_samples_y},
|
||||
Offset2D{.x = copy.src_x1 >> src_samples_x, .y = copy.src_y1 >> src_samples_y},
|
||||
};
|
||||
|
||||
const std::optional dst_base = dst_image.TryFindBase(dst.Address());
|
||||
const SubresourceRange dst_range{.base = dst_base.value(), .extent = {1, 1}};
|
||||
const ImageViewInfo dst_view_info(ImageViewType::e2D, images.dst_format, dst_range);
|
||||
const auto [dst_framebuffer_id, dst_view_id] = RenderTargetFromImage(dst_id, dst_view_info);
|
||||
const auto [dst_samples_x, dst_samples_y] = SamplesLog2(dst_image.info.num_samples);
|
||||
const Region2D dst_region{
|
||||
Offset2D{.x = copy.dst_x0 >> dst_samples_x, .y = copy.dst_y0 >> dst_samples_y},
|
||||
Offset2D{.x = copy.dst_x1 >> dst_samples_x, .y = copy.dst_y1 >> dst_samples_y},
|
||||
};
|
||||
const auto dst_region = use_override ? *dst_override : dst_dimensions;
|
||||
|
||||
// Always call this after src_framebuffer_id was queried, as the address might be invalidated.
|
||||
Framebuffer* const dst_framebuffer = &slot_framebuffers[dst_framebuffer_id];
|
||||
@@ -775,21 +804,6 @@ void TextureCache<P>::BlitImage(const Tegra::Engines::Fermi2D::Surface& dst,
|
||||
runtime.BlitImage(dst_framebuffer, dst_view, src_view, dst_region, src_region, copy.filter,
|
||||
copy.operation);
|
||||
}
|
||||
|
||||
if (width_oob) {
|
||||
// Continue copy of the oob region of the texture on the next row
|
||||
auto oob_src = src;
|
||||
oob_src.height++;
|
||||
const Region2D src_region_override{
|
||||
Offset2D{.x = 0, .y = src_y0 + 1},
|
||||
Offset2D{.x = width_diff, .y = src_y1 + 1},
|
||||
};
|
||||
const Region2D dst_region_override{
|
||||
Offset2D{.x = dst_x1 - width_diff, .y = dst_y0},
|
||||
Offset2D{.x = dst_x1, .y = dst_y1},
|
||||
};
|
||||
BlitImage(dst, oob_src, copy, src_region_override, dst_region_override);
|
||||
}
|
||||
}
|
||||
|
||||
template <class P>
|
||||
@@ -833,9 +847,10 @@ typename P::ImageView* TextureCache<P>::TryFindFramebufferImageView(VAddr cpu_ad
|
||||
if (it == page_table.end()) {
|
||||
return nullptr;
|
||||
}
|
||||
const auto& image_ids = it->second;
|
||||
for (const ImageId image_id : image_ids) {
|
||||
const ImageBase& image = slot_images[image_id];
|
||||
const auto& image_map_ids = it->second;
|
||||
for (const ImageMapId map_id : image_map_ids) {
|
||||
const ImageMapView& map = slot_map_views[map_id];
|
||||
const ImageBase& image = slot_images[map.image_id];
|
||||
if (image.cpu_addr != cpu_addr) {
|
||||
continue;
|
||||
}
|
||||
@@ -915,13 +930,13 @@ bool TextureCache<P>::IsRegionGpuModified(VAddr addr, size_t size) {
|
||||
}
|
||||
|
||||
template <class P>
|
||||
void TextureCache<P>::RefreshContents(Image& image) {
|
||||
void TextureCache<P>::RefreshContents(Image& image, ImageId image_id) {
|
||||
if (False(image.flags & ImageFlagBits::CpuModified)) {
|
||||
// Only upload modified images
|
||||
return;
|
||||
}
|
||||
image.flags &= ~ImageFlagBits::CpuModified;
|
||||
TrackImage(image);
|
||||
TrackImage(image, image_id);
|
||||
|
||||
if (image.info.num_samples > 1) {
|
||||
LOG_WARNING(HW_GPU, "MSAA image uploads are not implemented");
|
||||
@@ -958,7 +973,7 @@ void TextureCache<P>::UploadImageContents(Image& image, StagingBuffer& staging)
|
||||
|
||||
template <class P>
|
||||
ImageViewId TextureCache<P>::FindImageView(const TICEntry& config) {
|
||||
if (!IsValidAddress(gpu_memory, config)) {
|
||||
if (!IsValidEntry(gpu_memory, config)) {
|
||||
return NULL_IMAGE_VIEW_ID;
|
||||
}
|
||||
const auto [pair, is_new] = image_views.try_emplace(config);
|
||||
@@ -1000,14 +1015,20 @@ ImageId TextureCache<P>::FindOrInsertImage(const ImageInfo& info, GPUVAddr gpu_a
|
||||
template <class P>
|
||||
ImageId TextureCache<P>::FindImage(const ImageInfo& info, GPUVAddr gpu_addr,
|
||||
RelaxedOptions options) {
|
||||
const std::optional<VAddr> cpu_addr = gpu_memory.GpuToCpuAddress(gpu_addr);
|
||||
std::optional<VAddr> cpu_addr = gpu_memory.GpuToCpuAddress(gpu_addr);
|
||||
if (!cpu_addr) {
|
||||
return ImageId{};
|
||||
cpu_addr = gpu_memory.GpuToCpuAddress(gpu_addr, CalculateGuestSizeInBytes(info));
|
||||
if (!cpu_addr) {
|
||||
return ImageId{};
|
||||
}
|
||||
}
|
||||
const bool broken_views = runtime.HasBrokenTextureViewFormats();
|
||||
const bool native_bgr = runtime.HasNativeBgr();
|
||||
ImageId image_id;
|
||||
const auto lambda = [&](ImageId existing_image_id, ImageBase& existing_image) {
|
||||
if (True(existing_image.flags & ImageFlagBits::Remapped)) {
|
||||
return false;
|
||||
}
|
||||
if (info.type == ImageType::Linear || existing_image.info.type == ImageType::Linear) {
|
||||
const bool strict_size = False(options & RelaxedOptions::Size) &&
|
||||
True(existing_image.flags & ImageFlagBits::Strong);
|
||||
@@ -1033,7 +1054,16 @@ ImageId TextureCache<P>::FindImage(const ImageInfo& info, GPUVAddr gpu_addr,
|
||||
template <class P>
|
||||
ImageId TextureCache<P>::InsertImage(const ImageInfo& info, GPUVAddr gpu_addr,
|
||||
RelaxedOptions options) {
|
||||
const std::optional<VAddr> cpu_addr = gpu_memory.GpuToCpuAddress(gpu_addr);
|
||||
std::optional<VAddr> cpu_addr = gpu_memory.GpuToCpuAddress(gpu_addr);
|
||||
if (!cpu_addr) {
|
||||
const auto size = CalculateGuestSizeInBytes(info);
|
||||
cpu_addr = gpu_memory.GpuToCpuAddress(gpu_addr, size);
|
||||
if (!cpu_addr) {
|
||||
const VAddr fake_addr = ~(1ULL << 40ULL) + virtual_invalid_space;
|
||||
virtual_invalid_space += Common::AlignUp(size, 32);
|
||||
cpu_addr = std::optional<VAddr>(fake_addr);
|
||||
}
|
||||
}
|
||||
ASSERT_MSG(cpu_addr, "Tried to insert an image to an invalid gpu_addr=0x{:x}", gpu_addr);
|
||||
const ImageId image_id = JoinImages(info, gpu_addr, *cpu_addr);
|
||||
const Image& image = slot_images[image_id];
|
||||
@@ -1053,10 +1083,16 @@ ImageId TextureCache<P>::JoinImages(const ImageInfo& info, GPUVAddr gpu_addr, VA
|
||||
const bool broken_views = runtime.HasBrokenTextureViewFormats();
|
||||
const bool native_bgr = runtime.HasNativeBgr();
|
||||
std::vector<ImageId> overlap_ids;
|
||||
std::unordered_set<ImageId> overlaps_found;
|
||||
std::vector<ImageId> left_aliased_ids;
|
||||
std::vector<ImageId> right_aliased_ids;
|
||||
std::unordered_set<ImageId> ignore_textures;
|
||||
std::vector<ImageId> bad_overlap_ids;
|
||||
ForEachImageInRegion(cpu_addr, size_bytes, [&](ImageId overlap_id, ImageBase& overlap) {
|
||||
const auto region_check = [&](ImageId overlap_id, ImageBase& overlap) {
|
||||
if (True(overlap.flags & ImageFlagBits::Remapped)) {
|
||||
ignore_textures.insert(overlap_id);
|
||||
return;
|
||||
}
|
||||
if (info.type == ImageType::Linear) {
|
||||
if (info.pitch == overlap.info.pitch && gpu_addr == overlap.gpu_addr) {
|
||||
// Alias linear images with the same pitch
|
||||
@@ -1064,6 +1100,7 @@ ImageId TextureCache<P>::JoinImages(const ImageInfo& info, GPUVAddr gpu_addr, VA
|
||||
}
|
||||
return;
|
||||
}
|
||||
overlaps_found.insert(overlap_id);
|
||||
static constexpr bool strict_size = true;
|
||||
const std::optional<OverlapResult> solution = ResolveOverlap(
|
||||
new_info, gpu_addr, cpu_addr, overlap, strict_size, broken_views, native_bgr);
|
||||
@@ -1087,12 +1124,40 @@ ImageId TextureCache<P>::JoinImages(const ImageInfo& info, GPUVAddr gpu_addr, VA
|
||||
bad_overlap_ids.push_back(overlap_id);
|
||||
overlap.flags |= ImageFlagBits::BadOverlap;
|
||||
}
|
||||
});
|
||||
};
|
||||
ForEachImageInRegion(cpu_addr, size_bytes, region_check);
|
||||
const auto region_check_gpu = [&](ImageId overlap_id, ImageBase& overlap) {
|
||||
if (!overlaps_found.contains(overlap_id)) {
|
||||
if (True(overlap.flags & ImageFlagBits::Remapped)) {
|
||||
ignore_textures.insert(overlap_id);
|
||||
}
|
||||
if (overlap.gpu_addr == gpu_addr && overlap.guest_size_bytes == size_bytes) {
|
||||
ignore_textures.insert(overlap_id);
|
||||
}
|
||||
}
|
||||
};
|
||||
ForEachSparseImageInRegion(gpu_addr, size_bytes, region_check_gpu);
|
||||
const ImageId new_image_id = slot_images.insert(runtime, new_info, gpu_addr, cpu_addr);
|
||||
Image& new_image = slot_images[new_image_id];
|
||||
|
||||
if (!gpu_memory.IsContinousRange(new_image.gpu_addr, new_image.guest_size_bytes)) {
|
||||
new_image.flags |= ImageFlagBits::Sparse;
|
||||
}
|
||||
|
||||
for (const ImageId overlap_id : ignore_textures) {
|
||||
Image& overlap = slot_images[overlap_id];
|
||||
if (True(overlap.flags & ImageFlagBits::GpuModified)) {
|
||||
UNIMPLEMENTED();
|
||||
}
|
||||
if (True(overlap.flags & ImageFlagBits::Tracked)) {
|
||||
UntrackImage(overlap, overlap_id);
|
||||
}
|
||||
UnregisterImage(overlap_id);
|
||||
DeleteImage(overlap_id);
|
||||
}
|
||||
|
||||
// TODO: Only upload what we need
|
||||
RefreshContents(new_image);
|
||||
RefreshContents(new_image, new_image_id);
|
||||
|
||||
for (const ImageId overlap_id : overlap_ids) {
|
||||
Image& overlap = slot_images[overlap_id];
|
||||
@@ -1104,7 +1169,7 @@ ImageId TextureCache<P>::JoinImages(const ImageInfo& info, GPUVAddr gpu_addr, VA
|
||||
runtime.CopyImage(new_image, overlap, copies);
|
||||
}
|
||||
if (True(overlap.flags & ImageFlagBits::Tracked)) {
|
||||
UntrackImage(overlap);
|
||||
UntrackImage(overlap, overlap_id);
|
||||
}
|
||||
UnregisterImage(overlap_id);
|
||||
DeleteImage(overlap_id);
|
||||
@@ -1239,7 +1304,8 @@ void TextureCache<P>::ForEachImageInRegion(VAddr cpu_addr, size_t size, Func&& f
|
||||
using FuncReturn = typename std::invoke_result<Func, ImageId, Image&>::type;
|
||||
static constexpr bool BOOL_BREAK = std::is_same_v<FuncReturn, bool>;
|
||||
boost::container::small_vector<ImageId, 32> images;
|
||||
ForEachPage(cpu_addr, size, [this, &images, cpu_addr, size, func](u64 page) {
|
||||
boost::container::small_vector<ImageMapId, 32> maps;
|
||||
ForEachCPUPage(cpu_addr, size, [this, &images, &maps, cpu_addr, size, func](u64 page) {
|
||||
const auto it = page_table.find(page);
|
||||
if (it == page_table.end()) {
|
||||
if constexpr (BOOL_BREAK) {
|
||||
@@ -1248,12 +1314,63 @@ void TextureCache<P>::ForEachImageInRegion(VAddr cpu_addr, size_t size, Func&& f
|
||||
return;
|
||||
}
|
||||
}
|
||||
for (const ImageMapId map_id : it->second) {
|
||||
ImageMapView& map = slot_map_views[map_id];
|
||||
if (map.picked) {
|
||||
continue;
|
||||
}
|
||||
if (!map.Overlaps(cpu_addr, size)) {
|
||||
continue;
|
||||
}
|
||||
map.picked = true;
|
||||
maps.push_back(map_id);
|
||||
Image& image = slot_images[map.image_id];
|
||||
if (True(image.flags & ImageFlagBits::Picked)) {
|
||||
continue;
|
||||
}
|
||||
image.flags |= ImageFlagBits::Picked;
|
||||
images.push_back(map.image_id);
|
||||
if constexpr (BOOL_BREAK) {
|
||||
if (func(map.image_id, image)) {
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
func(map.image_id, image);
|
||||
}
|
||||
}
|
||||
if constexpr (BOOL_BREAK) {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
for (const ImageId image_id : images) {
|
||||
slot_images[image_id].flags &= ~ImageFlagBits::Picked;
|
||||
}
|
||||
for (const ImageMapId map_id : maps) {
|
||||
slot_map_views[map_id].picked = false;
|
||||
}
|
||||
}
|
||||
|
||||
template <class P>
|
||||
template <typename Func>
|
||||
void TextureCache<P>::ForEachImageInRegionGPU(GPUVAddr gpu_addr, size_t size, Func&& func) {
|
||||
using FuncReturn = typename std::invoke_result<Func, ImageId, Image&>::type;
|
||||
static constexpr bool BOOL_BREAK = std::is_same_v<FuncReturn, bool>;
|
||||
boost::container::small_vector<ImageId, 8> images;
|
||||
ForEachGPUPage(gpu_addr, size, [this, &images, gpu_addr, size, func](u64 page) {
|
||||
const auto it = gpu_page_table.find(page);
|
||||
if (it == gpu_page_table.end()) {
|
||||
if constexpr (BOOL_BREAK) {
|
||||
return false;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
for (const ImageId image_id : it->second) {
|
||||
Image& image = slot_images[image_id];
|
||||
if (True(image.flags & ImageFlagBits::Picked)) {
|
||||
continue;
|
||||
}
|
||||
if (!image.Overlaps(cpu_addr, size)) {
|
||||
if (!image.OverlapsGPU(gpu_addr, size)) {
|
||||
continue;
|
||||
}
|
||||
image.flags |= ImageFlagBits::Picked;
|
||||
@@ -1275,6 +1392,69 @@ void TextureCache<P>::ForEachImageInRegion(VAddr cpu_addr, size_t size, Func&& f
|
||||
}
|
||||
}
|
||||
|
||||
template <class P>
|
||||
template <typename Func>
|
||||
void TextureCache<P>::ForEachSparseImageInRegion(GPUVAddr gpu_addr, size_t size, Func&& func) {
|
||||
using FuncReturn = typename std::invoke_result<Func, ImageId, Image&>::type;
|
||||
static constexpr bool BOOL_BREAK = std::is_same_v<FuncReturn, bool>;
|
||||
boost::container::small_vector<ImageId, 8> images;
|
||||
ForEachGPUPage(gpu_addr, size, [this, &images, gpu_addr, size, func](u64 page) {
|
||||
const auto it = sparse_page_table.find(page);
|
||||
if (it == sparse_page_table.end()) {
|
||||
if constexpr (BOOL_BREAK) {
|
||||
return false;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
for (const ImageId image_id : it->second) {
|
||||
Image& image = slot_images[image_id];
|
||||
if (True(image.flags & ImageFlagBits::Picked)) {
|
||||
continue;
|
||||
}
|
||||
if (!image.OverlapsGPU(gpu_addr, size)) {
|
||||
continue;
|
||||
}
|
||||
image.flags |= ImageFlagBits::Picked;
|
||||
images.push_back(image_id);
|
||||
if constexpr (BOOL_BREAK) {
|
||||
if (func(image_id, image)) {
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
func(image_id, image);
|
||||
}
|
||||
}
|
||||
if constexpr (BOOL_BREAK) {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
for (const ImageId image_id : images) {
|
||||
slot_images[image_id].flags &= ~ImageFlagBits::Picked;
|
||||
}
|
||||
}
|
||||
|
||||
template <class P>
|
||||
template <typename Func>
|
||||
void TextureCache<P>::ForEachSparseSegment(ImageBase& image, Func&& func) {
|
||||
using FuncReturn = typename std::invoke_result<Func, GPUVAddr, VAddr, size_t>::type;
|
||||
static constexpr bool RETURNS_BOOL = std::is_same_v<FuncReturn, bool>;
|
||||
const auto segments = gpu_memory.GetSubmappedRange(image.gpu_addr, image.guest_size_bytes);
|
||||
for (auto& segment : segments) {
|
||||
const auto gpu_addr = segment.first;
|
||||
const auto size = segment.second;
|
||||
std::optional<VAddr> cpu_addr = gpu_memory.GpuToCpuAddress(gpu_addr);
|
||||
ASSERT(cpu_addr);
|
||||
if constexpr (RETURNS_BOOL) {
|
||||
if (func(gpu_addr, *cpu_addr, size)) {
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
func(gpu_addr, *cpu_addr, size);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <class P>
|
||||
ImageViewId TextureCache<P>::FindOrEmplaceImageView(ImageId image_id, const ImageViewInfo& info) {
|
||||
Image& image = slot_images[image_id];
|
||||
@@ -1292,8 +1472,6 @@ void TextureCache<P>::RegisterImage(ImageId image_id) {
|
||||
ASSERT_MSG(False(image.flags & ImageFlagBits::Registered),
|
||||
"Trying to register an already registered image");
|
||||
image.flags |= ImageFlagBits::Registered;
|
||||
ForEachPage(image.cpu_addr, image.guest_size_bytes,
|
||||
[this, image_id](u64 page) { page_table[page].push_back(image_id); });
|
||||
u64 tentative_size = std::max(image.guest_size_bytes, image.unswizzled_size_bytes);
|
||||
if ((IsPixelFormatASTC(image.info.format) &&
|
||||
True(image.flags & ImageFlagBits::AcceleratedUpload)) ||
|
||||
@@ -1301,6 +1479,27 @@ void TextureCache<P>::RegisterImage(ImageId image_id) {
|
||||
tentative_size = EstimatedDecompressedSize(tentative_size, image.info.format);
|
||||
}
|
||||
total_used_memory += Common::AlignUp(tentative_size, 1024);
|
||||
ForEachGPUPage(image.gpu_addr, image.guest_size_bytes,
|
||||
[this, image_id](u64 page) { gpu_page_table[page].push_back(image_id); });
|
||||
if (False(image.flags & ImageFlagBits::Sparse)) {
|
||||
auto map_id =
|
||||
slot_map_views.insert(image.gpu_addr, image.cpu_addr, image.guest_size_bytes, image_id);
|
||||
ForEachCPUPage(image.cpu_addr, image.guest_size_bytes,
|
||||
[this, map_id](u64 page) { page_table[page].push_back(map_id); });
|
||||
image.map_view_id = map_id;
|
||||
return;
|
||||
}
|
||||
std::vector<ImageViewId> sparse_maps{};
|
||||
ForEachSparseSegment(
|
||||
image, [this, image_id, &sparse_maps](GPUVAddr gpu_addr, VAddr cpu_addr, size_t size) {
|
||||
auto map_id = slot_map_views.insert(gpu_addr, cpu_addr, size, image_id);
|
||||
ForEachCPUPage(cpu_addr, size,
|
||||
[this, map_id](u64 page) { page_table[page].push_back(map_id); });
|
||||
sparse_maps.push_back(map_id);
|
||||
});
|
||||
sparse_views.emplace(image_id, std::move(sparse_maps));
|
||||
ForEachGPUPage(image.gpu_addr, image.guest_size_bytes,
|
||||
[this, image_id](u64 page) { sparse_page_table[page].push_back(image_id); });
|
||||
}
|
||||
|
||||
template <class P>
|
||||
@@ -1317,34 +1516,125 @@ void TextureCache<P>::UnregisterImage(ImageId image_id) {
|
||||
tentative_size = EstimatedDecompressedSize(tentative_size, image.info.format);
|
||||
}
|
||||
total_used_memory -= Common::AlignUp(tentative_size, 1024);
|
||||
ForEachPage(image.cpu_addr, image.guest_size_bytes, [this, image_id](u64 page) {
|
||||
const auto page_it = page_table.find(page);
|
||||
if (page_it == page_table.end()) {
|
||||
UNREACHABLE_MSG("Unregistering unregistered page=0x{:x}", page << PAGE_BITS);
|
||||
return;
|
||||
}
|
||||
std::vector<ImageId>& image_ids = page_it->second;
|
||||
const auto vector_it = std::ranges::find(image_ids, image_id);
|
||||
if (vector_it == image_ids.end()) {
|
||||
UNREACHABLE_MSG("Unregistering unregistered image in page=0x{:x}", page << PAGE_BITS);
|
||||
return;
|
||||
}
|
||||
image_ids.erase(vector_it);
|
||||
const auto& clear_page_table =
|
||||
[this, image_id](
|
||||
u64 page,
|
||||
std::unordered_map<u64, std::vector<ImageId>, IdentityHash<u64>>& selected_page_table) {
|
||||
const auto page_it = selected_page_table.find(page);
|
||||
if (page_it == selected_page_table.end()) {
|
||||
UNREACHABLE_MSG("Unregistering unregistered page=0x{:x}", page << PAGE_BITS);
|
||||
return;
|
||||
}
|
||||
std::vector<ImageId>& image_ids = page_it->second;
|
||||
const auto vector_it = std::ranges::find(image_ids, image_id);
|
||||
if (vector_it == image_ids.end()) {
|
||||
UNREACHABLE_MSG("Unregistering unregistered image in page=0x{:x}",
|
||||
page << PAGE_BITS);
|
||||
return;
|
||||
}
|
||||
image_ids.erase(vector_it);
|
||||
};
|
||||
ForEachGPUPage(image.gpu_addr, image.guest_size_bytes,
|
||||
[this, &clear_page_table](u64 page) { clear_page_table(page, gpu_page_table); });
|
||||
if (False(image.flags & ImageFlagBits::Sparse)) {
|
||||
const auto map_id = image.map_view_id;
|
||||
ForEachCPUPage(image.cpu_addr, image.guest_size_bytes, [this, map_id](u64 page) {
|
||||
const auto page_it = page_table.find(page);
|
||||
if (page_it == page_table.end()) {
|
||||
UNREACHABLE_MSG("Unregistering unregistered page=0x{:x}", page << PAGE_BITS);
|
||||
return;
|
||||
}
|
||||
std::vector<ImageMapId>& image_map_ids = page_it->second;
|
||||
const auto vector_it = std::ranges::find(image_map_ids, map_id);
|
||||
if (vector_it == image_map_ids.end()) {
|
||||
UNREACHABLE_MSG("Unregistering unregistered image in page=0x{:x}",
|
||||
page << PAGE_BITS);
|
||||
return;
|
||||
}
|
||||
image_map_ids.erase(vector_it);
|
||||
});
|
||||
slot_map_views.erase(map_id);
|
||||
return;
|
||||
}
|
||||
ForEachGPUPage(image.gpu_addr, image.guest_size_bytes, [this, &clear_page_table](u64 page) {
|
||||
clear_page_table(page, sparse_page_table);
|
||||
});
|
||||
auto it = sparse_views.find(image_id);
|
||||
ASSERT(it != sparse_views.end());
|
||||
auto& sparse_maps = it->second;
|
||||
for (auto& map_view_id : sparse_maps) {
|
||||
const auto& map_range = slot_map_views[map_view_id];
|
||||
const VAddr cpu_addr = map_range.cpu_addr;
|
||||
const std::size_t size = map_range.size;
|
||||
ForEachCPUPage(cpu_addr, size, [this, image_id](u64 page) {
|
||||
const auto page_it = page_table.find(page);
|
||||
if (page_it == page_table.end()) {
|
||||
UNREACHABLE_MSG("Unregistering unregistered page=0x{:x}", page << PAGE_BITS);
|
||||
return;
|
||||
}
|
||||
std::vector<ImageMapId>& image_map_ids = page_it->second;
|
||||
auto vector_it = image_map_ids.begin();
|
||||
while (vector_it != image_map_ids.end()) {
|
||||
ImageMapView& map = slot_map_views[*vector_it];
|
||||
if (map.image_id != image_id) {
|
||||
vector_it++;
|
||||
continue;
|
||||
}
|
||||
if (!map.picked) {
|
||||
map.picked = true;
|
||||
}
|
||||
vector_it = image_map_ids.erase(vector_it);
|
||||
}
|
||||
});
|
||||
slot_map_views.erase(map_view_id);
|
||||
}
|
||||
sparse_views.erase(it);
|
||||
}
|
||||
|
||||
template <class P>
|
||||
void TextureCache<P>::TrackImage(ImageBase& image) {
|
||||
void TextureCache<P>::TrackImage(ImageBase& image, ImageId image_id) {
|
||||
ASSERT(False(image.flags & ImageFlagBits::Tracked));
|
||||
image.flags |= ImageFlagBits::Tracked;
|
||||
rasterizer.UpdatePagesCachedCount(image.cpu_addr, image.guest_size_bytes, 1);
|
||||
if (False(image.flags & ImageFlagBits::Sparse)) {
|
||||
rasterizer.UpdatePagesCachedCount(image.cpu_addr, image.guest_size_bytes, 1);
|
||||
return;
|
||||
}
|
||||
if (True(image.flags & ImageFlagBits::Registered)) {
|
||||
auto it = sparse_views.find(image_id);
|
||||
ASSERT(it != sparse_views.end());
|
||||
auto& sparse_maps = it->second;
|
||||
for (auto& map_view_id : sparse_maps) {
|
||||
const auto& map = slot_map_views[map_view_id];
|
||||
const VAddr cpu_addr = map.cpu_addr;
|
||||
const std::size_t size = map.size;
|
||||
rasterizer.UpdatePagesCachedCount(cpu_addr, size, 1);
|
||||
}
|
||||
return;
|
||||
}
|
||||
ForEachSparseSegment(image,
|
||||
[this]([[maybe_unused]] GPUVAddr gpu_addr, VAddr cpu_addr, size_t size) {
|
||||
rasterizer.UpdatePagesCachedCount(cpu_addr, size, 1);
|
||||
});
|
||||
}
|
||||
|
||||
template <class P>
|
||||
void TextureCache<P>::UntrackImage(ImageBase& image) {
|
||||
void TextureCache<P>::UntrackImage(ImageBase& image, ImageId image_id) {
|
||||
ASSERT(True(image.flags & ImageFlagBits::Tracked));
|
||||
image.flags &= ~ImageFlagBits::Tracked;
|
||||
rasterizer.UpdatePagesCachedCount(image.cpu_addr, image.guest_size_bytes, -1);
|
||||
if (False(image.flags & ImageFlagBits::Sparse)) {
|
||||
rasterizer.UpdatePagesCachedCount(image.cpu_addr, image.guest_size_bytes, -1);
|
||||
return;
|
||||
}
|
||||
ASSERT(True(image.flags & ImageFlagBits::Registered));
|
||||
auto it = sparse_views.find(image_id);
|
||||
ASSERT(it != sparse_views.end());
|
||||
auto& sparse_maps = it->second;
|
||||
for (auto& map_view_id : sparse_maps) {
|
||||
const auto& map = slot_map_views[map_view_id];
|
||||
const VAddr cpu_addr = map.cpu_addr;
|
||||
const std::size_t size = map.size;
|
||||
rasterizer.UpdatePagesCachedCount(cpu_addr, size, -1);
|
||||
}
|
||||
}
|
||||
|
||||
template <class P>
|
||||
@@ -1486,10 +1776,10 @@ void TextureCache<P>::PrepareImage(ImageId image_id, bool is_modification, bool
|
||||
if (invalidate) {
|
||||
image.flags &= ~(ImageFlagBits::CpuModified | ImageFlagBits::GpuModified);
|
||||
if (False(image.flags & ImageFlagBits::Tracked)) {
|
||||
TrackImage(image);
|
||||
TrackImage(image, image_id);
|
||||
}
|
||||
} else {
|
||||
RefreshContents(image);
|
||||
RefreshContents(image, image_id);
|
||||
SynchronizeAliases(image_id);
|
||||
}
|
||||
if (is_modification) {
|
||||
|
||||
@@ -16,6 +16,7 @@ constexpr size_t MAX_MIP_LEVELS = 14;
|
||||
constexpr SlotId CORRUPT_ID{0xfffffffe};
|
||||
|
||||
using ImageId = SlotId;
|
||||
using ImageMapId = SlotId;
|
||||
using ImageViewId = SlotId;
|
||||
using ImageAllocId = SlotId;
|
||||
using SamplerId = SlotId;
|
||||
|
||||
@@ -394,7 +394,7 @@ template <u32 GOB_EXTENT>
|
||||
const s32 mip_offset = diff % layer_stride;
|
||||
const std::array offsets = CalculateMipLevelOffsets(new_info);
|
||||
const auto end = offsets.begin() + new_info.resources.levels;
|
||||
const auto it = std::find(offsets.begin(), end, mip_offset);
|
||||
const auto it = std::find(offsets.begin(), end, static_cast<u32>(mip_offset));
|
||||
if (it == end) {
|
||||
// Mipmap is not aligned to any valid size
|
||||
return std::nullopt;
|
||||
@@ -664,6 +664,16 @@ LevelArray CalculateMipLevelOffsets(const ImageInfo& info) noexcept {
|
||||
return offsets;
|
||||
}
|
||||
|
||||
LevelArray CalculateMipLevelSizes(const ImageInfo& info) noexcept {
|
||||
const u32 num_levels = info.resources.levels;
|
||||
const LevelInfo level_info = MakeLevelInfo(info);
|
||||
LevelArray sizes{};
|
||||
for (u32 level = 0; level < num_levels; ++level) {
|
||||
sizes[level] = CalculateLevelSize(level_info, level);
|
||||
}
|
||||
return sizes;
|
||||
}
|
||||
|
||||
std::vector<u32> CalculateSliceOffsets(const ImageInfo& info) {
|
||||
ASSERT(info.type == ImageType::e3D);
|
||||
std::vector<u32> offsets;
|
||||
@@ -776,14 +786,20 @@ std::vector<ImageCopy> MakeShrinkImageCopies(const ImageInfo& dst, const ImageIn
|
||||
return copies;
|
||||
}
|
||||
|
||||
bool IsValidAddress(const Tegra::MemoryManager& gpu_memory, const TICEntry& config) {
|
||||
if (config.Address() == 0) {
|
||||
bool IsValidEntry(const Tegra::MemoryManager& gpu_memory, const TICEntry& config) {
|
||||
const GPUVAddr address = config.Address();
|
||||
if (address == 0) {
|
||||
return false;
|
||||
}
|
||||
if (config.Address() > (u64(1) << 48)) {
|
||||
if (address > (1ULL << 48)) {
|
||||
return false;
|
||||
}
|
||||
return gpu_memory.GpuToCpuAddress(config.Address()).has_value();
|
||||
if (gpu_memory.GpuToCpuAddress(address).has_value()) {
|
||||
return true;
|
||||
}
|
||||
const ImageInfo info{config};
|
||||
const size_t guest_size_bytes = CalculateGuestSizeInBytes(info);
|
||||
return gpu_memory.GpuToCpuAddress(address, guest_size_bytes).has_value();
|
||||
}
|
||||
|
||||
std::vector<BufferImageCopy> UnswizzleImage(Tegra::MemoryManager& gpu_memory, GPUVAddr gpu_addr,
|
||||
|
||||
@@ -40,6 +40,8 @@ struct OverlapResult {
|
||||
|
||||
[[nodiscard]] LevelArray CalculateMipLevelOffsets(const ImageInfo& info) noexcept;
|
||||
|
||||
[[nodiscard]] LevelArray CalculateMipLevelSizes(const ImageInfo& info) noexcept;
|
||||
|
||||
[[nodiscard]] std::vector<u32> CalculateSliceOffsets(const ImageInfo& info);
|
||||
|
||||
[[nodiscard]] std::vector<SubresourceBase> CalculateSliceSubresources(const ImageInfo& info);
|
||||
@@ -55,7 +57,7 @@ struct OverlapResult {
|
||||
const ImageInfo& src,
|
||||
SubresourceBase base);
|
||||
|
||||
[[nodiscard]] bool IsValidAddress(const Tegra::MemoryManager& gpu_memory, const TICEntry& config);
|
||||
[[nodiscard]] bool IsValidEntry(const Tegra::MemoryManager& gpu_memory, const TICEntry& config);
|
||||
|
||||
[[nodiscard]] std::vector<BufferImageCopy> UnswizzleImage(Tegra::MemoryManager& gpu_memory,
|
||||
GPUVAddr gpu_addr, const ImageInfo& info,
|
||||
|
||||
@@ -1365,8 +1365,8 @@ static void DecompressBlock(std::span<const u8, 16> inBuf, const u32 blockWidth,
|
||||
// each partition.
|
||||
|
||||
// Determine partitions, partition index, and color endpoint modes
|
||||
s32 planeIdx = -1;
|
||||
u32 partitionIndex;
|
||||
u32 planeIdx{UINT32_MAX};
|
||||
u32 partitionIndex{};
|
||||
u32 colorEndpointMode[4] = {0, 0, 0, 0};
|
||||
|
||||
// Define color data.
|
||||
|
||||
@@ -272,6 +272,66 @@ void Config::Initialize(const std::string& config_name) {
|
||||
}
|
||||
}
|
||||
|
||||
/* {Read,Write}BasicSetting and WriteGlobalSetting templates must be defined here before their
|
||||
* usages later in this file. This allows explicit definition of some types that don't work
|
||||
* nicely with the general version.
|
||||
*/
|
||||
|
||||
// Explicit std::string definition: Qt can't implicitly convert a std::string to a QVariant, nor
|
||||
// can it implicitly convert a QVariant back to a {std::,Q}string
|
||||
template <>
|
||||
void Config::ReadBasicSetting(Settings::BasicSetting<std::string>& setting) {
|
||||
const QString name = QString::fromStdString(setting.GetLabel());
|
||||
const auto default_value = QString::fromStdString(setting.GetDefault());
|
||||
if (qt_config->value(name + QStringLiteral("/default"), false).toBool()) {
|
||||
setting.SetValue(default_value.toStdString());
|
||||
} else {
|
||||
setting.SetValue(qt_config->value(name, default_value).toString().toStdString());
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Type>
|
||||
void Config::ReadBasicSetting(Settings::BasicSetting<Type>& setting) {
|
||||
const QString name = QString::fromStdString(setting.GetLabel());
|
||||
const Type default_value = setting.GetDefault();
|
||||
if (qt_config->value(name + QStringLiteral("/default"), false).toBool()) {
|
||||
setting.SetValue(default_value);
|
||||
} else {
|
||||
setting.SetValue(
|
||||
static_cast<QVariant>(qt_config->value(name, default_value)).value<Type>());
|
||||
}
|
||||
}
|
||||
|
||||
// Explicit std::string definition: Qt can't implicitly convert a std::string to a QVariant
|
||||
template <>
|
||||
void Config::WriteBasicSetting(const Settings::BasicSetting<std::string>& setting) {
|
||||
const QString name = QString::fromStdString(setting.GetLabel());
|
||||
const std::string& value = setting.GetValue();
|
||||
qt_config->setValue(name + QStringLiteral("/default"), value == setting.GetDefault());
|
||||
qt_config->setValue(name, QString::fromStdString(value));
|
||||
}
|
||||
|
||||
template <typename Type>
|
||||
void Config::WriteBasicSetting(const Settings::BasicSetting<Type>& setting) {
|
||||
const QString name = QString::fromStdString(setting.GetLabel());
|
||||
const Type value = setting.GetValue();
|
||||
qt_config->setValue(name + QStringLiteral("/default"), value == setting.GetDefault());
|
||||
qt_config->setValue(name, value);
|
||||
}
|
||||
|
||||
template <typename Type>
|
||||
void Config::WriteGlobalSetting(const Settings::Setting<Type>& setting) {
|
||||
const QString name = QString::fromStdString(setting.GetLabel());
|
||||
const Type& value = setting.GetValue(global);
|
||||
if (!global) {
|
||||
qt_config->setValue(name + QStringLiteral("/use_global"), setting.UsingGlobal());
|
||||
}
|
||||
if (global || !setting.UsingGlobal()) {
|
||||
qt_config->setValue(name + QStringLiteral("/default"), value == setting.GetDefault());
|
||||
qt_config->setValue(name, value);
|
||||
}
|
||||
}
|
||||
|
||||
void Config::ReadPlayerValue(std::size_t player_index) {
|
||||
const QString player_prefix = [this, player_index] {
|
||||
if (type == ConfigType::InputProfile) {
|
||||
@@ -395,8 +455,7 @@ void Config::ReadPlayerValue(std::size_t player_index) {
|
||||
}
|
||||
|
||||
void Config::ReadDebugValues() {
|
||||
Settings::values.debug_pad_enabled =
|
||||
ReadSetting(QStringLiteral("debug_pad_enabled"), false).toBool();
|
||||
ReadBasicSetting(Settings::values.debug_pad_enabled);
|
||||
|
||||
for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) {
|
||||
const std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]);
|
||||
@@ -432,8 +491,7 @@ void Config::ReadDebugValues() {
|
||||
}
|
||||
|
||||
void Config::ReadKeyboardValues() {
|
||||
Settings::values.keyboard_enabled =
|
||||
ReadSetting(QStringLiteral("keyboard_enabled"), false).toBool();
|
||||
ReadBasicSetting(Settings::values.keyboard_enabled);
|
||||
|
||||
std::transform(default_keyboard_keys.begin(), default_keyboard_keys.end(),
|
||||
Settings::values.keyboard_keys.begin(), InputCommon::GenerateKeyboardParam);
|
||||
@@ -446,7 +504,7 @@ void Config::ReadKeyboardValues() {
|
||||
}
|
||||
|
||||
void Config::ReadMouseValues() {
|
||||
Settings::values.mouse_enabled = ReadSetting(QStringLiteral("mouse_enabled"), false).toBool();
|
||||
ReadBasicSetting(Settings::values.mouse_enabled);
|
||||
|
||||
for (int i = 0; i < Settings::NativeMouseButton::NumMouseButtons; ++i) {
|
||||
const std::string default_param =
|
||||
@@ -481,18 +539,11 @@ void Config::ReadAudioValues() {
|
||||
qt_config->beginGroup(QStringLiteral("Audio"));
|
||||
|
||||
if (global) {
|
||||
Settings::values.sink_id =
|
||||
ReadSetting(QStringLiteral("output_engine"), QStringLiteral("auto"))
|
||||
.toString()
|
||||
.toStdString();
|
||||
Settings::values.audio_device_id =
|
||||
ReadSetting(QStringLiteral("output_device"), QStringLiteral("auto"))
|
||||
.toString()
|
||||
.toStdString();
|
||||
ReadBasicSetting(Settings::values.audio_device_id);
|
||||
ReadBasicSetting(Settings::values.sink_id);
|
||||
}
|
||||
ReadSettingGlobal(Settings::values.enable_audio_stretching,
|
||||
QStringLiteral("enable_audio_stretching"), true);
|
||||
ReadSettingGlobal(Settings::values.volume, QStringLiteral("volume"), 1);
|
||||
ReadGlobalSetting(Settings::values.enable_audio_stretching);
|
||||
ReadGlobalSetting(Settings::values.volume);
|
||||
|
||||
qt_config->endGroup();
|
||||
}
|
||||
@@ -509,13 +560,11 @@ void Config::ReadControlValues() {
|
||||
ReadTouchscreenValues();
|
||||
ReadMotionTouchValues();
|
||||
|
||||
Settings::values.emulate_analog_keyboard =
|
||||
ReadSetting(QStringLiteral("emulate_analog_keyboard"), false).toBool();
|
||||
ReadBasicSetting(Settings::values.emulate_analog_keyboard);
|
||||
Settings::values.mouse_panning = false;
|
||||
Settings::values.mouse_panning_sensitivity =
|
||||
ReadSetting(QStringLiteral("mouse_panning_sensitivity"), 1).toFloat();
|
||||
ReadBasicSetting(Settings::values.mouse_panning_sensitivity);
|
||||
|
||||
ReadSettingGlobal(Settings::values.use_docked_mode, QStringLiteral("use_docked_mode"), true);
|
||||
ReadGlobalSetting(Settings::values.use_docked_mode);
|
||||
|
||||
// Disable docked mode if handheld is selected
|
||||
const auto controller_type = Settings::values.players.GetValue()[0].controller_type;
|
||||
@@ -523,11 +572,9 @@ void Config::ReadControlValues() {
|
||||
Settings::values.use_docked_mode.SetValue(false);
|
||||
}
|
||||
|
||||
ReadSettingGlobal(Settings::values.vibration_enabled, QStringLiteral("vibration_enabled"),
|
||||
true);
|
||||
ReadSettingGlobal(Settings::values.enable_accurate_vibrations,
|
||||
QStringLiteral("enable_accurate_vibrations"), false);
|
||||
ReadSettingGlobal(Settings::values.motion_enabled, QStringLiteral("motion_enabled"), true);
|
||||
ReadGlobalSetting(Settings::values.vibration_enabled);
|
||||
ReadGlobalSetting(Settings::values.enable_accurate_vibrations);
|
||||
ReadGlobalSetting(Settings::values.motion_enabled);
|
||||
|
||||
qt_config->endGroup();
|
||||
}
|
||||
@@ -565,33 +612,19 @@ void Config::ReadMotionTouchValues() {
|
||||
}
|
||||
qt_config->endArray();
|
||||
|
||||
Settings::values.motion_device =
|
||||
ReadSetting(QStringLiteral("motion_device"),
|
||||
QStringLiteral("engine:motion_emu,update_period:100,sensitivity:0.01"))
|
||||
.toString()
|
||||
.toStdString();
|
||||
Settings::values.touch_device =
|
||||
ReadSetting(QStringLiteral("touch_device"),
|
||||
QStringLiteral("min_x:100,min_y:50,max_x:1800,max_y:850"))
|
||||
.toString()
|
||||
.toStdString();
|
||||
Settings::values.use_touch_from_button =
|
||||
ReadSetting(QStringLiteral("use_touch_from_button"), false).toBool();
|
||||
Settings::values.touch_from_button_map_index =
|
||||
ReadSetting(QStringLiteral("touch_from_button_map"), 0).toInt();
|
||||
Settings::values.touch_from_button_map_index =
|
||||
std::clamp(Settings::values.touch_from_button_map_index, 0, num_touch_from_button_maps - 1);
|
||||
Settings::values.udp_input_servers =
|
||||
ReadSetting(QStringLiteral("udp_input_servers"),
|
||||
QString::fromUtf8(InputCommon::CemuhookUDP::DEFAULT_SRV))
|
||||
.toString()
|
||||
.toStdString();
|
||||
ReadBasicSetting(Settings::values.motion_device);
|
||||
ReadBasicSetting(Settings::values.touch_device);
|
||||
ReadBasicSetting(Settings::values.use_touch_from_button);
|
||||
ReadBasicSetting(Settings::values.touch_from_button_map_index);
|
||||
Settings::values.touch_from_button_map_index = std::clamp(
|
||||
Settings::values.touch_from_button_map_index.GetValue(), 0, num_touch_from_button_maps - 1);
|
||||
ReadBasicSetting(Settings::values.udp_input_servers);
|
||||
}
|
||||
|
||||
void Config::ReadCoreValues() {
|
||||
qt_config->beginGroup(QStringLiteral("Core"));
|
||||
|
||||
ReadSettingGlobal(Settings::values.use_multi_core, QStringLiteral("use_multi_core"), true);
|
||||
ReadGlobalSetting(Settings::values.use_multi_core);
|
||||
|
||||
qt_config->endGroup();
|
||||
}
|
||||
@@ -599,7 +632,7 @@ void Config::ReadCoreValues() {
|
||||
void Config::ReadDataStorageValues() {
|
||||
qt_config->beginGroup(QStringLiteral("Data Storage"));
|
||||
|
||||
Settings::values.use_virtual_sd = ReadSetting(QStringLiteral("use_virtual_sd"), true).toBool();
|
||||
ReadBasicSetting(Settings::values.use_virtual_sd);
|
||||
FS::SetYuzuPath(
|
||||
FS::YuzuPath::NANDDir,
|
||||
qt_config
|
||||
@@ -628,12 +661,9 @@ void Config::ReadDataStorageValues() {
|
||||
QString::fromStdString(FS::GetYuzuPathString(FS::YuzuPath::DumpDir)))
|
||||
.toString()
|
||||
.toStdString());
|
||||
Settings::values.gamecard_inserted =
|
||||
ReadSetting(QStringLiteral("gamecard_inserted"), false).toBool();
|
||||
Settings::values.gamecard_current_game =
|
||||
ReadSetting(QStringLiteral("gamecard_current_game"), false).toBool();
|
||||
Settings::values.gamecard_path =
|
||||
ReadSetting(QStringLiteral("gamecard_path"), QString{}).toString().toStdString();
|
||||
ReadBasicSetting(Settings::values.gamecard_inserted);
|
||||
ReadBasicSetting(Settings::values.gamecard_current_game);
|
||||
ReadBasicSetting(Settings::values.gamecard_path);
|
||||
|
||||
qt_config->endGroup();
|
||||
}
|
||||
@@ -644,34 +674,24 @@ void Config::ReadDebuggingValues() {
|
||||
// Intentionally not using the QT default setting as this is intended to be changed in the ini
|
||||
Settings::values.record_frame_times =
|
||||
qt_config->value(QStringLiteral("record_frame_times"), false).toBool();
|
||||
Settings::values.program_args =
|
||||
ReadSetting(QStringLiteral("program_args"), QString{}).toString().toStdString();
|
||||
Settings::values.dump_exefs = ReadSetting(QStringLiteral("dump_exefs"), false).toBool();
|
||||
Settings::values.dump_nso = ReadSetting(QStringLiteral("dump_nso"), false).toBool();
|
||||
Settings::values.enable_fs_access_log =
|
||||
ReadSetting(QStringLiteral("enable_fs_access_log"), false).toBool();
|
||||
Settings::values.reporting_services =
|
||||
ReadSetting(QStringLiteral("reporting_services"), false).toBool();
|
||||
Settings::values.quest_flag = ReadSetting(QStringLiteral("quest_flag"), false).toBool();
|
||||
Settings::values.disable_macro_jit =
|
||||
ReadSetting(QStringLiteral("disable_macro_jit"), false).toBool();
|
||||
Settings::values.extended_logging =
|
||||
ReadSetting(QStringLiteral("extended_logging"), false).toBool();
|
||||
Settings::values.use_debug_asserts =
|
||||
ReadSetting(QStringLiteral("use_debug_asserts"), false).toBool();
|
||||
Settings::values.use_auto_stub = ReadSetting(QStringLiteral("use_auto_stub"), false).toBool();
|
||||
ReadBasicSetting(Settings::values.program_args);
|
||||
ReadBasicSetting(Settings::values.dump_exefs);
|
||||
ReadBasicSetting(Settings::values.dump_nso);
|
||||
ReadBasicSetting(Settings::values.enable_fs_access_log);
|
||||
ReadBasicSetting(Settings::values.reporting_services);
|
||||
ReadBasicSetting(Settings::values.quest_flag);
|
||||
ReadBasicSetting(Settings::values.disable_macro_jit);
|
||||
ReadBasicSetting(Settings::values.extended_logging);
|
||||
ReadBasicSetting(Settings::values.use_debug_asserts);
|
||||
ReadBasicSetting(Settings::values.use_auto_stub);
|
||||
|
||||
qt_config->endGroup();
|
||||
}
|
||||
|
||||
void Config::ReadServiceValues() {
|
||||
qt_config->beginGroup(QStringLiteral("Services"));
|
||||
Settings::values.bcat_backend =
|
||||
ReadSetting(QStringLiteral("bcat_backend"), QStringLiteral("none"))
|
||||
.toString()
|
||||
.toStdString();
|
||||
Settings::values.bcat_boxcat_local =
|
||||
ReadSetting(QStringLiteral("bcat_boxcat_local"), false).toBool();
|
||||
ReadBasicSetting(Settings::values.bcat_backend);
|
||||
ReadBasicSetting(Settings::values.bcat_boxcat_local);
|
||||
qt_config->endGroup();
|
||||
}
|
||||
|
||||
@@ -697,11 +717,8 @@ void Config::ReadDisabledAddOnValues() {
|
||||
void Config::ReadMiscellaneousValues() {
|
||||
qt_config->beginGroup(QStringLiteral("Miscellaneous"));
|
||||
|
||||
Settings::values.log_filter =
|
||||
ReadSetting(QStringLiteral("log_filter"), QStringLiteral("*:Info"))
|
||||
.toString()
|
||||
.toStdString();
|
||||
Settings::values.use_dev_keys = ReadSetting(QStringLiteral("use_dev_keys"), false).toBool();
|
||||
ReadBasicSetting(Settings::values.log_filter);
|
||||
ReadBasicSetting(Settings::values.use_dev_keys);
|
||||
|
||||
qt_config->endGroup();
|
||||
}
|
||||
@@ -751,38 +768,24 @@ void Config::ReadPathValues() {
|
||||
void Config::ReadCpuValues() {
|
||||
qt_config->beginGroup(QStringLiteral("Cpu"));
|
||||
|
||||
ReadSettingGlobal(Settings::values.cpu_accuracy, QStringLiteral("cpu_accuracy"), 0);
|
||||
ReadGlobalSetting(Settings::values.cpu_accuracy);
|
||||
|
||||
ReadSettingGlobal(Settings::values.cpuopt_unsafe_unfuse_fma,
|
||||
QStringLiteral("cpuopt_unsafe_unfuse_fma"), true);
|
||||
ReadSettingGlobal(Settings::values.cpuopt_unsafe_reduce_fp_error,
|
||||
QStringLiteral("cpuopt_unsafe_reduce_fp_error"), true);
|
||||
ReadSettingGlobal(Settings::values.cpuopt_unsafe_ignore_standard_fpcr,
|
||||
QStringLiteral("cpuopt_unsafe_ignore_standard_fpcr"), true);
|
||||
ReadSettingGlobal(Settings::values.cpuopt_unsafe_inaccurate_nan,
|
||||
QStringLiteral("cpuopt_unsafe_inaccurate_nan"), true);
|
||||
ReadSettingGlobal(Settings::values.cpuopt_unsafe_fastmem_check,
|
||||
QStringLiteral("cpuopt_unsafe_fastmem_check"), true);
|
||||
ReadGlobalSetting(Settings::values.cpuopt_unsafe_unfuse_fma);
|
||||
ReadGlobalSetting(Settings::values.cpuopt_unsafe_reduce_fp_error);
|
||||
ReadGlobalSetting(Settings::values.cpuopt_unsafe_ignore_standard_fpcr);
|
||||
ReadGlobalSetting(Settings::values.cpuopt_unsafe_inaccurate_nan);
|
||||
ReadGlobalSetting(Settings::values.cpuopt_unsafe_fastmem_check);
|
||||
|
||||
if (global) {
|
||||
Settings::values.cpuopt_page_tables =
|
||||
ReadSetting(QStringLiteral("cpuopt_page_tables"), true).toBool();
|
||||
Settings::values.cpuopt_block_linking =
|
||||
ReadSetting(QStringLiteral("cpuopt_block_linking"), true).toBool();
|
||||
Settings::values.cpuopt_return_stack_buffer =
|
||||
ReadSetting(QStringLiteral("cpuopt_return_stack_buffer"), true).toBool();
|
||||
Settings::values.cpuopt_fast_dispatcher =
|
||||
ReadSetting(QStringLiteral("cpuopt_fast_dispatcher"), true).toBool();
|
||||
Settings::values.cpuopt_context_elimination =
|
||||
ReadSetting(QStringLiteral("cpuopt_context_elimination"), true).toBool();
|
||||
Settings::values.cpuopt_const_prop =
|
||||
ReadSetting(QStringLiteral("cpuopt_const_prop"), true).toBool();
|
||||
Settings::values.cpuopt_misc_ir =
|
||||
ReadSetting(QStringLiteral("cpuopt_misc_ir"), true).toBool();
|
||||
Settings::values.cpuopt_reduce_misalign_checks =
|
||||
ReadSetting(QStringLiteral("cpuopt_reduce_misalign_checks"), true).toBool();
|
||||
Settings::values.cpuopt_fastmem =
|
||||
ReadSetting(QStringLiteral("cpuopt_fastmem"), true).toBool();
|
||||
ReadBasicSetting(Settings::values.cpuopt_page_tables);
|
||||
ReadBasicSetting(Settings::values.cpuopt_block_linking);
|
||||
ReadBasicSetting(Settings::values.cpuopt_return_stack_buffer);
|
||||
ReadBasicSetting(Settings::values.cpuopt_fast_dispatcher);
|
||||
ReadBasicSetting(Settings::values.cpuopt_context_elimination);
|
||||
ReadBasicSetting(Settings::values.cpuopt_const_prop);
|
||||
ReadBasicSetting(Settings::values.cpuopt_misc_ir);
|
||||
ReadBasicSetting(Settings::values.cpuopt_reduce_misalign_checks);
|
||||
ReadBasicSetting(Settings::values.cpuopt_fastmem);
|
||||
}
|
||||
|
||||
qt_config->endGroup();
|
||||
@@ -791,41 +794,28 @@ void Config::ReadCpuValues() {
|
||||
void Config::ReadRendererValues() {
|
||||
qt_config->beginGroup(QStringLiteral("Renderer"));
|
||||
|
||||
ReadSettingGlobal(Settings::values.renderer_backend, QStringLiteral("backend"), 0);
|
||||
ReadSettingGlobal(Settings::values.renderer_debug, QStringLiteral("debug"), false);
|
||||
ReadSettingGlobal(Settings::values.vulkan_device, QStringLiteral("vulkan_device"), 0);
|
||||
#ifdef _WIN32
|
||||
ReadSettingGlobal(Settings::values.fullscreen_mode, QStringLiteral("fullscreen_mode"), 0);
|
||||
#else
|
||||
// *nix platforms may have issues with the borderless windowed fullscreen mode.
|
||||
// Default to exclusive fullscreen on these platforms for now.
|
||||
ReadSettingGlobal(Settings::values.fullscreen_mode, QStringLiteral("fullscreen_mode"), 1);
|
||||
#endif
|
||||
ReadSettingGlobal(Settings::values.aspect_ratio, QStringLiteral("aspect_ratio"), 0);
|
||||
ReadSettingGlobal(Settings::values.max_anisotropy, QStringLiteral("max_anisotropy"), 0);
|
||||
ReadSettingGlobal(Settings::values.use_frame_limit, QStringLiteral("use_frame_limit"), true);
|
||||
ReadSettingGlobal(Settings::values.frame_limit, QStringLiteral("frame_limit"), 100);
|
||||
ReadSettingGlobal(Settings::values.use_disk_shader_cache,
|
||||
QStringLiteral("use_disk_shader_cache"), true);
|
||||
ReadSettingGlobal(Settings::values.gpu_accuracy, QStringLiteral("gpu_accuracy"), 1);
|
||||
ReadSettingGlobal(Settings::values.use_asynchronous_gpu_emulation,
|
||||
QStringLiteral("use_asynchronous_gpu_emulation"), true);
|
||||
ReadSettingGlobal(Settings::values.use_nvdec_emulation, QStringLiteral("use_nvdec_emulation"),
|
||||
true);
|
||||
ReadSettingGlobal(Settings::values.accelerate_astc, QStringLiteral("accelerate_astc"), true);
|
||||
ReadSettingGlobal(Settings::values.use_vsync, QStringLiteral("use_vsync"), true);
|
||||
ReadSettingGlobal(Settings::values.disable_fps_limit, QStringLiteral("disable_fps_limit"),
|
||||
false);
|
||||
ReadSettingGlobal(Settings::values.use_assembly_shaders, QStringLiteral("use_assembly_shaders"),
|
||||
false);
|
||||
ReadSettingGlobal(Settings::values.use_asynchronous_shaders,
|
||||
QStringLiteral("use_asynchronous_shaders"), false);
|
||||
ReadSettingGlobal(Settings::values.use_fast_gpu_time, QStringLiteral("use_fast_gpu_time"),
|
||||
true);
|
||||
ReadSettingGlobal(Settings::values.use_caches_gc, QStringLiteral("use_caches_gc"), false);
|
||||
ReadSettingGlobal(Settings::values.bg_red, QStringLiteral("bg_red"), 0.0);
|
||||
ReadSettingGlobal(Settings::values.bg_green, QStringLiteral("bg_green"), 0.0);
|
||||
ReadSettingGlobal(Settings::values.bg_blue, QStringLiteral("bg_blue"), 0.0);
|
||||
ReadGlobalSetting(Settings::values.renderer_backend);
|
||||
ReadBasicSetting(Settings::values.renderer_debug);
|
||||
ReadGlobalSetting(Settings::values.vulkan_device);
|
||||
ReadGlobalSetting(Settings::values.fullscreen_mode);
|
||||
ReadGlobalSetting(Settings::values.aspect_ratio);
|
||||
ReadGlobalSetting(Settings::values.max_anisotropy);
|
||||
ReadGlobalSetting(Settings::values.use_frame_limit);
|
||||
ReadGlobalSetting(Settings::values.frame_limit);
|
||||
ReadGlobalSetting(Settings::values.use_disk_shader_cache);
|
||||
ReadGlobalSetting(Settings::values.gpu_accuracy);
|
||||
ReadGlobalSetting(Settings::values.use_asynchronous_gpu_emulation);
|
||||
ReadGlobalSetting(Settings::values.use_nvdec_emulation);
|
||||
ReadGlobalSetting(Settings::values.accelerate_astc);
|
||||
ReadGlobalSetting(Settings::values.use_vsync);
|
||||
ReadGlobalSetting(Settings::values.disable_fps_limit);
|
||||
ReadGlobalSetting(Settings::values.use_assembly_shaders);
|
||||
ReadGlobalSetting(Settings::values.use_asynchronous_shaders);
|
||||
ReadGlobalSetting(Settings::values.use_fast_gpu_time);
|
||||
ReadGlobalSetting(Settings::values.use_caches_gc);
|
||||
ReadGlobalSetting(Settings::values.bg_red);
|
||||
ReadGlobalSetting(Settings::values.bg_green);
|
||||
ReadGlobalSetting(Settings::values.bg_blue);
|
||||
|
||||
qt_config->endGroup();
|
||||
}
|
||||
@@ -870,15 +860,15 @@ void Config::ReadShortcutValues() {
|
||||
void Config::ReadSystemValues() {
|
||||
qt_config->beginGroup(QStringLiteral("System"));
|
||||
|
||||
ReadSettingGlobal(Settings::values.current_user, QStringLiteral("current_user"), 0);
|
||||
Settings::values.current_user =
|
||||
std::clamp<int>(Settings::values.current_user, 0, Service::Account::MAX_USERS - 1);
|
||||
ReadBasicSetting(Settings::values.current_user);
|
||||
Settings::values.current_user = std::clamp<int>(Settings::values.current_user.GetValue(), 0,
|
||||
Service::Account::MAX_USERS - 1);
|
||||
|
||||
ReadSettingGlobal(Settings::values.language_index, QStringLiteral("language_index"), 1);
|
||||
ReadGlobalSetting(Settings::values.language_index);
|
||||
|
||||
ReadSettingGlobal(Settings::values.region_index, QStringLiteral("region_index"), 1);
|
||||
ReadGlobalSetting(Settings::values.region_index);
|
||||
|
||||
ReadSettingGlobal(Settings::values.time_zone_index, QStringLiteral("time_zone_index"), 0);
|
||||
ReadGlobalSetting(Settings::values.time_zone_index);
|
||||
|
||||
bool rng_seed_enabled;
|
||||
ReadSettingGlobal(rng_seed_enabled, QStringLiteral("rng_seed_enabled"), false);
|
||||
@@ -904,7 +894,7 @@ void Config::ReadSystemValues() {
|
||||
}
|
||||
}
|
||||
|
||||
ReadSettingGlobal(Settings::values.sound_index, QStringLiteral("sound_index"), 1);
|
||||
ReadGlobalSetting(Settings::values.sound_index);
|
||||
|
||||
qt_config->endGroup();
|
||||
}
|
||||
@@ -915,10 +905,8 @@ void Config::ReadUIValues() {
|
||||
UISettings::values.theme =
|
||||
ReadSetting(QStringLiteral("theme"), QString::fromUtf8(UISettings::themes[0].second))
|
||||
.toString();
|
||||
UISettings::values.enable_discord_presence =
|
||||
ReadSetting(QStringLiteral("enable_discord_presence"), true).toBool();
|
||||
UISettings::values.select_user_on_boot =
|
||||
ReadSetting(QStringLiteral("select_user_on_boot"), false).toBool();
|
||||
ReadBasicSetting(UISettings::values.enable_discord_presence);
|
||||
ReadBasicSetting(UISettings::values.select_user_on_boot);
|
||||
|
||||
ReadUIGamelistValues();
|
||||
ReadUILayoutValues();
|
||||
@@ -926,24 +914,17 @@ void Config::ReadUIValues() {
|
||||
ReadScreenshotValues();
|
||||
ReadShortcutValues();
|
||||
|
||||
UISettings::values.single_window_mode =
|
||||
ReadSetting(QStringLiteral("singleWindowMode"), true).toBool();
|
||||
UISettings::values.fullscreen = ReadSetting(QStringLiteral("fullscreen"), false).toBool();
|
||||
UISettings::values.display_titlebar =
|
||||
ReadSetting(QStringLiteral("displayTitleBars"), true).toBool();
|
||||
UISettings::values.show_filter_bar =
|
||||
ReadSetting(QStringLiteral("showFilterBar"), true).toBool();
|
||||
UISettings::values.show_status_bar =
|
||||
ReadSetting(QStringLiteral("showStatusBar"), true).toBool();
|
||||
UISettings::values.confirm_before_closing =
|
||||
ReadSetting(QStringLiteral("confirmClose"), true).toBool();
|
||||
UISettings::values.first_start = ReadSetting(QStringLiteral("firstStart"), true).toBool();
|
||||
UISettings::values.callout_flags = ReadSetting(QStringLiteral("calloutFlags"), 0).toUInt();
|
||||
UISettings::values.show_console = ReadSetting(QStringLiteral("showConsole"), false).toBool();
|
||||
UISettings::values.pause_when_in_background =
|
||||
ReadSetting(QStringLiteral("pauseWhenInBackground"), false).toBool();
|
||||
UISettings::values.hide_mouse =
|
||||
ReadSetting(QStringLiteral("hideInactiveMouse"), false).toBool();
|
||||
ReadBasicSetting(UISettings::values.single_window_mode);
|
||||
ReadBasicSetting(UISettings::values.fullscreen);
|
||||
ReadBasicSetting(UISettings::values.display_titlebar);
|
||||
ReadBasicSetting(UISettings::values.show_filter_bar);
|
||||
ReadBasicSetting(UISettings::values.show_status_bar);
|
||||
ReadBasicSetting(UISettings::values.confirm_before_closing);
|
||||
ReadBasicSetting(UISettings::values.first_start);
|
||||
ReadBasicSetting(UISettings::values.callout_flags);
|
||||
ReadBasicSetting(UISettings::values.show_console);
|
||||
ReadBasicSetting(UISettings::values.pause_when_in_background);
|
||||
ReadBasicSetting(UISettings::values.hide_mouse);
|
||||
|
||||
qt_config->endGroup();
|
||||
}
|
||||
@@ -951,12 +932,11 @@ void Config::ReadUIValues() {
|
||||
void Config::ReadUIGamelistValues() {
|
||||
qt_config->beginGroup(QStringLiteral("UIGameList"));
|
||||
|
||||
UISettings::values.show_add_ons = ReadSetting(QStringLiteral("show_add_ons"), true).toBool();
|
||||
UISettings::values.icon_size = ReadSetting(QStringLiteral("icon_size"), 64).toUInt();
|
||||
UISettings::values.row_1_text_id = ReadSetting(QStringLiteral("row_1_text_id"), 3).toUInt();
|
||||
UISettings::values.row_2_text_id = ReadSetting(QStringLiteral("row_2_text_id"), 2).toUInt();
|
||||
UISettings::values.cache_game_list =
|
||||
ReadSetting(QStringLiteral("cache_game_list"), true).toBool();
|
||||
ReadBasicSetting(UISettings::values.show_add_ons);
|
||||
ReadBasicSetting(UISettings::values.icon_size);
|
||||
ReadBasicSetting(UISettings::values.row_1_text_id);
|
||||
ReadBasicSetting(UISettings::values.row_2_text_id);
|
||||
ReadBasicSetting(UISettings::values.cache_game_list);
|
||||
const int favorites_size = qt_config->beginReadArray(QStringLiteral("favorites"));
|
||||
for (int i = 0; i < favorites_size; i++) {
|
||||
qt_config->setArrayIndex(i);
|
||||
@@ -979,8 +959,7 @@ void Config::ReadUILayoutValues() {
|
||||
ReadSetting(QStringLiteral("gameListHeaderState")).toByteArray();
|
||||
UISettings::values.microprofile_geometry =
|
||||
ReadSetting(QStringLiteral("microProfileDialogGeometry")).toByteArray();
|
||||
UISettings::values.microprofile_visible =
|
||||
ReadSetting(QStringLiteral("microProfileDialogVisible"), false).toBool();
|
||||
ReadBasicSetting(UISettings::values.microprofile_visible);
|
||||
|
||||
qt_config->endGroup();
|
||||
}
|
||||
@@ -988,16 +967,10 @@ void Config::ReadUILayoutValues() {
|
||||
void Config::ReadWebServiceValues() {
|
||||
qt_config->beginGroup(QStringLiteral("WebService"));
|
||||
|
||||
Settings::values.enable_telemetry =
|
||||
ReadSetting(QStringLiteral("enable_telemetry"), true).toBool();
|
||||
Settings::values.web_api_url =
|
||||
ReadSetting(QStringLiteral("web_api_url"), QStringLiteral("https://api.yuzu-emu.org"))
|
||||
.toString()
|
||||
.toStdString();
|
||||
Settings::values.yuzu_username =
|
||||
ReadSetting(QStringLiteral("yuzu_username")).toString().toStdString();
|
||||
Settings::values.yuzu_token =
|
||||
ReadSetting(QStringLiteral("yuzu_token")).toString().toStdString();
|
||||
ReadBasicSetting(Settings::values.enable_telemetry);
|
||||
ReadBasicSetting(Settings::values.web_api_url);
|
||||
ReadBasicSetting(Settings::values.yuzu_username);
|
||||
ReadBasicSetting(Settings::values.yuzu_token);
|
||||
|
||||
qt_config->endGroup();
|
||||
}
|
||||
@@ -1083,7 +1056,7 @@ void Config::SavePlayerValue(std::size_t player_index) {
|
||||
}
|
||||
|
||||
void Config::SaveDebugValues() {
|
||||
WriteSetting(QStringLiteral("debug_pad_enabled"), Settings::values.debug_pad_enabled, false);
|
||||
WriteBasicSetting(Settings::values.debug_pad_enabled);
|
||||
for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) {
|
||||
const std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]);
|
||||
WriteSetting(QStringLiteral("debug_pad_") +
|
||||
@@ -1103,7 +1076,7 @@ void Config::SaveDebugValues() {
|
||||
}
|
||||
|
||||
void Config::SaveMouseValues() {
|
||||
WriteSetting(QStringLiteral("mouse_enabled"), Settings::values.mouse_enabled, false);
|
||||
WriteBasicSetting(Settings::values.mouse_enabled);
|
||||
|
||||
for (int i = 0; i < Settings::NativeMouseButton::NumMouseButtons; ++i) {
|
||||
const std::string default_param =
|
||||
@@ -1126,19 +1099,11 @@ void Config::SaveTouchscreenValues() {
|
||||
}
|
||||
|
||||
void Config::SaveMotionTouchValues() {
|
||||
WriteSetting(QStringLiteral("motion_device"),
|
||||
QString::fromStdString(Settings::values.motion_device),
|
||||
QStringLiteral("engine:motion_emu,update_period:100,sensitivity:0.01"));
|
||||
WriteSetting(QStringLiteral("touch_device"),
|
||||
QString::fromStdString(Settings::values.touch_device),
|
||||
QStringLiteral("engine:emu_window"));
|
||||
WriteSetting(QStringLiteral("use_touch_from_button"), Settings::values.use_touch_from_button,
|
||||
false);
|
||||
WriteSetting(QStringLiteral("touch_from_button_map"),
|
||||
Settings::values.touch_from_button_map_index, 0);
|
||||
WriteSetting(QStringLiteral("udp_input_servers"),
|
||||
QString::fromStdString(Settings::values.udp_input_servers),
|
||||
QString::fromUtf8(InputCommon::CemuhookUDP::DEFAULT_SRV));
|
||||
WriteBasicSetting(Settings::values.motion_device);
|
||||
WriteBasicSetting(Settings::values.touch_device);
|
||||
WriteBasicSetting(Settings::values.use_touch_from_button);
|
||||
WriteBasicSetting(Settings::values.touch_from_button_map_index);
|
||||
WriteBasicSetting(Settings::values.udp_input_servers);
|
||||
|
||||
qt_config->beginWriteArray(QStringLiteral("touch_from_button_maps"));
|
||||
for (std::size_t p = 0; p < Settings::values.touch_from_button_maps.size(); ++p) {
|
||||
@@ -1181,15 +1146,11 @@ void Config::SaveAudioValues() {
|
||||
qt_config->beginGroup(QStringLiteral("Audio"));
|
||||
|
||||
if (global) {
|
||||
WriteSetting(QStringLiteral("output_engine"),
|
||||
QString::fromStdString(Settings::values.sink_id), QStringLiteral("auto"));
|
||||
WriteSetting(QStringLiteral("output_device"),
|
||||
QString::fromStdString(Settings::values.audio_device_id),
|
||||
QStringLiteral("auto"));
|
||||
WriteBasicSetting(Settings::values.sink_id);
|
||||
WriteBasicSetting(Settings::values.audio_device_id);
|
||||
}
|
||||
WriteSettingGlobal(QStringLiteral("enable_audio_stretching"),
|
||||
Settings::values.enable_audio_stretching, true);
|
||||
WriteSettingGlobal(QStringLiteral("volume"), Settings::values.volume, 1.0f);
|
||||
WriteGlobalSetting(Settings::values.enable_audio_stretching);
|
||||
WriteGlobalSetting(Settings::values.volume);
|
||||
|
||||
qt_config->endGroup();
|
||||
}
|
||||
@@ -1205,30 +1166,21 @@ void Config::SaveControlValues() {
|
||||
SaveTouchscreenValues();
|
||||
SaveMotionTouchValues();
|
||||
|
||||
WriteSettingGlobal(QStringLiteral("use_docked_mode"), Settings::values.use_docked_mode, true);
|
||||
WriteSettingGlobal(QStringLiteral("vibration_enabled"), Settings::values.vibration_enabled,
|
||||
true);
|
||||
WriteSettingGlobal(QStringLiteral("enable_accurate_vibrations"),
|
||||
Settings::values.enable_accurate_vibrations, false);
|
||||
WriteSettingGlobal(QStringLiteral("motion_enabled"), Settings::values.motion_enabled, true);
|
||||
WriteSetting(QStringLiteral("motion_device"),
|
||||
QString::fromStdString(Settings::values.motion_device),
|
||||
QStringLiteral("engine:motion_emu,update_period:100,sensitivity:0.01"));
|
||||
WriteSetting(QStringLiteral("touch_device"),
|
||||
QString::fromStdString(Settings::values.touch_device),
|
||||
QStringLiteral("engine:emu_window"));
|
||||
WriteSetting(QStringLiteral("keyboard_enabled"), Settings::values.keyboard_enabled, false);
|
||||
WriteSetting(QStringLiteral("emulate_analog_keyboard"),
|
||||
Settings::values.emulate_analog_keyboard, false);
|
||||
WriteSetting(QStringLiteral("mouse_panning_sensitivity"),
|
||||
Settings::values.mouse_panning_sensitivity, 1.0f);
|
||||
WriteGlobalSetting(Settings::values.use_docked_mode);
|
||||
WriteGlobalSetting(Settings::values.vibration_enabled);
|
||||
WriteGlobalSetting(Settings::values.enable_accurate_vibrations);
|
||||
WriteGlobalSetting(Settings::values.motion_enabled);
|
||||
WriteBasicSetting(Settings::values.keyboard_enabled);
|
||||
WriteBasicSetting(Settings::values.emulate_analog_keyboard);
|
||||
WriteBasicSetting(Settings::values.mouse_panning_sensitivity);
|
||||
|
||||
qt_config->endGroup();
|
||||
}
|
||||
|
||||
void Config::SaveCoreValues() {
|
||||
qt_config->beginGroup(QStringLiteral("Core"));
|
||||
|
||||
WriteSettingGlobal(QStringLiteral("use_multi_core"), Settings::values.use_multi_core, true);
|
||||
WriteGlobalSetting(Settings::values.use_multi_core);
|
||||
|
||||
qt_config->endGroup();
|
||||
}
|
||||
@@ -1236,7 +1188,7 @@ void Config::SaveCoreValues() {
|
||||
void Config::SaveDataStorageValues() {
|
||||
qt_config->beginGroup(QStringLiteral("Data Storage"));
|
||||
|
||||
WriteSetting(QStringLiteral("use_virtual_sd"), Settings::values.use_virtual_sd, true);
|
||||
WriteBasicSetting(Settings::values.use_virtual_sd);
|
||||
WriteSetting(QStringLiteral("nand_directory"),
|
||||
QString::fromStdString(FS::GetYuzuPathString(FS::YuzuPath::NANDDir)),
|
||||
QString::fromStdString(FS::GetYuzuPathString(FS::YuzuPath::NANDDir)));
|
||||
@@ -1249,11 +1201,9 @@ void Config::SaveDataStorageValues() {
|
||||
WriteSetting(QStringLiteral("dump_directory"),
|
||||
QString::fromStdString(FS::GetYuzuPathString(FS::YuzuPath::DumpDir)),
|
||||
QString::fromStdString(FS::GetYuzuPathString(FS::YuzuPath::DumpDir)));
|
||||
WriteSetting(QStringLiteral("gamecard_inserted"), Settings::values.gamecard_inserted, false);
|
||||
WriteSetting(QStringLiteral("gamecard_current_game"), Settings::values.gamecard_current_game,
|
||||
false);
|
||||
WriteSetting(QStringLiteral("gamecard_path"),
|
||||
QString::fromStdString(Settings::values.gamecard_path), QString{});
|
||||
WriteBasicSetting(Settings::values.gamecard_inserted);
|
||||
WriteBasicSetting(Settings::values.gamecard_current_game);
|
||||
WriteBasicSetting(Settings::values.gamecard_path);
|
||||
|
||||
qt_config->endGroup();
|
||||
}
|
||||
@@ -1263,24 +1213,23 @@ void Config::SaveDebuggingValues() {
|
||||
|
||||
// Intentionally not using the QT default setting as this is intended to be changed in the ini
|
||||
qt_config->setValue(QStringLiteral("record_frame_times"), Settings::values.record_frame_times);
|
||||
WriteSetting(QStringLiteral("program_args"),
|
||||
QString::fromStdString(Settings::values.program_args), QString{});
|
||||
WriteSetting(QStringLiteral("dump_exefs"), Settings::values.dump_exefs, false);
|
||||
WriteSetting(QStringLiteral("dump_nso"), Settings::values.dump_nso, false);
|
||||
WriteSetting(QStringLiteral("enable_fs_access_log"), Settings::values.enable_fs_access_log,
|
||||
false);
|
||||
WriteSetting(QStringLiteral("quest_flag"), Settings::values.quest_flag, false);
|
||||
WriteSetting(QStringLiteral("use_debug_asserts"), Settings::values.use_debug_asserts, false);
|
||||
WriteSetting(QStringLiteral("disable_macro_jit"), Settings::values.disable_macro_jit, false);
|
||||
WriteBasicSetting(Settings::values.program_args);
|
||||
WriteBasicSetting(Settings::values.dump_exefs);
|
||||
WriteBasicSetting(Settings::values.dump_nso);
|
||||
WriteBasicSetting(Settings::values.enable_fs_access_log);
|
||||
WriteBasicSetting(Settings::values.quest_flag);
|
||||
WriteBasicSetting(Settings::values.use_debug_asserts);
|
||||
WriteBasicSetting(Settings::values.disable_macro_jit);
|
||||
|
||||
qt_config->endGroup();
|
||||
}
|
||||
|
||||
void Config::SaveServiceValues() {
|
||||
qt_config->beginGroup(QStringLiteral("Services"));
|
||||
WriteSetting(QStringLiteral("bcat_backend"),
|
||||
QString::fromStdString(Settings::values.bcat_backend), QStringLiteral("none"));
|
||||
WriteSetting(QStringLiteral("bcat_boxcat_local"), Settings::values.bcat_boxcat_local, false);
|
||||
|
||||
WriteBasicSetting(Settings::values.bcat_backend);
|
||||
WriteBasicSetting(Settings::values.bcat_boxcat_local);
|
||||
|
||||
qt_config->endGroup();
|
||||
}
|
||||
|
||||
@@ -1306,9 +1255,8 @@ void Config::SaveDisabledAddOnValues() {
|
||||
void Config::SaveMiscellaneousValues() {
|
||||
qt_config->beginGroup(QStringLiteral("Miscellaneous"));
|
||||
|
||||
WriteSetting(QStringLiteral("log_filter"), QString::fromStdString(Settings::values.log_filter),
|
||||
QStringLiteral("*:Info"));
|
||||
WriteSetting(QStringLiteral("use_dev_keys"), Settings::values.use_dev_keys, false);
|
||||
WriteBasicSetting(Settings::values.log_filter);
|
||||
WriteBasicSetting(Settings::values.use_dev_keys);
|
||||
|
||||
qt_config->endGroup();
|
||||
}
|
||||
@@ -1336,38 +1284,27 @@ void Config::SavePathValues() {
|
||||
void Config::SaveCpuValues() {
|
||||
qt_config->beginGroup(QStringLiteral("Cpu"));
|
||||
|
||||
WriteSettingGlobal(QStringLiteral("cpu_accuracy"),
|
||||
static_cast<u32>(Settings::values.cpu_accuracy.GetValue(global)),
|
||||
Settings::values.cpu_accuracy.UsingGlobal(),
|
||||
static_cast<u32>(Settings::CPUAccuracy::Accurate));
|
||||
WriteSetting(QStringLiteral("cpu_accuracy"),
|
||||
static_cast<u32>(Settings::values.cpu_accuracy.GetValue(global)),
|
||||
static_cast<u32>(Settings::values.cpu_accuracy.GetDefault()),
|
||||
Settings::values.cpu_accuracy.UsingGlobal());
|
||||
|
||||
WriteSettingGlobal(QStringLiteral("cpuopt_unsafe_unfuse_fma"),
|
||||
Settings::values.cpuopt_unsafe_unfuse_fma, true);
|
||||
WriteSettingGlobal(QStringLiteral("cpuopt_unsafe_reduce_fp_error"),
|
||||
Settings::values.cpuopt_unsafe_reduce_fp_error, true);
|
||||
WriteSettingGlobal(QStringLiteral("cpuopt_unsafe_ignore_standard_fpcr"),
|
||||
Settings::values.cpuopt_unsafe_ignore_standard_fpcr, true);
|
||||
WriteSettingGlobal(QStringLiteral("cpuopt_unsafe_inaccurate_nan"),
|
||||
Settings::values.cpuopt_unsafe_inaccurate_nan, true);
|
||||
WriteSettingGlobal(QStringLiteral("cpuopt_unsafe_fastmem_check"),
|
||||
Settings::values.cpuopt_unsafe_fastmem_check, true);
|
||||
WriteGlobalSetting(Settings::values.cpuopt_unsafe_unfuse_fma);
|
||||
WriteGlobalSetting(Settings::values.cpuopt_unsafe_reduce_fp_error);
|
||||
WriteGlobalSetting(Settings::values.cpuopt_unsafe_ignore_standard_fpcr);
|
||||
WriteGlobalSetting(Settings::values.cpuopt_unsafe_inaccurate_nan);
|
||||
WriteGlobalSetting(Settings::values.cpuopt_unsafe_fastmem_check);
|
||||
|
||||
if (global) {
|
||||
WriteSetting(QStringLiteral("cpuopt_page_tables"), Settings::values.cpuopt_page_tables,
|
||||
true);
|
||||
WriteSetting(QStringLiteral("cpuopt_block_linking"), Settings::values.cpuopt_block_linking,
|
||||
true);
|
||||
WriteSetting(QStringLiteral("cpuopt_return_stack_buffer"),
|
||||
Settings::values.cpuopt_return_stack_buffer, true);
|
||||
WriteSetting(QStringLiteral("cpuopt_fast_dispatcher"),
|
||||
Settings::values.cpuopt_fast_dispatcher, true);
|
||||
WriteSetting(QStringLiteral("cpuopt_context_elimination"),
|
||||
Settings::values.cpuopt_context_elimination, true);
|
||||
WriteSetting(QStringLiteral("cpuopt_const_prop"), Settings::values.cpuopt_const_prop, true);
|
||||
WriteSetting(QStringLiteral("cpuopt_misc_ir"), Settings::values.cpuopt_misc_ir, true);
|
||||
WriteSetting(QStringLiteral("cpuopt_reduce_misalign_checks"),
|
||||
Settings::values.cpuopt_reduce_misalign_checks, true);
|
||||
WriteSetting(QStringLiteral("cpuopt_fastmem"), Settings::values.cpuopt_fastmem, true);
|
||||
WriteBasicSetting(Settings::values.cpuopt_page_tables);
|
||||
WriteBasicSetting(Settings::values.cpuopt_block_linking);
|
||||
WriteBasicSetting(Settings::values.cpuopt_return_stack_buffer);
|
||||
WriteBasicSetting(Settings::values.cpuopt_fast_dispatcher);
|
||||
WriteBasicSetting(Settings::values.cpuopt_context_elimination);
|
||||
WriteBasicSetting(Settings::values.cpuopt_const_prop);
|
||||
WriteBasicSetting(Settings::values.cpuopt_misc_ir);
|
||||
WriteBasicSetting(Settings::values.cpuopt_reduce_misalign_checks);
|
||||
WriteBasicSetting(Settings::values.cpuopt_fastmem);
|
||||
}
|
||||
|
||||
qt_config->endGroup();
|
||||
@@ -1376,46 +1313,34 @@ void Config::SaveCpuValues() {
|
||||
void Config::SaveRendererValues() {
|
||||
qt_config->beginGroup(QStringLiteral("Renderer"));
|
||||
|
||||
WriteSettingGlobal(QStringLiteral("backend"),
|
||||
static_cast<int>(Settings::values.renderer_backend.GetValue(global)),
|
||||
Settings::values.renderer_backend.UsingGlobal(), 0);
|
||||
WriteSetting(QStringLiteral("debug"), Settings::values.renderer_debug, false);
|
||||
WriteSettingGlobal(QStringLiteral("vulkan_device"), Settings::values.vulkan_device, 0);
|
||||
#ifdef _WIN32
|
||||
WriteSettingGlobal(QStringLiteral("fullscreen_mode"), Settings::values.fullscreen_mode, 0);
|
||||
#else
|
||||
// *nix platforms may have issues with the borderless windowed fullscreen mode.
|
||||
// Default to exclusive fullscreen on these platforms for now.
|
||||
WriteSettingGlobal(QStringLiteral("fullscreen_mode"), Settings::values.fullscreen_mode, 1);
|
||||
#endif
|
||||
WriteSettingGlobal(QStringLiteral("aspect_ratio"), Settings::values.aspect_ratio, 0);
|
||||
WriteSettingGlobal(QStringLiteral("max_anisotropy"), Settings::values.max_anisotropy, 0);
|
||||
WriteSettingGlobal(QStringLiteral("use_frame_limit"), Settings::values.use_frame_limit, true);
|
||||
WriteSettingGlobal(QStringLiteral("frame_limit"), Settings::values.frame_limit, 100);
|
||||
WriteSettingGlobal(QStringLiteral("use_disk_shader_cache"),
|
||||
Settings::values.use_disk_shader_cache, true);
|
||||
WriteSettingGlobal(QStringLiteral("gpu_accuracy"),
|
||||
static_cast<int>(Settings::values.gpu_accuracy.GetValue(global)),
|
||||
Settings::values.gpu_accuracy.UsingGlobal(), 1);
|
||||
WriteSettingGlobal(QStringLiteral("use_asynchronous_gpu_emulation"),
|
||||
Settings::values.use_asynchronous_gpu_emulation, true);
|
||||
WriteSettingGlobal(QStringLiteral("use_nvdec_emulation"), Settings::values.use_nvdec_emulation,
|
||||
true);
|
||||
WriteSettingGlobal(QStringLiteral("accelerate_astc"), Settings::values.accelerate_astc, true);
|
||||
WriteSettingGlobal(QStringLiteral("use_vsync"), Settings::values.use_vsync, true);
|
||||
WriteSettingGlobal(QStringLiteral("disable_fps_limit"), Settings::values.disable_fps_limit,
|
||||
false);
|
||||
WriteSettingGlobal(QStringLiteral("use_assembly_shaders"),
|
||||
Settings::values.use_assembly_shaders, false);
|
||||
WriteSettingGlobal(QStringLiteral("use_asynchronous_shaders"),
|
||||
Settings::values.use_asynchronous_shaders, false);
|
||||
WriteSettingGlobal(QStringLiteral("use_fast_gpu_time"), Settings::values.use_fast_gpu_time,
|
||||
true);
|
||||
WriteSettingGlobal(QStringLiteral("use_caches_gc"), Settings::values.use_caches_gc, false);
|
||||
// Cast to double because Qt's written float values are not human-readable
|
||||
WriteSettingGlobal(QStringLiteral("bg_red"), Settings::values.bg_red, 0.0);
|
||||
WriteSettingGlobal(QStringLiteral("bg_green"), Settings::values.bg_green, 0.0);
|
||||
WriteSettingGlobal(QStringLiteral("bg_blue"), Settings::values.bg_blue, 0.0);
|
||||
WriteSetting(QString::fromStdString(Settings::values.renderer_backend.GetLabel()),
|
||||
static_cast<u32>(Settings::values.renderer_backend.GetValue(global)),
|
||||
static_cast<u32>(Settings::values.renderer_backend.GetDefault()),
|
||||
Settings::values.renderer_backend.UsingGlobal());
|
||||
WriteBasicSetting(Settings::values.renderer_debug);
|
||||
WriteGlobalSetting(Settings::values.vulkan_device);
|
||||
WriteGlobalSetting(Settings::values.fullscreen_mode);
|
||||
WriteGlobalSetting(Settings::values.aspect_ratio);
|
||||
WriteGlobalSetting(Settings::values.max_anisotropy);
|
||||
WriteGlobalSetting(Settings::values.use_frame_limit);
|
||||
WriteGlobalSetting(Settings::values.frame_limit);
|
||||
WriteGlobalSetting(Settings::values.use_disk_shader_cache);
|
||||
WriteSetting(QString::fromStdString(Settings::values.gpu_accuracy.GetLabel()),
|
||||
static_cast<u32>(Settings::values.gpu_accuracy.GetValue(global)),
|
||||
static_cast<u32>(Settings::values.gpu_accuracy.GetDefault()),
|
||||
Settings::values.gpu_accuracy.UsingGlobal());
|
||||
WriteGlobalSetting(Settings::values.use_asynchronous_gpu_emulation);
|
||||
WriteGlobalSetting(Settings::values.use_nvdec_emulation);
|
||||
WriteGlobalSetting(Settings::values.accelerate_astc);
|
||||
WriteGlobalSetting(Settings::values.use_vsync);
|
||||
WriteGlobalSetting(Settings::values.disable_fps_limit);
|
||||
WriteGlobalSetting(Settings::values.use_assembly_shaders);
|
||||
WriteGlobalSetting(Settings::values.use_asynchronous_shaders);
|
||||
WriteGlobalSetting(Settings::values.use_fast_gpu_time);
|
||||
WriteGlobalSetting(Settings::values.use_caches_gc);
|
||||
WriteGlobalSetting(Settings::values.bg_red);
|
||||
WriteGlobalSetting(Settings::values.bg_green);
|
||||
WriteGlobalSetting(Settings::values.bg_blue);
|
||||
|
||||
qt_config->endGroup();
|
||||
}
|
||||
@@ -1423,8 +1348,7 @@ void Config::SaveRendererValues() {
|
||||
void Config::SaveScreenshotValues() {
|
||||
qt_config->beginGroup(QStringLiteral("Screenshots"));
|
||||
|
||||
WriteSetting(QStringLiteral("enable_screenshot_save_as"),
|
||||
UISettings::values.enable_screenshot_save_as);
|
||||
WriteBasicSetting(UISettings::values.enable_screenshot_save_as);
|
||||
WriteSetting(QStringLiteral("screenshot_path"),
|
||||
QString::fromStdString(FS::GetYuzuPathString(FS::YuzuPath::ScreenshotsDir)));
|
||||
|
||||
@@ -1454,17 +1378,16 @@ void Config::SaveShortcutValues() {
|
||||
void Config::SaveSystemValues() {
|
||||
qt_config->beginGroup(QStringLiteral("System"));
|
||||
|
||||
WriteSetting(QStringLiteral("current_user"), Settings::values.current_user, 0);
|
||||
WriteSettingGlobal(QStringLiteral("language_index"), Settings::values.language_index, 1);
|
||||
WriteSettingGlobal(QStringLiteral("region_index"), Settings::values.region_index, 1);
|
||||
WriteSettingGlobal(QStringLiteral("time_zone_index"), Settings::values.time_zone_index, 0);
|
||||
WriteBasicSetting(Settings::values.current_user);
|
||||
WriteGlobalSetting(Settings::values.language_index);
|
||||
WriteGlobalSetting(Settings::values.region_index);
|
||||
WriteGlobalSetting(Settings::values.time_zone_index);
|
||||
|
||||
WriteSettingGlobal(QStringLiteral("rng_seed_enabled"),
|
||||
Settings::values.rng_seed.GetValue(global).has_value(),
|
||||
Settings::values.rng_seed.UsingGlobal(), false);
|
||||
WriteSettingGlobal(QStringLiteral("rng_seed"),
|
||||
Settings::values.rng_seed.GetValue(global).value_or(0),
|
||||
Settings::values.rng_seed.UsingGlobal(), 0);
|
||||
WriteSetting(QStringLiteral("rng_seed_enabled"),
|
||||
Settings::values.rng_seed.GetValue(global).has_value(), false,
|
||||
Settings::values.rng_seed.UsingGlobal());
|
||||
WriteSetting(QStringLiteral("rng_seed"), Settings::values.rng_seed.GetValue(global).value_or(0),
|
||||
0, Settings::values.rng_seed.UsingGlobal());
|
||||
|
||||
if (global) {
|
||||
WriteSetting(QStringLiteral("custom_rtc_enabled"), Settings::values.custom_rtc.has_value(),
|
||||
@@ -1475,7 +1398,7 @@ void Config::SaveSystemValues() {
|
||||
0);
|
||||
}
|
||||
|
||||
WriteSettingGlobal(QStringLiteral("sound_index"), Settings::values.sound_index, 1);
|
||||
WriteGlobalSetting(Settings::values.sound_index);
|
||||
|
||||
qt_config->endGroup();
|
||||
}
|
||||
@@ -1485,10 +1408,8 @@ void Config::SaveUIValues() {
|
||||
|
||||
WriteSetting(QStringLiteral("theme"), UISettings::values.theme,
|
||||
QString::fromUtf8(UISettings::themes[0].second));
|
||||
WriteSetting(QStringLiteral("enable_discord_presence"),
|
||||
UISettings::values.enable_discord_presence, true);
|
||||
WriteSetting(QStringLiteral("select_user_on_boot"), UISettings::values.select_user_on_boot,
|
||||
false);
|
||||
WriteBasicSetting(UISettings::values.enable_discord_presence);
|
||||
WriteBasicSetting(UISettings::values.select_user_on_boot);
|
||||
|
||||
SaveUIGamelistValues();
|
||||
SaveUILayoutValues();
|
||||
@@ -1496,18 +1417,17 @@ void Config::SaveUIValues() {
|
||||
SaveScreenshotValues();
|
||||
SaveShortcutValues();
|
||||
|
||||
WriteSetting(QStringLiteral("singleWindowMode"), UISettings::values.single_window_mode, true);
|
||||
WriteSetting(QStringLiteral("fullscreen"), UISettings::values.fullscreen, false);
|
||||
WriteSetting(QStringLiteral("displayTitleBars"), UISettings::values.display_titlebar, true);
|
||||
WriteSetting(QStringLiteral("showFilterBar"), UISettings::values.show_filter_bar, true);
|
||||
WriteSetting(QStringLiteral("showStatusBar"), UISettings::values.show_status_bar, true);
|
||||
WriteSetting(QStringLiteral("confirmClose"), UISettings::values.confirm_before_closing, true);
|
||||
WriteSetting(QStringLiteral("firstStart"), UISettings::values.first_start, true);
|
||||
WriteSetting(QStringLiteral("calloutFlags"), UISettings::values.callout_flags, 0);
|
||||
WriteSetting(QStringLiteral("showConsole"), UISettings::values.show_console, false);
|
||||
WriteSetting(QStringLiteral("pauseWhenInBackground"),
|
||||
UISettings::values.pause_when_in_background, false);
|
||||
WriteSetting(QStringLiteral("hideInactiveMouse"), UISettings::values.hide_mouse, false);
|
||||
WriteBasicSetting(UISettings::values.single_window_mode);
|
||||
WriteBasicSetting(UISettings::values.fullscreen);
|
||||
WriteBasicSetting(UISettings::values.display_titlebar);
|
||||
WriteBasicSetting(UISettings::values.show_filter_bar);
|
||||
WriteBasicSetting(UISettings::values.show_status_bar);
|
||||
WriteBasicSetting(UISettings::values.confirm_before_closing);
|
||||
WriteBasicSetting(UISettings::values.first_start);
|
||||
WriteBasicSetting(UISettings::values.callout_flags);
|
||||
WriteBasicSetting(UISettings::values.show_console);
|
||||
WriteBasicSetting(UISettings::values.pause_when_in_background);
|
||||
WriteBasicSetting(UISettings::values.hide_mouse);
|
||||
|
||||
qt_config->endGroup();
|
||||
}
|
||||
@@ -1515,11 +1435,11 @@ void Config::SaveUIValues() {
|
||||
void Config::SaveUIGamelistValues() {
|
||||
qt_config->beginGroup(QStringLiteral("UIGameList"));
|
||||
|
||||
WriteSetting(QStringLiteral("show_add_ons"), UISettings::values.show_add_ons, true);
|
||||
WriteSetting(QStringLiteral("icon_size"), UISettings::values.icon_size, 64);
|
||||
WriteSetting(QStringLiteral("row_1_text_id"), UISettings::values.row_1_text_id, 3);
|
||||
WriteSetting(QStringLiteral("row_2_text_id"), UISettings::values.row_2_text_id, 2);
|
||||
WriteSetting(QStringLiteral("cache_game_list"), UISettings::values.cache_game_list, true);
|
||||
WriteBasicSetting(UISettings::values.show_add_ons);
|
||||
WriteBasicSetting(UISettings::values.icon_size);
|
||||
WriteBasicSetting(UISettings::values.row_1_text_id);
|
||||
WriteBasicSetting(UISettings::values.row_2_text_id);
|
||||
WriteBasicSetting(UISettings::values.cache_game_list);
|
||||
qt_config->beginWriteArray(QStringLiteral("favorites"));
|
||||
for (int i = 0; i < UISettings::values.favorited_ids.size(); i++) {
|
||||
qt_config->setArrayIndex(i);
|
||||
@@ -1540,8 +1460,7 @@ void Config::SaveUILayoutValues() {
|
||||
WriteSetting(QStringLiteral("gameListHeaderState"), UISettings::values.gamelist_header_state);
|
||||
WriteSetting(QStringLiteral("microProfileDialogGeometry"),
|
||||
UISettings::values.microprofile_geometry);
|
||||
WriteSetting(QStringLiteral("microProfileDialogVisible"),
|
||||
UISettings::values.microprofile_visible, false);
|
||||
WriteBasicSetting(UISettings::values.microprofile_visible);
|
||||
|
||||
qt_config->endGroup();
|
||||
}
|
||||
@@ -1549,13 +1468,10 @@ void Config::SaveUILayoutValues() {
|
||||
void Config::SaveWebServiceValues() {
|
||||
qt_config->beginGroup(QStringLiteral("WebService"));
|
||||
|
||||
WriteSetting(QStringLiteral("enable_telemetry"), Settings::values.enable_telemetry, true);
|
||||
WriteSetting(QStringLiteral("web_api_url"),
|
||||
QString::fromStdString(Settings::values.web_api_url),
|
||||
QStringLiteral("https://api.yuzu-emu.org"));
|
||||
WriteSetting(QStringLiteral("yuzu_username"),
|
||||
QString::fromStdString(Settings::values.yuzu_username));
|
||||
WriteSetting(QStringLiteral("yuzu_token"), QString::fromStdString(Settings::values.yuzu_token));
|
||||
WriteBasicSetting(Settings::values.enable_telemetry);
|
||||
WriteBasicSetting(Settings::values.web_api_url);
|
||||
WriteBasicSetting(Settings::values.yuzu_username);
|
||||
WriteBasicSetting(Settings::values.yuzu_token);
|
||||
|
||||
qt_config->endGroup();
|
||||
}
|
||||
@@ -1575,21 +1491,14 @@ QVariant Config::ReadSetting(const QString& name, const QVariant& default_value)
|
||||
}
|
||||
|
||||
template <typename Type>
|
||||
void Config::ReadSettingGlobal(Settings::Setting<Type>& setting, const QString& name) {
|
||||
void Config::ReadGlobalSetting(Settings::Setting<Type>& setting) {
|
||||
QString name = QString::fromStdString(setting.GetLabel());
|
||||
const bool use_global = qt_config->value(name + QStringLiteral("/use_global"), true).toBool();
|
||||
setting.SetGlobal(use_global);
|
||||
if (global || !use_global) {
|
||||
setting.SetValue(ReadSetting(name).value<Type>());
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Type>
|
||||
void Config::ReadSettingGlobal(Settings::Setting<Type>& setting, const QString& name,
|
||||
const QVariant& default_value) {
|
||||
const bool use_global = qt_config->value(name + QStringLiteral("/use_global"), true).toBool();
|
||||
setting.SetGlobal(use_global);
|
||||
if (global || !use_global) {
|
||||
setting.SetValue(ReadSetting(name, default_value).value<Type>());
|
||||
setting.SetValue(static_cast<QVariant>(
|
||||
ReadSetting(name, QVariant::fromValue<Type>(setting.GetDefault())))
|
||||
.value<Type>());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1612,31 +1521,8 @@ void Config::WriteSetting(const QString& name, const QVariant& value,
|
||||
qt_config->setValue(name, value);
|
||||
}
|
||||
|
||||
template <typename Type>
|
||||
void Config::WriteSettingGlobal(const QString& name, const Settings::Setting<Type>& setting) {
|
||||
if (!global) {
|
||||
qt_config->setValue(name + QStringLiteral("/use_global"), setting.UsingGlobal());
|
||||
}
|
||||
if (global || !setting.UsingGlobal()) {
|
||||
qt_config->setValue(name, setting.GetValue(global));
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Type>
|
||||
void Config::WriteSettingGlobal(const QString& name, const Settings::Setting<Type>& setting,
|
||||
const QVariant& default_value) {
|
||||
if (!global) {
|
||||
qt_config->setValue(name + QStringLiteral("/use_global"), setting.UsingGlobal());
|
||||
}
|
||||
if (global || !setting.UsingGlobal()) {
|
||||
qt_config->setValue(name + QStringLiteral("/default"),
|
||||
setting.GetValue(global) == default_value.value<Type>());
|
||||
qt_config->setValue(name, setting.GetValue(global));
|
||||
}
|
||||
}
|
||||
|
||||
void Config::WriteSettingGlobal(const QString& name, const QVariant& value, bool use_global,
|
||||
const QVariant& default_value) {
|
||||
void Config::WriteSetting(const QString& name, const QVariant& value, const QVariant& default_value,
|
||||
bool use_global) {
|
||||
if (!global) {
|
||||
qt_config->setValue(name + QStringLiteral("/use_global"), use_global);
|
||||
}
|
||||
|
||||
@@ -102,28 +102,75 @@ private:
|
||||
void SaveUILayoutValues();
|
||||
void SaveWebServiceValues();
|
||||
|
||||
/**
|
||||
* Reads a setting from the qt_config.
|
||||
*
|
||||
* @param name The setting's identifier
|
||||
* @param default_value The value to use when the setting is not already present in the config
|
||||
*/
|
||||
QVariant ReadSetting(const QString& name) const;
|
||||
QVariant ReadSetting(const QString& name, const QVariant& default_value) const;
|
||||
// Templated ReadSettingGlobal functions will also look for the use_global setting and set
|
||||
// both the value and the global state properly
|
||||
template <typename Type>
|
||||
void ReadSettingGlobal(Settings::Setting<Type>& setting, const QString& name);
|
||||
template <typename Type>
|
||||
void ReadSettingGlobal(Settings::Setting<Type>& setting, const QString& name,
|
||||
const QVariant& default_value);
|
||||
|
||||
/**
|
||||
* Only reads a setting from the qt_config if the current config is a global config, or if the
|
||||
* current config is a custom config and the setting is overriding the global setting. Otherwise
|
||||
* it does nothing.
|
||||
*
|
||||
* @param setting The variable to be modified
|
||||
* @param name The setting's identifier
|
||||
* @param default_value The value to use when the setting is not already present in the config
|
||||
*/
|
||||
template <typename Type>
|
||||
void ReadSettingGlobal(Type& setting, const QString& name, const QVariant& default_value) const;
|
||||
// Templated WriteSettingGlobal functions will also write the global state if needed and will
|
||||
// skip writing the actual setting if it defers to the global value
|
||||
|
||||
/**
|
||||
* Writes a setting to the qt_config.
|
||||
*
|
||||
* @param name The setting's idetentifier
|
||||
* @param value Value of the setting
|
||||
* @param default_value Default of the setting if not present in qt_config
|
||||
* @param use_global Specifies if the custom or global config should be in use, for custom
|
||||
* configs
|
||||
*/
|
||||
void WriteSetting(const QString& name, const QVariant& value);
|
||||
void WriteSetting(const QString& name, const QVariant& value, const QVariant& default_value);
|
||||
void WriteSetting(const QString& name, const QVariant& value, const QVariant& default_value,
|
||||
bool use_global);
|
||||
|
||||
/**
|
||||
* Reads a value from the qt_config and applies it to the setting, using its label and default
|
||||
* value. If the config is a custom config, this will also read the global state of the setting
|
||||
* and apply that information to it.
|
||||
*
|
||||
* @param The setting
|
||||
*/
|
||||
template <typename Type>
|
||||
void WriteSettingGlobal(const QString& name, const Settings::Setting<Type>& setting);
|
||||
void ReadGlobalSetting(Settings::Setting<Type>& setting);
|
||||
|
||||
/**
|
||||
* Sets a value to the qt_config using the setting's label and default value. If the config is a
|
||||
* custom config, it will apply the global state, and the custom value if needed.
|
||||
*
|
||||
* @param The setting
|
||||
*/
|
||||
template <typename Type>
|
||||
void WriteSettingGlobal(const QString& name, const Settings::Setting<Type>& setting,
|
||||
const QVariant& default_value);
|
||||
void WriteSettingGlobal(const QString& name, const QVariant& value, bool use_global,
|
||||
const QVariant& default_value);
|
||||
void WriteGlobalSetting(const Settings::Setting<Type>& setting);
|
||||
|
||||
/**
|
||||
* Reads a value from the qt_config using the setting's label and default value and applies the
|
||||
* value to the setting.
|
||||
*
|
||||
* @param The setting
|
||||
*/
|
||||
template <typename Type>
|
||||
void ReadBasicSetting(Settings::BasicSetting<Type>& setting);
|
||||
|
||||
/** Sets a value from the setting in the qt_config using the setting's label and default value.
|
||||
*
|
||||
* @param The setting
|
||||
*/
|
||||
template <typename Type>
|
||||
void WriteBasicSetting(const Settings::BasicSetting<Type>& setting);
|
||||
|
||||
ConfigType type;
|
||||
std::unique_ptr<QSettings> qt_config;
|
||||
|
||||
@@ -47,7 +47,8 @@ void ConfigureAudio::SetConfiguration() {
|
||||
|
||||
SetAudioDeviceFromDeviceID();
|
||||
|
||||
ui->volume_slider->setValue(Settings::values.volume.GetValue() * ui->volume_slider->maximum());
|
||||
const auto volume_value = Settings::values.volume.GetValue() * ui->volume_slider->maximum();
|
||||
ui->volume_slider->setValue(volume_value / 100);
|
||||
|
||||
ui->toggle_audio_stretching->setChecked(Settings::values.enable_audio_stretching.GetValue());
|
||||
|
||||
@@ -69,7 +70,7 @@ void ConfigureAudio::SetOutputSinkFromSinkID() {
|
||||
[[maybe_unused]] const QSignalBlocker blocker(ui->output_sink_combo_box);
|
||||
|
||||
int new_sink_index = 0;
|
||||
const QString sink_id = QString::fromStdString(Settings::values.sink_id);
|
||||
const QString sink_id = QString::fromStdString(Settings::values.sink_id.GetValue());
|
||||
for (int index = 0; index < ui->output_sink_combo_box->count(); index++) {
|
||||
if (ui->output_sink_combo_box->itemText(index) == sink_id) {
|
||||
new_sink_index = index;
|
||||
@@ -83,7 +84,7 @@ void ConfigureAudio::SetOutputSinkFromSinkID() {
|
||||
void ConfigureAudio::SetAudioDeviceFromDeviceID() {
|
||||
int new_device_index = -1;
|
||||
|
||||
const QString device_id = QString::fromStdString(Settings::values.audio_device_id);
|
||||
const QString device_id = QString::fromStdString(Settings::values.audio_device_id.GetValue());
|
||||
for (int index = 0; index < ui->audio_device_combo_box->count(); index++) {
|
||||
if (ui->audio_device_combo_box->itemText(index) == device_id) {
|
||||
new_device_index = index;
|
||||
@@ -106,24 +107,22 @@ void ConfigureAudio::ApplyConfiguration() {
|
||||
Settings::values.sink_id =
|
||||
ui->output_sink_combo_box->itemText(ui->output_sink_combo_box->currentIndex())
|
||||
.toStdString();
|
||||
Settings::values.audio_device_id =
|
||||
Settings::values.audio_device_id.SetValue(
|
||||
ui->audio_device_combo_box->itemText(ui->audio_device_combo_box->currentIndex())
|
||||
.toStdString();
|
||||
.toStdString());
|
||||
|
||||
// Guard if during game and set to game-specific value
|
||||
if (Settings::values.volume.UsingGlobal()) {
|
||||
Settings::values.volume.SetValue(
|
||||
static_cast<float>(ui->volume_slider->sliderPosition()) /
|
||||
ui->volume_slider->maximum());
|
||||
const s32 volume = ui->volume_slider->sliderPosition() / ui->volume_slider->maximum();
|
||||
Settings::values.volume.SetValue(static_cast<u8>(100 * volume));
|
||||
}
|
||||
} else {
|
||||
if (ui->volume_combo_box->currentIndex() == 0) {
|
||||
Settings::values.volume.SetGlobal(true);
|
||||
} else {
|
||||
Settings::values.volume.SetGlobal(false);
|
||||
Settings::values.volume.SetValue(
|
||||
static_cast<float>(ui->volume_slider->sliderPosition()) /
|
||||
ui->volume_slider->maximum());
|
||||
const s32 volume = ui->volume_slider->sliderPosition() / ui->volume_slider->maximum();
|
||||
Settings::values.volume.SetValue(static_cast<u8>(100 * volume));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,23 +24,26 @@ void ConfigureCpuDebug::SetConfiguration() {
|
||||
const bool runtime_lock = !Core::System::GetInstance().IsPoweredOn();
|
||||
|
||||
ui->cpuopt_page_tables->setEnabled(runtime_lock);
|
||||
ui->cpuopt_page_tables->setChecked(Settings::values.cpuopt_page_tables);
|
||||
ui->cpuopt_page_tables->setChecked(Settings::values.cpuopt_page_tables.GetValue());
|
||||
ui->cpuopt_block_linking->setEnabled(runtime_lock);
|
||||
ui->cpuopt_block_linking->setChecked(Settings::values.cpuopt_block_linking);
|
||||
ui->cpuopt_block_linking->setChecked(Settings::values.cpuopt_block_linking.GetValue());
|
||||
ui->cpuopt_return_stack_buffer->setEnabled(runtime_lock);
|
||||
ui->cpuopt_return_stack_buffer->setChecked(Settings::values.cpuopt_return_stack_buffer);
|
||||
ui->cpuopt_return_stack_buffer->setChecked(
|
||||
Settings::values.cpuopt_return_stack_buffer.GetValue());
|
||||
ui->cpuopt_fast_dispatcher->setEnabled(runtime_lock);
|
||||
ui->cpuopt_fast_dispatcher->setChecked(Settings::values.cpuopt_fast_dispatcher);
|
||||
ui->cpuopt_fast_dispatcher->setChecked(Settings::values.cpuopt_fast_dispatcher.GetValue());
|
||||
ui->cpuopt_context_elimination->setEnabled(runtime_lock);
|
||||
ui->cpuopt_context_elimination->setChecked(Settings::values.cpuopt_context_elimination);
|
||||
ui->cpuopt_context_elimination->setChecked(
|
||||
Settings::values.cpuopt_context_elimination.GetValue());
|
||||
ui->cpuopt_const_prop->setEnabled(runtime_lock);
|
||||
ui->cpuopt_const_prop->setChecked(Settings::values.cpuopt_const_prop);
|
||||
ui->cpuopt_const_prop->setChecked(Settings::values.cpuopt_const_prop.GetValue());
|
||||
ui->cpuopt_misc_ir->setEnabled(runtime_lock);
|
||||
ui->cpuopt_misc_ir->setChecked(Settings::values.cpuopt_misc_ir);
|
||||
ui->cpuopt_misc_ir->setChecked(Settings::values.cpuopt_misc_ir.GetValue());
|
||||
ui->cpuopt_reduce_misalign_checks->setEnabled(runtime_lock);
|
||||
ui->cpuopt_reduce_misalign_checks->setChecked(Settings::values.cpuopt_reduce_misalign_checks);
|
||||
ui->cpuopt_reduce_misalign_checks->setChecked(
|
||||
Settings::values.cpuopt_reduce_misalign_checks.GetValue());
|
||||
ui->cpuopt_fastmem->setEnabled(runtime_lock);
|
||||
ui->cpuopt_fastmem->setChecked(Settings::values.cpuopt_fastmem);
|
||||
ui->cpuopt_fastmem->setChecked(Settings::values.cpuopt_fastmem.GetValue());
|
||||
}
|
||||
|
||||
void ConfigureCpuDebug::ApplyConfiguration() {
|
||||
|
||||
@@ -31,20 +31,21 @@ void ConfigureDebug::SetConfiguration() {
|
||||
const bool runtime_lock = !Core::System::GetInstance().IsPoweredOn();
|
||||
|
||||
ui->toggle_console->setEnabled(runtime_lock);
|
||||
ui->toggle_console->setChecked(UISettings::values.show_console);
|
||||
ui->log_filter_edit->setText(QString::fromStdString(Settings::values.log_filter));
|
||||
ui->homebrew_args_edit->setText(QString::fromStdString(Settings::values.program_args));
|
||||
ui->toggle_console->setChecked(UISettings::values.show_console.GetValue());
|
||||
ui->log_filter_edit->setText(QString::fromStdString(Settings::values.log_filter.GetValue()));
|
||||
ui->homebrew_args_edit->setText(
|
||||
QString::fromStdString(Settings::values.program_args.GetValue()));
|
||||
ui->fs_access_log->setEnabled(runtime_lock);
|
||||
ui->fs_access_log->setChecked(Settings::values.enable_fs_access_log);
|
||||
ui->reporting_services->setChecked(Settings::values.reporting_services);
|
||||
ui->quest_flag->setChecked(Settings::values.quest_flag);
|
||||
ui->use_debug_asserts->setChecked(Settings::values.use_debug_asserts);
|
||||
ui->use_auto_stub->setChecked(Settings::values.use_auto_stub);
|
||||
ui->fs_access_log->setChecked(Settings::values.enable_fs_access_log.GetValue());
|
||||
ui->reporting_services->setChecked(Settings::values.reporting_services.GetValue());
|
||||
ui->quest_flag->setChecked(Settings::values.quest_flag.GetValue());
|
||||
ui->use_debug_asserts->setChecked(Settings::values.use_debug_asserts.GetValue());
|
||||
ui->use_auto_stub->setChecked(Settings::values.use_auto_stub.GetValue());
|
||||
ui->enable_graphics_debugging->setEnabled(runtime_lock);
|
||||
ui->enable_graphics_debugging->setChecked(Settings::values.renderer_debug);
|
||||
ui->enable_graphics_debugging->setChecked(Settings::values.renderer_debug.GetValue());
|
||||
ui->disable_macro_jit->setEnabled(runtime_lock);
|
||||
ui->disable_macro_jit->setChecked(Settings::values.disable_macro_jit);
|
||||
ui->extended_logging->setChecked(Settings::values.extended_logging);
|
||||
ui->disable_macro_jit->setChecked(Settings::values.disable_macro_jit.GetValue());
|
||||
ui->extended_logging->setChecked(Settings::values.extended_logging.GetValue());
|
||||
}
|
||||
|
||||
void ConfigureDebug::ApplyConfiguration() {
|
||||
@@ -61,7 +62,7 @@ void ConfigureDebug::ApplyConfiguration() {
|
||||
Settings::values.extended_logging = ui->extended_logging->isChecked();
|
||||
Debugger::ToggleConsole();
|
||||
Common::Log::Filter filter;
|
||||
filter.ParseFilterString(Settings::values.log_filter);
|
||||
filter.ParseFilterString(Settings::values.log_filter.GetValue());
|
||||
Common::Log::SetGlobalFilter(filter);
|
||||
}
|
||||
|
||||
|
||||
@@ -43,18 +43,19 @@ void ConfigureFilesystem::setConfiguration() {
|
||||
QString::fromStdString(Common::FS::GetYuzuPathString(Common::FS::YuzuPath::NANDDir)));
|
||||
ui->sdmc_directory_edit->setText(
|
||||
QString::fromStdString(Common::FS::GetYuzuPathString(Common::FS::YuzuPath::SDMCDir)));
|
||||
ui->gamecard_path_edit->setText(QString::fromStdString(Settings::values.gamecard_path));
|
||||
ui->gamecard_path_edit->setText(
|
||||
QString::fromStdString(Settings::values.gamecard_path.GetValue()));
|
||||
ui->dump_path_edit->setText(
|
||||
QString::fromStdString(Common::FS::GetYuzuPathString(Common::FS::YuzuPath::DumpDir)));
|
||||
ui->load_path_edit->setText(
|
||||
QString::fromStdString(Common::FS::GetYuzuPathString(Common::FS::YuzuPath::LoadDir)));
|
||||
|
||||
ui->gamecard_inserted->setChecked(Settings::values.gamecard_inserted);
|
||||
ui->gamecard_current_game->setChecked(Settings::values.gamecard_current_game);
|
||||
ui->dump_exefs->setChecked(Settings::values.dump_exefs);
|
||||
ui->dump_nso->setChecked(Settings::values.dump_nso);
|
||||
ui->gamecard_inserted->setChecked(Settings::values.gamecard_inserted.GetValue());
|
||||
ui->gamecard_current_game->setChecked(Settings::values.gamecard_current_game.GetValue());
|
||||
ui->dump_exefs->setChecked(Settings::values.dump_exefs.GetValue());
|
||||
ui->dump_nso->setChecked(Settings::values.dump_nso.GetValue());
|
||||
|
||||
ui->cache_game_list->setChecked(UISettings::values.cache_game_list);
|
||||
ui->cache_game_list->setChecked(UISettings::values.cache_game_list.GetValue());
|
||||
|
||||
UpdateEnabledControls();
|
||||
}
|
||||
|
||||
@@ -40,10 +40,10 @@ void ConfigureGeneral::SetConfiguration() {
|
||||
ui->use_multi_core->setEnabled(runtime_lock);
|
||||
ui->use_multi_core->setChecked(Settings::values.use_multi_core.GetValue());
|
||||
|
||||
ui->toggle_check_exit->setChecked(UISettings::values.confirm_before_closing);
|
||||
ui->toggle_user_on_boot->setChecked(UISettings::values.select_user_on_boot);
|
||||
ui->toggle_background_pause->setChecked(UISettings::values.pause_when_in_background);
|
||||
ui->toggle_hide_mouse->setChecked(UISettings::values.hide_mouse);
|
||||
ui->toggle_check_exit->setChecked(UISettings::values.confirm_before_closing.GetValue());
|
||||
ui->toggle_user_on_boot->setChecked(UISettings::values.select_user_on_boot.GetValue());
|
||||
ui->toggle_background_pause->setChecked(UISettings::values.pause_when_in_background.GetValue());
|
||||
ui->toggle_hide_mouse->setChecked(UISettings::values.hide_mouse.GetValue());
|
||||
|
||||
ui->toggle_frame_limit->setChecked(Settings::values.use_frame_limit.GetValue());
|
||||
ui->frame_limit->setValue(Settings::values.frame_limit.GetValue());
|
||||
|
||||
@@ -101,9 +101,9 @@ void ConfigureGraphics::SetConfiguration() {
|
||||
ConfigurationShared::SetHighlight(ui->bg_layout, !Settings::values.bg_red.UsingGlobal());
|
||||
}
|
||||
|
||||
UpdateBackgroundColorButton(QColor::fromRgbF(Settings::values.bg_red.GetValue(),
|
||||
Settings::values.bg_green.GetValue(),
|
||||
Settings::values.bg_blue.GetValue()));
|
||||
UpdateBackgroundColorButton(QColor::fromRgb(Settings::values.bg_red.GetValue(),
|
||||
Settings::values.bg_green.GetValue(),
|
||||
Settings::values.bg_blue.GetValue()));
|
||||
UpdateDeviceComboBox();
|
||||
}
|
||||
|
||||
@@ -132,9 +132,9 @@ void ConfigureGraphics::ApplyConfiguration() {
|
||||
Settings::values.vulkan_device.SetValue(vulkan_device);
|
||||
}
|
||||
if (Settings::values.bg_red.UsingGlobal()) {
|
||||
Settings::values.bg_red.SetValue(static_cast<float>(bg_color.redF()));
|
||||
Settings::values.bg_green.SetValue(static_cast<float>(bg_color.greenF()));
|
||||
Settings::values.bg_blue.SetValue(static_cast<float>(bg_color.blueF()));
|
||||
Settings::values.bg_red.SetValue(static_cast<u8>(bg_color.red()));
|
||||
Settings::values.bg_green.SetValue(static_cast<u8>(bg_color.green()));
|
||||
Settings::values.bg_blue.SetValue(static_cast<u8>(bg_color.blue()));
|
||||
}
|
||||
} else {
|
||||
if (ui->api->currentIndex() == ConfigurationShared::USE_GLOBAL_INDEX) {
|
||||
@@ -159,9 +159,9 @@ void ConfigureGraphics::ApplyConfiguration() {
|
||||
Settings::values.bg_red.SetGlobal(false);
|
||||
Settings::values.bg_green.SetGlobal(false);
|
||||
Settings::values.bg_blue.SetGlobal(false);
|
||||
Settings::values.bg_red.SetValue(static_cast<float>(bg_color.redF()));
|
||||
Settings::values.bg_green.SetValue(static_cast<float>(bg_color.greenF()));
|
||||
Settings::values.bg_blue.SetValue(static_cast<float>(bg_color.blueF()));
|
||||
Settings::values.bg_red.SetValue(static_cast<u8>(bg_color.red()));
|
||||
Settings::values.bg_green.SetValue(static_cast<u8>(bg_color.green()));
|
||||
Settings::values.bg_blue.SetValue(static_cast<u8>(bg_color.blue()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -148,12 +148,12 @@ void ConfigureInputAdvanced::LoadConfiguration() {
|
||||
}
|
||||
}
|
||||
|
||||
ui->debug_enabled->setChecked(Settings::values.debug_pad_enabled);
|
||||
ui->mouse_enabled->setChecked(Settings::values.mouse_enabled);
|
||||
ui->keyboard_enabled->setChecked(Settings::values.keyboard_enabled);
|
||||
ui->emulate_analog_keyboard->setChecked(Settings::values.emulate_analog_keyboard);
|
||||
ui->mouse_panning->setChecked(Settings::values.mouse_panning);
|
||||
ui->mouse_panning_sensitivity->setValue(Settings::values.mouse_panning_sensitivity);
|
||||
ui->debug_enabled->setChecked(Settings::values.debug_pad_enabled.GetValue());
|
||||
ui->mouse_enabled->setChecked(Settings::values.mouse_enabled.GetValue());
|
||||
ui->keyboard_enabled->setChecked(Settings::values.keyboard_enabled.GetValue());
|
||||
ui->emulate_analog_keyboard->setChecked(Settings::values.emulate_analog_keyboard.GetValue());
|
||||
ui->mouse_panning->setChecked(Settings::values.mouse_panning.GetValue());
|
||||
ui->mouse_panning_sensitivity->setValue(Settings::values.mouse_panning_sensitivity.GetValue());
|
||||
ui->touchscreen_enabled->setChecked(Settings::values.touchscreen.enabled);
|
||||
|
||||
UpdateUIEnabled();
|
||||
|
||||
@@ -2573,27 +2573,24 @@
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="2">
|
||||
<widget class="QDoubleSpinBox" name="mouse_panning_sensitivity">
|
||||
<widget class="QSpinBox" name="mouse_panning_sensitivity">
|
||||
<property name="toolTip">
|
||||
<string>Mouse sensitivity</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignCenter</set>
|
||||
</property>
|
||||
<property name="decimals">
|
||||
<number>2</number>
|
||||
<property name="suffix">
|
||||
<string>%</string>
|
||||
</property>
|
||||
<property name="minimum">
|
||||
<double>0.100000000000000</double>
|
||||
<number>1</number>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<double>16.000000000000000</double>
|
||||
</property>
|
||||
<property name="singleStep">
|
||||
<double>0.010000000000000</double>
|
||||
<number>100</number>
|
||||
</property>
|
||||
<property name="value">
|
||||
<double>1.000000000000000</double>
|
||||
<number>100</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
|
||||
@@ -101,15 +101,16 @@ ConfigureMotionTouch::ConfigureMotionTouch(QWidget* parent,
|
||||
ConfigureMotionTouch::~ConfigureMotionTouch() = default;
|
||||
|
||||
void ConfigureMotionTouch::SetConfiguration() {
|
||||
const Common::ParamPackage motion_param(Settings::values.motion_device);
|
||||
const Common::ParamPackage touch_param(Settings::values.touch_device);
|
||||
const Common::ParamPackage motion_param(Settings::values.motion_device.GetValue());
|
||||
const Common::ParamPackage touch_param(Settings::values.touch_device.GetValue());
|
||||
|
||||
ui->touch_from_button_checkbox->setChecked(Settings::values.use_touch_from_button);
|
||||
ui->touch_from_button_checkbox->setChecked(Settings::values.use_touch_from_button.GetValue());
|
||||
touch_from_button_maps = Settings::values.touch_from_button_maps;
|
||||
for (const auto& touch_map : touch_from_button_maps) {
|
||||
ui->touch_from_button_map->addItem(QString::fromStdString(touch_map.name));
|
||||
}
|
||||
ui->touch_from_button_map->setCurrentIndex(Settings::values.touch_from_button_map_index);
|
||||
ui->touch_from_button_map->setCurrentIndex(
|
||||
Settings::values.touch_from_button_map_index.GetValue());
|
||||
ui->motion_sensitivity->setValue(motion_param.Get("sensitivity", 0.01f));
|
||||
|
||||
min_x = touch_param.Get("min_x", 100);
|
||||
@@ -124,7 +125,7 @@ void ConfigureMotionTouch::SetConfiguration() {
|
||||
udp_server_list_model->setStringList({});
|
||||
ui->udp_server_list->setModel(udp_server_list_model);
|
||||
|
||||
std::stringstream ss(Settings::values.udp_input_servers);
|
||||
std::stringstream ss(Settings::values.udp_input_servers.GetValue());
|
||||
std::string token;
|
||||
|
||||
while (std::getline(ss, token, ',')) {
|
||||
|
||||
@@ -166,7 +166,7 @@ void ConfigureProfileManager::PopulateUserList() {
|
||||
void ConfigureProfileManager::UpdateCurrentUser() {
|
||||
ui->pm_add->setEnabled(profile_manager->GetUserCount() < Service::Account::MAX_USERS);
|
||||
|
||||
const auto& current_user = profile_manager->GetUser(Settings::values.current_user);
|
||||
const auto& current_user = profile_manager->GetUser(Settings::values.current_user.GetValue());
|
||||
ASSERT(current_user);
|
||||
const auto username = GetAccountUsername(*profile_manager, *current_user);
|
||||
|
||||
@@ -245,15 +245,18 @@ void ConfigureProfileManager::DeleteUser() {
|
||||
this, tr("Confirm Delete"),
|
||||
tr("You are about to delete user with name \"%1\". Are you sure?").arg(username));
|
||||
|
||||
if (confirm == QMessageBox::No)
|
||||
if (confirm == QMessageBox::No) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (Settings::values.current_user == tree_view->currentIndex().row())
|
||||
if (Settings::values.current_user.GetValue() == tree_view->currentIndex().row()) {
|
||||
Settings::values.current_user = 0;
|
||||
}
|
||||
UpdateCurrentUser();
|
||||
|
||||
if (!profile_manager->RemoveUser(*uuid))
|
||||
if (!profile_manager->RemoveUser(*uuid)) {
|
||||
return;
|
||||
}
|
||||
|
||||
item_model->removeRows(tree_view->currentIndex().row(), 1);
|
||||
tree_view->clearSelection();
|
||||
|
||||
@@ -65,7 +65,7 @@ void ConfigureService::RetranslateUi() {
|
||||
|
||||
void ConfigureService::SetConfiguration() {
|
||||
const int index =
|
||||
ui->bcat_source->findData(QString::fromStdString(Settings::values.bcat_backend));
|
||||
ui->bcat_source->findData(QString::fromStdString(Settings::values.bcat_backend.GetValue()));
|
||||
ui->bcat_source->setCurrentIndex(index == -1 ? 0 : index);
|
||||
}
|
||||
|
||||
|
||||
@@ -113,11 +113,12 @@ void ConfigureUi::SetConfiguration() {
|
||||
ui->theme_combobox->setCurrentIndex(ui->theme_combobox->findData(UISettings::values.theme));
|
||||
ui->language_combobox->setCurrentIndex(
|
||||
ui->language_combobox->findData(UISettings::values.language));
|
||||
ui->show_add_ons->setChecked(UISettings::values.show_add_ons);
|
||||
ui->show_add_ons->setChecked(UISettings::values.show_add_ons.GetValue());
|
||||
ui->icon_size_combobox->setCurrentIndex(
|
||||
ui->icon_size_combobox->findData(UISettings::values.icon_size));
|
||||
ui->icon_size_combobox->findData(UISettings::values.icon_size.GetValue()));
|
||||
|
||||
ui->enable_screenshot_save_as->setChecked(UISettings::values.enable_screenshot_save_as);
|
||||
ui->enable_screenshot_save_as->setChecked(
|
||||
UISettings::values.enable_screenshot_save_as.GetValue());
|
||||
ui->screenshot_path_edit->setText(QString::fromStdString(
|
||||
Common::FS::GetYuzuPathString(Common::FS::YuzuPath::ScreenshotsDir)));
|
||||
}
|
||||
@@ -178,7 +179,7 @@ void ConfigureUi::InitializeRowComboBoxes() {
|
||||
|
||||
void ConfigureUi::UpdateFirstRowComboBox(bool init) {
|
||||
const int currentIndex =
|
||||
init ? UISettings::values.row_1_text_id
|
||||
init ? UISettings::values.row_1_text_id.GetValue()
|
||||
: ui->row_1_text_combobox->findData(ui->row_1_text_combobox->currentData());
|
||||
|
||||
ui->row_1_text_combobox->clear();
|
||||
@@ -197,7 +198,7 @@ void ConfigureUi::UpdateFirstRowComboBox(bool init) {
|
||||
|
||||
void ConfigureUi::UpdateSecondRowComboBox(bool init) {
|
||||
const int currentIndex =
|
||||
init ? UISettings::values.row_2_text_id
|
||||
init ? UISettings::values.row_2_text_id.GetValue()
|
||||
: ui->row_2_text_combobox->findData(ui->row_2_text_combobox->currentData());
|
||||
|
||||
ui->row_2_text_combobox->clear();
|
||||
|
||||
@@ -88,22 +88,22 @@ void ConfigureWeb::SetConfiguration() {
|
||||
ui->web_signup_link->setOpenExternalLinks(true);
|
||||
ui->web_token_info_link->setOpenExternalLinks(true);
|
||||
|
||||
if (Settings::values.yuzu_username.empty()) {
|
||||
if (Settings::values.yuzu_username.GetValue().empty()) {
|
||||
ui->username->setText(tr("Unspecified"));
|
||||
} else {
|
||||
ui->username->setText(QString::fromStdString(Settings::values.yuzu_username));
|
||||
ui->username->setText(QString::fromStdString(Settings::values.yuzu_username.GetValue()));
|
||||
}
|
||||
|
||||
ui->toggle_telemetry->setChecked(Settings::values.enable_telemetry);
|
||||
ui->edit_token->setText(QString::fromStdString(
|
||||
GenerateDisplayToken(Settings::values.yuzu_username, Settings::values.yuzu_token)));
|
||||
ui->toggle_telemetry->setChecked(Settings::values.enable_telemetry.GetValue());
|
||||
ui->edit_token->setText(QString::fromStdString(GenerateDisplayToken(
|
||||
Settings::values.yuzu_username.GetValue(), Settings::values.yuzu_token.GetValue())));
|
||||
|
||||
// Connect after setting the values, to avoid calling OnLoginChanged now
|
||||
connect(ui->edit_token, &QLineEdit::textChanged, this, &ConfigureWeb::OnLoginChanged);
|
||||
|
||||
user_verified = true;
|
||||
|
||||
ui->toggle_discordrpc->setChecked(UISettings::values.enable_discord_presence);
|
||||
ui->toggle_discordrpc->setChecked(UISettings::values.enable_discord_presence.GetValue());
|
||||
}
|
||||
|
||||
void ConfigureWeb::ApplyConfiguration() {
|
||||
|
||||
@@ -15,10 +15,10 @@
|
||||
namespace Debugger {
|
||||
void ToggleConsole() {
|
||||
static bool console_shown = false;
|
||||
if (console_shown == UISettings::values.show_console) {
|
||||
if (console_shown == UISettings::values.show_console.GetValue()) {
|
||||
return;
|
||||
} else {
|
||||
console_shown = UISettings::values.show_console;
|
||||
console_shown = UISettings::values.show_console.GetValue();
|
||||
}
|
||||
|
||||
#if defined(_WIN32) && !defined(_DEBUG)
|
||||
|
||||
@@ -244,7 +244,8 @@ void GameList::OnUpdateThemedIcons() {
|
||||
for (int i = 0; i < item_model->invisibleRootItem()->rowCount(); i++) {
|
||||
QStandardItem* child = item_model->invisibleRootItem()->child(i);
|
||||
|
||||
const int icon_size = std::min(static_cast<int>(UISettings::values.icon_size), 64);
|
||||
const int icon_size =
|
||||
std::min(static_cast<int>(UISettings::values.icon_size.GetValue()), 64);
|
||||
switch (child->data(GameListItem::TypeRole).value<GameListItemType>()) {
|
||||
case GameListItemType::SdmcDir:
|
||||
child->setData(
|
||||
|
||||
@@ -80,7 +80,7 @@ public:
|
||||
setData(qulonglong(program_id), ProgramIdRole);
|
||||
setData(game_type, FileTypeRole);
|
||||
|
||||
const u32 size = UISettings::values.icon_size;
|
||||
const u32 size = UISettings::values.icon_size.GetValue();
|
||||
|
||||
QPixmap picture;
|
||||
if (!picture.loadFromData(picture_data.data(), static_cast<u32>(picture_data.size()))) {
|
||||
@@ -108,8 +108,8 @@ public:
|
||||
data(TitleRole).toString(),
|
||||
}};
|
||||
|
||||
const auto& row1 = row_data.at(UISettings::values.row_1_text_id);
|
||||
const int row2_id = UISettings::values.row_2_text_id;
|
||||
const auto& row1 = row_data.at(UISettings::values.row_1_text_id.GetValue());
|
||||
const int row2_id = UISettings::values.row_2_text_id.GetValue();
|
||||
|
||||
if (role == SortRole) {
|
||||
return row1.toLower();
|
||||
@@ -233,7 +233,8 @@ public:
|
||||
UISettings::GameDir* game_dir = &directory;
|
||||
setData(QVariant(UISettings::values.game_dirs.indexOf(directory)), GameDirRole);
|
||||
|
||||
const int icon_size = std::min(static_cast<int>(UISettings::values.icon_size), 64);
|
||||
const int icon_size =
|
||||
std::min(static_cast<int>(UISettings::values.icon_size.GetValue()), 64);
|
||||
switch (dir_type) {
|
||||
case GameListItemType::SdmcDir:
|
||||
setData(
|
||||
@@ -294,7 +295,8 @@ public:
|
||||
explicit GameListAddDir() {
|
||||
setData(type(), TypeRole);
|
||||
|
||||
const int icon_size = std::min(static_cast<int>(UISettings::values.icon_size), 64);
|
||||
const int icon_size =
|
||||
std::min(static_cast<int>(UISettings::values.icon_size.GetValue()), 64);
|
||||
setData(QIcon::fromTheme(QStringLiteral("plus"))
|
||||
.pixmap(icon_size)
|
||||
.scaled(icon_size, icon_size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation),
|
||||
@@ -316,7 +318,8 @@ public:
|
||||
explicit GameListFavorites() {
|
||||
setData(type(), TypeRole);
|
||||
|
||||
const int icon_size = std::min(static_cast<int>(UISettings::values.icon_size), 64);
|
||||
const int icon_size =
|
||||
std::min(static_cast<int>(UISettings::values.icon_size.GetValue()), 64);
|
||||
setData(QIcon::fromTheme(QStringLiteral("star"))
|
||||
.pixmap(icon_size)
|
||||
.scaled(icon_size, icon_size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation),
|
||||
|
||||
@@ -156,11 +156,13 @@ enum class CalloutFlag : uint32_t {
|
||||
};
|
||||
|
||||
void GMainWindow::ShowTelemetryCallout() {
|
||||
if (UISettings::values.callout_flags & static_cast<uint32_t>(CalloutFlag::Telemetry)) {
|
||||
if (UISettings::values.callout_flags.GetValue() &
|
||||
static_cast<uint32_t>(CalloutFlag::Telemetry)) {
|
||||
return;
|
||||
}
|
||||
|
||||
UISettings::values.callout_flags |= static_cast<uint32_t>(CalloutFlag::Telemetry);
|
||||
UISettings::values.callout_flags =
|
||||
UISettings::values.callout_flags.GetValue() | static_cast<uint32_t>(CalloutFlag::Telemetry);
|
||||
const QString telemetry_message =
|
||||
tr("<a href='https://yuzu-emu.org/help/feature/telemetry/'>Anonymous "
|
||||
"data is collected</a> to help improve yuzu. "
|
||||
@@ -177,7 +179,7 @@ static void InitializeLogging() {
|
||||
using namespace Common;
|
||||
|
||||
Log::Filter log_filter;
|
||||
log_filter.ParseFilterString(Settings::values.log_filter);
|
||||
log_filter.ParseFilterString(Settings::values.log_filter.GetValue());
|
||||
Log::SetGlobalFilter(log_filter);
|
||||
|
||||
const auto log_dir = FS::GetYuzuPath(FS::YuzuPath::LogDir);
|
||||
@@ -216,7 +218,7 @@ GMainWindow::GMainWindow()
|
||||
default_theme_paths = QIcon::themeSearchPaths();
|
||||
UpdateUITheme();
|
||||
|
||||
SetDiscordEnabled(UISettings::values.enable_discord_presence);
|
||||
SetDiscordEnabled(UISettings::values.enable_discord_presence.GetValue());
|
||||
discord_rpc->Update();
|
||||
|
||||
RegisterMetaTypes();
|
||||
@@ -1060,23 +1062,24 @@ void GMainWindow::RestoreUIState() {
|
||||
render_window->restoreGeometry(UISettings::values.renderwindow_geometry);
|
||||
#if MICROPROFILE_ENABLED
|
||||
microProfileDialog->restoreGeometry(UISettings::values.microprofile_geometry);
|
||||
microProfileDialog->setVisible(UISettings::values.microprofile_visible);
|
||||
microProfileDialog->setVisible(UISettings::values.microprofile_visible.GetValue());
|
||||
#endif
|
||||
|
||||
game_list->LoadInterfaceLayout();
|
||||
|
||||
ui.action_Single_Window_Mode->setChecked(UISettings::values.single_window_mode);
|
||||
ui.action_Single_Window_Mode->setChecked(UISettings::values.single_window_mode.GetValue());
|
||||
ToggleWindowMode();
|
||||
|
||||
ui.action_Fullscreen->setChecked(UISettings::values.fullscreen);
|
||||
ui.action_Fullscreen->setChecked(UISettings::values.fullscreen.GetValue());
|
||||
|
||||
ui.action_Display_Dock_Widget_Headers->setChecked(UISettings::values.display_titlebar);
|
||||
ui.action_Display_Dock_Widget_Headers->setChecked(
|
||||
UISettings::values.display_titlebar.GetValue());
|
||||
OnDisplayTitleBars(ui.action_Display_Dock_Widget_Headers->isChecked());
|
||||
|
||||
ui.action_Show_Filter_Bar->setChecked(UISettings::values.show_filter_bar);
|
||||
ui.action_Show_Filter_Bar->setChecked(UISettings::values.show_filter_bar.GetValue());
|
||||
game_list->SetFilterVisible(ui.action_Show_Filter_Bar->isChecked());
|
||||
|
||||
ui.action_Show_Status_Bar->setChecked(UISettings::values.show_status_bar);
|
||||
ui.action_Show_Status_Bar->setChecked(UISettings::values.show_status_bar.GetValue());
|
||||
statusBar()->setVisible(ui.action_Show_Status_Bar->isChecked());
|
||||
Debugger::ToggleConsole();
|
||||
}
|
||||
@@ -1243,13 +1246,14 @@ bool GMainWindow::LoadROM(const QString& filename, std::size_t program_index) {
|
||||
const Core::System::ResultStatus result{
|
||||
system.Load(*render_window, filename.toStdString(), program_index)};
|
||||
|
||||
const auto drd_callout =
|
||||
(UISettings::values.callout_flags & static_cast<u32>(CalloutFlag::DRDDeprecation)) == 0;
|
||||
const auto drd_callout = (UISettings::values.callout_flags.GetValue() &
|
||||
static_cast<u32>(CalloutFlag::DRDDeprecation)) == 0;
|
||||
|
||||
if (result == Core::System::ResultStatus::Success &&
|
||||
system.GetAppLoader().GetFileType() == Loader::FileType::DeconstructedRomDirectory &&
|
||||
drd_callout) {
|
||||
UISettings::values.callout_flags |= static_cast<u32>(CalloutFlag::DRDDeprecation);
|
||||
UISettings::values.callout_flags = UISettings::values.callout_flags.GetValue() |
|
||||
static_cast<u32>(CalloutFlag::DRDDeprecation);
|
||||
QMessageBox::warning(
|
||||
this, tr("Warning Outdated Game Format"),
|
||||
tr("You are using the deconstructed ROM directory format for this game, which is an "
|
||||
@@ -2453,7 +2457,8 @@ void GMainWindow::ErrorDisplayDisplayError(QString error_code, QString error_tex
|
||||
}
|
||||
|
||||
void GMainWindow::OnMenuReportCompatibility() {
|
||||
if (!Settings::values.yuzu_token.empty() && !Settings::values.yuzu_username.empty()) {
|
||||
if (!Settings::values.yuzu_token.GetValue().empty() &&
|
||||
!Settings::values.yuzu_username.GetValue().empty()) {
|
||||
CompatDB compatdb{this};
|
||||
compatdb.exec();
|
||||
} else {
|
||||
@@ -2618,7 +2623,7 @@ void GMainWindow::ResetWindowSize1080() {
|
||||
|
||||
void GMainWindow::OnConfigure() {
|
||||
const auto old_theme = UISettings::values.theme;
|
||||
const bool old_discord_presence = UISettings::values.enable_discord_presence;
|
||||
const bool old_discord_presence = UISettings::values.enable_discord_presence.GetValue();
|
||||
|
||||
ConfigureDialog configure_dialog(this, hotkey_registry, input_subsystem.get());
|
||||
connect(&configure_dialog, &ConfigureDialog::LanguageChanged, this,
|
||||
@@ -2675,8 +2680,8 @@ void GMainWindow::OnConfigure() {
|
||||
if (UISettings::values.theme != old_theme) {
|
||||
UpdateUITheme();
|
||||
}
|
||||
if (UISettings::values.enable_discord_presence != old_discord_presence) {
|
||||
SetDiscordEnabled(UISettings::values.enable_discord_presence);
|
||||
if (UISettings::values.enable_discord_presence.GetValue() != old_discord_presence) {
|
||||
SetDiscordEnabled(UISettings::values.enable_discord_presence.GetValue());
|
||||
}
|
||||
emit UpdateThemedIcons();
|
||||
|
||||
@@ -2832,7 +2837,8 @@ void GMainWindow::OnCaptureScreenshot() {
|
||||
}
|
||||
}
|
||||
#endif
|
||||
render_window->CaptureScreenshot(UISettings::values.screenshot_resolution_factor, filename);
|
||||
render_window->CaptureScreenshot(UISettings::values.screenshot_resolution_factor.GetValue(),
|
||||
filename);
|
||||
OnStartGame();
|
||||
}
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#include <QStringList>
|
||||
#include <QVector>
|
||||
#include "common/common_types.h"
|
||||
#include "common/settings.h"
|
||||
|
||||
namespace UISettings {
|
||||
|
||||
@@ -48,26 +49,26 @@ struct Values {
|
||||
QByteArray gamelist_header_state;
|
||||
|
||||
QByteArray microprofile_geometry;
|
||||
bool microprofile_visible;
|
||||
Settings::BasicSetting<bool> microprofile_visible{false, "microProfileDialogVisible"};
|
||||
|
||||
bool single_window_mode;
|
||||
bool fullscreen;
|
||||
bool display_titlebar;
|
||||
bool show_filter_bar;
|
||||
bool show_status_bar;
|
||||
Settings::BasicSetting<bool> single_window_mode{true, "singleWindowMode"};
|
||||
Settings::BasicSetting<bool> fullscreen{false, "fullscreen"};
|
||||
Settings::BasicSetting<bool> display_titlebar{true, "displayTitleBars"};
|
||||
Settings::BasicSetting<bool> show_filter_bar{true, "showFilterBar"};
|
||||
Settings::BasicSetting<bool> show_status_bar{true, "showStatusBar"};
|
||||
|
||||
bool confirm_before_closing;
|
||||
bool first_start;
|
||||
bool pause_when_in_background;
|
||||
bool hide_mouse;
|
||||
Settings::BasicSetting<bool> confirm_before_closing{true, "confirmClose"};
|
||||
Settings::BasicSetting<bool> first_start{true, "firstStart"};
|
||||
Settings::BasicSetting<bool> pause_when_in_background{false, "pauseWhenInBackground"};
|
||||
Settings::BasicSetting<bool> hide_mouse{false, "hideInactiveMouse"};
|
||||
|
||||
bool select_user_on_boot;
|
||||
Settings::BasicSetting<bool> select_user_on_boot{false, "select_user_on_boot"};
|
||||
|
||||
// Discord RPC
|
||||
bool enable_discord_presence;
|
||||
Settings::BasicSetting<bool> enable_discord_presence{true, "enable_discord_presence"};
|
||||
|
||||
bool enable_screenshot_save_as;
|
||||
u16 screenshot_resolution_factor;
|
||||
Settings::BasicSetting<bool> enable_screenshot_save_as{true, "enable_screenshot_save_as"};
|
||||
Settings::BasicSetting<u16> screenshot_resolution_factor{0, "screenshot_resolution_factor"};
|
||||
|
||||
QString roms_path;
|
||||
QString symbols_path;
|
||||
@@ -83,18 +84,18 @@ struct Values {
|
||||
// Shortcut name <Shortcut, context>
|
||||
std::vector<Shortcut> shortcuts;
|
||||
|
||||
uint32_t callout_flags;
|
||||
Settings::BasicSetting<uint32_t> callout_flags{0, "calloutFlags"};
|
||||
|
||||
// logging
|
||||
bool show_console;
|
||||
Settings::BasicSetting<bool> show_console{false, "showConsole"};
|
||||
|
||||
// Game List
|
||||
bool show_add_ons;
|
||||
uint32_t icon_size;
|
||||
uint8_t row_1_text_id;
|
||||
uint8_t row_2_text_id;
|
||||
Settings::BasicSetting<bool> show_add_ons{true, "show_add_ons"};
|
||||
Settings::BasicSetting<uint32_t> icon_size{64, "icon_size"};
|
||||
Settings::BasicSetting<uint8_t> row_1_text_id{3, "row_1_text_id"};
|
||||
Settings::BasicSetting<uint8_t> row_2_text_id{2, "row_2_text_id"};
|
||||
std::atomic_bool is_game_list_reload_pending{false};
|
||||
bool cache_game_list;
|
||||
Settings::BasicSetting<bool> cache_game_list{true, "cache_game_list"};
|
||||
|
||||
bool configuration_applied;
|
||||
bool reset_to_defaults;
|
||||
|
||||
@@ -241,6 +241,22 @@ static const std::array<int, 8> keyboard_mods{
|
||||
SDL_SCANCODE_RCTRL, SDL_SCANCODE_RSHIFT, SDL_SCANCODE_RALT, SDL_SCANCODE_RGUI,
|
||||
};
|
||||
|
||||
template <>
|
||||
void Config::ReadSetting(const std::string& group, Settings::BasicSetting<std::string>& setting) {
|
||||
setting = sdl2_config->Get(group, setting.GetLabel(), setting.GetDefault());
|
||||
}
|
||||
|
||||
template <>
|
||||
void Config::ReadSetting(const std::string& group, Settings::BasicSetting<bool>& setting) {
|
||||
setting = sdl2_config->GetBoolean(group, setting.GetLabel(), setting.GetDefault());
|
||||
}
|
||||
|
||||
template <typename Type>
|
||||
void Config::ReadSetting(const std::string& group, Settings::BasicSetting<Type>& setting) {
|
||||
setting = static_cast<Type>(sdl2_config->GetInteger(group, setting.GetLabel(),
|
||||
static_cast<long>(setting.GetDefault())));
|
||||
}
|
||||
|
||||
void Config::ReadValues() {
|
||||
// Controls
|
||||
for (std::size_t p = 0; p < Settings::values.players.GetValue().size(); ++p) {
|
||||
@@ -264,8 +280,7 @@ void Config::ReadValues() {
|
||||
}
|
||||
}
|
||||
|
||||
Settings::values.mouse_enabled =
|
||||
sdl2_config->GetBoolean("ControlsGeneral", "mouse_enabled", false);
|
||||
ReadSetting("ControlsGeneral", Settings::values.mouse_enabled);
|
||||
for (int i = 0; i < Settings::NativeMouseButton::NumMouseButtons; ++i) {
|
||||
std::string default_param = InputCommon::GenerateKeyboardParam(default_mouse_buttons[i]);
|
||||
Settings::values.mouse_buttons[i] = sdl2_config->Get(
|
||||
@@ -275,14 +290,11 @@ void Config::ReadValues() {
|
||||
Settings::values.mouse_buttons[i] = default_param;
|
||||
}
|
||||
|
||||
Settings::values.motion_device = sdl2_config->Get(
|
||||
"ControlsGeneral", "motion_device", "engine:motion_emu,update_period:100,sensitivity:0.01");
|
||||
ReadSetting("ControlsGeneral", Settings::values.motion_device);
|
||||
|
||||
Settings::values.keyboard_enabled =
|
||||
sdl2_config->GetBoolean("ControlsGeneral", "keyboard_enabled", false);
|
||||
ReadSetting("ControlsGeneral", Settings::values.keyboard_enabled);
|
||||
|
||||
Settings::values.debug_pad_enabled =
|
||||
sdl2_config->GetBoolean("ControlsGeneral", "debug_pad_enabled", false);
|
||||
ReadSetting("ControlsGeneral", Settings::values.debug_pad_enabled);
|
||||
for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) {
|
||||
std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]);
|
||||
Settings::values.debug_pad_buttons[i] = sdl2_config->Get(
|
||||
@@ -303,12 +315,9 @@ void Config::ReadValues() {
|
||||
Settings::values.debug_pad_analogs[i] = default_param;
|
||||
}
|
||||
|
||||
Settings::values.vibration_enabled.SetValue(
|
||||
sdl2_config->GetBoolean("ControlsGeneral", "vibration_enabled", true));
|
||||
Settings::values.enable_accurate_vibrations.SetValue(
|
||||
sdl2_config->GetBoolean("ControlsGeneral", "enable_accurate_vibrations", false));
|
||||
Settings::values.motion_enabled.SetValue(
|
||||
sdl2_config->GetBoolean("ControlsGeneral", "motion_enabled", true));
|
||||
ReadSetting("ControlsGeneral", Settings::values.vibration_enabled);
|
||||
ReadSetting("ControlsGeneral", Settings::values.enable_accurate_vibrations);
|
||||
ReadSetting("ControlsGeneral", Settings::values.motion_enabled);
|
||||
Settings::values.touchscreen.enabled =
|
||||
sdl2_config->GetBoolean("ControlsGeneral", "touch_enabled", true);
|
||||
Settings::values.touchscreen.rotation_angle =
|
||||
@@ -349,13 +358,11 @@ void Config::ReadValues() {
|
||||
Settings::TouchFromButtonMap{"default", {}});
|
||||
num_touch_from_button_maps = 1;
|
||||
}
|
||||
Settings::values.use_touch_from_button =
|
||||
sdl2_config->GetBoolean("ControlsGeneral", "use_touch_from_button", false);
|
||||
Settings::values.touch_from_button_map_index =
|
||||
std::clamp(Settings::values.touch_from_button_map_index, 0, num_touch_from_button_maps - 1);
|
||||
ReadSetting("ControlsGeneral", Settings::values.use_touch_from_button);
|
||||
Settings::values.touch_from_button_map_index = std::clamp(
|
||||
Settings::values.touch_from_button_map_index.GetValue(), 0, num_touch_from_button_maps - 1);
|
||||
|
||||
Settings::values.udp_input_servers =
|
||||
sdl2_config->Get("Controls", "udp_input_address", InputCommon::CemuhookUDP::DEFAULT_SRV);
|
||||
ReadSetting("ControlsGeneral", Settings::values.udp_input_servers);
|
||||
|
||||
std::transform(keyboard_keys.begin(), keyboard_keys.end(),
|
||||
Settings::values.keyboard_keys.begin(), InputCommon::GenerateKeyboardParam);
|
||||
@@ -367,8 +374,7 @@ void Config::ReadValues() {
|
||||
Settings::values.keyboard_mods.begin(), InputCommon::GenerateKeyboardParam);
|
||||
|
||||
// Data Storage
|
||||
Settings::values.use_virtual_sd =
|
||||
sdl2_config->GetBoolean("Data Storage", "use_virtual_sd", true);
|
||||
ReadSetting("Data Storage", Settings::values.use_virtual_sd);
|
||||
FS::SetYuzuPath(FS::YuzuPath::NANDDir,
|
||||
sdl2_config->Get("Data Storage", "nand_directory",
|
||||
FS::GetYuzuPathString(FS::YuzuPath::NANDDir)));
|
||||
@@ -381,18 +387,16 @@ void Config::ReadValues() {
|
||||
FS::SetYuzuPath(FS::YuzuPath::DumpDir,
|
||||
sdl2_config->Get("Data Storage", "dump_directory",
|
||||
FS::GetYuzuPathString(FS::YuzuPath::DumpDir)));
|
||||
Settings::values.gamecard_inserted =
|
||||
sdl2_config->GetBoolean("Data Storage", "gamecard_inserted", false);
|
||||
Settings::values.gamecard_current_game =
|
||||
sdl2_config->GetBoolean("Data Storage", "gamecard_current_game", false);
|
||||
Settings::values.gamecard_path = sdl2_config->Get("Data Storage", "gamecard_path", "");
|
||||
ReadSetting("Data Storage", Settings::values.gamecard_inserted);
|
||||
ReadSetting("Data Storage", Settings::values.gamecard_current_game);
|
||||
ReadSetting("Data Storage", Settings::values.gamecard_path);
|
||||
|
||||
// System
|
||||
Settings::values.use_docked_mode.SetValue(
|
||||
sdl2_config->GetBoolean("System", "use_docked_mode", true));
|
||||
ReadSetting("System", Settings::values.use_docked_mode);
|
||||
|
||||
Settings::values.current_user = std::clamp<int>(
|
||||
sdl2_config->GetInteger("System", "current_user", 0), 0, Service::Account::MAX_USERS - 1);
|
||||
ReadSetting("System", Settings::values.current_user);
|
||||
Settings::values.current_user = std::clamp<int>(Settings::values.current_user.GetValue(), 0,
|
||||
Service::Account::MAX_USERS - 1);
|
||||
|
||||
const auto rng_seed_enabled = sdl2_config->GetBoolean("System", "rng_seed_enabled", false);
|
||||
if (rng_seed_enabled) {
|
||||
@@ -409,89 +413,60 @@ void Config::ReadValues() {
|
||||
Settings::values.custom_rtc = std::nullopt;
|
||||
}
|
||||
|
||||
Settings::values.language_index.SetValue(
|
||||
sdl2_config->GetInteger("System", "language_index", 1));
|
||||
Settings::values.time_zone_index.SetValue(
|
||||
sdl2_config->GetInteger("System", "time_zone_index", 0));
|
||||
ReadSetting("System", Settings::values.language_index);
|
||||
ReadSetting("System", Settings::values.time_zone_index);
|
||||
|
||||
// Core
|
||||
Settings::values.use_multi_core.SetValue(
|
||||
sdl2_config->GetBoolean("Core", "use_multi_core", true));
|
||||
ReadSetting("Core", Settings::values.use_multi_core);
|
||||
|
||||
// Renderer
|
||||
const int renderer_backend = sdl2_config->GetInteger(
|
||||
"Renderer", "backend", static_cast<int>(Settings::RendererBackend::OpenGL));
|
||||
Settings::values.renderer_backend.SetValue(
|
||||
static_cast<Settings::RendererBackend>(renderer_backend));
|
||||
Settings::values.renderer_debug = sdl2_config->GetBoolean("Renderer", "debug", false);
|
||||
Settings::values.vulkan_device.SetValue(
|
||||
sdl2_config->GetInteger("Renderer", "vulkan_device", 0));
|
||||
ReadSetting("Renderer", Settings::values.renderer_backend);
|
||||
ReadSetting("Renderer", Settings::values.renderer_debug);
|
||||
ReadSetting("Renderer", Settings::values.vulkan_device);
|
||||
|
||||
Settings::values.aspect_ratio.SetValue(
|
||||
static_cast<int>(sdl2_config->GetInteger("Renderer", "aspect_ratio", 0)));
|
||||
Settings::values.max_anisotropy.SetValue(
|
||||
static_cast<int>(sdl2_config->GetInteger("Renderer", "max_anisotropy", 0)));
|
||||
Settings::values.use_frame_limit.SetValue(
|
||||
sdl2_config->GetBoolean("Renderer", "use_frame_limit", true));
|
||||
Settings::values.frame_limit.SetValue(
|
||||
static_cast<u16>(sdl2_config->GetInteger("Renderer", "frame_limit", 100)));
|
||||
Settings::values.use_disk_shader_cache.SetValue(
|
||||
sdl2_config->GetBoolean("Renderer", "use_disk_shader_cache", false));
|
||||
const int gpu_accuracy_level = sdl2_config->GetInteger("Renderer", "gpu_accuracy", 1);
|
||||
Settings::values.gpu_accuracy.SetValue(static_cast<Settings::GPUAccuracy>(gpu_accuracy_level));
|
||||
Settings::values.use_asynchronous_gpu_emulation.SetValue(
|
||||
sdl2_config->GetBoolean("Renderer", "use_asynchronous_gpu_emulation", true));
|
||||
Settings::values.use_vsync.SetValue(
|
||||
static_cast<u16>(sdl2_config->GetInteger("Renderer", "use_vsync", 1)));
|
||||
Settings::values.disable_fps_limit.SetValue(
|
||||
sdl2_config->GetBoolean("Renderer", "disable_fps_limit", false));
|
||||
Settings::values.use_assembly_shaders.SetValue(
|
||||
sdl2_config->GetBoolean("Renderer", "use_assembly_shaders", true));
|
||||
Settings::values.use_asynchronous_shaders.SetValue(
|
||||
sdl2_config->GetBoolean("Renderer", "use_asynchronous_shaders", false));
|
||||
Settings::values.use_nvdec_emulation.SetValue(
|
||||
sdl2_config->GetBoolean("Renderer", "use_nvdec_emulation", true));
|
||||
Settings::values.accelerate_astc.SetValue(
|
||||
sdl2_config->GetBoolean("Renderer", "accelerate_astc", true));
|
||||
Settings::values.use_fast_gpu_time.SetValue(
|
||||
sdl2_config->GetBoolean("Renderer", "use_fast_gpu_time", true));
|
||||
ReadSetting("Renderer", Settings::values.aspect_ratio);
|
||||
ReadSetting("Renderer", Settings::values.max_anisotropy);
|
||||
ReadSetting("Renderer", Settings::values.use_frame_limit);
|
||||
ReadSetting("Renderer", Settings::values.frame_limit);
|
||||
ReadSetting("Renderer", Settings::values.use_disk_shader_cache);
|
||||
ReadSetting("Renderer", Settings::values.gpu_accuracy);
|
||||
ReadSetting("Renderer", Settings::values.use_asynchronous_gpu_emulation);
|
||||
ReadSetting("Renderer", Settings::values.use_vsync);
|
||||
ReadSetting("Renderer", Settings::values.disable_fps_limit);
|
||||
ReadSetting("Renderer", Settings::values.use_assembly_shaders);
|
||||
ReadSetting("Renderer", Settings::values.use_asynchronous_shaders);
|
||||
ReadSetting("Renderer", Settings::values.use_nvdec_emulation);
|
||||
ReadSetting("Renderer", Settings::values.accelerate_astc);
|
||||
ReadSetting("Renderer", Settings::values.use_fast_gpu_time);
|
||||
|
||||
Settings::values.bg_red.SetValue(
|
||||
static_cast<float>(sdl2_config->GetReal("Renderer", "bg_red", 0.0)));
|
||||
Settings::values.bg_green.SetValue(
|
||||
static_cast<float>(sdl2_config->GetReal("Renderer", "bg_green", 0.0)));
|
||||
Settings::values.bg_blue.SetValue(
|
||||
static_cast<float>(sdl2_config->GetReal("Renderer", "bg_blue", 0.0)));
|
||||
ReadSetting("Renderer", Settings::values.bg_red);
|
||||
ReadSetting("Renderer", Settings::values.bg_green);
|
||||
ReadSetting("Renderer", Settings::values.bg_blue);
|
||||
|
||||
// Audio
|
||||
Settings::values.sink_id = sdl2_config->Get("Audio", "output_engine", "auto");
|
||||
Settings::values.enable_audio_stretching.SetValue(
|
||||
sdl2_config->GetBoolean("Audio", "enable_audio_stretching", true));
|
||||
Settings::values.audio_device_id = sdl2_config->Get("Audio", "output_device", "auto");
|
||||
Settings::values.volume.SetValue(
|
||||
static_cast<float>(sdl2_config->GetReal("Audio", "volume", 1)));
|
||||
ReadSetting("Audio", Settings::values.sink_id);
|
||||
ReadSetting("Audio", Settings::values.enable_audio_stretching);
|
||||
ReadSetting("Audio", Settings::values.audio_device_id);
|
||||
ReadSetting("Audio", Settings::values.volume);
|
||||
|
||||
// Miscellaneous
|
||||
Settings::values.log_filter = sdl2_config->Get("Miscellaneous", "log_filter", "*:Trace");
|
||||
Settings::values.use_dev_keys = sdl2_config->GetBoolean("Miscellaneous", "use_dev_keys", false);
|
||||
// log_filter has a different default here than from common
|
||||
Settings::values.log_filter =
|
||||
sdl2_config->Get("Miscellaneous", Settings::values.log_filter.GetLabel(), "*:Trace");
|
||||
ReadSetting("Miscellaneous", Settings::values.use_dev_keys);
|
||||
|
||||
// Debugging
|
||||
Settings::values.record_frame_times =
|
||||
sdl2_config->GetBoolean("Debugging", "record_frame_times", false);
|
||||
Settings::values.program_args = sdl2_config->Get("Debugging", "program_args", "");
|
||||
Settings::values.dump_exefs = sdl2_config->GetBoolean("Debugging", "dump_exefs", false);
|
||||
Settings::values.dump_nso = sdl2_config->GetBoolean("Debugging", "dump_nso", false);
|
||||
Settings::values.enable_fs_access_log =
|
||||
sdl2_config->GetBoolean("Debugging", "enable_fs_access_log", false);
|
||||
Settings::values.reporting_services =
|
||||
sdl2_config->GetBoolean("Debugging", "reporting_services", false);
|
||||
Settings::values.quest_flag = sdl2_config->GetBoolean("Debugging", "quest_flag", false);
|
||||
Settings::values.use_debug_asserts =
|
||||
sdl2_config->GetBoolean("Debugging", "use_debug_asserts", false);
|
||||
Settings::values.use_auto_stub = sdl2_config->GetBoolean("Debugging", "use_auto_stub", false);
|
||||
|
||||
Settings::values.disable_macro_jit =
|
||||
sdl2_config->GetBoolean("Debugging", "disable_macro_jit", false);
|
||||
ReadSetting("Debugging", Settings::values.program_args);
|
||||
ReadSetting("Debugging", Settings::values.dump_exefs);
|
||||
ReadSetting("Debugging", Settings::values.dump_nso);
|
||||
ReadSetting("Debugging", Settings::values.enable_fs_access_log);
|
||||
ReadSetting("Debugging", Settings::values.reporting_services);
|
||||
ReadSetting("Debugging", Settings::values.quest_flag);
|
||||
ReadSetting("Debugging", Settings::values.use_debug_asserts);
|
||||
ReadSetting("Debugging", Settings::values.use_auto_stub);
|
||||
ReadSetting("Debugging", Settings::values.disable_macro_jit);
|
||||
|
||||
const auto title_list = sdl2_config->Get("AddOns", "title_ids", "");
|
||||
std::stringstream ss(title_list);
|
||||
@@ -511,17 +486,14 @@ void Config::ReadValues() {
|
||||
}
|
||||
|
||||
// Web Service
|
||||
Settings::values.enable_telemetry =
|
||||
sdl2_config->GetBoolean("WebService", "enable_telemetry", true);
|
||||
Settings::values.web_api_url =
|
||||
sdl2_config->Get("WebService", "web_api_url", "https://api.yuzu-emu.org");
|
||||
Settings::values.yuzu_username = sdl2_config->Get("WebService", "yuzu_username", "");
|
||||
Settings::values.yuzu_token = sdl2_config->Get("WebService", "yuzu_token", "");
|
||||
ReadSetting("WebService", Settings::values.enable_telemetry);
|
||||
ReadSetting("WebService", Settings::values.web_api_url);
|
||||
ReadSetting("WebService", Settings::values.yuzu_username);
|
||||
ReadSetting("WebService", Settings::values.yuzu_token);
|
||||
|
||||
// Services
|
||||
Settings::values.bcat_backend = sdl2_config->Get("Services", "bcat_backend", "none");
|
||||
Settings::values.bcat_boxcat_local =
|
||||
sdl2_config->GetBoolean("Services", "bcat_boxcat_local", false);
|
||||
ReadSetting("Services", Settings::values.bcat_backend);
|
||||
ReadSetting("Services", Settings::values.bcat_boxcat_local);
|
||||
}
|
||||
|
||||
void Config::Reload() {
|
||||
|
||||
@@ -8,6 +8,8 @@
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include "common/settings.h"
|
||||
|
||||
class INIReader;
|
||||
|
||||
class Config {
|
||||
@@ -22,4 +24,14 @@ public:
|
||||
~Config();
|
||||
|
||||
void Reload();
|
||||
|
||||
private:
|
||||
/**
|
||||
* Applies a value read from the sdl2_config to a BasicSetting.
|
||||
*
|
||||
* @param group The name of the INI group
|
||||
* @param setting The yuzu setting to modify
|
||||
*/
|
||||
template <typename Type>
|
||||
void ReadSetting(const std::string& group, Settings::BasicSetting<Type>& setting);
|
||||
};
|
||||
|
||||
@@ -232,7 +232,7 @@ use_vsync =
|
||||
use_caches_gc =
|
||||
|
||||
# The clear color for the renderer. What shows up on the sides of the bottom screen.
|
||||
# Must be in range of 0.0-1.0. Defaults to 1.0 for all.
|
||||
# Must be in range of 0-255. Defaults to 0 for all.
|
||||
bg_red =
|
||||
bg_blue =
|
||||
bg_green =
|
||||
@@ -281,7 +281,7 @@ enable_audio_stretching =
|
||||
output_device =
|
||||
|
||||
# Output volume.
|
||||
# 1.0 (default): 100%, 0.0; mute
|
||||
# 100 (default): 100%, 0; mute
|
||||
volume =
|
||||
|
||||
[Data Storage]
|
||||
|
||||
@@ -78,7 +78,7 @@ static void InitializeLogging() {
|
||||
using namespace Common;
|
||||
|
||||
Log::Filter log_filter(Log::Level::Debug);
|
||||
log_filter.ParseFilterString(Settings::values.log_filter);
|
||||
log_filter.ParseFilterString(static_cast<std::string>(Settings::values.log_filter));
|
||||
Log::SetGlobalFilter(log_filter);
|
||||
|
||||
Log::AddBackend(std::make_unique<Log::ColorConsoleBackend>());
|
||||
|
||||
Reference in New Issue
Block a user