GPU2D: hardware renders sprites one scanline in advance.

fixes #375 (midframe OAM update)
This commit is contained in:
Arisotura
2019-06-10 03:05:26 +02:00
parent 9ab331c6dd
commit d28035674a
3 changed files with 63 additions and 120 deletions

View File

@ -871,12 +871,24 @@ void StartHBlank(u32 line)
GPU2D_B->DrawScanline(line); GPU2D_B->DrawScanline(line);
} }
// sprites are pre-rendered one scanline in advance
if (line < 191)
{
GPU2D_A->DrawSprites(line+1);
GPU2D_B->DrawSprites(line+1);
}
NDS::CheckDMAs(0, 0x02); NDS::CheckDMAs(0, 0x02);
} }
else if (VCount == 215) else if (VCount == 215)
{ {
GPU3D::VCount215(); GPU3D::VCount215();
} }
else if (VCount == 262)
{
GPU2D_A->DrawSprites(0);
GPU2D_B->DrawSprites(0);
}
if (DispStat[0] & (1<<4)) NDS::SetIRQ(0, NDS::IRQ_HBlank); if (DispStat[0] & (1<<4)) NDS::SetIRQ(0, NDS::IRQ_HBlank);
if (DispStat[1] & (1<<4)) NDS::SetIRQ(1, NDS::IRQ_HBlank); if (DispStat[1] & (1<<4)) NDS::SetIRQ(1, NDS::IRQ_HBlank);

View File

