Author Topic: Trying to speed up this falling snowflake effect  (Read 5749 times)

0 Members and 1 Guest are viewing this topic.

This topic contains a post which is marked as Best Answer. Press here if you would like to see it.

Offline Dav

  • Forum Resident
  • Posts: 792
    • View Profile
Trying to speed up this falling snowflake effect
« on: October 15, 2021, 08:06:04 am »
I'm using the image transparency method @bplus posted and the RotoZoom function to make a pretty snowflake effect.  Looks good to me, but I can't get the snow to fall any faster (on my pc anyways).  Does anyone see where I can improve the speed here? 

I'll post the code and the zip of images to run it.

Thanks!

- Dav

  [ You are not allowed to view this attachment ]   (50k)  <--- Download here

Code: QB64: [Select]
  1. 'Snowflake effect using transparency and rotozoom.
  2. 'Dav is trying to speed this up...
  3.  
  4. SCREEN _NEWIMAGE(980, 550, 32)
  5.  
  6. flakes = 400 'number of flakes
  7.  
  8. DIM SHARED tree&, snow&
  9. DIM SHARED flake.x(flakes), flake.y(flakes), flake.ys(flakes) 'x/y values, y speed
  10. DIM SHARED flake.r(flakes), flake.rs(flakes) 'rotation and rotation speed
  11. DIM SHARED flake.a(flakes), flake.s(flakes) 'alpha value & flake size
  12.  
  13. 'generate random snowflake vaues
  14.  
  15. FOR f = 1 TO flakes
  16.  
  17.     flake.x(f) = RND * _WIDTH + 30 'make randowm starting x position
  18.     flake.y(f) = RND * _HEIGHT * 2 - _HEIGHT 'make random starting y position
  19.  
  20.     SELECT CASE INT(RND * 4) + 1 'make a random y falling speed
  21.         CASE 1: flake.ys(f) = 1
  22.         CASE 2: flake.ys(f) = 1.5
  23.         CASE 3: flake.ys(f) = 2
  24.         CASE 4: flake.ys(f) = 2.5
  25.     END SELECT
  26.  
  27.     flake.r(f) = RND * 360 'make random rotation sstarting value
  28.     SELECT CASE INT(RND * 4) + 1 'make random rotation speed
  29.         CASE 1: flake.rs(f) = .3
  30.         CASE 2: flake.rs(f) = .5
  31.         CASE 3: flake.rs(f) = .7
  32.         CASE 4: flake.rs(f) = .9
  33.     END SELECT
  34.  
  35.     flake.a(f) = 25 + (RND * 60) 'random alpha value
  36.  
  37.     flake.s(f) = RND * 1.75 + .1 'randomw snowflake size
  38.  
  39.  
  40. tree& = _LOADIMAGE("tree.jpg", 32)
  41. snow& = _LOADIMAGE("snow.png")
  42.  
  43.  
  44.     CLS
  45.     _PUTIMAGE (0, 0)-(980, 550), tree&, 0
  46.  
  47.     FOR f = 1 TO flakes
  48.  
  49.         _SETALPHA flake.a(f), 1 TO snow&, snow&
  50.  
  51.         RotoZoom3 flake.x(f), flake.y(f), snow&, flake.s(f), flake.s(f), _D2R(flake.r(f))
  52.  
  53.         flake.r(f) = flake.r(f) + flake.rs(f) 'increase rotation
  54.         IF flake.r(f) > 360 THEN flake.r(f) = 1
  55.  
  56.         flake.y(f) = flake.y(f) + flake.ys(f) 'lower y position
  57.  
  58.         'if flake goes off screen, make a new flake above
  59.         IF flake.y(f) > _HEIGHT + (flake.s(f) * 8) THEN
  60.             flake.y(f) = RND * _HEIGHT - _HEIGHT - (flake.s(f) * 8)
  61.         END IF
  62.  
  63.     NEXT
  64.  
  65.     _LIMIT 30
  66.  
  67.     _DISPLAY
  68.  
  69.  
  70.  
  71. SUB RotoZoom3 (X AS LONG, Y AS LONG, Image AS LONG, xScale AS SINGLE, yScale AS SINGLE, radianRotation AS SINGLE)
  72.     ' This assumes you have set your drawing location with _DEST or default to screen.
  73.     ' X, Y - is where you want to put the middle of the image
  74.     ' Image - is the handle assigned with _LOADIMAGE
  75.     ' xScale, yScale - are shrinkage < 1 or magnification > 1 on the given axis, 1 just uses image size.
  76.     ' These are multipliers so .5 will create image .5 size on given axis and 2 for twice image size.
  77.     ' radianRotation is the Angle in Radian units to rotate the image
  78.     ' note: Radian units for rotation because it matches angle units of other Basic Trig functions
  79.     '       and saves a little time converting from degree.
  80.     '       Use the _D2R() function if you prefer to work in degree units for angles.
  81.  
  82.     DIM px(3) AS SINGLE: DIM py(3) AS SINGLE ' simple arrays for x, y to hold the 4 corners of image
  83.     DIM W&, H&, sinr!, cosr!, i&, x2&, y2& '   variables for image manipulation
  84.     W& = _WIDTH(Image&): H& = _HEIGHT(Image&)
  85.     px(0) = -W& / 2: py(0) = -H& / 2 'left top corner
  86.     px(1) = -W& / 2: py(1) = H& / 2 ' left bottom corner
  87.     px(2) = W& / 2: py(2) = H& / 2 '  right bottom
  88.     px(3) = W& / 2: py(3) = -H& / 2 ' right top
  89.     sinr! = SIN(-radianRotation): cosr! = COS(-radianRotation) ' rotation helpers
  90.     FOR i& = 0 TO 3 ' calc new point locations with rotation and zoom
  91.         x2& = xScale * (px(i&) * cosr! + sinr! * py(i&)) + X: y2& = yScale * (py(i&) * cosr! - px(i&) * sinr!) + Y
  92.         px(i&) = x2&: py(i&) = y2&
  93.     NEXT
  94.     _MAPTRIANGLE _SEAMLESS(0, 0)-(0, H& - 1)-(W& - 1, H& - 1), Image TO(px(0), py(0))-(px(1), py(1))-(px(2), py(2))
  95.     _MAPTRIANGLE _SEAMLESS(0, 0)-(W& - 1, 0)-(W& - 1, H& - 1), Image TO(px(0), py(0))-(px(3), py(3))-(px(2), py(2))
  96.  




