mirror of https://github.com/mgba-emu/mgba.git
Simple rewind buffer
This commit is contained in:
parent
b1a648e46e
commit
705b6e9b13
|
@ -2,6 +2,7 @@
|
|||
|
||||
#include "gba-audio.h"
|
||||
#include "gba-io.h"
|
||||
#include "gba-thread.h"
|
||||
#include "memory.h"
|
||||
|
||||
#include <fcntl.h>
|
||||
|
@ -101,10 +102,41 @@ struct GBASerializedState* GBAMapState(int fd) {
|
|||
return fileMemoryMap(fd, sizeof(struct GBASerializedState), MEMORY_WRITE);
|
||||
}
|
||||
|
||||
struct GBASerializedState* GBAAloocateState(void) {
|
||||
struct GBASerializedState* GBAAllocateState(void) {
|
||||
return anonymousMemoryMap(sizeof(struct GBASerializedState));
|
||||
}
|
||||
|
||||
void GBADeallocateState(struct GBASerializedState* state) {
|
||||
mappedMemoryFree(state, sizeof(struct GBASerializedState));
|
||||
}
|
||||
|
||||
void GBARecordFrame(struct GBAThread* thread) {
|
||||
int offset = thread->rewindBufferWriteOffset;
|
||||
struct GBASerializedState* state = thread->rewindBuffer[offset];
|
||||
if (!state) {
|
||||
state = GBAAllocateState();
|
||||
thread->rewindBuffer[offset] = state;
|
||||
}
|
||||
GBASerialize(thread->gba, state);
|
||||
thread->rewindBufferSize = thread->rewindBufferSize == thread->rewindBufferCapacity ? thread->rewindBufferCapacity : thread->rewindBufferSize + 1;
|
||||
thread->rewindBufferWriteOffset = (offset + 1) % thread->rewindBufferSize;
|
||||
}
|
||||
|
||||
void GBARewind(struct GBAThread* thread, int nStates) {
|
||||
if (nStates > thread->rewindBufferSize || nStates < 0) {
|
||||
nStates = thread->rewindBufferSize;
|
||||
}
|
||||
if (nStates == 0) {
|
||||
return;
|
||||
}
|
||||
int offset = thread->rewindBufferWriteOffset - nStates;
|
||||
if (offset < 0) {
|
||||
offset += thread->rewindBufferSize;
|
||||
}
|
||||
struct GBASerializedState* state = thread->rewindBuffer[offset];
|
||||
if (!state) {
|
||||
return;
|
||||
}
|
||||
thread->rewindBufferSize -= nStates;
|
||||
GBADeserialize(thread->gba, state);
|
||||
}
|
||||
|
|
|
@ -233,4 +233,8 @@ struct GBASerializedState* GBAMapState(int fd);
|
|||
struct GBASerializedState* GBAAllocateState(void);
|
||||
void GBADeallocateState(struct GBASerializedState* state);
|
||||
|
||||
struct GBAThread;
|
||||
void GBARecordFrame(struct GBAThread* thread);
|
||||
void GBARewind(struct GBAThread* thread, int nStates);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
#include "arm.h"
|
||||
#include "debugger.h"
|
||||
#include "gba.h"
|
||||
#include "gba-serialize.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <signal.h>
|
||||
|
@ -157,6 +158,12 @@ int GBAThreadStart(struct GBAThread* threadContext) {
|
|||
threadContext->sync.videoFrameOn = 1;
|
||||
threadContext->sync.videoFrameSkip = 0;
|
||||
|
||||
threadContext->rewindBufferNext = threadContext->rewindBufferInterval;
|
||||
threadContext->rewindBufferSize = 0;
|
||||
if (threadContext->rewindBufferCapacity) {
|
||||
threadContext->rewindBuffer = calloc(threadContext->rewindBufferCapacity, sizeof(void*));
|
||||
}
|
||||
|
||||
MutexInit(&threadContext->stateMutex);
|
||||
ConditionInit(&threadContext->stateCond);
|
||||
|
||||
|
@ -204,6 +211,14 @@ void GBAThreadJoin(struct GBAThread* threadContext) {
|
|||
ConditionWake(&threadContext->sync.audioRequiredCond);
|
||||
ConditionDeinit(&threadContext->sync.audioRequiredCond);
|
||||
MutexDeinit(&threadContext->sync.audioBufferMutex);
|
||||
|
||||
int i;
|
||||
for (i = 0; i < threadContext->rewindBufferCapacity; ++i) {
|
||||
if (threadContext->rewindBuffer[i]) {
|
||||
GBADeallocateState(threadContext->rewindBuffer[i]);
|
||||
}
|
||||
}
|
||||
free(threadContext->rewindBuffer);
|
||||
}
|
||||
|
||||
void GBAThreadPause(struct GBAThread* threadContext) {
|
||||
|
@ -293,6 +308,13 @@ void GBASyncPostFrame(struct GBASync* sync) {
|
|||
MutexUnlock(&sync->videoFrameMutex);
|
||||
|
||||
struct GBAThread* thread = GBAThreadGetContext();
|
||||
if (thread->rewindBuffer) {
|
||||
--thread->rewindBufferNext;
|
||||
if (thread->rewindBufferNext <= 0) {
|
||||
thread->rewindBufferNext = thread->rewindBufferInterval;
|
||||
GBARecordFrame(thread);
|
||||
}
|
||||
}
|
||||
if (thread->frameCallback) {
|
||||
thread->frameCallback(thread);
|
||||
}
|
||||
|
|
|
@ -52,6 +52,13 @@ struct GBAThread {
|
|||
Condition audioRequiredCond;
|
||||
Mutex audioBufferMutex;
|
||||
} sync;
|
||||
|
||||
int rewindBufferSize;
|
||||
int rewindBufferCapacity;
|
||||
int rewindBufferInterval;
|
||||
int rewindBufferNext;
|
||||
struct GBASerializedState** rewindBuffer;
|
||||
int rewindBufferWriteOffset;
|
||||
};
|
||||
|
||||
int GBAThreadStart(struct GBAThread* threadContext);
|
||||
|
|
|
@ -80,6 +80,8 @@ int main(int argc, char** argv) {
|
|||
context.cleanCallback = _GBASDLClean;
|
||||
context.frameCallback = 0;
|
||||
context.userData = &renderer;
|
||||
context.rewindBufferCapacity = 10;
|
||||
context.rewindBufferInterval = 30;
|
||||
GBAThreadStart(&context);
|
||||
|
||||
_GBASDLRunloop(&context, &renderer);
|
||||
|
|
|
@ -68,6 +68,10 @@ static void _GBASDLHandleKeypress(struct GBAThread* context, const struct SDL_Ke
|
|||
case SDLK_TAB:
|
||||
context->sync.audioWait = event->type != SDL_KEYDOWN;
|
||||
return;
|
||||
case SDLK_LEFTBRACKET:
|
||||
GBAThreadPause(context);
|
||||
GBARewind(context, 10);
|
||||
GBAThreadUnpause(context);
|
||||
default:
|
||||
if (event->type == SDL_KEYDOWN) {
|
||||
if (event->keysym.mod & KMOD_CTRL) {
|
||||
|
|
Loading…
Reference in New Issue