@ -1218,10 +1218,14 @@ void GPU2D::CalculateWindowMask(u32 line)
for (u32 i = 0; i < 256; i++) for (u32 i = 0; i < 256; i++)
WindowMask[i] = WinCnt[2]; // window outside WindowMask[i] = WinCnt[2]; // window outside
if ((DispCnt & (1<<15)) && (DispCnt & (1<<12))) if (DispCnt & (1<<15))
{ {
// OBJ window // OBJ window
DrawSpritesWindow(line); for (int i = 0; i < 256; i++)
{
if (OBJWindow[i])
WindowMask[i] = WinCnt[3];
}
} }
if (DispCnt & (1<<14)) if (DispCnt & (1<<14))
@ -1257,7 +1261,7 @@ void GPU2D::CalculateWindowMask(u32 line)
template<u32 bgmode> template<u32 bgmode>
void GPU2D::DrawScanlineBGMode(u32 line, u32 nsprites) void GPU2D::DrawScanlineBGMode(u32 line)
{ {
for (int i = 3; i >= 0; i--) for (int i = 3; i >= 0; i--)
{ {
@ -1302,12 +1306,12 @@ void GPU2D::DrawScanlineBGMode(u32 line, u32 nsprites)
DrawBG_Text(line, 0); DrawBG_Text(line, 0);
} }
} }
if ((DispCnt & 0x1000) && nsprites) if ((DispCnt & 0x1000) && NumSprites)
InterleaveSprites(0x8000 | (i<<16)); InterleaveSprites(0x8000 | (i<<16));
} }
} }
void GPU2D::DrawScanlineBGMode6(u32 line, u32 nsprites) void GPU2D::DrawScanlineBGMode6(u32 line)
{ {
if (Num) if (Num)
{ {
@ -1332,7 +1336,7 @@ void GPU2D::DrawScanlineBGMode6(u32 line, u32 nsprites)
DrawBG_3D(); DrawBG_3D();
} }
} }
if ((DispCnt & 0x1000) && nsprites) if ((DispCnt & 0x1000) && NumSprites)
InterleaveSprites(0x8000 | (i<<16)); InterleaveSprites(0x8000 | (i<<16));
} }
} }
@ -1360,21 +1364,16 @@ void GPU2D::DrawScanline_BGOBJ(u32 line)
else else
memset(WindowMask, 0xFF, 256); memset(WindowMask, 0xFF, 256);
// prerender sprites
u32 nsprites = 0;
memset(OBJLine, 0, 256*4);
if (DispCnt & 0x1000) nsprites = DrawSprites(line);
// TODO: what happens in mode 7? mode 6 on the sub engine? // TODO: what happens in mode 7? mode 6 on the sub engine?
switch (DispCnt & 0x7) switch (DispCnt & 0x7)
{ {
case 0: DrawScanlineBGMode<0>(line, nsprites); break; case 0: DrawScanlineBGMode<0>(line); break;
case 1: DrawScanlineBGMode<1>(line, nsprites); break; case 1: DrawScanlineBGMode<1>(line); break;
case 2: DrawScanlineBGMode<2>(line, nsprites); break; case 2: DrawScanlineBGMode<2>(line); break;
case 3: DrawScanlineBGMode<3>(line, nsprites); break; case 3: DrawScanlineBGMode<3>(line); break;
case 4: DrawScanlineBGMode<4>(line, nsprites); break; case 4: DrawScanlineBGMode<4>(line); break;
case 5: DrawScanlineBGMode<5>(line, nsprites); break; case 5: DrawScanlineBGMode<5>(line); break;
case 6: DrawScanlineBGMode6(line, nsprites); break; case 6: DrawScanlineBGMode6(line); break;
} }
// color special effects // color special effects
@ -2124,8 +2123,13 @@ void GPU2D::InterleaveSprites(u32 prio)
} }
} }
u32 GPU2D::DrawSprites(u32 line) void GPU2D::DrawSprites(u32 line)
{ {
NumSprites = 0;
memset(OBJLine, 0, 256*4);
memset(OBJWindow, 0, 256);
if (!(DispCnt & 0x1000)) return;
u16* oam = (u16*)&GPU::OAM[Num ? 0x400 : 0]; u16* oam = (u16*)&GPU::OAM[Num ? 0x400 : 0];
const s32 spritewidth[16] = const s32 spritewidth[16] =
@ -2154,8 +2158,7 @@ u32 GPU2D::DrawSprites(u32 line)
if ((attrib[2] & 0x0C00) != bgnum) if ((attrib[2] & 0x0C00) != bgnum)
continue; continue;
if (((attrib[0] >> 10) & 0x3) == 2) bool iswin = (((attrib[0] >> 10) & 0x3) == 2);
continue;
if (attrib[0] & 0x0100) if (attrib[0] & 0x0100)
{ {
@ -2182,8 +2185,12 @@ u32 GPU2D::DrawSprites(u32 line)
u32 rotparamgroup = (attrib[1] >> 9) & 0x1F; u32 rotparamgroup = (attrib[1] >> 9) & 0x1F;
DrawSprite_Rotscale<false>(attrib, &oam[(rotparamgroup*16) + 3], boundwidth, boundheight, width, height, xpos, ypos); if (iswin)
nsprites++; DrawSprite_Rotscale<true>(attrib, &oam[(rotparamgroup*16) + 3], boundwidth, boundheight, width, height, xpos, ypos);
else
DrawSprite_Rotscale<false>(attrib, &oam[(rotparamgroup*16) + 3], boundwidth, boundheight, width, height, xpos, ypos);
NumSprites++;
} }
else else
{ {
@ -2207,93 +2214,15 @@ u32 GPU2D::DrawSprites(u32 line)
if (attrib[1] & 0x2000) if (attrib[1] & 0x2000)
ypos = height-1 - ypos; ypos = height-1 - ypos;
DrawSprite_Normal<false>(attrib, width, xpos, ypos); if (iswin)
nsprites++; DrawSprite_Normal<true>(attrib, width, xpos, ypos);
else
DrawSprite_Normal<false>(attrib, width, xpos, ypos);
NumSprites++;
} }
} }
} }
return nsprites;
}
void GPU2D::DrawSpritesWindow(u32 line)
{
u16* oam = (u16*)&GPU::OAM[Num ? 0x400 : 0];
const s32 spritewidth[16] =
{
8, 16, 8, 0,
16, 32, 8, 0,
32, 32, 16, 0,
64, 64, 32, 0
};
const s32 spriteheight[16] =
{
8, 8, 16, 0,
16, 8, 32, 0,
32, 16, 32, 0,
64, 32, 64, 0
};
for (int sprnum = 127; sprnum >= 0; sprnum--)
{
u16* attrib = &oam[sprnum*4];
if (((attrib[0] >> 10) & 0x3) != 2)
continue;
if (attrib[0] & 0x0100)
{
u32 sizeparam = (attrib[0] >> 14) | ((attrib[1] & 0xC000) >> 12);
s32 width = spritewidth[sizeparam];
s32 height = spriteheight[sizeparam];
s32 boundwidth = width;
s32 boundheight = height;
if (attrib[0] & 0x0200)
{
boundwidth <<= 1;
boundheight <<= 1;
}
u32 ypos = attrib[0] & 0xFF;
ypos = (line - ypos) & 0xFF;
if (ypos >= (u32)boundheight)
continue;
s32 xpos = (s32)(attrib[1] << 23) >> 23;
if (xpos <= -boundwidth)
continue;
u32 rotparamgroup = (attrib[1] >> 9) & 0x1F;
DrawSprite_Rotscale<true>(attrib, &oam[(rotparamgroup*16) + 3], boundwidth, boundheight, width, height, xpos, ypos);
}
else
{
if (attrib[0] & 0x0200)
continue;
u32 sizeparam = (attrib[0] >> 14) | ((attrib[1] & 0xC000) >> 12);
s32 width = spritewidth[sizeparam];
s32 height = spriteheight[sizeparam];
u32 ypos = attrib[0] & 0xFF;
ypos = (line - ypos) & 0xFF;
if (ypos >= (u32)height)
continue;
s32 xpos = (s32)(attrib[1] << 23) >> 23;
if (xpos <= -width)
continue;
// yflip
if (attrib[1] & 0x2000)
ypos = height-1 - ypos;
DrawSprite_Normal<true>(attrib, width, xpos, ypos);
}
}
} }
template<bool window> template<bool window>
@ -2399,7 +2328,7 @@ void GPU2D::DrawSprite_Rotscale(u16* attrib, u16* rotparams, u32 boundwidth, u32
if (color & 0x8000) if (color & 0x8000)
{ {
if (window) WindowMask[xpos] = WinCnt[3]; if (window) OBJWindow[xpos] = 1;
else OBJLine[xpos] = color | prio; else OBJLine[xpos] = color | prio;
} }
} }
@ -2463,7 +2392,7 @@ void GPU2D::DrawSprite_Rotscale(u16* attrib, u16* rotparams, u32 boundwidth, u32
if (color) if (color)
{ {
if (window) WindowMask[xpos] = WinCnt[3]; if (window) OBJWindow[xpos] = 1;
else OBJLine[xpos] = pal[color] | prio; else OBJLine[xpos] = pal[color] | prio;
} }
} }
@ -2521,7 +2450,7 @@ void GPU2D::DrawSprite_Rotscale(u16* attrib, u16* rotparams, u32 boundwidth, u32
if (color) if (color)
{ {
if (window) WindowMask[xpos] = WinCnt[3]; if (window) OBJWindow[xpos] = 1;
else OBJLine[xpos] = pal[color] | prio; else OBJLine[xpos] = pal[color] | prio;
} }
} }
@ -2635,7 +2564,7 @@ void GPU2D::DrawSprite_Normal(u16* attrib, u32 width, s32 xpos, s32 ypos)
if (color & 0x8000) if (color & 0x8000)
{ {
if (window) WindowMask[xpos] = WinCnt[3]; if (window) OBJWindow[xpos] = 1;
else OBJLine[xpos] = color | prio; else OBJLine[xpos] = color | prio;
} }
@ -2662,7 +2591,7 @@ void GPU2D::DrawSprite_Normal(u16* attrib, u32 width, s32 xpos, s32 ypos)
if (color & 0x8000) if (color & 0x8000)
{ {
if (window) WindowMask[xpos] = WinCnt[3]; if (window) OBJWindow[xpos] = 1;
else OBJLine[xpos] = color | prio; else OBJLine[xpos] = color | prio;
} }
@ -2722,7 +2651,7 @@ void GPU2D::DrawSprite_Normal(u16* attrib, u32 width, s32 xpos, s32 ypos)
if (color) if (color)
{ {
if (window) WindowMask[xpos] = WinCnt[3]; if (window) OBJWindow[xpos] = 1;
else OBJLine[xpos] = pal[color] | prio; else OBJLine[xpos] = pal[color] | prio;
} }
@ -2751,7 +2680,7 @@ void GPU2D::DrawSprite_Normal(u16* attrib, u32 width, s32 xpos, s32 ypos)
if (color) if (color)
{ {
if (window) WindowMask[xpos] = WinCnt[3]; if (window) OBJWindow[xpos] = 1;
else OBJLine[xpos] = pal[color] | prio; else OBJLine[xpos] = pal[color] | prio;
} }
@ -2800,7 +2729,7 @@ void GPU2D::DrawSprite_Normal(u16* attrib, u32 width, s32 xpos, s32 ypos)
if (color) if (color)
{ {
if (window) WindowMask[xpos] = WinCnt[3]; if (window) OBJWindow[xpos] = 1;
else OBJLine[xpos] = pal[color] | prio; else OBJLine[xpos] = pal[color] | prio;
} }
@ -2834,7 +2763,7 @@ void GPU2D::DrawSprite_Normal(u16* attrib, u32 width, s32 xpos, s32 ypos)
if (color) if (color)
{ {
if (window) WindowMask[xpos] = WinCnt[3]; if (window) OBJWindow[xpos] = 1;
else OBJLine[xpos] = pal[color] | prio; else OBJLine[xpos] = pal[color] | prio;
} }

