Ren'Py Ren'Py optimization tips?

dyho

Newbie
Jan 31, 2020
87
243
What exactly are you using to display videos?

More on topic, why are you asking about it in this thread?
Use ist
image triss = Movie(play="wayhome1.webm")

this is why
Ren'Py optimization tips?
 

Jman9

Engaged Member
Jul 17, 2019
2,295
957
Yeah, but how is movie size related to optimisation?

The Movie displayable has a 'size' parameter. Or just use renpy.movie_cutscene, as the docs suggest.
 

Jman9

Engaged Member
Jul 17, 2019
2,295
957
So, I recently went back, (re)discovered that 'config.save_dump' exists, and squeezed a dump file out of my now annoyingly huge 17MB test save. And, wait for it...

Holy cow, that thing turned out to be 1.6+Gb! Which is more than the entire game itself (on-disk, that is). And something like 99.4% of it is full of things of this sort:
Code:
     12 log.log[20138].context.dynamic_stack[4991] = <dict>
      0 log.log[20138].context.dynamic_stack[4992][u'_args'] = alias None
      0 log.log[20138].context.dynamic_stack[4992][u'from_assign'] = alias False
      0 log.log[20138].context.dynamic_stack[4992][u'_kwargs'] = alias None
      0 log.log[20138].context.dynamic_stack[4992][u'from_room'] = alias False
Code:
      6 log.log[0].context.call_location_stack[11056] = <tuple>
      0 log.log[0].context.call_location_stack[11057][0] = alias u'game/_story/hunt/plains.rpy'
      1 log.log[0].context.call_location_stack[11057][1] = 1648860522
      1 log.log[0].context.call_location_stack[11057][2] = 16343
Code:
      1 log.log[0].context.return_stack[12214][0] = u'game/_story/hunt/plains.rpy'
      1 log.log[0].context.return_stack[12214][1] = 1648860522
      1 log.log[0].context.return_stack[12214][2] = 16344
      7 log.log[0].context.return_stack[12214] = <tuple>
Code:
      0 log.log[5].context.abnormal_stack[4617] = alias False
That is not normal, is it? I assume it's due to me fucking something up, but how? And, more importantly, how do I disable all this superfluous logging of various calls?
 

MischievousPanda

New Member
Nov 24, 2021
4
2
1 log.log[0].context.return_stack[12214][2] = 16344
Judging from this, it looks like you call stack is... 12214 levels deep (holy shit!)

Which would imply that in multiple places you call something without returning from it, and they add up as you play the game. Check your call and return statements

Code:
renpy.call_stack_depth()
function can help you track when it is increased
 
Last edited:
  • Wow
Reactions: Jman9

Jman9

Engaged Member
Jul 17, 2019
2,295
957
As another user said, I wish I could facepalm myself...

Any way to clear the existing call stack?
 

MischievousPanda

New Member
Nov 24, 2021
4
2
Whenever you call something (label or screen), it needs to have a return for labels and Return() action if it's a screen

Trying to manually edit it will most likely break the game

Code:
renpy.game.context().return_stack
Use this to see what is there. Don't do it when it's 12k levels deep because you will probably crash the console lol
 

Jman9

Engaged Member
Jul 17, 2019
2,295
957
I don't think this is true for screens? Why else are there 'hide screen' and other things like that?

I'm looking at them calls right now, and I have the problem that in a few places I basically need a jump with arguments, which call kinda supplies, but obviously that leads to these problems seen here. I don't think some returns will ever be reached, so merely adding them will not help. I can imagine some ways out of it... Like some global variable for these specific calls, perhaps, but this seems so... wrong.

renpy.get_return_stack() seems to do the same, and gives me 20k+ levels. :eek: Fortunately, Ren'Py doesn't try to show all of these at once, since it's a list. I tried replacing this with renpy.set_return_stack(), which seemed to do nothing useful wrt save size.
 

MischievousPanda

New Member
Nov 24, 2021
4
2
It depends on whether you use show screen or call screen

hide screen exists if you need to hide a screen added with show

If you call a screen, it will automatically be hidden when you Return() something from it (and you really should)

Try something like renpy.get_return_stack()[:20] to see the first 20 entries or so
 
  • Like
Reactions: Jman9

Jman9

Engaged Member
Jul 17, 2019
2,295
957
Fortunately, the only open-ended 'call screen' I have is in a label called 'main_menu', which doesn't seem to be tied to anything. Not sure why it's there. As said before, I was not the original architect of this game.

Try something like renpy.get_return_stack()[:20] to see the first 20 entries or so
Most of these are the same 'game/_story/hunt/plains.rpy'. Which is strange, because the main label inside that is jumped into and out of, and the few calls inside all have returns and I'm 90% plus certain they also always reach these returns.

