diff --git a/src/gba/gba-thread.c b/src/gba/gba-thread.c index 2af5d94a6..c14d62a13 100644 --- a/src/gba/gba-thread.c +++ b/src/gba/gba-thread.c @@ -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; +} diff --git a/src/gba/gba-thread.h b/src/gba/gba-thread.h index ea00d465c..3611ca686 100644 --- a/src/gba/gba-thread.h +++ b/src/gba/gba-thread.h @@ -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 diff --git a/src/gba/gba-video.c b/src/gba/gba-video.c index 97a1310f7..e8f586458 100644 --- a/src/gba/gba-video.c +++ b/src/gba/gba-video.c @@ -2,6 +2,7 @@ #include "gba.h" #include "gba-io.h" +#include "gba-thread.h" #include #include @@ -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 { diff --git a/src/gba/gba-video.h b/src/gba/gba-video.h index 0e32bfc02..a93001fe4 100644 --- a/src/gba/gba-video.h +++ b/src/gba/gba-video.h @@ -168,10 +168,6 @@ struct GBAVideoRenderer { uint16_t* palette; uint16_t* vram; union GBAOAM* oam; - - int framesPending; - int frameskip; - int turbo; }; struct GBAVideo { diff --git a/src/gba/gba.h b/src/gba/gba.h index b5fc86af9..075273f2f 100644 --- a/src/gba/gba.h +++ b/src/gba/gba.h @@ -66,6 +66,8 @@ struct GBA { struct GBAVideo video; struct GBAAudio audio; + struct GBASync* sync; + struct ARMDebugger* debugger; int timersEnabled; diff --git a/src/gba/renderers/video-software.c b/src/gba/renderers/video-software.c index eb556b00e..3cabe79e4 100644 --- a/src/gba/renderers/video-software.c +++ b/src/gba/renderers/video-software.c @@ -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; diff --git a/src/gba/renderers/video-software.h b/src/gba/renderers/video-software.h index 55051eb8a..26cb56352 100644 --- a/src/gba/renderers/video-software.h +++ b/src/gba/renderers/video-software.h @@ -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); diff --git a/src/gl-main.c b/src/gl-main.c index 8453df504..c7f7bb1c7 100644 --- a/src/gl-main.c +++ b/src/gl-main.c @@ -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); } } diff --git a/src/sdl/sdl-events.c b/src/sdl/sdl-events.c index 09dd00af8..2e0f8ced0 100644 --- a/src/sdl/sdl-events.c +++ b/src/sdl/sdl-events.c @@ -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;