ВУЗ: Не указан
Категория: Не указан
Дисциплина: Не указана
Добавлен: 09.06.2020
Просмотров: 1708
Скачиваний: 4
76
Глава
8
Оптимизация
программ
8.1.
Организация
приложения
На
первый
взгляд
может
показаться
,
что
производительность
приложений
,
основанных
на
OpenGL,
определяется
в
первую
очередь
производительностью
реализации
самой
библиотеки
OpenGL.
Это
верно
,
однако
организация
всего
приложения
также
очень
важна
.
8.1.1.
Высокоуровневая
оптимизация
Обычно
от
программы
под
OpenGL
требуется
визуализация
высокого
качества
на
интерактивных
скоростях
.
Но
,
как
правило
,
и
то
и
другое
сразу
получить
не
удается
.
Следовательно
,
необходим
поиск
компромисса
между
качеством
и
производительностью
.
Существует
множество
различных
подходов
,
но
их
подробное
описание
выходит
за
пределы
этого
пособия
.
Приведем
лишь
несколько
примеров
.
Можно
отображать
геометрию
сцены
с
низким
качеством
во
время
анимации
,
а
в
моменты
остановок
показывать
ее
с
наилучшим
качеством
.
Во
время
интерактивного
вращения
(
например
,
при
нажатой
клавише
мыши
)
визуализировать
модель
с
уменьшенным
количеством
примитивов
.
При
рисовании
статичного
изображения
отображать
модель
полностью
.
Аналогично
,
объекты
,
которые
располагаются
далеко
от
наблюдателя
,
могут
быть
представлены
моделями
пониженной
сложности
.
Это
значительно
снизит
нагрузку
на
все
ступени
конвейера
OpenGL.
Объекты
,
которые
находятся
полностью
вне
поля
видимости
,
могут
быть
эффективно
отсечены
без
передачи
на
конвейер
OpenGL
с
помощью
проверки
попадания
ограничивающих
их
простых
объемов
(
сфер
или
кубов
)
в
пирамиду
зрения
.
Во
время
анимации
можно
отключить
псевдотонирование
(dithering),
плавную
заливку
,
текстуры
.
Опять
-
таки
включать
все
это
во
время
демонстрации
статичных
изображений
.
Этот
77
подход
особенно
эффективен
для
систем
без
аппаратной
поддержки
OpenGL.
8.1.2.
Низкоуровневая
оптимизация
Объекты
,
отображаемые
с
помощью
OpenGL,
хранятся
в
некоторых
структурах
данных
.
Одни
типы
таких
структур
более
эффективны
в
использовании
,
чем
другие
,
что
определяет
скорость
визуализации
.
Желательно
,
чтобы
использовались
структуры
данных
,
которые
могут
быть
быстро
и
эффективно
переданы
на
конвейер
OpenGL.
Например
,
если
мы
хотим
отобразить
массив
треугольников
,
то
использование
указателя
на
этот
массив
значительно
более
эффективно
,
чем
передача
его
OpenGL
поэлементно
.
Пример
:
Предположим
,
что
мы
пишем
приложение
,
которое
реализует
рисование
карты
местности
.
Один
из
компонентов
базы
данных
–
список
городов
с
их
шириной
,
долготой
и
названием
.
Соответствующая
структура
данных
может
быть
такой
:
struct city
{
float latitute, longitude; /*
положение
города
*/
char *name; /*
название
*/
int large_flag; /* 0 =
маленький
, 1 =
большой
*/
};
Список
городов
может
храниться
как
массив
таких
структур
.
Допустим
,
мы
пишем
функцию
,
которая
рисует
города
на
карте
в
виде
точек
разного
размера
с
подписями
:
void draw_cities( int n, struct city citylist[] )
{
int i;
for (i=0; i < n; i++)
{
if (citylist[i].large_flag)
glPointSize( 4.0 );
else
glPointSize( 2.0 );
glBegin( GL_POINTS );
glVertex2f( citylist[i].longitude,
78
citylist[i].latitude );
glEnd();
/*
рисуем
название
города
*/
DrawText(citylist[i].longitude,
citylist[i].latitude,
citylist[i].name);
}
}
Это
реализация
неудачна
по
следующим
причинам
:
glPointSize
вызывается
для
каждой
итерации
цикла
.
между
glBegin
и
glEnd
рисуется
только
одна
точка
.
вершины
определяются
в
неоптимальном
формате
.
Ниже
приведено
более
рациональное
решение
:
void draw_cities( int n, struct city citylist[] )
{
int i;
/*
сначала
рисуем
маленькие
точки
*/
glPointSize( 2.0 );
glBegin( GL_POINTS );
for (i=0; i < n ;i++)
{
if (citylist[i].large_flag==0) {
glVertex2f( citylist[i].longitude,
citylist[i].latitude );
}
}
glEnd();
/*
большие
точки
рисуем
во
вторую
очередь
*/
glPointSize( 4.0 );
glBegin( GL_POINTS );
for (i=0; i < n ;i++)
{
if (citylist[i].large_flag==1)
{
glVertex2f( citylist[i].longitude,
citylist[i].latitude );
}
}
glEnd();
/*
затем
рисуем
названия
городов
*/
for (i=0; i < n ;i++)
{
79
DrawText(citylist[i].longitude,
citylist[i].latitude,
citylist[i].name);
}
}
В
такой
реализации
мы
вызываем
glPointSize
дважды
и
увеличиваем
число
вершин
между
glBegin
и
glEnd
.
Однако
остаются
еще
пути
для
оптимизации
.
Если
мы
поменяем
наши
структуры
данных
,
то
можем
еще
повысить
эффективность
рисования
точек
.
Например
:
struct city_list
{
int num_cities; /*
число
городов
в
списке
*/
float *position;/*
координаты
города
*/
char **name; /*
указатель
на
названия
городов
*/
float size; /*
размер
точки
,
обозначающей
город
*/
};
Теперь
города
разных
размеров
хранятся
в
разных
списках
.
Положения
точек
хранятся
отдельно
в
динамическом
массиве
.
После
реорганизации
мы
исключаем
необходимость
в
условном
операторе
внутри
glBegin/glEnd
и
получаем
возможность
использовать
массивы
вершин
для
оптимизации
.
В
результате
наша
функция
выглядит
следующим
образом
:
void draw_cities( struct city_list *list )
{
int i;
/*
рисуем
точки
*/
glPointSize( list->size );
glVertexPointer( 2, GL_FLOAT, 0,
list->num_cities,
list->position );
glDrawArrays( GL_POINTS, 0, list->num_cities );
/*
рисуем
название
города
*/
for (i=0; i < list->num_cities ;i++)
{
DrawText(citylist[i].longitude,
citylist[i].latitude
citylist[i].name);
}
}
80
8.2.
Оптимизация
вызовов
OpenGL
Существует
много
возможностей
улучшения
производительности
OpenGL.
К
тому
же
разные
подходы
к
оптимизации
приводят
к
разным
эффектам
при
аппаратной
и
программной
визуализации
.
Например
,
интерполяция
цветов
может
быть
очень
дорогой
операцией
без
аппаратной
поддержки
,
а
при
аппаратной
визуализации
почти
не
дает
задержек
.
После
каждой
из
следующих
методик
следуют
квадратные
скобки
,
в
которых
указан
один
из
символов
,
обозначающих
значимость
данной
методики
для
конкретной
системы
[
А
] –
предпочтительно
для
систем
с
аппаратной
поддержкой
OpenGL
[
П
] –
предпочтительно
для
программных
реализаций
[
все
] –
вероятно
предпочтительно
для
всех
реализаций
8.2.1.
Передача
данных
в
OpenGL
В
данном
разделе
рассмотрим
способы
минимизации
времени
на
передачу
данных
о
примитивах
в
OpenGL
Используйте
связанные
примитивы
.
Связанные
примитивы
,
такие
как
GL_LINES
,
GL_LINE_LOOP
,
GL_TRIANGLE_STRIP
,
GL_TRIANGLE_FAN
,
и
GL_QUAD_STRIP
требуют
для
определения
меньше
вершин
,
чем
отдельные
линия
или
многоугольник
.
Это
уменьшает
количество
данных
,
передаваемых
OpenGL [
все
]
Используйте
массивы
вершин
.
На
большинстве
архитектур
замена
множественных
вызовов
glVertex/glColor/glNormal
на
механизм
массивов
вершин
может
быть
очень
выигрышной
. [
все
]
Используйте
индексированные
примитивы
.
В
некоторых
случаях
даже
при
использовании
связанных
примитивов
GL_TRIANGLE_STRIP
(
GL_QUAD_STRIP
)
вершины
дублируются
.