spritesort reduces spriteflicker

Page 2/13
1 | | 3 | 4 | 5 | 6 | 7

By PingPong

Prophet (3459)

PingPong's picture

07-03-2010, 21:38

PingPong: That's one of those horrible pieces of design that more problems than it should. I guess they just expanded the sprite colour attribute and never thought about whether it was practical. It would have been very little trouble to make colours per sprite pattern or even make it selectable through the unused byte in the SAT. This is probably proof that the chip designers aren't normally doing much game programming.
I partially agree with you- the real problem is that the sprite subsystem should have been redesigned from start.
another thing that comes from the roots of the tms is the special y coordinate. a non sense even in TMS.

problem is compatibilty: where yamaha designers started without the issue of compatibility they worked well.
for sprites colors they have done pratically the same thing that apply between charaters colors in screen 1 and 2. Basically reading different vram location using the decoded scanline counter.

Even screen2, with the huge amount of vram used could had been more colorfulTongue

By ARTRAG

Enlighted (6275)

ARTRAG's picture

07-03-2010, 21:49

@ARTRAG
@hit9918
Good work, the idea gives good results, but seems very CPU expensive.

That is the problem. In readme I mention further tradeoffs. Also maybe
the whole thing can be rewritten to snip IX IY and stuff them to the
EXX registerset.

In my first version I did not dare to trade too much accuracy.
The trade towards full speed is one scanline buffer entry is for
16 scanlines. Then spritesort only needs to touch 2 cells with
the DEC (HL) OR (HL) INC L cascade.


The idea is simple:

If the 5th sprite bit in S0 is set, its plane is in S0 too.
All you need to do is to move it (and all the seguent sprites) on top of the SAT.
In this way it (and some other sprite that was hidden in the previous frame) will appear in the next frame

rotation is "optimal" if the 5th sprite problem ocuurs only once
as S0 holds only the last occurrence of 5th sprite condition

Wow. Is this just a slight upgrade on the ringbuffer, or is it a trick that
makes spritesort unnessesary, I cannot imagine by now.

In first look, it looks like only a slight upgrade to the ringbuffer method.
On a second look however it does attack the problems where they are happening,
which was my goal.

It is not ideal, on the other hand it does attack the next problem right next frame
without the usual roundtrip time. I got to look into this.

My solution makes sorting unnecessary.
The right reordering is given by the VDP itself.

The source is this (to be placed in the ISR):

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
; SAT update
; sprite multiplexing
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

SAT  equ  0x1B00                         ; SAT in VRAM
MSAT equ  0xC000                         ; SAT in RAM



;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

	global _vdps0

	global _copysat

_copysat:
	ld	a,SAT & 255
	out	(099h),a
	ld	a,SAT/256 + 040h
	out	(099h),a

	ld    c,098h
	ld    a,(_vdps0)
	bit   6,a
	jr    z,no5th

	and   31
	add   a,a
	add   a,a
CURR_PLAN:      		; self modifing code (!!! warning !!!)
	add   a,0
	ld    e,a	  		; curr_plan + = (vdps0 & 31)*4; 5->20

		; sprt is the 32*4=128 copy of the SPRATR in RAM,
		; n_sprt holds the number of valid bytes in sprt,
		; curr_plan is the VDP sprite plan in sprt that is currently mapped on plane 0 (max priority)

	ld    a,(_NNPC)     ; nbytes	to be copied from the MSAT to SAT in VRAM		
	ld    b,a

	ex	af,af'
	ld	a,e

1:              
	sub    b
	jr     nc,1b
	add    a,b					;   in A rest of E / B

	jr    z,no5th

	ld    (CURR_PLAN+1),a      ; curr_plan -= (curr_plan/(n_sprt)) * (n_sprt);
	ld    e,a
	ld    d,0

	ld    hl,MSAT
	add   hl,de			; hl = sprt + curr_plan

	ex	af,af'		   	; ld    a,nbytes
	sub   e
	ld    b,a	  		;  b = n_sprt -curr_plan
	otir

	ld    b,e	  		;  b = curr_plan
	jr    1f
no5th:
	xor   a
	ld   (CURR_PLAN+1),a

	ld    a,(_NNPC)     ; nbytes	to be copied from the MSAT to VRAM		
	ld    b,a

1:              
	ld    hl,MSAT
	otir

	ld    a,208			; sprite delimiter
	out   (98h),a

	ret

By PingPong

Prophet (3459)

PingPong's picture

07-03-2010, 21:57


interesting, would be nice to have the same for msx2. however there is a problem. swapping sprites priority causes also the need to move sprite attribute table for colors. each sprite is 16 bytes long. so this solution does not work so well on msx2.