Offline Dav

  • Forum Resident
  • Posts: 792
    • View Profile
Re: Trying to speed up this falling snowflake effect
« Reply #1 on: October 15, 2021, 10:04:48 am »
Lol, I wasn't changing the falling speed values right.  Silly me.  But, I did find a couple things.  I can ditch the CLS, and only draw flakes when in the screen area.   Now I can have a blizzard.  Gonna work on adding a little x position flutter for more falling realism.

- Dav

Code: QB64: [Select]
  1. 'Snowflake effect using transparency and rotozoom.
  2. 'Dav is trying to speed this up...
  3.  
  4. SCREEN _NEWIMAGE(980, 550, 32)
  5.  
  6. flakes = 1000 'number of flakes
  7.  
  8. DIM SHARED tree&, snow&
  9. DIM SHARED flake.x(flakes), flake.y(flakes), flake.ys(flakes) 'x/y values, y speed
  10. DIM SHARED flake.r(flakes), flake.rs(flakes) 'rotation and rotation speed
  11. DIM SHARED flake.a(flakes), flake.s(flakes) 'alpha value & flake size
  12.  
  13. 'generate random snowflake vaues
  14.  
  15. FOR f = 1 TO flakes
  16.  
  17.     flake.x(f) = RND * _WIDTH + 30 'make randowm starting x position
  18.     flake.y(f) = RND * _HEIGHT * 2 - _HEIGHT 'make random starting y position
  19.  
  20.     SELECT CASE INT(RND * 4) + 1 'make a random y falling speed
  21.         CASE 1: flake.ys(f) = 1
  22.         CASE 2: flake.ys(f) = 1.5
  23.         CASE 3: flake.ys(f) = 2
  24.         CASE 4: flake.ys(f) = 2.5
  25.     END SELECT
  26.  
  27.     flake.r(f) = RND * 360 'make random rotation sstarting value
  28.     SELECT CASE INT(RND * 4) + 1 'make random rotation speed
  29.         CASE 1: flake.rs(f) = .3
  30.         CASE 2: flake.rs(f) = .5
  31.         CASE 3: flake.rs(f) = .7
  32.         CASE 4: flake.rs(f) = .9
  33.     END SELECT
  34.  
  35.     flake.a(f) = 25 + (RND * 60) 'random alpha value
  36.  
  37.     flake.s(f) = RND * 1.75 + .1 'randomw snowflake size
  38.  
  39.  
  40. tree& = _LOADIMAGE("tree.jpg", 32)
  41. snow& = _LOADIMAGE("snow.png")
  42.  
  43.  
  44.     'CLS
  45.     _PUTIMAGE (0, 0)-(980, 550), tree&, 0
  46.  
  47.     FOR f = 1 TO flakes
  48.  
  49.         IF flake.y(f) > -50 THEN
  50.             _SETALPHA flake.a(f), 1 TO snow&, snow&
  51.             RotoZoom3 flake.x(f), flake.y(f), snow&, flake.s(f), flake.s(f), _D2R(flake.r(f))
  52.         END IF
  53.  
  54.         flake.r(f) = flake.r(f) + flake.rs(f) 'increase rotation
  55.         IF flake.r(f) > 360 THEN flake.r(f) = 1
  56.  
  57.         flake.y(f) = flake.y(f) + flake.ys(f) 'lower y position
  58.  
  59.         'if flake goes off screen, make a new flake above
  60.         IF flake.y(f) > _HEIGHT + (flake.s(f) * 8) THEN
  61.             flake.y(f) = RND * _HEIGHT - _HEIGHT - (flake.s(f) * 8)
  62.         END IF
  63.  
  64.     NEXT
  65.  
  66.     _LIMIT 30
  67.  
  68.     _DISPLAY
  69.  
  70.  
  71.  
  72. SUB RotoZoom3 (X AS LONG, Y AS LONG, Image AS LONG, xScale AS SINGLE, yScale AS SINGLE, radianRotation AS SINGLE)
  73.     ' This assumes you have set your drawing location with _DEST or default to screen.
  74.     ' X, Y - is where you want to put the middle of the image
  75.     ' Image - is the handle assigned with _LOADIMAGE
  76.     ' xScale, yScale - are shrinkage < 1 or magnification > 1 on the given axis, 1 just uses image size.
  77.     ' These are multipliers so .5 will create image .5 size on given axis and 2 for twice image size.
  78.     ' radianRotation is the Angle in Radian units to rotate the image
  79.     ' note: Radian units for rotation because it matches angle units of other Basic Trig functions
  80.     '       and saves a little time converting from degree.
  81.     '       Use the _D2R() function if you prefer to work in degree units for angles.
  82.  
  83.     DIM px(3) AS SINGLE: DIM py(3) AS SINGLE ' simple arrays for x, y to hold the 4 corners of image
  84.     DIM W&, H&, sinr!, cosr!, i&, x2&, y2& '   variables for image manipulation
  85.     W& = _WIDTH(Image&): H& = _HEIGHT(Image&)
  86.     px(0) = -W& / 2: py(0) = -H& / 2 'left top corner
  87.     px(1) = -W& / 2: py(1) = H& / 2 ' left bottom corner
  88.     px(2) = W& / 2: py(2) = H& / 2 '  right bottom
  89.     px(3) = W& / 2: py(3) = -H& / 2 ' right top
  90.     sinr! = SIN(-radianRotation): cosr! = COS(-radianRotation) ' rotation helpers
  91.     FOR i& = 0 TO 3 ' calc new point locations with rotation and zoom
  92.         x2& = xScale * (px(i&) * cosr! + sinr! * py(i&)) + X: y2& = yScale * (py(i&) * cosr! - px(i&) * sinr!) + Y
  93.         px(i&) = x2&: py(i&) = y2&
  94.     NEXT
  95.     _MAPTRIANGLE _SEAMLESS(0, 0)-(0, H& - 1)-(W& - 1, H& - 1), Image TO(px(0), py(0))-(px(1), py(1))-(px(2), py(2))
  96.     _MAPTRIANGLE _SEAMLESS(0, 0)-(W& - 1, 0)-(W& - 1, H& - 1), Image TO(px(0), py(0))-(px(3), py(3))-(px(2), py(2))
  97.  
  98.  

