DynamicDisplayable updates too frequently, draws way too much CPU
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.
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.
-
- Newbie
- Posts: 11
- Joined: Fri Nov 23, 2018 2:20 am
- Contact:
DynamicDisplayable updates too frequently, draws way too much CPU
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.
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.
-
- Newbie
- Posts: 11
- Joined: Fri Nov 23, 2018 2:20 am
- Contact:
Re: DynamicDisplayable updates too frequently, draws way too much CPU
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?
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?
-
- 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
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
from https://www.renpy.org/doc/html/displaya ... isplayable
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
- 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
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?
Facebook:
Renpy Creators and Enthusiasts Facebook Group
My latest game:
Famous Fables: Read, Learn and Play - Android App for teaching children to read
Creative Commons stuff:
Shape transitions,
100+ 360° photos of Japan,
- 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
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.
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:
- Speech Bubble dialogue system
- Multiple Notify with ATL and history
- (WIP) Radial Masking - needs updating to use Shader
- 7.4 - Smooth Tinting using ATL and matrixcolor
- Several other repositories there too
-
- Newbie
- Posts: 11
- Joined: Fri Nov 23, 2018 2:20 am
- Contact:
Re: DynamicDisplayable updates too frequently, draws way too much CPU
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?
"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?
- 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
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
Hard to say more without seeing your actual code or a more full synopsis of it
Frameworks & Scriptlets:
- Speech Bubble dialogue system
- Multiple Notify with ATL and history
- (WIP) Radial Masking - needs updating to use Shader
- 7.4 - Smooth Tinting using ATL and matrixcolor
- Several other repositories there too
-
- Newbie
- Posts: 11
- Joined: Fri Nov 23, 2018 2:20 am
- Contact:
Re: DynamicDisplayable updates too frequently, draws way too much CPU
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.
-
- Newbie
- Posts: 11
- Joined: Fri Nov 23, 2018 2:20 am
- Contact:
Re: DynamicDisplayable updates too frequently, draws way too much CPU
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.
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.
-
- Newbie
- Posts: 11
- Joined: Fri Nov 23, 2018 2:20 am
- Contact:
Re: DynamicDisplayable updates too frequently, draws way too much CPU
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.
-
- Newbie
- Posts: 11
- Joined: Fri Nov 23, 2018 2:20 am
- Contact:
Re: DynamicDisplayable updates too frequently, draws way too much CPU
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)?
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)?
Who is online
Users browsing this forum: No registered users