GLSL - Объявление глобальных переменных вне основной области функции


12

Помогает ли это объявлять переменные вне области вашей основной функции в GLSL? Эти переменные действительно используются повторно, и является ли это более эффективным?

Вот код, о котором идет речь:

varying vec2 vposition;
uniform float seed;
uniform float top;
uniform float bottom;
uniform float phi;
uniform float theta;
uniform float scaledPI;
uniform float yn;
uniform float ym;
uniform float rx;
uniform float ry;
uniform float radius;

const float PI = 3.141592653589793238462643383;

float left;
float right;
float mscaled;
float xn;
float xm;
void main() {
    float t = vposition.y * yn + ym;

    if(t <= 0.0 || t >= PI){
        left = phi - PI;
        right = phi + PI;
    }else{
        mscaled = scaledPI / (1 - abs(Math.cos(theta)));
        mscaled = mscaled < PI ? mscaled : PI;
        left = phi - mscaled;
        right = phi + mscaled;
    }
    xn = (left - right) / ((-rx / 2.0) - (rx / 2.0));
    xm = left - ((-rx/2.0) * xn);
    float p = vposition.x * xn + xm;

    vec3 coords = vec3( sin(p) * sin(t), cos(t), cos(p) * sin(t) );
    float nv = surface( vec4( coords, seed ) );
    gl_FragColor = vec4( vec3( nv, nv, nv ), 1.0 );
}

3
Этот вопрос сбивает с толку. GLSL не имеет основного цикла. Вы имеете в виду main()функцию? Являются ли ваши значения глобальными переменными (униформа или атрибуты на языке GLSL) или постоянными значениями?
Шон Мидлдич

Можете ли вы опубликовать некоторый код в качестве примера того, о чем вы говорите?
Натан Рид

Я обновил вопрос с кодом.
RodgerDodger

@ user1286792: Это не меняет того факта, что GLSL не имеет основного цикла . Непонятно, о чем ты говоришь. Что именно вы думаете, будет спасено этим?
Николь Болас

@NicolBolas Я обновил вопрос, чтобы быть более понятным. Надеюсь, это будет полезно сейчас для кого-то в будущем.
RodgerDodger

Ответы:


33

Я думаю, я понимаю, что вы пытаетесь спросить. Я предполагаю, что вашей главной заботой являются неоднородные переменные, определенные вне main():

float left;
float right;
float mscaled;
float xn;
float xm;

Давайте посмотрим, как работают GPU и GLSL. Графический процессор не имеет стека или записей активации вызова. В GLSL нет способа имитировать область видимости или локальные переменные, как это делает компилятор C на большинстве процессоров. Все, что существует, это регистры, которые являются либо унифицированными регистрами, входами, выходами каскада шейдера, и локальным файлом регистров, уникальным для этого вызова шейдера.

Другими словами, поскольку такой функции, как стек, куча, не существует, все объявленные где-либо переменные живут в регистре. Независимо от того, являются ли они локальными для некоторой области в GLSL или глобальными для всего файла, не имеет значения. Они просто регистры.

Однако распределитель регистров не является частью стандарта GLSL. Различные реализации OpenGL могут иметь разные уровни качества, когда дело доходит до преобразования высокоуровневого кода GLSL в низкоуровневый машинный код, который понимает графический процессор. Одна из более сложных частей компилятора (GLSL или иначе) - это распределение регистров . Это та часть компилятора, которая определяет, какие регистры занимает данная переменная. В C это немного сложнее, так как обычно приходится иметь дело с очень маленькими файлами регистров (особенно в x86), и он имеет дело с разливом регистров (перемещением переменных в стек) и псевдонимами (сохранение переменных обратно в RAM перед вызовом функций) и нечетные инструкции, требующие вывода в конкретном регистре (x86idivнапример). Графические процессоры имеют большой регистровый файл из-за отсутствия стека или кучи, поэтому распределитель может быть проще.

Тем не менее, файл реестра не является бесконечным. Если у вас больше переменных, чем регистров, поддерживаемых вашим оборудованием, компилятор должен попытаться поместить все ваши переменные в регистры. Это обычно требует некоторой формы проверки диапазона живучести. То есть, если вы используете переменную xnдля одного вычисления, а затем никогда не будете использовать ее снова, компилятор может определить это и затем узнать, что регистр, занятый этим, xnможет быть использован другой переменной позже, что позволяет использовать больше переменных, чем есть регистры (так долго поскольку не слишком много живых переменных одновременно).

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

В общем, я очень рекомендую хранить все переменные локализованными. Держите определение как можно ближе к использованию переменной, как это имеет смысл. Это относится ко всем языкам программирования, а не только к GLSL. Я также рекомендовал бы делать каждую «переменную» const в каждом возможном случае. Для некоторых менее способных компиляторов снова может быть указание на то, что определенные оптимизации возможны, и, что более важно, это делает ваш код более самодокументируемым и простым в обслуживании.

И, конечно же, вот ваш обязательный совет «просто профиль, чтобы проверить и узнать наверняка». Напишите свой шейдер с и без ваших глобалов и профилируйте его. Любой и все советы по производительности в сети должны быть недоверчивыми и предполагаться, что они либо основаны на предположениях, либо устарели.


Это именно то, что я имел в виду. Вы отлично ответили на мой вопрос, а также лучше объяснили, что я спрашиваю.
RodgerDodger

1
Фактически иногда объявление массивов как const вместо non-const делает весь шейдер медленнее (НАМНОГО медленнее). Я заметил эту проблему на моем GTX 460.
Тара

Я только что избавился от странной ошибки и сильно подозревал, что это был графический процессор Adreno (OpenGL ES 3.1), который не смог скомпилировать шейдер, потому что переменные были объявлены вне main.
Comodoro

Один из самых тщательных SO ответов, которые я когда-либо видел - молодец!
Духайме

Сегодня я узнаю что-то новое. Сегодня я учусь, я действительно не знаю и не понимаю glsl. Просто потому, что я могу использовать его, чтобы сделать gif из цилиндрического пространства с преобразованной геометрией, не значит, что я понимаю, как это работает.
Чмарангу
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.