Steady 60 fps suddenly dropping to exactly half

Hi, I’m having some trouble using Monogame on Android. I’m building a simple platformer-type game, and there are very few objects even being drawn or updated. Furthermore I have narrowed the issue down to the background that is being drawn.

I test on two different devices, a LG G4 and a Samsung Galaxy Note 4. On the Note 4 I don’t really notice the issue as much, though I do see the fps drop to exactly half randomly, but it only does it once and not often, so it isn’t noticeable.

On the LG G4 however I’ve noticed that roughly 20-30 seconds after the app is running, the FPS starts to stutter back and forth between around 60 fps, and exactly half of the 60 fps. FPS Example: FPS: 59.99988000024 FPS: 29.99994000012 (from the console log). It usually lasts at least 3 seconds or so, and sometimes even longer. Then it will correct itself and be steady at the 59 fps, and then do the same thing.

I’ve tested it a couple of times, and I actually have a second app that I noticed is doing the same thing. Which I’m thinking is also the background. I then created a new app, only using the background, and it still seems to be happening.

I’m really new to all of this, so I’m hoping it’s just something I am doing wrong. The image size is 1080x3840. I read about the power of 2 for graphics, and I tried making it 4096x4096 to try it, but it didn’t change anything. I’ve also tried reducing the image size, and it didn’t change anything. Setting the FPS to 30 avoids the problem, but I would like to figure out my problem so I make my app the right way. I’ve tried searching but cannot find anything similar to this problem.

If there is anything I can provide to get this resolved, I certainly will. Thank you in advance for any help!

Vsync doing what it should do, syncing fps with refresh rate (thus droping it to 30, one frame per two refreshes when it cant reach 60fps). You can try to turn it off or figure out why are your fps unstable and optimize app accordingly.

Thanks for the response Raven, I tried out different things and changing the Vsync didn’t help. I notice that when I make the image smaller it runs fine, but when the image is larger or scaled to fit, even with the smaller resolution, it lags. Do you know why the same image scaled larger would cause performance problems? I used 480x800, and if it’s scaled it has the issue, if it isn’t, it runs perfect.

This happens when I simply draw one image, with no logic in the entire app. Is there anything specific that I should to do optimize the app? I’ve also tried different images in place of the background image and I get the same problem.

So, you have a game loop that draws a single image? On the LG G4 with Adreno 418 & snapdragon 818?

Show us you update/ draw code.
Did you load/initialize your sprittebatch, textures, states, etc once in the LoadContent()?
In cases like those I run a profiler, it’ the only way to know for sure what is happening instead of guessing. I also have a windows project, it’s much easier to develop,debug,profile on windows and then at the end of the day test & verify that everything work good on devices as well.

Here is my code, like I said I stripped everything down to find the problem being when the background is drawn too large, this is the entire application (which still has the problem):

public class Game1 : Game
    {
        GraphicsDeviceManager graphics;
        SpriteBatch spriteBatch;

        Texture2D background;

        public Game1()
        {
            graphics = new GraphicsDeviceManager(this);
            Content.RootDirectory = "Content";	            
            graphics.IsFullScreen = true;	
            graphics.SupportedOrientations = DisplayOrientation.Portrait;
        }

        // Initialize
        protected override void Initialize()
        {
            // TODO: Add your initialization logic here
            base.Initialize();
				
        }

        // Load Content
        protected override void LoadContent()
        {
            // Create a new SpriteBatch, which can be used to draw textures.
            spriteBatch = new SpriteBatch(GraphicsDevice);

            //TODO: use this.Content to load your game content here 
            background = Content.Load<Texture2D>("bg");
        }

        // Update
        protected override void Update(GameTime gameTime)
        {
            // For Mobile devices, this logic will close the Game when the Back button is pressed
            // Exit() is obsolete on iOS
            #if !__IOS__ &&  !__TVOS__
            if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed ||
            Keyboard.GetState().IsKeyDown(Keys.Escape))
            {
                Exit();
            }
            #endif
            // TODO: Add your update logic here	

            base.Update(gameTime);
        }

        // Draw
        protected override void Draw(GameTime gameTime)
        {
            graphics.GraphicsDevice.Clear(Color.CornflowerBlue);

            var fps = 1 / gameTime.ElapsedGameTime.TotalSeconds;
            Console.WriteLine("FPS: {0}", fps);
		
            //TODO: Add your drawing code here
            spriteBatch.Begin();

            spriteBatch.Draw(background, new Vector2(0, 0), Color.White);

            spriteBatch.End();
            
            base.Draw(gameTime);
        }
    }

