Author Topic: Need a cheaper way to do clearcolor  (Read 6632 times)

0 Members and 1 Guest are viewing this topic.

Offline johannhowitzer

  • Forum Regular
  • Posts: 118
    • View Profile
Need a cheaper way to do clearcolor
« on: June 24, 2021, 05:35:25 pm »
I'm having a slowdown issue with my game project, and I've isolated the cause to my use of clearcolor.  My game is a sidescrolling shooter, and when any ship takes a hit, its sprite flashes - each ship has a value hit_flash that is set to 1 when it takes a hit, and then every frame, decays back toward 0.  This value determines the alpha of the overlay drawn on top of the sprite.

This is not a problem when only one ship gets hit, but if four or five get hit, the program has to run that many clearcolor statements every frame, which causes the slowdown.  The following sub shows how this is being done:


Code: QB64: [Select]
  1. sub overlay(d&, h~&)
  2.  
  3. preserve& = dest: preserve2& = source
  4. dest d&: source d&
  5.  
  6. line(0, 0)-(_width, _height), h~&, bf
  7. clearcolor point(width - 1, height - 1), d&
  8.  
  9. dest preserve&: source preserve2&
  10.  


 [ You are not allowed to view this attachment ]  

The image here shows the steps of the overlay process.  The line statement covers the whole image surface d&, with color h~&, which has an alpha component.  Then the clearcolor statement uses the bottom-right of that surface to mask out all of the overlay except the sprite.  Without the clearcolor, the overlay will look like the middle sprite in the image - just an overlaid rectangle, which looks really ugly.  When I commented out the clearcolor statement, no slowdown occurred.

I'd like to keep the hit flash if possible, there might be a way to do this with pre-draw mask data, but that would be a lot of work that I'd also have to keep doing as the project progresses.  I considered a paint statement, but paint would ignore isolated islands - if I had a donut sprite, paint wouldn't remove the overlay from the donut hole.

So, is there a faster way?  This is simple and elegant, but it seems clearcolor is a very expensive operation.
« Last Edit: June 24, 2021, 05:39:28 pm by johannhowitzer »

Offline Galleon

  • QB64 Developer
  • Newbie
  • Posts: 25
    • View Profile
Re: Need a cheaper way to do clearcolor
« Reply #1 on: June 25, 2021, 01:01:43 am »
Keep multiple images of same sprite in memory, one with red, one without
Prepare them once when your program loads, then use them whenever needed
This technique will also make the transition to hardware-based images (33) easier later

Offline johannhowitzer

  • Forum Regular
  • Posts: 118
    • View Profile
Re: Need a cheaper way to do clearcolor
« Reply #2 on: June 25, 2021, 04:02:36 am »
It doesn't go from 0 to 1, then back to 0 some frames later...  it goes to 1, then gradually goes away over about 15 frames, a quarter second.  And I have various flashes - red for low health, white for being hit, blue for engine burnout.

Also, I have other modifiers going on with my sprite sheet already - tilt up or down, animation frames, etc.  Having to duplicate my entire sprite sheet, color everything red, then use another modifier, that's a ton of extra busywork for a single-color effect.  I wouldn't just have to color each ship red once, I'd have to color every animation frame three times for every color overlay I want.
« Last Edit: June 25, 2021, 04:05:43 am by johannhowitzer »

Offline NOVARSEG

  • Forum Resident
  • Posts: 509
    • View Profile
Re: Need a cheaper way to do clearcolor
« Reply #3 on: June 25, 2021, 04:34:00 am »
In theory you could draw a ship with MEM.   I'm not so sure that alpha channel will allow a total fade out to black for example.

MEM seems to be very fast and is the closest thing to assembler in QB64.  If a ship can be moved with MEM then any effect is possible.

Some code using MEM to draw custom cursors. very basic.   

https://www.qb64.org/forum/index.php?topic=3809.15

