Войти
Мобильные платформыФорумОбщее

Отрисовка несколько раз измененной через glTexSubImage2D текстуры за кадр. (Opengl ES 2.0, Android)

#0
20:38, 20 фев. 2012

Реализую работу с текстом в opengl.

Создается одна текстура определенного размера.

Дается задание вывести текст на экран.
Для каждого символа проверяем, есть ли он уже в текстуре. Если нет, то рендерим его изображение в freetype и через glTexSubImage2D ставим на свободное место в текстуре.
Потом создаем список из квадратных полигонов(из двух треугольников) и присваиваем каждому квадрату текстурные координаты нужного символа.
И рисуем что получилось.

Все хорошо, пока все символы строки помещаются на текстуре. Если разместить их все на текстуре не получается, то разбиваем отрисовку на несколько частей. Заносим символы в текстуру, пока помещаются, потом рисуем. Потом заносим очередную порцию символов и опять рисуем.

Проблема в том, что фактически отрисовка происходит не в момент вызова glDrawElements, а в момент смены кадров(glFlush не на всех gpu работает, по крайней мере на adreno glFlush после отрисовки помог, а на powervr никакого эфекта не произвел). А изменение текстуры формально происходит в момент вызова. В результате при такой организации процесса возникают глюки(вместо нужного символа на экране что-то другое), причем вид глюка зависит от производителя GPU (на powervr и adreno разные результаты).

Вопрос первый. Как можно справится с такой ситуацией? Для каждой строки или для каждого символа создавать текстуру не вариант. Учитывая что работать могу только с текстурами размера степени 2, то получается много лишней памяти будет, да и текстуры переключать часто. На одной текстуре все можно компактнее расположить, почти без потерь.

Вопрос второй. Opengl должен разруливать такую ситуацию с изменяемой текстурой? Пытается ли он для каждого вызова glDrawElements запомнить состояние текстуры? Или вообще не парится, а использует ту текстуру, которая получилась к концу кадра? Может обычный opengl умеет, а ES версия не умеет?


#1
18:36, 28 фев. 2012

Тебе нужно эти текстуры вообще сделать статическими. Т.е. ты добавил в текстуру символы и рисуешь. Когда рисуешь в следующий раз тот же глиф, то смотришь: "Ага, вот в этой текстуре уже есть такой символ!" - и заносишь подходящие UV-координаты в массив. Если символа нет, то добавляешь его (создаёшь новую текстуру при необходимости).

Тогда динамически будет обновляться только VBO, а с текстурами никаких проблем не будет.

#2
18:54, 28 фев. 2012

в помощь http://www.angelcode.com/products/bmfont/

#3
19:40, 28 фев. 2012

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

В текущей реализации если новый символ не влазит на текстуру, то я освобождаю место для него(удаляю старые записи о расположении на текстуре для глифов) и ставлю на его место этот новый. Но, вот такой пример. Надо вывести строку ABC. Текстура маленькая, и на неё помещаются только два символа. Поэтому вывод строки происходит в два этапа. Сначала на текстуру заносится AB, выводятся первые два символа, потом на место А ставится С и рисуется последний символ. Но обновление текстуры происходит в момент вызова glTexSubImage2D, а отрисовка при смене буферов (elgSwapBuffers в андроиде вызывается когда завершится функция onDrawFrame). В результате так как на момент отрисовки текстура выглядит как CB, строка на экране выглядит как CBC.

На adreno если поставить после каждой отрисовки части строки glFlush, всё рисуется правильно. Но сильно замедляет фпс. На Powervr glFlush не работает, но тем не менее у него драйвер как то пытается разрешить эту ситуацию, и почти всегда на выходе получается ABC, но вот механизм, как он запоминает состояние текстуры для разных вызовов glDrawElements остается загадкой. Тем более иногда проскакивает CBC.

Так что вопрос - opengl должен запоминать состояния текстур или нет? Если вот я вывожу данные не через vbo, а даю указатель на кусок памяти, где лежат координаты вершин. Данные эти при каждом вызове glDraw* копируются во внутренние буферы и мне не надо заботится о состоянии памяти на момент фактической отрисовки. Я вообще эти данные могу удалить. А вот при изменении текстуры через glTexSubImage2D должен ли копировать драйвер куда то к себе внутрь новые данные и изменять текстуру именно когда это надо?

#4
19:52, 28 фев. 2012

alorken
Рисуешь строку из символов, некоторые из которых будут в разных текстурах. Но рисуешь не "по символам", а "по текстурам".
Сначала определяешь, какие текстуры тебе нужны, чтобы отрисовать строку. Допустим, что их N. Тогда за N проходов ты можешь отрисовать всю строку, при этом в проходе i (для текстуры номер 0<=i<N) ты рисуешь все символы, которые есть в этой текстуре. Символы, которых в текущей текстуре нет ты просто пропускаешь - заполняешь координаты в VBO так, чтобы оставить место для этих символов, которые ты просто нарисуешь в другом проходе.

#5
20:06, 28 фев. 2012

_NetSurfer_
Это конечно вариант. Но всё таки если текстура одна, то как должен в выше описанной ситуации вести себя драйвер opengl? Если бы на всех видах gpu выводилось CBC, то сомнений никаких и не возникало, получалось бы что при отрисовке используется та текстура, которая получилась к концу кадра. Но powervr как то же старается справиться.

P.S. В конечной реализации текстовых объектов если новый символ не может поместится на текстуру, не испортив данные, то отрисовка просто не происходит. Всё-таки для достаточно большой текстуры такие случаи практически никогда не будут возникать. Но вопрос хоть и перекочевал в разряд теоретических, всё равно интересно.

#6
20:09, 28 фев. 2012

При попытке обновить текстуру должен происходить stall и драйвер должен дождаться, пока отрисуются все примитивы, использующие эту текстуру. Вобщем стрёмно это и медленно.

#7
20:28, 28 фев. 2012

_NetSurfer_
А этот stall должен происходить автоматически? Или подразумевается, что я должен вызывать glFinish сам?

#8
20:29, 28 фев. 2012

Должен происходить автоматически. Но если не происходит на каких-то имплементациях (я уже ничему не удивляюсь), то придётся делать руками.
Но ещё раз повторюсь - это порочный путь.

#9
20:36, 28 фев. 2012

_NetSurfer_
Спасибо большое за ответы. Энтропия в моем мозгу уменьшилась. :)

Мобильные платформыФорумОбщее

Тема в архиве.