mirror of https://github.com/mgba-emu/mgba.git
GBA Video: Threaded proxy renderer first draft
This commit is contained in:
parent
ddd81b27b4
commit
43ec351d49
|
@ -0,0 +1,287 @@
|
||||||
|
/* Copyright (c) 2013-2015 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 "thread-proxy.h"
|
||||||
|
|
||||||
|
#include "gba/io.h"
|
||||||
|
|
||||||
|
#include "util/memory.h"
|
||||||
|
|
||||||
|
DEFINE_VECTOR(GBAVideoDirtyQueue, struct GBAVideoDirtyInfo);
|
||||||
|
|
||||||
|
static void GBAVideoThreadProxyRendererInit(struct GBAVideoRenderer* renderer);
|
||||||
|
static void GBAVideoThreadProxyRendererReset(struct GBAVideoRenderer* renderer);
|
||||||
|
static void GBAVideoThreadProxyRendererDeinit(struct GBAVideoRenderer* renderer);
|
||||||
|
static uint16_t GBAVideoThreadProxyRendererWriteVideoRegister(struct GBAVideoRenderer* renderer, uint32_t address, uint16_t value);
|
||||||
|
static void GBAVideoThreadProxyRendererWriteVRAM(struct GBAVideoRenderer* renderer, uint32_t address);
|
||||||
|
static void GBAVideoThreadProxyRendererWritePalette(struct GBAVideoRenderer* renderer, uint32_t address, uint16_t value);
|
||||||
|
static void GBAVideoThreadProxyRendererWriteOAM(struct GBAVideoRenderer* renderer, uint32_t oam);
|
||||||
|
static void GBAVideoThreadProxyRendererDrawScanline(struct GBAVideoRenderer* renderer, int y);
|
||||||
|
static void GBAVideoThreadProxyRendererFinishFrame(struct GBAVideoRenderer* renderer);
|
||||||
|
static void GBAVideoThreadProxyRendererGetPixels(struct GBAVideoRenderer* renderer, unsigned* stride, void** pixels);
|
||||||
|
static void GBAVideoThreadProxyRendererPutPixels(struct GBAVideoRenderer* renderer, unsigned stride, void* pixels);
|
||||||
|
|
||||||
|
static THREAD_ENTRY _proxyThread(void* renderer);
|
||||||
|
|
||||||
|
void GBAVideoThreadProxyRendererCreate(struct GBAVideoThreadProxyRenderer* renderer, struct GBAVideoRenderer* backend) {
|
||||||
|
renderer->d.init = GBAVideoThreadProxyRendererInit;
|
||||||
|
renderer->d.reset = GBAVideoThreadProxyRendererReset;
|
||||||
|
renderer->d.deinit = GBAVideoThreadProxyRendererDeinit;
|
||||||
|
renderer->d.writeVideoRegister = GBAVideoThreadProxyRendererWriteVideoRegister;
|
||||||
|
renderer->d.writeVRAM = GBAVideoThreadProxyRendererWriteVRAM;
|
||||||
|
renderer->d.writeOAM = GBAVideoThreadProxyRendererWriteOAM;
|
||||||
|
renderer->d.writePalette = GBAVideoThreadProxyRendererWritePalette;
|
||||||
|
renderer->d.drawScanline = GBAVideoThreadProxyRendererDrawScanline;
|
||||||
|
renderer->d.finishFrame = GBAVideoThreadProxyRendererFinishFrame;
|
||||||
|
renderer->d.getPixels = GBAVideoThreadProxyRendererGetPixels;
|
||||||
|
renderer->d.putPixels = GBAVideoThreadProxyRendererPutPixels;
|
||||||
|
|
||||||
|
renderer->d.disableBG[0] = false;
|
||||||
|
renderer->d.disableBG[1] = false;
|
||||||
|
renderer->d.disableBG[2] = false;
|
||||||
|
renderer->d.disableBG[3] = false;
|
||||||
|
renderer->d.disableOBJ = false;
|
||||||
|
|
||||||
|
renderer->backend = backend;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GBAVideoThreadProxyRendererInit(struct GBAVideoRenderer* renderer) {
|
||||||
|
struct GBAVideoThreadProxyRenderer* proxyRenderer = (struct GBAVideoThreadProxyRenderer*) renderer;
|
||||||
|
ConditionInit(&proxyRenderer->fromThreadCond);
|
||||||
|
ConditionInit(&proxyRenderer->toThreadCond);
|
||||||
|
MutexInit(&proxyRenderer->mutex);
|
||||||
|
GBAVideoDirtyQueueInit(&proxyRenderer->dirtyQueue, 1024);
|
||||||
|
proxyRenderer->threadState = PROXY_THREAD_STOPPED;
|
||||||
|
|
||||||
|
proxyRenderer->vramProxy = anonymousMemoryMap(SIZE_VRAM);
|
||||||
|
proxyRenderer->backend->palette = proxyRenderer->paletteProxy;
|
||||||
|
proxyRenderer->backend->vram = proxyRenderer->vramProxy;
|
||||||
|
proxyRenderer->backend->oam = &proxyRenderer->oamProxy;
|
||||||
|
|
||||||
|
proxyRenderer->backend->init(proxyRenderer->backend);
|
||||||
|
|
||||||
|
proxyRenderer->vramDirtyBitmap = 0;
|
||||||
|
memset(proxyRenderer->oamDirtyBitmap, 0, sizeof(proxyRenderer->oamDirtyBitmap));
|
||||||
|
memset(proxyRenderer->paletteDirtyBitmap, 0, sizeof(proxyRenderer->paletteDirtyBitmap));
|
||||||
|
memset(proxyRenderer->regDirtyBitmap, 0, sizeof(proxyRenderer->regDirtyBitmap));
|
||||||
|
proxyRenderer->threadState = PROXY_THREAD_IDLE;
|
||||||
|
ThreadCreate(&proxyRenderer->thread, _proxyThread, proxyRenderer);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GBAVideoThreadProxyRendererReset(struct GBAVideoRenderer* renderer) {
|
||||||
|
struct GBAVideoThreadProxyRenderer* proxyRenderer = (struct GBAVideoThreadProxyRenderer*) renderer;
|
||||||
|
MutexLock(&proxyRenderer->mutex);
|
||||||
|
while (proxyRenderer->threadState == PROXY_THREAD_BUSY) {
|
||||||
|
ConditionWait(&proxyRenderer->fromThreadCond, &proxyRenderer->mutex);
|
||||||
|
}
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < 128; ++i) {
|
||||||
|
proxyRenderer->oamProxy.raw[i * 4] = 0x0200;
|
||||||
|
proxyRenderer->oamProxy.raw[i * 4 + 1] = 0x0000;
|
||||||
|
proxyRenderer->oamProxy.raw[i * 4 + 2] = 0x0000;
|
||||||
|
proxyRenderer->oamProxy.raw[i * 4 + 3] = 0x0000;
|
||||||
|
}
|
||||||
|
proxyRenderer->backend->reset(proxyRenderer->backend);
|
||||||
|
MutexUnlock(&proxyRenderer->mutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GBAVideoThreadProxyRendererDeinit(struct GBAVideoRenderer* renderer) {
|
||||||
|
struct GBAVideoThreadProxyRenderer* proxyRenderer = (struct GBAVideoThreadProxyRenderer*) renderer;
|
||||||
|
bool waiting = false;
|
||||||
|
MutexLock(&proxyRenderer->mutex);
|
||||||
|
while (proxyRenderer->threadState == PROXY_THREAD_BUSY) {
|
||||||
|
ConditionWake(&proxyRenderer->toThreadCond);
|
||||||
|
ConditionWait(&proxyRenderer->fromThreadCond, &proxyRenderer->mutex);
|
||||||
|
}
|
||||||
|
if (proxyRenderer->threadState == PROXY_THREAD_IDLE) {
|
||||||
|
proxyRenderer->threadState = PROXY_THREAD_STOPPED;
|
||||||
|
ConditionWake(&proxyRenderer->toThreadCond);
|
||||||
|
waiting = true;
|
||||||
|
}
|
||||||
|
MutexUnlock(&proxyRenderer->mutex);
|
||||||
|
if (waiting) {
|
||||||
|
ThreadJoin(proxyRenderer->thread);
|
||||||
|
}
|
||||||
|
ConditionDeinit(&proxyRenderer->fromThreadCond);
|
||||||
|
ConditionDeinit(&proxyRenderer->toThreadCond);
|
||||||
|
MutexDeinit(&proxyRenderer->mutex);
|
||||||
|
proxyRenderer->backend->deinit(proxyRenderer->backend);
|
||||||
|
|
||||||
|
mappedMemoryFree(proxyRenderer->vramProxy, SIZE_VRAM);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t GBAVideoThreadProxyRendererWriteVideoRegister(struct GBAVideoRenderer* renderer, uint32_t address, uint16_t value) {
|
||||||
|
struct GBAVideoThreadProxyRenderer* proxyRenderer = (struct GBAVideoThreadProxyRenderer*) renderer;
|
||||||
|
switch (address) {
|
||||||
|
case REG_BG0CNT:
|
||||||
|
case REG_BG1CNT:
|
||||||
|
case REG_BG2CNT:
|
||||||
|
case REG_BG3CNT:
|
||||||
|
value &= 0xFFCF;
|
||||||
|
break;
|
||||||
|
case REG_BG0HOFS:
|
||||||
|
case REG_BG0VOFS:
|
||||||
|
case REG_BG1HOFS:
|
||||||
|
case REG_BG1VOFS:
|
||||||
|
case REG_BG2HOFS:
|
||||||
|
case REG_BG2VOFS:
|
||||||
|
case REG_BG3HOFS:
|
||||||
|
case REG_BG3VOFS:
|
||||||
|
value &= 0x01FF;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (address > REG_BLDY) {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
proxyRenderer->regProxy[address >> 1] = value;
|
||||||
|
int bit = 1 << ((address >> 1) & 31);
|
||||||
|
int base = address >> 6;
|
||||||
|
if (proxyRenderer->regDirtyBitmap[base] & bit) {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
proxyRenderer->regDirtyBitmap[base] |= bit;
|
||||||
|
|
||||||
|
struct GBAVideoDirtyInfo dirty = {
|
||||||
|
DIRTY_REGISTER,
|
||||||
|
address
|
||||||
|
};
|
||||||
|
*GBAVideoDirtyQueueAppend(&proxyRenderer->dirtyQueue) = dirty;
|
||||||
|
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;
|
||||||
|
struct GBAVideoDirtyInfo dirty = {
|
||||||
|
DIRTY_VRAM,
|
||||||
|
address
|
||||||
|
};
|
||||||
|
*GBAVideoDirtyQueueAppend(&proxyRenderer->dirtyQueue) = dirty;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GBAVideoThreadProxyRendererWritePalette(struct GBAVideoRenderer* renderer, uint32_t address, uint16_t value) {
|
||||||
|
struct GBAVideoThreadProxyRenderer* proxyRenderer = (struct GBAVideoThreadProxyRenderer*) renderer;
|
||||||
|
proxyRenderer->paletteProxy[address >> 1] = value;
|
||||||
|
int bit = 1 << ((address >> 1) & 31);
|
||||||
|
int base = address >> 6;
|
||||||
|
if (proxyRenderer->paletteDirtyBitmap[base] & bit) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
proxyRenderer->paletteDirtyBitmap[base] |= bit;
|
||||||
|
struct GBAVideoDirtyInfo dirty = {
|
||||||
|
DIRTY_PALETTE,
|
||||||
|
address
|
||||||
|
};
|
||||||
|
*GBAVideoDirtyQueueAppend(&proxyRenderer->dirtyQueue) = dirty;
|
||||||
|
}
|
||||||
|
|
||||||
|
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) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
proxyRenderer->oamDirtyBitmap[base] |= bit;
|
||||||
|
struct GBAVideoDirtyInfo dirty = {
|
||||||
|
DIRTY_OAM,
|
||||||
|
oam
|
||||||
|
};
|
||||||
|
*GBAVideoDirtyQueueAppend(&proxyRenderer->dirtyQueue) = dirty;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GBAVideoThreadProxyRendererDrawScanline(struct GBAVideoRenderer* renderer, int y) {
|
||||||
|
struct GBAVideoThreadProxyRenderer* proxyRenderer = (struct GBAVideoThreadProxyRenderer*) renderer;
|
||||||
|
MutexLock(&proxyRenderer->mutex);
|
||||||
|
while (proxyRenderer->threadState == PROXY_THREAD_BUSY) {
|
||||||
|
ConditionWake(&proxyRenderer->toThreadCond);
|
||||||
|
ConditionWait(&proxyRenderer->fromThreadCond, &proxyRenderer->mutex);
|
||||||
|
}
|
||||||
|
proxyRenderer->y = y;
|
||||||
|
ConditionWake(&proxyRenderer->toThreadCond);
|
||||||
|
while (proxyRenderer->threadState == PROXY_THREAD_IDLE) {
|
||||||
|
ConditionWake(&proxyRenderer->toThreadCond);
|
||||||
|
ConditionWait(&proxyRenderer->fromThreadCond, &proxyRenderer->mutex);
|
||||||
|
}
|
||||||
|
MutexUnlock(&proxyRenderer->mutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GBAVideoThreadProxyRendererFinishFrame(struct GBAVideoRenderer* renderer) {
|
||||||
|
struct GBAVideoThreadProxyRenderer* proxyRenderer = (struct GBAVideoThreadProxyRenderer*) renderer;
|
||||||
|
MutexLock(&proxyRenderer->mutex);
|
||||||
|
while (proxyRenderer->threadState == PROXY_THREAD_BUSY) {
|
||||||
|
ConditionWake(&proxyRenderer->toThreadCond);
|
||||||
|
ConditionWait(&proxyRenderer->fromThreadCond, &proxyRenderer->mutex);
|
||||||
|
}
|
||||||
|
proxyRenderer->backend->finishFrame(proxyRenderer->backend);
|
||||||
|
MutexUnlock(&proxyRenderer->mutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GBAVideoThreadProxyRendererGetPixels(struct GBAVideoRenderer* renderer, unsigned* stride, void** pixels) {
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
void GBAVideoThreadProxyRendererPutPixels(struct GBAVideoRenderer* renderer, unsigned stride, void* pixels) {
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static THREAD_ENTRY _proxyThread(void* renderer) {
|
||||||
|
struct GBAVideoThreadProxyRenderer* proxyRenderer = renderer;
|
||||||
|
ThreadSetName("Proxy Renderer Thread");
|
||||||
|
|
||||||
|
MutexLock(&proxyRenderer->mutex);
|
||||||
|
while (1) {
|
||||||
|
ConditionWait(&proxyRenderer->toThreadCond, &proxyRenderer->mutex);
|
||||||
|
if (proxyRenderer->threadState == PROXY_THREAD_STOPPED) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
proxyRenderer->threadState = PROXY_THREAD_BUSY;
|
||||||
|
proxyRenderer->vramDirtyBitmap = 0;
|
||||||
|
memset(proxyRenderer->oamDirtyBitmap, 0, sizeof(proxyRenderer->oamDirtyBitmap));
|
||||||
|
memset(proxyRenderer->paletteDirtyBitmap, 0, sizeof(proxyRenderer->paletteDirtyBitmap));
|
||||||
|
memset(proxyRenderer->regDirtyBitmap, 0, sizeof(proxyRenderer->regDirtyBitmap));
|
||||||
|
size_t queueSize = GBAVideoDirtyQueueSize(&proxyRenderer->dirtyQueue);
|
||||||
|
struct GBAVideoDirtyInfo* queue = malloc(queueSize * sizeof(struct GBAVideoDirtyInfo));
|
||||||
|
memcpy(queue, GBAVideoDirtyQueueGetPointer(&proxyRenderer->dirtyQueue, 0), queueSize * sizeof(struct GBAVideoDirtyInfo));
|
||||||
|
GBAVideoDirtyQueueClear(&proxyRenderer->dirtyQueue);
|
||||||
|
size_t i;
|
||||||
|
for (i = 0; i < queueSize; ++i) {
|
||||||
|
switch (queue[i].type) {
|
||||||
|
case DIRTY_REGISTER:
|
||||||
|
proxyRenderer->backend->writeVideoRegister(proxyRenderer->backend, queue[i].address, proxyRenderer->regProxy[queue[i].address >> 1]);
|
||||||
|
break;
|
||||||
|
case DIRTY_VRAM:
|
||||||
|
proxyRenderer->backend->writeVRAM(proxyRenderer->backend, queue[i].address);
|
||||||
|
memcpy(&proxyRenderer->vramProxy[(queue[i].address & ~0xFFF) >> 1], &proxyRenderer->d.vram[(queue[i].address & ~0xFFF) >> 1], 0x1000);
|
||||||
|
break;
|
||||||
|
case DIRTY_PALETTE:
|
||||||
|
proxyRenderer->backend->writePalette(proxyRenderer->backend, queue[i].address, proxyRenderer->paletteProxy[queue[i].address >> 1]);
|
||||||
|
break;
|
||||||
|
case DIRTY_OAM:
|
||||||
|
proxyRenderer->backend->writeOAM(proxyRenderer->backend, queue[i].address);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
free(queue);
|
||||||
|
ConditionWake(&proxyRenderer->fromThreadCond);
|
||||||
|
ConditionWait(&proxyRenderer->toThreadCond, &proxyRenderer->mutex);
|
||||||
|
MutexUnlock(&proxyRenderer->mutex);
|
||||||
|
|
||||||
|
proxyRenderer->backend->drawScanline(proxyRenderer->backend, proxyRenderer->y);
|
||||||
|
|
||||||
|
MutexLock(&proxyRenderer->mutex);
|
||||||
|
proxyRenderer->threadState = PROXY_THREAD_IDLE;
|
||||||
|
ConditionWake(&proxyRenderer->fromThreadCond);
|
||||||
|
}
|
||||||
|
MutexUnlock(&proxyRenderer->mutex);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
|
@ -0,0 +1,59 @@
|
||||||
|
/* Copyright (c) 2013-2015 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_THREAD_PROXY_H
|
||||||
|
#define VIDEO_THREAD_PROXY_H
|
||||||
|
|
||||||
|
#include "gba/video.h"
|
||||||
|
#include "util/threading.h"
|
||||||
|
#include "util/vector.h"
|
||||||
|
|
||||||
|
enum GBAVideoDirtyType {
|
||||||
|
DIRTY_REGISTER,
|
||||||
|
DIRTY_VRAM,
|
||||||
|
DIRTY_OAM,
|
||||||
|
DIRTY_PALETTE
|
||||||
|
};
|
||||||
|
|
||||||
|
enum GBAVideoThreadProxyState {
|
||||||
|
PROXY_THREAD_STOPPED = 0,
|
||||||
|
PROXY_THREAD_IDLE,
|
||||||
|
PROXY_THREAD_BUSY
|
||||||
|
};
|
||||||
|
|
||||||
|
struct GBAVideoDirtyInfo {
|
||||||
|
enum GBAVideoDirtyType type;
|
||||||
|
uint32_t address;
|
||||||
|
};
|
||||||
|
|
||||||
|
DECLARE_VECTOR(GBAVideoDirtyQueue, struct GBAVideoDirtyInfo);
|
||||||
|
|
||||||
|
struct GBAVideoThreadProxyRenderer {
|
||||||
|
struct GBAVideoRenderer d;
|
||||||
|
struct GBAVideoRenderer* backend;
|
||||||
|
|
||||||
|
Thread thread;
|
||||||
|
Condition fromThreadCond;
|
||||||
|
Condition toThreadCond;
|
||||||
|
Mutex mutex;
|
||||||
|
enum GBAVideoThreadProxyState threadState;
|
||||||
|
|
||||||
|
struct GBAVideoDirtyQueue dirtyQueue;
|
||||||
|
uint32_t vramDirtyBitmap;
|
||||||
|
uint32_t oamDirtyBitmap[16];
|
||||||
|
uint32_t paletteDirtyBitmap[16];
|
||||||
|
uint32_t regDirtyBitmap[2];
|
||||||
|
|
||||||
|
uint16_t* vramProxy;
|
||||||
|
union GBAOAM oamProxy;
|
||||||
|
uint16_t paletteProxy[512];
|
||||||
|
uint16_t regProxy[42];
|
||||||
|
|
||||||
|
int y;
|
||||||
|
};
|
||||||
|
|
||||||
|
void GBAVideoThreadProxyRendererCreate(struct GBAVideoThreadProxyRenderer* renderer, struct GBAVideoRenderer* backend);
|
||||||
|
|
||||||
|
#endif
|
Loading…
Reference in New Issue