From 619bc956bded4588acf760ac3b2f83af24f10b84 Mon Sep 17 00:00:00 2001 From: Souryo Date: Sun, 2 Apr 2017 17:41:24 -0400 Subject: [PATCH] Optimizations: Improved emulation performance (5-15% speed boost depending on game) --- Core/BaseApuChannel.h | 2 +- Core/MMC5.h | 44 ++++++++++++++++++------------------- Core/MMC5Audio.h | 27 +++++++++++------------ Core/PPU.cpp | 21 ++++++++++++------ Core/PPU.h | 1 + Core/SoundMixer.cpp | 50 ++++++++++++++++++++++++++++++------------- Core/SoundMixer.h | 2 ++ Core/SquareChannel.h | 11 ++++++---- 8 files changed, 95 insertions(+), 63 deletions(-) diff --git a/Core/BaseApuChannel.h b/Core/BaseApuChannel.h index bf606f43..d8226446 100644 --- a/Core/BaseApuChannel.h +++ b/Core/BaseApuChannel.h @@ -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); diff --git a/Core/MMC5.h b/Core/MMC5.h index d89c5890..b6abeeda 100644 --- a/Core/MMC5.h +++ b/Core/MMC5.h @@ -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; } } diff --git a/Core/MMC5Audio.h b/Core/MMC5Audio.h index e844b722..2771bdf9 100644 --- a/Core/MMC5Audio.h +++ b/Core/MMC5Audio.h @@ -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(); diff --git a/Core/PPU.cpp b/Core/PPU.cpp index a3b3dae4..e98e3370 100644 --- a/Core/PPU.cpp +++ b/Core/PPU.cpp @@ -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; + } } } \ No newline at end of file diff --git a/Core/PPU.h b/Core/PPU.h index 47f5484f..b8307199 100644 --- a/Core/PPU.h +++ b/Core/PPU.h @@ -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]; diff --git a/Core/SoundMixer.cpp b/Core/SoundMixer.cpp index b0dd670f..ff76ce45 100644 --- a/Core/SoundMixer.cpp +++ b/Core/SoundMixer.cpp @@ -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)); } diff --git a/Core/SoundMixer.h b/Core/SoundMixer.h index a5fef2e0..623e3a1b 100644 --- a/Core/SoundMixer.h +++ b/Core/SoundMixer.h @@ -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); diff --git a/Core/SquareChannel.h b/Core/SquareChannel.h index 6cdcd120..01105b79 100644 --- a/Core/SquareChannel.h +++ b/Core/SquareChannel.h @@ -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()