Performance: Code optimizations (7-10% faster than 0.9.3)

This commit is contained in:
Sour 2017-12-04 00:09:11 -05:00
parent 6588872e42
commit 2b851d5d38
9 changed files with 184 additions and 93 deletions

View file

@ -409,93 +409,92 @@ void Console::Run()
UpdateNesModel(true); UpdateNesModel(true);
bool crashed = false; bool crashed = false;
while(true) { try {
try { while(true) {
_cpu->Exec(); _cpu->Exec();
} catch(const std::runtime_error &ex) {
crashed = true;
MessageManager::DisplayMessage("Error", "GameCrash", ex.what());
break;
}
if(_resetRequested) { if(_resetRequested) {
//Used by NSF player to reset console after changing track //Used by NSF player to reset console after changing track
//Also used with DisablePpuReset option to reset mid-frame //Also used with DisablePpuReset option to reset mid-frame
MovieManager::Stop(); MovieManager::Stop();
ResetComponents(true); ResetComponents(true);
_resetRequested = false; _resetRequested = false;
}
uint32_t currentFrameNumber = PPU::GetFrameCount();
if(currentFrameNumber != lastFrameNumber) {
_rewindManager->ProcessEndOfFrame();
EmulationSettings::DisableOverclocking(_disableOcNextFrame || NsfMapper::GetInstance());
_disableOcNextFrame = false;
//Sleep until we're ready to start the next frame
clockTimer.WaitUntil(targetTime);
if(!_pauseLock.IsFree()) {
//Need to temporarely pause the emu (to save/load a state, etc.)
_runLock.Release();
//Spin wait until we are allowed to start again
_pauseLock.WaitForRelease();
_runLock.Acquire();
} }
bool paused = EmulationSettings::IsPaused(); uint32_t currentFrameNumber = PPU::GetFrameCount();
if(paused && !_stop) { if(currentFrameNumber != lastFrameNumber) {
MessageManager::SendNotification(ConsoleNotificationType::GamePaused); _rewindManager->ProcessEndOfFrame();
EmulationSettings::DisableOverclocking(_disableOcNextFrame || NsfMapper::GetInstance());
//Prevent audio from looping endlessly while game is paused _disableOcNextFrame = false;
SoundMixer::StopAudio();
_runLock.Release(); //Sleep until we're ready to start the next frame
clockTimer.WaitUntil(targetTime);
PlatformUtilities::EnableScreensaver();
while(paused && !_stop) { if(!_pauseLock.IsFree()) {
//Sleep until emulation is resumed //Need to temporarely pause the emu (to save/load a state, etc.)
std::this_thread::sleep_for(std::chrono::duration<int, std::milli>(30)); _runLock.Release();
paused = EmulationSettings::IsPaused();
//Spin wait until we are allowed to start again
_pauseLock.WaitForRelease();
_runLock.Acquire();
} }
if(EmulationSettings::CheckFlag(EmulationFlags::DebuggerWindowEnabled)) { bool paused = EmulationSettings::IsPaused();
//Prevent pausing when debugger is active if(paused && !_stop) {
EmulationSettings::ClearFlags(EmulationFlags::Paused); MessageManager::SendNotification(ConsoleNotificationType::GamePaused);
//Prevent audio from looping endlessly while game is paused
SoundMixer::StopAudio();
_runLock.Release();
PlatformUtilities::EnableScreensaver();
while(paused && !_stop) {
//Sleep until emulation is resumed
std::this_thread::sleep_for(std::chrono::duration<int, std::milli>(30));
paused = EmulationSettings::IsPaused();
}
if(EmulationSettings::CheckFlag(EmulationFlags::DebuggerWindowEnabled)) {
//Prevent pausing when debugger is active
EmulationSettings::ClearFlags(EmulationFlags::Paused);
}
PlatformUtilities::DisableScreensaver();
_runLock.Acquire();
MessageManager::SendNotification(ConsoleNotificationType::GameResumed);
} }
PlatformUtilities::DisableScreensaver(); _systemActionManager->ProcessSystemActions();
_runLock.Acquire();
MessageManager::SendNotification(ConsoleNotificationType::GameResumed);
}
_systemActionManager->ProcessSystemActions();
shared_ptr<Debugger> debugger = _debugger; shared_ptr<Debugger> debugger = _debugger;
if(debugger) { if(debugger) {
debugger->ProcessEvent(EventType::StartFrame); debugger->ProcessEvent(EventType::StartFrame);
} }
//Get next target time, and adjust based on whether we are ahead or behind //Get next target time, and adjust based on whether we are ahead or behind
double timeLag = EmulationSettings::GetEmulationSpeed() == 0 ? 0 : clockTimer.GetElapsedMS() - targetTime; double timeLag = EmulationSettings::GetEmulationSpeed() == 0 ? 0 : clockTimer.GetElapsedMS() - targetTime;
UpdateNesModel(true); UpdateNesModel(true);
targetTime = GetFrameDelay(); targetTime = GetFrameDelay();
clockTimer.Reset(); clockTimer.Reset();
targetTime -= timeLag; targetTime -= timeLag;
if(targetTime < 0) { if(targetTime < 0) {
targetTime = 0; targetTime = 0;
} }
lastFrameNumber = PPU::GetFrameCount(); lastFrameNumber = PPU::GetFrameCount();
if(_stop) { if(_stop) {
_stop = false; _stop = false;
break; break;
}
} }
} }
} catch(const std::runtime_error &ex) {
crashed = true;
MessageManager::DisplayMessage("Error", "GameCrash", ex.what());
} }
if(!crashed) { if(!crashed) {

View file

@ -46,6 +46,7 @@ void PPU::Reset()
{ {
_cyclesNeeded = 0; _cyclesNeeded = 0;
_needStateUpdate = false;
_prevRenderingEnabled = false; _prevRenderingEnabled = false;
_renderingEnabled = false; _renderingEnabled = false;
@ -278,7 +279,8 @@ uint8_t PPU::ReadRAM(uint16_t addr)
} }
UpdateVideoRamAddr(); UpdateVideoRamAddr();
_ignoreVramRead = 2; _ignoreVramRead = 6;
_needStateUpdate = true;
} }
break; break;
@ -344,6 +346,7 @@ void PPU::WriteRAM(uint16_t addr, uint8_t value)
//Video RAM update is apparently delayed by 2-3 PPU cycles (based on Visual NES findings) //Video RAM update is apparently delayed by 2-3 PPU cycles (based on Visual NES findings)
//A 3-cycle delay causes issues with the scanline test. //A 3-cycle delay causes issues with the scanline test.
_needStateUpdate = true;
_updateVramAddrDelay = 2; _updateVramAddrDelay = 2;
_updateVramAddr = _state.TmpVideoRamAddr; _updateVramAddr = _state.TmpVideoRamAddr;
Debugger::SetLastFramePpuScroll(_updateVramAddr, _state.XScroll, false); Debugger::SetLastFramePpuScroll(_updateVramAddr, _state.XScroll, false);
@ -444,6 +447,10 @@ void PPU::SetMaskRegister(uint8_t value)
_flags.SpritesEnabled = (_state.Mask & 0x10) == 0x10; _flags.SpritesEnabled = (_state.Mask & 0x10) == 0x10;
_flags.IntensifyBlue = (_state.Mask & 0x80) == 0x80; _flags.IntensifyBlue = (_state.Mask & 0x80) == 0x80;
if(_renderingEnabled != (_flags.BackgroundEnabled | _flags.SpritesEnabled)) {
_needStateUpdate = true;
}
UpdateMinimumDrawCycles(); UpdateMinimumDrawCycles();
UpdateGrayscaleAndIntensifyBits(); UpdateGrayscaleAndIntensifyBits();
@ -1109,9 +1116,21 @@ void PPU::Exec()
} }
} }
if(_needStateUpdate) {
UpdateState();
}
}
void PPU::UpdateState()
{
_needStateUpdate = false;
//Rendering enabled flag is apparently set with a 1 cycle delay (i.e setting it at cycle 5 will render cycle 6 like cycle 5 and then take the new settings for cycle 7) //Rendering enabled flag is apparently set with a 1 cycle delay (i.e setting it at cycle 5 will render cycle 6 like cycle 5 and then take the new settings for cycle 7)
_prevRenderingEnabled = _renderingEnabled; _prevRenderingEnabled = _renderingEnabled;
_renderingEnabled = _flags.BackgroundEnabled | _flags.SpritesEnabled; _renderingEnabled = _flags.BackgroundEnabled | _flags.SpritesEnabled;
if(_prevRenderingEnabled != _renderingEnabled) {
_needStateUpdate = true;
}
if(_updateVramAddrDelay > 0) { if(_updateVramAddrDelay > 0) {
_updateVramAddrDelay--; _updateVramAddrDelay--;
@ -1121,6 +1140,15 @@ void PPU::Exec()
//Trigger memory read when setting the vram address - needed by MMC3 IRQ counter //Trigger memory read when setting the vram address - needed by MMC3 IRQ counter
//"4) Should be clocked when A12 changes to 1 via $2006 write" //"4) Should be clocked when A12 changes to 1 via $2006 write"
_mapper->ReadVRAM(_state.VideoRamAddr, MemoryOperationType::Read); _mapper->ReadVRAM(_state.VideoRamAddr, MemoryOperationType::Read);
} else {
_needStateUpdate = true;
}
}
if(_ignoreVramRead > 0) {
_ignoreVramRead--;
if(_ignoreVramRead > 0) {
_needStateUpdate = true;
} }
} }
} }
@ -1148,10 +1176,6 @@ void PPU::ExecStatic()
Instance->_cyclesNeeded--; Instance->_cyclesNeeded--;
} }
} }
if(PPU::Instance->_ignoreVramRead) {
PPU::Instance->_ignoreVramRead--;
}
} }
void PPU::StreamState(bool saving) void PPU::StreamState(bool saving)
@ -1183,7 +1207,7 @@ void PPU::StreamState(bool saving)
_nextTile.PaletteOffset, _nextTile.TileAddr, _previousTile.LowByte, _previousTile.HighByte, _previousTile.PaletteOffset, _spriteIndex, _spriteCount, _nextTile.PaletteOffset, _nextTile.TileAddr, _previousTile.LowByte, _previousTile.HighByte, _previousTile.PaletteOffset, _spriteIndex, _spriteCount,
_secondaryOAMAddr, _sprite0Visible, _oamCopybuffer, _spriteInRange, _sprite0Added, _spriteAddrH, _spriteAddrL, _oamCopyDone, _nesModel, unusedSpriteDmaAddr, _secondaryOAMAddr, _sprite0Visible, _oamCopybuffer, _spriteInRange, _sprite0Added, _spriteAddrH, _spriteAddrL, _oamCopyDone, _nesModel, unusedSpriteDmaAddr,
unusedSpriteDmaCounter, _prevRenderingEnabled, _renderingEnabled, _openBus, _ignoreVramRead, unusedSkipScrollIncrement, paletteRam, spriteRam, secondarySpriteRam, unusedSpriteDmaCounter, _prevRenderingEnabled, _renderingEnabled, _openBus, _ignoreVramRead, unusedSkipScrollIncrement, paletteRam, spriteRam, secondarySpriteRam,
openBusDecayStamp, _cyclesNeeded, disablePpu2004Reads, disablePaletteRead, disableOamAddrBug, _overflowBugCounter, _updateVramAddr, _updateVramAddrDelay); openBusDecayStamp, _cyclesNeeded, disablePpu2004Reads, disablePaletteRead, disableOamAddrBug, _overflowBugCounter, _updateVramAddr, _updateVramAddrDelay, _needStateUpdate);
for(int i = 0; i < 64; i++) { for(int i = 0; i < 64; i++) {
Stream(_spriteTiles[i].SpriteX, _spriteTiles[i].LowByte, _spriteTiles[i].HighByte, _spriteTiles[i].PaletteOffset, _spriteTiles[i].HorizontalMirror, _spriteTiles[i].BackgroundPriority); Stream(_spriteTiles[i].SpriteX, _spriteTiles[i].LowByte, _spriteTiles[i].HighByte, _spriteTiles[i].PaletteOffset, _spriteTiles[i].HorizontalMirror, _spriteTiles[i].BackgroundPriority);

View file

@ -87,6 +87,7 @@ class PPU : public IMemoryHandler, public Snapshotable
bool _oamCopyDone; bool _oamCopyDone;
uint8_t _overflowBugCounter; uint8_t _overflowBugCounter;
bool _needStateUpdate;
bool _renderingEnabled; bool _renderingEnabled;
bool _prevRenderingEnabled; bool _prevRenderingEnabled;
@ -142,6 +143,8 @@ class PPU : public IMemoryHandler, public Snapshotable
void UpdateGrayscaleAndIntensifyBits(); void UpdateGrayscaleAndIntensifyBits();
virtual void SendFrame(); virtual void SendFrame();
void UpdateState();
void UpdateApuStatus(); void UpdateApuStatus();
PPURegisters GetRegisterID(uint16_t addr) PPURegisters GetRegisterID(uint16_t addr)

View file

@ -9,11 +9,15 @@ void Snapshotable::StreamStartBlock()
throw new std::runtime_error("Cannot start a new block before ending the last block"); throw new std::runtime_error("Cannot start a new block before ending the last block");
} }
_blockBuffer = new uint8_t[0xFFFFF];
if(!_saving) { if(!_saving) {
InternalStream(_blockSize); InternalStream(_blockSize);
ArrayInfo<uint8_t> arrayInfo = { _blockBuffer, std::min((uint32_t)0xFFFFF, _blockSize) }; _blockSize = std::min(_blockSize, (uint32_t)0xFFFFF);
_blockBuffer = new uint8_t[_blockSize];
ArrayInfo<uint8_t> arrayInfo = { _blockBuffer, _blockSize };
InternalStream(arrayInfo); InternalStream(arrayInfo);
} else {
_blockSize = 0x100;
_blockBuffer = new uint8_t[_blockSize];
} }
_blockPosition = 0; _blockPosition = 0;
_inBlock = true; _inBlock = true;
@ -67,7 +71,8 @@ void Snapshotable::SaveSnapshot(ostream* file)
{ {
_stateVersion = SaveStateManager::FileFormatVersion; _stateVersion = SaveStateManager::FileFormatVersion;
_stream = new uint8_t[0xFFFFF]; _streamSize = 0x1000;
_stream = new uint8_t[_streamSize];
_position = 0; _position = 0;
_saving = true; _saving = true;
@ -86,12 +91,11 @@ void Snapshotable::LoadSnapshot(istream* file, uint32_t stateVersion)
{ {
_stateVersion = stateVersion; _stateVersion = stateVersion;
_stream = new uint8_t[0xFFFFF];
memset((char*)_stream, 0, 0xFFFFF);
_position = 0; _position = 0;
_saving = false; _saving = false;
file->read((char*)&_streamSize, sizeof(_streamSize)); file->read((char*)&_streamSize, sizeof(_streamSize));
_stream = new uint8_t[_streamSize];
file->read((char*)_stream, _streamSize); file->read((char*)_stream, _streamSize);
StreamState(_saving); StreamState(_saving);

View file

@ -51,12 +51,54 @@ private:
bool _saving; bool _saving;
private: private:
void EnsureCapacity(uint32_t typeSize)
{
//Make sure the current block/stream is large enough to fit the next write
uint32_t oldSize;
uint32_t sizeRequired;
uint8_t *oldBuffer;
if(_inBlock) {
oldBuffer = _blockBuffer;
oldSize = _blockSize;
sizeRequired = _blockPosition + typeSize;
} else {
oldBuffer = _stream;
oldSize = _streamSize;
sizeRequired = _position + typeSize;
}
uint8_t *newBuffer = nullptr;
uint32_t newSize = oldSize * 2;
if(oldSize < sizeRequired) {
while(newSize < sizeRequired) {
newSize *= 2;
}
newBuffer = new uint8_t[newSize];
memcpy(newBuffer, oldBuffer, oldSize);
delete[] oldBuffer;
}
if(newBuffer) {
if(_inBlock) {
_blockBuffer = newBuffer;
_blockSize = newSize;
} else {
_stream = newBuffer;
_streamSize = newSize;
}
}
}
template<typename T> template<typename T>
void StreamElement(T &value, T defaultValue = T()) void StreamElement(T &value, T defaultValue = T())
{ {
if(_saving) { if(_saving) {
uint8_t* bytes = (uint8_t*)&value; uint8_t* bytes = (uint8_t*)&value;
int typeSize = sizeof(T); int typeSize = sizeof(T);
EnsureCapacity(typeSize);
for(int i = 0; i < typeSize; i++) { for(int i = 0; i < typeSize; i++) {
if(_inBlock) { if(_inBlock) {
_blockBuffer[_blockPosition++] = bytes[i]; _blockBuffer[_blockPosition++] = bytes[i];

View file

@ -67,6 +67,13 @@ void TraceLogger::StopLogging()
} }
} }
void TraceLogger::LogStatic(char *log)
{
if(_instance && _instance->_logToFile && _instance->_options.ShowExtraInfo) {
LogStatic(string(log));
}
}
void TraceLogger::LogStatic(string log) void TraceLogger::LogStatic(string log)
{ {
if(_instance && _instance->_logToFile && _instance->_options.ShowExtraInfo) { if(_instance && _instance->_logToFile && _instance->_options.ShowExtraInfo) {

View file

@ -88,6 +88,7 @@ public:
const char* GetExecutionTrace(uint32_t lineCount); const char* GetExecutionTrace(uint32_t lineCount);
static void LogStatic(char *log);
static void LogStatic(string log); static void LogStatic(string log);
}; };

View file

@ -149,12 +149,15 @@ bool SoundManager::InitializeDirectSound(uint32_t sampleRate, bool isStereo)
// Release the temporary buffer. // Release the temporary buffer.
tempBuffer->Release(); tempBuffer->Release();
_playing = false;
return true; return true;
} }
void SoundManager::Release() void SoundManager::Release()
{ {
_playing = false;
_needReset = false; _needReset = false;
_lastWriteOffset = 0; _lastWriteOffset = 0;
@ -209,6 +212,7 @@ void SoundManager::Pause()
if(_secondaryBuffer) { if(_secondaryBuffer) {
_secondaryBuffer->Stop(); _secondaryBuffer->Stop();
} }
_playing = false;
} }
void SoundManager::Stop() void SoundManager::Stop()
@ -217,12 +221,14 @@ void SoundManager::Stop()
_secondaryBuffer->Stop(); _secondaryBuffer->Stop();
ClearSecondaryBuffer(); ClearSecondaryBuffer();
} }
_playing = false;
} }
void SoundManager::Play() void SoundManager::Play()
{ {
if(_secondaryBuffer) { if(_secondaryBuffer) {
_secondaryBuffer->Play(0, 0, DSBPLAY_LOOPING); _secondaryBuffer->Play(0, 0, DSBPLAY_LOOPING);
_playing = true;
} }
} }
@ -232,6 +238,18 @@ void SoundManager::PlayBuffer(int16_t *soundBuffer, uint32_t sampleCount, uint32
if(_sampleRate != sampleRate || _isStereo != isStereo || _needReset) { if(_sampleRate != sampleRate || _isStereo != isStereo || _needReset) {
Release(); Release();
InitializeDirectSound(sampleRate, isStereo); InitializeDirectSound(sampleRate, isStereo);
_secondaryBuffer->SetFrequency(sampleRate);
_emulationSpeed = 100;
}
uint32_t emulationSpeed = EmulationSettings::GetEmulationSpeed();
if(emulationSpeed != _emulationSpeed) {
uint32_t targetRate = sampleRate;
if(emulationSpeed > 0 && emulationSpeed < 100) {
targetRate = (uint32_t)(targetRate * ((double)emulationSpeed / 100.0));
}
_secondaryBuffer->SetFrequency(targetRate);
_emulationSpeed = emulationSpeed;
} }
if(isStereo) { if(isStereo) {
@ -261,17 +279,9 @@ void SoundManager::PlayBuffer(int16_t *soundBuffer, uint32_t sampleCount, uint32
_secondaryBuffer->SetCurrentPosition(_lastWriteOffset - byteLatency); _secondaryBuffer->SetCurrentPosition(_lastWriteOffset - byteLatency);
} }
uint32_t targetRate = sampleRate;
if(EmulationSettings::GetEmulationSpeed() > 0 && EmulationSettings::GetEmulationSpeed() < 100) {
targetRate = (uint32_t)(targetRate * ((double)EmulationSettings::GetEmulationSpeed() / 100.0));
}
_secondaryBuffer->SetFrequency(targetRate);
CopyToSecondaryBuffer((uint8_t*)soundBuffer, soundBufferSize); CopyToSecondaryBuffer((uint8_t*)soundBuffer, soundBufferSize);
DWORD status; if(!_playing && _lastWriteOffset >= byteLatency) {
_secondaryBuffer->GetStatus(&status);
if(!(status & DSBSTATUS_PLAYING) && _lastWriteOffset >= byteLatency) {
Play(); Play();
} }
} }

View file

@ -28,7 +28,6 @@ private:
vector<SoundDeviceInfo> GetAvailableDeviceInfo(); vector<SoundDeviceInfo> GetAvailableDeviceInfo();
static bool CALLBACK DirectSoundEnumProc(LPGUID lpGUID, LPCWSTR lpszDesc, LPCSTR lpszDrvName, LPVOID lpContext); static bool CALLBACK DirectSoundEnumProc(LPGUID lpGUID, LPCWSTR lpszDesc, LPCSTR lpszDrvName, LPVOID lpContext);
bool InitializeDirectSound(uint32_t sampleRate, bool isStereo); bool InitializeDirectSound(uint32_t sampleRate, bool isStereo);
void ShutdownDirectSound();
void ClearSecondaryBuffer(); void ClearSecondaryBuffer();
void CopyToSecondaryBuffer(uint8_t *data, uint32_t size); void CopyToSecondaryBuffer(uint8_t *data, uint32_t size);
@ -41,6 +40,8 @@ private:
uint16_t _previousLatency = 0; uint16_t _previousLatency = 0;
uint32_t _sampleRate = 0; uint32_t _sampleRate = 0;
bool _isStereo = false; bool _isStereo = false;
bool _playing = false;
uint32_t _emulationSpeed = 100;
IDirectSound8* _directSound; IDirectSound8* _directSound;
IDirectSoundBuffer* _primaryBuffer; IDirectSoundBuffer* _primaryBuffer;