4 ms·
But the overheads of tracking which characters might have been changed here completely outweighed simply scrolling / updating the whole thing. The code becomes
by jimsmart 4y ago
But the overheads of tracking which characters might have been changed here completely outweighed simply scrolling / updating the whole thing. The code becomes too involved in tracking changes, and fudging about with- / rewriting- the splat code.
You can leave it as 'a creative exercise for the reader', but that's because you can't solve this for the generic case (i.e. any map the graphics artists might give you) in less cycles than simply dealing with each and every character, which is the worst case.
Processing that many bytes, and doing comparisons and extra branches, simply becomes overheads, and, very quickly, your code is slower than simply updating / scrolling everything simply.
For the colour splat routine, having a giant, pre-assmebled block of immediate-mode load store pairs for every character is as optimal as it gets — and handles all cases — on the C64, you only have a frame to update the colour RAM (because it cannot be relocated/paged), and you are generally chasing the scan beam to move that much data before the next frame.
You don't have the luxury of having extra cycles to re-write that block of code at runtime, and rewrite the code that scrolls the data within that code, nor do you have the luxury of having enough spare cycles to be comparing data, and branching conditionally depending on if it has changed or not.
Perhaps you misunderstand the technique I describe, or perhaps you under-estimate the overheads required to perform what you describe. Or perhaps both.
- 6510 4y agofor example: say we use only 4 chars and repeat them in the same order then we only have 4 pre-baked transitions. You could then swap all 4 characters in all 4 positions for free. I confess I was a bit amused when you wanted to give the graphics artist complete freedom.
- vidarh 4y agoI agree with you. So many C64 games have plenty of restrictions on the art that are very clearly a result of cycle and memory budgets. Other tricks to reduce transitions too. Say you have a cityscape where the map is a road with assorted buildings and street level stuff. The road itself might never change colour. Assorted stuff low down in the playing field might. You might want building stretching the full height of the screen, but you can avoid full height colour transitions by demanding that the graphics never go directly full height from one colour too another. Splitting the transitions by requiring set-backs and "air" or colour consistency for part of the buildings might well let you reduce the cost of setting the colours significantly. E.g. I have no idea if Cobra took advantage of this, but on a hunch I just sped through a long-play video of it, and I don't think there are any full height transitions from one colour to another, and there are bands where the colour never changes within a level. Buildings overlap, so most colours are either "add a bit" or "remove a bit", and you if you needed (Cobra is simple enough they probably didn't) I'm pretty sure you could beat the generic case both on speed and size by baking a few small transition functions even with the cost of having to pick the transitions. Akin to using fixed bands of same colour at the top or bottom, possibly accented with a couple of multiplexed sprites, another "cheap" trick that'd limit the graphics artists a bit but wouldn't do much in terms of appearance would simply be to require colour changes only every few rows near the top of the screen. E.g. let those buildings (or whatever) extend to the top, but require the top floors to stick to the same colour. Now you can reduce e.g LDA/STA/LDA/STA/LDA/STA to LDA/STA/STA/STA and similarly reduce your inlined scroll code. E.g. Cobra again, I haven't taken the time to verify, but a quick scan suggests there are bands in each level where the graphic is formulaic enough that colour changes only happens in pairs of rows or more. It's certainly close enough that if not, and you wanted to save a few cycles it'd be trivial to make sure it actually did.
- jimsmart 4y ago> colour changes only every few rows near the top of the screen. E.g. let those buildings (or whatever) extend to the top, but require the top floors to stick to the same colour. Now you can reduce e.g LDA/STA/LDA/STA/LDA/STA to LDA/STA/STA/STA and similarly reduce your inlined scroll code. Here's the thing: I've just looked at video of Cobra. It's 100% plain to my eye (having worked on a multitude of published C64 games, over a number of years, and played a hundreds of C64 games, and taken their code apart to see how they tick) that this game is using full colour scrolling — albeit in only one direction. It might look like it's not, particularly on the first level, because you think the graphics look too simple. But sadly that's just how it looks. You can plainly see this when various accent colours for e.g. windows and such like scroll into view. Don't be fooled by the fact it's tile based and the first level has poor graphics. And the fact it's full colour scrolling is even more obvious on later maps, e.g. see 5mins into this video: https://www.youtube.com/watch?v=eog6-zvts6g https://www.youtube.com/watch?v=eog6-zvts6g — That's full colour scrolling. In one direction. With tiles of 2x2 chars, with each char in the tile being able to have its own colour. 100%. There's no tricks there. Your theoretical/toy code example simply won't do what is seen on at the 5min mark there (level 2 or whatever).
- vidarh 4y ago> It might look like it's not, particularly on the first level, because you think the graphics look too simple. But sadly that's just how it looks. I specificaly did not say that Cobra isn't using full colour scrolling. You're probably right that it is. But what you've described does not require it. I am saying that Cobra is an example of the style of level design that could easily get savings without sacrificing the graphics - including the accent colours. Cobra is otherwise simple enough that it probably wouldn't be worth it, but that is entirely besides the point I was making. I did note the windows etc., yes. But the point is that there is no place in the whole game where there's a lot of big or different transitions going on at once, and the number of transitions is very small, and notably a lot of the transitions only occur at specific rows. The graphics as is is very constrained. The 5m mark is a perfect example of exactly what I'm saying - only about ~11 rows changes colour, and from what I saw that's one of the most extreme transitions in the game. But while it affects many rows, it also uses few colours and so you can offset even some of that cost. The way it's structured in faux 3D "layers" seems ripe to "decompress" the levels into a set of transition functions inspired by painters algorithm to "paint" bottom up, front to back, and then JSR at an offset into the transition function for the object "behind" if it extends past. You'd want to flatten some of the "layers" (at the cost of increasing the number of transition functions). Here's what I'd try: Since the transition functions can hardcode LDA immediate's, and STA absolute gets X indexing for one extra cycle, you can have the transition functions X index and used that to reuse the transition functions for every column. So e.g. instead of this: scroll_splat_colour: LDA #$00 # colour data for char STA $D800 # colour ram LDA #$00 STA $D801 You'd get this for each transition sequence (could be a slice of an object, but in practice you'd probably want to flatten it somewhat, at the cost of more transition functions but reducing the cycle cost because of fewer JSR/RTS pairs): some_transition: LDA #$col1 STA $D800+offset_of_last_row_start, X STA $D800+offset_of_second_to_last_row_start,X ... and so on until colour change LDA #$col2 .... sta to next row. RTS You then decompress the level into a series of JSR calls per column, right to left + DEX, and a set of offsets. Every time you scroll, you then overwrite the DEX after the last (leftmost) transition with an RTS (and fix up the previous one), and JSR to the first (rightmost) list of bottom-up JSRs. [Beware the likely errors in counting; long time since I've looked at cycle counts..] You need to save 12 cycles per transition function for the JSR/RTS plus 2 for the DEX per column plus a handful of cycles to do the setup per frame to break even, plus 1 cycle per STA. An LDA immediate/STA absolute pair is 6 cycles, so moving, say a 39x20 field adds up to 4680 cycles that way. If "everything" fails and you have one JSR + one DEX for 39 columns + STA absolute X-indexed for all 780 characters, you pay a cost of an extra 1287 cycles plus setup. Let's call it 1320. But this is before taking into account that you now don't need to do this: LDA scroll_splat_colour+6 STA scroll_splat_colour+1 LDA scroll_splat_colour+11 STA scroll_splat_colour+6 Assuming you split that in 7 segments of 111 (for 780/7) updates of 4+4 cycles each, for 888 cycles, we're 432 short in the pathological case where every colour is different to the one below it and different to the one to its right. But each STA absolute X-indexed we save, because colours do not change right to left, saves us 5 cycles. Each LDA we save because colours often repeat bottom to top saves us 2 cycles. If we assume these are evenly spread, we need 62 of each every frame to break even. In other words only 8% need to repeat. Of course if it just breaks even it's pointless. The only constraint on the artist to do this would be to set a "budget" for transitions to make sure there's always enough repetition to make it worthwhile, but it's certainly doable.
- jimsmart 4y ago> I confess I was a bit amused when you wanted to give the graphics artist complete freedom. That's because I'm speaking from a number of years of actual real-world experience working on all manner of released production games for the C64 (plus other platforms) — from arcade conversions, to film licenses, to cross-platform ports, to original projects — and in most of those situations, you simply don't get to call the shots. It's hardly some unknown abnormal situation to allow the graphic artist to use the character colour. Loads of games had unrestricted use of colour RAM. It's just what you do in most cases, unless you have a monochrome game. Your toy example here is all well and good, but is not a fully working system by any means — I don't see how it can generalise out at all. And it certainly doesn't seem to be a more optimal way to handle unrestricted scrolling, with a colour map, on the C64, which is what I was outlining the most optimal method of doing. Furthermore, it seems tangential to your originally mentioned idea. Whereas the method I outline involves no restrictions, no overheads, and will handle 8-way scrolling — with full colour map. And it works 100%, and has been used in multiple published projects. It's not theoretical. Do you have any actual examples of released games that used the technique which you describe? Or any articles you can point me at? Or perhaps a more detailed explanation of this technique and how it can be used across the whole screen (in whatever directions). I'm more than happy to take a look to try and see what I may have missed. — Out of interest, have you ever coded any complete C64 games? IDK, perhaps you're speaking about some trick used in demos/intros, in which fancy techniques often wouldn't generalise out into a full game. Apologies if it seems that I am missing something that might be obvious to you. Clearly my work on a whole bunch of published C64 titles, and my years of programming the C64 commercially, in various teams with- or alongside- other experienced programmers, was some years ago now (I quit the game industry in the 90s), and clearly we must've overlooked the technique of which you speak of. Or dismissed it for some reason.
- 6510 4y ago> Whereas the method I outline involves no restrictions, no overheads, and will handle 8-way scrolling — with full colour map. And it works 100%, and has been used in multiple published projects. It's not theoretical. yes, it was wonderful enough to give the reader a clue how one would go about squeezing more and more performance out of that good old box. Nothing comes for free of course but there are pretty much infinite interesting trade offs to be had that usually exponentially blow up the complexity. I mean you started out with a pretty easy to understand bit of memory transfer. If the goal is to update "every visible char onscreen in scrolling area" it seems pretty much the final solution, nothing better is to be had. Rephrasing the goal into "every changed char" is simply a different perspective. Most likely less productive but one could explore it as it would be faster if we restrict the freedom of graphics a bit. Say you have a space ship <ooo> moving left to right by at most a single char. You have to set the chars that make up the ship and you have to insert a space next to it or it would turn into <<<<ooo> or <ooo>>>> There is no need to fill the entire line with spaces. > perhaps you're speaking about some trick used in demos/intros, in which fancy techniques often wouldn't generalize out into a full game. I never wrote games, I just tinker and rewrite bits of my demos producing inferior results 99 our of 100 times. How many chars are we updating really if we change _ _<ooo>_ _ 1234567890A into _ <ooo> _ _ 1234567890A I think 3,4,7,8 but not 1,2,5,6,8,9,0,A ? That was the train of thought in those days at least for me :)