mirror of
https://github.com/RaySollium99/picodrive.git
synced 2025-09-05 15:27:46 -04:00
core vdp, fix sprite rendering issues with Overdrive 1/2
This commit is contained in:
parent
c7e1c39b28
commit
d515a352b3
5 changed files with 76 additions and 32 deletions
63
pico/draw.c
63
pico/draw.c
|
@ -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_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 rendlines;
|
||||
|
@ -290,7 +292,8 @@ TileFlipMaker(TileFlip_and, pix_and)
|
|||
pal |= 0xc0; /* leave s/h bits untouched in pixel "and" */ \
|
||||
if (likely(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)
|
||||
|
@ -977,7 +980,7 @@ static void DrawSpritesSHi(unsigned char *sprited, const struct PicoEState *est)
|
|||
unsigned char *p;
|
||||
int cnt, w;
|
||||
|
||||
cnt = sprited[0] & 0x7f;
|
||||
cnt = sprited[0];
|
||||
if (cnt == 0) return;
|
||||
|
||||
p = &sprited[4];
|
||||
|
@ -1046,7 +1049,7 @@ static void DrawSpritesHiAS(unsigned char *sprited, int sh)
|
|||
unsigned m;
|
||||
int entry, cnt;
|
||||
|
||||
cnt = sprited[0] & 0x7f;
|
||||
cnt = sprited[0];
|
||||
if (cnt == 0) return;
|
||||
|
||||
memset(mb, 0xff, sizeof(mb));
|
||||
|
@ -1323,7 +1326,7 @@ static void DrawSpritesForced(unsigned char *sprited)
|
|||
unsigned m;
|
||||
int entry, cnt;
|
||||
|
||||
cnt = sprited[0] & 0x7f;
|
||||
cnt = sprited[0];
|
||||
if (cnt == 0) { memset(pd, 0, sizeof(DefHighCol)); return; }
|
||||
|
||||
memset(mb, 0xff, sizeof(mb));
|
||||
|
@ -1382,7 +1385,9 @@ static void DrawSpritesForced(unsigned char *sprited)
|
|||
*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++)
|
||||
if (mb[cnt] == 0xff) {
|
||||
*(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 + 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 PicoEState *est=&Pico.est;
|
||||
|
@ -1417,6 +1422,10 @@ static NOINLINE void ParseSprites(int max_lines)
|
|||
if (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))
|
||||
max_sprites = 64, max_line_sprites = 16, max_width = 264;
|
||||
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)?
|
||||
{
|
||||
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);
|
||||
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);
|
||||
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];
|
||||
int cnt = p[0];
|
||||
if (p[3] >= max_line_sprites) continue; // sprite limit?
|
||||
if (p[1] & SPRL_MASKED) continue; // masked?
|
||||
|
||||
if (p[3] >= max_line_sprites) continue; // sprite limit?
|
||||
p[3] ++;
|
||||
|
||||
w = width;
|
||||
if (p[2] + width > max_line_sprites*2) { // tile limit?
|
||||
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];
|
||||
}
|
||||
p[2] += w;
|
||||
p[3] ++;
|
||||
|
||||
if (sx == -0x78) {
|
||||
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
|
||||
|
||||
p[4+cnt] = entry;
|
||||
p[5+cnt] = w; // width clipped by tile limit for sprite renderer
|
||||
p[0] = cnt + 1;
|
||||
// sprite is (partly) visible, store info for renderer
|
||||
p[1] |= (entry & 0x80) ? SPRL_HAVE_HI : SPRL_HAVE_LO;
|
||||
p[1] |= maybe_op; // there might be op sprites on this line
|
||||
if (cnt > 0 && (code2 & 0x8000) && !(p[4+cnt-1]&0x80))
|
||||
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;
|
||||
|
||||
// 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
|
||||
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;
|
||||
int cnt, w;
|
||||
|
||||
cnt = sprited[0] & 0x7f;
|
||||
cnt = sprited[0];
|
||||
if (cnt == 0) return;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
void PicoDrawSync(int to, int blank_last_line)
|
||||
void PicoDrawSync(int to, int blank_last_line, int limit_sprites)
|
||||
{
|
||||
struct PicoEState *est = &Pico.est;
|
||||
int line, offs = 0;
|
||||
|
@ -1990,7 +2021,7 @@ void PicoDrawSync(int to, int blank_last_line)
|
|||
}
|
||||
if (est->DrawScanline <= to && (est->rendstatus &
|
||||
(PDRAW_SPRITES_MOVED|PDRAW_DIRTY_SPRITES|PDRAW_PARSE_SPRITES)))
|
||||
ParseSprites(to + 1);
|
||||
ParseSprites(to + 1, limit_sprites);
|
||||
|
||||
for (line = est->DrawScanline; line < to; line++)
|
||||
PicoLine(line, offs, sh, bgc);
|
||||
|
|
|
@ -286,7 +286,7 @@ void PicoFrameDrawOnly(void)
|
|||
{
|
||||
if (!(PicoIn.AHW & PAHW_SMS)) {
|
||||
PicoFrameStart();
|
||||
PicoDrawSync(Pico.m.pal?239:223, 0);
|
||||
PicoDrawSync(Pico.m.pal?239:223, 0, 0);
|
||||
} else {
|
||||
PicoFrameDrawOnlyMS();
|
||||
}
|
||||
|
|
|
@ -167,7 +167,7 @@ static int PicoFrameHints(void)
|
|||
if (!skip)
|
||||
{
|
||||
if (Pico.est.DrawScanline < y)
|
||||
PicoDrawSync(y - 1, 0);
|
||||
PicoDrawSync(y - 1, 0, 0);
|
||||
#ifdef DRAW_FINISH_FUNC
|
||||
DRAW_FINISH_FUNC();
|
||||
#endif
|
||||
|
|
|
@ -692,7 +692,7 @@ int CM_compareRun(int cyc, int is_sub);
|
|||
// draw.c
|
||||
void PicoDrawInit(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 FinalizeLine555(int sh, int line, struct PicoEState *est);
|
||||
void FinalizeLine8bit(int sh, int line, struct PicoEState *est);
|
||||
|
|
|
@ -139,6 +139,7 @@ void PicoVideoInit(void)
|
|||
|
||||
|
||||
static int blankline; // display disabled for this line
|
||||
static int limitsprites;
|
||||
|
||||
u32 SATaddr, SATmask; // VRAM addr of sprite attribute table
|
||||
|
||||
|
@ -793,6 +794,11 @@ static NOINLINE void CommandChange(struct PicoVideo *pvid)
|
|||
|
||||
// VDP interface
|
||||
|
||||
static inline int InHblank(int offs)
|
||||
{
|
||||
return SekCyclesDone() - Pico.t.m68c_line_start <= 488-offs;
|
||||
}
|
||||
|
||||
static void DrawSync(int skip)
|
||||
{
|
||||
int lines = Pico.video.reg[1]&0x08 ? 240 : 224;
|
||||
|
@ -802,10 +808,12 @@ static void DrawSync(int skip)
|
|||
!PicoIn.skipFrame && Pico.est.DrawScanline <= last) {
|
||||
//elprintf(EL_ANOMALY, "sync");
|
||||
if (blankline >= 0 && blankline < last) {
|
||||
PicoDrawSync(blankline, 1);
|
||||
PicoDrawSync(blankline, 1, 0);
|
||||
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)
|
||||
{
|
||||
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) {
|
||||
CommandChange(pvid);
|
||||
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))
|
||||
{
|
||||
VdpFIFO.fifo_data[++VdpFIFO.fifo_dx&3] = d;
|
||||
|
@ -862,7 +870,7 @@ PICO_INTERNAL_ASM void PicoVideoWrite(u32 a,unsigned short d)
|
|||
CommandChange(pvid);
|
||||
// Check for dma:
|
||||
if (d & 0x80) {
|
||||
DrawSync(SekCyclesDone() - Pico.t.m68c_line_start <= 488-390);
|
||||
DrawSync(InHblank(390));
|
||||
CommandDma();
|
||||
}
|
||||
}
|
||||
|
@ -884,12 +892,17 @@ PICO_INTERNAL_ASM void PicoVideoWrite(u32 a,unsigned short d)
|
|||
if (num == 1 && ((pvid->reg[1]^d)&0x40)) {
|
||||
PicoVideoFIFOMode(d & 0x40, pvid->reg[12]&1);
|
||||
// handle line blanking before line rendering
|
||||
if (SekCyclesDone() - Pico.t.m68c_line_start <= 488-390)
|
||||
blankline = d&0x40 ? -1 : Pico.m.scanline;
|
||||
if (InHblank(390)) {
|
||||
// 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))
|
||||
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;
|
||||
pvid->reg[num]=(unsigned char)d;
|
||||
switch (num)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue