Счетчик FPS

Счетчик количества кадров в секунду (FPS counter)

В рамках этой статьи мы разработаем небольшой счетчик количества кадров в секунду (Frames per second — FPS).

Ни для кого не секрет, что счетчик FPS является основным (или одним из основных) инструментом для определения проблем с производительностью в графических приложениях. Как понятно из названия, счтчик просто определяет сколько кадров может нарисовать ваше приложение за секунду. Соответственно, можно определить сколько времени требуется на построение одного кадра.


Оптимально включать счетчик в каждое приложение в самом начале, таким образом можно уловить тот момент, когда значение сильно упадет и быстро разобраться из-за чего это происходит (возможно даже не использовать какой-то эффект вовсе, если не получается его оптимизировать).

Когда количество кадров в секунду падает до 20 – это уже плохо. Игрок увидит заметные тормоза, скачки при резких исменениях положения камеры и объектов на экране. Большие значения говорят о том, что еще есть простор для творчества, например, можно добавить какой-нибудь эффект.

Можно нагружать свой движок, например, добавлением большого количества моделей, источников света, эффектов в сцену, таким образом можно определить возможности вашего движка. Так, например, если окажется, что движок может справиться только с N моделями на экране и не поддается оптимизации, можно просто разработывать уровни с расчетов на это огрничение и все будет нормально.

Разработка счетчика кадров в секунду не является сложной задачей, но он пригодится мне в дальнейшем.

В XNA Framework максимальное количество кадров в секунду по умолчанию установлено равным 60. Это означает, что счетчик количества кадром мало что нам даст. Мы заметим снижение FPS только тогда, когда оно окажется ниже 60, то есть когда уже будет поздно. По-этому, первое, что нужно сделать – это отключить эти значения по умолчанию.

Сделать это можно, например, в конструкторе класса, наследуемого от Game:

public Game1()

{

graphics = new
GraphicsDeviceManager(this);

Content.RootDirectory = «Content»;

graphics.SynchronizeWithVerticalRetrace = false;

IsFixedTimeStep = false;

}

Счетчик FPS должен максимально быстро и просто подключаться к новому приложению, так что я оформлю его в виде DrawableGameComponent.


class
FPSCounter : DrawableGameComponent

{


public FPSCounter(Game game) : base(game)

{

}


public
override
void Update(GameTime gameTime)

{

}


public
override
void Draw(GameTime gameTime)

{

}

}

Теперь рассмотрим два способа определения FPS:

  1. В методе Draw мы знаем количество секунд, прошедшее в предыдущего вызова Draw. По сути – это и есть время, затрачиваемое на обработку одного кадра. То есть 1/(количество секунд) = FPS. Проблем с таким счетчиком несколько, основной я считаю то, что полученное значение слишком быстро меняется в реальных приложения и, следовательно, мало о чем говорит


    public
    override
    void Draw(GameTime gameTime)

    {

    FPS = (int) (1.0 / gameTime.ElapsedGameTime.TotalSeconds);


    base.Draw(gameTime);

    }

  2. Можно просто посчитать сколько кадров было наривано (сколько раз был вызван метод Draw) за последнюю секунду. Такой метод является более точным.


    public
    override
    void Update(GameTime gameTime)

    {

    seconds += gameTime.ElapsedGameTime.TotalSeconds;


    if (seconds >= 1)

    {

    FPS = frames;

    seconds = 0;

    frames = 0;

}


base.Update(gameTime);

}


public
override
void Draw(GameTime gameTime)

{

frames++;


base.Draw(gameTime);

}

Все, что нам осталось сделать – это вывести куда-нибудь значение, полученное со счетчика, выведем его в заголовок окна, в результате получится следующий код:


class
FPSCounter : DrawableGameComponent

{


public
int FPS;


int frames;


double seconds;


public FPSCounter(Game game) : base(game)

{

}


public
override
void Update(GameTime gameTime)

{

seconds += gameTime.ElapsedGameTime.TotalSeconds;


if (seconds >= 1)

{

FPS = frames;

seconds = 0;

frames = 0;

Game.Window.Title = «fps:» + FPS.ToString();

}


base.Update(gameTime);

}


public
override
void Draw(GameTime gameTime)

{

frames++;


base.Draw(gameTime);

}

}

