Rerender only if there is actual change.

This commit is contained in:
Christian Speckner 2018-05-17 22:56:07 +02:00
parent 639b6af1e9
commit ed6eae6a67
5 changed files with 68 additions and 40 deletions

View File

@ -258,30 +258,17 @@ FBInitStatus FrameBuffer::createDisplay(const string& title,
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Int64 FrameBuffer::update(uInt32 maxCycles) void FrameBuffer::update()
{ {
// Determine which mode we are in (from the EventHandler) // Determine which mode we are in (from the EventHandler)
// Take care of S_EMULATE mode here, otherwise let the GUI // Take care of S_EMULATE mode here, otherwise let the GUI
// figure out what to draw // figure out what to draw
Int64 cycles = -1;
invalidate(); invalidate();
switch(myOSystem.eventHandler().state()) switch(myOSystem.eventHandler().state())
{ {
case EventHandlerState::EMULATION: case EventHandlerState::EMULATION:
{ {
// Run the console for one frame
// Note that the debugger can cause a breakpoint to occur, which changes
// the EventHandler state 'behind our back' - we need to check for that
cycles = myOSystem.console().tia().update(maxCycles);
#ifdef DEBUGGER_SUPPORT
if(myOSystem.eventHandler().state() != EventHandlerState::EMULATION) break;
#endif
if(myOSystem.eventHandler().frying())
myOSystem.console().fry();
// And update the screen
myTIASurface->render(); myTIASurface->render();
// Show frame statistics // Show frame statistics
@ -342,7 +329,7 @@ Int64 FrameBuffer::update(uInt32 maxCycles)
} }
case EventHandlerState::NONE: case EventHandlerState::NONE:
return -1; return;
} }
// Draw any pending messages // Draw any pending messages
@ -351,8 +338,6 @@ Int64 FrameBuffer::update(uInt32 maxCycles)
// Do any post-frame stuff // Do any post-frame stuff
postFrameUpdate(); postFrameUpdate();
return cycles;
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

View File

@ -116,7 +116,7 @@ class FrameBuffer
drawing the TIA, any pending menus, etc. Returns the numbers of CPU cycles drawing the TIA, any pending menus, etc. Returns the numbers of CPU cycles
spent during emulation, or -1 if not applicable. spent during emulation, or -1 if not applicable.
*/ */
Int64 update(uInt32 maxCycles = 50000); void update();
/** /**
Shows a message onscreen. Shows a message onscreen.

View File

@ -52,6 +52,7 @@
#include "SerialPort.hxx" #include "SerialPort.hxx"
#include "StateManager.hxx" #include "StateManager.hxx"
#include "Version.hxx" #include "Version.hxx"
#include "TIA.hxx"
#include "OSystem.hxx" #include "OSystem.hxx"
@ -646,23 +647,38 @@ void OSystem::mainLoop()
myEventHandler->poll(getTicks()); myEventHandler->poll(getTicks());
if(myQuitLoop) break; // Exit if the user wants to quit if(myQuitLoop) break; // Exit if the user wants to quit
Int64 totalCycles = 0; double timesliceSeconds;
const Int64 minCycles = myConsole ? myConsole->emulationTiming().minCyclesPerTimeslice() : 50000;
const Int64 maxCycles = myConsole ? myConsole->emulationTiming().maxCyclesPerTimeslice() : 0;
const uInt32 cyclesPerSecond = myConsole ? myConsole->emulationTiming().cyclesPerSecond() : 1;
do { if (myEventHandler->state() == EventHandlerState::EMULATION) {
Int64 cycles = myFrameBuffer->update(totalCycles > 0 ? minCycles - totalCycles : maxCycles); Int64 totalCycles = 0;
if (cycles < 0) break; const Int64 minCycles = myConsole ? myConsole->emulationTiming().minCyclesPerTimeslice() : 50000;
const Int64 maxCycles = myConsole ? myConsole->emulationTiming().maxCyclesPerTimeslice() : 0;
const uInt32 cyclesPerSecond = myConsole ? myConsole->emulationTiming().cyclesPerSecond() : 1;
totalCycles += cycles; do {
} while (totalCycles < minCycles); Int64 cycles = myConsole ? myConsole->tia().update(totalCycles > 0 ? minCycles - totalCycles : maxCycles) : 0;
duration<double> timeslice ( totalCycles += cycles;
(totalCycles > 0) ? } while (myConsole && totalCycles < minCycles && myEventHandler->state() == EventHandlerState::EMULATION);
static_cast<double>(totalCycles) / static_cast<double>(cyclesPerSecond) :
1. / 30. if (myEventHandler->state() == EventHandlerState::EMULATION && myEventHandler->frying())
); myConsole->fry();
timesliceSeconds = static_cast<double>(totalCycles) / static_cast<double>(cyclesPerSecond);
}
else
timesliceSeconds = 1. / 30.;
if (myEventHandler->state() == EventHandlerState::EMULATION) {
if (myConsole && myConsole->tia().newFramePending()) {
myFrameBuffer->update();
myConsole->tia().clearNewFramePending();
}
}
else
myFrameBuffer->update();
duration<double> timeslice(timesliceSeconds);
virtualTime += duration_cast<high_resolution_clock::duration>(timeslice); virtualTime += duration_cast<high_resolution_clock::duration>(timeslice);
time_point<high_resolution_clock> now = high_resolution_clock::now(); time_point<high_resolution_clock> now = high_resolution_clock::now();
@ -670,7 +686,7 @@ void OSystem::mainLoop()
if (duration_cast<duration<double>>(now - virtualTime).count() > 0) if (duration_cast<duration<double>>(now - virtualTime).count() > 0)
virtualTime = now; virtualTime = now;
else if (virtualTime > now) { else if (virtualTime > now) {
if (busyWait && totalCycles > 0) { if (busyWait && myEventHandler->state() == EventHandlerState::EMULATION) {
while (high_resolution_clock::now() < virtualTime); while (high_resolution_clock::now() < virtualTime);
} }
else std::this_thread::sleep_until(virtualTime); else std::this_thread::sleep_until(virtualTime);

View File

@ -180,6 +180,8 @@ void TIA::reset()
frameReset(); // Recalculate the size of the display frameReset(); // Recalculate the size of the display
} }
myNewFramePending = false;
// Must be done last, after all other items have reset // Must be done last, after all other items have reset
enableFixedColors(mySettings.getBool(mySettings.getBool("dev.settings") ? "dev.debugcolors" : "plr.debugcolors")); enableFixedColors(mySettings.getBool(mySettings.getBool("dev.settings") ? "dev.debugcolors" : "plr.debugcolors"));
setFixedColorPalette(mySettings.getString("tia.dbgcolors")); setFixedColorPalette(mySettings.getString("tia.dbgcolors"));
@ -192,6 +194,7 @@ void TIA::reset()
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void TIA::frameReset() void TIA::frameReset()
{ {
memset(myBackBuffer, 0, 160 * TIAConstants::frameBufferHeight);
memset(myFramebuffer, 0, 160 * TIAConstants::frameBufferHeight); memset(myFramebuffer, 0, 160 * TIAConstants::frameBufferHeight);
enableColorLoss(mySettings.getBool("dev.settings") ? "dev.colorloss" : "plr.colorloss"); enableColorLoss(mySettings.getBool("dev.settings") ? "dev.colorloss" : "plr.colorloss");
} }
@ -778,7 +781,9 @@ bool TIA::saveDisplay(Serializer& out) const
{ {
try try
{ {
out.putByteArray(myFramebuffer, 160*TIAConstants::frameBufferHeight); out.putByteArray(myFramebuffer, 160* TIAConstants::frameBufferHeight);
out.putByteArray(myBackBuffer, 160 * TIAConstants::frameBufferHeight);
out.putBool(myNewFramePending);
} }
catch(...) catch(...)
{ {
@ -796,6 +801,8 @@ bool TIA::loadDisplay(Serializer& in)
{ {
// Reset frame buffer pointer and data // Reset frame buffer pointer and data
in.getByteArray(myFramebuffer, 160*TIAConstants::frameBufferHeight); in.getByteArray(myFramebuffer, 160*TIAConstants::frameBufferHeight);
in.getByteArray(myBackBuffer, 160 * TIAConstants::frameBufferHeight);
myNewFramePending = in.getBool();
} }
catch(...) catch(...)
{ {
@ -1149,12 +1156,15 @@ void TIA::onFrameComplete()
myCyclesAtFrameStart = mySystem->cycles(); myCyclesAtFrameStart = mySystem->cycles();
if (myXAtRenderingStart > 0) if (myXAtRenderingStart > 0)
memset(myFramebuffer, 0, myXAtRenderingStart); memset(myBackBuffer, 0, myXAtRenderingStart);
// Blank out any extra lines not drawn this frame // Blank out any extra lines not drawn this frame
const Int32 missingScanlines = myFrameManager->missingScanlines(); const Int32 missingScanlines = myFrameManager->missingScanlines();
if (missingScanlines > 0) if (missingScanlines > 0)
memset(myFramebuffer + 160 * myFrameManager->getY(), 0, missingScanlines * 160); memset(myBackBuffer + 160 * myFrameManager->getY(), 0, missingScanlines * 160);
memcpy(&myFramebuffer, &myBackBuffer, 160 * TIAConstants::frameBufferHeight);
myNewFramePending = true;
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -1265,7 +1275,7 @@ void TIA::applyRsync()
myHctrDelta = 225 - myHctr; myHctrDelta = 225 - myHctr;
if (myFrameManager->isRendering()) if (myFrameManager->isRendering())
memset(myFramebuffer + myFrameManager->getY() * 160 + x, 0, 160 - x); memset(myBackBuffer + myFrameManager->getY() * 160 + x, 0, 160 - x);
myHctr = 225; myHctr = 225;
} }
@ -1304,7 +1314,7 @@ void TIA::cloneLastLine()
if (!myFrameManager->isRendering() || y == 0) return; if (!myFrameManager->isRendering() || y == 0) return;
uInt8* buffer = myFramebuffer; uInt8* buffer = myBackBuffer;
memcpy(buffer + y * 160, buffer + (y-1) * 160, 160); memcpy(buffer + y * 160, buffer + (y-1) * 160, 160);
} }
@ -1377,7 +1387,7 @@ void TIA::renderPixel(uInt32 x, uInt32 y)
} }
} }
myFramebuffer[y * 160 + x] = color; myBackBuffer[y * 160 + x] = color;
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -1403,7 +1413,7 @@ void TIA::flushLineCache()
void TIA::clearHmoveComb() void TIA::clearHmoveComb()
{ {
if (myFrameManager->isRendering() && myHstate == HState::blank) if (myFrameManager->isRendering() && myHstate == HState::blank)
memset(myFramebuffer + myFrameManager->getY() * 160, myColorHBlank, 8); memset(myBackBuffer + myFrameManager->getY() * 160, myColorHBlank, 8);
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

View File

@ -201,6 +201,16 @@ class TIA : public Device
*/ */
uInt64 update(uInt32 maxCycles = 50000); uInt64 update(uInt32 maxCycles = 50000);
/**
Did we generate a new frame?
*/
bool newFramePending() { return myNewFramePending; }
/**
Clear the flag
*/
void clearNewFramePending() { myNewFramePending = false; }
/** /**
Returns a pointer to the internal frame buffer. Returns a pointer to the internal frame buffer.
*/ */
@ -657,6 +667,13 @@ class TIA : public Device
// Pointer to the internal color-index-based frame buffer // Pointer to the internal color-index-based frame buffer
uInt8 myFramebuffer[160 * TIAConstants::frameBufferHeight]; uInt8 myFramebuffer[160 * TIAConstants::frameBufferHeight];
// The frame is rendered to the backbuffer and only copied to the framebuffer
// upon completion
uInt8 myBackBuffer[160 * TIAConstants::frameBufferHeight];
// Did we emit a frame?
bool myNewFramePending;
/** /**
* Setting this to true injects random values into undefined reads. * Setting this to true injects random values into undefined reads.
*/ */