GBA Video: Less blocking

This commit is contained in:
Jeffrey Pfau 2015-07-29 01:04:23 -07:00
parent 43ec351d49
commit 0cfff99652
2 changed files with 67 additions and 69 deletions

View File

@ -64,8 +64,6 @@ void GBAVideoThreadProxyRendererInit(struct GBAVideoRenderer* renderer) {
proxyRenderer->vramDirtyBitmap = 0; proxyRenderer->vramDirtyBitmap = 0;
memset(proxyRenderer->oamDirtyBitmap, 0, sizeof(proxyRenderer->oamDirtyBitmap)); memset(proxyRenderer->oamDirtyBitmap, 0, sizeof(proxyRenderer->oamDirtyBitmap));
memset(proxyRenderer->paletteDirtyBitmap, 0, sizeof(proxyRenderer->paletteDirtyBitmap));
memset(proxyRenderer->regDirtyBitmap, 0, sizeof(proxyRenderer->regDirtyBitmap));
proxyRenderer->threadState = PROXY_THREAD_IDLE; proxyRenderer->threadState = PROXY_THREAD_IDLE;
ThreadCreate(&proxyRenderer->thread, _proxyThread, proxyRenderer); ThreadCreate(&proxyRenderer->thread, _proxyThread, proxyRenderer);
} }
@ -135,19 +133,14 @@ uint16_t GBAVideoThreadProxyRendererWriteVideoRegister(struct GBAVideoRenderer*
if (address > REG_BLDY) { if (address > REG_BLDY) {
return value; return value;
} }
proxyRenderer->regProxy[address >> 1] = value;
int bit = 1 << ((address >> 1) & 31);
int base = address >> 6;
if (proxyRenderer->regDirtyBitmap[base] & bit) {
return value;
}
proxyRenderer->regDirtyBitmap[base] |= bit;
struct GBAVideoDirtyInfo dirty = { struct GBAVideoDirtyInfo dirty = {
DIRTY_REGISTER, DIRTY_REGISTER,
address address,
value
}; };
*GBAVideoDirtyQueueAppend(&proxyRenderer->dirtyQueue) = dirty; *GBAVideoDirtyQueueAppend(&proxyRenderer->dirtyQueue) = dirty;
ConditionWake(&proxyRenderer->toThreadCond);
return value; return value;
} }
@ -158,27 +151,17 @@ void GBAVideoThreadProxyRendererWriteVRAM(struct GBAVideoRenderer* renderer, uin
return; return;
} }
proxyRenderer->vramDirtyBitmap |= bit; proxyRenderer->vramDirtyBitmap |= bit;
struct GBAVideoDirtyInfo dirty = {
DIRTY_VRAM,
address
};
*GBAVideoDirtyQueueAppend(&proxyRenderer->dirtyQueue) = dirty;
} }
void GBAVideoThreadProxyRendererWritePalette(struct GBAVideoRenderer* renderer, uint32_t address, uint16_t value) { void GBAVideoThreadProxyRendererWritePalette(struct GBAVideoRenderer* renderer, uint32_t address, uint16_t value) {
struct GBAVideoThreadProxyRenderer* proxyRenderer = (struct GBAVideoThreadProxyRenderer*) renderer; struct GBAVideoThreadProxyRenderer* proxyRenderer = (struct GBAVideoThreadProxyRenderer*) renderer;
proxyRenderer->paletteProxy[address >> 1] = value;
int bit = 1 << ((address >> 1) & 31);
int base = address >> 6;
if (proxyRenderer->paletteDirtyBitmap[base] & bit) {
return;
}
proxyRenderer->paletteDirtyBitmap[base] |= bit;
struct GBAVideoDirtyInfo dirty = { struct GBAVideoDirtyInfo dirty = {
DIRTY_PALETTE, DIRTY_PALETTE,
address address,
value
}; };
*GBAVideoDirtyQueueAppend(&proxyRenderer->dirtyQueue) = dirty; *GBAVideoDirtyQueueAppend(&proxyRenderer->dirtyQueue) = dirty;
ConditionWake(&proxyRenderer->toThreadCond);
} }
void GBAVideoThreadProxyRendererWriteOAM(struct GBAVideoRenderer* renderer, uint32_t oam) { void GBAVideoThreadProxyRendererWriteOAM(struct GBAVideoRenderer* renderer, uint32_t oam) {
@ -192,35 +175,43 @@ void GBAVideoThreadProxyRendererWriteOAM(struct GBAVideoRenderer* renderer, uint
proxyRenderer->oamDirtyBitmap[base] |= bit; proxyRenderer->oamDirtyBitmap[base] |= bit;
struct GBAVideoDirtyInfo dirty = { struct GBAVideoDirtyInfo dirty = {
DIRTY_OAM, DIRTY_OAM,
oam oam,
0
}; };
*GBAVideoDirtyQueueAppend(&proxyRenderer->dirtyQueue) = dirty; *GBAVideoDirtyQueueAppend(&proxyRenderer->dirtyQueue) = dirty;
ConditionWake(&proxyRenderer->toThreadCond);
} }
void GBAVideoThreadProxyRendererDrawScanline(struct GBAVideoRenderer* renderer, int y) { void GBAVideoThreadProxyRendererDrawScanline(struct GBAVideoRenderer* renderer, int y) {
struct GBAVideoThreadProxyRenderer* proxyRenderer = (struct GBAVideoThreadProxyRenderer*) renderer; struct GBAVideoThreadProxyRenderer* proxyRenderer = (struct GBAVideoThreadProxyRenderer*) renderer;
MutexLock(&proxyRenderer->mutex); struct GBAVideoDirtyInfo dirty = {
while (proxyRenderer->threadState == PROXY_THREAD_BUSY) { DIRTY_SCANLINE,
ConditionWake(&proxyRenderer->toThreadCond); y,
ConditionWait(&proxyRenderer->fromThreadCond, &proxyRenderer->mutex); 0
} };
proxyRenderer->y = y; *GBAVideoDirtyQueueAppend(&proxyRenderer->dirtyQueue) = dirty;
ConditionWake(&proxyRenderer->toThreadCond); ConditionWake(&proxyRenderer->toThreadCond);
while (proxyRenderer->threadState == PROXY_THREAD_IDLE) {
ConditionWake(&proxyRenderer->toThreadCond);
ConditionWait(&proxyRenderer->fromThreadCond, &proxyRenderer->mutex);
}
MutexUnlock(&proxyRenderer->mutex);
} }
void GBAVideoThreadProxyRendererFinishFrame(struct GBAVideoRenderer* renderer) { void GBAVideoThreadProxyRendererFinishFrame(struct GBAVideoRenderer* renderer) {
struct GBAVideoThreadProxyRenderer* proxyRenderer = (struct GBAVideoThreadProxyRenderer*) renderer; struct GBAVideoThreadProxyRenderer* proxyRenderer = (struct GBAVideoThreadProxyRenderer*) renderer;
MutexLock(&proxyRenderer->mutex); MutexLock(&proxyRenderer->mutex);
while (proxyRenderer->threadState == PROXY_THREAD_BUSY) { // Insert an extra item into the queue to make sure it gets flushed
struct GBAVideoDirtyInfo dirty = {
DIRTY_FLUSH,
0,
0
};
*GBAVideoDirtyQueueAppend(&proxyRenderer->dirtyQueue) = dirty;
do {
ConditionWake(&proxyRenderer->toThreadCond); ConditionWake(&proxyRenderer->toThreadCond);
ConditionWait(&proxyRenderer->fromThreadCond, &proxyRenderer->mutex); ConditionWait(&proxyRenderer->fromThreadCond, &proxyRenderer->mutex);
} } while (proxyRenderer->threadState == PROXY_THREAD_BUSY);
proxyRenderer->backend->finishFrame(proxyRenderer->backend); proxyRenderer->backend->finishFrame(proxyRenderer->backend);
GBAVideoDirtyQueueClear(&proxyRenderer->dirtyQueue);
proxyRenderer->queueCleared = true;
proxyRenderer->vramDirtyBitmap = 0;
memset(proxyRenderer->oamDirtyBitmap, 0, sizeof(proxyRenderer->oamDirtyBitmap));
MutexUnlock(&proxyRenderer->mutex); MutexUnlock(&proxyRenderer->mutex);
} }
@ -232,51 +223,59 @@ void GBAVideoThreadProxyRendererPutPixels(struct GBAVideoRenderer* renderer, uns
// TODO // TODO
} }
static THREAD_ENTRY _proxyThread(void* renderer) { static THREAD_ENTRY _proxyThread(void* renderer) {
struct GBAVideoThreadProxyRenderer* proxyRenderer = renderer; struct GBAVideoThreadProxyRenderer* proxyRenderer = renderer;
ThreadSetName("Proxy Renderer Thread"); ThreadSetName("Proxy Renderer Thread");
MutexLock(&proxyRenderer->mutex); MutexLock(&proxyRenderer->mutex);
size_t i = 0;
while (1) { while (1) {
ConditionWait(&proxyRenderer->toThreadCond, &proxyRenderer->mutex); ConditionWait(&proxyRenderer->toThreadCond, &proxyRenderer->mutex);
if (proxyRenderer->threadState == PROXY_THREAD_STOPPED) { if (proxyRenderer->threadState == PROXY_THREAD_STOPPED) {
break; break;
} }
proxyRenderer->threadState = PROXY_THREAD_BUSY; proxyRenderer->threadState = PROXY_THREAD_BUSY;
proxyRenderer->vramDirtyBitmap = 0; if (proxyRenderer->queueCleared) {
memset(proxyRenderer->oamDirtyBitmap, 0, sizeof(proxyRenderer->oamDirtyBitmap)); proxyRenderer->queueCleared = false;
memset(proxyRenderer->paletteDirtyBitmap, 0, sizeof(proxyRenderer->paletteDirtyBitmap)); i = 0;
memset(proxyRenderer->regDirtyBitmap, 0, sizeof(proxyRenderer->regDirtyBitmap)); }
size_t queueSize = GBAVideoDirtyQueueSize(&proxyRenderer->dirtyQueue); if (!GBAVideoDirtyQueueSize(&proxyRenderer->dirtyQueue)) {
struct GBAVideoDirtyInfo* queue = malloc(queueSize * sizeof(struct GBAVideoDirtyInfo)); continue;
memcpy(queue, GBAVideoDirtyQueueGetPointer(&proxyRenderer->dirtyQueue, 0), queueSize * sizeof(struct GBAVideoDirtyInfo)); }
GBAVideoDirtyQueueClear(&proxyRenderer->dirtyQueue); MutexUnlock(&proxyRenderer->mutex);
size_t i; for (; i < GBAVideoDirtyQueueSize(&proxyRenderer->dirtyQueue) - 1; ++i) {
for (i = 0; i < queueSize; ++i) { struct GBAVideoDirtyInfo* item = GBAVideoDirtyQueueGetPointer(&proxyRenderer->dirtyQueue, i);
switch (queue[i].type) { switch (item->type) {
case DIRTY_REGISTER: case DIRTY_REGISTER:
proxyRenderer->backend->writeVideoRegister(proxyRenderer->backend, queue[i].address, proxyRenderer->regProxy[queue[i].address >> 1]); proxyRenderer->backend->writeVideoRegister(proxyRenderer->backend, item->address, item->value);
break;
case DIRTY_VRAM:
proxyRenderer->backend->writeVRAM(proxyRenderer->backend, queue[i].address);
memcpy(&proxyRenderer->vramProxy[(queue[i].address & ~0xFFF) >> 1], &proxyRenderer->d.vram[(queue[i].address & ~0xFFF) >> 1], 0x1000);
break; break;
case DIRTY_PALETTE: case DIRTY_PALETTE:
proxyRenderer->backend->writePalette(proxyRenderer->backend, queue[i].address, proxyRenderer->paletteProxy[queue[i].address >> 1]); proxyRenderer->paletteProxy[item->address >> 1] = item->value;
proxyRenderer->backend->writePalette(proxyRenderer->backend, item->address, item->value);
break; break;
case DIRTY_OAM: case DIRTY_OAM:
proxyRenderer->backend->writeOAM(proxyRenderer->backend, queue[i].address); proxyRenderer->backend->writeOAM(proxyRenderer->backend, item->address);
break;
case DIRTY_SCANLINE:
if (proxyRenderer->vramDirtyBitmap) {
int bitmap = proxyRenderer->vramDirtyBitmap;
proxyRenderer->vramDirtyBitmap = 0;
int j;
for (j = 0; j < 24; ++j) {
if (!(bitmap & (1 << j))) {
continue;
}
proxyRenderer->backend->writeVRAM(proxyRenderer->backend, j * 0x1000);
memcpy(&proxyRenderer->vramProxy[j * 0x800], &proxyRenderer->d.vram[j * 0x800], 0x1000);
}
}
proxyRenderer->backend->drawScanline(proxyRenderer->backend, item->address);
break;
case DIRTY_FLUSH:
// This is only here to ensure the queue gets flushed
break; break;
} }
} }
free(queue);
ConditionWake(&proxyRenderer->fromThreadCond);
ConditionWait(&proxyRenderer->toThreadCond, &proxyRenderer->mutex);
MutexUnlock(&proxyRenderer->mutex);
proxyRenderer->backend->drawScanline(proxyRenderer->backend, proxyRenderer->y);
MutexLock(&proxyRenderer->mutex); MutexLock(&proxyRenderer->mutex);
proxyRenderer->threadState = PROXY_THREAD_IDLE; proxyRenderer->threadState = PROXY_THREAD_IDLE;
ConditionWake(&proxyRenderer->fromThreadCond); ConditionWake(&proxyRenderer->fromThreadCond);

View File

@ -12,9 +12,10 @@
enum GBAVideoDirtyType { enum GBAVideoDirtyType {
DIRTY_REGISTER, DIRTY_REGISTER,
DIRTY_VRAM,
DIRTY_OAM, DIRTY_OAM,
DIRTY_PALETTE DIRTY_PALETTE,
DIRTY_SCANLINE,
DIRTY_FLUSH
}; };
enum GBAVideoThreadProxyState { enum GBAVideoThreadProxyState {
@ -26,6 +27,7 @@ enum GBAVideoThreadProxyState {
struct GBAVideoDirtyInfo { struct GBAVideoDirtyInfo {
enum GBAVideoDirtyType type; enum GBAVideoDirtyType type;
uint32_t address; uint32_t address;
uint16_t value;
}; };
DECLARE_VECTOR(GBAVideoDirtyQueue, struct GBAVideoDirtyInfo); DECLARE_VECTOR(GBAVideoDirtyQueue, struct GBAVideoDirtyInfo);
@ -43,15 +45,12 @@ struct GBAVideoThreadProxyRenderer {
struct GBAVideoDirtyQueue dirtyQueue; struct GBAVideoDirtyQueue dirtyQueue;
uint32_t vramDirtyBitmap; uint32_t vramDirtyBitmap;
uint32_t oamDirtyBitmap[16]; uint32_t oamDirtyBitmap[16];
uint32_t paletteDirtyBitmap[16];
uint32_t regDirtyBitmap[2];
uint16_t* vramProxy; uint16_t* vramProxy;
union GBAOAM oamProxy; union GBAOAM oamProxy;
uint16_t paletteProxy[512]; uint16_t paletteProxy[512];
uint16_t regProxy[42];
int y; bool queueCleared;
}; };
void GBAVideoThreadProxyRendererCreate(struct GBAVideoThreadProxyRenderer* renderer, struct GBAVideoRenderer* backend); void GBAVideoThreadProxyRendererCreate(struct GBAVideoThreadProxyRenderer* renderer, struct GBAVideoRenderer* backend);