early-access version 1860
This commit is contained in:
parent
8a46c57b52
commit
2da54f6e73
20 changed files with 314 additions and 731 deletions
|
@ -1,7 +1,7 @@
|
||||||
yuzu emulator early access
|
yuzu emulator early access
|
||||||
=============
|
=============
|
||||||
|
|
||||||
This is the source code for early-access 1859.
|
This is the source code for early-access 1860.
|
||||||
|
|
||||||
## Legal Notice
|
## Legal Notice
|
||||||
|
|
||||||
|
|
|
@ -29,10 +29,9 @@ namespace {
|
||||||
(static_cast<float>(r_channel) * r_mix_amount)));
|
(static_cast<float>(r_channel) * r_mix_amount)));
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] static constexpr std::tuple<s16, s16> Mix6To2(s16 fl_channel, s16 fr_channel,
|
[[maybe_unused, nodiscard]] static constexpr std::tuple<s16, s16> Mix6To2(
|
||||||
s16 fc_channel,
|
s16 fl_channel, s16 fr_channel, s16 fc_channel, [[maybe_unused]] s16 lf_channel, s16 bl_channel,
|
||||||
[[maybe_unused]] s16 lf_channel,
|
s16 br_channel) {
|
||||||
s16 bl_channel, s16 br_channel) {
|
|
||||||
// Front channels are mixed 36.94%, Center channels are mixed to be 26.12% & the back channels
|
// Front channels are mixed 36.94%, Center channels are mixed to be 26.12% & the back channels
|
||||||
// are mixed to be 36.94%
|
// are mixed to be 36.94%
|
||||||
|
|
||||||
|
@ -57,11 +56,11 @@ namespace {
|
||||||
const std::array<float_le, 4>& coeff) {
|
const std::array<float_le, 4>& coeff) {
|
||||||
const auto left =
|
const auto left =
|
||||||
static_cast<float>(fl_channel) * coeff[0] + static_cast<float>(fc_channel) * coeff[1] +
|
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 =
|
const auto right =
|
||||||
static_cast<float>(fr_channel) * coeff[0] + static_cast<float>(fc_channel) * coeff[1] +
|
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))};
|
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 channel_count = buffer_offsets.size();
|
||||||
const auto& final_mix = mix_context.GetFinalMixInfo();
|
const auto& final_mix = mix_context.GetFinalMixInfo();
|
||||||
const auto& in_params = final_mix.GetInParams();
|
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++) {
|
for (std::size_t i = 0; i < channel_count; i++) {
|
||||||
mix_buffers[i] =
|
mix_buffers[i] =
|
||||||
command_generator.GetMixBuffer(in_params.buffer_offset + buffer_offsets[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);
|
buffer[i * stream_channel_count + 0] = Mix2To1(fl_sample, fr_sample);
|
||||||
} else if (stream_channel_count == 2) {
|
} else if (stream_channel_count == 2) {
|
||||||
// Mix all channels into 2 channels
|
// Mix all channels into 2 channels
|
||||||
if (sink_context.HasDownMixingCoefficients()) {
|
const auto [left, right] = Mix6To2WithCoefficients(
|
||||||
const auto [left, right] = Mix6To2WithCoefficients(
|
fl_sample, fr_sample, fc_sample, lf_sample, bl_sample, br_sample,
|
||||||
fl_sample, fr_sample, fc_sample, lf_sample, bl_sample, br_sample,
|
sink_context.GetDownmixCoefficients());
|
||||||
sink_context.GetDownmixCoefficients());
|
buffer[i * stream_channel_count + 0] = left;
|
||||||
buffer[i * stream_channel_count + 0] = left;
|
buffer[i * stream_channel_count + 1] = right;
|
||||||
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;
|
|
||||||
}
|
|
||||||
} else if (stream_channel_count == 6) {
|
} else if (stream_channel_count == 6) {
|
||||||
// Pass through
|
// Pass through
|
||||||
buffer[i * stream_channel_count + 0] = fl_sample;
|
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};
|
0.72867f, 0.69794f, 0.5464f, 0.24563f, 0.45214f, 0.44042f};
|
||||||
|
|
||||||
template <std::size_t N>
|
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 i = 0; i < static_cast<std::size_t>(sample_count); i += N) {
|
||||||
for (std::size_t j = 0; j < N; j++) {
|
for (std::size_t j = 0; j < N; j++) {
|
||||||
output[i + j] +=
|
output[i + j] +=
|
||||||
|
@ -40,7 +40,17 @@ void ApplyMix(s32* output, const s32* input, s32 gain, s32 sample_count) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
s32 ApplyMixRamp(s32* output, const s32* input, float gain, float delta, s32 sample_count) {
|
s32 ApplyMixRamp(std::span<s32> output, std::span<const s32> input, float gain, float delta,
|
||||||
|
s32 sample_count) {
|
||||||
|
// XC2 passes in NaN mix volumes, causing further issues as we handle everything as s32 rather
|
||||||
|
// than float, so the NaN propogation is lost. As the samples get further modified for
|
||||||
|
// volume etc, they can get out of NaN range, so a later heuristic for catching this is
|
||||||
|
// more difficult. Handle that here by setting these samples to silence.
|
||||||
|
if (std::isnan(gain)) {
|
||||||
|
gain = 0.0f;
|
||||||
|
delta = 0.0f;
|
||||||
|
}
|
||||||
|
|
||||||
s32 x = 0;
|
s32 x = 0;
|
||||||
for (s32 i = 0; i < sample_count; i++) {
|
for (s32 i = 0; i < sample_count; i++) {
|
||||||
x = static_cast<s32>(static_cast<float>(input[i]) * gain);
|
x = static_cast<s32>(static_cast<float>(input[i]) * gain);
|
||||||
|
@ -50,20 +60,22 @@ s32 ApplyMixRamp(s32* output, const s32* input, float gain, float delta, s32 sam
|
||||||
return x;
|
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++) {
|
for (s32 i = 0; i < sample_count; i++) {
|
||||||
output[i] = static_cast<s32>((static_cast<s64>(input[i]) * gain + 0x4000) >> 15);
|
output[i] = static_cast<s32>((static_cast<s64>(input[i]) * gain + 0x4000) >> 15);
|
||||||
gain += delta;
|
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++) {
|
for (s32 i = 0; i < sample_count; i++) {
|
||||||
output[i] = static_cast<s32>((static_cast<s64>(input[i]) * gain + 0x4000) >> 15);
|
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;
|
const bool positive = first_sample > 0;
|
||||||
auto final_sample = std::abs(first_sample);
|
auto final_sample = std::abs(first_sample);
|
||||||
for (s32 i = 0; i < sample_count; i++) {
|
for (s32 i = 0; i < sample_count; i++) {
|
||||||
|
@ -128,10 +140,10 @@ constexpr std::array<std::size_t, 20> REVERB_TAP_INDEX_6CH{4, 0, 0, 1, 1, 1, 1,
|
||||||
1, 1, 1, 0, 0, 0, 0, 3, 3, 3};
|
1, 1, 1, 0, 0, 0, 0, 3, 3, 3};
|
||||||
|
|
||||||
template <std::size_t CHANNEL_COUNT>
|
template <std::size_t CHANNEL_COUNT>
|
||||||
void ApplyReverbGeneric(I3dl2ReverbState& state,
|
void ApplyReverbGeneric(
|
||||||
const std::array<const s32*, AudioCommon::MAX_CHANNEL_COUNT>& input,
|
I3dl2ReverbState& state,
|
||||||
const std::array<s32*, AudioCommon::MAX_CHANNEL_COUNT>& output,
|
const std::array<std::span<const s32>, AudioCommon::MAX_CHANNEL_COUNT>& input,
|
||||||
s32 sample_count) {
|
const std::array<std::span<s32>, AudioCommon::MAX_CHANNEL_COUNT>& output, s32 sample_count) {
|
||||||
|
|
||||||
auto GetTapLookup = []() {
|
auto GetTapLookup = []() {
|
||||||
if constexpr (CHANNEL_COUNT == 1) {
|
if constexpr (CHANNEL_COUNT == 1) {
|
||||||
|
@ -457,8 +469,8 @@ void CommandGenerator::GenerateBiquadFilterCommand([[maybe_unused]] s32 mix_buff
|
||||||
"input_mix_buffer={}, output_mix_buffer={}",
|
"input_mix_buffer={}, output_mix_buffer={}",
|
||||||
node_id, input_offset, output_offset);
|
node_id, input_offset, output_offset);
|
||||||
}
|
}
|
||||||
const auto* input = GetMixBuffer(input_offset);
|
std::span<const s32> input = GetMixBuffer(input_offset);
|
||||||
auto* output = GetMixBuffer(output_offset);
|
std::span<s32> output = GetMixBuffer(output_offset);
|
||||||
|
|
||||||
// Biquad filter parameters
|
// Biquad filter parameters
|
||||||
const auto [n0, n1, n2] = params.numerator;
|
const auto [n0, n1, n2] = params.numerator;
|
||||||
|
@ -551,8 +563,8 @@ void CommandGenerator::GenerateI3dl2ReverbEffectCommand(s32 mix_buffer_offset, E
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::array<const s32*, AudioCommon::MAX_CHANNEL_COUNT> input{};
|
std::array<std::span<const s32>, AudioCommon::MAX_CHANNEL_COUNT> input{};
|
||||||
std::array<s32*, AudioCommon::MAX_CHANNEL_COUNT> output{};
|
std::array<std::span<s32>, AudioCommon::MAX_CHANNEL_COUNT> output{};
|
||||||
|
|
||||||
const auto status = params.status;
|
const auto status = params.status;
|
||||||
for (s32 i = 0; i < channel_count; i++) {
|
for (s32 i = 0; i < channel_count; i++) {
|
||||||
|
@ -587,7 +599,8 @@ void CommandGenerator::GenerateI3dl2ReverbEffectCommand(s32 mix_buffer_offset, E
|
||||||
for (s32 i = 0; i < channel_count; i++) {
|
for (s32 i = 0; i < channel_count; i++) {
|
||||||
// Only copy if the buffer input and output do not match!
|
// Only copy if the buffer input and output do not match!
|
||||||
if ((mix_buffer_offset + params.input[i]) != (mix_buffer_offset + params.output[i])) {
|
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));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -603,8 +616,8 @@ void CommandGenerator::GenerateBiquadFilterEffectCommand(s32 mix_buffer_offset,
|
||||||
for (s32 i = 0; i < channel_count; i++) {
|
for (s32 i = 0; i < channel_count; i++) {
|
||||||
// TODO(ogniK): Actually implement biquad filter
|
// TODO(ogniK): Actually implement biquad filter
|
||||||
if (params.input[i] != params.output[i]) {
|
if (params.input[i] != params.output[i]) {
|
||||||
const auto* input = GetMixBuffer(mix_buffer_offset + params.input[i]);
|
std::span<const s32> input = GetMixBuffer(mix_buffer_offset + params.input[i]);
|
||||||
auto* output = GetMixBuffer(mix_buffer_offset + params.output[i]);
|
std::span<s32> output = GetMixBuffer(mix_buffer_offset + params.output[i]);
|
||||||
ApplyMix<1>(output, input, 32768, worker_params.sample_count);
|
ApplyMix<1>(output, input, 32768, worker_params.sample_count);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -643,14 +656,15 @@ void CommandGenerator::GenerateAuxCommand(s32 mix_buffer_offset, EffectBase* inf
|
||||||
|
|
||||||
if (samples_read != static_cast<int>(worker_params.sample_count) &&
|
if (samples_read != static_cast<int>(worker_params.sample_count) &&
|
||||||
samples_read <= 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 {
|
} else {
|
||||||
AuxInfoDSP empty{};
|
AuxInfoDSP empty{};
|
||||||
memory.WriteBlock(aux->GetSendInfo(), &empty, sizeof(AuxInfoDSP));
|
memory.WriteBlock(aux->GetSendInfo(), &empty, sizeof(AuxInfoDSP));
|
||||||
memory.WriteBlock(aux->GetRecvInfo(), &empty, sizeof(AuxInfoDSP));
|
memory.WriteBlock(aux->GetRecvInfo(), &empty, sizeof(AuxInfoDSP));
|
||||||
if (output_index != input_index) {
|
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));
|
worker_params.sample_count * sizeof(s32));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -668,7 +682,7 @@ ServerSplitterDestinationData* CommandGenerator::GetDestinationData(s32 splitter
|
||||||
}
|
}
|
||||||
|
|
||||||
s32 CommandGenerator::WriteAuxBuffer(AuxInfoDSP& dsp_info, VAddr send_buffer, u32 max_samples,
|
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) {
|
u32 write_count) {
|
||||||
if (max_samples == 0) {
|
if (max_samples == 0) {
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -678,14 +692,14 @@ s32 CommandGenerator::WriteAuxBuffer(AuxInfoDSP& dsp_info, VAddr send_buffer, u3
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::size_t data_offset{};
|
s32 data_offset{};
|
||||||
u32 remaining = sample_count;
|
u32 remaining = sample_count;
|
||||||
while (remaining > 0) {
|
while (remaining > 0) {
|
||||||
// Get position in buffer
|
// Get position in buffer
|
||||||
const auto base = send_buffer + (offset * sizeof(u32));
|
const auto base = send_buffer + (offset * sizeof(u32));
|
||||||
const auto samples_to_grab = std::min(max_samples - offset, remaining);
|
const auto samples_to_grab = std::min(max_samples - offset, remaining);
|
||||||
// Write to output
|
// 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;
|
offset = (offset + samples_to_grab) % max_samples;
|
||||||
remaining -= samples_to_grab;
|
remaining -= samples_to_grab;
|
||||||
data_offset += samples_to_grab;
|
data_offset += samples_to_grab;
|
||||||
|
@ -698,7 +712,7 @@ s32 CommandGenerator::WriteAuxBuffer(AuxInfoDSP& dsp_info, VAddr send_buffer, u3
|
||||||
}
|
}
|
||||||
|
|
||||||
s32 CommandGenerator::ReadAuxBuffer(AuxInfoDSP& recv_info, VAddr recv_buffer, u32 max_samples,
|
s32 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) {
|
u32 read_count) {
|
||||||
if (max_samples == 0) {
|
if (max_samples == 0) {
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -710,15 +724,16 @@ s32 CommandGenerator::ReadAuxBuffer(AuxInfoDSP& recv_info, VAddr recv_buffer, u3
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 remaining = sample_count;
|
u32 remaining = sample_count;
|
||||||
|
s32 data_offset{};
|
||||||
while (remaining > 0) {
|
while (remaining > 0) {
|
||||||
const auto base = recv_buffer + (offset * sizeof(u32));
|
const auto base = recv_buffer + (offset * sizeof(u32));
|
||||||
const auto samples_to_grab = std::min(max_samples - offset, remaining);
|
const auto samples_to_grab = std::min(max_samples - offset, remaining);
|
||||||
std::vector<s32> buffer(samples_to_grab);
|
std::vector<s32> buffer(samples_to_grab);
|
||||||
memory.ReadBlock(base, buffer.data(), buffer.size() * sizeof(u32));
|
memory.ReadBlock(base, buffer.data(), buffer.size() * sizeof(u32));
|
||||||
std::memcpy(out_data, buffer.data(), buffer.size() * sizeof(u32));
|
std::memcpy(out_data.data() + data_offset, buffer.data(), buffer.size() * sizeof(u32));
|
||||||
out_data += samples_to_grab;
|
|
||||||
offset = (offset + samples_to_grab) % max_samples;
|
offset = (offset + samples_to_grab) % max_samples;
|
||||||
remaining -= samples_to_grab;
|
remaining -= samples_to_grab;
|
||||||
|
data_offset += samples_to_grab;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (read_count != 0) {
|
if (read_count != 0) {
|
||||||
|
@ -965,8 +980,8 @@ void CommandGenerator::GenerateMixCommand(std::size_t output_offset, std::size_t
|
||||||
node_id, input_offset, output_offset, volume);
|
node_id, input_offset, output_offset, volume);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto* output = GetMixBuffer(output_offset);
|
std::span<s32> output = GetMixBuffer(output_offset);
|
||||||
const auto* input = GetMixBuffer(input_offset);
|
std::span<const s32> input = GetMixBuffer(input_offset);
|
||||||
|
|
||||||
const s32 gain = static_cast<s32>(volume * 32768.0f);
|
const s32 gain = static_cast<s32>(volume * 32768.0f);
|
||||||
// Mix with loop unrolling
|
// Mix with loop unrolling
|
||||||
|
@ -1172,12 +1187,14 @@ s32 CommandGenerator::DecodeAdpcm(ServerVoiceInfo& voice_info, VoiceState& dsp_s
|
||||||
return samples_processed;
|
return samples_processed;
|
||||||
}
|
}
|
||||||
|
|
||||||
s32* CommandGenerator::GetMixBuffer(std::size_t index) {
|
std::span<s32> CommandGenerator::GetMixBuffer(std::size_t index) {
|
||||||
return mix_buffer.data() + (index * worker_params.sample_count);
|
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 {
|
std::span<const s32> CommandGenerator::GetMixBuffer(std::size_t index) const {
|
||||||
return mix_buffer.data() + (index * worker_params.sample_count);
|
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 {
|
std::size_t CommandGenerator::GetMixChannelBufferOffset(s32 channel) const {
|
||||||
|
@ -1188,15 +1205,15 @@ std::size_t CommandGenerator::GetTotalMixBufferCount() const {
|
||||||
return worker_params.mix_buffer_count + AudioCommon::MAX_CHANNEL_COUNT;
|
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);
|
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);
|
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,
|
VoiceState& dsp_state, s32 channel,
|
||||||
s32 target_sample_rate, s32 sample_count,
|
s32 target_sample_rate, s32 sample_count,
|
||||||
s32 node_id) {
|
s32 node_id) {
|
||||||
|
@ -1208,7 +1225,7 @@ void CommandGenerator::DecodeFromWaveBuffers(ServerVoiceInfo& voice_info, s32* o
|
||||||
node_id, channel, in_params.sample_format, sample_count, in_params.sample_rate,
|
node_id, channel, in_params.sample_format, sample_count, in_params.sample_rate,
|
||||||
in_params.mix_id, in_params.splitter_info_id);
|
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>(
|
const auto resample_rate = static_cast<s32>(
|
||||||
static_cast<float>(in_params.sample_rate) / static_cast<float>(target_sample_rate) *
|
static_cast<float>(in_params.sample_rate) / static_cast<float>(target_sample_rate) *
|
||||||
|
@ -1225,6 +1242,7 @@ void CommandGenerator::DecodeFromWaveBuffers(ServerVoiceInfo& voice_info, s32* o
|
||||||
}
|
}
|
||||||
|
|
||||||
std::size_t temp_mix_offset{};
|
std::size_t temp_mix_offset{};
|
||||||
|
s32 samples_output{};
|
||||||
auto samples_remaining = sample_count;
|
auto samples_remaining = sample_count;
|
||||||
while (samples_remaining > 0) {
|
while (samples_remaining > 0) {
|
||||||
const auto samples_to_output = std::min(samples_remaining, min_required_samples);
|
const auto samples_to_output = std::min(samples_remaining, min_required_samples);
|
||||||
|
@ -1328,20 +1346,21 @@ void CommandGenerator::DecodeFromWaveBuffers(ServerVoiceInfo& voice_info, s32* o
|
||||||
|
|
||||||
if (in_params.behavior_flags.is_pitch_and_src_skipped.Value()) {
|
if (in_params.behavior_flags.is_pitch_and_src_skipped.Value()) {
|
||||||
// No need to resample
|
// 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 {
|
} else {
|
||||||
std::fill(sample_buffer.begin() + temp_mix_offset,
|
std::fill(sample_buffer.begin() + temp_mix_offset,
|
||||||
sample_buffer.begin() + temp_mix_offset + (samples_to_read - samples_read),
|
sample_buffer.begin() + temp_mix_offset + (samples_to_read - samples_read),
|
||||||
0);
|
0);
|
||||||
AudioCore::Resample(output, sample_buffer.data(), resample_rate, dsp_state.fraction,
|
AudioCore::Resample(output.data() + samples_output, sample_buffer.data(), resample_rate,
|
||||||
samples_to_output);
|
dsp_state.fraction, samples_to_output);
|
||||||
// Resample
|
// Resample
|
||||||
for (std::size_t i = 0; i < AudioCommon::MAX_SAMPLE_HISTORY; i++) {
|
for (std::size_t i = 0; i < AudioCommon::MAX_SAMPLE_HISTORY; i++) {
|
||||||
dsp_state.sample_history[i] = sample_buffer[samples_to_read + i];
|
dsp_state.sample_history[i] = sample_buffer[samples_to_read + i];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
output += samples_to_output;
|
|
||||||
samples_remaining -= samples_to_output;
|
samples_remaining -= samples_to_output;
|
||||||
|
samples_output += samples_to_output;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <array>
|
#include <array>
|
||||||
|
#include <span>
|
||||||
#include "audio_core/common.h"
|
#include "audio_core/common.h"
|
||||||
#include "audio_core/voice_context.h"
|
#include "audio_core/voice_context.h"
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
|
@ -41,10 +42,10 @@ public:
|
||||||
void PreCommand();
|
void PreCommand();
|
||||||
void PostCommand();
|
void PostCommand();
|
||||||
|
|
||||||
[[nodiscard]] s32* GetChannelMixBuffer(s32 channel);
|
[[nodiscard]] std::span<s32> GetChannelMixBuffer(s32 channel);
|
||||||
[[nodiscard]] const s32* GetChannelMixBuffer(s32 channel) const;
|
[[nodiscard]] std::span<const s32> GetChannelMixBuffer(s32 channel) const;
|
||||||
[[nodiscard]] s32* GetMixBuffer(std::size_t index);
|
[[nodiscard]] std::span<s32> GetMixBuffer(std::size_t index);
|
||||||
[[nodiscard]] const s32* GetMixBuffer(std::size_t index) const;
|
[[nodiscard]] std::span<const s32> GetMixBuffer(std::size_t index) const;
|
||||||
[[nodiscard]] std::size_t GetMixChannelBufferOffset(s32 channel) const;
|
[[nodiscard]] std::size_t GetMixChannelBufferOffset(s32 channel) const;
|
||||||
|
|
||||||
[[nodiscard]] std::size_t GetTotalMixBufferCount() const;
|
[[nodiscard]] std::size_t GetTotalMixBufferCount() const;
|
||||||
|
@ -77,10 +78,11 @@ private:
|
||||||
void GenerateAuxCommand(s32 mix_buffer_offset, EffectBase* info, bool enabled);
|
void GenerateAuxCommand(s32 mix_buffer_offset, EffectBase* info, bool enabled);
|
||||||
[[nodiscard]] ServerSplitterDestinationData* GetDestinationData(s32 splitter_id, s32 index);
|
[[nodiscard]] ServerSplitterDestinationData* GetDestinationData(s32 splitter_id, s32 index);
|
||||||
|
|
||||||
s32 WriteAuxBuffer(AuxInfoDSP& dsp_info, VAddr send_buffer, u32 max_samples, const s32* data,
|
s32 WriteAuxBuffer(AuxInfoDSP& dsp_info, VAddr send_buffer, u32 max_samples,
|
||||||
u32 sample_count, u32 write_offset, u32 write_count);
|
std::span<const s32> data, u32 sample_count, u32 write_offset,
|
||||||
s32 ReadAuxBuffer(AuxInfoDSP& recv_info, VAddr recv_buffer, u32 max_samples, s32* out_data,
|
u32 write_count);
|
||||||
u32 sample_count, u32 read_offset, u32 read_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,
|
void InitializeI3dl2Reverb(I3dl2ReverbParams& info, I3dl2ReverbState& state,
|
||||||
std::vector<u8>& work_buffer);
|
std::vector<u8>& work_buffer);
|
||||||
|
@ -91,8 +93,9 @@ private:
|
||||||
s32 sample_end_offset, s32 sample_count, s32 channel, std::size_t mix_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 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);
|
s32 sample_end_offset, s32 sample_count, s32 channel, std::size_t mix_offset);
|
||||||
void DecodeFromWaveBuffers(ServerVoiceInfo& voice_info, s32* output, VoiceState& dsp_state,
|
void DecodeFromWaveBuffers(ServerVoiceInfo& voice_info, std::span<s32> output,
|
||||||
s32 channel, s32 target_sample_rate, s32 sample_count, s32 node_id);
|
VoiceState& dsp_state, s32 channel, s32 target_sample_rate,
|
||||||
|
s32 sample_count, s32 node_id);
|
||||||
|
|
||||||
AudioCommon::AudioRendererParameter& worker_params;
|
AudioCommon::AudioRendererParameter& worker_params;
|
||||||
VoiceContext& voice_context;
|
VoiceContext& voice_context;
|
||||||
|
|
|
@ -15,10 +15,17 @@ std::size_t SinkContext::GetCount() const {
|
||||||
void SinkContext::UpdateMainSink(const SinkInfo::InParams& in) {
|
void SinkContext::UpdateMainSink(const SinkInfo::InParams& in) {
|
||||||
ASSERT(in.type == SinkTypes::Device);
|
ASSERT(in.type == SinkTypes::Device);
|
||||||
|
|
||||||
has_downmix_coefs = in.device.down_matrix_enabled;
|
if (in.device.down_matrix_enabled) {
|
||||||
if (has_downmix_coefs) {
|
|
||||||
downmix_coefficients = in.device.down_matrix_coef;
|
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;
|
in_use = in.in_use;
|
||||||
use_count = in.device.input_count;
|
use_count = in.device.input_count;
|
||||||
buffers = in.device.input;
|
buffers = in.device.input;
|
||||||
|
@ -34,10 +41,6 @@ std::vector<u8> SinkContext::OutputBuffers() const {
|
||||||
return buffer_ret;
|
return buffer_ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SinkContext::HasDownMixingCoefficients() const {
|
|
||||||
return has_downmix_coefs;
|
|
||||||
}
|
|
||||||
|
|
||||||
const DownmixCoefficients& SinkContext::GetDownmixCoefficients() const {
|
const DownmixCoefficients& SinkContext::GetDownmixCoefficients() const {
|
||||||
return downmix_coefficients;
|
return downmix_coefficients;
|
||||||
}
|
}
|
||||||
|
|
|
@ -84,7 +84,6 @@ public:
|
||||||
[[nodiscard]] bool InUse() const;
|
[[nodiscard]] bool InUse() const;
|
||||||
[[nodiscard]] std::vector<u8> OutputBuffers() const;
|
[[nodiscard]] std::vector<u8> OutputBuffers() const;
|
||||||
|
|
||||||
[[nodiscard]] bool HasDownMixingCoefficients() const;
|
|
||||||
[[nodiscard]] const DownmixCoefficients& GetDownmixCoefficients() const;
|
[[nodiscard]] const DownmixCoefficients& GetDownmixCoefficients() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -92,7 +91,6 @@ private:
|
||||||
s32 use_count{};
|
s32 use_count{};
|
||||||
std::array<u8, AudioCommon::MAX_CHANNEL_COUNT> buffers{};
|
std::array<u8, AudioCommon::MAX_CHANNEL_COUNT> buffers{};
|
||||||
std::size_t sink_count{};
|
std::size_t sink_count{};
|
||||||
bool has_downmix_coefs{false};
|
|
||||||
DownmixCoefficients downmix_coefficients{};
|
DownmixCoefficients downmix_coefficients{};
|
||||||
};
|
};
|
||||||
} // namespace AudioCore
|
} // namespace AudioCore
|
||||||
|
|
|
@ -591,7 +591,6 @@ void BufferCache<P>::CommitAsyncFlushesHigh() {
|
||||||
for (auto& interval : intervals) {
|
for (auto& interval : intervals) {
|
||||||
const std::size_t size = interval.upper() - interval.lower();
|
const std::size_t size = interval.upper() - interval.lower();
|
||||||
const VAddr cpu_addr = interval.lower();
|
const VAddr cpu_addr = interval.lower();
|
||||||
const VAddr cpu_addr_end = interval.upper();
|
|
||||||
ForEachBufferInRange(cpu_addr, size, [&](BufferId buffer_id, Buffer& buffer) {
|
ForEachBufferInRange(cpu_addr, size, [&](BufferId buffer_id, Buffer& buffer) {
|
||||||
boost::container::small_vector<BufferCopy, 1> copies;
|
boost::container::small_vector<BufferCopy, 1> copies;
|
||||||
buffer.ForEachDownloadRange(
|
buffer.ForEachDownloadRange(
|
||||||
|
|
|
@ -1785,8 +1785,6 @@ public:
|
||||||
SSY,
|
SSY,
|
||||||
SYNC,
|
SYNC,
|
||||||
BRK,
|
BRK,
|
||||||
CAL,
|
|
||||||
RET,
|
|
||||||
DEPBAR,
|
DEPBAR,
|
||||||
VOTE,
|
VOTE,
|
||||||
VOTE_VTG,
|
VOTE_VTG,
|
||||||
|
@ -2110,8 +2108,6 @@ private:
|
||||||
INST("1111000011111---", Id::SYNC, Type::Flow, "SYNC"),
|
INST("1111000011111---", Id::SYNC, Type::Flow, "SYNC"),
|
||||||
INST("111000110100----", Id::BRK, Type::Flow, "BRK"),
|
INST("111000110100----", Id::BRK, Type::Flow, "BRK"),
|
||||||
INST("111000110000----", Id::EXIT, Type::Flow, "EXIT"),
|
INST("111000110000----", Id::EXIT, Type::Flow, "EXIT"),
|
||||||
INST("111000100110----", Id::CAL, Type::Flow, "CAL"),
|
|
||||||
INST("111000110010----", Id::RET, Type::Flow, "RET"),
|
|
||||||
INST("1111000011110---", Id::DEPBAR, Type::Synch, "DEPBAR"),
|
INST("1111000011110---", Id::DEPBAR, Type::Synch, "DEPBAR"),
|
||||||
INST("0101000011011---", Id::VOTE, Type::Warp, "VOTE"),
|
INST("0101000011011---", Id::VOTE, Type::Warp, "VOTE"),
|
||||||
INST("0101000011100---", Id::VOTE_VTG, Type::Warp, "VOTE_VTG"),
|
INST("0101000011100---", Id::VOTE_VTG, Type::Warp, "VOTE_VTG"),
|
||||||
|
|
|
@ -491,9 +491,6 @@ private:
|
||||||
const Registry& registry;
|
const Registry& registry;
|
||||||
const ShaderType stage;
|
const ShaderType stage;
|
||||||
|
|
||||||
std::shared_ptr<ShaderFunctionIR> context_func;
|
|
||||||
u32 ast_var_base{};
|
|
||||||
|
|
||||||
std::size_t num_temporaries = 0;
|
std::size_t num_temporaries = 0;
|
||||||
std::size_t max_temporaries = 0;
|
std::size_t max_temporaries = 0;
|
||||||
|
|
||||||
|
@ -810,33 +807,13 @@ ARBDecompiler::ARBDecompiler(const Device& device_, const ShaderIR& ir_, const R
|
||||||
: device{device_}, ir{ir_}, registry{registry_}, stage{stage_} {
|
: device{device_}, ir{ir_}, registry{registry_}, stage{stage_} {
|
||||||
DefineGlobalMemory();
|
DefineGlobalMemory();
|
||||||
|
|
||||||
context_func = ir.GetMainFunction();
|
|
||||||
ast_var_base = 0;
|
|
||||||
|
|
||||||
AddLine("TEMP RC;");
|
AddLine("TEMP RC;");
|
||||||
AddLine("TEMP FSWZA[4];");
|
AddLine("TEMP FSWZA[4];");
|
||||||
AddLine("TEMP FSWZB[4];");
|
AddLine("TEMP FSWZB[4];");
|
||||||
InitializeVariables();
|
if (ir.IsDecompiled()) {
|
||||||
AddLine("main:");
|
|
||||||
if (context_func->IsDecompiled()) {
|
|
||||||
DecompileAST();
|
DecompileAST();
|
||||||
} else {
|
} else {
|
||||||
DecompileBranchMode();
|
DecompileBranchMode();
|
||||||
AddLine("RET;");
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto& subfunctions = ir.GetSubFunctions();
|
|
||||||
auto it = subfunctions.begin();
|
|
||||||
while (it != subfunctions.end()) {
|
|
||||||
context_func = *it;
|
|
||||||
AddLine("func_{}:", context_func->GetId());
|
|
||||||
if (context_func->IsDecompiled()) {
|
|
||||||
DecompileAST();
|
|
||||||
} else {
|
|
||||||
DecompileBranchMode();
|
|
||||||
AddLine("RET;");
|
|
||||||
}
|
|
||||||
it++;
|
|
||||||
}
|
}
|
||||||
AddLine("END");
|
AddLine("END");
|
||||||
|
|
||||||
|
@ -1083,38 +1060,41 @@ void ARBDecompiler::InitializeVariables() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void ARBDecompiler::DecompileAST() {
|
void ARBDecompiler::DecompileAST() {
|
||||||
const u32 num_flow_variables = context_func->GetASTNumVariables();
|
const u32 num_flow_variables = ir.GetASTNumVariables();
|
||||||
for (u32 i = 0; i < num_flow_variables; ++i) {
|
for (u32 i = 0; i < num_flow_variables; ++i) {
|
||||||
AddLine("TEMP F{};", i + ast_var_base);
|
AddLine("TEMP F{};", i);
|
||||||
}
|
}
|
||||||
for (u32 i = 0; i < num_flow_variables; ++i) {
|
for (u32 i = 0; i < num_flow_variables; ++i) {
|
||||||
AddLine("MOV.U F{}, {{0, 0, 0, 0}};", i + ast_var_base);
|
AddLine("MOV.U F{}, {{0, 0, 0, 0}};", i);
|
||||||
}
|
}
|
||||||
|
|
||||||
VisitAST(context_func->GetASTProgram());
|
InitializeVariables();
|
||||||
ast_var_base += num_flow_variables;
|
|
||||||
|
VisitAST(ir.GetASTProgram());
|
||||||
}
|
}
|
||||||
|
|
||||||
void ARBDecompiler::DecompileBranchMode() {
|
void ARBDecompiler::DecompileBranchMode() {
|
||||||
static constexpr u32 FLOW_STACK_SIZE = 20;
|
static constexpr u32 FLOW_STACK_SIZE = 20;
|
||||||
if (!context_func->IsFlowStackDisabled()) {
|
if (!ir.IsFlowStackDisabled()) {
|
||||||
AddLine("TEMP SSY[{}];", FLOW_STACK_SIZE);
|
AddLine("TEMP SSY[{}];", FLOW_STACK_SIZE);
|
||||||
AddLine("TEMP PBK[{}];", FLOW_STACK_SIZE);
|
AddLine("TEMP PBK[{}];", FLOW_STACK_SIZE);
|
||||||
AddLine("TEMP SSY_TOP;");
|
AddLine("TEMP SSY_TOP;");
|
||||||
AddLine("TEMP PBK_TOP;");
|
AddLine("TEMP PBK_TOP;");
|
||||||
}
|
}
|
||||||
|
|
||||||
AddLine("TEMP PC{};", context_func->GetId());
|
AddLine("TEMP PC;");
|
||||||
|
|
||||||
if (!context_func->IsFlowStackDisabled()) {
|
if (!ir.IsFlowStackDisabled()) {
|
||||||
AddLine("MOV.U SSY_TOP.x, 0;");
|
AddLine("MOV.U SSY_TOP.x, 0;");
|
||||||
AddLine("MOV.U PBK_TOP.x, 0;");
|
AddLine("MOV.U PBK_TOP.x, 0;");
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto basic_block_end = context_func->GetBasicBlocks().end();
|
InitializeVariables();
|
||||||
auto basic_block_it = context_func->GetBasicBlocks().begin();
|
|
||||||
|
const auto basic_block_end = ir.GetBasicBlocks().end();
|
||||||
|
auto basic_block_it = ir.GetBasicBlocks().begin();
|
||||||
const u32 first_address = basic_block_it->first;
|
const u32 first_address = basic_block_it->first;
|
||||||
AddLine("MOV.U PC{}.x, {};", context_func->GetId(), first_address);
|
AddLine("MOV.U PC.x, {};", first_address);
|
||||||
|
|
||||||
AddLine("REP;");
|
AddLine("REP;");
|
||||||
|
|
||||||
|
@ -1123,7 +1103,7 @@ void ARBDecompiler::DecompileBranchMode() {
|
||||||
const auto& [address, bb] = *basic_block_it;
|
const auto& [address, bb] = *basic_block_it;
|
||||||
++num_blocks;
|
++num_blocks;
|
||||||
|
|
||||||
AddLine("SEQ.S.CC RC.x, PC{}.x, {};", context_func->GetId(), address);
|
AddLine("SEQ.S.CC RC.x, PC.x, {};", address);
|
||||||
AddLine("IF NE.x;");
|
AddLine("IF NE.x;");
|
||||||
|
|
||||||
VisitBlock(bb);
|
VisitBlock(bb);
|
||||||
|
@ -1134,7 +1114,7 @@ void ARBDecompiler::DecompileBranchMode() {
|
||||||
const auto op = std::get_if<OperationNode>(&*bb[bb.size() - 1]);
|
const auto op = std::get_if<OperationNode>(&*bb[bb.size() - 1]);
|
||||||
if (!op || op->GetCode() != OperationCode::Branch) {
|
if (!op || op->GetCode() != OperationCode::Branch) {
|
||||||
const u32 next_address = basic_block_it->first;
|
const u32 next_address = basic_block_it->first;
|
||||||
AddLine("MOV.U PC{}.x, {};", context_func->GetId(), next_address);
|
AddLine("MOV.U PC.x, {};", next_address);
|
||||||
AddLine("CONT;");
|
AddLine("CONT;");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1172,8 +1152,7 @@ void ARBDecompiler::VisitAST(const ASTNode& node) {
|
||||||
} else if (const auto decoded = std::get_if<ASTBlockDecoded>(&*node->GetInnerData())) {
|
} else if (const auto decoded = std::get_if<ASTBlockDecoded>(&*node->GetInnerData())) {
|
||||||
VisitBlock(decoded->nodes);
|
VisitBlock(decoded->nodes);
|
||||||
} else if (const auto var_set = std::get_if<ASTVarSet>(&*node->GetInnerData())) {
|
} else if (const auto var_set = std::get_if<ASTVarSet>(&*node->GetInnerData())) {
|
||||||
AddLine("MOV.U F{}, {};", var_set->index + ast_var_base,
|
AddLine("MOV.U F{}, {};", var_set->index, VisitExpression(var_set->condition));
|
||||||
VisitExpression(var_set->condition));
|
|
||||||
ResetTemporaries();
|
ResetTemporaries();
|
||||||
} else if (const auto do_while = std::get_if<ASTDoWhile>(&*node->GetInnerData())) {
|
} else if (const auto do_while = std::get_if<ASTDoWhile>(&*node->GetInnerData())) {
|
||||||
const std::string condition = VisitExpression(do_while->condition);
|
const std::string condition = VisitExpression(do_while->condition);
|
||||||
|
@ -1193,11 +1172,7 @@ void ARBDecompiler::VisitAST(const ASTNode& node) {
|
||||||
ResetTemporaries();
|
ResetTemporaries();
|
||||||
}
|
}
|
||||||
if (ast_return->kills) {
|
if (ast_return->kills) {
|
||||||
if (stage == ShaderType::Fragment) {
|
AddLine("KIL TR;");
|
||||||
AddLine("KIL TR;");
|
|
||||||
} else {
|
|
||||||
AddLine("RET;");
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
Exit();
|
Exit();
|
||||||
}
|
}
|
||||||
|
@ -1244,7 +1219,7 @@ std::string ARBDecompiler::VisitExpression(const Expr& node) {
|
||||||
return Visit(ir.GetConditionCode(expr->cc));
|
return Visit(ir.GetConditionCode(expr->cc));
|
||||||
}
|
}
|
||||||
if (const auto expr = std::get_if<ExprVar>(&*node)) {
|
if (const auto expr = std::get_if<ExprVar>(&*node)) {
|
||||||
return fmt::format("F{}.x", expr->var_index + ast_var_base);
|
return fmt::format("F{}.x", expr->var_index);
|
||||||
}
|
}
|
||||||
if (const auto expr = std::get_if<ExprBoolean>(&*node)) {
|
if (const auto expr = std::get_if<ExprBoolean>(&*node)) {
|
||||||
return expr->value ? "0xffffffff" : "0";
|
return expr->value ? "0xffffffff" : "0";
|
||||||
|
@ -1431,11 +1406,6 @@ std::string ARBDecompiler::Visit(const Node& node) {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (const auto func_call = std::get_if<FunctionCallNode>(&*node)) {
|
|
||||||
AddLine("CAL func_{};", func_call->GetFuncId());
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
if ([[maybe_unused]] const auto cmt = std::get_if<CommentNode>(&*node)) {
|
if ([[maybe_unused]] const auto cmt = std::get_if<CommentNode>(&*node)) {
|
||||||
// Uncommenting this will generate invalid code. GLASM lacks comments.
|
// Uncommenting this will generate invalid code. GLASM lacks comments.
|
||||||
// AddLine("// {}", cmt->GetText());
|
// AddLine("// {}", cmt->GetText());
|
||||||
|
@ -1509,7 +1479,7 @@ std::string ARBDecompiler::GlobalMemoryPointer(const GmemNode& gmem) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void ARBDecompiler::Exit() {
|
void ARBDecompiler::Exit() {
|
||||||
if (!context_func->IsMain() || stage != ShaderType::Fragment) {
|
if (stage != ShaderType::Fragment) {
|
||||||
AddLine("RET;");
|
AddLine("RET;");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -2051,13 +2021,13 @@ std::string ARBDecompiler::ImageStore(Operation operation) {
|
||||||
|
|
||||||
std::string ARBDecompiler::Branch(Operation operation) {
|
std::string ARBDecompiler::Branch(Operation operation) {
|
||||||
const auto target = std::get<ImmediateNode>(*operation[0]);
|
const auto target = std::get<ImmediateNode>(*operation[0]);
|
||||||
AddLine("MOV.U PC{}.x, {};", context_func->GetId(), target.GetValue());
|
AddLine("MOV.U PC.x, {};", target.GetValue());
|
||||||
AddLine("CONT;");
|
AddLine("CONT;");
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string ARBDecompiler::BranchIndirect(Operation operation) {
|
std::string ARBDecompiler::BranchIndirect(Operation operation) {
|
||||||
AddLine("MOV.U PC{}.x, {};", context_func->GetId(), Visit(operation[0]));
|
AddLine("MOV.U PC.x, {};", Visit(operation[0]));
|
||||||
AddLine("CONT;");
|
AddLine("CONT;");
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
@ -2075,7 +2045,7 @@ std::string ARBDecompiler::PopFlowStack(Operation operation) {
|
||||||
const auto stack = std::get<MetaStackClass>(operation.GetMeta());
|
const auto stack = std::get<MetaStackClass>(operation.GetMeta());
|
||||||
const std::string_view stack_name = StackName(stack);
|
const std::string_view stack_name = StackName(stack);
|
||||||
AddLine("SUB.S {}_TOP.x, {}_TOP.x, 1;", stack_name, stack_name);
|
AddLine("SUB.S {}_TOP.x, {}_TOP.x, 1;", stack_name, stack_name);
|
||||||
AddLine("MOV.U PC{}.x, {}[{}_TOP.x].x;", context_func->GetId(), stack_name, stack_name);
|
AddLine("MOV.U PC.x, {}[{}_TOP.x].x;", stack_name, stack_name);
|
||||||
AddLine("CONT;");
|
AddLine("CONT;");
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
@ -2086,10 +2056,6 @@ std::string ARBDecompiler::Exit(Operation) {
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string ARBDecompiler::Discard(Operation) {
|
std::string ARBDecompiler::Discard(Operation) {
|
||||||
if (stage != ShaderType::Fragment) {
|
|
||||||
AddLine("RET;");
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
AddLine("KIL TR;");
|
AddLine("KIL TR;");
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
|
@ -79,11 +79,6 @@ const float fswzadd_modifiers_a[] = float[4](-1.0f, 1.0f, -1.0f, 0.0f );
|
||||||
const float fswzadd_modifiers_b[] = float[4](-1.0f, -1.0f, 1.0f, -1.0f );
|
const float fswzadd_modifiers_b[] = float[4](-1.0f, -1.0f, 1.0f, -1.0f );
|
||||||
)";
|
)";
|
||||||
|
|
||||||
enum class HelperFunction {
|
|
||||||
SignedAtomic = 0,
|
|
||||||
Total,
|
|
||||||
};
|
|
||||||
|
|
||||||
class ShaderWriter final {
|
class ShaderWriter final {
|
||||||
public:
|
public:
|
||||||
void AddExpression(std::string_view text) {
|
void AddExpression(std::string_view text) {
|
||||||
|
@ -439,28 +434,6 @@ public:
|
||||||
DeclareInternalFlags();
|
DeclareInternalFlags();
|
||||||
DeclareCustomVariables();
|
DeclareCustomVariables();
|
||||||
DeclarePhysicalAttributeReader();
|
DeclarePhysicalAttributeReader();
|
||||||
DeclareHelpersForward();
|
|
||||||
|
|
||||||
const auto& subfunctions = ir.GetSubFunctions();
|
|
||||||
auto it = subfunctions.rbegin();
|
|
||||||
while (it != subfunctions.rend()) {
|
|
||||||
context_func = *it;
|
|
||||||
code.AddLine("void func_{}() {{", context_func->GetId());
|
|
||||||
++code.scope;
|
|
||||||
|
|
||||||
if (context_func->IsDecompiled()) {
|
|
||||||
DecompileAST();
|
|
||||||
} else {
|
|
||||||
DecompileBranchMode();
|
|
||||||
}
|
|
||||||
|
|
||||||
--code.scope;
|
|
||||||
code.AddLine("}}");
|
|
||||||
|
|
||||||
it++;
|
|
||||||
}
|
|
||||||
|
|
||||||
context_func = ir.GetMainFunction();
|
|
||||||
|
|
||||||
code.AddLine("void main() {{");
|
code.AddLine("void main() {{");
|
||||||
++code.scope;
|
++code.scope;
|
||||||
|
@ -469,7 +442,7 @@ public:
|
||||||
code.AddLine("gl_Position = vec4(0.0f, 0.0f, 0.0f, 1.0f);");
|
code.AddLine("gl_Position = vec4(0.0f, 0.0f, 0.0f, 1.0f);");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (context_func->IsDecompiled()) {
|
if (ir.IsDecompiled()) {
|
||||||
DecompileAST();
|
DecompileAST();
|
||||||
} else {
|
} else {
|
||||||
DecompileBranchMode();
|
DecompileBranchMode();
|
||||||
|
@ -477,9 +450,6 @@ public:
|
||||||
|
|
||||||
--code.scope;
|
--code.scope;
|
||||||
code.AddLine("}}");
|
code.AddLine("}}");
|
||||||
|
|
||||||
code.AddNewLine();
|
|
||||||
DeclareHelpers();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string GetResult() {
|
std::string GetResult() {
|
||||||
|
@ -492,13 +462,13 @@ private:
|
||||||
|
|
||||||
void DecompileBranchMode() {
|
void DecompileBranchMode() {
|
||||||
// VM's program counter
|
// VM's program counter
|
||||||
const auto first_address = context_func->GetBasicBlocks().begin()->first;
|
const auto first_address = ir.GetBasicBlocks().begin()->first;
|
||||||
code.AddLine("uint jmp_to = {}U;", first_address);
|
code.AddLine("uint jmp_to = {}U;", first_address);
|
||||||
|
|
||||||
// TODO(Subv): Figure out the actual depth of the flow stack, for now it seems
|
// TODO(Subv): Figure out the actual depth of the flow stack, for now it seems
|
||||||
// unlikely that shaders will use 20 nested SSYs and PBKs.
|
// unlikely that shaders will use 20 nested SSYs and PBKs.
|
||||||
constexpr u32 FLOW_STACK_SIZE = 20;
|
constexpr u32 FLOW_STACK_SIZE = 20;
|
||||||
if (!context_func->IsFlowStackDisabled()) {
|
if (!ir.IsFlowStackDisabled()) {
|
||||||
for (const auto stack : std::array{MetaStackClass::Ssy, MetaStackClass::Pbk}) {
|
for (const auto stack : std::array{MetaStackClass::Ssy, MetaStackClass::Pbk}) {
|
||||||
code.AddLine("uint {}[{}];", FlowStackName(stack), FLOW_STACK_SIZE);
|
code.AddLine("uint {}[{}];", FlowStackName(stack), FLOW_STACK_SIZE);
|
||||||
code.AddLine("uint {} = 0U;", FlowStackTopName(stack));
|
code.AddLine("uint {} = 0U;", FlowStackTopName(stack));
|
||||||
|
@ -510,7 +480,7 @@ private:
|
||||||
|
|
||||||
code.AddLine("switch (jmp_to) {{");
|
code.AddLine("switch (jmp_to) {{");
|
||||||
|
|
||||||
for (const auto& pair : context_func->GetBasicBlocks()) {
|
for (const auto& pair : ir.GetBasicBlocks()) {
|
||||||
const auto& [address, bb] = pair;
|
const auto& [address, bb] = pair;
|
||||||
code.AddLine("case 0x{:X}U: {{", address);
|
code.AddLine("case 0x{:X}U: {{", address);
|
||||||
++code.scope;
|
++code.scope;
|
||||||
|
@ -629,7 +599,7 @@ private:
|
||||||
size = limit;
|
size = limit;
|
||||||
}
|
}
|
||||||
|
|
||||||
code.AddLine("shared uint {}[{}];", GetSharedMemory(), size / 4);
|
code.AddLine("shared uint smem[{}];", size / 4);
|
||||||
code.AddNewLine();
|
code.AddNewLine();
|
||||||
}
|
}
|
||||||
code.AddLine("layout (local_size_x = {}, local_size_y = {}, local_size_z = {}) in;",
|
code.AddLine("layout (local_size_x = {}, local_size_y = {}, local_size_z = {}) in;",
|
||||||
|
@ -1013,27 +983,6 @@ private:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void DeclareHelpersForward() {
|
|
||||||
code.AddLine("int Helpers_AtomicShared(uint offset, int value, bool is_min);");
|
|
||||||
code.AddNewLine();
|
|
||||||
}
|
|
||||||
|
|
||||||
void DeclareHelpers() {
|
|
||||||
if (IsHelperEnabled(HelperFunction::SignedAtomic)) {
|
|
||||||
code.AddLine(
|
|
||||||
R"(int Helpers_AtomicShared(uint offset, int value, bool is_min) {{
|
|
||||||
uint oldValue, newValue;
|
|
||||||
do {{
|
|
||||||
oldValue = {}[offset];
|
|
||||||
newValue = is_min ? uint(min(int(oldValue), value)) : uint(max(int(oldValue), value));
|
|
||||||
}} while (atomicCompSwap({}[offset], newValue, oldValue) != oldValue);
|
|
||||||
return int(oldValue);
|
|
||||||
}})",
|
|
||||||
GetSharedMemory(), GetSharedMemory());
|
|
||||||
code.AddNewLine();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void VisitBlock(const NodeBlock& bb) {
|
void VisitBlock(const NodeBlock& bb) {
|
||||||
for (const auto& node : bb) {
|
for (const auto& node : bb) {
|
||||||
Visit(node).CheckVoid();
|
Visit(node).CheckVoid();
|
||||||
|
@ -1160,9 +1109,7 @@ private:
|
||||||
}
|
}
|
||||||
|
|
||||||
if (const auto smem = std::get_if<SmemNode>(&*node)) {
|
if (const auto smem = std::get_if<SmemNode>(&*node)) {
|
||||||
return {
|
return {fmt::format("smem[{} >> 2]", Visit(smem->GetAddress()).AsUint()), Type::Uint};
|
||||||
fmt::format("{}[{} >> 2]", GetSharedMemory(), Visit(smem->GetAddress()).AsUint()),
|
|
||||||
Type::Uint};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (const auto internal_flag = std::get_if<InternalFlagNode>(&*node)) {
|
if (const auto internal_flag = std::get_if<InternalFlagNode>(&*node)) {
|
||||||
|
@ -1184,11 +1131,6 @@ private:
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (const auto func_call = std::get_if<FunctionCallNode>(&*node)) {
|
|
||||||
code.AddLine("func_{}();", func_call->GetFuncId());
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (const auto comment = std::get_if<CommentNode>(&*node)) {
|
if (const auto comment = std::get_if<CommentNode>(&*node)) {
|
||||||
code.AddLine("// " + comment->GetText());
|
code.AddLine("// " + comment->GetText());
|
||||||
return {};
|
return {};
|
||||||
|
@ -1656,9 +1598,7 @@ private:
|
||||||
Type::Uint};
|
Type::Uint};
|
||||||
} else if (const auto smem = std::get_if<SmemNode>(&*dest)) {
|
} else if (const auto smem = std::get_if<SmemNode>(&*dest)) {
|
||||||
ASSERT(stage == ShaderType::Compute);
|
ASSERT(stage == ShaderType::Compute);
|
||||||
target = {
|
target = {fmt::format("smem[{} >> 2]", Visit(smem->GetAddress()).AsUint()), Type::Uint};
|
||||||
fmt::format("{}[{} >> 2]", GetSharedMemory(), Visit(smem->GetAddress()).AsUint()),
|
|
||||||
Type::Uint};
|
|
||||||
} else if (const auto gmem = std::get_if<GmemNode>(&*dest)) {
|
} else if (const auto gmem = std::get_if<GmemNode>(&*dest)) {
|
||||||
const std::string real = Visit(gmem->GetRealAddress()).AsUint();
|
const std::string real = Visit(gmem->GetRealAddress()).AsUint();
|
||||||
const std::string base = Visit(gmem->GetBaseAddress()).AsUint();
|
const std::string base = Visit(gmem->GetBaseAddress()).AsUint();
|
||||||
|
@ -2175,14 +2115,7 @@ private:
|
||||||
UNIMPLEMENTED_IF(meta->sampler.is_array);
|
UNIMPLEMENTED_IF(meta->sampler.is_array);
|
||||||
const std::size_t count = operation.GetOperandsCount();
|
const std::size_t count = operation.GetOperandsCount();
|
||||||
|
|
||||||
std::string expr = "texelFetch";
|
std::string expr = "texelFetch(";
|
||||||
|
|
||||||
if (!meta->aoffi.empty()) {
|
|
||||||
expr += "Offset";
|
|
||||||
}
|
|
||||||
|
|
||||||
expr += '(';
|
|
||||||
|
|
||||||
expr += GetSampler(meta->sampler);
|
expr += GetSampler(meta->sampler);
|
||||||
expr += ", ";
|
expr += ", ";
|
||||||
|
|
||||||
|
@ -2204,20 +2137,6 @@ private:
|
||||||
expr += ", ";
|
expr += ", ";
|
||||||
expr += Visit(meta->lod).AsInt();
|
expr += Visit(meta->lod).AsInt();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!meta->aoffi.empty()) {
|
|
||||||
expr += ", ";
|
|
||||||
expr += constructors.at(meta->aoffi.size() - 1);
|
|
||||||
expr += '(';
|
|
||||||
for (size_t i = 0; i < meta->aoffi.size(); ++i) {
|
|
||||||
if (i > 0) {
|
|
||||||
expr += ", ";
|
|
||||||
}
|
|
||||||
expr += Visit(meta->aoffi[i]).AsInt();
|
|
||||||
}
|
|
||||||
expr += ')';
|
|
||||||
}
|
|
||||||
|
|
||||||
expr += ')';
|
expr += ')';
|
||||||
expr += GetSwizzle(meta->element);
|
expr += GetSwizzle(meta->element);
|
||||||
|
|
||||||
|
@ -2264,11 +2183,8 @@ private:
|
||||||
template <const std::string_view& opname, Type type>
|
template <const std::string_view& opname, Type type>
|
||||||
Expression Atomic(Operation operation) {
|
Expression Atomic(Operation operation) {
|
||||||
if ((opname == Func::Min || opname == Func::Max) && type == Type::Int) {
|
if ((opname == Func::Min || opname == Func::Max) && type == Type::Int) {
|
||||||
// Use a helper as a workaround due to memory being uint
|
UNIMPLEMENTED_MSG("Unimplemented Min & Max for atomic operations");
|
||||||
SetHelperEnabled(HelperFunction::SignedAtomic, true);
|
return {};
|
||||||
return {fmt::format("Helpers_AtomicShared({}, {}, {})", Visit(operation[0]).AsInt(),
|
|
||||||
Visit(operation[1]).AsInt(), opname == Func::Min),
|
|
||||||
Type::Int};
|
|
||||||
}
|
}
|
||||||
return {fmt::format("atomic{}({}, {})", opname, Visit(operation[0]).GetCode(),
|
return {fmt::format("atomic{}({}, {})", opname, Visit(operation[0]).GetCode(),
|
||||||
Visit(operation[1]).AsUint()),
|
Visit(operation[1]).AsUint()),
|
||||||
|
@ -2351,9 +2267,7 @@ private:
|
||||||
}
|
}
|
||||||
|
|
||||||
Expression Exit(Operation operation) {
|
Expression Exit(Operation operation) {
|
||||||
if (context_func->IsMain()) {
|
PreExit();
|
||||||
PreExit();
|
|
||||||
}
|
|
||||||
code.AddLine("return;");
|
code.AddLine("return;");
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
@ -2363,11 +2277,7 @@ private:
|
||||||
// about unexecuted instructions that may follow this.
|
// about unexecuted instructions that may follow this.
|
||||||
code.AddLine("if (true) {{");
|
code.AddLine("if (true) {{");
|
||||||
++code.scope;
|
++code.scope;
|
||||||
if (stage != ShaderType::Fragment) {
|
code.AddLine("discard;");
|
||||||
code.AddLine("return;");
|
|
||||||
} else {
|
|
||||||
code.AddLine("discard;");
|
|
||||||
}
|
|
||||||
--code.scope;
|
--code.scope;
|
||||||
code.AddLine("}}");
|
code.AddLine("}}");
|
||||||
return {};
|
return {};
|
||||||
|
@ -2478,7 +2388,7 @@ private:
|
||||||
}
|
}
|
||||||
|
|
||||||
Expression Barrier(Operation) {
|
Expression Barrier(Operation) {
|
||||||
if (!context_func->IsDecompiled()) {
|
if (!ir.IsDecompiled()) {
|
||||||
LOG_ERROR(Render_OpenGL, "barrier() used but shader is not decompiled");
|
LOG_ERROR(Render_OpenGL, "barrier() used but shader is not decompiled");
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
@ -2795,10 +2705,6 @@ private:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr std::string_view GetSharedMemory() const {
|
|
||||||
return "shared_mem";
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string GetInternalFlag(InternalFlag flag) const {
|
std::string GetInternalFlag(InternalFlag flag) const {
|
||||||
constexpr std::array InternalFlagNames = {"zero_flag", "sign_flag", "carry_flag",
|
constexpr std::array InternalFlagNames = {"zero_flag", "sign_flag", "carry_flag",
|
||||||
"overflow_flag"};
|
"overflow_flag"};
|
||||||
|
@ -2840,14 +2746,6 @@ private:
|
||||||
return std::min<u32>(device.GetMaxVaryings(), Maxwell::NumVaryings);
|
return std::min<u32>(device.GetMaxVaryings(), Maxwell::NumVaryings);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SetHelperEnabled(HelperFunction hf, bool enabled) {
|
|
||||||
helper_functions_enabled[static_cast<size_t>(hf)] = enabled;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool IsHelperEnabled(HelperFunction hf) const {
|
|
||||||
return helper_functions_enabled[static_cast<size_t>(hf)];
|
|
||||||
}
|
|
||||||
|
|
||||||
const Device& device;
|
const Device& device;
|
||||||
const ShaderIR& ir;
|
const ShaderIR& ir;
|
||||||
const Registry& registry;
|
const Registry& registry;
|
||||||
|
@ -2857,13 +2755,9 @@ private:
|
||||||
const Header header;
|
const Header header;
|
||||||
std::unordered_map<u8, VaryingTFB> transform_feedback;
|
std::unordered_map<u8, VaryingTFB> transform_feedback;
|
||||||
|
|
||||||
std::shared_ptr<ShaderFunctionIR> context_func;
|
|
||||||
|
|
||||||
ShaderWriter code;
|
ShaderWriter code;
|
||||||
|
|
||||||
std::optional<u32> max_input_vertices;
|
std::optional<u32> max_input_vertices;
|
||||||
|
|
||||||
std::array<bool, static_cast<size_t>(HelperFunction::Total)> helper_functions_enabled{};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
std::string GetFlowVariable(u32 index) {
|
std::string GetFlowVariable(u32 index) {
|
||||||
|
@ -3008,15 +2902,9 @@ public:
|
||||||
decomp.code.scope++;
|
decomp.code.scope++;
|
||||||
}
|
}
|
||||||
if (ast.kills) {
|
if (ast.kills) {
|
||||||
if (decomp.stage != ShaderType::Fragment) {
|
decomp.code.AddLine("discard;");
|
||||||
decomp.code.AddLine("return;");
|
|
||||||
} else {
|
|
||||||
decomp.code.AddLine("discard;");
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
if (decomp.context_func->IsMain()) {
|
decomp.PreExit();
|
||||||
decomp.PreExit();
|
|
||||||
}
|
|
||||||
decomp.code.AddLine("return;");
|
decomp.code.AddLine("return;");
|
||||||
}
|
}
|
||||||
if (!is_true) {
|
if (!is_true) {
|
||||||
|
@ -3049,13 +2937,13 @@ private:
|
||||||
};
|
};
|
||||||
|
|
||||||
void GLSLDecompiler::DecompileAST() {
|
void GLSLDecompiler::DecompileAST() {
|
||||||
const u32 num_flow_variables = context_func->GetASTNumVariables();
|
const u32 num_flow_variables = ir.GetASTNumVariables();
|
||||||
for (u32 i = 0; i < num_flow_variables; i++) {
|
for (u32 i = 0; i < num_flow_variables; i++) {
|
||||||
code.AddLine("bool {} = false;", GetFlowVariable(i));
|
code.AddLine("bool {} = false;", GetFlowVariable(i));
|
||||||
}
|
}
|
||||||
|
|
||||||
ASTDecompiler decompiler{*this};
|
ASTDecompiler decompiler{*this};
|
||||||
decompiler.Visit(context_func->GetASTProgram());
|
decompiler.Visit(ir.GetASTProgram());
|
||||||
}
|
}
|
||||||
|
|
||||||
} // Anonymous namespace
|
} // Anonymous namespace
|
||||||
|
|
|
@ -406,38 +406,10 @@ private:
|
||||||
binding = DeclareStorageTexels(binding);
|
binding = DeclareStorageTexels(binding);
|
||||||
binding = DeclareImages(binding);
|
binding = DeclareImages(binding);
|
||||||
|
|
||||||
const auto& subfunctions = ir.GetSubFunctions();
|
|
||||||
|
|
||||||
labels.resize(subfunctions.size() + 1);
|
|
||||||
other_functions.resize(subfunctions.size());
|
|
||||||
|
|
||||||
auto it = subfunctions.rbegin();
|
|
||||||
while (it != subfunctions.rend()) {
|
|
||||||
context_func = *it;
|
|
||||||
other_functions[context_func->GetId() - 1] =
|
|
||||||
OpFunction(t_void, {}, TypeFunction(t_void));
|
|
||||||
AddLabel();
|
|
||||||
|
|
||||||
if (context_func->IsDecompiled()) {
|
|
||||||
DeclareFlowVariables();
|
|
||||||
DecompileAST();
|
|
||||||
} else {
|
|
||||||
AllocateLabels();
|
|
||||||
DecompileBranchMode();
|
|
||||||
}
|
|
||||||
|
|
||||||
OpReturn();
|
|
||||||
OpFunctionEnd();
|
|
||||||
|
|
||||||
it++;
|
|
||||||
}
|
|
||||||
|
|
||||||
context_func = ir.GetMainFunction();
|
|
||||||
|
|
||||||
const Id main = OpFunction(t_void, {}, TypeFunction(t_void));
|
const Id main = OpFunction(t_void, {}, TypeFunction(t_void));
|
||||||
AddLabel();
|
AddLabel();
|
||||||
|
|
||||||
if (context_func->IsDecompiled()) {
|
if (ir.IsDecompiled()) {
|
||||||
DeclareFlowVariables();
|
DeclareFlowVariables();
|
||||||
DecompileAST();
|
DecompileAST();
|
||||||
} else {
|
} else {
|
||||||
|
@ -469,18 +441,16 @@ private:
|
||||||
void DecompileAST();
|
void DecompileAST();
|
||||||
|
|
||||||
void DecompileBranchMode() {
|
void DecompileBranchMode() {
|
||||||
const u32 first_address = context_func->GetBasicBlocks().begin()->first;
|
const u32 first_address = ir.GetBasicBlocks().begin()->first;
|
||||||
const u32 func_id = context_func->GetId();
|
const Id loop_label = OpLabel("loop");
|
||||||
const std::string func_id_msg = std::to_string(func_id);
|
const Id merge_label = OpLabel("merge");
|
||||||
const Id loop_label = OpLabel("loop_" + func_id_msg);
|
|
||||||
const Id merge_label = OpLabel("merge_" + func_id_msg);
|
|
||||||
const Id dummy_label = OpLabel();
|
const Id dummy_label = OpLabel();
|
||||||
const Id jump_label = OpLabel();
|
const Id jump_label = OpLabel();
|
||||||
continue_label = OpLabel("continue_" + func_id_msg);
|
continue_label = OpLabel("continue");
|
||||||
|
|
||||||
std::vector<Sirit::Literal> literals;
|
std::vector<Sirit::Literal> literals;
|
||||||
std::vector<Id> branch_labels;
|
std::vector<Id> branch_labels;
|
||||||
for (const auto& [literal, label] : labels[func_id]) {
|
for (const auto& [literal, label] : labels) {
|
||||||
literals.push_back(literal);
|
literals.push_back(literal);
|
||||||
branch_labels.push_back(label);
|
branch_labels.push_back(label);
|
||||||
}
|
}
|
||||||
|
@ -492,11 +462,11 @@ private:
|
||||||
std::tie(ssy_flow_stack, ssy_flow_stack_top) = CreateFlowStack();
|
std::tie(ssy_flow_stack, ssy_flow_stack_top) = CreateFlowStack();
|
||||||
std::tie(pbk_flow_stack, pbk_flow_stack_top) = CreateFlowStack();
|
std::tie(pbk_flow_stack, pbk_flow_stack_top) = CreateFlowStack();
|
||||||
|
|
||||||
Name(jmp_to, "jmp_to_" + func_id_msg);
|
Name(jmp_to, "jmp_to");
|
||||||
Name(ssy_flow_stack, "ssy_flow_stack_" + func_id_msg);
|
Name(ssy_flow_stack, "ssy_flow_stack");
|
||||||
Name(ssy_flow_stack_top, "ssy_flow_stack_top_" + func_id_msg);
|
Name(ssy_flow_stack_top, "ssy_flow_stack_top");
|
||||||
Name(pbk_flow_stack, "pbk_flow_stack_" + func_id_msg);
|
Name(pbk_flow_stack, "pbk_flow_stack");
|
||||||
Name(pbk_flow_stack_top, "pbk_flow_stack_top_" + func_id_msg);
|
Name(pbk_flow_stack_top, "pbk_flow_stack_top");
|
||||||
|
|
||||||
DefinePrologue();
|
DefinePrologue();
|
||||||
|
|
||||||
|
@ -514,14 +484,13 @@ private:
|
||||||
AddLabel(default_branch);
|
AddLabel(default_branch);
|
||||||
OpReturn();
|
OpReturn();
|
||||||
|
|
||||||
for (const auto& [address, bb] : context_func->GetBasicBlocks()) {
|
for (const auto& [address, bb] : ir.GetBasicBlocks()) {
|
||||||
AddLabel(labels[func_id].at(address));
|
AddLabel(labels.at(address));
|
||||||
|
|
||||||
VisitBasicBlock(bb);
|
VisitBasicBlock(bb);
|
||||||
|
|
||||||
const auto next_it = labels[func_id].lower_bound(address + 1);
|
const auto next_it = labels.lower_bound(address + 1);
|
||||||
const Id next_label =
|
const Id next_label = next_it != labels.end() ? next_it->second : default_branch;
|
||||||
next_it != labels[func_id].end() ? next_it->second : default_branch;
|
|
||||||
OpBranch(next_label);
|
OpBranch(next_label);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -539,10 +508,9 @@ private:
|
||||||
static constexpr auto INTERNAL_FLAGS_COUNT = static_cast<std::size_t>(InternalFlag::Amount);
|
static constexpr auto INTERNAL_FLAGS_COUNT = static_cast<std::size_t>(InternalFlag::Amount);
|
||||||
|
|
||||||
void AllocateLabels() {
|
void AllocateLabels() {
|
||||||
const u32 func_id = context_func->GetId();
|
for (const auto& pair : ir.GetBasicBlocks()) {
|
||||||
for (const auto& pair : context_func->GetBasicBlocks()) {
|
|
||||||
const u32 address = pair.first;
|
const u32 address = pair.first;
|
||||||
labels[func_id].emplace(address, OpLabel(fmt::format("label_0x{:x}", address)));
|
labels.emplace(address, OpLabel(fmt::format("label_0x{:x}", address)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -621,14 +589,6 @@ private:
|
||||||
DeclareOutputVertex();
|
DeclareOutputVertex();
|
||||||
}
|
}
|
||||||
|
|
||||||
void SafeKill() {
|
|
||||||
if (stage != ShaderType::Fragment) {
|
|
||||||
OpReturn();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
OpKill();
|
|
||||||
}
|
|
||||||
|
|
||||||
void DeclareFragment() {
|
void DeclareFragment() {
|
||||||
if (stage != ShaderType::Fragment) {
|
if (stage != ShaderType::Fragment) {
|
||||||
return;
|
return;
|
||||||
|
@ -696,7 +656,7 @@ private:
|
||||||
}
|
}
|
||||||
|
|
||||||
void DeclareFlowVariables() {
|
void DeclareFlowVariables() {
|
||||||
for (u32 i = 0; i < context_func->GetASTNumVariables(); i++) {
|
for (u32 i = 0; i < ir.GetASTNumVariables(); i++) {
|
||||||
const Id id = OpVariable(t_prv_bool, spv::StorageClass::Private, v_false);
|
const Id id = OpVariable(t_prv_bool, spv::StorageClass::Private, v_false);
|
||||||
Name(id, fmt::format("flow_var_{}", static_cast<u32>(i)));
|
Name(id, fmt::format("flow_var_{}", static_cast<u32>(i)));
|
||||||
flow_variables.emplace(i, AddGlobalVariable(id));
|
flow_variables.emplace(i, AddGlobalVariable(id));
|
||||||
|
@ -1373,12 +1333,6 @@ private:
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (const auto func_call = std::get_if<FunctionCallNode>(&*node)) {
|
|
||||||
const u32 func_id = func_call->GetFuncId();
|
|
||||||
OpFunctionCall(t_void, other_functions[func_id - 1]);
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (const auto comment = std::get_if<CommentNode>(&*node)) {
|
if (const auto comment = std::get_if<CommentNode>(&*node)) {
|
||||||
if (device.HasDebuggingToolAttached()) {
|
if (device.HasDebuggingToolAttached()) {
|
||||||
// We should insert comments with OpString instead of using named variables
|
// We should insert comments with OpString instead of using named variables
|
||||||
|
@ -2170,7 +2124,7 @@ private:
|
||||||
|
|
||||||
OpBranchConditional(condition, true_label, discard_label);
|
OpBranchConditional(condition, true_label, discard_label);
|
||||||
AddLabel(discard_label);
|
AddLabel(discard_label);
|
||||||
SafeKill();
|
OpKill();
|
||||||
AddLabel(true_label);
|
AddLabel(true_label);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2221,9 +2175,7 @@ private:
|
||||||
}
|
}
|
||||||
|
|
||||||
Expression Exit(Operation operation) {
|
Expression Exit(Operation operation) {
|
||||||
if (context_func->IsMain()) {
|
PreExit();
|
||||||
PreExit();
|
|
||||||
}
|
|
||||||
inside_branch = true;
|
inside_branch = true;
|
||||||
if (conditional_branch_set) {
|
if (conditional_branch_set) {
|
||||||
OpReturn();
|
OpReturn();
|
||||||
|
@ -2240,12 +2192,12 @@ private:
|
||||||
Expression Discard(Operation operation) {
|
Expression Discard(Operation operation) {
|
||||||
inside_branch = true;
|
inside_branch = true;
|
||||||
if (conditional_branch_set) {
|
if (conditional_branch_set) {
|
||||||
SafeKill();
|
OpKill();
|
||||||
} else {
|
} else {
|
||||||
const Id dummy = OpLabel();
|
const Id dummy = OpLabel();
|
||||||
OpBranch(dummy);
|
OpBranch(dummy);
|
||||||
AddLabel(dummy);
|
AddLabel(dummy);
|
||||||
SafeKill();
|
OpKill();
|
||||||
AddLabel();
|
AddLabel();
|
||||||
}
|
}
|
||||||
return {};
|
return {};
|
||||||
|
@ -2324,7 +2276,7 @@ private:
|
||||||
}
|
}
|
||||||
|
|
||||||
Expression Barrier(Operation) {
|
Expression Barrier(Operation) {
|
||||||
if (!context_func->IsDecompiled()) {
|
if (!ir.IsDecompiled()) {
|
||||||
LOG_ERROR(Render_Vulkan, "OpBarrier used by shader is not decompiled");
|
LOG_ERROR(Render_Vulkan, "OpBarrier used by shader is not decompiled");
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
@ -2818,8 +2770,6 @@ private:
|
||||||
const Specialization& specialization;
|
const Specialization& specialization;
|
||||||
std::unordered_map<u8, VaryingTFB> transform_feedback;
|
std::unordered_map<u8, VaryingTFB> transform_feedback;
|
||||||
|
|
||||||
std::shared_ptr<ShaderFunctionIR> context_func;
|
|
||||||
|
|
||||||
const Id t_void = Name(TypeVoid(), "void");
|
const Id t_void = Name(TypeVoid(), "void");
|
||||||
|
|
||||||
const Id t_bool = Name(TypeBool(), "bool");
|
const Id t_bool = Name(TypeBool(), "bool");
|
||||||
|
@ -2946,8 +2896,7 @@ private:
|
||||||
Id ssy_flow_stack{};
|
Id ssy_flow_stack{};
|
||||||
Id pbk_flow_stack{};
|
Id pbk_flow_stack{};
|
||||||
Id continue_label{};
|
Id continue_label{};
|
||||||
std::vector<std::map<u32, Id>> labels;
|
std::map<u32, Id> labels;
|
||||||
std::vector<Id> other_functions;
|
|
||||||
|
|
||||||
bool conditional_branch_set{};
|
bool conditional_branch_set{};
|
||||||
bool inside_branch{};
|
bool inside_branch{};
|
||||||
|
@ -3098,11 +3047,9 @@ public:
|
||||||
decomp.OpBranchConditional(condition, then_label, endif_label);
|
decomp.OpBranchConditional(condition, then_label, endif_label);
|
||||||
decomp.AddLabel(then_label);
|
decomp.AddLabel(then_label);
|
||||||
if (ast.kills) {
|
if (ast.kills) {
|
||||||
decomp.SafeKill();
|
decomp.OpKill();
|
||||||
} else {
|
} else {
|
||||||
if (decomp.context_func->IsMain()) {
|
decomp.PreExit();
|
||||||
decomp.PreExit();
|
|
||||||
}
|
|
||||||
decomp.OpReturn();
|
decomp.OpReturn();
|
||||||
}
|
}
|
||||||
decomp.AddLabel(endif_label);
|
decomp.AddLabel(endif_label);
|
||||||
|
@ -3111,11 +3058,9 @@ public:
|
||||||
decomp.OpBranch(next_block);
|
decomp.OpBranch(next_block);
|
||||||
decomp.AddLabel(next_block);
|
decomp.AddLabel(next_block);
|
||||||
if (ast.kills) {
|
if (ast.kills) {
|
||||||
decomp.SafeKill();
|
decomp.OpKill();
|
||||||
} else {
|
} else {
|
||||||
if (decomp.context_func->IsMain()) {
|
decomp.PreExit();
|
||||||
decomp.PreExit();
|
|
||||||
}
|
|
||||||
decomp.OpReturn();
|
decomp.OpReturn();
|
||||||
}
|
}
|
||||||
decomp.AddLabel(decomp.OpLabel());
|
decomp.AddLabel(decomp.OpLabel());
|
||||||
|
@ -3152,7 +3097,7 @@ private:
|
||||||
};
|
};
|
||||||
|
|
||||||
void SPIRVDecompiler::DecompileAST() {
|
void SPIRVDecompiler::DecompileAST() {
|
||||||
const u32 num_flow_variables = context_func->GetASTNumVariables();
|
const u32 num_flow_variables = ir.GetASTNumVariables();
|
||||||
for (u32 i = 0; i < num_flow_variables; i++) {
|
for (u32 i = 0; i < num_flow_variables; i++) {
|
||||||
const Id id = OpVariable(t_prv_bool, spv::StorageClass::Private, v_false);
|
const Id id = OpVariable(t_prv_bool, spv::StorageClass::Private, v_false);
|
||||||
Name(id, fmt::format("flow_var_{}", i));
|
Name(id, fmt::format("flow_var_{}", i));
|
||||||
|
@ -3161,7 +3106,7 @@ void SPIRVDecompiler::DecompileAST() {
|
||||||
|
|
||||||
DefinePrologue();
|
DefinePrologue();
|
||||||
|
|
||||||
const ASTNode program = context_func->GetASTProgram();
|
const ASTNode program = ir.GetASTProgram();
|
||||||
ASTDecompiler decompiler{*this};
|
ASTDecompiler decompiler{*this};
|
||||||
decompiler.Visit(program);
|
decompiler.Visit(program);
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,6 @@
|
||||||
#include <set>
|
#include <set>
|
||||||
#include <stack>
|
#include <stack>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
#include <unordered_set>
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "common/assert.h"
|
#include "common/assert.h"
|
||||||
|
@ -27,29 +26,17 @@ using Tegra::Shader::OpCode;
|
||||||
|
|
||||||
constexpr s32 unassigned_branch = -2;
|
constexpr s32 unassigned_branch = -2;
|
||||||
|
|
||||||
enum class JumpLabel : u32 {
|
|
||||||
SSYClass = 0,
|
|
||||||
PBKClass = 1,
|
|
||||||
};
|
|
||||||
|
|
||||||
struct JumpItem {
|
|
||||||
JumpLabel type;
|
|
||||||
u32 address;
|
|
||||||
|
|
||||||
bool operator==(const JumpItem& other) const {
|
|
||||||
return std::tie(type, address) == std::tie(other.type, other.address);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Query {
|
struct Query {
|
||||||
u32 address{};
|
u32 address{};
|
||||||
std::stack<JumpItem> stack{};
|
std::stack<u32> ssy_stack{};
|
||||||
|
std::stack<u32> pbk_stack{};
|
||||||
};
|
};
|
||||||
|
|
||||||
struct BlockStack {
|
struct BlockStack {
|
||||||
BlockStack() = default;
|
BlockStack() = default;
|
||||||
explicit BlockStack(const Query& q) : stack{q.stack} {}
|
explicit BlockStack(const Query& q) : ssy_stack{q.ssy_stack}, pbk_stack{q.pbk_stack} {}
|
||||||
std::stack<JumpItem> stack{};
|
std::stack<u32> ssy_stack{};
|
||||||
|
std::stack<u32> pbk_stack{};
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename T, typename... Args>
|
template <typename T, typename... Args>
|
||||||
|
@ -78,36 +65,20 @@ struct BlockInfo {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ProgramControl {
|
|
||||||
std::unordered_set<u32> found_functions{};
|
|
||||||
std::list<u32> pending_functions{};
|
|
||||||
|
|
||||||
void RegisterFunction(u32 address) {
|
|
||||||
if (found_functions.count(address) != 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
found_functions.insert(address);
|
|
||||||
pending_functions.emplace_back(address);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct CFGRebuildState {
|
struct CFGRebuildState {
|
||||||
explicit CFGRebuildState(ProgramControl& control_, const ProgramCode& program_code_, u32 start_,
|
explicit CFGRebuildState(const ProgramCode& program_code_, u32 start_, Registry& registry_)
|
||||||
u32 base_start_, Registry& registry_)
|
: program_code{program_code_}, registry{registry_}, start{start_} {}
|
||||||
: control{control_}, program_code{program_code_}, registry{registry_}, start{start_},
|
|
||||||
base_start{base_start_} {}
|
|
||||||
|
|
||||||
ProgramControl& control;
|
|
||||||
const ProgramCode& program_code;
|
const ProgramCode& program_code;
|
||||||
Registry& registry;
|
Registry& registry;
|
||||||
u32 start{};
|
u32 start{};
|
||||||
u32 base_start{};
|
|
||||||
std::vector<BlockInfo> block_info;
|
std::vector<BlockInfo> block_info;
|
||||||
std::list<u32> inspect_queries;
|
std::list<u32> inspect_queries;
|
||||||
std::list<Query> queries;
|
std::list<Query> queries;
|
||||||
std::unordered_map<u32, u32> registered;
|
std::unordered_map<u32, u32> registered;
|
||||||
std::set<u32> labels;
|
std::set<u32> labels;
|
||||||
std::map<u32, JumpItem> jump_labels;
|
std::map<u32, u32> ssy_labels;
|
||||||
|
std::map<u32, u32> pbk_labels;
|
||||||
std::unordered_map<u32, BlockStack> stacks;
|
std::unordered_map<u32, BlockStack> stacks;
|
||||||
ASTManager* manager{};
|
ASTManager* manager{};
|
||||||
};
|
};
|
||||||
|
@ -182,7 +153,7 @@ template <typename Result, typename TestCallable, typename PackCallable>
|
||||||
std::optional<Result> TrackInstruction(const CFGRebuildState& state, u32& pos, TestCallable test,
|
std::optional<Result> TrackInstruction(const CFGRebuildState& state, u32& pos, TestCallable test,
|
||||||
PackCallable pack) {
|
PackCallable pack) {
|
||||||
for (; pos >= state.start; --pos) {
|
for (; pos >= state.start; --pos) {
|
||||||
if (IsSchedInstruction(pos, state.base_start)) {
|
if (IsSchedInstruction(pos, state.start)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
const Instruction instr = state.program_code[pos];
|
const Instruction instr = state.program_code[pos];
|
||||||
|
@ -291,7 +262,7 @@ std::pair<ParseResult, ParseInfo> ParseCode(CFGRebuildState& state, u32 address)
|
||||||
single_branch.ignore = true;
|
single_branch.ignore = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (IsSchedInstruction(offset, state.base_start)) {
|
if (IsSchedInstruction(offset, state.start)) {
|
||||||
offset++;
|
offset++;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -303,7 +274,6 @@ std::pair<ParseResult, ParseInfo> ParseCode(CFGRebuildState& state, u32 address)
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (opcode->get().GetId()) {
|
switch (opcode->get().GetId()) {
|
||||||
case OpCode::Id::RET:
|
|
||||||
case OpCode::Id::EXIT: {
|
case OpCode::Id::EXIT: {
|
||||||
const auto pred_index = static_cast<u32>(instr.pred.pred_index);
|
const auto pred_index = static_cast<u32>(instr.pred.pred_index);
|
||||||
single_branch.condition.predicate = GetPredicate(pred_index, instr.negate_pred != 0);
|
single_branch.condition.predicate = GetPredicate(pred_index, instr.negate_pred != 0);
|
||||||
|
@ -441,20 +411,13 @@ std::pair<ParseResult, ParseInfo> ParseCode(CFGRebuildState& state, u32 address)
|
||||||
case OpCode::Id::SSY: {
|
case OpCode::Id::SSY: {
|
||||||
const u32 target = offset + instr.bra.GetBranchTarget();
|
const u32 target = offset + instr.bra.GetBranchTarget();
|
||||||
insert_label(state, target);
|
insert_label(state, target);
|
||||||
JumpItem it = {JumpLabel::SSYClass, target};
|
state.ssy_labels.emplace(offset, target);
|
||||||
state.jump_labels.emplace(offset, it);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case OpCode::Id::PBK: {
|
case OpCode::Id::PBK: {
|
||||||
const u32 target = offset + instr.bra.GetBranchTarget();
|
const u32 target = offset + instr.bra.GetBranchTarget();
|
||||||
insert_label(state, target);
|
insert_label(state, target);
|
||||||
JumpItem it = {JumpLabel::PBKClass, target};
|
state.pbk_labels.emplace(offset, target);
|
||||||
state.jump_labels.emplace(offset, it);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case OpCode::Id::CAL: {
|
|
||||||
const u32 target = offset + instr.bra.GetBranchTarget();
|
|
||||||
state.control.RegisterFunction(target);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case OpCode::Id::BRX: {
|
case OpCode::Id::BRX: {
|
||||||
|
@ -550,7 +513,7 @@ bool TryInspectAddress(CFGRebuildState& state) {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TryQuery(CFGRebuildState& state) {
|
bool TryQuery(CFGRebuildState& state) {
|
||||||
const auto gather_labels = [](std::stack<JumpItem>& cc, std::map<u32, JumpItem>& labels,
|
const auto gather_labels = [](std::stack<u32>& cc, std::map<u32, u32>& labels,
|
||||||
BlockInfo& block) {
|
BlockInfo& block) {
|
||||||
auto gather_start = labels.lower_bound(block.start);
|
auto gather_start = labels.lower_bound(block.start);
|
||||||
const auto gather_end = labels.upper_bound(block.end);
|
const auto gather_end = labels.upper_bound(block.end);
|
||||||
|
@ -559,19 +522,6 @@ bool TryQuery(CFGRebuildState& state) {
|
||||||
++gather_start;
|
++gather_start;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
const auto pop_labels = [](JumpLabel type, SingleBranch* branch, Query& query) -> bool {
|
|
||||||
while (!query.stack.empty() && query.stack.top().type != type) {
|
|
||||||
query.stack.pop();
|
|
||||||
}
|
|
||||||
if (query.stack.empty()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (branch->address == unassigned_branch) {
|
|
||||||
branch->address = query.stack.top().address;
|
|
||||||
}
|
|
||||||
query.stack.pop();
|
|
||||||
return true;
|
|
||||||
};
|
|
||||||
if (state.queries.empty()) {
|
if (state.queries.empty()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -584,7 +534,8 @@ bool TryQuery(CFGRebuildState& state) {
|
||||||
// consumes a label. Schedule new queries accordingly
|
// consumes a label. Schedule new queries accordingly
|
||||||
if (block.visited) {
|
if (block.visited) {
|
||||||
BlockStack& stack = state.stacks[q.address];
|
BlockStack& stack = state.stacks[q.address];
|
||||||
const bool all_okay = (stack.stack.empty() || q.stack == stack.stack);
|
const bool all_okay = (stack.ssy_stack.empty() || q.ssy_stack == stack.ssy_stack) &&
|
||||||
|
(stack.pbk_stack.empty() || q.pbk_stack == stack.pbk_stack);
|
||||||
state.queries.pop_front();
|
state.queries.pop_front();
|
||||||
return all_okay;
|
return all_okay;
|
||||||
}
|
}
|
||||||
|
@ -593,7 +544,8 @@ bool TryQuery(CFGRebuildState& state) {
|
||||||
|
|
||||||
Query q2(q);
|
Query q2(q);
|
||||||
state.queries.pop_front();
|
state.queries.pop_front();
|
||||||
gather_labels(q2.stack, state.jump_labels, block);
|
gather_labels(q2.ssy_stack, state.ssy_labels, block);
|
||||||
|
gather_labels(q2.pbk_stack, state.pbk_labels, block);
|
||||||
if (std::holds_alternative<SingleBranch>(*block.branch)) {
|
if (std::holds_alternative<SingleBranch>(*block.branch)) {
|
||||||
auto* branch = std::get_if<SingleBranch>(block.branch.get());
|
auto* branch = std::get_if<SingleBranch>(block.branch.get());
|
||||||
if (!branch->condition.IsUnconditional()) {
|
if (!branch->condition.IsUnconditional()) {
|
||||||
|
@ -603,10 +555,16 @@ bool TryQuery(CFGRebuildState& state) {
|
||||||
|
|
||||||
auto& conditional_query = state.queries.emplace_back(q2);
|
auto& conditional_query = state.queries.emplace_back(q2);
|
||||||
if (branch->is_sync) {
|
if (branch->is_sync) {
|
||||||
pop_labels(JumpLabel::SSYClass, branch, conditional_query);
|
if (branch->address == unassigned_branch) {
|
||||||
|
branch->address = conditional_query.ssy_stack.top();
|
||||||
|
}
|
||||||
|
conditional_query.ssy_stack.pop();
|
||||||
}
|
}
|
||||||
if (branch->is_brk) {
|
if (branch->is_brk) {
|
||||||
pop_labels(JumpLabel::PBKClass, branch, conditional_query);
|
if (branch->address == unassigned_branch) {
|
||||||
|
branch->address = conditional_query.pbk_stack.top();
|
||||||
|
}
|
||||||
|
conditional_query.pbk_stack.pop();
|
||||||
}
|
}
|
||||||
conditional_query.address = branch->address;
|
conditional_query.address = branch->address;
|
||||||
return true;
|
return true;
|
||||||
|
@ -688,23 +646,25 @@ void DecompileShader(CFGRebuildState& state) {
|
||||||
state.manager->Decompile();
|
state.manager->Decompile();
|
||||||
}
|
}
|
||||||
|
|
||||||
ShaderFunction ScanFunction(ProgramControl& control, const ProgramCode& program_code,
|
} // Anonymous namespace
|
||||||
u32 start_address, u32 base_start, const CompilerSettings& settings,
|
|
||||||
Registry& registry) {
|
std::unique_ptr<ShaderCharacteristics> ScanFlow(const ProgramCode& program_code, u32 start_address,
|
||||||
ShaderFunction result_out{};
|
const CompilerSettings& settings,
|
||||||
|
Registry& registry) {
|
||||||
|
auto result_out = std::make_unique<ShaderCharacteristics>();
|
||||||
if (settings.depth == CompileDepth::BruteForce) {
|
if (settings.depth == CompileDepth::BruteForce) {
|
||||||
result_out.settings.depth = CompileDepth::BruteForce;
|
result_out->settings.depth = CompileDepth::BruteForce;
|
||||||
return result_out;
|
return result_out;
|
||||||
}
|
}
|
||||||
|
|
||||||
CFGRebuildState state{control, program_code, start_address, base_start, registry};
|
CFGRebuildState state{program_code, start_address, registry};
|
||||||
// Inspect Code and generate blocks
|
// Inspect Code and generate blocks
|
||||||
state.labels.clear();
|
state.labels.clear();
|
||||||
state.labels.emplace(start_address);
|
state.labels.emplace(start_address);
|
||||||
state.inspect_queries.push_back(state.start);
|
state.inspect_queries.push_back(state.start);
|
||||||
while (!state.inspect_queries.empty()) {
|
while (!state.inspect_queries.empty()) {
|
||||||
if (!TryInspectAddress(state)) {
|
if (!TryInspectAddress(state)) {
|
||||||
result_out.settings.depth = CompileDepth::BruteForce;
|
result_out->settings.depth = CompileDepth::BruteForce;
|
||||||
return result_out;
|
return result_out;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -715,7 +675,7 @@ ShaderFunction ScanFunction(ProgramControl& control, const ProgramCode& program_
|
||||||
|
|
||||||
if (settings.depth != CompileDepth::FlowStack) {
|
if (settings.depth != CompileDepth::FlowStack) {
|
||||||
// Decompile Stacks
|
// Decompile Stacks
|
||||||
state.queries.push_back(Query{state.start, {}});
|
state.queries.push_back(Query{state.start, {}, {}});
|
||||||
decompiled = true;
|
decompiled = true;
|
||||||
while (!state.queries.empty()) {
|
while (!state.queries.empty()) {
|
||||||
if (!TryQuery(state)) {
|
if (!TryQuery(state)) {
|
||||||
|
@ -745,18 +705,19 @@ ShaderFunction ScanFunction(ProgramControl& control, const ProgramCode& program_
|
||||||
state.manager->ShowCurrentState("Of Shader");
|
state.manager->ShowCurrentState("Of Shader");
|
||||||
state.manager->Clear();
|
state.manager->Clear();
|
||||||
} else {
|
} else {
|
||||||
result_out.start = start_address;
|
auto characteristics = std::make_unique<ShaderCharacteristics>();
|
||||||
result_out.settings.depth = settings.depth;
|
characteristics->start = start_address;
|
||||||
result_out.manager = std::move(manager);
|
characteristics->settings.depth = settings.depth;
|
||||||
result_out.end = state.block_info.back().end + 1;
|
characteristics->manager = std::move(manager);
|
||||||
return result_out;
|
characteristics->end = state.block_info.back().end + 1;
|
||||||
|
return characteristics;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
result_out.start = start_address;
|
result_out->start = start_address;
|
||||||
result_out.settings.depth =
|
result_out->settings.depth =
|
||||||
use_flow_stack ? CompileDepth::FlowStack : CompileDepth::NoFlowStack;
|
use_flow_stack ? CompileDepth::FlowStack : CompileDepth::NoFlowStack;
|
||||||
result_out.blocks.clear();
|
result_out->blocks.clear();
|
||||||
for (auto& block : state.block_info) {
|
for (auto& block : state.block_info) {
|
||||||
ShaderBlock new_block{};
|
ShaderBlock new_block{};
|
||||||
new_block.start = block.start;
|
new_block.start = block.start;
|
||||||
|
@ -765,20 +726,20 @@ ShaderFunction ScanFunction(ProgramControl& control, const ProgramCode& program_
|
||||||
if (!new_block.ignore_branch) {
|
if (!new_block.ignore_branch) {
|
||||||
new_block.branch = block.branch;
|
new_block.branch = block.branch;
|
||||||
}
|
}
|
||||||
result_out.end = std::max(result_out.end, block.end);
|
result_out->end = std::max(result_out->end, block.end);
|
||||||
result_out.blocks.push_back(new_block);
|
result_out->blocks.push_back(new_block);
|
||||||
}
|
}
|
||||||
if (!use_flow_stack) {
|
if (!use_flow_stack) {
|
||||||
result_out.labels = std::move(state.labels);
|
result_out->labels = std::move(state.labels);
|
||||||
return result_out;
|
return result_out;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto back = result_out.blocks.begin();
|
auto back = result_out->blocks.begin();
|
||||||
auto next = std::next(back);
|
auto next = std::next(back);
|
||||||
while (next != result_out.blocks.end()) {
|
while (next != result_out->blocks.end()) {
|
||||||
if (!state.labels.contains(next->start) && next->start == back->end + 1) {
|
if (!state.labels.contains(next->start) && next->start == back->end + 1) {
|
||||||
back->end = next->end;
|
back->end = next->end;
|
||||||
next = result_out.blocks.erase(next);
|
next = result_out->blocks.erase(next);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
back = next;
|
back = next;
|
||||||
|
@ -787,22 +748,4 @@ ShaderFunction ScanFunction(ProgramControl& control, const ProgramCode& program_
|
||||||
|
|
||||||
return result_out;
|
return result_out;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // Anonymous namespace
|
|
||||||
|
|
||||||
std::unique_ptr<ShaderProgram> ScanFlow(const ProgramCode& program_code, u32 start_address,
|
|
||||||
const CompilerSettings& settings, Registry& registry) {
|
|
||||||
ProgramControl control{};
|
|
||||||
auto result_out = std::make_unique<ShaderProgram>();
|
|
||||||
result_out->main =
|
|
||||||
ScanFunction(control, program_code, start_address, start_address, settings, registry);
|
|
||||||
while (!control.pending_functions.empty()) {
|
|
||||||
u32 address = control.pending_functions.front();
|
|
||||||
auto fun = ScanFunction(control, program_code, address, start_address, settings, registry);
|
|
||||||
result_out->subfunctions.emplace(address, std::move(fun));
|
|
||||||
control.pending_functions.pop_front();
|
|
||||||
}
|
|
||||||
return result_out;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace VideoCommon::Shader
|
} // namespace VideoCommon::Shader
|
||||||
|
|
|
@ -5,7 +5,6 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <list>
|
#include <list>
|
||||||
#include <map>
|
|
||||||
#include <optional>
|
#include <optional>
|
||||||
#include <set>
|
#include <set>
|
||||||
#include <variant>
|
#include <variant>
|
||||||
|
@ -102,7 +101,7 @@ struct ShaderBlock {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ShaderFunction {
|
struct ShaderCharacteristics {
|
||||||
std::list<ShaderBlock> blocks{};
|
std::list<ShaderBlock> blocks{};
|
||||||
std::set<u32> labels{};
|
std::set<u32> labels{};
|
||||||
u32 start{};
|
u32 start{};
|
||||||
|
@ -111,12 +110,8 @@ struct ShaderFunction {
|
||||||
CompilerSettings settings{};
|
CompilerSettings settings{};
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ShaderProgram {
|
std::unique_ptr<ShaderCharacteristics> ScanFlow(const ProgramCode& program_code, u32 start_address,
|
||||||
ShaderFunction main;
|
const CompilerSettings& settings,
|
||||||
std::map<u32, ShaderFunction> subfunctions;
|
Registry& registry);
|
||||||
};
|
|
||||||
|
|
||||||
std::unique_ptr<ShaderProgram> ScanFlow(const ProgramCode& program_code, u32 start_address,
|
|
||||||
const CompilerSettings& settings, Registry& registry);
|
|
||||||
|
|
||||||
} // namespace VideoCommon::Shader
|
} // namespace VideoCommon::Shader
|
||||||
|
|
|
@ -64,52 +64,9 @@ std::optional<u32> TryDeduceSamplerSize(const SamplerEntry& sampler_to_deduce,
|
||||||
|
|
||||||
} // Anonymous namespace
|
} // Anonymous namespace
|
||||||
|
|
||||||
class ExprDecoder {
|
|
||||||
public:
|
|
||||||
explicit ExprDecoder(ShaderIR& ir_) : ir(ir_) {}
|
|
||||||
|
|
||||||
void operator()(const ExprAnd& expr) {
|
|
||||||
Visit(expr.operand1);
|
|
||||||
Visit(expr.operand2);
|
|
||||||
}
|
|
||||||
|
|
||||||
void operator()(const ExprOr& expr) {
|
|
||||||
Visit(expr.operand1);
|
|
||||||
Visit(expr.operand2);
|
|
||||||
}
|
|
||||||
|
|
||||||
void operator()(const ExprNot& expr) {
|
|
||||||
Visit(expr.operand1);
|
|
||||||
}
|
|
||||||
|
|
||||||
void operator()(const ExprPredicate& expr) {
|
|
||||||
const auto pred = static_cast<Tegra::Shader::Pred>(expr.predicate);
|
|
||||||
if (pred != Pred::UnusedIndex && pred != Pred::NeverExecute) {
|
|
||||||
ir.used_predicates.insert(pred);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void operator()(const ExprCondCode& expr) {}
|
|
||||||
|
|
||||||
void operator()(const ExprVar& expr) {}
|
|
||||||
|
|
||||||
void operator()(const ExprBoolean& expr) {}
|
|
||||||
|
|
||||||
void operator()(const ExprGprEqual& expr) {
|
|
||||||
ir.used_registers.insert(expr.gpr);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Visit(const Expr& node) {
|
|
||||||
return std::visit(*this, *node);
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
ShaderIR& ir;
|
|
||||||
};
|
|
||||||
|
|
||||||
class ASTDecoder {
|
class ASTDecoder {
|
||||||
public:
|
public:
|
||||||
explicit ASTDecoder(ShaderIR& ir_) : ir(ir_), decoder(ir_) {}
|
explicit ASTDecoder(ShaderIR& ir_) : ir(ir_) {}
|
||||||
|
|
||||||
void operator()(ASTProgram& ast) {
|
void operator()(ASTProgram& ast) {
|
||||||
ASTNode current = ast.nodes.GetFirst();
|
ASTNode current = ast.nodes.GetFirst();
|
||||||
|
@ -120,7 +77,6 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
void operator()(ASTIfThen& ast) {
|
void operator()(ASTIfThen& ast) {
|
||||||
decoder.Visit(ast.condition);
|
|
||||||
ASTNode current = ast.nodes.GetFirst();
|
ASTNode current = ast.nodes.GetFirst();
|
||||||
while (current) {
|
while (current) {
|
||||||
Visit(current);
|
Visit(current);
|
||||||
|
@ -140,18 +96,13 @@ public:
|
||||||
|
|
||||||
void operator()(ASTBlockDecoded& ast) {}
|
void operator()(ASTBlockDecoded& ast) {}
|
||||||
|
|
||||||
void operator()(ASTVarSet& ast) {
|
void operator()(ASTVarSet& ast) {}
|
||||||
decoder.Visit(ast.condition);
|
|
||||||
}
|
|
||||||
|
|
||||||
void operator()(ASTLabel& ast) {}
|
void operator()(ASTLabel& ast) {}
|
||||||
|
|
||||||
void operator()(ASTGoto& ast) {
|
void operator()(ASTGoto& ast) {}
|
||||||
decoder.Visit(ast.condition);
|
|
||||||
}
|
|
||||||
|
|
||||||
void operator()(ASTDoWhile& ast) {
|
void operator()(ASTDoWhile& ast) {
|
||||||
decoder.Visit(ast.condition);
|
|
||||||
ASTNode current = ast.nodes.GetFirst();
|
ASTNode current = ast.nodes.GetFirst();
|
||||||
while (current) {
|
while (current) {
|
||||||
Visit(current);
|
Visit(current);
|
||||||
|
@ -159,13 +110,9 @@ public:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void operator()(ASTReturn& ast) {
|
void operator()(ASTReturn& ast) {}
|
||||||
decoder.Visit(ast.condition);
|
|
||||||
}
|
|
||||||
|
|
||||||
void operator()(ASTBreak& ast) {
|
void operator()(ASTBreak& ast) {}
|
||||||
decoder.Visit(ast.condition);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Visit(ASTNode& node) {
|
void Visit(ASTNode& node) {
|
||||||
std::visit(*this, *node->GetInnerData());
|
std::visit(*this, *node->GetInnerData());
|
||||||
|
@ -178,113 +125,77 @@ public:
|
||||||
|
|
||||||
private:
|
private:
|
||||||
ShaderIR& ir;
|
ShaderIR& ir;
|
||||||
ExprDecoder decoder;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
void ShaderIR::Decode() {
|
void ShaderIR::Decode() {
|
||||||
const auto decode_function = ([this](ShaderFunction& shader_info) {
|
|
||||||
coverage_end = std::max<u32>(0, shader_info.end);
|
|
||||||
switch (shader_info.settings.depth) {
|
|
||||||
case CompileDepth::FlowStack: {
|
|
||||||
for (const auto& block : shader_info.blocks) {
|
|
||||||
basic_blocks.insert({block.start, DecodeRange(block.start, block.end + 1)});
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case CompileDepth::NoFlowStack: {
|
|
||||||
disable_flow_stack = true;
|
|
||||||
const auto insert_block = [this](NodeBlock& nodes, u32 label) {
|
|
||||||
if (label == static_cast<u32>(exit_branch)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
basic_blocks.insert({label, nodes});
|
|
||||||
};
|
|
||||||
const auto& blocks = shader_info.blocks;
|
|
||||||
NodeBlock current_block;
|
|
||||||
u32 current_label = static_cast<u32>(exit_branch);
|
|
||||||
for (const auto& block : blocks) {
|
|
||||||
if (shader_info.labels.contains(block.start)) {
|
|
||||||
insert_block(current_block, current_label);
|
|
||||||
current_block.clear();
|
|
||||||
current_label = block.start;
|
|
||||||
}
|
|
||||||
if (!block.ignore_branch) {
|
|
||||||
DecodeRangeInner(current_block, block.start, block.end);
|
|
||||||
InsertControlFlow(current_block, block);
|
|
||||||
} else {
|
|
||||||
DecodeRangeInner(current_block, block.start, block.end + 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
insert_block(current_block, current_label);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case CompileDepth::DecompileBackwards:
|
|
||||||
case CompileDepth::FullDecompile: {
|
|
||||||
program_manager = std::move(shader_info.manager);
|
|
||||||
disable_flow_stack = true;
|
|
||||||
decompiled = true;
|
|
||||||
ASTDecoder decoder{*this};
|
|
||||||
ASTNode program = program_manager.GetProgram();
|
|
||||||
decoder.Visit(program);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
LOG_CRITICAL(HW_GPU, "Unknown decompilation mode!");
|
|
||||||
[[fallthrough]];
|
|
||||||
case CompileDepth::BruteForce: {
|
|
||||||
const auto shader_end = static_cast<u32>(program_code.size());
|
|
||||||
coverage_begin = main_offset;
|
|
||||||
coverage_end = shader_end;
|
|
||||||
for (u32 label = main_offset; label < shader_end; ++label) {
|
|
||||||
basic_blocks.insert({label, DecodeRange(label, label + 1)});
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (settings.depth != shader_info.settings.depth) {
|
|
||||||
LOG_WARNING(
|
|
||||||
HW_GPU,
|
|
||||||
"Decompiling to this setting \"{}\" failed, downgrading to this setting \"{}\"",
|
|
||||||
CompileDepthAsString(settings.depth),
|
|
||||||
CompileDepthAsString(shader_info.settings.depth));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
const auto gen_function =
|
|
||||||
([this](ShaderFunction& shader_info, u32 id) -> std::shared_ptr<ShaderFunctionIR> {
|
|
||||||
std::shared_ptr<ShaderFunctionIR> result;
|
|
||||||
if (decompiled) {
|
|
||||||
result = std::make_shared<ShaderFunctionIR>(std::move(program_manager), id,
|
|
||||||
shader_info.start, shader_info.end);
|
|
||||||
} else {
|
|
||||||
result =
|
|
||||||
std::make_shared<ShaderFunctionIR>(std::move(basic_blocks), disable_flow_stack,
|
|
||||||
id, shader_info.start, shader_info.end);
|
|
||||||
}
|
|
||||||
decompiled = false;
|
|
||||||
disable_flow_stack = false;
|
|
||||||
basic_blocks.clear();
|
|
||||||
program_manager.Clear();
|
|
||||||
return result;
|
|
||||||
});
|
|
||||||
std::memcpy(&header, program_code.data(), sizeof(Tegra::Shader::Header));
|
std::memcpy(&header, program_code.data(), sizeof(Tegra::Shader::Header));
|
||||||
|
|
||||||
decompiled = false;
|
decompiled = false;
|
||||||
auto info = ScanFlow(program_code, main_offset, settings, registry);
|
auto info = ScanFlow(program_code, main_offset, settings, registry);
|
||||||
u32 id_start = 1;
|
auto& shader_info = *info;
|
||||||
for (auto& pair : info->subfunctions) {
|
coverage_begin = shader_info.start;
|
||||||
func_map.emplace(pair.first, id_start);
|
coverage_end = shader_info.end;
|
||||||
id_start++;
|
switch (shader_info.settings.depth) {
|
||||||
|
case CompileDepth::FlowStack: {
|
||||||
|
for (const auto& block : shader_info.blocks) {
|
||||||
|
basic_blocks.insert({block.start, DecodeRange(block.start, block.end + 1)});
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
coverage_begin = info->main.start;
|
case CompileDepth::NoFlowStack: {
|
||||||
coverage_end = 0;
|
disable_flow_stack = true;
|
||||||
decode_function(info->main);
|
const auto insert_block = [this](NodeBlock& nodes, u32 label) {
|
||||||
main_function = gen_function(info->main, 0);
|
if (label == static_cast<u32>(exit_branch)) {
|
||||||
subfunctions.resize(info->subfunctions.size());
|
return;
|
||||||
for (auto& pair : info->subfunctions) {
|
}
|
||||||
auto& func_info = pair.second;
|
basic_blocks.insert({label, nodes});
|
||||||
decode_function(func_info);
|
};
|
||||||
u32 id = func_map[pair.first];
|
const auto& blocks = shader_info.blocks;
|
||||||
subfunctions[id - 1] = gen_function(func_info, id);
|
NodeBlock current_block;
|
||||||
|
u32 current_label = static_cast<u32>(exit_branch);
|
||||||
|
for (const auto& block : blocks) {
|
||||||
|
if (shader_info.labels.contains(block.start)) {
|
||||||
|
insert_block(current_block, current_label);
|
||||||
|
current_block.clear();
|
||||||
|
current_label = block.start;
|
||||||
|
}
|
||||||
|
if (!block.ignore_branch) {
|
||||||
|
DecodeRangeInner(current_block, block.start, block.end);
|
||||||
|
InsertControlFlow(current_block, block);
|
||||||
|
} else {
|
||||||
|
DecodeRangeInner(current_block, block.start, block.end + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
insert_block(current_block, current_label);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case CompileDepth::DecompileBackwards:
|
||||||
|
case CompileDepth::FullDecompile: {
|
||||||
|
program_manager = std::move(shader_info.manager);
|
||||||
|
disable_flow_stack = true;
|
||||||
|
decompiled = true;
|
||||||
|
ASTDecoder decoder{*this};
|
||||||
|
ASTNode program = GetASTProgram();
|
||||||
|
decoder.Visit(program);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
LOG_CRITICAL(HW_GPU, "Unknown decompilation mode!");
|
||||||
|
[[fallthrough]];
|
||||||
|
case CompileDepth::BruteForce: {
|
||||||
|
const auto shader_end = static_cast<u32>(program_code.size());
|
||||||
|
coverage_begin = main_offset;
|
||||||
|
coverage_end = shader_end;
|
||||||
|
for (u32 label = main_offset; label < shader_end; ++label) {
|
||||||
|
basic_blocks.insert({label, DecodeRange(label, label + 1)});
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (settings.depth != shader_info.settings.depth) {
|
||||||
|
LOG_WARNING(
|
||||||
|
HW_GPU, "Decompiling to this setting \"{}\" failed, downgrading to this setting \"{}\"",
|
||||||
|
CompileDepthAsString(settings.depth), CompileDepthAsString(shader_info.settings.depth));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -33,7 +33,6 @@ u32 ShaderIR::DecodeOther(NodeBlock& bb, u32 pc) {
|
||||||
// With the previous preconditions, this instruction is a no-operation.
|
// With the previous preconditions, this instruction is a no-operation.
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case OpCode::Id::RET:
|
|
||||||
case OpCode::Id::EXIT: {
|
case OpCode::Id::EXIT: {
|
||||||
const ConditionCode cc = instr.flow_condition_code;
|
const ConditionCode cc = instr.flow_condition_code;
|
||||||
UNIMPLEMENTED_IF_MSG(cc != ConditionCode::T, "EXIT condition code used: {}", cc);
|
UNIMPLEMENTED_IF_MSG(cc != ConditionCode::T, "EXIT condition code used: {}", cc);
|
||||||
|
@ -313,16 +312,6 @@ u32 ShaderIR::DecodeOther(NodeBlock& bb, u32 pc) {
|
||||||
LOG_DEBUG(HW_GPU, "DEPBAR instruction is stubbed");
|
LOG_DEBUG(HW_GPU, "DEPBAR instruction is stubbed");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case OpCode::Id::CAL: {
|
|
||||||
const u32 target = pc + instr.bra.GetBranchTarget();
|
|
||||||
const auto it = func_map.find(target);
|
|
||||||
if (it == func_map.end()) {
|
|
||||||
UNREACHABLE();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
bb.push_back(FunctionCall(it->second));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default:
|
default:
|
||||||
UNIMPLEMENTED_MSG("Unhandled instruction: {}", opcode->get().GetName());
|
UNIMPLEMENTED_MSG("Unhandled instruction: {}", opcode->get().GetName());
|
||||||
}
|
}
|
||||||
|
|
|
@ -339,6 +339,8 @@ u32 ShaderIR::DecodeTexture(NodeBlock& bb, u32 pc) {
|
||||||
const TextureType texture_type{instr.tlds.GetTextureType()};
|
const TextureType texture_type{instr.tlds.GetTextureType()};
|
||||||
const bool is_array{instr.tlds.IsArrayTexture()};
|
const bool is_array{instr.tlds.IsArrayTexture()};
|
||||||
|
|
||||||
|
UNIMPLEMENTED_IF_MSG(instr.tlds.UsesMiscMode(TextureMiscMode::AOFFI),
|
||||||
|
"AOFFI is not implemented");
|
||||||
UNIMPLEMENTED_IF_MSG(instr.tlds.UsesMiscMode(TextureMiscMode::MZ), "MZ is not implemented");
|
UNIMPLEMENTED_IF_MSG(instr.tlds.UsesMiscMode(TextureMiscMode::MZ), "MZ is not implemented");
|
||||||
|
|
||||||
const Node4 components = GetTldsCode(instr, texture_type, is_array);
|
const Node4 components = GetTldsCode(instr, texture_type, is_array);
|
||||||
|
@ -820,7 +822,7 @@ Node4 ShaderIR::GetTldsCode(Instruction instr, TextureType texture_type, bool is
|
||||||
for (std::size_t i = 0; i < type_coord_count; ++i) {
|
for (std::size_t i = 0; i < type_coord_count; ++i) {
|
||||||
const bool last = (i == (type_coord_count - 1)) && (type_coord_count > 1);
|
const bool last = (i == (type_coord_count - 1)) && (type_coord_count > 1);
|
||||||
coords.push_back(
|
coords.push_back(
|
||||||
GetRegister(last && !aoffi_enabled ? last_coord_register : (coord_register + i)));
|
GetRegister(last && !aoffi_enabled ? last_coord_register : coord_register + i));
|
||||||
}
|
}
|
||||||
|
|
||||||
const Node array = is_array ? GetRegister(array_register) : nullptr;
|
const Node array = is_array ? GetRegister(array_register) : nullptr;
|
||||||
|
|
|
@ -267,11 +267,10 @@ class PatchNode;
|
||||||
class SmemNode;
|
class SmemNode;
|
||||||
class GmemNode;
|
class GmemNode;
|
||||||
class CommentNode;
|
class CommentNode;
|
||||||
class FunctionCallNode;
|
|
||||||
|
|
||||||
using NodeData = std::variant<OperationNode, ConditionalNode, GprNode, CustomVarNode, ImmediateNode,
|
using NodeData = std::variant<OperationNode, ConditionalNode, GprNode, CustomVarNode, ImmediateNode,
|
||||||
InternalFlagNode, PredicateNode, AbufNode, PatchNode, CbufNode,
|
InternalFlagNode, PredicateNode, AbufNode, PatchNode, CbufNode,
|
||||||
LmemNode, SmemNode, GmemNode, FunctionCallNode, CommentNode>;
|
LmemNode, SmemNode, GmemNode, CommentNode>;
|
||||||
using Node = std::shared_ptr<NodeData>;
|
using Node = std::shared_ptr<NodeData>;
|
||||||
using Node4 = std::array<Node, 4>;
|
using Node4 = std::array<Node, 4>;
|
||||||
using NodeBlock = std::vector<Node>;
|
using NodeBlock = std::vector<Node>;
|
||||||
|
@ -495,18 +494,6 @@ private:
|
||||||
std::vector<Node> code; ///< Code to execute
|
std::vector<Node> code; ///< Code to execute
|
||||||
};
|
};
|
||||||
|
|
||||||
class FunctionCallNode final : public AmendNode {
|
|
||||||
public:
|
|
||||||
explicit FunctionCallNode(u32 func_id_) : func_id{func_id_} {}
|
|
||||||
|
|
||||||
[[nodiscard]] u32 GetFuncId() const {
|
|
||||||
return func_id;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
u32 func_id; ///< Id of the function to call
|
|
||||||
};
|
|
||||||
|
|
||||||
/// A general purpose register
|
/// A general purpose register
|
||||||
class GprNode final {
|
class GprNode final {
|
||||||
public:
|
public:
|
||||||
|
|
|
@ -19,11 +19,6 @@ Node Comment(std::string text) {
|
||||||
return MakeNode<CommentNode>(std::move(text));
|
return MakeNode<CommentNode>(std::move(text));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a function call
|
|
||||||
Node FunctionCall(u32 func_id) {
|
|
||||||
return MakeNode<FunctionCallNode>(func_id);
|
|
||||||
}
|
|
||||||
|
|
||||||
Node Immediate(u32 value) {
|
Node Immediate(u32 value) {
|
||||||
return MakeNode<ImmediateNode>(value);
|
return MakeNode<ImmediateNode>(value);
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,9 +27,6 @@ Node Conditional(Node condition, std::vector<Node> code);
|
||||||
/// Creates a commentary node
|
/// Creates a commentary node
|
||||||
Node Comment(std::string text);
|
Node Comment(std::string text);
|
||||||
|
|
||||||
/// Creates a function call
|
|
||||||
Node FunctionCall(u32 func_id);
|
|
||||||
|
|
||||||
/// Creates an u32 immediate
|
/// Creates an u32 immediate
|
||||||
Node Immediate(u32 value);
|
Node Immediate(u32 value);
|
||||||
|
|
||||||
|
|
|
@ -26,7 +26,7 @@ namespace VideoCommon::Shader {
|
||||||
|
|
||||||
struct ShaderBlock;
|
struct ShaderBlock;
|
||||||
|
|
||||||
constexpr u32 MAX_PROGRAM_LENGTH = 0x2000;
|
constexpr u32 MAX_PROGRAM_LENGTH = 0x1000;
|
||||||
|
|
||||||
struct ConstBuffer {
|
struct ConstBuffer {
|
||||||
constexpr explicit ConstBuffer(u32 max_offset_, bool is_indirect_)
|
constexpr explicit ConstBuffer(u32 max_offset_, bool is_indirect_)
|
||||||
|
@ -64,68 +64,16 @@ struct GlobalMemoryUsage {
|
||||||
bool is_written{};
|
bool is_written{};
|
||||||
};
|
};
|
||||||
|
|
||||||
class ShaderFunctionIR final {
|
|
||||||
public:
|
|
||||||
explicit ShaderFunctionIR(std::map<u32, NodeBlock>&& basic_blocks_, bool disable_flow_stack_,
|
|
||||||
u32 id_, u32 coverage_begin_, u32 coverage_end_)
|
|
||||||
: basic_blocks{std::move(basic_blocks_)}, decompiled{false},
|
|
||||||
disable_flow_stack{disable_flow_stack_}, id{id_}, coverage_begin{coverage_begin_},
|
|
||||||
coverage_end{coverage_end_} {}
|
|
||||||
explicit ShaderFunctionIR(ASTManager&& program_manager_, u32 id_, u32 coverage_begin_,
|
|
||||||
u32 coverage_end_)
|
|
||||||
: program_manager{std::move(program_manager_)}, decompiled{true}, disable_flow_stack{true},
|
|
||||||
id{id_}, coverage_begin{coverage_begin_}, coverage_end{coverage_end_} {}
|
|
||||||
|
|
||||||
const std::map<u32, NodeBlock>& GetBasicBlocks() const {
|
|
||||||
return basic_blocks;
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] bool IsFlowStackDisabled() const {
|
|
||||||
return disable_flow_stack;
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] bool IsDecompiled() const {
|
|
||||||
return decompiled;
|
|
||||||
}
|
|
||||||
|
|
||||||
const ASTManager& GetASTManager() const {
|
|
||||||
return program_manager;
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] ASTNode GetASTProgram() const {
|
|
||||||
return program_manager.GetProgram();
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] u32 GetASTNumVariables() const {
|
|
||||||
return program_manager.GetVariables();
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] bool IsMain() const {
|
|
||||||
return id == 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] u32 GetId() const {
|
|
||||||
return id;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::map<u32, NodeBlock> basic_blocks;
|
|
||||||
ASTManager program_manager{true, true};
|
|
||||||
|
|
||||||
bool decompiled{};
|
|
||||||
bool disable_flow_stack{};
|
|
||||||
u32 id{};
|
|
||||||
|
|
||||||
u32 coverage_begin{};
|
|
||||||
u32 coverage_end{};
|
|
||||||
};
|
|
||||||
|
|
||||||
class ShaderIR final {
|
class ShaderIR final {
|
||||||
public:
|
public:
|
||||||
explicit ShaderIR(const ProgramCode& program_code_, u32 main_offset_,
|
explicit ShaderIR(const ProgramCode& program_code_, u32 main_offset_,
|
||||||
CompilerSettings settings_, Registry& registry_);
|
CompilerSettings settings_, Registry& registry_);
|
||||||
~ShaderIR();
|
~ShaderIR();
|
||||||
|
|
||||||
|
const std::map<u32, NodeBlock>& GetBasicBlocks() const {
|
||||||
|
return basic_blocks;
|
||||||
|
}
|
||||||
|
|
||||||
const std::set<u32>& GetRegisters() const {
|
const std::set<u32>& GetRegisters() const {
|
||||||
return used_registers;
|
return used_registers;
|
||||||
}
|
}
|
||||||
|
@ -207,6 +155,26 @@ public:
|
||||||
return header;
|
return header;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool IsFlowStackDisabled() const {
|
||||||
|
return disable_flow_stack;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsDecompiled() const {
|
||||||
|
return decompiled;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ASTManager& GetASTManager() const {
|
||||||
|
return program_manager;
|
||||||
|
}
|
||||||
|
|
||||||
|
ASTNode GetASTProgram() const {
|
||||||
|
return program_manager.GetProgram();
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 GetASTNumVariables() const {
|
||||||
|
return program_manager.GetVariables();
|
||||||
|
}
|
||||||
|
|
||||||
u32 ConvertAddressToNvidiaSpace(u32 address) const {
|
u32 ConvertAddressToNvidiaSpace(u32 address) const {
|
||||||
return (address - main_offset) * static_cast<u32>(sizeof(Tegra::Shader::Instruction));
|
return (address - main_offset) * static_cast<u32>(sizeof(Tegra::Shader::Instruction));
|
||||||
}
|
}
|
||||||
|
@ -222,16 +190,7 @@ public:
|
||||||
return num_custom_variables;
|
return num_custom_variables;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<ShaderFunctionIR> GetMainFunction() const {
|
|
||||||
return main_function;
|
|
||||||
}
|
|
||||||
|
|
||||||
const std::vector<std::shared_ptr<ShaderFunctionIR>>& GetSubFunctions() const {
|
|
||||||
return subfunctions;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
friend class ExprDecoder;
|
|
||||||
friend class ASTDecoder;
|
friend class ASTDecoder;
|
||||||
|
|
||||||
struct SamplerInfo {
|
struct SamplerInfo {
|
||||||
|
@ -494,10 +453,6 @@ private:
|
||||||
std::vector<Node> amend_code;
|
std::vector<Node> amend_code;
|
||||||
u32 num_custom_variables{};
|
u32 num_custom_variables{};
|
||||||
|
|
||||||
std::shared_ptr<ShaderFunctionIR> main_function;
|
|
||||||
std::vector<std::shared_ptr<ShaderFunctionIR>> subfunctions;
|
|
||||||
std::unordered_map<u32, u32> func_map;
|
|
||||||
|
|
||||||
std::set<u32> used_registers;
|
std::set<u32> used_registers;
|
||||||
std::set<Tegra::Shader::Pred> used_predicates;
|
std::set<Tegra::Shader::Pred> used_predicates;
|
||||||
std::set<Tegra::Shader::Attribute::Index> used_input_attributes;
|
std::set<Tegra::Shader::Attribute::Index> used_input_attributes;
|
||||||
|
|
Loading…
Reference in a new issue