Концептуальная модель графического приложения, использующего функции OpenGL



Дата29.05.2016
өлшемі84.5 Kb.
#100724

  1. Концептуальная модель графического приложения, использующего функции

OpenGL

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

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

С точки зрения разработчиков, OpenGL – это набор команд, которые управляют использованием графической аппаратуры. Если аппаратура состоит только из адресуемого буфера кадра, тогда OpenGL должен быть реализован полностью с использованием ресурсов центрального процессора. Обычно графическая аппаратура предоставляет различные уровни ускорения: от аппаратной реализации вывода линий и многоугольников до изощренных графических процессоров с поддержкой различных операций над геометрическими данными.






Рис. 1. Функционирование конвейера OpenGL





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

Графический конвейер


В самом общем виде можно рассматривать 3-х фазный конвейер OpenGL, каждая фаза которого, в зависимости от реализации, может выполняться CPU или графической платой (accelerator card). В рамках такой концептуальной модели (лекция 1) удобно рассмотреть временные (и другие ресурсные) затраты программы. Итак, рассматриваем следующие стадии (фазы) конвейера:

  • “Приложение” – обеспечивает взаимодействие функций OpenGL и графической системы ( очевидно, связана только с работой CPU).

  • Геометрические преобразования – все операции, связанные с преобразованием координат ((например, вычисление освещенности, вычисление координат накладываемой текстуры (может быть поддержан графической платой)The geometry subsystem.

  • Растеризация – операции над пикселями, например, запись значения цвета в буфер кадра, или работа с буфером глубины

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

2. Обеспечение производительности OpenGL программ

Повысить производительность( сократить время выполнения) графических программ можно как за счет более эффективного использования оборудования (hardware), так и программ. Обеспечивать эффективность работы программ можно за счет оптимизации кода и выполнения определенных правил их организации, которые относятся к обеспечению определенного компромисса между точностью и производительностью. Эти правила, разумеется, должны иметь учитывать характеристики используемого оборудование (платформы).

ниже мы рассмотрим основные правила оптимизации.
1. Анализ “узких мест” графических приложений

Под “узким местом” (bottleneck) производительности приложения будем понимать элементы (участки) программ и оборудования, структурные и функциональные характеристики которых препятствуют достижению высокой производительности. Например, видеокарта с малой памятью является узким местом для графического приложения на высокопроизводительном (по остальным характеристикам) платформе.

Узкие места можно определить, измеряя времена выполнения программ или их участков. Если оказывается, что изменение в элементах, подозреваемых как bottleneck приводит к существенному сокращению времени выполнения программы, то мы действительно нашли узкое место. и способ его устранения.

Итак, чтобы решить задачу повышения производительности, следует

- определить элементы (компоненты) программ и/или оборудования, которые могут быть причиной низкой производительности,

-выбрать программы или наборы данных, на которых может проявиться недостаток производительности,

- произвести измерения времени выполнения

Узкие места на уровне стадии приложений.


Производительность стадии приложения графического конвейера определятся числом используемых инструкций обращений к памяти. Подмножества кода,на выполнение которого затрачивается значительно большее время чем на остальные участки, может рассматриваться как “ узкое место” (bottleneck). Пользователи OpenGL называют также приложения, время выполнения которых лишь незначительно уменьшается при отключении ,большинства вызовов OpenGl функций.
Устранение “ bottleneck ” Например, при построении поверхности с использованием вызовов функций вершин и нормалей glVertex3fv() и glNormal3fv() эти функции можно заменить функцией glColor3fv(), которая не использует рисования и вычисления освещенности в графическом конвейере, и следовательно, должно уменьшится время выполнения. Если этого не произойдет, значит приложение само по себе почти полностью использует ресурсы процессора.

Узкие места геометрических преобразований.


Чтобы проверить наличие узких мест в геометрических преобразованиях, следует устранить, по возможности, другие факторы, например, использовать glDisable() с аргументом GL_LIGHTING для временного отключения функций освещенности.

Причиной Geometry bottlenecks может быть пропускная способность шины между CPU и графической картой, что можно проверить устранив вызовы glColor3fv() или glNormal3fv() .


Основные факторы, влияющие на производительность приведены в таблице 1.  

Performance Parameter

Pipeline Stage

Amount of data per polygon

All stages

Application overhead

Application

Transform rate and geometry mode setting

Geometry subsystem

Total number of polygons in a frame

Geometry and raster subsystem

Number of pixels filled

Raster subsystem

Fill rate for the current mode settings

Raster subsystem

Duration of screen and/or depth buffer clear

Raster subsystem

Таблица факторов производительности

Известно, что до 90% времени выполнения графических приложений приходится на сравнительно небольшую часть кода (не более 10% ) кода. Большинство графических акселераторов настроены на работу с конвейером, где одна фаза (сегмент) выполняет


геометрические преобразования и вычисление освещенности, в то время как другая прорисовывает пиксели в буфер кадров (frame buffer). Поскольку оба сегмента конвейера выполняются одновременно, в каждый момент один из сегментов-“узкое место”, и его оптимизация наилучшим образом повлияет на производительность.

Так как разные части программ воздействуют на различные фазы конвейера, важно понять, какие из элементов образуют “ узкие мета”

Если графическая задача решается только программными средствами, нет смысла увеличивать рабочую нагрузку для одной из фаз, если другая использует сущестенно больше процессорного времени. Это может лишь снизить производительность.

Чтобы повысить производительность на фазе геометрических преобразований, следует по возможности отключать свойства, требующие для своей реализации значительных ресурсов, но без которые, приданных к требованиях к качеству сцены, можно обойтись. Например, использования моделирования тумана (параметр GL_FOG в glEnable) замедляет скорость геометриеских преобразований для полигонов. Поэтому, если, например, используемая плотность тумана близка к нулю, это свойство можно попробовать отключить glDisableGL_FOG (GL_FOG).



Основные рекомендации по оптимальной, с точки зрения производительности, организации программ, использующих библиотеки OpenGL, можно найти в материале tutorial.doc, отправленный всем Вам в начале семестра.

Ниже приводится дополнительный список рекомендаций по управлению

производительностью.

Minimize mode changes.


  • Be especially careful about expensive mode changes such as changing glDepthRange() parameters and changing fog parameters when fog is enabled.

  • For optimum performance of most software renderers and many hardware renderers as well, use flat shading. This reduces the number of lighting computations from one per-vertex to one per-primitive, and also reduces the amount of data that must be processed for each primitive. Keep in mind that long triangle strips approach one vertex per primitive and may show little benefit from flat shading.

Optimizing Transformations

  • Use glLoadIdentity() to initialize a matrix, rather than loading your own copy of the identity matrix.

  • Use specific matrix calls such as glRotate\*(), glTranslate\*(), and glScale\*() rather than composing your own rotation, translation, or scale matrices and calling glLoadMatrix() and/ or glMultMatrix().

Optimizing Lighting Performance


OpenGL offers a large selection of lighting features. The penalties some features carry may vary depending on the hardware you're running on. Be prepared to experiment with the lighting configuration.

As a general rule, use the simplest possible lighting model: a single infinite light with an infinite viewer. For some local effects, try replacing local lights with infinite lights and a local viewer. Keep in mind, however, that not all rules listed here increase performance for all architectures.

Use the following settings for peak performance lighting:


  • Single infinite light.

  • Nonlocal viewing. Set GL_LIGHT_MODEL_LOCAL_VIEWER to GL_FALSE in glLightModel() (the default).

  • Single-sided lighting. Set GL_LIGHT_MODEL_TWO_SIDE to GL_FALSE in glLightModel() (the default).

  • If two-sided lighting is used, use the same material properties for front and back by specifying GL_FRONT_AND_BACK.

  • Don't use per-vertex color.

  • Disable GL_NORMALIZE. Since it is usually only necessary to renormalize when the model-view matrix includes a scaling transformation, consider preprocessing the scene to eliminate scaling.

In addition, follow these guidelines to achieve peak lighting performance:

  • Avoid using multiple lights.

There may be a sharp drop in lighting performance when adding lights.

  • Avoid using local lights.

Local lights are noticeably more expensive than infinite lights.

  • Use positional light sources rather than spot lights.

If local lights must be used, a positional light is less expensive than a spot light.

  • Don't change material parameters frequently.

Changing material parameters can be expensive. If you need to change the material parameters many times per frame, consider rearranging the scene to minimize material changes. Also consider using glColorMaterial() if you need to change some material parameters often, rather than using glMaterial() to change parameters explicitly. Changing material parameters inside a glBegin()/glEnd() sequence can be more expensive than changing them outside.

The following code fragment illustrates how to change ambient and diffuse material parameters at every polygon or at every vertex:

glColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE);

glEnable(GL_COLOR_MATERIAL);

/* Draw triangles: */

glBegin(GL_TRIANGLES);

/* Set ambient and diffuse material parameters: */

glColor4f(red, green, blue, alpha);

glVertex3fv(...);glVertex3fv(...);glVertex3fv(...);

glColor4f(red, green, blue, alpha);

glVertex3fv(...);glVertex3fv(...);glVertex3fv(...);

...


glEnd();

  • Avoid local viewer.

Local viewing: Setting GL_LIGHT_MODEL_LOCAL_VIEWER to GL_TRUE with glLightModel(), while using infinite lights only, reduces performance by a small amount. However, each additional local light noticeably degrades the transform rate.

  • Disable two-sided lighting.

Two-sided lighting illuminates both sides of a polygon. This is much faster than the alternative of drawing polygons twice. However, using two-sided lighting can be significantly slower than one-sided lighting for a single rendering of an object.

  • Disable GL_NORMALIZE.

If possible, provide unit-length normals and don't call glScale\*() to avoid the overhead of GL_NORMALIZE. On some OpenGL implementations it may be faster to simply rescale the normal, instead of renormalizing it, when the modelview matrix contains a uniform scale matrix. The normal rescaling functionality in OpenGL 1.2, or the EXT_rescale_normal extension for older OpenGL versions, can be used to improve the performance of this case. If it is supported, you can enable GL_RESCALE_NORMAL_EXT and the normal will be rescaled making re- normalization unnecessary.

  • Avoid changing the GL_SHININESS material parameter if possible.

Some portions of the lighting calculation may be approximated with a table, and changing the GL_SHININESS value may force those tables to be regenerated.

Minimizing Cache Misses.


Most CPUs have first-level instruction and data caches on chip and many have second-level caches that are bigger but somewhat slower. Memory accesses are much faster if the data is already loaded into the first-level cache. When your program accesses data that isn't in one of the caches, a cache miss occurs. This causes a block of consecutively addressed words, including the data that your program just accessed, to be loaded into the cache. Since cache misses are costly, you should try to minimize them, using these tips:

  • Keep frequently accessed data together. Store and access frequently used data in flat, sequential data structures and avoid pointer indirection. This way, the most frequently accessed data remains in the first-level cache as much as possible.

  • Access data sequentially. Each cache miss brings in a block of consecutively addressed words of needed data. If you are accessing data sequentially then each cache miss will bring in n words (where n is system dependent); if you are accessing only every nth word, then you will constantly be bringing in unneeded data, degrading performance.

  • Avoid simultaneously traversing several large buffers of data, such as an array of vertex coordinates and an array of colors within a loop since there can be cache conflicts between the buffers. Instead, pack the contents into one buffer whenever possible. If you are using vertex arrays, try to use interleaved arrays. (For more information on vertex arrays see ``Rendering Geometry Efficiently''.)

Some framebuffers have cache-like behaviors as well. It is a good idea to group geometry so that the drawing is done to one part of the screen at a time. Using triangle strips and polylines tends to do this while simultaneously offering other performance advantages as well.

Minimizing State Changes.


Your program will almost always benefit if you reduce the number of state changes. A good way to do this is to rearrange your scene data according to what state is set and render primitives with the same state settings together. Mode changes should be ordered so that the most expensive state changes occur least often. Typically it is expensive to change texture binding, material parameters, fog parameters, texture filter modes, and the lighting model. However, some experimentation will be required to determine which state settings are most expensive on your target systems. For example, on systems that accelerate rasterization, it may not be that expensive to change rasterization controls such as the depth test function and whether or not depth testing is enabled. However, if you are running on a system with software rasterization, this may cause cached graphics state, such as function pointers or automatically generated code, to be flushed and regenerated.

Your target OpenGL implementation may not optimize state changes that are redundant, so it's also important for your application to avoid setting the same state values twice, such as enabling lighting when it is already enabled.


.2 Decomposition and Tessellation


Tessellation refers to the process of decomposing a complex surface such as a sphere into simpler primitives such as triangles or quadrilaterals. Most OpenGL implementations are tuned to process triangle strips and triangle fans efficiently. Triangles are desirable because they are planar, easy to rasterize, and can always be interpolated unambiguously. When an implementation is optimized for processing triangles, more complex primitives such as quad strips, quads, and polygons are decomposed into triangles early in the pipeline.

If the underlying implementation is performing this decomposition, there is a performance benefit in performing this decomposition a priori, either when the database is created or at application initialization time, rather than each time the primitive is issued. A second advantage of performing this decomposition under the control of the application is that the decomposition can be done consistently and independently of the OpenGL implementation. Since OpenGL doesn't specify its decomposition algorithm, different implementations may decompose a given quadrilateral along different diagonals. This can result in an image that is shaded differently and has different silhouette edges when drawn on two different OpenGL implementations.

Quadrilaterals may be decomposed by finding the diagonal that creates two triangles with the greatest difference in orientation. A good way to find this diagonal is to compute the angles between the normals at opposing vertices, compute the dot product, then choose the pair with the largest angle (smallest dot product) as shown in Figure 2. The normals for a vertex can be computed by taking the cross products of the the two vectors with origins at that vertex. An alternative decomposition method is to split the quadrilateral into triangles that are closest to equal in size.

Tessellation of simple surfaces such as spheres and cylinders is not difficult. Most implementations of the GLU library use a simple latitude-longitude tessellation for a sphere. While the algorithm is simple to implement, it has the disadvantage that the triangles produced from the tessellation have widely varying sizes. These widely varying sizes can cause noticeable artifacts, particularly if the object is lit and rotating.

A better algorithm generates triangles with sizes that are more consistent. Octahedral and icosahedral tessellations work well and are not very difficult to implement. An octahedral tessellation approximates a sphere with an octahedron whose vertices are all on the unit sphere. Since the faces of the octahedron are triangles they can easily be split into four triangles, as shown in Figure 3.

Each triangle is split by creating a new vertex in the middle of each edge and adding three new edges. These vertices are scaled onto the unit sphere by dividing them by their distance from the origin (normalizing them). This process can be repeated as desired, recursively dividing all of the triangles generated in each iteration.



The same algorithm can be applied using an icosahedron as the base object, recursively dividing all 20 sides. In both cases the algorithms can be coded so that triangle strips are generated instead of independent triangles, maximizing rendering performance. It is not necessary to split the triangle edges in half, since tessellating the triangle by other amounts, such as by thirds, or even any arbitrary number, may produce a more desirable final uniform triangle size.

Достарыңызбен бөлісу:




©dereksiz.org 2024
әкімшілігінің қараңыз

    Басты бет