If the bg image is 480x800, without scaling it, it runs fine.
If the bg image is 480x800, and is scaled, the fps stutters often at half fps, and lags.
If the bg image is 1080x1920, without scaling it, it has the same problem.
If the bg image is any bigger, without scaling it, it has the problem as well.

Ok, maybe you should find other means to get the fps. An onscreen Draw for example.
Writing to the console on every frame is most probably the problem here.

I thought that may be a problem, so I tried removing it from the actual game I am making that had the problem, and I still notice the lag. The lag doesn’t kick in until about 20 seconds in, but it’s very noticable. Could it be a resolution problem? Should I be creating at 1080p with 60 fps? Is that normally what games run at? Is there a better solution to scaling rather than a transformMatrix in the spriteBatch (how I scaled the 480x800 image)?

The lag doesn’t kick in until about 20 seconds in, but it’s very noticable.

This sounds like the GC. In your Game you generate garbage. Run under a profiler to find out.

I thought it may be a possibility that the GC was causing the problem, I don’t have profiling for Xamarin currently, is there anyway I can change the GC to test that as the problem? Or is there another profiler I can use?

If it was the GC, you’d see lag spikes, not a consistent 30fps. The only way it could be the GC is if you’re creating so much garbage every frame that the GC is kicking in every frame. But from your code above, the only garbage you’re creating is the Vector2 when you draw the background texture.

Definitely try profiling your game. Either use the Xamarin Profiler, or use a Stopwatch to profile specific areas. If you’re triggering the issue with that code you posted and with a specific background image, post the image so that we can test it ourselves.

I was talking about the game. The lag kicks in after 20-30 sec and the fps stutter back and forth between 60 and 30. That’s almost certainty the GC. As for the test code above it will run perfectly fine at 60fps without the slow console.Write(). That is the problem when you compare apples with oranges, you are doomed to run into cycles.

@Attict
Add an onscreen fps counter on the test proj to verify that the resolution is not the problem. The 808 is one of the top ARM chips right now, is well capable to draw multiple textures before hitting the fillrate limit.
http://blogs.msdn.com/b/shawnhar/archive/2007/06/08/displaying-the-framerate.aspx

Create a Windows project along with the Android project to develop and test on windows for shorter iteration times. The VS 2015 for example is free and has a good profiler.
Here is an article that explains Garbage in C#
http://blogs.msdn.com/b/shawnhar/archive/2007/06/08/displaying-the-framerate.aspx

Ah, I misunderstood. I missed where he said it flickered between 60 and 30 (i found it). Yes, it definitely sounds like the GC.

Profiling should definitely be the first step. I linked the Xamarin Profiler, because I’m pretty sure the VS 2015 doesn’t profile Xamarin apps. (At least I haven’t been able to get it to do so) Also, he might not be on Windows.

My guess is you are running out of texture memory on the GPU and it is having to swap out textures from system memory to the GPU (although that shouldn’t be happening with just 1 texture). This would be why a smaller texture alleviates the problem as you don’t run out of texture memory.

Scaling everything on each draw call might also be an issue, A render target might help with this. Rather than scaling all the draw calls, just render everything to a fixed size render target then just scale the render target to fix the screen.The article [1] might help :slight_smile:

