Refactor frame synchronization
Jeffrey Pfau jeffrey@endrift.com
Sat, 05 Oct 2013 02:11:53 -0700
9 files changed,
103 insertions(+),
87 deletions(-)
M
src/gba/gba-thread.c
→
src/gba/gba-thread.c
@@ -28,6 +28,7 @@ pthread_sigmask(SIG_UNBLOCK, &signals, 0);
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 @@ }
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 @@ }
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_init(&threadContext->sync.videoFrameMutex, 0); + pthread_cond_init(&threadContext->sync.videoFrameAvailableCond, 0); + pthread_cond_init(&threadContext->sync.videoFrameRequiredCond, 0); - pthread_mutex_lock(&threadContext->mutex); + 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; +}
M
src/gba/gba-thread.h
→
src/gba/gba-thread.h
@@ -15,15 +15,31 @@ struct GBAVideoRenderer* renderer;
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
M
src/gba/gba-video.c
→
src/gba/gba-video.c
@@ -2,6 +2,7 @@ #include "gba-video.h"
#include "gba.h" #include "gba-io.h" +#include "gba-thread.h" #include <limits.h> #include <string.h>@@ -79,13 +80,15 @@
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 @@ GBARaiseIRQ(video->p, IRQ_VCOUNTER);
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 {
M
src/gba/gba-video.h
→
src/gba/gba-video.h
@@ -168,10 +168,6 @@
uint16_t* palette; uint16_t* vram; union GBAOAM* oam; - - int framesPending; - int frameskip; - int turbo; }; struct GBAVideo {
M
src/gba/gba.h
→
src/gba/gba.h
@@ -66,6 +66,8 @@ struct GBAMemory memory;
struct GBAVideo video; struct GBAAudio audio; + struct GBASync* sync; + struct ARMDebugger* debugger; int timersEnabled;
M
src/gba/renderers/video-software.c
→
src/gba/renderers/video-software.c
@@ -48,18 +48,6 @@ renderer->d.writeOAM = GBAVideoSoftwareRendererWriteOAM;
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 @@ bg->dmy = 256;
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 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;@@ -436,18 +410,6 @@ }
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;
M
src/gba/renderers/video-software.h
→
src/gba/renderers/video-software.h
@@ -135,10 +135,6 @@ int start;
int end; uint32_t enabledBitmap[4]; - - pthread_mutex_t mutex; - pthread_cond_t upCond; - pthread_cond_t downCond; }; void GBAVideoSoftwareRendererCreate(struct GBAVideoSoftwareRenderer* renderer);
M
src/gl-main.c
→
src/gl-main.c
@@ -71,6 +71,7 @@ context.fd = fd;
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 @@ glMatrixMode (GL_PROJECTION);
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); } }
M
src/sdl/sdl-events.c
→
src/sdl/sdl-events.c
@@ -52,7 +52,7 @@ case SDLK_RIGHT:
key = GBA_KEY_RIGHT; break; case SDLK_TAB: - context->renderer->turbo = !context->renderer->turbo; + context->sync.videoFrameWait = !context->sync.videoFrameWait; return; default: return;