Теперь добавим новый игровой компонент с приложению:


protected
override
void Initialize()

{


// TODO: Add your initialization logic here

Components.Add(new
FPSCounter(this));


base.Initialize();

}

Дальше компонент будет все делать самостоятельно.


Для того, чтобы добавить счетчик в новый проект нужно сделать следующее:

  1. Добавить файл к проекту
    (Add -> Existing item…)
  2. Можно поменять название пространства имен в файле со счетчиком на то которое используется в новом проекте, для того, чтобы не добавлять лишнюю строчку using
  3. Добавить новый счетчик кадров в секунду в коллекцию игровых компонентов

    protected
    override
    void Initialize()

    {

    Components.Add(new
    FPSCounter(this));

    base.Initialize();

    }

  4. Изменить значение маскимального количества кадров секунду, установленные по умолчению

    public Game1()

    {

    graphics = new
    GraphicsDeviceManager(this);

    Content.RootDirectory = «Content»;

    graphics.SynchronizeWithVerticalRetrace = false;

    IsFixedTimeStep = false;

    }

В тот момент, когда вам перестанет быть нужен счетчик количества кадров можно просто закомментировать строку добавления компонента в коллекцию, также можно использовать следующую конструкцию.


protected
override
void Initialize()

{


// TODO: Add your initialization logic here

#if DEBUG

Components.Add(new FPSCounter(this));

#endif


base.Initialize();

}

В таком случае счетчик будет подключен только в конфигурации Debug, для Release конфигурации данная строчка кода будет проигнорирована. Хотя, конечно, это не слишком правильно, поскольку FPS нужно определять и для Release конфигурации.

И еще пара советов, которые, наверняка, покажутся читателю слишком очевидными:

  1. В какой-то момент стоит начать проверять приложение в Release конфигурации. Все равно, к игроку приложение попадет именно в такой конфигурации, а разница в производительности между Debug и Release, обычно, достаточно большая
  2. Для проверки производительности лучше запускать игру без дебаггера. Для этого в Visual Studio нужно нажать Ctrl+F5 (а не просто F5). С другой стороны, в таком режиме нельзя отлаживать приложение. Отладчик очень сильно замедляет работу приложения.

Исходный код:

FPSCounter.cs

using System;

using Microsoft.Xna.Framework;

namespace MyGame

{


class
FPSCounter : DrawableGameComponent

{


public
int FPS;


int frames;


double seconds;


public FPSCounter(Game game) : base(game)

{

}


public
override
void Update(GameTime gameTime)

{

seconds += gameTime.ElapsedGameTime.TotalSeconds;


if (seconds >= 1)

{

FPS = frames;

seconds = 0;

frames = 0;

Game.Window.Title = «fps:» + FPS.ToString();

}


base.Update(gameTime);

}


public
override
void Draw(GameTime gameTime)

{

frames++;


base.Draw(gameTime);

}

}

}

Game1.cs:

using System;

using System.Collections.Generic;

using System.Linq;

using Microsoft.Xna.Framework;

using Microsoft.Xna.Framework.Audio;

using Microsoft.Xna.Framework.Content;

using Microsoft.Xna.Framework.GamerServices;

using Microsoft.Xna.Framework.Graphics;

using Microsoft.Xna.Framework.Input;

using Microsoft.Xna.Framework.Media;

using Microsoft.Xna.Framework.Net;

using Microsoft.Xna.Framework.Storage;

namespace MyGame

{


///
<summary>


/// This is the main type for your game


///
</summary>


public
class
Game1 : Microsoft.Xna.Framework.Game

{


GraphicsDeviceManager graphics;


SpriteBatch spriteBatch;


public Game1()

{

graphics = new
GraphicsDeviceManager(this);

Content.RootDirectory = «Content»;

graphics.SynchronizeWithVerticalRetrace = false;

IsFixedTimeStep = false;

}


///
<summary>


/// Allows the game to perform any initialization it needs to before starting to run.


/// This is where it can query for any required services and load any non-graphic


/// related content. Calling base.Initialize will enumerate through any components


/// and initialize them as well.


///
</summary>


protected
override
void Initialize()

{


// TODO: Add your initialization logic here

Components.Add(new
FPSCounter(this));


base.Initialize();

}


///
<summary>


/// LoadContent will be called once per game and is the place to load


/// all of your content.


///
</summary>


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

}


///
<summary>


/// UnloadContent will be called once per game and is the place to unload


/// all content.


///
</summary>


protected
override
void UnloadContent()

{


// TODO: Unload any non ContentManager content here

}


///
<summary>


/// Allows the game to run logic such as updating the world,


/// checking for collisions, gathering input, and playing audio.


///
</summary>


///
<param name=»gameTime»>Provides a snapshot of timing values.</param>


protected
override
void Update(GameTime gameTime)

{


// Allows the game to exit


if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)


this.Exit();


// TODO: Add your update logic here


base.Update(gameTime);

}


