Author Topic: Animated texture / ANIs not working with gr.DrawImage ?  (Read 12760 times)

0 Members and 1 Guest are viewing this topic.

Offline Parias

  • 27
Re: Animated texture / ANIs not working with gr.DrawImage ?
Ack! OK... I've run into a really weird problem that I'm utterly stumped how to resolve (or even troubleshoot further), and was hoping for some more input. I've nearly got my glorious upgrade of a co-operative-friendly-FSPort-campaign ready to roll, but this issue is turning into a huge blocker.

Basically, after about 15 animations play, I suddenly start getting nil values returned from my variables. Here's an example error that's thrown:

Code: [Select]
---------------------------
Error!
---------------------------
LUA ERROR: [string "scripting.tbl - On HUD Draw"]:15: attempt to perform arithmetic on global 'totalframes' (a nil value)

------------------------------------------------------------------
ADE Debug:

------------------------------------------------------------------
Name: (null)

Name of:

Function type: main

Defined on: 0

Upvalues: 0



Source: scripting.tbl - On HUD Draw

Short source: [string "scripting.tbl - On HUD Draw"]

Current line: 13

- Function line: 0

------------------------------------------------------------------




------------------------------------------------------------------


stack traceback:
[C]: ?
[string "scripting.tbl - On HUD Draw"]:15: in main chunk

------------------------------------------------------------------


------------------------------------------------------------------


[ This info is in the clipboard so you can paste it somewhere now ]





Use Yes to break into Debugger, No to continue.

and Cancel to Quit
---------------------------
Yes   No   Cancel   
---------------------------

"totalframes" is a variable in my script that gets populated with the number of frames in the animation I'm loading. Errors involving a nil value can occur if I pass it an invalid file to process... except I'm not.  I've thrown debug notices just ahead of the execution and can confirm it's getting a valid file to process. Everything just BEFORE that step in the execution gets lined up fine, but when it hits "totalframes", suddenly it thinks the data is nil / invalid.

And this suddenly only happens after the same animation script has already been called several times and has worked fine (in a particular test case that I'll attach, it's the 16th execution that causes this).

What's up - am I tripping on some kind of obscure garbage cleanup routine? Is there some kind of weird overflow happening with my data? Am I stuffing one of my variables or arrays with too much data to process?

I don't have any command briefings in my co-op campaign which in themselves involve sixteen stages - but over the course of a continuous gameplay session and multiple command briefing segments, you'll eventually hit this limit and break the script. The only way to fix it is to restart the game - no new animations called from the script will properly appear until you do this.

I've back-ported all of my work into a small sample that can be easily dropped and tested into a base FSOpen install just in case you want to see this for yourselves - this includes an exaggerated mission that will load 16 cbanims, in sequence. Here it is:

http://illuminatihq.net/mp-cbanim.rar

To repro: Stick Blank.fs2 into your data\missions folder, cbanims.txt into your data\scripts folder, and scripting.tbl (which of course is the actual script in question) into your data\tables folder. Load FSOpen (i.e. fs2_open_3_7_2_RC3-DEBUG), start a new multiplayer co-op session, and load the "CrashTestDummy" single mission.

Step through the command briefings (hit "T" to advance the stage, as per on-screen instructions) - as each loads, you should get a pop-up window confirming which anim file is about to be loaded. When it hits the 16th, you'll see it tries to load it, then throws the error about "totalframes" being nil and it'll refuse to render any further anims. You can bomb out of the mission and retry it to get the same result (all previously-loaded animations will play until it again hits the 16th). If you want to confirm that the file being loaded *is* valid, feel free to open cbanims.txt and switch the order of the files around; no matter what file it is, it'll throw the same error when you hit the 16th stage and break.

In short: Zah?

Just for a quick at-a-glance reference, here's a copy of the latest version of the script I'm using (same as in the linked sample download, minus a couple of pages of "howto" comments)

Code: [Select]
#Conditional Hooks

$Application: FS2_Open

$On Game Init:

