From ec25074cec84c82e4105a6b84e8779b1cf3a937e Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Wed, 2 Aug 2017 18:17:51 -0700 Subject: [PATCH] GBA Video: Mark scanlines as dirty if they need to be updated --- .../internal/gba/renderers/video-software.h | 4 + src/gba/core.c | 1 + src/gba/memory.c | 77 ++++++++---- src/gba/renderers/video-software.c | 116 +++++++++++++----- 4 files changed, 146 insertions(+), 52 deletions(-) diff --git a/include/mgba/internal/gba/renderers/video-software.h b/include/mgba/internal/gba/renderers/video-software.h index 853a6f335..c6a0c7781 100644 --- a/include/mgba/internal/gba/renderers/video-software.h +++ b/include/mgba/internal/gba/renderers/video-software.h @@ -11,6 +11,7 @@ CXX_GUARD_START #include +#include #include struct GBAVideoSoftwareSprite { @@ -157,6 +158,9 @@ struct GBAVideoSoftwareRenderer { int oamMax; struct GBAVideoSoftwareSprite sprites[128]; + uint32_t scanlineDirty[5]; + uint16_t ioCache[REG_SOUND1CNT_LO]; + int start; int end; }; diff --git a/src/gba/core.c b/src/gba/core.c index 695f842b6..1940d6418 100644 --- a/src/gba/core.c +++ b/src/gba/core.c @@ -258,6 +258,7 @@ static void _GBACoreSetVideoBuffer(struct mCore* core, color_t* buffer, size_t s struct GBACore* gbacore = (struct GBACore*) core; gbacore->renderer.outputBuffer = buffer; gbacore->renderer.outputBufferStride = stride; + memset(gbacore->renderer.scanlineDirty, 0xFFFFFFFF, sizeof(gbacore->renderer.scanlineDirty)); } static void _GBACoreGetPixels(struct mCore* core, const void** buffer, size_t* stride) { diff --git a/src/gba/memory.c b/src/gba/memory.c index 8b407fa09..5b81f1963 100644 --- a/src/gba/memory.c +++ b/src/gba/memory.c @@ -683,27 +683,39 @@ uint32_t GBALoad8(struct ARMCore* cpu, uint32_t address, int* cycleCounter) { GBAIOWrite32(gba, address & (OFFSET_MASK - 3), value); #define STORE_PALETTE_RAM \ - STORE_32(value, address & (SIZE_PALETTE_RAM - 4), gba->video.palette); \ - gba->video.renderer->writePalette(gba->video.renderer, (address & (SIZE_PALETTE_RAM - 4)) + 2, value >> 16); \ - wait += waitstatesRegion[REGION_PALETTE_RAM]; \ - gba->video.renderer->writePalette(gba->video.renderer, address & (SIZE_PALETTE_RAM - 4), value); + LOAD_32(oldValue, address & (SIZE_PALETTE_RAM - 4), gba->video.palette); \ + if (oldValue != value) { \ + STORE_32(value, address & (SIZE_PALETTE_RAM - 4), gba->video.palette); \ + gba->video.renderer->writePalette(gba->video.renderer, (address & (SIZE_PALETTE_RAM - 4)) + 2, value >> 16); \ + gba->video.renderer->writePalette(gba->video.renderer, address & (SIZE_PALETTE_RAM - 4), value); \ + } \ + wait += waitstatesRegion[REGION_PALETTE_RAM]; #define STORE_VRAM \ if ((address & 0x0001FFFF) < SIZE_VRAM) { \ - STORE_32(value, address & 0x0001FFFC, gba->video.vram); \ - gba->video.renderer->writeVRAM(gba->video.renderer, (address & 0x0001FFFC) + 2); \ - gba->video.renderer->writeVRAM(gba->video.renderer, (address & 0x0001FFFC)); \ + LOAD_32(oldValue, address & 0x0001FFFC, gba->video.vram); \ + if (oldValue != value) { \ + STORE_32(value, address & 0x0001FFFC, gba->video.vram); \ + gba->video.renderer->writeVRAM(gba->video.renderer, (address & 0x0001FFFC) + 2); \ + gba->video.renderer->writeVRAM(gba->video.renderer, (address & 0x0001FFFC)); \ + } \ } else { \ - STORE_32(value, address & 0x00017FFC, gba->video.vram); \ - gba->video.renderer->writeVRAM(gba->video.renderer, (address & 0x00017FFC) + 2); \ - gba->video.renderer->writeVRAM(gba->video.renderer, (address & 0x00017FFC)); \ + LOAD_32(oldValue, address & 0x00017FFC, gba->video.vram); \ + if (oldValue != value) { \ + STORE_32(value, address & 0x00017FFC, gba->video.vram); \ + gba->video.renderer->writeVRAM(gba->video.renderer, (address & 0x00017FFC) + 2); \ + gba->video.renderer->writeVRAM(gba->video.renderer, (address & 0x00017FFC)); \ + } \ } \ wait += waitstatesRegion[REGION_VRAM]; #define STORE_OAM \ - STORE_32(value, address & (SIZE_OAM - 4), gba->video.oam.raw); \ - gba->video.renderer->writeOAM(gba->video.renderer, (address & (SIZE_OAM - 4)) >> 1); \ - gba->video.renderer->writeOAM(gba->video.renderer, ((address & (SIZE_OAM - 4)) >> 1) + 1); + LOAD_32(oldValue, address & (SIZE_OAM - 4), gba->video.oam.raw); \ + if (oldValue != value) { \ + STORE_32(value, address & (SIZE_OAM - 4), gba->video.oam.raw); \ + gba->video.renderer->writeOAM(gba->video.renderer, (address & (SIZE_OAM - 4)) >> 1); \ + gba->video.renderer->writeOAM(gba->video.renderer, ((address & (SIZE_OAM - 4)) >> 1) + 1); \ + } #define STORE_CART \ wait += waitstatesRegion[address >> BASE_OFFSET]; \ @@ -726,6 +738,7 @@ void GBAStore32(struct ARMCore* cpu, uint32_t address, int32_t value, int* cycle struct GBA* gba = (struct GBA*) cpu->master; struct GBAMemory* memory = &gba->memory; int wait = 0; + int32_t oldValue; char* waitstatesRegion = memory->waitstatesNonseq32; switch (address >> BASE_OFFSET) { @@ -777,6 +790,7 @@ void GBAStore16(struct ARMCore* cpu, uint32_t address, int16_t value, int* cycle struct GBA* gba = (struct GBA*) cpu->master; struct GBAMemory* memory = &gba->memory; int wait = 0; + int16_t oldValue; switch (address >> BASE_OFFSET) { case REGION_WORKING_RAM: @@ -790,21 +804,33 @@ void GBAStore16(struct ARMCore* cpu, uint32_t address, int16_t value, int* cycle GBAIOWrite(gba, address & (OFFSET_MASK - 1), value); break; case REGION_PALETTE_RAM: - STORE_16(value, address & (SIZE_PALETTE_RAM - 2), gba->video.palette); - gba->video.renderer->writePalette(gba->video.renderer, address & (SIZE_PALETTE_RAM - 2), value); + LOAD_16(oldValue, address & (SIZE_PALETTE_RAM - 2), gba->video.palette); + if (oldValue != value) { + STORE_16(value, address & (SIZE_PALETTE_RAM - 2), gba->video.palette); + gba->video.renderer->writePalette(gba->video.renderer, address & (SIZE_PALETTE_RAM - 2), value); + } break; case REGION_VRAM: if ((address & 0x0001FFFF) < SIZE_VRAM) { - STORE_16(value, address & 0x0001FFFE, gba->video.vram); - gba->video.renderer->writeVRAM(gba->video.renderer, address & 0x0001FFFE); + LOAD_16(oldValue, address & 0x0001FFFE, gba->video.vram); + if (value != oldValue) { + STORE_16(value, address & 0x0001FFFE, gba->video.vram); + gba->video.renderer->writeVRAM(gba->video.renderer, address & 0x0001FFFE); + } } else { - STORE_16(value, address & 0x00017FFE, gba->video.vram); - gba->video.renderer->writeVRAM(gba->video.renderer, address & 0x00017FFE); + LOAD_16(oldValue, address & 0x00017FFE, gba->video.vram); + if (value != oldValue) { + STORE_16(value, address & 0x00017FFE, gba->video.vram); + gba->video.renderer->writeVRAM(gba->video.renderer, address & 0x00017FFE); + } } break; case REGION_OAM: - STORE_16(value, address & (SIZE_OAM - 2), gba->video.oam.raw); - gba->video.renderer->writeOAM(gba->video.renderer, (address & (SIZE_OAM - 2)) >> 1); + LOAD_16(oldValue, address & (SIZE_OAM - 2), gba->video.oam.raw); + if (value != oldValue) { + STORE_16(value, address & (SIZE_OAM - 2), gba->video.oam.raw); + gba->video.renderer->writeOAM(gba->video.renderer, (address & (SIZE_OAM - 2)) >> 1); + } break; case REGION_CART0: if (memory->hw.devices != HW_NONE && IS_GPIO_REGISTER(address & 0xFFFFFE)) { @@ -844,6 +870,7 @@ void GBAStore8(struct ARMCore* cpu, uint32_t address, int8_t value, int* cycleCo struct GBA* gba = (struct GBA*) cpu->master; struct GBAMemory* memory = &gba->memory; int wait = 0; + uint16_t oldValue; switch (address >> BASE_OFFSET) { case REGION_WORKING_RAM: @@ -865,8 +892,11 @@ void GBAStore8(struct ARMCore* cpu, uint32_t address, int8_t value, int* cycleCo mLOG(GBA_MEM, GAME_ERROR, "Cannot Store8 to OBJ: 0x%08X", address); break; } - gba->video.renderer->vram[(address & 0x1FFFE) >> 1] = ((uint8_t) value) | (value << 8); - gba->video.renderer->writeVRAM(gba->video.renderer, address & 0x0001FFFE); + oldValue = gba->video.renderer->vram[(address & 0x1FFFE) >> 1]; + if (oldValue != (((uint8_t) value) | (value << 8))) { + gba->video.renderer->vram[(address & 0x1FFFE) >> 1] = ((uint8_t) value) | (value << 8); + gba->video.renderer->writeVRAM(gba->video.renderer, address & 0x0001FFFE); + } break; case REGION_OAM: mLOG(GBA_MEM, GAME_ERROR, "Cannot Store8 to OAM: 0x%08X", address); @@ -1369,6 +1399,7 @@ uint32_t GBAStoreMultiple(struct ARMCore* cpu, uint32_t address, int mask, enum struct GBA* gba = (struct GBA*) cpu->master; struct GBAMemory* memory = &gba->memory; uint32_t value; + uint32_t oldValue; char* waitstatesRegion = memory->waitstatesSeq32; int i; diff --git a/src/gba/renderers/video-software.c b/src/gba/renderers/video-software.c index 9898fa9ae..11b8b7515 100644 --- a/src/gba/renderers/video-software.c +++ b/src/gba/renderers/video-software.c @@ -26,10 +26,6 @@ static void GBAVideoSoftwareRendererPutPixels(struct GBAVideoRenderer* renderer, static void GBAVideoSoftwareRendererUpdateDISPCNT(struct GBAVideoSoftwareRenderer* renderer); static void GBAVideoSoftwareRendererWriteBGCNT(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* bg, uint16_t value); -static void GBAVideoSoftwareRendererWriteBGPA(struct GBAVideoSoftwareBackground* bg, uint16_t value); -static void GBAVideoSoftwareRendererWriteBGPB(struct GBAVideoSoftwareBackground* bg, uint16_t value); -static void GBAVideoSoftwareRendererWriteBGPC(struct GBAVideoSoftwareBackground* bg, uint16_t value); -static void GBAVideoSoftwareRendererWriteBGPD(struct GBAVideoSoftwareBackground* bg, uint16_t value); static void GBAVideoSoftwareRendererWriteBGX_LO(struct GBAVideoSoftwareBackground* bg, uint16_t value); static void GBAVideoSoftwareRendererWriteBGX_HI(struct GBAVideoSoftwareBackground* bg, uint16_t value); static void GBAVideoSoftwareRendererWriteBGY_LO(struct GBAVideoSoftwareBackground* bg, uint16_t value); @@ -112,6 +108,9 @@ static void GBAVideoSoftwareRendererReset(struct GBAVideoRenderer* renderer) { softwareRenderer->mosaic = 0; + memset(softwareRenderer->scanlineDirty, 0xFFFFFFFF, sizeof(softwareRenderer->scanlineDirty)); + memset(softwareRenderer->ioCache, 0, sizeof(softwareRenderer->ioCache)); + for (i = 0; i < 4; ++i) { struct GBAVideoSoftwareBackground* bg = &softwareRenderer->bg[i]; bg->index = i; @@ -145,106 +144,162 @@ static void GBAVideoSoftwareRendererDeinit(struct GBAVideoRenderer* renderer) { static uint16_t GBAVideoSoftwareRendererWriteVideoRegister(struct GBAVideoRenderer* renderer, uint32_t address, uint16_t value) { struct GBAVideoSoftwareRenderer* softwareRenderer = (struct GBAVideoSoftwareRenderer*) renderer; + if (softwareRenderer->ioCache[address >> 1] == value) { + return value; + } switch (address) { case REG_DISPCNT: softwareRenderer->dispcnt = value; GBAVideoSoftwareRendererUpdateDISPCNT(softwareRenderer); + memset(softwareRenderer->scanlineDirty, 0xFFFFFFFF, sizeof(softwareRenderer->scanlineDirty)); break; case REG_BG0CNT: value &= 0xDFFF; GBAVideoSoftwareRendererWriteBGCNT(softwareRenderer, &softwareRenderer->bg[0], value); + if (softwareRenderer->bg[0].enabled) { + memset(softwareRenderer->scanlineDirty, 0xFFFFFFFF, sizeof(softwareRenderer->scanlineDirty)); + } break; case REG_BG1CNT: value &= 0xDFFF; GBAVideoSoftwareRendererWriteBGCNT(softwareRenderer, &softwareRenderer->bg[1], value); + if (softwareRenderer->bg[1].enabled) { + memset(softwareRenderer->scanlineDirty, 0xFFFFFFFF, sizeof(softwareRenderer->scanlineDirty)); + } break; case REG_BG2CNT: value &= 0xFFFF; GBAVideoSoftwareRendererWriteBGCNT(softwareRenderer, &softwareRenderer->bg[2], value); + if (softwareRenderer->bg[2].enabled) { + memset(softwareRenderer->scanlineDirty, 0xFFFFFFFF, sizeof(softwareRenderer->scanlineDirty)); + } break; case REG_BG3CNT: value &= 0xFFFF; GBAVideoSoftwareRendererWriteBGCNT(softwareRenderer, &softwareRenderer->bg[3], value); + if (softwareRenderer->bg[3].enabled) { + memset(softwareRenderer->scanlineDirty, 0xFFFFFFFF, sizeof(softwareRenderer->scanlineDirty)); + } break; case REG_BG0HOFS: value &= 0x01FF; softwareRenderer->bg[0].x = value; + if (softwareRenderer->bg[0].enabled && GBARegisterDISPCNTGetMode(softwareRenderer->dispcnt) < 2) { + memset(softwareRenderer->scanlineDirty, 0xFFFFFFFF, sizeof(softwareRenderer->scanlineDirty)); + } break; case REG_BG0VOFS: value &= 0x01FF; softwareRenderer->bg[0].y = value; + if (softwareRenderer->bg[0].enabled && GBARegisterDISPCNTGetMode(softwareRenderer->dispcnt) < 2) { + memset(softwareRenderer->scanlineDirty, 0xFFFFFFFF, sizeof(softwareRenderer->scanlineDirty)); + } break; case REG_BG1HOFS: value &= 0x01FF; softwareRenderer->bg[1].x = value; + if (softwareRenderer->bg[1].enabled && GBARegisterDISPCNTGetMode(softwareRenderer->dispcnt) < 2) { + memset(softwareRenderer->scanlineDirty, 0xFFFFFFFF, sizeof(softwareRenderer->scanlineDirty)); + } break; case REG_BG1VOFS: value &= 0x01FF; softwareRenderer->bg[1].y = value; + if (softwareRenderer->bg[1].enabled && GBARegisterDISPCNTGetMode(softwareRenderer->dispcnt) < 2) { + memset(softwareRenderer->scanlineDirty, 0xFFFFFFFF, sizeof(softwareRenderer->scanlineDirty)); + } break; case REG_BG2HOFS: value &= 0x01FF; softwareRenderer->bg[2].x = value; + if (softwareRenderer->bg[2].enabled && GBARegisterDISPCNTGetMode(softwareRenderer->dispcnt) == 0) { + memset(softwareRenderer->scanlineDirty, 0xFFFFFFFF, sizeof(softwareRenderer->scanlineDirty)); + } break; case REG_BG2VOFS: value &= 0x01FF; softwareRenderer->bg[2].y = value; + if (softwareRenderer->bg[2].enabled && GBARegisterDISPCNTGetMode(softwareRenderer->dispcnt) == 0) { + memset(softwareRenderer->scanlineDirty, 0xFFFFFFFF, sizeof(softwareRenderer->scanlineDirty)); + } break; case REG_BG3HOFS: value &= 0x01FF; softwareRenderer->bg[3].x = value; + if (softwareRenderer->bg[3].enabled && GBARegisterDISPCNTGetMode(softwareRenderer->dispcnt) == 0) { + memset(softwareRenderer->scanlineDirty, 0xFFFFFFFF, sizeof(softwareRenderer->scanlineDirty)); + } break; case REG_BG3VOFS: value &= 0x01FF; softwareRenderer->bg[3].y = value; + if (softwareRenderer->bg[2].enabled && GBARegisterDISPCNTGetMode(softwareRenderer->dispcnt) == 0) { + memset(softwareRenderer->scanlineDirty, 0xFFFFFFFF, sizeof(softwareRenderer->scanlineDirty)); + } break; case REG_BG2PA: - GBAVideoSoftwareRendererWriteBGPA(&softwareRenderer->bg[2], value); + softwareRenderer->bg[2].dx = value; + memset(softwareRenderer->scanlineDirty, 0xFFFFFFFF, sizeof(softwareRenderer->scanlineDirty)); break; case REG_BG2PB: - GBAVideoSoftwareRendererWriteBGPB(&softwareRenderer->bg[2], value); + softwareRenderer->bg[2].dmx = value; + memset(softwareRenderer->scanlineDirty, 0xFFFFFFFF, sizeof(softwareRenderer->scanlineDirty)); break; case REG_BG2PC: - GBAVideoSoftwareRendererWriteBGPC(&softwareRenderer->bg[2], value); + softwareRenderer->bg[2].dy = value; + memset(softwareRenderer->scanlineDirty, 0xFFFFFFFF, sizeof(softwareRenderer->scanlineDirty)); break; case REG_BG2PD: - GBAVideoSoftwareRendererWriteBGPD(&softwareRenderer->bg[2], value); + softwareRenderer->bg[2].dmy = value; + memset(softwareRenderer->scanlineDirty, 0xFFFFFFFF, sizeof(softwareRenderer->scanlineDirty)); break; case REG_BG2X_LO: GBAVideoSoftwareRendererWriteBGX_LO(&softwareRenderer->bg[2], value); + memset(softwareRenderer->scanlineDirty, 0xFFFFFFFF, sizeof(softwareRenderer->scanlineDirty)); break; case REG_BG2X_HI: GBAVideoSoftwareRendererWriteBGX_HI(&softwareRenderer->bg[2], value); + memset(softwareRenderer->scanlineDirty, 0xFFFFFFFF, sizeof(softwareRenderer->scanlineDirty)); break; case REG_BG2Y_LO: GBAVideoSoftwareRendererWriteBGY_LO(&softwareRenderer->bg[2], value); + memset(softwareRenderer->scanlineDirty, 0xFFFFFFFF, sizeof(softwareRenderer->scanlineDirty)); break; case REG_BG2Y_HI: GBAVideoSoftwareRendererWriteBGY_HI(&softwareRenderer->bg[2], value); + memset(softwareRenderer->scanlineDirty, 0xFFFFFFFF, sizeof(softwareRenderer->scanlineDirty)); break; case REG_BG3PA: - GBAVideoSoftwareRendererWriteBGPA(&softwareRenderer->bg[3], value); + softwareRenderer->bg[3].dx = value; + memset(softwareRenderer->scanlineDirty, 0xFFFFFFFF, sizeof(softwareRenderer->scanlineDirty)); break; case REG_BG3PB: - GBAVideoSoftwareRendererWriteBGPB(&softwareRenderer->bg[3], value); + softwareRenderer->bg[3].dmx = value; + memset(softwareRenderer->scanlineDirty, 0xFFFFFFFF, sizeof(softwareRenderer->scanlineDirty)); break; case REG_BG3PC: - GBAVideoSoftwareRendererWriteBGPC(&softwareRenderer->bg[3], value); + softwareRenderer->bg[3].dy = value; + memset(softwareRenderer->scanlineDirty, 0xFFFFFFFF, sizeof(softwareRenderer->scanlineDirty)); break; case REG_BG3PD: - GBAVideoSoftwareRendererWriteBGPD(&softwareRenderer->bg[3], value); + softwareRenderer->bg[3].dmy = value; + memset(softwareRenderer->scanlineDirty, 0xFFFFFFFF, sizeof(softwareRenderer->scanlineDirty)); break; case REG_BG3X_LO: GBAVideoSoftwareRendererWriteBGX_LO(&softwareRenderer->bg[3], value); + memset(softwareRenderer->scanlineDirty, 0xFFFFFFFF, sizeof(softwareRenderer->scanlineDirty)); break; case REG_BG3X_HI: GBAVideoSoftwareRendererWriteBGX_HI(&softwareRenderer->bg[3], value); + memset(softwareRenderer->scanlineDirty, 0xFFFFFFFF, sizeof(softwareRenderer->scanlineDirty)); break; case REG_BG3Y_LO: GBAVideoSoftwareRendererWriteBGY_LO(&softwareRenderer->bg[3], value); + memset(softwareRenderer->scanlineDirty, 0xFFFFFFFF, sizeof(softwareRenderer->scanlineDirty)); break; case REG_BG3Y_HI: GBAVideoSoftwareRendererWriteBGY_HI(&softwareRenderer->bg[3], value); + memset(softwareRenderer->scanlineDirty, 0xFFFFFFFF, sizeof(softwareRenderer->scanlineDirty)); break; case REG_BLDCNT: GBAVideoSoftwareRendererWriteBLDCNT(softwareRenderer, value); @@ -260,6 +315,7 @@ static uint16_t GBAVideoSoftwareRendererWriteVideoRegister(struct GBAVideoRender softwareRenderer->bldb = 0x10; } value &= 0x1F1F; + memset(softwareRenderer->scanlineDirty, 0xFFFFFFFF, sizeof(softwareRenderer->scanlineDirty)); break; case REG_BLDY: value &= 0x1F; @@ -270,6 +326,7 @@ static uint16_t GBAVideoSoftwareRendererWriteVideoRegister(struct GBAVideoRender softwareRenderer->bldy = value; softwareRenderer->blendDirty = true; } + memset(softwareRenderer->scanlineDirty, 0xFFFFFFFF, sizeof(softwareRenderer->scanlineDirty)); break; case REG_WIN0H: softwareRenderer->winN[0].h.end = value; @@ -283,6 +340,7 @@ static uint16_t GBAVideoSoftwareRendererWriteVideoRegister(struct GBAVideoRender softwareRenderer->winN[0].h.start = VIDEO_HORIZONTAL_PIXELS; } } + memset(softwareRenderer->scanlineDirty, 0xFFFFFFFF, sizeof(softwareRenderer->scanlineDirty)); break; case REG_WIN1H: softwareRenderer->winN[1].h.end = value; @@ -296,6 +354,7 @@ static uint16_t GBAVideoSoftwareRendererWriteVideoRegister(struct GBAVideoRender softwareRenderer->winN[1].h.start = VIDEO_HORIZONTAL_PIXELS; } } + memset(softwareRenderer->scanlineDirty, 0xFFFFFFFF, sizeof(softwareRenderer->scanlineDirty)); break; case REG_WIN0V: softwareRenderer->winN[0].v.end = value; @@ -309,6 +368,7 @@ static uint16_t GBAVideoSoftwareRendererWriteVideoRegister(struct GBAVideoRender softwareRenderer->winN[0].v.start = VIDEO_VERTICAL_PIXELS; } } + memset(softwareRenderer->scanlineDirty, 0xFFFFFFFF, sizeof(softwareRenderer->scanlineDirty)); break; case REG_WIN1V: softwareRenderer->winN[1].v.end = value; @@ -322,19 +382,23 @@ static uint16_t GBAVideoSoftwareRendererWriteVideoRegister(struct GBAVideoRender softwareRenderer->winN[1].v.start = VIDEO_VERTICAL_PIXELS; } } + memset(softwareRenderer->scanlineDirty, 0xFFFFFFFF, sizeof(softwareRenderer->scanlineDirty)); break; case REG_WININ: value &= 0x3F3F; softwareRenderer->winN[0].control.packed = value; softwareRenderer->winN[1].control.packed = value >> 8; + memset(softwareRenderer->scanlineDirty, 0xFFFFFFFF, sizeof(softwareRenderer->scanlineDirty)); break; case REG_WINOUT: value &= 0x3F3F; softwareRenderer->winout.packed = value; softwareRenderer->objwin.packed = value >> 8; + memset(softwareRenderer->scanlineDirty, 0xFFFFFFFF, sizeof(softwareRenderer->scanlineDirty)); break; case REG_MOSAIC: softwareRenderer->mosaic = value; + memset(softwareRenderer->scanlineDirty, 0xFFFFFFFF, sizeof(softwareRenderer->scanlineDirty)); break; case REG_GREENSWP: mLOG(GBA_VIDEO, STUB, "Stub video register write: 0x%03X", address); @@ -342,19 +406,23 @@ static uint16_t GBAVideoSoftwareRendererWriteVideoRegister(struct GBAVideoRender default: mLOG(GBA_VIDEO, GAME_ERROR, "Invalid video register: 0x%03X", address); } + softwareRenderer->ioCache[address >> 1] = value; return value; } static void GBAVideoSoftwareRendererWriteVRAM(struct GBAVideoRenderer* renderer, uint32_t address) { + struct GBAVideoSoftwareRenderer* softwareRenderer = (struct GBAVideoSoftwareRenderer*) renderer; if (renderer->cache) { mTileCacheWriteVRAM(renderer->cache, address); } + memset(softwareRenderer->scanlineDirty, 0xFFFFFFFF, sizeof(softwareRenderer->scanlineDirty)); } static void GBAVideoSoftwareRendererWriteOAM(struct GBAVideoRenderer* renderer, uint32_t oam) { struct GBAVideoSoftwareRenderer* softwareRenderer = (struct GBAVideoSoftwareRenderer*) renderer; - softwareRenderer->oamDirty = 1; UNUSED(oam); + softwareRenderer->oamDirty = 1; + memset(softwareRenderer->scanlineDirty, 0xFFFFFFFF, sizeof(softwareRenderer->scanlineDirty)); } static void GBAVideoSoftwareRendererWritePalette(struct GBAVideoRenderer* renderer, uint32_t address, uint16_t value) { @@ -369,6 +437,7 @@ static void GBAVideoSoftwareRendererWritePalette(struct GBAVideoRenderer* render if (renderer->cache) { mTileCacheWritePalette(renderer->cache, address); } + memset(softwareRenderer->scanlineDirty, 0xFFFFFFFF, sizeof(softwareRenderer->scanlineDirty)); } static void _breakWindow(struct GBAVideoSoftwareRenderer* softwareRenderer, struct WindowN* win, int y) { @@ -472,6 +541,10 @@ static void _cleanOAM(struct GBAVideoSoftwareRenderer* renderer) { static void GBAVideoSoftwareRendererDrawScanline(struct GBAVideoRenderer* renderer, int y) { struct GBAVideoSoftwareRenderer* softwareRenderer = (struct GBAVideoSoftwareRenderer*) renderer; + if (!(softwareRenderer->scanlineDirty[y >> 5] & (1 << (y & 0x1F)))) { + return; + } + color_t* row = &softwareRenderer->outputBuffer[softwareRenderer->outputBufferStride * y]; if (GBARegisterDISPCNTIsForcedBlank(softwareRenderer->dispcnt)) { int x; @@ -587,6 +660,7 @@ static void GBAVideoSoftwareRendererDrawScanline(struct GBAVideoRenderer* render #else memcpy(row, softwareRenderer->row, VIDEO_HORIZONTAL_PIXELS * sizeof(*row)); #endif + softwareRenderer->scanlineDirty[y >> 5] &= ~(1 << (y & 0x1F)); } static void GBAVideoSoftwareRendererFinishFrame(struct GBAVideoRenderer* renderer) { @@ -636,22 +710,6 @@ static void GBAVideoSoftwareRendererWriteBGCNT(struct GBAVideoSoftwareRenderer* bg->size = GBARegisterBGCNTGetSize(value); } -static void GBAVideoSoftwareRendererWriteBGPA(struct GBAVideoSoftwareBackground* bg, uint16_t value) { - bg->dx = value; -} - -static void GBAVideoSoftwareRendererWriteBGPB(struct GBAVideoSoftwareBackground* bg, uint16_t value) { - bg->dmx = value; -} - -static void GBAVideoSoftwareRendererWriteBGPC(struct GBAVideoSoftwareBackground* bg, uint16_t value) { - bg->dy = value; -} - -static void GBAVideoSoftwareRendererWriteBGPD(struct GBAVideoSoftwareBackground* bg, uint16_t value) { - bg->dmy = value; -} - static void GBAVideoSoftwareRendererWriteBGX_LO(struct GBAVideoSoftwareBackground* bg, uint16_t value) { bg->refx = (bg->refx & 0xFFFF0000) | value; bg->sx = bg->refx;