Offline SMcNeill

  • QB64 Developer
  • Forum Resident
  • Posts: 3972
    • View Profile
    • Steve’s QB64 Archive Forum
Re: Trying to speed up this falling snowflake effect
« Reply #2 on: October 15, 2021, 11:00:13 am »
Here's one simple method to keep _SETAPLHA from slowing down your code too much, like I was telling bplus in a thread somewhere before -- Do all the prerendering of you alpha levels once and be done with them.

Code: QB64: [Select]
  1. 'Snowflake effect using transparency and rotozoom.
  2. 'Dav is trying to speed this up...
  3.  
  4. Screen _NewImage(980, 550, 32)
  5.  
  6. flakes = 1000 'number of flakes
  7.  
  8. Dim Shared tree&, snow&
  9. Dim Shared flake.x(flakes), flake.y(flakes), flake.ys(flakes) 'x/y values, y speed
  10. Dim Shared flake.r(flakes), flake.rs(flakes) 'rotation and rotation speed
  11. Dim Shared flake.a(flakes), flake.s(flakes) 'alpha value & flake size
  12.  
  13. Dim Shared snows(0 To 255) As Long
  14.  
  15.  
  16.  
  17. 'generate random snowflake vaues
  18.  
  19. For f = 1 To flakes
  20.  
  21.     flake.x(f) = Rnd * _Width + 30 'make randowm starting x position
  22.     flake.y(f) = Rnd * _Height * 2 - _Height 'make random starting y position
  23.  
  24.     Select Case Int(Rnd * 4) + 1 'make a random y falling speed
  25.         Case 1: flake.ys(f) = 1
  26.         Case 2: flake.ys(f) = 1.5
  27.         Case 3: flake.ys(f) = 2
  28.         Case 4: flake.ys(f) = 2.5
  29.     End Select
  30.  
  31.     flake.r(f) = Rnd * 360 'make random rotation sstarting value
  32.     Select Case Int(Rnd * 4) + 1 'make random rotation speed
  33.         Case 1: flake.rs(f) = .3
  34.         Case 2: flake.rs(f) = .5
  35.         Case 3: flake.rs(f) = .7
  36.         Case 4: flake.rs(f) = .9
  37.     End Select
  38.  
  39.     flake.a(f) = (Rnd * 128) 'random alpha value
  40.  
  41.     flake.s(f) = Rnd * 1.75 + .1 'randomw snowflake size
  42.  
  43.  
  44. tree& = _LoadImage("tree.jpg", 32)
  45. snow& = _LoadImage("snow.png", 32)
  46. For i = 0 To 255
  47.     snows(i) = _CopyImage(snow&)
  48.     _SetAlpha i, 1 To -1, snows(i)
  49.  
  50.  
  51.     'Cls
  52.     _PutImage (0, 0)-(980, 550), tree&, 0
  53.     Locate 1, 1: Print "FPS:"; counter
  54.     k = _KeyHit
  55.     If k = 32 Then mode = Not mode
  56.     For f = 1 To flakes
  57.  
  58.         If flake.y(f) > -50 Then
  59.             If mode Then 'use the spacebar to toggle alpha methods
  60.                 _SetAlpha flake.a(f), 1 To -1&, snow&
  61.                 RotoZoom3 flake.x(f), flake.y(f), snow&, flake.s(f), flake.s(f), _D2R(flake.r(f))
  62.             Else
  63.                 RotoZoom3 flake.x(f), flake.y(f), snows(flake.a(f)), flake.s(f), flake.s(f), _D2R(flake.r(f))
  64.             End If
  65.         End If
  66.  
  67.         flake.r(f) = (flake.r(f) + flake.rs(f)) Mod 360 'increase rotation
  68.  
  69.         flake.y(f) = flake.y(f) + flake.ys(f) 'lower y position
  70.  
  71.         'if flake goes off screen, make a new flake above
  72.         If flake.y(f) > _Height + (flake.s(f) * 8) Then
  73.             flake.y(f) = Rnd * _Height - _Height - (flake.s(f) * 8)
  74.         End If
  75.     Next
  76.  
  77.     If Timer > time# Then time# = Timer + 1: counter = c: c = 0
  78.     c = c + 1
  79.     '    _Limit 30
  80.  
  81.     _Display
  82.  
  83. Loop Until k = 27
  84.  
  85.  
  86. Sub RotoZoom3 (X As Long, Y As Long, Image As Long, xScale As Single, yScale As Single, radianRotation As Single)
  87.     ' This assumes you have set your drawing location with _DEST or default to screen.
  88.     ' X, Y - is where you want to put the middle of the image
  89.     ' Image - is the handle assigned with _LOADIMAGE
  90.     ' xScale, yScale - are shrinkage < 1 or magnification > 1 on the given axis, 1 just uses image size.
  91.     ' These are multipliers so .5 will create image .5 size on given axis and 2 for twice image size.
  92.     ' radianRotation is the Angle in Radian units to rotate the image
  93.     ' note: Radian units for rotation because it matches angle units of other Basic Trig functions
  94.     '       and saves a little time converting from degree.
  95.     '       Use the _D2R() function if you prefer to work in degree units for angles.
  96.  
  97.     Dim px(3) As Single: Dim py(3) As Single ' simple arrays for x, y to hold the 4 corners of image
  98.     Dim W&, H&, sinr!, cosr!, i&, x2&, y2& '   variables for image manipulation
  99.     W& = _Width(Image&): H& = _Height(Image&)
  100.     px(0) = -W& / 2: py(0) = -H& / 2 'left top corner
  101.     px(1) = -W& / 2: py(1) = H& / 2 ' left bottom corner
  102.     px(2) = W& / 2: py(2) = H& / 2 '  right bottom
  103.     px(3) = W& / 2: py(3) = -H& / 2 ' right top
  104.     sinr! = Sin(-radianRotation): cosr! = Cos(-radianRotation) ' rotation helpers
  105.     For i& = 0 To 3 ' calc new point locations with rotation and zoom
  106.         x2& = xScale * (px(i&) * cosr! + sinr! * py(i&)) + X: y2& = yScale * (py(i&) * cosr! - px(i&) * sinr!) + Y
  107.         px(i&) = x2&: py(i&) = y2&
  108.     Next
  109.     _MapTriangle _Seamless(0, 0)-(0, H& - 1)-(W& - 1, H& - 1), Image To(px(0), py(0))-(px(1), py(1))-(px(2), py(2))
  110.     _MapTriangle _Seamless(0, 0)-(W& - 1, 0)-(W& - 1, H& - 1), Image To(px(0), py(0))-(px(3), py(3))-(px(2), py(2))
  111.  

