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-audio.h"
|
||||||
#include "gba-io.h"
|
#include "gba-io.h"
|
||||||
|
#include "gba-thread.h"
|
||||||
#include "memory.h"
|
#include "memory.h"
|
||||||
|
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
|
@ -101,10 +102,41 @@ struct GBASerializedState* GBAMapState(int fd) {
|
||||||
return fileMemoryMap(fd, sizeof(struct GBASerializedState), MEMORY_WRITE);
|
return fileMemoryMap(fd, sizeof(struct GBASerializedState), MEMORY_WRITE);
|
||||||
}
|
}
|
||||||
|
|
||||||
struct GBASerializedState* GBAAloocateState(void) {
|
struct GBASerializedState* GBAAllocateState(void) {
|
||||||
return anonymousMemoryMap(sizeof(struct GBASerializedState));
|
return anonymousMemoryMap(sizeof(struct GBASerializedState));
|
||||||
}
|
}
|
||||||
|
|
||||||
void GBADeallocateState(struct GBASerializedState* state) {
|
void GBADeallocateState(struct GBASerializedState* state) {
|
||||||
mappedMemoryFree(state, sizeof(struct GBASerializedState));
|
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);
|
struct GBASerializedState* GBAAllocateState(void);
|
||||||
void GBADeallocateState(struct GBASerializedState* state);
|
void GBADeallocateState(struct GBASerializedState* state);
|
||||||
|
|
||||||
|
struct GBAThread;
|
||||||
|
void GBARecordFrame(struct GBAThread* thread);
|
||||||
|
void GBARewind(struct GBAThread* thread, int nStates);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
#include "arm.h"
|
#include "arm.h"
|
||||||
#include "debugger.h"
|
#include "debugger.h"
|
||||||
#include "gba.h"
|
#include "gba.h"
|
||||||
|
#include "gba-serialize.h"
|
||||||
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
|
@ -157,6 +158,12 @@ int GBAThreadStart(struct GBAThread* threadContext) {
|
||||||
threadContext->sync.videoFrameOn = 1;
|
threadContext->sync.videoFrameOn = 1;
|
||||||
threadContext->sync.videoFrameSkip = 0;
|
threadContext->sync.videoFrameSkip = 0;
|
||||||
|
|
||||||
|
threadContext->rewindBufferNext = threadContext->rewindBufferInterval;
|
||||||
|
threadContext->rewindBufferSize = 0;
|
||||||
|
if (threadContext->rewindBufferCapacity) {
|
||||||
|
threadContext->rewindBuffer = calloc(threadContext->rewindBufferCapacity, sizeof(void*));
|
||||||
|
}
|
||||||
|
|
||||||
MutexInit(&threadContext->stateMutex);
|
MutexInit(&threadContext->stateMutex);
|
||||||
ConditionInit(&threadContext->stateCond);
|
ConditionInit(&threadContext->stateCond);
|
||||||
|
|
||||||
|
@ -204,6 +211,14 @@ void GBAThreadJoin(struct GBAThread* threadContext) {
|
||||||
ConditionWake(&threadContext->sync.audioRequiredCond);
|
ConditionWake(&threadContext->sync.audioRequiredCond);
|
||||||
ConditionDeinit(&threadContext->sync.audioRequiredCond);
|
ConditionDeinit(&threadContext->sync.audioRequiredCond);
|
||||||
MutexDeinit(&threadContext->sync.audioBufferMutex);
|
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) {
|
void GBAThreadPause(struct GBAThread* threadContext) {
|
||||||
|
@ -293,6 +308,13 @@ void GBASyncPostFrame(struct GBASync* sync) {
|
||||||
MutexUnlock(&sync->videoFrameMutex);
|
MutexUnlock(&sync->videoFrameMutex);
|
||||||
|
|
||||||
struct GBAThread* thread = GBAThreadGetContext();
|
struct GBAThread* thread = GBAThreadGetContext();
|
||||||
|
if (thread->rewindBuffer) {
|
||||||
|
--thread->rewindBufferNext;
|
||||||
|
if (thread->rewindBufferNext <= 0) {
|
||||||
|
thread->rewindBufferNext = thread->rewindBufferInterval;
|
||||||
|
GBARecordFrame(thread);
|
||||||
|
}
|
||||||
|
}
|
||||||
if (thread->frameCallback) {
|
if (thread->frameCallback) {
|
||||||
thread->frameCallback(thread);
|
thread->frameCallback(thread);
|
||||||
}
|
}
|
||||||
|
|
|
@ -52,6 +52,13 @@ struct GBAThread {
|
||||||
Condition audioRequiredCond;
|
Condition audioRequiredCond;
|
||||||
Mutex audioBufferMutex;
|
Mutex audioBufferMutex;
|
||||||
} sync;
|
} sync;
|
||||||
|
|
||||||
|
int rewindBufferSize;
|
||||||
|
int rewindBufferCapacity;
|
||||||
|
int rewindBufferInterval;
|
||||||
|
int rewindBufferNext;
|
||||||
|
struct GBASerializedState** rewindBuffer;
|
||||||
|
int rewindBufferWriteOffset;
|
||||||
};
|
};
|
||||||
|
|
||||||
int GBAThreadStart(struct GBAThread* threadContext);
|
int GBAThreadStart(struct GBAThread* threadContext);
|
||||||
|
|
|
@ -80,6 +80,8 @@ int main(int argc, char** argv) {
|
||||||
context.cleanCallback = _GBASDLClean;
|
context.cleanCallback = _GBASDLClean;
|
||||||
context.frameCallback = 0;
|
context.frameCallback = 0;
|
||||||
context.userData = &renderer;
|
context.userData = &renderer;
|
||||||
|
context.rewindBufferCapacity = 10;
|
||||||
|
context.rewindBufferInterval = 30;
|
||||||
GBAThreadStart(&context);
|
GBAThreadStart(&context);
|
||||||
|
|
||||||
_GBASDLRunloop(&context, &renderer);
|
_GBASDLRunloop(&context, &renderer);
|
||||||
|
|
|
@ -68,6 +68,10 @@ static void _GBASDLHandleKeypress(struct GBAThread* context, const struct SDL_Ke
|
||||||
case SDLK_TAB:
|
case SDLK_TAB:
|
||||||
context->sync.audioWait = event->type != SDL_KEYDOWN;
|
context->sync.audioWait = event->type != SDL_KEYDOWN;
|
||||||
return;
|
return;
|
||||||
|
case SDLK_LEFTBRACKET:
|
||||||
|
GBAThreadPause(context);
|
||||||
|
GBARewind(context, 10);
|
||||||
|
GBAThreadUnpause(context);
|
||||||
default:
|
default:
|
||||||
if (event->type == SDL_KEYDOWN) {
|
if (event->type == SDL_KEYDOWN) {
|
||||||
if (event->keysym.mod & KMOD_CTRL) {
|
if (event->keysym.mod & KMOD_CTRL) {
|
||||||
|
|
Loading…
Reference in New Issue