Есть вычисления, которые можно выполнить один раз на обычном процессоре, - их результаты остаются постоянными для множества запусков и вершинного, и фрагментного шейдеров. За счет этого можно сэкономить память для хранения команд шейдера или улучшить производительность (или и то и другое), вычисляя некоторые значения заранее с помощью приложения и передавая их в шейдер через uniform-переменные. Зачастую такие вычисления можно распознать, анализируя код шейдера. Допустим, 1 ength передается как uniform-переменная, и шейдер всегда вычисляет sqrt (1 ength), так что выгоднее вычислить это значение заранее и передать в шейдер. Если шейдеру нужны и 1 ength и sqrt (1 ength), эти значения можно передавать как два отдельных параметра.
Принимая решение о месте выполнения вычислений, нужно определить узкие места программного обеспечения для конкретной операции рендеринга. Сначала нужно ускорять самую медленную часть системы. Не стоит тратить время на улучшение производительности не в узких местах, так как все равно выигрыша не будет.
8.2.2. Анализ алгоритма
Если разобраться в математике алгоритма, использующегося в шейдере, можно сделать этот шейдер более эффективным. Например, пусть нужно приводить любое значение переменной final col or к диапазону [0, 1]. Зная, что значение этой переменной может быть только больше 0, можно не проверять f i nal со! or на отрицательные значения. Команда mi п (final со! or, 1.0) всегда приведет результат к 1,0, поэтому она гораздо более эффективна, чем clamp (final со! or, 0.0,1.0),так как число нужно сравнивать только с одним значением, а не с двумя. Если для каждой переменной в шейдере определить диапазон допустимых значений, будет легче выявлять возможности оптимизации.
8.2.3. Использование встроенных функций
Встроенные функции лучше использовать везде, где только возможно, - это поможет повысить производительность. Встроенные функции реализованы производителем аппаратного обеспечения, и обычно они работают быстрее, чем те же вычисления в обычном коде. Если вычисления в шейдере все-таки сделаны вручную, без использования встроенных функций, вероятность того, что код будет работать быстрее, довольно мал, а ухудшить производительность вполне реально.
8.2.4. Использование векторов
Язык шейдеров OpenGL позволяет выполнять операции с векторами простыми и естественными способами, а графическое аппаратное обеспечение обычно спроектировано так, что может работать одновременно со всеми значениями вектора. Так что следует пользоваться этим преимуществом и применять векторы в вычислениях везде, где это возможно. С другой стороны, не следует использовать векторы, большие размером, чем того требуют вычисления. Это влечет за собой ненужный расход регистров, аппаратных интерполяторов (в случае varying-переменных), уменьшает пропускную способность процессора и занимает память.
8.2.5. Представление сложных функций в виде текстур
Так как обработка фрагментов уже программируема, область применения текстур для рисования изображения значительно расширена. Можно сохранять в виде текстур сложные функции, а затем вместо громоздких вычислений просто проводить поиск из фрагментного шейдера. Пример такого использования текстур приведен в главе 12, где функция шума представлена в виде 3D-текстуры. Этот подход дает преимущество при использовании специализированного высокопроизводительного аппаратного обеспечения, обеспечивающего доступ к текстурам, а также аппаратного обеспечения, которое выполняет интерполяцию между значениями, хранящимися в виде текстур.