To test this, simply let it run for a few seconds and watch the number that pops up on your top-left corner of the animation.  See what it averages to be for you after 5 or 6 updates.  Mine starts out around 110 FPS or so.

Then hit the space bar and watch how that number changes over the next five or six seconds.  Mine tends to drop down to about 80 FPS or so.

That's an increase of what?  From 80 to 110.. about 40% faster, just by doing the pre-alpha work once and being done with it?

Now, if we *really* wanted to see an increase in speed and performance, couldn't we also do the scaling in a similar manner?  (Scale is .1 to 1.75, size is 30x30, so that's a max scale range from 3  to 53, so only a total of 50 actual levels in sizing, which is a small enough array of images to keep in memory.)  Once the scaling is predone, we no longer need RotoZoom at all.  We can stick to a single simple _PUTIMAGE for our snowflakes.... 

And in that case, we can then _COPYIMAGE(image,33) everything to swap over and make them HARDWARE images and then let the GPU do all the work with that _PUTIMAGE command, which will run much, much faster than software rendering everything like we're currently doing.

It'd require more memory usage up front to store all the pre-rendered and pre-sized images, but these are small images (30x30 is only 900 pixels or 7200 bytes of information) and really shouldn't cause that big of a footprint for memory usage in modern systems.  For example, the 256 pre-rendered alpha images I'm using above only uses a total of 7200bytes * 256 alpha levels = 1.8MB of memory...  PUHH!  If your modern computer can't handle 1.8MB of memory usage, then it's time for you to upgrade!  Even if I pre-rendered and pre-sized to match every possible combination, we'd only be looking at 1.8 * 50 = ~92MB of memory usage for the whole shebang!

