From 2b851d5d38451276f7033c226b316f088b8805c6 Mon Sep 17 00:00:00 2001 From: Sour Date: Mon, 4 Dec 2017 00:09:11 -0500 Subject: [PATCH] Performance: Code optimizations (7-10% faster than 0.9.3) --- Core/Console.cpp | 143 +++++++++++++++++++-------------------- Core/PPU.cpp | 36 ++++++++-- Core/PPU.h | 3 + Core/Snapshotable.cpp | 14 ++-- Core/Snapshotable.h | 42 ++++++++++++ Core/TraceLogger.cpp | 7 ++ Core/TraceLogger.h | 1 + Windows/SoundManager.cpp | 28 +++++--- Windows/SoundManager.h | 3 +- 9 files changed, 184 insertions(+), 93 deletions(-) diff --git a/Core/Console.cpp b/Core/Console.cpp index fa4f72f3..bdfd4a1b 100644 --- a/Core/Console.cpp +++ b/Core/Console.cpp @@ -409,93 +409,92 @@ void Console::Run() UpdateNesModel(true); bool crashed = false; - while(true) { - try { + try { + while(true) { _cpu->Exec(); - } catch(const std::runtime_error &ex) { - crashed = true; - MessageManager::DisplayMessage("Error", "GameCrash", ex.what()); - break; - } - if(_resetRequested) { - //Used by NSF player to reset console after changing track - //Also used with DisablePpuReset option to reset mid-frame - MovieManager::Stop(); - ResetComponents(true); - _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(); + if(_resetRequested) { + //Used by NSF player to reset console after changing track + //Also used with DisablePpuReset option to reset mid-frame + MovieManager::Stop(); + ResetComponents(true); + _resetRequested = false; } - bool paused = EmulationSettings::IsPaused(); - if(paused && !_stop) { - MessageManager::SendNotification(ConsoleNotificationType::GamePaused); - - //Prevent audio from looping endlessly while game is paused - SoundMixer::StopAudio(); + uint32_t currentFrameNumber = PPU::GetFrameCount(); + if(currentFrameNumber != lastFrameNumber) { + _rewindManager->ProcessEndOfFrame(); + EmulationSettings::DisableOverclocking(_disableOcNextFrame || NsfMapper::GetInstance()); + _disableOcNextFrame = false; - _runLock.Release(); - - PlatformUtilities::EnableScreensaver(); - while(paused && !_stop) { - //Sleep until emulation is resumed - std::this_thread::sleep_for(std::chrono::duration(30)); - paused = EmulationSettings::IsPaused(); + //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(); } - if(EmulationSettings::CheckFlag(EmulationFlags::DebuggerWindowEnabled)) { - //Prevent pausing when debugger is active - EmulationSettings::ClearFlags(EmulationFlags::Paused); + bool paused = EmulationSettings::IsPaused(); + if(paused && !_stop) { + 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(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(); - _runLock.Acquire(); - MessageManager::SendNotification(ConsoleNotificationType::GameResumed); - } - - _systemActionManager->ProcessSystemActions(); + _systemActionManager->ProcessSystemActions(); - shared_ptr debugger = _debugger; - if(debugger) { - debugger->ProcessEvent(EventType::StartFrame); - } + shared_ptr debugger = _debugger; + if(debugger) { + debugger->ProcessEvent(EventType::StartFrame); + } - //Get next target time, and adjust based on whether we are ahead or behind - double timeLag = EmulationSettings::GetEmulationSpeed() == 0 ? 0 : clockTimer.GetElapsedMS() - targetTime; - UpdateNesModel(true); - targetTime = GetFrameDelay(); + //Get next target time, and adjust based on whether we are ahead or behind + double timeLag = EmulationSettings::GetEmulationSpeed() == 0 ? 0 : clockTimer.GetElapsedMS() - targetTime; + UpdateNesModel(true); + targetTime = GetFrameDelay(); - clockTimer.Reset(); - targetTime -= timeLag; - if(targetTime < 0) { - targetTime = 0; - } + clockTimer.Reset(); + targetTime -= timeLag; + if(targetTime < 0) { + targetTime = 0; + } - lastFrameNumber = PPU::GetFrameCount(); - - if(_stop) { - _stop = false; - break; + lastFrameNumber = PPU::GetFrameCount(); + + if(_stop) { + _stop = false; + break; + } } } + } catch(const std::runtime_error &ex) { + crashed = true; + MessageManager::DisplayMessage("Error", "GameCrash", ex.what()); } if(!crashed) { diff --git a/Core/PPU.cpp b/Core/PPU.cpp index f97346fa..611e02d7 100644 --- a/Core/PPU.cpp +++ b/Core/PPU.cpp @@ -46,6 +46,7 @@ void PPU::Reset() { _cyclesNeeded = 0; + _needStateUpdate = false; _prevRenderingEnabled = false; _renderingEnabled = false; @@ -278,7 +279,8 @@ uint8_t PPU::ReadRAM(uint16_t addr) } UpdateVideoRamAddr(); - _ignoreVramRead = 2; + _ignoreVramRead = 6; + _needStateUpdate = true; } 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) //A 3-cycle delay causes issues with the scanline test. + _needStateUpdate = true; _updateVramAddrDelay = 2; _updateVramAddr = _state.TmpVideoRamAddr; Debugger::SetLastFramePpuScroll(_updateVramAddr, _state.XScroll, false); @@ -444,6 +447,10 @@ void PPU::SetMaskRegister(uint8_t value) _flags.SpritesEnabled = (_state.Mask & 0x10) == 0x10; _flags.IntensifyBlue = (_state.Mask & 0x80) == 0x80; + if(_renderingEnabled != (_flags.BackgroundEnabled | _flags.SpritesEnabled)) { + _needStateUpdate = true; + } + UpdateMinimumDrawCycles(); 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) _prevRenderingEnabled = _renderingEnabled; _renderingEnabled = _flags.BackgroundEnabled | _flags.SpritesEnabled; + if(_prevRenderingEnabled != _renderingEnabled) { + _needStateUpdate = true; + } if(_updateVramAddrDelay > 0) { _updateVramAddrDelay--; @@ -1121,6 +1140,15 @@ void PPU::Exec() //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" _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--; } } - - if(PPU::Instance->_ignoreVramRead) { - PPU::Instance->_ignoreVramRead--; - } } 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, _secondaryOAMAddr, _sprite0Visible, _oamCopybuffer, _spriteInRange, _sprite0Added, _spriteAddrH, _spriteAddrL, _oamCopyDone, _nesModel, unusedSpriteDmaAddr, 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++) { Stream(_spriteTiles[i].SpriteX, _spriteTiles[i].LowByte, _spriteTiles[i].HighByte, _spriteTiles[i].PaletteOffset, _spriteTiles[i].HorizontalMirror, _spriteTiles[i].BackgroundPriority); diff --git a/Core/PPU.h b/Core/PPU.h index d5711efe..b9eef2d0 100644 --- a/Core/PPU.h +++ b/Core/PPU.h @@ -87,6 +87,7 @@ class PPU : public IMemoryHandler, public Snapshotable bool _oamCopyDone; uint8_t _overflowBugCounter; + bool _needStateUpdate; bool _renderingEnabled; bool _prevRenderingEnabled; @@ -142,6 +143,8 @@ class PPU : public IMemoryHandler, public Snapshotable void UpdateGrayscaleAndIntensifyBits(); virtual void SendFrame(); + void UpdateState(); + void UpdateApuStatus(); PPURegisters GetRegisterID(uint16_t addr) diff --git a/Core/Snapshotable.cpp b/Core/Snapshotable.cpp index 301a6692..d3b0c335 100644 --- a/Core/Snapshotable.cpp +++ b/Core/Snapshotable.cpp @@ -9,11 +9,15 @@ void Snapshotable::StreamStartBlock() throw new std::runtime_error("Cannot start a new block before ending the last block"); } - _blockBuffer = new uint8_t[0xFFFFF]; if(!_saving) { InternalStream(_blockSize); - ArrayInfo arrayInfo = { _blockBuffer, std::min((uint32_t)0xFFFFF, _blockSize) }; + _blockSize = std::min(_blockSize, (uint32_t)0xFFFFF); + _blockBuffer = new uint8_t[_blockSize]; + ArrayInfo arrayInfo = { _blockBuffer, _blockSize }; InternalStream(arrayInfo); + } else { + _blockSize = 0x100; + _blockBuffer = new uint8_t[_blockSize]; } _blockPosition = 0; _inBlock = true; @@ -67,7 +71,8 @@ void Snapshotable::SaveSnapshot(ostream* file) { _stateVersion = SaveStateManager::FileFormatVersion; - _stream = new uint8_t[0xFFFFF]; + _streamSize = 0x1000; + _stream = new uint8_t[_streamSize]; _position = 0; _saving = true; @@ -86,12 +91,11 @@ void Snapshotable::LoadSnapshot(istream* file, uint32_t stateVersion) { _stateVersion = stateVersion; - _stream = new uint8_t[0xFFFFF]; - memset((char*)_stream, 0, 0xFFFFF); _position = 0; _saving = false; file->read((char*)&_streamSize, sizeof(_streamSize)); + _stream = new uint8_t[_streamSize]; file->read((char*)_stream, _streamSize); StreamState(_saving); diff --git a/Core/Snapshotable.h b/Core/Snapshotable.h index 7b87339f..f158d1aa 100644 --- a/Core/Snapshotable.h +++ b/Core/Snapshotable.h @@ -51,12 +51,54 @@ private: bool _saving; 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 void StreamElement(T &value, T defaultValue = T()) { if(_saving) { uint8_t* bytes = (uint8_t*)&value; int typeSize = sizeof(T); + + EnsureCapacity(typeSize); + for(int i = 0; i < typeSize; i++) { if(_inBlock) { _blockBuffer[_blockPosition++] = bytes[i]; diff --git a/Core/TraceLogger.cpp b/Core/TraceLogger.cpp index b38e6951..4f0f2770 100644 --- a/Core/TraceLogger.cpp +++ b/Core/TraceLogger.cpp @@ -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) { if(_instance && _instance->_logToFile && _instance->_options.ShowExtraInfo) { diff --git a/Core/TraceLogger.h b/Core/TraceLogger.h index 3be78ddc..1320b96d 100644 --- a/Core/TraceLogger.h +++ b/Core/TraceLogger.h @@ -88,6 +88,7 @@ public: const char* GetExecutionTrace(uint32_t lineCount); + static void LogStatic(char *log); static void LogStatic(string log); }; \ No newline at end of file diff --git a/Windows/SoundManager.cpp b/Windows/SoundManager.cpp index 9fd985fa..74162eb9 100644 --- a/Windows/SoundManager.cpp +++ b/Windows/SoundManager.cpp @@ -149,12 +149,15 @@ bool SoundManager::InitializeDirectSound(uint32_t sampleRate, bool isStereo) // Release the temporary buffer. tempBuffer->Release(); + _playing = false; + return true; } void SoundManager::Release() { + _playing = false; _needReset = false; _lastWriteOffset = 0; @@ -209,6 +212,7 @@ void SoundManager::Pause() if(_secondaryBuffer) { _secondaryBuffer->Stop(); } + _playing = false; } void SoundManager::Stop() @@ -217,12 +221,14 @@ void SoundManager::Stop() _secondaryBuffer->Stop(); ClearSecondaryBuffer(); } + _playing = false; } void SoundManager::Play() { if(_secondaryBuffer) { _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) { Release(); 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) { @@ -261,17 +279,9 @@ void SoundManager::PlayBuffer(int16_t *soundBuffer, uint32_t sampleCount, uint32 _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); - DWORD status; - _secondaryBuffer->GetStatus(&status); - if(!(status & DSBSTATUS_PLAYING) && _lastWriteOffset >= byteLatency) { + if(!_playing && _lastWriteOffset >= byteLatency) { Play(); } } \ No newline at end of file diff --git a/Windows/SoundManager.h b/Windows/SoundManager.h index b175638c..4dafb35a 100644 --- a/Windows/SoundManager.h +++ b/Windows/SoundManager.h @@ -28,7 +28,6 @@ private: vector GetAvailableDeviceInfo(); static bool CALLBACK DirectSoundEnumProc(LPGUID lpGUID, LPCWSTR lpszDesc, LPCSTR lpszDrvName, LPVOID lpContext); bool InitializeDirectSound(uint32_t sampleRate, bool isStereo); - void ShutdownDirectSound(); void ClearSecondaryBuffer(); void CopyToSecondaryBuffer(uint8_t *data, uint32_t size); @@ -41,6 +40,8 @@ private: uint16_t _previousLatency = 0; uint32_t _sampleRate = 0; bool _isStereo = false; + bool _playing = false; + uint32_t _emulationSpeed = 100; IDirectSound8* _directSound; IDirectSoundBuffer* _primaryBuffer;