mirror of https://github.com/mgba-emu/mgba.git
GB Video: Optimize renderer
This commit is contained in:
parent
e6e535e39a
commit
94ff4f7c4e
|
@ -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:
|
||||
|
|
|
@ -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 {
|
||||
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];
|
||||
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,13 +168,15 @@ 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 x;
|
||||
for (x = startX; x < endX; ++x) {
|
||||
int topX = ((x + sx) >> 3) & 0x1F;
|
||||
int bottomX = 7 - ((x + sx) & 7);
|
||||
int bgTile;
|
||||
|
@ -176,9 +190,20 @@ static void GBVideoSoftwareRendererDrawBackground(struct GBVideoSoftwareRenderer
|
|||
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,17 +218,11 @@ 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;
|
||||
int x;
|
||||
for (x = startX; x < endX; ++x) {
|
||||
if (GBObjAttributesIsXFlip(obj->attr)) {
|
||||
bottomX = (x - obj->x) & 7;
|
||||
} else {
|
||||
|
@ -218,4 +237,5 @@ static void GBVideoSoftwareRendererDrawObj(struct GBVideoSoftwareRenderer* rende
|
|||
if (((tileDataUpper | tileDataLower) & 1) && current <= mask) {
|
||||
renderer->row[x] = p | ((tileDataUpper & 1) << 1) | (tileDataLower & 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in New Issue