DynamicDisplayable updates too frequently, draws way too much CPU

Discuss how to use the Ren'Py engine to create visual novels and story-based games. New releases are announced in this section.
Forum rules
This is the right place for Ren'Py help. Please ask one question per thread, use a descriptive subject like 'NotFound error in option.rpy' , and include all the relevant information - especially any relevant code and traceback messages. Use the code tag to format scripts.
Post Reply
Message
Author
randallarrow
Newbie
Posts: 11
Joined: Fri Nov 23, 2018 2:20 am
Contact:

DynamicDisplayable updates too frequently, draws way too much CPU

#1 Post by randallarrow »

So, what I'm trying to do may be a bit complicated to explain, though in principle it doesn't seem that weird to want to do.

I want to have multiple characters, each with customizable attributes, who can change their emotions over the course of the game.

So here's what I'm doing so far:
Each character is a Composite of several body parts and clothing items inside a DynamicDisplayable. So far so good.

Since I want players to be able to customize colors, I used AlphaMask to generate most of the images: That way instead of having a different image file for each color of eyes, hair, clothes, etc., I can have one image file for each color that can be used for all things, and alpha masks for the eyes, hair, and clothes.

This basically seems to work, in itself: The characters are rendered and they change color and emotion appropriately.

Here's the problem: If more than one character is on screen (and sometimes even with just one character for a long time), the system gradually ramps up drawing more and more CPU and often creating weird image artifacts.

Now, my understanding was that DynamicDisplayable was supposed to be used for things that update infrequently; but in fact the problem seems to be that it's updating ALL THE TIME. I actually intentionally added a renpy.time.sleep(0.05) for a 50 ms delay in the composite_person function that DynamicDisplayable calls just to prevent it from running as fast as the CPU would allow. I have that function printing to the console every time it's called, and it's basically limited only by that 50 ms delay.

Is there some way to tell DynamicDisplayable to stop updating like that? I would only need it to update about once every second, or better yet specifically when the conditions change.

Alternatively, is there some other way to implement these images? I tried using static Image() objects for each emotion, but Renpy won't let me generate them except in an init block, which of course I can't do, because I want user customization. (This also seems really arbitrary, by the way.)

This seems like a very basic functionality that most developers would want, so I feel like I must be missing something.

randallarrow
Newbie
Posts: 11
Joined: Fri Nov 23, 2018 2:20 am
Contact:

Re: DynamicDisplayable updates too frequently, draws way too much CPU

#2 Post by randallarrow »

It seems to really depend upon the AlphaMask in some way I can't quite understand. When it's just static PNG images that are being used in the Composite, everything behaves as it should. The problem with that is that there are so many possible combinations, even with each body part separated out into a different layer: The game was ballooning to over 2 GB of assets---that's with PNGQuant compression---before we came upon the AlphaMask solution.

But it shouldn't be re-generating the AlphaMask objects at all. The code is specifically set up to check whether there has been a change in that body part, and if not, to keep the current image rather than generating a new one. And based on print statements in the code, this appears to be working as designed: the functions to generate new AlphaMask objects aren't being called.

Does the mere EXISTENCE of an AlphaMask create high demands on CPU? Is each AlphaMask constantly being re-rendered for some reason? If so, what can I do about that?

DragoonHP
Miko-Class Veteran
Posts: 758
Joined: Tue Jun 22, 2010 12:54 am
Completed: Christmas
IRC Nick: DragoonHP
Location: Zion Island, Solario
Contact:

Re: DynamicDisplayable updates too frequently, draws way too much CPU

#3 Post by DragoonHP »

Can't be sure without looking at your code, but you can tell the DynamicDisplayable to wait a certain amount of time before redrawing. You don't need to call renpy.time.sleep

Code: Select all

    def show_countdown(st, at):
        if st > 5.0:
            return Text("0.0"), None #None tells Ren'Py to not redraw the Displayable at all
        else:
            d = Text("{:.1f}".format(5.0 - st))
            return d, 0.1 # 0.1 tells Ren'Py to redraw the displayable after 0.1 seconds
from https://www.renpy.org/doc/html/displaya ... isplayable

User avatar
ComputerArt.Club
Veteran
Posts: 427
Joined: Mon May 22, 2017 8:12 am
Completed: Famous Fables, BoPoMoFo: Learn Chinese, Santa's workshop, Cat's Bath, Computer Art Club
Location: Taiwan
Contact:

Re: DynamicDisplayable updates too frequently, draws way too much CPU

#4 Post by ComputerArt.Club »

