diff --git a/include/mgba/core/video-proxy.h b/include/mgba/core/video-proxy.h index 0d6d94def..bd2dd0474 100644 --- a/include/mgba/core/video-proxy.h +++ b/include/mgba/core/video-proxy.h @@ -28,10 +28,13 @@ struct mVideoProxyDirtyInfo { }; struct mVideoProxy { - bool (*writeData)(struct mVideoProxy* proxy, void* data, size_t length); - uint16_t* (*vramBlock)(struct mVideoProxy* proxy, uint32_t address); + bool (*writeData)(struct mVideoProxy* proxy, const void* data, size_t length); + bool (*readData)(struct mVideoProxy* proxy, void* data, size_t length, bool block); 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 oamSize; size_t paletteSize; @@ -56,6 +59,8 @@ void mVideoProxyRendererWriteOAM(struct mVideoProxy* proxy, uint32_t address, ui void mVideoProxyRendererDrawScanline(struct mVideoProxy* proxy, int y); void mVideoProxyRendererFlush(struct mVideoProxy* proxy); +bool mVideoProxyRendererRun(struct mVideoProxy* proxy); + CXX_GUARD_END #endif diff --git a/src/core/video-proxy.c b/src/core/video-proxy.c index 5e736c720..ed3e639ea 100644 --- a/src/core/video-proxy.c +++ b/src/core/video-proxy.c @@ -104,7 +104,6 @@ void mVideoProxyRendererDrawScanline(struct mVideoProxy* proxy, int y) { proxy->writeData(proxy, &dirty, sizeof(dirty)); } - void mVideoProxyRendererFlush(struct mVideoProxy* proxy) { struct mVideoProxyDirtyInfo dirty = { DIRTY_FLUSH, @@ -114,3 +113,24 @@ void mVideoProxyRendererFlush(struct mVideoProxy* proxy) { }; 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; +} diff --git a/src/gba/renderers/thread-proxy.c b/src/gba/renderers/thread-proxy.c index dc9002ed7..6dc7b51fc 100644 --- a/src/gba/renderers/thread-proxy.c +++ b/src/gba/renderers/thread-proxy.c @@ -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 * 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 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); void GBAVideoThreadProxyRendererCreate(struct GBAVideoThreadProxyRenderer* renderer, struct GBAVideoRenderer* backend) { @@ -51,6 +53,8 @@ void GBAVideoThreadProxyRendererCreate(struct GBAVideoThreadProxyRenderer* rende renderer->proxy.context = renderer; renderer->proxy.writeData = _writeData; + renderer->proxy.readData = _readData; + renderer->proxy.parsePacket = _parsePacket; renderer->proxy.vramBlock = _vramBlock; renderer->proxy.paletteSize = SIZE_PALETTE_RAM; renderer->proxy.vramSize = SIZE_VRAM; @@ -134,7 +138,7 @@ void _proxyThreadRecover(struct GBAVideoThreadProxyRenderer* 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; while (!RingFIFOWrite(&proxyRenderer->dirtyQueue, data, 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; } +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) { struct GBAVideoThreadProxyRenderer* proxyRenderer = proxy->context; return &proxyRenderer->d.vram[address >> 1]; @@ -262,55 +312,19 @@ static THREAD_ENTRY _proxyThread(void* renderer) { ThreadSetName("Proxy Renderer Thread"); MutexLock(&proxyRenderer->mutex); - struct mVideoProxyDirtyInfo item = {0}; while (proxyRenderer->threadState != PROXY_THREAD_STOPPED) { ConditionWait(&proxyRenderer->toThreadCond, &proxyRenderer->mutex); if (proxyRenderer->threadState == PROXY_THREAD_STOPPED) { break; } - if (RingFIFORead(&proxyRenderer->dirtyQueue, &item, sizeof(item))) { - proxyRenderer->threadState = PROXY_THREAD_BUSY; - MutexUnlock(&proxyRenderer->mutex); - do { - switch (item.type) { - case DIRTY_REGISTER: - 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); + proxyRenderer->threadState = PROXY_THREAD_BUSY; + MutexUnlock(&proxyRenderer->mutex); + if (!mVideoProxyRendererRun(&proxyRenderer->proxy)) { + // FIFO was corrupted + proxyRenderer->threadState = PROXY_THREAD_STOPPED; + mLOG(GBA_VIDEO, ERROR, "Proxy thread queue got corrupted!"); } - out: + MutexLock(&proxyRenderer->mutex); ConditionWake(&proxyRenderer->fromThreadCond); if (proxyRenderer->threadState != PROXY_THREAD_STOPPED) { proxyRenderer->threadState = PROXY_THREAD_IDLE;