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);
|
||||
threadContext->gba = &gba;
|
||||
gba.sync = &threadContext->sync;
|
||||
pthread_setspecific(contextKey, threadContext);
|
||||
if (threadContext->renderer) {
|
||||
GBAVideoAssociateRenderer(&gba.video, threadContext->renderer);
|
||||
|
@ -65,9 +66,9 @@ static void* _GBAThreadRun(void* context) {
|
|||
gba.keySource = &threadContext->activeKeys;
|
||||
|
||||
threadContext->started = 1;
|
||||
pthread_mutex_lock(&threadContext->mutex);
|
||||
pthread_cond_broadcast(&threadContext->cond);
|
||||
pthread_mutex_unlock(&threadContext->mutex);
|
||||
pthread_mutex_lock(&threadContext->startMutex);
|
||||
pthread_cond_broadcast(&threadContext->startCond);
|
||||
pthread_mutex_unlock(&threadContext->startMutex);
|
||||
|
||||
if (threadContext->useDebugger) {
|
||||
ARMDebuggerRun(&debugger);
|
||||
|
@ -85,32 +86,80 @@ static void* _GBAThreadRun(void* context) {
|
|||
|
||||
int GBAThreadStart(struct GBAThread* threadContext) {
|
||||
// TODO: error check
|
||||
{
|
||||
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||
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_init(&threadContext->startMutex, 0);
|
||||
pthread_cond_init(&threadContext->startCond, 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->started = 0;
|
||||
pthread_create(&threadContext->thread, 0, _GBAThreadRun, threadContext);
|
||||
pthread_cond_wait(&threadContext->cond, &threadContext->mutex);
|
||||
pthread_mutex_unlock(&threadContext->mutex);
|
||||
pthread_cond_wait(&threadContext->startCond, &threadContext->startMutex);
|
||||
pthread_mutex_unlock(&threadContext->startMutex);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
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_mutex_destroy(&threadContext->mutex);
|
||||
pthread_cond_destroy(&threadContext->cond);
|
||||
pthread_mutex_destroy(&threadContext->startMutex);
|
||||
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) {
|
||||
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;
|
||||
const char* fname;
|
||||
int activeKeys;
|
||||
int frameskip;
|
||||
|
||||
// Threading state
|
||||
pthread_mutex_t mutex;
|
||||
pthread_cond_t cond;
|
||||
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);
|
||||
void GBAThreadJoin(struct GBAThread* threadContext);
|
||||
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
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
#include "gba.h"
|
||||
#include "gba-io.h"
|
||||
#include "gba-thread.h"
|
||||
|
||||
#include <limits.h>
|
||||
#include <string.h>
|
||||
|
@ -79,13 +80,15 @@ int32_t GBAVideoProcessEvents(struct GBAVideo* video, int32_t cycles) {
|
|||
switch (video->vcount) {
|
||||
case VIDEO_VERTICAL_PIXELS:
|
||||
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;
|
||||
GBAMemoryRunVblankDMAs(&video->p->memory);
|
||||
if (video->vblankIRQ) {
|
||||
GBARaiseIRQ(video->p, IRQ_VBLANK);
|
||||
}
|
||||
//video->vblankCallback();
|
||||
GBASyncPostFrame(video->p->sync);
|
||||
break;
|
||||
case VIDEO_VERTICAL_TOTAL_PIXELS - 1:
|
||||
video->inVblank = 0;
|
||||
|
@ -102,7 +105,7 @@ int32_t GBAVideoProcessEvents(struct GBAVideo* video, int32_t cycles) {
|
|||
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);
|
||||
}
|
||||
} else {
|
||||
|
|
|
@ -168,10 +168,6 @@ struct GBAVideoRenderer {
|
|||
uint16_t* palette;
|
||||
uint16_t* vram;
|
||||
union GBAOAM* oam;
|
||||
|
||||
int framesPending;
|
||||
int frameskip;
|
||||
int turbo;
|
||||
};
|
||||
|
||||
struct GBAVideo {
|
||||
|
|
|
@ -66,6 +66,8 @@ struct GBA {
|
|||
struct GBAVideo video;
|
||||
struct GBAAudio audio;
|
||||
|
||||
struct GBASync* sync;
|
||||
|
||||
struct ARMDebugger* debugger;
|
||||
|
||||
int timersEnabled;
|
||||
|
|
|
@ -48,18 +48,6 @@ void GBAVideoSoftwareRendererCreate(struct GBAVideoSoftwareRenderer* renderer) {
|
|||
renderer->d.writePalette = GBAVideoSoftwareRendererWritePalette;
|
||||
renderer->d.drawScanline = GBAVideoSoftwareRendererDrawScanline;
|
||||
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) {
|
||||
|
@ -110,22 +98,10 @@ static void GBAVideoSoftwareRendererInit(struct GBAVideoRenderer* renderer) {
|
|||
bg->sx = 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) {
|
||||
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) {
|
||||
|
@ -362,9 +338,7 @@ static void GBAVideoSoftwareRendererWritePalette(struct GBAVideoRenderer* render
|
|||
|
||||
static void GBAVideoSoftwareRendererDrawScanline(struct GBAVideoRenderer* renderer, int y) {
|
||||
struct GBAVideoSoftwareRenderer* softwareRenderer = (struct GBAVideoSoftwareRenderer*) renderer;
|
||||
if (renderer->frameskip > 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
color_t* row = &softwareRenderer->outputBuffer[softwareRenderer->outputBufferStride * y];
|
||||
if (softwareRenderer->dispcnt.forcedBlank) {
|
||||
int x;
|
||||
|
@ -437,18 +411,6 @@ static void GBAVideoSoftwareRendererDrawScanline(struct GBAVideoRenderer* render
|
|||
static void GBAVideoSoftwareRendererFinishFrame(struct GBAVideoRenderer* 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].sy = softwareRenderer->bg[2].refy;
|
||||
softwareRenderer->bg[3].sx = softwareRenderer->bg[3].refx;
|
||||
|
|
|
@ -135,10 +135,6 @@ struct GBAVideoSoftwareRenderer {
|
|||
int end;
|
||||
|
||||
uint32_t enabledBitmap[4];
|
||||
|
||||
pthread_mutex_t mutex;
|
||||
pthread_cond_t upCond;
|
||||
pthread_cond_t downCond;
|
||||
};
|
||||
|
||||
void GBAVideoSoftwareRendererCreate(struct GBAVideoSoftwareRenderer* renderer);
|
||||
|
|
|
@ -71,6 +71,7 @@ int main(int argc, char** argv) {
|
|||
context.fname = fname;
|
||||
context.useDebugger = 1;
|
||||
context.renderer = &renderer.d.d;
|
||||
context.frameskip = 0;
|
||||
GBAThreadStart(&context);
|
||||
renderer.audio.audio = &context.gba->audio;
|
||||
|
||||
|
@ -130,30 +131,21 @@ static void _GBASDLRunloop(struct GBAThread* context, struct GLSoftwareRenderer*
|
|||
glLoadIdentity();
|
||||
glOrtho(0, 240, 160, 0, 0, 1);
|
||||
while (context->started && (!context->debugger || context->debugger->state != DEBUGGER_EXITING)) {
|
||||
pthread_mutex_lock(&renderer->d.mutex);
|
||||
if (renderer->d.d.framesPending) {
|
||||
renderer->d.d.framesPending = 0;
|
||||
pthread_mutex_unlock(&renderer->d.mutex);
|
||||
glBindTexture(GL_TEXTURE_2D, renderer->tex);
|
||||
GBASyncWaitFrameStart(&context->sync, context->frameskip);
|
||||
glBindTexture(GL_TEXTURE_2D, renderer->tex);
|
||||
#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
|
||||
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
|
||||
glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
|
||||
glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
|
||||
|
||||
SDL_GL_SwapBuffers();
|
||||
SDL_GL_SwapBuffers();
|
||||
|
||||
while (SDL_PollEvent(&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);
|
||||
while (SDL_PollEvent(&event)) {
|
||||
GBASDLHandleEvent(context, &event);
|
||||
}
|
||||
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;
|
||||
break;
|
||||
case SDLK_TAB:
|
||||
context->renderer->turbo = !context->renderer->turbo;
|
||||
context->sync.videoFrameWait = !context->sync.videoFrameWait;
|
||||
return;
|
||||
default:
|
||||
return;
|
||||
|
|
Loading…
Reference in New Issue