Compare commits
8
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6e09e59b32 | ||
|
|
4e2b80f37a | ||
|
|
90610bde9b | ||
|
|
8378b8a61f | ||
|
|
6269cd7f1d | ||
|
|
7ad63ea542 | ||
|
|
d28b942458 | ||
|
|
4a7fd91857 |
No files matched your search
@@ -15,6 +15,8 @@ add_library(audio_core STATIC
|
||||
command_generator.cpp
|
||||
command_generator.h
|
||||
common.h
|
||||
delay_line.cpp
|
||||
delay_line.h
|
||||
effect_context.cpp
|
||||
effect_context.h
|
||||
info_updater.cpp
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <cmath>
|
||||
#include <numbers>
|
||||
#include "audio_core/algorithm/interpolate.h"
|
||||
#include "audio_core/command_generator.h"
|
||||
#include "audio_core/effect_context.h"
|
||||
@@ -13,6 +15,20 @@ namespace AudioCore {
|
||||
namespace {
|
||||
constexpr std::size_t MIX_BUFFER_SIZE = 0x3f00;
|
||||
constexpr std::size_t SCALED_MIX_BUFFER_SIZE = MIX_BUFFER_SIZE << 15ULL;
|
||||
using DelayLineTimes = std::array<f32, AudioCommon::I3DL2REVERB_DELAY_LINE_COUNT>;
|
||||
|
||||
constexpr DelayLineTimes FDN_MIN_DELAY_LINE_TIMES{5.0f, 6.0f, 13.0f, 14.0f};
|
||||
constexpr DelayLineTimes FDN_MAX_DELAY_LINE_TIMES{45.704f, 82.782f, 149.94f, 271.58f};
|
||||
constexpr DelayLineTimes DECAY0_MAX_DELAY_LINE_TIMES{17.0f, 13.0f, 9.0f, 7.0f};
|
||||
constexpr DelayLineTimes DECAY1_MAX_DELAY_LINE_TIMES{19.0f, 11.0f, 10.0f, 6.0f};
|
||||
constexpr std::array<f32, AudioCommon::I3DL2REVERB_TAPS> EARLY_TAP_TIMES{
|
||||
0.017136f, 0.059154f, 0.161733f, 0.390186f, 0.425262f, 0.455411f, 0.689737f,
|
||||
0.745910f, 0.833844f, 0.859502f, 0.000000f, 0.075024f, 0.168788f, 0.299901f,
|
||||
0.337443f, 0.371903f, 0.599011f, 0.716741f, 0.817859f, 0.851664f};
|
||||
constexpr std::array<f32, AudioCommon::I3DL2REVERB_TAPS> EARLY_GAIN{
|
||||
0.67096f, 0.61027f, 1.0f, 0.35680f, 0.68361f, 0.65978f, 0.51939f,
|
||||
0.24712f, 0.45945f, 0.45021f, 0.64196f, 0.54879f, 0.92925f, 0.38270f,
|
||||
0.72867f, 0.69794f, 0.5464f, 0.24563f, 0.45214f, 0.44042f};
|
||||
|
||||
template <std::size_t N>
|
||||
void ApplyMix(s32* output, const s32* input, s32 gain, s32 sample_count) {
|
||||
@@ -65,6 +81,154 @@ s32 ApplyMixDepop(s32* output, s32 first_sample, s32 delta, s32 sample_count) {
|
||||
}
|
||||
}
|
||||
|
||||
float Pow10(float x) {
|
||||
if (x >= 0.0f) {
|
||||
return 1.0f;
|
||||
} else if (x <= -5.3f) {
|
||||
return 0.0f;
|
||||
}
|
||||
return std::pow(10.0f, x);
|
||||
}
|
||||
|
||||
float SinD(float degrees) {
|
||||
return std::sin(degrees * std::numbers::pi_v<float> / 180.0f);
|
||||
}
|
||||
|
||||
float CosD(float degrees) {
|
||||
return std::cos(degrees * std::numbers::pi_v<float> / 180.0f);
|
||||
}
|
||||
|
||||
float ToFloat(s32 sample) {
|
||||
return static_cast<float>(sample) / 65536.f;
|
||||
}
|
||||
|
||||
s32 ToS32(float sample) {
|
||||
constexpr auto min = -8388608.0f;
|
||||
constexpr auto max = 8388607.f;
|
||||
float rescaled_sample = sample * 65536.0f;
|
||||
if (rescaled_sample < min) {
|
||||
rescaled_sample = min;
|
||||
}
|
||||
if (rescaled_sample > max) {
|
||||
rescaled_sample = max;
|
||||
}
|
||||
return static_cast<s32>(rescaled_sample);
|
||||
}
|
||||
|
||||
constexpr std::array<std::size_t, 20> REVERB_TAP_INDEX_1CH{0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
|
||||
|
||||
constexpr std::array<std::size_t, 20> REVERB_TAP_INDEX_2CH{0, 0, 0, 1, 1, 1, 1, 0, 0, 0,
|
||||
1, 1, 1, 0, 0, 0, 0, 1, 1, 1};
|
||||
|
||||
constexpr std::array<std::size_t, 20> REVERB_TAP_INDEX_4CH{0, 0, 0, 1, 1, 1, 1, 2, 2, 2,
|
||||
1, 1, 1, 0, 0, 0, 0, 3, 3, 3};
|
||||
|
||||
constexpr std::array<std::size_t, 20> REVERB_TAP_INDEX_6CH{4, 0, 0, 1, 1, 1, 1, 2, 2, 2,
|
||||
1, 1, 1, 0, 0, 0, 0, 3, 3, 3};
|
||||
|
||||
template <std::size_t CHANNEL_COUNT>
|
||||
void ApplyReverbGeneric(I3dl2ReverbState& state,
|
||||
const std::array<const s32*, AudioCommon::MAX_CHANNEL_COUNT>& input,
|
||||
const std::array<s32*, AudioCommon::MAX_CHANNEL_COUNT>& output,
|
||||
s32 sample_count) {
|
||||
|
||||
auto GetTapLookup = []() {
|
||||
if constexpr (CHANNEL_COUNT == 1) {
|
||||
return REVERB_TAP_INDEX_1CH;
|
||||
} else if constexpr (CHANNEL_COUNT == 2) {
|
||||
return REVERB_TAP_INDEX_2CH;
|
||||
} else if constexpr (CHANNEL_COUNT == 4) {
|
||||
return REVERB_TAP_INDEX_4CH;
|
||||
} else if constexpr (CHANNEL_COUNT == 6) {
|
||||
return REVERB_TAP_INDEX_6CH;
|
||||
}
|
||||
};
|
||||
|
||||
const auto& tap_index_lut = GetTapLookup();
|
||||
for (s32 sample = 0; sample < sample_count; sample++) {
|
||||
std::array<f32, CHANNEL_COUNT> out_samples{};
|
||||
std::array<f32, AudioCommon::I3DL2REVERB_DELAY_LINE_COUNT> fsamp{};
|
||||
std::array<f32, AudioCommon::I3DL2REVERB_DELAY_LINE_COUNT> mixed{};
|
||||
std::array<f32, AudioCommon::I3DL2REVERB_DELAY_LINE_COUNT> osamp{};
|
||||
|
||||
// Mix everything into a single sample
|
||||
s32 temp_mixed_sample = 0;
|
||||
for (std::size_t i = 0; i < CHANNEL_COUNT; i++) {
|
||||
temp_mixed_sample += input[i][sample];
|
||||
}
|
||||
const auto current_sample = ToFloat(temp_mixed_sample);
|
||||
const auto early_tap = state.early_delay_line.TapOut(state.early_to_late_taps);
|
||||
|
||||
for (std::size_t i = 0; i < AudioCommon::I3DL2REVERB_TAPS; i++) {
|
||||
const auto tapped_samp =
|
||||
state.early_delay_line.TapOut(state.early_tap_steps[i]) * EARLY_GAIN[i];
|
||||
out_samples[tap_index_lut[i]] += tapped_samp;
|
||||
|
||||
if constexpr (CHANNEL_COUNT == 6) {
|
||||
// handle lfe
|
||||
out_samples[5] += tapped_samp;
|
||||
}
|
||||
}
|
||||
|
||||
state.lowpass_0 = current_sample * state.lowpass_2 + state.lowpass_0 * state.lowpass_1;
|
||||
state.early_delay_line.Tick(state.lowpass_0);
|
||||
|
||||
for (std::size_t i = 0; i < CHANNEL_COUNT; i++) {
|
||||
out_samples[i] *= state.early_gain;
|
||||
}
|
||||
|
||||
// Two channel seems to apply a latet gain, we require to save this
|
||||
f32 filter{};
|
||||
for (std::size_t i = 0; i < AudioCommon::I3DL2REVERB_DELAY_LINE_COUNT; i++) {
|
||||
filter = state.fdn_delay_line[i].GetOutputSample();
|
||||
const auto computed = filter * state.lpf_coefficients[0][i] + state.shelf_filter[i];
|
||||
state.shelf_filter[i] =
|
||||
filter * state.lpf_coefficients[1][i] + computed * state.lpf_coefficients[2][i];
|
||||
fsamp[i] = computed;
|
||||
}
|
||||
|
||||
// Mixing matrix
|
||||
mixed[0] = fsamp[1] + fsamp[2];
|
||||
mixed[1] = -fsamp[0] - fsamp[3];
|
||||
mixed[2] = fsamp[0] - fsamp[3];
|
||||
mixed[3] = fsamp[1] - fsamp[2];
|
||||
|
||||
if constexpr (CHANNEL_COUNT == 2) {
|
||||
for (auto& mix : mixed) {
|
||||
mix *= (filter * state.late_gain);
|
||||
}
|
||||
}
|
||||
|
||||
for (std::size_t i = 0; i < AudioCommon::I3DL2REVERB_DELAY_LINE_COUNT; i++) {
|
||||
const auto late = early_tap * state.late_gain;
|
||||
osamp[i] = state.decay_delay_line0[i].Tick(late + mixed[i]);
|
||||
osamp[i] = state.decay_delay_line1[i].Tick(osamp[i]);
|
||||
state.fdn_delay_line[i].Tick(osamp[i]);
|
||||
}
|
||||
|
||||
if constexpr (CHANNEL_COUNT == 1) {
|
||||
output[0][sample] = ToS32(state.dry_gain * ToFloat(input[0][sample]) +
|
||||
(out_samples[0] + osamp[0] + osamp[1]));
|
||||
} else if constexpr (CHANNEL_COUNT == 2 || CHANNEL_COUNT == 4) {
|
||||
for (std::size_t i = 0; i < CHANNEL_COUNT; i++) {
|
||||
output[i][sample] =
|
||||
ToS32(state.dry_gain * ToFloat(input[i][sample]) + (out_samples[i] + osamp[i]));
|
||||
}
|
||||
} else if constexpr (CHANNEL_COUNT == 6) {
|
||||
const auto temp_center = state.center_delay_line.Tick(0.5f * (osamp[2] - osamp[3]));
|
||||
for (std::size_t i = 0; i < 4; i++) {
|
||||
output[i][sample] =
|
||||
ToS32(state.dry_gain * ToFloat(input[i][sample]) + (out_samples[i] + osamp[i]));
|
||||
}
|
||||
output[4][sample] =
|
||||
ToS32(state.dry_gain * ToFloat(input[4][sample]) + (out_samples[4] + temp_center));
|
||||
output[5][sample] =
|
||||
ToS32(state.dry_gain * ToFloat(input[5][sample]) + (out_samples[5] + osamp[3]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
CommandGenerator::CommandGenerator(AudioCommon::AudioRendererParameter& worker_params_,
|
||||
@@ -271,11 +435,10 @@ void CommandGenerator::GenerateBiquadFilterCommandForVoice(ServerVoiceInfo& voic
|
||||
}
|
||||
|
||||
// Generate biquad filter
|
||||
// GenerateBiquadFilterCommand(mix_buffer_count, biquad_filter,
|
||||
// dsp_state.biquad_filter_state,
|
||||
// mix_buffer_count + channel, mix_buffer_count +
|
||||
// channel, worker_params.sample_count,
|
||||
// voice_info.GetInParams().node_id);
|
||||
// GenerateBiquadFilterCommand(mix_buffer_count, biquad_filter,
|
||||
// dsp_state.biquad_filter_state,
|
||||
// mix_buffer_count + channel, mix_buffer_count + channel,
|
||||
// worker_params.sample_count, voice_info.GetInParams().node_id);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -376,21 +539,54 @@ void CommandGenerator::GenerateEffectCommand(ServerMixInfo& mix_info) {
|
||||
|
||||
void CommandGenerator::GenerateI3dl2ReverbEffectCommand(s32 mix_buffer_offset, EffectBase* info,
|
||||
bool enabled) {
|
||||
if (!enabled) {
|
||||
auto* reverb = dynamic_cast<EffectI3dl2Reverb*>(info);
|
||||
const auto& params = reverb->GetParams();
|
||||
auto& state = reverb->GetState();
|
||||
const auto channel_count = params.channel_count;
|
||||
|
||||
if (channel_count != 1 && channel_count != 2 && channel_count != 4 && channel_count != 6) {
|
||||
return;
|
||||
}
|
||||
const auto& params = dynamic_cast<EffectI3dl2Reverb*>(info)->GetParams();
|
||||
const auto channel_count = params.channel_count;
|
||||
|
||||
std::array<const s32*, AudioCommon::MAX_CHANNEL_COUNT> input{};
|
||||
std::array<s32*, AudioCommon::MAX_CHANNEL_COUNT> output{};
|
||||
|
||||
const auto status = params.status;
|
||||
for (s32 i = 0; i < channel_count; i++) {
|
||||
// TODO(ogniK): Actually implement reverb
|
||||
/*
|
||||
if (params.input[i] != params.output[i]) {
|
||||
const auto* input = GetMixBuffer(mix_buffer_offset + params.input[i]);
|
||||
auto* output = GetMixBuffer(mix_buffer_offset + params.output[i]);
|
||||
ApplyMix<1>(output, input, 32768, worker_params.sample_count);
|
||||
}*/
|
||||
auto* output = GetMixBuffer(mix_buffer_offset + params.output[i]);
|
||||
std::memset(output, 0, worker_params.sample_count * sizeof(s32));
|
||||
input[i] = GetMixBuffer(mix_buffer_offset + params.input[i]);
|
||||
output[i] = GetMixBuffer(mix_buffer_offset + params.output[i]);
|
||||
}
|
||||
|
||||
if (enabled) {
|
||||
if (status == ParameterStatus::Initialized) {
|
||||
InitializeI3dl2Reverb(reverb->GetParams(), state, info->GetWorkBuffer());
|
||||
} else if (status == ParameterStatus::Updating) {
|
||||
UpdateI3dl2Reverb(reverb->GetParams(), state, false);
|
||||
}
|
||||
}
|
||||
|
||||
if (enabled) {
|
||||
switch (channel_count) {
|
||||
case 1:
|
||||
ApplyReverbGeneric<1>(state, input, output, worker_params.sample_count);
|
||||
break;
|
||||
case 2:
|
||||
ApplyReverbGeneric<2>(state, input, output, worker_params.sample_count);
|
||||
break;
|
||||
case 4:
|
||||
ApplyReverbGeneric<4>(state, input, output, worker_params.sample_count);
|
||||
break;
|
||||
case 6:
|
||||
ApplyReverbGeneric<6>(state, input, output, worker_params.sample_count);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
for (s32 i = 0; i < channel_count; i++) {
|
||||
// Only copy if the buffer input and output do not match!
|
||||
if ((mix_buffer_offset + params.input[i]) != (mix_buffer_offset + params.output[i])) {
|
||||
std::memcpy(output[i], input[i], worker_params.sample_count * sizeof(s32));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -528,6 +724,133 @@ s32 CommandGenerator::ReadAuxBuffer(AuxInfoDSP& recv_info, VAddr recv_buffer, u3
|
||||
return sample_count;
|
||||
}
|
||||
|
||||
void CommandGenerator::InitializeI3dl2Reverb(I3dl2ReverbParams& info, I3dl2ReverbState& state,
|
||||
std::vector<u8>& work_buffer) {
|
||||
// Reset state
|
||||
state.lowpass_0 = 0.0f;
|
||||
state.lowpass_1 = 0.0f;
|
||||
state.lowpass_2 = 0.0f;
|
||||
|
||||
state.early_delay_line.Reset();
|
||||
state.early_tap_steps.fill(0);
|
||||
state.early_gain = 0.0f;
|
||||
state.late_gain = 0.0f;
|
||||
state.early_to_late_taps = 0;
|
||||
for (std::size_t i = 0; i < AudioCommon::I3DL2REVERB_DELAY_LINE_COUNT; i++) {
|
||||
state.fdn_delay_line[i].Reset();
|
||||
state.decay_delay_line0[i].Reset();
|
||||
state.decay_delay_line1[i].Reset();
|
||||
}
|
||||
state.last_reverb_echo = 0.0f;
|
||||
state.center_delay_line.Reset();
|
||||
for (auto& coef : state.lpf_coefficients) {
|
||||
coef.fill(0.0f);
|
||||
}
|
||||
state.shelf_filter.fill(0.0f);
|
||||
state.dry_gain = 0.0f;
|
||||
|
||||
const auto sample_rate = info.sample_rate / 1000;
|
||||
f32* work_buffer_ptr = reinterpret_cast<f32*>(work_buffer.data());
|
||||
|
||||
s32 delay_samples{};
|
||||
for (std::size_t i = 0; i < AudioCommon::I3DL2REVERB_DELAY_LINE_COUNT; i++) {
|
||||
delay_samples =
|
||||
AudioCommon::CalculateDelaySamples(sample_rate, FDN_MAX_DELAY_LINE_TIMES[i]);
|
||||
state.fdn_delay_line[i].Initialize(delay_samples, work_buffer_ptr);
|
||||
work_buffer_ptr += delay_samples + 1;
|
||||
|
||||
delay_samples =
|
||||
AudioCommon::CalculateDelaySamples(sample_rate, DECAY0_MAX_DELAY_LINE_TIMES[i]);
|
||||
state.decay_delay_line0[i].Initialize(delay_samples, 0.0f, work_buffer_ptr);
|
||||
work_buffer_ptr += delay_samples + 1;
|
||||
|
||||
delay_samples =
|
||||
AudioCommon::CalculateDelaySamples(sample_rate, DECAY1_MAX_DELAY_LINE_TIMES[i]);
|
||||
state.decay_delay_line1[i].Initialize(delay_samples, 0.0f, work_buffer_ptr);
|
||||
work_buffer_ptr += delay_samples + 1;
|
||||
}
|
||||
delay_samples = AudioCommon::CalculateDelaySamples(sample_rate, 5.0f);
|
||||
state.center_delay_line.Initialize(delay_samples, work_buffer_ptr);
|
||||
work_buffer_ptr += delay_samples + 1;
|
||||
|
||||
delay_samples = AudioCommon::CalculateDelaySamples(sample_rate, 400.0f);
|
||||
state.early_delay_line.Initialize(delay_samples, work_buffer_ptr);
|
||||
|
||||
UpdateI3dl2Reverb(info, state, true);
|
||||
}
|
||||
|
||||
void CommandGenerator::UpdateI3dl2Reverb(I3dl2ReverbParams& info, I3dl2ReverbState& state,
|
||||
bool should_clear) {
|
||||
|
||||
state.dry_gain = info.dry_gain;
|
||||
state.shelf_filter.fill(0.0f);
|
||||
state.lowpass_0 = 0.0f;
|
||||
state.early_gain = Pow10(std::min(info.room + info.reflection, 5000.0f) / 2000.0f);
|
||||
state.late_gain = Pow10(std::min(info.room + info.reverb, 5000.0f) / 2000.0f);
|
||||
|
||||
const auto sample_rate = info.sample_rate / 1000;
|
||||
const f32 hf_gain = Pow10(info.room_hf / 2000.0f);
|
||||
if (hf_gain >= 1.0f) {
|
||||
state.lowpass_2 = 1.0f;
|
||||
state.lowpass_1 = 0.0f;
|
||||
} else {
|
||||
const auto a = 1.0f - hf_gain;
|
||||
const auto b = 2.0f * (1.0f - hf_gain * CosD(256.0f * info.hf_reference /
|
||||
static_cast<f32>(info.sample_rate)));
|
||||
const auto c = std::sqrt(b * b - 4.0f * a * a);
|
||||
|
||||
state.lowpass_1 = (b - c) / (2.0f * a);
|
||||
state.lowpass_2 = 1.0f - state.lowpass_1;
|
||||
}
|
||||
state.early_to_late_taps = AudioCommon::CalculateDelaySamples(
|
||||
sample_rate, 1000.0f * (info.reflection_delay + info.reverb_delay));
|
||||
|
||||
state.last_reverb_echo = 0.6f * info.diffusion * 0.01f;
|
||||
for (std::size_t i = 0; i < AudioCommon::I3DL2REVERB_DELAY_LINE_COUNT; i++) {
|
||||
const auto length =
|
||||
FDN_MIN_DELAY_LINE_TIMES[i] +
|
||||
(info.density / 100.0f) * (FDN_MAX_DELAY_LINE_TIMES[i] - FDN_MIN_DELAY_LINE_TIMES[i]);
|
||||
state.fdn_delay_line[i].SetDelay(AudioCommon::CalculateDelaySamples(sample_rate, length));
|
||||
|
||||
const auto delay_sample_counts = state.fdn_delay_line[i].GetDelay() +
|
||||
state.decay_delay_line0[i].GetDelay() +
|
||||
state.decay_delay_line1[i].GetDelay();
|
||||
|
||||
float a = (-60.0f * static_cast<f32>(delay_sample_counts)) /
|
||||
(info.decay_time * static_cast<f32>(info.sample_rate));
|
||||
float b = a / info.hf_decay_ratio;
|
||||
float c = CosD(128.0f * 0.5f * info.hf_reference / static_cast<f32>(info.sample_rate)) /
|
||||
SinD(128.0f * 0.5f * info.hf_reference / static_cast<f32>(info.sample_rate));
|
||||
float d = Pow10((b - a) / 40.0f);
|
||||
float e = Pow10((b + a) / 40.0f) * 0.7071f;
|
||||
|
||||
state.lpf_coefficients[0][i] = e * ((d * c) + 1.0f) / (c + d);
|
||||
state.lpf_coefficients[1][i] = e * (1.0f - (d * c)) / (c + d);
|
||||
state.lpf_coefficients[2][i] = (c - d) / (c + d);
|
||||
|
||||
state.decay_delay_line0[i].SetCoefficient(state.last_reverb_echo);
|
||||
state.decay_delay_line1[i].SetCoefficient(-0.9f * state.last_reverb_echo);
|
||||
}
|
||||
|
||||
if (should_clear) {
|
||||
for (std::size_t i = 0; i < AudioCommon::I3DL2REVERB_DELAY_LINE_COUNT; i++) {
|
||||
state.fdn_delay_line[i].Clear();
|
||||
state.decay_delay_line0[i].Clear();
|
||||
state.decay_delay_line1[i].Clear();
|
||||
}
|
||||
state.early_delay_line.Clear();
|
||||
state.center_delay_line.Clear();
|
||||
}
|
||||
|
||||
const auto max_early_delay = state.early_delay_line.GetMaxDelay();
|
||||
const auto reflection_time = 1000.0f * (0.0098f * info.reverb_delay + 0.02f);
|
||||
for (std::size_t tap = 0; tap < AudioCommon::I3DL2REVERB_TAPS; tap++) {
|
||||
const auto length = AudioCommon::CalculateDelaySamples(
|
||||
sample_rate, 1000.0f * info.reflection_delay + reflection_time * EARLY_TAP_TIMES[tap]);
|
||||
state.early_tap_steps[tap] = std::min(length, max_early_delay);
|
||||
}
|
||||
}
|
||||
|
||||
void CommandGenerator::GenerateVolumeRampCommand(float last_volume, float current_volume,
|
||||
s32 channel, s32 node_id) {
|
||||
const auto last = static_cast<s32>(last_volume * 32768.0f);
|
||||
|
||||
@@ -21,6 +21,8 @@ class ServerMixInfo;
|
||||
class EffectContext;
|
||||
class EffectBase;
|
||||
struct AuxInfoDSP;
|
||||
struct I3dl2ReverbParams;
|
||||
struct I3dl2ReverbState;
|
||||
using MixVolumeBuffer = std::array<float, AudioCommon::MAX_MIX_BUFFERS>;
|
||||
|
||||
class CommandGenerator {
|
||||
@@ -80,6 +82,9 @@ private:
|
||||
s32 ReadAuxBuffer(AuxInfoDSP& recv_info, VAddr recv_buffer, u32 max_samples, s32* out_data,
|
||||
u32 sample_count, u32 read_offset, u32 read_count);
|
||||
|
||||
void InitializeI3dl2Reverb(I3dl2ReverbParams& info, I3dl2ReverbState& state,
|
||||
std::vector<u8>& work_buffer);
|
||||
void UpdateI3dl2Reverb(I3dl2ReverbParams& info, I3dl2ReverbState& state, bool should_clear);
|
||||
// DSP Code
|
||||
s32 DecodePcm16(ServerVoiceInfo& voice_info, VoiceState& dsp_state, s32 sample_count,
|
||||
s32 channel, std::size_t mix_offset);
|
||||
|
||||
@@ -33,6 +33,29 @@ constexpr std::size_t TEMP_MIX_BASE_SIZE = 0x3f00; // TODO(ogniK): Work out this
|
||||
// and our const ends up being 0x3f04, the 4 bytes are most
|
||||
// likely the sample history
|
||||
constexpr std::size_t TOTAL_TEMP_MIX_SIZE = TEMP_MIX_BASE_SIZE + AudioCommon::MAX_SAMPLE_HISTORY;
|
||||
constexpr f32 I3DL2REVERB_MAX_LEVEL = 5000.0f;
|
||||
constexpr f32 I3DL2REVERB_MIN_REFLECTION_DURATION = 0.02f;
|
||||
constexpr std::size_t I3DL2REVERB_TAPS = 20;
|
||||
constexpr std::size_t I3DL2REVERB_DELAY_LINE_COUNT = 4;
|
||||
using Fractional = s32;
|
||||
|
||||
template <typename T>
|
||||
constexpr Fractional ToFractional(T x) {
|
||||
return static_cast<Fractional>(x * static_cast<T>(0x4000));
|
||||
}
|
||||
|
||||
constexpr Fractional MultiplyFractional(Fractional lhs, Fractional rhs) {
|
||||
return static_cast<Fractional>(static_cast<s64>(lhs) * rhs >> 14);
|
||||
}
|
||||
|
||||
constexpr s32 FractionalToFixed(Fractional x) {
|
||||
const auto s = x & (1 << 13);
|
||||
return static_cast<s32>(x >> 14) + s;
|
||||
}
|
||||
|
||||
constexpr s32 CalculateDelaySamples(s32 sample_rate_khz, float time) {
|
||||
return FractionalToFixed(MultiplyFractional(ToFractional(sample_rate_khz), ToFractional(time)));
|
||||
}
|
||||
|
||||
static constexpr u32 VersionFromRevision(u32_le rev) {
|
||||
// "REV7" -> 7
|
||||
|
||||
@@ -0,0 +1,104 @@
|
||||
#include <cstring>
|
||||
#include "audio_core/delay_line.h"
|
||||
|
||||
namespace AudioCore {
|
||||
DelayLineBase::DelayLineBase() = default;
|
||||
DelayLineBase::~DelayLineBase() = default;
|
||||
|
||||
void DelayLineBase::Initialize(s32 max_delay_, float* src_buffer) {
|
||||
buffer = src_buffer;
|
||||
buffer_end = buffer + max_delay_;
|
||||
max_delay = max_delay_;
|
||||
output = buffer;
|
||||
SetDelay(max_delay_);
|
||||
Clear();
|
||||
}
|
||||
|
||||
void DelayLineBase::SetDelay(s32 new_delay) {
|
||||
if (max_delay < new_delay) {
|
||||
return;
|
||||
}
|
||||
delay = new_delay;
|
||||
input = (buffer + ((output - buffer) + new_delay) % (max_delay + 1));
|
||||
}
|
||||
|
||||
s32 DelayLineBase::GetDelay() const {
|
||||
return delay;
|
||||
}
|
||||
|
||||
s32 DelayLineBase::GetMaxDelay() const {
|
||||
return max_delay;
|
||||
}
|
||||
|
||||
f32 DelayLineBase::TapOut(s32 last_sample) {
|
||||
const float* ptr = input - (last_sample + 1);
|
||||
if (ptr < buffer) {
|
||||
ptr += (max_delay + 1);
|
||||
}
|
||||
|
||||
return *ptr;
|
||||
}
|
||||
|
||||
f32 DelayLineBase::Tick(f32 sample) {
|
||||
*(input++) = sample;
|
||||
const auto out_sample = *(output++);
|
||||
|
||||
if (buffer_end < input) {
|
||||
input = buffer;
|
||||
}
|
||||
|
||||
if (buffer_end < output) {
|
||||
output = buffer;
|
||||
}
|
||||
|
||||
return out_sample;
|
||||
}
|
||||
|
||||
float* DelayLineBase::GetInput() {
|
||||
return input;
|
||||
}
|
||||
|
||||
const float* DelayLineBase::GetInput() const {
|
||||
return input;
|
||||
}
|
||||
|
||||
f32 DelayLineBase::GetOutputSample() const {
|
||||
return *output;
|
||||
}
|
||||
|
||||
void DelayLineBase::Clear() {
|
||||
std::memset(buffer, 0, sizeof(float) * max_delay);
|
||||
}
|
||||
|
||||
void DelayLineBase::Reset() {
|
||||
buffer = nullptr;
|
||||
buffer_end = nullptr;
|
||||
max_delay = 0;
|
||||
input = nullptr;
|
||||
output = nullptr;
|
||||
delay = 0;
|
||||
}
|
||||
|
||||
DelayLineAllPass::DelayLineAllPass() = default;
|
||||
DelayLineAllPass::~DelayLineAllPass() = default;
|
||||
|
||||
void DelayLineAllPass::Initialize(u32 delay_, float coeffcient_, f32* src_buffer) {
|
||||
DelayLineBase::Initialize(delay_, src_buffer);
|
||||
SetCoefficient(coeffcient_);
|
||||
}
|
||||
|
||||
void DelayLineAllPass::SetCoefficient(float coeffcient_) {
|
||||
coefficient = coeffcient_;
|
||||
}
|
||||
|
||||
f32 DelayLineAllPass::Tick(f32 sample) {
|
||||
const auto temp = sample - coefficient * *output;
|
||||
return coefficient * temp + DelayLineBase::Tick(temp);
|
||||
}
|
||||
|
||||
void DelayLineAllPass::Reset() {
|
||||
coefficient = 0.0f;
|
||||
DelayLineBase::Reset();
|
||||
}
|
||||
|
||||
} // namespace AudioCore
|
||||
@@ -0,0 +1,46 @@
|
||||
#pragma once
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace AudioCore {
|
||||
|
||||
class DelayLineBase {
|
||||
public:
|
||||
DelayLineBase();
|
||||
~DelayLineBase();
|
||||
|
||||
void Initialize(s32 max_delay_, float* src_buffer);
|
||||
void SetDelay(s32 new_delay);
|
||||
s32 GetDelay() const;
|
||||
s32 GetMaxDelay() const;
|
||||
f32 TapOut(s32 last_sample);
|
||||
f32 Tick(f32 sample);
|
||||
float* GetInput();
|
||||
const float* GetInput() const;
|
||||
f32 GetOutputSample() const;
|
||||
void Clear();
|
||||
void Reset();
|
||||
|
||||
protected:
|
||||
float* buffer{nullptr};
|
||||
float* buffer_end{nullptr};
|
||||
s32 max_delay{};
|
||||
float* input{nullptr};
|
||||
float* output{nullptr};
|
||||
s32 delay{};
|
||||
};
|
||||
|
||||
class DelayLineAllPass final : public DelayLineBase {
|
||||
public:
|
||||
DelayLineAllPass();
|
||||
~DelayLineAllPass();
|
||||
|
||||
void Initialize(u32 delay, float coeffcient_, f32* src_buffer);
|
||||
void SetCoefficient(float coeffcient_);
|
||||
f32 Tick(f32 sample);
|
||||
void Reset();
|
||||
|
||||
private:
|
||||
float coefficient{};
|
||||
};
|
||||
} // namespace AudioCore
|
||||
@@ -90,6 +90,14 @@ s32 EffectBase::GetProcessingOrder() const {
|
||||
return processing_order;
|
||||
}
|
||||
|
||||
std::vector<u8>& EffectBase::GetWorkBuffer() {
|
||||
return work_buffer;
|
||||
}
|
||||
|
||||
const std::vector<u8>& EffectBase::GetWorkBuffer() const {
|
||||
return work_buffer;
|
||||
}
|
||||
|
||||
EffectI3dl2Reverb::EffectI3dl2Reverb() : EffectGeneric(EffectType::I3dl2Reverb) {}
|
||||
EffectI3dl2Reverb::~EffectI3dl2Reverb() = default;
|
||||
|
||||
@@ -117,6 +125,12 @@ void EffectI3dl2Reverb::Update(EffectInfo::InParams& in_params) {
|
||||
usage = UsageState::Initialized;
|
||||
params.status = ParameterStatus::Initialized;
|
||||
skipped = in_params.buffer_address == 0 || in_params.buffer_size == 0;
|
||||
if (!skipped) {
|
||||
auto& cur_work_buffer = GetWorkBuffer();
|
||||
// Has two buffers internally
|
||||
cur_work_buffer.resize(in_params.buffer_size * 2);
|
||||
std::fill(cur_work_buffer.begin(), cur_work_buffer.end(), 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -129,6 +143,14 @@ void EffectI3dl2Reverb::UpdateForCommandGeneration() {
|
||||
GetParams().status = ParameterStatus::Updated;
|
||||
}
|
||||
|
||||
I3dl2ReverbState& EffectI3dl2Reverb::GetState() {
|
||||
return state;
|
||||
}
|
||||
|
||||
const I3dl2ReverbState& EffectI3dl2Reverb::GetState() const {
|
||||
return state;
|
||||
}
|
||||
|
||||
EffectBiquadFilter::EffectBiquadFilter() : EffectGeneric(EffectType::BiquadFilter) {}
|
||||
EffectBiquadFilter::~EffectBiquadFilter() = default;
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include "audio_core/common.h"
|
||||
#include "audio_core/delay_line.h"
|
||||
#include "common/common_funcs.h"
|
||||
#include "common/common_types.h"
|
||||
#include "common/swap.h"
|
||||
@@ -194,6 +195,8 @@ public:
|
||||
[[nodiscard]] bool IsEnabled() const;
|
||||
[[nodiscard]] s32 GetMixID() const;
|
||||
[[nodiscard]] s32 GetProcessingOrder() const;
|
||||
[[nodiscard]] std::vector<u8>& GetWorkBuffer();
|
||||
[[nodiscard]] const std::vector<u8>& GetWorkBuffer() const;
|
||||
|
||||
protected:
|
||||
UsageState usage{UsageState::Invalid};
|
||||
@@ -201,6 +204,7 @@ protected:
|
||||
s32 mix_id{};
|
||||
s32 processing_order{};
|
||||
bool enabled = false;
|
||||
std::vector<u8> work_buffer{};
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
@@ -212,7 +216,7 @@ public:
|
||||
return internal_params;
|
||||
}
|
||||
|
||||
const I3dl2ReverbParams& GetParams() const {
|
||||
const T& GetParams() const {
|
||||
return internal_params;
|
||||
}
|
||||
|
||||
@@ -229,6 +233,27 @@ public:
|
||||
void UpdateForCommandGeneration() override;
|
||||
};
|
||||
|
||||
struct I3dl2ReverbState {
|
||||
f32 lowpass_0{};
|
||||
f32 lowpass_1{};
|
||||
f32 lowpass_2{};
|
||||
|
||||
DelayLineBase early_delay_line{};
|
||||
std::array<u32, AudioCommon::I3DL2REVERB_TAPS> early_tap_steps{};
|
||||
f32 early_gain{};
|
||||
f32 late_gain{};
|
||||
|
||||
u32 early_to_late_taps{};
|
||||
std::array<DelayLineBase, AudioCommon::I3DL2REVERB_DELAY_LINE_COUNT> fdn_delay_line{};
|
||||
std::array<DelayLineAllPass, AudioCommon::I3DL2REVERB_DELAY_LINE_COUNT> decay_delay_line0{};
|
||||
std::array<DelayLineAllPass, AudioCommon::I3DL2REVERB_DELAY_LINE_COUNT> decay_delay_line1{};
|
||||
f32 last_reverb_echo{};
|
||||
DelayLineBase center_delay_line{};
|
||||
std::array<std::array<f32, AudioCommon::I3DL2REVERB_DELAY_LINE_COUNT>, 3> lpf_coefficients{};
|
||||
std::array<f32, AudioCommon::I3DL2REVERB_DELAY_LINE_COUNT> shelf_filter{};
|
||||
f32 dry_gain{};
|
||||
};
|
||||
|
||||
class EffectI3dl2Reverb : public EffectGeneric<I3dl2ReverbParams> {
|
||||
public:
|
||||
explicit EffectI3dl2Reverb();
|
||||
@@ -237,8 +262,12 @@ public:
|
||||
void Update(EffectInfo::InParams& in_params) override;
|
||||
void UpdateForCommandGeneration() override;
|
||||
|
||||
I3dl2ReverbState& GetState();
|
||||
const I3dl2ReverbState& GetState() const;
|
||||
|
||||
private:
|
||||
bool skipped = false;
|
||||
I3dl2ReverbState state{};
|
||||
};
|
||||
|
||||
class EffectBiquadFilter : public EffectGeneric<BiquadFilterParams> {
|
||||
|
||||
+9
-12
@@ -29,22 +29,19 @@ assert_noinline_call(const Fn& fn) {
|
||||
}
|
||||
|
||||
#define ASSERT(_a_) \
|
||||
do \
|
||||
if (!(_a_)) { \
|
||||
assert_noinline_call([] { LOG_CRITICAL(Debug, "Assertion Failed!"); }); \
|
||||
} \
|
||||
while (0)
|
||||
if (!(_a_)) { \
|
||||
LOG_CRITICAL(Debug, "Assertion Failed!"); \
|
||||
}
|
||||
|
||||
#define ASSERT_MSG(_a_, ...) \
|
||||
do \
|
||||
if (!(_a_)) { \
|
||||
assert_noinline_call([&] { LOG_CRITICAL(Debug, "Assertion Failed!\n" __VA_ARGS__); }); \
|
||||
} \
|
||||
while (0)
|
||||
if (!(_a_)) { \
|
||||
LOG_CRITICAL(Debug, "Assertion Failed! " __VA_ARGS__); \
|
||||
}
|
||||
|
||||
#define UNREACHABLE() assert_noinline_call([] { LOG_CRITICAL(Debug, "Unreachable code!"); })
|
||||
#define UNREACHABLE() \
|
||||
{ LOG_CRITICAL(Debug, "Unreachable code!"); }
|
||||
#define UNREACHABLE_MSG(...) \
|
||||
assert_noinline_call([&] { LOG_CRITICAL(Debug, "Unreachable code!\n" __VA_ARGS__); })
|
||||
{ LOG_CRITICAL(Debug, "Unreachable code!\n" __VA_ARGS__); }
|
||||
|
||||
#ifdef _DEBUG
|
||||
#define DEBUG_ASSERT(_a_) ASSERT(_a_)
|
||||
|
||||
@@ -1785,6 +1785,8 @@ public:
|
||||
SSY,
|
||||
SYNC,
|
||||
BRK,
|
||||
CAL,
|
||||
RET,
|
||||
DEPBAR,
|
||||
VOTE,
|
||||
VOTE_VTG,
|
||||
@@ -2108,6 +2110,8 @@ private:
|
||||
INST("1111000011111---", Id::SYNC, Type::Flow, "SYNC"),
|
||||
INST("111000110100----", Id::BRK, Type::Flow, "BRK"),
|
||||
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("0101000011011---", Id::VOTE, Type::Warp, "VOTE"),
|
||||
INST("0101000011100---", Id::VOTE_VTG, Type::Warp, "VOTE_VTG"),
|
||||
|
||||
@@ -491,6 +491,9 @@ private:
|
||||
const Registry& registry;
|
||||
const ShaderType stage;
|
||||
|
||||
std::shared_ptr<ShaderFunctionIR> context_func;
|
||||
u32 ast_var_base{};
|
||||
|
||||
std::size_t num_temporaries = 0;
|
||||
std::size_t max_temporaries = 0;
|
||||
|
||||
@@ -807,13 +810,33 @@ ARBDecompiler::ARBDecompiler(const Device& device_, const ShaderIR& ir_, const R
|
||||
: device{device_}, ir{ir_}, registry{registry_}, stage{stage_} {
|
||||
DefineGlobalMemory();
|
||||
|
||||
context_func = ir.GetMainFunction();
|
||||
ast_var_base = 0;
|
||||
|
||||
AddLine("TEMP RC;");
|
||||
AddLine("TEMP FSWZA[4];");
|
||||
AddLine("TEMP FSWZB[4];");
|
||||
if (ir.IsDecompiled()) {
|
||||
InitializeVariables();
|
||||
AddLine("main:");
|
||||
if (context_func->IsDecompiled()) {
|
||||
DecompileAST();
|
||||
} else {
|
||||
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");
|
||||
|
||||
@@ -1060,41 +1083,38 @@ void ARBDecompiler::InitializeVariables() {
|
||||
}
|
||||
|
||||
void ARBDecompiler::DecompileAST() {
|
||||
const u32 num_flow_variables = ir.GetASTNumVariables();
|
||||
const u32 num_flow_variables = context_func->GetASTNumVariables();
|
||||
for (u32 i = 0; i < num_flow_variables; ++i) {
|
||||
AddLine("TEMP F{};", i);
|
||||
AddLine("TEMP F{};", i + ast_var_base);
|
||||
}
|
||||
for (u32 i = 0; i < num_flow_variables; ++i) {
|
||||
AddLine("MOV.U F{}, {{0, 0, 0, 0}};", i);
|
||||
AddLine("MOV.U F{}, {{0, 0, 0, 0}};", i + ast_var_base);
|
||||
}
|
||||
|
||||
InitializeVariables();
|
||||
|
||||
VisitAST(ir.GetASTProgram());
|
||||
VisitAST(context_func->GetASTProgram());
|
||||
ast_var_base += num_flow_variables;
|
||||
}
|
||||
|
||||
void ARBDecompiler::DecompileBranchMode() {
|
||||
static constexpr u32 FLOW_STACK_SIZE = 20;
|
||||
if (!ir.IsFlowStackDisabled()) {
|
||||
if (!context_func->IsFlowStackDisabled()) {
|
||||
AddLine("TEMP SSY[{}];", FLOW_STACK_SIZE);
|
||||
AddLine("TEMP PBK[{}];", FLOW_STACK_SIZE);
|
||||
AddLine("TEMP SSY_TOP;");
|
||||
AddLine("TEMP PBK_TOP;");
|
||||
}
|
||||
|
||||
AddLine("TEMP PC;");
|
||||
AddLine("TEMP PC{};", context_func->GetId());
|
||||
|
||||
if (!ir.IsFlowStackDisabled()) {
|
||||
if (!context_func->IsFlowStackDisabled()) {
|
||||
AddLine("MOV.U SSY_TOP.x, 0;");
|
||||
AddLine("MOV.U PBK_TOP.x, 0;");
|
||||
}
|
||||
|
||||
InitializeVariables();
|
||||
|
||||
const auto basic_block_end = ir.GetBasicBlocks().end();
|
||||
auto basic_block_it = ir.GetBasicBlocks().begin();
|
||||
const auto basic_block_end = context_func->GetBasicBlocks().end();
|
||||
auto basic_block_it = context_func->GetBasicBlocks().begin();
|
||||
const u32 first_address = basic_block_it->first;
|
||||
AddLine("MOV.U PC.x, {};", first_address);
|
||||
AddLine("MOV.U PC{}.x, {};", context_func->GetId(), first_address);
|
||||
|
||||
AddLine("REP;");
|
||||
|
||||
@@ -1103,7 +1123,7 @@ void ARBDecompiler::DecompileBranchMode() {
|
||||
const auto& [address, bb] = *basic_block_it;
|
||||
++num_blocks;
|
||||
|
||||
AddLine("SEQ.S.CC RC.x, PC.x, {};", address);
|
||||
AddLine("SEQ.S.CC RC.x, PC{}.x, {};", context_func->GetId(), address);
|
||||
AddLine("IF NE.x;");
|
||||
|
||||
VisitBlock(bb);
|
||||
@@ -1114,7 +1134,7 @@ void ARBDecompiler::DecompileBranchMode() {
|
||||
const auto op = std::get_if<OperationNode>(&*bb[bb.size() - 1]);
|
||||
if (!op || op->GetCode() != OperationCode::Branch) {
|
||||
const u32 next_address = basic_block_it->first;
|
||||
AddLine("MOV.U PC.x, {};", next_address);
|
||||
AddLine("MOV.U PC{}.x, {};", context_func->GetId(), next_address);
|
||||
AddLine("CONT;");
|
||||
}
|
||||
}
|
||||
@@ -1152,7 +1172,8 @@ void ARBDecompiler::VisitAST(const ASTNode& node) {
|
||||
} else if (const auto decoded = std::get_if<ASTBlockDecoded>(&*node->GetInnerData())) {
|
||||
VisitBlock(decoded->nodes);
|
||||
} else if (const auto var_set = std::get_if<ASTVarSet>(&*node->GetInnerData())) {
|
||||
AddLine("MOV.U F{}, {};", var_set->index, VisitExpression(var_set->condition));
|
||||
AddLine("MOV.U F{}, {};", var_set->index + ast_var_base,
|
||||
VisitExpression(var_set->condition));
|
||||
ResetTemporaries();
|
||||
} else if (const auto do_while = std::get_if<ASTDoWhile>(&*node->GetInnerData())) {
|
||||
const std::string condition = VisitExpression(do_while->condition);
|
||||
@@ -1172,7 +1193,11 @@ void ARBDecompiler::VisitAST(const ASTNode& node) {
|
||||
ResetTemporaries();
|
||||
}
|
||||
if (ast_return->kills) {
|
||||
AddLine("KIL TR;");
|
||||
if (stage == ShaderType::Fragment) {
|
||||
AddLine("KIL TR;");
|
||||
} else {
|
||||
AddLine("RET;");
|
||||
}
|
||||
} else {
|
||||
Exit();
|
||||
}
|
||||
@@ -1219,7 +1244,7 @@ std::string ARBDecompiler::VisitExpression(const Expr& node) {
|
||||
return Visit(ir.GetConditionCode(expr->cc));
|
||||
}
|
||||
if (const auto expr = std::get_if<ExprVar>(&*node)) {
|
||||
return fmt::format("F{}.x", expr->var_index);
|
||||
return fmt::format("F{}.x", expr->var_index + ast_var_base);
|
||||
}
|
||||
if (const auto expr = std::get_if<ExprBoolean>(&*node)) {
|
||||
return expr->value ? "0xffffffff" : "0";
|
||||
@@ -1406,6 +1431,11 @@ std::string ARBDecompiler::Visit(const Node& node) {
|
||||
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)) {
|
||||
// Uncommenting this will generate invalid code. GLASM lacks comments.
|
||||
// AddLine("// {}", cmt->GetText());
|
||||
@@ -1479,7 +1509,7 @@ std::string ARBDecompiler::GlobalMemoryPointer(const GmemNode& gmem) {
|
||||
}
|
||||
|
||||
void ARBDecompiler::Exit() {
|
||||
if (stage != ShaderType::Fragment) {
|
||||
if (!context_func->IsMain() || stage != ShaderType::Fragment) {
|
||||
AddLine("RET;");
|
||||
return;
|
||||
}
|
||||
@@ -2021,13 +2051,13 @@ std::string ARBDecompiler::ImageStore(Operation operation) {
|
||||
|
||||
std::string ARBDecompiler::Branch(Operation operation) {
|
||||
const auto target = std::get<ImmediateNode>(*operation[0]);
|
||||
AddLine("MOV.U PC.x, {};", target.GetValue());
|
||||
AddLine("MOV.U PC{}.x, {};", context_func->GetId(), target.GetValue());
|
||||
AddLine("CONT;");
|
||||
return {};
|
||||
}
|
||||
|
||||
std::string ARBDecompiler::BranchIndirect(Operation operation) {
|
||||
AddLine("MOV.U PC.x, {};", Visit(operation[0]));
|
||||
AddLine("MOV.U PC{}.x, {};", context_func->GetId(), Visit(operation[0]));
|
||||
AddLine("CONT;");
|
||||
return {};
|
||||
}
|
||||
@@ -2045,7 +2075,7 @@ std::string ARBDecompiler::PopFlowStack(Operation operation) {
|
||||
const auto stack = std::get<MetaStackClass>(operation.GetMeta());
|
||||
const std::string_view stack_name = StackName(stack);
|
||||
AddLine("SUB.S {}_TOP.x, {}_TOP.x, 1;", stack_name, stack_name);
|
||||
AddLine("MOV.U PC.x, {}[{}_TOP.x].x;", stack_name, stack_name);
|
||||
AddLine("MOV.U PC{}.x, {}[{}_TOP.x].x;", context_func->GetId(), stack_name, stack_name);
|
||||
AddLine("CONT;");
|
||||
return {};
|
||||
}
|
||||
@@ -2056,6 +2086,10 @@ std::string ARBDecompiler::Exit(Operation) {
|
||||
}
|
||||
|
||||
std::string ARBDecompiler::Discard(Operation) {
|
||||
if (stage != ShaderType::Fragment) {
|
||||
AddLine("RET;");
|
||||
return {};
|
||||
}
|
||||
AddLine("KIL TR;");
|
||||
return {};
|
||||
}
|
||||
|
||||
@@ -435,6 +435,27 @@ public:
|
||||
DeclareCustomVariables();
|
||||
DeclarePhysicalAttributeReader();
|
||||
|
||||
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.scope;
|
||||
|
||||
@@ -442,7 +463,7 @@ public:
|
||||
code.AddLine("gl_Position = vec4(0.0f, 0.0f, 0.0f, 1.0f);");
|
||||
}
|
||||
|
||||
if (ir.IsDecompiled()) {
|
||||
if (context_func->IsDecompiled()) {
|
||||
DecompileAST();
|
||||
} else {
|
||||
DecompileBranchMode();
|
||||
@@ -462,13 +483,13 @@ private:
|
||||
|
||||
void DecompileBranchMode() {
|
||||
// VM's program counter
|
||||
const auto first_address = ir.GetBasicBlocks().begin()->first;
|
||||
const auto first_address = context_func->GetBasicBlocks().begin()->first;
|
||||
code.AddLine("uint jmp_to = {}U;", first_address);
|
||||
|
||||
// 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.
|
||||
constexpr u32 FLOW_STACK_SIZE = 20;
|
||||
if (!ir.IsFlowStackDisabled()) {
|
||||
if (!context_func->IsFlowStackDisabled()) {
|
||||
for (const auto stack : std::array{MetaStackClass::Ssy, MetaStackClass::Pbk}) {
|
||||
code.AddLine("uint {}[{}];", FlowStackName(stack), FLOW_STACK_SIZE);
|
||||
code.AddLine("uint {} = 0U;", FlowStackTopName(stack));
|
||||
@@ -480,7 +501,7 @@ private:
|
||||
|
||||
code.AddLine("switch (jmp_to) {{");
|
||||
|
||||
for (const auto& pair : ir.GetBasicBlocks()) {
|
||||
for (const auto& pair : context_func->GetBasicBlocks()) {
|
||||
const auto& [address, bb] = pair;
|
||||
code.AddLine("case 0x{:X}U: {{", address);
|
||||
++code.scope;
|
||||
@@ -1131,6 +1152,11 @@ private:
|
||||
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)) {
|
||||
code.AddLine("// " + comment->GetText());
|
||||
return {};
|
||||
@@ -2267,7 +2293,9 @@ private:
|
||||
}
|
||||
|
||||
Expression Exit(Operation operation) {
|
||||
PreExit();
|
||||
if (context_func->IsMain()) {
|
||||
PreExit();
|
||||
}
|
||||
code.AddLine("return;");
|
||||
return {};
|
||||
}
|
||||
@@ -2277,7 +2305,11 @@ private:
|
||||
// about unexecuted instructions that may follow this.
|
||||
code.AddLine("if (true) {{");
|
||||
++code.scope;
|
||||
code.AddLine("discard;");
|
||||
if (stage != ShaderType::Fragment) {
|
||||
code.AddLine("return;");
|
||||
} else {
|
||||
code.AddLine("discard;");
|
||||
}
|
||||
--code.scope;
|
||||
code.AddLine("}}");
|
||||
return {};
|
||||
@@ -2388,7 +2420,7 @@ private:
|
||||
}
|
||||
|
||||
Expression Barrier(Operation) {
|
||||
if (!ir.IsDecompiled()) {
|
||||
if (!context_func->IsDecompiled()) {
|
||||
LOG_ERROR(Render_OpenGL, "barrier() used but shader is not decompiled");
|
||||
return {};
|
||||
}
|
||||
@@ -2755,6 +2787,8 @@ private:
|
||||
const Header header;
|
||||
std::unordered_map<u8, VaryingTFB> transform_feedback;
|
||||
|
||||
std::shared_ptr<ShaderFunctionIR> context_func;
|
||||
|
||||
ShaderWriter code;
|
||||
|
||||
std::optional<u32> max_input_vertices;
|
||||
@@ -2902,9 +2936,15 @@ public:
|
||||
decomp.code.scope++;
|
||||
}
|
||||
if (ast.kills) {
|
||||
decomp.code.AddLine("discard;");
|
||||
if (decomp.stage != ShaderType::Fragment) {
|
||||
decomp.code.AddLine("return;");
|
||||
} else {
|
||||
decomp.code.AddLine("discard;");
|
||||
}
|
||||
} else {
|
||||
decomp.PreExit();
|
||||
if (decomp.context_func->IsMain()) {
|
||||
decomp.PreExit();
|
||||
}
|
||||
decomp.code.AddLine("return;");
|
||||
}
|
||||
if (!is_true) {
|
||||
@@ -2937,13 +2977,13 @@ private:
|
||||
};
|
||||
|
||||
void GLSLDecompiler::DecompileAST() {
|
||||
const u32 num_flow_variables = ir.GetASTNumVariables();
|
||||
const u32 num_flow_variables = context_func->GetASTNumVariables();
|
||||
for (u32 i = 0; i < num_flow_variables; i++) {
|
||||
code.AddLine("bool {} = false;", GetFlowVariable(i));
|
||||
}
|
||||
|
||||
ASTDecompiler decompiler{*this};
|
||||
decompiler.Visit(ir.GetASTProgram());
|
||||
decompiler.Visit(context_func->GetASTProgram());
|
||||
}
|
||||
|
||||
} // Anonymous namespace
|
||||
|
||||
@@ -406,10 +406,38 @@ private:
|
||||
binding = DeclareStorageTexels(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));
|
||||
AddLabel();
|
||||
|
||||
if (ir.IsDecompiled()) {
|
||||
if (context_func->IsDecompiled()) {
|
||||
DeclareFlowVariables();
|
||||
DecompileAST();
|
||||
} else {
|
||||
@@ -441,16 +469,18 @@ private:
|
||||
void DecompileAST();
|
||||
|
||||
void DecompileBranchMode() {
|
||||
const u32 first_address = ir.GetBasicBlocks().begin()->first;
|
||||
const Id loop_label = OpLabel("loop");
|
||||
const Id merge_label = OpLabel("merge");
|
||||
const u32 first_address = context_func->GetBasicBlocks().begin()->first;
|
||||
const u32 func_id = context_func->GetId();
|
||||
const std::string func_id_msg = std::to_string(func_id);
|
||||
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 jump_label = OpLabel();
|
||||
continue_label = OpLabel("continue");
|
||||
continue_label = OpLabel("continue_" + func_id_msg);
|
||||
|
||||
std::vector<Sirit::Literal> literals;
|
||||
std::vector<Id> branch_labels;
|
||||
for (const auto& [literal, label] : labels) {
|
||||
for (const auto& [literal, label] : labels[func_id]) {
|
||||
literals.push_back(literal);
|
||||
branch_labels.push_back(label);
|
||||
}
|
||||
@@ -462,11 +492,11 @@ private:
|
||||
std::tie(ssy_flow_stack, ssy_flow_stack_top) = CreateFlowStack();
|
||||
std::tie(pbk_flow_stack, pbk_flow_stack_top) = CreateFlowStack();
|
||||
|
||||
Name(jmp_to, "jmp_to");
|
||||
Name(ssy_flow_stack, "ssy_flow_stack");
|
||||
Name(ssy_flow_stack_top, "ssy_flow_stack_top");
|
||||
Name(pbk_flow_stack, "pbk_flow_stack");
|
||||
Name(pbk_flow_stack_top, "pbk_flow_stack_top");
|
||||
Name(jmp_to, "jmp_to_" + func_id_msg);
|
||||
Name(ssy_flow_stack, "ssy_flow_stack_" + func_id_msg);
|
||||
Name(ssy_flow_stack_top, "ssy_flow_stack_top_" + func_id_msg);
|
||||
Name(pbk_flow_stack, "pbk_flow_stack_" + func_id_msg);
|
||||
Name(pbk_flow_stack_top, "pbk_flow_stack_top_" + func_id_msg);
|
||||
|
||||
DefinePrologue();
|
||||
|
||||
@@ -484,13 +514,14 @@ private:
|
||||
AddLabel(default_branch);
|
||||
OpReturn();
|
||||
|
||||
for (const auto& [address, bb] : ir.GetBasicBlocks()) {
|
||||
AddLabel(labels.at(address));
|
||||
for (const auto& [address, bb] : context_func->GetBasicBlocks()) {
|
||||
AddLabel(labels[func_id].at(address));
|
||||
|
||||
VisitBasicBlock(bb);
|
||||
|
||||
const auto next_it = labels.lower_bound(address + 1);
|
||||
const Id next_label = next_it != labels.end() ? next_it->second : default_branch;
|
||||
const auto next_it = labels[func_id].lower_bound(address + 1);
|
||||
const Id next_label =
|
||||
next_it != labels[func_id].end() ? next_it->second : default_branch;
|
||||
OpBranch(next_label);
|
||||
}
|
||||
|
||||
@@ -508,9 +539,10 @@ private:
|
||||
static constexpr auto INTERNAL_FLAGS_COUNT = static_cast<std::size_t>(InternalFlag::Amount);
|
||||
|
||||
void AllocateLabels() {
|
||||
for (const auto& pair : ir.GetBasicBlocks()) {
|
||||
const u32 func_id = context_func->GetId();
|
||||
for (const auto& pair : context_func->GetBasicBlocks()) {
|
||||
const u32 address = pair.first;
|
||||
labels.emplace(address, OpLabel(fmt::format("label_0x{:x}", address)));
|
||||
labels[func_id].emplace(address, OpLabel(fmt::format("label_0x{:x}", address)));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -589,6 +621,14 @@ private:
|
||||
DeclareOutputVertex();
|
||||
}
|
||||
|
||||
void SafeKill() {
|
||||
if (stage != ShaderType::Fragment) {
|
||||
OpReturn();
|
||||
return;
|
||||
}
|
||||
OpKill();
|
||||
}
|
||||
|
||||
void DeclareFragment() {
|
||||
if (stage != ShaderType::Fragment) {
|
||||
return;
|
||||
@@ -656,7 +696,7 @@ private:
|
||||
}
|
||||
|
||||
void DeclareFlowVariables() {
|
||||
for (u32 i = 0; i < ir.GetASTNumVariables(); i++) {
|
||||
for (u32 i = 0; i < context_func->GetASTNumVariables(); i++) {
|
||||
const Id id = OpVariable(t_prv_bool, spv::StorageClass::Private, v_false);
|
||||
Name(id, fmt::format("flow_var_{}", static_cast<u32>(i)));
|
||||
flow_variables.emplace(i, AddGlobalVariable(id));
|
||||
@@ -1333,6 +1373,12 @@ private:
|
||||
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 (device.HasDebuggingToolAttached()) {
|
||||
// We should insert comments with OpString instead of using named variables
|
||||
@@ -2107,7 +2153,7 @@ private:
|
||||
|
||||
OpBranchConditional(condition, true_label, discard_label);
|
||||
AddLabel(discard_label);
|
||||
OpKill();
|
||||
SafeKill();
|
||||
AddLabel(true_label);
|
||||
}
|
||||
|
||||
@@ -2158,7 +2204,9 @@ private:
|
||||
}
|
||||
|
||||
Expression Exit(Operation operation) {
|
||||
PreExit();
|
||||
if (context_func->IsMain()) {
|
||||
PreExit();
|
||||
}
|
||||
inside_branch = true;
|
||||
if (conditional_branch_set) {
|
||||
OpReturn();
|
||||
@@ -2175,12 +2223,12 @@ private:
|
||||
Expression Discard(Operation operation) {
|
||||
inside_branch = true;
|
||||
if (conditional_branch_set) {
|
||||
OpKill();
|
||||
SafeKill();
|
||||
} else {
|
||||
const Id dummy = OpLabel();
|
||||
OpBranch(dummy);
|
||||
AddLabel(dummy);
|
||||
OpKill();
|
||||
SafeKill();
|
||||
AddLabel();
|
||||
}
|
||||
return {};
|
||||
@@ -2259,7 +2307,7 @@ private:
|
||||
}
|
||||
|
||||
Expression Barrier(Operation) {
|
||||
if (!ir.IsDecompiled()) {
|
||||
if (!context_func->IsDecompiled()) {
|
||||
LOG_ERROR(Render_Vulkan, "OpBarrier used by shader is not decompiled");
|
||||
return {};
|
||||
}
|
||||
@@ -2753,6 +2801,8 @@ private:
|
||||
const Specialization& specialization;
|
||||
std::unordered_map<u8, VaryingTFB> transform_feedback;
|
||||
|
||||
std::shared_ptr<ShaderFunctionIR> context_func;
|
||||
|
||||
const Id t_void = Name(TypeVoid(), "void");
|
||||
|
||||
const Id t_bool = Name(TypeBool(), "bool");
|
||||
@@ -2879,7 +2929,8 @@ private:
|
||||
Id ssy_flow_stack{};
|
||||
Id pbk_flow_stack{};
|
||||
Id continue_label{};
|
||||
std::map<u32, Id> labels;
|
||||
std::vector<std::map<u32, Id>> labels;
|
||||
std::vector<Id> other_functions;
|
||||
|
||||
bool conditional_branch_set{};
|
||||
bool inside_branch{};
|
||||
@@ -3030,9 +3081,11 @@ public:
|
||||
decomp.OpBranchConditional(condition, then_label, endif_label);
|
||||
decomp.AddLabel(then_label);
|
||||
if (ast.kills) {
|
||||
decomp.OpKill();
|
||||
decomp.SafeKill();
|
||||
} else {
|
||||
decomp.PreExit();
|
||||
if (decomp.context_func->IsMain()) {
|
||||
decomp.PreExit();
|
||||
}
|
||||
decomp.OpReturn();
|
||||
}
|
||||
decomp.AddLabel(endif_label);
|
||||
@@ -3041,9 +3094,11 @@ public:
|
||||
decomp.OpBranch(next_block);
|
||||
decomp.AddLabel(next_block);
|
||||
if (ast.kills) {
|
||||
decomp.OpKill();
|
||||
decomp.SafeKill();
|
||||
} else {
|
||||
decomp.PreExit();
|
||||
if (decomp.context_func->IsMain()) {
|
||||
decomp.PreExit();
|
||||
}
|
||||
decomp.OpReturn();
|
||||
}
|
||||
decomp.AddLabel(decomp.OpLabel());
|
||||
@@ -3080,7 +3135,7 @@ private:
|
||||
};
|
||||
|
||||
void SPIRVDecompiler::DecompileAST() {
|
||||
const u32 num_flow_variables = ir.GetASTNumVariables();
|
||||
const u32 num_flow_variables = context_func->GetASTNumVariables();
|
||||
for (u32 i = 0; i < num_flow_variables; i++) {
|
||||
const Id id = OpVariable(t_prv_bool, spv::StorageClass::Private, v_false);
|
||||
Name(id, fmt::format("flow_var_{}", i));
|
||||
@@ -3089,7 +3144,7 @@ void SPIRVDecompiler::DecompileAST() {
|
||||
|
||||
DefinePrologue();
|
||||
|
||||
const ASTNode program = ir.GetASTProgram();
|
||||
const ASTNode program = context_func->GetASTProgram();
|
||||
ASTDecompiler decompiler{*this};
|
||||
decompiler.Visit(program);
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include <set>
|
||||
#include <stack>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include <vector>
|
||||
|
||||
#include "common/assert.h"
|
||||
@@ -26,17 +27,29 @@ using Tegra::Shader::OpCode;
|
||||
|
||||
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 {
|
||||
u32 address{};
|
||||
std::stack<u32> ssy_stack{};
|
||||
std::stack<u32> pbk_stack{};
|
||||
std::stack<JumpItem> stack{};
|
||||
};
|
||||
|
||||
struct BlockStack {
|
||||
BlockStack() = default;
|
||||
explicit BlockStack(const Query& q) : ssy_stack{q.ssy_stack}, pbk_stack{q.pbk_stack} {}
|
||||
std::stack<u32> ssy_stack{};
|
||||
std::stack<u32> pbk_stack{};
|
||||
explicit BlockStack(const Query& q) : stack{q.stack} {}
|
||||
std::stack<JumpItem> stack{};
|
||||
};
|
||||
|
||||
template <typename T, typename... Args>
|
||||
@@ -65,20 +78,36 @@ struct BlockInfo {
|
||||
}
|
||||
};
|
||||
|
||||
struct CFGRebuildState {
|
||||
explicit CFGRebuildState(const ProgramCode& program_code_, u32 start_, Registry& registry_)
|
||||
: program_code{program_code_}, registry{registry_}, start{start_} {}
|
||||
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 {
|
||||
explicit CFGRebuildState(ProgramControl& control_, const ProgramCode& program_code_, u32 start_,
|
||||
u32 base_start_, Registry& registry_)
|
||||
: control{control_}, program_code{program_code_}, registry{registry_}, start{start_},
|
||||
base_start{base_start_} {}
|
||||
|
||||
ProgramControl& control;
|
||||
const ProgramCode& program_code;
|
||||
Registry& registry;
|
||||
u32 start{};
|
||||
u32 base_start{};
|
||||
std::vector<BlockInfo> block_info;
|
||||
std::list<u32> inspect_queries;
|
||||
std::list<Query> queries;
|
||||
std::unordered_map<u32, u32> registered;
|
||||
std::set<u32> labels;
|
||||
std::map<u32, u32> ssy_labels;
|
||||
std::map<u32, u32> pbk_labels;
|
||||
std::map<u32, JumpItem> jump_labels;
|
||||
std::unordered_map<u32, BlockStack> stacks;
|
||||
ASTManager* manager{};
|
||||
};
|
||||
@@ -153,7 +182,7 @@ template <typename Result, typename TestCallable, typename PackCallable>
|
||||
std::optional<Result> TrackInstruction(const CFGRebuildState& state, u32& pos, TestCallable test,
|
||||
PackCallable pack) {
|
||||
for (; pos >= state.start; --pos) {
|
||||
if (IsSchedInstruction(pos, state.start)) {
|
||||
if (IsSchedInstruction(pos, state.base_start)) {
|
||||
continue;
|
||||
}
|
||||
const Instruction instr = state.program_code[pos];
|
||||
@@ -262,7 +291,7 @@ std::pair<ParseResult, ParseInfo> ParseCode(CFGRebuildState& state, u32 address)
|
||||
single_branch.ignore = true;
|
||||
break;
|
||||
}
|
||||
if (IsSchedInstruction(offset, state.start)) {
|
||||
if (IsSchedInstruction(offset, state.base_start)) {
|
||||
offset++;
|
||||
continue;
|
||||
}
|
||||
@@ -274,6 +303,7 @@ std::pair<ParseResult, ParseInfo> ParseCode(CFGRebuildState& state, u32 address)
|
||||
}
|
||||
|
||||
switch (opcode->get().GetId()) {
|
||||
case OpCode::Id::RET:
|
||||
case OpCode::Id::EXIT: {
|
||||
const auto pred_index = static_cast<u32>(instr.pred.pred_index);
|
||||
single_branch.condition.predicate = GetPredicate(pred_index, instr.negate_pred != 0);
|
||||
@@ -411,13 +441,20 @@ std::pair<ParseResult, ParseInfo> ParseCode(CFGRebuildState& state, u32 address)
|
||||
case OpCode::Id::SSY: {
|
||||
const u32 target = offset + instr.bra.GetBranchTarget();
|
||||
insert_label(state, target);
|
||||
state.ssy_labels.emplace(offset, target);
|
||||
JumpItem it = {JumpLabel::SSYClass, target};
|
||||
state.jump_labels.emplace(offset, it);
|
||||
break;
|
||||
}
|
||||
case OpCode::Id::PBK: {
|
||||
const u32 target = offset + instr.bra.GetBranchTarget();
|
||||
insert_label(state, target);
|
||||
state.pbk_labels.emplace(offset, target);
|
||||
JumpItem it = {JumpLabel::PBKClass, target};
|
||||
state.jump_labels.emplace(offset, it);
|
||||
break;
|
||||
}
|
||||
case OpCode::Id::CAL: {
|
||||
const u32 target = offset + instr.bra.GetBranchTarget();
|
||||
state.control.RegisterFunction(target);
|
||||
break;
|
||||
}
|
||||
case OpCode::Id::BRX: {
|
||||
@@ -513,7 +550,7 @@ bool TryInspectAddress(CFGRebuildState& state) {
|
||||
}
|
||||
|
||||
bool TryQuery(CFGRebuildState& state) {
|
||||
const auto gather_labels = [](std::stack<u32>& cc, std::map<u32, u32>& labels,
|
||||
const auto gather_labels = [](std::stack<JumpItem>& cc, std::map<u32, JumpItem>& labels,
|
||||
BlockInfo& block) {
|
||||
auto gather_start = labels.lower_bound(block.start);
|
||||
const auto gather_end = labels.upper_bound(block.end);
|
||||
@@ -522,6 +559,19 @@ bool TryQuery(CFGRebuildState& state) {
|
||||
++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()) {
|
||||
return false;
|
||||
}
|
||||
@@ -534,8 +584,7 @@ bool TryQuery(CFGRebuildState& state) {
|
||||
// consumes a label. Schedule new queries accordingly
|
||||
if (block.visited) {
|
||||
BlockStack& stack = state.stacks[q.address];
|
||||
const bool all_okay = (stack.ssy_stack.empty() || q.ssy_stack == stack.ssy_stack) &&
|
||||
(stack.pbk_stack.empty() || q.pbk_stack == stack.pbk_stack);
|
||||
const bool all_okay = (stack.stack.empty() || q.stack == stack.stack);
|
||||
state.queries.pop_front();
|
||||
return all_okay;
|
||||
}
|
||||
@@ -544,8 +593,7 @@ bool TryQuery(CFGRebuildState& state) {
|
||||
|
||||
Query q2(q);
|
||||
state.queries.pop_front();
|
||||
gather_labels(q2.ssy_stack, state.ssy_labels, block);
|
||||
gather_labels(q2.pbk_stack, state.pbk_labels, block);
|
||||
gather_labels(q2.stack, state.jump_labels, block);
|
||||
if (std::holds_alternative<SingleBranch>(*block.branch)) {
|
||||
auto* branch = std::get_if<SingleBranch>(block.branch.get());
|
||||
if (!branch->condition.IsUnconditional()) {
|
||||
@@ -555,16 +603,10 @@ bool TryQuery(CFGRebuildState& state) {
|
||||
|
||||
auto& conditional_query = state.queries.emplace_back(q2);
|
||||
if (branch->is_sync) {
|
||||
if (branch->address == unassigned_branch) {
|
||||
branch->address = conditional_query.ssy_stack.top();
|
||||
}
|
||||
conditional_query.ssy_stack.pop();
|
||||
pop_labels(JumpLabel::SSYClass, branch, conditional_query);
|
||||
}
|
||||
if (branch->is_brk) {
|
||||
if (branch->address == unassigned_branch) {
|
||||
branch->address = conditional_query.pbk_stack.top();
|
||||
}
|
||||
conditional_query.pbk_stack.pop();
|
||||
pop_labels(JumpLabel::PBKClass, branch, conditional_query);
|
||||
}
|
||||
conditional_query.address = branch->address;
|
||||
return true;
|
||||
@@ -646,25 +688,23 @@ void DecompileShader(CFGRebuildState& state) {
|
||||
state.manager->Decompile();
|
||||
}
|
||||
|
||||
} // Anonymous namespace
|
||||
|
||||
std::unique_ptr<ShaderCharacteristics> ScanFlow(const ProgramCode& program_code, u32 start_address,
|
||||
const CompilerSettings& settings,
|
||||
Registry& registry) {
|
||||
auto result_out = std::make_unique<ShaderCharacteristics>();
|
||||
ShaderFunction ScanFunction(ProgramControl& control, const ProgramCode& program_code,
|
||||
u32 start_address, u32 base_start, const CompilerSettings& settings,
|
||||
Registry& registry) {
|
||||
ShaderFunction result_out{};
|
||||
if (settings.depth == CompileDepth::BruteForce) {
|
||||
result_out->settings.depth = CompileDepth::BruteForce;
|
||||
result_out.settings.depth = CompileDepth::BruteForce;
|
||||
return result_out;
|
||||
}
|
||||
|
||||
CFGRebuildState state{program_code, start_address, registry};
|
||||
CFGRebuildState state{control, program_code, start_address, base_start, registry};
|
||||
// Inspect Code and generate blocks
|
||||
state.labels.clear();
|
||||
state.labels.emplace(start_address);
|
||||
state.inspect_queries.push_back(state.start);
|
||||
while (!state.inspect_queries.empty()) {
|
||||
if (!TryInspectAddress(state)) {
|
||||
result_out->settings.depth = CompileDepth::BruteForce;
|
||||
result_out.settings.depth = CompileDepth::BruteForce;
|
||||
return result_out;
|
||||
}
|
||||
}
|
||||
@@ -675,7 +715,7 @@ std::unique_ptr<ShaderCharacteristics> ScanFlow(const ProgramCode& program_code,
|
||||
|
||||
if (settings.depth != CompileDepth::FlowStack) {
|
||||
// Decompile Stacks
|
||||
state.queries.push_back(Query{state.start, {}, {}});
|
||||
state.queries.push_back(Query{state.start, {}});
|
||||
decompiled = true;
|
||||
while (!state.queries.empty()) {
|
||||
if (!TryQuery(state)) {
|
||||
@@ -705,19 +745,18 @@ std::unique_ptr<ShaderCharacteristics> ScanFlow(const ProgramCode& program_code,
|
||||
state.manager->ShowCurrentState("Of Shader");
|
||||
state.manager->Clear();
|
||||
} else {
|
||||
auto characteristics = std::make_unique<ShaderCharacteristics>();
|
||||
characteristics->start = start_address;
|
||||
characteristics->settings.depth = settings.depth;
|
||||
characteristics->manager = std::move(manager);
|
||||
characteristics->end = state.block_info.back().end + 1;
|
||||
return characteristics;
|
||||
result_out.start = start_address;
|
||||
result_out.settings.depth = settings.depth;
|
||||
result_out.manager = std::move(manager);
|
||||
result_out.end = state.block_info.back().end + 1;
|
||||
return result_out;
|
||||
}
|
||||
}
|
||||
|
||||
result_out->start = start_address;
|
||||
result_out->settings.depth =
|
||||
result_out.start = start_address;
|
||||
result_out.settings.depth =
|
||||
use_flow_stack ? CompileDepth::FlowStack : CompileDepth::NoFlowStack;
|
||||
result_out->blocks.clear();
|
||||
result_out.blocks.clear();
|
||||
for (auto& block : state.block_info) {
|
||||
ShaderBlock new_block{};
|
||||
new_block.start = block.start;
|
||||
@@ -726,20 +765,20 @@ std::unique_ptr<ShaderCharacteristics> ScanFlow(const ProgramCode& program_code,
|
||||
if (!new_block.ignore_branch) {
|
||||
new_block.branch = block.branch;
|
||||
}
|
||||
result_out->end = std::max(result_out->end, block.end);
|
||||
result_out->blocks.push_back(new_block);
|
||||
result_out.end = std::max(result_out.end, block.end);
|
||||
result_out.blocks.push_back(new_block);
|
||||
}
|
||||
if (!use_flow_stack) {
|
||||
result_out->labels = std::move(state.labels);
|
||||
result_out.labels = std::move(state.labels);
|
||||
return result_out;
|
||||
}
|
||||
|
||||
auto back = result_out->blocks.begin();
|
||||
auto back = result_out.blocks.begin();
|
||||
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) {
|
||||
back->end = next->end;
|
||||
next = result_out->blocks.erase(next);
|
||||
next = result_out.blocks.erase(next);
|
||||
continue;
|
||||
}
|
||||
back = next;
|
||||
@@ -748,4 +787,22 @@ std::unique_ptr<ShaderCharacteristics> ScanFlow(const ProgramCode& program_code,
|
||||
|
||||
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
|
||||
@@ -5,6 +5,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <list>
|
||||
#include <map>
|
||||
#include <optional>
|
||||
#include <set>
|
||||
#include <variant>
|
||||
@@ -101,7 +102,7 @@ struct ShaderBlock {
|
||||
}
|
||||
};
|
||||
|
||||
struct ShaderCharacteristics {
|
||||
struct ShaderFunction {
|
||||
std::list<ShaderBlock> blocks{};
|
||||
std::set<u32> labels{};
|
||||
u32 start{};
|
||||
@@ -110,8 +111,12 @@ struct ShaderCharacteristics {
|
||||
CompilerSettings settings{};
|
||||
};
|
||||
|
||||
std::unique_ptr<ShaderCharacteristics> ScanFlow(const ProgramCode& program_code, u32 start_address,
|
||||
const CompilerSettings& settings,
|
||||
Registry& registry);
|
||||
struct ShaderProgram {
|
||||
ShaderFunction main;
|
||||
std::map<u32, ShaderFunction> subfunctions;
|
||||
};
|
||||
|
||||
std::unique_ptr<ShaderProgram> ScanFlow(const ProgramCode& program_code, u32 start_address,
|
||||
const CompilerSettings& settings, Registry& registry);
|
||||
|
||||
} // namespace VideoCommon::Shader
|
||||
@@ -64,9 +64,52 @@ std::optional<u32> TryDeduceSamplerSize(const SamplerEntry& sampler_to_deduce,
|
||||
|
||||
} // 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 {
|
||||
public:
|
||||
explicit ASTDecoder(ShaderIR& ir_) : ir(ir_) {}
|
||||
explicit ASTDecoder(ShaderIR& ir_) : ir(ir_), decoder(ir_) {}
|
||||
|
||||
void operator()(ASTProgram& ast) {
|
||||
ASTNode current = ast.nodes.GetFirst();
|
||||
@@ -77,6 +120,7 @@ public:
|
||||
}
|
||||
|
||||
void operator()(ASTIfThen& ast) {
|
||||
decoder.Visit(ast.condition);
|
||||
ASTNode current = ast.nodes.GetFirst();
|
||||
while (current) {
|
||||
Visit(current);
|
||||
@@ -96,13 +140,18 @@ public:
|
||||
|
||||
void operator()(ASTBlockDecoded& ast) {}
|
||||
|
||||
void operator()(ASTVarSet& ast) {}
|
||||
void operator()(ASTVarSet& ast) {
|
||||
decoder.Visit(ast.condition);
|
||||
}
|
||||
|
||||
void operator()(ASTLabel& ast) {}
|
||||
|
||||
void operator()(ASTGoto& ast) {}
|
||||
void operator()(ASTGoto& ast) {
|
||||
decoder.Visit(ast.condition);
|
||||
}
|
||||
|
||||
void operator()(ASTDoWhile& ast) {
|
||||
decoder.Visit(ast.condition);
|
||||
ASTNode current = ast.nodes.GetFirst();
|
||||
while (current) {
|
||||
Visit(current);
|
||||
@@ -110,9 +159,13 @@ 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) {
|
||||
std::visit(*this, *node->GetInnerData());
|
||||
@@ -125,77 +178,113 @@ public:
|
||||
|
||||
private:
|
||||
ShaderIR& ir;
|
||||
ExprDecoder decoder;
|
||||
};
|
||||
|
||||
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));
|
||||
|
||||
decompiled = false;
|
||||
auto info = ScanFlow(program_code, main_offset, settings, registry);
|
||||
auto& shader_info = *info;
|
||||
coverage_begin = shader_info.start;
|
||||
coverage_end = 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;
|
||||
u32 id_start = 1;
|
||||
for (auto& pair : info->subfunctions) {
|
||||
func_map.emplace(pair.first, id_start);
|
||||
id_start++;
|
||||
}
|
||||
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 = 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));
|
||||
coverage_begin = info->main.start;
|
||||
coverage_end = 0;
|
||||
decode_function(info->main);
|
||||
main_function = gen_function(info->main, 0);
|
||||
subfunctions.resize(info->subfunctions.size());
|
||||
for (auto& pair : info->subfunctions) {
|
||||
auto& func_info = pair.second;
|
||||
decode_function(func_info);
|
||||
u32 id = func_map[pair.first];
|
||||
subfunctions[id - 1] = gen_function(func_info, id);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -33,6 +33,7 @@ u32 ShaderIR::DecodeOther(NodeBlock& bb, u32 pc) {
|
||||
// With the previous preconditions, this instruction is a no-operation.
|
||||
break;
|
||||
}
|
||||
case OpCode::Id::RET:
|
||||
case OpCode::Id::EXIT: {
|
||||
const ConditionCode cc = instr.flow_condition_code;
|
||||
UNIMPLEMENTED_IF_MSG(cc != ConditionCode::T, "EXIT condition code used: {}", cc);
|
||||
@@ -312,6 +313,16 @@ u32 ShaderIR::DecodeOther(NodeBlock& bb, u32 pc) {
|
||||
LOG_DEBUG(HW_GPU, "DEPBAR instruction is stubbed");
|
||||
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:
|
||||
UNIMPLEMENTED_MSG("Unhandled instruction: {}", opcode->get().GetName());
|
||||
}
|
||||
|
||||
@@ -267,10 +267,11 @@ class PatchNode;
|
||||
class SmemNode;
|
||||
class GmemNode;
|
||||
class CommentNode;
|
||||
class FunctionCallNode;
|
||||
|
||||
using NodeData = std::variant<OperationNode, ConditionalNode, GprNode, CustomVarNode, ImmediateNode,
|
||||
InternalFlagNode, PredicateNode, AbufNode, PatchNode, CbufNode,
|
||||
LmemNode, SmemNode, GmemNode, CommentNode>;
|
||||
LmemNode, SmemNode, GmemNode, FunctionCallNode, CommentNode>;
|
||||
using Node = std::shared_ptr<NodeData>;
|
||||
using Node4 = std::array<Node, 4>;
|
||||
using NodeBlock = std::vector<Node>;
|
||||
@@ -494,6 +495,18 @@ private:
|
||||
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
|
||||
class GprNode final {
|
||||
public:
|
||||
|
||||
@@ -19,6 +19,11 @@ Node Comment(std::string 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) {
|
||||
return MakeNode<ImmediateNode>(value);
|
||||
}
|
||||
|
||||
@@ -27,6 +27,9 @@ Node Conditional(Node condition, std::vector<Node> code);
|
||||
/// Creates a commentary node
|
||||
Node Comment(std::string text);
|
||||
|
||||
/// Creates a function call
|
||||
Node FunctionCall(u32 func_id);
|
||||
|
||||
/// Creates an u32 immediate
|
||||
Node Immediate(u32 value);
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ namespace VideoCommon::Shader {
|
||||
|
||||
struct ShaderBlock;
|
||||
|
||||
constexpr u32 MAX_PROGRAM_LENGTH = 0x1000;
|
||||
constexpr u32 MAX_PROGRAM_LENGTH = 0x2000;
|
||||
|
||||
struct ConstBuffer {
|
||||
constexpr explicit ConstBuffer(u32 max_offset_, bool is_indirect_)
|
||||
@@ -64,16 +64,68 @@ struct GlobalMemoryUsage {
|
||||
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 {
|
||||
public:
|
||||
explicit ShaderIR(const ProgramCode& program_code_, u32 main_offset_,
|
||||
CompilerSettings settings_, Registry& registry_);
|
||||
~ShaderIR();
|
||||
|
||||
const std::map<u32, NodeBlock>& GetBasicBlocks() const {
|
||||
return basic_blocks;
|
||||
}
|
||||
|
||||
const std::set<u32>& GetRegisters() const {
|
||||
return used_registers;
|
||||
}
|
||||
@@ -155,26 +207,6 @@ public:
|
||||
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 {
|
||||
return (address - main_offset) * static_cast<u32>(sizeof(Tegra::Shader::Instruction));
|
||||
}
|
||||
@@ -190,7 +222,16 @@ public:
|
||||
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:
|
||||
friend class ExprDecoder;
|
||||
friend class ASTDecoder;
|
||||
|
||||
struct SamplerInfo {
|
||||
@@ -453,6 +494,10 @@ private:
|
||||
std::vector<Node> amend_code;
|
||||
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<Tegra::Shader::Pred> used_predicates;
|
||||
std::set<Tegra::Shader::Attribute::Index> used_input_attributes;
|
||||
|
||||
@@ -42,7 +42,7 @@ void ControllerDialog::refreshConfiguration() {
|
||||
|
||||
QAction* ControllerDialog::toggleViewAction() {
|
||||
if (toggle_view_action == nullptr) {
|
||||
toggle_view_action = new QAction(windowTitle(), this);
|
||||
toggle_view_action = new QAction(tr("&Controller P1"), this);
|
||||
toggle_view_action->setCheckable(true);
|
||||
toggle_view_action->setChecked(isVisible());
|
||||
connect(toggle_view_action, &QAction::toggled, this, &ControllerDialog::setVisible);
|
||||
|
||||
Reference in new issue
Block a user