Избегайте ожидания на SwapBuffers - PullRequest
21 голосов
/ 29 апреля 2011

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

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

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

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

Ответы [ 2 ]

24 голосов
/ 29 апреля 2011

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

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

Просто создайте дополнительные потоки для параллельной обработки данных. Держите OpenGL в одном потоке, обработку данных - в другом. Если вы хотите сократить использование процессора, о котором сообщалось, добавьте Sleep (0) или Sleep (1) после SwapBuffers. Sleep (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 блокировать без каких-либо вспомогательных снов, однако я даю некоторым другим рабочим потокам о том времени, чтобы делать вещи на GPU через общий контекст (например, обновление текстур) и у меня работает сборщик мусора. Нет необходимости точно определять время, но рабочие потоки, заканчивающиеся незадолго до возврата SwapBuffers, позволяют начинать рендеринг следующего кадра практически сразу, поскольку большинство мьютексов уже разблокировано.

0 голосов
/ 05 декабря 2014

Хотя eglSwapBuffers не занят, ожидает законного использования неблокирующего eglSwapBuffers, чтобы иметь более отзывчивый поток GUI, который может прослушивать сигналы ввода или вывода пользователя вместо ожидания, пока 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;
     }
 }
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...