Как ссылаться на структуру в GLSL?

Ссылки в стиле C++ могут сделать код намного аккуратнее, но в GLSL их нет.

например. Этот код:

for (int i=0; i<numCascades; ++i) {
    if (eyeDistance < shadow.cascade[i].end) {
        vec3 sc = eyePos*shadow.cascade[i].transform;

        // ...many more references to "shadow.cascade[i]" here

    }
}

Было бы намного аккуратнее, если бы я мог вместо этого напечатать это:

for (int i=0; i<numCascades; ++i) {
    const ShadowCascade& cascade = shadow.cascade[i];
    if (eyeDistance < cascade.end) {
        vec3 sc = eyePos*cascade.transform;

        // ...many more references to "cascade" here

    }
}

Вместо этого я мог бы ввести это (то же самое, что и выше, но без знака «&»):

for (int i=0; i<numCascades; ++i) {
    const ShadowCascade cascade = shadow.cascade[i];
    if (eyeDistance < cascade.end) {
        vec3 sc = eyePos*cascade.transform;

        // ...many more references to "cascade" here

    }
}

Будет ли за это штраф за производительность? Будет ли каскадная структура скопирована в локальную переменную (как в C++)?

Как люди наводят порядок в таком коде на GLSL? (Использовать #define? (тьфу!))

В целом да, каскадная структура будет скопирована в локальную, как в C++. Однако хороший оптимизатор может увидеть это насквозь (с помощью const) и оптимизировать текст. Что касается использования #define — перед компиляцией он заменяется препроцессором, поэтому я не вижу, насколько это актуально для производительности.

wohlstad 26.08.2024 08:49
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
1
52
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Да, вы правы, вы не можете создать ссылку или указатель в GLSL так, как это можно сделать в C++.

В последнем примере вы копируете всю структуру (const ShadowCascade cascade = shadow.cascade[i];). Но так ли это? В теории компиляторов существует метод оптимизации, называемый распространением копирования. Это означает, что при доступе к cascade.transform вы получите то же значение, что и при доступе к shadow.cascade[i].transform. Почему? Потому что вы скопировали свою структуру и с тех пор не меняли ни одну структуру. И если он выбирает shadow.cascade[i].transform все время, переменная cascade никогда не используется, поэтому можно исключить инициализацию и копирование. Таким образом, теоретически компилятор может понять, что если он постоянно использует исходный массив, ему не нужно копировать структуру.

Однако, когда вы используете, например, glCompileShader, вы используете компилятор в своем драйвере. У Nvidia, AMD и Intel разные компиляторы, возможно, разные версии драйверов содержат разные компиляторы и, возможно, они по-разному работают с разными графическими процессорами. Поэтому сложно сказать, использует ли компилятор эту оптимизацию или нет. Мой совет — скомпилируйте ваши шейдеры GLSL в SPIR-V. Он похож на IL C# или байт-код Java в Java. Это нейтральный с точки зрения архитектуры промежуточный язык. Таким образом, после того как вы скомпилировали шейдеры, вы можете просмотреть результат и проверить, существует ли копия или нет. SPIR-V имеет множество других преимуществ, таких как сокращение времени компиляции во время выполнения (поскольку компиляция из SPIR-V в машинный код происходит быстрее, чем из GLSL).

Однако SPIR-V доступен не везде. Он всегда доступен в Vulkan, OpenGL 4.6 и OpenGL ES 3.2. обычно он доступен в более старых версиях OpenGL и OpenGL ES с расширением, но никогда не доступен в Интернете. Решение, которое работает везде, - это макрос (как вы упомянули). В университете, где я изучал программирование, преподаватели говорили, что макросы — это зло. И да, не так уж и сложно неправильно использовать макросы и другие директивы препроцессора. Однако позже, когда я работал графическим программистом, я увидел, что люди часто используют эти вещи, потому что иногда им нет реальной альтернативы, или это самая производительная вещь. Итак, хотя я понимаю, что вы не решаетесь использовать макросы, и вам определенно не следует использовать их для всего, я не думаю, что это проблема написать что-то вроде этого: #define CASCADE(i) shadow.cascade[i]. Он будет работать со всеми версиями GLSL, со всеми поставщиками, драйверами, вам не придется иметь дело со SPIR-V и т. д.

Подводя итог, я советую вам использовать SPIR-V, макросы или и то, и другое.

Однако у меня есть еще одно сомнение по поводу макросов: будет ли он вычислять смещение массива («shadow.cascade[i]») каждый раз, когда я обращаюсь к униформе. Для этого нужно умножить i на размер структуры, добавить смещение и т. д. Будет ли это оптимизировано? Все это очень раздражает, когда приходится просто «доверять компилятору».

Chifti Saidi 26.08.2024 02:50

Я думаю, что использование «const» поможет оптимизации распространения копирования.

Chifti Saidi 26.08.2024 02:51

Ну, вероятно, он не будет вычислять указатель элемента массива несколько раз. Компиляторы часто используют еще один метод оптимизации, который называется устранением общего подвыражения. Если компилятор находит одно и то же выражение, например, обращаясь к элементу массива несколько раз, он вычисляет его только один раз. Да, раздражает то, что некоторые компиляторы могут это сделать, а другие — нет. Но опять же, если вы скомпилируете свой шейдер в SPIR-V, вы сможете увидеть все результирующие операции и проверить, оптимизирован ли он.

racz16 27.08.2024 01:09
Ответ принят как подходящий

Отвечу на мой собственный вопрос: я провел небольшое неформальное тестирование, и использование #define, как показано ниже, дало мне заметное улучшение частоты кадров.

for (int i=0; i<numCascades; ++i) {
    #define cascade shadow.cascade[i]
    if (eyeDistance < cascade.end) {
        vec3 sc = eyePos*cascade.transform;

        // ... more references to "cascade" here

    }
}

Это было на настольной NVIDIA, так что, я думаю, если они не оптимизируют этот шаблон, то никто больше не будет.

Я думаю, что отступ #define добавляет немного семантической информации, но нужно ли мне после этого #undef? Решения, решения...

Chifti Saidi 26.08.2024 12:55

Другие вопросы по теме