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"
|
||||
|
||||
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 GBAVideoThreadProxyRendererReset(struct GBAVideoRenderer* renderer);
|
||||
|
@ -52,7 +69,7 @@ void GBAVideoThreadProxyRendererInit(struct GBAVideoRenderer* renderer) {
|
|||
ConditionInit(&proxyRenderer->fromThreadCond);
|
||||
ConditionInit(&proxyRenderer->toThreadCond);
|
||||
MutexInit(&proxyRenderer->mutex);
|
||||
GBAVideoDirtyQueueInit(&proxyRenderer->dirtyQueue, 1024);
|
||||
RingFIFOInit(&proxyRenderer->dirtyQueue, 0x200000, 0x1000);
|
||||
proxyRenderer->threadState = PROXY_THREAD_STOPPED;
|
||||
|
||||
proxyRenderer->vramProxy = anonymousMemoryMap(SIZE_VRAM);
|
||||
|
@ -137,9 +154,10 @@ uint16_t GBAVideoThreadProxyRendererWriteVideoRegister(struct GBAVideoRenderer*
|
|||
struct GBAVideoDirtyInfo dirty = {
|
||||
DIRTY_REGISTER,
|
||||
address,
|
||||
value
|
||||
value,
|
||||
0xDEADBEEF,
|
||||
};
|
||||
*GBAVideoDirtyQueueAppend(&proxyRenderer->dirtyQueue) = dirty;
|
||||
RingFIFOWrite(&proxyRenderer->dirtyQueue, &dirty, sizeof(dirty));
|
||||
ConditionWake(&proxyRenderer->toThreadCond);
|
||||
return value;
|
||||
}
|
||||
|
@ -158,15 +176,15 @@ void GBAVideoThreadProxyRendererWritePalette(struct GBAVideoRenderer* renderer,
|
|||
struct GBAVideoDirtyInfo dirty = {
|
||||
DIRTY_PALETTE,
|
||||
address,
|
||||
value
|
||||
value,
|
||||
0xDEADBEEF,
|
||||
};
|
||||
*GBAVideoDirtyQueueAppend(&proxyRenderer->dirtyQueue) = dirty;
|
||||
RingFIFOWrite(&proxyRenderer->dirtyQueue, &dirty, sizeof(dirty));
|
||||
ConditionWake(&proxyRenderer->toThreadCond);
|
||||
}
|
||||
|
||||
void GBAVideoThreadProxyRendererWriteOAM(struct GBAVideoRenderer* renderer, uint32_t oam) {
|
||||
struct GBAVideoThreadProxyRenderer* proxyRenderer = (struct GBAVideoThreadProxyRenderer*) renderer;
|
||||
proxyRenderer->oamProxy.raw[oam] = proxyRenderer->d.oam->raw[oam];
|
||||
int bit = 1 << (oam & 31);
|
||||
int base = oam >> 5;
|
||||
if (proxyRenderer->oamDirtyBitmap[base] & bit) {
|
||||
|
@ -176,20 +194,40 @@ void GBAVideoThreadProxyRendererWriteOAM(struct GBAVideoRenderer* renderer, uint
|
|||
struct GBAVideoDirtyInfo dirty = {
|
||||
DIRTY_OAM,
|
||||
oam,
|
||||
0
|
||||
proxyRenderer->d.oam->raw[oam],
|
||||
0xDEADBEEF,
|
||||
};
|
||||
*GBAVideoDirtyQueueAppend(&proxyRenderer->dirtyQueue) = dirty;
|
||||
RingFIFOWrite(&proxyRenderer->dirtyQueue, &dirty, sizeof(dirty));
|
||||
ConditionWake(&proxyRenderer->toThreadCond);
|
||||
}
|
||||
|
||||
void GBAVideoThreadProxyRendererDrawScanline(struct GBAVideoRenderer* renderer, int y) {
|
||||
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 = {
|
||||
DIRTY_SCANLINE,
|
||||
y,
|
||||
0
|
||||
0,
|
||||
0xDEADBEEF,
|
||||
};
|
||||
*GBAVideoDirtyQueueAppend(&proxyRenderer->dirtyQueue) = dirty;
|
||||
RingFIFOWrite(&proxyRenderer->dirtyQueue, &dirty, sizeof(dirty));
|
||||
ConditionWake(&proxyRenderer->toThreadCond);
|
||||
}
|
||||
|
||||
|
@ -200,16 +238,15 @@ void GBAVideoThreadProxyRendererFinishFrame(struct GBAVideoRenderer* renderer) {
|
|||
struct GBAVideoDirtyInfo dirty = {
|
||||
DIRTY_FLUSH,
|
||||
0,
|
||||
0
|
||||
0,
|
||||
0xDEADBEEF,
|
||||
};
|
||||
*GBAVideoDirtyQueueAppend(&proxyRenderer->dirtyQueue) = dirty;
|
||||
RingFIFOWrite(&proxyRenderer->dirtyQueue, &dirty, sizeof(dirty));
|
||||
do {
|
||||
ConditionWake(&proxyRenderer->toThreadCond);
|
||||
ConditionWait(&proxyRenderer->fromThreadCond, &proxyRenderer->mutex);
|
||||
} while (proxyRenderer->threadState == PROXY_THREAD_BUSY);
|
||||
proxyRenderer->backend->finishFrame(proxyRenderer->backend);
|
||||
GBAVideoDirtyQueueClear(&proxyRenderer->dirtyQueue);
|
||||
proxyRenderer->queueCleared = true;
|
||||
proxyRenderer->vramDirtyBitmap = 0;
|
||||
memset(proxyRenderer->oamDirtyBitmap, 0, sizeof(proxyRenderer->oamDirtyBitmap));
|
||||
MutexUnlock(&proxyRenderer->mutex);
|
||||
|
@ -228,52 +265,42 @@ static THREAD_ENTRY _proxyThread(void* renderer) {
|
|||
ThreadSetName("Proxy Renderer Thread");
|
||||
|
||||
MutexLock(&proxyRenderer->mutex);
|
||||
size_t i = 0;
|
||||
while (1) {
|
||||
ConditionWait(&proxyRenderer->toThreadCond, &proxyRenderer->mutex);
|
||||
if (proxyRenderer->threadState == PROXY_THREAD_STOPPED) {
|
||||
break;
|
||||
}
|
||||
proxyRenderer->threadState = PROXY_THREAD_BUSY;
|
||||
if (proxyRenderer->queueCleared) {
|
||||
proxyRenderer->queueCleared = false;
|
||||
i = 0;
|
||||
}
|
||||
if (!GBAVideoDirtyQueueSize(&proxyRenderer->dirtyQueue)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
MutexUnlock(&proxyRenderer->mutex);
|
||||
for (; i < GBAVideoDirtyQueueSize(&proxyRenderer->dirtyQueue) - 1; ++i) {
|
||||
struct GBAVideoDirtyInfo* item = GBAVideoDirtyQueueGetPointer(&proxyRenderer->dirtyQueue, i);
|
||||
switch (item->type) {
|
||||
struct GBAVideoDirtyInfo item;
|
||||
while (RingFIFORead(&proxyRenderer->dirtyQueue, &item, sizeof(item))) {
|
||||
switch (item.type) {
|
||||
case DIRTY_REGISTER:
|
||||
proxyRenderer->backend->writeVideoRegister(proxyRenderer->backend, item->address, item->value);
|
||||
proxyRenderer->backend->writeVideoRegister(proxyRenderer->backend, item.address, item.value);
|
||||
break;
|
||||
case DIRTY_PALETTE:
|
||||
proxyRenderer->paletteProxy[item->address >> 1] = item->value;
|
||||
proxyRenderer->backend->writePalette(proxyRenderer->backend, item->address, item->value);
|
||||
proxyRenderer->paletteProxy[item.address >> 1] = item.value;
|
||||
proxyRenderer->backend->writePalette(proxyRenderer->backend, item.address, item.value);
|
||||
break;
|
||||
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;
|
||||
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);
|
||||
proxyRenderer->backend->drawScanline(proxyRenderer->backend, item.address);
|
||||
break;
|
||||
case DIRTY_FLUSH:
|
||||
// This is only here to ensure the queue gets flushed
|
||||
break;
|
||||
default:
|
||||
// FIFO was corrupted
|
||||
abort();
|
||||
break;
|
||||
}
|
||||
}
|
||||
MutexLock(&proxyRenderer->mutex);
|
||||
|
@ -284,3 +311,5 @@ static THREAD_ENTRY _proxyThread(void* renderer) {
|
|||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -8,15 +8,7 @@
|
|||
|
||||
#include "gba/video.h"
|
||||
#include "util/threading.h"
|
||||
#include "util/vector.h"
|
||||
|
||||
enum GBAVideoDirtyType {
|
||||
DIRTY_REGISTER,
|
||||
DIRTY_OAM,
|
||||
DIRTY_PALETTE,
|
||||
DIRTY_SCANLINE,
|
||||
DIRTY_FLUSH
|
||||
};
|
||||
#include "util/ring-fifo.h"
|
||||
|
||||
enum GBAVideoThreadProxyState {
|
||||
PROXY_THREAD_STOPPED = 0,
|
||||
|
@ -24,14 +16,6 @@ enum GBAVideoThreadProxyState {
|
|||
PROXY_THREAD_BUSY
|
||||
};
|
||||
|
||||
struct GBAVideoDirtyInfo {
|
||||
enum GBAVideoDirtyType type;
|
||||
uint32_t address;
|
||||
uint16_t value;
|
||||
};
|
||||
|
||||
DECLARE_VECTOR(GBAVideoDirtyQueue, struct GBAVideoDirtyInfo);
|
||||
|
||||
struct GBAVideoThreadProxyRenderer {
|
||||
struct GBAVideoRenderer d;
|
||||
struct GBAVideoRenderer* backend;
|
||||
|
@ -42,15 +26,14 @@ struct GBAVideoThreadProxyRenderer {
|
|||
Mutex mutex;
|
||||
enum GBAVideoThreadProxyState threadState;
|
||||
|
||||
struct GBAVideoDirtyQueue dirtyQueue;
|
||||
struct RingFIFO dirtyQueue;
|
||||
|
||||
uint32_t vramDirtyBitmap;
|
||||
uint32_t oamDirtyBitmap[16];
|
||||
|
||||
uint16_t* vramProxy;
|
||||
union GBAOAM oamProxy;
|
||||
uint16_t paletteProxy[512];
|
||||
|
||||
bool queueCleared;
|
||||
};
|
||||
|
||||
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