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)
// Take care of S_EMULATE mode here, otherwise let the GUI
// figure out what to draw
Int64 cycles = -1;
invalidate();
switch(myOSystem.eventHandler().state())
{
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();
// Show frame statistics
@ -342,7 +329,7 @@ Int64 FrameBuffer::update(uInt32 maxCycles)
}
case EventHandlerState::NONE:
return -1;
return;
}
// Draw any pending messages
@ -351,8 +338,6 @@ Int64 FrameBuffer::update(uInt32 maxCycles)
// Do any post-frame stuff
postFrameUpdate();
return cycles;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

View File

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

View File

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

View File

@ -180,6 +180,8 @@ void TIA::reset()
frameReset(); // Recalculate the size of the display
}
myNewFramePending = false;
// Must be done last, after all other items have reset
enableFixedColors(mySettings.getBool(mySettings.getBool("dev.settings") ? "dev.debugcolors" : "plr.debugcolors"));
setFixedColorPalette(mySettings.getString("tia.dbgcolors"));
@ -192,6 +194,7 @@ void TIA::reset()
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void TIA::frameReset()
{
memset(myBackBuffer, 0, 160 * TIAConstants::frameBufferHeight);
memset(myFramebuffer, 0, 160 * TIAConstants::frameBufferHeight);
enableColorLoss(mySettings.getBool("dev.settings") ? "dev.colorloss" : "plr.colorloss");
}
@ -778,7 +781,9 @@ bool TIA::saveDisplay(Serializer& out) const
{
try
{
out.putByteArray(myFramebuffer, 160*TIAConstants::frameBufferHeight);
out.putByteArray(myFramebuffer, 160* TIAConstants::frameBufferHeight);
out.putByteArray(myBackBuffer, 160 * TIAConstants::frameBufferHeight);
out.putBool(myNewFramePending);
}
catch(...)
{
@ -796,6 +801,8 @@ bool TIA::loadDisplay(Serializer& in)
{
// Reset frame buffer pointer and data
in.getByteArray(myFramebuffer, 160*TIAConstants::frameBufferHeight);
in.getByteArray(myBackBuffer, 160 * TIAConstants::frameBufferHeight);
myNewFramePending = in.getBool();
}
catch(...)
{
@ -1149,12 +1156,15 @@ void TIA::onFrameComplete()
myCyclesAtFrameStart = mySystem->cycles();
if (myXAtRenderingStart > 0)
memset(myFramebuffer, 0, myXAtRenderingStart);
memset(myBackBuffer, 0, myXAtRenderingStart);
// Blank out any extra lines not drawn this frame
const Int32 missingScanlines = myFrameManager->missingScanlines();
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;
if (myFrameManager->isRendering())
memset(myFramebuffer + myFrameManager->getY() * 160 + x, 0, 160 - x);
memset(myBackBuffer + myFrameManager->getY() * 160 + x, 0, 160 - x);
myHctr = 225;
}
@ -1304,7 +1314,7 @@ void TIA::cloneLastLine()
if (!myFrameManager->isRendering() || y == 0) return;
uInt8* buffer = myFramebuffer;
uInt8* buffer = myBackBuffer;
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()
{
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);
/**
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.
*/
@ -657,6 +667,13 @@ class TIA : public Device
// Pointer to the internal color-index-based frame buffer
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.
*/