this sounds like the MSX2 sprite color table is not adressed same way as sprite pattern
table, by 4th byte in sprite attribute table?

First: msx2 allows 8 sprites on scanline. msx2 sprites are like charathers patterns in screen 2, only one foreground color per scanline.

Second:
normal tms takes the color calculating the priority *4 and adding an offset where the color byte is stored.
msx2 vdp does this calculation as the tms with one difference:
vramaddress = 4*plane + (currentscanlinecounter-y spritepos)-512+SAT_BASE_ADDR.
So a color different for each scanline. like patterns in screen2. this implies 16 bytes X plane stored 512 bytes before the SAT is located. the old SAT color attribute byte is unused.

time ago i've realized a similar routine for msx2.

Basically: i divide the screen into 12 bands, (each of 16pixels for 192pixels vertically)
each band stores the number of sprites so a sprite having y=10 is on two bands, (band 0 & 1)
when the vdp signals overflow, i process every band that is in overflow, setting offscreen every of the 8 sprites in the band. Others sprites will therefore be visible on the next frame. process continues until the vdp does not complain.
After this i turn on all the sprites. Then the cycle restart.

For example if one have two rows of 16 sprites aligned on 2 different bands, i can with only two frames, do the entire multiplex process.

routine is a little less expensive (if i remember correctly) than yours, but more than a single priority cycle.
please note: i do not move the sprite priority, only i hidden the displayed sprites to allow hidden to be displayed.

see the example:

Frame 0

band0, sprites planes:
0 2 3 4 5 6 7 8 9 10 (last three hidden, vdp overflow)
band5, sprites planes:
20 21 22 23 24 25 26 27 28 29 (last two hidden, vdp overflow)

Frame 1

band0, sprites planes:
8 9 10
band5, sprites planes:
28 29

Frame 2: like frame 0

By hit9918

Prophet (2868)

hit9918's picture

08-03-2010, 21:40

@ARTRAG

My solution makes sorting unnecessary.
The right reordering is given by the VDP itself.

I got major problems! I put a version on jf.peer.name/msx/spritesort_5th.zip which does
rotate to the 5th given by VDP (note the different url. again press cursorkey
for non-spritesort method).

If you press cursor key, then sometimes it gets in a state where
some sprites disappear FOREVER!

Release key and press again to find such states (spritesort does different moves,
which is why release and press again gets into a different state).

VDP questions:

the smaller problem was that the bios reading 99h seems to reset the VDP info.
so I peek 99h in a 0FD9Ah handler.

question: does the VDP set the first 5th it gets at or does the register contain
the most recent 5th sprite ?

Well what does "recent" actually mean?

I see 4 possibilities:
#1 register contains 5th sprite that happened on the scanline latest in TIME.
#2 register contains 5th sprite that happened on the earliest scanline in TIME.
#3 register contains the highest PRIORITY 5th sprite.
#4 register contains the lowest PRIOTIRY 5th sprite.

I think that method #3 would be the easy kick ass "just rotate to 5th sprites" method.
But it looks like VDP is doing #1 or #2. And that is a major showstopper:

imagine 2 5 sprite rows on top of the screen.
the 5th-rotator-code will toggle between those 2.
it does not matter where in the spritelist they are, all that matters is that
they are first on a scanline, i.e. top of screen.

And the sprites more below on the screen flicker too. BUT only as a
side effect, and their situation never is an input to the code:
so there are situations where a sprite does disappear forever.
forever means: the condition might last easily for multiple
seconds if the enemies above walk horizontal.

I get disappeared sprites states whenever I work with VDP reister
as a guide, no matter what I am trying. no mather whether I just
rotate to it, or whether I put 5th sprite on top of the list, or at
bottom - always lost sprite states are possible.

This is why I think VDP uses method #1 or #2. This method does
not need an comparison op "if 5th sprite found is less PRIORITY than
the one in status register, then do not change status register".

By ARTRAG

Enlighted (6275)

ARTRAG's picture

08-03-2010, 22:31

@ARTRAG

My solution makes sorting unnecessary.
The right reordering is given by the VDP itself.

[...]
imagine 2 5 sprite rows on top of the screen.
the 5th-rotator-code will toggle between those 2.
it does not matter where in the spritelist they are, all that matters is that
they are first on a scanline, i.e. top of screen.
[...]

As I told you, the method works only when the 5th sprite condition occurs once

when the 5th sprite condition occurs twice, the reordering is optimal only for the second row of sprites

By ARTRAG

Enlighted (6275)

ARTRAG's picture

08-03-2010, 22:36

@ARTRAG

My solution makes sorting unnecessary.
The right reordering is given by the VDP itself.

[...]
question: does the VDP set the first 5th it gets at or does the register contain
the most recent 5th sprite ?

