Author Topic: _CopyImage crashing my program after a while, can somebody please help?  (Read 6799 times)

0 Members and 1 Guest are viewing this topic.

Offline madscijr

  • Seasoned Forum Regular
  • Posts: 295
    • View Profile
I'm working on a basic tile-based game engine.

It mostly works, but it seems to crash after a certain number of iterations.

(The program window just closes when the sub PlotTextToScreenFast runs a 3rd time.)

I did a bunch of testing, writing log files, etc, and determined it is dying at the line:
Code: QB64: [Select]
  1. swapcolor& = _CopyImage(handle&, 32)
where the value of handle& = -8191.

However I suspect the problem isn't just that line, because there are other times when it runs with that value and doesn't die.

I have attached the code and associated image files, if anyone wants to try running it.

Just search in the .bas file for the word "crash" where I have marked the lines leading up to the crash.

If you run it you will see a menu.
If you press 4 <ENTER> you will see some graphics and text.
Press <ENTER> again to exit back to the menu.

Repeat above 2 more times.
The 3rd time the program will just die.

Why is this happening?

I am definitely not a programming or QB64 expert, and I have been driving myself crazy trying to figure out what I'm doing wrong.

Any help would be much appreciated!

FellippeHeitor

  • Guest
Every time you call _CopyImage you generate an extra image in memory, as the name implies. Do you ever _FreeImage the unused handles? This sounds like a case of memory leaking.

FellippeHeitor

  • Guest
I'm replying solely based on your post above. I haven't checked your code.

Offline bplus

  • Global Moderator
  • Forum Resident
  • Posts: 8053
  • b = b + ...
    • View Profile
7K lines, yikes!

Offline madscijr

  • Seasoned Forum Regular
  • Posts: 295
    • View Profile
Every time you call _CopyImage you generate an extra image in memory, as the name implies. Do you ever _FreeImage the unused handles? This sounds like a case of memory leaking.

You know, that could be it!
It is clearing the main screen, tilesets, etc. with _FreeImage at the end, but I think that isn't happening in all the calls that repeat over & over in the main loop!
I'll check and let you know what happens.
Thanks for your reply!

Offline madscijr

  • Seasoned Forum Regular
  • Posts: 295
    • View Profile
7K lines, yikes!

Yeah, I was not really sure where the problem exactly was, so I figured I'd post the whole thing. That's why I said search for the word "crash", lol. We will see if that _FREEIMAGE was the issue...

Offline madscijr

  • Seasoned Forum Regular
  • Posts: 295
    • View Profile
I'm replying solely based on your post above. I haven't checked your code.

I did find a couple of places where there was not _FREEIMAGE and added it, however it is still crashing.

Here are just the 3 routines that seem to have the problem.

The function swapcolor& at the bottom is where the program actually dies.

Do you see any other commands in here other than _COPYIMAGE that might require _FREEIMAGE,
or anything else that looks like it could be causing the program to exit?

