mirror of https://github.com/mgba-emu/mgba.git
Core: Begin splitting threading out from GBA proxy
This commit is contained in:
parent
11edac0aa4
commit
eab5ed6e14
|
@ -28,10 +28,13 @@ struct mVideoProxyDirtyInfo {
|
||||||
};
|
};
|
||||||
|
|
||||||
struct mVideoProxy {
|
struct mVideoProxy {
|
||||||
bool (*writeData)(struct mVideoProxy* proxy, void* data, size_t length);
|
bool (*writeData)(struct mVideoProxy* proxy, const void* data, size_t length);
|
||||||
uint16_t* (*vramBlock)(struct mVideoProxy* proxy, uint32_t address);
|
bool (*readData)(struct mVideoProxy* proxy, void* data, size_t length, bool block);
|
||||||
void* context;
|
void* context;
|
||||||
|
|
||||||
|
bool (*parsePacket)(struct mVideoProxy* proxy, const struct mVideoProxyDirtyInfo* packet);
|
||||||
|
uint16_t* (*vramBlock)(struct mVideoProxy* proxy, uint32_t address);
|
||||||
|
|
||||||
size_t vramSize;
|
size_t vramSize;
|
||||||
size_t oamSize;
|
size_t oamSize;
|
||||||
size_t paletteSize;
|
size_t paletteSize;
|
||||||
|
@ -56,6 +59,8 @@ void mVideoProxyRendererWriteOAM(struct mVideoProxy* proxy, uint32_t address, ui
|
||||||
void mVideoProxyRendererDrawScanline(struct mVideoProxy* proxy, int y);
|
void mVideoProxyRendererDrawScanline(struct mVideoProxy* proxy, int y);
|
||||||
void mVideoProxyRendererFlush(struct mVideoProxy* proxy);
|
void mVideoProxyRendererFlush(struct mVideoProxy* proxy);
|
||||||
|
|
||||||
|
bool mVideoProxyRendererRun(struct mVideoProxy* proxy);
|
||||||
|
|
||||||
CXX_GUARD_END
|
CXX_GUARD_END
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -104,7 +104,6 @@ void mVideoProxyRendererDrawScanline(struct mVideoProxy* proxy, int y) {
|
||||||
proxy->writeData(proxy, &dirty, sizeof(dirty));
|
proxy->writeData(proxy, &dirty, sizeof(dirty));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void mVideoProxyRendererFlush(struct mVideoProxy* proxy) {
|
void mVideoProxyRendererFlush(struct mVideoProxy* proxy) {
|
||||||
struct mVideoProxyDirtyInfo dirty = {
|
struct mVideoProxyDirtyInfo dirty = {
|
||||||
DIRTY_FLUSH,
|
DIRTY_FLUSH,
|
||||||
|
@ -114,3 +113,24 @@ void mVideoProxyRendererFlush(struct mVideoProxy* proxy) {
|
||||||
};
|
};
|
||||||
proxy->writeData(proxy, &dirty, sizeof(dirty));
|
proxy->writeData(proxy, &dirty, sizeof(dirty));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool mVideoProxyRendererRun(struct mVideoProxy* proxy) {
|
||||||
|
struct mVideoProxyDirtyInfo item = {0};
|
||||||
|
while (proxy->readData(proxy, &item, sizeof(item), false)) {
|
||||||
|
switch (item.type) {
|
||||||
|
case DIRTY_REGISTER:
|
||||||
|
case DIRTY_PALETTE:
|
||||||
|
case DIRTY_OAM:
|
||||||
|
case DIRTY_VRAM:
|
||||||
|
case DIRTY_SCANLINE:
|
||||||
|
case DIRTY_FLUSH:
|
||||||
|
if (!proxy->parsePacket(proxy, &item)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
/* Copyright (c) 2013-2015 Jeffrey Pfau
|
/* Copyright (c) 2013-2017 Jeffrey Pfau
|
||||||
*
|
*
|
||||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
* 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
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
@ -27,7 +27,9 @@ static void GBAVideoThreadProxyRendererPutPixels(struct GBAVideoRenderer* render
|
||||||
|
|
||||||
static THREAD_ENTRY _proxyThread(void* renderer);
|
static THREAD_ENTRY _proxyThread(void* renderer);
|
||||||
|
|
||||||
static bool _writeData(struct mVideoProxy* proxy, void* data, size_t length);
|
static bool _writeData(struct mVideoProxy* proxy, const void* data, size_t length);
|
||||||
|
static bool _readData(struct mVideoProxy* proxy, void* data, size_t length, bool block);
|
||||||
|
static bool _parsePacket(struct mVideoProxy* proxy, const struct mVideoProxyDirtyInfo* packet);
|
||||||
static uint16_t* _vramBlock(struct mVideoProxy* proxy, uint32_t address);
|
static uint16_t* _vramBlock(struct mVideoProxy* proxy, uint32_t address);
|
||||||
|
|
||||||
void GBAVideoThreadProxyRendererCreate(struct GBAVideoThreadProxyRenderer* renderer, struct GBAVideoRenderer* backend) {
|
void GBAVideoThreadProxyRendererCreate(struct GBAVideoThreadProxyRenderer* renderer, struct GBAVideoRenderer* backend) {
|
||||||
|
@ -51,6 +53,8 @@ void GBAVideoThreadProxyRendererCreate(struct GBAVideoThreadProxyRenderer* rende
|
||||||
|
|
||||||
renderer->proxy.context = renderer;
|
renderer->proxy.context = renderer;
|
||||||
renderer->proxy.writeData = _writeData;
|
renderer->proxy.writeData = _writeData;
|
||||||
|
renderer->proxy.readData = _readData;
|
||||||
|
renderer->proxy.parsePacket = _parsePacket;
|
||||||
renderer->proxy.vramBlock = _vramBlock;
|
renderer->proxy.vramBlock = _vramBlock;
|
||||||
renderer->proxy.paletteSize = SIZE_PALETTE_RAM;
|
renderer->proxy.paletteSize = SIZE_PALETTE_RAM;
|
||||||
renderer->proxy.vramSize = SIZE_VRAM;
|
renderer->proxy.vramSize = SIZE_VRAM;
|
||||||
|
@ -134,7 +138,7 @@ void _proxyThreadRecover(struct GBAVideoThreadProxyRenderer* proxyRenderer) {
|
||||||
ThreadCreate(&proxyRenderer->thread, _proxyThread, proxyRenderer);
|
ThreadCreate(&proxyRenderer->thread, _proxyThread, proxyRenderer);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool _writeData(struct mVideoProxy* proxy, void* data, size_t length) {
|
static bool _writeData(struct mVideoProxy* proxy, const void* data, size_t length) {
|
||||||
struct GBAVideoThreadProxyRenderer* proxyRenderer = proxy->context;
|
struct GBAVideoThreadProxyRenderer* proxyRenderer = proxy->context;
|
||||||
while (!RingFIFOWrite(&proxyRenderer->dirtyQueue, data, length)) {
|
while (!RingFIFOWrite(&proxyRenderer->dirtyQueue, data, length)) {
|
||||||
mLOG(GBA_VIDEO, DEBUG, "Can't write %"PRIz"u bytes. Proxy thread asleep?", length);
|
mLOG(GBA_VIDEO, DEBUG, "Can't write %"PRIz"u bytes. Proxy thread asleep?", length);
|
||||||
|
@ -151,6 +155,52 @@ static bool _writeData(struct mVideoProxy* proxy, void* data, size_t length) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool _readData(struct mVideoProxy* proxy, void* data, size_t length, bool block) {
|
||||||
|
struct GBAVideoThreadProxyRenderer* proxyRenderer = proxy->context;
|
||||||
|
bool read = false;
|
||||||
|
while (true) {
|
||||||
|
read = RingFIFORead(&proxyRenderer->dirtyQueue, data, length);
|
||||||
|
if (!block || read) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
mLOG(GBA_VIDEO, DEBUG, "Proxy thread can't read VRAM. CPU thread asleep?");
|
||||||
|
MutexLock(&proxyRenderer->mutex);
|
||||||
|
ConditionWake(&proxyRenderer->fromThreadCond);
|
||||||
|
ConditionWait(&proxyRenderer->toThreadCond, &proxyRenderer->mutex);
|
||||||
|
MutexUnlock(&proxyRenderer->mutex);
|
||||||
|
}
|
||||||
|
return read;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool _parsePacket(struct mVideoProxy* proxy, const struct mVideoProxyDirtyInfo* item) {
|
||||||
|
struct GBAVideoThreadProxyRenderer* proxyRenderer = proxy->context;
|
||||||
|
switch (item->type) {
|
||||||
|
case DIRTY_REGISTER:
|
||||||
|
proxyRenderer->backend->writeVideoRegister(proxyRenderer->backend, item->address, item->value);
|
||||||
|
break;
|
||||||
|
case DIRTY_PALETTE:
|
||||||
|
proxy->palette[item->address >> 1] = item->value;
|
||||||
|
proxyRenderer->backend->writePalette(proxyRenderer->backend, item->address, item->value);
|
||||||
|
break;
|
||||||
|
case DIRTY_OAM:
|
||||||
|
proxy->oam[item->address] = item->value;
|
||||||
|
proxyRenderer->backend->writeOAM(proxyRenderer->backend, item->address);
|
||||||
|
break;
|
||||||
|
case DIRTY_VRAM:
|
||||||
|
proxy->readData(proxy, &proxy->vram[item->address >> 1], 0x1000, true);
|
||||||
|
proxyRenderer->backend->writeVRAM(proxyRenderer->backend, item->address);
|
||||||
|
break;
|
||||||
|
case DIRTY_SCANLINE:
|
||||||
|
proxyRenderer->backend->drawScanline(proxyRenderer->backend, item->address);
|
||||||
|
break;
|
||||||
|
case DIRTY_FLUSH:
|
||||||
|
return false;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
static uint16_t* _vramBlock(struct mVideoProxy* proxy, uint32_t address) {
|
static uint16_t* _vramBlock(struct mVideoProxy* proxy, uint32_t address) {
|
||||||
struct GBAVideoThreadProxyRenderer* proxyRenderer = proxy->context;
|
struct GBAVideoThreadProxyRenderer* proxyRenderer = proxy->context;
|
||||||
return &proxyRenderer->d.vram[address >> 1];
|
return &proxyRenderer->d.vram[address >> 1];
|
||||||
|
@ -262,55 +312,19 @@ static THREAD_ENTRY _proxyThread(void* renderer) {
|
||||||
ThreadSetName("Proxy Renderer Thread");
|
ThreadSetName("Proxy Renderer Thread");
|
||||||
|
|
||||||
MutexLock(&proxyRenderer->mutex);
|
MutexLock(&proxyRenderer->mutex);
|
||||||
struct mVideoProxyDirtyInfo 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);
|
if (!mVideoProxyRendererRun(&proxyRenderer->proxy)) {
|
||||||
do {
|
// FIFO was corrupted
|
||||||
switch (item.type) {
|
proxyRenderer->threadState = PROXY_THREAD_STOPPED;
|
||||||
case DIRTY_REGISTER:
|
mLOG(GBA_VIDEO, ERROR, "Proxy thread queue got corrupted!");
|
||||||
proxyRenderer->backend->writeVideoRegister(proxyRenderer->backend, item.address, item.value);
|
|
||||||
break;
|
|
||||||
case DIRTY_PALETTE:
|
|
||||||
proxyRenderer->proxy.palette[item.address >> 1] = item.value;
|
|
||||||
proxyRenderer->backend->writePalette(proxyRenderer->backend, item.address, item.value);
|
|
||||||
break;
|
|
||||||
case DIRTY_OAM:
|
|
||||||
proxyRenderer->proxy.oam[item.address] = item.value;
|
|
||||||
proxyRenderer->backend->writeOAM(proxyRenderer->backend, item.address);
|
|
||||||
break;
|
|
||||||
case DIRTY_VRAM:
|
|
||||||
while (!RingFIFORead(&proxyRenderer->dirtyQueue, &proxyRenderer->proxy.vram[item.address >> 1], 0x1000)) {
|
|
||||||
mLOG(GBA_VIDEO, DEBUG, "Proxy thread can't read VRAM. CPU thread asleep?");
|
|
||||||
MutexLock(&proxyRenderer->mutex);
|
|
||||||
ConditionWake(&proxyRenderer->fromThreadCond);
|
|
||||||
ConditionWait(&proxyRenderer->toThreadCond, &proxyRenderer->mutex);
|
|
||||||
MutexUnlock(&proxyRenderer->mutex);
|
|
||||||
}
|
|
||||||
proxyRenderer->backend->writeVRAM(proxyRenderer->backend, item.address);
|
|
||||||
break;
|
|
||||||
case DIRTY_SCANLINE:
|
|
||||||
proxyRenderer->backend->drawScanline(proxyRenderer->backend, item.address);
|
|
||||||
break;
|
|
||||||
case DIRTY_FLUSH:
|
|
||||||
MutexLock(&proxyRenderer->mutex);
|
|
||||||
goto out;
|
|
||||||
default:
|
|
||||||
// FIFO was corrupted
|
|
||||||
MutexLock(&proxyRenderer->mutex);
|
|
||||||
proxyRenderer->threadState = PROXY_THREAD_STOPPED;
|
|
||||||
mLOG(GBA_VIDEO, ERROR, "Proxy thread queue got corrupted!");
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
} while (proxyRenderer->threadState == PROXY_THREAD_BUSY && RingFIFORead(&proxyRenderer->dirtyQueue, &item, sizeof(item)));
|
|
||||||
MutexLock(&proxyRenderer->mutex);
|
|
||||||
}
|
}
|
||||||
out:
|
MutexLock(&proxyRenderer->mutex);
|
||||||
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;
|
||||||
|
|
Loading…
Reference in New Issue