Edit: The other common one is a label (and that one is by it's label name) that is never called, only jumped into and out of.

Why are these entries by script filename and not label name, anyway? That might be tied to them being in there, forever.
 
Last edited:

MischievousPanda

New Member
Nov 24, 2021
4
2
Why are these entries by script filename and not label name, anyway?
Because renpy needs the filename and some kind of line number/hash to know where to return to

That might be tied to them being in there, forever.
No, they are in there because they got called from somewhere and never returned. I have never seen a return stack size of more than 4-5 in my game, and I call things all the time. So there is an error in the logic somewhere
 

Jman9

Engaged Member
Jul 17, 2019
2,295
957
Because renpy needs the filename and some kind of line number/hash to know where to return to
But some of them are stored as label names. So clearly that is not universally true.

So there is an error in the logic somewhere
There's a ton of things that get called (and called, and called) as a replacement for jump with arguments. I really should do something about that, but I'm not sure that will help because...

...of what I just did. Which was wiping the rollback logs via renpy.game.log.log = []. Saves are down by a factor of 18+ (a bit under a Mb, which is reasonable given how much data there is), save dump is down to ~28Mb, mini-freezes seem to be slightly reduced in power. I never wanted rollback in the first place, and turned it off somewhere in the middle of the test save, so that might explain some of it.

Edit: If I could just find out what the reason behind these damned mini-freezes is, I'd be one happy camper. I looked through everything documented for config variables and custom callback lists that might be invoking something on a semi-regular basis, but nothing seems out of place. Saving speed is much improved, but that doesn't seem to affect the timing for the mini-freezes much if at all, so it's probably not anything related to variables.
 
Last edited:

anne O'nymous

I'm not grumpy, I'm just coded that way.
Modder
Donor
Respected User
Jun 10, 2017
10,302
15,172
Judging from this, it looks like you call stack is... 12214 levels deep (holy shit!)
It's that, yes.


Any way to clear the existing call stack?
Python:
init python:
    def cleanCallStack():
        while renpy.call_stack_depth():
            renpy.pop_call()

label whatever:
    $ cleanCallStack()

I don't think this is true for screens?
It's not true for screens, the "call" in call screen have another meaning.


Why else are there 'hide screen' and other things like that?
Because there's screen that can be shown and not called.


I'm looking at them calls right now, and I have the problem that in a few places I basically need a jump with arguments, which call kinda supplies, but obviously that leads to these problems seen here.
It imply a bit more codding, but you can perfectly pass the arguments through an external dict and therefore use jump. Something like:
Python:
label whatever:
    call myCalledLabel( "abc", 1 )
[...]
label myCalledLabel( name, value ):
    if name == "abc":
    [...]
can perfectly be:
Python:
label whatever:
    $ params = { "name":"abc", "value":1 }
    jump myCalledLabel
[...]
label myCalledLabel:
    if params["name"] == "abc":
    [...]

I don't think some returns will ever be reached, so merely adding them will not help.
Then there's a enormous design flaw in the code.


Most of these are the same 'game/_story/hunt/plains.rpy'. Which is strange, because the main label inside that is jumped into and out of, and the few calls inside all have returns and I'm 90% plus certain they also always reach these returns.

Edit: The other common one is a label (and that one is by it's label name) that is never called, only jumped into and out of.
If I remember correctly, you said that the save file size grow significantly over time. This mean that there's none returned called labels. While it's perhaps not the only cause, the huge call stack is part of the problem, and its size increase over time.


I have never seen a return stack size of more than 4-5 in my game, and I call things all the time.
The worse I seen before was with a totally broken, and abandoned since years, game that goes up to 100 entry in the call stack. I admit that I'm impressed by the size of this one.
 
  • Like
Reactions: Jman9

Jman9

Engaged Member
Jul 17, 2019
2,295
957
Python:
        while renpy.call_stack_depth():
            renpy.pop_call()
Doesn't work, because apparently that 'main_menu' label has snuck a call to the main menu in there and thus the above is merely an elaborate way to exit the game. Nope, that's not it. But still, something is booting me back to the menu.

Can I check the labels before popping?

Because there's screen that can be shown and not called.
A called screen can be hidden and not 'Return()'-ed, no? Which means a called screen is not required to be returned from, as I understand it.

It imply a bit more codding, but you can perfectly pass the arguments through an external dict...
Yeah, I thought of that. Means a lot of combing the scripts, but I guess I do need to close this insanity down at some point.

Then there's a enormous design flaw in the code.
Yep, that one's entirely on me. Edit: Or maybe not entirely, but definitely mostly.

If I remember correctly, you said that the save file size grow significantly over time.
See my last post. Wiping the rollback cut down on all of this by a huge margin. I guess the call stack itself is still there and growing, so that's not all of it, but it's like 90%+ better now.

I admit that I'm impressed by the size of this one.
I aim to please! :D



Edit2: Hmm, I pruned the call stack down to 1, because that first call boots me back to the main menu. Got saves down to 700+ KB instead of 900+. OTOH, setting renpy.game.log.log = [] seems to have a more immediate effect. Unfortunately, Ren'Py is insisting on repopulating this, and while it's not going overboard (yet?), it is increasing saves a little. I also can't put a call (heh, function call) to this before a save, because rollback doesn't apparently like this and complains about emptyness, while manually clearing it and then saving works. Strange.

Why is the engine so insistent on creating the rollback logs when rollback has been turned off as hard as I am able to do it?

Edit3: Although without the 20K entries in each rollback entry, I guess this is manageable. Especially since I found that I can still trim the length of the overall log via config.rollback_length. I suppose the log issues are mostly fine now. I only I knew what the other cause(s) of the mini-freezes might be.
 
Last edited:

Deleted member 2282952

Developing I SCREAM
Game Developer
May 1, 2020
416
869
I'm a bit too lazy to read everything if it was discussed, but there are a bunch of tools that optimize your code like that new one from OpenAI (or DeepMind), DeepCode, etc. Those are pretty solid from what I've heard, and all can handle Python.
 

osanaiko

Engaged Member
Modder
Jul 4, 2017
2,243
3,831
I'm a bit too lazy to read everything if it was discussed, but there are a bunch of tools that optimize your code like that new one from OpenAI (or DeepMind), DeepCode, etc. Those are pretty solid from what I've heard, and all can handle Python.
With all due respect that's not helpful to the issue here - it's fundamental to the way Jman has coded his game to "call" to labels and then not consistently "return" to remove the frame from the callstack. If you don't read the thread it's hard to make a useful contribution based on thread title only.
 
  • Like
Reactions: anne O'nymous