all repos — mgba @ e1ffc685827fc0f4068b745533457b43f5a1a1e9

mGBA Game Boy Advance Emulator

GBA Video: Better threaded renderer using a new RingFIFO
Jeffrey Pfau jeffrey@endrift.com
Sat, 29 Aug 2015 20:08:00 -0700
commit

e1ffc685827fc0f4068b745533457b43f5a1a1e9

parent

0cfff99652a9d9324f9904b3223128d324096422

M src/gba/renderers/thread-proxy.csrc/gba/renderers/thread-proxy.c

@@ -9,7 +9,24 @@ #include "gba/io.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 GBAVideoThreadProxyRendererReset(struct GBAVideoRenderer* renderer);

@@ -52,7 +69,7 @@ struct GBAVideoThreadProxyRenderer* proxyRenderer = (struct GBAVideoThreadProxyRenderer*) 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 @@

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 @@ struct GBAVideoThreadProxyRenderer* proxyRenderer = (struct GBAVideoThreadProxyRenderer*) 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 @@ proxyRenderer->oamDirtyBitmap[base] |= bit;

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 @@ // Insert an extra item into the queue to make sure it gets flushed

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,51 +265,41 @@ struct GBAVideoThreadProxyRenderer* proxyRenderer = 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; } }

@@ -284,3 +311,5 @@ MutexUnlock(&proxyRenderer->mutex);

return 0; } + +#endif
M src/gba/renderers/thread-proxy.hsrc/gba/renderers/thread-proxy.h

@@ -8,15 +8,7 @@ #define VIDEO_THREAD_PROXY_H

#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 @@ PROXY_THREAD_IDLE,

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

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);
A src/util/ring-fifo.c

@@ -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; +}
A src/util/ring-fifo.h

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