mirror of https://github.com/mgba-emu/mgba.git
Refactor frame synchronization
This commit is contained in:
parent
2f00e3d146
commit
4ef6a70731
|
@ -28,6 +28,7 @@ static void* _GBAThreadRun(void* context) {
|
||||||
|
|
||||||
GBAInit(&gba);
|
GBAInit(&gba);
|
||||||
threadContext->gba = &gba;
|
threadContext->gba = &gba;
|
||||||
|
gba.sync = &threadContext->sync;
|
||||||
pthread_setspecific(contextKey, threadContext);
|
pthread_setspecific(contextKey, threadContext);
|
||||||
if (threadContext->renderer) {
|
if (threadContext->renderer) {
|
||||||
GBAVideoAssociateRenderer(&gba.video, threadContext->renderer);
|
GBAVideoAssociateRenderer(&gba.video, threadContext->renderer);
|
||||||
|
@ -65,9 +66,9 @@ static void* _GBAThreadRun(void* context) {
|
||||||
gba.keySource = &threadContext->activeKeys;
|
gba.keySource = &threadContext->activeKeys;
|
||||||
|
|
||||||
threadContext->started = 1;
|
threadContext->started = 1;
|
||||||
pthread_mutex_lock(&threadContext->mutex);
|
pthread_mutex_lock(&threadContext->startMutex);
|
||||||
pthread_cond_broadcast(&threadContext->cond);
|
pthread_cond_broadcast(&threadContext->startCond);
|
||||||
pthread_mutex_unlock(&threadContext->mutex);
|
pthread_mutex_unlock(&threadContext->startMutex);
|
||||||
|
|
||||||
if (threadContext->useDebugger) {
|
if (threadContext->useDebugger) {
|
||||||
ARMDebuggerRun(&debugger);
|
ARMDebuggerRun(&debugger);
|
||||||
|
@ -85,32 +86,80 @@ static void* _GBAThreadRun(void* context) {
|
||||||
|
|
||||||
int GBAThreadStart(struct GBAThread* threadContext) {
|
int GBAThreadStart(struct GBAThread* threadContext) {
|
||||||
// TODO: error check
|
// TODO: error check
|
||||||
{
|
pthread_mutex_init(&threadContext->startMutex, 0);
|
||||||
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
|
pthread_cond_init(&threadContext->startCond, 0);
|
||||||
threadContext->mutex = mutex;
|
|
||||||
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
|
|
||||||
threadContext->cond = cond;
|
|
||||||
}
|
|
||||||
pthread_mutex_init(&threadContext->mutex, 0);
|
|
||||||
pthread_cond_init(&threadContext->cond, 0);
|
|
||||||
|
|
||||||
pthread_mutex_lock(&threadContext->mutex);
|
pthread_mutex_init(&threadContext->sync.videoFrameMutex, 0);
|
||||||
|
pthread_cond_init(&threadContext->sync.videoFrameAvailableCond, 0);
|
||||||
|
pthread_cond_init(&threadContext->sync.videoFrameRequiredCond, 0);
|
||||||
|
|
||||||
|
pthread_mutex_lock(&threadContext->startMutex);
|
||||||
threadContext->activeKeys = 0;
|
threadContext->activeKeys = 0;
|
||||||
threadContext->started = 0;
|
threadContext->started = 0;
|
||||||
pthread_create(&threadContext->thread, 0, _GBAThreadRun, threadContext);
|
pthread_create(&threadContext->thread, 0, _GBAThreadRun, threadContext);
|
||||||
pthread_cond_wait(&threadContext->cond, &threadContext->mutex);
|
pthread_cond_wait(&threadContext->startCond, &threadContext->startMutex);
|
||||||
pthread_mutex_unlock(&threadContext->mutex);
|
pthread_mutex_unlock(&threadContext->startMutex);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void GBAThreadJoin(struct GBAThread* threadContext) {
|
void GBAThreadJoin(struct GBAThread* threadContext) {
|
||||||
|
pthread_mutex_lock(&threadContext->sync.videoFrameMutex);
|
||||||
|
threadContext->sync.videoFrameWait = 0;
|
||||||
|
pthread_cond_broadcast(&threadContext->sync.videoFrameRequiredCond);
|
||||||
|
pthread_mutex_unlock(&threadContext->sync.videoFrameMutex);
|
||||||
|
|
||||||
pthread_join(threadContext->thread, 0);
|
pthread_join(threadContext->thread, 0);
|
||||||
|
|
||||||
pthread_mutex_destroy(&threadContext->mutex);
|
pthread_mutex_destroy(&threadContext->startMutex);
|
||||||
pthread_cond_destroy(&threadContext->cond);
|
pthread_cond_destroy(&threadContext->startCond);
|
||||||
|
|
||||||
|
pthread_mutex_destroy(&threadContext->sync.videoFrameMutex);
|
||||||
|
pthread_cond_broadcast(&threadContext->sync.videoFrameAvailableCond);
|
||||||
|
pthread_cond_destroy(&threadContext->sync.videoFrameAvailableCond);
|
||||||
|
pthread_cond_broadcast(&threadContext->sync.videoFrameRequiredCond);
|
||||||
|
pthread_cond_destroy(&threadContext->sync.videoFrameRequiredCond);
|
||||||
}
|
}
|
||||||
|
|
||||||
struct GBAThread* GBAThreadGetContext(void) {
|
struct GBAThread* GBAThreadGetContext(void) {
|
||||||
return pthread_getspecific(contextKey);
|
return pthread_getspecific(contextKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GBASyncPostFrame(struct GBASync* sync) {
|
||||||
|
if (!sync) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
pthread_mutex_lock(&sync->videoFrameMutex);
|
||||||
|
++sync->videoFramePending;
|
||||||
|
--sync->videoFrameSkip;
|
||||||
|
pthread_cond_broadcast(&sync->videoFrameAvailableCond);
|
||||||
|
if (sync->videoFrameWait) {
|
||||||
|
pthread_cond_wait(&sync->videoFrameRequiredCond, &sync->videoFrameMutex);
|
||||||
|
}
|
||||||
|
pthread_mutex_unlock(&sync->videoFrameMutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GBASyncWaitFrameStart(struct GBASync* sync, int frameskip) {
|
||||||
|
if (!sync) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
pthread_mutex_lock(&sync->videoFrameMutex);
|
||||||
|
pthread_cond_broadcast(&sync->videoFrameRequiredCond);
|
||||||
|
pthread_cond_wait(&sync->videoFrameAvailableCond, &sync->videoFrameMutex);
|
||||||
|
sync->videoFramePending = 0;
|
||||||
|
sync->videoFrameSkip = frameskip;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GBASyncWaitFrameEnd(struct GBASync* sync) {
|
||||||
|
if (!sync) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
pthread_mutex_unlock(&sync->videoFrameMutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
int GBASyncDrawingFrame(struct GBASync* sync) {
|
||||||
|
return sync->videoFrameSkip <= 0;
|
||||||
|
}
|
||||||
|
|
|
@ -15,15 +15,31 @@ struct GBAThread {
|
||||||
int fd;
|
int fd;
|
||||||
const char* fname;
|
const char* fname;
|
||||||
int activeKeys;
|
int activeKeys;
|
||||||
|
int frameskip;
|
||||||
|
|
||||||
// Threading state
|
// Threading state
|
||||||
pthread_mutex_t mutex;
|
|
||||||
pthread_cond_t cond;
|
|
||||||
pthread_t thread;
|
pthread_t thread;
|
||||||
|
|
||||||
|
pthread_mutex_t startMutex;
|
||||||
|
pthread_cond_t startCond;
|
||||||
|
|
||||||
|
struct GBASync {
|
||||||
|
int videoFramePending;
|
||||||
|
int videoFrameWait;
|
||||||
|
int videoFrameSkip;
|
||||||
|
pthread_mutex_t videoFrameMutex;
|
||||||
|
pthread_cond_t videoFrameAvailableCond;
|
||||||
|
pthread_cond_t videoFrameRequiredCond;
|
||||||
|
} sync;
|
||||||
};
|
};
|
||||||
|
|
||||||
int GBAThreadStart(struct GBAThread* threadContext);
|
int GBAThreadStart(struct GBAThread* threadContext);
|
||||||
void GBAThreadJoin(struct GBAThread* threadContext);
|
void GBAThreadJoin(struct GBAThread* threadContext);
|
||||||
struct GBAThread* GBAThreadGetContext(void);
|
struct GBAThread* GBAThreadGetContext(void);
|
||||||
|
|
||||||
|
void GBASyncPostFrame(struct GBASync* sync);
|
||||||
|
void GBASyncWaitFrameStart(struct GBASync* sync, int frameskip);
|
||||||
|
void GBASyncWaitFrameEnd(struct GBASync* sync);
|
||||||
|
int GBASyncDrawingFrame(struct GBASync* sync);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
#include "gba.h"
|
#include "gba.h"
|
||||||
#include "gba-io.h"
|
#include "gba-io.h"
|
||||||
|
#include "gba-thread.h"
|
||||||
|
|
||||||
#include <limits.h>
|
#include <limits.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
@ -79,13 +80,15 @@ int32_t GBAVideoProcessEvents(struct GBAVideo* video, int32_t cycles) {
|
||||||
switch (video->vcount) {
|
switch (video->vcount) {
|
||||||
case VIDEO_VERTICAL_PIXELS:
|
case VIDEO_VERTICAL_PIXELS:
|
||||||
video->inVblank = 1;
|
video->inVblank = 1;
|
||||||
video->renderer->finishFrame(video->renderer);
|
if (GBASyncDrawingFrame(video->p->sync)) {
|
||||||
|
video->renderer->finishFrame(video->renderer);
|
||||||
|
}
|
||||||
video->nextVblankIRQ = video->nextEvent + VIDEO_TOTAL_LENGTH;
|
video->nextVblankIRQ = video->nextEvent + VIDEO_TOTAL_LENGTH;
|
||||||
GBAMemoryRunVblankDMAs(&video->p->memory);
|
GBAMemoryRunVblankDMAs(&video->p->memory);
|
||||||
if (video->vblankIRQ) {
|
if (video->vblankIRQ) {
|
||||||
GBARaiseIRQ(video->p, IRQ_VBLANK);
|
GBARaiseIRQ(video->p, IRQ_VBLANK);
|
||||||
}
|
}
|
||||||
//video->vblankCallback();
|
GBASyncPostFrame(video->p->sync);
|
||||||
break;
|
break;
|
||||||
case VIDEO_VERTICAL_TOTAL_PIXELS - 1:
|
case VIDEO_VERTICAL_TOTAL_PIXELS - 1:
|
||||||
video->inVblank = 0;
|
video->inVblank = 0;
|
||||||
|
@ -102,7 +105,7 @@ int32_t GBAVideoProcessEvents(struct GBAVideo* video, int32_t cycles) {
|
||||||
video->nextVcounterIRQ += VIDEO_TOTAL_LENGTH;
|
video->nextVcounterIRQ += VIDEO_TOTAL_LENGTH;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (video->vcount < VIDEO_VERTICAL_PIXELS) {
|
if (video->vcount < VIDEO_VERTICAL_PIXELS && GBASyncDrawingFrame(video->p->sync)) {
|
||||||
video->renderer->drawScanline(video->renderer, video->vcount);
|
video->renderer->drawScanline(video->renderer, video->vcount);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -168,10 +168,6 @@ struct GBAVideoRenderer {
|
||||||
uint16_t* palette;
|
uint16_t* palette;
|
||||||
uint16_t* vram;
|
uint16_t* vram;
|
||||||
union GBAOAM* oam;
|
union GBAOAM* oam;
|
||||||
|
|
||||||
int framesPending;
|
|
||||||
int frameskip;
|
|
||||||
int turbo;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct GBAVideo {
|
struct GBAVideo {
|
||||||
|
|
|
@ -66,6 +66,8 @@ struct GBA {
|
||||||
struct GBAVideo video;
|
struct GBAVideo video;
|
||||||
struct GBAAudio audio;
|
struct GBAAudio audio;
|
||||||
|
|
||||||
|
struct GBASync* sync;
|
||||||
|
|
||||||
struct ARMDebugger* debugger;
|
struct ARMDebugger* debugger;
|
||||||
|
|
||||||
int timersEnabled;
|
int timersEnabled;
|
||||||
|
|
|
@ -48,18 +48,6 @@ void GBAVideoSoftwareRendererCreate(struct GBAVideoSoftwareRenderer* renderer) {
|
||||||
renderer->d.writePalette = GBAVideoSoftwareRendererWritePalette;
|
renderer->d.writePalette = GBAVideoSoftwareRendererWritePalette;
|
||||||
renderer->d.drawScanline = GBAVideoSoftwareRendererDrawScanline;
|
renderer->d.drawScanline = GBAVideoSoftwareRendererDrawScanline;
|
||||||
renderer->d.finishFrame = GBAVideoSoftwareRendererFinishFrame;
|
renderer->d.finishFrame = GBAVideoSoftwareRendererFinishFrame;
|
||||||
|
|
||||||
renderer->d.turbo = 0;
|
|
||||||
renderer->d.framesPending = 0;
|
|
||||||
renderer->d.frameskip = 0;
|
|
||||||
|
|
||||||
{
|
|
||||||
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
|
|
||||||
renderer->mutex = mutex;
|
|
||||||
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
|
|
||||||
renderer->upCond = cond;
|
|
||||||
renderer->downCond = cond;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void GBAVideoSoftwareRendererInit(struct GBAVideoRenderer* renderer) {
|
static void GBAVideoSoftwareRendererInit(struct GBAVideoRenderer* renderer) {
|
||||||
|
@ -110,22 +98,10 @@ static void GBAVideoSoftwareRendererInit(struct GBAVideoRenderer* renderer) {
|
||||||
bg->sx = 0;
|
bg->sx = 0;
|
||||||
bg->sy = 0;
|
bg->sy = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
pthread_mutex_init(&softwareRenderer->mutex, 0);
|
|
||||||
pthread_cond_init(&softwareRenderer->upCond, 0);
|
|
||||||
pthread_cond_init(&softwareRenderer->downCond, 0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void GBAVideoSoftwareRendererDeinit(struct GBAVideoRenderer* renderer) {
|
static void GBAVideoSoftwareRendererDeinit(struct GBAVideoRenderer* renderer) {
|
||||||
struct GBAVideoSoftwareRenderer* softwareRenderer = (struct GBAVideoSoftwareRenderer*) renderer;
|
struct GBAVideoSoftwareRenderer* softwareRenderer = (struct GBAVideoSoftwareRenderer*) renderer;
|
||||||
|
|
||||||
pthread_mutex_lock(&softwareRenderer->mutex);
|
|
||||||
pthread_cond_broadcast(&softwareRenderer->upCond);
|
|
||||||
pthread_mutex_unlock(&softwareRenderer->mutex);
|
|
||||||
|
|
||||||
pthread_mutex_destroy(&softwareRenderer->mutex);
|
|
||||||
pthread_cond_destroy(&softwareRenderer->upCond);
|
|
||||||
pthread_cond_destroy(&softwareRenderer->downCond);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static uint16_t GBAVideoSoftwareRendererWriteVideoRegister(struct GBAVideoRenderer* renderer, uint32_t address, uint16_t value) {
|
static uint16_t GBAVideoSoftwareRendererWriteVideoRegister(struct GBAVideoRenderer* renderer, uint32_t address, uint16_t value) {
|
||||||
|
@ -362,9 +338,7 @@ static void GBAVideoSoftwareRendererWritePalette(struct GBAVideoRenderer* render
|
||||||
|
|
||||||
static void GBAVideoSoftwareRendererDrawScanline(struct GBAVideoRenderer* renderer, int y) {
|
static void GBAVideoSoftwareRendererDrawScanline(struct GBAVideoRenderer* renderer, int y) {
|
||||||
struct GBAVideoSoftwareRenderer* softwareRenderer = (struct GBAVideoSoftwareRenderer*) renderer;
|
struct GBAVideoSoftwareRenderer* softwareRenderer = (struct GBAVideoSoftwareRenderer*) renderer;
|
||||||
if (renderer->frameskip > 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
color_t* row = &softwareRenderer->outputBuffer[softwareRenderer->outputBufferStride * y];
|
color_t* row = &softwareRenderer->outputBuffer[softwareRenderer->outputBufferStride * y];
|
||||||
if (softwareRenderer->dispcnt.forcedBlank) {
|
if (softwareRenderer->dispcnt.forcedBlank) {
|
||||||
int x;
|
int x;
|
||||||
|
@ -437,18 +411,6 @@ static void GBAVideoSoftwareRendererDrawScanline(struct GBAVideoRenderer* render
|
||||||
static void GBAVideoSoftwareRendererFinishFrame(struct GBAVideoRenderer* renderer) {
|
static void GBAVideoSoftwareRendererFinishFrame(struct GBAVideoRenderer* renderer) {
|
||||||
struct GBAVideoSoftwareRenderer* softwareRenderer = (struct GBAVideoSoftwareRenderer*) renderer;
|
struct GBAVideoSoftwareRenderer* softwareRenderer = (struct GBAVideoSoftwareRenderer*) renderer;
|
||||||
|
|
||||||
pthread_mutex_lock(&softwareRenderer->mutex);
|
|
||||||
if (renderer->frameskip > 0) {
|
|
||||||
--renderer->frameskip;
|
|
||||||
} else {
|
|
||||||
renderer->framesPending++;
|
|
||||||
pthread_cond_broadcast(&softwareRenderer->upCond);
|
|
||||||
if (!renderer->turbo) {
|
|
||||||
pthread_cond_wait(&softwareRenderer->downCond, &softwareRenderer->mutex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pthread_mutex_unlock(&softwareRenderer->mutex);
|
|
||||||
|
|
||||||
softwareRenderer->bg[2].sx = softwareRenderer->bg[2].refx;
|
softwareRenderer->bg[2].sx = softwareRenderer->bg[2].refx;
|
||||||
softwareRenderer->bg[2].sy = softwareRenderer->bg[2].refy;
|
softwareRenderer->bg[2].sy = softwareRenderer->bg[2].refy;
|
||||||
softwareRenderer->bg[3].sx = softwareRenderer->bg[3].refx;
|
softwareRenderer->bg[3].sx = softwareRenderer->bg[3].refx;
|
||||||
|
|
|
@ -135,10 +135,6 @@ struct GBAVideoSoftwareRenderer {
|
||||||
int end;
|
int end;
|
||||||
|
|
||||||
uint32_t enabledBitmap[4];
|
uint32_t enabledBitmap[4];
|
||||||
|
|
||||||
pthread_mutex_t mutex;
|
|
||||||
pthread_cond_t upCond;
|
|
||||||
pthread_cond_t downCond;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
void GBAVideoSoftwareRendererCreate(struct GBAVideoSoftwareRenderer* renderer);
|
void GBAVideoSoftwareRendererCreate(struct GBAVideoSoftwareRenderer* renderer);
|
||||||
|
|
|
@ -71,6 +71,7 @@ int main(int argc, char** argv) {
|
||||||
context.fname = fname;
|
context.fname = fname;
|
||||||
context.useDebugger = 1;
|
context.useDebugger = 1;
|
||||||
context.renderer = &renderer.d.d;
|
context.renderer = &renderer.d.d;
|
||||||
|
context.frameskip = 0;
|
||||||
GBAThreadStart(&context);
|
GBAThreadStart(&context);
|
||||||
renderer.audio.audio = &context.gba->audio;
|
renderer.audio.audio = &context.gba->audio;
|
||||||
|
|
||||||
|
@ -130,30 +131,21 @@ static void _GBASDLRunloop(struct GBAThread* context, struct GLSoftwareRenderer*
|
||||||
glLoadIdentity();
|
glLoadIdentity();
|
||||||
glOrtho(0, 240, 160, 0, 0, 1);
|
glOrtho(0, 240, 160, 0, 0, 1);
|
||||||
while (context->started && (!context->debugger || context->debugger->state != DEBUGGER_EXITING)) {
|
while (context->started && (!context->debugger || context->debugger->state != DEBUGGER_EXITING)) {
|
||||||
pthread_mutex_lock(&renderer->d.mutex);
|
GBASyncWaitFrameStart(&context->sync, context->frameskip);
|
||||||
if (renderer->d.d.framesPending) {
|
glBindTexture(GL_TEXTURE_2D, renderer->tex);
|
||||||
renderer->d.d.framesPending = 0;
|
|
||||||
pthread_mutex_unlock(&renderer->d.mutex);
|
|
||||||
glBindTexture(GL_TEXTURE_2D, renderer->tex);
|
|
||||||
#ifdef COLOR_16_BIT
|
#ifdef COLOR_16_BIT
|
||||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 256, 256, 0, GL_RGBA, GL_UNSIGNED_SHORT_1_5_5_5_REV, renderer->d.outputBuffer);
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 256, 256, 0, GL_RGBA, GL_UNSIGNED_SHORT_1_5_5_5_REV, renderer->d.outputBuffer);
|
||||||
#else
|
#else
|
||||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 256, 256, 0, GL_RGBA, GL_UNSIGNED_BYTE, renderer->d.outputBuffer);
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 256, 256, 0, GL_RGBA, GL_UNSIGNED_BYTE, renderer->d.outputBuffer);
|
||||||
#endif
|
#endif
|
||||||
glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
|
glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
|
||||||
|
|
||||||
SDL_GL_SwapBuffers();
|
SDL_GL_SwapBuffers();
|
||||||
|
|
||||||
while (SDL_PollEvent(&event)) {
|
while (SDL_PollEvent(&event)) {
|
||||||
GBASDLHandleEvent(context, &event);
|
GBASDLHandleEvent(context, &event);
|
||||||
}
|
|
||||||
pthread_mutex_lock(&renderer->d.mutex);
|
|
||||||
pthread_cond_broadcast(&renderer->d.downCond);
|
|
||||||
} else {
|
|
||||||
pthread_cond_broadcast(&renderer->d.downCond);
|
|
||||||
pthread_cond_wait(&renderer->d.upCond, &renderer->d.mutex);
|
|
||||||
}
|
}
|
||||||
pthread_mutex_unlock(&renderer->d.mutex);
|
GBASyncWaitFrameEnd(&context->sync);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -52,7 +52,7 @@ static void _GBASDLHandleKeypress(struct GBAThread* context, const struct SDL_Ke
|
||||||
key = GBA_KEY_RIGHT;
|
key = GBA_KEY_RIGHT;
|
||||||
break;
|
break;
|
||||||
case SDLK_TAB:
|
case SDLK_TAB:
|
||||||
context->renderer->turbo = !context->renderer->turbo;
|
context->sync.videoFrameWait = !context->sync.videoFrameWait;
|
||||||
return;
|
return;
|
||||||
default:
|
default:
|
||||||
return;
|
return;
|
||||||
|
|
Loading…
Reference in New Issue