QB64 can work blazingly fast with modern systems and modern memory limits, if we want it to.  It's a little more programming to make it do so, and to swap everything over from software (which most of us old school programmers are used to) to hardware images, but *when necessary*, it can make one helluva difference. 
https://github.com/SteveMcNeill/Steve64 — A github collection of all things Steve!

Offline Dav

  • Forum Resident
  • Posts: 792
    • View Profile
Re: Trying to speed up this falling snowflake effect
« Reply #3 on: October 15, 2021, 11:31:19 am »
Great tips, @SMcNeill.  Thanks for the code too.  Yeah that makes sense. 

My laptop must really be slow, I can't get over 33 FPS.  Space toggle brings it down to 28.  I'm using an old T430 laptop. 

- Dav

Marked as best answer by Dav on October 15, 2021, 09:05:10 am

Offline SMcNeill

  • QB64 Developer
  • Forum Resident
  • Posts: 3972
    • View Profile
    • Steve’s QB64 Archive Forum
Re: Trying to speed up this falling snowflake effect
« Reply #4 on: October 15, 2021, 12:55:17 pm »
Give this a shot @Dav:

Code: QB64: [Select]
  1. 'Snowflake effect using transparency and rotozoom.
  2. 'Dav is trying to speed this up...
  3.  
  4. Screen _NewImage(980, 550, 32)
  5.  
  6. flakes = 1000 'number of flakes
  7.  
  8. Dim Shared tree&, snow&
  9. Dim Shared flake.x(flakes), flake.y(flakes), flake.ys(flakes) 'x/y values, y speed
  10. Dim Shared flake.r(flakes), flake.rs(flakes) 'rotation and rotation speed
  11. Dim Shared flake.a(flakes), flake.s(flakes) 'alpha value & flake size
  12.  
  13. Dim Shared snows(0 To 255) As Long
  14.  
  15.  
  16.  
  17. 'generate random snowflake vaues
  18.  
  19. For f = 1 To flakes
  20.  
  21.     flake.x(f) = Rnd * _Width + 30 'make randowm starting x position
  22.     flake.y(f) = Rnd * _Height * 2 - _Height 'make random starting y position
  23.  
  24.     Select Case Int(Rnd * 4) + 1 'make a random y falling speed
  25.         Case 1: flake.ys(f) = 1
  26.         Case 2: flake.ys(f) = 1.5
  27.         Case 3: flake.ys(f) = 2
  28.         Case 4: flake.ys(f) = 2.5
  29.     End Select
  30.  
  31.     flake.r(f) = Rnd * 360 'make random rotation sstarting value
  32.     Select Case Int(Rnd * 4) + 1 'make random rotation speed
  33.         Case 1: flake.rs(f) = .3
  34.         Case 2: flake.rs(f) = .5
  35.         Case 3: flake.rs(f) = .7
  36.         Case 4: flake.rs(f) = .9
  37.     End Select
  38.  
  39.     flake.a(f) = (Rnd * 128) 'random alpha value
  40.  
  41.     flake.s(f) = Rnd * 1.75 + .1 'randomw snowflake size
  42.  
  43.  
  44. tree& = _LoadImage("tree.jpg", 33)
  45. snow& = _LoadImage("snow.png", 32)
  46. For i = 0 To 255
  47.     _SetAlpha i, 1 To -1, snow&
  48.     snows(i) = _CopyImage(snow&, 33)
  49.  
  50.     ' Cls
  51.     _PutImage (0, 0)-(980, 550), tree&, 0
  52.     _Title "FPS:" + Str$(counter)
  53.     k = _KeyHit
  54.     If k = 32 Then mode = Not mode
  55.     For f = 1 To flakes
  56.         _PutImage (flake.x(f), flake.y(f))-Step(30 * flake.s(f), 30 * flake.s(f)), snows(flake.a(f))
  57.         flake.r(f) = (flake.r(f) + flake.rs(f)) Mod 360 'increase rotation
  58.  
  59.         flake.y(f) = flake.y(f) + flake.ys(f) 'lower y position
  60.  
  61.         'if flake goes off screen, make a new flake above
  62.         If flake.y(f) > _Height + (flake.s(f) * 8) Then
  63.             flake.y(f) = Rnd * _Height - _Height - (flake.s(f) * 8)
  64.         End If
  65.     Next
  66.  
  67.     If Timer > time# Then time# = Timer + 1: counter = c: c = 0
  68.     c = c + 1
  69.     '    _Limit 30
  70.  
  71.     _Display
  72.  
  73. Loop Until k = 27
  74.  
  75.  
  76. Sub RotoZoom3 (X As Long, Y As Long, Image As Long, xScale As Single, yScale As Single, radianRotation As Single)
  77.     ' This assumes you have set your drawing location with _DEST or default to screen.
  78.     ' X, Y - is where you want to put the middle of the image
  79.     ' Image - is the handle assigned with _LOADIMAGE
  80.     ' xScale, yScale - are shrinkage < 1 or magnification > 1 on the given axis, 1 just uses image size.
  81.     ' These are multipliers so .5 will create image .5 size on given axis and 2 for twice image size.
  82.     ' radianRotation is the Angle in Radian units to rotate the image
  83.     ' note: Radian units for rotation because it matches angle units of other Basic Trig functions
  84.     '       and saves a little time converting from degree.
  85.     '       Use the _D2R() function if you prefer to work in degree units for angles.
  86.  
  87.     Dim px(3) As Single: Dim py(3) As Single ' simple arrays for x, y to hold the 4 corners of image
  88.     Dim W&, H&, sinr!, cosr!, i&, x2&, y2& '   variables for image manipulation
  89.     W& = _Width(Image&): H& = _Height(Image&)
  90.     px(0) = -W& / 2: py(0) = -H& / 2 'left top corner
  91.     px(1) = -W& / 2: py(1) = H& / 2 ' left bottom corner
  92.     px(2) = W& / 2: py(2) = H& / 2 '  right bottom
  93.     px(3) = W& / 2: py(3) = -H& / 2 ' right top
  94.     sinr! = Sin(-radianRotation): cosr! = Cos(-radianRotation) ' rotation helpers
  95.     For i& = 0 To 3 ' calc new point locations with rotation and zoom
  96.         x2& = xScale * (px(i&) * cosr! + sinr! * py(i&)) + X: y2& = yScale * (py(i&) * cosr! - px(i&) * sinr!) + Y
  97.         px(i&) = x2&: py(i&) = y2&
  98.     Next
  99.     _MapTriangle _Seamless(0, 0)-(0, H& - 1)-(W& - 1, H& - 1), Image To(px(0), py(0))-(px(1), py(1))-(px(2), py(2))
  100.     _MapTriangle _Seamless(0, 0)-(W& - 1, 0)-(W& - 1, H& - 1), Image To(px(0), py(0))-(px(3), py(3))-(px(2), py(2))

