core vdp, fix sprite rendering issues with Overdrive 1/2

This commit is contained in:
kub 2023-04-05 20:00:37 +02:00
parent c7e1c39b28
commit d515a352b3
5 changed files with 76 additions and 32 deletions

View file

@ -97,7 +97,9 @@ u32 VdpSATCache[2*128]; // VDP sprite cache (1st 32 sprite attr bits)
#define SPRL_HAVE_MASK0 0x02 // have sprite with x == 0 in 1st slot #define SPRL_HAVE_MASK0 0x02 // have sprite with x == 0 in 1st slot
#define SPRL_MASKED 0x01 // lo prio masking by sprite with x == 0 active #define SPRL_MASKED 0x01 // lo prio masking by sprite with x == 0 active
unsigned char HighLnSpr[240][4+MAX_LINE_SPRITES+1]; // sprite_count, ^flags, tile_count, sprites_total, [spritep]..., last_width // sprite cache. stores results of sprite parsing for each display line:
// [visible_sprites_count, sprl_flags, tile_count, sprites_processed, sprite_idx[sprite_count], last_width]
unsigned char HighLnSpr[240][4+MAX_LINE_SPRITES+1];
int rendstatus_old; int rendstatus_old;
int rendlines; int rendlines;
@ -290,7 +292,8 @@ TileFlipMaker(TileFlip_and, pix_and)
pal |= 0xc0; /* leave s/h bits untouched in pixel "and" */ \ pal |= 0xc0; /* leave s/h bits untouched in pixel "and" */ \
if (likely(m & (1<<(x+8)))) { \ if (likely(m & (1<<(x+8)))) { \
m &= ~(1<<(x+8)); \ m &= ~(1<<(x+8)); \
if (t<0xe) pd[x] &= pal|t; \ /* if (!t) pd[x] |= 0x40; as per titan hw notes? */ \
pd[x] &= pal|t; \
} }
TileNormMakerAS(TileNormSH_AS_and, pix_sh_as_and) TileNormMakerAS(TileNormSH_AS_and, pix_sh_as_and)
@ -977,7 +980,7 @@ static void DrawSpritesSHi(unsigned char *sprited, const struct PicoEState *est)
unsigned char *p; unsigned char *p;
int cnt, w; int cnt, w;
cnt = sprited[0] & 0x7f; cnt = sprited[0];
if (cnt == 0) return; if (cnt == 0) return;
p = &sprited[4]; p = &sprited[4];
@ -1046,7 +1049,7 @@ static void DrawSpritesHiAS(unsigned char *sprited, int sh)
unsigned m; unsigned m;
int entry, cnt; int entry, cnt;
cnt = sprited[0] & 0x7f; cnt = sprited[0];
if (cnt == 0) return; if (cnt == 0) return;
memset(mb, 0xff, sizeof(mb)); memset(mb, 0xff, sizeof(mb));
@ -1323,7 +1326,7 @@ static void DrawSpritesForced(unsigned char *sprited)
unsigned m; unsigned m;
int entry, cnt; int entry, cnt;
cnt = sprited[0] & 0x7f; cnt = sprited[0];
if (cnt == 0) { memset(pd, 0, sizeof(DefHighCol)); return; } if (cnt == 0) { memset(pd, 0, sizeof(DefHighCol)); return; }
memset(mb, 0xff, sizeof(mb)); memset(mb, 0xff, sizeof(mb));
@ -1382,7 +1385,9 @@ static void DrawSpritesForced(unsigned char *sprited)
*mp = m; // write last mask byte *mp = m; // write last mask byte
} }
// anything not covered by a sprite is off (XXX or bg?) // anything not covered by a sprite is off
// XXX Titan hw notes say that transparent pixels remove shadow. Is this also
// the case in areas where no sprites are displayed?
for (cnt = 1; cnt < sizeof(mb)-1; cnt++) for (cnt = 1; cnt < sizeof(mb)-1; cnt++)
if (mb[cnt] == 0xff) { if (mb[cnt] == 0xff) {
*(u32 *)(pd+8*cnt+0) = 0; *(u32 *)(pd+8*cnt+0) = 0;
@ -1401,7 +1406,7 @@ static void DrawSpritesForced(unsigned char *sprited)
// Index + 0 : hhhhvvvv ----hhvv yyyyyyyy yyyyyyyy // v, h: vert./horiz. size // Index + 0 : hhhhvvvv ----hhvv yyyyyyyy yyyyyyyy // v, h: vert./horiz. size
// Index + 4 : xxxxxxxx xxxxxxxx pccvhnnn nnnnnnnn // x: x coord + 8 // Index + 4 : xxxxxxxx xxxxxxxx pccvhnnn nnnnnnnn // x: x coord + 8
static NOINLINE void ParseSprites(int max_lines) static NOINLINE void ParseSprites(int max_lines, int limit)
{ {
const struct PicoVideo *pvid=&Pico.video; const struct PicoVideo *pvid=&Pico.video;
const struct PicoEState *est=&Pico.est; const struct PicoEState *est=&Pico.est;
@ -1417,6 +1422,10 @@ static NOINLINE void ParseSprites(int max_lines)
if (max_lines > rendlines-1) if (max_lines > rendlines-1)
max_lines = rendlines-1; max_lines = rendlines-1;
// look-ahead SAT parsing for next line and sprite pixel fetching for current
// line are limited if display was disabled during HBLANK before current line
if (limit) limit = 16; // max sprites/pixels processed
if (!(Pico.video.reg[12]&1)) if (!(Pico.video.reg[12]&1))
max_sprites = 64, max_line_sprites = 16, max_width = 264; max_sprites = 64, max_line_sprites = 16, max_width = 264;
if (*est->PicoOpt & POPT_DIS_SPRITE_LIM) if (*est->PicoOpt & POPT_DIS_SPRITE_LIM)
@ -1453,6 +1462,8 @@ static NOINLINE void ParseSprites(int max_lines)
if (sy <= max_lines && sy + (height<<3) >= first_line) // sprite onscreen (y)? if (sy <= max_lines && sy + (height<<3) >= first_line) // sprite onscreen (y)?
{ {
int entry, y, w, sx_min, onscr_x, maybe_op = 0; int entry, y, w, sx_min, onscr_x, maybe_op = 0;
// omit look-ahead line if sprite parsing limit reached
int last_line = (limit && u >= 2*limit ? max_lines-1 : max_lines);
sx_min = 8-(width<<3); sx_min = 8-(width<<3);
onscr_x = sx_min < sx && sx < max_width; onscr_x = sx_min < sx && sx < max_width;
@ -1461,13 +1472,15 @@ static NOINLINE void ParseSprites(int max_lines)
entry = ((pd - HighPreSpr) / 2) | ((code2>>8)&0x80); entry = ((pd - HighPreSpr) / 2) | ((code2>>8)&0x80);
y = (sy >= first_line) ? sy : first_line; y = (sy >= first_line) ? sy : first_line;
for (; y < sy + (height<<3) && y <= max_lines; y++) for (; y < sy + (height<<3) && y <= last_line; y++)
{ {
unsigned char *p = &HighLnSpr[y][0]; unsigned char *p = &HighLnSpr[y][0];
int cnt = p[0]; int cnt = p[0];
if (p[3] >= max_line_sprites) continue; // sprite limit?
if (p[1] & SPRL_MASKED) continue; // masked? if (p[1] & SPRL_MASKED) continue; // masked?
if (p[3] >= max_line_sprites) continue; // sprite limit?
p[3] ++;
w = width; w = width;
if (p[2] + width > max_line_sprites*2) { // tile limit? if (p[2] + width > max_line_sprites*2) { // tile limit?
if (y+1 < 240) HighLnSpr[y+1][1] |= SPRL_TILE_OVFL; if (y+1 < 240) HighLnSpr[y+1][1] |= SPRL_TILE_OVFL;
@ -1475,7 +1488,6 @@ static NOINLINE void ParseSprites(int max_lines)
w = max_line_sprites*2 - p[2]; w = max_line_sprites*2 - p[2];
} }
p[2] += w; p[2] += w;
p[3] ++;
if (sx == -0x78) { if (sx == -0x78) {
if (p[1] & (SPRL_HAVE_X|SPRL_TILE_OVFL)) if (p[1] & (SPRL_HAVE_X|SPRL_TILE_OVFL))
@ -1487,13 +1499,15 @@ static NOINLINE void ParseSprites(int max_lines)
if (!onscr_x) continue; // offscreen x if (!onscr_x) continue; // offscreen x
p[4+cnt] = entry; // sprite is (partly) visible, store info for renderer
p[5+cnt] = w; // width clipped by tile limit for sprite renderer
p[0] = cnt + 1;
p[1] |= (entry & 0x80) ? SPRL_HAVE_HI : SPRL_HAVE_LO; p[1] |= (entry & 0x80) ? SPRL_HAVE_HI : SPRL_HAVE_LO;
p[1] |= maybe_op; // there might be op sprites on this line p[1] |= maybe_op; // there might be op sprites on this line
if (cnt > 0 && (code2 & 0x8000) && !(p[4+cnt-1]&0x80)) if (cnt > 0 && (code2 & 0x8000) && !(p[4+cnt-1]&0x80))
p[1] |= SPRL_LO_ABOVE_HI; p[1] |= SPRL_LO_ABOVE_HI;
p[4+cnt] = entry;
p[5+cnt] = w; // width clipped by tile limit for sprite renderer
p[0] = cnt + 1;
} }
} }
@ -1506,6 +1520,23 @@ static NOINLINE void ParseSprites(int max_lines)
} }
*pd = 0; *pd = 0;
// fetching sprite pixels isn't done while display is disabled during HBLANK
if (limit) {
int w = 0;
unsigned char *sprited = &HighLnSpr[max_lines-1][0]; // current render line
for (u = 0; u < sprited[0]; u++) {
s32 *sp = HighPreSpr + (sprited[4+u] & 0x7f) * 2;
int sw = sp[0] >> 28;
if (w + sw > limit) {
sprited[0] = u;
sprited[4+u] = limit-w;
break;
}
w += sw;
}
}
#if 0 #if 0
for (u = first_line; u <= max_lines; u++) for (u = first_line; u <= max_lines; u++)
{ {
@ -1528,7 +1559,7 @@ static void DrawAllSprites(unsigned char *sprited, int prio, int sh,
unsigned char *p; unsigned char *p;
int cnt, w; int cnt, w;
cnt = sprited[0] & 0x7f; cnt = sprited[0];
if (cnt == 0) return; if (cnt == 0) return;
p = &sprited[4]; p = &sprited[4];
@ -1974,7 +2005,7 @@ static void PicoLine(int line, int offs, int sh, int bgc)
Pico.est.DrawLineDest = (char *)Pico.est.DrawLineDest + DrawLineDestIncrement; Pico.est.DrawLineDest = (char *)Pico.est.DrawLineDest + DrawLineDestIncrement;
} }
void PicoDrawSync(int to, int blank_last_line) void PicoDrawSync(int to, int blank_last_line, int limit_sprites)
{ {
struct PicoEState *est = &Pico.est; struct PicoEState *est = &Pico.est;
int line, offs = 0; int line, offs = 0;
@ -1990,7 +2021,7 @@ void PicoDrawSync(int to, int blank_last_line)
} }
if (est->DrawScanline <= to && (est->rendstatus & if (est->DrawScanline <= to && (est->rendstatus &
(PDRAW_SPRITES_MOVED|PDRAW_DIRTY_SPRITES|PDRAW_PARSE_SPRITES))) (PDRAW_SPRITES_MOVED|PDRAW_DIRTY_SPRITES|PDRAW_PARSE_SPRITES)))
ParseSprites(to + 1); ParseSprites(to + 1, limit_sprites);
for (line = est->DrawScanline; line < to; line++) for (line = est->DrawScanline; line < to; line++)
PicoLine(line, offs, sh, bgc); PicoLine(line, offs, sh, bgc);

View file

@ -286,7 +286,7 @@ void PicoFrameDrawOnly(void)
{ {
if (!(PicoIn.AHW & PAHW_SMS)) { if (!(PicoIn.AHW & PAHW_SMS)) {
PicoFrameStart(); PicoFrameStart();
PicoDrawSync(Pico.m.pal?239:223, 0); PicoDrawSync(Pico.m.pal?239:223, 0, 0);
} else { } else {
PicoFrameDrawOnlyMS(); PicoFrameDrawOnlyMS();
} }

View file

@ -167,7 +167,7 @@ static int PicoFrameHints(void)
if (!skip) if (!skip)
{ {
if (Pico.est.DrawScanline < y) if (Pico.est.DrawScanline < y)
PicoDrawSync(y - 1, 0); PicoDrawSync(y - 1, 0, 0);
#ifdef DRAW_FINISH_FUNC #ifdef DRAW_FINISH_FUNC
DRAW_FINISH_FUNC(); DRAW_FINISH_FUNC();
#endif #endif

View file

@ -692,7 +692,7 @@ int CM_compareRun(int cyc, int is_sub);
// draw.c // draw.c
void PicoDrawInit(void); void PicoDrawInit(void);
PICO_INTERNAL void PicoFrameStart(void); PICO_INTERNAL void PicoFrameStart(void);
void PicoDrawSync(int to, int blank_last_line); void PicoDrawSync(int to, int blank_last_line, int limit_sprites);
void BackFill(int reg7, int sh, struct PicoEState *est); void BackFill(int reg7, int sh, struct PicoEState *est);
void FinalizeLine555(int sh, int line, struct PicoEState *est); void FinalizeLine555(int sh, int line, struct PicoEState *est);
void FinalizeLine8bit(int sh, int line, struct PicoEState *est); void FinalizeLine8bit(int sh, int line, struct PicoEState *est);

View file

@ -139,6 +139,7 @@ void PicoVideoInit(void)
static int blankline; // display disabled for this line static int blankline; // display disabled for this line
static int limitsprites;
u32 SATaddr, SATmask; // VRAM addr of sprite attribute table u32 SATaddr, SATmask; // VRAM addr of sprite attribute table
@ -793,6 +794,11 @@ static NOINLINE void CommandChange(struct PicoVideo *pvid)
// VDP interface // VDP interface
static inline int InHblank(int offs)
{
return SekCyclesDone() - Pico.t.m68c_line_start <= 488-offs;
}
static void DrawSync(int skip) static void DrawSync(int skip)
{ {
int lines = Pico.video.reg[1]&0x08 ? 240 : 224; int lines = Pico.video.reg[1]&0x08 ? 240 : 224;
@ -802,10 +808,12 @@ static void DrawSync(int skip)
!PicoIn.skipFrame && Pico.est.DrawScanline <= last) { !PicoIn.skipFrame && Pico.est.DrawScanline <= last) {
//elprintf(EL_ANOMALY, "sync"); //elprintf(EL_ANOMALY, "sync");
if (blankline >= 0 && blankline < last) { if (blankline >= 0 && blankline < last) {
PicoDrawSync(blankline, 1); PicoDrawSync(blankline, 1, 0);
blankline = -1; blankline = -1;
} }
PicoDrawSync(last, 0); PicoDrawSync(last, 0, last == limitsprites);
if (last >= limitsprites)
limitsprites = -1;
} }
} }
@ -820,18 +828,18 @@ PICO_INTERNAL_ASM void PicoVideoWrite(u32 a,unsigned short d)
switch (a) switch (a)
{ {
case 0x00: // Data port 0 or 2 case 0x00: // Data port 0 or 2
// try avoiding the sync..
if (Pico.m.scanline < (pvid->reg[1]&0x08 ? 240 : 224) && (pvid->reg[1]&0x40) &&
!(!pvid->pending && ((pvid->command & 0xc00000f0) == 0x40000010 &&
PicoMem.vsram[(pvid->addr>>1) & 0x3f] == (d & 0x7ff)))
)
DrawSync(0); // XXX it's unclear when vscroll data is fetched from vsram?
if (pvid->pending) { if (pvid->pending) {
CommandChange(pvid); CommandChange(pvid);
pvid->pending=0; pvid->pending=0;
} }
// try avoiding the sync. can't easily do this for VRAM writes since they
// might update the SAT cache
if (Pico.m.scanline < (pvid->reg[1]&0x08 ? 240 : 224) && (pvid->reg[1]&0x40) &&
!(pvid->type == 3 && PicoMem.cram[(pvid->addr>>1) & 0x3f] == (d & 0xeee)) &&
!(pvid->type == 5 && PicoMem.vsram[(pvid->addr>>1) & 0x3f] == (d & 0x7ff)))
DrawSync(InHblank(440)); // experimentally, Overdrive 2
if (!(PicoIn.opt&POPT_DIS_VDP_FIFO)) if (!(PicoIn.opt&POPT_DIS_VDP_FIFO))
{ {
VdpFIFO.fifo_data[++VdpFIFO.fifo_dx&3] = d; VdpFIFO.fifo_data[++VdpFIFO.fifo_dx&3] = d;
@ -862,7 +870,7 @@ PICO_INTERNAL_ASM void PicoVideoWrite(u32 a,unsigned short d)
CommandChange(pvid); CommandChange(pvid);
// Check for dma: // Check for dma:
if (d & 0x80) { if (d & 0x80) {
DrawSync(SekCyclesDone() - Pico.t.m68c_line_start <= 488-390); DrawSync(InHblank(390));
CommandDma(); CommandDma();
} }
} }
@ -884,12 +892,17 @@ PICO_INTERNAL_ASM void PicoVideoWrite(u32 a,unsigned short d)
if (num == 1 && ((pvid->reg[1]^d)&0x40)) { if (num == 1 && ((pvid->reg[1]^d)&0x40)) {
PicoVideoFIFOMode(d & 0x40, pvid->reg[12]&1); PicoVideoFIFOMode(d & 0x40, pvid->reg[12]&1);
// handle line blanking before line rendering // handle line blanking before line rendering
if (SekCyclesDone() - Pico.t.m68c_line_start <= 488-390) if (InHblank(390)) {
blankline = d&0x40 ? -1 : Pico.m.scanline; // sprite rendering is limited if display is disabled and reenabled
// in HBLANK of the same scanline (Overdrive)
limitsprites = (d&0x40) && blankline == Pico.m.scanline ? Pico.m.scanline : -1;
blankline = (d&0x40) ? -1 : Pico.m.scanline;
}
} }
if (num == 12 && ((pvid->reg[12]^d)&0x01)) if (num == 12 && ((pvid->reg[12]^d)&0x01))
PicoVideoFIFOMode(pvid->reg[1]&0x40, d & 1); PicoVideoFIFOMode(pvid->reg[1]&0x40, d & 1);
DrawSync(SekCyclesDone() - Pico.t.m68c_line_start <= 488-390); if (num <= 18) // no sync needed for DMA setup registers
DrawSync(InHblank(390));
d &= 0xff; d &= 0xff;
pvid->reg[num]=(unsigned char)d; pvid->reg[num]=(unsigned char)d;
switch (num) switch (num)