Performance: Code optimizations (7-10% faster than 0.9.3)
This commit is contained in:
parent
6588872e42
commit
2b851d5d38
9 changed files with 184 additions and 93 deletions
143
Core/Console.cpp
143
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<int, std::milli>(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<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();
|
||||
_runLock.Acquire();
|
||||
MessageManager::SendNotification(ConsoleNotificationType::GameResumed);
|
||||
}
|
||||
|
||||
_systemActionManager->ProcessSystemActions();
|
||||
_systemActionManager->ProcessSystemActions();
|
||||
|
||||
shared_ptr<Debugger> debugger = _debugger;
|
||||
if(debugger) {
|
||||
debugger->ProcessEvent(EventType::StartFrame);
|
||||
}
|
||||
shared_ptr<Debugger> 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) {
|
||||
|
|
36
Core/PPU.cpp
36
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);
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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<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);
|
||||
} 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);
|
||||
|
||||
|
|
|
@ -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<typename T>
|
||||
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];
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -88,6 +88,7 @@ public:
|
|||
|
||||
const char* GetExecutionTrace(uint32_t lineCount);
|
||||
|
||||
static void LogStatic(char *log);
|
||||
static void LogStatic(string log);
|
||||
|
||||
};
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -28,7 +28,6 @@ private:
|
|||
vector<SoundDeviceInfo> 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;
|
||||
|
|
Loading…
Add table
Reference in a new issue