Now, this is running the same as before, with one exception -- I tossed out Rotozoom and simply used _PUTIMAGE to scale our  images, which allowed me to swap this over to all hardware images...

The FPS counter is now up in the title bar, rather than printed to the screen (as print is a slow beast), and there's no toggle between modes for this as I wanted to showcase the difference in how it'd perform without anything much extra involved in the process...

Before, I was running at 80FPS while setting alpha as needed, and 110FPS with alpha preset...

Now, using hardware images, this is what I'm getting:

  [ You are not allowed to view this attachment ]  

(Note the number in the title bar...)

I think we're to the point where we need to put a _LIMIT back in that dang program!!  :D
https://github.com/SteveMcNeill/Steve64 — A github collection of all things Steve!

Offline SMcNeill

  • QB64 Developer
  • Forum Resident
  • Posts: 3972
    • View Profile
    • Steve’s QB64 Archive Forum
Re: Trying to speed up this falling snowflake effect
« Reply #5 on: October 15, 2021, 12:59:10 pm »
And I think this topic makes a great example of why more people need to start looking into hardware images and what they can do for their programs.

Thanks for sharing something simple like this which can be used to make a good contrast in the various performance differnces, @Dav.



I'd also like to take a second to point out something else important here:

  [ You are not allowed to view this attachment ]

If you'll look at the screen shot above, you can see how much of the CPU each program is using.

The software rendered version is coming in at 10.5% CPU usage, and 78MB total memory usage.
The hardware rendered version is coming in at 5.7% CPU usage, and 90MB total memory usage.

As far as I'm concerned, both of these programs use trivial amounts of memory in today's computer environments.  The extra 12MB of memory for the hardware version is completely trivial, in my opinion.

The difference in an unthrottled program using 10.5% CPU and 5.7% CPU power is the difference between night and day on my system!  The 10.5% CPU usuage kicks my fans into high gear and my PC sounds like an airplane getting ready to take off, while the 5.7% CPU usage isn't enough to do anything, letting my performance remain nice and quiet and utterly unnoticeable.

