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);
}
// 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);
}
else if (VCount == 215)
{
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[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++)
WindowMask[i] = WinCnt[2]; // window outside
if ((DispCnt & (1<<15)) && (DispCnt & (1<<12)))
if (DispCnt & (1<<15))
{
// OBJ window
DrawSpritesWindow(line);
for (int i = 0; i < 256; i++)
{
if (OBJWindow[i])
WindowMask[i] = WinCnt[3];
}
}
if (DispCnt & (1<<14))
@ -1257,7 +1261,7 @@ void GPU2D::CalculateWindowMask(u32 line)
template<u32 bgmode>
void GPU2D::DrawScanlineBGMode(u32 line, u32 nsprites)
void GPU2D::DrawScanlineBGMode(u32 line)
{
for (int i = 3; i >= 0; i--)
{
@ -1302,12 +1306,12 @@ void GPU2D::DrawScanlineBGMode(u32 line, u32 nsprites)
DrawBG_Text(line, 0);
}
}
if ((DispCnt & 0x1000) && nsprites)
if ((DispCnt & 0x1000) && NumSprites)
InterleaveSprites(0x8000 | (i<<16));
}
}
void GPU2D::DrawScanlineBGMode6(u32 line, u32 nsprites)
void GPU2D::DrawScanlineBGMode6(u32 line)
{
if (Num)
{
@ -1332,7 +1336,7 @@ void GPU2D::DrawScanlineBGMode6(u32 line, u32 nsprites)
DrawBG_3D();
}
}
if ((DispCnt & 0x1000) && nsprites)
if ((DispCnt & 0x1000) && NumSprites)
InterleaveSprites(0x8000 | (i<<16));
}
}
@ -1360,21 +1364,16 @@ void GPU2D::DrawScanline_BGOBJ(u32 line)
else
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?
switch (DispCnt & 0x7)
{
case 0: DrawScanlineBGMode<0>(line, nsprites); break;
case 1: DrawScanlineBGMode<1>(line, nsprites); break;
case 2: DrawScanlineBGMode<2>(line, nsprites); break;
case 3: DrawScanlineBGMode<3>(line, nsprites); break;
case 4: DrawScanlineBGMode<4>(line, nsprites); break;
case 5: DrawScanlineBGMode<5>(line, nsprites); break;
case 6: DrawScanlineBGMode6(line, nsprites); break;
case 0: DrawScanlineBGMode<0>(line); break;
case 1: DrawScanlineBGMode<1>(line); break;
case 2: DrawScanlineBGMode<2>(line); break;
case 3: DrawScanlineBGMode<3>(line); break;
case 4: DrawScanlineBGMode<4>(line); break;
case 5: DrawScanlineBGMode<5>(line); break;
case 6: DrawScanlineBGMode6(line); break;
}
// 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];
const s32 spritewidth[16] =
@ -2154,8 +2158,7 @@ u32 GPU2D::DrawSprites(u32 line)
if ((attrib[2] & 0x0C00) != bgnum)
continue;
if (((attrib[0] >> 10) & 0x3) == 2)
continue;
bool iswin = (((attrib[0] >> 10) & 0x3) == 2);
if (attrib[0] & 0x0100)
{
@ -2182,8 +2185,12 @@ u32 GPU2D::DrawSprites(u32 line)
u32 rotparamgroup = (attrib[1] >> 9) & 0x1F;
DrawSprite_Rotscale<false>(attrib, &oam[(rotparamgroup*16) + 3], boundwidth, boundheight, width, height, xpos, ypos);
nsprites++;
if (iswin)
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
{
@ -2207,93 +2214,15 @@ u32 GPU2D::DrawSprites(u32 line)
if (attrib[1] & 0x2000)
ypos = height-1 - ypos;
DrawSprite_Normal<false>(attrib, width, xpos, ypos);
nsprites++;
if (iswin)
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>
@ -2399,7 +2328,7 @@ void GPU2D::DrawSprite_Rotscale(u16* attrib, u16* rotparams, u32 boundwidth, u32
if (color & 0x8000)
{
if (window) WindowMask[xpos] = WinCnt[3];
if (window) OBJWindow[xpos] = 1;
else OBJLine[xpos] = color | prio;
}
}
@ -2463,7 +2392,7 @@ void GPU2D::DrawSprite_Rotscale(u16* attrib, u16* rotparams, u32 boundwidth, u32
if (color)
{
if (window) WindowMask[xpos] = WinCnt[3];
if (window) OBJWindow[xpos] = 1;
else OBJLine[xpos] = pal[color] | prio;
}
}
@ -2521,7 +2450,7 @@ void GPU2D::DrawSprite_Rotscale(u16* attrib, u16* rotparams, u32 boundwidth, u32
if (color)
{
if (window) WindowMask[xpos] = WinCnt[3];
if (window) OBJWindow[xpos] = 1;
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 (window) WindowMask[xpos] = WinCnt[3];
if (window) OBJWindow[xpos] = 1;
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 (window) WindowMask[xpos] = WinCnt[3];
if (window) OBJWindow[xpos] = 1;
else OBJLine[xpos] = color | prio;
}
@ -2722,7 +2651,7 @@ void GPU2D::DrawSprite_Normal(u16* attrib, u32 width, s32 xpos, s32 ypos)
if (color)
{
if (window) WindowMask[xpos] = WinCnt[3];
if (window) OBJWindow[xpos] = 1;
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 (window) WindowMask[xpos] = WinCnt[3];
if (window) OBJWindow[xpos] = 1;
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 (window) WindowMask[xpos] = WinCnt[3];
if (window) OBJWindow[xpos] = 1;
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 (window) WindowMask[xpos] = WinCnt[3];
if (window) OBJWindow[xpos] = 1;
else OBJLine[xpos] = pal[color] | prio;
}

View File

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