Another thing that seems to slow down code is DISPLAY.  With MEM I did not have to use DISPLAY (well I don't understand it anyway)
« Last Edit: June 25, 2021, 04:51:13 am by NOVARSEG »

Offline johannhowitzer

  • Forum Regular
  • Posts: 118
    • View Profile
Re: Need a cheaper way to do clearcolor
« Reply #4 on: June 25, 2021, 05:06:53 am »
The standard practical use of DISPLAY is once per frame in your main gameplay loop, so you can draw everything behind the scenes, and have the screen update all at once.  For QB64 game developers, it is essential for anything above a text adventure, without it, you will see crazy flickering.  But once per loop is not terribly expensive.

Regarding MEM, if I have to resort to essentially learning to code assembly for a single visual effect, that visual effect might have to get scrapped.  I'm already putting off learning another programming language until the demo is done.  I was hoping there was a workaround with some clever trick.

Offline Cobalt

  • QB64 Developer
  • Forum Resident
  • Posts: 878
  • At 60 I become highly radioactive!
    • View Profile
Re: Need a cheaper way to do clearcolor
« Reply #5 on: June 25, 2021, 11:53:58 am »
The standard practical use of DISPLAY is once per frame in your main gameplay loop, so you can draw everything behind the scenes, and have the screen update all at once.  For QB64 game developers, it is essential for anything above a text adventure, without it, you will see crazy flickering.  But once per loop is not terribly expensive.

Regarding MEM, if I have to resort to essentially learning to code assembly for a single visual effect, that visual effect might have to get scrapped.  I'm already putting off learning another programming language until the demo is done.  I was hoping there was a workaround with some clever trick.

Some things that really impact speed are , _SETALPHA, _CLEARCOLOR, _DEST\_SOURCE(when used repeatedly), and _PUTIMAGE(when zooming). Those are the 4(5) main power vampires(not the only ones just the MAIN ones) and if you don't want that drain or can't allow for their use you will need to find another way.

Your OVERLAY routine has 5 slowdown routines, you have double _DEST\_SOURCE(I have not see an impact when using these in their FUNCTION form to save the current dest or source just their SUB form when setting thats why I say double not triple but its still not helping the speed when you do it over and over) and a _CLEARCOLOR (plus a POINT routine!).
So that routine itself is going to be very slow(and hard to read with no capitals)

like @Galleon said,
The easiest and most straight forward is to pre-make all the sprites your game will use. If your game makes use of several hundred sprites or images that will need to be duplicated multiple times with each effect, then yes this will take some effort and time but will pay off with dividends!.

Another way is to pre-process the graphics once you load them, set up temporary sheets(I often use the term "Layers") to have everything already done when the game loads up, this is where a nice loading(splash) screen or sequence comes in nice and handy!


As for _DISPLAY, you don't need _DISPLAY, there are other ways too. (you will not see it used in my Dragon Warrior clone nor my up coming Phantasy Star clone)
As for MEM, that is basically our advanced PEEK and POKE of QB64. Doesn't work exactly the same as DirectQB's GETMEM and PUTMEM but isn't too different. I don't believe it will help with what your doing in this instance though.
Granted after becoming radioactive I only have a half-life!

Offline SMcNeill

  • QB64 Developer
  • Forum Resident
  • Posts: 3972
    • View Profile
    • Steve’s QB64 Archive Forum
Re: Need a cheaper way to do clearcolor
« Reply #6 on: June 25, 2021, 05:00:27 pm »
From what I see, it appears to me as if the easiest, and best, solution here would be to just go with with a single sprite sheet.  Instead of a one image of the ship, like you had originally, you'd have a sprite sheet with say 20 images on it, which would look a lot like what you posted side by side on the forums here.  When the ship gets hit, you'd then _PUTIMAGE the portion of that sheet that you need frame by frame, rather than using _CLEARCOLOR and alpha overlays on the same image over and over.
https://github.com/SteveMcNeill/Steve64 — A github collection of all things Steve!

Offline johannhowitzer

  • Forum Regular
  • Posts: 118
    • View Profile
Re: Need a cheaper way to do clearcolor
« Reply #7 on: June 25, 2021, 06:26:15 pm »
I do appreciate the suggestions, though I have to shoot down the "pre-make everything" again because of the orders of magnitude involved here.  Just for the one ship the player flies, we've got 3 versions for tilt, 4 animation frames currently, then if I want 3 color overlays each with a 15-frame decay back to normal color, that is already 540 versions of one ship on the sprite sheet, all arranged in a super complicated venn diagram to account for calculating various modifiers.

Pre-processing on game start would work, I suppose, again it gets pretty complex with the modifiers in the code, but at least the external sprite sheet wouldn't blow up.  However, again it's for one visual effect, so this would be a last resort.  For now, I think I just have to abandon the overlays, maybe find a different effect I can use.

I can see some ways around display, such as alternating between two images with SCREEN.  Prepare on the one not being used, then flip over, and use a toggle variable to keep track.  I might look into that possibility.

Offline NOVARSEG

  • Forum Resident
  • Posts: 509
    • View Profile
Re: Need a cheaper way to do clearcolor
« Reply #8 on: June 26, 2021, 01:34:45 am »
@johannhowitzer

Quote
The image here shows the steps of the overlay process.  The line statement covers the whole image surface d&, with color h~&, which has an alpha component.  Then the clearcolor statement uses the bottom-right of that surface to mask out all of the overlay except the sprite.  Without the clearcolor, the overlay will look like the middle sprite in the image - just an overlaid rectangle, which looks really ugly.  When I commented out the clearcolor statement, no slowdown occurred.


One way to speed the code up is to apply the overlay over the image of the ship and not the background.  In that way CLEARCOLOR does not have to be used. This can be done with MEM.

How  to do this.   Ok the sprite image has a start /  stop pixel for every line of pixels that the image has.  I would write custom code to generate these start stop pixels and then store that data in a READ statement.

How to implement
Feed the image into the custom program and set the alpha channel to say 254 so the program knows where the start / stop pixels are.  The program outputs BAS source code consisting of READ statements etc. ready to be pasted into your BAS file.

Now when you run your code it MEMPUTs from the READ data -  the overlay directly on the ship image ONLY.   There is probably better ways.

Another variation of this is to put an overlay behind the ship.  Might require extra coding but MEM is fast.
« Last Edit: June 26, 2021, 02:25:22 am by NOVARSEG »

Offline Galleon

  • QB64 Developer
  • Newbie
  • Posts: 25
    • View Profile
Re: Need a cheaper way to do clearcolor
« Reply #9 on: June 26, 2021, 09:33:37 am »
I find it hard to believe that _CLEARCOLOR is solely responsible for the slowness. If we could see your full code we would be able to give better suggestions.

FYI - This is the core loop of _CLEARCOLOR in C++:
------------------------------------------
for (lp=im->offset32;lp<last;lp++){
     if ((*lp&0xFFFFFF)==c) *lp=c;
}
------------------------------------------
Just trying replace _CLEARCOLOR with something faster is a dead end imo.

Offline johannhowitzer

  • Forum Regular
  • Posts: 118
    • View Profile
Re: Need a cheaper way to do clearcolor
« Reply #10 on: June 26, 2021, 10:04:48 am »
Oh believe me, I'd love nothing more than to reveal the full game code, I'm quite the show-off and terrible at keeping my own secrets, haha.  But I've got some convincing reasons for keeping things close to the chest, and have thus far been able to resist giving everything away.  The game is story heavy and very spoiler sensitive, and I'm planning to go full commercial release with this eventually.  Also, the full code is nearing 10k lines, I think... I have released certain key pieces of the code in the past that I thought would be useful in others' projects, like the keybinding / input handling system, or the sprite sheet processor.

In the end, what I'm learning here is that the workarounds involved in getting rid of this slowness are either prohibitively laborious, or involve learning yet another new discipline.  Which, I'm not against learning new disciplines, it's kinda what I've been doing the past two years in developing this game, after all!  But a whole discipline for a one-off visual effect is overkill, so I think I will just have to come up with another effect.

The purpose of this thread is served, I got a convincing answer in the negative, and that lets me move ahead.  I can still use the overlay for things that only happen once per frame, like the engine burnout and low-health alert.  The slowdown was showing up when I fired a laser through 4+ enemies and they all had the effect going off at once.

Definitely was the clearcolor, though.  I commented that one line out, and nothing else, and the slowdown vanished completely.
« Last Edit: June 26, 2021, 10:07:35 am by johannhowitzer »

Offline Cobalt

  • QB64 Developer
  • Forum Resident
  • Posts: 878
  • At 60 I become highly radioactive!
    • View Profile
Re: Need a cheaper way to do clearcolor
« Reply #11 on: June 26, 2021, 12:00:56 pm »
I find it hard to believe that _CLEARCOLOR is solely responsible for the slowness. If we could see your full code we would be able to give better suggestions.
In a scenario where you are calling it over an over as he is, it is a big drop in performance, for what ever reason?

  Also, the full code is nearing 10k lines, I think...
just FYI there is a limit to the size that can be compiled, You may eventually want to go back and rework some areas to condense and simplify your code. Another reason to pre-create all sprite effects or simplify them.(when your sprite sheet exceeds 64kx12k pxls then you will beat my largest, that was a lot of work 1000s of 16x16px sprites\tiles and a several background images before I learned the golden rule of Simplicity)

"Simplicity is good, Simplicity is wonderful, Simplicity is LIFE, Complex code kills!"
(technically 'Complexity kills' but as a programmer I slide in 'code' there. ;D)
Granted after becoming radioactive I only have a half-life!

Offline johannhowitzer

  • Forum Regular
  • Posts: 118
    • View Profile
Re: Need a cheaper way to do clearcolor
« Reply #12 on: June 26, 2021, 01:06:20 pm »
The functional code IS simple.  A lot of the bulk of that is coming from data setting and scripting, which will eventually have to be offloaded to external files.  Right now I'm pushing for demo completion, the full game is going to take quite a bit of optimization and smarter resource organization.  Right now I've got a whole routine dedicated to setting all the dialogue lines, in the end that'll be a resource file too.

I've been coding an engine from scratch, and building my first fully commercial-potential game from scratch at the same time... tons of learning experiences, and I'm definitely aware of some things that are unsustainable the way I started doing them, gotta wait until after the demo to address them though, or I'll keep pushing it back and never get there.

Offline Petr

  • Forum Resident
  • Posts: 1720
  • The best code is the DNA of the hops.
    • View Profile
Re: Need a cheaper way to do clearcolor
« Reply #13 on: June 26, 2021, 02:18:52 pm »
Used POINT function after CLEARCOLOR is the most speed problem.

Lets say, you need 30 overlay frames. You can easy do next frames from 1 picture to RAM:
(in this case is used white square without external file)

Code: QB64: [Select]
  1. SCREEN _NEWIMAGE(800, 600, 32)
  2. Texture = _NEWIMAGE(100, 100, 32)
  3. _DEST Texture
  4. CLS , White
  5.  
  6. DIM Overlays(1 TO 30) AS LONG
  7.  
  8. FOR CreateFrames = 1 TO 30
  9.     Overlays(CreateFrames) = _COPYIMAGE(Texture, 32)
  10.     _DEST Overlays(CreateFrames)
  11.     LINE (0, 0)-(99, 99), _RGBA32(0, 0, 0, CreateFrames / 30 * 255), BF
  12.  
  13. DO UNTIL i$ <> ""
  14.     i$ = INKEY$
  15.     MAX i, 1, 30, 1
  16.     _PUTIMAGE (400, 300), Overlays(i)
  17.     _LIMIT 60
  18.  
  19. SUB MAX (value, minimum, maximum, stp)
  20.     value = value + stp
  21.     IF value > maximum THEN value = minimum
  22.     IF value < minimum THEN value = minimum
  23.  


« Last Edit: June 26, 2021, 02:29:56 pm by Petr »

Offline Cobalt

  • QB64 Developer
  • Forum Resident
  • Posts: 878
  • At 60 I become highly radioactive!
    • View Profile
Re: Need a cheaper way to do clearcolor
« Reply #14 on: June 26, 2021, 06:25:01 pm »
The functional code IS simple.  A lot of the bulk of that is coming from data setting and scripting, which will eventually have to be offloaded to external files.  Right now I'm pushing for demo completion, the full game is going to take quite a bit of optimization and smarter resource organization.  Right now I've got a whole routine dedicated to setting all the dialogue lines, in the end that'll be a resource file too.

I've been coding an engine from scratch, and building my first fully commercial-potential game from scratch at the same time... tons of learning experiences, and I'm definitely aware of some things that are unsustainable the way I started doing them, gotta wait until after the demo to address them though, or I'll keep pushing it back and never get there.
I hear that, I usually start with 2 programs on a project, the MAIN code source and my DATA program that outputs all the data into an external file, I rarely(but sometimes do) attempt to start putting data into the MAIN code cause it quickly becomes messy, It can be a little tricky keeping both in sync but make does it make the main code cleaner.

You might want to look into my MFI file maker to pack all the resources into an external file. Graphix, SFX, Music, fonts, and data in a single package. Could add basic compression or an encryption algo to button it up, but once its out there people will always find ways of accessing it. Thats why I didn't bother, just made it so all the program resources are combined in one file and not directly accessible, as I make clones of old console games and don't want to directly distribute content.
Granted after becoming radioactive I only have a half-life!