Войти
ПрограммированиеФорумОбщее

Шрифт с юникодом в OGL

Страницы: 1 2 321 22 Следующая »
#0
19:49, 4 янв. 2012

Как вы выкручивались? Рендер в фреймбуфер, предварительный батчинг, хэш-таблицы, или просто однобайтовая кодировка?

Просто я как представлю что нужно выделить массив по крайней мере 65535 * 4 float (по одному float на текстурную координату) аж страшно становится. Сейчас у меня массив из 256 (поменьше даже) чаров с текстурными координатами и координатами смещения, которые передаются в лист или шейдр в зависимости от возможностей железа. Держится баланс между тормозами и памятью.

Может так и оставить? Китайской локализации вроде не планировалось.


#1
20:16, 4 янв. 2012

RPG
> массив по крайней мере 65535 * 4 float

вас пугает 1 мегабайт?

а чем не нравится рендерить текст на CPU в битмап? например, с помощью pango?

#2
21:03, 4 янв. 2012

ITIhonov
> вас пугает 1 мегабайт?

Ещё как. 1 мегабайт это как минимум, в растровом шрифте на каждый глиф у меня 4 текстурные координаты, 4 координаты смещения включая высоту и ширину, плюс инт для того чтобы хранить сам код чара. Помножаем на 10-20 шрифтов и получаем немалую цифру. Если скрестить с моим алгоритмом упаковки - получается по 40 байт на один символ.

ITIhonov
> а чем не нравится рендерить текст на CPU в битмап? например, с помощью pango?

Была такая шальная мысль. А что делать с теми кто не поддерживает NPOT?

Не понравились решения с предварительным рендерингом текста тем, что в движке нельзя просто написать print(x,y,text), придётся создавать объект текст, рендерить его в память, и потом выводить, сложно использовать, сложная реализация. Если текст меняется часто - хорошего мало. Хотя я сделал класс вывода текста батчем, он ни разу не использовался.

#3
21:33, 4 янв. 2012

А зачем делать все-все символы?

#4
21:34, 4 янв. 2012

RPG
> Если текст меняется часто - хорошего мало

Менять текст чаще, чем раз в секунду бессмысленно

#5
21:42, 4 янв. 2012

zlos
> А зачем делать все-все символы?
Вот ведь в чём соль. Все символы юникода обычно не нужны. Однако, память-то выделять надо на все - иначе как их потом искать? Либо выделять память под все, либо хэш со всеми вытекающими случайными обращениями к памяти. Поэтому я остановился пока на более щадащем для памяти режиме - сp1251 называется:)

ITIhonov
> Менять текст чаще, чем раз в секунду бессмысленно
Юникс вей. Жертва производительностью в угоду удобства АПИ. В движке всё рисуется draw call'ами, в том числе и шрифты. Если внести в это дело хаос - апи получится не ахти.

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

#6
21:58, 4 янв. 2012

RPG
1. Заранее оцениваем какие символы нужны.
2. Генерируем только их.
Всё. И на экран влазит не столько много букв чтобы так заморачиваться кэшами. А то предварительная оптимизация получается, на ровном месте.

#7
22:03, 4 янв. 2012

zlos
Это понятно, генерятся только те символы что нужны. А как же все-таки потом быть с их загрузкой в движок, хранением символов в памяти, отображением?

#8
22:17, 4 янв. 2012

Юзай заготовленные заранее растровые шрифты со всем набором необходимых тебе символов. В файле описания к шрифту хранишь помимо текстурных координат еще и ID lookup значение - к примеру 1-й по-порядку глиф имеет код 1568, это и будут твои ид лукап значения (1 и 1568). При загрузке создаешь id lookup table, обычный СОРТИРОВАННЫЙ массив вида:
struct GlyphId
{
    short atlasId;
    short charId;
};

Array<GlyphId> glyphLookupTable;
//...
Array<Glyph> glyps;

При загрузке пихаешь все свои буковки в glyps массив. Когда тебе надо получить какую-то букву ты бинарным поиском по коду чара в glyps массиве находишь его ид из атласа, что одновременно будет ид из массива glyps. Ну а дальше уже как обычно ;)
Конечно, у тебя будут кэш миссы, в твоем изначальном варианте они тоже будут ;), но этот вариант дает намного лучше производительность чем мапа и кушает намного меньше памяти чем твои 65536 ;)

zlos
> А то предварительная оптимизация получается, на ровном месте.
Не вижу ничего плохого в предварительных оптимизациях. Имея опыт, знания можно сразу писать быстрый, хорошо читаемый и безбажный код ;)

#9
22:39, 4 янв. 2012