[
readfile = {} --Sets up our table and gets it ready to store strings.
readfile.strings = {}
readfile.filename = "cbanims.txt" --We're going to be pulling our strings from "cbanims.txt"

screenx = gr.getScreenWidth()
screeny = gr.getScreenHeight() --Get our current screen resolution. Used for image scaling and placement purposes.

if cf.fileExists(readfile.filename, "data/scripts",true) then --Confirms the existence of, then opens cbanims.txt in the data\scripts sub-folder.
local file = cf.openFile(readfile.filename,"r","data/scripts")
local i = 1
while true do
local entry = file:read("*l")
if entry == nil then break end
readfile.strings[i] = entry --Store indexed lines from cbanims.txt in readfile.strings
i=i+1
end
file:close()
end

readfile.showcbanim = function(line) --Function called from FRED for showing our anim - uses the line number in cbanims.txt as an argument
local t = readfile.strings[line]
if t then
ba.warning("Attempting to render " ..t.. " ")   --Blast a debug notice confirming what file we're loading (confirm data is being read correctly)
cbanim = gr.loadTexture(""..t.."",true) --Pull what anim resource to use from cbanims.txt and store it in  cbanim

totalframes = cbanim:getFramesLeft() --Magic 3 lines to make the animation animate
animfps = cbanim:getFPS()
starttime = mn.getMissionTime()
else
ba.warning("readfile.showcbanim() has just attempted to load line "..line.." from "..readfile.filename.." and failed. Most likely this line or the file does not exist.") --Give a debug warning if the table entry is empty
end
end

readfile.showcboverlay = function(line) --Function called from FRED for showing our command-briefing overlay - uses the line number in cbanims.txt as an argument
local t = readfile.strings[line]
if t then
--ba.warning("Attempting to render " ..t.. " ")   --Blast a debug notice confirming what file we're loading (confirm data is being read correctly)
cboverlay = gr.loadTexture(""..t.."",true) --Pull what anim resource to use from cbanims.txt and store it in cbanim

else
ba.warning("readfile.showcboverlay() has just attempted to load line "..line.." from "..readfile.filename.." and failed. Most likely this line or the file does not exist.") --Give a debug warning if the table entry is empty
end
end

]

$On HUD Draw:

[

if cboverlay ~= nil then --If readfile.showcboverlay has been called and has populated the "cboverlay" variable, display the command-briefing overlay.
local x = (screenx - cboverlay:getWidth())
local y = (screeny - cboverlay:getHeight()) --Stretch it across the entire screen
gr.drawImage(cboverlay, 0, 0, screenx, screeny) --Play our animation
end

stopcboverlay = function() --FRED-called function to stop drawing the command-briefing overlay. REMEMBER TO CALL THIS BEFORE ENDING YOUR BRIEFING MISSION.
cboverlay:unload()
cboverlay = nil
end

if cbanim ~= nil then --If readfile.showcbanim has been called and has populated the "cbanim" variable, display the command-briefing animation.
local animtime = mn.getMissionTime() - starttime
--local i = animtime * animfps
local i = (animtime * animfps) % totalframes
if i >= totalframes then
cbanim = nil
else
local x = (screenx - cbanim:getWidth()) / 2 - 120             
local y = (screeny - cbanim:getHeight()) / 2 + 200 --Make this appear in the appropriate box towards lower-left-ish corner of the screen. TODO: Adjust for proper resolution scaling!
gr.drawImage(cbanim[i],x, y) --Play our animation
end
end

stopcbanim = function() --FRED-called function to stop drawing the command-briefing animation. REMEMBER TO CALL THIS BEFORE ENDING YOUR BRIEFING MISSION.
cbanim:unload()
cbanim = nil
end

if (mn.SEXPVariables['playanim']:isValid()) then --Ignore all these, chaos happened here. TODO: Clean up the chaos
if (mn.SEXPVariables['playanim'].Value) > 0 then
ba.warning("Trying to process your variable dood")
readfile.showcbanim(mn.SEXPVariables['playanim'].Value)
mn.SEXPVariables['playanim'].Value = 0
end
end


]

#End

Any ideas? Hope I'm not missing something obvious :)

Going to experiment a bit more with some of the unloading functions - I already tried invoking the "unload" function for cbanim (and setting it to nil) for the first step of every stage of the briefing, and while this fixes a potential long-term memory usage problem (and is something I should do for the final product), it doesn't seem to make any difference as far as this issue goes.
« Last Edit: August 05, 2014, 01:44:39 am by Parias »

 

Offline m!m

  • 211
Re: Animated texture / ANIs not working with gr.DrawImage ?
Do you unload the cbanims once you don't need them anymore?
It shouldn't be a problem but it's something that you should do to keep the code clean.

 

Offline ngld

  • Administrator
  • 29
  • Knossos dev
Re: Animated texture / ANIs not working with gr.DrawImage ?
EDIT: You have to manually unload *all* animations you have loaded. You should replace this line
Code: [Select]
cbanim = gr.loadTexture(""..t.."",true)
with this:
Code: [Select]
if cbanim ~= nil then
  cbanim:unload()