///
<summary>


/// This is called when the game should draw itself.


///
</summary>


///
<param name=»gameTime»>Provides a snapshot of timing values.</param>


protected
override
void Draw(GameTime gameTime)

{

GraphicsDevice.Clear(Color.CornflowerBlue);


// TODO: Add your drawing code here


base.Draw(gameTime);

}

}

}

Реклама
Запись опубликована в рубрике Uncategorized с метками , , , . Добавьте в закладки постоянную ссылку.

9 комментариев на «Счетчик FPS»

  1. Ched:

    Иван, добрый день!
    Не до конца разобрался, что выводит счетчик: у меня в пределах 2500. Это кол-во кадров в секунду? Также не понятно, как, после отключения значения fps по умолчанию, настроить работу приложения со скоростью, например, 60. У меня на сцене спрайт-лист, который я анимирую: со счетчиком кадры проигрываются очень быстро, но когда устанавливаю для спрайта fps персонально, смена кадров практически останавливается.

    • Добрый день,
      Счетчик из статьи просто выводит количество кадров, которое рисуется в приложении за секунду.
      Сам счетчик не влияет на скорость приложения, анимации и т.д.
      Влияют, на сколько я понял проблему, строчки graphics.SynchronizeWithVerticalRetrace = false; и IsFixedTimeStep = false; Если установить оба свойства в true (как они есть по умолчанию), то Update и Draw будут вызываться по 60 раз в секунду (или меньше, если компьютер не успевает выполнять работу с нужной скоростью). При false такая синхронизация отключается и Update и Draw вызываются максимально часто.
      Так вот, привязываться к значению 60 или любому другому не стоит, стоит учитывать во всех расчетах, в том числе и в расчетах анимации, время, прошедшее с последнего обновления.
      Вот тут у меня, на сколько я помню, были примеры http://www.intuit.ru/studies/courses/3471/713/info

  2. Ched:

    Все примеры, которые встречал, привязаны к ограниченному fps. У вас по ссылке есть пример Анимированные спрайты. Там тоже fps по умолчанию. Сделал этот пример (там же спрайт анимируется со своей скоростью, не привязываясь к 60 кадрам в сек.), прикрутил счетчик — и все: ковбой замирает на одном кадре, то ли анимация так быстро прокручивается, то ли действительно замирает. Как это все разрешить?

  3. Ched:

    Закинул исходник на яндекс.диск:
    https://yadi.sk/d/Nk3GNwH_widrZ

  4. А, понятно. Проблема в том, что обновления происходят слишком быстро, из-за этого gameTime.ElapsedGameTime.Milliseconds всегда равно 0. И нужно использовать вместо него gameTime.ElapsedGameTime.TotalMilliseconds (ну и timeElapsed нужно сделать double). Это свойство поддерживает нецелые значения.

  5. Ched:

    Теперь бегает) Спасибо!
    Только как быть с fps, которое показывает счетчик?! FPS для всего приложения совсем не нужно настраивать? Меня пугают эти 2500.

  6. Имеет смысл настраивать для релизной версии. А во время разработки и отладки лучше всегда знать максимальный возможный fps. В целом я это описал в самом посте )

Добавить комментарий

Заполните поля или щелкните по значку, чтобы оставить свой комментарий:

Логотип WordPress.com

Для комментария используется ваша учётная запись WordPress.com. Выход / Изменить )

Фотография Twitter

Для комментария используется ваша учётная запись Twitter. Выход / Изменить )

Фотография Facebook

Для комментария используется ваша учётная запись Facebook. Выход / Изменить )

Google+ photo

Для комментария используется ваша учётная запись Google+. Выход / Изменить )

Connecting to %s