Подтвердить что ты не робот

Избегайте ожидания на SwapBuffers

Я обнаружил, что SwapBuffers в OpenGL будет занят - подождите, пока графическая карта не будет выполнена с ее рендерингом или если она ждет V-Sync.

Это проблема для меня, потому что я не хочу тратить 100% процессорного ядра, просто ожидая завершения карты. Я не пишу игру, поэтому я не могу использовать циклы процессора для чего-то более продуктивного, я просто хочу привести их к другому процессу в операционной системе.

Я нашел функции обратного вызова, такие как glutTimerFunc и glutIdleFunc, которые могли бы работать для меня, но я не хочу использовать перенасыщение. Тем не менее, избыток должен каким-то образом использовать обычные функции gl для этого, правильно?

Есть ли какая-либо функция, такая как "glReadyToSwap" или аналогичная? В этом случае я мог бы проверить, что каждая миллисекунда или около того, и определить, следует ли мне подождать дольше или сделать обмен. Я мог бы также предположить, что, возможно, пропустите SwapBuffers и напишите мою собственную аналогичную функцию, которая не занята - подождите, если кто-то сможет указать мне в правильном направлении.

4b9b3361

Ответ 1

SwapBuffers не занят ожиданием, он просто блокирует ваш поток в контексте драйвера, что делает Windows неправильно вычислением процессора: Windows вычисляет использование ЦП, определяя, сколько процессорного времени занимает процесс простоя + t тратить в контексте водителя. SwapBuffers блокируется в контексте драйвера, и ваша программа, очевидно, забирает это время процессора из процесса ожидания. Но ваш процессор практически ничего не делает, планировщик с радостью ждет времени для других процессов. Простой процесс OTOH не делает ничего, кроме как сразу же дает свое время остальной системе, поэтому планировщик перескакивает обратно в ваш процесс, который блокирует в драйвере то, что Windows считает "забивающим CPU". Если вы измерили фактическое потребление энергии или тепловую мощность, для простой программы OpenGL это будет оставаться довольно низким.

Это раздражающее поведение на самом деле является вопросом OpenGL!

Просто создайте дополнительные потоки для параллельной обработки данных. Держите OpenGL в одном потоке, обработка данных - в другом. Если вы хотите отказаться от заявленного использования ЦП, добавьте Sleep (0) или Sleep (1) после SwapBuffers. Сон (1) заставит ваш процесс тратить время на блокирование в пользовательском контексте, поэтому процесс простоя получает больше времени, что равносильно номерам. Если вы не хотите спать, вы можете сделать следующее:

const float time_margin = ... // some margin
float display_refresh_period; // something like 1./60. or so.

void render(){

    float rendertime_start = get_time();

    render_scene();
    glFinish();

    float rendertime_finish = get_time();
    float time_to_finish = rendertime_finish - rendertime_start;

    float time_rest = fmod(render_finish - time_margin, display_refresh_period);
    sleep(time_rest);
    SwapBuffers();
 }

В моих программах я использую такой тип времени, но по другой причине: я разрешаю SwapBuffers блокировать без каких-либо помощников Sleeps, однако я даю некоторые другие рабочие потоки о том времени, чтобы делать вещи на GPU через общий контекст (например, обновление текстур) и у меня работает сборщик мусора. На самом деле не обязательно точно это время, но рабочие потоки завершаются непосредственно перед возвратом SwapBuffers, позволяет начать рендеринг следующего кадра почти сразу, так как большинство мьютексов уже разблокированы.

Ответ 2

Хотя eglSwapBuffers не занят, ожидание законного использования для неблокирующего eglSwapBuffers должно иметь более гибкий поток графического интерфейса, который может прослушивать сигналы ввода или выхода пользователя, а не ждать, пока OpenGL завершит замену буферов. У меня есть решение половины этой проблемы. Сначала в вашем основном цикле вы буферизируете свои команды OpenGL для выполнения в вашем выгруженном буфере. Затем вы проводите опрос на объекте синхронизации, чтобы проверить, завершены ли ваши команды в вашем выгруженном буфере. Затем вы можете поменять буферы, если команды завершили выполнение. К сожалению, это решение только асинхронно ожидает, что команды закончат выполнение в вашем выгруженном буфере и не асинхронно ждут vsync. Вот код:

 void process_gpu_stuff(struct gpu_context *gpu_context)
 {
     int errnum = 0;

     switch (gpu_context->state) {
     case BUFFER_COMMANDS:
         glDeleteSync(gpu_context->sync_object);
         gpu_context->sync_object = 0;

         real_draw(gpu_context);
         glFlush();

         gpu_context->sync_object = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
         if (0 == gpu_context->sync_object) {
             errnum = get_gl_error();
             break;
         }
         gpu_context->state = SWAP_BUFFERS;
         break;

     case SWAP_BUFFERS:
         /* Poll to see if the buffer is ready for swapping, if
          * it is not in ready we can listen for updates in the
          * meanwhile. */
         switch (glClientWaitSync(gpu_context->sync_object, 0, 1000U)) {
         case GL_ALREADY_SIGNALED:
         case GL_CONDITION_SATISFIED:
             if (EGL_FALSE == eglSwapBuffers(display, surface)) {
                 errnum = get_egl_error();
                 break;
             }
             gpu_context->state = BUFFER_COMMANDS;
             break;

         case GL_TIMEOUT_EXPIRED:
             /* Do nothing. */
             break;

         case GL_WAIT_FAILED:
             errnum = get_gl_error();
             break;
         }
         break;
     }
 }