mirror of https://github.com/mgba-emu/mgba.git
GBA Video: Better threaded renderer using a new RingFIFO
This commit is contained in:
parent
0cfff99652
commit
e1ffc68582
|
@ -9,7 +9,24 @@
|
||||||
|
|
||||||
#include "util/memory.h"
|
#include "util/memory.h"
|
||||||
|
|
||||||
DEFINE_VECTOR(GBAVideoDirtyQueue, struct GBAVideoDirtyInfo);
|
#ifndef DISABLE_THREADING
|
||||||
|
|
||||||
|
enum GBAVideoDirtyType {
|
||||||
|
DIRTY_DUMMY = 0,
|
||||||
|
DIRTY_REGISTER,
|
||||||
|
DIRTY_OAM,
|
||||||
|
DIRTY_PALETTE,
|
||||||
|
DIRTY_VRAM,
|
||||||
|
DIRTY_SCANLINE,
|
||||||
|
DIRTY_FLUSH
|
||||||
|
};
|
||||||
|
|
||||||
|
struct GBAVideoDirtyInfo {
|
||||||
|
enum GBAVideoDirtyType type;
|
||||||
|
uint32_t address;
|
||||||
|
uint16_t value;
|
||||||
|
uint32_t padding;
|
||||||
|
};
|
||||||
|
|
||||||
static void GBAVideoThreadProxyRendererInit(struct GBAVideoRenderer* renderer);
|
static void GBAVideoThreadProxyRendererInit(struct GBAVideoRenderer* renderer);
|
||||||
static void GBAVideoThreadProxyRendererReset(struct GBAVideoRenderer* renderer);
|
static void GBAVideoThreadProxyRendererReset(struct GBAVideoRenderer* renderer);
|
||||||
|
@ -52,7 +69,7 @@ void GBAVideoThreadProxyRendererInit(struct GBAVideoRenderer* renderer) {
|
||||||
ConditionInit(&proxyRenderer->fromThreadCond);
|
ConditionInit(&proxyRenderer->fromThreadCond);
|
||||||
ConditionInit(&proxyRenderer->toThreadCond);
|
ConditionInit(&proxyRenderer->toThreadCond);
|
||||||
MutexInit(&proxyRenderer->mutex);
|
MutexInit(&proxyRenderer->mutex);
|
||||||
GBAVideoDirtyQueueInit(&proxyRenderer->dirtyQueue, 1024);
|
RingFIFOInit(&proxyRenderer->dirtyQueue, 0x200000, 0x1000);
|
||||||
proxyRenderer->threadState = PROXY_THREAD_STOPPED;
|
proxyRenderer->threadState = PROXY_THREAD_STOPPED;
|
||||||
|
|
||||||
proxyRenderer->vramProxy = anonymousMemoryMap(SIZE_VRAM);
|
proxyRenderer->vramProxy = anonymousMemoryMap(SIZE_VRAM);
|
||||||
|
@ -137,9 +154,10 @@ uint16_t GBAVideoThreadProxyRendererWriteVideoRegister(struct GBAVideoRenderer*
|
||||||
struct GBAVideoDirtyInfo dirty = {
|
struct GBAVideoDirtyInfo dirty = {
|
||||||
DIRTY_REGISTER,
|
DIRTY_REGISTER,
|
||||||
address,
|
address,
|
||||||
value
|
value,
|
||||||
|
0xDEADBEEF,
|
||||||
};
|
};
|
||||||
*GBAVideoDirtyQueueAppend(&proxyRenderer->dirtyQueue) = dirty;
|
RingFIFOWrite(&proxyRenderer->dirtyQueue, &dirty, sizeof(dirty));
|
||||||
ConditionWake(&proxyRenderer->toThreadCond);
|
ConditionWake(&proxyRenderer->toThreadCond);
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
@ -158,15 +176,15 @@ void GBAVideoThreadProxyRendererWritePalette(struct GBAVideoRenderer* renderer,
|
||||||
struct GBAVideoDirtyInfo dirty = {
|
struct GBAVideoDirtyInfo dirty = {
|
||||||
DIRTY_PALETTE,
|
DIRTY_PALETTE,
|
||||||
address,
|
address,
|
||||||
value
|
value,
|
||||||
|
0xDEADBEEF,
|
||||||
};
|
};
|
||||||
*GBAVideoDirtyQueueAppend(&proxyRenderer->dirtyQueue) = dirty;
|
RingFIFOWrite(&proxyRenderer->dirtyQueue, &dirty, sizeof(dirty));
|
||||||
ConditionWake(&proxyRenderer->toThreadCond);
|
ConditionWake(&proxyRenderer->toThreadCond);
|
||||||
}
|
}
|
||||||
|
|
||||||
void GBAVideoThreadProxyRendererWriteOAM(struct GBAVideoRenderer* renderer, uint32_t oam) {
|
void GBAVideoThreadProxyRendererWriteOAM(struct GBAVideoRenderer* renderer, uint32_t oam) {
|
||||||
struct GBAVideoThreadProxyRenderer* proxyRenderer = (struct GBAVideoThreadProxyRenderer*) renderer;
|
struct GBAVideoThreadProxyRenderer* proxyRenderer = (struct GBAVideoThreadProxyRenderer*) renderer;
|
||||||
proxyRenderer->oamProxy.raw[oam] = proxyRenderer->d.oam->raw[oam];
|
|
||||||
int bit = 1 << (oam & 31);
|
int bit = 1 << (oam & 31);
|
||||||
int base = oam >> 5;
|
int base = oam >> 5;
|
||||||
if (proxyRenderer->oamDirtyBitmap[base] & bit) {
|
if (proxyRenderer->oamDirtyBitmap[base] & bit) {
|
||||||
|
@ -176,20 +194,40 @@ void GBAVideoThreadProxyRendererWriteOAM(struct GBAVideoRenderer* renderer, uint
|
||||||
struct GBAVideoDirtyInfo dirty = {
|
struct GBAVideoDirtyInfo dirty = {
|
||||||
DIRTY_OAM,
|
DIRTY_OAM,
|
||||||
oam,
|
oam,
|
||||||
0
|
proxyRenderer->d.oam->raw[oam],
|
||||||
|
0xDEADBEEF,
|
||||||
};
|
};
|
||||||
*GBAVideoDirtyQueueAppend(&proxyRenderer->dirtyQueue) = dirty;
|
RingFIFOWrite(&proxyRenderer->dirtyQueue, &dirty, sizeof(dirty));
|
||||||
ConditionWake(&proxyRenderer->toThreadCond);
|
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;
|
||||||
|
if (proxyRenderer->vramDirtyBitmap) {
|
||||||
|
int bitmap = proxyRenderer->vramDirtyBitmap;
|
||||||
|
proxyRenderer->vramDirtyBitmap = 0;
|
||||||
|
int j;
|
||||||
|
for (j = 0; j < 24; ++j) {
|
||||||
|
if (!(bitmap & (1 << j))) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
struct GBAVideoDirtyInfo dirty = {
|
||||||
|
DIRTY_VRAM,
|
||||||
|
j * 0x1000,
|
||||||
|
0xABCD,
|
||||||
|
0xDEADBEEF,
|
||||||
|
};
|
||||||
|
RingFIFOWrite(&proxyRenderer->dirtyQueue, &dirty, sizeof(dirty));
|
||||||
|
RingFIFOWrite(&proxyRenderer->dirtyQueue, &proxyRenderer->d.vram[j * 0x800], 0x1000);
|
||||||
|
}
|
||||||
|
}
|
||||||
struct GBAVideoDirtyInfo dirty = {
|
struct GBAVideoDirtyInfo dirty = {
|
||||||
DIRTY_SCANLINE,
|
DIRTY_SCANLINE,
|
||||||
y,
|
y,
|
||||||
0
|
0,
|
||||||
|
0xDEADBEEF,
|
||||||
};
|
};
|
||||||
*GBAVideoDirtyQueueAppend(&proxyRenderer->dirtyQueue) = dirty;
|
RingFIFOWrite(&proxyRenderer->dirtyQueue, &dirty, sizeof(dirty));
|
||||||
ConditionWake(&proxyRenderer->toThreadCond);
|
ConditionWake(&proxyRenderer->toThreadCond);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -200,16 +238,15 @@ void GBAVideoThreadProxyRendererFinishFrame(struct GBAVideoRenderer* renderer) {
|
||||||
struct GBAVideoDirtyInfo dirty = {
|
struct GBAVideoDirtyInfo dirty = {
|
||||||
DIRTY_FLUSH,
|
DIRTY_FLUSH,
|
||||||
0,
|
0,
|
||||||
0
|
0,
|
||||||
|
0xDEADBEEF,
|
||||||
};
|
};
|
||||||
*GBAVideoDirtyQueueAppend(&proxyRenderer->dirtyQueue) = dirty;
|
RingFIFOWrite(&proxyRenderer->dirtyQueue, &dirty, sizeof(dirty));
|
||||||
do {
|
do {
|
||||||
ConditionWake(&proxyRenderer->toThreadCond);
|
ConditionWake(&proxyRenderer->toThreadCond);
|
||||||
ConditionWait(&proxyRenderer->fromThreadCond, &proxyRenderer->mutex);
|
ConditionWait(&proxyRenderer->fromThreadCond, &proxyRenderer->mutex);
|
||||||
} while (proxyRenderer->threadState == PROXY_THREAD_BUSY);
|
} 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;
|
proxyRenderer->vramDirtyBitmap = 0;
|
||||||
memset(proxyRenderer->oamDirtyBitmap, 0, sizeof(proxyRenderer->oamDirtyBitmap));
|
memset(proxyRenderer->oamDirtyBitmap, 0, sizeof(proxyRenderer->oamDirtyBitmap));
|
||||||
MutexUnlock(&proxyRenderer->mutex);
|
MutexUnlock(&proxyRenderer->mutex);
|
||||||
|
@ -228,52 +265,42 @@ static THREAD_ENTRY _proxyThread(void* 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;
|
||||||
if (proxyRenderer->queueCleared) {
|
|
||||||
proxyRenderer->queueCleared = false;
|
|
||||||
i = 0;
|
|
||||||
}
|
|
||||||
if (!GBAVideoDirtyQueueSize(&proxyRenderer->dirtyQueue)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
MutexUnlock(&proxyRenderer->mutex);
|
MutexUnlock(&proxyRenderer->mutex);
|
||||||
for (; i < GBAVideoDirtyQueueSize(&proxyRenderer->dirtyQueue) - 1; ++i) {
|
struct GBAVideoDirtyInfo item;
|
||||||
struct GBAVideoDirtyInfo* item = GBAVideoDirtyQueueGetPointer(&proxyRenderer->dirtyQueue, i);
|
while (RingFIFORead(&proxyRenderer->dirtyQueue, &item, sizeof(item))) {
|
||||||
switch (item->type) {
|
switch (item.type) {
|
||||||
case DIRTY_REGISTER:
|
case DIRTY_REGISTER:
|
||||||
proxyRenderer->backend->writeVideoRegister(proxyRenderer->backend, item->address, item->value);
|
proxyRenderer->backend->writeVideoRegister(proxyRenderer->backend, item.address, item.value);
|
||||||
break;
|
break;
|
||||||
case DIRTY_PALETTE:
|
case DIRTY_PALETTE:
|
||||||
proxyRenderer->paletteProxy[item->address >> 1] = item->value;
|
proxyRenderer->paletteProxy[item.address >> 1] = item.value;
|
||||||
proxyRenderer->backend->writePalette(proxyRenderer->backend, item->address, item->value);
|
proxyRenderer->backend->writePalette(proxyRenderer->backend, item.address, item.value);
|
||||||
break;
|
break;
|
||||||
case DIRTY_OAM:
|
case DIRTY_OAM:
|
||||||
proxyRenderer->backend->writeOAM(proxyRenderer->backend, item->address);
|
proxyRenderer->oamProxy.raw[item.address] = item.value;
|
||||||
|
proxyRenderer->backend->writeOAM(proxyRenderer->backend, item.address);
|
||||||
|
break;
|
||||||
|
case DIRTY_VRAM:
|
||||||
|
while (!RingFIFORead(&proxyRenderer->dirtyQueue, &proxyRenderer->vramProxy[item.address >> 1], 0x1000));
|
||||||
|
proxyRenderer->backend->writeVRAM(proxyRenderer->backend, item.address);
|
||||||
break;
|
break;
|
||||||
case DIRTY_SCANLINE:
|
case DIRTY_SCANLINE:
|
||||||
if (proxyRenderer->vramDirtyBitmap) {
|
proxyRenderer->backend->drawScanline(proxyRenderer->backend, item.address);
|
||||||
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;
|
break;
|
||||||
case DIRTY_FLUSH:
|
case DIRTY_FLUSH:
|
||||||
// This is only here to ensure the queue gets flushed
|
// This is only here to ensure the queue gets flushed
|
||||||
break;
|
break;
|
||||||
|
default:
|
||||||
|
// FIFO was corrupted
|
||||||
|
abort();
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
MutexLock(&proxyRenderer->mutex);
|
MutexLock(&proxyRenderer->mutex);
|
||||||
|
@ -284,3 +311,5 @@ static THREAD_ENTRY _proxyThread(void* renderer) {
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
|
@ -8,15 +8,7 @@
|
||||||
|
|
||||||
#include "gba/video.h"
|
#include "gba/video.h"
|
||||||
#include "util/threading.h"
|
#include "util/threading.h"
|
||||||
#include "util/vector.h"
|
#include "util/ring-fifo.h"
|
||||||
|
|
||||||
enum GBAVideoDirtyType {
|
|
||||||
DIRTY_REGISTER,
|
|
||||||
DIRTY_OAM,
|
|
||||||
DIRTY_PALETTE,
|
|
||||||
DIRTY_SCANLINE,
|
|
||||||
DIRTY_FLUSH
|
|
||||||
};
|
|
||||||
|
|
||||||
enum GBAVideoThreadProxyState {
|
enum GBAVideoThreadProxyState {
|
||||||
PROXY_THREAD_STOPPED = 0,
|
PROXY_THREAD_STOPPED = 0,
|
||||||
|
@ -24,14 +16,6 @@ enum GBAVideoThreadProxyState {
|
||||||
PROXY_THREAD_BUSY
|
PROXY_THREAD_BUSY
|
||||||
};
|
};
|
||||||
|
|
||||||
struct GBAVideoDirtyInfo {
|
|
||||||
enum GBAVideoDirtyType type;
|
|
||||||
uint32_t address;
|
|
||||||
uint16_t value;
|
|
||||||
};
|
|
||||||
|
|
||||||
DECLARE_VECTOR(GBAVideoDirtyQueue, struct GBAVideoDirtyInfo);
|
|
||||||
|
|
||||||
struct GBAVideoThreadProxyRenderer {
|
struct GBAVideoThreadProxyRenderer {
|
||||||
struct GBAVideoRenderer d;
|
struct GBAVideoRenderer d;
|
||||||
struct GBAVideoRenderer* backend;
|
struct GBAVideoRenderer* backend;
|
||||||
|
@ -42,15 +26,14 @@ struct GBAVideoThreadProxyRenderer {
|
||||||
Mutex mutex;
|
Mutex mutex;
|
||||||
enum GBAVideoThreadProxyState threadState;
|
enum GBAVideoThreadProxyState threadState;
|
||||||
|
|
||||||
struct GBAVideoDirtyQueue dirtyQueue;
|
struct RingFIFO dirtyQueue;
|
||||||
|
|
||||||
uint32_t vramDirtyBitmap;
|
uint32_t vramDirtyBitmap;
|
||||||
uint32_t oamDirtyBitmap[16];
|
uint32_t oamDirtyBitmap[16];
|
||||||
|
|
||||||
uint16_t* vramProxy;
|
uint16_t* vramProxy;
|
||||||
union GBAOAM oamProxy;
|
union GBAOAM oamProxy;
|
||||||
uint16_t paletteProxy[512];
|
uint16_t paletteProxy[512];
|
||||||
|
|
||||||
bool queueCleared;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
void GBAVideoThreadProxyRendererCreate(struct GBAVideoThreadProxyRenderer* renderer, struct GBAVideoRenderer* backend);
|
void GBAVideoThreadProxyRendererCreate(struct GBAVideoThreadProxyRenderer* renderer, struct GBAVideoRenderer* backend);
|
||||||
|
|
|
@ -0,0 +1,67 @@
|
||||||
|
/* Copyright (c) 2013-2014 Jeffrey Pfau
|
||||||
|
*
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
#include "ring-fifo.h"
|
||||||
|
|
||||||
|
void RingFIFOInit(struct RingFIFO* buffer, size_t capacity, size_t maxalloc) {
|
||||||
|
buffer->data = malloc(capacity);
|
||||||
|
buffer->capacity = capacity;
|
||||||
|
buffer->maxalloc = maxalloc;
|
||||||
|
RingFIFOClear(buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
void RingFIFODeinit(struct RingFIFO* buffer) {
|
||||||
|
free(buffer->data);
|
||||||
|
buffer->data = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t RingFIFOCapacity(const struct RingFIFO* buffer) {
|
||||||
|
return buffer->capacity;
|
||||||
|
}
|
||||||
|
|
||||||
|
void RingFIFOClear(struct RingFIFO* buffer) {
|
||||||
|
buffer->readPtr = buffer->data;
|
||||||
|
buffer->writePtr = buffer->data;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t RingFIFOWrite(struct RingFIFO* buffer, const void* value, size_t length) {
|
||||||
|
void* data = buffer->writePtr;
|
||||||
|
void* end = buffer->readPtr;
|
||||||
|
size_t remaining;
|
||||||
|
if ((intptr_t) data - (intptr_t) buffer->data + buffer->maxalloc >= buffer->capacity) {
|
||||||
|
data = buffer->data;
|
||||||
|
}
|
||||||
|
if (data >= end) {
|
||||||
|
remaining = (intptr_t) buffer->data + buffer->capacity - (intptr_t) data;
|
||||||
|
} else {
|
||||||
|
remaining = (intptr_t) end - (intptr_t) data;
|
||||||
|
}
|
||||||
|
if (remaining <= length) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
memcpy(data, value, length);
|
||||||
|
buffer->writePtr = (void*) ((intptr_t) data + length);
|
||||||
|
return length;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t RingFIFORead(struct RingFIFO* buffer, void* output, size_t length) {
|
||||||
|
void* data = buffer->readPtr;
|
||||||
|
void* end = buffer->writePtr;
|
||||||
|
size_t remaining;
|
||||||
|
if ((intptr_t) data - (intptr_t) buffer->data + buffer->maxalloc >= buffer->capacity) {
|
||||||
|
data = buffer->data;
|
||||||
|
}
|
||||||
|
if (data > end) {
|
||||||
|
remaining = (intptr_t) buffer->data + buffer->capacity - (intptr_t) data;
|
||||||
|
} else {
|
||||||
|
remaining = (intptr_t) end - (intptr_t) data;
|
||||||
|
}
|
||||||
|
if (remaining <= length) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
memcpy(output, data, length);
|
||||||
|
buffer->readPtr = (void*) ((intptr_t) data + length);
|
||||||
|
return length;
|
||||||
|
}
|
|
@ -0,0 +1,26 @@
|
||||||
|
/* Copyright (c) 2013-2014 Jeffrey Pfau
|
||||||
|
*
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
#ifndef RING_FIFO_H
|
||||||
|
#define RING_FIFO_H
|
||||||
|
|
||||||
|
#include "util/common.h"
|
||||||
|
|
||||||
|
struct RingFIFO {
|
||||||
|
void* data;
|
||||||
|
size_t capacity;
|
||||||
|
size_t maxalloc;
|
||||||
|
void* readPtr;
|
||||||
|
void* writePtr;
|
||||||
|
};
|
||||||
|
|
||||||
|
void RingFIFOInit(struct RingFIFO* buffer, size_t capacity, size_t maxalloc);
|
||||||
|
void RingFIFODeinit(struct RingFIFO* buffer);
|
||||||
|
size_t RingFIFOCapacity(const struct RingFIFO* buffer);
|
||||||
|
void RingFIFOClear(struct RingFIFO* buffer);
|
||||||
|
size_t RingFIFOWrite(struct RingFIFO* buffer, const void* value, size_t length);
|
||||||
|
size_t RingFIFORead(struct RingFIFO* buffer, void* output, size_t length);
|
||||||
|
|
||||||
|
#endif
|
Loading…
Reference in New Issue