mirror of
https://github.com/libretro/Mesen.git
synced 2024-11-27 02:50:28 +00:00
Optimizations: Improved emulation performance (5-15% speed boost depending on game)
This commit is contained in:
parent
cd1c46eaff
commit
619bc956bd
@ -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);
|
||||
|
44
Core/MMC5.h
44
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;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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();
|
||||
|
21
Core/PPU.cpp
21
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;
|
||||
}
|
||||
}
|
||||
}
|
@ -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];
|
||||
|
@ -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));
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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()
|
||||
|
Loading…
Reference in New Issue
Block a user