| |
Taskmaster Account closed
Registered: May 2012 Posts: 12 |
Clearing bitmap screen quickly
I've begun tinkering with drawing software sprites to the bitmap screen and I'm already running into serious problem. Good ol' 64. Nice and challenging, just how I remember it.
At any rate, my thought was to take my PC mentality into the rendering here and clear the screen each frame and redraw all the graphics from scratch. This is proving a challenge because ... well, clearing the bitmap memory takes the entire screen refresh and THEN some. Makes for kind of a boring game.
Anyhoo, I tried several methods of loop unrolling and self modifying code but it's just too much. Always too slow.
So I started to think about maybe drawing back over the graphics I drew the last frame and turn those bits off before drawing the new frame. Essentially drawing the frame once so the player can see it, and then inverted to erase it when the time comes.
Is this the generally accepted method on the C=64? I'm running out of options here... |
|
| |
Codey
Registered: Oct 2005 Posts: 79 |
i would zero out any unique bytes that were previously written to only. this could possibly be pre-calculated if you know what you're plotting. keep everything unrolled of course. if u can't do it in a single frame, double buffering would work... |
| |
Angel of Death
Registered: Apr 2008 Posts: 211 |
Do you really need to redraw EVERY frame?
No idea what game you are making but 12 to 16 frames/second is generally accepted for a 'vector' game. Then double-buffering is the way to go. (even DOT.NET in Windows uses DB)
For redrawing parts of the screen or just updating seperate objects you could use a sort of partial clear. (with blocks of fixed size?)
If you want to scroll a hires screen then mostly a witches-brew of VSP and hardware-scroll is used, all or not combined with DB. (there should be some games on here showing an example) |
| |
MagerValp
Registered: Dec 2001 Posts: 1078 |
You might want to check in the with Plus/4 coders at Plus/4 World since they have a lot of experience working with software sprites. And I assume you have a valid reason for not using char mode and a sprite multiplexer, which is how you typically move a lot of graphics around? If you share what you're trying to achieve it'd be easier to give good suggestions.
I've never implemented software sprites myself, but from what I've gleaned from others the two big cycle savers are keeping pre-rotated sprites in memory so you don't have to shift when blitting, and only erasing what you need to erase. |
| |
Skate
Registered: Jul 2003 Posts: 494 |
@Taskmaster: Yes, deleting only the drawn parts instead of the entire screen is a commonly used method on C64. But to achieve that, you have to slow down your drawing routine a little bit. So, if you draw only a few software sprites, you have a gain. But if you plan to draw a lot of them, covering a big part of the screen, a simple drawing routine with an unrolled clear screen code might be faster. So, the answer is "it depends".
If you believe cleaning the drawn parts would be faster, here is the fastest method i know.
clearScreen
sta $ffff
sta $ffff
sta $ffff
sta $ffff
sta $ffff
...
You need a lot of STAs prepared in your initialization routine. You set a pointer to clearScreen+1 address and when you set a byte on the bitmap screen, you write this byte's address as parameters of the STA opcode.
Next thing to do is to increase your pointer by 3. Something like;
lda #<clearScreen+1
sta pointerClearScreen ; a zeropage pointer
lda #>clearScreen+1
sta pointerClearScreen
...
...
ldy #$00
lda #<address
sta (pointerClearScreen),y
iny
lda #>address
sta (pointerClearScreen),y
lda pointerClearScreen
clc
adc #3
sta pointerClearScreen
bcc +
inc pointerClearScreen+1
+
you need to blend this code with your drawing routine and optimize as much as you can.
After you set the last drawing byte you need to put an RTS opcode using pointerClearScreen to the correct position. After that, to clear screen, all you need to do is;
jsr clearScreen
Notes:
1) After you call clearScreen routine, don't forget to replace the last RTS with STA again.
2) Required STAs for each frame would be different. Be sure to have enough STAs reserved for your drawing/cleaning routine. You may want to measure a maximum first and add a 10% to that for safety.
3) If you need double buffering (i think you would...), you will probably need 2x space for cleaning code since clear screen routine is created with the drawing routine. But you may find a way to use the same memory space somehow. It might be possible with a well designed frame switching system, i'm not really sure. |
| |
Wisdom
Registered: Dec 2001 Posts: 90 |
You might employ a "dirty rectangles" algorithm, with optionally taking delta of two consecutive frames to determine the final dirty area. Then you can generate an unrolled eraser routine with the addresses of the dirty area on the fly. Just the first thought that came to my mind.
|
| |
enthusi
Registered: May 2004 Posts: 677 |
It also often helps to just set the FG colors to BG of areas you want 'erased'.
Often when you plot new data into 8x8-blocks you set the bg-col new anyway. |
| |
Taskmaster Account closed
Registered: May 2012 Posts: 12 |
I'm playing around with a little space ship shooter game idea. Player controls a ship that fires at a screen full of randomly spawning enemies and there are some power ups and things they can collect along the way.
Nothing all that complicated but there is a lot of stuff moving around so I didn't want to mess with sprites for this.
At any rate, you guys are BEASTS. Thanks for the ideas! Some of these methods would never have occurred to me.
I'll give this stuff a shot and report back, thanks! |
| |
Skate
Registered: Jul 2003 Posts: 494 |
@Taskmaster: I've never coded a shoot'em up game before but afaik, fastest method is using hardware sprites for player & enemies (multiplexers & sorting algorithms are heavily involved) and software sprites for bullets and other small stuff. I know multiplexers are not really easy to handle and using multiplexers may affect your game design (not trying to have more than 8 sprites in a line etc.) but if you try to do everything using software sprites, you shouldn't expect a reasonable FPS i guess.
I remember Doynax was working on some shooter 4-5 years ago. I don't know the project's current status but he may share his experience if he sees this thread.
Edit: I just found this.
http://www.gtw64.co.uk/Pages/b/Review_Ballsofscrolling.php
It seems like Doynax' game is unfinished but it was in a good shape when he decided to abandon the project. He has released the source codes. It may help you to find a faster way for your project. |
| |
Taskmaster Account closed
Registered: May 2012 Posts: 12 |
OK, so this will be a silly question I'm sure but I can't make heads or tails out of the documentation on this.
I know that:
lda #$18
sta $d018
Sets my bitmap screen to be at $2000 ... how do I set it to be at $4000? This SHOULD be simple, and maybe it's just too early in the morning, but I'll be damned if I can figure it out. |
| |
Mixer
Registered: Apr 2008 Posts: 452 |
Learn about c-64 memory banks, and address $dd00
Check following for instance. http://unusedino.de/ec64/technical/project64/mapping_c64.html |
| |
Taskmaster Account closed
Registered: May 2012 Posts: 12 |
I appreciate the link, but my hard copy of that book is one of the ones I was looking at. I guess I'll go over it again. |
| |
Oswald
Registered: Apr 2002 Posts: 5094 |
http://codebase64.org/doku.php?id=base:vicii_memory_organizing&.. |
| |
Taskmaster Account closed
Registered: May 2012 Posts: 12 |
So I need to switch the Vic bank then. :) OK, will try that next time I'm at my home computer. Thanks all!
EDIT : Hey thanks for that link Oswald, that make it click in my head finally! |
| |
Taskmaster Account closed
Registered: May 2012 Posts: 12 |
OK, so I switched the Vic bank and got it rendering on another bitmap screen. Now I just need to toggle the bank each frame to eliminate the flicker.
Shame this eats so much memory. And it really sucks to have to duplicate the screen memory as that's basically just color data that will never change. Blah... Price of the machine I guess.
Anyway, thanks all! On to the next stage of this thing... |
| |
MagerValp
Registered: Dec 2001 Posts: 1078 |
Well, this is why we have char mode - 8x improvement in speed and memory usage. |
| |
Taskmaster Account closed
Registered: May 2012 Posts: 12 |
Can someone break that down for me? I'm not getting that.
Bitmap memory is laid out exactly the same as character memory (8 vertical bytes, etc) and to cover the screen with characters would be pretty memory consuming so ... I don't understand where the savings come from. It should be the same speed to copy to/from it so ... ?
Is the C=64 just inherently faster at character graphics or something? I was never aware of this and I feel that I've been cheated. :) |
| |
JackAsser
Registered: Jun 2002 Posts: 2014 |
Quote: Can someone break that down for me? I'm not getting that.
Bitmap memory is laid out exactly the same as character memory (8 vertical bytes, etc) and to cover the screen with characters would be pretty memory consuming so ... I don't understand where the savings come from. It should be the same speed to copy to/from it so ... ?
Is the C=64 just inherently faster at character graphics or something? I was never aware of this and I feel that I've been cheated. :)
Well imagine the following (a possible use for char mode and software bobs):
Let's say your object spans 1x1 characters or when unaligned to the character grid 2x2 characters. And say you can move these in half resolution steps. Then you need 2*2*4*4 = 64 different chars to describe all possible combinations. This gives you 256/64=4 different possible background blocks to play with. Splitting the screen every character row into an odd / even charset would double that to 8 different background blocks.
So, in char mode you can very quickly move around an 8x8 pixel object in half resolution motion over a background consisting of 8 various chars if you like. This method would allow you to have "tons" of objects flying around (but they can't intersect each other).
But the normal use on a c64 for a game would of course to use charmode as background and a sprite multiplexer for objects that need pixel precision motion. Use chars for objects that do not need pixel precision objects, like a fast shooting bullet or so. |
| |
MagerValp
Registered: Dec 2001 Posts: 1078 |
Quoting TaskmasterIs the C=64 just inherently faster at character graphics or something?
Basically:
STA $0400 ; updates 64 pixels
STA $2000 ; updates 8 pixels
Let's say you want to move 16x16 pixels bobs around on a black background with 2 pixel horizontal resultion and single pixel vertical resolution. For each bob, allocate 9 characters arranged like so:
036
147
258
For each bob, keep four copies of the source graphics in memory, shifted 0, 2, 4, and 6 pixels. Now, to draw the bob into the character buffer, do something along the lines of:
; Call with position in X/Y
; X: 0-158
; y: 0-199
draw_bob_0:
lda screen_line_lo,y ; get screen coordinates
sta @screen_line
lda screen_line_hi,y
sta @screen_line + 1
txa
lsr
lsr
sta @screen_offset
txa ; select source image
and #3
tax
lda bob_src_lo,x
sta @bob_src
lda bob_src_hi,x
sta @bob_src + 1
tya
and #7
sta @save_y_and_7
jsr erase_bob_0 ; erase unused lines in buffer
@save_y_and_7 = * + 1 ; get offsets for source and dest
lda #$5e
ora #$40
tay
ldx #$3f
@copy: ; copy shifted image
@bob_src = * + 1
lda $5e1f,x
sta bob_0_buf,x
dey
dex
bpl @copy
You can now plot these 9 chars into the screen using the address in @screen_line + @screen_offset. To erase it again all you have to do is zero out those 9 chars on the screen.
There is a lot of room for optimization here and you can do a lot of unrolling - it's just a question of how much memory you can spare. If you don't need smooth movement it gets a lot easier of course, e.g. for bullets it's common to just plot 8x8 chars on the grid. |
| |
Taskmaster Account closed
Registered: May 2012 Posts: 12 |
Ahh OK. This code assumes no overlap though, right? If a blob touches another blob, it will just overwrite what's there as opposed to OR'ing anything together. Choices and compromises... |
| |
MagerValp
Registered: Dec 2001 Posts: 1078 |
You also have to manage backgrounds and masking manually if you don't want a blank background.
Meanwhile a 32-sprite multiplexer manages all of this without breaking a sweat at 50 fps. |
| |
Taskmaster Account closed
Registered: May 2012 Posts: 12 |
The most direct path is not always the most interesting one .... you know? :) |