04 March 2016, 15:12 | #1 |
Lemon. / Core Design
Join Date: Mar 2016
Location: Tier 5
Posts: 1,211
|
Fast way of converting screen coordinates to HW Sprite control words
So I found this code (by Leffman) in another thread.. it's a nice way to do it, but was concerned about the cycle count considering there are a couple of 8 bit shifts in there..
Code:
move.w #50, d0 ; x position move.w #100, d1 ; y position move.w #20, d2 ; height add.w #$80, d0 add.w #$2c, d1 add.w d1, d2 clr.b d3 lsl.w #8, d1 ; move vertical start bit 8 in place addx.b d3, d3 lsl.w #8, d2 ; vertical stop bit 8 addx.b d3, d3 lsr.w #1, d0 ; horizontal start bit 0 addx.b d3, d3 move.b d0, d1 ; make first control word move.b d3, d2 ; second control word Code:
; d0 = Screen X , d1 = Screen Y lea HW_SpriteXTable,a0 lea HW_SpriteYTable,a1 add.w d0,d0 add.w d0,d0 move.l (a0,d0.w),d0 add.w d1,d1 add.w d1,d1 or.l (a1,d1.w),d0 anyone else ever addressed this issue before? Last edited by DanScott; 04 March 2016 at 16:06. |
04 March 2016, 18:52 | #2 |
Join Date: Jul 2008
Location: Sweden
Posts: 2,269
|
Why, yes, I have actually!! Something like this maybe:
Code:
XTable rept 320 x set REPTN+$80 dc.b 0, x>>1, 0, x&1 endr YTable rept 256 ys set REPTN+$2c ye set ys+8 dc.b ys&255, 0, ye&255, ((ys>>6)&%100) | ((ye>>7)&%10) endr |
05 March 2016, 10:35 | #3 |
Lemon. / Core Design
Join Date: Mar 2016
Location: Tier 5
Posts: 1,211
|
Yes Exactly like this.
I was actually creating the tables in code...silly me... |
05 March 2016, 12:11 | #4 | |
68k
Join Date: Sep 2005
Location: Somewhere
Posts: 828
|
Quote:
Code:
lea HW_SpriteXTable(pc),a0 add.w d0,d0 add.w d0,d0 move.l (a0,d0.w),d0 add.w d1,d1 add.w d1,d1 swap d1 or.l (a0,d1.l),d0 HW_SpriteXTable: ; offset 0 ;... ;end of table ;$1000-$500 free of data storage ;offset $10000 ;here starts table y Last edited by Asman; 05 March 2016 at 13:01. Reason: added missing (pc) |
|
05 March 2016, 14:41 | #5 |
Lemon. / Core Design
Join Date: Mar 2016
Location: Tier 5
Posts: 1,211
|
I think that "swap" is just going to put the offsets as $00000000,$00040000,$00080000 etc..
Actually, the lea of the tables will be done once outside the main sprite processing loop, so will be able to load into 2 registers.. Now the next thing.. how to maintain a list of sorted (in Y) sprites... Perhaps when a sprite moves, to move it up or down the list depending on if it moves past another sprite? I would guess that only 1 or 2 sprites would be inserted into the list at any given frame, so could insert them in the correct position. |
05 March 2016, 23:52 | #6 |
Moderator
Join Date: Nov 2004
Location: Eksjö / Sweden
Posts: 5,602
|
Consider making all sprites the same height (the height of the highest sprite for that channel[-pair]. Saves copying spritedata into the right place of the chain. I always thought that kind of double work was unattractive for Amiga. But ofc it also offers to set the control words after waits in the Copper, and to cover the spacing you can point to a single screen-high blank sprite. The DMA cost is lower than what basically equates to a bob routine to build the chain of sprites
|
06 March 2016, 02:16 | #7 | |
Join Date: Jul 2008
Location: Sweden
Posts: 2,269
|
Quote:
Bubble and Insertion are bad for general purpose sorting, but they are great for this because they come close to O(n) complexity when the list is almost sorted. |
|
06 March 2016, 10:32 | #8 | |
Lemon. / Core Design
Join Date: Mar 2016
Location: Tier 5
Posts: 1,211
|
Quote:
|
|
06 March 2016, 19:37 | #9 |
Join Date: Jul 2008
Location: Sweden
Posts: 2,269
|
I have a working example here now, it multiplexes 50 small objects across 4 sprites, with about 1 object dropped per frame.
The sprites are just "chains" of the same object repeated vertically, and after sorting I assign the objects from top to bottom to each sprite in turn. It needs about 2-3 milliseconds of time per frame on an A500, but the code can still be improved. Last edited by Leffmann; 27 March 2018 at 20:26. |
10 March 2016, 09:52 | #10 |
Lemon. / Core Design
Join Date: Mar 2016
Location: Tier 5
Posts: 1,211
|
That's looking great already
I'm currently investigating the process of sorting "already nearly sorted" lists fast My HW sprite routine that I am working on right now, is currently inserting new sprites at an already sorted position in the list. Then after all sprites are moved in their update, the list is still pretty much in sorted order... maybe only a very few sprites have actually change position relative to each other. This makes re-sorting the list very quick in one pass (even bubble sort would have to do a minimum of 2 passes if even only 2 sprites switch sorting order.. i think.. i may be wrong ) Also, as the sprite information will contain a lot more than just positions and velocities (flags, control info etc..), I am not swapping big chunks of data in the sort, but maintaining a sorted list of ptrs to the data Just have to swap longword pointers |
10 March 2016, 23:04 | #11 | |
68k
Join Date: Sep 2005
Location: Somewhere
Posts: 828
|
Quote:
1) How many objects you mean ? Max 100 ? 2) range of y position is between 0 and 255 ? Or it's rather uword value ? |
|
10 March 2016, 23:14 | #12 |
Lemon. / Core Design
Join Date: Mar 2016
Location: Tier 5
Posts: 1,211
|
Currently I have max of 32 sprites, distributed between 2 hardware sprites
of course, I can change my equate to MaxNumSprite equ 100 X & Y positions are signed word values in fixed point 12:4 32 is ample even for nearly bullet hell style |
11 March 2016, 00:16 | #13 |
Join Date: Jul 2008
Location: Sweden
Posts: 2,269
|
I played a bit with larger objects of different sizes and found that it's difficult to get away with, even when you multiplex over many hardware sprites. There's too much surface that blinks off the screen, and for too long.
In my example the objects are just bouncing off the walls randomly, to have something to try it with, but I think multipexing like this works much better for "bullet hell" where you typically have showers or explosions of small bullets that move away from eachother. |
15 March 2016, 11:49 | #14 |
Lemon. / Core Design
Join Date: Mar 2016
Location: Tier 5
Posts: 1,211
|
Back on the "plexing" and just did a simple bubble sort for now.. it's pretty efficient for nearly sorted lists.. bullets are generally moving down the screen, or towards the player, so not many will switch order that often
Here's the sort routine.. maybe someone can see some glaringy obvious optimisation that I have missed (I am not sure an insertion sort would be any quicker, as the whole list will need iterating ,and then more checks up through the list for the insert.. perhaps there would be less swaps? don't know...) Code:
move.w Var_NumberOfSprites(a5),d6 ; Get number of sprites in list subq.w #2,d6 ; Subtract 2 for dbf value blt.s .NoSort ; No sorting required if number of sprites is 1 or less lea Var_SpritePointerList(a5),a3 ; Get pointer to start of our sprite ptr list .SortLoopOuter move.l a3,a0 ; a0 = start of sprite ptr list moveq #0,d1 ; d1 = flag (will be set to 1 if a swap occured) move.w d6,d7 ; d7 = loop counter (number of sprites-2) .SortLoopInner move.l (a0)+,a1 ; Get address of sprite data 1 in a1 move.l (a0),a2 ; Get address of sprite data 2 in a2 move.w EB_YPos(a1),d0 ; Get Y position of sprite data 1 in d0 cmp.w EB_YPos(a2),d0 ; Compare Y position of sprite data 2 with Y position of sprite data 1 (in d0) ble.s .NoSwap ; Important!! Less than OR equal = No Swap.. otherwise swaps forever equal values.... move.l a2,-4(a0) ; Store sprite 2 pointer where sprite 1 pointer was stored move.l a1,(a0) ; Store sprite 1 pointer where sprite 2 pointer was stored moveq #1,d1 ; d1 = 1.. There has been a swap .NoSwap dbf d7,.SortLoopInner ; Loop through all sprite pointers tst.w d1 ; Check the d1 flag bne.s .SortLoopOuter ; If flag is not 0, there was a swap in the last pass, so need to pass through again .NoSort |
15 March 2016, 13:04 | #15 | |
Registered User
Join Date: Jan 2008
Location: Warsaw/Poland
Age: 55
Posts: 1,959
|
Quote:
Code:
move.w Var_NumberOfSprites(a5),d6 ; Get number of sprites in list subq.w #2,d6 ; Subtract 2 for dbf value blt.s .NoSort ; No sorting required if number of sprites is 1 or less lea Var_SpritePointerList(a5),a3 ; Get pointer to start of our sprite ptr list moveq #0,d1 ; d1 = flag (will be set to -128 if a swap occured) .SortLoopOuter move.l a3,a0 ; a0 = start of sprite ptr list move.w d6,d7 ; d7 = loop counter (number of sprites-2) .SortLoopInner move.l (a0)+,a1 ; Get address of sprite data 1 in a1 move.l (a0),a2 ; Get address of sprite data 2 in a2 move.w EB_YPos(a1),d0 ; Get Y position of sprite data 1 in d0 cmp.w EB_YPos(a2),d0 ; Compare Y position of sprite data 2 with Y position of sprite data 1 (in d0) ble.s .NoSwap ; Important!! Less than OR equal = No Swap.. otherwise swaps forever equal values.... move.l a2,-4(a0) ; Store sprite 2 pointer where sprite 1 pointer was stored move.l a1,(a0) ; Store sprite 1 pointer where sprite 2 pointer was stored moveq #-128,d1 ; d1 = -128.. There has been a swap .NoSwap dbf d7,.SortLoopInner ; Loop through all sprite pointers add.b d1,d1 ; Check the d1 flag bcs.s .SortLoopOuter ; If flag is not 0, there was a swap in the last pass, so need to pass through again .NoSort |
|
15 March 2016, 13:14 | #16 |
Lemon. / Core Design
Join Date: Mar 2016
Location: Tier 5
Posts: 1,211
|
thanks just saw it..
it is little things like this that I tend to miss |
15 March 2016, 13:40 | #17 |
68k
Join Date: Sep 2005
Location: Somewhere
Posts: 828
|
Other obvious optimization is when EB_YPos = 0.
I found another one: (based on Don_Adan post #15 - cheers Don ). Code:
move.w Var_NumberOfSprites(a5),d6 ; Get number of sprites in list subq.w #2,d6 ; Subtract 2 for dbf value blt.s .NoSort ; No sorting required if number of sprites is 1 or less lea Var_SpritePointerList(a5),a3 ; Get pointer to start of our sprite ptr list moveq #0,d1 ; d1 = flag (will be set to -128 if a swap occured) .SortLoopOuter move.l a3,a0 ; a0 = start of sprite ptr list move.w d6,d7 ; d7 = loop counter (number of sprites-2) .SortLoopInner move.l (a0)+,a1 ; Get address of sprite data 1 in a1 move.l (a0)+,a2 ; Get address of sprite data 2 in a2 move.w EB_YPos(a1),d0 ; Get Y position of sprite data 1 in d0 cmp.w EB_YPos(a2),d0 ; Compare Y position of sprite data 2 with Y position of sprite data 1 (in d0) ble.s .NoSwap ; Important!! Less than OR equal = No Swap.. otherwise swaps forever equal values.... move.l a1,-(a0) ; Store sprite 1 pointer where sprite 2 pointer was stored move.l a2,-(a0) ; Store sprite 2 pointer where sprite 1 pointer was stored moveq #-128,d1 ; d1 = -128.. There has been a swap .NoSwap dbf d7,.SortLoopInner ; Loop through all sprite pointers add.b d1,d1 ; Check the d1 flag bcs.s .SortLoopOuter ; If flag is not 0, there was a swap in the last pass, so need to pass through again Last edited by Asman; 15 March 2016 at 13:55. Reason: added information about on which version source is based |
15 March 2016, 13:51 | #18 |
Registered User
Join Date: Jan 2008
Location: Warsaw/Poland
Age: 55
Posts: 1,959
|
Next small optimisation:
Code:
move.w Var_NumberOfSprites(a5),d6 ; Get number of sprites in list subq.w #2,d6 ; Subtract 2 for dbf value blt.s .NoSort ; No sorting required if number of sprites is 1 or less lea Var_SpritePointerList(a5),a3 ; Get pointer to start of our sprite ptr list .SortLoopOuter move.l a3,a0 ; a0 = start of sprite ptr list move.w d6,d7 ; d7 = loop counter (number of sprites-2) .SortLoopInner move.l (a0)+,a1 ; Get address of sprite data 1 in a1 move.l (a0),a2 ; Get address of sprite data 2 in a2 move.w EB_YPos(a1),d0 ; Get Y position of sprite data 1 in d0 sub.w EB_YPos(a2),d0 ; Compare Y position of sprite data 2 with Y position of sprite data 1 (in d0) ble.s .NoSwap ; Important!! Less than OR equal = No Swap.. otherwise swaps forever equal values.... move.l a2,-4(a0) ; Store sprite 2 pointer where sprite 1 pointer was stored move.l a1,(a0) ; Store sprite 1 pointer where sprite 2 pointer was stored .NoSwap dbf d7,.SortLoopInner ; Loop through all sprite pointers tst.w d0 ; bgt.s .SortLoopOuter ; If flag is not 0, there was a swap in the last pass, so need to pass through again .NoSort Last edited by Don_Adan; 15 March 2016 at 13:56. |
15 March 2016, 13:58 | #19 |
68k
Join Date: Sep 2005
Location: Somewhere
Posts: 828
|
@Don_Adan. Please add my optimization to your post #18. Thanks.
@DanScott - I'm not sure but if in list there is only one swap then loop will be done twice instead of one time, right ? |
15 March 2016, 14:28 | #20 |
Lemon. / Core Design
Join Date: Mar 2016
Location: Tier 5
Posts: 1,211
|
No sure if that change in #18 will work. the sub from d0, will only test for the last sub in the d7 dbf loop
Won't the (A0)+,(A0)+ then -(A0),-(A0) bring A0 back to it's original value.. A0 needs to be +4 for the next iteration Yes, if there is only one swap, it will pass through again. This is why perhaps an insertion sort would be better.. it can do it in one pass.. but there are extra overheads in the insert upwards routine |
Currently Active Users Viewing This Thread: 1 (0 members and 1 guests) | |
Thread Tools | |
Similar Threads | ||||
Thread | Thread Starter | Forum | Replies | Last Post |
Translating sprite coordinates to playfield coordinates | nandius_c | Coders. Asm / Hardware | 15 | 31 July 2014 23:17 |
32 and 64 bit sprite control words question | FrenchShark | Coders. General | 8 | 10 January 2008 02:32 |
Cpu problems...in other words, BRAIN | oldpx | support.Hardware | 2 | 24 March 2003 22:01 |
Swear words | Kodoichi | project.EAB | 19 | 14 December 2001 00:53 |
|
|