Much, much faster.  Half the CPU usage.  No triggering the fans when running with no _LIMIT...  What more do I need to point out to showcase the difference between hardware images and software?
« Last Edit: October 15, 2021, 01:10:41 pm by SMcNeill »
https://github.com/SteveMcNeill/Steve64 — A github collection of all things Steve!

Offline Dav

  • Forum Resident
  • Posts: 792
    • View Profile
Re: Trying to speed up this falling snowflake effect
« Reply #6 on: October 15, 2021, 01:04:46 pm »
Wow, a huge speed increase here.  I'm hitting over 100 now.  Big thank you, @SMcNeill.  I'm planning a Christmas screensaver with holiday music, need all the speed advantage. 
 
- Dav 

Offline Dav

  • Forum Resident
  • Posts: 792
    • View Profile
Re: Trying to speed up this falling snowflake effect
« Reply #7 on: October 15, 2021, 01:10:23 pm »
Just notice it knocks out rotation though.  Hmm.. I suppose a could make several images already rotated if I really need to keep that in.   I started thinking of this for the halloween program first, making a level where faded ghosts are on the screen, and moved on to a snowflakes thing.

- Dav

Offline SMcNeill

  • QB64 Developer
  • Forum Resident
  • Posts: 3972
    • View Profile
    • Steve’s QB64 Archive Forum
Re: Trying to speed up this falling snowflake effect
« Reply #8 on: October 15, 2021, 01:16:59 pm »
Just notice it knocks out rotation though.  Hmm.. I suppose a could make several images already rotated if I really need to keep that in.   I started thinking of this for the halloween program first, making a level where faded ghosts are on the screen, and moved on to a snowflakes thing.

- Dav

Since your snowflakes are symmetrical, there's no real point to rotate them more than something like 60 degrees.  (6 point snowflakes right?  I didn't look that close.)  A 1 degree rotation per frame isn't going to make that much of a difference (I wouldn't think), so you should be able to get by with all of about 20 three-degree rotations.  Just pre-rotate those 20 images and save them as they are.  It's an extra 7200 bytes memory per image, but I honestly don't see how that'd be an issue for any system out there today.  ;)
https://github.com/SteveMcNeill/Steve64 — A github collection of all things Steve!

Offline Dav

  • Forum Resident
  • Posts: 792
    • View Profile
Re: Trying to speed up this falling snowflake effect
« Reply #9 on: October 15, 2021, 01:40:37 pm »
That's a good idea.

This will be a benefit in another program I have on the back burner.  I'm wanting to redo a fish aquarium saver, now I can have random see through bubbles floating up in front and even behind some fish.

- Dav

Offline johnno56

  • Forum Resident
  • Posts: 1270
  • Live long and prosper.
    • View Profile
Re: Trying to speed up this falling snowflake effect
« Reply #10 on: October 15, 2021, 04:50:52 pm »
Fast. Slow. CPU rates high or low. This much ram or that much ram. Regardless of all those matters, I think the program is pretty cool. well done, Dav. Well done!
Logic is the beginning of wisdom.

Offline jack

  • Seasoned Forum Regular
  • Posts: 408
    • View Profile
Re: Trying to speed up this falling snowflake effect
« Reply #11 on: October 15, 2021, 04:59:07 pm »
now you need to add some fun stuff like kids making a snowman

Offline SMcNeill

  • QB64 Developer
  • Forum Resident
  • Posts: 3972
    • View Profile
    • Steve’s QB64 Archive Forum
Re: Trying to speed up this falling snowflake effect
« Reply #12 on: October 15, 2021, 05:09:18 pm »
TWINS??  😱😱
https://github.com/SteveMcNeill/Steve64 — A github collection of all things Steve!

Offline bplus

  • Global Moderator
  • Forum Resident
  • Posts: 8053
  • b = b + ...
    • View Profile