StiX
> Конечно, у тебя будут кэш миссы, в твоем изначальном варианте они тоже будут
> ;), но этот вариант дает намного лучше производительность чем мапа и кушает
> намного меньше памяти чем твои 65536 ;)
Сейчас у меня lookup идет по массиву из 255 структур:) Всего 512 байт на шрифт: один инт это ид листа, второй флоат - ширина символа. Ну это 1 байт кодировка конечно.

Бинарный поиск тоже как вариант, но быстро ли? Бинарный поиск насколько я помню в худшем случае требует log2N операций, то есть где-то в среднем 7 операций сравнения, чтения и сложения. Нехило.

Хотя можно сделать lookup table с указателями на структуры - это всего лишь 256 кб, плюс сами структуры. Хотя на 64 бит побольше будет. Можно попробовать указатель  на массив хранить отдельно а в таблице хранить short смещение. Как-то так:)

#10
22:52, 4 янв. 2012

Идеальная хэш функция + массив.
Вот только как её генерировать?

#11
23:03, 4 янв. 2012

RPG

std::map<wchar_t, GlyphInfo>
И не заморачиваться теми местами которые заведомо не будут узкими.
#12
23:06, 4 янв. 2012

zlos
Си

#13
0:25, 5 янв. 2012

генерим ток нужные символы

struct glyph { uint texture; vec2 uv; vec2 size; ... че угодно в любом кол-ве ... };
glyph* chars[65536] = {0};
...
chars['я'] = new glyph;
...

итого 256k на хранение указателей + удобство и скорость доступа

#14
0:26, 5 янв. 2012

Хэш таблицы - это не тот инстурмент, который нужно использовать при рендере юникоде шрифтов.
Дело в том, что наша задача предполагает использование не единичных символов, а блоков символов.
Мы же не будем выводить один кирилический символ, один арабский, один греческий и т.п.


Простой и экономичный способ реализации юникода:
Буду говорить применительно UTF-8 как к самой простой и удобной кодировке.
Каждый символ кодируется последовательностью из нескольких байт.

-----------------------
Инициализация:
Изначально инициализируется массив из 256 значений.
Первые 128 символов по стандарту заполняются сразу значениями - текстурными координаты, номер текстуры, etc.
Соответственно в текстуру рисуем стандартные символы. Английский, цифры, спец символы - они нам наверняка пригодятся, есть смысл их сгенерировать сразу.

Остальные 128 значений записываем как не инициализированные.

Потери памяти в этот момент составляют 128*размер символа + 128*sizeof(void*) заполненных NULL.
-----------------------
Рендер текста:
Пришла строка на рендер.

Если код <=128 - это уже символ.
Если больше - переходим к элементу массива. Он у нас еще не инициализирован. Вызываем его инициализацию.
Например пришел текст:
127
128 130
225 128 130
225 128 133

Проверяем первый символ:
127: Он попадает в диапазон стандартных символов. Рисуем.
Переходим к следующем:
128: Он не попадает в диапазон стандартных символов. Попадает в диапазон символов из двух байт. Блок не инициализирован. Инициализируем его(как - описание ниже). Берем символ по дополнительному байту 130.
Переходим к следующем:
225: Он не попадает в диапазон стандартных символов. Попадает в диапазон символов из 3х байт. Блок не инициализирован. Инициализируем. Берем дополнительный байт 128. Блок не инициализирован. Инициализируем. Берем символ по дополнительному байту 130.
Переходим к следующем:
225: Он не попадает в диапазон стандартных символов. Попадает в диапазон символов из 3х байт. Блок инициализирован. Берем дополнительный байт 128. Блок Инициализирован. Берем символ по дополнительному байту 133.

-----------------------
Инициализация не инициализированного блока.

Если блок промежуточный, например 225 128 130, то просто выделяем память под массив из 128 значений и забиваем его NULL.

Если блок конечный, например 128 130 или 225 128 133, то выделяем память под 128 значений и забиваем их символами для этого блока.
Можно конечно по одному символу инициализировать, но это ИМХО только для всяких китайских языков имеет смысл, т.к. у них иероглифы могут не пересекаться в одном блоке. Для всех остальных язщыков жффективно инициализировать блоками, т.к. если я написал русскую А, вероятно я захочу в скором времени написать Б, В и другие буквы, которые находятся в этом же блоке.

-----------------------
После того как будет сделана вышеприведенная работа в памяти будет примерно следующее(текстуру с символами сюда не привожу, с ней и так все понятно):
unicode_memory | Шрифт с юникодом в OGL

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

Страницы: 1 2 321 22 Следующая »
ПрограммированиеФорумОбщее

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