My layered image sprites have animated attributes (for talking and blinking and sometimes running. The game is for Android but I often worry about how much strain these features will cause. How are you measuring the CPU isage? Task manager or something else? What is a good target to aim for?

User avatar
Remix
Eileen-Class Veteran
Posts: 1628
Joined: Tue May 30, 2017 6:10 am
Completed: None... yet (as I'm still looking for an artist)
Projects: An un-named anime based trainer game
Contact:

Re: DynamicDisplayable updates too frequently, draws way too much CPU

#5 Post by Remix »

If your Dynamics are correctly using some form of cache in their renders I'd suggest moving up the chain a bit and just slowing things down at a higher level.

Composite naturally will re-compute at every frame draw (so basically as fast as possible) yet your system (from what I understand) doesn't need that amount of speed. So, maybe rather than having the top level as a Composite, have it as a DynamicDisplayable that returns a Composite and set the redraw time to 1/10th of a second or even slower.
As DragoonHP says, you wouldn't want to add a time.sleep, just alter the second part of the dynamic displayable return tuple (for the full image and for each composited part)

Realistically though, you might want to be looking at caching the returned composite then, on redraw, checking if any used variable or tag attribute has changed and recompiling or using cache for the return.
Frameworks & Scriptlets:

randallarrow
Newbie
Posts: 11
Joined: Fri Nov 23, 2018 2:20 am
Contact:

Re: DynamicDisplayable updates too frequently, draws way too much CPU

#6 Post by randallarrow »

I think is what I was looking for:
"Can't be sure without looking at your code, but you can tell the DynamicDisplayable to wait a certain amount of time before redrawing. You don't need to call renpy.time.sleep"

I will try that first. In case that fails, can you explain this method a bit more? "So, maybe rather than having the top level as a Composite, have it as a DynamicDisplayable that returns a Composite and set the redraw time to 1/10th of a second or even slower."

Right now the code looks like this:
image you = DynamicDisplayable(composite_person, you)
and the composite_person() function returns a Composite.

How does your solution differ from that?

User avatar
Remix
Eileen-Class Veteran
Posts: 1628
Joined: Tue May 30, 2017 6:10 am
Completed: None... yet (as I'm still looking for an artist)
Projects: An un-named anime based trainer game
Contact:

Re: DynamicDisplayable updates too frequently, draws way too much CPU

#7 Post by Remix »

Ah, you're already doing like I suggested, just the composite_person() function should return a tuple of both the output displayable and the recall time...

Hard to say more without seeing your actual code or a more full synopsis of it
Frameworks & Scriptlets:

randallarrow
Newbie
Posts: 11
Joined: Fri Nov 23, 2018 2:20 am
Contact:

Re: DynamicDisplayable updates too frequently, draws way too much CPU

#8 Post by randallarrow »

Changing the update time of DynamicDisplayable largely fixed the problem. I knew there should be a simple way to do this; I feel silly now for not seeing it.

randallarrow
Newbie
Posts: 11
Joined: Fri Nov 23, 2018 2:20 am
Contact:

Re: DynamicDisplayable updates too frequently, draws way too much CPU

#9 Post by randallarrow »

Correction: The problem is NOT fixed. Reduced, but not fixed.
I don't think DynamicDisplayable is the culprit after all. I think it's AlphaMask.
Even when I generate images as static Image objects in an init: statement, sometimes displaying those images results in weird flickering artifacts and then the game hangs. But it only happens if the images are generated using AlphaMask, not if they're created directly from PNG files.

This doesn't make any sense to me, because the image should be static, right? It shouldn't need to call any functions or generate anything. It should be as if I had just told it to display a PNG. But it's not.

In fact, if there is a way to generate PNG files and save them to the disk and then make images with those, I'm willing to do that at this point. AlphaMask seems like a lifesaver in terms of disk space, but it has given me nothing but trouble.

randallarrow
Newbie
Posts: 11
Joined: Fri Nov 23, 2018 2:20 am
Contact:

Re: DynamicDisplayable updates too frequently, draws way too much CPU

#10 Post by randallarrow »

Is there a way to save a displayable as a PNG image? I know PIL can do that, but I'm not sure about Renpy's image handling system (which conflicts with PIL in many ways). So far the best I can think of is to show the displayable on screen and then take a screenshot.

randallarrow
Newbie
Posts: 11
Joined: Fri Nov 23, 2018 2:20 am
Contact:

Re: DynamicDisplayable updates too frequently, draws way too much CPU

#11 Post by randallarrow »

I can confirm that showing the displayable, taking a screenshot, and then cropping and adding transparency to that screenshot, allows me to generate usable PNG images, and furthermore that DynamicDisplayable doesn't draw excess CPU or hang the game when it uses only such images.

This is at least a partial workaround, but it's still not what I wanted, because if I want to allow the level of character customization originally planned, I would still need an absurd number of PNG images. So I would really like to find a way to stop AlphaMask() from causing the game to hang. Is there some parameter I should be passing to AlphaMask() telling it not to update too frequently (as with DynamicDisplayable)?

Post Reply

Who is online

Users browsing this forum: No registered users