Re: Trying to speed up this falling snowflake effect
« Reply #13 on: October 16, 2021, 05:27:00 pm »
I am curious how this holds up speed-wise:
Code: QB64: [Select]
  1. _Title "Snowjob, drawn flakes" ' B+ mod 2018-12-5
  2.  
  3. ' screen
  4. Const XMAX = 800
  5. Const YMAX = 600
  6. Screen _NewImage(XMAX, YMAX, 32)
  7. _ScreenMove 200, 100
  8. 'snow making machine
  9. Type PARTICLE
  10.     x As Single
  11.     y As Single
  12.     dx As Single
  13.     dy As Single
  14.     size As Single
  15.     density As Single
  16.     angle As Single
  17.     dir As Single
  18.     maxy As Single
  19.  
  20. '  background, try 3 backgrounds
  21. wallpaper& = _NewImage(XMAX, YMAX, 32)
  22. _Dest wallpaper&
  23. drawLandscape
  24.  
  25. nLayers = 15
  26. flakes = 2 ^ (nLayers + 1) - 1
  27. Dim snow(flakes) As PARTICLE
  28. horizon = .5 * YMAX
  29. For layer = nLayers To 1 Step -1
  30.     For flake = 0 To 2 ^ layer
  31.         snow(flake).x = Rnd * 2 * XMAX - .5 * XMAX
  32.         snow(flake).y = Rnd * YMAX
  33.         snow(flake).dx = .1 * (nLayers + 1 - layer) * Cos(Rnd * _Pi(.6666) + _Pi(.0833))
  34.         snow(flake).dy = .1 * (nLayers + 1 - layer) * Sin(Rnd * _Pi(.6666) + _Pi(.0833))
  35.         snow(flake).size = .5 * (nLayers - layer)
  36.         snow(flake).density = 2.3 + Rnd * .5
  37.         snow(flake).angle = Rnd * _Pi
  38.         If Rnd < .5 Then snow(flake).dir = -1 Else snow(flake).dir = 1
  39.         snow(flake).maxy = horizon + (nLayers + 1 - layer) * 30
  40.     Next
  41.  
  42.  
  43.     _PutImage , wallpaper&, 0
  44.     For flake = flakes To 0 Step -1
  45.         snow(flake).x = snow(flake).x + snow(flake).dx
  46.         snow(flake).y = snow(flake).y + snow(flake).dy
  47.  
  48.         If snow(flake).size <= 1 Then
  49.             PSet (snow(flake).x, snow(flake).y), _RGBA32(255, 255, 255, 80)
  50.         ElseIf snow(flake).size <= 2 Then
  51.             Circle (snow(flake).x, snow(flake).y), 1, _RGBA32(255, 255, 255, 100)
  52.         Else
  53.             snow(flake).angle = snow(flake).angle + snow(flake).dir * _Pi(1 / 400)
  54.             rFlake snow(flake).x, snow(flake).y, snow(flake).size, snow(flake).density, snow(flake).angle
  55.         End If
  56.         If snow(flake).y > snow(flake).maxy Or snow(flake).x < -.5 * XMAX Or snow(flake).x > 1.5 * XMAX Then
  57.             snow(flake).x = Rnd * 2 * XMAX - .5 * XMAX
  58.             snow(flake).y = Rnd * YMAX - 1.1 * YMAX
  59.         End If
  60.     Next
  61.     _Display
  62.     _Limit 60
  63.  
  64. Sub rFlake (x, y, r, DV, rAng)
  65.     'DV = flake density
  66.     Color _RGBA32(225, 225, 245, r ^ 2 * 30)
  67.     For a = 0 To 5
  68.         armX = x + r * Cos(a * _Pi(1 / 3) + rAng)
  69.         armY = y + r * Sin(a * _Pi(1 / 3) + rAng)
  70.         Line (x, y)-(armX, armY)
  71.         If r > 2.5 Then rFlake armX, armY, r / DV, DV, rAng
  72.     Next
  73.  
  74. Sub midInk (r1%, g1%, b1%, r2%, g2%, b2%, fr##)
  75.     Color _RGB(r1% + (r2% - r1%) * fr##, g1% + (g2% - g1%) * fr##, b1% + (b2% - b1%) * fr##)
  76.  
  77. Function rand% (lo%, hi%)
  78.     rand% = Int(Rnd * (hi% - lo% + 1)) + lo%
  79.  
  80. Sub drawLandscape
  81.     'needs midInk, rand
  82.     'the sky
  83.     For i = 0 To YMAX
  84.         midInk 200, 90, 158, 58, 28, 90, i / YMAX
  85.         Line (0, i)-(XMAX, i)
  86.     Next
  87.     'the land
  88.     startH = YMAX - 300
  89.     rgb = 160
  90.     For mountain = 1 To 6
  91.         Xright = 0
  92.         y = startH
  93.         While Xright < XMAX
  94.             ' upDown = local up / down over range, change along Y
  95.             ' range = how far up / down, along X
  96.             upDown = (Rnd * .8 - .35) * (mountain * .5)
  97.             range = Xright + rand%(15, 35) * 3.5 / mountain
  98.             lastx = Xright - 1
  99.             Color _RGB32(rgb + 40, rgb - 30, rgb)
  100.             For X = Xright To range
  101.                 y = y + upDown
  102.  
  103.                 Line (lastx, y)-(X, YMAX), , BF 'just lines weren't filling right
  104.                 lastx = X
  105.             Next
  106.             Xright = range
  107.         Wend
  108.         '_DELAY 1
  109.         rgb = rand%(rgb, rgb + 20)
  110.         startH = startH + rand%(5, 20)
  111.     Next
  112.  
  113.  

Offline Dav

  • Forum Resident
  • Posts: 792
    • View Profile
Re: Trying to speed up this falling snowflake effect
« Reply #14 on: October 16, 2021, 07:26:39 pm »
Now that's pretty, @bplus.  The falling looks very natural too.  On my laptop I get the same speed with or without _LIMIT 60 in, so my FPS is below that.  I don't notice a change in speed until around _LIMIT 30.  Really cool code.

- Dav