all repos — mgba @ 94ff4f7c4e25c717f3dc0b049e0e54c1818688ce

mGBA Game Boy Advance Emulator

GB Video: Optimize renderer
Jeffrey Pfau jeffrey@endrift.com
Sat, 13 Feb 2016 23:57:02 -0800
commit

94ff4f7c4e25c717f3dc0b049e0e54c1818688ce

parent

e6e535e39af599989e44957a7f297ada148a9185

4 files changed, 109 insertions(+), 73 deletions(-)

jump to
M src/gb/io.csrc/gb/io.c

@@ -275,6 +275,7 @@ case REG_WX:

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:
M src/gb/renderers/software.csrc/gb/renderers/software.c

@@ -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 @@ renderer->d.init = GBVideoSoftwareRendererInit;

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 @@ }

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 @@ }

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;

@@ -192,30 +217,25 @@ bottomY = (y - obj->y - 16) & 7;

if (GBRegisterLCDCIsObjSize(renderer->lcdc) && y - obj->y >= -8) { ++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); + } } }
M src/gb/video.csrc/gb/video.c

@@ -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 @@ .init = GBVideoDummyRendererInit,

.reset = GBVideoDummyRendererReset, .deinit = GBVideoDummyRendererDeinit, .writeVideoRegister = GBVideoDummyRendererWriteVideoRegister, - .drawDot = GBVideoDummyRendererDrawDot, + .drawRange = GBVideoDummyRendererDrawRange, + .finishScanline = GBVideoDummyRendererFinishScanline, .finishFrame = GBVideoDummyRendererFinishFrame, .getPixels = GBVideoDummyRendererGetPixels };

@@ -47,7 +49,7 @@ video->nextEvent = INT_MAX;

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 @@ }

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 @@ GBUpdateIRQs(video->p);

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;

@@ -174,9 +168,6 @@ break;

} 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 @@ }

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,12 +270,19 @@ UNUSED(address);

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 }
M src/gb/video.hsrc/gb/video.h

@@ -17,8 +17,8 @@ GB_VIDEO_VBLANK_PIXELS = 10,

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 @@ void (*reset)(struct GBVideoRenderer* renderer);

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 @@ int32_t nextEvent;

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);