Refactor frame synchronization

This commit is contained in:
Jeffrey Pfau 2013-10-05 02:11:53 -07:00
parent 2f00e3d146
commit 4ef6a70731
9 changed files with 103 additions and 87 deletions

View File

@ -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;
}

View File

@ -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

View File

@ -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 {

View File

@ -168,10 +168,6 @@ struct GBAVideoRenderer {
uint16_t* palette;
uint16_t* vram;
union GBAOAM* oam;
int framesPending;
int frameskip;
int turbo;
};
struct GBAVideo {

View File

@ -66,6 +66,8 @@ struct GBA {
struct GBAVideo video;
struct GBAAudio audio;
struct GBASync* sync;
struct ARMDebugger* debugger;
int timersEnabled;

View File

@ -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;

View File

@ -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);

View File

@ -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);
}
}

View File

@ -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;