News:

Printed Amstrad Addict magazine announced, check it out here!

Main Menu
avatar_zhulien

Best way to update screen - vector graphics

Started by zhulien, 13:30, 03 July 23

Previous topic - Next topic

0 Members and 1 Guest are viewing this topic.

zhulien

Hi, just wondering what is the best way to update the screen with regards to vector graphics.  

Would it be best to double buffer an entire screen, or best to have a screen buffer and work out a pixel list to be updated and run through thst list to actually do the update? Any better method? (Assume the 3d matrix calculations are not part of the screen update)

SpDizzy

I think that taking into account that rendering is such a delicate thing, there's no 'better approach' for that concern, and that depends a lot on your requirements.
Psyborg (Loriciel) for example does a good job with a reduced play area and double buffer (4000 - C000), 10 fps on average (12.5 down to 8.33 ocassionally) with a real feel of speed (not as fast as original Amiga version, but damn really good).
I don't know if that could be the idea you are looking for, but anyway I guess interesting to take a look.
Psyborg (Loriciel 1992)


lightforce6128

Hi. I'm a bit late with my reply, but maybe it is still helpful.

The game Vector Vaults is - the name hints at this - a game with vector graphics. What could be interesting is the "mode c" (for fast screen clearing) that is described in its blog entry in section "Vector Vaults technology":

https://albertoven.com/2016/09/26/vector-vaults/

It speeds up screen clearing by a factor of seven, but heavily reduces the available colors. This idea might be altered to have more colors available or to use the higher resolution in screen mode 1, both with reducing the possible speed-up.

andycadley

Quote from: lightforce6128 on 22:23, 11 September 23It speeds up screen clearing by a factor of seven, but heavily reduces the available colors. This idea might be altered to have more colors available or to use the higher resolution in screen mode 1, both with reducing the possible speed-up.
Based on the description, it's a variation on "colour planes" coupled with double buffering. Essentially you sacrifice part of the palette to hide what's being drawn. You could probably get 7 colours in Mode 0 or just monochrome Mode 1, but you'd lose a lot of the speed up by doing that and I doubt it's worth it.

zhulien

Quote from: lightforce6128 on 22:23, 11 September 23Hi. I'm a bit late with my reply, but maybe it is still helpful.

The game Vector Vaults is - the name hints at this - a game with vector graphics. What could be interesting is the "mode c" (for fast screen clearing) that is described in its blog entry in section "Vector Vaults technology":

https://albertoven.com/2016/09/26/vector-vaults/

It speeds up screen clearing by a factor of seven, but heavily reduces the available colors. This idea might be altered to have more colors available or to use the higher resolution in screen mode 1, both with reducing the possible speed-up.
wow, very impressive!  so smooth - they are clearing the whole screen each frame? or blanking out lines they already drew?

Anthony Flack

Reading the description, it seems that what they are doing is using bitplanes, basically. Instead of clearing the whole screen every frame, they only clear 1/7th of it, then just make the previous ink invisible and start drawing with another colour over the top. So the whole screen is cleared after 7 frames and they can start using the first colour again on the 8th frame.

Hence, screen clearing is 7x faster. Quite clever! 

angry

Quote from: Anthony Flack on 22:18, 12 September 23Reading the description, it seems that what they are doing is using bitplanes, basically. Instead of clearing the whole screen every frame, they only clear 1/7th of it, then just make the previous ink invisible and start drawing with another colour over the top. So the whole screen is cleared after 7 frames and they can start using the first colour again on the 8th frame.

Hence, screen clearing is 7x faster. Quite clever!
It says it's double buffered which means it's writing to the foreground screen and clearing a 7th of the background screen @50fps(?)

It also says it's written in C and assembler. I wonder if the main game loop is C and the screen clearing is assembler - making it very quite clever!

Anthony Flack

Clearing 1/7th of the back screen before writing over it. The screen never gets completely cleared, you just set pen 1 to the background colour to hide the remaining junk and start drawing with pen 2. By the time you reach frame 8, the rolling wipe has fully cleared the screen of pen 1 so you can use pen 1 again. When you reach frame 9, it's fully cleared of pen 2 so you can use pen 2 again. Etc. 

angry

