GB Video: Optimize renderer

This commit is contained in:
Jeffrey Pfau 2016-02-13 23:57:02 -08:00
parent e6e535e39a
commit 94ff4f7c4e
4 changed files with 109 additions and 73 deletions

View File

@ -275,6 +275,7 @@ void GBIOWrite(struct GB* gb, unsigned address, uint8_t value) {
case REG_BGP:
case REG_OBP0:
case REG_OBP1:
GBVideoProcessDots(&gb->video);
value = gb->video.renderer->writeVideoRegister(gb->video.renderer, address, value);
break;
case REG_STAT:

View File

@ -12,13 +12,14 @@ static void GBVideoSoftwareRendererInit(struct GBVideoRenderer* renderer);
static void GBVideoSoftwareRendererDeinit(struct GBVideoRenderer* renderer);
static void GBVideoSoftwareRendererReset(struct GBVideoRenderer* renderer);
static uint8_t GBVideoSoftwareRendererWriteVideoRegister(struct GBVideoRenderer* renderer, uint16_t address, uint8_t value);
static void GBVideoSoftwareRendererDrawDot(struct GBVideoRenderer* renderer, int x, int y, struct GBObj** obj, size_t oamMax);
static void GBVideoSoftwareRendererDrawRange(struct GBVideoRenderer* renderer, int startX, int endX, int y, struct GBObj** obj, size_t oamMax);
static void GBVideoSoftwareRendererFinishScanline(struct GBVideoRenderer* renderer, int y);
static void GBVideoSoftwareRendererFinishFrame(struct GBVideoRenderer* renderer);
static void GBVideoSoftwareRendererGetPixels(struct GBVideoRenderer* renderer, unsigned* stride, const void** pixels);
static void GBVideoSoftwareRendererPutPixels(struct GBVideoRenderer* renderer, unsigned stride, void* pixels);
static void GBVideoSoftwareRendererDrawBackground(struct GBVideoSoftwareRenderer* renderer, uint8_t* maps, int x, int y, int sx, int sy);
static void GBVideoSoftwareRendererDrawObj(struct GBVideoSoftwareRenderer* renderer, struct GBObj* obj, int x, int y);
static void GBVideoSoftwareRendererDrawBackground(struct GBVideoSoftwareRenderer* renderer, uint8_t* maps, int startX, int endX, int y, int sx, int sy);
static void GBVideoSoftwareRendererDrawObj(struct GBVideoSoftwareRenderer* renderer, struct GBObj* obj, int startX, int endX, int y);
#ifdef COLOR_16_BIT
#ifdef COLOR_5_6_5
@ -35,7 +36,8 @@ void GBVideoSoftwareRendererCreate(struct GBVideoSoftwareRenderer* renderer) {
renderer->d.reset = GBVideoSoftwareRendererReset;
renderer->d.deinit = GBVideoSoftwareRendererDeinit;
renderer->d.writeVideoRegister = GBVideoSoftwareRendererWriteVideoRegister;
renderer->d.drawDot = GBVideoSoftwareRendererDrawDot;
renderer->d.drawRange = GBVideoSoftwareRendererDrawRange;
renderer->d.finishScanline = GBVideoSoftwareRendererFinishScanline;
renderer->d.finishFrame = GBVideoSoftwareRendererFinishFrame;
renderer->d.getPixels = 0;
renderer->d.putPixels = 0;
@ -112,38 +114,48 @@ static uint8_t GBVideoSoftwareRendererWriteVideoRegister(struct GBVideoRenderer*
return value;
}
static void GBVideoSoftwareRendererDrawDot(struct GBVideoRenderer* renderer, int x, int y, struct GBObj** obj, size_t oamMax) {
static void GBVideoSoftwareRendererDrawRange(struct GBVideoRenderer* renderer, int startX, int endX, int y, struct GBObj** obj, size_t oamMax) {
struct GBVideoSoftwareRenderer* softwareRenderer = (struct GBVideoSoftwareRenderer*) renderer;
uint8_t* maps = &softwareRenderer->d.vram[GB_BASE_MAP];
if (GBRegisterLCDCIsTileMap(softwareRenderer->lcdc)) {
maps += GB_SIZE_MAP;
}
if (GBRegisterLCDCIsBgEnable(softwareRenderer->lcdc)) {
GBVideoSoftwareRendererDrawBackground(softwareRenderer, maps, x, y, softwareRenderer->scx, softwareRenderer->scy);
GBVideoSoftwareRendererDrawBackground(softwareRenderer, maps, startX, endX, y, softwareRenderer->scx, softwareRenderer->scy);
if (GBRegisterLCDCIsWindow(softwareRenderer->lcdc) && softwareRenderer->wy <= y && x >= softwareRenderer->wx - 7) {
if (GBRegisterLCDCIsWindow(softwareRenderer->lcdc) && softwareRenderer->wy <= y && startX >= softwareRenderer->wx - 7) {
maps = &softwareRenderer->d.vram[GB_BASE_MAP];
if (GBRegisterLCDCIsWindowTileMap(softwareRenderer->lcdc)) {
maps += GB_SIZE_MAP;
}
GBVideoSoftwareRendererDrawBackground(softwareRenderer, maps, x, y, 7 - softwareRenderer->wx, (softwareRenderer->currentWy - y) - softwareRenderer->wy);
if (x == 159) { // TODO: Find a better way to do this
++softwareRenderer->currentWy;
}
GBVideoSoftwareRendererDrawBackground(softwareRenderer, maps, startX, endX, y, 7 - softwareRenderer->wx, (softwareRenderer->currentWy - y) - softwareRenderer->wy);
}
} else {
softwareRenderer->row[x] = 0;
int x;
for (x = startX; x < endX; ++x) {
softwareRenderer->row[x] = 0;
}
}
if (GBRegisterLCDCIsObjEnable(softwareRenderer->lcdc)) {
size_t i;
for (i = 0; i < oamMax; ++i) {
GBVideoSoftwareRendererDrawObj(softwareRenderer, obj[i], x, y);
GBVideoSoftwareRendererDrawObj(softwareRenderer, obj[i], startX, endX, y);
}
}
color_t* row = &softwareRenderer->outputBuffer[softwareRenderer->outputBufferStride * y];
row[x] = softwareRenderer->palette[softwareRenderer->row[x]];
int x;
for (x = startX; x < endX; ++x) {
row[x] = softwareRenderer->palette[softwareRenderer->row[x]];
}
}
static void GBVideoSoftwareRendererFinishScanline(struct GBVideoRenderer* renderer, int y) {
struct GBVideoSoftwareRenderer* softwareRenderer = (struct GBVideoSoftwareRenderer*) renderer;
if (GBRegisterLCDCIsBgEnable(softwareRenderer->lcdc) && GBRegisterLCDCIsWindow(softwareRenderer->lcdc) && softwareRenderer->wy <= y && softwareRenderer->wx - 7 < GB_VIDEO_HORIZONTAL_PIXELS) {
++softwareRenderer->currentWy;
}
}
static void GBVideoSoftwareRendererFinishFrame(struct GBVideoRenderer* renderer) {
@ -156,29 +168,42 @@ static void GBVideoSoftwareRendererFinishFrame(struct GBVideoRenderer* renderer)
softwareRenderer->currentWy = 0;
}
static void GBVideoSoftwareRendererDrawBackground(struct GBVideoSoftwareRenderer* renderer, uint8_t* maps, int x, int y, int sx, int sy) {
static void GBVideoSoftwareRendererDrawBackground(struct GBVideoSoftwareRenderer* renderer, uint8_t* maps, int startX, int endX, int y, int sx, int sy) {
uint8_t* data = renderer->d.vram;
if (!GBRegisterLCDCIsTileData(renderer->lcdc)) {
data += 0x1000;
}
int topY = (((y + sy) >> 3) & 0x1F) * 0x20;
int bottomY = (y + sy) & 7;
int topX = ((x + sx) >> 3) & 0x1F;
int bottomX = 7 - ((x + sx) & 7);
int bgTile;
if (GBRegisterLCDCIsTileData(renderer->lcdc)) {
bgTile = maps[topX + topY];
} else {
bgTile = ((int8_t*) maps)[topX + topY];
int x;
for (x = startX; x < endX; ++x) {
int topX = ((x + sx) >> 3) & 0x1F;
int bottomX = 7 - ((x + sx) & 7);
int bgTile;
if (GBRegisterLCDCIsTileData(renderer->lcdc)) {
bgTile = maps[topX + topY];
} else {
bgTile = ((int8_t*) maps)[topX + topY];
}
uint8_t tileDataLower = data[(bgTile * 8 + bottomY) * 2];
uint8_t tileDataUpper = data[(bgTile * 8 + bottomY) * 2 + 1];
tileDataUpper >>= bottomX;
tileDataLower >>= bottomX;
renderer->row[x] = ((tileDataUpper & 1) << 1) | (tileDataLower & 1);
}
uint8_t tileDataLower = data[(bgTile * 8 + bottomY) * 2];
uint8_t tileDataUpper = data[(bgTile * 8 + bottomY) * 2 + 1];
tileDataUpper >>= bottomX;
tileDataLower >>= bottomX;
renderer->row[x] = ((tileDataUpper & 1) << 1) | (tileDataLower & 1);
}
static void GBVideoSoftwareRendererDrawObj(struct GBVideoSoftwareRenderer* renderer, struct GBObj* obj, int x, int y) {
static void GBVideoSoftwareRendererDrawObj(struct GBVideoSoftwareRenderer* renderer, struct GBObj* obj, int startX, int endX, int y) {
int ix = obj->x - 8;
if (endX < ix || startX >= ix + 8) {
return;
}
if (obj->x < endX) {
endX = obj->x;
}
if (obj->x - 8 > startX) {
startX = obj->x - 8;
}
uint8_t* data = renderer->d.vram;
int tileOffset = 0;
int bottomY;
@ -193,29 +218,24 @@ static void GBVideoSoftwareRendererDrawObj(struct GBVideoSoftwareRenderer* rende
++tileOffset;
}
}
int end = GB_VIDEO_HORIZONTAL_PIXELS;
if (obj->x < end) {
end = obj->x;
}
int ix = obj->x - 8;
if (x < ix || x >= ix + 8) {
return;
}
uint8_t mask = GBObjAttributesIsPriority(obj->attr) ? 0 : 0x20;
int p = (GBObjAttributesGetPalette(obj->attr) + 8) * 4;
int bottomX;
if (GBObjAttributesIsXFlip(obj->attr)) {
bottomX = (x - obj->x) & 7;
} else {
bottomX = 7 - ((x - obj->x) & 7);
}
int objTile = obj->tile + tileOffset;
uint8_t tileDataLower = data[(objTile * 8 + bottomY) * 2];
uint8_t tileDataUpper = data[(objTile * 8 + bottomY) * 2 + 1];
tileDataUpper >>= bottomX;
tileDataLower >>= bottomX;
color_t current = renderer->row[x];
if (((tileDataUpper | tileDataLower) & 1) && current <= mask) {
renderer->row[x] = p | ((tileDataUpper & 1) << 1) | (tileDataLower & 1);
int x;
for (x = startX; x < endX; ++x) {
if (GBObjAttributesIsXFlip(obj->attr)) {
bottomX = (x - obj->x) & 7;
} else {
bottomX = 7 - ((x - obj->x) & 7);
}
int objTile = obj->tile + tileOffset;
uint8_t tileDataLower = data[(objTile * 8 + bottomY) * 2];
uint8_t tileDataUpper = data[(objTile * 8 + bottomY) * 2 + 1];
tileDataUpper >>= bottomX;
tileDataLower >>= bottomX;
color_t current = renderer->row[x];
if (((tileDataUpper | tileDataLower) & 1) && current <= mask) {
renderer->row[x] = p | ((tileDataUpper & 1) << 1) | (tileDataLower & 1);
}
}
}

View File

@ -16,7 +16,8 @@ static void GBVideoDummyRendererInit(struct GBVideoRenderer* renderer);
static void GBVideoDummyRendererReset(struct GBVideoRenderer* renderer);
static void GBVideoDummyRendererDeinit(struct GBVideoRenderer* renderer);
static uint8_t GBVideoDummyRendererWriteVideoRegister(struct GBVideoRenderer* renderer, uint16_t address, uint8_t value);
static void GBVideoDummyRendererDrawDot(struct GBVideoRenderer* renderer, int x, int y, struct GBObj** obj, size_t oamMax);
static void GBVideoDummyRendererDrawRange(struct GBVideoRenderer* renderer, int startX, int endX, int y, struct GBObj** obj, size_t oamMax);
static void GBVideoDummyRendererFinishScanline(struct GBVideoRenderer* renderer, int y);
static void GBVideoDummyRendererFinishFrame(struct GBVideoRenderer* renderer);
static void GBVideoDummyRendererGetPixels(struct GBVideoRenderer* renderer, unsigned* stride, const void** pixels);
@ -27,7 +28,8 @@ static struct GBVideoRenderer dummyRenderer = {
.reset = GBVideoDummyRendererReset,
.deinit = GBVideoDummyRendererDeinit,
.writeVideoRegister = GBVideoDummyRendererWriteVideoRegister,
.drawDot = GBVideoDummyRendererDrawDot,
.drawRange = GBVideoDummyRendererDrawRange,
.finishScanline = GBVideoDummyRendererFinishScanline,
.finishFrame = GBVideoDummyRendererFinishFrame,
.getPixels = GBVideoDummyRendererGetPixels
};
@ -47,7 +49,7 @@ void GBVideoReset(struct GBVideo* video) {
video->eventDiff = 0;
video->nextMode = INT_MAX;
video->nextDot = INT_MAX;
video->dotCounter = INT_MIN;
video->frameCounter = 0;
video->frameskipCounter = 0;
@ -84,22 +86,14 @@ int32_t GBVideoProcessEvents(struct GBVideo* video, int32_t cycles) {
if (video->nextEvent <= 0) {
if (video->nextEvent != INT_MAX) {
video->nextMode -= video->eventDiff;
video->nextDot -= video->eventDiff;
}
video->nextEvent = INT_MAX;
if (video->nextDot <= 0) {
video->renderer->drawDot(video->renderer, video->x, video->ly, video->objThisLine, video->objMax);
++video->x;
if (video->x < GB_VIDEO_HORIZONTAL_PIXELS) {
video->nextDot = 1;
} else {
video->nextDot = INT_MAX;
}
}
GBVideoProcessDots(video);
if (video->nextMode <= 0) {
int lyc = video->p->memory.io[REG_LYC];
switch (video->mode) {
case 0:
video->renderer->finishScanline(video->renderer, video->ly);
++video->ly;
video->p->memory.io[REG_LY] = video->ly;
video->stat = GBRegisterSTATSetLYC(video->stat, lyc == video->ly);
@ -157,8 +151,8 @@ int32_t GBVideoProcessEvents(struct GBVideo* video, int32_t cycles) {
break;
case 2:
_cleanOAM(video, video->ly);
video->nextDot = 1;
video->nextEvent = video->nextDot;
video->dotCounter = 0;
video->nextEvent = GB_VIDEO_HORIZONTAL_LENGTH;
video->x = 0;
video->nextMode = GB_VIDEO_MODE_3_LENGTH_BASE + video->objMax * 8;
video->mode = 3;
@ -175,9 +169,6 @@ int32_t GBVideoProcessEvents(struct GBVideo* video, int32_t cycles) {
video->stat = GBRegisterSTATSetMode(video->stat, video->mode);
video->p->memory.io[REG_STAT] = video->stat;
}
if (video->nextDot < video->nextEvent) {
video->nextEvent = video->nextDot;
}
if (video->nextMode < video->nextEvent) {
video->nextEvent = video->nextMode;
}
@ -211,6 +202,21 @@ static void _cleanOAM(struct GBVideo* video, int y) {
video->objMax = o;
}
void GBVideoProcessDots(struct GBVideo* video) {
if (video->mode != 3 || video->dotCounter < 0) {
return;
}
int oldX = video->x;
video->x = video->dotCounter + video->eventDiff + video->p->cpu->cycles;
if (video->x > GB_VIDEO_HORIZONTAL_PIXELS) {
video->x = GB_VIDEO_HORIZONTAL_PIXELS;
}
if (video->x == GB_VIDEO_HORIZONTAL_PIXELS) {
video->dotCounter = INT_MIN;
}
video->renderer->drawRange(video->renderer, oldX, video->x, video->ly, video->objThisLine, video->objMax);
}
void GBVideoWriteLCDC(struct GBVideo* video, GBRegisterLCDC value) {
if (!GBRegisterLCDCIsEnable(video->p->memory.io[REG_LCDC]) && GBRegisterLCDCIsEnable(value)) {
video->mode = 2;
@ -264,15 +270,22 @@ static uint8_t GBVideoDummyRendererWriteVideoRegister(struct GBVideoRenderer* re
return value;
}
static void GBVideoDummyRendererDrawDot(struct GBVideoRenderer* renderer, int x, int y, struct GBObj** obj, size_t oamMax) {
static void GBVideoDummyRendererDrawRange(struct GBVideoRenderer* renderer, int startX, int endX, int y, struct GBObj** obj, size_t oamMax) {
UNUSED(renderer);
UNUSED(x);
UNUSED(endX);
UNUSED(startX);
UNUSED(y);
UNUSED(obj);
UNUSED(oamMax);
// Nothing to do
}
static void GBVideoDummyRendererFinishScanline(struct GBVideoRenderer* renderer, int y) {
UNUSED(renderer);
UNUSED(y);
// Nothing to do
}
static void GBVideoDummyRendererFinishFrame(struct GBVideoRenderer* renderer) {
UNUSED(renderer);
// Nothing to do

View File

@ -17,8 +17,8 @@ enum {
GB_VIDEO_VERTICAL_TOTAL_PIXELS = GB_VIDEO_VERTICAL_PIXELS + GB_VIDEO_VBLANK_PIXELS,
// TODO: Figure out exact lengths
GB_VIDEO_MODE_2_LENGTH = 84,
GB_VIDEO_MODE_3_LENGTH_BASE = 168,
GB_VIDEO_MODE_2_LENGTH = 85,
GB_VIDEO_MODE_3_LENGTH_BASE = 167,
GB_VIDEO_MODE_0_LENGTH_BASE = 204,
GB_VIDEO_HORIZONTAL_LENGTH = GB_VIDEO_MODE_0_LENGTH_BASE + GB_VIDEO_MODE_2_LENGTH + GB_VIDEO_MODE_3_LENGTH_BASE,
@ -54,7 +54,8 @@ struct GBVideoRenderer {
void (*deinit)(struct GBVideoRenderer* renderer);
uint8_t (*writeVideoRegister)(struct GBVideoRenderer* renderer, uint16_t address, uint8_t value);
void (*drawDot)(struct GBVideoRenderer* renderer, int x, int y, struct GBObj** objOnLine, size_t nObj);
void (*drawRange)(struct GBVideoRenderer* renderer, int startX, int endX, int y, struct GBObj** objOnLine, size_t nObj);
void (*finishScanline)(struct GBVideoRenderer* renderer, int y);
void (*finishFrame)(struct GBVideoRenderer* renderer);
void (*getPixels)(struct GBVideoRenderer* renderer, unsigned* stride, const void** pixels);
@ -96,7 +97,7 @@ struct GBVideo {
int32_t eventDiff;
int32_t nextMode;
int32_t nextDot;
int32_t dotCounter;
uint8_t* vram;
uint8_t* vramBank;
@ -115,6 +116,7 @@ void GBVideoReset(struct GBVideo* video);
void GBVideoDeinit(struct GBVideo* video);
void GBVideoAssociateRenderer(struct GBVideo* video, struct GBVideoRenderer* renderer);
int32_t GBVideoProcessEvents(struct GBVideo* video, int32_t cycles);
void GBVideoProcessDots(struct GBVideo* video);
void GBVideoWriteLCDC(struct GBVideo* video, GBRegisterLCDC value);
void GBVideoWriteSTAT(struct GBVideo* video, GBRegisterSTAT value);