Расширение ARB_sync

Данное расширение (введенное в OpenGL 3.2 core) является развитием расширения NV_fence от компании NVidia. Подобно ему расширение ARB_sync вводит понятие примитивов синхронизации. Каждый подобный примитив может находится в одном из дух состояний - GL_SIGNALED и GL_UNSIGNALED. Пользователю предоставляется возможно проверять статус примитива без ожидания и ожидать перевода состояния в GL_SIGNALED как на стороне клиента, так и на стороне сервера.

Каждый примитив соответствует некоторому событию, наступление которого и изменяет статус примитива. Каждый примитив характеризуется значением типа GLsync, отличным от нуля. Проверить, является ли заданное значение идентификатором какого-либо примитива синхронизации, можно при помощи функции glIsSync:

GLboolean glIsSync ( GLsync sync );

На данный момент поддерживается только один тип таких примитивов - fence, позволяющий отслеживать прохождение определенной точки среди последовательности команд. Создание примитива подобного типа и вставка его в текущий поток команд OpenGL осуществляет команда glFenceSync:

GLsync glFenceSync ( GLenum condition, GLbitfield flags );

Данная функция создает примитив синхронизации, выставляет ему статус GL_UNSIGNALED и возвращает его идентификатор (не равный нулю). На данный момент параметр condition может принимать только значение GL_SYNC_GPU_COMMANDS_COMPLETE, а параметр flags - только нулевое значение. Такому выбору соответствует переход объекта в состояние GL_SIGNALED когда все команды, предшествующие данной команде glFenceSync будут выполнены и все их результаты будут доступны и для клиента и для сервера.

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

Таблица 1. Свойства примитива синхронизации после вызова glFenceSync.

Свойство Начальное значение
GL_OBJECT_TYPE GL_SYNC_FENCE
GL_SYNC_CONDITION GL_SYNC_GPU_COMMANDS_COMPLETE
GL_SYNC_STATUS GL_UNSIGNALED
GL_SYNC_FLAGS 0

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

void glGetSynciv ( GLsync sync, GLenum pname, GLsizei bufSize, GLsizei * length, GLint * values );

Данный вызов возвращает для объекта синхронизации, заданного параметром sync, свойство, задаваемое параметром pname. Результаты помещаются в массив, задаваемый параметром values, параметр bufSize задает максимальный количество целочисленных значений, которое можно поместить в этот массив. В параметре length возвращается число записанных в values целых чисел. Следующая таблица содержит информацию о возвращаемых значениях для всех допустимых значений pname.

Таблица 2. Возвращаемые значения для функции glGetSynciv.

Значение pname Возвращаемое значение
GL_OBJECT_TYPE Возвращает одно целое число, равное GL_SYNC_FENCE
GL_SYNC_CONDITION Возвращает одно целочисленное значение, равное GL_SYNC_GPU_COMMANDS_COMPLETE
GL_SYNC_STATUS Возвращает одно целочисленное значение, равное GLSIGNALED или GL_UNSIGNALED
GL_SYNC_FLAGS Возвращает одно целочисленное значение, равное нулю

Для уничтожения примитива синхронизации служит функция glDeleteSync:

void glDeleteSync ( GLsync sync );

При этом, если имеет место ожидание по данному примитиву или соответствующее событие еще не наступило, то объект просто помечается для удаления и будет удален по завершению ожидания или наступлению события. В противном случае объект удаляется немедленно.

Существует две функции, служащие для ожидания перевода примитива в состояние GL_SIGNALED. Одна функция служит для ожидания на стороне клиента, другая - для ожидания на стороне сервера.

GLenum glClientWaitSync ( GLsync sync, GLbitfield flags, GLuint64 timeout );
void   glWaitSync       ( GLsync sync, GLbitfield flags, GLuint64 timeout );

При помощи команды glClientWaitSync можно явно задать ожидание (клиентом) наступления события или же просто проверить статус без ожидания. Параметр flags на данный момент должен быть равен константе GL_SYNC_FLUSH_COMMANDS_BIT, параметр timeout задает время ожидания клиента в наносекундах (10-9 сек). Если параметр timeout равен нулю, то никакого ожидания вообще не происходит, а сразу же возвращается код GL_ALREADY_SIGNALED (если событие уже наступило) или же GL_TIMEOUT_EXPIRED (если событие еще не наступило).

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

Таблица 3. Возвращаемые значения для функции glClientWaitSync.

Значение pname Возвращаемое значение
GL_ALREADY_SIGNALED На момент вызова событие уже наступило
GL_TIMEOUT_EXPIRED За timeout наносекунд событие так и не наступило
GL_CONDITION_SATISFIED Событие наступило до истечения timeout наносекунд
GL_WAIT_FALIED Произошла ошибка

Функция glWaitSync аналогична функции glClientWaitSync, но она блокирует на время ожидания не клиентскую часть, а GL-сервер. Параметр timeout всегда должен быть равен константе GL_TIMEOUT_IGNORED, а параметр flags должен быть равен нулю. При этом ожидание сервера будет происходит на зависящее от реализации время, которое можно узнать при помощи следующего фрагмента кода:

GLint64 maxTimeout;

glGetInteger64v ( GL_MAX_SERVER_WAIT_TIMEOUT, &maxTimeout );

Обратите внимание, что возможно одновременная блокировка как клиента, так и сервера.

Для удобства работы завернем примитив синхронизации в следующий класс:

#ifndef	__SYNC_OBJECT__
#define	__SYNC_OBJECT__

class Sync
{
	GLsync	sync;
public:
	Sync  ();
	~Sync ();	

	bool isOk () const
	{
		return sync != 0;
	}

	void setFence ( GLenum condition = GL_SYNC_GPU_COMMANDS_COMPLETE, 
			GLbitfield flags = 0 );

	GLenum clientWait ( GLuint64 timeout, GLbitfield flags = GL_SYNC_FLUSH_COMMANDS_BIT );
	void   serverWait ( GLuint64 timeout = GL_TIMEOUT_IGNORED, GLbitfield flags = 0 );

	static GLint64	maxServerTimeout ();
};

#endif

По этой ссылке можно скачать весь исходный код класса Sync.