Quote from: Anthony Flack on 10:33, 13 September 23Clearing 1/7th of the back screen before writing over it. The screen never gets completely cleared, you just set pen 1 to the background colour to hide the remaining junk and start drawing with pen 2. By the time you reach frame 8, the rolling wipe has fully cleared the screen of pen 1 so you can use pen 1 again. When you reach frame 9, it's fully cleared of pen 2 so you can use pen 2 again. Etc.
Oh yeah! That's.. mind bending.. I think it actually runs at 25fps maybe....

It's very cute anyway, and a very CPC technique.

A little zoom in / out here and there could have given it a bit more "wow" for pretty much free.

Anthony Flack

The author says that this method ran faster in tests than clearing the lines by drawing over them.

It's a clever idea and it could be the all-round fastest practical way to render vector graphics on the CPC if you don't mind chunky lines.

If you really wanted to maximise speed you could go all the way to mode 0 monochrome and you'd have 15 whole frames to clear the screen. Or maybe set aside a few colours for hud elements that don't have to move, and do the clear in slightly less than 15.

angry

#10
Might work for a midi maze type thing - background colour + 5 x 3 colours. With a palette split for the floor / ceiling, and a split for the hud.

..or maybe that would just make for an uneven fps, better for wireframe maybe.

For drawing a ton of wireframe in mono in mode 0, you'd only have to clear 1/15th of the play area for each draw - surely it would be the fastest way.

lightforce6128

Quote from: zhulien on 13:30, 03 July 23Hi, just wondering what is the best way to update the screen with regards to vector graphics. 

Would it be best to double buffer an entire screen, or best to have a screen buffer and work out a pixel list to be updated and run through thst list to actually do the update? Any better method? (Assume the 3d matrix calculations are not part of the screen update)

One thought about the introductory question: If no double buffer is used and drawing takes longer then the border part of the frame, then there will be flickering. This alone does not need to be a problem and might even give it a even more retro touch, because really old graphics hardware always created flickering images; the more objects were visible, the heavier the flickering gets. But with a pixel-buffer based graphics hardware one has two interfering frequencies: one is the 50 Hz display refresh, the other one is given by the speed of the software drawing routine. This interference can lead to that there is no flickering at all, but that the same (random) parts of the image stays visible all the time, while other (random) parts of the image are hidden all the time (some kind of Moiré effect).

So in my opinion the decision is between either to draw a highly limited number of lines really fast without a double buffer or to draw more lines with a double buffer. The palette technique discussed above can be used in both cases to speed up the process.

lightforce6128

Quote from: angry on 12:14, 13 September 23A little zoom in / out here and there could have given it a bit more "wow" for pretty much free.

This would allow the game to focus on current local obstacles or to give an overview after the obstacles have been passed. But there is a problem: Moving in all directions are just additions, something the Z80 can do well. Zooming requires multiplication. This is much more demanding, because the missing multiplication commands need to be emulated somehow. I do not know how the drawing routine in this game works, but it will probably get much slower if a layer of multiplications is added.

andycadley

Quote from: lightforce6128 on 22:56, 13 September 23So in my opinion the decision is between either to draw a highly limited number of lines really fast without a double buffer or to draw more lines with a double buffer. The palette technique discussed above can be used in both cases to speed up the process.

If you're careful about palette selection and draw/erase using AND/OR techniques you can effectively double buffer using a single screen. As long as the colours you are currently drawing in are part of the "background pens" selection they won't be visible, similarly when you're erasing the screen - you only do it with colours that are currently "background" so you can't see the partial erasing.

That's why you can't just get 15 frames by using monochrome, as you'd have to clear all the pixels at once which puts you back in the same position as if you were using all 15 colours.

angry


Quote from: andycadley on 09:47, 14 September 23
Quote from: lightforce6128 on 22:56, 13 September 23So in my opinion the decision is between either to draw a highly limited number of lines really fast without a double buffer or to draw more lines with a double buffer. The palette technique discussed above can be used in both cases to speed up the process.

If you're careful about palette selection and draw/erase using AND/OR techniques you can effectively double buffer using a single screen. As long as the colours you are currently drawing in are part of the "background pens" selection they won't be visible, similarly when you're erasing the screen - you only do it with colours that are currently "background" so you can't see the partial erasing.

