How do I pause the redraw in XNA?
Asked Answered
D

2

15

I made an XNA image viewer, but it always redraws the scene, even if it's not changing, and it's making my netbook burn like hell, so I'd like it to pause drawing when nothing's changing.

Reducing framerate to 1 is one way to keep it cool, but it results in laggy output.

How do I prevent the redraw while there is no input?


This problem was solved, but another problem was found — the game consumes a lot of CPU when its window is in focus, but when it's not, it only takes about 1% of the CPU. See this question for details on how to solve this other problem:

How to reduce XNA game CPU usage while nothing worth computing is happening?

Dreamy answered 15/5, 2013 at 0:6 Comment(2)
That was a quick downvote and a close vote. Care to help improve the question?Dreamy
It would be nice if people would leave comments to explain their downvotes. This seems like a perfectly legitimate question with a definite answer.Brochu
M
11

You can use the Game.SupressDraw method for this purpose. From the remarks at the link:

Call this method during Update to prevent any calls to Draw until after the next call to Update. This method can be used on small devices to conserve battery life if the display does not change as a result of Update. For example, if the screen is static with no background animations, the player input can be examined during Update to determine whether the player is performing any action. If no input is detected, this method allows the game to skip drawing until the next update.

As an example, the following code will only call the Draw function once. When I tested this I noticed that the CPU usage was high (70%) without the SuppressDraw call. When using SupressDraw, the CPU usage dropped dramatically (to less than 15%). Your numbers may vary.

public class Game1 : Microsoft.Xna.Framework.Game
{
    ...
    private bool _drawn = false;
    protected override void Update(GameTime gameTime)
    {
        if (_drawn)
            SuppressDraw();
    }

    protected override void Draw(GameTime gameTime)
    {
        GraphicsDevice.Clear(Color.CornflowerBlue);
        spriteBatch.Begin();
        /* draw stuff here */
        spriteBatch.End();

        _drawn = true;
    }
    ...
}

Note that SupressDraw only suppresses one Draw call. To prevent more calls to Draw, you have to continually call SupressDraw in Update. Hopefully that makes sense.

Mcintosh answered 19/5, 2013 at 3:41 Comment(3)
This definitely works, though it does not save me as much CPU as I hoped it would. Performance analysis says with each draw frame suppressed, the main update method gets all attention, but still it costs as much CPU as before. I guess the question asked is answered, but I still need to know how to reduce CPU cost of doing almost nothing.Dreamy
@Dreamy - without seeing your code, it is difficult to provide an answer as to why your CPU usage did not get reduced. As you can see with my answer above, it did reduce my sample program's CPU time - from 70% usage to around 15%.Mcintosh
As you said — percentage varies. I guess the Draw call wasn't actually consuming any significant amount of resources, whereas the whole application was using as much as it could get (and still does). So basically this became another problem — determining what exactly consumes CPU while nothing special is actually going on besides waiting for input and drawing a frame 60 times per second.Dreamy
C
0

As the accepted answer says, you can use SuppressDraw to avoid drawing an unchanged frame. Your Update-method will run regardless, however, which is why it still uses more CPU than you expect. To avoid using as much CPU, you need to somehow avoid updating things you know haven't changed since the last frame.

I assume you would know this the same way as you know when to suppress draw, so what you need to do is rearrange your Update-method like this:

private void Update(GameTime gameTime)
{
    var frameHasChanges = DetectChanges(); //This method you need to figure out yourself
    if (!frameHasChanges)
    {
        SuppressDraw();
        base.Update(gameTime);
        return;
    }

    //Do the rest of normal Update

}

As a final note; I have never had to use this in any game I have ever made. Which I feel suggests you may have some other, underlying problem with performance. I have, however, used many techniques to avoid rendering text and so on each frame (causes boxing/unboxing). Ask about this in comment if you want to know more.

Cardholder answered 23/5, 2013 at 12:59 Comment(1)
While in general this is the way to go, in this specific case, as I said in the question, the update method is very simple and it can't hog all the CPU just for checking if there was change in input states (and it doesn't, I quadruple-checked it). The solution was found through a different question to the problem that appeared after solving the no-redraw thing in this question. See question footnotes for details.Dreamy

© 2022 - 2024 — McMap. All rights reserved.