end
cbanim = gr.loadTexture(""..t.."",true)
It still won't work because of an engine bug. cbanim:unload() will unload the bitmap but not release it.

Whenever totalframes is nil, cbanim:getFramesLeft() returned nil because cbanim wasn't a valid handle.

If you're running a debug build, you can press Shift+Enter to open the debug console and type bm_frag to show the bitmap memory. Most blocks should be grey AKA free. When you advance to the 10th stage and look at the bitmap memory again you'll notice that many (if not most) grey blocks have turned into blue blocks (AKA reserved for animations). At some point bm_load_animation() (called by loadTexture()) will fail because it ran out of memory.
« Last Edit: August 05, 2014, 06:16:44 am by ngld »

 

Offline Parias

  • 27
Re: Animated texture / ANIs not working with gr.DrawImage ?
Do you unload the cbanims once you don't need them anymore?
It shouldn't be a problem but it's something that you should do to keep the code clean.

Totally agree. Right now at the end of each command briefing "mission" (but only at the very end, i.e. after several animations have loaded), I call stopcbanim() and stopcboverlay() before I trigger the end-mission SEXP to unload those components and set their values to nil. However watching the VRAM memory usage in debug mode, I could see this perpetually climbing with each briefing stage - more aggressive unloading is definitely prudent here.

Which brings me on to...

EDIT: You have to manually unload *all* animations you have loaded. You should replace this line
Code: [Select]
cbanim = gr.loadTexture(""..t.."",true)
with this:
Code: [Select]
if cbanim ~= nil then
  cbanim:unload()
end
cbanim = gr.loadTexture(""..t.."",true)
It still won't work because of an engine bug. cbanim:unload() will unload the bitmap but not release it.

Whenever totalframes is nil, cbanim:getFramesLeft() returned nil because cbanim wasn't a valid handle.

If you're running a debug build, you can press Shift+Enter to open the debug console and type bm_frag to show the bitmap memory. Most blocks should be grey AKA free. When you advance to the 10th stage and look at the bitmap memory again you'll notice that many (if not most) grey blocks have turned into blue blocks (AKA reserved for animations). At some point bm_load_animation() (called by loadTexture()) will fail because it ran out of memory.

Aha! Engine bug :D This was it - and thank you for detailing this in the Mantis report. I recompiled my build with the adjustments you suggested. I added your suggested script adjustment, and updated all of my command briefing "missions" in FRED to call ( multi-eval/script-eval "stopcbanim()" ) as the very first step of every briefing stage (before calling the next anim).

The combination of these steps seems to have done the trick! I think the lua.cpp change was the catalyst though, as I had played with more aggressive unloading earlier with no apparent impact on the overall problem. If it's OK, I'm going to add a note to your ticket referencing back to this thread as another reason for the fix being needed. I'm also working under the assumption of course that the suggested changes aren't going to discreetly break something somewhere else...

Unfortunately this means I now have three disparate reasons for needing to run my own separate fork of the code for my co-op mod to work:

--Availability of multi-eval (arguable as I could probably use properly-set network-variables to accomplish similar triggers, but multi-eval is a bit easier to work with in this case and may give me a few more options) (Mantis 3079)
--Multiplayer clients crash when subtitle string larger than 32 characters is triggered (Mantis 3077)
--Bitmaps loaded with gr.loadTexture() are never released (Mantis 3084)

But this is all part of the process :) All of these have associated Mantis tickets open, and I know they'll be subjected to the appropriate code review for integration when it comes time to add more features, so it's all good. Let's see if I can find any more bugs with this project!

 

Offline m!m

  • 211
Re: Animated texture / ANIs not working with gr.DrawImage ?
Can you upload your patch for Mantis 3084?

I'll see what I can do about 3079, the patch was reviewed before and if karajorma is fine with adding it with a warning I'll commit it ASAP.

EDIT: 3079 and 3084 are now fixed in trunk.
« Last Edit: August 05, 2014, 12:46:08 pm by m!m »

 

Offline Parias

  • 27
Re: Animated texture / ANIs not working with gr.DrawImage ?
Sure, my pleasure.  .patch has been submitted. And thanks for taking a look at 3079!

Any idea on when 3077 will get a closer look as well? It sounds like this would lie in karajorma's camp and I don't know if the change I made is at all the proper way of resolving this, but so long as that problem exists, I'm stuck with a much more arduous way of presenting briefing text (being limited to ~31 characters per-message is a huge pain).
« Last Edit: August 05, 2014, 11:55:46 am by Parias »