The idea of using the Windows project to debug is good, however the .net and Mono GC’s are different so you will get different behaviour and I suspect the stuttering won’t be a problem on a desktop project. But it might help see if there is any garbage being generated.

Android devices vary wildly, some devices will be able to handle a 1080x3840 texture without any issue, but I suspect most won’t especially as 1080x3840 is basically 4 full screen textures.

The other thing to try is texture compression :slight_smile: if you are using .pngs directly your single image might be small on disk but will be

1080x3840x4 = 16588800k = 16meg of texture memory.

If you add in all the other textures on top of that… it will quickly add up and you’ll run out of texture memory performance will start to suck [2].

If you are using the content pipeline (in the stable release) by default android uses DXT compression which is currently converted to Color on devices that do not support DXT which are ALL devices Not using an Nvidia chip atm.

Looking at the spec the LG 4G is using Adreno 418 which uses ASTC (or ATITC) texture compression.

You will need to try the Development builds as that has more texture compression options, in which case
if your image has no Alpha at all you can try to set the Compression mode to ETC1 which should help and will work on all android platforms. Alternatively Color16 bit will will dither the image but probably reduce the size by half which might help and again will work on all android platforms. Ideally you would use ATITC as that will give the best compression for that device because of the GPU it uses.

If you do go down the route of using device specific texture compression you will need to release an .apk for each GPU type you want to support (and include the correct textures). This has come up before so I’ll try to write up a blog post/docs on the subject :slight_smile:

[1] http://www.infinitespace-studios.co.uk/general/monogame-scaling-your-game-using-rendertargets-and-touchpanel/
[2] http://www.infinitespace-studios.co.uk/general/monogame-content-pipeline-why/

Trying to test out everything still, but I have managed to test out the on screen FPS monitoring and I still seem to have the problem. Though not having the FPS in the console helped me see more of the console log. When the GC started, I didn’t notice anything change, it actually kept the FPS at 59, the problem occured around 10-20 seconds after the first GC minor. I noticed that whenever the app starts, I get skipped frames, and I’m not sure if that’s involved in my problem. This log is with using the image size of 1440x2500 with the screen drawing the FPS.

Here’s the log:

[Choreographer] Skipped 54 frames!  The application may be doing too much work on its main thread.
[Timeline] Timeline: Activity_idle id: android.os.BinderProxy@34b5b11 time:48344951
[Mono] GC_BRIDGE waiting for bridge processing to finish
[Mono] GC_OLD_BRIDGE num-objects 17 num_hash_entries 17 sccs size 17 init 0.00ms df1 0.02ms sort 0.03ms dfs2 0.05ms setup-cb 0.01ms free-data 0.01ms links 0/0/0/0 dfs passes 0/0
[Mono] GC_MINOR: (Nursery full) pause 54.30ms, total 54.40ms, bridge 0.00ms promoted 1536K major 1536K los 36408K
[ViewRootImpl] ViewRoot's Touch Event : ACTION_DOWN
[ViewRootImpl] ViewRoot's Touch Event : ACTION_UP
[ViewRootImpl] ViewRoot's Touch Event : ACTION_DOWN
[ViewRootImpl] ViewRoot's Touch Event : ACTION_UP
[art] Explicit concurrent mark sweep GC freed 45885(1576KB) AllocSpace objects, 8(672KB) LOS objects, 38% free, 51MB/83MB, paused 1.223ms total 42.292ms
[Mono] GC_OLD_BRIDGE num-objects 1 num_hash_entries 1 sccs size 1 init 0.00ms df1 0.01ms sort 0.04ms dfs2 0.00ms setup-cb 0.00ms free-data 0.00ms links 0/0/0/0 dfs passes 2/1
[Mono] GC_MINOR: (Nursery full) pause 48.18ms, total 48.27ms, bridge 0.00ms promoted 1952K major 1952K los 36408K

Thank you guys for all of your help so far!