Optimizations: Improved emulation performance (5-15% speed boost depending on game)

This commit is contained in:
Souryo 2017-04-02 17:41:24 -04:00
parent cd1c46eaff
commit 619bc956bd
8 changed files with 95 additions and 63 deletions

View File

@ -90,7 +90,7 @@ public:
return 0;
}
virtual void AddOutput(int8_t output)
void AddOutput(int8_t output)
{
if(output != _lastOutput) {
_mixer->AddDelta(_channel, _previousCycle, output - _lastOutput);

View File

@ -171,42 +171,40 @@ private:
void ProcessCpuClock() override
{
if(!PPU::GetControlFlags().BackgroundEnabled && !PPU::GetControlFlags().SpritesEnabled) {
_ppuInFrame = false;
}
_audio.Clock();
}
virtual void NotifyVRAMAddressChange(uint16_t addr) override
{
if(_spriteFetch != IsSpriteFetch() || _largeSprites != PPU::GetControlFlags().LargeSprites) {
if(PPU::GetControlFlags().BackgroundEnabled || PPU::GetControlFlags().SpritesEnabled) {
if(PPU::GetControlFlags().BackgroundEnabled || PPU::GetControlFlags().SpritesEnabled) {
if(_spriteFetch != IsSpriteFetch() || _largeSprites != PPU::GetControlFlags().LargeSprites) {
UpdateChrBanks();
}
}
int16_t currentScanline = PPU::GetCurrentScanline();
if(currentScanline != _previousScanline) {
if(currentScanline >= 239 || currentScanline < 0) {
_ppuInFrame = false;
} else {
if(!_ppuInFrame) {
_ppuInFrame = true;
_irqCounter = 0;
_irqPending = false;
CPU::ClearIRQSource(IRQSource::External);
int16_t currentScanline = PPU::GetCurrentScanline();
if(currentScanline != _previousScanline) {
if(currentScanline >= 239 || currentScanline < 0) {
_ppuInFrame = false;
} else {
_irqCounter++;
if(_irqCounter == _irqCounterTarget) {
_irqPending = true;
if(_irqEnabled) {
CPU::SetIRQSource(IRQSource::External);
if(!_ppuInFrame) {
_ppuInFrame = true;
_irqCounter = 0;
_irqPending = false;
CPU::ClearIRQSource(IRQSource::External);
} else {
_irqCounter++;
if(_irqCounter == _irqCounterTarget) {
_irqPending = true;
if(_irqEnabled) {
CPU::SetIRQSource(IRQSource::External);
}
}
}
}
_previousScanline = currentScanline;
}
_previousScanline = currentScanline;
} else {
_ppuInFrame = false;
}
}

View File

@ -15,20 +15,11 @@ private:
//"$5001 has no effect. The MMC5 pulse channels will not sweep, as they have no sweep unit."
}
bool IsMuted() override
{
//"Frequency values less than 8 do not silence the MMC5 pulse channels; they can output ultrasonic frequencies."
return false;
}
public:
MMC5Square() : SquareChannel(AudioChannel::MMC5, nullptr, false)
{
_currentOutput = 0;
}
virtual void AddOutput(int8_t output) override
{
_currentOutput = output;
_isMmc5Square = true;
}
int8_t GetOutput()
@ -38,8 +29,14 @@ public:
void RunChannel()
{
Run(1);
EndFrame();
if(_timer == 0) {
_dutyPos = (_dutyPos - 1) & 0x07;
//"Frequency values less than 8 do not silence the MMC5 pulse channels; they can output ultrasonic frequencies."
_currentOutput = _dutySequences[_duty][_dutyPos] * GetVolume();
_timer = _period;
} else {
_timer--;
}
}
};
@ -80,8 +77,10 @@ protected:
}
int16_t summedOutput = (_square1.GetOutput() + _square2.GetOutput()) * 4 + _pcmOutput;
APU::AddExpansionAudioDelta(AudioChannel::MMC5, summedOutput - _lastOutput);
_lastOutput = summedOutput;
if(summedOutput != _lastOutput) {
APU::AddExpansionAudioDelta(AudioChannel::MMC5, summedOutput - _lastOutput);
_lastOutput = summedOutput;
}
_square1.ReloadCounter();
_square2.ReloadCounter();

View File

@ -577,6 +577,10 @@ void PPU::LoadSprite(uint8_t spriteY, uint8_t tileIndex, uint8_t attributes, uin
info.TileAddr = tileAddr;
info.OffsetY = lineOffset;
info.SpriteX = spriteX;
for(int i = 0; i < 8 && spriteX + i + 1 < 257; i++) {
_hasSprite[spriteX + i + 1] = true;
}
}
if(fetchLastSprite) {
@ -650,10 +654,10 @@ uint32_t PPU::GetPixelColor()
}
}
if(_cycle > _minimumDrawSpriteCycle) {
if(_hasSprite[_cycle] && _cycle > _minimumDrawSpriteCycle) {
//SpriteMask = true: Hide sprites in leftmost 8 pixels of screen
for(uint8_t i = 0; i < _spriteCount; i++) {
int32_t shift = -((int32_t)_spriteTiles[i].SpriteX - (int32_t)_cycle + 1);
int32_t shift = (int32_t)_cycle - _spriteTiles[i].SpriteX - 1;
if(shift >= 0 && shift < 8) {
_lastSprite = &_spriteTiles[i];
uint32_t spriteColor;
@ -699,10 +703,7 @@ void PPU::DrawPixel()
void PPU::ProcessScanline()
{
if(_scanline == -1 && _cycle == 0) {
_statusFlags.SpriteOverflow = false;
_statusFlags.Sprite0Hit = false;
} else if(_cycle > 0 && _cycle <= 256) {
if(_cycle > 0 && _cycle <= 256) {
LoadTileInfo();
if(_prevRenderingEnabled && (_cycle & 0x07) == 0) {
@ -723,6 +724,7 @@ void PPU::ProcessScanline()
} else if(_cycle >= 257 && _cycle <= 320) {
if(_cycle == 257) {
_spriteIndex = 0;
memset(_hasSprite, 0, sizeof(_hasSprite));
if(_prevRenderingEnabled) {
//copy horizontal scrolling value from t
_state.VideoRamAddr = (_state.VideoRamAddr & ~0x041F) | (_state.TmpVideoRamAddr & 0x041F);
@ -773,6 +775,9 @@ void PPU::ProcessScanline()
_cycle = 340;
}
}
} else if(_scanline == -1 && _cycle == 0) {
_statusFlags.SpriteOverflow = false;
_statusFlags.Sprite0Hit = false;
}
}
@ -1047,5 +1052,9 @@ void PPU::StreamState(bool saving)
SetNesModel(_nesModel);
UpdateMinimumDrawCycles();
for(int i = 0; i < 257; i++) {
_hasSprite[i] = true;
}
}
}

View File

@ -41,6 +41,7 @@ class PPU : public IMemoryHandler, public Snapshotable
uint8_t _spriteRAM[0x100];
uint8_t _secondarySpriteRAM[0x20];
bool _hasSprite[257];
uint16_t *_currentOutputBuffer;
uint16_t *_outputBuffers[2];

View File

@ -84,7 +84,13 @@ void SoundMixer::PlayAudioBuffer(uint32_t time)
EndFrame(time);
size_t sampleCount = blip_read_samples(_blipBufLeft, _outputBuffer, SoundMixer::MaxSamplesPerFrame, 1);
blip_read_samples(_blipBufRight, _outputBuffer + 1, SoundMixer::MaxSamplesPerFrame, 1);
if(_hasPanning) {
blip_read_samples(_blipBufRight, _outputBuffer + 1, SoundMixer::MaxSamplesPerFrame, 1);
} else {
for(int i = 0; i < sampleCount * 2; i+=2) {
_outputBuffer[i + 1] = _outputBuffer[i];
}
}
//Apply low pass filter/volume reduction when in background (based on options)
if(!VideoDecoder::GetInstance()->IsRecording() && !_waveRecorder && !EmulationSettings::CheckFlag(EmulationFlags::NsfPlayerEnabled) && EmulationSettings::CheckFlag(EmulationFlags::InBackground)) {
@ -129,8 +135,9 @@ void SoundMixer::PlayAudioBuffer(uint32_t time)
//Update sample rate for next frame if setting changed
_sampleRate = EmulationSettings::GetSampleRate();
UpdateRates(true);
} else {
UpdateRates(false);
}
UpdateRates(false);
}
void SoundMixer::SetNesModel(NesModel model)
@ -151,6 +158,20 @@ void SoundMixer::UpdateRates(bool forceUpdate)
blip_set_rates(_blipBufLeft, _clockRate, _sampleRate);
blip_set_rates(_blipBufRight, _clockRate, _sampleRate);
}
bool hasPanning = false;
for(uint32_t i = 0; i < MaxChannelCount; i++) {
_volumes[i] = EmulationSettings::GetChannelVolume((AudioChannel)i);
_panning[i] = EmulationSettings::GetChannelPanning((AudioChannel)i);
if(_panning[i] != 1.0) {
if(_hasPanning) {
blip_clear(_blipBufLeft);
blip_clear(_blipBufRight);
}
_hasPanning = true;
}
}
_hasPanning = hasPanning;
}
double SoundMixer::GetChannelOutput(AudioChannel channel, bool forRightChannel)
@ -167,8 +188,8 @@ int16_t SoundMixer::GetOutputVolume(bool forRightChannel)
double squareOutput = GetChannelOutput(AudioChannel::Square1, forRightChannel) + GetChannelOutput(AudioChannel::Square2, forRightChannel);
double tndOutput = 3 * GetChannelOutput(AudioChannel::Triangle, forRightChannel) + 2 * GetChannelOutput(AudioChannel::Noise, forRightChannel) + GetChannelOutput(AudioChannel::DMC, forRightChannel);
uint16_t squareVolume = (uint16_t)(95.52 / (8128.0 / squareOutput + 100.0) * 5000);
uint16_t tndVolume = (uint16_t)(163.67 / (24329.0 / tndOutput + 100.0) * 5000);
uint16_t squareVolume = (uint16_t)(477600 / (8128.0 / squareOutput + 100.0));
uint16_t tndVolume = (uint16_t)(818350 / (24329.0 / tndOutput + 100.0));
return (int16_t)(squareVolume + tndVolume +
GetChannelOutput(AudioChannel::FDS, forRightChannel) * 20 +
@ -189,7 +210,7 @@ void SoundMixer::AddDelta(AudioChannel channel, uint32_t time, int16_t delta)
void SoundMixer::EndFrame(uint32_t time)
{
double masterVolume = EmulationSettings::GetMasterVolume();
double masterVolume = EmulationSettings::GetMasterVolume() * _fadeRatio;
sort(_timestamps.begin(), _timestamps.end());
_timestamps.erase(std::unique(_timestamps.begin(), _timestamps.end()), _timestamps.end());
@ -206,16 +227,20 @@ void SoundMixer::EndFrame(uint32_t time)
}
int16_t currentOutput = GetOutputVolume(false);
blip_add_delta(_blipBufLeft, stamp, (int)((currentOutput - _previousOutputLeft) * masterVolume * _fadeRatio));
blip_add_delta(_blipBufLeft, stamp, (int)((currentOutput - _previousOutputLeft) * masterVolume));
_previousOutputLeft = currentOutput;
currentOutput = GetOutputVolume(true);
blip_add_delta(_blipBufRight, stamp, (int)((currentOutput - _previousOutputRight) * masterVolume * _fadeRatio));
_previousOutputRight = currentOutput;
if(_hasPanning) {
currentOutput = GetOutputVolume(true);
blip_add_delta(_blipBufRight, stamp, (int)((currentOutput - _previousOutputRight) * masterVolume));
_previousOutputRight = currentOutput;
}
}
blip_end_frame(_blipBufLeft, time);
blip_end_frame(_blipBufRight, time);
if(_hasPanning) {
blip_end_frame(_blipBufRight, time);
}
if(muteFrame) {
_muteFrameCount++;
@ -224,11 +249,6 @@ void SoundMixer::EndFrame(uint32_t time)
}
//Reset everything
for(uint32_t i = 0; i < MaxChannelCount; i++) {
_volumes[i] = EmulationSettings::GetChannelVolume((AudioChannel)i);
_panning[i] = EmulationSettings::GetChannelPanning((AudioChannel)i);
}
_timestamps.clear();
memset(_channelOutput, 0, sizeof(_channelOutput));
}

View File

@ -52,6 +52,8 @@ private:
uint32_t _sampleRate;
uint32_t _clockRate;
bool _hasPanning;
double GetChannelOutput(AudioChannel channel, bool forRightChannel);
int16_t GetOutputVolume(bool forRightChannel);
void EndFrame(uint32_t time);

View File

@ -6,7 +6,7 @@
class SquareChannel : public ApuEnvelope
{
private:
protected:
const uint8_t _dutySequences[4][8] = {
{ 0, 0, 0, 0, 0, 0, 0, 1 },
{ 0, 0, 0, 0, 0, 0, 1, 1 },
@ -15,6 +15,7 @@ private:
};
bool _isChannel1 = false;
bool _isMmc5Square = false;
uint8_t _duty = 0;
uint8_t _dutyPos = 0;
@ -28,7 +29,7 @@ private:
uint32_t _sweepTargetPeriod = 0;
uint16_t _realPeriod = 0;
virtual bool IsMuted()
bool IsMuted()
{
//A period of t < 8, either set explicitly or via a sweep period update, silences the corresponding pulse channel.
return _realPeriod < 8 || (!_sweepNegate && _sweepTargetPeriod > 0x7FF);
@ -161,8 +162,10 @@ public:
ResetEnvelope();
break;
}
UpdateOutput();
if(!_isMmc5Square) {
UpdateOutput();
}
}
void TickSweep()