2010-03-25 3 views
0

Я создаю приложение directx, которое полагается на системное время (потому что оно должно быть точным), , и мне нужно выполнять строки кода 60 раз в секунду в фоновом режиме (в потоке, созданном boost :: thread). это равно 60 FPS (кадр в секунду), но не зависит от базовой частоты кадров приложения.Коды запуска всего 60 раз в секунду

//................. 
void frameThread() 
{ 
    // I want to run codes inside this loop for *exactly* 60 times in a second. 
    // In other words, every 16.67 (1000/60) milliseconds 
    for(;;) 
    { 
     DoWork(); 
     //......... 
    } 
} 
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd) 
{ 
    initialize(); 
    //.....stuffs 
    boost::thread framethread(frameThread); 
    //...... 
} 

Есть ли способ сделать это?

Любой вид помощи был бы оценен :)

+5

Знаете ли вы, что существует разница между ходом 60 раз в секунду и запуском каждые 1/60-й секунды :) – Axarydax

+0

Если выполнение DoWork() должно быть равномерно распределено в течение 1 секунды? Может ли он выполняться 60 раз, скажем, в течение 0,5 секунд, а затем 0,5 секунды бездействия? – n0rd

+0

он непрерывный, но он должен работать 60 раз в секунду. –

ответ

5

Поскольку 60 Гц - это общая частота обновления монитора, похоже, что вы хотите включить V-Sync, вместо того, чтобы возиться со таймерами и спит.

Вы упомянули, что используете DirectX. Если вы используете DirectX 9, укажите D3DPRESENT_INTERVAL_ONE при создании устройства, и приложение будет синхронизироваться с частотой обновления монитора. Обычно это 60 Гц - но это может быть и теоретически, например, 50 Гц, 85 Гц или 120 Гц. Вы получаете очень хороший дисплей без слез, хотя - если вы не используете V-sync, вы, вероятно, получите разрывающие артефакты при прокрутке.

Возникает проблема: как вы делаете игру с такой же скоростью, если частота кадров может варьироваться? Ответ прост - используйте таймер с высоким разрешением (например, QueryPerformanceCounter на Windows) и измерьте время между каждым кадром. Затем вам нужно заложить любое движение или движение с этого значения «дельта-времени».В любом случае это хорошо, даже с фиксированной частотой кадров - это предотвращает внезапную работу вашей игры в замедленном режиме, если компьютер работает медленно, а игрок получает только 20 кадров в секунду.

Ниже приведен пример перемещения объекта по горизонтали на 100 пикселей в секунду, независимо от частоты кадров:

object.x += 100.0f * delta_time; // same speed no matter the framerate 

Это ИМО является гораздо лучшим способом сделать это, особенно с учетом того, что это разрывая свободным, framerate- независимы и синхронизируются с монитором пользователя, а не какой-либо произвольной частотой кадров, которую вы устанавливаете сами.

+0

Спасибо. Можете ли вы показать мне пример использования QueryPerformanceCounter; простые? Я новичок в этом API. –

+0

Просто примеры Google, это очень просто. QueryPerformanceCounter дает вам значение времени высокого разрешения, QueryPerformanceFrequency дает вам единицы в секунду, возвращаемые QueryPerformanceCounter. Поэтому вызовите QueryPerformanceCounter каждый тик, получите разницу между значениями, разделите их по частоте, у вас есть время с высоким разрешением в секундах в качестве поплавка. – AshleysBrain

2

Получить системное время, прежде чем DoWork(), добавить к нему 1/60 секунды и сохранить его для последующего использования. После DoWork() (который должен выполняться менее 1/60 секунды) вам нужно спать, пока системное время не будет равно ранее сохраненному времени. Это должно делать свое дело.

+0

Помните, что 1/60-я секунда может быть меньше разрешения системного времени. Конечно, это близко к нему, поэтому вы не получите плавного распределения с использованием системного времени. –

+0

Это правда. Вы полностью зависете от точности таймера, который вы используете для проверки времени и таймера, используемого для вызова Sleep(). С другой стороны, небольшие неточности в цикле 60FPS не должны быть действительно заметны для пользователя. –

1

Используйте waitable timer как объект синхронизации.

HANDLE hWaitTimer; 

void frameThread() 
{ 
    while (WaitForSingleObject(hWaitTimer, INFINITE) == WAIT_OBJECT_0) 
    { 
     // reactivate timer 
     LARGE_INTEGER due_time = {0}; 
     due_time.LowPart = 10000/60; // in 100 nanosecond intervals 
     ::SetWaitableTimer(hWaitTimer, &due_time, 0, NULL, NULL, FALSE); 

     DoWork(); 
     //......... 
    } 
} 

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd) 
{ 
    hWaitTimer = ::CreateWaitableTimer(NULL, FALSE, NULL); 
    LARGE_INTEGER due_time = {0}; 
    due_time.LowPart = 10000/60; // in 100 nanosecond intervals 
    ::SetWaitableTimer(hWaitTimer, &due_time, 0, NULL, NULL, FALSE); 

    // create trhead etc. 


    // before exit 
    CloseHandle(hWaitTimer); 
} 
0

Если вам действительно нужна высокая точность, использование конкретной платформы мультимедийный таймер, см функцию timeSetEvent, http://msdn.microsoft.com/en-us/library/aa448195.aspx. Используйте TIME_PERIODIC с флажками TIME_CALLBACK_FUNCTION, uResolution = 0. В любом случае DirectX не переносится.

Я не думаю, что вы можете получить требуемую точность, используя кросс-платформенную нить.

0

Если вы на 100% не будете работать дольше, чем 1/60, тогда решение будет использовать semaphore и отдельный таймер, который будет тикать каждые 1/60 секунд.

В потоке таймера вы семафор.V(), а в вашей рабочей ветке вы семафор.P().

Windows имеет native semaphores.

Если ваш рабочий поток может занимать больше 1/60, вы должны компенсировать это с помощью какой-либо переменной экземпляра «worker_lock». Никогда не запирайте таймер, он должен быть как можно быстрее. Также повысите приоритет потока приема таймера.

1

Что вы подразумеваете под "точной"? Windows не является RTOS, поэтому вы не можете получить реальную точность. Вы можете получить довольно близко, но вы не можете гарантировать, что ваш поток будет запланирован, когда вы этого захотите.

В любом случае, я бы выбрал ожидаемый таймер, пул потоков и RegisterWaitForSingleObject.

+0

означает, что мне нужно, чтобы код выполнялся ровно 60 раз в секунду; не менее. –

+1

Опять же, что вы подразумеваете под «точно»? Какова требуемая точность? это код, который работает каждую секунду плюс или минус 10 MS? 1 мс? 100 мс? Является ли регулярность более важной, чем точность? Требует ли интервал времени, затрачиваемого на выполнение кода, или нет? – Stephane

+1

@djzmo: это невозможно в Windows. Предположим, что какой-то высокоприоритетный объект, такой как ядро ​​или драйвер устройства, работает медленно и забивает процессор (ы) на 1/30 секунды или дольше. Тогда ваша программа не будет запускаться вообще за это время, и вы не сможете «догнать» к концу второго. Вы должны изменить свои ожидания. –

Смежные вопросы