Расширение NV_fence

Как известно работа OpenGL построена по принципу клиент-сервер: приложение (клиент) выдает команды, которые получает на обработку сервер.

При этом обработка носит асинхронный характер - команды запоминаются во внутреннем буфере (буферизуются), а управление сразу же возвращается в передающую программу. Сама же команда может быть реально выполнена GPU намного позже.

Стандартный OpenGL предоставляет всего две команды, которые обеспечивают синхронизацию между CPU и GPU - это glFlush и glFinish.

Команда glFinish обеспечивает явную синхронизацию - управление из нее не возвращается до тех пор, пока все переданные до нее команды не будут выполнены на GPU.

Команда glFlush просто вызывает немедленную передачу всех команд из буфера на GPU и управление из нее возвращается сразу же.

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

Именно такую функциональность и дает расширение NV_fence. Это расширение вводит новый тип объекта - fence - служащего для обозначения позиций к потоке команд. По этим fence-объектам можно осуществлять синхронизацию с точностью до соответствующей команды.

Каждый fence-объект идентифицируется при помощи беззнакового целого числа (GLuint) и работа с ними полностью аналогично работе текстурами.

fence-объекты могут вставляться в поток команд (при помощи команды glSetFenceNV) и далее можно либо осуществлять запросы "выполнились ли к данному моменту все команды до данного fence-объекта", либо при помощи команды glFinishFenceNV ожидать, когда соответствующие команды будут выполнены.

Таким образом, расширение NV_fence позволяет задавать контрольные точки (при помощи fence-объектов) внутри потока команд и отслеживать их прохождение синхронным или асинхронным способом.

Для создания и уничтожение таких объектов служат команды glGenFencesNV и glDeleteFencesNV:

void glGenFencesNV    ( GLsizei n, GLuint * fences );
void glDeleteFencesNV ( GLsizei n, const GLuint * fences );

Работа этих функций полностью аналогична glGenTextures и glDeleteTextures.

Для проверки, является ли заданное беззнаковое целое число идентификатором какого-либо fence-объекта, служит команда glIsFenceNV:

void glIsFenceNV ( GLuint id );

Поместить fence-объект с заданным идентификатором id в поток команд (тем самым отметив предыдущую команду) можно при помощи вызова glSetFenceNV:

void glSetFenceNV ( GLuint id, GLenum condition );

Параметр id задает помещаемый fence-объект, а параметр condition задает отслеживаемое условие и может принимать только одно значение - GL_ALL_COMPLETED_NV. При этом для соответствующего fence-объекта выставляется значение FALSE и это значение станет истинным, когда все команды, предшествующие данному fence-объекту будут выполнены.

Если fence-объект с данным идентификатором уже был вставлен в командный поток, то он удаляется и вставляется новый объект. Получить информацию о статусе ранее вставленного объекта становится уже невозможно.

Следующая команда служит для проверки состояния fence-объекта по его идентификатору.

GLboolean glTestFenceNV ( GLuint id );

Значение TRUE возвращается только в том случае, если все команды, предшествующие заданному fence-объекту уже выполнены. В противном случае возвращается значение FALSE.

Можно также для явной синхронизации использовать команду glFinishFenceNV, которая ожидает пока не будут выполнены все команды, предшествующие заданному fence-объекту:

void glFinishFenceNV ( GLuint id );

Обратите внимание, что все рассмотренные выше команды (кроме glFinishFenceNV) сразу же выполняются. Все эти команды не могут быть занесены в дисплейный список. Также их нельзя использовать внутри блока glBegin/glEnd.

Использование этого расширения удобно в тех случаях, когда осуществляется совместное использование каких-либо ресурсов как CPU, так и GPU (например различных буферов). Так если осуществляется рендеринг из нескольких постоянно обновляемых буферов, то использование данного расширения позволяет точно определить когда содержимое буфера уже было использовано GPU и его можно изменять на CPU.