mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2025-08-25 11:46:27 +00:00
DSP: Convert accelerator to a C++ class
Slightly cleaner, allows DSP accelerator behaviour to be added to both HLE and LLE pretty easily, and makes the accelerator easier to unit test. I chose to include all accelerator state as private members, and to expose state that is accessible via registers with getters/setters. It's more verbose, yes, but it makes it very clear what is part of the accelerator state and what isn't (e.g. coefs). This works quite well for registers, since the accelerator can do whatever it wants internally. For example, the start/end/current addresses are masked -- having a getter/setter makes it easier to enforce the mask.
This commit is contained in:
parent
017bfcda2b
commit
003dba5275
7 changed files with 256 additions and 143 deletions
|
@ -13,6 +13,7 @@
|
|||
#endif
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
|
||||
#include "Common/CommonTypes.h"
|
||||
#include "Common/MathUtil.h"
|
||||
|
@ -163,57 +164,33 @@ void DumpPB(const PB_TYPE& pb)
|
|||
#endif
|
||||
|
||||
// Simulated accelerator state.
|
||||
static u32 acc_loop_addr, acc_end_addr;
|
||||
static u32* acc_cur_addr;
|
||||
static PB_TYPE* acc_pb;
|
||||
static bool acc_end_reached;
|
||||
|
||||
// Sets up the simulated accelerator.
|
||||
void AcceleratorSetup(PB_TYPE* pb, u32* cur_addr)
|
||||
class HLEAccelerator final : public Accelerator
|
||||
{
|
||||
acc_pb = pb;
|
||||
// Masking occurs for the start and end addresses as soon as the registers are written to.
|
||||
acc_loop_addr = HILO_TO_32(pb->audio_addr.loop_addr) & 0x3fffffff;
|
||||
acc_end_addr = HILO_TO_32(pb->audio_addr.end_addr) & 0x3fffffff;
|
||||
acc_cur_addr = cur_addr;
|
||||
// It also happens for the current address, but with a different mask.
|
||||
*acc_cur_addr &= 0xbfffffff;
|
||||
acc_end_reached = false;
|
||||
}
|
||||
|
||||
// Reads a sample from the accelerator. Also handles looping and
|
||||
// disabling streams that reached the end (this is done by an exception raised
|
||||
// by the accelerator on real hardware).
|
||||
u16 AcceleratorGetSample()
|
||||
{
|
||||
// See below for explanations about acc_end_reached.
|
||||
if (acc_end_reached)
|
||||
return 0;
|
||||
|
||||
auto end_address_reached = [] {
|
||||
// loop back to loop_addr.
|
||||
*acc_cur_addr = acc_loop_addr;
|
||||
|
||||
protected:
|
||||
void OnEndException() override
|
||||
{
|
||||
if (acc_pb->audio_addr.looping)
|
||||
{
|
||||
// Set the ADPCM info to continue processing at loop_addr.
|
||||
//
|
||||
// For some reason, yn1 and yn2 aren't set if the voice is not of
|
||||
// stream type. This is what the AX UCode does and I don't really
|
||||
// know why.
|
||||
acc_pb->adpcm.pred_scale = acc_pb->adpcm_loop_info.pred_scale;
|
||||
SetPredScale(acc_pb->adpcm_loop_info.pred_scale);
|
||||
if (!acc_pb->is_stream)
|
||||
{
|
||||
acc_pb->adpcm.yn1 = acc_pb->adpcm_loop_info.yn1;
|
||||
acc_pb->adpcm.yn2 = acc_pb->adpcm_loop_info.yn2;
|
||||
SetYn1(acc_pb->adpcm_loop_info.yn1);
|
||||
SetYn2(acc_pb->adpcm_loop_info.yn2);
|
||||
}
|
||||
#ifdef AX_GC
|
||||
else
|
||||
{
|
||||
// Refresh YN1 and YN2. This indirectly causes the accelerator to resume reads.
|
||||
SetYn1(GetYn1());
|
||||
SetYn2(GetYn2());
|
||||
#ifdef AX_GC
|
||||
// If we're streaming, increment the loop counter.
|
||||
acc_pb->loop_counter++;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -229,11 +206,38 @@ u16 AcceleratorGetSample()
|
|||
acc_end_reached = true;
|
||||
#endif
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
return ReadAccelerator(acc_loop_addr, acc_end_addr, acc_cur_addr,
|
||||
acc_pb->audio_addr.sample_format, &acc_pb->adpcm.yn1, &acc_pb->adpcm.yn2,
|
||||
&acc_pb->adpcm.pred_scale, acc_pb->adpcm.coefs, end_address_reached);
|
||||
u8 ReadMemory(u32 address) override { return ReadARAM(address); }
|
||||
void WriteMemory(u32 address, u8 value) override { WriteARAM(value, address); }
|
||||
};
|
||||
|
||||
static std::unique_ptr<Accelerator> s_accelerator = std::make_unique<HLEAccelerator>();
|
||||
|
||||
// Sets up the simulated accelerator.
|
||||
void AcceleratorSetup(PB_TYPE* pb)
|
||||
{
|
||||
acc_pb = pb;
|
||||
s_accelerator->SetStartAddress(HILO_TO_32(pb->audio_addr.loop_addr));
|
||||
s_accelerator->SetEndAddress(HILO_TO_32(pb->audio_addr.end_addr));
|
||||
s_accelerator->SetCurrentAddress(HILO_TO_32(pb->audio_addr.cur_addr));
|
||||
s_accelerator->SetSampleFormat(pb->audio_addr.sample_format);
|
||||
s_accelerator->SetYn1(pb->adpcm.yn1);
|
||||
s_accelerator->SetYn2(pb->adpcm.yn2);
|
||||
s_accelerator->SetPredScale(pb->adpcm.pred_scale);
|
||||
acc_end_reached = false;
|
||||
}
|
||||
|
||||
// Reads a sample from the accelerator. Also handles looping and
|
||||
// disabling streams that reached the end (this is done by an exception raised
|
||||
// by the accelerator on real hardware).
|
||||
u16 AcceleratorGetSample()
|
||||
{
|
||||
// See below for explanations about acc_end_reached.
|
||||
if (acc_end_reached)
|
||||
return 0;
|
||||
|
||||
return s_accelerator->Read(acc_pb->adpcm.coefs);
|
||||
}
|
||||
|
||||
// Reads samples from the input callback, resamples them to <count> samples at
|
||||
|
@ -375,8 +379,7 @@ u32 ResampleAudio(std::function<s16(u32)> input_callback, s16* output, u32 count
|
|||
// if required.
|
||||
void GetInputSamples(PB_TYPE& pb, s16* samples, u16 count, const s16* coeffs)
|
||||
{
|
||||
u32 cur_addr = HILO_TO_32(pb.audio_addr.cur_addr);
|
||||
AcceleratorSetup(&pb, &cur_addr);
|
||||
AcceleratorSetup(&pb);
|
||||
|
||||
if (coeffs)
|
||||
coeffs += pb.coef_select * 0x200;
|
||||
|
@ -385,9 +388,12 @@ void GetInputSamples(PB_TYPE& pb, s16* samples, u16 count, const s16* coeffs)
|
|||
pb.src.cur_addr_frac, HILO_TO_32(pb.src.ratio), pb.src_type, coeffs);
|
||||
pb.src.cur_addr_frac = (curr_pos & 0xFFFF);
|
||||
|
||||
// Update current position in the PB.
|
||||
pb.audio_addr.cur_addr_hi = static_cast<u16>(cur_addr >> 16) & 0xbfff;
|
||||
pb.audio_addr.cur_addr_lo = static_cast<u16>(cur_addr);
|
||||
// Update current position, YN1, YN2 and pred scale in the PB.
|
||||
pb.audio_addr.cur_addr_hi = static_cast<u16>(s_accelerator->GetCurrentAddress() >> 16);
|
||||
pb.audio_addr.cur_addr_lo = static_cast<u16>(s_accelerator->GetCurrentAddress());
|
||||
pb.adpcm.yn1 = s_accelerator->GetYn1();
|
||||
pb.adpcm.yn2 = s_accelerator->GetYn2();
|
||||
pb.adpcm.pred_scale = s_accelerator->GetPredScale();
|
||||
}
|
||||
|
||||
// Add samples to an output buffer, with optional volume ramping.
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
#include "Common/Thread.h"
|
||||
#include "Core/ConfigManager.h"
|
||||
#include "Core/Core.h"
|
||||
#include "Core/DSP/DSPAccelerator.h"
|
||||
#include "Core/DSP/DSPCaptureLogger.h"
|
||||
#include "Core/DSP/DSPCore.h"
|
||||
#include "Core/DSP/DSPHWInterface.h"
|
||||
|
@ -71,6 +72,7 @@ void DSPLLE::DoState(PointerWrap& p)
|
|||
|
||||
p.Do(g_dsp.step_counter);
|
||||
p.DoArray(g_dsp.ifx_regs);
|
||||
g_dsp.accelerator->DoState(p);
|
||||
p.Do(g_dsp.mbox[0]);
|
||||
p.Do(g_dsp.mbox[1]);
|
||||
Common::UnWriteProtectMemory(g_dsp.iram, DSP_IRAM_BYTE_SIZE, false);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue