пятница, 16 октября 2009 г.

Multithreading in OpenGL API

Как известно, OpenGL имеет некоторые неудобства для работы в разных потоках, по сравнению с Direct3D. Сам недавно столкнулся с этими неудобствами на платформе Windows.
Согласно документации MSDN, для каждого потока в котором будет работа с OpenGL API, должен быть свой текущий контекст рендеринга, установку текущего контекста
осуществляется с помощью функции wglMakeCurrent.
Если это не так, то при любом обращении к функциям OpenGL API будет ошибка GL_INVALID_OPERATION, ну а дальше нарушенный доступ к памяти и далее поведение
программы не определено.
Для решения этой проблемы, нужно создать для нового потока новый контекcт воспроизведения, используя функцию wglCreateContext.
В качестве контекста окна можно передать контекст окна приложения, хотя по некоторым советам, следует создать дополнительное маленькое окно размером 1x1 пиксель.
Хотя у меня работало и без дополнительного окна. Далее, нужно что-бы все изменения между потоками были известны друг другу. К примеру, создается
текстура, вершинный или индексный буфер и т.д. в одном потоке, а использоваться будет в другом. Осуществить это нужно с помощью функции wglShareLists,
причем тут дисплейные списки в описании функции я не знаю.
Лучше сделать расшаривание контекстов друг на друга, хотя все зависит от задачи, т.е. если есть к примеру есть load_context и render_context, которые используются в потоке загрузки и рендеринга соответственно, то пара вызовов:
wglShareLists(load_context, render_contex);
wglShareLists(render_contex, load_context);

обеспечат видимость изменений между этими потоками.
В случае неудачи вызова wglShareLists у меня GetLastError возвращает ошибку, строковое соответствие полученное через FormatMessage было таким:
"невозможно создать файл так как он уже существует", почему это так, для меня загадка.
При завершении каждого потока нужно не забыть освободить связанный с ним контекст.

6 комментариев:

Neill комментирует...

интересно по мультипоточности, а делал какие-то замеры производительности. Что получается в случае с двумя потоками?

Andrey комментирует...

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

Анонимный комментирует...

Интересно, а на многоядерном увеличится или уменьшится производительность ?

Andrey комментирует...

По идее должно увеличиться.
Слышал еще что на платформе Windows автоматическое распределение потоков по ядрам, таким образом явных вызовов
SetThreadAffinityMask
делать не обязательно.

Анонимный комментирует...

Андрей, работать с GAPI ( хоть GL, хоть DX ) в разных потоках - создание себе кучи проблем. Если нужна асинхронная загрузка - второй поток загружает с диска и ставит в очередь первому - а тот уже загружает ресурсы на GPU и др

Andrey комментирует...

У меня точно с этим нет проблем.