View File

@ -53,6 +53,7 @@ public:
void SampleFIFO(u32 offset, u32 num); void SampleFIFO(u32 offset, u32 num);
void DrawScanline(u32 line); void DrawScanline(u32 line);
void DrawSprites(u32 line);
void VBlank(); void VBlank();
void VBlankEnd(); void VBlankEnd();
@ -76,6 +77,9 @@ private:
u8 WindowMask[256] __attribute__((aligned (8))); u8 WindowMask[256] __attribute__((aligned (8)));
u32 OBJLine[256] __attribute__((aligned (8))); u32 OBJLine[256] __attribute__((aligned (8)));
u8 OBJWindow[256] __attribute__((aligned (8)));
u32 NumSprites;
u16 DispFIFO[16]; u16 DispFIFO[16];
u32 DispFIFOReadPtr; u32 DispFIFOReadPtr;
@ -129,8 +133,8 @@ private:
u32 ColorBrightnessDown(u32 val, u32 factor); u32 ColorBrightnessDown(u32 val, u32 factor);
u32 ColorComposite(int i, u32 val1, u32 val2); u32 ColorComposite(int i, u32 val1, u32 val2);
template<u32 bgmode> void DrawScanlineBGMode(u32 line, u32 nsprites); template<u32 bgmode> void DrawScanlineBGMode(u32 line);
void DrawScanlineBGMode6(u32 line, u32 nsprites); void DrawScanlineBGMode6(u32 line);
void DrawScanline_BGOBJ(u32 line); void DrawScanline_BGOBJ(u32 line);
static void DrawPixel_Normal(u32* dst, u16 color, u32 flag); static void DrawPixel_Normal(u32* dst, u16 color, u32 flag);
@ -144,8 +148,6 @@ private:
void DrawBG_Large(u32 line); void DrawBG_Large(u32 line);
void InterleaveSprites(u32 prio); void InterleaveSprites(u32 prio);
u32 DrawSprites(u32 line);
void DrawSpritesWindow(u32 line);
template<bool window> void DrawSprite_Rotscale(u16* attrib, u16* rotparams, u32 boundwidth, u32 boundheight, u32 width, u32 height, s32 xpos, s32 ypos); template<bool window> void DrawSprite_Rotscale(u16* attrib, u16* rotparams, u32 boundwidth, u32 boundheight, u32 width, u32 height, s32 xpos, s32 ypos);
template<bool window> void DrawSprite_Normal(u16* attrib, u32 width, s32 xpos, s32 ypos); template<bool window> void DrawSprite_Normal(u16* attrib, u32 width, s32 xpos, s32 ypos);