Code: QB64: [Select]
  1. ' /////////////////////////////////////////////////////////////////////////////
  2. ' Draw entire array on screen in the specified fonts/colors for each character.
  3.  
  4. ' Like PlotTextToScreen but faster because it uses precalculated coordinates
  5. ' to find the tile on the tile sheet, and the destination on the screen.
  6.  
  7. ' Receives:
  8. ' imgScreen& = handle to screen to draw tile on
  9. ' arrImage() = array of image handles pointing to 16x16 tile sheets of 16x16 pixel tiles colored black (256 tiles)
  10. ' arrTileSheetMap() = array of TileSheetMapType which holds precalculated positions of tiles on tilesheet
  11. ' arrTileMap() = array of TileMapType which holds precalculated positions where tiles go on screen
  12. ' tileWidthPx% = width of tiles in pixels
  13. ' tileHeightPx% = height of tiles in pixels
  14. ' TextInfo = TextInfoType which stores info about the text to plot (# columns, # rows. among other things not used here)
  15. ' arrText(80, 80) = 2D array of PlotTextType which stores character code, forecolor, backcolor, bitmap font's tileset image handle; indexed by arrText(column #, index in arrLineOrder)
  16. ' arrLineOrder(80) AS INTEGER = array storing the row position of lines of text in arrText (done this way to speed up scrolling text)
  17.  
  18. Sub PlotTextToScreenFast(imgScreen&, arrImage() AS Long, arrTileSheetMap() AS TileSheetMapType, arrTileMap() AS TileMapType, tileWidthPx%, tileHeightPx%, TextInfo AS TextInfoType, arrText() AS PlotTextType, arrLineOrder() AS INTEGER)
  19.     Dim dx%
  20.     Dim dy%
  21.     Dim TileNum%
  22.     Dim TileColor&
  23.     DIM BackColor&
  24.     DIM iRowPos%
  25.    
  26.     IF TextInfo.numCols > UBound(arrText, 1) THEN
  27.         TextInfo.numCols = UBound(arrText, 1)
  28.     END IF
  29.     IF TextInfo.numRows > UBound(arrText, 2) THEN
  30.         TextInfo.numRows = UBound(arrText, 2)
  31.     END IF
  32.    
  33.     For dy% = 1 To TextInfo.numRows
  34.         iRowPos% = arrLineOrder(dy%)
  35.         For dx% = 1 To TextInfo.numCols
  36.             TileNum% = arrText(dx%, dy%).charCode
  37.             TileColor& = arrText(dx%, dy%).foreColor
  38.             BackColor& = arrText(dx%, dy%).backColor
  39.            
  40.             IF arrText(dx%, dy%).fontNumber > 0 AND arrText(dx%, dy%).fontNumber < 11 THEN
  41. 'THIS IS WHERE THE PROGRAM CRASHES:
  42.                 DrawColorTileFast imgScreen&, arrImage(arrText(dx%, dy%).fontNumber), arrTileSheetMap(), arrTileMap(), tileWidthPx%, tileHeightPx%, TileNum%, TileColor&, BackColor&, dx%, iRowPos%
  43.             ELSE
  44. 'THIS IS WHERE THE PROGRAM CRASHES:
  45.                 DrawColorTileFast imgScreen&, arrImage(1), arrTileSheetMap(), arrTileMap(), tileWidthPx%, tileHeightPx%, TileNum%, TileColor&, BackColor&, dx%, iRowPos%
  46.             END IF
  47.            
  48.         Next dx%
  49.     Next dy%
  50.    
  51.     _Dest imgScreen&
  52.    
  53. End Sub ' PlotTextToScreenFast
  54.  
  55. ' /////////////////////////////////////////////////////////////////////////////
  56. ' Like DrawColorTile but faster because it uses precalculated coordinates
  57. ' to find the tile on the tile sheet, and the destination on the screen.
  58.  
  59. ' Receives:
  60. ' imgScreen& = handle to screen to draw tile on
  61. ' imgTiles& = handle to 16x16 tile sheet of 16x16 pixel tiles colored black (256 tiles)
  62. ' arrTileSheetMap() = array of TileSheetMapType which holds precalculated positions of tiles on tilesheet
  63. ' arrTileMap() = array of TileMapType which holds precalculated positions where tiles go on screen
  64. ' tileWidthPx% = width of tiles in pixels
  65. ' tileHeightPx% = height of tiles in pixels
  66. ' TileNum% = ordinal number of tile on tile sheet to draw (0-255)
  67. ' TileColor& = color to use for tile
  68. ' BackColor& = color to use for background
  69. ' dx% = column # to draw tile at (where each column is 16 pixels wide)
  70. ' dy% = row # to draw tile at (where each row is 16 pixels high)
  71.  
  72. ' *** QUESTION
  73. ' *** IF THE SUB RECEIVES IMAGE HANDLE PARAMETERS imgScreen&, imgTiles&
  74. ' *** ARE THESE BY REFERENCE?
  75. ' *** IF NOT, DO THESE LOCAL COPIES HAVE TO BE RELEASED WITH _FREEIMAGE ?
  76.  
  77. Sub DrawColorTileFast (imgScreen&, imgTiles&, arrTileSheetMap() AS TileSheetMapType, arrTileMap() AS TileMapType, tileWidthPx%, tileHeightPx%, TileNum%, TileColor&, BackColor&, dx%, dy%)
  78.     DIM ColorSprite&
  79.     DIM UniversalSprite&
  80.    
  81.     ' IS THERE A BACKGROUND COLOR?
  82.     IF BackColor& <> cEmpty& THEN
  83.         ColorSprite& = _NewImage(tileWidthPx%, tileHeightPx%, 32)
  84.         _Dest ColorSprite&
  85.         CLS, BackColor&
  86.        
  87.         ' COPY THE TEMPORARY TILE TO THE SCREEN imgScreen&
  88.         _SOURCE ColorSprite&
  89.         _Dest imgScreen&
  90.        
  91.         _PutImage (arrTileMap(dx%, dy%).xPos, arrTileMap(dx%, dy%).yPos), ColorSprite&, imgScreen&, (0, 0)-(tileWidthPx% - 1, tileHeightPx% - 1) ' portion of source to the top-left corner of the destination page
  92.     END IF
  93.  
  94.     ' CREATE A TEMPORARY TILE TO COLOR
  95.     UniversalSprite& = _NewImage(tileWidthPx%, tileHeightPx%, 32)
  96.    
  97.     ' COPY THE TILE TO THE TEMPORARY ONE
  98.     _SOURCE imgTiles&
  99.     _Dest UniversalSprite&
  100.     _PutImage (0, 0), imgTiles&, UniversalSprite&, (arrTileSheetMap(TileNum%).xStart, arrTileSheetMap(TileNum%).yStart)-(arrTileSheetMap(TileNum%).xEnd, arrTileSheetMap(TileNum%).yEnd)
  101.    
  102.     ' COLOR IN THE TEMPORARY TILE
  103.     ' REPLACING BLACK (THE SOURCE COLOR) WITH THE TILE COLOR
  104. 'THIS IS WHERE THE PROGRAM CRASHES:
  105.     ColorSprite& = swapcolor&(UniversalSprite&, cBlack&, TileColor&)
  106.    
  107.     ' COPY THE TEMPORARY TILE TO THE SCREEN imgScreen&
  108.     _SOURCE ColorSprite&
  109.     _Dest imgScreen&
  110.     _PutImage (arrTileMap(dx%, dy%).xPos, arrTileMap(dx%, dy%).yPos), ColorSprite&, imgScreen&, (0, 0)-(tileWidthPx% - 1, tileHeightPx% - 1) ' portion of source to the top-left corner of the destination page
  111.    
  112.     ' ADDED PER FellipeHeitor
  113.     _FREEIMAGE ColorSprite&
  114.     _FREEIMAGE UniversalSprite&
  115.    
  116. End Sub ' DrawColorTileFast
  117.  
  118. ' /////////////////////////////////////////////////////////////////////////////
  119. ' Image color swap?
  120. ' https://www.qb64.org/forum/index.php?topic=2312.0
  121.  
  122. ' Receives:
  123. ' handle& = image handle to swap colors in
  124. ' oldcolor~& = old color to be replaced
  125. ' newcolor~& = new color
  126.  
  127. ' Return value = image handle to copy of image with the new colors
  128.  
  129. Function swapcolor& (handle&, oldcolor~&, newcolor~&)
  130.     Dim m As _MEM
  131.     DIM a&
  132.     'Dim c As _Unsigned Long
  133.     DIM c~&
  134.    
  135. 'THIS IS WHERE THE PROGRAM CRASHES:
  136.     swapcolor& = _CopyImage(handle&, 32)
  137.    
  138.     m = _MemImage(swapcolor&)
  139.    
  140.     Do Until a& = m.SIZE - 4
  141.         a& = a& + 4
  142.        
  143.         c~& = _MemGet(m, m.OFFSET + a&, _Unsigned Long)
  144.        
  145.         If c~& = oldcolor~& Then
  146.             _MemPut m, m.OFFSET + a&, newcolor~&
  147.         End If
  148.     Loop
  149.    
  150.     _MemFree m
  151.    
  152. End Function ' swapcolor&
  153.  

« Last Edit: May 17, 2021, 10:39:03 pm by madscijr »

Offline NOVARSEG

  • Forum Resident
  • Posts: 509
    • View Profile
without looking at the code

Say a variable is counting and then suddenly becomes  negative?

FellippeHeitor

  • Guest
When you call Function swapcolor&, it returns a handle that's the newly created image you used _CopyImage for. The variables you eventually use to do variable& = swapcolor&(etc) need freeing too.

Offline madscijr

  • Seasoned Forum Regular
  • Posts: 295
    • View Profile
When you call Function swapcolor&, it returns a handle that's the newly created image you used _CopyImage for. The variables you eventually use to do variable& = swapcolor&(etc) need freeing too.

Yes, it is doing that. The value retrieved from the function in line 105:
Code: QB64: [Select]
  1.     ColorSprite& = swapcolor&(UniversalSprite&, cBlack&, TileColor&)

is freed in line 113:
Code: QB64: [Select]
  1.     _FREEIMAGE ColorSprite&

Is it possible that swapcolor& is causing a memory leak just by being a function?
Would it be better to instead make it a sub and return the value in a parameter?
« Last Edit: May 17, 2021, 10:50:13 pm by madscijr »

Offline NOVARSEG

  • Forum Resident
  • Posts: 509
    • View Profile
Quote
Do Until a& = m.SIZE - 4
        a& = a& + 4
               
        c~& = _MemGet(m, m.OFFSET + a&, _Unsigned Long)
               
        If c~& = oldcolor~& Then
            _MemPut m, m.OFFSET + a&, newcolor~&
        End If
    Loop

It looks like you are memgetting 4 bytes at a time.  Is a& large enough to hold the  count?

There should be a a& = 0 after the loop or before it?

and is

 DIM a&
    'Dim c As _Unsigned Long
    DIM c~&

a valid way of DIMming?
« Last Edit: May 17, 2021, 11:04:10 pm by NOVARSEG »

FellippeHeitor

  • Guest
Try this:

Code: QB64: [Select]
  1. IF ColorSprite& < -1 THEN _FreeImage ColorSprite&
  2. ColorSprite& = swapcolor&(UniversalSprite&, cBlack&, TileColor&)

FellippeHeitor

  • Guest
Also, monitor your program as it runs in Task Manager to see if memory usage is constantly increasing until it crashes, if the above doesn't cut it.

Offline NOVARSEG

  • Forum Resident
  • Posts: 509
    • View Profile
Quote
Do Until a& = m.SIZE - 4
        a& = a& + 4
               
        c~& = _MemGet(m, m.OFFSET + a&, _Unsigned Long)
               
        If c~& = oldcolor~& Then
            _MemPut m, m.OFFSET + a&, newcolor~&
        End If
    Loop


 
Quote
a& = a& + 4
               
 c~& = _MemGet(m, m.OFFSET + a&, _Unsigned Long)

the count has gotta  be after 
             
Quote
c~& = _MemGet(m, m.OFFSET + a&, _Unsigned Long)

a& = a& + 4

because the first memget is at byte zero

Offline madscijr

  • Seasoned Forum Regular
  • Posts: 295
    • View Profile
Try this:

Code: QB64: [Select]
  1. IF ColorSprite& < -1 THEN _FreeImage ColorSprite&
  2. ColorSprite& = swapcolor&(UniversalSprite&, cBlack&, TileColor&)

I added this but on its own prog was still crashing, but see next reply to NOVARSEG.