Well what does "recent" actually mean?

I see 4 possibilities:
#1 register contains 5th sprite that happened on the scanline latest in TIME.
#2 register contains 5th sprite that happened on the earliest scanline in TIME.
#3 register contains the highest PRIORITY 5th sprite.
#4 register contains the lowest PRIOTIRY 5th sprite.

I think that method #3 would be the easy kick ass "just rotate to 5th sprites" method.
But it looks like VDP is doing #1 or #2. And that is a major showstopper:

[...]

IMHO S#0 returns always the highest priority 5th sprite in the last row where 5th sprite condition occured.

I did some tests on real HW and on emulators few years ago when I developed the method
But naturally I cannot be sure of real HW: indipendent tests are wellcome

By hit9918

Prophet (2868)

hit9918's picture

08-03-2010, 22:46

@PingPong

time ago i've realized a similar routine for msx2.
Basically: i divide the screen into 12 bands, (each of 16pixels for 192pixels vertically)
[...]
please note: i do not move the sprite priority, only i hidden the displayed sprites to allow hidden to be displayed.
see the example:
Frame 0
band0, sprites planes:
0 2 3 4 5 6 7 8 9 10 (last three hidden, vdp overflow)
[...]

The band structure sounds very similar to spritesort scanline cells?
a mix of VDP 5th sprite hinting and calculating 5th sprites in software?

It is unclear to me how you find out the sprites to turn off,
because "0 1 2 3 4 5" examples are more like local priority examples,
but a real life situation would be "5 29 15 7 13 ....".
And this is not present as a list in memory, but

- what VDP draws onscreen
- maybe this list of sprites has touched a scanline array

but how do you get this list? and how you sort out the lowest
8 sprites? how do you get speed?

By PingPong

Prophet (3459)

PingPong's picture

09-03-2010, 13:42

@PingPong

time ago i've realized a similar routine for msx2.
Basically: i divide the screen into 12 bands, (each of 16pixels for 192pixels vertically)
[...]
please note: i do not move the sprite priority, only i hidden the displayed sprites to allow hidden to be displayed.
see the example:
Frame 0
band0, sprites planes:
0 2 3 4 5 6 7 8 9 10 (last three hidden, vdp overflow)
[...]

The band structure sounds very similar to spritesort scanline cells?
a mix of VDP 5th sprite hinting and calculating 5th sprites in software?

It is unclear to me how you find out the sprites to turn off,
because "0 1 2 3 4 5" examples are more like local priority examples,
but a real life situation would be "5 29 15 7 13 ....".
And this is not present as a list in memory, but

- what VDP draws onscreen
- maybe this list of sprites has touched a scanline array

but how do you get this list? and how you sort out the lowest
8 sprites? how do you get speed?

band structure is a list (if i remember correctly) of sprite planes in every band. there is also a bnd counter, that store the number of sprites on each band.
I do not sort, i get it sorted when i build the list every vblank, when vdp signal 'overflow'. next, i start a full cycle of sprite removing.
for each band, i remove the first 8 sprites with lower priority.

if i found the code i will post it.

By PingPong

Prophet (3459)

PingPong's picture

09-03-2010, 14:38

is something similar to this:

void addsprite(uchar* spritelistptr, uchar spriteplane)
{
// add the sprite to spritelist
}

// executed a VBLANK if vdp signals overflow
void resetBands()
{
// 
	for (i=0;i<32;i++)
	{
		bands[sat[i].y/16].nspr=0; // no sprites on this band
		
	}

}


// executed a VBLANK if vdp signals overflow
void updateBands()
{

	for (i=0;i<32;i++)
	{
	// add a sprite (16x16) to the bands[] indexes as sprite y coord / 16
		bands[sat[i].y/16].nspr++;
		addsprite(bands[sat[i].y/16].spritelistptr,i); // spritelistptr is the list ptr were sprites are stored.
	}
}


the removing sprite part is missing. but it's easy to understand what it does.
Please note that for the limitations of msx2 vdp I DO NOT DO sprite plane rotation. i only remove a high priority sprite by placing offscreen

By ARTRAG

Enlighted (6275)

ARTRAG's picture

09-03-2010, 15:03

In a recent msx2 project I tried to do sprite rotation (I mean mine, using the 9th sprite info from the vdp) using the copy engine in the VDP to move color definitions.

In I/O terms, one vdp command costs generally 15 bytes while the color definition is 16 bytes (:-P) but in my case the idea works as I use two sprite layer per object.

In this way the cost of issuing the command is lower than the cost of coping the data (thus 32 bytes of color definition) with the cpu.

Page 2/13
1 | | 3 | 4 | 5 | 6 | 7