In my menu scene, I rotate the camera slowly to animate the skybox. I also have a function that selects a random file image within a folder and creates a texture, then adds the texture to a UI image. Half of my menu scene is a slideshow of random images and is swapped out every few seconds.
While a new slide is being added the camera rotation stalls. How can I ensure the camera rotates smoothly while performing a slide swap? Rotate camera is a single line in update script and is attached to the camera. And the slideshow function is in my menu manager script.
I’m not on my dev box right now so I don’t have code to share. But I’m sure you’re correct that my slideshow function completely tanks the framerate. I actually haven’t done a profile to verify, but since it’s on a timer, and fires every few seconds, it’s pretty obvious. The function selects a random file, loads the file, converts to a texture, and sets the UI image. I don’t know what is more costly, but I’m guessing it’s the reading of the image file, converting to a texture and cashing is probably costly too. The Images are screenshots, so they are fairly large, and reduced to about 33% at the UI size
My obvious next step is to do these steps separately, which will either fix the problem or show which one is the problem.
I thought before I try to solve this on my own, maybe someone out there has done this before and can share the method used or verify my next step is in the right direction. Lastly, if what I’m about to try only reduces the problem, what can I try next? Is threading possible? Is there a way to force the update to fire on the camera script regardless of framerate?
Yes, it’s almost certainly reading the files and building the texture. You should do that all at once, before displaying the menu. (You should also reduce the files as much as you can without sacrificing visible quality.)
No, there is no way that threading or anything else is going to avoid the need for that. Several of the steps in reading a file and converting to a texture are atomic operations, and Unity is not a multithreaded engine. So, you simply can’t do that work while you expect to have a smooth framerate — do it at some other time when you don’t care.
I’m still not home yet Joe, but you’ve cinched the cause. Now I have to find a way to mitigate it issue. My game allows the user to take screenshots. They are saved in a specific folder. While my menu scene sits idle waiting for the user to do something, I rotate the skybox (it’s a space game and it’s cheap animation) and I do a slideshow of the screenshots from the screenshots folder. Since there is no limit but drive space for the number of screenshots, this can be abused and most likely will. Due to the amount possible, preloading at startup is impossible and would not include recent screenshots. I’ve thought of many ways to manage the screenshot volume, but all have issues I do not like and a player of this game would not appreciate at all.
I’m going to break this into three separate functions in hopes of reducing the load. I can’t break it down any more. This whole scenario doesn’t seem that unique. Am I the first to think of this concept? In all the years of Unity and all the Unity developers, surely someone has done something similar to this in the past.
Joe thanks, you saved me a lot of wandering, my only fix available is going to work or not.
OK, so given all that, I think you have to find a way to carve the work up into non-atomic chunks, so you can do a little bit each frame.
I don’t think there’s any way to do that with Texture2D.LoadData; it’s the only way I can see to create a texture from PNG or JPEG data at runtime, and there’s no asynchronous or partial-load function. So, let’s hope that’s fast (it probably is).
The real bottleneck may be in loading from disk. And that you can break into chunks. Instead of something like File.ReadAllBytes, you could use FileStream.Read to read the file into memory a chunk at a time. Yield (or return from Update) after each chunk, and if the chunks are small enough, it shouldn’t cause a glitch in the frame rate.
Then when you’ve finally got the entire file in memory, you do Texture2D.LoadData (and nothing else) on the next frame, and hopefully that will do it!
Some random thoughts, in the approximate order you should try them.
Step 1: Profile it. Find out exactly which call is causing your slow down
Step 2: Thread it. You only need to be on the main thread for calls that involve the Unity API. Push all of the data reading onto another thread.
Step 3: Cheat. Preload a dozen or so random screen shots at scene loading. Next time you open the scene load a dozen or so different screenshots. Create the illusion of the slide show of thousands of images without actually having to load thousands of images
Step 4: Cheat in a different way. Break each image up into a dozen smaller textures, and load these all in one per frame. Then show them all as a single image. There are a bunch of technical issues with this solution, I wouldn’t go this way unless all other options fail.
Step 1 wasn’t necessary, I already knew what the issue was. The only insight I’d gain is how bad.
Step 2 please tell me how this is done. Joe said no threads, how do I create and run in a thread?
Step 3 was a consideration. I can imagine someone getting a highscore, taking a screenshot, and the following day running again and waiting for their screenshot to come up, but never will. This would also take away other functionality I’ve have in place (see below).
Step 4 is not an option as I’d have to load the entire file into memory to create the cells. Don’t get me wrong I kind of like the idea as an effect. Make the image a 4x4 grid and fill in a random cell one-at-a-time until it is complete. But otherwise I don’t think this would solve the bottleneck due to loading.
There is some info on using the WWW class to stream a local file, which might be an option. I have a message pop up at game startup if there are more than 50 screenshots. I really don’t know what a good figure to use, 50 seems a lot to me, but I’m not a screenshot taker and usually forget the option is there in games. I also have a make copy on desktop function and a delete function for each screenshot shown. I’m also looking for a cross platform print solution too.
You actually don’t. You know that choosing a random file and loading it and making a texture and displaying it on screen is expensive. The profiler will let you pinpoint exactly where that expense is. The response for loading a file being slow is different to the response for creating a texture being slow.
So which is it? Not much point going further without knowing what we are up against.
Well, haven’t solved the problem, but have some interesting observations in my quest to solve.
File.ReadAllBytes is not a bottleneck and large files are read pretty fast. Loading a file with a single frame does not produce any lag. I’m guessing microsoft has highly optimized the file functions. The thing I though would be most costly does not seem to be an issue.
Turning the byte data into a texture causes a slight stall and assigning the sprite to the UI image also causes a slight stall.
I don’t think I’ve exhausted my options yet, but they are definitely narrowed. Assumptions were proven wrong and my problem has been redefined. Stand-by for chapter 3.
Ok, I have things working as well as it’s going to get, and I am not unpleased by the results. I still have a slight hesitation when converting the byte array into a texture. I also found setting the UI sprite texture was a costly and unnecessary step.
I define a private Texture var and assign the UI image texture as that texture var. Converting the byte array into the texture var also updates the UI image. This is where @Kiwasi suggestion to break into smaller images and convert a segment per frame would completely solve this, I sure. I’m happy with the 99%+ I have now, I really do have much more critical issues within the game that should and need to be attended to, but this was one of itches I was compelled to scratch as this problem nagged at me for weeks.