How To Remove Sprite Flicker
Posted: Sat Jan 06, 2024 12:51 am
Hello All,
I am hoping you guys can give me some advice on fixing flicking sprites for my AGI interpreter.
You can see from this video that sprites that don't move flicker occasionally.
https://www.youtube.com/watch?v=rIVZNwVOI7c
The code to make the sprites work is really complicated, so I can't display it all.
However here is basically how it works:
At the end of the game loop once per cycle:
1. Disable IRQ
For every sprite, which is visible:
a. If the sprite animation sequence isn't in memory allocate some unused VRAM with a manager, and blit the animation sequence into VRAM. I emphasise unused, as the segment of VRAM is unused it should be able to be updated outside of the IRQ handler without causing artifacts.
b. Fill a sequential buffer with the following things. This buffer will be read by an IRQ handler to create sprite attributes:
I. Vera Address Sprite Data Address Middle (Low will always be 0)
II. 1 Vera Address Sprite Data High
III. 2 x position low
IV. 3 x position high
V. 4 y position (high will always be zero)
VI. 5 Sprite Attr Size/Palette Offset
c. Increment bESpritesUpdatedBufferPointer once for every byte. This the pointer a position in the buffer. This pointer is useful for marking where to put the next byte
d. Add two zeros on the end of the buffer without incrementing the pointer, as an end of list marker. If there is another sprite these zeros will be overwritten.
4. Renable IRQ. There should now be 6 bytes for every visible sprite in the buffer plus two zeros on the end
5. In the IRQ Handler execute the bEHandleSpriteUpdates method:
I have done all of the things I could think of to make sure there is no flicker:
1. Reduced cycles in the IRQ Handler routine
2. Moved the sprite handling code to the beginning of the IRQ handler, to rule out unrelated code eating the VBLANK time
3. Ensured that IRQ is disabled when it should be
I think we can rule out too many sprite per scanline, because even though the sprites are currently all 32 x 32 or 64 x 64 sprites (to be fixed) there are not enough sprites to cause that issue
I can’t see how I am running over IRQ time, there are only 6 sprites on screen currently
I also noticed that the flicker only affects static sprites, the door and Gwydion (boy with the blue pants). Notice how the chickens don’t flicker.
I am hoping you guys can give me some advice on fixing flicking sprites for my AGI interpreter.
You can see from this video that sprites that don't move flicker occasionally.
https://www.youtube.com/watch?v=rIVZNwVOI7c
The code to make the sprites work is really complicated, so I can't display it all.
However here is basically how it works:
At the end of the game loop once per cycle:
1. Disable IRQ
For every sprite, which is visible:
a. If the sprite animation sequence isn't in memory allocate some unused VRAM with a manager, and blit the animation sequence into VRAM. I emphasise unused, as the segment of VRAM is unused it should be able to be updated outside of the IRQ handler without causing artifacts.
b. Fill a sequential buffer with the following things. This buffer will be read by an IRQ handler to create sprite attributes:
I. Vera Address Sprite Data Address Middle (Low will always be 0)
II. 1 Vera Address Sprite Data High
III. 2 x position low
IV. 3 x position high
V. 4 y position (high will always be zero)
VI. 5 Sprite Attr Size/Palette Offset
c. Increment bESpritesUpdatedBufferPointer once for every byte. This the pointer a position in the buffer. This pointer is useful for marking where to put the next byte
d. Add two zeros on the end of the buffer without incrementing the pointer, as an end of list marker. If there is another sprite these zeros will be overwritten.
4. Renable IRQ. There should now be 6 bytes for every visible sprite in the buffer plus two zeros on the end
5. In the IRQ Handler execute the bEHandleSpriteUpdates method:
Code: Select all
;Macro Definitions
.macro CLEAR_SPRITE_ATTRS NO_TO_CLEAR
.local @outerLoop
.local @outerLoopCheck
.local @innerLoop
.local @innerLoopCheck
.local @end
ldy NO_TO_CLEAR
beq @end
SET_VERA_START_SPRITE_ATTRS #$0, #$4 ;Set VERA channel 0 to the start of the sprites attributes table with a stride of 4
SET_VERA_START_SPRITE_ATTRS_HIGH #$1, #$4 ;Set VERA channel 1 to the start of the sprites attributes table + 1 with a stride of 4
@outerLoop:
stz VERA_data0
stz VERA_data1
@outerLoopCheck:
dey
bne @outerLoop
@end:
.endmacro
.macro GET_NEXT_FROM_SPRITE_UPDATE_BUFFER
.local @continue
lda _bESpritesUpdatedBuffer,y
iny
.endmacro
;Define Insertion Order:
;0 Vera Address Sprite Data Middle (Low will always be 0) (If both the first two bytes are zero that indicates the end of the buffer)
;1 Vera Address Sprite Data High
;2 x low
;3 x high
;4 y
;5 Sprite Attr Size/Palette Offset
ZP_LOW_BYTE = ZP_TMP_5
bEHandleSpriteUpdates: ; --------------Executed method is here
lda _bESpritesUpdatedBufferPointer ; If there is nothing in the buffer do nothing, leave the sprites as is
cmp #< _bESpritesUpdatedBuffer
bne @start
lda _bESpritesUpdatedBufferPointer + 1
cmp #> _bESpritesUpdatedBuffer
bne @start
jmp @end
@start:
CLEAR_SPRITE_ATTRS _maxViewTable
SET_VERA_START_SPRITE_ATTRS #$0, #$1 ; Sets VERA channel 0 to the start of the sprites attributes table with a stride of 1
@loop:
GET_NEXT_FROM_SPRITE_UPDATE_BUFFER ;Address 12:5 0 (buffer 0)
sta ZP_LOW_BYTE
GET_NEXT_FROM_SPRITE_UPDATE_BUFFER ;Address 16:13 1 (buffer 1)
ora ZP_LOW_BYTE
beq @addressReset
GET_NEXT_FROM_SPRITE_UPDATE_BUFFER ;X Low 2 (buffer 2)
GET_NEXT_FROM_SPRITE_UPDATE_BUFFER ;X High 3 (buffer 3)
GET_NEXT_FROM_SPRITE_UPDATE_BUFFER ;Y Low 4 (buffer 4)
stz VERA_data0 ;Y High 5 Always 0
lda #$8 ; Collision Z Lvl 2 and Flip 6 (8 means in front of bitmap but behind text layers and not flipped, with a zero collision mask)
sta VERA_data0
GET_NEXT_FROM_SPRITE_UPDATE_BUFFER ;Sprite Attr Size 7 (buffer 5)
bra @loop
@addressReset:
lda #< _bESpritesUpdatedBuffer
sta _bESpritesUpdatedBufferPointer
lda #> _bESpritesUpdatedBuffer
sta _bESpritesUpdatedBufferPointer + 1
@end:
rts
1. Reduced cycles in the IRQ Handler routine
2. Moved the sprite handling code to the beginning of the IRQ handler, to rule out unrelated code eating the VBLANK time
3. Ensured that IRQ is disabled when it should be
I think we can rule out too many sprite per scanline, because even though the sprites are currently all 32 x 32 or 64 x 64 sprites (to be fixed) there are not enough sprites to cause that issue
I can’t see how I am running over IRQ time, there are only 6 sprites on screen currently
I also noticed that the flicker only affects static sprites, the door and Gwydion (boy with the blue pants). Notice how the chickens don’t flicker.