Core: Start working on video proxy
@@ -0,0 +1,61 @@
+/* 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 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#ifndef VIDEO_PROXY_H +#define VIDEO_PROXY_H + +#include <mgba-util/common.h> + +CXX_GUARD_START + +enum mVideoProxyDirtyType { + DIRTY_DUMMY = 0, + DIRTY_FLUSH, + DIRTY_SCANLINE, + DIRTY_REGISTER, + DIRTY_OAM, + DIRTY_PALETTE, + DIRTY_VRAM +}; + +struct mVideoProxyDirtyInfo { + enum mVideoProxyDirtyType type; + uint32_t address; + uint16_t value; + uint32_t padding; +}; + +struct mVideoProxy { + bool (*writeData)(struct mVideoProxy* proxy, void* data, size_t length); + uint16_t* (*vramBlock)(struct mVideoProxy* proxy, uint32_t address); + void* context; + + size_t vramSize; + size_t oamSize; + size_t paletteSize; + + uint32_t* vramDirtyBitmap; + uint32_t* oamDirtyBitmap; + + uint16_t* vram; + uint16_t* oam; + uint16_t* palette; +}; + +void mVideoProxyRendererInit(struct mVideoProxy* proxy); +void mVideoProxyRendererDeinit(struct mVideoProxy* proxy); +void mVideoProxyRendererReset(struct mVideoProxy* proxy); + +void mVideoProxyRendererWriteVideoRegister(struct mVideoProxy* proxy, uint32_t address, uint16_t value); +void mVideoProxyRendererWriteVRAM(struct mVideoProxy* proxy, uint32_t address); +void mVideoProxyRendererWritePalette(struct mVideoProxy* proxy, uint32_t address, uint16_t value); +void mVideoProxyRendererWriteOAM(struct mVideoProxy* proxy, uint32_t address, uint16_t value); + +void mVideoProxyRendererDrawScanline(struct mVideoProxy* proxy, int y); +void mVideoProxyRendererFlush(struct mVideoProxy* proxy); + +CXX_GUARD_END + +#endif
@@ -10,6 +10,7 @@ #include <mgba-util/common.h>
CXX_GUARD_START +#include <mgba/core/video-proxy.h> #include <mgba/internal/gba/video.h> #include <mgba-util/threading.h> #include <mgba-util/ring-fifo.h>@@ -23,6 +24,7 @@
struct GBAVideoThreadProxyRenderer { struct GBAVideoRenderer d; struct GBAVideoRenderer* backend; + struct mVideoProxy proxy; Thread thread; Condition fromThreadCond;@@ -31,13 +33,6 @@ Mutex mutex;
enum GBAVideoThreadProxyState threadState; struct RingFIFO dirtyQueue; - - uint32_t vramDirtyBitmap; - uint32_t oamDirtyBitmap[16]; - - uint16_t* vramProxy; - union GBAOAM oamProxy; - uint16_t paletteProxy[512]; }; void GBAVideoThreadProxyRendererCreate(struct GBAVideoThreadProxyRenderer* renderer, struct GBAVideoRenderer* backend);
@@ -0,0 +1,116 @@
+/* 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 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#include <mgba/core/video-proxy.h> + +#include <mgba-util/memory.h> + +static inline size_t _roundUp(size_t value, int shift) { + value += (1 << shift) - 1; + return value >> shift; +} + +void mVideoProxyRendererInit(struct mVideoProxy* proxy) { + proxy->palette = anonymousMemoryMap(proxy->paletteSize); + proxy->vram = anonymousMemoryMap(proxy->vramSize); + proxy->oam = anonymousMemoryMap(proxy->oamSize); + + proxy->vramDirtyBitmap = calloc(_roundUp(proxy->vramSize, 17), sizeof(uint32_t)); + proxy->oamDirtyBitmap = calloc(_roundUp(proxy->oamSize, 6), sizeof(uint32_t)); +} + +void mVideoProxyRendererDeinit(struct mVideoProxy* proxy) { + mappedMemoryFree(proxy->palette, proxy->paletteSize); + mappedMemoryFree(proxy->vram, proxy->vramSize); + mappedMemoryFree(proxy->oam, proxy->oamSize); + + free(proxy->vramDirtyBitmap); + free(proxy->oamDirtyBitmap); +} + +void mVideoProxyRendererReset(struct mVideoProxy* proxy) { + memset(proxy->vramDirtyBitmap, 0, sizeof(uint32_t) * _roundUp(proxy->vramSize, 17)); + memset(proxy->oamDirtyBitmap, 0, sizeof(uint32_t) * _roundUp(proxy->oamSize, 6)); +} + +void mVideoProxyRendererWriteVideoRegister(struct mVideoProxy* proxy, uint32_t address, uint16_t value) { + struct mVideoProxyDirtyInfo dirty = { + DIRTY_REGISTER, + address, + value, + 0xDEADBEEF, + }; + proxy->writeData(proxy, &dirty, sizeof(dirty)); +} + +void mVideoProxyRendererWriteVRAM(struct mVideoProxy* proxy, uint32_t address) { + int bit = 1 << (address >> 12); + if (proxy->vramDirtyBitmap[address >> 17] & bit) { + return; + } + proxy->vramDirtyBitmap[address >> 17] |= bit; +} + +void mVideoProxyRendererWritePalette(struct mVideoProxy* proxy, uint32_t address, uint16_t value) { + struct mVideoProxyDirtyInfo dirty = { + DIRTY_PALETTE, + address, + value, + 0xDEADBEEF, + }; + proxy->writeData(proxy, &dirty, sizeof(dirty)); +} + +void mVideoProxyRendererWriteOAM(struct mVideoProxy* proxy, uint32_t address, uint16_t value) { + struct mVideoProxyDirtyInfo dirty = { + DIRTY_OAM, + address, + value, + 0xDEADBEEF, + }; + proxy->writeData(proxy, &dirty, sizeof(dirty)); +} + +void mVideoProxyRendererDrawScanline(struct mVideoProxy* proxy, int y) { + size_t i; + for (i = 0; i < _roundUp(proxy->vramSize, 17); ++i) { + if (proxy->vramDirtyBitmap[i]) { + uint32_t bitmap = proxy->vramDirtyBitmap[i]; + proxy->vramDirtyBitmap[i] = 0; + int j; + for (j = 0; j < 32; ++j) { + if (!(bitmap & (1 << j))) { + continue; + } + struct mVideoProxyDirtyInfo dirty = { + DIRTY_VRAM, + j * 0x1000, + 0xABCD, + 0xDEADBEEF, + }; + proxy->writeData(proxy, &dirty, sizeof(dirty)); + proxy->writeData(proxy, proxy->vramBlock(proxy, j * 0x1000), 0x1000); + } + } + } + struct mVideoProxyDirtyInfo dirty = { + DIRTY_SCANLINE, + y, + 0, + 0xDEADBEEF, + }; + proxy->writeData(proxy, &dirty, sizeof(dirty)); +} + + +void mVideoProxyRendererFlush(struct mVideoProxy* proxy) { + struct mVideoProxyDirtyInfo dirty = { + DIRTY_FLUSH, + 0, + 0, + 0xDEADBEEF, + }; + proxy->writeData(proxy, &dirty, sizeof(dirty)); +}
@@ -13,23 +13,6 @@ #include <mgba-util/memory.h>
#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); static void GBAVideoThreadProxyRendererDeinit(struct GBAVideoRenderer* renderer);@@ -44,6 +27,9 @@ static void GBAVideoThreadProxyRendererPutPixels(struct GBAVideoRenderer* renderer, size_t stride, const void* pixels);
static THREAD_ENTRY _proxyThread(void* renderer); +static bool _writeData(struct mVideoProxy* proxy, void* data, size_t length); +static uint16_t* _vramBlock(struct mVideoProxy* proxy, uint32_t address); + void GBAVideoThreadProxyRendererCreate(struct GBAVideoThreadProxyRenderer* renderer, struct GBAVideoRenderer* backend) { renderer->d.init = GBAVideoThreadProxyRendererInit; renderer->d.reset = GBAVideoThreadProxyRendererReset;@@ -63,6 +49,13 @@ renderer->d.disableBG[2] = false;
renderer->d.disableBG[3] = false; renderer->d.disableOBJ = false; + renderer->proxy.context = renderer; + renderer->proxy.writeData = _writeData; + renderer->proxy.vramBlock = _vramBlock; + renderer->proxy.paletteSize = SIZE_PALETTE_RAM; + renderer->proxy.vramSize = SIZE_VRAM; + renderer->proxy.oamSize = SIZE_OAM; + renderer->backend = backend; }@@ -73,15 +66,15 @@ ConditionInit(&proxyRenderer->toThreadCond);
MutexInit(&proxyRenderer->mutex); RingFIFOInit(&proxyRenderer->dirtyQueue, 0x40000); - proxyRenderer->vramProxy = anonymousMemoryMap(SIZE_VRAM); - proxyRenderer->backend->palette = proxyRenderer->paletteProxy; - proxyRenderer->backend->vram = proxyRenderer->vramProxy; - proxyRenderer->backend->oam = &proxyRenderer->oamProxy; + mVideoProxyRendererInit(&proxyRenderer->proxy); + + proxyRenderer->backend->palette = proxyRenderer->proxy.palette; + proxyRenderer->backend->vram = proxyRenderer->proxy.vram; + proxyRenderer->backend->oam = (union GBAOAM*) proxyRenderer->proxy.oam; proxyRenderer->backend->cache = NULL; proxyRenderer->backend->init(proxyRenderer->backend); - proxyRenderer->vramDirtyBitmap = 0; proxyRenderer->threadState = PROXY_THREAD_IDLE; ThreadCreate(&proxyRenderer->thread, _proxyThread, proxyRenderer); }@@ -93,9 +86,12 @@ while (proxyRenderer->threadState == PROXY_THREAD_BUSY) {
ConditionWake(&proxyRenderer->toThreadCond); ConditionWait(&proxyRenderer->fromThreadCond, &proxyRenderer->mutex); } - memcpy(&proxyRenderer->oamProxy.raw, &renderer->oam->raw, SIZE_OAM); - memcpy(proxyRenderer->paletteProxy, renderer->palette, SIZE_PALETTE_RAM); - memcpy(proxyRenderer->vramProxy, renderer->vram, SIZE_VRAM); + memcpy(proxyRenderer->proxy.oam, &renderer->oam->raw, SIZE_OAM); + memcpy(proxyRenderer->proxy.palette, renderer->palette, SIZE_PALETTE_RAM); + memcpy(proxyRenderer->proxy.vram, renderer->vram, SIZE_VRAM); + + mVideoProxyRendererReset(&proxyRenderer->proxy); + proxyRenderer->backend->reset(proxyRenderer->backend); MutexUnlock(&proxyRenderer->mutex); }@@ -122,7 +118,7 @@ ConditionDeinit(&proxyRenderer->toThreadCond);
MutexDeinit(&proxyRenderer->mutex); proxyRenderer->backend->deinit(proxyRenderer->backend); - mappedMemoryFree(proxyRenderer->vramProxy, SIZE_VRAM); + mVideoProxyRendererDeinit(&proxyRenderer->proxy); } void _proxyThreadRecover(struct GBAVideoThreadProxyRenderer* proxyRenderer) {@@ -138,7 +134,8 @@ proxyRenderer->threadState = PROXY_THREAD_IDLE;
ThreadCreate(&proxyRenderer->thread, _proxyThread, proxyRenderer); } -static bool _writeData(struct GBAVideoThreadProxyRenderer* proxyRenderer, void* data, size_t length) { +static bool _writeData(struct mVideoProxy* proxy, 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); MutexLock(&proxyRenderer->mutex);@@ -154,6 +151,11 @@ }
return true; } +static uint16_t* _vramBlock(struct mVideoProxy* proxy, uint32_t address) { + struct GBAVideoThreadProxyRenderer* proxyRenderer = proxy->context; + return &proxyRenderer->d.vram[address >> 1]; +} + uint16_t GBAVideoThreadProxyRendererWriteVideoRegister(struct GBAVideoRenderer* renderer, uint32_t address, uint16_t value) { struct GBAVideoThreadProxyRenderer* proxyRenderer = (struct GBAVideoThreadProxyRenderer*) renderer; switch (address) {@@ -178,23 +180,13 @@ if (address > REG_BLDY) {
return value; } - struct GBAVideoDirtyInfo dirty = { - DIRTY_REGISTER, - address, - value, - 0xDEADBEEF, - }; - _writeData(proxyRenderer, &dirty, sizeof(dirty)); + mVideoProxyRendererWriteVideoRegister(&proxyRenderer->proxy, address, value); return value; } void GBAVideoThreadProxyRendererWriteVRAM(struct GBAVideoRenderer* renderer, uint32_t address) { struct GBAVideoThreadProxyRenderer* proxyRenderer = (struct GBAVideoThreadProxyRenderer*) renderer; - int bit = 1 << (address >> 12); - if (proxyRenderer->vramDirtyBitmap & bit) { - return; - } - proxyRenderer->vramDirtyBitmap |= bit; + mVideoProxyRendererWriteVRAM(&proxyRenderer->proxy, address); if (renderer->cache) { mTileCacheWriteVRAM(renderer->cache, address); }@@ -202,13 +194,7 @@ }
void GBAVideoThreadProxyRendererWritePalette(struct GBAVideoRenderer* renderer, uint32_t address, uint16_t value) { struct GBAVideoThreadProxyRenderer* proxyRenderer = (struct GBAVideoThreadProxyRenderer*) renderer; - struct GBAVideoDirtyInfo dirty = { - DIRTY_PALETTE, - address, - value, - 0xDEADBEEF, - }; - _writeData(proxyRenderer, &dirty, sizeof(dirty)); + mVideoProxyRendererWritePalette(&proxyRenderer->proxy, address, value); if (renderer->cache) { mTileCacheWritePalette(renderer->cache, address); }@@ -216,42 +202,12 @@ }
void GBAVideoThreadProxyRendererWriteOAM(struct GBAVideoRenderer* renderer, uint32_t oam) { struct GBAVideoThreadProxyRenderer* proxyRenderer = (struct GBAVideoThreadProxyRenderer*) renderer; - struct GBAVideoDirtyInfo dirty = { - DIRTY_OAM, - oam, - proxyRenderer->d.oam->raw[oam], - 0xDEADBEEF, - }; - _writeData(proxyRenderer, &dirty, sizeof(dirty)); + mVideoProxyRendererWriteOAM(&proxyRenderer->proxy, oam, proxyRenderer->d.oam->raw[oam]); } 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, - }; - _writeData(proxyRenderer, &dirty, sizeof(dirty)); - _writeData(proxyRenderer, &proxyRenderer->d.vram[j * 0x800], 0x1000); - } - } - struct GBAVideoDirtyInfo dirty = { - DIRTY_SCANLINE, - y, - 0, - 0xDEADBEEF, - }; - _writeData(proxyRenderer, &dirty, sizeof(dirty)); + mVideoProxyRendererDrawScanline(&proxyRenderer->proxy, y); if ((y & 15) == 15) { ConditionWake(&proxyRenderer->toThreadCond); }@@ -266,19 +222,12 @@ return;
} MutexLock(&proxyRenderer->mutex); // Insert an extra item into the queue to make sure it gets flushed - struct GBAVideoDirtyInfo dirty = { - DIRTY_FLUSH, - 0, - 0, - 0xDEADBEEF, - }; - _writeData(proxyRenderer, &dirty, sizeof(dirty)); + mVideoProxyRendererFlush(&proxyRenderer->proxy); do { ConditionWake(&proxyRenderer->toThreadCond); ConditionWait(&proxyRenderer->fromThreadCond, &proxyRenderer->mutex); } while (proxyRenderer->threadState == PROXY_THREAD_BUSY); proxyRenderer->backend->finishFrame(proxyRenderer->backend); - proxyRenderer->vramDirtyBitmap = 0; MutexUnlock(&proxyRenderer->mutex); }@@ -286,13 +235,7 @@ static void GBAVideoThreadProxyRendererGetPixels(struct GBAVideoRenderer* renderer, size_t* stride, const void** pixels) {
struct GBAVideoThreadProxyRenderer* proxyRenderer = (struct GBAVideoThreadProxyRenderer*) renderer; MutexLock(&proxyRenderer->mutex); // Insert an extra item into the queue to make sure it gets flushed - struct GBAVideoDirtyInfo dirty = { - DIRTY_FLUSH, - 0, - 0, - 0xDEADBEEF, - }; - _writeData(proxyRenderer, &dirty, sizeof(dirty)); + mVideoProxyRendererFlush(&proxyRenderer->proxy); while (proxyRenderer->threadState == PROXY_THREAD_BUSY) { ConditionWake(&proxyRenderer->toThreadCond); ConditionWait(&proxyRenderer->fromThreadCond, &proxyRenderer->mutex);@@ -305,13 +248,7 @@ static void GBAVideoThreadProxyRendererPutPixels(struct GBAVideoRenderer* renderer, size_t stride, const void* pixels) {
struct GBAVideoThreadProxyRenderer* proxyRenderer = (struct GBAVideoThreadProxyRenderer*) renderer; MutexLock(&proxyRenderer->mutex); // Insert an extra item into the queue to make sure it gets flushed - struct GBAVideoDirtyInfo dirty = { - DIRTY_FLUSH, - 0, - 0, - 0xDEADBEEF, - }; - _writeData(proxyRenderer, &dirty, sizeof(dirty)); + mVideoProxyRendererFlush(&proxyRenderer->proxy); while (proxyRenderer->threadState == PROXY_THREAD_BUSY) { ConditionWake(&proxyRenderer->toThreadCond); ConditionWait(&proxyRenderer->fromThreadCond, &proxyRenderer->mutex);@@ -325,7 +262,7 @@ struct GBAVideoThreadProxyRenderer* proxyRenderer = renderer;
ThreadSetName("Proxy Renderer Thread"); MutexLock(&proxyRenderer->mutex); - struct GBAVideoDirtyInfo item = {0}; + struct mVideoProxyDirtyInfo item = {0}; while (proxyRenderer->threadState != PROXY_THREAD_STOPPED) { ConditionWait(&proxyRenderer->toThreadCond, &proxyRenderer->mutex); if (proxyRenderer->threadState == PROXY_THREAD_STOPPED) {@@ -340,15 +277,15 @@ case DIRTY_REGISTER:
proxyRenderer->backend->writeVideoRegister(proxyRenderer->backend, item.address, item.value); break; case DIRTY_PALETTE: - proxyRenderer->paletteProxy[item.address >> 1] = item.value; + proxyRenderer->proxy.palette[item.address >> 1] = item.value; proxyRenderer->backend->writePalette(proxyRenderer->backend, item.address, item.value); break; case DIRTY_OAM: - proxyRenderer->oamProxy.raw[item.address] = item.value; + proxyRenderer->proxy.oam[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)) { + 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);