Мне нужен таймер для отображения буфера изображения каждые 25fps (C ++ win32) - PullRequest
2 голосов
/ 11 ноября 2010

Я использовал SetTimer с 40 мс и сообщение WM_TIMER, чтобы рисовать изображение каждые 40 мс (25 кадров в секунду), но результат слишком медленный.

Я обнаружил, что WM_TIMER не точен, я пытался использовать мультимедийный таймер, но процедура обратного вызова не работала.

Мой вопрос: Как я могу нарисовать мои изображения с 25fps?

Спасибо!

Ответы [ 3 ]

3 голосов
/ 11 ноября 2010

SetTimer может быть настолько точным, насколько позволяет ваше приложение. Он не может доставить сообщение WM_TIMER, пока ваш основной поток не перейдет в режим ожидания и не войдет в цикл обработки сообщений. И если для рисования изображения потребуется более 40 мсек, вы неизбежно отстанете. Это то же самое, что происходит, когда вы пытаетесь играть в современную игру на старой машине, частота кадров становится дерьмовой.

И да, timeSetEvent () не может решить эту проблему. Его обратный вызов выполняется в фоновом потоке. Вы не можете обновить окно в таком потоке, окна не являются потокобезопасными.

Вы можете достичь скорости 25 кадров в секунду, увеличив скорость написания кода. DirectX помогает с этим.

2 голосов
/ 11 ноября 2010

Вы действительно правы.WM_TIMER крайне неточен.Если вы попытаетесь сделать что-то подобное с GetTickCount (), WaitForSingleObject (..., timeout) или Sleep (timeout), они также не будут работать, потому что они используют механизм синхронизации, аналогичный WM_TIMER.Механизм, который я могу придумать для достижения вашей цели, заключается в использовании счетчика производительности с высоким разрешением .Обратите внимание, что здесь есть некоторые проблемы, такие как потенциальные проблемы с многопроцессорными системами, потраченные впустую циклы из-за вращения и даже процессоров, которые не используют аппаратные таймеры.Но это отправная точка для вас.

Вот как вы его используете:

Сначала спросите систему, какова частота счетчиков производительности.Затем выполните цикл и несколько раз получите текущее время и посмотрите, сколько времени прошло.Когда пройдет 1/25 секунды, делай свое дело.Пример кода:

#include <windows.h>
#include <string>
#include <sstream>
#include <iostream>
using namespace std;

typedef unsigned __int64 u64;

string format_elapsed(u64 elapsed, u64 freq)
{
    stringstream ss;
    ss << static_cast<double>(elapsed)/static_cast<double>(freq) << " sec";
    return ss.str();
}

int main()
{

    LARGE_INTEGER li = {};
    QueryPerformanceFrequency(&li);
    u64 freq = static_cast<u64>(li.QuadPart); // clock ticks per second
    u64 period = 25;  // 25 fps
    u64 delay = freq/period;    // clock ticks between frame paints

    u64 start = 0, now = 0;
    QueryPerformanceCounter(&li);
    start = static_cast<u64>(li.QuadPart);

    for( ; ; )  // loop forever
    {
        QueryPerformanceCounter(&li);
        now = static_cast<u64>(li.QuadPart);
        if( now - start >= delay )
        {
            // time to paint
            cout << "Paint!  Elapsed = " << format_elapsed(now-start,freq) << "\n";
            // reset new start time & loop
            start = now;
        }
    }


}
0 голосов
/ 11 ноября 2010

WM_TIMER - это сообщение с низким приоритетом .Это означает, что если вы обрабатываете сообщение с более высоким приоритетом (например, WM_PAINT или WM_MOUSEMOVE) через PeekMessage или GetMessage, оно не сработает.Вы также не можете использовать мультимедийные таймеры напрямую, поскольку не следует рисовать в окне из фонового потока.

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

Это может быть нежелательно, поэтому вам нужно изменить ваш обратный вызов, чтобы вместо этого вызывать SendMessage и использовать timeSetEvent с TIME_ONESHOT при постановке его в очередь.Затем, когда возвращается SendMessage, вызовите timeSetEvent с задержкой, установленной на 25 мс - время обработки SendMessage.Таким образом, вы гарантируете, что если поток сообщений Windows отстанет, вы не заполняете очередь сообщений тем же сообщением.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...