Почему простая программа glfw потребляет весь доступный процессор, даже если программа простаивает (согласно исследованию процессов)?

У меня есть очень простой игровой цикл с использованием GLFW следующим образом (режим Windows x64 Release)

Я ожидаю, что программа будет выполняться очень быстро, однако кажется, что моя дельта, рассчитанная ниже, всегда составляет 16,667 мс, что может показаться тем, что glfw каким-то образом ограничивает скорость моего основного цикла. Это не проблема, так как я не забочусь о частоте более 60 Гц. Однако проводник процессов и диспетчер задач Windows сообщают, что моя программа использует большую часть ядра процессора.

В частности, кажется, что glfwSwapBuffers() потребляет много ресурсов процессора, хотя я ничего не рисую. Удаление этого вызова снижает загрузку процессора до 0,5%.

Кстати, моя функция Sleep почти никогда не вызывается, потому что дельта всегда около 16,6 мс.

main ()
{
    double prevTime = glfwGetTime();
    //init glfw ..
    while(!glfwWindowShouldClose(window)) 
    {
        double time0=glfwGetTime();
        double delta = time0- prevTime;

        if (delta >= g_FrameInterval)
        {
            glfwPollEvents();
            prevTime= time0;
            glfwSwapBuffers(window);
        }
        else 
        {
            Sleep(10);
        }
    }
}

person skimon    schedule 19.09.2014    source источник
comment
Вероятно, вам следует добавить тег Windows, поскольку ваш вопрос относится к Windows.   -  person Basile Starynkevitch    schedule 19.09.2014


Ответы (2)


glfwSwapBuffers ожидает вертикальной синхронизации монитора. Вот почему ваш цикл работает с частотой 60 Гц (это частота обновления вашего монитора). Что касается высокой загрузки ЦП, ОС, скорее всего, не усыпит ваш процесс. Это, вероятно, приведет к тому, что он пропустит вертикальную синхронизацию, потому что он не сможет достаточно быстро проснуться. Вместо этого ЦП помещается в цикл занятости до тех пор, пока vsync. Вот более полное объяснение проблемы.

person Dark Falcon    schedule 19.09.2014
comment
ах интересно. Это имеет смысл. Я только что обнаружил после публикации, что glfwSwapBuffers, по-видимому, блокирует и этот параметр glfwSwapInterval(0); останавливает это и, следовательно, резко снижает использование процессора, о котором сообщает Windows. Я полагаю, что недостатком отключения этого и использования моего сна будет разрыв. - person skimon; 19.09.2014

Кажется, вам нужно синхронизировать поток на основе возврата буферов подкачки. Сделайте пару «фиктивных» вызовов буфера подкачки (с начальным экраном), считывая таймер после каждого вызова, чтобы получить частоту (это может быть 120 Гц на некоторых мониторах, или, если старый ЭЛТ-монитор, 60 Гц, 75 Гц, 85 Гц, 100 Гц, 120 Гц, 160 Гц, 200 Гц) и установить начальный счетчик таймера.

Если можно просто работать со скоростью монитора, вы можете использовать фиксированное значение Sleep(), предполагая некоторые максимальные накладные расходы для вашего кода (в зависимости от самой медленной целевой системы). Частота тактов по умолчанию для Windows составляет 64 Гц (15,625 мс), но ее можно ускорить с помощью timeBeginPeriod(1), и в этом случае Sleep(n) занимает около n мс в Windows 7 или более поздних версиях, но до n+1 мс в Windows XP. Например, если вашему коду требуется менее 5 мс процессорного времени для каждого кадра, то при частоте 60 Гц вы можете просто использовать фиксированный Sleep(10) (или Sleep(9) в Windows XP) после каждого вызова буфера подкачки, или если при 120 Гц, затем Sleep(2) (или Sleep(1) в Windows XP).

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

/* code for a thread to run at fixed frequency */
typedef unsigned long long UI64;        /* unsigned 64 bit int */
#define FREQ    400                     /* frequency */

LARGE_INTEGER liPerfTemp;               /* used for query */
UI64 uFreq = FREQ;                      /* process frequency */
UI64 uOrig;                             /* original tick */
UI64 uWait;                             /* tick rate / freq */
UI64 uRem = 0;                          /* tick rate % freq */
UI64 uPrev;                             /* previous tick based on original tick */
UI64 uDelta;                            /* current tick - previous */
UI64 u2ms;                              /* 2ms of ticks */
UI64 i;

    /* ... */ /* wait for some event to start thread */
    timeBeginPeriod(1);                 /* set period to 1ms */
    Sleep(128);                         /* wait for it to stabilize */

    u2ms = ((UI64)(liPerfFreq.QuadPart)+499) / ((UI64)500);

    QueryPerformanceCounter((PLARGE_INTEGER)&liPerfTemp);
    uOrig = uPrev = liPerfTemp.QuadPart;

    for(i = 0; i < (uFreq*30); i++){
        /* update uWait and uRem based on uRem */
        uWait = ((UI64)(liPerfFreq.QuadPart) + uRem) / uFreq;
        uRem  = ((UI64)(liPerfFreq.QuadPart) + uRem) % uFreq;
        /* wait for uWait ticks */
        while(1){
            QueryPerformanceCounter((PLARGE_INTEGER)&liPerfTemp);
            uDelta = (UI64)(liPerfTemp.QuadPart - uPrev);
            if(uDelta >= uWait)
                break;
            if((uWait - uDelta) > u2ms)
                Sleep(1);
        }
        if(uDelta >= (uWait*2))
            dwLateStep += 1;
        uPrev += uWait;
        /* fixed frequency code goes here */
        /*  along with some type of break when done */
    }

    timeEndPeriod(1);                   /* restore period */
person rcgldr    schedule 19.09.2014