That's why you can't just get 15 frames by using monochrome, as you'd have to clear all the pixels at once which puts you back in the same position as if you were using all 15 colours.
You draw all over the screen in pen 1 then switch pen 1 to the background colour and clear the 1st 1/15th of the screen - then proceed with pen 2 and blank the 2nd 1/15th etc. By the time you get back to the top you've blanked all the pen 1 off the screen and you can switch pen 1 back on and start again.

andycadley

Quote from: angry on 11:28, 14 September 23You draw all over the screen in pen 1 then switch pen 1 to the background colour and clear the 1st 1/15th of the screen - then proceed with pen 2 and blank the 2nd 1/15th etc. By the time you get back to the top you've blanked all the pen 1 off the screen and you can switch pen 1 back on and start again.

The problem with that is that "just clearing PENS other than 4" etc becomes a much more complex operation. You'd lose a lot of the advantages of partial screen clearing by the added complexity. In contrast doing the clear as a bitwise operation has a much more minimal impact.

angry

Quote from: andycadley on 13:37, 14 September 23
Quote from: angry on 11:28, 14 September 23You draw all over the screen in pen 1 then switch pen 1 to the background colour and clear the 1st 1/15th of the screen - then proceed with pen 2 and blank the 2nd 1/15th etc. By the time you get back to the top you've blanked all the pen 1 off the screen and you can switch pen 1 back on and start again.

The problem with that is that "just clearing PENS other than 4" etc becomes a much more complex operation. You'd lose a lot of the advantages of partial screen clearing by the added complexity. In contrast doing the clear as a bitwise operation has a much more minimal impact.
But that's not it.

You draw all over the screen in pen 1 then completely clear the top 1/15th of the screen and switch pen 1 "off", leaving pen 1 in 14/15ths of the screen then do pen 2 etc. By the time you've done 15 1/15th clears of the screen you're back at the top, pen 1 is cleared from the whole screen - there's 1/15th of the screen with pen 2 left which will be dealt with in the next clear - 2/15th of pen 3 is left etc etc.

I'm sure it's not a new idea, it must be used in demos somewhere.

