mirror of https://github.com/mgba-emu/mgba.git
GBA Video: Fix threaded rendering race conditions
This commit is contained in:
parent
492c2612b9
commit
30f124fae4
|
@ -70,7 +70,6 @@ void GBAVideoThreadProxyRendererInit(struct GBAVideoRenderer* renderer) {
|
||||||
ConditionInit(&proxyRenderer->toThreadCond);
|
ConditionInit(&proxyRenderer->toThreadCond);
|
||||||
MutexInit(&proxyRenderer->mutex);
|
MutexInit(&proxyRenderer->mutex);
|
||||||
RingFIFOInit(&proxyRenderer->dirtyQueue, 0x40000, 0x1000);
|
RingFIFOInit(&proxyRenderer->dirtyQueue, 0x40000, 0x1000);
|
||||||
proxyRenderer->threadState = PROXY_THREAD_STOPPED;
|
|
||||||
|
|
||||||
proxyRenderer->vramProxy = anonymousMemoryMap(SIZE_VRAM);
|
proxyRenderer->vramProxy = anonymousMemoryMap(SIZE_VRAM);
|
||||||
proxyRenderer->backend->palette = proxyRenderer->paletteProxy;
|
proxyRenderer->backend->palette = proxyRenderer->paletteProxy;
|
||||||
|
@ -230,10 +229,11 @@ void GBAVideoThreadProxyRendererFinishFrame(struct GBAVideoRenderer* renderer) {
|
||||||
0xDEADBEEF,
|
0xDEADBEEF,
|
||||||
};
|
};
|
||||||
RingFIFOWrite(&proxyRenderer->dirtyQueue, &dirty, sizeof(dirty));
|
RingFIFOWrite(&proxyRenderer->dirtyQueue, &dirty, sizeof(dirty));
|
||||||
while (proxyRenderer->threadState == PROXY_THREAD_BUSY) {
|
do {
|
||||||
|
RingFIFOWrite(&proxyRenderer->dirtyQueue, &dirty, sizeof(dirty));
|
||||||
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);
|
||||||
proxyRenderer->vramDirtyBitmap = 0;
|
proxyRenderer->vramDirtyBitmap = 0;
|
||||||
MutexUnlock(&proxyRenderer->mutex);
|
MutexUnlock(&proxyRenderer->mutex);
|
||||||
|
@ -280,16 +280,16 @@ static THREAD_ENTRY _proxyThread(void* renderer) {
|
||||||
ThreadSetName("Proxy Renderer Thread");
|
ThreadSetName("Proxy Renderer Thread");
|
||||||
|
|
||||||
MutexLock(&proxyRenderer->mutex);
|
MutexLock(&proxyRenderer->mutex);
|
||||||
|
struct GBAVideoDirtyInfo item = {0};
|
||||||
while (proxyRenderer->threadState != PROXY_THREAD_STOPPED) {
|
while (proxyRenderer->threadState != PROXY_THREAD_STOPPED) {
|
||||||
ConditionWait(&proxyRenderer->toThreadCond, &proxyRenderer->mutex);
|
ConditionWait(&proxyRenderer->toThreadCond, &proxyRenderer->mutex);
|
||||||
if (proxyRenderer->threadState == PROXY_THREAD_STOPPED) {
|
if (proxyRenderer->threadState == PROXY_THREAD_STOPPED) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
if (RingFIFORead(&proxyRenderer->dirtyQueue, &item, sizeof(item))) {
|
||||||
proxyRenderer->threadState = PROXY_THREAD_BUSY;
|
proxyRenderer->threadState = PROXY_THREAD_BUSY;
|
||||||
|
|
||||||
MutexUnlock(&proxyRenderer->mutex);
|
MutexUnlock(&proxyRenderer->mutex);
|
||||||
struct GBAVideoDirtyInfo item;
|
do {
|
||||||
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);
|
||||||
|
@ -310,15 +310,18 @@ static THREAD_ENTRY _proxyThread(void* renderer) {
|
||||||
proxyRenderer->backend->drawScanline(proxyRenderer->backend, item.address);
|
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
|
MutexLock(&proxyRenderer->mutex);
|
||||||
break;
|
goto out;
|
||||||
default:
|
default:
|
||||||
// FIFO was corrupted
|
// FIFO was corrupted
|
||||||
proxyRenderer->threadState = PROXY_THREAD_STOPPED;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
MutexLock(&proxyRenderer->mutex);
|
MutexLock(&proxyRenderer->mutex);
|
||||||
|
proxyRenderer->threadState = PROXY_THREAD_STOPPED;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
} while (proxyRenderer->threadState == PROXY_THREAD_BUSY && RingFIFORead(&proxyRenderer->dirtyQueue, &item, sizeof(item)));
|
||||||
|
MutexLock(&proxyRenderer->mutex);
|
||||||
|
}
|
||||||
|
out:
|
||||||
ConditionWake(&proxyRenderer->fromThreadCond);
|
ConditionWake(&proxyRenderer->fromThreadCond);
|
||||||
if (proxyRenderer->threadState != PROXY_THREAD_STOPPED) {
|
if (proxyRenderer->threadState != PROXY_THREAD_STOPPED) {
|
||||||
proxyRenderer->threadState = PROXY_THREAD_IDLE;
|
proxyRenderer->threadState = PROXY_THREAD_IDLE;
|
||||||
|
|
|
@ -7,6 +7,15 @@
|
||||||
|
|
||||||
#include "util/memory.h"
|
#include "util/memory.h"
|
||||||
|
|
||||||
|
#ifndef _MSC_VER
|
||||||
|
#define ATOMIC_STORE(DST, SRC) __atomic_store_n(&DST, SRC, __ATOMIC_RELEASE)
|
||||||
|
#define ATOMIC_LOAD(DST, SRC) DST = __atomic_load_n(&SRC, __ATOMIC_ACQUIRE)
|
||||||
|
#else
|
||||||
|
// TODO
|
||||||
|
#define ATOMIC_STORE(DST, SRC) DST = SRC
|
||||||
|
#define ATOMIC_LOAD(DST, SRC) DST = SRC
|
||||||
|
#endif
|
||||||
|
|
||||||
void RingFIFOInit(struct RingFIFO* buffer, size_t capacity, size_t maxalloc) {
|
void RingFIFOInit(struct RingFIFO* buffer, size_t capacity, size_t maxalloc) {
|
||||||
buffer->data = anonymousMemoryMap(capacity);
|
buffer->data = anonymousMemoryMap(capacity);
|
||||||
buffer->capacity = capacity;
|
buffer->capacity = capacity;
|
||||||
|
@ -24,13 +33,14 @@ size_t RingFIFOCapacity(const struct RingFIFO* buffer) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void RingFIFOClear(struct RingFIFO* buffer) {
|
void RingFIFOClear(struct RingFIFO* buffer) {
|
||||||
buffer->readPtr = buffer->data;
|
ATOMIC_STORE(buffer->readPtr, buffer->data);
|
||||||
buffer->writePtr = buffer->data;
|
ATOMIC_STORE(buffer->writePtr, buffer->data);
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t RingFIFOWrite(struct RingFIFO* buffer, const void* value, size_t length) {
|
size_t RingFIFOWrite(struct RingFIFO* buffer, const void* value, size_t length) {
|
||||||
void* data = buffer->writePtr;
|
void* data = buffer->writePtr;
|
||||||
void* end = buffer->readPtr;
|
void* end;
|
||||||
|
ATOMIC_LOAD(end, buffer->readPtr);
|
||||||
size_t remaining;
|
size_t remaining;
|
||||||
if ((intptr_t) data - (intptr_t) buffer->data + buffer->maxalloc >= buffer->capacity) {
|
if ((intptr_t) data - (intptr_t) buffer->data + buffer->maxalloc >= buffer->capacity) {
|
||||||
data = buffer->data;
|
data = buffer->data;
|
||||||
|
@ -46,13 +56,14 @@ size_t RingFIFOWrite(struct RingFIFO* buffer, const void* value, size_t length)
|
||||||
if (value) {
|
if (value) {
|
||||||
memcpy(data, value, length);
|
memcpy(data, value, length);
|
||||||
}
|
}
|
||||||
buffer->writePtr = (void*) ((intptr_t) data + length);
|
ATOMIC_STORE(buffer->writePtr, (void*) ((intptr_t) data + length));
|
||||||
return length;
|
return length;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t RingFIFORead(struct RingFIFO* buffer, void* output, size_t length) {
|
size_t RingFIFORead(struct RingFIFO* buffer, void* output, size_t length) {
|
||||||
void* data = buffer->readPtr;
|
void* data = buffer->readPtr;
|
||||||
void* end = buffer->writePtr;
|
void* end;
|
||||||
|
ATOMIC_LOAD(end, buffer->writePtr);
|
||||||
size_t remaining;
|
size_t remaining;
|
||||||
if ((intptr_t) data - (intptr_t) buffer->data + buffer->maxalloc >= buffer->capacity) {
|
if ((intptr_t) data - (intptr_t) buffer->data + buffer->maxalloc >= buffer->capacity) {
|
||||||
data = buffer->data;
|
data = buffer->data;
|
||||||
|
@ -68,6 +79,6 @@ size_t RingFIFORead(struct RingFIFO* buffer, void* output, size_t length) {
|
||||||
if (output) {
|
if (output) {
|
||||||
memcpy(output, data, length);
|
memcpy(output, data, length);
|
||||||
}
|
}
|
||||||
buffer->readPtr = (void*) ((intptr_t) data + length);
|
ATOMIC_STORE(buffer->readPtr, (void*) ((intptr_t) data + length));
|
||||||
return length;
|
return length;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue