mirror of https://github.com/mgba-emu/mgba.git
GBA Video: Less blocking
This commit is contained in:
parent
43ec351d49
commit
0cfff99652
|
@ -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);
|
||||||
|
|
|
@ -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);
|
||||||
|
|
Loading…
Reference in New Issue