PeteCotton
 Guru
 Joined: 13/08/2020 Location: CanadaPosts: 490 |
Posted: 01:08am 08 Jan 2025 |
|
|
|
As I mentioned, we have three mode 1 graphics pages available on the Gen 1 CMM2. We are using pages 0 and 1 for the double buffering, which leaves us one more page to play with. Normally we would use this for storing sprites etc. which we would blit onto the main screen as needed. But because we are remapping fonts and using text only, we don't really need this page just yet. So I use it to hold the bezel image, and when I want to clear page 0 or 1, I just copy the bezel from page 2 over the new screen that I'm about to write to. This clears off any old graphics, ready for a new frame (note the F-buttons are also part of the bezel - as they are the same for every screen in the game).

Back to my slow update rate problem. I just assumed that this was because text writing is relatively slow (I was wrong by the way). But this is a dev diary - so I'm recording my mistakes as well.
Time to do some testing: It takes 16 milliseconds to copy a full screen of 800x600 pixels using Blit. To write out a screenful of text takes 66ms. Here is the command I am using to write my characters to fill the screen. TEXT X%,Y%,"A","L",9,1,RGB(0,0,0)
Removing the colour at the end drops the test time from 66ms down to 55ms. That's pretty significant. Removing the scaling factor shaved another 2ms off the time. And finally I remove the alignment "L" which dropped the time to fill the screen with text to 45ms. So I'm left with this line: TEXT X%,Y%,"A",,9 Which is pretty much the bare minimum I need.
Well - it looks like writing text to the screen isn't the problem after all. It's a testament to the efficiency of the Blit command and the sheer raw power of the CMM2 with MMBasic that we are hitting these speeds. I also do recognise that I am trying to use the machine in a way that it wasn't necessarily intended to work. If I was throwing large sprites at the screen, this would be child's play for the CMM2. But by copying one text character at a time, I'm really making it work much harder than it should have to.
Hmmm, why is mine taking 250ms?
The biggest bit of real estate on the screen is the 21x21 grid of space. I'd been looping through the Y and X axis to draw the background grid - I decided to write out each X line as just long strings of data. This got me down to 140ms to draw a screen. Still some way off the 45ms I could get in the test, but a significant improvement.
That's when I started commenting out parts of my logic - and it turns out I was making one very time expensive call when I was looping through the grid squares getting ready to plot enemy ships. It wasn't brutally slow, but because we have a 21x21 grid to loop through , it was being called 441 times on each redraw.
I fixed that with a more efficient call and now the redraw time of the screen is down to a pretty decent 120ms.
Looking further at the nested loops, there are some other significant improvements I can make:
It looks something like this:
FOR Y% = -10 TO 10 absY% = PlayerSectorY% + Y% FOR X% = -10 TO 10 absX% = PlayerSectorX% + X% if absX% >= 0 AND absX% <= 99 AND absY% >= 0 AND absY% <= 99 THEN .. Do stuff ENDIF NEXT X% NEXT Y%
But this means for every loop, I'm doing 4 comparisons to make sure it's not out-with the bounds of the playfield array. If I set the start and end values of the loops to make sure they are within the array, and also set them to be the absolute grid positions to begin with, then I can save myself a lot of comparisons and calculations. This gives us the much neater code below - and shaves 20ms off the re-draw rate (100ms)
startX% = LIMIT(PlayerSectorX% - 10,0,99):endX% = LIMIT(PlayerSectorX% + 10,0,99) startY% = LIMIT(PlayerSectorY% - 10,0,99):endY% = LIMIT(PlayerSectorY% + 10,0,99) FOR Y% = startY% TO endY% FOR X% = startX% TO endX% .. Do stuff NEXT X% NEXT Y%
Next up: I effectively have three 100x100 arrays that hold the contents of the sector. One for ships, one for random objects and one for starbases. Each time I draw the grid around the player, I need to check all three arrays. To speed this up, I created a fourth array which tracks which squares are empty. Given that most of space is empty, I only need to check this single array to see if I can skip looking for ships items or starbases. This shaved another 10ms off the scan time. I just need to make sure I keep the array updated every time I move or destroy a ship.
At this stage I'm down to a fairly respectable 90ms.
I think the lesson here for me is that I jumped to the conclusion that the slow Text writing was the issue - but when I started to test that theory, I quickly realised that I was mistaken. Normally I would have looked at the nested loops first- but in the back of my mind I was prejudiced against the text draw speed - as this was famously slow on old 8-bit computers. But it turns out that it's not an issue on the CMM2. I have learned something here.
Finally, I decide to be a bit smarter about using that spare Display Page 2. About half of the draw time for the screen is drawing parts of the tactical screen that are always present (e.g. the 21x21 grid). See image below:
 So I add some logic to draw the static background and save it to display page 2 (see image below). Now, whenever I need to clear page 0 or 1, I copy the background from page 2 - and just draw the foreground text and now the draw time is down to a respectable 45ms - or 22 frames per second. Which should be good enough for animations. The ship movement also feels far more responsive.
 Edited 2025-01-08 11:10 by PeteCotton |