If you had a double buffer and took 2 or 3 frames (let's say) to render as many (ugly) mono mode 0 lines all over the screen as you can, and your clean up is just a 1/15th screen clear - I can't see how undrawing all those lines could possibly be faster. Maybe at 50fps it would be marginal one way or the other idk....

andycadley

Ah, yes I can see that now. Would mean you'd need to be able to clear a section and draw the image in a single frame unless you were doing double buffering though, as you wouldn't be able to draw the lines in hidden pen.

zhulien

what does Starion for example do?  Just xor and redraw the same lines twice?

lightforce6128

#19
As far as I can see, Starion uses mode 1 with medium resolution, but only four colors. All colors are used in the vector graphics area and in the status area. This means, none of the palette tricks described above is used.

The width is set to 256 pixels (64 bytes) what greatly simplifies the calculation of screen addresses.

The height is a bit odd: The vector graphics area uses 120 scan lines, the status area uses 64 scan lines, together 184 scan lines. But 256 scan lines would have been possible to fill up one 16k bank. This means, 8 memory stripes of 576 bytes each remains and maybe can be used for something else.

The frame rate is dynamic: Sometimes every frame is updated, sometimes - when many objects are visible - only every fourth.

Because you can never see only partially drawn images, there are two options: Either the whole drawing process (not other calculations) is fast enough and can be done in about 12.000 NOPs. Or the drawing process needs more time and a double buffer is used. It seems to me that the separating line between the vector graphics area and the status area (scan line 163) is far off the raster interrupt lines, what makes it at least time consuming to create a rupture between both areas. This means, a double buffer needs the full screen size again. Another possibility is that the whole buffer is copied after drawing is complete, but this would need a big amount of time (more than 30.000 NOPs).

Deleting the whole vector graphics area would still need more than 15.000 NOPs. This is possible within one frame (19.968 NOPs), but does not leave much to actually draw the lines.

To come to a conclusion: A double buffer is used, the lines are drawn, and they are drawn again in black to delete them (using a XOR operation is too time consuming if there is no background that should be saved). The line drawing algorithm is really fast. For drawing in black some intermediate results (start address, algorithm to use, fractional offset, fractional step) can be reused to avoid to calculate them again.

MaV

Redrawing the lines is a ludicrous idea, since it will take much longer than clearing the vector graphics screen if anything more complex is drawn. You need to strive for a consistent in-game frame time, otherwise it'd be unplayable with extremely fast frames where nothing is visualised alternating with a very low frame rate if too many objects are visible on screen. The gameplay would be a jarring experience.

Here the source of the screen clearing routine in Starion. It's double buffered (screens @ #4000 and #8000), and it uses self modifying code to determine which screen it should clear atm (e.g. the line "ld hl, #7C00" is modified by another part of the code to "ld hl, #FC00".) The code itself takes a consistent 17006 NOPs to execute.

; Starion
; Clear the vector graphics screen
; @ address #AC39
; Screen address starts @ #4000.
; Screen size 64 bytes x 192 lines.
; The lower third of the screen is
; used for the HUD.
; Therefore the vector graphics screen
; is 256x128 in size (the upper two thirds
; of the visible screen.


.cls_vec_screen:
ld e, 0
ld c, 16
ld hl, #7C00       ; First address of the HUD.
                   ; #7BFF is the last byte of the vec screen.
                   ; Alternative address @ #FC00.
ld (#8282), sp     ; save SP
ld d, e

.vec_gfx_clear_main_loop
ld b, c
ld sp, hl

.vec_gfx_clear_one_line
repeat 32
push de
rend
djnz vec_gfx_clear_one_line

ld a, #F8          ; #F8 = #F800 = -#800
add h
ld h, a            ; subtract -#800 from current screen address
bit 6, a           ; check if high byte of address is below #4000 (bit 14 = 0)
                   ; or below #8000. If not, continue loop.
jr nz, vec_gfx_clear_main_loop

ld sp, (#8282)     ; restore SP
ret
Black Mesa Transit Announcement System:
"Work safe, work smart. Your future depends on it."

Anthony Flack

Erasing lines will be faster than drawing them since you're just pushing &00 bytes at the screen and presumably hauling all the numbers out of a buffer you prepared while drawing the lines. But it would depend on the density of the lines vs the size of the playfield.

If Starion spends almost one entire monitor cycle erasing each frame then using the Vector Vaults method could potentially improve the update rate by one monitor cycle across the board, and spend more of the CPU time doing visually interesting things. If you really wanted to go for speed you could try the Vector Vaults on a large playfield that only draws on every other scanline. 

I have no idea how much frame time a vector game typically spends calculating 3d points... it's worth noting that Vector Vaults is a 2d game. I think it's pretty normal for 3d games to have a variable frame rate isn't it? You don't have to have your game logic clocked to the same rate as your display.

lightforce6128

#22
Undrawing lines:

A short calculation about undrawing lines:

Inside the drawing loop, if a byte on screen is updated, store its address:
PUSH HL    ;; 4 NOPs
To clear the screen, read the stored address and delete the byte on screen:
POP HL     ;; 3 NOPs
LD (HL), A ;; 2 NOPs

Although some additional management code (e.g. loops cannot be completely unrolled) for this solution is required and the stack cannot be used freely all the time, in average this allows to delete one byte within 9 NOPs. I checked some arbitrary scenes in Starion, showing multiple objects close to the viewer. The number of set bytes was below 500 most of the time. Only the startup animation with the rotating spaceship reaches 700 bytes, and the scrolling letters reaches 1200 bytes. This means, most scenes can be undrawn within less than 500*9 = 4500 NOPs, much faster than deleting the whole screen within 17000 NOPs.


Variable frame rate:

Many games use a variable frame rate. To hide this from the player, the lower the frame rate gets, the faster the objects need to move accross the screen to compensate for this. This trick can help in crowded scenes to get some more computation time. But it gets visible: Movement of objects somehow does not look fluent any longer, although objects reach their target points right on time. Therefore fast arcade games should try to achieve a constant frame rate of 50 Hz. Many CPC games don't.


3D calculations:

Doing 3D transformations and projection to 2D can get a nightmare without multiplication, divison, or hardware floating point support. Drawing the graphics takes some time. As we discussed, deleting the graphics might take almost the same time. But the 3D-2D calculations easily can take longer than the other steps.

Solutions to this problem are to use precalculated data as often as possible, to coarsely simplify the remaining calculations (e.g. replace rotation of distant objects by translation), and to use lookup tables (e.g. for logarithms) to speed up still needed calculations.

Powered by SMFPacks Menu Editor Mod